From 6190fd61d4d632cef58409ceac42d2b9882601c6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:13 +0200 Subject: [PATCH 0001/2884] meson: do not link pixman automatically into all targets The dependency on pixman is listed manually in all sourcesets that need it. There is no need to bring into libqemuutil, since there is nothing in util/ that needs pixman either. Reported-by: Michael Tokarev Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 91a0aa64c6..8c1271b884 100644 --- a/meson.build +++ b/meson.build @@ -3481,7 +3481,7 @@ util_ss = util_ss.apply({}) libqemuutil = static_library('qemuutil', build_by_default: false, sources: util_ss.sources() + stub_ss.sources() + genh, - dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman]) + dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc]) qemuutil = declare_dependency(link_with: libqemuutil, sources: genh + version_res, dependencies: [event_loop_base]) From d04c7e5535e97e4a1a55d05cfe632b8c66369b36 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:14 +0200 Subject: [PATCH 0002/2884] tests: only build plugins if TCG is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no way to use them for testing, if all the available accelerators use hardware virtualization. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-3-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- tests/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/meson.build b/tests/meson.build index 0a6f96f8f8..acb6807094 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -78,9 +78,9 @@ subdir('decode') if 'CONFIG_TCG' in config_all_accel subdir('fp') + subdir('plugin') endif -subdir('plugin') subdir('unit') subdir('qapi-schema') subdir('qtest') From bb23c33f9381a8986798deb896cd15c2c0932cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Apr 2024 17:53:15 +0200 Subject: [PATCH 0003/2884] ebpf: Restrict to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eBPF is not used in user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240404194757.9343-2-philmd@linaro.org> Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-4-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- ebpf/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebpf/meson.build b/ebpf/meson.build index c5bf9295a2..bff6156f51 100644 --- a/ebpf/meson.build +++ b/ebpf/meson.build @@ -1 +1 @@ -common_ss.add(when: libbpf, if_true: files('ebpf.c', 'ebpf_rss.c'), if_false: files('ebpf_rss-stub.c')) +system_ss.add(when: libbpf, if_true: files('ebpf.c', 'ebpf_rss.c'), if_false: files('ebpf_rss-stub.c')) From 34dca3f5be81897338176cd4bbe54fecbdd5c3af Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:16 +0200 Subject: [PATCH 0004/2884] tests/unit: match some unit tests to corresponding feature switches Try not to test code that is not used by user mode emulation, or by the block layer, unless they are being compiled; and fix test-timed-average which was not compiled with --disable-system --enable-tools. This is by no means complete, it only touches the more blatantly wrong cases. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-5-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- tests/unit/meson.build | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 228a21d03c..26c109c968 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -18,7 +18,6 @@ tests = { 'test-forward-visitor': [testqapi], 'test-string-input-visitor': [testqapi], 'test-string-output-visitor': [testqapi], - 'test-opts-visitor': [testqapi], 'test-visitor-serialization': [testqapi], 'test-bitmap': [], 'test-resv-mem': [], @@ -46,12 +45,8 @@ tests = { 'test-qemu-opts': [], 'test-keyval': [testqapi], 'test-logging': [], - 'test-uuid': [], - 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-qapi-util': [], 'test-interval-tree': [], - 'test-xs-node': [qom], - 'test-virtio-dmabuf': [meson.project_source_root() / 'hw/display/virtio-dmabuf.c'], } if have_system or have_tools @@ -97,6 +92,8 @@ if have_block 'test-crypto-ivgen': [io], 'test-crypto-afsplit': [io], 'test-crypto-block': [io], + 'test-timed-average': [], + 'test-uuid': [], } if gnutls.found() and \ tasn1.found() and \ @@ -131,10 +128,13 @@ endif if have_system tests += { + 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-iov': [], + 'test-opts-visitor': [testqapi], + 'test-xs-node': [qom], + 'test-virtio-dmabuf': [meson.project_source_root() / 'hw/display/virtio-dmabuf.c'], 'test-qmp-cmds': [testqapi], 'test-xbzrle': [migration], - 'test-timed-average': [], 'test-util-sockets': ['socket-helpers.c'], 'test-base64': [], 'test-bufferiszero': [], From b9ad27a9a4040805e9bbae95853db41f33285ffa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:17 +0200 Subject: [PATCH 0005/2884] yank: only build if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The yank feature is not used in user emulation. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-6-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- util/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/meson.build b/util/meson.build index 0ef9886be0..2ad57b10ba 100644 --- a/util/meson.build +++ b/util/meson.build @@ -60,7 +60,6 @@ util_ss.add(files('stats64.c')) util_ss.add(files('systemd.c')) util_ss.add(files('transactions.c')) util_ss.add(files('guest-random.c')) -util_ss.add(files('yank.c')) util_ss.add(files('int128.c')) util_ss.add(files('memalign.c')) util_ss.add(files('interval-tree.c')) @@ -117,6 +116,7 @@ if have_block util_ss.add(files('vfio-helpers.c')) util_ss.add(files('chardev_open.c')) endif + util_ss.add(files('yank.c')) endif if cpu == 'aarch64' From 3df4c28860e100011db3a51a8a331506a3fe51f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Apr 2024 17:53:18 +0200 Subject: [PATCH 0006/2884] util/qemu-config: Extract QMP commands to qemu-config-qmp.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QMP is irrelevant for user emulation. Extract the code related to QMP in a different source file, which won't be build for user emulation binaries. This avoid pulling pointless code. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20240404194757.9343-5-philmd@linaro.org> Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-7-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/config-file.h | 3 + monitor/meson.build | 1 + monitor/qemu-config-qmp.c | 206 +++++++++++++++++++++++++++++++++++++ util/qemu-config.c | 204 +----------------------------------- 4 files changed, 212 insertions(+), 202 deletions(-) create mode 100644 monitor/qemu-config-qmp.c diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index b82a778123..51b310fa3b 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -8,6 +8,9 @@ QemuOptsList *qemu_find_opts(const char *group); QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); QemuOpts *qemu_find_opts_singleton(const char *group); +extern QemuOptsList *vm_config_groups[]; +extern QemuOptsList *drive_config_groups[]; + void qemu_add_opts(QemuOptsList *list); void qemu_add_drive_opts(QemuOptsList *list); int qemu_global_option(const char *str); diff --git a/monitor/meson.build b/monitor/meson.build index 5269492cf0..a71523a1ce 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -4,6 +4,7 @@ system_ss.add(files( 'fds.c', 'hmp-cmds.c', 'hmp.c', + 'qemu-config-qmp.c', )) system_ss.add([spice_headers, files('qmp-cmds.c')]) diff --git a/monitor/qemu-config-qmp.c b/monitor/qemu-config-qmp.c new file mode 100644 index 0000000000..24477a0e44 --- /dev/null +++ b/monitor/qemu-config-qmp.c @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qlist.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "hw/boards.h" + +static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) +{ + CommandLineParameterInfoList *param_list = NULL; + CommandLineParameterInfo *info; + int i; + + for (i = 0; desc[i].name != NULL; i++) { + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(desc[i].name); + + switch (desc[i].type) { + case QEMU_OPT_STRING: + info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; + break; + case QEMU_OPT_BOOL: + info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; + break; + case QEMU_OPT_NUMBER: + info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; + break; + case QEMU_OPT_SIZE: + info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; + break; + } + + info->help = g_strdup(desc[i].help); + info->q_default = g_strdup(desc[i].def_value_str); + + QAPI_LIST_PREPEND(param_list, info); + } + + return param_list; +} + +/* remove repeated entry from the info list */ +static void cleanup_infolist(CommandLineParameterInfoList *head) +{ + CommandLineParameterInfoList *pre_entry, *cur, *del_entry; + + cur = head; + while (cur->next) { + pre_entry = head; + while (pre_entry != cur->next) { + if (!strcmp(pre_entry->value->name, cur->next->value->name)) { + del_entry = cur->next; + cur->next = cur->next->next; + del_entry->next = NULL; + qapi_free_CommandLineParameterInfoList(del_entry); + break; + } + pre_entry = pre_entry->next; + } + cur = cur->next; + } +} + +/* merge the description items of two parameter infolists */ +static void connect_infolist(CommandLineParameterInfoList *head, + CommandLineParameterInfoList *new) +{ + CommandLineParameterInfoList *cur; + + cur = head; + while (cur->next) { + cur = cur->next; + } + cur->next = new; +} + +/* access all the local QemuOptsLists for drive option */ +static CommandLineParameterInfoList *get_drive_infolist(void) +{ + CommandLineParameterInfoList *head = NULL, *cur; + int i; + + for (i = 0; drive_config_groups[i] != NULL; i++) { + if (!head) { + head = query_option_descs(drive_config_groups[i]->desc); + } else { + cur = query_option_descs(drive_config_groups[i]->desc); + connect_infolist(head, cur); + } + } + cleanup_infolist(head); + + return head; +} + +static CommandLineParameterInfo *objprop_to_cmdline_prop(ObjectProperty *prop) +{ + CommandLineParameterInfo *info; + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(prop->name); + + if (g_str_equal(prop->type, "bool") || g_str_equal(prop->type, "OnOffAuto")) { + info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; + } else if (g_str_equal(prop->type, "int")) { + info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; + } else if (g_str_equal(prop->type, "size")) { + info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; + } else { + info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; + } + + if (prop->description) { + info->help = g_strdup(prop->description); + } + + return info; +} + +static CommandLineParameterInfoList *query_all_machine_properties(void) +{ + CommandLineParameterInfoList *params = NULL, *clpiter; + CommandLineParameterInfo *info; + GSList *machines, *curr_mach; + ObjectPropertyIterator op_iter; + ObjectProperty *prop; + bool is_new; + + machines = object_class_get_list(TYPE_MACHINE, false); + assert(machines); + + /* Loop over all machine classes */ + for (curr_mach = machines; curr_mach; curr_mach = curr_mach->next) { + object_class_property_iter_init(&op_iter, curr_mach->data); + /* ... and over the properties of each machine: */ + while ((prop = object_property_iter_next(&op_iter))) { + if (!prop->set) { + continue; + } + /* + * Check whether the property has already been put into the list + * (via another machine class) + */ + is_new = true; + for (clpiter = params; clpiter != NULL; clpiter = clpiter->next) { + if (g_str_equal(clpiter->value->name, prop->name)) { + is_new = false; + break; + } + } + /* If it hasn't been added before, add it now to the list */ + if (is_new) { + info = objprop_to_cmdline_prop(prop); + QAPI_LIST_PREPEND(params, info); + } + } + } + + g_slist_free(machines); + + /* Add entry for the "type" parameter */ + info = g_malloc0(sizeof(*info)); + info->name = g_strdup("type"); + info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; + info->help = g_strdup("machine type"); + QAPI_LIST_PREPEND(params, info); + + return params; +} + +CommandLineOptionInfoList *qmp_query_command_line_options(const char *option, + Error **errp) +{ + CommandLineOptionInfoList *conf_list = NULL; + CommandLineOptionInfo *info; + int i; + + for (i = 0; vm_config_groups[i] != NULL; i++) { + if (!option || !strcmp(option, vm_config_groups[i]->name)) { + info = g_malloc0(sizeof(*info)); + info->option = g_strdup(vm_config_groups[i]->name); + if (!strcmp("drive", vm_config_groups[i]->name)) { + info->parameters = get_drive_infolist(); + } else { + info->parameters = + query_option_descs(vm_config_groups[i]->desc); + } + QAPI_LIST_PREPEND(conf_list, info); + } + } + + if (!option || !strcmp(option, "machine")) { + info = g_malloc0(sizeof(*info)); + info->option = g_strdup("machine"); + info->parameters = query_all_machine_properties(); + QAPI_LIST_PREPEND(conf_list, info); + } + + if (conf_list == NULL) { + error_setg(errp, "invalid option name: %s", option); + } + + return conf_list; +} diff --git a/util/qemu-config.c b/util/qemu-config.c index 42076efe1e..a90c18dad2 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -1,16 +1,14 @@ #include "qemu/osdep.h" #include "block/qdict.h" /* for qdict_extract_subqdict() */ #include "qapi/error.h" -#include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/config-file.h" -#include "hw/boards.h" -static QemuOptsList *vm_config_groups[48]; -static QemuOptsList *drive_config_groups[5]; +QemuOptsList *vm_config_groups[48]; +QemuOptsList *drive_config_groups[5]; static QemuOptsList *find_list(QemuOptsList **lists, const char *group, Error **errp) @@ -55,204 +53,6 @@ QemuOpts *qemu_find_opts_singleton(const char *group) return opts; } -static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) -{ - CommandLineParameterInfoList *param_list = NULL; - CommandLineParameterInfo *info; - int i; - - for (i = 0; desc[i].name != NULL; i++) { - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(desc[i].name); - - switch (desc[i].type) { - case QEMU_OPT_STRING: - info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; - break; - case QEMU_OPT_BOOL: - info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; - break; - case QEMU_OPT_NUMBER: - info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; - break; - case QEMU_OPT_SIZE: - info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; - break; - } - - info->help = g_strdup(desc[i].help); - info->q_default = g_strdup(desc[i].def_value_str); - - QAPI_LIST_PREPEND(param_list, info); - } - - return param_list; -} - -/* remove repeated entry from the info list */ -static void cleanup_infolist(CommandLineParameterInfoList *head) -{ - CommandLineParameterInfoList *pre_entry, *cur, *del_entry; - - cur = head; - while (cur->next) { - pre_entry = head; - while (pre_entry != cur->next) { - if (!strcmp(pre_entry->value->name, cur->next->value->name)) { - del_entry = cur->next; - cur->next = cur->next->next; - del_entry->next = NULL; - qapi_free_CommandLineParameterInfoList(del_entry); - break; - } - pre_entry = pre_entry->next; - } - cur = cur->next; - } -} - -/* merge the description items of two parameter infolists */ -static void connect_infolist(CommandLineParameterInfoList *head, - CommandLineParameterInfoList *new) -{ - CommandLineParameterInfoList *cur; - - cur = head; - while (cur->next) { - cur = cur->next; - } - cur->next = new; -} - -/* access all the local QemuOptsLists for drive option */ -static CommandLineParameterInfoList *get_drive_infolist(void) -{ - CommandLineParameterInfoList *head = NULL, *cur; - int i; - - for (i = 0; drive_config_groups[i] != NULL; i++) { - if (!head) { - head = query_option_descs(drive_config_groups[i]->desc); - } else { - cur = query_option_descs(drive_config_groups[i]->desc); - connect_infolist(head, cur); - } - } - cleanup_infolist(head); - - return head; -} - -static CommandLineParameterInfo *objprop_to_cmdline_prop(ObjectProperty *prop) -{ - CommandLineParameterInfo *info; - - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(prop->name); - - if (g_str_equal(prop->type, "bool") || g_str_equal(prop->type, "OnOffAuto")) { - info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; - } else if (g_str_equal(prop->type, "int")) { - info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; - } else if (g_str_equal(prop->type, "size")) { - info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; - } else { - info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; - } - - if (prop->description) { - info->help = g_strdup(prop->description); - } - - return info; -} - -static CommandLineParameterInfoList *query_all_machine_properties(void) -{ - CommandLineParameterInfoList *params = NULL, *clpiter; - CommandLineParameterInfo *info; - GSList *machines, *curr_mach; - ObjectPropertyIterator op_iter; - ObjectProperty *prop; - bool is_new; - - machines = object_class_get_list(TYPE_MACHINE, false); - assert(machines); - - /* Loop over all machine classes */ - for (curr_mach = machines; curr_mach; curr_mach = curr_mach->next) { - object_class_property_iter_init(&op_iter, curr_mach->data); - /* ... and over the properties of each machine: */ - while ((prop = object_property_iter_next(&op_iter))) { - if (!prop->set) { - continue; - } - /* - * Check whether the property has already been put into the list - * (via another machine class) - */ - is_new = true; - for (clpiter = params; clpiter != NULL; clpiter = clpiter->next) { - if (g_str_equal(clpiter->value->name, prop->name)) { - is_new = false; - break; - } - } - /* If it hasn't been added before, add it now to the list */ - if (is_new) { - info = objprop_to_cmdline_prop(prop); - QAPI_LIST_PREPEND(params, info); - } - } - } - - g_slist_free(machines); - - /* Add entry for the "type" parameter */ - info = g_malloc0(sizeof(*info)); - info->name = g_strdup("type"); - info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; - info->help = g_strdup("machine type"); - QAPI_LIST_PREPEND(params, info); - - return params; -} - -CommandLineOptionInfoList *qmp_query_command_line_options(const char *option, - Error **errp) -{ - CommandLineOptionInfoList *conf_list = NULL; - CommandLineOptionInfo *info; - int i; - - for (i = 0; vm_config_groups[i] != NULL; i++) { - if (!option || !strcmp(option, vm_config_groups[i]->name)) { - info = g_malloc0(sizeof(*info)); - info->option = g_strdup(vm_config_groups[i]->name); - if (!strcmp("drive", vm_config_groups[i]->name)) { - info->parameters = get_drive_infolist(); - } else { - info->parameters = - query_option_descs(vm_config_groups[i]->desc); - } - QAPI_LIST_PREPEND(conf_list, info); - } - } - - if (!option || !strcmp(option, "machine")) { - info = g_malloc0(sizeof(*info)); - info->option = g_strdup("machine"); - info->parameters = query_all_machine_properties(); - QAPI_LIST_PREPEND(conf_list, info); - } - - if (conf_list == NULL) { - error_setg(errp, "invalid option name: %s", option); - } - - return conf_list; -} - QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) { return find_list(vm_config_groups, group, errp); From 971febb8f5e810c2d167ac9cb5bd1cdaf6ca688d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:19 +0200 Subject: [PATCH 0007/2884] hw/core: Move system emulation files to system_ss hotplug.c, qdev-hotplug.c and reset.c are not used by user emulation and need not be included in hwcore_ss. Move them to system_ss, where they belong, by letting the linker pull in the stubs when needed. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-8-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/meson.build | 14 +++----------- {hw/core => stubs}/hotplug-stubs.c | 0 stubs/meson.build | 1 + 3 files changed, 4 insertions(+), 11 deletions(-) rename {hw/core => stubs}/hotplug-stubs.c (100%) diff --git a/hw/core/meson.build b/hw/core/meson.build index e26f2e088c..f20d4143f7 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -3,7 +3,6 @@ hwcore_ss.add(files( 'bus.c', 'qdev-properties.c', 'qdev.c', - 'reset.c', 'resetcontainer.c', 'resettable.c', 'vmstate-if.c', @@ -12,16 +11,6 @@ hwcore_ss.add(files( 'clock.c', 'qdev-clock.c', )) -if have_system - hwcore_ss.add(files( - 'hotplug.c', - 'qdev-hotplug.c', - )) -else - hwcore_ss.add(files( - 'hotplug-stubs.c', - )) -endif common_ss.add(files('cpu-common.c')) common_ss.add(files('machine-smp.c')) @@ -40,6 +29,7 @@ system_ss.add(files( 'cpu-sysemu.c', 'fw-path-provider.c', 'gpio.c', + 'hotplug.c', 'loader.c', 'machine-hmp-cmds.c', 'machine-qmp-cmds.c', @@ -48,7 +38,9 @@ system_ss.add(files( 'null-machine.c', 'numa.c', 'qdev-fw.c', + 'qdev-hotplug.c', 'qdev-properties-system.c', + 'reset.c', 'sysbus.c', 'vm-change-state-handler.c', 'clock-vmstate.c', diff --git a/hw/core/hotplug-stubs.c b/stubs/hotplug-stubs.c similarity index 100% rename from hw/core/hotplug-stubs.c rename to stubs/hotplug-stubs.c diff --git a/stubs/meson.build b/stubs/meson.build index 0bf25e6ca5..f87f5c1110 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -14,6 +14,7 @@ stub_ss.add(files('fdset.c')) stub_ss.add(files('gdbstub.c')) stub_ss.add(files('get-vm-name.c')) stub_ss.add(files('graph-lock.c')) +stub_ss.add(files('hotplug-stubs.c')) if linux_io_uring.found() stub_ss.add(files('io_uring.c')) endif From 68621262bd4317dfcdd511d8005b8558c3ddc353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Apr 2024 17:53:20 +0200 Subject: [PATCH 0008/2884] hw: Include minimal source set in user emulation build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the files in hwcore_ss[] are required to link a user emulation binary. Have meson process the hw/ sub-directories if system emulation is selected, otherwise directly process hw/core/ to get hwcore_ss[], which is the only set required by user emulation. This removes about 10% from the time needed to run "../configure --disable-system --disable-tools --disable-guest-agent". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240404194757.9343-8-philmd@linaro.org> Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-9-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8c1271b884..84e59dcbb4 100644 --- a/meson.build +++ b/meson.build @@ -3451,8 +3451,12 @@ subdir('qom') subdir('authz') subdir('crypto') subdir('ui') -subdir('hw') subdir('gdbstub') +if have_system + subdir('hw') +else + subdir('hw/core') +endif if enable_modules libmodulecommon = static_library('module-common', files('module-common.c') + genh, pic: true, c_args: '-DBUILD_DSO') From d4d0ebb7da0489a2fff8c7150a38fa897ca3eea5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:21 +0200 Subject: [PATCH 0009/2884] stubs: remove obsolete stubs These file define functions are are not called from common code anymore. Delete those functions and, if applicable, the entire files. Signed-off-by: Paolo Bonzini Acked-by: Richard Henderson Message-ID: <20240408155330.522792-10-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- include/sysemu/sysemu.h | 2 -- stubs/isa-bus.c | 7 ------- stubs/meson.build | 3 --- stubs/module-opts.c | 2 -- stubs/monitor-core.c | 6 ------ stubs/pci-bus.c | 7 ------- stubs/qdev.c | 6 ------ stubs/qtest.c | 10 ---------- stubs/usb-dev-stub.c | 5 ----- 9 files changed, 48 deletions(-) delete mode 100644 stubs/isa-bus.c delete mode 100644 stubs/module-opts.c delete mode 100644 stubs/pci-bus.c diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index eb1dc1e4ed..5b4397eeb8 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -71,8 +71,6 @@ Chardev *serial_hd(int i); extern Chardev *parallel_hds[MAX_PARALLEL_PORTS]; -void hmp_info_usb(Monitor *mon, const QDict *qdict); - void add_boot_device_path(int32_t bootindex, DeviceState *dev, const char *suffix); char *get_boot_devices_list(size_t *size); diff --git a/stubs/isa-bus.c b/stubs/isa-bus.c deleted file mode 100644 index 522f448997..0000000000 --- a/stubs/isa-bus.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/isa/isa.h" - -ISADevice *isa_create_simple(ISABus *bus, const char *name) -{ - g_assert_not_reached(); -} diff --git a/stubs/meson.build b/stubs/meson.build index f87f5c1110..aa7120f711 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -22,13 +22,11 @@ stub_ss.add(files('iothread-lock.c')) if have_block stub_ss.add(files('iothread-lock-block.c')) endif -stub_ss.add(files('isa-bus.c')) stub_ss.add(files('is-daemonized.c')) if libaio.found() stub_ss.add(files('linux-aio.c')) endif stub_ss.add(files('migr-blocker.c')) -stub_ss.add(files('module-opts.c')) stub_ss.add(files('monitor.c')) stub_ss.add(files('monitor-core.c')) stub_ss.add(files('physmem.c')) @@ -57,7 +55,6 @@ if have_block or have_ga endif if have_system stub_ss.add(files('fw_cfg.c')) - stub_ss.add(files('pci-bus.c')) stub_ss.add(files('semihost.c')) stub_ss.add(files('usb-dev-stub.c')) stub_ss.add(files('xen-hw-stub.c')) diff --git a/stubs/module-opts.c b/stubs/module-opts.c deleted file mode 100644 index 5412429ea8..0000000000 --- a/stubs/module-opts.c +++ /dev/null @@ -1,2 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/config-file.h" diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c index afa477aae6..1894cdfe1f 100644 --- a/stubs/monitor-core.c +++ b/stubs/monitor-core.c @@ -12,10 +12,6 @@ Monitor *monitor_set_cur(Coroutine *co, Monitor *mon) return NULL; } -void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) -{ -} - void qapi_event_emit(QAPIEvent event, QDict *qdict) { } @@ -24,5 +20,3 @@ int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); } - - diff --git a/stubs/pci-bus.c b/stubs/pci-bus.c deleted file mode 100644 index a8932fa932..0000000000 --- a/stubs/pci-bus.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/pci/pci.h" - -PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) -{ - g_assert_not_reached(); -} diff --git a/stubs/qdev.c b/stubs/qdev.c index 6869f6f90a..7e957b3e52 100644 --- a/stubs/qdev.c +++ b/stubs/qdev.c @@ -20,9 +20,3 @@ void qapi_event_send_device_deleted(const char *device, { /* Nothing to do. */ } - -void qapi_event_send_device_unplug_guest_error(const char *device, - const char *path) -{ - /* Nothing to do. */ -} diff --git a/stubs/qtest.c b/stubs/qtest.c index 4666a49d7d..39e376eb67 100644 --- a/stubs/qtest.c +++ b/stubs/qtest.c @@ -13,13 +13,3 @@ /* Needed for qtest_allowed() */ bool qtest_allowed; - -bool qtest_driver(void) -{ - return false; -} - -int64_t qtest_get_virtual_clock(void) -{ - return 0; -} diff --git a/stubs/usb-dev-stub.c b/stubs/usb-dev-stub.c index aa557692b7..fcabe8429e 100644 --- a/stubs/usb-dev-stub.c +++ b/stubs/usb-dev-stub.c @@ -26,8 +26,3 @@ HumanReadableText *qmp_x_query_usb(Error **errp) error_setg(errp, "Support for USB devices not built-in"); return NULL; } - -void hmp_info_usb(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "Support for USB devices not built-in\n"); -} From 89857312f3245e589b161eb535261bae7b7fcce2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:22 +0200 Subject: [PATCH 0010/2884] hw/usb: move stubs out of stubs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the USB stubs are needed exactly when the Kconfig symbols are not enabled, they can be placed in hw/usb/ and conditionalized on CONFIG_USB. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-11-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- stubs/usb-dev-stub.c => hw/usb/bus-stub.c | 0 hw/usb/meson.build | 2 +- stubs/meson.build | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) rename stubs/usb-dev-stub.c => hw/usb/bus-stub.c (100%) diff --git a/stubs/usb-dev-stub.c b/hw/usb/bus-stub.c similarity index 100% rename from stubs/usb-dev-stub.c rename to hw/usb/bus-stub.c diff --git a/hw/usb/meson.build b/hw/usb/meson.build index aac3bb35f2..23f7f7acb5 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -9,7 +9,7 @@ system_ss.add(when: 'CONFIG_USB', if_true: files( 'desc-msos.c', 'libhw.c', 'pcap.c', -)) +), if_false: files('bus-stub.c')) # usb host adapters system_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c')) diff --git a/stubs/meson.build b/stubs/meson.build index aa7120f711..45616afbfa 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -56,7 +56,6 @@ endif if have_system stub_ss.add(files('fw_cfg.c')) stub_ss.add(files('semihost.c')) - stub_ss.add(files('usb-dev-stub.c')) stub_ss.add(files('xen-hw-stub.c')) stub_ss.add(files('virtio-md-pci.c')) else From f2604d8508a12a2060e854283fa076c9f09d1d10 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:23 +0200 Subject: [PATCH 0011/2884] hw/virtio: move stubs out of stubs/ Since the virtio memory device stubs are needed exactly when the Kconfig symbol is not enabled, they can be placed in hw/virtio/ and conditionalized on CONFIG_VIRTIO_MD. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-12-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/virtio/meson.build | 2 ++ stubs/virtio-md-pci.c => hw/virtio/virtio-md-stubs.c | 0 stubs/meson.build | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) rename stubs/virtio-md-pci.c => hw/virtio/virtio-md-stubs.c (100%) diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index d7f18c96e6..621fc65454 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -87,6 +87,8 @@ specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: system_virtio_ss) system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) +system_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-md-stubs.c')) + system_ss.add(files('virtio-hmp-cmds.c')) specific_ss.add_all(when: 'CONFIG_VIRTIO', if_true: specific_virtio_ss) diff --git a/stubs/virtio-md-pci.c b/hw/virtio/virtio-md-stubs.c similarity index 100% rename from stubs/virtio-md-pci.c rename to hw/virtio/virtio-md-stubs.c diff --git a/stubs/meson.build b/stubs/meson.build index 45616afbfa..60e32d363f 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -57,7 +57,6 @@ if have_system stub_ss.add(files('fw_cfg.c')) stub_ss.add(files('semihost.c')) stub_ss.add(files('xen-hw-stub.c')) - stub_ss.add(files('virtio-md-pci.c')) else stub_ss.add(files('qdev.c')) endif From 5837db465053f57414f671448b370a4b29250bae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:24 +0200 Subject: [PATCH 0012/2884] semihosting: move stubs out of stubs/ Since the semihosting stubs are needed exactly when the Kconfig symbols are not needed, move them to semihosting/ and conditionalize them on CONFIG_SEMIHOSTING and/or CONFIG_SYSTEM_ONLY. Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-13-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- semihosting/meson.build | 3 +++ stubs/semihost-all.c => semihosting/stubs-all.c | 0 stubs/semihost.c => semihosting/stubs-system.c | 0 stubs/meson.build | 2 -- 4 files changed, 3 insertions(+), 2 deletions(-) rename stubs/semihost-all.c => semihosting/stubs-all.c (100%) rename stubs/semihost.c => semihosting/stubs-system.c (100%) diff --git a/semihosting/meson.build b/semihosting/meson.build index b07cbd980f..34933e5a19 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -9,5 +9,8 @@ specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: fil 'uaccess.c', )) +common_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_false: files('stubs-all.c')) +system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_false: files('stubs-system.c')) + specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], if_true: files('arm-compat-semi.c')) diff --git a/stubs/semihost-all.c b/semihosting/stubs-all.c similarity index 100% rename from stubs/semihost-all.c rename to semihosting/stubs-all.c diff --git a/stubs/semihost.c b/semihosting/stubs-system.c similarity index 100% rename from stubs/semihost.c rename to semihosting/stubs-system.c diff --git a/stubs/meson.build b/stubs/meson.build index 60e32d363f..84ecaa4daa 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -55,9 +55,7 @@ if have_block or have_ga endif if have_system stub_ss.add(files('fw_cfg.c')) - stub_ss.add(files('semihost.c')) stub_ss.add(files('xen-hw-stub.c')) else stub_ss.add(files('qdev.c')) endif -stub_ss.add(files('semihost-all.c')) From 5643190b74df601719ca713ea21eea4997a26e78 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:25 +0200 Subject: [PATCH 0013/2884] ramfb: move stubs out of stubs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the ramfb stubs are needed exactly when the Kconfig symbols are not needed, move them to hw/display/ and compile them when ramfb.c is absent. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-14-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/display/meson.build | 2 +- stubs/ramfb.c => hw/display/ramfb-stubs.c | 0 stubs/meson.build | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) rename stubs/ramfb.c => hw/display/ramfb-stubs.c (100%) diff --git a/hw/display/meson.build b/hw/display/meson.build index f93a69f70f..4751aab3ba 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -3,7 +3,7 @@ hw_display_modules = {} system_ss.add(when: 'CONFIG_DDC', if_true: files('i2c-ddc.c')) system_ss.add(when: 'CONFIG_EDID', if_true: files('edid-generate.c', 'edid-region.c')) -system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c'), if_false: files('ramfb-stubs.c')) system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb-standalone.c')) system_ss.add(when: 'CONFIG_VGA_CIRRUS', if_true: files('cirrus_vga.c')) diff --git a/stubs/ramfb.c b/hw/display/ramfb-stubs.c similarity index 100% rename from stubs/ramfb.c rename to hw/display/ramfb-stubs.c diff --git a/stubs/meson.build b/stubs/meson.build index 84ecaa4daa..92887660e4 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -36,7 +36,6 @@ stub_ss.add(files('qmp-command-available.c')) stub_ss.add(files('qmp-quit.c')) stub_ss.add(files('qtest.c')) stub_ss.add(files('ram-block.c')) -stub_ss.add(files('ramfb.c')) stub_ss.add(files('replay.c')) stub_ss.add(files('runstate-check.c')) stub_ss.add(files('sysbus.c')) From 2c888febdfa0290a9e1d1f2133b2a24de47f6120 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:26 +0200 Subject: [PATCH 0014/2884] memory-device: move stubs out of stubs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the memory-device stubs are needed exactly when the Kconfig symbols are not needed, move them to hw/mem/. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-15-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- stubs/memory_device.c => hw/mem/memory-device-stubs.c | 0 hw/mem/meson.build | 1 + stubs/meson.build | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename stubs/memory_device.c => hw/mem/memory-device-stubs.c (100%) diff --git a/stubs/memory_device.c b/hw/mem/memory-device-stubs.c similarity index 100% rename from stubs/memory_device.c rename to hw/mem/memory-device-stubs.c diff --git a/hw/mem/meson.build b/hw/mem/meson.build index faee1fe936..1c1c6da24b 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -6,6 +6,7 @@ mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c')) mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c')) system_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_false: files('cxl_type3_stubs.c')) +system_ss.add(when: 'CONFIG_MEM_DEVICE', if_false: files('memory-device-stubs.c')) system_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) system_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) diff --git a/stubs/meson.build b/stubs/meson.build index 92887660e4..a4404e765a 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -31,7 +31,6 @@ stub_ss.add(files('monitor.c')) stub_ss.add(files('monitor-core.c')) stub_ss.add(files('physmem.c')) stub_ss.add(files('qemu-timer-notify-cb.c')) -stub_ss.add(files('memory_device.c')) stub_ss.add(files('qmp-command-available.c')) stub_ss.add(files('qmp-quit.c')) stub_ss.add(files('qtest.c')) From 857f504cf279a063e375c32e88d5cf3312d6b30c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:27 +0200 Subject: [PATCH 0015/2884] colo: move stubs out of stubs/ Since the colo stubs are needed exactly when the build options are not enabled, move them together with the code they stub. Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-16-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- stubs/colo.c => migration/colo-stubs.c | 0 migration/meson.build | 2 ++ stubs/colo-compare.c => net/colo-stubs.c | 0 net/meson.build | 2 ++ stubs/meson.build | 2 -- 5 files changed, 4 insertions(+), 2 deletions(-) rename stubs/colo.c => migration/colo-stubs.c (100%) rename stubs/colo-compare.c => net/colo-stubs.c (100%) diff --git a/stubs/colo.c b/migration/colo-stubs.c similarity index 100% rename from stubs/colo.c rename to migration/colo-stubs.c diff --git a/migration/meson.build b/migration/meson.build index 1eeb915ff6..f76b1ba328 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -34,6 +34,8 @@ system_ss.add(files( if get_option('replication').allowed() system_ss.add(files('colo-failover.c', 'colo.c')) +else + system_ss.add(files('colo-stubs.c')) endif system_ss.add(when: rdma, if_true: files('rdma.c')) diff --git a/stubs/colo-compare.c b/net/colo-stubs.c similarity index 100% rename from stubs/colo-compare.c rename to net/colo-stubs.c diff --git a/net/meson.build b/net/meson.build index 9432a588e4..e0cd71470e 100644 --- a/net/meson.build +++ b/net/meson.build @@ -20,6 +20,8 @@ if get_option('replication').allowed() or \ get_option('colo_proxy').allowed() system_ss.add(files('colo-compare.c')) system_ss.add(files('colo.c')) +else + system_ss.add(files('colo-stubs.c')) endif if get_option('colo_proxy').allowed() diff --git a/stubs/meson.build b/stubs/meson.build index a4404e765a..a252bffad0 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -42,8 +42,6 @@ stub_ss.add(files('target-get-monitor-def.c')) stub_ss.add(files('target-monitor-defs.c')) stub_ss.add(files('trace-control.c')) stub_ss.add(files('uuid.c')) -stub_ss.add(files('colo.c')) -stub_ss.add(files('colo-compare.c')) stub_ss.add(files('vmstate.c')) stub_ss.add(files('vm-stop.c')) stub_ss.add(files('win32-kbd-hook.c')) From 957eca9e73279453899193c053d1a47304e9aa58 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:28 +0200 Subject: [PATCH 0016/2884] stubs: split record/replay stubs further replay.c symbols are only needed by user mode emulation, with the exception of replay_mode that is needed by both user mode emulation (by way of qemu_guest_getrandom) and block layer tools (by way of util/qemu-timer.c). Since it is needed by libqemuutil rather than specific files that are part of the tools and emulators, split the replay_mode stub into its own file. Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-17-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- stubs/meson.build | 1 + stubs/replay-mode.c | 4 ++++ stubs/replay.c | 2 -- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 stubs/replay-mode.c diff --git a/stubs/meson.build b/stubs/meson.build index a252bffad0..4a524f5816 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -36,6 +36,7 @@ stub_ss.add(files('qmp-quit.c')) stub_ss.add(files('qtest.c')) stub_ss.add(files('ram-block.c')) stub_ss.add(files('replay.c')) +stub_ss.add(files('replay-mode.c')) stub_ss.add(files('runstate-check.c')) stub_ss.add(files('sysbus.c')) stub_ss.add(files('target-get-monitor-def.c')) diff --git a/stubs/replay-mode.c b/stubs/replay-mode.c new file mode 100644 index 0000000000..264be9d96c --- /dev/null +++ b/stubs/replay-mode.c @@ -0,0 +1,4 @@ +#include "qemu/osdep.h" +#include "sysemu/replay.h" + +ReplayMode replay_mode; diff --git a/stubs/replay.c b/stubs/replay.c index 42c92e4acb..b4dd6a566e 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -1,8 +1,6 @@ #include "qemu/osdep.h" #include "exec/replay-core.h" -ReplayMode replay_mode; - void replay_finish(void) { } From 3a15604900c4f433c970cc6294520a98f201287e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:29 +0200 Subject: [PATCH 0017/2884] stubs: include stubs only if needed Currently it is not documented anywhere why some functions need to be stubbed. Group the files in stubs/meson.build according to who needs them, both to reduce the size of the compilation and to clarify the use of stubs. Signed-off-by: Paolo Bonzini Message-ID: <20240408155330.522792-18-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- stubs/meson.build | 122 +++++++++++++++--------- stubs/{monitor.c => monitor-internal.c} | 0 2 files changed, 75 insertions(+), 47 deletions(-) rename stubs/{monitor.c => monitor-internal.c} (100%) diff --git a/stubs/meson.build b/stubs/meson.build index 4a524f5816..8ee1fd5753 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -1,58 +1,86 @@ -stub_ss.add(files('bdrv-next-monitor-owned.c')) -stub_ss.add(files('blk-commit-all.c')) -stub_ss.add(files('blk-exp-close-all.c')) -stub_ss.add(files('blockdev-close-all-bdrv-states.c')) -stub_ss.add(files('change-state-handler.c')) -stub_ss.add(files('cmos.c')) +# If possible, add new files to other directories, by using "if_false". +# If you need them here, try to add them under one of the if statements +# below, so that it is clear who needs the stubbed functionality. + stub_ss.add(files('cpu-get-clock.c')) -stub_ss.add(files('cpus-get-virtual-clock.c')) -stub_ss.add(files('qemu-timer-notify-cb.c')) -stub_ss.add(files('icount.c')) -stub_ss.add(files('dump.c')) -stub_ss.add(files('error-printf.c')) stub_ss.add(files('fdset.c')) -stub_ss.add(files('gdbstub.c')) -stub_ss.add(files('get-vm-name.c')) -stub_ss.add(files('graph-lock.c')) -stub_ss.add(files('hotplug-stubs.c')) -if linux_io_uring.found() - stub_ss.add(files('io_uring.c')) -endif stub_ss.add(files('iothread-lock.c')) -if have_block - stub_ss.add(files('iothread-lock-block.c')) -endif stub_ss.add(files('is-daemonized.c')) -if libaio.found() - stub_ss.add(files('linux-aio.c')) -endif -stub_ss.add(files('migr-blocker.c')) -stub_ss.add(files('monitor.c')) stub_ss.add(files('monitor-core.c')) -stub_ss.add(files('physmem.c')) -stub_ss.add(files('qemu-timer-notify-cb.c')) -stub_ss.add(files('qmp-command-available.c')) -stub_ss.add(files('qmp-quit.c')) -stub_ss.add(files('qtest.c')) -stub_ss.add(files('ram-block.c')) -stub_ss.add(files('replay.c')) stub_ss.add(files('replay-mode.c')) -stub_ss.add(files('runstate-check.c')) -stub_ss.add(files('sysbus.c')) -stub_ss.add(files('target-get-monitor-def.c')) -stub_ss.add(files('target-monitor-defs.c')) stub_ss.add(files('trace-control.c')) -stub_ss.add(files('uuid.c')) -stub_ss.add(files('vmstate.c')) -stub_ss.add(files('vm-stop.c')) -stub_ss.add(files('win32-kbd-hook.c')) -stub_ss.add(files('cpu-synchronize-state.c')) -if have_block or have_ga + +if have_block + stub_ss.add(files('bdrv-next-monitor-owned.c')) + stub_ss.add(files('blk-commit-all.c')) + stub_ss.add(files('blk-exp-close-all.c')) + stub_ss.add(files('blockdev-close-all-bdrv-states.c')) + stub_ss.add(files('change-state-handler.c')) + stub_ss.add(files('get-vm-name.c')) + stub_ss.add(files('iothread-lock-block.c')) + stub_ss.add(files('migr-blocker.c')) + stub_ss.add(files('physmem.c')) + stub_ss.add(files('ram-block.c')) stub_ss.add(files('replay-tools.c')) + stub_ss.add(files('runstate-check.c')) + stub_ss.add(files('uuid.c')) endif -if have_system - stub_ss.add(files('fw_cfg.c')) - stub_ss.add(files('xen-hw-stub.c')) -else + +if have_block or have_ga + # stubs for hooks in util/main-loop.c, util/async.c etc. + stub_ss.add(files('cpus-get-virtual-clock.c')) + stub_ss.add(files('icount.c')) + stub_ss.add(files('graph-lock.c')) + if linux_io_uring.found() + stub_ss.add(files('io_uring.c')) + endif + if libaio.found() + stub_ss.add(files('linux-aio.c')) + endif + stub_ss.add(files('qemu-timer-notify-cb.c')) + + # stubs for monitor + stub_ss.add(files('monitor-internal.c')) + stub_ss.add(files('qmp-command-available.c')) + stub_ss.add(files('qmp-quit.c')) +endif + +if have_block or have_user + stub_ss.add(files('qtest.c')) + stub_ss.add(files('vm-stop.c')) + stub_ss.add(files('vmstate.c')) + + # more symbols provided by the monitor + stub_ss.add(files('error-printf.c')) +endif + +if have_user + # Symbols that are used by hw/core. + stub_ss.add(files('cpu-synchronize-state.c')) stub_ss.add(files('qdev.c')) endif + +if have_system + # Symbols that are only needed in some configurations. Try not + # adding more of these. If the symbol is used in specific_ss, + # in particular, consider defining a preprocessor macro via + # Kconfig or configs/targets/. + stub_ss.add(files('dump.c')) + stub_ss.add(files('cmos.c')) + stub_ss.add(files('fw_cfg.c')) + stub_ss.add(files('target-get-monitor-def.c')) + stub_ss.add(files('target-monitor-defs.c')) + stub_ss.add(files('win32-kbd-hook.c')) + stub_ss.add(files('xen-hw-stub.c')) +endif + +if have_system or have_user + stub_ss.add(files('gdbstub.c')) + + # Also included in have_system for --disable-tcg builds + stub_ss.add(files('replay.c')) + + # Also included in have_system for tests/unit/test-qdev-global-props + stub_ss.add(files('hotplug-stubs.c')) + stub_ss.add(files('sysbus.c')) +endif diff --git a/stubs/monitor.c b/stubs/monitor-internal.c similarity index 100% rename from stubs/monitor.c rename to stubs/monitor-internal.c From 748e62dbf5065c7d166c827425c7797389b5f9fe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Apr 2024 17:53:30 +0200 Subject: [PATCH 0018/2884] stubs: move monitor_fdsets_cleanup with other fdset stubs Even though monitor_get_fd() has to remain separate because it is mocked by tests/unit/test-util-sockets, monitor_fdsets_cleanup() is logically part of the stubs for monitor/fds.c, so move it there. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240408155330.522792-19-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- stubs/fdset.c | 6 ++++++ stubs/monitor-internal.c | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/stubs/fdset.c b/stubs/fdset.c index 56b3663d58..d7c39a28ac 100644 --- a/stubs/fdset.c +++ b/stubs/fdset.c @@ -1,5 +1,7 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "monitor/monitor.h" +#include "../monitor/monitor-internal.h" int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) { @@ -15,3 +17,7 @@ int64_t monitor_fdset_dup_fd_find(int dup_fd) void monitor_fdset_dup_fd_remove(int dupfd) { } + +void monitor_fdsets_cleanup(void) +{ +} diff --git a/stubs/monitor-internal.c b/stubs/monitor-internal.c index 20786ac4ff..4fece49d53 100644 --- a/stubs/monitor-internal.c +++ b/stubs/monitor-internal.c @@ -1,7 +1,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "monitor/monitor.h" -#include "../monitor/monitor-internal.h" int monitor_get_fd(Monitor *mon, const char *name, Error **errp) { @@ -12,7 +11,3 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp) void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) { } - -void monitor_fdsets_cleanup(void) -{ -} From ab75ecb79be3b856f63bef4c91aef0dc17d405cb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Apr 2024 12:31:35 +0200 Subject: [PATCH 0019/2884] vga: optimize computation of dirty memory region The depth == 0 and depth == 15 have to be special cased because width * depth / 8 does not provide the correct scanline length. However, thanks to the recent reorganization of vga_draw_graphic() the correct value of VRAM bits per pixel is available in "bits". Use it (via the same "bwidth" computation that is used later in the function), thus restricting the slow path to the wraparound case. Signed-off-by: Paolo Bonzini --- hw/display/vga.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index 77f59e8c11..77d709a3d6 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1574,22 +1574,16 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) /* Horizontal pel panning bit 3 is only used in text mode. */ hpel = bits <= 8 ? s->params.hpel & 7 : 0; + bwidth = DIV_ROUND_UP(width * bits, 8); /* scanline length */ + if (hpel) { + bwidth += 4; + } region_start = (s->params.start_addr * 4); - region_end = region_start + (ram_addr_t)s->params.line_offset * height; - region_end += width * depth / 8; /* scanline length */ - region_end -= s->params.line_offset; - if (hpel) { - region_end += 4; - } - if (region_end > s->vbe_size || depth == 0 || depth == 15) { + region_end = region_start + (ram_addr_t)s->params.line_offset * (height - 1) + bwidth; + if (region_end > s->vbe_size) { /* - * We land here on: - * - wraps around (can happen with cirrus vbe modes) - * - depth == 0 (256 color palette video mode) - * - depth == 15 - * - * Take the safe and slow route: + * On wrap around take the safe and slow route: * - create a dirty bitmap snapshot for all vga memory. * - force shadowing (so all vga memory access goes * through vga_read_*() helpers). @@ -1667,10 +1661,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->params.line_compare, sr(s, VGA_SEQ_CLOCK_MODE)); #endif addr1 = (s->params.start_addr * 4); - bwidth = DIV_ROUND_UP(width * bits, 8); - if (hpel) { - bwidth += 4; - } y_start = -1; d = surface_data(surface); linesize = surface_stride(surface); From f89761d349999a3d898b35fd85737cf25114d44a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Apr 2024 13:27:59 +0200 Subject: [PATCH 0020/2884] vga: move dirty memory region code together Take into account split screen mode close to wrap around, which is the other special case for dirty memory region computation. Signed-off-by: Paolo Bonzini --- hw/display/vga.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index 77d709a3d6..e91a76bf76 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1596,6 +1596,10 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) region_end = s->vbe_size; force_shadow = true; } + if (s->params.line_compare < height) { + /* split screen mode */ + region_start = 0; + } /* * Check whether we can share the surface with the backend @@ -1667,10 +1671,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) y1 = 0; if (!full_update) { - if (s->params.line_compare < height) { - /* split screen mode */ - region_start = 0; - } snap = memory_region_snapshot_and_clear_dirty(&s->vram, region_start, region_end - region_start, DIRTY_MEMORY_VGA); From 1e1e48792a92652af05f59b4df2f643542c71d90 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Mar 2024 19:29:07 +0100 Subject: [PATCH 0021/2884] kvm: use configs/ definition to conditionalize debug support If an architecture adds support for KVM_CAP_SET_GUEST_DEBUG but QEMU does not have the necessary code, QEMU will fail to build after updating kernel headers. Avoid this by using a #define in config-target.h instead of KVM_CAP_SET_GUEST_DEBUG. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-accel-ops.c | 4 ++-- accel/kvm/kvm-all.c | 10 +++++----- configs/targets/aarch64-softmmu.mak | 1 + configs/targets/i386-softmmu.mak | 1 + configs/targets/ppc-softmmu.mak | 1 + configs/targets/ppc64-softmmu.mak | 1 + configs/targets/s390x-softmmu.mak | 1 + configs/targets/x86_64-softmmu.mak | 1 + include/sysemu/kvm.h | 2 +- include/sysemu/kvm_int.h | 2 +- 10 files changed, 15 insertions(+), 9 deletions(-) diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index b3c946dc4b..f5ac643fca 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -85,7 +85,7 @@ static bool kvm_cpus_are_resettable(void) return !kvm_enabled() || kvm_cpu_check_are_resettable(); } -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG static int kvm_update_guest_debug_ops(CPUState *cpu) { return kvm_update_guest_debug(cpu, 0); @@ -104,7 +104,7 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) ops->synchronize_state = kvm_cpu_synchronize_state; ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG ops->update_guest_debug = kvm_update_guest_debug_ops; ops->supports_guest_debug = kvm_supports_guest_debug; ops->insert_breakpoint = kvm_insert_breakpoint; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 931f74256e..d58916e33a 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2360,7 +2360,7 @@ static int kvm_init(MachineState *ms) s->sigmask_len = 8; accel_blocker_init(); -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG QTAILQ_INIT(&s->kvm_sw_breakpoints); #endif QLIST_INIT(&s->kvm_parked_vcpus); @@ -2544,7 +2544,7 @@ static int kvm_init(MachineState *ms) kvm_vm_attributes_allowed = (kvm_check_extension(s, KVM_CAP_VM_ATTRIBUTES) > 0); -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG kvm_has_guest_debug = (kvm_check_extension(s, KVM_CAP_SET_GUEST_DEBUG) > 0); #endif @@ -2553,7 +2553,7 @@ static int kvm_init(MachineState *ms) if (kvm_has_guest_debug) { kvm_sstep_flags = SSTEP_ENABLE; -#if defined KVM_CAP_SET_GUEST_DEBUG2 +#if defined TARGET_KVM_HAVE_GUEST_DEBUG int guest_debug_flags = kvm_check_extension(s, KVM_CAP_SET_GUEST_DEBUG2); @@ -3157,7 +3157,7 @@ bool kvm_arm_supports_user_irq(void) return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ); } -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, vaddr pc) { struct kvm_sw_breakpoint *bp; @@ -3317,7 +3317,7 @@ void kvm_remove_all_breakpoints(CPUState *cpu) } } -#endif /* !KVM_CAP_SET_GUEST_DEBUG */ +#endif /* !TARGET_KVM_HAVE_GUEST_DEBUG */ static int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset) { diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index b4338e9568..83c22391a6 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -1,5 +1,6 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_SUPPORTS_MTTCG=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml TARGET_NEED_FDT=y diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 6b3c99fc86..d61b507613 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=i386 TARGET_SUPPORTS_MTTCG=y TARGET_NEED_FDT=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index 774440108f..f3ea9c98f7 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=ppc TARGET_BIG_ENDIAN=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml TARGET_NEED_FDT=y diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index ddf0c39617..1db8d8381d 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -2,5 +2,6 @@ TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml TARGET_NEED_FDT=y diff --git a/configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak index 70d2f9f0ba..b22218aacc 100644 --- a/configs/targets/s390x-softmmu.mak +++ b/configs/targets/s390x-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=s390x TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-virt-kvm.xml gdb-xml/s390-gs.xml diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index 197817c943..c5f882e5ba 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SUPPORTS_MTTCG=y TARGET_NEED_FDT=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index fad9a7e8ff..2cba899270 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -224,7 +224,7 @@ void kvm_flush_coalesced_mmio_buffer(void); * calling down to kvm_arch_update_guest_debug after the generic * fields have been set. */ -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); #else static inline int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 882e37e12c..94488d2c1a 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -78,7 +78,7 @@ struct KVMState struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; bool coalesced_flush_in_progress; int vcpu_events; -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; #endif int max_nested_state_len; From 85fa9acda887438feb0711970bb528959a37568e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Mar 2024 15:01:51 +0100 Subject: [PATCH 0022/2884] hw: Add compat machines for 9.1 Add 9.1 machine types for arm/i440fx/m68k/q35/s390x/spapr. Reviewed-by: Cornelia Huck Acked-by: Thomas Huth Reviewed-by: Harsh Prateek Bora Reviewed-by: Zhao Liu Cc: Gavin Shan Signed-off-by: Paolo Bonzini --- hw/arm/virt.c | 11 +++++++++-- hw/core/machine.c | 3 +++ hw/i386/pc.c | 3 +++ hw/i386/pc_piix.c | 17 ++++++++++++++--- hw/i386/pc_q35.c | 14 ++++++++++++-- hw/m68k/virt.c | 11 +++++++++-- hw/ppc/spapr.c | 17 ++++++++++++++--- hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++- include/hw/boards.h | 3 +++ include/hw/i386/pc.h | 3 +++ 10 files changed, 83 insertions(+), 13 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a9a913aead..c9119ef384 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3223,10 +3223,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_9_0_options(MachineClass *mc) +static void virt_machine_9_1_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(9, 0) +DEFINE_VIRT_MACHINE_AS_LATEST(9, 1) + +static void virt_machine_9_0_options(MachineClass *mc) +{ + virt_machine_9_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); +} +DEFINE_VIRT_MACHINE(9, 0) static void virt_machine_8_2_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index 37ede0e7d4..a92bec2314 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -33,6 +33,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_9_0[] = {}; +const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); + GlobalProperty hw_compat_8_2[] = { { "migration", "zero-page-detection", "legacy"}, { TYPE_VIRTIO_IOMMU_PCI, "granule", "4k" }, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 5c21b0c4db..b0d818b209 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -78,6 +78,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, +GlobalProperty pc_compat_9_0[] = {}; +const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0); + GlobalProperty pc_compat_8_2[] = {}; const size_t pc_compat_8_2_len = G_N_ELEMENTS(pc_compat_8_2); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 18ba076609..8850c49c66 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -513,13 +513,26 @@ static void pc_i440fx_machine_options(MachineClass *m) "Use a different south bridge than PIIX3"); } -static void pc_i440fx_9_0_machine_options(MachineClass *m) +static void pc_i440fx_9_1_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = true; } +DEFINE_I440FX_MACHINE(v9_1, "pc-i440fx-9.1", NULL, + pc_i440fx_9_1_machine_options); + +static void pc_i440fx_9_0_machine_options(MachineClass *m) +{ + pc_i440fx_9_1_machine_options(m); + m->alias = NULL; + m->is_default = false; + + compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); + compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); +} + DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL, pc_i440fx_9_0_machine_options); @@ -528,8 +541,6 @@ static void pc_i440fx_8_2_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_9_0_machine_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index c7bc8a2041..6e1180d4b6 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -365,12 +365,23 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_9_0_machine_options(MachineClass *m) +static void pc_q35_9_1_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v9_1, "pc-q35-9.1", NULL, + pc_q35_9_1_machine_options); + +static void pc_q35_9_0_machine_options(MachineClass *m) +{ + pc_q35_9_1_machine_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); + compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); +} + DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL, pc_q35_9_0_machine_options); @@ -378,7 +389,6 @@ static void pc_q35_8_2_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_9_0_machine_options(m); - m->alias = NULL; m->max_cpus = 1024; compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index b8e5e102e6..09bc9bdfef 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -357,10 +357,17 @@ type_init(virt_machine_register_types) } \ type_init(machvirt_machine_##major##_##minor##_init); -static void virt_machine_9_0_options(MachineClass *mc) +static void virt_machine_9_1_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE(9, 0, true) +DEFINE_VIRT_MACHINE(9, 1, true) + +static void virt_machine_9_0_options(MachineClass *mc) +{ + virt_machine_9_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); +} +DEFINE_VIRT_MACHINE(9, 0, false) static void virt_machine_8_2_options(MachineClass *mc) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index e9bc97fee0..36ada4d0ba 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4806,14 +4806,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) type_init(spapr_machine_register_##suffix) /* - * pseries-9.0 + * pseries-9.1 */ -static void spapr_machine_9_0_class_options(MachineClass *mc) +static void spapr_machine_9_1_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(9_0, "9.0", true); +DEFINE_SPAPR_MACHINE(9_1, "9.1", true); + +/* + * pseries-9.0 + */ +static void spapr_machine_9_0_class_options(MachineClass *mc) +{ + spapr_machine_9_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); +} + +DEFINE_SPAPR_MACHINE(9_0, "9.0", false); /* * pseries-8.2 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index b1dcb3857f..7cebbf6c02 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -859,14 +859,26 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +static void ccw_machine_9_1_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_9_1_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(9_1, "9.1", true); + static void ccw_machine_9_0_instance_options(MachineState *machine) { + ccw_machine_9_1_instance_options(machine); } static void ccw_machine_9_0_class_options(MachineClass *mc) { + ccw_machine_9_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); } -DEFINE_CCW_MACHINE(9_0, "9.0", true); +DEFINE_CCW_MACHINE(9_0, "9.0", false); static void ccw_machine_8_2_instance_options(MachineState *machine) { diff --git a/include/hw/boards.h b/include/hw/boards.h index 8b8f6d5c00..50e0cf4278 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -425,6 +425,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_9_0[]; +extern const size_t hw_compat_9_0_len; + extern GlobalProperty hw_compat_8_2[]; extern const size_t hw_compat_8_2_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 27a68071d7..349f79df08 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -198,6 +198,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_9_0[]; +extern const size_t pc_compat_9_0_len; + extern GlobalProperty pc_compat_8_2[]; extern const size_t pc_compat_8_2_len; From 513ba32dccc659c80722b3a43233b26eaa50309a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 18 Mar 2024 16:53:36 +0100 Subject: [PATCH 0023/2884] target/i386: add guest-phys-bits cpu property Allows to set guest-phys-bits (cpuid leaf 80000008, eax[23:16]) via -cpu $model,guest-phys-bits=$nr. Signed-off-by: Gerd Hoffmann Message-ID: <20240318155336.156197-3-kraxel@redhat.com> Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 4 +++- target/i386/cpu.c | 22 ++++++++++++++++++++++ target/i386/cpu.h | 8 ++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b0d818b209..41909f7bd7 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -78,7 +78,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, -GlobalProperty pc_compat_9_0[] = {}; +GlobalProperty pc_compat_9_0[] = { + { TYPE_X86_CPU, "guest-phys-bits", "0" }, +}; const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0); GlobalProperty pc_compat_8_2[] = {}; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 33760a2ee1..eef3d08473 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6570,6 +6570,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { /* 64 bit processor */ *eax |= (cpu_x86_virtual_addr_width(env) << 8); + *eax |= (cpu->guest_phys_bits << 16); } *ebx = env->features[FEAT_8000_0008_EBX]; if (cs->nr_cores * cs->nr_threads > 1) { @@ -7329,6 +7330,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) goto out; } + if (cpu->guest_phys_bits == -1) { + /* + * If it was not set by the user, or by the accelerator via + * cpu_exec_realizefn, clear. + */ + cpu->guest_phys_bits = 0; + } + if (cpu->ucode_rev == 0) { /* * The default is the same as KVM's. Note that this check @@ -7379,6 +7388,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (cpu->phys_bits == 0) { cpu->phys_bits = TCG_PHYS_ADDR_BITS; } + if (cpu->guest_phys_bits && + (cpu->guest_phys_bits > cpu->phys_bits || + cpu->guest_phys_bits < 32)) { + error_setg(errp, "guest-phys-bits should be between 32 and %u " + " (but is %u)", + cpu->phys_bits, cpu->guest_phys_bits); + return; + } } else { /* For 32 bit systems don't use the user set value, but keep * phys_bits consistent with what we tell the guest. @@ -7387,6 +7404,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) error_setg(errp, "phys-bits is not user-configurable in 32 bit"); return; } + if (cpu->guest_phys_bits != 0) { + error_setg(errp, "guest-phys-bits is not user-configurable in 32 bit"); + return; + } if (env->features[FEAT_1_EDX] & (CPUID_PSE36 | CPUID_PAE)) { cpu->phys_bits = 36; @@ -7887,6 +7908,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("x-force-features", X86CPU, force_features, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0), + DEFINE_PROP_UINT32("guest-phys-bits", X86CPU, guest_phys_bits, -1), DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0), DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 6b05738079..6112e27bfd 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2027,6 +2027,14 @@ struct ArchCPU { /* Number of physical address bits supported */ uint32_t phys_bits; + /* + * Number of guest physical address bits available. Usually this is + * identical to host physical address bits. With NPT or EPT 4-level + * paging, guest physical address space might be restricted to 48 bits + * even if the host cpu supports more physical address bits. + */ + uint32_t guest_phys_bits; + /* in order to simplify APIC support, we leave this pointer to the user */ struct DeviceState *apic_state; From 0d08c423688edcca857f88dab20f1fc56de2b281 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 18 Mar 2024 16:53:35 +0100 Subject: [PATCH 0024/2884] kvm: add support for guest physical bits Query kvm for supported guest physical address bits, in cpuid function 80000008, eax[23:16]. Usually this is identical to host physical address bits. With NPT or EPT being used this might be restricted to 48 (max 4-level paging address space size) even if the host cpu supports more physical address bits. When set pass this to the guest, using cpuid too. Guest firmware can use this to figure how big the usable guest physical address space is, so PCI bar mapping are actually reachable. Signed-off-by: Gerd Hoffmann Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-ID: <20240318155336.156197-2-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm-cpu.c | 50 ++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 9c791b7b05..f76972e47e 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -18,10 +18,32 @@ #include "kvm_i386.h" #include "hw/core/accel-cpu.h" +static void kvm_set_guest_phys_bits(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + uint32_t eax, guest_phys_bits; + + eax = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x80000008, 0, R_EAX); + guest_phys_bits = (eax >> 16) & 0xff; + if (!guest_phys_bits) { + return; + } + cpu->guest_phys_bits = guest_phys_bits; + if (cpu->guest_phys_bits > cpu->phys_bits) { + cpu->guest_phys_bits = cpu->phys_bits; + } + + if (cpu->host_phys_bits && cpu->host_phys_bits_limit && + cpu->guest_phys_bits > cpu->host_phys_bits_limit) { + cpu->guest_phys_bits = cpu->host_phys_bits_limit; + } +} + static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + bool ret; /* * The realize order is important, since x86_cpu_realize() checks if @@ -32,13 +54,15 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * * realize order: * - * x86_cpu_realize(): - * -> x86_cpu_expand_features() - * -> cpu_exec_realizefn(): - * -> accel_cpu_common_realize() - * kvm_cpu_realizefn() -> host_cpu_realizefn() - * -> cpu_common_realizefn() - * -> check/update ucode_rev, phys_bits, mwait + * x86_cpu_realizefn(): + * x86_cpu_expand_features() + * cpu_exec_realizefn(): + * accel_cpu_common_realize() + * kvm_cpu_realizefn() + * host_cpu_realizefn() + * kvm_set_guest_phys_bits() + * check/update ucode_rev, phys_bits, guest_phys_bits, mwait + * cpu_common_realizefn() (via xcc->parent_realize) */ if (cpu->max_features) { if (enable_cpu_pm && kvm_has_waitpkg()) { @@ -50,7 +74,17 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) MSR_IA32_UCODE_REV); } } - return host_cpu_realizefn(cs, errp); + ret = host_cpu_realizefn(cs, errp); + if (!ret) { + return ret; + } + + if ((env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) && + cpu->guest_phys_bits == -1) { + kvm_set_guest_phys_bits(cs); + } + + return true; } static bool lmce_supported(void) From a5acf4f26c208a05d05ef1bde65553ce2ab5e5d0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 29 Feb 2024 01:36:43 -0500 Subject: [PATCH 0025/2884] i386/kvm: Move architectural CPUID leaf generation to separate helper Move the architectural (for lack of a better term) CPUID leaf generation to a separate helper so that the generation code can be reused by TDX, which needs to generate a canonical VM-scoped configuration. For now this is just a cleanup, so keep the function static. Signed-off-by: Sean Christopherson Signed-off-by: Xiaoyao Li Message-ID: <20240229063726.610065-23-xiaoyao.li@intel.com> Reviewed-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 449 +++++++++++++++++++++--------------------- 1 file changed, 227 insertions(+), 222 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e68cbe9293..f1b59011d1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1706,6 +1706,231 @@ static void kvm_init_nested_state(CPUX86State *env) } } +static uint32_t kvm_x86_build_cpuid(CPUX86State *env, + struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i) +{ + uint32_t limit, i, j; + uint32_t unused; + struct kvm_cpuid_entry2 *c; + + cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); + + for (i = 0; i <= limit; i++) { + j = 0; + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + switch (i) { + case 2: { + /* Keep reading function 2 till all the input is received */ + int times; + + c->function = i; + c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC | + KVM_CPUID_FLAG_STATE_READ_NEXT; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + times = c->eax & 0xff; + + for (j = 1; j < times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + c->function = i; + c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + break; + } + case 0x1f: + if (env->nr_dies < 2) { + cpuid_i--; + break; + } + /* fallthrough */ + case 4: + case 0xb: + case 0xd: + for (j = 0; ; j++) { + if (i == 0xd && j == 64) { + break; + } + + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (i == 4 && c->eax == 0) { + break; + } + if (i == 0xb && !(c->ecx & 0xff00)) { + break; + } + if (i == 0x1f && !(c->ecx & 0xff00)) { + break; + } + if (i == 0xd && c->eax == 0) { + continue; + } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + } + break; + case 0x12: + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (j > 1 && (c->eax & 0xf) != 1) { + break; + } + + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + } + break; + case 0x7: + case 0x14: + case 0x1d: + case 0x1e: { + uint32_t times; + + c->function = i; + c->index = 0; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + times = c->eax; + + for (j = 1; j <= times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + c->function = i; + c->index = j; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + break; + } + default: + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + if (!c->eax && !c->ebx && !c->ecx && !c->edx) { + /* + * KVM already returns all zeroes if a CPUID entry is missing, + * so we can omit it and avoid hitting KVM's 80-entry limit. + */ + cpuid_i--; + } + break; + } + } + + if (limit >= 0x0a) { + uint32_t eax, edx; + + cpu_x86_cpuid(env, 0x0a, 0, &eax, &unused, &unused, &edx); + + has_architectural_pmu_version = eax & 0xff; + if (has_architectural_pmu_version > 0) { + num_architectural_pmu_gp_counters = (eax & 0xff00) >> 8; + + /* Shouldn't be more than 32, since that's the number of bits + * available in EBX to tell us _which_ counters are available. + * Play it safe. + */ + if (num_architectural_pmu_gp_counters > MAX_GP_COUNTERS) { + num_architectural_pmu_gp_counters = MAX_GP_COUNTERS; + } + + if (has_architectural_pmu_version > 1) { + num_architectural_pmu_fixed_counters = edx & 0x1f; + + if (num_architectural_pmu_fixed_counters > MAX_FIXED_COUNTERS) { + num_architectural_pmu_fixed_counters = MAX_FIXED_COUNTERS; + } + } + } + } + + cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); + + for (i = 0x80000000; i <= limit; i++) { + j = 0; + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + + switch (i) { + case 0x8000001d: + /* Query for all AMD cache information leaves */ + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (c->eax == 0) { + break; + } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + } + break; + default: + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + if (!c->eax && !c->ebx && !c->ecx && !c->edx) { + /* + * KVM already returns all zeroes if a CPUID entry is missing, + * so we can omit it and avoid hitting KVM's 80-entry limit. + */ + cpuid_i--; + } + break; + } + } + + /* Call Centaur's CPUID instructions they are supported. */ + if (env->cpuid_xlevel2 > 0) { + cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); + + for (i = 0xC0000000; i <= limit; i++) { + j = 0; + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + } + + return cpuid_i; + +full: + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); + abort(); +} + int kvm_arch_init_vcpu(CPUState *cs) { struct { @@ -1722,8 +1947,7 @@ int kvm_arch_init_vcpu(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - uint32_t limit, i, j, cpuid_i; - uint32_t unused; + uint32_t cpuid_i; struct kvm_cpuid_entry2 *c; uint32_t signature[3]; int kvm_base = KVM_CPUID_SIGNATURE; @@ -1876,8 +2100,6 @@ int kvm_arch_init_vcpu(CPUState *cs) c->edx = env->features[FEAT_KVM_HINTS]; } - cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); - if (cpu->kvm_pv_enforce_cpuid) { r = kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); if (r < 0) { @@ -1888,224 +2110,7 @@ int kvm_arch_init_vcpu(CPUState *cs) } } - for (i = 0; i <= limit; i++) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported level value: 0x%x\n", limit); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - - switch (i) { - case 2: { - /* Keep reading function 2 till all the input is received */ - int times; - - c->function = i; - c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC | - KVM_CPUID_FLAG_STATE_READ_NEXT; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - times = c->eax & 0xff; - - for (j = 1; j < times; ++j) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:2):eax & 0xf = 0x%x\n", times); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - c->function = i; - c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - } - break; - } - case 0x1f: - if (env->nr_dies < 2) { - cpuid_i--; - break; - } - /* fallthrough */ - case 4: - case 0xb: - case 0xd: - for (j = 0; ; j++) { - if (i == 0xd && j == 64) { - break; - } - - c->function = i; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - c->index = j; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - - if (i == 4 && c->eax == 0) { - break; - } - if (i == 0xb && !(c->ecx & 0xff00)) { - break; - } - if (i == 0x1f && !(c->ecx & 0xff00)) { - break; - } - if (i == 0xd && c->eax == 0) { - continue; - } - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - } - break; - case 0x12: - for (j = 0; ; j++) { - c->function = i; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - c->index = j; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - - if (j > 1 && (c->eax & 0xf) != 1) { - break; - } - - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x12,ecx:0x%x)\n", j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - } - break; - case 0x7: - case 0x14: - case 0x1d: - case 0x1e: { - uint32_t times; - - c->function = i; - c->index = 0; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - times = c->eax; - - for (j = 1; j <= times; ++j) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - c->function = i; - c->index = j; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - } - break; - } - default: - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - if (!c->eax && !c->ebx && !c->ecx && !c->edx) { - /* - * KVM already returns all zeroes if a CPUID entry is missing, - * so we can omit it and avoid hitting KVM's 80-entry limit. - */ - cpuid_i--; - } - break; - } - } - - if (limit >= 0x0a) { - uint32_t eax, edx; - - cpu_x86_cpuid(env, 0x0a, 0, &eax, &unused, &unused, &edx); - - has_architectural_pmu_version = eax & 0xff; - if (has_architectural_pmu_version > 0) { - num_architectural_pmu_gp_counters = (eax & 0xff00) >> 8; - - /* Shouldn't be more than 32, since that's the number of bits - * available in EBX to tell us _which_ counters are available. - * Play it safe. - */ - if (num_architectural_pmu_gp_counters > MAX_GP_COUNTERS) { - num_architectural_pmu_gp_counters = MAX_GP_COUNTERS; - } - - if (has_architectural_pmu_version > 1) { - num_architectural_pmu_fixed_counters = edx & 0x1f; - - if (num_architectural_pmu_fixed_counters > MAX_FIXED_COUNTERS) { - num_architectural_pmu_fixed_counters = MAX_FIXED_COUNTERS; - } - } - } - } - - cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); - - for (i = 0x80000000; i <= limit; i++) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - - switch (i) { - case 0x8000001d: - /* Query for all AMD cache information leaves */ - for (j = 0; ; j++) { - c->function = i; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - c->index = j; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - - if (c->eax == 0) { - break; - } - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - } - break; - default: - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - if (!c->eax && !c->ebx && !c->ecx && !c->edx) { - /* - * KVM already returns all zeroes if a CPUID entry is missing, - * so we can omit it and avoid hitting KVM's 80-entry limit. - */ - cpuid_i--; - } - break; - } - } - - /* Call Centaur's CPUID instructions they are supported. */ - if (env->cpuid_xlevel2 > 0) { - cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); - - for (i = 0xC0000000; i <= limit; i++) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - } - } - + cpuid_i = kvm_x86_build_cpuid(env, cpuid_data.entries, cpuid_i); cpuid_data.cpuid.nent = cpuid_i; if (((env->cpuid_version >> 8)&0xF) >= 6 From c895fa54e3060c5ac6f3888dce96c9b78626072b Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 20 Mar 2024 17:31:38 +0800 Subject: [PATCH 0026/2884] target/i386: Introduce Icelake-Server-v7 to enable TSX When start L2 guest with both L1/L2 using Icelake-Server-v3 or above, QEMU reports below warning: "warning: host doesn't support requested feature: MSR(10AH).taa-no [bit 8]" Reason is QEMU Icelake-Server-v3 has TSX feature disabled but enables taa-no bit. It's meaningless that TSX isn't supported but still claim TSX is secure. So L1 KVM doesn't expose taa-no to L2 if TSX is unsupported, then starting L2 triggers the warning. Fix it by introducing a new version Icelake-Server-v7 which has both TSX and taa-no features. Then guest can use TSX securely when it see taa-no. This matches the production Icelake which supports TSX and isn't susceptible to TSX Async Abort (TAA) vulnerabilities, a.k.a, taa-no. Ideally, TSX should have being enabled together with taa-no since v3, but for compatibility, we'd better to add v7 to enable it. Fixes: d965dc35592d ("target/i386: Add ARCH_CAPABILITIES related bits into Icelake-Server CPU model") Tested-by: Xiangfei Ma Signed-off-by: Zhenzhong Duan Message-ID: <20240320093138.80267-2-zhenzhong.duan@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index eef3d08473..3f4b121468 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3822,6 +3822,16 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } }, }, + { + .version = 7, + .note = "TSX, taa-no", + .props = (PropValue[]) { + /* Restore TSX features removed by -v2 above */ + { "hle", "on" }, + { "rtm", "on" }, + { /* end of list */ } + }, + }, { /* end of list */ } } }, From 6e82d3b6220777667968a04c87e1667f164ebe88 Mon Sep 17 00:00:00 2001 From: Tao Su Date: Wed, 20 Mar 2024 10:10:44 +0800 Subject: [PATCH 0027/2884] target/i386: Add new CPU model SierraForest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to table 1-2 in Intel Architecture Instruction Set Extensions and Future Features (rev 051) [1], SierraForest has the following new features which have already been virtualized: - CMPCCXADD CPUID.(EAX=7,ECX=1):EAX[bit 7] - AVX-IFMA CPUID.(EAX=7,ECX=1):EAX[bit 23] - AVX-VNNI-INT8 CPUID.(EAX=7,ECX=1):EDX[bit 4] - AVX-NE-CONVERT CPUID.(EAX=7,ECX=1):EDX[bit 5] Add above features to new CPU model SierraForest. Comparing with GraniteRapids CPU model, SierraForest bare-metal removes the following features: - HLE CPUID.(EAX=7,ECX=0):EBX[bit 4] - RTM CPUID.(EAX=7,ECX=0):EBX[bit 11] - AVX512F CPUID.(EAX=7,ECX=0):EBX[bit 16] - AVX512DQ CPUID.(EAX=7,ECX=0):EBX[bit 17] - AVX512_IFMA CPUID.(EAX=7,ECX=0):EBX[bit 21] - AVX512CD CPUID.(EAX=7,ECX=0):EBX[bit 28] - AVX512BW CPUID.(EAX=7,ECX=0):EBX[bit 30] - AVX512VL CPUID.(EAX=7,ECX=0):EBX[bit 31] - AVX512_VBMI CPUID.(EAX=7,ECX=0):ECX[bit 1] - AVX512_VBMI2 CPUID.(EAX=7,ECX=0):ECX[bit 6] - AVX512_VNNI CPUID.(EAX=7,ECX=0):ECX[bit 11] - AVX512_BITALG CPUID.(EAX=7,ECX=0):ECX[bit 12] - AVX512_VPOPCNTDQ CPUID.(EAX=7,ECX=0):ECX[bit 14] - LA57 CPUID.(EAX=7,ECX=0):ECX[bit 16] - TSXLDTRK CPUID.(EAX=7,ECX=0):EDX[bit 16] - AMX-BF16 CPUID.(EAX=7,ECX=0):EDX[bit 22] - AVX512_FP16 CPUID.(EAX=7,ECX=0):EDX[bit 23] - AMX-TILE CPUID.(EAX=7,ECX=0):EDX[bit 24] - AMX-INT8 CPUID.(EAX=7,ECX=0):EDX[bit 25] - AVX512_BF16 CPUID.(EAX=7,ECX=1):EAX[bit 5] - fast zero-length MOVSB CPUID.(EAX=7,ECX=1):EAX[bit 10] - fast short CMPSB, SCASB CPUID.(EAX=7,ECX=1):EAX[bit 12] - AMX-FP16 CPUID.(EAX=7,ECX=1):EAX[bit 21] - PREFETCHI CPUID.(EAX=7,ECX=1):EDX[bit 14] - XFD CPUID.(EAX=0xD,ECX=1):EAX[bit 4] - EPT_PAGE_WALK_LENGTH_5 VMX_EPT_VPID_CAP(0x48c)[bit 7] Add all features of GraniteRapids CPU model except above features to SierraForest CPU model. SierraForest doesn’t support TSX and RTM but supports TAA_NO. When RTM is not enabled in host, KVM will not report TAA_NO. So, just don't include TAA_NO in SierraForest CPU model. [1] https://cdrdv2.intel.com/v1/dl/getContent/671368 Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Signed-off-by: Tao Su Message-ID: <20240320021044.508263-1-tao1.su@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3f4b121468..c295491d8a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -4109,6 +4109,132 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ }, }, }, + { + .name = "SierraForest", + .level = 0x23, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 175, + .stepping = 0, + /* + * please keep the ascending order so that we can have a clear view of + * bit position of each feature. + */ + .features[FEAT_1_EDX] = + CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC | + CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | + CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | + CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX | CPUID_FXSR | + CPUID_SSE | CPUID_SSE2, + .features[FEAT_1_ECX] = + CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 | + CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | + CPUID_EXT_POPCNT | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_AES | + CPUID_EXT_XSAVE | CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_WBNOINVD, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | + CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_SHA_NI, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_BUS_LOCK_DETECT, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_SERIALIZE | + CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_ARCH_CAPABILITIES | + CPUID_7_0_EDX_SPEC_CTRL_SSBD, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_IBRS_ALL | + MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | MSR_ARCH_CAP_MDS_NO | + MSR_ARCH_CAP_PSCHANGE_MC_NO | MSR_ARCH_CAP_SBDR_SSDP_NO | + MSR_ARCH_CAP_FBSDP_NO | MSR_ARCH_CAP_PSDP_NO | + MSR_ARCH_CAP_PBRSB_NO, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_CMPCCXADD | + CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_AVX_IFMA, + .features[FEAT_7_1_EDX] = + CPUID_7_1_EDX_AVX_VNNI_INT8 | CPUID_7_1_EDX_AVX_NE_CONVERT, + .features[FEAT_7_2_EDX] = + CPUID_7_2_EDX_MCDT_NO, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | VMX_VM_ENTRY_LOAD_IA32_EFER, + .features[FEAT_VMX_EPT_VPID_CAPS] = + MSR_VMX_EPT_EXECONLY | MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | + MSR_VMX_EPT_INVVPID_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_VMWRITE_VMEXIT, + .features[FEAT_VMX_PINBASED_CTLS] = + VMX_PIN_BASED_EXT_INTR_MASK | VMX_PIN_BASED_NMI_EXITING | + VMX_PIN_BASED_VIRTUAL_NMIS | VMX_PIN_BASED_VMX_PREEMPTION_TIMER | + VMX_PIN_BASED_POSTED_INTR, + .features[FEAT_VMX_PROCBASED_CTLS] = + VMX_CPU_BASED_VIRTUAL_INTR_PENDING | + VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_INVLPG_EXITING | VMX_CPU_BASED_MWAIT_EXITING | + VMX_CPU_BASED_RDPMC_EXITING | VMX_CPU_BASED_RDTSC_EXITING | + VMX_CPU_BASED_CR3_LOAD_EXITING | VMX_CPU_BASED_CR3_STORE_EXITING | + VMX_CPU_BASED_CR8_LOAD_EXITING | VMX_CPU_BASED_CR8_STORE_EXITING | + VMX_CPU_BASED_TPR_SHADOW | VMX_CPU_BASED_VIRTUAL_NMI_PENDING | + VMX_CPU_BASED_MOV_DR_EXITING | VMX_CPU_BASED_UNCOND_IO_EXITING | + VMX_CPU_BASED_USE_IO_BITMAPS | VMX_CPU_BASED_MONITOR_TRAP_FLAG | + VMX_CPU_BASED_USE_MSR_BITMAPS | VMX_CPU_BASED_MONITOR_EXITING | + VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + VMX_SECONDARY_EXEC_ENABLE_EPT | VMX_SECONDARY_EXEC_DESC | + VMX_SECONDARY_EXEC_RDTSCP | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + VMX_SECONDARY_EXEC_ENABLE_VPID | VMX_SECONDARY_EXEC_WBINVD_EXITING | + VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | + VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | + VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | + VMX_SECONDARY_EXEC_RDRAND_EXITING | + VMX_SECONDARY_EXEC_ENABLE_INVPCID | + VMX_SECONDARY_EXEC_ENABLE_VMFUNC | VMX_SECONDARY_EXEC_SHADOW_VMCS | + VMX_SECONDARY_EXEC_RDSEED_EXITING | VMX_SECONDARY_EXEC_ENABLE_PML | + VMX_SECONDARY_EXEC_XSAVES, + .features[FEAT_VMX_VMFUNC] = + MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (SierraForest)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { /* end of list */ }, + }, + }, { .name = "Denverton", .level = 21, From 41bdd9812863c150284a9339a048ed88c40f4df7 Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Wed, 13 Mar 2024 07:53:23 -0700 Subject: [PATCH 0028/2884] target/i386: Export RFDS bit to guests Register File Data Sampling (RFDS) is a CPU side-channel vulnerability that may expose stale register value. CPUs that set RFDS_NO bit in MSR IA32_ARCH_CAPABILITIES indicate that they are not vulnerable to RFDS. Similarly, RFDS_CLEAR indicates that CPU is affected by RFDS, and has the microcode to help mitigate RFDS. Make RFDS_CLEAR and RFDS_NO bits available to guests. Signed-off-by: Pawan Gupta Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-ID: <9a38877857392b5c2deae7e7db1b170d15510314.1710341348.git.pawan.kumar.gupta@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c295491d8a..eda15b0d4c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1158,8 +1158,8 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, "sbdr-ssdp-no", "fbsdp-no", "psdp-no", NULL, "fb-clear", NULL, NULL, NULL, NULL, NULL, NULL, - "pbrsb-no", NULL, "gds-no", NULL, - NULL, NULL, NULL, NULL, + "pbrsb-no", NULL, "gds-no", "rfds-no", + "rfds-clear", NULL, NULL, NULL, }, .msr = { .index = MSR_IA32_ARCH_CAPABILITIES, From 42c11ae2416dcbcd694ec3ee574fe2f3e70099ae Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 20 Mar 2024 03:39:13 -0500 Subject: [PATCH 0029/2884] pci-host/q35: Move PAM initialization above SMRAM initialization In mch_realize(), process PAM initialization before SMRAM initialization so that later patch can skill all the SMRAM related with a single check. Signed-off-by: Isaku Yamahata Signed-off-by: Xiaoyao Li Signed-off-by: Michael Roth Message-ID: <20240320083945.991426-18-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- hw/pci-host/q35.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 0d7d4e3f08..98d4a7c253 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -568,6 +568,16 @@ static void mch_realize(PCIDevice *d, Error **errp) /* setup pci memory mapping */ pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); + /* PAM */ + init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { + init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); + } + /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, @@ -634,15 +644,6 @@ static void mch_realize(PCIDevice *d, Error **errp) object_property_add_const_link(qdev_get_machine(), "smram", OBJECT(&mch->smram)); - - init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { - init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); - } } uint64_t mch_mcfg_base(void) From b07bf7b73fd02d24a7baa64a580f4974b86bbc86 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 20 Mar 2024 03:39:14 -0500 Subject: [PATCH 0030/2884] q35: Introduce smm_ranges property for q35-pci-host Add a q35 property to check whether or not SMM ranges, e.g. SMRAM, TSEG, etc... exist for the target platform. TDX doesn't support SMM and doesn't play nice with QEMU modifying related guest memory ranges. Signed-off-by: Isaku Yamahata Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Signed-off-by: Xiaoyao Li Signed-off-by: Michael Roth Message-ID: <20240320083945.991426-19-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_q35.c | 2 ++ hw/pci-host/q35.c | 42 +++++++++++++++++++++++++++------------ include/hw/i386/pc.h | 1 + include/hw/pci-host/q35.h | 1 + 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6e1180d4b6..bb53a51ac1 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -219,6 +219,8 @@ static void pc_q35_init(MachineState *machine) x86ms->above_4g_mem_size, NULL); object_property_set_bool(phb, PCI_HOST_BYPASS_IOMMU, pcms->default_bus_bypass_iommu, NULL); + object_property_set_bool(phb, PCI_HOST_PROP_SMM_RANGES, + x86_machine_is_smm_enabled(x86ms), NULL); sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); /* pci */ diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 98d4a7c253..0b6cbaed7e 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -179,6 +179,8 @@ static Property q35_host_props[] = { mch.below_4g_mem_size, 0), DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MEM_SIZE, Q35PCIHost, mch.above_4g_mem_size, 0), + DEFINE_PROP_BOOL(PCI_HOST_PROP_SMM_RANGES, Q35PCIHost, + mch.has_smm_ranges, true), DEFINE_PROP_BOOL("x-pci-hole64-fix", Q35PCIHost, pci_hole64_fix, true), DEFINE_PROP_END_OF_LIST(), }; @@ -214,6 +216,7 @@ static void q35_host_initfn(Object *obj) /* mch's object_initialize resets the default value, set it again */ qdev_prop_set_uint64(DEVICE(s), PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35_PCI_HOST_HOLE64_SIZE_DEFAULT); + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "uint32", q35_host_get_pci_hole_start, NULL, NULL, NULL); @@ -476,6 +479,10 @@ static void mch_write_config(PCIDevice *d, mch_update_pciexbar(mch); } + if (!mch->has_smm_ranges) { + return; + } + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, MCH_HOST_BRIDGE_SMRAM_SIZE)) { mch_update_smram(mch); @@ -494,10 +501,13 @@ static void mch_write_config(PCIDevice *d, static void mch_update(MCHPCIState *mch) { mch_update_pciexbar(mch); + mch_update_pam(mch); - mch_update_smram(mch); - mch_update_ext_tseg_mbytes(mch); - mch_update_smbase_smram(mch); + if (mch->has_smm_ranges) { + mch_update_smram(mch); + mch_update_ext_tseg_mbytes(mch); + mch_update_smbase_smram(mch); + } /* * pci hole goes from end-of-low-ram to io-apic. @@ -538,19 +548,21 @@ static void mch_reset(DeviceState *qdev) pci_set_quad(d->config + MCH_HOST_BRIDGE_PCIEXBAR, MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT); - d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; - d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT; - d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; - d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; + if (mch->has_smm_ranges) { + d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; + d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT; + d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; + d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; - if (mch->ext_tseg_mbytes > 0) { - pci_set_word(d->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, - MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); + if (mch->ext_tseg_mbytes > 0) { + pci_set_word(d->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); + } + + d->config[MCH_HOST_BRIDGE_F_SMBASE] = 0; + d->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0xff; } - d->config[MCH_HOST_BRIDGE_F_SMBASE] = 0; - d->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0xff; - mch_update(mch); } @@ -578,6 +590,10 @@ static void mch_realize(PCIDevice *d, Error **errp) PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } + if (!mch->has_smm_ranges) { + return; + } + /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 349f79df08..e52290916c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -161,6 +161,7 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level); #define PCI_HOST_PROP_PCI_HOLE64_SIZE "pci-hole64-size" #define PCI_HOST_BELOW_4G_MEM_SIZE "below-4g-mem-size" #define PCI_HOST_ABOVE_4G_MEM_SIZE "above-4g-mem-size" +#define PCI_HOST_PROP_SMM_RANGES "smm-ranges" void pc_pci_as_mapping_init(MemoryRegion *system_memory, diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index bafcbe6752..22fadfa3ed 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -50,6 +50,7 @@ struct MCHPCIState { MemoryRegion tseg_blackhole, tseg_window; MemoryRegion smbase_blackhole, smbase_window; bool has_smram_at_smbase; + bool has_smm_ranges; Range pci_hole; uint64_t below_4g_mem_size; uint64_t above_4g_mem_size; From 292dd287e78e0cbafde9d1522c729349d132d844 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 3 Apr 2024 10:59:53 -0400 Subject: [PATCH 0031/2884] hw/i386/acpi: Set PCAT_COMPAT bit only when pic is not disabled A value 1 of PCAT_COMPAT (bit 0) of MADT.Flags indicates that the system also has a PC-AT-compatible dual-8259 setup, i.e., the PIC. When PIC is not enabled (pic=off) for x86 machine, the PCAT_COMPAT bit needs to be cleared. The PIC probe should then print: [ 0.155970] Using NULL legacy PIC However, no such log printed in guest kernel unless PCAT_COMPAT is cleared. Signed-off-by: Xiaoyao Li Message-ID: <20240403145953.3082491-1-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/acpi-common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 20f19269da..0cc2919bb8 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -107,7 +107,9 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, acpi_table_begin(&table, table_data); /* Local APIC Address */ build_append_int_noprefix(table_data, APIC_DEFAULT_ADDRESS, 4); - build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ + /* Flags. bit 0: PCAT_COMPAT */ + build_append_int_noprefix(table_data, + x86ms->pic != ON_OFF_AUTO_OFF ? 1 : 0 , 4); for (i = 0; i < apic_ids->len; i++) { pc_madt_cpu_entry(i, apic_ids, table_data, false); From 41a605944e3fecae43ca18ded95ec31f28e0c7fe Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 29 Feb 2024 01:00:35 -0500 Subject: [PATCH 0032/2884] confidential guest support: Add kvm_init() and kvm_reset() in class Different confidential VMs in different architectures all have the same needs to do their specific initialization (and maybe resetting) stuffs with KVM. Currently each of them exposes individual *_kvm_init() functions and let machine code or kvm code to call it. To facilitate the introduction of confidential guest technology from different x86 vendors, add two virtual functions, kvm_init() and kvm_reset() in ConfidentialGuestSupportClass, and expose two helpers functions for invodking them. Signed-off-by: Xiaoyao Li Message-Id: <20240229060038.606591-1-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- include/exec/confidential-guest-support.h | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h index ba2dd4b5df..e5b188cffb 100644 --- a/include/exec/confidential-guest-support.h +++ b/include/exec/confidential-guest-support.h @@ -23,7 +23,10 @@ #include "qom/object.h" #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" -OBJECT_DECLARE_SIMPLE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) +OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, + ConfidentialGuestSupportClass, + CONFIDENTIAL_GUEST_SUPPORT) + struct ConfidentialGuestSupport { Object parent; @@ -55,8 +58,37 @@ struct ConfidentialGuestSupport { typedef struct ConfidentialGuestSupportClass { ObjectClass parent; + + int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); + int (*kvm_reset)(ConfidentialGuestSupport *cgs, Error **errp); } ConfidentialGuestSupportClass; +static inline int confidential_guest_kvm_init(ConfidentialGuestSupport *cgs, + Error **errp) +{ + ConfidentialGuestSupportClass *klass; + + klass = CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs); + if (klass->kvm_init) { + return klass->kvm_init(cgs, errp); + } + + return 0; +} + +static inline int confidential_guest_kvm_reset(ConfidentialGuestSupport *cgs, + Error **errp) +{ + ConfidentialGuestSupportClass *klass; + + klass = CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs); + if (klass->kvm_reset) { + return klass->kvm_reset(cgs, errp); + } + + return 0; +} + #endif /* !CONFIG_USER_ONLY */ #endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */ From 637c95b37b106c2eeba313e0abb38ec12e918a59 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 29 Feb 2024 01:00:36 -0500 Subject: [PATCH 0033/2884] i386/sev: Switch to use confidential_guest_kvm_init() Use confidential_guest_kvm_init() instead of calling SEV specific sev_kvm_init(). This allows the introduction of multiple confidential-guest-support subclasses for different x86 vendors. As a bonus, stubs are not needed anymore since there is no direct call from target/i386/kvm/kvm.c to SEV code. Signed-off-by: Xiaoyao Li Message-Id: <20240229060038.606591-1-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 10 +-- target/i386/kvm/meson.build | 2 - target/i386/kvm/sev-stub.c | 21 ------ target/i386/sev.c | 127 ++++++++++++++++++------------------ target/i386/sev.h | 2 - 5 files changed, 69 insertions(+), 93 deletions(-) delete mode 100644 target/i386/kvm/sev-stub.c diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index f1b59011d1..c64b9562c0 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2543,10 +2543,12 @@ int kvm_arch_init(MachineState *ms, KVMState *s) * mechanisms are supported in future (e.g. TDX), they'll need * their own initialization either here or elsewhere. */ - ret = sev_kvm_init(ms->cgs, &local_err); - if (ret < 0) { - error_report_err(local_err); - return ret; + if (ms->cgs) { + ret = confidential_guest_kvm_init(ms->cgs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } } has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 84d9143e60..e7850981e6 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -7,8 +7,6 @@ i386_kvm_ss.add(files( i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) -i386_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) - i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c deleted file mode 100644 index 1be5341e8a..0000000000 --- a/target/i386/kvm/sev-stub.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * QEMU SEV stub - * - * Copyright Advanced Micro Devices 2018 - * - * Authors: - * Brijesh Singh - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "sev.h" - -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - /* If we get here, cgs must be some non-SEV thing */ - return 0; -} diff --git a/target/i386/sev.c b/target/i386/sev.c index 72930ff0dc..b8f79d34d1 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -353,63 +353,6 @@ static void sev_guest_set_kernel_hashes(Object *obj, bool value, Error **errp) sev->kernel_hashes = value; } -static void -sev_guest_class_init(ObjectClass *oc, void *data) -{ - object_class_property_add_str(oc, "sev-device", - sev_guest_get_sev_device, - sev_guest_set_sev_device); - object_class_property_set_description(oc, "sev-device", - "SEV device to use"); - object_class_property_add_str(oc, "dh-cert-file", - sev_guest_get_dh_cert_file, - sev_guest_set_dh_cert_file); - object_class_property_set_description(oc, "dh-cert-file", - "guest owners DH certificate (encoded with base64)"); - object_class_property_add_str(oc, "session-file", - sev_guest_get_session_file, - sev_guest_set_session_file); - object_class_property_set_description(oc, "session-file", - "guest owners session parameters (encoded with base64)"); - object_class_property_add_bool(oc, "kernel-hashes", - sev_guest_get_kernel_hashes, - sev_guest_set_kernel_hashes); - object_class_property_set_description(oc, "kernel-hashes", - "add kernel hashes to guest firmware for measured Linux boot"); -} - -static void -sev_guest_instance_init(Object *obj) -{ - SevGuestState *sev = SEV_GUEST(obj); - - sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); - sev->policy = DEFAULT_GUEST_POLICY; - object_property_add_uint32_ptr(obj, "policy", &sev->policy, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "handle", &sev->handle, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "reduced-phys-bits", - &sev->reduced_phys_bits, - OBJ_PROP_FLAG_READWRITE); -} - -/* sev guest info */ -static const TypeInfo sev_guest_info = { - .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, - .name = TYPE_SEV_GUEST, - .instance_size = sizeof(SevGuestState), - .instance_finalize = sev_guest_finalize, - .class_init = sev_guest_class_init, - .instance_init = sev_guest_instance_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } - } -}; - bool sev_enabled(void) { @@ -906,20 +849,15 @@ sev_vm_state_change(void *opaque, bool running, RunState state) } } -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - SevGuestState *sev - = (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); + SevGuestState *sev = SEV_GUEST(cgs); char *devname; int ret, fw_error, cmd; uint32_t ebx; uint32_t host_cbitpos; struct sev_user_data_status status = {}; - if (!sev) { - return 0; - } - ret = ram_block_discard_disable(true); if (ret) { error_report("%s: cannot disable RAM discard", __func__); @@ -1384,6 +1322,67 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) return ret; } +static void +sev_guest_class_init(ObjectClass *oc, void *data) +{ + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = sev_kvm_init; + + object_class_property_add_str(oc, "sev-device", + sev_guest_get_sev_device, + sev_guest_set_sev_device); + object_class_property_set_description(oc, "sev-device", + "SEV device to use"); + object_class_property_add_str(oc, "dh-cert-file", + sev_guest_get_dh_cert_file, + sev_guest_set_dh_cert_file); + object_class_property_set_description(oc, "dh-cert-file", + "guest owners DH certificate (encoded with base64)"); + object_class_property_add_str(oc, "session-file", + sev_guest_get_session_file, + sev_guest_set_session_file); + object_class_property_set_description(oc, "session-file", + "guest owners session parameters (encoded with base64)"); + object_class_property_add_bool(oc, "kernel-hashes", + sev_guest_get_kernel_hashes, + sev_guest_set_kernel_hashes); + object_class_property_set_description(oc, "kernel-hashes", + "add kernel hashes to guest firmware for measured Linux boot"); +} + +static void +sev_guest_instance_init(Object *obj) +{ + SevGuestState *sev = SEV_GUEST(obj); + + sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); + sev->policy = DEFAULT_GUEST_POLICY; + object_property_add_uint32_ptr(obj, "policy", &sev->policy, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint32_ptr(obj, "handle", &sev->handle, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint32_ptr(obj, "reduced-phys-bits", + &sev->reduced_phys_bits, + OBJ_PROP_FLAG_READWRITE); +} + +/* sev guest info */ +static const TypeInfo sev_guest_info = { + .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, + .name = TYPE_SEV_GUEST, + .instance_size = sizeof(SevGuestState), + .instance_finalize = sev_guest_finalize, + .class_init = sev_guest_class_init, + .instance_init = sev_guest_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + static void sev_register_types(void) { diff --git a/target/i386/sev.h b/target/i386/sev.h index e7499c95b1..9e10d09539 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -57,6 +57,4 @@ int sev_inject_launch_secret(const char *hdr, const char *secret, int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size); void sev_es_set_reset_vector(CPUState *cpu); -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); - #endif From 00a238b1a845fd5f0acd771664c5e184a63ed9b6 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 29 Feb 2024 01:00:37 -0500 Subject: [PATCH 0034/2884] ppc/pef: switch to use confidential_guest_kvm_init/reset() Use the unified interface to call confidential guest related kvm_init() and kvm_reset(), to avoid exposing pef specific functions. As a bonus, pef.h goes away since there is no direct call from sPAPR board code to PEF code anymore. Signed-off-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- hw/ppc/pef.c | 9 ++++++--- hw/ppc/spapr.c | 10 +++++++--- include/hw/ppc/pef.h | 17 ----------------- 3 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 include/hw/ppc/pef.h diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index d28ed3ba73..47553348b1 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -15,7 +15,6 @@ #include "sysemu/kvm.h" #include "migration/blocker.h" #include "exec/confidential-guest-support.h" -#include "hw/ppc/pef.h" #define TYPE_PEF_GUEST "pef-guest" OBJECT_DECLARE_SIMPLE_TYPE(PefGuest, PEF_GUEST) @@ -93,7 +92,7 @@ static int kvmppc_svm_off(Error **errp) #endif } -int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +static int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) { return 0; @@ -107,7 +106,7 @@ int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return kvmppc_svm_init(cgs, errp); } -int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp) +static int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp) { if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) { return 0; @@ -131,6 +130,10 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest, static void pef_guest_class_init(ObjectClass *oc, void *data) { + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = pef_kvm_init; + klass->kvm_reset = pef_kvm_reset; } static void pef_guest_init(Object *obj) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 36ada4d0ba..533ea0f914 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -75,6 +75,7 @@ #include "hw/virtio/vhost-scsi-common.h" #include "exec/ram_addr.h" +#include "exec/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -87,7 +88,6 @@ #include "hw/ppc/spapr_tpm_proxy.h" #include "hw/ppc/spapr_nvdimm.h" #include "hw/ppc/spapr_numa.h" -#include "hw/ppc/pef.h" #include "monitor/monitor.h" @@ -1715,7 +1715,9 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) qemu_guest_getrandom_nofail(spapr->fdt_rng_seed, 32); } - pef_kvm_reset(machine->cgs, &error_fatal); + if (machine->cgs) { + confidential_guest_kvm_reset(machine->cgs, &error_fatal); + } spapr_caps_apply(spapr); spapr_nested_reset(spapr); @@ -2841,7 +2843,9 @@ static void spapr_machine_init(MachineState *machine) /* * if Secure VM (PEF) support is configured, then initialize it */ - pef_kvm_init(machine->cgs, &error_fatal); + if (machine->cgs) { + confidential_guest_kvm_init(machine->cgs, &error_fatal); + } msi_nonbroken = true; diff --git a/include/hw/ppc/pef.h b/include/hw/ppc/pef.h deleted file mode 100644 index 707dbe524c..0000000000 --- a/include/hw/ppc/pef.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * PEF (Protected Execution Facility) for POWER support - * - * Copyright Red Hat. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef HW_PPC_PEF_H -#define HW_PPC_PEF_H - -int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); -int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp); - -#endif /* HW_PPC_PEF_H */ From a14a2b0148e657cc526b7a75f2a1937628764e7a Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 29 Feb 2024 01:00:38 -0500 Subject: [PATCH 0035/2884] s390: Switch to use confidential_guest_kvm_init() Use unified confidential_guest_kvm_init() for consistency with other architectures. Signed-off-by: Xiaoyao Li Message-Id: <20240229060038.606591-1-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- hw/s390x/s390-virtio-ccw.c | 5 ++++- target/s390x/kvm/pv.c | 10 +++++++++- target/s390x/kvm/pv.h | 14 -------------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 7cebbf6c02..4dcc213820 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/ram_addr.h" +#include "exec/confidential-guest-support.h" #include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" @@ -260,7 +261,9 @@ static void ccw_init(MachineState *machine) s390_init_cpus(machine); /* Need CPU model to be determined before we can set up PV */ - s390_pv_init(machine->cgs, &error_fatal); + if (machine->cgs) { + confidential_guest_kvm_init(machine->cgs, &error_fatal); + } s390_flic_init(); diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index 7ca7faec73..dde836d21a 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -334,12 +334,17 @@ static bool s390_pv_guest_check(ConfidentialGuestSupport *cgs, Error **errp) return s390_pv_check_cpus(errp); } -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +static int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { return 0; } + if (!kvm_enabled()) { + error_setg(errp, "Protected Virtualization requires KVM"); + return -1; + } + if (!s390_has_feat(S390_FEAT_UNPACK)) { error_setg(errp, "CPU model does not support Protected Virtualization"); @@ -364,6 +369,9 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, static void s390_pv_guest_class_init(ObjectClass *oc, void *data) { + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = s390_pv_kvm_init; } static void s390_pv_guest_init(Object *obj) diff --git a/target/s390x/kvm/pv.h b/target/s390x/kvm/pv.h index 5877d28ff1..4b40817439 100644 --- a/target/s390x/kvm/pv.h +++ b/target/s390x/kvm/pv.h @@ -80,18 +80,4 @@ static inline int kvm_s390_dump_mem_state(uint64_t addr, size_t len, static inline int kvm_s390_dump_completion_data(void *buff) { return 0; } #endif /* CONFIG_KVM */ -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); -static inline int s390_pv_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - if (!cgs) { - return 0; - } - if (kvm_enabled()) { - return s390_pv_kvm_init(cgs, errp); - } - - error_setg(errp, "Protected Virtualization requires KVM"); - return -1; -} - #endif /* HW_S390_PV_H */ From 66210a1a30f2384bb59f9dad8d769dba56dd30f1 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Sun, 18 Feb 2024 23:35:02 -0600 Subject: [PATCH 0036/2884] scripts/update-linux-headers: Add setup_data.h to import list Data structures like struct setup_data have been moved to a separate setup_data.h header which bootparam.h relies on. Add setup_data.h to the cp_portable() list and sync it along with the other header files. Note that currently struct setup_data is stripped away as part of generating bootparam.h, but that handling is no currently needed for setup_data.h since it doesn't pull in many external headers/dependencies. However, QEMU currently redefines struct setup_data in hw/i386/x86.c, so that will need to be removed as part of any header update that pulls in the new setup_data.h to avoid build bisect breakage. Because is the first architecture specific #include in include/standard-headers/, add a new sed substitution to rewrite asm/ include to the standard-headers/asm-* subdirectory for the current architecture. And while at it, remove asm-generic/kvm_para.h from the list of allowed includes: it does not have a matching substitution, and therefore it would not be possible to use it on non-Linux systems where there is no /usr/include/asm-generic/ directory. Signed-off-by: Michael Roth Signed-off-by: Paolo Bonzini --- scripts/update-linux-headers.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index a0006eec6f..d48856f9e2 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -61,7 +61,7 @@ cp_portable() { -e 'linux/const' \ -e 'linux/kernel' \ -e 'linux/sysinfo' \ - -e 'asm-generic/kvm_para' \ + -e 'asm/setup_data.h' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -77,6 +77,7 @@ cp_portable() { -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/"\(input-event-codes\.h\)"/"standard-headers\/linux\/\1"/' \ -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ + -e 's/]*\)>/"standard-headers\/asm-'$arch'\/\1"/' \ -e 's/__bitwise//' \ -e 's/__attribute__((packed))/QEMU_PACKED/' \ -e 's/__inline__/inline/' \ @@ -155,11 +156,14 @@ for arch in $ARCHLIST; do "$tmpdir/include/asm/bootparam.h" > "$tmpdir/bootparam.h" cp_portable "$tmpdir/bootparam.h" \ "$output/include/standard-headers/asm-$arch" + cp_portable "$tmpdir/include/asm/setup_data.h" \ + "$output/standard-headers/asm-x86" fi if [ $arch = riscv ]; then cp "$tmpdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" fi done +arch= rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" From b40b8eb609d3549ac14aab43849b20f5cba951c9 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Wed, 21 Feb 2024 10:51:38 -0600 Subject: [PATCH 0037/2884] scripts/update-linux-headers: Add bits.h to file imports Signed-off-by: Michael Roth Signed-off-by: Paolo Bonzini --- scripts/update-linux-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index d48856f9e2..5f20434d5c 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -169,7 +169,7 @@ rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \ - vduse.h iommufd.h; do + vduse.h iommufd.h bits.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done From ab0c7fb22b56523f24d6e127cd4d10ecff67bf85 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Apr 2024 11:46:47 +0200 Subject: [PATCH 0038/2884] linux-headers: update to current kvm/next Signed-off-by: Paolo Bonzini --- hw/i386/x86.c | 8 - include/standard-headers/asm-x86/bootparam.h | 17 +- include/standard-headers/asm-x86/kvm_para.h | 3 +- include/standard-headers/asm-x86/setup_data.h | 83 +++ include/standard-headers/linux/ethtool.h | 48 ++ include/standard-headers/linux/fuse.h | 39 +- .../linux/input-event-codes.h | 1 + include/standard-headers/linux/virtio_gpu.h | 2 + include/standard-headers/linux/virtio_pci.h | 10 +- include/standard-headers/linux/virtio_snd.h | 154 ++++ linux-headers/asm-arm64/kvm.h | 15 +- linux-headers/asm-arm64/sve_context.h | 11 + linux-headers/asm-generic/bitsperlong.h | 4 + linux-headers/asm-loongarch/kvm.h | 2 - linux-headers/asm-mips/kvm.h | 2 - linux-headers/asm-powerpc/kvm.h | 45 +- linux-headers/asm-riscv/kvm.h | 3 +- linux-headers/asm-s390/kvm.h | 315 +++++++- linux-headers/asm-x86/kvm.h | 328 ++++++++- linux-headers/linux/bits.h | 15 + linux-headers/linux/kvm.h | 689 +----------------- linux-headers/linux/psp-sev.h | 59 ++ linux-headers/linux/vhost.h | 7 + 23 files changed, 1120 insertions(+), 740 deletions(-) create mode 100644 include/standard-headers/asm-x86/setup_data.h create mode 100644 linux-headers/linux/bits.h diff --git a/hw/i386/x86.c b/hw/i386/x86.c index ffbda48917..84a4801977 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -679,14 +679,6 @@ DeviceState *ioapic_init_secondary(GSIState *gsi_state) return dev; } -struct setup_data { - uint64_t next; - uint32_t type; - uint32_t len; - uint8_t data[]; -} __attribute__((packed)); - - /* * The entry point into the kernel for PVH boot is different from * the native entry point. The PVH entry is defined by the x86/HVM diff --git a/include/standard-headers/asm-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h index 0b06d2bff1..b582a105c0 100644 --- a/include/standard-headers/asm-x86/bootparam.h +++ b/include/standard-headers/asm-x86/bootparam.h @@ -2,21 +2,7 @@ #ifndef _ASM_X86_BOOTPARAM_H #define _ASM_X86_BOOTPARAM_H -/* setup_data/setup_indirect types */ -#define SETUP_NONE 0 -#define SETUP_E820_EXT 1 -#define SETUP_DTB 2 -#define SETUP_PCI 3 -#define SETUP_EFI 4 -#define SETUP_APPLE_PROPERTIES 5 -#define SETUP_JAILHOUSE 6 -#define SETUP_CC_BLOB 7 -#define SETUP_IMA 8 -#define SETUP_RNG_SEED 9 -#define SETUP_ENUM_MAX SETUP_RNG_SEED - -#define SETUP_INDIRECT (1<<31) -#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) +#include "standard-headers/asm-x86/setup_data.h" /* ram_size flags */ #define RAMDISK_IMAGE_START_MASK 0x07FF @@ -38,6 +24,7 @@ #define XLF_EFI_KEXEC (1<<4) #define XLF_5LEVEL (1<<5) #define XLF_5LEVEL_ENABLED (1<<6) +#define XLF_MEM_ENCRYPTION (1<<7) #endif /* _ASM_X86_BOOTPARAM_H */ diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index f0235e58a1..9a011d20f0 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -92,7 +92,7 @@ struct kvm_clock_pairing { #define KVM_ASYNC_PF_DELIVERY_AS_INT (1 << 3) /* MSR_KVM_ASYNC_PF_INT */ -#define KVM_ASYNC_PF_VEC_MASK GENMASK(7, 0) +#define KVM_ASYNC_PF_VEC_MASK __GENMASK(7, 0) /* MSR_KVM_MIGRATION_CONTROL */ #define KVM_MIGRATION_READY (1 << 0) @@ -142,7 +142,6 @@ struct kvm_vcpu_pv_apf_data { uint32_t token; uint8_t pad[56]; - uint32_t enabled; }; #define KVM_PV_EOI_BIT 0 diff --git a/include/standard-headers/asm-x86/setup_data.h b/include/standard-headers/asm-x86/setup_data.h new file mode 100644 index 0000000000..09355f54c5 --- /dev/null +++ b/include/standard-headers/asm-x86/setup_data.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_X86_SETUP_DATA_H +#define _ASM_X86_SETUP_DATA_H + +/* setup_data/setup_indirect types */ +#define SETUP_NONE 0 +#define SETUP_E820_EXT 1 +#define SETUP_DTB 2 +#define SETUP_PCI 3 +#define SETUP_EFI 4 +#define SETUP_APPLE_PROPERTIES 5 +#define SETUP_JAILHOUSE 6 +#define SETUP_CC_BLOB 7 +#define SETUP_IMA 8 +#define SETUP_RNG_SEED 9 +#define SETUP_ENUM_MAX SETUP_RNG_SEED + +#define SETUP_INDIRECT (1<<31) +#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) + +#ifndef __ASSEMBLY__ + +#include "standard-headers/linux/types.h" + +/* extensible setup data list node */ +struct setup_data { + uint64_t next; + uint32_t type; + uint32_t len; + uint8_t data[]; +}; + +/* extensible setup indirect data node */ +struct setup_indirect { + uint32_t type; + uint32_t reserved; /* Reserved, must be set to zero. */ + uint64_t len; + uint64_t addr; +}; + +/* + * The E820 memory region entry of the boot protocol ABI: + */ +struct boot_e820_entry { + uint64_t addr; + uint64_t size; + uint32_t type; +} QEMU_PACKED; + +/* + * The boot loader is passing platform information via this Jailhouse-specific + * setup data structure. + */ +struct jailhouse_setup_data { + struct { + uint16_t version; + uint16_t compatible_version; + } QEMU_PACKED hdr; + struct { + uint16_t pm_timer_address; + uint16_t num_cpus; + uint64_t pci_mmconfig_base; + uint32_t tsc_khz; + uint32_t apic_khz; + uint8_t standard_ioapic; + uint8_t cpu_ids[255]; + } QEMU_PACKED v1; + struct { + uint32_t flags; + } QEMU_PACKED v2; +} QEMU_PACKED; + +/* + * IMA buffer setup data information from the previous kernel during kexec + */ +struct ima_setup_data { + uint64_t addr; + uint64_t size; +} QEMU_PACKED; + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_X86_SETUP_DATA_H */ diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index dfb54eff6f..01503784d2 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -2023,6 +2023,53 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define IPV4_FLOW 0x10 /* hash only */ #define IPV6_FLOW 0x11 /* hash only */ #define ETHER_FLOW 0x12 /* spec only (ether_spec) */ + +/* Used for GTP-U IPv4 and IPv6. + * The format of GTP packets only includes + * elements such as TEID and GTP version. + * It is primarily intended for data communication of the UE. + */ +#define GTPU_V4_FLOW 0x13 /* hash only */ +#define GTPU_V6_FLOW 0x14 /* hash only */ + +/* Use for GTP-C IPv4 and v6. + * The format of these GTP packets does not include TEID. + * Primarily expected to be used for communication + * to create sessions for UE data communication, + * commonly referred to as CSR (Create Session Request). + */ +#define GTPC_V4_FLOW 0x15 /* hash only */ +#define GTPC_V6_FLOW 0x16 /* hash only */ + +/* Use for GTP-C IPv4 and v6. + * Unlike GTPC_V4_FLOW, the format of these GTP packets includes TEID. + * After session creation, it becomes this packet. + * This is mainly used for requests to realize UE handover. + */ +#define GTPC_TEID_V4_FLOW 0x17 /* hash only */ +#define GTPC_TEID_V6_FLOW 0x18 /* hash only */ + +/* Use for GTP-U and extended headers for the PSC (PDU Session Container). + * The format of these GTP packets includes TEID and QFI. + * In 5G communication using UPF (User Plane Function), + * data communication with this extended header is performed. + */ +#define GTPU_EH_V4_FLOW 0x19 /* hash only */ +#define GTPU_EH_V6_FLOW 0x1a /* hash only */ + +/* Use for GTP-U IPv4 and v6 PSC (PDU Session Container) extended headers. + * This differs from GTPU_EH_V(4|6)_FLOW in that it is distinguished by + * UL/DL included in the PSC. + * There are differences in the data included based on Downlink/Uplink, + * and can be used to distinguish packets. + * The functions described so far are useful when you want to + * handle communication from the mobile network in UPF, PGW, etc. + */ +#define GTPU_UL_V4_FLOW 0x1b /* hash only */ +#define GTPU_UL_V6_FLOW 0x1c /* hash only */ +#define GTPU_DL_V4_FLOW 0x1d /* hash only */ +#define GTPU_DL_V6_FLOW 0x1e /* hash only */ + /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 #define FLOW_MAC_EXT 0x40000000 @@ -2037,6 +2084,7 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define RXH_IP_DST (1 << 5) #define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ #define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_GTP_TEID (1 << 8) /* teid in case of GTP */ #define RXH_DISCARD (1 << 31) #define RX_CLS_FLOW_DISC 0xffffffffffffffffULL diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h index fc0dcd10ae..bac9dbc49f 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -211,6 +211,12 @@ * 7.39 * - add FUSE_DIRECT_IO_ALLOW_MMAP * - add FUSE_STATX and related structures + * + * 7.40 + * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag + * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag + * - add FUSE_NO_EXPORT_SUPPORT init flag + * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag */ #ifndef _LINUX_FUSE_H @@ -242,7 +248,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 39 +#define FUSE_KERNEL_MINOR_VERSION 40 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -349,6 +355,7 @@ struct fuse_file_lock { * FOPEN_STREAM: the file is stream-like (no file position at all) * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode + * FOPEN_PASSTHROUGH: passthrough read/write io for this open file */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) @@ -357,6 +364,7 @@ struct fuse_file_lock { #define FOPEN_STREAM (1 << 4) #define FOPEN_NOFLUSH (1 << 5) #define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) +#define FOPEN_PASSTHROUGH (1 << 7) /** * INIT request/reply flags @@ -406,6 +414,9 @@ struct fuse_file_lock { * symlink and mknod (single group that matches parent) * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode. + * FUSE_NO_EXPORT_SUPPORT: explicitly disable export support + * FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit + * of the request ID indicates resend requests */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -445,6 +456,9 @@ struct fuse_file_lock { #define FUSE_CREATE_SUPP_GROUP (1ULL << 34) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) #define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) +#define FUSE_PASSTHROUGH (1ULL << 37) +#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38) +#define FUSE_HAS_RESEND (1ULL << 39) /* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ #define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP @@ -631,6 +645,7 @@ enum fuse_notify_code { FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_CODE_MAX, }; @@ -757,7 +772,7 @@ struct fuse_create_in { struct fuse_open_out { uint64_t fh; uint32_t open_flags; - uint32_t padding; + int32_t backing_id; }; struct fuse_release_in { @@ -873,7 +888,8 @@ struct fuse_init_out { uint16_t max_pages; uint16_t map_alignment; uint32_t flags2; - uint32_t unused[7]; + uint32_t max_stack_depth; + uint32_t unused[6]; }; #define CUSE_INIT_INFO_MAX 4096 @@ -956,6 +972,14 @@ struct fuse_fallocate_in { uint32_t padding; }; +/** + * FUSE request unique ID flag + * + * Indicates whether this is a resend request. The receiver should handle this + * request accordingly. + */ +#define FUSE_UNIQUE_RESEND (1ULL << 63) + struct fuse_in_header { uint32_t len; uint32_t opcode; @@ -1045,9 +1069,18 @@ struct fuse_notify_retrieve_in { uint64_t dummy4; }; +struct fuse_backing_map { + int32_t fd; + uint32_t flags; + uint64_t padding; +}; + /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) +#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \ + struct fuse_backing_map) +#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t) struct fuse_lseek_in { uint64_t fh; diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index f6bab08540..2221b0c383 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -602,6 +602,7 @@ #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ #define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ +#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */ #define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ #define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index 2da48d3d4c..2db643ed8f 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -309,6 +309,8 @@ struct virtio_gpu_cmd_submit { #define VIRTIO_GPU_CAPSET_VIRGL 1 #define VIRTIO_GPU_CAPSET_VIRGL2 2 +/* 3 is reserved for gfxstream */ +#define VIRTIO_GPU_CAPSET_VENUS 4 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ struct virtio_gpu_get_capset_info { diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h index 3e2bc2c97e..4010216103 100644 --- a/include/standard-headers/linux/virtio_pci.h +++ b/include/standard-headers/linux/virtio_pci.h @@ -240,7 +240,7 @@ struct virtio_pci_cfg_cap { #define VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ 0x5 #define VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO 0x6 -struct QEMU_PACKED virtio_admin_cmd_hdr { +struct virtio_admin_cmd_hdr { uint16_t opcode; /* * 1 - SR-IOV @@ -252,20 +252,20 @@ struct QEMU_PACKED virtio_admin_cmd_hdr { uint64_t group_member_id; }; -struct QEMU_PACKED virtio_admin_cmd_status { +struct virtio_admin_cmd_status { uint16_t status; uint16_t status_qualifier; /* Unused, reserved for future extensions. */ uint8_t reserved2[4]; }; -struct QEMU_PACKED virtio_admin_cmd_legacy_wr_data { +struct virtio_admin_cmd_legacy_wr_data { uint8_t offset; /* Starting offset of the register(s) to write. */ uint8_t reserved[7]; uint8_t registers[]; }; -struct QEMU_PACKED virtio_admin_cmd_legacy_rd_data { +struct virtio_admin_cmd_legacy_rd_data { uint8_t offset; /* Starting offset of the register(s) to read. */ }; @@ -275,7 +275,7 @@ struct QEMU_PACKED virtio_admin_cmd_legacy_rd_data { #define VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO 4 -struct QEMU_PACKED virtio_admin_cmd_notify_info_data { +struct virtio_admin_cmd_notify_info_data { uint8_t flags; /* 0 = end of list, 1 = owner device, 2 = member device */ uint8_t bar; /* BAR of the member or the owner device */ uint8_t padding[6]; diff --git a/include/standard-headers/linux/virtio_snd.h b/include/standard-headers/linux/virtio_snd.h index 1af96b9fc6..860f12e0a4 100644 --- a/include/standard-headers/linux/virtio_snd.h +++ b/include/standard-headers/linux/virtio_snd.h @@ -7,6 +7,14 @@ #include "standard-headers/linux/virtio_types.h" +/******************************************************************************* + * FEATURE BITS + */ +enum { + /* device supports control elements */ + VIRTIO_SND_F_CTLS = 0 +}; + /******************************************************************************* * CONFIGURATION SPACE */ @@ -17,6 +25,8 @@ struct virtio_snd_config { uint32_t streams; /* # of available channel maps */ uint32_t chmaps; + /* # of available control elements */ + uint32_t controls; }; enum { @@ -55,6 +65,15 @@ enum { /* channel map control request types */ VIRTIO_SND_R_CHMAP_INFO = 0x0200, + /* control element request types */ + VIRTIO_SND_R_CTL_INFO = 0x0300, + VIRTIO_SND_R_CTL_ENUM_ITEMS, + VIRTIO_SND_R_CTL_READ, + VIRTIO_SND_R_CTL_WRITE, + VIRTIO_SND_R_CTL_TLV_READ, + VIRTIO_SND_R_CTL_TLV_WRITE, + VIRTIO_SND_R_CTL_TLV_COMMAND, + /* jack event types */ VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, VIRTIO_SND_EVT_JACK_DISCONNECTED, @@ -63,6 +82,9 @@ enum { VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, VIRTIO_SND_EVT_PCM_XRUN, + /* control element event types */ + VIRTIO_SND_EVT_CTL_NOTIFY = 0x1200, + /* common status codes */ VIRTIO_SND_S_OK = 0x8000, VIRTIO_SND_S_BAD_MSG, @@ -331,4 +353,136 @@ struct virtio_snd_chmap_info { uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE]; }; +/******************************************************************************* + * CONTROL ELEMENTS MESSAGES + */ +struct virtio_snd_ctl_hdr { + /* VIRTIO_SND_R_CTL_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + uint32_t control_id; +}; + +/* supported roles for control elements */ +enum { + VIRTIO_SND_CTL_ROLE_UNDEFINED = 0, + VIRTIO_SND_CTL_ROLE_VOLUME, + VIRTIO_SND_CTL_ROLE_MUTE, + VIRTIO_SND_CTL_ROLE_GAIN +}; + +/* supported value types for control elements */ +enum { + VIRTIO_SND_CTL_TYPE_BOOLEAN = 0, + VIRTIO_SND_CTL_TYPE_INTEGER, + VIRTIO_SND_CTL_TYPE_INTEGER64, + VIRTIO_SND_CTL_TYPE_ENUMERATED, + VIRTIO_SND_CTL_TYPE_BYTES, + VIRTIO_SND_CTL_TYPE_IEC958 +}; + +/* supported access rights for control elements */ +enum { + VIRTIO_SND_CTL_ACCESS_READ = 0, + VIRTIO_SND_CTL_ACCESS_WRITE, + VIRTIO_SND_CTL_ACCESS_VOLATILE, + VIRTIO_SND_CTL_ACCESS_INACTIVE, + VIRTIO_SND_CTL_ACCESS_TLV_READ, + VIRTIO_SND_CTL_ACCESS_TLV_WRITE, + VIRTIO_SND_CTL_ACCESS_TLV_COMMAND +}; + +struct virtio_snd_ctl_info { + /* common header */ + struct virtio_snd_info hdr; + /* element role (VIRTIO_SND_CTL_ROLE_XXX) */ + uint32_t role; + /* element value type (VIRTIO_SND_CTL_TYPE_XXX) */ + uint32_t type; + /* element access right bit map (1 << VIRTIO_SND_CTL_ACCESS_XXX) */ + uint32_t access; + /* # of members in the element value */ + uint32_t count; + /* index for an element with a non-unique name */ + uint32_t index; + /* name identifier string for the element */ + uint8_t name[44]; + /* additional information about the element's value */ + union { + /* VIRTIO_SND_CTL_TYPE_INTEGER */ + struct { + /* minimum supported value */ + uint32_t min; + /* maximum supported value */ + uint32_t max; + /* fixed step size for value (0 = variable size) */ + uint32_t step; + } integer; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 */ + struct { + /* minimum supported value */ + uint64_t min; + /* maximum supported value */ + uint64_t max; + /* fixed step size for value (0 = variable size) */ + uint64_t step; + } integer64; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED */ + struct { + /* # of options supported for value */ + uint32_t items; + } enumerated; + } value; +}; + +struct virtio_snd_ctl_enum_item { + /* option name */ + uint8_t item[64]; +}; + +struct virtio_snd_ctl_iec958 { + /* AES/IEC958 channel status bits */ + uint8_t status[24]; + /* AES/IEC958 subcode bits */ + uint8_t subcode[147]; + /* nothing */ + uint8_t pad; + /* AES/IEC958 subframe bits */ + uint8_t dig_subframe[4]; +}; + +struct virtio_snd_ctl_value { + union { + /* VIRTIO_SND_CTL_TYPE_BOOLEAN|INTEGER value */ + uint32_t integer[128]; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 value */ + uint64_t integer64[64]; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED value (option indexes) */ + uint32_t enumerated[128]; + /* VIRTIO_SND_CTL_TYPE_BYTES value */ + uint8_t bytes[512]; + /* VIRTIO_SND_CTL_TYPE_IEC958 value */ + struct virtio_snd_ctl_iec958 iec958; + } value; +}; + +/* supported event reason types */ +enum { + /* element's value has changed */ + VIRTIO_SND_CTL_EVT_MASK_VALUE = 0, + /* element's information has changed */ + VIRTIO_SND_CTL_EVT_MASK_INFO, + /* element's metadata has changed */ + VIRTIO_SND_CTL_EVT_MASK_TLV +}; + +struct virtio_snd_ctl_event { + /* VIRTIO_SND_EVT_CTL_NOTIFY */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + uint16_t control_id; + /* event reason bit map (1 << VIRTIO_SND_CTL_EVT_MASK_XXX) */ + uint16_t mask; +}; + #endif /* VIRTIO_SND_IF_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index c59ea55cd8..2af9931ae9 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -37,9 +37,7 @@ #include #include -#define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_READONLY_MEM #define __KVM_HAVE_VCPU_EVENTS #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -76,11 +74,11 @@ struct kvm_regs { /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ #define KVM_ARM_DEVICE_TYPE_SHIFT 0 -#define KVM_ARM_DEVICE_TYPE_MASK GENMASK(KVM_ARM_DEVICE_TYPE_SHIFT + 15, \ - KVM_ARM_DEVICE_TYPE_SHIFT) +#define KVM_ARM_DEVICE_TYPE_MASK __GENMASK(KVM_ARM_DEVICE_TYPE_SHIFT + 15, \ + KVM_ARM_DEVICE_TYPE_SHIFT) #define KVM_ARM_DEVICE_ID_SHIFT 16 -#define KVM_ARM_DEVICE_ID_MASK GENMASK(KVM_ARM_DEVICE_ID_SHIFT + 15, \ - KVM_ARM_DEVICE_ID_SHIFT) +#define KVM_ARM_DEVICE_ID_MASK __GENMASK(KVM_ARM_DEVICE_ID_SHIFT + 15, \ + KVM_ARM_DEVICE_ID_SHIFT) /* Supported device IDs */ #define KVM_ARM_DEVICE_VGIC_V2 0 @@ -162,6 +160,11 @@ struct kvm_sync_regs { __u64 device_irq_level; }; +/* Bits for run->s.regs.device_irq_level */ +#define KVM_ARM_DEV_EL1_VTIMER (1 << 0) +#define KVM_ARM_DEV_EL1_PTIMER (1 << 1) +#define KVM_ARM_DEV_PMU (1 << 2) + /* * PMU filter structure. Describe a range of events with a particular * action. To be used with KVM_ARM_VCPU_PMU_V3_FILTER. diff --git a/linux-headers/asm-arm64/sve_context.h b/linux-headers/asm-arm64/sve_context.h index 1d0e3e1d09..d1b1ec8cb1 100644 --- a/linux-headers/asm-arm64/sve_context.h +++ b/linux-headers/asm-arm64/sve_context.h @@ -13,6 +13,17 @@ #define __SVE_VQ_BYTES 16 /* number of bytes per quadword */ +/* + * Yes, __SVE_VQ_MAX is 512 QUADWORDS. + * + * To help ensure forward portability, this is much larger than the + * current maximum value defined by the SVE architecture. While arrays + * or static allocations can be sized based on this value, watch out! + * It will waste a surprisingly large amount of memory. + * + * Dynamic sizing based on the actual runtime vector length is likely to + * be preferable for most purposes. + */ #define __SVE_VQ_MIN 1 #define __SVE_VQ_MAX 512 diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h index 75f320fa91..1fb4f0c9f2 100644 --- a/linux-headers/asm-generic/bitsperlong.h +++ b/linux-headers/asm-generic/bitsperlong.h @@ -24,4 +24,8 @@ #endif #endif +#ifndef __BITS_PER_LONG_LONG +#define __BITS_PER_LONG_LONG 64 +#endif + #endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h index 923d0bd382..109785922c 100644 --- a/linux-headers/asm-loongarch/kvm.h +++ b/linux-headers/asm-loongarch/kvm.h @@ -14,8 +14,6 @@ * Some parts derived from the x86 version of this file. */ -#define __KVM_HAVE_READONLY_MEM - #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 #define KVM_DIRTY_LOG_PAGE_OFFSET 64 diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index edcf717c43..9673dc9cb3 100644 --- a/linux-headers/asm-mips/kvm.h +++ b/linux-headers/asm-mips/kvm.h @@ -20,8 +20,6 @@ * Some parts derived from the x86 version of this file. */ -#define __KVM_HAVE_READONLY_MEM - #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 /* diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 9f18fa090f..1691297a76 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -28,7 +28,6 @@ #define __KVM_HAVE_PPC_SMT #define __KVM_HAVE_IRQCHIP #define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_GUEST_DEBUG /* Not always available, but if it is, this is the correct offset. */ #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -733,4 +732,48 @@ struct kvm_ppc_xive_eq { #define KVM_XIVE_TIMA_PAGE_OFFSET 0 #define KVM_XIVE_ESB_PAGE_OFFSET 4 +/* for KVM_PPC_GET_PVINFO */ + +#define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) + +struct kvm_ppc_pvinfo { + /* out */ + __u32 flags; + __u32 hcall[4]; + __u8 pad[108]; +}; + +/* for KVM_PPC_GET_SMMU_INFO */ +#define KVM_PPC_PAGE_SIZES_MAX_SZ 8 + +struct kvm_ppc_one_page_size { + __u32 page_shift; /* Page shift (or 0) */ + __u32 pte_enc; /* Encoding in the HPTE (>>12) */ +}; + +struct kvm_ppc_one_seg_page_size { + __u32 page_shift; /* Base page shift of segment (or 0) */ + __u32 slb_enc; /* SLB encoding for BookS */ + struct kvm_ppc_one_page_size enc[KVM_PPC_PAGE_SIZES_MAX_SZ]; +}; + +#define KVM_PPC_PAGE_SIZES_REAL 0x00000001 +#define KVM_PPC_1T_SEGMENTS 0x00000002 +#define KVM_PPC_NO_HASH 0x00000004 + +struct kvm_ppc_smmu_info { + __u64 flags; + __u32 slb_size; + __u16 data_keys; /* # storage keys supported for data */ + __u16 instr_keys; /* # storage keys supported for instructions */ + struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ]; +}; + +/* for KVM_PPC_RESIZE_HPT_{PREPARE,COMMIT} */ +struct kvm_ppc_resize_hpt { + __u64 flags; + __u32 shift; + __u32 pad; +}; + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index 7499e88a94..b1c503c295 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -16,7 +16,6 @@ #include #define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_READONLY_MEM #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -166,6 +165,8 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_ZVFH, KVM_RISCV_ISA_EXT_ZVFHMIN, KVM_RISCV_ISA_EXT_ZFA, + KVM_RISCV_ISA_EXT_ZTSO, + KVM_RISCV_ISA_EXT_ZACAS, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index 023a2763a9..684c4e1205 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -12,7 +12,320 @@ #include #define __KVM_S390 -#define __KVM_HAVE_GUEST_DEBUG + +struct kvm_s390_skeys { + __u64 start_gfn; + __u64 count; + __u64 skeydata_addr; + __u32 flags; + __u32 reserved[9]; +}; + +#define KVM_S390_CMMA_PEEK (1 << 0) + +/** + * kvm_s390_cmma_log - Used for CMMA migration. + * + * Used both for input and output. + * + * @start_gfn: Guest page number to start from. + * @count: Size of the result buffer. + * @flags: Control operation mode via KVM_S390_CMMA_* flags + * @remaining: Used with KVM_S390_GET_CMMA_BITS. Indicates how many dirty + * pages are still remaining. + * @mask: Used with KVM_S390_SET_CMMA_BITS. Bitmap of bits to actually set + * in the PGSTE. + * @values: Pointer to the values buffer. + * + * Used in KVM_S390_{G,S}ET_CMMA_BITS ioctls. + */ +struct kvm_s390_cmma_log { + __u64 start_gfn; + __u32 count; + __u32 flags; + union { + __u64 remaining; + __u64 mask; + }; + __u64 values; +}; + +#define KVM_S390_RESET_POR 1 +#define KVM_S390_RESET_CLEAR 2 +#define KVM_S390_RESET_SUBSYSTEM 4 +#define KVM_S390_RESET_CPU_INIT 8 +#define KVM_S390_RESET_IPL 16 + +/* for KVM_S390_MEM_OP */ +struct kvm_s390_mem_op { + /* in */ + __u64 gaddr; /* the guest address */ + __u64 flags; /* flags */ + __u32 size; /* amount of bytes */ + __u32 op; /* type of operation */ + __u64 buf; /* buffer in userspace */ + union { + struct { + __u8 ar; /* the access register number */ + __u8 key; /* access key, ignored if flag unset */ + __u8 pad1[6]; /* ignored */ + __u64 old_addr; /* ignored if cmpxchg flag unset */ + }; + __u32 sida_offset; /* offset into the sida */ + __u8 reserved[32]; /* ignored */ + }; +}; +/* types for kvm_s390_mem_op->op */ +#define KVM_S390_MEMOP_LOGICAL_READ 0 +#define KVM_S390_MEMOP_LOGICAL_WRITE 1 +#define KVM_S390_MEMOP_SIDA_READ 2 +#define KVM_S390_MEMOP_SIDA_WRITE 3 +#define KVM_S390_MEMOP_ABSOLUTE_READ 4 +#define KVM_S390_MEMOP_ABSOLUTE_WRITE 5 +#define KVM_S390_MEMOP_ABSOLUTE_CMPXCHG 6 + +/* flags for kvm_s390_mem_op->flags */ +#define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0) +#define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1) +#define KVM_S390_MEMOP_F_SKEY_PROTECTION (1ULL << 2) + +/* flags specifying extension support via KVM_CAP_S390_MEM_OP_EXTENSION */ +#define KVM_S390_MEMOP_EXTENSION_CAP_BASE (1 << 0) +#define KVM_S390_MEMOP_EXTENSION_CAP_CMPXCHG (1 << 1) + +struct kvm_s390_psw { + __u64 mask; + __u64 addr; +}; + +/* valid values for type in kvm_s390_interrupt */ +#define KVM_S390_SIGP_STOP 0xfffe0000u +#define KVM_S390_PROGRAM_INT 0xfffe0001u +#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u +#define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u +#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u +#define KVM_S390_MCHK 0xfffe1000u +#define KVM_S390_INT_CLOCK_COMP 0xffff1004u +#define KVM_S390_INT_CPU_TIMER 0xffff1005u +#define KVM_S390_INT_VIRTIO 0xffff2603u +#define KVM_S390_INT_SERVICE 0xffff2401u +#define KVM_S390_INT_EMERGENCY 0xffff1201u +#define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u +/* Anything below 0xfffe0000u is taken by INT_IO */ +#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \ + (((schid)) | \ + ((ssid) << 16) | \ + ((cssid) << 18) | \ + ((ai) << 26)) +#define KVM_S390_INT_IO_MIN 0x00000000u +#define KVM_S390_INT_IO_MAX 0xfffdffffu +#define KVM_S390_INT_IO_AI_MASK 0x04000000u + + +struct kvm_s390_interrupt { + __u32 type; + __u32 parm; + __u64 parm64; +}; + +struct kvm_s390_io_info { + __u16 subchannel_id; + __u16 subchannel_nr; + __u32 io_int_parm; + __u32 io_int_word; +}; + +struct kvm_s390_ext_info { + __u32 ext_params; + __u32 pad; + __u64 ext_params2; +}; + +struct kvm_s390_pgm_info { + __u64 trans_exc_code; + __u64 mon_code; + __u64 per_address; + __u32 data_exc_code; + __u16 code; + __u16 mon_class_nr; + __u8 per_code; + __u8 per_atmid; + __u8 exc_access_id; + __u8 per_access_id; + __u8 op_access_id; +#define KVM_S390_PGM_FLAGS_ILC_VALID 0x01 +#define KVM_S390_PGM_FLAGS_ILC_0 0x02 +#define KVM_S390_PGM_FLAGS_ILC_1 0x04 +#define KVM_S390_PGM_FLAGS_ILC_MASK 0x06 +#define KVM_S390_PGM_FLAGS_NO_REWIND 0x08 + __u8 flags; + __u8 pad[2]; +}; + +struct kvm_s390_prefix_info { + __u32 address; +}; + +struct kvm_s390_extcall_info { + __u16 code; +}; + +struct kvm_s390_emerg_info { + __u16 code; +}; + +#define KVM_S390_STOP_FLAG_STORE_STATUS 0x01 +struct kvm_s390_stop_info { + __u32 flags; +}; + +struct kvm_s390_mchk_info { + __u64 cr14; + __u64 mcic; + __u64 failing_storage_address; + __u32 ext_damage_code; + __u32 pad; + __u8 fixed_logout[16]; +}; + +struct kvm_s390_irq { + __u64 type; + union { + struct kvm_s390_io_info io; + struct kvm_s390_ext_info ext; + struct kvm_s390_pgm_info pgm; + struct kvm_s390_emerg_info emerg; + struct kvm_s390_extcall_info extcall; + struct kvm_s390_prefix_info prefix; + struct kvm_s390_stop_info stop; + struct kvm_s390_mchk_info mchk; + char reserved[64]; + } u; +}; + +struct kvm_s390_irq_state { + __u64 buf; + __u32 flags; /* will stay unused for compatibility reasons */ + __u32 len; + __u32 reserved[4]; /* will stay unused for compatibility reasons */ +}; + +struct kvm_s390_ucas_mapping { + __u64 user_addr; + __u64 vcpu_addr; + __u64 length; +}; + +struct kvm_s390_pv_sec_parm { + __u64 origin; + __u64 length; +}; + +struct kvm_s390_pv_unp { + __u64 addr; + __u64 size; + __u64 tweak; +}; + +enum pv_cmd_dmp_id { + KVM_PV_DUMP_INIT, + KVM_PV_DUMP_CONFIG_STOR_STATE, + KVM_PV_DUMP_COMPLETE, + KVM_PV_DUMP_CPU, +}; + +struct kvm_s390_pv_dmp { + __u64 subcmd; + __u64 buff_addr; + __u64 buff_len; + __u64 gaddr; /* For dump storage state */ + __u64 reserved[4]; +}; + +enum pv_cmd_info_id { + KVM_PV_INFO_VM, + KVM_PV_INFO_DUMP, +}; + +struct kvm_s390_pv_info_dump { + __u64 dump_cpu_buffer_len; + __u64 dump_config_mem_buffer_per_1m; + __u64 dump_config_finalize_len; +}; + +struct kvm_s390_pv_info_vm { + __u64 inst_calls_list[4]; + __u64 max_cpus; + __u64 max_guests; + __u64 max_guest_addr; + __u64 feature_indication; +}; + +struct kvm_s390_pv_info_header { + __u32 id; + __u32 len_max; + __u32 len_written; + __u32 reserved; +}; + +struct kvm_s390_pv_info { + struct kvm_s390_pv_info_header header; + union { + struct kvm_s390_pv_info_dump dump; + struct kvm_s390_pv_info_vm vm; + }; +}; + +enum pv_cmd_id { + KVM_PV_ENABLE, + KVM_PV_DISABLE, + KVM_PV_SET_SEC_PARMS, + KVM_PV_UNPACK, + KVM_PV_VERIFY, + KVM_PV_PREP_RESET, + KVM_PV_UNSHARE_ALL, + KVM_PV_INFO, + KVM_PV_DUMP, + KVM_PV_ASYNC_CLEANUP_PREPARE, + KVM_PV_ASYNC_CLEANUP_PERFORM, +}; + +struct kvm_pv_cmd { + __u32 cmd; /* Command to be executed */ + __u16 rc; /* Ultravisor return code */ + __u16 rrc; /* Ultravisor return reason code */ + __u64 data; /* Data or address */ + __u32 flags; /* flags for future extensions. Must be 0 for now */ + __u32 reserved[3]; +}; + +struct kvm_s390_zpci_op { + /* in */ + __u32 fh; /* target device */ + __u8 op; /* operation to perform */ + __u8 pad[3]; + union { + /* for KVM_S390_ZPCIOP_REG_AEN */ + struct { + __u64 ibv; /* Guest addr of interrupt bit vector */ + __u64 sb; /* Guest addr of summary bit */ + __u32 flags; + __u32 noi; /* Number of interrupts */ + __u8 isc; /* Guest interrupt subclass */ + __u8 sbo; /* Offset of guest summary bit vector */ + __u16 pad; + } reg_aen; + __u64 reserved[8]; + } u; +}; + +/* types for kvm_s390_zpci_op->op */ +#define KVM_S390_ZPCIOP_REG_AEN 0 +#define KVM_S390_ZPCIOP_DEREG_AEN 1 + +/* flags for kvm_s390_zpci_op->u.reg_aen.flags */ +#define KVM_S390_ZPCIOP_REGAEN_HOST (1 << 0) /* Device control API: s390-specific devices */ #define KVM_DEV_FLIC_GET_ALL_IRQS 1 diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 003fb74534..31c95c2dfe 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -7,6 +7,8 @@ * */ +#include +#include #include #include #include @@ -40,7 +42,6 @@ #define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_MSI #define __KVM_HAVE_USER_NMI -#define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_MSIX #define __KVM_HAVE_MCE #define __KVM_HAVE_PIT_STATE2 @@ -49,7 +50,6 @@ #define __KVM_HAVE_DEBUGREGS #define __KVM_HAVE_XSAVE #define __KVM_HAVE_XCRS -#define __KVM_HAVE_READONLY_MEM /* Architectural interrupt line count. */ #define KVM_NR_INTERRUPTS 256 @@ -455,8 +455,13 @@ struct kvm_sync_regs { #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 -/* attributes for system fd (group 0) */ -#define KVM_X86_XCOMP_GUEST_SUPP 0 +/* vendor-independent attributes for system fd (group 0) */ +#define KVM_X86_GRP_SYSTEM 0 +# define KVM_X86_XCOMP_GUEST_SUPP 0 + +/* vendor-specific groups and attributes for system fd */ +#define KVM_X86_GRP_SEV 1 +# define KVM_X86_SEV_VMSA_FEATURES 0 struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; @@ -524,9 +529,310 @@ struct kvm_pmu_event_filter { #define KVM_PMU_EVENT_ALLOW 0 #define KVM_PMU_EVENT_DENY 1 -#define KVM_PMU_EVENT_FLAG_MASKED_EVENTS BIT(0) +#define KVM_PMU_EVENT_FLAG_MASKED_EVENTS _BITUL(0) #define KVM_PMU_EVENT_FLAGS_VALID_MASK (KVM_PMU_EVENT_FLAG_MASKED_EVENTS) +/* for KVM_CAP_MCE */ +struct kvm_x86_mce { + __u64 status; + __u64 addr; + __u64 misc; + __u64 mcg_status; + __u8 bank; + __u8 pad1[7]; + __u64 pad2[3]; +}; + +/* for KVM_CAP_XEN_HVM */ +#define KVM_XEN_HVM_CONFIG_HYPERCALL_MSR (1 << 0) +#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1) +#define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2) +#define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 3) +#define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 4) +#define KVM_XEN_HVM_CONFIG_EVTCHN_SEND (1 << 5) +#define KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG (1 << 6) +#define KVM_XEN_HVM_CONFIG_PVCLOCK_TSC_UNSTABLE (1 << 7) +#define KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA (1 << 8) + +struct kvm_xen_hvm_config { + __u32 flags; + __u32 msr; + __u64 blob_addr_32; + __u64 blob_addr_64; + __u8 blob_size_32; + __u8 blob_size_64; + __u8 pad2[30]; +}; + +struct kvm_xen_hvm_attr { + __u16 type; + __u16 pad[3]; + union { + __u8 long_mode; + __u8 vector; + __u8 runstate_update_flag; + union { + __u64 gfn; +#define KVM_XEN_INVALID_GFN ((__u64)-1) + __u64 hva; + } shared_info; + struct { + __u32 send_port; + __u32 type; /* EVTCHNSTAT_ipi / EVTCHNSTAT_interdomain */ + __u32 flags; +#define KVM_XEN_EVTCHN_DEASSIGN (1 << 0) +#define KVM_XEN_EVTCHN_UPDATE (1 << 1) +#define KVM_XEN_EVTCHN_RESET (1 << 2) + /* + * Events sent by the guest are either looped back to + * the guest itself (potentially on a different port#) + * or signalled via an eventfd. + */ + union { + struct { + __u32 port; + __u32 vcpu; + __u32 priority; + } port; + struct { + __u32 port; /* Zero for eventfd */ + __s32 fd; + } eventfd; + __u32 padding[4]; + } deliver; + } evtchn; + __u32 xen_version; + __u64 pad[8]; + } u; +}; + + +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ +#define KVM_XEN_ATTR_TYPE_LONG_MODE 0x0 +#define KVM_XEN_ATTR_TYPE_SHARED_INFO 0x1 +#define KVM_XEN_ATTR_TYPE_UPCALL_VECTOR 0x2 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ +#define KVM_XEN_ATTR_TYPE_EVTCHN 0x3 +#define KVM_XEN_ATTR_TYPE_XEN_VERSION 0x4 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG */ +#define KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG 0x5 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA */ +#define KVM_XEN_ATTR_TYPE_SHARED_INFO_HVA 0x6 + +struct kvm_xen_vcpu_attr { + __u16 type; + __u16 pad[3]; + union { + __u64 gpa; +#define KVM_XEN_INVALID_GPA ((__u64)-1) + __u64 hva; + __u64 pad[8]; + struct { + __u64 state; + __u64 state_entry_time; + __u64 time_running; + __u64 time_runnable; + __u64 time_blocked; + __u64 time_offline; + } runstate; + __u32 vcpu_id; + struct { + __u32 port; + __u32 priority; + __u64 expires_ns; + } timer; + __u8 vector; + } u; +}; + +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO 0x0 +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO 0x1 +#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR 0x2 +#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT 0x3 +#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA 0x4 +#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST 0x5 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID 0x6 +#define KVM_XEN_VCPU_ATTR_TYPE_TIMER 0x7 +#define KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR 0x8 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA */ +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA 0x9 + +/* Secure Encrypted Virtualization command */ +enum sev_cmd_id { + /* Guest initialization commands */ + KVM_SEV_INIT = 0, + KVM_SEV_ES_INIT, + /* Guest launch commands */ + KVM_SEV_LAUNCH_START, + KVM_SEV_LAUNCH_UPDATE_DATA, + KVM_SEV_LAUNCH_UPDATE_VMSA, + KVM_SEV_LAUNCH_SECRET, + KVM_SEV_LAUNCH_MEASURE, + KVM_SEV_LAUNCH_FINISH, + /* Guest migration commands (outgoing) */ + KVM_SEV_SEND_START, + KVM_SEV_SEND_UPDATE_DATA, + KVM_SEV_SEND_UPDATE_VMSA, + KVM_SEV_SEND_FINISH, + /* Guest migration commands (incoming) */ + KVM_SEV_RECEIVE_START, + KVM_SEV_RECEIVE_UPDATE_DATA, + KVM_SEV_RECEIVE_UPDATE_VMSA, + KVM_SEV_RECEIVE_FINISH, + /* Guest status and debug commands */ + KVM_SEV_GUEST_STATUS, + KVM_SEV_DBG_DECRYPT, + KVM_SEV_DBG_ENCRYPT, + /* Guest certificates commands */ + KVM_SEV_CERT_EXPORT, + /* Attestation report */ + KVM_SEV_GET_ATTESTATION_REPORT, + /* Guest Migration Extension */ + KVM_SEV_SEND_CANCEL, + + /* Second time is the charm; improved versions of the above ioctls. */ + KVM_SEV_INIT2, + + KVM_SEV_NR_MAX, +}; + +struct kvm_sev_cmd { + __u32 id; + __u32 pad0; + __u64 data; + __u32 error; + __u32 sev_fd; +}; + +struct kvm_sev_init { + __u64 vmsa_features; + __u32 flags; + __u32 pad[9]; +}; + +struct kvm_sev_launch_start { + __u32 handle; + __u32 policy; + __u64 dh_uaddr; + __u32 dh_len; + __u32 pad0; + __u64 session_uaddr; + __u32 session_len; + __u32 pad1; +}; + +struct kvm_sev_launch_update_data { + __u64 uaddr; + __u32 len; + __u32 pad0; +}; + + +struct kvm_sev_launch_secret { + __u64 hdr_uaddr; + __u32 hdr_len; + __u32 pad0; + __u64 guest_uaddr; + __u32 guest_len; + __u32 pad1; + __u64 trans_uaddr; + __u32 trans_len; + __u32 pad2; +}; + +struct kvm_sev_launch_measure { + __u64 uaddr; + __u32 len; + __u32 pad0; +}; + +struct kvm_sev_guest_status { + __u32 handle; + __u32 policy; + __u32 state; +}; + +struct kvm_sev_dbg { + __u64 src_uaddr; + __u64 dst_uaddr; + __u32 len; + __u32 pad0; +}; + +struct kvm_sev_attestation_report { + __u8 mnonce[16]; + __u64 uaddr; + __u32 len; + __u32 pad0; +}; + +struct kvm_sev_send_start { + __u32 policy; + __u32 pad0; + __u64 pdh_cert_uaddr; + __u32 pdh_cert_len; + __u32 pad1; + __u64 plat_certs_uaddr; + __u32 plat_certs_len; + __u32 pad2; + __u64 amd_certs_uaddr; + __u32 amd_certs_len; + __u32 pad3; + __u64 session_uaddr; + __u32 session_len; + __u32 pad4; +}; + +struct kvm_sev_send_update_data { + __u64 hdr_uaddr; + __u32 hdr_len; + __u32 pad0; + __u64 guest_uaddr; + __u32 guest_len; + __u32 pad1; + __u64 trans_uaddr; + __u32 trans_len; + __u32 pad2; +}; + +struct kvm_sev_receive_start { + __u32 handle; + __u32 policy; + __u64 pdh_uaddr; + __u32 pdh_len; + __u32 pad0; + __u64 session_uaddr; + __u32 session_len; + __u32 pad1; +}; + +struct kvm_sev_receive_update_data { + __u64 hdr_uaddr; + __u32 hdr_len; + __u32 pad0; + __u64 guest_uaddr; + __u32 guest_len; + __u32 pad1; + __u64 trans_uaddr; + __u32 trans_len; + __u32 pad2; +}; + +#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0) +#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1) + +struct kvm_hyperv_eventfd { + __u32 conn_id; + __s32 fd; + __u32 flags; + __u32 padding[3]; +}; + +#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff +#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) + /* * Masked event layout. * Bits Description @@ -547,10 +853,10 @@ struct kvm_pmu_event_filter { ((__u64)(!!(exclude)) << 55)) #define KVM_PMU_MASKED_ENTRY_EVENT_SELECT \ - (GENMASK_ULL(7, 0) | GENMASK_ULL(35, 32)) -#define KVM_PMU_MASKED_ENTRY_UMASK_MASK (GENMASK_ULL(63, 56)) -#define KVM_PMU_MASKED_ENTRY_UMASK_MATCH (GENMASK_ULL(15, 8)) -#define KVM_PMU_MASKED_ENTRY_EXCLUDE (BIT_ULL(55)) + (__GENMASK_ULL(7, 0) | __GENMASK_ULL(35, 32)) +#define KVM_PMU_MASKED_ENTRY_UMASK_MASK (__GENMASK_ULL(63, 56)) +#define KVM_PMU_MASKED_ENTRY_UMASK_MATCH (__GENMASK_ULL(15, 8)) +#define KVM_PMU_MASKED_ENTRY_EXCLUDE (_BITULL(55)) #define KVM_PMU_MASKED_ENTRY_UMASK_MASK_SHIFT (56) /* for KVM_{GET,SET,HAS}_DEVICE_ATTR */ @@ -558,9 +864,11 @@ struct kvm_pmu_event_filter { #define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */ /* x86-specific KVM_EXIT_HYPERCALL flags. */ -#define KVM_EXIT_HYPERCALL_LONG_MODE BIT(0) +#define KVM_EXIT_HYPERCALL_LONG_MODE _BITULL(0) #define KVM_X86_DEFAULT_VM 0 #define KVM_X86_SW_PROTECTED_VM 1 +#define KVM_X86_SEV_VM 2 +#define KVM_X86_SEV_ES_VM 3 #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/linux/bits.h b/linux-headers/linux/bits.h new file mode 100644 index 0000000000..d9897771be --- /dev/null +++ b/linux-headers/linux/bits.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* bits.h: Macros for dealing with bitmasks. */ + +#ifndef _LINUX_BITS_H +#define _LINUX_BITS_H + +#define __GENMASK(h, l) \ + (((~_UL(0)) - (_UL(1) << (l)) + 1) & \ + (~_UL(0) >> (__BITS_PER_LONG - 1 - (h)))) + +#define __GENMASK_ULL(h, l) \ + (((~_ULL(0)) - (_ULL(1) << (l)) + 1) & \ + (~_ULL(0) >> (__BITS_PER_LONG_LONG - 1 - (h)))) + +#endif /* _LINUX_BITS_H */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 17839229b2..038731cdef 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -16,6 +16,11 @@ #define KVM_API_VERSION 12 +/* + * Backwards-compatible definitions. + */ +#define __KVM_HAVE_GUEST_DEBUG + /* for KVM_SET_USER_MEMORY_REGION */ struct kvm_userspace_memory_region { __u32 slot; @@ -85,43 +90,6 @@ struct kvm_pit_config { #define KVM_PIT_SPEAKER_DUMMY 1 -struct kvm_s390_skeys { - __u64 start_gfn; - __u64 count; - __u64 skeydata_addr; - __u32 flags; - __u32 reserved[9]; -}; - -#define KVM_S390_CMMA_PEEK (1 << 0) - -/** - * kvm_s390_cmma_log - Used for CMMA migration. - * - * Used both for input and output. - * - * @start_gfn: Guest page number to start from. - * @count: Size of the result buffer. - * @flags: Control operation mode via KVM_S390_CMMA_* flags - * @remaining: Used with KVM_S390_GET_CMMA_BITS. Indicates how many dirty - * pages are still remaining. - * @mask: Used with KVM_S390_SET_CMMA_BITS. Bitmap of bits to actually set - * in the PGSTE. - * @values: Pointer to the values buffer. - * - * Used in KVM_S390_{G,S}ET_CMMA_BITS ioctls. - */ -struct kvm_s390_cmma_log { - __u64 start_gfn; - __u32 count; - __u32 flags; - union { - __u64 remaining; - __u64 mask; - }; - __u64 values; -}; - struct kvm_hyperv_exit { #define KVM_EXIT_HYPERV_SYNIC 1 #define KVM_EXIT_HYPERV_HCALL 2 @@ -313,11 +281,6 @@ struct kvm_run { __u32 ipb; } s390_sieic; /* KVM_EXIT_S390_RESET */ -#define KVM_S390_RESET_POR 1 -#define KVM_S390_RESET_CLEAR 2 -#define KVM_S390_RESET_SUBSYSTEM 4 -#define KVM_S390_RESET_CPU_INIT 8 -#define KVM_S390_RESET_IPL 16 __u64 s390_reset_flags; /* KVM_EXIT_S390_UCONTROL */ struct { @@ -532,43 +495,6 @@ struct kvm_translation { __u8 pad[5]; }; -/* for KVM_S390_MEM_OP */ -struct kvm_s390_mem_op { - /* in */ - __u64 gaddr; /* the guest address */ - __u64 flags; /* flags */ - __u32 size; /* amount of bytes */ - __u32 op; /* type of operation */ - __u64 buf; /* buffer in userspace */ - union { - struct { - __u8 ar; /* the access register number */ - __u8 key; /* access key, ignored if flag unset */ - __u8 pad1[6]; /* ignored */ - __u64 old_addr; /* ignored if cmpxchg flag unset */ - }; - __u32 sida_offset; /* offset into the sida */ - __u8 reserved[32]; /* ignored */ - }; -}; -/* types for kvm_s390_mem_op->op */ -#define KVM_S390_MEMOP_LOGICAL_READ 0 -#define KVM_S390_MEMOP_LOGICAL_WRITE 1 -#define KVM_S390_MEMOP_SIDA_READ 2 -#define KVM_S390_MEMOP_SIDA_WRITE 3 -#define KVM_S390_MEMOP_ABSOLUTE_READ 4 -#define KVM_S390_MEMOP_ABSOLUTE_WRITE 5 -#define KVM_S390_MEMOP_ABSOLUTE_CMPXCHG 6 - -/* flags for kvm_s390_mem_op->flags */ -#define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0) -#define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1) -#define KVM_S390_MEMOP_F_SKEY_PROTECTION (1ULL << 2) - -/* flags specifying extension support via KVM_CAP_S390_MEM_OP_EXTENSION */ -#define KVM_S390_MEMOP_EXTENSION_CAP_BASE (1 << 0) -#define KVM_S390_MEMOP_EXTENSION_CAP_CMPXCHG (1 << 1) - /* for KVM_INTERRUPT */ struct kvm_interrupt { /* in */ @@ -633,124 +559,6 @@ struct kvm_mp_state { __u32 mp_state; }; -struct kvm_s390_psw { - __u64 mask; - __u64 addr; -}; - -/* valid values for type in kvm_s390_interrupt */ -#define KVM_S390_SIGP_STOP 0xfffe0000u -#define KVM_S390_PROGRAM_INT 0xfffe0001u -#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u -#define KVM_S390_RESTART 0xfffe0003u -#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u -#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u -#define KVM_S390_MCHK 0xfffe1000u -#define KVM_S390_INT_CLOCK_COMP 0xffff1004u -#define KVM_S390_INT_CPU_TIMER 0xffff1005u -#define KVM_S390_INT_VIRTIO 0xffff2603u -#define KVM_S390_INT_SERVICE 0xffff2401u -#define KVM_S390_INT_EMERGENCY 0xffff1201u -#define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u -/* Anything below 0xfffe0000u is taken by INT_IO */ -#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \ - (((schid)) | \ - ((ssid) << 16) | \ - ((cssid) << 18) | \ - ((ai) << 26)) -#define KVM_S390_INT_IO_MIN 0x00000000u -#define KVM_S390_INT_IO_MAX 0xfffdffffu -#define KVM_S390_INT_IO_AI_MASK 0x04000000u - - -struct kvm_s390_interrupt { - __u32 type; - __u32 parm; - __u64 parm64; -}; - -struct kvm_s390_io_info { - __u16 subchannel_id; - __u16 subchannel_nr; - __u32 io_int_parm; - __u32 io_int_word; -}; - -struct kvm_s390_ext_info { - __u32 ext_params; - __u32 pad; - __u64 ext_params2; -}; - -struct kvm_s390_pgm_info { - __u64 trans_exc_code; - __u64 mon_code; - __u64 per_address; - __u32 data_exc_code; - __u16 code; - __u16 mon_class_nr; - __u8 per_code; - __u8 per_atmid; - __u8 exc_access_id; - __u8 per_access_id; - __u8 op_access_id; -#define KVM_S390_PGM_FLAGS_ILC_VALID 0x01 -#define KVM_S390_PGM_FLAGS_ILC_0 0x02 -#define KVM_S390_PGM_FLAGS_ILC_1 0x04 -#define KVM_S390_PGM_FLAGS_ILC_MASK 0x06 -#define KVM_S390_PGM_FLAGS_NO_REWIND 0x08 - __u8 flags; - __u8 pad[2]; -}; - -struct kvm_s390_prefix_info { - __u32 address; -}; - -struct kvm_s390_extcall_info { - __u16 code; -}; - -struct kvm_s390_emerg_info { - __u16 code; -}; - -#define KVM_S390_STOP_FLAG_STORE_STATUS 0x01 -struct kvm_s390_stop_info { - __u32 flags; -}; - -struct kvm_s390_mchk_info { - __u64 cr14; - __u64 mcic; - __u64 failing_storage_address; - __u32 ext_damage_code; - __u32 pad; - __u8 fixed_logout[16]; -}; - -struct kvm_s390_irq { - __u64 type; - union { - struct kvm_s390_io_info io; - struct kvm_s390_ext_info ext; - struct kvm_s390_pgm_info pgm; - struct kvm_s390_emerg_info emerg; - struct kvm_s390_extcall_info extcall; - struct kvm_s390_prefix_info prefix; - struct kvm_s390_stop_info stop; - struct kvm_s390_mchk_info mchk; - char reserved[64]; - } u; -}; - -struct kvm_s390_irq_state { - __u64 buf; - __u32 flags; /* will stay unused for compatibility reasons */ - __u32 len; - __u32 reserved[4]; /* will stay unused for compatibility reasons */ -}; - /* for KVM_SET_GUEST_DEBUG */ #define KVM_GUESTDBG_ENABLE 0x00000001 @@ -806,50 +614,6 @@ struct kvm_enable_cap { __u8 pad[64]; }; -/* for KVM_PPC_GET_PVINFO */ - -#define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) - -struct kvm_ppc_pvinfo { - /* out */ - __u32 flags; - __u32 hcall[4]; - __u8 pad[108]; -}; - -/* for KVM_PPC_GET_SMMU_INFO */ -#define KVM_PPC_PAGE_SIZES_MAX_SZ 8 - -struct kvm_ppc_one_page_size { - __u32 page_shift; /* Page shift (or 0) */ - __u32 pte_enc; /* Encoding in the HPTE (>>12) */ -}; - -struct kvm_ppc_one_seg_page_size { - __u32 page_shift; /* Base page shift of segment (or 0) */ - __u32 slb_enc; /* SLB encoding for BookS */ - struct kvm_ppc_one_page_size enc[KVM_PPC_PAGE_SIZES_MAX_SZ]; -}; - -#define KVM_PPC_PAGE_SIZES_REAL 0x00000001 -#define KVM_PPC_1T_SEGMENTS 0x00000002 -#define KVM_PPC_NO_HASH 0x00000004 - -struct kvm_ppc_smmu_info { - __u64 flags; - __u32 slb_size; - __u16 data_keys; /* # storage keys supported for data */ - __u16 instr_keys; /* # storage keys supported for instructions */ - struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ]; -}; - -/* for KVM_PPC_RESIZE_HPT_{PREPARE,COMMIT} */ -struct kvm_ppc_resize_hpt { - __u64 flags; - __u32 shift; - __u32 pad; -}; - #define KVMIO 0xAE /* machine type bits, to be used as argument to KVM_CREATE_VM */ @@ -919,9 +683,7 @@ struct kvm_ppc_resize_hpt { /* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 #define KVM_CAP_USER_NMI 22 -#ifdef __KVM_HAVE_GUEST_DEBUG #define KVM_CAP_SET_GUEST_DEBUG 23 -#endif #ifdef __KVM_HAVE_PIT #define KVM_CAP_REINJECT_CONTROL 24 #endif @@ -1152,8 +914,6 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_GUEST_MEMFD 234 #define KVM_CAP_VM_TYPES 235 -#ifdef KVM_CAP_IRQ_ROUTING - struct kvm_irq_routing_irqchip { __u32 irqchip; __u32 pin; @@ -1218,42 +978,6 @@ struct kvm_irq_routing { struct kvm_irq_routing_entry entries[]; }; -#endif - -#ifdef KVM_CAP_MCE -/* x86 MCE */ -struct kvm_x86_mce { - __u64 status; - __u64 addr; - __u64 misc; - __u64 mcg_status; - __u8 bank; - __u8 pad1[7]; - __u64 pad2[3]; -}; -#endif - -#ifdef KVM_CAP_XEN_HVM -#define KVM_XEN_HVM_CONFIG_HYPERCALL_MSR (1 << 0) -#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1) -#define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2) -#define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 3) -#define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 4) -#define KVM_XEN_HVM_CONFIG_EVTCHN_SEND (1 << 5) -#define KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG (1 << 6) -#define KVM_XEN_HVM_CONFIG_PVCLOCK_TSC_UNSTABLE (1 << 7) - -struct kvm_xen_hvm_config { - __u32 flags; - __u32 msr; - __u64 blob_addr_32; - __u64 blob_addr_64; - __u8 blob_size_32; - __u8 blob_size_64; - __u8 pad2[30]; -}; -#endif - #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) /* * Available with KVM_CAP_IRQFD_RESAMPLE @@ -1438,11 +1162,6 @@ struct kvm_vfio_spapr_tce { struct kvm_userspace_memory_region2) /* enable ucontrol for s390 */ -struct kvm_s390_ucas_mapping { - __u64 user_addr; - __u64 vcpu_addr; - __u64 length; -}; #define KVM_S390_UCAS_MAP _IOW(KVMIO, 0x50, struct kvm_s390_ucas_mapping) #define KVM_S390_UCAS_UNMAP _IOW(KVMIO, 0x51, struct kvm_s390_ucas_mapping) #define KVM_S390_VCPU_FAULT _IOW(KVMIO, 0x52, unsigned long) @@ -1637,89 +1356,6 @@ struct kvm_enc_region { #define KVM_S390_NORMAL_RESET _IO(KVMIO, 0xc3) #define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4) -struct kvm_s390_pv_sec_parm { - __u64 origin; - __u64 length; -}; - -struct kvm_s390_pv_unp { - __u64 addr; - __u64 size; - __u64 tweak; -}; - -enum pv_cmd_dmp_id { - KVM_PV_DUMP_INIT, - KVM_PV_DUMP_CONFIG_STOR_STATE, - KVM_PV_DUMP_COMPLETE, - KVM_PV_DUMP_CPU, -}; - -struct kvm_s390_pv_dmp { - __u64 subcmd; - __u64 buff_addr; - __u64 buff_len; - __u64 gaddr; /* For dump storage state */ - __u64 reserved[4]; -}; - -enum pv_cmd_info_id { - KVM_PV_INFO_VM, - KVM_PV_INFO_DUMP, -}; - -struct kvm_s390_pv_info_dump { - __u64 dump_cpu_buffer_len; - __u64 dump_config_mem_buffer_per_1m; - __u64 dump_config_finalize_len; -}; - -struct kvm_s390_pv_info_vm { - __u64 inst_calls_list[4]; - __u64 max_cpus; - __u64 max_guests; - __u64 max_guest_addr; - __u64 feature_indication; -}; - -struct kvm_s390_pv_info_header { - __u32 id; - __u32 len_max; - __u32 len_written; - __u32 reserved; -}; - -struct kvm_s390_pv_info { - struct kvm_s390_pv_info_header header; - union { - struct kvm_s390_pv_info_dump dump; - struct kvm_s390_pv_info_vm vm; - }; -}; - -enum pv_cmd_id { - KVM_PV_ENABLE, - KVM_PV_DISABLE, - KVM_PV_SET_SEC_PARMS, - KVM_PV_UNPACK, - KVM_PV_VERIFY, - KVM_PV_PREP_RESET, - KVM_PV_UNSHARE_ALL, - KVM_PV_INFO, - KVM_PV_DUMP, - KVM_PV_ASYNC_CLEANUP_PREPARE, - KVM_PV_ASYNC_CLEANUP_PERFORM, -}; - -struct kvm_pv_cmd { - __u32 cmd; /* Command to be executed */ - __u16 rc; /* Ultravisor return code */ - __u16 rrc; /* Ultravisor return reason code */ - __u64 data; /* Data or address */ - __u32 flags; /* flags for future extensions. Must be 0 for now */ - __u32 reserved[3]; -}; - /* Available with KVM_CAP_S390_PROTECTED */ #define KVM_S390_PV_COMMAND _IOWR(KVMIO, 0xc5, struct kvm_pv_cmd) @@ -1733,58 +1369,6 @@ struct kvm_pv_cmd { #define KVM_XEN_HVM_GET_ATTR _IOWR(KVMIO, 0xc8, struct kvm_xen_hvm_attr) #define KVM_XEN_HVM_SET_ATTR _IOW(KVMIO, 0xc9, struct kvm_xen_hvm_attr) -struct kvm_xen_hvm_attr { - __u16 type; - __u16 pad[3]; - union { - __u8 long_mode; - __u8 vector; - __u8 runstate_update_flag; - struct { - __u64 gfn; -#define KVM_XEN_INVALID_GFN ((__u64)-1) - } shared_info; - struct { - __u32 send_port; - __u32 type; /* EVTCHNSTAT_ipi / EVTCHNSTAT_interdomain */ - __u32 flags; -#define KVM_XEN_EVTCHN_DEASSIGN (1 << 0) -#define KVM_XEN_EVTCHN_UPDATE (1 << 1) -#define KVM_XEN_EVTCHN_RESET (1 << 2) - /* - * Events sent by the guest are either looped back to - * the guest itself (potentially on a different port#) - * or signalled via an eventfd. - */ - union { - struct { - __u32 port; - __u32 vcpu; - __u32 priority; - } port; - struct { - __u32 port; /* Zero for eventfd */ - __s32 fd; - } eventfd; - __u32 padding[4]; - } deliver; - } evtchn; - __u32 xen_version; - __u64 pad[8]; - } u; -}; - - -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ -#define KVM_XEN_ATTR_TYPE_LONG_MODE 0x0 -#define KVM_XEN_ATTR_TYPE_SHARED_INFO 0x1 -#define KVM_XEN_ATTR_TYPE_UPCALL_VECTOR 0x2 -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ -#define KVM_XEN_ATTR_TYPE_EVTCHN 0x3 -#define KVM_XEN_ATTR_TYPE_XEN_VERSION 0x4 -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG */ -#define KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG 0x5 - /* Per-vCPU Xen attributes */ #define KVM_XEN_VCPU_GET_ATTR _IOWR(KVMIO, 0xca, struct kvm_xen_vcpu_attr) #define KVM_XEN_VCPU_SET_ATTR _IOW(KVMIO, 0xcb, struct kvm_xen_vcpu_attr) @@ -1795,242 +1379,6 @@ struct kvm_xen_hvm_attr { #define KVM_GET_SREGS2 _IOR(KVMIO, 0xcc, struct kvm_sregs2) #define KVM_SET_SREGS2 _IOW(KVMIO, 0xcd, struct kvm_sregs2) -struct kvm_xen_vcpu_attr { - __u16 type; - __u16 pad[3]; - union { - __u64 gpa; -#define KVM_XEN_INVALID_GPA ((__u64)-1) - __u64 pad[8]; - struct { - __u64 state; - __u64 state_entry_time; - __u64 time_running; - __u64 time_runnable; - __u64 time_blocked; - __u64 time_offline; - } runstate; - __u32 vcpu_id; - struct { - __u32 port; - __u32 priority; - __u64 expires_ns; - } timer; - __u8 vector; - } u; -}; - -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ -#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO 0x0 -#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO 0x1 -#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR 0x2 -#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT 0x3 -#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA 0x4 -#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST 0x5 -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ -#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID 0x6 -#define KVM_XEN_VCPU_ATTR_TYPE_TIMER 0x7 -#define KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR 0x8 - -/* Secure Encrypted Virtualization command */ -enum sev_cmd_id { - /* Guest initialization commands */ - KVM_SEV_INIT = 0, - KVM_SEV_ES_INIT, - /* Guest launch commands */ - KVM_SEV_LAUNCH_START, - KVM_SEV_LAUNCH_UPDATE_DATA, - KVM_SEV_LAUNCH_UPDATE_VMSA, - KVM_SEV_LAUNCH_SECRET, - KVM_SEV_LAUNCH_MEASURE, - KVM_SEV_LAUNCH_FINISH, - /* Guest migration commands (outgoing) */ - KVM_SEV_SEND_START, - KVM_SEV_SEND_UPDATE_DATA, - KVM_SEV_SEND_UPDATE_VMSA, - KVM_SEV_SEND_FINISH, - /* Guest migration commands (incoming) */ - KVM_SEV_RECEIVE_START, - KVM_SEV_RECEIVE_UPDATE_DATA, - KVM_SEV_RECEIVE_UPDATE_VMSA, - KVM_SEV_RECEIVE_FINISH, - /* Guest status and debug commands */ - KVM_SEV_GUEST_STATUS, - KVM_SEV_DBG_DECRYPT, - KVM_SEV_DBG_ENCRYPT, - /* Guest certificates commands */ - KVM_SEV_CERT_EXPORT, - /* Attestation report */ - KVM_SEV_GET_ATTESTATION_REPORT, - /* Guest Migration Extension */ - KVM_SEV_SEND_CANCEL, - - KVM_SEV_NR_MAX, -}; - -struct kvm_sev_cmd { - __u32 id; - __u64 data; - __u32 error; - __u32 sev_fd; -}; - -struct kvm_sev_launch_start { - __u32 handle; - __u32 policy; - __u64 dh_uaddr; - __u32 dh_len; - __u64 session_uaddr; - __u32 session_len; -}; - -struct kvm_sev_launch_update_data { - __u64 uaddr; - __u32 len; -}; - - -struct kvm_sev_launch_secret { - __u64 hdr_uaddr; - __u32 hdr_len; - __u64 guest_uaddr; - __u32 guest_len; - __u64 trans_uaddr; - __u32 trans_len; -}; - -struct kvm_sev_launch_measure { - __u64 uaddr; - __u32 len; -}; - -struct kvm_sev_guest_status { - __u32 handle; - __u32 policy; - __u32 state; -}; - -struct kvm_sev_dbg { - __u64 src_uaddr; - __u64 dst_uaddr; - __u32 len; -}; - -struct kvm_sev_attestation_report { - __u8 mnonce[16]; - __u64 uaddr; - __u32 len; -}; - -struct kvm_sev_send_start { - __u32 policy; - __u64 pdh_cert_uaddr; - __u32 pdh_cert_len; - __u64 plat_certs_uaddr; - __u32 plat_certs_len; - __u64 amd_certs_uaddr; - __u32 amd_certs_len; - __u64 session_uaddr; - __u32 session_len; -}; - -struct kvm_sev_send_update_data { - __u64 hdr_uaddr; - __u32 hdr_len; - __u64 guest_uaddr; - __u32 guest_len; - __u64 trans_uaddr; - __u32 trans_len; -}; - -struct kvm_sev_receive_start { - __u32 handle; - __u32 policy; - __u64 pdh_uaddr; - __u32 pdh_len; - __u64 session_uaddr; - __u32 session_len; -}; - -struct kvm_sev_receive_update_data { - __u64 hdr_uaddr; - __u32 hdr_len; - __u64 guest_uaddr; - __u32 guest_len; - __u64 trans_uaddr; - __u32 trans_len; -}; - -#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) -#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) -#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) - -struct kvm_assigned_pci_dev { - __u32 assigned_dev_id; - __u32 busnr; - __u32 devfn; - __u32 flags; - __u32 segnr; - union { - __u32 reserved[11]; - }; -}; - -#define KVM_DEV_IRQ_HOST_INTX (1 << 0) -#define KVM_DEV_IRQ_HOST_MSI (1 << 1) -#define KVM_DEV_IRQ_HOST_MSIX (1 << 2) - -#define KVM_DEV_IRQ_GUEST_INTX (1 << 8) -#define KVM_DEV_IRQ_GUEST_MSI (1 << 9) -#define KVM_DEV_IRQ_GUEST_MSIX (1 << 10) - -#define KVM_DEV_IRQ_HOST_MASK 0x00ff -#define KVM_DEV_IRQ_GUEST_MASK 0xff00 - -struct kvm_assigned_irq { - __u32 assigned_dev_id; - __u32 host_irq; /* ignored (legacy field) */ - __u32 guest_irq; - __u32 flags; - union { - __u32 reserved[12]; - }; -}; - -struct kvm_assigned_msix_nr { - __u32 assigned_dev_id; - __u16 entry_nr; - __u16 padding; -}; - -#define KVM_MAX_MSIX_PER_DEV 256 -struct kvm_assigned_msix_entry { - __u32 assigned_dev_id; - __u32 gsi; - __u16 entry; /* The index of entry in the MSI-X table */ - __u16 padding[3]; -}; - -#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0) -#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1) - -/* Available with KVM_CAP_ARM_USER_IRQ */ - -/* Bits for run->s.regs.device_irq_level */ -#define KVM_ARM_DEV_EL1_VTIMER (1 << 0) -#define KVM_ARM_DEV_EL1_PTIMER (1 << 1) -#define KVM_ARM_DEV_PMU (1 << 2) - -struct kvm_hyperv_eventfd { - __u32 conn_id; - __s32 fd; - __u32 flags; - __u32 padding[3]; -}; - -#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff -#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) - #define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (1 << 0) #define KVM_DIRTY_LOG_INITIALLY_SET (1 << 1) @@ -2176,33 +1524,6 @@ struct kvm_stats_desc { /* Available with KVM_CAP_S390_ZPCI_OP */ #define KVM_S390_ZPCI_OP _IOW(KVMIO, 0xd1, struct kvm_s390_zpci_op) -struct kvm_s390_zpci_op { - /* in */ - __u32 fh; /* target device */ - __u8 op; /* operation to perform */ - __u8 pad[3]; - union { - /* for KVM_S390_ZPCIOP_REG_AEN */ - struct { - __u64 ibv; /* Guest addr of interrupt bit vector */ - __u64 sb; /* Guest addr of summary bit */ - __u32 flags; - __u32 noi; /* Number of interrupts */ - __u8 isc; /* Guest interrupt subclass */ - __u8 sbo; /* Offset of guest summary bit vector */ - __u16 pad; - } reg_aen; - __u64 reserved[8]; - } u; -}; - -/* types for kvm_s390_zpci_op->op */ -#define KVM_S390_ZPCIOP_REG_AEN 0 -#define KVM_S390_ZPCIOP_DEREG_AEN 1 - -/* flags for kvm_s390_zpci_op->u.reg_aen.flags */ -#define KVM_S390_ZPCIOP_REGAEN_HOST (1 << 0) - /* Available with KVM_CAP_MEMORY_ATTRIBUTES */ #define KVM_SET_MEMORY_ATTRIBUTES _IOW(KVMIO, 0xd2, struct kvm_memory_attributes) diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index bcb21339ee..c3046c6bff 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -28,6 +28,9 @@ enum { SEV_PEK_CERT_IMPORT, SEV_GET_ID, /* This command is deprecated, use SEV_GET_ID2 */ SEV_GET_ID2, + SNP_PLATFORM_STATUS, + SNP_COMMIT, + SNP_SET_CONFIG, SEV_MAX, }; @@ -69,6 +72,12 @@ typedef enum { SEV_RET_RESOURCE_LIMIT, SEV_RET_SECURE_DATA_INVALID, SEV_RET_INVALID_KEY = 0x27, + SEV_RET_INVALID_PAGE_SIZE, + SEV_RET_INVALID_PAGE_STATE, + SEV_RET_INVALID_MDATA_ENTRY, + SEV_RET_INVALID_PAGE_OWNER, + SEV_RET_INVALID_PAGE_AEAD_OFLOW, + SEV_RET_RMP_INIT_REQUIRED, SEV_RET_MAX, } sev_ret_code; @@ -155,6 +164,56 @@ struct sev_user_data_get_id2 { __u32 length; /* In/Out */ } __attribute__((packed)); +/** + * struct sev_user_data_snp_status - SNP status + * + * @api_major: API major version + * @api_minor: API minor version + * @state: current platform state + * @is_rmp_initialized: whether RMP is initialized or not + * @rsvd: reserved + * @build_id: firmware build id for the API version + * @mask_chip_id: whether chip id is present in attestation reports or not + * @mask_chip_key: whether attestation reports are signed or not + * @vlek_en: VLEK (Version Loaded Endorsement Key) hashstick is loaded + * @rsvd1: reserved + * @guest_count: the number of guest currently managed by the firmware + * @current_tcb_version: current TCB version + * @reported_tcb_version: reported TCB version + */ +struct sev_user_data_snp_status { + __u8 api_major; /* Out */ + __u8 api_minor; /* Out */ + __u8 state; /* Out */ + __u8 is_rmp_initialized:1; /* Out */ + __u8 rsvd:7; + __u32 build_id; /* Out */ + __u32 mask_chip_id:1; /* Out */ + __u32 mask_chip_key:1; /* Out */ + __u32 vlek_en:1; /* Out */ + __u32 rsvd1:29; + __u32 guest_count; /* Out */ + __u64 current_tcb_version; /* Out */ + __u64 reported_tcb_version; /* Out */ +} __attribute__((packed)); + +/** + * struct sev_user_data_snp_config - system wide configuration value for SNP. + * + * @reported_tcb: the TCB version to report in the guest attestation report. + * @mask_chip_id: whether chip id is present in attestation reports or not + * @mask_chip_key: whether attestation reports are signed or not + * @rsvd: reserved + * @rsvd1: reserved + */ +struct sev_user_data_snp_config { + __u64 reported_tcb ; /* In */ + __u32 mask_chip_id:1; /* In */ + __u32 mask_chip_key:1; /* In */ + __u32 rsvd:30; /* In */ + __u8 rsvd1[52]; +} __attribute__((packed)); + /** * struct sev_issue_cmd - SEV ioctl parameters * diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index 649560c685..bea6973906 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -227,4 +227,11 @@ */ #define VHOST_VDPA_GET_VRING_DESC_GROUP _IOWR(VHOST_VIRTIO, 0x7F, \ struct vhost_vring_state) + +/* Get the queue size of a specific virtqueue. + * userspace set the vring index in vhost_vring_state.index + * kernel set the queue size in vhost_vring_state.num + */ +#define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x80, \ + struct vhost_vring_state) #endif From 08b2d15cdd0d3fbbe37ce23bf192b770db3a7539 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Mar 2024 17:45:56 -0400 Subject: [PATCH 0039/2884] runstate: skip initial CPU reset if reset is not actually possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now, the system reset is concluded by a call to cpu_synchronize_all_post_reset() in order to sync any changes that the machine reset callback applied to the CPU state. However, for VMs with encrypted state such as SEV-ES guests (currently the only case of guests with non-resettable CPUs) this cannot be done, because guest state has already been finalized by machine-init-done notifiers. cpu_synchronize_all_post_reset() does nothing on these guests, and actually we would like to make it fail if called once guest has been encrypted. So, assume that boards that support non-resettable CPUs do not touch CPU state and that all such setup is done before, at the time of cpu_synchronize_all_post_init(). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- system/runstate.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/system/runstate.c b/system/runstate.c index d6ab860eca..cb4905a40f 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -501,7 +501,20 @@ void qemu_system_reset(ShutdownCause reason) default: qapi_event_send_reset(shutdown_caused_by_guest(reason), reason); } - cpu_synchronize_all_post_reset(); + + /* + * Some boards use the machine reset callback to point CPUs to the firmware + * entry point. Assume that this is not the case for boards that support + * non-resettable CPUs (currently used only for confidential guests), in + * which case cpu_synchronize_all_post_init() is enough because + * it does _more_ than cpu_synchronize_all_post_reset(). + */ + if (cpus_are_resettable()) { + cpu_synchronize_all_post_reset(); + } else { + assert(runstate_check(RUN_STATE_PRELAUNCH)); + } + vm_set_suspended(false); } From 5c3131c392f84c660033d511ec39872d8beb4b1e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Mar 2024 14:41:10 -0400 Subject: [PATCH 0040/2884] KVM: track whether guest state is encrypted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far, KVM has allowed KVM_GET/SET_* ioctls to execute even if the guest state is encrypted, in which case they do nothing. For the new API using VM types, instead, the ioctls will fail which is a safer and more robust approach. The new API will be the only one available for SEV-SNP and TDX, but it is also usable for SEV and SEV-ES. In preparation for that, require architecture-specific KVM code to communicate the point at which guest state is protected (which must be after kvm_cpu_synchronize_post_init(), though that might change in the future in order to suppor migration). From that point, skip reading registers so that cpu->vcpu_dirty is never true: if it ever becomes true, kvm_arch_put_registers() will fail miserably. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 17 ++++++++++++++--- include/sysemu/kvm.h | 2 ++ include/sysemu/kvm_int.h | 1 + target/i386/sev.c | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d58916e33a..d6ebadbf38 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2703,7 +2703,7 @@ bool kvm_cpu_check_are_resettable(void) static void do_kvm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->vcpu_dirty) { + if (!cpu->vcpu_dirty && !kvm_state->guest_state_protected) { int ret = kvm_arch_get_registers(cpu); if (ret) { error_report("Failed to get registers: %s", strerror(-ret)); @@ -2717,7 +2717,7 @@ static void do_kvm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) void kvm_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->vcpu_dirty && !kvm_state->guest_state_protected) { run_on_cpu(cpu, do_kvm_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -2752,7 +2752,13 @@ static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) void kvm_cpu_synchronize_post_init(CPUState *cpu) { - run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL); + if (!kvm_state->guest_state_protected) { + /* + * This runs before the machine_init_done notifiers, and is the last + * opportunity to synchronize the state of confidential guests. + */ + run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL); + } } static void do_kvm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) @@ -4099,3 +4105,8 @@ void query_stats_schemas_cb(StatsSchemaList **result, Error **errp) query_stats_schema_vcpu(first_cpu, &stats_args); } } + +void kvm_mark_guest_state_protected(void) +{ + kvm_state->guest_state_protected = true; +} diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 2cba899270..14b1ddb3be 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -539,6 +539,8 @@ bool kvm_dirty_ring_enabled(void); uint32_t kvm_dirty_ring_size(void); +void kvm_mark_guest_state_protected(void); + /** * kvm_hwpoisoned_mem - indicate if there is any hwpoisoned page * reported for the VM. diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 94488d2c1a..227b61fec3 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -87,6 +87,7 @@ struct KVMState bool kernel_irqchip_required; OnOffAuto kernel_irqchip_split; bool sync_mmu; + bool guest_state_protected; uint64_t manual_dirty_log_protect; /* The man page (and posix) say ioctl numbers are signed int, but * they're not. Linux, glibc and *BSD all treat ioctl numbers as diff --git a/target/i386/sev.c b/target/i386/sev.c index b8f79d34d1..c49a8fd55e 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -755,6 +755,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) if (ret) { exit(1); } + kvm_mark_guest_state_protected(); } /* query the measurement blob length */ From a99c0c66ebe7d8db3af6f16689ade9375247e43e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Mar 2024 14:41:33 -0400 Subject: [PATCH 0041/2884] KVM: remove kvm_arch_cpu_check_are_resettable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Board reset requires writing a fresh CPU state. As far as KVM is concerned, the only thing that blocks reset is that CPU state is encrypted; therefore, kvm_cpus_are_resettable() can simply check if that is the case. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-accel-ops.c | 2 +- accel/kvm/kvm-all.c | 5 ----- include/sysemu/kvm.h | 10 ---------- target/arm/kvm.c | 5 ----- target/i386/kvm/kvm.c | 5 ----- target/loongarch/kvm/kvm.c | 5 ----- target/mips/kvm.c | 5 ----- target/ppc/kvm.c | 5 ----- target/riscv/kvm/kvm-cpu.c | 5 ----- target/s390x/kvm/kvm.c | 5 ----- 10 files changed, 1 insertion(+), 51 deletions(-) diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index f5ac643fca..94c828ac8d 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -82,7 +82,7 @@ static bool kvm_vcpu_thread_is_idle(CPUState *cpu) static bool kvm_cpus_are_resettable(void) { - return !kvm_enabled() || kvm_cpu_check_are_resettable(); + return !kvm_enabled() || !kvm_state->guest_state_protected; } #ifdef TARGET_KVM_HAVE_GUEST_DEBUG diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d6ebadbf38..5a2fbee32a 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2696,11 +2696,6 @@ void kvm_flush_coalesced_mmio_buffer(void) s->coalesced_flush_in_progress = false; } -bool kvm_cpu_check_are_resettable(void) -{ - return kvm_arch_cpu_check_are_resettable(); -} - static void do_kvm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { if (!cpu->vcpu_dirty && !kvm_state->guest_state_protected) { diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 14b1ddb3be..bd247f3a23 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -525,16 +525,6 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); /* Notify resamplefd for EOI of specific interrupts. */ void kvm_resample_fd_notify(int gsi); -/** - * kvm_cpu_check_are_resettable - return whether CPUs can be reset - * - * Returns: true: CPUs are resettable - * false: CPUs are not resettable - */ -bool kvm_cpu_check_are_resettable(void); - -bool kvm_arch_cpu_check_are_resettable(void); - bool kvm_dirty_ring_enabled(void); uint32_t kvm_dirty_ring_size(void); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index ab85d628a8..21ebbf3b8f 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1598,11 +1598,6 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) return (data - 32) & 0xffff; } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - static void kvm_arch_get_eager_split_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c64b9562c0..c8fdab40fc 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5619,11 +5619,6 @@ bool kvm_has_waitpkg(void) return has_msr_umwait; } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return !sev_es_enabled(); -} - #define ARCH_REQ_XCOMP_GUEST_PERM 0x1025 void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index d630cc39cb..8224d94333 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -733,11 +733,6 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cs) return true; } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { int ret = 0; diff --git a/target/mips/kvm.c b/target/mips/kvm.c index 6c52e59f55..a631ab544f 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -1273,11 +1273,6 @@ int kvm_arch_get_default_type(MachineState *machine) return -1; } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - void kvm_arch_accel_class_init(ObjectClass *oc) { } diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 8231feb2d4..63930d4a77 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2956,11 +2956,6 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) } } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - void kvm_arch_accel_class_init(ObjectClass *oc) { } diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 6a6c6cae80..49d2f3ad58 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1475,11 +1475,6 @@ void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level) } } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - static int aia_mode; static const char *kvm_aia_mode_str(uint64_t mode) diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 4ce809c5d4..4dcd757cdc 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -2622,11 +2622,6 @@ void kvm_s390_stop_interrupt(S390CPU *cpu) kvm_s390_vcpu_interrupt(cpu, &irq); } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - int kvm_s390_get_zpci_op(void) { return cap_zpci_op; From d82e9c843d662f13821026618aba936eda31a6c0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Mar 2024 11:07:43 -0400 Subject: [PATCH 0042/2884] target/i386: introduce x86-confidential-guest Introduce a common superclass for x86 confidential guest implementations. It will extend ConfidentialGuestSupportClass with a method that provides the VM type to be passed to KVM_CREATE_VM. Signed-off-by: Paolo Bonzini --- target/i386/confidential-guest.c | 33 ++++++++++++++++++++++++++ target/i386/confidential-guest.h | 40 ++++++++++++++++++++++++++++++++ target/i386/meson.build | 2 +- target/i386/sev.c | 6 ++--- 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 target/i386/confidential-guest.c create mode 100644 target/i386/confidential-guest.h diff --git a/target/i386/confidential-guest.c b/target/i386/confidential-guest.c new file mode 100644 index 0000000000..b3727845ad --- /dev/null +++ b/target/i386/confidential-guest.c @@ -0,0 +1,33 @@ +/* + * QEMU Confidential Guest support + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "confidential-guest.h" + +OBJECT_DEFINE_ABSTRACT_TYPE(X86ConfidentialGuest, + x86_confidential_guest, + X86_CONFIDENTIAL_GUEST, + CONFIDENTIAL_GUEST_SUPPORT) + +static void x86_confidential_guest_class_init(ObjectClass *oc, void *data) +{ +} + +static void x86_confidential_guest_init(Object *obj) +{ +} + +static void x86_confidential_guest_finalize(Object *obj) +{ +} diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h new file mode 100644 index 0000000000..ca12d5a8fb --- /dev/null +++ b/target/i386/confidential-guest.h @@ -0,0 +1,40 @@ +/* + * x86-specific confidential guest methods. + * + * Copyright (c) 2024 Red Hat Inc. + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef TARGET_I386_CG_H +#define TARGET_I386_CG_H + +#include "qom/object.h" + +#include "exec/confidential-guest-support.h" + +#define TYPE_X86_CONFIDENTIAL_GUEST "x86-confidential-guest" + +OBJECT_DECLARE_TYPE(X86ConfidentialGuest, + X86ConfidentialGuestClass, + X86_CONFIDENTIAL_GUEST) + +struct X86ConfidentialGuest { + /* */ + ConfidentialGuestSupport parent_obj; +}; + +/** + * X86ConfidentialGuestClass: + * + * Class to be implemented by confidential-guest-support concrete objects + * for the x86 target. + */ +struct X86ConfidentialGuestClass { + /* */ + ConfidentialGuestSupportClass parent; +}; +#endif diff --git a/target/i386/meson.build b/target/i386/meson.build index 7c74bfa859..8abce725f8 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -6,7 +6,7 @@ i386_ss.add(files( 'xsave_helper.c', 'cpu-dump.c', )) -i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest.c')) # x86 cpu type i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) diff --git a/target/i386/sev.c b/target/i386/sev.c index c49a8fd55e..ebe36d4c10 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -35,7 +35,7 @@ #include "monitor/monitor.h" #include "monitor/hmp-target.h" #include "qapi/qapi-commands-misc-target.h" -#include "exec/confidential-guest-support.h" +#include "confidential-guest.h" #include "hw/i386/pc.h" #include "exec/address-spaces.h" @@ -54,7 +54,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) * -machine ...,memory-encryption=sev0 */ struct SevGuestState { - ConfidentialGuestSupport parent_obj; + X86ConfidentialGuest parent_obj; /* configuration parameters */ char *sev_device; @@ -1372,7 +1372,7 @@ sev_guest_instance_init(Object *obj) /* sev guest info */ static const TypeInfo sev_guest_info = { - .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, + .parent = TYPE_X86_CONFIDENTIAL_GUEST, .name = TYPE_SEV_GUEST, .instance_size = sizeof(SevGuestState), .instance_finalize = sev_guest_finalize, From ee88612df1e8d6c2bfec75bff3f9482ea44acec1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 Mar 2024 15:29:33 +0100 Subject: [PATCH 0043/2884] target/i386: Implement mc->kvm_type() to get VM type KVM is introducing a new API to create confidential guests, which will be used by TDX and SEV-SNP but is also available for SEV and SEV-ES. The API uses the VM type argument to KVM_CREATE_VM to identify which confidential computing technology to use. Since there are no other expected uses of VM types, delegate mc->kvm_type() for x86 boards to the confidential-guest-support object pointed to by ms->cgs. For example, if a sev-guest object is specified to confidential-guest-support, like, qemu -machine ...,confidential-guest-support=sev0 \ -object sev-guest,id=sev0,... it will check if a VM type KVM_X86_SEV_VM or KVM_X86_SEV_ES_VM is supported, and if so use them together with the KVM_SEV_INIT2 function of the KVM_MEMORY_ENCRYPT_OP ioctl. If not, it will fall back to KVM_SEV_INIT and KVM_SEV_ES_INIT. This is a preparatory work towards TDX and SEV-SNP support, but it will also enable support for VMSA features such as DebugSwap, which are only available via KVM_SEV_INIT2. Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- hw/i386/x86.c | 11 ++++++++ target/i386/confidential-guest.h | 19 ++++++++++++++ target/i386/kvm/kvm.c | 44 ++++++++++++++++++++++++++++++++ target/i386/kvm/kvm_i386.h | 2 ++ 4 files changed, 76 insertions(+) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 84a4801977..3d5b51e92d 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1381,6 +1381,16 @@ static void machine_set_sgx_epc(Object *obj, Visitor *v, const char *name, qapi_free_SgxEPCList(list); } +static int x86_kvm_type(MachineState *ms, const char *vm_type) +{ + /* + * No x86 machine has a kvm-type property. If one is added that has + * it, it should call kvm_get_vm_type() directly or not use it at all. + */ + assert(vm_type == NULL); + return kvm_enabled() ? kvm_get_vm_type(ms) : 0; +} + static void x86_machine_initfn(Object *obj) { X86MachineState *x86ms = X86_MACHINE(obj); @@ -1405,6 +1415,7 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = x86_cpu_index_to_props; mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; + mc->kvm_type = x86_kvm_type; x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index ca12d5a8fb..532e172a60 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -36,5 +36,24 @@ struct X86ConfidentialGuest { struct X86ConfidentialGuestClass { /* */ ConfidentialGuestSupportClass parent; + + /* */ + int (*kvm_type)(X86ConfidentialGuest *cg); }; + +/** + * x86_confidential_guest_kvm_type: + * + * Calls #X86ConfidentialGuestClass.unplug callback of @plug_handler. + */ +static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->kvm_type) { + return klass->kvm_type(cg); + } else { + return 0; + } +} #endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c8fdab40fc..ae76924bc6 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -31,6 +31,7 @@ #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "kvm_i386.h" +#include "../confidential-guest.h" #include "sev.h" #include "xen-emu.h" #include "hyperv.h" @@ -161,6 +162,49 @@ static KVMMSRHandlers msr_handlers[KVM_MSR_FILTER_MAX_RANGES]; static RateLimit bus_lock_ratelimit_ctrl; static int kvm_get_one_msr(X86CPU *cpu, int index, uint64_t *value); +static const char *vm_type_name[] = { + [KVM_X86_DEFAULT_VM] = "default", +}; + +bool kvm_is_vm_type_supported(int type) +{ + uint32_t machine_types; + + /* + * old KVM doesn't support KVM_CAP_VM_TYPES but KVM_X86_DEFAULT_VM + * is always supported + */ + if (type == KVM_X86_DEFAULT_VM) { + return true; + } + + machine_types = kvm_check_extension(KVM_STATE(current_machine->accelerator), + KVM_CAP_VM_TYPES); + return !!(machine_types & BIT(type)); +} + +int kvm_get_vm_type(MachineState *ms) +{ + int kvm_type = KVM_X86_DEFAULT_VM; + + if (ms->cgs) { + if (!object_dynamic_cast(OBJECT(ms->cgs), TYPE_X86_CONFIDENTIAL_GUEST)) { + error_report("configuration type %s not supported for x86 guests", + object_get_typename(OBJECT(ms->cgs))); + exit(1); + } + kvm_type = x86_confidential_guest_kvm_type( + X86_CONFIDENTIAL_GUEST(ms->cgs)); + } + + if (!kvm_is_vm_type_supported(kvm_type)) { + error_report("vm-type %s not supported by KVM", vm_type_name[kvm_type]); + exit(1); + } + + return kvm_type; +} + bool kvm_has_smm(void) { return kvm_vm_check_extension(kvm_state, KVM_CAP_X86_SMM); diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 30fedcffea..6b44844d95 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -37,6 +37,7 @@ bool kvm_hv_vpindex_settable(void); bool kvm_enable_sgx_provisioning(KVMState *s); bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); +int kvm_get_vm_type(MachineState *ms); void kvm_arch_reset_vcpu(X86CPU *cs); void kvm_arch_after_reset_vcpu(X86CPU *cpu); void kvm_arch_do_init_vcpu(X86CPU *cs); @@ -49,6 +50,7 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); #ifdef CONFIG_KVM +bool kvm_is_vm_type_supported(int type); bool kvm_has_adjust_clock_stable(void); bool kvm_has_exception_payload(void); void kvm_synchronize_all_tsc(void); From 663e2f443e5722370708ce2f4c27d94a2087d2d3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 Mar 2024 15:30:25 +0100 Subject: [PATCH 0044/2884] target/i386: SEV: use KVM_SEV_INIT2 if possible Implement support for the KVM_X86_SEV_VM and KVM_X86_SEV_ES_VM virtual machine types, and the KVM_SEV_INIT2 function of KVM_MEMORY_ENCRYPT_OP. These replace the KVM_SEV_INIT and KVM_SEV_ES_INIT functions, and have several advantages: - sharing the initialization sequence with SEV-SNP and TDX - allowing arguments including the set of desired VMSA features - protection against invalid use of KVM_GET/SET_* ioctls for guests with encrypted state If the KVM_X86_SEV_VM and KVM_X86_SEV_ES_VM types are not supported, fall back to KVM_SEV_INIT and KVM_SEV_ES_INIT (which use the default x86 VM type). Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 2 ++ target/i386/sev.c | 41 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index ae76924bc6..c5943605ee 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -164,6 +164,8 @@ static int kvm_get_one_msr(X86CPU *cpu, int index, uint64_t *value); static const char *vm_type_name[] = { [KVM_X86_DEFAULT_VM] = "default", + [KVM_X86_SEV_VM] = "SEV", + [KVM_X86_SEV_ES_VM] = "SEV-ES", }; bool kvm_is_vm_type_supported(int type) diff --git a/target/i386/sev.c b/target/i386/sev.c index ebe36d4c10..9dab4060b8 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -26,6 +26,7 @@ #include "qemu/error-report.h" #include "crypto/hash.h" #include "sysemu/kvm.h" +#include "kvm/kvm_i386.h" #include "sev.h" #include "sysemu/sysemu.h" #include "sysemu/runstate.h" @@ -56,6 +57,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) struct SevGuestState { X86ConfidentialGuest parent_obj; + int kvm_type; + /* configuration parameters */ char *sev_device; uint32_t policy; @@ -850,6 +853,26 @@ sev_vm_state_change(void *opaque, bool running, RunState state) } } +static int sev_kvm_type(X86ConfidentialGuest *cg) +{ + SevGuestState *sev = SEV_GUEST(cg); + int kvm_type; + + if (sev->kvm_type != -1) { + goto out; + } + + kvm_type = (sev->policy & SEV_POLICY_ES) ? KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; + if (kvm_is_vm_type_supported(kvm_type)) { + sev->kvm_type = kvm_type; + } else { + sev->kvm_type = KVM_X86_DEFAULT_VM; + } + +out: + return sev->kvm_type; +} + static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { SevGuestState *sev = SEV_GUEST(cgs); @@ -929,13 +952,19 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) __func__); goto err; } - cmd = KVM_SEV_ES_INIT; - } else { - cmd = KVM_SEV_INIT; } trace_kvm_sev_init(); - ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); + if (sev_kvm_type(X86_CONFIDENTIAL_GUEST(sev)) == KVM_X86_DEFAULT_VM) { + cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT; + + ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); + } else { + struct kvm_sev_init args = { 0 }; + + ret = sev_ioctl(sev->sev_fd, KVM_SEV_INIT2, &args, &fw_error); + } + if (ret) { error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); @@ -1327,8 +1356,10 @@ static void sev_guest_class_init(ObjectClass *oc, void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); klass->kvm_init = sev_kvm_init; + x86_klass->kvm_type = sev_kvm_type; object_class_property_add_str(oc, "sev-device", sev_guest_get_sev_device, @@ -1357,6 +1388,8 @@ sev_guest_instance_init(Object *obj) { SevGuestState *sev = SEV_GUEST(obj); + sev->kvm_type = -1; + sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); sev->policy = DEFAULT_GUEST_POLICY; object_property_add_uint32_ptr(obj, "policy", &sev->policy, From 023267334da375226720e62963df9545aa8fc2fd Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 9 Apr 2024 18:07:41 -0500 Subject: [PATCH 0045/2884] i386/sev: Add 'legacy-vm-type' parameter for SEV guest objects QEMU will currently automatically make use of the KVM_SEV_INIT2 API for initializing SEV and SEV-ES guests verses the older KVM_SEV_INIT/KVM_SEV_ES_INIT interfaces. However, the older interfaces will silently avoid sync'ing FPU/XSAVE state to the VMSA prior to encryption, thus relying on behavior and measurements that assume the related fields to be allow zero. With KVM_SEV_INIT2, this state is now synced into the VMSA, resulting in measurements changes and, theoretically, behaviorial changes, though the latter are unlikely to be seen in practice. To allow a smooth transition to the newer interface, while still providing a mechanism to maintain backward compatibility with VMs created using the older interfaces, provide a new command-line parameter: -object sev-guest,legacy-vm-type=true,... and have it default to false. Signed-off-by: Michael Roth Message-ID: <20240409230743.962513-2-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- qapi/qom.json | 11 ++++++++++- target/i386/sev.c | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/qapi/qom.json b/qapi/qom.json index 85e6b4f84a..38dde6d785 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -898,6 +898,14 @@ # designated guest firmware page for measured boot with -kernel # (default: false) (since 6.2) # +# @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating the VM. +# The newer KVM_SEV_INIT2 interface syncs additional vCPU +# state when initializing the VMSA structures, which will +# result in a different guest measurement. Set this to +# maintain compatibility with older QEMU or kernel versions +# that rely on legacy KVM_SEV_INIT behavior. +# (default: false) (since 9.1) +# # Since: 2.12 ## { 'struct': 'SevGuestProperties', @@ -908,7 +916,8 @@ '*handle': 'uint32', '*cbitpos': 'uint32', 'reduced-phys-bits': 'uint32', - '*kernel-hashes': 'bool' } } + '*kernel-hashes': 'bool', + '*legacy-vm-type': 'bool' } } ## # @ThreadContextProperties: diff --git a/target/i386/sev.c b/target/i386/sev.c index 9dab4060b8..f4ee317cb0 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -67,6 +67,7 @@ struct SevGuestState { uint32_t cbitpos; uint32_t reduced_phys_bits; bool kernel_hashes; + bool legacy_vm_type; /* runtime state */ uint32_t handle; @@ -356,6 +357,16 @@ static void sev_guest_set_kernel_hashes(Object *obj, bool value, Error **errp) sev->kernel_hashes = value; } +static bool sev_guest_get_legacy_vm_type(Object *obj, Error **errp) +{ + return SEV_GUEST(obj)->legacy_vm_type; +} + +static void sev_guest_set_legacy_vm_type(Object *obj, bool value, Error **errp) +{ + SEV_GUEST(obj)->legacy_vm_type = value; +} + bool sev_enabled(void) { @@ -863,7 +874,7 @@ static int sev_kvm_type(X86ConfidentialGuest *cg) } kvm_type = (sev->policy & SEV_POLICY_ES) ? KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; - if (kvm_is_vm_type_supported(kvm_type)) { + if (kvm_is_vm_type_supported(kvm_type) && !sev->legacy_vm_type) { sev->kvm_type = kvm_type; } else { sev->kvm_type = KVM_X86_DEFAULT_VM; @@ -1381,6 +1392,11 @@ sev_guest_class_init(ObjectClass *oc, void *data) sev_guest_set_kernel_hashes); object_class_property_set_description(oc, "kernel-hashes", "add kernel hashes to guest firmware for measured Linux boot"); + object_class_property_add_bool(oc, "legacy-vm-type", + sev_guest_get_legacy_vm_type, + sev_guest_set_legacy_vm_type); + object_class_property_set_description(oc, "legacy-vm-type", + "use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions."); } static void From ea7fbd37537b3a598335c21ccb2ea674630fc810 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 9 Apr 2024 18:07:43 -0500 Subject: [PATCH 0046/2884] hw/i386/sev: Use legacy SEV VM types for older machine types Newer 9.1 machine types will default to using the KVM_SEV_INIT2 API for creating SEV/SEV-ES going forward. However, this API results in guest measurement changes which are generally not expected for users of these older guest types and can cause disruption if they switch to a newer QEMU/kernel version. Avoid this by continuing to use the older KVM_SEV_INIT/KVM_SEV_ES_INIT APIs for older machine types. Signed-off-by: Michael Roth Message-ID: <20240409230743.962513-4-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/sev.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 41909f7bd7..08c7de416f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -80,6 +80,7 @@ GlobalProperty pc_compat_9_0[] = { { TYPE_X86_CPU, "guest-phys-bits", "0" }, + { "sev-guest", "legacy-vm-type", "true" }, }; const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0); diff --git a/target/i386/sev.c b/target/i386/sev.c index f4ee317cb0..d30b68c11e 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1417,6 +1417,7 @@ sev_guest_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "reduced-phys-bits", &sev->reduced_phys_bits, OBJ_PROP_FLAG_READWRITE); + object_apply_compat_props(obj); } /* sev guest info */ From 72853afc638b3e28779c86dd05da2f3bb149fe2c Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 29 Feb 2024 01:36:25 -0500 Subject: [PATCH 0047/2884] trace/kvm: Split address space and slot id in trace_kvm_set_user_memory() The upper 16 bits of kvm_userspace_memory_region::slot are address space id. Parse it separately in trace_kvm_set_user_memory(). Signed-off-by: Xiaoyao Li Message-ID: <20240229063726.610065-5-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 5 +++-- accel/kvm/trace-events | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 5a2fbee32a..ed50c80b1e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -303,8 +303,9 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); slot->old_flags = mem.flags; err: - trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, - mem.memory_size, mem.userspace_addr, ret); + trace_kvm_set_user_memory(mem.slot >> 16, (uint16_t)mem.slot, mem.flags, + mem.guest_phys_addr, mem.memory_size, + mem.userspace_addr, ret); if (ret < 0) { error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d," " start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s", diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index a25902597b..9f599abc17 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -15,7 +15,7 @@ kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_release_virq(int virq) "virq %d" kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" -kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" +kvm_set_user_memory(uint16_t as, uint16_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "AddrSpace#%d Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32 kvm_resample_fd_notify(int gsi) "gsi %d" kvm_dirty_ring_full(int id) "vcpu %d" From 0811baed49010a9b651b8029ab6b9828b09a884f Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 20 Mar 2024 03:39:06 -0500 Subject: [PATCH 0048/2884] kvm: Introduce support for memory_attributes Introduce the helper functions to set the attributes of a range of memory to private or shared. This is necessary to notify KVM the private/shared attribute of each gpa range. KVM needs the information to decide the GPA needs to be mapped at hva-based shared memory or guest_memfd based private memory. Signed-off-by: Xiaoyao Li Message-ID: <20240320083945.991426-11-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 32 ++++++++++++++++++++++++++++++++ include/sysemu/kvm.h | 4 ++++ 2 files changed, 36 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index ed50c80b1e..db0b1a16ed 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -91,6 +91,7 @@ bool kvm_msi_use_devid; static bool kvm_has_guest_debug; static int kvm_sstep_flags; static bool kvm_immediate_exit; +static uint64_t kvm_supported_memory_attributes; static hwaddr kvm_max_slot_size = ~0; static const KVMCapabilityInfo kvm_required_capabilites[] = { @@ -1266,6 +1267,36 @@ void kvm_set_max_memslot_size(hwaddr max_slot_size) kvm_max_slot_size = max_slot_size; } +static int kvm_set_memory_attributes(hwaddr start, uint64_t size, uint64_t attr) +{ + struct kvm_memory_attributes attrs; + int r; + + assert((attr & kvm_supported_memory_attributes) == attr); + attrs.attributes = attr; + attrs.address = start; + attrs.size = size; + attrs.flags = 0; + + r = kvm_vm_ioctl(kvm_state, KVM_SET_MEMORY_ATTRIBUTES, &attrs); + if (r) { + error_report("failed to set memory (0x%" HWADDR_PRIx "+0x%" PRIx64 ") " + "with attr 0x%" PRIx64 " error '%s'", + start, size, attr, strerror(errno)); + } + return r; +} + +int kvm_set_memory_attributes_private(hwaddr start, uint64_t size) +{ + return kvm_set_memory_attributes(start, size, KVM_MEMORY_ATTRIBUTE_PRIVATE); +} + +int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size) +{ + return kvm_set_memory_attributes(start, size, 0); +} + /* Called with KVMMemoryListener.slots_lock held */ static void kvm_set_phys_mem(KVMMemoryListener *kml, MemoryRegionSection *section, bool add) @@ -2387,6 +2418,7 @@ static int kvm_init(MachineState *ms) goto err; } + kvm_supported_memory_attributes = kvm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES); kvm_immediate_exit = kvm_check_extension(s, KVM_CAP_IMMEDIATE_EXIT); s->nr_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index bd247f3a23..594ae9b460 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -536,4 +536,8 @@ void kvm_mark_guest_state_protected(void); * reported for the VM. */ bool kvm_hwpoisoned_mem(void); + +int kvm_set_memory_attributes_private(hwaddr start, uint64_t size); +int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size); + #endif From 15f7a80c49cb3637f62fa37fa4a17da913bd91ff Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 20 Mar 2024 03:39:02 -0500 Subject: [PATCH 0049/2884] RAMBlock: Add support of KVM private guest memfd Add KVM guest_memfd support to RAMBlock so both normal hva based memory and kvm guest memfd based private memory can be associated in one RAMBlock. Introduce new flag RAM_GUEST_MEMFD. When it's set, it calls KVM ioctl to create private guest_memfd during RAMBlock setup. Allocating a new RAM_GUEST_MEMFD flag to instruct the setup of guest memfd is more flexible and extensible than simply relying on the VM type because in the future we may have the case that not all the memory of a VM need guest memfd. As a benefit, it also avoid getting MachineState in memory subsystem. Note, RAM_GUEST_MEMFD is supposed to be set for memory backends of confidential guests, such as TDX VM. How and when to set it for memory backends will be implemented in the following patches. Introduce memory_region_has_guest_memfd() to query if the MemoryRegion has KVM guest_memfd allocated. Signed-off-by: Xiaoyao Li Reviewed-by: David Hildenbrand Message-ID: <20240320083945.991426-7-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 28 ++++++++++++++++++++++++++++ accel/stubs/kvm-stub.c | 5 +++++ include/exec/memory.h | 20 +++++++++++++++++--- include/exec/ram_addr.h | 2 +- include/exec/ramblock.h | 1 + include/sysemu/kvm.h | 2 ++ system/memory.c | 5 +++++ system/physmem.c | 34 +++++++++++++++++++++++++++++++--- 8 files changed, 90 insertions(+), 7 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index db0b1a16ed..1b7bbd838c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -92,6 +92,7 @@ static bool kvm_has_guest_debug; static int kvm_sstep_flags; static bool kvm_immediate_exit; static uint64_t kvm_supported_memory_attributes; +static bool kvm_guest_memfd_supported; static hwaddr kvm_max_slot_size = ~0; static const KVMCapabilityInfo kvm_required_capabilites[] = { @@ -2419,6 +2420,11 @@ static int kvm_init(MachineState *ms) } kvm_supported_memory_attributes = kvm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES); + kvm_guest_memfd_supported = + kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) && + kvm_check_extension(s, KVM_CAP_USER_MEMORY2) && + (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE); + kvm_immediate_exit = kvm_check_extension(s, KVM_CAP_IMMEDIATE_EXIT); s->nr_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); @@ -4138,3 +4144,25 @@ void kvm_mark_guest_state_protected(void) { kvm_state->guest_state_protected = true; } + +int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp) +{ + int fd; + struct kvm_create_guest_memfd guest_memfd = { + .size = size, + .flags = flags, + }; + + if (!kvm_guest_memfd_supported) { + error_setg(errp, "KVM does not support guest_memfd"); + return -1; + } + + fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_GUEST_MEMFD, &guest_memfd); + if (fd < 0) { + error_setg_errno(errp, errno, "Error creating KVM guest_memfd"); + return -1; + } + + return fd; +} diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index ca38172884..8e0eb22e61 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -129,3 +129,8 @@ bool kvm_hwpoisoned_mem(void) { return false; } + +int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp) +{ + return -ENOSYS; +} diff --git a/include/exec/memory.h b/include/exec/memory.h index 8626a355b3..679a847685 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -243,6 +243,9 @@ typedef struct IOMMUTLBEvent { /* RAM FD is opened read-only */ #define RAM_READONLY_FD (1 << 11) +/* RAM can be private that has kvm guest memfd backend */ +#define RAM_GUEST_MEMFD (1 << 12) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -1307,7 +1310,8 @@ bool memory_region_init_ram_nomigrate(MemoryRegion *mr, * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_NORESERVE. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_NORESERVE, + * RAM_GUEST_MEMFD. * @errp: pointer to Error*, to store an error if it happens. * * Note that this function does not do anything to cause the data in the @@ -1369,7 +1373,7 @@ bool memory_region_init_resizeable_ram(MemoryRegion *mr, * (getpagesize()) will be used. * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, - * RAM_READONLY_FD + * RAM_READONLY_FD, RAM_GUEST_MEMFD * @path: the path in which to allocate the RAM. * @offset: offset within the file referenced by path * @errp: pointer to Error*, to store an error if it happens. @@ -1399,7 +1403,7 @@ bool memory_region_init_ram_from_file(MemoryRegion *mr, * @size: size of the region. * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, - * RAM_READONLY_FD + * RAM_READONLY_FD, RAM_GUEST_MEMFD * @fd: the fd to mmap. * @offset: offset within the file referenced by fd * @errp: pointer to Error*, to store an error if it happens. @@ -1722,6 +1726,16 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) */ bool memory_region_is_protected(MemoryRegion *mr); +/** + * memory_region_has_guest_memfd: check whether a memory region has guest_memfd + * associated + * + * Returns %true if a memory region's ram_block has valid guest_memfd assigned. + * + * @mr: the memory region being queried + */ +bool memory_region_has_guest_memfd(MemoryRegion *mr); + /** * memory_region_get_iommu: check whether a memory region is an iommu * diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index de45ba7bc9..07c8f86375 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -110,7 +110,7 @@ long qemu_maxrampagesize(void); * @mr: the memory region where the ram block is * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, - * RAM_READONLY_FD + * RAM_READONLY_FD, RAM_GUEST_MEMFD * @mem_path or @fd: specify the backing file or device * @offset: Offset into target file * @errp: pointer to Error*, to store an error if it happens diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h index 848915ea5b..459c8917de 100644 --- a/include/exec/ramblock.h +++ b/include/exec/ramblock.h @@ -41,6 +41,7 @@ struct RAMBlock { QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; int fd; uint64_t fd_offset; + int guest_memfd; size_t page_size; /* dirty bitmap used during migration */ unsigned long *bmap; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 594ae9b460..217f3fe17b 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -537,6 +537,8 @@ void kvm_mark_guest_state_protected(void); */ bool kvm_hwpoisoned_mem(void); +int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp); + int kvm_set_memory_attributes_private(hwaddr start, uint64_t size); int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size); diff --git a/system/memory.c b/system/memory.c index a229a79988..c756950c0c 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1850,6 +1850,11 @@ bool memory_region_is_protected(MemoryRegion *mr) return mr->ram && (mr->ram_block->flags & RAM_PROTECTED); } +bool memory_region_has_guest_memfd(MemoryRegion *mr) +{ + return mr->ram_block && mr->ram_block->guest_memfd >= 0; +} + uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) { uint8_t mask = mr->dirty_log_mask; diff --git a/system/physmem.c b/system/physmem.c index a4fe3d2bf8..f5dfa20e57 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1808,6 +1808,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) const bool shared = qemu_ram_is_shared(new_block); RAMBlock *block; RAMBlock *last_block = NULL; + bool free_on_error = false; ram_addr_t old_ram_size, new_ram_size; Error *err = NULL; @@ -1837,6 +1838,19 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) return; } memory_try_enable_merging(new_block->host, new_block->max_length); + free_on_error = true; + } + } + + if (new_block->flags & RAM_GUEST_MEMFD) { + assert(kvm_enabled()); + assert(new_block->guest_memfd < 0); + + new_block->guest_memfd = kvm_create_guest_memfd(new_block->max_length, + 0, errp); + if (new_block->guest_memfd < 0) { + qemu_mutex_unlock_ramlist(); + goto out_free; } } @@ -1888,6 +1902,13 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) ram_block_notify_add(new_block->host, new_block->used_length, new_block->max_length); } + return; + +out_free: + if (free_on_error) { + qemu_anon_ram_free(new_block->host, new_block->max_length); + new_block->host = NULL; + } } #ifdef CONFIG_POSIX @@ -1902,7 +1923,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, /* Just support these ram flags by now. */ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | RAM_PROTECTED | RAM_NAMED_FILE | RAM_READONLY | - RAM_READONLY_FD)) == 0); + RAM_READONLY_FD | RAM_GUEST_MEMFD)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -1939,6 +1960,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, new_block->used_length = size; new_block->max_length = size; new_block->flags = ram_flags; + new_block->guest_memfd = -1; new_block->host = file_ram_alloc(new_block, size, fd, !file_size, offset, errp); if (!new_block->host) { @@ -2018,7 +2040,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, int align; assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC | - RAM_NORESERVE)) == 0); + RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); assert(!host ^ (ram_flags & RAM_PREALLOC)); align = qemu_real_host_page_size(); @@ -2033,6 +2055,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, new_block->max_length = max_size; assert(max_size >= size); new_block->fd = -1; + new_block->guest_memfd = -1; new_block->page_size = qemu_real_host_page_size(); new_block->host = host; new_block->flags = ram_flags; @@ -2055,7 +2078,7 @@ RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { - assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } @@ -2083,6 +2106,11 @@ static void reclaim_ramblock(RAMBlock *block) } else { qemu_anon_ram_free(block->host, block->max_length); } + + if (block->guest_memfd >= 0) { + close(block->guest_memfd); + } + g_free(block); } From ce5a983233b4ca94ced88c9581014346509b5c71 Mon Sep 17 00:00:00 2001 From: Chao Peng Date: Wed, 20 Mar 2024 03:39:05 -0500 Subject: [PATCH 0050/2884] kvm: Enable KVM_SET_USER_MEMORY_REGION2 for memslot Switch to KVM_SET_USER_MEMORY_REGION2 when supported by KVM. With KVM_SET_USER_MEMORY_REGION2, QEMU can set up memory region that backend'ed both by hva-based shared memory and guest memfd based private memory. Signed-off-by: Chao Peng Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Message-ID: <20240320083945.991426-10-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 46 +++++++++++++++++++++++++++++++++------- accel/kvm/trace-events | 2 +- include/sysemu/kvm_int.h | 2 ++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 1b7bbd838c..0386d4901f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -284,35 +284,58 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) { KVMState *s = kvm_state; - struct kvm_userspace_memory_region mem; + struct kvm_userspace_memory_region2 mem; int ret; mem.slot = slot->slot | (kml->as_id << 16); mem.guest_phys_addr = slot->start_addr; mem.userspace_addr = (unsigned long)slot->ram; mem.flags = slot->flags; + mem.guest_memfd = slot->guest_memfd; + mem.guest_memfd_offset = slot->guest_memfd_offset; if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { /* Set the slot size to 0 before setting the slot to the desired * value. This is needed based on KVM commit 75d61fbc. */ mem.memory_size = 0; - ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + + if (kvm_guest_memfd_supported) { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION2, &mem); + } else { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + } if (ret < 0) { goto err; } } mem.memory_size = slot->memory_size; - ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + if (kvm_guest_memfd_supported) { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION2, &mem); + } else { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + } slot->old_flags = mem.flags; err: trace_kvm_set_user_memory(mem.slot >> 16, (uint16_t)mem.slot, mem.flags, mem.guest_phys_addr, mem.memory_size, - mem.userspace_addr, ret); + mem.userspace_addr, mem.guest_memfd, + mem.guest_memfd_offset, ret); if (ret < 0) { - error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d," - " start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s", - __func__, mem.slot, slot->start_addr, - (uint64_t)mem.memory_size, strerror(errno)); + if (kvm_guest_memfd_supported) { + error_report("%s: KVM_SET_USER_MEMORY_REGION2 failed, slot=%d," + " start=0x%" PRIx64 ", size=0x%" PRIx64 "," + " flags=0x%" PRIx32 ", guest_memfd=%" PRId32 "," + " guest_memfd_offset=0x%" PRIx64 ": %s", + __func__, mem.slot, slot->start_addr, + (uint64_t)mem.memory_size, mem.flags, + mem.guest_memfd, (uint64_t)mem.guest_memfd_offset, + strerror(errno)); + } else { + error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d," + " start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s", + __func__, mem.slot, slot->start_addr, + (uint64_t)mem.memory_size, strerror(errno)); + } } return ret; } @@ -467,6 +490,10 @@ static int kvm_mem_flags(MemoryRegion *mr) if (readonly && kvm_readonly_mem_allowed) { flags |= KVM_MEM_READONLY; } + if (memory_region_has_guest_memfd(mr)) { + assert(kvm_guest_memfd_supported); + flags |= KVM_MEM_GUEST_MEMFD; + } return flags; } @@ -1394,6 +1421,9 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, mem->ram_start_offset = ram_start_offset; mem->ram = ram; mem->flags = kvm_mem_flags(mr); + mem->guest_memfd = mr->ram_block->guest_memfd; + mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host; + kvm_slot_init_dirty_bitmap(mem); err = kvm_set_user_memory_region(kml, mem, true); if (err) { diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index 9f599abc17..e8c52cb9e7 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -15,7 +15,7 @@ kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_release_virq(int virq) "virq %d" kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" -kvm_set_user_memory(uint16_t as, uint16_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "AddrSpace#%d Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" +kvm_set_user_memory(uint16_t as, uint16_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, uint32_t fd, uint64_t fd_offset, int ret) "AddrSpace#%d Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " guest_memfd=%d" " guest_memfd_offset=0x%" PRIx64 " ret=%d" kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32 kvm_resample_fd_notify(int gsi) "gsi %d" kvm_dirty_ring_full(int id) "vcpu %d" diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 227b61fec3..3f3d13f816 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -30,6 +30,8 @@ typedef struct KVMSlot int as_id; /* Cache of the offset in ram address space */ ram_addr_t ram_start_offset; + int guest_memfd; + hwaddr guest_memfd_offset; } KVMSlot; typedef struct KVMMemoryUpdate { From bd3bcf6962b664ca3bf9c60fdcc4534e8e3d0641 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 20 Mar 2024 03:39:11 -0500 Subject: [PATCH 0051/2884] kvm/memory: Make memory type private by default if it has guest memfd backend KVM side leaves the memory to shared by default, which may incur the overhead of paging conversion on the first visit of each page. Because the expectation is that page is likely to private for the VMs that require private memory (has guest memfd). Explicitly set the memory to private when memory region has valid guest memfd backend. Signed-off-by: Xiaoyao Li Signed-off-by: Michael Roth Message-ID: <20240320083945.991426-16-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 0386d4901f..f49b2b95b5 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1431,6 +1431,16 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, strerror(-err)); abort(); } + + if (memory_region_has_guest_memfd(mr)) { + err = kvm_set_memory_attributes_private(start_addr, slot_size); + if (err) { + error_report("%s: failed to set memory attribute private: %s", + __func__, strerror(-err)); + exit(1); + } + } + start_addr += slot_size; ram_start_offset += slot_size; ram += slot_size; From 37662d85b0b7dded0ebdf6747bef6c3bb7ed6a0c Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 20 Mar 2024 03:39:03 -0500 Subject: [PATCH 0052/2884] HostMem: Add mechanism to opt in kvm guest memfd via MachineState Add a new member "guest_memfd" to memory backends. When it's set to true, it enables RAM_GUEST_MEMFD in ram_flags, thus private kvm guest_memfd will be allocated during RAMBlock allocation. Memory backend's @guest_memfd is wired with @require_guest_memfd field of MachineState. It avoid looking up the machine in phymem.c. MachineState::require_guest_memfd is supposed to be set by any VMs that requires KVM guest memfd as private memory, e.g., TDX VM. Signed-off-by: Xiaoyao Li Reviewed-by: David Hildenbrand Message-ID: <20240320083945.991426-8-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- backends/hostmem-file.c | 1 + backends/hostmem-memfd.c | 1 + backends/hostmem-ram.c | 1 + backends/hostmem.c | 1 + hw/core/machine.c | 5 +++++ include/hw/boards.h | 2 ++ include/sysemu/hostmem.h | 1 + 7 files changed, 12 insertions(+) diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index ac3e433cbd..3c69db7946 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -85,6 +85,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) ram_flags |= fb->readonly ? RAM_READONLY_FD : 0; ram_flags |= fb->rom == ON_OFF_AUTO_ON ? RAM_READONLY : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; + ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; ram_flags |= fb->is_pmem ? RAM_PMEM : 0; ram_flags |= RAM_NAMED_FILE; return memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 3923ea9364..745ead0034 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -55,6 +55,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; + ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, backend->size, ram_flags, fd, 0, errp); } diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index d121249f0f..f7d81af783 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -30,6 +30,7 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; + ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; return memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), name, backend->size, ram_flags, errp); diff --git a/backends/hostmem.c b/backends/hostmem.c index 81a72ce40b..eb9682b4a8 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -277,6 +277,7 @@ static void host_memory_backend_init(Object *obj) /* TODO: convert access to globals to compat properties */ backend->merge = machine_mem_merge(machine); backend->dump = machine_dump_guest_core(machine); + backend->guest_memfd = machine_require_guest_memfd(machine); backend->reserve = true; backend->prealloc_threads = machine->smp.cpus; } diff --git a/hw/core/machine.c b/hw/core/machine.c index a92bec2314..582c2df37a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1201,6 +1201,11 @@ bool machine_mem_merge(MachineState *machine) return machine->mem_merge; } +bool machine_require_guest_memfd(MachineState *machine) +{ + return machine->require_guest_memfd; +} + static char *cpu_slot_to_string(const CPUArchId *cpu) { GString *s = g_string_new(NULL); diff --git a/include/hw/boards.h b/include/hw/boards.h index 50e0cf4278..69c1ba45cf 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -36,6 +36,7 @@ bool machine_usb(MachineState *machine); int machine_phandle_start(MachineState *machine); bool machine_dump_guest_core(MachineState *machine); bool machine_mem_merge(MachineState *machine); +bool machine_require_guest_memfd(MachineState *machine); HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine); void machine_set_cpu_numa_node(MachineState *machine, const CpuInstanceProperties *props, @@ -370,6 +371,7 @@ struct MachineState { char *dt_compatible; bool dump_guest_core; bool mem_merge; + bool require_guest_memfd; bool usb; bool usb_disabled; char *firmware; diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 0e411aaa29..04b884bf42 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -74,6 +74,7 @@ struct HostMemoryBackend { uint64_t size; bool merge, dump, use_canonical_path; bool prealloc, is_mapped, share, reserve; + bool guest_memfd; uint32_t prealloc_threads; ThreadContext *prealloc_context; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); From 852f0048f3ea9f14de18eb279a99fccb6d250e8f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Mar 2024 17:45:29 +0100 Subject: [PATCH 0053/2884] RAMBlock: make guest_memfd require uncoordinated discard Some subsystems like VFIO might disable ram block discard, but guest_memfd uses discard operations to implement conversions between private and shared memory. Because of this, sequences like the following can result in stale IOMMU mappings: 1. allocate shared page 2. convert page shared->private 3. discard shared page 4. convert page private->shared 5. allocate shared page 6. issue DMA operations against that shared page This is not a use-after-free, because after step 3 VFIO is still pinning the page. However, DMA operations in step 6 will hit the old mapping that was allocated in step 1. Address this by taking ram_block_discard_is_enabled() into account when deciding whether or not to discard pages. Since kvm_convert_memory()/guest_memfd doesn't implement a RamDiscardManager handler to convey and replay discard operations, this is a case of uncoordinated discard, which is blocked/released by ram_block_discard_require(). Interestingly, this function had no use so far. Alternative approaches would be to block discard of shared pages, but this would cause guests to consume twice the memory if they use VFIO; or to implement a RamDiscardManager and only block uncoordinated discard, i.e. use ram_block_coordinated_discard_require(). [Commit message mostly by Michael Roth ] Signed-off-by: Paolo Bonzini --- system/physmem.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/system/physmem.c b/system/physmem.c index f5dfa20e57..5ebcf5be11 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1846,6 +1846,13 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) assert(kvm_enabled()); assert(new_block->guest_memfd < 0); + if (ram_block_discard_require(true) < 0) { + error_setg_errno(errp, errno, + "cannot set up private guest memory: discard currently blocked"); + error_append_hint(errp, "Are you using assigned devices?\n"); + goto out_free; + } + new_block->guest_memfd = kvm_create_guest_memfd(new_block->max_length, 0, errp); if (new_block->guest_memfd < 0) { @@ -2109,6 +2116,7 @@ static void reclaim_ramblock(RAMBlock *block) if (block->guest_memfd >= 0) { close(block->guest_memfd); + ram_block_discard_require(false); } g_free(block); From b2e9426c04fdd32d93a3a37db6b0c2e67c88c335 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 20 Mar 2024 03:39:07 -0500 Subject: [PATCH 0054/2884] physmem: Introduce ram_block_discard_guest_memfd_range() When memory page is converted from private to shared, the original private memory is back'ed by guest_memfd. Introduce ram_block_discard_guest_memfd_range() for discarding memory in guest_memfd. Based on a patch by Isaku Yamahata . Signed-off-by: Xiaoyao Li Reviewed-by: David Hildenbrand Signed-off-by: Michael Roth Message-ID: <20240320083945.991426-12-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- include/exec/cpu-common.h | 2 ++ system/physmem.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 6346df17ce..6d5318895a 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -159,6 +159,8 @@ typedef int (RAMBlockIterFunc)(RAMBlock *rb, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, + size_t length); #endif diff --git a/system/physmem.c b/system/physmem.c index 5ebcf5be11..c3d04ca921 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3721,6 +3721,29 @@ err: return ret; } +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, + size_t length) +{ + int ret = -1; + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + ret = fallocate(rb->guest_memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + start, length); + + if (ret) { + ret = -errno; + error_report("%s: Failed to fallocate %s:%" PRIx64 " +%zx (%d)", + __func__, rb->idstr, start, length, ret); + } +#else + ret = -ENOSYS; + error_report("%s: fallocate not available %s:%" PRIx64 " +%zx (%d)", + __func__, rb->idstr, start, length, ret); +#endif + + return ret; +} + bool ramblock_is_pmem(RAMBlock *rb) { return rb->flags & RAM_PMEM; From c15e5684071d93174e446be318f49d8d59b15d6d Mon Sep 17 00:00:00 2001 From: Chao Peng Date: Wed, 20 Mar 2024 03:39:08 -0500 Subject: [PATCH 0055/2884] kvm: handle KVM_EXIT_MEMORY_FAULT Upon an KVM_EXIT_MEMORY_FAULT exit, userspace needs to do the memory conversion on the RAMBlock to turn the memory into desired attribute, switching between private and shared. Currently only KVM_MEMORY_EXIT_FLAG_PRIVATE in flags is valid when KVM_EXIT_MEMORY_FAULT happens. Note, KVM_EXIT_MEMORY_FAULT makes sense only when the RAMBlock has guest_memfd memory backend. Note, KVM_EXIT_MEMORY_FAULT returns with -EFAULT, so special handling is added. When page is converted from shared to private, the original shared memory can be discarded via ram_block_discard_range(). Note, shared memory can be discarded only when it's not back'ed by hugetlb because hugetlb is supposed to be pre-allocated and no need for discarding. Signed-off-by: Chao Peng Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Message-ID: <20240320083945.991426-13-michael.roth@amd.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 98 +++++++++++++++++++++++++++++++++++++----- accel/kvm/trace-events | 2 + include/sysemu/kvm.h | 2 + 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f49b2b95b5..9eef2c6400 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2900,6 +2900,69 @@ static void kvm_eat_signals(CPUState *cpu) } while (sigismember(&chkset, SIG_IPI)); } +int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) +{ + MemoryRegionSection section; + ram_addr_t offset; + MemoryRegion *mr; + RAMBlock *rb; + void *addr; + int ret = -1; + + trace_kvm_convert_memory(start, size, to_private ? "shared_to_private" : "private_to_shared"); + + if (!QEMU_PTR_IS_ALIGNED(start, qemu_real_host_page_size()) || + !QEMU_PTR_IS_ALIGNED(size, qemu_real_host_page_size())) { + return -1; + } + + if (!size) { + return -1; + } + + section = memory_region_find(get_system_memory(), start, size); + mr = section.mr; + if (!mr) { + return -1; + } + + if (!memory_region_has_guest_memfd(mr)) { + error_report("Converting non guest_memfd backed memory region " + "(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s", + start, size, to_private ? "private" : "shared"); + goto out_unref; + } + + if (to_private) { + ret = kvm_set_memory_attributes_private(start, size); + } else { + ret = kvm_set_memory_attributes_shared(start, size); + } + if (ret) { + goto out_unref; + } + + addr = memory_region_get_ram_ptr(mr) + section.offset_within_region; + rb = qemu_ram_block_from_host(addr, false, &offset); + + if (to_private) { + if (rb->page_size != qemu_real_host_page_size()) { + /* + * shared memory is backed by hugetlb, which is supposed to be + * pre-allocated and doesn't need to be discarded + */ + goto out_unref; + } + ret = ram_block_discard_range(rb, offset, size); + } else { + ret = ram_block_discard_guest_memfd_range(rb, offset, size); + } + +out_unref: + memory_region_unref(mr); + return ret; +} + int kvm_cpu_exec(CPUState *cpu) { struct kvm_run *run = cpu->kvm_run; @@ -2967,18 +3030,20 @@ int kvm_cpu_exec(CPUState *cpu) ret = EXCP_INTERRUPT; break; } - fprintf(stderr, "error: kvm run failed %s\n", - strerror(-run_ret)); + if (!(run_ret == -EFAULT && run->exit_reason == KVM_EXIT_MEMORY_FAULT)) { + fprintf(stderr, "error: kvm run failed %s\n", + strerror(-run_ret)); #ifdef TARGET_PPC - if (run_ret == -EBUSY) { - fprintf(stderr, - "This is probably because your SMT is enabled.\n" - "VCPU can only run on primary threads with all " - "secondary threads offline.\n"); - } + if (run_ret == -EBUSY) { + fprintf(stderr, + "This is probably because your SMT is enabled.\n" + "VCPU can only run on primary threads with all " + "secondary threads offline.\n"); + } #endif - ret = -1; - break; + ret = -1; + break; + } } trace_kvm_run_exit(cpu->cpu_index, run->exit_reason); @@ -3061,6 +3126,19 @@ int kvm_cpu_exec(CPUState *cpu) break; } break; + case KVM_EXIT_MEMORY_FAULT: + trace_kvm_memory_fault(run->memory_fault.gpa, + run->memory_fault.size, + run->memory_fault.flags); + if (run->memory_fault.flags & ~KVM_MEMORY_EXIT_FLAG_PRIVATE) { + error_report("KVM_EXIT_MEMORY_FAULT: Unknown flag 0x%" PRIx64, + (uint64_t)run->memory_fault.flags); + ret = -1; + break; + } + ret = kvm_convert_memory(run->memory_fault.gpa, run->memory_fault.size, + run->memory_fault.flags & KVM_MEMORY_EXIT_FLAG_PRIVATE); + break; default: ret = kvm_arch_handle_exit(cpu, run); break; diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index e8c52cb9e7..681ccb667d 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -31,3 +31,5 @@ kvm_cpu_exec(void) "" kvm_interrupt_exit_request(void) "" kvm_io_window_exit(void) "" kvm_run_exit_system_event(int cpu_index, uint32_t event_type) "cpu_index %d, system_even_type %"PRIu32 +kvm_convert_memory(uint64_t start, uint64_t size, const char *msg) "start 0x%" PRIx64 " size 0x%" PRIx64 " %s" +kvm_memory_fault(uint64_t start, uint64_t size, uint64_t flags) "start 0x%" PRIx64 " size 0x%" PRIx64 " flags 0x%" PRIx64 diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 217f3fe17b..47f9e8be1b 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -542,4 +542,6 @@ int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp); int kvm_set_memory_attributes_private(hwaddr start, uint64_t size); int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size); +int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private); + #endif From c5d9425ef4da9f43fc0903905ad415456d1ab843 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 29 Feb 2024 01:36:54 -0500 Subject: [PATCH 0056/2884] kvm/tdx: Don't complain when converting vMMIO region to shared Because vMMIO region needs to be shared region, guest TD may explicitly convert such region from private to shared. Don't complain such conversion. Signed-off-by: Isaku Yamahata Signed-off-by: Xiaoyao Li Message-ID: <20240229063726.610065-34-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 9eef2c6400..0911154bf8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2927,9 +2927,22 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) } if (!memory_region_has_guest_memfd(mr)) { - error_report("Converting non guest_memfd backed memory region " - "(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s", - start, size, to_private ? "private" : "shared"); + /* + * Because vMMIO region must be shared, guest TD may convert vMMIO + * region to shared explicitly. Don't complain such case. See + * memory_region_type() for checking if the region is MMIO region. + */ + if (!to_private && + !memory_region_is_ram(mr) && + !memory_region_is_ram_device(mr) && + !memory_region_is_rom(mr) && + !memory_region_is_romd(mr)) { + ret = 0; + } else { + error_report("Convert non guest_memfd backed memory region " + "(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s", + start, size, to_private ? "private" : "shared"); + } goto out_unref; } From 565f4768bb9cf840b2f8cca41483bb91aa3196a3 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 29 Feb 2024 01:36:55 -0500 Subject: [PATCH 0057/2884] kvm/tdx: Ignore memory conversion to shared of unassigned region TDX requires vMMIO region to be shared. For KVM, MMIO region is the region which kvm memslot isn't assigned to (except in-kernel emulation). qemu has the memory region for vMMIO at each device level. While OVMF issues MapGPA(to-shared) conservatively on 32bit PCI MMIO region, qemu doesn't find corresponding vMMIO region because it's before PCI device allocation and memory_region_find() finds the device region, not PCI bus region. It's safe to ignore MapGPA(to-shared) because when guest accesses those region they use GPA with shared bit set for vMMIO. Ignore memory conversion request of non-assigned region to shared and return success. Otherwise OVMF is confused and panics there. Signed-off-by: Isaku Yamahata Signed-off-by: Xiaoyao Li Message-ID: <20240229063726.610065-35-xiaoyao.li@intel.com> Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 0911154bf8..d7281b93f3 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2923,6 +2923,18 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) section = memory_region_find(get_system_memory(), start, size); mr = section.mr; if (!mr) { + /* + * Ignore converting non-assigned region to shared. + * + * TDX requires vMMIO region to be shared to inject #VE to guest. + * OVMF issues conservatively MapGPA(shared) on 32bit PCI MMIO region, + * and vIO-APIC 0xFEC00000 4K page. + * OVMF assigns 32bit PCI MMIO region to + * [top of low memory: typically 2GB=0xC000000, 0xFC00000) + */ + if (!to_private) { + return 0; + } return -1; } From 7502ffb2f3240247e9426fd73b8422e5be54501a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 27 Mar 2024 18:39:49 +0800 Subject: [PATCH 0058/2884] target/i386/host-cpu: Consolidate the use of warn_report_once() Use warn_report_once() to get rid of the static local variable "warned". Signed-off-by: Zhao Liu Message-ID: <20240327103951.3853425-2-zhao1.liu@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/host-cpu.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 92ecb7254b..280e427c01 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -55,18 +55,15 @@ static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu) { uint32_t host_phys_bits = host_cpu_phys_bits(); uint32_t phys_bits = cpu->phys_bits; - static bool warned; /* * Print a warning if the user set it to a value that's not the * host value. */ - if (phys_bits != host_phys_bits && phys_bits != 0 && - !warned) { - warn_report("Host physical bits (%u)" - " does not match phys-bits property (%u)", - host_phys_bits, phys_bits); - warned = true; + if (phys_bits != host_phys_bits && phys_bits != 0) { + warn_report_once("Host physical bits (%u)" + " does not match phys-bits property (%u)", + host_phys_bits, phys_bits); } if (cpu->host_phys_bits) { From 8e3991ebc865e659e32195286caf0ca14ffcf806 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 27 Mar 2024 18:39:50 +0800 Subject: [PATCH 0059/2884] target/i386/cpu: Consolidate the use of warn_report_once() The difference between error_printf() and error_report() is the latter may contain more information, such as the name of the program ("qemu-system-x86_64"). Thus its variant error_report_once() and warn_report()'s variant warn_report_once() can be used here to print the information only once without a static local variable "ht_warned". Signed-off-by: Zhao Liu Message-ID: <20240327103951.3853425-3-zhao1.liu@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index eda15b0d4c..2845298378 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7367,7 +7367,6 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) X86CPUClass *xcc = X86_CPU_GET_CLASS(dev); CPUX86State *env = &cpu->env; Error *local_err = NULL; - static bool ht_warned; unsigned requested_lbr_fmt; #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) @@ -7610,13 +7609,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) */ if (IS_AMD_CPU(env) && !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && - cs->nr_threads > 1 && !ht_warned) { - warn_report("This family of AMD CPU doesn't support " - "hyperthreading(%d)", - cs->nr_threads); - error_printf("Please configure -smp options properly" - " or try enabling topoext feature.\n"); - ht_warned = true; + cs->nr_threads > 1) { + warn_report_once("This family of AMD CPU doesn't support " + "hyperthreading(%d).", cs->nr_threads); + error_report_once("Please configure -smp options properly" + " or try enabling topoext feature."); } #ifndef CONFIG_USER_ONLY From aec202cb0eb800049c85428ba31566a9ef634cfc Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 27 Mar 2024 18:39:51 +0800 Subject: [PATCH 0060/2884] target/i386/cpu: Merge the warning and error messages for AMD HT check Currently, the difference between warn_report_once() and error_report_once() is the former has the "warning:" prefix, while the latter does not have a similar level prefix. At the meantime, considering that there is no error handling logic here, and the purpose of error_report_once() is only to prompt the user with an abnormal message, there is no need to use an error-level message here, and instead we can just use a warning. Therefore, downgrade the message in error_report_once() to warning, and merge it into the previous warn_report_once(). Signed-off-by: Zhao Liu Message-ID: <20240327103951.3853425-4-zhao1.liu@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2845298378..fd6af0d763 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7611,9 +7611,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && cs->nr_threads > 1) { warn_report_once("This family of AMD CPU doesn't support " - "hyperthreading(%d).", cs->nr_threads); - error_report_once("Please configure -smp options properly" - " or try enabling topoext feature."); + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext " + "feature.", cs->nr_threads); } #ifndef CONFIG_USER_ONLY From 94da7b6e9ad8da2b0cb2ef86a64d9fa1d6c8320c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 18 Apr 2024 18:07:16 +0800 Subject: [PATCH 0061/2884] accel/tcg/icount-common: Consolidate the use of warn_report_once() Use warn_report_once() to get rid of the static local variable "notified". Signed-off-by: Zhao Liu Message-ID: <20240418100716.1085491-1-zhao1.liu@linux.intel.com> Signed-off-by: Paolo Bonzini --- accel/tcg/icount-common.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/accel/tcg/icount-common.c b/accel/tcg/icount-common.c index a4a747d1dc..8d3d3a7e9d 100644 --- a/accel/tcg/icount-common.c +++ b/accel/tcg/icount-common.c @@ -336,10 +336,8 @@ void icount_start_warp_timer(void) deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, ~QEMU_TIMER_ATTR_EXTERNAL); if (deadline < 0) { - static bool notified; - if (!icount_sleep && !notified) { - warn_report("icount sleep disabled and no active timers"); - notified = true; + if (!icount_sleep) { + warn_report_once("icount sleep disabled and no active timers"); } return; } From 9c05071719ca3a479b677e58db8ab0c5ad3f9b83 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 18 Apr 2024 11:14:31 +0200 Subject: [PATCH 0062/2884] pythondeps.toml: warn about updates needed to docs/requirements.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/requirements.txt is expected by readthedocs and should be in sync with pythondeps.toml. Add a comment to both. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- docs/requirements.txt | 3 +++ pythondeps.toml | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 691e5218ec..02583f209a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,5 @@ +# Used by readthedocs.io +# Should be in sync with the "installed" key of pythondeps.toml + sphinx==5.3.0 sphinx_rtd_theme==1.1.1 diff --git a/pythondeps.toml b/pythondeps.toml index 0e88415999..9c16602d30 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -22,6 +22,7 @@ meson = { accepted = ">=0.63.0", installed = "1.2.3", canary = "meson" } [docs] +# Please keep the installed versions in sync with docs/requirements.txt sphinx = { accepted = ">=1.6", installed = "5.3.0", canary = "sphinx-build" } sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" } From 7653b44534d3267fa63ebc9d7221eaa7b48bf5ae Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 19 Apr 2024 20:51:47 +0100 Subject: [PATCH 0063/2884] target/i386/translate.c: always write 32-bits for SGDT and SIDT The various Intel CPU manuals claim that SGDT and SIDT can write either 24-bits or 32-bits depending upon the operand size, but this is incorrect. Not only do the Intel CPU manuals give contradictory information between processor revisions, but this information doesn't even match real-life behaviour. In fact, tests on real hardware show that the CPU always writes 32-bits for SGDT and SIDT, and this behaviour is required for at least OS/2 Warp and WFW 3.11 with Win32s to function correctly. Remove the masking applied due to the operand size for SGDT and SIDT so that the TCG behaviour matches the behaviour on real hardware. Signed-off-by: Mark Cave-Ayland Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2198 -- MCA: Whilst I don't have a copy of OS/2 Warp handy, I've confirmed that this patch fixes the issue in WFW 3.11 with Win32s. For more technical information I highly recommend the excellent write-up at https://www.os2museum.com/wp/sgdtsidt-fiction-and-reality/. Message-ID: <20240419195147.434894-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 76a42c679c..c05d9e5225 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -5846,9 +5846,10 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) gen_op_st_v(s, MO_16, s->T0, s->A0); gen_add_A0_im(s, 2); tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, gdt.base)); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } + /* + * NB: Despite a confusing description in Intel CPU documentation, + * all 32-bits are written regardless of operand size. + */ gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); break; @@ -5901,9 +5902,10 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) gen_op_st_v(s, MO_16, s->T0, s->A0); gen_add_A0_im(s, 2); tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, idt.base)); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } + /* + * NB: Despite a confusing description in Intel CPU documentation, + * all 32-bits are written regardless of operand size. + */ gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); break; From 8c47168cca012aa6f64dc50eebdb126ab81e360a Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:27 +0000 Subject: [PATCH 0064/2884] tests/qtest/migration: Add 'to' object into migrate_qmp() Add the 'to' object into migrate_qmp(), so we can use migrate_get_socket_address() inside migrate_qmp() to get the port value. This is not applied to other migrate_qmp* because they don't need the port. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-2-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 3 ++- tests/qtest/migration-helpers.h | 5 +++-- tests/qtest/migration-test.c | 28 ++++++++++++++-------------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index e451dbdbed..b6206a04fb 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -68,7 +68,8 @@ void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...) * Arguments are built from @fmt... (formatted like * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. */ -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *fmt, ...) { va_list ap; QDict *args; diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 3bf7ded1b9..e16a34c796 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -25,8 +25,9 @@ typedef struct QTestMigrationState { bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque); -G_GNUC_PRINTF(3, 4) -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); +G_GNUC_PRINTF(4, 5) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *fmt, ...); G_GNUC_PRINTF(3, 4) void migrate_incoming_qmp(QTestState *who, const char *uri, diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 1d2cee87ea..39c393b7d9 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1350,7 +1350,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, wait_for_suspend(from, &src_state); g_autofree char *uri = migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -1500,7 +1500,7 @@ static void postcopy_recover_fail(QTestState *from, QTestState *to) g_assert_cmpint(ret, ==, 1); migrate_recover(to, "fd:fd-mig"); - migrate_qmp(from, "fd:fd-mig", "{'resume': true}"); + migrate_qmp(from, to, "fd:fd-mig", "{'resume': true}"); /* * Make sure both QEMU instances will go into RECOVER stage, then test @@ -1588,7 +1588,7 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * Try to rebuild the migration channel using the resume flag and * the newly created channel */ - migrate_qmp(from, uri, "{'resume': true}"); + migrate_qmp(from, to, uri, "{'resume': true}"); /* Restore the postcopy bandwidth to unlimited */ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); @@ -1669,7 +1669,7 @@ static void test_baddest(void) if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } - migrate_qmp(from, "tcp:127.0.0.1:0", "{}"); + migrate_qmp(from, to, "tcp:127.0.0.1:0", "{}"); wait_for_migration_fail(from, false); test_migrate_end(from, to, false); } @@ -1708,7 +1708,7 @@ static void test_analyze_script(void) uri = g_strdup_printf("exec:cat > %s", file); migrate_ensure_converge(from); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); wait_for_migration_complete(from); pid = fork(); @@ -1777,7 +1777,7 @@ static void test_precopy_common(MigrateCommon *args) goto finish; } - migrate_qmp(from, connect_uri, "{}"); + migrate_qmp(from, to, connect_uri, "{}"); if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; @@ -1873,7 +1873,7 @@ static void test_file_common(MigrateCommon *args, bool stop_src) goto finish; } - migrate_qmp(from, connect_uri, "{}"); + migrate_qmp(from, to, connect_uri, "{}"); wait_for_migration_complete(from); /* @@ -2029,7 +2029,7 @@ static void test_ignore_shared(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -2568,7 +2568,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); if (should_fail) { qtest_set_expected_status(to, EXIT_FAILURE); @@ -2671,7 +2671,7 @@ static void test_migrate_auto_converge(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); /* Wait for throttling begins */ percentage = 0; @@ -3040,7 +3040,7 @@ static void test_multifd_tcp_cancel(void) uri = migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -3072,7 +3072,7 @@ static void test_multifd_tcp_cancel(void) migrate_ensure_non_converge(from); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to2, uri, "{}"); migrate_wait_for_dirty_mem(from, to2); @@ -3405,7 +3405,7 @@ static void test_migrate_dirty_limit(void) migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); /* Start migrate */ - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; @@ -3446,7 +3446,7 @@ static void test_migrate_dirty_limit(void) } /* Start migrate */ - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; From d1155fd485d54e55fd26804c04635404ce5da43b Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:28 +0000 Subject: [PATCH 0065/2884] tests/qtest/migration: Replace connect_uri and move migrate_get_socket_address inside migrate_qmp Move the calls to migrate_get_socket_address() into migrate_qmp(). Get rid of connect_uri and replace it with args->connect_uri only because 'to' object will help to generate connect_uri with the correct port number. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-3-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 54 ++++++++++++++++++++- tests/qtest/migration-test.c | 83 ++++----------------------------- 2 files changed, 63 insertions(+), 74 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index b6206a04fb..3e8c19c4de 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -13,6 +13,9 @@ #include "qemu/osdep.h" #include "qemu/ctype.h" #include "qapi/qmp/qjson.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/error.h" #include "migration-helpers.h" @@ -24,6 +27,51 @@ */ #define MIGRATION_STATUS_WAIT_TIMEOUT 120 +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("tcp:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static char * +migrate_get_socket_address(QTestState *who, const char *parameter) +{ + QDict *rsp; + char *result; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, parameter); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); + visit_free(iv); + + /* we are only using a single address */ + result = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + qobject_unref(rsp); + return result; +} + bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque) { @@ -73,13 +121,17 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, { va_list ap; QDict *args; + g_autofree char *connect_uri = NULL; va_start(ap, fmt); args = qdict_from_vjsonf_nofail(fmt, ap); va_end(ap); g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); + if (!uri) { + connect_uri = migrate_get_socket_address(to, "socket-address"); + } + qdict_put_str(args, "uri", uri ? uri : connect_uri); qtest_qmp_assert_success(who, "{ 'execute': 'migrate', 'arguments': %p}", args); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 39c393b7d9..0c76fe2615 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -13,16 +13,12 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" #include "qemu/sockets.h" #include "chardev/char.h" -#include "qapi/qapi-visit-sockets.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qobject-output-visitor.h" #include "crypto/tlscredspsk.h" #include "qapi/qmp/qlist.h" @@ -369,50 +365,6 @@ static void cleanup(const char *filename) unlink(path); } -static char *SocketAddress_to_str(SocketAddress *addr) -{ - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("tcp:%s:%s", - addr->u.inet.host, - addr->u.inet.port); - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("unix:%s", - addr->u.q_unix.path); - case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("fd:%s", addr->u.fd.str); - case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("tcp:%s:%s", - addr->u.vsock.cid, - addr->u.vsock.port); - default: - return g_strdup("unknown address type"); - } -} - -static char *migrate_get_socket_address(QTestState *who, const char *parameter) -{ - QDict *rsp; - char *result; - SocketAddressList *addrs; - Visitor *iv = NULL; - QObject *object; - - rsp = migrate_query(who); - object = qdict_get(rsp, parameter); - - iv = qobject_input_visitor_new(object); - visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); - visit_free(iv); - - /* we are only using a single address */ - result = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); - qobject_unref(rsp); - return result; -} - static long long migrate_get_parameter_int(QTestState *who, const char *parameter) { @@ -1349,8 +1301,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, wait_for_serial("src_serial"); wait_for_suspend(from, &src_state); - g_autofree char *uri = migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, NULL, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -1733,7 +1684,6 @@ static void test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; - g_autofree char *connect_uri = NULL; if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -1766,18 +1716,12 @@ static void test_precopy_common(MigrateCommon *args) } } - if (!args->connect_uri) { - connect_uri = migrate_get_socket_address(to, "socket-address"); - } else { - connect_uri = g_strdup(args->connect_uri); - } - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, connect_uri, "{}"); + migrate_qmp_fail(from, args->connect_uri, "{}"); goto finish; } - migrate_qmp(from, to, connect_uri, "{}"); + migrate_qmp(from, to, args->connect_uri, "{}"); if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; @@ -1843,7 +1787,6 @@ static void test_file_common(MigrateCommon *args, bool stop_src) { QTestState *from, *to; void *data_hook = NULL; - g_autofree char *connect_uri = g_strdup(args->connect_uri); if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -1869,18 +1812,18 @@ static void test_file_common(MigrateCommon *args, bool stop_src) } if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, connect_uri, "{}"); + migrate_qmp_fail(from, args->connect_uri, "{}"); goto finish; } - migrate_qmp(from, to, connect_uri, "{}"); + migrate_qmp(from, to, args->connect_uri, "{}"); wait_for_migration_complete(from); /* * We need to wait for the source to finish before starting the * destination. */ - migrate_incoming_qmp(to, connect_uri, "{}"); + migrate_incoming_qmp(to, args->connect_uri, "{}"); wait_for_migration_complete(to); if (stop_src) { @@ -3017,7 +2960,6 @@ static void test_multifd_tcp_cancel(void) .hide_stderr = true, }; QTestState *from, *to, *to2; - g_autofree char *uri = NULL; if (test_migrate_start(&from, &to, "defer", &args)) { return; @@ -3038,9 +2980,7 @@ static void test_multifd_tcp_cancel(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - uri = migrate_get_socket_address(to, "socket-address"); - - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, NULL, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -3065,14 +3005,11 @@ static void test_multifd_tcp_cancel(void) /* Start incoming migration from the 1st socket */ migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); - g_free(uri); - uri = migrate_get_socket_address(to2, "socket-address"); - wait_for_migration_status(from, "cancelled", NULL); migrate_ensure_non_converge(from); - migrate_qmp(from, to2, uri, "{}"); + migrate_qmp(from, to2, NULL, "{}"); migrate_wait_for_dirty_mem(from, to2); @@ -3405,7 +3342,7 @@ static void test_migrate_dirty_limit(void) migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); /* Start migrate */ - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, args.connect_uri, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; @@ -3446,7 +3383,7 @@ static void test_migrate_dirty_limit(void) } /* Start migrate */ - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, args.connect_uri, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; From 4f2f5b694d9dec2dde87a9155b0cb674dc3e6644 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:29 +0000 Subject: [PATCH 0066/2884] tests/qtest/migration: Replace migrate_get_connect_uri inplace of migrate_get_socket_address Refactor migrate_get_socket_address to internally utilize 'socket-address' parameter, reducing redundancy in the function definition. migrate_get_socket_address implicitly converts SocketAddress into str. Move migrate_get_socket_address inside migrate_get_connect_uri which should return the uri string instead. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-4-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 3e8c19c4de..8806dc841e 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -48,28 +48,37 @@ static char *SocketAddress_to_str(SocketAddress *addr) } } -static char * -migrate_get_socket_address(QTestState *who, const char *parameter) +static SocketAddress *migrate_get_socket_address(QTestState *who) { QDict *rsp; - char *result; SocketAddressList *addrs; + SocketAddress *addr; Visitor *iv = NULL; QObject *object; rsp = migrate_query(who); - object = qdict_get(rsp, parameter); + object = qdict_get(rsp, "socket-address"); iv = qobject_input_visitor_new(object); visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); + addr = addrs->value; visit_free(iv); - /* we are only using a single address */ - result = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); qobject_unref(rsp); - return result; + return addr; +} + +static char * +migrate_get_connect_uri(QTestState *who) +{ + SocketAddress *addrs; + char *connect_uri; + + addrs = migrate_get_socket_address(who); + connect_uri = SocketAddress_to_str(addrs); + + qapi_free_SocketAddress(addrs); + return connect_uri; } bool migrate_watch_for_events(QTestState *who, const char *name, @@ -129,7 +138,7 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, g_assert(!qdict_haskey(args, "uri")); if (!uri) { - connect_uri = migrate_get_socket_address(to, "socket-address"); + connect_uri = migrate_get_connect_uri(to); } qdict_put_str(args, "uri", uri ? uri : connect_uri); From 387dc407db6137cec479f6c6efb3851464ea9026 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:30 +0000 Subject: [PATCH 0067/2884] tests/qtest/migration: Add channels parameter in migrate_qmp_fail Alter migrate_qmp_fail() to allow both uri and channels independently. For channels, convert string to a Dict. No dealing with migrate_get_socket_address() here because we will fail before starting the migration anyway. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-5-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 13 +++++++++++-- tests/qtest/migration-helpers.h | 5 +++-- tests/qtest/migration-test.c | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 8806dc841e..f215f44467 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -100,7 +100,8 @@ bool migrate_watch_for_events(QTestState *who, const char *name, return false; } -void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...) +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...) { va_list ap; QDict *args, *err; @@ -110,7 +111,15 @@ void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...) va_end(ap); g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + qdict_put_obj(args, "channels", channels_obj); + } err = qtest_qmp_assert_failure_ref( who, "{ 'execute': 'migrate', 'arguments': %p}", args); diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index e16a34c796..4e664148a5 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -33,8 +33,9 @@ G_GNUC_PRINTF(3, 4) void migrate_incoming_qmp(QTestState *who, const char *uri, const char *fmt, ...); -G_GNUC_PRINTF(3, 4) -void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...); +G_GNUC_PRINTF(4, 5) +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...); void migrate_set_capability(QTestState *who, const char *capability, bool value); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0c76fe2615..763ff27f33 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1717,7 +1717,7 @@ static void test_precopy_common(MigrateCommon *args) } if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, "{}"); + migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); goto finish; } @@ -1812,7 +1812,7 @@ static void test_file_common(MigrateCommon *args, bool stop_src) } if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, "{}"); + migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); goto finish; } From 2a49e3c618cd9edd0ef44af5cd19f7159bc52efc Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:31 +0000 Subject: [PATCH 0068/2884] tests/qtest/migration: Add migrate_set_ports into migrate_qmp to update migration port value migrate_get_connect_qdict gets qdict with the dst QEMU parameters. migrate_set_ports() from list of channels reads each QDict for port, and fills the port with correct value in case it was 0 in the test. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-6-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index f215f44467..a330ef9c7f 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -16,6 +16,8 @@ #include "qapi/qapi-visit-sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" +#include "qapi/qmp/qlist.h" +#include "qemu/cutils.h" #include "migration-helpers.h" @@ -48,6 +50,37 @@ static char *SocketAddress_to_str(SocketAddress *addr) } } +static QDict *SocketAddress_to_qdict(SocketAddress *addr) +{ + QDict *dict = qdict_new(); + + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + qdict_put_str(dict, "type", "inet"); + qdict_put_str(dict, "host", addr->u.inet.host); + qdict_put_str(dict, "port", addr->u.inet.port); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + qdict_put_str(dict, "type", "unix"); + qdict_put_str(dict, "path", addr->u.q_unix.path); + break; + case SOCKET_ADDRESS_TYPE_FD: + qdict_put_str(dict, "type", "fd"); + qdict_put_str(dict, "str", addr->u.fd.str); + break; + case SOCKET_ADDRESS_TYPE_VSOCK: + qdict_put_str(dict, "type", "vsock"); + qdict_put_str(dict, "cid", addr->u.vsock.cid); + qdict_put_str(dict, "port", addr->u.vsock.port); + break; + default: + g_assert_not_reached(); + break; + } + + return dict; +} + static SocketAddress *migrate_get_socket_address(QTestState *who) { QDict *rsp; @@ -81,6 +114,46 @@ migrate_get_connect_uri(QTestState *who) return connect_uri; } +static QDict * +migrate_get_connect_qdict(QTestState *who) +{ + SocketAddress *addrs; + QDict *connect_qdict; + + addrs = migrate_get_socket_address(who); + connect_qdict = SocketAddress_to_qdict(addrs); + + qapi_free_SocketAddress(addrs); + return connect_qdict; +} + +static void migrate_set_ports(QTestState *to, QList *channel_list) +{ + QDict *addr; + QListEntry *entry; + const char *addr_port = NULL; + + if (channel_list == NULL) { + return; + } + + addr = migrate_get_connect_qdict(to); + + QLIST_FOREACH_ENTRY(channel_list, entry) { + QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); + QDict *addrdict = qdict_get_qdict(channel, "addr"); + + if (qdict_haskey(addrdict, "port") && + qdict_haskey(addr, "port") && + (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { + addr_port = qdict_get_str(addr, "port"); + qdict_put_str(addrdict, "port", g_strdup(addr_port)); + } + } + + qobject_unref(addr); +} + bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque) { @@ -139,6 +212,7 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, { va_list ap; QDict *args; + QList *channel_list = NULL; g_autofree char *connect_uri = NULL; va_start(ap, fmt); @@ -149,6 +223,7 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, if (!uri) { connect_uri = migrate_get_connect_uri(to); } + migrate_set_ports(to, channel_list); qdict_put_str(args, "uri", uri ? uri : connect_uri); qtest_qmp_assert_success(who, From d5ee387de9169a0b1b7f20a930d58b7a3b676f45 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:32 +0000 Subject: [PATCH 0069/2884] tests/qtest/migration: Add channels parameter in migrate_qmp Alter migrate_qmp() to allow use of channels parameter, but only fill the uri with correct port number if there are no channels. Here we don't want to allow the wrong cases of having both or none (ex: migrate_qmp_fail). Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-7-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 22 +++++++++++++--------- tests/qtest/migration-helpers.h | 4 ++-- tests/qtest/migration-test.c | 28 ++++++++++++++-------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index a330ef9c7f..3b72cad6c1 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -133,10 +133,6 @@ static void migrate_set_ports(QTestState *to, QList *channel_list) QListEntry *entry; const char *addr_port = NULL; - if (channel_list == NULL) { - return; - } - addr = migrate_get_connect_qdict(to); QLIST_FOREACH_ENTRY(channel_list, entry) { @@ -208,11 +204,10 @@ void migrate_qmp_fail(QTestState *who, const char *uri, * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. */ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *fmt, ...) + const char *channels, const char *fmt, ...) { va_list ap; QDict *args; - QList *channel_list = NULL; g_autofree char *connect_uri = NULL; va_start(ap, fmt); @@ -220,11 +215,20 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, va_end(ap); g_assert(!qdict_haskey(args, "uri")); - if (!uri) { + if (uri) { + qdict_put_str(args, "uri", uri); + } else if (!channels) { connect_uri = migrate_get_connect_uri(to); + qdict_put_str(args, "uri", connect_uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + QList *channel_list = qobject_to(QList, channels_obj); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels_obj); } - migrate_set_ports(to, channel_list); - qdict_put_str(args, "uri", uri ? uri : connect_uri); qtest_qmp_assert_success(who, "{ 'execute': 'migrate', 'arguments': %p}", args); diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 4e664148a5..1339835698 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -25,9 +25,9 @@ typedef struct QTestMigrationState { bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque); -G_GNUC_PRINTF(4, 5) +G_GNUC_PRINTF(5, 6) void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *fmt, ...); + const char *channels, const char *fmt, ...); G_GNUC_PRINTF(3, 4) void migrate_incoming_qmp(QTestState *who, const char *uri, diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 763ff27f33..af5e7dc6d6 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1301,7 +1301,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, wait_for_serial("src_serial"); wait_for_suspend(from, &src_state); - migrate_qmp(from, to, NULL, "{}"); + migrate_qmp(from, to, NULL, NULL, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -1451,7 +1451,7 @@ static void postcopy_recover_fail(QTestState *from, QTestState *to) g_assert_cmpint(ret, ==, 1); migrate_recover(to, "fd:fd-mig"); - migrate_qmp(from, to, "fd:fd-mig", "{'resume': true}"); + migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); /* * Make sure both QEMU instances will go into RECOVER stage, then test @@ -1539,7 +1539,7 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * Try to rebuild the migration channel using the resume flag and * the newly created channel */ - migrate_qmp(from, to, uri, "{'resume': true}"); + migrate_qmp(from, to, uri, NULL, "{'resume': true}"); /* Restore the postcopy bandwidth to unlimited */ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); @@ -1620,7 +1620,7 @@ static void test_baddest(void) if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } - migrate_qmp(from, to, "tcp:127.0.0.1:0", "{}"); + migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); wait_for_migration_fail(from, false); test_migrate_end(from, to, false); } @@ -1659,7 +1659,7 @@ static void test_analyze_script(void) uri = g_strdup_printf("exec:cat > %s", file); migrate_ensure_converge(from); - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); wait_for_migration_complete(from); pid = fork(); @@ -1721,7 +1721,7 @@ static void test_precopy_common(MigrateCommon *args) goto finish; } - migrate_qmp(from, to, args->connect_uri, "{}"); + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; @@ -1816,7 +1816,7 @@ static void test_file_common(MigrateCommon *args, bool stop_src) goto finish; } - migrate_qmp(from, to, args->connect_uri, "{}"); + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); wait_for_migration_complete(from); /* @@ -1972,7 +1972,7 @@ static void test_ignore_shared(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -2511,7 +2511,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); if (should_fail) { qtest_set_expected_status(to, EXIT_FAILURE); @@ -2614,7 +2614,7 @@ static void test_migrate_auto_converge(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, to, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); /* Wait for throttling begins */ percentage = 0; @@ -2980,7 +2980,7 @@ static void test_multifd_tcp_cancel(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, to, NULL, "{}"); + migrate_qmp(from, to, NULL, NULL, "{}"); migrate_wait_for_dirty_mem(from, to); @@ -3009,7 +3009,7 @@ static void test_multifd_tcp_cancel(void) migrate_ensure_non_converge(from); - migrate_qmp(from, to2, NULL, "{}"); + migrate_qmp(from, to2, NULL, NULL, "{}"); migrate_wait_for_dirty_mem(from, to2); @@ -3342,7 +3342,7 @@ static void test_migrate_dirty_limit(void) migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, "{}"); + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; @@ -3383,7 +3383,7 @@ static void test_migrate_dirty_limit(void) } /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, "{}"); + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; From 9d36d62c00fce08103cc6a801365e5df45acbaa6 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:33 +0000 Subject: [PATCH 0070/2884] tests/qtest/migration: Add multifd_tcp_plain test using list of channels instead of uri Add a positive test to check multifd live migration but this time using list of channels (restricted to 1) as the starting point instead of simple uri string. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-8-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-test.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index af5e7dc6d6..be67a92536 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -655,6 +655,13 @@ typedef struct { */ const char *connect_uri; + /* + * Optional: JSON-formatted list of src QEMU URIs. If a port is + * defined as '0' in any QDict key a value of '0' will be + * automatically converted to the correct destination port. + */ + const char *connect_channels; + /* Optional: callback to run at start to set migration parameters */ TestMigrateStartHook start_hook; /* Optional: callback to run at finish to cleanup */ @@ -1721,7 +1728,7 @@ static void test_precopy_common(MigrateCommon *args) goto finish; } - migrate_qmp(from, to, args->connect_uri, NULL, "{}"); + migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; @@ -2721,7 +2728,7 @@ test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, } #endif /* CONFIG_ZSTD */ -static void test_multifd_tcp_none(void) +static void test_multifd_tcp_uri_none(void) { MigrateCommon args = { .listen_uri = "defer", @@ -2766,6 +2773,21 @@ static void test_multifd_tcp_no_zero_page(void) test_precopy_common(&args); } +static void test_multifd_tcp_channels_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_start, + .live = true, + .connect_channels = "[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]", + }; + test_precopy_common(&args); +} + static void test_multifd_tcp_zlib(void) { MigrateCommon args = { @@ -3669,8 +3691,10 @@ int main(int argc, char **argv) test_migrate_dirty_limit); } } - migration_test_add("/migration/multifd/tcp/plain/none", - test_multifd_tcp_none); + migration_test_add("/migration/multifd/tcp/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/channels/plain/none", + test_multifd_tcp_channels_none); migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", test_multifd_tcp_zero_page_legacy); migration_test_add("/migration/multifd/tcp/plain/zero-page/none", From bc6307a5eea8cfee4a0239a61151da2d83e88e5b Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 12 Mar 2024 20:26:34 +0000 Subject: [PATCH 0071/2884] tests/qtest/migration: Add negative tests to validate migration QAPIs Migration QAPI arguments - uri and channels are mutually exhaustive. Add negative validation tests, one with both arguments present and one with none present. Signed-off-by: Het Gala Suggested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240312202634.63349-9-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-test.c | 55 +++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index be67a92536..5d6d8cd634 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1724,7 +1724,7 @@ static void test_precopy_common(MigrateCommon *args) } if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); goto finish; } @@ -2571,6 +2571,55 @@ static void test_validate_uuid_dst_not_set(void) do_test_validate_uuid(&args, false); } +static void do_test_validate_uri_channel(MigrateCommon *args) +{ + QTestState *from, *to; + + if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + /* + * 'uri' and 'channels' validation is checked even before the migration + * starts. + */ + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + test_migrate_end(from, to, false); +} + +static void test_validate_uri_channels_both_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .connect_uri = "tcp:127.0.0.1:0", + .connect_channels = "[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]", + }; + + do_test_validate_uri_channel(&args); +} + +static void test_validate_uri_channels_none_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + }; + + do_test_validate_uri_channel(&args); +} + /* * The way auto_converge works, we need to do too many passes to * run this test. Auto_converge logic is only run once every @@ -3679,6 +3728,10 @@ int main(int argc, char **argv) test_validate_uuid_src_not_set); migration_test_add("/migration/validate_uuid_dst_not_set", test_validate_uuid_dst_not_set); + migration_test_add("/migration/validate_uri/channels/both_set", + test_validate_uri_channels_both_set); + migration_test_add("/migration/validate_uri/channels/none_set", + test_validate_uri_channels_none_set); /* * See explanation why this test is slow on function definition */ From fe3ba17b330afd2f6004bcd36e5181f91d319106 Mon Sep 17 00:00:00 2001 From: Het Gala Date: Tue, 19 Mar 2024 20:48:40 +0000 Subject: [PATCH 0072/2884] tests/qtest/migration: Fix typo for vsock in SocketAddress_to_str Signed-off-by: Het Gala Link: https://lore.kernel.org/r/20240319204840.211632-2-het.gala@nutanix.com Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 3b72cad6c1..ce6d6615b5 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -42,7 +42,7 @@ static char *SocketAddress_to_str(SocketAddress *addr) case SOCKET_ADDRESS_TYPE_FD: return g_strdup_printf("fd:%s", addr->u.fd.str); case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("tcp:%s:%s", + return g_strdup_printf("vsock:%s:%s", addr->u.vsock.cid, addr->u.vsock.port); default: From e86f243487ed8cf71eddfe9b28eb8109d09556af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:48:57 +0100 Subject: [PATCH 0073/2884] s390/stattrib: Add Error** argument to set_migrationmode() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will prepare ground for future changes adding an Error** argument to the save_setup() handler. We need to make sure that on failure, set_migrationmode() always sets a new error. See the Rules section in qapi/error.h. Cc: Halil Pasic Cc: Christian Borntraeger Cc: Thomas Huth Reviewed-by: Fabiano Rosas Reviewed-by: Thomas Huth Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-2-clg@redhat.com Signed-off-by: Peter Xu --- hw/s390x/s390-stattrib-kvm.c | 12 ++++++++++-- hw/s390x/s390-stattrib.c | 15 ++++++++++----- include/hw/s390x/storage-attributes.h | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index 24cd01382e..eeaa811098 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -17,6 +17,7 @@ #include "sysemu/kvm.h" #include "exec/ram_addr.h" #include "kvm/kvm_s390x.h" +#include "qapi/error.h" Object *kvm_s390_stattrib_create(void) { @@ -137,14 +138,21 @@ static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) } } -static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val) +static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val, + Error **errp) { struct kvm_device_attr attr = { .group = KVM_S390_VM_MIGRATION, .attr = val, .addr = 0, }; - return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); + int r; + + r = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); + if (r) { + error_setg_errno(errp, -r, "setting KVM_S390_VM_MIGRATION failed"); + } + return r; } static long long kvm_s390_stattrib_get_dirtycount(S390StAttribState *sa) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index c483b62a9b..b743e8a2fe 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -60,11 +60,13 @@ void hmp_migrationmode(Monitor *mon, const QDict *qdict) S390StAttribState *sas = s390_get_stattrib_device(); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); uint64_t what = qdict_get_int(qdict, "mode"); + Error *local_err = NULL; int r; - r = sac->set_migrationmode(sas, what); + r = sac->set_migrationmode(sas, what, &local_err); if (r < 0) { - monitor_printf(mon, "Error: %s", strerror(-r)); + monitor_printf(mon, "Error: %s", error_get_pretty(local_err)); + error_free(local_err); } } @@ -170,13 +172,15 @@ static int cmma_save_setup(QEMUFile *f, void *opaque) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + Error *local_err = NULL; int res; /* * Signal that we want to start a migration, thus needing PGSTE dirty * tracking. */ - res = sac->set_migrationmode(sas, 1); + res = sac->set_migrationmode(sas, true, &local_err); if (res) { + error_report_err(local_err); return res; } qemu_put_be64(f, STATTR_FLAG_EOS); @@ -260,7 +264,7 @@ static void cmma_save_cleanup(void *opaque) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); - sac->set_migrationmode(sas, 0); + sac->set_migrationmode(sas, false, NULL); } static bool cmma_active(void *opaque) @@ -293,7 +297,8 @@ static long long qemu_s390_get_dirtycount_stub(S390StAttribState *sa) { return 0; } -static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value) +static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value, + Error **errp) { return 0; } diff --git a/include/hw/s390x/storage-attributes.h b/include/hw/s390x/storage-attributes.h index 5239eb538c..8921a04d51 100644 --- a/include/hw/s390x/storage-attributes.h +++ b/include/hw/s390x/storage-attributes.h @@ -39,7 +39,7 @@ struct S390StAttribClass { int (*set_stattr)(S390StAttribState *sa, uint64_t start_gfn, uint32_t count, uint8_t *values); void (*synchronize)(S390StAttribState *sa); - int (*set_migrationmode)(S390StAttribState *sa, bool value); + int (*set_migrationmode)(S390StAttribState *sa, bool value, Error **errp); int (*get_active)(S390StAttribState *sa); long long (*get_dirtycount)(S390StAttribState *sa); }; From 31cf7c1413db6c8fba6d9e73d77e4ab6faee0408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:48:58 +0100 Subject: [PATCH 0074/2884] vfio: Always report an error in vfio_save_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will prepare ground for future changes adding an Error** argument to the save_setup() handler. We need to make sure that on failure, vfio_save_setup() always sets a new error. Reviewed-by: Fabiano Rosas Reviewed-by: Eric Auger Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-3-clg@redhat.com Signed-off-by: Peter Xu --- hw/vfio/migration.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 1149c6b374..bf5a29ddc1 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -381,6 +381,7 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; uint64_t stop_copy_size = VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE; + int ret; qemu_put_be64(f, VFIO_MIG_FLAG_DEV_SETUP_STATE); @@ -395,13 +396,13 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) } if (vfio_precopy_supported(vbasedev)) { - int ret; - switch (migration->device_state) { case VFIO_DEVICE_STATE_RUNNING: ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_PRE_COPY, VFIO_DEVICE_STATE_RUNNING); if (ret) { + error_report("%s: Failed to set new PRE_COPY state", + vbasedev->name); return ret; } @@ -412,6 +413,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) /* vfio_save_complete_precopy() will go to STOP_COPY */ break; default: + error_report("%s: Invalid device state %d", vbasedev->name, + migration->device_state); return -EINVAL; } } @@ -420,7 +423,13 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); - return qemu_file_get_error(f); + ret = qemu_file_get_error(f); + if (ret < 0) { + error_report("%s: save setup failed : %s", vbasedev->name, + strerror(-ret)); + } + + return ret; } static void vfio_save_cleanup(void *opaque) From 150da48cb28ff68ca51b5ab26c6d930bad60a460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:48:59 +0100 Subject: [PATCH 0075/2884] migration: Always report an error in block_save_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will prepare ground for future changes adding an Error** argument to the save_setup() handler. We need to make sure that on failure, block_save_setup() always sets a new error. Cc: Stefan Hajnoczi Reviewed-by: Fabiano Rosas Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-4-clg@redhat.com Signed-off-by: Peter Xu --- migration/block.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/migration/block.c b/migration/block.c index 2b9054889a..f8a11beb37 100644 --- a/migration/block.c +++ b/migration/block.c @@ -367,7 +367,7 @@ static void unset_dirty_tracking(void) } } -static int init_blk_migration(QEMUFile *f) +static int init_blk_migration(QEMUFile *f, Error **errp) { BlockDriverState *bs; BlkMigDevState *bmds; @@ -378,7 +378,6 @@ static int init_blk_migration(QEMUFile *f) BlkMigDevState *bmds; BlockDriverState *bs; } *bmds_bs; - Error *local_err = NULL; int ret; GRAPH_RDLOCK_GUARD_MAINLOOP(); @@ -406,6 +405,8 @@ static int init_blk_migration(QEMUFile *f) continue; } if (sectors < 0) { + error_setg(errp, "Error getting length of block device %s", + bdrv_get_device_name(bs)); ret = sectors; bdrv_next_cleanup(&it); goto out; @@ -442,9 +443,8 @@ static int init_blk_migration(QEMUFile *f) bs = bmds_bs[i].bs; if (bmds) { - ret = blk_insert_bs(bmds->blk, bs, &local_err); + ret = blk_insert_bs(bmds->blk, bs, errp); if (ret < 0) { - error_report_err(local_err); goto out; } @@ -714,6 +714,7 @@ static void block_migration_cleanup(void *opaque) static int block_save_setup(QEMUFile *f, void *opaque) { int ret; + Error *local_err = NULL; trace_migration_block_save("setup", block_mig_state.submitted, block_mig_state.transferred); @@ -721,18 +722,27 @@ static int block_save_setup(QEMUFile *f, void *opaque) warn_report("block migration is deprecated;" " use blockdev-mirror with NBD instead"); - ret = init_blk_migration(f); + ret = init_blk_migration(f, &local_err); if (ret < 0) { + error_report_err(local_err); return ret; } /* start track dirty blocks */ ret = set_dirty_tracking(); if (ret) { + error_setg_errno(&local_err, -ret, + "Failed to start block dirty tracking"); + error_report_err(local_err); return ret; } ret = flush_blks(f); + if (ret) { + error_setg_errno(&local_err, -ret, "Flushing block failed"); + error_report_err(local_err); + return ret; + } blk_mig_reset_dirty_cursor(); qemu_put_be64(f, BLK_MIG_FLAG_EOS); From 76936bbc313190f8e4ea5e33f793fd00bf49b3f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:00 +0100 Subject: [PATCH 0076/2884] migration: Always report an error in ram_save_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will prepare ground for future changes adding an Error** argument to the save_setup() handler. We need to make sure that on failure, ram_save_setup() sets a new error. Reviewed-by: Fabiano Rosas Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-5-clg@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 8deb84984f..44d7073730 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3074,12 +3074,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) int ret, max_hg_page_size; if (compress_threads_save_setup()) { + error_report("%s: failed to start compress threads", __func__); return -1; } /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { if (ram_init_all(rsp) != 0) { + error_report("%s: failed to setup RAM for migration", __func__); compress_threads_save_cleanup(); return -1; } @@ -3116,12 +3118,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ret = rdma_registration_start(f, RAM_CONTROL_SETUP); if (ret < 0) { + error_report("%s: failed to start RDMA registration", __func__); qemu_file_set_error(f, ret); return ret; } ret = rdma_registration_stop(f, RAM_CONTROL_SETUP); if (ret < 0) { + error_report("%s: failed to stop RDMA registration", __func__); qemu_file_set_error(f, ret); return ret; } @@ -3138,6 +3142,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ret = multifd_send_sync_main(); bql_lock(); if (ret < 0) { + error_report("%s: multifd synchronization failed", __func__); return ret; } @@ -3147,7 +3152,11 @@ static int ram_save_setup(QEMUFile *f, void *opaque) } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - return qemu_fflush(f); + ret = qemu_fflush(f); + if (ret < 0) { + error_report("%s failed : %s", __func__, strerror(-ret)); + } + return ret; } static void ram_save_file_bmap(QEMUFile *f) From 6138d43ab2b1d013ebe2cfe2917e6279396473b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:01 +0100 Subject: [PATCH 0077/2884] migration: Add Error** argument to vmstate_save() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will prepare ground for future changes adding an Error** argument to qemu_savevm_state_setup(). Reviewed-by: Prasad Pandit Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-6-clg@redhat.com Signed-off-by: Peter Xu --- migration/savevm.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index e7c1215671..6252fc37b7 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1009,11 +1009,10 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se) } } -static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) +static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc, + Error **errp) { int ret; - Error *local_err = NULL; - MigrationState *s = migrate_get_current(); if ((!se->ops || !se->ops->save_state) && !se->vmsd) { return 0; @@ -1035,10 +1034,9 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) if (!se->vmsd) { vmstate_save_old_style(f, se, vmdesc); } else { - ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc, &local_err); + ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc, + errp); if (ret) { - migrate_set_error(s, local_err); - error_report_err(local_err); return ret; } } @@ -1325,8 +1323,10 @@ void qemu_savevm_state_setup(QEMUFile *f) trace_savevm_state_setup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->early_setup) { - ret = vmstate_save(f, se, ms->vmdesc); + ret = vmstate_save(f, se, ms->vmdesc, &local_err); if (ret) { + migrate_set_error(ms, local_err); + error_report_err(local_err); qemu_file_set_error(f, ret); break; } @@ -1542,6 +1542,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, JSONWriter *vmdesc = ms->vmdesc; int vmdesc_len; SaveStateEntry *se; + Error *local_err = NULL; int ret; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1552,8 +1553,10 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); - ret = vmstate_save(f, se, vmdesc); + ret = vmstate_save(f, se, vmdesc, &local_err); if (ret) { + migrate_set_error(ms, local_err); + error_report_err(local_err); qemu_file_set_error(f, ret); return ret; } @@ -1568,7 +1571,6 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, * bdrv_activate_all() on the other end won't fail. */ ret = bdrv_inactivate_all(); if (ret) { - Error *local_err = NULL; error_setg(&local_err, "%s: bdrv_inactivate_all() failed (%d)", __func__, ret); migrate_set_error(ms, local_err); @@ -1764,6 +1766,8 @@ void qemu_savevm_live_state(QEMUFile *f) int qemu_save_device_state(QEMUFile *f) { + MigrationState *ms = migrate_get_current(); + Error *local_err = NULL; SaveStateEntry *se; if (!migration_in_colo_state()) { @@ -1778,8 +1782,10 @@ int qemu_save_device_state(QEMUFile *f) if (se->is_ram) { continue; } - ret = vmstate_save(f, se, NULL); + ret = vmstate_save(f, se, NULL, &local_err); if (ret) { + migrate_set_error(ms, local_err); + error_report_err(local_err); return ret; } } From 057a20099b62c5ac3c925f3fe12bdedac96e647b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:02 +0100 Subject: [PATCH 0078/2884] migration: Add Error** argument to qemu_savevm_state_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares ground for the changes coming next which add an Error** argument to the .save_setup() handler. Callers of qemu_savevm_state_setup() now handle the error and fail earlier setting the migration state from MIGRATION_STATUS_SETUP to MIGRATION_STATUS_FAILED. In qemu_savevm_state(), move the cleanup to preserve the error reported by .save_setup() handlers. Since the previous behavior was to ignore errors at this step of migration, this change should be examined closely to check that cleanups are still correctly done. Signed-off-by: Cédric Le Goater Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20240320064911.545001-7-clg@redhat.com Signed-off-by: Peter Xu --- migration/migration.c | 33 +++++++++++++++++++++++++++++++-- migration/savevm.c | 26 +++++++++++++++----------- migration/savevm.h | 2 +- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 86bf76e925..696762bc64 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3431,6 +3431,8 @@ static void *migration_thread(void *opaque) int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); MigThrError thr_error; bool urgent = false; + Error *local_err = NULL; + int ret; thread = migration_threads_add("live_migration", qemu_get_thread_id()); @@ -3474,12 +3476,24 @@ static void *migration_thread(void *opaque) } bql_lock(); - qemu_savevm_state_setup(s->to_dst_file); + ret = qemu_savevm_state_setup(s->to_dst_file, &local_err); bql_unlock(); qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + /* + * Handle SETUP failures after waiting for virtio-net-failover + * devices to unplug. This to preserve migration state transitions. + */ + if (ret) { + migrate_set_error(s, local_err); + error_free(local_err); + migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); + goto out; + } + s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; trace_migration_thread_setup_complete(); @@ -3553,6 +3567,8 @@ static void *bg_migration_thread(void *opaque) MigThrError thr_error; QEMUFile *fb; bool early_fail = true; + Error *local_err = NULL; + int ret; rcu_register_thread(); object_ref(OBJECT(s)); @@ -3586,12 +3602,24 @@ static void *bg_migration_thread(void *opaque) bql_lock(); qemu_savevm_state_header(s->to_dst_file); - qemu_savevm_state_setup(s->to_dst_file); + ret = qemu_savevm_state_setup(s->to_dst_file, &local_err); bql_unlock(); qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + /* + * Handle SETUP failures after waiting for virtio-net-failover + * devices to unplug. This to preserve migration state transitions. + */ + if (ret) { + migrate_set_error(s, local_err); + error_free(local_err); + migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); + goto fail_setup; + } + s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; trace_migration_thread_setup_complete(); @@ -3660,6 +3688,7 @@ fail: bql_unlock(); } +fail_setup: bg_migration_iteration_finish(s); qemu_fclose(fb); diff --git a/migration/savevm.c b/migration/savevm.c index 6252fc37b7..327e9b346e 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1310,11 +1310,11 @@ int qemu_savevm_state_prepare(Error **errp) return 0; } -void qemu_savevm_state_setup(QEMUFile *f) +int qemu_savevm_state_setup(QEMUFile *f, Error **errp) { + ERRP_GUARD(); MigrationState *ms = migrate_get_current(); SaveStateEntry *se; - Error *local_err = NULL; int ret = 0; json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size()); @@ -1323,10 +1323,9 @@ void qemu_savevm_state_setup(QEMUFile *f) trace_savevm_state_setup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->early_setup) { - ret = vmstate_save(f, se, ms->vmdesc, &local_err); + ret = vmstate_save(f, se, ms->vmdesc, errp); if (ret) { - migrate_set_error(ms, local_err); - error_report_err(local_err); + migrate_set_error(ms, *errp); qemu_file_set_error(f, ret); break; } @@ -1346,18 +1345,19 @@ void qemu_savevm_state_setup(QEMUFile *f) ret = se->ops->save_setup(f, se->opaque); save_section_footer(f, se); if (ret < 0) { + error_setg(errp, "failed to setup SaveStateEntry with id(name): " + "%d(%s): %d", se->section_id, se->idstr, ret); qemu_file_set_error(f, ret); break; } } if (ret) { - return; + return ret; } - if (precopy_notify(PRECOPY_NOTIFY_SETUP, &local_err)) { - error_report_err(local_err); - } + /* TODO: Should we check that errp is set in case of failure ? */ + return precopy_notify(PRECOPY_NOTIFY_SETUP, errp); } int qemu_savevm_state_resume_prepare(MigrationState *s) @@ -1725,7 +1725,10 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) ms->to_dst_file = f; qemu_savevm_state_header(f); - qemu_savevm_state_setup(f); + ret = qemu_savevm_state_setup(f, errp); + if (ret) { + goto cleanup; + } while (qemu_file_get_error(f) == 0) { if (qemu_savevm_state_iterate(f, false) > 0) { @@ -1738,10 +1741,11 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) qemu_savevm_state_complete_precopy(f, false, false); ret = qemu_file_get_error(f); } - qemu_savevm_state_cleanup(); if (ret != 0) { error_setg_errno(errp, -ret, "Error while writing VM state"); } +cleanup: + qemu_savevm_state_cleanup(); if (ret != 0) { status = MIGRATION_STATUS_FAILED; diff --git a/migration/savevm.h b/migration/savevm.h index 74669733dd..9ec96a995c 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -32,7 +32,7 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_non_migratable_list(strList **reasons); int qemu_savevm_state_prepare(Error **errp); -void qemu_savevm_state_setup(QEMUFile *f); +int qemu_savevm_state_setup(QEMUFile *f, Error **errp); bool qemu_savevm_state_guest_unplug_pending(void); int qemu_savevm_state_resume_prepare(MigrationState *s); void qemu_savevm_state_header(QEMUFile *f); From 01c3ac681bd6709d2bf6a7d9591c40a394e39536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:03 +0100 Subject: [PATCH 0079/2884] migration: Add Error** argument to .save_setup() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The purpose is to record a potential error in the migration stream if qemu_savevm_state_setup() fails. Most of the current .save_setup() handlers can be modified to use the Error argument instead of managing their own and calling locally error_report(). Cc: Nicholas Piggin Cc: Harsh Prateek Bora Cc: Halil Pasic Cc: Thomas Huth Cc: Eric Blake Cc: Vladimir Sementsov-Ogievskiy Cc: John Snow Cc: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Reviewed-by: Thomas Huth Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-8-clg@redhat.com Signed-off-by: Peter Xu --- hw/ppc/spapr.c | 2 +- hw/s390x/s390-stattrib.c | 6 ++---- hw/vfio/migration.c | 17 ++++++++--------- include/migration/register.h | 3 ++- migration/block-dirty-bitmap.c | 4 +++- migration/block.c | 13 ++++--------- migration/ram.c | 15 ++++++++------- migration/savevm.c | 4 +--- 8 files changed, 29 insertions(+), 35 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index e9bc97fee0..823164e81c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2172,7 +2172,7 @@ static const VMStateDescription vmstate_spapr = { } }; -static int htab_save_setup(QEMUFile *f, void *opaque) +static int htab_save_setup(QEMUFile *f, void *opaque, Error **errp) { SpaprMachineState *spapr = opaque; diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index b743e8a2fe..bc04187b2b 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -168,19 +168,17 @@ static int cmma_load(QEMUFile *f, void *opaque, int version_id) return ret; } -static int cmma_save_setup(QEMUFile *f, void *opaque) +static int cmma_save_setup(QEMUFile *f, void *opaque, Error **errp) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); - Error *local_err = NULL; int res; /* * Signal that we want to start a migration, thus needing PGSTE dirty * tracking. */ - res = sac->set_migrationmode(sas, true, &local_err); + res = sac->set_migrationmode(sas, true, errp); if (res) { - error_report_err(local_err); return res; } qemu_put_be64(f, STATTR_FLAG_EOS); diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index bf5a29ddc1..5763c0b683 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -376,7 +376,7 @@ static int vfio_save_prepare(void *opaque, Error **errp) return 0; } -static int vfio_save_setup(QEMUFile *f, void *opaque) +static int vfio_save_setup(QEMUFile *f, void *opaque, Error **errp) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; @@ -390,8 +390,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) stop_copy_size); migration->data_buffer = g_try_malloc0(migration->data_buffer_size); if (!migration->data_buffer) { - error_report("%s: Failed to allocate migration data buffer", - vbasedev->name); + error_setg(errp, "%s: Failed to allocate migration data buffer", + vbasedev->name); return -ENOMEM; } @@ -401,8 +401,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_PRE_COPY, VFIO_DEVICE_STATE_RUNNING); if (ret) { - error_report("%s: Failed to set new PRE_COPY state", - vbasedev->name); + error_setg(errp, "%s: Failed to set new PRE_COPY state", + vbasedev->name); return ret; } @@ -413,8 +413,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) /* vfio_save_complete_precopy() will go to STOP_COPY */ break; default: - error_report("%s: Invalid device state %d", vbasedev->name, - migration->device_state); + error_setg(errp, "%s: Invalid device state %d", vbasedev->name, + migration->device_state); return -EINVAL; } } @@ -425,8 +425,7 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) ret = qemu_file_get_error(f); if (ret < 0) { - error_report("%s: save setup failed : %s", vbasedev->name, - strerror(-ret)); + error_setg_errno(errp, -ret, "%s: save setup failed", vbasedev->name); } return ret; diff --git a/include/migration/register.h b/include/migration/register.h index d7b70a8be6..64fc7c1103 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -60,10 +60,11 @@ typedef struct SaveVMHandlers { * * @f: QEMUFile where to send the data * @opaque: data pointer passed to register_savevm_live() + * @errp: pointer to Error*, to store an error if it happens. * * Returns zero to indicate success and negative for error */ - int (*save_setup)(QEMUFile *f, void *opaque); + int (*save_setup)(QEMUFile *f, void *opaque, Error **errp); /** * @save_cleanup diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 2708abf3d7..542a8c297b 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -1213,12 +1213,14 @@ fail: return ret; } -static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) +static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque, Error **errp) { DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms = NULL; if (init_dirty_bitmap_migration(s) < 0) { + error_setg(errp, + "Failed to initialize dirty tracking bitmap for blocks"); return -1; } diff --git a/migration/block.c b/migration/block.c index f8a11beb37..bae6e94891 100644 --- a/migration/block.c +++ b/migration/block.c @@ -711,10 +711,9 @@ static void block_migration_cleanup(void *opaque) blk_mig_unlock(); } -static int block_save_setup(QEMUFile *f, void *opaque) +static int block_save_setup(QEMUFile *f, void *opaque, Error **errp) { int ret; - Error *local_err = NULL; trace_migration_block_save("setup", block_mig_state.submitted, block_mig_state.transferred); @@ -722,25 +721,21 @@ static int block_save_setup(QEMUFile *f, void *opaque) warn_report("block migration is deprecated;" " use blockdev-mirror with NBD instead"); - ret = init_blk_migration(f, &local_err); + ret = init_blk_migration(f, errp); if (ret < 0) { - error_report_err(local_err); return ret; } /* start track dirty blocks */ ret = set_dirty_tracking(); if (ret) { - error_setg_errno(&local_err, -ret, - "Failed to start block dirty tracking"); - error_report_err(local_err); + error_setg_errno(errp, -ret, "Failed to start block dirty tracking"); return ret; } ret = flush_blks(f); if (ret) { - error_setg_errno(&local_err, -ret, "Flushing block failed"); - error_report_err(local_err); + error_setg_errno(errp, -ret, "Flushing block failed"); return ret; } blk_mig_reset_dirty_cursor(); diff --git a/migration/ram.c b/migration/ram.c index 44d7073730..6ea5a06e00 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3066,22 +3066,23 @@ static bool mapped_ram_read_header(QEMUFile *file, MappedRamHeader *header, * * @f: QEMUFile where to send the data * @opaque: RAMState pointer + * @errp: pointer to Error*, to store an error if it happens. */ -static int ram_save_setup(QEMUFile *f, void *opaque) +static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) { RAMState **rsp = opaque; RAMBlock *block; int ret, max_hg_page_size; if (compress_threads_save_setup()) { - error_report("%s: failed to start compress threads", __func__); + error_setg(errp, "%s: failed to start compress threads", __func__); return -1; } /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { if (ram_init_all(rsp) != 0) { - error_report("%s: failed to setup RAM for migration", __func__); + error_setg(errp, "%s: failed to setup RAM for migration", __func__); compress_threads_save_cleanup(); return -1; } @@ -3118,14 +3119,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ret = rdma_registration_start(f, RAM_CONTROL_SETUP); if (ret < 0) { - error_report("%s: failed to start RDMA registration", __func__); + error_setg(errp, "%s: failed to start RDMA registration", __func__); qemu_file_set_error(f, ret); return ret; } ret = rdma_registration_stop(f, RAM_CONTROL_SETUP); if (ret < 0) { - error_report("%s: failed to stop RDMA registration", __func__); + error_setg(errp, "%s: failed to stop RDMA registration", __func__); qemu_file_set_error(f, ret); return ret; } @@ -3142,7 +3143,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ret = multifd_send_sync_main(); bql_lock(); if (ret < 0) { - error_report("%s: multifd synchronization failed", __func__); + error_setg(errp, "%s: multifd synchronization failed", __func__); return ret; } @@ -3154,7 +3155,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) qemu_put_be64(f, RAM_SAVE_FLAG_EOS); ret = qemu_fflush(f); if (ret < 0) { - error_report("%s failed : %s", __func__, strerror(-ret)); + error_setg_errno(errp, -ret, "%s failed", __func__); } return ret; } diff --git a/migration/savevm.c b/migration/savevm.c index 327e9b346e..a2679ba0b8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1342,11 +1342,9 @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp) } save_section_header(f, se, QEMU_VM_SECTION_START); - ret = se->ops->save_setup(f, se->opaque); + ret = se->ops->save_setup(f, se->opaque, errp); save_section_footer(f, se); if (ret < 0) { - error_setg(errp, "failed to setup SaveStateEntry with id(name): " - "%d(%s): %d", se->section_id, se->idstr, ret); qemu_file_set_error(f, ret); break; } From e4fa064d5610a96e50b49c1ea34c98ef12d0034a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:04 +0100 Subject: [PATCH 0080/2884] migration: Add Error** argument to .load_setup() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be useful to report errors at a higher level, mostly in VFIO today. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20240320064911.545001-9-clg@redhat.com [peterx: drop comment for ERRP_GUARD, per Markus] Signed-off-by: Peter Xu --- hw/vfio/migration.c | 9 +++++++-- include/migration/register.h | 3 ++- migration/ram.c | 3 ++- migration/savevm.c | 11 +++++++---- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 5763c0b683..06ae40969b 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -588,12 +588,17 @@ static void vfio_save_state(QEMUFile *f, void *opaque) } } -static int vfio_load_setup(QEMUFile *f, void *opaque) +static int vfio_load_setup(QEMUFile *f, void *opaque, Error **errp) { VFIODevice *vbasedev = opaque; + int ret; - return vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, + ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, vbasedev->migration->device_state); + if (ret) { + error_setg(errp, "%s: Failed to set RESUMING state", vbasedev->name); + } + return ret; } static int vfio_load_cleanup(void *opaque) diff --git a/include/migration/register.h b/include/migration/register.h index 64fc7c1103..f60e797894 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -234,10 +234,11 @@ typedef struct SaveVMHandlers { * * @f: QEMUFile where to receive the data * @opaque: data pointer passed to register_savevm_live() + * @errp: pointer to Error*, to store an error if it happens. * * Returns zero to indicate success and negative for error */ - int (*load_setup)(QEMUFile *f, void *opaque); + int (*load_setup)(QEMUFile *f, void *opaque, Error **errp); /** * @load_cleanup diff --git a/migration/ram.c b/migration/ram.c index 6ea5a06e00..4cd4f0158c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3704,8 +3704,9 @@ void colo_release_ram_cache(void) * * @f: QEMUFile where to receive the data * @opaque: RAMState pointer + * @errp: pointer to Error*, to store an error if it happens. */ -static int ram_load_setup(QEMUFile *f, void *opaque) +static int ram_load_setup(QEMUFile *f, void *opaque, Error **errp) { xbzrle_load_setup(); ramblock_recv_map_init(); diff --git a/migration/savevm.c b/migration/savevm.c index a2679ba0b8..5d200cf42a 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2768,8 +2768,9 @@ static void qemu_loadvm_state_switchover_ack_needed(MigrationIncomingState *mis) trace_loadvm_state_switchover_ack_needed(mis->switchover_ack_pending_num); } -static int qemu_loadvm_state_setup(QEMUFile *f) +static int qemu_loadvm_state_setup(QEMUFile *f, Error **errp) { + ERRP_GUARD(); SaveStateEntry *se; int ret; @@ -2784,10 +2785,11 @@ static int qemu_loadvm_state_setup(QEMUFile *f) } } - ret = se->ops->load_setup(f, se->opaque); + ret = se->ops->load_setup(f, se->opaque, errp); if (ret < 0) { + error_prepend(errp, "Load state of device %s failed: ", + se->idstr); qemu_file_set_error(f, ret); - error_report("Load state of device %s failed", se->idstr); return ret; } } @@ -2968,7 +2970,8 @@ int qemu_loadvm_state(QEMUFile *f) return ret; } - if (qemu_loadvm_state_setup(f) != 0) { + if (qemu_loadvm_state_setup(f, &local_err) != 0) { + error_report_err(local_err); return -EINVAL; } From 3688fec8923101b3a44acde7f3db59b76f82b838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:05 +0100 Subject: [PATCH 0081/2884] memory: Add Error** argument to .log_global_start() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify all .log_global_start() handlers to take an Error** parameter and return a bool. Adapt memory_global_dirty_log_start() to interrupt on the first error the loop on handlers. In such case, a rollback is performed to stop dirty logging on all listeners where it was previously enabled. Cc: Stefano Stabellini Cc: Anthony Perard Cc: Paul Durrant Cc: "Michael S. Tsirkin" Cc: Paolo Bonzini Cc: David Hildenbrand Signed-off-by: Cédric Le Goater Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20240320064911.545001-10-clg@redhat.com [peterx: modify & enrich the comment for listener_add_address_space() ] Signed-off-by: Peter Xu --- hw/i386/xen/xen-hvm.c | 3 ++- hw/vfio/common.c | 4 +++- hw/virtio/vhost.c | 3 ++- include/exec/memory.h | 5 ++++- system/memory.c | 41 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 7745cb3963..f6e9a1bc86 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -457,11 +457,12 @@ static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) int128_get64(section->size)); } -static void xen_log_global_start(MemoryListener *listener) +static bool xen_log_global_start(MemoryListener *listener, Error **errp) { if (xen_enabled()) { xen_in_migration = true; } + return true; } static void xen_log_global_stop(MemoryListener *listener) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 011ceaab89..8f9cbdc026 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1066,7 +1066,8 @@ out: return ret; } -static void vfio_listener_log_global_start(MemoryListener *listener) +static bool vfio_listener_log_global_start(MemoryListener *listener, + Error **errp) { VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener); @@ -1083,6 +1084,7 @@ static void vfio_listener_log_global_start(MemoryListener *listener) ret, strerror(-ret)); vfio_set_migration_error(ret); } + return !ret; } static void vfio_listener_log_global_stop(MemoryListener *listener) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index f50180e60e..4acd77e890 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1044,7 +1044,7 @@ check_dev_state: return r; } -static void vhost_log_global_start(MemoryListener *listener) +static bool vhost_log_global_start(MemoryListener *listener, Error **errp) { int r; @@ -1052,6 +1052,7 @@ static void vhost_log_global_start(MemoryListener *listener) if (r < 0) { abort(); } + return true; } static void vhost_log_global_stop(MemoryListener *listener) diff --git a/include/exec/memory.h b/include/exec/memory.h index 8626a355b3..5555567bc4 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -998,8 +998,11 @@ struct MemoryListener { * active at that time. * * @listener: The #MemoryListener. + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ - void (*log_global_start)(MemoryListener *listener); + bool (*log_global_start)(MemoryListener *listener, Error **errp); /** * @log_global_stop: diff --git a/system/memory.c b/system/memory.c index a229a79988..86d6c33180 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2914,9 +2914,33 @@ static unsigned int postponed_stop_flags; static VMChangeStateEntry *vmstate_change; static void memory_global_dirty_log_stop_postponed_run(void); +static bool memory_global_dirty_log_do_start(Error **errp) +{ + MemoryListener *listener; + + QTAILQ_FOREACH(listener, &memory_listeners, link) { + if (listener->log_global_start) { + if (!listener->log_global_start(listener, errp)) { + goto err; + } + } + } + return true; + +err: + while ((listener = QTAILQ_PREV(listener, link)) != NULL) { + if (listener->log_global_stop) { + listener->log_global_stop(listener); + } + } + + return false; +} + void memory_global_dirty_log_start(unsigned int flags) { unsigned int old_flags; + Error *local_err = NULL; assert(flags && !(flags & (~GLOBAL_DIRTY_MASK))); @@ -2936,7 +2960,13 @@ void memory_global_dirty_log_start(unsigned int flags) trace_global_dirty_changed(global_dirty_tracking); if (!old_flags) { - MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward); + if (!memory_global_dirty_log_do_start(&local_err)) { + global_dirty_tracking &= ~flags; + trace_global_dirty_changed(global_dirty_tracking); + error_report_err(local_err); + return; + } + memory_region_transaction_begin(); memory_region_update_pending = true; memory_region_transaction_commit(); @@ -3014,8 +3044,15 @@ static void listener_add_address_space(MemoryListener *listener, listener->begin(listener); } if (global_dirty_tracking) { + /* + * Currently only VFIO can fail log_global_start(), and it's not + * yet allowed to hotplug any PCI device during migration. So this + * should never fail when invoked, guard it with error_abort. If + * it can start to fail in the future, we need to be able to fail + * the whole listener_add_address_space() and its callers. + */ if (listener->log_global_start) { - listener->log_global_start(listener); + listener->log_global_start(listener, &error_abort); } } From 92c20b2fc5cd3b423973a65aac945a605f93142e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:06 +0100 Subject: [PATCH 0082/2884] migration: Introduce ram_bitmaps_destroy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will use it in ram_init_bitmaps() to clear the allocated bitmaps when support for error reporting is added to memory_global_dirty_log_start(). Signed-off-by: Cédric Le Goater Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240320064911.545001-11-clg@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 4cd4f0158c..f0bd71438a 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2438,10 +2438,23 @@ static void xbzrle_cleanup(void) XBZRLE_cache_unlock(); } +static void ram_bitmaps_destroy(void) +{ + RAMBlock *block; + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + g_free(block->clear_bmap); + block->clear_bmap = NULL; + g_free(block->bmap); + block->bmap = NULL; + g_free(block->file_bmap); + block->file_bmap = NULL; + } +} + static void ram_save_cleanup(void *opaque) { RAMState **rsp = opaque; - RAMBlock *block; /* We don't use dirty log with background snapshots */ if (!migrate_background_snapshot()) { @@ -2458,12 +2471,7 @@ static void ram_save_cleanup(void *opaque) } } - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - g_free(block->clear_bmap); - block->clear_bmap = NULL; - g_free(block->bmap); - block->bmap = NULL; - } + ram_bitmaps_destroy(); xbzrle_cleanup(); compress_threads_save_cleanup(); From 639ec3fbf96c15b1568f52a50b9fa727cde3144b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:07 +0100 Subject: [PATCH 0083/2884] memory: Add Error** argument to the global_dirty_log routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the log_global*() handlers take an Error** parameter and return a bool, do the same for memory_global_dirty_log_start() and memory_global_dirty_log_stop(). The error is reported in the callers for now and it will be propagated in the call stack in the next changes. To be noted a functional change in ram_init_bitmaps(), if the dirty pages logger fails to start, there is no need to synchronize the dirty pages bitmaps. colo_incoming_start_dirty_log() could be modified in a similar way. Cc: Stefano Stabellini Cc: Anthony Perard Cc: Paul Durrant Cc: "Michael S. Tsirkin" Cc: Paolo Bonzini Cc: David Hildenbrand Cc: Hyman Huang Signed-off-by: Cédric Le Goater Reviewed-by: Fabiano Rosas Acked-by: Peter Xu Link: https://lore.kernel.org/r/20240320064911.545001-12-clg@redhat.com Signed-off-by: Peter Xu --- hw/i386/xen/xen-hvm.c | 2 +- include/exec/memory.h | 5 ++++- migration/dirtyrate.c | 13 +++++++++++-- migration/ram.c | 23 +++++++++++++++++++++-- system/memory.c | 11 +++++------ 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index f6e9a1bc86..006d219ad5 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -669,7 +669,7 @@ void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) void qmp_xen_set_global_dirty_log(bool enable, Error **errp) { if (enable) { - memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); + memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, errp); } else { memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); } diff --git a/include/exec/memory.h b/include/exec/memory.h index 5555567bc4..c129ee6db7 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2570,8 +2570,11 @@ void memory_listener_unregister(MemoryListener *listener); * memory_global_dirty_log_start: begin dirty logging for all regions * * @flags: purpose of starting dirty log, migration or dirty rate + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ -void memory_global_dirty_log_start(unsigned int flags); +bool memory_global_dirty_log_start(unsigned int flags, Error **errp); /** * memory_global_dirty_log_stop: end dirty logging for all regions diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index 1d2e85746f..d02d70b7b4 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -90,9 +90,15 @@ static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, void global_dirty_log_change(unsigned int flag, bool start) { + Error *local_err = NULL; + bool ret; + bql_lock(); if (start) { - memory_global_dirty_log_start(flag); + ret = memory_global_dirty_log_start(flag, &local_err); + if (!ret) { + error_report_err(local_err); + } } else { memory_global_dirty_log_stop(flag); } @@ -608,9 +614,12 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) { int64_t start_time; DirtyPageRecord dirty_pages; + Error *local_err = NULL; bql_lock(); - memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); + if (!memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE, &local_err)) { + error_report_err(local_err); + } /* * 1'round of log sync may return all 1 bits with diff --git a/migration/ram.c b/migration/ram.c index f0bd71438a..bade3e9281 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2862,18 +2862,32 @@ static void migration_bitmap_clear_discarded_pages(RAMState *rs) static void ram_init_bitmaps(RAMState *rs) { + Error *local_err = NULL; + bool ret = true; + qemu_mutex_lock_ramlist(); WITH_RCU_READ_LOCK_GUARD() { ram_list_init_bitmaps(); /* We don't use dirty log with background snapshots */ if (!migrate_background_snapshot()) { - memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); + ret = memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, + &local_err); + if (!ret) { + error_report_err(local_err); + goto out_unlock; + } migration_bitmap_sync_precopy(rs, false); } } +out_unlock: qemu_mutex_unlock_ramlist(); + if (!ret) { + ram_bitmaps_destroy(); + return; + } + /* * After an eventual first bitmap sync, fixup the initial bitmap * containing all 1s to exclude any discarded pages from migration. @@ -3665,6 +3679,8 @@ int colo_init_ram_cache(void) void colo_incoming_start_dirty_log(void) { RAMBlock *block = NULL; + Error *local_err = NULL; + /* For memory_global_dirty_log_start below. */ bql_lock(); qemu_mutex_lock_ramlist(); @@ -3676,7 +3692,10 @@ void colo_incoming_start_dirty_log(void) /* Discard this dirty bitmap record */ bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS); } - memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); + if (!memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, + &local_err)) { + error_report_err(local_err); + } } ram_state->migration_dirty_pages = 0; qemu_mutex_unlock_ramlist(); diff --git a/system/memory.c b/system/memory.c index 86d6c33180..c02c1d4bed 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2937,10 +2937,9 @@ err: return false; } -void memory_global_dirty_log_start(unsigned int flags) +bool memory_global_dirty_log_start(unsigned int flags, Error **errp) { unsigned int old_flags; - Error *local_err = NULL; assert(flags && !(flags & (~GLOBAL_DIRTY_MASK))); @@ -2952,7 +2951,7 @@ void memory_global_dirty_log_start(unsigned int flags) flags &= ~global_dirty_tracking; if (!flags) { - return; + return true; } old_flags = global_dirty_tracking; @@ -2960,17 +2959,17 @@ void memory_global_dirty_log_start(unsigned int flags) trace_global_dirty_changed(global_dirty_tracking); if (!old_flags) { - if (!memory_global_dirty_log_do_start(&local_err)) { + if (!memory_global_dirty_log_do_start(errp)) { global_dirty_tracking &= ~flags; trace_global_dirty_changed(global_dirty_tracking); - error_report_err(local_err); - return; + return false; } memory_region_transaction_begin(); memory_region_update_pending = true; memory_region_transaction_commit(); } + return true; } static void memory_global_dirty_log_do_stop(unsigned int flags) From 16ecd25a4f324fd98cb974a0fd80390c7e136ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:08 +0100 Subject: [PATCH 0084/2884] migration: Add Error** argument to ram_state_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the return value not exploited, follow the recommendations of qapi/error.h and change it to a bool Signed-off-by: Cédric Le Goater Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240320064911.545001-13-clg@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index bade3e9281..26ce11a337 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2780,13 +2780,13 @@ err_out: return -ENOMEM; } -static int ram_state_init(RAMState **rsp) +static bool ram_state_init(RAMState **rsp, Error **errp) { *rsp = g_try_new0(RAMState, 1); if (!*rsp) { - error_report("%s: Init ramstate fail", __func__); - return -1; + error_setg(errp, "%s: Init ramstate fail", __func__); + return false; } qemu_mutex_init(&(*rsp)->bitmap_mutex); @@ -2802,7 +2802,7 @@ static int ram_state_init(RAMState **rsp) (*rsp)->migration_dirty_pages = (*rsp)->ram_bytes_total >> TARGET_PAGE_BITS; ram_state_reset(*rsp); - return 0; + return true; } static void ram_list_init_bitmaps(void) @@ -2897,7 +2897,10 @@ out_unlock: static int ram_init_all(RAMState **rsp) { - if (ram_state_init(rsp)) { + Error *local_err = NULL; + + if (!ram_state_init(rsp, &local_err)) { + error_report_err(local_err); return -1; } @@ -3624,7 +3627,11 @@ void ram_handle_zero(void *host, uint64_t size) static void colo_init_ram_state(void) { - ram_state_init(&ram_state); + Error *local_err = NULL; + + if (!ram_state_init(&ram_state, &local_err)) { + error_report_err(local_err); + } } /* From 7bee8ba8bbcef27cc98bc85747258e942f8d9717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:09 +0100 Subject: [PATCH 0085/2884] migration: Add Error** argument to xbzrle_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the return value (-ENOMEM) is not exploited, follow the recommendations of qapi/error.h and change it to a bool Signed-off-by: Cédric Le Goater Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240320064911.545001-14-clg@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 26ce11a337..70797ef5d8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2727,44 +2727,41 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) * For every allocation, we will try not to crash the VM if the * allocation failed. */ -static int xbzrle_init(void) +static bool xbzrle_init(Error **errp) { - Error *local_err = NULL; - if (!migrate_xbzrle()) { - return 0; + return true; } XBZRLE_cache_lock(); XBZRLE.zero_target_page = g_try_malloc0(TARGET_PAGE_SIZE); if (!XBZRLE.zero_target_page) { - error_report("%s: Error allocating zero page", __func__); + error_setg(errp, "%s: Error allocating zero page", __func__); goto err_out; } XBZRLE.cache = cache_init(migrate_xbzrle_cache_size(), - TARGET_PAGE_SIZE, &local_err); + TARGET_PAGE_SIZE, errp); if (!XBZRLE.cache) { - error_report_err(local_err); goto free_zero_page; } XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE); if (!XBZRLE.encoded_buf) { - error_report("%s: Error allocating encoded_buf", __func__); + error_setg(errp, "%s: Error allocating encoded_buf", __func__); goto free_cache; } XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE); if (!XBZRLE.current_buf) { - error_report("%s: Error allocating current_buf", __func__); + error_setg(errp, "%s: Error allocating current_buf", __func__); goto free_encoded_buf; } /* We are all good */ XBZRLE_cache_unlock(); - return 0; + return true; free_encoded_buf: g_free(XBZRLE.encoded_buf); @@ -2777,7 +2774,7 @@ free_zero_page: XBZRLE.zero_target_page = NULL; err_out: XBZRLE_cache_unlock(); - return -ENOMEM; + return false; } static bool ram_state_init(RAMState **rsp, Error **errp) @@ -2904,7 +2901,8 @@ static int ram_init_all(RAMState **rsp) return -1; } - if (xbzrle_init()) { + if (!xbzrle_init(&local_err)) { + error_report_err(local_err); ram_state_cleanup(rsp); return -1; } From 030b56b280375242cd8591b06e806978b8564be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 20 Mar 2024 07:49:10 +0100 Subject: [PATCH 0086/2884] migration: Modify ram_init_bitmaps() to report dirty tracking errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .save_setup() handler has now an Error** argument that we can use to propagate errors reported by the .log_global_start() handler. Do that for the RAM. The caller qemu_savevm_state_setup() will store the error under the migration stream for later detection in the migration sequence. Signed-off-by: Cédric Le Goater Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240320064911.545001-15-clg@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 70797ef5d8..daffcd82d4 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2857,9 +2857,8 @@ static void migration_bitmap_clear_discarded_pages(RAMState *rs) } } -static void ram_init_bitmaps(RAMState *rs) +static bool ram_init_bitmaps(RAMState *rs, Error **errp) { - Error *local_err = NULL; bool ret = true; qemu_mutex_lock_ramlist(); @@ -2868,10 +2867,8 @@ static void ram_init_bitmaps(RAMState *rs) ram_list_init_bitmaps(); /* We don't use dirty log with background snapshots */ if (!migrate_background_snapshot()) { - ret = memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, - &local_err); + ret = memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, errp); if (!ret) { - error_report_err(local_err); goto out_unlock; } migration_bitmap_sync_precopy(rs, false); @@ -2882,7 +2879,7 @@ out_unlock: if (!ret) { ram_bitmaps_destroy(); - return; + return false; } /* @@ -2890,24 +2887,23 @@ out_unlock: * containing all 1s to exclude any discarded pages from migration. */ migration_bitmap_clear_discarded_pages(rs); + return true; } -static int ram_init_all(RAMState **rsp) +static int ram_init_all(RAMState **rsp, Error **errp) { - Error *local_err = NULL; - - if (!ram_state_init(rsp, &local_err)) { - error_report_err(local_err); + if (!ram_state_init(rsp, errp)) { return -1; } - if (!xbzrle_init(&local_err)) { - error_report_err(local_err); + if (!xbzrle_init(errp)) { ram_state_cleanup(rsp); return -1; } - ram_init_bitmaps(*rsp); + if (!ram_init_bitmaps(*rsp, errp)) { + return -1; + } return 0; } @@ -3104,8 +3100,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { - if (ram_init_all(rsp) != 0) { - error_setg(errp, "%s: failed to setup RAM for migration", __func__); + if (ram_init_all(rsp, errp) != 0) { compress_threads_save_cleanup(); return -1; } From dd031677257d7d00a72f28f073befc22691a0ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 29 Mar 2024 11:56:27 +0100 Subject: [PATCH 0087/2884] migration: Add Error** argument to add_bitmaps_to_list() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to report more precise errors in the migration handler dirty_bitmap_save_setup(). Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Vladimir Sementsov-Ogievskiy Link: https://lore.kernel.org/r/20240329105627.311227-1-clg@redhat.com Signed-off-by: Peter Xu --- migration/block-dirty-bitmap.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 542a8c297b..a7d55048c2 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -481,13 +481,13 @@ static void dirty_bitmap_do_save_cleanup(DBMSaveState *s) /* Called with the BQL taken. */ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, - const char *bs_name, GHashTable *alias_map) + const char *bs_name, GHashTable *alias_map, + Error **errp) { BdrvDirtyBitmap *bitmap; SaveBitmapState *dbms; GHashTable *bitmap_aliases; const char *node_alias, *bitmap_name, *bitmap_alias; - Error *local_err = NULL; /* When an alias map is given, @bs_name must be @bs's node name */ assert(!alias_map || !strcmp(bs_name, bdrv_get_node_name(bs))); @@ -504,8 +504,8 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, bitmap_name = bdrv_dirty_bitmap_name(bitmap); if (!bs_name || strcmp(bs_name, "") == 0) { - error_report("Bitmap '%s' in unnamed node can't be migrated", - bitmap_name); + error_setg(errp, "Bitmap '%s' in unnamed node can't be migrated", + bitmap_name); return -1; } @@ -525,9 +525,9 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, } if (node_alias[0] == '#') { - error_report("Bitmap '%s' in a node with auto-generated " - "name '%s' can't be migrated", - bitmap_name, node_alias); + error_setg(errp, "Bitmap '%s' in a node with auto-generated " + "name '%s' can't be migrated", + bitmap_name, node_alias); return -1; } @@ -538,8 +538,7 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, continue; } - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, &local_err)) { - error_report_err(local_err); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { return -1; } @@ -558,9 +557,9 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, } } else { if (strlen(bitmap_name) > UINT8_MAX) { - error_report("Cannot migrate bitmap '%s' on node '%s': " - "Name is longer than %u bytes", - bitmap_name, bs_name, UINT8_MAX); + error_setg(errp, "Cannot migrate bitmap '%s' on node '%s': " + "Name is longer than %u bytes", + bitmap_name, bs_name, UINT8_MAX); return -1; } bitmap_alias = bitmap_name; @@ -599,7 +598,7 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, } /* Called with the BQL taken. */ -static int init_dirty_bitmap_migration(DBMSaveState *s) +static int init_dirty_bitmap_migration(DBMSaveState *s, Error **errp) { BlockDriverState *bs; SaveBitmapState *dbms; @@ -643,7 +642,7 @@ static int init_dirty_bitmap_migration(DBMSaveState *s) } if (bs && bs->drv && !bs->drv->is_filter) { - if (add_bitmaps_to_list(s, bs, name, NULL)) { + if (add_bitmaps_to_list(s, bs, name, NULL, errp)) { goto fail; } g_hash_table_add(handled_by_blk, bs); @@ -656,7 +655,8 @@ static int init_dirty_bitmap_migration(DBMSaveState *s) continue; } - if (add_bitmaps_to_list(s, bs, bdrv_get_node_name(bs), alias_map)) { + if (add_bitmaps_to_list(s, bs, bdrv_get_node_name(bs), alias_map, + errp)) { goto fail; } } @@ -1218,9 +1218,7 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque, Error **errp) DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms = NULL; - if (init_dirty_bitmap_migration(s) < 0) { - error_setg(errp, - "Failed to initialize dirty tracking bitmap for blocks"); + if (init_dirty_bitmap_migration(s, errp) < 0) { return -1; } From 5ef7e26bdb7eda10d6d5e1b77121be9945e5e550 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 1 Apr 2024 23:41:10 +0800 Subject: [PATCH 0088/2884] migration/multifd: solve zero page causing multiple page faults Implemented recvbitmap tracking of received pages in multifd. If the zero page appears for the first time in the recvbitmap, this page is not checked and set. If the zero page has already appeared in the recvbitmap, there is no need to check the data but directly set the data to 0, because it is unlikely that the zero page will be migrated multiple times. Signed-off-by: Yuan Liu Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240401154110.2028453-2-yuan1.liu@intel.com [peterx: touch up the comment, as the bitmap is used outside postcopy now] Signed-off-by: Peter Xu --- include/exec/ramblock.h | 2 +- migration/multifd-zero-page.c | 4 +++- migration/multifd-zlib.c | 1 + migration/multifd-zstd.c | 1 + migration/multifd.c | 1 + migration/ram.c | 4 ++++ migration/ram.h | 1 + 7 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h index 848915ea5b..7062da380b 100644 --- a/include/exec/ramblock.h +++ b/include/exec/ramblock.h @@ -57,7 +57,7 @@ struct RAMBlock { off_t bitmap_offset; uint64_t pages_offset; - /* bitmap of already received pages in postcopy */ + /* Bitmap of already received pages. Only used on destination side. */ unsigned long *receivedmap; /* diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c index 1ba38be636..e1b8370f88 100644 --- a/migration/multifd-zero-page.c +++ b/migration/multifd-zero-page.c @@ -80,8 +80,10 @@ void multifd_recv_zero_page_process(MultiFDRecvParams *p) { for (int i = 0; i < p->zero_num; i++) { void *page = p->host + p->zero[i]; - if (!buffer_is_zero(page, p->page_size)) { + if (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) { memset(page, 0, p->page_size); + } else { + ramblock_recv_bitmap_set_offset(p->block, p->zero[i]); } } } diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 99821cd4d5..737a9645d2 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -284,6 +284,7 @@ static int zlib_recv(MultiFDRecvParams *p, Error **errp) int flush = Z_NO_FLUSH; unsigned long start = zs->total_out; + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); if (i == p->normal_num - 1) { flush = Z_SYNC_FLUSH; } diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 02112255ad..256858df0a 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -278,6 +278,7 @@ static int zstd_recv(MultiFDRecvParams *p, Error **errp) z->in.pos = 0; for (i = 0; i < p->normal_num; i++) { + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); z->out.dst = p->host + p->normal[i]; z->out.size = p->page_size; z->out.pos = 0; diff --git a/migration/multifd.c b/migration/multifd.c index 2802afe79d..f317bff077 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -277,6 +277,7 @@ static int nocomp_recv(MultiFDRecvParams *p, Error **errp) for (int i = 0; i < p->normal_num; i++) { p->iov[i].iov_base = p->host + p->normal[i]; p->iov[i].iov_len = p->page_size; + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); } return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); } diff --git a/migration/ram.c b/migration/ram.c index daffcd82d4..a975c5af16 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -275,6 +275,10 @@ void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, nr); } +void ramblock_recv_bitmap_set_offset(RAMBlock *rb, uint64_t byte_offset) +{ + set_bit_atomic(byte_offset >> TARGET_PAGE_BITS, rb->receivedmap); +} #define RAMBLOCK_RECV_BITMAP_ENDING (0x0123456789abcdefULL) /* diff --git a/migration/ram.h b/migration/ram.h index 08feecaf51..bc0318b834 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -69,6 +69,7 @@ int ramblock_recv_bitmap_test(RAMBlock *rb, void *host_addr); bool ramblock_recv_bitmap_test_byte_offset(RAMBlock *rb, uint64_t byte_offset); void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); +void ramblock_recv_bitmap_set_offset(RAMBlock *rb, uint64_t byte_offset); int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp); From 2cc637f1ea08d2a1b19fc5b1a30bc609f948de93 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 17 Apr 2024 10:56:34 +0800 Subject: [PATCH 0089/2884] migration/colo: Fix bdrv_graph_rdlock_main_loop: Assertion `!qemu_in_coroutine()' failed. bdrv_activate_all() should not be called from the coroutine context, move it to the QEMU thread colo_process_incoming_thread() with the bql_lock protected. The backtrace is as follows: #4 0x0000561af7948362 in bdrv_graph_rdlock_main_loop () at ../block/graph-lock.c:260 #5 0x0000561af7907a68 in graph_lockable_auto_lock_mainloop (x=0x7fd29810be7b) at /patch/to/qemu/include/block/graph-lock.h:259 #6 0x0000561af79167d1 in bdrv_activate_all (errp=0x7fd29810bed0) at ../block.c:6906 #7 0x0000561af762b4af in colo_incoming_co () at ../migration/colo.c:935 #8 0x0000561af7607e57 in process_incoming_migration_co (opaque=0x0) at ../migration/migration.c:793 #9 0x0000561af7adbeeb in coroutine_trampoline (i0=-106876144, i1=22042) at ../util/coroutine-ucontext.c:175 #10 0x00007fd2a5cf21c0 in () at /lib64/libc.so.6 Cc: qemu-stable@nongnu.org Cc: Fabiano Rosas Closes: https://gitlab.com/qemu-project/qemu/-/issues/2277 Fixes: 2b3912f135 ("block: Mark bdrv_first_blk() and bdrv_is_root_node() GRAPH_RDLOCK") Signed-off-by: Li Zhijian Reviewed-by: Zhang Chen Tested-by: Zhang Chen Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20240417025634.1014582-1-lizhijian@fujitsu.com Signed-off-by: Peter Xu --- migration/colo.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index 84632a603e..5600a43d78 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -835,6 +835,16 @@ static void *colo_process_incoming_thread(void *opaque) return NULL; } + /* Make sure all file formats throw away their mutable metadata */ + bql_lock(); + bdrv_activate_all(&local_err); + if (local_err) { + bql_unlock(); + error_report_err(local_err); + return NULL; + } + bql_unlock(); + failover_init_state(); mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); @@ -922,7 +932,6 @@ out: int coroutine_fn colo_incoming_co(void) { MigrationIncomingState *mis = migration_incoming_get_current(); - Error *local_err = NULL; QemuThread th; assert(bql_locked()); @@ -931,13 +940,6 @@ int coroutine_fn colo_incoming_co(void) return 0; } - /* Make sure all file formats throw away their mutable metadata */ - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - return -EINVAL; - } - qemu_thread_create(&th, "COLO incoming", colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); From 1a6f53953df65f31e922f8a1763dac9f10adc81b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 23 Apr 2024 17:33:36 -0700 Subject: [PATCH 0090/2884] Open 9.1 development tree Signed-off-by: Richard Henderson --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f7ee06693c..bc66ba6669 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.0 +9.0.50 From 4a18751cf423970221d86853c6909696fae52785 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 12 Mar 2024 15:13:34 +0100 Subject: [PATCH 0091/2884] error: Drop superfluous #include "qapi/qmp/qerror.h" Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-2-armbru@redhat.com> --- backends/iommufd.c | 1 - chardev/char-fe.c | 1 - system/rtc.c | 1 - 3 files changed, 3 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index 62a79fa6b0..76a0204852 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "sysemu/iommufd.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/module.h" #include "qom/object_interfaces.h" #include "qemu/error-report.h" diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 66cee8475a..b214ba3802 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "sysemu/replay.h" #include "chardev/char-fe.h" diff --git a/system/rtc.c b/system/rtc.c index 4904581abe..dc44576686 100644 --- a/system/rtc.c +++ b/system/rtc.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/timer.h" From 7f65e789abf496480be6a54ba454dd6641b45784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:35 +0100 Subject: [PATCH 0092/2884] qapi: Inline and remove QERR_BUS_NO_HOTPLUG definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Mechanical transformation using sed, and manual cleanup. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-3-armbru@redhat.com> --- hw/ppc/spapr_pci.c | 5 ++--- include/qapi/qmp/qerror.h | 3 --- system/qdev-monitor.c | 8 +++++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 25e0295d6f..72cfba419a 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -39,7 +39,6 @@ #include "trace.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "qapi/qmp/qerror.h" #include "hw/ppc/fdt.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" @@ -1554,7 +1553,7 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, * we need to let them know it's not enabled */ if (plugged_dev->hotplugged) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, + error_setg(errp, "Bus '%s' does not support hotplugging", phb->parent_obj.bus->qbus.name); return; } @@ -1675,7 +1674,7 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, SpaprDrc *drc = drc_from_dev(phb, pdev); if (!phb->dr_enabled) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, + error_setg(errp, "Bus '%s' does not support hotplugging", phb->parent_obj.bus->qbus.name); return; } diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 0c2689cf8a..06a1d2248e 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,9 +17,6 @@ * add new ones! */ -#define QERR_BUS_NO_HOTPLUG \ - "Bus '%s' does not support hotplugging" - #define QERR_DEVICE_HAS_NO_MEDIUM \ "Device '%s' has no medium" diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 840177d19f..2ae1460e21 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -660,7 +660,8 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, if (qdev_should_hide_device(opts, from_json, errp)) { if (bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); + error_setg(errp, "Bus '%s' does not support hotplugging", + bus->name); } return NULL; } else if (*errp) { @@ -668,7 +669,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); + error_setg(errp, "Bus '%s' does not support hotplugging", bus->name); return NULL; } @@ -910,7 +911,8 @@ void qdev_unplug(DeviceState *dev, Error **errp) } if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); + error_setg(errp, "Bus '%s' does not support hotplugging", + dev->parent_bus->name); return; } From a95921f1712b5f3bb9f905a08984b478b89e0fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:36 +0100 Subject: [PATCH 0093/2884] qapi: Inline and remove QERR_DEVICE_HAS_NO_MEDIUM definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Mechanical transformation using sed, and manual cleanup. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-4-armbru@redhat.com> --- block/snapshot.c | 7 ++++--- blockdev.c | 2 +- include/qapi/qmp/qerror.h | 3 --- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/block/snapshot.c b/block/snapshot.c index 8242b4abac..8fd1756777 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -28,7 +28,6 @@ #include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qstring.h" #include "qemu/option.h" #include "sysemu/block-backend.h" @@ -359,7 +358,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, GLOBAL_STATE_CODE(); if (!drv) { - error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); + error_setg(errp, "Device '%s' has no medium", + bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { @@ -437,7 +437,8 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs, GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!drv) { - error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); + error_setg(errp, "Device '%s' has no medium", + bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { diff --git a/blockdev.c b/blockdev.c index 057601dcf0..08eccc9052 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1395,7 +1395,7 @@ static void external_snapshot_action(TransactionAction *action, bdrv_drained_begin(state->old_bs); if (!bdrv_is_inserted(state->old_bs)) { - error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, + error_setg(errp, "Device '%s' has no medium", bdrv_get_device_or_node_name(state->old_bs)); return; } diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 06a1d2248e..daa889809b 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,9 +17,6 @@ * add new ones! */ -#define QERR_DEVICE_HAS_NO_MEDIUM \ - "Device '%s' has no medium" - #define QERR_DEVICE_NO_HOTPLUG \ "Device '%s' does not support hotplugging" From f95b25c37e34d575346fb171b2f59c162bbefb38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:37 +0100 Subject: [PATCH 0094/2884] qapi: Inline and remove QERR_DEVICE_NO_HOTPLUG definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Mechanical transformation using sed, and manual cleanup. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-5-armbru@redhat.com> --- hw/core/qdev.c | 4 ++-- include/qapi/qmp/qerror.h | 3 --- system/qdev-monitor.c | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index c68d0f7c51..00efaf1bd1 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -29,7 +29,6 @@ #include "qapi/error.h" #include "qapi/qapi-events-qdev.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -479,7 +478,8 @@ static void device_set_realized(Object *obj, bool value, Error **errp) static int unattached_count; if (dev->hotplugged && !dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj)); + error_setg(errp, "Device '%s' does not support hotplugging", + object_get_typename(obj)); return; } diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index daa889809b..e93211085a 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,9 +17,6 @@ * add new ones! */ -#define QERR_DEVICE_NO_HOTPLUG \ - "Device '%s' does not support hotplugging" - #define QERR_INVALID_PARAMETER \ "Invalid parameter '%s'" diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 2ae1460e21..6af6ef7d66 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -917,7 +917,7 @@ void qdev_unplug(DeviceState *dev, Error **errp) } if (!dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, + error_setg(errp, "Device '%s' does not support hotplugging", object_get_typename(OBJECT(dev))); return; } From c6f5d406e1ace1e3d7697e085024a3c7b93d6db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:38 +0100 Subject: [PATCH 0095/2884] qapi: Inline and remove QERR_INVALID_PARAMETER definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Mechanical transformation using: $ sed -i -e "s/QERR_INVALID_PARAMETER,/\"Invalid parameter '%s'\",/" \ $(git grep -lw QERR_INVALID_PARAMETER) Manually simplify qemu_opts_create(), and remove the macro definition in include/qapi/qmp/qerror.h. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-6-armbru@redhat.com> --- include/qapi/qmp/qerror.h | 3 --- qapi/opts-visitor.c | 2 +- util/qemu-option.c | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index e93211085a..63ab775dc5 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,9 +17,6 @@ * add new ones! */ -#define QERR_INVALID_PARAMETER \ - "Invalid parameter '%s'" - #define QERR_INVALID_PARAMETER_TYPE \ "Invalid parameter type for '%s', expected: %s" diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 8f1efab8b9..3d1a28b419 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -184,7 +184,7 @@ opts_check_struct(Visitor *v, Error **errp) const QemuOpt *first; first = g_queue_peek_head(any); - error_setg(errp, QERR_INVALID_PARAMETER, first->name); + error_setg(errp, "Invalid parameter '%s'", first->name); return false; } return true; diff --git a/util/qemu-option.c b/util/qemu-option.c index eedd08929b..201f7a87f3 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -498,7 +498,7 @@ static bool opt_validate(QemuOpt *opt, Error **errp) desc = find_desc_by_name(list->desc, opt->name); if (!desc && !opts_accepts_any(list)) { - error_setg(errp, QERR_INVALID_PARAMETER, opt->name); + error_setg(errp, "Invalid parameter '%s'", opt->name); return false; } @@ -531,7 +531,7 @@ bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, desc = find_desc_by_name(list->desc, name); if (!desc && !opts_accepts_any(list)) { - error_setg(errp, QERR_INVALID_PARAMETER, name); + error_setg(errp, "Invalid parameter '%s'", name); return false; } @@ -554,7 +554,7 @@ bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, desc = find_desc_by_name(list->desc, name); if (!desc && !opts_accepts_any(list)) { - error_setg(errp, QERR_INVALID_PARAMETER, name); + error_setg(errp, "Invalid parameter '%s'", name); return false; } @@ -612,7 +612,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, if (list->merge_lists) { if (id) { - error_setg(errp, QERR_INVALID_PARAMETER, "id"); + error_setg(errp, "Invalid parameter 'id'"); return NULL; } opts = qemu_opts_find(list, NULL); @@ -1103,7 +1103,7 @@ bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) QTAILQ_FOREACH(opt, &opts->head, next) { opt->desc = find_desc_by_name(desc, opt->name); if (!opt->desc) { - error_setg(errp, QERR_INVALID_PARAMETER, opt->name); + error_setg(errp, "Invalid parameter '%s'", opt->name); return false; } From aaeafa5090c8d006d7c39b4e11bcfb8515ef1ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:39 +0100 Subject: [PATCH 0096/2884] qapi: Inline QERR_INVALID_PARAMETER_TYPE definition (constant value) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Mechanical transformation using the following coccinelle semantic patch: @match@ expression errp; expression param; constant value; @@ error_setg(errp, QERR_INVALID_PARAMETER_TYPE, param, value); @script:python strformat depends on match@ value << match.value; fixedfmt; // new var @@ fixedfmt = f'"Invalid parameter type for \'%s\', expected: {value[1:-1]}"' coccinelle.fixedfmt = cocci.make_ident(fixedfmt) @replace@ expression match.errp; expression match.param; constant match.value; identifier strformat.fixedfmt; @@ - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, param, value); + error_setg(errp, fixedfmt, param); Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-7-armbru@redhat.com> Reviewed-by: Zhao Liu --- qapi/qobject-input-visitor.c | 32 ++++++++++++++++---------------- qapi/string-input-visitor.c | 8 ++++---- qom/object.c | 12 ++++++++---- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 3e8aca6b15..f110a804b2 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -288,8 +288,8 @@ static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, return false; } if (qobject_type(qobj) != QTYPE_QDICT) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "object"); + error_setg(errp, "Invalid parameter type for '%s', expected: object", + full_name(qiv, name)); return false; } @@ -326,8 +326,8 @@ static bool qobject_input_start_list(Visitor *v, const char *name, return false; } if (qobject_type(qobj) != QTYPE_QLIST) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "array"); + error_setg(errp, "Invalid parameter type for '%s', expected: array", + full_name(qiv, name)); return false; } @@ -405,8 +405,8 @@ static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, } qnum = qobject_to(QNum, qobj); if (!qnum || !qnum_get_try_int(qnum, obj)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "integer"); + error_setg(errp, "Invalid parameter type for '%s', expected: integer", + full_name(qiv, name)); return false; } return true; @@ -494,8 +494,8 @@ static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, } qbool = qobject_to(QBool, qobj); if (!qbool) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "boolean"); + error_setg(errp, "Invalid parameter type for '%s', expected: boolean", + full_name(qiv, name)); return false; } @@ -534,8 +534,8 @@ static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, } qstr = qobject_to(QString, qobj); if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "string"); + error_setg(errp, "Invalid parameter type for '%s', expected: string", + full_name(qiv, name)); return false; } @@ -565,8 +565,8 @@ static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, } qnum = qobject_to(QNum, qobj); if (!qnum) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "number"); + error_setg(errp, "Invalid parameter type for '%s', expected: number", + full_name(qiv, name)); return false; } @@ -587,8 +587,8 @@ static bool qobject_input_type_number_keyval(Visitor *v, const char *name, if (qemu_strtod_finite(str, NULL, &val)) { /* TODO report -ERANGE more nicely */ - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "number"); + error_setg(errp, "Invalid parameter type for '%s', expected: number", + full_name(qiv, name)); return false; } @@ -623,8 +623,8 @@ static bool qobject_input_type_null(Visitor *v, const char *name, } if (qobject_type(qobj) != QTYPE_QNULL) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "null"); + error_setg(errp, "Invalid parameter type for '%s', expected: null", + full_name(qiv, name)); return false; } *obj = qnull(); diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index 197139c1c0..3f1b9e9b41 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -353,8 +353,8 @@ static bool parse_type_number(Visitor *v, const char *name, double *obj, assert(siv->lm == LM_NONE); if (qemu_strtod_finite(siv->string, NULL, &val)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "number"); + error_setg(errp, "Invalid parameter type for '%s', expected: number", + name ? name : "null"); return false; } @@ -371,8 +371,8 @@ static bool parse_type_null(Visitor *v, const char *name, QNull **obj, *obj = NULL; if (siv->string[0]) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "null"); + error_setg(errp, "Invalid parameter type for '%s', expected: null", + name ? name : "null"); return false; } diff --git a/qom/object.c b/qom/object.c index d4a001cf41..3d96818f7d 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1495,7 +1495,8 @@ char *object_property_get_str(Object *obj, const char *name, } qstring = qobject_to(QString, ret); if (!qstring) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string"); + error_setg(errp, "Invalid parameter type for '%s', expected: string", + name); retval = NULL; } else { retval = g_strdup(qstring_get_str(qstring)); @@ -1556,7 +1557,8 @@ bool object_property_get_bool(Object *obj, const char *name, } qbool = qobject_to(QBool, ret); if (!qbool) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean"); + error_setg(errp, "Invalid parameter type for '%s', expected: boolean", + name); retval = false; } else { retval = qbool_get_bool(qbool); @@ -1589,7 +1591,8 @@ int64_t object_property_get_int(Object *obj, const char *name, qnum = qobject_to(QNum, ret); if (!qnum || !qnum_get_try_int(qnum, &retval)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "int"); + error_setg(errp, "Invalid parameter type for '%s', expected: int", + name); retval = -1; } @@ -1663,7 +1666,8 @@ uint64_t object_property_get_uint(Object *obj, const char *name, } qnum = qobject_to(QNum, ret); if (!qnum || !qnum_get_try_uint(qnum, &retval)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "uint"); + error_setg(errp, "Invalid parameter type for '%s', expected: uint", + name); retval = 0; } From ef929281f1ddb1ce74f5fe39377a88e6cc8237aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:40 +0100 Subject: [PATCH 0097/2884] qapi: Inline and remove QERR_INVALID_PARAMETER_TYPE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Manual changes (escaping the format in qapi/visit.py). Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-8-armbru@redhat.com> Reviewed-by: Zhao Liu --- include/qapi/qmp/qerror.h | 3 --- qom/object.c | 4 ++-- scripts/qapi/visit.py | 5 ++--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 63ab775dc5..b723830eff 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,9 +17,6 @@ * add new ones! */ -#define QERR_INVALID_PARAMETER_TYPE \ - "Invalid parameter type for '%s', expected: %s" - #define QERR_INVALID_PARAMETER_VALUE \ "Parameter '%s' expects %s" diff --git a/qom/object.c b/qom/object.c index 3d96818f7d..44ec8f6460 100644 --- a/qom/object.c +++ b/qom/object.c @@ -23,7 +23,6 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/forward-visitor.h" #include "qapi/qapi-builtin-visit.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" #include "trace.h" @@ -1912,7 +1911,8 @@ static Object *object_resolve_link(Object *obj, const char *name, } else if (!target) { target = object_resolve_path(path, &ambiguous); if (target || ambiguous) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type); + error_setg(errp, "Invalid parameter type for '%s', expected: %s", + name, target_type); } else { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", path); diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index c56ea4d724..a21b7b1468 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -278,8 +278,8 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, abort(); default: assert(visit_is_input(v)); - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "%(name)s"); + error_setg(errp, "Invalid parameter type for '%%s', expected: %(name)s", + name ? name : "null"); /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */ g_free(*obj); *obj = NULL; @@ -356,7 +356,6 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "%(visit)s.h" ''', visit=visit)) From 45d19d9306ade96404905aa89d534fc989ff9389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:41 +0100 Subject: [PATCH 0098/2884] qapi: Correct error message for 'vcpu_dirty_limit' parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QERR_INVALID_PARAMETER_VALUE is defined as: #define QERR_INVALID_PARAMETER_VALUE \ "Parameter '%s' expects %s" The current error is formatted as: "Parameter 'vcpu_dirty_limit' expects is invalid, it must greater then 1 MB/s" Replace by: "Parameter 'vcpu_dirty_limit' must be greater than 1 MB/s" Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-9-armbru@redhat.com> Reviewed-by: Zhao Liu [New error message corrected, commit message updated accordingly] --- migration/options.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/migration/options.c b/migration/options.c index bfd7753b69..d7a773aea8 100644 --- a/migration/options.c +++ b/migration/options.c @@ -1286,9 +1286,8 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) if (params->has_vcpu_dirty_limit && (params->vcpu_dirty_limit < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "vcpu_dirty_limit", - "is invalid, it must greater then 1 MB/s"); + error_setg(errp, + "Parameter 'vcpu_dirty_limit' must be greater than 1 MB/s"); return false; } From 005807860d3e1498ee591223db38f90d2c736165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:42 +0100 Subject: [PATCH 0099/2884] qapi: Inline and remove QERR_MIGRATION_ACTIVE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Mechanical transformation using sed, manually removing the definition in include/qapi/qmp/qerror.h. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-10-armbru@redhat.com> Reviewed-by: Zhao Liu [Straightforward conflict with commit aeaafb1e59f (migration: export migration_is_running) resolved] --- include/qapi/qmp/qerror.h | 3 --- migration/migration.c | 2 +- migration/options.c | 4 ++-- migration/savevm.c | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index b723830eff..385a4876d6 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -23,9 +23,6 @@ #define QERR_IO_ERROR \ "An IO error has occurred" -#define QERR_MIGRATION_ACTIVE \ - "There's a migration process in progress" - #define QERR_MISSING_PARAMETER \ "Parameter '%s' is missing" diff --git a/migration/migration.c b/migration/migration.c index 86bf76e925..32cd89e13d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1956,7 +1956,7 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, } if (migration_is_running()) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + error_setg(errp, "There's a migration process in progress"); return false; } diff --git a/migration/options.c b/migration/options.c index d7a773aea8..239f5ecfb4 100644 --- a/migration/options.c +++ b/migration/options.c @@ -685,7 +685,7 @@ bool migrate_cap_set(int cap, bool value, Error **errp) bool new_caps[MIGRATION_CAPABILITY__MAX]; if (migration_is_running()) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + error_setg(errp, "There's a migration process in progress"); return false; } @@ -729,7 +729,7 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, bool new_caps[MIGRATION_CAPABILITY__MAX]; if (migration_is_running() || migration_in_colo_state()) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + error_setg(errp, "There's a migration process in progress"); return; } diff --git a/migration/savevm.c b/migration/savevm.c index e7c1215671..3fae9f155f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1707,7 +1707,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) MigrationStatus status; if (migration_is_running()) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + error_setg(errp, "There's a migration process in progress"); return -EINVAL; } From be842efbe3102c6aa1980039f6d4a8f997f1ff8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Mar 2024 15:13:43 +0100 Subject: [PATCH 0100/2884] qapi: Inline and remove QERR_PROPERTY_VALUE_BAD definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the comment added in commit 4629ed1e98 ("qerror: Finally unused, clean up"), from 2015: /* * These macros will go away, please don't use * in new code, and do not add new ones! */ Manual change. Remove the definition in include/qapi/qmp/qerror.h. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Markus Armbruster Message-ID: <20240312141343.3168265-11-armbru@redhat.com> Reviewed-by: Zhao Liu --- hw/core/qdev-properties.c | 3 +-- include/qapi/qmp/qerror.h | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 7d6fa726fd..86a583574d 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -2,7 +2,6 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qapi/qapi-types-misc.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" @@ -792,7 +791,7 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj, break; default: case -EINVAL: - error_setg(errp, QERR_PROPERTY_VALUE_BAD, + error_setg(errp, "Property '%s.%s' doesn't take value '%s'", object_get_typename(obj), name, value); break; case -ENOENT: diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 385a4876d6..00b18e9082 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -26,9 +26,6 @@ #define QERR_MISSING_PARAMETER \ "Parameter '%s' is missing" -#define QERR_PROPERTY_VALUE_BAD \ - "Property '%s.%s' doesn't take value '%s'" - #define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" From 4f8f199fa569492bb07efee02489f521629d275d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:37 +0100 Subject: [PATCH 0101/2884] qapi/parser: fix typo - self.returns.info => self.errors.info Small copy-pasto. The correct info field to use in this conditional block is self.errors.info. Fixes: 3a025d3d1ffa Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-2-armbru@redhat.com> --- scripts/qapi/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index d8f76060b8..fed88e9074 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -733,7 +733,7 @@ class QAPIDoc: "'Returns' section is only valid for commands") if self.errors: raise QAPISemError( - self.returns.info, + self.errors.info, "'Errors' section is only valid for commands") def check(self) -> None: From 2daf52df8b832a221e486ef373953ba160c2d9e1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:38 +0100 Subject: [PATCH 0102/2884] qapi/parser: shush up pylint Shhh! Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-3-armbru@redhat.com> --- scripts/qapi/parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index fed88e9074..ec4ebef4e3 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -607,6 +607,7 @@ class QAPIDoc: """ class Section: + # pylint: disable=too-few-public-methods def __init__(self, info: QAPISourceInfo, tag: Optional[str] = None): # section source info, i.e. where it begins From cebc18810ab7dd26117e68cadba1f4373712c508 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:39 +0100 Subject: [PATCH 0103/2884] qapi: sort pylint suppressions Suggested-by: Markus Armbruster Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-4-armbru@redhat.com> --- scripts/qapi/pylintrc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index 90546df534..1342412c3c 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -16,13 +16,13 @@ ignore-patterns=schema.py, # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=fixme, +disable=consider-using-f-string, + fixme, missing-docstring, too-many-arguments, too-many-branches, - too-many-statements, too-many-instance-attributes, - consider-using-f-string, + too-many-statements, useless-option-value, [REPORTS] From ce7fde06306e0e63227bdf91be4c5d04ed26b23f Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:40 +0100 Subject: [PATCH 0104/2884] qapi/schema: add pylint suppressions With this patch, pylint is happy with the file, so enable it in the configuration. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-5-armbru@redhat.com> --- scripts/qapi/pylintrc | 5 ----- scripts/qapi/schema.py | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index 1342412c3c..c028a1f9f5 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -1,10 +1,5 @@ [MASTER] -# Add files or directories matching the regex patterns to the ignore list. -# The regex matches against base names, not paths. -ignore-patterns=schema.py, - - [MESSAGES CONTROL] # Disable the message, report, category or checker with the given id(s). You diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 8ba5665bc6..117f0f78f0 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -12,6 +12,8 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. +# pylint: disable=too-many-lines + # TODO catching name collisions in generated code would be nice from collections import OrderedDict @@ -83,6 +85,7 @@ class QAPISchemaEntity: return c_name(self.name) def check(self, schema): + # pylint: disable=unused-argument assert not self._checked seen = {} for f in self.features: @@ -113,6 +116,7 @@ class QAPISchemaEntity: return not self.info def visit(self, visitor): + # pylint: disable=unused-argument assert self._checked def describe(self): @@ -131,6 +135,7 @@ class QAPISchemaVisitor: pass def visit_needed(self, entity): + # pylint: disable=unused-argument # Default to visiting everything return True From 2418d1c43add3e8e7089a092574da703c15ac14c Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:41 +0100 Subject: [PATCH 0105/2884] qapi: create QAPISchemaDefinition Include entities don't have names, but we generally expect "entities" to have names. Reclassify all entities with names as *definitions*, leaving the nameless include entities as QAPISchemaEntity instances. This is primarily to help simplify typing around expectations of what callers expect for properties of an "entity". Suggested-by: Markus Armbruster Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-6-armbru@redhat.com> --- scripts/qapi/schema.py | 144 +++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 62 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 117f0f78f0..b298c8b4f9 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -55,14 +55,13 @@ class QAPISchemaIfCond: class QAPISchemaEntity: - meta: Optional[str] = None + """ + A schema entity. - def __init__(self, name: str, info, doc, ifcond=None, features=None): - assert name is None or isinstance(name, str) - for f in features or []: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) - self.name = name + This is either a directive, such as include, or a definition. + The latter uses sub-class `QAPISchemaDefinition`. + """ + def __init__(self, info): self._module = None # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. @@ -70,33 +69,17 @@ class QAPISchemaEntity: # triggered the implicit definition (there may be more than one # such place). self.info = info - self.doc = doc - self._ifcond = ifcond or QAPISchemaIfCond() - self.features = features or [] self._checked = False def __repr__(self): - if self.name is None: - return "<%s at 0x%x>" % (type(self).__name__, id(self)) - return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, - id(self)) - - def c_name(self): - return c_name(self.name) + return "<%s at 0x%x>" % (type(self).__name__, id(self)) def check(self, schema): # pylint: disable=unused-argument - assert not self._checked - seen = {} - for f in self.features: - f.check_clash(self.info, seen) self._checked = True def connect_doc(self, doc=None): - doc = doc or self.doc - if doc: - for f in self.features: - doc.connect_feature(f) + pass def _set_module(self, schema, info): assert self._checked @@ -107,6 +90,46 @@ class QAPISchemaEntity: def set_module(self, schema): self._set_module(schema, self.info) + def visit(self, visitor): + # pylint: disable=unused-argument + assert self._checked + + +class QAPISchemaDefinition(QAPISchemaEntity): + meta: Optional[str] = None + + def __init__(self, name: str, info, doc, ifcond=None, features=None): + assert isinstance(name, str) + super().__init__(info) + for f in features or []: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + self.name = name + self.doc = doc + self._ifcond = ifcond or QAPISchemaIfCond() + self.features = features or [] + + def __repr__(self): + return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, + id(self)) + + def c_name(self): + return c_name(self.name) + + def check(self, schema): + assert not self._checked + super().check(schema) + seen = {} + for f in self.features: + f.check_clash(self.info, seen) + + def connect_doc(self, doc=None): + super().connect_doc(doc) + doc = doc or self.doc + if doc: + for f in self.features: + doc.connect_feature(f) + @property def ifcond(self): assert self._checked @@ -115,10 +138,6 @@ class QAPISchemaEntity: def is_implicit(self): return not self.info - def visit(self, visitor): - # pylint: disable=unused-argument - assert self._checked - def describe(self): assert self.meta return "%s '%s'" % (self.meta, self.name) @@ -218,7 +237,7 @@ class QAPISchemaModule: class QAPISchemaInclude(QAPISchemaEntity): def __init__(self, sub_module, info): - super().__init__(None, info, None) + super().__init__(info) self._sub_module = sub_module def visit(self, visitor): @@ -226,7 +245,7 @@ class QAPISchemaInclude(QAPISchemaEntity): visitor.visit_include(self._sub_module.name, self.info) -class QAPISchemaType(QAPISchemaEntity): +class QAPISchemaType(QAPISchemaDefinition): # Return the C type for common use. # For the types we commonly box, this is a pointer type. def c_type(self): @@ -797,7 +816,7 @@ class QAPISchemaVariant(QAPISchemaObjectTypeMember): super().__init__(name, info, typ, False, ifcond) -class QAPISchemaCommand(QAPISchemaEntity): +class QAPISchemaCommand(QAPISchemaDefinition): meta = 'command' def __init__(self, name, info, doc, ifcond, features, @@ -868,7 +887,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.coroutine) -class QAPISchemaEvent(QAPISchemaEntity): +class QAPISchemaEvent(QAPISchemaDefinition): meta = 'event' def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): @@ -939,23 +958,24 @@ class QAPISchema: self.check() def _def_entity(self, ent): - # Only the predefined types are allowed to not have info - assert ent.info or self._predefining self._entity_list.append(ent) - if ent.name is None: - return + + def _def_definition(self, defn): + # Only the predefined types are allowed to not have info + assert defn.info or self._predefining + self._def_entity(defn) # TODO reject names that differ only in '_' vs. '.' vs. '-', # because they're liable to clash in generated C. - other_ent = self._entity_dict.get(ent.name) - if other_ent: - if other_ent.info: - where = QAPISourceError(other_ent.info, "previous definition") + other_defn = self._entity_dict.get(defn.name) + if other_defn: + if other_defn.info: + where = QAPISourceError(other_defn.info, "previous definition") raise QAPISemError( - ent.info, - "'%s' is already defined\n%s" % (ent.name, where)) + defn.info, + "'%s' is already defined\n%s" % (defn.name, where)) raise QAPISemError( - ent.info, "%s is already defined" % other_ent.describe()) - self._entity_dict[ent.name] = ent + defn.info, "%s is already defined" % other_defn.describe()) + self._entity_dict[defn.name] = defn def lookup_entity(self, name, typ=None): ent = self._entity_dict.get(name) @@ -997,7 +1017,7 @@ class QAPISchema: QAPISchemaInclude(self._make_module(include), expr.info)) def _def_builtin_type(self, name, json_type, c_type): - self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) + self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type)) # Instantiating only the arrays that are actually used would # be nice, but we can't as long as their generated code # (qapi-builtin-types.[ch]) may be shared by some other @@ -1023,15 +1043,15 @@ class QAPISchema: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( 'q_empty', None, None, None, None, None, [], None) - self._def_entity(self.the_empty_object_type) + self._def_definition(self.the_empty_object_type) qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] qtype_values = self._make_enum_members( [{'name': n} for n in qtypes], None) - self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, - qtype_values, 'QTYPE')) + self._def_definition(QAPISchemaEnumType( + 'QType', None, None, None, None, qtype_values, 'QTYPE')) def _make_features(self, features, info): if features is None: @@ -1053,7 +1073,8 @@ class QAPISchema: def _make_array_type(self, element_type, info): name = element_type + 'List' # reserved by check_defn_name_str() if not self.lookup_type(name): - self._def_entity(QAPISchemaArrayType(name, info, element_type)) + self._def_definition(QAPISchemaArrayType( + name, info, element_type)) return name def _make_implicit_object_type(self, name, info, ifcond, role, members): @@ -1068,7 +1089,7 @@ class QAPISchema: # later. pass else: - self._def_entity(QAPISchemaObjectType( + self._def_definition(QAPISchemaObjectType( name, info, None, ifcond, None, None, members, None)) return name @@ -1079,7 +1100,7 @@ class QAPISchema: ifcond = QAPISchemaIfCond(expr.get('if')) info = expr.info features = self._make_features(expr.get('features'), info) - self._def_entity(QAPISchemaEnumType( + self._def_definition(QAPISchemaEnumType( name, info, expr.doc, ifcond, features, self._make_enum_members(data, info), prefix)) @@ -1107,7 +1128,7 @@ class QAPISchema: info = expr.info ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) - self._def_entity(QAPISchemaObjectType( + self._def_definition(QAPISchemaObjectType( name, info, expr.doc, ifcond, features, base, self._make_members(data, info), None)) @@ -1137,7 +1158,7 @@ class QAPISchema: info) for (key, value) in data.items()] members: List[QAPISchemaObjectTypeMember] = [] - self._def_entity( + self._def_definition( QAPISchemaObjectType(name, info, expr.doc, ifcond, features, base, members, QAPISchemaVariants( @@ -1156,7 +1177,7 @@ class QAPISchema: info) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) - self._def_entity( + self._def_definition( QAPISchemaAlternateType( name, info, expr.doc, ifcond, features, QAPISchemaVariants(None, info, tag_member, variants))) @@ -1181,11 +1202,10 @@ class QAPISchema: if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, expr.doc, ifcond, - features, data, rets, - gen, success_response, - boxed, allow_oob, allow_preconfig, - coroutine)) + self._def_definition( + QAPISchemaCommand(name, info, expr.doc, ifcond, features, data, + rets, gen, success_response, boxed, allow_oob, + allow_preconfig, coroutine)) def _def_event(self, expr: QAPIExpression): name = expr['event'] @@ -1198,8 +1218,8 @@ class QAPISchema: data = self._make_implicit_object_type( name, info, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond, - features, data, boxed)) + self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond, + features, data, boxed)) def _def_exprs(self, exprs): for expr in exprs: From ec103961bf2b8e690d53f8ae98258e9c76caf99a Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:42 +0100 Subject: [PATCH 0106/2884] qapi/schema: declare type for QAPISchemaObjectTypeMember.type A QAPISchemaObjectTypeMember's type gets resolved only during .check(). We have QAPISchemaObjectTypeMember.__init__() initialize self.type = None, and .check() assign the actual type. Using .type before .check() is wrong, and hopefully crashes due to the value being None. Works. However, it makes for awkward typing. With .type: Optional[QAPISchemaType], mypy is of course unable to see that it's None before .check(), and a QAPISchemaType after. To help it over the hump, we'd have to assert self.type is not None before all the (valid) uses. The assertion catches invalid uses, but only at run time; mypy can't flag them. Instead, declare .type in .__init__() as QAPISchemaType *without* initializing it. Using .type before .check() now certainly crashes, which is an improvement. Mypy still can't flag invalid uses, but that's okay. Addresses typing errors such as these: qapi/schema.py:657: error: "None" has no attribute "alternate_qtype" [attr-defined] qapi/schema.py:662: error: "None" has no attribute "describe" [attr-defined] Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-7-armbru@redhat.com> --- scripts/qapi/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index b298c8b4f9..307f8af01a 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -786,7 +786,7 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self._type_name = typ - self.type = None + self.type: QAPISchemaType # set during check() self.optional = optional self.features = features or [] From 578cd9329b2c5beac55a8a8c672f96bd40cc183f Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:43 +0100 Subject: [PATCH 0107/2884] qapi/schema: declare type for QAPISchemaArrayType.element_type A QAPISchemaArrayType's element type gets resolved only during .check(). We have QAPISchemaArrayType.__init__() initialize self.element_type = None, and .check() assign the actual type. Using .element_type before .check() is wrong, and hopefully crashes due to the value being None. Works. However, it makes for awkward typing. With .element_type: Optional[QAPISchemaType], mypy is of course unable to see that it's None before .check(), and a QAPISchemaType after. To help it over the hump, we'd have to assert self.element_type is not None before all the (valid) uses. The assertion catches invalid uses, but only at run time; mypy can't flag them. Instead, declare .element_type in .__init__() as QAPISchemaType *without* initializing it. Using .element_type before .check() now certainly crashes, which is an improvement. Mypy still can't flag invalid uses, but that's okay. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-8-armbru@redhat.com> --- scripts/qapi/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 307f8af01a..48f157fb91 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -381,7 +381,7 @@ class QAPISchemaArrayType(QAPISchemaType): super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type - self.element_type = None + self.element_type: QAPISchemaType def need_has_if_optional(self): # When FOO is an array, we still need has_FOO to distinguish From d150be3d54e4df04948b7205f7ccdde5a84f0684 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:44 +0100 Subject: [PATCH 0108/2884] qapi/schema: make c_type() and json_type() abstract methods These methods should always return a str, it's only the default abstract implementation that doesn't. They can be marked "abstract", which requires subclasses to override the method with the proper return type. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-9-armbru@redhat.com> --- scripts/qapi/schema.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 48f157fb91..0b01c841ff 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -16,6 +16,7 @@ # TODO catching name collisions in generated code would be nice +from abc import ABC, abstractmethod from collections import OrderedDict import os import re @@ -245,9 +246,10 @@ class QAPISchemaInclude(QAPISchemaEntity): visitor.visit_include(self._sub_module.name, self.info) -class QAPISchemaType(QAPISchemaDefinition): +class QAPISchemaType(QAPISchemaDefinition, ABC): # Return the C type for common use. # For the types we commonly box, this is a pointer type. + @abstractmethod def c_type(self): pass @@ -259,6 +261,7 @@ class QAPISchemaType(QAPISchemaDefinition): def c_unboxed_type(self): return self.c_type() + @abstractmethod def json_type(self): pass From 9bda6c7d1108c13be67e615b50f5d1b61fa3177e Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:45 +0100 Subject: [PATCH 0109/2884] qapi/schema: adjust type narrowing for mypy's benefit We already take care to perform some type narrowing for arg_type and ret_type, but not in a way where mypy can utilize the result once we add type hints, e.g.: qapi/schema.py:833: error: Incompatible types in assignment (expression has type "QAPISchemaType", variable has type "Optional[QAPISchemaObjectType]") [assignment] qapi/schema.py:893: error: Incompatible types in assignment (expression has type "QAPISchemaType", variable has type "Optional[QAPISchemaObjectType]") [assignment] A simple change to use a temporary variable helps the medicine go down. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-10-armbru@redhat.com> --- scripts/qapi/schema.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 0b01c841ff..e44802369d 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -843,13 +843,14 @@ class QAPISchemaCommand(QAPISchemaDefinition): def check(self, schema): super().check(schema) if self._arg_type_name: - self.arg_type = schema.resolve_type( + arg_type = schema.resolve_type( self._arg_type_name, self.info, "command's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): + if not isinstance(arg_type, QAPISchemaObjectType): raise QAPISemError( self.info, "command's 'data' cannot take %s" - % self.arg_type.describe()) + % arg_type.describe()) + self.arg_type = arg_type if self.arg_type.variants and not self.boxed: raise QAPISemError( self.info, @@ -866,8 +867,8 @@ class QAPISchemaCommand(QAPISchemaDefinition): if self.name not in self.info.pragma.command_returns_exceptions: typ = self.ret_type if isinstance(typ, QAPISchemaArrayType): - typ = self.ret_type.element_type assert typ + typ = typ.element_type if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, @@ -903,13 +904,14 @@ class QAPISchemaEvent(QAPISchemaDefinition): def check(self, schema): super().check(schema) if self._arg_type_name: - self.arg_type = schema.resolve_type( + typ = schema.resolve_type( self._arg_type_name, self.info, "event's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): + if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, "event's 'data' cannot take %s" - % self.arg_type.describe()) + % typ.describe()) + self.arg_type = typ if self.arg_type.variants and not self.boxed: raise QAPISemError( self.info, From 10755a9536eaf134f86df1d32a47ee8b07c2f1b9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:46 +0100 Subject: [PATCH 0110/2884] qapi/schema: add type narrowing to lookup_type() This function is a bit hard to type as-is; mypy needs some assertions to assist with the type narrowing. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-11-armbru@redhat.com> --- scripts/qapi/schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index e44802369d..1034825415 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -989,7 +989,9 @@ class QAPISchema: return ent def lookup_type(self, name): - return self.lookup_entity(name, QAPISchemaType) + typ = self.lookup_entity(name, QAPISchemaType) + assert typ is None or isinstance(typ, QAPISchemaType) + return typ def resolve_type(self, name, info, what): typ = self.lookup_type(name) From 802a3e3f74fa0dccf5f2e2198647e5fc15af8c5f Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:47 +0100 Subject: [PATCH 0111/2884] qapi/schema: assert resolve_type has 'info' and 'what' args on error resolve_type() is generally used to resolve configuration-provided type names into type objects, and generally requires valid 'info' and 'what' parameters. In some cases, such as with QAPISchemaArrayType.check(), resolve_type may be used to resolve built-in types and as such will not have an 'info' argument, but also must not fail in this scenario. Use an assertion to sate mypy that we will indeed have 'info' and 'what' parameters for the error pathway in resolve_type. Note: there are only three callsites to resolve_type at present where "info" is perceived by mypy to be possibly None: 1) QAPISchemaArrayType.check() 2) QAPISchemaObjectTypeMember.check() 3) QAPISchemaEvent.check() Of those three, only the first actually ever passes None; the other two are limited by their base class initializers which accept info=None, but neither subclass actually use a None value in practice, currently. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-12-armbru@redhat.com> --- scripts/qapi/schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 1034825415..0ef9b3398a 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -996,6 +996,7 @@ class QAPISchema: def resolve_type(self, name, info, what): typ = self.lookup_type(name) if not typ: + assert info and what # built-in types must not fail lookup if callable(what): what = what(info) raise QAPISemError( From 7191400a44c4eef49d2af356e0744bfb5d273046 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 16:22:48 +0100 Subject: [PATCH 0112/2884] qapi: Assert built-in types exist QAPISchema.lookup_type('FOO') returns a QAPISchemaType when type 'FOO' exists, else None. It won't return None for built-in types like 'int'. Since mypy can't see that, it'll complain that we assign the Optional[QAPISchemaType] returned by .lookup_type() to QAPISchemaType variables. Add assertions to help it over the hump. Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-13-armbru@redhat.com> Reviewed-by: John Snow --- scripts/qapi/introspect.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 67c7d89aae..4679b1bc2c 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -227,10 +227,14 @@ const QLitObject %(c_name)s = %(c_string)s; # Map the various integer types to plain int if typ.json_type() == 'int': - typ = self._schema.lookup_type('int') + type_int = self._schema.lookup_type('int') + assert type_int + typ = type_int elif (isinstance(typ, QAPISchemaArrayType) and typ.element_type.json_type() == 'int'): - typ = self._schema.lookup_type('intList') + type_intList = self._schema.lookup_type('intList') + assert type_intList + typ = type_intList # Add type to work queue if new if typ not in self._used_types: self._used_types.append(typ) From 8c91329ff09cec94dac22b459cc9914a7f49c54a Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:49 +0100 Subject: [PATCH 0113/2884] qapi/schema: fix QAPISchemaArrayType.check's call to resolve_type Adjust the expression at the callsite to work around mypy's weak type introspection that believes this expression can resolve to QAPISourceInfo; it cannot. (Fundamentally: self.info only resolves to false in a boolean expression when it is None; therefore this expression may only ever produce Optional[str]. mypy does not know that 'info', when it is a QAPISourceInfo object, cannot ever be false.) Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-14-armbru@redhat.com> --- scripts/qapi/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 0ef9b3398a..087c6e9366 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -395,7 +395,7 @@ class QAPISchemaArrayType(QAPISchemaType): super().check(schema) self.element_type = schema.resolve_type( self._element_type_name, self.info, - self.info and self.info.defn_meta) + self.info.defn_meta if self.info else None) assert not isinstance(self.element_type, QAPISchemaArrayType) def set_module(self, schema): From 8b9e7fd3b38d4e0fb9311752a5b44b71cd8fbbc1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:50 +0100 Subject: [PATCH 0114/2884] qapi/schema: assert info is present when necessary QAPISchemaInfo arguments can often be None because built-in definitions don't have such information. The type hint can only be Optional[QAPISchemaInfo] then. But, mypy gets upset about all the places where we exploit that it can't actually be None there. Add assertions that will help mypy over the hump, to enable adding type hints in a forthcoming commit. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-15-armbru@redhat.com> --- scripts/qapi/schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 087c6e9366..173e27d9e2 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -751,6 +751,7 @@ class QAPISchemaMember: else: assert False + assert info is not None if defined_in != info.defn_name: return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) return "%s '%s'" % (role, self.name) @@ -841,6 +842,7 @@ class QAPISchemaCommand(QAPISchemaDefinition): self.coroutine = coroutine def check(self, schema): + assert self.info is not None super().check(schema) if self._arg_type_name: arg_type = schema.resolve_type( From 875f6242321a63b3599e0cf6f0694adf8d855799 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:51 +0100 Subject: [PATCH 0115/2884] qapi/schema: add _check_complete flag Instead of using the None value for the members field, use a dedicated flag to detect recursive misconfigurations. This is intended to assist with subsequent patches that seek to remove the "None" value from the members field (which can never hold that value after the final call to check()) in order to simplify the static typing of that field; avoiding the need of assertions littered at many callsites to eliminate the possibility of the None value. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-16-armbru@redhat.com> --- scripts/qapi/schema.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 173e27d9e2..2c3de72ae6 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -450,12 +450,13 @@ class QAPISchemaObjectType(QAPISchemaType): self.local_members = local_members self.variants = variants self.members = None + self._check_complete = False def check(self, schema): # This calls another type T's .check() exactly when the C # struct emitted by gen_object() contains that T's C struct # (pointers don't count). - if self.members is not None: + if self._check_complete: # A previous .check() completed: nothing to do return if self._checked: @@ -464,7 +465,7 @@ class QAPISchemaObjectType(QAPISchemaType): "object %s contains itself" % self.name) super().check(schema) - assert self._checked and self.members is None + assert self._checked and not self._check_complete seen = OrderedDict() if self._base_name: @@ -487,7 +488,8 @@ class QAPISchemaObjectType(QAPISchemaType): self.variants.check(schema, seen) self.variants.check_clash(self.info, seen) - self.members = members # mark completed + self.members = members + self._check_complete = True # mark completed # Check that the members of this type do not cause duplicate JSON members, # and update seen to track the members seen so far. Report any errors From 9beda22dcbd93150c822d651322f62e45eab25bb Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:52 +0100 Subject: [PATCH 0116/2884] qapi/schema: Don't initialize "members" with `None` Declare, but don't initialize the "members" field with type List[QAPISchemaObjectTypeMember]. This simplifies the typing from what would otherwise be Optional[List[T]] to merely List[T]. This removes the need to add assertions to several callsites that this value is not None - which it never will be after the delayed initialization in check() anyway. The type declaration without initialization trick will cause accidental uses of this field prior to full initialization to raise an AttributeError. (Note that it is valid to have an empty members list, see the internal q_empty object as an example. For this reason, we cannot use the empty list as a replacement test for full initialization and instead rely on the _checked/_check_complete fields.) Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-17-armbru@redhat.com> --- scripts/qapi/schema.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 2c3de72ae6..74b0d7b007 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -20,7 +20,7 @@ from abc import ABC, abstractmethod from collections import OrderedDict import os import re -from typing import List, Optional +from typing import List, Optional, cast from .common import ( POINTER_SUFFIX, @@ -449,7 +449,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.base = None self.local_members = local_members self.variants = variants - self.members = None + self.members: List[QAPISchemaObjectTypeMember] self._check_complete = False def check(self, schema): @@ -482,7 +482,11 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.local_members: m.check(schema) m.check_clash(self.info, seen) - members = seen.values() + + # self.check_clash() works in terms of the supertype, but + # self.members is declared List[QAPISchemaObjectTypeMember]. + # Cast down to the subtype. + members = cast(List[QAPISchemaObjectTypeMember], list(seen.values())) if self.variants: self.variants.check(schema, seen) @@ -515,11 +519,9 @@ class QAPISchemaObjectType(QAPISchemaType): return self.name.startswith('q_') def is_empty(self): - assert self.members is not None return not self.members and not self.variants def has_conditional_members(self): - assert self.members is not None return any(m.ifcond.is_present() for m in self.members) def c_name(self): From 583f4d6fdd96f0f25f39018a9b160654e3f9aa01 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:53 +0100 Subject: [PATCH 0117/2884] qapi/schema: fix typing for QAPISchemaVariants.tag_member There are two related changes here: (1) We need to perform type narrowing for resolving the type of tag_member during check(), and (2) tag_member is a delayed initialization field, but we can hide it behind a property that raises an Exception if it's called too early. This simplifies the typing in quite a few places and avoids needing to assert that the "tag_member is not None" at a dozen callsites, which can be confusing and suggest the wrong thing to a drive-by contributor. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-18-armbru@redhat.com> --- scripts/qapi/schema.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 74b0d7b007..9c138badb0 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -627,25 +627,39 @@ class QAPISchemaVariants: assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info - self.tag_member = tag_member + self._tag_member: Optional[QAPISchemaObjectTypeMember] = tag_member self.variants = variants + @property + def tag_member(self) -> 'QAPISchemaObjectTypeMember': + if self._tag_member is None: + raise RuntimeError( + "QAPISchemaVariants has no tag_member property until " + "after check() has been run." + ) + return self._tag_member + def set_defined_in(self, name): for v in self.variants: v.set_defined_in(name) def check(self, schema, seen): if self._tag_name: # union - self.tag_member = seen.get(c_name(self._tag_name)) + # We need to narrow the member type: + tmp = seen.get(c_name(self._tag_name)) + assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember) + self._tag_member = tmp + base = "'base'" # Pointing to the base type when not implicit would be # nice, but we don't know it here - if not self.tag_member or self._tag_name != self.tag_member.name: + if not self._tag_member or self._tag_name != self._tag_member.name: raise QAPISemError( self.info, "discriminator '%s' is not a member of %s" % (self._tag_name, base)) # Here we do: + assert self.tag_member.defined_in base_type = schema.lookup_type(self.tag_member.defined_in) assert base_type if not base_type.is_implicit(): @@ -666,11 +680,13 @@ class QAPISchemaVariants: "discriminator member '%s' of %s must not be conditional" % (self._tag_name, base)) else: # alternate + assert self._tag_member assert isinstance(self.tag_member.type, QAPISchemaEnumType) assert not self.tag_member.optional assert not self.tag_member.ifcond.is_present() if self._tag_name: # union # branches that are not explicitly covered get an empty type + assert self.tag_member.defined_in cases = {v.name for v in self.variants} for m in self.tag_member.type.members: if m.name not in cases: From 7e09dd686f9dc8ee75453fc400e0366cd4c9d3e2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:54 +0100 Subject: [PATCH 0118/2884] qapi/schema: assert inner type of QAPISchemaVariants in check_clash() QAPISchemaVariant's "variants" field is typed as List[QAPISchemaVariant], where the typing for QAPISchemaVariant allows its type field to be any QAPISchemaType. However, QAPISchemaVariant expects that all of its variants contain the narrower QAPISchemaObjectType. This relationship is enforced at runtime in QAPISchemaVariants.check(). This relationship is not embedded in the type system though, so QAPISchemaVariants.check_clash() needs to re-assert this property in order to call QAPISchemaVariant.type.check_clash(). Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-19-armbru@redhat.com> --- scripts/qapi/schema.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 9c138badb0..177bfa0d11 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -716,7 +716,10 @@ class QAPISchemaVariants: def check_clash(self, info, seen): for v in self.variants: # Reset seen map for each variant, since qapi names from one - # branch do not affect another branch + # branch do not affect another branch. + # + # v.type's typing is enforced in check() above. + assert isinstance(v.type, QAPISchemaObjectType) v.type.check_clash(info, dict(seen)) From 7c6e446476ddcaa9c587c13f7815d14f8866ecea Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:55 +0100 Subject: [PATCH 0119/2884] qapi/parser: demote QAPIExpression to Dict[str, Any] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dict[str, object] is a stricter type, but with the way that code is currently arranged, it is infeasible to enforce this strictness. In particular, although expr.py's entire raison d'être is normalization and type-checking of QAPI Expressions, that type information is not "remembered" in any meaningful way by mypy because each individual expression is not downcast to a specific expression type that holds all the details of each expression's unique form. As a result, all of the code in schema.py that deals with actually creating type-safe specialized structures has no guarantee (myopically) that the data it is being passed is correct. There are two ways to solve this: (1) Re-assert that the incoming data is in the shape we expect it to be, or (2) Disable type checking for this data. (1) is appealing to my sense of strictness, but I gotta concede that it is asinine to re-check the shape of a QAPIExpression in schema.py when expr.py has just completed that work at length. The duplication of code and the nightmare thought of needing to update both locations if and when we change the shape of these structures makes me extremely reluctant to go down this route. (2) allows us the chance to miss updating types in the case that types are updated in expr.py, but it *is* an awful lot simpler and, importantly, gets us closer to type checking schema.py *at all*. Something is better than nothing, I'd argue. So, do the simpler dumber thing and worry about future strictness improvements later. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-20-armbru@redhat.com> --- scripts/qapi/parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index ec4ebef4e3..2f3c704fa2 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -19,6 +19,7 @@ import os import re from typing import ( TYPE_CHECKING, + Any, Dict, List, Mapping, @@ -43,7 +44,7 @@ if TYPE_CHECKING: _ExprValue = Union[List[object], Dict[str, object], str, bool] -class QAPIExpression(Dict[str, object]): +class QAPIExpression(Dict[str, Any]): # pylint: disable=too-few-public-methods def __init__(self, data: Mapping[str, object], From d5e2f3d03c38db716b3f18927626facc8233830c Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:56 +0100 Subject: [PATCH 0120/2884] qapi/parser.py: assert member.info is present in connect_member Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-21-armbru@redhat.com> --- scripts/qapi/parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 2f3c704fa2..7b13a583ac 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -707,6 +707,7 @@ class QAPIDoc: def connect_member(self, member: 'QAPISchemaMember') -> None: if member.name not in self.args: + assert member.info if self.symbol not in member.info.pragma.documentation_exceptions: raise QAPISemError(member.info, "%s '%s' lacks documentation" From 4ed3fe08222abcf4f3d15a9b810ded0790123b2e Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:57 +0100 Subject: [PATCH 0121/2884] qapi/schema: add type hints This patch only adds type hints, which aren't utilized at runtime and don't change the behavior of this module in any way. In a scant few locations, type hints are removed where no longer necessary due to inference power from typing all of the rest of creation; and any type hints that no longer need string quotes are changed. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-22-armbru@redhat.com> --- scripts/qapi/schema.py | 568 ++++++++++++++++++++++++++++------------- 1 file changed, 396 insertions(+), 172 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 177bfa0d11..ef4d6a7def 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -16,11 +16,21 @@ # TODO catching name collisions in generated code would be nice +from __future__ import annotations + from abc import ABC, abstractmethod from collections import OrderedDict import os import re -from typing import List, Optional, cast +from typing import ( + Any, + Callable, + Dict, + List, + Optional, + Union, + cast, +) from .common import ( POINTER_SUFFIX, @@ -32,26 +42,30 @@ from .common import ( ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs -from .parser import QAPIExpression, QAPISchemaParser +from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser +from .source import QAPISourceInfo class QAPISchemaIfCond: - def __init__(self, ifcond=None): + def __init__( + self, + ifcond: Optional[Union[str, Dict[str, object]]] = None, + ) -> None: self.ifcond = ifcond - def _cgen(self): + def _cgen(self) -> str: return cgen_ifcond(self.ifcond) - def gen_if(self): + def gen_if(self) -> str: return gen_if(self._cgen()) - def gen_endif(self): + def gen_endif(self) -> str: return gen_endif(self._cgen()) - def docgen(self): + def docgen(self) -> str: return docgen_ifcond(self.ifcond) - def is_present(self): + def is_present(self) -> bool: return bool(self.ifcond) @@ -62,8 +76,8 @@ class QAPISchemaEntity: This is either a directive, such as include, or a definition. The latter uses sub-class `QAPISchemaDefinition`. """ - def __init__(self, info): - self._module = None + def __init__(self, info: Optional[QAPISourceInfo]): + self._module: Optional[QAPISchemaModule] = None # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. # For implicitly defined entities, info points to a place that @@ -72,34 +86,43 @@ class QAPISchemaEntity: self.info = info self._checked = False - def __repr__(self): + def __repr__(self) -> str: return "<%s at 0x%x>" % (type(self).__name__, id(self)) - def check(self, schema): + def check(self, schema: QAPISchema) -> None: # pylint: disable=unused-argument self._checked = True - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: pass - def _set_module(self, schema, info): + def _set_module( + self, schema: QAPISchema, info: Optional[QAPISourceInfo] + ) -> None: assert self._checked fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME self._module = schema.module_by_fname(fname) self._module.add_entity(self) - def set_module(self, schema): + def set_module(self, schema: QAPISchema) -> None: self._set_module(schema, self.info) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: # pylint: disable=unused-argument assert self._checked class QAPISchemaDefinition(QAPISchemaEntity): - meta: Optional[str] = None + meta: str - def __init__(self, name: str, info, doc, ifcond=None, features=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): assert isinstance(name, str) super().__init__(info) for f in features or []: @@ -110,21 +133,21 @@ class QAPISchemaDefinition(QAPISchemaEntity): self._ifcond = ifcond or QAPISchemaIfCond() self.features = features or [] - def __repr__(self): + def __repr__(self) -> str: return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, id(self)) - def c_name(self): + def c_name(self) -> str: return c_name(self.name) - def check(self, schema): + def check(self, schema: QAPISchema) -> None: assert not self._checked super().check(schema) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for f in self.features: f.check_clash(self.info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: @@ -132,62 +155,120 @@ class QAPISchemaDefinition(QAPISchemaEntity): doc.connect_feature(f) @property - def ifcond(self): + def ifcond(self) -> QAPISchemaIfCond: assert self._checked return self._ifcond - def is_implicit(self): + def is_implicit(self) -> bool: return not self.info - def describe(self): + def describe(self) -> str: assert self.meta return "%s '%s'" % (self.meta, self.name) class QAPISchemaVisitor: - def visit_begin(self, schema): + def visit_begin(self, schema: QAPISchema) -> None: pass - def visit_end(self): + def visit_end(self) -> None: pass - def visit_module(self, name): + def visit_module(self, name: str) -> None: pass - def visit_needed(self, entity): + def visit_needed(self, entity: QAPISchemaEntity) -> bool: # pylint: disable=unused-argument # Default to visiting everything return True - def visit_include(self, name, info): + def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None: pass - def visit_builtin_type(self, name, info, json_type): + def visit_builtin_type( + self, name: str, info: Optional[QAPISourceInfo], json_type: str + ) -> None: pass - def visit_enum_type(self, name, info, ifcond, features, members, prefix): + def visit_enum_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str], + ) -> None: pass - def visit_array_type(self, name, info, ifcond, element_type): + def visit_array_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + element_type: QAPISchemaType, + ) -> None: pass - def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + def visit_object_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants], + ) -> None: pass - def visit_object_type_flat(self, name, info, ifcond, features, - members, variants): + def visit_object_type_flat( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants], + ) -> None: pass - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants, + ) -> None: pass - def visit_command(self, name, info, ifcond, features, - arg_type, ret_type, gen, success_response, boxed, - allow_oob, allow_preconfig, coroutine): + def visit_command( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + ret_type: Optional[QAPISchemaType], + gen: bool, + success_response: bool, + boxed: bool, + allow_oob: bool, + allow_preconfig: bool, + coroutine: bool, + ) -> None: pass - def visit_event(self, name, info, ifcond, features, arg_type, boxed): + def visit_event( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + boxed: bool, + ) -> None: pass @@ -195,9 +276,9 @@ class QAPISchemaModule: BUILTIN_MODULE_NAME = './builtin' - def __init__(self, name): + def __init__(self, name: str): self.name = name - self._entity_list = [] + self._entity_list: List[QAPISchemaEntity] = [] @staticmethod def is_system_module(name: str) -> bool: @@ -226,10 +307,10 @@ class QAPISchemaModule: """ return name == cls.BUILTIN_MODULE_NAME - def add_entity(self, ent): + def add_entity(self, ent: QAPISchemaEntity) -> None: self._entity_list.append(ent) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_module(self.name) for entity in self._entity_list: if visitor.visit_needed(entity): @@ -237,11 +318,11 @@ class QAPISchemaModule: class QAPISchemaInclude(QAPISchemaEntity): - def __init__(self, sub_module, info): + def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo): super().__init__(info) self._sub_module = sub_module - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_include(self._sub_module.name, self.info) @@ -250,22 +331,22 @@ class QAPISchemaType(QAPISchemaDefinition, ABC): # Return the C type for common use. # For the types we commonly box, this is a pointer type. @abstractmethod - def c_type(self): + def c_type(self) -> str: pass # Return the C type to be used in a parameter list. - def c_param_type(self): + def c_param_type(self) -> str: return self.c_type() # Return the C type to be used where we suppress boxing. - def c_unboxed_type(self): + def c_unboxed_type(self) -> str: return self.c_type() @abstractmethod - def json_type(self): + def json_type(self) -> str: pass - def alternate_qtype(self): + def alternate_qtype(self) -> Optional[str]: json2qtype = { 'null': 'QTYPE_QNULL', 'string': 'QTYPE_QSTRING', @@ -277,17 +358,17 @@ class QAPISchemaType(QAPISchemaDefinition, ABC): } return json2qtype.get(self.json_type()) - def doc_type(self): + def doc_type(self) -> Optional[str]: if self.is_implicit(): return None return self.name - def need_has_if_optional(self): + def need_has_if_optional(self) -> bool: # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant. # Except for arrays; see QAPISchemaArrayType.need_has_if_optional(). return not self.c_type().endswith(POINTER_SUFFIX) - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) for feat in self.features: if feat.is_special(): @@ -295,7 +376,7 @@ class QAPISchemaType(QAPISchemaDefinition, ABC): self.info, f"feature '{feat.name}' is not supported for types") - def describe(self): + def describe(self) -> str: assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -303,7 +384,7 @@ class QAPISchemaType(QAPISchemaDefinition, ABC): class QAPISchemaBuiltinType(QAPISchemaType): meta = 'built-in' - def __init__(self, name, json_type, c_type): + def __init__(self, name: str, json_type: str, c_type: str): super().__init__(name, None, None) assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', @@ -311,24 +392,24 @@ class QAPISchemaBuiltinType(QAPISchemaType): self._json_type_name = json_type self._c_type_name = c_type - def c_name(self): + def c_name(self) -> str: return self.name - def c_type(self): + def c_type(self) -> str: return self._c_type_name - def c_param_type(self): + def c_param_type(self) -> str: if self.name == 'str': return 'const ' + self._c_type_name return self._c_type_name - def json_type(self): + def json_type(self) -> str: return self._json_type_name - def doc_type(self): + def doc_type(self) -> str: return self.json_type() - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_builtin_type(self.name, self.info, self.json_type()) @@ -336,7 +417,16 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, features, members, prefix): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: Optional[List[QAPISchemaFeature]], + members: List[QAPISchemaEnumMember], + prefix: Optional[str], + ): super().__init__(name, info, doc, ifcond, features) for m in members: assert isinstance(m, QAPISchemaEnumMember) @@ -345,32 +435,32 @@ class QAPISchemaEnumType(QAPISchemaType): self.members = members self.prefix = prefix - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for m in self.members: m.check_clash(self.info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc for m in self.members: m.connect_doc(doc) - def is_implicit(self): + def is_implicit(self) -> bool: # See QAPISchema._def_predefineds() return self.name == 'QType' - def c_type(self): + def c_type(self) -> str: return c_name(self.name) - def member_names(self): + def member_names(self) -> List[str]: return [m.name for m in self.members] - def json_type(self): + def json_type(self) -> str: return 'string' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_enum_type( self.name, self.info, self.ifcond, self.features, @@ -380,60 +470,71 @@ class QAPISchemaEnumType(QAPISchemaType): class QAPISchemaArrayType(QAPISchemaType): meta = 'array' - def __init__(self, name, info, element_type): + def __init__( + self, name: str, info: Optional[QAPISourceInfo], element_type: str + ): super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type: QAPISchemaType - def need_has_if_optional(self): + def need_has_if_optional(self) -> bool: # When FOO is an array, we still need has_FOO to distinguish # absent (!has_FOO) from present and empty (has_FOO && !FOO). return True - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) self.element_type = schema.resolve_type( self._element_type_name, self.info, self.info.defn_meta if self.info else None) assert not isinstance(self.element_type, QAPISchemaArrayType) - def set_module(self, schema): + def set_module(self, schema: QAPISchema) -> None: self._set_module(schema, self.element_type.info) @property - def ifcond(self): + def ifcond(self) -> QAPISchemaIfCond: assert self._checked return self.element_type.ifcond - def is_implicit(self): + def is_implicit(self) -> bool: return True - def c_type(self): + def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX - def json_type(self): + def json_type(self) -> str: return 'array' - def doc_type(self): + def doc_type(self) -> Optional[str]: elt_doc_type = self.element_type.doc_type() if not elt_doc_type: return None return 'array of ' + elt_doc_type - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_array_type(self.name, self.info, self.ifcond, self.element_type) - def describe(self): + def describe(self) -> str: assert self.meta return "%s type ['%s']" % (self.meta, self._element_type_name) class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, features, - base, local_members, variants): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: Optional[List[QAPISchemaFeature]], + base: Optional[str], + local_members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants], + ): # struct has local_members, optional base, and no variants # union has base, variants, and no local_members super().__init__(name, info, doc, ifcond, features) @@ -452,7 +553,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.members: List[QAPISchemaObjectTypeMember] self._check_complete = False - def check(self, schema): + def check(self, schema: QAPISchema) -> None: # This calls another type T's .check() exactly when the C # struct emitted by gen_object() contains that T's C struct # (pointers don't count). @@ -498,14 +599,18 @@ class QAPISchemaObjectType(QAPISchemaType): # Check that the members of this type do not cause duplicate JSON members, # and update seen to track the members seen so far. Report any errors # on behalf of info, which is not necessarily self.info - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: assert self._checked for m in self.members: m.check_clash(info, seen) if self.variants: self.variants.check_clash(info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if self.base and self.base.is_implicit(): @@ -513,32 +618,32 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.local_members: m.connect_doc(doc) - def is_implicit(self): + def is_implicit(self) -> bool: # See QAPISchema._make_implicit_object_type(), as well as # _def_predefineds() return self.name.startswith('q_') - def is_empty(self): + def is_empty(self) -> bool: return not self.members and not self.variants - def has_conditional_members(self): + def has_conditional_members(self) -> bool: return any(m.ifcond.is_present() for m in self.members) - def c_name(self): + def c_name(self) -> str: assert self.name != 'q_empty' return super().c_name() - def c_type(self): + def c_type(self) -> str: assert not self.is_implicit() return c_name(self.name) + POINTER_SUFFIX - def c_unboxed_type(self): + def c_unboxed_type(self) -> str: return c_name(self.name) - def json_type(self): + def json_type(self) -> str: return 'object' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_object_type( self.name, self.info, self.ifcond, self.features, @@ -551,7 +656,15 @@ class QAPISchemaObjectType(QAPISchemaType): class QAPISchemaAlternateType(QAPISchemaType): meta = 'alternate' - def __init__(self, name, info, doc, ifcond, features, variants): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants, + ): super().__init__(name, info, doc, ifcond, features) assert isinstance(variants, QAPISchemaVariants) assert variants.tag_member @@ -559,7 +672,7 @@ class QAPISchemaAlternateType(QAPISchemaType): variants.tag_member.set_defined_in(self.name) self.variants = variants - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) self.variants.tag_member.check(schema) # Not calling self.variants.check_clash(), because there's nothing @@ -567,8 +680,8 @@ class QAPISchemaAlternateType(QAPISchemaType): self.variants.check(schema, {}) # Alternate branch names have no relation to the tag enum values; # so we have to check for potential name collisions ourselves. - seen = {} - types_seen = {} + seen: Dict[str, QAPISchemaMember] = {} + types_seen: Dict[str, str] = {} for v in self.variants.variants: v.check_clash(self.info, seen) qtype = v.type.alternate_qtype() @@ -597,26 +710,32 @@ class QAPISchemaAlternateType(QAPISchemaType): % (v.describe(self.info), types_seen[qt])) types_seen[qt] = v.name - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc for v in self.variants.variants: v.connect_doc(doc) - def c_type(self): + def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX - def json_type(self): + def json_type(self) -> str: return 'value' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_alternate_type( self.name, self.info, self.ifcond, self.features, self.variants) class QAPISchemaVariants: - def __init__(self, tag_name, info, tag_member, variants): + def __init__( + self, + tag_name: Optional[str], + info: QAPISourceInfo, + tag_member: Optional[QAPISchemaObjectTypeMember], + variants: List[QAPISchemaVariant], + ): # Unions pass tag_name but not tag_member. # Alternates pass tag_member but not tag_name. # After check(), tag_member is always set. @@ -627,11 +746,11 @@ class QAPISchemaVariants: assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info - self._tag_member: Optional[QAPISchemaObjectTypeMember] = tag_member + self._tag_member = tag_member self.variants = variants @property - def tag_member(self) -> 'QAPISchemaObjectTypeMember': + def tag_member(self) -> QAPISchemaObjectTypeMember: if self._tag_member is None: raise RuntimeError( "QAPISchemaVariants has no tag_member property until " @@ -639,11 +758,13 @@ class QAPISchemaVariants: ) return self._tag_member - def set_defined_in(self, name): + def set_defined_in(self, name: str) -> None: for v in self.variants: v.set_defined_in(name) - def check(self, schema, seen): + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: if self._tag_name: # union # We need to narrow the member type: tmp = seen.get(c_name(self._tag_name)) @@ -713,7 +834,11 @@ class QAPISchemaVariants: % (v.describe(self.info), v.type.describe())) v.type.check(schema) - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: for v in self.variants: # Reset seen map for each variant, since qapi names from one # branch do not affect another branch. @@ -727,18 +852,27 @@ class QAPISchemaMember: """ Represents object members, enum members and features """ role = 'member' - def __init__(self, name, info, ifcond=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: Optional[QAPISchemaIfCond] = None, + ): assert isinstance(name, str) self.name = name self.info = info self.ifcond = ifcond or QAPISchemaIfCond() - self.defined_in = None + self.defined_in: Optional[str] = None - def set_defined_in(self, name): + def set_defined_in(self, name: str) -> None: assert not self.defined_in self.defined_in = name - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: cname = c_name(self.name) if cname in seen: raise QAPISemError( @@ -747,11 +881,11 @@ class QAPISchemaMember: % (self.describe(info), seen[cname].describe(info))) seen[cname] = self - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: if doc: doc.connect_member(self) - def describe(self, info): + def describe(self, info: Optional[QAPISourceInfo]) -> str: role = self.role meta = 'type' defined_in = self.defined_in @@ -783,14 +917,20 @@ class QAPISchemaMember: class QAPISchemaEnumMember(QAPISchemaMember): role = 'value' - def __init__(self, name, info, ifcond=None, features=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): super().__init__(name, info, ifcond) for f in features or []: assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.features = features or [] - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: super().connect_doc(doc) if doc: for f in self.features: @@ -800,12 +940,20 @@ class QAPISchemaEnumMember(QAPISchemaMember): class QAPISchemaFeature(QAPISchemaMember): role = 'feature' - def is_special(self): + def is_special(self) -> bool: return self.name in ('deprecated', 'unstable') class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None, features=None): + def __init__( + self, + name: str, + info: QAPISourceInfo, + typ: str, + optional: bool, + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): super().__init__(name, info, ifcond) assert isinstance(typ, str) assert isinstance(optional, bool) @@ -817,19 +965,19 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): self.optional = optional self.features = features or [] - def need_has(self): + def need_has(self) -> bool: assert self.type return self.optional and self.type.need_has_if_optional() - def check(self, schema): + def check(self, schema: QAPISchema) -> None: assert self.defined_in self.type = schema.resolve_type(self._type_name, self.info, self.describe) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for f in self.features: f.check_clash(self.info, seen) - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: super().connect_doc(doc) if doc: for f in self.features: @@ -839,24 +987,42 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): class QAPISchemaVariant(QAPISchemaObjectTypeMember): role = 'branch' - def __init__(self, name, info, typ, ifcond=None): + def __init__( + self, + name: str, + info: QAPISourceInfo, + typ: str, + ifcond: QAPISchemaIfCond, + ): super().__init__(name, info, typ, False, ifcond) class QAPISchemaCommand(QAPISchemaDefinition): meta = 'command' - def __init__(self, name, info, doc, ifcond, features, - arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - coroutine): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[str], + ret_type: Optional[str], + gen: bool, + success_response: bool, + boxed: bool, + allow_oob: bool, + allow_preconfig: bool, + coroutine: bool, + ): super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type - self.arg_type = None + self.arg_type: Optional[QAPISchemaObjectType] = None self._ret_type_name = ret_type - self.ret_type = None + self.ret_type: Optional[QAPISchemaType] = None self.gen = gen self.success_response = success_response self.boxed = boxed @@ -864,7 +1030,7 @@ class QAPISchemaCommand(QAPISchemaDefinition): self.allow_preconfig = allow_preconfig self.coroutine = coroutine - def check(self, schema): + def check(self, schema: QAPISchema) -> None: assert self.info is not None super().check(schema) if self._arg_type_name: @@ -900,14 +1066,14 @@ class QAPISchemaCommand(QAPISchemaDefinition): "command's 'returns' cannot take %s" % self.ret_type.describe()) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_command( self.name, self.info, self.ifcond, self.features, @@ -919,14 +1085,23 @@ class QAPISchemaCommand(QAPISchemaDefinition): class QAPISchemaEvent(QAPISchemaDefinition): meta = 'event' - def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[str], + boxed: bool, + ): super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type - self.arg_type = None + self.arg_type: Optional[QAPISchemaObjectType] = None self.boxed = boxed - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) if self._arg_type_name: typ = schema.resolve_type( @@ -948,14 +1123,14 @@ class QAPISchemaEvent(QAPISchemaDefinition): self.info, "conditional event arguments require 'boxed': true") - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_event( self.name, self.info, self.ifcond, self.features, @@ -963,7 +1138,7 @@ class QAPISchemaEvent(QAPISchemaDefinition): class QAPISchema: - def __init__(self, fname): + def __init__(self, fname: str): self.fname = fname try: @@ -975,9 +1150,9 @@ class QAPISchema: exprs = check_exprs(parser.exprs) self.docs = parser.docs - self._entity_list = [] - self._entity_dict = {} - self._module_dict = OrderedDict() + self._entity_list: List[QAPISchemaEntity] = [] + self._entity_dict: Dict[str, QAPISchemaDefinition] = {} + self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() self._schema_dir = os.path.dirname(fname) self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) self._make_module(fname) @@ -987,10 +1162,10 @@ class QAPISchema: self._def_exprs(exprs) self.check() - def _def_entity(self, ent): + def _def_entity(self, ent: QAPISchemaEntity) -> None: self._entity_list.append(ent) - def _def_definition(self, defn): + def _def_definition(self, defn: QAPISchemaDefinition) -> None: # Only the predefined types are allowed to not have info assert defn.info or self._predefining self._def_entity(defn) @@ -1007,18 +1182,27 @@ class QAPISchema: defn.info, "%s is already defined" % other_defn.describe()) self._entity_dict[defn.name] = defn - def lookup_entity(self, name, typ=None): + def lookup_entity( + self, + name: str, + typ: Optional[type] = None, + ) -> Optional[QAPISchemaDefinition]: ent = self._entity_dict.get(name) if typ and not isinstance(ent, typ): return None return ent - def lookup_type(self, name): + def lookup_type(self, name: str) -> Optional[QAPISchemaType]: typ = self.lookup_entity(name, QAPISchemaType) assert typ is None or isinstance(typ, QAPISchemaType) return typ - def resolve_type(self, name, info, what): + def resolve_type( + self, + name: str, + info: Optional[QAPISourceInfo], + what: Union[None, str, Callable[[QAPISourceInfo], str]], + ) -> QAPISchemaType: typ = self.lookup_type(name) if not typ: assert info and what # built-in types must not fail lookup @@ -1033,23 +1217,25 @@ class QAPISchema: return fname return os.path.relpath(fname, self._schema_dir) - def _make_module(self, fname): + def _make_module(self, fname: str) -> QAPISchemaModule: name = self._module_name(fname) if name not in self._module_dict: self._module_dict[name] = QAPISchemaModule(name) return self._module_dict[name] - def module_by_fname(self, fname): + def module_by_fname(self, fname: str) -> QAPISchemaModule: name = self._module_name(fname) return self._module_dict[name] - def _def_include(self, expr: QAPIExpression): + def _def_include(self, expr: QAPIExpression) -> None: include = expr['include'] assert expr.doc is None self._def_entity( QAPISchemaInclude(self._make_module(include), expr.info)) - def _def_builtin_type(self, name, json_type, c_type): + def _def_builtin_type( + self, name: str, json_type: str, c_type: str + ) -> None: self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type)) # Instantiating only the arrays that are actually used would # be nice, but we can't as long as their generated code @@ -1057,7 +1243,7 @@ class QAPISchema: # schema. self._make_array_type(name, None) - def _def_predefineds(self): + def _def_predefineds(self) -> None: for t in [('str', 'string', 'char' + POINTER_SUFFIX), ('number', 'number', 'double'), ('int', 'int', 'int64_t'), @@ -1086,31 +1272,52 @@ class QAPISchema: self._def_definition(QAPISchemaEnumType( 'QType', None, None, None, None, qtype_values, 'QTYPE')) - def _make_features(self, features, info): + def _make_features( + self, + features: Optional[List[Dict[str, Any]]], + info: Optional[QAPISourceInfo], + ) -> List[QAPISchemaFeature]: if features is None: return [] return [QAPISchemaFeature(f['name'], info, QAPISchemaIfCond(f.get('if'))) for f in features] - def _make_enum_member(self, name, ifcond, features, info): + def _make_enum_member( + self, + name: str, + ifcond: Optional[Union[str, Dict[str, Any]]], + features: Optional[List[Dict[str, Any]]], + info: Optional[QAPISourceInfo], + ) -> QAPISchemaEnumMember: return QAPISchemaEnumMember(name, info, QAPISchemaIfCond(ifcond), self._make_features(features, info)) - def _make_enum_members(self, values, info): + def _make_enum_members( + self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo] + ) -> List[QAPISchemaEnumMember]: return [self._make_enum_member(v['name'], v.get('if'), v.get('features'), info) for v in values] - def _make_array_type(self, element_type, info): + def _make_array_type( + self, element_type: str, info: Optional[QAPISourceInfo] + ) -> str: name = element_type + 'List' # reserved by check_defn_name_str() if not self.lookup_type(name): self._def_definition(QAPISchemaArrayType( name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, ifcond, role, members): + def _make_implicit_object_type( + self, + name: str, + info: QAPISourceInfo, + ifcond: QAPISchemaIfCond, + role: str, + members: List[QAPISchemaObjectTypeMember], + ) -> Optional[str]: if not members: return None # See also QAPISchemaObjectTypeMember.describe() @@ -1126,7 +1333,7 @@ class QAPISchema: name, info, None, ifcond, None, None, members, None)) return name - def _def_enum_type(self, expr: QAPIExpression): + def _def_enum_type(self, expr: QAPIExpression) -> None: name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') @@ -1137,7 +1344,14 @@ class QAPISchema: name, info, expr.doc, ifcond, features, self._make_enum_members(data, info), prefix)) - def _make_member(self, name, typ, ifcond, features, info): + def _make_member( + self, + name: str, + typ: Union[List[str], str], + ifcond: QAPISchemaIfCond, + features: Optional[List[Dict[str, Any]]], + info: QAPISourceInfo, + ) -> QAPISchemaObjectTypeMember: optional = False if name.startswith('*'): name = name[1:] @@ -1148,13 +1362,17 @@ class QAPISchema: return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, self._make_features(features, info)) - def _make_members(self, data, info): + def _make_members( + self, + data: Dict[str, Any], + info: QAPISourceInfo, + ) -> List[QAPISchemaObjectTypeMember]: return [self._make_member(key, value['type'], QAPISchemaIfCond(value.get('if')), value.get('features'), info) for (key, value) in data.items()] - def _def_struct_type(self, expr: QAPIExpression): + def _def_struct_type(self, expr: QAPIExpression) -> None: name = expr['struct'] base = expr.get('base') data = expr['data'] @@ -1166,13 +1384,19 @@ class QAPISchema: self._make_members(data, info), None)) - def _make_variant(self, case, typ, ifcond, info): + def _make_variant( + self, + case: str, + typ: str, + ifcond: QAPISchemaIfCond, + info: QAPISourceInfo, + ) -> QAPISchemaVariant: if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) return QAPISchemaVariant(case, info, typ, ifcond) - def _def_union_type(self, expr: QAPIExpression): + def _def_union_type(self, expr: QAPIExpression) -> None: name = expr['union'] base = expr['base'] tag_name = expr['discriminator'] @@ -1197,7 +1421,7 @@ class QAPISchema: QAPISchemaVariants( tag_name, info, None, variants))) - def _def_alternate_type(self, expr: QAPIExpression): + def _def_alternate_type(self, expr: QAPIExpression) -> None: name = expr['alternate'] data = expr['data'] assert isinstance(data, dict) @@ -1215,7 +1439,7 @@ class QAPISchema: name, info, expr.doc, ifcond, features, QAPISchemaVariants(None, info, tag_member, variants))) - def _def_command(self, expr: QAPIExpression): + def _def_command(self, expr: QAPIExpression) -> None: name = expr['command'] data = expr.get('data') rets = expr.get('returns') @@ -1240,7 +1464,7 @@ class QAPISchema: rets, gen, success_response, boxed, allow_oob, allow_preconfig, coroutine)) - def _def_event(self, expr: QAPIExpression): + def _def_event(self, expr: QAPIExpression) -> None: name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) @@ -1254,7 +1478,7 @@ class QAPISchema: self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond, features, data, boxed)) - def _def_exprs(self, exprs): + def _def_exprs(self, exprs: List[QAPIExpression]) -> None: for expr in exprs: if 'enum' in expr: self._def_enum_type(expr) @@ -1273,7 +1497,7 @@ class QAPISchema: else: assert False - def check(self): + def check(self) -> None: for ent in self._entity_list: ent.check(self) ent.connect_doc() @@ -1282,7 +1506,7 @@ class QAPISchema: for doc in self.docs: doc.check() - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_begin(self) for mod in self._module_dict.values(): mod.visit(visitor) From aa1fed9f54e11ae3e923bc12affbbcb31b997c76 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:58 +0100 Subject: [PATCH 0122/2884] qapi/schema: turn on mypy strictness This patch can be rolled in with the previous one once the series is ready for merge, but for work-in-progress' sake, it's separate here. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-23-armbru@redhat.com> --- scripts/qapi/mypy.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index 56e0dfb132..8109470a03 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -2,8 +2,3 @@ strict = True disallow_untyped_calls = False python_version = 3.8 - -[mypy-qapi.schema] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False From 8d413dbd5d369edd4b16e341dafbdde843397660 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 15 Mar 2024 16:22:59 +0100 Subject: [PATCH 0123/2884] qapi/schema: remove unnecessary asserts With strict typing enabled, these runtime statements aren't necessary anymore; we can prove them statically. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-24-armbru@redhat.com> --- scripts/qapi/schema.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index ef4d6a7def..e52930a48a 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -123,10 +123,8 @@ class QAPISchemaDefinition(QAPISchemaEntity): ifcond: Optional[QAPISchemaIfCond] = None, features: Optional[List[QAPISchemaFeature]] = None, ): - assert isinstance(name, str) super().__init__(info) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.name = name self.doc = doc @@ -163,7 +161,6 @@ class QAPISchemaDefinition(QAPISchemaEntity): return not self.info def describe(self) -> str: - assert self.meta return "%s '%s'" % (self.meta, self.name) @@ -377,7 +374,6 @@ class QAPISchemaType(QAPISchemaDefinition, ABC): f"feature '{feat.name}' is not supported for types") def describe(self) -> str: - assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -386,7 +382,6 @@ class QAPISchemaBuiltinType(QAPISchemaType): def __init__(self, name: str, json_type: str, c_type: str): super().__init__(name, None, None) - assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', 'value') self._json_type_name = json_type @@ -429,9 +424,7 @@ class QAPISchemaEnumType(QAPISchemaType): ): super().__init__(name, info, doc, ifcond, features) for m in members: - assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) - assert prefix is None or isinstance(prefix, str) self.members = members self.prefix = prefix @@ -474,7 +467,6 @@ class QAPISchemaArrayType(QAPISchemaType): self, name: str, info: Optional[QAPISourceInfo], element_type: str ): super().__init__(name, info, None) - assert isinstance(element_type, str) self._element_type_name = element_type self.element_type: QAPISchemaType @@ -519,7 +511,6 @@ class QAPISchemaArrayType(QAPISchemaType): self.element_type) def describe(self) -> str: - assert self.meta return "%s type ['%s']" % (self.meta, self._element_type_name) @@ -539,12 +530,9 @@ class QAPISchemaObjectType(QAPISchemaType): # union has base, variants, and no local_members super().__init__(name, info, doc, ifcond, features) self.meta = 'union' if variants else 'struct' - assert base is None or isinstance(base, str) for m in local_members: - assert isinstance(m, QAPISchemaObjectTypeMember) m.set_defined_in(name) if variants is not None: - assert isinstance(variants, QAPISchemaVariants) variants.set_defined_in(name) self._base_name = base self.base = None @@ -666,7 +654,6 @@ class QAPISchemaAlternateType(QAPISchemaType): variants: QAPISchemaVariants, ): super().__init__(name, info, doc, ifcond, features) - assert isinstance(variants, QAPISchemaVariants) assert variants.tag_member variants.set_defined_in(name) variants.tag_member.set_defined_in(self.name) @@ -742,8 +729,6 @@ class QAPISchemaVariants: assert bool(tag_member) != bool(tag_name) assert (isinstance(tag_name, str) or isinstance(tag_member, QAPISchemaObjectTypeMember)) - for v in variants: - assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info self._tag_member = tag_member @@ -858,7 +843,6 @@ class QAPISchemaMember: info: Optional[QAPISourceInfo], ifcond: Optional[QAPISchemaIfCond] = None, ): - assert isinstance(name, str) self.name = name self.info = info self.ifcond = ifcond or QAPISchemaIfCond() @@ -926,7 +910,6 @@ class QAPISchemaEnumMember(QAPISchemaMember): ): super().__init__(name, info, ifcond) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.features = features or [] @@ -955,10 +938,7 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): features: Optional[List[QAPISchemaFeature]] = None, ): super().__init__(name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self._type_name = typ self.type: QAPISchemaType # set during check() @@ -966,7 +946,6 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): self.features = features or [] def need_has(self) -> bool: - assert self.type return self.optional and self.type.need_has_if_optional() def check(self, schema: QAPISchema) -> None: @@ -1017,8 +996,6 @@ class QAPISchemaCommand(QAPISchemaDefinition): coroutine: bool, ): super().__init__(name, info, doc, ifcond, features) - assert not arg_type or isinstance(arg_type, str) - assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type self.arg_type: Optional[QAPISchemaObjectType] = None self._ret_type_name = ret_type @@ -1058,7 +1035,6 @@ class QAPISchemaCommand(QAPISchemaDefinition): if self.name not in self.info.pragma.command_returns_exceptions: typ = self.ret_type if isinstance(typ, QAPISchemaArrayType): - assert typ typ = typ.element_type if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( @@ -1096,7 +1072,6 @@ class QAPISchemaEvent(QAPISchemaDefinition): boxed: bool, ): super().__init__(name, info, doc, ifcond, features) - assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type: Optional[QAPISchemaObjectType] = None self.boxed = boxed From 99e75d8c2a9fd13e8a2c0eb0718359b06445d38b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 16:23:00 +0100 Subject: [PATCH 0124/2884] qapi: Tighten check whether implicit object type already exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Entities with names starting with q_obj_ are implicit object types. Therefore, QAPISchema._make_implicit_object_type()'s .lookup_entity() can only return a QAPISchemaObjectType. Assert that. Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-25-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: John Snow --- scripts/qapi/schema.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index e52930a48a..a6180f93c6 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1297,8 +1297,9 @@ class QAPISchema: return None # See also QAPISchemaObjectTypeMember.describe() name = 'q_obj_%s-%s' % (name, role) - typ = self.lookup_entity(name, QAPISchemaObjectType) + typ = self.lookup_entity(name) if typ: + assert(isinstance(typ, QAPISchemaObjectType)) # The implicit object type has multiple users. This can # only be a duplicate definition, which will be flagged # later. From 060b5a9323e5f49c5edcb77c42e231065bbcc42e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 16:23:01 +0100 Subject: [PATCH 0125/2884] qapi: Dumb down QAPISchema.lookup_entity() QAPISchema.lookup_entity() takes an optional type argument, a subtype of QAPISchemaDefinition, and returns that type or None. Callers can use this to save themselves an isinstance() test. The only remaining user of this convenience feature is .lookup_type(). But we don't actually save anything anymore there: we still need the isinstance() to help mypy over the hump. Drop the .lookup_entity() argument, and adjust .lookup_type(). Signed-off-by: Markus Armbruster Message-ID: <20240315152301.3621858-26-armbru@redhat.com> Reviewed-by: John Snow [Commit message typo fixed] --- scripts/qapi/schema.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index a6180f93c6..5924947fc3 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1157,20 +1157,14 @@ class QAPISchema: defn.info, "%s is already defined" % other_defn.describe()) self._entity_dict[defn.name] = defn - def lookup_entity( - self, - name: str, - typ: Optional[type] = None, - ) -> Optional[QAPISchemaDefinition]: - ent = self._entity_dict.get(name) - if typ and not isinstance(ent, typ): - return None - return ent + def lookup_entity(self,name: str) -> Optional[QAPISchemaEntity]: + return self._entity_dict.get(name) def lookup_type(self, name: str) -> Optional[QAPISchemaType]: - typ = self.lookup_entity(name, QAPISchemaType) - assert typ is None or isinstance(typ, QAPISchemaType) - return typ + typ = self.lookup_entity(name) + if isinstance(typ, QAPISchemaType): + return typ + return None def resolve_type( self, From 92360d6e624404492afe5d32ca669a33df181742 Mon Sep 17 00:00:00 2001 From: Sriram Yagnaraman Date: Wed, 28 Feb 2024 09:06:25 +0100 Subject: [PATCH 0126/2884] MAINTAINERS: Update Sriram Yagnaraman mail address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to company policies, I have changed my mail address. Updating MAINTAINERS and .mailmap to show my latest mail address. Signed-off-by: Sriram Yagnaraman Message-ID: <20240228080625.2412372-1-sriram.yagnaraman@ericsson.com> Signed-off-by: Philippe Mathieu-Daudé --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 88fb68143e..ef1b8a53f4 100644 --- a/.mailmap +++ b/.mailmap @@ -100,6 +100,7 @@ Philippe Mathieu-Daudé Philippe Mathieu-Daudé Philippe Mathieu-Daudé Roman Bolshakov +Sriram Yagnaraman Stefan Brankovic Stefan Weil Stefan Weil Taylor Simpson diff --git a/MAINTAINERS b/MAINTAINERS index f1f6922025..3a49b1433c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2478,7 +2478,7 @@ F: tests/qtest/libqos/e1000e.* igb M: Akihiko Odaki -R: Sriram Yagnaraman +R: Sriram Yagnaraman S: Maintained F: docs/system/devices/igb.rst F: hw/net/igb* From 6c3014858c4c0024dd0560f08a6eda0f92f658d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Mar 2024 12:10:58 +0100 Subject: [PATCH 0127/2884] target/nios2: Remove the deprecated Nios II target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Nios II target is deprecated since v8.2 in commit 9997771bc1 ("target/nios2: Deprecate the Nios II architecture"). Remove: - Buildsys / CI infra - User emulation - System emulation (10m50-ghrd & nios2-generic-nommu machines) - Tests Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Marek Vasut Message-Id: <20240327144806.11319-3-philmd@linaro.org> --- .gitlab-ci.d/buildtest.yml | 4 +- .gitlab-ci.d/crossbuild-template.yml | 2 +- .gitlab-ci.d/crossbuilds.yml | 2 +- MAINTAINERS | 13 - configs/devices/nios2-softmmu/default.mak | 6 - configs/targets/nios2-linux-user.mak | 1 - configs/targets/nios2-softmmu.mak | 2 - configure | 2 - disas/meson.build | 1 - disas/nios2.c | 3514 ----------------- docs/about/deprecated.rst | 11 - docs/about/emulation.rst | 7 - docs/about/removed-features.rst | 17 + docs/system/replay.rst | 2 +- docs/user/main.rst | 4 - fpu/softfloat-specialize.c.inc | 2 +- hw/Kconfig | 1 - hw/intc/Kconfig | 3 - hw/intc/meson.build | 1 - hw/intc/nios2_vic.c | 313 -- hw/meson.build | 1 - hw/nios2/10m50_devboard.c | 181 - hw/nios2/Kconfig | 13 - hw/nios2/boot.c | 234 -- hw/nios2/boot.h | 10 - hw/nios2/generic_nommu.c | 101 - hw/nios2/meson.build | 6 - include/disas/dis-asm.h | 5 - include/exec/poison.h | 2 - include/exec/user/abitypes.h | 3 +- include/hw/intc/nios2_vic.h | 66 - include/sysemu/arch_init.h | 1 - linux-user/elfload.c | 99 - linux-user/nios2/cpu_loop.c | 157 - linux-user/nios2/signal.c | 210 - linux-user/nios2/sockbits.h | 1 - linux-user/nios2/syscall_nr.h | 333 -- linux-user/nios2/target_cpu.h | 49 - linux-user/nios2/target_elf.h | 14 - linux-user/nios2/target_errno_defs.h | 7 - linux-user/nios2/target_fcntl.h | 11 - linux-user/nios2/target_mman.h | 11 - linux-user/nios2/target_prctl.h | 1 - linux-user/nios2/target_proc.h | 1 - linux-user/nios2/target_resource.h | 1 - linux-user/nios2/target_signal.h | 9 - linux-user/nios2/target_structs.h | 1 - linux-user/nios2/target_syscall.h | 37 - linux-user/nios2/termbits.h | 1 - linux-user/syscall_defs.h | 4 +- meson.build | 2 - qapi/machine.json | 2 +- qemu-options.hx | 8 +- scripts/coverity-scan/COMPONENTS.md | 3 - scripts/gensyscalls.sh | 1 - scripts/probe-gdb-support.py | 1 - target/Kconfig | 1 - target/meson.build | 1 - target/nios2/Kconfig | 3 - target/nios2/cpu-param.h | 20 - target/nios2/cpu-qom.h | 18 - target/nios2/cpu.c | 410 -- target/nios2/cpu.h | 301 -- target/nios2/helper.c | 371 -- target/nios2/helper.h | 32 - target/nios2/meson.build | 17 - target/nios2/mmu.c | 216 - target/nios2/mmu.h | 52 - target/nios2/monitor.c | 35 - target/nios2/nios2-semi.c | 230 -- target/nios2/op_helper.c | 119 - target/nios2/trace-events | 10 - target/nios2/translate.c | 1107 ------ tests/avocado/boot_linux_console.py | 8 - tests/avocado/replay_kernel.py | 11 - tests/docker/Makefile.include | 5 - .../debian-nios2-cross.d/build-toolchain.sh | 87 - tests/qtest/machine-none-test.c | 1 - tests/tcg/nios2/10m50-ghrd.ld | 70 - tests/tcg/nios2/Makefile.softmmu-target | 32 - tests/tcg/nios2/Makefile.target | 11 - tests/tcg/nios2/boot.S | 218 - tests/tcg/nios2/intr.S | 31 - tests/tcg/nios2/semicall.h | 28 - tests/tcg/nios2/test-shadow-1.S | 40 - 85 files changed, 31 insertions(+), 8949 deletions(-) delete mode 100644 configs/devices/nios2-softmmu/default.mak delete mode 100644 configs/targets/nios2-linux-user.mak delete mode 100644 configs/targets/nios2-softmmu.mak delete mode 100644 disas/nios2.c delete mode 100644 hw/intc/nios2_vic.c delete mode 100644 hw/nios2/10m50_devboard.c delete mode 100644 hw/nios2/Kconfig delete mode 100644 hw/nios2/boot.c delete mode 100644 hw/nios2/boot.h delete mode 100644 hw/nios2/generic_nommu.c delete mode 100644 hw/nios2/meson.build delete mode 100644 include/hw/intc/nios2_vic.h delete mode 100644 linux-user/nios2/cpu_loop.c delete mode 100644 linux-user/nios2/signal.c delete mode 100644 linux-user/nios2/sockbits.h delete mode 100644 linux-user/nios2/syscall_nr.h delete mode 100644 linux-user/nios2/target_cpu.h delete mode 100644 linux-user/nios2/target_elf.h delete mode 100644 linux-user/nios2/target_errno_defs.h delete mode 100644 linux-user/nios2/target_fcntl.h delete mode 100644 linux-user/nios2/target_mman.h delete mode 100644 linux-user/nios2/target_prctl.h delete mode 100644 linux-user/nios2/target_proc.h delete mode 100644 linux-user/nios2/target_resource.h delete mode 100644 linux-user/nios2/target_signal.h delete mode 100644 linux-user/nios2/target_structs.h delete mode 100644 linux-user/nios2/target_syscall.h delete mode 100644 linux-user/nios2/termbits.h delete mode 100644 target/nios2/Kconfig delete mode 100644 target/nios2/cpu-param.h delete mode 100644 target/nios2/cpu-qom.h delete mode 100644 target/nios2/cpu.c delete mode 100644 target/nios2/cpu.h delete mode 100644 target/nios2/helper.c delete mode 100644 target/nios2/helper.h delete mode 100644 target/nios2/meson.build delete mode 100644 target/nios2/mmu.c delete mode 100644 target/nios2/mmu.h delete mode 100644 target/nios2/monitor.c delete mode 100644 target/nios2/nios2-semi.c delete mode 100644 target/nios2/op_helper.c delete mode 100644 target/nios2/trace-events delete mode 100644 target/nios2/translate.c delete mode 100755 tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh delete mode 100644 tests/tcg/nios2/10m50-ghrd.ld delete mode 100644 tests/tcg/nios2/Makefile.softmmu-target delete mode 100644 tests/tcg/nios2/Makefile.target delete mode 100644 tests/tcg/nios2/boot.S delete mode 100644 tests/tcg/nios2/intr.S delete mode 100644 tests/tcg/nios2/semicall.h delete mode 100644 tests/tcg/nios2/test-shadow-1.S diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index cfdff175c3..5848c73e00 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -164,7 +164,7 @@ build-system-centos: CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server --enable-modules --enable-trace-backends=dtrace --enable-docs TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu - x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu + x86_64-softmmu rx-softmmu sh4-softmmu MAKE_CHECK_ARGS: check-build # Previous QEMU release. Used for cross-version migration tests. @@ -254,7 +254,7 @@ avocado-system-centos: IMAGE: centos8 MAKE_CHECK_ARGS: check-avocado AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx - arch:sh4 arch:nios2 + arch:sh4 build-system-opensuse: extends: diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 3e5f4d9cd8..d9f81b7061 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -72,7 +72,7 @@ - ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS --disable-system --target-list-exclude="aarch64_be-linux-user alpha-linux-user cris-linux-user m68k-linux-user microblazeel-linux-user - nios2-linux-user or1k-linux-user ppc-linux-user sparc-linux-user + or1k-linux-user ppc-linux-user sparc-linux-user xtensa-linux-user $CROSS_SKIP_TARGETS" - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 987ba9694b..47bdb99b5b 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -167,7 +167,7 @@ cross-win64-system: IMAGE: fedora-win64-cross EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu - m68k-softmmu microblazeel-softmmu nios2-softmmu + m68k-softmmu microblazeel-softmmu or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu tricore-softmmu xtensaeb-softmmu artifacts: diff --git a/MAINTAINERS b/MAINTAINERS index 3a49b1433c..4ccba2f722 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -291,19 +291,6 @@ F: disas/*mips.c F: docs/system/cpu-models-mips.rst.inc F: tests/tcg/mips/ -NiosII TCG CPUs -R: Chris Wulff -R: Marek Vasut -S: Orphan -F: target/nios2/ -F: hw/nios2/ -F: hw/intc/nios2_vic.c -F: disas/nios2.c -F: include/hw/intc/nios2_vic.h -F: configs/devices/nios2-softmmu/default.mak -F: tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh -F: tests/tcg/nios2/ - OpenRISC TCG CPUs M: Stafford Horne S: Odd Fixes diff --git a/configs/devices/nios2-softmmu/default.mak b/configs/devices/nios2-softmmu/default.mak deleted file mode 100644 index e130d024e6..0000000000 --- a/configs/devices/nios2-softmmu/default.mak +++ /dev/null @@ -1,6 +0,0 @@ -# Default configuration for nios2-softmmu - -# Boards: -# -CONFIG_NIOS2_10M50=y -CONFIG_NIOS2_GENERIC_NOMMU=y diff --git a/configs/targets/nios2-linux-user.mak b/configs/targets/nios2-linux-user.mak deleted file mode 100644 index 9a372f0717..0000000000 --- a/configs/targets/nios2-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -TARGET_ARCH=nios2 diff --git a/configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak deleted file mode 100644 index c99ae3777e..0000000000 --- a/configs/targets/nios2-softmmu.mak +++ /dev/null @@ -1,2 +0,0 @@ -TARGET_ARCH=nios2 -TARGET_NEED_FDT=y diff --git a/configure b/configure index 3cd736b139..1dca3d94c0 100755 --- a/configure +++ b/configure @@ -1169,7 +1169,6 @@ fi : ${cross_prefix_mips64="mips64-linux-gnuabi64-"} : ${cross_prefix_mipsel="mipsel-linux-gnu-"} : ${cross_prefix_mips="mips-linux-gnu-"} -: ${cross_prefix_nios2="nios2-linux-gnu-"} : ${cross_prefix_ppc="powerpc-linux-gnu-"} : ${cross_prefix_ppc64="powerpc64-linux-gnu-"} : ${cross_prefix_ppc64le="$cross_prefix_ppc64"} @@ -1258,7 +1257,6 @@ probe_target_compiler() { mips64) container_hosts=x86_64 ;; mipsel) container_hosts=x86_64 ;; mips) container_hosts=x86_64 ;; - nios2) container_hosts=x86_64 ;; ppc) container_hosts=x86_64 ;; ppc64|ppc64le) container_hosts=x86_64 ;; riscv64) container_hosts=x86_64 ;; diff --git a/disas/meson.build b/disas/meson.build index 815523ab85..5c8073beb3 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -5,7 +5,6 @@ common_ss.add(when: 'CONFIG_HPPA_DIS', if_true: files('hppa.c')) common_ss.add(when: 'CONFIG_M68K_DIS', if_true: files('m68k.c')) common_ss.add(when: 'CONFIG_MICROBLAZE_DIS', if_true: files('microblaze.c')) common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c', 'nanomips.c')) -common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c')) common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files( 'riscv.c', 'riscv-xthead.c', diff --git a/disas/nios2.c b/disas/nios2.c deleted file mode 100644 index 98ac07d72e..0000000000 --- a/disas/nios2.c +++ /dev/null @@ -1,3514 +0,0 @@ -/* Nios II opcode library for QEMU. - Copyright (C) 2012-2016 Free Software Foundation, Inc. - Contributed by Nigel Gray (ngray@altera.com). - Contributed by Mentor Graphics, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/* This file resembles a concatenation of the following files from - binutils: - - include/opcode/nios2.h - include/opcode/nios2r1.h - include/opcode/nios2r2.h - opcodes/nios2-opc.c - opcodes/nios2-dis.c - - It has been derived from the original patches which have been - relicensed by the contributors as GPL version 2 for inclusion - in QEMU. */ - -#ifndef _NIOS2_H_ -#define _NIOS2_H_ - -/*#include "bfd.h"*/ -#include "qemu/osdep.h" -#include "disas/dis-asm.h" - - -/**************************************************************************** - * This file contains structures, bit masks and shift counts used - * by the GNU toolchain to define the Nios II instruction set and - * access various opcode fields. - ****************************************************************************/ - -/* Instruction encoding formats. */ -enum iw_format_type { - /* R1 formats. */ - iw_i_type, - iw_r_type, - iw_j_type, - iw_custom_type, - - /* 32-bit R2 formats. */ - iw_L26_type, - iw_F2I16_type, - iw_F2X4I12_type, - iw_F1X4I12_type, - iw_F1X4L17_type, - iw_F3X6L5_type, - iw_F2X6L10_type, - iw_F3X6_type, - iw_F3X8_type, - - /* 16-bit R2 formats. */ - iw_I10_type, - iw_T1I7_type, - iw_T2I4_type, - iw_T1X1I6_type, - iw_X1I7_type, - iw_L5I4X1_type, - iw_T2X1L3_type, - iw_T2X1I3_type, - iw_T3X1_type, - iw_T2X3_type, - iw_F1X1_type, - iw_X2L5_type, - iw_F1I5_type, - iw_F2_type -}; - -/* Identify different overflow situations for error messages. */ -enum overflow_type -{ - call_target_overflow = 0, - branch_target_overflow, - address_offset_overflow, - signed_immed16_overflow, - unsigned_immed16_overflow, - unsigned_immed5_overflow, - signed_immed12_overflow, - custom_opcode_overflow, - enumeration_overflow, - no_overflow -}; - -/* This structure holds information for a particular instruction. - - The args field is a string describing the operands. The following - letters can appear in the args: - c - a 5-bit control register index - d - a 5-bit destination register index - s - a 5-bit left source register index - t - a 5-bit right source register index - D - a 3-bit encoded destination register - S - a 3-bit encoded left source register - T - a 3-bit encoded right source register - i - a 16-bit signed immediate - j - a 5-bit unsigned immediate - k - a (second) 5-bit unsigned immediate - l - a 8-bit custom instruction constant - m - a 26-bit unsigned immediate - o - a 16-bit signed pc-relative offset - u - a 16-bit unsigned immediate - I - a 12-bit signed immediate - M - a 6-bit unsigned immediate - N - a 6-bit unsigned immediate with 2-bit shift - O - a 10-bit signed pc-relative offset with 1-bit shift - P - a 7-bit signed pc-relative offset with 1-bit shift - U - a 7-bit unsigned immediate with 2-bit shift - V - a 5-bit unsigned immediate with 2-bit shift - W - a 4-bit unsigned immediate with 2-bit shift - X - a 4-bit unsigned immediate with 1-bit shift - Y - a 4-bit unsigned immediate - e - an immediate coded as an enumeration for addi.n/subi.n - f - an immediate coded as an enumeration for slli.n/srli.n - g - an immediate coded as an enumeration for andi.n - h - an immediate coded as an enumeration for movi.n - R - a reglist for ldwm/stwm or push.n/pop.n - B - a base register specifier and option list for ldwm/stwm - Literal ',', '(', and ')' characters may also appear in the args as - delimiters. - - Note that the args describe the semantics and assembly-language syntax - of the operands, not their encoding into the instruction word. - - The pinfo field is INSN_MACRO for a macro. Otherwise, it is a collection - of bits describing the instruction, notably any relevant hazard - information. - - When assembling, the match field contains the opcode template, which - is modified by the arguments to produce the actual opcode - that is emitted. If pinfo is INSN_MACRO, then this is 0. - - If pinfo is INSN_MACRO, the mask field stores the macro identifier. - Otherwise this is a bit mask for the relevant portions of the opcode - when disassembling. If the actual opcode anded with the match field - equals the opcode field, then we have found the correct instruction. */ - -struct nios2_opcode -{ - const char *name; /* The name of the instruction. */ - const char *args; /* A string describing the arguments for this - instruction. */ - const char *args_test; /* Like args, but with an extra argument for - the expected opcode. */ - unsigned long num_args; /* The number of arguments the instruction - takes. */ - unsigned size; /* Size in bytes of the instruction. */ - enum iw_format_type format; /* Instruction format. */ - unsigned long match; /* The basic opcode for the instruction. */ - unsigned long mask; /* Mask for the opcode field of the - instruction. */ - unsigned long pinfo; /* Is this a real instruction or instruction - macro? */ - enum overflow_type overflow_msg; /* Used to generate informative - message when fixup overflows. */ -}; - -/* This value is used in the nios2_opcode.pinfo field to indicate that the - instruction is a macro or pseudo-op. This requires special treatment by - the assembler, and is used by the disassembler to determine whether to - check for a nop. */ -#define NIOS2_INSN_MACRO 0x80000000 -#define NIOS2_INSN_MACRO_MOV 0x80000001 -#define NIOS2_INSN_MACRO_MOVI 0x80000002 -#define NIOS2_INSN_MACRO_MOVIA 0x80000004 - -#define NIOS2_INSN_RELAXABLE 0x40000000 -#define NIOS2_INSN_UBRANCH 0x00000010 -#define NIOS2_INSN_CBRANCH 0x00000020 -#define NIOS2_INSN_CALL 0x00000040 - -#define NIOS2_INSN_OPTARG 0x00000080 - -/* Register attributes. */ -#define REG_NORMAL (1<<0) /* Normal registers. */ -#define REG_CONTROL (1<<1) /* Control registers. */ -#define REG_COPROCESSOR (1<<2) /* For custom instructions. */ -#define REG_3BIT (1<<3) /* For R2 CDX instructions. */ -#define REG_LDWM (1<<4) /* For R2 ldwm/stwm. */ -#define REG_POP (1<<5) /* For R2 pop.n/push.n. */ - -struct nios2_reg -{ - const char *name; - const int index; - unsigned long regtype; -}; - -/* Pull in the instruction field accessors, opcodes, and masks. */ -/*#include "nios2r1.h"*/ - -#ifndef _NIOS2R1_H_ -#define _NIOS2R1_H_ - -/* R1 fields. */ -#define IW_R1_OP_LSB 0 -#define IW_R1_OP_SIZE 6 -#define IW_R1_OP_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R1_OP_SIZE)) -#define IW_R1_OP_SHIFTED_MASK (IW_R1_OP_UNSHIFTED_MASK << IW_R1_OP_LSB) -#define GET_IW_R1_OP(W) (((W) >> IW_R1_OP_LSB) & IW_R1_OP_UNSHIFTED_MASK) -#define SET_IW_R1_OP(V) (((V) & IW_R1_OP_UNSHIFTED_MASK) << IW_R1_OP_LSB) - -#define IW_I_A_LSB 27 -#define IW_I_A_SIZE 5 -#define IW_I_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I_A_SIZE)) -#define IW_I_A_SHIFTED_MASK (IW_I_A_UNSHIFTED_MASK << IW_I_A_LSB) -#define GET_IW_I_A(W) (((W) >> IW_I_A_LSB) & IW_I_A_UNSHIFTED_MASK) -#define SET_IW_I_A(V) (((V) & IW_I_A_UNSHIFTED_MASK) << IW_I_A_LSB) - -#define IW_I_B_LSB 22 -#define IW_I_B_SIZE 5 -#define IW_I_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I_B_SIZE)) -#define IW_I_B_SHIFTED_MASK (IW_I_B_UNSHIFTED_MASK << IW_I_B_LSB) -#define GET_IW_I_B(W) (((W) >> IW_I_B_LSB) & IW_I_B_UNSHIFTED_MASK) -#define SET_IW_I_B(V) (((V) & IW_I_B_UNSHIFTED_MASK) << IW_I_B_LSB) - -#define IW_I_IMM16_LSB 6 -#define IW_I_IMM16_SIZE 16 -#define IW_I_IMM16_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I_IMM16_SIZE)) -#define IW_I_IMM16_SHIFTED_MASK (IW_I_IMM16_UNSHIFTED_MASK << IW_I_IMM16_LSB) -#define GET_IW_I_IMM16(W) (((W) >> IW_I_IMM16_LSB) & IW_I_IMM16_UNSHIFTED_MASK) -#define SET_IW_I_IMM16(V) (((V) & IW_I_IMM16_UNSHIFTED_MASK) << IW_I_IMM16_LSB) - -#define IW_R_A_LSB 27 -#define IW_R_A_SIZE 5 -#define IW_R_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_A_SIZE)) -#define IW_R_A_SHIFTED_MASK (IW_R_A_UNSHIFTED_MASK << IW_R_A_LSB) -#define GET_IW_R_A(W) (((W) >> IW_R_A_LSB) & IW_R_A_UNSHIFTED_MASK) -#define SET_IW_R_A(V) (((V) & IW_R_A_UNSHIFTED_MASK) << IW_R_A_LSB) - -#define IW_R_B_LSB 22 -#define IW_R_B_SIZE 5 -#define IW_R_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_B_SIZE)) -#define IW_R_B_SHIFTED_MASK (IW_R_B_UNSHIFTED_MASK << IW_R_B_LSB) -#define GET_IW_R_B(W) (((W) >> IW_R_B_LSB) & IW_R_B_UNSHIFTED_MASK) -#define SET_IW_R_B(V) (((V) & IW_R_B_UNSHIFTED_MASK) << IW_R_B_LSB) - -#define IW_R_C_LSB 17 -#define IW_R_C_SIZE 5 -#define IW_R_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_C_SIZE)) -#define IW_R_C_SHIFTED_MASK (IW_R_C_UNSHIFTED_MASK << IW_R_C_LSB) -#define GET_IW_R_C(W) (((W) >> IW_R_C_LSB) & IW_R_C_UNSHIFTED_MASK) -#define SET_IW_R_C(V) (((V) & IW_R_C_UNSHIFTED_MASK) << IW_R_C_LSB) - -#define IW_R_OPX_LSB 11 -#define IW_R_OPX_SIZE 6 -#define IW_R_OPX_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_OPX_SIZE)) -#define IW_R_OPX_SHIFTED_MASK (IW_R_OPX_UNSHIFTED_MASK << IW_R_OPX_LSB) -#define GET_IW_R_OPX(W) (((W) >> IW_R_OPX_LSB) & IW_R_OPX_UNSHIFTED_MASK) -#define SET_IW_R_OPX(V) (((V) & IW_R_OPX_UNSHIFTED_MASK) << IW_R_OPX_LSB) - -#define IW_R_IMM5_LSB 6 -#define IW_R_IMM5_SIZE 5 -#define IW_R_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_IMM5_SIZE)) -#define IW_R_IMM5_SHIFTED_MASK (IW_R_IMM5_UNSHIFTED_MASK << IW_R_IMM5_LSB) -#define GET_IW_R_IMM5(W) (((W) >> IW_R_IMM5_LSB) & IW_R_IMM5_UNSHIFTED_MASK) -#define SET_IW_R_IMM5(V) (((V) & IW_R_IMM5_UNSHIFTED_MASK) << IW_R_IMM5_LSB) - -#define IW_J_IMM26_LSB 6 -#define IW_J_IMM26_SIZE 26 -#define IW_J_IMM26_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_J_IMM26_SIZE)) -#define IW_J_IMM26_SHIFTED_MASK (IW_J_IMM26_UNSHIFTED_MASK << IW_J_IMM26_LSB) -#define GET_IW_J_IMM26(W) (((W) >> IW_J_IMM26_LSB) & IW_J_IMM26_UNSHIFTED_MASK) -#define SET_IW_J_IMM26(V) (((V) & IW_J_IMM26_UNSHIFTED_MASK) << IW_J_IMM26_LSB) - -#define IW_CUSTOM_A_LSB 27 -#define IW_CUSTOM_A_SIZE 5 -#define IW_CUSTOM_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_A_SIZE)) -#define IW_CUSTOM_A_SHIFTED_MASK (IW_CUSTOM_A_UNSHIFTED_MASK << IW_CUSTOM_A_LSB) -#define GET_IW_CUSTOM_A(W) (((W) >> IW_CUSTOM_A_LSB) & IW_CUSTOM_A_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_A(V) (((V) & IW_CUSTOM_A_UNSHIFTED_MASK) << IW_CUSTOM_A_LSB) - -#define IW_CUSTOM_B_LSB 22 -#define IW_CUSTOM_B_SIZE 5 -#define IW_CUSTOM_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_B_SIZE)) -#define IW_CUSTOM_B_SHIFTED_MASK (IW_CUSTOM_B_UNSHIFTED_MASK << IW_CUSTOM_B_LSB) -#define GET_IW_CUSTOM_B(W) (((W) >> IW_CUSTOM_B_LSB) & IW_CUSTOM_B_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_B(V) (((V) & IW_CUSTOM_B_UNSHIFTED_MASK) << IW_CUSTOM_B_LSB) - -#define IW_CUSTOM_C_LSB 17 -#define IW_CUSTOM_C_SIZE 5 -#define IW_CUSTOM_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_C_SIZE)) -#define IW_CUSTOM_C_SHIFTED_MASK (IW_CUSTOM_C_UNSHIFTED_MASK << IW_CUSTOM_C_LSB) -#define GET_IW_CUSTOM_C(W) (((W) >> IW_CUSTOM_C_LSB) & IW_CUSTOM_C_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_C(V) (((V) & IW_CUSTOM_C_UNSHIFTED_MASK) << IW_CUSTOM_C_LSB) - -#define IW_CUSTOM_READA_LSB 16 -#define IW_CUSTOM_READA_SIZE 1 -#define IW_CUSTOM_READA_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_READA_SIZE)) -#define IW_CUSTOM_READA_SHIFTED_MASK (IW_CUSTOM_READA_UNSHIFTED_MASK << IW_CUSTOM_READA_LSB) -#define GET_IW_CUSTOM_READA(W) (((W) >> IW_CUSTOM_READA_LSB) & IW_CUSTOM_READA_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_READA(V) (((V) & IW_CUSTOM_READA_UNSHIFTED_MASK) << IW_CUSTOM_READA_LSB) - -#define IW_CUSTOM_READB_LSB 15 -#define IW_CUSTOM_READB_SIZE 1 -#define IW_CUSTOM_READB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_READB_SIZE)) -#define IW_CUSTOM_READB_SHIFTED_MASK (IW_CUSTOM_READB_UNSHIFTED_MASK << IW_CUSTOM_READB_LSB) -#define GET_IW_CUSTOM_READB(W) (((W) >> IW_CUSTOM_READB_LSB) & IW_CUSTOM_READB_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_READB(V) (((V) & IW_CUSTOM_READB_UNSHIFTED_MASK) << IW_CUSTOM_READB_LSB) - -#define IW_CUSTOM_READC_LSB 14 -#define IW_CUSTOM_READC_SIZE 1 -#define IW_CUSTOM_READC_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_READC_SIZE)) -#define IW_CUSTOM_READC_SHIFTED_MASK (IW_CUSTOM_READC_UNSHIFTED_MASK << IW_CUSTOM_READC_LSB) -#define GET_IW_CUSTOM_READC(W) (((W) >> IW_CUSTOM_READC_LSB) & IW_CUSTOM_READC_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_READC(V) (((V) & IW_CUSTOM_READC_UNSHIFTED_MASK) << IW_CUSTOM_READC_LSB) - -#define IW_CUSTOM_N_LSB 6 -#define IW_CUSTOM_N_SIZE 8 -#define IW_CUSTOM_N_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_N_SIZE)) -#define IW_CUSTOM_N_SHIFTED_MASK (IW_CUSTOM_N_UNSHIFTED_MASK << IW_CUSTOM_N_LSB) -#define GET_IW_CUSTOM_N(W) (((W) >> IW_CUSTOM_N_LSB) & IW_CUSTOM_N_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_N(V) (((V) & IW_CUSTOM_N_UNSHIFTED_MASK) << IW_CUSTOM_N_LSB) - -/* R1 opcodes. */ -#define R1_OP_CALL 0 -#define R1_OP_JMPI 1 -#define R1_OP_LDBU 3 -#define R1_OP_ADDI 4 -#define R1_OP_STB 5 -#define R1_OP_BR 6 -#define R1_OP_LDB 7 -#define R1_OP_CMPGEI 8 -#define R1_OP_LDHU 11 -#define R1_OP_ANDI 12 -#define R1_OP_STH 13 -#define R1_OP_BGE 14 -#define R1_OP_LDH 15 -#define R1_OP_CMPLTI 16 -#define R1_OP_INITDA 19 -#define R1_OP_ORI 20 -#define R1_OP_STW 21 -#define R1_OP_BLT 22 -#define R1_OP_LDW 23 -#define R1_OP_CMPNEI 24 -#define R1_OP_FLUSHDA 27 -#define R1_OP_XORI 28 -#define R1_OP_BNE 30 -#define R1_OP_CMPEQI 32 -#define R1_OP_LDBUIO 35 -#define R1_OP_MULI 36 -#define R1_OP_STBIO 37 -#define R1_OP_BEQ 38 -#define R1_OP_LDBIO 39 -#define R1_OP_CMPGEUI 40 -#define R1_OP_LDHUIO 43 -#define R1_OP_ANDHI 44 -#define R1_OP_STHIO 45 -#define R1_OP_BGEU 46 -#define R1_OP_LDHIO 47 -#define R1_OP_CMPLTUI 48 -#define R1_OP_CUSTOM 50 -#define R1_OP_INITD 51 -#define R1_OP_ORHI 52 -#define R1_OP_STWIO 53 -#define R1_OP_BLTU 54 -#define R1_OP_LDWIO 55 -#define R1_OP_RDPRS 56 -#define R1_OP_OPX 58 -#define R1_OP_FLUSHD 59 -#define R1_OP_XORHI 60 - -#define R1_OPX_ERET 1 -#define R1_OPX_ROLI 2 -#define R1_OPX_ROL 3 -#define R1_OPX_FLUSHP 4 -#define R1_OPX_RET 5 -#define R1_OPX_NOR 6 -#define R1_OPX_MULXUU 7 -#define R1_OPX_CMPGE 8 -#define R1_OPX_BRET 9 -#define R1_OPX_ROR 11 -#define R1_OPX_FLUSHI 12 -#define R1_OPX_JMP 13 -#define R1_OPX_AND 14 -#define R1_OPX_CMPLT 16 -#define R1_OPX_SLLI 18 -#define R1_OPX_SLL 19 -#define R1_OPX_WRPRS 20 -#define R1_OPX_OR 22 -#define R1_OPX_MULXSU 23 -#define R1_OPX_CMPNE 24 -#define R1_OPX_SRLI 26 -#define R1_OPX_SRL 27 -#define R1_OPX_NEXTPC 28 -#define R1_OPX_CALLR 29 -#define R1_OPX_XOR 30 -#define R1_OPX_MULXSS 31 -#define R1_OPX_CMPEQ 32 -#define R1_OPX_DIVU 36 -#define R1_OPX_DIV 37 -#define R1_OPX_RDCTL 38 -#define R1_OPX_MUL 39 -#define R1_OPX_CMPGEU 40 -#define R1_OPX_INITI 41 -#define R1_OPX_TRAP 45 -#define R1_OPX_WRCTL 46 -#define R1_OPX_CMPLTU 48 -#define R1_OPX_ADD 49 -#define R1_OPX_BREAK 52 -#define R1_OPX_SYNC 54 -#define R1_OPX_SUB 57 -#define R1_OPX_SRAI 58 -#define R1_OPX_SRA 59 - -/* Some convenience macros for R1 encodings, for use in instruction tables. - MATCH_R1_OPX0(NAME) and MASK_R1_OPX0 are used for R-type instructions - with 3 register operands and constant 0 in the immediate field. - The general forms are MATCH_R1_OPX(NAME, A, B, C) where the arguments specify - constant values and MASK_R1_OPX(A, B, C, N) where the arguments are booleans - that are true if the field should be included in the mask. - */ -#define MATCH_R1_OP(NAME) \ - (SET_IW_R1_OP (R1_OP_##NAME)) -#define MASK_R1_OP \ - IW_R1_OP_SHIFTED_MASK - -#define MATCH_R1_OPX0(NAME) \ - (SET_IW_R1_OP (R1_OP_OPX) | SET_IW_R_OPX (R1_OPX_##NAME)) -#define MASK_R1_OPX0 \ - (IW_R1_OP_SHIFTED_MASK | IW_R_OPX_SHIFTED_MASK | IW_R_IMM5_SHIFTED_MASK) - -#define MATCH_R1_OPX(NAME, A, B, C) \ - (MATCH_R1_OPX0 (NAME) | SET_IW_R_A (A) | SET_IW_R_B (B) | SET_IW_R_C (C)) -#define MASK_R1_OPX(A, B, C, N) \ - (IW_R1_OP_SHIFTED_MASK | IW_R_OPX_SHIFTED_MASK \ - | (A ? IW_R_A_SHIFTED_MASK : 0) \ - | (B ? IW_R_B_SHIFTED_MASK : 0) \ - | (C ? IW_R_C_SHIFTED_MASK : 0) \ - | (N ? IW_R_IMM5_SHIFTED_MASK : 0)) - -/* And here's the match/mask macros for the R1 instruction set. */ -#define MATCH_R1_ADD MATCH_R1_OPX0 (ADD) -#define MASK_R1_ADD MASK_R1_OPX0 -#define MATCH_R1_ADDI MATCH_R1_OP (ADDI) -#define MASK_R1_ADDI MASK_R1_OP -#define MATCH_R1_AND MATCH_R1_OPX0 (AND) -#define MASK_R1_AND MASK_R1_OPX0 -#define MATCH_R1_ANDHI MATCH_R1_OP (ANDHI) -#define MASK_R1_ANDHI MASK_R1_OP -#define MATCH_R1_ANDI MATCH_R1_OP (ANDI) -#define MASK_R1_ANDI MASK_R1_OP -#define MATCH_R1_BEQ MATCH_R1_OP (BEQ) -#define MASK_R1_BEQ MASK_R1_OP -#define MATCH_R1_BGE MATCH_R1_OP (BGE) -#define MASK_R1_BGE MASK_R1_OP -#define MATCH_R1_BGEU MATCH_R1_OP (BGEU) -#define MASK_R1_BGEU MASK_R1_OP -#define MATCH_R1_BGT MATCH_R1_OP (BLT) -#define MASK_R1_BGT MASK_R1_OP -#define MATCH_R1_BGTU MATCH_R1_OP (BLTU) -#define MASK_R1_BGTU MASK_R1_OP -#define MATCH_R1_BLE MATCH_R1_OP (BGE) -#define MASK_R1_BLE MASK_R1_OP -#define MATCH_R1_BLEU MATCH_R1_OP (BGEU) -#define MASK_R1_BLEU MASK_R1_OP -#define MATCH_R1_BLT MATCH_R1_OP (BLT) -#define MASK_R1_BLT MASK_R1_OP -#define MATCH_R1_BLTU MATCH_R1_OP (BLTU) -#define MASK_R1_BLTU MASK_R1_OP -#define MATCH_R1_BNE MATCH_R1_OP (BNE) -#define MASK_R1_BNE MASK_R1_OP -#define MATCH_R1_BR MATCH_R1_OP (BR) -#define MASK_R1_BR MASK_R1_OP | IW_I_A_SHIFTED_MASK | IW_I_B_SHIFTED_MASK -#define MATCH_R1_BREAK MATCH_R1_OPX (BREAK, 0, 0, 0x1e) -#define MASK_R1_BREAK MASK_R1_OPX (1, 1, 1, 0) -#define MATCH_R1_BRET MATCH_R1_OPX (BRET, 0x1e, 0, 0) -#define MASK_R1_BRET MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_CALL MATCH_R1_OP (CALL) -#define MASK_R1_CALL MASK_R1_OP -#define MATCH_R1_CALLR MATCH_R1_OPX (CALLR, 0, 0, 0x1f) -#define MASK_R1_CALLR MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_CMPEQ MATCH_R1_OPX0 (CMPEQ) -#define MASK_R1_CMPEQ MASK_R1_OPX0 -#define MATCH_R1_CMPEQI MATCH_R1_OP (CMPEQI) -#define MASK_R1_CMPEQI MASK_R1_OP -#define MATCH_R1_CMPGE MATCH_R1_OPX0 (CMPGE) -#define MASK_R1_CMPGE MASK_R1_OPX0 -#define MATCH_R1_CMPGEI MATCH_R1_OP (CMPGEI) -#define MASK_R1_CMPGEI MASK_R1_OP -#define MATCH_R1_CMPGEU MATCH_R1_OPX0 (CMPGEU) -#define MASK_R1_CMPGEU MASK_R1_OPX0 -#define MATCH_R1_CMPGEUI MATCH_R1_OP (CMPGEUI) -#define MASK_R1_CMPGEUI MASK_R1_OP -#define MATCH_R1_CMPGT MATCH_R1_OPX0 (CMPLT) -#define MASK_R1_CMPGT MASK_R1_OPX0 -#define MATCH_R1_CMPGTI MATCH_R1_OP (CMPGEI) -#define MASK_R1_CMPGTI MASK_R1_OP -#define MATCH_R1_CMPGTU MATCH_R1_OPX0 (CMPLTU) -#define MASK_R1_CMPGTU MASK_R1_OPX0 -#define MATCH_R1_CMPGTUI MATCH_R1_OP (CMPGEUI) -#define MASK_R1_CMPGTUI MASK_R1_OP -#define MATCH_R1_CMPLE MATCH_R1_OPX0 (CMPGE) -#define MASK_R1_CMPLE MASK_R1_OPX0 -#define MATCH_R1_CMPLEI MATCH_R1_OP (CMPLTI) -#define MASK_R1_CMPLEI MASK_R1_OP -#define MATCH_R1_CMPLEU MATCH_R1_OPX0 (CMPGEU) -#define MASK_R1_CMPLEU MASK_R1_OPX0 -#define MATCH_R1_CMPLEUI MATCH_R1_OP (CMPLTUI) -#define MASK_R1_CMPLEUI MASK_R1_OP -#define MATCH_R1_CMPLT MATCH_R1_OPX0 (CMPLT) -#define MASK_R1_CMPLT MASK_R1_OPX0 -#define MATCH_R1_CMPLTI MATCH_R1_OP (CMPLTI) -#define MASK_R1_CMPLTI MASK_R1_OP -#define MATCH_R1_CMPLTU MATCH_R1_OPX0 (CMPLTU) -#define MASK_R1_CMPLTU MASK_R1_OPX0 -#define MATCH_R1_CMPLTUI MATCH_R1_OP (CMPLTUI) -#define MASK_R1_CMPLTUI MASK_R1_OP -#define MATCH_R1_CMPNE MATCH_R1_OPX0 (CMPNE) -#define MASK_R1_CMPNE MASK_R1_OPX0 -#define MATCH_R1_CMPNEI MATCH_R1_OP (CMPNEI) -#define MASK_R1_CMPNEI MASK_R1_OP -#define MATCH_R1_CUSTOM MATCH_R1_OP (CUSTOM) -#define MASK_R1_CUSTOM MASK_R1_OP -#define MATCH_R1_DIV MATCH_R1_OPX0 (DIV) -#define MASK_R1_DIV MASK_R1_OPX0 -#define MATCH_R1_DIVU MATCH_R1_OPX0 (DIVU) -#define MASK_R1_DIVU MASK_R1_OPX0 -#define MATCH_R1_ERET MATCH_R1_OPX (ERET, 0x1d, 0x1e, 0) -#define MASK_R1_ERET MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_FLUSHD MATCH_R1_OP (FLUSHD) | SET_IW_I_B (0) -#define MASK_R1_FLUSHD MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_FLUSHDA MATCH_R1_OP (FLUSHDA) | SET_IW_I_B (0) -#define MASK_R1_FLUSHDA MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_FLUSHI MATCH_R1_OPX (FLUSHI, 0, 0, 0) -#define MASK_R1_FLUSHI MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_FLUSHP MATCH_R1_OPX (FLUSHP, 0, 0, 0) -#define MASK_R1_FLUSHP MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_INITD MATCH_R1_OP (INITD) | SET_IW_I_B (0) -#define MASK_R1_INITD MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_INITDA MATCH_R1_OP (INITDA) | SET_IW_I_B (0) -#define MASK_R1_INITDA MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_INITI MATCH_R1_OPX (INITI, 0, 0, 0) -#define MASK_R1_INITI MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_JMP MATCH_R1_OPX (JMP, 0, 0, 0) -#define MASK_R1_JMP MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_JMPI MATCH_R1_OP (JMPI) -#define MASK_R1_JMPI MASK_R1_OP -#define MATCH_R1_LDB MATCH_R1_OP (LDB) -#define MASK_R1_LDB MASK_R1_OP -#define MATCH_R1_LDBIO MATCH_R1_OP (LDBIO) -#define MASK_R1_LDBIO MASK_R1_OP -#define MATCH_R1_LDBU MATCH_R1_OP (LDBU) -#define MASK_R1_LDBU MASK_R1_OP -#define MATCH_R1_LDBUIO MATCH_R1_OP (LDBUIO) -#define MASK_R1_LDBUIO MASK_R1_OP -#define MATCH_R1_LDH MATCH_R1_OP (LDH) -#define MASK_R1_LDH MASK_R1_OP -#define MATCH_R1_LDHIO MATCH_R1_OP (LDHIO) -#define MASK_R1_LDHIO MASK_R1_OP -#define MATCH_R1_LDHU MATCH_R1_OP (LDHU) -#define MASK_R1_LDHU MASK_R1_OP -#define MATCH_R1_LDHUIO MATCH_R1_OP (LDHUIO) -#define MASK_R1_LDHUIO MASK_R1_OP -#define MATCH_R1_LDW MATCH_R1_OP (LDW) -#define MASK_R1_LDW MASK_R1_OP -#define MATCH_R1_LDWIO MATCH_R1_OP (LDWIO) -#define MASK_R1_LDWIO MASK_R1_OP -#define MATCH_R1_MOV MATCH_R1_OPX (ADD, 0, 0, 0) -#define MASK_R1_MOV MASK_R1_OPX (0, 1, 0, 1) -#define MATCH_R1_MOVHI MATCH_R1_OP (ORHI) | SET_IW_I_A (0) -#define MASK_R1_MOVHI MASK_R1_OP | IW_I_A_SHIFTED_MASK -#define MATCH_R1_MOVI MATCH_R1_OP (ADDI) | SET_IW_I_A (0) -#define MASK_R1_MOVI MASK_R1_OP | IW_I_A_SHIFTED_MASK -#define MATCH_R1_MOVUI MATCH_R1_OP (ORI) | SET_IW_I_A (0) -#define MASK_R1_MOVUI MASK_R1_OP | IW_I_A_SHIFTED_MASK -#define MATCH_R1_MUL MATCH_R1_OPX0 (MUL) -#define MASK_R1_MUL MASK_R1_OPX0 -#define MATCH_R1_MULI MATCH_R1_OP (MULI) -#define MASK_R1_MULI MASK_R1_OP -#define MATCH_R1_MULXSS MATCH_R1_OPX0 (MULXSS) -#define MASK_R1_MULXSS MASK_R1_OPX0 -#define MATCH_R1_MULXSU MATCH_R1_OPX0 (MULXSU) -#define MASK_R1_MULXSU MASK_R1_OPX0 -#define MATCH_R1_MULXUU MATCH_R1_OPX0 (MULXUU) -#define MASK_R1_MULXUU MASK_R1_OPX0 -#define MATCH_R1_NEXTPC MATCH_R1_OPX (NEXTPC, 0, 0, 0) -#define MASK_R1_NEXTPC MASK_R1_OPX (1, 1, 0, 1) -#define MATCH_R1_NOP MATCH_R1_OPX (ADD, 0, 0, 0) -#define MASK_R1_NOP MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_NOR MATCH_R1_OPX0 (NOR) -#define MASK_R1_NOR MASK_R1_OPX0 -#define MATCH_R1_OR MATCH_R1_OPX0 (OR) -#define MASK_R1_OR MASK_R1_OPX0 -#define MATCH_R1_ORHI MATCH_R1_OP (ORHI) -#define MASK_R1_ORHI MASK_R1_OP -#define MATCH_R1_ORI MATCH_R1_OP (ORI) -#define MASK_R1_ORI MASK_R1_OP -#define MATCH_R1_RDCTL MATCH_R1_OPX (RDCTL, 0, 0, 0) -#define MASK_R1_RDCTL MASK_R1_OPX (1, 1, 0, 0) -#define MATCH_R1_RDPRS MATCH_R1_OP (RDPRS) -#define MASK_R1_RDPRS MASK_R1_OP -#define MATCH_R1_RET MATCH_R1_OPX (RET, 0x1f, 0, 0) -#define MASK_R1_RET MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_ROL MATCH_R1_OPX0 (ROL) -#define MASK_R1_ROL MASK_R1_OPX0 -#define MATCH_R1_ROLI MATCH_R1_OPX (ROLI, 0, 0, 0) -#define MASK_R1_ROLI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_ROR MATCH_R1_OPX0 (ROR) -#define MASK_R1_ROR MASK_R1_OPX0 -#define MATCH_R1_SLL MATCH_R1_OPX0 (SLL) -#define MASK_R1_SLL MASK_R1_OPX0 -#define MATCH_R1_SLLI MATCH_R1_OPX (SLLI, 0, 0, 0) -#define MASK_R1_SLLI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_SRA MATCH_R1_OPX0 (SRA) -#define MASK_R1_SRA MASK_R1_OPX0 -#define MATCH_R1_SRAI MATCH_R1_OPX (SRAI, 0, 0, 0) -#define MASK_R1_SRAI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_SRL MATCH_R1_OPX0 (SRL) -#define MASK_R1_SRL MASK_R1_OPX0 -#define MATCH_R1_SRLI MATCH_R1_OPX (SRLI, 0, 0, 0) -#define MASK_R1_SRLI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_STB MATCH_R1_OP (STB) -#define MASK_R1_STB MASK_R1_OP -#define MATCH_R1_STBIO MATCH_R1_OP (STBIO) -#define MASK_R1_STBIO MASK_R1_OP -#define MATCH_R1_STH MATCH_R1_OP (STH) -#define MASK_R1_STH MASK_R1_OP -#define MATCH_R1_STHIO MATCH_R1_OP (STHIO) -#define MASK_R1_STHIO MASK_R1_OP -#define MATCH_R1_STW MATCH_R1_OP (STW) -#define MASK_R1_STW MASK_R1_OP -#define MATCH_R1_STWIO MATCH_R1_OP (STWIO) -#define MASK_R1_STWIO MASK_R1_OP -#define MATCH_R1_SUB MATCH_R1_OPX0 (SUB) -#define MASK_R1_SUB MASK_R1_OPX0 -#define MATCH_R1_SUBI MATCH_R1_OP (ADDI) -#define MASK_R1_SUBI MASK_R1_OP -#define MATCH_R1_SYNC MATCH_R1_OPX (SYNC, 0, 0, 0) -#define MASK_R1_SYNC MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_TRAP MATCH_R1_OPX (TRAP, 0, 0, 0x1d) -#define MASK_R1_TRAP MASK_R1_OPX (1, 1, 1, 0) -#define MATCH_R1_WRCTL MATCH_R1_OPX (WRCTL, 0, 0, 0) -#define MASK_R1_WRCTL MASK_R1_OPX (0, 1, 1, 0) -#define MATCH_R1_WRPRS MATCH_R1_OPX (WRPRS, 0, 0, 0) -#define MASK_R1_WRPRS MASK_R1_OPX (0, 1, 0, 1) -#define MATCH_R1_XOR MATCH_R1_OPX0 (XOR) -#define MASK_R1_XOR MASK_R1_OPX0 -#define MATCH_R1_XORHI MATCH_R1_OP (XORHI) -#define MASK_R1_XORHI MASK_R1_OP -#define MATCH_R1_XORI MATCH_R1_OP (XORI) -#define MASK_R1_XORI MASK_R1_OP - -#endif /* _NIOS2R1_H */ - -/*#include "nios2r2.h"*/ - -#ifndef _NIOS2R2_H_ -#define _NIOS2R2_H_ - -/* Fields for 32-bit R2 instructions. */ - -#define IW_R2_OP_LSB 0 -#define IW_R2_OP_SIZE 6 -#define IW_R2_OP_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R2_OP_SIZE)) -#define IW_R2_OP_SHIFTED_MASK (IW_R2_OP_UNSHIFTED_MASK << IW_R2_OP_LSB) -#define GET_IW_R2_OP(W) (((W) >> IW_R2_OP_LSB) & IW_R2_OP_UNSHIFTED_MASK) -#define SET_IW_R2_OP(V) (((V) & IW_R2_OP_UNSHIFTED_MASK) << IW_R2_OP_LSB) - -#define IW_L26_IMM26_LSB 6 -#define IW_L26_IMM26_SIZE 26 -#define IW_L26_IMM26_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L26_IMM26_SIZE)) -#define IW_L26_IMM26_SHIFTED_MASK (IW_L26_IMM26_UNSHIFTED_MASK << IW_L26_IMM26_LSB) -#define GET_IW_L26_IMM26(W) (((W) >> IW_L26_IMM26_LSB) & IW_L26_IMM26_UNSHIFTED_MASK) -#define SET_IW_L26_IMM26(V) (((V) & IW_L26_IMM26_UNSHIFTED_MASK) << IW_L26_IMM26_LSB) - -#define IW_F2I16_A_LSB 6 -#define IW_F2I16_A_SIZE 5 -#define IW_F2I16_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2I16_A_SIZE)) -#define IW_F2I16_A_SHIFTED_MASK (IW_F2I16_A_UNSHIFTED_MASK << IW_F2I16_A_LSB) -#define GET_IW_F2I16_A(W) (((W) >> IW_F2I16_A_LSB) & IW_F2I16_A_UNSHIFTED_MASK) -#define SET_IW_F2I16_A(V) (((V) & IW_F2I16_A_UNSHIFTED_MASK) << IW_F2I16_A_LSB) - -#define IW_F2I16_B_LSB 11 -#define IW_F2I16_B_SIZE 5 -#define IW_F2I16_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2I16_B_SIZE)) -#define IW_F2I16_B_SHIFTED_MASK (IW_F2I16_B_UNSHIFTED_MASK << IW_F2I16_B_LSB) -#define GET_IW_F2I16_B(W) (((W) >> IW_F2I16_B_LSB) & IW_F2I16_B_UNSHIFTED_MASK) -#define SET_IW_F2I16_B(V) (((V) & IW_F2I16_B_UNSHIFTED_MASK) << IW_F2I16_B_LSB) - -#define IW_F2I16_IMM16_LSB 16 -#define IW_F2I16_IMM16_SIZE 16 -#define IW_F2I16_IMM16_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2I16_IMM16_SIZE)) -#define IW_F2I16_IMM16_SHIFTED_MASK (IW_F2I16_IMM16_UNSHIFTED_MASK << IW_F2I16_IMM16_LSB) -#define GET_IW_F2I16_IMM16(W) (((W) >> IW_F2I16_IMM16_LSB) & IW_F2I16_IMM16_UNSHIFTED_MASK) -#define SET_IW_F2I16_IMM16(V) (((V) & IW_F2I16_IMM16_UNSHIFTED_MASK) << IW_F2I16_IMM16_LSB) - -/* Common to all three I12-group formats F2X4I12, F1X4I12, F1X4L17. */ -#define IW_I12_X_LSB 28 -#define IW_I12_X_SIZE 4 -#define IW_I12_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I12_X_SIZE)) -#define IW_I12_X_SHIFTED_MASK (IW_I12_X_UNSHIFTED_MASK << IW_I12_X_LSB) -#define GET_IW_I12_X(W) (((W) >> IW_I12_X_LSB) & IW_I12_X_UNSHIFTED_MASK) -#define SET_IW_I12_X(V) (((V) & IW_I12_X_UNSHIFTED_MASK) << IW_I12_X_LSB) - -#define IW_F2X4I12_A_LSB 6 -#define IW_F2X4I12_A_SIZE 5 -#define IW_F2X4I12_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X4I12_A_SIZE)) -#define IW_F2X4I12_A_SHIFTED_MASK (IW_F2X4I12_A_UNSHIFTED_MASK << IW_F2X4I12_A_LSB) -#define GET_IW_F2X4I12_A(W) (((W) >> IW_F2X4I12_A_LSB) & IW_F2X4I12_A_UNSHIFTED_MASK) -#define SET_IW_F2X4I12_A(V) (((V) & IW_F2X4I12_A_UNSHIFTED_MASK) << IW_F2X4I12_A_LSB) - -#define IW_F2X4I12_B_LSB 11 -#define IW_F2X4I12_B_SIZE 5 -#define IW_F2X4I12_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X4I12_B_SIZE)) -#define IW_F2X4I12_B_SHIFTED_MASK (IW_F2X4I12_B_UNSHIFTED_MASK << IW_F2X4I12_B_LSB) -#define GET_IW_F2X4I12_B(W) (((W) >> IW_F2X4I12_B_LSB) & IW_F2X4I12_B_UNSHIFTED_MASK) -#define SET_IW_F2X4I12_B(V) (((V) & IW_F2X4I12_B_UNSHIFTED_MASK) << IW_F2X4I12_B_LSB) - -#define IW_F2X4I12_IMM12_LSB 16 -#define IW_F2X4I12_IMM12_SIZE 12 -#define IW_F2X4I12_IMM12_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X4I12_IMM12_SIZE)) -#define IW_F2X4I12_IMM12_SHIFTED_MASK (IW_F2X4I12_IMM12_UNSHIFTED_MASK << IW_F2X4I12_IMM12_LSB) -#define GET_IW_F2X4I12_IMM12(W) (((W) >> IW_F2X4I12_IMM12_LSB) & IW_F2X4I12_IMM12_UNSHIFTED_MASK) -#define SET_IW_F2X4I12_IMM12(V) (((V) & IW_F2X4I12_IMM12_UNSHIFTED_MASK) << IW_F2X4I12_IMM12_LSB) - -#define IW_F1X4I12_A_LSB 6 -#define IW_F1X4I12_A_SIZE 5 -#define IW_F1X4I12_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4I12_A_SIZE)) -#define IW_F1X4I12_A_SHIFTED_MASK (IW_F1X4I12_A_UNSHIFTED_MASK << IW_F1X4I12_A_LSB) -#define GET_IW_F1X4I12_A(W) (((W) >> IW_F1X4I12_A_LSB) & IW_F1X4I12_A_UNSHIFTED_MASK) -#define SET_IW_F1X4I12_A(V) (((V) & IW_F1X4I12_A_UNSHIFTED_MASK) << IW_F1X4I12_A_LSB) - -#define IW_F1X4I12_X_LSB 11 -#define IW_F1X4I12_X_SIZE 5 -#define IW_F1X4I12_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4I12_X_SIZE)) -#define IW_F1X4I12_X_SHIFTED_MASK (IW_F1X4I12_X_UNSHIFTED_MASK << IW_F1X4I12_X_LSB) -#define GET_IW_F1X4I12_X(W) (((W) >> IW_F1X4I12_X_LSB) & IW_F1X4I12_X_UNSHIFTED_MASK) -#define SET_IW_F1X4I12_X(V) (((V) & IW_F1X4I12_X_UNSHIFTED_MASK) << IW_F1X4I12_X_LSB) - -#define IW_F1X4I12_IMM12_LSB 16 -#define IW_F1X4I12_IMM12_SIZE 12 -#define IW_F1X4I12_IMM12_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4I12_IMM12_SIZE)) -#define IW_F1X4I12_IMM12_SHIFTED_MASK (IW_F1X4I12_IMM12_UNSHIFTED_MASK << IW_F1X4I12_IMM12_LSB) -#define GET_IW_F1X4I12_IMM12(W) (((W) >> IW_F1X4I12_IMM12_LSB) & IW_F1X4I12_IMM12_UNSHIFTED_MASK) -#define SET_IW_F1X4I12_IMM12(V) (((V) & IW_F1X4I12_IMM12_UNSHIFTED_MASK) << IW_F1X4I12_IMM12_LSB) - -#define IW_F1X4L17_A_LSB 6 -#define IW_F1X4L17_A_SIZE 5 -#define IW_F1X4L17_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_A_SIZE)) -#define IW_F1X4L17_A_SHIFTED_MASK (IW_F1X4L17_A_UNSHIFTED_MASK << IW_F1X4L17_A_LSB) -#define GET_IW_F1X4L17_A(W) (((W) >> IW_F1X4L17_A_LSB) & IW_F1X4L17_A_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_A(V) (((V) & IW_F1X4L17_A_UNSHIFTED_MASK) << IW_F1X4L17_A_LSB) - -#define IW_F1X4L17_ID_LSB 11 -#define IW_F1X4L17_ID_SIZE 1 -#define IW_F1X4L17_ID_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_ID_SIZE)) -#define IW_F1X4L17_ID_SHIFTED_MASK (IW_F1X4L17_ID_UNSHIFTED_MASK << IW_F1X4L17_ID_LSB) -#define GET_IW_F1X4L17_ID(W) (((W) >> IW_F1X4L17_ID_LSB) & IW_F1X4L17_ID_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_ID(V) (((V) & IW_F1X4L17_ID_UNSHIFTED_MASK) << IW_F1X4L17_ID_LSB) - -#define IW_F1X4L17_WB_LSB 12 -#define IW_F1X4L17_WB_SIZE 1 -#define IW_F1X4L17_WB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_WB_SIZE)) -#define IW_F1X4L17_WB_SHIFTED_MASK (IW_F1X4L17_WB_UNSHIFTED_MASK << IW_F1X4L17_WB_LSB) -#define GET_IW_F1X4L17_WB(W) (((W) >> IW_F1X4L17_WB_LSB) & IW_F1X4L17_WB_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_WB(V) (((V) & IW_F1X4L17_WB_UNSHIFTED_MASK) << IW_F1X4L17_WB_LSB) - -#define IW_F1X4L17_RS_LSB 13 -#define IW_F1X4L17_RS_SIZE 1 -#define IW_F1X4L17_RS_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_RS_SIZE)) -#define IW_F1X4L17_RS_SHIFTED_MASK (IW_F1X4L17_RS_UNSHIFTED_MASK << IW_F1X4L17_RS_LSB) -#define GET_IW_F1X4L17_RS(W) (((W) >> IW_F1X4L17_RS_LSB) & IW_F1X4L17_RS_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_RS(V) (((V) & IW_F1X4L17_RS_UNSHIFTED_MASK) << IW_F1X4L17_RS_LSB) - -#define IW_F1X4L17_PC_LSB 14 -#define IW_F1X4L17_PC_SIZE 1 -#define IW_F1X4L17_PC_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_PC_SIZE)) -#define IW_F1X4L17_PC_SHIFTED_MASK (IW_F1X4L17_PC_UNSHIFTED_MASK << IW_F1X4L17_PC_LSB) -#define GET_IW_F1X4L17_PC(W) (((W) >> IW_F1X4L17_PC_LSB) & IW_F1X4L17_PC_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_PC(V) (((V) & IW_F1X4L17_PC_UNSHIFTED_MASK) << IW_F1X4L17_PC_LSB) - -#define IW_F1X4L17_RSV_LSB 15 -#define IW_F1X4L17_RSV_SIZE 1 -#define IW_F1X4L17_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_RSV_SIZE)) -#define IW_F1X4L17_RSV_SHIFTED_MASK (IW_F1X4L17_RSV_UNSHIFTED_MASK << IW_F1X4L17_RSV_LSB) -#define GET_IW_F1X4L17_RSV(W) (((W) >> IW_F1X4L17_RSV_LSB) & IW_F1X4L17_RSV_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_RSV(V) (((V) & IW_F1X4L17_RSV_UNSHIFTED_MASK) << IW_F1X4L17_RSV_LSB) - -#define IW_F1X4L17_REGMASK_LSB 16 -#define IW_F1X4L17_REGMASK_SIZE 12 -#define IW_F1X4L17_REGMASK_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_REGMASK_SIZE)) -#define IW_F1X4L17_REGMASK_SHIFTED_MASK (IW_F1X4L17_REGMASK_UNSHIFTED_MASK << IW_F1X4L17_REGMASK_LSB) -#define GET_IW_F1X4L17_REGMASK(W) (((W) >> IW_F1X4L17_REGMASK_LSB) & IW_F1X4L17_REGMASK_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_REGMASK(V) (((V) & IW_F1X4L17_REGMASK_UNSHIFTED_MASK) << IW_F1X4L17_REGMASK_LSB) - -/* Shared by OPX-group formats F3X6L5, F2X6L10, F3X6. */ -#define IW_OPX_X_LSB 26 -#define IW_OPX_X_SIZE 6 -#define IW_OPX_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_OPX_X_SIZE)) -#define IW_OPX_X_SHIFTED_MASK (IW_OPX_X_UNSHIFTED_MASK << IW_OPX_X_LSB) -#define GET_IW_OPX_X(W) (((W) >> IW_OPX_X_LSB) & IW_OPX_X_UNSHIFTED_MASK) -#define SET_IW_OPX_X(V) (((V) & IW_OPX_X_UNSHIFTED_MASK) << IW_OPX_X_LSB) - -/* F3X6L5 accessors are also used for F3X6 formats. */ -#define IW_F3X6L5_A_LSB 6 -#define IW_F3X6L5_A_SIZE 5 -#define IW_F3X6L5_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_A_SIZE)) -#define IW_F3X6L5_A_SHIFTED_MASK (IW_F3X6L5_A_UNSHIFTED_MASK << IW_F3X6L5_A_LSB) -#define GET_IW_F3X6L5_A(W) (((W) >> IW_F3X6L5_A_LSB) & IW_F3X6L5_A_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_A(V) (((V) & IW_F3X6L5_A_UNSHIFTED_MASK) << IW_F3X6L5_A_LSB) - -#define IW_F3X6L5_B_LSB 11 -#define IW_F3X6L5_B_SIZE 5 -#define IW_F3X6L5_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_B_SIZE)) -#define IW_F3X6L5_B_SHIFTED_MASK (IW_F3X6L5_B_UNSHIFTED_MASK << IW_F3X6L5_B_LSB) -#define GET_IW_F3X6L5_B(W) (((W) >> IW_F3X6L5_B_LSB) & IW_F3X6L5_B_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_B(V) (((V) & IW_F3X6L5_B_UNSHIFTED_MASK) << IW_F3X6L5_B_LSB) - -#define IW_F3X6L5_C_LSB 16 -#define IW_F3X6L5_C_SIZE 5 -#define IW_F3X6L5_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_C_SIZE)) -#define IW_F3X6L5_C_SHIFTED_MASK (IW_F3X6L5_C_UNSHIFTED_MASK << IW_F3X6L5_C_LSB) -#define GET_IW_F3X6L5_C(W) (((W) >> IW_F3X6L5_C_LSB) & IW_F3X6L5_C_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_C(V) (((V) & IW_F3X6L5_C_UNSHIFTED_MASK) << IW_F3X6L5_C_LSB) - -#define IW_F3X6L5_IMM5_LSB 21 -#define IW_F3X6L5_IMM5_SIZE 5 -#define IW_F3X6L5_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_IMM5_SIZE)) -#define IW_F3X6L5_IMM5_SHIFTED_MASK (IW_F3X6L5_IMM5_UNSHIFTED_MASK << IW_F3X6L5_IMM5_LSB) -#define GET_IW_F3X6L5_IMM5(W) (((W) >> IW_F3X6L5_IMM5_LSB) & IW_F3X6L5_IMM5_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_IMM5(V) (((V) & IW_F3X6L5_IMM5_UNSHIFTED_MASK) << IW_F3X6L5_IMM5_LSB) - -#define IW_F2X6L10_A_LSB 6 -#define IW_F2X6L10_A_SIZE 5 -#define IW_F2X6L10_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_A_SIZE)) -#define IW_F2X6L10_A_SHIFTED_MASK (IW_F2X6L10_A_UNSHIFTED_MASK << IW_F2X6L10_A_LSB) -#define GET_IW_F2X6L10_A(W) (((W) >> IW_F2X6L10_A_LSB) & IW_F2X6L10_A_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_A(V) (((V) & IW_F2X6L10_A_UNSHIFTED_MASK) << IW_F2X6L10_A_LSB) - -#define IW_F2X6L10_B_LSB 11 -#define IW_F2X6L10_B_SIZE 5 -#define IW_F2X6L10_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_B_SIZE)) -#define IW_F2X6L10_B_SHIFTED_MASK (IW_F2X6L10_B_UNSHIFTED_MASK << IW_F2X6L10_B_LSB) -#define GET_IW_F2X6L10_B(W) (((W) >> IW_F2X6L10_B_LSB) & IW_F2X6L10_B_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_B(V) (((V) & IW_F2X6L10_B_UNSHIFTED_MASK) << IW_F2X6L10_B_LSB) - -#define IW_F2X6L10_LSB_LSB 16 -#define IW_F2X6L10_LSB_SIZE 5 -#define IW_F2X6L10_LSB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_LSB_SIZE)) -#define IW_F2X6L10_LSB_SHIFTED_MASK (IW_F2X6L10_LSB_UNSHIFTED_MASK << IW_F2X6L10_LSB_LSB) -#define GET_IW_F2X6L10_LSB(W) (((W) >> IW_F2X6L10_LSB_LSB) & IW_F2X6L10_LSB_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_LSB(V) (((V) & IW_F2X6L10_LSB_UNSHIFTED_MASK) << IW_F2X6L10_LSB_LSB) - -#define IW_F2X6L10_MSB_LSB 21 -#define IW_F2X6L10_MSB_SIZE 5 -#define IW_F2X6L10_MSB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_MSB_SIZE)) -#define IW_F2X6L10_MSB_SHIFTED_MASK (IW_F2X6L10_MSB_UNSHIFTED_MASK << IW_F2X6L10_MSB_LSB) -#define GET_IW_F2X6L10_MSB(W) (((W) >> IW_F2X6L10_MSB_LSB) & IW_F2X6L10_MSB_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_MSB(V) (((V) & IW_F2X6L10_MSB_UNSHIFTED_MASK) << IW_F2X6L10_MSB_LSB) - -#define IW_F3X8_A_LSB 6 -#define IW_F3X8_A_SIZE 5 -#define IW_F3X8_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_A_SIZE)) -#define IW_F3X8_A_SHIFTED_MASK (IW_F3X8_A_UNSHIFTED_MASK << IW_F3X8_A_LSB) -#define GET_IW_F3X8_A(W) (((W) >> IW_F3X8_A_LSB) & IW_F3X8_A_UNSHIFTED_MASK) -#define SET_IW_F3X8_A(V) (((V) & IW_F3X8_A_UNSHIFTED_MASK) << IW_F3X8_A_LSB) - -#define IW_F3X8_B_LSB 11 -#define IW_F3X8_B_SIZE 5 -#define IW_F3X8_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_B_SIZE)) -#define IW_F3X8_B_SHIFTED_MASK (IW_F3X8_B_UNSHIFTED_MASK << IW_F3X8_B_LSB) -#define GET_IW_F3X8_B(W) (((W) >> IW_F3X8_B_LSB) & IW_F3X8_B_UNSHIFTED_MASK) -#define SET_IW_F3X8_B(V) (((V) & IW_F3X8_B_UNSHIFTED_MASK) << IW_F3X8_B_LSB) - -#define IW_F3X8_C_LSB 16 -#define IW_F3X8_C_SIZE 5 -#define IW_F3X8_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_C_SIZE)) -#define IW_F3X8_C_SHIFTED_MASK (IW_F3X8_C_UNSHIFTED_MASK << IW_F3X8_C_LSB) -#define GET_IW_F3X8_C(W) (((W) >> IW_F3X8_C_LSB) & IW_F3X8_C_UNSHIFTED_MASK) -#define SET_IW_F3X8_C(V) (((V) & IW_F3X8_C_UNSHIFTED_MASK) << IW_F3X8_C_LSB) - -#define IW_F3X8_READA_LSB 21 -#define IW_F3X8_READA_SIZE 1 -#define IW_F3X8_READA_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_READA_SIZE)) -#define IW_F3X8_READA_SHIFTED_MASK (IW_F3X8_READA_UNSHIFTED_MASK << IW_F3X8_READA_LSB) -#define GET_IW_F3X8_READA(W) (((W) >> IW_F3X8_READA_LSB) & IW_F3X8_READA_UNSHIFTED_MASK) -#define SET_IW_F3X8_READA(V) (((V) & IW_F3X8_READA_UNSHIFTED_MASK) << IW_F3X8_READA_LSB) - -#define IW_F3X8_READB_LSB 22 -#define IW_F3X8_READB_SIZE 1 -#define IW_F3X8_READB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_READB_SIZE)) -#define IW_F3X8_READB_SHIFTED_MASK (IW_F3X8_READB_UNSHIFTED_MASK << IW_F3X8_READB_LSB) -#define GET_IW_F3X8_READB(W) (((W) >> IW_F3X8_READB_LSB) & IW_F3X8_READB_UNSHIFTED_MASK) -#define SET_IW_F3X8_READB(V) (((V) & IW_F3X8_READB_UNSHIFTED_MASK) << IW_F3X8_READB_LSB) - -#define IW_F3X8_READC_LSB 23 -#define IW_F3X8_READC_SIZE 1 -#define IW_F3X8_READC_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_READC_SIZE)) -#define IW_F3X8_READC_SHIFTED_MASK (IW_F3X8_READC_UNSHIFTED_MASK << IW_F3X8_READC_LSB) -#define GET_IW_F3X8_READC(W) (((W) >> IW_F3X8_READC_LSB) & IW_F3X8_READC_UNSHIFTED_MASK) -#define SET_IW_F3X8_READC(V) (((V) & IW_F3X8_READC_UNSHIFTED_MASK) << IW_F3X8_READC_LSB) - -#define IW_F3X8_N_LSB 24 -#define IW_F3X8_N_SIZE 8 -#define IW_F3X8_N_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_N_SIZE)) -#define IW_F3X8_N_SHIFTED_MASK (IW_F3X8_N_UNSHIFTED_MASK << IW_F3X8_N_LSB) -#define GET_IW_F3X8_N(W) (((W) >> IW_F3X8_N_LSB) & IW_F3X8_N_UNSHIFTED_MASK) -#define SET_IW_F3X8_N(V) (((V) & IW_F3X8_N_UNSHIFTED_MASK) << IW_F3X8_N_LSB) - -/* 16-bit R2 fields. */ - -#define IW_I10_IMM10_LSB 6 -#define IW_I10_IMM10_SIZE 10 -#define IW_I10_IMM10_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I10_IMM10_SIZE)) -#define IW_I10_IMM10_SHIFTED_MASK (IW_I10_IMM10_UNSHIFTED_MASK << IW_I10_IMM10_LSB) -#define GET_IW_I10_IMM10(W) (((W) >> IW_I10_IMM10_LSB) & IW_I10_IMM10_UNSHIFTED_MASK) -#define SET_IW_I10_IMM10(V) (((V) & IW_I10_IMM10_UNSHIFTED_MASK) << IW_I10_IMM10_LSB) - -#define IW_T1I7_A3_LSB 6 -#define IW_T1I7_A3_SIZE 3 -#define IW_T1I7_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1I7_A3_SIZE)) -#define IW_T1I7_A3_SHIFTED_MASK (IW_T1I7_A3_UNSHIFTED_MASK << IW_T1I7_A3_LSB) -#define GET_IW_T1I7_A3(W) (((W) >> IW_T1I7_A3_LSB) & IW_T1I7_A3_UNSHIFTED_MASK) -#define SET_IW_T1I7_A3(V) (((V) & IW_T1I7_A3_UNSHIFTED_MASK) << IW_T1I7_A3_LSB) - -#define IW_T1I7_IMM7_LSB 9 -#define IW_T1I7_IMM7_SIZE 7 -#define IW_T1I7_IMM7_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1I7_IMM7_SIZE)) -#define IW_T1I7_IMM7_SHIFTED_MASK (IW_T1I7_IMM7_UNSHIFTED_MASK << IW_T1I7_IMM7_LSB) -#define GET_IW_T1I7_IMM7(W) (((W) >> IW_T1I7_IMM7_LSB) & IW_T1I7_IMM7_UNSHIFTED_MASK) -#define SET_IW_T1I7_IMM7(V) (((V) & IW_T1I7_IMM7_UNSHIFTED_MASK) << IW_T1I7_IMM7_LSB) - -#define IW_T2I4_A3_LSB 6 -#define IW_T2I4_A3_SIZE 3 -#define IW_T2I4_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2I4_A3_SIZE)) -#define IW_T2I4_A3_SHIFTED_MASK (IW_T2I4_A3_UNSHIFTED_MASK << IW_T2I4_A3_LSB) -#define GET_IW_T2I4_A3(W) (((W) >> IW_T2I4_A3_LSB) & IW_T2I4_A3_UNSHIFTED_MASK) -#define SET_IW_T2I4_A3(V) (((V) & IW_T2I4_A3_UNSHIFTED_MASK) << IW_T2I4_A3_LSB) - -#define IW_T2I4_B3_LSB 9 -#define IW_T2I4_B3_SIZE 3 -#define IW_T2I4_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2I4_B3_SIZE)) -#define IW_T2I4_B3_SHIFTED_MASK (IW_T2I4_B3_UNSHIFTED_MASK << IW_T2I4_B3_LSB) -#define GET_IW_T2I4_B3(W) (((W) >> IW_T2I4_B3_LSB) & IW_T2I4_B3_UNSHIFTED_MASK) -#define SET_IW_T2I4_B3(V) (((V) & IW_T2I4_B3_UNSHIFTED_MASK) << IW_T2I4_B3_LSB) - -#define IW_T2I4_IMM4_LSB 12 -#define IW_T2I4_IMM4_SIZE 4 -#define IW_T2I4_IMM4_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2I4_IMM4_SIZE)) -#define IW_T2I4_IMM4_SHIFTED_MASK (IW_T2I4_IMM4_UNSHIFTED_MASK << IW_T2I4_IMM4_LSB) -#define GET_IW_T2I4_IMM4(W) (((W) >> IW_T2I4_IMM4_LSB) & IW_T2I4_IMM4_UNSHIFTED_MASK) -#define SET_IW_T2I4_IMM4(V) (((V) & IW_T2I4_IMM4_UNSHIFTED_MASK) << IW_T2I4_IMM4_LSB) - -#define IW_T1X1I6_A3_LSB 6 -#define IW_T1X1I6_A3_SIZE 3 -#define IW_T1X1I6_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1X1I6_A3_SIZE)) -#define IW_T1X1I6_A3_SHIFTED_MASK (IW_T1X1I6_A3_UNSHIFTED_MASK << IW_T1X1I6_A3_LSB) -#define GET_IW_T1X1I6_A3(W) (((W) >> IW_T1X1I6_A3_LSB) & IW_T1X1I6_A3_UNSHIFTED_MASK) -#define SET_IW_T1X1I6_A3(V) (((V) & IW_T1X1I6_A3_UNSHIFTED_MASK) << IW_T1X1I6_A3_LSB) - -#define IW_T1X1I6_IMM6_LSB 9 -#define IW_T1X1I6_IMM6_SIZE 6 -#define IW_T1X1I6_IMM6_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1X1I6_IMM6_SIZE)) -#define IW_T1X1I6_IMM6_SHIFTED_MASK (IW_T1X1I6_IMM6_UNSHIFTED_MASK << IW_T1X1I6_IMM6_LSB) -#define GET_IW_T1X1I6_IMM6(W) (((W) >> IW_T1X1I6_IMM6_LSB) & IW_T1X1I6_IMM6_UNSHIFTED_MASK) -#define SET_IW_T1X1I6_IMM6(V) (((V) & IW_T1X1I6_IMM6_UNSHIFTED_MASK) << IW_T1X1I6_IMM6_LSB) - -#define IW_T1X1I6_X_LSB 15 -#define IW_T1X1I6_X_SIZE 1 -#define IW_T1X1I6_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1X1I6_X_SIZE)) -#define IW_T1X1I6_X_SHIFTED_MASK (IW_T1X1I6_X_UNSHIFTED_MASK << IW_T1X1I6_X_LSB) -#define GET_IW_T1X1I6_X(W) (((W) >> IW_T1X1I6_X_LSB) & IW_T1X1I6_X_UNSHIFTED_MASK) -#define SET_IW_T1X1I6_X(V) (((V) & IW_T1X1I6_X_UNSHIFTED_MASK) << IW_T1X1I6_X_LSB) - -#define IW_X1I7_IMM7_LSB 6 -#define IW_X1I7_IMM7_SIZE 7 -#define IW_X1I7_IMM7_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X1I7_IMM7_SIZE)) -#define IW_X1I7_IMM7_SHIFTED_MASK (IW_X1I7_IMM7_UNSHIFTED_MASK << IW_X1I7_IMM7_LSB) -#define GET_IW_X1I7_IMM7(W) (((W) >> IW_X1I7_IMM7_LSB) & IW_X1I7_IMM7_UNSHIFTED_MASK) -#define SET_IW_X1I7_IMM7(V) (((V) & IW_X1I7_IMM7_UNSHIFTED_MASK) << IW_X1I7_IMM7_LSB) - -#define IW_X1I7_RSV_LSB 13 -#define IW_X1I7_RSV_SIZE 2 -#define IW_X1I7_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X1I7_RSV_SIZE)) -#define IW_X1I7_RSV_SHIFTED_MASK (IW_X1I7_RSV_UNSHIFTED_MASK << IW_X1I7_RSV_LSB) -#define GET_IW_X1I7_RSV(W) (((W) >> IW_X1I7_RSV_LSB) & IW_X1I7_RSV_UNSHIFTED_MASK) -#define SET_IW_X1I7_RSV(V) (((V) & IW_X1I7_RSV_UNSHIFTED_MASK) << IW_X1I7_RSV_LSB) - -#define IW_X1I7_X_LSB 15 -#define IW_X1I7_X_SIZE 1 -#define IW_X1I7_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X1I7_X_SIZE)) -#define IW_X1I7_X_SHIFTED_MASK (IW_X1I7_X_UNSHIFTED_MASK << IW_X1I7_X_LSB) -#define GET_IW_X1I7_X(W) (((W) >> IW_X1I7_X_LSB) & IW_X1I7_X_UNSHIFTED_MASK) -#define SET_IW_X1I7_X(V) (((V) & IW_X1I7_X_UNSHIFTED_MASK) << IW_X1I7_X_LSB) - -#define IW_L5I4X1_IMM4_LSB 6 -#define IW_L5I4X1_IMM4_SIZE 4 -#define IW_L5I4X1_IMM4_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_IMM4_SIZE)) -#define IW_L5I4X1_IMM4_SHIFTED_MASK (IW_L5I4X1_IMM4_UNSHIFTED_MASK << IW_L5I4X1_IMM4_LSB) -#define GET_IW_L5I4X1_IMM4(W) (((W) >> IW_L5I4X1_IMM4_LSB) & IW_L5I4X1_IMM4_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_IMM4(V) (((V) & IW_L5I4X1_IMM4_UNSHIFTED_MASK) << IW_L5I4X1_IMM4_LSB) - -#define IW_L5I4X1_REGRANGE_LSB 10 -#define IW_L5I4X1_REGRANGE_SIZE 3 -#define IW_L5I4X1_REGRANGE_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_REGRANGE_SIZE)) -#define IW_L5I4X1_REGRANGE_SHIFTED_MASK (IW_L5I4X1_REGRANGE_UNSHIFTED_MASK << IW_L5I4X1_REGRANGE_LSB) -#define GET_IW_L5I4X1_REGRANGE(W) (((W) >> IW_L5I4X1_REGRANGE_LSB) & IW_L5I4X1_REGRANGE_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_REGRANGE(V) (((V) & IW_L5I4X1_REGRANGE_UNSHIFTED_MASK) << IW_L5I4X1_REGRANGE_LSB) - -#define IW_L5I4X1_FP_LSB 13 -#define IW_L5I4X1_FP_SIZE 1 -#define IW_L5I4X1_FP_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_FP_SIZE)) -#define IW_L5I4X1_FP_SHIFTED_MASK (IW_L5I4X1_FP_UNSHIFTED_MASK << IW_L5I4X1_FP_LSB) -#define GET_IW_L5I4X1_FP(W) (((W) >> IW_L5I4X1_FP_LSB) & IW_L5I4X1_FP_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_FP(V) (((V) & IW_L5I4X1_FP_UNSHIFTED_MASK) << IW_L5I4X1_FP_LSB) - -#define IW_L5I4X1_CS_LSB 14 -#define IW_L5I4X1_CS_SIZE 1 -#define IW_L5I4X1_CS_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_CS_SIZE)) -#define IW_L5I4X1_CS_SHIFTED_MASK (IW_L5I4X1_CS_UNSHIFTED_MASK << IW_L5I4X1_CS_LSB) -#define GET_IW_L5I4X1_CS(W) (((W) >> IW_L5I4X1_CS_LSB) & IW_L5I4X1_CS_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_CS(V) (((V) & IW_L5I4X1_CS_UNSHIFTED_MASK) << IW_L5I4X1_CS_LSB) - -#define IW_L5I4X1_X_LSB 15 -#define IW_L5I4X1_X_SIZE 1 -#define IW_L5I4X1_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_X_SIZE)) -#define IW_L5I4X1_X_SHIFTED_MASK (IW_L5I4X1_X_UNSHIFTED_MASK << IW_L5I4X1_X_LSB) -#define GET_IW_L5I4X1_X(W) (((W) >> IW_L5I4X1_X_LSB) & IW_L5I4X1_X_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_X(V) (((V) & IW_L5I4X1_X_UNSHIFTED_MASK) << IW_L5I4X1_X_LSB) - -#define IW_T2X1L3_A3_LSB 6 -#define IW_T2X1L3_A3_SIZE 3 -#define IW_T2X1L3_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_A3_SIZE)) -#define IW_T2X1L3_A3_SHIFTED_MASK (IW_T2X1L3_A3_UNSHIFTED_MASK << IW_T2X1L3_A3_LSB) -#define GET_IW_T2X1L3_A3(W) (((W) >> IW_T2X1L3_A3_LSB) & IW_T2X1L3_A3_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_A3(V) (((V) & IW_T2X1L3_A3_UNSHIFTED_MASK) << IW_T2X1L3_A3_LSB) - -#define IW_T2X1L3_B3_LSB 9 -#define IW_T2X1L3_B3_SIZE 3 -#define IW_T2X1L3_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_B3_SIZE)) -#define IW_T2X1L3_B3_SHIFTED_MASK (IW_T2X1L3_B3_UNSHIFTED_MASK << IW_T2X1L3_B3_LSB) -#define GET_IW_T2X1L3_B3(W) (((W) >> IW_T2X1L3_B3_LSB) & IW_T2X1L3_B3_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_B3(V) (((V) & IW_T2X1L3_B3_UNSHIFTED_MASK) << IW_T2X1L3_B3_LSB) - -#define IW_T2X1L3_SHAMT_LSB 12 -#define IW_T2X1L3_SHAMT_SIZE 3 -#define IW_T2X1L3_SHAMT_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_SHAMT_SIZE)) -#define IW_T2X1L3_SHAMT_SHIFTED_MASK (IW_T2X1L3_SHAMT_UNSHIFTED_MASK << IW_T2X1L3_SHAMT_LSB) -#define GET_IW_T2X1L3_SHAMT(W) (((W) >> IW_T2X1L3_SHAMT_LSB) & IW_T2X1L3_SHAMT_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_SHAMT(V) (((V) & IW_T2X1L3_SHAMT_UNSHIFTED_MASK) << IW_T2X1L3_SHAMT_LSB) - -#define IW_T2X1L3_X_LSB 15 -#define IW_T2X1L3_X_SIZE 1 -#define IW_T2X1L3_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_X_SIZE)) -#define IW_T2X1L3_X_SHIFTED_MASK (IW_T2X1L3_X_UNSHIFTED_MASK << IW_T2X1L3_X_LSB) -#define GET_IW_T2X1L3_X(W) (((W) >> IW_T2X1L3_X_LSB) & IW_T2X1L3_X_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_X(V) (((V) & IW_T2X1L3_X_UNSHIFTED_MASK) << IW_T2X1L3_X_LSB) - -#define IW_T2X1I3_A3_LSB 6 -#define IW_T2X1I3_A3_SIZE 3 -#define IW_T2X1I3_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_A3_SIZE)) -#define IW_T2X1I3_A3_SHIFTED_MASK (IW_T2X1I3_A3_UNSHIFTED_MASK << IW_T2X1I3_A3_LSB) -#define GET_IW_T2X1I3_A3(W) (((W) >> IW_T2X1I3_A3_LSB) & IW_T2X1I3_A3_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_A3(V) (((V) & IW_T2X1I3_A3_UNSHIFTED_MASK) << IW_T2X1I3_A3_LSB) - -#define IW_T2X1I3_B3_LSB 9 -#define IW_T2X1I3_B3_SIZE 3 -#define IW_T2X1I3_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_B3_SIZE)) -#define IW_T2X1I3_B3_SHIFTED_MASK (IW_T2X1I3_B3_UNSHIFTED_MASK << IW_T2X1I3_B3_LSB) -#define GET_IW_T2X1I3_B3(W) (((W) >> IW_T2X1I3_B3_LSB) & IW_T2X1I3_B3_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_B3(V) (((V) & IW_T2X1I3_B3_UNSHIFTED_MASK) << IW_T2X1I3_B3_LSB) - -#define IW_T2X1I3_IMM3_LSB 12 -#define IW_T2X1I3_IMM3_SIZE 3 -#define IW_T2X1I3_IMM3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_IMM3_SIZE)) -#define IW_T2X1I3_IMM3_SHIFTED_MASK (IW_T2X1I3_IMM3_UNSHIFTED_MASK << IW_T2X1I3_IMM3_LSB) -#define GET_IW_T2X1I3_IMM3(W) (((W) >> IW_T2X1I3_IMM3_LSB) & IW_T2X1I3_IMM3_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_IMM3(V) (((V) & IW_T2X1I3_IMM3_UNSHIFTED_MASK) << IW_T2X1I3_IMM3_LSB) - -#define IW_T2X1I3_X_LSB 15 -#define IW_T2X1I3_X_SIZE 1 -#define IW_T2X1I3_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_X_SIZE)) -#define IW_T2X1I3_X_SHIFTED_MASK (IW_T2X1I3_X_UNSHIFTED_MASK << IW_T2X1I3_X_LSB) -#define GET_IW_T2X1I3_X(W) (((W) >> IW_T2X1I3_X_LSB) & IW_T2X1I3_X_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_X(V) (((V) & IW_T2X1I3_X_UNSHIFTED_MASK) << IW_T2X1I3_X_LSB) - -#define IW_T3X1_A3_LSB 6 -#define IW_T3X1_A3_SIZE 3 -#define IW_T3X1_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_A3_SIZE)) -#define IW_T3X1_A3_SHIFTED_MASK (IW_T3X1_A3_UNSHIFTED_MASK << IW_T3X1_A3_LSB) -#define GET_IW_T3X1_A3(W) (((W) >> IW_T3X1_A3_LSB) & IW_T3X1_A3_UNSHIFTED_MASK) -#define SET_IW_T3X1_A3(V) (((V) & IW_T3X1_A3_UNSHIFTED_MASK) << IW_T3X1_A3_LSB) - -#define IW_T3X1_B3_LSB 9 -#define IW_T3X1_B3_SIZE 3 -#define IW_T3X1_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_B3_SIZE)) -#define IW_T3X1_B3_SHIFTED_MASK (IW_T3X1_B3_UNSHIFTED_MASK << IW_T3X1_B3_LSB) -#define GET_IW_T3X1_B3(W) (((W) >> IW_T3X1_B3_LSB) & IW_T3X1_B3_UNSHIFTED_MASK) -#define SET_IW_T3X1_B3(V) (((V) & IW_T3X1_B3_UNSHIFTED_MASK) << IW_T3X1_B3_LSB) - -#define IW_T3X1_C3_LSB 12 -#define IW_T3X1_C3_SIZE 3 -#define IW_T3X1_C3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_C3_SIZE)) -#define IW_T3X1_C3_SHIFTED_MASK (IW_T3X1_C3_UNSHIFTED_MASK << IW_T3X1_C3_LSB) -#define GET_IW_T3X1_C3(W) (((W) >> IW_T3X1_C3_LSB) & IW_T3X1_C3_UNSHIFTED_MASK) -#define SET_IW_T3X1_C3(V) (((V) & IW_T3X1_C3_UNSHIFTED_MASK) << IW_T3X1_C3_LSB) - -#define IW_T3X1_X_LSB 15 -#define IW_T3X1_X_SIZE 1 -#define IW_T3X1_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_X_SIZE)) -#define IW_T3X1_X_SHIFTED_MASK (IW_T3X1_X_UNSHIFTED_MASK << IW_T3X1_X_LSB) -#define GET_IW_T3X1_X(W) (((W) >> IW_T3X1_X_LSB) & IW_T3X1_X_UNSHIFTED_MASK) -#define SET_IW_T3X1_X(V) (((V) & IW_T3X1_X_UNSHIFTED_MASK) << IW_T3X1_X_LSB) - -/* The X field for all three R.N-class instruction formats is represented - here as 4 bits, including the bits defined as constant 0 or 1 that - determine which of the formats T2X3, F1X1, or X2L5 it is. */ -#define IW_R_N_X_LSB 12 -#define IW_R_N_X_SIZE 4 -#define IW_R_N_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_N_X_SIZE)) -#define IW_R_N_X_SHIFTED_MASK (IW_R_N_X_UNSHIFTED_MASK << IW_R_N_X_LSB) -#define GET_IW_R_N_X(W) (((W) >> IW_R_N_X_LSB) & IW_R_N_X_UNSHIFTED_MASK) -#define SET_IW_R_N_X(V) (((V) & IW_R_N_X_UNSHIFTED_MASK) << IW_R_N_X_LSB) - -#define IW_T2X3_A3_LSB 6 -#define IW_T2X3_A3_SIZE 3 -#define IW_T2X3_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X3_A3_SIZE)) -#define IW_T2X3_A3_SHIFTED_MASK (IW_T2X3_A3_UNSHIFTED_MASK << IW_T2X3_A3_LSB) -#define GET_IW_T2X3_A3(W) (((W) >> IW_T2X3_A3_LSB) & IW_T2X3_A3_UNSHIFTED_MASK) -#define SET_IW_T2X3_A3(V) (((V) & IW_T2X3_A3_UNSHIFTED_MASK) << IW_T2X3_A3_LSB) - -#define IW_T2X3_B3_LSB 9 -#define IW_T2X3_B3_SIZE 3 -#define IW_T2X3_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X3_B3_SIZE)) -#define IW_T2X3_B3_SHIFTED_MASK (IW_T2X3_B3_UNSHIFTED_MASK << IW_T2X3_B3_LSB) -#define GET_IW_T2X3_B3(W) (((W) >> IW_T2X3_B3_LSB) & IW_T2X3_B3_UNSHIFTED_MASK) -#define SET_IW_T2X3_B3(V) (((V) & IW_T2X3_B3_UNSHIFTED_MASK) << IW_T2X3_B3_LSB) - -#define IW_F1X1_A_LSB 6 -#define IW_F1X1_A_SIZE 5 -#define IW_F1X1_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X1_A_SIZE)) -#define IW_F1X1_A_SHIFTED_MASK (IW_F1X1_A_UNSHIFTED_MASK << IW_F1X1_A_LSB) -#define GET_IW_F1X1_A(W) (((W) >> IW_F1X1_A_LSB) & IW_F1X1_A_UNSHIFTED_MASK) -#define SET_IW_F1X1_A(V) (((V) & IW_F1X1_A_UNSHIFTED_MASK) << IW_F1X1_A_LSB) - -#define IW_F1X1_RSV_LSB 11 -#define IW_F1X1_RSV_SIZE 1 -#define IW_F1X1_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X1_RSV_SIZE)) -#define IW_F1X1_RSV_SHIFTED_MASK (IW_F1X1_RSV_UNSHIFTED_MASK << IW_F1X1_RSV_LSB) -#define GET_IW_F1X1_RSV(W) (((W) >> IW_F1X1_RSV_LSB) & IW_F1X1_RSV_UNSHIFTED_MASK) -#define SET_IW_F1X1_RSV(V) (((V) & IW_F1X1_RSV_UNSHIFTED_MASK) << IW_F1X1_RSV_LSB) - -#define IW_X2L5_IMM5_LSB 6 -#define IW_X2L5_IMM5_SIZE 5 -#define IW_X2L5_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X2L5_IMM5_SIZE)) -#define IW_X2L5_IMM5_SHIFTED_MASK (IW_X2L5_IMM5_UNSHIFTED_MASK << IW_X2L5_IMM5_LSB) -#define GET_IW_X2L5_IMM5(W) (((W) >> IW_X2L5_IMM5_LSB) & IW_X2L5_IMM5_UNSHIFTED_MASK) -#define SET_IW_X2L5_IMM5(V) (((V) & IW_X2L5_IMM5_UNSHIFTED_MASK) << IW_X2L5_IMM5_LSB) - -#define IW_X2L5_RSV_LSB 11 -#define IW_X2L5_RSV_SIZE 1 -#define IW_X2L5_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X2L5_RSV_SIZE)) -#define IW_X2L5_RSV_SHIFTED_MASK (IW_X2L5_RSV_UNSHIFTED_MASK << IW_X2L5_RSV_LSB) -#define GET_IW_X2L5_RSV(W) (((W) >> IW_X2L5_RSV_LSB) & IW_X2L5_RSV_UNSHIFTED_MASK) -#define SET_IW_X2L5_RSV(V) (((V) & IW_X2L5_RSV_UNSHIFTED_MASK) << IW_X2L5_RSV_LSB) - -#define IW_F1I5_IMM5_LSB 6 -#define IW_F1I5_IMM5_SIZE 5 -#define IW_F1I5_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1I5_IMM5_SIZE)) -#define IW_F1I5_IMM5_SHIFTED_MASK (IW_F1I5_IMM5_UNSHIFTED_MASK << IW_F1I5_IMM5_LSB) -#define GET_IW_F1I5_IMM5(W) (((W) >> IW_F1I5_IMM5_LSB) & IW_F1I5_IMM5_UNSHIFTED_MASK) -#define SET_IW_F1I5_IMM5(V) (((V) & IW_F1I5_IMM5_UNSHIFTED_MASK) << IW_F1I5_IMM5_LSB) - -#define IW_F1I5_B_LSB 11 -#define IW_F1I5_B_SIZE 5 -#define IW_F1I5_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1I5_B_SIZE)) -#define IW_F1I5_B_SHIFTED_MASK (IW_F1I5_B_UNSHIFTED_MASK << IW_F1I5_B_LSB) -#define GET_IW_F1I5_B(W) (((W) >> IW_F1I5_B_LSB) & IW_F1I5_B_UNSHIFTED_MASK) -#define SET_IW_F1I5_B(V) (((V) & IW_F1I5_B_UNSHIFTED_MASK) << IW_F1I5_B_LSB) - -#define IW_F2_A_LSB 6 -#define IW_F2_A_SIZE 5 -#define IW_F2_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2_A_SIZE)) -#define IW_F2_A_SHIFTED_MASK (IW_F2_A_UNSHIFTED_MASK << IW_F2_A_LSB) -#define GET_IW_F2_A(W) (((W) >> IW_F2_A_LSB) & IW_F2_A_UNSHIFTED_MASK) -#define SET_IW_F2_A(V) (((V) & IW_F2_A_UNSHIFTED_MASK) << IW_F2_A_LSB) - -#define IW_F2_B_LSB 11 -#define IW_F2_B_SIZE 5 -#define IW_F2_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2_B_SIZE)) -#define IW_F2_B_SHIFTED_MASK (IW_F2_B_UNSHIFTED_MASK << IW_F2_B_LSB) -#define GET_IW_F2_B(W) (((W) >> IW_F2_B_LSB) & IW_F2_B_UNSHIFTED_MASK) -#define SET_IW_F2_B(V) (((V) & IW_F2_B_UNSHIFTED_MASK) << IW_F2_B_LSB) - -/* R2 opcodes. */ -#define R2_OP_CALL 0 -#define R2_OP_AS_N 1 -#define R2_OP_BR 2 -#define R2_OP_BR_N 3 -#define R2_OP_ADDI 4 -#define R2_OP_LDBU_N 5 -#define R2_OP_LDBU 6 -#define R2_OP_LDB 7 -#define R2_OP_JMPI 8 -#define R2_OP_R_N 9 -#define R2_OP_ANDI_N 11 -#define R2_OP_ANDI 12 -#define R2_OP_LDHU_N 13 -#define R2_OP_LDHU 14 -#define R2_OP_LDH 15 -#define R2_OP_ASI_N 17 -#define R2_OP_BGE 18 -#define R2_OP_LDWSP_N 19 -#define R2_OP_ORI 20 -#define R2_OP_LDW_N 21 -#define R2_OP_CMPGEI 22 -#define R2_OP_LDW 23 -#define R2_OP_SHI_N 25 -#define R2_OP_BLT 26 -#define R2_OP_MOVI_N 27 -#define R2_OP_XORI 28 -#define R2_OP_STZ_N 29 -#define R2_OP_CMPLTI 30 -#define R2_OP_ANDCI 31 -#define R2_OP_OPX 32 -#define R2_OP_PP_N 33 -#define R2_OP_BNE 34 -#define R2_OP_BNEZ_N 35 -#define R2_OP_MULI 36 -#define R2_OP_STB_N 37 -#define R2_OP_CMPNEI 38 -#define R2_OP_STB 39 -#define R2_OP_I12 40 -#define R2_OP_SPI_N 41 -#define R2_OP_BEQ 42 -#define R2_OP_BEQZ_N 43 -#define R2_OP_ANDHI 44 -#define R2_OP_STH_N 45 -#define R2_OP_CMPEQI 46 -#define R2_OP_STH 47 -#define R2_OP_CUSTOM 48 -#define R2_OP_BGEU 50 -#define R2_OP_STWSP_N 51 -#define R2_OP_ORHI 52 -#define R2_OP_STW_N 53 -#define R2_OP_CMPGEUI 54 -#define R2_OP_STW 55 -#define R2_OP_BLTU 58 -#define R2_OP_MOV_N 59 -#define R2_OP_XORHI 60 -#define R2_OP_SPADDI_N 61 -#define R2_OP_CMPLTUI 62 -#define R2_OP_ANDCHI 63 - -#define R2_OPX_WRPIE 0 -#define R2_OPX_ERET 1 -#define R2_OPX_ROLI 2 -#define R2_OPX_ROL 3 -#define R2_OPX_FLUSHP 4 -#define R2_OPX_RET 5 -#define R2_OPX_NOR 6 -#define R2_OPX_MULXUU 7 -#define R2_OPX_ENI 8 -#define R2_OPX_BRET 9 -#define R2_OPX_ROR 11 -#define R2_OPX_FLUSHI 12 -#define R2_OPX_JMP 13 -#define R2_OPX_AND 14 -#define R2_OPX_CMPGE 16 -#define R2_OPX_SLLI 18 -#define R2_OPX_SLL 19 -#define R2_OPX_WRPRS 20 -#define R2_OPX_OR 22 -#define R2_OPX_MULXSU 23 -#define R2_OPX_CMPLT 24 -#define R2_OPX_SRLI 26 -#define R2_OPX_SRL 27 -#define R2_OPX_NEXTPC 28 -#define R2_OPX_CALLR 29 -#define R2_OPX_XOR 30 -#define R2_OPX_MULXSS 31 -#define R2_OPX_CMPNE 32 -#define R2_OPX_INSERT 35 -#define R2_OPX_DIVU 36 -#define R2_OPX_DIV 37 -#define R2_OPX_RDCTL 38 -#define R2_OPX_MUL 39 -#define R2_OPX_CMPEQ 40 -#define R2_OPX_INITI 41 -#define R2_OPX_MERGE 43 -#define R2_OPX_HBREAK 44 -#define R2_OPX_TRAP 45 -#define R2_OPX_WRCTL 46 -#define R2_OPX_CMPGEU 48 -#define R2_OPX_ADD 49 -#define R2_OPX_EXTRACT 51 -#define R2_OPX_BREAK 52 -#define R2_OPX_LDEX 53 -#define R2_OPX_SYNC 54 -#define R2_OPX_LDSEX 55 -#define R2_OPX_CMPLTU 56 -#define R2_OPX_SUB 57 -#define R2_OPX_SRAI 58 -#define R2_OPX_SRA 59 -#define R2_OPX_STEX 61 -#define R2_OPX_STSEX 63 - -#define R2_I12_LDBIO 0 -#define R2_I12_STBIO 1 -#define R2_I12_LDBUIO 2 -#define R2_I12_DCACHE 3 -#define R2_I12_LDHIO 4 -#define R2_I12_STHIO 5 -#define R2_I12_LDHUIO 6 -#define R2_I12_RDPRS 7 -#define R2_I12_LDWIO 8 -#define R2_I12_STWIO 9 -#define R2_I12_LDWM 12 -#define R2_I12_STWM 13 - -#define R2_DCACHE_INITD 0 -#define R2_DCACHE_INITDA 1 -#define R2_DCACHE_FLUSHD 2 -#define R2_DCACHE_FLUSHDA 3 - -#define R2_AS_N_ADD_N 0 -#define R2_AS_N_SUB_N 1 - -#define R2_R_N_AND_N 0 -#define R2_R_N_OR_N 2 -#define R2_R_N_XOR_N 3 -#define R2_R_N_SLL_N 4 -#define R2_R_N_SRL_N 5 -#define R2_R_N_NOT_N 6 -#define R2_R_N_NEG_N 7 -#define R2_R_N_CALLR_N 8 -#define R2_R_N_JMPR_N 10 -#define R2_R_N_BREAK_N 12 -#define R2_R_N_TRAP_N 13 -#define R2_R_N_RET_N 14 - -#define R2_SPI_N_SPINCI_N 0 -#define R2_SPI_N_SPDECI_N 1 - -#define R2_ASI_N_ADDI_N 0 -#define R2_ASI_N_SUBI_N 1 - -#define R2_SHI_N_SLLI_N 0 -#define R2_SHI_N_SRLI_N 1 - -#define R2_PP_N_POP_N 0 -#define R2_PP_N_PUSH_N 1 - -#define R2_STZ_N_STWZ_N 0 -#define R2_STZ_N_STBZ_N 1 - -/* Convenience macros for R2 encodings. */ - -#define MATCH_R2_OP(NAME) \ - (SET_IW_R2_OP (R2_OP_##NAME)) -#define MASK_R2_OP \ - IW_R2_OP_SHIFTED_MASK - -#define MATCH_R2_OPX0(NAME) \ - (SET_IW_R2_OP (R2_OP_OPX) | SET_IW_OPX_X (R2_OPX_##NAME)) -#define MASK_R2_OPX0 \ - (IW_R2_OP_SHIFTED_MASK | IW_OPX_X_SHIFTED_MASK \ - | IW_F3X6L5_IMM5_SHIFTED_MASK) - -#define MATCH_R2_OPX(NAME, A, B, C) \ - (MATCH_R2_OPX0 (NAME) | SET_IW_F3X6L5_A (A) | SET_IW_F3X6L5_B (B) \ - | SET_IW_F3X6L5_C (C)) -#define MASK_R2_OPX(A, B, C, N) \ - (IW_R2_OP_SHIFTED_MASK | IW_OPX_X_SHIFTED_MASK \ - | (A ? IW_F3X6L5_A_SHIFTED_MASK : 0) \ - | (B ? IW_F3X6L5_B_SHIFTED_MASK : 0) \ - | (C ? IW_F3X6L5_C_SHIFTED_MASK : 0) \ - | (N ? IW_F3X6L5_IMM5_SHIFTED_MASK : 0)) - -#define MATCH_R2_I12(NAME) \ - (SET_IW_R2_OP (R2_OP_I12) | SET_IW_I12_X (R2_I12_##NAME)) -#define MASK_R2_I12 \ - (IW_R2_OP_SHIFTED_MASK | IW_I12_X_SHIFTED_MASK ) - -#define MATCH_R2_DCACHE(NAME) \ - (MATCH_R2_I12(DCACHE) | SET_IW_F1X4I12_X (R2_DCACHE_##NAME)) -#define MASK_R2_DCACHE \ - (MASK_R2_I12 | IW_F1X4I12_X_SHIFTED_MASK) - -#define MATCH_R2_R_N(NAME) \ - (SET_IW_R2_OP (R2_OP_R_N) | SET_IW_R_N_X (R2_R_N_##NAME)) -#define MASK_R2_R_N \ - (IW_R2_OP_SHIFTED_MASK | IW_R_N_X_SHIFTED_MASK ) - -/* Match/mask macros for R2 instructions. */ - -#define MATCH_R2_ADD MATCH_R2_OPX0 (ADD) -#define MASK_R2_ADD MASK_R2_OPX0 -#define MATCH_R2_ADDI MATCH_R2_OP (ADDI) -#define MASK_R2_ADDI MASK_R2_OP -#define MATCH_R2_ADD_N (MATCH_R2_OP (AS_N) | SET_IW_T3X1_X (R2_AS_N_ADD_N)) -#define MASK_R2_ADD_N (MASK_R2_OP | IW_T3X1_X_SHIFTED_MASK) -#define MATCH_R2_ADDI_N (MATCH_R2_OP (ASI_N) | SET_IW_T2X1I3_X (R2_ASI_N_ADDI_N)) -#define MASK_R2_ADDI_N (MASK_R2_OP | IW_T2X1I3_X_SHIFTED_MASK) -#define MATCH_R2_AND MATCH_R2_OPX0 (AND) -#define MASK_R2_AND MASK_R2_OPX0 -#define MATCH_R2_ANDCHI MATCH_R2_OP (ANDCHI) -#define MASK_R2_ANDCHI MASK_R2_OP -#define MATCH_R2_ANDCI MATCH_R2_OP (ANDCI) -#define MASK_R2_ANDCI MASK_R2_OP -#define MATCH_R2_ANDHI MATCH_R2_OP (ANDHI) -#define MASK_R2_ANDHI MASK_R2_OP -#define MATCH_R2_ANDI MATCH_R2_OP (ANDI) -#define MASK_R2_ANDI MASK_R2_OP -#define MATCH_R2_ANDI_N MATCH_R2_OP (ANDI_N) -#define MASK_R2_ANDI_N MASK_R2_OP -#define MATCH_R2_AND_N MATCH_R2_R_N (AND_N) -#define MASK_R2_AND_N MASK_R2_R_N -#define MATCH_R2_BEQ MATCH_R2_OP (BEQ) -#define MASK_R2_BEQ MASK_R2_OP -#define MATCH_R2_BEQZ_N MATCH_R2_OP (BEQZ_N) -#define MASK_R2_BEQZ_N MASK_R2_OP -#define MATCH_R2_BGE MATCH_R2_OP (BGE) -#define MASK_R2_BGE MASK_R2_OP -#define MATCH_R2_BGEU MATCH_R2_OP (BGEU) -#define MASK_R2_BGEU MASK_R2_OP -#define MATCH_R2_BGT MATCH_R2_OP (BLT) -#define MASK_R2_BGT MASK_R2_OP -#define MATCH_R2_BGTU MATCH_R2_OP (BLTU) -#define MASK_R2_BGTU MASK_R2_OP -#define MATCH_R2_BLE MATCH_R2_OP (BGE) -#define MASK_R2_BLE MASK_R2_OP -#define MATCH_R2_BLEU MATCH_R2_OP (BGEU) -#define MASK_R2_BLEU MASK_R2_OP -#define MATCH_R2_BLT MATCH_R2_OP (BLT) -#define MASK_R2_BLT MASK_R2_OP -#define MATCH_R2_BLTU MATCH_R2_OP (BLTU) -#define MASK_R2_BLTU MASK_R2_OP -#define MATCH_R2_BNE MATCH_R2_OP (BNE) -#define MASK_R2_BNE MASK_R2_OP -#define MATCH_R2_BNEZ_N MATCH_R2_OP (BNEZ_N) -#define MASK_R2_BNEZ_N MASK_R2_OP -#define MATCH_R2_BR MATCH_R2_OP (BR) -#define MASK_R2_BR MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK | IW_F2I16_B_SHIFTED_MASK -#define MATCH_R2_BREAK MATCH_R2_OPX (BREAK, 0, 0, 0x1e) -#define MASK_R2_BREAK MASK_R2_OPX (1, 1, 1, 0) -#define MATCH_R2_BREAK_N MATCH_R2_R_N (BREAK_N) -#define MASK_R2_BREAK_N MASK_R2_R_N -#define MATCH_R2_BRET MATCH_R2_OPX (BRET, 0x1e, 0, 0) -#define MASK_R2_BRET MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_BR_N MATCH_R2_OP (BR_N) -#define MASK_R2_BR_N MASK_R2_OP -#define MATCH_R2_CALL MATCH_R2_OP (CALL) -#define MASK_R2_CALL MASK_R2_OP -#define MATCH_R2_CALLR MATCH_R2_OPX (CALLR, 0, 0, 0x1f) -#define MASK_R2_CALLR MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_CALLR_N MATCH_R2_R_N (CALLR_N) -#define MASK_R2_CALLR_N MASK_R2_R_N -#define MATCH_R2_CMPEQ MATCH_R2_OPX0 (CMPEQ) -#define MASK_R2_CMPEQ MASK_R2_OPX0 -#define MATCH_R2_CMPEQI MATCH_R2_OP (CMPEQI) -#define MASK_R2_CMPEQI MASK_R2_OP -#define MATCH_R2_CMPGE MATCH_R2_OPX0 (CMPGE) -#define MASK_R2_CMPGE MASK_R2_OPX0 -#define MATCH_R2_CMPGEI MATCH_R2_OP (CMPGEI) -#define MASK_R2_CMPGEI MASK_R2_OP -#define MATCH_R2_CMPGEU MATCH_R2_OPX0 (CMPGEU) -#define MASK_R2_CMPGEU MASK_R2_OPX0 -#define MATCH_R2_CMPGEUI MATCH_R2_OP (CMPGEUI) -#define MASK_R2_CMPGEUI MASK_R2_OP -#define MATCH_R2_CMPGT MATCH_R2_OPX0 (CMPLT) -#define MASK_R2_CMPGT MASK_R2_OPX0 -#define MATCH_R2_CMPGTI MATCH_R2_OP (CMPGEI) -#define MASK_R2_CMPGTI MASK_R2_OP -#define MATCH_R2_CMPGTU MATCH_R2_OPX0 (CMPLTU) -#define MASK_R2_CMPGTU MASK_R2_OPX0 -#define MATCH_R2_CMPGTUI MATCH_R2_OP (CMPGEUI) -#define MASK_R2_CMPGTUI MASK_R2_OP -#define MATCH_R2_CMPLE MATCH_R2_OPX0 (CMPGE) -#define MASK_R2_CMPLE MASK_R2_OPX0 -#define MATCH_R2_CMPLEI MATCH_R2_OP (CMPLTI) -#define MASK_R2_CMPLEI MASK_R2_OP -#define MATCH_R2_CMPLEU MATCH_R2_OPX0 (CMPGEU) -#define MASK_R2_CMPLEU MASK_R2_OPX0 -#define MATCH_R2_CMPLEUI MATCH_R2_OP (CMPLTUI) -#define MASK_R2_CMPLEUI MASK_R2_OP -#define MATCH_R2_CMPLT MATCH_R2_OPX0 (CMPLT) -#define MASK_R2_CMPLT MASK_R2_OPX0 -#define MATCH_R2_CMPLTI MATCH_R2_OP (CMPLTI) -#define MASK_R2_CMPLTI MASK_R2_OP -#define MATCH_R2_CMPLTU MATCH_R2_OPX0 (CMPLTU) -#define MASK_R2_CMPLTU MASK_R2_OPX0 -#define MATCH_R2_CMPLTUI MATCH_R2_OP (CMPLTUI) -#define MASK_R2_CMPLTUI MASK_R2_OP -#define MATCH_R2_CMPNE MATCH_R2_OPX0 (CMPNE) -#define MASK_R2_CMPNE MASK_R2_OPX0 -#define MATCH_R2_CMPNEI MATCH_R2_OP (CMPNEI) -#define MASK_R2_CMPNEI MASK_R2_OP -#define MATCH_R2_CUSTOM MATCH_R2_OP (CUSTOM) -#define MASK_R2_CUSTOM MASK_R2_OP -#define MATCH_R2_DIV MATCH_R2_OPX0 (DIV) -#define MASK_R2_DIV MASK_R2_OPX0 -#define MATCH_R2_DIVU MATCH_R2_OPX0 (DIVU) -#define MASK_R2_DIVU MASK_R2_OPX0 -#define MATCH_R2_ENI MATCH_R2_OPX (ENI, 0, 0, 0) -#define MASK_R2_ENI MASK_R2_OPX (1, 1, 1, 0) -#define MATCH_R2_ERET MATCH_R2_OPX (ERET, 0x1d, 0x1e, 0) -#define MASK_R2_ERET MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_EXTRACT MATCH_R2_OPX (EXTRACT, 0, 0, 0) -#define MASK_R2_EXTRACT MASK_R2_OPX (0, 0, 0, 0) -#define MATCH_R2_FLUSHD MATCH_R2_DCACHE (FLUSHD) -#define MASK_R2_FLUSHD MASK_R2_DCACHE -#define MATCH_R2_FLUSHDA MATCH_R2_DCACHE (FLUSHDA) -#define MASK_R2_FLUSHDA MASK_R2_DCACHE -#define MATCH_R2_FLUSHI MATCH_R2_OPX (FLUSHI, 0, 0, 0) -#define MASK_R2_FLUSHI MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_FLUSHP MATCH_R2_OPX (FLUSHP, 0, 0, 0) -#define MASK_R2_FLUSHP MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_INITD MATCH_R2_DCACHE (INITD) -#define MASK_R2_INITD MASK_R2_DCACHE -#define MATCH_R2_INITDA MATCH_R2_DCACHE (INITDA) -#define MASK_R2_INITDA MASK_R2_DCACHE -#define MATCH_R2_INITI MATCH_R2_OPX (INITI, 0, 0, 0) -#define MASK_R2_INITI MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_INSERT MATCH_R2_OPX (INSERT, 0, 0, 0) -#define MASK_R2_INSERT MASK_R2_OPX (0, 0, 0, 0) -#define MATCH_R2_JMP MATCH_R2_OPX (JMP, 0, 0, 0) -#define MASK_R2_JMP MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_JMPI MATCH_R2_OP (JMPI) -#define MASK_R2_JMPI MASK_R2_OP -#define MATCH_R2_JMPR_N MATCH_R2_R_N (JMPR_N) -#define MASK_R2_JMPR_N MASK_R2_R_N -#define MATCH_R2_LDB MATCH_R2_OP (LDB) -#define MASK_R2_LDB MASK_R2_OP -#define MATCH_R2_LDBIO MATCH_R2_I12 (LDBIO) -#define MASK_R2_LDBIO MASK_R2_I12 -#define MATCH_R2_LDBU MATCH_R2_OP (LDBU) -#define MASK_R2_LDBU MASK_R2_OP -#define MATCH_R2_LDBUIO MATCH_R2_I12 (LDBUIO) -#define MASK_R2_LDBUIO MASK_R2_I12 -#define MATCH_R2_LDBU_N MATCH_R2_OP (LDBU_N) -#define MASK_R2_LDBU_N MASK_R2_OP -#define MATCH_R2_LDEX MATCH_R2_OPX (LDEX, 0, 0, 0) -#define MASK_R2_LDEX MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_LDH MATCH_R2_OP (LDH) -#define MASK_R2_LDH MASK_R2_OP -#define MATCH_R2_LDHIO MATCH_R2_I12 (LDHIO) -#define MASK_R2_LDHIO MASK_R2_I12 -#define MATCH_R2_LDHU MATCH_R2_OP (LDHU) -#define MASK_R2_LDHU MASK_R2_OP -#define MATCH_R2_LDHUIO MATCH_R2_I12 (LDHUIO) -#define MASK_R2_LDHUIO MASK_R2_I12 -#define MATCH_R2_LDHU_N MATCH_R2_OP (LDHU_N) -#define MASK_R2_LDHU_N MASK_R2_OP -#define MATCH_R2_LDSEX MATCH_R2_OPX (LDSEX, 0, 0, 0) -#define MASK_R2_LDSEX MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_LDW MATCH_R2_OP (LDW) -#define MASK_R2_LDW MASK_R2_OP -#define MATCH_R2_LDWIO MATCH_R2_I12 (LDWIO) -#define MASK_R2_LDWIO MASK_R2_I12 -#define MATCH_R2_LDWM MATCH_R2_I12 (LDWM) -#define MASK_R2_LDWM MASK_R2_I12 -#define MATCH_R2_LDWSP_N MATCH_R2_OP (LDWSP_N) -#define MASK_R2_LDWSP_N MASK_R2_OP -#define MATCH_R2_LDW_N MATCH_R2_OP (LDW_N) -#define MASK_R2_LDW_N MASK_R2_OP -#define MATCH_R2_MERGE MATCH_R2_OPX (MERGE, 0, 0, 0) -#define MASK_R2_MERGE MASK_R2_OPX (0, 0, 0, 0) -#define MATCH_R2_MOV MATCH_R2_OPX (ADD, 0, 0, 0) -#define MASK_R2_MOV MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_MOVHI MATCH_R2_OP (ORHI) | SET_IW_F2I16_A (0) -#define MASK_R2_MOVHI MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK -#define MATCH_R2_MOVI MATCH_R2_OP (ADDI) | SET_IW_F2I16_A (0) -#define MASK_R2_MOVI MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK -#define MATCH_R2_MOVUI MATCH_R2_OP (ORI) | SET_IW_F2I16_A (0) -#define MASK_R2_MOVUI MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK -#define MATCH_R2_MOV_N MATCH_R2_OP (MOV_N) -#define MASK_R2_MOV_N MASK_R2_OP -#define MATCH_R2_MOVI_N MATCH_R2_OP (MOVI_N) -#define MASK_R2_MOVI_N MASK_R2_OP -#define MATCH_R2_MUL MATCH_R2_OPX0 (MUL) -#define MASK_R2_MUL MASK_R2_OPX0 -#define MATCH_R2_MULI MATCH_R2_OP (MULI) -#define MASK_R2_MULI MASK_R2_OP -#define MATCH_R2_MULXSS MATCH_R2_OPX0 (MULXSS) -#define MASK_R2_MULXSS MASK_R2_OPX0 -#define MATCH_R2_MULXSU MATCH_R2_OPX0 (MULXSU) -#define MASK_R2_MULXSU MASK_R2_OPX0 -#define MATCH_R2_MULXUU MATCH_R2_OPX0 (MULXUU) -#define MASK_R2_MULXUU MASK_R2_OPX0 -#define MATCH_R2_NEG_N MATCH_R2_R_N (NEG_N) -#define MASK_R2_NEG_N MASK_R2_R_N -#define MATCH_R2_NEXTPC MATCH_R2_OPX (NEXTPC, 0, 0, 0) -#define MASK_R2_NEXTPC MASK_R2_OPX (1, 1, 0, 1) -#define MATCH_R2_NOP MATCH_R2_OPX (ADD, 0, 0, 0) -#define MASK_R2_NOP MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_NOP_N (MATCH_R2_OP (MOV_N) | SET_IW_F2_A (0) | SET_IW_F2_B (0)) -#define MASK_R2_NOP_N (MASK_R2_OP | IW_F2_A_SHIFTED_MASK | IW_F2_B_SHIFTED_MASK) -#define MATCH_R2_NOR MATCH_R2_OPX0 (NOR) -#define MASK_R2_NOR MASK_R2_OPX0 -#define MATCH_R2_NOT_N MATCH_R2_R_N (NOT_N) -#define MASK_R2_NOT_N MASK_R2_R_N -#define MATCH_R2_OR MATCH_R2_OPX0 (OR) -#define MASK_R2_OR MASK_R2_OPX0 -#define MATCH_R2_OR_N MATCH_R2_R_N (OR_N) -#define MASK_R2_OR_N MASK_R2_R_N -#define MATCH_R2_ORHI MATCH_R2_OP (ORHI) -#define MASK_R2_ORHI MASK_R2_OP -#define MATCH_R2_ORI MATCH_R2_OP (ORI) -#define MASK_R2_ORI MASK_R2_OP -#define MATCH_R2_POP_N (MATCH_R2_OP (PP_N) | SET_IW_L5I4X1_X (R2_PP_N_POP_N)) -#define MASK_R2_POP_N (MASK_R2_OP | IW_L5I4X1_X_SHIFTED_MASK) -#define MATCH_R2_PUSH_N (MATCH_R2_OP (PP_N) | SET_IW_L5I4X1_X (R2_PP_N_PUSH_N)) -#define MASK_R2_PUSH_N (MASK_R2_OP | IW_L5I4X1_X_SHIFTED_MASK) -#define MATCH_R2_RDCTL MATCH_R2_OPX (RDCTL, 0, 0, 0) -#define MASK_R2_RDCTL MASK_R2_OPX (1, 1, 0, 0) -#define MATCH_R2_RDPRS MATCH_R2_I12 (RDPRS) -#define MASK_R2_RDPRS MASK_R2_I12 -#define MATCH_R2_RET MATCH_R2_OPX (RET, 0x1f, 0, 0) -#define MASK_R2_RET MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_RET_N (MATCH_R2_R_N (RET_N) | SET_IW_X2L5_IMM5 (0)) -#define MASK_R2_RET_N (MASK_R2_R_N | IW_X2L5_IMM5_SHIFTED_MASK) -#define MATCH_R2_ROL MATCH_R2_OPX0 (ROL) -#define MASK_R2_ROL MASK_R2_OPX0 -#define MATCH_R2_ROLI MATCH_R2_OPX (ROLI, 0, 0, 0) -#define MASK_R2_ROLI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_ROR MATCH_R2_OPX0 (ROR) -#define MASK_R2_ROR MASK_R2_OPX0 -#define MATCH_R2_SLL MATCH_R2_OPX0 (SLL) -#define MASK_R2_SLL MASK_R2_OPX0 -#define MATCH_R2_SLLI MATCH_R2_OPX (SLLI, 0, 0, 0) -#define MASK_R2_SLLI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_SLL_N MATCH_R2_R_N (SLL_N) -#define MASK_R2_SLL_N MASK_R2_R_N -#define MATCH_R2_SLLI_N (MATCH_R2_OP (SHI_N) | SET_IW_T2X1L3_X (R2_SHI_N_SLLI_N)) -#define MASK_R2_SLLI_N (MASK_R2_OP | IW_T2X1L3_X_SHIFTED_MASK) -#define MATCH_R2_SPADDI_N MATCH_R2_OP (SPADDI_N) -#define MASK_R2_SPADDI_N MASK_R2_OP -#define MATCH_R2_SPDECI_N (MATCH_R2_OP (SPI_N) | SET_IW_X1I7_X (R2_SPI_N_SPDECI_N)) -#define MASK_R2_SPDECI_N (MASK_R2_OP | IW_X1I7_X_SHIFTED_MASK) -#define MATCH_R2_SPINCI_N (MATCH_R2_OP (SPI_N) | SET_IW_X1I7_X (R2_SPI_N_SPINCI_N)) -#define MASK_R2_SPINCI_N (MASK_R2_OP | IW_X1I7_X_SHIFTED_MASK) -#define MATCH_R2_SRA MATCH_R2_OPX0 (SRA) -#define MASK_R2_SRA MASK_R2_OPX0 -#define MATCH_R2_SRAI MATCH_R2_OPX (SRAI, 0, 0, 0) -#define MASK_R2_SRAI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_SRL MATCH_R2_OPX0 (SRL) -#define MASK_R2_SRL MASK_R2_OPX0 -#define MATCH_R2_SRLI MATCH_R2_OPX (SRLI, 0, 0, 0) -#define MASK_R2_SRLI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_SRL_N MATCH_R2_R_N (SRL_N) -#define MASK_R2_SRL_N MASK_R2_R_N -#define MATCH_R2_SRLI_N (MATCH_R2_OP (SHI_N) | SET_IW_T2X1L3_X (R2_SHI_N_SRLI_N)) -#define MASK_R2_SRLI_N (MASK_R2_OP | IW_T2X1L3_X_SHIFTED_MASK) -#define MATCH_R2_STB MATCH_R2_OP (STB) -#define MASK_R2_STB MASK_R2_OP -#define MATCH_R2_STBIO MATCH_R2_I12 (STBIO) -#define MASK_R2_STBIO MASK_R2_I12 -#define MATCH_R2_STB_N MATCH_R2_OP (STB_N) -#define MASK_R2_STB_N MASK_R2_OP -#define MATCH_R2_STBZ_N (MATCH_R2_OP (STZ_N) | SET_IW_T1X1I6_X (R2_STZ_N_STBZ_N)) -#define MASK_R2_STBZ_N (MASK_R2_OP | IW_T1X1I6_X_SHIFTED_MASK) -#define MATCH_R2_STEX MATCH_R2_OPX0 (STEX) -#define MASK_R2_STEX MASK_R2_OPX0 -#define MATCH_R2_STH MATCH_R2_OP (STH) -#define MASK_R2_STH MASK_R2_OP -#define MATCH_R2_STHIO MATCH_R2_I12 (STHIO) -#define MASK_R2_STHIO MASK_R2_I12 -#define MATCH_R2_STH_N MATCH_R2_OP (STH_N) -#define MASK_R2_STH_N MASK_R2_OP -#define MATCH_R2_STSEX MATCH_R2_OPX0 (STSEX) -#define MASK_R2_STSEX MASK_R2_OPX0 -#define MATCH_R2_STW MATCH_R2_OP (STW) -#define MASK_R2_STW MASK_R2_OP -#define MATCH_R2_STWIO MATCH_R2_I12 (STWIO) -#define MASK_R2_STWIO MASK_R2_I12 -#define MATCH_R2_STWM MATCH_R2_I12 (STWM) -#define MASK_R2_STWM MASK_R2_I12 -#define MATCH_R2_STWSP_N MATCH_R2_OP (STWSP_N) -#define MASK_R2_STWSP_N MASK_R2_OP -#define MATCH_R2_STW_N MATCH_R2_OP (STW_N) -#define MASK_R2_STW_N MASK_R2_OP -#define MATCH_R2_STWZ_N MATCH_R2_OP (STZ_N) -#define MASK_R2_STWZ_N MASK_R2_OP -#define MATCH_R2_SUB MATCH_R2_OPX0 (SUB) -#define MASK_R2_SUB MASK_R2_OPX0 -#define MATCH_R2_SUBI MATCH_R2_OP (ADDI) -#define MASK_R2_SUBI MASK_R2_OP -#define MATCH_R2_SUB_N (MATCH_R2_OP (AS_N) | SET_IW_T3X1_X (R2_AS_N_SUB_N)) -#define MASK_R2_SUB_N (MASK_R2_OP | IW_T3X1_X_SHIFTED_MASK) -#define MATCH_R2_SUBI_N (MATCH_R2_OP (ASI_N) | SET_IW_T2X1I3_X (R2_ASI_N_SUBI_N)) -#define MASK_R2_SUBI_N (MASK_R2_OP | IW_T2X1I3_X_SHIFTED_MASK) -#define MATCH_R2_SYNC MATCH_R2_OPX (SYNC, 0, 0, 0) -#define MASK_R2_SYNC MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_TRAP MATCH_R2_OPX (TRAP, 0, 0, 0x1d) -#define MASK_R2_TRAP MASK_R2_OPX (1, 1, 1, 0) -#define MATCH_R2_TRAP_N MATCH_R2_R_N (TRAP_N) -#define MASK_R2_TRAP_N MASK_R2_R_N -#define MATCH_R2_WRCTL MATCH_R2_OPX (WRCTL, 0, 0, 0) -#define MASK_R2_WRCTL MASK_R2_OPX (0, 1, 1, 0) -#define MATCH_R2_WRPIE MATCH_R2_OPX (WRPIE, 0, 0, 0) -#define MASK_R2_WRPIE MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_WRPRS MATCH_R2_OPX (WRPRS, 0, 0, 0) -#define MASK_R2_WRPRS MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_XOR MATCH_R2_OPX0 (XOR) -#define MASK_R2_XOR MASK_R2_OPX0 -#define MATCH_R2_XORHI MATCH_R2_OP (XORHI) -#define MASK_R2_XORHI MASK_R2_OP -#define MATCH_R2_XORI MATCH_R2_OP (XORI) -#define MASK_R2_XORI MASK_R2_OP -#define MATCH_R2_XOR_N MATCH_R2_R_N (XOR_N) -#define MASK_R2_XOR_N MASK_R2_R_N - -#endif /* _NIOS2R2_H */ - - -/* These are the data structures used to hold the instruction information. */ -extern const struct nios2_opcode nios2_r1_opcodes[]; -extern const int nios2_num_r1_opcodes; -extern const struct nios2_opcode nios2_r2_opcodes[]; -extern const int nios2_num_r2_opcodes; -extern struct nios2_opcode *nios2_opcodes; -extern int nios2_num_opcodes; - -/* These are the data structures used to hold the register information. */ -extern const struct nios2_reg nios2_builtin_regs[]; -extern struct nios2_reg *nios2_regs; -extern const int nios2_num_builtin_regs; -extern int nios2_num_regs; - -/* Return the opcode descriptor for a single instruction. */ -extern const struct nios2_opcode * -nios2_find_opcode_hash (unsigned long, unsigned long); - -/* Lookup tables for R2 immediate decodings. */ -extern unsigned int nios2_r2_asi_n_mappings[]; -extern const int nios2_num_r2_asi_n_mappings; -extern unsigned int nios2_r2_shi_n_mappings[]; -extern const int nios2_num_r2_shi_n_mappings; -extern unsigned int nios2_r2_andi_n_mappings[]; -extern const int nios2_num_r2_andi_n_mappings; - -/* Lookup table for 3-bit register decodings. */ -extern int nios2_r2_reg3_mappings[]; -extern const int nios2_num_r2_reg3_mappings; - -/* Lookup table for REG_RANGE value list decodings. */ -extern unsigned long nios2_r2_reg_range_mappings[]; -extern const int nios2_num_r2_reg_range_mappings; - -#endif /* _NIOS2_H */ - -/*#include "sysdep.h" -#include "opcode/nios2.h" -*/ -/* Register string table */ - -const struct nios2_reg nios2_builtin_regs[] = { - /* Standard register names. */ - {"zero", 0, REG_NORMAL}, - {"at", 1, REG_NORMAL}, /* assembler temporary */ - {"r2", 2, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r3", 3, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r4", 4, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r5", 5, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r6", 6, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r7", 7, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r8", 8, REG_NORMAL | REG_LDWM}, - {"r9", 9, REG_NORMAL | REG_LDWM}, - {"r10", 10, REG_NORMAL | REG_LDWM}, - {"r11", 11, REG_NORMAL | REG_LDWM}, - {"r12", 12, REG_NORMAL | REG_LDWM}, - {"r13", 13, REG_NORMAL | REG_LDWM}, - {"r14", 14, REG_NORMAL | REG_LDWM}, - {"r15", 15, REG_NORMAL | REG_LDWM}, - {"r16", 16, REG_NORMAL | REG_3BIT | REG_LDWM | REG_POP}, - {"r17", 17, REG_NORMAL | REG_3BIT | REG_LDWM | REG_POP}, - {"r18", 18, REG_NORMAL | REG_LDWM | REG_POP}, - {"r19", 19, REG_NORMAL | REG_LDWM | REG_POP}, - {"r20", 20, REG_NORMAL | REG_LDWM | REG_POP}, - {"r21", 21, REG_NORMAL | REG_LDWM | REG_POP}, - {"r22", 22, REG_NORMAL | REG_LDWM | REG_POP}, - {"r23", 23, REG_NORMAL | REG_LDWM | REG_POP}, - {"et", 24, REG_NORMAL}, - {"bt", 25, REG_NORMAL}, - {"gp", 26, REG_NORMAL}, /* global pointer */ - {"sp", 27, REG_NORMAL}, /* stack pointer */ - {"fp", 28, REG_NORMAL | REG_LDWM | REG_POP}, /* frame pointer */ - {"ea", 29, REG_NORMAL}, /* exception return address */ - {"sstatus", 30, REG_NORMAL}, /* saved processor status */ - {"ra", 31, REG_NORMAL | REG_LDWM | REG_POP}, /* return address */ - - /* Alternative names for special registers. */ - {"r0", 0, REG_NORMAL}, - {"r1", 1, REG_NORMAL}, - {"r24", 24, REG_NORMAL}, - {"r25", 25, REG_NORMAL}, - {"r26", 26, REG_NORMAL}, - {"r27", 27, REG_NORMAL}, - {"r28", 28, REG_NORMAL | REG_LDWM | REG_POP}, - {"r29", 29, REG_NORMAL}, - {"r30", 30, REG_NORMAL}, - {"ba", 30, REG_NORMAL}, /* breakpoint return address */ - {"r31", 31, REG_NORMAL | REG_LDWM | REG_POP}, - - /* Control register names. */ - {"status", 0, REG_CONTROL}, - {"estatus", 1, REG_CONTROL}, - {"bstatus", 2, REG_CONTROL}, - {"ienable", 3, REG_CONTROL}, - {"ipending", 4, REG_CONTROL}, - {"cpuid", 5, REG_CONTROL}, - {"ctl6", 6, REG_CONTROL}, - {"exception", 7, REG_CONTROL}, - {"pteaddr", 8, REG_CONTROL}, - {"tlbacc", 9, REG_CONTROL}, - {"tlbmisc", 10, REG_CONTROL}, - {"eccinj", 11, REG_CONTROL}, - {"badaddr", 12, REG_CONTROL}, - {"config", 13, REG_CONTROL}, - {"mpubase", 14, REG_CONTROL}, - {"mpuacc", 15, REG_CONTROL}, - {"ctl16", 16, REG_CONTROL}, - {"ctl17", 17, REG_CONTROL}, - {"ctl18", 18, REG_CONTROL}, - {"ctl19", 19, REG_CONTROL}, - {"ctl20", 20, REG_CONTROL}, - {"ctl21", 21, REG_CONTROL}, - {"ctl22", 22, REG_CONTROL}, - {"ctl23", 23, REG_CONTROL}, - {"ctl24", 24, REG_CONTROL}, - {"ctl25", 25, REG_CONTROL}, - {"ctl26", 26, REG_CONTROL}, - {"ctl27", 27, REG_CONTROL}, - {"ctl28", 28, REG_CONTROL}, - {"ctl29", 29, REG_CONTROL}, - {"ctl30", 30, REG_CONTROL}, - {"ctl31", 31, REG_CONTROL}, - - /* Alternative names for special control registers. */ - {"ctl0", 0, REG_CONTROL}, - {"ctl1", 1, REG_CONTROL}, - {"ctl2", 2, REG_CONTROL}, - {"ctl3", 3, REG_CONTROL}, - {"ctl4", 4, REG_CONTROL}, - {"ctl5", 5, REG_CONTROL}, - {"ctl7", 7, REG_CONTROL}, - {"ctl8", 8, REG_CONTROL}, - {"ctl9", 9, REG_CONTROL}, - {"ctl10", 10, REG_CONTROL}, - {"ctl11", 11, REG_CONTROL}, - {"ctl12", 12, REG_CONTROL}, - {"ctl13", 13, REG_CONTROL}, - {"ctl14", 14, REG_CONTROL}, - {"ctl15", 15, REG_CONTROL}, - - /* Coprocessor register names. */ - {"c0", 0, REG_COPROCESSOR}, - {"c1", 1, REG_COPROCESSOR}, - {"c2", 2, REG_COPROCESSOR}, - {"c3", 3, REG_COPROCESSOR}, - {"c4", 4, REG_COPROCESSOR}, - {"c5", 5, REG_COPROCESSOR}, - {"c6", 6, REG_COPROCESSOR}, - {"c7", 7, REG_COPROCESSOR}, - {"c8", 8, REG_COPROCESSOR}, - {"c9", 9, REG_COPROCESSOR}, - {"c10", 10, REG_COPROCESSOR}, - {"c11", 11, REG_COPROCESSOR}, - {"c12", 12, REG_COPROCESSOR}, - {"c13", 13, REG_COPROCESSOR}, - {"c14", 14, REG_COPROCESSOR}, - {"c15", 15, REG_COPROCESSOR}, - {"c16", 16, REG_COPROCESSOR}, - {"c17", 17, REG_COPROCESSOR}, - {"c18", 18, REG_COPROCESSOR}, - {"c19", 19, REG_COPROCESSOR}, - {"c20", 20, REG_COPROCESSOR}, - {"c21", 21, REG_COPROCESSOR}, - {"c22", 22, REG_COPROCESSOR}, - {"c23", 23, REG_COPROCESSOR}, - {"c24", 24, REG_COPROCESSOR}, - {"c25", 25, REG_COPROCESSOR}, - {"c26", 26, REG_COPROCESSOR}, - {"c27", 27, REG_COPROCESSOR}, - {"c28", 28, REG_COPROCESSOR}, - {"c29", 29, REG_COPROCESSOR}, - {"c30", 30, REG_COPROCESSOR}, - {"c31", 31, REG_COPROCESSOR}, -}; - -#define NIOS2_NUM_REGS \ - ((sizeof nios2_builtin_regs) / (sizeof (nios2_builtin_regs[0]))) -const int nios2_num_builtin_regs = NIOS2_NUM_REGS; - -/* This is not const in order to allow for dynamic extensions to the - built-in instruction set. */ -struct nios2_reg *nios2_regs = (struct nios2_reg *) nios2_builtin_regs; -int nios2_num_regs = NIOS2_NUM_REGS; -#undef NIOS2_NUM_REGS - -/* This is the opcode table used by the Nios II GNU as, disassembler - and GDB. */ -const struct nios2_opcode nios2_r1_opcodes[] = -{ - /* { name, args, args_test, num_args, size, format, - match, mask, pinfo, overflow } */ - {"add", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_ADD, MASK_R1_ADD, 0, no_overflow}, - {"addi", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_ADDI, MASK_R1_ADDI, 0, signed_immed16_overflow}, - {"and", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_AND, MASK_R1_AND, 0, no_overflow}, - {"andhi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ANDHI, MASK_R1_ANDHI, 0, unsigned_immed16_overflow}, - {"andi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ANDI, MASK_R1_ANDI, 0, unsigned_immed16_overflow}, - {"beq", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BEQ, MASK_R1_BEQ, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bge", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGE, MASK_R1_BGE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgeu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGEU, MASK_R1_BGEU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgt", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGT, MASK_R1_BGT, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgtu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGTU, MASK_R1_BGTU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"ble", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLE, MASK_R1_BLE, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bleu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLEU, MASK_R1_BLEU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"blt", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLT, MASK_R1_BLT, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bltu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLTU, MASK_R1_BLTU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bne", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BNE, MASK_R1_BNE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"br", "o", "o,E", 1, 4, iw_i_type, - MATCH_R1_BR, MASK_R1_BR, NIOS2_INSN_UBRANCH, branch_target_overflow}, - {"break", "j", "j,E", 1, 4, iw_r_type, - MATCH_R1_BREAK, MASK_R1_BREAK, NIOS2_INSN_OPTARG, no_overflow}, - {"bret", "", "E", 0, 4, iw_r_type, - MATCH_R1_BRET, MASK_R1_BRET, 0, no_overflow}, - {"call", "m", "m,E", 1, 4, iw_j_type, - MATCH_R1_CALL, MASK_R1_CALL, NIOS2_INSN_CALL, call_target_overflow}, - {"callr", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_CALLR, MASK_R1_CALLR, 0, no_overflow}, - {"cmpeq", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPEQ, MASK_R1_CMPEQ, 0, no_overflow}, - {"cmpeqi", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPEQI, MASK_R1_CMPEQI, 0, signed_immed16_overflow}, - {"cmpge", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGE, MASK_R1_CMPGE, 0, no_overflow}, - {"cmpgei", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPGEI, MASK_R1_CMPGEI, 0, signed_immed16_overflow}, - {"cmpgeu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGEU, MASK_R1_CMPGEU, 0, no_overflow}, - {"cmpgeui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPGEUI, MASK_R1_CMPGEUI, 0, unsigned_immed16_overflow}, - {"cmpgt", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGT, MASK_R1_CMPGT, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgti", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPGTI, MASK_R1_CMPGTI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpgtu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGTU, MASK_R1_CMPGTU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgtui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPGTUI, MASK_R1_CMPGTUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmple", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLE, MASK_R1_CMPLE, NIOS2_INSN_MACRO, no_overflow}, - {"cmplei", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPLEI, MASK_R1_CMPLEI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpleu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLEU, MASK_R1_CMPLEU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpleui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPLEUI, MASK_R1_CMPLEUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmplt", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLT, MASK_R1_CMPLT, 0, no_overflow}, - {"cmplti", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPLTI, MASK_R1_CMPLTI, 0, signed_immed16_overflow}, - {"cmpltu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLTU, MASK_R1_CMPLTU, 0, no_overflow}, - {"cmpltui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPLTUI, MASK_R1_CMPLTUI, 0, unsigned_immed16_overflow}, - {"cmpne", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPNE, MASK_R1_CMPNE, 0, no_overflow}, - {"cmpnei", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPNEI, MASK_R1_CMPNEI, 0, signed_immed16_overflow}, - {"custom", "l,d,s,t", "l,d,s,t,E", 4, 4, iw_custom_type, - MATCH_R1_CUSTOM, MASK_R1_CUSTOM, 0, custom_opcode_overflow}, - {"div", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_DIV, MASK_R1_DIV, 0, no_overflow}, - {"divu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_DIVU, MASK_R1_DIVU, 0, no_overflow}, - {"eret", "", "E", 0, 4, iw_r_type, - MATCH_R1_ERET, MASK_R1_ERET, 0, no_overflow}, - {"flushd", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_FLUSHD, MASK_R1_FLUSHD, 0, address_offset_overflow}, - {"flushda", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_FLUSHDA, MASK_R1_FLUSHDA, 0, address_offset_overflow}, - {"flushi", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_FLUSHI, MASK_R1_FLUSHI, 0, no_overflow}, - {"flushp", "", "E", 0, 4, iw_r_type, - MATCH_R1_FLUSHP, MASK_R1_FLUSHP, 0, no_overflow}, - {"initd", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_INITD, MASK_R1_INITD, 0, address_offset_overflow}, - {"initda", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_INITDA, MASK_R1_INITDA, 0, address_offset_overflow}, - {"initi", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_INITI, MASK_R1_INITI, 0, no_overflow}, - {"jmp", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_JMP, MASK_R1_JMP, 0, no_overflow}, - {"jmpi", "m", "m,E", 1, 4, iw_j_type, - MATCH_R1_JMPI, MASK_R1_JMPI, 0, call_target_overflow}, - {"ldb", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDB, MASK_R1_LDB, 0, address_offset_overflow}, - {"ldbio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDBIO, MASK_R1_LDBIO, 0, address_offset_overflow}, - {"ldbu", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDBU, MASK_R1_LDBU, 0, address_offset_overflow}, - {"ldbuio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDBUIO, MASK_R1_LDBUIO, 0, address_offset_overflow}, - {"ldh", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDH, MASK_R1_LDH, 0, address_offset_overflow}, - {"ldhio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDHIO, MASK_R1_LDHIO, 0, address_offset_overflow}, - {"ldhu", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDHU, MASK_R1_LDHU, 0, address_offset_overflow}, - {"ldhuio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDHUIO, MASK_R1_LDHUIO, 0, address_offset_overflow}, - {"ldw", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDW, MASK_R1_LDW, 0, address_offset_overflow}, - {"ldwio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDWIO, MASK_R1_LDWIO, 0, address_offset_overflow}, - {"mov", "d,s", "d,s,E", 2, 4, iw_r_type, - MATCH_R1_MOV, MASK_R1_MOV, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"movhi", "t,u", "t,u,E", 2, 4, iw_i_type, - MATCH_R1_MOVHI, MASK_R1_MOVHI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"movi", "t,i", "t,i,E", 2, 4, iw_i_type, - MATCH_R1_MOVI, MASK_R1_MOVI, NIOS2_INSN_MACRO_MOVI, signed_immed16_overflow}, - {"movia", "t,o", "t,o,E", 2, 4, iw_i_type, - MATCH_R1_ORHI, MASK_R1_ORHI, NIOS2_INSN_MACRO_MOVIA, no_overflow}, - {"movui", "t,u", "t,u,E", 2, 4, iw_i_type, - MATCH_R1_MOVUI, MASK_R1_MOVUI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"mul", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MUL, MASK_R1_MUL, 0, no_overflow}, - {"muli", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_MULI, MASK_R1_MULI, 0, signed_immed16_overflow}, - {"mulxss", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MULXSS, MASK_R1_MULXSS, 0, no_overflow}, - {"mulxsu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MULXSU, MASK_R1_MULXSU, 0, no_overflow}, - {"mulxuu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MULXUU, MASK_R1_MULXUU, 0, no_overflow}, - {"nextpc", "d", "d,E", 1, 4, iw_r_type, - MATCH_R1_NEXTPC, MASK_R1_NEXTPC, 0, no_overflow}, - {"nop", "", "E", 0, 4, iw_r_type, - MATCH_R1_NOP, MASK_R1_NOP, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"nor", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_NOR, MASK_R1_NOR, 0, no_overflow}, - {"or", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_OR, MASK_R1_OR, 0, no_overflow}, - {"orhi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ORHI, MASK_R1_ORHI, 0, unsigned_immed16_overflow}, - {"ori", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ORI, MASK_R1_ORI, 0, unsigned_immed16_overflow}, - {"rdctl", "d,c", "d,c,E", 2, 4, iw_r_type, - MATCH_R1_RDCTL, MASK_R1_RDCTL, 0, no_overflow}, - {"rdprs", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_RDPRS, MASK_R1_RDPRS, 0, signed_immed16_overflow}, - {"ret", "", "E", 0, 4, iw_r_type, - MATCH_R1_RET, MASK_R1_RET, 0, no_overflow}, - {"rol", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_ROL, MASK_R1_ROL, 0, no_overflow}, - {"roli", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_ROLI, MASK_R1_ROLI, 0, unsigned_immed5_overflow}, - {"ror", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_ROR, MASK_R1_ROR, 0, no_overflow}, - {"sll", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SLL, MASK_R1_SLL, 0, no_overflow}, - {"slli", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_SLLI, MASK_R1_SLLI, 0, unsigned_immed5_overflow}, - {"sra", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SRA, MASK_R1_SRA, 0, no_overflow}, - {"srai", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_SRAI, MASK_R1_SRAI, 0, unsigned_immed5_overflow}, - {"srl", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SRL, MASK_R1_SRL, 0, no_overflow}, - {"srli", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_SRLI, MASK_R1_SRLI, 0, unsigned_immed5_overflow}, - {"stb", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STB, MASK_R1_STB, 0, address_offset_overflow}, - {"stbio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STBIO, MASK_R1_STBIO, 0, address_offset_overflow}, - {"sth", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STH, MASK_R1_STH, 0, address_offset_overflow}, - {"sthio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STHIO, MASK_R1_STHIO, 0, address_offset_overflow}, - {"stw", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STW, MASK_R1_STW, 0, address_offset_overflow}, - {"stwio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STWIO, MASK_R1_STWIO, 0, address_offset_overflow}, - {"sub", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SUB, MASK_R1_SUB, 0, no_overflow}, - {"subi", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_SUBI, MASK_R1_SUBI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"sync", "", "E", 0, 4, iw_r_type, - MATCH_R1_SYNC, MASK_R1_SYNC, 0, no_overflow}, - {"trap", "j", "j,E", 1, 4, iw_r_type, - MATCH_R1_TRAP, MASK_R1_TRAP, NIOS2_INSN_OPTARG, no_overflow}, - {"wrctl", "c,s", "c,s,E", 2, 4, iw_r_type, - MATCH_R1_WRCTL, MASK_R1_WRCTL, 0, no_overflow}, - {"wrprs", "d,s", "d,s,E", 2, 4, iw_r_type, - MATCH_R1_WRPRS, MASK_R1_WRPRS, 0, no_overflow}, - {"xor", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_XOR, MASK_R1_XOR, 0, no_overflow}, - {"xorhi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_XORHI, MASK_R1_XORHI, 0, unsigned_immed16_overflow}, - {"xori", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_XORI, MASK_R1_XORI, 0, unsigned_immed16_overflow} -}; - -#define NIOS2_NUM_R1_OPCODES \ - ((sizeof nios2_r1_opcodes) / (sizeof (nios2_r1_opcodes[0]))) -const int nios2_num_r1_opcodes = NIOS2_NUM_R1_OPCODES; - - -const struct nios2_opcode nios2_r2_opcodes[] = -{ - /* { name, args, args_test, num_args, size, format, - match, mask, pinfo, overflow } */ - {"add", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_ADD, MASK_R2_ADD, 0, no_overflow}, - {"addi", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_ADDI, MASK_R2_ADDI, 0, signed_immed16_overflow}, - {"add.n", "D,S,T", "D,S,T,E", 3, 2, iw_T3X1_type, - MATCH_R2_ADD_N, MASK_R2_ADD_N, 0, no_overflow}, - {"addi.n", "D,S,e", "D,S,e,E", 3, 2, iw_T2X1I3_type, - MATCH_R2_ADDI_N, MASK_R2_ADDI_N, 0, enumeration_overflow}, - {"and", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_AND, MASK_R2_AND, 0, no_overflow}, - {"andchi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDCHI, MASK_R2_ANDCHI, 0, unsigned_immed16_overflow}, - {"andci", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDCI, MASK_R2_ANDCI, 0, unsigned_immed16_overflow}, - {"andhi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDHI, MASK_R2_ANDHI, 0, unsigned_immed16_overflow}, - {"andi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDI, MASK_R2_ANDI, 0, unsigned_immed16_overflow}, - {"andi.n", "T,S,g", "T,S,g,E", 3, 2, iw_T2I4_type, - MATCH_R2_ANDI_N, MASK_R2_ANDI_N, 0, enumeration_overflow}, - {"and.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_AND_N, MASK_R2_AND_N, 0, no_overflow}, - {"beq", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BEQ, MASK_R2_BEQ, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"beqz.n", "S,P", "S,P,E", 2, 2, iw_T1I7_type, - MATCH_R2_BEQZ_N, MASK_R2_BEQZ_N, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bge", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGE, MASK_R2_BGE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgeu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGEU, MASK_R2_BGEU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgt", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGT, MASK_R2_BGT, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgtu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGTU, MASK_R2_BGTU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"ble", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLE, MASK_R2_BLE, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bleu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLEU, MASK_R2_BLEU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"blt", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLT, MASK_R2_BLT, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bltu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLTU, MASK_R2_BLTU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bne", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BNE, MASK_R2_BNE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bnez.n", "S,P", "S,P,E", 2, 2, iw_T1I7_type, - MATCH_R2_BNEZ_N, MASK_R2_BNEZ_N, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"br", "o", "o,E", 1, 4, iw_F2I16_type, - MATCH_R2_BR, MASK_R2_BR, NIOS2_INSN_UBRANCH, branch_target_overflow}, - {"break", "j", "j,E", 1, 4, iw_F3X6L5_type, - MATCH_R2_BREAK, MASK_R2_BREAK, NIOS2_INSN_OPTARG, no_overflow}, - {"break.n", "j", "j,E", 1, 2, iw_X2L5_type, - MATCH_R2_BREAK_N, MASK_R2_BREAK_N, NIOS2_INSN_OPTARG, no_overflow}, - {"bret", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_BRET, MASK_R2_BRET, 0, no_overflow}, - {"br.n", "O", "O,E", 1, 2, iw_I10_type, - MATCH_R2_BR_N, MASK_R2_BR_N, NIOS2_INSN_UBRANCH, branch_target_overflow}, - {"call", "m", "m,E", 1, 4, iw_L26_type, - MATCH_R2_CALL, MASK_R2_CALL, NIOS2_INSN_CALL, call_target_overflow}, - {"callr", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_CALLR, MASK_R2_CALLR, 0, no_overflow}, - {"callr.n", "s", "s,E", 1, 2, iw_F1X1_type, - MATCH_R2_CALLR_N, MASK_R2_CALLR_N, 0, no_overflow}, - {"cmpeq", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPEQ, MASK_R2_CMPEQ, 0, no_overflow}, - {"cmpeqi", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPEQI, MASK_R2_CMPEQI, 0, signed_immed16_overflow}, - {"cmpge", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGE, MASK_R2_CMPGE, 0, no_overflow}, - {"cmpgei", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGEI, MASK_R2_CMPGEI, 0, signed_immed16_overflow}, - {"cmpgeu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGEU, MASK_R2_CMPGEU, 0, no_overflow}, - {"cmpgeui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGEUI, MASK_R2_CMPGEUI, 0, unsigned_immed16_overflow}, - {"cmpgt", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGT, MASK_R2_CMPGT, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgti", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGTI, MASK_R2_CMPGTI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpgtu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGTU, MASK_R2_CMPGTU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgtui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGTUI, MASK_R2_CMPGTUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmple", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLE, MASK_R2_CMPLE, NIOS2_INSN_MACRO, no_overflow}, - {"cmplei", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLEI, MASK_R2_CMPLEI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpleu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLEU, MASK_R2_CMPLEU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpleui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLEUI, MASK_R2_CMPLEUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmplt", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLT, MASK_R2_CMPLT, 0, no_overflow}, - {"cmplti", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLTI, MASK_R2_CMPLTI, 0, signed_immed16_overflow}, - {"cmpltu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLTU, MASK_R2_CMPLTU, 0, no_overflow}, - {"cmpltui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLTUI, MASK_R2_CMPLTUI, 0, unsigned_immed16_overflow}, - {"cmpne", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPNE, MASK_R2_CMPNE, 0, no_overflow}, - {"cmpnei", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPNEI, MASK_R2_CMPNEI, 0, signed_immed16_overflow}, - {"custom", "l,d,s,t", "l,d,s,t,E", 4, 4, iw_F3X8_type, - MATCH_R2_CUSTOM, MASK_R2_CUSTOM, 0, custom_opcode_overflow}, - {"div", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_DIV, MASK_R2_DIV, 0, no_overflow}, - {"divu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_DIVU, MASK_R2_DIVU, 0, no_overflow}, - {"eni", "j", "j,E", 1, 4, iw_F3X6L5_type, - MATCH_R2_ENI, MASK_R2_ENI, NIOS2_INSN_OPTARG, no_overflow}, - {"eret", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_ERET, MASK_R2_ERET, 0, no_overflow}, - {"extract", "t,s,j,k", "t,s,j,k,E", 4, 4, iw_F2X6L10_type, - MATCH_R2_EXTRACT, MASK_R2_EXTRACT, 0, no_overflow}, - {"flushd", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_FLUSHD, MASK_R2_FLUSHD, 0, address_offset_overflow}, - {"flushda", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_FLUSHDA, MASK_R2_FLUSHDA, 0, address_offset_overflow}, - {"flushi", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_FLUSHI, MASK_R2_FLUSHI, 0, no_overflow}, - {"flushp", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_FLUSHP, MASK_R2_FLUSHP, 0, no_overflow}, - {"initd", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_INITD, MASK_R2_INITD, 0, address_offset_overflow}, - {"initda", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_INITDA, MASK_R2_INITDA, 0, address_offset_overflow}, - {"initi", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_INITI, MASK_R2_INITI, 0, no_overflow}, - {"insert", "t,s,j,k", "t,s,j,k,E", 4, 4, iw_F2X6L10_type, - MATCH_R2_INSERT, MASK_R2_INSERT, 0, no_overflow}, - {"jmp", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_JMP, MASK_R2_JMP, 0, no_overflow}, - {"jmpi", "m", "m,E", 1, 4, iw_L26_type, - MATCH_R2_JMPI, MASK_R2_JMPI, 0, call_target_overflow}, - {"jmpr.n", "s", "s,E", 1, 2, iw_F1X1_type, - MATCH_R2_JMPR_N, MASK_R2_JMPR_N, 0, no_overflow}, - {"ldb", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDB, MASK_R2_LDB, 0, address_offset_overflow}, - {"ldbio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDBIO, MASK_R2_LDBIO, 0, signed_immed12_overflow}, - {"ldbu", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDBU, MASK_R2_LDBU, 0, address_offset_overflow}, - {"ldbuio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDBUIO, MASK_R2_LDBUIO, 0, signed_immed12_overflow}, - {"ldbu.n", "T,Y(S)", "T,Y(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_LDBU_N, MASK_R2_LDBU_N, 0, address_offset_overflow}, - {"ldex", "d,(s)", "d,(s),E", 2, 4, iw_F3X6_type, - MATCH_R2_LDEX, MASK_R2_LDEX, 0, no_overflow}, - {"ldh", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDH, MASK_R2_LDH, 0, address_offset_overflow}, - {"ldhio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDHIO, MASK_R2_LDHIO, 0, signed_immed12_overflow}, - {"ldhu", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDHU, MASK_R2_LDHU, 0, address_offset_overflow}, - {"ldhuio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDHUIO, MASK_R2_LDHUIO, 0, signed_immed12_overflow}, - {"ldhu.n", "T,X(S)", "T,X(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_LDHU_N, MASK_R2_LDHU_N, 0, address_offset_overflow}, - {"ldsex", "d,(s)", "d,(s),E", 2, 4, iw_F3X6_type, - MATCH_R2_LDSEX, MASK_R2_LDSEX, 0, no_overflow}, - {"ldw", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDW, MASK_R2_LDW, 0, address_offset_overflow}, - {"ldwio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDWIO, MASK_R2_LDWIO, 0, signed_immed12_overflow}, - {"ldwm", "R,B", "R,B,E", 2, 4, iw_F1X4L17_type, - MATCH_R2_LDWM, MASK_R2_LDWM, 0, no_overflow}, - {"ldw.n", "T,W(S)", "T,W(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_LDW_N, MASK_R2_LDW_N, 0, address_offset_overflow}, - {"ldwsp.n", "t,V(s)", "t,V(s),E", 3, 2, iw_F1I5_type, - MATCH_R2_LDWSP_N, MASK_R2_LDWSP_N, 0, address_offset_overflow}, - {"merge", "t,s,j,k", "t,s,j,k,E", 4, 4, iw_F2X6L10_type, - MATCH_R2_MERGE, MASK_R2_MERGE, 0, no_overflow}, - {"mov", "d,s", "d,s,E", 2, 4, iw_F3X6_type, - MATCH_R2_MOV, MASK_R2_MOV, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"mov.n", "d,s", "d,s,E", 2, 2, iw_F2_type, - MATCH_R2_MOV_N, MASK_R2_MOV_N, 0, no_overflow}, - {"movi.n", "D,h", "D,h,E", 2, 2, iw_T1I7_type, - MATCH_R2_MOVI_N, MASK_R2_MOVI_N, 0, enumeration_overflow}, - {"movhi", "t,u", "t,u,E", 2, 4, iw_F2I16_type, - MATCH_R2_MOVHI, MASK_R2_MOVHI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"movi", "t,i", "t,i,E", 2, 4, iw_F2I16_type, - MATCH_R2_MOVI, MASK_R2_MOVI, NIOS2_INSN_MACRO_MOVI, signed_immed16_overflow}, - {"movia", "t,o", "t,o,E", 2, 4, iw_F2I16_type, - MATCH_R2_ORHI, MASK_R2_ORHI, NIOS2_INSN_MACRO_MOVIA, no_overflow}, - {"movui", "t,u", "t,u,E", 2, 4, iw_F2I16_type, - MATCH_R2_MOVUI, MASK_R2_MOVUI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"mul", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MUL, MASK_R2_MUL, 0, no_overflow}, - {"muli", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_MULI, MASK_R2_MULI, 0, signed_immed16_overflow}, - {"mulxss", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MULXSS, MASK_R2_MULXSS, 0, no_overflow}, - {"mulxsu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MULXSU, MASK_R2_MULXSU, 0, no_overflow}, - {"mulxuu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MULXUU, MASK_R2_MULXUU, 0, no_overflow}, - /* The encoding of the neg.n operands is backwards, not - the interpretation -- the first operand is still the - destination and the second the source. */ - {"neg.n", "S,D", "S,D,E", 2, 2, iw_T2X3_type, - MATCH_R2_NEG_N, MASK_R2_NEG_N, 0, no_overflow}, - {"nextpc", "d", "d,E", 1, 4, iw_F3X6_type, - MATCH_R2_NEXTPC, MASK_R2_NEXTPC, 0, no_overflow}, - {"nop", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_NOP, MASK_R2_NOP, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"nop.n", "", "E", 0, 2, iw_F2_type, - MATCH_R2_NOP_N, MASK_R2_NOP_N, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"nor", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_NOR, MASK_R2_NOR, 0, no_overflow}, - {"not.n", "D,S", "D,S,E", 2, 2, iw_T2X3_type, - MATCH_R2_NOT_N, MASK_R2_NOT_N, 0, no_overflow}, - {"or", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_OR, MASK_R2_OR, 0, no_overflow}, - {"orhi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ORHI, MASK_R2_ORHI, 0, unsigned_immed16_overflow}, - {"ori", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ORI, MASK_R2_ORI, 0, unsigned_immed16_overflow}, - {"or.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_OR_N, MASK_R2_OR_N, 0, no_overflow}, - {"pop.n", "R,W", "R,W,E", 2, 2, iw_L5I4X1_type, - MATCH_R2_POP_N, MASK_R2_POP_N, NIOS2_INSN_OPTARG, no_overflow}, - {"push.n", "R,W", "R,W,E", 2, 2, iw_L5I4X1_type, - MATCH_R2_PUSH_N, MASK_R2_PUSH_N, NIOS2_INSN_OPTARG, no_overflow}, - {"rdctl", "d,c", "d,c,E", 2, 4, iw_F3X6L5_type, - MATCH_R2_RDCTL, MASK_R2_RDCTL, 0, no_overflow}, - {"rdprs", "t,s,I", "t,s,I,E", 3, 4, iw_F2X4I12_type, - MATCH_R2_RDPRS, MASK_R2_RDPRS, 0, signed_immed12_overflow}, - {"ret", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_RET, MASK_R2_RET, 0, no_overflow}, - {"ret.n", "", "E", 0, 2, iw_X2L5_type, - MATCH_R2_RET_N, MASK_R2_RET_N, 0, no_overflow}, - {"rol", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_ROL, MASK_R2_ROL, 0, no_overflow}, - {"roli", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_ROLI, MASK_R2_ROLI, 0, unsigned_immed5_overflow}, - {"ror", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_ROR, MASK_R2_ROR, 0, no_overflow}, - {"sll", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SLL, MASK_R2_SLL, 0, no_overflow}, - {"slli", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_SLLI, MASK_R2_SLLI, 0, unsigned_immed5_overflow}, - {"sll.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_SLL_N, MASK_R2_SLL_N, 0, no_overflow}, - {"slli.n", "D,S,f", "D,S,f,E", 3, 2, iw_T2X1L3_type, - MATCH_R2_SLLI_N, MASK_R2_SLLI_N, 0, enumeration_overflow}, - {"spaddi.n", "D,U", "D,U,E", 2, 2, iw_T1I7_type, - MATCH_R2_SPADDI_N, MASK_R2_SPADDI_N, 0, address_offset_overflow}, - {"spdeci.n", "U", "U,E", 1, 2, iw_X1I7_type, - MATCH_R2_SPDECI_N, MASK_R2_SPDECI_N, 0, address_offset_overflow}, - {"spinci.n", "U", "U,E", 1, 2, iw_X1I7_type, - MATCH_R2_SPINCI_N, MASK_R2_SPINCI_N, 0, address_offset_overflow}, - {"sra", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SRA, MASK_R2_SRA, 0, no_overflow}, - {"srai", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_SRAI, MASK_R2_SRAI, 0, unsigned_immed5_overflow}, - {"srl", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SRL, MASK_R2_SRL, 0, no_overflow}, - {"srli", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_SRLI, MASK_R2_SRLI, 0, unsigned_immed5_overflow}, - {"srl.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_SRL_N, MASK_R2_SRL_N, 0, no_overflow}, - {"srli.n", "D,S,f", "D,S,f,E", 3, 2, iw_T2X1L3_type, - MATCH_R2_SRLI_N, MASK_R2_SRLI_N, 0, enumeration_overflow}, - {"stb", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_STB, MASK_R2_STB, 0, address_offset_overflow}, - {"stbio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_STBIO, MASK_R2_STBIO, 0, signed_immed12_overflow}, - {"stb.n", "T,Y(S)", "T,Y(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_STB_N, MASK_R2_STB_N, 0, address_offset_overflow}, - {"stbz.n", "t,M(S)", "t,M(S),E", 3, 2, iw_T1X1I6_type, - MATCH_R2_STBZ_N, MASK_R2_STBZ_N, 0, address_offset_overflow}, - {"stex", "d,t,(s)", "d,t,(s),E", 3, 4, iw_F3X6_type, - MATCH_R2_STEX, MASK_R2_STEX, 0, no_overflow}, - {"sth", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_STH, MASK_R2_STH, 0, address_offset_overflow}, - {"sthio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_STHIO, MASK_R2_STHIO, 0, signed_immed12_overflow}, - {"sth.n", "T,X(S)", "T,X(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_STH_N, MASK_R2_STH_N, 0, address_offset_overflow}, - {"stsex", "d,t,(s)", "d,t,(s),E", 3, 4, iw_F3X6_type, - MATCH_R2_STSEX, MASK_R2_STSEX, 0, no_overflow}, - {"stw", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_STW, MASK_R2_STW, 0, address_offset_overflow}, - {"stwio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_STWIO, MASK_R2_STWIO, 0, signed_immed12_overflow}, - {"stwm", "R,B", "R,B,E", 2, 4, iw_F1X4L17_type, - MATCH_R2_STWM, MASK_R2_STWM, 0, no_overflow}, - {"stwsp.n", "t,V(s)", "t,V(s),E", 3, 2, iw_F1I5_type, - MATCH_R2_STWSP_N, MASK_R2_STWSP_N, 0, address_offset_overflow}, - {"stw.n", "T,W(S)", "T,W(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_STW_N, MASK_R2_STW_N, 0, address_offset_overflow}, - {"stwz.n", "t,N(S)", "t,N(S),E", 3, 2, iw_T1X1I6_type, - MATCH_R2_STWZ_N, MASK_R2_STWZ_N, 0, address_offset_overflow}, - {"sub", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SUB, MASK_R2_SUB, 0, no_overflow}, - {"subi", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_SUBI, MASK_R2_SUBI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"sub.n", "D,S,T", "D,S,T,E", 3, 2, iw_T3X1_type, - MATCH_R2_SUB_N, MASK_R2_SUB_N, 0, no_overflow}, - {"subi.n", "D,S,e", "D,S,e,E", 3, 2, iw_T2X1I3_type, - MATCH_R2_SUBI_N, MASK_R2_SUBI_N, 0, enumeration_overflow}, - {"sync", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_SYNC, MASK_R2_SYNC, 0, no_overflow}, - {"trap", "j", "j,E", 1, 4, iw_F3X6L5_type, - MATCH_R2_TRAP, MASK_R2_TRAP, NIOS2_INSN_OPTARG, no_overflow}, - {"trap.n", "j", "j,E", 1, 2, iw_X2L5_type, - MATCH_R2_TRAP_N, MASK_R2_TRAP_N, NIOS2_INSN_OPTARG, no_overflow}, - {"wrctl", "c,s", "c,s,E", 2, 4, iw_F3X6L5_type, - MATCH_R2_WRCTL, MASK_R2_WRCTL, 0, no_overflow}, - {"wrpie", "d,s", "d,s,E", 2, 4, iw_F3X6L5_type, - MATCH_R2_WRPIE, MASK_R2_WRPIE, 0, no_overflow}, - {"wrprs", "d,s", "d,s,E", 2, 4, iw_F3X6_type, - MATCH_R2_WRPRS, MASK_R2_WRPRS, 0, no_overflow}, - {"xor", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_XOR, MASK_R2_XOR, 0, no_overflow}, - {"xorhi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_XORHI, MASK_R2_XORHI, 0, unsigned_immed16_overflow}, - {"xori", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_XORI, MASK_R2_XORI, 0, unsigned_immed16_overflow}, - {"xor.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_XOR_N, MASK_R2_XOR_N, 0, no_overflow}, -}; - -#define NIOS2_NUM_R2_OPCODES \ - ((sizeof nios2_r2_opcodes) / (sizeof (nios2_r2_opcodes[0]))) -const int nios2_num_r2_opcodes = NIOS2_NUM_R2_OPCODES; - -/* Default to using the R1 instruction tables. */ -struct nios2_opcode *nios2_opcodes = (struct nios2_opcode *) nios2_r1_opcodes; -int nios2_num_opcodes = NIOS2_NUM_R1_OPCODES; -#undef NIOS2_NUM_R1_OPCODES -#undef NIOS2_NUM_R2_OPCODES - -/* Decodings for R2 asi.n (addi.n/subi.n) immediate values. */ -unsigned int nios2_r2_asi_n_mappings[] = - {1, 2, 4, 8, 16, 32, 64, 128}; -const int nios2_num_r2_asi_n_mappings = 8; - -/* Decodings for R2 shi.n (slli.n/srli.n) immediate values. */ -unsigned int nios2_r2_shi_n_mappings[] = - {1, 2, 3, 8, 12, 16, 24, 31}; -const int nios2_num_r2_shi_n_mappings = 8; - -/* Decodings for R2 andi.n immediate values. */ -unsigned int nios2_r2_andi_n_mappings[] = - {1, 2, 3, 4, 8, 0xf, 0x10, 0x1f, - 0x20, 0x3f, 0x7f, 0x80, 0xff, 0x7ff, 0xff00, 0xffff}; -const int nios2_num_r2_andi_n_mappings = 16; - -/* Decodings for R2 3-bit register fields. */ -int nios2_r2_reg3_mappings[] = - {16, 17, 2, 3, 4, 5, 6, 7}; -const int nios2_num_r2_reg3_mappings = 8; - -/* Decodings for R2 push.n/pop.n REG_RANGE value list. */ -unsigned long nios2_r2_reg_range_mappings[] = { - 0x00010000, - 0x00030000, - 0x00070000, - 0x000f0000, - 0x001f0000, - 0x003f0000, - 0x007f0000, - 0x00ff0000 -}; -const int nios2_num_r2_reg_range_mappings = 8; - -/*#include "sysdep.h" -#include "dis-asm.h" -#include "opcode/nios2.h" -#include "libiberty.h" -*/ -/* No symbol table is available when this code runs out in an embedded - system as when it is used for disassembler support in a monitor. */ -#if !defined(EMBEDDED_ENV) -#define SYMTAB_AVAILABLE 1 -/* -#include "elf-bfd.h" -#include "elf/nios2.h" -*/ -#endif - -/* Default length of Nios II instruction in bytes. */ -#define INSNLEN 4 - -/* Data structures used by the opcode hash table. */ -typedef struct _nios2_opcode_hash -{ - const struct nios2_opcode *opcode; - struct _nios2_opcode_hash *next; -} nios2_opcode_hash; - -/* Hash table size. */ -#define OPCODE_HASH_SIZE (IW_R1_OP_UNSHIFTED_MASK + 1) - -/* Extract the opcode from an instruction word. */ -static unsigned int -nios2_r1_extract_opcode (unsigned int x) -{ - return GET_IW_R1_OP (x); -} - -static unsigned int -nios2_r2_extract_opcode (unsigned int x) -{ - return GET_IW_R2_OP (x); -} - -/* We maintain separate hash tables for R1 and R2 opcodes, and pseudo-ops - are stored in a different table than regular instructions. */ - -typedef struct _nios2_disassembler_state -{ - const struct nios2_opcode *opcodes; - const int *num_opcodes; - unsigned int (*extract_opcode) (unsigned int); - nios2_opcode_hash *hash[OPCODE_HASH_SIZE]; - nios2_opcode_hash *ps_hash[OPCODE_HASH_SIZE]; - const struct nios2_opcode *nop; - bfd_boolean init; -} nios2_disassembler_state; - -static nios2_disassembler_state -nios2_r1_disassembler_state = { - nios2_r1_opcodes, - &nios2_num_r1_opcodes, - nios2_r1_extract_opcode, - {}, - {}, - NULL, - 0 -}; - -static nios2_disassembler_state -nios2_r2_disassembler_state = { - nios2_r2_opcodes, - &nios2_num_r2_opcodes, - nios2_r2_extract_opcode, - {}, - {}, - NULL, - 0 -}; - -/* Function to initialize the opcode hash table. */ -static void -nios2_init_opcode_hash (nios2_disassembler_state *state) -{ - unsigned int i; - register const struct nios2_opcode *op; - - for (i = 0; i < OPCODE_HASH_SIZE; i++) - for (op = state->opcodes; op < &state->opcodes[*(state->num_opcodes)]; op++) - { - nios2_opcode_hash *new_hash; - nios2_opcode_hash **bucket = NULL; - - if ((op->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO) - { - if (i == state->extract_opcode (op->match) - && (op->pinfo & (NIOS2_INSN_MACRO_MOV | NIOS2_INSN_MACRO_MOVI) - & 0x7fffffff)) - { - bucket = &(state->ps_hash[i]); - if (strcmp (op->name, "nop") == 0) - state->nop = op; - } - } - else if (i == state->extract_opcode (op->match)) - bucket = &(state->hash[i]); - - if (bucket) - { - new_hash = - (nios2_opcode_hash *) malloc (sizeof (nios2_opcode_hash)); - if (new_hash == NULL) - { - fprintf (stderr, - "error allocating memory...broken disassembler\n"); - abort (); - } - new_hash->opcode = op; - new_hash->next = NULL; - while (*bucket) - bucket = &((*bucket)->next); - *bucket = new_hash; - } - } - state->init = 1; - -#ifdef DEBUG_HASHTABLE - for (i = 0; i < OPCODE_HASH_SIZE; ++i) - { - nios2_opcode_hash *tmp_hash = state->hash[i]; - printf ("index: 0x%02X ops: ", i); - while (tmp_hash != NULL) - { - printf ("%s ", tmp_hash->opcode->name); - tmp_hash = tmp_hash->next; - } - printf ("\n"); - } - - for (i = 0; i < OPCODE_HASH_SIZE; ++i) - { - nios2_opcode_hash *tmp_hash = state->ps_hash[i]; - printf ("index: 0x%02X ops: ", i); - while (tmp_hash != NULL) - { - printf ("%s ", tmp_hash->opcode->name); - tmp_hash = tmp_hash->next; - } - printf ("\n"); - } -#endif /* DEBUG_HASHTABLE */ -} - -/* Return a pointer to an nios2_opcode struct for a given instruction - word OPCODE for bfd machine MACH, or NULL if there is an error. */ -const struct nios2_opcode * -nios2_find_opcode_hash (unsigned long opcode, unsigned long mach) -{ - nios2_opcode_hash *entry; - nios2_disassembler_state *state; - - /* Select the right instruction set, hash tables, and opcode accessor - for the mach variant. */ - if (mach == bfd_mach_nios2r2) - state = &nios2_r2_disassembler_state; - else - state = &nios2_r1_disassembler_state; - - /* Build a hash table to shorten the search time. */ - if (!state->init) - nios2_init_opcode_hash (state); - - /* Check for NOP first. Both NOP and MOV are macros that expand into - an ADD instruction, and we always want to give priority to NOP. */ - if (state->nop->match == (opcode & state->nop->mask)) - return state->nop; - - /* First look in the pseudo-op hashtable. */ - for (entry = state->ps_hash[state->extract_opcode (opcode)]; - entry; entry = entry->next) - if (entry->opcode->match == (opcode & entry->opcode->mask)) - return entry->opcode; - - /* Otherwise look in the main hashtable. */ - for (entry = state->hash[state->extract_opcode (opcode)]; - entry; entry = entry->next) - if (entry->opcode->match == (opcode & entry->opcode->mask)) - return entry->opcode; - - return NULL; -} - -/* There are 32 regular registers, 32 coprocessor registers, - and 32 control registers. */ -#define NUMREGNAMES 32 - -/* Return a pointer to the base of the coprocessor register name array. */ -static struct nios2_reg * -nios2_coprocessor_regs (void) -{ - static struct nios2_reg *cached = NULL; - - if (!cached) - { - int i; - for (i = NUMREGNAMES; i < nios2_num_regs; i++) - if (!strcmp (nios2_regs[i].name, "c0")) - { - cached = nios2_regs + i; - break; - } - assert (cached); - } - return cached; -} - -/* Return a pointer to the base of the control register name array. */ -static struct nios2_reg * -nios2_control_regs (void) -{ - static struct nios2_reg *cached = NULL; - - if (!cached) - { - int i; - for (i = NUMREGNAMES; i < nios2_num_regs; i++) - if (!strcmp (nios2_regs[i].name, "status")) - { - cached = nios2_regs + i; - break; - } - assert (cached); - } - return cached; -} - -/* Helper routine to report internal errors. */ -static void -bad_opcode (const struct nios2_opcode *op) -{ - fprintf (stderr, "Internal error: broken opcode descriptor for `%s %s'\n", - op->name, op->args); - abort (); -} - -/* The function nios2_print_insn_arg uses the character pointed - to by ARGPTR to determine how it print the next token or separator - character in the arguments to an instruction. */ -static int -nios2_print_insn_arg (const char *argptr, - unsigned long opcode, bfd_vma address, - disassemble_info *info, - const struct nios2_opcode *op) -{ - unsigned long i = 0; - struct nios2_reg *reg_base; - - switch (*argptr) - { - case ',': - case '(': - case ')': - (*info->fprintf_func) (info->stream, "%c", *argptr); - break; - - case 'c': - /* Control register index. */ - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_IMM5 (opcode); - break; - case iw_F3X6L5_type: - i = GET_IW_F3X6L5_IMM5 (opcode); - break; - default: - bad_opcode (op); - } - reg_base = nios2_control_regs (); - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - break; - - case 'd': - reg_base = nios2_regs; - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_C (opcode); - break; - case iw_custom_type: - i = GET_IW_CUSTOM_C (opcode); - if (GET_IW_CUSTOM_READC (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F3X6L5_type: - case iw_F3X6_type: - i = GET_IW_F3X6L5_C (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_C (opcode); - if (GET_IW_F3X8_READC (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F2_type: - i = GET_IW_F2_B (opcode); - break; - default: - bad_opcode (op); - } - if (i < NUMREGNAMES) - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - else - (*info->fprintf_func) (info->stream, "unknown"); - break; - - case 's': - reg_base = nios2_regs; - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_A (opcode); - break; - case iw_i_type: - i = GET_IW_I_A (opcode); - break; - case iw_custom_type: - i = GET_IW_CUSTOM_A (opcode); - if (GET_IW_CUSTOM_READA (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F2I16_type: - i = GET_IW_F2I16_A (opcode); - break; - case iw_F2X4I12_type: - i = GET_IW_F2X4I12_A (opcode); - break; - case iw_F1X4I12_type: - i = GET_IW_F1X4I12_A (opcode); - break; - case iw_F1X4L17_type: - i = GET_IW_F1X4L17_A (opcode); - break; - case iw_F3X6L5_type: - case iw_F3X6_type: - i = GET_IW_F3X6L5_A (opcode); - break; - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_A (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_A (opcode); - if (GET_IW_F3X8_READA (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F1X1_type: - i = GET_IW_F1X1_A (opcode); - break; - case iw_F1I5_type: - i = 27; /* Implicit stack pointer reference. */ - break; - case iw_F2_type: - i = GET_IW_F2_A (opcode); - break; - default: - bad_opcode (op); - } - if (i < NUMREGNAMES) - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - else - (*info->fprintf_func) (info->stream, "unknown"); - break; - - case 't': - reg_base = nios2_regs; - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_B (opcode); - break; - case iw_i_type: - i = GET_IW_I_B (opcode); - break; - case iw_custom_type: - i = GET_IW_CUSTOM_B (opcode); - if (GET_IW_CUSTOM_READB (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F2I16_type: - i = GET_IW_F2I16_B (opcode); - break; - case iw_F2X4I12_type: - i = GET_IW_F2X4I12_B (opcode); - break; - case iw_F3X6L5_type: - case iw_F3X6_type: - i = GET_IW_F3X6L5_B (opcode); - break; - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_B (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_B (opcode); - if (GET_IW_F3X8_READB (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F1I5_type: - i = GET_IW_F1I5_B (opcode); - break; - case iw_F2_type: - i = GET_IW_F2_B (opcode); - break; - case iw_T1X1I6_type: - i = 0; - break; - default: - bad_opcode (op); - } - if (i < NUMREGNAMES) - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - else - (*info->fprintf_func) (info->stream, "unknown"); - break; - - case 'D': - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_A3 (opcode); - break; - case iw_T2X1L3_type: - i = GET_IW_T2X1L3_B3 (opcode); - break; - case iw_T2X1I3_type: - i = GET_IW_T2X1I3_B3 (opcode); - break; - case iw_T3X1_type: - i = GET_IW_T3X1_C3 (opcode); - break; - case iw_T2X3_type: - if (op->num_args == 3) - i = GET_IW_T2X3_A3 (opcode); - else - i = GET_IW_T2X3_B3 (opcode); - break; - default: - bad_opcode (op); - } - i = nios2_r2_reg3_mappings[i]; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[i].name); - break; - - case 'M': - /* 6-bit unsigned immediate with no shift. */ - switch (op->format) - { - case iw_T1X1I6_type: - i = GET_IW_T1X1I6_IMM6 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'N': - /* 6-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_T1X1I6_type: - i = GET_IW_T1X1I6_IMM6 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'S': - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_A3 (opcode); - break; - case iw_T2I4_type: - i = GET_IW_T2I4_A3 (opcode); - break; - case iw_T2X1L3_type: - i = GET_IW_T2X1L3_A3 (opcode); - break; - case iw_T2X1I3_type: - i = GET_IW_T2X1I3_A3 (opcode); - break; - case iw_T3X1_type: - i = GET_IW_T3X1_A3 (opcode); - break; - case iw_T2X3_type: - i = GET_IW_T2X3_A3 (opcode); - break; - case iw_T1X1I6_type: - i = GET_IW_T1X1I6_A3 (opcode); - break; - default: - bad_opcode (op); - } - i = nios2_r2_reg3_mappings[i]; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[i].name); - break; - - case 'T': - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_B3 (opcode); - break; - case iw_T3X1_type: - i = GET_IW_T3X1_B3 (opcode); - break; - case iw_T2X3_type: - i = GET_IW_T2X3_B3 (opcode); - break; - default: - bad_opcode (op); - } - i = nios2_r2_reg3_mappings[i]; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[i].name); - break; - - case 'i': - /* 16-bit signed immediate. */ - switch (op->format) - { - case iw_i_type: - i = (signed) (GET_IW_I_IMM16 (opcode) << 16) >> 16; - break; - case iw_F2I16_type: - i = (signed) (GET_IW_F2I16_IMM16 (opcode) << 16) >> 16; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'I': - /* 12-bit signed immediate. */ - switch (op->format) - { - case iw_F2X4I12_type: - i = (signed) (GET_IW_F2X4I12_IMM12 (opcode) << 20) >> 20; - break; - case iw_F1X4I12_type: - i = (signed) (GET_IW_F1X4I12_IMM12 (opcode) << 20) >> 20; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'u': - /* 16-bit unsigned immediate. */ - switch (op->format) - { - case iw_i_type: - i = GET_IW_I_IMM16 (opcode); - break; - case iw_F2I16_type: - i = GET_IW_F2I16_IMM16 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'U': - /* 7-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_IMM7 (opcode) << 2; - break; - case iw_X1I7_type: - i = GET_IW_X1I7_IMM7 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'V': - /* 5-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_F1I5_type: - i = GET_IW_F1I5_IMM5 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'W': - /* 4-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_IMM4 (opcode) << 2; - break; - case iw_L5I4X1_type: - i = GET_IW_L5I4X1_IMM4 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'X': - /* 4-bit unsigned immediate with 1-bit shift. */ - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_IMM4 (opcode) << 1; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'Y': - /* 4-bit unsigned immediate without shift. */ - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_IMM4 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'o': - /* 16-bit signed immediate address offset. */ - switch (op->format) - { - case iw_i_type: - i = (signed) (GET_IW_I_IMM16 (opcode) << 16) >> 16; - break; - case iw_F2I16_type: - i = (signed) (GET_IW_F2I16_IMM16 (opcode) << 16) >> 16; - break; - default: - bad_opcode (op); - } - address = address + 4 + i; - (*info->print_address_func) (address, info); - break; - - case 'O': - /* 10-bit signed address offset with 1-bit shift. */ - switch (op->format) - { - case iw_I10_type: - i = (signed) (GET_IW_I10_IMM10 (opcode) << 22) >> 21; - break; - default: - bad_opcode (op); - } - address = address + 2 + i; - (*info->print_address_func) (address, info); - break; - - case 'P': - /* 7-bit signed address offset with 1-bit shift. */ - switch (op->format) - { - case iw_T1I7_type: - i = (signed) (GET_IW_T1I7_IMM7 (opcode) << 25) >> 24; - break; - default: - bad_opcode (op); - } - address = address + 2 + i; - (*info->print_address_func) (address, info); - break; - - case 'j': - /* 5-bit unsigned immediate. */ - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_IMM5 (opcode); - break; - case iw_F3X6L5_type: - i = GET_IW_F3X6L5_IMM5 (opcode); - break; - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_MSB (opcode); - break; - case iw_X2L5_type: - i = GET_IW_X2L5_IMM5 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'k': - /* Second 5-bit unsigned immediate field. */ - switch (op->format) - { - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_LSB (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'l': - /* 8-bit unsigned immediate. */ - switch (op->format) - { - case iw_custom_type: - i = GET_IW_CUSTOM_N (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_N (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'm': - /* 26-bit unsigned immediate. */ - switch (op->format) - { - case iw_j_type: - i = GET_IW_J_IMM26 (opcode); - break; - case iw_L26_type: - i = GET_IW_L26_IMM26 (opcode); - break; - default: - bad_opcode (op); - } - /* This translates to an address because it's only used in call - instructions. */ - address = (address & 0xf0000000) | (i << 2); - (*info->print_address_func) (address, info); - break; - - case 'e': - /* Encoded enumeration for addi.n/subi.n. */ - switch (op->format) - { - case iw_T2X1I3_type: - i = nios2_r2_asi_n_mappings[GET_IW_T2X1I3_IMM3 (opcode)]; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'f': - /* Encoded enumeration for slli.n/srli.n. */ - switch (op->format) - { - case iw_T2X1L3_type: - i = nios2_r2_shi_n_mappings[GET_IW_T2X1I3_IMM3 (opcode)]; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'g': - /* Encoded enumeration for andi.n. */ - switch (op->format) - { - case iw_T2I4_type: - i = nios2_r2_andi_n_mappings[GET_IW_T2I4_IMM4 (opcode)]; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'h': - /* Encoded enumeration for movi.n. */ - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_IMM7 (opcode); - if (i == 125) - i = 0xff; - else if (i == 126) - i = -2; - else if (i == 127) - i = -1; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'R': - { - unsigned long reglist = 0; - int dir = 1; - int k, t; - - switch (op->format) - { - case iw_F1X4L17_type: - /* Encoding for ldwm/stwm. */ - i = GET_IW_F1X4L17_REGMASK (opcode); - if (GET_IW_F1X4L17_RS (opcode)) - { - reglist = ((i << 14) & 0x00ffc000); - if (i & (1 << 10)) - reglist |= (1 << 28); - if (i & (1 << 11)) - reglist |= (1 << 31); - } - else - reglist = i << 2; - dir = GET_IW_F1X4L17_REGMASK (opcode) ? 1 : -1; - break; - - case iw_L5I4X1_type: - /* Encoding for push.n/pop.n. */ - reglist |= (1 << 31); - if (GET_IW_L5I4X1_FP (opcode)) - reglist |= (1 << 28); - if (GET_IW_L5I4X1_CS (opcode)) - { - int val = GET_IW_L5I4X1_REGRANGE (opcode); - reglist |= nios2_r2_reg_range_mappings[val]; - } - dir = (op->match == MATCH_R2_POP_N ? 1 : -1); - break; - - default: - bad_opcode (op); - } - - t = 0; - (*info->fprintf_func) (info->stream, "{"); - for (k = (dir == 1 ? 0 : 31); - (dir == 1 && k < 32) || (dir == -1 && k >= 0); - k += dir) - if (reglist & (1 << k)) - { - if (t) - (*info->fprintf_func) (info->stream, ","); - else - t++; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[k].name); - } - (*info->fprintf_func) (info->stream, "}"); - break; - } - - case 'B': - /* Base register and options for ldwm/stwm. */ - switch (op->format) - { - case iw_F1X4L17_type: - if (GET_IW_F1X4L17_ID (opcode) == 0) - (*info->fprintf_func) (info->stream, "--"); - - i = GET_IW_F1X4I12_A (opcode); - (*info->fprintf_func) (info->stream, "(%s)", - nios2_builtin_regs[i].name); - - if (GET_IW_F1X4L17_ID (opcode)) - (*info->fprintf_func) (info->stream, "++"); - if (GET_IW_F1X4L17_WB (opcode)) - (*info->fprintf_func) (info->stream, ",writeback"); - if (GET_IW_F1X4L17_PC (opcode)) - (*info->fprintf_func) (info->stream, ",ret"); - break; - default: - bad_opcode (op); - } - break; - - default: - (*info->fprintf_func) (info->stream, "unknown"); - break; - } - return 0; -} - -/* nios2_disassemble does all the work of disassembling a Nios II - instruction opcode. */ -static int -nios2_disassemble (bfd_vma address, unsigned long opcode, - disassemble_info *info) -{ - const struct nios2_opcode *op; - - info->bytes_per_line = INSNLEN; - info->bytes_per_chunk = INSNLEN; - info->display_endian = info->endian; - info->insn_info_valid = 1; - info->branch_delay_insns = 0; - info->data_size = 0; - info->insn_type = dis_nonbranch; - info->target = 0; - info->target2 = 0; - - /* Find the major opcode and use this to disassemble - the instruction and its arguments. */ - op = nios2_find_opcode_hash (opcode, info->mach); - - if (op != NULL) - { - const char *argstr = op->args; - (*info->fprintf_func) (info->stream, "%s", op->name); - if (argstr != NULL && *argstr != '\0') - { - (*info->fprintf_func) (info->stream, "\t"); - while (*argstr != '\0') - { - nios2_print_insn_arg (argstr, opcode, address, info, op); - ++argstr; - } - } - /* Tell the caller how far to advance the program counter. */ - info->bytes_per_chunk = op->size; - return op->size; - } - else - { - /* Handle undefined instructions. */ - info->insn_type = dis_noninsn; - (*info->fprintf_func) (info->stream, "0x%lx", opcode); - return INSNLEN; - } -} - - -/* print_insn_nios2 is the main disassemble function for Nios II. - The function diassembler(abfd) (source in disassemble.c) returns a - pointer to this either print_insn_big_nios2 or - print_insn_little_nios2, which in turn call this function when the - bfd machine type is Nios II. print_insn_nios2 reads the - instruction word at the address given, and prints the disassembled - instruction on the stream info->stream using info->fprintf_func. */ - -int print_insn_nios2(bfd_vma address, disassemble_info *info) -{ - bfd_byte buffer[INSNLEN]; - int status; - - status = (*info->read_memory_func)(address, buffer, INSNLEN, info); - if (status == 0) { - unsigned long insn; - if (info->endian == BFD_ENDIAN_BIG) { - insn = (unsigned long) bfd_getb32(buffer); - } else { - insn = (unsigned long) bfd_getl32(buffer); - } - return nios2_disassemble(address, insn, info); - } - - /* We might have a 16-bit R2 instruction at the end of memory. Try that. */ - if (info->mach == bfd_mach_nios2r2) { - status = (*info->read_memory_func)(address, buffer, 2, info); - if (status == 0) { - unsigned long insn; - if (info->endian == BFD_ENDIAN_BIG) { - insn = (unsigned long) bfd_getb16(buffer); - } else { - insn = (unsigned long) bfd_getl16(buffer); - } - return nios2_disassemble(address, insn, info); - } - } - - /* If we got here, we couldn't read anything. */ - (*info->memory_error_func)(status, address, info); - return -1; -} diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 7b548519b5..06090dd2c2 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -185,12 +185,6 @@ it. Since all recent x86 hardware from the past >10 years is capable of the System emulator CPUs -------------------- -Nios II CPU (since 8.2) -''''''''''''''''''''''' - -The Nios II architecture is orphan. The ``nios2`` guest CPU support is -deprecated and will be removed in a future version of QEMU. - ``power5+`` and ``power7+`` CPU names (since 9.0) ''''''''''''''''''''''''''''''''''''''''''''''''' @@ -226,11 +220,6 @@ These old machine types are quite neglected nowadays and thus might have various pitfalls with regards to live migration. Use a newer machine type instead. -Nios II ``10m50-ghrd`` and ``nios2-generic-nommu`` machines (since 8.2) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The Nios II architecture is orphan. - ``shix`` (since 9.0) '''''''''''''''''''' diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index a2eefe3f3f..b5ff9c5f69 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -58,10 +58,6 @@ depending on the guest architecture. - :ref:`Yes` - Yes - Venerable RISC architecture originally out of Stanford University - * - Nios2 - - Yes - - Yes - - 32 bit embedded soft-core by Altera * - OpenRISC - :ref:`Yes` - Yes @@ -180,9 +176,6 @@ for that architecture. * - MIPS - System - Unified Hosting Interface (MD01069) - * - Nios II - - System - - https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD * - RISC-V - System and User-mode - https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index f9cf874f7b..299c844b8b 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -757,6 +757,12 @@ x86 ``Icelake-Client`` CPU (removed in 7.1) There isn't ever Icelake Client CPU, it is some wrong and imaginary one. Use ``Icelake-Server`` instead. +Nios II CPU (removed in 9.1) +'''''''''''''''''''''''''''' + +QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II +processor IP (see `Intel discontinuance notification`_). + System accelerators ------------------- @@ -841,6 +847,11 @@ ppc ``taihu`` machine (removed in 7.2) This machine was removed because it was partially emulated and 405 machines are very similar. Use the ``ref405ep`` machine instead. +Nios II ``10m50-ghrd`` and ``nios2-generic-nommu`` machines (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The Nios II architecture was orphan. + linux-user mode CPUs -------------------- @@ -860,6 +871,11 @@ The ``ppc64abi32`` architecture has a number of issues which regularly tripped up the CI testing and was suspected to be quite broken. For that reason the maintainers strongly suspected no one actually used it. +``nios2`` CPU (removed in 9.1) +'''''''''''''''''''''''''''''' + +QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II +processor IP (see `Intel discontinuance notification`_). TCG introspection features -------------------------- @@ -1006,3 +1022,4 @@ stable for some time and is now widely used. The command line and feature set is very close to the removed C implementation. +.. _Intel discontinuance notification: https://www.intel.com/content/www/us/en/content-details/781327/intel-is-discontinuing-ip-ordering-codes-listed-in-pdn2312-for-nios-ii-ip.html diff --git a/docs/system/replay.rst b/docs/system/replay.rst index ca7c17c63d..28e5772a2b 100644 --- a/docs/system/replay.rst +++ b/docs/system/replay.rst @@ -24,7 +24,7 @@ Deterministic replay has the following features: * Writes execution log into the file for later replaying for multiple times on different machines. * Supports i386, x86_64, ARM, AArch64, Risc-V, MIPS, MIPS64, S390X, Alpha, - PowerPC, PowerPC64, M68000, Microblaze, OpenRISC, Nios II, SPARC, + PowerPC, PowerPC64, M68000, Microblaze, OpenRISC, SPARC, and Xtensa hardware platforms. * Performs deterministic replay of all operations with keyboard and mouse input devices, serial ports, and network. diff --git a/docs/user/main.rst b/docs/user/main.rst index d5fbb78d3c..e04bc2cb86 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -159,10 +159,6 @@ Other binaries * ``qemu-mipsn32el`` executes 32-bit little endian MIPS binaries (MIPS N32 ABI). -- user mode (NiosII) - - * ``qemu-nios2`` TODO. - - user mode (PowerPC) * ``qemu-ppc64`` TODO. diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 1c85c48a73..f573014532 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -152,7 +152,7 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) /* * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, * S390, SH4, TriCore, and Xtensa. Our other supported targets, - * CRIS and Nios2, do not have floating-point. + * such CRIS, do not have floating-point. */ if (snan_bit_is_one(status)) { /* set all bits other than msb */ diff --git a/hw/Kconfig b/hw/Kconfig index 2c00936c28..ea6a68b1a1 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -57,7 +57,6 @@ source loongarch/Kconfig source m68k/Kconfig source microblaze/Kconfig source mips/Kconfig -source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/Kconfig diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 2b5b2d2301..ad59abebaa 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -87,9 +87,6 @@ config GOLDFISH_PIC config M68K_IRQC bool -config NIOS2_VIC - bool - config LOONGARCH_IPI bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index ed355941d1..58140da5f2 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -68,7 +68,6 @@ specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) -specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) diff --git a/hw/intc/nios2_vic.c b/hw/intc/nios2_vic.c deleted file mode 100644 index 7e2d9d6327..0000000000 --- a/hw/intc/nios2_vic.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Vectored Interrupt Controller for nios2 processor - * - * Copyright (c) 2022 Neuroblade - * - * Interface: - * QOM property "cpu": link to the Nios2 CPU (must be set) - * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines - * IRQ should be connected to nios2 IRQ0. - * - * Reference: "Embedded Peripherals IP User Guide - * for Intel® Quartus® Prime Design Suite: 21.4" - * Chapter 38 "Vectored Interrupt Controller Core" - * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "qom/object.h" -#include "hw/intc/nios2_vic.h" -#include "cpu.h" - - -enum { - INT_CONFIG0 = 0, - INT_CONFIG31 = 31, - INT_ENABLE = 32, - INT_ENABLE_SET = 33, - INT_ENABLE_CLR = 34, - INT_PENDING = 35, - INT_RAW_STATUS = 36, - SW_INTERRUPT = 37, - SW_INTERRUPT_SET = 38, - SW_INTERRUPT_CLR = 39, - VIC_CONFIG = 40, - VIC_STATUS = 41, - VEC_TBL_BASE = 42, - VEC_TBL_ADDR = 43, - CSR_COUNT /* Last! */ -}; - -/* Requested interrupt level (INT_CONFIG[0:5]) */ -static inline uint32_t vic_int_config_ril(const Nios2VIC *vic, int irq_num) -{ - return extract32(vic->int_config[irq_num], 0, 6); -} - -/* Requested NMI (INT_CONFIG[6]) */ -static inline uint32_t vic_int_config_rnmi(const Nios2VIC *vic, int irq_num) -{ - return extract32(vic->int_config[irq_num], 6, 1); -} - -/* Requested register set (INT_CONFIG[7:12]) */ -static inline uint32_t vic_int_config_rrs(const Nios2VIC *vic, int irq_num) -{ - return extract32(vic->int_config[irq_num], 7, 6); -} - -static inline uint32_t vic_config_vec_size(const Nios2VIC *vic) -{ - return 1 << (2 + extract32(vic->vic_config, 0, 3)); -} - -static inline uint32_t vic_int_pending(const Nios2VIC *vic) -{ - return (vic->int_raw_status | vic->sw_int) & vic->int_enable; -} - -static void vic_update_irq(Nios2VIC *vic) -{ - Nios2CPU *cpu = NIOS2_CPU(vic->cpu); - uint32_t pending = vic_int_pending(vic); - int irq = -1; - int max_ril = 0; - /* Note that if RIL is 0 for an interrupt it is effectively disabled */ - - vic->vec_tbl_addr = 0; - vic->vic_status = 0; - - if (pending == 0) { - qemu_irq_lower(vic->output_int); - return; - } - - for (int i = 0; i < NIOS2_VIC_MAX_IRQ; i++) { - if (pending & BIT(i)) { - int ril = vic_int_config_ril(vic, i); - if (ril > max_ril) { - irq = i; - max_ril = ril; - } - } - } - - if (irq < 0) { - qemu_irq_lower(vic->output_int); - return; - } - - vic->vec_tbl_addr = irq * vic_config_vec_size(vic) + vic->vec_tbl_base; - vic->vic_status = irq | BIT(31); - - /* - * In hardware, the interface between the VIC and the CPU is via the - * External Interrupt Controller interface, where the interrupt controller - * presents the CPU with a packet of data containing: - * - Requested Handler Address (RHA): 32 bits - * - Requested Register Set (RRS) : 6 bits - * - Requested Interrupt Level (RIL) : 6 bits - * - Requested NMI flag (RNMI) : 1 bit - * In our emulation, we implement this by writing the data directly to - * fields in the CPU object and then raising the IRQ line to tell - * the CPU that we've done so. - */ - - cpu->rha = vic->vec_tbl_addr; - cpu->ril = max_ril; - cpu->rrs = vic_int_config_rrs(vic, irq); - cpu->rnmi = vic_int_config_rnmi(vic, irq); - - qemu_irq_raise(vic->output_int); -} - -static void vic_set_irq(void *opaque, int irq_num, int level) -{ - Nios2VIC *vic = opaque; - - vic->int_raw_status = deposit32(vic->int_raw_status, irq_num, 1, !!level); - vic_update_irq(vic); -} - -static void nios2_vic_reset(DeviceState *dev) -{ - Nios2VIC *vic = NIOS2_VIC(dev); - - memset(&vic->int_config, 0, sizeof(vic->int_config)); - vic->vic_config = 0; - vic->int_raw_status = 0; - vic->int_enable = 0; - vic->sw_int = 0; - vic->vic_status = 0; - vic->vec_tbl_base = 0; - vic->vec_tbl_addr = 0; -} - -static uint64_t nios2_vic_csr_read(void *opaque, hwaddr offset, unsigned size) -{ - Nios2VIC *vic = opaque; - int index = offset / 4; - - switch (index) { - case INT_CONFIG0 ... INT_CONFIG31: - return vic->int_config[index - INT_CONFIG0]; - case INT_ENABLE: - return vic->int_enable; - case INT_PENDING: - return vic_int_pending(vic); - case INT_RAW_STATUS: - return vic->int_raw_status; - case SW_INTERRUPT: - return vic->sw_int; - case VIC_CONFIG: - return vic->vic_config; - case VIC_STATUS: - return vic->vic_status; - case VEC_TBL_BASE: - return vic->vec_tbl_base; - case VEC_TBL_ADDR: - return vic->vec_tbl_addr; - default: - return 0; - } -} - -static void nios2_vic_csr_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - Nios2VIC *vic = opaque; - int index = offset / 4; - - switch (index) { - case INT_CONFIG0 ... INT_CONFIG31: - vic->int_config[index - INT_CONFIG0] = value; - break; - case INT_ENABLE: - vic->int_enable = value; - break; - case INT_ENABLE_SET: - vic->int_enable |= value; - break; - case INT_ENABLE_CLR: - vic->int_enable &= ~value; - break; - case SW_INTERRUPT: - vic->sw_int = value; - break; - case SW_INTERRUPT_SET: - vic->sw_int |= value; - break; - case SW_INTERRUPT_CLR: - vic->sw_int &= ~value; - break; - case VIC_CONFIG: - vic->vic_config = value; - break; - case VEC_TBL_BASE: - vic->vec_tbl_base = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "nios2-vic: write to invalid CSR address %#" - HWADDR_PRIx "\n", offset); - } - - vic_update_irq(vic); -} - -static const MemoryRegionOps nios2_vic_csr_ops = { - .read = nios2_vic_csr_read, - .write = nios2_vic_csr_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { .min_access_size = 4, .max_access_size = 4 } -}; - -static void nios2_vic_realize(DeviceState *dev, Error **errp) -{ - Nios2VIC *vic = NIOS2_VIC(dev); - - if (!vic->cpu) { - /* This is a programming error in the code using this device */ - error_setg(errp, "nios2-vic 'cpu' link property was not set"); - return; - } - - sysbus_init_irq(SYS_BUS_DEVICE(dev), &vic->output_int); - qdev_init_gpio_in(dev, vic_set_irq, NIOS2_VIC_MAX_IRQ); - - memory_region_init_io(&vic->csr, OBJECT(dev), &nios2_vic_csr_ops, vic, - "nios2.vic.csr", CSR_COUNT * sizeof(uint32_t)); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &vic->csr); -} - -static Property nios2_vic_properties[] = { - DEFINE_PROP_LINK("cpu", Nios2VIC, cpu, TYPE_CPU, CPUState *), - DEFINE_PROP_END_OF_LIST() -}; - -static const VMStateDescription nios2_vic_vmstate = { - .name = "nios2-vic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (const VMStateField[]){ - VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32), - VMSTATE_UINT32(vic_config, Nios2VIC), - VMSTATE_UINT32(int_raw_status, Nios2VIC), - VMSTATE_UINT32(int_enable, Nios2VIC), - VMSTATE_UINT32(sw_int, Nios2VIC), - VMSTATE_UINT32(vic_status, Nios2VIC), - VMSTATE_UINT32(vec_tbl_base, Nios2VIC), - VMSTATE_UINT32(vec_tbl_addr, Nios2VIC), - VMSTATE_END_OF_LIST() - }, -}; - -static void nios2_vic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = nios2_vic_reset; - dc->realize = nios2_vic_realize; - dc->vmsd = &nios2_vic_vmstate; - device_class_set_props(dc, nios2_vic_properties); -} - -static const TypeInfo nios2_vic_info = { - .name = TYPE_NIOS2_VIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Nios2VIC), - .class_init = nios2_vic_class_init, -}; - -static void nios2_vic_register_types(void) -{ - type_register_static(&nios2_vic_info); -} - -type_init(nios2_vic_register_types); diff --git a/hw/meson.build b/hw/meson.build index 463d702683..fb998aae0f 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -56,7 +56,6 @@ subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') -subdir('nios2') subdir('openrisc') subdir('ppc') subdir('remote') diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c deleted file mode 100644 index 6cb32f777b..0000000000 --- a/hw/nios2/10m50_devboard.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Altera 10M50 Nios2 GHRD - * - * Copyright (c) 2016 Marek Vasut - * - * Based on LabX device code - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" - -#include "hw/sysbus.h" -#include "hw/char/serial.h" -#include "hw/intc/nios2_vic.h" -#include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "qemu/config-file.h" - -#include "boot.h" - -struct Nios2MachineState { - MachineState parent_obj; - - MemoryRegion phys_tcm; - MemoryRegion phys_tcm_alias; - MemoryRegion phys_ram; - MemoryRegion phys_ram_alias; - - bool vic; -}; - -#define TYPE_NIOS2_MACHINE MACHINE_TYPE_NAME("10m50-ghrd") -OBJECT_DECLARE_TYPE(Nios2MachineState, MachineClass, NIOS2_MACHINE) - -#define BINARY_DEVICE_TREE_FILE "10m50-devboard.dtb" - -static void nios2_10m50_ghrd_init(MachineState *machine) -{ - Nios2MachineState *nms = NIOS2_MACHINE(machine); - Nios2CPU *cpu; - DeviceState *dev; - MemoryRegion *address_space_mem = get_system_memory(); - ram_addr_t tcm_base = 0x0; - ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */ - ram_addr_t ram_base = 0x08000000; - ram_addr_t ram_size = 0x08000000; - qemu_irq irq[32]; - int i; - - /* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */ - memory_region_init_ram(&nms->phys_tcm, NULL, "nios2.tcm", tcm_size, - &error_abort); - memory_region_init_alias(&nms->phys_tcm_alias, NULL, "nios2.tcm.alias", - &nms->phys_tcm, 0, tcm_size); - memory_region_add_subregion(address_space_mem, tcm_base, &nms->phys_tcm); - memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base, - &nms->phys_tcm_alias); - - /* Physical DRAM with alias at 0xc0000000 */ - memory_region_init_ram(&nms->phys_ram, NULL, "nios2.ram", ram_size, - &error_abort); - memory_region_init_alias(&nms->phys_ram_alias, NULL, "nios2.ram.alias", - &nms->phys_ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, ram_base, &nms->phys_ram); - memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base, - &nms->phys_ram_alias); - - /* Create CPU. We need to set eic_present between init and realize. */ - cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU)); - - /* Enable the External Interrupt Controller within the CPU. */ - cpu->eic_present = nms->vic; - - /* Configure new exception vectors. */ - cpu->reset_addr = 0xd4000000; - cpu->exception_addr = 0xc8000120; - cpu->fast_tlb_miss_addr = 0xc0000100; - - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); - - if (nms->vic) { - dev = qdev_new(TYPE_NIOS2_VIC); - MemoryRegion *dev_mr; - qemu_irq cpu_irq; - - object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - cpu_irq = qdev_get_gpio_in_named(DEVICE(cpu), "EIC", 0); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - - dev_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(address_space_mem, 0x18002000, dev_mr); - } else { - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i); - } - } - - /* Register: Altera 16550 UART */ - serial_mm_init(address_space_mem, 0xf8001600, 2, irq[1], 115200, - serial_hd(0), DEVICE_NATIVE_ENDIAN); - - /* Register: Timer sys_clk_timer */ - dev = qdev_new("ALTR.timer"); - qdev_prop_set_uint32(dev, "clock-frequency", 75 * 1000000); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xf8001440); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[0]); - - /* Register: Timer sys_clk_timer_1 */ - dev = qdev_new("ALTR.timer"); - qdev_prop_set_uint32(dev, "clock-frequency", 75 * 1000000); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xe0000880); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[5]); - - nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename, - BINARY_DEVICE_TREE_FILE, NULL); -} - -static bool get_vic(Object *obj, Error **errp) -{ - Nios2MachineState *nms = NIOS2_MACHINE(obj); - return nms->vic; -} - -static void set_vic(Object *obj, bool value, Error **errp) -{ - Nios2MachineState *nms = NIOS2_MACHINE(obj); - nms->vic = value; -} - -static void nios2_10m50_ghrd_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Altera 10M50 GHRD Nios II design"; - mc->init = nios2_10m50_ghrd_init; - mc->is_default = true; - mc->deprecation_reason = "Nios II architecture is deprecated"; - - object_class_property_add_bool(oc, "vic", get_vic, set_vic); - object_class_property_set_description(oc, "vic", - "Set on/off to enable/disable the Vectored Interrupt Controller"); -} - -static const TypeInfo nios2_10m50_ghrd_type_info = { - .name = TYPE_NIOS2_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(Nios2MachineState), - .class_init = nios2_10m50_ghrd_class_init, -}; - -static void nios2_10m50_ghrd_type_init(void) -{ - type_register_static(&nios2_10m50_ghrd_type_info); -} -type_init(nios2_10m50_ghrd_type_init); diff --git a/hw/nios2/Kconfig b/hw/nios2/Kconfig deleted file mode 100644 index 4748ae27b6..0000000000 --- a/hw/nios2/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config NIOS2_10M50 - bool - select NIOS2 - select SERIAL - select ALTERA_TIMER - select NIOS2_VIC - -config NIOS2_GENERIC_NOMMU - bool - select NIOS2 - -config NIOS2 - bool diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c deleted file mode 100644 index cd75803fc2..0000000000 --- a/hw/nios2/boot.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Nios2 kernel loader - * - * Copyright (c) 2016 Marek Vasut - * - * Based on microblaze kernel loader - * - * Copyright (c) 2012 Peter Crosthwaite - * Copyright (c) 2012 PetaLogix - * Copyright (c) 2009 Edgar E. Iglesias. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qemu/datadir.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "qemu/guest-random.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" - -#include "boot.h" - -#include - -#define NIOS2_MAGIC 0x534f494e - -static struct nios2_boot_info { - void (*machine_cpu_reset)(Nios2CPU *); - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t initrd_start; - uint32_t initrd_end; - uint32_t fdt; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - Nios2CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUNios2State *env = &cpu->env; - - cpu_reset(CPU(cpu)); - - env->regs[R_ARG0] = NIOS2_MAGIC; - env->regs[R_ARG1] = boot_info.initrd_start; - env->regs[R_ARG2] = boot_info.fdt; - env->regs[R_ARG3] = boot_info.cmdline; - - cpu_set_pc(cs, boot_info.bootstrap_pc); - if (boot_info.machine_cpu_reset) { - boot_info.machine_cpu_reset(cpu); - } -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0xc0000000LL; -} - -static int nios2_load_dtb(struct nios2_boot_info bi, const uint32_t ramsize, - const char *kernel_cmdline, const char *dtb_filename) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - int fdt_size; - void *fdt = NULL; - int r; - uint8_t rng_seed[32]; - - if (dtb_filename) { - fdt = load_device_tree(dtb_filename, &fdt_size); - } - if (!fdt) { - return 0; - } - - qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); - qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); - - if (kernel_cmdline) { - r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (r < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - } - } - - if (bi.initrd_start) { - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - translate_kernel_address(NULL, bi.initrd_start)); - - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - translate_kernel_address(NULL, bi.initrd_end)); - } - - cpu_physical_memory_write(bi.fdt, fdt, fdt_size); - - /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ - machine->fdt = fdt; - - return fdt_size; -} - -void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, - uint32_t ramsize, - const char *initrd_filename, - const char *dtb_filename, - void (*machine_cpu_reset)(Nios2CPU *)) -{ - const char *kernel_filename; - const char *kernel_cmdline; - const char *dtb_arg; - char *filename = NULL; - - kernel_filename = current_machine->kernel_filename; - kernel_cmdline = current_machine->kernel_cmdline; - dtb_arg = current_machine->dtb; - /* default to pcbios dtb as passed by machine_init */ - if (!dtb_arg) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); - } - - boot_info.machine_cpu_reset = machine_cpu_reset; - qemu_register_reset(main_cpu_reset, cpu); - - if (kernel_filename) { - int kernel_size, fdt_size; - uint64_t entry, high; - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, &high, NULL, - TARGET_BIG_ENDIAN, EM_ALTERA_NIOS2, 0, 0); - if ((uint32_t)entry == 0xc0000000) { - /* - * The Nios II processor reference guide documents that the - * kernel is placed at virtual memory address 0xc0000000, - * and we've got something that points there. Reload it - * and adjust the entry to get the address in physical RAM. - */ - kernel_size = load_elf(kernel_filename, NULL, - translate_kernel_address, NULL, - &entry, NULL, NULL, NULL, - TARGET_BIG_ENDIAN, EM_ALTERA_NIOS2, 0, 0); - boot_info.bootstrap_pc = ddr_base + 0xc0000000 + - (entry & 0x07ffffff); - } else { - /* Use the entry point in the ELF image. */ - boot_info.bootstrap_pc = (uint32_t)entry; - } - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, - NULL, NULL); - boot_info.bootstrap_pc = uentry; - high = loadaddr + kernel_size; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ramsize); - boot_info.bootstrap_pc = ddr_base; - high = ddr_base + kernel_size; - } - - high = ROUND_UP(high, 1 * MiB); - - /* If initrd is available, it goes after the kernel, aligned to 1M. */ - if (initrd_filename) { - int initrd_size; - uint32_t initrd_offset; - - boot_info.initrd_start = high; - initrd_offset = boot_info.initrd_start - ddr_base; - - initrd_size = load_ramdisk(initrd_filename, - boot_info.initrd_start, - ramsize - initrd_offset); - if (initrd_size < 0) { - initrd_size = load_image_targphys(initrd_filename, - boot_info.initrd_start, - ramsize - initrd_offset); - } - if (initrd_size < 0) { - error_report("could not load initrd '%s'", - initrd_filename); - exit(EXIT_FAILURE); - } - high += initrd_size; - } - high = ROUND_UP(high, 4); - boot_info.initrd_end = high; - - /* Device tree must be placed right after initrd (if available) */ - boot_info.fdt = high; - fdt_size = nios2_load_dtb(boot_info, ramsize, kernel_cmdline, - /* Preference a -dtb argument */ - dtb_arg ? dtb_arg : filename); - high += fdt_size; - - /* Kernel command is at the end, 4k aligned. */ - boot_info.cmdline = ROUND_UP(high, 4 * KiB); - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - } - g_free(filename); -} diff --git a/hw/nios2/boot.h b/hw/nios2/boot.h deleted file mode 100644 index 59b9fbfc62..0000000000 --- a/hw/nios2/boot.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef NIOS2_BOOT_H -#define NIOS2_BOOT_H - -#include "cpu.h" - -void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, uint32_t ramsize, - const char *initrd_filename, const char *dtb_filename, - void (*machine_cpu_reset)(Nios2CPU *)); - -#endif /* NIOS2_BOOT_H */ diff --git a/hw/nios2/generic_nommu.c b/hw/nios2/generic_nommu.c deleted file mode 100644 index defa16953f..0000000000 --- a/hw/nios2/generic_nommu.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Generic simulator target with no MMU or devices. This emulation is - * compatible with the libgloss qemu-hosted.ld linker script for using - * QEMU as an instruction set simulator. - * - * Copyright (c) 2018-2019 Mentor Graphics - * - * Copyright (c) 2016 Marek Vasut - * - * Based on LabX device code - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" - -#include "hw/char/serial.h" -#include "hw/boards.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "qemu/config-file.h" - -#include "boot.h" - -#define BINARY_DEVICE_TREE_FILE "generic-nommu.dtb" - -static void nios2_generic_nommu_init(MachineState *machine) -{ - Nios2CPU *cpu; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_tcm = g_new(MemoryRegion, 1); - MemoryRegion *phys_tcm_alias = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1); - ram_addr_t tcm_base = 0x0; - ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */ - ram_addr_t ram_base = 0x10000000; - ram_addr_t ram_size = 0x08000000; - - /* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */ - memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size, - &error_abort); - memory_region_init_alias(phys_tcm_alias, NULL, "nios2.tcm.alias", - phys_tcm, 0, tcm_size); - memory_region_add_subregion(address_space_mem, tcm_base, phys_tcm); - memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base, - phys_tcm_alias); - - /* Physical DRAM with alias at 0xc0000000 */ - memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size, - &error_abort); - memory_region_init_alias(phys_ram_alias, NULL, "nios2.ram.alias", - phys_ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); - memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base, - phys_ram_alias); - - cpu = NIOS2_CPU(cpu_create(TYPE_NIOS2_CPU)); - - /* Remove MMU */ - cpu->mmu_present = false; - - /* Reset vector is the first 32 bytes of RAM. */ - cpu->reset_addr = ram_base; - - /* The interrupt vector comes right after reset. */ - cpu->exception_addr = ram_base + 0x20; - - /* - * The linker script does have a TLB miss memory region declared, - * but this should never be used with no MMU. - */ - cpu->fast_tlb_miss_addr = 0x7fff400; - - nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename, - BINARY_DEVICE_TREE_FILE, NULL); -} - -static void nios2_generic_nommu_machine_init(struct MachineClass *mc) -{ - mc->desc = "Generic NOMMU Nios II design"; - mc->init = nios2_generic_nommu_init; - mc->deprecation_reason = "Nios II architecture is deprecated"; -} - -DEFINE_MACHINE("nios2-generic-nommu", nios2_generic_nommu_machine_init); diff --git a/hw/nios2/meson.build b/hw/nios2/meson.build deleted file mode 100644 index 22277bd6c5..0000000000 --- a/hw/nios2/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -nios2_ss = ss.source_set() -nios2_ss.add(files('boot.c'), fdt) -nios2_ss.add(when: 'CONFIG_NIOS2_10M50', if_true: files('10m50_devboard.c')) -nios2_ss.add(when: 'CONFIG_NIOS2_GENERIC_NOMMU', if_true: files('generic_nommu.c')) - -hw_arch += {'nios2': nios2_ss} diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index b26867b641..a1d26ce903 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -241,10 +241,6 @@ enum bfd_architecture bfd_arch_ia64, /* HP/Intel ia64 */ #define bfd_mach_ia64_elf64 64 #define bfd_mach_ia64_elf32 32 - bfd_arch_nios2, /* Nios II */ -#define bfd_mach_nios2 0 -#define bfd_mach_nios2r1 1 -#define bfd_mach_nios2r2 2 bfd_arch_rx, /* Renesas RX */ #define bfd_mach_rx 0x75 #define bfd_mach_rx_v2 0x76 @@ -456,7 +452,6 @@ int print_insn_crisv32 (bfd_vma, disassemble_info*); int print_insn_crisv10 (bfd_vma, disassemble_info*); int print_insn_microblaze (bfd_vma, disassemble_info*); int print_insn_ia64 (bfd_vma, disassemble_info*); -int print_insn_nios2(bfd_vma, disassemble_info*); int print_insn_xtensa (bfd_vma, disassemble_info*); int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); diff --git a/include/exec/poison.h b/include/exec/poison.h index 1ea5633eb3..792a83f493 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -22,7 +22,6 @@ #pragma GCC poison TARGET_ABI_MIPSO32 #pragma GCC poison TARGET_MIPS64 #pragma GCC poison TARGET_ABI_MIPSN64 -#pragma GCC poison TARGET_NIOS2 #pragma GCC poison TARGET_OPENRISC #pragma GCC poison TARGET_PPC #pragma GCC poison TARGET_PPC64 @@ -73,7 +72,6 @@ #pragma GCC poison CONFIG_M68K_DIS #pragma GCC poison CONFIG_MICROBLAZE_DIS #pragma GCC poison CONFIG_MIPS_DIS -#pragma GCC poison CONFIG_NIOS2_DIS #pragma GCC poison CONFIG_PPC_DIS #pragma GCC poison CONFIG_RISCV_DIS #pragma GCC poison CONFIG_S390_DIS diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index 6178453d94..db4a670328 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -25,8 +25,7 @@ #if (defined(TARGET_I386) && !defined(TARGET_X86_64)) \ || defined(TARGET_SH4) \ || defined(TARGET_OPENRISC) \ - || defined(TARGET_MICROBLAZE) \ - || defined(TARGET_NIOS2) + || defined(TARGET_MICROBLAZE) #define ABI_LLONG_ALIGNMENT 4 #endif diff --git a/include/hw/intc/nios2_vic.h b/include/hw/intc/nios2_vic.h deleted file mode 100644 index 5c975a2ac4..0000000000 --- a/include/hw/intc/nios2_vic.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Vectored Interrupt Controller for nios2 processor - * - * Copyright (c) 2022 Neuroblade - * - * Interface: - * QOM property "cpu": link to the Nios2 CPU (must be set) - * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines - * IRQ should be connected to nios2 IRQ0. - * - * Reference: "Embedded Peripherals IP User Guide - * for Intel® Quartus® Prime Design Suite: 21.4" - * Chapter 38 "Vectored Interrupt Controller Core" - * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef HW_INTC_NIOS2_VIC_H -#define HW_INTC_NIOS2_VIC_H - -#include "hw/sysbus.h" - -#define TYPE_NIOS2_VIC "nios2-vic" -OBJECT_DECLARE_SIMPLE_TYPE(Nios2VIC, NIOS2_VIC) - -#define NIOS2_VIC_MAX_IRQ 32 - -struct Nios2VIC { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - qemu_irq output_int; - - /* properties */ - CPUState *cpu; - MemoryRegion csr; - - uint32_t int_config[NIOS2_VIC_MAX_IRQ]; - uint32_t vic_config; - uint32_t int_raw_status; - uint32_t int_enable; - uint32_t sw_int; - uint32_t vic_status; - uint32_t vec_tbl_base; - uint32_t vec_tbl_addr; -}; - -#endif /* HW_INTC_NIOS2_VIC_H */ diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 8850cb1a14..8d041aa84e 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -18,7 +18,6 @@ enum { QEMU_ARCH_XTENSA = (1 << 12), QEMU_ARCH_OPENRISC = (1 << 13), QEMU_ARCH_TRICORE = (1 << 16), - QEMU_ARCH_NIOS2 = (1 << 17), QEMU_ARCH_HPPA = (1 << 18), QEMU_ARCH_RISCV = (1 << 19), QEMU_ARCH_RX = (1 << 20), diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 60cf55b36c..f4a0b78c75 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1505,105 +1505,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #endif /* TARGET_MICROBLAZE */ -#ifdef TARGET_NIOS2 - -#define elf_check_arch(x) ((x) == EM_ALTERA_NIOS2) - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_ALTERA_NIOS2 - -static void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->ea = infop->entry; - regs->sp = infop->start_stack; -} - -#define LO_COMMPAGE TARGET_PAGE_SIZE - -static bool init_guest_commpage(void) -{ - static const uint8_t kuser_page[4 + 2 * 64] = { - /* __kuser_helper_version */ - [0x00] = 0x02, 0x00, 0x00, 0x00, - - /* __kuser_cmpxchg */ - [0x04] = 0x3a, 0x6c, 0x3b, 0x00, /* trap 16 */ - 0x3a, 0x28, 0x00, 0xf8, /* ret */ - - /* __kuser_sigtramp */ - [0x44] = 0xc4, 0x22, 0x80, 0x00, /* movi r2, __NR_rt_sigreturn */ - 0x3a, 0x68, 0x3b, 0x00, /* trap 0 */ - }; - - int host_page_size = qemu_real_host_page_size(); - void *want, *addr; - - want = g2h_untagged(LO_COMMPAGE & -host_page_size); - addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | - (reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), - -1, 0); - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - - memcpy(g2h_untagged(LO_COMMPAGE), kuser_page, sizeof(kuser_page)); - - if (mprotect(addr, host_page_size, PROT_READ)) { - perror("Protecting guest commpage"); - exit(EXIT_FAILURE); - } - - page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, - PAGE_READ | PAGE_EXEC | PAGE_VALID); - return true; -} - -#define ELF_EXEC_PAGESIZE 4096 - -#define USE_ELF_CORE_DUMP -#define ELF_NREG 49 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUNios2State *env) -{ - int i; - - (*regs)[0] = -1; - for (i = 1; i < 8; i++) /* r0-r7 */ - (*regs)[i] = tswapreg(env->regs[i + 7]); - - for (i = 8; i < 16; i++) /* r8-r15 */ - (*regs)[i] = tswapreg(env->regs[i - 8]); - - for (i = 16; i < 24; i++) /* r16-r23 */ - (*regs)[i] = tswapreg(env->regs[i + 7]); - (*regs)[24] = -1; /* R_ET */ - (*regs)[25] = -1; /* R_BT */ - (*regs)[26] = tswapreg(env->regs[R_GP]); - (*regs)[27] = tswapreg(env->regs[R_SP]); - (*regs)[28] = tswapreg(env->regs[R_FP]); - (*regs)[29] = tswapreg(env->regs[R_EA]); - (*regs)[30] = -1; /* R_SSTATUS */ - (*regs)[31] = tswapreg(env->regs[R_RA]); - - (*regs)[32] = tswapreg(env->pc); - - (*regs)[33] = -1; /* R_STATUS */ - (*regs)[34] = tswapreg(env->regs[CR_ESTATUS]); - - for (i = 35; i < 49; i++) /* ... */ - (*regs)[i] = -1; -} - -#endif /* TARGET_NIOS2 */ - #ifdef TARGET_OPENRISC #define ELF_ARCH EM_OPENRISC diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c deleted file mode 100644 index 7fe08c8750..0000000000 --- a/linux-user/nios2/cpu_loop.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * qemu user cpu loop - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu.h" -#include "user-internals.h" -#include "cpu_loop-common.h" -#include "signal-common.h" - -void cpu_loop(CPUNios2State *env) -{ - CPUState *cs = env_cpu(env); - int trapnr, ret; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - - case EXCP_DIV: - /* Match kernel's handle_diverror_c(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); - break; - - case EXCP_UNALIGN: - case EXCP_UNALIGND: - force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, - env->ctrl[CR_BADADDR]); - break; - - case EXCP_ILLEGAL: - case EXCP_UNIMPL: - /* Match kernel's handle_illegal_c(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); - break; - case EXCP_SUPERI: - /* Match kernel's handle_supervisor_instr(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); - break; - - case EXCP_TRAP: - switch (env->error_code) { - case 0: - qemu_log_mask(CPU_LOG_INT, "\nSyscall\n"); - - ret = do_syscall(env, env->regs[2], - env->regs[4], env->regs[5], env->regs[6], - env->regs[7], env->regs[8], env->regs[9], - 0, 0); - - if (ret == -QEMU_ESIGRETURN) { - /* rt_sigreturn has set all state. */ - break; - } - if (ret == -QEMU_ERESTARTSYS) { - env->pc -= 4; - break; - } - /* - * See the code after translate_rc_and_ret: all negative - * values are errors (aided by userspace restricted to 2G), - * errno is returned positive in r2, and error indication - * is a boolean in r7. - */ - env->regs[2] = abs(ret); - env->regs[7] = ret < 0; - break; - - case 1: - qemu_log_mask(CPU_LOG_INT, "\nTrap 1\n"); - force_sig_fault(TARGET_SIGUSR1, 0, env->pc); - break; - case 2: - qemu_log_mask(CPU_LOG_INT, "\nTrap 2\n"); - force_sig_fault(TARGET_SIGUSR2, 0, env->pc); - break; - case 31: - qemu_log_mask(CPU_LOG_INT, "\nTrap 31\n"); - /* Match kernel's breakpoint_c(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); - break; - default: - qemu_log_mask(CPU_LOG_INT, "\nTrap %d\n", env->error_code); - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, env->pc); - break; - - case 16: /* QEMU specific, for __kuser_cmpxchg */ - { - abi_ptr g = env->regs[4]; - uint32_t *h, n, o; - - if (g & 0x3) { - force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, g); - break; - } - ret = page_get_flags(g); - if (!(ret & PAGE_VALID)) { - force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, g); - break; - } - if (!(ret & PAGE_READ) || !(ret & PAGE_WRITE)) { - force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_ACCERR, g); - break; - } - h = g2h(cs, g); - o = env->regs[5]; - n = env->regs[6]; - env->regs[2] = qatomic_cmpxchg(h, o, n) - o; - } - break; - } - break; - - case EXCP_DEBUG: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); - break; - default: - EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", - trapnr); - abort(); - } - - process_pending_signals(env); - } -} - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) -{ - env->regs[R_SP] = regs->sp; - env->pc = regs->ea; -} diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c deleted file mode 100644 index 64c345f409..0000000000 --- a/linux-user/nios2/signal.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Emulation of Linux signals - * - * Copyright (c) 2003 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu.h" -#include "user-internals.h" -#include "signal-common.h" -#include "linux-user/trace.h" - -#define MCONTEXT_VERSION 2 - -struct target_sigcontext { - int version; - unsigned long gregs[32]; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -static void rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) -{ - unsigned long *gregs = uc->tuc_mcontext.gregs; - - __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); - __put_user(env->regs[1], &gregs[0]); - __put_user(env->regs[2], &gregs[1]); - __put_user(env->regs[3], &gregs[2]); - __put_user(env->regs[4], &gregs[3]); - __put_user(env->regs[5], &gregs[4]); - __put_user(env->regs[6], &gregs[5]); - __put_user(env->regs[7], &gregs[6]); - __put_user(env->regs[8], &gregs[7]); - __put_user(env->regs[9], &gregs[8]); - __put_user(env->regs[10], &gregs[9]); - __put_user(env->regs[11], &gregs[10]); - __put_user(env->regs[12], &gregs[11]); - __put_user(env->regs[13], &gregs[12]); - __put_user(env->regs[14], &gregs[13]); - __put_user(env->regs[15], &gregs[14]); - __put_user(env->regs[16], &gregs[15]); - __put_user(env->regs[17], &gregs[16]); - __put_user(env->regs[18], &gregs[17]); - __put_user(env->regs[19], &gregs[18]); - __put_user(env->regs[20], &gregs[19]); - __put_user(env->regs[21], &gregs[20]); - __put_user(env->regs[22], &gregs[21]); - __put_user(env->regs[23], &gregs[22]); - __put_user(env->regs[R_RA], &gregs[23]); - __put_user(env->regs[R_FP], &gregs[24]); - __put_user(env->regs[R_GP], &gregs[25]); - __put_user(env->pc, &gregs[27]); - __put_user(env->regs[R_SP], &gregs[28]); -} - -static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc) -{ - int temp; - unsigned long *gregs = uc->tuc_mcontext.gregs; - - /* Always make any pending restarted system calls return -EINTR */ - /* current->restart_block.fn = do_no_restart_syscall; */ - - __get_user(temp, &uc->tuc_mcontext.version); - if (temp != MCONTEXT_VERSION) { - return 1; - } - - /* restore passed registers */ - __get_user(env->regs[1], &gregs[0]); - __get_user(env->regs[2], &gregs[1]); - __get_user(env->regs[3], &gregs[2]); - __get_user(env->regs[4], &gregs[3]); - __get_user(env->regs[5], &gregs[4]); - __get_user(env->regs[6], &gregs[5]); - __get_user(env->regs[7], &gregs[6]); - __get_user(env->regs[8], &gregs[7]); - __get_user(env->regs[9], &gregs[8]); - __get_user(env->regs[10], &gregs[9]); - __get_user(env->regs[11], &gregs[10]); - __get_user(env->regs[12], &gregs[11]); - __get_user(env->regs[13], &gregs[12]); - __get_user(env->regs[14], &gregs[13]); - __get_user(env->regs[15], &gregs[14]); - __get_user(env->regs[16], &gregs[15]); - __get_user(env->regs[17], &gregs[16]); - __get_user(env->regs[18], &gregs[17]); - __get_user(env->regs[19], &gregs[18]); - __get_user(env->regs[20], &gregs[19]); - __get_user(env->regs[21], &gregs[20]); - __get_user(env->regs[22], &gregs[21]); - __get_user(env->regs[23], &gregs[22]); - /* gregs[23] is handled below */ - /* Verify, should this be settable */ - __get_user(env->regs[R_FP], &gregs[24]); - /* Verify, should this be settable */ - __get_user(env->regs[R_GP], &gregs[25]); - /* Not really necessary no user settable bits */ - __get_user(temp, &gregs[26]); - __get_user(env->pc, &gregs[27]); - - __get_user(env->regs[R_RA], &gregs[23]); - __get_user(env->regs[R_SP], &gregs[28]); - - target_restore_altstack(&uc->tuc_stack, env); - return 0; -} - -static abi_ptr get_sigframe(struct target_sigaction *ka, CPUNios2State *env, - size_t frame_size) -{ - unsigned long usp; - - /* This is the X/Open sanctioned signal stack switching. */ - usp = target_sigsp(get_sp_from_cpustate(env), ka); - - /* Verify, is it 32 or 64 bit aligned */ - return (usp - frame_size) & -8; -} - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, - CPUNios2State *env) -{ - struct target_rt_sigframe *frame; - abi_ptr frame_addr; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - force_sigsegv(sig); - return; - } - - frame->info = *info; - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - target_save_altstack(&frame->uc.tuc_stack, env); - rt_setup_ucontext(&frame->uc, env); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace; jump to fixed address sigreturn - trampoline on kuser page. */ - env->regs[R_RA] = (unsigned long) (0x1044); - - /* Set up registers for signal handler */ - env->regs[R_SP] = frame_addr; - env->regs[4] = sig; - env->regs[5] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->regs[6] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->pc = ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); -} - -long do_rt_sigreturn(CPUNios2State *env) -{ - /* Verify, can we follow the stack back */ - abi_ulong frame_addr = env->regs[R_SP]; - struct target_rt_sigframe *frame; - sigset_t set; - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - if (rt_restore_ucontext(env, &frame->uc)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -QEMU_ESIGRETURN; -} diff --git a/linux-user/nios2/sockbits.h b/linux-user/nios2/sockbits.h deleted file mode 100644 index 0e4c8f012d..0000000000 --- a/linux-user/nios2/sockbits.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/sockbits.h" diff --git a/linux-user/nios2/syscall_nr.h b/linux-user/nios2/syscall_nr.h deleted file mode 100644 index 11a37b32e8..0000000000 --- a/linux-user/nios2/syscall_nr.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_NIOS2_SYSCALL_NR_H -#define LINUX_USER_NIOS2_SYSCALL_NR_H - -#define TARGET_NR_cacheflush (TARGET_NR_arch_specific_syscall) -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl64 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs64 43 -#define TARGET_NR_fstatfs64 44 -#define TARGET_NR_truncate64 45 -#define TARGET_NR_ftruncate64 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_llseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile64 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 -#define TARGET_NR_fstat64 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_io_pgetevents 292 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_clock_gettime64 403 -#define TARGET_NR_clock_settime64 404 -#define TARGET_NR_clock_adjtime64 405 -#define TARGET_NR_clock_getres_time64 406 -#define TARGET_NR_clock_nanosleep_time64 407 -#define TARGET_NR_timer_gettime64 408 -#define TARGET_NR_timer_settime64 409 -#define TARGET_NR_timerfd_gettime64 410 -#define TARGET_NR_timerfd_settime64 411 -#define TARGET_NR_utimensat_time64 412 -#define TARGET_NR_pselect6_time64 413 -#define TARGET_NR_ppoll_time64 414 -#define TARGET_NR_io_pgetevents_time64 416 -#define TARGET_NR_recvmmsg_time64 417 -#define TARGET_NR_mq_timedsend_time64 418 -#define TARGET_NR_mq_timedreceive_time64 419 -#define TARGET_NR_semtimedop_time64 420 -#define TARGET_NR_rt_sigtimedwait_time64 421 -#define TARGET_NR_futex_time64 422 -#define TARGET_NR_sched_rr_get_interval_time64 423 -#define TARGET_NR_pidfd_send_signal 424 -#define TARGET_NR_io_uring_setup 425 -#define TARGET_NR_io_uring_enter 426 -#define TARGET_NR_io_uring_register 427 -#define TARGET_NR_open_tree 428 -#define TARGET_NR_move_mount 429 -#define TARGET_NR_fsopen 430 -#define TARGET_NR_fsconfig 431 -#define TARGET_NR_fsmount 432 -#define TARGET_NR_fspick 433 -#define TARGET_NR_pidfd_open 434 -#define TARGET_NR_close_range 436 -#define TARGET_NR_openat2 437 -#define TARGET_NR_pidfd_getfd 438 -#define TARGET_NR_faccessat2 439 -#define TARGET_NR_process_madvise 440 -#define TARGET_NR_epoll_pwait2 441 -#define TARGET_NR_mount_setattr 442 -#define TARGET_NR_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_NIOS2_SYSCALL_NR_H */ diff --git a/linux-user/nios2/target_cpu.h b/linux-user/nios2/target_cpu.h deleted file mode 100644 index 830b4c0741..0000000000 --- a/linux-user/nios2/target_cpu.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Nios2 specific CPU ABI and functions for linux-user - * - * Copyright (c) 2016 Marek Vasut - * - * 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.1 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 . - */ - -#ifndef NIOS2_TARGET_CPU_H -#define NIOS2_TARGET_CPU_H - -static inline void cpu_clone_regs_child(CPUNios2State *env, target_ulong newsp, - unsigned flags) -{ - if (newsp) { - env->regs[R_SP] = newsp; - } - env->regs[R_RET0] = 0; - env->regs[7] = 0; -} - -static inline void cpu_clone_regs_parent(CPUNios2State *env, unsigned flags) -{ -} - -static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls) -{ - /* - * Linux kernel 3.10 does not pay any attention to CLONE_SETTLS - * in copy_thread(), so QEMU need not do so either. - */ -} - -static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) -{ - return state->regs[R_SP]; -} -#endif diff --git a/linux-user/nios2/target_elf.h b/linux-user/nios2/target_elf.h deleted file mode 100644 index 801e20afaf..0000000000 --- a/linux-user/nios2/target_elf.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef NIOS2_TARGET_ELF_H -#define NIOS2_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} -#endif diff --git a/linux-user/nios2/target_errno_defs.h b/linux-user/nios2/target_errno_defs.h deleted file mode 100644 index 28120013e2..0000000000 --- a/linux-user/nios2/target_errno_defs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NIOS2_TARGET_ERRNO_DEFS_H -#define NIOS2_TARGET_ERRNO_DEFS_H - -/* Target uses generic errno */ -#include "../generic/target_errno_defs.h" - -#endif diff --git a/linux-user/nios2/target_fcntl.h b/linux-user/nios2/target_fcntl.h deleted file mode 100644 index 714583215d..0000000000 --- a/linux-user/nios2/target_fcntl.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef NIOS2_TARGET_FCNTL_H -#define NIOS2_TARGET_FCNTL_H -#include "../generic/fcntl.h" -#endif diff --git a/linux-user/nios2/target_mman.h b/linux-user/nios2/target_mman.h deleted file mode 100644 index ab16ad4f03..0000000000 --- a/linux-user/nios2/target_mman.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * arch/nios2/include/asm/processor.h: - * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) - * TASK_SIZE 0x7FFF0000UL - */ -#define TASK_UNMAPPED_BASE TARGET_PAGE_ALIGN(0x7FFF0000 / 3) - -/* arch/nios2/include/asm/elf.h */ -#define ELF_ET_DYN_BASE 0xD0000000 - -#include "../generic/target_mman.h" diff --git a/linux-user/nios2/target_prctl.h b/linux-user/nios2/target_prctl.h deleted file mode 100644 index eb53b31ad5..0000000000 --- a/linux-user/nios2/target_prctl.h +++ /dev/null @@ -1 +0,0 @@ -/* No special prctl support required. */ diff --git a/linux-user/nios2/target_proc.h b/linux-user/nios2/target_proc.h deleted file mode 100644 index 43fe29ca72..0000000000 --- a/linux-user/nios2/target_proc.h +++ /dev/null @@ -1 +0,0 @@ -/* No target-specific /proc support */ diff --git a/linux-user/nios2/target_resource.h b/linux-user/nios2/target_resource.h deleted file mode 100644 index 227259594c..0000000000 --- a/linux-user/nios2/target_resource.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_resource.h" diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h deleted file mode 100644 index 46ca5948ce..0000000000 --- a/linux-user/nios2/target_signal.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef NIOS2_TARGET_SIGNAL_H -#define NIOS2_TARGET_SIGNAL_H - -#include "../generic/signal.h" - -/* Nios2 uses a fixed address on the kuser page for sigreturn. */ -#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 - -#endif /* NIOS2_TARGET_SIGNAL_H */ diff --git a/linux-user/nios2/target_structs.h b/linux-user/nios2/target_structs.h deleted file mode 100644 index 3a06f373c3..0000000000 --- a/linux-user/nios2/target_structs.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_structs.h" diff --git a/linux-user/nios2/target_syscall.h b/linux-user/nios2/target_syscall.h deleted file mode 100644 index 561b28d281..0000000000 --- a/linux-user/nios2/target_syscall.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef NIOS2_TARGET_SYSCALL_H -#define NIOS2_TARGET_SYSCALL_H - -#define UNAME_MACHINE "nios2" -#define UNAME_MINIMUM_RELEASE "3.19.0" - -struct target_pt_regs { - unsigned long r8; /* r8-r15 Caller-saved GP registers */ - unsigned long r9; - unsigned long r10; - unsigned long r11; - unsigned long r12; - unsigned long r13; - unsigned long r14; - unsigned long r15; - unsigned long r1; /* Assembler temporary */ - unsigned long r2; /* Retval LS 32bits */ - unsigned long r3; /* Retval MS 32bits */ - unsigned long r4; /* r4-r7 Register arguments */ - unsigned long r5; - unsigned long r6; - unsigned long r7; - unsigned long orig_r2; /* Copy of r2 ?? */ - unsigned long ra; /* Return address */ - unsigned long fp; /* Frame pointer */ - unsigned long sp; /* Stack pointer */ - unsigned long gp; /* Global pointer */ - unsigned long estatus; - unsigned long ea; /* Exception return address (pc) */ - unsigned long orig_r7; -}; - -#define TARGET_MCL_CURRENT 1 -#define TARGET_MCL_FUTURE 2 -#define TARGET_MCL_ONFAULT 4 - -#endif /* NIOS2_TARGET_SYSCALL_H */ diff --git a/linux-user/nios2/termbits.h b/linux-user/nios2/termbits.h deleted file mode 100644 index b1d4f4fedb..0000000000 --- a/linux-user/nios2/termbits.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/termbits.h" diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 744fda599e..3995487630 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -73,7 +73,7 @@ #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ || defined(TARGET_M68K) || defined(TARGET_CRIS) \ || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \ + || defined(TARGET_RISCV) \ || defined(TARGET_XTENSA) || defined(TARGET_LOONGARCH64) #define TARGET_IOC_SIZEBITS 14 @@ -1974,7 +1974,7 @@ struct target_stat64 { abi_ulong __unused5; }; -#elif defined(TARGET_OPENRISC) || defined(TARGET_NIOS2) \ +#elif defined(TARGET_OPENRISC) \ || defined(TARGET_RISCV) || defined(TARGET_HEXAGON) /* These are the asm-generic versions of the stat and stat64 structures */ diff --git a/meson.build b/meson.build index 91a0aa64c6..58d4e157db 100644 --- a/meson.build +++ b/meson.build @@ -2971,7 +2971,6 @@ disassemblers = { 'm68k' : ['CONFIG_M68K_DIS'], 'microblaze' : ['CONFIG_MICROBLAZE_DIS'], 'mips' : ['CONFIG_MIPS_DIS'], - 'nios2' : ['CONFIG_NIOS2_DIS'], 'or1k' : ['CONFIG_OPENRISC_DIS'], 'ppc' : ['CONFIG_PPC_DIS'], 'riscv' : ['CONFIG_RISCV_DIS'], @@ -3398,7 +3397,6 @@ if have_system or have_user 'target/i386/kvm', 'target/loongarch', 'target/mips/tcg', - 'target/nios2', 'target/ppc', 'target/riscv', 'target/s390x', diff --git a/qapi/machine.json b/qapi/machine.json index e8b60641f2..6ab713a4c4 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -33,7 +33,7 @@ { 'enum' : 'SysEmuTarget', 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', - 'mips64el', 'mipsel', 'nios2', 'or1k', 'ppc', + 'mips64el', 'mipsel', 'or1k', 'ppc', 'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4', 'sh4eb', 'sparc', 'sparc64', 'tricore', 'x86_64', 'xtensa', 'xtensaeb' ] } diff --git a/qemu-options.hx b/qemu-options.hx index 8ce85d4559..617d4c6ebd 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4849,10 +4849,10 @@ ERST DEF("semihosting", 0, QEMU_OPTION_semihosting, "-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | - QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) + QEMU_ARCH_MIPS | QEMU_ARCH_RISCV) SRST ``-semihosting`` - Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). + Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, RISC-V only). .. warning:: Note that this allows guest direct access to the host filesystem, so @@ -4865,10 +4865,10 @@ DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, "-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]\n" \ " semihosting configuration\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | -QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) +QEMU_ARCH_MIPS | QEMU_ARCH_RISCV) SRST ``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]`` - Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V + Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, RISC-V only). .. warning:: diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 0e62f10aad..91be8d1c36 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -36,9 +36,6 @@ microblaze mips ~ (/qemu)?((/include)?/hw/mips/.*|/target/mips/.*) -nios2 - ~ (/qemu)?((/include)?/hw/nios2/.*|/target/nios2/.*) - openrisc ~ (/qemu)?((/include)?/hw/openrisc/.*|/target/openrisc/.*) diff --git a/scripts/gensyscalls.sh b/scripts/gensyscalls.sh index a2f7664b7b..84957280da 100755 --- a/scripts/gensyscalls.sh +++ b/scripts/gensyscalls.sh @@ -94,7 +94,6 @@ mkdir "$TMP/asm" > "$TMP/asm/bitsperlong.h" generate_syscall_nr arm64 64 "$output/linux-user/aarch64/syscall_nr.h" -generate_syscall_nr nios2 32 "$output/linux-user/nios2/syscall_nr.h" generate_syscall_nr openrisc 32 "$output/linux-user/openrisc/syscall_nr.h" generate_syscall_nr riscv 32 "$output/linux-user/riscv/syscall32_nr.h" diff --git a/scripts/probe-gdb-support.py b/scripts/probe-gdb-support.py index 5755255966..46d6c00140 100644 --- a/scripts/probe-gdb-support.py +++ b/scripts/probe-gdb-support.py @@ -37,7 +37,6 @@ mappings = { "m68k" : "m68k", "MicroBlaze" : "microblaze", "mips:isa64" : ["mips64", "mips64el"], - "nios2" : "nios2", "or1k" : "or1k", "powerpc:common" : "ppc", "powerpc:common64" : ["ppc64", "ppc64le"], diff --git a/target/Kconfig b/target/Kconfig index 83da0bd293..5275a93ad0 100644 --- a/target/Kconfig +++ b/target/Kconfig @@ -8,7 +8,6 @@ source loongarch/Kconfig source m68k/Kconfig source microblaze/Kconfig source mips/Kconfig -source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/Kconfig diff --git a/target/meson.build b/target/meson.build index dee2ac47e0..59b46b2ef4 100644 --- a/target/meson.build +++ b/target/meson.build @@ -9,7 +9,6 @@ subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') -subdir('nios2') subdir('openrisc') subdir('ppc') subdir('riscv') diff --git a/target/nios2/Kconfig b/target/nios2/Kconfig deleted file mode 100644 index c65550c861..0000000000 --- a/target/nios2/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -config NIOS2 - bool - select SEMIHOSTING diff --git a/target/nios2/cpu-param.h b/target/nios2/cpu-param.h deleted file mode 100644 index 767bba4b7b..0000000000 --- a/target/nios2/cpu-param.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Altera Nios II cpu parameters for qemu. - * - * Copyright (c) 2012 Chris Wulff - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef NIOS2_CPU_PARAM_H -#define NIOS2_CPU_PARAM_H - -#define TARGET_LONG_BITS 32 -#define TARGET_PAGE_BITS 12 -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#ifdef CONFIG_USER_ONLY -# define TARGET_VIRT_ADDR_SPACE_BITS 31 -#else -# define TARGET_VIRT_ADDR_SPACE_BITS 32 -#endif - -#endif diff --git a/target/nios2/cpu-qom.h b/target/nios2/cpu-qom.h deleted file mode 100644 index 2fd9121540..0000000000 --- a/target/nios2/cpu-qom.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * QEMU Nios II CPU QOM header (target agnostic) - * - * Copyright (c) 2012 Chris Wulff - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef QEMU_NIOS2_CPU_QOM_H -#define QEMU_NIOS2_CPU_QOM_H - -#include "hw/core/cpu.h" - -#define TYPE_NIOS2_CPU "nios2-cpu" - -OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) - -#endif diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c deleted file mode 100644 index 679aff5730..0000000000 --- a/target/nios2/cpu.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * QEMU Nios II CPU - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "qemu/module.h" -#include "qapi/error.h" -#include "cpu.h" -#include "exec/log.h" -#include "gdbstub/helpers.h" -#include "hw/qdev-properties.h" - -static void nios2_cpu_set_pc(CPUState *cs, vaddr value) -{ - cpu_env(cs)->pc = value; -} - -static vaddr nios2_cpu_get_pc(CPUState *cs) -{ - return cpu_env(cs)->pc; -} - -static void nios2_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data) -{ - cpu_env(cs)->pc = data[0]; -} - -static bool nios2_cpu_has_work(CPUState *cs) -{ - return cs->interrupt_request & CPU_INTERRUPT_HARD; -} - -static int nios2_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return (cpu_env(cs)->ctrl[CR_STATUS] & CR_STATUS_U - ? MMU_USER_IDX : MMU_SUPERVISOR_IDX); -} - -static void nios2_cpu_reset_hold(Object *obj) -{ - CPUState *cs = CPU(obj); - Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(obj); - CPUNios2State *env = &cpu->env; - - if (ncc->parent_phases.hold) { - ncc->parent_phases.hold(obj); - } - - memset(env->ctrl, 0, sizeof(env->ctrl)); - env->pc = cpu->reset_addr; - -#if defined(CONFIG_USER_ONLY) - /* Start in user mode with interrupts enabled. */ - env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE; - memset(env->regs, 0, sizeof(env->regs)); -#else - env->ctrl[CR_STATUS] = CR_STATUS_RSIE; - nios2_update_crs(env); - memset(env->shadow_regs, 0, sizeof(env->shadow_regs)); -#endif -} - -#ifndef CONFIG_USER_ONLY -static void eic_set_irq(void *opaque, int irq, int level) -{ - Nios2CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void iic_set_irq(void *opaque, int irq, int level) -{ - Nios2CPU *cpu = opaque; - CPUNios2State *env = &cpu->env; - CPUState *cs = CPU(cpu); - - env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level); - - if (env->ctrl[CR_IPENDING]) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} -#endif - -static void nios2_cpu_initfn(Object *obj) -{ -#if !defined(CONFIG_USER_ONLY) - Nios2CPU *cpu = NIOS2_CPU(obj); - - mmu_init(&cpu->env); -#endif -} - -static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model) -{ - return object_class_by_name(TYPE_NIOS2_CPU); -} - -static void realize_cr_status(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - - /* Begin with all fields of all registers are reserved. */ - memset(cpu->cr_state, 0, sizeof(cpu->cr_state)); - - /* - * The combination of writable and readonly is the set of all - * non-reserved fields. We apply writable as a mask to bits, - * and merge in existing readonly bits, before storing. - */ -#define WR_REG(C) cpu->cr_state[C].writable = -1 -#define RO_REG(C) cpu->cr_state[C].readonly = -1 -#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK -#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK - - WR_FIELD(CR_STATUS, PIE); - WR_REG(CR_ESTATUS); - WR_REG(CR_BSTATUS); - RO_REG(CR_CPUID); - RO_REG(CR_EXCEPTION); - WR_REG(CR_BADADDR); - - if (cpu->eic_present) { - WR_FIELD(CR_STATUS, RSIE); - RO_FIELD(CR_STATUS, NMI); - WR_FIELD(CR_STATUS, PRS); - RO_FIELD(CR_STATUS, CRS); - WR_FIELD(CR_STATUS, IL); - WR_FIELD(CR_STATUS, IH); - } else { - RO_FIELD(CR_STATUS, RSIE); - WR_REG(CR_IENABLE); - RO_REG(CR_IPENDING); - } - - if (cpu->mmu_present) { - WR_FIELD(CR_STATUS, U); - WR_FIELD(CR_STATUS, EH); - - WR_FIELD(CR_PTEADDR, VPN); - WR_FIELD(CR_PTEADDR, PTBASE); - - RO_FIELD(CR_TLBMISC, D); - RO_FIELD(CR_TLBMISC, PERM); - RO_FIELD(CR_TLBMISC, BAD); - RO_FIELD(CR_TLBMISC, DBL); - WR_FIELD(CR_TLBMISC, PID); - WR_FIELD(CR_TLBMISC, WE); - WR_FIELD(CR_TLBMISC, RD); - WR_FIELD(CR_TLBMISC, WAY); - - WR_REG(CR_TLBACC); - } - - /* - * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are - * unimplemented, so their corresponding control regs remain reserved. - */ - -#undef WR_REG -#undef RO_REG -#undef WR_FIELD -#undef RO_FIELD -} - -static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) -{ - CPUState *cs = CPU(dev); - Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev); - Error *local_err = NULL; - - cpu_exec_realizefn(cs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - - realize_cr_status(cs); - qemu_init_vcpu(cs); - cpu_reset(cs); - - /* We have reserved storage for cpuid; might as well use it. */ - cpu->env.ctrl[CR_CPUID] = cs->cpu_index; - -#ifndef CONFIG_USER_ONLY - if (cpu->eic_present) { - qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); - } else { - qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); - } -#endif - - ncc->parent_realize(dev, errp); -} - -#ifndef CONFIG_USER_ONLY -static bool eic_take_interrupt(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - const uint32_t status = env->ctrl[CR_STATUS]; - - if (cpu->rnmi) { - return !(status & CR_STATUS_NMI); - } - if (!(status & CR_STATUS_PIE)) { - return false; - } - if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) { - return false; - } - if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) { - return true; - } - return status & CR_STATUS_RSIE; -} - -static bool iic_take_interrupt(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - - if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) { - return false; - } - return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE]; -} - -static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - - if (interrupt_request & CPU_INTERRUPT_HARD) { - if (cpu->eic_present - ? eic_take_interrupt(cpu) - : iic_take_interrupt(cpu)) { - cs->exception_index = EXCP_IRQ; - nios2_cpu_do_interrupt(cs); - return true; - } - } - return false; -} -#endif /* !CONFIG_USER_ONLY */ - -static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) -{ - /* NOTE: NiosII R2 is not supported yet. */ - info->mach = bfd_arch_nios2; - info->print_insn = print_insn_nios2; -} - -static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - uint32_t val; - - if (n < 32) { /* GP regs */ - val = env->regs[n]; - } else if (n == 32) { /* PC */ - val = env->pc; - } else if (n < 49) { /* Status regs */ - unsigned cr = n - 33; - if (nios2_cr_reserved(&cpu->cr_state[cr])) { - val = 0; - } else { - val = env->ctrl[n - 33]; - } - } else { - /* Invalid regs */ - return 0; - } - - return gdb_get_reg32(mem_buf, val); -} - -static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); - CPUNios2State *env = &cpu->env; - uint32_t val; - - if (n > cc->gdb_num_core_regs) { - return 0; - } - val = ldl_p(mem_buf); - - if (n < 32) { /* GP regs */ - env->regs[n] = val; - } else if (n == 32) { /* PC */ - env->pc = val; - } else if (n < 49) { /* Status regs */ - unsigned cr = n - 33; - /* ??? Maybe allow the debugger to write to readonly fields. */ - val &= cpu->cr_state[cr].writable; - val |= cpu->cr_state[cr].readonly & env->ctrl[cr]; - env->ctrl[cr] = val; - } else { - g_assert_not_reached(); - } - - return 4; -} - -static Property nios2_properties[] = { - DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true), - DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true), - /* ALTR,pid-num-bits */ - DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8), - /* ALTR,tlb-num-ways */ - DEFINE_PROP_UINT32("mmu_tlb_num_ways", Nios2CPU, tlb_num_ways, 16), - /* ALTR,tlb-num-entries */ - DEFINE_PROP_UINT32("mmu_pid_num_entries", Nios2CPU, tlb_num_entries, 256), - DEFINE_PROP_END_OF_LIST(), -}; - -#ifndef CONFIG_USER_ONLY -#include "hw/core/sysemu-cpu-ops.h" - -static const struct SysemuCPUOps nios2_sysemu_ops = { - .get_phys_page_debug = nios2_cpu_get_phys_page_debug, -}; -#endif - -#include "hw/core/tcg-cpu-ops.h" - -static const TCGCPUOps nios2_tcg_ops = { - .initialize = nios2_tcg_init, - .restore_state_to_opc = nios2_restore_state_to_opc, - -#ifndef CONFIG_USER_ONLY - .tlb_fill = nios2_cpu_tlb_fill, - .cpu_exec_interrupt = nios2_cpu_exec_interrupt, - .do_interrupt = nios2_cpu_do_interrupt, - .do_unaligned_access = nios2_cpu_do_unaligned_access, -#endif /* !CONFIG_USER_ONLY */ -}; - -static void nios2_cpu_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - CPUClass *cc = CPU_CLASS(oc); - Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc); - ResettableClass *rc = RESETTABLE_CLASS(oc); - - device_class_set_parent_realize(dc, nios2_cpu_realizefn, - &ncc->parent_realize); - device_class_set_props(dc, nios2_properties); - resettable_class_set_parent_phases(rc, NULL, nios2_cpu_reset_hold, NULL, - &ncc->parent_phases); - - cc->class_by_name = nios2_cpu_class_by_name; - cc->has_work = nios2_cpu_has_work; - cc->mmu_index = nios2_cpu_mmu_index; - cc->dump_state = nios2_cpu_dump_state; - cc->set_pc = nios2_cpu_set_pc; - cc->get_pc = nios2_cpu_get_pc; - cc->disas_set_info = nios2_cpu_disas_set_info; -#ifndef CONFIG_USER_ONLY - cc->sysemu_ops = &nios2_sysemu_ops; -#endif - cc->gdb_read_register = nios2_cpu_gdb_read_register; - cc->gdb_write_register = nios2_cpu_gdb_write_register; - cc->gdb_num_core_regs = 49; - cc->tcg_ops = &nios2_tcg_ops; -} - -static const TypeInfo nios2_cpu_type_info = { - .name = TYPE_NIOS2_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(Nios2CPU), - .instance_align = __alignof(Nios2CPU), - .instance_init = nios2_cpu_initfn, - .class_size = sizeof(Nios2CPUClass), - .class_init = nios2_cpu_class_init, -}; - -static void nios2_cpu_register_types(void) -{ - type_register_static(&nios2_cpu_type_info); -} - -type_init(nios2_cpu_register_types) diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h deleted file mode 100644 index 4164a3432e..0000000000 --- a/target/nios2/cpu.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Altera Nios II virtual CPU header - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#ifndef NIOS2_CPU_H -#define NIOS2_CPU_H - -#include "cpu-qom.h" -#include "exec/cpu-defs.h" -#include "hw/registerfields.h" - -typedef struct CPUArchState CPUNios2State; -#if !defined(CONFIG_USER_ONLY) -#include "mmu.h" -#endif - -/** - * Nios2CPUClass: - * @parent_phases: The parent class' reset phase handlers. - * - * A Nios2 CPU model. - */ -struct Nios2CPUClass { - CPUClass parent_class; - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - -#define TARGET_HAS_ICE 1 - -/* Configuration options for Nios II */ -#define RESET_ADDRESS 0x00000000 -#define EXCEPTION_ADDRESS 0x00000004 -#define FAST_TLB_MISS_ADDRESS 0x00000008 - -#define NUM_GP_REGS 32 -#define NUM_CR_REGS 32 - -#ifndef CONFIG_USER_ONLY -/* 63 shadow register sets; index 0 is the primary register set. */ -#define NUM_REG_SETS 64 -#endif - -/* General purpose register aliases */ -enum { - R_ZERO = 0, - R_AT = 1, - R_RET0 = 2, - R_RET1 = 3, - R_ARG0 = 4, - R_ARG1 = 5, - R_ARG2 = 6, - R_ARG3 = 7, - R_ET = 24, - R_BT = 25, - R_GP = 26, - R_SP = 27, - R_FP = 28, - R_EA = 29, - R_BA = 30, - R_SSTATUS = 30, - R_RA = 31, -}; - -/* Control register aliases */ -enum { - CR_STATUS = 0, - CR_ESTATUS = 1, - CR_BSTATUS = 2, - CR_IENABLE = 3, - CR_IPENDING = 4, - CR_CPUID = 5, - CR_EXCEPTION = 7, - CR_PTEADDR = 8, - CR_TLBACC = 9, - CR_TLBMISC = 10, - CR_ENCINJ = 11, - CR_BADADDR = 12, - CR_CONFIG = 13, - CR_MPUBASE = 14, - CR_MPUACC = 15, -}; - -FIELD(CR_STATUS, PIE, 0, 1) -FIELD(CR_STATUS, U, 1, 1) -FIELD(CR_STATUS, EH, 2, 1) -FIELD(CR_STATUS, IH, 3, 1) -FIELD(CR_STATUS, IL, 4, 6) -FIELD(CR_STATUS, CRS, 10, 6) -FIELD(CR_STATUS, PRS, 16, 6) -FIELD(CR_STATUS, NMI, 22, 1) -FIELD(CR_STATUS, RSIE, 23, 1) -FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */ - -#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK -#define CR_STATUS_U R_CR_STATUS_U_MASK -#define CR_STATUS_EH R_CR_STATUS_EH_MASK -#define CR_STATUS_IH R_CR_STATUS_IH_MASK -#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK -#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK -#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK - -FIELD(CR_EXCEPTION, CAUSE, 2, 5) -FIELD(CR_EXCEPTION, ECCFTL, 31, 1) - -FIELD(CR_PTEADDR, VPN, 2, 20) -FIELD(CR_PTEADDR, PTBASE, 22, 10) - -FIELD(CR_TLBACC, PFN, 0, 20) -FIELD(CR_TLBACC, G, 20, 1) -FIELD(CR_TLBACC, X, 21, 1) -FIELD(CR_TLBACC, W, 22, 1) -FIELD(CR_TLBACC, R, 23, 1) -FIELD(CR_TLBACC, C, 24, 1) -FIELD(CR_TLBACC, IG, 25, 7) - -#define CR_TLBACC_C R_CR_TLBACC_C_MASK -#define CR_TLBACC_R R_CR_TLBACC_R_MASK -#define CR_TLBACC_W R_CR_TLBACC_W_MASK -#define CR_TLBACC_X R_CR_TLBACC_X_MASK -#define CR_TLBACC_G R_CR_TLBACC_G_MASK - -FIELD(CR_TLBMISC, D, 0, 1) -FIELD(CR_TLBMISC, PERM, 1, 1) -FIELD(CR_TLBMISC, BAD, 2, 1) -FIELD(CR_TLBMISC, DBL, 3, 1) -FIELD(CR_TLBMISC, PID, 4, 14) -FIELD(CR_TLBMISC, WE, 18, 1) -FIELD(CR_TLBMISC, RD, 19, 1) -FIELD(CR_TLBMISC, WAY, 20, 4) -FIELD(CR_TLBMISC, EE, 24, 1) - -#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK -#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK -#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK -#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK -#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK -#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK -#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK - -/* Exceptions */ -#define EXCP_BREAK 0x1000 -#define EXCP_SEMIHOST 0x1001 -#define EXCP_RESET 0 -#define EXCP_PRESET 1 -#define EXCP_IRQ 2 -#define EXCP_TRAP 3 -#define EXCP_UNIMPL 4 -#define EXCP_ILLEGAL 5 -#define EXCP_UNALIGN 6 -#define EXCP_UNALIGND 7 -#define EXCP_DIV 8 -#define EXCP_SUPERA_X 9 -#define EXCP_SUPERI 10 -#define EXCP_SUPERA_D 11 -#define EXCP_TLB_X 12 -#define EXCP_TLB_D (0x1000 | EXCP_TLB_X) -#define EXCP_PERM_X 13 -#define EXCP_PERM_R 14 -#define EXCP_PERM_W 15 -#define EXCP_MPUI 16 -#define EXCP_MPUD 17 - -struct CPUArchState { -#ifdef CONFIG_USER_ONLY - uint32_t regs[NUM_GP_REGS]; -#else - uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS]; - /* Pointer into shadow_regs for the current register set. */ - uint32_t *regs; -#endif - uint32_t ctrl[NUM_CR_REGS]; - uint32_t pc; - -#if !defined(CONFIG_USER_ONLY) - Nios2MMU mmu; -#endif - int error_code; -}; - -typedef struct { - uint32_t writable; - uint32_t readonly; -} ControlRegState; - -/** - * Nios2CPU: - * @env: #CPUNios2State - * - * A Nios2 CPU. - */ -struct ArchCPU { - CPUState parent_obj; - - CPUNios2State env; - - bool diverr_present; - bool mmu_present; - bool eic_present; - - uint32_t pid_num_bits; - uint32_t tlb_num_ways; - uint32_t tlb_num_entries; - - /* Addresses that are hard-coded in the FPGA build settings */ - uint32_t reset_addr; - uint32_t exception_addr; - uint32_t fast_tlb_miss_addr; - - /* Bits within each control register which are reserved or readonly. */ - ControlRegState cr_state[NUM_CR_REGS]; - - /* External Interrupt Controller Interface */ - uint32_t rha; /* Requested handler address */ - uint32_t ril; /* Requested interrupt level */ - uint32_t rrs; /* Requested register set */ - bool rnmi; /* Requested nonmaskable interrupt */ -}; - - -static inline bool nios2_cr_reserved(const ControlRegState *s) -{ - return (s->writable | s->readonly) == 0; -} - -static inline void nios2_update_crs(CPUNios2State *env) -{ -#ifndef CONFIG_USER_ONLY - unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); - env->regs = env->shadow_regs[crs]; -#endif -} - -void nios2_tcg_init(void); -void nios2_cpu_do_interrupt(CPUState *cs); -void dump_mmu(CPUNios2State *env); -void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr); -G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env, - uintptr_t retaddr); - -void do_nios2_semihosting(CPUNios2State *env); - -#define CPU_RESOLVING_TYPE TYPE_NIOS2_CPU - -#define cpu_gen_code cpu_nios2_gen_code - -#define CPU_SAVE_VERSION 1 - -/* MMU modes definitions */ -#define MMU_SUPERVISOR_IDX 0 -#define MMU_USER_IDX 1 - -#ifndef CONFIG_USER_ONLY -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); -#endif - -typedef CPUNios2State CPUArchState; -typedef Nios2CPU ArchCPU; - -#include "exec/cpu-all.h" - -FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */ -FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */ -FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */ - -static inline void cpu_get_tb_cpu_state(CPUNios2State *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); - - *pc = env->pc; - *cs_base = 0; - *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U) - | (crs ? 0 : R_TBFLAGS_CRS0_MASK) - | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK); -} - -#endif /* NIOS2_CPU_H */ diff --git a/target/nios2/helper.c b/target/nios2/helper.c deleted file mode 100644 index ac57121afc..0000000000 --- a/target/nios2/helper.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Altera Nios II helper routines. - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" - -#include "cpu.h" -#include "qemu/host-utils.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/log.h" -#include "exec/helper-proto.h" -#include "semihosting/semihost.h" - - -static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, - uint32_t tlbmisc_set, bool is_break) -{ - CPUNios2State *env = &cpu->env; - CPUState *cs = CPU(cpu); - uint32_t old_status = env->ctrl[CR_STATUS]; - uint32_t new_status = old_status; - - /* With shadow regs, exceptions are always taken into CRS 0. */ - new_status &= ~R_CR_STATUS_CRS_MASK; - env->regs = env->shadow_regs[0]; - - if ((old_status & CR_STATUS_EH) == 0) { - int r_ea = R_EA, cr_es = CR_ESTATUS; - - if (is_break) { - r_ea = R_BA; - cr_es = CR_BSTATUS; - } - env->ctrl[cr_es] = old_status; - env->regs[r_ea] = env->pc; - - if (cpu->mmu_present) { - new_status |= CR_STATUS_EH; - - /* - * There are 4 bits that are always written. - * Explicitly clear them, to be set via the argument. - */ - env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | - CR_TLBMISC_PERM | - CR_TLBMISC_BAD | - CR_TLBMISC_DBL); - env->ctrl[CR_TLBMISC] |= tlbmisc_set; - } - - /* - * With shadow regs, and EH == 0, PRS is set from CRS. - * At least, so says Table 3-9, and some other text, - * though Table 3-38 says otherwise. - */ - new_status = FIELD_DP32(new_status, CR_STATUS, PRS, - FIELD_EX32(old_status, CR_STATUS, CRS)); - } - - new_status &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->ctrl[CR_STATUS] = new_status; - if (!is_break) { - env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, - cs->exception_index); - } - env->pc = exception_addr; -} - -static void do_iic_irq(Nios2CPU *cpu) -{ - do_exception(cpu, cpu->exception_addr, 0, false); -} - -static void do_eic_irq(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - uint32_t old_status = env->ctrl[CR_STATUS]; - uint32_t new_status = old_status; - uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS); - uint32_t new_rs = cpu->rrs; - - new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs); - new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril); - new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi); - new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U); - new_status |= CR_STATUS_IH; - - if (!(new_status & CR_STATUS_EH)) { - new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs); - if (new_rs == 0) { - env->ctrl[CR_ESTATUS] = old_status; - } else { - if (new_rs != old_rs) { - old_status |= CR_STATUS_SRS; - } - env->shadow_regs[new_rs][R_SSTATUS] = old_status; - } - env->shadow_regs[new_rs][R_EA] = env->pc; - } - - env->ctrl[CR_STATUS] = new_status; - nios2_update_crs(env); - - env->pc = cpu->rha; -} - -void nios2_cpu_do_interrupt(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - uint32_t tlbmisc_set = 0; - - if (qemu_loglevel_mask(CPU_LOG_INT)) { - const char *name = NULL; - - switch (cs->exception_index) { - case EXCP_IRQ: - name = "interrupt"; - break; - case EXCP_TLB_X: - case EXCP_TLB_D: - if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { - name = "TLB MISS (double)"; - } else { - name = "TLB MISS (fast)"; - } - break; - case EXCP_PERM_R: - case EXCP_PERM_W: - case EXCP_PERM_X: - name = "TLB PERM"; - break; - case EXCP_SUPERA_X: - case EXCP_SUPERA_D: - name = "SUPERVISOR (address)"; - break; - case EXCP_SUPERI: - name = "SUPERVISOR (insn)"; - break; - case EXCP_ILLEGAL: - name = "ILLEGAL insn"; - break; - case EXCP_UNALIGN: - name = "Misaligned (data)"; - break; - case EXCP_UNALIGND: - name = "Misaligned (destination)"; - break; - case EXCP_DIV: - name = "DIV error"; - break; - case EXCP_TRAP: - name = "TRAP insn"; - break; - case EXCP_BREAK: - name = "BREAK insn"; - break; - case EXCP_SEMIHOST: - name = "SEMIHOST insn"; - break; - } - if (name) { - qemu_log("%s at pc=0x%08x\n", name, env->pc); - } else { - qemu_log("Unknown exception %d at pc=0x%08x\n", - cs->exception_index, env->pc); - } - } - - switch (cs->exception_index) { - case EXCP_IRQ: - /* Note that PC is advanced for interrupts as well. */ - env->pc += 4; - if (cpu->eic_present) { - do_eic_irq(cpu); - } else { - do_iic_irq(cpu); - } - break; - - case EXCP_TLB_D: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_TLB_X: - if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { - tlbmisc_set |= CR_TLBMISC_DBL; - /* - * Normally, we don't write to tlbmisc unless !EH, - * so do it manually for the double-tlb miss exception. - */ - env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | - CR_TLBMISC_PERM | - CR_TLBMISC_BAD); - env->ctrl[CR_TLBMISC] |= tlbmisc_set; - do_exception(cpu, cpu->exception_addr, 0, false); - } else { - tlbmisc_set |= CR_TLBMISC_WE; - do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false); - } - break; - - case EXCP_PERM_R: - case EXCP_PERM_W: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_PERM_X: - tlbmisc_set |= CR_TLBMISC_PERM; - if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) { - tlbmisc_set |= CR_TLBMISC_WE; - } - do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); - break; - - case EXCP_SUPERA_D: - case EXCP_UNALIGN: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_SUPERA_X: - case EXCP_UNALIGND: - tlbmisc_set |= CR_TLBMISC_BAD; - do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); - break; - - case EXCP_SUPERI: - case EXCP_ILLEGAL: - case EXCP_DIV: - case EXCP_TRAP: - do_exception(cpu, cpu->exception_addr, 0, false); - break; - - case EXCP_BREAK: - do_exception(cpu, cpu->exception_addr, 0, true); - break; - - case EXCP_SEMIHOST: - do_nios2_semihosting(env); - break; - - default: - cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); - } -} - -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - target_ulong vaddr, paddr = 0; - Nios2MMULookup lu; - unsigned int hit; - - if (cpu->mmu_present && (addr < 0xC0000000)) { - hit = mmu_translate(env, &lu, addr, 0, 0); - if (hit) { - vaddr = addr & TARGET_PAGE_MASK; - paddr = lu.paddr + vaddr - lu.vaddr; - } else { - paddr = -1; - qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr); - } - } else { - paddr = addr & TARGET_PAGE_MASK; - } - - return paddr; -} - -void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - CPUNios2State *env = cpu_env(cs); - - env->ctrl[CR_BADADDR] = addr; - cs->exception_index = EXCP_UNALIGN; - nios2_cpu_loop_exit_advance(env, retaddr); -} - -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - unsigned int excp; - target_ulong vaddr, paddr; - Nios2MMULookup lu; - unsigned int hit; - - if (!cpu->mmu_present) { - /* No MMU */ - address &= TARGET_PAGE_MASK; - tlb_set_page(cs, address, address, PAGE_BITS, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - if (MMU_SUPERVISOR_IDX == mmu_idx) { - if (address >= 0xC0000000) { - /* Kernel physical page - TLB bypassed */ - address &= TARGET_PAGE_MASK; - tlb_set_page(cs, address, address, PAGE_BITS, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - } else { - if (address >= 0x80000000) { - /* Illegal access from user mode */ - if (probe) { - return false; - } - cs->exception_index = (access_type == MMU_INST_FETCH - ? EXCP_SUPERA_X : EXCP_SUPERA_D); - env->ctrl[CR_BADADDR] = address; - nios2_cpu_loop_exit_advance(env, retaddr); - } - } - - /* Virtual page. */ - hit = mmu_translate(env, &lu, address, access_type, mmu_idx); - if (hit) { - vaddr = address & TARGET_PAGE_MASK; - paddr = lu.paddr + vaddr - lu.vaddr; - - if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) || - ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) || - ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) { - tlb_set_page(cs, vaddr, paddr, lu.prot, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - /* Permission violation */ - excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R : - access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X); - } else { - excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D); - } - - if (probe) { - return false; - } - - env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D, - access_type != MMU_INST_FETCH); - env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN, - address >> TARGET_PAGE_BITS); - env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR]; - - cs->exception_index = excp; - env->ctrl[CR_BADADDR] = address; - nios2_cpu_loop_exit_advance(env, retaddr); -} diff --git a/target/nios2/helper.h b/target/nios2/helper.h deleted file mode 100644 index 1648d76ade..0000000000 --- a/target/nios2/helper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Altera Nios II helper routines header. - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32) -DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) - -#if !defined(CONFIG_USER_ONLY) -DEF_HELPER_3(eret, noreturn, env, i32, i32) -DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32) -DEF_HELPER_3(wrprs, void, env, i32, i32) -DEF_HELPER_2(mmu_write_tlbacc, void, env, i32) -DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32) -DEF_HELPER_2(mmu_write_pteaddr, void, env, i32) -#endif diff --git a/target/nios2/meson.build b/target/nios2/meson.build deleted file mode 100644 index 12d8abf0bd..0000000000 --- a/target/nios2/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -nios2_ss = ss.source_set() -nios2_ss.add(files( - 'cpu.c', - 'op_helper.c', - 'translate.c', -)) - -nios2_system_ss = ss.source_set() -nios2_system_ss.add(files( - 'helper.c', - 'monitor.c', - 'mmu.c', - 'nios2-semi.c', -)) - -target_arch += {'nios2': nios2_ss} -target_system_arch += {'nios2': nios2_system_ss} diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c deleted file mode 100644 index d9b690b78e..0000000000 --- a/target/nios2/mmu.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Altera Nios II MMU emulation for qemu. - * - * Copyright (C) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "qemu/qemu-print.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "mmu.h" -#include "exec/helper-proto.h" -#include "trace/trace-target_nios2.h" - - -/* rw - 0 = read, 1 = write, 2 = fetch. */ -unsigned int mmu_translate(CPUNios2State *env, - Nios2MMULookup *lu, - target_ulong vaddr, int rw, int mmu_idx) -{ - Nios2CPU *cpu = env_archcpu(env); - int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - int vpn = vaddr >> 12; - int way, n_ways = cpu->tlb_num_ways; - - for (way = 0; way < n_ways; way++) { - uint32_t index = (way * n_ways) + (vpn & env->mmu.tlb_entry_mask); - Nios2TLBEntry *entry = &env->mmu.tlb[index]; - - if (((entry->tag >> 12) != vpn) || - (((entry->tag & (1 << 11)) == 0) && - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) != pid))) { - trace_nios2_mmu_translate_miss(vaddr, pid, index, entry->tag); - continue; - } - - lu->vaddr = vaddr & TARGET_PAGE_MASK; - lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS; - lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) | - ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) | - ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0); - - trace_nios2_mmu_translate_hit(vaddr, pid, index, lu->paddr, lu->prot); - return 1; - } - return 0; -} - -static void mmu_flush_pid(CPUNios2State *env, uint32_t pid) -{ - CPUState *cs = env_cpu(env); - Nios2CPU *cpu = env_archcpu(env); - int idx; - - for (idx = 0; idx < cpu->tlb_num_entries; idx++) { - Nios2TLBEntry *entry = &env->mmu.tlb[idx]; - - if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) && - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) == pid)) { - uint32_t vaddr = entry->tag & TARGET_PAGE_MASK; - - trace_nios2_mmu_flush_pid_hit(pid, idx, vaddr); - tlb_flush_page(cs, vaddr); - } else { - trace_nios2_mmu_flush_pid_miss(pid, idx, entry->tag); - } - } -} - -void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) -{ - CPUState *cs = env_cpu(env); - Nios2CPU *cpu = env_archcpu(env); - - trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG), - (v & CR_TLBACC_C) ? 'C' : '.', - (v & CR_TLBACC_R) ? 'R' : '.', - (v & CR_TLBACC_W) ? 'W' : '.', - (v & CR_TLBACC_X) ? 'X' : '.', - (v & CR_TLBACC_G) ? 'G' : '.', - FIELD_EX32(v, CR_TLBACC, PFN)); - - /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ - if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) { - int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY); - int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); - int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - int g = FIELD_EX32(v, CR_TLBACC, G); - int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000; - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid; - uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W | - CR_TLBACC_X | R_CR_TLBACC_PFN_MASK); - - if ((entry->tag != newTag) || (entry->data != newData)) { - if (entry->tag & (1 << 10)) { - /* Flush existing entry */ - tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK); - } - entry->tag = newTag; - entry->data = newData; - } - /* Auto-increment tlbmisc.WAY */ - env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], - CR_TLBMISC, WAY, - (way + 1) & (cpu->tlb_num_ways - 1)); - } - - /* Writes to TLBACC don't change the read-back value */ - env->mmu.tlbacc_wr = v; -} - -void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) -{ - Nios2CPU *cpu = env_archcpu(env); - uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID); - uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY); - - trace_nios2_mmu_write_tlbmisc(way, - (v & CR_TLBMISC_RD) ? 'R' : '.', - (v & CR_TLBMISC_WE) ? 'W' : '.', - (v & CR_TLBMISC_DBL) ? '2' : '.', - (v & CR_TLBMISC_BAD) ? 'B' : '.', - (v & CR_TLBMISC_PERM) ? 'P' : '.', - (v & CR_TLBMISC_D) ? 'D' : '.', - new_pid); - - if (new_pid != old_pid) { - mmu_flush_pid(env, old_pid); - } - - /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ - if (v & CR_TLBMISC_RD) { - int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - - env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK; - env->ctrl[CR_TLBACC] |= entry->data; - env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; - env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID, - entry->tag & - ((1 << cpu->pid_num_bits) - 1)); - env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], - CR_PTEADDR, VPN, - entry->tag >> TARGET_PAGE_BITS); - } else { - env->ctrl[CR_TLBMISC] = v; - } - - env->mmu.tlbmisc_wr = v; -} - -void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v) -{ - trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE), - FIELD_EX32(v, CR_PTEADDR, VPN)); - - /* Writes to PTEADDR don't change the read-back VPN value */ - env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) | - (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK)); - env->mmu.pteaddr_wr = v; -} - -void mmu_init(CPUNios2State *env) -{ - Nios2CPU *cpu = env_archcpu(env); - Nios2MMU *mmu = &env->mmu; - - mmu->tlb_entry_mask = (cpu->tlb_num_entries / cpu->tlb_num_ways) - 1; - mmu->tlb = g_new0(Nios2TLBEntry, cpu->tlb_num_entries); -} - -void dump_mmu(CPUNios2State *env) -{ - Nios2CPU *cpu = env_archcpu(env); - int i; - - qemu_printf("MMU: ways %d, entries %d, pid bits %d\n", - cpu->tlb_num_ways, cpu->tlb_num_entries, - cpu->pid_num_bits); - - for (i = 0; i < cpu->tlb_num_entries; i++) { - Nios2TLBEntry *entry = &env->mmu.tlb[i]; - qemu_printf("TLB[%d] = %08X %08X %c VPN %05X " - "PID %02X %c PFN %05X %c%c%c%c\n", - i, entry->tag, entry->data, - (entry->tag & (1 << 10)) ? 'V' : '-', - entry->tag >> 12, - entry->tag & ((1 << cpu->pid_num_bits) - 1), - (entry->tag & (1 << 11)) ? 'G' : '-', - FIELD_EX32(entry->data, CR_TLBACC, PFN), - (entry->data & CR_TLBACC_C) ? 'C' : '-', - (entry->data & CR_TLBACC_R) ? 'R' : '-', - (entry->data & CR_TLBACC_W) ? 'W' : '-', - (entry->data & CR_TLBACC_X) ? 'X' : '-'); - } -} diff --git a/target/nios2/mmu.h b/target/nios2/mmu.h deleted file mode 100644 index 5b085900fb..0000000000 --- a/target/nios2/mmu.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Altera Nios II MMU emulation for qemu. - * - * Copyright (C) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#ifndef NIOS2_MMU_H -#define NIOS2_MMU_H - -#include "cpu.h" - -typedef struct Nios2TLBEntry { - target_ulong tag; - target_ulong data; -} Nios2TLBEntry; - -typedef struct Nios2MMU { - int tlb_entry_mask; - uint32_t pteaddr_wr; - uint32_t tlbacc_wr; - uint32_t tlbmisc_wr; - Nios2TLBEntry *tlb; -} Nios2MMU; - -typedef struct Nios2MMULookup { - target_ulong vaddr; - target_ulong paddr; - int prot; -} Nios2MMULookup; - -void mmu_flip_um(CPUNios2State *env, unsigned int um); -unsigned int mmu_translate(CPUNios2State *env, - Nios2MMULookup *lu, - target_ulong vaddr, int rw, int mmu_idx); -void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v); -void mmu_init(CPUNios2State *env); - -#endif /* NIOS2_MMU_H */ diff --git a/target/nios2/monitor.c b/target/nios2/monitor.c deleted file mode 100644 index 0152dec3fa..0000000000 --- a/target/nios2/monitor.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * QEMU monitor - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "cpu.h" -#include "monitor/monitor.h" -#include "monitor/hmp-target.h" -#include "monitor/hmp.h" - -void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env1 = mon_get_cpu_env(mon); - - dump_mmu(env1); -} diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c deleted file mode 100644 index 420702e293..0000000000 --- a/target/nios2/nios2-semi.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Nios II Semihosting syscall interface. - * This code is derived from m68k-semi.c. - * The semihosting protocol implemented here is described in the - * libgloss sources: - * https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD - * - * Copyright (c) 2017-2019 Mentor Graphics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "gdbstub/syscalls.h" -#include "gdbstub/helpers.h" -#include "semihosting/syscalls.h" -#include "semihosting/uaccess.h" -#include "qemu/log.h" - -#define HOSTED_EXIT 0 -#define HOSTED_INIT_SIM 1 -#define HOSTED_OPEN 2 -#define HOSTED_CLOSE 3 -#define HOSTED_READ 4 -#define HOSTED_WRITE 5 -#define HOSTED_LSEEK 6 -#define HOSTED_RENAME 7 -#define HOSTED_UNLINK 8 -#define HOSTED_STAT 9 -#define HOSTED_FSTAT 10 -#define HOSTED_GETTIMEOFDAY 11 -#define HOSTED_ISATTY 12 -#define HOSTED_SYSTEM 13 - -static int host_to_gdb_errno(int err) -{ -#define E(X) case E##X: return GDB_E##X - switch (err) { - E(PERM); - E(NOENT); - E(INTR); - E(BADF); - E(ACCES); - E(FAULT); - E(BUSY); - E(EXIST); - E(NODEV); - E(NOTDIR); - E(ISDIR); - E(INVAL); - E(NFILE); - E(MFILE); - E(FBIG); - E(NOSPC); - E(SPIPE); - E(ROFS); - E(NAMETOOLONG); - default: - return GDB_EUNKNOWN; - } -#undef E -} - -static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) -{ - CPUNios2State *env = cpu_env(cs); - target_ulong args = env->regs[R_ARG1]; - - if (put_user_u32(ret, args) || - put_user_u32(host_to_gdb_errno(err), args + 4)) { - /* - * The nios2 semihosting ABI does not provide any way to report this - * error to the guest, so the best we can do is log it in qemu. - * It is always a guest error not to pass us a valid argument block. - */ - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " - "discarded because argument block not writable\n"); - } -} - -static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) -{ - CPUNios2State *env = cpu_env(cs); - target_ulong args = env->regs[R_ARG1]; - - if (put_user_u32(ret >> 32, args) || - put_user_u32(ret, args + 4) || - put_user_u32(host_to_gdb_errno(err), args + 8)) { - /* No way to report this via nios2 semihosting ABI; just log it */ - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " - "discarded because argument block not writable\n"); - } -} - -/* - * Read the input value from the argument block; fail the semihosting - * call if the memory read fails. - */ -#define GET_ARG(n) do { \ - if (get_user_ual(arg ## n, args + (n) * 4)) { \ - goto failed; \ - } \ -} while (0) - -#define GET_ARG64(n) do { \ - if (get_user_ual(arg ## n, args + (n) * 4)) { \ - goto failed64; \ - } \ -} while (0) - -void do_nios2_semihosting(CPUNios2State *env) -{ - CPUState *cs = env_cpu(env); - int nr; - uint32_t args; - target_ulong arg0, arg1, arg2, arg3; - - nr = env->regs[R_ARG0]; - args = env->regs[R_ARG1]; - switch (nr) { - case HOSTED_EXIT: - gdb_exit(env->regs[R_ARG1]); - exit(env->regs[R_ARG1]); - - case HOSTED_OPEN: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - semihost_sys_open(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); - break; - - case HOSTED_CLOSE: - GET_ARG(0); - semihost_sys_close(cs, nios2_semi_u32_cb, arg0); - break; - - case HOSTED_READ: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_read(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_WRITE: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_write(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_LSEEK: - GET_ARG64(0); - GET_ARG64(1); - GET_ARG64(2); - GET_ARG64(3); - semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0, - deposit64(arg2, 32, 32, arg1), arg3); - break; - - case HOSTED_RENAME: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - semihost_sys_rename(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); - break; - - case HOSTED_UNLINK: - GET_ARG(0); - GET_ARG(1); - semihost_sys_remove(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_STAT: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_stat(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_FSTAT: - GET_ARG(0); - GET_ARG(1); - semihost_sys_fstat(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_GETTIMEOFDAY: - GET_ARG(0); - GET_ARG(1); - semihost_sys_gettimeofday(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_ISATTY: - GET_ARG(0); - semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0); - break; - - case HOSTED_SYSTEM: - GET_ARG(0); - GET_ARG(1); - semihost_sys_system(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported " - "semihosting syscall %d\n", nr); - nios2_semi_u32_cb(cs, -1, ENOSYS); - break; - - failed: - nios2_semi_u32_cb(cs, -1, EFAULT); - break; - failed64: - nios2_semi_u64_cb(cs, -1, EFAULT); - break; - } -} diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c deleted file mode 100644 index 5017457c5e..0000000000 --- a/target/nios2/op_helper.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Altera Nios II helper routines. - * - * Copyright (C) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" - -void helper_raise_exception(CPUNios2State *env, uint32_t index) -{ - CPUState *cs = env_cpu(env); - cs->exception_index = index; - cpu_loop_exit(cs); -} - -void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr) -{ - CPUState *cs = env_cpu(env); - - /* - * Note that PC is advanced for all hardware exceptions. - * Do this here, rather than in restore_state_to_opc(), - * lest we affect QEMU internal exceptions, like EXCP_DEBUG. - */ - cpu_restore_state(cs, retaddr); - env->pc += 4; - cpu_loop_exit(cs); -} - -static void maybe_raise_div(CPUNios2State *env, uintptr_t ra) -{ - Nios2CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (cpu->diverr_present) { - cs->exception_index = EXCP_DIV; - nios2_cpu_loop_exit_advance(env, ra); - } -} - -int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den) -{ - if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) { - maybe_raise_div(env, GETPC()); - return num; /* undefined */ - } - return num / den; -} - -uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den) -{ - if (unlikely(den == 0)) { - maybe_raise_div(env, GETPC()); - return num; /* undefined */ - } - return num / den; -} - -#ifndef CONFIG_USER_ONLY -void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc) -{ - Nios2CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (unlikely(new_pc & 3)) { - env->ctrl[CR_BADADDR] = new_pc; - cs->exception_index = EXCP_UNALIGND; - nios2_cpu_loop_exit_advance(env, GETPC()); - } - - /* - * None of estatus, bstatus, or sstatus have constraints on write; - * do not allow reserved fields in status to be set. - * When shadow registers are enabled, eret *does* restore CRS. - * Rather than testing eic_present to decide, mask CRS out of - * the set of readonly fields. - */ - new_status &= cpu->cr_state[CR_STATUS].writable | - (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK); - - env->ctrl[CR_STATUS] = new_status; - env->pc = new_pc; - nios2_update_crs(env); - cpu_loop_exit(cs); -} - -/* - * RDPRS and WRPRS are implemented out of line so that if PRS == CRS, - * all of the tcg global temporaries are synced back to ENV. - */ -uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno) -{ - unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); - return env->shadow_regs[prs][regno]; -} - -void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val) -{ - unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); - env->shadow_regs[prs][regno] = val; -} -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/trace-events b/target/nios2/trace-events deleted file mode 100644 index 07f1f0a5e7..0000000000 --- a/target/nios2/trace-events +++ /dev/null @@ -1,10 +0,0 @@ -# mmu.c -nios2_mmu_translate_miss(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t tag) "mmu_translate: MISS vaddr=0x%08x pid=%u TLB[%u] tag=0x%08x" -nios2_mmu_translate_hit(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t paddr, uint32_t prot) "mmu_translate: HIT vaddr=0x%08x pid=%u TLB[%u] paddr=0x%08x prot=0x%x" - -nios2_mmu_flush_pid_miss(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: MISS pid=%u TLB[%u] tag=0x%08x" -nios2_mmu_flush_pid_hit(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: HIT pid=%u TLB[%u] vaddr=0x%08x" - -nios2_mmu_write_tlbacc(uint32_t ig, char c, char r, char w, char x, char g, uint32_t pfn) "mmu_write_tlbacc: ig=0x%02x flags=%c%c%c%c%c pfn=0x%08x" -nios2_mmu_write_tlbmisc(uint32_t way, char r, char w, char t, char b, char p, char d, uint32_t pid) "mmu_write_tlbmisc: way=0x%x flags=%c%c%c%c%c%c pid=%u" -nios2_mmu_write_pteaddr(uint32_t ptb, uint32_t vpn) "mmu_write_pteaddr: ptbase=0x%03x vpn=0x%05x" diff --git a/target/nios2/translate.c b/target/nios2/translate.c deleted file mode 100644 index 7ddc6ac1a2..0000000000 --- a/target/nios2/translate.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * Altera Nios II emulation for qemu: main translation routines. - * - * Copyright (C) 2016 Marek Vasut - * Copyright (C) 2012 Chris Wulff - * Copyright (C) 2010 Tobias Klauser - * (Portions of this file that were originally from nios2sim-ng.) - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "tcg/tcg-op.h" -#include "exec/exec-all.h" -#include "disas/disas.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" -#include "exec/log.h" -#include "exec/translator.h" -#include "qemu/qemu-print.h" -#include "semihosting/semihost.h" - -#define HELPER_H "helper.h" -#include "exec/helper-info.c.inc" -#undef HELPER_H - - -/* is_jmp field values */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ - -#define INSTRUCTION_FLG(func, flags) { (func), (flags) } -#define INSTRUCTION(func) \ - INSTRUCTION_FLG(func, 0) -#define INSTRUCTION_NOP() \ - INSTRUCTION_FLG(nop, 0) -#define INSTRUCTION_UNIMPLEMENTED() \ - INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL) -#define INSTRUCTION_ILLEGAL() \ - INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL) - -/* Special R-Type instruction opcode */ -#define INSN_R_TYPE 0x3A - -/* I-Type instruction parsing */ -typedef struct { - uint8_t op; - union { - uint16_t u; - int16_t s; - } imm16; - uint8_t b; - uint8_t a; -} InstrIType; - -#define I_TYPE(instr, code) \ - InstrIType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm16.u = extract32((code), 6, 16), \ - .b = extract32((code), 22, 5), \ - .a = extract32((code), 27, 5), \ - } - -typedef target_ulong ImmFromIType(const InstrIType *); - -static target_ulong imm_unsigned(const InstrIType *i) -{ - return i->imm16.u; -} - -static target_ulong imm_signed(const InstrIType *i) -{ - return i->imm16.s; -} - -static target_ulong imm_shifted(const InstrIType *i) -{ - return i->imm16.u << 16; -} - -/* R-Type instruction parsing */ -typedef struct { - uint8_t op; - uint8_t imm5; - uint8_t opx; - uint8_t c; - uint8_t b; - uint8_t a; -} InstrRType; - -#define R_TYPE(instr, code) \ - InstrRType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm5 = extract32((code), 6, 5), \ - .opx = extract32((code), 11, 6), \ - .c = extract32((code), 17, 5), \ - .b = extract32((code), 22, 5), \ - .a = extract32((code), 27, 5), \ - } - -/* J-Type instruction parsing */ -typedef struct { - uint8_t op; - uint32_t imm26; -} InstrJType; - -#define J_TYPE(instr, code) \ - InstrJType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm26 = extract32((code), 6, 26), \ - } - -typedef void GenFn2i(TCGv, TCGv, target_long); -typedef void GenFn3(TCGv, TCGv, TCGv); -typedef void GenFn4(TCGv, TCGv, TCGv, TCGv); - -typedef struct DisasContext { - DisasContextBase base; - target_ulong pc; - int mem_idx; - uint32_t tb_flags; - TCGv sink; - const ControlRegState *cr_state; - bool eic_present; -} DisasContext; - -static TCGv cpu_R[NUM_GP_REGS]; -static TCGv cpu_pc; -#ifndef CONFIG_USER_ONLY -static TCGv cpu_crs_R[NUM_GP_REGS]; -#endif - -typedef struct Nios2Instruction { - void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); - uint32_t flags; -} Nios2Instruction; - -static uint8_t get_opcode(uint32_t code) -{ - I_TYPE(instr, code); - return instr.op; -} - -static uint8_t get_opxcode(uint32_t code) -{ - R_TYPE(instr, code); - return instr.opx; -} - -static TCGv load_gpr(DisasContext *dc, unsigned reg) -{ - assert(reg < NUM_GP_REGS); - - /* - * With shadow register sets, register r0 does not necessarily contain 0, - * but it is overwhelmingly likely that it does -- software is supposed - * to have set r0 to 0 in every shadow register set before use. - */ - if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { - return tcg_constant_tl(0); - } - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - return cpu_R[reg]; - } -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - return cpu_crs_R[reg]; -#endif -} - -static TCGv dest_gpr(DisasContext *dc, unsigned reg) -{ - assert(reg < NUM_GP_REGS); - - /* - * The spec for shadow register sets isn't clear, but we assume that - * writes to r0 are discarded regardless of CRS. - */ - if (unlikely(reg == R_ZERO)) { - if (dc->sink == NULL) { - dc->sink = tcg_temp_new(); - } - return dc->sink; - } - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - return cpu_R[reg]; - } -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - return cpu_crs_R[reg]; -#endif -} - -static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) -{ - /* Note that PC is advanced for all hardware exceptions. */ - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_helper_raise_exception(tcg_env, tcg_constant_i32(index)); - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) -{ - const TranslationBlock *tb = dc->base.tb; - - if (translator_use_goto_tb(&dc->base, dest)) { - tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb(tb, n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_jumpr(DisasContext *dc, int regno, bool is_call) -{ - TCGLabel *l = gen_new_label(); - TCGv test = tcg_temp_new(); - TCGv dest = load_gpr(dc, regno); - - tcg_gen_andi_tl(test, dest, 3); - tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l); - - tcg_gen_mov_tl(cpu_pc, dest); - if (is_call) { - tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); - } - tcg_gen_lookup_and_goto_ptr(); - - gen_set_label(l); - tcg_gen_st_tl(dest, tcg_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); - t_gen_helper_raise_exception(dc, EXCP_UNALIGND); - - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) -{ - t_gen_helper_raise_exception(dc, flags); -} - -static bool gen_check_supervisor(DisasContext *dc) -{ - if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) { - /* CPU in user mode, privileged instruction called, stop. */ - t_gen_helper_raise_exception(dc, EXCP_SUPERI); - return false; - } - return true; -} - -/* - * Used as a placeholder for all instructions which do not have - * an effect on the simulator (e.g. flush, sync) - */ -static void nop(DisasContext *dc, uint32_t code, uint32_t flags) -{ - /* Nothing to do here */ -} - -/* - * J-Type instructions - */ -static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) -{ - J_TYPE(instr, code); - gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); -} - -static void call(DisasContext *dc, uint32_t code, uint32_t flags) -{ - tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); - jmpi(dc, code, flags); -} - -/* - * I-Type instructions - */ -/* Load instructions */ -static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - TCGv addr = tcg_temp_new(); - TCGv data = dest_gpr(dc, instr.b); - - tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); -#ifdef CONFIG_USER_ONLY - flags |= MO_UNALN; -#else - flags |= MO_ALIGN; -#endif - tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); -} - -/* Store instructions */ -static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - TCGv val = load_gpr(dc, instr.b); - - TCGv addr = tcg_temp_new(); - tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); -#ifdef CONFIG_USER_ONLY - flags |= MO_UNALN; -#else - flags |= MO_ALIGN; -#endif - tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); -} - -/* Branch instructions */ -static void br(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4)); -} - -static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - TCGLabel *l1 = gen_new_label(); - tcg_gen_brcond_tl(flags, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1); - gen_goto_tb(dc, 0, dc->base.pc_next); - gen_set_label(l1); - gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4)); -} - -/* Comparison instructions */ -static void do_i_cmpxx(DisasContext *dc, uint32_t insn, - TCGCond cond, ImmFromIType *imm) -{ - I_TYPE(instr, insn); - tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b), - load_gpr(dc, instr.a), imm(&instr)); -} - -#define gen_i_cmpxx(fname, imm) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_i_cmpxx(dc, code, flags, imm); } - -gen_i_cmpxx(gen_cmpxxsi, imm_signed) -gen_i_cmpxx(gen_cmpxxui, imm_unsigned) - -/* Math/logic instructions */ -static void do_i_math_logic(DisasContext *dc, uint32_t insn, - GenFn2i *fn, ImmFromIType *imm, - bool x_op_0_eq_x) -{ - I_TYPE(instr, insn); - target_ulong val; - - if (unlikely(instr.b == R_ZERO)) { - /* Store to R_ZERO is ignored -- this catches the canonical NOP. */ - return; - } - - val = imm(&instr); - - if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { - /* This catches the canonical expansions of movi and movhi. */ - tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0); - } else { - fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val); - } -} - -#define gen_i_math_logic(fname, insn, x_op_0, imm) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); } - -gen_i_math_logic(addi, addi, 1, imm_signed) -gen_i_math_logic(muli, muli, 0, imm_signed) - -gen_i_math_logic(andi, andi, 0, imm_unsigned) -gen_i_math_logic(ori, ori, 1, imm_unsigned) -gen_i_math_logic(xori, xori, 1, imm_unsigned) - -gen_i_math_logic(andhi, andi, 0, imm_shifted) -gen_i_math_logic(orhi , ori, 1, imm_shifted) -gen_i_math_logic(xorhi, xori, 1, imm_shifted) - -/* rB <- prs.rA + sigma(IMM16) */ -static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!dc->eic_present) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - I_TYPE(instr, code); - TCGv dest = dest_gpr(dc, instr.b); - gen_helper_rdprs(dest, tcg_env, tcg_constant_i32(instr.a)); - tcg_gen_addi_tl(dest, dest, instr.imm16.s); -#endif -} - -/* Prototype only, defined below */ -static void handle_r_type_instr(DisasContext *dc, uint32_t code, - uint32_t flags); - -static const Nios2Instruction i_type_instructions[] = { - INSTRUCTION(call), /* call */ - INSTRUCTION(jmpi), /* jmpi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbu */ - INSTRUCTION(addi), /* addi */ - INSTRUCTION_FLG(gen_stx, MO_UB), /* stb */ - INSTRUCTION(br), /* br */ - INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldb */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_TEUW), /* ldhu */ - INSTRUCTION(andi), /* andi */ - INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sth */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ - INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldh */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_NOP(), /* initda */ - INSTRUCTION(ori), /* ori */ - INSTRUCTION_FLG(gen_stx, MO_TEUL), /* stw */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ - INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* ldw */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_NOP(), /* flushda */ - INSTRUCTION(xori), /* xori */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_bxx, TCG_COND_NE), /* bne */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ), /* cmpeqi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbuio */ - INSTRUCTION(muli), /* muli */ - INSTRUCTION_FLG(gen_stx, MO_UB), /* stbio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ), /* beq */ - INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldbio */ - INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_TEUW), /* ldhuio */ - INSTRUCTION(andhi), /* andhi */ - INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sthio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ - INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldhio */ - INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_UNIMPLEMENTED(), /* custom */ - INSTRUCTION_NOP(), /* initd */ - INSTRUCTION(orhi), /* orhi */ - INSTRUCTION_FLG(gen_stx, MO_TESL), /* stwio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ - INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* ldwio */ - INSTRUCTION(rdprs), /* rdprs */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ - INSTRUCTION_NOP(), /* flushd */ - INSTRUCTION(xorhi), /* xorhi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), -}; - -/* - * R-Type instructions - */ -/* - * status <- estatus - * PC <- ea - */ -static void eret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); - gen_helper_eret(tcg_env, tmp, load_gpr(dc, R_EA)); - } else { - gen_helper_eret(tcg_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); - } - dc->base.is_jmp = DISAS_NORETURN; -#endif -} - -/* PC <- ra */ -static void ret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - gen_jumpr(dc, R_RA, false); -} - -/* - * status <- bstatus - * PC <- ba - */ -static void bret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); - gen_helper_eret(tcg_env, tmp, load_gpr(dc, R_BA)); - - dc->base.is_jmp = DISAS_NORETURN; -#endif -} - -/* PC <- rA */ -static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - gen_jumpr(dc, instr.a, false); -} - -/* rC <- PC + 4 */ -static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next); -} - -/* - * ra <- PC + 4 - * PC <- rA - */ -static void callr(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - gen_jumpr(dc, instr.a, true); -} - -/* rC <- ctlN */ -static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - TCGv t1, t2, dest = dest_gpr(dc, instr.c); - - /* Reserved registers read as zero. */ - if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) { - tcg_gen_movi_tl(dest, 0); - return; - } - - switch (instr.imm5) { - case CR_IPENDING: - /* - * The value of the ipending register is synthetic. - * In hw, this is the AND of a set of hardware irq lines - * with the ienable register. In qemu, we re-use the space - * of CR_IPENDING to store the set of irq lines, and so we - * must perform the AND here, and anywhere else we need the - * guest value of ipending. - */ - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - tcg_gen_ld_tl(t1, tcg_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); - tcg_gen_ld_tl(t2, tcg_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); - tcg_gen_and_tl(dest, t1, t2); - break; - default: - tcg_gen_ld_tl(dest, tcg_env, - offsetof(CPUNios2State, ctrl[instr.imm5])); - break; - } -#endif -} - -/* ctlN <- rA */ -static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - TCGv v = load_gpr(dc, instr.a); - uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]); - uint32_t wr = dc->cr_state[instr.imm5].writable; - uint32_t ro = dc->cr_state[instr.imm5].readonly; - - /* Skip reserved or readonly registers. */ - if (wr == 0) { - return; - } - - switch (instr.imm5) { - case CR_PTEADDR: - gen_helper_mmu_write_pteaddr(tcg_env, v); - break; - case CR_TLBACC: - gen_helper_mmu_write_tlbacc(tcg_env, v); - break; - case CR_TLBMISC: - gen_helper_mmu_write_tlbmisc(tcg_env, v); - break; - case CR_STATUS: - case CR_IENABLE: - /* If interrupts were enabled using WRCTL, trigger them. */ - dc->base.is_jmp = DISAS_UPDATE; - /* fall through */ - default: - if (wr == -1) { - /* The register is entirely writable. */ - tcg_gen_st_tl(v, tcg_env, ofs); - } else { - /* - * The register is partially read-only or reserved: - * merge the value. - */ - TCGv n = tcg_temp_new(); - - tcg_gen_andi_tl(n, v, wr); - - if (ro != 0) { - TCGv o = tcg_temp_new(); - tcg_gen_ld_tl(o, tcg_env, ofs); - tcg_gen_andi_tl(o, o, ro); - tcg_gen_or_tl(n, n, o); - } - - tcg_gen_st_tl(n, tcg_env, ofs); - } - break; - } -#endif -} - -/* prs.rC <- rA */ -static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!dc->eic_present) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - gen_helper_wrprs(tcg_env, tcg_constant_i32(instr.c), - load_gpr(dc, instr.a)); - /* - * The expected write to PRS[r0] is 0, from CRS[r0]. - * If not, and CRS == PRS (which we cannot tell from here), - * we may now have a non-zero value in our current r0. - * By ending the TB, we re-evaluate tb_flags and find out. - */ - if (instr.c == 0 - && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) { - dc->base.is_jmp = DISAS_UPDATE; - } -#endif -} - -/* Comparison instructions */ -static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c), - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -/* Math/logic instructions */ -static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn) -{ - R_TYPE(instr, insn); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5); -} - -static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn) -{ - R_TYPE(instr, insn); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -#define gen_ri_math_logic(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); } - -#define gen_rr_math_logic(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_math_logic(add, add) -gen_rr_math_logic(sub, sub) -gen_rr_math_logic(mul, mul) - -gen_rr_math_logic(and, and) -gen_rr_math_logic(or, or) -gen_rr_math_logic(xor, xor) -gen_rr_math_logic(nor, nor) - -gen_ri_math_logic(srai, sari) -gen_ri_math_logic(srli, shri) -gen_ri_math_logic(slli, shli) -gen_ri_math_logic(roli, rotli) - -static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn) -{ - R_TYPE(instr, insn); - TCGv discard = tcg_temp_new(); - - fn(discard, dest_gpr(dc, instr.c), - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -#define gen_rr_mul_high(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_mul_high(mulxss, muls2) -gen_rr_mul_high(mulxuu, mulu2) -gen_rr_mul_high(mulxsu, mulsu2) - -static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn) -{ - R_TYPE(instr, insn); - TCGv sh = tcg_temp_new(); - - tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh); -} - -#define gen_rr_shift(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_shift(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_shift(sra, sar) -gen_rr_shift(srl, shr) -gen_rr_shift(sll, shl) -gen_rr_shift(rol, rotl) -gen_rr_shift(ror, rotr) - -static void divs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, (code)); - gen_helper_divs(dest_gpr(dc, instr.c), tcg_env, - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -static void divu(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, (code)); - gen_helper_divu(dest_gpr(dc, instr.c), tcg_env, - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -static void trap(DisasContext *dc, uint32_t code, uint32_t flags) -{ -#ifdef CONFIG_USER_ONLY - /* - * The imm5 field is not stored anywhere on real hw; the kernel - * has to load the insn and extract the field. But we can make - * things easier for cpu_loop if we pop this into env->error_code. - */ - R_TYPE(instr, code); - tcg_gen_st_i32(tcg_constant_i32(instr.imm5), tcg_env, - offsetof(CPUNios2State, error_code)); -#endif - t_gen_helper_raise_exception(dc, EXCP_TRAP); -} - -static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) -{ -#ifndef CONFIG_USER_ONLY - /* The semihosting instruction is "break 1". */ - bool is_user = FIELD_EX32(dc->tb_flags, TBFLAGS, U); - R_TYPE(instr, code); - if (semihosting_enabled(is_user) && instr.imm5 == 1) { - t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); - return; - } -#endif - - t_gen_helper_raise_exception(dc, EXCP_BREAK); -} - -static const Nios2Instruction r_type_instructions[] = { - INSTRUCTION_ILLEGAL(), - INSTRUCTION(eret), /* eret */ - INSTRUCTION(roli), /* roli */ - INSTRUCTION(rol), /* rol */ - INSTRUCTION_NOP(), /* flushp */ - INSTRUCTION(ret), /* ret */ - INSTRUCTION(nor), /* nor */ - INSTRUCTION(mulxuu), /* mulxuu */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE), /* cmpge */ - INSTRUCTION(bret), /* bret */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(ror), /* ror */ - INSTRUCTION_NOP(), /* flushi */ - INSTRUCTION(jmp), /* jmp */ - INSTRUCTION(and), /* and */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT), /* cmplt */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(slli), /* slli */ - INSTRUCTION(sll), /* sll */ - INSTRUCTION(wrprs), /* wrprs */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(or), /* or */ - INSTRUCTION(mulxsu), /* mulxsu */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE), /* cmpne */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(srli), /* srli */ - INSTRUCTION(srl), /* srl */ - INSTRUCTION(nextpc), /* nextpc */ - INSTRUCTION(callr), /* callr */ - INSTRUCTION(xor), /* xor */ - INSTRUCTION(mulxss), /* mulxss */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ), /* cmpeq */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(divu), /* divu */ - INSTRUCTION(divs), /* div */ - INSTRUCTION(rdctl), /* rdctl */ - INSTRUCTION(mul), /* mul */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU), /* cmpgeu */ - INSTRUCTION_NOP(), /* initi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(trap), /* trap */ - INSTRUCTION(wrctl), /* wrctl */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ - INSTRUCTION(add), /* add */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(gen_break), /* break */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(nop), /* nop */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(sub), /* sub */ - INSTRUCTION(srai), /* srai */ - INSTRUCTION(sra), /* sra */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), -}; - -static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags) -{ - uint8_t opx; - const Nios2Instruction *instr; - - opx = get_opxcode(code); - if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) { - goto illegal_op; - } - - instr = &r_type_instructions[opx]; - instr->handler(dc, code, instr->flags); - - return; - -illegal_op: - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); -} - -static const char * const gr_regnames[NUM_GP_REGS] = { - "zero", "at", "r2", "r3", - "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", - "r12", "r13", "r14", "r15", - "r16", "r17", "r18", "r19", - "r20", "r21", "r22", "r23", - "et", "bt", "gp", "sp", - "fp", "ea", "ba", "ra", -}; - -#ifndef CONFIG_USER_ONLY -static const char * const cr_regnames[NUM_CR_REGS] = { - "status", "estatus", "bstatus", "ienable", - "ipending", "cpuid", "res6", "exception", - "pteaddr", "tlbacc", "tlbmisc", "reserved1", - "badaddr", "config", "mpubase", "mpuacc", - "res16", "res17", "res18", "res19", - "res20", "res21", "res22", "res23", - "res24", "res25", "res26", "res27", - "res28", "res29", "res30", "res31", -}; -#endif - -/* generate intermediate code for basic block 'tb'. */ -static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUNios2State *env = cpu_env(cs); - Nios2CPU *cpu = env_archcpu(env); - int page_insns; - - dc->mem_idx = cpu_mmu_index(cs, false); - dc->cr_state = cpu->cr_state; - dc->tb_flags = dc->base.tb->flags; - dc->eic_present = cpu->eic_present; - - /* Bound the number of insns to execute to those left on the page. */ - page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - dc->base.max_insns = MIN(page_insns, dc->base.max_insns); -} - -static void nios2_tr_tb_start(DisasContextBase *db, CPUState *cs) -{ -} - -static void nios2_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) -{ - tcg_gen_insn_start(dcbase->pc_next); -} - -static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - const Nios2Instruction *instr; - uint32_t code, pc; - uint8_t op; - - pc = dc->base.pc_next; - dc->pc = pc; - dc->base.pc_next = pc + 4; - - /* Decode an instruction */ - code = cpu_ldl_code(cpu_env(cs), pc); - op = get_opcode(code); - - if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - - dc->sink = NULL; - - instr = &i_type_instructions[op]; - instr->handler(dc, code, instr->flags); -} - -static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - /* Indicate where the next block should start */ - switch (dc->base.is_jmp) { - case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, dc->base.pc_next); - break; - - case DISAS_UPDATE: - /* Save the current PC, and return to the main loop. */ - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - tcg_gen_exit_tb(NULL, 0); - break; - - case DISAS_NORETURN: - /* nothing more to generate */ - break; - - default: - g_assert_not_reached(); - } -} - -static void nios2_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - -static const TranslatorOps nios2_tr_ops = { - .init_disas_context = nios2_tr_init_disas_context, - .tb_start = nios2_tr_tb_start, - .insn_start = nios2_tr_insn_start, - .translate_insn = nios2_tr_translate_insn, - .tb_stop = nios2_tr_tb_stop, - .disas_log = nios2_tr_disas_log, -}; - -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, - vaddr pc, void *host_pc) -{ - DisasContext dc; - translator_loop(cs, tb, max_insns, pc, host_pc, &nios2_tr_ops, &dc.base); -} - -void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - int i; - - qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc)); - - for (i = 0; i < NUM_GP_REGS; i++) { - qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - -#if !defined(CONFIG_USER_ONLY) - int j; - - for (i = j = 0; i < NUM_CR_REGS; i++) { - if (!nios2_cr_reserved(&cpu->cr_state[i])) { - qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); - if (++j % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - } - if (j % 4 != 0) { - qemu_fprintf(f, "\n"); - } - if (cpu->mmu_present) { - qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", - env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, - FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), - env->mmu.tlbacc_wr); - } -#endif - qemu_fprintf(f, "\n\n"); -} - -void nios2_tcg_init(void) -{ -#ifndef CONFIG_USER_ONLY - TCGv_ptr crs = tcg_global_mem_new_ptr(tcg_env, - offsetof(CPUNios2State, regs), "crs"); - - for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]); - } - -#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N]) -#else -#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N]) -#endif - - for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_R[i] = tcg_global_mem_new(tcg_env, offsetof_regs0(i), - gr_regnames[i]); - } - -#undef offsetof_regs0 - - cpu_pc = tcg_global_mem_new(tcg_env, - offsetof(CPUNios2State, pc), "pc"); -} diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 989b65111c..180ac17326 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -1426,14 +1426,6 @@ class BootLinuxConsole(LinuxKernelTest): tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' self.do_test_advcal_2018('20', tar_hash, 'vmlinux') - def test_nios2_10m50(self): - """ - :avocado: tags=arch:nios2 - :avocado: tags=machine:10m50-ghrd - """ - tar_hash = 'e4251141726c412ac0407c5a6bceefbbff018918' - self.do_test_advcal_2018('14', tar_hash, 'vmlinux.elf') - def test_ppc64_e500(self): """ :avocado: tags=arch:ppc64 diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 10d99403a4..2c81412dba 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -382,17 +382,6 @@ class ReplayKernelNormal(ReplayKernelBase): file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'vmlinux') - def test_nios2_10m50(self): - """ - :avocado: tags=arch:nios2 - :avocado: tags=machine:10m50-ghrd - """ - tar_hash = 'e4251141726c412ac0407c5a6bceefbbff018918' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day14.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'vmlinux.elf') - def test_ppc_g3beige(self): """ :avocado: tags=arch:ppc diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 5ba5b50ab9..8df50a0ca0 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -114,13 +114,8 @@ docker-image-debian-microblaze-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docke $(DOCKER_FILES_DIR)/debian-microblaze-cross.d/build-toolchain.sh $(call debian-toolchain, $@) -docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ - $(DOCKER_FILES_DIR)/debian-nios2-cross.d/build-toolchain.sh - $(call debian-toolchain, $@) - # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-microblaze-cross -DOCKER_PARTIAL_IMAGES += debian-nios2-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross DOCKER_PARTIAL_IMAGES += fedora-cris-cross diff --git a/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh deleted file mode 100755 index ba3c9d8aff..0000000000 --- a/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash - -set -e - -TARGET=nios2-linux-gnu -LINUX_ARCH=nios2 - -J=$(expr $(nproc) / 2) -TOOLCHAIN_INSTALL=/usr/local -TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin -CROSS_SYSROOT=${TOOLCHAIN_INSTALL}/$TARGET/sys-root - -export PATH=${TOOLCHAIN_BIN}:$PATH - -# -# Grab all of the source for the toolchain bootstrap. -# - -wget https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz -wget https://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.xz -wget https://ftp.gnu.org/gnu/glibc/glibc-2.34.tar.xz -wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.70.tar.xz - -tar axf binutils-2.37.tar.xz -tar axf gcc-11.2.0.tar.xz -tar axf glibc-2.34.tar.xz -tar axf linux-5.10.70.tar.xz - -mv binutils-2.37 src-binu -mv gcc-11.2.0 src-gcc -mv glibc-2.34 src-glibc -mv linux-5.10.70 src-linux - -mkdir -p bld-hdr bld-binu bld-gcc bld-glibc -mkdir -p ${CROSS_SYSROOT}/usr/include - -# -# Install kernel and glibc headers -# - -cd src-linux -make headers_install ARCH=${LINUX_ARCH} INSTALL_HDR_PATH=${CROSS_SYSROOT}/usr -cd .. - -cd bld-hdr -../src-glibc/configure --prefix=/usr --host=${TARGET} -make install-headers DESTDIR=${CROSS_SYSROOT} -touch ${CROSS_SYSROOT}/usr/include/gnu/stubs.h -cd .. - -# -# Build binutils -# - -cd bld-binu -../src-binu/configure --disable-werror \ - --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} -make -j${J} -make install -cd .. - -# -# Build gcc, without shared libraries, because we do not yet -# have a shared libc against which to link. -# - -cd bld-gcc -../src-gcc/configure --disable-werror --disable-shared \ - --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} \ - --enable-languages=c --disable-libssp --disable-libsanitizer \ - --disable-libatomic --disable-libgomp --disable-libquadmath -make -j${J} -make install -cd .. - -# -# Build glibc -# There are a few random things that use c++ but we didn't build that -# cross-compiler. We can get away without them. Disable CXX so that -# glibc doesn't try to use the host c++ compiler. -# - -cd bld-glibc -CXX=false ../src-glibc/configure --prefix=/usr --host=${TARGET} -make -j${j} -make install DESTDIR=${CROSS_SYSROOT} -cd .. diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index 31cc0bfb01..05da7bc72d 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -38,7 +38,6 @@ static struct arch2cpu cpus_map[] = { { "mipsel", "I7200" }, { "mips64", "20Kc" }, { "mips64el", "I6500" }, - { "nios2", "FIXME" }, { "or1k", "or1200" }, { "ppc", "604" }, { "ppc64", "power8e_v2.1" }, diff --git a/tests/tcg/nios2/10m50-ghrd.ld b/tests/tcg/nios2/10m50-ghrd.ld deleted file mode 100644 index 71cdda450c..0000000000 --- a/tests/tcg/nios2/10m50-ghrd.ld +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Link script for the Nios2 10m50-ghrd board. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -MEMORY -{ - tpf (rx) : ORIGIN = 0xc0000000, LENGTH = 1K - ram (rwx) : ORIGIN = 0xc8000000, LENGTH = 128M -} - -PHDRS -{ - RAM PT_LOAD; -} - -ENTRY(_start) -EXTERN(_start) -EXTERN(_interrupt) -EXTERN(_fast_tlb_miss) - -SECTIONS -{ - /* Begin at the (hardcoded) _interrupt entry point. */ - .text 0xc8000120 : { - *(.text.intr) - *(.text .text.* .gnu.linkonce.t.*) - } >ram :RAM - - .rodata : ALIGN(4) { - *(.rodata .rodata.* .gnu.linkonce.r.*) - } > ram :RAM - - .eh_frame_hdr : ALIGN (4) { - KEEP (*(.eh_frame_hdr)) - *(.eh_frame_entry .eh_frame_entry.*) - } >ram :RAM - .eh_frame : ALIGN (4) { - KEEP (*(.eh_frame)) *(.eh_frame.*) - } >ram :RAM - - .data : ALIGN(4) { - *(.shdata) - *(.data .data.* .gnu.linkonce.d.*) - } >ram :RAM - - HIDDEN (_gp = ALIGN(16) + 0x7ff0); - PROVIDE_HIDDEN (gp = _gp); - .got : ALIGN(4) { - *(.got.plt) *(.igot.plt) *(.got) *(.igot) - } >ram :RAM - - .sdata : ALIGN(4) { - *(.sdata .sdata.* .gnu.linkonce.s.*) - } >ram :RAM - - .bss : ALIGN(4) { - __bss_start = ABSOLUTE(.); - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - . = ALIGN(4); - __bss_end = ABSOLUTE(.); - } >ram :RAM - - __stack = ORIGIN(ram) + LENGTH(ram); -} diff --git a/tests/tcg/nios2/Makefile.softmmu-target b/tests/tcg/nios2/Makefile.softmmu-target deleted file mode 100644 index bc7fd55060..0000000000 --- a/tests/tcg/nios2/Makefile.softmmu-target +++ /dev/null @@ -1,32 +0,0 @@ -# -# Nios2 system tests -# -# Copyright Linaro Ltd 2022 -# SPDX-License-Identifier: GPL-2.0-or-later -# - -NIOS2_SYSTEM_SRC = $(SRC_PATH)/tests/tcg/nios2 -VPATH += $(NIOS2_SYSTEM_SRC) - -# These objects provide the basic boot code and helper functions for all tests -CRT_OBJS = boot.o intr.o $(MINILIB_OBJS) -LINK_SCRIPT = $(NIOS2_SYSTEM_SRC)/10m50-ghrd.ld - -CFLAGS += -nostdlib -g -O0 $(MINILIB_INC) -LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc - -%.o: %.S - $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@, AS, $@) - -%.o: %.c - $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@, CC, $@) - -# Build and link the tests -%: %.o $(LINK_SCRIPT) $(CRT_OBJS) - $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@) - -QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting-config enable=on,target=native,chardev=output -kernel - -memory: CFLAGS+=-DCHECK_UNALIGNED=0 -TESTS += $(MULTIARCH_TESTS) -TESTS += test-shadow-1 diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target deleted file mode 100644 index b38e2352b7..0000000000 --- a/tests/tcg/nios2/Makefile.target +++ /dev/null @@ -1,11 +0,0 @@ -# nios2 specific test tweaks - -# Currently nios2 signal handling is broken -run-signals: signals - $(call skip-test, $<, "BROKEN") -run-plugin-signals-with-%: - $(call skip-test, $<, "BROKEN") -run-linux-test: linux-test - $(call skip-test, $<, "BROKEN") -run-plugin-linux-test-with-%: - $(call skip-test, $<, "BROKEN") diff --git a/tests/tcg/nios2/boot.S b/tests/tcg/nios2/boot.S deleted file mode 100644 index f6771cbc81..0000000000 --- a/tests/tcg/nios2/boot.S +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Minimal Nios2 system boot code. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "semicall.h" - - .text - .set noat - -_start: - /* Linker script defines stack at end of ram. */ - movia sp, __stack - - /* Install trampoline to _fast_tlb_miss at hardcoded vector. */ - movia r4, 0xc0000100 - movia r5, _ftm_tramp - movi r6, .L__ftm_end - _ftm_tramp - call memcpy - - /* Zero the bss to satisfy C. */ - movia r4, __bss_start - movia r6, __bss_end - sub r6, r6, r4 - movi r5, 0 - call memset - - /* Test! */ - call main - - /* Exit with main's return value. */ - movi r4, HOSTED_EXIT - mov r5, r2 - semihosting_call - - .globl _start - .type _start, @function - .size _start, . - _start - -_ftm_tramp: - movia et, _fast_tlb_miss - jmp et -.L__ftm_end: - - .type _ftm_tramp, @function - .size _ftm_tramp, . - _ftm_tramp - -#define dst r4 -#define src r5 -#define len r6 - -memcpy: - /* Store return value right away, per API */ - mov r2, dst - - /* Check for both dst and src aligned. */ - or at, dst, src - andi at, at, 3 - bne at, zero, .L_mc_test1 - - /* Copy blocks of 8. */ - - movi at, 8 - bltu len, at, .L_mc_test4 - -.L_mc_loop8: - ldw r8, 0(src) - ldw r9, 4(src) - addi src, src, 8 - addi dst, dst, 8 - subi len, len, 8 - stw r8, -8(dst) - stw r9, -4(dst) - bgeu len, at, .L_mc_loop8 - - /* Copy final aligned block of 4. */ - -.L_mc_test4: - movi at, 4 - bltu len, at, .L_mc_test1 - - ldw r8, 0(src) - addi src, src, 4 - addi dst, dst, 4 - subi len, len, 4 - stw r8, -4(dst) - - /* Copy single bytes to finish. */ - -.L_mc_test1: - beq len, zero, .L_mc_done - -.L_mc_loop1: - ldb r8, 0(src) - addi src, src, 1 - addi dst, dst, 1 - subi len, len, 1 - stb r8, -1(dst) - bne len, zero, .L_mc_loop1 - -.L_mc_done: - ret - -#undef dst -#undef src -#undef len - - .global memcpy - .type memcpy, @function - .size memcpy, . - memcpy - -#define dst r4 -#define val r5 -#define len r6 - -memset: - /* Store return value right away, per API */ - mov r2, dst - - /* Check for small blocks; fall back to bytewise. */ - movi r3, 8 - bltu len, r3, .L_ms_test1 - - /* Replicate the byte across the word. */ - andi val, val, 0xff - slli at, val, 8 - or val, val, at - slli at, val, 16 - or val, val, at - - /* Check for destination alignment; realign if needed. */ - andi at, dst, 3 - bne at, zero, .L_ms_align - - /* Set blocks of 8. */ - -.L_ms_loop8: - stw val, 0(dst) - stw val, 4(dst) - addi dst, dst, 8 - subi len, len, 8 - bgeu len, r3, .L_ms_loop8 - - /* Set final aligned block of 4. */ - -.L_ms_test4: - movi at, 4 - bltu len, at, .L_ms_test1 - - stw r8, 0(dst) - addi dst, dst, 4 - subi len, len, 4 - stw r8, -4(dst) - - /* Set single bytes to finish. */ - -.L_ms_test1: - beq len, zero, .L_ms_done - -.L_ms_loop1: - stb r8, 0(dst) - addi dst, dst, 1 - subi len, len, 1 - bne len, zero, .L_ms_loop1 - -.L_ms_done: - ret - - /* Realign for a large block, len >= 8. */ -.L_ms_align: - andi at, dst, 1 - beq at, zero, 2f - - stb val, 0(dst) - addi dst, dst, 1 - subi len, len, 1 - -2: andi at, dst, 2 - beq at, zero, 4f - - sth val, 0(dst) - addi dst, dst, 2 - subi len, len, 2 - -4: bgeu len, r3, .L_ms_loop8 - br .L_ms_test4 - -#undef dst -#undef val -#undef len - - .global memset - .type memset, @function - .size memset, . - memset - -/* - * void __sys_outc(char c); - */ -__sys_outc: - subi sp, sp, 16 - stb r4, 0(sp) /* buffer[0] = c */ - movi at, 1 - stw at, 4(sp) /* STDOUT_FILENO */ - stw sp, 8(sp) /* buffer */ - stw at, 12(sp) /* len */ - - movi r4, HOSTED_WRITE - addi r5, sp, 4 - semihosting_call - - addi sp, sp, 16 - ret - - .global __sys_outc - .type __sys_outc, @function - .size __sys_outc, . - __sys_outc diff --git a/tests/tcg/nios2/intr.S b/tests/tcg/nios2/intr.S deleted file mode 100644 index c1730692ba..0000000000 --- a/tests/tcg/nios2/intr.S +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Minimal Nios2 system boot code -- exit on interrupt. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "semicall.h" - - .section .text.intr, "ax" - .global _interrupt - .type _interrupt, @function - -_interrupt: - rdctl r5, exception /* extract exception.CAUSE */ - srli r5, r5, 2 - movi r4, HOSTED_EXIT - semihosting_call - - .size _interrupt, . - _interrupt - - .text - .global _fast_tlb_miss - .type _fast_tlb_miss, @function - -_fast_tlb_miss: - movi r5, 32 - movi r4, HOSTED_EXIT - semihosting_call - - .size _fast_tlb_miss, . - _fast_tlb_miss diff --git a/tests/tcg/nios2/semicall.h b/tests/tcg/nios2/semicall.h deleted file mode 100644 index 6ad4978099..0000000000 --- a/tests/tcg/nios2/semicall.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Nios2 semihosting interface. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef SEMICALL_H -#define SEMICALL_H - -#define HOSTED_EXIT 0 -#define HOSTED_INIT_SIM 1 -#define HOSTED_OPEN 2 -#define HOSTED_CLOSE 3 -#define HOSTED_READ 4 -#define HOSTED_WRITE 5 -#define HOSTED_LSEEK 6 -#define HOSTED_RENAME 7 -#define HOSTED_UNLINK 8 -#define HOSTED_STAT 9 -#define HOSTED_FSTAT 10 -#define HOSTED_GETTIMEOFDAY 11 -#define HOSTED_ISATTY 12 -#define HOSTED_SYSTEM 13 - -#define semihosting_call break 1 - -#endif /* SEMICALL_H */ diff --git a/tests/tcg/nios2/test-shadow-1.S b/tests/tcg/nios2/test-shadow-1.S deleted file mode 100644 index 79ef69db12..0000000000 --- a/tests/tcg/nios2/test-shadow-1.S +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Regression test for TCG indirect global lowering. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "semicall.h" - - .text - .set noat - .align 2 - .globl main - .type main, @function - -main: - /* Initialize r0 in shadow register set 1. */ - movhi at, 1 /* PRS=1, CRS=0, RSIE=0, PIE=0 */ - wrctl status, at - wrprs zero, zero - - /* Change current register set to 1. */ - movi at, 1 << 10 /* PRS=0, CRS=1, RSIE=0, PIE=0 */ - wrctl estatus, at - movia ea, 1f - eret - - /* Load address for callr, then end TB. */ -1: movia at, 3f - br 2f - - /* Test case! TCG abort on indirect lowering across brcond. */ -2: callr at - - /* exit(0) */ -3: movi r4, HOSTED_EXIT - movi r5, 0 - semihosting_call - - .size main, . - main From a60e53fa8fecb3c2449f9be0f4dfc2afe17cf39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Mar 2024 15:22:20 +0100 Subject: [PATCH 0128/2884] hw/timer: Remove the ALTERA_TIMER model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ALTERA_TIMER was only used by Nios II machines, which have been removed. Since it has no direct user, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240327144806.11319-4-philmd@linaro.org> --- hw/timer/Kconfig | 4 - hw/timer/altera_timer.c | 244 ---------------------------------------- hw/timer/meson.build | 1 - 3 files changed, 249 deletions(-) delete mode 100644 hw/timer/altera_timer.c diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 010be7ed1f..61fbb62b65 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -17,10 +17,6 @@ config I8254 bool depends on ISA_BUS -config ALTERA_TIMER - bool - select PTIMER - config ALLWINNER_A10_PIT bool select PTIMER diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c deleted file mode 100644 index 0f1f54206a..0000000000 --- a/hw/timer/altera_timer.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * QEMU model of the Altera timer. - * - * Copyright (c) 2012 Chris Wulff - * - * 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.1 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 - * - */ - -#include "qemu/osdep.h" -#include "qemu/module.h" -#include "qapi/error.h" - -#include "hw/sysbus.h" -#include "hw/irq.h" -#include "hw/ptimer.h" -#include "hw/qdev-properties.h" -#include "qom/object.h" - -#define R_STATUS 0 -#define R_CONTROL 1 -#define R_PERIODL 2 -#define R_PERIODH 3 -#define R_SNAPL 4 -#define R_SNAPH 5 -#define R_MAX 6 - -#define STATUS_TO 0x0001 -#define STATUS_RUN 0x0002 - -#define CONTROL_ITO 0x0001 -#define CONTROL_CONT 0x0002 -#define CONTROL_START 0x0004 -#define CONTROL_STOP 0x0008 - -#define TYPE_ALTERA_TIMER "ALTR.timer" -OBJECT_DECLARE_SIMPLE_TYPE(AlteraTimer, ALTERA_TIMER) - -struct AlteraTimer { - SysBusDevice busdev; - MemoryRegion mmio; - qemu_irq irq; - uint32_t freq_hz; - ptimer_state *ptimer; - uint32_t regs[R_MAX]; -}; - -static int timer_irq_state(AlteraTimer *t) -{ - bool irq = (t->regs[R_STATUS] & STATUS_TO) && - (t->regs[R_CONTROL] & CONTROL_ITO); - return irq; -} - -static uint64_t timer_read(void *opaque, hwaddr addr, - unsigned int size) -{ - AlteraTimer *t = opaque; - uint64_t r = 0; - - addr >>= 2; - - switch (addr) { - case R_CONTROL: - r = t->regs[R_CONTROL] & (CONTROL_ITO | CONTROL_CONT); - break; - - default: - if (addr < ARRAY_SIZE(t->regs)) { - r = t->regs[addr]; - } - break; - } - - return r; -} - -static void timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned int size) -{ - AlteraTimer *t = opaque; - uint64_t tvalue; - uint32_t count = 0; - int irqState = timer_irq_state(t); - - addr >>= 2; - - switch (addr) { - case R_STATUS: - /* The timeout bit is cleared by writing the status register. */ - t->regs[R_STATUS] &= ~STATUS_TO; - break; - - case R_CONTROL: - ptimer_transaction_begin(t->ptimer); - t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT); - if ((value & CONTROL_START) && - !(t->regs[R_STATUS] & STATUS_RUN)) { - ptimer_run(t->ptimer, 1); - t->regs[R_STATUS] |= STATUS_RUN; - } - if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) { - ptimer_stop(t->ptimer); - t->regs[R_STATUS] &= ~STATUS_RUN; - } - ptimer_transaction_commit(t->ptimer); - break; - - case R_PERIODL: - case R_PERIODH: - ptimer_transaction_begin(t->ptimer); - t->regs[addr] = value & 0xFFFF; - if (t->regs[R_STATUS] & STATUS_RUN) { - ptimer_stop(t->ptimer); - t->regs[R_STATUS] &= ~STATUS_RUN; - } - tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; - ptimer_set_limit(t->ptimer, tvalue + 1, 1); - ptimer_transaction_commit(t->ptimer); - break; - - case R_SNAPL: - case R_SNAPH: - count = ptimer_get_count(t->ptimer); - t->regs[R_SNAPL] = count & 0xFFFF; - t->regs[R_SNAPH] = count >> 16; - break; - - default: - break; - } - - if (irqState != timer_irq_state(t)) { - qemu_set_irq(t->irq, timer_irq_state(t)); - } -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -static void timer_hit(void *opaque) -{ - AlteraTimer *t = opaque; - const uint64_t tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; - - t->regs[R_STATUS] |= STATUS_TO; - - ptimer_set_limit(t->ptimer, tvalue + 1, 1); - - if (!(t->regs[R_CONTROL] & CONTROL_CONT)) { - t->regs[R_STATUS] &= ~STATUS_RUN; - ptimer_set_count(t->ptimer, tvalue); - } else { - ptimer_run(t->ptimer, 1); - } - - qemu_set_irq(t->irq, timer_irq_state(t)); -} - -static void altera_timer_realize(DeviceState *dev, Error **errp) -{ - AlteraTimer *t = ALTERA_TIMER(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - if (t->freq_hz == 0) { - error_setg(errp, "\"clock-frequency\" property must be provided."); - return; - } - - t->ptimer = ptimer_init(timer_hit, t, PTIMER_POLICY_LEGACY); - ptimer_transaction_begin(t->ptimer); - ptimer_set_freq(t->ptimer, t->freq_hz); - ptimer_transaction_commit(t->ptimer); - - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, - TYPE_ALTERA_TIMER, R_MAX * sizeof(uint32_t)); - sysbus_init_mmio(sbd, &t->mmio); -} - -static void altera_timer_init(Object *obj) -{ - AlteraTimer *t = ALTERA_TIMER(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - sysbus_init_irq(sbd, &t->irq); -} - -static void altera_timer_reset(DeviceState *dev) -{ - AlteraTimer *t = ALTERA_TIMER(dev); - - ptimer_transaction_begin(t->ptimer); - ptimer_stop(t->ptimer); - ptimer_set_limit(t->ptimer, 0xffffffff, 1); - ptimer_transaction_commit(t->ptimer); - memset(t->regs, 0, sizeof(t->regs)); -} - -static Property altera_timer_properties[] = { - DEFINE_PROP_UINT32("clock-frequency", AlteraTimer, freq_hz, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void altera_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = altera_timer_realize; - device_class_set_props(dc, altera_timer_properties); - dc->reset = altera_timer_reset; -} - -static const TypeInfo altera_timer_info = { - .name = TYPE_ALTERA_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AlteraTimer), - .instance_init = altera_timer_init, - .class_init = altera_timer_class_init, -}; - -static void altera_timer_register(void) -{ - type_register_static(&altera_timer_info); -} - -type_init(altera_timer_register) diff --git a/hw/timer/meson.build b/hw/timer/meson.build index fc632ad445..80427852e0 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -1,6 +1,5 @@ system_ss.add(when: 'CONFIG_A9_GTIMER', if_true: files('a9gtimer.c')) system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIT', if_true: files('allwinner-a10-pit.c')) -system_ss.add(when: 'CONFIG_ALTERA_TIMER', if_true: files('altera_timer.c')) system_ss.add(when: 'CONFIG_ARM_MPTIMER', if_true: files('arm_mptimer.c')) system_ss.add(when: 'CONFIG_ARM_TIMER', if_true: files('arm_timer.c')) system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_systick.c')) From 1dfd42c4264bbf47415a9e73f0d6b4e6a7cd7393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Mar 2024 12:53:00 +0100 Subject: [PATCH 0129/2884] hw/rdma: Remove deprecated pvrdma device and rdmacm-mux helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The whole RDMA subsystem was deprecated in commit e9a54265f5 ("hw/rdma: Deprecate the pvrdma device and the rdma subsystem") released in v8.2. Remove: - PVRDMA device - generated vmw_pvrdma/ directory from linux-headers - rdmacm-mux tool from contrib/ Cc: Yuval Shaia Cc: Marcel Apfelbaum Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Michael S. Tsirkin Message-Id: <20240328130255.52257-2-philmd@linaro.org> --- Kconfig.host | 3 - MAINTAINERS | 10 - contrib/rdmacm-mux/main.c | 831 ---------- contrib/rdmacm-mux/meson.build | 7 - contrib/rdmacm-mux/rdmacm-mux.h | 61 - docs/about/deprecated.rst | 9 - docs/about/removed-features.rst | 4 + docs/pvrdma.txt | 345 ---- docs/system/loongarch/virt.rst | 2 +- hmp-commands-info.hx | 13 - hw/Kconfig | 1 - hw/core/machine-qmp-cmds.c | 32 - hw/meson.build | 1 - hw/rdma/Kconfig | 3 - hw/rdma/meson.build | 12 - hw/rdma/rdma.c | 30 - hw/rdma/rdma_backend.c | 1401 ----------------- hw/rdma/rdma_backend.h | 129 -- hw/rdma/rdma_backend_defs.h | 76 - hw/rdma/rdma_rm.c | 812 ---------- hw/rdma/rdma_rm.h | 97 -- hw/rdma/rdma_rm_defs.h | 146 -- hw/rdma/rdma_utils.c | 126 -- hw/rdma/rdma_utils.h | 63 - hw/rdma/trace-events | 31 - hw/rdma/trace.h | 1 - hw/rdma/vmw/pvrdma.h | 144 -- hw/rdma/vmw/pvrdma_cmd.c | 815 ---------- hw/rdma/vmw/pvrdma_dev_ring.c | 141 -- hw/rdma/vmw/pvrdma_dev_ring.h | 46 - hw/rdma/vmw/pvrdma_main.c | 735 --------- hw/rdma/vmw/pvrdma_qp_ops.c | 298 ---- hw/rdma/vmw/pvrdma_qp_ops.h | 28 - hw/rdma/vmw/trace-events | 17 - hw/rdma/vmw/trace.h | 1 - include/hw/rdma/rdma.h | 37 - include/monitor/hmp.h | 1 - .../infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h | 685 -------- .../infiniband/hw/vmw_pvrdma/pvrdma_verbs.h | 348 ---- .../standard-headers/rdma/vmw_pvrdma-abi.h | 310 ---- meson.build | 36 - meson_options.txt | 2 - monitor/qmp-cmds.c | 1 - qapi/machine.json | 17 - qapi/meson.build | 1 - qapi/qapi-schema.json | 1 - qapi/rdma.json | 38 - qemu-options.hx | 3 - .../ci/org.centos/stream/8/x86_64/configure | 1 - scripts/meson-buildoptions.sh | 3 - scripts/update-linux-headers.sh | 27 - 51 files changed, 5 insertions(+), 7977 deletions(-) delete mode 100644 contrib/rdmacm-mux/main.c delete mode 100644 contrib/rdmacm-mux/meson.build delete mode 100644 contrib/rdmacm-mux/rdmacm-mux.h delete mode 100644 docs/pvrdma.txt delete mode 100644 hw/rdma/Kconfig delete mode 100644 hw/rdma/meson.build delete mode 100644 hw/rdma/rdma.c delete mode 100644 hw/rdma/rdma_backend.c delete mode 100644 hw/rdma/rdma_backend.h delete mode 100644 hw/rdma/rdma_backend_defs.h delete mode 100644 hw/rdma/rdma_rm.c delete mode 100644 hw/rdma/rdma_rm.h delete mode 100644 hw/rdma/rdma_rm_defs.h delete mode 100644 hw/rdma/rdma_utils.c delete mode 100644 hw/rdma/rdma_utils.h delete mode 100644 hw/rdma/trace-events delete mode 100644 hw/rdma/trace.h delete mode 100644 hw/rdma/vmw/pvrdma.h delete mode 100644 hw/rdma/vmw/pvrdma_cmd.c delete mode 100644 hw/rdma/vmw/pvrdma_dev_ring.c delete mode 100644 hw/rdma/vmw/pvrdma_dev_ring.h delete mode 100644 hw/rdma/vmw/pvrdma_main.c delete mode 100644 hw/rdma/vmw/pvrdma_qp_ops.c delete mode 100644 hw/rdma/vmw/pvrdma_qp_ops.h delete mode 100644 hw/rdma/vmw/trace-events delete mode 100644 hw/rdma/vmw/trace.h delete mode 100644 include/hw/rdma/rdma.h delete mode 100644 include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h delete mode 100644 include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h delete mode 100644 include/standard-headers/rdma/vmw_pvrdma-abi.h delete mode 100644 qapi/rdma.json diff --git a/Kconfig.host b/Kconfig.host index f496475f8e..f6a2a131e6 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -35,9 +35,6 @@ config VHOST_KERNEL config VIRTFS bool -config PVRDMA - bool - config MULTIPROCESS_ALLOWED bool imply MULTIPROCESS diff --git a/MAINTAINERS b/MAINTAINERS index 4ccba2f722..8bb32f4a7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4044,16 +4044,6 @@ F: block/replication.c F: tests/unit/test-replication.c F: docs/block-replication.txt -PVRDMA -M: Yuval Shaia -M: Marcel Apfelbaum -S: Odd Fixes -F: hw/rdma/* -F: hw/rdma/vmw/* -F: docs/pvrdma.txt -F: contrib/rdmacm-mux/* -F: qapi/rdma.json - Semihosting M: Alex Bennée S: Maintained diff --git a/contrib/rdmacm-mux/main.c b/contrib/rdmacm-mux/main.c deleted file mode 100644 index 771ca01e03..0000000000 --- a/contrib/rdmacm-mux/main.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * QEMU paravirtual RDMA - rdmacm-mux implementation - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "rdmacm-mux.h" - -#define SCALE_US 1000 -#define COMMID_TTL 2 /* How many SCALE_US a context of MAD session is saved */ -#define SLEEP_SECS 5 /* This is used both in poll() and thread */ -#define SERVER_LISTEN_BACKLOG 10 -#define MAX_CLIENTS 4096 -#define MAD_RMPP_VERSION 0 -#define MAD_METHOD_MASK0 0x8 - -#define IB_USER_MAD_LONGS_PER_METHOD_MASK (128 / (8 * sizeof(long))) - -#define CM_REQ_DGID_POS 80 -#define CM_SIDR_REQ_DGID_POS 44 - -/* The below can be override by command line parameter */ -#define UNIX_SOCKET_PATH "/var/run/rdmacm-mux" -/* Has format %s-%s-%d" -- */ -#define SOCKET_PATH_MAX (PATH_MAX - NAME_MAX - sizeof(int) - 2) -#define RDMA_PORT_NUM 1 - -typedef struct RdmaCmServerArgs { - char unix_socket_path[PATH_MAX]; - char rdma_dev_name[NAME_MAX]; - int rdma_port_num; -} RdmaCMServerArgs; - -typedef struct CommId2FdEntry { - int fd; - int ttl; /* Initialized to 2, decrement each timeout, entry delete when 0 */ - __be64 gid_ifid; -} CommId2FdEntry; - -typedef struct RdmaCmUMadAgent { - int port_id; - int agent_id; - GHashTable *gid2fd; /* Used to find fd of a given gid */ - GHashTable *commid2fd; /* Used to find fd on of a given comm_id */ -} RdmaCmUMadAgent; - -typedef struct RdmaCmServer { - bool run; - RdmaCMServerArgs args; - struct pollfd fds[MAX_CLIENTS]; - int nfds; - RdmaCmUMadAgent umad_agent; - pthread_t umad_recv_thread; - pthread_rwlock_t lock; -} RdmaCMServer; - -static RdmaCMServer server = {0}; - -static void usage(const char *progname) -{ - printf("Usage: %s [OPTION]...\n" - "Start a RDMA-CM multiplexer\n" - "\n" - "\t-h Show this help\n" - "\t-d rdma-device-name Name of RDMA device to register with\n" - "\t-s unix-socket-path Path to unix socket to listen on (default %s)\n" - "\t-p rdma-device-port Port number of RDMA device to register with (default %d)\n", - progname, UNIX_SOCKET_PATH, RDMA_PORT_NUM); -} - -static void help(const char *progname) -{ - fprintf(stderr, "Try '%s -h' for more information.\n", progname); -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - char unix_socket_path[SOCKET_PATH_MAX]; - - strcpy(server.args.rdma_dev_name, ""); - strcpy(unix_socket_path, UNIX_SOCKET_PATH); - server.args.rdma_port_num = RDMA_PORT_NUM; - - while ((c = getopt(argc, argv, "hs:d:p:")) != -1) { - switch (c) { - case 'h': - usage(argv[0]); - exit(0); - - case 'd': - strncpy(server.args.rdma_dev_name, optarg, NAME_MAX - 1); - break; - - case 's': - /* This is temporary, final name will build below */ - strncpy(unix_socket_path, optarg, SOCKET_PATH_MAX - 1); - break; - - case 'p': - server.args.rdma_port_num = atoi(optarg); - break; - - default: - help(argv[0]); - exit(1); - } - } - - if (!strcmp(server.args.rdma_dev_name, "")) { - fprintf(stderr, "Missing RDMA device name\n"); - help(argv[0]); - exit(1); - } - - /* Build unique unix-socket file name */ - snprintf(server.args.unix_socket_path, PATH_MAX, "%s-%s-%d", - unix_socket_path, server.args.rdma_dev_name, - server.args.rdma_port_num); - - syslog(LOG_INFO, "unix_socket_path=%s", server.args.unix_socket_path); - syslog(LOG_INFO, "rdma-device-name=%s", server.args.rdma_dev_name); - syslog(LOG_INFO, "rdma-device-port=%d", server.args.rdma_port_num); -} - -static void hash_tbl_alloc(void) -{ - - server.umad_agent.gid2fd = g_hash_table_new_full(g_int64_hash, - g_int64_equal, - g_free, g_free); - server.umad_agent.commid2fd = g_hash_table_new_full(g_int_hash, - g_int_equal, - g_free, g_free); -} - -static void hash_tbl_free(void) -{ - if (server.umad_agent.commid2fd) { - g_hash_table_destroy(server.umad_agent.commid2fd); - } - if (server.umad_agent.gid2fd) { - g_hash_table_destroy(server.umad_agent.gid2fd); - } -} - - -static int _hash_tbl_search_fd_by_ifid(__be64 *gid_ifid) -{ - int *fd; - - fd = g_hash_table_lookup(server.umad_agent.gid2fd, gid_ifid); - if (!fd) { - /* Let's try IPv4 */ - *gid_ifid |= 0x00000000ffff0000; - fd = g_hash_table_lookup(server.umad_agent.gid2fd, gid_ifid); - } - - return fd ? *fd : 0; -} - -static int hash_tbl_search_fd_by_ifid(int *fd, __be64 *gid_ifid) -{ - pthread_rwlock_rdlock(&server.lock); - *fd = _hash_tbl_search_fd_by_ifid(gid_ifid); - pthread_rwlock_unlock(&server.lock); - - if (!*fd) { - syslog(LOG_WARNING, "Can't find matching for ifid 0x%llx\n", *gid_ifid); - return -ENOENT; - } - - return 0; -} - -static int hash_tbl_search_fd_by_comm_id(uint32_t comm_id, int *fd, - __be64 *gid_idid) -{ - CommId2FdEntry *fde; - - pthread_rwlock_rdlock(&server.lock); - fde = g_hash_table_lookup(server.umad_agent.commid2fd, &comm_id); - pthread_rwlock_unlock(&server.lock); - - if (!fde) { - syslog(LOG_WARNING, "Can't find matching for comm_id 0x%x\n", comm_id); - return -ENOENT; - } - - *fd = fde->fd; - *gid_idid = fde->gid_ifid; - - return 0; -} - -static RdmaCmMuxErrCode add_fd_ifid_pair(int fd, __be64 gid_ifid) -{ - int fd1; - - pthread_rwlock_wrlock(&server.lock); - - fd1 = _hash_tbl_search_fd_by_ifid(&gid_ifid); - if (fd1) { /* record already exist - an error */ - pthread_rwlock_unlock(&server.lock); - return fd == fd1 ? RDMACM_MUX_ERR_CODE_EEXIST : - RDMACM_MUX_ERR_CODE_EACCES; - } - - g_hash_table_insert(server.umad_agent.gid2fd, g_memdup(&gid_ifid, - sizeof(gid_ifid)), g_memdup(&fd, sizeof(fd))); - - pthread_rwlock_unlock(&server.lock); - - syslog(LOG_INFO, "0x%lx registered on socket %d", - be64toh((uint64_t)gid_ifid), fd); - - return RDMACM_MUX_ERR_CODE_OK; -} - -static RdmaCmMuxErrCode delete_fd_ifid_pair(int fd, __be64 gid_ifid) -{ - int fd1; - - pthread_rwlock_wrlock(&server.lock); - - fd1 = _hash_tbl_search_fd_by_ifid(&gid_ifid); - if (!fd1) { /* record not exist - an error */ - pthread_rwlock_unlock(&server.lock); - return RDMACM_MUX_ERR_CODE_ENOTFOUND; - } - - g_hash_table_remove(server.umad_agent.gid2fd, g_memdup(&gid_ifid, - sizeof(gid_ifid))); - pthread_rwlock_unlock(&server.lock); - - syslog(LOG_INFO, "0x%lx unregistered on socket %d", - be64toh((uint64_t)gid_ifid), fd); - - return RDMACM_MUX_ERR_CODE_OK; -} - -static void hash_tbl_save_fd_comm_id_pair(int fd, uint32_t comm_id, - uint64_t gid_ifid) -{ - CommId2FdEntry fde = {fd, COMMID_TTL, gid_ifid}; - - pthread_rwlock_wrlock(&server.lock); - g_hash_table_insert(server.umad_agent.commid2fd, - g_memdup(&comm_id, sizeof(comm_id)), - g_memdup(&fde, sizeof(fde))); - pthread_rwlock_unlock(&server.lock); -} - -static gboolean remove_old_comm_ids(gpointer key, gpointer value, - gpointer user_data) -{ - CommId2FdEntry *fde = (CommId2FdEntry *)value; - - return !fde->ttl--; -} - -static gboolean remove_entry_from_gid2fd(gpointer key, gpointer value, - gpointer user_data) -{ - if (*(int *)value == *(int *)user_data) { - syslog(LOG_INFO, "0x%lx unregistered on socket %d", - be64toh(*(uint64_t *)key), *(int *)value); - return true; - } - - return false; -} - -static void hash_tbl_remove_fd_ifid_pair(int fd) -{ - pthread_rwlock_wrlock(&server.lock); - g_hash_table_foreach_remove(server.umad_agent.gid2fd, - remove_entry_from_gid2fd, (gpointer)&fd); - pthread_rwlock_unlock(&server.lock); -} - -static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid) -{ - struct umad_hdr *hdr = (struct umad_hdr *)mad; - char *data = (char *)hdr + sizeof(*hdr); - int32_t comm_id = 0; - uint16_t attr_id = be16toh(hdr->attr_id); - int rc = 0; - - if (umad_len <= sizeof(*hdr)) { - rc = -EINVAL; - syslog(LOG_DEBUG, "Ignoring MAD packets with header only\n"); - goto out; - } - - switch (attr_id) { - case UMAD_CM_ATTR_REQ: - if (unlikely(umad_len < sizeof(*hdr) + CM_REQ_DGID_POS + - sizeof(*gid_ifid))) { - rc = -EINVAL; - syslog(LOG_WARNING, - "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, - attr_id); - goto out; - } - memcpy(gid_ifid, data + CM_REQ_DGID_POS, sizeof(*gid_ifid)); - rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid); - break; - - case UMAD_CM_ATTR_SIDR_REQ: - if (unlikely(umad_len < sizeof(*hdr) + CM_SIDR_REQ_DGID_POS + - sizeof(*gid_ifid))) { - rc = -EINVAL; - syslog(LOG_WARNING, - "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, - attr_id); - goto out; - } - memcpy(gid_ifid, data + CM_SIDR_REQ_DGID_POS, sizeof(*gid_ifid)); - rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid); - break; - - case UMAD_CM_ATTR_REP: - /* Fall through */ - case UMAD_CM_ATTR_REJ: - /* Fall through */ - case UMAD_CM_ATTR_DREQ: - /* Fall through */ - case UMAD_CM_ATTR_DREP: - /* Fall through */ - case UMAD_CM_ATTR_RTU: - data += sizeof(comm_id); - /* Fall through */ - case UMAD_CM_ATTR_SIDR_REP: - if (unlikely(umad_len < sizeof(*hdr) + sizeof(comm_id))) { - rc = -EINVAL; - syslog(LOG_WARNING, - "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, - attr_id); - goto out; - } - memcpy(&comm_id, data, sizeof(comm_id)); - if (comm_id) { - rc = hash_tbl_search_fd_by_comm_id(comm_id, fd, gid_ifid); - } - break; - - default: - rc = -EINVAL; - syslog(LOG_WARNING, "Unsupported attr_id 0x%x\n", attr_id); - } - - syslog(LOG_DEBUG, "mad_to_vm: %d 0x%x 0x%x\n", *fd, attr_id, comm_id); - -out: - return rc; -} - -static void *umad_recv_thread_func(void *args) -{ - int rc; - RdmaCmMuxMsg msg = {}; - int fd = -2; - - msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ; - msg.hdr.op_code = RDMACM_MUX_OP_CODE_MAD; - - while (server.run) { - do { - msg.umad_len = sizeof(msg.umad.mad); - rc = umad_recv(server.umad_agent.port_id, &msg.umad, &msg.umad_len, - SLEEP_SECS * SCALE_US); - if ((rc == -EIO) || (rc == -EINVAL)) { - syslog(LOG_CRIT, "Fatal error while trying to read MAD"); - } - - if (rc == -ETIMEDOUT) { - g_hash_table_foreach_remove(server.umad_agent.commid2fd, - remove_old_comm_ids, NULL); - } - } while (rc && server.run); - - if (server.run) { - rc = get_fd(msg.umad.mad, msg.umad_len, &fd, - &msg.hdr.sgid.global.interface_id); - if (rc) { - continue; - } - - send(fd, &msg, sizeof(msg), 0); - } - } - - return NULL; -} - -static int read_and_process(int fd) -{ - int rc; - RdmaCmMuxMsg msg = {}; - struct umad_hdr *hdr; - uint32_t *comm_id = 0; - uint16_t attr_id; - - rc = recv(fd, &msg, sizeof(msg), 0); - syslog(LOG_DEBUG, "Socket %d, recv %d\n", fd, rc); - - if (rc < 0 && errno != EWOULDBLOCK) { - syslog(LOG_ERR, "Fail to read from socket %d\n", fd); - return -EIO; - } - - if (!rc) { - syslog(LOG_ERR, "Fail to read from socket %d\n", fd); - return -EPIPE; - } - - if (msg.hdr.msg_type != RDMACM_MUX_MSG_TYPE_REQ) { - syslog(LOG_WARNING, "Got non-request message (%d) from socket %d\n", - msg.hdr.msg_type, fd); - return -EPERM; - } - - switch (msg.hdr.op_code) { - case RDMACM_MUX_OP_CODE_REG: - rc = add_fd_ifid_pair(fd, msg.hdr.sgid.global.interface_id); - break; - - case RDMACM_MUX_OP_CODE_UNREG: - rc = delete_fd_ifid_pair(fd, msg.hdr.sgid.global.interface_id); - break; - - case RDMACM_MUX_OP_CODE_MAD: - /* If this is REQ or REP then store the pair comm_id,fd to be later - * used for other messages where gid is unknown */ - hdr = (struct umad_hdr *)msg.umad.mad; - attr_id = be16toh(hdr->attr_id); - if ((attr_id == UMAD_CM_ATTR_REQ) || (attr_id == UMAD_CM_ATTR_DREQ) || - (attr_id == UMAD_CM_ATTR_SIDR_REQ) || - (attr_id == UMAD_CM_ATTR_REP) || (attr_id == UMAD_CM_ATTR_DREP)) { - comm_id = (uint32_t *)(msg.umad.mad + sizeof(*hdr)); - hash_tbl_save_fd_comm_id_pair(fd, *comm_id, - msg.hdr.sgid.global.interface_id); - } - - syslog(LOG_DEBUG, "vm_to_mad: %d 0x%x 0x%x\n", fd, attr_id, - comm_id ? *comm_id : 0); - rc = umad_send(server.umad_agent.port_id, server.umad_agent.agent_id, - &msg.umad, msg.umad_len, 1, 0); - if (rc) { - syslog(LOG_ERR, - "Fail to send MAD message (0x%x) from socket %d, err=%d", - attr_id, fd, rc); - } - break; - - default: - syslog(LOG_ERR, "Got invalid op_code (%d) from socket %d", - msg.hdr.msg_type, fd); - rc = RDMACM_MUX_ERR_CODE_EINVAL; - } - - msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_RESP; - msg.hdr.err_code = rc; - rc = send(fd, &msg, sizeof(msg), 0); - - return rc == sizeof(msg) ? 0 : -EPIPE; -} - -static int accept_all(void) -{ - int fd, rc = 0; - - pthread_rwlock_wrlock(&server.lock); - - do { - if ((server.nfds + 1) > MAX_CLIENTS) { - syslog(LOG_WARNING, "Too many clients (%d)", server.nfds); - rc = -EIO; - goto out; - } - - fd = accept(server.fds[0].fd, NULL, NULL); - if (fd < 0) { - if (errno != EWOULDBLOCK) { - syslog(LOG_WARNING, "accept() failed"); - rc = -EIO; - goto out; - } - break; - } - - syslog(LOG_INFO, "Client connected on socket %d\n", fd); - server.fds[server.nfds].fd = fd; - server.fds[server.nfds].events = POLLIN; - server.nfds++; - } while (fd != -1); - -out: - pthread_rwlock_unlock(&server.lock); - return rc; -} - -static void compress_fds(void) -{ - int i, j; - int closed = 0; - - pthread_rwlock_wrlock(&server.lock); - - for (i = 1; i < server.nfds; i++) { - if (!server.fds[i].fd) { - closed++; - for (j = i; j < server.nfds - 1; j++) { - server.fds[j] = server.fds[j + 1]; - } - } - } - - server.nfds -= closed; - - pthread_rwlock_unlock(&server.lock); -} - -static void close_fd(int idx) -{ - close(server.fds[idx].fd); - syslog(LOG_INFO, "Socket %d closed\n", server.fds[idx].fd); - hash_tbl_remove_fd_ifid_pair(server.fds[idx].fd); - server.fds[idx].fd = 0; -} - -static void run(void) -{ - int rc, nfds, i; - bool compress = false; - - syslog(LOG_INFO, "Service started"); - - while (server.run) { - rc = poll(server.fds, server.nfds, SLEEP_SECS * SCALE_US); - if (rc < 0) { - if (errno != EINTR) { - syslog(LOG_WARNING, "poll() failed"); - } - continue; - } - - if (rc == 0) { - continue; - } - - nfds = server.nfds; - for (i = 0; i < nfds; i++) { - syslog(LOG_DEBUG, "pollfd[%d]: revents 0x%x, events 0x%x\n", i, - server.fds[i].revents, server.fds[i].events); - if (server.fds[i].revents == 0) { - continue; - } - - if (server.fds[i].revents != POLLIN) { - if (i == 0) { - syslog(LOG_NOTICE, "Unexpected poll() event (0x%x)\n", - server.fds[i].revents); - } else { - close_fd(i); - compress = true; - } - continue; - } - - if (i == 0) { - rc = accept_all(); - if (rc) { - continue; - } - } else { - rc = read_and_process(server.fds[i].fd); - if (rc) { - close_fd(i); - compress = true; - } - } - } - - if (compress) { - compress = false; - compress_fds(); - } - } -} - -static void fini_listener(void) -{ - int i; - - if (server.fds[0].fd <= 0) { - return; - } - - for (i = server.nfds - 1; i >= 0; i--) { - if (server.fds[i].fd) { - close(server.fds[i].fd); - } - } - - unlink(server.args.unix_socket_path); -} - -static void fini_umad(void) -{ - if (server.umad_agent.agent_id) { - umad_unregister(server.umad_agent.port_id, server.umad_agent.agent_id); - } - - if (server.umad_agent.port_id) { - umad_close_port(server.umad_agent.port_id); - } - - hash_tbl_free(); -} - -static void fini(void) -{ - if (server.umad_recv_thread) { - pthread_join(server.umad_recv_thread, NULL); - server.umad_recv_thread = 0; - } - fini_umad(); - fini_listener(); - pthread_rwlock_destroy(&server.lock); - - syslog(LOG_INFO, "Service going down"); -} - -static int init_listener(void) -{ - struct sockaddr_un sun; - int rc, on = 1; - - server.fds[0].fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server.fds[0].fd < 0) { - syslog(LOG_ALERT, "socket() failed"); - return -EIO; - } - - rc = setsockopt(server.fds[0].fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, - sizeof(on)); - if (rc < 0) { - syslog(LOG_ALERT, "setsockopt() failed"); - rc = -EIO; - goto err; - } - - rc = ioctl(server.fds[0].fd, FIONBIO, (char *)&on); - if (rc < 0) { - syslog(LOG_ALERT, "ioctl() failed"); - rc = -EIO; - goto err; - } - - if (strlen(server.args.unix_socket_path) >= sizeof(sun.sun_path)) { - syslog(LOG_ALERT, - "Invalid unix_socket_path, size must be less than %ld\n", - sizeof(sun.sun_path)); - rc = -EINVAL; - goto err; - } - - sun.sun_family = AF_UNIX; - rc = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", - server.args.unix_socket_path); - if (rc < 0 || rc >= sizeof(sun.sun_path)) { - syslog(LOG_ALERT, "Could not copy unix socket path\n"); - rc = -EINVAL; - goto err; - } - - rc = bind(server.fds[0].fd, (struct sockaddr *)&sun, sizeof(sun)); - if (rc < 0) { - syslog(LOG_ALERT, "bind() failed"); - rc = -EIO; - goto err; - } - - rc = listen(server.fds[0].fd, SERVER_LISTEN_BACKLOG); - if (rc < 0) { - syslog(LOG_ALERT, "listen() failed"); - rc = -EIO; - goto err; - } - - server.fds[0].events = POLLIN; - server.nfds = 1; - server.run = true; - - return 0; - -err: - close(server.fds[0].fd); - return rc; -} - -static int init_umad(void) -{ - long method_mask[IB_USER_MAD_LONGS_PER_METHOD_MASK]; - - server.umad_agent.port_id = umad_open_port(server.args.rdma_dev_name, - server.args.rdma_port_num); - - if (server.umad_agent.port_id < 0) { - syslog(LOG_WARNING, "umad_open_port() failed"); - return -EIO; - } - - memset(&method_mask, 0, sizeof(method_mask)); - method_mask[0] = MAD_METHOD_MASK0; - server.umad_agent.agent_id = umad_register(server.umad_agent.port_id, - UMAD_CLASS_CM, - UMAD_SA_CLASS_VERSION, - MAD_RMPP_VERSION, method_mask); - if (server.umad_agent.agent_id < 0) { - syslog(LOG_WARNING, "umad_register() failed"); - return -EIO; - } - - hash_tbl_alloc(); - - return 0; -} - -static void signal_handler(int sig, siginfo_t *siginfo, void *context) -{ - static bool warned; - - /* Prevent stop if clients are connected */ - if (server.nfds != 1) { - if (!warned) { - syslog(LOG_WARNING, - "Can't stop while active client exist, resend SIGINT to overid"); - warned = true; - return; - } - } - - if (sig == SIGINT) { - server.run = false; - fini(); - } - - exit(0); -} - -static int init(void) -{ - int rc; - struct sigaction sig = {}; - - rc = init_listener(); - if (rc) { - return rc; - } - - rc = init_umad(); - if (rc) { - return rc; - } - - pthread_rwlock_init(&server.lock, 0); - - rc = pthread_create(&server.umad_recv_thread, NULL, umad_recv_thread_func, - NULL); - if (rc) { - syslog(LOG_ERR, "Fail to create UMAD receiver thread (%d)\n", rc); - return rc; - } - - sig.sa_sigaction = &signal_handler; - sig.sa_flags = SA_SIGINFO; - rc = sigaction(SIGINT, &sig, NULL); - if (rc < 0) { - syslog(LOG_ERR, "Fail to install SIGINT handler (%d)\n", errno); - return rc; - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - int rc; - - memset(&server, 0, sizeof(server)); - - parse_args(argc, argv); - - rc = init(); - if (rc) { - syslog(LOG_ERR, "Fail to initialize server (%d)\n", rc); - rc = -EAGAIN; - goto out; - } - - run(); - -out: - fini(); - - return rc; -} diff --git a/contrib/rdmacm-mux/meson.build b/contrib/rdmacm-mux/meson.build deleted file mode 100644 index 36c9c89630..0000000000 --- a/contrib/rdmacm-mux/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -if have_pvrdma - # FIXME: broken on big endian architectures - executable('rdmacm-mux', files('main.c'), genh, - dependencies: [glib, libumad], - build_by_default: false, - install: false) -endif diff --git a/contrib/rdmacm-mux/rdmacm-mux.h b/contrib/rdmacm-mux/rdmacm-mux.h deleted file mode 100644 index 07a4722913..0000000000 --- a/contrib/rdmacm-mux/rdmacm-mux.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * QEMU paravirtual RDMA - rdmacm-mux declarations - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMACM_MUX_H -#define RDMACM_MUX_H - -#include "linux/if.h" -#include -#include -#include - -typedef enum RdmaCmMuxMsgType { - RDMACM_MUX_MSG_TYPE_REQ = 0, - RDMACM_MUX_MSG_TYPE_RESP = 1, -} RdmaCmMuxMsgType; - -typedef enum RdmaCmMuxOpCode { - RDMACM_MUX_OP_CODE_REG = 0, - RDMACM_MUX_OP_CODE_UNREG = 1, - RDMACM_MUX_OP_CODE_MAD = 2, -} RdmaCmMuxOpCode; - -typedef enum RdmaCmMuxErrCode { - RDMACM_MUX_ERR_CODE_OK = 0, - RDMACM_MUX_ERR_CODE_EINVAL = 1, - RDMACM_MUX_ERR_CODE_EEXIST = 2, - RDMACM_MUX_ERR_CODE_EACCES = 3, - RDMACM_MUX_ERR_CODE_ENOTFOUND = 4, -} RdmaCmMuxErrCode; - -typedef struct RdmaCmMuxHdr { - RdmaCmMuxMsgType msg_type; - RdmaCmMuxOpCode op_code; - union ibv_gid sgid; - RdmaCmMuxErrCode err_code; -} RdmaCmUHdr; - -typedef struct RdmaCmUMad { - struct ib_user_mad hdr; - char mad[RDMA_MAX_PRIVATE_DATA]; -} RdmaCmUMad; - -typedef struct RdmaCmMuxMsg { - RdmaCmUHdr hdr; - int umad_len; - RdmaCmUMad umad; -} RdmaCmMuxMsg; - -#endif diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 06090dd2c2..7b8aafa15b 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -365,15 +365,6 @@ recommending to switch to their stable counterparts: - "Zve64f" should be replaced with "zve64f" - "Zve64d" should be replaced with "zve64d" -``-device pvrdma`` and the rdma subsystem (since 8.2) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The pvrdma device and the whole rdma subsystem are in a bad shape and -without active maintenance. The QEMU project intends to remove this -device and subsystem from the code base in a future release without -replacement unless somebody steps up and improves the situation. - - Block device options '''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 299c844b8b..53ca08aba9 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -925,6 +925,10 @@ contains native support for this feature and thus use of the option ROM approach was obsolete. The native SeaBIOS support can be activated by using ``-machine graphics=off``. +``pvrdma`` and the RDMA subsystem (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +The 'pvrdma' device and the whole RDMA subsystem have been removed. Related binaries ---------------- diff --git a/docs/pvrdma.txt b/docs/pvrdma.txt deleted file mode 100644 index 5c122fe818..0000000000 --- a/docs/pvrdma.txt +++ /dev/null @@ -1,345 +0,0 @@ -Paravirtualized RDMA Device (PVRDMA) -==================================== - - -1. Description -=============== -PVRDMA is the QEMU implementation of VMware's paravirtualized RDMA device. -It works with its Linux Kernel driver AS IS, no need for any special guest -modifications. - -While it complies with the VMware device, it can also communicate with bare -metal RDMA-enabled machines as peers. - -It does not require an RDMA HCA in the host, it can work with Soft-RoCE (rxe). - -It does not require the whole guest RAM to be pinned allowing memory -over-commit and, even if not implemented yet, migration support will be -possible with some HW assistance. - -A project presentation accompany this document: -- https://blog.linuxplumbersconf.org/2017/ocw/system/presentations/4730/original/lpc-2017-pvrdma-marcel-apfelbaum-yuval-shaia.pdf - - - -2. Setup -======== - - -2.1 Guest setup -=============== -Fedora 27+ kernels work out of the box, older distributions -require updating the kernel to 4.14 to include the pvrdma driver. - -However the libpvrdma library needed by User Level Software is still -not available as part of the distributions, so the rdma-core library -needs to be compiled and optionally installed. - -Please follow the instructions at: - https://github.com/linux-rdma/rdma-core.git - - -2.2 Host Setup -============== -The pvrdma backend is an ibdevice interface that can be exposed -either by a Soft-RoCE(rxe) device on machines with no RDMA device, -or an HCA SRIOV function(VF/PF). -Note that ibdevice interfaces can't be shared between pvrdma devices, -each one requiring a separate instance (rxe or SRIOV VF). - - -2.2.1 Soft-RoCE backend(rxe) -=========================== -A stable version of rxe is required, Fedora 27+ or a Linux -Kernel 4.14+ is preferred. - -The rdma_rxe module is part of the Linux Kernel but not loaded by default. -Install the User Level library (librxe) following the instructions from: -https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home - -Associate an ETH interface with rxe by running: - rxe_cfg add eth0 -An rxe0 ibdevice interface will be created and can be used as pvrdma backend. - - -2.2.2 RDMA device Virtual Function backend -========================================== -Nothing special is required, the pvrdma device can work not only with -Ethernet Links, but also Infinibands Links. -All is needed is an ibdevice with an active port, for Mellanox cards -will be something like mlx5_6 which can be the backend. - - -2.2.3 QEMU setup -================ -Configure QEMU with --enable-rdma flag, installing -the required RDMA libraries. - - - -3. Usage -======== - - -3.1 VM Memory settings -====================== -Currently the device is working only with memory backed RAM -and it must be mark as "shared": - -m 1G \ - -object memory-backend-ram,id=mb1,size=1G,share \ - -numa node,memdev=mb1 \ - - -3.2 MAD Multiplexer -=================== -MAD Multiplexer is a service that exposes MAD-like interface for VMs in -order to overcome the limitation where only single entity can register with -MAD layer to send and receive RDMA-CM MAD packets. - -To build rdmacm-mux run -# make rdmacm-mux - -Before running the rdmacm-mux make sure that both ib_cm and rdma_cm kernel -modules aren't loaded, otherwise the rdmacm-mux service will fail to start. - -The application accepts 3 command line arguments and exposes a UNIX socket -to pass control and data to it. --d rdma-device-name Name of RDMA device to register with --s unix-socket-path Path to unix socket to listen (default /var/run/rdmacm-mux) --p rdma-device-port Port number of RDMA device to register with (default 1) -The final UNIX socket file name is a concatenation of the 3 arguments so -for example for device mlx5_0 on port 2 this /var/run/rdmacm-mux-mlx5_0-2 -will be created. - -pvrdma requires this service. - -Please refer to contrib/rdmacm-mux for more details. - - -3.3 Service exposed by libvirt daemon -===================================== -The control over the RDMA device's GID table is done by updating the -device's Ethernet function addresses. -Usually the first GID entry is determined by the MAC address, the second by -the first IPv6 address and the third by the IPv4 address. Other entries can -be added by adding more IP addresses. The opposite is the same, i.e. -whenever an address is removed, the corresponding GID entry is removed. -The process is done by the network and RDMA stacks. Whenever an address is -added the ib_core driver is notified and calls the device driver add_gid -function which in turn update the device. -To support this in pvrdma device the device hooks into the create_bind and -destroy_bind HW commands triggered by pvrdma driver in guest. - -Whenever changed is made to the pvrdma port's GID table a special QMP -messages is sent to be processed by libvirt to update the address of the -backend Ethernet device. - -pvrdma requires that libvirt service will be up. - - -3.4 PCI devices settings -======================== -RoCE device exposes two functions - an Ethernet and RDMA. -To support it, pvrdma device is composed of two PCI functions, an Ethernet -device of type vmxnet3 on PCI slot 0 and a PVRDMA device on PCI slot 1. The -Ethernet function can be used for other Ethernet purposes such as IP. - - -3.5 Device parameters -===================== -- netdev: Specifies the Ethernet device function name on the host for - example enp175s0f0. For Soft-RoCE device (rxe) this would be the Ethernet - device used to create it. -- ibdev: The IB device name on host for example rxe0, mlx5_0 etc. -- mad-chardev: The name of the MAD multiplexer char device. -- ibport: In case of multi-port device (such as Mellanox's HCA) this - specify the port to use. If not set 1 will be used. -- dev-caps-max-mr-size: The maximum size of MR. -- dev-caps-max-qp: Maximum number of QPs. -- dev-caps-max-cq: Maximum number of CQs. -- dev-caps-max-mr: Maximum number of MRs. -- dev-caps-max-pd: Maximum number of PDs. -- dev-caps-max-ah: Maximum number of AHs. - -Notes: -- The first 3 parameters are mandatory settings, the rest have their - defaults. -- The last 8 parameters (the ones that prefixed by dev-caps) defines the top - limits but the final values is adjusted by the backend device limitations. -- netdev can be extracted from ibdev's sysfs - (/sys/class/infiniband//device/net/) - - -3.6 Example -=========== -Define bridge device with vmxnet3 network backend: - - - - -
- - -Define pvrdma device: - - - - - - - - - - - - - -4. Implementation details -========================= - - -4.1 Overview -============ -The device acts like a proxy between the Guest Driver and the host -ibdevice interface. -On configuration path: - - For every hardware resource request (PD/QP/CQ/...) the pvrdma will request - a resource from the backend interface, maintaining a 1-1 mapping - between the guest and host. -On data path: - - Every post_send/receive received from the guest will be converted into - a post_send/receive for the backend. The buffers data will not be touched - or copied resulting in near bare-metal performance for large enough buffers. - - Completions from the backend interface will result in completions for - the pvrdma device. - - -4.2 PCI BARs -============ -PCI Bars: - BAR 0 - MSI-X - MSI-X vectors: - (0) Command - used when execution of a command is completed. - (1) Async - not in use. - (2) Completion - used when a completion event is placed in - device's CQ ring. - BAR 1 - Registers - -------------------------------------------------------- - | VERSION | DSR | CTL | REQ | ERR | ICR | IMR | MAC | - -------------------------------------------------------- - DSR - Address of driver/device shared memory used - for the command channel, used for passing: - - General info such as driver version - - Address of 'command' and 'response' - - Address of async ring - - Address of device's CQ ring - - Device capabilities - CTL - Device control operations (activate, reset etc) - IMG - Set interrupt mask - REQ - Command execution register - ERR - Operation status - - BAR 2 - UAR - --------------------------------------------------------- - | QP_NUM | SEND/RECV Flag || CQ_NUM | ARM/POLL Flag | - --------------------------------------------------------- - - Offset 0 used for QP operations (send and recv) - - Offset 4 used for CQ operations (arm and poll) - - -4.3 Major flows -=============== - -4.3.1 Create CQ -=============== - - Guest driver - - Allocates pages for CQ ring - - Creates page directory (pdir) to hold CQ ring's pages - - Initializes CQ ring - - Initializes 'Create CQ' command object (cqe, pdir etc) - - Copies the command to 'command' address - - Writes 0 into REQ register - - Device - - Reads the request object from the 'command' address - - Allocates CQ object and initialize CQ ring based on pdir - - Creates the backend CQ - - Writes operation status to ERR register - - Posts command-interrupt to guest - - Guest driver - - Reads the HW response code from ERR register - -4.3.2 Create QP -=============== - - Guest driver - - Allocates pages for send and receive rings - - Creates page directory(pdir) to hold the ring's pages - - Initializes 'Create QP' command object (max_send_wr, - send_cq_handle, recv_cq_handle, pdir etc) - - Copies the object to 'command' address - - Write 0 into REQ register - - Device - - Reads the request object from 'command' address - - Allocates the QP object and initialize - - Send and recv rings based on pdir - - Send and recv ring state - - Creates the backend QP - - Writes the operation status to ERR register - - Posts command-interrupt to guest - - Guest driver - - Reads the HW response code from ERR register - -4.3.3 Post receive -================== - - Guest driver - - Initializes a wqe and place it on recv ring - - Write to qpn|qp_recv_bit (31) to QP offset in UAR - - Device - - Extracts qpn from UAR - - Walks through the ring and does the following for each wqe - - Prepares the backend CQE context to be used when - receiving completion from backend (wr_id, op_code, emu_cq_num) - - For each sge prepares backend sge - - Calls backend's post_recv - -4.3.4 Process backend events -============================ - - Done by a dedicated thread used to process backend events; - at initialization is attached to the device and creates - the communication channel. - - Thread main loop: - - Polls for completions - - Extracts QEMU _cq_num, wr_id and op_code from context - - Writes CQE to CQ ring - - Writes CQ number to device CQ - - Sends completion-interrupt to guest - - Deallocates context - - Acks the event to backend - - - -5. Limitations -============== -- The device obviously is limited by the Guest Linux Driver features implementation - of the VMware device API. -- Memory registration mechanism requires mremap for every page in the buffer in order - to map it to a contiguous virtual address range. Since this is not the data path - it should not matter much. If the default max mr size is increased, be aware that - memory registration can take up to 0.5 seconds for 1GB of memory. -- The device requires target page size to be the same as the host page size, - otherwise it will fail to init. -- QEMU cannot map guest RAM from a file descriptor if a pvrdma device is attached, - so it can't work with huge pages. The limitation will be addressed in the future, - however QEMU allocates Guest RAM with MADV_HUGEPAGE so if there are enough huge - pages available, QEMU will use them. QEMU will fail to init if the requirements - are not met. - - - -6. Performance -============== -By design the pvrdma device exits on each post-send/receive, so for small buffers -the performance is affected; however for medium buffers it will became close to -bare metal and from 1MB buffers and up it reaches bare metal performance. -(tested with 2 VMs, the pvrdma devices connected to 2 VFs of the same device) - -All the above assumes no memory registration is done on data path. diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst index c37268b404..06d034b8ef 100644 --- a/docs/system/loongarch/virt.rst +++ b/docs/system/loongarch/virt.rst @@ -39,7 +39,7 @@ can be accessed by following steps. .. code-block:: bash - ./configure --disable-rdma --disable-pvrdma --prefix=/usr \ + ./configure --disable-rdma --prefix=/usr \ --target-list="loongarch64-softmmu" \ --disable-libiscsi --disable-libnfs --disable-libpmem \ --disable-glusterfs --enable-libusb --enable-usb-redir \ diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ad1b1306e3..20a9835ea8 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -182,19 +182,6 @@ SRST Show PIC state. ERST - { - .name = "rdma", - .args_type = "", - .params = "", - .help = "show RDMA state", - .cmd_info_hrt = qmp_x_query_rdma, - }, - -SRST - ``info rdma`` - Show RDMA state. -ERST - { .name = "pci", .args_type = "", diff --git a/hw/Kconfig b/hw/Kconfig index ea6a68b1a1..b1cc40d6be 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -29,7 +29,6 @@ source pci-bridge/Kconfig source pci-host/Kconfig source pcmcia/Kconfig source pci/Kconfig -source rdma/Kconfig source remote/Kconfig source rtc/Kconfig source scsi/Kconfig diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 4b72009cd3..c20829b9ae 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -12,7 +12,6 @@ #include "hw/boards.h" #include "hw/intc/intc.h" #include "hw/mem/memory-device.h" -#include "hw/rdma/rdma.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" @@ -291,37 +290,6 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp) return mem_info; } -static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) -{ - RdmaProvider *rdma; - RdmaProviderClass *k; - GString *buf = opaque; - - if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { - rdma = RDMA_PROVIDER(obj); - k = RDMA_PROVIDER_GET_CLASS(obj); - if (k->format_statistics) { - k->format_statistics(rdma, buf); - } else { - g_string_append_printf(buf, - "RDMA statistics not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -HumanReadableText *qmp_x_query_rdma(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - object_child_foreach_recursive(object_get_root(), - qmp_x_query_rdma_foreach, buf); - - return human_readable_text_from_str(buf); -} - HumanReadableText *qmp_x_query_ramblock(Error **errp) { g_autoptr(GString) buf = ram_block_format(); diff --git a/hw/meson.build b/hw/meson.build index fb998aae0f..1c6308fe95 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -28,7 +28,6 @@ subdir('pci') subdir('pci-bridge') subdir('pci-host') subdir('pcmcia') -subdir('rdma') subdir('rtc') subdir('scsi') subdir('sd') diff --git a/hw/rdma/Kconfig b/hw/rdma/Kconfig deleted file mode 100644 index 840320bdc0..0000000000 --- a/hw/rdma/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -config VMW_PVRDMA - default y if PCI_DEVICES - depends on PVRDMA && MSI_NONBROKEN && VMXNET3_PCI diff --git a/hw/rdma/meson.build b/hw/rdma/meson.build deleted file mode 100644 index 363c9b8c83..0000000000 --- a/hw/rdma/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -system_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( - 'rdma.c', - 'rdma_backend.c', - 'rdma_utils.c', - 'vmw/pvrdma_qp_ops.c', -)) -specific_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( - 'rdma_rm.c', - 'vmw/pvrdma_cmd.c', - 'vmw/pvrdma_dev_ring.c', - 'vmw/pvrdma_main.c', -)) diff --git a/hw/rdma/rdma.c b/hw/rdma/rdma.c deleted file mode 100644 index 7bec0d0d2c..0000000000 --- a/hw/rdma/rdma.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * RDMA device interface - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/rdma/rdma.h" -#include "qemu/module.h" - -static const TypeInfo rdma_hmp_info = { - .name = INTERFACE_RDMA_PROVIDER, - .parent = TYPE_INTERFACE, - .class_size = sizeof(RdmaProviderClass), -}; - -static void rdma_register_types(void) -{ - type_register_static(&rdma_hmp_info); -} - -type_init(rdma_register_types) diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c deleted file mode 100644 index 6dcdfbbbe2..0000000000 --- a/hw/rdma/rdma_backend.c +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * QEMU paravirtual RDMA - Generic RDMA backend - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/qapi-events-rdma.h" - -#include - -#include "contrib/rdmacm-mux/rdmacm-mux.h" -#include "trace.h" -#include "rdma_utils.h" -#include "rdma_rm.h" -#include "rdma_backend.h" - -#define THR_NAME_LEN 16 -#define THR_POLL_TO 5000 - -#define MAD_HDR_SIZE sizeof(struct ibv_grh) - -typedef struct BackendCtx { - void *up_ctx; - struct ibv_sge sge; /* Used to save MAD recv buffer */ - RdmaBackendQP *backend_qp; /* To maintain recv buffers */ - RdmaBackendSRQ *backend_srq; -} BackendCtx; - -struct backend_umad { - struct ib_user_mad hdr; - char mad[RDMA_MAX_PRIVATE_DATA]; -}; - -static void (*comp_handler)(void *ctx, struct ibv_wc *wc); - -static void dummy_comp_handler(void *ctx, struct ibv_wc *wc) -{ - rdma_error_report("No completion handler is registered"); -} - -static inline void complete_work(enum ibv_wc_status status, uint32_t vendor_err, - void *ctx) -{ - struct ibv_wc wc = {}; - - wc.status = status; - wc.vendor_err = vendor_err; - - comp_handler(ctx, &wc); -} - -static void free_cqe_ctx(gpointer data, gpointer user_data) -{ - BackendCtx *bctx; - RdmaDeviceResources *rdma_dev_res = user_data; - unsigned long cqe_ctx_id = GPOINTER_TO_INT(data); - - bctx = rdma_rm_get_cqe_ctx(rdma_dev_res, cqe_ctx_id); - if (bctx) { - rdma_rm_dealloc_cqe_ctx(rdma_dev_res, cqe_ctx_id); - qatomic_dec(&rdma_dev_res->stats.missing_cqe); - } - g_free(bctx); -} - -static void clean_recv_mads(RdmaBackendDev *backend_dev) -{ - unsigned long cqe_ctx_id; - - do { - cqe_ctx_id = rdma_protected_gqueue_pop_int64(&backend_dev-> - recv_mads_list); - if (cqe_ctx_id != -ENOENT) { - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - free_cqe_ctx(GINT_TO_POINTER(cqe_ctx_id), - backend_dev->rdma_dev_res); - } - } while (cqe_ctx_id != -ENOENT); -} - -static int rdma_poll_cq(RdmaDeviceResources *rdma_dev_res, struct ibv_cq *ibcq) -{ - int i, ne, total_ne = 0; - BackendCtx *bctx; - struct ibv_wc wc[2]; - RdmaProtectedGSList *cqe_ctx_list; - - WITH_QEMU_LOCK_GUARD(&rdma_dev_res->lock) { - do { - ne = ibv_poll_cq(ibcq, ARRAY_SIZE(wc), wc); - - trace_rdma_poll_cq(ne, ibcq); - - for (i = 0; i < ne; i++) { - bctx = rdma_rm_get_cqe_ctx(rdma_dev_res, wc[i].wr_id); - if (unlikely(!bctx)) { - rdma_error_report("No matching ctx for req %"PRId64, - wc[i].wr_id); - continue; - } - - comp_handler(bctx->up_ctx, &wc[i]); - - if (bctx->backend_qp) { - cqe_ctx_list = &bctx->backend_qp->cqe_ctx_list; - } else { - cqe_ctx_list = &bctx->backend_srq->cqe_ctx_list; - } - - rdma_protected_gslist_remove_int32(cqe_ctx_list, wc[i].wr_id); - rdma_rm_dealloc_cqe_ctx(rdma_dev_res, wc[i].wr_id); - g_free(bctx); - } - total_ne += ne; - } while (ne > 0); - qatomic_sub(&rdma_dev_res->stats.missing_cqe, total_ne); - } - - if (ne < 0) { - rdma_error_report("ibv_poll_cq fail, rc=%d, errno=%d", ne, errno); - } - - rdma_dev_res->stats.completions += total_ne; - - return total_ne; -} - -static void *comp_handler_thread(void *arg) -{ - RdmaBackendDev *backend_dev = (RdmaBackendDev *)arg; - int rc; - struct ibv_cq *ev_cq; - void *ev_ctx; - int flags; - GPollFD pfds[1]; - - /* Change to non-blocking mode */ - flags = fcntl(backend_dev->channel->fd, F_GETFL); - rc = fcntl(backend_dev->channel->fd, F_SETFL, flags | O_NONBLOCK); - if (rc < 0) { - rdma_error_report("Failed to change backend channel FD to non-blocking"); - return NULL; - } - - pfds[0].fd = backend_dev->channel->fd; - pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; - - backend_dev->comp_thread.is_running = true; - - while (backend_dev->comp_thread.run) { - do { - rc = qemu_poll_ns(pfds, 1, THR_POLL_TO * (int64_t)SCALE_MS); - if (!rc) { - backend_dev->rdma_dev_res->stats.poll_cq_ppoll_to++; - } - } while (!rc && backend_dev->comp_thread.run); - - if (backend_dev->comp_thread.run) { - rc = ibv_get_cq_event(backend_dev->channel, &ev_cq, &ev_ctx); - if (unlikely(rc)) { - rdma_error_report("ibv_get_cq_event fail, rc=%d, errno=%d", rc, - errno); - continue; - } - - rc = ibv_req_notify_cq(ev_cq, 0); - if (unlikely(rc)) { - rdma_error_report("ibv_req_notify_cq fail, rc=%d, errno=%d", rc, - errno); - } - - backend_dev->rdma_dev_res->stats.poll_cq_from_bk++; - rdma_poll_cq(backend_dev->rdma_dev_res, ev_cq); - - ibv_ack_cq_events(ev_cq, 1); - } - } - - backend_dev->comp_thread.is_running = false; - - qemu_thread_exit(0); - - return NULL; -} - -static inline void disable_rdmacm_mux_async(RdmaBackendDev *backend_dev) -{ - qatomic_set(&backend_dev->rdmacm_mux.can_receive, 0); -} - -static inline void enable_rdmacm_mux_async(RdmaBackendDev *backend_dev) -{ - qatomic_set(&backend_dev->rdmacm_mux.can_receive, sizeof(RdmaCmMuxMsg)); -} - -static inline int rdmacm_mux_can_process_async(RdmaBackendDev *backend_dev) -{ - return qatomic_read(&backend_dev->rdmacm_mux.can_receive); -} - -static int rdmacm_mux_check_op_status(CharBackend *mad_chr_be) -{ - RdmaCmMuxMsg msg = {}; - int ret; - - ret = qemu_chr_fe_read_all(mad_chr_be, (uint8_t *)&msg, sizeof(msg)); - if (ret != sizeof(msg)) { - rdma_error_report("Got invalid message from mux: size %d, expecting %d", - ret, (int)sizeof(msg)); - return -EIO; - } - - trace_rdmacm_mux_check_op_status(msg.hdr.msg_type, msg.hdr.op_code, - msg.hdr.err_code); - - if (msg.hdr.msg_type != RDMACM_MUX_MSG_TYPE_RESP) { - rdma_error_report("Got invalid message type %d", msg.hdr.msg_type); - return -EIO; - } - - if (msg.hdr.err_code != RDMACM_MUX_ERR_CODE_OK) { - rdma_error_report("Operation failed in mux, error code %d", - msg.hdr.err_code); - return -EIO; - } - - return 0; -} - -static int rdmacm_mux_send(RdmaBackendDev *backend_dev, RdmaCmMuxMsg *msg) -{ - int rc = 0; - - msg->hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ; - trace_rdmacm_mux("send", msg->hdr.msg_type, msg->hdr.op_code); - disable_rdmacm_mux_async(backend_dev); - rc = qemu_chr_fe_write(backend_dev->rdmacm_mux.chr_be, - (const uint8_t *)msg, sizeof(*msg)); - if (rc != sizeof(*msg)) { - enable_rdmacm_mux_async(backend_dev); - rdma_error_report("Failed to send request to rdmacm_mux (rc=%d)", rc); - return -EIO; - } - - rc = rdmacm_mux_check_op_status(backend_dev->rdmacm_mux.chr_be); - if (rc) { - rdma_error_report("Failed to execute rdmacm_mux request %d (rc=%d)", - msg->hdr.op_code, rc); - } - - enable_rdmacm_mux_async(backend_dev); - - return 0; -} - -static void stop_backend_thread(RdmaBackendThread *thread) -{ - thread->run = false; - while (thread->is_running) { - sleep(THR_POLL_TO / SCALE_US / 2); - } -} - -static void start_comp_thread(RdmaBackendDev *backend_dev) -{ - char thread_name[THR_NAME_LEN] = {}; - - stop_backend_thread(&backend_dev->comp_thread); - - snprintf(thread_name, sizeof(thread_name), "rdma_comp_%s", - ibv_get_device_name(backend_dev->ib_dev)); - backend_dev->comp_thread.run = true; - qemu_thread_create(&backend_dev->comp_thread.thread, thread_name, - comp_handler_thread, backend_dev, QEMU_THREAD_DETACHED); -} - -void rdma_backend_register_comp_handler(void (*handler)(void *ctx, - struct ibv_wc *wc)) -{ - comp_handler = handler; -} - -void rdma_backend_unregister_comp_handler(void) -{ - rdma_backend_register_comp_handler(dummy_comp_handler); -} - -int rdma_backend_query_port(RdmaBackendDev *backend_dev, - struct ibv_port_attr *port_attr) -{ - int rc; - - rc = ibv_query_port(backend_dev->context, backend_dev->port_num, port_attr); - if (rc) { - rdma_error_report("ibv_query_port fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq) -{ - int polled; - - rdma_dev_res->stats.poll_cq_from_guest++; - polled = rdma_poll_cq(rdma_dev_res, cq->ibcq); - if (!polled) { - rdma_dev_res->stats.poll_cq_from_guest_empty++; - } -} - -static GHashTable *ah_hash; - -static struct ibv_ah *create_ah(RdmaBackendDev *backend_dev, struct ibv_pd *pd, - uint8_t sgid_idx, union ibv_gid *dgid) -{ - GBytes *ah_key = g_bytes_new(dgid, sizeof(*dgid)); - struct ibv_ah *ah = g_hash_table_lookup(ah_hash, ah_key); - - if (ah) { - trace_rdma_create_ah_cache_hit(be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); - g_bytes_unref(ah_key); - } else { - struct ibv_ah_attr ah_attr = { - .is_global = 1, - .port_num = backend_dev->port_num, - .grh.hop_limit = 1, - }; - - ah_attr.grh.dgid = *dgid; - ah_attr.grh.sgid_index = sgid_idx; - - ah = ibv_create_ah(pd, &ah_attr); - if (ah) { - g_hash_table_insert(ah_hash, ah_key, ah); - } else { - g_bytes_unref(ah_key); - rdma_error_report("Failed to create AH for gid <0x%" PRIx64", 0x%"PRIx64">", - be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); - } - - trace_rdma_create_ah_cache_miss(be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); - } - - return ah; -} - -static void destroy_ah_hash_key(gpointer data) -{ - g_bytes_unref(data); -} - -static void destroy_ah_hast_data(gpointer data) -{ - struct ibv_ah *ah = data; - - ibv_destroy_ah(ah); -} - -static void ah_cache_init(void) -{ - ah_hash = g_hash_table_new_full(g_bytes_hash, g_bytes_equal, - destroy_ah_hash_key, destroy_ah_hast_data); -} - -#ifdef LEGACY_RDMA_REG_MR -static int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, - struct ibv_sge *sge, uint8_t num_sge, - uint64_t *total_length) -{ - RdmaRmMR *mr; - int idx; - - for (idx = 0; idx < num_sge; idx++) { - mr = rdma_rm_get_mr(rdma_dev_res, sge[idx].lkey); - if (unlikely(!mr)) { - rdma_error_report("Invalid lkey 0x%x", sge[idx].lkey); - return VENDOR_ERR_INVLKEY | sge[idx].lkey; - } - - sge[idx].addr = (uintptr_t)mr->virt + sge[idx].addr - mr->start; - sge[idx].lkey = rdma_backend_mr_lkey(&mr->backend_mr); - - *total_length += sge[idx].length; - } - - return 0; -} -#else -static inline int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, - struct ibv_sge *sge, uint8_t num_sge, - uint64_t *total_length) -{ - int idx; - - for (idx = 0; idx < num_sge; idx++) { - *total_length += sge[idx].length; - } - return 0; -} -#endif - -static void trace_mad_message(const char *title, char *buf, int len) -{ - int i; - char *b = g_malloc0(len * 3 + 1); - char b1[4]; - - for (i = 0; i < len; i++) { - sprintf(b1, "%.2X ", buf[i] & 0x000000FF); - strcat(b, b1); - } - - trace_rdma_mad_message(title, len, b); - - g_free(b); -} - -static int mad_send(RdmaBackendDev *backend_dev, uint8_t sgid_idx, - union ibv_gid *sgid, struct ibv_sge *sge, uint32_t num_sge) -{ - RdmaCmMuxMsg msg = {}; - char *hdr, *data; - int ret; - - if (num_sge != 2) { - return -EINVAL; - } - - msg.hdr.op_code = RDMACM_MUX_OP_CODE_MAD; - memcpy(msg.hdr.sgid.raw, sgid->raw, sizeof(msg.hdr.sgid)); - - msg.umad_len = sge[0].length + sge[1].length; - - if (msg.umad_len > sizeof(msg.umad.mad)) { - return -ENOMEM; - } - - msg.umad.hdr.addr.qpn = htobe32(1); - msg.umad.hdr.addr.grh_present = 1; - msg.umad.hdr.addr.gid_index = sgid_idx; - memcpy(msg.umad.hdr.addr.gid, sgid->raw, sizeof(msg.umad.hdr.addr.gid)); - msg.umad.hdr.addr.hop_limit = 0xFF; - - hdr = rdma_pci_dma_map(backend_dev->dev, sge[0].addr, sge[0].length); - if (!hdr) { - return -ENOMEM; - } - data = rdma_pci_dma_map(backend_dev->dev, sge[1].addr, sge[1].length); - if (!data) { - rdma_pci_dma_unmap(backend_dev->dev, hdr, sge[0].length); - return -ENOMEM; - } - - memcpy(&msg.umad.mad[0], hdr, sge[0].length); - memcpy(&msg.umad.mad[sge[0].length], data, sge[1].length); - - rdma_pci_dma_unmap(backend_dev->dev, data, sge[1].length); - rdma_pci_dma_unmap(backend_dev->dev, hdr, sge[0].length); - - trace_mad_message("send", msg.umad.mad, msg.umad_len); - - ret = rdmacm_mux_send(backend_dev, &msg); - if (ret) { - rdma_error_report("Failed to send MAD to rdma_umadmux (%d)", ret); - return -EIO; - } - - return 0; -} - -void rdma_backend_post_send(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, - uint8_t sgid_idx, union ibv_gid *sgid, - union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey, - void *ctx) -{ - BackendCtx *bctx; - uint32_t bctx_id; - int rc; - struct ibv_send_wr wr = {}, *bad_wr; - - if (!qp->ibqp) { /* This field is not initialized for QP0 and QP1 */ - if (qp_type == IBV_QPT_SMI) { - rdma_error_report("Got QP0 request"); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); - } else if (qp_type == IBV_QPT_GSI) { - rc = mad_send(backend_dev, sgid_idx, sgid, sge, num_sge); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_MAD_SEND, ctx); - backend_dev->rdma_dev_res->stats.mad_tx_err++; - } else { - complete_work(IBV_WC_SUCCESS, 0, ctx); - backend_dev->rdma_dev_res->stats.mad_tx++; - } - } - return; - } - - bctx = g_malloc0(sizeof(*bctx)); - bctx->up_ctx = ctx; - bctx->backend_qp = qp; - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto err_free_bctx; - } - - rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); - - rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, - &backend_dev->rdma_dev_res->stats.tx_len); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - goto err_dealloc_cqe_ctx; - } - - if (qp_type == IBV_QPT_UD) { - wr.wr.ud.ah = create_ah(backend_dev, qp->ibpd, sgid_idx, dgid); - if (!wr.wr.ud.ah) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - wr.wr.ud.remote_qpn = dqpn; - wr.wr.ud.remote_qkey = dqkey; - } - - wr.num_sge = num_sge; - wr.opcode = IBV_WR_SEND; - wr.send_flags = IBV_SEND_SIGNALED; - wr.sg_list = sge; - wr.wr_id = bctx_id; - - rc = ibv_post_send(qp->ibqp, &wr, &bad_wr); - if (rc) { - rdma_error_report("ibv_post_send fail, qpn=0x%x, rc=%d, errno=%d", - qp->ibqp->qp_num, rc, errno); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - backend_dev->rdma_dev_res->stats.tx++; - - return; - -err_dealloc_cqe_ctx: - backend_dev->rdma_dev_res->stats.tx_err++; - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); - -err_free_bctx: - g_free(bctx); -} - -static unsigned int save_mad_recv_buffer(RdmaBackendDev *backend_dev, - struct ibv_sge *sge, uint32_t num_sge, - void *ctx) -{ - BackendCtx *bctx; - int rc; - uint32_t bctx_id; - - if (num_sge != 1) { - rdma_error_report("Invalid num_sge (%d), expecting 1", num_sge); - return VENDOR_ERR_INV_NUM_SGE; - } - - if (sge[0].length < RDMA_MAX_PRIVATE_DATA + sizeof(struct ibv_grh)) { - rdma_error_report("Too small buffer for MAD"); - return VENDOR_ERR_INV_MAD_BUFF; - } - - bctx = g_malloc0(sizeof(*bctx)); - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - g_free(bctx); - return VENDOR_ERR_NOMEM; - } - - bctx->up_ctx = ctx; - bctx->sge = *sge; - - rdma_protected_gqueue_append_int64(&backend_dev->recv_mads_list, bctx_id); - - return 0; -} - -void rdma_backend_post_recv(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, void *ctx) -{ - BackendCtx *bctx; - uint32_t bctx_id; - int rc; - struct ibv_recv_wr wr = {}, *bad_wr; - - if (!qp->ibqp) { /* This field does not get initialized for QP0 and QP1 */ - if (qp_type == IBV_QPT_SMI) { - rdma_error_report("Got QP0 request"); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); - } - if (qp_type == IBV_QPT_GSI) { - rc = save_mad_recv_buffer(backend_dev, sge, num_sge, ctx); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - backend_dev->rdma_dev_res->stats.mad_rx_bufs_err++; - } else { - backend_dev->rdma_dev_res->stats.mad_rx_bufs++; - } - } - return; - } - - bctx = g_malloc0(sizeof(*bctx)); - bctx->up_ctx = ctx; - bctx->backend_qp = qp; - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto err_free_bctx; - } - - rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); - - rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, - &backend_dev->rdma_dev_res->stats.rx_bufs_len); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - goto err_dealloc_cqe_ctx; - } - - wr.num_sge = num_sge; - wr.sg_list = sge; - wr.wr_id = bctx_id; - rc = ibv_post_recv(qp->ibqp, &wr, &bad_wr); - if (rc) { - rdma_error_report("ibv_post_recv fail, qpn=0x%x, rc=%d, errno=%d", - qp->ibqp->qp_num, rc, errno); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - backend_dev->rdma_dev_res->stats.rx_bufs++; - - return; - -err_dealloc_cqe_ctx: - backend_dev->rdma_dev_res->stats.rx_bufs_err++; - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); - -err_free_bctx: - g_free(bctx); -} - -void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, - RdmaBackendSRQ *srq, struct ibv_sge *sge, - uint32_t num_sge, void *ctx) -{ - BackendCtx *bctx; - uint32_t bctx_id; - int rc; - struct ibv_recv_wr wr = {}, *bad_wr; - - bctx = g_malloc0(sizeof(*bctx)); - bctx->up_ctx = ctx; - bctx->backend_srq = srq; - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto err_free_bctx; - } - - rdma_protected_gslist_append_int32(&srq->cqe_ctx_list, bctx_id); - - rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, - &backend_dev->rdma_dev_res->stats.rx_bufs_len); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - goto err_dealloc_cqe_ctx; - } - - wr.num_sge = num_sge; - wr.sg_list = sge; - wr.wr_id = bctx_id; - rc = ibv_post_srq_recv(srq->ibsrq, &wr, &bad_wr); - if (rc) { - rdma_error_report("ibv_post_srq_recv fail, srqn=0x%x, rc=%d, errno=%d", - srq->ibsrq->handle, rc, errno); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - backend_dev->rdma_dev_res->stats.rx_bufs++; - backend_dev->rdma_dev_res->stats.rx_srq++; - - return; - -err_dealloc_cqe_ctx: - backend_dev->rdma_dev_res->stats.rx_bufs_err++; - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); - -err_free_bctx: - g_free(bctx); -} - -int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd) -{ - pd->ibpd = ibv_alloc_pd(backend_dev->context); - - if (!pd->ibpd) { - rdma_error_report("ibv_alloc_pd fail, errno=%d", errno); - return -EIO; - } - - return 0; -} - -void rdma_backend_destroy_pd(RdmaBackendPD *pd) -{ - if (pd->ibpd) { - ibv_dealloc_pd(pd->ibpd); - } -} - -int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, - size_t length, uint64_t guest_start, int access) -{ -#ifdef LEGACY_RDMA_REG_MR - mr->ibmr = ibv_reg_mr(pd->ibpd, addr, length, access); -#else - mr->ibmr = ibv_reg_mr_iova(pd->ibpd, addr, length, guest_start, access); -#endif - if (!mr->ibmr) { - rdma_error_report("ibv_reg_mr fail, errno=%d", errno); - return -EIO; - } - - mr->ibpd = pd->ibpd; - - return 0; -} - -void rdma_backend_destroy_mr(RdmaBackendMR *mr) -{ - if (mr->ibmr) { - ibv_dereg_mr(mr->ibmr); - } -} - -int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq, - int cqe) -{ - int rc; - - cq->ibcq = ibv_create_cq(backend_dev->context, cqe + 1, NULL, - backend_dev->channel, 0); - if (!cq->ibcq) { - rdma_error_report("ibv_create_cq fail, errno=%d", errno); - return -EIO; - } - - rc = ibv_req_notify_cq(cq->ibcq, 0); - if (rc) { - rdma_warn_report("ibv_req_notify_cq fail, rc=%d, errno=%d", rc, errno); - } - - cq->backend_dev = backend_dev; - - return 0; -} - -void rdma_backend_destroy_cq(RdmaBackendCQ *cq) -{ - if (cq->ibcq) { - ibv_destroy_cq(cq->ibcq); - } -} - -int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, - RdmaBackendPD *pd, RdmaBackendCQ *scq, - RdmaBackendCQ *rcq, RdmaBackendSRQ *srq, - uint32_t max_send_wr, uint32_t max_recv_wr, - uint32_t max_send_sge, uint32_t max_recv_sge) -{ - struct ibv_qp_init_attr attr = {}; - - qp->ibqp = 0; - - switch (qp_type) { - case IBV_QPT_GSI: - return 0; - - case IBV_QPT_RC: - /* fall through */ - case IBV_QPT_UD: - /* do nothing */ - break; - - default: - rdma_error_report("Unsupported QP type %d", qp_type); - return -EIO; - } - - attr.qp_type = qp_type; - attr.send_cq = scq->ibcq; - attr.recv_cq = rcq->ibcq; - attr.cap.max_send_wr = max_send_wr; - attr.cap.max_recv_wr = max_recv_wr; - attr.cap.max_send_sge = max_send_sge; - attr.cap.max_recv_sge = max_recv_sge; - if (srq) { - attr.srq = srq->ibsrq; - } - - qp->ibqp = ibv_create_qp(pd->ibpd, &attr); - if (!qp->ibqp) { - rdma_error_report("ibv_create_qp fail, errno=%d", errno); - return -EIO; - } - - rdma_protected_gslist_init(&qp->cqe_ctx_list); - - qp->ibpd = pd->ibpd; - - /* TODO: Query QP to get max_inline_data and save it to be used in send */ - - return 0; -} - -int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint32_t qkey) -{ - struct ibv_qp_attr attr = {}; - int rc, attr_mask; - - attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT; - attr.qp_state = IBV_QPS_INIT; - attr.pkey_index = 0; - attr.port_num = backend_dev->port_num; - - switch (qp_type) { - case IBV_QPT_RC: - attr_mask |= IBV_QP_ACCESS_FLAGS; - trace_rdma_backend_rc_qp_state_init(qp->ibqp->qp_num); - break; - - case IBV_QPT_UD: - attr.qkey = qkey; - attr_mask |= IBV_QP_QKEY; - trace_rdma_backend_ud_qp_state_init(qp->ibqp->qp_num, qkey); - break; - - default: - rdma_error_report("Unsupported QP type %d", qp_type); - return -EIO; - } - - rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); - if (rc) { - rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - uint32_t rq_psn, uint32_t qkey, bool use_qkey) -{ - struct ibv_qp_attr attr = {}; - union ibv_gid ibv_gid = { - .global.interface_id = dgid->global.interface_id, - .global.subnet_prefix = dgid->global.subnet_prefix - }; - int rc, attr_mask; - - attr.qp_state = IBV_QPS_RTR; - attr_mask = IBV_QP_STATE; - - qp->sgid_idx = sgid_idx; - - switch (qp_type) { - case IBV_QPT_RC: - attr.path_mtu = IBV_MTU_1024; - attr.dest_qp_num = dqpn; - attr.max_dest_rd_atomic = 1; - attr.min_rnr_timer = 12; - attr.ah_attr.port_num = backend_dev->port_num; - attr.ah_attr.is_global = 1; - attr.ah_attr.grh.hop_limit = 1; - attr.ah_attr.grh.dgid = ibv_gid; - attr.ah_attr.grh.sgid_index = qp->sgid_idx; - attr.rq_psn = rq_psn; - - attr_mask |= IBV_QP_AV | IBV_QP_PATH_MTU | IBV_QP_DEST_QPN | - IBV_QP_RQ_PSN | IBV_QP_MAX_DEST_RD_ATOMIC | - IBV_QP_MIN_RNR_TIMER; - - trace_rdma_backend_rc_qp_state_rtr(qp->ibqp->qp_num, - be64_to_cpu(ibv_gid.global. - subnet_prefix), - be64_to_cpu(ibv_gid.global. - interface_id), - qp->sgid_idx, dqpn, rq_psn); - break; - - case IBV_QPT_UD: - if (use_qkey) { - attr.qkey = qkey; - attr_mask |= IBV_QP_QKEY; - } - trace_rdma_backend_ud_qp_state_rtr(qp->ibqp->qp_num, use_qkey ? qkey : - 0); - break; - } - - rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); - if (rc) { - rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, - uint32_t sq_psn, uint32_t qkey, bool use_qkey) -{ - struct ibv_qp_attr attr = {}; - int rc, attr_mask; - - attr.qp_state = IBV_QPS_RTS; - attr.sq_psn = sq_psn; - attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN; - - switch (qp_type) { - case IBV_QPT_RC: - attr.timeout = 14; - attr.retry_cnt = 7; - attr.rnr_retry = 7; - attr.max_rd_atomic = 1; - - attr_mask |= IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | - IBV_QP_MAX_QP_RD_ATOMIC; - trace_rdma_backend_rc_qp_state_rts(qp->ibqp->qp_num, sq_psn); - break; - - case IBV_QPT_UD: - if (use_qkey) { - attr.qkey = qkey; - attr_mask |= IBV_QP_QKEY; - } - trace_rdma_backend_ud_qp_state_rts(qp->ibqp->qp_num, sq_psn, - use_qkey ? qkey : 0); - break; - } - - rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); - if (rc) { - rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr) -{ - if (!qp->ibqp) { - attr->qp_state = IBV_QPS_RTS; - return 0; - } - - return ibv_query_qp(qp->ibqp, attr, attr_mask, init_attr); -} - -void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res) -{ - if (qp->ibqp) { - ibv_destroy_qp(qp->ibqp); - } - g_slist_foreach(qp->cqe_ctx_list.list, free_cqe_ctx, dev_res); - rdma_protected_gslist_destroy(&qp->cqe_ctx_list); -} - -int rdma_backend_create_srq(RdmaBackendSRQ *srq, RdmaBackendPD *pd, - uint32_t max_wr, uint32_t max_sge, - uint32_t srq_limit) -{ - struct ibv_srq_init_attr srq_init_attr = {}; - - srq_init_attr.attr.max_wr = max_wr; - srq_init_attr.attr.max_sge = max_sge; - srq_init_attr.attr.srq_limit = srq_limit; - - srq->ibsrq = ibv_create_srq(pd->ibpd, &srq_init_attr); - if (!srq->ibsrq) { - rdma_error_report("ibv_create_srq failed, errno=%d", errno); - return -EIO; - } - - rdma_protected_gslist_init(&srq->cqe_ctx_list); - - return 0; -} - -int rdma_backend_query_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr) -{ - if (!srq->ibsrq) { - return -EINVAL; - } - - return ibv_query_srq(srq->ibsrq, srq_attr); -} - -int rdma_backend_modify_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr, - int srq_attr_mask) -{ - if (!srq->ibsrq) { - return -EINVAL; - } - - return ibv_modify_srq(srq->ibsrq, srq_attr, srq_attr_mask); -} - -void rdma_backend_destroy_srq(RdmaBackendSRQ *srq, RdmaDeviceResources *dev_res) -{ - if (srq->ibsrq) { - ibv_destroy_srq(srq->ibsrq); - } - g_slist_foreach(srq->cqe_ctx_list.list, free_cqe_ctx, dev_res); - rdma_protected_gslist_destroy(&srq->cqe_ctx_list); -} - -#define CHK_ATTR(req, dev, member, fmt) ({ \ - trace_rdma_check_dev_attr(#member, dev.member, req->member); \ - if (req->member > dev.member) { \ - rdma_warn_report("%s = "fmt" is higher than host device capability "fmt, \ - #member, req->member, dev.member); \ - req->member = dev.member; \ - } \ -}) - -static int init_device_caps(RdmaBackendDev *backend_dev, - struct ibv_device_attr *dev_attr) -{ - struct ibv_device_attr bk_dev_attr; - int rc; - - rc = ibv_query_device(backend_dev->context, &bk_dev_attr); - if (rc) { - rdma_error_report("ibv_query_device fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - dev_attr->max_sge = MAX_SGE; - dev_attr->max_srq_sge = MAX_SGE; - - CHK_ATTR(dev_attr, bk_dev_attr, max_mr_size, "%" PRId64); - CHK_ATTR(dev_attr, bk_dev_attr, max_qp, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_sge, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_cq, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_mr, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_pd, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_qp_rd_atom, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_qp_init_rd_atom, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_ah, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_srq, "%d"); - - return 0; -} - -static inline void build_mad_hdr(struct ibv_grh *grh, union ibv_gid *sgid, - union ibv_gid *my_gid, int paylen) -{ - grh->paylen = htons(paylen); - grh->sgid = *sgid; - grh->dgid = *my_gid; -} - -static void process_incoming_mad_req(RdmaBackendDev *backend_dev, - RdmaCmMuxMsg *msg) -{ - unsigned long cqe_ctx_id; - BackendCtx *bctx; - char *mad; - - trace_mad_message("recv", msg->umad.mad, msg->umad_len); - - cqe_ctx_id = rdma_protected_gqueue_pop_int64(&backend_dev->recv_mads_list); - if (cqe_ctx_id == -ENOENT) { - rdma_warn_report("No more free MADs buffers, waiting for a while"); - sleep(THR_POLL_TO); - return; - } - - bctx = rdma_rm_get_cqe_ctx(backend_dev->rdma_dev_res, cqe_ctx_id); - if (unlikely(!bctx)) { - rdma_error_report("No matching ctx for req %ld", cqe_ctx_id); - backend_dev->rdma_dev_res->stats.mad_rx_err++; - return; - } - - mad = rdma_pci_dma_map(backend_dev->dev, bctx->sge.addr, - bctx->sge.length); - if (!mad || bctx->sge.length < msg->umad_len + MAD_HDR_SIZE) { - backend_dev->rdma_dev_res->stats.mad_rx_err++; - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_INV_MAD_BUFF, - bctx->up_ctx); - } else { - struct ibv_wc wc = {}; - memset(mad, 0, bctx->sge.length); - build_mad_hdr((struct ibv_grh *)mad, - (union ibv_gid *)&msg->umad.hdr.addr.gid, &msg->hdr.sgid, - msg->umad_len); - memcpy(&mad[MAD_HDR_SIZE], msg->umad.mad, msg->umad_len); - rdma_pci_dma_unmap(backend_dev->dev, mad, bctx->sge.length); - - wc.byte_len = msg->umad_len; - wc.status = IBV_WC_SUCCESS; - wc.wc_flags = IBV_WC_GRH; - backend_dev->rdma_dev_res->stats.mad_rx++; - comp_handler(bctx->up_ctx, &wc); - } - - g_free(bctx); - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, cqe_ctx_id); -} - -static inline int rdmacm_mux_can_receive(void *opaque) -{ - RdmaBackendDev *backend_dev = (RdmaBackendDev *)opaque; - - return rdmacm_mux_can_process_async(backend_dev); -} - -static void rdmacm_mux_read(void *opaque, const uint8_t *buf, int size) -{ - RdmaBackendDev *backend_dev = (RdmaBackendDev *)opaque; - RdmaCmMuxMsg *msg = (RdmaCmMuxMsg *)buf; - - trace_rdmacm_mux("read", msg->hdr.msg_type, msg->hdr.op_code); - - if (msg->hdr.msg_type != RDMACM_MUX_MSG_TYPE_REQ && - msg->hdr.op_code != RDMACM_MUX_OP_CODE_MAD) { - rdma_error_report("Error: Not a MAD request, skipping"); - return; - } - process_incoming_mad_req(backend_dev, msg); -} - -static int mad_init(RdmaBackendDev *backend_dev, CharBackend *mad_chr_be) -{ - int ret; - - backend_dev->rdmacm_mux.chr_be = mad_chr_be; - - ret = qemu_chr_fe_backend_connected(backend_dev->rdmacm_mux.chr_be); - if (!ret) { - rdma_error_report("Missing chardev for MAD multiplexer"); - return -EIO; - } - - rdma_protected_gqueue_init(&backend_dev->recv_mads_list); - - enable_rdmacm_mux_async(backend_dev); - - qemu_chr_fe_set_handlers(backend_dev->rdmacm_mux.chr_be, - rdmacm_mux_can_receive, rdmacm_mux_read, NULL, - NULL, backend_dev, NULL, true); - - return 0; -} - -static void mad_stop(RdmaBackendDev *backend_dev) -{ - clean_recv_mads(backend_dev); -} - -static void mad_fini(RdmaBackendDev *backend_dev) -{ - disable_rdmacm_mux_async(backend_dev); - qemu_chr_fe_disconnect(backend_dev->rdmacm_mux.chr_be); - rdma_protected_gqueue_destroy(&backend_dev->recv_mads_list); -} - -int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev, - union ibv_gid *gid) -{ - union ibv_gid sgid; - int ret; - int i = 0; - - do { - ret = ibv_query_gid(backend_dev->context, backend_dev->port_num, i, - &sgid); - i++; - } while (!ret && (memcmp(&sgid, gid, sizeof(*gid)))); - - trace_rdma_backend_get_gid_index(be64_to_cpu(gid->global.subnet_prefix), - be64_to_cpu(gid->global.interface_id), - i - 1); - - return ret ? ret : i - 1; -} - -int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid) -{ - RdmaCmMuxMsg msg = {}; - int ret; - - trace_rdma_backend_gid_change("add", be64_to_cpu(gid->global.subnet_prefix), - be64_to_cpu(gid->global.interface_id)); - - msg.hdr.op_code = RDMACM_MUX_OP_CODE_REG; - memcpy(msg.hdr.sgid.raw, gid->raw, sizeof(msg.hdr.sgid)); - - ret = rdmacm_mux_send(backend_dev, &msg); - if (ret) { - rdma_error_report("Failed to register GID to rdma_umadmux (%d)", ret); - return -EIO; - } - - qapi_event_send_rdma_gid_status_changed(ifname, true, - gid->global.subnet_prefix, - gid->global.interface_id); - - return ret; -} - -int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid) -{ - RdmaCmMuxMsg msg = {}; - int ret; - - trace_rdma_backend_gid_change("del", be64_to_cpu(gid->global.subnet_prefix), - be64_to_cpu(gid->global.interface_id)); - - msg.hdr.op_code = RDMACM_MUX_OP_CODE_UNREG; - memcpy(msg.hdr.sgid.raw, gid->raw, sizeof(msg.hdr.sgid)); - - ret = rdmacm_mux_send(backend_dev, &msg); - if (ret) { - rdma_error_report("Failed to unregister GID from rdma_umadmux (%d)", - ret); - return -EIO; - } - - qapi_event_send_rdma_gid_status_changed(ifname, false, - gid->global.subnet_prefix, - gid->global.interface_id); - - return 0; -} - -int rdma_backend_init(RdmaBackendDev *backend_dev, PCIDevice *pdev, - RdmaDeviceResources *rdma_dev_res, - const char *backend_device_name, uint8_t port_num, - struct ibv_device_attr *dev_attr, CharBackend *mad_chr_be) -{ - int i; - int ret = 0; - int num_ibv_devices; - struct ibv_device **dev_list; - - memset(backend_dev, 0, sizeof(*backend_dev)); - - backend_dev->dev = pdev; - backend_dev->port_num = port_num; - backend_dev->rdma_dev_res = rdma_dev_res; - - rdma_backend_register_comp_handler(dummy_comp_handler); - - dev_list = ibv_get_device_list(&num_ibv_devices); - if (!dev_list) { - rdma_error_report("Failed to get IB devices list"); - return -EIO; - } - - if (num_ibv_devices == 0) { - rdma_error_report("No IB devices were found"); - ret = -ENXIO; - goto out_free_dev_list; - } - - if (backend_device_name) { - for (i = 0; dev_list[i]; ++i) { - if (!strcmp(ibv_get_device_name(dev_list[i]), - backend_device_name)) { - break; - } - } - - backend_dev->ib_dev = dev_list[i]; - if (!backend_dev->ib_dev) { - rdma_error_report("Failed to find IB device %s", - backend_device_name); - ret = -EIO; - goto out_free_dev_list; - } - } else { - backend_dev->ib_dev = *dev_list; - } - - rdma_info_report("uverb device %s", backend_dev->ib_dev->dev_name); - - backend_dev->context = ibv_open_device(backend_dev->ib_dev); - if (!backend_dev->context) { - rdma_error_report("Failed to open IB device %s", - ibv_get_device_name(backend_dev->ib_dev)); - ret = -EIO; - goto out; - } - - backend_dev->channel = ibv_create_comp_channel(backend_dev->context); - if (!backend_dev->channel) { - rdma_error_report("Failed to create IB communication channel"); - ret = -EIO; - goto out_close_device; - } - - ret = init_device_caps(backend_dev, dev_attr); - if (ret) { - rdma_error_report("Failed to initialize device capabilities"); - ret = -EIO; - goto out_destroy_comm_channel; - } - - - ret = mad_init(backend_dev, mad_chr_be); - if (ret) { - rdma_error_report("Failed to initialize mad"); - ret = -EIO; - goto out_destroy_comm_channel; - } - - backend_dev->comp_thread.run = false; - backend_dev->comp_thread.is_running = false; - - ah_cache_init(); - - goto out_free_dev_list; - -out_destroy_comm_channel: - ibv_destroy_comp_channel(backend_dev->channel); - -out_close_device: - ibv_close_device(backend_dev->context); - -out_free_dev_list: - ibv_free_device_list(dev_list); - -out: - return ret; -} - - -void rdma_backend_start(RdmaBackendDev *backend_dev) -{ - start_comp_thread(backend_dev); -} - -void rdma_backend_stop(RdmaBackendDev *backend_dev) -{ - mad_stop(backend_dev); - stop_backend_thread(&backend_dev->comp_thread); -} - -void rdma_backend_fini(RdmaBackendDev *backend_dev) -{ - mad_fini(backend_dev); - g_hash_table_destroy(ah_hash); - ibv_destroy_comp_channel(backend_dev->channel); - ibv_close_device(backend_dev->context); -} diff --git a/hw/rdma/rdma_backend.h b/hw/rdma/rdma_backend.h deleted file mode 100644 index 225af481e0..0000000000 --- a/hw/rdma/rdma_backend.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * RDMA device: Definitions of Backend Device functions - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMA_BACKEND_H -#define RDMA_BACKEND_H - -#include "qapi/error.h" -#include "chardev/char-fe.h" - -#include "rdma_rm_defs.h" -#include "rdma_backend_defs.h" - -/* Vendor Errors */ -#define VENDOR_ERR_FAIL_BACKEND 0x201 -#define VENDOR_ERR_TOO_MANY_SGES 0x202 -#define VENDOR_ERR_NOMEM 0x203 -#define VENDOR_ERR_QP0 0x204 -#define VENDOR_ERR_INV_NUM_SGE 0x205 -#define VENDOR_ERR_MAD_SEND 0x206 -#define VENDOR_ERR_INVLKEY 0x207 -#define VENDOR_ERR_MR_SMALL 0x208 -#define VENDOR_ERR_INV_MAD_BUFF 0x209 -#define VENDOR_ERR_INV_GID_IDX 0x210 - -/* Add definition for QP0 and QP1 as there is no userspace enums for them */ -enum ibv_special_qp_type { - IBV_QPT_SMI = 0, - IBV_QPT_GSI = 1, -}; - -static inline uint32_t rdma_backend_qpn(const RdmaBackendQP *qp) -{ - return qp->ibqp ? qp->ibqp->qp_num : 1; -} - -static inline uint32_t rdma_backend_mr_lkey(const RdmaBackendMR *mr) -{ - return mr->ibmr ? mr->ibmr->lkey : 0; -} - -static inline uint32_t rdma_backend_mr_rkey(const RdmaBackendMR *mr) -{ - return mr->ibmr ? mr->ibmr->rkey : 0; -} - -int rdma_backend_init(RdmaBackendDev *backend_dev, PCIDevice *pdev, - RdmaDeviceResources *rdma_dev_res, - const char *backend_device_name, uint8_t port_num, - struct ibv_device_attr *dev_attr, - CharBackend *mad_chr_be); -void rdma_backend_fini(RdmaBackendDev *backend_dev); -int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid); -int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid); -int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev, - union ibv_gid *gid); -void rdma_backend_start(RdmaBackendDev *backend_dev); -void rdma_backend_stop(RdmaBackendDev *backend_dev); -void rdma_backend_register_comp_handler(void (*handler)(void *ctx, - struct ibv_wc *wc)); -void rdma_backend_unregister_comp_handler(void); - -int rdma_backend_query_port(RdmaBackendDev *backend_dev, - struct ibv_port_attr *port_attr); -int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd); -void rdma_backend_destroy_pd(RdmaBackendPD *pd); - -int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, - size_t length, uint64_t guest_start, int access); -void rdma_backend_destroy_mr(RdmaBackendMR *mr); - -int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq, - int cqe); -void rdma_backend_destroy_cq(RdmaBackendCQ *cq); -void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq); - -int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, - RdmaBackendPD *pd, RdmaBackendCQ *scq, - RdmaBackendCQ *rcq, RdmaBackendSRQ *srq, - uint32_t max_send_wr, uint32_t max_recv_wr, - uint32_t max_send_sge, uint32_t max_recv_sge); -int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint32_t qkey); -int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - uint32_t rq_psn, uint32_t qkey, bool use_qkey); -int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, - uint32_t sq_psn, uint32_t qkey, bool use_qkey); -int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr); -void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res); - -void rdma_backend_post_send(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, - uint8_t sgid_idx, union ibv_gid *sgid, - union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey, - void *ctx); -void rdma_backend_post_recv(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, void *ctx); - -int rdma_backend_create_srq(RdmaBackendSRQ *srq, RdmaBackendPD *pd, - uint32_t max_wr, uint32_t max_sge, - uint32_t srq_limit); -int rdma_backend_query_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr); -int rdma_backend_modify_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr, - int srq_attr_mask); -void rdma_backend_destroy_srq(RdmaBackendSRQ *srq, - RdmaDeviceResources *dev_res); -void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, - RdmaBackendSRQ *srq, struct ibv_sge *sge, - uint32_t num_sge, void *ctx); - -#endif diff --git a/hw/rdma/rdma_backend_defs.h b/hw/rdma/rdma_backend_defs.h deleted file mode 100644 index 4e6c0ad695..0000000000 --- a/hw/rdma/rdma_backend_defs.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * RDMA device: Definitions of Backend Device structures - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMA_BACKEND_DEFS_H -#define RDMA_BACKEND_DEFS_H - -#include "qemu/thread.h" -#include "chardev/char-fe.h" -#include -#include "contrib/rdmacm-mux/rdmacm-mux.h" -#include "rdma_utils.h" - -typedef struct RdmaDeviceResources RdmaDeviceResources; - -typedef struct RdmaBackendThread { - QemuThread thread; - bool run; /* Set by thread manager to let thread know it should exit */ - bool is_running; /* Set by the thread to report its status */ -} RdmaBackendThread; - -typedef struct RdmaCmMux { - CharBackend *chr_be; - int can_receive; -} RdmaCmMux; - -typedef struct RdmaBackendDev { - RdmaBackendThread comp_thread; - PCIDevice *dev; - RdmaDeviceResources *rdma_dev_res; - struct ibv_device *ib_dev; - struct ibv_context *context; - struct ibv_comp_channel *channel; - uint8_t port_num; - RdmaProtectedGQueue recv_mads_list; - RdmaCmMux rdmacm_mux; -} RdmaBackendDev; - -typedef struct RdmaBackendPD { - struct ibv_pd *ibpd; -} RdmaBackendPD; - -typedef struct RdmaBackendMR { - struct ibv_pd *ibpd; - struct ibv_mr *ibmr; -} RdmaBackendMR; - -typedef struct RdmaBackendCQ { - RdmaBackendDev *backend_dev; - struct ibv_cq *ibcq; -} RdmaBackendCQ; - -typedef struct RdmaBackendQP { - struct ibv_pd *ibpd; - struct ibv_qp *ibqp; - uint8_t sgid_idx; - RdmaProtectedGSList cqe_ctx_list; -} RdmaBackendQP; - -typedef struct RdmaBackendSRQ { - struct ibv_srq *ibsrq; - RdmaProtectedGSList cqe_ctx_list; -} RdmaBackendSRQ; - -#endif diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c deleted file mode 100644 index 038d564433..0000000000 --- a/hw/rdma/rdma_rm.c +++ /dev/null @@ -1,812 +0,0 @@ -/* - * QEMU paravirtual RDMA - Resource Manager Implementation - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "cpu.h" -#include "monitor/monitor.h" - -#include "trace.h" -#include "rdma_utils.h" -#include "rdma_backend.h" -#include "rdma_rm.h" - -void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf) -{ - g_string_append_printf(buf, "\ttx : %" PRId64 "\n", - dev_res->stats.tx); - g_string_append_printf(buf, "\ttx_len : %" PRId64 "\n", - dev_res->stats.tx_len); - g_string_append_printf(buf, "\ttx_err : %" PRId64 "\n", - dev_res->stats.tx_err); - g_string_append_printf(buf, "\trx_bufs : %" PRId64 "\n", - dev_res->stats.rx_bufs); - g_string_append_printf(buf, "\trx_srq : %" PRId64 "\n", - dev_res->stats.rx_srq); - g_string_append_printf(buf, "\trx_bufs_len : %" PRId64 "\n", - dev_res->stats.rx_bufs_len); - g_string_append_printf(buf, "\trx_bufs_err : %" PRId64 "\n", - dev_res->stats.rx_bufs_err); - g_string_append_printf(buf, "\tcomps : %" PRId64 "\n", - dev_res->stats.completions); - g_string_append_printf(buf, "\tmissing_comps : %" PRId32 "\n", - dev_res->stats.missing_cqe); - g_string_append_printf(buf, "\tpoll_cq (bk) : %" PRId64 "\n", - dev_res->stats.poll_cq_from_bk); - g_string_append_printf(buf, "\tpoll_cq_ppoll_to : %" PRId64 "\n", - dev_res->stats.poll_cq_ppoll_to); - g_string_append_printf(buf, "\tpoll_cq (fe) : %" PRId64 "\n", - dev_res->stats.poll_cq_from_guest); - g_string_append_printf(buf, "\tpoll_cq_empty : %" PRId64 "\n", - dev_res->stats.poll_cq_from_guest_empty); - g_string_append_printf(buf, "\tmad_tx : %" PRId64 "\n", - dev_res->stats.mad_tx); - g_string_append_printf(buf, "\tmad_tx_err : %" PRId64 "\n", - dev_res->stats.mad_tx_err); - g_string_append_printf(buf, "\tmad_rx : %" PRId64 "\n", - dev_res->stats.mad_rx); - g_string_append_printf(buf, "\tmad_rx_err : %" PRId64 "\n", - dev_res->stats.mad_rx_err); - g_string_append_printf(buf, "\tmad_rx_bufs : %" PRId64 "\n", - dev_res->stats.mad_rx_bufs); - g_string_append_printf(buf, "\tmad_rx_bufs_err : %" PRId64 "\n", - dev_res->stats.mad_rx_bufs_err); - g_string_append_printf(buf, "\tPDs : %" PRId32 "\n", - dev_res->pd_tbl.used); - g_string_append_printf(buf, "\tMRs : %" PRId32 "\n", - dev_res->mr_tbl.used); - g_string_append_printf(buf, "\tUCs : %" PRId32 "\n", - dev_res->uc_tbl.used); - g_string_append_printf(buf, "\tQPs : %" PRId32 "\n", - dev_res->qp_tbl.used); - g_string_append_printf(buf, "\tCQs : %" PRId32 "\n", - dev_res->cq_tbl.used); - g_string_append_printf(buf, "\tCEQ_CTXs : %" PRId32 "\n", - dev_res->cqe_ctx_tbl.used); -} - -static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl, - uint32_t tbl_sz, uint32_t res_sz) -{ - tbl->tbl = g_malloc(tbl_sz * res_sz); - - strncpy(tbl->name, name, MAX_RM_TBL_NAME); - tbl->name[MAX_RM_TBL_NAME - 1] = 0; - - tbl->bitmap = bitmap_new(tbl_sz); - tbl->tbl_sz = tbl_sz; - tbl->res_sz = res_sz; - tbl->used = 0; - qemu_mutex_init(&tbl->lock); -} - -static inline void res_tbl_free(RdmaRmResTbl *tbl) -{ - if (!tbl->bitmap) { - return; - } - qemu_mutex_destroy(&tbl->lock); - g_free(tbl->tbl); - g_free(tbl->bitmap); -} - -static inline void *rdma_res_tbl_get(RdmaRmResTbl *tbl, uint32_t handle) -{ - trace_rdma_res_tbl_get(tbl->name, handle); - - if ((handle < tbl->tbl_sz) && (test_bit(handle, tbl->bitmap))) { - return tbl->tbl + handle * tbl->res_sz; - } else { - rdma_error_report("Table %s, invalid handle %d", tbl->name, handle); - return NULL; - } -} - -static inline void *rdma_res_tbl_alloc(RdmaRmResTbl *tbl, uint32_t *handle) -{ - qemu_mutex_lock(&tbl->lock); - - *handle = find_first_zero_bit(tbl->bitmap, tbl->tbl_sz); - if (*handle > tbl->tbl_sz) { - rdma_error_report("Table %s, failed to allocate, bitmap is full", - tbl->name); - qemu_mutex_unlock(&tbl->lock); - return NULL; - } - - set_bit(*handle, tbl->bitmap); - - tbl->used++; - - qemu_mutex_unlock(&tbl->lock); - - memset(tbl->tbl + *handle * tbl->res_sz, 0, tbl->res_sz); - - trace_rdma_res_tbl_alloc(tbl->name, *handle); - - return tbl->tbl + *handle * tbl->res_sz; -} - -static inline void rdma_res_tbl_dealloc(RdmaRmResTbl *tbl, uint32_t handle) -{ - trace_rdma_res_tbl_dealloc(tbl->name, handle); - - QEMU_LOCK_GUARD(&tbl->lock); - - if (handle < tbl->tbl_sz) { - clear_bit(handle, tbl->bitmap); - tbl->used--; - } - -} - -int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t *pd_handle, uint32_t ctx_handle) -{ - RdmaRmPD *pd; - int ret = -ENOMEM; - - pd = rdma_res_tbl_alloc(&dev_res->pd_tbl, pd_handle); - if (!pd) { - goto out; - } - - ret = rdma_backend_create_pd(backend_dev, &pd->backend_pd); - if (ret) { - ret = -EIO; - goto out_tbl_dealloc; - } - - pd->ctx_handle = ctx_handle; - - return 0; - -out_tbl_dealloc: - rdma_res_tbl_dealloc(&dev_res->pd_tbl, *pd_handle); - -out: - return ret; -} - -RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) -{ - return rdma_res_tbl_get(&dev_res->pd_tbl, pd_handle); -} - -void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) -{ - RdmaRmPD *pd = rdma_rm_get_pd(dev_res, pd_handle); - - if (pd) { - rdma_backend_destroy_pd(&pd->backend_pd); - rdma_res_tbl_dealloc(&dev_res->pd_tbl, pd_handle); - } -} - -int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint64_t guest_start, uint64_t guest_length, - void *host_virt, int access_flags, uint32_t *mr_handle, - uint32_t *lkey, uint32_t *rkey) -{ - RdmaRmMR *mr; - int ret = 0; - RdmaRmPD *pd; - - pd = rdma_rm_get_pd(dev_res, pd_handle); - if (!pd) { - return -EINVAL; - } - - mr = rdma_res_tbl_alloc(&dev_res->mr_tbl, mr_handle); - if (!mr) { - return -ENOMEM; - } - trace_rdma_rm_alloc_mr(*mr_handle, host_virt, guest_start, guest_length, - access_flags); - - if (host_virt) { - mr->virt = host_virt; - mr->start = guest_start; - mr->length = guest_length; - mr->virt += (mr->start & (TARGET_PAGE_SIZE - 1)); - - ret = rdma_backend_create_mr(&mr->backend_mr, &pd->backend_pd, mr->virt, - mr->length, guest_start, access_flags); - if (ret) { - ret = -EIO; - goto out_dealloc_mr; - } -#ifdef LEGACY_RDMA_REG_MR - /* We keep mr_handle in lkey so send and recv get get mr ptr */ - *lkey = *mr_handle; -#else - *lkey = rdma_backend_mr_lkey(&mr->backend_mr); -#endif - } - - *rkey = -1; - - mr->pd_handle = pd_handle; - - return 0; - -out_dealloc_mr: - rdma_res_tbl_dealloc(&dev_res->mr_tbl, *mr_handle); - - return ret; -} - -RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) -{ - return rdma_res_tbl_get(&dev_res->mr_tbl, mr_handle); -} - -void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) -{ - RdmaRmMR *mr = rdma_rm_get_mr(dev_res, mr_handle); - - if (mr) { - rdma_backend_destroy_mr(&mr->backend_mr); - trace_rdma_rm_dealloc_mr(mr_handle, mr->start); - if (mr->start) { - mr->virt -= (mr->start & (TARGET_PAGE_SIZE - 1)); - munmap(mr->virt, mr->length); - } - rdma_res_tbl_dealloc(&dev_res->mr_tbl, mr_handle); - } -} - -int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn, - uint32_t *uc_handle) -{ - RdmaRmUC *uc; - - /* TODO: Need to make sure pfn is between bar start address and - * bsd+RDMA_BAR2_UAR_SIZE - if (pfn > RDMA_BAR2_UAR_SIZE) { - rdma_error_report("pfn out of range (%d > %d)", pfn, - RDMA_BAR2_UAR_SIZE); - return -ENOMEM; - } - */ - - uc = rdma_res_tbl_alloc(&dev_res->uc_tbl, uc_handle); - if (!uc) { - return -ENOMEM; - } - - return 0; -} - -RdmaRmUC *rdma_rm_get_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) -{ - return rdma_res_tbl_get(&dev_res->uc_tbl, uc_handle); -} - -void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) -{ - RdmaRmUC *uc = rdma_rm_get_uc(dev_res, uc_handle); - - if (uc) { - rdma_res_tbl_dealloc(&dev_res->uc_tbl, uc_handle); - } -} - -RdmaRmCQ *rdma_rm_get_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) -{ - return rdma_res_tbl_get(&dev_res->cq_tbl, cq_handle); -} - -int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t cqe, uint32_t *cq_handle, void *opaque) -{ - int rc; - RdmaRmCQ *cq; - - cq = rdma_res_tbl_alloc(&dev_res->cq_tbl, cq_handle); - if (!cq) { - return -ENOMEM; - } - - cq->opaque = opaque; - cq->notify = CNT_CLEAR; - - rc = rdma_backend_create_cq(backend_dev, &cq->backend_cq, cqe); - if (rc) { - rc = -EIO; - goto out_dealloc_cq; - } - - return 0; - -out_dealloc_cq: - rdma_rm_dealloc_cq(dev_res, *cq_handle); - - return rc; -} - -void rdma_rm_req_notify_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle, - bool notify) -{ - RdmaRmCQ *cq; - - cq = rdma_rm_get_cq(dev_res, cq_handle); - if (!cq) { - return; - } - - if (cq->notify != CNT_SET) { - cq->notify = notify ? CNT_ARM : CNT_CLEAR; - } -} - -void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) -{ - RdmaRmCQ *cq; - - cq = rdma_rm_get_cq(dev_res, cq_handle); - if (!cq) { - return; - } - - rdma_backend_destroy_cq(&cq->backend_cq); - - rdma_res_tbl_dealloc(&dev_res->cq_tbl, cq_handle); -} - -RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn) -{ - GBytes *key = g_bytes_new(&qpn, sizeof(qpn)); - - RdmaRmQP *qp = g_hash_table_lookup(dev_res->qp_hash, key); - - g_bytes_unref(key); - - if (!qp) { - rdma_error_report("Invalid QP handle %d", qpn); - } - - return qp; -} - -int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint8_t qp_type, uint32_t max_send_wr, - uint32_t max_send_sge, uint32_t send_cq_handle, - uint32_t max_recv_wr, uint32_t max_recv_sge, - uint32_t recv_cq_handle, void *opaque, uint32_t *qpn, - uint8_t is_srq, uint32_t srq_handle) -{ - int rc; - RdmaRmQP *qp; - RdmaRmCQ *scq, *rcq; - RdmaRmPD *pd; - RdmaRmSRQ *srq = NULL; - uint32_t rm_qpn; - - pd = rdma_rm_get_pd(dev_res, pd_handle); - if (!pd) { - return -EINVAL; - } - - scq = rdma_rm_get_cq(dev_res, send_cq_handle); - rcq = rdma_rm_get_cq(dev_res, recv_cq_handle); - - if (!scq || !rcq) { - rdma_error_report("Invalid send_cqn or recv_cqn (%d, %d)", - send_cq_handle, recv_cq_handle); - return -EINVAL; - } - - if (is_srq) { - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - rdma_error_report("Invalid srqn %d", srq_handle); - return -EINVAL; - } - - srq->recv_cq_handle = recv_cq_handle; - } - - if (qp_type == IBV_QPT_GSI) { - scq->notify = CNT_SET; - rcq->notify = CNT_SET; - } - - qp = rdma_res_tbl_alloc(&dev_res->qp_tbl, &rm_qpn); - if (!qp) { - return -ENOMEM; - } - - qp->qpn = rm_qpn; - qp->qp_state = IBV_QPS_RESET; - qp->qp_type = qp_type; - qp->send_cq_handle = send_cq_handle; - qp->recv_cq_handle = recv_cq_handle; - qp->opaque = opaque; - qp->is_srq = is_srq; - - rc = rdma_backend_create_qp(&qp->backend_qp, qp_type, &pd->backend_pd, - &scq->backend_cq, &rcq->backend_cq, - is_srq ? &srq->backend_srq : NULL, - max_send_wr, max_recv_wr, max_send_sge, - max_recv_sge); - - if (rc) { - rc = -EIO; - goto out_dealloc_qp; - } - - *qpn = rdma_backend_qpn(&qp->backend_qp); - trace_rdma_rm_alloc_qp(rm_qpn, *qpn, qp_type); - g_hash_table_insert(dev_res->qp_hash, g_bytes_new(qpn, sizeof(*qpn)), qp); - - return 0; - -out_dealloc_qp: - rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); - - return rc; -} - -int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - enum ibv_qp_state qp_state, uint32_t qkey, - uint32_t rq_psn, uint32_t sq_psn) -{ - RdmaRmQP *qp; - int ret; - - qp = rdma_rm_get_qp(dev_res, qp_handle); - if (!qp) { - return -EINVAL; - } - - if (qp->qp_type == IBV_QPT_SMI) { - rdma_error_report("Got QP0 request"); - return -EPERM; - } else if (qp->qp_type == IBV_QPT_GSI) { - return 0; - } - - trace_rdma_rm_modify_qp(qp_handle, attr_mask, qp_state, sgid_idx); - - if (attr_mask & IBV_QP_STATE) { - qp->qp_state = qp_state; - - if (qp->qp_state == IBV_QPS_INIT) { - ret = rdma_backend_qp_state_init(backend_dev, &qp->backend_qp, - qp->qp_type, qkey); - if (ret) { - return -EIO; - } - } - - if (qp->qp_state == IBV_QPS_RTR) { - /* Get backend gid index */ - sgid_idx = rdma_rm_get_backend_gid_index(dev_res, backend_dev, - sgid_idx); - if (sgid_idx <= 0) { /* TODO check also less than bk.max_sgid */ - rdma_error_report("Failed to get bk sgid_idx for sgid_idx %d", - sgid_idx); - return -EIO; - } - - ret = rdma_backend_qp_state_rtr(backend_dev, &qp->backend_qp, - qp->qp_type, sgid_idx, dgid, dqpn, - rq_psn, qkey, - attr_mask & IBV_QP_QKEY); - if (ret) { - return -EIO; - } - } - - if (qp->qp_state == IBV_QPS_RTS) { - ret = rdma_backend_qp_state_rts(&qp->backend_qp, qp->qp_type, - sq_psn, qkey, - attr_mask & IBV_QP_QKEY); - if (ret) { - return -EIO; - } - } - } - - return 0; -} - -int rdma_rm_query_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr) -{ - RdmaRmQP *qp; - - qp = rdma_rm_get_qp(dev_res, qp_handle); - if (!qp) { - return -EINVAL; - } - - return rdma_backend_query_qp(&qp->backend_qp, attr, attr_mask, init_attr); -} - -void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle) -{ - RdmaRmQP *qp; - GBytes *key; - - key = g_bytes_new(&qp_handle, sizeof(qp_handle)); - qp = g_hash_table_lookup(dev_res->qp_hash, key); - g_hash_table_remove(dev_res->qp_hash, key); - g_bytes_unref(key); - - if (!qp) { - return; - } - - rdma_backend_destroy_qp(&qp->backend_qp, dev_res); - - rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); -} - -RdmaRmSRQ *rdma_rm_get_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle) -{ - return rdma_res_tbl_get(&dev_res->srq_tbl, srq_handle); -} - -int rdma_rm_alloc_srq(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint32_t max_wr, uint32_t max_sge, uint32_t srq_limit, - uint32_t *srq_handle, void *opaque) -{ - RdmaRmSRQ *srq; - RdmaRmPD *pd; - int rc; - - pd = rdma_rm_get_pd(dev_res, pd_handle); - if (!pd) { - return -EINVAL; - } - - srq = rdma_res_tbl_alloc(&dev_res->srq_tbl, srq_handle); - if (!srq) { - return -ENOMEM; - } - - rc = rdma_backend_create_srq(&srq->backend_srq, &pd->backend_pd, - max_wr, max_sge, srq_limit); - if (rc) { - rc = -EIO; - goto out_dealloc_srq; - } - - srq->opaque = opaque; - - return 0; - -out_dealloc_srq: - rdma_res_tbl_dealloc(&dev_res->srq_tbl, *srq_handle); - - return rc; -} - -int rdma_rm_query_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr) -{ - RdmaRmSRQ *srq; - - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - return -EINVAL; - } - - return rdma_backend_query_srq(&srq->backend_srq, srq_attr); -} - -int rdma_rm_modify_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr, int srq_attr_mask) -{ - RdmaRmSRQ *srq; - - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - return -EINVAL; - } - - if ((srq_attr_mask & IBV_SRQ_LIMIT) && - (srq_attr->srq_limit == 0)) { - return -EINVAL; - } - - if ((srq_attr_mask & IBV_SRQ_MAX_WR) && - (srq_attr->max_wr == 0)) { - return -EINVAL; - } - - return rdma_backend_modify_srq(&srq->backend_srq, srq_attr, - srq_attr_mask); -} - -void rdma_rm_dealloc_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle) -{ - RdmaRmSRQ *srq; - - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - return; - } - - rdma_backend_destroy_srq(&srq->backend_srq, dev_res); - rdma_res_tbl_dealloc(&dev_res->srq_tbl, srq_handle); -} - -void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) -{ - void **cqe_ctx; - - cqe_ctx = rdma_res_tbl_get(&dev_res->cqe_ctx_tbl, cqe_ctx_id); - if (!cqe_ctx) { - return NULL; - } - - return *cqe_ctx; -} - -int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, - void *ctx) -{ - void **cqe_ctx; - - cqe_ctx = rdma_res_tbl_alloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); - if (!cqe_ctx) { - return -ENOMEM; - } - - *cqe_ctx = ctx; - - return 0; -} - -void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) -{ - rdma_res_tbl_dealloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); -} - -int rdma_rm_add_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, union ibv_gid *gid, int gid_idx) -{ - int rc; - - rc = rdma_backend_add_gid(backend_dev, ifname, gid); - if (rc) { - return -EINVAL; - } - - memcpy(&dev_res->port.gid_tbl[gid_idx].gid, gid, sizeof(*gid)); - - return 0; -} - -int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, int gid_idx) -{ - int rc; - - if (!dev_res->port.gid_tbl[gid_idx].gid.global.interface_id) { - return 0; - } - - rc = rdma_backend_del_gid(backend_dev, ifname, - &dev_res->port.gid_tbl[gid_idx].gid); - if (rc) { - return -EINVAL; - } - - memset(dev_res->port.gid_tbl[gid_idx].gid.raw, 0, - sizeof(dev_res->port.gid_tbl[gid_idx].gid)); - dev_res->port.gid_tbl[gid_idx].backend_gid_index = -1; - - return 0; -} - -int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res, - RdmaBackendDev *backend_dev, int sgid_idx) -{ - if (unlikely(sgid_idx < 0 || sgid_idx >= MAX_PORT_GIDS)) { - rdma_error_report("Got invalid sgid_idx %d", sgid_idx); - return -EINVAL; - } - - if (unlikely(dev_res->port.gid_tbl[sgid_idx].backend_gid_index == -1)) { - dev_res->port.gid_tbl[sgid_idx].backend_gid_index = - rdma_backend_get_gid_index(backend_dev, - &dev_res->port.gid_tbl[sgid_idx].gid); - } - - return dev_res->port.gid_tbl[sgid_idx].backend_gid_index; -} - -static void destroy_qp_hash_key(gpointer data) -{ - g_bytes_unref(data); -} - -static void init_ports(RdmaDeviceResources *dev_res) -{ - int i; - - memset(&dev_res->port, 0, sizeof(dev_res->port)); - - dev_res->port.state = IBV_PORT_DOWN; - for (i = 0; i < MAX_PORT_GIDS; i++) { - dev_res->port.gid_tbl[i].backend_gid_index = -1; - } -} - -static void fini_ports(RdmaDeviceResources *dev_res, - RdmaBackendDev *backend_dev, const char *ifname) -{ - int i; - - dev_res->port.state = IBV_PORT_DOWN; - for (i = 0; i < MAX_PORT_GIDS; i++) { - rdma_rm_del_gid(dev_res, backend_dev, ifname, i); - } -} - -int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr) -{ - dev_res->qp_hash = g_hash_table_new_full(g_bytes_hash, g_bytes_equal, - destroy_qp_hash_key, NULL); - if (!dev_res->qp_hash) { - return -ENOMEM; - } - - res_tbl_init("PD", &dev_res->pd_tbl, dev_attr->max_pd, sizeof(RdmaRmPD)); - res_tbl_init("CQ", &dev_res->cq_tbl, dev_attr->max_cq, sizeof(RdmaRmCQ)); - res_tbl_init("MR", &dev_res->mr_tbl, dev_attr->max_mr, sizeof(RdmaRmMR)); - res_tbl_init("QP", &dev_res->qp_tbl, dev_attr->max_qp, sizeof(RdmaRmQP)); - res_tbl_init("CQE_CTX", &dev_res->cqe_ctx_tbl, dev_attr->max_qp * - dev_attr->max_qp_wr, sizeof(void *)); - res_tbl_init("UC", &dev_res->uc_tbl, MAX_UCS, sizeof(RdmaRmUC)); - res_tbl_init("SRQ", &dev_res->srq_tbl, dev_attr->max_srq, - sizeof(RdmaRmSRQ)); - - init_ports(dev_res); - - qemu_mutex_init(&dev_res->lock); - - memset(&dev_res->stats, 0, sizeof(dev_res->stats)); - qatomic_set(&dev_res->stats.missing_cqe, 0); - - return 0; -} - -void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname) -{ - qemu_mutex_destroy(&dev_res->lock); - - fini_ports(dev_res, backend_dev, ifname); - - res_tbl_free(&dev_res->srq_tbl); - res_tbl_free(&dev_res->uc_tbl); - res_tbl_free(&dev_res->cqe_ctx_tbl); - res_tbl_free(&dev_res->qp_tbl); - res_tbl_free(&dev_res->mr_tbl); - res_tbl_free(&dev_res->cq_tbl); - res_tbl_free(&dev_res->pd_tbl); - - if (dev_res->qp_hash) { - g_hash_table_destroy(dev_res->qp_hash); - } -} diff --git a/hw/rdma/rdma_rm.h b/hw/rdma/rdma_rm.h deleted file mode 100644 index d69a917795..0000000000 --- a/hw/rdma/rdma_rm.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * RDMA device: Definitions of Resource Manager functions - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMA_RM_H -#define RDMA_RM_H - -#include "qapi/error.h" -#include "rdma_backend_defs.h" -#include "rdma_rm_defs.h" - -int rdma_rm_init(RdmaDeviceResources *dev_res, - struct ibv_device_attr *dev_attr); -void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname); - -int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t *pd_handle, uint32_t ctx_handle); -RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle); -void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle); - -int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint64_t guest_start, uint64_t guest_length, - void *host_virt, int access_flags, uint32_t *mr_handle, - uint32_t *lkey, uint32_t *rkey); -RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle); -void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle); - -int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn, - uint32_t *uc_handle); -RdmaRmUC *rdma_rm_get_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle); -void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle); - -int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t cqe, uint32_t *cq_handle, void *opaque); -RdmaRmCQ *rdma_rm_get_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle); -void rdma_rm_req_notify_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle, - bool notify); -void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle); - -int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint8_t qp_type, uint32_t max_send_wr, - uint32_t max_send_sge, uint32_t send_cq_handle, - uint32_t max_recv_wr, uint32_t max_recv_sge, - uint32_t recv_cq_handle, void *opaque, uint32_t *qpn, - uint8_t is_srq, uint32_t srq_handle); -RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn); -int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - enum ibv_qp_state qp_state, uint32_t qkey, - uint32_t rq_psn, uint32_t sq_psn); -int rdma_rm_query_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr); -void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle); - -RdmaRmSRQ *rdma_rm_get_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle); -int rdma_rm_alloc_srq(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint32_t max_wr, uint32_t max_sge, uint32_t srq_limit, - uint32_t *srq_handle, void *opaque); -int rdma_rm_query_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr); -int rdma_rm_modify_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr, int srq_attr_mask); -void rdma_rm_dealloc_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle); - -int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, - void *ctx); -void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); -void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); - -int rdma_rm_add_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, union ibv_gid *gid, int gid_idx); -int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, int gid_idx); -int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res, - RdmaBackendDev *backend_dev, int sgid_idx); -static inline union ibv_gid *rdma_rm_get_gid(RdmaDeviceResources *dev_res, - int sgid_idx) -{ - return &dev_res->port.gid_tbl[sgid_idx].gid; -} -void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf); - -#endif diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h deleted file mode 100644 index 534f2f74d3..0000000000 --- a/hw/rdma/rdma_rm_defs.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * RDMA device: Definitions of Resource Manager structures - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMA_RM_DEFS_H -#define RDMA_RM_DEFS_H - -#include "rdma_backend_defs.h" - -#define MAX_PORTS 1 /* Do not change - we support only one port */ -#define MAX_PORT_GIDS 255 -#define MAX_GIDS MAX_PORT_GIDS -#define MAX_PORT_PKEYS 1 -#define MAX_PKEYS MAX_PORT_PKEYS -#define MAX_UCS 512 -#define MAX_MR_SIZE (1UL << 27) -#define MAX_QP 1024 -#define MAX_SGE 4 -#define MAX_CQ 2048 -#define MAX_MR 1024 -#define MAX_PD 1024 -#define MAX_QP_RD_ATOM 16 -#define MAX_QP_INIT_RD_ATOM 16 -#define MAX_AH 64 -#define MAX_SRQ 512 - -#define MAX_RM_TBL_NAME 16 -#define MAX_CONSEQ_EMPTY_POLL_CQ 4096 /* considered as error above this */ - -typedef struct RdmaRmResTbl { - char name[MAX_RM_TBL_NAME]; - QemuMutex lock; - unsigned long *bitmap; - size_t tbl_sz; - size_t res_sz; - void *tbl; - uint32_t used; /* number of used entries in the table */ -} RdmaRmResTbl; - -typedef struct RdmaRmPD { - RdmaBackendPD backend_pd; - uint32_t ctx_handle; -} RdmaRmPD; - -typedef enum CQNotificationType { - CNT_CLEAR, - CNT_ARM, - CNT_SET, -} CQNotificationType; - -typedef struct RdmaRmCQ { - RdmaBackendCQ backend_cq; - void *opaque; - CQNotificationType notify; -} RdmaRmCQ; - -/* MR (DMA region) */ -typedef struct RdmaRmMR { - RdmaBackendMR backend_mr; - void *virt; - uint64_t start; - size_t length; - uint32_t pd_handle; - uint32_t lkey; - uint32_t rkey; -} RdmaRmMR; - -typedef struct RdmaRmUC { - uint64_t uc_handle; -} RdmaRmUC; - -typedef struct RdmaRmQP { - RdmaBackendQP backend_qp; - void *opaque; - uint32_t qp_type; - uint32_t qpn; - uint32_t send_cq_handle; - uint32_t recv_cq_handle; - enum ibv_qp_state qp_state; - uint8_t is_srq; -} RdmaRmQP; - -typedef struct RdmaRmSRQ { - RdmaBackendSRQ backend_srq; - uint32_t recv_cq_handle; - void *opaque; -} RdmaRmSRQ; - -typedef struct RdmaRmGid { - union ibv_gid gid; - int backend_gid_index; -} RdmaRmGid; - -typedef struct RdmaRmPort { - RdmaRmGid gid_tbl[MAX_PORT_GIDS]; - enum ibv_port_state state; -} RdmaRmPort; - -typedef struct RdmaRmStats { - uint64_t tx; - uint64_t tx_len; - uint64_t tx_err; - uint64_t rx_bufs; - uint64_t rx_bufs_len; - uint64_t rx_bufs_err; - uint64_t rx_srq; - uint64_t completions; - uint64_t mad_tx; - uint64_t mad_tx_err; - uint64_t mad_rx; - uint64_t mad_rx_err; - uint64_t mad_rx_bufs; - uint64_t mad_rx_bufs_err; - uint64_t poll_cq_from_bk; - uint64_t poll_cq_from_guest; - uint64_t poll_cq_from_guest_empty; - uint64_t poll_cq_ppoll_to; - uint32_t missing_cqe; -} RdmaRmStats; - -struct RdmaDeviceResources { - RdmaRmPort port; - RdmaRmResTbl pd_tbl; - RdmaRmResTbl mr_tbl; - RdmaRmResTbl uc_tbl; - RdmaRmResTbl qp_tbl; - RdmaRmResTbl cq_tbl; - RdmaRmResTbl cqe_ctx_tbl; - RdmaRmResTbl srq_tbl; - GHashTable *qp_hash; /* Keeps mapping between real and emulated */ - QemuMutex lock; - RdmaRmStats stats; -}; - -#endif diff --git a/hw/rdma/rdma_utils.c b/hw/rdma/rdma_utils.c deleted file mode 100644 index c948baf052..0000000000 --- a/hw/rdma/rdma_utils.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * QEMU paravirtual RDMA - Generic RDMA backend - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci_device.h" -#include "trace.h" -#include "rdma_utils.h" - -void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len) -{ - void *p; - dma_addr_t pci_len = len; - - if (!addr) { - rdma_error_report("addr is NULL"); - return NULL; - } - - p = pci_dma_map(dev, addr, &pci_len, DMA_DIRECTION_TO_DEVICE); - if (!p) { - rdma_error_report("pci_dma_map fail, addr=0x%"PRIx64", len=%"PRId64, - addr, pci_len); - return NULL; - } - - if (pci_len != len) { - rdma_pci_dma_unmap(dev, p, pci_len); - return NULL; - } - - trace_rdma_pci_dma_map(addr, p, pci_len); - - return p; -} - -void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len) -{ - trace_rdma_pci_dma_unmap(buffer); - if (buffer) { - pci_dma_unmap(dev, buffer, len, DMA_DIRECTION_TO_DEVICE, 0); - } -} - -void rdma_protected_gqueue_init(RdmaProtectedGQueue *list) -{ - qemu_mutex_init(&list->lock); - list->list = g_queue_new(); -} - -void rdma_protected_gqueue_destroy(RdmaProtectedGQueue *list) -{ - if (list->list) { - g_queue_free_full(list->list, g_free); - qemu_mutex_destroy(&list->lock); - list->list = NULL; - } -} - -void rdma_protected_gqueue_append_int64(RdmaProtectedGQueue *list, - int64_t value) -{ - qemu_mutex_lock(&list->lock); - g_queue_push_tail(list->list, g_memdup(&value, sizeof(value))); - qemu_mutex_unlock(&list->lock); -} - -int64_t rdma_protected_gqueue_pop_int64(RdmaProtectedGQueue *list) -{ - int64_t *valp; - int64_t val; - - qemu_mutex_lock(&list->lock); - - valp = g_queue_pop_head(list->list); - qemu_mutex_unlock(&list->lock); - - if (!valp) { - return -ENOENT; - } - - val = *valp; - g_free(valp); - return val; -} - -void rdma_protected_gslist_init(RdmaProtectedGSList *list) -{ - qemu_mutex_init(&list->lock); -} - -void rdma_protected_gslist_destroy(RdmaProtectedGSList *list) -{ - if (list->list) { - g_slist_free(list->list); - qemu_mutex_destroy(&list->lock); - list->list = NULL; - } -} - -void rdma_protected_gslist_append_int32(RdmaProtectedGSList *list, - int32_t value) -{ - qemu_mutex_lock(&list->lock); - list->list = g_slist_prepend(list->list, GINT_TO_POINTER(value)); - qemu_mutex_unlock(&list->lock); -} - -void rdma_protected_gslist_remove_int32(RdmaProtectedGSList *list, - int32_t value) -{ - qemu_mutex_lock(&list->lock); - list->list = g_slist_remove(list->list, GINT_TO_POINTER(value)); - qemu_mutex_unlock(&list->lock); -} diff --git a/hw/rdma/rdma_utils.h b/hw/rdma/rdma_utils.h deleted file mode 100644 index 54e4f56edd..0000000000 --- a/hw/rdma/rdma_utils.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * RDMA device: Debug utilities - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMA_UTILS_H -#define RDMA_UTILS_H - -#include "qemu/error-report.h" -#include "sysemu/dma.h" - -#define rdma_error_report(fmt, ...) \ - error_report("%s: " fmt, "rdma", ## __VA_ARGS__) -#define rdma_warn_report(fmt, ...) \ - warn_report("%s: " fmt, "rdma", ## __VA_ARGS__) -#define rdma_info_report(fmt, ...) \ - info_report("%s: " fmt, "rdma", ## __VA_ARGS__) - -typedef struct RdmaProtectedGQueue { - QemuMutex lock; - GQueue *list; -} RdmaProtectedGQueue; - -typedef struct RdmaProtectedGSList { - QemuMutex lock; - GSList *list; -} RdmaProtectedGSList; - -void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len); -void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len); -void rdma_protected_gqueue_init(RdmaProtectedGQueue *list); -void rdma_protected_gqueue_destroy(RdmaProtectedGQueue *list); -void rdma_protected_gqueue_append_int64(RdmaProtectedGQueue *list, - int64_t value); -int64_t rdma_protected_gqueue_pop_int64(RdmaProtectedGQueue *list); -void rdma_protected_gslist_init(RdmaProtectedGSList *list); -void rdma_protected_gslist_destroy(RdmaProtectedGSList *list); -void rdma_protected_gslist_append_int32(RdmaProtectedGSList *list, - int32_t value); -void rdma_protected_gslist_remove_int32(RdmaProtectedGSList *list, - int32_t value); - -static inline void addrconf_addr_eui48(uint8_t *eui, const char *addr) -{ - memcpy(eui, addr, 3); - eui[3] = 0xFF; - eui[4] = 0xFE; - memcpy(eui + 5, addr + 3, 3); - eui[0] ^= 2; -} - -#endif diff --git a/hw/rdma/trace-events b/hw/rdma/trace-events deleted file mode 100644 index c23175120e..0000000000 --- a/hw/rdma/trace-events +++ /dev/null @@ -1,31 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# rdma_backend.c -rdma_check_dev_attr(const char *name, int max_bk, int max_fe) "%s: be=%d, fe=%d" -rdma_create_ah_cache_hit(uint64_t subnet, uint64_t if_id) "subnet=0x%"PRIx64",if_id=0x%"PRIx64 -rdma_create_ah_cache_miss(uint64_t subnet, uint64_t if_id) "subnet=0x%"PRIx64",if_id=0x%"PRIx64 -rdma_poll_cq(int ne, void *ibcq) "Got %d completion(s) from cq %p" -rdmacm_mux(const char *title, int msg_type, int op_code) "%s: msg_type=%d, op_code=%d" -rdmacm_mux_check_op_status(int msg_type, int op_code, int err_code) "resp: msg_type=%d, op_code=%d, err_code=%d" -rdma_mad_message(const char *title, int len, char *data) "mad %s (%d): %s" -rdma_backend_rc_qp_state_init(uint32_t qpn) "RC QP 0x%x switch to INIT" -rdma_backend_ud_qp_state_init(uint32_t qpn, uint32_t qkey) "UD QP 0x%x switch to INIT, qkey=0x%x" -rdma_backend_rc_qp_state_rtr(uint32_t qpn, uint64_t subnet, uint64_t ifid, uint8_t sgid_idx, uint32_t dqpn, uint32_t rq_psn) "RC QP 0x%x switch to RTR, subnet = 0x%"PRIx64", ifid = 0x%"PRIx64 ", sgid_idx=%d, dqpn=0x%x, rq_psn=0x%x" -rdma_backend_ud_qp_state_rtr(uint32_t qpn, uint32_t qkey) "UD QP 0x%x switch to RTR, qkey=0x%x" -rdma_backend_rc_qp_state_rts(uint32_t qpn, uint32_t sq_psn) "RC QP 0x%x switch to RTS, sq_psn=0x%x, " -rdma_backend_ud_qp_state_rts(uint32_t qpn, uint32_t sq_psn, uint32_t qkey) "UD QP 0x%x switch to RTS, sq_psn=0x%x, qkey=0x%x" -rdma_backend_get_gid_index(uint64_t subnet, uint64_t ifid, int gid_idx) "subnet=0x%"PRIx64", ifid=0x%"PRIx64 ", gid_idx=%d" -rdma_backend_gid_change(const char *op, uint64_t subnet, uint64_t ifid) "%s subnet=0x%"PRIx64", ifid=0x%"PRIx64 - -# rdma_rm.c -rdma_res_tbl_get(char *name, uint32_t handle) "tbl %s, handle %d" -rdma_res_tbl_alloc(char *name, uint32_t handle) "tbl %s, handle %d" -rdma_res_tbl_dealloc(char *name, uint32_t handle) "tbl %s, handle %d" -rdma_rm_alloc_mr(uint32_t mr_handle, void *host_virt, uint64_t guest_start, uint64_t guest_length, int access_flags) "mr_handle=%d, host_virt=%p, guest_start=0x%"PRIx64", length=%" PRId64", access_flags=0x%x" -rdma_rm_dealloc_mr(uint32_t mr_handle, uint64_t guest_start) "mr_handle=%d, guest_start=0x%"PRIx64 -rdma_rm_alloc_qp(uint32_t rm_qpn, uint32_t backend_qpn, uint8_t qp_type) "rm_qpn=%d, backend_qpn=0x%x, qp_type=%d" -rdma_rm_modify_qp(uint32_t qpn, uint32_t attr_mask, int qp_state, uint8_t sgid_idx) "qpn=0x%x, attr_mask=0x%x, qp_state=%d, sgid_idx=%d" - -# rdma_utils.c -rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRIu64")" -rdma_pci_dma_unmap(void *vaddr) "%p" diff --git a/hw/rdma/trace.h b/hw/rdma/trace.h deleted file mode 100644 index b3fa8ebc51..0000000000 --- a/hw/rdma/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-hw_rdma.h" diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h deleted file mode 100644 index 4cbc10c980..0000000000 --- a/hw/rdma/vmw/pvrdma.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * QEMU VMWARE paravirtual RDMA device definitions - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef PVRDMA_PVRDMA_H -#define PVRDMA_PVRDMA_H - -#include "qemu/units.h" -#include "qemu/notify.h" -#include "hw/pci/msix.h" -#include "hw/pci/pci_device.h" -#include "chardev/char-fe.h" -#include "hw/net/vmxnet3_defs.h" - -#include "../rdma_backend_defs.h" -#include "../rdma_rm_defs.h" - -#include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" -#include "pvrdma_dev_ring.h" -#include "qom/object.h" - -/* BARs */ -#define RDMA_MSIX_BAR_IDX 0 -#define RDMA_REG_BAR_IDX 1 -#define RDMA_UAR_BAR_IDX 2 -#define RDMA_BAR0_MSIX_SIZE (16 * KiB) -#define RDMA_BAR1_REGS_SIZE 64 -#define RDMA_BAR2_UAR_SIZE (0x1000 * MAX_UCS) /* each uc gets page */ - -/* MSIX */ -#define RDMA_MAX_INTRS 3 -#define RDMA_MSIX_TABLE 0x0000 -#define RDMA_MSIX_PBA 0x2000 - -/* Interrupts Vectors */ -#define INTR_VEC_CMD_RING 0 -#define INTR_VEC_CMD_ASYNC_EVENTS 1 -#define INTR_VEC_CMD_COMPLETION_Q 2 - -/* HW attributes */ -#define PVRDMA_HW_NAME "pvrdma" -#define PVRDMA_HW_VERSION 17 -#define PVRDMA_FW_VERSION 14 - -/* Some defaults */ -#define PVRDMA_PKEY 0xFFFF - -typedef struct DSRInfo { - dma_addr_t dma; - struct pvrdma_device_shared_region *dsr; - - union pvrdma_cmd_req *req; - union pvrdma_cmd_resp *rsp; - - PvrdmaRingState *async_ring_state; - PvrdmaRing async; - - PvrdmaRingState *cq_ring_state; - PvrdmaRing cq; -} DSRInfo; - -typedef struct PVRDMADevStats { - uint64_t commands; - uint64_t regs_reads; - uint64_t regs_writes; - uint64_t uar_writes; - uint64_t interrupts; -} PVRDMADevStats; - -struct PVRDMADev { - PCIDevice parent_obj; - MemoryRegion msix; - MemoryRegion regs; - uint32_t regs_data[RDMA_BAR1_REGS_SIZE]; - MemoryRegion uar; - uint32_t uar_data[RDMA_BAR2_UAR_SIZE]; - DSRInfo dsr_info; - int interrupt_mask; - struct ibv_device_attr dev_attr; - uint64_t node_guid; - char *backend_eth_device_name; - char *backend_device_name; - uint8_t backend_port_num; - RdmaBackendDev backend_dev; - RdmaDeviceResources rdma_dev_res; - CharBackend mad_chr; - VMXNET3State *func0; - Notifier shutdown_notifier; - PVRDMADevStats stats; -}; -typedef struct PVRDMADev PVRDMADev; -DECLARE_INSTANCE_CHECKER(PVRDMADev, PVRDMA_DEV, - PVRDMA_HW_NAME) - -static inline int get_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t *val) -{ - int idx = addr >> 2; - - if (idx >= RDMA_BAR1_REGS_SIZE) { - return -EINVAL; - } - - *val = dev->regs_data[idx]; - - return 0; -} - -static inline int set_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t val) -{ - int idx = addr >> 2; - - if (idx >= RDMA_BAR1_REGS_SIZE) { - return -EINVAL; - } - - dev->regs_data[idx] = val; - - return 0; -} - -static inline void post_interrupt(PVRDMADev *dev, unsigned vector) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - - if (likely(!dev->interrupt_mask)) { - dev->stats.interrupts++; - msix_notify(pci_dev, vector); - } -} - -int pvrdma_exec_cmd(PVRDMADev *dev); - -#endif diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c deleted file mode 100644 index d385d18d9c..0000000000 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ /dev/null @@ -1,815 +0,0 @@ -/* - * QEMU paravirtual RDMA - Command channel - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_ids.h" - -#include "../rdma_backend.h" -#include "../rdma_rm.h" -#include "../rdma_utils.h" - -#include "trace.h" -#include "pvrdma.h" -#include "standard-headers/rdma/vmw_pvrdma-abi.h" - -static void *pvrdma_map_to_pdir(PCIDevice *pdev, uint64_t pdir_dma, - uint32_t nchunks, size_t length) -{ - uint64_t *dir, *tbl; - int tbl_idx, dir_idx, addr_idx; - void *host_virt = NULL, *curr_page; - - if (!nchunks) { - rdma_error_report("Got nchunks=0"); - return NULL; - } - - length = ROUND_UP(length, TARGET_PAGE_SIZE); - if (nchunks * TARGET_PAGE_SIZE != length) { - rdma_error_report("Invalid nchunks/length (%u, %lu)", nchunks, - (unsigned long)length); - return NULL; - } - - dir = rdma_pci_dma_map(pdev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to page directory"); - return NULL; - } - - tbl = rdma_pci_dma_map(pdev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to page table 0"); - goto out_unmap_dir; - } - - curr_page = rdma_pci_dma_map(pdev, (dma_addr_t)tbl[0], TARGET_PAGE_SIZE); - if (!curr_page) { - rdma_error_report("Failed to map the page 0"); - goto out_unmap_tbl; - } - - host_virt = mremap(curr_page, 0, length, MREMAP_MAYMOVE); - if (host_virt == MAP_FAILED) { - host_virt = NULL; - rdma_error_report("Failed to remap memory for host_virt"); - goto out_unmap_tbl; - } - trace_pvrdma_map_to_pdir_host_virt(curr_page, host_virt); - - rdma_pci_dma_unmap(pdev, curr_page, TARGET_PAGE_SIZE); - - dir_idx = 0; - tbl_idx = 1; - addr_idx = 1; - while (addr_idx < nchunks) { - if (tbl_idx == TARGET_PAGE_SIZE / sizeof(uint64_t)) { - tbl_idx = 0; - dir_idx++; - rdma_pci_dma_unmap(pdev, tbl, TARGET_PAGE_SIZE); - tbl = rdma_pci_dma_map(pdev, dir[dir_idx], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to page table %d", dir_idx); - goto out_unmap_host_virt; - } - } - - curr_page = rdma_pci_dma_map(pdev, (dma_addr_t)tbl[tbl_idx], - TARGET_PAGE_SIZE); - if (!curr_page) { - rdma_error_report("Failed to map to page %d, dir %d", tbl_idx, - dir_idx); - goto out_unmap_host_virt; - } - - mremap(curr_page, 0, TARGET_PAGE_SIZE, MREMAP_MAYMOVE | MREMAP_FIXED, - host_virt + TARGET_PAGE_SIZE * addr_idx); - - trace_pvrdma_map_to_pdir_next_page(addr_idx, curr_page, host_virt + - TARGET_PAGE_SIZE * addr_idx); - - rdma_pci_dma_unmap(pdev, curr_page, TARGET_PAGE_SIZE); - - addr_idx++; - - tbl_idx++; - } - - goto out_unmap_tbl; - -out_unmap_host_virt: - munmap(host_virt, length); - host_virt = NULL; - -out_unmap_tbl: - rdma_pci_dma_unmap(pdev, tbl, TARGET_PAGE_SIZE); - -out_unmap_dir: - rdma_pci_dma_unmap(pdev, dir, TARGET_PAGE_SIZE); - - return host_virt; -} - -static int query_port(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_query_port *cmd = &req->query_port; - struct pvrdma_cmd_query_port_resp *resp = &rsp->query_port_resp; - struct ibv_port_attr attrs = {}; - - if (cmd->port_num > MAX_PORTS) { - return -EINVAL; - } - - if (rdma_backend_query_port(&dev->backend_dev, &attrs)) { - return -ENOMEM; - } - - memset(resp, 0, sizeof(*resp)); - - /* - * The state, max_mtu and active_mtu fields are enums; the values - * for pvrdma_port_state and pvrdma_mtu match those for - * ibv_port_state and ibv_mtu, so we can cast them safely. - */ - resp->attrs.state = dev->func0->device_active ? - (enum pvrdma_port_state)attrs.state : PVRDMA_PORT_DOWN; - resp->attrs.max_mtu = (enum pvrdma_mtu)attrs.max_mtu; - resp->attrs.active_mtu = (enum pvrdma_mtu)attrs.active_mtu; - resp->attrs.phys_state = attrs.phys_state; - resp->attrs.gid_tbl_len = MIN(MAX_PORT_GIDS, attrs.gid_tbl_len); - resp->attrs.max_msg_sz = 1024; - resp->attrs.pkey_tbl_len = MIN(MAX_PORT_PKEYS, attrs.pkey_tbl_len); - resp->attrs.active_width = 1; - resp->attrs.active_speed = 1; - - return 0; -} - -static int query_pkey(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_query_pkey *cmd = &req->query_pkey; - struct pvrdma_cmd_query_pkey_resp *resp = &rsp->query_pkey_resp; - - if (cmd->port_num > MAX_PORTS) { - return -EINVAL; - } - - if (cmd->index > MAX_PKEYS) { - return -EINVAL; - } - - memset(resp, 0, sizeof(*resp)); - - resp->pkey = PVRDMA_PKEY; - - return 0; -} - -static int create_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_pd *cmd = &req->create_pd; - struct pvrdma_cmd_create_pd_resp *resp = &rsp->create_pd_resp; - - memset(resp, 0, sizeof(*resp)); - return rdma_rm_alloc_pd(&dev->rdma_dev_res, &dev->backend_dev, - &resp->pd_handle, cmd->ctx_handle); -} - -static int destroy_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_pd *cmd = &req->destroy_pd; - - rdma_rm_dealloc_pd(&dev->rdma_dev_res, cmd->pd_handle); - - return 0; -} - -static int create_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_mr *cmd = &req->create_mr; - struct pvrdma_cmd_create_mr_resp *resp = &rsp->create_mr_resp; - PCIDevice *pci_dev = PCI_DEVICE(dev); - void *host_virt = NULL; - int rc = 0; - - memset(resp, 0, sizeof(*resp)); - - if (!(cmd->flags & PVRDMA_MR_FLAG_DMA)) { - host_virt = pvrdma_map_to_pdir(pci_dev, cmd->pdir_dma, cmd->nchunks, - cmd->length); - if (!host_virt) { - rdma_error_report("Failed to map to pdir"); - return -EINVAL; - } - } - - rc = rdma_rm_alloc_mr(&dev->rdma_dev_res, cmd->pd_handle, cmd->start, - cmd->length, host_virt, cmd->access_flags, - &resp->mr_handle, &resp->lkey, &resp->rkey); - if (rc && host_virt) { - munmap(host_virt, cmd->length); - } - - return rc; -} - -static int destroy_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_mr *cmd = &req->destroy_mr; - - rdma_rm_dealloc_mr(&dev->rdma_dev_res, cmd->mr_handle); - - return 0; -} - -static int create_cq_ring(PCIDevice *pci_dev , PvrdmaRing **ring, - uint64_t pdir_dma, uint32_t nchunks, uint32_t cqe) -{ - uint64_t *dir = NULL, *tbl = NULL; - PvrdmaRing *r; - int rc = -EINVAL; - char ring_name[MAX_RING_NAME_SZ]; - - if (!nchunks || nchunks > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid nchunks: %d", nchunks); - return rc; - } - - dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to CQ page directory"); - goto out; - } - - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to CQ page table"); - goto out; - } - - r = g_malloc(sizeof(*r)); - *ring = r; - - r->ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); - - if (!r->ring_state) { - rdma_error_report("Failed to map to CQ ring state"); - goto out_free_ring; - } - - sprintf(ring_name, "cq_ring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(r, ring_name, pci_dev, &r->ring_state[1], - cqe, sizeof(struct pvrdma_cqe), - /* first page is ring state */ - (dma_addr_t *)&tbl[1], nchunks - 1); - if (rc) { - goto out_unmap_ring_state; - } - - goto out; - -out_unmap_ring_state: - /* ring_state was in slot 1, not 0 so need to jump back */ - rdma_pci_dma_unmap(pci_dev, --r->ring_state, TARGET_PAGE_SIZE); - -out_free_ring: - g_free(r); - -out: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - - return rc; -} - -static void destroy_cq_ring(PvrdmaRing *ring) -{ - pvrdma_ring_free(ring); - /* ring_state was in slot 1, not 0 so need to jump back */ - rdma_pci_dma_unmap(ring->dev, --ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); -} - -static int create_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_cq *cmd = &req->create_cq; - struct pvrdma_cmd_create_cq_resp *resp = &rsp->create_cq_resp; - PvrdmaRing *ring = NULL; - int rc; - - memset(resp, 0, sizeof(*resp)); - - resp->cqe = cmd->cqe; - - rc = create_cq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, cmd->nchunks, - cmd->cqe); - if (rc) { - return rc; - } - - rc = rdma_rm_alloc_cq(&dev->rdma_dev_res, &dev->backend_dev, cmd->cqe, - &resp->cq_handle, ring); - if (rc) { - destroy_cq_ring(ring); - } - - resp->cqe = cmd->cqe; - - return rc; -} - -static int destroy_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_cq *cmd = &req->destroy_cq; - RdmaRmCQ *cq; - PvrdmaRing *ring; - - cq = rdma_rm_get_cq(&dev->rdma_dev_res, cmd->cq_handle); - if (!cq) { - rdma_error_report("Got invalid CQ handle"); - return -EINVAL; - } - - ring = (PvrdmaRing *)cq->opaque; - destroy_cq_ring(ring); - - rdma_rm_dealloc_cq(&dev->rdma_dev_res, cmd->cq_handle); - - return 0; -} - -static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, - PvrdmaRing **rings, uint32_t scqe, uint32_t smax_sge, - uint32_t spages, uint32_t rcqe, uint32_t rmax_sge, - uint32_t rpages, uint8_t is_srq) -{ - uint64_t *dir = NULL, *tbl = NULL; - PvrdmaRing *sr, *rr; - int rc = -EINVAL; - char ring_name[MAX_RING_NAME_SZ]; - uint32_t wqe_sz; - - if (!spages || spages > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid send page count for QP ring: %d", - spages); - return rc; - } - - if (!is_srq && (!rpages || rpages > PVRDMA_MAX_FAST_REG_PAGES)) { - rdma_error_report("Got invalid recv page count for QP ring: %d", - rpages); - return rc; - } - - dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to QP page directory"); - goto out; - } - - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to QP page table"); - goto out; - } - - if (!is_srq) { - sr = g_malloc(2 * sizeof(*rr)); - rr = &sr[1]; - } else { - sr = g_malloc(sizeof(*sr)); - } - - *rings = sr; - - /* Create send ring */ - sr->ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); - if (!sr->ring_state) { - rdma_error_report("Failed to map to QP ring state"); - goto out_free_sr_mem; - } - - wqe_sz = pow2ceil(sizeof(struct pvrdma_sq_wqe_hdr) + - sizeof(struct pvrdma_sge) * smax_sge - 1); - - sprintf(ring_name, "qp_sring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(sr, ring_name, pci_dev, sr->ring_state, - scqe, wqe_sz, (dma_addr_t *)&tbl[1], spages); - if (rc) { - goto out_unmap_ring_state; - } - - if (!is_srq) { - /* Create recv ring */ - rr->ring_state = &sr->ring_state[1]; - wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge) * rmax_sge - 1); - sprintf(ring_name, "qp_rring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(rr, ring_name, pci_dev, rr->ring_state, - rcqe, wqe_sz, (dma_addr_t *)&tbl[1 + spages], - rpages); - if (rc) { - goto out_free_sr; - } - } - - goto out; - -out_free_sr: - pvrdma_ring_free(sr); - -out_unmap_ring_state: - rdma_pci_dma_unmap(pci_dev, sr->ring_state, TARGET_PAGE_SIZE); - -out_free_sr_mem: - g_free(sr); - -out: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - - return rc; -} - -static void destroy_qp_rings(PvrdmaRing *ring, uint8_t is_srq) -{ - pvrdma_ring_free(&ring[0]); - if (!is_srq) { - pvrdma_ring_free(&ring[1]); - } - - rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); -} - -static int create_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_qp *cmd = &req->create_qp; - struct pvrdma_cmd_create_qp_resp *resp = &rsp->create_qp_resp; - PvrdmaRing *rings = NULL; - int rc; - - memset(resp, 0, sizeof(*resp)); - - rc = create_qp_rings(PCI_DEVICE(dev), cmd->pdir_dma, &rings, - cmd->max_send_wr, cmd->max_send_sge, cmd->send_chunks, - cmd->max_recv_wr, cmd->max_recv_sge, - cmd->total_chunks - cmd->send_chunks - 1, cmd->is_srq); - if (rc) { - return rc; - } - - rc = rdma_rm_alloc_qp(&dev->rdma_dev_res, cmd->pd_handle, cmd->qp_type, - cmd->max_send_wr, cmd->max_send_sge, - cmd->send_cq_handle, cmd->max_recv_wr, - cmd->max_recv_sge, cmd->recv_cq_handle, rings, - &resp->qpn, cmd->is_srq, cmd->srq_handle); - if (rc) { - destroy_qp_rings(rings, cmd->is_srq); - return rc; - } - - resp->max_send_wr = cmd->max_send_wr; - resp->max_recv_wr = cmd->max_recv_wr; - resp->max_send_sge = cmd->max_send_sge; - resp->max_recv_sge = cmd->max_recv_sge; - resp->max_inline_data = cmd->max_inline_data; - - return 0; -} - -static int modify_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_modify_qp *cmd = &req->modify_qp; - - /* No need to verify sgid_index since it is u8 */ - - return rdma_rm_modify_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, cmd->attr_mask, - cmd->attrs.ah_attr.grh.sgid_index, - (union ibv_gid *)&cmd->attrs.ah_attr.grh.dgid, - cmd->attrs.dest_qp_num, - (enum ibv_qp_state)cmd->attrs.qp_state, - cmd->attrs.qkey, cmd->attrs.rq_psn, - cmd->attrs.sq_psn); -} - -static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_query_qp *cmd = &req->query_qp; - struct pvrdma_cmd_query_qp_resp *resp = &rsp->query_qp_resp; - struct ibv_qp_init_attr init_attr; - - memset(resp, 0, sizeof(*resp)); - - return rdma_rm_query_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, - (struct ibv_qp_attr *)&resp->attrs, - cmd->attr_mask, - &init_attr); -} - -static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_qp *cmd = &req->destroy_qp; - RdmaRmQP *qp; - PvrdmaRing *ring; - - qp = rdma_rm_get_qp(&dev->rdma_dev_res, cmd->qp_handle); - if (!qp) { - return -EINVAL; - } - - ring = (PvrdmaRing *)qp->opaque; - destroy_qp_rings(ring, qp->is_srq); - rdma_rm_dealloc_qp(&dev->rdma_dev_res, cmd->qp_handle); - - return 0; -} - -static int create_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_bind *cmd = &req->create_bind; - union ibv_gid *gid = (union ibv_gid *)&cmd->new_gid; - - if (cmd->index >= MAX_PORT_GIDS) { - return -EINVAL; - } - - return rdma_rm_add_gid(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name, gid, cmd->index); -} - -static int destroy_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; - - if (cmd->index >= MAX_PORT_GIDS) { - return -EINVAL; - } - - return rdma_rm_del_gid(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name, cmd->index); -} - -static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_uc *cmd = &req->create_uc; - struct pvrdma_cmd_create_uc_resp *resp = &rsp->create_uc_resp; - - memset(resp, 0, sizeof(*resp)); - return rdma_rm_alloc_uc(&dev->rdma_dev_res, cmd->pfn, &resp->ctx_handle); -} - -static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_uc *cmd = &req->destroy_uc; - - rdma_rm_dealloc_uc(&dev->rdma_dev_res, cmd->ctx_handle); - - return 0; -} - -static int create_srq_ring(PCIDevice *pci_dev, PvrdmaRing **ring, - uint64_t pdir_dma, uint32_t max_wr, - uint32_t max_sge, uint32_t nchunks) -{ - uint64_t *dir = NULL, *tbl = NULL; - PvrdmaRing *r; - int rc = -EINVAL; - char ring_name[MAX_RING_NAME_SZ]; - uint32_t wqe_sz; - - if (!nchunks || nchunks > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid page count for SRQ ring: %d", - nchunks); - return rc; - } - - dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to SRQ page directory"); - goto out; - } - - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to SRQ page table"); - goto out; - } - - r = g_malloc(sizeof(*r)); - *ring = r; - - r->ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); - if (!r->ring_state) { - rdma_error_report("Failed to map tp SRQ ring state"); - goto out_free_ring_mem; - } - - wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge) * max_sge - 1); - sprintf(ring_name, "srq_ring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(r, ring_name, pci_dev, &r->ring_state[1], max_wr, - wqe_sz, (dma_addr_t *)&tbl[1], nchunks - 1); - if (rc) { - goto out_unmap_ring_state; - } - - goto out; - -out_unmap_ring_state: - rdma_pci_dma_unmap(pci_dev, r->ring_state, TARGET_PAGE_SIZE); - -out_free_ring_mem: - g_free(r); - -out: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - - return rc; -} - -static void destroy_srq_ring(PvrdmaRing *ring) -{ - pvrdma_ring_free(ring); - rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); -} - -static int create_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_srq *cmd = &req->create_srq; - struct pvrdma_cmd_create_srq_resp *resp = &rsp->create_srq_resp; - PvrdmaRing *ring = NULL; - int rc; - - memset(resp, 0, sizeof(*resp)); - - rc = create_srq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, - cmd->attrs.max_wr, cmd->attrs.max_sge, - cmd->nchunks); - if (rc) { - return rc; - } - - rc = rdma_rm_alloc_srq(&dev->rdma_dev_res, cmd->pd_handle, - cmd->attrs.max_wr, cmd->attrs.max_sge, - cmd->attrs.srq_limit, &resp->srqn, ring); - if (rc) { - destroy_srq_ring(ring); - return rc; - } - - return 0; -} - -static int query_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_query_srq *cmd = &req->query_srq; - struct pvrdma_cmd_query_srq_resp *resp = &rsp->query_srq_resp; - - memset(resp, 0, sizeof(*resp)); - - return rdma_rm_query_srq(&dev->rdma_dev_res, cmd->srq_handle, - (struct ibv_srq_attr *)&resp->attrs); -} - -static int modify_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_modify_srq *cmd = &req->modify_srq; - - /* Only support SRQ limit */ - if (!(cmd->attr_mask & IBV_SRQ_LIMIT) || - (cmd->attr_mask & IBV_SRQ_MAX_WR)) - return -EINVAL; - - return rdma_rm_modify_srq(&dev->rdma_dev_res, cmd->srq_handle, - (struct ibv_srq_attr *)&cmd->attrs, - cmd->attr_mask); -} - -static int destroy_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_srq *cmd = &req->destroy_srq; - RdmaRmSRQ *srq; - PvrdmaRing *ring; - - srq = rdma_rm_get_srq(&dev->rdma_dev_res, cmd->srq_handle); - if (!srq) { - return -EINVAL; - } - - ring = (PvrdmaRing *)srq->opaque; - destroy_srq_ring(ring); - rdma_rm_dealloc_srq(&dev->rdma_dev_res, cmd->srq_handle); - - return 0; -} - -struct cmd_handler { - uint32_t cmd; - uint32_t ack; - int (*exec)(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp); -}; - -static struct cmd_handler cmd_handlers[] = { - {PVRDMA_CMD_QUERY_PORT, PVRDMA_CMD_QUERY_PORT_RESP, query_port}, - {PVRDMA_CMD_QUERY_PKEY, PVRDMA_CMD_QUERY_PKEY_RESP, query_pkey}, - {PVRDMA_CMD_CREATE_PD, PVRDMA_CMD_CREATE_PD_RESP, create_pd}, - {PVRDMA_CMD_DESTROY_PD, PVRDMA_CMD_DESTROY_PD_RESP_NOOP, destroy_pd}, - {PVRDMA_CMD_CREATE_MR, PVRDMA_CMD_CREATE_MR_RESP, create_mr}, - {PVRDMA_CMD_DESTROY_MR, PVRDMA_CMD_DESTROY_MR_RESP_NOOP, destroy_mr}, - {PVRDMA_CMD_CREATE_CQ, PVRDMA_CMD_CREATE_CQ_RESP, create_cq}, - {PVRDMA_CMD_RESIZE_CQ, PVRDMA_CMD_RESIZE_CQ_RESP, NULL}, - {PVRDMA_CMD_DESTROY_CQ, PVRDMA_CMD_DESTROY_CQ_RESP_NOOP, destroy_cq}, - {PVRDMA_CMD_CREATE_QP, PVRDMA_CMD_CREATE_QP_RESP, create_qp}, - {PVRDMA_CMD_MODIFY_QP, PVRDMA_CMD_MODIFY_QP_RESP, modify_qp}, - {PVRDMA_CMD_QUERY_QP, PVRDMA_CMD_QUERY_QP_RESP, query_qp}, - {PVRDMA_CMD_DESTROY_QP, PVRDMA_CMD_DESTROY_QP_RESP, destroy_qp}, - {PVRDMA_CMD_CREATE_UC, PVRDMA_CMD_CREATE_UC_RESP, create_uc}, - {PVRDMA_CMD_DESTROY_UC, PVRDMA_CMD_DESTROY_UC_RESP_NOOP, destroy_uc}, - {PVRDMA_CMD_CREATE_BIND, PVRDMA_CMD_CREATE_BIND_RESP_NOOP, create_bind}, - {PVRDMA_CMD_DESTROY_BIND, PVRDMA_CMD_DESTROY_BIND_RESP_NOOP, destroy_bind}, - {PVRDMA_CMD_CREATE_SRQ, PVRDMA_CMD_CREATE_SRQ_RESP, create_srq}, - {PVRDMA_CMD_QUERY_SRQ, PVRDMA_CMD_QUERY_SRQ_RESP, query_srq}, - {PVRDMA_CMD_MODIFY_SRQ, PVRDMA_CMD_MODIFY_SRQ_RESP, modify_srq}, - {PVRDMA_CMD_DESTROY_SRQ, PVRDMA_CMD_DESTROY_SRQ_RESP, destroy_srq}, -}; - -int pvrdma_exec_cmd(PVRDMADev *dev) -{ - int err = 0xFFFF; - DSRInfo *dsr_info; - - dsr_info = &dev->dsr_info; - - if (!dsr_info->dsr) { - /* Buggy or malicious guest driver */ - rdma_error_report("Exec command without dsr, req or rsp buffers"); - goto out; - } - - if (dsr_info->req->hdr.cmd >= sizeof(cmd_handlers) / - sizeof(struct cmd_handler)) { - rdma_error_report("Unsupported command"); - goto out; - } - - if (!cmd_handlers[dsr_info->req->hdr.cmd].exec) { - rdma_error_report("Unsupported command (not implemented yet)"); - goto out; - } - - err = cmd_handlers[dsr_info->req->hdr.cmd].exec(dev, dsr_info->req, - dsr_info->rsp); - dsr_info->rsp->hdr.response = dsr_info->req->hdr.response; - dsr_info->rsp->hdr.ack = cmd_handlers[dsr_info->req->hdr.cmd].ack; - dsr_info->rsp->hdr.err = err < 0 ? -err : 0; - - trace_pvrdma_exec_cmd(dsr_info->req->hdr.cmd, dsr_info->rsp->hdr.err); - - dev->stats.commands++; - -out: - set_reg_val(dev, PVRDMA_REG_ERR, err); - post_interrupt(dev, INTR_VEC_CMD_RING); - - return (err == 0) ? 0 : -EINVAL; -} diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c deleted file mode 100644 index 30ce22a5be..0000000000 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * QEMU paravirtual RDMA - Device rings - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci.h" -#include "cpu.h" -#include "qemu/cutils.h" - -#include "trace.h" - -#include "../rdma_utils.h" -#include "pvrdma_dev_ring.h" - -int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, - PvrdmaRingState *ring_state, uint32_t max_elems, - size_t elem_sz, dma_addr_t *tbl, uint32_t npages) -{ - int i; - int rc = 0; - - pstrcpy(ring->name, MAX_RING_NAME_SZ, name); - ring->dev = dev; - ring->ring_state = ring_state; - ring->max_elems = max_elems; - ring->elem_sz = elem_sz; - /* TODO: Give a moment to think if we want to redo driver settings - qatomic_set(&ring->ring_state->prod_tail, 0); - qatomic_set(&ring->ring_state->cons_head, 0); - */ - ring->npages = npages; - ring->pages = g_new0(void *, npages); - - for (i = 0; i < npages; i++) { - if (!tbl[i]) { - rdma_error_report("npages=%d but tbl[%d] is NULL", npages, i); - continue; - } - - ring->pages[i] = rdma_pci_dma_map(dev, tbl[i], TARGET_PAGE_SIZE); - if (!ring->pages[i]) { - rc = -ENOMEM; - rdma_error_report("Failed to map to page %d in ring %s", i, name); - goto out_free; - } - memset(ring->pages[i], 0, TARGET_PAGE_SIZE); - } - - goto out; - -out_free: - while (i--) { - rdma_pci_dma_unmap(dev, ring->pages[i], TARGET_PAGE_SIZE); - } - g_free(ring->pages); - -out: - return rc; -} - -void *pvrdma_ring_next_elem_read(PvrdmaRing *ring) -{ - unsigned int idx, offset; - const uint32_t tail = qatomic_read(&ring->ring_state->prod_tail); - const uint32_t head = qatomic_read(&ring->ring_state->cons_head); - - if (tail & ~((ring->max_elems << 1) - 1) || - head & ~((ring->max_elems << 1) - 1) || - tail == head) { - trace_pvrdma_ring_next_elem_read_no_data(ring->name); - return NULL; - } - - idx = head & (ring->max_elems - 1); - offset = idx * ring->elem_sz; - return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); -} - -void pvrdma_ring_read_inc(PvrdmaRing *ring) -{ - uint32_t idx = qatomic_read(&ring->ring_state->cons_head); - - idx = (idx + 1) & ((ring->max_elems << 1) - 1); - qatomic_set(&ring->ring_state->cons_head, idx); -} - -void *pvrdma_ring_next_elem_write(PvrdmaRing *ring) -{ - unsigned int idx, offset; - const uint32_t tail = qatomic_read(&ring->ring_state->prod_tail); - const uint32_t head = qatomic_read(&ring->ring_state->cons_head); - - if (tail & ~((ring->max_elems << 1) - 1) || - head & ~((ring->max_elems << 1) - 1) || - tail == (head ^ ring->max_elems)) { - rdma_error_report("CQ is full"); - return NULL; - } - - idx = tail & (ring->max_elems - 1); - offset = idx * ring->elem_sz; - return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); -} - -void pvrdma_ring_write_inc(PvrdmaRing *ring) -{ - uint32_t idx = qatomic_read(&ring->ring_state->prod_tail); - - idx = (idx + 1) & ((ring->max_elems << 1) - 1); - qatomic_set(&ring->ring_state->prod_tail, idx); -} - -void pvrdma_ring_free(PvrdmaRing *ring) -{ - if (!ring) { - return; - } - - if (!ring->pages) { - return; - } - - while (ring->npages--) { - rdma_pci_dma_unmap(ring->dev, ring->pages[ring->npages], - TARGET_PAGE_SIZE); - } - - g_free(ring->pages); - ring->pages = NULL; -} diff --git a/hw/rdma/vmw/pvrdma_dev_ring.h b/hw/rdma/vmw/pvrdma_dev_ring.h deleted file mode 100644 index d231588ce0..0000000000 --- a/hw/rdma/vmw/pvrdma_dev_ring.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * QEMU VMWARE paravirtual RDMA ring utilities - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef PVRDMA_DEV_RING_H -#define PVRDMA_DEV_RING_H - - -#define MAX_RING_NAME_SZ 32 - -typedef struct PvrdmaRingState { - int prod_tail; /* producer tail */ - int cons_head; /* consumer head */ -} PvrdmaRingState; - -typedef struct PvrdmaRing { - char name[MAX_RING_NAME_SZ]; - PCIDevice *dev; - uint32_t max_elems; - size_t elem_sz; - PvrdmaRingState *ring_state; /* used only for unmap */ - int npages; - void **pages; -} PvrdmaRing; - -int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, - PvrdmaRingState *ring_state, uint32_t max_elems, - size_t elem_sz, dma_addr_t *tbl, uint32_t npages); -void *pvrdma_ring_next_elem_read(PvrdmaRing *ring); -void pvrdma_ring_read_inc(PvrdmaRing *ring); -void *pvrdma_ring_next_elem_write(PvrdmaRing *ring); -void pvrdma_ring_write_inc(PvrdmaRing *ring); -void pvrdma_ring_free(PvrdmaRing *ring); - -#endif diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c deleted file mode 100644 index e735ff97eb..0000000000 --- a/hw/rdma/vmw/pvrdma_main.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - * QEMU paravirtual RDMA - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "cpu.h" -#include "trace.h" -#include "monitor/monitor.h" -#include "hw/rdma/rdma.h" - -#include "../rdma_rm.h" -#include "../rdma_backend.h" -#include "../rdma_utils.h" - -#include -#include "pvrdma.h" -#include "standard-headers/rdma/vmw_pvrdma-abi.h" -#include "sysemu/runstate.h" -#include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" -#include "pvrdma_qp_ops.h" - -static Property pvrdma_dev_properties[] = { - DEFINE_PROP_STRING("netdev", PVRDMADev, backend_eth_device_name), - DEFINE_PROP_STRING("ibdev", PVRDMADev, backend_device_name), - DEFINE_PROP_UINT8("ibport", PVRDMADev, backend_port_num, 1), - DEFINE_PROP_UINT64("dev-caps-max-mr-size", PVRDMADev, dev_attr.max_mr_size, - MAX_MR_SIZE), - DEFINE_PROP_INT32("dev-caps-max-qp", PVRDMADev, dev_attr.max_qp, MAX_QP), - DEFINE_PROP_INT32("dev-caps-max-cq", PVRDMADev, dev_attr.max_cq, MAX_CQ), - DEFINE_PROP_INT32("dev-caps-max-mr", PVRDMADev, dev_attr.max_mr, MAX_MR), - DEFINE_PROP_INT32("dev-caps-max-pd", PVRDMADev, dev_attr.max_pd, MAX_PD), - DEFINE_PROP_INT32("dev-caps-qp-rd-atom", PVRDMADev, dev_attr.max_qp_rd_atom, - MAX_QP_RD_ATOM), - DEFINE_PROP_INT32("dev-caps-max-qp-init-rd-atom", PVRDMADev, - dev_attr.max_qp_init_rd_atom, MAX_QP_INIT_RD_ATOM), - DEFINE_PROP_INT32("dev-caps-max-ah", PVRDMADev, dev_attr.max_ah, MAX_AH), - DEFINE_PROP_INT32("dev-caps-max-srq", PVRDMADev, dev_attr.max_srq, MAX_SRQ), - DEFINE_PROP_CHR("mad-chardev", PVRDMADev, mad_chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pvrdma_format_statistics(RdmaProvider *obj, GString *buf) -{ - PVRDMADev *dev = PVRDMA_DEV(obj); - PCIDevice *pdev = PCI_DEVICE(dev); - - g_string_append_printf(buf, "%s, %x.%x\n", - pdev->name, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn)); - g_string_append_printf(buf, "\tcommands : %" PRId64 "\n", - dev->stats.commands); - g_string_append_printf(buf, "\tregs_reads : %" PRId64 "\n", - dev->stats.regs_reads); - g_string_append_printf(buf, "\tregs_writes : %" PRId64 "\n", - dev->stats.regs_writes); - g_string_append_printf(buf, "\tuar_writes : %" PRId64 "\n", - dev->stats.uar_writes); - g_string_append_printf(buf, "\tinterrupts : %" PRId64 "\n", - dev->stats.interrupts); - rdma_format_device_counters(&dev->rdma_dev_res, buf); -} - -static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring, - void *ring_state) -{ - pvrdma_ring_free(ring); - rdma_pci_dma_unmap(pci_dev, ring_state, TARGET_PAGE_SIZE); -} - -static int init_dev_ring(PvrdmaRing *ring, PvrdmaRingState **ring_state, - const char *name, PCIDevice *pci_dev, - dma_addr_t dir_addr, uint32_t num_pages) -{ - uint64_t *dir, *tbl; - int max_pages, rc = 0; - - if (!num_pages) { - rdma_error_report("Ring pages count must be strictly positive"); - return -EINVAL; - } - - /* - * Make sure we can satisfy the requested number of pages in a single - * TARGET_PAGE_SIZE sized page table (taking into account that first entry - * is reserved for ring-state) - */ - max_pages = TARGET_PAGE_SIZE / sizeof(dma_addr_t) - 1; - if (num_pages > max_pages) { - rdma_error_report("Maximum pages on a single directory must not exceed %d\n", - max_pages); - return -EINVAL; - } - - dir = rdma_pci_dma_map(pci_dev, dir_addr, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to page directory (ring %s)", name); - rc = -ENOMEM; - goto out; - } - - /* We support only one page table for a ring */ - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to page table (ring %s)", name); - rc = -ENOMEM; - goto out_free_dir; - } - - *ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); - if (!*ring_state) { - rdma_error_report("Failed to map to ring state (ring %s)", name); - rc = -ENOMEM; - goto out_free_tbl; - } - /* RX ring is the second */ - (*ring_state)++; - rc = pvrdma_ring_init(ring, name, pci_dev, - (PvrdmaRingState *)*ring_state, - (num_pages - 1) * TARGET_PAGE_SIZE / - sizeof(struct pvrdma_cqne), - sizeof(struct pvrdma_cqne), - (dma_addr_t *)&tbl[1], (dma_addr_t)num_pages - 1); - if (rc) { - rc = -ENOMEM; - goto out_free_ring_state; - } - - goto out_free_tbl; - -out_free_ring_state: - rdma_pci_dma_unmap(pci_dev, *ring_state, TARGET_PAGE_SIZE); - -out_free_tbl: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - -out_free_dir: - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - -out: - return rc; -} - -static void free_dsr(PVRDMADev *dev) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - - if (!dev->dsr_info.dsr) { - return; - } - - free_dev_ring(pci_dev, &dev->dsr_info.async, - dev->dsr_info.async_ring_state); - - free_dev_ring(pci_dev, &dev->dsr_info.cq, dev->dsr_info.cq_ring_state); - - rdma_pci_dma_unmap(pci_dev, dev->dsr_info.req, - sizeof(union pvrdma_cmd_req)); - - rdma_pci_dma_unmap(pci_dev, dev->dsr_info.rsp, - sizeof(union pvrdma_cmd_resp)); - - rdma_pci_dma_unmap(pci_dev, dev->dsr_info.dsr, - sizeof(struct pvrdma_device_shared_region)); - - dev->dsr_info.dsr = NULL; -} - -static int load_dsr(PVRDMADev *dev) -{ - int rc = 0; - PCIDevice *pci_dev = PCI_DEVICE(dev); - DSRInfo *dsr_info; - struct pvrdma_device_shared_region *dsr; - - free_dsr(dev); - - /* Map to DSR */ - dev->dsr_info.dsr = rdma_pci_dma_map(pci_dev, dev->dsr_info.dma, - sizeof(struct pvrdma_device_shared_region)); - if (!dev->dsr_info.dsr) { - rdma_error_report("Failed to map to DSR"); - rc = -ENOMEM; - goto out; - } - - /* Shortcuts */ - dsr_info = &dev->dsr_info; - dsr = dsr_info->dsr; - - /* Map to command slot */ - dsr_info->req = rdma_pci_dma_map(pci_dev, dsr->cmd_slot_dma, - sizeof(union pvrdma_cmd_req)); - if (!dsr_info->req) { - rdma_error_report("Failed to map to command slot address"); - rc = -ENOMEM; - goto out_free_dsr; - } - - /* Map to response slot */ - dsr_info->rsp = rdma_pci_dma_map(pci_dev, dsr->resp_slot_dma, - sizeof(union pvrdma_cmd_resp)); - if (!dsr_info->rsp) { - rdma_error_report("Failed to map to response slot address"); - rc = -ENOMEM; - goto out_free_req; - } - - /* Map to CQ notification ring */ - rc = init_dev_ring(&dsr_info->cq, &dsr_info->cq_ring_state, "dev_cq", - pci_dev, dsr->cq_ring_pages.pdir_dma, - dsr->cq_ring_pages.num_pages); - if (rc) { - rc = -ENOMEM; - goto out_free_rsp; - } - - /* Map to event notification ring */ - rc = init_dev_ring(&dsr_info->async, &dsr_info->async_ring_state, - "dev_async", pci_dev, dsr->async_ring_pages.pdir_dma, - dsr->async_ring_pages.num_pages); - if (rc) { - rc = -ENOMEM; - goto out_free_rsp; - } - - goto out; - -out_free_rsp: - rdma_pci_dma_unmap(pci_dev, dsr_info->rsp, sizeof(union pvrdma_cmd_resp)); - -out_free_req: - rdma_pci_dma_unmap(pci_dev, dsr_info->req, sizeof(union pvrdma_cmd_req)); - -out_free_dsr: - rdma_pci_dma_unmap(pci_dev, dsr_info->dsr, - sizeof(struct pvrdma_device_shared_region)); - dsr_info->dsr = NULL; - -out: - return rc; -} - -static void init_dsr_dev_caps(PVRDMADev *dev) -{ - struct pvrdma_device_shared_region *dsr; - - if (!dev->dsr_info.dsr) { - /* Buggy or malicious guest driver */ - rdma_error_report("Can't initialized DSR"); - return; - } - - dsr = dev->dsr_info.dsr; - dsr->caps.fw_ver = PVRDMA_FW_VERSION; - dsr->caps.mode = PVRDMA_DEVICE_MODE_ROCE; - dsr->caps.gid_types |= PVRDMA_GID_TYPE_FLAG_ROCE_V1; - dsr->caps.max_uar = RDMA_BAR2_UAR_SIZE; - dsr->caps.max_mr_size = dev->dev_attr.max_mr_size; - dsr->caps.max_qp = dev->dev_attr.max_qp; - dsr->caps.max_qp_wr = dev->dev_attr.max_qp_wr; - dsr->caps.max_sge = dev->dev_attr.max_sge; - dsr->caps.max_cq = dev->dev_attr.max_cq; - dsr->caps.max_cqe = dev->dev_attr.max_cqe; - dsr->caps.max_mr = dev->dev_attr.max_mr; - dsr->caps.max_pd = dev->dev_attr.max_pd; - dsr->caps.max_ah = dev->dev_attr.max_ah; - dsr->caps.max_srq = dev->dev_attr.max_srq; - dsr->caps.max_srq_wr = dev->dev_attr.max_srq_wr; - dsr->caps.max_srq_sge = dev->dev_attr.max_srq_sge; - dsr->caps.gid_tbl_len = MAX_GIDS; - dsr->caps.sys_image_guid = 0; - dsr->caps.node_guid = dev->node_guid; - dsr->caps.phys_port_cnt = MAX_PORTS; - dsr->caps.max_pkeys = MAX_PKEYS; -} - -static void uninit_msix(PCIDevice *pdev, int used_vectors) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - int i; - - for (i = 0; i < used_vectors; i++) { - msix_vector_unuse(pdev, i); - } - - msix_uninit(pdev, &dev->msix, &dev->msix); -} - -static int init_msix(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - int i; - int rc; - - rc = msix_init(pdev, RDMA_MAX_INTRS, &dev->msix, RDMA_MSIX_BAR_IDX, - RDMA_MSIX_TABLE, &dev->msix, RDMA_MSIX_BAR_IDX, - RDMA_MSIX_PBA, 0, NULL); - - if (rc < 0) { - rdma_error_report("Failed to initialize MSI-X"); - return rc; - } - - for (i = 0; i < RDMA_MAX_INTRS; i++) { - msix_vector_use(PCI_DEVICE(dev), i); - } - - return 0; -} - -static void pvrdma_fini(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - notifier_remove(&dev->shutdown_notifier); - - pvrdma_qp_ops_fini(); - - rdma_backend_stop(&dev->backend_dev); - - rdma_rm_fini(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name); - - rdma_backend_fini(&dev->backend_dev); - - free_dsr(dev); - - if (msix_enabled(pdev)) { - uninit_msix(pdev, RDMA_MAX_INTRS); - } - - rdma_info_report("Device %s %x.%x is down", pdev->name, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); -} - -static void pvrdma_stop(PVRDMADev *dev) -{ - rdma_backend_stop(&dev->backend_dev); -} - -static void pvrdma_start(PVRDMADev *dev) -{ - rdma_backend_start(&dev->backend_dev); -} - -static void activate_device(PVRDMADev *dev) -{ - pvrdma_start(dev); - set_reg_val(dev, PVRDMA_REG_ERR, 0); -} - -static int unquiesce_device(PVRDMADev *dev) -{ - return 0; -} - -static void reset_device(PVRDMADev *dev) -{ - pvrdma_stop(dev); -} - -static uint64_t pvrdma_regs_read(void *opaque, hwaddr addr, unsigned size) -{ - PVRDMADev *dev = opaque; - uint32_t val; - - dev->stats.regs_reads++; - - if (get_reg_val(dev, addr, &val)) { - rdma_error_report("Failed to read REG value from address 0x%x", - (uint32_t)addr); - return -EINVAL; - } - - trace_pvrdma_regs_read(addr, val); - - return val; -} - -static void pvrdma_regs_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - PVRDMADev *dev = opaque; - - dev->stats.regs_writes++; - - if (set_reg_val(dev, addr, val)) { - rdma_error_report("Failed to set REG value, addr=0x%"PRIx64 ", val=0x%"PRIx64, - addr, val); - return; - } - - switch (addr) { - case PVRDMA_REG_DSRLOW: - trace_pvrdma_regs_write(addr, val, "DSRLOW", ""); - dev->dsr_info.dma = val; - break; - case PVRDMA_REG_DSRHIGH: - trace_pvrdma_regs_write(addr, val, "DSRHIGH", ""); - dev->dsr_info.dma |= val << 32; - load_dsr(dev); - init_dsr_dev_caps(dev); - break; - case PVRDMA_REG_CTL: - switch (val) { - case PVRDMA_DEVICE_CTL_ACTIVATE: - trace_pvrdma_regs_write(addr, val, "CTL", "ACTIVATE"); - activate_device(dev); - break; - case PVRDMA_DEVICE_CTL_UNQUIESCE: - trace_pvrdma_regs_write(addr, val, "CTL", "UNQUIESCE"); - unquiesce_device(dev); - break; - case PVRDMA_DEVICE_CTL_RESET: - trace_pvrdma_regs_write(addr, val, "CTL", "URESET"); - reset_device(dev); - break; - } - break; - case PVRDMA_REG_IMR: - trace_pvrdma_regs_write(addr, val, "INTR_MASK", ""); - dev->interrupt_mask = val; - break; - case PVRDMA_REG_REQUEST: - if (val == 0) { - trace_pvrdma_regs_write(addr, val, "REQUEST", ""); - pvrdma_exec_cmd(dev); - } - break; - default: - break; - } -} - -static const MemoryRegionOps regs_ops = { - .read = pvrdma_regs_read, - .write = pvrdma_regs_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = sizeof(uint32_t), - .max_access_size = sizeof(uint32_t), - }, -}; - -static uint64_t pvrdma_uar_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffff; -} - -static void pvrdma_uar_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - PVRDMADev *dev = opaque; - - dev->stats.uar_writes++; - - switch (addr & 0xFFF) { /* Mask with 0xFFF as each UC gets page */ - case PVRDMA_UAR_QP_OFFSET: - if (val & PVRDMA_UAR_QP_SEND) { - trace_pvrdma_uar_write(addr, val, "QP", "SEND", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_qp_send(dev, val & PVRDMA_UAR_HANDLE_MASK); - } - if (val & PVRDMA_UAR_QP_RECV) { - trace_pvrdma_uar_write(addr, val, "QP", "RECV", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_qp_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); - } - break; - case PVRDMA_UAR_CQ_OFFSET: - if (val & PVRDMA_UAR_CQ_ARM) { - trace_pvrdma_uar_write(addr, val, "CQ", "ARM", - val & PVRDMA_UAR_HANDLE_MASK, - !!(val & PVRDMA_UAR_CQ_ARM_SOL)); - rdma_rm_req_notify_cq(&dev->rdma_dev_res, - val & PVRDMA_UAR_HANDLE_MASK, - !!(val & PVRDMA_UAR_CQ_ARM_SOL)); - } - if (val & PVRDMA_UAR_CQ_ARM_SOL) { - trace_pvrdma_uar_write(addr, val, "CQ", "ARMSOL - not supported", 0, - 0); - } - if (val & PVRDMA_UAR_CQ_POLL) { - trace_pvrdma_uar_write(addr, val, "CQ", "POLL", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_cq_poll(&dev->rdma_dev_res, val & PVRDMA_UAR_HANDLE_MASK); - } - break; - case PVRDMA_UAR_SRQ_OFFSET: - if (val & PVRDMA_UAR_SRQ_RECV) { - trace_pvrdma_uar_write(addr, val, "QP", "SRQ", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_srq_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); - } - break; - default: - rdma_error_report("Unsupported command, addr=0x%"PRIx64", val=0x%"PRIx64, - addr, val); - break; - } -} - -static const MemoryRegionOps uar_ops = { - .read = pvrdma_uar_read, - .write = pvrdma_uar_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = sizeof(uint32_t), - .max_access_size = sizeof(uint32_t), - }, -}; - -static void init_pci_config(PCIDevice *pdev) -{ - pdev->config[PCI_INTERRUPT_PIN] = 1; -} - -static void init_bars(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - /* BAR 0 - MSI-X */ - memory_region_init(&dev->msix, OBJECT(dev), "pvrdma-msix", - RDMA_BAR0_MSIX_SIZE); - pci_register_bar(pdev, RDMA_MSIX_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->msix); - - /* BAR 1 - Registers */ - memset(&dev->regs_data, 0, sizeof(dev->regs_data)); - memory_region_init_io(&dev->regs, OBJECT(dev), ®s_ops, dev, - "pvrdma-regs", sizeof(dev->regs_data)); - pci_register_bar(pdev, RDMA_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->regs); - - /* BAR 2 - UAR */ - memset(&dev->uar_data, 0, sizeof(dev->uar_data)); - memory_region_init_io(&dev->uar, OBJECT(dev), &uar_ops, dev, "rdma-uar", - sizeof(dev->uar_data)); - pci_register_bar(pdev, RDMA_UAR_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->uar); -} - -static void init_regs(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - set_reg_val(dev, PVRDMA_REG_VERSION, PVRDMA_HW_VERSION); - set_reg_val(dev, PVRDMA_REG_ERR, 0xFFFF); -} - -static void init_dev_caps(PVRDMADev *dev) -{ - size_t pg_tbl_bytes = TARGET_PAGE_SIZE * - (TARGET_PAGE_SIZE / sizeof(uint64_t)); - size_t wr_sz = MAX(sizeof(struct pvrdma_sq_wqe_hdr), - sizeof(struct pvrdma_rq_wqe_hdr)); - - dev->dev_attr.max_qp_wr = pg_tbl_bytes / - (wr_sz + sizeof(struct pvrdma_sge) * - dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; - /* First page is ring state ^^^^ */ - - dev->dev_attr.max_cqe = pg_tbl_bytes / sizeof(struct pvrdma_cqe) - - TARGET_PAGE_SIZE; /* First page is ring state */ - - dev->dev_attr.max_srq_wr = pg_tbl_bytes / - ((sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge)) * - dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; -} - -static int pvrdma_check_ram_shared(Object *obj, void *opaque) -{ - bool *shared = opaque; - - if (object_dynamic_cast(obj, "memory-backend-ram")) { - *shared = object_property_get_bool(obj, "share", NULL); - } - - return 0; -} - -static void pvrdma_shutdown_notifier(Notifier *n, void *opaque) -{ - PVRDMADev *dev = container_of(n, PVRDMADev, shutdown_notifier); - PCIDevice *pci_dev = PCI_DEVICE(dev); - - pvrdma_fini(pci_dev); -} - -static void pvrdma_realize(PCIDevice *pdev, Error **errp) -{ - int rc = 0; - PVRDMADev *dev = PVRDMA_DEV(pdev); - Object *memdev_root; - bool ram_shared = false; - PCIDevice *func0; - - warn_report_once("pvrdma is deprecated and will be removed in a future release"); - - rdma_info_report("Initializing device %s %x.%x", pdev->name, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - - if (TARGET_PAGE_SIZE != qemu_real_host_page_size()) { - error_setg(errp, "Target page size must be the same as host page size"); - return; - } - - func0 = pci_get_function_0(pdev); - /* Break if not vmxnet3 device in slot 0 */ - if (strcmp(object_get_typename(OBJECT(func0)), TYPE_VMXNET3)) { - error_setg(errp, "Device on %x.0 must be %s", PCI_SLOT(pdev->devfn), - TYPE_VMXNET3); - return; - } - dev->func0 = VMXNET3(func0); - - addrconf_addr_eui48((unsigned char *)&dev->node_guid, - (const char *)&dev->func0->conf.macaddr.a); - - memdev_root = object_resolve_path("/objects", NULL); - if (memdev_root) { - object_child_foreach(memdev_root, pvrdma_check_ram_shared, &ram_shared); - } - if (!ram_shared) { - error_setg(errp, "Only shared memory backed ram is supported"); - return; - } - - dev->dsr_info.dsr = NULL; - - init_pci_config(pdev); - - init_bars(pdev); - - init_regs(pdev); - - rc = init_msix(pdev); - if (rc) { - goto out; - } - - rc = rdma_backend_init(&dev->backend_dev, pdev, &dev->rdma_dev_res, - dev->backend_device_name, dev->backend_port_num, - &dev->dev_attr, &dev->mad_chr); - if (rc) { - goto out; - } - - init_dev_caps(dev); - - rc = rdma_rm_init(&dev->rdma_dev_res, &dev->dev_attr); - if (rc) { - goto out; - } - - rc = pvrdma_qp_ops_init(); - if (rc) { - goto out; - } - - memset(&dev->stats, 0, sizeof(dev->stats)); - - dev->shutdown_notifier.notify = pvrdma_shutdown_notifier; - qemu_register_shutdown_notifier(&dev->shutdown_notifier); - -#ifdef LEGACY_RDMA_REG_MR - rdma_info_report("Using legacy reg_mr"); -#else - rdma_info_report("Using iova reg_mr"); -#endif - -out: - if (rc) { - pvrdma_fini(pdev); - error_append_hint(errp, "Device failed to load\n"); - } -} - -static void pvrdma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - RdmaProviderClass *ir = RDMA_PROVIDER_CLASS(klass); - - k->realize = pvrdma_realize; - k->vendor_id = PCI_VENDOR_ID_VMWARE; - k->device_id = PCI_DEVICE_ID_VMWARE_PVRDMA; - k->revision = 0x00; - k->class_id = PCI_CLASS_NETWORK_OTHER; - - dc->desc = "RDMA Device"; - device_class_set_props(dc, pvrdma_dev_properties); - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - - ir->format_statistics = pvrdma_format_statistics; -} - -static const TypeInfo pvrdma_info = { - .name = PVRDMA_HW_NAME, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PVRDMADev), - .class_init = pvrdma_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { INTERFACE_RDMA_PROVIDER }, - { } - } -}; - -static void register_types(void) -{ - type_register_static(&pvrdma_info); -} - -type_init(register_types) diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c deleted file mode 100644 index c30c8344f6..0000000000 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * QEMU paravirtual RDMA - QP implementation - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "../rdma_utils.h" -#include "../rdma_rm.h" -#include "../rdma_backend.h" - -#include "trace.h" - -#include "pvrdma.h" -#include "standard-headers/rdma/vmw_pvrdma-abi.h" -#include "pvrdma_qp_ops.h" - -typedef struct CompHandlerCtx { - PVRDMADev *dev; - uint32_t cq_handle; - struct pvrdma_cqe cqe; -} CompHandlerCtx; - -/* Send Queue WQE */ -typedef struct PvrdmaSqWqe { - struct pvrdma_sq_wqe_hdr hdr; - struct pvrdma_sge sge[]; -} PvrdmaSqWqe; - -/* Recv Queue WQE */ -typedef struct PvrdmaRqWqe { - struct pvrdma_rq_wqe_hdr hdr; - struct pvrdma_sge sge[]; -} PvrdmaRqWqe; - -/* - * 1. Put CQE on send CQ ring - * 2. Put CQ number on dsr completion ring - * 3. Interrupt host - */ -static int pvrdma_post_cqe(PVRDMADev *dev, uint32_t cq_handle, - struct pvrdma_cqe *cqe, struct ibv_wc *wc) -{ - struct pvrdma_cqe *cqe1; - struct pvrdma_cqne *cqne; - PvrdmaRing *ring; - RdmaRmCQ *cq = rdma_rm_get_cq(&dev->rdma_dev_res, cq_handle); - - if (unlikely(!cq)) { - return -EINVAL; - } - - ring = (PvrdmaRing *)cq->opaque; - - /* Step #1: Put CQE on CQ ring */ - cqe1 = pvrdma_ring_next_elem_write(ring); - if (unlikely(!cqe1)) { - return -EINVAL; - } - - memset(cqe1, 0, sizeof(*cqe1)); - cqe1->wr_id = cqe->wr_id; - cqe1->qp = cqe->qp ? cqe->qp : wc->qp_num; - cqe1->opcode = cqe->opcode; - cqe1->status = wc->status; - cqe1->byte_len = wc->byte_len; - cqe1->src_qp = wc->src_qp; - cqe1->wc_flags = wc->wc_flags; - cqe1->vendor_err = wc->vendor_err; - - trace_pvrdma_post_cqe(cq_handle, cq->notify, cqe1->wr_id, cqe1->qp, - cqe1->opcode, cqe1->status, cqe1->byte_len, - cqe1->src_qp, cqe1->wc_flags, cqe1->vendor_err); - - pvrdma_ring_write_inc(ring); - - /* Step #2: Put CQ number on dsr completion ring */ - cqne = pvrdma_ring_next_elem_write(&dev->dsr_info.cq); - if (unlikely(!cqne)) { - return -EINVAL; - } - - cqne->info = cq_handle; - pvrdma_ring_write_inc(&dev->dsr_info.cq); - - if (cq->notify != CNT_CLEAR) { - if (cq->notify == CNT_ARM) { - cq->notify = CNT_CLEAR; - } - post_interrupt(dev, INTR_VEC_CMD_COMPLETION_Q); - } - - return 0; -} - -static void pvrdma_qp_ops_comp_handler(void *ctx, struct ibv_wc *wc) -{ - CompHandlerCtx *comp_ctx = (CompHandlerCtx *)ctx; - - pvrdma_post_cqe(comp_ctx->dev, comp_ctx->cq_handle, &comp_ctx->cqe, wc); - - g_free(ctx); -} - -static void complete_with_error(uint32_t vendor_err, void *ctx) -{ - struct ibv_wc wc = {}; - - wc.status = IBV_WC_GENERAL_ERR; - wc.vendor_err = vendor_err; - - pvrdma_qp_ops_comp_handler(ctx, &wc); -} - -void pvrdma_qp_ops_fini(void) -{ - rdma_backend_unregister_comp_handler(); -} - -int pvrdma_qp_ops_init(void) -{ - rdma_backend_register_comp_handler(pvrdma_qp_ops_comp_handler); - - return 0; -} - -void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) -{ - RdmaRmQP *qp; - PvrdmaSqWqe *wqe; - PvrdmaRing *ring; - int sgid_idx; - union ibv_gid *sgid; - - qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle); - if (unlikely(!qp)) { - return; - } - - ring = (PvrdmaRing *)qp->opaque; - - wqe = pvrdma_ring_next_elem_read(ring); - while (wqe) { - CompHandlerCtx *comp_ctx; - - /* Prepare CQE */ - comp_ctx = g_new(CompHandlerCtx, 1); - comp_ctx->dev = dev; - comp_ctx->cq_handle = qp->send_cq_handle; - comp_ctx->cqe.wr_id = wqe->hdr.wr_id; - comp_ctx->cqe.qp = qp_handle; - comp_ctx->cqe.opcode = IBV_WC_SEND; - - sgid = rdma_rm_get_gid(&dev->rdma_dev_res, wqe->hdr.wr.ud.av.gid_index); - if (!sgid) { - rdma_error_report("Failed to get gid for idx %d", - wqe->hdr.wr.ud.av.gid_index); - complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx); - continue; - } - - sgid_idx = rdma_rm_get_backend_gid_index(&dev->rdma_dev_res, - &dev->backend_dev, - wqe->hdr.wr.ud.av.gid_index); - if (sgid_idx <= 0) { - rdma_error_report("Failed to get bk sgid_idx for sgid_idx %d", - wqe->hdr.wr.ud.av.gid_index); - complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx); - continue; - } - - if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { - rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, - dev->dev_attr.max_sge); - complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); - continue; - } - - rdma_backend_post_send(&dev->backend_dev, &qp->backend_qp, qp->qp_type, - (struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge, - sgid_idx, sgid, - (union ibv_gid *)wqe->hdr.wr.ud.av.dgid, - wqe->hdr.wr.ud.remote_qpn, - wqe->hdr.wr.ud.remote_qkey, comp_ctx); - - pvrdma_ring_read_inc(ring); - - wqe = pvrdma_ring_next_elem_read(ring); - } -} - -void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) -{ - RdmaRmQP *qp; - PvrdmaRqWqe *wqe; - PvrdmaRing *ring; - - qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle); - if (unlikely(!qp)) { - return; - } - - ring = &((PvrdmaRing *)qp->opaque)[1]; - - wqe = pvrdma_ring_next_elem_read(ring); - while (wqe) { - CompHandlerCtx *comp_ctx; - - /* Prepare CQE */ - comp_ctx = g_new(CompHandlerCtx, 1); - comp_ctx->dev = dev; - comp_ctx->cq_handle = qp->recv_cq_handle; - comp_ctx->cqe.wr_id = wqe->hdr.wr_id; - comp_ctx->cqe.qp = qp_handle; - comp_ctx->cqe.opcode = IBV_WC_RECV; - - if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { - rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, - dev->dev_attr.max_sge); - complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); - continue; - } - - rdma_backend_post_recv(&dev->backend_dev, &qp->backend_qp, qp->qp_type, - (struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge, - comp_ctx); - - pvrdma_ring_read_inc(ring); - - wqe = pvrdma_ring_next_elem_read(ring); - } -} - -void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle) -{ - RdmaRmSRQ *srq; - PvrdmaRqWqe *wqe; - PvrdmaRing *ring; - - srq = rdma_rm_get_srq(&dev->rdma_dev_res, srq_handle); - if (unlikely(!srq)) { - return; - } - - ring = (PvrdmaRing *)srq->opaque; - - wqe = pvrdma_ring_next_elem_read(ring); - while (wqe) { - CompHandlerCtx *comp_ctx; - - /* Prepare CQE */ - comp_ctx = g_new(CompHandlerCtx, 1); - comp_ctx->dev = dev; - comp_ctx->cq_handle = srq->recv_cq_handle; - comp_ctx->cqe.wr_id = wqe->hdr.wr_id; - comp_ctx->cqe.qp = 0; - comp_ctx->cqe.opcode = IBV_WC_RECV; - - if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { - rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, - dev->dev_attr.max_sge); - complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); - continue; - } - - rdma_backend_post_srq_recv(&dev->backend_dev, &srq->backend_srq, - (struct ibv_sge *)&wqe->sge[0], - wqe->hdr.num_sge, - comp_ctx); - - pvrdma_ring_read_inc(ring); - - wqe = pvrdma_ring_next_elem_read(ring); - } - -} - -void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) -{ - RdmaRmCQ *cq; - - cq = rdma_rm_get_cq(dev_res, cq_handle); - if (!cq) { - return; - } - - rdma_backend_poll_cq(dev_res, &cq->backend_cq); -} diff --git a/hw/rdma/vmw/pvrdma_qp_ops.h b/hw/rdma/vmw/pvrdma_qp_ops.h deleted file mode 100644 index bf2b15c5ce..0000000000 --- a/hw/rdma/vmw/pvrdma_qp_ops.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * QEMU VMWARE paravirtual RDMA QP Operations - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef PVRDMA_QP_OPS_H -#define PVRDMA_QP_OPS_H - -#include "pvrdma.h" - -int pvrdma_qp_ops_init(void); -void pvrdma_qp_ops_fini(void); -void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle); -void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle); -void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle); -void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle); - -#endif diff --git a/hw/rdma/vmw/trace-events b/hw/rdma/vmw/trace-events deleted file mode 100644 index a6c77e1e10..0000000000 --- a/hw/rdma/vmw/trace-events +++ /dev/null @@ -1,17 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# pvrdma_main.c -pvrdma_regs_read(uint64_t addr, uint64_t val) "pvrdma.regs[0x%"PRIx64"]=0x%"PRIx64 -pvrdma_regs_write(uint64_t addr, uint64_t val, const char *reg_name, const char *val_name) "pvrdma.regs[0x%"PRIx64"]=0x%"PRIx64" (%s %s)" -pvrdma_uar_write(uint64_t addr, uint64_t val, const char *reg_name, const char *val_name, int val1, int val2) "uar[0x%"PRIx64"]=0x%"PRIx64" (cls=%s, op=%s, obj=%d, val=%d)" - -# pvrdma_cmd.c -pvrdma_map_to_pdir_host_virt(void *vfirst, void *vremaped) "mremap %p -> %p" -pvrdma_map_to_pdir_next_page(int page_idx, void *vnext, void *vremaped) "mremap [%d] %p -> %p" -pvrdma_exec_cmd(int cmd, int err) "cmd=%d, err=%d" - -# pvrdma_dev_ring.c -pvrdma_ring_next_elem_read_no_data(char *ring_name) "pvrdma_ring %s is empty" - -# pvrdma_qp_ops.c -pvrdma_post_cqe(uint32_t cq_handle, int notify, uint64_t wr_id, uint64_t qpn, uint32_t op_code, uint32_t status, uint32_t byte_len, uint32_t src_qp, uint32_t wc_flags, uint32_t vendor_err) "cq_handle=%d, notify=%d, wr_id=0x%"PRIx64", qpn=0x%"PRIx64", opcode=%d, status=%d, byte_len=%d, src_qp=%d, wc_flags=%d, vendor_err=%d" diff --git a/hw/rdma/vmw/trace.h b/hw/rdma/vmw/trace.h deleted file mode 100644 index 3ebc9fb7ad..0000000000 --- a/hw/rdma/vmw/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-hw_rdma_vmw.h" diff --git a/include/hw/rdma/rdma.h b/include/hw/rdma/rdma.h deleted file mode 100644 index 80b2e531c4..0000000000 --- a/include/hw/rdma/rdma.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * RDMA device interface - * - * Copyright (C) 2019 Oracle - * Copyright (C) 2019 Red Hat Inc - * - * Authors: - * Yuval Shaia - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef RDMA_H -#define RDMA_H - -#include "qom/object.h" - -#define INTERFACE_RDMA_PROVIDER "rdma" - -typedef struct RdmaProviderClass RdmaProviderClass; -DECLARE_CLASS_CHECKERS(RdmaProviderClass, RDMA_PROVIDER, - INTERFACE_RDMA_PROVIDER) -#define RDMA_PROVIDER(obj) \ - INTERFACE_CHECK(RdmaProvider, (obj), \ - INTERFACE_RDMA_PROVIDER) - -typedef struct RdmaProvider RdmaProvider; - -struct RdmaProviderClass { - InterfaceClass parent; - - void (*format_statistics)(RdmaProvider *obj, GString *buf); -}; - -#endif diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 13f9a2dedb..f4cf8f6717 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -37,7 +37,6 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict); void hmp_info_balloon(Monitor *mon, const QDict *qdict); void hmp_info_irq(Monitor *mon, const QDict *qdict); void hmp_info_pic(Monitor *mon, const QDict *qdict); -void hmp_info_rdma(Monitor *mon, const QDict *qdict); void hmp_info_pci(Monitor *mon, const QDict *qdict); void hmp_info_tpm(Monitor *mon, const QDict *qdict); void hmp_info_iothreads(Monitor *mon, const QDict *qdict); diff --git a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h deleted file mode 100644 index a5a1c8234e..0000000000 --- a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h +++ /dev/null @@ -1,685 +0,0 @@ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program 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 General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __PVRDMA_DEV_API_H__ -#define __PVRDMA_DEV_API_H__ - -#include "standard-headers/linux/types.h" - -#include "pvrdma_verbs.h" - -/* - * PVRDMA version macros. Some new features require updates to PVRDMA_VERSION. - * These macros allow us to check for different features if necessary. - */ - -#define PVRDMA_ROCEV1_VERSION 17 -#define PVRDMA_ROCEV2_VERSION 18 -#define PVRDMA_PPN64_VERSION 19 -#define PVRDMA_QPHANDLE_VERSION 20 -#define PVRDMA_VERSION PVRDMA_QPHANDLE_VERSION - -#define PVRDMA_BOARD_ID 1 -#define PVRDMA_REV_ID 1 - -/* - * Masks and accessors for page directory, which is a two-level lookup: - * page directory -> page table -> page. Only one directory for now, but we - * could expand that easily. 9 bits for tables, 9 bits for pages, gives one - * gigabyte for memory regions and so forth. - */ - -#define PVRDMA_PDIR_SHIFT 18 -#define PVRDMA_PTABLE_SHIFT 9 -#define PVRDMA_PAGE_DIR_DIR(x) (((x) >> PVRDMA_PDIR_SHIFT) & 0x1) -#define PVRDMA_PAGE_DIR_TABLE(x) (((x) >> PVRDMA_PTABLE_SHIFT) & 0x1ff) -#define PVRDMA_PAGE_DIR_PAGE(x) ((x) & 0x1ff) -#define PVRDMA_PAGE_DIR_MAX_PAGES (1 * 512 * 512) -#define PVRDMA_MAX_FAST_REG_PAGES 128 - -/* - * Max MSI-X vectors. - */ - -#define PVRDMA_MAX_INTERRUPTS 3 - -/* Register offsets within PCI resource on BAR1. */ -#define PVRDMA_REG_VERSION 0x00 /* R: Version of device. */ -#define PVRDMA_REG_DSRLOW 0x04 /* W: Device shared region low PA. */ -#define PVRDMA_REG_DSRHIGH 0x08 /* W: Device shared region high PA. */ -#define PVRDMA_REG_CTL 0x0c /* W: PVRDMA_DEVICE_CTL */ -#define PVRDMA_REG_REQUEST 0x10 /* W: Indicate device request. */ -#define PVRDMA_REG_ERR 0x14 /* R: Device error. */ -#define PVRDMA_REG_ICR 0x18 /* R: Interrupt cause. */ -#define PVRDMA_REG_IMR 0x1c /* R/W: Interrupt mask. */ -#define PVRDMA_REG_MACL 0x20 /* R/W: MAC address low. */ -#define PVRDMA_REG_MACH 0x24 /* R/W: MAC address high. */ - -/* Object flags. */ -#define PVRDMA_CQ_FLAG_ARMED_SOL BIT(0) /* Armed for solicited-only. */ -#define PVRDMA_CQ_FLAG_ARMED BIT(1) /* Armed. */ -#define PVRDMA_MR_FLAG_DMA BIT(0) /* DMA region. */ -#define PVRDMA_MR_FLAG_FRMR BIT(1) /* Fast reg memory region. */ - -/* - * Atomic operation capability (masked versions are extended atomic - * operations. - */ - -#define PVRDMA_ATOMIC_OP_COMP_SWAP BIT(0) /* Compare and swap. */ -#define PVRDMA_ATOMIC_OP_FETCH_ADD BIT(1) /* Fetch and add. */ -#define PVRDMA_ATOMIC_OP_MASK_COMP_SWAP BIT(2) /* Masked compare and swap. */ -#define PVRDMA_ATOMIC_OP_MASK_FETCH_ADD BIT(3) /* Masked fetch and add. */ - -/* - * Base Memory Management Extension flags to support Fast Reg Memory Regions - * and Fast Reg Work Requests. Each flag represents a verb operation and we - * must support all of them to qualify for the BMME device cap. - */ - -#define PVRDMA_BMME_FLAG_LOCAL_INV BIT(0) /* Local Invalidate. */ -#define PVRDMA_BMME_FLAG_REMOTE_INV BIT(1) /* Remote Invalidate. */ -#define PVRDMA_BMME_FLAG_FAST_REG_WR BIT(2) /* Fast Reg Work Request. */ - -/* - * GID types. The interpretation of the gid_types bit field in the device - * capabilities will depend on the device mode. For now, the device only - * supports RoCE as mode, so only the different GID types for RoCE are - * defined. - */ - -#define PVRDMA_GID_TYPE_FLAG_ROCE_V1 BIT(0) -#define PVRDMA_GID_TYPE_FLAG_ROCE_V2 BIT(1) - -/* - * Version checks. This checks whether each version supports specific - * capabilities from the device. - */ - -#define PVRDMA_IS_VERSION17(_dev) \ - (_dev->dsr_version == PVRDMA_ROCEV1_VERSION && \ - _dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V1) - -#define PVRDMA_IS_VERSION18(_dev) \ - (_dev->dsr_version >= PVRDMA_ROCEV2_VERSION && \ - (_dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V1 || \ - _dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V2)) \ - -#define PVRDMA_SUPPORTED(_dev) \ - ((_dev->dsr->caps.mode == PVRDMA_DEVICE_MODE_ROCE) && \ - (PVRDMA_IS_VERSION17(_dev) || PVRDMA_IS_VERSION18(_dev))) - -/* - * Get capability values based on device version. - */ - -#define PVRDMA_GET_CAP(_dev, _old_val, _val) \ - ((PVRDMA_IS_VERSION18(_dev)) ? _val : _old_val) - -enum pvrdma_pci_resource { - PVRDMA_PCI_RESOURCE_MSIX, /* BAR0: MSI-X, MMIO. */ - PVRDMA_PCI_RESOURCE_REG, /* BAR1: Registers, MMIO. */ - PVRDMA_PCI_RESOURCE_UAR, /* BAR2: UAR pages, MMIO, 64-bit. */ - PVRDMA_PCI_RESOURCE_LAST, /* Last. */ -}; - -enum pvrdma_device_ctl { - PVRDMA_DEVICE_CTL_ACTIVATE, /* Activate device. */ - PVRDMA_DEVICE_CTL_UNQUIESCE, /* Unquiesce device. */ - PVRDMA_DEVICE_CTL_RESET, /* Reset device. */ -}; - -enum pvrdma_intr_vector { - PVRDMA_INTR_VECTOR_RESPONSE, /* Command response. */ - PVRDMA_INTR_VECTOR_ASYNC, /* Async events. */ - PVRDMA_INTR_VECTOR_CQ, /* CQ notification. */ - /* Additional CQ notification vectors. */ -}; - -enum pvrdma_intr_cause { - PVRDMA_INTR_CAUSE_RESPONSE = (1 << PVRDMA_INTR_VECTOR_RESPONSE), - PVRDMA_INTR_CAUSE_ASYNC = (1 << PVRDMA_INTR_VECTOR_ASYNC), - PVRDMA_INTR_CAUSE_CQ = (1 << PVRDMA_INTR_VECTOR_CQ), -}; - -enum pvrdma_gos_bits { - PVRDMA_GOS_BITS_UNK, /* Unknown. */ - PVRDMA_GOS_BITS_32, /* 32-bit. */ - PVRDMA_GOS_BITS_64, /* 64-bit. */ -}; - -enum pvrdma_gos_type { - PVRDMA_GOS_TYPE_UNK, /* Unknown. */ - PVRDMA_GOS_TYPE_LINUX, /* Linux. */ -}; - -enum pvrdma_device_mode { - PVRDMA_DEVICE_MODE_ROCE, /* RoCE. */ - PVRDMA_DEVICE_MODE_IWARP, /* iWarp. */ - PVRDMA_DEVICE_MODE_IB, /* InfiniBand. */ -}; - -struct pvrdma_gos_info { - uint32_t gos_bits:2; /* W: PVRDMA_GOS_BITS_ */ - uint32_t gos_type:4; /* W: PVRDMA_GOS_TYPE_ */ - uint32_t gos_ver:16; /* W: Guest OS version. */ - uint32_t gos_misc:10; /* W: Other. */ - uint32_t pad; /* Pad to 8-byte alignment. */ -}; - -struct pvrdma_device_caps { - uint64_t fw_ver; /* R: Query device. */ - uint64_t node_guid; - uint64_t sys_image_guid; - uint64_t max_mr_size; - uint64_t page_size_cap; - uint64_t atomic_arg_sizes; /* EX verbs. */ - uint32_t ex_comp_mask; /* EX verbs. */ - uint32_t device_cap_flags2; /* EX verbs. */ - uint32_t max_fa_bit_boundary; /* EX verbs. */ - uint32_t log_max_atomic_inline_arg; /* EX verbs. */ - uint32_t vendor_id; - uint32_t vendor_part_id; - uint32_t hw_ver; - uint32_t max_qp; - uint32_t max_qp_wr; - uint32_t device_cap_flags; - uint32_t max_sge; - uint32_t max_sge_rd; - uint32_t max_cq; - uint32_t max_cqe; - uint32_t max_mr; - uint32_t max_pd; - uint32_t max_qp_rd_atom; - uint32_t max_ee_rd_atom; - uint32_t max_res_rd_atom; - uint32_t max_qp_init_rd_atom; - uint32_t max_ee_init_rd_atom; - uint32_t max_ee; - uint32_t max_rdd; - uint32_t max_mw; - uint32_t max_raw_ipv6_qp; - uint32_t max_raw_ethy_qp; - uint32_t max_mcast_grp; - uint32_t max_mcast_qp_attach; - uint32_t max_total_mcast_qp_attach; - uint32_t max_ah; - uint32_t max_fmr; - uint32_t max_map_per_fmr; - uint32_t max_srq; - uint32_t max_srq_wr; - uint32_t max_srq_sge; - uint32_t max_uar; - uint32_t gid_tbl_len; - uint16_t max_pkeys; - uint8_t local_ca_ack_delay; - uint8_t phys_port_cnt; - uint8_t mode; /* PVRDMA_DEVICE_MODE_ */ - uint8_t atomic_ops; /* PVRDMA_ATOMIC_OP_* bits */ - uint8_t bmme_flags; /* FRWR Mem Mgmt Extensions */ - uint8_t gid_types; /* PVRDMA_GID_TYPE_FLAG_ */ - uint32_t max_fast_reg_page_list_len; -}; - -struct pvrdma_ring_page_info { - uint32_t num_pages; /* Num pages incl. header. */ - uint32_t reserved; /* Reserved. */ - uint64_t pdir_dma; /* Page directory PA. */ -}; - -#pragma pack(push, 1) - -struct pvrdma_device_shared_region { - uint32_t driver_version; /* W: Driver version. */ - uint32_t pad; /* Pad to 8-byte align. */ - struct pvrdma_gos_info gos_info; /* W: Guest OS information. */ - uint64_t cmd_slot_dma; /* W: Command slot address. */ - uint64_t resp_slot_dma; /* W: Response slot address. */ - struct pvrdma_ring_page_info async_ring_pages; - /* W: Async ring page info. */ - struct pvrdma_ring_page_info cq_ring_pages; - /* W: CQ ring page info. */ - union { - uint32_t uar_pfn; /* W: UAR pageframe. */ - uint64_t uar_pfn64; /* W: 64-bit UAR page frame. */ - }; - struct pvrdma_device_caps caps; /* R: Device capabilities. */ -}; - -#pragma pack(pop) - -/* Event types. Currently a 1:1 mapping with enum ib_event. */ -enum pvrdma_eqe_type { - PVRDMA_EVENT_CQ_ERR, - PVRDMA_EVENT_QP_FATAL, - PVRDMA_EVENT_QP_REQ_ERR, - PVRDMA_EVENT_QP_ACCESS_ERR, - PVRDMA_EVENT_COMM_EST, - PVRDMA_EVENT_SQ_DRAINED, - PVRDMA_EVENT_PATH_MIG, - PVRDMA_EVENT_PATH_MIG_ERR, - PVRDMA_EVENT_DEVICE_FATAL, - PVRDMA_EVENT_PORT_ACTIVE, - PVRDMA_EVENT_PORT_ERR, - PVRDMA_EVENT_LID_CHANGE, - PVRDMA_EVENT_PKEY_CHANGE, - PVRDMA_EVENT_SM_CHANGE, - PVRDMA_EVENT_SRQ_ERR, - PVRDMA_EVENT_SRQ_LIMIT_REACHED, - PVRDMA_EVENT_QP_LAST_WQE_REACHED, - PVRDMA_EVENT_CLIENT_REREGISTER, - PVRDMA_EVENT_GID_CHANGE, -}; - -/* Event queue element. */ -struct pvrdma_eqe { - uint32_t type; /* Event type. */ - uint32_t info; /* Handle, other. */ -}; - -/* CQ notification queue element. */ -struct pvrdma_cqne { - uint32_t info; /* Handle */ -}; - -enum { - PVRDMA_CMD_FIRST, - PVRDMA_CMD_QUERY_PORT = PVRDMA_CMD_FIRST, - PVRDMA_CMD_QUERY_PKEY, - PVRDMA_CMD_CREATE_PD, - PVRDMA_CMD_DESTROY_PD, - PVRDMA_CMD_CREATE_MR, - PVRDMA_CMD_DESTROY_MR, - PVRDMA_CMD_CREATE_CQ, - PVRDMA_CMD_RESIZE_CQ, - PVRDMA_CMD_DESTROY_CQ, - PVRDMA_CMD_CREATE_QP, - PVRDMA_CMD_MODIFY_QP, - PVRDMA_CMD_QUERY_QP, - PVRDMA_CMD_DESTROY_QP, - PVRDMA_CMD_CREATE_UC, - PVRDMA_CMD_DESTROY_UC, - PVRDMA_CMD_CREATE_BIND, - PVRDMA_CMD_DESTROY_BIND, - PVRDMA_CMD_CREATE_SRQ, - PVRDMA_CMD_MODIFY_SRQ, - PVRDMA_CMD_QUERY_SRQ, - PVRDMA_CMD_DESTROY_SRQ, - PVRDMA_CMD_MAX, -}; - -enum { - PVRDMA_CMD_FIRST_RESP = (1 << 31), - PVRDMA_CMD_QUERY_PORT_RESP = PVRDMA_CMD_FIRST_RESP, - PVRDMA_CMD_QUERY_PKEY_RESP, - PVRDMA_CMD_CREATE_PD_RESP, - PVRDMA_CMD_DESTROY_PD_RESP_NOOP, - PVRDMA_CMD_CREATE_MR_RESP, - PVRDMA_CMD_DESTROY_MR_RESP_NOOP, - PVRDMA_CMD_CREATE_CQ_RESP, - PVRDMA_CMD_RESIZE_CQ_RESP, - PVRDMA_CMD_DESTROY_CQ_RESP_NOOP, - PVRDMA_CMD_CREATE_QP_RESP, - PVRDMA_CMD_MODIFY_QP_RESP, - PVRDMA_CMD_QUERY_QP_RESP, - PVRDMA_CMD_DESTROY_QP_RESP, - PVRDMA_CMD_CREATE_UC_RESP, - PVRDMA_CMD_DESTROY_UC_RESP_NOOP, - PVRDMA_CMD_CREATE_BIND_RESP_NOOP, - PVRDMA_CMD_DESTROY_BIND_RESP_NOOP, - PVRDMA_CMD_CREATE_SRQ_RESP, - PVRDMA_CMD_MODIFY_SRQ_RESP, - PVRDMA_CMD_QUERY_SRQ_RESP, - PVRDMA_CMD_DESTROY_SRQ_RESP, - PVRDMA_CMD_MAX_RESP, -}; - -struct pvrdma_cmd_hdr { - uint64_t response; /* Key for response lookup. */ - uint32_t cmd; /* PVRDMA_CMD_ */ - uint32_t reserved; /* Reserved. */ -}; - -struct pvrdma_cmd_resp_hdr { - uint64_t response; /* From cmd hdr. */ - uint32_t ack; /* PVRDMA_CMD_XXX_RESP */ - uint8_t err; /* Error. */ - uint8_t reserved[3]; /* Reserved. */ -}; - -struct pvrdma_cmd_query_port { - struct pvrdma_cmd_hdr hdr; - uint8_t port_num; - uint8_t reserved[7]; -}; - -struct pvrdma_cmd_query_port_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_port_attr attrs; -}; - -struct pvrdma_cmd_query_pkey { - struct pvrdma_cmd_hdr hdr; - uint8_t port_num; - uint8_t index; - uint8_t reserved[6]; -}; - -struct pvrdma_cmd_query_pkey_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint16_t pkey; - uint8_t reserved[6]; -}; - -struct pvrdma_cmd_create_uc { - struct pvrdma_cmd_hdr hdr; - union { - uint32_t pfn; /* UAR page frame number */ - uint64_t pfn64; /* 64-bit UAR page frame number */ - }; -}; - -struct pvrdma_cmd_create_uc_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t ctx_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_uc { - struct pvrdma_cmd_hdr hdr; - uint32_t ctx_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_pd { - struct pvrdma_cmd_hdr hdr; - uint32_t ctx_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_pd_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t pd_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_pd { - struct pvrdma_cmd_hdr hdr; - uint32_t pd_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_mr { - struct pvrdma_cmd_hdr hdr; - uint64_t start; - uint64_t length; - uint64_t pdir_dma; - uint32_t pd_handle; - uint32_t access_flags; - uint32_t flags; - uint32_t nchunks; -}; - -struct pvrdma_cmd_create_mr_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t mr_handle; - uint32_t lkey; - uint32_t rkey; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_mr { - struct pvrdma_cmd_hdr hdr; - uint32_t mr_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_cq { - struct pvrdma_cmd_hdr hdr; - uint64_t pdir_dma; - uint32_t ctx_handle; - uint32_t cqe; - uint32_t nchunks; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_cq_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t cq_handle; - uint32_t cqe; -}; - -struct pvrdma_cmd_resize_cq { - struct pvrdma_cmd_hdr hdr; - uint32_t cq_handle; - uint32_t cqe; -}; - -struct pvrdma_cmd_resize_cq_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t cqe; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_cq { - struct pvrdma_cmd_hdr hdr; - uint32_t cq_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_srq { - struct pvrdma_cmd_hdr hdr; - uint64_t pdir_dma; - uint32_t pd_handle; - uint32_t nchunks; - struct pvrdma_srq_attr attrs; - uint8_t srq_type; - uint8_t reserved[7]; -}; - -struct pvrdma_cmd_create_srq_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t srqn; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_modify_srq { - struct pvrdma_cmd_hdr hdr; - uint32_t srq_handle; - uint32_t attr_mask; - struct pvrdma_srq_attr attrs; -}; - -struct pvrdma_cmd_query_srq { - struct pvrdma_cmd_hdr hdr; - uint32_t srq_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_query_srq_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_srq_attr attrs; -}; - -struct pvrdma_cmd_destroy_srq { - struct pvrdma_cmd_hdr hdr; - uint32_t srq_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_qp { - struct pvrdma_cmd_hdr hdr; - uint64_t pdir_dma; - uint32_t pd_handle; - uint32_t send_cq_handle; - uint32_t recv_cq_handle; - uint32_t srq_handle; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; - uint32_t lkey; - uint32_t access_flags; - uint16_t total_chunks; - uint16_t send_chunks; - uint16_t max_atomic_arg; - uint8_t sq_sig_all; - uint8_t qp_type; - uint8_t is_srq; - uint8_t reserved[3]; -}; - -struct pvrdma_cmd_create_qp_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t qpn; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; -}; - -struct pvrdma_cmd_create_qp_resp_v2 { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t qpn; - uint32_t qp_handle; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; -}; - -struct pvrdma_cmd_modify_qp { - struct pvrdma_cmd_hdr hdr; - uint32_t qp_handle; - uint32_t attr_mask; - struct pvrdma_qp_attr attrs; -}; - -struct pvrdma_cmd_query_qp { - struct pvrdma_cmd_hdr hdr; - uint32_t qp_handle; - uint32_t attr_mask; -}; - -struct pvrdma_cmd_query_qp_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_qp_attr attrs; -}; - -struct pvrdma_cmd_destroy_qp { - struct pvrdma_cmd_hdr hdr; - uint32_t qp_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_qp_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t events_reported; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_bind { - struct pvrdma_cmd_hdr hdr; - uint32_t mtu; - uint32_t vlan; - uint32_t index; - uint8_t new_gid[16]; - uint8_t gid_type; - uint8_t reserved[3]; -}; - -struct pvrdma_cmd_destroy_bind { - struct pvrdma_cmd_hdr hdr; - uint32_t index; - uint8_t dest_gid[16]; - uint8_t reserved[4]; -}; - -union pvrdma_cmd_req { - struct pvrdma_cmd_hdr hdr; - struct pvrdma_cmd_query_port query_port; - struct pvrdma_cmd_query_pkey query_pkey; - struct pvrdma_cmd_create_uc create_uc; - struct pvrdma_cmd_destroy_uc destroy_uc; - struct pvrdma_cmd_create_pd create_pd; - struct pvrdma_cmd_destroy_pd destroy_pd; - struct pvrdma_cmd_create_mr create_mr; - struct pvrdma_cmd_destroy_mr destroy_mr; - struct pvrdma_cmd_create_cq create_cq; - struct pvrdma_cmd_resize_cq resize_cq; - struct pvrdma_cmd_destroy_cq destroy_cq; - struct pvrdma_cmd_create_qp create_qp; - struct pvrdma_cmd_modify_qp modify_qp; - struct pvrdma_cmd_query_qp query_qp; - struct pvrdma_cmd_destroy_qp destroy_qp; - struct pvrdma_cmd_create_bind create_bind; - struct pvrdma_cmd_destroy_bind destroy_bind; - struct pvrdma_cmd_create_srq create_srq; - struct pvrdma_cmd_modify_srq modify_srq; - struct pvrdma_cmd_query_srq query_srq; - struct pvrdma_cmd_destroy_srq destroy_srq; -}; - -union pvrdma_cmd_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_cmd_query_port_resp query_port_resp; - struct pvrdma_cmd_query_pkey_resp query_pkey_resp; - struct pvrdma_cmd_create_uc_resp create_uc_resp; - struct pvrdma_cmd_create_pd_resp create_pd_resp; - struct pvrdma_cmd_create_mr_resp create_mr_resp; - struct pvrdma_cmd_create_cq_resp create_cq_resp; - struct pvrdma_cmd_resize_cq_resp resize_cq_resp; - struct pvrdma_cmd_create_qp_resp create_qp_resp; - struct pvrdma_cmd_create_qp_resp_v2 create_qp_resp_v2; - struct pvrdma_cmd_query_qp_resp query_qp_resp; - struct pvrdma_cmd_destroy_qp_resp destroy_qp_resp; - struct pvrdma_cmd_create_srq_resp create_srq_resp; - struct pvrdma_cmd_query_srq_resp query_srq_resp; -}; - -#endif /* __PVRDMA_DEV_API_H__ */ diff --git a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h deleted file mode 100644 index 94d41b202c..0000000000 --- a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program 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 General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __PVRDMA_VERBS_H__ -#define __PVRDMA_VERBS_H__ - -#include "standard-headers/linux/types.h" - -union pvrdma_gid { - uint8_t raw[16]; - struct { - uint64_t subnet_prefix; - uint64_t interface_id; - } global; -}; - -enum pvrdma_link_layer { - PVRDMA_LINK_LAYER_UNSPECIFIED, - PVRDMA_LINK_LAYER_INFINIBAND, - PVRDMA_LINK_LAYER_ETHERNET, -}; - -enum pvrdma_mtu { - PVRDMA_MTU_256 = 1, - PVRDMA_MTU_512 = 2, - PVRDMA_MTU_1024 = 3, - PVRDMA_MTU_2048 = 4, - PVRDMA_MTU_4096 = 5, -}; - -enum pvrdma_port_state { - PVRDMA_PORT_NOP = 0, - PVRDMA_PORT_DOWN = 1, - PVRDMA_PORT_INIT = 2, - PVRDMA_PORT_ARMED = 3, - PVRDMA_PORT_ACTIVE = 4, - PVRDMA_PORT_ACTIVE_DEFER = 5, -}; - -enum pvrdma_port_cap_flags { - PVRDMA_PORT_SM = 1 << 1, - PVRDMA_PORT_NOTICE_SUP = 1 << 2, - PVRDMA_PORT_TRAP_SUP = 1 << 3, - PVRDMA_PORT_OPT_IPD_SUP = 1 << 4, - PVRDMA_PORT_AUTO_MIGR_SUP = 1 << 5, - PVRDMA_PORT_SL_MAP_SUP = 1 << 6, - PVRDMA_PORT_MKEY_NVRAM = 1 << 7, - PVRDMA_PORT_PKEY_NVRAM = 1 << 8, - PVRDMA_PORT_LED_INFO_SUP = 1 << 9, - PVRDMA_PORT_SM_DISABLED = 1 << 10, - PVRDMA_PORT_SYS_IMAGE_GUID_SUP = 1 << 11, - PVRDMA_PORT_PKEY_SW_EXT_PORT_TRAP_SUP = 1 << 12, - PVRDMA_PORT_EXTENDED_SPEEDS_SUP = 1 << 14, - PVRDMA_PORT_CM_SUP = 1 << 16, - PVRDMA_PORT_SNMP_TUNNEL_SUP = 1 << 17, - PVRDMA_PORT_REINIT_SUP = 1 << 18, - PVRDMA_PORT_DEVICE_MGMT_SUP = 1 << 19, - PVRDMA_PORT_VENDOR_CLASS_SUP = 1 << 20, - PVRDMA_PORT_DR_NOTICE_SUP = 1 << 21, - PVRDMA_PORT_CAP_MASK_NOTICE_SUP = 1 << 22, - PVRDMA_PORT_BOOT_MGMT_SUP = 1 << 23, - PVRDMA_PORT_LINK_LATENCY_SUP = 1 << 24, - PVRDMA_PORT_CLIENT_REG_SUP = 1 << 25, - PVRDMA_PORT_IP_BASED_GIDS = 1 << 26, - PVRDMA_PORT_CAP_FLAGS_MAX = PVRDMA_PORT_IP_BASED_GIDS, -}; - -enum pvrdma_port_width { - PVRDMA_WIDTH_1X = 1, - PVRDMA_WIDTH_4X = 2, - PVRDMA_WIDTH_8X = 4, - PVRDMA_WIDTH_12X = 8, -}; - -enum pvrdma_port_speed { - PVRDMA_SPEED_SDR = 1, - PVRDMA_SPEED_DDR = 2, - PVRDMA_SPEED_QDR = 4, - PVRDMA_SPEED_FDR10 = 8, - PVRDMA_SPEED_FDR = 16, - PVRDMA_SPEED_EDR = 32, -}; - -struct pvrdma_port_attr { - enum pvrdma_port_state state; - enum pvrdma_mtu max_mtu; - enum pvrdma_mtu active_mtu; - uint32_t gid_tbl_len; - uint32_t port_cap_flags; - uint32_t max_msg_sz; - uint32_t bad_pkey_cntr; - uint32_t qkey_viol_cntr; - uint16_t pkey_tbl_len; - uint16_t lid; - uint16_t sm_lid; - uint8_t lmc; - uint8_t max_vl_num; - uint8_t sm_sl; - uint8_t subnet_timeout; - uint8_t init_type_reply; - uint8_t active_width; - uint8_t active_speed; - uint8_t phys_state; - uint8_t reserved[2]; -}; - -struct pvrdma_global_route { - union pvrdma_gid dgid; - uint32_t flow_label; - uint8_t sgid_index; - uint8_t hop_limit; - uint8_t traffic_class; - uint8_t reserved; -}; - -struct pvrdma_grh { - uint32_t version_tclass_flow; - uint16_t paylen; - uint8_t next_hdr; - uint8_t hop_limit; - union pvrdma_gid sgid; - union pvrdma_gid dgid; -}; - -enum pvrdma_ah_flags { - PVRDMA_AH_GRH = 1, -}; - -enum pvrdma_rate { - PVRDMA_RATE_PORT_CURRENT = 0, - PVRDMA_RATE_2_5_GBPS = 2, - PVRDMA_RATE_5_GBPS = 5, - PVRDMA_RATE_10_GBPS = 3, - PVRDMA_RATE_20_GBPS = 6, - PVRDMA_RATE_30_GBPS = 4, - PVRDMA_RATE_40_GBPS = 7, - PVRDMA_RATE_60_GBPS = 8, - PVRDMA_RATE_80_GBPS = 9, - PVRDMA_RATE_120_GBPS = 10, - PVRDMA_RATE_14_GBPS = 11, - PVRDMA_RATE_56_GBPS = 12, - PVRDMA_RATE_112_GBPS = 13, - PVRDMA_RATE_168_GBPS = 14, - PVRDMA_RATE_25_GBPS = 15, - PVRDMA_RATE_100_GBPS = 16, - PVRDMA_RATE_200_GBPS = 17, - PVRDMA_RATE_300_GBPS = 18, -}; - -struct pvrdma_ah_attr { - struct pvrdma_global_route grh; - uint16_t dlid; - uint16_t vlan_id; - uint8_t sl; - uint8_t src_path_bits; - uint8_t static_rate; - uint8_t ah_flags; - uint8_t port_num; - uint8_t dmac[6]; - uint8_t reserved; -}; - -enum pvrdma_cq_notify_flags { - PVRDMA_CQ_SOLICITED = 1 << 0, - PVRDMA_CQ_NEXT_COMP = 1 << 1, - PVRDMA_CQ_SOLICITED_MASK = PVRDMA_CQ_SOLICITED | - PVRDMA_CQ_NEXT_COMP, - PVRDMA_CQ_REPORT_MISSED_EVENTS = 1 << 2, -}; - -struct pvrdma_qp_cap { - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; - uint32_t reserved; -}; - -enum pvrdma_sig_type { - PVRDMA_SIGNAL_ALL_WR, - PVRDMA_SIGNAL_REQ_WR, -}; - -enum pvrdma_qp_type { - PVRDMA_QPT_SMI, - PVRDMA_QPT_GSI, - PVRDMA_QPT_RC, - PVRDMA_QPT_UC, - PVRDMA_QPT_UD, - PVRDMA_QPT_RAW_IPV6, - PVRDMA_QPT_RAW_ETHERTYPE, - PVRDMA_QPT_RAW_PACKET = 8, - PVRDMA_QPT_XRC_INI = 9, - PVRDMA_QPT_XRC_TGT, - PVRDMA_QPT_MAX, -}; - -enum pvrdma_qp_create_flags { - PVRDMA_QP_CREATE_IPOPVRDMA_UD_LSO = 1 << 0, - PVRDMA_QP_CREATE_BLOCK_MULTICAST_LOOPBACK = 1 << 1, -}; - -enum pvrdma_qp_attr_mask { - PVRDMA_QP_STATE = 1 << 0, - PVRDMA_QP_CUR_STATE = 1 << 1, - PVRDMA_QP_EN_SQD_ASYNC_NOTIFY = 1 << 2, - PVRDMA_QP_ACCESS_FLAGS = 1 << 3, - PVRDMA_QP_PKEY_INDEX = 1 << 4, - PVRDMA_QP_PORT = 1 << 5, - PVRDMA_QP_QKEY = 1 << 6, - PVRDMA_QP_AV = 1 << 7, - PVRDMA_QP_PATH_MTU = 1 << 8, - PVRDMA_QP_TIMEOUT = 1 << 9, - PVRDMA_QP_RETRY_CNT = 1 << 10, - PVRDMA_QP_RNR_RETRY = 1 << 11, - PVRDMA_QP_RQ_PSN = 1 << 12, - PVRDMA_QP_MAX_QP_RD_ATOMIC = 1 << 13, - PVRDMA_QP_ALT_PATH = 1 << 14, - PVRDMA_QP_MIN_RNR_TIMER = 1 << 15, - PVRDMA_QP_SQ_PSN = 1 << 16, - PVRDMA_QP_MAX_DEST_RD_ATOMIC = 1 << 17, - PVRDMA_QP_PATH_MIG_STATE = 1 << 18, - PVRDMA_QP_CAP = 1 << 19, - PVRDMA_QP_DEST_QPN = 1 << 20, - PVRDMA_QP_ATTR_MASK_MAX = PVRDMA_QP_DEST_QPN, -}; - -enum pvrdma_qp_state { - PVRDMA_QPS_RESET, - PVRDMA_QPS_INIT, - PVRDMA_QPS_RTR, - PVRDMA_QPS_RTS, - PVRDMA_QPS_SQD, - PVRDMA_QPS_SQE, - PVRDMA_QPS_ERR, -}; - -enum pvrdma_mig_state { - PVRDMA_MIG_MIGRATED, - PVRDMA_MIG_REARM, - PVRDMA_MIG_ARMED, -}; - -enum pvrdma_mw_type { - PVRDMA_MW_TYPE_1 = 1, - PVRDMA_MW_TYPE_2 = 2, -}; - -struct pvrdma_srq_attr { - uint32_t max_wr; - uint32_t max_sge; - uint32_t srq_limit; - uint32_t reserved; -}; - -struct pvrdma_qp_attr { - enum pvrdma_qp_state qp_state; - enum pvrdma_qp_state cur_qp_state; - enum pvrdma_mtu path_mtu; - enum pvrdma_mig_state path_mig_state; - uint32_t qkey; - uint32_t rq_psn; - uint32_t sq_psn; - uint32_t dest_qp_num; - uint32_t qp_access_flags; - uint16_t pkey_index; - uint16_t alt_pkey_index; - uint8_t en_sqd_async_notify; - uint8_t sq_draining; - uint8_t max_rd_atomic; - uint8_t max_dest_rd_atomic; - uint8_t min_rnr_timer; - uint8_t port_num; - uint8_t timeout; - uint8_t retry_cnt; - uint8_t rnr_retry; - uint8_t alt_port_num; - uint8_t alt_timeout; - uint8_t reserved[5]; - struct pvrdma_qp_cap cap; - struct pvrdma_ah_attr ah_attr; - struct pvrdma_ah_attr alt_ah_attr; -}; - -enum pvrdma_send_flags { - PVRDMA_SEND_FENCE = 1 << 0, - PVRDMA_SEND_SIGNALED = 1 << 1, - PVRDMA_SEND_SOLICITED = 1 << 2, - PVRDMA_SEND_INLINE = 1 << 3, - PVRDMA_SEND_IP_CSUM = 1 << 4, - PVRDMA_SEND_FLAGS_MAX = PVRDMA_SEND_IP_CSUM, -}; - -enum pvrdma_access_flags { - PVRDMA_ACCESS_LOCAL_WRITE = 1 << 0, - PVRDMA_ACCESS_REMOTE_WRITE = 1 << 1, - PVRDMA_ACCESS_REMOTE_READ = 1 << 2, - PVRDMA_ACCESS_REMOTE_ATOMIC = 1 << 3, - PVRDMA_ACCESS_MW_BIND = 1 << 4, - PVRDMA_ZERO_BASED = 1 << 5, - PVRDMA_ACCESS_ON_DEMAND = 1 << 6, - PVRDMA_ACCESS_FLAGS_MAX = PVRDMA_ACCESS_ON_DEMAND, -}; - -#endif /* __PVRDMA_VERBS_H__ */ diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h deleted file mode 100644 index c30182a7ae..0000000000 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ /dev/null @@ -1,310 +0,0 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program 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 General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __VMW_PVRDMA_ABI_H__ -#define __VMW_PVRDMA_ABI_H__ - -#include "standard-headers/linux/types.h" - -#define PVRDMA_UVERBS_ABI_VERSION 3 /* ABI Version. */ -#define PVRDMA_UAR_HANDLE_MASK 0x00FFFFFF /* Bottom 24 bits. */ -#define PVRDMA_UAR_QP_OFFSET 0 /* QP doorbell. */ -#define PVRDMA_UAR_QP_SEND (1 << 30) /* Send bit. */ -#define PVRDMA_UAR_QP_RECV (1 << 31) /* Recv bit. */ -#define PVRDMA_UAR_CQ_OFFSET 4 /* CQ doorbell. */ -#define PVRDMA_UAR_CQ_ARM_SOL (1 << 29) /* Arm solicited bit. */ -#define PVRDMA_UAR_CQ_ARM (1 << 30) /* Arm bit. */ -#define PVRDMA_UAR_CQ_POLL (1 << 31) /* Poll bit. */ -#define PVRDMA_UAR_SRQ_OFFSET 8 /* SRQ doorbell. */ -#define PVRDMA_UAR_SRQ_RECV (1 << 30) /* Recv bit. */ - -enum pvrdma_wr_opcode { - PVRDMA_WR_RDMA_WRITE, - PVRDMA_WR_RDMA_WRITE_WITH_IMM, - PVRDMA_WR_SEND, - PVRDMA_WR_SEND_WITH_IMM, - PVRDMA_WR_RDMA_READ, - PVRDMA_WR_ATOMIC_CMP_AND_SWP, - PVRDMA_WR_ATOMIC_FETCH_AND_ADD, - PVRDMA_WR_LSO, - PVRDMA_WR_SEND_WITH_INV, - PVRDMA_WR_RDMA_READ_WITH_INV, - PVRDMA_WR_LOCAL_INV, - PVRDMA_WR_FAST_REG_MR, - PVRDMA_WR_MASKED_ATOMIC_CMP_AND_SWP, - PVRDMA_WR_MASKED_ATOMIC_FETCH_AND_ADD, - PVRDMA_WR_BIND_MW, - PVRDMA_WR_REG_SIG_MR, - PVRDMA_WR_ERROR, -}; - -enum pvrdma_wc_status { - PVRDMA_WC_SUCCESS, - PVRDMA_WC_LOC_LEN_ERR, - PVRDMA_WC_LOC_QP_OP_ERR, - PVRDMA_WC_LOC_EEC_OP_ERR, - PVRDMA_WC_LOC_PROT_ERR, - PVRDMA_WC_WR_FLUSH_ERR, - PVRDMA_WC_MW_BIND_ERR, - PVRDMA_WC_BAD_RESP_ERR, - PVRDMA_WC_LOC_ACCESS_ERR, - PVRDMA_WC_REM_INV_REQ_ERR, - PVRDMA_WC_REM_ACCESS_ERR, - PVRDMA_WC_REM_OP_ERR, - PVRDMA_WC_RETRY_EXC_ERR, - PVRDMA_WC_RNR_RETRY_EXC_ERR, - PVRDMA_WC_LOC_RDD_VIOL_ERR, - PVRDMA_WC_REM_INV_RD_REQ_ERR, - PVRDMA_WC_REM_ABORT_ERR, - PVRDMA_WC_INV_EECN_ERR, - PVRDMA_WC_INV_EEC_STATE_ERR, - PVRDMA_WC_FATAL_ERR, - PVRDMA_WC_RESP_TIMEOUT_ERR, - PVRDMA_WC_GENERAL_ERR, -}; - -enum pvrdma_wc_opcode { - PVRDMA_WC_SEND, - PVRDMA_WC_RDMA_WRITE, - PVRDMA_WC_RDMA_READ, - PVRDMA_WC_COMP_SWAP, - PVRDMA_WC_FETCH_ADD, - PVRDMA_WC_BIND_MW, - PVRDMA_WC_LSO, - PVRDMA_WC_LOCAL_INV, - PVRDMA_WC_FAST_REG_MR, - PVRDMA_WC_MASKED_COMP_SWAP, - PVRDMA_WC_MASKED_FETCH_ADD, - PVRDMA_WC_RECV = 1 << 7, - PVRDMA_WC_RECV_RDMA_WITH_IMM, -}; - -enum pvrdma_wc_flags { - PVRDMA_WC_GRH = 1 << 0, - PVRDMA_WC_WITH_IMM = 1 << 1, - PVRDMA_WC_WITH_INVALIDATE = 1 << 2, - PVRDMA_WC_IP_CSUM_OK = 1 << 3, - PVRDMA_WC_WITH_SMAC = 1 << 4, - PVRDMA_WC_WITH_VLAN = 1 << 5, - PVRDMA_WC_WITH_NETWORK_HDR_TYPE = 1 << 6, - PVRDMA_WC_FLAGS_MAX = PVRDMA_WC_WITH_NETWORK_HDR_TYPE, -}; - -enum pvrdma_network_type { - PVRDMA_NETWORK_IB, - PVRDMA_NETWORK_ROCE_V1 = PVRDMA_NETWORK_IB, - PVRDMA_NETWORK_IPV4, - PVRDMA_NETWORK_IPV6 -}; - -struct pvrdma_alloc_ucontext_resp { - uint32_t qp_tab_size; - uint32_t reserved; -}; - -struct pvrdma_alloc_pd_resp { - uint32_t pdn; - uint32_t reserved; -}; - -struct pvrdma_create_cq { - uint64_t __attribute__((aligned(8))) buf_addr; - uint32_t buf_size; - uint32_t reserved; -}; - -struct pvrdma_create_cq_resp { - uint32_t cqn; - uint32_t reserved; -}; - -struct pvrdma_resize_cq { - uint64_t __attribute__((aligned(8))) buf_addr; - uint32_t buf_size; - uint32_t reserved; -}; - -struct pvrdma_create_srq { - uint64_t __attribute__((aligned(8))) buf_addr; - uint32_t buf_size; - uint32_t reserved; -}; - -struct pvrdma_create_srq_resp { - uint32_t srqn; - uint32_t reserved; -}; - -struct pvrdma_create_qp { - uint64_t __attribute__((aligned(8))) rbuf_addr; - uint64_t __attribute__((aligned(8))) sbuf_addr; - uint32_t rbuf_size; - uint32_t sbuf_size; - uint64_t __attribute__((aligned(8))) qp_addr; -}; - -struct pvrdma_create_qp_resp { - uint32_t qpn; - uint32_t qp_handle; -}; - -/* PVRDMA masked atomic compare and swap */ -struct pvrdma_ex_cmp_swap { - uint64_t __attribute__((aligned(8))) swap_val; - uint64_t __attribute__((aligned(8))) compare_val; - uint64_t __attribute__((aligned(8))) swap_mask; - uint64_t __attribute__((aligned(8))) compare_mask; -}; - -/* PVRDMA masked atomic fetch and add */ -struct pvrdma_ex_fetch_add { - uint64_t __attribute__((aligned(8))) add_val; - uint64_t __attribute__((aligned(8))) field_boundary; -}; - -/* PVRDMA address vector. */ -struct pvrdma_av { - uint32_t port_pd; - uint32_t sl_tclass_flowlabel; - uint8_t dgid[16]; - uint8_t src_path_bits; - uint8_t gid_index; - uint8_t stat_rate; - uint8_t hop_limit; - uint8_t dmac[6]; - uint8_t reserved[6]; -}; - -/* PVRDMA scatter/gather entry */ -struct pvrdma_sge { - uint64_t __attribute__((aligned(8))) addr; - uint32_t length; - uint32_t lkey; -}; - -/* PVRDMA receive queue work request */ -struct pvrdma_rq_wqe_hdr { - uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ - uint32_t num_sge; /* size of s/g array */ - uint32_t total_len; /* reserved */ -}; -/* Use pvrdma_sge (ib_sge) for receive queue s/g array elements. */ - -/* PVRDMA send queue work request */ -struct pvrdma_sq_wqe_hdr { - uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ - uint32_t num_sge; /* size of s/g array */ - uint32_t total_len; /* reserved */ - uint32_t opcode; /* operation type */ - uint32_t send_flags; /* wr flags */ - union { - uint32_t imm_data; - uint32_t invalidate_rkey; - } ex; - uint32_t reserved; - union { - struct { - uint64_t __attribute__((aligned(8))) remote_addr; - uint32_t rkey; - uint8_t reserved[4]; - } rdma; - struct { - uint64_t __attribute__((aligned(8))) remote_addr; - uint64_t __attribute__((aligned(8))) compare_add; - uint64_t __attribute__((aligned(8))) swap; - uint32_t rkey; - uint32_t reserved; - } atomic; - struct { - uint64_t __attribute__((aligned(8))) remote_addr; - uint32_t log_arg_sz; - uint32_t rkey; - union { - struct pvrdma_ex_cmp_swap cmp_swap; - struct pvrdma_ex_fetch_add fetch_add; - } wr_data; - } masked_atomics; - struct { - uint64_t __attribute__((aligned(8))) iova_start; - uint64_t __attribute__((aligned(8))) pl_pdir_dma; - uint32_t page_shift; - uint32_t page_list_len; - uint32_t length; - uint32_t access_flags; - uint32_t rkey; - uint32_t reserved; - } fast_reg; - struct { - uint32_t remote_qpn; - uint32_t remote_qkey; - struct pvrdma_av av; - } ud; - } wr; -}; -/* Use pvrdma_sge (ib_sge) for send queue s/g array elements. */ - -/* Completion queue element. */ -struct pvrdma_cqe { - uint64_t __attribute__((aligned(8))) wr_id; - uint64_t __attribute__((aligned(8))) qp; - uint32_t opcode; - uint32_t status; - uint32_t byte_len; - uint32_t imm_data; - uint32_t src_qp; - uint32_t wc_flags; - uint32_t vendor_err; - uint16_t pkey_index; - uint16_t slid; - uint8_t sl; - uint8_t dlid_path_bits; - uint8_t port_num; - uint8_t smac[6]; - uint8_t network_hdr_type; - uint8_t reserved2[6]; /* Pad to next power of 2 (64). */ -}; - -#endif /* __VMW_PVRDMA_ABI_H__ */ diff --git a/meson.build b/meson.build index 58d4e157db..34b0ab061a 100644 --- a/meson.build +++ b/meson.build @@ -2833,37 +2833,6 @@ config_host_data.set('CONFIG_ARM_AES_BUILTIN', cc.compiles(''' void foo(uint8x16_t *p) { *p = vaesmcq_u8(*p); } ''')) -have_pvrdma = get_option('pvrdma') \ - .require(rdma.found(), error_message: 'PVRDMA requires OpenFabrics libraries') \ - .require(cc.compiles(gnu_source_prefix + ''' - #include - int main(void) - { - char buf = 0; - void *addr = &buf; - addr = mremap(addr, 0, 1, MREMAP_MAYMOVE | MREMAP_FIXED); - - return 0; - }'''), error_message: 'PVRDMA requires mremap').allowed() - -if have_pvrdma - config_host_data.set('LEGACY_RDMA_REG_MR', not cc.links(''' - #include - int main(void) - { - struct ibv_mr *mr; - struct ibv_pd *pd = NULL; - size_t length = 10; - uint64_t iova = 0; - int access = 0; - void *addr = NULL; - - mr = ibv_reg_mr_iova(pd, addr, length, iova, access); - ibv_dereg_mr(mr); - return 0; - }''')) -endif - if get_option('membarrier').disabled() have_membarrier = false elif host_os == 'windows' @@ -2996,7 +2965,6 @@ host_kconfig = \ (have_vhost_kernel ? ['CONFIG_VHOST_KERNEL=y'] : []) + \ (have_virtfs ? ['CONFIG_VIRTFS=y'] : []) + \ (host_os == 'linux' ? ['CONFIG_LINUX=y'] : []) + \ - (have_pvrdma ? ['CONFIG_PVRDMA=y'] : []) + \ (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) + \ (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + \ (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=y'] : []) @@ -3360,8 +3328,6 @@ if have_system 'hw/pci', 'hw/pci-host', 'hw/ppc', - 'hw/rdma', - 'hw/rdma/vmw', 'hw/rtc', 'hw/s390x', 'hw/scsi', @@ -4030,7 +3996,6 @@ if have_tools }] endforeach - subdir('contrib/rdmacm-mux') subdir('contrib/elf2dmp') executable('qemu-edid', files('qemu-edid.c', 'hw/display/edid-generate.c'), @@ -4436,7 +4401,6 @@ summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} summary_info += {'RDMA support': rdma} -summary_info += {'PVRDMA support': have_pvrdma} summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} diff --git a/meson_options.txt b/meson_options.txt index 0a99a059ec..b5c0bad9e7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -198,8 +198,6 @@ option('opengl', type : 'feature', value : 'auto', description: 'OpenGL support') option('rdma', type : 'feature', value : 'auto', description: 'Enable RDMA-based migration') -option('pvrdma', type : 'feature', value : 'auto', - description: 'Enable PVRDMA support') option('gtk', type : 'feature', value : 'auto', description: 'GTK+ user interface') option('sdl', type : 'feature', value : 'auto', diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index b0f948d337..f84a0dc523 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -31,7 +31,6 @@ #include "qapi/type-helpers.h" #include "hw/mem/memory-device.h" #include "hw/intc/intc.h" -#include "hw/rdma/rdma.h" NameInfo *qmp_query_name(Error **errp) { diff --git a/qapi/machine.json b/qapi/machine.json index 6ab713a4c4..2df407e877 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1737,23 +1737,6 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } -## -# @x-query-rdma: -# -# Query RDMA state -# -# Features: -# -# @unstable: This command is meant for debugging. -# -# Returns: RDMA state -# -# Since: 6.2 -## -{ 'command': 'x-query-rdma', - 'returns': 'HumanReadableText', - 'features': [ 'unstable' ] } - ## # @x-query-roms: # diff --git a/qapi/meson.build b/qapi/meson.build index 375d564277..c92af6e063 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -62,7 +62,6 @@ if have_system 'cryptodev', 'qdev', 'pci', - 'rdma', 'rocker', 'tpm', ] diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 8304d45625..5e33da7228 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -54,7 +54,6 @@ { 'include': 'dump.json' } { 'include': 'net.json' } { 'include': 'ebpf.json' } -{ 'include': 'rdma.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } { 'include': 'ui.json' } diff --git a/qapi/rdma.json b/qapi/rdma.json deleted file mode 100644 index 195c001850..0000000000 --- a/qapi/rdma.json +++ /dev/null @@ -1,38 +0,0 @@ -# -*- Mode: Python -*- -# vim: filetype=python -# - -## -# = RDMA device -## - -## -# @RDMA_GID_STATUS_CHANGED: -# -# Emitted when guest driver adds/deletes GID to/from device -# -# @netdev: RoCE Network Device name -# -# @gid-status: Add or delete indication -# -# @subnet-prefix: Subnet Prefix -# -# @interface-id: Interface ID -# -# Since: 4.0 -# -# Example: -# -# <- {"timestamp": {"seconds": 1541579657, "microseconds": 986760}, -# "event": "RDMA_GID_STATUS_CHANGED", -# "data": -# {"netdev": "bridge0", -# "interface-id": 15880512517475447892, -# "gid-status": true, -# "subnet-prefix": 33022}} -## -{ 'event': 'RDMA_GID_STATUS_CHANGED', - 'data': { 'netdev' : 'str', - 'gid-status' : 'bool', - 'subnet-prefix' : 'uint64', - 'interface-id' : 'uint64' } } diff --git a/qemu-options.hx b/qemu-options.hx index 617d4c6ebd..cf61f6b863 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5113,9 +5113,6 @@ SRST allows a co-operating external process to access the QEMU memory region. - The ``share`` is also required for pvrdma devices due to - limitations in the RDMA API provided by Linux. - Setting share=on might affect the ability to configure NUMA bindings for the memory backend under some circumstances, see Documentation/vm/numa\_memory\_policy.txt on the Linux kernel diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure index 76781f17f4..868db665f6 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ b/scripts/ci/org.centos/stream/8/x86_64/configure @@ -99,7 +99,6 @@ --disable-opengl \ --disable-parallels \ --disable-pie \ ---disable-pvrdma \ --disable-qcow1 \ --disable-qed \ --disable-qom-cast-debug \ diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 680fa3f581..5ace33f167 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -163,7 +163,6 @@ meson_options_help() { printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' printf "%s\n" ' png PNG support with libpng' - printf "%s\n" ' pvrdma Enable PVRDMA support' printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)' @@ -428,8 +427,6 @@ _meson_option_parse() { --enable-png) printf "%s" -Dpng=enabled ;; --disable-png) printf "%s" -Dpng=disabled ;; --prefix=*) quote_sh "-Dprefix=$2" ;; - --enable-pvrdma) printf "%s" -Dpvrdma=enabled ;; - --disable-pvrdma) printf "%s" -Dpvrdma=disabled ;; --enable-qcow1) printf "%s" -Dqcow1=enabled ;; --disable-qcow1) printf "%s" -Dqcow1=disabled ;; --enable-qed) printf "%s" -Dqed=enabled ;; diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index a0006eec6f..73c292bbac 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -55,7 +55,6 @@ cp_portable() { -e 'linux/if_ether' \ -e 'input-event-codes' \ -e 'sys/' \ - -e 'pvrdma_verbs' \ -e 'drm.h' \ -e 'limits' \ -e 'linux/const' \ @@ -226,32 +225,6 @@ mkdir -p "$output/include/standard-headers/drm" cp_portable "$tmpdir/include/drm/drm_fourcc.h" \ "$output/include/standard-headers/drm" -rm -rf "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma" -mkdir -p "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma" - -# Remove the unused functions from pvrdma_verbs.h avoiding the unnecessary -# import of several infiniband/networking/other headers -tmp_pvrdma_verbs="$tmpdir/pvrdma_verbs.h" -# Parse the entire file instead of single lines to match -# function declarations expanding over multiple lines -# and strip the declarations starting with pvrdma prefix. -sed -e '1h;2,$H;$!d;g' -e 's/[^};]*pvrdma[^(| ]*([^)]*);//g' \ - "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h" > \ - "$tmp_pvrdma_verbs"; - -for i in "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" \ - "$tmp_pvrdma_verbs"; do \ - cp_portable "$i" \ - "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/" -done - -rm -rf "$output/include/standard-headers/rdma/" -mkdir -p "$output/include/standard-headers/rdma/" -for i in "$tmpdir/include/rdma/vmw_pvrdma-abi.h"; do - cp_portable "$i" \ - "$output/include/standard-headers/rdma/" -done - cat <$output/include/standard-headers/linux/types.h /* For QEMU all types are already defined via osdep.h, so this * header does not need to do anything. From 2d9cbbea64e67d90c3c22d7e2d5ab530d830eb33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Mar 2024 13:41:10 +0100 Subject: [PATCH 0130/2884] block/gluster: Remove deprecated RDMA protocol handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GlusterFS+RDMA has been deprecated 8 years ago in commit 0552ff2465 ("block/gluster: deprecate rdma support"): gluster volfile server fetch happens through unix and/or tcp, it doesn't support volfile fetch over rdma. The rdma code may actually mislead, so to make sure things do not break, for now we fallback to tcp when requested for rdma, with a warning. If you are wondering how this worked all these days, its the gluster libgfapi code which handles anything other than unix transport as socket/tcp, sad but true. Besides, the whole RDMA subsystem was deprecated in commit e9a54265f5 ("hw/rdma: Deprecate the pvrdma device and the rdma subsystem") released in v8.2. Cc: Prasanna Kumar Kalever Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Michael S. Tsirkin Message-Id: <20240328130255.52257-4-philmd@linaro.org> --- block/gluster.c | 39 -------------------------- docs/system/device-url-syntax.rst.inc | 4 +-- docs/system/qemu-block-drivers.rst.inc | 1 - 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/block/gluster.c b/block/gluster.c index cc74af06dc..4253c8db5e 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -371,9 +371,6 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf, } else if (!strcmp(uri->scheme, "gluster+unix")) { gsconf->type = SOCKET_ADDRESS_TYPE_UNIX; is_unix = true; - } else if (!strcmp(uri->scheme, "gluster+rdma")) { - gsconf->type = SOCKET_ADDRESS_TYPE_INET; - warn_report("rdma feature is not supported, falling back to tcp"); } else { ret = -EINVAL; goto out; @@ -1638,44 +1635,8 @@ static BlockDriver bdrv_gluster_unix = { .strong_runtime_opts = gluster_strong_open_opts, }; -/* rdma is deprecated (actually never supported for volfile fetch). - * Let's maintain it for the protocol compatibility, to make sure things - * won't break immediately. For now, gluster+rdma will fall back to gluster+tcp - * protocol with a warning. - * TODO: remove gluster+rdma interface support - */ -static BlockDriver bdrv_gluster_rdma = { - .format_name = "gluster", - .protocol_name = "gluster+rdma", - .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, - .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, - .bdrv_reopen_commit = qemu_gluster_reopen_commit, - .bdrv_reopen_abort = qemu_gluster_reopen_abort, - .bdrv_close = qemu_gluster_close, - .bdrv_co_create = qemu_gluster_co_create, - .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_co_getlength = qemu_gluster_co_getlength, - .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, - .bdrv_co_truncate = qemu_gluster_co_truncate, - .bdrv_co_readv = qemu_gluster_co_readv, - .bdrv_co_writev = qemu_gluster_co_writev, - .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, -#ifdef CONFIG_GLUSTERFS_DISCARD - .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, -#endif -#ifdef CONFIG_GLUSTERFS_ZEROFILL - .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes, -#endif - .bdrv_co_block_status = qemu_gluster_co_block_status, - .bdrv_refresh_limits = qemu_gluster_refresh_limits, - .create_opts = &qemu_gluster_create_opts, - .strong_runtime_opts = gluster_strong_open_opts, -}; - static void bdrv_gluster_init(void) { - bdrv_register(&bdrv_gluster_rdma); bdrv_register(&bdrv_gluster_unix); bdrv_register(&bdrv_gluster_tcp); bdrv_register(&bdrv_gluster); diff --git a/docs/system/device-url-syntax.rst.inc b/docs/system/device-url-syntax.rst.inc index 7dbc525fa8..43b5c2596b 100644 --- a/docs/system/device-url-syntax.rst.inc +++ b/docs/system/device-url-syntax.rst.inc @@ -87,8 +87,8 @@ These are specified using a special URL syntax. ``GlusterFS`` GlusterFS is a user space distributed file system. QEMU supports the - use of GlusterFS volumes for hosting VM disk images using TCP, Unix - Domain Sockets and RDMA transport protocols. + use of GlusterFS volumes for hosting VM disk images using TCP and Unix + Domain Sockets transport protocols. Syntax for specifying a VM disk image on GlusterFS volume is diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc index 105cb9679c..384e95ba76 100644 --- a/docs/system/qemu-block-drivers.rst.inc +++ b/docs/system/qemu-block-drivers.rst.inc @@ -737,7 +737,6 @@ Examples |qemu_system| -drive file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img |qemu_system| -drive file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img |qemu_system| -drive file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket - |qemu_system| -drive file=gluster+rdma://1.2.3.4:24007/testvol/a.img |qemu_system| -drive file=gluster://1.2.3.4/testvol/a.img,file.debug=9,file.logfile=/var/log/qemu-gluster.log |qemu_system| 'json:{"driver":"qcow2", "file":{"driver":"gluster", From 1f2355f53c752297789d431575c4ba975219599c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 12 Mar 2024 01:23:30 +0100 Subject: [PATCH 0131/2884] meson: Make DEBUG_REMAP a meson option Currently DEBUG_REMAP is a macro that needs to be manually #defined to be activated, which makes it hard to have separate build directories dedicated to testing the code with it. Promote it to a meson option. Signed-off-by: Ilya Leoshkevich Message-Id: <20240312002402.14344-1-iii@linux.ibm.com> Signed-off-by: Richard Henderson --- bsd-user/qemu.h | 6 ++---- linux-user/qemu.h | 4 +--- linux-user/uaccess.c | 4 ++-- meson.build | 4 ++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 1b0a591d2d..8629f0dcde 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -22,8 +22,6 @@ #include "exec/cpu_ldst.h" #include "exec/exec-all.h" -#undef DEBUG_REMAP - #include "exec/user/abitypes.h" extern char **environ; @@ -437,7 +435,7 @@ static inline void *lock_user(int type, abi_ulong guest_addr, long len, if (!access_ok(type, guest_addr, len)) { return NULL; } -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP { void *addr; addr = g_malloc(len); @@ -461,7 +459,7 @@ static inline void unlock_user(void *host_ptr, abi_ulong guest_addr, long len) { -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP if (!host_ptr) { return; } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 32cd43d9ef..4777856b52 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -4,8 +4,6 @@ #include "cpu.h" #include "exec/cpu_ldst.h" -#undef DEBUG_REMAP - #include "exec/user/abitypes.h" #include "syscall_defs.h" @@ -332,7 +330,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy); /* Unlock an area of guest memory. The first LEN bytes must be flushed back to guest memory. host_ptr = NULL is explicitly allowed and does nothing. */ -#ifndef DEBUG_REMAP +#ifndef CONFIG_DEBUG_REMAP static inline void unlock_user(void *host_ptr, abi_ulong guest_addr, ssize_t len) { diff --git a/linux-user/uaccess.c b/linux-user/uaccess.c index 425cbf677f..27e841e651 100644 --- a/linux-user/uaccess.c +++ b/linux-user/uaccess.c @@ -14,7 +14,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy) return NULL; } host_addr = g2h_untagged(guest_addr); -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP if (copy) { host_addr = g_memdup(host_addr, len); } else { @@ -24,7 +24,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy) return host_addr; } -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP void unlock_user(void *host_ptr, abi_ulong guest_addr, ssize_t len) { void *host_ptr_conv; diff --git a/meson.build b/meson.build index 95cee7046e..553b940999 100644 --- a/meson.build +++ b/meson.build @@ -2350,6 +2350,7 @@ config_host_data.set('CONFIG_DEBUG_GRAPH_LOCK', get_option('debug_graph_lock')) config_host_data.set('CONFIG_DEBUG_MUTEX', get_option('debug_mutex')) config_host_data.set('CONFIG_DEBUG_STACK_USAGE', get_option('debug_stack_usage')) config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg')) +config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap')) config_host_data.set('CONFIG_LIVE_BLOCK_MIGRATION', get_option('live_block_migration').allowed()) config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug')) config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed()) @@ -4277,6 +4278,9 @@ if config_all_accel.has_key('CONFIG_TCG') endif summary_info += {'TCG plugins': get_option('plugins')} summary_info += {'TCG debug enabled': get_option('debug_tcg')} + if have_linux_user or have_bsd_user + summary_info += {'syscall buffer debugging support': get_option('debug_remap')} + endif endif summary_info += {'target list': ' '.join(target_dirs)} if have_system diff --git a/meson_options.txt b/meson_options.txt index b5c0bad9e7..adc77bae0c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -85,6 +85,8 @@ option('plugins', type: 'boolean', value: false, description: 'TCG plugins via shared library loading') option('debug_tcg', type: 'boolean', value: false, description: 'TCG debugging') +option('debug_remap', type: 'boolean', value: false, + description: 'syscall buffer debugging support') option('tcg_interpreter', type: 'boolean', value: false, description: 'TCG with bytecode interpreter (slow)') option('safe_stack', type: 'boolean', value: false, diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 5ace33f167..0a29d35fdb 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -29,6 +29,7 @@ meson_options_help() { printf "%s\n" ' --enable-debug-graph-lock' printf "%s\n" ' graph lock debugging support' printf "%s\n" ' --enable-debug-mutex mutex debugging support' + printf "%s\n" ' --enable-debug-remap syscall buffer debugging support' printf "%s\n" ' --enable-debug-stack-usage' printf "%s\n" ' measure coroutine stack usage' printf "%s\n" ' --enable-debug-tcg TCG debugging' @@ -294,6 +295,8 @@ _meson_option_parse() { --disable-debug-graph-lock) printf "%s" -Ddebug_graph_lock=false ;; --enable-debug-mutex) printf "%s" -Ddebug_mutex=true ;; --disable-debug-mutex) printf "%s" -Ddebug_mutex=false ;; + --enable-debug-remap) printf "%s" -Ddebug_remap=true ;; + --disable-debug-remap) printf "%s" -Ddebug_remap=false ;; --enable-debug-stack-usage) printf "%s" -Ddebug_stack_usage=true ;; --disable-debug-stack-usage) printf "%s" -Ddebug_stack_usage=false ;; --enable-debug-tcg) printf "%s" -Ddebug_tcg=true ;; From 04f6fb897a5aeb3e356a7b889869c9962f9c16c7 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 31 Mar 2024 13:07:34 +0300 Subject: [PATCH 0132/2884] linux-user: do_setsockopt: fix SOL_ALG.ALG_SET_KEY This setsockopt accepts zero-lengh optlen (current qemu implementation does not allow this). Also, there's no need to make a copy of the key, it is enough to use lock_user() (which accepts zero length already). Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2197 Fixes: f31dddd2fc "linux-user: Add support for setsockopt() option SOL_ALG" Signed-off-by: Michael Tokarev Message-Id: <20240331100737.2724186-2-mjt@tls.msk.ru> Signed-off-by: Richard Henderson --- linux-user/syscall.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3df2b94d9a..59fb3e911f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2277,18 +2277,13 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, switch (optname) { case ALG_SET_KEY: { - char *alg_key = g_malloc(optlen); - + char *alg_key = lock_user(VERIFY_READ, optval_addr, optlen, 1); if (!alg_key) { - return -TARGET_ENOMEM; - } - if (copy_from_user(alg_key, optval_addr, optlen)) { - g_free(alg_key); return -TARGET_EFAULT; } ret = get_errno(setsockopt(sockfd, level, optname, alg_key, optlen)); - g_free(alg_key); + unlock_user(alg_key, optval_addr, optlen); break; } case ALG_SET_AEAD_AUTHSIZE: From 124a1341a66287ab8f4ce3de3c98eed6747a639f Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 31 Mar 2024 13:07:35 +0300 Subject: [PATCH 0133/2884] linux-user: do_setsockopt: make ip_mreq local to the place it is used and inline target_to_host_ip_mreq() ip_mreq is declared at the beginning of do_setsockopt(), while it is used in only one place. Move its declaration to that very place and replace pointer to alloca()-allocated memory with the structure itself. target_to_host_ip_mreq() is used only once, inline it. This change also properly handles TARGET_EFAULT when the address is wrong. Signed-off-by: Michael Tokarev Message-Id: <20240331100737.2724186-3-mjt@tls.msk.ru> [rth: Fix braces, adjust optlen to match host structure size] Signed-off-by: Richard Henderson --- linux-user/syscall.c | 47 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 59fb3e911f..cca9cafe4f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1615,24 +1615,6 @@ static abi_long do_pipe(CPUArchState *cpu_env, abi_ulong pipedes, return get_errno(ret); } -static inline abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, - abi_ulong target_addr, - socklen_t len) -{ - struct target_ip_mreqn *target_smreqn; - - target_smreqn = lock_user(VERIFY_READ, target_addr, len, 1); - if (!target_smreqn) - return -TARGET_EFAULT; - mreqn->imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; - mreqn->imr_address.s_addr = target_smreqn->imr_address.s_addr; - if (len == sizeof(struct target_ip_mreqn)) - mreqn->imr_ifindex = tswapal(target_smreqn->imr_ifindex); - unlock_user(target_smreqn, target_addr, 0); - - return 0; -} - static inline abi_long target_to_host_sockaddr(int fd, struct sockaddr *addr, abi_ulong target_addr, socklen_t len) @@ -2067,7 +2049,6 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, { abi_long ret; int val; - struct ip_mreqn *ip_mreq; struct ip_mreq_source *ip_mreq_source; switch(level) { @@ -2111,15 +2092,33 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, break; case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: + { + struct ip_mreqn ip_mreq; + struct target_ip_mreqn *target_smreqn; + + QEMU_BUILD_BUG_ON(sizeof(struct ip_mreq) != + sizeof(struct target_ip_mreq)); + if (optlen < sizeof (struct target_ip_mreq) || - optlen > sizeof (struct target_ip_mreqn)) + optlen > sizeof (struct target_ip_mreqn)) { return -TARGET_EINVAL; + } - ip_mreq = (struct ip_mreqn *) alloca(optlen); - target_to_host_ip_mreq(ip_mreq, optval_addr, optlen); - ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq, optlen)); + target_smreqn = lock_user(VERIFY_READ, optval_addr, optlen, 1); + if (!target_smreqn) { + return -TARGET_EFAULT; + } + ip_mreq.imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; + ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; + if (optlen == sizeof(struct target_ip_mreqn)) { + ip_mreq.imr_ifindex = tswapal(target_smreqn->imr_ifindex); + optlen = sizeof(struct ip_mreqn); + } + unlock_user(target_smreqn, optval_addr, 0); + + ret = get_errno(setsockopt(sockfd, level, optname, &ip_mreq, optlen)); break; - + } case IP_BLOCK_SOURCE: case IP_UNBLOCK_SOURCE: case IP_ADD_SOURCE_MEMBERSHIP: From 166bd92e3b4dfcd058013453e4dd94ebe5e87819 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 31 Mar 2024 13:07:36 +0300 Subject: [PATCH 0134/2884] linux-user: do_setsockopt: make ip_mreq_source local to the place where it is used Signed-off-by: Michael Tokarev Message-Id: <20240331100737.2724186-4-mjt@tls.msk.ru> Signed-off-by: Richard Henderson --- linux-user/syscall.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index cca9cafe4f..1fedf16650 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2049,7 +2049,6 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, { abi_long ret; int val; - struct ip_mreq_source *ip_mreq_source; switch(level) { case SOL_TCP: @@ -2123,6 +2122,9 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case IP_UNBLOCK_SOURCE: case IP_ADD_SOURCE_MEMBERSHIP: case IP_DROP_SOURCE_MEMBERSHIP: + { + struct ip_mreq_source *ip_mreq_source; + if (optlen != sizeof (struct target_ip_mreq_source)) return -TARGET_EINVAL; @@ -2133,7 +2135,7 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq_source, optlen)); unlock_user (ip_mreq_source, optval_addr, 0); break; - + } default: goto unimplemented; } From 88a722b6ad59bf6ca42c01ac806c54bd94d98642 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 31 Mar 2024 13:07:37 +0300 Subject: [PATCH 0135/2884] linux-user: do_setsockopt: eliminate goto in switch for SO_SNDTIMEO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's identical code for SO_SNDTIMEO and SO_RCVTIMEO, currently implemented using an ugly goto into another switch case. Eliminate that using arithmetic if, making code flow more natural. Signed-off-by: Michael Tokarev Message-Id: <20240331100737.2724186-5-mjt@tls.msk.ru> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/syscall.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1fedf16650..41659b63f5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2301,12 +2301,10 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case TARGET_SOL_SOCKET: switch (optname) { case TARGET_SO_RCVTIMEO: + case TARGET_SO_SNDTIMEO: { struct timeval tv; - optname = SO_RCVTIMEO; - -set_timeout: if (optlen != sizeof(struct target_timeval)) { return -TARGET_EINVAL; } @@ -2315,13 +2313,12 @@ set_timeout: return -TARGET_EFAULT; } - ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, + optname == TARGET_SO_RCVTIMEO ? + SO_RCVTIMEO : SO_SNDTIMEO, &tv, sizeof(tv))); return ret; } - case TARGET_SO_SNDTIMEO: - optname = SO_SNDTIMEO; - goto set_timeout; case TARGET_SO_ATTACH_FILTER: { struct target_sock_fprog *tfprog; From 7048fc59ef6a9f76693db3bafede6234a3897385 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 3 Apr 2024 11:20:49 +0200 Subject: [PATCH 0136/2884] linux-user: Add FITRIM ioctl Tiny patch to add the missing FITRIM ioctl. Signed-off-by: Michael Vogt Message-Id: <20240403092048.16023-2-michael.vogt@gmail.com> Signed-off-by: Richard Henderson --- linux-user/ioctls.h | 3 +++ linux-user/syscall_defs.h | 1 + linux-user/syscall_types.h | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 1aec9d5836..d508d0c04a 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -140,6 +140,9 @@ #ifdef FITHAW IOCTL(FITHAW, IOC_W | IOC_R, TYPE_INT) #endif +#ifdef FITRIM + IOCTL(FITRIM, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_fstrim_range))) +#endif IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG)) #ifdef CONFIG_FIEMAP diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 3995487630..a00b617cae 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -945,6 +945,7 @@ struct target_rtc_pll_info { #define TARGET_FIFREEZE TARGET_IOWR('X', 119, abi_int) #define TARGET_FITHAW TARGET_IOWR('X', 120, abi_int) +#define TARGET_FITRIM TARGET_IOWR('X', 121, struct fstrim_range) /* * Note that the ioctl numbers for FS_IOC_ diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index c3b43f8022..6dd7a80ce5 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -341,6 +341,11 @@ STRUCT(file_clone_range, TYPE_ULONGLONG, /* src_length */ TYPE_ULONGLONG) /* dest_offset */ +STRUCT(fstrim_range, + TYPE_ULONGLONG, /* start */ + TYPE_ULONGLONG, /* len */ + TYPE_ULONGLONG) /* minlen */ + STRUCT(fiemap_extent, TYPE_ULONGLONG, /* fe_logical */ TYPE_ULONGLONG, /* fe_physical */ From 5fae5110eedba1110605c88d96cd4a5f057bfca2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 2 Aug 2023 09:19:12 -0700 Subject: [PATCH 0137/2884] target/m68k: Pass semihosting arg to exit Instead of using d0 (the semihost function number), use d1 (the provide exit status). Signed-off-by: Keith Packard Reviewed-by: Peter Maydell Message-Id: <20230802161914.395443-2-keithp@keithp.com> Signed-off-by: Richard Henderson --- target/m68k/m68k-semi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 546cff2246..6fbbd140f3 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -132,8 +132,8 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) args = env->dregs[1]; switch (nr) { case HOSTED_EXIT: - gdb_exit(env->dregs[0]); - exit(env->dregs[0]); + gdb_exit(env->dregs[1]); + exit(env->dregs[1]); case HOSTED_OPEN: GET_ARG(0); From f161e723fdfddfc820c7f19eb6ac76ac6ba6d2a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 3 Oct 2023 15:15:24 -0700 Subject: [PATCH 0138/2884] target/m68k: Perform the semihosting test during translate Replace EXCP_HALT_INSN by EXCP_SEMIHOSTING. Perform the pre- and post-insn tests during translate, leaving only the actual semihosting operation for the exception. Signed-off-by: Richard Henderson --- target/m68k/cpu.h | 2 +- target/m68k/op_helper.c | 14 ++----------- target/m68k/translate.c | 45 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index e184239a81..b5bbeedb7a 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -66,7 +66,7 @@ #define EXCP_MMU_ACCESS 58 /* MMU Access Level Violation Error */ #define EXCP_RTE 0x100 -#define EXCP_HALT_INSN 0x101 +#define EXCP_SEMIHOSTING 0x101 #define M68K_DTTR0 0 #define M68K_DTTR1 1 diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 125f6c1b08..15bad5dd46 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -202,18 +202,8 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw) /* Return from an exception. */ cf_rte(env); return; - case EXCP_HALT_INSN: - if (semihosting_enabled((env->sr & SR_S) == 0) - && (env->pc & 3) == 0 - && cpu_lduw_code(env, env->pc - 4) == 0x4e71 - && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - return; - } - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); + case EXCP_SEMIHOSTING: + do_m68k_semihosting(env, env->dregs[0]); return; } } diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 8a194f2f21..647bd9d9be 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -26,12 +26,11 @@ #include "qemu/log.h" #include "qemu/qemu-print.h" #include "exec/translator.h" - #include "exec/helper-proto.h" #include "exec/helper-gen.h" - #include "exec/log.h" #include "fpu/softfloat.h" +#include "semihosting/semihost.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -1401,6 +1400,40 @@ static void gen_jmp_tb(DisasContext *s, int n, target_ulong dest, s->base.is_jmp = DISAS_NORETURN; } +#ifndef CONFIG_USER_ONLY +static bool semihosting_test(DisasContext *s) +{ + uint32_t test; + + if (!semihosting_enabled(IS_USER(s))) { + return false; + } + + /* + * "The semihosting instruction is immediately preceded by a + * nop aligned to a 4-byte boundary..." + * The preceding 2-byte (aligned) nop plus the 2-byte halt/bkpt + * means that we have advanced 4 bytes from the required nop. + */ + if (s->pc % 4 != 0) { + return false; + } + test = translator_lduw(s->env, &s->base, s->pc - 4); + if (test != 0x4e71) { + return false; + } + /* "... and followed by an invalid sentinel instruction movec %sp,0." */ + test = translator_ldl(s->env, &s->base, s->pc); + if (test != 0x4e7bf000) { + return false; + } + + /* Consume the sentinel. */ + s->pc += 4; + return true; +} +#endif /* !CONFIG_USER_ONLY */ + DISAS_INSN(scc) { DisasCompare c; @@ -4465,8 +4498,12 @@ DISAS_INSN(halt) gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - - gen_exception(s, s->pc, EXCP_HALT_INSN); + if (semihosting_test(s)) { + gen_exception(s, s->pc, EXCP_SEMIHOSTING); + return; + } + tcg_gen_movi_i32(cpu_halted, 1); + gen_exception(s, s->pc, EXCP_HLT); } DISAS_INSN(stop) From 0815c228bd1e0c24ac064ce299807b32f8e05d83 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 3 Oct 2023 15:26:06 -0700 Subject: [PATCH 0139/2884] target/m68k: Support semihosting on non-ColdFire targets According to the m68k semihosting spec: "The instruction used to trigger a semihosting request depends on the m68k processor variant. On ColdFire, "halt" is used; on other processors (which don't implement "halt"), "bkpt #0" may be used." Add support for non-CodeFire processors by matching BKPT #0 instructions. Signed-off-by: Keith Packard [rth: Use semihosting_test()] Signed-off-by: Richard Henderson --- target/m68k/translate.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 647bd9d9be..169927552a 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -2646,6 +2646,11 @@ DISAS_INSN(bkpt) #if defined(CONFIG_USER_ONLY) gen_exception(s, s->base.pc_next, EXCP_DEBUG); #else + /* BKPT #0 is the alternate semihosting instruction. */ + if ((insn & 7) == 0 && semihosting_test(s)) { + gen_exception(s, s->pc, EXCP_SEMIHOSTING); + return; + } gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); #endif } From cbd58e7cc26cef811bd947f48345ec930481e4e2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2023 10:50:39 +0100 Subject: [PATCH 0140/2884] MAINTAINERS: update email of Peter Lieven I will leave KAMP in the next days. Update email to stay reachable. Signed-off-by: Peter Lieven Message-ID: <20230105095039.182718-1-pl@kamp.de> Signed-off-by: Thomas Huth --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8bb32f4a7e..cf46131f3b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3805,7 +3805,7 @@ F: block/vmdk.c RBD M: Ilya Dryomov -R: Peter Lieven +R: Peter Lieven L: qemu-block@nongnu.org S: Supported F: block/rbd.c @@ -3831,7 +3831,7 @@ F: block/blkio.c iSCSI M: Ronnie Sahlberg M: Paolo Bonzini -M: Peter Lieven +M: Peter Lieven L: qemu-block@nongnu.org S: Odd Fixes F: block/iscsi.c @@ -3854,7 +3854,7 @@ T: git https://repo.or.cz/qemu/ericb.git nbd T: git https://gitlab.com/vsementsov/qemu.git block NFS -M: Peter Lieven +M: Peter Lieven L: qemu-block@nongnu.org S: Maintained F: block/nfs.c From 58045186fccaf400d3938fad220a99b1b5f3da6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Sun, 14 Apr 2024 19:28:21 +0200 Subject: [PATCH 0141/2884] tests/qtest : Use `g_assert_cmphex` instead of `g_assert_cmpuint` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The messages for assertions using hexadecimal numbers will be easier to understand with `g_assert_cmphex`. Cases changed : "cmpuint.*0x", "cmpuint.*<<" Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Ninad Palsule Message-ID: <20240414173349.31194-1-ines.varhol@telecom-paris.fr> Signed-off-by: Thomas Huth --- tests/qtest/aspeed_fsi-test.c | 20 ++-- tests/qtest/cmsdk-apb-dualtimer-test.c | 2 +- tests/qtest/cmsdk-apb-watchdog-test.c | 2 +- tests/qtest/erst-test.c | 2 +- tests/qtest/ivshmem-test.c | 10 +- tests/qtest/libqos/ahci.c | 4 +- tests/qtest/microbit-test.c | 46 ++++----- tests/qtest/sse-timer-test.c | 4 +- tests/qtest/stm32l4x5_exti-test.c | 138 ++++++++++++------------- tests/qtest/stm32l4x5_syscfg-test.c | 74 ++++++------- 10 files changed, 151 insertions(+), 151 deletions(-) diff --git a/tests/qtest/aspeed_fsi-test.c b/tests/qtest/aspeed_fsi-test.c index b3020dd821..f5ab269972 100644 --- a/tests/qtest/aspeed_fsi-test.c +++ b/tests/qtest/aspeed_fsi-test.c @@ -63,22 +63,22 @@ static void test_fsi_setup(QTestState *s, uint32_t base_addr) /* Unselect FSI1 */ aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_SELECT, 0x0); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_SELECT); - g_assert_cmpuint(curval, ==, 0x0); + g_assert_cmphex(curval, ==, 0x0); /* Select FSI0 */ aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_SELECT, 0x1); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_SELECT); - g_assert_cmpuint(curval, ==, 0x1); + g_assert_cmphex(curval, ==, 0x1); } else if (base_addr == AST2600_OPB_FSI1_BASE_ADDR) { /* Unselect FSI0 */ aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_SELECT, 0x0); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_SELECT); - g_assert_cmpuint(curval, ==, 0x0); + g_assert_cmphex(curval, ==, 0x0); /* Select FSI1 */ aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_SELECT, 0x1); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_SELECT); - g_assert_cmpuint(curval, ==, 0x1); + g_assert_cmphex(curval, ==, 0x1); } else { g_assert_not_reached(); } @@ -145,11 +145,11 @@ static void test_fsi0_getcfam_addr0(const void *data) aspeed_fsi_writel(s, ASPEED_FSI_ENGINER_TRIGGER, 0x1); curval = aspeed_fsi_readl(s, ASPEED_FSI_INTRRUPT_STATUS); - g_assert_cmpuint(curval, ==, 0x10000); + g_assert_cmphex(curval, ==, 0x10000); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_STATUS); - g_assert_cmpuint(curval, ==, 0x0); + g_assert_cmphex(curval, ==, 0x0); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_READ_DATA); - g_assert_cmpuint(curval, ==, 0x152d02c0); + g_assert_cmphex(curval, ==, 0x152d02c0); } static void test_fsi1_getcfam_addr0(const void *data) @@ -168,11 +168,11 @@ static void test_fsi1_getcfam_addr0(const void *data) aspeed_fsi_writel(s, ASPEED_FSI_ENGINER_TRIGGER, 0x1); curval = aspeed_fsi_readl(s, ASPEED_FSI_INTRRUPT_STATUS); - g_assert_cmpuint(curval, ==, 0x20000); + g_assert_cmphex(curval, ==, 0x20000); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_STATUS); - g_assert_cmpuint(curval, ==, 0x0); + g_assert_cmphex(curval, ==, 0x0); curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_READ_DATA); - g_assert_cmpuint(curval, ==, 0x152d02c0); + g_assert_cmphex(curval, ==, 0x152d02c0); } int main(int argc, char **argv) diff --git a/tests/qtest/cmsdk-apb-dualtimer-test.c b/tests/qtest/cmsdk-apb-dualtimer-test.c index ad6a758289..3b89bed97d 100644 --- a/tests/qtest/cmsdk-apb-dualtimer-test.c +++ b/tests/qtest/cmsdk-apb-dualtimer-test.c @@ -69,7 +69,7 @@ static void test_dualtimer(void) * tick VALUE should have wrapped round to 0xffff. */ clock_step(40); - g_assert_cmpuint(readl(TIMER_BASE + TIMER1VALUE), ==, 0xffff); + g_assert_cmphex(readl(TIMER_BASE + TIMER1VALUE), ==, 0xffff); /* Check that any write to INTCLR clears interrupt */ writel(TIMER_BASE + TIMER1INTCLR, 1); diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c index 2710cb17b8..00b5dbbc81 100644 --- a/tests/qtest/cmsdk-apb-watchdog-test.c +++ b/tests/qtest/cmsdk-apb-watchdog-test.c @@ -88,7 +88,7 @@ static void test_clock_change(void) /* Rewrite RCC.SYSDIV from 16 to 8, so the clock is now 40ns per tick */ rcc = readl(SSYS_BASE + RCC); - g_assert_cmpuint(extract32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH), ==, 0xf); + g_assert_cmphex(extract32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH), ==, 0xf); rcc = deposit32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH, 7); writel(SSYS_BASE + RCC, rcc); diff --git a/tests/qtest/erst-test.c b/tests/qtest/erst-test.c index c45bee7f05..36bbe122ab 100644 --- a/tests/qtest/erst-test.c +++ b/tests/qtest/erst-test.c @@ -109,7 +109,7 @@ static void setup_vm_cmd(ERSTState *s, const char *cmd) g_assert_cmpuint(s->reg_barsize, ==, 16); s->mem_bar = qpci_iomap(s->dev, 1, &s->mem_barsize); - g_assert_cmpuint(s->mem_barsize, ==, 0x2000); + g_assert_cmphex(s->mem_barsize, ==, 0x2000); qpci_device_enable(s->dev); } diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c index 9bf8e78df6..fb45fdeb07 100644 --- a/tests/qtest/ivshmem-test.c +++ b/tests/qtest/ivshmem-test.c @@ -158,7 +158,7 @@ static void test_ivshmem_single(void) /* trigger interrupt via registers */ out_reg(s, INTRMASK, 0xffffffff); - g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff); + g_assert_cmphex(in_reg(s, INTRMASK), ==, 0xffffffff); out_reg(s, INTRSTATUS, 1); /* check interrupt status */ g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1); @@ -211,11 +211,11 @@ static void test_ivshmem_pair(void) memset(tmpshmem, 0x42, TMPSHMSIZE); read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x42); + g_assert_cmphex(data[i], ==, 0x42); } read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x42); + g_assert_cmphex(data[i], ==, 0x42); } /* guest 1 write, guest 2 read */ @@ -224,7 +224,7 @@ static void test_ivshmem_pair(void) memset(data, 0, TMPSHMSIZE); read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x43); + g_assert_cmphex(data[i], ==, 0x43); } /* guest 2 write, guest 1 read */ @@ -233,7 +233,7 @@ static void test_ivshmem_pair(void) memset(data, 0, TMPSHMSIZE); read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x44); + g_assert_cmphex(data[i], ==, 0x44); } cleanup_vm(s1); diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index 6d59c7551a..34a75b7f43 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -1046,7 +1046,7 @@ static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba) case CMD_ATAPI_REQUEST_SENSE: case CMD_ATAPI_TEST_UNIT_READY: case CMD_ATAPI_START_STOP_UNIT: - g_assert_cmpuint(lba, ==, 0x00); + g_assert_cmphex(lba, ==, 0x00); break; default: /* SCSI doesn't have uniform packet formats, @@ -1109,7 +1109,7 @@ static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) break; case CMD_ATAPI_READ_CD: /* 24bit BE store */ - g_assert_cmpuint(nsectors, <, 1ULL << 24); + g_assert_cmphex(nsectors, <, 1ULL << 24); tmp = nsectors; cbd[6] = (tmp & 0xFF0000) >> 16; cbd[7] = (tmp & 0xFF00) >> 8; diff --git a/tests/qtest/microbit-test.c b/tests/qtest/microbit-test.c index 72190d38f7..505c831f13 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -143,14 +143,14 @@ static void test_microbit_i2c(void) /* MMA8653 magnetometer detection */ val = i2c_read_byte(qts, 0x3A, 0x0D); - g_assert_cmpuint(val, ==, 0x5A); + g_assert_cmphex(val, ==, 0x5A); val = i2c_read_byte(qts, 0x3A, 0x0D); - g_assert_cmpuint(val, ==, 0x5A); + g_assert_cmphex(val, ==, 0x5A); /* LSM303 accelerometer detection */ val = i2c_read_byte(qts, 0x3C, 0x4F); - g_assert_cmpuint(val, ==, 0x40); + g_assert_cmphex(val, ==, 0x40); qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0); @@ -171,7 +171,7 @@ static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size, /* Check memory */ for (i = 0; i < size / 4; i++) { - g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); } /* Fill memory */ @@ -191,7 +191,7 @@ static void test_nrf51_nvmc(void) /* Test always ready */ value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY); - g_assert_cmpuint(value & 0x01, ==, 0x01); + g_assert_cmphex(value & 0x01, ==, 0x01); /* Test write-read config register */ qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03); @@ -302,19 +302,19 @@ static void test_nrf51_gpio(void) g_assert_cmpuint(actual, ==, expected); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); /* Check clear via DIRCLR */ qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); - g_assert_cmpuint(actual, ==, 0x00000000); + g_assert_cmphex(actual, ==, 0x00000000); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); /* Check set via DIR */ expected = 0x80000001; @@ -323,9 +323,9 @@ static void test_nrf51_gpio(void) g_assert_cmpuint(actual, ==, expected); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); /* Reset DIR */ qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); @@ -334,33 +334,33 @@ static void test_nrf51_gpio(void) qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); /* Check pull-up working */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); /* Check pull-down working */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); @@ -376,11 +376,11 @@ static void test_nrf51_gpio(void) qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); /* * Check short-circuit - generates an guest_error which must be checked @@ -410,7 +410,7 @@ static void test_nrf51_gpio_detect(void) /* Set pin high */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 1); uint32_t actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); - g_assert_cmpuint(actual, ==, 1 << i); + g_assert_cmphex(actual, ==, 1 << i); /* Check that DETECT is high */ g_assert_true(qtest_get_irq(qts, 0)); @@ -418,7 +418,7 @@ static void test_nrf51_gpio_detect(void) /* Set pin low, check that DETECT goes low. */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 0); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); - g_assert_cmpuint(actual, ==, 0x0); + g_assert_cmphex(actual, ==, 0x0); g_assert_false(qtest_get_irq(qts, 0)); } diff --git a/tests/qtest/sse-timer-test.c b/tests/qtest/sse-timer-test.c index a65d7542d5..fd5635d4c9 100644 --- a/tests/qtest/sse-timer-test.c +++ b/tests/qtest/sse-timer-test.c @@ -181,12 +181,12 @@ static void test_timer(void) writel(TIMER_BASE + CNTP_AIVAL_CTL, 0); clock_step_ticks(0x42ULL << 32); g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 4400); - g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42); + g_assert_cmphex(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42); /* Turn on the autoinc again to check AIVAL_HI */ writel(TIMER_BASE + CNTP_AIVAL_CTL, 1); g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600); - g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42); + g_assert_cmphex(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42); } static void test_timer_scale_change(void) diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c index 81830be8ae..7092860b9b 100644 --- a/tests/qtest/stm32l4x5_exti-test.c +++ b/tests/qtest/stm32l4x5_exti-test.c @@ -70,44 +70,44 @@ static void test_reg_write_read(void) /* Test that non-reserved bits in xMR and xTSR can be set and cleared */ exti_writel(EXTI_IMR1, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_IMR1), ==, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_IMR1), ==, 0xFFFFFFFF); exti_writel(EXTI_IMR1, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_IMR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_IMR1), ==, 0x00000000); exti_writel(EXTI_EMR1, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_EMR1), ==, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_EMR1), ==, 0xFFFFFFFF); exti_writel(EXTI_EMR1, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_EMR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_EMR1), ==, 0x00000000); exti_writel(EXTI_RTSR1, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x007DFFFF); + g_assert_cmphex(exti_readl(EXTI_RTSR1), ==, 0x007DFFFF); exti_writel(EXTI_RTSR1, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR1), ==, 0x00000000); exti_writel(EXTI_FTSR1, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x007DFFFF); + g_assert_cmphex(exti_readl(EXTI_FTSR1), ==, 0x007DFFFF); exti_writel(EXTI_FTSR1, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_FTSR1), ==, 0x00000000); exti_writel(EXTI_IMR2, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x000000FF); + g_assert_cmphex(exti_readl(EXTI_IMR2), ==, 0x000000FF); exti_writel(EXTI_IMR2, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_IMR2), ==, 0x00000000); exti_writel(EXTI_EMR2, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x000000FF); + g_assert_cmphex(exti_readl(EXTI_EMR2), ==, 0x000000FF); exti_writel(EXTI_EMR2, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_EMR2), ==, 0x00000000); exti_writel(EXTI_RTSR2, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000078); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000078); exti_writel(EXTI_RTSR2, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000000); exti_writel(EXTI_FTSR2, 0xFFFFFFFF); - g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000078); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000078); exti_writel(EXTI_FTSR2, 0x00000000); - g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000000); } static void test_direct_lines_write(void) @@ -115,28 +115,28 @@ static void test_direct_lines_write(void) /* Test that direct lines reserved bits are not written to */ exti_writel(EXTI_RTSR1, 0xFF820000); - g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR1), ==, 0x00000000); exti_writel(EXTI_FTSR1, 0xFF820000); - g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_FTSR1), ==, 0x00000000); exti_writel(EXTI_SWIER1, 0xFF820000); - g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000000); exti_writel(EXTI_PR1, 0xFF820000); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); exti_writel(EXTI_RTSR2, 0x00000087); - g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000000); exti_writel(EXTI_FTSR2, 0x00000087); - g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000000); exti_writel(EXTI_SWIER2, 0x00000087); - g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000000); exti_writel(EXTI_PR2, 0x00000087); - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); } static void test_reserved_bits_write(void) @@ -144,22 +144,22 @@ static void test_reserved_bits_write(void) /* Test that reserved bits stay are not written to */ exti_writel(EXTI_IMR2, 0xFFFFFF00); - g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_IMR2), ==, 0x00000000); exti_writel(EXTI_EMR2, 0xFFFFFF00); - g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_EMR2), ==, 0x00000000); exti_writel(EXTI_RTSR2, 0xFFFFFF00); - g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000000); exti_writel(EXTI_FTSR2, 0xFFFFFF00); - g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000000); exti_writel(EXTI_SWIER2, 0xFFFFFF00); - g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000000); exti_writel(EXTI_PR2, 0xFFFFFF00); - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); } static void test_software_interrupt(void) @@ -180,7 +180,7 @@ static void test_software_interrupt(void) enable_nvic_irq(EXTI0_IRQ); /* Check that there are no interrupts already pending in PR */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that this specific interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -191,9 +191,9 @@ static void test_software_interrupt(void) exti_writel(EXTI_SWIER1, 0x00000001); /* Check that the write in SWIER was effective */ - g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000001); /* Check that the corresponding pending bit in PR is set */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); /* Check that the corresponding interrupt is pending in the NVIC */ g_assert_true(check_nvic_pending(EXTI0_IRQ)); @@ -201,9 +201,9 @@ static void test_software_interrupt(void) exti_writel(EXTI_PR1, 0x00000001); /* Check that the write in PR was effective */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the corresponding bit in SWIER was cleared */ - g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000000); /* Check that the interrupt is still pending in the NVIC */ g_assert_true(check_nvic_pending(EXTI0_IRQ)); @@ -214,7 +214,7 @@ static void test_software_interrupt(void) enable_nvic_irq(EXTI35_IRQ); /* Check that there are no interrupts already pending */ - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); g_assert_false(check_nvic_pending(EXTI35_IRQ)); /* Enable interrupt line EXTI0 */ @@ -224,9 +224,9 @@ static void test_software_interrupt(void) exti_writel(EXTI_SWIER2, 0x00000008); /* Check that the write in SWIER was effective */ - g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000008); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000008); /* Check that the corresponding pending bit in PR is set */ - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000008); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000008); /* Check that the corresponding interrupt is pending in the NVIC */ g_assert_true(check_nvic_pending(EXTI35_IRQ)); @@ -234,9 +234,9 @@ static void test_software_interrupt(void) exti_writel(EXTI_PR2, 0x00000008); /* Check that the write in PR was effective */ - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); /* Check that the corresponding bit in SWIER was cleared */ - g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000000); /* Check that the interrupt is still pending in the NVIC */ g_assert_true(check_nvic_pending(EXTI35_IRQ)); @@ -259,16 +259,16 @@ static void test_edge_selector(void) /* Test that an irq is raised on rising edge only */ exti_set_irq(0, 0); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); g_assert_false(check_nvic_pending(EXTI0_IRQ)); exti_set_irq(0, 1); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); g_assert_true(check_nvic_pending(EXTI0_IRQ)); /* Clean the test */ exti_writel(EXTI_PR1, 0x00000001); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); unpend_nvic_irq(EXTI0_IRQ); g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -280,16 +280,16 @@ static void test_edge_selector(void) /* Test that an irq is raised on falling edge only */ exti_set_irq(0, 1); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); g_assert_false(check_nvic_pending(EXTI0_IRQ)); exti_set_irq(0, 0); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); g_assert_true(check_nvic_pending(EXTI0_IRQ)); /* Clean the test */ exti_writel(EXTI_PR1, 0x00000001); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); unpend_nvic_irq(EXTI0_IRQ); g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -300,23 +300,23 @@ static void test_edge_selector(void) /* Test that an irq is raised on rising edge */ exti_set_irq(0, 1); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); g_assert_true(check_nvic_pending(EXTI0_IRQ)); /* Clean the test */ exti_writel(EXTI_PR1, 0x00000001); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); unpend_nvic_irq(EXTI0_IRQ); g_assert_false(check_nvic_pending(EXTI0_IRQ)); /* Test that an irq is raised on falling edge */ exti_set_irq(0, 0); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); g_assert_true(check_nvic_pending(EXTI0_IRQ)); /* Clean the test */ exti_writel(EXTI_PR1, 0x00000001); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); unpend_nvic_irq(EXTI0_IRQ); g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -327,11 +327,11 @@ static void test_edge_selector(void) /* Test that no irq is raised */ exti_set_irq(0, 1); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); g_assert_false(check_nvic_pending(EXTI0_IRQ)); exti_set_irq(0, 0); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); g_assert_false(check_nvic_pending(EXTI0_IRQ)); } @@ -350,7 +350,7 @@ static void test_no_software_interrupt(void) enable_nvic_irq(EXTI0_IRQ); /* Check that there are no interrupts already pending in PR */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that this specific interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -361,9 +361,9 @@ static void test_no_software_interrupt(void) exti_writel(EXTI_SWIER1, 0x00000001); /* Check that the write in SWIER was effective */ - g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000001); + g_assert_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000001); /* Check that the pending bit in PR wasn't set */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -371,7 +371,7 @@ static void test_no_software_interrupt(void) exti_writel(EXTI_IMR1, 0x00000001); /* Check that the pending bit in PR wasn't set */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI0_IRQ)); @@ -382,7 +382,7 @@ static void test_no_software_interrupt(void) enable_nvic_irq(EXTI35_IRQ); /* Check that there are no interrupts already pending in PR */ - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); /* Check that this specific interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI35_IRQ)); @@ -393,9 +393,9 @@ static void test_no_software_interrupt(void) exti_writel(EXTI_SWIER2, 0x00000008); /* Check that the write in SWIER was effective */ - g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000008); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000008); /* Check that the pending bit in PR wasn't set */ - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI35_IRQ)); @@ -403,7 +403,7 @@ static void test_no_software_interrupt(void) exti_writel(EXTI_IMR2, 0x00000008); /* Check that the pending bit in PR wasn't set */ - g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI35_IRQ)); } @@ -423,7 +423,7 @@ static void test_masked_interrupt(void) enable_nvic_irq(EXTI1_IRQ); /* Check that there are no interrupts already pending in PR */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that this specific interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI1_IRQ)); @@ -437,7 +437,7 @@ static void test_masked_interrupt(void) exti_set_irq(1, 1); /* Check that the pending bit in PR wasn't set */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI1_IRQ)); @@ -445,7 +445,7 @@ static void test_masked_interrupt(void) exti_writel(EXTI_IMR1, 0x00000002); /* Check that the pending bit in PR wasn't set */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI1_IRQ)); } @@ -469,7 +469,7 @@ static void test_interrupt(void) enable_nvic_irq(EXTI1_IRQ); /* Check that there are no interrupts already pending in PR */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that this specific interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI1_IRQ)); @@ -483,7 +483,7 @@ static void test_interrupt(void) exti_set_irq(1, 1); /* Check that the pending bit in PR was set */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000002); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000002); /* Check that the interrupt is pending in NVIC */ g_assert_true(check_nvic_pending(EXTI1_IRQ)); @@ -491,7 +491,7 @@ static void test_interrupt(void) exti_writel(EXTI_PR1, 0x00000002); /* Check that the write in PR was effective */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the interrupt is still pending in the NVIC */ g_assert_true(check_nvic_pending(EXTI1_IRQ)); @@ -509,7 +509,7 @@ static void test_orred_interrupts(void) */ enable_nvic_irq(EXTI5_9_IRQ); /* Check that there are no interrupts already pending in PR */ - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that this specific interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI5_9_IRQ)); @@ -522,11 +522,11 @@ static void test_orred_interrupts(void) /* Raise GPIO line i, check that the interrupt is pending */ for (unsigned i = 5; i < 10; i++) { exti_set_irq(i, 1); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 1 << i); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 1 << i); g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); exti_writel(EXTI_PR1, 1 << i); - g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); unpend_nvic_irq(EXTI5_9_IRQ); diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index ed4801798d..59bac829b7 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -54,27 +54,27 @@ static void test_reset(void) /* * Test that registers are initialized at the correct values */ - g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SCSR), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SCSR), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SWPR), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR2), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SWPR2), ==, 0x00000000); } static void test_reserved_bits(void) @@ -87,25 +87,25 @@ static void test_reserved_bits(void) * register is still at reset value */ syscfg_writel(SYSCFG_MEMRMP, 0xFFFFFEF8); - g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); syscfg_writel(SYSCFG_CFGR1, 0x7F00FEFF); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); syscfg_writel(SYSCFG_EXTICR1, 0xFFFF0000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); syscfg_writel(SYSCFG_EXTICR2, 0xFFFF0000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); syscfg_writel(SYSCFG_EXTICR3, 0xFFFF0000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); syscfg_writel(SYSCFG_EXTICR4, 0xFFFF0000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); syscfg_writel(SYSCFG_SKR, 0xFFFFFF00); - g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); } static void test_set_and_clear(void) @@ -114,40 +114,40 @@ static void test_set_and_clear(void) * Test that regular bits can be set and cleared */ syscfg_writel(SYSCFG_MEMRMP, 0x00000107); - g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000107); + g_assert_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000107); syscfg_writel(SYSCFG_MEMRMP, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); /* cfgr1 bit 0 is clear only so we keep it set */ syscfg_writel(SYSCFG_CFGR1, 0xFCFF0101); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0xFCFF0101); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0xFCFF0101); syscfg_writel(SYSCFG_CFGR1, 0x00000001); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000001); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000001); syscfg_writel(SYSCFG_EXTICR1, 0x0000FFFF); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x0000FFFF); syscfg_writel(SYSCFG_EXTICR1, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); syscfg_writel(SYSCFG_EXTICR2, 0x0000FFFF); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x0000FFFF); syscfg_writel(SYSCFG_EXTICR2, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); syscfg_writel(SYSCFG_EXTICR3, 0x0000FFFF); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x0000FFFF); syscfg_writel(SYSCFG_EXTICR3, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); syscfg_writel(SYSCFG_EXTICR4, 0x0000FFFF); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x0000FFFF); syscfg_writel(SYSCFG_EXTICR4, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); syscfg_writel(SYSCFG_SKR, 0x000000FF); - g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x000000FF); + g_assert_cmphex(syscfg_readl(SYSCFG_SKR), ==, 0x000000FF); syscfg_writel(SYSCFG_SKR, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); } static void test_clear_by_writing_1(void) @@ -156,7 +156,7 @@ static void test_clear_by_writing_1(void) * Test that writing '1' doesn't set the bit */ syscfg_writel(SYSCFG_CFGR2, 0x00000100); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); } static void test_set_only_bits(void) @@ -166,15 +166,15 @@ static void test_set_only_bits(void) */ syscfg_writel(SYSCFG_CFGR2, 0x0000000F); syscfg_writel(SYSCFG_CFGR2, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x0000000F); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR2), ==, 0x0000000F); syscfg_writel(SYSCFG_SWPR, 0xFFFFFFFF); syscfg_writel(SYSCFG_SWPR, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR), ==, 0xFFFFFFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_SWPR), ==, 0xFFFFFFFF); syscfg_writel(SYSCFG_SWPR2, 0xFFFFFFFF); syscfg_writel(SYSCFG_SWPR2, 0x00000000); - g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); system_reset(); } @@ -186,7 +186,7 @@ static void test_clear_only_bits(void) */ syscfg_writel(SYSCFG_CFGR1, 0x00000000); syscfg_writel(SYSCFG_CFGR1, 0x00000001); - g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); system_reset(); } From 838f82468a1282f7e89dbbd6c015c8742bfdafce Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 12 Apr 2024 16:53:58 +0800 Subject: [PATCH 0142/2884] docs: i386: pc: Update maximum CPU numbers for PC Q35 Commit e4e98c7eebfa ("pc: q35: Bump max_cpus to 4096 vcpus") increases the supported CPUs for PC Q35 machine. Update maximum CPU numbers for PC Q35 in the document. Signed-off-by: Zhao Liu Message-ID: <20240412085358.731560-1-zhao1.liu@linux.intel.com> Signed-off-by: Thomas Huth --- docs/system/target-i386-desc.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/target-i386-desc.rst.inc b/docs/system/target-i386-desc.rst.inc index 5ebbcda9db..319e540573 100644 --- a/docs/system/target-i386-desc.rst.inc +++ b/docs/system/target-i386-desc.rst.inc @@ -36,7 +36,7 @@ The QEMU PC System emulator simulates the following peripherals: - PCI UHCI, OHCI, EHCI or XHCI USB controller and a virtual USB-1.1 hub. -SMP is supported with up to 255 CPUs. +SMP is supported with up to 255 CPUs (and 4096 CPUs for PC Q35 machine). QEMU uses the PC BIOS from the Seabios project and the Plex86/Bochs LGPL VGA BIOS. From 5e279f38c7eabfcb9c3ab0bac8ae04316c3d7814 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Wed, 10 Apr 2024 08:17:26 -0400 Subject: [PATCH 0143/2884] tests/vm: update openbsd image to 7.5 tests/vm: update openbsd to release 7.5 Signed-off-by: Brad Smith Message-ID: Signed-off-by: Thomas Huth --- tests/vm/openbsd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 85c9863633..5e646f7c51 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM): name = "openbsd" arch = "x86_64" - link = "https://cdn.openbsd.org/pub/OpenBSD/7.4/amd64/install74.iso" - csum = "a1001736ed9fe2307965b5fcdb426ae11f9b80d26eb21e404a705144a0a224a0" + link = "https://cdn.openbsd.org/pub/OpenBSD/7.5/amd64/install75.iso" + csum = "034435c6e27405d5a7fafb058162943c194eb793dafdc412c08d49bb56b3892a" size = "20G" pkgs = [ # tools @@ -124,7 +124,7 @@ class OpenBSDVM(basevm.BaseVM): self.console_wait_send("Allow root ssh login", "yes\n") self.console_wait_send("timezone", "UTC\n") self.console_wait_send("root disk", "\n") - self.console_wait_send("Encrypt the root disk with a passphrase", "no\n") + self.console_wait_send("Encrypt the root disk with a (p)assphrase", "no\n") self.console_wait_send("(W)hole disk", "\n") self.console_wait_send("(A)uto layout", "c\n") From 6705587adbf11dacb9722e7c4091936439ae5dad Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 20 Mar 2024 11:41:41 +0100 Subject: [PATCH 0144/2884] Revert ".travis.yml: Cache Avocado cache" This reverts commit c1073e44b46490133e16420e1784dec7bcd4e030. The Avocado tests have been removed from Travis a long time ago with commit c5008c76ee ("gitlab: add acceptance testing to system builds"), so we don't need to cache the avocado files here anymore. Message-ID: <20240320104144.823425-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a3ae76a7c..8da88c4360 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,6 @@ cache: timeout: 1200 ccache: true pip: true - directories: - - $HOME/avocado/data/cache # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu From aeb99d0772477c6c94e7def85526ee88fbe6135b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 20 Mar 2024 11:41:43 +0100 Subject: [PATCH 0145/2884] .travis.yml: Remove the unused UNRELIABLE environment variable This variable was used to allow jobs to fail without spoiling the overall result. But the required "allow_failures:" hunk has been accidentally removed in commit 9d03f5abed ("travis.yml: Remove the "Release tarball" job"), and it was anyway only useful while we still had the x86 jobs here around that were our main CI jobs. Thus let's simply remove this useless variable now. Message-ID: <20240320104144.823425-6-thuth@redhat.com> Signed-off-by: Thomas Huth --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8da88c4360..196725d5a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -113,7 +113,6 @@ jobs: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --enable-fdt=system --target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false" - - UNRELIABLE=true - name: "[ppc64] GCC check-tcg" arch: ppc64le @@ -184,7 +183,6 @@ jobs: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=hppa-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" - - UNRELIABLE=true script: - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? - | @@ -274,4 +272,3 @@ jobs: - TEST_CMD="make check-unit" - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools --enable-fdt=system --host-cc=clang --cxx=clang++" - - UNRELIABLE=true From 66163bc7d79f40fb323d405559f19edb0f92f72b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:48 +0200 Subject: [PATCH 0146/2884] .travis.yml: Update the jobs to Ubuntu 22.04 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to our support policy, we'll soon drop our official support for Ubuntu 20.04 ("Focal Fossa") in QEMU. Thus we should update the Travis jobs now to a newer release (Ubuntu 22.04 - "Jammy Jellyfish") for future testing. Since all jobs are using this release now, we can drop the entries from the individual jobs and use the global setting again. Reviewed-by: Daniel P. Berrangé Message-ID: <20240418101056.302103-6-thuth@redhat.com> Signed-off-by: Thomas Huth --- .travis.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 196725d5a3..7527f71c05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ os: linux -dist: focal +dist: jammy language: c compiler: - gcc @@ -7,7 +7,7 @@ cache: # There is one cache per branch and compiler version. # characteristics of each job are used to identify the cache: # - OS name (currently only linux) - # - OS distribution (for Linux, bionic or focal) + # - OS distribution (e.g. "jammy" for Linux) # - Names and values of visible environment variables set in .travis.yml or Settings panel timeout: 1200 ccache: true @@ -81,7 +81,6 @@ jobs: - name: "[aarch64] GCC check-tcg" arch: arm64 - dist: focal addons: apt_packages: - libaio-dev @@ -116,7 +115,6 @@ jobs: - name: "[ppc64] GCC check-tcg" arch: ppc64le - dist: focal addons: apt_packages: - libaio-dev @@ -151,7 +149,6 @@ jobs: - name: "[s390x] GCC check-tcg" arch: s390x - dist: focal addons: apt_packages: - libaio-dev @@ -195,7 +192,6 @@ jobs: - name: "[s390x] GCC (other-system)" arch: s390x - dist: focal addons: apt_packages: - libaio-dev @@ -225,7 +221,6 @@ jobs: - name: "[s390x] GCC (user)" arch: s390x - dist: focal addons: apt_packages: - libgcrypt20-dev @@ -240,8 +235,7 @@ jobs: - name: "[s390x] Clang (disable-tcg)" arch: s390x - dist: focal - compiler: clang-10 + compiler: clang addons: apt_packages: - libaio-dev @@ -267,7 +261,6 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build - - clang-10 env: - TEST_CMD="make check-unit" - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools From 4d6ae2df56b7ef7a479a1656872a95e0ed23f2d9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 20 Mar 2024 11:41:44 +0100 Subject: [PATCH 0147/2884] .travis.yml: Do some more testing with Clang We are doing a lot of cross-compilation tests with GCC in the gitlab-CI already, so we could get some more test coverage by using Clang in the Travis-CI instead. Thus let's switch two additional jobs to use Clang for compilation. Message-ID: <20240320104144.823425-7-thuth@redhat.com> Signed-off-by: Thomas Huth --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7527f71c05..cef0308952 100644 --- a/.travis.yml +++ b/.travis.yml @@ -113,8 +113,9 @@ jobs: - CONFIG="--disable-containers --enable-fdt=system --target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false" - - name: "[ppc64] GCC check-tcg" + - name: "[ppc64] Clang check-tcg" arch: ppc64le + compiler: clang addons: apt_packages: - libaio-dev @@ -190,8 +191,9 @@ jobs: $(exit $BUILD_RC); fi - - name: "[s390x] GCC (other-system)" + - name: "[s390x] Clang (other-system)" arch: s390x + compiler: clang addons: apt_packages: - libaio-dev From 45070eb716172cf168f6334fe5346510cdde1a92 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:44 +0200 Subject: [PATCH 0148/2884] tests: Remove Ubuntu 20.04 container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since Ubuntu 22.04 has now been available for more than two years, we can stop actively supporting the previous LTS version of Ubuntu now. Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240418101056.302103-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/docker/dockerfiles/ubuntu2004.docker | 157 --------------------- tests/lcitool/refresh | 1 - 2 files changed, 158 deletions(-) delete mode 100644 tests/docker/dockerfiles/ubuntu2004.docker diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker deleted file mode 100644 index d3e212060c..0000000000 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ /dev/null @@ -1,157 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all ubuntu-2004 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM docker.io/library/ubuntu:20.04 - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bison \ - bsdmainutils \ - bzip2 \ - ca-certificates \ - ccache \ - clang \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - flex \ - g++ \ - gcc \ - gcovr \ - gettext \ - git \ - hostname \ - libaio-dev \ - libasan6 \ - libasound2-dev \ - libattr1-dev \ - libbrlapi-dev \ - libbz2-dev \ - libc6-dev \ - libcacard-dev \ - libcap-ng-dev \ - libcapstone-dev \ - libcmocka-dev \ - libcurl4-gnutls-dev \ - libdaxctl-dev \ - libdrm-dev \ - libepoxy-dev \ - libfdt-dev \ - libffi-dev \ - libfuse3-dev \ - libgbm-dev \ - libgcrypt20-dev \ - libglib2.0-dev \ - libglusterfs-dev \ - libgnutls28-dev \ - libgtk-3-dev \ - libibumad-dev \ - libibverbs-dev \ - libiscsi-dev \ - libjemalloc-dev \ - libjpeg-turbo8-dev \ - libjson-c-dev \ - liblttng-ust-dev \ - liblzo2-dev \ - libncursesw5-dev \ - libnfs-dev \ - libnuma-dev \ - libpam0g-dev \ - libpcre2-dev \ - libpixman-1-dev \ - libpmem-dev \ - libpng-dev \ - libpulse-dev \ - librbd-dev \ - librdmacm-dev \ - libsasl2-dev \ - libsdl2-dev \ - libsdl2-image-dev \ - libseccomp-dev \ - libselinux1-dev \ - libslirp-dev \ - libsnappy-dev \ - libsndio-dev \ - libspice-protocol-dev \ - libspice-server-dev \ - libssh-dev \ - libsystemd-dev \ - libtasn1-6-dev \ - libubsan1 \ - libudev-dev \ - libusb-1.0-0-dev \ - libusbredirhost-dev \ - libvdeplug-dev \ - libvirglrenderer-dev \ - libvte-2.91-dev \ - libxen-dev \ - libzstd-dev \ - llvm \ - locales \ - make \ - mtools \ - multipath-tools \ - ncat \ - nettle-dev \ - ninja-build \ - openssh-client \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - socat \ - sparse \ - systemtap-sdt-dev \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - xfslibs-dev \ - xorriso \ - zlib1g-dev \ - zstd && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales && \ - dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ - mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc - -RUN /usr/bin/pip3 install \ - meson==0.63.2 \ - tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index fe7692c500..692752a3df 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -130,7 +130,6 @@ try: trailer="".join(debian12_extras)) generate_dockerfile("fedora", "fedora-38") generate_dockerfile("opensuse-leap", "opensuse-leap-15") - generate_dockerfile("ubuntu2004", "ubuntu-2004") generate_dockerfile("ubuntu2204", "ubuntu-2204") # From c723d9d16f15e4255f9bcf293e55763da21ab51f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:45 +0200 Subject: [PATCH 0149/2884] tests/lcitool/libvirt-ci: Update to the latest master branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need the latest fixes for the lcitool to be able to properly update our CentOS docker file to CentOS Stream 9. Reviewed-by: Daniel P. Berrangé Message-ID: <20240418101056.302103-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/lcitool/libvirt-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index 77c800186f..cec6703971 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit 77c800186f34b21be7660750577cc5582a914deb +Subproject commit cec67039719becbfbab866f9c23574f389cf9559 From 2355d18c79e05c2b790113c95c5ccfee35fb7fcf Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:46 +0200 Subject: [PATCH 0150/2884] tests/docker/dockerfiles: Run lcitool-refresh after the lcitool update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This update adds the removing of the EXTERNALLY-MANAGED marker files that has been added to the lcitool recently. Quoting Daniel: "For those who don't know, python now commonly blocks the ability to run 'pip install' outside of a venv. This generally makes sense for a precious installation environment. Our containers are disposable though, so a venv has no benefit. Removing the 'EXTERNALLY-MANAGED' allows the historical arbitrary use of 'pip' outside a venv. lcitool just does this unconditionally given the containers are not precious." Reviewed-by: Daniel P. Berrangé Message-ID: <20240418101056.302103-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/docker/dockerfiles/alpine.docker | 3 ++- tests/docker/dockerfiles/centos8.docker | 1 + tests/docker/dockerfiles/debian-amd64-cross.docker | 3 ++- tests/docker/dockerfiles/debian-arm64-cross.docker | 3 ++- tests/docker/dockerfiles/debian-armel-cross.docker | 3 ++- tests/docker/dockerfiles/debian-armhf-cross.docker | 3 ++- tests/docker/dockerfiles/debian-i686-cross.docker | 3 ++- tests/docker/dockerfiles/debian-mips64el-cross.docker | 3 ++- tests/docker/dockerfiles/debian-mipsel-cross.docker | 3 ++- tests/docker/dockerfiles/debian-ppc64el-cross.docker | 3 ++- tests/docker/dockerfiles/debian-riscv64-cross.docker | 3 ++- tests/docker/dockerfiles/debian-s390x-cross.docker | 3 ++- tests/docker/dockerfiles/debian.docker | 1 + tests/docker/dockerfiles/fedora-win64-cross.docker | 3 ++- tests/docker/dockerfiles/fedora.docker | 1 + tests/docker/dockerfiles/opensuse-leap.docker | 1 + tests/docker/dockerfiles/ubuntu2204.docker | 1 + 17 files changed, 29 insertions(+), 12 deletions(-) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 42f6928627..cd9d7af1ce 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -116,7 +116,8 @@ RUN apk update && \ zlib-static \ zstd \ zstd-dev && \ - apk list | sort > /packages.txt && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + apk list --installed | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index d97c30e96a..ea618bf352 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -123,6 +123,7 @@ RUN dnf distro-sync -y && \ zstd && \ dnf autoremove -y && \ dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 00bdc06021..d0b0e9778e 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -64,7 +64,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 2dae3777f7..8cb225740e 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -64,7 +64,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index 75342c09b0..e6f37418ed 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -65,7 +65,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 180ed836e6..407a014f57 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -64,7 +64,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 3fc4e15acd..bdc9566b67 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -65,7 +65,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 17d3e01ecc..4d995d0b12 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -65,7 +65,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 5fcd641f15..0cf803bda5 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -65,7 +65,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index d6be2f0cc5..6180ec08c3 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -64,7 +64,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index a26637ec4f..591572ae94 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -33,7 +33,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index ec0041d6aa..90c8d3c7b8 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -64,7 +64,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index b5e642d5b6..5722482e4c 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -137,6 +137,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index f8e4cb70d3..d1a480fa71 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -64,7 +64,8 @@ exec "$@"\n' > /usr/bin/nosync && \ xorriso \ zstd && \ nosync dnf autoremove -y && \ - nosync dnf clean all -y + nosync dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 9e9c71fa94..7e6ab0308a 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -137,6 +137,7 @@ exec "$@"\n' > /usr/bin/nosync && \ zstd && \ nosync dnf autoremove -y && \ nosync dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index cf753383a4..c4055bdd10 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -119,6 +119,7 @@ RUN zypper update -y && \ zlib-devel-static \ zstd && \ zypper clean --all && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 2ca9cff79c..b8e78331db 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -137,6 +137,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ From 641b1efe01b2dd6e7ac92f23d392dcee73508746 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:47 +0200 Subject: [PATCH 0151/2884] tests: Update our CI to use CentOS Stream 9 instead of 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RHEL 9 (and thus also the derivatives) have been available since two years now, so according to QEMU's support policy, we can drop the active support for the previous major version 8 now. Another reason for doing this is that Centos Stream 8 will go EOL soon: https://blog.centos.org/2023/04/end-dates-are-coming-for-centos-stream-8-and-centos-linux-7/ "After May 31, 2024, CentOS Stream 8 will be archived and no further updates will be provided." Thus upgrade our CentOS Stream container to major version 9 now. Reviewed-by: Daniel P. Berrangé Message-ID: <20240418101056.302103-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest.yml | 16 ++++----- .gitlab-ci.d/container-core.yml | 4 +-- .../{centos8.docker => centos9.docker} | 34 +++++++------------ tests/lcitool/mappings.yml | 20 ----------- tests/lcitool/refresh | 2 +- tests/vm/centos | 4 +-- 6 files changed, 26 insertions(+), 54 deletions(-) rename tests/docker/dockerfiles/{centos8.docker => centos9.docker} (82%) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 5848c73e00..6394b8f41e 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -158,9 +158,9 @@ build-system-centos: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-centos8-container + job: amd64-centos9-container variables: - IMAGE: centos8 + IMAGE: centos9 CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server --enable-modules --enable-trace-backends=dtrace --enable-docs TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu @@ -242,7 +242,7 @@ check-system-centos: - job: build-system-centos artifacts: true variables: - IMAGE: centos8 + IMAGE: centos9 MAKE_CHECK_ARGS: check avocado-system-centos: @@ -251,7 +251,7 @@ avocado-system-centos: - job: build-system-centos artifacts: true variables: - IMAGE: centos8 + IMAGE: centos9 MAKE_CHECK_ARGS: check-avocado AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx arch:sh4 @@ -327,9 +327,9 @@ avocado-system-flaky: build-tcg-disabled: extends: .native_build_job_template needs: - job: amd64-centos8-container + job: amd64-centos9-container variables: - IMAGE: centos8 + IMAGE: centos9 script: - mkdir build - cd build @@ -651,9 +651,9 @@ build-tci: build-without-defaults: extends: .native_build_job_template needs: - job: amd64-centos8-container + job: amd64-centos9-container variables: - IMAGE: centos8 + IMAGE: centos9 CONFIGURE_ARGS: --without-default-devices --without-default-features diff --git a/.gitlab-ci.d/container-core.yml b/.gitlab-ci.d/container-core.yml index 08f8450fa1..5459447676 100644 --- a/.gitlab-ci.d/container-core.yml +++ b/.gitlab-ci.d/container-core.yml @@ -1,10 +1,10 @@ include: - local: '/.gitlab-ci.d/container-template.yml' -amd64-centos8-container: +amd64-centos9-container: extends: .container_job_template variables: - NAME: centos8 + NAME: centos9 amd64-fedora-container: extends: .container_job_template diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos9.docker similarity index 82% rename from tests/docker/dockerfiles/centos8.docker rename to tests/docker/dockerfiles/centos9.docker index ea618bf352..6cf47ce786 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -1,15 +1,14 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all centos-stream-8 qemu +# $ lcitool dockerfile --layers all centos-stream-9 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM quay.io/centos/centos:stream8 +FROM quay.io/centos/centos:stream9 RUN dnf distro-sync -y && \ dnf install 'dnf-command(config-manager)' -y && \ - dnf config-manager --set-enabled -y powertools && \ - dnf install -y centos-release-advanced-virtualization && \ + dnf config-manager --set-enabled -y crb && \ dnf install -y epel-release && \ dnf install -y epel-next-release && \ dnf install -y \ @@ -42,7 +41,6 @@ RUN dnf distro-sync -y && \ glib2-static \ glibc-langpack-en \ glibc-static \ - glusterfs-api-devel \ gnutls-devel \ gtk3-devel \ hostname \ @@ -82,6 +80,7 @@ RUN dnf distro-sync -y && \ lzo-devel \ make \ mesa-libgbm-devel \ + meson \ mtools \ ncurses-devel \ nettle-devel \ @@ -95,25 +94,25 @@ RUN dnf distro-sync -y && \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ - python38 \ - python38-PyYAML \ - python38-numpy \ - python38-pip \ - python38-setuptools \ - python38-wheel \ + python3 \ + python3-PyYAML \ + python3-numpy \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx_rtd_theme \ + python3-tomli \ rdma-core-devel \ sed \ snappy-devel \ socat \ spice-protocol \ - spice-server-devel \ swtpm \ systemd-devel \ systemtap-sdt-devel \ tar \ usbredir-devel \ util-linux \ - virglrenderer-devel \ vte291-devel \ which \ xfsprogs-devel \ @@ -132,18 +131,11 @@ RUN dnf distro-sync -y && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3.8 install \ - meson==0.63.2 \ - pillow \ - sphinx \ - sphinx-rtd-theme \ - tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3.8" +ENV PYTHON "/usr/bin/python3" # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 407c03301b..03b974ad02 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -1,66 +1,50 @@ mappings: flake8: - CentOSStream8: OpenSUSELeap15: meson: - CentOSStream8: OpenSUSELeap15: python3: - CentOSStream8: python38 OpenSUSELeap15: python311-base python3-PyYAML: - CentOSStream8: python38-PyYAML OpenSUSELeap15: python3-devel: - CentOSStream8: python38-devel OpenSUSELeap15: python311-devel python3-docutils: - CentOSStream8: OpenSUSELeap15: python3-numpy: - CentOSStream8: python38-numpy OpenSUSELeap15: python3-opencv: - CentOSStream8: OpenSUSELeap15: python3-pillow: - CentOSStream8: OpenSUSELeap15: python3-pip: - CentOSStream8: python38-pip OpenSUSELeap15: python311-pip python3-pillow: - CentOSStream8: OpenSUSELeap15: python3-selinux: - CentOSStream8: OpenSUSELeap15: python3-setuptools: - CentOSStream8: python38-setuptools OpenSUSELeap15: python311-setuptools python3-sphinx: - CentOSStream8: OpenSUSELeap15: python3-sphinx-rtd-theme: - CentOSStream8: OpenSUSELeap15: python3-sqlite3: - CentOSStream8: python38 OpenSUSELeap15: python311 python3-tomli: @@ -69,15 +53,11 @@ mappings: Fedora: Debian12: OpenSUSELeap15: - # Not available for Python 3.8 - CentOSStream8: python3-venv: - CentOSStream8: python38 OpenSUSELeap15: python311-base python3-wheel: - CentOSStream8: python38-wheel OpenSUSELeap15: python311-pip pypi_mappings: diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 692752a3df..24a735a3f2 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -125,7 +125,7 @@ try: # Standard native builds # generate_dockerfile("alpine", "alpine-318") - generate_dockerfile("centos8", "centos-stream-8") + generate_dockerfile("centos9", "centos-stream-9") generate_dockerfile("debian", "debian-12", trailer="".join(debian12_extras)) generate_dockerfile("fedora", "fedora-38") diff --git a/tests/vm/centos b/tests/vm/centos index 097a9ca14d..d25c8f8b5b 100755 --- a/tests/vm/centos +++ b/tests/vm/centos @@ -26,8 +26,8 @@ class CentosVM(basevm.BaseVM): export SRC_ARCHIVE=/dev/vdb; sudo chmod a+r $SRC_ARCHIVE; tar -xf $SRC_ARCHIVE; - make docker-test-block@centos8 {verbose} J={jobs} NETWORK=1; - make docker-test-quick@centos8 {verbose} J={jobs} NETWORK=1; + make docker-test-block@centos9 {verbose} J={jobs} NETWORK=1; + make docker-test-quick@centos9 {verbose} J={jobs} NETWORK=1; """ def build_image(self, img): From 443df40cad0f5de602ac6f6ca1559e0923e180b3 Mon Sep 17 00:00:00 2001 From: Maksim Davydov Date: Tue, 19 Mar 2024 00:35:47 +0300 Subject: [PATCH 0152/2884] qom: add default value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmp_qom_list_properties can print default values if they are available as qmp_device_list_properties does, because both of them use the ObjectPropertyInfo structure with default_value field. This can be useful when working with "not device" types (e.g. memory-backend). Signed-off-by: Maksim Davydov Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-ID: <20240318213550.155573-2-davydov-max@yandex-team.ru> Signed-off-by: Philippe Mathieu-Daudé --- qom/qom-qmp-cmds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 7c087299de..e91a235347 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -212,6 +212,7 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, info->name = g_strdup(prop->name); info->type = g_strdup(prop->type); info->description = g_strdup(prop->description); + info->default_value = qobject_ref(prop->defval); QAPI_LIST_PREPEND(prop_list, info); } From 236e9397b320518372a67c55777193c032b93d89 Mon Sep 17 00:00:00 2001 From: Maksim Davydov Date: Tue, 19 Mar 2024 00:35:48 +0300 Subject: [PATCH 0153/2884] qmp: add dump machine type compatibility properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To control that creating new machine type doesn't affect the previous types (their compat_props) and to check complex compat_props inheritance we need qmp command to print machine type compatibility properties. This patch adds the ability to get list of all the compat_props of the corresponding supported machines for their comparison via new optional argument of "query-machines" command. Since information on compatibility properties can increase the command output by a factor of 40, add an argument to enable it, default off. Signed-off-by: Maksim Davydov Reviewed-by: Vladimir Sementsov-Ogievskiy Acked-by: Markus Armbruster Message-ID: <20240318213550.155573-3-davydov-max@yandex-team.ru> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-qmp-cmds.c | 23 ++++++++++++- qapi/machine.json | 67 +++++++++++++++++++++++++++++++++++-- tests/qtest/fuzz/qos_fuzz.c | 2 +- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index c20829b9ae..5972100b1f 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -64,7 +64,8 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) return head; } -MachineInfoList *qmp_query_machines(Error **errp) +MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, + Error **errp) { GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); MachineInfoList *mach_list = NULL; @@ -96,6 +97,26 @@ MachineInfoList *qmp_query_machines(Error **errp) info->default_ram_id = g_strdup(mc->default_ram_id); } + if (compat_props && mc->compat_props) { + int i; + info->compat_props = NULL; + CompatPropertyList **tail = &(info->compat_props); + info->has_compat_props = true; + + for (i = 0; i < mc->compat_props->len; i++) { + GlobalProperty *mt_prop = g_ptr_array_index(mc->compat_props, + i); + CompatProperty *prop; + + prop = g_malloc0(sizeof(*prop)); + prop->qom_type = g_strdup(mt_prop->driver); + prop->property = g_strdup(mt_prop->property); + prop->value = g_strdup(mt_prop->value); + + QAPI_LIST_APPEND(tail, prop); + } + } + QAPI_LIST_PREPEND(mach_list, info); } diff --git a/qapi/machine.json b/qapi/machine.json index 2df407e877..3e9cc3f17d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -135,6 +135,26 @@ ## { 'command': 'query-cpus-fast', 'returns': [ 'CpuInfoFast' ] } +## +# @CompatProperty: +# +# Property default values specific to a machine type, for use by +# scripts/compare-machine-types. +# +# @qom-type: name of the QOM type to which the default applies +# +# @property: name of its property to which the default applies +# +# @value: the default value (machine-specific default can overwrite +# the "default" default, to avoid this use -machine none) +# +# Since: 9.1 +## +{ 'struct': 'CompatProperty', + 'data': { 'qom-type': 'str', + 'property': 'str', + 'value': 'str' } } + ## # @MachineInfo: # @@ -166,6 +186,14 @@ # # @acpi: machine type supports ACPI (since 8.0) # +# @compat-props: The machine type's compatibility properties. Only +# present when query-machines argument @compat-props is true. +# (since 9.1) +# +# Features: +# +# @unstable: Member @compat-props is experimental. +# # Since: 1.2 ## { 'struct': 'MachineInfo', @@ -173,18 +201,53 @@ '*is-default': 'bool', 'cpu-max': 'int', 'hotpluggable-cpus': 'bool', 'numa-mem-supported': 'bool', 'deprecated': 'bool', '*default-cpu-type': 'str', - '*default-ram-id': 'str', 'acpi': 'bool' } } + '*default-ram-id': 'str', 'acpi': 'bool', + '*compat-props': { 'type': ['CompatProperty'], + 'features': ['unstable'] } } } ## # @query-machines: # # Return a list of supported machines # +# @compat-props: if true, also return compatibility properties. +# (default: false) (since 9.1) +# +# Features: +# +# @unstable: Argument @compat-props is experimental. +# # Returns: a list of MachineInfo # # Since: 1.2 +# +# Example: +# +# -> { "execute": "query-machines", "arguments": { "compat-props": true } } +# <- { "return": [ +# { +# "hotpluggable-cpus": true, +# "name": "pc-q35-6.2", +# "compat-props": [ +# { +# "qom-type": "virtio-mem", +# "property": "unplugged-inaccessible", +# "value": "off" +# } +# ], +# "numa-mem-supported": false, +# "default-cpu-type": "qemu64-x86_64-cpu", +# "cpu-max": 288, +# "deprecated": false, +# "default-ram-id": "pc.ram" +# }, +# ... +# } ## -{ 'command': 'query-machines', 'returns': ['MachineInfo'] } +{ 'command': 'query-machines', + 'data': { '*compat-props': { 'type': 'bool', + 'features': [ 'unstable' ] } }, + 'returns': ['MachineInfo'] } ## # @CurrentMachineParams: diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index e403d373a0..b71e945c5f 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -46,7 +46,7 @@ static void qos_set_machines_devices_available(void) MachineInfoList *mach_info; ObjectTypeInfoList *type_info; - mach_info = qmp_query_machines(&error_abort); + mach_info = qmp_query_machines(false, false, &error_abort); machines_apply_to_node(mach_info); qapi_free_MachineInfoList(mach_info); From 33956e476802a6ae9b9a8e047f6a78e09e9ae180 Mon Sep 17 00:00:00 2001 From: Maksim Davydov Date: Tue, 19 Mar 2024 00:35:49 +0300 Subject: [PATCH 0154/2884] python/qemu/machine: add method to retrieve QEMUMachine::binary field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a supportive property to access the path to the QEMU binary Signed-off-by: Maksim Davydov Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240318213550.155573-4-davydov-max@yandex-team.ru> Signed-off-by: Philippe Mathieu-Daudé --- python/qemu/machine/machine.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 31cb9d617d..f648f6af45 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -328,6 +328,11 @@ class QEMUMachine: """Returns the list of arguments given to the QEMU binary.""" return self._args + @property + def binary(self) -> str: + """Returns path to the QEMU binary""" + return self._binary + def _pre_launch(self) -> None: if self._qmp_set: if self._monitor_address is None: From b928505d39b634cdd3ba27b94561e6aadcd677af Mon Sep 17 00:00:00 2001 From: Maksim Davydov Date: Tue, 19 Mar 2024 00:35:50 +0300 Subject: [PATCH 0155/2884] scripts: add script to compare compatibility properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This script runs QEMU to obtain compat_props of machines and default values of different types of drivers to produce comparison table. This table can be used to compare machine types to choose the most suitable machine or compare binaries to be sure that migration to the newer version will save all device properties. Also the json or csv format of this table can be used to check does a new machine affect the previous ones by comparing tables with and without the new machine. Default values (that will be used without machine compat_props) of properties are needed to fill "holes" in the table (one machine has the property but another machine not. For instance, 2.12 machine has `{ "EPYC-" TYPE_X86_CPU, "xlevel", "0x8000000a" }`, but compat_pros of 3.1 machine doesn't have it. Thus, to compare these machines we need to get unknown value of "EPYC-x86_64-cpu-xlevel" for 3.1 machine. These unknown values in the table are called "holes". To get values for these "holes" the script uses list of appropriate methods.) Notes: * Some init values from the devices can't be available like properties from virtio-9p when configure has --disable-virtfs. This situations will be seen in the table as "unavailable driver". * Default values can be obtained in an unobvious way, like x86 features. If the script doesn't know how to get property default value to compare one machine with another it fills "holes" with "unavailable method". This is done because script uses whitelist model to get default values of different types. It means that the method that can't be applied to a new type that can crash this script. It is better to get an "unavailable driver" when creating a new machine with new compatible properties than to break this script. So it turns out a more stable and generic script. * If the default value can't be obtained because this property doesn't exist or because this property can't have default value, appropriate "hole" will be filled by "unknown property" or "no default value" * If the property is applied to the abstract class, the script collects default values from all child classes and prints all these classes * Raw table (--raw flag) should be used with json/csv parameters for scripts and etc. Human-readable (default) format contains transformed and simplified values and it doesn't contain lines with the same values in columns Example: ./scripts/compare-machine-types.py --mt pc-q35-6.2 pc-q35-7.1 ╒══════════════════╤══════════════════════════╤════════════════════════════╤════════════════════════════╕ │ Driver │ Property │ build/qemu-system-x86_64 │ build/qemu-system-x86_64 │ │ │ │ pc-q35-6.2 │ pc-q35-7.1 │ ╞══════════════════╪══════════════════════════╪════════════════════════════╪════════════════════════════╡ │ PIIX4_PM │ x-not-migrate-acpi-index │ True │ False │ ├──────────────────┼──────────────────────────┼────────────────────────────┼────────────────────────────┤ │ arm-gicv3-common │ force-8-bit-prio │ True │ unavailable driver │ ├──────────────────┼──────────────────────────┼────────────────────────────┼────────────────────────────┤ │ nvme-ns │ eui64-default │ True │ False │ ├──────────────────┼──────────────────────────┼────────────────────────────┼────────────────────────────┤ │ virtio-mem │ unplugged-inaccessible │ False │ auto │ ╘══════════════════╧══════════════════════════╧════════════════════════════╧════════════════════════════╛ Signed-off-by: Maksim Davydov Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20240318213550.155573-5-davydov-max@yandex-team.ru> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 5 + scripts/compare-machine-types.py | 486 +++++++++++++++++++++++++++++++ 2 files changed, 491 insertions(+) create mode 100755 scripts/compare-machine-types.py diff --git a/MAINTAINERS b/MAINTAINERS index 8bb32f4a7e..118206c3e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4229,3 +4229,8 @@ Code Coverage Tools M: Alex Bennée S: Odd Fixes F: scripts/coverage/ + +Machine development tool +M: Maksim Davydov +S: Supported +F: scripts/compare-machine-types.py diff --git a/scripts/compare-machine-types.py b/scripts/compare-machine-types.py new file mode 100755 index 0000000000..2af3995eb8 --- /dev/null +++ b/scripts/compare-machine-types.py @@ -0,0 +1,486 @@ +#!/usr/bin/env python3 +# +# Script to compare machine type compatible properties (include/hw/boards.h). +# compat_props are applied to the driver during initialization to change +# default values, for instance, to maintain compatibility. +# This script constructs table with machines and values of their compat_props +# to compare and to find places for improvements or places with bugs. If +# during the comparison, some machine type doesn't have a property (it is in +# the comparison table because another machine type has it), then the +# appropriate method will be used to obtain the default value of this driver +# property via qmp command (e.g. query-cpu-model-expansion for x86_64-cpu). +# These methods are defined below in qemu_property_methods. +# +# Copyright (c) Yandex Technologies LLC, 2023 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . + +import sys +from os import path +from argparse import ArgumentParser, RawTextHelpFormatter, Namespace +import pandas as pd +from contextlib import ExitStack +from typing import Optional, List, Dict, Generator, Tuple, Union, Any, Set + +try: + qemu_dir = path.abspath(path.dirname(path.dirname(__file__))) + sys.path.append(path.join(qemu_dir, 'python')) + from qemu.machine import QEMUMachine +except ModuleNotFoundError as exc: + print(f"Module '{exc.name}' not found.") + print("Try export PYTHONPATH=top-qemu-dir/python or run from top-qemu-dir") + sys.exit(1) + + +default_qemu_args = '-enable-kvm -machine none' +default_qemu_binary = 'build/qemu-system-x86_64' + + +# Methods for gettig the right values of drivers properties +# +# Use these methods as a 'whitelist' and add entries only if necessary. It's +# important to be stable and predictable in analysis and tests. +# Be careful: +# * Class must be inherited from 'QEMUObject' and used in new_driver() +# * Class has to implement get_prop method in order to get values +# * Specialization always wins (with the given classes for 'device' and +# 'x86_64-cpu', method of 'x86_64-cpu' will be used for '486-x86_64-cpu') + +class Driver(): + def __init__(self, vm: QEMUMachine, name: str, abstract: bool) -> None: + self.vm = vm + self.name = name + self.abstract = abstract + self.parent: Optional[Driver] = None + self.property_getter: Optional[Driver] = None + + def get_prop(self, driver: str, prop: str) -> str: + if self.property_getter: + return self.property_getter.get_prop(driver, prop) + else: + return 'Unavailable method' + + def is_child_of(self, parent: 'Driver') -> bool: + """Checks whether self is (recursive) child of @parent""" + cur_parent = self.parent + while cur_parent: + if cur_parent is parent: + return True + cur_parent = cur_parent.parent + + return False + + def set_implementations(self, implementations: List['Driver']) -> None: + self.implementations = implementations + + +class QEMUObject(Driver): + def __init__(self, vm: QEMUMachine, name: str) -> None: + super().__init__(vm, name, True) + + def set_implementations(self, implementations: List[Driver]) -> None: + self.implementations = implementations + + # each implementation of the abstract driver has to use property getter + # of this abstract driver unless it has specialization. (e.g. having + # 'device' and 'x86_64-cpu', property getter of 'x86_64-cpu' will be + # used for '486-x86_64-cpu') + for impl in implementations: + if not impl.property_getter or\ + self.is_child_of(impl.property_getter): + impl.property_getter = self + + +class QEMUDevice(QEMUObject): + def __init__(self, vm: QEMUMachine) -> None: + super().__init__(vm, 'device') + self.cached: Dict[str, List[Dict[str, Any]]] = {} + + def get_prop(self, driver: str, prop_name: str) -> str: + if driver not in self.cached: + self.cached[driver] = self.vm.cmd('device-list-properties', + typename=driver) + for prop in self.cached[driver]: + if prop['name'] == prop_name: + return str(prop.get('default-value', 'No default value')) + + return 'Unknown property' + + +class QEMUx86CPU(QEMUObject): + def __init__(self, vm: QEMUMachine) -> None: + super().__init__(vm, 'x86_64-cpu') + self.cached: Dict[str, Dict[str, Any]] = {} + + def get_prop(self, driver: str, prop_name: str) -> str: + if not driver.endswith('-x86_64-cpu'): + return 'Wrong x86_64-cpu name' + + # crop last 11 chars '-x86_64-cpu' + name = driver[:-11] + if name not in self.cached: + self.cached[name] = self.vm.cmd( + 'query-cpu-model-expansion', type='full', + model={'name': name})['model']['props'] + return str(self.cached[name].get(prop_name, 'Unknown property')) + + +# Now it's stub, because all memory_backend types don't have default values +# but this behaviour can be changed +class QEMUMemoryBackend(QEMUObject): + def __init__(self, vm: QEMUMachine) -> None: + super().__init__(vm, 'memory-backend') + self.cached: Dict[str, List[Dict[str, Any]]] = {} + + def get_prop(self, driver: str, prop_name: str) -> str: + if driver not in self.cached: + self.cached[driver] = self.vm.cmd('qom-list-properties', + typename=driver) + for prop in self.cached[driver]: + if prop['name'] == prop_name: + return str(prop.get('default-value', 'No default value')) + + return 'Unknown property' + + +def new_driver(vm: QEMUMachine, name: str, is_abstr: bool) -> Driver: + if name == 'object': + return QEMUObject(vm, 'object') + elif name == 'device': + return QEMUDevice(vm) + elif name == 'x86_64-cpu': + return QEMUx86CPU(vm) + elif name == 'memory-backend': + return QEMUMemoryBackend(vm) + else: + return Driver(vm, name, is_abstr) +# End of methods definition + + +class VMPropertyGetter: + """It implements the relationship between drivers and how to get their + properties""" + def __init__(self, vm: QEMUMachine) -> None: + self.drivers: Dict[str, Driver] = {} + + qom_all_types = vm.cmd('qom-list-types', abstract=True) + self.drivers = {t['name']: new_driver(vm, t['name'], + t.get('abstract', False)) + for t in qom_all_types} + + for t in qom_all_types: + drv = self.drivers[t['name']] + if 'parent' in t: + drv.parent = self.drivers[t['parent']] + + for drv in self.drivers.values(): + imps = vm.cmd('qom-list-types', implements=drv.name) + # only implementations inherit property getter + drv.set_implementations([self.drivers[imp['name']] + for imp in imps]) + + def get_prop(self, driver: str, prop: str) -> str: + # wrong driver name or disabled in config driver + try: + drv = self.drivers[driver] + except KeyError: + return 'Unavailable driver' + + assert not drv.abstract + + return drv.get_prop(driver, prop) + + def get_implementations(self, driver: str) -> List[str]: + return [impl.name for impl in self.drivers[driver].implementations] + + +class Machine: + """A short QEMU machine type description. It contains only processed + compat_props (properties of abstract classes are applied to its + implementations) + """ + # raw_mt_dict - dict produced by `query-machines` + def __init__(self, raw_mt_dict: Dict[str, Any], + qemu_drivers: VMPropertyGetter) -> None: + self.name = raw_mt_dict['name'] + self.compat_props: Dict[str, Any] = {} + # properties are applied sequentially and can rewrite values like in + # QEMU. Also it has to resolve class relationships to apply appropriate + # values from abstract class to all implementations + for prop in raw_mt_dict['compat-props']: + driver = prop['qom-type'] + try: + # implementation adds only itself, abstract class adds + # lementation (abstract classes are uninterestiong) + impls = qemu_drivers.get_implementations(driver) + for impl in impls: + if impl not in self.compat_props: + self.compat_props[impl] = {} + self.compat_props[impl][prop['property']] = prop['value'] + except KeyError: + # QEMU doesn't know this driver thus it has to be saved + if driver not in self.compat_props: + self.compat_props[driver] = {} + self.compat_props[driver][prop['property']] = prop['value'] + + +class Configuration(): + """Class contains all necessary components to generate table and is used + to compare different binaries""" + def __init__(self, vm: QEMUMachine, + req_mt: List[str], all_mt: bool) -> None: + self._vm = vm + self._binary = vm.binary + self._qemu_args = args.qemu_args.split(' ') + + self._qemu_drivers = VMPropertyGetter(vm) + self.req_mt = get_req_mt(self._qemu_drivers, vm, req_mt, all_mt) + + def get_implementations(self, driver_name: str) -> List[str]: + return self._qemu_drivers.get_implementations(driver_name) + + def get_table(self, req_props: List[Tuple[str, str]]) -> pd.DataFrame: + table: List[pd.DataFrame] = [] + for mt in self.req_mt: + name = f'{self._binary}\n{mt.name}' + column = [] + for driver, prop in req_props: + try: + # values from QEMU machine type definitions + column.append(mt.compat_props[driver][prop]) + except KeyError: + # values from QEMU type definitions + column.append(self._qemu_drivers.get_prop(driver, prop)) + table.append(pd.DataFrame({name: column})) + + return pd.concat(table, axis=1) + + +script_desc = """Script to compare machine types (their compat_props). + +Examples: +* save info about all machines: ./scripts/compare-machine-types.py --all \ +--format csv --raw > table.csv +* compare machines: ./scripts/compare-machine-types.py --mt pc-q35-2.12 \ +pc-q35-3.0 +* compare binaries and machines: ./scripts/compare-machine-types.py \ +--mt pc-q35-6.2 pc-q35-7.0 --qemu-binary build/qemu-system-x86_64 \ +build/qemu-exp + ╒════════════╤══════════════════════════╤════════════════════════════\ +╤════════════════════════════╤══════════════════╤══════════════════╕ + │ Driver │ Property │ build/qemu-system-x86_64 \ +│ build/qemu-system-x86_64 │ build/qemu-exp │ build/qemu-exp │ + │ │ │ pc-q35-6.2 \ +│ pc-q35-7.0 │ pc-q35-6.2 │ pc-q35-7.0 │ + ╞════════════╪══════════════════════════╪════════════════════════════\ +╪════════════════════════════╪══════════════════╪══════════════════╡ + │ PIIX4_PM │ x-not-migrate-acpi-index │ True \ +│ False │ False │ False │ + ├────────────┼──────────────────────────┼────────────────────────────\ +┼────────────────────────────┼──────────────────┼──────────────────┤ + │ virtio-mem │ unplugged-inaccessible │ False \ +│ auto │ False │ auto │ + ╘════════════╧══════════════════════════╧════════════════════════════\ +╧════════════════════════════╧══════════════════╧══════════════════╛ + +If a property from QEMU machine defintion applies to an abstract class (e.g. \ +x86_64-cpu) this script will compare all implementations of this class. + +"Unavailable method" - means that this script doesn't know how to get \ +default values of the driver. To add method use the construction described \ +at the top of the script. +"Unavailable driver" - means that this script doesn't know this driver. \ +For instance, this can happen if you configure QEMU without this device or \ +if machine type definition has error. +"No default value" - means that the appropriate method can't get the default \ +value and most likely that this property doesn't have it. +"Unknown property" - means that the appropriate method can't find property \ +with this name.""" + + +def parse_args() -> Namespace: + parser = ArgumentParser(formatter_class=RawTextHelpFormatter, + description=script_desc) + parser.add_argument('--format', choices=['human-readable', 'json', 'csv'], + default='human-readable', + help='returns table in json format') + parser.add_argument('--raw', action='store_true', + help='prints ALL defined properties without value ' + 'transformation. By default, only rows ' + 'with different values will be printed and ' + 'values will be transformed(e.g. "on" -> True)') + parser.add_argument('--qemu-args', default=default_qemu_args, + help='command line to start qemu. ' + f'Default: "{default_qemu_args}"') + parser.add_argument('--qemu-binary', nargs="*", type=str, + default=[default_qemu_binary], + help='list of qemu binaries that will be compared. ' + f'Deafult: {default_qemu_binary}') + + mt_args_group = parser.add_mutually_exclusive_group() + mt_args_group.add_argument('--all', action='store_true', + help='prints all available machine types (list ' + 'of machine types will be ignored)') + mt_args_group.add_argument('--mt', nargs="*", type=str, + help='list of Machine Types ' + 'that will be compared') + + return parser.parse_args() + + +def mt_comp(mt: Machine) -> Tuple[str, int, int, int]: + """Function to compare and sort machine by names. + It returns socket_name, major version, minor version, revision""" + # none, microvm, x-remote and etc. + if '-' not in mt.name or '.' not in mt.name: + return mt.name, 0, 0, 0 + + socket, ver = mt.name.rsplit('-', 1) + ver_list = list(map(int, ver.split('.', 2))) + ver_list += [0] * (3 - len(ver_list)) + return socket, ver_list[0], ver_list[1], ver_list[2] + + +def get_mt_definitions(qemu_drivers: VMPropertyGetter, + vm: QEMUMachine) -> List[Machine]: + """Constructs list of machine definitions (primarily compat_props) via + info from QEMU""" + raw_mt_defs = vm.cmd('query-machines', compat_props=True) + mt_defs = [] + for raw_mt in raw_mt_defs: + mt_defs.append(Machine(raw_mt, qemu_drivers)) + + mt_defs.sort(key=mt_comp) + return mt_defs + + +def get_req_mt(qemu_drivers: VMPropertyGetter, vm: QEMUMachine, + req_mt: Optional[List[str]], all_mt: bool) -> List[Machine]: + """Returns list of requested by user machines""" + mt_defs = get_mt_definitions(qemu_drivers, vm) + if all_mt: + return mt_defs + + if req_mt is None: + print('Enter machine types for comparision') + exit(0) + + matched_mt = [] + for mt in mt_defs: + if mt.name in req_mt: + matched_mt.append(mt) + + return matched_mt + + +def get_affected_props(configs: List[Configuration]) -> Generator[Tuple[str, + str], + None, None]: + """Helps to go through all affected in machine definitions drivers + and properties""" + driver_props: Dict[str, Set[Any]] = {} + for config in configs: + for mt in config.req_mt: + compat_props = mt.compat_props + for driver, prop in compat_props.items(): + if driver not in driver_props: + driver_props[driver] = set() + driver_props[driver].update(prop.keys()) + + for driver, props in sorted(driver_props.items()): + for prop in sorted(props): + yield driver, prop + + +def transform_value(value: str) -> Union[str, bool]: + true_list = ['true', 'on'] + false_list = ['false', 'off'] + + out = value.lower() + + if out in true_list: + return True + + if out in false_list: + return False + + return value + + +def simplify_table(table: pd.DataFrame) -> pd.DataFrame: + """transforms values to make it easier to compare it and drops rows + with the same values for all columns""" + + table = table.map(transform_value) + + return table[~table.iloc[:, 3:].eq(table.iloc[:, 2], axis=0).all(axis=1)] + + +# constructs table in the format: +# +# Driver | Property | binary1 | binary1 | ... +# | | machine1 | machine2 | ... +# ------------------------------------------------------ ... +# driver1 | property1 | value1 | value2 | ... +# driver1 | property2 | value3 | value4 | ... +# driver2 | property3 | value5 | value6 | ... +# ... | ... | ... | ... | ... +# +def fill_prop_table(configs: List[Configuration], + is_raw: bool) -> pd.DataFrame: + req_props = list(get_affected_props(configs)) + if not req_props: + print('No drivers to compare. Check machine names') + exit(0) + + driver_col, prop_col = tuple(zip(*req_props)) + table = [pd.DataFrame({'Driver': driver_col}), + pd.DataFrame({'Property': prop_col})] + + table.extend([config.get_table(req_props) for config in configs]) + + df_table = pd.concat(table, axis=1) + + if is_raw: + return df_table + + return simplify_table(df_table) + + +def print_table(table: pd.DataFrame, table_format: str) -> None: + if table_format == 'json': + print(comp_table.to_json()) + elif table_format == 'csv': + print(comp_table.to_csv()) + else: + print(comp_table.to_markdown(index=False, stralign='center', + colalign=('center',), headers='keys', + tablefmt='fancy_grid', + disable_numparse=True)) + + +if __name__ == '__main__': + args = parse_args() + with ExitStack() as stack: + vms = [stack.enter_context(QEMUMachine(binary=binary, qmp_timer=15, + args=args.qemu_args.split(' '))) for binary in args.qemu_binary] + + configurations = [] + for vm in vms: + vm.launch() + configurations.append(Configuration(vm, args.mt, args.all)) + + comp_table = fill_prop_table(configurations, args.raw) + if not comp_table.empty: + print_table(comp_table, args.format) From 6c3b78532ca7b92eeeef756b4c3d8c5cc82ed3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 19 Mar 2024 19:29:50 +0100 Subject: [PATCH 0156/2884] hw/core: Remove check on NEED_CPU_H in tcg-cpu-ops.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit fd3f7d24d4 ("include/hw/core: Remove i386 conditional on fake_user_interrupt") remove the need to check on NEED_CPU_H. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240321154838.95771-3-philmd@linaro.org> --- include/hw/core/tcg-cpu-ops.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index bf8ff8e3ee..dc1f16a977 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -49,7 +49,6 @@ struct TCGCPUOps { /** @debug_excp_handler: Callback for handling debug exceptions */ void (*debug_excp_handler)(CPUState *cpu); -#ifdef NEED_CPU_H #ifdef CONFIG_USER_ONLY /** * @fake_user_interrupt: Callback for 'fake exception' handling. @@ -174,8 +173,6 @@ struct TCGCPUOps { */ bool (*need_replay_interrupt)(int interrupt_request); #endif /* !CONFIG_USER_ONLY */ -#endif /* NEED_CPU_H */ - }; #if defined(CONFIG_USER_ONLY) From 63073574e8d5551dc31a30b59830b886e9f9dbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Mar 2024 14:29:11 +0100 Subject: [PATCH 0157/2884] target/i386: Move APIC related code to cpu-apic.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move APIC related code split in cpu-sysemu.c and monitor.c to cpu-apic.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20240321154838.95771-4-philmd@linaro.org> --- target/i386/cpu-apic.c | 112 +++++++++++++++++++++++++++++++++++++++ target/i386/cpu-sysemu.c | 77 --------------------------- target/i386/meson.build | 1 + target/i386/monitor.c | 25 --------- 4 files changed, 113 insertions(+), 102 deletions(-) create mode 100644 target/i386/cpu-apic.c diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c new file mode 100644 index 0000000000..d397ec94dc --- /dev/null +++ b/target/i386/cpu-apic.c @@ -0,0 +1,112 @@ +/* + * QEMU x86 CPU <-> APIC + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "sysemu/hw_accel.h" +#include "sysemu/kvm.h" +#include "sysemu/xen.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "hw/i386/apic_internal.h" +#include "cpu-internal.h" + +APICCommonClass *apic_get_class(Error **errp) +{ + const char *apic_type = "apic"; + + /* TODO: in-kernel irqchip for hvf */ + if (kvm_enabled()) { + if (!kvm_irqchip_in_kernel()) { + error_setg(errp, "KVM does not support userspace APIC"); + return NULL; + } + apic_type = "kvm-apic"; + } else if (xen_enabled()) { + apic_type = "xen-apic"; + } else if (whpx_apic_in_platform()) { + apic_type = "whpx-apic"; + } + + return APIC_COMMON_CLASS(object_class_by_name(apic_type)); +} + +void x86_cpu_apic_create(X86CPU *cpu, Error **errp) +{ + APICCommonState *apic; + APICCommonClass *apic_class = apic_get_class(errp); + + if (!apic_class) { + return; + } + + cpu->apic_state = DEVICE(object_new_with_class(OBJECT_CLASS(apic_class))); + object_property_add_child(OBJECT(cpu), "lapic", + OBJECT(cpu->apic_state)); + object_unref(OBJECT(cpu->apic_state)); + + /* TODO: convert to link<> */ + apic = APIC_COMMON(cpu->apic_state); + apic->cpu = cpu; + apic->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; + + /* + * apic_common_set_id needs to check if the CPU has x2APIC + * feature in case APIC ID >= 255, so we need to set apic->cpu + * before setting APIC ID + */ + qdev_prop_set_uint32(cpu->apic_state, "id", cpu->apic_id); +} + +void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) +{ + APICCommonState *apic; + static bool apic_mmio_map_once; + + if (cpu->apic_state == NULL) { + return; + } + qdev_realize(DEVICE(cpu->apic_state), NULL, errp); + + /* Map APIC MMIO area */ + apic = APIC_COMMON(cpu->apic_state); + if (!apic_mmio_map_once) { + memory_region_add_subregion_overlap(get_system_memory(), + apic->apicbase & + MSR_IA32_APICBASE_BASE, + &apic->io_memory, + 0x1000); + apic_mmio_map_once = true; + } +} + +void hmp_info_local_apic(Monitor *mon, const QDict *qdict) +{ + CPUState *cs; + + if (qdict_haskey(qdict, "apic-id")) { + int id = qdict_get_try_int(qdict, "apic-id", 0); + + cs = cpu_by_arch_id(id); + if (cs) { + cpu_synchronize_state(cs); + } + } else { + cs = mon_get_cpu(mon); + } + + + if (!cs) { + monitor_printf(mon, "No CPU available\n"); + return; + } + x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU); +} diff --git a/target/i386/cpu-sysemu.c b/target/i386/cpu-sysemu.c index 3f9093d285..227ac021f6 100644 --- a/target/i386/cpu-sysemu.c +++ b/target/i386/cpu-sysemu.c @@ -19,19 +19,12 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/kvm.h" -#include "sysemu/xen.h" -#include "sysemu/whpx.h" #include "qapi/error.h" #include "qapi/qapi-visit-run-state.h" #include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qom/qom-qobject.h" #include "qapi/qapi-commands-machine-target.h" -#include "hw/qdev-properties.h" - -#include "exec/address-spaces.h" -#include "hw/i386/apic_internal.h" #include "cpu-internal.h" @@ -273,75 +266,6 @@ void x86_cpu_machine_reset_cb(void *opaque) cpu_reset(CPU(cpu)); } -APICCommonClass *apic_get_class(Error **errp) -{ - const char *apic_type = "apic"; - - /* TODO: in-kernel irqchip for hvf */ - if (kvm_enabled()) { - if (!kvm_irqchip_in_kernel()) { - error_setg(errp, "KVM does not support userspace APIC"); - return NULL; - } - apic_type = "kvm-apic"; - } else if (xen_enabled()) { - apic_type = "xen-apic"; - } else if (whpx_apic_in_platform()) { - apic_type = "whpx-apic"; - } - - return APIC_COMMON_CLASS(object_class_by_name(apic_type)); -} - -void x86_cpu_apic_create(X86CPU *cpu, Error **errp) -{ - APICCommonState *apic; - APICCommonClass *apic_class = apic_get_class(errp); - - if (!apic_class) { - return; - } - - cpu->apic_state = DEVICE(object_new_with_class(OBJECT_CLASS(apic_class))); - object_property_add_child(OBJECT(cpu), "lapic", - OBJECT(cpu->apic_state)); - object_unref(OBJECT(cpu->apic_state)); - - /* TODO: convert to link<> */ - apic = APIC_COMMON(cpu->apic_state); - apic->cpu = cpu; - apic->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; - - /* - * apic_common_set_id needs to check if the CPU has x2APIC - * feature in case APIC ID >= 255, so we need to set apic->cpu - * before setting APIC ID - */ - qdev_prop_set_uint32(cpu->apic_state, "id", cpu->apic_id); -} - -void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) -{ - APICCommonState *apic; - static bool apic_mmio_map_once; - - if (cpu->apic_state == NULL) { - return; - } - qdev_realize(DEVICE(cpu->apic_state), NULL, errp); - - /* Map APIC MMIO area */ - apic = APIC_COMMON(cpu->apic_state); - if (!apic_mmio_map_once) { - memory_region_add_subregion_overlap(get_system_memory(), - apic->apicbase & - MSR_IA32_APICBASE_BASE, - &apic->io_memory, - 0x1000); - apic_mmio_map_once = true; - } -} - GuestPanicInformation *x86_cpu_get_crash_info(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); @@ -385,4 +309,3 @@ void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, errp); qapi_free_GuestPanicInformation(panic_info); } - diff --git a/target/i386/meson.build b/target/i386/meson.build index 8abce725f8..075117989b 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -18,6 +18,7 @@ i386_system_ss.add(files( 'arch_memory_mapping.c', 'machine.c', 'monitor.c', + 'cpu-apic.c', 'cpu-sysemu.c', )) i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 3a281dab02..2d766b2637 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -28,8 +28,6 @@ #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "qapi/qmp/qdict.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" @@ -647,26 +645,3 @@ const MonitorDef *target_monitor_defs(void) { return monitor_defs; } - -void hmp_info_local_apic(Monitor *mon, const QDict *qdict) -{ - CPUState *cs; - - if (qdict_haskey(qdict, "apic-id")) { - int id = qdict_get_try_int(qdict, "apic-id", 0); - - cs = cpu_by_arch_id(id); - if (cs) { - cpu_synchronize_state(cs); - } - } else { - cs = mon_get_cpu(mon); - } - - - if (!cs) { - monitor_printf(mon, "No CPU available\n"); - return; - } - x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU); -} From 2b0d2ab895022814da13127e47c17890690488da Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:56 +0100 Subject: [PATCH 0158/2884] target/arm: Handle HCR_EL2 accesses for bits introduced with FEAT_NMI FEAT_NMI defines another three new bits in HCRX_EL2: TALLINT, HCRX_VINMI and HCRX_VFNMI. When the feature is enabled, allow these bits to be written in HCRX_EL2. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-2-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ target/arm/helper.c | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e5758d9fbc..b300d0446d 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -681,6 +681,11 @@ static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; } +static inline bool isar_feature_aa64_nmi(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, NMI) != 0; +} + static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) { return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; diff --git a/target/arm/helper.c b/target/arm/helper.c index a620481d7c..7a25ea65c9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6187,13 +6187,19 @@ bool el_is_in_host(CPUARMState *env, int el) static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; /* FEAT_MOPS adds MSCEn and MCE2 */ - if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { + if (cpu_isar_feature(aa64_mops, cpu)) { valid_mask |= HCRX_MSCEN | HCRX_MCE2; } + /* FEAT_NMI adds TALLINT, VINMI and VFNMI */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; + } + /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; } From 6aa20415613096034ac943872aebd49fbe48e2d0 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:56 +0100 Subject: [PATCH 0159/2884] target/arm: Add PSTATE.ALLINT When PSTATE.ALLINT is set, an IRQ or FIQ interrupt that is targeted to ELx, with or without superpriority is masked. As Richard suggested, place ALLINT bit in PSTATE in env->pstate. In the pseudocode, AArch64.ExceptionReturn() calls SetPSTATEFromPSR(), which treats PSTATE.ALLINT as one of the bits which are reinstated from SPSR to PSTATE regardless of whether this is an illegal exception return or not. So handle PSTATE.ALLINT the same way as PSTATE.DAIF in the illegal_return exit path of the exception_return helper. With the change, exception entry and return are automatically handled. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-3-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 + target/arm/tcg/helper-a64.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index bc0c84873f..de740d223f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1430,6 +1430,7 @@ void pmu_init(ARMCPU *cpu); #define PSTATE_D (1U << 9) #define PSTATE_BTYPE (3U << 10) #define PSTATE_SSBS (1U << 12) +#define PSTATE_ALLINT (1U << 13) #define PSTATE_IL (1U << 20) #define PSTATE_SS (1U << 21) #define PSTATE_PAN (1U << 22) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index ebaa7f00df..29f3ef274a 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -892,8 +892,8 @@ illegal_return: */ env->pstate |= PSTATE_IL; env->pc = new_pc; - spsr &= PSTATE_NZCV | PSTATE_DAIF; - spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); + spsr &= PSTATE_NZCV | PSTATE_DAIF | PSTATE_ALLINT; + spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF | PSTATE_ALLINT); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { env->pstate &= ~PSTATE_SS; From 4833c75611e334164b970c79be95f239ce676ab1 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:57 +0100 Subject: [PATCH 0160/2884] target/arm: Add support for FEAT_NMI, Non-maskable Interrupt Add support for FEAT_NMI. NMI (FEAT_NMI) is an mandatory feature in ARMv8.8-A and ARM v9.3-A. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-4-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/internals.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/internals.h b/target/arm/internals.h index dd3da211a3..516e0584bf 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1229,6 +1229,9 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) if (isar_feature_aa64_mte(id)) { valid |= PSTATE_TCO; } + if (isar_feature_aa64_nmi(id)) { + valid |= PSTATE_ALLINT; + } return valid; } From cbf817a2ff7dc12b62e0bccc15ae93369ea5829e Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:57 +0100 Subject: [PATCH 0161/2884] target/arm: Implement ALLINT MSR (immediate) Add ALLINT MSR (immediate) to decodetree, in which the CRm is 0b000x. The EL0 check is necessary to ALLINT, and the EL1 check is necessary when imm == 1. So implement it inline for EL2/3, or EL1 with imm==0. Avoid the unconditional write to pc and use raise_exception_ra to unwind. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-5-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 1 + target/arm/tcg/helper-a64.c | 12 ++++++++++++ target/arm/tcg/helper-a64.h | 1 + target/arm/tcg/translate-a64.c | 19 +++++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8a20dce3c8..0e7656fd15 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -207,6 +207,7 @@ MSR_i_DIT 1101 0101 0000 0 011 0100 .... 010 11111 @msr_i MSR_i_TCO 1101 0101 0000 0 011 0100 .... 100 11111 @msr_i MSR_i_DAIFSET 1101 0101 0000 0 011 0100 .... 110 11111 @msr_i MSR_i_DAIFCLEAR 1101 0101 0000 0 011 0100 .... 111 11111 @msr_i +MSR_i_ALLINT 1101 0101 0000 0 001 0100 000 imm:1 000 11111 MSR_i_SVCR 1101 0101 0000 0 011 0100 0 mask:2 imm:1 011 11111 # MRS, MSR (register), SYS, SYSL. These are all essentially the diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 29f3ef274a..0ea8668ab4 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -66,6 +66,18 @@ void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) update_spsel(env, imm); } +void HELPER(msr_set_allint_el1)(CPUARMState *env) +{ + /* ALLINT update to PSTATE. */ + if (arm_hcrx_el2_eff(env) & HCRX_TALLINT) { + raise_exception_ra(env, EXCP_UDEF, + syn_aa64_sysregtrap(0, 1, 0, 4, 1, 0x1f, 0), 2, + GETPC()); + } + + env->pstate |= PSTATE_ALLINT; +} + static void daif_check(CPUARMState *env, uint32_t op, uint32_t imm, uintptr_t ra) { diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 575a5dab7d..0518165399 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -22,6 +22,7 @@ DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(msr_i_spsel, void, env, i32) DEF_HELPER_2(msr_i_daifset, void, env, i32) DEF_HELPER_2(msr_i_daifclear, void, env, i32) +DEF_HELPER_1(msr_set_allint_el1, void, env) DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2666d52711..976094a5c8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2036,6 +2036,25 @@ static bool trans_MSR_i_DAIFCLEAR(DisasContext *s, arg_i *a) return true; } +static bool trans_MSR_i_ALLINT(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_nmi, s) || s->current_el == 0) { + return false; + } + + if (a->imm == 0) { + clear_pstate_bits(PSTATE_ALLINT); + } else if (s->current_el > 1) { + set_pstate_bits(PSTATE_ALLINT); + } else { + gen_helper_msr_set_allint_el1(tcg_env); + } + + /* Exit the cpu loop to re-evaluate pending IRQs. */ + s->base.is_jmp = DISAS_UPDATE_EXIT; + return true; +} + static bool trans_MSR_i_SVCR(DisasContext *s, arg_MSR_i_SVCR *a) { if (!dc_isar_feature(aa64_sme, s) || a->mask == 0) { From 5c2169746136ffc93bf1e18c294a05e8446d8af7 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:57 +0100 Subject: [PATCH 0162/2884] target/arm: Support MSR access to ALLINT Support ALLINT msr access as follow: mrs , ALLINT // read allint msr ALLINT, // write allint with imm Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-6-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7a25ea65c9..b9443b1813 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7500,6 +7500,37 @@ static const ARMCPRegInfo rme_mte_reginfo[] = { .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 5, .access = PL3_W, .type = ARM_CP_NOP }, }; + +static void aa64_allint_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->pstate = (env->pstate & ~PSTATE_ALLINT) | (value & PSTATE_ALLINT); +} + +static uint64_t aa64_allint_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pstate & PSTATE_ALLINT; +} + +static CPAccessResult aa64_allint_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if (!isread && arm_current_el(env) == 1 && + (arm_hcrx_el2_eff(env) & HCRX_TALLINT)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo nmi_reginfo[] = { + { .name = "ALLINT", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 0, .crn = 4, .crm = 3, + .type = ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = aa64_allint_access, + .fieldoffset = offsetof(CPUARMState, pstate), + .writefn = aa64_allint_write, .readfn = aa64_allint_read, + .resetfn = arm_cp_reset_ignore }, +}; #endif /* TARGET_AARCH64 */ static void define_pmu_regs(ARMCPU *cpu) @@ -9894,6 +9925,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_nv2, cpu)) { define_arm_cp_regs(cpu, nv2_reginfo); } + + if (cpu_isar_feature(aa64_nmi, cpu)) { + define_arm_cp_regs(cpu, nmi_reginfo); + } #endif if (cpu_isar_feature(any_predinv, cpu)) { From b36a32ead1596929b1fa5436593d49cac20c20e6 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:58 +0100 Subject: [PATCH 0163/2884] target/arm: Add support for Non-maskable Interrupt This only implements the external delivery method via the GICv3. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-7-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu-qom.h | 5 +- target/arm/cpu.c | 147 ++++++++++++++++++++++++++++++++++++++--- target/arm/cpu.h | 6 ++ target/arm/helper.c | 33 +++++++-- target/arm/internals.h | 18 +++++ 5 files changed, 193 insertions(+), 16 deletions(-) diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 8e032691db..b497667d61 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -36,11 +36,14 @@ DECLARE_CLASS_CHECKERS(AArch64CPUClass, AARCH64_CPU, #define ARM_CPU_TYPE_SUFFIX "-" TYPE_ARM_CPU #define ARM_CPU_TYPE_NAME(name) (name ARM_CPU_TYPE_SUFFIX) -/* Meanings of the ARMCPU object's four inbound GPIO lines */ +/* Meanings of the ARMCPU object's seven inbound GPIO lines */ #define ARM_CPU_IRQ 0 #define ARM_CPU_FIQ 1 #define ARM_CPU_VIRQ 2 #define ARM_CPU_VFIQ 3 +#define ARM_CPU_NMI 4 +#define ARM_CPU_VINMI 5 +#define ARM_CPU_VFNMI 6 /* For M profile, some registers are banked secure vs non-secure; * these are represented as a 2-element array where the first element diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ab8d007a86..d2dfd36fd4 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -122,6 +122,13 @@ void arm_restore_state_to_opc(CPUState *cs, } #endif /* CONFIG_TCG */ +/* + * With SCTLR_ELx.NMI == 0, IRQ with Superpriority is masked identically with + * IRQ without Superpriority. Moreover, if the GIC is configured so that + * FEAT_GICv3_NMI is only set if FEAT_NMI is set, then we won't ever see + * CPU_INTERRUPT_*NMI anyway. So we might as well accept NMI here + * unconditionally. + */ static bool arm_cpu_has_work(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -129,6 +136,7 @@ static bool arm_cpu_has_work(CPUState *cs) return (cpu->power_state != PSCI_OFF) && cs->interrupt_request & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR | CPU_INTERRUPT_EXITTB); } @@ -668,6 +676,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, CPUARMState *env = cpu_env(cs); bool pstate_unmasked; bool unmasked = false; + bool allIntMask = false; /* * Don't take exceptions if they target a lower EL. @@ -678,13 +687,36 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, return false; } + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { + allIntMask = env->pstate & PSTATE_ALLINT || + ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && + (env->pstate & PSTATE_SP)); + } + switch (excp_idx) { + case EXCP_NMI: + pstate_unmasked = !allIntMask; + break; + + case EXCP_VINMI: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VINMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_VFNMI: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFNMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; case EXCP_FIQ: - pstate_unmasked = !(env->daif & PSTATE_F); + pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); break; case EXCP_IRQ: - pstate_unmasked = !(env->daif & PSTATE_I); + pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); break; case EXCP_VFIQ: @@ -692,13 +724,13 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, /* VFIQs are only taken when hypervized. */ return false; } - return !(env->daif & PSTATE_F); + return !(env->daif & PSTATE_F) && (!allIntMask); case EXCP_VIRQ: if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized. */ return false; } - return !(env->daif & PSTATE_I); + return !(env->daif & PSTATE_I) && (!allIntMask); case EXCP_VSERR: if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized. */ @@ -804,6 +836,48 @@ static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + (arm_sctlr(env, cur_el) & SCTLR_NMI)) { + if (interrupt_request & CPU_INTERRUPT_NMI) { + excp_idx = EXCP_NMI; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + excp_idx = EXCP_VINMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + excp_idx = EXCP_VFNMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + } else { + /* + * NMI disabled: interrupts with superpriority are handled + * as if they didn't have it + */ + if (interrupt_request & CPU_INTERRUPT_NMI) { + interrupt_request |= CPU_INTERRUPT_HARD; + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + interrupt_request |= CPU_INTERRUPT_VIRQ; + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + interrupt_request |= CPU_INTERRUPT_VFIQ; + } + } + if (interrupt_request & CPU_INTERRUPT_FIQ) { excp_idx = EXCP_FIQ; target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); @@ -867,7 +941,8 @@ void arm_cpu_update_virq(ARMCPU *cpu) CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool new_state = (env->cp15.hcr_el2 & HCR_VI) || + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VIRQ); if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { @@ -888,7 +963,8 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool new_state = (env->cp15.hcr_el2 & HCR_VF) || + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && + !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || (env->irq_line_state & CPU_INTERRUPT_VFIQ); if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { @@ -900,6 +976,48 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) } } +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VINMI, which is the logical OR of + * the HCRX_EL2.VINMI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VINMI); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VINMI) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VINMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); + } + } +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && + (arm_hcrx_el2_eff(env) & HCRX_VFNMI); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFNMI) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); + } + } +} + void arm_cpu_update_vserr(ARMCPU *cpu) { /* @@ -929,7 +1047,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) [ARM_CPU_IRQ] = CPU_INTERRUPT_HARD, [ARM_CPU_FIQ] = CPU_INTERRUPT_FIQ, [ARM_CPU_VIRQ] = CPU_INTERRUPT_VIRQ, - [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ + [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ, + [ARM_CPU_NMI] = CPU_INTERRUPT_NMI, + [ARM_CPU_VINMI] = CPU_INTERRUPT_VINMI, }; if (!arm_feature(env, ARM_FEATURE_EL2) && @@ -955,8 +1075,12 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) case ARM_CPU_VFIQ: arm_cpu_update_vfiq(cpu); break; + case ARM_CPU_VINMI: + arm_cpu_update_vinmi(cpu); + break; case ARM_CPU_IRQ: case ARM_CPU_FIQ: + case ARM_CPU_NMI: if (level) { cpu_interrupt(cs, mask[irq]); } else { @@ -1350,12 +1474,13 @@ static void arm_cpu_initfn(Object *obj) #else /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { - /* VIRQ and VFIQ are unused with KVM but we add them to maintain - * the same interface as non-KVM CPUs. + /* + * VIRQ, VFIQ, NMI, VINMI are unused with KVM but we add + * them to maintain the same interface as non-KVM CPUs. */ - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 4); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 6); } else { - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 4); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 6); } qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, diff --git a/target/arm/cpu.h b/target/arm/cpu.h index de740d223f..08a6bc50de 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -61,6 +61,9 @@ #define EXCP_DIVBYZERO 23 /* v7M DIVBYZERO UsageFault */ #define EXCP_VSERR 24 #define EXCP_GPC 25 /* v9 Granule Protection Check Fault */ +#define EXCP_NMI 26 +#define EXCP_VINMI 27 +#define EXCP_VFNMI 28 /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 @@ -80,6 +83,9 @@ #define CPU_INTERRUPT_VIRQ CPU_INTERRUPT_TGT_EXT_2 #define CPU_INTERRUPT_VFIQ CPU_INTERRUPT_TGT_EXT_3 #define CPU_INTERRUPT_VSERR CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_4 +#define CPU_INTERRUPT_VINMI CPU_INTERRUPT_TGT_EXT_0 +#define CPU_INTERRUPT_VFNMI CPU_INTERRUPT_TGT_INT_1 /* The usual mapping for an AArch64 system register to its AArch32 * counterpart is for the 32 bit world to have access to the lower diff --git a/target/arm/helper.c b/target/arm/helper.c index b9443b1813..f61a65d811 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6046,15 +6046,19 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) * and the state of the input lines from the GIC. (This requires * that we have the BQL, which is done by marking the * reginfo structs as ARM_CP_IO.) - * Note that if a write to HCR pends a VIRQ or VFIQ it is never - * possible for it to be taken immediately, because VIRQ and - * VFIQ are masked unless running at EL0 or EL1, and HCR - * can only be written at EL2. + * Note that if a write to HCR pends a VIRQ or VFIQ or VINMI or + * VFNMI, it is never possible for it to be taken immediately + * because VIRQ, VFIQ, VINMI and VFNMI are masked unless running + * at EL0 or EL1, and HCR can only be written at EL2. */ g_assert(bql_locked()); arm_cpu_update_virq(cpu); arm_cpu_update_vfiq(cpu); arm_cpu_update_vserr(cpu); + if (cpu_isar_feature(aa64_nmi, cpu)) { + arm_cpu_update_vinmi(cpu); + arm_cpu_update_vfnmi(cpu); + } } static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -6202,6 +6206,23 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; + + /* + * Updates to VINMI and VFNMI require us to update the status of + * virtual NMI, which are the logical OR of these bits + * and the state of the input lines from the GIC. (This requires + * that we have the BQL, which is done by marking the + * reginfo structs as ARM_CP_IO.) + * Note that if a write to HCRX pends a VINMI or VFNMI it is never + * possible for it to be taken immediately, because VINMI and + * VFNMI are masked unless running at EL0 or EL1, and HCRX + * can only be written at EL2. + */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + g_assert(bql_locked()); + arm_cpu_update_vinmi(cpu); + arm_cpu_update_vfnmi(cpu); + } } static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6217,6 +6238,7 @@ static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo hcrx_el2_reginfo = { .name = "HCRX_EL2", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_IO, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2, .access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen, .nv2_redirect_offset = 0xa0, @@ -10799,6 +10821,9 @@ void arm_log_exception(CPUState *cs) [EXCP_DIVBYZERO] = "v7M DIVBYZERO UsageFault", [EXCP_VSERR] = "Virtual SERR", [EXCP_GPC] = "Granule Protection Check", + [EXCP_NMI] = "NMI", + [EXCP_VINMI] = "Virtual IRQ NMI", + [EXCP_VFNMI] = "Virtual FIQ NMI", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 516e0584bf..b53f5e8ff2 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1109,6 +1109,24 @@ void arm_cpu_update_virq(ARMCPU *cpu); */ void arm_cpu_update_vfiq(ARMCPU *cpu); +/** + * arm_cpu_update_vinmi: Update CPU_INTERRUPT_VINMI bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VINMI bit in cs->interrupt_request, following + * a change to either the input VNMI line from the GIC or the HCRX_EL2.VINMI. + * Must be called with the BQL held. + */ +void arm_cpu_update_vinmi(ARMCPU *cpu); + +/** + * arm_cpu_update_vfnmi: Update CPU_INTERRUPT_VFNMI bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VFNMI bit in cs->interrupt_request, following + * a change to the HCRX_EL2.VFNMI. + * Must be called with the BQL held. + */ +void arm_cpu_update_vfnmi(ARMCPU *cpu); + /** * arm_cpu_update_vserr: Update CPU_INTERRUPT_VSERR bit * From 963e4e3648e0601a8f0b288edaf524b3c98fffbd Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:58 +0100 Subject: [PATCH 0164/2884] target/arm: Add support for NMI in arm_phys_excp_target_el() According to Arm GIC section 4.6.3 Interrupt superpriority, the interrupt with superpriority is always IRQ, never FIQ, so handle NMI same as IRQ in arm_phys_excp_target_el(). Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-8-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index f61a65d811..4ee59b3705 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -10763,6 +10763,7 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, hcr_el2 = arm_hcr_el2_eff(env); switch (excp_idx) { case EXCP_IRQ: + case EXCP_NMI: scr = ((env->cp15.scr_el3 & SCR_IRQ) == SCR_IRQ); hcr = hcr_el2 & HCR_IMO; break; From 2e0be5f6b122e3dca53b926514287ffcead2689c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:59 +0100 Subject: [PATCH 0165/2884] target/arm: Handle IS/FS in ISR_EL1 for NMI, VINMI and VFNMI Add IS and FS bit in ISR_EL1 and handle the read. With CPU_INTERRUPT_NMI or CPU_INTERRUPT_VINMI, both CPSR_I and ISR_IS must be set. With CPU_INTERRUPT_VFNMI, both CPSR_F and ISR_FS must be set. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-9-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu.h | 2 ++ target/arm/helper.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 08a6bc50de..97997dbd08 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1398,6 +1398,8 @@ void pmu_init(ARMCPU *cpu); #define CPSR_N (1U << 31) #define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V) #define CPSR_AIF (CPSR_A | CPSR_I | CPSR_F) +#define ISR_FS (1U << 9) +#define ISR_IS (1U << 10) #define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7) #define CACHED_CPSR_BITS (CPSR_T | CPSR_AIF | CPSR_GE | CPSR_IT | CPSR_Q \ diff --git a/target/arm/helper.c b/target/arm/helper.c index 4ee59b3705..6b6d8a349a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2021,16 +2021,29 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { ret |= CPSR_I; } + if (cs->interrupt_request & CPU_INTERRUPT_VINMI) { + ret |= ISR_IS; + ret |= CPSR_I; + } } else { if (cs->interrupt_request & CPU_INTERRUPT_HARD) { ret |= CPSR_I; } + + if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + ret |= ISR_IS; + ret |= CPSR_I; + } } if (hcr_el2 & HCR_FMO) { if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { ret |= CPSR_F; } + if (cs->interrupt_request & CPU_INTERRUPT_VFNMI) { + ret |= ISR_FS; + ret |= CPSR_F; + } } else { if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { ret |= CPSR_F; From 167f2631df98c5b1af622a9afe3afe00867ef080 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:59 +0100 Subject: [PATCH 0166/2884] target/arm: Handle PSTATE.ALLINT on taking an exception Set or clear PSTATE.ALLINT on taking an exception to ELx according to the SCTLR_ELx.SPINTMASK bit. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-10-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6b6d8a349a..5ff9e44649 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11733,6 +11733,14 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } } + if (cpu_isar_feature(aa64_nmi, cpu)) { + if (!(env->cp15.sctlr_el[new_el] & SCTLR_SPINTMASK)) { + new_mode |= PSTATE_ALLINT; + } else { + new_mode &= ~PSTATE_ALLINT; + } + } + pstate_write(env, PSTATE_DAIF | new_mode); env->aarch64 = true; aarch64_restore_sp(env, new_el); From 83f320753827da6bd381b46b8f3e6736046c86cd Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:00 +0100 Subject: [PATCH 0167/2884] hw/intc/arm_gicv3: Add external IRQ lines for NMI Augment the GICv3's QOM device interface by adding one new set of sysbus IRQ line, to signal NMI to each CPU. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-11-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 6 ++++++ include/hw/intc/arm_gic_common.h | 2 ++ include/hw/intc/arm_gicv3_common.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index cb55c72681..c52f060026 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -299,6 +299,12 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->cpu[i].parent_vfiq); } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_nmi); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_vnmi); + } memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 7080375008..97fea4102d 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -71,6 +71,8 @@ struct GICState { qemu_irq parent_fiq[GIC_NCPU]; qemu_irq parent_virq[GIC_NCPU]; qemu_irq parent_vfiq[GIC_NCPU]; + qemu_irq parent_nmi[GIC_NCPU]; + qemu_irq parent_vnmi[GIC_NCPU]; qemu_irq maintenance_irq[GIC_NCPU]; /* GICD_CTLR; for a GIC with the security extensions the NS banked version diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 4e2fb518e7..7324c7d983 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -155,6 +155,8 @@ struct GICv3CPUState { qemu_irq parent_fiq; qemu_irq parent_virq; qemu_irq parent_vfiq; + qemu_irq parent_nmi; + qemu_irq parent_vnmi; /* Redistributor */ uint32_t level; /* Current IRQ level */ From 34d94b7af9f1dc4cae550ecc7b825c9567741a12 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:00 +0100 Subject: [PATCH 0168/2884] hw/arm/virt: Wire NMI and VINMI irq lines from GIC to CPU Wire the new NMI and VINMI interrupt line from the GIC to each CPU if it is not GICv2. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Message-id: 20240407081733.3231820-12-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c9119ef384..c4b03b09c2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -821,7 +821,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) /* Wire the outputs from each CPU's generic timer and the GICv3 * maintenance interrupt signal to the appropriate GIC PPI inputs, - * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + * and the GIC's IRQ/FIQ/VIRQ/VFIQ/NMI/VINMI interrupt outputs to the + * CPU's inputs. */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); @@ -865,6 +866,13 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + if (vms->gic_version != VIRT_GIC_VERSION_2) { + sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_NMI)); + sysbus_connect_irq(gicbusdev, i + 5 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VINMI)); + } } fdt_add_gic_node(vms); From e4eb290571606c5e6dc7739547b5056389cd92fb Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:01 +0100 Subject: [PATCH 0169/2884] target/arm: Handle NMI in arm_cpu_do_interrupt_aarch64() According to Arm GIC section 4.6.3 Interrupt superpriority, the interrupt with superpriority is always IRQ, never FIQ, so the NMI exception trap entry behave like IRQ. And VINMI(vIRQ with Superpriority) can be raised from the GIC or come from the hcrx_el2.HCRX_VINMI bit, VFNMI(vFIQ with Superpriority) come from the hcrx_el2.HCRX_VFNMI bit. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-13-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5ff9e44649..6b224826fb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11653,10 +11653,13 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) break; case EXCP_IRQ: case EXCP_VIRQ: + case EXCP_NMI: + case EXCP_VINMI: addr += 0x80; break; case EXCP_FIQ: case EXCP_VFIQ: + case EXCP_VFNMI: addr += 0x100; break; case EXCP_VSERR: From c9e86cbd3400032dc818bf24e823959e565b8b41 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:01 +0100 Subject: [PATCH 0170/2884] hw/intc/arm_gicv3: Add has-nmi property to GICv3 device Add a property has-nmi to the GICv3 device, and use this to set the NMI bit in the GICD_TYPER register. This isn't visible to guests yet because the property defaults to false and we won't set it in the board code until we've landed all of the changes needed to implement FEAT_GICV3_NMI. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-14-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 1 + hw/intc/arm_gicv3_dist.c | 2 ++ hw/intc/gicv3_internal.h | 1 + include/hw/intc/arm_gicv3_common.h | 1 + 4 files changed, 5 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index c52f060026..2d2cea6858 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -569,6 +569,7 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0), + DEFINE_PROP_BOOL("has-nmi", GICv3State, nmi_support, 0), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), /* * Compatibility property: force 8 bits of physical priority, even diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 35e850685c..22ddc0d666 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -389,6 +389,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * by GICD_TYPER.IDbits) * MBIS == 0 (message-based SPIs not supported) * SecurityExtn == 1 if security extns supported + * NMI = 1 if Non-maskable interrupt property is supported * CPUNumber == 0 since for us ARE is always 1 * ITLinesNumber == (((max SPI IntID + 1) / 32) - 1) */ @@ -402,6 +403,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, bool dvis = s->revision >= 4; *data = (1 << 25) | (1 << 24) | (dvis << 18) | (sec_extn << 10) | + (s->nmi_support << GICD_TYPER_NMI_SHIFT) | (s->lpi_enable << GICD_TYPER_LPIS_SHIFT) | (0xf << 19) | itlinesnumber; return true; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 29d5cdc1b6..8f4ebed2f4 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -68,6 +68,7 @@ #define GICD_CTLR_E1NWF (1U << 7) #define GICD_CTLR_RWP (1U << 31) +#define GICD_TYPER_NMI_SHIFT 9 #define GICD_TYPER_LPIS_SHIFT 17 /* 16 bits EventId */ diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 7324c7d983..4358c5319c 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -249,6 +249,7 @@ struct GICv3State { uint32_t num_irq; uint32_t revision; bool lpi_enable; + bool nmi_support; bool security_extn; bool force_8bit_prio; bool irq_reset_nonsecure; From 67d74e4c54236b53917edfa9f52efb4207064014 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:01 +0100 Subject: [PATCH 0171/2884] hw/intc/arm_gicv3_kvm: Not set has-nmi=true for the KVM GICv3 So far, there is no FEAT_GICv3_NMI support in the in-kernel GIC, so make it an error to try to set has-nmi=true for the KVM GICv3. Signed-off-by: Jinjie Ruan Message-id: 20240407081733.3231820-15-ruanjinjie@huawei.com Suggested-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 77eb37e131..00a383079b 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -805,6 +805,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + if (s->nmi_support) { + error_setg(errp, "NMI is not supported with the in-kernel GIC"); + return; + } + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { From 0e9f4e8e7b9e3bde8b8c0a84c577f64c679b535c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:02 +0100 Subject: [PATCH 0172/2884] hw/intc/arm_gicv3: Add irq non-maskable property A SPI, PPI or SGI interrupt can have non-maskable property. So maintain non-maskable property in PendingIrq and GICR/GICD. Since add new device state, it also needs to be migrated, so also save NMI info in vmstate_gicv3_cpu and vmstate_gicv3. Signed-off-by: Jinjie Ruan Acked-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-16-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 38 ++++++++++++++++++++++++++++++ include/hw/intc/arm_gicv3_common.h | 4 ++++ 2 files changed, 42 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 2d2cea6858..9810558b07 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -164,6 +164,24 @@ const VMStateDescription vmstate_gicv3_gicv4 = { } }; +static bool gicv3_cpu_nmi_needed(void *opaque) +{ + GICv3CPUState *cs = opaque; + + return cs->gic->nmi_support; +} + +static const VMStateDescription vmstate_gicv3_cpu_nmi = { + .name = "arm_gicv3_cpu/nmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_cpu_nmi_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(gicr_inmir0, GICv3CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_gicv3_cpu", .version_id = 1, @@ -196,6 +214,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { &vmstate_gicv3_cpu_virt, &vmstate_gicv3_cpu_sre_el1, &vmstate_gicv3_gicv4, + &vmstate_gicv3_cpu_nmi, NULL } }; @@ -238,6 +257,24 @@ const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { } }; +static bool gicv3_nmi_needed(void *opaque) +{ + GICv3State *cs = opaque; + + return cs->nmi_support; +} + +const VMStateDescription vmstate_gicv3_gicd_nmi = { + .name = "arm_gicv3/gicd_nmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_nmi_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(nmi, GICv3State, GICV3_BMP_SIZE), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", .version_id = 1, @@ -266,6 +303,7 @@ static const VMStateDescription vmstate_gicv3 = { }, .subsections = (const VMStateDescription * const []) { &vmstate_gicv3_gicd_no_migration_shift_bug, + &vmstate_gicv3_gicd_nmi, NULL } }; diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 4358c5319c..88533749eb 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -146,6 +146,7 @@ typedef struct { int irq; uint8_t prio; int grp; + bool nmi; } PendingIrq; struct GICv3CPUState { @@ -172,6 +173,7 @@ struct GICv3CPUState { uint32_t gicr_ienabler0; uint32_t gicr_ipendr0; uint32_t gicr_iactiver0; + uint32_t gicr_inmir0; uint32_t edge_trigger; /* ICFGR0 and ICFGR1 even bits */ uint32_t gicr_igrpmodr0; uint32_t gicr_nsacr; @@ -275,6 +277,7 @@ struct GICv3State { GIC_DECLARE_BITMAP(active); /* GICD_ISACTIVER */ GIC_DECLARE_BITMAP(level); /* Current level */ GIC_DECLARE_BITMAP(edge_trigger); /* GICD_ICFGR even bits */ + GIC_DECLARE_BITMAP(nmi); /* GICD_INMIR */ uint8_t gicd_ipriority[GICV3_MAXIRQ]; uint64_t gicd_irouter[GICV3_MAXIRQ]; /* Cached information: pointer to the cpu i/f for the CPUs specified @@ -314,6 +317,7 @@ GICV3_BITMAP_ACCESSORS(pending) GICV3_BITMAP_ACCESSORS(active) GICV3_BITMAP_ACCESSORS(level) GICV3_BITMAP_ACCESSORS(edge_trigger) +GICV3_BITMAP_ACCESSORS(nmi) #define TYPE_ARM_GICV3_COMMON "arm-gicv3-common" typedef struct ARMGICv3CommonClass ARMGICv3CommonClass; From 7c79d98d2e4de5b8c919002e6ead6bae7f46003d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:02 +0100 Subject: [PATCH 0173/2884] hw/intc/arm_gicv3_redist: Implement GICR_INMIR0 Add GICR_INMIR0 register and support access GICR_INMIR0. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-17-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_redist.c | 19 +++++++++++++++++++ hw/intc/gicv3_internal.h | 1 + 2 files changed, 20 insertions(+) diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8153525849..ed1f9d1e44 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -35,6 +35,15 @@ static int gicr_ns_access(GICv3CPUState *cs, int irq) return extract32(cs->gicr_nsacr, irq * 2, 2); } +static void gicr_write_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, + uint32_t *reg, uint32_t val) +{ + /* Helper routine to implement writing to a "set" register */ + val &= mask_group(cs, attrs); + *reg = val; + gicv3_redist_update(cs); +} + static void gicr_write_set_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, uint32_t *reg, uint32_t val) { @@ -406,6 +415,10 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, *data = value; return MEMTX_OK; } + case GICR_INMIR0: + *data = cs->gic->nmi_support ? + gicr_read_bitmap_reg(cs, attrs, cs->gicr_inmir0) : 0; + return MEMTX_OK; case GICR_ICFGR0: case GICR_ICFGR1: { @@ -555,6 +568,12 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, gicv3_redist_update(cs); return MEMTX_OK; } + case GICR_INMIR0: + if (cs->gic->nmi_support) { + gicr_write_bitmap_reg(cs, attrs, &cs->gicr_inmir0, value); + } + return MEMTX_OK; + case GICR_ICFGR0: /* Register is all RAZ/WI or RAO/WI bits */ return MEMTX_OK; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 8f4ebed2f4..21697ecf39 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -110,6 +110,7 @@ #define GICR_ICFGR1 (GICR_SGI_OFFSET + 0x0C04) #define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00) #define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00) +#define GICR_INMIR0 (GICR_SGI_OFFSET + 0x0F80) /* VLPI redistributor registers, offsets from VLPI_base */ #define GICR_VPROPBASER (GICR_VLPI_OFFSET + 0x70) From 44ed1e4b9a4df256bb56487ae5150b6807536703 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:03 +0100 Subject: [PATCH 0174/2884] hw/intc/arm_gicv3: Implement GICD_INMIR Add GICD_INMIR, GICD_INMIRnE register and support access GICD_INMIR0. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-18-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_dist.c | 34 ++++++++++++++++++++++++++++++++++ hw/intc/gicv3_internal.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 22ddc0d666..d8207acb22 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -89,6 +89,29 @@ static int gicd_ns_access(GICv3State *s, int irq) return extract32(s->gicd_nsacr[irq / 16], (irq % 16) * 2, 2); } +static void gicd_write_bitmap_reg(GICv3State *s, MemTxAttrs attrs, + uint32_t *bmp, maskfn *maskfn, + int offset, uint32_t val) +{ + /* + * Helper routine to implement writing to a "set" register + * (GICD_INMIR, etc). + * Semantics implemented here: + * RAZ/WI for SGIs, PPIs, unimplemented IRQs + * Bits corresponding to Group 0 or Secure Group 1 interrupts RAZ/WI. + * offset should be the offset in bytes of the register from the start + * of its group. + */ + int irq = offset * 8; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + val &= mask_group_and_nsacr(s, attrs, maskfn, irq); + *gic_bmp_ptr32(bmp, irq) = val; + gicv3_update(s, irq, 32); +} + static void gicd_write_set_bitmap_reg(GICv3State *s, MemTxAttrs attrs, uint32_t *bmp, maskfn *maskfn, @@ -545,6 +568,11 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, /* RAZ/WI since affinity routing is always enabled */ *data = 0; return true; + case GICD_INMIR ... GICD_INMIR + 0x7f: + *data = (!s->nmi_support) ? 0 : + gicd_read_bitmap_reg(s, attrs, s->nmi, NULL, + offset - GICD_INMIR); + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; @@ -754,6 +782,12 @@ static bool gicd_writel(GICv3State *s, hwaddr offset, case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: /* RAZ/WI since affinity routing is always enabled */ return true; + case GICD_INMIR ... GICD_INMIR + 0x7f: + if (s->nmi_support) { + gicd_write_bitmap_reg(s, attrs, s->nmi, NULL, + offset - GICD_INMIR, value); + } + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 21697ecf39..8d793243f4 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -52,6 +52,8 @@ #define GICD_SGIR 0x0F00 #define GICD_CPENDSGIR 0x0F10 #define GICD_SPENDSGIR 0x0F20 +#define GICD_INMIR 0x0F80 +#define GICD_INMIRnE 0x3B00 #define GICD_IROUTER 0x6000 #define GICD_IDREGS 0xFFD0 From 28cca59c469b16f1352e784b566fd36ace2be4b4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 19 Apr 2024 14:36:00 +0100 Subject: [PATCH 0175/2884] hw/intc/arm_gicv3: Add NMI handling CPU interface registers Add the NMIAR CPU interface registers which deal with acknowledging NMI. When introduce NMI interrupt, there are some updates to the semantics for the register ICC_IAR1_EL1 and ICC_HPPIR1_EL1. For ICC_IAR1_EL1 register, it should return 1022 if the intid has non-maskable property. And for ICC_NMIAR1_EL1 register, it should return 1023 if the intid do not have non-maskable property. Howerever, these are not necessary for ICC_HPPIR1_EL1 register. And the APR and RPR has NMI bits which should be handled correctly. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson [PMM: Separate out whether cpuif supports NMI from whether the GIC proper (IRI) supports NMI] Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-19-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 147 ++++++++++++++++++++++++++++- hw/intc/gicv3_internal.h | 5 + hw/intc/trace-events | 1 + include/hw/intc/arm_gicv3_common.h | 7 ++ 4 files changed, 155 insertions(+), 5 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 67d8fd07b7..715909d0f7 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,6 +21,7 @@ #include "hw/irq.h" #include "cpu.h" #include "target/arm/cpregs.h" +#include "target/arm/cpu-features.h" #include "sysemu/tcg.h" #include "sysemu/qtest.h" @@ -795,6 +796,13 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) return intid; } +static uint64_t icv_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* todo */ + uint64_t intid = INTID_SPURIOUS; + return intid; +} + static uint32_t icc_fullprio_mask(GICv3CPUState *cs) { /* @@ -832,6 +840,23 @@ static int icc_highest_active_prio(GICv3CPUState *cs) */ int i; + if (cs->nmi_support) { + /* + * If an NMI is active this takes precedence over anything else + * for priority purposes; the NMI bit is only in the AP1R0 bit. + * We return here the effective priority of the NMI, which is + * either 0x0 or 0x80. Callers will need to check NMI again for + * purposes of either setting the RPR register bits or for + * prioritization of NMI vs non-NMI. + */ + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + return 0; + } + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + return (cs->gic->gicd_ctlr & GICD_CTLR_DS) ? 0 : 0x80; + } + } + for (i = 0; i < icc_num_aprs(cs); i++) { uint32_t apr = cs->icc_apr[GICV3_G0][i] | cs->icc_apr[GICV3_G1][i] | cs->icc_apr[GICV3_G1NS][i]; @@ -898,12 +923,24 @@ static bool icc_hppi_can_preempt(GICv3CPUState *cs) */ int rprio; uint32_t mask; + ARMCPU *cpu = ARM_CPU(cs->cpu); + CPUARMState *env = &cpu->env; if (icc_no_enabled_hppi(cs)) { return false; } - if (cs->hppi.prio >= cs->icc_pmr_el1) { + if (cs->hppi.nmi) { + if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + cs->hppi.grp == GICV3_G1NS) { + if (cs->icc_pmr_el1 < 0x80) { + return false; + } + if (arm_is_secure(env) && cs->icc_pmr_el1 == 0x80) { + return false; + } + } + } else if (cs->hppi.prio >= cs->icc_pmr_el1) { /* Priority mask masks this interrupt */ return false; } @@ -923,6 +960,12 @@ static bool icc_hppi_can_preempt(GICv3CPUState *cs) return true; } + if (cs->hppi.nmi && (cs->hppi.prio & mask) == (rprio & mask)) { + if (!(cs->icc_apr[cs->hppi.grp][0] & ICC_AP1R_EL1_NMI)) { + return true; + } + } + return false; } @@ -1044,8 +1087,13 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq) int aprbit = prio >> (8 - cs->prebits); int regno = aprbit / 32; int regbit = aprbit % 32; + bool nmi = cs->hppi.nmi; - cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + if (nmi) { + cs->icc_apr[cs->hppi.grp][regno] |= ICC_AP1R_EL1_NMI; + } else { + cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + } if (irq < GIC_INTERNAL) { cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1); @@ -1159,6 +1207,7 @@ static uint64_t icc_iar0_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); uint64_t intid; if (icv_access(env, HCR_IMO)) { @@ -1172,13 +1221,44 @@ static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) } if (!gicv3_intid_is_special(intid)) { - icc_activate_irq(cs, intid); + if (cs->hppi.nmi && env->cp15.sctlr_el[el] & SCTLR_NMI) { + intid = INTID_NMI; + } else { + icc_activate_irq(cs, intid); + } } trace_gicv3_icc_iar1_read(gicv3_redist_affid(cs), intid); return intid; } +static uint64_t icc_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t intid; + + if (icv_access(env, HCR_IMO)) { + return icv_nmiar1_read(env, ri); + } + + if (!icc_hppi_can_preempt(cs)) { + intid = INTID_SPURIOUS; + } else { + intid = icc_hppir1_value(cs, env); + } + + if (!gicv3_intid_is_special(intid)) { + if (!cs->hppi.nmi) { + intid = INTID_SPURIOUS; + } else { + icc_activate_irq(cs, intid); + } + } + + trace_gicv3_icc_nmiar1_read(gicv3_redist_affid(cs), intid); + return intid; +} + static void icc_drop_prio(GICv3CPUState *cs, int grp) { /* Drop the priority of the currently active interrupt in @@ -1205,6 +1285,12 @@ static void icc_drop_prio(GICv3CPUState *cs, int grp) if (!*papr) { continue; } + + if (i == 0 && cs->nmi_support && (*papr & ICC_AP1R_EL1_NMI)) { + *papr &= (~ICC_AP1R_EL1_NMI); + break; + } + /* Clear the lowest set bit */ *papr &= *papr - 1; break; @@ -1239,6 +1325,15 @@ static int icc_highest_active_group(GICv3CPUState *cs) */ int i; + if (cs->nmi_support) { + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + return GICV3_G1; + } + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + return GICV3_G1NS; + } + } + for (i = 0; i < ARRAY_SIZE(cs->icc_apr[0]); i++) { int g0ctz = ctz32(cs->icc_apr[GICV3_G0][i]); int g1ctz = ctz32(cs->icc_apr[GICV3_G1][i]); @@ -1693,7 +1788,11 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } - cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->icc_apr[grp][regno] = value & (0xFFFFFFFFU | ICC_AP1R_EL1_NMI); + } else { + cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_update(cs); } @@ -1783,7 +1882,7 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio; + uint64_t prio; if (icv_access(env, HCR_FMO | HCR_IMO)) { return icv_rpr_read(env, ri); @@ -1803,6 +1902,22 @@ static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) } } + if (cs->nmi_support) { + /* NMI info is reported in the high bits of RPR */ + if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env)) { + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NMI; + } + } else { + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NSNMI; + } + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NMI; + } + } + } + trace_gicv3_icc_rpr_read(gicv3_redist_affid(cs), prio); return prio; } @@ -2482,6 +2597,15 @@ static const ARMCPRegInfo gicv3_cpuif_icc_apxr23_reginfo[] = { }, }; +static const ARMCPRegInfo gicv3_cpuif_gicv3_nmi_reginfo[] = { + { .name = "ICC_NMIAR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 5, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_irq_access, + .readfn = icc_nmiar1_read, + }, +}; + static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); @@ -2838,6 +2962,19 @@ void gicv3_init_cpuif(GICv3State *s) */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + /* + * If the CPU implements FEAT_NMI and FEAT_GICv3 it must also + * implement FEAT_GICv3_NMI, which is the CPU interface part + * of NMI support. This is distinct from whether the GIC proper + * (redistributors and distributor) have NMI support. In QEMU + * that is a property of the GIC device in s->nmi_support; + * cs->nmi_support indicates the CPU interface's support. + */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + cs->nmi_support = true; + define_arm_cp_regs(cpu, gicv3_cpuif_gicv3_nmi_reginfo); + } + /* * The CPU implementation specifies the number of supported * bits of physical priority. For backwards compatibility diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 8d793243f4..81200eb90e 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -194,6 +194,10 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICC_CTLR_EL3_A3V (1U << 15) #define ICC_CTLR_EL3_NDS (1U << 17) +#define ICC_AP1R_EL1_NMI (1ULL << 63) +#define ICC_RPR_EL1_NSNMI (1ULL << 62) +#define ICC_RPR_EL1_NMI (1ULL << 63) + #define ICH_VMCR_EL2_VENG0_SHIFT 0 #define ICH_VMCR_EL2_VENG0 (1U << ICH_VMCR_EL2_VENG0_SHIFT) #define ICH_VMCR_EL2_VENG1_SHIFT 1 @@ -511,6 +515,7 @@ FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH) /* Special interrupt IDs */ #define INTID_SECURE 1020 #define INTID_NONSECURE 1021 +#define INTID_NMI 1022 #define INTID_SPURIOUS 1023 /* Functions internal to the emulated GICv3 */ diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 1ef29d0256..94030550d5 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -116,6 +116,7 @@ gicv3_cpuif_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f gicv3_icc_generate_sgi(uint32_t cpuid, int irq, int irm, uint32_t aff, uint32_t targetlist) "GICv3 CPU i/f 0x%x generating SGI %d IRM %d target affinity 0x%xxx targetlist 0x%x" gicv3_icc_iar0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR0 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_iar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR1 read cpu 0x%x value 0x%" PRIx64 +gicv3_icc_nmiar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_NMIAR1 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_EOIR%d write cpu 0x%x value 0x%" PRIx64 gicv3_icc_hppir0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR0 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_hppir1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR1 read cpu 0x%x value 0x%" PRIx64 diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 88533749eb..cd09bee3bc 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -225,6 +225,13 @@ struct GICv3CPUState { /* This is temporary working state, to avoid a malloc in gicv3_update() */ bool seenbetter; + + /* + * Whether the CPU interface has NMI support (FEAT_GICv3_NMI). The + * CPU interface may support NMIs even when the GIC proper (what the + * spec calls the IRI; the redistributors and distributor) does not. + */ + bool nmi_support; }; /* From d2c0c6aab6c6748726149c37159a75751ec6ac92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 19 Apr 2024 14:36:33 +0100 Subject: [PATCH 0176/2884] hw/intc/arm_gicv3: Handle icv_nmiar1_read() for icc_nmiar1_read() Implement icv_nmiar1_read() for icc_nmiar1_read(), so add definition for ICH_LR_EL2.NMI and ICH_AP1R_EL2.NMI bit. If FEAT_GICv3_NMI is supported, ich_ap_write() should consider ICV_AP1R_EL1.NMI bit. In icv_activate_irq() and icv_eoir_write(), the ICV_AP1R_EL1.NMI bit should be set or clear according to the Non-maskable property. And the RPR priority should also update the NMI bit according to the APR priority NMI bit. By the way, add gicv3_icv_nmiar1_read trace event. If the hpp irq is a NMI, the icv iar read should return 1022 and trap for NMI again Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson [PMM: use cs->nmi_support instead of cs->gic->nmi_support] Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-20-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 105 +++++++++++++++++++++++++++++++++----- hw/intc/gicv3_internal.h | 4 ++ hw/intc/trace-events | 1 + 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 715909d0f7..b1f6c16ffe 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -158,6 +158,10 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs) int i; int aprmax = ich_num_aprs(cs); + if (cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI) { + return 0x0; + } + for (i = 0; i < aprmax; i++) { uint32_t apr = cs->ich_apr[GICV3_G0][i] | cs->ich_apr[GICV3_G1NS][i]; @@ -192,6 +196,7 @@ static int hppvi_index(GICv3CPUState *cs) * correct behaviour. */ int prio = 0xff; + bool nmi = false; if (!(cs->ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) { /* Both groups disabled, definitely nothing to do */ @@ -200,6 +205,7 @@ static int hppvi_index(GICv3CPUState *cs) for (i = 0; i < cs->num_list_regs; i++) { uint64_t lr = cs->ich_lr_el2[i]; + bool thisnmi; int thisprio; if (ich_lr_state(lr) != ICH_LR_EL2_STATE_PENDING) { @@ -218,10 +224,12 @@ static int hppvi_index(GICv3CPUState *cs) } } + thisnmi = lr & ICH_LR_EL2_NMI; thisprio = ich_lr_prio(lr); - if (thisprio < prio) { + if ((thisprio < prio) || ((thisprio == prio) && (thisnmi & (!nmi)))) { prio = thisprio; + nmi = thisnmi; idx = i; } } @@ -290,6 +298,7 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) * equivalent of these checks. */ int grp; + bool is_nmi; uint32_t mask, prio, rprio, vpmr; if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) { @@ -302,10 +311,11 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) */ prio = ich_lr_prio(lr); + is_nmi = lr & ICH_LR_EL2_NMI; vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, ICH_VMCR_EL2_VPMR_LENGTH); - if (prio >= vpmr) { + if (!is_nmi && prio >= vpmr) { /* Priority mask masks this interrupt */ return false; } @@ -327,6 +337,11 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) return true; } + if ((prio & mask) == (rprio & mask) && is_nmi && + !(cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI)) { + return true; + } + return false; } @@ -551,7 +566,11 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); - cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->ich_apr[grp][regno] = value & (0xFFFFFFFFU | ICV_AP1R_EL1_NMI); + } else { + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_virt_irq_fiq_update(cs); return; @@ -698,7 +717,11 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio = ich_highest_active_virt_prio(cs); + uint64_t prio = ich_highest_active_virt_prio(cs); + + if (cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI) { + prio |= ICV_RPR_EL1_NMI; + } trace_gicv3_icv_rpr_read(gicv3_redist_affid(cs), prio); return prio; @@ -737,13 +760,19 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp) */ uint32_t mask = icv_gprio_mask(cs, grp); int prio = ich_lr_prio(cs->ich_lr_el2[idx]) & mask; + bool nmi = cs->ich_lr_el2[idx] & ICH_LR_EL2_NMI; int aprbit = prio >> (8 - cs->vprebits); int regno = aprbit / 32; int regbit = aprbit % 32; cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; cs->ich_lr_el2[idx] |= ICH_LR_EL2_STATE_ACTIVE_BIT; - cs->ich_apr[grp][regno] |= (1 << regbit); + + if (nmi) { + cs->ich_apr[grp][regno] |= ICV_AP1R_EL1_NMI; + } else { + cs->ich_apr[grp][regno] |= (1 << regbit); + } } static void icv_activate_vlpi(GICv3CPUState *cs) @@ -764,6 +793,7 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; int idx = hppvi_index(cs); uint64_t intid = INTID_SPURIOUS; + int el = arm_current_el(env); if (idx == HPPVI_INDEX_VLPI) { if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) { @@ -773,11 +803,16 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) } else if (idx >= 0) { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + bool nmi = env->cp15.sctlr_el[el] & SCTLR_NMI && lr & ICH_LR_EL2_NMI; if (thisgrp == grp && icv_hppi_can_preempt(cs, lr)) { intid = ich_lr_vintid(lr); if (!gicv3_intid_is_special(intid)) { - icv_activate_irq(cs, idx, grp); + if (!nmi) { + icv_activate_irq(cs, idx, grp); + } else { + intid = INTID_NMI; + } } else { /* Interrupt goes from Pending to Invalid */ cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; @@ -798,8 +833,37 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t icv_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) { - /* todo */ + GICv3CPUState *cs = icc_cs_from_env(env); + int idx = hppvi_index(cs); uint64_t intid = INTID_SPURIOUS; + + if (idx >= 0 && idx != HPPVI_INDEX_VLPI) { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + + if ((thisgrp == GICV3_G1NS) && icv_hppi_can_preempt(cs, lr)) { + intid = ich_lr_vintid(lr); + if (!gicv3_intid_is_special(intid)) { + if (lr & ICH_LR_EL2_NMI) { + icv_activate_irq(cs, idx, GICV3_G1NS); + } else { + intid = INTID_SPURIOUS; + } + } else { + /* Interrupt goes from Pending to Invalid */ + cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; + /* + * We will now return the (bogus) ID from the list register, + * as per the pseudocode. + */ + } + } + } + + trace_gicv3_icv_nmiar1_read(gicv3_redist_affid(cs), intid); + + gicv3_cpuif_virt_update(cs); + return intid; } @@ -1424,7 +1488,7 @@ static void icv_increment_eoicount(GICv3CPUState *cs) ICH_HCR_EL2_EOICOUNT_LENGTH, eoicount + 1); } -static int icv_drop_prio(GICv3CPUState *cs) +static int icv_drop_prio(GICv3CPUState *cs, bool *nmi) { /* Drop the priority of the currently active virtual interrupt * (favouring group 0 if there is a set active bit at @@ -1446,6 +1510,12 @@ static int icv_drop_prio(GICv3CPUState *cs) continue; } + if (i == 0 && cs->nmi_support && (*papr1 & ICV_AP1R_EL1_NMI)) { + *papr1 &= (~ICV_AP1R_EL1_NMI); + *nmi = true; + return 0xff; + } + /* We can't just use the bit-twiddling hack icc_drop_prio() does * because we need to return the bit number we cleared so * it can be compared against the list register's priority field. @@ -1505,6 +1575,7 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, int irq = value & 0xffffff; int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; int idx, dropprio; + bool nmi = false; trace_gicv3_icv_eoir_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); @@ -1517,8 +1588,8 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, * error checks" (because that lets us avoid scanning the AP * registers twice). */ - dropprio = icv_drop_prio(cs); - if (dropprio == 0xff) { + dropprio = icv_drop_prio(cs, &nmi); + if (dropprio == 0xff && !nmi) { /* No active interrupt. It is CONSTRAINED UNPREDICTABLE * whether the list registers are checked in this * situation; we choose not to. @@ -1540,8 +1611,9 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); + bool thisnmi = lr & ICH_LR_EL2_NMI; - if (thisgrp == grp && lr_gprio == dropprio) { + if (thisgrp == grp && (lr_gprio == dropprio || (thisnmi & nmi))) { if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) { /* * Priority drop and deactivate not split: deactivate irq now. @@ -2627,7 +2699,11 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); - cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->ich_apr[grp][regno] = value & (0xFFFFFFFFU | ICV_AP1R_EL1_NMI); + } else { + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_virt_irq_fiq_update(cs); } @@ -2744,6 +2820,11 @@ static void ich_lr_write(CPUARMState *env, const ARMCPRegInfo *ri, 8 - cs->vpribits, 0); } + /* Enforce RES0 bit in NMI field when FEAT_GICv3_NMI is not implemented */ + if (!cs->nmi_support) { + value &= ~ICH_LR_EL2_NMI; + } + cs->ich_lr_el2[regno] = value; gicv3_cpuif_virt_update(cs); } diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 81200eb90e..bc9f518fe8 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -246,6 +246,7 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICH_LR_EL2_PRIORITY_SHIFT 48 #define ICH_LR_EL2_PRIORITY_LENGTH 8 #define ICH_LR_EL2_PRIORITY_MASK (0xffULL << ICH_LR_EL2_PRIORITY_SHIFT) +#define ICH_LR_EL2_NMI (1ULL << 59) #define ICH_LR_EL2_GROUP (1ULL << 60) #define ICH_LR_EL2_HW (1ULL << 61) #define ICH_LR_EL2_STATE_SHIFT 62 @@ -277,6 +278,9 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICH_VTR_EL2_PREBITS_SHIFT 26 #define ICH_VTR_EL2_PRIBITS_SHIFT 29 +#define ICV_AP1R_EL1_NMI (1ULL << 63) +#define ICV_RPR_EL1_NMI (1ULL << 63) + /* ITS Registers */ FIELD(GITS_BASER, SIZE, 0, 8) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 94030550d5..47340b5bc1 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -152,6 +152,7 @@ gicv3_icv_rpr_read(uint32_t cpu, uint64_t val) "GICv3 ICV_RPR read cpu 0x%x valu gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d read cpu 0x%x value 0x%" PRIx64 gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64 gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64 +gicv3_icv_nmiar1_read(uint32_t cpu, uint64_t val) "GICv3 ICV_NMIAR1 read cpu 0x%x value 0x%" PRIx64 gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64 gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d" gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d" From d89daa893f51280652032640d77a8bc1dea95bdd Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:05 +0100 Subject: [PATCH 0177/2884] hw/intc/arm_gicv3: Implement NMI interrupt priority If GICD_CTLR_DS bit is zero and the NMI is non-secure, the NMI priority is higher than 0x80, otherwise it is higher than 0x0. And save the interrupt non-maskable property in hppi.nmi to deliver NMI exception. Since both GICR and GICD can deliver NMI, it is both necessary to check whether the pending irq is NMI in gicv3_redist_update_noirqset and gicv3_update_noirqset. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-21-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3.c | 67 +++++++++++++++++++++++++++++++++----- hw/intc/arm_gicv3_common.c | 3 ++ hw/intc/arm_gicv3_redist.c | 3 ++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 0b8f79a122..58e18fff54 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -21,7 +21,7 @@ #include "hw/intc/arm_gicv3.h" #include "gicv3_internal.h" -static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) +static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio, bool nmi) { /* Return true if this IRQ at this priority should take * precedence over the current recorded highest priority @@ -30,14 +30,23 @@ static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) * is the same as this one (a property which the calling code * relies on). */ - if (prio < cs->hppi.prio) { - return true; + if (prio != cs->hppi.prio) { + return prio < cs->hppi.prio; } + + /* + * The same priority IRQ with non-maskable property should signal to + * the CPU as it have the priority higher than the labelled 0x80 or 0x00. + */ + if (nmi != cs->hppi.nmi) { + return nmi; + } + /* If multiple pending interrupts have the same priority then it is an * IMPDEF choice which of them to signal to the CPU. We choose to * signal the one with the lowest interrupt number. */ - if (prio == cs->hppi.prio && irq <= cs->hppi.irq) { + if (irq <= cs->hppi.irq) { return true; } return false; @@ -129,6 +138,40 @@ static uint32_t gicr_int_pending(GICv3CPUState *cs) return pend; } +static bool gicv3_get_priority(GICv3CPUState *cs, bool is_redist, int irq, + uint8_t *prio) +{ + uint32_t nmi = 0x0; + + if (is_redist) { + nmi = extract32(cs->gicr_inmir0, irq, 1); + } else { + nmi = *gic_bmp_ptr32(cs->gic->nmi, irq); + nmi = nmi & (1 << (irq & 0x1f)); + } + + if (nmi) { + /* DS = 0 & Non-secure NMI */ + if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + ((is_redist && extract32(cs->gicr_igroupr0, irq, 1)) || + (!is_redist && gicv3_gicd_group_test(cs->gic, irq)))) { + *prio = 0x80; + } else { + *prio = 0x0; + } + + return true; + } + + if (is_redist) { + *prio = cs->gicr_ipriorityr[irq]; + } else { + *prio = cs->gic->gicd_ipriority[irq]; + } + + return false; +} + /* Update the interrupt status after state in a redistributor * or CPU interface has changed, but don't tell the CPU i/f. */ @@ -141,6 +184,7 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) uint8_t prio; int i; uint32_t pend; + bool nmi = false; /* Find out which redistributor interrupts are eligible to be * signaled to the CPU interface. @@ -152,10 +196,11 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) if (!(pend & (1 << i))) { continue; } - prio = cs->gicr_ipriorityr[i]; - if (irqbetter(cs, i, prio)) { + nmi = gicv3_get_priority(cs, true, i, &prio); + if (irqbetter(cs, i, prio, nmi)) { cs->hppi.irq = i; cs->hppi.prio = prio; + cs->hppi.nmi = nmi; seenbetter = true; } } @@ -168,9 +213,10 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable && (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) && (cs->hpplpi.prio != 0xff)) { - if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) { + if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio, cs->hpplpi.nmi)) { cs->hppi.irq = cs->hpplpi.irq; cs->hppi.prio = cs->hpplpi.prio; + cs->hppi.nmi = cs->hpplpi.nmi; cs->hppi.grp = cs->hpplpi.grp; seenbetter = true; } @@ -213,6 +259,7 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) int i; uint8_t prio; uint32_t pend = 0; + bool nmi = false; assert(start >= GIC_INTERNAL); assert(len > 0); @@ -240,10 +287,11 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) */ continue; } - prio = s->gicd_ipriority[i]; - if (irqbetter(cs, i, prio)) { + nmi = gicv3_get_priority(cs, false, i, &prio); + if (irqbetter(cs, i, prio, nmi)) { cs->hppi.irq = i; cs->hppi.prio = prio; + cs->hppi.nmi = nmi; cs->seenbetter = true; } } @@ -293,6 +341,7 @@ void gicv3_full_update_noirqset(GICv3State *s) for (i = 0; i < s->num_cpu; i++) { s->cpu[i].hppi.prio = 0xff; + s->cpu[i].hppi.nmi = false; } /* Note that we can guarantee that these functions will not diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 9810558b07..207f8417e1 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -536,8 +536,11 @@ static void arm_gicv3_common_reset_hold(Object *obj) memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr)); cs->hppi.prio = 0xff; + cs->hppi.nmi = false; cs->hpplpi.prio = 0xff; + cs->hpplpi.nmi = false; cs->hppvlpi.prio = 0xff; + cs->hppvlpi.nmi = false; /* State in the CPU interface must *not* be reset here, because it * is part of the CPU's reset domain, not the GIC device's. diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index ed1f9d1e44..90b238fac0 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -120,6 +120,7 @@ static void update_for_one_lpi(GICv3CPUState *cs, int irq, ((prio == hpp->prio) && (irq <= hpp->irq))) { hpp->irq = irq; hpp->prio = prio; + hpp->nmi = false; /* LPIs and vLPIs are always non-secure Grp1 interrupts */ hpp->grp = GICV3_G1NS; } @@ -156,6 +157,7 @@ static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase, int i, bit; hpp->prio = 0xff; + hpp->nmi = false; for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1); @@ -241,6 +243,7 @@ static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs) if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) { cs->hppvlpi.prio = 0xff; + cs->hppvlpi.nmi = false; return; } From f3c26a44fe3dc27988f07b7e1c4155b9a55818fc Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:05 +0100 Subject: [PATCH 0178/2884] hw/intc/arm_gicv3: Report the NMI interrupt in gicv3_cpuif_update() In CPU Interface, if the IRQ has the non-maskable property, report NMI to the corresponding PE. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-22-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index b1f6c16ffe..2cf232d099 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1038,6 +1038,7 @@ void gicv3_cpuif_update(GICv3CPUState *cs) /* Tell the CPU about its highest priority pending interrupt */ int irqlevel = 0; int fiqlevel = 0; + int nmilevel = 0; ARMCPU *cpu = ARM_CPU(cs->cpu); CPUARMState *env = &cpu->env; @@ -1076,6 +1077,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs) if (isfiq) { fiqlevel = 1; + } else if (cs->hppi.nmi) { + nmilevel = 1; } else { irqlevel = 1; } @@ -1085,6 +1088,7 @@ void gicv3_cpuif_update(GICv3CPUState *cs) qemu_set_irq(cs->parent_fiq, fiqlevel); qemu_set_irq(cs->parent_irq, irqlevel); + qemu_set_irq(cs->parent_nmi, nmilevel); } static uint64_t icc_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) From c57e81889faa5823d0d47c14a4dfc45914205d71 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:05 +0100 Subject: [PATCH 0179/2884] hw/intc/arm_gicv3: Report the VINMI interrupt In vCPU Interface, if the vIRQ has the non-maskable property, report vINMI to the corresponding vPE. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-23-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 2cf232d099..bdb13b00e9 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -481,6 +481,7 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) int idx; int irqlevel = 0; int fiqlevel = 0; + int nmilevel = 0; idx = hppvi_index(cs); trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx, @@ -498,9 +499,17 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) uint64_t lr = cs->ich_lr_el2[idx]; if (icv_hppi_can_preempt(cs, lr)) { - /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ + /* + * Virtual interrupts are simple: G0 are always FIQ, and G1 are + * IRQ or NMI which depends on the ICH_LR_EL2.NMI to have + * non-maskable property. + */ if (lr & ICH_LR_EL2_GROUP) { - irqlevel = 1; + if (lr & ICH_LR_EL2_NMI) { + nmilevel = 1; + } else { + irqlevel = 1; + } } else { fiqlevel = 1; } @@ -510,6 +519,7 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel); qemu_set_irq(cs->parent_vfiq, fiqlevel); qemu_set_irq(cs->parent_virq, irqlevel); + qemu_set_irq(cs->parent_vnmi, nmilevel); } static void gicv3_cpuif_virt_update(GICv3CPUState *cs) From 14a164030ada5c5d6f346e152521cb878b142b2a Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:06 +0100 Subject: [PATCH 0180/2884] target/arm: Add FEAT_NMI to max Enable FEAT_NMI on the 'max' CPU. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-24-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 2a7bbb82dc..a9ae7ede9f 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -64,6 +64,7 @@ the following architecture extensions: - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) +- FEAT_NMI (Non-maskable Interrupt) - FEAT_NV (Nested Virtualization) - FEAT_NV2 (Enhanced nested virtualization support) - FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 9f7a9f3d2c..62c4663512 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1175,6 +1175,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ t = FIELD_DP64(t, ID_AA64PFR1, SME, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_2 */ + t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ cpu->isar.id_aa64pfr1 = t; t = cpu->isar.id_aa64mmfr0; From 5ae47f7aece19c5a6efbf99d000f389278e93c1d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:06 +0100 Subject: [PATCH 0181/2884] hw/arm/virt: Enable NMI support in the GIC if the CPU has FEAT_NMI If the CPU implements FEAT_NMI, then turn on the NMI support in the GICv3 too. It's permitted to have a configuration with FEAT_NMI in the CPU (and thus NMI support in the CPU interfaces too) but no NMI support in the distributor and redistributor, but this isn't a very useful setup as it's close to having no NMI support at all. We don't need to gate the enabling of NMI in the GIC behind a machine version property, because none of our current CPUs implement FEAT_NMI, and '-cpu max' is not something we maintain migration compatibility across versions for. So we can always enable the GIC NMI support when the CPU has it. Neither hvf nor KVM support NMI in the GIC yet, so we don't enable it unless we're using TCG. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Message-id: 20240407081733.3231820-25-ruanjinjie@huawei.com [PMM: Update comment and commit message] Suggested-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c4b03b09c2..3c93c0c0a6 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -729,6 +729,20 @@ static void create_v2m(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } +/* + * If the CPU has FEAT_NMI, then turn on the NMI support in the GICv3 too. + * It's permitted to have a configuration with NMI in the CPU (and thus the + * GICv3 CPU interface) but not in the distributor/redistributors, but it's + * not very useful. + */ +static bool gicv3_nmi_present(VirtMachineState *vms) +{ + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(0)); + + return tcg_enabled() && cpu_isar_feature(aa64_nmi, cpu) && + (vms->gic_version != VIRT_GIC_VERSION_2); +} + static void create_gic(VirtMachineState *vms, MemoryRegion *mem) { MachineState *ms = MACHINE(vms); @@ -802,6 +816,11 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) vms->virt); } } + + if (gicv3_nmi_present(vms)) { + qdev_prop_set_bit(vms->gic, "has-nmi", true); + } + gicbusdev = SYS_BUS_DEVICE(vms->gic); sysbus_realize_and_unref(gicbusdev, &error_fatal); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); From c3a68dfd191682b140951e423b72866102097df9 Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Tue, 9 Apr 2024 14:53:01 +0300 Subject: [PATCH 0182/2884] hw/dma: avoid apparent overflow in soc_dma_set_request In soc_dma_set_request() we try to set a bit in a uint64_t, but we do it with "1 << ch->num", which can't set any bits past 31; any use for a channel number of 32 or more would fail due to integer overflow. This doesn't happen in practice for our current use of this code, because the worst case is when we call soc_dma_init() with an argument of 32 for the number of channels, and QEMU builds with -fwrapv so the shift into the sign bit is well-defined. However, it's obviously not the intended behaviour of the code. Add casts to force the shift to be done as 64-bit arithmetic, allowing up to 64 channels. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: afbb5194d4 ("Handle on-chip DMA controllers in one place, convert OMAP DMA to use it.") Signed-off-by: Anastasia Belova Message-id: 20240409115301.21829-1-abelova@astralinux.ru [PMM: Edit commit message to clarify that this doesn't actually bite us in our current usage of this code.] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/dma/soc_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 3a430057f5..d5c52b804f 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -209,9 +209,9 @@ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) dma->enabled_count += level - ch->enable; if (level) - dma->ch_enable_mask |= 1 << ch->num; + dma->ch_enable_mask |= (uint64_t)1 << ch->num; else - dma->ch_enable_mask &= ~(1 << ch->num); + dma->ch_enable_mask &= ~((uint64_t)1 << ch->num); if (level != ch->enable) { soc_dma_ch_freq_update(dma); From a6819c1bd0b76d7fa0c8f4ae19e4e80dd61a1502 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 11 Apr 2024 12:53:13 +0100 Subject: [PATCH 0183/2884] linux-user/flatload.c: Remove unused bFLT shared-library and ZFLAT code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ever since the bFLT format support was added in 2006, there has been a chunk of code in the file guarded by CONFIG_BINFMT_SHARED_FLAT which is supposedly for shared library support. This is not enabled and it's not possible to enable it, because if you do you'll run into the "#error needs checking" in the calc_reloc() function. Similarly, CONFIG_BINFMT_ZFLAT exists but can't be enabled because of an "#error code needs checking" in load_flat_file(). This code is obviously unfinished and has never been used; nobody in the intervening 18 years has complained about this or fixed it, so just delete the dead code. If anybody ever wants the feature they can always pull it out of git, or (perhaps better) write it from scratch based on the current Linux bFLT loader rather than the one of 18 years ago. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240411115313.680433-1-peter.maydell@linaro.org --- linux-user/flat.h | 5 +- linux-user/flatload.c | 293 ++---------------------------------------- 2 files changed, 11 insertions(+), 287 deletions(-) diff --git a/linux-user/flat.h b/linux-user/flat.h index ed518e2013..e374b73e26 100644 --- a/linux-user/flat.h +++ b/linux-user/flat.h @@ -12,11 +12,8 @@ #define FLAT_VERSION 0x00000004L -#ifdef CONFIG_BINFMT_SHARED_FLAT -#define MAX_SHARED_LIBS (4) -#else +/* QEMU doesn't support bflt shared libraries */ #define MAX_SHARED_LIBS (1) -#endif /* * To make everything easier to port and manage cross platform diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 5b62aa0a2b..04d8138d12 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -29,8 +29,6 @@ * JAN/99 -- coded full program relocation (gerg@snapgear.com) */ -/* ??? ZFLAT and shared library support is currently disabled. */ - /****************************************************************************/ #include "qemu/osdep.h" @@ -64,10 +62,6 @@ struct lib_info { short loaded; /* Has this library been loaded? */ }; -#ifdef CONFIG_BINFMT_SHARED_FLAT -static int load_flat_shared_library(int id, struct lib_info *p); -#endif - struct linux_binprm; /****************************************************************************/ @@ -108,153 +102,6 @@ static int target_pread(int fd, abi_ulong ptr, abi_ulong len, unlock_user(buf, ptr, len); return ret; } -/****************************************************************************/ - -#ifdef CONFIG_BINFMT_ZFLAT - -#include - -#define LBUFSIZE 4000 - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -static int decompress_exec( - struct linux_binprm *bprm, - unsigned long offset, - char *dst, - long len, - int fd) -{ - unsigned char *buf; - z_stream strm; - loff_t fpos; - int ret, retval; - - DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n",(int)offset, (int)dst, (int)len); - - memset(&strm, 0, sizeof(strm)); - strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL); - if (strm.workspace == NULL) { - DBG_FLT("binfmt_flat: no memory for decompress workspace\n"); - return -ENOMEM; - } - buf = kmalloc(LBUFSIZE, GFP_KERNEL); - if (buf == NULL) { - DBG_FLT("binfmt_flat: no memory for read buffer\n"); - retval = -ENOMEM; - goto out_free; - } - - /* Read in first chunk of data and parse gzip header. */ - fpos = offset; - ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); - - strm.next_in = buf; - strm.avail_in = ret; - strm.total_in = 0; - - retval = -ENOEXEC; - - /* Check minimum size -- gzip header */ - if (ret < 10) { - DBG_FLT("binfmt_flat: file too small?\n"); - goto out_free_buf; - } - - /* Check gzip magic number */ - if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) { - DBG_FLT("binfmt_flat: unknown compression magic?\n"); - goto out_free_buf; - } - - /* Check gzip method */ - if (buf[2] != 8) { - DBG_FLT("binfmt_flat: unknown compression method?\n"); - goto out_free_buf; - } - /* Check gzip flags */ - if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) || - (buf[3] & RESERVED)) { - DBG_FLT("binfmt_flat: unknown flags?\n"); - goto out_free_buf; - } - - ret = 10; - if (buf[3] & EXTRA_FIELD) { - ret += 2 + buf[10] + (buf[11] << 8); - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n"); - goto out_free_buf; - } - } - if (buf[3] & ORIG_NAME) { - for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) - ; - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n"); - goto out_free_buf; - } - } - if (buf[3] & COMMENT) { - for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) - ; - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n"); - goto out_free_buf; - } - } - - strm.next_in += ret; - strm.avail_in -= ret; - - strm.next_out = dst; - strm.avail_out = len; - strm.total_out = 0; - - if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) { - DBG_FLT("binfmt_flat: zlib init failed?\n"); - goto out_free_buf; - } - - while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) { - ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); - if (ret <= 0) - break; - if (is_error(ret)) { - break; - } - len -= ret; - - strm.next_in = buf; - strm.avail_in = ret; - strm.total_in = 0; - } - - if (ret < 0) { - DBG_FLT("binfmt_flat: decompression failed (%d), %s\n", - ret, strm.msg); - goto out_zlib; - } - - retval = 0; -out_zlib: - zlib_inflateEnd(&strm); -out_free_buf: - kfree(buf); -out_free: - kfree(strm.workspace); -out: - return retval; -} - -#endif /* CONFIG_BINFMT_ZFLAT */ /****************************************************************************/ @@ -268,40 +115,7 @@ calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp) abi_ulong text_len; abi_ulong start_code; -#ifdef CONFIG_BINFMT_SHARED_FLAT -#error needs checking - if (r == 0) - id = curid; /* Relocs of 0 are always self referring */ - else { - id = (r >> 24) & 0xff; /* Find ID for this reloc */ - r &= 0x00ffffff; /* Trim ID off here */ - } - if (id >= MAX_SHARED_LIBS) { - fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n", - (unsigned) r, id); - goto failed; - } - if (curid != id) { - if (internalp) { - fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not " - "in same module (%d != %d)\n", - (unsigned) r, curid, id); - goto failed; - } else if (!p[id].loaded && is_error(load_flat_shared_library(id, p))) { - fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id); - goto failed; - } - /* Check versioning information (i.e. time stamps) */ - if (p[id].build_date && p[curid].build_date - && p[curid].build_date < p[id].build_date) { - fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n", - id, curid); - goto failed; - } - } -#else id = 0; -#endif start_brk = p[id].start_brk; start_data = p[id].start_data; @@ -425,12 +239,10 @@ static int load_flat_file(struct linux_binprm * bprm, if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags)) flags = FLAT_FLAG_RAM; -#ifndef CONFIG_BINFMT_ZFLAT if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) { - fprintf(stderr, "Support for ZFLAT executables is not enabled\n"); + fprintf(stderr, "ZFLAT executables are not supported\n"); return -ENOEXEC; } -#endif /* * calculate the extra space we need to map in @@ -483,17 +295,9 @@ static int load_flat_file(struct linux_binprm * bprm, (int)(data_len + bss_len + stack_len), (int)datapos); fpos = ntohl(hdr->data_start); -#ifdef CONFIG_BINFMT_ZFLAT - if (flags & FLAT_FLAG_GZDATA) { - result = decompress_exec(bprm, fpos, (char *) datapos, - data_len + (relocs * sizeof(abi_ulong))) - } else -#endif - { - result = target_pread(bprm->src.fd, datapos, - data_len + (relocs * sizeof(abi_ulong)), - fpos); - } + result = target_pread(bprm->src.fd, datapos, + data_len + (relocs * sizeof(abi_ulong)), + fpos); if (result < 0) { fprintf(stderr, "Unable to read data+bss\n"); return result; @@ -515,38 +319,12 @@ static int load_flat_file(struct linux_binprm * bprm, datapos = realdatastart + indx_len; reloc = (textpos + ntohl(hdr->reloc_start) + indx_len); -#ifdef CONFIG_BINFMT_ZFLAT -#error code needs checking - /* - * load it all in and treat it like a RAM load from now on - */ - if (flags & FLAT_FLAG_GZIP) { - result = decompress_exec(bprm, sizeof (struct flat_hdr), - (((char *) textpos) + sizeof (struct flat_hdr)), - (text_len + data_len + (relocs * sizeof(unsigned long)) - - sizeof (struct flat_hdr)), - 0); - memmove((void *) datapos, (void *) realdatastart, - data_len + (relocs * sizeof(unsigned long))); - } else if (flags & FLAT_FLAG_GZDATA) { - fpos = 0; - result = bprm->file->f_op->read(bprm->file, - (char *) textpos, text_len, &fpos); - if (!is_error(result)) { - result = decompress_exec(bprm, text_len, (char *) datapos, - data_len + (relocs * sizeof(unsigned long)), 0); - } - } - else -#endif - { - result = target_pread(bprm->src.fd, textpos, - text_len, 0); - if (result >= 0) { - result = target_pread(bprm->src.fd, datapos, - data_len + (relocs * sizeof(abi_ulong)), - ntohl(hdr->data_start)); - } + result = target_pread(bprm->src.fd, textpos, + text_len, 0); + if (result >= 0) { + result = target_pread(bprm->src.fd, datapos, + data_len + (relocs * sizeof(abi_ulong)), + ntohl(hdr->data_start)); } if (result < 0) { fprintf(stderr, "Unable to read code+data+bss\n"); @@ -678,44 +456,6 @@ static int load_flat_file(struct linux_binprm * bprm, /****************************************************************************/ -#ifdef CONFIG_BINFMT_SHARED_FLAT - -/* - * Load a shared library into memory. The library gets its own data - * segment (including bss) but not argv/argc/environ. - */ - -static int load_flat_shared_library(int id, struct lib_info *libs) -{ - struct linux_binprm bprm; - int res; - char buf[16]; - - /* Create the file name */ - sprintf(buf, "/lib/lib%d.so", id); - - /* Open the file up */ - bprm.filename = buf; - bprm.file = open_exec(bprm.filename); - res = PTR_ERR(bprm.file); - if (IS_ERR(bprm.file)) - return res; - - res = prepare_binprm(&bprm); - - if (!is_error(res)) { - res = load_flat_file(&bprm, libs, id, NULL); - } - if (bprm.file) { - allow_write_access(bprm.file); - fput(bprm.file); - bprm.file = NULL; - } - return(res); -} - -#endif /* CONFIG_BINFMT_SHARED_FLAT */ - int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) { struct lib_info libinfo[MAX_SHARED_LIBS]; @@ -793,19 +533,6 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) */ start_addr = libinfo[0].entry; -#ifdef CONFIG_BINFMT_SHARED_FLAT -#error here - for (i = MAX_SHARED_LIBS-1; i>0; i--) { - if (libinfo[i].loaded) { - /* Push previous first to call address */ - --sp; - if (put_user_ual(start_addr, sp)) - return -EFAULT; - start_addr = libinfo[i].entry; - } - } -#endif - /* Stash our initial stack pointer into the mm structure */ info->start_code = libinfo[0].start_code; info->end_code = libinfo[0].start_code + libinfo[0].text_len; From 1e0f2b38ac104ec3606750cce847cc9d8e4f66ac Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:04 +0100 Subject: [PATCH 0184/2884] hw/misc: Don't special case RESET_TYPE_COLD in npcm7xx_clk, gcr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The npcm7xx_clk and npcm7xx_gcr device reset methods look at the ResetType argument and only handle RESET_TYPE_COLD, producing a warning if another reset type is passed. This is different from how every other three-phase-reset method we have works, and makes it difficult to add new reset types. A better pattern is "assume that any reset type you don't know about should be handled like RESET_TYPE_COLD"; switch these devices to do that. Then adding a new reset type will only need to touch those devices where its behaviour really needs to be different from the standard cold reset. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-2-peter.maydell@linaro.org --- hw/misc/npcm7xx_clk.c | 13 +++---------- hw/misc/npcm7xx_gcr.c | 12 ++++-------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index ac1622c38a..2098c85ee0 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -873,20 +873,13 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - switch (type) { - case RESET_TYPE_COLD: - memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); - s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - npcm7xx_clk_update_all_clocks(s); - return; - } - + memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); + s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + npcm7xx_clk_update_all_clocks(s); /* * A small number of registers need to be reset on a core domain reset, * but no such reset type exists yet. */ - qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.", - __func__, type); } static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c index 9252f9d148..c4c4e246d7 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm7xx_gcr.c @@ -159,14 +159,10 @@ static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - switch (type) { - case RESET_TYPE_COLD: - memcpy(s->regs, cold_reset_values, sizeof(s->regs)); - s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; - s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; - s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; - break; - } + memcpy(s->regs, cold_reset_values, sizeof(s->regs)); + s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; + s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; + s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; } static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) From ef6ab2922fc94956c0b28c55ec2abe61f71446a9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:05 +0100 Subject: [PATCH 0185/2884] allwinner-i2c, adm1272: Use device_cold_reset() for software-triggered reset Rather than directly calling the device's implementation of its 'hold' reset phase, call device_cold_reset(). This means we don't have to adjust this callsite when we add another argument to the function signature for the hold and exit reset methods. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-3-peter.maydell@linaro.org --- hw/i2c/allwinner-i2c.c | 3 +-- hw/sensor/adm1272.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c index 8abcc39a5c..96c20c8637 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -385,8 +385,7 @@ static void allwinner_i2c_write(void *opaque, hwaddr offset, break; case TWI_SRST_REG: if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) { - /* Perform reset */ - allwinner_i2c_reset_hold(OBJECT(s)); + device_cold_reset(DEVICE(s)); } s->srst = value & TWI_SRST_MASK; break; diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c index 1f7c8abb83..a19557ec9e 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -386,7 +386,7 @@ static int adm1272_write_data(PMBusDevice *pmdev, const uint8_t *buf, break; case ADM1272_MFR_POWER_CYCLE: - adm1272_exit_reset((Object *)s); + device_cold_reset(DEVICE(s)); break; case ADM1272_HYSTERESIS_LOW: From aadea887f4429fcc96429b126c254de94317b474 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:06 +0100 Subject: [PATCH 0186/2884] scripts/coccinelle: New script to add ResetType to hold and exit phases We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. We should pass the type to all three of the phase methods to avoid having to do that. This coccinelle script adds the ResetType argument to the hold and exit phases of the Resettable interface. The first part of the script (rules holdfn_assigned, holdfn_defined, exitfn_assigned, exitfn_defined) update implementations of the interface within device models, both to change the signature of their method implementations and to pass on the reset type when they invoke reset on some other device. The second part of the script is various special cases: * method callsites in resettable_phase_hold(), resettable_phase_exit() and device_phases_reset() * updating the typedefs for the methods * isl_pmbus_vr.c has some code where one device's reset method directly calls the implementation of a different device's method Signed-off-by: Peter Maydell Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-4-peter.maydell@linaro.org --- scripts/coccinelle/reset-type.cocci | 133 ++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 scripts/coccinelle/reset-type.cocci diff --git a/scripts/coccinelle/reset-type.cocci b/scripts/coccinelle/reset-type.cocci new file mode 100644 index 0000000000..14abdd7bd0 --- /dev/null +++ b/scripts/coccinelle/reset-type.cocci @@ -0,0 +1,133 @@ +// Convert device code using three-phase reset to add a ResetType +// argument to implementations of ResettableHoldPhase and +// ResettableEnterPhase methods. +// +// Copyright Linaro Ltd 2024 +// SPDX-License-Identifier: GPL-2.0-or-later +// +// for dir in include hw target; do \ +// spatch --macro-file scripts/cocci-macro-file.h \ +// --sp-file scripts/coccinelle/reset-type.cocci \ +// --keep-comments --smpl-spacing --in-place --include-headers \ +// --dir $dir; done +// +// This coccinelle script aims to produce a complete change that needs +// no human interaction, so as well as the generic "update device +// implementations of the hold and exit phase methods" it includes +// the special-case transformations needed for the core code and for +// one device model that does something a bit nonstandard. Those +// special cases are at the end of the file. + +// Look for where we use a function as a ResettableHoldPhase method, +// either by directly assigning it to phases.hold or by calling +// resettable_class_set_parent_phases, and remember the function name. +@ holdfn_assigned @ +identifier enterfn, holdfn, exitfn; +identifier rc; +expression e; +@@ +ResettableClass *rc; +... +( + rc->phases.hold = holdfn; +| + resettable_class_set_parent_phases(rc, enterfn, holdfn, exitfn, e); +) + +// Look for the definition of the function we found in holdfn_assigned, +// and add the new argument. If the function calls a hold function +// itself (probably chaining to the parent class reset) then add the +// new argument there too. +@ holdfn_defined @ +identifier holdfn_assigned.holdfn; +typedef Object; +identifier obj; +expression parent; +@@ +-holdfn(Object *obj) ++holdfn(Object *obj, ResetType type) +{ + <... +- parent.hold(obj) ++ parent.hold(obj, type) + ...> +} + +// Similarly for ResettableExitPhase. +@ exitfn_assigned @ +identifier enterfn, holdfn, exitfn; +identifier rc; +expression e; +@@ +ResettableClass *rc; +... +( + rc->phases.exit = exitfn; +| + resettable_class_set_parent_phases(rc, enterfn, holdfn, exitfn, e); +) +@ exitfn_defined @ +identifier exitfn_assigned.exitfn; +typedef Object; +identifier obj; +expression parent; +@@ +-exitfn(Object *obj) ++exitfn(Object *obj, ResetType type) +{ + <... +- parent.exit(obj) ++ parent.exit(obj, type) + ...> +} + +// SPECIAL CASES ONLY BELOW HERE +// We use a python scripted constraint on the position of the match +// to ensure that they only match in a particular function. See +// https://public-inbox.org/git/alpine.DEB.2.21.1808240652370.2344@hadrien/ +// which recommends this as the way to do "match only in this function". + +// Special case: isl_pmbus_vr.c has some reset methods calling others directly +@ isl_pmbus_vr @ +identifier obj; +@@ +- isl_pmbus_vr_exit_reset(obj); ++ isl_pmbus_vr_exit_reset(obj, type); + +// Special case: device_phases_reset() needs to pass RESET_TYPE_COLD +@ device_phases_reset_hold @ +expression obj; +identifier rc; +identifier phase; +position p : script:python() { p[0].current_element == "device_phases_reset" }; +@@ +- rc->phases.phase(obj)@p ++ rc->phases.phase(obj, RESET_TYPE_COLD) + +// Special case: in resettable_phase_hold() and resettable_phase_exit() +// we need to pass through the ResetType argument to the method being called +@ resettable_phase_hold @ +expression obj; +identifier rc; +position p : script:python() { p[0].current_element == "resettable_phase_hold" }; +@@ +- rc->phases.hold(obj)@p ++ rc->phases.hold(obj, type) +@ resettable_phase_exit @ +expression obj; +identifier rc; +position p : script:python() { p[0].current_element == "resettable_phase_exit" }; +@@ +- rc->phases.exit(obj)@p ++ rc->phases.exit(obj, type) +// Special case: the typedefs for the methods need to declare the new argument +@ phase_typedef_hold @ +identifier obj; +@@ +- typedef void (*ResettableHoldPhase)(Object *obj); ++ typedef void (*ResettableHoldPhase)(Object *obj, ResetType type); +@ phase_typedef_exit @ +identifier obj; +@@ +- typedef void (*ResettableExitPhase)(Object *obj); ++ typedef void (*ResettableExitPhase)(Object *obj, ResetType type); From ad80e36744785fe9326d4104d98e976822e90cc2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:07 +0100 Subject: [PATCH 0187/2884] hw, target: Add ResetType argument to hold and exit phase methods We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org --- hw/adc/npcm7xx_adc.c | 2 +- hw/arm/pxa2xx_pic.c | 2 +- hw/arm/smmu-common.c | 2 +- hw/arm/smmuv3.c | 4 ++-- hw/arm/stellaris.c | 10 +++++----- hw/audio/asc.c | 2 +- hw/char/cadence_uart.c | 2 +- hw/char/sifive_uart.c | 2 +- hw/core/cpu-common.c | 2 +- hw/core/qdev.c | 4 ++-- hw/core/reset.c | 2 +- hw/core/resettable.c | 4 ++-- hw/display/virtio-vga.c | 4 ++-- hw/gpio/npcm7xx_gpio.c | 2 +- hw/gpio/pl061.c | 2 +- hw/gpio/stm32l4x5_gpio.c | 2 +- hw/hyperv/vmbus.c | 2 +- hw/i2c/allwinner-i2c.c | 2 +- hw/i2c/npcm7xx_smbus.c | 2 +- hw/input/adb.c | 2 +- hw/input/ps2.c | 12 ++++++------ hw/intc/arm_gic_common.c | 2 +- hw/intc/arm_gic_kvm.c | 4 ++-- hw/intc/arm_gicv3_common.c | 2 +- hw/intc/arm_gicv3_its.c | 4 ++-- hw/intc/arm_gicv3_its_common.c | 2 +- hw/intc/arm_gicv3_its_kvm.c | 4 ++-- hw/intc/arm_gicv3_kvm.c | 4 ++-- hw/intc/xics.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/misc/djmemc.c | 2 +- hw/misc/iosb.c | 2 +- hw/misc/mac_via.c | 8 ++++---- hw/misc/macio/cuda.c | 4 ++-- hw/misc/macio/pmu.c | 4 ++-- hw/misc/mos6522.c | 2 +- hw/misc/npcm7xx_mft.c | 2 +- hw/misc/npcm7xx_pwm.c | 2 +- hw/misc/stm32l4x5_exti.c | 2 +- hw/misc/stm32l4x5_rcc.c | 10 +++++----- hw/misc/stm32l4x5_syscfg.c | 2 +- hw/misc/xlnx-versal-cframe-reg.c | 2 +- hw/misc/xlnx-versal-crl.c | 2 +- hw/misc/xlnx-versal-pmc-iou-slcr.c | 2 +- hw/misc/xlnx-versal-trng.c | 2 +- hw/misc/xlnx-versal-xramc.c | 2 +- hw/misc/xlnx-zynqmp-apu-ctrl.c | 2 +- hw/misc/xlnx-zynqmp-crf.c | 2 +- hw/misc/zynq_slcr.c | 4 ++-- hw/net/can/xlnx-zynqmp-can.c | 2 +- hw/net/e1000.c | 2 +- hw/net/e1000e.c | 2 +- hw/net/igb.c | 2 +- hw/net/igbvf.c | 2 +- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-versal-efuse-ctrl.c | 2 +- hw/nvram/xlnx-zynqmp-efuse.c | 2 +- hw/pci-bridge/cxl_root_port.c | 4 ++-- hw/pci-bridge/pcie_root_port.c | 2 +- hw/pci-host/bonito.c | 2 +- hw/pci-host/pnv_phb.c | 4 ++-- hw/pci-host/pnv_phb3_msi.c | 4 ++-- hw/pci/pci.c | 4 ++-- hw/rtc/mc146818rtc.c | 2 +- hw/s390x/css-bridge.c | 2 +- hw/sensor/adm1266.c | 2 +- hw/sensor/adm1272.c | 2 +- hw/sensor/isl_pmbus_vr.c | 10 +++++----- hw/sensor/max31785.c | 2 +- hw/sensor/max34451.c | 2 +- hw/ssi/npcm7xx_fiu.c | 2 +- hw/timer/etraxfs_timer.c | 2 +- hw/timer/npcm7xx_timer.c | 2 +- hw/usb/hcd-dwc2.c | 8 ++++---- hw/usb/xlnx-versal-usb2-ctrl-regs.c | 2 +- hw/virtio/virtio-pci.c | 2 +- include/hw/resettable.h | 4 ++-- target/arm/cpu.c | 4 ++-- target/avr/cpu.c | 4 ++-- target/cris/cpu.c | 4 ++-- target/hexagon/cpu.c | 4 ++-- target/i386/cpu.c | 4 ++-- target/loongarch/cpu.c | 4 ++-- target/m68k/cpu.c | 4 ++-- target/microblaze/cpu.c | 4 ++-- target/mips/cpu.c | 4 ++-- target/openrisc/cpu.c | 4 ++-- target/ppc/cpu_init.c | 4 ++-- target/riscv/cpu.c | 4 ++-- target/rx/cpu.c | 4 ++-- target/sh4/cpu.c | 4 ++-- target/sparc/cpu.c | 4 ++-- target/tricore/cpu.c | 4 ++-- target/xtensa/cpu.c | 4 ++-- 94 files changed, 150 insertions(+), 150 deletions(-) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index c6647eec6d..de8469dae4 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -218,7 +218,7 @@ static void npcm7xx_adc_enter_reset(Object *obj, ResetType type) npcm7xx_adc_reset(s); } -static void npcm7xx_adc_hold_reset(Object *obj) +static void npcm7xx_adc_hold_reset(Object *obj, ResetType type) { NPCM7xxADCState *s = NPCM7XX_ADC(obj); diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index f54546cd4d..34c5555dba 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -272,7 +272,7 @@ static int pxa2xx_pic_post_load(void *opaque, int version_id) return 0; } -static void pxa2xx_pic_reset_hold(Object *obj) +static void pxa2xx_pic_reset_hold(Object *obj, ResetType type) { PXA2xxPICState *s = PXA2XX_PIC(obj); diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index c4b540656c..1ce706bf94 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -682,7 +682,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) } } -static void smmu_base_reset_hold(Object *obj) +static void smmu_base_reset_hold(Object *obj, ResetType type) { SMMUState *s = ARM_SMMU(obj); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 9eb56a70f3..2d1e0d55ec 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1727,13 +1727,13 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) } } -static void smmu_reset_hold(Object *obj) +static void smmu_reset_hold(Object *obj, ResetType type) { SMMUv3State *s = ARM_SMMUV3(obj); SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } smmuv3_init_regs(s); diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index a2f998bf9e..376746251e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -394,7 +394,7 @@ static void stellaris_sys_reset_enter(Object *obj, ResetType type) s->dcgc[0] = 1; } -static void stellaris_sys_reset_hold(Object *obj) +static void stellaris_sys_reset_hold(Object *obj, ResetType type) { ssys_state *s = STELLARIS_SYS(obj); @@ -402,7 +402,7 @@ static void stellaris_sys_reset_hold(Object *obj) ssys_calculate_system_clock(s, true); } -static void stellaris_sys_reset_exit(Object *obj) +static void stellaris_sys_reset_exit(Object *obj, ResetType type) { } @@ -618,7 +618,7 @@ static void stellaris_i2c_reset_enter(Object *obj, ResetType type) i2c_end_transfer(s->bus); } -static void stellaris_i2c_reset_hold(Object *obj) +static void stellaris_i2c_reset_hold(Object *obj, ResetType type) { stellaris_i2c_state *s = STELLARIS_I2C(obj); @@ -631,7 +631,7 @@ static void stellaris_i2c_reset_hold(Object *obj) s->mcr = 0; } -static void stellaris_i2c_reset_exit(Object *obj) +static void stellaris_i2c_reset_exit(Object *obj, ResetType type) { stellaris_i2c_state *s = STELLARIS_I2C(obj); @@ -787,7 +787,7 @@ static void stellaris_adc_trigger(void *opaque, int irq, int level) } } -static void stellaris_adc_reset_hold(Object *obj) +static void stellaris_adc_reset_hold(Object *obj, ResetType type) { StellarisADCState *s = STELLARIS_ADC(obj); int n; diff --git a/hw/audio/asc.c b/hw/audio/asc.c index 87b5624326..805416372c 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -610,7 +610,7 @@ static void asc_fifo_init(ASCFIFOState *fs, int index) g_free(name); } -static void asc_reset_hold(Object *obj) +static void asc_reset_hold(Object *obj, ResetType type) { ASCState *s = ASC(obj); diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index db31d7cc85..77d9a2a221 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -525,7 +525,7 @@ static void cadence_uart_reset_init(Object *obj, ResetType type) s->r[R_TTRIG] = 0x00000020; } -static void cadence_uart_reset_hold(Object *obj) +static void cadence_uart_reset_hold(Object *obj, ResetType type) { CadenceUARTState *s = CADENCE_UART(obj); diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index e8716c4252..7fc6787f69 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -214,7 +214,7 @@ static void sifive_uart_reset_enter(Object *obj, ResetType type) s->rx_fifo_len = 0; } -static void sifive_uart_reset_hold(Object *obj) +static void sifive_uart_reset_hold(Object *obj, ResetType type) { SiFiveUARTState *s = SIFIVE_UART(obj); qemu_irq_lower(s->irq); diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 4bd9c70a83..a72d48d9e1 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -113,7 +113,7 @@ void cpu_reset(CPUState *cpu) trace_cpu_reset(cpu->cpu_index); } -static void cpu_common_reset_hold(Object *obj) +static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 00efaf1bd1..f3a996f57d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -760,10 +760,10 @@ static void device_phases_reset(DeviceState *dev) rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD); } if (rc->phases.hold) { - rc->phases.hold(OBJECT(dev)); + rc->phases.hold(OBJECT(dev), RESET_TYPE_COLD); } if (rc->phases.exit) { - rc->phases.exit(OBJECT(dev)); + rc->phases.exit(OBJECT(dev), RESET_TYPE_COLD); } } diff --git a/hw/core/reset.c b/hw/core/reset.c index d50da7e304..f9fef45e05 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -73,7 +73,7 @@ static ResettableState *legacy_reset_get_state(Object *obj) return &lr->reset_state; } -static void legacy_reset_hold(Object *obj) +static void legacy_reset_hold(Object *obj, ResetType type) { LegacyReset *lr = LEGACY_RESET(obj); diff --git a/hw/core/resettable.c b/hw/core/resettable.c index c3df75c6ba..bebf7f10b2 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -181,7 +181,7 @@ static void resettable_phase_hold(Object *obj, void *opaque, ResetType type) trace_resettable_transitional_function(obj, obj_typename); tr_func(obj); } else if (rc->phases.hold) { - rc->phases.hold(obj); + rc->phases.hold(obj, type); } } trace_resettable_phase_hold_end(obj, obj_typename, s->count); @@ -204,7 +204,7 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) if (--s->count == 0) { trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit); if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) { - rc->phases.exit(obj); + rc->phases.exit(obj, type); } } s->exit_phase_in_progress = false; diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 94d3353f54..276f315108 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -180,14 +180,14 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_vga_base_reset_hold(Object *obj) +static void virtio_vga_base_reset_hold(Object *obj, ResetType type) { VirtIOVGABaseClass *klass = VIRTIO_VGA_BASE_GET_CLASS(obj); VirtIOVGABase *vvga = VIRTIO_VGA_BASE(obj); /* reset virtio-gpu */ if (klass->parent_phases.hold) { - klass->parent_phases.hold(obj); + klass->parent_phases.hold(obj, type); } /* reset vga */ diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 6e70ac1f24..ba19b9ebad 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -352,7 +352,7 @@ static void npcm7xx_gpio_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_GPIO_ODSC] = s->reset_odsc; } -static void npcm7xx_gpio_hold_reset(Object *obj) +static void npcm7xx_gpio_hold_reset(Object *obj, ResetType type) { NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 86f2383655..d5838b8e98 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -484,7 +484,7 @@ static void pl061_enter_reset(Object *obj, ResetType type) s->amsel = 0; } -static void pl061_hold_reset(Object *obj) +static void pl061_hold_reset(Object *obj, ResetType type) { PL061State *s = PL061(obj); int i, level; diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c index 63b8763e9d..71bf5fddb2 100644 --- a/hw/gpio/stm32l4x5_gpio.c +++ b/hw/gpio/stm32l4x5_gpio.c @@ -70,7 +70,7 @@ static bool is_push_pull(Stm32l4x5GpioState *s, unsigned pin) return extract32(s->otyper, pin, 1) == 0; } -static void stm32l4x5_gpio_reset_hold(Object *obj) +static void stm32l4x5_gpio_reset_hold(Object *obj, ResetType type) { Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index f33afeeea2..490d805d29 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2453,7 +2453,7 @@ static void vmbus_unrealize(BusState *bus) qemu_mutex_destroy(&vmbus->rx_queue_lock); } -static void vmbus_reset_hold(Object *obj) +static void vmbus_reset_hold(Object *obj, ResetType type) { vmbus_deinit(VMBUS(obj)); } diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c index 96c20c8637..16f1d6d40e 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -170,7 +170,7 @@ static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s) return s->cntr & TWI_CNTR_INT_EN; } -static void allwinner_i2c_reset_hold(Object *obj) +static void allwinner_i2c_reset_hold(Object *obj, ResetType type) { AWI2CState *s = AW_I2C(obj); diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index 0ea3083bb6..22d68fc67d 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -1022,7 +1022,7 @@ static void npcm7xx_smbus_enter_reset(Object *obj, ResetType type) s->rx_cur = 0; } -static void npcm7xx_smbus_hold_reset(Object *obj) +static void npcm7xx_smbus_hold_reset(Object *obj, ResetType type) { NPCM7xxSMBusState *s = NPCM7XX_SMBUS(obj); diff --git a/hw/input/adb.c b/hw/input/adb.c index 98f39b4281..aff7130fd0 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -231,7 +231,7 @@ static const VMStateDescription vmstate_adb_bus = { } }; -static void adb_bus_reset_hold(Object *obj) +static void adb_bus_reset_hold(Object *obj, ResetType type) { ADBBusState *adb_bus = ADB_BUS(obj); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 00b695a0b9..d6f834443d 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -1007,7 +1007,7 @@ void ps2_write_mouse(PS2MouseState *s, int val) } } -static void ps2_reset_hold(Object *obj) +static void ps2_reset_hold(Object *obj, ResetType type) { PS2State *s = PS2_DEVICE(obj); @@ -1015,7 +1015,7 @@ static void ps2_reset_hold(Object *obj) ps2_reset_queue(s); } -static void ps2_reset_exit(Object *obj) +static void ps2_reset_exit(Object *obj, ResetType type) { PS2State *s = PS2_DEVICE(obj); @@ -1048,7 +1048,7 @@ static void ps2_common_post_load(PS2State *s) q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } -static void ps2_kbd_reset_hold(Object *obj) +static void ps2_kbd_reset_hold(Object *obj, ResetType type) { PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); PS2KbdState *s = PS2_KBD_DEVICE(obj); @@ -1056,7 +1056,7 @@ static void ps2_kbd_reset_hold(Object *obj) trace_ps2_kbd_reset(s); if (ps2dc->parent_phases.hold) { - ps2dc->parent_phases.hold(obj); + ps2dc->parent_phases.hold(obj, type); } s->scan_enabled = 1; @@ -1065,7 +1065,7 @@ static void ps2_kbd_reset_hold(Object *obj) s->modifiers = 0; } -static void ps2_mouse_reset_hold(Object *obj) +static void ps2_mouse_reset_hold(Object *obj, ResetType type) { PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); PS2MouseState *s = PS2_MOUSE_DEVICE(obj); @@ -1073,7 +1073,7 @@ static void ps2_mouse_reset_hold(Object *obj) trace_ps2_mouse_reset(s); if (ps2dc->parent_phases.hold) { - ps2dc->parent_phases.hold(obj); + ps2dc->parent_phases.hold(obj, type); } s->mouse_status = 0; diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 94c173cb07..53fb2c4e2d 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -263,7 +263,7 @@ static inline void arm_gic_common_reset_irq_state(GICState *s, int cidx, } } -static void arm_gic_common_reset_hold(Object *obj) +static void arm_gic_common_reset_hold(Object *obj, ResetType type) { GICState *s = ARM_GIC_COMMON(obj); int i, j; diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index e0d9e512a3..53defee7d5 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -473,13 +473,13 @@ static void kvm_arm_gic_get(GICState *s) } } -static void kvm_arm_gic_reset_hold(Object *obj) +static void kvm_arm_gic_reset_hold(Object *obj, ResetType type) { GICState *s = ARM_GIC_COMMON(obj); KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); if (kgc->parent_phases.hold) { - kgc->parent_phases.hold(obj); + kgc->parent_phases.hold(obj, type); } if (kvm_arm_gic_can_save_restore(s)) { diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 207f8417e1..bd50a1b079 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -495,7 +495,7 @@ static void arm_gicv3_finalize(Object *obj) g_free(s->redist_region_count); } -static void arm_gicv3_common_reset_hold(Object *obj) +static void arm_gicv3_common_reset_hold(Object *obj, ResetType type) { GICv3State *s = ARM_GICV3_COMMON(obj); int i; diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 52e9aca9c6..bf31158470 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -1950,13 +1950,13 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) } } -static void gicv3_its_reset_hold(Object *obj) +static void gicv3_its_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } /* Quiescent bit reset to 1 */ diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 331d6b93cc..0b97362cd2 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -123,7 +123,7 @@ void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, msi_nonbroken = true; } -static void gicv3_its_common_reset_hold(Object *obj) +static void gicv3_its_common_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 3befc960db..35539c099f 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -197,14 +197,14 @@ static void kvm_arm_its_post_load(GICv3ITSState *s) GITS_CTLR, &s->ctlr, true, &error_abort); } -static void kvm_arm_its_reset_hold(Object *obj) +static void kvm_arm_its_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); KVMARMITSClass *c = KVM_ARM_ITS_GET_CLASS(s); int i; if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 00a383079b..9ea6b8e218 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -703,7 +703,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS]; } -static void kvm_arm_gicv3_reset_hold(Object *obj) +static void kvm_arm_gicv3_reset_hold(Object *obj, ResetType type) { GICv3State *s = ARM_GICV3_COMMON(obj); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); @@ -711,7 +711,7 @@ static void kvm_arm_gicv3_reset_hold(Object *obj) DPRINTF("Reset\n"); if (kgc->parent_phases.hold) { - kgc->parent_phases.hold(obj); + kgc->parent_phases.hold(obj, type); } if (s->migration_blocker) { diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 700abfa7a6..9b3b7abaea 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -579,7 +579,7 @@ static void ics_reset_irq(ICSIRQState *irq) irq->saved_priority = 0xff; } -static void ics_reset_hold(Object *obj) +static void ics_reset_hold(Object *obj, ResetType type) { ICSState *ics = ICS(obj); g_autofree uint8_t *flags = g_malloc(ics->nr_irqs); diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index b5a7713863..e2ae7c3201 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -175,7 +175,7 @@ static void glue_nmi_release(void *opaque) GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); } -static void glue_reset_hold(Object *obj) +static void glue_reset_hold(Object *obj, ResetType type) { GLUEState *s = GLUE(obj); diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c index 9b69656c3a..96d5efb5e3 100644 --- a/hw/misc/djmemc.c +++ b/hw/misc/djmemc.c @@ -96,7 +96,7 @@ static void djmemc_init(Object *obj) sysbus_init_mmio(sbd, &s->mem_regs); } -static void djmemc_reset_hold(Object *obj) +static void djmemc_reset_hold(Object *obj, ResetType type) { DJMEMCState *s = DJMEMC(obj); diff --git a/hw/misc/iosb.c b/hw/misc/iosb.c index e20305e801..31927eaedb 100644 --- a/hw/misc/iosb.c +++ b/hw/misc/iosb.c @@ -81,7 +81,7 @@ static const MemoryRegionOps iosb_mmio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void iosb_reset_hold(Object *obj) +static void iosb_reset_hold(Object *obj, ResetType type) { IOSBState *s = IOSB(obj); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index db6142b5f4..652395b84f 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1203,7 +1203,7 @@ static int via1_post_load(void *opaque, int version_id) } /* VIA 1 */ -static void mos6522_q800_via1_reset_hold(Object *obj) +static void mos6522_q800_via1_reset_hold(Object *obj, ResetType type) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(obj); MOS6522State *ms = MOS6522(v1s); @@ -1211,7 +1211,7 @@ static void mos6522_q800_via1_reset_hold(Object *obj) ADBBusState *adb_bus = &v1s->adb_bus; if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; @@ -1359,13 +1359,13 @@ static void mos6522_q800_via2_portB_write(MOS6522State *s) } } -static void mos6522_q800_via2_reset_hold(Object *obj) +static void mos6522_q800_via2_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 41934e2cf8..beab0ffb13 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -586,13 +586,13 @@ static void mos6522_cuda_portB_write(MOS6522State *s) cuda_update(cs); } -static void mos6522_cuda_reset_hold(Object *obj) +static void mos6522_cuda_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = CUDA_TIMER_FREQ; diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index e40c51bf52..238da58ead 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -792,7 +792,7 @@ static void mos6522_pmu_portB_write(MOS6522State *s) pmu_update(ps); } -static void mos6522_pmu_reset_hold(Object *obj) +static void mos6522_pmu_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); @@ -800,7 +800,7 @@ static void mos6522_pmu_reset_hold(Object *obj) MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index e3fe87c20c..515f62e687 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -642,7 +642,7 @@ const VMStateDescription vmstate_mos6522 = { } }; -static void mos6522_reset_hold(Object *obj) +static void mos6522_reset_hold(Object *obj, ResetType type) { MOS6522State *s = MOS6522(obj); diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c index 9a848584e1..9fcc69fe5c 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -467,7 +467,7 @@ static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) npcm7xx_mft_reset(s); } -static void npcm7xx_mft_hold_reset(Object *obj) +static void npcm7xx_mft_hold_reset(Object *obj, ResetType type) { NPCM7xxMFTState *s = NPCM7XX_MFT(obj); diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c index fca2dd2e5a..f7f77e30a2 100644 --- a/hw/misc/npcm7xx_pwm.c +++ b/hw/misc/npcm7xx_pwm.c @@ -468,7 +468,7 @@ static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type) s->piir = 0x00000000; } -static void npcm7xx_pwm_hold_reset(Object *obj) +static void npcm7xx_pwm_hold_reset(Object *obj, ResetType type) { NPCM7xxPWMState *s = NPCM7XX_PWM(obj); int i; diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index 9fd859160d..a090dbd366 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -77,7 +77,7 @@ static unsigned configurable_mask(unsigned bank) return valid_mask(bank) & ~exti_romask[bank]; } -static void stm32l4x5_exti_reset_hold(Object *obj) +static void stm32l4x5_exti_reset_hold(Object *obj, ResetType type) { Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index ed2dbd9dc3..417bd5e85f 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -113,13 +113,13 @@ static void clock_mux_reset_enter(Object *obj, ResetType type) set_clock_mux_init_info(s, s->id); } -static void clock_mux_reset_hold(Object *obj) +static void clock_mux_reset_hold(Object *obj, ResetType type) { RccClockMuxState *s = RCC_CLOCK_MUX(obj); clock_mux_update(s, true); } -static void clock_mux_reset_exit(Object *obj) +static void clock_mux_reset_exit(Object *obj, ResetType type) { RccClockMuxState *s = RCC_CLOCK_MUX(obj); clock_mux_update(s, false); @@ -263,13 +263,13 @@ static void pll_reset_enter(Object *obj, ResetType type) set_pll_init_info(s, s->id); } -static void pll_reset_hold(Object *obj) +static void pll_reset_hold(Object *obj, ResetType type) { RccPllState *s = RCC_PLL(obj); pll_update(s, true); } -static void pll_reset_exit(Object *obj) +static void pll_reset_exit(Object *obj, ResetType type) { RccPllState *s = RCC_PLL(obj); pll_update(s, false); @@ -907,7 +907,7 @@ static void rcc_update_csr(Stm32l4x5RccState *s) rcc_update_irq(s); } -static void stm32l4x5_rcc_reset_hold(Object *obj) +static void stm32l4x5_rcc_reset_hold(Object *obj, ResetType type) { Stm32l4x5RccState *s = STM32L4X5_RCC(obj); s->cr = 0x00000063; diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c index 3dafc00b49..a5a1ce2680 100644 --- a/hw/misc/stm32l4x5_syscfg.c +++ b/hw/misc/stm32l4x5_syscfg.c @@ -65,7 +65,7 @@ #define NUM_LINES_PER_EXTICR_REG 4 -static void stm32l4x5_syscfg_hold_reset(Object *obj) +static void stm32l4x5_syscfg_hold_reset(Object *obj, ResetType type) { Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj); diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index a6ab287b01..3fc838bd54 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -542,7 +542,7 @@ static void cframe_reg_reset_enter(Object *obj, ResetType type) } } -static void cframe_reg_reset_hold(Object *obj) +static void cframe_reg_reset_hold(Object *obj, ResetType type) { XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 1f1762ef16..f143900d5b 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -311,7 +311,7 @@ static void crl_reset_enter(Object *obj, ResetType type) } } -static void crl_reset_hold(Object *obj) +static void crl_reset_hold(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); diff --git a/hw/misc/xlnx-versal-pmc-iou-slcr.c b/hw/misc/xlnx-versal-pmc-iou-slcr.c index 60e13a78ab..e469c04d76 100644 --- a/hw/misc/xlnx-versal-pmc-iou-slcr.c +++ b/hw/misc/xlnx-versal-pmc-iou-slcr.c @@ -1350,7 +1350,7 @@ static void xlnx_versal_pmc_iou_slcr_reset_init(Object *obj, ResetType type) } } -static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj) +static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj, ResetType type) { XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj); diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 6495188dc7..51eb760041 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -632,7 +632,7 @@ static void trng_unrealize(DeviceState *dev) s->prng = NULL; } -static void trng_reset_hold(Object *obj) +static void trng_reset_hold(Object *obj, ResetType type) { trng_reset(XLNX_VERSAL_TRNG(obj)); } diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index a5f78c190e..ad839ce7e9 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -137,7 +137,7 @@ static void xram_ctrl_reset_enter(Object *obj, ResetType type) ARRAY_FIELD_DP32(s->regs, XRAM_IMP, SIZE, s->cfg.encoded_size); } -static void xram_ctrl_reset_hold(Object *obj) +static void xram_ctrl_reset_hold(Object *obj, ResetType type) { XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj); diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c index 1d441b41df..87e4a14067 100644 --- a/hw/misc/xlnx-zynqmp-apu-ctrl.c +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -150,7 +150,7 @@ static void zynqmp_apu_reset_enter(Object *obj, ResetType type) s->cpu_in_wfi = 0; } -static void zynqmp_apu_reset_hold(Object *obj) +static void zynqmp_apu_reset_hold(Object *obj, ResetType type) { XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c index a83efb44e3..e5aba56f69 100644 --- a/hw/misc/xlnx-zynqmp-crf.c +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -191,7 +191,7 @@ static void crf_reset_enter(Object *obj, ResetType type) } } -static void crf_reset_hold(Object *obj) +static void crf_reset_hold(Object *obj, ResetType type) { XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); ir_update_irq(s); diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index d2ac2e77f2..3412ff099e 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -416,7 +416,7 @@ static void zynq_slcr_reset_init(Object *obj, ResetType type) s->regs[R_DDRIOB + 12] = 0x00000021; } -static void zynq_slcr_reset_hold(Object *obj) +static void zynq_slcr_reset_hold(Object *obj, ResetType type) { ZynqSLCRState *s = ZYNQ_SLCR(obj); @@ -425,7 +425,7 @@ static void zynq_slcr_reset_hold(Object *obj) zynq_slcr_propagate_clocks(s); } -static void zynq_slcr_reset_exit(Object *obj) +static void zynq_slcr_reset_exit(Object *obj, ResetType type) { ZynqSLCRState *s = ZYNQ_SLCR(obj); diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index ca0ce4e8bb..58f1432bb3 100644 --- a/hw/net/can/xlnx-zynqmp-can.c +++ b/hw/net/can/xlnx-zynqmp-can.c @@ -1006,7 +1006,7 @@ static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type) ptimer_transaction_commit(s->can_timer); } -static void xlnx_zynqmp_can_reset_hold(Object *obj) +static void xlnx_zynqmp_can_reset_hold(Object *obj, ResetType type) { XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); unsigned int i; diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 43f3a4a701..5012b96464 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -373,7 +373,7 @@ static bool e1000_vet_init_need(void *opaque) return chkflag(VET); } -static void e1000_reset_hold(Object *obj) +static void e1000_reset_hold(Object *obj, ResetType type) { E1000State *d = E1000(obj); E1000BaseClass *edc = E1000_GET_CLASS(d); diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 7c6f602951..edc101eaf6 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -513,7 +513,7 @@ static void e1000e_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void e1000e_qdev_reset_hold(Object *obj) +static void e1000e_qdev_reset_hold(Object *obj, ResetType type) { E1000EState *s = E1000E(obj); diff --git a/hw/net/igb.c b/hw/net/igb.c index 9b37523d6d..1ef6170465 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -486,7 +486,7 @@ static void igb_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void igb_qdev_reset_hold(Object *obj) +static void igb_qdev_reset_hold(Object *obj, ResetType type) { IGBState *s = IGB(obj); diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 94a4e885f2..21a97d4d61 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -282,7 +282,7 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) pcie_ari_init(dev, 0x150); } -static void igbvf_qdev_reset_hold(Object *obj) +static void igbvf_qdev_reset_hold(Object *obj, ResetType type) { PCIDevice *vf = PCI_DEVICE(obj); diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 0a71a005c6..09575a77d7 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -417,7 +417,7 @@ static RegisterAccessInfo bbram_ctrl_regs_info[] = { } }; -static void bbram_ctrl_reset_hold(Object *obj) +static void bbram_ctrl_reset_hold(Object *obj, ResetType type) { XlnxBBRam *s = XLNX_BBRAM(obj); unsigned int i; diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index e4b9e11a3d..def6fe3302 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -658,7 +658,7 @@ static void efuse_ctrl_register_reset(RegisterInfo *reg) register_reset(reg); } -static void efuse_ctrl_reset_hold(Object *obj) +static void efuse_ctrl_reset_hold(Object *obj, ResetType type) { XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); unsigned int i; diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index ec98456e5d..2d465f0fc6 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -770,7 +770,7 @@ static void zynqmp_efuse_register_reset(RegisterInfo *reg) register_reset(reg); } -static void zynqmp_efuse_reset_hold(Object *obj) +static void zynqmp_efuse_reset_hold(Object *obj, ResetType type) { XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); unsigned int i; diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 8a30da602c..2dd10239bd 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -186,13 +186,13 @@ static void cxl_rp_realize(DeviceState *dev, Error **errp) component_bar); } -static void cxl_rp_reset_hold(Object *obj) +static void cxl_rp_reset_hold(Object *obj, ResetType type) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); CXLRootPort *crp = CXL_ROOT_PORT(obj); if (rpc->parent_phases.hold) { - rpc->parent_phases.hold(obj); + rpc->parent_phases.hold(obj, type); } latch_registers(crp); diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index efd96bf174..09a34786bc 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -43,7 +43,7 @@ static void rp_write_config(PCIDevice *d, uint32_t address, pcie_aer_root_write_config(d, address, val, len, root_cmd); } -static void rp_reset_hold(Object *obj) +static void rp_reset_hold(Object *obj, ResetType type) { PCIDevice *d = PCI_DEVICE(obj); DeviceState *qdev = DEVICE(obj); diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 1f0c435348..1516d0074d 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -590,7 +590,7 @@ static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num) } } -static void bonito_reset_hold(Object *obj) +static void bonito_reset_hold(Object *obj, ResetType type) { PCIBonitoState *s = PCI_BONITO(obj); uint32_t val = 0; diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index 157c00782c..d4c118d443 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -208,7 +208,7 @@ static void pnv_phb_class_init(ObjectClass *klass, void *data) dc->user_creatable = true; } -static void pnv_phb_root_port_reset_hold(Object *obj) +static void pnv_phb_root_port_reset_hold(Object *obj, ResetType type) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(obj); @@ -216,7 +216,7 @@ static void pnv_phb_root_port_reset_hold(Object *obj) uint8_t *conf = d->config; if (rpc->parent_phases.hold) { - rpc->parent_phases.hold(obj); + rpc->parent_phases.hold(obj, type); } if (phb_rp->version == 3) { diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index dc8d8637f2..a6d827f903 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -228,13 +228,13 @@ static void phb3_msi_resend(ICSState *ics) } } -static void phb3_msi_reset_hold(Object *obj) +static void phb3_msi_reset_hold(Object *obj, ResetType type) { Phb3MsiState *msi = PHB3_MSI(obj); ICSStateClass *icsc = ICS_GET_CLASS(obj); if (icsc->parent_phases.hold) { - icsc->parent_phases.hold(obj); + icsc->parent_phases.hold(obj, type); } memset(msi->rba, 0, sizeof(msi->rba)); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e7a39cb203..324c1302d2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -64,7 +64,7 @@ bool pci_available = true; static char *pcibus_get_dev_path(DeviceState *dev); static char *pcibus_get_fw_dev_path(DeviceState *dev); -static void pcibus_reset_hold(Object *obj); +static void pcibus_reset_hold(Object *obj, ResetType type); static bool pcie_has_upstream_port(PCIDevice *dev); static Property pci_props[] = { @@ -427,7 +427,7 @@ void pci_device_reset(PCIDevice *dev) * Called via bus_cold_reset on RST# assert, after the devices * have been reset device_cold_reset-ed already. */ -static void pcibus_reset_hold(Object *obj) +static void pcibus_reset_hold(Object *obj, ResetType type) { PCIBus *bus = PCI_BUS(obj); int i; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index f4c1869232..3379f92748 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -998,7 +998,7 @@ static void rtc_reset_enter(Object *obj, ResetType type) } } -static void rtc_reset_hold(Object *obj) +static void rtc_reset_hold(Object *obj, ResetType type) { MC146818RtcState *s = MC146818_RTC(obj); diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 34639f2143..8657ff7bf4 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -56,7 +56,7 @@ static void ccw_device_unplug(HotplugHandler *hotplug_dev, qdev_unrealize(dev); } -static void virtual_css_bus_reset_hold(Object *obj) +static void virtual_css_bus_reset_hold(Object *obj, ResetType type) { /* This should actually be modelled via the generic css */ css_reset(); diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c index 5454b73a63..25b87a7296 100644 --- a/hw/sensor/adm1266.c +++ b/hw/sensor/adm1266.c @@ -76,7 +76,7 @@ static const uint8_t adm1266_ic_device_id[] = {0x03, 0x41, 0x12, 0x66}; static const uint8_t adm1266_ic_device_rev[] = {0x08, 0x01, 0x08, 0x07, 0x0, 0x0, 0x07, 0x41, 0x30}; -static void adm1266_exit_reset(Object *obj) +static void adm1266_exit_reset(Object *obj, ResetType type) { ADM1266State *s = ADM1266(obj); PMBusDevice *pmdev = PMBUS_DEVICE(obj); diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c index a19557ec9e..3fc1e5d0ad 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -185,7 +185,7 @@ static uint32_t adm1272_direct_to_watts(uint16_t value) return pmbus_direct_mode2data(c, value); } -static void adm1272_exit_reset(Object *obj) +static void adm1272_exit_reset(Object *obj, ResetType type) { ADM1272State *s = ADM1272(obj); PMBusDevice *pmdev = PMBUS_DEVICE(obj); diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index e51269f6b8..304a66ea8b 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -63,7 +63,7 @@ static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name, pmbus_check_limits(pmdev); } -static void isl_pmbus_vr_exit_reset(Object *obj) +static void isl_pmbus_vr_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -102,11 +102,11 @@ static void isl_pmbus_vr_exit_reset(Object *obj) } /* The raa228000 uses different direct mode coefficients from most isl devices */ -static void raa228000_exit_reset(Object *obj) +static void raa228000_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); - isl_pmbus_vr_exit_reset(obj); + isl_pmbus_vr_exit_reset(obj, type); pmdev->pages[0].read_iout = 0; pmdev->pages[0].read_pout = 0; @@ -119,13 +119,13 @@ static void raa228000_exit_reset(Object *obj) pmdev->pages[0].read_temperature_3 = 0; } -static void isl69259_exit_reset(Object *obj) +static void isl69259_exit_reset(Object *obj, ResetType type) { ISLState *s = ISL69260(obj); static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 0x3c}; g_assert(sizeof(ic_device_id) <= sizeof(s->ic_device_id)); - isl_pmbus_vr_exit_reset(obj); + isl_pmbus_vr_exit_reset(obj, type); s->ic_device_id_len = sizeof(ic_device_id); memcpy(s->ic_device_id, ic_device_id, sizeof(ic_device_id)); diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c index 916ed4d457..3577a7c218 100644 --- a/hw/sensor/max31785.c +++ b/hw/sensor/max31785.c @@ -444,7 +444,7 @@ static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf, return 0; } -static void max31785_exit_reset(Object *obj) +static void max31785_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); MAX31785State *s = MAX31785(obj); diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c index 031ae53f59..93b53f3db2 100644 --- a/hw/sensor/max34451.c +++ b/hw/sensor/max34451.c @@ -608,7 +608,7 @@ static inline void *memset_word(void *s, uint16_t c, size_t n) return s; } -static void max34451_exit_reset(Object *obj) +static void max34451_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); MAX34451State *s = MAX34451(obj); diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 81dd972ee8..119c38c415 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -483,7 +483,7 @@ static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_FIU_CFG] = 0x0000000b; } -static void npcm7xx_fiu_hold_reset(Object *obj) +static void npcm7xx_fiu_hold_reset(Object *obj, ResetType type) { NPCM7xxFIUState *s = NPCM7XX_FIU(obj); int i; diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index da7c946af5..dd6d96b0a1 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -357,7 +357,7 @@ static void etraxfs_timer_reset_enter(Object *obj, ResetType type) t->rw_intr_mask = 0; } -static void etraxfs_timer_reset_hold(Object *obj) +static void etraxfs_timer_reset_hold(Object *obj, ResetType type) { ETRAXTimerState *t = ETRAX_TIMER(obj); diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c index 779c6049fa..c55ba02235 100644 --- a/hw/timer/npcm7xx_timer.c +++ b/hw/timer/npcm7xx_timer.c @@ -592,7 +592,7 @@ static void npcm7xx_watchdog_timer_expired(void *opaque) } } -static void npcm7xx_timer_hold_reset(Object *obj) +static void npcm7xx_timer_hold_reset(Object *obj, ResetType type) { NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); int i; diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index 222eef82a5..8cac9c0a06 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1305,7 +1305,7 @@ static void dwc2_reset_enter(Object *obj, ResetType type) } } -static void dwc2_reset_hold(Object *obj) +static void dwc2_reset_hold(Object *obj, ResetType type) { DWC2Class *c = DWC2_USB_GET_CLASS(obj); DWC2State *s = DWC2_USB(obj); @@ -1313,13 +1313,13 @@ static void dwc2_reset_hold(Object *obj) trace_usb_dwc2_reset_hold(); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } dwc2_update_irq(s); } -static void dwc2_reset_exit(Object *obj) +static void dwc2_reset_exit(Object *obj, ResetType type) { DWC2Class *c = DWC2_USB_GET_CLASS(obj); DWC2State *s = DWC2_USB(obj); @@ -1327,7 +1327,7 @@ static void dwc2_reset_exit(Object *obj) trace_usb_dwc2_reset_exit(); if (c->parent_phases.exit) { - c->parent_phases.exit(obj); + c->parent_phases.exit(obj, type); } s->hprt0 = HPRT0_PWR; diff --git a/hw/usb/xlnx-versal-usb2-ctrl-regs.c b/hw/usb/xlnx-versal-usb2-ctrl-regs.c index 6fc453817e..66c793a602 100644 --- a/hw/usb/xlnx-versal-usb2-ctrl-regs.c +++ b/hw/usb/xlnx-versal-usb2-ctrl-regs.c @@ -153,7 +153,7 @@ static void usb2_ctrl_regs_reset_init(Object *obj, ResetType type) } } -static void usb2_ctrl_regs_reset_hold(Object *obj) +static void usb2_ctrl_regs_reset_hold(Object *obj, ResetType type) { VersalUsb2CtrlRegs *s = XILINX_VERSAL_USB2_CTRL_REGS(obj); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index cb159fd078..b1d02f4b3d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2292,7 +2292,7 @@ static void virtio_pci_reset(DeviceState *qdev) } } -static void virtio_pci_bus_reset_hold(Object *obj) +static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) { PCIDevice *dev = PCI_DEVICE(obj); DeviceState *qdev = DEVICE(obj); diff --git a/include/hw/resettable.h b/include/hw/resettable.h index bdcd1276b6..3161e471c9 100644 --- a/include/hw/resettable.h +++ b/include/hw/resettable.h @@ -103,8 +103,8 @@ typedef enum ResetType { * the callback. */ typedef void (*ResettableEnterPhase)(Object *obj, ResetType type); -typedef void (*ResettableHoldPhase)(Object *obj); -typedef void (*ResettableExitPhase)(Object *obj); +typedef void (*ResettableHoldPhase)(Object *obj, ResetType type); +typedef void (*ResettableExitPhase)(Object *obj, ResetType type); typedef ResettableState * (*ResettableGetState)(Object *obj); typedef void (*ResettableTrFunction)(Object *obj); typedef ResettableTrFunction (*ResettableGetTrFunction)(Object *obj); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d2dfd36fd4..a152def241 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -220,7 +220,7 @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) assert(oldvalue == newvalue); } -static void arm_cpu_reset_hold(Object *obj) +static void arm_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); ARMCPU *cpu = ARM_CPU(cs); @@ -228,7 +228,7 @@ static void arm_cpu_reset_hold(Object *obj) CPUARMState *env = &cpu->env; if (acc->parent_phases.hold) { - acc->parent_phases.hold(obj); + acc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUARMState, end_reset_fields)); diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 45ee1b5f89..71ce62a4c2 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -66,7 +66,7 @@ static void avr_restore_state_to_opc(CPUState *cs, cpu_env(cs)->pc_w = data[0]; } -static void avr_cpu_reset_hold(Object *obj) +static void avr_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); AVRCPU *cpu = AVR_CPU(cs); @@ -74,7 +74,7 @@ static void avr_cpu_reset_hold(Object *obj) CPUAVRState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } env->pc_w = 0; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index eb4bddcb7e..535ec39c73 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -61,7 +61,7 @@ static int cris_cpu_mmu_index(CPUState *cs, bool ifetch) return !!(cpu_env(cs)->pregs[PR_CCS] & U_FLAG); } -static void cris_cpu_reset_hold(Object *obj) +static void cris_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(obj); @@ -69,7 +69,7 @@ static void cris_cpu_reset_hold(Object *obj) uint32_t vr; if (ccc->parent_phases.hold) { - ccc->parent_phases.hold(obj); + ccc->parent_phases.hold(obj, type); } vr = env->pregs[PR_VR]; diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3a716b9be3..a56bb4b075 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -273,14 +273,14 @@ static void hexagon_restore_state_to_opc(CPUState *cs, cpu_env(cs)->gpr[HEX_REG_PC] = data[0]; } -static void hexagon_cpu_reset_hold(Object *obj) +static void hexagon_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj); CPUHexagonState *env = cpu_env(cs); if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } set_default_nan_mode(1, &env->fp_status); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fd6af0d763..fa1ea3735d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6830,7 +6830,7 @@ static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) #endif } -static void x86_cpu_reset_hold(Object *obj) +static void x86_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); X86CPU *cpu = X86_CPU(cs); @@ -6841,7 +6841,7 @@ static void x86_cpu_reset_hold(Object *obj) int i; if (xcc->parent_phases.hold) { - xcc->parent_phases.hold(obj); + xcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUX86State, end_reset_fields)); diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 203a349055..bac84dca7a 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -495,14 +495,14 @@ static void loongarch_max_initfn(Object *obj) loongarch_la464_initfn(obj); } -static void loongarch_cpu_reset_hold(Object *obj) +static void loongarch_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj); CPULoongArchState *env = cpu_env(cs); if (lacc->parent_phases.hold) { - lacc->parent_phases.hold(obj); + lacc->parent_phases.hold(obj, type); } env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index df49ff1880..efd6bbded8 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -71,7 +71,7 @@ static void m68k_unset_feature(CPUM68KState *env, int feature) env->features &= ~BIT_ULL(feature); } -static void m68k_cpu_reset_hold(Object *obj) +static void m68k_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(obj); @@ -80,7 +80,7 @@ static void m68k_cpu_reset_hold(Object *obj) int i; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 96c2b71f7f..f8dc3173fc 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -181,7 +181,7 @@ static void microblaze_cpu_set_irq(void *opaque, int irq, int level) } #endif -static void mb_cpu_reset_hold(Object *obj) +static void mb_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); @@ -189,7 +189,7 @@ static void mb_cpu_reset_hold(Object *obj) CPUMBState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUMBState, end_reset_fields)); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 8d8f690a53..bbe01d07dd 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -185,7 +185,7 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) #include "cpu-defs.c.inc" -static void mips_cpu_reset_hold(Object *obj) +static void mips_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); MIPSCPU *cpu = MIPS_CPU(cs); @@ -193,7 +193,7 @@ static void mips_cpu_reset_hold(Object *obj) CPUMIPSState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 33c45dbf04..d711035cf5 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -85,14 +85,14 @@ static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) info->print_insn = print_insn_or1k; } -static void openrisc_cpu_reset_hold(Object *obj) +static void openrisc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); OpenRISCCPU *cpu = OPENRISC_CPU(cs); OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(obj); if (occ->parent_phases.hold) { - occ->parent_phases.hold(obj); + occ->parent_phases.hold(obj, type); } memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6241de62ce..6d82f24c87 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7136,7 +7136,7 @@ static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) return ppc_env_mmu_index(cpu_env(cs), ifetch); } -static void ppc_cpu_reset_hold(Object *obj) +static void ppc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -7146,7 +7146,7 @@ static void ppc_cpu_reset_hold(Object *obj) int i; if (pcc->parent_phases.hold) { - pcc->parent_phases.hold(obj); + pcc->parent_phases.hold(obj, type); } msr = (target_ulong)0; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 36e3e5fdaf..eb1a2e7d6d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -918,7 +918,7 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) return riscv_env_mmu_index(cpu_env(cs), ifetch); } -static void riscv_cpu_reset_hold(Object *obj) +static void riscv_cpu_reset_hold(Object *obj, ResetType type) { #ifndef CONFIG_USER_ONLY uint8_t iprio; @@ -930,7 +930,7 @@ static void riscv_cpu_reset_hold(Object *obj) CPURISCVState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } #ifndef CONFIG_USER_ONLY env->misa_mxl = mcc->misa_mxl_max; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index da673a595d..e3dfb09722 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -69,7 +69,7 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifunc) return 0; } -static void rx_cpu_reset_hold(Object *obj) +static void rx_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); RXCPUClass *rcc = RX_CPU_GET_CLASS(obj); @@ -77,7 +77,7 @@ static void rx_cpu_reset_hold(Object *obj) uint32_t *resetvec; if (rcc->parent_phases.hold) { - rcc->parent_phases.hold(obj); + rcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPURXState, end_reset_fields)); diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4f5a4a3d98..43e35ec2ca 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -103,14 +103,14 @@ static int sh4_cpu_mmu_index(CPUState *cs, bool ifetch) } } -static void superh_cpu_reset_hold(Object *obj) +static void superh_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(obj); CPUSH4State *env = cpu_env(cs); if (scc->parent_phases.hold) { - scc->parent_phases.hold(obj); + scc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index e820f50acf..485d416925 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -29,14 +29,14 @@ //#define DEBUG_FEATURES -static void sparc_cpu_reset_hold(Object *obj) +static void sparc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(obj); CPUSPARCState *env = cpu_env(cs); if (scc->parent_phases.hold) { - scc->parent_phases.hold(obj); + scc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index a9af73aeb5..8f9b72c3a0 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -58,13 +58,13 @@ static void tricore_restore_state_to_opc(CPUState *cs, cpu_env(cs)->PC = data[0]; } -static void tricore_cpu_reset_hold(Object *obj) +static void tricore_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(obj); if (tcc->parent_phases.hold) { - tcc->parent_phases.hold(obj); + tcc->parent_phases.hold(obj, type); } cpu_state_reset(cpu_env(cs)); diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 875cf843c9..de907cfeb1 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -93,7 +93,7 @@ bool xtensa_abi_call0(void) } #endif -static void xtensa_cpu_reset_hold(Object *obj) +static void xtensa_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(obj); @@ -102,7 +102,7 @@ static void xtensa_cpu_reset_hold(Object *obj) XTENSA_OPTION_DFP_COPROCESSOR); if (xcc->parent_phases.hold) { - xcc->parent_phases.hold(obj); + xcc->parent_phases.hold(obj, type); } env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; From 41d49ec190db9171d2ebb158fd4d5daad06ed8e1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:08 +0100 Subject: [PATCH 0188/2884] docs/devel/reset: Update to new API for hold and exit phase methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the reset documentation's example code to match the new API for the hold and exit phase method APIs where they take a ResetType argument. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-6-peter.maydell@linaro.org --- docs/devel/reset.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index 2ea85e7779..49baa1ea27 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -150,25 +150,25 @@ in reset. mydev->var = 0; } - static void mydev_reset_hold(Object *obj) + static void mydev_reset_hold(Object *obj, ResetType type) { MyDevClass *myclass = MYDEV_GET_CLASS(obj); MyDevState *mydev = MYDEV(obj); /* call parent class hold phase */ if (myclass->parent_phases.hold) { - myclass->parent_phases.hold(obj); + myclass->parent_phases.hold(obj, type); } /* set an IO */ qemu_set_irq(mydev->irq, 1); } - static void mydev_reset_exit(Object *obj) + static void mydev_reset_exit(Object *obj, ResetType type) { MyDevClass *myclass = MYDEV_GET_CLASS(obj); MyDevState *mydev = MYDEV(obj); /* call parent class exit phase */ if (myclass->parent_phases.exit) { - myclass->parent_phases.exit(obj); + myclass->parent_phases.exit(obj, type); } /* clear an IO */ qemu_set_irq(mydev->irq, 0); From 631f46d4ea7cb7ac0e529aacc1e7d832473f96c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:09 +0100 Subject: [PATCH 0189/2884] reset: Add RESET_TYPE_SNAPSHOT_LOAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices and machines need to handle the reset before a vmsave snapshot is loaded differently -- the main user is the handling of RNG seed information, which does not want to put a new RNG seed into a ROM blob when we are doing a snapshot load. Currently this kind of reset handling is supported only for: * TYPE_MACHINE reset methods, which take a ShutdownCause argument * reset functions registered with qemu_register_reset_nosnapshotload To allow a three-phase-reset device to also distinguish "snapshot load" reset from the normal kind, add a new ResetType RESET_TYPE_SNAPSHOT_LOAD. All our existing reset methods ignore the reset type, so we don't need to update any device code. Add the enum type, and make qemu_devices_reset() use the right reset type for the ShutdownCause it is passed. This allows us to get rid of the device_reset_reason global we were using to implement qemu_register_reset_nosnapshotload(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-7-peter.maydell@linaro.org --- docs/devel/reset.rst | 17 ++++++++++++++--- hw/core/reset.c | 15 ++++----------- hw/core/resettable.c | 4 ---- include/hw/resettable.h | 1 + 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index 49baa1ea27..9746a4e8a0 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -27,9 +27,7 @@ instantly reset an object, without keeping it in reset state, just call ``resettable_reset()``. These functions take two parameters: a pointer to the object to reset and a reset type. -Several types of reset will be supported. For now only cold reset is defined; -others may be added later. The Resettable interface handles reset types with an -enum: +The Resettable interface handles reset types with an enum ``ResetType``: ``RESET_TYPE_COLD`` Cold reset is supported by every resettable object. In QEMU, it means we reset @@ -37,6 +35,19 @@ enum: from what is a real hardware cold reset. It differs from other resets (like warm or bus resets) which may keep certain parts untouched. +``RESET_TYPE_SNAPSHOT_LOAD`` + This is called for a reset which is being done to put the system into a + clean state prior to loading a snapshot. (This corresponds to a reset + with ``SHUTDOWN_CAUSE_SNAPSHOT_LOAD``.) Almost all devices should treat + this the same as ``RESET_TYPE_COLD``. The main exception is devices which + have some non-deterministic state they want to reinitialize to a different + value on each cold reset, such as RNG seed information, and which they + must not reinitialize on a snapshot-load reset. + +Devices which implement reset methods must treat any unknown ``ResetType`` +as equivalent to ``RESET_TYPE_COLD``; this will reduce the amount of +existing code we need to change if we add more types in future. + Calling ``resettable_reset()`` is equivalent to calling ``resettable_assert_reset()`` then ``resettable_release_reset()``. It is possible to interleave multiple calls to these three functions. There may diff --git a/hw/core/reset.c b/hw/core/reset.c index f9fef45e05..58dfc8db3d 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -43,13 +43,6 @@ static ResettableContainer *get_root_reset_container(void) return root_reset_container; } -/* - * Reason why the currently in-progress qemu_devices_reset() was called. - * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding - * ResetType we could perhaps avoid the need for this global. - */ -static ShutdownCause device_reset_reason; - /* * This is an Object which implements Resettable simply to call the * callback function in the hold phase. @@ -77,8 +70,7 @@ static void legacy_reset_hold(Object *obj, ResetType type) { LegacyReset *lr = LEGACY_RESET(obj); - if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && - lr->skip_on_snapshot_load) { + if (type == RESET_TYPE_SNAPSHOT_LOAD && lr->skip_on_snapshot_load) { return; } lr->func(lr->opaque); @@ -180,8 +172,9 @@ void qemu_unregister_resettable(Object *obj) void qemu_devices_reset(ShutdownCause reason) { - device_reset_reason = reason; + ResetType type = (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD) ? + RESET_TYPE_SNAPSHOT_LOAD : RESET_TYPE_COLD; /* Reset the simulation */ - resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD); + resettable_reset(OBJECT(get_root_reset_container()), type); } diff --git a/hw/core/resettable.c b/hw/core/resettable.c index bebf7f10b2..6dd3e3dc48 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -48,8 +48,6 @@ void resettable_reset(Object *obj, ResetType type) void resettable_assert_reset(Object *obj, ResetType type) { - /* TODO: change this assert when adding support for other reset types */ - assert(type == RESET_TYPE_COLD); trace_resettable_reset_assert_begin(obj, type); assert(!enter_phase_in_progress); @@ -64,8 +62,6 @@ void resettable_assert_reset(Object *obj, ResetType type) void resettable_release_reset(Object *obj, ResetType type) { - /* TODO: change this assert when adding support for other reset types */ - assert(type == RESET_TYPE_COLD); trace_resettable_reset_release_begin(obj, type); assert(!enter_phase_in_progress); diff --git a/include/hw/resettable.h b/include/hw/resettable.h index 3161e471c9..7e249deb8b 100644 --- a/include/hw/resettable.h +++ b/include/hw/resettable.h @@ -35,6 +35,7 @@ typedef struct ResettableState ResettableState; */ typedef enum ResetType { RESET_TYPE_COLD, + RESET_TYPE_SNAPSHOT_LOAD, } ResetType; /* From 4fb37aea7e01679db44758eb9407d5a49e3dbd7a Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:43:58 +0100 Subject: [PATCH 0190/2884] hw/char: Implement STM32L4x5 USART skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the basic infrastructure (register read/write, type...) to implement the STM32L4x5 USART. Also create different types for the USART, UART and LPUART of the STM32L4x5 to deduplicate code and enable the implementation of different behaviors depending on the type. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-2-arnaud.minier@telecom-paris.fr [PMM: update to new reset hold method signature; fixed a few checkpatch nits] Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/stm32l4x5_usart.c | 396 ++++++++++++++++++++++++++++++ hw/char/trace-events | 4 + include/hw/char/stm32l4x5_usart.h | 66 +++++ 6 files changed, 471 insertions(+) create mode 100644 hw/char/stm32l4x5_usart.c create mode 100644 include/hw/char/stm32l4x5_usart.h diff --git a/MAINTAINERS b/MAINTAINERS index 8bb32f4a7e..d5a5181242 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1115,6 +1115,7 @@ M: Inès Varhol L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32l4x5_soc.c +F: hw/char/stm32l4x5_usart.c F: hw/misc/stm32l4x5_exti.c F: hw/misc/stm32l4x5_syscfg.c F: hw/misc/stm32l4x5_rcc.c diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 6b6cf2fc1d..4fd74ea878 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -41,6 +41,9 @@ config VIRTIO_SERIAL config STM32F2XX_USART bool +config STM32L4X5_USART + bool + config CMSDK_APB_UART bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 006d20f1e2..e5b13b6958 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -31,6 +31,7 @@ system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: files('stm32l4x5_usart.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c new file mode 100644 index 0000000000..62957ec3e5 --- /dev/null +++ b/hw/char/stm32l4x5_usart.c @@ -0,0 +1,396 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" +#include "migration/vmstate.h" +#include "hw/char/stm32l4x5_usart.h" +#include "hw/clock.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/registerfields.h" +#include "trace.h" + + +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) /* Word length (part 2, see M0) */ + FIELD(CR1, EOBIE, 27, 1) /* End of Block interrupt enable */ + FIELD(CR1, RTOIE, 26, 1) /* Receiver timeout interrupt enable */ + FIELD(CR1, DEAT, 21, 5) /* Driver Enable assertion time */ + FIELD(CR1, DEDT, 16, 5) /* Driver Enable de-assertion time */ + FIELD(CR1, OVER8, 15, 1) /* Oversampling mode */ + FIELD(CR1, CMIE, 14, 1) /* Character match interrupt enable */ + FIELD(CR1, MME, 13, 1) /* Mute mode enable */ + FIELD(CR1, M0, 12, 1) /* Word length (part 1, see M1) */ + FIELD(CR1, WAKE, 11, 1) /* Receiver wakeup method */ + FIELD(CR1, PCE, 10, 1) /* Parity control enable */ + FIELD(CR1, PS, 9, 1) /* Parity selection */ + FIELD(CR1, PEIE, 8, 1) /* PE interrupt enable */ + FIELD(CR1, TXEIE, 7, 1) /* TXE interrupt enable */ + FIELD(CR1, TCIE, 6, 1) /* Transmission complete interrupt enable */ + FIELD(CR1, RXNEIE, 5, 1) /* RXNE interrupt enable */ + FIELD(CR1, IDLEIE, 4, 1) /* IDLE interrupt enable */ + FIELD(CR1, TE, 3, 1) /* Transmitter enable */ + FIELD(CR1, RE, 2, 1) /* Receiver enable */ + FIELD(CR1, UESM, 1, 1) /* USART enable in Stop mode */ + FIELD(CR1, UE, 0, 1) /* USART enable */ +REG32(CR2, 0x04) + FIELD(CR2, ADD_1, 28, 4) /* ADD[7:4] */ + FIELD(CR2, ADD_0, 24, 1) /* ADD[3:0] */ + FIELD(CR2, RTOEN, 23, 1) /* Receiver timeout enable */ + FIELD(CR2, ABRMOD, 21, 2) /* Auto baud rate mode */ + FIELD(CR2, ABREN, 20, 1) /* Auto baud rate enable */ + FIELD(CR2, MSBFIRST, 19, 1) /* Most significant bit first */ + FIELD(CR2, DATAINV, 18, 1) /* Binary data inversion */ + FIELD(CR2, TXINV, 17, 1) /* TX pin active level inversion */ + FIELD(CR2, RXINV, 16, 1) /* RX pin active level inversion */ + FIELD(CR2, SWAP, 15, 1) /* Swap RX/TX pins */ + FIELD(CR2, LINEN, 14, 1) /* LIN mode enable */ + FIELD(CR2, STOP, 12, 2) /* STOP bits */ + FIELD(CR2, CLKEN, 11, 1) /* Clock enable */ + FIELD(CR2, CPOL, 10, 1) /* Clock polarity */ + FIELD(CR2, CPHA, 9, 1) /* Clock phase */ + FIELD(CR2, LBCL, 8, 1) /* Last bit clock pulse */ + FIELD(CR2, LBDIE, 6, 1) /* LIN break detection interrupt enable */ + FIELD(CR2, LBDL, 5, 1) /* LIN break detection length */ + FIELD(CR2, ADDM7, 4, 1) /* 7-bit / 4-bit Address Detection */ + +REG32(CR3, 0x08) + /* TCBGTIE only on STM32L496xx/4A6xx devices */ + FIELD(CR3, UCESM, 23, 1) /* USART Clock Enable in Stop Mode */ + FIELD(CR3, WUFIE, 22, 1) /* Wakeup from Stop mode interrupt enable */ + FIELD(CR3, WUS, 20, 2) /* Wakeup from Stop mode interrupt flag selection */ + FIELD(CR3, SCARCNT, 17, 3) /* Smartcard auto-retry count */ + FIELD(CR3, DEP, 15, 1) /* Driver enable polarity selection */ + FIELD(CR3, DEM, 14, 1) /* Driver enable mode */ + FIELD(CR3, DDRE, 13, 1) /* DMA Disable on Reception Error */ + FIELD(CR3, OVRDIS, 12, 1) /* Overrun Disable */ + FIELD(CR3, ONEBIT, 11, 1) /* One sample bit method enable */ + FIELD(CR3, CTSIE, 10, 1) /* CTS interrupt enable */ + FIELD(CR3, CTSE, 9, 1) /* CTS enable */ + FIELD(CR3, RTSE, 8, 1) /* RTS enable */ + FIELD(CR3, DMAT, 7, 1) /* DMA enable transmitter */ + FIELD(CR3, DMAR, 6, 1) /* DMA enable receiver */ + FIELD(CR3, SCEN, 5, 1) /* Smartcard mode enable */ + FIELD(CR3, NACK, 4, 1) /* Smartcard NACK enable */ + FIELD(CR3, HDSEL, 3, 1) /* Half-duplex selection */ + FIELD(CR3, IRLP, 2, 1) /* IrDA low-power */ + FIELD(CR3, IREN, 1, 1) /* IrDA mode enable */ + FIELD(CR3, EIE, 0, 1) /* Error interrupt enable */ +REG32(BRR, 0x0C) + FIELD(BRR, BRR, 0, 16) +REG32(GTPR, 0x10) + FIELD(GTPR, GT, 8, 8) /* Guard time value */ + FIELD(GTPR, PSC, 0, 8) /* Prescaler value */ +REG32(RTOR, 0x14) + FIELD(RTOR, BLEN, 24, 8) /* Block Length */ + FIELD(RTOR, RTO, 0, 24) /* Receiver timeout value */ +REG32(RQR, 0x18) + FIELD(RQR, TXFRQ, 4, 1) /* Transmit data flush request */ + FIELD(RQR, RXFRQ, 3, 1) /* Receive data flush request */ + FIELD(RQR, MMRQ, 2, 1) /* Mute mode request */ + FIELD(RQR, SBKRQ, 1, 1) /* Send break request */ + FIELD(RQR, ABBRRQ, 0, 1) /* Auto baud rate request */ +REG32(ISR, 0x1C) + /* TCBGT only for STM32L475xx/476xx/486xx devices */ + FIELD(ISR, REACK, 22, 1) /* Receive enable acknowledge flag */ + FIELD(ISR, TEACK, 21, 1) /* Transmit enable acknowledge flag */ + FIELD(ISR, WUF, 20, 1) /* Wakeup from Stop mode flag */ + FIELD(ISR, RWU, 19, 1) /* Receiver wakeup from Mute mode */ + FIELD(ISR, SBKF, 18, 1) /* Send break flag */ + FIELD(ISR, CMF, 17, 1) /* Character match flag */ + FIELD(ISR, BUSY, 16, 1) /* Busy flag */ + FIELD(ISR, ABRF, 15, 1) /* Auto Baud rate flag */ + FIELD(ISR, ABRE, 14, 1) /* Auto Baud rate error */ + FIELD(ISR, EOBF, 12, 1) /* End of block flag */ + FIELD(ISR, RTOF, 11, 1) /* Receiver timeout */ + FIELD(ISR, CTS, 10, 1) /* CTS flag */ + FIELD(ISR, CTSIF, 9, 1) /* CTS interrupt flag */ + FIELD(ISR, LBDF, 8, 1) /* LIN break detection flag */ + FIELD(ISR, TXE, 7, 1) /* Transmit data register empty */ + FIELD(ISR, TC, 6, 1) /* Transmission complete */ + FIELD(ISR, RXNE, 5, 1) /* Read data register not empty */ + FIELD(ISR, IDLE, 4, 1) /* Idle line detected */ + FIELD(ISR, ORE, 3, 1) /* Overrun error */ + FIELD(ISR, NF, 2, 1) /* START bit Noise detection flag */ + FIELD(ISR, FE, 1, 1) /* Framing Error */ + FIELD(ISR, PE, 0, 1) /* Parity Error */ +REG32(ICR, 0x20) + FIELD(ICR, WUCF, 20, 1) /* Wakeup from Stop mode clear flag */ + FIELD(ICR, CMCF, 17, 1) /* Character match clear flag */ + FIELD(ICR, EOBCF, 12, 1) /* End of block clear flag */ + FIELD(ICR, RTOCF, 11, 1) /* Receiver timeout clear flag */ + FIELD(ICR, CTSCF, 9, 1) /* CTS clear flag */ + FIELD(ICR, LBDCF, 8, 1) /* LIN break detection clear flag */ + /* TCBGTCF only on STM32L496xx/4A6xx devices */ + FIELD(ICR, TCCF, 6, 1) /* Transmission complete clear flag */ + FIELD(ICR, IDLECF, 4, 1) /* Idle line detected clear flag */ + FIELD(ICR, ORECF, 3, 1) /* Overrun error clear flag */ + FIELD(ICR, NCF, 2, 1) /* Noise detected clear flag */ + FIELD(ICR, FECF, 1, 1) /* Framing error clear flag */ + FIELD(ICR, PECF, 0, 1) /* Parity error clear flag */ +REG32(RDR, 0x24) + FIELD(RDR, RDR, 0, 9) +REG32(TDR, 0x28) + FIELD(TDR, TDR, 0, 9) + +static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); + + s->cr1 = 0x00000000; + s->cr2 = 0x00000000; + s->cr3 = 0x00000000; + s->brr = 0x00000000; + s->gtpr = 0x00000000; + s->rtor = 0x00000000; + s->isr = 0x020000C0; + s->rdr = 0x00000000; + s->tdr = 0x00000000; +} + +static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + uint64_t retvalue = 0; + + switch (addr) { + case A_CR1: + retvalue = s->cr1; + break; + case A_CR2: + retvalue = s->cr2; + break; + case A_CR3: + retvalue = s->cr3; + break; + case A_BRR: + retvalue = FIELD_EX32(s->brr, BRR, BRR); + break; + case A_GTPR: + retvalue = s->gtpr; + break; + case A_RTOR: + retvalue = s->rtor; + break; + case A_RQR: + /* RQR is a write only register */ + retvalue = 0x00000000; + break; + case A_ISR: + retvalue = s->isr; + break; + case A_ICR: + /* ICR is a clear register */ + retvalue = 0x00000000; + break; + case A_RDR: + retvalue = FIELD_EX32(s->rdr, RDR, RDR); + /* Reset RXNE flag */ + s->isr &= ~R_ISR_RXNE_MASK; + break; + case A_TDR: + retvalue = FIELD_EX32(s->tdr, TDR, TDR); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } + + trace_stm32l4x5_usart_read(addr, retvalue); + + return retvalue; +} + +static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + const uint32_t value = val64; + + trace_stm32l4x5_usart_write(addr, value); + + switch (addr) { + case A_CR1: + s->cr1 = value; + return; + case A_CR2: + s->cr2 = value; + return; + case A_CR3: + s->cr3 = value; + return; + case A_BRR: + s->brr = value; + return; + case A_GTPR: + s->gtpr = value; + return; + case A_RTOR: + s->rtor = value; + return; + case A_RQR: + return; + case A_ISR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ISR is read only !\n", __func__); + return; + case A_ICR: + /* Clear the status flags */ + s->isr &= ~value; + return; + case A_RDR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RDR is read only !\n", __func__); + return; + case A_TDR: + s->tdr = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_usart_base_ops = { + .read = stm32l4x5_usart_base_read, + .write = stm32l4x5_usart_base_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, + .impl = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, +}; + +static Property stm32l4x5_usart_base_properties[] = { + DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l4x5_usart_base_init(Object *obj) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_usart_base_ops, s, + TYPE_STM32L4X5_USART_BASE, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); +} + +static const VMStateDescription vmstate_stm32l4x5_usart_base = { + .name = TYPE_STM32L4X5_USART_BASE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr3, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(brr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(gtpr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rtor, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(isr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rdr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(tdr, Stm32l4x5UsartBaseState), + VMSTATE_CLOCK(clk, Stm32l4x5UsartBaseState), + VMSTATE_END_OF_LIST() + } +}; + + +static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(dev); + if (!clock_has_source(s->clk)) { + error_setg(errp, "USART clock must be wired up by SoC code"); + return; + } +} + +static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = stm32l4x5_usart_base_reset_hold; + device_class_set_props(dc, stm32l4x5_usart_base_properties); + dc->realize = stm32l4x5_usart_base_realize; + dc->vmsd = &vmstate_stm32l4x5_usart_base; +} + +static void stm32l4x5_usart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_USART; +} + +static void stm32l4x5_uart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_UART; +} + +static void stm32l4x5_lpuart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_LPUART; +} + +static const TypeInfo stm32l4x5_usart_types[] = { + { + .name = TYPE_STM32L4X5_USART_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5UsartBaseState), + .instance_init = stm32l4x5_usart_base_init, + .class_init = stm32l4x5_usart_base_class_init, + .abstract = true, + }, { + .name = TYPE_STM32L4X5_USART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_usart_class_init, + }, { + .name = TYPE_STM32L4X5_UART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_uart_class_init, + }, { + .name = TYPE_STM32L4X5_LPUART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_lpuart_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_usart_types) diff --git a/hw/char/trace-events b/hw/char/trace-events index 7a398c82a5..689bed9e02 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -106,6 +106,10 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u" sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 +# stm32l4x5_usart.c +stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64 "> -> 0x%" PRIx32 "" +stm32l4x5_usart_write(uint64_t addr, uint32_t data) "USART: Write <0x%" PRIx64 "> <- 0x%" PRIx32 "" + # xen_console.c xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" xen_console_disconnect(unsigned int idx) "idx %u" diff --git a/include/hw/char/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h new file mode 100644 index 0000000000..8d38a85a6e --- /dev/null +++ b/include/hw/char/stm32l4x5_usart.h @@ -0,0 +1,66 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#ifndef HW_STM32L4X5_USART_H +#define HW_STM32L4X5_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_USART_BASE "stm32l4x5-usart-base" +#define TYPE_STM32L4X5_USART "stm32l4x5-usart" +#define TYPE_STM32L4X5_UART "stm32l4x5-uart" +#define TYPE_STM32L4X5_LPUART "stm32l4x5-lpuart" +OBJECT_DECLARE_TYPE(Stm32l4x5UsartBaseState, Stm32l4x5UsartBaseClass, + STM32L4X5_USART_BASE) + +typedef enum { + STM32L4x5_USART, + STM32L4x5_UART, + STM32L4x5_LPUART, +} Stm32l4x5UsartType; + +struct Stm32l4x5UsartBaseState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t cr1; + uint32_t cr2; + uint32_t cr3; + uint32_t brr; + uint32_t gtpr; + uint32_t rtor; + /* rqr is write-only */ + uint32_t isr; + /* icr is a clear register */ + uint32_t rdr; + uint32_t tdr; + + Clock *clk; + CharBackend chr; + qemu_irq irq; +}; + +struct Stm32l4x5UsartBaseClass { + SysBusDeviceClass parent_class; + + Stm32l4x5UsartType type; +}; + +#endif /* HW_STM32L4X5_USART_H */ From 87b77e6e01cad9a6cbdc3532a5bb3c674e34d089 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:43:59 +0100 Subject: [PATCH 0191/2884] hw/char/stm32l4x5_usart: Enable serial read and write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the ability to read and write characters to the usart using the serial port. The character transmission is based on the cmsdk-apb-uart implementation. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-3-arnaud.minier@telecom-paris.fr [PMM: fixed a few checkpatch nits] Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 143 ++++++++++++++++++++++++++++++ hw/char/trace-events | 7 ++ include/hw/char/stm32l4x5_usart.h | 1 + 3 files changed, 151 insertions(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 62957ec3e5..755fc0bb5a 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -154,6 +154,123 @@ REG32(RDR, 0x24) REG32(TDR, 0x28) FIELD(TDR, TDR, 0, 9) +static void stm32l4x5_update_irq(Stm32l4x5UsartBaseState *s) +{ + if (((s->isr & R_ISR_WUF_MASK) && (s->cr3 & R_CR3_WUFIE_MASK)) || + ((s->isr & R_ISR_CMF_MASK) && (s->cr1 & R_CR1_CMIE_MASK)) || + ((s->isr & R_ISR_ABRF_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) || + ((s->isr & R_ISR_EOBF_MASK) && (s->cr1 & R_CR1_EOBIE_MASK)) || + ((s->isr & R_ISR_RTOF_MASK) && (s->cr1 & R_CR1_RTOIE_MASK)) || + ((s->isr & R_ISR_CTSIF_MASK) && (s->cr3 & R_CR3_CTSIE_MASK)) || + ((s->isr & R_ISR_LBDF_MASK) && (s->cr2 & R_CR2_LBDIE_MASK)) || + ((s->isr & R_ISR_TXE_MASK) && (s->cr1 & R_CR1_TXEIE_MASK)) || + ((s->isr & R_ISR_TC_MASK) && (s->cr1 & R_CR1_TCIE_MASK)) || + ((s->isr & R_ISR_RXNE_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) || + ((s->isr & R_ISR_IDLE_MASK) && (s->cr1 & R_CR1_IDLEIE_MASK)) || + ((s->isr & R_ISR_ORE_MASK) && + ((s->cr1 & R_CR1_RXNEIE_MASK) || (s->cr3 & R_CR3_EIE_MASK))) || + /* TODO: Handle NF ? */ + ((s->isr & R_ISR_FE_MASK) && (s->cr3 & R_CR3_EIE_MASK)) || + ((s->isr & R_ISR_PE_MASK) && (s->cr1 & R_CR1_PEIE_MASK))) { + qemu_irq_raise(s->irq); + trace_stm32l4x5_usart_irq_raised(s->isr); + } else { + qemu_irq_lower(s->irq); + trace_stm32l4x5_usart_irq_lowered(); + } +} + +static int stm32l4x5_usart_base_can_receive(void *opaque) +{ + Stm32l4x5UsartBaseState *s = opaque; + + if (!(s->isr & R_ISR_RXNE_MASK)) { + return 1; + } + + return 0; +} + +static void stm32l4x5_usart_base_receive(void *opaque, const uint8_t *buf, + int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + + if (!((s->cr1 & R_CR1_UE_MASK) && (s->cr1 & R_CR1_RE_MASK))) { + trace_stm32l4x5_usart_receiver_not_enabled( + FIELD_EX32(s->cr1, CR1, UE), FIELD_EX32(s->cr1, CR1, RE)); + return; + } + + /* Check if overrun detection is enabled and if there is an overrun */ + if (!(s->cr3 & R_CR3_OVRDIS_MASK) && (s->isr & R_ISR_RXNE_MASK)) { + /* + * A character has been received while + * the previous has not been read = Overrun. + */ + s->isr |= R_ISR_ORE_MASK; + trace_stm32l4x5_usart_overrun_detected(s->rdr, *buf); + } else { + /* No overrun */ + s->rdr = *buf; + s->isr |= R_ISR_RXNE_MASK; + trace_stm32l4x5_usart_rx(s->rdr); + } + + stm32l4x5_update_irq(s); +} + +/* + * Try to send tx data, and arrange to be called back later if + * we can't (ie the char backend is busy/blocking). + */ +static gboolean usart_transmit(void *do_not_use, GIOCondition cond, + void *opaque) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(opaque); + int ret; + /* TODO: Handle 9 bits transmission */ + uint8_t ch = s->tdr; + + s->watch_tag = 0; + + if (!(s->cr1 & R_CR1_TE_MASK) || (s->isr & R_ISR_TXE_MASK)) { + return G_SOURCE_REMOVE; + } + + ret = qemu_chr_fe_write(&s->chr, &ch, 1); + if (ret <= 0) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + usart_transmit, s); + if (!s->watch_tag) { + /* + * Most common reason to be here is "no chardev backend": + * just insta-drain the buffer, so the serial output + * goes into a void, rather than blocking the guest. + */ + goto buffer_drained; + } + /* Transmit pending */ + trace_stm32l4x5_usart_tx_pending(); + return G_SOURCE_REMOVE; + } + +buffer_drained: + /* Character successfully sent */ + trace_stm32l4x5_usart_tx(ch); + s->isr |= R_ISR_TC_MASK | R_ISR_TXE_MASK; + stm32l4x5_update_irq(s); + return G_SOURCE_REMOVE; +} + +static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s) +{ + if (s->watch_tag) { + g_source_remove(s->watch_tag); + s->watch_tag = 0; + } +} + static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) { Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); @@ -167,6 +284,22 @@ static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) s->isr = 0x020000C0; s->rdr = 0x00000000; s->tdr = 0x00000000; + + usart_cancel_transmit(s); + stm32l4x5_update_irq(s); +} + +static void usart_update_rqr(Stm32l4x5UsartBaseState *s, uint32_t value) +{ + /* TXFRQ */ + /* Reset RXNE flag */ + if (value & R_RQR_RXFRQ_MASK) { + s->isr &= ~R_ISR_RXNE_MASK; + } + /* MMRQ */ + /* SBKRQ */ + /* ABRRQ */ + stm32l4x5_update_irq(s); } static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, @@ -209,6 +342,7 @@ static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, retvalue = FIELD_EX32(s->rdr, RDR, RDR); /* Reset RXNE flag */ s->isr &= ~R_ISR_RXNE_MASK; + stm32l4x5_update_irq(s); break; case A_TDR: retvalue = FIELD_EX32(s->tdr, TDR, TDR); @@ -235,6 +369,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, switch (addr) { case A_CR1: s->cr1 = value; + stm32l4x5_update_irq(s); return; case A_CR2: s->cr2 = value; @@ -252,6 +387,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, s->rtor = value; return; case A_RQR: + usart_update_rqr(s, value); return; case A_ISR: qemu_log_mask(LOG_GUEST_ERROR, @@ -260,6 +396,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, case A_ICR: /* Clear the status flags */ s->isr &= ~value; + stm32l4x5_update_irq(s); return; case A_RDR: qemu_log_mask(LOG_GUEST_ERROR, @@ -267,6 +404,8 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, return; case A_TDR: s->tdr = value; + s->isr &= ~R_ISR_TXE_MASK; + usart_transmit(NULL, G_IO_OUT, s); return; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -336,6 +475,10 @@ static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) error_setg(errp, "USART clock must be wired up by SoC code"); return; } + + qemu_chr_fe_set_handlers(&s->chr, stm32l4x5_usart_base_can_receive, + stm32l4x5_usart_base_receive, NULL, NULL, + s, NULL, true); } static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/trace-events b/hw/char/trace-events index 689bed9e02..f22f0ee2bc 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -109,6 +109,13 @@ sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size % # stm32l4x5_usart.c stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64 "> -> 0x%" PRIx32 "" stm32l4x5_usart_write(uint64_t addr, uint32_t data) "USART: Write <0x%" PRIx64 "> <- 0x%" PRIx32 "" +stm32l4x5_usart_rx(uint8_t c) "USART: got character 0x%x from backend" +stm32l4x5_usart_tx(uint8_t c) "USART: character 0x%x sent to backend" +stm32l4x5_usart_tx_pending(void) "USART: character send to backend pending" +stm32l4x5_usart_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32 +stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered" +stm32l4x5_usart_overrun_detected(uint8_t current, uint8_t received) "USART: Overrun detected, RDR='0x%x', received 0x%x" +stm32l4x5_usart_receiver_not_enabled(uint8_t ue_bit, uint8_t re_bit) "USART: Receiver not enabled, UE=0x%x, RE=0x%x" # xen_console.c xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" diff --git a/include/hw/char/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h index 8d38a85a6e..dd3866682a 100644 --- a/include/hw/char/stm32l4x5_usart.h +++ b/include/hw/char/stm32l4x5_usart.h @@ -55,6 +55,7 @@ struct Stm32l4x5UsartBaseState { Clock *clk; CharBackend chr; qemu_irq irq; + guint watch_tag; }; struct Stm32l4x5UsartBaseClass { From c4c12ee48709aeb896ebb60163e8eb26cdaa3d65 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:00 +0100 Subject: [PATCH 0192/2884] hw/char/stm32l4x5_usart: Add options for serial parameters setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to change the settings of the serial connection. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-4-arnaud.minier@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 98 +++++++++++++++++++++++++++++++++++++++ hw/char/trace-events | 1 + 2 files changed, 99 insertions(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 755fc0bb5a..2627aab832 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -271,6 +271,92 @@ static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s) } } +static void stm32l4x5_update_params(Stm32l4x5UsartBaseState *s) +{ + int speed, parity, data_bits, stop_bits; + uint32_t value, usart_div; + QEMUSerialSetParams ssp; + + /* Select the parity type */ + if (s->cr1 & R_CR1_PCE_MASK) { + if (s->cr1 & R_CR1_PS_MASK) { + parity = 'O'; + } else { + parity = 'E'; + } + } else { + parity = 'N'; + } + + /* Select the number of stop bits */ + switch (FIELD_EX32(s->cr2, CR2, STOP)) { + case 0: + stop_bits = 1; + break; + case 2: + stop_bits = 2; + break; + default: + qemu_log_mask(LOG_UNIMP, + "UNIMPLEMENTED: fractionnal stop bits; CR2[13:12] = %u", + FIELD_EX32(s->cr2, CR2, STOP)); + return; + } + + /* Select the length of the word */ + switch ((FIELD_EX32(s->cr1, CR1, M1) << 1) | FIELD_EX32(s->cr1, CR1, M0)) { + case 0: + data_bits = 8; + break; + case 1: + data_bits = 9; + break; + case 2: + data_bits = 7; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: invalid word length, CR1.M = 0b11"); + return; + } + + /* Select the baud rate */ + value = FIELD_EX32(s->brr, BRR, BRR); + if (value < 16) { + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: BRR less than 16: %u", value); + return; + } + + if (FIELD_EX32(s->cr1, CR1, OVER8) == 0) { + /* + * Oversampling by 16 + * BRR = USARTDIV + */ + usart_div = value; + } else { + /* + * Oversampling by 8 + * - BRR[2:0] = USARTDIV[3:0] shifted 1 bit to the right. + * - BRR[3] must be kept cleared. + * - BRR[15:4] = USARTDIV[15:4] + * - The frequency is multiplied by 2 + */ + usart_div = ((value & 0xFFF0) | ((value & 0x0007) << 1)) / 2; + } + + speed = clock_get_hz(s->clk) / usart_div; + + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + + trace_stm32l4x5_usart_update_params(speed, parity, data_bits, stop_bits); +} + static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) { Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); @@ -369,16 +455,19 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, switch (addr) { case A_CR1: s->cr1 = value; + stm32l4x5_update_params(s); stm32l4x5_update_irq(s); return; case A_CR2: s->cr2 = value; + stm32l4x5_update_params(s); return; case A_CR3: s->cr3 = value; return; case A_BRR: s->brr = value; + stm32l4x5_update_params(s); return; case A_GTPR: s->gtpr = value; @@ -447,10 +536,19 @@ static void stm32l4x5_usart_base_init(Object *obj) s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); } +static int stm32l4x5_usart_base_post_load(void *opaque, int version_id) +{ + Stm32l4x5UsartBaseState *s = (Stm32l4x5UsartBaseState *)opaque; + + stm32l4x5_update_params(s); + return 0; +} + static const VMStateDescription vmstate_stm32l4x5_usart_base = { .name = TYPE_STM32L4X5_USART_BASE, .version_id = 1, .minimum_version_id = 1, + .post_load = stm32l4x5_usart_base_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), diff --git a/hw/char/trace-events b/hw/char/trace-events index f22f0ee2bc..8875758076 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -116,6 +116,7 @@ stm32l4x5_usart_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32 stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered" stm32l4x5_usart_overrun_detected(uint8_t current, uint8_t received) "USART: Overrun detected, RDR='0x%x', received 0x%x" stm32l4x5_usart_receiver_not_enabled(uint8_t ue_bit, uint8_t re_bit) "USART: Receiver not enabled, UE=0x%x, RE=0x%x" +stm32l4x5_usart_update_params(int speed, uint8_t parity, int data, int stop) "USART: speed: %d, parity: %c, data bits: %d, stop bits: %d" # xen_console.c xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" From 92741432ed6f98ab01bdace5baaf2894c7855563 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:01 +0100 Subject: [PATCH 0193/2884] hw/arm: Add the USART to the stm32l4x5 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the USART to the SoC and connect it to the other implemented devices. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-5-arnaud.minier@telecom-paris.fr [PMM: fixed a few checkpatch nits] Signed-off-by: Peter Maydell --- docs/system/arm/b-l475e-iot01a.rst | 2 +- hw/arm/Kconfig | 1 + hw/arm/stm32l4x5_soc.c | 83 +++++++++++++++++++++++++++--- include/hw/arm/stm32l4x5_soc.h | 7 +++ 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst index 0afef8e4f4..a76c9976c5 100644 --- a/docs/system/arm/b-l475e-iot01a.rst +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -19,13 +19,13 @@ Currently B-L475E-IOT01A machine's only supports the following devices: - STM32L4x5 SYSCFG (System configuration controller) - STM32L4x5 RCC (Reset and clock control) - STM32L4x5 GPIOs (General-purpose I/Os) +- STM32L4x5 USARTs, UARTs and LPUART (Serial ports) Missing devices """"""""""""""" The B-L475E-IOT01A does *not* support the following devices: -- Serial ports (UART) - Analog to Digital Converter (ADC) - SPI controller - Timer controller (TIMER) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 893a7bff66..098d043375 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -477,6 +477,7 @@ config STM32L4X5_SOC select STM32L4X5_SYSCFG select STM32L4X5_RCC select STM32L4X5_GPIO + select STM32L4X5_USART config XLNX_ZYNQMP_ARM bool diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 40e294f838..39924822f3 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -28,6 +28,7 @@ #include "sysemu/sysemu.h" #include "hw/or-irq.h" #include "hw/arm/stm32l4x5_soc.h" +#include "hw/char/stm32l4x5_usart.h" #include "hw/gpio/stm32l4x5_gpio.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" @@ -116,6 +117,22 @@ static const struct { { 0x48001C00, 0x0000000F, 0x00000000, 0x00000000 }, }; +static const hwaddr usart_addr[] = { + 0x40013800, /* "USART1", 0x400 */ + 0x40004400, /* "USART2", 0x400 */ + 0x40004800, /* "USART3", 0x400 */ +}; +static const hwaddr uart_addr[] = { + 0x40004C00, /* "UART4" , 0x400 */ + 0x40005000 /* "UART5" , 0x400 */ +}; + +#define LPUART_BASE_ADDRESS 0x40008000 + +static const int usart_irq[] = { 37, 38, 39 }; +static const int uart_irq[] = { 52, 53 }; +#define LPUART_IRQ 70 + static void stm32l4x5_soc_initfn(Object *obj) { Stm32l4x5SocState *s = STM32L4X5_SOC(obj); @@ -132,6 +149,18 @@ static void stm32l4x5_soc_initfn(Object *obj) g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i); object_initialize_child(obj, name, &s->gpio[i], TYPE_STM32L4X5_GPIO); } + + for (int i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32L4X5_USART); + } + + for (int i = 0; i < STM_NUM_UARTS; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], + TYPE_STM32L4X5_UART); + } + object_initialize_child(obj, "lpuart1", &s->lpuart, + TYPE_STM32L4X5_LPUART); } static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) @@ -279,6 +308,54 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(busdev, 0, RCC_BASE_ADDRESS); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, RCC_IRQ)); + /* USART devices */ + for (int i = 0; i < STM_NUM_USARTS; i++) { + g_autofree char *name = g_strdup_printf("usart%d-out", i + 1); + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + + /* + * TODO: Connect the USARTs, UARTs and LPUART to the EXTI once the EXTI + * can handle other gpio-in than the gpios. (e.g. Direct Lines for the + * usarts) + */ + + /* UART devices */ + for (int i = 0; i < STM_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d-out", STM_NUM_USARTS + i + 1); + dev = DEVICE(&(s->uart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + i)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, uart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, uart_irq[i])); + } + + /* LPUART device*/ + dev = DEVICE(&(s->lpuart)); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + STM_NUM_UARTS)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "lpuart1-out")); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, LPUART_BASE_ADDRESS); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, LPUART_IRQ)); + /* APB1 BUS */ create_unimplemented_device("TIM2", 0x40000000, 0x400); create_unimplemented_device("TIM3", 0x40000400, 0x400); @@ -294,10 +371,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("SPI2", 0x40003800, 0x400); create_unimplemented_device("SPI3", 0x40003C00, 0x400); /* RESERVED: 0x40004000, 0x400 */ - create_unimplemented_device("USART2", 0x40004400, 0x400); - create_unimplemented_device("USART3", 0x40004800, 0x400); - create_unimplemented_device("UART4", 0x40004C00, 0x400); - create_unimplemented_device("UART5", 0x40005000, 0x400); create_unimplemented_device("I2C1", 0x40005400, 0x400); create_unimplemented_device("I2C2", 0x40005800, 0x400); create_unimplemented_device("I2C3", 0x40005C00, 0x400); @@ -308,7 +381,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("DAC1", 0x40007400, 0x400); create_unimplemented_device("OPAMP", 0x40007800, 0x400); create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); - create_unimplemented_device("LPUART1", 0x40008000, 0x400); /* RESERVED: 0x40008400, 0x400 */ create_unimplemented_device("SWPMI1", 0x40008800, 0x400); /* RESERVED: 0x40008C00, 0x800 */ @@ -325,7 +397,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("TIM1", 0x40012C00, 0x400); create_unimplemented_device("SPI1", 0x40013000, 0x400); create_unimplemented_device("TIM8", 0x40013400, 0x400); - create_unimplemented_device("USART1", 0x40013800, 0x400); /* RESERVED: 0x40013C00, 0x400 */ create_unimplemented_device("TIM15", 0x40014000, 0x400); create_unimplemented_device("TIM16", 0x40014400, 0x400); diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index ee5f362405..c243fb0e7f 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -31,6 +31,7 @@ #include "hw/misc/stm32l4x5_exti.h" #include "hw/misc/stm32l4x5_rcc.h" #include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/char/stm32l4x5_usart.h" #include "qom/object.h" #define TYPE_STM32L4X5_SOC "stm32l4x5-soc" @@ -41,6 +42,9 @@ OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC) #define NUM_EXTI_OR_GATES 4 +#define STM_NUM_USARTS 3 +#define STM_NUM_UARTS 2 + struct Stm32l4x5SocState { SysBusDevice parent_obj; @@ -51,6 +55,9 @@ struct Stm32l4x5SocState { Stm32l4x5SyscfgState syscfg; Stm32l4x5RccState rcc; Stm32l4x5GpioState gpio[NUM_GPIOS]; + Stm32l4x5UsartBaseState usart[STM_NUM_USARTS]; + Stm32l4x5UsartBaseState uart[STM_NUM_UARTS]; + Stm32l4x5UsartBaseState lpuart; MemoryRegion sram1; MemoryRegion sram2; From 214652da123e3821657a64691ee556281e9f6238 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:02 +0100 Subject: [PATCH 0194/2884] tests/qtest: Add tests for the STM32L4x5 USART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: - read/write from/to the usart registers - send/receive a character/string over the serial port Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-6-arnaud.minier@telecom-paris.fr [PMM: fix checkpatch nits, remove commented out code] Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 4 +- tests/qtest/stm32l4x5_usart-test.c | 315 +++++++++++++++++++++++++++++ 2 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/stm32l4x5_usart-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 36c5c13a7b..b128fa5a4b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -7,6 +7,7 @@ slow_qtests = { 'npcm7xx_pwm-test': 300, 'npcm7xx_watchdog_timer-test': 120, 'qom-test' : 900, + 'stm32l4x5_usart-test' : 600, 'test-hmp' : 240, 'pxe-test': 610, 'prom-env-test': 360, @@ -205,7 +206,8 @@ qtests_stm32l4x5 = \ ['stm32l4x5_exti-test', 'stm32l4x5_syscfg-test', 'stm32l4x5_rcc-test', - 'stm32l4x5_gpio-test'] + 'stm32l4x5_gpio-test', + 'stm32l4x5_usart-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c new file mode 100644 index 0000000000..8902518233 --- /dev/null +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -0,0 +1,315 @@ +/* + * QTest testcase for STML4X5_USART + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/misc/stm32l4x5_rcc_internals.h" +#include "hw/registerfields.h" + +#define RCC_BASE_ADDR 0x40021000 +/* Use USART 1 ADDR, assume the others work the same */ +#define USART1_BASE_ADDR 0x40013800 + +/* See stm32l4x5_usart for definitions */ +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) + FIELD(CR1, OVER8, 15, 1) + FIELD(CR1, M0, 12, 1) + FIELD(CR1, PCE, 10, 1) + FIELD(CR1, TXEIE, 7, 1) + FIELD(CR1, RXNEIE, 5, 1) + FIELD(CR1, TE, 3, 1) + FIELD(CR1, RE, 2, 1) + FIELD(CR1, UE, 0, 1) +REG32(CR2, 0x04) +REG32(CR3, 0x08) + FIELD(CR3, OVRDIS, 12, 1) +REG32(BRR, 0x0C) +REG32(GTPR, 0x10) +REG32(RTOR, 0x14) +REG32(RQR, 0x18) +REG32(ISR, 0x1C) + FIELD(ISR, TXE, 7, 1) + FIELD(ISR, RXNE, 5, 1) + FIELD(ISR, ORE, 3, 1) +REG32(ICR, 0x20) +REG32(RDR, 0x24) +REG32(TDR, 0x28) + +#define NVIC_ISPR1 0XE000E204 +#define NVIC_ICPR1 0xE000E284 +#define USART1_IRQ 37 + +static bool check_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + assert(n > 32); + n -= 32; + return qtest_readl(qts, NVIC_ISPR1) & (1 << n); +} + +static bool clear_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + assert(n > 32); + n -= 32; + qtest_writel(qts, NVIC_ICPR1, (1 << n)); + return true; +} + +/* + * Wait indefinitely for the flag to be updated. + * If this is run on a slow CI runner, + * the meson harness will timeout after 10 minutes for us. + */ +static bool usart_wait_for_flag(QTestState *qts, uint32_t event_addr, + uint32_t flag) +{ + while (true) { + if ((qtest_readl(qts, event_addr) & flag)) { + return true; + } + g_usleep(1000); + } + + return false; +} + +static void usart_receive_string(QTestState *qts, int sock_fd, const char *in, + char *out) +{ + int i, in_len = strlen(in); + + g_assert_true(send(sock_fd, in, in_len, 0) == in_len); + for (i = 0; i < in_len; i++) { + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK)); + out[i] = qtest_readl(qts, USART1_BASE_ADDR + A_RDR); + } + out[i] = '\0'; +} + +static void usart_send_string(QTestState *qts, const char *in) +{ + int i, in_len = strlen(in); + + for (i = 0; i < in_len; i++) { + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, in[i]); + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_TXE_MASK)); + } +} + +/* Init the RCC clocks to run at 80 MHz */ +static void init_clocks(QTestState *qts) +{ + uint32_t value; + + /* MSIRANGE can be set only when MSI is OFF or READY */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), R_CR_MSION_MASK); + + /* Clocking from MSI, in case MSI was not the default source */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* + * Update PLL and set MSI as the source clock. + * PLLM = 1 --> 000 + * PLLN = 40 --> 40 + * PPLLR = 2 --> 00 + * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1) + * SRC = MSI --> 01 + */ + qtest_writel(qts, (RCC_BASE_ADDR + A_PLLCFGR), R_PLLCFGR_PLLREN_MASK | + (40 << R_PLLCFGR_PLLN_SHIFT) | + (0b01 << R_PLLCFGR_PLLSRC_SHIFT)); + + /* PLL activation */ + + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), value | R_CR_PLLON_MASK); + + /* RCC_CFGR is OK by defaut */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* CCIPR : no periph clock by default */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Switches on the PLL clock source */ + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CFGR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), (value & ~R_CFGR_SW_MASK) | + (0b11 << R_CFGR_SW_SHIFT)); + + /* Enable SYSCFG clock enabled */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), R_APB2ENR_SYSCFGEN_MASK); + + /* Enable the IO port B clock (See p.252) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_AHB2ENR), R_AHB2ENR_GPIOBEN_MASK); + + /* Enable the clock for USART1 (cf p.259) */ + /* We rewrite SYSCFGEN to not disable it */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), + R_APB2ENR_SYSCFGEN_MASK | R_APB2ENR_USART1EN_MASK); + + /* TODO: Enable usart via gpio */ + + /* Set PCLK as the clock for USART1(cf p.272) i.e. reset both bits */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Reset USART1 (see p.249) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 1 << 14); + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 0); +} + +static void init_uart(QTestState *qts) +{ + uint32_t cr1; + + init_clocks(qts); + + /* + * For 115200 bauds, see p.1349. + * The clock has a frequency of 80Mhz, + * for 115200, we have to put a divider of 695 = 0x2B7. + */ + qtest_writel(qts, (USART1_BASE_ADDR + A_BRR), 0x2B7); + + /* + * Set the oversampling by 16, + * disable the parity control and + * set the word length to 8. (cf p.1377) + */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 &= ~(R_CR1_M1_MASK | R_CR1_M0_MASK | R_CR1_OVER8_MASK | R_CR1_PCE_MASK); + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), cr1); + + /* Enable the transmitter, the receiver and the USART. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); +} + +static void test_write_read(void) +{ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + /* Test that we can write and retrieve a value from the device */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 0xFFFFFFFF); + const uint32_t tdr = qtest_readl(qts, USART1_BASE_ADDR + A_TDR); + g_assert_cmpuint(tdr, ==, 0x000001FF); +} + +static void test_receive_char(void) +{ + int sock_fd; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + g_assert_true(send(sock_fd, "a", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'a'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_RXNEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + g_assert_true(send(sock_fd, "b", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'b'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_char(void) +{ + int sock_fd; + char s[1]; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'c'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'c'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_TXEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'd'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'd'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_receive_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_receive_string(qts, sock_fd, "hello", s); + g_assert_true(memcmp(s, "hello", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_send_string(qts, "world"); + g_assert_true(recv(sock_fd, s, 10, 0) == 5); + g_assert_true(memcmp(s, "world", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("stm32l4x5/usart/write_read", test_write_read); + qtest_add_func("stm32l4x5/usart/receive_char", test_receive_char); + qtest_add_func("stm32l4x5/usart/send_char", test_send_char); + qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); + qtest_add_func("stm32l4x5/usart/send_str", test_send_str); + ret = g_test_run(); + + return ret; +} + From a6ab7a98c9a8106cb8ef86cbc88a27fe69963423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Apr 2024 13:15:44 +0200 Subject: [PATCH 0195/2884] hw/misc/applesmc: Simplify DeviceReset handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have applesmc_find_key() return a const pointer. Since the returned buffers are not modified in applesmc_io_data_write(), it is pointless to delete and re-add the keys in the DeviceReset handler. Add them once in DeviceRealize, and discard them in the DeviceUnrealize handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20240410180819.92332-1-philmd@linaro.org> --- hw/misc/applesmc.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 14e3ef667d..59a4899312 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -145,7 +145,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, s->data_pos = 0; } -static struct AppleSMCData *applesmc_find_key(AppleSMCState *s) +static const struct AppleSMCData *applesmc_find_key(AppleSMCState *s) { struct AppleSMCData *d; @@ -161,7 +161,7 @@ static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { AppleSMCState *s = opaque; - struct AppleSMCData *d; + const struct AppleSMCData *d; smc_debug("DATA received: 0x%02x\n", (uint8_t)val); switch (s->cmd) { @@ -269,23 +269,10 @@ static void applesmc_add_key(AppleSMCState *s, const char *key, static void qdev_applesmc_isa_reset(DeviceState *dev) { AppleSMCState *s = APPLE_SMC(dev); - struct AppleSMCData *d, *next; - /* Remove existing entries */ - QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { - QLIST_REMOVE(d, node); - g_free(d); - } s->status = 0x00; s->status_1e = 0x00; s->last_ret = 0x00; - - applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); - applesmc_add_key(s, "OSK0", 32, s->osk); - applesmc_add_key(s, "OSK1", 32, s->osk + 32); - applesmc_add_key(s, "NATJ", 1, "\0"); - applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); } static const MemoryRegionOps applesmc_data_io_ops = { @@ -343,6 +330,24 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) } QLIST_INIT(&s->data_def); + applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); + applesmc_add_key(s, "OSK0", 32, s->osk); + applesmc_add_key(s, "OSK1", 32, s->osk + 32); + applesmc_add_key(s, "NATJ", 1, "\0"); + applesmc_add_key(s, "MSSP", 1, "\0"); + applesmc_add_key(s, "MSSD", 1, "\0x3"); +} + +static void applesmc_unrealize(DeviceState *dev) +{ + AppleSMCState *s = APPLE_SMC(dev); + struct AppleSMCData *d, *next; + + /* Remove existing entries */ + QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { + QLIST_REMOVE(d, node); + g_free(d); + } } static Property applesmc_isa_properties[] = { @@ -377,6 +382,7 @@ static void qdev_applesmc_class_init(ObjectClass *klass, void *data) AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = applesmc_isa_realize; + dc->unrealize = applesmc_unrealize; dc->reset = qdev_applesmc_isa_reset; device_class_set_props(dc, applesmc_isa_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); From ca4af17c5e4a55b73850163dfc64a7d3c69378be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Apr 2024 12:31:14 +0200 Subject: [PATCH 0196/2884] hw/misc/imx: Replace sprintf() by snprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1, resulting in painful developer experience. Use snprintf() instead. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20240411104340.6617-6-philmd@linaro.org> Signed-off-by: Richard Henderson --- hw/misc/imx25_ccm.c | 2 +- hw/misc/imx31_ccm.c | 2 +- hw/misc/imx6_ccm.c | 4 ++-- hw/misc/imx6_src.c | 2 +- hw/misc/imx6ul_ccm.c | 4 ++-- hw/misc/imx7_src.c | 2 +- hw/net/imx_fec.c | 2 +- hw/ssi/imx_spi.c | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c index d888966014..faa726a86a 100644 --- a/hw/misc/imx25_ccm.c +++ b/hw/misc/imx25_ccm.c @@ -91,7 +91,7 @@ static const char *imx25_ccm_reg_name(uint32_t reg) case IMX25_CCM_LPIMR1_REG: return "lpimr1"; default: - sprintf(unknown, "[%u ?]", reg); + snprintf(unknown, sizeof(unknown), "[%u ?]", reg); return unknown; } } diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c index a9059bb1f7..125d4fceeb 100644 --- a/hw/misc/imx31_ccm.c +++ b/hw/misc/imx31_ccm.c @@ -89,7 +89,7 @@ static const char *imx31_ccm_reg_name(uint32_t reg) case IMX31_CCM_PDR2_REG: return "PDR2"; default: - sprintf(unknown, "[%u ?]", reg); + snprintf(unknown, sizeof(unknown), "[%u ?]", reg); return unknown; } } diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 56489d8b57..b1def7f05b 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -85,7 +85,7 @@ static const char *imx6_ccm_reg_name(uint32_t reg) case CCM_CMEOR: return "CMEOR"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -224,7 +224,7 @@ static const char *imx6_analog_reg_name(uint32_t reg) case USB_ANALOG_DIGPROG: return "USB_ANALOG_DIGPROG"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c index 0c6003559f..3766bdf561 100644 --- a/hw/misc/imx6_src.c +++ b/hw/misc/imx6_src.c @@ -68,7 +68,7 @@ static const char *imx6_src_reg_name(uint32_t reg) case SRC_GPR10: return "SRC_GPR10"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } diff --git a/hw/misc/imx6ul_ccm.c b/hw/misc/imx6ul_ccm.c index bbc0be9921..0ac49ea34b 100644 --- a/hw/misc/imx6ul_ccm.c +++ b/hw/misc/imx6ul_ccm.c @@ -143,7 +143,7 @@ static const char *imx6ul_ccm_reg_name(uint32_t reg) case CCM_CMEOR: return "CMEOR"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -274,7 +274,7 @@ static const char *imx6ul_analog_reg_name(uint32_t reg) case USB_ANALOG_DIGPROG: return "USB_ANALOG_DIGPROG"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } diff --git a/hw/misc/imx7_src.c b/hw/misc/imx7_src.c index b3725ff6e7..d19f0450d4 100644 --- a/hw/misc/imx7_src.c +++ b/hw/misc/imx7_src.c @@ -75,7 +75,7 @@ static const char *imx7_src_reg_name(uint32_t reg) case SRC_GPR10: return "SRC_GPR10"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index cee84af7ba..8c91d20d44 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -41,7 +41,7 @@ static const char *imx_default_reg_name(IMXFECState *s, uint32_t index) { static char tmp[20]; - sprintf(tmp, "index %d", index); + snprintf(tmp, sizeof(tmp), "index %d", index); return tmp; } diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index d8a7583ff3..12d897d306 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -53,7 +53,7 @@ static const char *imx_spi_reg_name(uint32_t reg) case ECSPI_MSGDATA: return "ECSPI_MSGDATA"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } From b8ff846ec88513112008bff3a4001c839fd50f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Apr 2024 12:33:31 +0200 Subject: [PATCH 0197/2884] hw/riscv/virt: Replace sprintf by g_strdup_printf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Use g_strdup_printf instead. Signed-off-by: Philippe Mathieu-Daudé [rth: Use g_strdup_printf] Signed-off-by: Richard Henderson Message-Id: <20240412073346.458116-26-richard.henderson@linaro.org> --- hw/riscv/virt.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index d171e74f7b..4fdb660525 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1617,10 +1617,8 @@ static void virt_machine_instance_init(Object *obj) static char *virt_get_aia_guests(Object *obj, Error **errp) { RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); - char val[32]; - sprintf(val, "%d", s->aia_guests); - return g_strdup(val); + return g_strdup_printf("%d", s->aia_guests); } static void virt_set_aia_guests(Object *obj, const char *val, Error **errp) @@ -1741,7 +1739,6 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, static void virt_machine_class_init(ObjectClass *oc, void *data) { - char str[128]; MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -1767,7 +1764,6 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif - object_class_property_add_bool(oc, "aclint", virt_get_aclint, virt_set_aclint); object_class_property_set_description(oc, "aclint", @@ -1785,9 +1781,14 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_str(oc, "aia-guests", virt_get_aia_guests, virt_set_aia_guests); - sprintf(str, "Set number of guest MMIO pages for AIA IMSIC. Valid value " - "should be between 0 and %d.", VIRT_IRQCHIP_MAX_GUESTS); - object_class_property_set_description(oc, "aia-guests", str); + { + g_autofree char *str = + g_strdup_printf("Set number of guest MMIO pages for AIA IMSIC. " + "Valid value should be between 0 and %d.", + VIRT_IRQCHIP_MAX_GUESTS); + object_class_property_set_description(oc, "aia-guests", str); + } + object_class_property_add(oc, "acpi", "OnOffAuto", virt_get_acpi, virt_set_acpi, NULL, NULL); From c1c350dc2ccbf92524754694547909e1455e4eef Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 15 Apr 2024 08:56:54 +0200 Subject: [PATCH 0198/2884] hw: Fix problem with the A*MPCORE switches in the Kconfig files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A9MPCORE, ARM11MPCORE and A15MPCORE are defined twice, once in hw/cpu/Kconfig and once in hw/arm/Kconfig. This is only possible by accident, since hw/cpu/Kconfig is never included from hw/Kconfig. Fix it by declaring the switches only in hw/cpu/Kconfig (since the related files reside in the hw/cpu/ folder) and by making sure that the file hw/cpu/Kconfig is now properly included from hw/Kconfig. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20240415065655.130099-2-thuth@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé --- hw/Kconfig | 1 + hw/arm/Kconfig | 15 --------------- hw/cpu/Kconfig | 12 +++++++++--- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/hw/Kconfig b/hw/Kconfig index b1cc40d6be..f7866e76f7 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -47,6 +47,7 @@ source watchdog/Kconfig # arch Kconfig source arm/Kconfig +source cpu/Kconfig source alpha/Kconfig source avr/Kconfig source cris/Kconfig diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 893a7bff66..d97015c45c 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -678,21 +678,6 @@ config ZAURUS select NAND select ECC -config A9MPCORE - bool - select A9_GTIMER - select A9SCU # snoop control unit - select ARM_GIC - select ARM_MPTIMER - -config A15MPCORE - bool - select ARM_GIC - -config ARM11MPCORE - bool - select ARM11SCU - config ARMSSE bool select ARM_V7M diff --git a/hw/cpu/Kconfig b/hw/cpu/Kconfig index 1767d028ac..f776e884cd 100644 --- a/hw/cpu/Kconfig +++ b/hw/cpu/Kconfig @@ -1,8 +1,14 @@ -config ARM11MPCORE - bool - config A9MPCORE bool + select A9_GTIMER + select A9SCU # snoop control unit + select ARM_GIC + select ARM_MPTIMER config A15MPCORE bool + select ARM_GIC + +config ARM11MPCORE + bool + select ARM11SCU From 259181d29f81aa72a489dddc7d59517894b51e0f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 15 Apr 2024 08:56:55 +0200 Subject: [PATCH 0199/2884] hw: Add a Kconfig switch for the TYPE_CPU_CLUSTER device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cpu-cluster device is only needed for some few arm and riscv machines. Let's avoid compiling and linking it if it is not really necessary. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20240415065655.130099-3-thuth@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/Kconfig | 3 +++ hw/cpu/Kconfig | 3 +++ hw/cpu/meson.build | 3 ++- hw/riscv/Kconfig | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index d97015c45c..5d4015b75a 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -485,6 +485,7 @@ config XLNX_ZYNQMP_ARM select AHCI select ARM_GIC select CADENCE + select CPU_CLUSTER select DDC select DPCD select SDHCI @@ -503,6 +504,7 @@ config XLNX_VERSAL default y depends on TCG && AARCH64 select ARM_GIC + select CPU_CLUSTER select PL011 select CADENCE select VIRTIO_MMIO @@ -688,6 +690,7 @@ config ARMSSE select CMSDK_APB_DUALTIMER select CMSDK_APB_UART select CMSDK_APB_WATCHDOG + select CPU_CLUSTER select IOTKIT_SECCTL select IOTKIT_SYSCTL select IOTKIT_SYSINFO diff --git a/hw/cpu/Kconfig b/hw/cpu/Kconfig index f776e884cd..baff478e1b 100644 --- a/hw/cpu/Kconfig +++ b/hw/cpu/Kconfig @@ -12,3 +12,6 @@ config A15MPCORE config ARM11MPCORE bool select ARM11SCU + +config CPU_CLUSTER + bool diff --git a/hw/cpu/meson.build b/hw/cpu/meson.build index 38cdcfbe57..9d36bf8ae2 100644 --- a/hw/cpu/meson.build +++ b/hw/cpu/meson.build @@ -1,4 +1,5 @@ -system_ss.add(files('core.c', 'cluster.c')) +system_ss.add(files('core.c')) +system_ss.add(when: 'CONFIG_CPU_CLUSTER', if_true: files('cluster.c')) system_ss.add(when: 'CONFIG_ARM11MPCORE', if_true: files('arm11mpcore.c')) system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_mpcore.c')) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 5d644eb7b1..fc72ef0379 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -9,6 +9,7 @@ config IBEX config MICROCHIP_PFSOC bool select CADENCE_SDHCI + select CPU_CLUSTER select MCHP_PFSOC_DMC select MCHP_PFSOC_IOSCB select MCHP_PFSOC_MMUART @@ -68,6 +69,7 @@ config SIFIVE_E config SIFIVE_U bool select CADENCE + select CPU_CLUSTER select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PDMA From 2c5b2d9128b22fa5b8eccc3993170cc49b414d29 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 18 Apr 2024 18:04:31 +0800 Subject: [PATCH 0200/2884] hw/cxl/cxl-cdat: Make ct3_load_cdat() return boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As error.h suggested, the best practice for callee is to return something to indicate success / failure. So make ct3_load_cdat() return boolean, and this is the preparation for cxl_doe_cdat_init() returning boolean. Suggested-by: Markus Armbruster Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Acked-by: Jonathan Cameron Message-ID: <20240418100433.1085447-2-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/cxl/cxl-cdat.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c index 551545f782..b3e496857a 100644 --- a/hw/cxl/cxl-cdat.c +++ b/hw/cxl/cxl-cdat.c @@ -111,7 +111,7 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) cdat->entry = g_steal_pointer(&cdat_st); } -static void ct3_load_cdat(CDATObject *cdat, Error **errp) +static bool ct3_load_cdat(CDATObject *cdat, Error **errp) { g_autofree CDATEntry *cdat_st = NULL; g_autofree uint8_t *buf = NULL; @@ -127,11 +127,11 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) &file_size, &error)) { error_setg(errp, "CDAT: File read failed: %s", error->message); g_error_free(error); - return; + return false; } if (file_size < sizeof(CDATTableHeader)) { error_setg(errp, "CDAT: File too short"); - return; + return false; } i = sizeof(CDATTableHeader); num_ent = 1; @@ -139,19 +139,19 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) hdr = (CDATSubHeader *)(buf + i); if (i + sizeof(CDATSubHeader) > file_size) { error_setg(errp, "CDAT: Truncated table"); - return; + return false; } cdat_len_check(hdr, errp); i += hdr->length; if (i > file_size) { error_setg(errp, "CDAT: Truncated table"); - return; + return false; } num_ent++; } if (i != file_size) { error_setg(errp, "CDAT: File length mismatch"); - return; + return false; } cdat_st = g_new0(CDATEntry, num_ent); @@ -185,6 +185,7 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) cdat->entry_len = num_ent; cdat->entry = g_steal_pointer(&cdat_st); cdat->buf = g_steal_pointer(&buf); + return true; } void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) From a133d207a8fefe934eb808c2b1ee8f2c695cb528 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 18 Apr 2024 18:04:32 +0800 Subject: [PATCH 0201/2884] hw/cxl/cxl-cdat: Make ct3_build_cdat() return boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As error.h suggested, the best practice for callee is to return something to indicate success / failure. So make ct3_build_cdat() return boolean, and this is the preparation for cxl_doe_cdat_init() returning boolean. Suggested-by: Markus Armbruster Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Acked-by: Jonathan Cameron Message-ID: <20240418100433.1085447-3-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/cxl/cxl-cdat.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c index b3e496857a..e7bc1380bf 100644 --- a/hw/cxl/cxl-cdat.c +++ b/hw/cxl/cxl-cdat.c @@ -44,7 +44,7 @@ static void cdat_len_check(CDATSubHeader *hdr, Error **errp) } } -static void ct3_build_cdat(CDATObject *cdat, Error **errp) +static bool ct3_build_cdat(CDATObject *cdat, Error **errp) { g_autofree CDATTableHeader *cdat_header = NULL; g_autofree CDATEntry *cdat_st = NULL; @@ -58,7 +58,7 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) cdat_header = g_malloc0(sizeof(*cdat_header)); if (!cdat_header) { error_setg(errp, "Failed to allocate CDAT header"); - return; + return false; } cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, @@ -67,14 +67,14 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) if (cdat->built_buf_len <= 0) { /* Build later as not all data available yet */ cdat->to_update = true; - return; + return true; } cdat->to_update = false; cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1)); if (!cdat_st) { error_setg(errp, "Failed to allocate CDAT entry array"); - return; + return false; } /* Entry 0 for CDAT header, starts with Entry 1 */ @@ -109,6 +109,7 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) cdat_st[0].length = sizeof(*cdat_header); cdat->entry_len = 1 + cdat->built_buf_len; cdat->entry = g_steal_pointer(&cdat_st); + return true; } static bool ct3_load_cdat(CDATObject *cdat, Error **errp) From e0ddabc6d4cfef4a5c7f154f0b0ad00dbf9a18d0 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 18 Apr 2024 18:04:33 +0800 Subject: [PATCH 0202/2884] hw/cxl/cxl-cdat: Make cxl_doe_cdat_init() return boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As error.h suggested, the best practice for callee is to return something to indicate success / failure. With returned boolean, there's no need to dereference @errp to check failure case. Suggested-by: Markus Armbruster Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Acked-by: Jonathan Cameron Message-ID: <20240418100433.1085447-4-zhao1.liu@linux.intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/cxl/cxl-cdat.c | 6 +++--- hw/mem/cxl_type3.c | 3 +-- hw/pci-bridge/cxl_upstream.c | 3 +-- include/hw/cxl/cxl_component.h | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c index e7bc1380bf..959a55518e 100644 --- a/hw/cxl/cxl-cdat.c +++ b/hw/cxl/cxl-cdat.c @@ -189,14 +189,14 @@ static bool ct3_load_cdat(CDATObject *cdat, Error **errp) return true; } -void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) +bool cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) { CDATObject *cdat = &cxl_cstate->cdat; if (cdat->filename) { - ct3_load_cdat(cdat, errp); + return ct3_load_cdat(cdat, errp); } else { - ct3_build_cdat(cdat, errp); + return ct3_build_cdat(cdat, errp); } } diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index b0a7e9f11b..3e42490b6c 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -705,8 +705,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table; cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table; cxl_cstate->cdat.private = ct3d; - cxl_doe_cdat_init(cxl_cstate, errp); - if (*errp) { + if (!cxl_doe_cdat_init(cxl_cstate, errp)) { goto err_free_special_ops; } diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index 783fa6adac..e51221a5f3 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -338,8 +338,7 @@ static void cxl_usp_realize(PCIDevice *d, Error **errp) cxl_cstate->cdat.build_cdat_table = build_cdat_table; cxl_cstate->cdat.free_cdat_table = free_default_cdat_table; cxl_cstate->cdat.private = d; - cxl_doe_cdat_init(cxl_cstate, errp); - if (*errp) { + if (!cxl_doe_cdat_init(cxl_cstate, errp)) { goto err_cap; } diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index 5012fab6f7..945ee6ffd0 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -273,7 +273,7 @@ hwaddr cxl_decode_ig(int ig); CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb); bool cxl_get_hb_passthrough(PCIHostState *hb); -void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp); +bool cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp); void cxl_doe_cdat_release(CXLComponentState *cxl_cstate); void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp); From 159fb790e48992f74644b0b0876615e8caacbfe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 18 Apr 2024 16:49:16 +0200 Subject: [PATCH 0203/2884] hw/elf_ops: Rename elf_ops.h -> elf_ops.h.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented in the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore rename "hw/elf_ops.h" as "hw/elf_ops.h.inc". Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20240424173333.96148-2-philmd@linaro.org> --- bsd-user/elfload.c | 2 +- hw/core/loader.c | 4 ++-- include/hw/{elf_ops.h => elf_ops.h.inc} | 0 linux-user/elfload.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename include/hw/{elf_ops.h => elf_ops.h.inc} (100%) diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index baf2f63d2f..833fa3bd05 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -383,7 +383,7 @@ static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) return ""; } -/* FIXME: This should use elf_ops.h */ +/* FIXME: This should use elf_ops.h.inc */ static int symcmp(const void *s0, const void *s1) { struct elf_sym *sym0 = (struct elf_sym *)s0; diff --git a/hw/core/loader.c b/hw/core/loader.c index b8e52f3fb0..2f8105d7de 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -305,7 +305,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define elf_word uint32_t #define elf_sword int32_t #define bswapSZs bswap32s -#include "hw/elf_ops.h" +#include "hw/elf_ops.h.inc" #undef elfhdr #undef elf_phdr @@ -327,7 +327,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define elf_sword int64_t #define bswapSZs bswap64s #define SZ 64 -#include "hw/elf_ops.h" +#include "hw/elf_ops.h.inc" const char *load_elf_strerror(ssize_t error) { diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h.inc similarity index 100% rename from include/hw/elf_ops.h rename to include/hw/elf_ops.h.inc diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f4a0b78c75..a0999dac15 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3572,7 +3572,7 @@ static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) return ""; } -/* FIXME: This should use elf_ops.h */ +/* FIXME: This should use elf_ops.h.inc */ static int symcmp(const void *s0, const void *s1) { struct elf_sym *sym0 = (struct elf_sym *)s0; From 206e562c5aa5db55362bff0e88ba220250858f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 18 Apr 2024 17:07:03 +0200 Subject: [PATCH 0204/2884] hw/xtensa: Include missing 'exec/cpu-common.h' in 'bootparam.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_physical_memory_write() is declared in "exec/cpu-common.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20240418192525.97451-21-philmd@linaro.org> --- hw/xtensa/bootparam.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/xtensa/bootparam.h b/hw/xtensa/bootparam.h index ade7891ec5..f57ff850bc 100644 --- a/hw/xtensa/bootparam.h +++ b/hw/xtensa/bootparam.h @@ -1,6 +1,8 @@ #ifndef HW_XTENSA_BOOTPARAM_H #define HW_XTENSA_BOOTPARAM_H +#include "exec/cpu-common.h" + #define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ #define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ #define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ From 4f88e5215a74a97563c958d63634b107eff30c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Sun, 21 Apr 2024 16:14:23 +0200 Subject: [PATCH 0205/2884] hw/misc : Correct 5 spaces indents in stm32l4x5_exti MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240421141455.116548-1-ines.varhol@telecom-paris.fr> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/stm32l4x5_exti.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index 9fd859160d..5c55ee4268 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -59,22 +59,22 @@ static const uint32_t exti_romask[EXTI_NUM_REGISTER] = { static unsigned regbank_index_by_irq(unsigned irq) { - return irq >= EXTI_MAX_IRQ_PER_BANK ? 1 : 0; + return irq >= EXTI_MAX_IRQ_PER_BANK ? 1 : 0; } static unsigned regbank_index_by_addr(hwaddr addr) { - return addr >= EXTI_IMR2 ? 1 : 0; + return addr >= EXTI_IMR2 ? 1 : 0; } static unsigned valid_mask(unsigned bank) { - return MAKE_64BIT_MASK(0, irqs_per_bank[bank]); + return MAKE_64BIT_MASK(0, irqs_per_bank[bank]); } static unsigned configurable_mask(unsigned bank) { - return valid_mask(bank) & ~exti_romask[bank]; + return valid_mask(bank) & ~exti_romask[bank]; } static void stm32l4x5_exti_reset_hold(Object *obj) From f4b63768b91811cdcf1fb7b270587123251dfea5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 22 Apr 2024 22:06:22 +0200 Subject: [PATCH 0206/2884] hw/i386/pc_sysfw: Remove unused parameter from pc_isa_bios_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240422200625.2768-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_sysfw.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 3efabbbab2..87b5bf59d6 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -41,8 +41,7 @@ #define FLASH_SECTOR_SIZE 4096 static void pc_isa_bios_init(MemoryRegion *rom_memory, - MemoryRegion *flash_mem, - int ram_size) + MemoryRegion *flash_mem) { int isa_bios_size; MemoryRegion *isa_bios; @@ -186,7 +185,7 @@ static void pc_system_flash_map(PCMachineState *pcms, if (i == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); - pc_isa_bios_init(rom_memory, flash_mem, size); + pc_isa_bios_init(rom_memory, flash_mem); /* Encrypt the pflash boot ROM */ if (sev_enabled()) { From dcba73b4453b7ed74d2ae24c5c8b273431c4484c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:09 +0800 Subject: [PATCH 0207/2884] hw/core/machine: Introduce the module as a CPU topology level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In x86, module is the topology level above core, which contains a set of cores that share certain resources (in current products, the resource usually includes L2 cache, as well as module scoped features and MSRs). Though smp.clusters could also share the L2 cache resource [1], there are following reasons that drive us to introduce the new smp.modules: * As the CPU topology abstraction in device tree [2], cluster supports nesting (though currently QEMU hasn't support that). In contrast, (x86) module does not support nesting. * Due to nesting, there is great flexibility in sharing resources on cluster, rather than narrowing cluster down to sharing L2 (and L3 tags) as the lowest topology level that contains cores. * Flexible nesting of cluster allows it to correspond to any level between the x86 package and core. * In Linux kernel, x86's cluster only represents the L2 cache domain but QEMU's smp.clusters is the CPU topology level. Linux kernel will also expose module level topology information in sysfs for x86. To avoid cluster ambiguity and keep a consistent CPU topology naming style with the Linux kernel, we introduce module level for x86. The module is, in existing hardware practice, the lowest layer that contains the core, while the cluster is able to have a higher topological scope than the module due to its nesting. Therefore, place the module between the cluster and the core: drawer/book/socket/die/cluster/module/core/thread With the above topological hierarchy order, introduce module level support in MachineState and MachineClass. [1]: https://lore.kernel.org/qemu-devel/c3d68005-54e0-b8fe-8dc1-5989fe3c7e69@huawei.com/ [2]: https://www.kernel.org/doc/Documentation/devicetree/bindings/cpu/cpu-topology.txt Suggested-by: Xiaoyao Li Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-2-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-smp.c | 2 +- hw/core/machine.c | 1 + include/hw/boards.h | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 27864c9507..2e68fcfdfd 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -266,7 +266,7 @@ void machine_parse_smp_config(MachineState *ms, unsigned int machine_topo_get_cores_per_socket(const MachineState *ms) { - return ms->smp.cores * ms->smp.clusters * ms->smp.dies; + return ms->smp.cores * ms->smp.modules * ms->smp.clusters * ms->smp.dies; } unsigned int machine_topo_get_threads_per_socket(const MachineState *ms) diff --git a/hw/core/machine.c b/hw/core/machine.c index 582c2df37a..9966641159 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1157,6 +1157,7 @@ static void machine_initfn(Object *obj) ms->smp.sockets = 1; ms->smp.dies = 1; ms->smp.clusters = 1; + ms->smp.modules = 1; ms->smp.cores = 1; ms->smp.threads = 1; diff --git a/include/hw/boards.h b/include/hw/boards.h index 69c1ba45cf..2fa800f11a 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -144,6 +144,7 @@ typedef struct { * provided SMP configuration * @books_supported - whether books are supported by the machine * @drawers_supported - whether drawers are supported by the machine + * @modules_supported - whether modules are supported by the machine */ typedef struct { bool prefer_sockets; @@ -152,6 +153,7 @@ typedef struct { bool has_clusters; bool books_supported; bool drawers_supported; + bool modules_supported; } SMPCompatProps; /** @@ -339,6 +341,7 @@ typedef struct DeviceMemoryState { * @sockets: the number of sockets in one book * @dies: the number of dies in one socket * @clusters: the number of clusters in one die + * @modules: the number of modules in one cluster * @cores: the number of cores in one cluster * @threads: the number of threads in one core * @max_cpus: the maximum number of logical processors on the machine @@ -350,6 +353,7 @@ typedef struct CpuTopology { unsigned int sockets; unsigned int dies; unsigned int clusters; + unsigned int modules; unsigned int cores; unsigned int threads; unsigned int max_cpus; From 8ec0a46347987c74464fe67c42030d074fe8c0f0 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:10 +0800 Subject: [PATCH 0208/2884] hw/core/machine: Support modules in -smp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add "modules" parameter parsing support in -smp. Suggested-by: Xiaoyao Li Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Acked-by: Markus Armbruster Message-ID: <20240424154929.1487382-3-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-smp.c | 39 +++++++++++++++++++++++++++++++++------ hw/core/machine.c | 1 + qapi/machine.json | 3 +++ system/vl.c | 3 +++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 2e68fcfdfd..2b93fa99c9 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -51,6 +51,10 @@ static char *cpu_hierarchy_to_string(MachineState *ms) g_string_append_printf(s, " * clusters (%u)", ms->smp.clusters); } + if (mc->smp_props.modules_supported) { + g_string_append_printf(s, " * modules (%u)", ms->smp.modules); + } + g_string_append_printf(s, " * cores (%u)", ms->smp.cores); g_string_append_printf(s, " * threads (%u)", ms->smp.threads); @@ -88,6 +92,7 @@ void machine_parse_smp_config(MachineState *ms, unsigned sockets = config->has_sockets ? config->sockets : 0; unsigned dies = config->has_dies ? config->dies : 0; unsigned clusters = config->has_clusters ? config->clusters : 0; + unsigned modules = config->has_modules ? config->modules : 0; unsigned cores = config->has_cores ? config->cores : 0; unsigned threads = config->has_threads ? config->threads : 0; unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; @@ -103,6 +108,7 @@ void machine_parse_smp_config(MachineState *ms, (config->has_sockets && config->sockets == 0) || (config->has_dies && config->dies == 0) || (config->has_clusters && config->clusters == 0) || + (config->has_modules && config->modules == 0) || (config->has_cores && config->cores == 0) || (config->has_threads && config->threads == 0) || (config->has_maxcpus && config->maxcpus == 0)) { @@ -115,6 +121,20 @@ void machine_parse_smp_config(MachineState *ms, * If not supported by the machine, a topology parameter must be * omitted. */ + if (!mc->smp_props.modules_supported && config->has_modules) { + if (config->modules > 1) { + error_setg(errp, "modules not supported by this " + "machine's CPU topology"); + return; + } else { + /* Here modules only equals 1 since we've checked zero case. */ + warn_report("Deprecated CPU topology (considered invalid): " + "Unsupported modules parameter mustn't be " + "specified as 1"); + } + } + modules = modules > 0 ? modules : 1; + if (!mc->smp_props.clusters_supported && config->has_clusters) { if (config->clusters > 1) { error_setg(errp, "clusters not supported by this " @@ -185,11 +205,13 @@ void machine_parse_smp_config(MachineState *ms, cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; sockets = maxcpus / - (drawers * books * dies * clusters * cores * threads); + (drawers * books * dies * clusters * + modules * cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; cores = maxcpus / - (drawers * books * sockets * dies * clusters * threads); + (drawers * books * sockets * dies * + clusters * modules * threads); } } else { /* prefer cores over sockets since 6.2 */ @@ -197,22 +219,26 @@ void machine_parse_smp_config(MachineState *ms, sockets = sockets > 0 ? sockets : 1; threads = threads > 0 ? threads : 1; cores = maxcpus / - (drawers * books * sockets * dies * clusters * threads); + (drawers * books * sockets * dies * + clusters * modules * threads); } else if (sockets == 0) { threads = threads > 0 ? threads : 1; sockets = maxcpus / - (drawers * books * dies * clusters * cores * threads); + (drawers * books * dies * clusters * + modules * cores * threads); } } /* try to calculate omitted threads at last */ if (threads == 0) { threads = maxcpus / - (drawers * books * sockets * dies * clusters * cores); + (drawers * books * sockets * dies * + clusters * modules * cores); } } - total_cpus = drawers * books * sockets * dies * clusters * cores * threads; + total_cpus = drawers * books * sockets * dies * + clusters * modules * cores * threads; maxcpus = maxcpus > 0 ? maxcpus : total_cpus; cpus = cpus > 0 ? cpus : maxcpus; @@ -222,6 +248,7 @@ void machine_parse_smp_config(MachineState *ms, ms->smp.sockets = sockets; ms->smp.dies = dies; ms->smp.clusters = clusters; + ms->smp.modules = modules; ms->smp.cores = cores; ms->smp.threads = threads; ms->smp.max_cpus = maxcpus; diff --git a/hw/core/machine.c b/hw/core/machine.c index 9966641159..494b712a76 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -881,6 +881,7 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name, .has_sockets = true, .sockets = ms->smp.sockets, .has_dies = true, .dies = ms->smp.dies, .has_clusters = true, .clusters = ms->smp.clusters, + .has_modules = true, .modules = ms->smp.modules, .has_cores = true, .cores = ms->smp.cores, .has_threads = true, .threads = ms->smp.threads, .has_maxcpus = true, .maxcpus = ms->smp.max_cpus, diff --git a/qapi/machine.json b/qapi/machine.json index 3e9cc3f17d..48f98e7d9f 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1696,6 +1696,8 @@ # # @clusters: number of clusters per parent container (since 7.0) # +# @modules: number of modules per parent container (since 9.1) +# # @cores: number of cores per parent container # # @threads: number of threads per core @@ -1709,6 +1711,7 @@ '*sockets': 'int', '*dies': 'int', '*clusters': 'int', + '*modules': 'int', '*cores': 'int', '*threads': 'int', '*maxcpus': 'int' } } diff --git a/system/vl.c b/system/vl.c index c644222982..7756eac81e 100644 --- a/system/vl.c +++ b/system/vl.c @@ -741,6 +741,9 @@ static QemuOptsList qemu_smp_opts = { }, { .name = "clusters", .type = QEMU_OPT_NUMBER, + }, { + .name = "modules", + .type = QEMU_OPT_NUMBER, }, { .name = "cores", .type = QEMU_OPT_NUMBER, From 989bb312b021d66927db8e5f443503d63722b38d Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:11 +0800 Subject: [PATCH 0209/2884] hw/core: Introduce module-id as the topology subindex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add module-id in CpuInstanceProperties, to locate the CPU with module level. Suggested-by: Xiaoyao Li Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Acked-by: Markus Armbruster Message-ID: <20240424154929.1487382-4-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-hmp-cmds.c | 4 ++++ qapi/machine.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index a6ff6a4875..8701f00cc7 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -87,6 +87,10 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) monitor_printf(mon, " cluster-id: \"%" PRIu64 "\"\n", c->cluster_id); } + if (c->has_module_id) { + monitor_printf(mon, " module-id: \"%" PRIu64 "\"\n", + c->module_id); + } if (c->has_core_id) { monitor_printf(mon, " core-id: \"%" PRIu64 "\"\n", c->core_id); } diff --git a/qapi/machine.json b/qapi/machine.json index 48f98e7d9f..bce6e1bbc4 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -988,6 +988,9 @@ # @cluster-id: cluster number within the parent container the CPU # belongs to (since 7.1) # +# @module-id: module number within the parent container the CPU belongs +# to (since 9.1) +# # @core-id: core number within the parent container the CPU belongs to # # @thread-id: thread number within the core the CPU belongs to @@ -1005,6 +1008,7 @@ '*socket-id': 'int', '*die-id': 'int', '*cluster-id': 'int', + '*module-id': 'int', '*core-id': 'int', '*thread-id': 'int' } From 098de99aad1aa911b4950b47b55d2e2bcc4f9c0c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:12 +0800 Subject: [PATCH 0210/2884] hw/core: Support module-id in numa configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Module is a level above the core, thereby supporting numa configuration on the module level can bring user more numa flexibility. This is the natural further support for module level. Add module level support in numa configuration. Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-5-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index 494b712a76..0dec48e802 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -800,6 +800,11 @@ void machine_set_cpu_numa_node(MachineState *machine, return; } + if (props->has_module_id && !slot->props.has_module_id) { + error_setg(errp, "module-id is not supported"); + return; + } + if (props->has_cluster_id && !slot->props.has_cluster_id) { error_setg(errp, "cluster-id is not supported"); return; @@ -824,6 +829,11 @@ void machine_set_cpu_numa_node(MachineState *machine, continue; } + if (props->has_module_id && + props->module_id != slot->props.module_id) { + continue; + } + if (props->has_cluster_id && props->cluster_id != slot->props.cluster_id) { continue; @@ -1226,6 +1236,12 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) } g_string_append_printf(s, "cluster-id: %"PRId64, cpu->props.cluster_id); } + if (cpu->props.has_module_id) { + if (s->len) { + g_string_append_printf(s, ", "); + } + g_string_append_printf(s, "module-id: %"PRId64, cpu->props.module_id); + } if (cpu->props.has_core_id) { if (s->len) { g_string_append_printf(s, ", "); From ae6d91a7e9b77abb029ed3fa9fad461422286942 Mon Sep 17 00:00:00 2001 From: Zhu Yangyang Date: Mon, 8 Apr 2024 11:00:43 -0500 Subject: [PATCH 0211/2884] nbd/server: do not poll within a coroutine context Coroutines are not supposed to block. Instead, they should yield. The client performs TLS upgrade outside of an AIOContext, during synchronous handshake; this still requires g_main_loop. But the server responds to TLS upgrade inside a coroutine, so a nested g_main_loop is wrong. Since the two callbacks no longer share more than the setting of data.complete and data.error, it's just as easy to use static helpers instead of trying to share a common code path. It is also possible to add assertions that no other code is interfering with the eventual path to qio reaching the callback, whether or not it required a yield or main loop. Fixes: f95910f ("nbd: implement TLS support in the protocol negotiation") Signed-off-by: Zhu Yangyang [eblake: move callbacks to their use point, add assertions] Signed-off-by: Eric Blake Message-ID: <20240408160214.1200629-5-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- nbd/client.c | 28 ++++++++++++++++++++++++---- nbd/common.c | 11 ----------- nbd/nbd-internal.h | 10 ---------- nbd/server.c | 28 +++++++++++++++++++++++----- 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/nbd/client.c b/nbd/client.c index 29ffc609a4..c89c750467 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -596,13 +596,31 @@ static int nbd_request_simple_option(QIOChannel *ioc, int opt, bool strict, return 1; } +/* Callback to learn when QIO TLS upgrade is complete */ +struct NBDTLSClientHandshakeData { + bool complete; + Error *error; + GMainLoop *loop; +}; + +static void nbd_client_tls_handshake(QIOTask *task, void *opaque) +{ + struct NBDTLSClientHandshakeData *data = opaque; + + qio_task_propagate_error(task, &data->error); + data->complete = true; + if (data->loop) { + g_main_loop_quit(data->loop); + } +} + static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, const char *hostname, Error **errp) { int ret; QIOChannelTLS *tioc; - struct NBDTLSHandshakeData data = { 0 }; + struct NBDTLSClientHandshakeData data = { 0 }; ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, true, errp); if (ret <= 0) { @@ -619,18 +637,20 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, return NULL; } qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); - data.loop = g_main_loop_new(g_main_context_default(), FALSE); trace_nbd_receive_starttls_tls_handshake(); qio_channel_tls_handshake(tioc, - nbd_tls_handshake, + nbd_client_tls_handshake, &data, NULL, NULL); if (!data.complete) { + data.loop = g_main_loop_new(g_main_context_default(), FALSE); g_main_loop_run(data.loop); + assert(data.complete); + g_main_loop_unref(data.loop); } - g_main_loop_unref(data.loop); + if (data.error) { error_propagate(errp, data.error); object_unref(OBJECT(tioc)); diff --git a/nbd/common.c b/nbd/common.c index 3247c1d618..589a748cfe 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -47,17 +47,6 @@ int nbd_drop(QIOChannel *ioc, size_t size, Error **errp) } -void nbd_tls_handshake(QIOTask *task, - void *opaque) -{ - struct NBDTLSHandshakeData *data = opaque; - - qio_task_propagate_error(task, &data->error); - data->complete = true; - g_main_loop_quit(data->loop); -} - - const char *nbd_opt_lookup(uint32_t opt) { switch (opt) { diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index dfa02f77ee..91895106a9 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -72,16 +72,6 @@ static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size, return qio_channel_write_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; } -struct NBDTLSHandshakeData { - GMainLoop *loop; - bool complete; - Error *error; -}; - - -void nbd_tls_handshake(QIOTask *task, - void *opaque); - int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); #endif diff --git a/nbd/server.c b/nbd/server.c index c3484cc1eb..98ae0e1632 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -748,6 +748,23 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) return rc; } +/* Callback to learn when QIO TLS upgrade is complete */ +struct NBDTLSServerHandshakeData { + bool complete; + Error *error; + Coroutine *co; +}; + +static void nbd_server_tls_handshake(QIOTask *task, void *opaque) +{ + struct NBDTLSServerHandshakeData *data = opaque; + + qio_task_propagate_error(task, &data->error); + data->complete = true; + if (!qemu_coroutine_entered(data->co)) { + aio_co_wake(data->co); + } +} /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the * new channel for all further (now-encrypted) communication. */ @@ -756,7 +773,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, { QIOChannel *ioc; QIOChannelTLS *tioc; - struct NBDTLSHandshakeData data = { 0 }; + struct NBDTLSServerHandshakeData data = { 0 }; assert(client->opt == NBD_OPT_STARTTLS); @@ -777,17 +794,18 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls"); trace_nbd_negotiate_handle_starttls_handshake(); - data.loop = g_main_loop_new(g_main_context_default(), FALSE); + data.co = qemu_coroutine_self(); qio_channel_tls_handshake(tioc, - nbd_tls_handshake, + nbd_server_tls_handshake, &data, NULL, NULL); if (!data.complete) { - g_main_loop_run(data.loop); + qemu_coroutine_yield(); + assert(data.complete); } - g_main_loop_unref(data.loop); + if (data.error) { object_unref(OBJECT(tioc)); error_propagate(errp, data.error); From 9a72bea6828ef3f9c957fde1a390a81d66b38cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Mar 2024 17:28:22 +0100 Subject: [PATCH 0212/2884] hw/s390x: Include missing 'cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "cpu.h" is implicitly included. Include it explicitly to avoid the following error when refactoring headers: hw/s390x/s390-stattrib.c:86:40: error: use of undeclared identifier 'TARGET_PAGE_SIZE' len = sac->peek_stattr(sas, addr / TARGET_PAGE_SIZE, buflen, vals); ^ hw/s390x/s390-stattrib.c:94:58: error: use of undeclared identifier 'TARGET_PAGE_MASK' addr / TARGET_PAGE_SIZE, len, addr & ~TARGET_PAGE_MASK); ^ hw/s390x/s390-stattrib.c:224:40: error: use of undeclared identifier 'TARGET_PAGE_BITS' qemu_put_be64(f, (start_gfn << TARGET_PAGE_BITS) | STATTR_FLAG_MORE); ^ In file included from hw/s390x/s390-virtio-ccw.c:17: hw/s390x/s390-virtio-hcall.h:22:27: error: unknown type name 'CPUS390XState' int s390_virtio_hypercall(CPUS390XState *env); ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Acked-by: Eric Farman Message-ID: <20240322162822.7391-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/s390-stattrib.c | 1 + hw/s390x/s390-virtio-hcall.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index bc04187b2b..c4259b5327 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -19,6 +19,7 @@ #include "exec/ram_addr.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" +#include "cpu.h" /* 512KiB cover 2GB of guest memory */ #define CMMA_BLOCK_SIZE (512 * KiB) diff --git a/hw/s390x/s390-virtio-hcall.h b/hw/s390x/s390-virtio-hcall.h index 9800c4b351..3ae6d6ae3a 100644 --- a/hw/s390x/s390-virtio-hcall.h +++ b/hw/s390x/s390-virtio-hcall.h @@ -13,6 +13,7 @@ #define HW_S390_VIRTIO_HCALL_H #include "standard-headers/asm-s390/virtio-ccw.h" +#include "cpu.h" /* The only thing that we need from the old kvm_virtio.h file */ #define KVM_S390_VIRTIO_NOTIFY 0 @@ -20,4 +21,5 @@ typedef int (*s390_virtio_fn)(const uint64_t *args); void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); int s390_virtio_hypercall(CPUS390XState *env); + #endif /* HW_S390_VIRTIO_HCALL_H */ From b1f8536c943dd1d0ffea081d79001ee124157f97 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 22 Apr 2024 15:41:28 +0300 Subject: [PATCH 0213/2884] docs/devel: fix minor typo in submitting-a-patch.rst s/Resolved:/Resolves:/ Signed-off-by: Manos Pitsidianakis Message-ID: <20240422124128.4034482-1-manos.pitsidianakis@linaro.org> Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth --- docs/devel/submitting-a-patch.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index c641d948f1..83e9092b8c 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -177,7 +177,7 @@ add an additional line with "Fixes: If your patch fixes a bug in the gitlab bug tracker, please add a line with "Resolves: " to the commit message, too. Gitlab can -close bugs automatically once commits with the "Resolved:" keyword get +close bugs automatically once commits with the "Resolves:" keyword get merged into the master branch of the project. And if your patch addresses a bug in another public bug tracker, you can also use a line with "Buglink: " for reference here, too. From 73a1e96935dbaec950a310916eec13b46871b8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 22 Apr 2024 13:22:46 +0200 Subject: [PATCH 0214/2884] tests/unit: Remove debug statements in test-nested-aio-poll.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have been running this test for almost a year; it is safe to remove its debug statements, which clutter CI jobs output: ▶ 88/100 /nested-aio-poll OK io_read 0x16bb26158 io_poll_true 0x16bb26158 > io_poll_ready io_read 0x16bb26164 < io_poll_ready io_poll_true 0x16bb26158 io_poll_false 0x16bb26164 > io_poll_ready io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_poll_false 0x16bb26164 io_read 0x16bb26164 < io_poll_ready 88/100 qemu:unit / test-nested-aio-poll OK Reviewed-by: Eric Blake Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Message-ID: <20240422112246.83812-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- tests/unit/test-nested-aio-poll.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/unit/test-nested-aio-poll.c b/tests/unit/test-nested-aio-poll.c index db33742af3..d8fd92c43b 100644 --- a/tests/unit/test-nested-aio-poll.c +++ b/tests/unit/test-nested-aio-poll.c @@ -30,19 +30,16 @@ typedef struct { static void io_read(EventNotifier *notifier) { - fprintf(stderr, "%s %p\n", __func__, notifier); event_notifier_test_and_clear(notifier); } static bool io_poll_true(void *opaque) { - fprintf(stderr, "%s %p\n", __func__, opaque); return true; } static bool io_poll_false(void *opaque) { - fprintf(stderr, "%s %p\n", __func__, opaque); return false; } @@ -50,8 +47,6 @@ static void io_poll_ready(EventNotifier *notifier) { TestData *td = container_of(notifier, TestData, poll_notifier); - fprintf(stderr, "> %s\n", __func__); - g_assert(!td->nested); td->nested = true; @@ -62,8 +57,6 @@ static void io_poll_ready(EventNotifier *notifier) g_assert(aio_poll(td->ctx, true)); td->nested = false; - - fprintf(stderr, "< %s\n", __func__); } /* dummy_notifier never triggers */ From 17523a38194d80f2955c6a8e0702e0fc86dd083d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 19 Apr 2024 11:06:31 +0200 Subject: [PATCH 0215/2884] target/s390x: Remove KVM stubs in cpu_models.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the calls are elided when KVM is not available, we can remove the stubs (which are never compiled). Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20240419090631.48055-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index d7b8912989..a89c2a15ab 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -114,23 +114,8 @@ static inline uint64_t s390_cpuid_from_cpu_model(const S390CPUModel *model) S390CPUDef const *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga, S390FeatBitmap features); -#ifdef CONFIG_KVM bool kvm_s390_cpu_models_supported(void); void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp); -#else -static inline void kvm_s390_get_host_cpu_model(S390CPUModel *model, - Error **errp) -{ -} -static inline void kvm_s390_apply_cpu_model(const S390CPUModel *model, - Error **errp) -{ -} -static inline bool kvm_s390_cpu_models_supported(void) -{ - return false; -} -#endif #endif /* TARGET_S390X_CPU_MODELS_H */ From 4fa333e08dd96395a99ea8dd9e4c73a29dd23344 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 8 Apr 2024 11:00:44 -0500 Subject: [PATCH 0216/2884] nbd/server: Mark negotiation functions as coroutine_fn nbd_negotiate() is already marked coroutine_fn. And given the fix in the previous patch to have nbd_negotiate_handle_starttls not create and wait on a g_main_loop (as that would violate coroutine constraints), it is worth marking the rest of the related static functions reachable only during option negotiation as also being coroutine_fn. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Eric Blake Message-ID: <20240408160214.1200629-6-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy [eblake: drop one spurious coroutine_fn marking] Signed-off-by: Eric Blake --- nbd/server.c | 102 +++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 43 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 98ae0e1632..892797bb11 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -195,8 +195,9 @@ static inline void set_be_option_rep(NBDOptionReply *rep, uint32_t option, /* Send a reply header, including length, but no payload. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, - uint32_t len, Error **errp) +static coroutine_fn int +nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, + uint32_t len, Error **errp) { NBDOptionReply rep; @@ -211,15 +212,15 @@ static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, /* Send a reply header with default 0 length. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_rep(NBDClient *client, uint32_t type, Error **errp) { return nbd_negotiate_send_rep_len(client, type, 0, errp); } /* Send an error reply. * Return -errno on error, 0 on success. */ -static int G_GNUC_PRINTF(4, 0) +static coroutine_fn int G_GNUC_PRINTF(4, 0) nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type, Error **errp, const char *fmt, va_list va) { @@ -259,7 +260,7 @@ nbd_sanitize_name(const char *name) /* Send an error reply. * Return -errno on error, 0 on success. */ -static int G_GNUC_PRINTF(4, 5) +static coroutine_fn int G_GNUC_PRINTF(4, 5) nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, Error **errp, const char *fmt, ...) { @@ -275,7 +276,7 @@ nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, /* Drop remainder of the current option, and send a reply with the * given error type and message. Return -errno on read or write * failure; or 0 if connection is still live. */ -static int G_GNUC_PRINTF(4, 0) +static coroutine_fn int G_GNUC_PRINTF(4, 0) nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, const char *fmt, va_list va) { @@ -288,7 +289,7 @@ nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, return ret; } -static int G_GNUC_PRINTF(4, 5) +static coroutine_fn int G_GNUC_PRINTF(4, 5) nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, const char *fmt, ...) { @@ -302,7 +303,7 @@ nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, return ret; } -static int G_GNUC_PRINTF(3, 4) +static coroutine_fn int G_GNUC_PRINTF(3, 4) nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...) { int ret; @@ -319,8 +320,9 @@ nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...) * If @check_nul, require that no NUL bytes appear in buffer. * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. */ -static int nbd_opt_read(NBDClient *client, void *buffer, size_t size, - bool check_nul, Error **errp) +static coroutine_fn int +nbd_opt_read(NBDClient *client, void *buffer, size_t size, + bool check_nul, Error **errp) { if (size > client->optlen) { return nbd_opt_invalid(client, errp, @@ -343,7 +345,8 @@ static int nbd_opt_read(NBDClient *client, void *buffer, size_t size, /* Drop size bytes from the unparsed payload of the current option. * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. */ -static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp) +static coroutine_fn int +nbd_opt_skip(NBDClient *client, size_t size, Error **errp) { if (size > client->optlen) { return nbd_opt_invalid(client, errp, @@ -366,8 +369,9 @@ static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp) * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. */ -static int nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length, - Error **errp) +static coroutine_fn int +nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length, + Error **errp) { int ret; uint32_t len; @@ -402,8 +406,8 @@ static int nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length, /* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, Error **errp) { ERRP_GUARD(); size_t name_len, desc_len; @@ -444,7 +448,8 @@ static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, /* Process the NBD_OPT_LIST command, with a potential series of replies. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_handle_list(NBDClient *client, Error **errp) { NBDExport *exp; assert(client->opt == NBD_OPT_LIST); @@ -459,7 +464,8 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); } -static void nbd_check_meta_export(NBDClient *client, NBDExport *exp) +static coroutine_fn void +nbd_check_meta_export(NBDClient *client, NBDExport *exp) { if (exp != client->contexts.exp) { client->contexts.count = 0; @@ -468,8 +474,9 @@ static void nbd_check_meta_export(NBDClient *client, NBDExport *exp) /* Send a reply to NBD_OPT_EXPORT_NAME. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, - Error **errp) +static coroutine_fn int +nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, + Error **errp) { ERRP_GUARD(); g_autofree char *name = NULL; @@ -536,9 +543,9 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, /* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes. * The buffer does NOT include the info type prefix. * Return -errno on error, 0 if ready to send more. */ -static int nbd_negotiate_send_info(NBDClient *client, - uint16_t info, uint32_t length, void *buf, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_info(NBDClient *client, uint16_t info, uint32_t length, + void *buf, Error **errp) { int rc; @@ -565,7 +572,8 @@ static int nbd_negotiate_send_info(NBDClient *client, * -errno transmission error occurred or @fatal was requested, errp is set * 0 error message successfully sent to client, errp is not set */ -static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp) +static coroutine_fn int +nbd_reject_length(NBDClient *client, bool fatal, Error **errp) { int ret; @@ -583,7 +591,8 @@ static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp) /* Handle NBD_OPT_INFO and NBD_OPT_GO. * Return -errno on error, 0 if ready for next option, and 1 to move * into transmission phase. */ -static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_handle_info(NBDClient *client, Error **errp) { int rc; g_autofree char *name = NULL; @@ -755,7 +764,8 @@ struct NBDTLSServerHandshakeData { Coroutine *co; }; -static void nbd_server_tls_handshake(QIOTask *task, void *opaque) +static void +nbd_server_tls_handshake(QIOTask *task, void *opaque) { struct NBDTLSServerHandshakeData *data = opaque; @@ -768,8 +778,8 @@ static void nbd_server_tls_handshake(QIOTask *task, void *opaque) /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the * new channel for all further (now-encrypted) communication. */ -static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, - Error **errp) +static coroutine_fn QIOChannel * +nbd_negotiate_handle_starttls(NBDClient *client, Error **errp) { QIOChannel *ioc; QIOChannelTLS *tioc; @@ -821,10 +831,9 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, * * For NBD_OPT_LIST_META_CONTEXT @context_id is ignored, 0 is used instead. */ -static int nbd_negotiate_send_meta_context(NBDClient *client, - const char *context, - uint32_t context_id, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_meta_context(NBDClient *client, const char *context, + uint32_t context_id, Error **errp) { NBDOptionReplyMetaContext opt; struct iovec iov[] = { @@ -849,8 +858,9 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, * Return true if @query matches @pattern, or if @query is empty when * the @client is performing _LIST_. */ -static bool nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, - const char *query) +static coroutine_fn bool +nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, + const char *query) { if (!*query) { trace_nbd_negotiate_meta_query_parse("empty"); @@ -867,7 +877,8 @@ static bool nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, /* * Return true and adjust @str in place if it begins with @prefix. */ -static bool nbd_strshift(const char **str, const char *prefix) +static coroutine_fn bool +nbd_strshift(const char **str, const char *prefix) { size_t len = strlen(prefix); @@ -883,8 +894,9 @@ static bool nbd_strshift(const char **str, const char *prefix) * Handle queries to 'base' namespace. For now, only the base:allocation * context is available. Return true if @query has been handled. */ -static bool nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta, - const char *query) +static coroutine_fn bool +nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta, + const char *query) { if (!nbd_strshift(&query, "base:")) { return false; @@ -903,8 +915,9 @@ static bool nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta, * and qemu:allocation-depth contexts are available. Return true if @query * has been handled. */ -static bool nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta, - const char *query) +static coroutine_fn bool +nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta, + const char *query) { size_t i; @@ -968,8 +981,9 @@ static bool nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta, * * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. */ -static int nbd_negotiate_meta_query(NBDClient *client, - NBDMetaContexts *meta, Error **errp) +static coroutine_fn int +nbd_negotiate_meta_query(NBDClient *client, + NBDMetaContexts *meta, Error **errp) { int ret; g_autofree char *query = NULL; @@ -1008,7 +1022,8 @@ static int nbd_negotiate_meta_query(NBDClient *client, * Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT * * Return -errno on I/O error, or 0 if option was completely handled. */ -static int nbd_negotiate_meta_queries(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_meta_queries(NBDClient *client, Error **errp) { int ret; g_autofree char *export_name = NULL; @@ -1136,7 +1151,8 @@ static int nbd_negotiate_meta_queries(NBDClient *client, Error **errp) * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, * errp is not set */ -static int nbd_negotiate_options(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_options(NBDClient *client, Error **errp) { uint32_t flags; bool fixedNewstyle = false; From 7d7a21ba691d3f52fdcf123adf2b79f7ce88174d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 13 Jun 2023 16:29:11 +0200 Subject: [PATCH 0217/2884] exec: Rename NEED_CPU_H -> COMPILING_PER_TARGET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'NEED_CPU_H' guard target-specific code; it is defined by meson altogether with the 'CONFIG_TARGET' definition. Rename NEED_CPU_H as COMPILING_PER_TARGET to clarify its meaning. Mechanical change running: $ sed -i s/NEED_CPU_H/COMPILING_PER_TARGET/g $(git grep -l NEED_CPU_H) then manually add a /* COMPILING_PER_TARGET */ comment after the '#endif' when the block is large. Inspired-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240322161439.6448-4-philmd@linaro.org> --- include/exec/cpu-defs.h | 2 +- include/exec/helper-head.h | 4 ++-- include/exec/memop.h | 4 ++-- include/exec/memory.h | 4 ++-- include/exec/tswap.h | 4 ++-- include/gdbstub/helpers.h | 2 +- include/hw/core/cpu.h | 4 ++-- include/qemu/osdep.h | 2 +- include/sysemu/hvf.h | 8 ++++---- include/sysemu/kvm.h | 6 +++--- include/sysemu/nvmm.h | 4 ++-- include/sysemu/whpx.h | 4 ++-- include/sysemu/xen.h | 4 ++-- meson.build | 4 ++-- scripts/analyze-inclusions | 6 +++--- target/arm/kvm-consts.h | 4 ++-- 16 files changed, 33 insertions(+), 33 deletions(-) diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index 3915438b83..0dbef3010c 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -19,7 +19,7 @@ #ifndef CPU_DEFS_H #define CPU_DEFS_H -#ifndef NEED_CPU_H +#ifndef COMPILING_PER_TARGET #error cpu.h included from common code #endif diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index 28ceab0a46..5ef467a79d 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -43,7 +43,7 @@ #define dh_ctype_noreturn G_NORETURN void #define dh_ctype(t) dh_ctype_##t -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET # ifdef TARGET_LONG_BITS # if TARGET_LONG_BITS == 32 # define dh_alias_tl i32 @@ -54,7 +54,7 @@ # endif # endif # define dh_ctype_tl target_ulong -#endif +#endif /* COMPILING_PER_TARGET */ /* We can't use glue() here because it falls foul of C preprocessor recursive expansion rules. */ diff --git a/include/exec/memop.h b/include/exec/memop.h index a86dc6743a..06417ff361 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -35,7 +35,7 @@ typedef enum MemOp { MO_LE = 0, MO_BE = MO_BSWAP, #endif -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #if TARGET_BIG_ENDIAN MO_TE = MO_BE, #else @@ -135,7 +135,7 @@ typedef enum MemOp { MO_BESL = MO_BE | MO_SL, MO_BESQ = MO_BE | MO_SQ, -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET MO_TEUW = MO_TE | MO_UW, MO_TEUL = MO_TE | MO_UL, MO_TEUQ = MO_TE | MO_UQ, diff --git a/include/exec/memory.h b/include/exec/memory.h index dbb1bad72f..dadb5cd65a 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3107,7 +3107,7 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, MemTxResult address_space_set(AddressSpace *as, hwaddr addr, uint8_t c, hwaddr len, MemTxAttrs attrs); -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET /* enum device_endian to MemOp. */ static inline MemOp devend_memop(enum device_endian end) { @@ -3125,7 +3125,7 @@ static inline MemOp devend_memop(enum device_endian end) return (end == non_host_endianness) ? MO_BSWAP : 0; #endif } -#endif +#endif /* COMPILING_PER_TARGET */ /* * Inhibit technologies that require discarding of pages in RAM blocks, e.g., diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 68944a880b..5089cd6a4c 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -15,11 +15,11 @@ * If we're in target-specific code, we can hard-code the swapping * condition, otherwise we have to do (slower) run-time checks. */ -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #define target_needs_bswap() (HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN) #else #define target_needs_bswap() (target_words_bigendian() != HOST_BIG_ENDIAN) -#endif +#endif /* COMPILING_PER_TARGET */ static inline uint16_t tswap16(uint16_t s) { diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h index c573aef2dc..6b97610f48 100644 --- a/include/gdbstub/helpers.h +++ b/include/gdbstub/helpers.h @@ -12,7 +12,7 @@ #ifndef _GDBSTUB_HELPERS_H_ #define _GDBSTUB_HELPERS_H_ -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #include "cpu.h" /* diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index ec14f74ce5..7f037b158e 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1182,7 +1182,7 @@ bool target_words_bigendian(void); const char *target_name(void); -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifndef CONFIG_USER_ONLY @@ -1197,7 +1197,7 @@ extern const VMStateDescription vmstate_cpu_common; } #endif /* !CONFIG_USER_ONLY */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #define UNASSIGNED_CPU_INDEX -1 #define UNASSIGNED_CLUSTER_INDEX -1 diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index c7053cdc2b..f61edcfdc2 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -32,7 +32,7 @@ #endif #include "config-host.h" -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #include CONFIG_TARGET #else #include "exec/poison.h" diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index 4a7c6af3a5..730f927f03 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -16,7 +16,7 @@ #include "qemu/accel.h" #include "qom/object.h" -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #include "cpu.h" #ifdef CONFIG_HVF @@ -26,7 +26,7 @@ extern bool hvf_allowed; #define hvf_enabled() 0 #endif /* !CONFIG_HVF */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") @@ -34,7 +34,7 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET struct hvf_sw_breakpoint { vaddr pc; vaddr saved_insn; @@ -66,6 +66,6 @@ void hvf_arch_update_guest_debug(CPUState *cpu); * Return whether the guest supports debugging. */ bool hvf_arch_supports_guest_debug(void); -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 47f9e8be1b..eaf801bc93 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -20,7 +20,7 @@ #include "qemu/accel.h" #include "qom/object.h" -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET # ifdef CONFIG_KVM # include # define CONFIG_KVM_IS_POSSIBLE @@ -210,7 +210,7 @@ bool kvm_arm_supports_user_irq(void); int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #include "cpu.h" void kvm_flush_coalesced_mmio_buffer(void); @@ -435,7 +435,7 @@ void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, hwaddr *phys_addr); -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ void kvm_cpu_synchronize_state(CPUState *cpu); diff --git a/include/sysemu/nvmm.h b/include/sysemu/nvmm.h index be7bc9a62d..6971ddb3a5 100644 --- a/include/sysemu/nvmm.h +++ b/include/sysemu/nvmm.h @@ -12,7 +12,7 @@ #ifndef QEMU_NVMM_H #define QEMU_NVMM_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_NVMM @@ -24,6 +24,6 @@ int nvmm_enabled(void); #endif /* CONFIG_NVMM */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #endif /* QEMU_NVMM_H */ diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 781ca5b2b6..00ff409b68 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -15,7 +15,7 @@ #ifndef QEMU_WHPX_H #define QEMU_WHPX_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_WHPX @@ -29,6 +29,6 @@ bool whpx_apic_in_platform(void); #endif /* CONFIG_WHPX */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #endif /* QEMU_WHPX_H */ diff --git a/include/sysemu/xen.h b/include/sysemu/xen.h index a9f591f26d..754ec2e6cb 100644 --- a/include/sysemu/xen.h +++ b/include/sysemu/xen.h @@ -16,13 +16,13 @@ #include "exec/cpu-common.h" -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET # ifdef CONFIG_XEN # define CONFIG_XEN_IS_POSSIBLE # endif #else # define CONFIG_XEN_IS_POSSIBLE -#endif +#endif /* COMPILING_PER_TARGET */ #ifdef CONFIG_XEN_IS_POSSIBLE diff --git a/meson.build b/meson.build index 553b940999..96fdc6dfd2 100644 --- a/meson.build +++ b/meson.build @@ -3610,7 +3610,7 @@ foreach d, list : target_modules if target.endswith('-softmmu') config_target = config_target_mak[target] target_inc = [include_directories('target' / config_target['TARGET_BASE_ARCH'])] - c_args = ['-DNEED_CPU_H', + c_args = ['-DCOMPILING_PER_TARGET', '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), '-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] target_module_ss = module_ss.apply(config_target, strict: false) @@ -3793,7 +3793,7 @@ foreach target : target_dirs target_base_arch = config_target['TARGET_BASE_ARCH'] arch_srcs = [config_target_h[target]] arch_deps = [] - c_args = ['-DNEED_CPU_H', + c_args = ['-DCOMPILING_PER_TARGET', '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), '-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] link_args = emulator_link_args diff --git a/scripts/analyze-inclusions b/scripts/analyze-inclusions index 45c821de32..b6280f25c8 100644 --- a/scripts/analyze-inclusions +++ b/scripts/analyze-inclusions @@ -92,7 +92,7 @@ echo trace/generated-tracers.h: analyze -include ../include/qemu/osdep.h trace/generated-tracers.h echo target/i386/cpu.h: -analyze -DNEED_CPU_H -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../target/i386/cpu.h +analyze -DCOMPILING_PER_TARGET -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../target/i386/cpu.h -echo hw/hw.h + NEED_CPU_H: -analyze -DNEED_CPU_H -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../include/hw/hw.h +echo hw/hw.h + COMPILING_PER_TARGET: +analyze -DCOMPILING_PER_TARGET -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../include/hw/hw.h diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 7c6adc14f6..c44d23dbe7 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -14,13 +14,13 @@ #ifndef ARM_KVM_CONSTS_H #define ARM_KVM_CONSTS_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_KVM #include #include #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(X != Y) #endif -#endif +#endif /* COMPILING_PER_TARGET */ #ifndef MISMATCH_CHECK #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(0) From 51579d40f9c719361ec9355bb48386e5d3ce85c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Sep 2023 21:40:07 +0200 Subject: [PATCH 0218/2884] exec: Reduce tlb_set_dirty() declaration scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tlb_set_dirty() is only used in accel/tcg/cputlb.c, where it is defined. Declare it statically, removing the stub. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-11-philmd@linaro.org> --- accel/stubs/tcg-stub.c | 4 ---- accel/tcg/cputlb.c | 2 +- include/exec/exec-all.h | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index 8a496a2a6f..dd890d6cf6 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -18,10 +18,6 @@ void tb_flush(CPUState *cpu) { } -void tlb_set_dirty(CPUState *cpu, vaddr vaddr) -{ -} - int probe_access_flags(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool nonfault, void **phost, uintptr_t retaddr) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 93b1ca810b..e16d02a62c 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1039,7 +1039,7 @@ static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry, /* update the TLB corresponding to virtual page vaddr so that it is no longer dirty */ -void tlb_set_dirty(CPUState *cpu, vaddr addr) +static void tlb_set_dirty(CPUState *cpu, vaddr addr) { int mmu_idx; diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 3e53501691..9599e16a09 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -654,7 +654,6 @@ static inline void mmap_unlock(void) {} #define WITH_MMAP_LOCK_GUARD() void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); -void tlb_set_dirty(CPUState *cpu, vaddr addr); void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); MemoryRegionSection * From 8501048b501aec0d2d422aafd713348c235d8b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Dec 2023 23:24:03 +0100 Subject: [PATCH 0219/2884] exec: Include 'cpu.h' before validating CPUArchState placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPUArchState 'env' field is defined within the ArchCPU structure, so we need to include each target "cpu.h" header which defines it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Message-Id: <20231211212003.21686-2-philmd@linaro.org> --- include/exec/cpu-all.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 1a6510fd3b..b86209fc49 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -391,6 +391,7 @@ static inline bool tlb_hit(uint64_t tlb_addr, vaddr addr) #endif /* !CONFIG_USER_ONLY */ /* Validate correct placement of CPUArchState. */ +#include "cpu.h" QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); From 75bbe6a4d2bc9c3681ab71021645d655ad045a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Dec 2023 10:41:27 +0100 Subject: [PATCH 0220/2884] exec: Expose 'target_page.h' API to user emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-only objects might benefit from the "exec/target_page.h" API, which allows to build some objects once for all targets. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Reviewed-by: Richard Henderson Message-Id: <20231211212003.21686-3-philmd@linaro.org> --- meson.build | 2 +- page-target.c | 44 ++++++++++++++++++++++++++++++++++++++++++ system/physmem.c | 30 ---------------------------- target/meson.build | 2 -- target/target-common.c | 10 ---------- 5 files changed, 45 insertions(+), 43 deletions(-) create mode 100644 page-target.c delete mode 100644 target/target-common.c diff --git a/meson.build b/meson.build index 96fdc6dfd2..5db2dbc12e 100644 --- a/meson.build +++ b/meson.build @@ -3523,7 +3523,7 @@ if get_option('b_lto') pagevary = declare_dependency(link_with: pagevary) endif common_ss.add(pagevary) -specific_ss.add(files('page-vary-target.c')) +specific_ss.add(files('page-target.c', 'page-vary-target.c')) subdir('backends') subdir('disas') diff --git a/page-target.c b/page-target.c new file mode 100644 index 0000000000..82211c8593 --- /dev/null +++ b/page-target.c @@ -0,0 +1,44 @@ +/* + * QEMU page values getters (target independent) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "exec/target_page.h" +#include "exec/cpu-defs.h" +#include "cpu.h" +#include "exec/cpu-all.h" + +size_t qemu_target_page_size(void) +{ + return TARGET_PAGE_SIZE; +} + +int qemu_target_page_mask(void) +{ + return TARGET_PAGE_MASK; +} + +int qemu_target_page_bits(void) +{ + return TARGET_PAGE_BITS; +} + +int qemu_target_page_bits_min(void) +{ + return TARGET_PAGE_BITS_MIN; +} + +/* Convert target pages to MiB (2**20). */ +size_t qemu_target_pages_to_MiB(size_t pages) +{ + int page_bits = TARGET_PAGE_BITS; + + /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ + g_assert(page_bits < 20); + + return pages >> (20 - page_bits); +} diff --git a/system/physmem.c b/system/physmem.c index c3d04ca921..1a81c226ba 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3540,36 +3540,6 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, return 0; } -/* - * Allows code that needs to deal with migration bitmaps etc to still be built - * target independent. - */ -size_t qemu_target_page_size(void) -{ - return TARGET_PAGE_SIZE; -} - -int qemu_target_page_bits(void) -{ - return TARGET_PAGE_BITS; -} - -int qemu_target_page_bits_min(void) -{ - return TARGET_PAGE_BITS_MIN; -} - -/* Convert target pages to MiB (2**20). */ -size_t qemu_target_pages_to_MiB(size_t pages) -{ - int page_bits = TARGET_PAGE_BITS; - - /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ - g_assert(page_bits < 20); - - return pages >> (20 - page_bits); -} - bool cpu_physical_memory_is_io(hwaddr phys_addr) { MemoryRegion*mr; diff --git a/target/meson.build b/target/meson.build index 59b46b2ef4..1c2e6f2b19 100644 --- a/target/meson.build +++ b/target/meson.build @@ -18,5 +18,3 @@ subdir('sh4') subdir('sparc') subdir('tricore') subdir('xtensa') - -specific_ss.add(files('target-common.c')) diff --git a/target/target-common.c b/target/target-common.c deleted file mode 100644 index 903b10cfe4..0000000000 --- a/target/target-common.c +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "qemu/osdep.h" - -#include "cpu.h" -#include "exec/target_page.h" - -int qemu_target_page_mask(void) -{ - return TARGET_PAGE_MASK; -} From 4597463b3851d9f6ec22542b6645511d7f889f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Dec 2023 23:02:11 +0100 Subject: [PATCH 0221/2884] accel: Include missing 'exec/cpu_ldst.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Theses files call cpu_ldl_code() which is declared in "exec/cpu_ldst.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231211212003.21686-5-philmd@linaro.org> --- accel/tcg/translator.c | 1 + target/hexagon/translate.c | 1 + target/microblaze/cpu.c | 1 + target/microblaze/translate.c | 1 + 4 files changed, 4 insertions(+) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 9de0bc34c8..6832e55135 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -12,6 +12,7 @@ #include "qemu/error-report.h" #include "exec/exec-all.h" #include "exec/translator.h" +#include "exec/cpu_ldst.h" #include "exec/plugin-gen.h" #include "tcg/tcg-op-common.h" #include "internal-target.h" diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index f163eefe97..47a870f42d 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -23,6 +23,7 @@ #include "exec/helper-gen.h" #include "exec/helper-proto.h" #include "exec/translation-block.h" +#include "exec/cpu_ldst.h" #include "exec/log.h" #include "internal.h" #include "attribs.h" diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index f8dc3173fc..9eb7374ccd 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -28,6 +28,7 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/gdbstub.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index fc451befae..6d89c1a175 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" From fe7667343c81bffc5f87e591589c691faa84286a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 7 Dec 2023 16:52:16 +0100 Subject: [PATCH 0222/2884] gdbstub: Include missing 'hw/core/cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions such gdb_get_cpu_pid() dereference CPUState so require the structure declaration from "hw/core/cpu.h": static uint32_t gdb_get_cpu_pid(CPUState *cpu) { ... return cpu->cluster_index + 1; } Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Warner Losh Message-Id: <20231211212003.21686-15-philmd@linaro.org> Reviewed-by: Richard Henderson --- gdbstub/gdbstub.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 9c23d44baf..9c2b8b5d0a 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -37,6 +37,7 @@ #include "hw/cpu/cluster.h" #include "hw/boards.h" #endif +#include "hw/core/cpu.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" From 94326e4f217991102770667f684156bdbef599e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Mar 2024 16:11:47 +0100 Subject: [PATCH 0223/2884] gdbstub: Simplify #ifdef'ry in helpers.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slightly simplify by checking NEED_CPU_H definition in header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240322161439.6448-2-philmd@linaro.org> --- include/gdbstub/helpers.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h index 6b97610f48..6277a858a1 100644 --- a/include/gdbstub/helpers.h +++ b/include/gdbstub/helpers.h @@ -12,7 +12,10 @@ #ifndef _GDBSTUB_HELPERS_H_ #define _GDBSTUB_HELPERS_H_ -#ifdef COMPILING_PER_TARGET +#ifndef COMPILING_PER_TARGET +#error "gdbstub helpers should only be included by target specific code" +#endif + #include "cpu.h" /* @@ -96,8 +99,4 @@ static inline uint8_t *gdb_get_reg_ptr(GByteArray *buf, int len) #define ldtul_p(addr) ldl_p(addr) #endif -#else -#error "gdbstub helpers should only be included by target specific code" -#endif - #endif /* _GDBSTUB_HELPERS_H_ */ From 0654c79416f346d967fbc7dad7ca451b49bbd822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 18 Apr 2024 16:45:33 +0200 Subject: [PATCH 0224/2884] gdbstub: Avoid including 'cpu.h' in 'gdbstub/helpers.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only need the "exec/tswap.h" and "cpu-param.h" headers. Only include "cpu.h" in the target gdbstub.c source files. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-20-philmd@linaro.org> --- include/gdbstub/helpers.h | 3 ++- target/avr/gdbstub.c | 1 + target/tricore/gdbstub.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h index 6277a858a1..26140ef1ac 100644 --- a/include/gdbstub/helpers.h +++ b/include/gdbstub/helpers.h @@ -16,7 +16,8 @@ #error "gdbstub helpers should only be included by target specific code" #endif -#include "cpu.h" +#include "exec/tswap.h" +#include "cpu-param.h" /* * The GDB remote protocol transfers values in target byte order. As diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c index 2eeee2bf4e..d6d3c1479b 100644 --- a/target/avr/gdbstub.c +++ b/target/avr/gdbstub.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "gdbstub/helpers.h" +#include "cpu.h" int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { diff --git a/target/tricore/gdbstub.c b/target/tricore/gdbstub.c index f9309c5e27..29a70051ff 100644 --- a/target/tricore/gdbstub.c +++ b/target/tricore/gdbstub.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "gdbstub/helpers.h" +#include "cpu.h" #define LCX_REGNUM 32 From 3aac8abaca536db275da671db088fef2dd82536d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Dec 2023 16:11:15 +0100 Subject: [PATCH 0225/2884] semihosting/uaccess: Avoid including 'cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "semihosting/uaccess.h" only requires the following headers: - "exec/cpu-defs.h" for target_ulong, - "exec/cpu-common.h" for cpu_memory_rw_debug() - "exec/tswap.h" for tswap32() and tswap64(). Include them instead of the huge "cpu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <42c6471e-8383-45e0-85ee-e20ca32ecbad@linaro.org> --- include/semihosting/uaccess.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h index 3963eafc3e..dd289af8dd 100644 --- a/include/semihosting/uaccess.h +++ b/include/semihosting/uaccess.h @@ -14,7 +14,9 @@ #error Cannot include semihosting/uaccess.h from user emulation #endif -#include "cpu.h" +#include "exec/cpu-common.h" +#include "exec/cpu-defs.h" +#include "exec/tswap.h" #define get_user_u64(val, addr) \ ({ uint64_t val_ = 0; \ From 83fb360d6a60b0a77dce3d3643d1a5311a235e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Dec 2023 16:12:12 +0100 Subject: [PATCH 0226/2884] semihosting/guestfd: Remove unused 'semihosting/uaccess.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing in guestfd.c requires "semihosting/uaccess.h" nor "qemu.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20231212123401.37493-8-philmd@linaro.org> Reviewed-by: Richard Henderson --- semihosting/guestfd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c index 955c2efbd0..d3241434c5 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -12,10 +12,7 @@ #include "gdbstub/syscalls.h" #include "semihosting/semihost.h" #include "semihosting/guestfd.h" -#ifdef CONFIG_USER_ONLY -#include "qemu.h" -#else -#include "semihosting/uaccess.h" +#ifndef CONFIG_USER_ONLY #include CONFIG_DEVICES #endif From e92dd33224603ee5a42e0b13b6e055691325ba47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Dec 2023 14:31:59 +0100 Subject: [PATCH 0227/2884] target: Define TCG_GUEST_DEFAULT_MO in 'cpu-param.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit accel/tcg/ files requires the following definitions: - TARGET_LONG_BITS - TARGET_PAGE_BITS - TARGET_PHYS_ADDR_SPACE_BITS - TCG_GUEST_DEFAULT_MO The first 3 are defined in "cpu-param.h". The last one in "cpu.h", with a bunch of definitions irrelevant for TCG. By moving the TCG_GUEST_DEFAULT_MO definition to "cpu-param.h", we can simplify various accel/tcg includes. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Acked-by: Nicholas Piggin Message-Id: <20231211212003.21686-4-philmd@linaro.org> --- target/alpha/cpu-param.h | 3 +++ target/alpha/cpu.h | 3 --- target/arm/cpu-param.h | 8 +++++--- target/arm/cpu.h | 3 --- target/avr/cpu-param.h | 2 ++ target/avr/cpu.h | 2 -- target/hppa/cpu-param.h | 8 ++++++++ target/hppa/cpu.h | 6 ------ target/i386/cpu-param.h | 3 +++ target/i386/cpu.h | 3 --- target/loongarch/cpu-param.h | 2 ++ target/loongarch/cpu.h | 2 -- target/microblaze/cpu-param.h | 3 +++ target/microblaze/cpu.h | 3 --- target/mips/cpu-param.h | 2 ++ target/mips/cpu.h | 2 -- target/openrisc/cpu-param.h | 2 ++ target/openrisc/cpu.h | 2 -- target/ppc/cpu-param.h | 2 ++ target/ppc/cpu.h | 2 -- target/riscv/cpu-param.h | 2 ++ target/riscv/cpu.h | 2 -- target/s390x/cpu-param.h | 6 ++++++ target/s390x/cpu.h | 3 --- target/sparc/cpu-param.h | 23 +++++++++++++++++++++++ target/sparc/cpu.h | 23 ----------------------- target/xtensa/cpu-param.h | 3 +++ target/xtensa/cpu.h | 3 --- 28 files changed, 66 insertions(+), 62 deletions(-) diff --git a/target/alpha/cpu-param.h b/target/alpha/cpu-param.h index c969cb016b..5ce213a9a1 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -27,4 +27,7 @@ # define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) #endif +/* Alpha processors have a weak memory model */ +#define TCG_GUEST_DEFAULT_MO (0) + #endif diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 7188a409a0..f9e2ecb90a 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -24,9 +24,6 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" -/* Alpha processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - #define ICACHE_LINE_SIZE 32 #define DCACHE_LINE_SIZE 32 diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index da3243ab21..2d5f3aa312 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -27,14 +27,16 @@ # else # define TARGET_PAGE_BITS 12 # endif -#else +#else /* !CONFIG_USER_ONLY */ /* * ARMv7 and later CPUs have 4K pages minimum, but ARMv5 and v6 * have to support 1K tiny pages. */ # define TARGET_PAGE_BITS_VARY # define TARGET_PAGE_BITS_MIN 10 - -#endif +#endif /* !CONFIG_USER_ONLY */ + +/* ARM processors have a weak memory model */ +#define TCG_GUEST_DEFAULT_MO (0) #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 97997dbd08..17efc5d565 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -30,9 +30,6 @@ #include "target/arm/multiprocessing.h" #include "target/arm/gtimer.h" -/* ARM processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - #ifdef TARGET_AARCH64 #define KVM_HAVE_MCE_INJECTION 1 #endif diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 9a92bc74fc..93c2f470d0 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -32,4 +32,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 24 #define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define TCG_GUEST_DEFAULT_MO 0 + #endif diff --git a/target/avr/cpu.h b/target/avr/cpu.h index d185d20dcb..4725535102 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -30,8 +30,6 @@ #define CPU_RESOLVING_TYPE TYPE_AVR_CPU -#define TCG_GUEST_DEFAULT_MO 0 - /* * AVR has two memory spaces, data & code. * e.g. both have 0 address diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index bb3d7ef6f7..473d489f01 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -21,4 +21,12 @@ #define TARGET_PAGE_BITS 12 +/* PA-RISC 1.x processors have a strong memory model. */ +/* + * ??? While we do not yet implement PA-RISC 2.0, those processors have + * a weak memory model, but with TLB bits that force ordering on a per-page + * basis. It's probably easier to fall back to a strong memory model. + */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index a072d0bb63..fb2e4c4a98 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -25,12 +25,6 @@ #include "qemu/cpu-float.h" #include "qemu/interval-tree.h" -/* PA-RISC 1.x processors have a strong memory model. */ -/* ??? While we do not yet implement PA-RISC 2.0, those processors have - a weak memory model, but with TLB bits that force ordering on a per-page - basis. It's probably easier to fall back to a strong memory model. */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #define MMU_ABS_W_IDX 6 #define MMU_ABS_IDX 7 #define MMU_KERNEL_IDX 8 diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index 911b4cd51b..5e15335203 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -24,4 +24,7 @@ #endif #define TARGET_PAGE_BITS 12 +/* The x86 has a strong memory model with some store-after-load re-ordering */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 6112e27bfd..565c7a98c3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -30,9 +30,6 @@ #define XEN_NR_VIRQS 24 -/* The x86 has a strong memory model with some store-after-load re-ordering */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - #define KVM_HAVE_MCE_INJECTION 1 /* support for self modifying code even if the modified instruction is diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index cfe195db4e..db5ad1c69f 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -14,4 +14,6 @@ #define TARGET_PAGE_BITS 12 +#define TCG_GUEST_DEFAULT_MO (0) + #endif diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index ec37579fd6..abb01b2cc7 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -39,8 +39,6 @@ #define IOCSR_MEM_SIZE 0x428 -#define TCG_GUEST_DEFAULT_MO (0) - #define FCSR0_M1 0x1f /* FCSR1 mask, Enables */ #define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */ #define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */ diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index 9770b0eb52..e530fead1c 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -29,4 +29,7 @@ /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 +/* MicroBlaze is always in-order. */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index c0c7574dbd..3e5a3e5c60 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -24,9 +24,6 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" -/* MicroBlaze is always in-order. */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - typedef struct CPUArchState CPUMBState; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index 594c91a156..6f6ac1688f 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -30,4 +30,6 @@ #define TARGET_PAGE_BITS_MIN 12 #endif +#define TCG_GUEST_DEFAULT_MO (0) + #endif diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 7329226d39..3e906a175a 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -10,8 +10,6 @@ #include "hw/clock.h" #include "mips-defs.h" -#define TCG_GUEST_DEFAULT_MO (0) - typedef struct CPUMIPSTLBContext CPUMIPSTLBContext; /* MSA Context */ diff --git a/target/openrisc/cpu-param.h b/target/openrisc/cpu-param.h index 3f08207485..fbfc0f568b 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -13,4 +13,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TCG_GUEST_DEFAULT_MO (0) + #endif diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index b1b7db5cbd..c9fe9ae12d 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -24,8 +24,6 @@ #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" -#define TCG_GUEST_DEFAULT_MO (0) - /** * OpenRISCCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index b7ad52de03..77c5ed9a67 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -40,4 +40,6 @@ # define TARGET_PAGE_BITS 12 #endif +#define TCG_GUEST_DEFAULT_MO 0 + #endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 67e6b2effd..0ac55d6b25 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -29,8 +29,6 @@ #define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU -#define TCG_GUEST_DEFAULT_MO 0 - #define TARGET_PAGE_BITS_64K 16 #define TARGET_PAGE_BITS_16M 24 diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h index b2a9396dec..1fbd64939d 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -28,4 +28,6 @@ * - M mode HLV/HLVX/HSV 0b111 */ +#define TCG_GUEST_DEFAULT_MO 0 + #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 3b1a02b944..2d0c02c35b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -43,8 +43,6 @@ typedef struct CPUArchState CPURISCVState; # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 #endif -#define TCG_GUEST_DEFAULT_MO 0 - /* * RISC-V-specific extra insn start words: * 1: Original instruction opcode diff --git a/target/s390x/cpu-param.h b/target/s390x/cpu-param.h index 84ca08626b..11d23b600d 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -13,4 +13,10 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 +/* + * The z/Architecture has a strong memory model with some + * store-after-load re-ordering. + */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + #endif diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 43a46a5a06..414680eed1 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -33,9 +33,6 @@ #define ELF_MACHINE_UNAME "S390X" -/* The z/Architecture has a strong memory model with some store-after-load re-ordering */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - #define TARGET_HAS_PRECISE_SMC #define TARGET_INSN_START_EXTRA_WORDS 2 diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h index cb11980404..82293fb844 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -23,4 +23,27 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +/* + * From Oracle SPARC Architecture 2015: + * + * Compatibility notes: The PSO memory model described in SPARC V8 and + * SPARC V9 compatibility architecture specifications was never implemented + * in a SPARC V9 implementation and is not included in the Oracle SPARC + * Architecture specification. + * + * The RMO memory model described in the SPARC V9 specification was + * implemented in some non-Sun SPARC V9 implementations, but is not + * directly supported in Oracle SPARC Architecture 2015 implementations. + * + * Therefore always use TSO in QEMU. + * + * D.5 Specification of Partial Store Order (PSO) + * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore. + * + * D.6 Specification of Total Store Order (TSO) + * ... PSO with the additional requirement that all [stores] are followed + * by an implied MEMBAR #StoreStore. + */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST) + #endif diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index f3cdd17c62..dfd9512a21 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -6,29 +6,6 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" -/* - * From Oracle SPARC Architecture 2015: - * - * Compatibility notes: The PSO memory model described in SPARC V8 and - * SPARC V9 compatibility architecture specifications was never implemented - * in a SPARC V9 implementation and is not included in the Oracle SPARC - * Architecture specification. - * - * The RMO memory model described in the SPARC V9 specification was - * implemented in some non-Sun SPARC V9 implementations, but is not - * directly supported in Oracle SPARC Architecture 2015 implementations. - * - * Therefore always use TSO in QEMU. - * - * D.5 Specification of Partial Store Order (PSO) - * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore. - * - * D.6 Specification of Total Store Order (TSO) - * ... PSO with the additional requirement that all [stores] are followed - * by an implied MEMBAR #StoreStore. - */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST) - #if !defined(TARGET_SPARC64) #define TARGET_DPREGS 16 #define TARGET_FCCREGS 1 diff --git a/target/xtensa/cpu-param.h b/target/xtensa/cpu-param.h index b1da0555de..0000725f2f 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -17,4 +17,7 @@ #define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +/* Xtensa processors have a weak memory model */ +#define TCG_GUEST_DEFAULT_MO (0) + #endif diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 6b8d0636d2..9f2341d856 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -34,9 +34,6 @@ #include "hw/clock.h" #include "xtensa-isa.h" -/* Xtensa processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - enum { /* Additional instructions */ XTENSA_OPTION_CODE_DENSITY, From eedd109525e12f435cf36a4a43e73bc38005caf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Dec 2023 15:29:19 +0100 Subject: [PATCH 0228/2884] target/ppc/excp_helper: Avoid 'abi_ptr' in system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'abi_ptr' is a user specific type. The system emulation equivalent is 'target_ulong'. Use it in ppc_ldl_code() to emphasis this is not an user emulation function. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Nicholas Piggin Reviewed-by: Thomas Huth Message-Id: <20231211212003.21686-18-philmd@linaro.org> Reviewed-by: Richard Henderson --- target/ppc/excp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 674c05a2ce..0712098cf7 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -142,7 +142,7 @@ static inline bool insn_need_byteswap(CPUArchState *env) return !!(env->msr & ((target_ulong)1 << MSR_LE)); } -static uint32_t ppc_ldl_code(CPUArchState *env, abi_ptr addr) +static uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) { uint32_t insn = cpu_ldl_code(env, addr); From 9b21d29acfc9b516c6785e7ca73bff735ebf3d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 18 Apr 2024 13:17:47 +0100 Subject: [PATCH 0229/2884] target/sparc: Replace abi_ulong by uint32_t for TARGET_ABI32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have abi_ulong == uint32_t for the 32-bit ABI. Use the generic type to avoid to depend on the "exec/user/abitypes.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-14-philmd@linaro.org> --- target/sparc/gdbstub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index 07ea81ab5f..ec0036e9ef 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -108,7 +108,7 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; #if defined(TARGET_ABI32) - abi_ulong tmp; + uint32_t tmp; tmp = ldl_p(mem_buf); #else From 7e17a524698809ea0cd2b848eab5516a480af75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 1 Dec 2023 18:58:00 +0100 Subject: [PATCH 0230/2884] target/i386: Include missing 'exec/exec-all.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XRSTOR instruction ends calling tlb_flush(), declared in "exec/exec-all.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231211212003.21686-13-philmd@linaro.org> --- target/i386/tcg/fpu_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 4b965a5d6c..ece22a3553 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -21,6 +21,7 @@ #include #include "cpu.h" #include "tcg-cpu.h" +#include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" From 3b28c27067dd577a3ea137724c4247c8356915d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Dec 2023 22:40:15 +0100 Subject: [PATCH 0231/2884] accel/tcg: Un-inline retaddr helpers to 'user-retaddr.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit set_helper_retaddr() is only used in accel/tcg/user-exec.c. clear_helper_retaddr() is only used in accel/tcg/cpu-exec.c and accel/tcg/user-exec.c. No need to expose their definitions to all user-emulation files including "exec/cpu_ldst.h", move them to a new "user-retaddr.h" header (restricted to accel/tcg/). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231211212003.21686-19-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 3 +++ accel/tcg/user-exec.c | 1 + accel/tcg/user-retaddr.h | 28 ++++++++++++++++++++++++++++ include/exec/cpu_ldst.h | 28 ++-------------------------- 4 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 accel/tcg/user-retaddr.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5c70748060..225e5fbd3e 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -41,6 +41,9 @@ #include "tb-context.h" #include "internal-common.h" #include "internal-target.h" +#if defined(CONFIG_USER_ONLY) +#include "user-retaddr.h" +#endif /* -icount align implementation. */ diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 3cac3a78c4..1c621477ad 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -31,6 +31,7 @@ #include "tcg/tcg-ldst.h" #include "internal-common.h" #include "internal-target.h" +#include "user-retaddr.h" __thread uintptr_t helper_retaddr; diff --git a/accel/tcg/user-retaddr.h b/accel/tcg/user-retaddr.h new file mode 100644 index 0000000000..e0f57e1994 --- /dev/null +++ b/accel/tcg/user-retaddr.h @@ -0,0 +1,28 @@ +#ifndef ACCEL_TCG_USER_RETADDR_H +#define ACCEL_TCG_USER_RETADDR_H + +#include "qemu/atomic.h" + +extern __thread uintptr_t helper_retaddr; + +static inline void set_helper_retaddr(uintptr_t ra) +{ + helper_retaddr = ra; + /* + * Ensure that this write is visible to the SIGSEGV handler that + * may be invoked due to a subsequent invalid memory operation. + */ + signal_barrier(); +} + +static inline void clear_helper_retaddr(void) +{ + /* + * Ensure that previous memory operations have succeeded before + * removing the data visible to the signal handler. + */ + signal_barrier(); + helper_retaddr = 0; +} + +#endif diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index eb8f3f0595..82690d3947 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -300,31 +300,7 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, abi_ptr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); -#if defined(CONFIG_USER_ONLY) - -extern __thread uintptr_t helper_retaddr; - -static inline void set_helper_retaddr(uintptr_t ra) -{ - helper_retaddr = ra; - /* - * Ensure that this write is visible to the SIGSEGV handler that - * may be invoked due to a subsequent invalid memory operation. - */ - signal_barrier(); -} - -static inline void clear_helper_retaddr(void) -{ - /* - * Ensure that previous memory operations have succeeded before - * removing the data visible to the signal handler. - */ - signal_barrier(); - helper_retaddr = 0; -} - -#else +#if !defined(CONFIG_USER_ONLY) #include "tcg/oversized-guest.h" @@ -376,7 +352,7 @@ static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx, return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; } -#endif /* defined(CONFIG_USER_ONLY) */ +#endif /* !defined(CONFIG_USER_ONLY) */ #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data From 6bba316e2333462aa8e93eda6908c06db3e4727a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 1 Dec 2023 16:15:27 +0100 Subject: [PATCH 0232/2884] accel/tcg: Include missing 'hw/core/cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tcg_cpu_init_cflags() accesses CPUState fields, so requires "hw/core/cpu.h" to get its structure definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20231212123401.37493-12-philmd@linaro.org> Reviewed-by: Richard Henderson --- accel/tcg/tcg-accel-ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 9c957f421c..2c7b0cc09e 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -37,6 +37,8 @@ #include "exec/tb-flush.h" #include "exec/gdbstub.h" +#include "hw/core/cpu.h" + #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" #include "tcg-accel-ops-rr.h" From 893b4bde885c1f02edbe9d9533919abb0ac50490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 10 Jan 2024 15:49:40 +0100 Subject: [PATCH 0233/2884] accel/tcg: Include missing headers in 'tb-jmp-cache.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to missing headers, when including "tb-jmp-cache.h" we might get: accel/tcg/tb-jmp-cache.h:21:21: error: field ‘rcu’ has incomplete type 21 | struct rcu_head rcu; | ^~~ accel/tcg/tb-jmp-cache.h:24:9: error: unknown type name ‘vaddr’ 24 | vaddr pc; | ^~~~~ Add the missing "qemu/rcu.h" and "exec/cpu-common.h" headers. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20240111162442.43755-1-philmd@linaro.org> --- accel/tcg/tb-jmp-cache.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h index 4ab8553afc..184bb3e3e2 100644 --- a/accel/tcg/tb-jmp-cache.h +++ b/accel/tcg/tb-jmp-cache.h @@ -9,6 +9,9 @@ #ifndef ACCEL_TCG_TB_JMP_CACHE_H #define ACCEL_TCG_TB_JMP_CACHE_H +#include "qemu/rcu.h" +#include "exec/cpu-common.h" + #define TB_JMP_CACHE_BITS 12 #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) From e4751d340a49b117b90a411b179b8c892cf43d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 25 Mar 2024 15:30:33 +0100 Subject: [PATCH 0234/2884] accel/tcg: Rename load-extract/store-insert headers using .h.inc suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented in the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore rename 'store-insert-al16.h' as 'store-insert-al16.h.inc' and 'load-extract-al16-al8.h' as 'load-extract-al16-al8.h.inc'. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20240424173333.96148-3-philmd@linaro.org> --- accel/tcg/ldst_atomicity.c.inc | 4 ++-- .../{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} | 0 .../host/{store-insert-al16.h => store-insert-al16.h.inc} | 0 .../{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} | 0 .../host/{store-insert-al16.h => store-insert-al16.h.inc} | 0 .../{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} | 0 .../host/{store-insert-al16.h => store-insert-al16.h.inc} | 0 .../{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename host/include/aarch64/host/{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} (100%) rename host/include/aarch64/host/{store-insert-al16.h => store-insert-al16.h.inc} (100%) rename host/include/generic/host/{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} (100%) rename host/include/generic/host/{store-insert-al16.h => store-insert-al16.h.inc} (100%) rename host/include/loongarch64/host/{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} (100%) rename host/include/loongarch64/host/{store-insert-al16.h => store-insert-al16.h.inc} (100%) rename host/include/x86_64/host/{load-extract-al16-al8.h => load-extract-al16-al8.h.inc} (100%) diff --git a/accel/tcg/ldst_atomicity.c.inc b/accel/tcg/ldst_atomicity.c.inc index 97dae70d53..134da3c1da 100644 --- a/accel/tcg/ldst_atomicity.c.inc +++ b/accel/tcg/ldst_atomicity.c.inc @@ -9,8 +9,8 @@ * See the COPYING file in the top-level directory. */ -#include "host/load-extract-al16-al8.h" -#include "host/store-insert-al16.h" +#include "host/load-extract-al16-al8.h.inc" +#include "host/store-insert-al16.h.inc" #ifdef CONFIG_ATOMIC64 # define HAVE_al8 true diff --git a/host/include/aarch64/host/load-extract-al16-al8.h b/host/include/aarch64/host/load-extract-al16-al8.h.inc similarity index 100% rename from host/include/aarch64/host/load-extract-al16-al8.h rename to host/include/aarch64/host/load-extract-al16-al8.h.inc diff --git a/host/include/aarch64/host/store-insert-al16.h b/host/include/aarch64/host/store-insert-al16.h.inc similarity index 100% rename from host/include/aarch64/host/store-insert-al16.h rename to host/include/aarch64/host/store-insert-al16.h.inc diff --git a/host/include/generic/host/load-extract-al16-al8.h b/host/include/generic/host/load-extract-al16-al8.h.inc similarity index 100% rename from host/include/generic/host/load-extract-al16-al8.h rename to host/include/generic/host/load-extract-al16-al8.h.inc diff --git a/host/include/generic/host/store-insert-al16.h b/host/include/generic/host/store-insert-al16.h.inc similarity index 100% rename from host/include/generic/host/store-insert-al16.h rename to host/include/generic/host/store-insert-al16.h.inc diff --git a/host/include/loongarch64/host/load-extract-al16-al8.h b/host/include/loongarch64/host/load-extract-al16-al8.h.inc similarity index 100% rename from host/include/loongarch64/host/load-extract-al16-al8.h rename to host/include/loongarch64/host/load-extract-al16-al8.h.inc diff --git a/host/include/loongarch64/host/store-insert-al16.h b/host/include/loongarch64/host/store-insert-al16.h.inc similarity index 100% rename from host/include/loongarch64/host/store-insert-al16.h rename to host/include/loongarch64/host/store-insert-al16.h.inc diff --git a/host/include/x86_64/host/load-extract-al16-al8.h b/host/include/x86_64/host/load-extract-al16-al8.h.inc similarity index 100% rename from host/include/x86_64/host/load-extract-al16-al8.h rename to host/include/x86_64/host/load-extract-al16-al8.h.inc From 2379866c3bf7c5fd654cef64246af9d8a03f7d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 22 Apr 2024 16:41:04 +0200 Subject: [PATCH 0235/2884] accel/tcg: Rename helper-head.h -> helper-head.h.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 139c1837db ("meson: rename included C source files to .c.inc"), QEMU standard procedure for included C files is to use *.c.inc. Besides, since commit 6a0057aa22 ("docs/devel: make a statement about includes") this is documented in the Coding Style: If you do use template header files they should be named with the ``.c.inc`` or ``.h.inc`` suffix to make it clear they are being included for expansion. Therefore rename "exec/helper-head.h" as "exec/helper-head.h.inc". Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20240424173333.96148-4-philmd@linaro.org> --- include/exec/helper-gen.h.inc | 2 +- include/exec/{helper-head.h => helper-head.h.inc} | 0 include/exec/helper-info.c.inc | 2 +- include/exec/helper-proto.h.inc | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename include/exec/{helper-head.h => helper-head.h.inc} (100%) diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc index c009641517..d9fd3ed72a 100644 --- a/include/exec/helper-gen.h.inc +++ b/include/exec/helper-gen.h.inc @@ -8,7 +8,7 @@ #include "tcg/tcg.h" #include "tcg/helper-info.h" -#include "exec/helper-head.h" +#include "exec/helper-head.h.inc" #define DEF_HELPER_FLAGS_0(name, flags, ret) \ extern TCGHelperInfo glue(helper_info_, name); \ diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h.inc similarity index 100% rename from include/exec/helper-head.h rename to include/exec/helper-head.h.inc diff --git a/include/exec/helper-info.c.inc b/include/exec/helper-info.c.inc index 530d2e6d35..c551736d49 100644 --- a/include/exec/helper-info.c.inc +++ b/include/exec/helper-info.c.inc @@ -7,7 +7,7 @@ #include "tcg/tcg.h" #include "tcg/helper-info.h" -#include "exec/helper-head.h" +#include "exec/helper-head.h.inc" /* * Need one more level of indirection before stringification diff --git a/include/exec/helper-proto.h.inc b/include/exec/helper-proto.h.inc index c3aa666929..f8e57e43ce 100644 --- a/include/exec/helper-proto.h.inc +++ b/include/exec/helper-proto.h.inc @@ -5,7 +5,7 @@ * Define HELPER_H for the header file to be expanded. */ -#include "exec/helper-head.h" +#include "exec/helper-head.h.inc" /* * Work around an issue with --enable-lto, in which GCC's ipa-split pass From 9ad49538c7b7c0672110994d81d687ed42bf3ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 18:16:34 +0200 Subject: [PATCH 0236/2884] accel/whpx: Use accel-specific per-vcpu @dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WHPX has a specific use of the CPUState::vcpu_dirty field (CPUState::vcpu_dirty is not used by common code). To make this field accel-specific, add and use a new @dirty variable in the AccelCPUState structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240424174506.326-2-philmd@linaro.org> --- target/i386/whpx/whpx-all.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 31eec7048c..b08e644517 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -237,6 +237,7 @@ struct AccelCPUState { uint64_t tpr; uint64_t apic_base; bool interruption_pending; + bool dirty; /* Must be the last field as it may have a tail */ WHV_RUN_VP_EXIT_CONTEXT exit_ctx; @@ -839,7 +840,7 @@ static HRESULT CALLBACK whpx_emu_setreg_callback( * The emulator just successfully wrote the register state. We clear the * dirty state so we avoid the double write on resume of the VP. */ - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; return hr; } @@ -1394,7 +1395,7 @@ static int whpx_last_vcpu_stopping(CPUState *cpu) /* Returns the address of the next instruction that is about to be executed. */ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { /* The CPU registers have been modified by other parts of QEMU. */ return cpu_env(cpu)->eip; } else if (exit_context_valid) { @@ -1713,9 +1714,9 @@ static int whpx_vcpu_run(CPUState *cpu) } do { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } if (exclusive_step_mode == WHPX_STEP_NONE) { @@ -2063,9 +2064,9 @@ static int whpx_vcpu_run(CPUState *cpu) static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { whpx_get_registers(cpu); - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } } @@ -2073,20 +2074,20 @@ static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_RESET_STATE); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_FULL_STATE); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } /* @@ -2095,7 +2096,7 @@ static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, void whpx_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -2235,7 +2236,7 @@ int whpx_init_vcpu(CPUState *cpu) } vcpu->interruptable = true; - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; cpu->accel = vcpu; max_vcpu_index = max(max_vcpu_index, cpu->cpu_index); qemu_add_vm_change_state_handler(whpx_cpu_update_state, env); From 79f1926b2dfa25ac47adbdc0748dc5f951b55ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 18:16:49 +0200 Subject: [PATCH 0237/2884] accel/nvmm: Use accel-specific per-vcpu @dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NVMM has a specific use of the CPUState::vcpu_dirty field (CPUState::vcpu_dirty is not used by common code). To make this field accel-specific, add and use a new @dirty variable in the AccelCPUState structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240424174506.326-3-philmd@linaro.org> --- target/i386/nvmm/nvmm-all.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 49a3a3b916..f9cced53b3 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -30,6 +30,7 @@ struct AccelCPUState { struct nvmm_vcpu vcpu; uint8_t tpr; bool stop; + bool dirty; /* Window-exiting for INTs/NMIs. */ bool int_window_exit; @@ -507,7 +508,7 @@ nvmm_io_callback(struct nvmm_io *io) } /* Needed, otherwise infinite loop. */ - current_cpu->vcpu_dirty = false; + current_cpu->accel->dirty = false; } static void @@ -516,7 +517,7 @@ nvmm_mem_callback(struct nvmm_mem *mem) cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write); /* Needed, otherwise infinite loop. */ - current_cpu->vcpu_dirty = false; + current_cpu->accel->dirty = false; } static struct nvmm_assist_callbacks nvmm_callbacks = { @@ -726,9 +727,9 @@ nvmm_vcpu_loop(CPUState *cpu) * Inner VCPU loop. */ do { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { nvmm_set_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } if (qcpu->stop) { @@ -826,32 +827,32 @@ static void do_nvmm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { nvmm_get_registers(cpu); - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } static void do_nvmm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_nvmm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } void nvmm_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { run_on_cpu(cpu, do_nvmm_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -981,7 +982,7 @@ nvmm_init_vcpu(CPUState *cpu) } } - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; cpu->accel = qcpu; return 0; From e620363687d468530e00db59ea00f08e6f67eabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 18:16:59 +0200 Subject: [PATCH 0238/2884] accel/hvf: Use accel-specific per-vcpu @dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HVF has a specific use of the CPUState::vcpu_dirty field (CPUState::vcpu_dirty is not used by common code). To make this field accel-specific, add and use a new @dirty variable in the AccelCPUState structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240424174506.326-4-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 10 +++++----- include/hw/core/cpu.h | 3 +-- include/sysemu/hvf_int.h | 1 + target/arm/hvf/hvf.c | 4 ++-- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86hvf.c | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index d94d41ab6d..40d4187d9d 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -204,15 +204,15 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { hvf_get_registers(cpu); - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } } static void hvf_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -221,7 +221,7 @@ static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu, run_on_cpu_data arg) { /* QEMU state is the reference, push it to HVF now and on next entry */ - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } static void hvf_cpu_synchronize_post_reset(CPUState *cpu) @@ -402,7 +402,7 @@ static int hvf_init_vcpu(CPUState *cpu) #else r = hv_vcpu_create((hv_vcpuid_t *)&cpu->accel->fd, HV_VCPU_DEFAULT); #endif - cpu->vcpu_dirty = 1; + cpu->accel->dirty = true; assert_hvf_ok(r); cpu->accel->guest_debug_enabled = false; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 7f037b158e..cead8f01a6 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -525,6 +525,7 @@ struct CPUState { uint32_t kvm_fetch_index; uint64_t dirty_pages; int kvm_vcpu_stats_fd; + bool vcpu_dirty; /* Use by accel-block: CPU is executing an ioctl() */ QemuLockCnt in_ioctl_lock; @@ -546,8 +547,6 @@ struct CPUState { int32_t exception_index; AccelCPUState *accel; - /* shared by kvm and hvf */ - bool vcpu_dirty; /* Used to keep track of an outstanding cpu throttle thread for migration * autoconverge diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 718beddcdd..4a327fd526 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -55,6 +55,7 @@ struct AccelCPUState { bool vtimer_masked; sigset_t unblock_ipi_mask; bool guest_debug_enabled; + bool dirty; }; void assert_hvf_ok(hv_return_t ret); diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 65a5601804..db628c1cba 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -806,9 +806,9 @@ int hvf_put_registers(CPUState *cpu) static void flush_cpu_state(CPUState *cpu) { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { hvf_put_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } } diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 1ed8ed5154..e493452acb 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -419,9 +419,9 @@ int hvf_vcpu_exec(CPUState *cpu) } do { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { hvf_put_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } if (hvf_inject_interrupts(cpu)) { diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index be2c46246e..1569f860eb 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -427,7 +427,7 @@ int hvf_process_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (!cs->vcpu_dirty) { + if (!cs->accel->dirty) { /* light weight sync for CPU_INTERRUPT_HARD and IF_MASK */ env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } From edfc85875d4926208a86acd8f4f212b664df35ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 2 Dec 2023 20:00:02 +0100 Subject: [PATCH 0239/2884] exec/cpu-all: Reduce 'qemu/rcu.h' header inclusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/cpu-all.h" doesn't need definitions from "qemu/rcu.h", however "exec/ram_addr.h" does. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231211212003.21686-17-philmd@linaro.org> --- include/exec/cpu-all.h | 1 - include/exec/ram_addr.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index b86209fc49..586dc56d9e 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -24,7 +24,6 @@ #include "exec/tswap.h" #include "qemu/thread.h" #include "hw/core/cpu.h" -#include "qemu/rcu.h" /* some important defines: * diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 07c8f86375..891c44cf2d 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -26,6 +26,7 @@ #include "exec/ramlist.h" #include "exec/ramblock.h" #include "exec/exec-all.h" +#include "qemu/rcu.h" extern uint64_t total_dirty_pages; From c8f7bbb773ec815aa49c15abd87ba8b02a14add3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 2 Dec 2023 20:01:07 +0100 Subject: [PATCH 0240/2884] exec/cpu-all: Remove unused 'qemu/thread.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing is required from "qemu/thread.h" in "exec/cpu-all.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20231212123401.37493-13-philmd@linaro.org> Reviewed-by: Richard Henderson --- include/exec/cpu-all.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 586dc56d9e..4de0d5a0d7 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -22,7 +22,6 @@ #include "exec/cpu-common.h" #include "exec/memory.h" #include "exec/tswap.h" -#include "qemu/thread.h" #include "hw/core/cpu.h" /* some important defines: From 77166c4568eb7cbd81afeecf4975c607b734f1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Dec 2023 11:18:13 +0100 Subject: [PATCH 0241/2884] exec/cpu-all: Remove unused tswapls() definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last use of tswapls() was removed 2 years ago in commit aee14c77f4 ("linux-user: Rewrite do_getdents, do_getdents64"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20231212123401.37493-15-philmd@linaro.org> Reviewed-by: Richard Henderson --- include/exec/cpu-all.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 4de0d5a0d7..7c44ffb3af 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -38,11 +38,9 @@ #if TARGET_LONG_SIZE == 4 #define tswapl(s) tswap32(s) -#define tswapls(s) tswap32s((uint32_t *)(s)) #define bswaptls(s) bswap32s(s) #else #define tswapl(s) tswap64(s) -#define tswapls(s) tswap64s((uint64_t *)(s)) #define bswaptls(s) bswap64s(s) #endif From 425082612c012843d8b33fa0d35966adf5600c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Dec 2023 11:34:25 +0100 Subject: [PATCH 0242/2884] exec: Declare target_words_bigendian() in 'exec/tswap.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We usually check target endianess before swapping values, so target_words_bigendian() declaration makes sense in "exec/tswap.h" with the target swapping helpers. Remove "hw/core/cpu.h" when it was only included to get the target_words_bigendian() declaration. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20231212123401.37493-16-philmd@linaro.org> Reviewed-by: Richard Henderson --- cpu-target.c | 1 + disas/disas.c | 1 + hw/audio/virtio-snd.c | 2 +- hw/core/cpu-sysemu.c | 2 +- hw/core/generic-loader.c | 2 +- hw/display/vga.c | 2 +- hw/virtio/virtio.c | 1 + include/exec/tswap.h | 12 +++++++++++- include/hw/core/cpu.h | 11 ----------- 9 files changed, 18 insertions(+), 16 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 4c0621bf33..f88649c299 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -35,6 +35,7 @@ #endif #include "sysemu/cpus.h" #include "sysemu/tcg.h" +#include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" diff --git a/disas/disas.c b/disas/disas.c index 7e3b0bb46c..ec14715ecd 100644 --- a/disas/disas.c +++ b/disas/disas.c @@ -6,6 +6,7 @@ #include "disas/disas.h" #include "disas/capstone.h" #include "hw/core/cpu.h" +#include "exec/tswap.h" #include "exec/memory.h" /* Filled in by elfload.c. Simplistic, but will do for now. */ diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index c80b58bf5d..6a2ee085c0 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -20,11 +20,11 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "include/qemu/lockable.h" +#include "exec/tswap.h" #include "sysemu/runstate.h" #include "trace.h" #include "qapi/error.h" #include "hw/audio/virtio-snd.h" -#include "hw/core/cpu.h" #define VIRTIO_SOUND_VM_VERSION 1 #define VIRTIO_SOUND_JACK_DEFAULT 0 diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-sysemu.c index d0d6a910f9..2a9a2a4eb5 100644 --- a/hw/core/cpu-sysemu.c +++ b/hw/core/cpu-sysemu.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "hw/core/cpu.h" +#include "exec/tswap.h" #include "hw/core/sysemu-cpu-ops.h" bool cpu_paging_enabled(const CPUState *cpu) diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index d4b5c501d8..ea8628b892 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -31,7 +31,7 @@ */ #include "qemu/osdep.h" -#include "hw/core/cpu.h" +#include "exec/tswap.h" #include "sysemu/dma.h" #include "sysemu/reset.h" #include "hw/boards.h" diff --git a/hw/display/vga.c b/hw/display/vga.c index e91a76bf76..30facc6c8e 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -26,7 +26,7 @@ #include "qemu/units.h" #include "sysemu/reset.h" #include "qapi/error.h" -#include "hw/core/cpu.h" +#include "exec/tswap.h" #include "hw/display/vga.h" #include "hw/i386/x86.h" #include "hw/pci/pci.h" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 871674f9be..893a072c9d 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -20,6 +20,7 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" +#include "exec/tswap.h" #include "qom/object_interfaces.h" #include "hw/core/cpu.h" #include "hw/virtio/virtio.h" diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 5089cd6a4c..b7a4191347 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -8,9 +8,19 @@ #ifndef TSWAP_H #define TSWAP_H -#include "hw/core/cpu.h" #include "qemu/bswap.h" +/** + * target_words_bigendian: + * Returns true if the (default) endianness of the target is big endian, + * false otherwise. Note that in target-specific code, you can use + * TARGET_BIG_ENDIAN directly instead. On the other hand, common + * code should normally never need to know about the endianness of the + * target, so please do *not* use this function unless you know very well + * what you are doing! + */ +bool target_words_bigendian(void); + /* * If we're in target-specific code, we can hard-code the swapping * condition, otherwise we have to do (slower) run-time checks. diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index cead8f01a6..d89b2cffcb 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1168,17 +1168,6 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); void cpu_exec_reset_hold(CPUState *cpu); -/** - * target_words_bigendian: - * Returns true if the (default) endianness of the target is big endian, - * false otherwise. Note that in target-specific code, you can use - * TARGET_BIG_ENDIAN directly instead. On the other hand, common - * code should normally never need to know about the endianness of the - * target, so please do *not* use this function unless you know very well - * what you are doing! - */ -bool target_words_bigendian(void); - const char *target_name(void); #ifdef COMPILING_PER_TARGET From 827238668e7dc44e43e71a3e12b605881fe6887e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Dec 2023 11:27:18 +0100 Subject: [PATCH 0243/2884] exec: Move [b]tswapl() declarations to 'exec/user/tswap-target.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tswapl() and bswaptls() are target-dependent and only used by user emulation. Move their definitions to a new header: "exec/user/tswap-target.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20231212123401.37493-17-philmd@linaro.org> Reviewed-by: Richard Henderson --- MAINTAINERS | 1 + bsd-user/freebsd/target_os_elf.h | 1 + bsd-user/freebsd/target_os_stack.h | 1 + bsd-user/netbsd/target_os_elf.h | 1 + bsd-user/openbsd/target_os_elf.h | 1 + bsd-user/signal.c | 1 + bsd-user/strace.c | 1 + include/exec/cpu-all.h | 8 -------- include/exec/user/abitypes.h | 1 + include/user/tswap-target.h | 22 ++++++++++++++++++++++ linux-user/elfload.c | 1 + linux-user/i386/signal.c | 1 + linux-user/ppc/signal.c | 1 + 13 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 include/user/tswap-target.h diff --git a/MAINTAINERS b/MAINTAINERS index 96411e6adf..302b6fd00c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3692,6 +3692,7 @@ Overall usermode emulation M: Riku Voipio S: Maintained F: accel/tcg/user-exec*.c +F: include/exec/user/ F: include/user/ F: common-user/ diff --git a/bsd-user/freebsd/target_os_elf.h b/bsd-user/freebsd/target_os_elf.h index 9df17d56d8..01124979f7 100644 --- a/bsd-user/freebsd/target_os_elf.h +++ b/bsd-user/freebsd/target_os_elf.h @@ -22,6 +22,7 @@ #include "target_arch_elf.h" #include "elf.h" +#include "user/tswap-target.h" #define bsd_get_ncpu() 1 /* until we pull in bsd-proc.[hc] */ diff --git a/bsd-user/freebsd/target_os_stack.h b/bsd-user/freebsd/target_os_stack.h index d15fc3263f..ac0ef22cd7 100644 --- a/bsd-user/freebsd/target_os_stack.h +++ b/bsd-user/freebsd/target_os_stack.h @@ -23,6 +23,7 @@ #include #include "target_arch_sigtramp.h" #include "qemu/guest-random.h" +#include "user/tswap-target.h" /* * The initial FreeBSD stack is as follows: diff --git a/bsd-user/netbsd/target_os_elf.h b/bsd-user/netbsd/target_os_elf.h index 2f3cb20871..9de0f290c0 100644 --- a/bsd-user/netbsd/target_os_elf.h +++ b/bsd-user/netbsd/target_os_elf.h @@ -22,6 +22,7 @@ #include "target_arch_elf.h" #include "elf.h" +#include "user/tswap-target.h" /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE diff --git a/bsd-user/openbsd/target_os_elf.h b/bsd-user/openbsd/target_os_elf.h index 6dca9c5a85..4cf5747dcd 100644 --- a/bsd-user/openbsd/target_os_elf.h +++ b/bsd-user/openbsd/target_os_elf.h @@ -22,6 +22,7 @@ #include "target_arch_elf.h" #include "elf.h" +#include "user/tswap-target.h" /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE diff --git a/bsd-user/signal.c b/bsd-user/signal.c index e5a773ddde..b2faf1d0dd 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu.h" +#include "user/tswap-target.h" #include "gdbstub/user.h" #include "signal-common.h" #include "trace.h" diff --git a/bsd-user/strace.c b/bsd-user/strace.c index 96499751eb..6dc01d3be7 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -22,6 +22,7 @@ #include #include "qemu.h" +#include "user/tswap-target.h" #include "os-strace.h" /* OS dependent strace print functions */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 7c44ffb3af..78848f018c 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -36,14 +36,6 @@ #define BSWAP_NEEDED #endif -#if TARGET_LONG_SIZE == 4 -#define tswapl(s) tswap32(s) -#define bswaptls(s) bswap32s(s) -#else -#define tswapl(s) tswap64(s) -#define bswaptls(s) bswap64s(s) -#endif - /* Target-endianness CPU memory access functions. These fit into the * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. */ diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index db4a670328..731f345ff5 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -2,6 +2,7 @@ #define EXEC_USER_ABITYPES_H #include "cpu.h" +#include "user/tswap-target.h" #ifdef TARGET_ABI32 #define TARGET_ABI_BITS 32 diff --git a/include/user/tswap-target.h b/include/user/tswap-target.h new file mode 100644 index 0000000000..4719330dbb --- /dev/null +++ b/include/user/tswap-target.h @@ -0,0 +1,22 @@ +/* + * target-specific swap() definitions + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef USER_TSWAP_H +#define USER_TSWAP_H + +#include "exec/cpu-defs.h" +#include "exec/tswap.h" + +#if TARGET_LONG_SIZE == 4 +#define tswapl(s) tswap32(s) +#define bswaptls(s) bswap32s(s) +#else +#define tswapl(s) tswap64(s) +#define bswaptls(s) bswap64s(s) +#endif + +#endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a0999dac15..207455c1ba 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -7,6 +7,7 @@ #include #include "qemu.h" +#include "user/tswap-target.h" #include "user-internals.h" #include "signal-common.h" #include "loader.h" diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index cfe70fc5cf..990048f42a 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "user/tswap-target.h" /* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 652038a53c..a1d8c0bccc 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "user/tswap-target.h" #include "vdso-asmoffset.h" /* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; From d25ddb3f543dda8189b19dafd0b9a972c331fade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Dec 2023 22:45:37 +0100 Subject: [PATCH 0244/2884] exec/user: Do not include 'cpu.h' in 'abitypes.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/user/abitypes.h" requires: - "exec/cpu-defs.h" (TARGET_LONG_BITS) - "exec/tswap.h" (tswap32) In order to avoid "cpu.h", pick the minimum required headers. Assert this user-specific header is only included from user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20231212123401.37493-20-philmd@linaro.org> Reviewed-by: Richard Henderson --- include/exec/user/abitypes.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index 731f345ff5..3ec1969368 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -1,7 +1,12 @@ #ifndef EXEC_USER_ABITYPES_H #define EXEC_USER_ABITYPES_H -#include "cpu.h" +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +#include "exec/cpu-defs.h" +#include "exec/tswap.h" #include "user/tswap-target.h" #ifdef TARGET_ABI32 From 471558cb6e1dcda005a61f66516684262864fc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Dec 2023 14:20:34 +0100 Subject: [PATCH 0245/2884] exec: Declare abi_ptr type in its own 'abi_ptr.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The abi_ptr type is declared in "exec/cpu_ldst.h" with all the load/store helpers. Some source files requiring abi_ptr type don't need the load/store helpers. In order to simplify, create a new "exec/abi_ptr.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231212123401.37493-21-philmd@linaro.org> --- include/exec/abi_ptr.h | 33 +++++++++++++++++++++++++++++++++ include/exec/cpu_ldst.h | 17 +++-------------- include/exec/exec-all.h | 1 + include/exec/translator.h | 5 ++++- 4 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 include/exec/abi_ptr.h diff --git a/include/exec/abi_ptr.h b/include/exec/abi_ptr.h new file mode 100644 index 0000000000..2aedcceb0c --- /dev/null +++ b/include/exec/abi_ptr.h @@ -0,0 +1,33 @@ +/* + * QEMU abi_ptr type definitions + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef EXEC_ABI_PTR_H +#define EXEC_ABI_PTR_H + +#include "cpu-param.h" + +#if defined(CONFIG_USER_ONLY) +/* + * sparc32plus has 64bit long but 32bit space address + * this can make bad result with g2h() and h2g() + */ +#if TARGET_VIRT_ADDR_SPACE_BITS <= 32 +typedef uint32_t abi_ptr; +#define TARGET_ABI_FMT_ptr "%x" +#else +typedef uint64_t abi_ptr; +#define TARGET_ABI_FMT_ptr "%"PRIx64 +#endif + +#else /* !CONFIG_USER_ONLY */ + +#include "exec/target_long.h" + +typedef target_ulong abi_ptr; +#define TARGET_ABI_FMT_ptr TARGET_FMT_lx + +#endif /* !CONFIG_USER_ONLY */ + +#endif diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 82690d3947..64e0319996 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -63,20 +63,11 @@ #define CPU_LDST_H #include "exec/memopidx.h" +#include "exec/abi_ptr.h" #include "qemu/int128.h" #include "cpu.h" #if defined(CONFIG_USER_ONLY) -/* sparc32plus has 64bit long but 32bit space address - * this can make bad result with g2h() and h2g() - */ -#if TARGET_VIRT_ADDR_SPACE_BITS <= 32 -typedef uint32_t abi_ptr; -#define TARGET_ABI_FMT_ptr "%x" -#else -typedef uint64_t abi_ptr; -#define TARGET_ABI_FMT_ptr "%"PRIx64 -#endif #ifndef TARGET_TAGGED_ADDRESSES static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) @@ -120,10 +111,8 @@ static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) assert(h2g_valid(x)); \ h2g_nocheck(x); \ }) -#else -typedef vaddr abi_ptr; -#define TARGET_ABI_FMT_ptr VADDR_PRIx -#endif + +#endif /* CONFIG_USER_ONLY */ uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 9599e16a09..530d442112 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -22,6 +22,7 @@ #include "cpu.h" #if defined(CONFIG_USER_ONLY) +#include "exec/abi_ptr.h" #include "exec/cpu_ldst.h" #endif #include "exec/translation-block.h" diff --git a/include/exec/translator.h b/include/exec/translator.h index 2c4fb818e7..6cd937ac5c 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -19,7 +19,10 @@ */ #include "qemu/bswap.h" -#include "exec/cpu_ldst.h" /* for abi_ptr */ +#include "exec/cpu-common.h" +#include "exec/cpu-defs.h" +#include "exec/abi_ptr.h" +#include "cpu.h" /** * gen_intermediate_code From 9c1283dd76a4c21e1dd9d6a268f5d7383bbde77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Mar 2024 18:27:31 +0100 Subject: [PATCH 0246/2884] exec: Declare MMUAccessType type in 'mmu-access-type.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MMUAccessType enum is declared in "hw/core/cpu.h". "hw/core/cpu.h" contains declarations related to CPUState and CPUClass. Some source files only require MMUAccessType and don't need to pull in all CPU* declarations. In order to simplify, create a new "exec/mmu-access-type.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-2-philmd@linaro.org> --- include/exec/cpu_ldst.h | 1 + include/exec/exec-all.h | 1 + include/exec/mmu-access-type.h | 18 ++++++++++++++++++ include/hw/core/cpu.h | 8 +------- 4 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 include/exec/mmu-access-type.h diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 64e0319996..5b99666702 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -64,6 +64,7 @@ #include "exec/memopidx.h" #include "exec/abi_ptr.h" +#include "exec/mmu-access-type.h" #include "qemu/int128.h" #include "cpu.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 530d442112..4c5e470581 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -25,6 +25,7 @@ #include "exec/abi_ptr.h" #include "exec/cpu_ldst.h" #endif +#include "exec/mmu-access-type.h" #include "exec/translation-block.h" #include "qemu/clang-tsa.h" diff --git a/include/exec/mmu-access-type.h b/include/exec/mmu-access-type.h new file mode 100644 index 0000000000..28bbb05b94 --- /dev/null +++ b/include/exec/mmu-access-type.h @@ -0,0 +1,18 @@ +/* + * QEMU MMU Access type definitions + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef EXEC_MMU_ACCESS_TYPE_H +#define EXEC_MMU_ACCESS_TYPE_H + +typedef enum MMUAccessType { + MMU_DATA_LOAD = 0, + MMU_DATA_STORE = 1, + MMU_INST_FETCH = 2 +#define MMU_ACCESS_COUNT 3 +} MMUAccessType; + +#endif diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index d89b2cffcb..759c3e7d89 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -25,6 +25,7 @@ #include "exec/hwaddr.h" #include "exec/vaddr.h" #include "exec/memattrs.h" +#include "exec/mmu-access-type.h" #include "exec/tlb-common.h" #include "qapi/qapi-types-run-state.h" #include "qemu/bitmap.h" @@ -80,13 +81,6 @@ DECLARE_CLASS_CHECKERS(CPUClass, CPU, typedef struct ArchCPU CpuInstanceType; \ OBJECT_DECLARE_TYPE(ArchCPU, CpuClassType, CPU_MODULE_OBJ_NAME); -typedef enum MMUAccessType { - MMU_DATA_LOAD = 0, - MMU_DATA_STORE = 1, - MMU_INST_FETCH = 2 -#define MMU_ACCESS_COUNT 3 -} MMUAccessType; - typedef struct CPUWatchpoint CPUWatchpoint; /* see accel-cpu.h */ From 6ce1c9d08554c70da6ca7262b00361d8bdc1705b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Mar 2024 18:37:25 +0100 Subject: [PATCH 0247/2884] exec: Declare CPUBreakpoint/CPUWatchpoint type in 'breakpoint.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPUBreakpoint and CPUWatchpoint structures are declared in "hw/core/cpu.h", which contains declarations related to CPUState and CPUClass. Some source files only require the BP/WP definitions and don't need to pull in all CPU* API. In order to simplify, create a new "exec/breakpoint.h" header. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20240418192525.97451-3-philmd@linaro.org> --- include/exec/breakpoint.h | 30 ++++++++++++++++++++++++++++++ include/hw/core/cpu.h | 16 +--------------- target/arm/internals.h | 1 + target/ppc/internal.h | 1 + target/riscv/debug.h | 2 ++ 5 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 include/exec/breakpoint.h diff --git a/include/exec/breakpoint.h b/include/exec/breakpoint.h new file mode 100644 index 0000000000..95f0482e6d --- /dev/null +++ b/include/exec/breakpoint.h @@ -0,0 +1,30 @@ +/* + * QEMU breakpoint & watchpoint definitions + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef EXEC_BREAKPOINT_H +#define EXEC_BREAKPOINT_H + +#include "qemu/queue.h" +#include "exec/vaddr.h" +#include "exec/memattrs.h" + +typedef struct CPUBreakpoint { + vaddr pc; + int flags; /* BP_* */ + QTAILQ_ENTRY(CPUBreakpoint) entry; +} CPUBreakpoint; + +typedef struct CPUWatchpoint { + vaddr vaddr; + vaddr len; + vaddr hitaddr; + MemTxAttrs hitattrs; + int flags; /* BP_* */ + QTAILQ_ENTRY(CPUWatchpoint) entry; +} CPUWatchpoint; + +#endif diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 759c3e7d89..46b99a7ea5 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -22,6 +22,7 @@ #include "hw/qdev-core.h" #include "disas/dis-asm.h" +#include "exec/breakpoint.h" #include "exec/hwaddr.h" #include "exec/vaddr.h" #include "exec/memattrs.h" @@ -347,21 +348,6 @@ typedef struct CPUNegativeOffsetState { bool can_do_io; } CPUNegativeOffsetState; -typedef struct CPUBreakpoint { - vaddr pc; - int flags; /* BP_* */ - QTAILQ_ENTRY(CPUBreakpoint) entry; -} CPUBreakpoint; - -struct CPUWatchpoint { - vaddr vaddr; - vaddr len; - vaddr hitaddr; - MemTxAttrs hitattrs; - int flags; /* BP_* */ - QTAILQ_ENTRY(CPUWatchpoint) entry; -}; - struct KVMState; struct kvm_run; diff --git a/target/arm/internals.h b/target/arm/internals.h index b53f5e8ff2..e40ec453d5 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -25,6 +25,7 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +#include "exec/breakpoint.h" #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" #include "syndrome.h" diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 5b20ecbd33..601c0b533f 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -18,6 +18,7 @@ #ifndef PPC_INTERNAL_H #define PPC_INTERNAL_H +#include "exec/breakpoint.h" #include "hw/registerfields.h" /* PM instructions */ diff --git a/target/riscv/debug.h b/target/riscv/debug.h index 5794aa6ee5..c347863578 100644 --- a/target/riscv/debug.h +++ b/target/riscv/debug.h @@ -22,6 +22,8 @@ #ifndef RISCV_DEBUG_H #define RISCV_DEBUG_H +#include "exec/breakpoint.h" + #define RV_MAX_TRIGGERS 2 /* register index of tdata CSRs */ From 43bc8a6f1a5aa69b815548ab79ec2ff38812135e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Apr 2024 12:32:14 +0200 Subject: [PATCH 0248/2884] exec: Restrict TCG specific declarations of 'cputlb.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid TCG specific declarations being used from non-TCG accelerators. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-5-philmd@linaro.org> --- include/exec/cputlb.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 6da1462c4f..ef18642a32 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -22,9 +22,14 @@ #include "exec/cpu-common.h" +#ifdef CONFIG_TCG + #if !defined(CONFIG_USER_ONLY) /* cputlb.c */ void tlb_protect_code(ram_addr_t ram_addr); void tlb_unprotect_code(ram_addr_t ram_addr); #endif + +#endif /* CONFIG_TCG */ + #endif From 1ce871a3e7dd3913752966064c406c08193aa992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Apr 2024 13:55:51 +0200 Subject: [PATCH 0249/2884] exec: Restrict 'cpu_ldst.h' to TCG accelerator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/cpu_ldst.h" is specific to TCG, do not allow its inclusion from other accelerators. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-6-philmd@linaro.org> --- include/exec/cpu_ldst.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 5b99666702..f3c2a3ca74 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -1,5 +1,5 @@ /* - * Software MMU support + * Software MMU support (per-target) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -62,6 +62,10 @@ #ifndef CPU_LDST_H #define CPU_LDST_H +#ifndef CONFIG_TCG +#error Can only include this header with TCG +#endif + #include "exec/memopidx.h" #include "exec/abi_ptr.h" #include "exec/mmu-access-type.h" From d3cbde7402fa44b1be898df0e13257e6fc399974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Mar 2024 12:27:29 +0100 Subject: [PATCH 0250/2884] exec: Rename 'exec/user/guest-base.h' as 'user/guest-base.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The include/user/ directory contains the user-emulation specific headers. Move guest-base.h there too. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20240418192525.97451-15-philmd@linaro.org> --- include/exec/cpu-all.h | 2 +- include/{exec => }/user/guest-base.h | 4 ++-- tcg/tcg.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename include/{exec => }/user/guest-base.h (72%) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 78848f018c..027f19e052 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -65,7 +65,7 @@ #if defined(CONFIG_USER_ONLY) #include "exec/user/abitypes.h" -#include "exec/user/guest-base.h" +#include "user/guest-base.h" extern bool have_guest_base; diff --git a/include/exec/user/guest-base.h b/include/user/guest-base.h similarity index 72% rename from include/exec/user/guest-base.h rename to include/user/guest-base.h index afe2ab7fbb..1e42bca5db 100644 --- a/include/exec/user/guest-base.h +++ b/include/user/guest-base.h @@ -4,8 +4,8 @@ * Copyright (c) 2003 Fabrice Bellard */ -#ifndef EXEC_USER_GUEST_BASE_H -#define EXEC_USER_GUEST_BASE_H +#ifndef USER_GUEST_BASE_H +#define USER_GUEST_BASE_H extern uintptr_t guest_base; diff --git a/tcg/tcg.c b/tcg/tcg.c index 0c0bb9d169..6a32656cd4 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -57,7 +57,7 @@ #include "tcg-internal.h" #include "tcg/perf.h" #ifdef CONFIG_USER_ONLY -#include "exec/user/guest-base.h" +#include "user/guest-base.h" #endif /* Forward declarations for functions declared in tcg-target.c.inc and From 16aa8eaaace3f8eb2d14521705fdccab518388a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Dec 2023 21:51:26 +0100 Subject: [PATCH 0251/2884] exec: Restrict inclusion of 'user/guest-base.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare 'have_guest_base' in "user/guest-base.h". Very few files require this header, so explicitly include it there instead of "exec/cpu-all.h" which is used in many source files. Assert this user-specific header is only included from user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20231211212003.21686-23-philmd@linaro.org> Reviewed-by: Anton Johansson --- bsd-user/main.c | 1 + include/exec/cpu-all.h | 3 --- include/exec/cpu_ldst.h | 2 ++ include/user/guest-base.h | 6 ++++++ linux-user/elfload.c | 1 + linux-user/main.c | 1 + 6 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 01b313756e..29a629d877 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -36,6 +36,7 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "exec/exec-all.h" +#include "user/guest-base.h" #include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 027f19e052..e75ec13cd0 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -65,9 +65,6 @@ #if defined(CONFIG_USER_ONLY) #include "exec/user/abitypes.h" -#include "user/guest-base.h" - -extern bool have_guest_base; /* * If non-zero, the guest virtual address space is a contiguous subset diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index f3c2a3ca74..7d0a0412ad 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -74,6 +74,8 @@ #if defined(CONFIG_USER_ONLY) +#include "user/guest-base.h" + #ifndef TARGET_TAGGED_ADDRESSES static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) { diff --git a/include/user/guest-base.h b/include/user/guest-base.h index 1e42bca5db..055c1d14fe 100644 --- a/include/user/guest-base.h +++ b/include/user/guest-base.h @@ -7,6 +7,12 @@ #ifndef USER_GUEST_BASE_H #define USER_GUEST_BASE_H +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + extern uintptr_t guest_base; +extern bool have_guest_base; + #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 207455c1ba..f9461d2844 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -8,6 +8,7 @@ #include "qemu.h" #include "user/tswap-target.h" +#include "user/guest-base.h" #include "user-internals.h" #include "signal-common.h" #include "loader.h" diff --git a/linux-user/main.c b/linux-user/main.c index 149e35432e..94e4c47f05 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -38,6 +38,7 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/plugin.h" +#include "user/guest-base.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" #include "gdbstub/user.h" From aacfd8bbaf99444f84b408e6b052651fb8056c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Apr 2024 14:13:18 +0200 Subject: [PATCH 0252/2884] exec: Move CPUTLBEntry helpers to cputlb.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following CPUTLBEntry helpers are only used in accel/tcg/cputlb.c: - tlb_index() - tlb_entry() - tlb_read_idx() - tlb_addr_write() Move them to this file, allowing to remove the huge "cpu.h" header inclusion from "exec/cpu_ldst.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-13-philmd@linaro.org> --- accel/tcg/cputlb.c | 51 ++++++++++++++++++++++++++++++++++++++ include/exec/cpu_ldst.h | 55 ----------------------------------------- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index e16d02a62c..953c437ba9 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -27,6 +27,9 @@ #include "exec/tb-flush.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" +#include "exec/mmu-access-type.h" +#include "exec/tlb-common.h" +#include "exec/vaddr.h" #include "tcg/tcg.h" #include "qemu/error-report.h" #include "exec/log.h" @@ -95,6 +98,54 @@ static inline size_t sizeof_tlb(CPUTLBDescFast *fast) return fast->mask + (1 << CPU_TLB_ENTRY_BITS); } +static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry, + MMUAccessType access_type) +{ + /* Do not rearrange the CPUTLBEntry structure members. */ + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) != + MMU_DATA_LOAD * sizeof(uint64_t)); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) != + MMU_DATA_STORE * sizeof(uint64_t)); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) != + MMU_INST_FETCH * sizeof(uint64_t)); + +#if TARGET_LONG_BITS == 32 + /* Use qatomic_read, in case of addr_write; only care about low bits. */ + const uint32_t *ptr = (uint32_t *)&entry->addr_idx[access_type]; + ptr += HOST_BIG_ENDIAN; + return qatomic_read(ptr); +#else + const uint64_t *ptr = &entry->addr_idx[access_type]; +# if TCG_OVERSIZED_GUEST + return *ptr; +# else + /* ofs might correspond to .addr_write, so use qatomic_read */ + return qatomic_read(ptr); +# endif +#endif +} + +static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry) +{ + return tlb_read_idx(entry, MMU_DATA_STORE); +} + +/* Find the TLB index corresponding to the mmu_idx + address pair. */ +static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx, + vaddr addr) +{ + uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS; + + return (addr >> TARGET_PAGE_BITS) & size_mask; +} + +/* Find the TLB entry corresponding to the mmu_idx + address pair. */ +static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx, + vaddr addr) +{ + return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; +} + static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, size_t max_entries) { diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 7d0a0412ad..11ba3778ba 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -70,7 +70,6 @@ #include "exec/abi_ptr.h" #include "exec/mmu-access-type.h" #include "qemu/int128.h" -#include "cpu.h" #if defined(CONFIG_USER_ONLY) @@ -296,60 +295,6 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, abi_ptr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); -#if !defined(CONFIG_USER_ONLY) - -#include "tcg/oversized-guest.h" - -static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry, - MMUAccessType access_type) -{ - /* Do not rearrange the CPUTLBEntry structure members. */ - QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) != - MMU_DATA_LOAD * sizeof(uint64_t)); - QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) != - MMU_DATA_STORE * sizeof(uint64_t)); - QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) != - MMU_INST_FETCH * sizeof(uint64_t)); - -#if TARGET_LONG_BITS == 32 - /* Use qatomic_read, in case of addr_write; only care about low bits. */ - const uint32_t *ptr = (uint32_t *)&entry->addr_idx[access_type]; - ptr += HOST_BIG_ENDIAN; - return qatomic_read(ptr); -#else - const uint64_t *ptr = &entry->addr_idx[access_type]; -# if TCG_OVERSIZED_GUEST - return *ptr; -# else - /* ofs might correspond to .addr_write, so use qatomic_read */ - return qatomic_read(ptr); -# endif -#endif -} - -static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry) -{ - return tlb_read_idx(entry, MMU_DATA_STORE); -} - -/* Find the TLB index corresponding to the mmu_idx + address pair. */ -static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx, - vaddr addr) -{ - uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS; - - return (addr >> TARGET_PAGE_BITS) & size_mask; -} - -/* Find the TLB entry corresponding to the mmu_idx + address pair. */ -static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx, - vaddr addr) -{ - return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; -} - -#endif /* !defined(CONFIG_USER_ONLY) */ - #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data # define cpu_ldsw_data cpu_ldsw_be_data From 76d07d321fad2e05f8a86243724f91577c5f94c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Mar 2024 18:38:02 +0100 Subject: [PATCH 0253/2884] hw/core: Avoid including the full 'hw/core/cpu.h' in 'tcg-cpu-ops.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only include what is required, avoiding the full CPUState API from the huge "hw/core/cpu.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240418192525.97451-4-philmd@linaro.org> --- include/hw/core/tcg-cpu-ops.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index dc1f16a977..9387d38748 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -10,7 +10,11 @@ #ifndef TCG_CPU_OPS_H #define TCG_CPU_OPS_H -#include "hw/core/cpu.h" +#include "exec/breakpoint.h" +#include "exec/hwaddr.h" +#include "exec/memattrs.h" +#include "exec/mmu-access-type.h" +#include "exec/vaddr.h" struct TCGCPUOps { /** From 671558d290ffb93752d3245e7c5604b04b6dcdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 18 Apr 2024 16:32:01 +0200 Subject: [PATCH 0254/2884] plugins: Include missing 'qemu/bitmap.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit c006147122 ("plugins: create CPUPluginState and migrate plugin_mask") "qemu/plugin.h" uses DECLARE_BITMAP(), which is declared in "qemu/bitmap.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240418192525.97451-19-philmd@linaro.org> --- include/qemu/plugin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 12a96cea2a..41db748eda 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -13,6 +13,7 @@ #include "qemu/queue.h" #include "qemu/option.h" #include "qemu/plugin-event.h" +#include "qemu/bitmap.h" #include "exec/memopidx.h" #include "hw/core/cpu.h" From 03555199b63aa1fbce24d16287e141c33f572a24 Mon Sep 17 00:00:00 2001 From: Nicholas Ngai Date: Sat, 25 Sep 2021 14:48:20 -0700 Subject: [PATCH 0255/2884] net/slirp: Use newer slirp_*_hostxfwd API libslirp provides a newer slirp_*_hostxfwd API meant for address-agnostic forwarding instead of the is_udp parameter which is limited to just TCP/UDP. This paves the way for IPv6 and Unix socket support. Signed-off-by: Nicholas Ngai Signed-off-by: Samuel Thibault Tested-by: Breno Leitao Message-Id: <20210925214820.18078-1-nicholas@ngai.me> --- net/slirp.c | 62 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/net/slirp.c b/net/slirp.c index 25b49c4526..eb9a456ed4 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -718,7 +718,12 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *id) void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) { - struct in_addr host_addr = { .s_addr = INADDR_ANY }; + struct sockaddr_in host_addr = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = INADDR_ANY, + }, + }; int host_port; char buf[256]; const char *src_str, *p; @@ -755,15 +760,21 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) { goto fail_syntax; } if (qemu_strtoi(p, NULL, 10, &host_port)) { goto fail_syntax; } + host_addr.sin_port = htons(host_port); - err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); +#if SLIRP_CHECK_VERSION(4, 5, 0) + err = slirp_remove_hostxfwd(s->slirp, (struct sockaddr *) &host_addr, + sizeof(host_addr), is_udp ? SLIRP_HOSTFWD_UDP : 0); +#else + err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr.sin_addr, host_port); +#endif monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, err ? "not found" : "removed"); @@ -775,13 +786,24 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) { - struct in_addr host_addr = { .s_addr = INADDR_ANY }; - struct in_addr guest_addr = { .s_addr = 0 }; + struct sockaddr_in host_addr = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = INADDR_ANY, + }, + }; + struct sockaddr_in guest_addr = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = 0, + }, + }; + int err; int host_port, guest_port; const char *p; char buf[256]; int is_udp; - char *end; + const char *end; const char *fail_reason = "Unknown reason"; p = redir_str; @@ -802,7 +824,7 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) fail_reason = "Missing : separator"; goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) { fail_reason = "Bad host address"; goto fail_syntax; } @@ -811,29 +833,41 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) fail_reason = "Bad host port separator"; goto fail_syntax; } - host_port = strtol(buf, &end, 0); - if (*end != '\0' || host_port < 0 || host_port > 65535) { + err = qemu_strtoi(buf, &end, 0, &host_port); + if (err || host_port < 0 || host_port > 65535) { fail_reason = "Bad host port"; goto fail_syntax; } + host_addr.sin_port = htons(host_port); if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { fail_reason = "Missing guest address"; goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { + if (buf[0] != '\0' && !inet_aton(buf, &guest_addr.sin_addr)) { fail_reason = "Bad guest address"; goto fail_syntax; } - guest_port = strtol(p, &end, 0); - if (*end != '\0' || guest_port < 1 || guest_port > 65535) { + err = qemu_strtoi(p, &end, 0, &guest_port); + if (err || guest_port < 1 || guest_port > 65535) { fail_reason = "Bad guest port"; goto fail_syntax; } + guest_addr.sin_port = htons(guest_port); - if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, - guest_port) < 0) { +#if SLIRP_CHECK_VERSION(4, 5, 0) + err = slirp_add_hostxfwd(s->slirp, + (struct sockaddr *) &host_addr, sizeof(host_addr), + (struct sockaddr *) &guest_addr, sizeof(guest_addr), + is_udp ? SLIRP_HOSTFWD_UDP : 0); +#else + err = slirp_add_hostfwd(s->slirp, is_udp, + host_addr.sin_addr, host_port, + guest_addr.sin_addr, guest_port); +#endif + + if (err < 0) { error_setg(errp, "Could not set up host forwarding rule '%s'", redir_str); return -1; From d771ca1c10ab146eae676dd6a6975a8f7cf84d65 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:35 +0800 Subject: [PATCH 0256/2884] hw/loongarch: Move boot functions to boot.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move some boot functions to boot.c and struct loongarch_boot_info into struct LoongArchMachineState. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240426091551.2397867-2-gaosong@loongson.cn> --- hw/loongarch/boot.c | 128 ++++++++++++++++++++++++++++++++++++ hw/loongarch/meson.build | 1 + hw/loongarch/virt.c | 121 +++------------------------------- include/hw/loongarch/boot.h | 21 ++++++ include/hw/loongarch/virt.h | 2 + 5 files changed, 160 insertions(+), 113 deletions(-) create mode 100644 hw/loongarch/boot.c create mode 100644 include/hw/loongarch/boot.h diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c new file mode 100644 index 0000000000..9feed17db3 --- /dev/null +++ b/hw/loongarch/boot.c @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch boot helper functions. + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "target/loongarch/cpu.h" +#include "hw/loongarch/virt.h" +#include "hw/loader.h" +#include "elf.h" +#include "qemu/error-report.h" +#include "sysemu/reset.h" +#include "sysemu/qtest.h" + +static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) +{ + return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); +} + +static int64_t load_kernel_info(struct loongarch_boot_info *info) +{ + uint64_t kernel_entry, kernel_low, kernel_high; + ssize_t kernel_size; + + kernel_size = load_elf(info->kernel_filename, NULL, + cpu_loongarch_virt_to_phys, NULL, + &kernel_entry, &kernel_low, + &kernel_high, NULL, 0, + EM_LOONGARCH, 1, 0); + + if (kernel_size < 0) { + error_report("could not load kernel '%s': %s", + info->kernel_filename, + load_elf_strerror(kernel_size)); + exit(1); + } + return kernel_entry; +} + +static void reset_load_elf(void *opaque) +{ + LoongArchCPU *cpu = opaque; + CPULoongArchState *env = &cpu->env; + + cpu_reset(CPU(cpu)); + if (env->load_elf) { + cpu_set_pc(CPU(cpu), env->elf_address); + } +} + +static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, + FWCfgState *fw_cfg) +{ + /* + * Expose the kernel, the command line, and the initrd in fw_cfg. + * We don't process them here at all, it's all left to the + * firmware. + */ + load_image_to_fw_cfg(fw_cfg, + FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, + info->kernel_filename, + false); + + if (info->initrd_filename) { + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + info->initrd_filename, false); + } + + if (info->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(info->kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + info->kernel_cmdline); + } +} + +static void loongarch_firmware_boot(LoongArchMachineState *lams, + struct loongarch_boot_info *info) +{ + fw_cfg_add_kernel_info(info, lams->fw_cfg); +} + +static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) +{ + int64_t kernel_addr = 0; + LoongArchCPU *lacpu; + CPUState *cs; + + if (info->kernel_filename) { + kernel_addr = load_kernel_info(info); + } else { + if(!qtest_enabled()) { + error_report("Need kernel filename\n"); + exit(1); + } + } + + CPU_FOREACH(cs) { + lacpu = LOONGARCH_CPU(cs); + lacpu->env.load_elf = true; + lacpu->env.elf_address = kernel_addr; + } +} + +void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(ms); + int i; + + /* register reset function */ + for (i = 0; i < ms->smp.cpus; i++) { + qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i))); + } + + info->kernel_filename = ms->kernel_filename; + info->kernel_cmdline = ms->kernel_cmdline; + info->initrd_filename = ms->initrd_filename; + + if (lams->bios_loaded) { + loongarch_firmware_boot(lams, info); + } else { + loongarch_direct_kernel_boot(info); + } +} diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index c0421502ab..d306d82c2e 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -1,6 +1,7 @@ loongarch_ss = ss.source_set() loongarch_ss.add(files( 'fw_cfg.c', + 'boot.c', )) loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt]) loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 441d764843..bfb88aedab 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -46,14 +46,6 @@ #include "hw/block/flash.h" #include "qemu/error-report.h" - -struct loaderparams { - uint64_t ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -}; - static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, const char *name, const char *alias_prop_name) @@ -412,31 +404,6 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) memmap_entries++; } -static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) -{ - return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); -} - -static int64_t load_kernel_info(const struct loaderparams *loaderparams) -{ - uint64_t kernel_entry, kernel_low, kernel_high; - ssize_t kernel_size; - - kernel_size = load_elf(loaderparams->kernel_filename, NULL, - cpu_loongarch_virt_to_phys, NULL, - &kernel_entry, &kernel_low, - &kernel_high, NULL, 0, - EM_LOONGARCH, 1, 0); - - if (kernel_size < 0) { - error_report("could not load kernel '%s': %s", - loaderparams->kernel_filename, - load_elf_strerror(kernel_size)); - exit(1); - } - return kernel_entry; -} - static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams) { DeviceState *dev; @@ -717,67 +684,6 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) } } -static void reset_load_elf(void *opaque) -{ - LoongArchCPU *cpu = opaque; - CPULoongArchState *env = &cpu->env; - - cpu_reset(CPU(cpu)); - if (env->load_elf) { - cpu_set_pc(CPU(cpu), env->elf_address); - } -} - -static void fw_cfg_add_kernel_info(const struct loaderparams *loaderparams, - FWCfgState *fw_cfg) -{ - /* - * Expose the kernel, the command line, and the initrd in fw_cfg. - * We don't process them here at all, it's all left to the - * firmware. - */ - load_image_to_fw_cfg(fw_cfg, - FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, - loaderparams->kernel_filename, - false); - - if (loaderparams->initrd_filename) { - load_image_to_fw_cfg(fw_cfg, - FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, - loaderparams->initrd_filename, false); - } - - if (loaderparams->kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(loaderparams->kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, - loaderparams->kernel_cmdline); - } -} - -static void loongarch_firmware_boot(LoongArchMachineState *lams, - const struct loaderparams *loaderparams) -{ - fw_cfg_add_kernel_info(loaderparams, lams->fw_cfg); -} - -static void loongarch_direct_kernel_boot(LoongArchMachineState *lams, - const struct loaderparams *loaderparams) -{ - MachineState *machine = MACHINE(lams); - int64_t kernel_addr = 0; - LoongArchCPU *lacpu; - int i; - - kernel_addr = load_kernel_info(loaderparams); - if (!machine->firmware) { - for (i = 0; i < machine->smp.cpus; i++) { - lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); - lacpu->env.load_elf = true; - lacpu->env.elf_address = kernel_addr; - } - } -} static void loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) @@ -833,7 +739,6 @@ static void loongarch_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); CPUState *cpu; char *ramName = NULL; - struct loaderparams loaderparams = { }; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -936,24 +841,8 @@ static void loongarch_init(MachineState *machine) sizeof(struct memmap_entry) * (memmap_entries)); } fdt_add_fw_cfg_node(lams); - loaderparams.ram_size = ram_size; - loaderparams.kernel_filename = machine->kernel_filename; - loaderparams.kernel_cmdline = machine->kernel_cmdline; - loaderparams.initrd_filename = machine->initrd_filename; - /* load the kernel. */ - if (loaderparams.kernel_filename) { - if (lams->bios_loaded) { - loongarch_firmware_boot(lams, &loaderparams); - } else { - loongarch_direct_kernel_boot(lams, &loaderparams); - } - } fdt_add_flash_node(lams); - /* register reset function */ - for (i = 0; i < machine->smp.cpus; i++) { - lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); - qemu_register_reset(reset_load_elf, lacpu); - } + /* Initialize the IO interrupt subsystem */ loongarch_irq_init(lams); fdt_add_irqchip_node(lams); @@ -977,7 +866,13 @@ static void loongarch_init(MachineState *machine) */ fdt_base = 1 * MiB; qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size); - rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, fdt_base); + rom_add_blob_fixed_as("fdt", machine->fdt, lams->fdt_size, fdt_base, + &address_space_memory); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, fdt_base, lams->fdt_size)); + + lams->bootinfo.ram_size = ram_size; + loongarch_load_kernel(machine, &lams->bootinfo); } bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h new file mode 100644 index 0000000000..3275c1e295 --- /dev/null +++ b/include/hw/loongarch/boot.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Definitions for LoongArch boot. + * + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_BOOT_H +#define HW_LOONGARCH_BOOT_H + +struct loongarch_boot_info { + uint64_t ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; + uint64_t a0, a1, a2; +}; + +void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info); + +#endif /* HW_LOONGARCH_BOOT_H */ diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 252f7df7f4..cf2f2bfb19 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -13,6 +13,7 @@ #include "qemu/queue.h" #include "hw/intc/loongarch_ipi.h" #include "hw/block/flash.h" +#include "hw/loongarch/boot.h" #define LOONGARCH_MAX_CPUS 256 @@ -55,6 +56,7 @@ struct LoongArchMachineState { MemoryRegion system_iocsr; MemoryRegion iocsr_mem; AddressSpace as_iocsr; + struct loongarch_boot_info bootinfo; }; #define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") From 02307a678c3cb366d089ef8d90f6acb9663b98ce Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:36 +0800 Subject: [PATCH 0257/2884] hw/loongarch: Add load initrd we load initrd ramdisk after kernel_high address Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-3-gaosong@loongson.cn> --- hw/loongarch/boot.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 9feed17db3..a5135fe542 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -22,7 +22,8 @@ static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) static int64_t load_kernel_info(struct loongarch_boot_info *info) { - uint64_t kernel_entry, kernel_low, kernel_high; + uint64_t kernel_entry, kernel_low, kernel_high, initrd_size; + ram_addr_t initrd_offset; ssize_t kernel_size; kernel_size = load_elf(info->kernel_filename, NULL, @@ -37,6 +38,31 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) load_elf_strerror(kernel_size)); exit(1); } + + if (info->initrd_filename) { + initrd_size = get_image_size(info->initrd_filename); + if (initrd_size > 0) { + initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); + + if (initrd_offset + initrd_size > info->ram_size) { + error_report("memory too small for initial ram disk '%s'", + info->initrd_filename); + exit(1); + } + + initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, + info->ram_size - initrd_offset); + } + + if (initrd_size == (target_ulong)-1) { + error_report("could not load initial ram disk '%s'", + info->initrd_filename); + exit(1); + } + } else { + initrd_size = 0; + } + return kernel_entry; } From 7e0510d7600cd0c1c4742751b415f4f7df84328e Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:37 +0800 Subject: [PATCH 0258/2884] hw/loongarch: Add slave cpu boot_code Load the slave CPU boot code at pflash0 and set the slave CPU elf_address to VIRT_FLASH0_BASE. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-4-gaosong@loongson.cn> --- hw/loongarch/boot.c | 62 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index a5135fe542..fb6effbaff 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -15,6 +15,54 @@ #include "sysemu/reset.h" #include "sysemu/qtest.h" +static const unsigned int slave_boot_code[] = { + /* Configure reset ebase. */ + 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ + + /* Disable interrupt. */ + 0x0380100c, /* ori $t0, $zero,0x4 */ + 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */ + + /* Clear mailbox. */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ + 0x06481da0, /* iocsrwr.d $zero, $t1 */ + + /* Enable IPI interrupt. */ + 0x1400002c, /* lu12i.w $t0, 1(0x1) */ + 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */ + 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */ + 0x064819ac, /* iocsrwr.w $t0, $t1 */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ + + /* Wait for wakeup <.L11>: */ + 0x06488000, /* idle 0x0 */ + 0x03400000, /* andi $zero, $zero, 0x0 */ + 0x064809ac, /* iocsrrd.w $t0, $t1 */ + 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */ + + /* Read and clear IPI interrupt. */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x064809ac, /* iocsrrd.w $t0, $t1 */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */ + 0x064819ac, /* iocsrwr.w $t0, $t1 */ + + /* Disable IPI interrupt. */ + 0x1400002c, /* lu12i.w $t0, 1(0x1) */ + 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */ + + /* Read mail buf and jump to specified entry */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ + 0x06480dac, /* iocsrrd.d $t0, $t1 */ + 0x00150181, /* move $ra, $t0 */ + 0x4c000020, /* jirl $zero, $ra,0 */ +}; + static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) { return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); @@ -125,11 +173,23 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) } } + /* Load slave boot code at pflash0 . */ + void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); + memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); + rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); + CPU_FOREACH(cs) { lacpu = LOONGARCH_CPU(cs); lacpu->env.load_elf = true; - lacpu->env.elf_address = kernel_addr; + if (cs == first_cpu) { + lacpu->env.elf_address = kernel_addr; + } else { + lacpu->env.elf_address = VIRT_FLASH0_BASE; + } + lacpu->env.boot_info = info; } + + g_free(boot_code); } void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) From 58ee60d2d29437d1ddb02640a12a3f028307594c Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:38 +0800 Subject: [PATCH 0259/2884] hw/loongarch: Add init_cmdline Add init_cmline and set boot_info->a0, a1 Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-5-gaosong@loongson.cn> --- hw/loongarch/boot.c | 30 ++++++++++++++++++++++++++++++ include/hw/loongarch/virt.h | 2 ++ target/loongarch/cpu.h | 2 ++ 3 files changed, 34 insertions(+) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index fb6effbaff..127085bcc4 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -63,6 +63,16 @@ static const unsigned int slave_boot_code[] = { 0x4c000020, /* jirl $zero, $ra,0 */ }; +static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) +{ + hwaddr cmdline_addr = p - start; + + info->a0 = 1; + info->a1 = cmdline_addr; + + memcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE); +} + static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) { return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); @@ -121,6 +131,10 @@ static void reset_load_elf(void *opaque) cpu_reset(CPU(cpu)); if (env->load_elf) { + if (cpu == LOONGARCH_CPU(first_cpu)) { + env->gpr[4] = env->boot_info->a0; + env->gpr[5] = env->boot_info->a1; + } cpu_set_pc(CPU(cpu), env->elf_address); } } @@ -158,8 +172,17 @@ static void loongarch_firmware_boot(LoongArchMachineState *lams, fw_cfg_add_kernel_info(info, lams->fw_cfg); } +static void init_boot_rom(struct loongarch_boot_info *info, void *p) +{ + void *start = p; + + init_cmdline(info, p, start); + p += COMMAND_LINE_SIZE; +} + static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) { + void *p, *bp; int64_t kernel_addr = 0; LoongArchCPU *lacpu; CPUState *cs; @@ -173,6 +196,12 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) } } + /* Load cmdline and system tables at [0 - 1 MiB] */ + p = g_malloc0(1 * MiB); + bp = p; + init_boot_rom(info, p); + rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory); + /* Load slave boot code at pflash0 . */ void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); @@ -190,6 +219,7 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) } g_free(boot_code); + g_free(bp); } void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index cf2f2bfb19..d7a074d69f 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -33,6 +33,8 @@ #define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) #define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) +#define COMMAND_LINE_SIZE 512 + struct LoongArchMachineState { /*< private >*/ MachineState parent_obj; diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index abb01b2cc7..c5722670f5 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -359,6 +359,8 @@ typedef struct CPUArchState { uint32_t mp_state; /* Store ipistate to access from this struct */ DeviceState *ipistate; + + struct loongarch_boot_info *boot_info; #endif } CPULoongArchState; From 4216baa90da53f9c53d0a9de264450040423ede0 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:39 +0800 Subject: [PATCH 0260/2884] hw/loongarch: Init efi_system_table Add init_systab and set boot_info->a2 Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-6-gaosong@loongson.cn> --- hw/loongarch/boot.c | 22 +++++++++++++++++ include/hw/loongarch/boot.h | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 127085bcc4..59889dbc90 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -63,6 +63,25 @@ static const unsigned int slave_boot_code[] = { 0x4c000020, /* jirl $zero, $ra,0 */ }; +static void init_systab(struct loongarch_boot_info *info, void *p, void *start) +{ + struct efi_system_table *systab = p; + + info->a2 = p - start; + + systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; + systab->hdr.revision = EFI_SPECIFICATION_VERSION; + systab->hdr.revision = sizeof(struct efi_system_table), + systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8; + systab->runtime = 0; + systab->boottime = 0; + systab->nr_tables = 0; + + p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB); + + systab->tables = p; +} + static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) { hwaddr cmdline_addr = p - start; @@ -134,6 +153,7 @@ static void reset_load_elf(void *opaque) if (cpu == LOONGARCH_CPU(first_cpu)) { env->gpr[4] = env->boot_info->a0; env->gpr[5] = env->boot_info->a1; + env->gpr[6] = env->boot_info->a2; } cpu_set_pc(CPU(cpu), env->elf_address); } @@ -178,6 +198,8 @@ static void init_boot_rom(struct loongarch_boot_info *info, void *p) init_cmdline(info, p, start); p += COMMAND_LINE_SIZE; + + init_systab(info, p, start); } static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index 3275c1e295..cf0e4d4f91 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -8,6 +8,54 @@ #ifndef HW_LOONGARCH_BOOT_H #define HW_LOONGARCH_BOOT_H +/* UEFI 2.10 */ +#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249 +#define EFI_2_100_SYSTEM_TABLE_REVISION ((2<<16) | (100)) +#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION +#define EFI_SYSTEM_TABLE_REVISION EFI_2_100_SYSTEM_TABLE_REVISION + +#define FW_VERSION 0x1 +#define FW_PATCHLEVEL 0x0 + +typedef struct { + uint8_t b[16]; +} efi_guid_t QEMU_ALIGNED(8); + +struct efi_config_table { + efi_guid_t guid; + uint64_t *ptr; + const char name[16]; +}; + +typedef struct { + uint64_t signature; + uint32_t revision; + uint32_t headersize; + uint32_t crc32; + uint32_t reserved; +} efi_table_hdr_t; + +struct efi_configuration_table { + efi_guid_t guid; + void *table; +}; + +struct efi_system_table { + efi_table_hdr_t hdr; + uint64_t fw_vendor; /* physical addr of CHAR16 vendor string */ + uint32_t fw_revision; + uint64_t con_in_handle; + uint64_t *con_in; + uint64_t con_out_handle; + uint64_t *con_out; + uint64_t stderr_handle; + uint64_t stderr_placeholder; + uint64_t *runtime; + uint64_t *boottime; + uint64_t nr_tables; + struct efi_configuration_table *tables; +}; + struct loongarch_boot_info { uint64_t ram_size; const char *kernel_filename; From 252b8e68994aa48508cf1c7d365967e3850b70bb Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:40 +0800 Subject: [PATCH 0261/2884] hw/loongarch: Init efi_boot_memmap table The efi_system_table adds a efi_boot_memmap configuration table. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-7-gaosong@loongson.cn> --- hw/loongarch/boot.c | 40 +++++++++++++++++++++++++++++++++++++ hw/loongarch/virt.c | 11 ++-------- include/hw/loongarch/boot.h | 27 +++++++++++++++++++++++++ include/hw/loongarch/virt.h | 10 ++++++++++ 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 59889dbc90..527fc9c0be 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -63,8 +63,41 @@ static const unsigned int slave_boot_code[] = { 0x4c000020, /* jirl $zero, $ra,0 */ }; +static inline void *guidcpy(void *dst, const void *src) +{ + return memcpy(dst, src, sizeof(efi_guid_t)); +} + +static void init_efi_boot_memmap(struct efi_system_table *systab, + void *p, void *start) +{ + unsigned i; + struct efi_boot_memmap *boot_memmap = p; + efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; + + /* efi_configuration_table 1 */ + guidcpy(&systab->tables[0].guid, &tbl_guid); + systab->tables[0].table = (struct efi_configuration_table *)(p - start); + systab->nr_tables = 1; + + boot_memmap->desc_size = sizeof(efi_memory_desc_t); + boot_memmap->desc_ver = 1; + boot_memmap->map_size = 0; + + efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap); + for (i = 0; i < memmap_entries; i++) { + map = (void *)boot_memmap + sizeof(*map); + map[i].type = memmap_table[i].type; + map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB); + map[i].num_pages = ROUND_DOWN(memmap_table[i].address + + memmap_table[i].length - map[i].phys_addr, 64 * KiB); + p += sizeof(efi_memory_desc_t); + } +} + static void init_systab(struct loongarch_boot_info *info, void *p, void *start) { + void *bp_tables_start; struct efi_system_table *systab = p; info->a2 = p - start; @@ -80,6 +113,13 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start) p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB); systab->tables = p; + bp_tables_start = p; + + init_efi_boot_memmap(systab, p, start); + p += ROUND_UP(sizeof(struct efi_boot_memmap) + + sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); + + systab->tables = (struct efi_configuration_table *)(bp_tables_start - start); } static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index bfb88aedab..708aa8bc60 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -378,15 +378,8 @@ static void virt_powerdown_req(Notifier *notifier, void *opaque) acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); } -struct memmap_entry { - uint64_t address; - uint64_t length; - uint32_t type; - uint32_t reserved; -}; - -static struct memmap_entry *memmap_table; -static unsigned memmap_entries; +struct memmap_entry *memmap_table; +unsigned memmap_entries; static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) { diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index cf0e4d4f91..76622af2e2 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -21,6 +21,15 @@ typedef struct { uint8_t b[16]; } efi_guid_t QEMU_ALIGNED(8); +#define EFI_GUID(a, b, c, d...) (efi_guid_t){ { \ + (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, d } } + +#define LINUX_EFI_BOOT_MEMMAP_GUID \ + EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, \ + 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) + struct efi_config_table { efi_guid_t guid; uint64_t *ptr; @@ -56,6 +65,24 @@ struct efi_system_table { struct efi_configuration_table *tables; }; +typedef struct { + uint32_t type; + uint32_t pad; + uint64_t phys_addr; + uint64_t virt_addr; + uint64_t num_pages; + uint64_t attribute; +} efi_memory_desc_t; + +struct efi_boot_memmap { + uint64_t map_size; + uint64_t desc_size; + uint32_t desc_ver; + uint64_t map_key; + uint64_t buff_size; + efi_memory_desc_t map[32]; +}; + struct loongarch_boot_info { uint64_t ram_size; const char *kernel_filename; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index d7a074d69f..8a9fe4053d 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -35,6 +35,16 @@ #define COMMAND_LINE_SIZE 512 +extern struct memmap_entry *memmap_table; +extern unsigned memmap_entries; + +struct memmap_entry { + uint64_t address; + uint64_t length; + uint32_t type; + uint32_t reserved; +}; + struct LoongArchMachineState { /*< private >*/ MachineState parent_obj; From 060685041ce6b98e5fb015b1d0318eb2f72944b5 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:41 +0800 Subject: [PATCH 0262/2884] hw/loongarch: Init efi_initrd table The efi_system_table adds a efi_initrd configuration table. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-8-gaosong@loongson.cn> --- hw/loongarch/boot.c | 23 +++++++++++++++++++++-- include/hw/loongarch/boot.h | 9 +++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 527fc9c0be..c8b3e742b4 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -15,6 +15,9 @@ #include "sysemu/reset.h" #include "sysemu/qtest.h" +ram_addr_t initrd_offset; +uint64_t initrd_size; + static const unsigned int slave_boot_code[] = { /* Configure reset ebase. */ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ @@ -95,6 +98,21 @@ static void init_efi_boot_memmap(struct efi_system_table *systab, } } +static void init_efi_initrd_table(struct efi_system_table *systab, + void *p, void *start) +{ + efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; + struct efi_initrd *initrd_table = p; + + /* efi_configuration_table 2 */ + guidcpy(&systab->tables[1].guid, &tbl_guid); + systab->tables[1].table = (struct efi_configuration_table *)(p - start); + systab->nr_tables = 2; + + initrd_table->base = initrd_offset; + initrd_table->size = initrd_size; +} + static void init_systab(struct loongarch_boot_info *info, void *p, void *start) { void *bp_tables_start; @@ -118,6 +136,8 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start) init_efi_boot_memmap(systab, p, start); p += ROUND_UP(sizeof(struct efi_boot_memmap) + sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); + init_efi_initrd_table(systab, p, start); + p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); systab->tables = (struct efi_configuration_table *)(bp_tables_start - start); } @@ -139,8 +159,7 @@ static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) static int64_t load_kernel_info(struct loongarch_boot_info *info) { - uint64_t kernel_entry, kernel_low, kernel_high, initrd_size; - ram_addr_t initrd_offset; + uint64_t kernel_entry, kernel_low, kernel_high; ssize_t kernel_size; kernel_size = load_elf(info->kernel_filename, NULL, diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index 76622af2e2..42d1ee3663 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -30,6 +30,10 @@ typedef struct { EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, \ 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) +#define LINUX_EFI_INITRD_MEDIA_GUID \ + EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, \ + 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) + struct efi_config_table { efi_guid_t guid; uint64_t *ptr; @@ -83,6 +87,11 @@ struct efi_boot_memmap { efi_memory_desc_t map[32]; }; +struct efi_initrd { + uint64_t base; + uint64_t size; +}; + struct loongarch_boot_info { uint64_t ram_size; const char *kernel_filename; From 6042385149a0fb3f4a51d264fc3dc42775819c79 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:42 +0800 Subject: [PATCH 0263/2884] hw/loongarch: Init efi_fdt table The efi_system_table adds a efi_fdt configuration table. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-9-gaosong@loongson.cn> --- hw/loongarch/boot.c | 11 +++++++++++ hw/loongarch/virt.c | 6 ++---- include/hw/loongarch/boot.h | 4 ++++ include/hw/loongarch/virt.h | 2 ++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index c8b3e742b4..7d1630b2e7 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -113,6 +113,16 @@ static void init_efi_initrd_table(struct efi_system_table *systab, initrd_table->size = initrd_size; } +static void init_efi_fdt_table(struct efi_system_table *systab) +{ + efi_guid_t tbl_guid = DEVICE_TREE_GUID; + + /* efi_configuration_table 3 */ + guidcpy(&systab->tables[2].guid, &tbl_guid); + systab->tables[2].table = (void *)FDT_BASE; + systab->nr_tables = 3; +} + static void init_systab(struct loongarch_boot_info *info, void *p, void *start) { void *bp_tables_start; @@ -138,6 +148,7 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start) sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); init_efi_initrd_table(systab, p, start); p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); + init_efi_fdt_table(systab); systab->tables = (struct efi_configuration_table *)(bp_tables_start - start); } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 708aa8bc60..42e5df8a24 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -727,7 +727,6 @@ static void loongarch_init(MachineState *machine) int nb_numa_nodes = machine->numa_state->num_nodes; NodeInfo *numa_info = machine->numa_state->nodes; int i; - hwaddr fdt_base; const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); CPUState *cpu; @@ -857,12 +856,11 @@ static void loongarch_init(MachineState *machine) * Put the FDT into the memory map as a ROM image: this will ensure * the FDT is copied again upon reset, even if addr points into RAM. */ - fdt_base = 1 * MiB; qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size); - rom_add_blob_fixed_as("fdt", machine->fdt, lams->fdt_size, fdt_base, + rom_add_blob_fixed_as("fdt", machine->fdt, lams->fdt_size, FDT_BASE, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, - rom_ptr_for_as(&address_space_memory, fdt_base, lams->fdt_size)); + rom_ptr_for_as(&address_space_memory, FDT_BASE, lams->fdt_size)); lams->bootinfo.ram_size = ram_size; loongarch_load_kernel(machine, &lams->bootinfo); diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index 42d1ee3663..4ebcc89dcf 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -34,6 +34,10 @@ typedef struct { EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, \ 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) +#define DEVICE_TREE_GUID \ + EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, \ + 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) + struct efi_config_table { efi_guid_t guid; uint64_t *ptr; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 8a9fe4053d..4e14bf6060 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -35,6 +35,8 @@ #define COMMAND_LINE_SIZE 512 +#define FDT_BASE 0x100000 + extern struct memmap_entry *memmap_table; extern unsigned memmap_entries; From b11f9814526b833b3a052be2559457b1affad7f5 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:43 +0800 Subject: [PATCH 0264/2884] hw/loongarch: Fix fdt memory node wrong 'reg' The right fdt memory node like [1], not [2] [1] memory@0 { device_type = "memory"; reg = <0x00 0x00 0x00 0x10000000>; }; [2] memory@0 { device_type = "memory"; reg = <0x02 0x00 0x02 0x10000000>; }; Reviewed-by: Bibo Mao Signed-off-by: Song Gao Message-Id: <20240426091551.2397867-10-gaosong@loongson.cn> --- hw/loongarch/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 42e5df8a24..032f5f2ddf 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -325,7 +325,7 @@ static void fdt_add_memory_node(MachineState *ms, char *nodename = g_strdup_printf("/memory@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, 0, size); qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); if (ms->numa_state && ms->numa_state->num_nodes) { From a0663efd81e6252b12ea716d13db10fdd022435b Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:44 +0800 Subject: [PATCH 0265/2884] hw/loongarch: fdt adds cpu interrupt controller node fdt adds cpu interrupt controller node, we use 'loongson,cpu-interrupt-controller'. See: https://github.com/torvalds/linux/blob/v6.7/drivers/irqchip/irq-loongarch-cpu.c https://lore.kernel.org/r/20221114113824.1880-2-liupeibao@loongson.cn Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-11-gaosong@loongson.cn> --- hw/loongarch/virt.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 032f5f2ddf..88cdcb6f91 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -106,6 +106,23 @@ static void virt_flash_map(LoongArchMachineState *lams, virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); } +static void fdt_add_cpuic_node(LoongArchMachineState *lams, + uint32_t *cpuintc_phandle) +{ + MachineState *ms = MACHINE(lams); + char *nodename; + + *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/cpuic"); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,cpu-interrupt-controller"); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + g_free(nodename); +} + static void fdt_add_flash_node(LoongArchMachineState *lams) { MachineState *ms = MACHINE(lams); @@ -528,6 +545,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) CPULoongArchState *env; CPUState *cpu_state; int cpu, pin, i, start, num; + uint32_t cpuintc_phandle; /* * The connection of interrupts: @@ -562,6 +580,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams) memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR, sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + /* Add cpu interrupt-controller */ + fdt_add_cpuic_node(lams, &cpuintc_phandle); + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { cpu_state = qemu_get_cpu(cpu); cpudev = DEVICE(cpu_state); From 975a5afe376c192b1e5d5e3eff435ac8188fe41c Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:45 +0800 Subject: [PATCH 0266/2884] hw/loongarch: fdt adds Extend I/O Interrupt Controller fdt adds Extend I/O Interrupt Controller, we use 'loongson,ls2k2000-eiointc'. See: https://github.com/torvalds/linux/blob/v6.7/drivers/irqchip/irq-loongson-eiointc.c https://lore.kernel.org/r/764e02d924094580ac0f1d15535f4b98308705c6.1683279769.git.zhoubinbin@loongson.cn Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-12-gaosong@loongson.cn> --- hw/loongarch/virt.c | 30 +++++++++++++++++++++++++++++- include/hw/intc/loongarch_extioi.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 88cdcb6f91..094b9d2e60 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -123,6 +123,31 @@ static void fdt_add_cpuic_node(LoongArchMachineState *lams, g_free(nodename); } +static void fdt_add_eiointc_node(LoongArchMachineState *lams, + uint32_t *cpuintc_phandle, + uint32_t *eiointc_phandle) +{ + MachineState *ms = MACHINE(lams); + char *nodename; + hwaddr extioi_base = APIC_BASE; + hwaddr extioi_size = EXTIOI_SIZE; + + *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,ls2k2000-eiointc"); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *cpuintc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, + extioi_base, 0x0, extioi_size); + g_free(nodename); +} + static void fdt_add_flash_node(LoongArchMachineState *lams) { MachineState *ms = MACHINE(lams); @@ -545,7 +570,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) CPULoongArchState *env; CPUState *cpu_state; int cpu, pin, i, start, num; - uint32_t cpuintc_phandle; + uint32_t cpuintc_phandle, eiointc_phandle; /* * The connection of interrupts: @@ -614,6 +639,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams) } } + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lams, &cpuintc_phandle, &eiointc_phandle); + pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); num = VIRT_PCH_PIC_IRQ_NUM; qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index a0a46b888c..410c6e1121 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -39,6 +39,7 @@ #define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) +#define EXTIOI_SIZE 0x800 typedef struct ExtIOICore { uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; From 2904f50a8119a7a3adee68df7baa18c073892061 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:46 +0800 Subject: [PATCH 0267/2884] hw/loongarch: fdt adds pch_pic Controller fdt adds pch pic controller, we use 'loongson,pch-pic-1.0' See: https://github.com/torvalds/linux/blob/v6.7/drivers/irqchip/irq-loongson-pch-pic.c https://lore.kernel.org/r/20200528152757.1028711-4-jiaxun.yang@flygoat.com Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-13-gaosong@loongson.cn> --- hw/loongarch/virt.c | 30 +++++++++++++++++++++++++++++- include/hw/pci-host/ls7a.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 094b9d2e60..5bef161d11 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -148,6 +148,31 @@ static void fdt_add_eiointc_node(LoongArchMachineState *lams, g_free(nodename); } +static void fdt_add_pch_pic_node(LoongArchMachineState *lams, + uint32_t *eiointc_phandle, + uint32_t *pch_pic_phandle) +{ + MachineState *ms = MACHINE(lams); + char *nodename; + hwaddr pch_pic_base = VIRT_PCH_REG_BASE; + hwaddr pch_pic_size = VIRT_PCH_REG_SIZE; + + *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,pch-pic-1.0"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, + pch_pic_base, 0, pch_pic_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *eiointc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0); + g_free(nodename); +} + static void fdt_add_flash_node(LoongArchMachineState *lams) { MachineState *ms = MACHINE(lams); @@ -570,7 +595,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) CPULoongArchState *env; CPUState *cpu_state; int cpu, pin, i, start, num; - uint32_t cpuintc_phandle, eiointc_phandle; + uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle; /* * The connection of interrupts: @@ -661,6 +686,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams) qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); } + /* Add PCH PIC node */ + fdt_add_pch_pic_node(lams, &eiointc_phandle, &pch_pic_phandle); + pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); start = num; num = EXTIOI_IRQS - start; diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index e753449593..fe260f0183 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -24,6 +24,7 @@ #define VIRT_PCH_REG_BASE 0x10000000UL #define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) #define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL +#define VIRT_PCH_REG_SIZE 0x400 /* * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot From 572d45e5469eb323a5a8b060ad195b9aac82ecc3 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:47 +0800 Subject: [PATCH 0268/2884] hw/loongarch: fdt adds pch_msi Controller fdt adds pch msi controller, we use 'loongson,pch-msi-1.0'. See: https://github.com/torvalds/linux/blob/v6.7/drivers/irqchip/irq-loongson-pch-msi.c https://lore.kernel.org/r/20200528152757.1028711-6-jiaxun.yang@flygoat.com Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-14-gaosong@loongson.cn> --- hw/loongarch/virt.c | 33 ++++++++++++++++++++++++++++++++- include/hw/pci-host/ls7a.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 5bef161d11..566f86e741 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -173,6 +173,34 @@ static void fdt_add_pch_pic_node(LoongArchMachineState *lams, g_free(nodename); } +static void fdt_add_pch_msi_node(LoongArchMachineState *lams, + uint32_t *eiointc_phandle, + uint32_t *pch_msi_phandle) +{ + MachineState *ms = MACHINE(lams); + char *nodename; + hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW; + hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE; + + *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,pch-msi-1.0"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", + 0, pch_msi_base, + 0, pch_msi_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *eiointc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec", + VIRT_PCH_PIC_IRQ_NUM); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs", + EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM); + g_free(nodename); +} + static void fdt_add_flash_node(LoongArchMachineState *lams) { MachineState *ms = MACHINE(lams); @@ -595,7 +623,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) CPULoongArchState *env; CPUState *cpu_state; int cpu, pin, i, start, num; - uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle; + uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; /* * The connection of interrupts: @@ -703,6 +731,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams) qdev_get_gpio_in(extioi, i + start)); } + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lams, &eiointc_phandle, &pch_msi_phandle); + loongarch_devices_init(pch_pic, lams); } diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index fe260f0183..cd7c9ec7bc 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -25,6 +25,7 @@ #define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) #define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL #define VIRT_PCH_REG_SIZE 0x400 +#define VIRT_PCH_MSI_SIZE 0x8 /* * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot From 07bf0b6aa1707a6d8d9c3132424df32f42915d66 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:48 +0800 Subject: [PATCH 0269/2884] hw/loongarch: fdt adds pcie irq_map node This patch adds pcie irq_map node for FDT. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-15-gaosong@loongson.cn> --- hw/loongarch/virt.c | 73 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 566f86e741..91a55a81b6 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -352,7 +352,62 @@ static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) g_free(nodename); } -static void fdt_add_pcie_node(const LoongArchMachineState *lams) +static void fdt_add_pcie_irq_map_node(const LoongArchMachineState *lams, + char *nodename, + uint32_t *pch_pic_phandle) +{ + int pin, dev; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {}; + uint32_t *irq_map = full_irq_map; + const MachineState *ms = MACHINE(lams); + + /* This code creates a standard swizzle of interrupts such that + * each device's first interrupt is based on it's PCI_SLOT number. + * (See pci_swizzle_map_irq_fn()) + * + * We only need one entry per interrupt in the table (not one per + * possible slot) seeing the interrupt-map-mask will allow the table + * to wrap to any number of devices. + */ + + for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { + int devfn = dev * 0x8; + + for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { + int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + int i = 0; + + /* Fill PCI address cells */ + irq_map[i] = cpu_to_be32(devfn << 8); + i += 3; + + /* Fill PCI Interrupt cells */ + irq_map[i] = cpu_to_be32(pin + 1); + i += 1; + + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(*pch_pic_phandle); + irq_map[i++] = cpu_to_be32(irq_nr); + + if (!irq_map_stride) { + irq_map_stride = i; + } + irq_map += irq_map_stride; + } + } + + + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map, + GPEX_NUM_IRQS * GPEX_NUM_IRQS * + irq_map_stride * sizeof(uint32_t)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + +static void fdt_add_pcie_node(const LoongArchMachineState *lams, + uint32_t *pch_pic_phandle, + uint32_t *pch_msi_phandle) { char *nodename; hwaddr base_mmio = VIRT_PCI_MEM_BASE; @@ -383,6 +438,11 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams) 2, base_pio, 2, size_pio, 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, 2, base_mmio, 2, size_mmio); + qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", + 0, *pch_msi_phandle, 0, 0x10000); + + fdt_add_pcie_irq_map_node(lams, nodename, pch_pic_phandle); + g_free(nodename); } @@ -542,7 +602,10 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) return dev; } -static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams) +static void loongarch_devices_init(DeviceState *pch_pic, + LoongArchMachineState *lams, + uint32_t *pch_pic_phandle, + uint32_t *pch_msi_phandle) { MachineClass *mc = MACHINE_GET_CLASS(lams); DeviceState *gpex_dev; @@ -588,6 +651,9 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); } + /* Add pcie node */ + fdt_add_pcie_node(lams, pch_pic_phandle, pch_msi_phandle); + serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0, qdev_get_gpio_in(pch_pic, VIRT_UART_IRQ - VIRT_GSI_BASE), @@ -734,7 +800,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) /* Add PCH MSI node */ fdt_add_pch_msi_node(lams, &eiointc_phandle, &pch_msi_phandle); - loongarch_devices_init(pch_pic, lams); + loongarch_devices_init(pch_pic, lams, &pch_pic_phandle, &pch_msi_phandle); } static void loongarch_firmware_init(LoongArchMachineState *lams) @@ -956,7 +1022,6 @@ static void loongarch_init(MachineState *machine) lams->powerdown_notifier.notify = virt_powerdown_req; qemu_register_powerdown_notifier(&lams->powerdown_notifier); - fdt_add_pcie_node(lams); /* * Since lowmem region starts from 0 and Linux kernel legacy start address * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer From 22126fdb1c4f71d7ff02327e14b612d77126334b Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:49 +0800 Subject: [PATCH 0270/2884] hw/loongarch: fdt remove unused irqchip node This patch removes the unused fdt irqchip node. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-16-gaosong@loongson.cn> --- hw/loongarch/virt.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 91a55a81b6..48f73edd8e 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -446,34 +446,6 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams, g_free(nodename); } -static void fdt_add_irqchip_node(LoongArchMachineState *lams) -{ - MachineState *ms = MACHINE(lams); - char *nodename; - uint32_t irqchip_phandle; - - irqchip_phandle = qemu_fdt_alloc_phandle(ms->fdt); - qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", irqchip_phandle); - - nodename = g_strdup_printf("/intc@%lx", VIRT_IOAPIC_REG_BASE); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3); - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2); - qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0); - - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongarch,ls7a"); - - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, VIRT_IOAPIC_REG_BASE, - 2, PCH_PIC_ROUTE_ENTRY_OFFSET); - - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", irqchip_phandle); - g_free(nodename); -} - static void fdt_add_memory_node(MachineState *ms, uint64_t base, uint64_t size, int node_id) { @@ -1011,8 +983,7 @@ static void loongarch_init(MachineState *machine) /* Initialize the IO interrupt subsystem */ loongarch_irq_init(lams); - fdt_add_irqchip_node(lams); - platform_bus_add_all_fdt_nodes(machine->fdt, "/intc", + platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", VIRT_PLATFORM_BUS_BASEADDRESS, VIRT_PLATFORM_BUS_SIZE, VIRT_PLATFORM_BUS_IRQ); From f5cce57f6aca89c83b7f99b11b93ad9212b1573e Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:50 +0800 Subject: [PATCH 0271/2884] hw/loongarch: Add cells missing from uart node uart node need interrupts and interrupt-parent cells. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-17-gaosong@loongson.cn> --- hw/loongarch/virt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 48f73edd8e..dbd7928759 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -245,7 +245,8 @@ static void fdt_add_rtc_node(LoongArchMachineState *lams) g_free(nodename); } -static void fdt_add_uart_node(LoongArchMachineState *lams) +static void fdt_add_uart_node(LoongArchMachineState *lams, + uint32_t *pch_pic_phandle) { char *nodename; hwaddr base = VIRT_UART_BASE; @@ -258,6 +259,10 @@ static void fdt_add_uart_node(LoongArchMachineState *lams) qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", + VIRT_UART_IRQ - VIRT_GSI_BASE, 0x4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *pch_pic_phandle); g_free(nodename); } @@ -630,7 +635,7 @@ static void loongarch_devices_init(DeviceState *pch_pic, qdev_get_gpio_in(pch_pic, VIRT_UART_IRQ - VIRT_GSI_BASE), 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - fdt_add_uart_node(lams); + fdt_add_uart_node(lams, pch_pic_phandle); /* Network init */ pci_init_nic_devices(pci_bus, mc->default_nic); From 841ef2c9df5317e32de590424b0c5c36fbb4de78 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 26 Apr 2024 17:15:51 +0800 Subject: [PATCH 0272/2884] hw/loongarch: Add cells missing from rtc node rtc node need interrupts and interrupt-parent cells. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240426091551.2397867-18-gaosong@loongson.cn> --- hw/loongarch/virt.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index dbd7928759..c0999878df 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -231,7 +231,8 @@ static void fdt_add_flash_node(LoongArchMachineState *lams) g_free(nodename); } -static void fdt_add_rtc_node(LoongArchMachineState *lams) +static void fdt_add_rtc_node(LoongArchMachineState *lams, + uint32_t *pch_pic_phandle) { char *nodename; hwaddr base = VIRT_RTC_REG_BASE; @@ -240,8 +241,13 @@ static void fdt_add_rtc_node(LoongArchMachineState *lams) nodename = g_strdup_printf("/rtc@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "loongson,ls7a-rtc"); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,ls7a-rtc"); qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", + VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *pch_pic_phandle); g_free(nodename); } @@ -648,7 +654,7 @@ static void loongarch_devices_init(DeviceState *pch_pic, sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, qdev_get_gpio_in(pch_pic, VIRT_RTC_IRQ - VIRT_GSI_BASE)); - fdt_add_rtc_node(lams); + fdt_add_rtc_node(lams, pch_pic_phandle); /* acpi ged */ lams->acpi_ged = create_acpi_ged(pch_pic, lams); From f2c8aeb1afefcda92054c448b21fc59cdd99db30 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Mon, 5 Feb 2024 12:20:15 +0900 Subject: [PATCH 0273/2884] hw/ufs: Fix buffer overflow bug It fixes the buffer overflow vulnerability in the ufs device. The bug was detected by sanitizers. You can reproduce it by: cat << EOF |\ qemu-system-x86_64 \ -display none -machine accel=qtest -m 512M -M q35 -nodefaults -drive \ file=null-co://,if=none,id=disk0 -device ufs,id=ufs_bus -device \ ufs-lu,drive=disk0,bus=ufs_bus -qtest stdio outl 0xcf8 0x80000810 outl 0xcfc 0xe0000000 outl 0xcf8 0x80000804 outw 0xcfc 0x06 write 0xe0000058 0x1 0xa7 write 0xa 0x1 0x50 EOF Resolves: #2299 Fixes: 329f16624499 ("hw/ufs: Support for Query Transfer Requests") Reported-by: Zheyu Ma Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index eccdb852a0..bac78a32bb 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -126,6 +126,10 @@ static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + data_segment_length; + if (copy_size > sizeof(req->req_upiu)) { + copy_size = sizeof(req->req_upiu); + } + ret = ufs_addr_read(u, req_upiu_base_addr, &req->req_upiu, copy_size); if (ret) { trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); @@ -225,6 +229,10 @@ static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req) copy_size = rsp_upiu_byte_len; } + if (copy_size > sizeof(req->rsp_upiu)) { + copy_size = sizeof(req->rsp_upiu); + } + ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size); if (ret) { trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr); From 49843214368eccbc46ad3946aae8cc81eaced98e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 20 Apr 2024 07:46:04 +0200 Subject: [PATCH 0274/2884] target/i386/cpu: Remove "x86" prefix from the CPU list Printing an "x86" in front of each CPU name is not helpful at all: It is confusing for the users since they don't know whether they have to specify these letters for the "-cpu" parameter, too, and it also takes some precious space in the dense output of the CPU entries. Let's simply remove this now and use two spaces at the beginning of the lines for the indentation of the entries instead, like most other target architectures are doing it for their CPU help output already. Signed-off-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fa1ea3735d..aa3b2d8391 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5708,7 +5708,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("%s (deprecated)", olddesc); } - qemu_printf("x86 %-20s %s\n", name, desc); + qemu_printf(" %-20s %s\n", name, desc); } /* list available CPU models and flags */ From 7febce361da200fa71e71e40316e4a6d2a5b40ab Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 20 Apr 2024 07:46:05 +0200 Subject: [PATCH 0275/2884] target/s390x/cpu_models: Rework the output of "-cpu help" Printing an "s390x" in front of each CPU name is not helpful at all: It is confusing for the users since they don't know whether they have to specify these letters for the "-cpu" parameter, too, and it also takes some precious space in the dense output of the CPU entries. Let's simply remove this now! While we're at it, use two spaces at the beginning of the lines for the indentation of the entries, and add a "Available CPUs" in the very first line, like most other target architectures are doing it for their "-cpu help" output already. Signed-off-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- target/s390x/cpu_models.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 8ed3bb6a27..58c58f05a0 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -355,9 +355,9 @@ static void s390_print_cpu_model_list_entry(gpointer data, gpointer user_data) /* strip off the -s390x-cpu */ g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; if (details->len) { - qemu_printf("s390 %-15s %-35s (%s)\n", name, scc->desc, details->str); + qemu_printf(" %-15s %-35s (%s)\n", name, scc->desc, details->str); } else { - qemu_printf("s390 %-15s %-35s\n", name, scc->desc); + qemu_printf(" %-15s %-35s\n", name, scc->desc); } g_free(name); } @@ -402,6 +402,7 @@ void s390_cpu_list(void) S390Feat feat; GSList *list; + qemu_printf("Available CPUs:\n"); list = object_class_get_list(TYPE_S390_CPU, false); list = g_slist_sort(list, s390_cpu_list_compare); g_slist_foreach(list, s390_print_cpu_model_list_entry, NULL); @@ -411,14 +412,14 @@ void s390_cpu_list(void) for (feat = 0; feat < S390_FEAT_MAX; feat++) { const S390FeatDef *def = s390_feat_def(feat); - qemu_printf("%-20s %s\n", def->name, def->desc); + qemu_printf(" %-20s %s\n", def->name, def->desc); } qemu_printf("\nRecognized feature groups:\n"); for (group = 0; group < S390_FEAT_GROUP_MAX; group++) { const S390FeatGroupDef *def = s390_feat_group_def(group); - qemu_printf("%-20s %s\n", def->name, def->desc); + qemu_printf(" %-20s %s\n", def->name, def->desc); } } From 5b638f6e900efd1d5f5d0697af69a0e9eb2bfc72 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sat, 20 Apr 2024 07:46:06 +0200 Subject: [PATCH 0276/2884] target/ppc/cpu_init: Remove "PowerPC" prefix from the CPU list Printing a "PowerPC" in front of each CPU name is not helpful at all: It is confusing for the users since they don't know whether they have to specify these letters for the "-cpu" parameter, too, and it also takes some precious space in the dense output of the CPU entries. Let's simply remove this now and use two spaces at the beginning of the lines for the indentation of the entries instead, and add a "Available CPUs" in the very first line, like most other target architectures are doing it for their CPU help output already. Signed-off-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- target/ppc/cpu_init.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6d82f24c87..c11a69fd90 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7063,7 +7063,7 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) } name = cpu_model_from_type(typename); - qemu_printf("PowerPC %-16s PVR %08x\n", name, pcc->pvr); + qemu_printf(" %-16s PVR %08x\n", name, pcc->pvr); for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; ObjectClass *alias_oc = ppc_cpu_class_by_name(alias->model); @@ -7076,10 +7076,10 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) * avoid printing the wrong alias here and use "preferred" instead */ if (strcmp(alias->alias, family->desc) == 0) { - qemu_printf("PowerPC %-16s (alias for preferred %s CPU)\n", + qemu_printf(" %-16s (alias for preferred %s CPU)\n", alias->alias, family->desc); } else { - qemu_printf("PowerPC %-16s (alias for %s)\n", + qemu_printf(" %-16s (alias for %s)\n", alias->alias, name); } } @@ -7090,6 +7090,7 @@ void ppc_cpu_list(void) { GSList *list; + qemu_printf("Available CPUs:\n"); list = object_class_get_list(TYPE_POWERPC_CPU, false); list = g_slist_sort(list, ppc_cpu_list_compare); g_slist_foreach(list, ppc_cpu_list_entry, NULL); @@ -7097,7 +7098,7 @@ void ppc_cpu_list(void) #ifdef CONFIG_KVM qemu_printf("\n"); - qemu_printf("PowerPC %s\n", "host"); + qemu_printf(" %s\n", "host"); #endif } From e3812d109663b8fee28e9334bbc12f684355984e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 2 Apr 2024 13:59:57 +0200 Subject: [PATCH 0277/2884] scripts/checkpatch: Avoid author email mangled by qemu-*@nongnu.org MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f5177798d8 ("scripts: report on author emails that are mangled by the mailing list") added a check for qemu-devel@ list, extend the regexp to cover more such qemu-trivial@, qemu-block@ and qemu-ppc@. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 7026895074..12e9028b10 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1573,7 +1573,7 @@ sub process { $is_patch = 1; } - if ($line =~ /^(Author|From): .* via .*/) { + if ($line =~ /^(Author|From): .* via .*/) { ERROR("Author email address is mangled by the mailing list\n" . $herecurr); } From af692fd338154a20010c78a6bd9acb2c889dd4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 2 Apr 2024 13:59:58 +0200 Subject: [PATCH 0278/2884] scripts/checkpatch: Do not use mailmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .mailmap file fixes mistake we already did. Do not use it when running checkpatch.pl, otherwise we might commit the very same mistakes. Reported-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- scripts/checkpatch.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 12e9028b10..76a0b79266 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -435,8 +435,8 @@ if ($chk_branch) { my @patches; my %git_commits = (); my $HASH; - open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--format=%H %s", $ARGV[0]) || - die "$P: git log --reverse --no-merges --format='%H %s' $ARGV[0] failed - $!\n"; + open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--no-mailmap", "--format=%H %s", $ARGV[0]) || + die "$P: git log --reverse --no-merges --no-mailmap --format='%H %s' $ARGV[0] failed - $!\n"; for my $line (<$HASH>) { $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; @@ -460,7 +460,7 @@ if ($chk_branch) { "-c", "diff.renamelimit=0", "-c", "diff.renames=True", "-c", "diff.algorithm=histogram", - "show", + "show", "--no-mailmap", "--patch-with-stat", $hash) || die "$P: git show $hash - $!\n"; while (<$FILE>) { From 06479dbf3d7d245572c4b3016e5a1d923ff04d66 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Mon, 22 Apr 2024 16:53:12 +0800 Subject: [PATCH 0279/2884] backends/cryptodev-builtin: Fix local_error leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems that this error does not need to be propagated to the upper, directly output the error to avoid the leaks Closes: https://gitlab.com/qemu-project/qemu/-/issues/2283 Fixes: 2fda101de07 ("virtio-crypto: Support asynchronous mode") Signed-off-by: Li Zhijian Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: zhenwei pi Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- backends/cryptodev-builtin.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index a514bbb310..940104ee55 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -23,6 +23,7 @@ #include "qemu/osdep.h" #include "sysemu/cryptodev.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" #include "crypto/cipher.h" @@ -396,8 +397,8 @@ static int cryptodev_builtin_create_session( case VIRTIO_CRYPTO_HASH_CREATE_SESSION: case VIRTIO_CRYPTO_MAC_CREATE_SESSION: default: - error_setg(&local_error, "Unsupported opcode :%" PRIu32 "", - sess_info->op_code); + error_report("Unsupported opcode :%" PRIu32 "", + sess_info->op_code); return -VIRTIO_CRYPTO_NOTSUPP; } @@ -554,8 +555,8 @@ static int cryptodev_builtin_operation( if (op_info->session_id >= MAX_NUM_SESSIONS || builtin->sessions[op_info->session_id] == NULL) { - error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "", - op_info->session_id); + error_report("Cannot find a valid session id: %" PRIu64 "", + op_info->session_id); return -VIRTIO_CRYPTO_INVSESS; } From 0cbb322f70e8a87e4acbffecef5ea8f9448f3513 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Wed, 24 Apr 2024 23:18:56 +0300 Subject: [PATCH 0280/2884] target/loongarch/cpu.c: typo fix: expection Fixes: 1590154ee437 ("target/loongarch: Fix qemu-system-loongarch64 assert failed with the option '-d int'") Signed-off-by: Michael Tokarev Reviewed-by: Richard Henderson --- target/loongarch/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index bac84dca7a..1ebba043f4 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -92,7 +92,7 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, { CPUState *cs = env_cpu(env); - qemu_log_mask(CPU_LOG_INT, "%s: expection: %d (%s)\n", + qemu_log_mask(CPU_LOG_INT, "%s: exception: %d (%s)\n", __func__, exception, loongarch_exception_name(exception)); From e4426353175f21f54095701c704ba4c50724cb80 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 24 Apr 2024 17:24:24 -0300 Subject: [PATCH 0281/2884] target/riscv/kvm: remove sneaky strerrorname_np() instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d424db2354 excluded some strerrorname_np() instances because they break musl libc builds. Another instance happened to slip by via commit d4ff3da8f4. Remove it before it causes trouble again. Fixes: d4ff3da8f4 (target/riscv/kvm: initialize 'vlenb' via get-reg-list) Signed-off-by: Daniel Henrique Barboza Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- target/riscv/kvm/kvm-cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 49d2f3ad58..eaa36121c7 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1054,8 +1054,8 @@ static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); if (ret != 0) { - error_report("Unable to read vlenb register, error code: %s", - strerrorname_np(errno)); + error_report("Unable to read vlenb register, error code: %d", + errno); exit(EXIT_FAILURE); } From ce1992d45c875c29a9018b7ac2fa9bad6587c711 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 24 Apr 2024 17:24:25 -0300 Subject: [PATCH 0282/2884] checkpatch.pl: forbid strerrorname_np() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d424db2354 removed an instance of strerrorname_np() because it was breaking building with musl libc. A recent RISC-V patch ended up re-introducing it again by accident. Put this function in the baddies list in checkpatch.pl to avoid this situation again. This is what it will look like next time: $ ./scripts/checkpatch.pl 0001-temp-test.patch ERROR: use strerror() instead of strerrorname_np() #22: FILE: target/riscv/kvm/kvm-cpu.c:1058: + strerrorname_np(errno)); total: 1 errors, 0 warnings, 10 lines checked Signed-off-by: Daniel Henrique Barboza Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- scripts/checkpatch.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 76a0b79266..ff373a7083 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3078,6 +3078,9 @@ sub process { if ($line =~ /\b(g_)?assert\(0\)/) { ERROR("use g_assert_not_reached() instead of assert(0)\n" . $herecurr); } + if ($line =~ /\bstrerrorname_np\(/) { + ERROR("use strerror() instead of strerrorname_np()\n" . $herecurr); + } my $non_exit_glib_asserts = qr{g_assert_cmpstr| g_assert_cmpint| g_assert_cmpuint| From 7c0c099a88fd45b3598118dd7dce9ba64a1d41b7 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 25 Apr 2024 11:12:26 +0800 Subject: [PATCH 0283/2884] target/s390x/cpu_model: Make check_compatibility() return boolean As error.h suggested, the best practice for callee is to return something to indicate success / failure. With returned boolean, there's no need to check @err. Suggested-by: Thomas Huth Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Message-ID: <20240425031232.1586401-2-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 8ed3bb6a27..8cb47d905f 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -510,7 +510,7 @@ static void check_compat_model_failed(Error **errp, return; } -static void check_compatibility(const S390CPUModel *max_model, +static bool check_compatibility(const S390CPUModel *max_model, const S390CPUModel *model, Error **errp) { ERRP_GUARD(); @@ -518,11 +518,11 @@ static void check_compatibility(const S390CPUModel *max_model, if (model->def->gen > max_model->def->gen) { check_compat_model_failed(errp, max_model, "Selected CPU generation is too new"); - return; + return false; } else if (model->def->gen == max_model->def->gen && model->def->ec_ga > max_model->def->ec_ga) { check_compat_model_failed(errp, max_model, "Selected CPU GA level is too new"); - return; + return false; } #ifndef CONFIG_USER_ONLY @@ -530,14 +530,14 @@ static void check_compatibility(const S390CPUModel *max_model, error_setg(errp, "The unpack facility is not compatible with " "the --only-migratable option. You must remove either " "the 'unpack' facility or the --only-migratable option"); - return; + return false; } #endif /* detect the missing features to properly report them */ bitmap_andnot(missing, model->features, max_model->features, S390_FEAT_MAX); if (bitmap_empty(missing, S390_FEAT_MAX)) { - return; + return true; } error_setg(errp, " "); @@ -546,6 +546,7 @@ static void check_compatibility(const S390CPUModel *max_model, "available in the current configuration: "); error_append_hint(errp, "Consider a different accelerator, QEMU, or kernel version\n"); + return false; } S390CPUModel *get_max_cpu_model(Error **errp) @@ -605,8 +606,7 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) cpu->model->cpu_ver = max_model->cpu_ver; check_consistency(cpu->model); - check_compatibility(max_model, cpu->model, &err); - if (err) { + if (!check_compatibility(max_model, cpu->model, &err)) { error_propagate(errp, err); return; } From 9c2df9c5e849ce2c24a6518a56e6e44371ff541e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 25 Apr 2024 11:12:27 +0800 Subject: [PATCH 0284/2884] target/s390x/cpu_model: Drop local @err in s390_realize_cpu_model() Use @errp to fetch error information directly and drop the local variable @err. Suggested-by: Thomas Huth Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Message-ID: <20240425031232.1586401-3-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 8cb47d905f..052540a866 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -577,7 +577,6 @@ S390CPUModel *get_max_cpu_model(Error **errp) void s390_realize_cpu_model(CPUState *cs, Error **errp) { ERRP_GUARD(); - Error *err = NULL; S390CPUClass *xcc = S390_CPU_GET_CLASS(cs); S390CPU *cpu = S390_CPU(cs); const S390CPUModel *max_model; @@ -606,8 +605,7 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) cpu->model->cpu_ver = max_model->cpu_ver; check_consistency(cpu->model); - if (!check_compatibility(max_model, cpu->model, &err)) { - error_propagate(errp, err); + if (!check_compatibility(max_model, cpu->model, errp)) { return; } From 47ab3b21374627419cd400141cacd534b9281f7b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 25 Apr 2024 11:12:29 +0800 Subject: [PATCH 0285/2884] target/s390x/cpu_models: Make kvm_s390_get_host_cpu_model() return boolean As error.h suggested, the best practice for callee is to return something to indicate success / failure. So make kvm_s390_get_host_cpu_model() return boolean and check the returned boolean in get_max_cpu_model() instead of accessing @err. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Message-ID: <20240425031232.1586401-5-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 9 ++++----- target/s390x/cpu_models.h | 2 +- target/s390x/kvm/kvm.c | 13 +++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 052540a866..a0e4acb707 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -560,16 +560,15 @@ S390CPUModel *get_max_cpu_model(Error **errp) } if (kvm_enabled()) { - kvm_s390_get_host_cpu_model(&max_model, &err); + if (!kvm_s390_get_host_cpu_model(&max_model, &err)) { + error_propagate(errp, err); + return NULL; + } } else { max_model.def = s390_find_cpu_def(QEMU_MAX_CPU_TYPE, QEMU_MAX_CPU_GEN, QEMU_MAX_CPU_EC_GA, NULL); bitmap_copy(max_model.features, qemu_max_cpu_feat, S390_FEAT_MAX); } - if (err) { - error_propagate(errp, err); - return NULL; - } cached = true; return &max_model; } diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index a89c2a15ab..c14aff6c10 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -115,7 +115,7 @@ S390CPUDef const *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga, S390FeatBitmap features); bool kvm_s390_cpu_models_supported(void); -void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); +bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp); #endif /* TARGET_S390X_CPU_MODELS_H */ diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 4dcd757cdc..2c3e05cae3 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -2375,7 +2375,7 @@ bool kvm_s390_cpu_models_supported(void) KVM_S390_VM_CPU_MACHINE_SUBFUNC); } -void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) +bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) { struct kvm_s390_vm_cpu_machine prop = {}; struct kvm_device_attr attr = { @@ -2390,14 +2390,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) if (!kvm_s390_cpu_models_supported()) { error_setg(errp, "KVM doesn't support CPU models"); - return; + return false; } /* query the basic cpu model properties */ rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); if (rc) { error_setg(errp, "KVM: Error querying host CPU model: %d", rc); - return; + return false; } cpu_type = cpuid_type(prop.cpuid); @@ -2420,13 +2420,13 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) rc = query_cpu_feat(model->features); if (rc) { error_setg(errp, "KVM: Error querying CPU features: %d", rc); - return; + return false; } /* get supported cpu subfunctions indicated via query / test bit */ rc = query_cpu_subfunc(model->features); if (rc) { error_setg(errp, "KVM: Error querying CPU subfunctions: %d", rc); - return; + return false; } /* PTFF subfunctions might be indicated although kernel support missing */ @@ -2482,7 +2482,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) } if (!model->def) { error_setg(errp, "KVM: host CPU model could not be identified"); - return; + return false; } /* for now, we can only provide the AP feature with HW support */ if (ap_available()) { @@ -2506,6 +2506,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) /* strip of features that are not part of the maximum model */ bitmap_and(model->features, model->features, model->def->full_feat, S390_FEAT_MAX); + return true; } static int configure_uv_feat_guest(const S390FeatBitmap features) From c6f1baf2d5a91fd2dcb31c3911fa5bec878faf6f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 25 Apr 2024 11:12:30 +0800 Subject: [PATCH 0286/2884] target/s390x/cpu_models: Drop local @err in get_max_cpu_model() Use @errp to fetch error information directly and drop the local variable @err. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Message-ID: <20240425031232.1586401-6-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index a0e4acb707..aae452cfd3 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -551,7 +551,6 @@ static bool check_compatibility(const S390CPUModel *max_model, S390CPUModel *get_max_cpu_model(Error **errp) { - Error *err = NULL; static S390CPUModel max_model; static bool cached; @@ -560,8 +559,7 @@ S390CPUModel *get_max_cpu_model(Error **errp) } if (kvm_enabled()) { - if (!kvm_s390_get_host_cpu_model(&max_model, &err)) { - error_propagate(errp, err); + if (!kvm_s390_get_host_cpu_model(&max_model, errp)) { return NULL; } } else { From 38098df346b3daaa8771f2aee64c6b47d4c00e56 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 25 Apr 2024 11:12:31 +0800 Subject: [PATCH 0287/2884] target/s390x/cpu_models: Make kvm_s390_apply_cpu_model() return boolean As error.h suggested, the best practice for callee is to return something to indicate success / failure. So make kvm_s390_apply_cpu_model() return boolean and check the returned boolean in apply_cpu_model() instead of accessing @err. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Message-ID: <20240425031232.1586401-7-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models.h | 2 +- target/s390x/cpu_models_sysemu.c | 3 +-- target/s390x/kvm/kvm.c | 15 ++++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index c14aff6c10..71d4bc2dd4 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -116,6 +116,6 @@ S390CPUDef const *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga, bool kvm_s390_cpu_models_supported(void); bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); -void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp); +bool kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp); #endif /* TARGET_S390X_CPU_MODELS_H */ diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index 2d99218069..bf855c659d 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -405,8 +405,7 @@ void apply_cpu_model(const S390CPUModel *model, Error **errp) } if (kvm_enabled()) { - kvm_s390_apply_cpu_model(model, &err); - if (err) { + if (!kvm_s390_apply_cpu_model(model, &err)) { error_propagate(errp, err); return; } diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 2c3e05cae3..1b494ecc20 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -2543,7 +2543,7 @@ static void kvm_s390_configure_apie(bool interpret) } } -void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) +bool kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) { struct kvm_s390_vm_cpu_processor prop = { .fac_list = { 0 }, @@ -2560,11 +2560,11 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) if (kvm_s390_cmma_available()) { kvm_s390_enable_cmma(); } - return; + return true; } if (!kvm_s390_cpu_models_supported()) { error_setg(errp, "KVM doesn't support CPU models"); - return; + return false; } prop.cpuid = s390_cpuid_from_cpu_model(model); prop.ibc = s390_ibc_from_cpu_model(model); @@ -2574,19 +2574,19 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); if (rc) { error_setg(errp, "KVM: Error configuring the CPU model: %d", rc); - return; + return false; } /* configure cpu features indicated e.g. via SCLP */ rc = configure_cpu_feat(model->features); if (rc) { error_setg(errp, "KVM: Error configuring CPU features: %d", rc); - return; + return false; } /* configure cpu subfunctions indicated via query / test bit */ rc = configure_cpu_subfunc(model->features); if (rc) { error_setg(errp, "KVM: Error configuring CPU subfunctions: %d", rc); - return; + return false; } /* enable CMM via CMMA */ if (test_bit(S390_FEAT_CMM, model->features)) { @@ -2601,8 +2601,9 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) rc = configure_uv_feat_guest(model->features); if (rc) { error_setg(errp, "KVM: Error configuring CPU UV features %d", rc); - return; + return false; } + return true; } void kvm_s390_restart_interrupt(S390CPU *cpu) From 046bf2a6184f0a87b89b735ef77edd9a13a96656 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 25 Apr 2024 11:12:32 +0800 Subject: [PATCH 0288/2884] target/s390x/cpu_models_sysemu: Drop local @err in apply_cpu_model() Use @errp to fetch error information directly and drop the local variable @err. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Message-ID: <20240425031232.1586401-8-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models_sysemu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index bf855c659d..15be729c3d 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -389,7 +389,6 @@ CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, void apply_cpu_model(const S390CPUModel *model, Error **errp) { - Error *err = NULL; static S390CPUModel applied_model; static bool applied; @@ -405,8 +404,7 @@ void apply_cpu_model(const S390CPUModel *model, Error **errp) } if (kvm_enabled()) { - if (!kvm_s390_apply_cpu_model(model, &err)) { - error_propagate(errp, err); + if (!kvm_s390_apply_cpu_model(model, errp)) { return; } } From 69826741593644f6e9ee735cff37599c33764d67 Mon Sep 17 00:00:00 2001 From: Chris Friedt Date: Tue, 18 Oct 2022 08:25:49 -0400 Subject: [PATCH 0289/2884] hw: misc: edu: fix 2 off-by-one errors In the case that size1 was zero, because of the explicit 'end1 > addr' check, the range check would fail and the error message would read as shown below. The correct comparison is 'end1 >= addr'. EDU: DMA range 0x40000-0x3ffff out of bounds (0x40000-0x40fff)! Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1254 Signed-off-by: Chris Friedt [thuth: Adjust patch with regards to the "end1 <= end2" check] Message-ID: <20221018122551.94567-1-cfriedt@meta.com> Signed-off-by: Thomas Huth --- hw/misc/edu.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 2a976ca2b1..14250e0ac3 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -103,19 +103,18 @@ static void edu_lower_irq(EduState *edu, uint32_t val) } } -static bool within(uint64_t addr, uint64_t start, uint64_t end) -{ - return start <= addr && addr < end; -} - -static void edu_check_range(uint64_t addr, uint64_t size1, uint64_t start, - uint64_t size2) +static void edu_check_range(uint64_t addr, uint64_t size1, + uint64_t start, uint64_t size2) { uint64_t end1 = addr + size1; uint64_t end2 = start + size2; - if (within(addr, start, end2) && - end1 > addr && end1 <= end2) { + /* + * 1. ensure we aren't overflowing + * 2. ensure that [addr, end1) is within [start, size2) + */ + if (end2 >= start && end1 >= addr && + addr >= start && end1 <= end2) { return; } From 3e64d7d7b8761107c39cc03da2d031d1d6f6912a Mon Sep 17 00:00:00 2001 From: Chris Friedt Date: Tue, 18 Oct 2022 08:25:50 -0400 Subject: [PATCH 0290/2884] hw: misc: edu: rename local vars in edu_check_range This serves to make the local variables a bit less ambiguous. The latter two arguments are named to match DMA_START, and DMA_SIZE. Signed-off-by: Chris Friedt Message-ID: <20221018122551.94567-2-cfriedt@meta.com> Signed-off-by: Thomas Huth --- hw/misc/edu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 14250e0ac3..e1cb443438 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -103,24 +103,24 @@ static void edu_lower_irq(EduState *edu, uint32_t val) } } -static void edu_check_range(uint64_t addr, uint64_t size1, - uint64_t start, uint64_t size2) +static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size, + uint64_t dma_start, uint64_t dma_size) { - uint64_t end1 = addr + size1; - uint64_t end2 = start + size2; + uint64_t xfer_end = xfer_start + xfer_size; + uint64_t dma_end = dma_start + dma_size; /* * 1. ensure we aren't overflowing - * 2. ensure that [addr, end1) is within [start, size2) + * 2. ensure that xfer is within dma address range */ - if (end2 >= start && end1 >= addr && - addr >= start && end1 <= end2) { + if (dma_end >= dma_start && xfer_end >= xfer_start && + xfer_start >= dma_start && xfer_end <= dma_end) { return; } hw_error("EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", - addr, end1 - 1, start, end2 - 1); + xfer_start, xfer_end - 1, dma_start, dma_end - 1); } static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) From 7b608e5d6c1d61430e81cd5c71b0277b99b03f3a Mon Sep 17 00:00:00 2001 From: Chris Friedt Date: Tue, 18 Oct 2022 08:25:51 -0400 Subject: [PATCH 0291/2884] hw: misc: edu: use qemu_log_mask instead of hw_error Log a guest error instead of a hardware error when the guest tries to DMA to / from an invalid address. Signed-off-by: Chris Friedt Message-ID: <20221018122551.94567-3-cfriedt@meta.com> [thuth: Add missing #include statement, fix error reported by checkpatch.pl] Signed-off-by: Thomas Huth --- hw/misc/edu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index e1cb443438..fa052c44db 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/units.h" #include "hw/pci/pci.h" #include "hw/hw.h" @@ -118,9 +119,10 @@ static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size, return; } - hw_error("EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 - " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", - xfer_start, xfer_end - 1, dma_start, dma_end - 1); + qemu_log_mask(LOG_GUEST_ERROR, + "EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 + " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", + xfer_start, xfer_end - 1, dma_start, dma_end - 1); } static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) @@ -128,7 +130,9 @@ static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) dma_addr_t res = addr & edu->dma_mask; if (addr != res) { - printf("EDU: clamping DMA %#.16"PRIx64" to %#.16"PRIx64"!\n", addr, res); + qemu_log_mask(LOG_GUEST_ERROR, + "EDU: clamping DMA 0x%016"PRIx64" to 0x%016"PRIx64"!", + addr, res); } return res; From 109f1a437f99d17059758d2e2ed8692d03744cbd Mon Sep 17 00:00:00 2001 From: Konstantin Kostiuk Date: Fri, 26 Apr 2024 15:13:42 +0300 Subject: [PATCH 0292/2884] stubs: Add missing qga stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compilation QGA without system and user fails ./configure --disable-system --disable-user --enable-guest-agent Link failure: /usr/bin/ld: libqemuutil.a.p/util_main-loop.c.o: in function `os_host_main_loop_wait': ../util/main-loop.c:303: undefined reference to `replay_mutex_unlock' /usr/bin/ld: ../util/main-loop.c:307: undefined reference to `replay_mutex_lock' /usr/bin/ld: libqemuutil.a.p/util_error-report.c.o: in function `error_printf': ../util/error-report.c:38: undefined reference to `error_vprintf' /usr/bin/ld: libqemuutil.a.p/util_error-report.c.o: in function `vreport': ../util/error-report.c:225: undefined reference to `error_vprintf' /usr/bin/ld: libqemuutil.a.p/util_qemu-timer.c.o: in function `timerlist_run_timers': ../util/qemu-timer.c:562: undefined reference to `replay_checkpoint' /usr/bin/ld: ../util/qemu-timer.c:530: undefined reference to `replay_checkpoint' /usr/bin/ld: ../util/qemu-timer.c:525: undefined reference to `replay_checkpoint' ninja: build stopped: subcommand failed. Fixes: 3a15604900 ("stubs: include stubs only if needed") Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Konstantin Kostiuk Message-ID: <20240426121347.18843-2-kkostiuk@redhat.com> Signed-off-by: Thomas Huth --- stubs/meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stubs/meson.build b/stubs/meson.build index 8ee1fd5753..3b9d42023c 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -21,12 +21,12 @@ if have_block stub_ss.add(files('migr-blocker.c')) stub_ss.add(files('physmem.c')) stub_ss.add(files('ram-block.c')) - stub_ss.add(files('replay-tools.c')) stub_ss.add(files('runstate-check.c')) stub_ss.add(files('uuid.c')) endif if have_block or have_ga + stub_ss.add(files('replay-tools.c')) # stubs for hooks in util/main-loop.c, util/async.c etc. stub_ss.add(files('cpus-get-virtual-clock.c')) stub_ss.add(files('icount.c')) @@ -45,6 +45,10 @@ if have_block or have_ga stub_ss.add(files('qmp-quit.c')) endif +if have_ga + stub_ss.add(files('error-printf.c')) +endif + if have_block or have_user stub_ss.add(files('qtest.c')) stub_ss.add(files('vm-stop.c')) From 26813f7f4acce1598ae7a52c2e79a86efe6177ee Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 26 Apr 2024 18:23:48 +0200 Subject: [PATCH 0293/2884] qga: Re-enable the qga-ssh-test when running without fuzzing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the comment in qga/meson.build, the test got disabled since there were problems with the fuzzing job. But instead of disabling this test completely, we should still be fine running it when fuzzing is disabled. Message-ID: <20240426162348.684143-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Konstantin Kostiuk Signed-off-by: Thomas Huth --- qga/meson.build | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qga/meson.build b/qga/meson.build index 1c3d2a3d1b..46c1d83d7f 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -181,12 +181,11 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -# disable qga-ssh-test for now. glib's G_TEST_OPTION_ISOLATE_DIRS triggers +# disable qga-ssh-test with fuzzing: glib's G_TEST_OPTION_ISOLATE_DIRS triggers # the leak detector in build-oss-fuzz Gitlab CI test. we should re-enable # this when an alternative is implemented or when the underlying glib # issue is identified/fix -#if host_os != 'windows' -if false +if host_os != 'windows' and not get_option('fuzzing') srcs = [files('commands-posix-ssh.c')] i = 0 foreach output: qga_qapi_outputs From e40e129922a1114c05c846b094aa13496394613b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 29 Apr 2024 09:59:08 +0200 Subject: [PATCH 0294/2884] hw/char/stm32l4x5_usart: Fix memory corruption by adding correct class_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "make check-qtest-aarch64" recently started failing on FreeBSD builds, and valgrind on Linux also detected that there is something fishy with the new stm32l4x5-usart: The code forgot to set the correct class_size here, so the various class_init functions in this file wrote beyond the allocated buffer when setting the subc->type field. Fixes: 4fb37aea7e ("hw/char: Implement STM32L4x5 USART skeleton") Message-ID: <20240429075908.36302-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/char/stm32l4x5_usart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 2627aab832..02f666308c 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -617,6 +617,7 @@ static const TypeInfo stm32l4x5_usart_types[] = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Stm32l4x5UsartBaseState), .instance_init = stm32l4x5_usart_base_init, + .class_size = sizeof(Stm32l4x5UsartBaseClass), .class_init = stm32l4x5_usart_base_class_init, .abstract = true, }, { From 83561896ac9d1ffbc872293ff449b7df79c6c667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 26 Apr 2024 16:39:36 +0100 Subject: [PATCH 0295/2884] build-environment: make some packages optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upgrading the s390x runner exposed some packages are not available for it. Add an additional optional stage we only enable for arm64/x86_64 for now. Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Message-ID: <20240426153938.1707723-2-alex.bennee@linaro.org> Signed-off-by: Thomas Huth --- scripts/ci/setup/build-environment.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml index f344d1a850..de0d866a1e 100644 --- a/scripts/ci/setup/build-environment.yml +++ b/scripts/ci/setup/build-environment.yml @@ -95,7 +95,6 @@ - libpam0g-dev - libpcre2-dev - libpixman-1-dev - - libpmem-dev - libpng-dev - libpulse-dev - librbd-dev @@ -107,7 +106,6 @@ - libslirp-dev - libsnappy-dev - libspice-protocol-dev - - libspice-server-dev - libssh-dev - libsystemd-dev - libtasn1-6-dev @@ -119,7 +117,6 @@ - libvdeplug-dev - libvirglrenderer-dev - libvte-2.91-dev - - libxen-dev - libxml2-dev - libzstd-dev - llvm @@ -156,6 +153,19 @@ - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['distribution_version'] == '22.04' + # not all packages are available for all architectures + - name: Install additional packages to build QEMU on Ubuntu 22.04 + package: + name: + - libpmem-dev + - libspice-server-dev + - libxen-dev + state: present + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' + - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 22.04 package: name: From 108d99742af1fa6e977dcfac9d4151b7915e33a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 26 Apr 2024 16:39:37 +0100 Subject: [PATCH 0296/2884] gitlab: migrate the s390x custom machine to 22.04 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 20.04 is dead (from QEMU's point of view), long live 22.04! Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Message-ID: <20240426153938.1707723-3-alex.bennee@linaro.org> Signed-off-by: Thomas Huth --- .gitlab-ci.d/custom-runners.yml | 2 +- ...20.04-s390x.yml => ubuntu-22.04-s390x.yml} | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) rename .gitlab-ci.d/custom-runners/{ubuntu-20.04-s390x.yml => ubuntu-22.04-s390x.yml} (88%) diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index a0e79acd39..29e52df283 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -29,7 +29,7 @@ junit: build/meson-logs/testlog.junit.xml include: - - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml' - local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml' diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml similarity index 88% rename from .gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml rename to .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index cdae6c5212..85e2809573 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -1,13 +1,13 @@ -# All ubuntu-20.04 jobs should run successfully in an environment +# All ubuntu-22.04 jobs should run successfully in an environment # setup by the scripts/ci/setup/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 20.04/20.04" +# "Install basic packages to build QEMU on Ubuntu 22.04" -ubuntu-20.04-s390x-all-linux-static: +ubuntu-22.04-s390x-all-linux-static: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -23,12 +23,12 @@ ubuntu-20.04-s390x-all-linux-static: - make --output-sync check-tcg - make --output-sync -j`nproc` check -ubuntu-20.04-s390x-all: +ubuntu-22.04-s390x-all: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - s390x timeout: 75m rules: @@ -42,12 +42,12 @@ ubuntu-20.04-s390x-all: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check -ubuntu-20.04-s390x-alldbg: +ubuntu-22.04-s390x-alldbg: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -65,12 +65,12 @@ ubuntu-20.04-s390x-alldbg: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check -ubuntu-20.04-s390x-clang: +ubuntu-22.04-s390x-clang: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -87,11 +87,11 @@ ubuntu-20.04-s390x-clang: - make --output-sync -j`nproc` - make --output-sync -j`nproc` check -ubuntu-20.04-s390x-tci: +ubuntu-22.04-s390x-tci: needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -107,12 +107,12 @@ ubuntu-20.04-s390x-tci: || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` -ubuntu-20.04-s390x-notcg: +ubuntu-22.04-s390x-notcg: extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - s390x rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' From 39ad72c2600af53d7697413afac382562910d3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 26 Apr 2024 16:39:38 +0100 Subject: [PATCH 0297/2884] gitlab: remove stale s390x-all-linux-static conf hacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The libssh bug references 18.04 which we are no longer running. We don't need to disable glusterfs because a linux-user build shouldn't be trying to link to it anyway. Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Message-ID: <20240426153938.1707723-4-alex.bennee@linaro.org> Signed-off-by: Thomas Huth --- .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index 85e2809573..105981879f 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -13,11 +13,9 @@ ubuntu-22.04-s390x-all-linux-static: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" script: - # --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763 - # --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages - mkdir build - cd build - - ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh + - ../configure --enable-debug --static --disable-system || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync check-tcg From 8682ff696050584cfcb5bf43d567680ad339cf41 Mon Sep 17 00:00:00 2001 From: Lev Kujawski Date: Mon, 10 Oct 2022 08:52:28 +0000 Subject: [PATCH 0298/2884] hw/ide/core.c (cmd_read_native_max): Avoid limited device parameters Always use the native CHS device parameters for the ATA commands READ NATIVE MAX ADDRESS and READ NATIVE MAX ADDRESS EXT, not those limited by the ATA command INITIALIZE_DEVICE_PARAMETERS (introduced in patch 176e4961, hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command, 2022-07-07.) As stated by the ATA/ATAPI specification, "[t]he native maximum is the highest address accepted by the device in the factory default condition." Therefore this patch substitutes the native values in drive_heads and drive_sectors before calling ide_set_sector(). One consequence of the prior behavior was that setting zero sectors per track could lead to an FPE within ide_set_sector(). Thanks to Alexander Bulekov for reporting this issue. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1243 Signed-off-by: Lev Kujawski Message-ID: <20221010085229.2431276-1-lkujaw@mailbox.org> Signed-off-by: Thomas Huth --- hw/ide/core.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index e8cb2dac92..08d9218455 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1623,11 +1623,24 @@ static bool cmd_read_native_max(IDEState *s, uint8_t cmd) /* Refuse if no sectors are addressable (e.g. medium not inserted) */ if (s->nb_sectors == 0) { ide_abort_command(s); - return true; - } + } else { + /* + * Save the active drive parameters, which may have been + * limited from their native counterparts by, e.g., INITIALIZE + * DEVICE PARAMETERS or SET MAX ADDRESS. + */ + const int aheads = s->heads; + const int asectors = s->sectors; - ide_cmd_lba48_transform(s, lba48); - ide_set_sector(s, s->nb_sectors - 1); + s->heads = s->drive_heads; + s->sectors = s->drive_sectors; + + ide_cmd_lba48_transform(s, lba48); + ide_set_sector(s, s->nb_sectors - 1); + + s->heads = aheads; + s->sectors = asectors; + } return true; } From 622f8eb1586def9a374eca27878156dec69b8730 Mon Sep 17 00:00:00 2001 From: Lev Kujawski Date: Mon, 10 Oct 2022 08:52:29 +0000 Subject: [PATCH 0299/2884] tests/qtest/ide-test: Verify READ NATIVE MAX ADDRESS is not limited Verify that the ATA command READ NATIVE MAX ADDRESS returns the last valid CHS tuple for the native device rather than any limit established by INITIALIZE DEVICE PARAMETERS. Signed-off-by: Lev Kujawski Message-ID: <20221010085229.2431276-2-lkujaw@mailbox.org> Signed-off-by: Thomas Huth --- tests/qtest/ide-test.c | 47 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index d6b4f6e36a..90ba6b298b 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -34,7 +34,8 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -#define TEST_IMAGE_SIZE 64 * 1024 * 1024 +/* Specified by ATA (physical) CHS geometry for ~64 MiB device. */ +#define TEST_IMAGE_SIZE ((130 * 16 * 63) * 512) #define IDE_PCI_DEV 1 #define IDE_PCI_FUNC 1 @@ -88,11 +89,13 @@ enum { enum { CMD_DSM = 0x06, CMD_DIAGNOSE = 0x90, + CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_READ_DMA = 0xc8, CMD_WRITE_DMA = 0xca, CMD_FLUSH_CACHE = 0xe7, CMD_IDENTIFY = 0xec, CMD_PACKET = 0xa0, + CMD_READ_NATIVE = 0xf8, /* READ NATIVE MAX ADDRESS */ CMDF_ABORT = 0x100, CMDF_NO_BM = 0x200, @@ -560,6 +563,46 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes) } } +static void test_specify(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t cyls; + uint8_t heads, spt; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Initialize drive with zero sectors per track and one head. */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 0); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + + /* READ NATIVE MAX ADDRESS (CHS mode). */ + qpci_io_writeb(dev, ide_bar, reg_device, 0xa0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ_NATIVE); + + heads = qpci_io_readb(dev, ide_bar, reg_device) & 0xf; + ++heads; + g_assert_cmpint(heads, ==, 16); + + cyls = qpci_io_readb(dev, ide_bar, reg_lba_high) << 8; + cyls |= qpci_io_readb(dev, ide_bar, reg_lba_middle); + ++cyls; + g_assert_cmpint(cyls, ==, 130); + + spt = qpci_io_readb(dev, ide_bar, reg_lba_low); + g_assert_cmpint(spt, ==, 63); + + ide_test_quit(qts); + free_pci_device(dev); +} + static void test_identify(void) { QTestState *qts; @@ -1077,6 +1120,8 @@ int main(int argc, char **argv) /* Run the tests */ g_test_init(&argc, &argv, NULL); + qtest_add_func("/ide/read_native", test_specify); + qtest_add_func("/ide/identify", test_identify); qtest_add_func("/ide/diagnostic", test_diagnostic); From a88a04906b966ffdcda23a5a456abe10aa8c826e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 29 Apr 2024 12:01:13 +0200 Subject: [PATCH 0300/2884] .gitlab-ci.d/cirrus.yml: Shorten the runtime of the macOS and FreeBSD jobs Cirrus-CI introduced limitations to the free CI minutes. To avoid that we are consuming them too fast, let's drop the usual targets that are not that important since they are either a subset of another target (like i386 or ppc being a subset of x86_64 or ppc64 respectively), or since there is still a similar target with the opposite endianness (like xtensa/xtensael, microblaze/microblazeel etc.). Message-ID: <20240429100113.53357-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/cirrus.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 4671f069c3..49f86fadaf 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -57,6 +57,7 @@ x64-freebsd-13-build: CIRRUS_VM_RAM: 8G UPDATE_COMMAND: pkg update; pkg upgrade -y INSTALL_COMMAND: pkg install -y + CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu TEST_TARGETS: check aarch64-macos-13-base-build: @@ -72,6 +73,7 @@ aarch64-macos-13-base-build: INSTALL_COMMAND: brew install PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig + CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblazeel-softmmu,mips64-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4-softmmu,xtensaeb-softmmu TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64 aarch64-macos-14-base-build: From cc6cb422e09592158586279fddeef107df05ecbb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 26 Apr 2024 13:37:42 +0200 Subject: [PATCH 0301/2884] .gitlab-ci.d/cirrus: Remove the netbsd and openbsd jobs During the past months, the netbsd and openbsd jobs in the Cirrus-CI were broken most of the time - the setup to run a BSD in KVM on Cirrus-CI from gitlab via the cirrus-run script was very fragile, and since the jobs were not run by default, it used to bitrot very fast. Now Cirrus-CI also introduce a limit on the amount of free CI minutes that you get there, so it is not appealing at all anymore to run these BSDs in this setup - it's better to run the checks locally via "make vm-build-openbsd" and "make vm-build-netbsd" instead. Thus let's remove these CI jobs now. Message-ID: <20240426113742.654748-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/cirrus.yml | 37 ------------------------------- .gitlab-ci.d/cirrus/kvm-build.yml | 31 -------------------------- 2 files changed, 68 deletions(-) delete mode 100644 .gitlab-ci.d/cirrus/kvm-build.yml diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 49f86fadaf..75df1273bc 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -91,40 +91,3 @@ aarch64-macos-14-base-build: PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64 QEMU_JOB_OPTIONAL: 1 - - -# The following jobs run VM-based tests via KVM on a Linux-based Cirrus-CI job -.cirrus_kvm_job: - extends: .base_job_template - stage: build - image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master - needs: [] - timeout: 80m - script: - - sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g" - -e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g" - -e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g" - -e "s|[@]NAME@|$NAME|g" - -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g" - -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g" - <.gitlab-ci.d/cirrus/kvm-build.yml >.gitlab-ci.d/cirrus/$NAME.yml - - cat .gitlab-ci.d/cirrus/$NAME.yml - - cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml - variables: - QEMU_JOB_CIRRUS: 1 - QEMU_JOB_OPTIONAL: 1 - - -x86-netbsd: - extends: .cirrus_kvm_job - variables: - NAME: netbsd - CONFIGURE_ARGS: --target-list=x86_64-softmmu,ppc64-softmmu,aarch64-softmmu - TEST_TARGETS: check - -x86-openbsd: - extends: .cirrus_kvm_job - variables: - NAME: openbsd - CONFIGURE_ARGS: --target-list=i386-softmmu,riscv64-softmmu,mips64-softmmu - TEST_TARGETS: check diff --git a/.gitlab-ci.d/cirrus/kvm-build.yml b/.gitlab-ci.d/cirrus/kvm-build.yml deleted file mode 100644 index a93881aa8b..0000000000 --- a/.gitlab-ci.d/cirrus/kvm-build.yml +++ /dev/null @@ -1,31 +0,0 @@ -container: - image: fedora:35 - cpu: 4 - memory: 8Gb - kvm: true - -env: - CIRRUS_CLONE_DEPTH: 1 - CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@" - CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@" - CI_COMMIT_SHA: "@CI_COMMIT_SHA@" - -@NAME@_task: - @NAME@_vm_cache: - folder: $HOME/.cache/qemu-vm - install_script: - - dnf update -y - - dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget meson - clone_script: - - git clone --depth 100 "$CI_REPOSITORY_URL" . - - git fetch origin "$CI_COMMIT_REF_NAME" - - git reset --hard "$CI_COMMIT_SHA" - build_script: - - if [ -f $HOME/.cache/qemu-vm/images/@NAME@.img ]; then - make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) - EXTRA_CONFIGURE_OPTS="@CONFIGURE_ARGS@" - BUILD_TARGET="@TEST_TARGETS@" ; - else - make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) BUILD_TARGET=help - EXTRA_CONFIGURE_OPTS="--disable-system --disable-user --disable-tools" ; - fi From 845dd0385eb738690ee19e60111952a363bbf257 Mon Sep 17 00:00:00 2001 From: Raphael Poggi Date: Fri, 19 Apr 2024 17:29:51 +0100 Subject: [PATCH 0302/2884] hw/core/clock: allow clock_propagate on child clocks clock_propagate() has an assert that clk->source is NULL, i.e. that you are calling it on a clock which has no source clock. This made sense in the original design where the only way for a clock's frequency to change if it had a source clock was when that source clock changed. However, we subsequently added multiplier/divider support, but didn't look at what that meant for propagation. If a clock-management device changes the multiplier or divider value on a clock, it needs to propagate that change down to child clocks, even if the clock has a source clock set. So the assertion is now incorrect. Remove the assertion. Signed-off-by: Raphael Poggi Message-id: 20240419162951.23558-1-raphael.poggi@lynxleap.co.uk Reviewed-by: Peter Maydell [PMM: Rewrote the commit message] Signed-off-by: Peter Maydell --- hw/core/clock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/core/clock.c b/hw/core/clock.c index a19c7db7df..e212865307 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -108,7 +108,6 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks) void clock_propagate(Clock *clk) { - assert(clk->source == NULL); trace_clock_propagate(CLOCK_PATH(clk)); clock_propagate_period(clk, true); } From a8aa8af99f87f20beca58f647a347ba576d59444 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 22 Apr 2024 17:27:15 +0800 Subject: [PATCH 0303/2884] hvf: arm: Remove PL1_WRITE_MASK As it had never been used since the first commit a1477da3ddeb ("hvf: Add Apple Silicon support"). Signed-off-by: Zenghui Yu Message-id: 20240422092715.71973-1-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index db628c1cba..8e942f89b3 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -150,7 +150,6 @@ void hvf_arm_init_debug(void) #define HVF_SYSREG(crn, crm, op0, op1, op2) \ ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) -#define PL1_WRITE_MASK 0x4 #define SYSREG_OP0_SHIFT 20 #define SYSREG_OP0_MASK 0x3 From 7b19a3554d2df22d29c75319a1dac17615d1b20e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 22 Apr 2024 10:07:22 -0700 Subject: [PATCH 0304/2884] target/arm: Restrict translation disabled alignment check to VMSA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For cpus using PMSA, when the MPU is disabled, the default memory type is Normal, Non-cachable. This means that it should not have alignment restrictions enforced. Cc: qemu-stable@nongnu.org Fixes: 59754f85ed3 ("target/arm: Do memory type alignment check when translation disabled") Reported-by: Clément Chigot Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Clément Chigot Message-id: 20240422170722.117409-1-richard.henderson@linaro.org [PMM: trivial comment, commit message tweaks] Signed-off-by: Peter Maydell --- target/arm/tcg/hflags.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 5da1b0fc1d..f03977b4b0 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -38,8 +38,16 @@ static bool aprofile_require_alignment(CPUARMState *env, int el, uint64_t sctlr) } /* - * If translation is disabled, then the default memory type is - * Device(-nGnRnE) instead of Normal, which requires that alignment + * With PMSA, when the MPU is disabled, all memory types in the + * default map are Normal, so don't need aligment enforcing. + */ + if (arm_feature(env, ARM_FEATURE_PMSA)) { + return false; + } + + /* + * With VMSA, if translation is disabled, then the default memory type + * is Device(-nGnRnE) instead of Normal, which requires that alignment * be enforced. Since this affects all ram, it is most efficient * to handle this during translation. */ From bc980d66308d3eabf8718477083c5989ea71ad42 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Apr 2024 16:20:00 +0100 Subject: [PATCH 0305/2884] docs/system/arm/emulation.rst: Add missing implemented features As of version DDI0487K.a of the Arm ARM, some architectural features which previously didn't have official names have been named. Add these to the list of features which QEMU's TCG emulation supports. Mostly these are features which we thought of as part of baseline 8.0 support. For SVE and SVE2, the names have been brought into line with the FEAT_* naming convention of other extensions, and some sub-components split into separate FEAT_ items. In a few cases (eg FEAT_CCIDX, FEAT_DPB2) the omission from our list was just an oversight. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20240418152004.2106516-2-peter.maydell@linaro.org --- docs/system/arm/emulation.rst | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index a9ae7ede9f..5fdc64a944 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -8,13 +8,26 @@ Armv8 versions of the A-profile architecture. It also has support for the following architecture extensions: - FEAT_AA32BF16 (AArch32 BFloat16 instructions) +- FEAT_AA32EL0 (Support for AArch32 at EL0) +- FEAT_AA32EL1 (Support for AArch32 at EL1) +- FEAT_AA32EL2 (Support for AArch32 at EL2) +- FEAT_AA32EL3 (Support for AArch32 at EL3) - FEAT_AA32HPD (AArch32 hierarchical permission disables) - FEAT_AA32I8MM (AArch32 Int8 matrix multiplication instructions) +- FEAT_AA64EL0 (Support for AArch64 at EL0) +- FEAT_AA64EL1 (Support for AArch64 at EL1) +- FEAT_AA64EL2 (Support for AArch64 at EL2) +- FEAT_AA64EL3 (Support for AArch64 at EL3) +- FEAT_AdvSIMD (Advanced SIMD Extension) - FEAT_AES (AESD and AESE instructions) +- FEAT_Armv9_Crypto (Armv9 Cryptographic Extension) +- FEAT_ASID16 (16 bit ASID) - FEAT_BBM at level 2 (Translation table break-before-make levels) - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) +- FEAT_CCIDX (Extended cache index) - FEAT_CRC32 (CRC32 instructions) +- FEAT_Crypto (Cryptographic Extension) - FEAT_CSV2 (Cache speculation variant 2) - FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1) - FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2) @@ -23,18 +36,27 @@ the following architecture extensions: - FEAT_DGH (Data gathering hint) - FEAT_DIT (Data Independent Timing instructions) - FEAT_DPB (DC CVAP instruction) +- FEAT_DPB2 (DC CVADP instruction) +- FEAT_Debugv8p1 (Debug with VHE) - FEAT_Debugv8p2 (Debug changes for v8.2) - FEAT_Debugv8p4 (Debug changes for v8.4) - FEAT_DotProd (Advanced SIMD dot product instructions) - FEAT_DoubleFault (Double Fault Extension) - FEAT_E0PD (Preventing EL0 access to halves of address maps) - FEAT_ECV (Enhanced Counter Virtualization) +- FEAT_EL0 (Support for execution at EL0) +- FEAT_EL1 (Support for execution at EL1) +- FEAT_EL2 (Support for execution at EL2) +- FEAT_EL3 (Support for execution at EL3) - FEAT_EPAC (Enhanced pointer authentication) - FEAT_ETS (Enhanced Translation Synchronization) - FEAT_EVT (Enhanced Virtualization Traps) +- FEAT_F32MM (Single-precision Matrix Multiplication) +- FEAT_F64MM (Double-precision Matrix Multiplication) - FEAT_FCMA (Floating-point complex number instructions) - FEAT_FGT (Fine-Grained Traps) - FEAT_FHM (Floating-point half-precision multiplication instructions) +- FEAT_FP (Floating Point extensions) - FEAT_FP16 (Half-precision floating-point data processing) - FEAT_FPAC (Faulting on AUT* instructions) - FEAT_FPACCOMBINE (Faulting on combined pointer authentication instructions) @@ -60,10 +82,13 @@ the following architecture extensions: - FEAT_LSE (Large System Extensions) - FEAT_LSE2 (Large System Extensions v2) - FEAT_LVA (Large Virtual Address space) +- FEAT_MixedEnd (Mixed-endian support) +- FEAT_MixdEndEL0 (Mixed-endian support at EL0) - FEAT_MOPS (Standardization of memory operations) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) +- FEAT_MTE_ASYM_FAULT (Memory tagging asymmetric faults) - FEAT_NMI (Non-maskable Interrupt) - FEAT_NV (Nested Virtualization) - FEAT_NV2 (Enhanced nested virtualization support) @@ -76,6 +101,7 @@ the following architecture extensions: - FEAT_PAuth (Pointer authentication) - FEAT_PAuth2 (Enhancements to pointer authentication) - FEAT_PMULL (PMULL, PMULL2 instructions) +- FEAT_PMUv3 (PMU extension version 3) - FEAT_PMUv3p1 (PMU Extensions v3.1) - FEAT_PMUv3p4 (PMU Extensions v3.4) - FEAT_PMUv3p5 (PMU Extensions v3.5) @@ -97,8 +123,18 @@ the following architecture extensions: - FEAT_SME_FA64 (Full A64 instruction set in Streaming SVE mode) - FEAT_SME_F64F64 (Double-precision floating-point outer product instructions) - FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) +- FEAT_SVE (Scalable Vector Extension) +- FEAT_SVE_AES (Scalable Vector AES instructions) +- FEAT_SVE_BitPerm (Scalable Vector Bit Permutes instructions) +- FEAT_SVE_PMULL128 (Scalable Vector PMULL instructions) +- FEAT_SVE_SHA3 (Scalable Vector SHA3 instructions) +- FEAT_SVE_SM4 (Scalable Vector SM4 instructions) +- FEAT_SVE2 (Scalable Vector Extension version 2) - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) +- FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1) +- FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1) +- FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1) - FEAT_TIDCP1 (EL0 use of IMPLEMENTATION DEFINED functionality) - FEAT_TLBIOS (TLB invalidate instructions in Outer Shareable domain) - FEAT_TLBIRANGE (TLB invalidate range instructions) @@ -109,8 +145,6 @@ the following architecture extensions: - FEAT_VHE (Virtualization Host Extensions) - FEAT_VMID16 (16-bit VMID) - FEAT_XNX (Translation table stage 2 Unprivileged Execute-never) -- SVE (The Scalable Vector Extension) -- SVE2 (The Scalable Vector Extension v2) For information on the specifics of these extensions, please refer to the `Armv8-A Arm Architecture Reference Manual From e197395180511b093027c2bac3a34e6a84ddecdc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Apr 2024 16:20:01 +0100 Subject: [PATCH 0306/2884] target/arm: Enable FEAT_CSV2_3 for -cpu max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FEAT_CSV2_3 adds a mechanism to identify if hardware cannot disclose information about whether branch targets and branch history trained in one hardware described context can control speculative execution in a different hardware context. There is no branch prediction in TCG, so we don't need to do anything to be compliant with this. Upadte the '-cpu max' ID registers to advertise the feature. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240418152004.2106516-3-peter.maydell@linaro.org --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 5fdc64a944..d70b66f753 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -32,6 +32,7 @@ the following architecture extensions: - FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1) - FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2) - FEAT_CSV2_2 (Cache speculation variant 2, version 2) +- FEAT_CSV2_3 (Cache speculation variant 2, version 3) - FEAT_CSV3 (Cache speculation variant 3) - FEAT_DGH (Data gathering hint) - FEAT_DIT (Data Independent Timing instructions) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 62c4663512..8ad05c53e8 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1159,7 +1159,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* FEAT_SEL2 */ t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* FEAT_DIT */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 2); /* FEAT_CSV2_2 */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 3); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ cpu->isar.id_aa64pfr0 = t; @@ -1174,7 +1174,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* FEAT_MTE3 */ t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ t = FIELD_DP64(t, ID_AA64PFR1, SME, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_2 */ + t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ cpu->isar.id_aa64pfr1 = t; From 74360f3544be380f3a6f7a0f1cd8082ddd4a75ad Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Apr 2024 16:20:02 +0100 Subject: [PATCH 0307/2884] target/arm: Enable FEAT_ETS2 for -cpu max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FEAT_ETS2 is a tighter set of guarantees about memory ordering involving translation table walks than the old FEAT_ETS; FEAT_ETS has been retired from the Arm ARM and the old ID_AA64MMFR1.ETS == 1 now gives no greater guarantees than ETS == 0. FEAT_ETS2 requires: * the virtual address of a load or store that appears in program order after a DSB cannot be translated until after the DSB completes (section B2.10.9) * TLB maintenance operations that only affect translations without execute permission are guaranteed complete after a DSB (R_BLDZX) * if a memory access RW2 is ordered-before memory access RW2, then RW1 is also ordered-before any translation table walk generated by RW2 that generates a Translation, Address size or Access flag fault (R_NNFPF, I_CLGHP) As with FEAT_ETS, QEMU is already compliant, because we do not reorder translation table walk memory accesses relative to other memory accesses, and we always guarantee to have finished TLB maintenance as soon as the TLB op is done. Update the documentation to list FEAT_ETS2 instead of the no-longer-existent FEAT_ETS, and update the 'max' CPU ID registers. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240418152004.2106516-4-peter.maydell@linaro.org --- docs/system/arm/emulation.rst | 2 +- target/arm/tcg/cpu32.c | 2 +- target/arm/tcg/cpu64.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index d70b66f753..307539cff9 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -50,7 +50,7 @@ the following architecture extensions: - FEAT_EL2 (Support for execution at EL2) - FEAT_EL3 (Support for execution at EL3) - FEAT_EPAC (Enhanced pointer authentication) -- FEAT_ETS (Enhanced Translation Synchronization) +- FEAT_ETS2 (Enhanced Translation Synchronization) - FEAT_EVT (Enhanced Virtualization Traps) - FEAT_F32MM (Single-precision Matrix Multiplication) - FEAT_F64MM (Double-precision Matrix Multiplication) diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index de8f2be941..b5a60682fa 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -67,7 +67,7 @@ void aa32_max_features(ARMCPU *cpu) cpu->isar.id_mmfr4 = t; t = cpu->isar.id_mmfr5; - t = FIELD_DP32(t, ID_MMFR5, ETS, 1); /* FEAT_ETS */ + t = FIELD_DP32(t, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ cpu->isar.id_mmfr5 = t; t = cpu->isar.id_pfr0; diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 8ad05c53e8..ebb585afd8 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1196,7 +1196,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* FEAT_LOR */ t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* FEAT_PAN3 */ t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 1); /* FEAT_ETS */ + t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* FEAT_ETS2 */ t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ cpu->isar.id_aa64mmfr1 = t; From f7ddd7b6a1f90cae677303e96b91e866a1570f6a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Apr 2024 16:20:03 +0100 Subject: [PATCH 0308/2884] target/arm: Implement ID_AA64MMFR3_EL1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer versions of the Arm ARM (e.g. rev K.a) now define fields for ID_AA64MMFR3_EL1. Implement this register, so that we can set the fields if we need to. There's no behaviour change here since we don't currently set the register value to non-zero. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240418152004.2106516-5-peter.maydell@linaro.org --- target/arm/cpu.h | 17 +++++++++++++++++ target/arm/helper.c | 6 ++++-- target/arm/hvf/hvf.c | 2 ++ target/arm/kvm.c | 2 ++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 17efc5d565..1f90590f93 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1011,6 +1011,7 @@ struct ArchCPU { uint64_t id_aa64mmfr0; uint64_t id_aa64mmfr1; uint64_t id_aa64mmfr2; + uint64_t id_aa64mmfr3; uint64_t id_aa64dfr0; uint64_t id_aa64dfr1; uint64_t id_aa64zfr0; @@ -2206,6 +2207,22 @@ FIELD(ID_AA64MMFR2, BBM, 52, 4) FIELD(ID_AA64MMFR2, EVT, 56, 4) FIELD(ID_AA64MMFR2, E0PD, 60, 4) +FIELD(ID_AA64MMFR3, TCRX, 0, 4) +FIELD(ID_AA64MMFR3, SCTLRX, 4, 4) +FIELD(ID_AA64MMFR3, S1PIE, 8, 4) +FIELD(ID_AA64MMFR3, S2PIE, 12, 4) +FIELD(ID_AA64MMFR3, S1POE, 16, 4) +FIELD(ID_AA64MMFR3, S2POE, 20, 4) +FIELD(ID_AA64MMFR3, AIE, 24, 4) +FIELD(ID_AA64MMFR3, MEC, 28, 4) +FIELD(ID_AA64MMFR3, D128, 32, 4) +FIELD(ID_AA64MMFR3, D128_2, 36, 4) +FIELD(ID_AA64MMFR3, SNERR, 40, 4) +FIELD(ID_AA64MMFR3, ANERR, 44, 4) +FIELD(ID_AA64MMFR3, SDERR, 52, 4) +FIELD(ID_AA64MMFR3, ADERR, 56, 4) +FIELD(ID_AA64MMFR3, SPEC_FPACC, 60, 4) + FIELD(ID_AA64DFR0, DEBUGVER, 0, 4) FIELD(ID_AA64DFR0, TRACEVER, 4, 4) FIELD(ID_AA64DFR0, PMUVER, 8, 4) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6b224826fb..bb0e1baf62 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9004,11 +9004,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = cpu->isar.id_aa64mmfr2 }, - { .name = "ID_AA64MMFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + { .name = "ID_AA64MMFR3_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, + .resetvalue = cpu->isar.id_aa64mmfr3 }, { .name = "ID_AA64MMFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, @@ -9165,6 +9165,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .exported_bits = R_ID_AA64MMFR1_AFP_MASK }, { .name = "ID_AA64MMFR2_EL1", .exported_bits = R_ID_AA64MMFR2_AT_MASK }, + { .name = "ID_AA64MMFR3_EL1", + .exported_bits = 0 }, { .name = "ID_AA64MMFR*_EL1_RESERVED", .is_glob = true }, { .name = "ID_AA64DFR0_EL1", diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 8e942f89b3..08d0757438 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -497,6 +497,7 @@ static struct hvf_sreg_match hvf_sreg_match[] = { #endif { HV_SYS_REG_ID_AA64MMFR1_EL1, HVF_SYSREG(0, 7, 3, 0, 1) }, { HV_SYS_REG_ID_AA64MMFR2_EL1, HVF_SYSREG(0, 7, 3, 0, 2) }, + /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ { HV_SYS_REG_MDSCR_EL1, HVF_SYSREG(0, 2, 2, 0, 2) }, { HV_SYS_REG_SCTLR_EL1, HVF_SYSREG(1, 0, 3, 0, 0) }, @@ -855,6 +856,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.id_aa64mmfr0 }, { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.id_aa64mmfr1 }, { HV_SYS_REG_ID_AA64MMFR2_EL1, &host_isar.id_aa64mmfr2 }, + /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ }; hv_vcpu_t fd; hv_return_t r = HV_SUCCESS; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 21ebbf3b8f..7cf5cf31de 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -331,6 +331,8 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 7, 1)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr2, ARM64_SYS_REG(3, 0, 0, 7, 2)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr3, + ARM64_SYS_REG(3, 0, 0, 7, 3)); /* * Note that if AArch32 support is not present in the host, From 663163f00785805e61f524ef744914029b2b6a87 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 18 Apr 2024 16:20:04 +0100 Subject: [PATCH 0309/2884] target/arm: Enable FEAT_Spec_FPACC for -cpu max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FEAT_Spec_FPACC is a feature describing speculative behaviour in the event of a PAC authontication failure when FEAT_FPACCOMBINE is implemented. FEAT_Spec_FPACC means that the speculative use of pointers processed by a PAC Authentication is not materially different in terms of the impact on cached microarchitectural state (caches, TLBs, etc) between passing and failing of the PAC Authentication. QEMU doesn't do speculative execution, so we can advertise this feature. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240418152004.2106516-6-peter.maydell@linaro.org --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 307539cff9..7fcea54d8d 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -61,6 +61,7 @@ the following architecture extensions: - FEAT_FP16 (Half-precision floating-point data processing) - FEAT_FPAC (Faulting on AUT* instructions) - FEAT_FPACCOMBINE (Faulting on combined pointer authentication instructions) +- FEAT_FPACC_SPEC (Speculative behavior of combined pointer authentication instructions) - FEAT_FRINTTS (Floating-point to integer instructions) - FEAT_FlagM (Flag manipulation instructions v2) - FEAT_FlagM2 (Enhancements to flag manipulation instructions) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index ebb585afd8..443cffe3a8 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1217,6 +1217,10 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ cpu->isar.id_aa64mmfr2 = t; + t = cpu->isar.id_aa64mmfr3; + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + cpu->isar.id_aa64mmfr3 = t; + t = cpu->isar.id_aa64zfr0; t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ From dcc5c018c7e6acddf81951bcbdf1019b9ab45f56 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 15 Apr 2024 16:18:45 +0100 Subject: [PATCH 0310/2884] tests/avocado: update sunxi kernel from armbian to 6.6.16 The Linux kernel 5.10.16 binary for sunxi has been removed from apt.armbian.com. This means that the avocado tests for these machines will be skipped (status CANCEL) if the old binary isn't present in the avocado cache. Update to 6.6.16, in the same way we did in commit e384db41d8661 when we moved to 5.10.16 in 2021. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2284 Signed-off-by: Peter Maydell Reviewed-by: Strahinja Jankovic Reviewed-by: Niek Linnenbank Tested-by: Niek Linnenbank Message-id: 20240415151845.1564201-1-peter.maydell@linaro.org --- tests/avocado/boot_linux_console.py | 70 ++++++++++++++--------------- tests/avocado/replay_kernel.py | 8 ++-- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 180ac17326..c35fc5e9ba 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -646,12 +646,12 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=accel:tcg """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -690,12 +690,12 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=accel:tcg """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -872,13 +872,13 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=machine:bpim2u :avocado: tags=accel:tcg """ - deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' - 'linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = ('/usr/lib/linux-image-current-sunxi/' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.extract_from_deb(deb_path, dtb_path) @@ -899,13 +899,13 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=accel:tcg :avocado: tags=machine:bpim2u """ - deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' - 'linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = ('/usr/lib/linux-image-current-sunxi/' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' @@ -946,13 +946,13 @@ class BootLinuxConsole(LinuxKernelTest): """ self.require_netdev('user') - deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' - 'linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = ('/usr/lib/linux-image-current-sunxi/' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' 'sun8i-r40-bananapi-m2-ultra.dtb') dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' @@ -1049,12 +1049,12 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=accel:tcg """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) self.vm.set_console() @@ -1075,12 +1075,12 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=machine:orangepi-pc """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -1121,12 +1121,12 @@ class BootLinuxConsole(LinuxKernelTest): self.require_netdev('user') deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 2c81412dba..232d287c27 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -203,12 +203,12 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:cubieboard """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' From bd8e9ddf6f6915b8a67909a21e7f46f77a05767d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 26 Apr 2024 13:29:10 +0100 Subject: [PATCH 0311/2884] target/arm: Refactor default generic timer frequency handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic timer frequency is settable by board code via a QOM property "cntfrq", but otherwise defaults to 62.5MHz. The way this is done includes some complication resulting from how this was originally a fixed value with no QOM property. Clean it up: * always set cpu->gt_cntfrq_hz to some sensible value, whether the CPU has the generic timer or not, and whether it's system or user-only emulation * this means we can always use gt_cntfrq_hz, and never need the old GTIMER_SCALE define * set the default value in exactly one place, in the realize fn The aim here is to pave the way for handling the ARMv8.6 requirement that the generic timer frequency is always 1GHz. We're going to do that by having old CPU types keep their legacy-in-QEMU behaviour and having the default for any new CPU types be a 1GHz rather han 62.5MHz cntfrq, so we want the point where the default is decided to be in one place, and in code, not in a DEFINE_PROP_UINT64() initializer. This commit should have no behavioural changes. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20240426122913.3427983-2-peter.maydell@linaro.org --- target/arm/cpu.c | 31 +++++++++++++++++-------------- target/arm/helper.c | 16 ++++++++-------- target/arm/internals.h | 7 ++++--- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a152def241..9f2ca6633a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1506,9 +1506,12 @@ static void arm_cpu_initfn(Object *obj) } } +/* + * 0 means "unset, use the default value". That default might vary depending + * on the CPU type, and is set in the realize fn. + */ static Property arm_cpu_gt_cntfrq_property = - DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, - NANOSECONDS_PER_SECOND / GTIMER_SCALE); + DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, 0); static Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); @@ -1954,6 +1957,17 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (!cpu->gt_cntfrq_hz) { + /* + * 0 means "the board didn't set a value, use the default". + * The default value of the generic timer frequency (as seen in + * CNTFRQ_EL0) is 62.5MHz, which corresponds to a period of 16ns. + * This is what you get (a) for a CONFIG_USER_ONLY CPU (b) if the + * board doesn't set it. + */ + cpu->gt_cntfrq_hz = GTIMER_DEFAULT_HZ; + } + #ifndef CONFIG_USER_ONLY /* The NVIC and M-profile CPU are two halves of a single piece of * hardware; trying to use one without the other is a command line @@ -2002,18 +2016,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } { - uint64_t scale; - - if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { - if (!cpu->gt_cntfrq_hz) { - error_setg(errp, "Invalid CNTFRQ: %"PRId64"Hz", - cpu->gt_cntfrq_hz); - return; - } - scale = gt_cntfrq_period_ns(cpu); - } else { - scale = GTIMER_SCALE; - } + uint64_t scale = gt_cntfrq_period_ns(cpu); cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale, arm_gt_ptimer_cb, cpu); diff --git a/target/arm/helper.c b/target/arm/helper.c index bb0e1baf62..7587635960 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2474,6 +2474,13 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { .resetvalue = 0 }, }; +static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) +{ + ARMCPU *cpu = env_archcpu(env); + + cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz; +} + #ifndef CONFIG_USER_ONLY static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3228,13 +3235,6 @@ void arm_gt_hvtimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_HYPVIRT); } -static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) -{ - ARMCPU *cpu = env_archcpu(env); - - cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz; -} - static const ARMCPRegInfo generic_timer_cp_reginfo[] = { /* * Note that CNTFRQ is purely reads-as-written for the benefit @@ -3514,7 +3514,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, .type = ARM_CP_CONST, .access = PL0_R /* no PL1_RW in linux-user */, .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), - .resetvalue = NANOSECONDS_PER_SECOND / GTIMER_SCALE, + .resetfn = arm_gt_cntfrq_reset, }, { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, diff --git a/target/arm/internals.h b/target/arm/internals.h index e40ec453d5..b6c78db024 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -60,10 +60,11 @@ static inline bool excp_is_internal(int excp) || excp == EXCP_SEMIHOST; } -/* Scale factor for generic timers, ie number of ns per tick. - * This gives a 62.5MHz timer. +/* + * Default frequency for the generic timer, in Hz. + * This is 62.5MHz, which gives a 16 ns tick period. */ -#define GTIMER_SCALE 16 +#define GTIMER_DEFAULT_HZ 62500000 /* Bit definitions for the v7M CONTROL register */ FIELD(V7M_CONTROL, NPRIV, 0, 1) From ee4336f9470b74f4dd3493d1fdbd61ff13005f9d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 26 Apr 2024 13:29:11 +0100 Subject: [PATCH 0312/2884] hw/arm/sbsa-ref: Force CPU generic timer to 62.5MHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently QEMU CPUs always run with a generic timer counter frequency of 62.5MHz, but ARMv8.6 CPUs will run at 1GHz. For older versions of the TF-A firmware that sbsa-ref runs, the frequency of the generic timer is hardcoded into the firmware, and so if the CPU actually has a different frequency then timers in the guest will be set incorrectly. The default frequency used by the 'max' CPU is about to change, so make the sbsa-ref board force the CPU frequency to the value which the firmware expects. Newer versions of TF-A will read the frequency from the CPU's CNTFRQ_EL0 register: https://github.com/ARM-software/arm-trusted-firmware/commit/4c77fac98dac0bebc63798aae9101ac865b87148 so in the longer term we could make this board use the 1GHz frequency. We will need to make sure we update the binaries used by our avocado test Aarch64SbsarefMachine.test_sbsaref_alpine_linux_max_pauth_impdef before we can do that. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marcin Juszkiewicz Message-id: 20240426122913.3427983-3-peter.maydell@linaro.org --- hw/arm/sbsa-ref.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index f5709d6c14..36f6f717b4 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -60,6 +60,19 @@ #define NUM_SMMU_IRQS 4 #define NUM_SATA_PORTS 6 +/* + * Generic timer frequency in Hz (which drives both the CPU generic timers + * and the SBSA watchdog-timer). Older versions of the TF-A firmware + * typically used with sbsa-ref (including the binaries in our Avocado test + * Aarch64SbsarefMachine.test_sbsaref_alpine_linux_max_pauth_impdef + * assume it is this value. + * + * TODO: this value is not architecturally correct for an Armv8.6 or + * better CPU, so we should move to 1GHz once the TF-A fix above has + * made it into a release and into our Avocado test. + */ +#define SBSA_GTIMER_HZ 62500000 + enum { SBSA_FLASH, SBSA_MEM, @@ -767,6 +780,8 @@ static void sbsa_ref_init(MachineState *machine) &error_abort); } + object_property_set_int(cpuobj, "cntfrq", SBSA_GTIMER_HZ, &error_abort); + object_property_set_link(cpuobj, "memory", OBJECT(sysmem), &error_abort); From 88c756bc9e5d38b6e46e631b9875ae954a319ed9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 26 Apr 2024 13:29:12 +0100 Subject: [PATCH 0313/2884] hw/watchdog/sbsa_gwdt: Make watchdog timer frequency a QOM property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the sbsa_gdwt watchdog device hardcodes its frequency at 62.5MHz. In real hardware, this watchdog is supposed to be driven from the system counter, which also drives the CPU generic timers. Newer CPU types (in particular from Armv8.6) should have a CPU generic timer frequency of 1GHz, so we can't leave the watchdog on the old QEMU default of 62.5GHz. Make the frequency a QOM property so it can be set by the board, and have our only board that uses this device set that frequency to the same value it sets the CPU frequency. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240426122913.3427983-4-peter.maydell@linaro.org --- hw/arm/sbsa-ref.c | 1 + hw/watchdog/sbsa_gwdt.c | 15 ++++++++++++++- include/hw/watchdog/sbsa_gwdt.h | 3 +-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 36f6f717b4..57c337fd92 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -543,6 +543,7 @@ static void create_wdt(const SBSAMachineState *sms) SysBusDevice *s = SYS_BUS_DEVICE(dev); int irq = sbsa_ref_irqmap[SBSA_GWDT_WS0]; + qdev_prop_set_uint64(dev, "clock-frequency", SBSA_GTIMER_HZ); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, rbase); sysbus_mmio_map(s, 1, cbase); diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 96895d7636..d437535cc6 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "sysemu/reset.h" #include "sysemu/watchdog.h" +#include "hw/qdev-properties.h" #include "hw/watchdog/sbsa_gwdt.h" #include "qemu/timer.h" #include "migration/vmstate.h" @@ -109,7 +110,7 @@ static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype) timeout = s->woru; timeout <<= 32; timeout |= s->worl; - timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ); + timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, s->freq); timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) && @@ -261,6 +262,17 @@ static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp) dev); } +static Property wdt_sbsa_gwdt_props[] = { + /* + * Timer frequency in Hz. This must match the frequency used by + * the CPU's generic timer. Default 62.5Hz matches QEMU's legacy + * CPU timer frequency default. + */ + DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq, + 62500000), + DEFINE_PROP_END_OF_LIST(), +}; + static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -271,6 +283,7 @@ static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_sbsa_gwdt; dc->desc = "SBSA-compliant generic watchdog device"; + device_class_set_props(dc, wdt_sbsa_gwdt_props); } static const TypeInfo wdt_sbsa_gwdt_info = { diff --git a/include/hw/watchdog/sbsa_gwdt.h b/include/hw/watchdog/sbsa_gwdt.h index 70b137de30..4bdc6c6fdb 100644 --- a/include/hw/watchdog/sbsa_gwdt.h +++ b/include/hw/watchdog/sbsa_gwdt.h @@ -55,8 +55,6 @@ #define SBSA_GWDT_RMMIO_SIZE 0x1000 #define SBSA_GWDT_CMMIO_SIZE 0x1000 -#define SBSA_TIMER_FREQ 62500000 /* Hz */ - typedef struct SBSA_GWDTState { /* */ SysBusDevice parent_obj; @@ -67,6 +65,7 @@ typedef struct SBSA_GWDTState { qemu_irq irq; QEMUTimer *timer; + uint64_t freq; uint32_t id; uint32_t wcs; From f037f5b4b91a32bf8f1ec2c8ff92d2d14242adb4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 26 Apr 2024 13:29:13 +0100 Subject: [PATCH 0314/2884] target/arm: Default to 1GHz cntfrq for 'max' and new CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In previous versions of the Arm architecture, the frequency of the generic timers as reported in CNTFRQ_EL0 could be any IMPDEF value, and for QEMU we picked 62.5MHz, giving a timer tick period of 16ns. In Armv8.6, the architecture standardized this frequency to 1GHz. Because there is no ID register feature field that indicates whether a CPU is v8.6 or that it ought to have this counter frequency, we implement this by changing our default CNTFRQ value for all CPUs, with exceptions for backwards compatibility: * CPU types which we already implement will retain the old default value. None of these are v8.6 CPUs, so this is architecturally OK. * CPUs used in versioned machine types with a version of 9.0 or earlier will retain the old default value. The upshot is that the only CPU type that changes is 'max'; but any new type we add in future (whether v8.6 or not) will also get the new 1GHz default. It remains the case that the machine model can override the default value via the 'cntfrq' QOM property (regardless of the CPU type). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240426122913.3427983-5-peter.maydell@linaro.org --- hw/core/machine.c | 4 +++- target/arm/cpu.c | 23 +++++++++++++++++------ target/arm/cpu.h | 11 +++++++++++ target/arm/cpu64.c | 2 ++ target/arm/internals.h | 12 ++++++++++-- target/arm/tcg/cpu32.c | 4 ++++ target/arm/tcg/cpu64.c | 18 ++++++++++++++++++ 7 files changed, 65 insertions(+), 9 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 0dec48e802..4ff60911e7 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -33,7 +33,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" -GlobalProperty hw_compat_9_0[] = {}; +GlobalProperty hw_compat_9_0[] = { + {"arm-cpu", "backcompat-cntfrq", "true" }, +}; const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); GlobalProperty hw_compat_8_2[] = { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 9f2ca6633a..fdc3eda318 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1959,13 +1959,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) if (!cpu->gt_cntfrq_hz) { /* - * 0 means "the board didn't set a value, use the default". - * The default value of the generic timer frequency (as seen in - * CNTFRQ_EL0) is 62.5MHz, which corresponds to a period of 16ns. - * This is what you get (a) for a CONFIG_USER_ONLY CPU (b) if the - * board doesn't set it. + * 0 means "the board didn't set a value, use the default". (We also + * get here for the CONFIG_USER_ONLY case.) + * ARMv8.6 and later CPUs architecturally must use a 1GHz timer; before + * that it was an IMPDEF choice, and QEMU initially picked 62.5MHz, + * which gives a 16ns tick period. + * + * We will use the back-compat value: + * - for QEMU CPU types added before we standardized on 1GHz + * - for versioned machine types with a version of 9.0 or earlier */ - cpu->gt_cntfrq_hz = GTIMER_DEFAULT_HZ; + if (arm_feature(env, ARM_FEATURE_BACKCOMPAT_CNTFRQ) || + cpu->backcompat_cntfrq) { + cpu->gt_cntfrq_hz = GTIMER_BACKCOMPAT_HZ; + } else { + cpu->gt_cntfrq_hz = GTIMER_DEFAULT_HZ; + } } #ifndef CONFIG_USER_ONLY @@ -2574,6 +2583,8 @@ static Property arm_cpu_properties[] = { mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID), DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), + /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */ + DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false), DEFINE_PROP_END_OF_LIST() }; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1f90590f93..a550bcd25f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -956,6 +956,9 @@ struct ArchCPU { */ bool host_cpu_probe_failed; + /* QOM property to indicate we should use the back-compat CNTFRQ default */ + bool backcompat_cntfrq; + /* Specify the number of cores in this CPU cluster. Used for the L2CTLR * register. */ @@ -2373,6 +2376,14 @@ enum arm_features { ARM_FEATURE_M_SECURITY, /* M profile Security Extension */ ARM_FEATURE_M_MAIN, /* M profile Main Extension */ ARM_FEATURE_V8_1M, /* M profile extras only in v8.1M and later */ + /* + * ARM_FEATURE_BACKCOMPAT_CNTFRQ makes the CPU default cntfrq be 62.5MHz + * if the board doesn't set a value, instead of 1GHz. It is for backwards + * compatibility and used only with CPU definitions that were already + * in QEMU before we changed the default. It should not be set on any + * CPU types added in future. + */ + ARM_FEATURE_BACKCOMPAT_CNTFRQ, /* 62.5MHz timer default */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 985b1efe16..c15d086049 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -599,6 +599,7 @@ static void aarch64_a57_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -656,6 +657,7 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); diff --git a/target/arm/internals.h b/target/arm/internals.h index b6c78db024..ee3ebd383e 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -62,9 +62,17 @@ static inline bool excp_is_internal(int excp) /* * Default frequency for the generic timer, in Hz. - * This is 62.5MHz, which gives a 16 ns tick period. + * ARMv8.6 and later CPUs architecturally must use a 1GHz timer; before + * that it was an IMPDEF choice, and QEMU initially picked 62.5MHz, + * which gives a 16ns tick period. + * + * We will use the back-compat value: + * - for QEMU CPU types added before we standardized on 1GHz + * - for versioned machine types with a version of 9.0 or earlier + * In any case, the machine model may override via the cntfrq property. */ -#define GTIMER_DEFAULT_HZ 62500000 +#define GTIMER_DEFAULT_HZ 1000000000 +#define GTIMER_BACKCOMPAT_HZ 62500000 /* Bit definitions for the v7M CONTROL register */ FIELD(V7M_CONTROL, NPRIV, 0, 1) diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index b5a60682fa..bdd82d912a 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -457,6 +457,7 @@ static void cortex_a7_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -505,6 +506,7 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -696,6 +698,7 @@ static void cortex_r52_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMSA); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_AUXCR); cpu->midr = 0x411fd133; /* r1p3 */ @@ -924,6 +927,7 @@ static void arm_max_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 443cffe3a8..da41a44f75 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -63,6 +63,7 @@ static void aarch64_a35_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -231,6 +232,7 @@ static void aarch64_a55_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -299,6 +301,7 @@ static void aarch64_a72_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -354,6 +357,7 @@ static void aarch64_a76_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -423,6 +427,7 @@ static void aarch64_a64fx_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -592,6 +597,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -663,6 +669,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -885,6 +892,7 @@ static void aarch64_a710_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -982,6 +990,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -1077,6 +1086,15 @@ void aarch64_max_tcg_initfn(Object *obj) uint64_t t; uint32_t u; + /* + * Unset ARM_FEATURE_BACKCOMPAT_CNTFRQ, which we would otherwise default + * to because we started with aarch64_a57_initfn(). A 'max' CPU might + * be a v8.6-or-later one, in which case the cntfrq must be 1GHz; and + * because it is our "may change" CPU type we are OK with it not being + * backwards-compatible with how it worked in old QEMU. + */ + unset_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); + /* * Reset MIDR so the guest doesn't mistake our 'max' CPU type for a real * one and try to apply errata workarounds or use impdef features we From 4b00855f0ee2e2eee8fd2500ffef27c108be6dc3 Mon Sep 17 00:00:00 2001 From: Alexandra Diupina Date: Sun, 28 Apr 2024 21:11:31 +0300 Subject: [PATCH 0315/2884] hw/dmax/xlnx_dpdma: fix handling of address_extension descriptor fields The DMA descriptor structures for this device have a set of "address extension" fields which extend the 32 bit source addresses with an extra 16 bits to give a 48 bit address: https://docs.amd.com/r/en-US/ug1085-zynq-ultrascale-trm/ADDR_EXT-Field However, we misimplemented this address extension in several ways: * we only extracted 12 bits of the extension fields, not 16 * we didn't shift the extension field up far enough * we accidentally did the shift as 32-bit arithmetic, which meant that we would have an overflow instead of setting bits [47:32] of the resulting 64-bit address Add a type cast and use extract64() instead of extract32() to avoid integer overflow on addition. Fix bit fields extraction according to documentation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: qemu-stable@nongnu.org Fixes: d3c6369a96 ("introduce xlnx-dpdma") Signed-off-by: Alexandra Diupina Message-id: 20240428181131.23801-1-adiupina@astralinux.ru [PMM: adjusted commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/dma/xlnx_dpdma.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index 1f5cd64ed1..530717d188 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -175,24 +175,24 @@ static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc, switch (frag) { case 0: - addr = desc->source_address - + (extract32(desc->address_extension, 16, 12) << 20); + addr = (uint64_t)desc->source_address + + (extract64(desc->address_extension, 16, 16) << 32); break; case 1: - addr = desc->source_address2 - + (extract32(desc->address_extension_23, 0, 12) << 8); + addr = (uint64_t)desc->source_address2 + + (extract64(desc->address_extension_23, 0, 16) << 32); break; case 2: - addr = desc->source_address3 - + (extract32(desc->address_extension_23, 16, 12) << 20); + addr = (uint64_t)desc->source_address3 + + (extract64(desc->address_extension_23, 16, 16) << 32); break; case 3: - addr = desc->source_address4 - + (extract32(desc->address_extension_45, 0, 12) << 8); + addr = (uint64_t)desc->source_address4 + + (extract64(desc->address_extension_45, 0, 16) << 32); break; case 4: - addr = desc->source_address5 - + (extract32(desc->address_extension_45, 16, 12) << 20); + addr = (uint64_t)desc->source_address5 + + (extract64(desc->address_extension_45, 16, 16) << 32); break; default: addr = 0; From afdc29b4a3a5a61c22342551bb38eea3731bfee1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 29 Apr 2024 09:59:08 +0200 Subject: [PATCH 0316/2884] hw/char/stm32l4x5_usart: Fix memory corruption by adding correct class_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "make check-qtest-aarch64" recently started failing on FreeBSD builds, and valgrind on Linux also detected that there is something fishy with the new stm32l4x5-usart: The code forgot to set the correct class_size here, so the various class_init functions in this file wrote beyond the allocated buffer when setting the subc->type field. Fixes: 4fb37aea7e ("hw/char: Implement STM32L4x5 USART skeleton") Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240429075908.36302-1-thuth@redhat.com Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 2627aab832..02f666308c 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -617,6 +617,7 @@ static const TypeInfo stm32l4x5_usart_types[] = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Stm32l4x5UsartBaseState), .instance_init = stm32l4x5_usart_base_init, + .class_size = sizeof(Stm32l4x5UsartBaseClass), .class_init = stm32l4x5_usart_base_class_init, .abstract = true, }, { From eb656a60fd93262b1e519b3162888bf261df7f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 22 Apr 2024 14:58:12 +0200 Subject: [PATCH 0317/2884] hw/arm/npcm7xx: Store derivative OTP fuse key in little endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use little endian for derivative OTP fuse key. Cc: qemu-stable@nongnu.org Fixes: c752bb079b ("hw/nvram: NPCM7xx OTP device model") Suggested-by: Avi Fishman Signed-off-by: Philippe Mathieu-Daudé Message-id: 20240422125813.1403-1-philmd@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/npcm7xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index cc68b5d8f1..9f2d96c733 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -24,6 +24,7 @@ #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "qapi/error.h" +#include "qemu/bswap.h" #include "qemu/units.h" #include "sysemu/sysemu.h" #include "target/arm/cpu-qom.h" @@ -386,7 +387,7 @@ static void npcm7xx_init_fuses(NPCM7xxState *s) * The initial mask of disabled modules indicates the chip derivative (e.g. * NPCM750 or NPCM730). */ - value = tswap32(nc->disabled_modules); + value = cpu_to_le32(nc->disabled_modules); npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE, sizeof(value)); } From c771f883f2e6db3acd7cbed0fde273bfc6cc580e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Wed, 24 Apr 2024 22:06:51 +0200 Subject: [PATCH 0318/2884] hw/display : Add device DM163 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device implements the IM120417002 colors shield v1.1 for Arduino (which relies on the DM163 8x3-channel led driving logic) and features a simple display of an 8x8 RGB matrix. The columns of the matrix are driven by the DM163 and the rows are driven externally. Acked-by: Alistair Francis Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240424200929.240921-2-ines.varhol@telecom-paris.fr [PMM: updated to new reset hold method prototype] Signed-off-by: Peter Maydell --- docs/system/arm/b-l475e-iot01a.rst | 3 +- hw/display/Kconfig | 3 + hw/display/dm163.c | 349 +++++++++++++++++++++++++++++ hw/display/meson.build | 1 + hw/display/trace-events | 14 ++ include/hw/display/dm163.h | 59 +++++ 6 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 hw/display/dm163.c create mode 100644 include/hw/display/dm163.h diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst index a76c9976c5..2adcc4b4c1 100644 --- a/docs/system/arm/b-l475e-iot01a.rst +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -12,7 +12,7 @@ USART, I2C, SPI, CAN and USB OTG, as well as a variety of sensors. Supported devices """"""""""""""""" -Currently B-L475E-IOT01A machine's only supports the following devices: +Currently B-L475E-IOT01A machines support the following devices: - Cortex-M4F based STM32L4x5 SoC - STM32L4x5 EXTI (Extended interrupts and events controller) @@ -20,6 +20,7 @@ Currently B-L475E-IOT01A machine's only supports the following devices: - STM32L4x5 RCC (Reset and clock control) - STM32L4x5 GPIOs (General-purpose I/Os) - STM32L4x5 USARTs, UARTs and LPUART (Serial ports) +- optional 8x8 led display (based on DM163 driver) Missing devices """"""""""""""" diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 234c7de027..a4552c8ed7 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -140,3 +140,6 @@ config XLNX_DISPLAYPORT bool # defaults to "N", enabled by specific boards depends on PIXMAN + +config DM163 + bool diff --git a/hw/display/dm163.c b/hw/display/dm163.c new file mode 100644 index 0000000000..f92aee371d --- /dev/null +++ b/hw/display/dm163.c @@ -0,0 +1,349 @@ +/* + * QEMU DM163 8x3-channel constant current led driver + * driving columns of associated 8x8 RGB matrix. + * + * Copyright (C) 2024 Samuel Tardieu + * Copyright (C) 2024 Arnaud Minier + * Copyright (C) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * The reference used for the DM163 is the following : + * http://www.siti.com.tw/product/spec/LED/DM163.pdf + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/display/dm163.h" +#include "ui/console.h" +#include "trace.h" + +#define LED_SQUARE_SIZE 100 +/* Number of frames a row stays visible after being turned off. */ +#define ROW_PERSISTENCE 3 +#define TURNED_OFF_ROW (COLOR_BUFFER_SIZE - 1) + +static const VMStateDescription vmstate_dm163 = { + .name = TYPE_DM163, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64_ARRAY(bank0_shift_register, DM163State, 3), + VMSTATE_UINT64_ARRAY(bank1_shift_register, DM163State, 3), + VMSTATE_UINT16_ARRAY(latched_outputs, DM163State, DM163_NUM_LEDS), + VMSTATE_UINT16_ARRAY(outputs, DM163State, DM163_NUM_LEDS), + VMSTATE_UINT8(dck, DM163State), + VMSTATE_UINT8(en_b, DM163State), + VMSTATE_UINT8(lat_b, DM163State), + VMSTATE_UINT8(rst_b, DM163State), + VMSTATE_UINT8(selbk, DM163State), + VMSTATE_UINT8(sin, DM163State), + VMSTATE_UINT8(activated_rows, DM163State), + VMSTATE_UINT32_2DARRAY(buffer, DM163State, COLOR_BUFFER_SIZE, + RGB_MATRIX_NUM_COLS), + VMSTATE_UINT8(last_buffer_idx, DM163State), + VMSTATE_UINT8_ARRAY(buffer_idx_of_row, DM163State, RGB_MATRIX_NUM_ROWS), + VMSTATE_UINT8_ARRAY(row_persistence_delay, DM163State, + RGB_MATRIX_NUM_ROWS), + VMSTATE_END_OF_LIST() + } +}; + +static void dm163_reset_hold(Object *obj, ResetType type) +{ + DM163State *s = DM163(obj); + + s->sin = 0; + s->dck = 0; + s->rst_b = 0; + /* Ensuring the first falling edge of lat_b isn't missed */ + s->lat_b = 1; + s->selbk = 0; + s->en_b = 0; + /* Reset stops the PWM, not the shift and latched registers. */ + memset(s->outputs, 0, sizeof(s->outputs)); + + s->activated_rows = 0; + s->redraw = 0; + trace_dm163_redraw(s->redraw); + for (unsigned i = 0; i < COLOR_BUFFER_SIZE; i++) { + memset(s->buffer[i], 0, sizeof(s->buffer[0])); + } + s->last_buffer_idx = 0; + memset(s->buffer_idx_of_row, TURNED_OFF_ROW, sizeof(s->buffer_idx_of_row)); + memset(s->row_persistence_delay, 0, sizeof(s->row_persistence_delay)); +} + +static void dm163_dck_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + if (new_state && !s->dck) { + /* + * On raising dck, sample selbk to get the bank to use, and + * sample sin for the bit to enter into the bank shift buffer. + */ + uint64_t *sb = + s->selbk ? s->bank1_shift_register : s->bank0_shift_register; + /* Output the outgoing bit on sout */ + const bool sout = (s->selbk ? sb[2] & MAKE_64BIT_MASK(63, 1) : + sb[2] & MAKE_64BIT_MASK(15, 1)) != 0; + qemu_set_irq(s->sout, sout); + /* Enter sin into the shift buffer */ + sb[2] = (sb[2] << 1) | ((sb[1] >> 63) & 1); + sb[1] = (sb[1] << 1) | ((sb[0] >> 63) & 1); + sb[0] = (sb[0] << 1) | s->sin; + } + + s->dck = new_state; + trace_dm163_dck(new_state); +} + +static void dm163_propagate_outputs(DM163State *s) +{ + s->last_buffer_idx = (s->last_buffer_idx + 1) % RGB_MATRIX_NUM_ROWS; + /* Values are output when reset is high and enable is low. */ + if (s->rst_b && !s->en_b) { + memcpy(s->outputs, s->latched_outputs, sizeof(s->outputs)); + } else { + memset(s->outputs, 0, sizeof(s->outputs)); + } + for (unsigned x = 0; x < RGB_MATRIX_NUM_COLS; x++) { + /* Grouping the 3 RGB channels in a pixel value */ + const uint16_t b = extract16(s->outputs[3 * x + 0], 6, 8); + const uint16_t g = extract16(s->outputs[3 * x + 1], 6, 8); + const uint16_t r = extract16(s->outputs[3 * x + 2], 6, 8); + uint32_t rgba = 0; + + trace_dm163_channels(3 * x + 2, r); + trace_dm163_channels(3 * x + 1, g); + trace_dm163_channels(3 * x + 0, b); + + rgba = deposit32(rgba, 0, 8, r); + rgba = deposit32(rgba, 8, 8, g); + rgba = deposit32(rgba, 16, 8, b); + + /* Led values are sent from the last one to the first one */ + s->buffer[s->last_buffer_idx][RGB_MATRIX_NUM_COLS - x - 1] = rgba; + } + for (unsigned row = 0; row < RGB_MATRIX_NUM_ROWS; row++) { + if (s->activated_rows & (1 << row)) { + s->buffer_idx_of_row[row] = s->last_buffer_idx; + s->redraw |= (1 << row); + trace_dm163_redraw(s->redraw); + } + } +} + +static void dm163_en_b_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->en_b = new_state; + dm163_propagate_outputs(s); + trace_dm163_en_b(new_state); +} + +static uint8_t dm163_bank0(const DM163State *s, uint8_t led) +{ + /* + * Bank 0 uses 6 bits per led, so a value may be stored accross + * two uint64_t entries. + */ + const uint8_t low_bit = 6 * led; + const uint8_t low_word = low_bit / 64; + const uint8_t high_word = (low_bit + 5) / 64; + const uint8_t low_shift = low_bit % 64; + + if (low_word == high_word) { + /* Simple case: the value belongs to one entry. */ + return extract64(s->bank0_shift_register[low_word], low_shift, 6); + } + + const uint8_t nb_bits_in_low_word = 64 - low_shift; + const uint8_t nb_bits_in_high_word = 6 - nb_bits_in_low_word; + + const uint64_t bits_in_low_word = \ + extract64(s->bank0_shift_register[low_word], low_shift, + nb_bits_in_low_word); + const uint64_t bits_in_high_word = \ + extract64(s->bank0_shift_register[high_word], 0, + nb_bits_in_high_word); + uint8_t val = 0; + + val = deposit32(val, 0, nb_bits_in_low_word, bits_in_low_word); + val = deposit32(val, nb_bits_in_low_word, nb_bits_in_high_word, + bits_in_high_word); + + return val; +} + +static uint8_t dm163_bank1(const DM163State *s, uint8_t led) +{ + const uint64_t entry = s->bank1_shift_register[led / RGB_MATRIX_NUM_COLS]; + return extract64(entry, 8 * (led % RGB_MATRIX_NUM_COLS), 8); +} + +static void dm163_lat_b_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + if (s->lat_b && !new_state) { + for (int led = 0; led < DM163_NUM_LEDS; led++) { + s->latched_outputs[led] = dm163_bank0(s, led) * dm163_bank1(s, led); + } + dm163_propagate_outputs(s); + } + + s->lat_b = new_state; + trace_dm163_lat_b(new_state); +} + +static void dm163_rst_b_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->rst_b = new_state; + dm163_propagate_outputs(s); + trace_dm163_rst_b(new_state); +} + +static void dm163_selbk_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->selbk = new_state; + trace_dm163_selbk(new_state); +} + +static void dm163_sin_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->sin = new_state; + trace_dm163_sin(new_state); +} + +static void dm163_rows_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + if (new_state) { + s->activated_rows |= (1 << line); + s->buffer_idx_of_row[line] = s->last_buffer_idx; + s->redraw |= (1 << line); + trace_dm163_redraw(s->redraw); + } else { + s->activated_rows &= ~(1 << line); + s->row_persistence_delay[line] = ROW_PERSISTENCE; + } + trace_dm163_activated_rows(s->activated_rows); +} + +static void dm163_invalidate_display(void *opaque) +{ + DM163State *s = (DM163State *)opaque; + s->redraw = 0xFF; + trace_dm163_redraw(s->redraw); +} + +static void update_row_persistence_delay(DM163State *s, unsigned row) +{ + if (s->row_persistence_delay[row]) { + s->row_persistence_delay[row]--; + } else { + /* + * If the ROW_PERSISTENCE delay is up, + * the row is turned off. + */ + s->buffer_idx_of_row[row] = TURNED_OFF_ROW; + s->redraw |= (1 << row); + trace_dm163_redraw(s->redraw); + } +} + +static uint32_t *update_display_of_row(DM163State *s, uint32_t *dest, + unsigned row) +{ + for (unsigned _ = 0; _ < LED_SQUARE_SIZE; _++) { + for (int x = 0; x < RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE; x++) { + /* UI layer guarantees that there's 32 bits per pixel (Mar 2024) */ + *dest++ = s->buffer[s->buffer_idx_of_row[row]][x / LED_SQUARE_SIZE]; + } + } + + dpy_gfx_update(s->console, 0, LED_SQUARE_SIZE * row, + RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, LED_SQUARE_SIZE); + s->redraw &= ~(1 << row); + trace_dm163_redraw(s->redraw); + + return dest; +} + +static void dm163_update_display(void *opaque) +{ + DM163State *s = (DM163State *)opaque; + DisplaySurface *surface = qemu_console_surface(s->console); + uint32_t *dest; + + dest = surface_data(surface); + for (unsigned row = 0; row < RGB_MATRIX_NUM_ROWS; row++) { + update_row_persistence_delay(s, row); + if (!extract8(s->redraw, row, 1)) { + dest += LED_SQUARE_SIZE * LED_SQUARE_SIZE * RGB_MATRIX_NUM_COLS; + continue; + } + dest = update_display_of_row(s, dest, row); + } +} + +static const GraphicHwOps dm163_ops = { + .invalidate = dm163_invalidate_display, + .gfx_update = dm163_update_display, +}; + +static void dm163_realize(DeviceState *dev, Error **errp) +{ + DM163State *s = DM163(dev); + + qdev_init_gpio_in(dev, dm163_rows_gpio_handler, RGB_MATRIX_NUM_ROWS); + qdev_init_gpio_in(dev, dm163_sin_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_dck_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_rst_b_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_lat_b_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_selbk_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_en_b_gpio_handler, 1); + qdev_init_gpio_out_named(dev, &s->sout, "sout", 1); + + s->console = graphic_console_init(dev, 0, &dm163_ops, s); + qemu_console_resize(s->console, RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, + RGB_MATRIX_NUM_ROWS * LED_SQUARE_SIZE); +} + +static void dm163_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "DM163"; + dc->vmsd = &vmstate_dm163; + dc->realize = dm163_realize; + rc->phases.hold = dm163_reset_hold; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo dm163_types[] = { + { + .name = TYPE_DM163, + .parent = TYPE_DEVICE, + .instance_size = sizeof(DM163State), + .class_init = dm163_class_init + } +}; + +DEFINE_TYPES(dm163_types) diff --git a/hw/display/meson.build b/hw/display/meson.build index 4751aab3ba..7893b94c8e 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -38,6 +38,7 @@ system_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c')) system_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-dmabuf.c')) +system_ss.add(when: 'CONFIG_DM163', if_true: files('dm163.c')) if (config_all_devices.has_key('CONFIG_VGA_CIRRUS') or config_all_devices.has_key('CONFIG_VGA_PCI') or diff --git a/hw/display/trace-events b/hw/display/trace-events index 2336a0ca15..781f8a3320 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -177,3 +177,17 @@ macfb_ctrl_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRI macfb_sense_read(uint32_t value) "video sense: 0x%"PRIx32 macfb_sense_write(uint32_t value) "video sense: 0x%"PRIx32 macfb_update_mode(uint32_t width, uint32_t height, uint8_t depth) "setting mode to width %"PRId32 " height %"PRId32 " size %d" + +# dm163.c +dm163_redraw(uint8_t redraw) "0x%02x" +dm163_dck(unsigned new_state) "dck : %u" +dm163_en_b(unsigned new_state) "en_b : %u" +dm163_rst_b(unsigned new_state) "rst_b : %u" +dm163_lat_b(unsigned new_state) "lat_b : %u" +dm163_sin(unsigned new_state) "sin : %u" +dm163_selbk(unsigned new_state) "selbk : %u" +dm163_activated_rows(int new_state) "Activated rows : 0x%" PRIx32 "" +dm163_bits_ppi(unsigned dest_width) "dest_width : %u" +dm163_leds(int led, uint32_t value) "led %d: 0x%x" +dm163_channels(int channel, uint8_t value) "channel %d: 0x%x" +dm163_refresh_rate(uint32_t rr) "refresh rate %d" diff --git a/include/hw/display/dm163.h b/include/hw/display/dm163.h new file mode 100644 index 0000000000..4377f77bb7 --- /dev/null +++ b/include/hw/display/dm163.h @@ -0,0 +1,59 @@ +/* + * QEMU DM163 8x3-channel constant current led driver + * driving columns of associated 8x8 RGB matrix. + * + * Copyright (C) 2024 Samuel Tardieu + * Copyright (C) 2024 Arnaud Minier + * Copyright (C) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_DISPLAY_DM163_H +#define HW_DISPLAY_DM163_H + +#include "qom/object.h" +#include "hw/qdev-core.h" + +#define TYPE_DM163 "dm163" +OBJECT_DECLARE_SIMPLE_TYPE(DM163State, DM163); + +#define RGB_MATRIX_NUM_ROWS 8 +#define RGB_MATRIX_NUM_COLS 8 +#define DM163_NUM_LEDS (RGB_MATRIX_NUM_COLS * 3) +/* The last row is filled with 0 (turned off row) */ +#define COLOR_BUFFER_SIZE (RGB_MATRIX_NUM_ROWS + 1) + +typedef struct DM163State { + DeviceState parent_obj; + + /* DM163 driver */ + uint64_t bank0_shift_register[3]; + uint64_t bank1_shift_register[3]; + uint16_t latched_outputs[DM163_NUM_LEDS]; + uint16_t outputs[DM163_NUM_LEDS]; + qemu_irq sout; + + uint8_t sin; + uint8_t dck; + uint8_t rst_b; + uint8_t lat_b; + uint8_t selbk; + uint8_t en_b; + + /* IM120417002 colors shield */ + uint8_t activated_rows; + + /* 8x8 RGB matrix */ + QemuConsole *console; + uint8_t redraw; + /* Rows currently being displayed on the matrix. */ + /* The last row is filled with 0 (turned off row) */ + uint32_t buffer[COLOR_BUFFER_SIZE][RGB_MATRIX_NUM_COLS]; + uint8_t last_buffer_idx; + uint8_t buffer_idx_of_row[RGB_MATRIX_NUM_ROWS]; + /* Used to simulate retinal persistence of rows */ + uint8_t row_persistence_delay[RGB_MATRIX_NUM_ROWS]; +} DM163State; + +#endif /* HW_DISPLAY_DM163_H */ From 5b5b014b32458d68b69fe08e31e4d7b2c570836a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Wed, 24 Apr 2024 22:06:52 +0200 Subject: [PATCH 0319/2884] hw/arm : Pass STM32L4x5 SYSCFG gpios to STM32L4x5 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exposing SYSCFG inputs to the SoC is practical in order to wire the SoC to the optional DM163 display from the board code (GPIOs outputs need to be connected to both SYSCFG inputs and DM163 inputs). STM32L4x5 SYSCFG in-irq interception needed to be changed accordingly. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240424200929.240921-3-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/arm/stm32l4x5_soc.c | 6 ++++-- tests/qtest/stm32l4x5_gpio-test.c | 13 ++++++++----- tests/qtest/stm32l4x5_syscfg-test.c | 17 ++++++++++------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 39924822f3..38f7a2d5d9 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -1,8 +1,8 @@ /* * STM32L4x5 SoC family * - * Copyright (c) 2023 Arnaud Minier - * Copyright (c) 2023 Inès Varhol + * Copyright (c) 2023-2024 Arnaud Minier + * Copyright (c) 2023-2024 Inès Varhol * * SPDX-License-Identifier: GPL-2.0-or-later * @@ -250,6 +250,8 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) } } + qdev_pass_gpios(DEVICE(&s->syscfg), dev_soc, NULL); + /* EXTI device */ busdev = SYS_BUS_DEVICE(&s->exti); if (!sysbus_realize(busdev, errp)) { diff --git a/tests/qtest/stm32l4x5_gpio-test.c b/tests/qtest/stm32l4x5_gpio-test.c index 0f6bda54d3..72a7823406 100644 --- a/tests/qtest/stm32l4x5_gpio-test.c +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -43,6 +43,9 @@ #define OTYPER_PUSH_PULL 0 #define OTYPER_OPEN_DRAIN 1 +/* SoC forwards GPIOs to SysCfg */ +#define SYSCFG "/machine/soc" + const uint32_t moder_reset[NUM_GPIOS] = { 0xABFFFFFF, 0xFFFFFEBF, @@ -284,7 +287,7 @@ static void test_gpio_output_mode(const void *data) uint32_t gpio = test_gpio_addr(data); unsigned int gpio_id = get_gpio_id(gpio); - qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + qtest_irq_intercept_in(global_qtest, SYSCFG); /* Set a bit in ODR and check nothing happens */ gpio_set_bit(gpio, ODR, pin, 1); @@ -319,7 +322,7 @@ static void test_gpio_input_mode(const void *data) uint32_t gpio = test_gpio_addr(data); unsigned int gpio_id = get_gpio_id(gpio); - qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + qtest_irq_intercept_in(global_qtest, SYSCFG); /* Configure a line as input, raise it, and check that the pin is high */ gpio_set_2bits(gpio, MODER, pin, MODER_INPUT); @@ -348,7 +351,7 @@ static void test_pull_up_pull_down(const void *data) uint32_t gpio = test_gpio_addr(data); unsigned int gpio_id = get_gpio_id(gpio); - qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + qtest_irq_intercept_in(global_qtest, SYSCFG); /* Configure a line as input with pull-up, check the line is set high */ gpio_set_2bits(gpio, MODER, pin, MODER_INPUT); @@ -378,7 +381,7 @@ static void test_push_pull(const void *data) uint32_t gpio = test_gpio_addr(data); uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio); - qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + qtest_irq_intercept_in(global_qtest, SYSCFG); /* Setting a line high externally, configuring it in push-pull output */ /* And checking the pin was disconnected */ @@ -425,7 +428,7 @@ static void test_open_drain(const void *data) uint32_t gpio = test_gpio_addr(data); uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio); - qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + qtest_irq_intercept_in(global_qtest, SYSCFG); /* Setting a line high externally, configuring it in open-drain output */ /* And checking the pin was disconnected */ diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index 59bac829b7..506ca08bc2 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -1,8 +1,8 @@ /* * QTest testcase for STM32L4x5_SYSCFG * - * Copyright (c) 2023 Arnaud Minier - * Copyright (c) 2023 Inès Varhol + * Copyright (c) 2024 Arnaud Minier + * Copyright (c) 2024 Inès Varhol * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -25,6 +25,10 @@ #define SYSCFG_SWPR2 0x28 #define INVALID_ADDR 0x2C +/* SoC forwards GPIOs to SysCfg */ +#define SYSCFG "/machine/soc" +#define EXTI "/machine/soc/exti" + static void syscfg_writel(unsigned int offset, uint32_t value) { writel(SYSCFG_BASE_ADDR + offset, value); @@ -37,8 +41,7 @@ static uint32_t syscfg_readl(unsigned int offset) static void syscfg_set_irq(int num, int level) { - qtest_set_irq_in(global_qtest, "/machine/soc/syscfg", - NULL, num, level); + qtest_set_irq_in(global_qtest, SYSCFG, NULL, num, level); } static void system_reset(void) @@ -197,7 +200,7 @@ static void test_interrupt(void) * Test that GPIO rising lines result in an irq * with the right configuration */ - qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + qtest_irq_intercept_in(global_qtest, EXTI); /* GPIOA is the default source for EXTI lines 0 to 15 */ @@ -230,7 +233,7 @@ static void test_irq_pin_multiplexer(void) * Test that syscfg irq sets the right exti irq */ - qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + qtest_irq_intercept_in(global_qtest, EXTI); syscfg_set_irq(0, 1); @@ -257,7 +260,7 @@ static void test_irq_gpio_multiplexer(void) * Test that an irq is generated only by the right GPIO */ - qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + qtest_irq_intercept_in(global_qtest, EXTI); /* GPIOA is the default source for EXTI lines 0 to 15 */ From 4c3308c61ee9adfbb0ad5764a6b0b232d325fb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Wed, 24 Apr 2024 22:06:53 +0200 Subject: [PATCH 0320/2884] hw/arm : Create Bl475eMachineState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240424200929.240921-4-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/arm/b-l475e-iot01a.c | 46 ++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index d862aa43fc..970c637ce6 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -2,8 +2,8 @@ * B-L475E-IOT01A Discovery Kit machine * (B-L475E-IOT01A IoT Node) * - * Copyright (c) 2023 Arnaud Minier - * Copyright (c) 2023 Inès Varhol + * Copyright (c) 2023-2024 Arnaud Minier + * Copyright (c) 2023-2024 Inès Varhol * * SPDX-License-Identifier: GPL-2.0-or-later * @@ -32,33 +32,51 @@ /* B-L475E-IOT01A implementation is derived from netduinoplus2 */ -static void b_l475e_iot01a_init(MachineState *machine) +#define TYPE_B_L475E_IOT01A MACHINE_TYPE_NAME("b-l475e-iot01a") +OBJECT_DECLARE_SIMPLE_TYPE(Bl475eMachineState, B_L475E_IOT01A) + +typedef struct Bl475eMachineState { + MachineState parent_obj; + + Stm32l4x5SocState soc; +} Bl475eMachineState; + +static void bl475e_init(MachineState *machine) { + Bl475eMachineState *s = B_L475E_IOT01A(machine); const Stm32l4x5SocClass *sc; - DeviceState *dev; - dev = qdev_new(TYPE_STM32L4X5XG_SOC); - object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_STM32L4X5XG_SOC); + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); - sc = STM32L4X5_SOC_GET_CLASS(dev); - armv7m_load_kernel(ARM_CPU(first_cpu), - machine->kernel_filename, - 0, sc->flash_size); + sc = STM32L4X5_SOC_GET_CLASS(&s->soc); + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0, + sc->flash_size); } -static void b_l475e_iot01a_machine_init(MachineClass *mc) +static void bl475e_machine_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); static const char *machine_valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-m4"), NULL }; mc->desc = "B-L475E-IOT01A Discovery Kit (Cortex-M4)"; - mc->init = b_l475e_iot01a_init; + mc->init = bl475e_init; mc->valid_cpu_types = machine_valid_cpu_types; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; } -DEFINE_MACHINE("b-l475e-iot01a", b_l475e_iot01a_machine_init) +static const TypeInfo bl475e_machine_type[] = { + { + .name = TYPE_B_L475E_IOT01A, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Bl475eMachineState), + .class_init = bl475e_machine_init, + } +}; + +DEFINE_TYPES(bl475e_machine_type) From 49157207c0da4a0fea26464c6e57fa790009186b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Wed, 24 Apr 2024 22:06:54 +0200 Subject: [PATCH 0321/2884] hw/arm : Connect DM163 to B-L475E-IOT01A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240424200929.240921-5-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/b-l475e-iot01a.c | 59 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index e8b6e5e5eb..fe1f9643bd 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -468,6 +468,7 @@ config B_L475E_IOT01A default y depends on TCG && ARM select STM32L4X5_SOC + imply DM163 config STM32L4X5_SOC bool diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index 970c637ce6..5002a40f06 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -27,10 +27,37 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" -#include "hw/arm/stm32l4x5_soc.h" #include "hw/arm/boot.h" +#include "hw/core/split-irq.h" +#include "hw/arm/stm32l4x5_soc.h" +#include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/display/dm163.h" -/* B-L475E-IOT01A implementation is derived from netduinoplus2 */ +/* B-L475E-IOT01A implementation is inspired from netduinoplus2 and arduino */ + +/* + * There are actually 14 input pins in the DM163 device. + * Here the DM163 input pin EN isn't connected to the STM32L4x5 + * GPIOs as the IM120417002 colors shield doesn't actually use + * this pin to drive the RGB matrix. + */ +#define NUM_DM163_INPUTS 13 + +static const unsigned dm163_input[NUM_DM163_INPUTS] = { + 1 * GPIO_NUM_PINS + 2, /* ROW0 PB2 */ + 0 * GPIO_NUM_PINS + 15, /* ROW1 PA15 */ + 0 * GPIO_NUM_PINS + 2, /* ROW2 PA2 */ + 0 * GPIO_NUM_PINS + 7, /* ROW3 PA7 */ + 0 * GPIO_NUM_PINS + 6, /* ROW4 PA6 */ + 0 * GPIO_NUM_PINS + 5, /* ROW5 PA5 */ + 1 * GPIO_NUM_PINS + 0, /* ROW6 PB0 */ + 0 * GPIO_NUM_PINS + 3, /* ROW7 PA3 */ + 0 * GPIO_NUM_PINS + 4, /* SIN (SDA) PA4 */ + 1 * GPIO_NUM_PINS + 1, /* DCK (SCK) PB1 */ + 2 * GPIO_NUM_PINS + 3, /* RST_B (RST) PC3 */ + 2 * GPIO_NUM_PINS + 4, /* LAT_B (LAT) PC4 */ + 2 * GPIO_NUM_PINS + 5, /* SELBK (SB) PC5 */ +}; #define TYPE_B_L475E_IOT01A MACHINE_TYPE_NAME("b-l475e-iot01a") OBJECT_DECLARE_SIMPLE_TYPE(Bl475eMachineState, B_L475E_IOT01A) @@ -39,12 +66,16 @@ typedef struct Bl475eMachineState { MachineState parent_obj; Stm32l4x5SocState soc; + SplitIRQ gpio_splitters[NUM_DM163_INPUTS]; + DM163State dm163; } Bl475eMachineState; static void bl475e_init(MachineState *machine) { Bl475eMachineState *s = B_L475E_IOT01A(machine); const Stm32l4x5SocClass *sc; + DeviceState *dev, *gpio_out_splitter; + unsigned gpio, pin; object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_STM32L4X5XG_SOC); @@ -53,6 +84,30 @@ static void bl475e_init(MachineState *machine) sc = STM32L4X5_SOC_GET_CLASS(&s->soc); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0, sc->flash_size); + + if (object_class_by_name(TYPE_DM163)) { + object_initialize_child(OBJECT(machine), "dm163", + &s->dm163, TYPE_DM163); + dev = DEVICE(&s->dm163); + qdev_realize(dev, NULL, &error_abort); + + for (unsigned i = 0; i < NUM_DM163_INPUTS; i++) { + object_initialize_child(OBJECT(machine), "gpio-out-splitters[*]", + &s->gpio_splitters[i], TYPE_SPLIT_IRQ); + gpio_out_splitter = DEVICE(&s->gpio_splitters[i]); + qdev_prop_set_uint32(gpio_out_splitter, "num-lines", 2); + qdev_realize(gpio_out_splitter, NULL, &error_fatal); + + qdev_connect_gpio_out(gpio_out_splitter, 0, + qdev_get_gpio_in(DEVICE(&s->soc), dm163_input[i])); + qdev_connect_gpio_out(gpio_out_splitter, 1, + qdev_get_gpio_in(dev, i)); + gpio = dm163_input[i] / GPIO_NUM_PINS; + pin = dm163_input[i] % GPIO_NUM_PINS; + qdev_connect_gpio_out(DEVICE(&s->soc.gpio[gpio]), pin, + qdev_get_gpio_in(DEVICE(gpio_out_splitter), 0)); + } + } } static void bl475e_machine_init(ObjectClass *oc, void *data) From a0c325c4b05cf7815739d6a84e567b95c8c5be7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Wed, 24 Apr 2024 22:06:55 +0200 Subject: [PATCH 0322/2884] tests/qtest : Add testcase for DM163 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `test_dm163_bank()` Checks that the pin "sout" of the DM163 led driver outputs the values received on pin "sin" with the expected latency (depending on the bank). `test_dm163_gpio_connection()` Check that changes to relevant STM32L4x5 GPIO pins are propagated to the DM163 device. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Acked-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240424200929.240921-6-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- tests/qtest/dm163-test.c | 194 +++++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 + 2 files changed, 196 insertions(+) create mode 100644 tests/qtest/dm163-test.c diff --git a/tests/qtest/dm163-test.c b/tests/qtest/dm163-test.c new file mode 100644 index 0000000000..3161c9208d --- /dev/null +++ b/tests/qtest/dm163-test.c @@ -0,0 +1,194 @@ +/* + * QTest testcase for DM163 + * + * Copyright (C) 2024 Samuel Tardieu + * Copyright (C) 2024 Arnaud Minier + * Copyright (C) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +enum DM163_INPUTS { + SIN = 8, + DCK = 9, + RST_B = 10, + LAT_B = 11, + SELBK = 12, + EN_B = 13 +}; + +#define DEVICE_NAME "/machine/dm163" +#define GPIO_OUT(name, value) qtest_set_irq_in(qts, DEVICE_NAME, NULL, name, \ + value) +#define GPIO_PULSE(name) \ + do { \ + GPIO_OUT(name, 1); \ + GPIO_OUT(name, 0); \ + } while (0) + + +static void rise_gpio_pin_dck(QTestState *qts) +{ + /* Configure output mode for pin PB1 */ + qtest_writel(qts, 0x48000400, 0xFFFFFEB7); + /* Write 1 in ODR for PB1 */ + qtest_writel(qts, 0x48000414, 0x00000002); +} + +static void lower_gpio_pin_dck(QTestState *qts) +{ + /* Configure output mode for pin PB1 */ + qtest_writel(qts, 0x48000400, 0xFFFFFEB7); + /* Write 0 in ODR for PB1 */ + qtest_writel(qts, 0x48000414, 0x00000000); +} + +static void rise_gpio_pin_selbk(QTestState *qts) +{ + /* Configure output mode for pin PC5 */ + qtest_writel(qts, 0x48000800, 0xFFFFF7FF); + /* Write 1 in ODR for PC5 */ + qtest_writel(qts, 0x48000814, 0x00000020); +} + +static void lower_gpio_pin_selbk(QTestState *qts) +{ + /* Configure output mode for pin PC5 */ + qtest_writel(qts, 0x48000800, 0xFFFFF7FF); + /* Write 0 in ODR for PC5 */ + qtest_writel(qts, 0x48000814, 0x00000000); +} + +static void rise_gpio_pin_lat_b(QTestState *qts) +{ + /* Configure output mode for pin PC4 */ + qtest_writel(qts, 0x48000800, 0xFFFFFDFF); + /* Write 1 in ODR for PC4 */ + qtest_writel(qts, 0x48000814, 0x00000010); +} + +static void lower_gpio_pin_lat_b(QTestState *qts) +{ + /* Configure output mode for pin PC4 */ + qtest_writel(qts, 0x48000800, 0xFFFFFDFF); + /* Write 0 in ODR for PC4 */ + qtest_writel(qts, 0x48000814, 0x00000000); +} + +static void rise_gpio_pin_rst_b(QTestState *qts) +{ + /* Configure output mode for pin PC3 */ + qtest_writel(qts, 0x48000800, 0xFFFFFF7F); + /* Write 1 in ODR for PC3 */ + qtest_writel(qts, 0x48000814, 0x00000008); +} + +static void lower_gpio_pin_rst_b(QTestState *qts) +{ + /* Configure output mode for pin PC3 */ + qtest_writel(qts, 0x48000800, 0xFFFFFF7F); + /* Write 0 in ODR for PC3 */ + qtest_writel(qts, 0x48000814, 0x00000000); +} + +static void rise_gpio_pin_sin(QTestState *qts) +{ + /* Configure output mode for pin PA4 */ + qtest_writel(qts, 0x48000000, 0xFFFFFDFF); + /* Write 1 in ODR for PA4 */ + qtest_writel(qts, 0x48000014, 0x00000010); +} + +static void lower_gpio_pin_sin(QTestState *qts) +{ + /* Configure output mode for pin PA4 */ + qtest_writel(qts, 0x48000000, 0xFFFFFDFF); + /* Write 0 in ODR for PA4 */ + qtest_writel(qts, 0x48000014, 0x00000000); +} + +static void test_dm163_bank(const void *opaque) +{ + const unsigned bank = (uintptr_t) opaque; + const int width = bank ? 192 : 144; + + QTestState *qts = qtest_initf("-M b-l475e-iot01a"); + qtest_irq_intercept_out_named(qts, DEVICE_NAME, "sout"); + GPIO_OUT(RST_B, 1); + GPIO_OUT(EN_B, 0); + GPIO_OUT(DCK, 0); + GPIO_OUT(SELBK, bank); + GPIO_OUT(LAT_B, 1); + + /* Fill bank with zeroes */ + GPIO_OUT(SIN, 0); + for (int i = 0; i < width; i++) { + GPIO_PULSE(DCK); + } + /* Fill bank with ones, check that we get the previous zeroes */ + GPIO_OUT(SIN, 1); + for (int i = 0; i < width; i++) { + GPIO_PULSE(DCK); + g_assert(!qtest_get_irq(qts, 0)); + } + + /* Pulse one more bit in the bank, check that we get a one */ + GPIO_PULSE(DCK); + g_assert(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +static void test_dm163_gpio_connection(void) +{ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + qtest_irq_intercept_in(qts, DEVICE_NAME); + + g_assert_false(qtest_get_irq(qts, SIN)); + g_assert_false(qtest_get_irq(qts, DCK)); + g_assert_false(qtest_get_irq(qts, RST_B)); + g_assert_false(qtest_get_irq(qts, LAT_B)); + g_assert_false(qtest_get_irq(qts, SELBK)); + + rise_gpio_pin_dck(qts); + g_assert_true(qtest_get_irq(qts, DCK)); + lower_gpio_pin_dck(qts); + g_assert_false(qtest_get_irq(qts, DCK)); + + rise_gpio_pin_lat_b(qts); + g_assert_true(qtest_get_irq(qts, LAT_B)); + lower_gpio_pin_lat_b(qts); + g_assert_false(qtest_get_irq(qts, LAT_B)); + + rise_gpio_pin_selbk(qts); + g_assert_true(qtest_get_irq(qts, SELBK)); + lower_gpio_pin_selbk(qts); + g_assert_false(qtest_get_irq(qts, SELBK)); + + rise_gpio_pin_rst_b(qts); + g_assert_true(qtest_get_irq(qts, RST_B)); + lower_gpio_pin_rst_b(qts); + g_assert_false(qtest_get_irq(qts, RST_B)); + + rise_gpio_pin_sin(qts); + g_assert_true(qtest_get_irq(qts, SIN)); + lower_gpio_pin_sin(qts); + g_assert_false(qtest_get_irq(qts, SIN)); + + g_assert_false(qtest_get_irq(qts, DCK)); + g_assert_false(qtest_get_irq(qts, LAT_B)); + g_assert_false(qtest_get_irq(qts, SELBK)); + g_assert_false(qtest_get_irq(qts, RST_B)); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_data_func("/dm163/bank0", (void *)0, test_dm163_bank); + qtest_add_data_func("/dm163/bank1", (void *)1, test_dm163_bank); + qtest_add_func("/dm163/gpio_connection", test_dm163_gpio_connection); + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index b128fa5a4b..6f2f594ace 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -224,6 +224,8 @@ qtests_arm = \ (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \ (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \ (config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \ + (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and + config_all_devices.has_key('CONFIG_DM163')? ['dm163-test'] : []) + \ ['arm-cpu-features', 'boot-serial-test'] From ed30e7b1d9f5639d346fc2f0285568516e324398 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 08:49:49 -1000 Subject: [PATCH 0323/2884] tcg: Make tcg/helper-info.h self-contained MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move MAX_CALL_IARGS from tcg.h and include for the define of TCG_TARGET_REG_BITS. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/helper-info.h | 3 +++ include/tcg/tcg.h | 2 -- tcg/tci.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h index 7c27d6164a..909fe73afa 100644 --- a/include/tcg/helper-info.h +++ b/include/tcg/helper-info.h @@ -12,6 +12,9 @@ #ifdef CONFIG_TCG_INTERPRETER #include #endif +#include "tcg-target-reg-bits.h" + +#define MAX_CALL_IARGS 7 /* * Describe the calling convention of a given argument type. diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 05a1912f8a..e4c598428d 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -39,8 +39,6 @@ /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 266 -#define MAX_CALL_IARGS 7 - #define CPU_TEMP_BUF_NLONGS 128 #define TCG_STATIC_FRAME_SIZE (CPU_TEMP_BUF_NLONGS * sizeof(long)) diff --git a/tcg/tci.c b/tcg/tci.c index 39adcb7d82..3afb223528 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" +#include "tcg/helper-info.h" #include "tcg/tcg-ldst.h" #include From 83a0ad26737b9bca3b09fc8d27163ef6a0f28bd9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 13:48:41 -1000 Subject: [PATCH 0324/2884] tcg: Pass function pointer to tcg_gen_call* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For normal helpers, read the function pointer from the structure earlier. For plugins, this will allow the function pointer to come from elsewhere. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/helper-gen.h.inc | 24 ++++++++++++------- include/tcg/tcg.h | 21 +++++++++------- tcg/tcg.c | 45 +++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 38 deletions(-) diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc index d9fd3ed72a..dabe138e20 100644 --- a/include/exec/helper-gen.h.inc +++ b/include/exec/helper-gen.h.inc @@ -14,7 +14,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ { \ - tcg_gen_call0(&glue(helper_info_, name), dh_retvar(ret)); \ + tcg_gen_call0(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret)); \ } #define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ @@ -22,7 +23,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1)) \ { \ - tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call1(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1)); \ } @@ -31,7 +33,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ { \ - tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call2(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2)); \ } @@ -40,7 +43,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ { \ - tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call3(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \ } @@ -50,7 +54,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ - tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call4(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2), \ dh_arg(t3, 3), dh_arg(t4, 4)); \ } @@ -61,7 +66,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \ { \ - tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call5(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ dh_arg(t4, 4), dh_arg(t5, 5)); \ } @@ -72,7 +78,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \ { \ - tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call6(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6)); \ } @@ -84,7 +91,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ dh_arg_decl(t7, 7)) \ { \ - tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call7(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ dh_arg(t7, 7)); \ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index e4c598428d..8d9f6585ff 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -852,19 +852,22 @@ typedef struct TCGTargetOpDef { bool tcg_op_supported(TCGOpcode op); -void tcg_gen_call0(TCGHelperInfo *, TCGTemp *ret); -void tcg_gen_call1(TCGHelperInfo *, TCGTemp *ret, TCGTemp *); -void tcg_gen_call2(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *); -void tcg_gen_call3(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, +void tcg_gen_call0(void *func, TCGHelperInfo *, TCGTemp *ret); +void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *); +void tcg_gen_call2(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *); -void tcg_gen_call4(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, - TCGTemp *, TCGTemp *); -void tcg_gen_call5(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, +void tcg_gen_call3(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, TCGTemp *); -void tcg_gen_call6(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, +void tcg_gen_call4(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); -void tcg_gen_call7(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, +void tcg_gen_call5(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call6(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *); +void tcg_gen_call7(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *); TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs); void tcg_op_remove(TCGContext *s, TCGOp *op); diff --git a/tcg/tcg.c b/tcg/tcg.c index 6a32656cd4..7484a07722 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2251,7 +2251,8 @@ bool tcg_op_supported(TCGOpcode op) static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); -static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) +static void tcg_gen_callN(void *func, TCGHelperInfo *info, + TCGTemp *ret, TCGTemp **args) { TCGv_i64 extend_free[MAX_CALL_IARGS]; int n_extend = 0; @@ -2329,7 +2330,7 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) g_assert_not_reached(); } } - op->args[pi++] = (uintptr_t)info->func; + op->args[pi++] = (uintptr_t)func; op->args[pi++] = (uintptr_t)info; tcg_debug_assert(pi == total_args); @@ -2345,56 +2346,58 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) } } -void tcg_gen_call0(TCGHelperInfo *info, TCGTemp *ret) +void tcg_gen_call0(void *func, TCGHelperInfo *info, TCGTemp *ret) { - tcg_gen_callN(info, ret, NULL); + tcg_gen_callN(func, info, ret, NULL); } -void tcg_gen_call1(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) +void tcg_gen_call1(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) { - tcg_gen_callN(info, ret, &t1); + tcg_gen_callN(func, info, ret, &t1); } -void tcg_gen_call2(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2) +void tcg_gen_call2(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2) { TCGTemp *args[2] = { t1, t2 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call3(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, - TCGTemp *t2, TCGTemp *t3) +void tcg_gen_call3(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2, TCGTemp *t3) { TCGTemp *args[3] = { t1, t2, t3 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call4(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, - TCGTemp *t2, TCGTemp *t3, TCGTemp *t4) +void tcg_gen_call4(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2, TCGTemp *t3, TCGTemp *t4) { TCGTemp *args[4] = { t1, t2, t3, t4 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call5(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, +void tcg_gen_call5(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, TCGTemp *t5) { TCGTemp *args[5] = { t1, t2, t3, t4, t5 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call6(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2, - TCGTemp *t3, TCGTemp *t4, TCGTemp *t5, TCGTemp *t6) +void tcg_gen_call6(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2, TCGTemp *t3, + TCGTemp *t4, TCGTemp *t5, TCGTemp *t6) { TCGTemp *args[6] = { t1, t2, t3, t4, t5, t6 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call7(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, +void tcg_gen_call7(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, TCGTemp *t5, TCGTemp *t6, TCGTemp *t7) { TCGTemp *args[7] = { t1, t2, t3, t4, t5, t6, t7 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } static void tcg_reg_alloc_start(TCGContext *s) From 25875fe92eb55e905655dcdf5f06f89ef2c1f404 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 09:07:46 -1000 Subject: [PATCH 0325/2884] plugins: Zero new qemu_plugin_dyn_cb entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- plugins/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/core.c b/plugins/core.c index 11ca20e626..4487cb7c48 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -307,7 +307,7 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) GArray *cbs = *arr; if (!cbs) { - cbs = g_array_sized_new(false, false, + cbs = g_array_sized_new(false, true, sizeof(struct qemu_plugin_dyn_cb), 1); *arr = cbs; } From aff56de576c949880d674d37b82bdc97841107fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 14:09:33 -1000 Subject: [PATCH 0326/2884] plugins: Move function pointer in qemu_plugin_dyn_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The out-of-line function pointer is mutually exclusive with inline expansion, so move it into the union. Wrap the pointer in a structure named 'regular' to match PLUGIN_CB_REGULAR. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 4 ++-- include/qemu/plugin.h | 4 +++- plugins/core.c | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index cd78ef94a1..4b488943ff 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -425,7 +425,7 @@ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, } /* call */ - op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx); + op = copy_call(&begin_op, op, cb->regular.f.vcpu_udata, cb_idx); return op; } @@ -473,7 +473,7 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, if (type == PLUGIN_GEN_CB_MEM) { /* call */ - op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx); + op = copy_call(&begin_op, op, cb->regular.f.vcpu_udata, cb_idx); } return op; diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 41db748eda..5676ab5ef2 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -85,13 +85,15 @@ enum plugin_dyn_cb_subtype { * instance of a callback to be called upon the execution of a particular TB. */ struct qemu_plugin_dyn_cb { - union qemu_plugin_cb_sig f; void *userp; enum plugin_dyn_cb_subtype type; /* @rw applies to mem callbacks only (both regular and inline) */ enum qemu_plugin_mem_rw rw; /* fields specific to each dyn_cb type go here */ union { + struct { + union qemu_plugin_cb_sig f; + } regular; struct { qemu_plugin_u64 entry; enum qemu_plugin_op op; diff --git a/plugins/core.c b/plugins/core.c index 4487cb7c48..837c373690 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -342,7 +342,7 @@ void plugin_register_dyn_cb__udata(GArray **arr, dyn_cb->userp = udata; /* Note flags are discarded as unused. */ - dyn_cb->f.vcpu_udata = cb; + dyn_cb->regular.f.vcpu_udata = cb; dyn_cb->type = PLUGIN_CB_REGULAR; } @@ -359,7 +359,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr, /* Note flags are discarded as unused. */ dyn_cb->type = PLUGIN_CB_REGULAR; dyn_cb->rw = rw; - dyn_cb->f.generic = cb; + dyn_cb->regular.f.vcpu_mem = cb; } /* @@ -511,8 +511,8 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, } switch (cb->type) { case PLUGIN_CB_REGULAR: - cb->f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), - vaddr, cb->userp); + cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), + vaddr, cb->userp); break; case PLUGIN_CB_INLINE: exec_inline_op(cb, cpu->cpu_index); From c7ba94836aa0665a931250e8f03f4aabce3c31f6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 09:09:11 -1000 Subject: [PATCH 0327/2884] plugins: Create TCGHelperInfo for all out-of-line callbacks TCGHelperInfo includes the ABI for every function call. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/qemu/plugin.h | 1 + plugins/core.c | 51 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 5676ab5ef2..d0d830bfc0 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -93,6 +93,7 @@ struct qemu_plugin_dyn_cb { union { struct { union qemu_plugin_cb_sig f; + TCGHelperInfo *info; } regular; struct { qemu_plugin_u64 entry; diff --git a/plugins/core.c b/plugins/core.c index 837c373690..b0a2e80874 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -338,12 +338,26 @@ void plugin_register_dyn_cb__udata(GArray **arr, enum qemu_plugin_cb_flags flags, void *udata) { - struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); + static TCGHelperInfo info[3] = { + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN, + [QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN, + /* + * Match qemu_plugin_vcpu_udata_cb_t: + * void (*)(uint32_t, void *) + */ + [0 ... 2].typemask = (dh_typemask(void, 0) | + dh_typemask(i32, 1) | + dh_typemask(ptr, 2)) + }; + struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); dyn_cb->userp = udata; - /* Note flags are discarded as unused. */ - dyn_cb->regular.f.vcpu_udata = cb; dyn_cb->type = PLUGIN_CB_REGULAR; + dyn_cb->regular.f.vcpu_udata = cb; + + assert((unsigned)flags < ARRAY_SIZE(info)); + dyn_cb->regular.info = &info[flags]; } void plugin_register_vcpu_mem_cb(GArray **arr, @@ -352,14 +366,39 @@ void plugin_register_vcpu_mem_cb(GArray **arr, enum qemu_plugin_mem_rw rw, void *udata) { - struct qemu_plugin_dyn_cb *dyn_cb; + /* + * Expect that the underlying type for enum qemu_plugin_meminfo_t + * is either int32_t or uint32_t, aka int or unsigned int. + */ + QEMU_BUILD_BUG_ON( + !__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) && + !__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t)); - dyn_cb = plugin_get_dyn_cb(arr); + static TCGHelperInfo info[3] = { + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN, + [QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN, + /* + * Match qemu_plugin_vcpu_mem_cb_t: + * void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *) + */ + [0 ... 2].typemask = + (dh_typemask(void, 0) | + dh_typemask(i32, 1) | + (__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) + ? dh_typemask(i32, 2) : dh_typemask(s32, 2)) | + dh_typemask(i64, 3) | + dh_typemask(ptr, 4)) + }; + + struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); dyn_cb->userp = udata; - /* Note flags are discarded as unused. */ dyn_cb->type = PLUGIN_CB_REGULAR; dyn_cb->rw = rw; dyn_cb->regular.f.vcpu_mem = cb; + + assert((unsigned)flags < ARRAY_SIZE(info)); + dyn_cb->regular.info = &info[flags]; } /* From a0948bb78c9bd883d965aac3853e5d61f03e224b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 07:09:57 -1000 Subject: [PATCH 0328/2884] plugins: Use emit_before_op for PLUGIN_GEN_AFTER_INSN Introduce a new plugin_cb op and migrate one operation. By using emit_before_op, we do not need to emit opcodes early and modify them later -- we can simply emit the final set of opcodes once. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 74 +++++++++++++++++++++---------------- include/tcg/tcg-op-common.h | 1 + include/tcg/tcg-opc.h | 1 + tcg/tcg-op.c | 5 +++ 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 4b488943ff..4b02c0bfbf 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -201,8 +201,7 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from) { switch (from) { case PLUGIN_GEN_AFTER_INSN: - gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER, - gen_empty_mem_helper); + tcg_gen_plugin_cb(from); break; case PLUGIN_GEN_FROM_INSN: /* @@ -608,16 +607,6 @@ static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb, inject_mem_helper(begin_op, arr); } -static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn, - TCGOp *begin_op) -{ - if (likely(!plugin_insn->mem_helper)) { - rm_ops(begin_op); - return; - } - inject_mem_helper(begin_op, NULL); -} - /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ void plugin_gen_disable_mem_helpers(void) { @@ -703,11 +692,14 @@ static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb, inject_mem_enable_helper(ptb, insn, begin_op); } -static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) +static void gen_disable_mem_helper(struct qemu_plugin_tb *ptb, + struct qemu_plugin_insn *insn) { - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - inject_mem_disable_helper(insn, begin_op); + if (insn->mem_helper) { + tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, + offsetof(CPUState, plugin_mem_cbs) - + offsetof(ArchCPU, env)); + } } /* #define DEBUG_PLUGIN_GEN_OPS */ @@ -766,16 +758,49 @@ static void pr_ops(void) static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { - TCGOp *op; + TCGOp *op, *next; int insn_idx = -1; pr_ops(); - QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { + /* + * While injecting code, we cannot afford to reuse any ebb temps + * that might be live within the existing opcode stream. + * The simplest solution is to release them all and create new. + */ + memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps)); + + QTAILQ_FOREACH_SAFE(op, &tcg_ctx->ops, link, next) { switch (op->opc) { case INDEX_op_insn_start: insn_idx++; break; + + case INDEX_op_plugin_cb: + { + enum plugin_gen_from from = op->args[0]; + struct qemu_plugin_insn *insn = NULL; + + if (insn_idx >= 0) { + insn = g_ptr_array_index(plugin_tb->insns, insn_idx); + } + + tcg_ctx->emit_before_op = op; + + switch (from) { + case PLUGIN_GEN_AFTER_INSN: + assert(insn != NULL); + gen_disable_mem_helper(plugin_tb, insn); + break; + default: + g_assert_not_reached(); + } + + tcg_ctx->emit_before_op = NULL; + tcg_op_remove(tcg_ctx, op); + break; + } + case INDEX_op_plugin_cb_start: { enum plugin_gen_from from = op->args[0]; @@ -840,19 +865,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) break; } - case PLUGIN_GEN_AFTER_INSN: - { - g_assert(insn_idx >= 0); - - switch (type) { - case PLUGIN_GEN_DISABLE_MEM_HELPER: - plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx); - break; - default: - g_assert_not_reached(); - } - break; - } default: g_assert_not_reached(); } diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 2d932a515e..9de5a7f280 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -74,6 +74,7 @@ void tcg_gen_goto_tb(unsigned idx); */ void tcg_gen_lookup_and_goto_ptr(void); +void tcg_gen_plugin_cb(unsigned from); void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr); void tcg_gen_plugin_cb_end(void); diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index b80227fa1c..3b7cb2bce1 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -197,6 +197,7 @@ DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) +DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT) DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index aa6bc6f57d..0f2026c91c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -312,6 +312,11 @@ void tcg_gen_mb(TCGBar mb_type) } } +void tcg_gen_plugin_cb(unsigned from) +{ + tcg_gen_op1(INDEX_op_plugin_cb, from); +} + void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr) { tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); From 21a3f62ff2b40a7a2abbd614b44fe5da461c3fd7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 10:18:00 -1000 Subject: [PATCH 0329/2884] plugins: Use emit_before_op for PLUGIN_GEN_FROM_TB By having the qemu_plugin_cb_flags be recorded in the TCGHelperInfo, we no longer need to distinguish PLUGIN_CB_REGULAR from PLUGIN_CB_REGULAR_R, so place all TB callbacks in the same queue. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 96 +++++++++++++++++++++++++----------------- plugins/api.c | 6 +-- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 4b02c0bfbf..c803fe8e96 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -201,6 +201,7 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from) { switch (from) { case PLUGIN_GEN_AFTER_INSN: + case PLUGIN_GEN_FROM_TB: tcg_gen_plugin_cb(from); break; case PLUGIN_GEN_FROM_INSN: @@ -210,8 +211,6 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from) */ gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER, gen_empty_mem_helper); - /* fall through */ - case PLUGIN_GEN_FROM_TB: gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg); gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg); gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb); @@ -626,24 +625,6 @@ void plugin_gen_disable_mem_helpers(void) offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); } -static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op) -{ - inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op); -} - -static void plugin_gen_tb_udata_r(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op) -{ - inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR_R], begin_op); -} - -static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op) -{ - inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok); -} - static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { @@ -702,6 +683,41 @@ static void gen_disable_mem_helper(struct qemu_plugin_tb *ptb, } } +static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb) +{ + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL, + tcgv_i32_temp(cpu_index), + tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_temp_free_i32(cpu_index); +} + +static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb) +{ + GArray *arr = cb->inline_insn.entry.score->data; + size_t offset = cb->inline_insn.entry.offset; + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); + + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr)); + tcg_gen_ext_i32_ptr(ptr, cpu_index); + tcg_temp_free_i32(cpu_index); + + tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data); + tcg_gen_ld_i64(val, ptr, offset); + tcg_gen_addi_i64(val, val, cb->inline_insn.imm); + tcg_gen_st_i64(val, ptr, offset); + + tcg_temp_free_i64(val); + tcg_temp_free_ptr(ptr); +} + /* #define DEBUG_PLUGIN_GEN_OPS */ static void pr_ops(void) { @@ -780,6 +796,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { enum plugin_gen_from from = op->args[0]; struct qemu_plugin_insn *insn = NULL; + const GArray *cbs; + int i, n; if (insn_idx >= 0) { insn = g_ptr_array_index(plugin_tb->insns, insn_idx); @@ -792,6 +810,25 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) assert(insn != NULL); gen_disable_mem_helper(plugin_tb, insn); break; + + case PLUGIN_GEN_FROM_TB: + assert(insn == NULL); + + cbs = plugin_tb->cbs[PLUGIN_CB_REGULAR]; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + struct qemu_plugin_dyn_cb *cb = + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); + gen_udata_cb(cb); + } + + cbs = plugin_tb->cbs[PLUGIN_CB_INLINE]; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + struct qemu_plugin_dyn_cb *cb = + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); + gen_inline_cb(cb); + } + break; + default: g_assert_not_reached(); } @@ -807,25 +844,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) enum plugin_gen_cb type = op->args[1]; switch (from) { - case PLUGIN_GEN_FROM_TB: - { - g_assert(insn_idx == -1); - - switch (type) { - case PLUGIN_GEN_CB_UDATA: - plugin_gen_tb_udata(plugin_tb, op); - break; - case PLUGIN_GEN_CB_UDATA_R: - plugin_gen_tb_udata_r(plugin_tb, op); - break; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_tb_inline(plugin_tb, op); - break; - default: - g_assert_not_reached(); - } - break; - } case PLUGIN_GEN_FROM_INSN: { g_assert(insn_idx >= 0); diff --git a/plugins/api.c b/plugins/api.c index 8fa5a600ac..5d119e8049 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -92,11 +92,7 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, void *udata) { if (!tb->mem_only) { - int index = flags == QEMU_PLUGIN_CB_R_REGS || - flags == QEMU_PLUGIN_CB_RW_REGS ? - PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR; - - plugin_register_dyn_cb__udata(&tb->cbs[index], + plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], cb, flags, udata); } } From 74bb8acc6a0c465eaf3a5a7d8b9fa5250a9243c7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 23:47:07 -1000 Subject: [PATCH 0330/2884] plugins: Add PLUGIN_GEN_AFTER_TB Delay test of plugin_tb->mem_helper until the inject pass. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index c803fe8e96..1faa49cb8f 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -69,6 +69,7 @@ enum plugin_gen_from { PLUGIN_GEN_FROM_INSN, PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_AFTER_INSN, + PLUGIN_GEN_AFTER_TB, PLUGIN_GEN_N_FROMS, }; @@ -609,20 +610,9 @@ static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb, /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ void plugin_gen_disable_mem_helpers(void) { - /* - * We could emit the clearing unconditionally and be done. However, this can - * be wasteful if for instance plugins don't track memory accesses, or if - * most TBs don't use helpers. Instead, emit the clearing iff the TB calls - * helpers that might access guest memory. - * - * Note: we do not reset plugin_tb->mem_helper here; a TB might have several - * exit points, and we want to emit the clearing from all of them. - */ - if (!tcg_ctx->plugin_tb->mem_helper) { - return; + if (tcg_ctx->plugin_insn) { + tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB); } - tcg_gen_st_ptr(tcg_constant_ptr(NULL), tcg_env, - offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); } static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb, @@ -673,14 +663,11 @@ static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb, inject_mem_enable_helper(ptb, insn, begin_op); } -static void gen_disable_mem_helper(struct qemu_plugin_tb *ptb, - struct qemu_plugin_insn *insn) +static void gen_disable_mem_helper(void) { - if (insn->mem_helper) { - tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, - offsetof(CPUState, plugin_mem_cbs) - - offsetof(ArchCPU, env)); - } + tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, + offsetof(CPUState, plugin_mem_cbs) - + offsetof(ArchCPU, env)); } static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb) @@ -806,9 +793,17 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) tcg_ctx->emit_before_op = op; switch (from) { + case PLUGIN_GEN_AFTER_TB: + if (plugin_tb->mem_helper) { + gen_disable_mem_helper(); + } + break; + case PLUGIN_GEN_AFTER_INSN: assert(insn != NULL); - gen_disable_mem_helper(plugin_tb, insn); + if (insn->mem_helper) { + gen_disable_mem_helper(); + } break; case PLUGIN_GEN_FROM_TB: From ac977170bf1e89fce25197ad54f04d9ec1f6a2b6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 11:27:35 -1000 Subject: [PATCH 0331/2884] plugins: Use emit_before_op for PLUGIN_GEN_FROM_INSN Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 286 ++++++++++------------------------------- include/qemu/plugin.h | 1 - plugins/api.c | 8 +- 3 files changed, 67 insertions(+), 228 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 1faa49cb8f..a3dd82df4b 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -98,30 +98,6 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, void *userdata) { } -static void gen_empty_udata_cb(void (*gen_helper)(TCGv_i32, TCGv_ptr)) -{ - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - TCGv_ptr udata = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_ptr(udata, 0); - tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - gen_helper(cpu_index, udata); - - tcg_temp_free_ptr(udata); - tcg_temp_free_i32(cpu_index); -} - -static void gen_empty_udata_cb_no_wg(void) -{ - gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_wg); -} - -static void gen_empty_udata_cb_no_rwg(void) -{ - gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_rwg); -} - /* * For now we only support addi_i64. * When we support more ops, we can generate one empty inline cb for each. @@ -170,51 +146,19 @@ static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info) tcg_temp_free_i32(cpu_index); } -/* - * Share the same function for enable/disable. When enabling, the NULL - * pointer will be overwritten later. - */ -static void gen_empty_mem_helper(void) -{ - TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_ptr(ptr, 0); - tcg_gen_st_ptr(ptr, tcg_env, offsetof(CPUState, plugin_mem_cbs) - - offsetof(ArchCPU, env)); - tcg_temp_free_ptr(ptr); -} - static void gen_plugin_cb_start(enum plugin_gen_from from, enum plugin_gen_cb type, unsigned wr) { tcg_gen_plugin_cb_start(from, type, wr); } -static void gen_wrapped(enum plugin_gen_from from, - enum plugin_gen_cb type, void (*func)(void)) -{ - gen_plugin_cb_start(from, type, 0); - func(); - tcg_gen_plugin_cb_end(); -} - static void plugin_gen_empty_callback(enum plugin_gen_from from) { switch (from) { case PLUGIN_GEN_AFTER_INSN: case PLUGIN_GEN_FROM_TB: - tcg_gen_plugin_cb(from); - break; case PLUGIN_GEN_FROM_INSN: - /* - * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being - * the first callback of an instruction - */ - gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER, - gen_empty_mem_helper); - gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg); - gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg); - gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb); + tcg_gen_plugin_cb(from); break; default: g_assert_not_reached(); @@ -368,18 +312,6 @@ static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v) return op; } -static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) -{ - if (UINTPTR_MAX == UINT32_MAX) { - /* st_i32 */ - op = copy_op(begin_op, op, INDEX_op_st_i32); - } else { - /* st_i64 */ - op = copy_st_i64(begin_op, op); - } - return op; -} - static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx) { TCGOp *old_op; @@ -403,32 +335,6 @@ static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx) return op; } -/* - * When we append/replace ops here we are sensitive to changing patterns of - * TCGOps generated by the tcg_gen_FOO calls when we generated the - * empty callbacks. This will assert very quickly in a debug build as - * we assert the ops we are replacing are the correct ones. - */ -static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *cb_idx) -{ - /* const_ptr */ - op = copy_const_ptr(&begin_op, op, cb->userp); - - /* copy the ld_i32, but note that we only have to copy it once */ - if (*cb_idx == -1) { - op = copy_op(&begin_op, op, INDEX_op_ld_i32); - } else { - begin_op = QTAILQ_NEXT(begin_op, link); - tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); - } - - /* call */ - op = copy_call(&begin_op, op, cb->regular.f.vcpu_udata, cb_idx); - - return op; -} - static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, TCGOp *begin_op, TCGOp *op, int *unused) @@ -482,11 +388,6 @@ typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb, TCGOp *begin_op, TCGOp *op, int *intp); typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb); -static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) -{ - return true; -} - static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) { int w; @@ -524,12 +425,6 @@ static void inject_cb_type(const GArray *cbs, TCGOp *begin_op, rm_ops_range(begin_op, end_op); } -static void -inject_udata_cb(const GArray *cbs, TCGOp *begin_op) -{ - inject_cb_type(cbs, begin_op, append_udata_cb, op_ok); -} - static void inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok) { @@ -542,71 +437,6 @@ inject_mem_cb(const GArray *cbs, TCGOp *begin_op) inject_cb_type(cbs, begin_op, append_mem_cb, op_rw); } -/* we could change the ops in place, but we can reuse more code by copying */ -static void inject_mem_helper(TCGOp *begin_op, GArray *arr) -{ - TCGOp *orig_op = begin_op; - TCGOp *end_op; - TCGOp *op; - - end_op = find_op(begin_op, INDEX_op_plugin_cb_end); - tcg_debug_assert(end_op); - - /* const ptr */ - op = copy_const_ptr(&begin_op, end_op, arr); - - /* st_ptr */ - op = copy_st_ptr(&begin_op, op); - - rm_ops_range(orig_op, end_op); -} - -/* - * Tracking memory accesses performed from helpers requires extra work. - * If an instruction is emulated with helpers, we do two things: - * (1) copy the CB descriptors, and keep track of it so that they can be - * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so - * that we can read them at run-time (i.e. when the helper executes). - * This run-time access is performed from qemu_plugin_vcpu_mem_cb. - * - * Note that plugin_gen_disable_mem_helpers undoes (2). Since it - * is possible that the code we generate after the instruction is - * dead, we also add checks before generating tb_exit etc. - */ -static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb, - struct qemu_plugin_insn *plugin_insn, - TCGOp *begin_op) -{ - GArray *cbs[2]; - GArray *arr; - size_t n_cbs, i; - - cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; - cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; - - n_cbs = 0; - for (i = 0; i < ARRAY_SIZE(cbs); i++) { - n_cbs += cbs[i]->len; - } - - plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs; - if (likely(!plugin_insn->mem_helper)) { - rm_ops(begin_op); - return; - } - ptb->mem_helper = true; - - arr = g_array_sized_new(false, false, - sizeof(struct qemu_plugin_dyn_cb), n_cbs); - - for (i = 0; i < ARRAY_SIZE(cbs); i++) { - g_array_append_vals(arr, cbs[i]->data, cbs[i]->len); - } - - qemu_plugin_add_dyn_cb_arr(arr); - inject_mem_helper(begin_op, arr); -} - /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ void plugin_gen_disable_mem_helpers(void) { @@ -615,30 +445,6 @@ void plugin_gen_disable_mem_helpers(void) } } -static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) -{ - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - - inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op); -} - -static void plugin_gen_insn_udata_r(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) -{ - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - - inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR_R], begin_op); -} - -static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) -{ - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], - begin_op, op_ok); -} - static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { @@ -656,11 +462,51 @@ static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, inject_inline_cb(cbs, begin_op, op_rw); } -static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) +static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, + struct qemu_plugin_insn *insn) { - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - inject_mem_enable_helper(ptb, insn, begin_op); + GArray *cbs[2]; + GArray *arr; + size_t n_cbs; + + /* + * Tracking memory accesses performed from helpers requires extra work. + * If an instruction is emulated with helpers, we do two things: + * (1) copy the CB descriptors, and keep track of it so that they can be + * freed later on, and (2) point CPUState.plugin_mem_cbs to the + * descriptors, so that we can read them at run-time + * (i.e. when the helper executes). + * This run-time access is performed from qemu_plugin_vcpu_mem_cb. + * + * Note that plugin_gen_disable_mem_helpers undoes (2). Since it + * is possible that the code we generate after the instruction is + * dead, we also add checks before generating tb_exit etc. + */ + if (!insn->calls_helpers) { + return; + } + + cbs[0] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; + cbs[1] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; + n_cbs = cbs[0]->len + cbs[1]->len; + + if (n_cbs == 0) { + insn->mem_helper = false; + return; + } + insn->mem_helper = true; + ptb->mem_helper = true; + + arr = g_array_sized_new(false, false, + sizeof(struct qemu_plugin_dyn_cb), n_cbs); + g_array_append_vals(arr, cbs[0]->data, cbs[0]->len); + g_array_append_vals(arr, cbs[1]->data, cbs[1]->len); + + qemu_plugin_add_dyn_cb_arr(arr); + + tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, + offsetof(CPUState, plugin_mem_cbs) - + offsetof(ArchCPU, env)); } static void gen_disable_mem_helper(void) @@ -824,6 +670,26 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) } break; + case PLUGIN_GEN_FROM_INSN: + assert(insn != NULL); + + gen_enable_mem_helper(plugin_tb, insn); + + cbs = insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR]; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + struct qemu_plugin_dyn_cb *cb = + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); + gen_udata_cb(cb); + } + + cbs = insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE]; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + struct qemu_plugin_dyn_cb *cb = + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); + gen_inline_cb(cb); + } + break; + default: g_assert_not_reached(); } @@ -839,28 +705,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) enum plugin_gen_cb type = op->args[1]; switch (from) { - case PLUGIN_GEN_FROM_INSN: - { - g_assert(insn_idx >= 0); - - switch (type) { - case PLUGIN_GEN_CB_UDATA: - plugin_gen_insn_udata(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_CB_UDATA_R: - plugin_gen_insn_udata_r(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_insn_inline(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_ENABLE_MEM_HELPER: - plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx); - break; - default: - g_assert_not_reached(); - } - break; - } case PLUGIN_GEN_FROM_MEM: { g_assert(insn_idx >= 0); diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index d0d830bfc0..0d0062448b 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -74,7 +74,6 @@ enum plugin_dyn_cb_type { enum plugin_dyn_cb_subtype { PLUGIN_CB_REGULAR, - PLUGIN_CB_REGULAR_R, PLUGIN_CB_INLINE, PLUGIN_N_CB_SUBTYPES, }; diff --git a/plugins/api.c b/plugins/api.c index 5d119e8049..29cce2d97c 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -115,12 +115,8 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, void *udata) { if (!insn->mem_only) { - int index = flags == QEMU_PLUGIN_CB_R_REGS || - flags == QEMU_PLUGIN_CB_RW_REGS ? - PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR; - - plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][index], - cb, flags, udata); + plugin_register_dyn_cb__udata( + &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], cb, flags, udata); } } From 8a2927f290fb6c3ba51dfd6465e4ea51a3c9e1a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 13:02:32 -1000 Subject: [PATCH 0332/2884] plugins: Use emit_before_op for PLUGIN_GEN_FROM_MEM Introduce a new plugin_mem_cb op to hold the address temp and meminfo computed by tcg-op-ldst.c. Because this now has its own opcode, we no longer need PLUGIN_GEN_FROM_MEM. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 408 ++++-------------------------------- include/exec/plugin-gen.h | 4 - include/tcg/tcg-op-common.h | 1 + include/tcg/tcg-opc.h | 1 + tcg/tcg-op-ldst.c | 6 +- tcg/tcg-op.c | 5 + 6 files changed, 54 insertions(+), 371 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index a3dd82df4b..8f8ae156b6 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -67,7 +67,6 @@ enum plugin_gen_from { PLUGIN_GEN_FROM_TB, PLUGIN_GEN_FROM_INSN, - PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_AFTER_INSN, PLUGIN_GEN_AFTER_TB, PLUGIN_GEN_N_FROMS, @@ -98,60 +97,6 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, void *userdata) { } -/* - * For now we only support addi_i64. - * When we support more ops, we can generate one empty inline cb for each. - */ -static void gen_empty_inline_cb(void) -{ - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr(); - TCGv_i64 val = tcg_temp_ebb_new_i64(); - TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); - - tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - /* second operand will be replaced by immediate value */ - tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index); - tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index); - - tcg_gen_movi_ptr(ptr, 0); - tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr); - tcg_gen_ld_i64(val, ptr, 0); - /* second operand will be replaced by immediate value */ - tcg_gen_add_i64(val, val, val); - - tcg_gen_st_i64(val, ptr, 0); - tcg_temp_free_ptr(ptr); - tcg_temp_free_i64(val); - tcg_temp_free_ptr(cpu_index_as_ptr); - tcg_temp_free_i32(cpu_index); -} - -static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info) -{ - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - TCGv_i32 meminfo = tcg_temp_ebb_new_i32(); - TCGv_ptr udata = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_i32(meminfo, info); - tcg_gen_movi_ptr(udata, 0); - tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - - gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata); - - tcg_temp_free_ptr(udata); - tcg_temp_free_i32(meminfo); - tcg_temp_free_i32(cpu_index); -} - -static void gen_plugin_cb_start(enum plugin_gen_from from, - enum plugin_gen_cb type, unsigned wr) -{ - tcg_gen_plugin_cb_start(from, type, wr); -} - static void plugin_gen_empty_callback(enum plugin_gen_from from) { switch (from) { @@ -165,278 +110,6 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from) } } -void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) -{ - enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); - - gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw); - gen_empty_mem_cb(addr, info); - tcg_gen_plugin_cb_end(); - - gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw); - gen_empty_inline_cb(); - tcg_gen_plugin_cb_end(); -} - -static TCGOp *find_op(TCGOp *op, TCGOpcode opc) -{ - while (op) { - if (op->opc == opc) { - return op; - } - op = QTAILQ_NEXT(op, link); - } - return NULL; -} - -static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end) -{ - TCGOp *ret = QTAILQ_NEXT(end, link); - - QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link); - return ret; -} - -/* remove all ops until (and including) plugin_cb_end */ -static TCGOp *rm_ops(TCGOp *op) -{ - TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end); - - tcg_debug_assert(end_op); - return rm_ops_range(op, end_op); -} - -static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op) -{ - TCGOp *old_op = QTAILQ_NEXT(*begin_op, link); - unsigned nargs = old_op->nargs; - - *begin_op = old_op; - op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs); - memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs); - - return op; -} - -static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc) -{ - op = copy_op_nocheck(begin_op, op); - tcg_debug_assert((*begin_op)->opc == opc); - return op; -} - -static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) -{ - if (UINTPTR_MAX == UINT32_MAX) { - /* mov_i32 */ - op = copy_op(begin_op, op, INDEX_op_mov_i32); - op->args[1] = tcgv_i32_arg(tcg_constant_i32((uintptr_t)ptr)); - } else { - /* mov_i64 */ - op = copy_op(begin_op, op, INDEX_op_mov_i64); - op->args[1] = tcgv_i64_arg(tcg_constant_i64((uintptr_t)ptr)); - } - return op; -} - -static TCGOp *copy_ld_i32(TCGOp **begin_op, TCGOp *op) -{ - return copy_op(begin_op, op, INDEX_op_ld_i32); -} - -static TCGOp *copy_ext_i32_ptr(TCGOp **begin_op, TCGOp *op) -{ - if (UINTPTR_MAX == UINT32_MAX) { - op = copy_op(begin_op, op, INDEX_op_mov_i32); - } else { - op = copy_op(begin_op, op, INDEX_op_ext_i32_i64); - } - return op; -} - -static TCGOp *copy_add_ptr(TCGOp **begin_op, TCGOp *op) -{ - if (UINTPTR_MAX == UINT32_MAX) { - op = copy_op(begin_op, op, INDEX_op_add_i32); - } else { - op = copy_op(begin_op, op, INDEX_op_add_i64); - } - return op; -} - -static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* 2x ld_i32 */ - op = copy_ld_i32(begin_op, op); - op = copy_ld_i32(begin_op, op); - } else { - /* ld_i64 */ - op = copy_op(begin_op, op, INDEX_op_ld_i64); - } - return op; -} - -static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* 2x st_i32 */ - op = copy_op(begin_op, op, INDEX_op_st_i32); - op = copy_op(begin_op, op, INDEX_op_st_i32); - } else { - /* st_i64 */ - op = copy_op(begin_op, op, INDEX_op_st_i64); - } - return op; -} - -static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* all 32-bit backends must implement add2_i32 */ - g_assert(TCG_TARGET_HAS_add2_i32); - op = copy_op(begin_op, op, INDEX_op_add2_i32); - op->args[4] = tcgv_i32_arg(tcg_constant_i32(v)); - op->args[5] = tcgv_i32_arg(tcg_constant_i32(v >> 32)); - } else { - op = copy_op(begin_op, op, INDEX_op_add_i64); - op->args[2] = tcgv_i64_arg(tcg_constant_i64(v)); - } - return op; -} - -static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v) -{ - op = copy_op(begin_op, op, INDEX_op_mul_i32); - op->args[2] = tcgv_i32_arg(tcg_constant_i32(v)); - return op; -} - -static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx) -{ - TCGOp *old_op; - int func_idx; - - /* copy all ops until the call */ - do { - op = copy_op_nocheck(begin_op, op); - } while (op->opc != INDEX_op_call); - - /* fill in the op call */ - old_op = *begin_op; - TCGOP_CALLI(op) = TCGOP_CALLI(old_op); - TCGOP_CALLO(op) = TCGOP_CALLO(old_op); - tcg_debug_assert(op->life == 0); - - func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op); - *cb_idx = func_idx; - op->args[func_idx] = (uintptr_t)func; - - return op; -} - -static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, - int *unused) -{ - char *ptr = cb->inline_insn.entry.score->data->data; - size_t elem_size = g_array_get_element_size( - cb->inline_insn.entry.score->data); - size_t offset = cb->inline_insn.entry.offset; - - op = copy_ld_i32(&begin_op, op); - op = copy_mul_i32(&begin_op, op, elem_size); - op = copy_ext_i32_ptr(&begin_op, op); - op = copy_const_ptr(&begin_op, op, ptr + offset); - op = copy_add_ptr(&begin_op, op); - op = copy_ld_i64(&begin_op, op); - op = copy_add_i64(&begin_op, op, cb->inline_insn.imm); - op = copy_st_i64(&begin_op, op); - return op; -} - -static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *cb_idx) -{ - enum plugin_gen_cb type = begin_op->args[1]; - - tcg_debug_assert(type == PLUGIN_GEN_CB_MEM); - - /* const_i32 == mov_i32 ("info", so it remains as is) */ - op = copy_op(&begin_op, op, INDEX_op_mov_i32); - - /* const_ptr */ - op = copy_const_ptr(&begin_op, op, cb->userp); - - /* copy the ld_i32, but note that we only have to copy it once */ - if (*cb_idx == -1) { - op = copy_op(&begin_op, op, INDEX_op_ld_i32); - } else { - begin_op = QTAILQ_NEXT(begin_op, link); - tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); - } - - if (type == PLUGIN_GEN_CB_MEM) { - /* call */ - op = copy_call(&begin_op, op, cb->regular.f.vcpu_udata, cb_idx); - } - - return op; -} - -typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *intp); -typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb); - -static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) -{ - int w; - - w = op->args[2]; - return !!(cb->rw & (w + 1)); -} - -static void inject_cb_type(const GArray *cbs, TCGOp *begin_op, - inject_fn inject, op_ok_fn ok) -{ - TCGOp *end_op; - TCGOp *op; - int cb_idx = -1; - int i; - - if (!cbs || cbs->len == 0) { - rm_ops(begin_op); - return; - } - - end_op = find_op(begin_op, INDEX_op_plugin_cb_end); - tcg_debug_assert(end_op); - - op = end_op; - for (i = 0; i < cbs->len; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - - if (!ok(begin_op, cb)) { - continue; - } - op = inject(cb, begin_op, op, &cb_idx); - } - rm_ops_range(begin_op, end_op); -} - -static void -inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok) -{ - inject_cb_type(cbs, begin_op, append_inline_cb, ok); -} - -static void -inject_mem_cb(const GArray *cbs, TCGOp *begin_op) -{ - inject_cb_type(cbs, begin_op, append_mem_cb, op_rw); -} - /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ void plugin_gen_disable_mem_helpers(void) { @@ -445,23 +118,6 @@ void plugin_gen_disable_mem_helpers(void) } } -static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) -{ - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - inject_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op); -} - -static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) -{ - const GArray *cbs; - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - - cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; - inject_inline_cb(cbs, begin_op, op_rw); -} - static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, struct qemu_plugin_insn *insn) { @@ -551,6 +207,21 @@ static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb) tcg_temp_free_ptr(ptr); } +static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, + qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) +{ + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + tcg_gen_call4(cb->regular.f.vcpu_mem, cb->regular.info, NULL, + tcgv_i32_temp(cpu_index), + tcgv_i32_temp(tcg_constant_i32(meminfo)), + tcgv_i64_temp(addr), + tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_temp_free_i32(cpu_index); +} + /* #define DEBUG_PLUGIN_GEN_OPS */ static void pr_ops(void) { @@ -699,34 +370,43 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) break; } - case INDEX_op_plugin_cb_start: + case INDEX_op_plugin_mem_cb: { - enum plugin_gen_from from = op->args[0]; - enum plugin_gen_cb type = op->args[1]; + TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0])); + qemu_plugin_meminfo_t meminfo = op->args[1]; + struct qemu_plugin_insn *insn; + const GArray *cbs; + int i, n, rw; - switch (from) { - case PLUGIN_GEN_FROM_MEM: - { - g_assert(insn_idx >= 0); + assert(insn_idx >= 0); + insn = g_ptr_array_index(plugin_tb->insns, insn_idx); + rw = qemu_plugin_mem_is_store(meminfo) ? 2 : 1; - switch (type) { - case PLUGIN_GEN_CB_MEM: - plugin_gen_mem_regular(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_mem_inline(plugin_tb, op, insn_idx); - break; - default: - g_assert_not_reached(); + tcg_ctx->emit_before_op = op; + + cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + struct qemu_plugin_dyn_cb *cb = + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); + if (cb->rw & rw) { + gen_mem_cb(cb, meminfo, addr); } + } - break; - } - default: - g_assert_not_reached(); + cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + struct qemu_plugin_dyn_cb *cb = + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); + if (cb->rw & rw) { + gen_inline_cb(cb); + } } + + tcg_ctx->emit_before_op = NULL; + tcg_op_remove(tcg_ctx, op); break; } + default: /* plugins don't care about any other ops */ break; diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index c4552b5061..f333f33198 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -25,7 +25,6 @@ void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db); void plugin_gen_insn_end(void); void plugin_gen_disable_mem_helpers(void); -void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info); #else /* !CONFIG_PLUGIN */ @@ -48,9 +47,6 @@ static inline void plugin_gen_tb_end(CPUState *cpu, size_t num_insns) static inline void plugin_gen_disable_mem_helpers(void) { } -static inline void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) -{ } - #endif /* CONFIG_PLUGIN */ #endif /* QEMU_PLUGIN_GEN_H */ diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 9de5a7f280..72b80b20d0 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -75,6 +75,7 @@ void tcg_gen_goto_tb(unsigned idx); void tcg_gen_lookup_and_goto_ptr(void); void tcg_gen_plugin_cb(unsigned from); +void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo); void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr); void tcg_gen_plugin_cb_end(void); diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 3b7cb2bce1..be9e36e386 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -198,6 +198,7 @@ DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT) DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT) DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index f11043b449..8510160258 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -161,14 +161,14 @@ plugin_gen_mem_callbacks(TCGv_i64 copy_addr, TCGTemp *orig_addr, MemOpIdx oi, copy_addr = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(copy_addr, temp_tcgv_i32(orig_addr)); } - plugin_gen_empty_mem_callback(copy_addr, info); + tcg_gen_plugin_mem_cb(copy_addr, info); tcg_temp_free_i64(copy_addr); } else { if (copy_addr) { - plugin_gen_empty_mem_callback(copy_addr, info); + tcg_gen_plugin_mem_cb(copy_addr, info); tcg_temp_free_i64(copy_addr); } else { - plugin_gen_empty_mem_callback(temp_tcgv_i64(orig_addr), info); + tcg_gen_plugin_mem_cb(temp_tcgv_i64(orig_addr), info); } } } diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 0f2026c91c..0ae12fa49d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -317,6 +317,11 @@ void tcg_gen_plugin_cb(unsigned from) tcg_gen_op1(INDEX_op_plugin_cb, from); } +void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) +{ + tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo); +} + void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr) { tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); From 5f2a5a5b345b111c18525f23d9094b6410a8730e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 13:10:27 -1000 Subject: [PATCH 0333/2884] plugins: Remove plugin helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These placeholder helpers are no longer required. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 20 -------------------- accel/tcg/plugin-helpers.h | 5 ----- include/exec/helper-gen-common.h | 4 ---- include/exec/helper-proto-common.h | 4 ---- 4 files changed, 33 deletions(-) delete mode 100644 accel/tcg/plugin-helpers.h diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 8f8ae156b6..fb77585ac0 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -51,11 +51,6 @@ #include "exec/exec-all.h" #include "exec/plugin-gen.h" #include "exec/translator.h" -#include "exec/helper-proto-common.h" - -#define HELPER_H "accel/tcg/plugin-helpers.h" -#include "exec/helper-info.c.inc" -#undef HELPER_H /* * plugin_cb_start TCG op args[]: @@ -82,21 +77,6 @@ enum plugin_gen_cb { PLUGIN_GEN_N_CBS, }; -/* - * These helpers are stubs that get dynamically switched out for calls - * direct to the plugin if they are subscribed to. - */ -void HELPER(plugin_vcpu_udata_cb_no_wg)(uint32_t cpu_index, void *udata) -{ } - -void HELPER(plugin_vcpu_udata_cb_no_rwg)(uint32_t cpu_index, void *udata) -{ } - -void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, - qemu_plugin_meminfo_t info, uint64_t vaddr, - void *userdata) -{ } - static void plugin_gen_empty_callback(enum plugin_gen_from from) { switch (from) { diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h deleted file mode 100644 index 11796436f3..0000000000 --- a/accel/tcg/plugin-helpers.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef CONFIG_PLUGIN -DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_wg, TCG_CALL_NO_WG | TCG_CALL_PLUGIN, void, i32, ptr) -DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_rwg, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, ptr) -DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, i32, i64, ptr) -#endif diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h index 5d6d78a625..834590dc4e 100644 --- a/include/exec/helper-gen-common.h +++ b/include/exec/helper-gen-common.h @@ -11,8 +11,4 @@ #include "exec/helper-gen.h.inc" #undef HELPER_H -#define HELPER_H "accel/tcg/plugin-helpers.h" -#include "exec/helper-gen.h.inc" -#undef HELPER_H - #endif /* HELPER_GEN_COMMON_H */ diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h index 8b67170a22..16782ef46c 100644 --- a/include/exec/helper-proto-common.h +++ b/include/exec/helper-proto-common.h @@ -13,8 +13,4 @@ #include "exec/helper-proto.h.inc" #undef HELPER_H -#define HELPER_H "accel/tcg/plugin-helpers.h" -#include "exec/helper-proto.h.inc" -#undef HELPER_H - #endif /* HELPER_PROTO_COMMON_H */ From b0748975b8b4c3da0b4fce1f8d53b1b4ab422cd7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 22:07:07 -1000 Subject: [PATCH 0334/2884] tcg: Remove TCG_CALL_PLUGIN Since we no longer emit plugin helpers during the initial code translation phase, we don't need to specially mark plugin helpers. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 2 -- plugins/core.c | 10 ++++------ tcg/tcg.c | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 8d9f6585ff..196e3b7ba1 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -353,8 +353,6 @@ typedef TCGv_ptr TCGv_env; #define TCG_CALL_NO_SIDE_EFFECTS 0x0004 /* Helper is G_NORETURN. */ #define TCG_CALL_NO_RETURN 0x0008 -/* Helper is part of Plugins. */ -#define TCG_CALL_PLUGIN 0x0010 /* convenience version of most used call flags */ #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS diff --git a/plugins/core.c b/plugins/core.c index b0a2e80874..b0615f1e7f 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -339,9 +339,8 @@ void plugin_register_dyn_cb__udata(GArray **arr, void *udata) { static TCGHelperInfo info[3] = { - [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, - [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN, - [QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN, + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, /* * Match qemu_plugin_vcpu_udata_cb_t: * void (*)(uint32_t, void *) @@ -375,9 +374,8 @@ void plugin_register_vcpu_mem_cb(GArray **arr, !__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t)); static TCGHelperInfo info[3] = { - [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, - [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN, - [QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN, + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, /* * Match qemu_plugin_vcpu_mem_cb_t: * void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *) diff --git a/tcg/tcg.c b/tcg/tcg.c index 7484a07722..cfcf9122d6 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2269,9 +2269,7 @@ static void tcg_gen_callN(void *func, TCGHelperInfo *info, #ifdef CONFIG_PLUGIN /* Flag helpers that may affect guest state */ - if (tcg_ctx->plugin_insn && - !(info->flags & TCG_CALL_PLUGIN) && - !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) { + if (tcg_ctx->plugin_insn && !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) { tcg_ctx->plugin_insn->calls_helpers = true; } #endif From 36ab430645c99052d83ef94bbe2640193be047c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 15:03:09 -1000 Subject: [PATCH 0335/2884] tcg: Remove INDEX_op_plugin_cb_{start,end} These opcodes are no longer used. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 18 ------------------ include/tcg/tcg-op-common.h | 2 -- include/tcg/tcg-opc.h | 2 -- tcg/tcg-op.c | 10 ---------- 4 files changed, 32 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index fb77585ac0..d9ee9bb2ec 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -52,29 +52,11 @@ #include "exec/plugin-gen.h" #include "exec/translator.h" -/* - * plugin_cb_start TCG op args[]: - * 0: enum plugin_gen_from - * 1: enum plugin_gen_cb - * 2: set to 1 for mem callback that is a write, 0 otherwise. - */ - enum plugin_gen_from { PLUGIN_GEN_FROM_TB, PLUGIN_GEN_FROM_INSN, PLUGIN_GEN_AFTER_INSN, PLUGIN_GEN_AFTER_TB, - PLUGIN_GEN_N_FROMS, -}; - -enum plugin_gen_cb { - PLUGIN_GEN_CB_UDATA, - PLUGIN_GEN_CB_UDATA_R, - PLUGIN_GEN_CB_INLINE, - PLUGIN_GEN_CB_MEM, - PLUGIN_GEN_ENABLE_MEM_HELPER, - PLUGIN_GEN_DISABLE_MEM_HELPER, - PLUGIN_GEN_N_CBS, }; static void plugin_gen_empty_callback(enum plugin_gen_from from) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 72b80b20d0..009e2778c5 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -76,8 +76,6 @@ void tcg_gen_lookup_and_goto_ptr(void); void tcg_gen_plugin_cb(unsigned from); void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo); -void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr); -void tcg_gen_plugin_cb_end(void); /* 32 bit ops */ diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index be9e36e386..546eb49c11 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -199,8 +199,6 @@ DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT) -DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT) -DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) /* Replicate ld/st ops for 32 and 64-bit guest addresses. */ DEF(qemu_ld_a32_i32, 1, 1, 1, diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 0ae12fa49d..eff3728622 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -322,16 +322,6 @@ void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo); } -void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr) -{ - tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); -} - -void tcg_gen_plugin_cb_end(void) -{ - tcg_emit_op(INDEX_op_plugin_cb_end, 0); -} - /* 32 bit ops */ void tcg_gen_discard_i32(TCGv_i32 arg) From db409c01fde5be31ef34c69aa1d91880975e93c5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 16:02:19 -1000 Subject: [PATCH 0336/2884] plugins: Simplify callback queues We have qemu_plugin_dyn_cb.type to differentiate the various callback types, so we do not need to keep them in separate queues. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 90 ++++++++++++++++++++++-------------------- include/qemu/plugin.h | 35 ++++++---------- plugins/api.c | 18 +++------ 3 files changed, 65 insertions(+), 78 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index d9ee9bb2ec..e77ff2a565 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -83,9 +83,8 @@ void plugin_gen_disable_mem_helpers(void) static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, struct qemu_plugin_insn *insn) { - GArray *cbs[2]; GArray *arr; - size_t n_cbs; + size_t len; /* * Tracking memory accesses performed from helpers requires extra work. @@ -104,22 +103,25 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, return; } - cbs[0] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; - cbs[1] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; - n_cbs = cbs[0]->len + cbs[1]->len; - - if (n_cbs == 0) { + if (!insn->mem_cbs || !insn->mem_cbs->len) { insn->mem_helper = false; return; } insn->mem_helper = true; ptb->mem_helper = true; + /* + * TODO: It seems like we should be able to use ref/unref + * to avoid needing to actually copy this array. + * Alternately, perhaps we could allocate new memory adjacent + * to the TranslationBlock itself, so that we do not have to + * actively manage the lifetime after this. + */ + len = insn->mem_cbs->len; arr = g_array_sized_new(false, false, - sizeof(struct qemu_plugin_dyn_cb), n_cbs); - g_array_append_vals(arr, cbs[0]->data, cbs[0]->len); - g_array_append_vals(arr, cbs[1]->data, cbs[1]->len); - + sizeof(struct qemu_plugin_dyn_cb), len); + memcpy(arr->data, insn->mem_cbs->data, + len * sizeof(struct qemu_plugin_dyn_cb)); qemu_plugin_add_dyn_cb_arr(arr); tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, @@ -288,18 +290,21 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) case PLUGIN_GEN_FROM_TB: assert(insn == NULL); - cbs = plugin_tb->cbs[PLUGIN_CB_REGULAR]; + cbs = plugin_tb->cbs; for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { struct qemu_plugin_dyn_cb *cb = &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - gen_udata_cb(cb); - } - cbs = plugin_tb->cbs[PLUGIN_CB_INLINE]; - for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - gen_inline_cb(cb); + switch (cb->type) { + case PLUGIN_CB_REGULAR: + gen_udata_cb(cb); + break; + case PLUGIN_CB_INLINE: + gen_inline_cb(cb); + break; + default: + g_assert_not_reached(); + } } break; @@ -308,18 +313,21 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) gen_enable_mem_helper(plugin_tb, insn); - cbs = insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR]; + cbs = insn->insn_cbs; for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { struct qemu_plugin_dyn_cb *cb = &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - gen_udata_cb(cb); - } - cbs = insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE]; - for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - gen_inline_cb(cb); + switch (cb->type) { + case PLUGIN_CB_REGULAR: + gen_udata_cb(cb); + break; + case PLUGIN_CB_INLINE: + gen_inline_cb(cb); + break; + default: + g_assert_not_reached(); + } } break; @@ -346,21 +354,22 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) tcg_ctx->emit_before_op = op; - cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; + cbs = insn->mem_cbs; for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { struct qemu_plugin_dyn_cb *cb = &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - if (cb->rw & rw) { - gen_mem_cb(cb, meminfo, addr); - } - } - cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; - for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); if (cb->rw & rw) { - gen_inline_cb(cb); + switch (cb->type) { + case PLUGIN_CB_REGULAR: + gen_mem_cb(cb, meminfo, addr); + break; + case PLUGIN_CB_INLINE: + gen_inline_cb(cb); + break; + default: + g_assert_not_reached(); + } } } @@ -384,13 +393,10 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_state->event_mask)) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - int i; /* reset callbacks */ - for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { - if (ptb->cbs[i]) { - g_array_set_size(ptb->cbs[i], 0); - } + if (ptb->cbs) { + g_array_set_size(ptb->cbs, 0); } ptb->n = 0; diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 0d0062448b..b679a22bae 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -67,15 +67,8 @@ union qemu_plugin_cb_sig { }; enum plugin_dyn_cb_type { - PLUGIN_CB_INSN, - PLUGIN_CB_MEM, - PLUGIN_N_CB_TYPES, -}; - -enum plugin_dyn_cb_subtype { PLUGIN_CB_REGULAR, PLUGIN_CB_INLINE, - PLUGIN_N_CB_SUBTYPES, }; /* @@ -85,7 +78,7 @@ enum plugin_dyn_cb_subtype { */ struct qemu_plugin_dyn_cb { void *userp; - enum plugin_dyn_cb_subtype type; + enum plugin_dyn_cb_type type; /* @rw applies to mem callbacks only (both regular and inline) */ enum qemu_plugin_mem_rw rw; /* fields specific to each dyn_cb type go here */ @@ -107,7 +100,8 @@ struct qemu_plugin_insn { GByteArray *data; uint64_t vaddr; void *haddr; - GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES]; + GArray *insn_cbs; + GArray *mem_cbs; bool calls_helpers; /* if set, the instruction calls helpers that might access guest memory */ @@ -136,16 +130,9 @@ static inline void qemu_plugin_insn_cleanup_fn(gpointer data) static inline struct qemu_plugin_insn *qemu_plugin_insn_alloc(void) { - int i, j; struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1); - insn->data = g_byte_array_sized_new(4); - for (i = 0; i < PLUGIN_N_CB_TYPES; i++) { - for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) { - insn->cbs[i][j] = g_array_new(false, false, - sizeof(struct qemu_plugin_dyn_cb)); - } - } + insn->data = g_byte_array_sized_new(4); return insn; } @@ -162,7 +149,7 @@ struct qemu_plugin_tb { /* if set, the TB calls helpers that might access guest memory */ bool mem_helper; - GArray *cbs[PLUGIN_N_CB_SUBTYPES]; + GArray *cbs; }; /** @@ -175,22 +162,22 @@ struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb, uint64_t pc) { struct qemu_plugin_insn *insn; - int i, j; if (unlikely(tb->n == tb->insns->len)) { struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc(); g_ptr_array_add(tb->insns, new_insn); } + insn = g_ptr_array_index(tb->insns, tb->n++); g_byte_array_set_size(insn->data, 0); insn->calls_helpers = false; insn->mem_helper = false; insn->vaddr = pc; - - for (i = 0; i < PLUGIN_N_CB_TYPES; i++) { - for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) { - g_array_set_size(insn->cbs[i][j], 0); - } + if (insn->insn_cbs) { + g_array_set_size(insn->insn_cbs, 0); + } + if (insn->mem_cbs) { + g_array_set_size(insn->mem_cbs, 0); } return insn; diff --git a/plugins/api.c b/plugins/api.c index 29cce2d97c..3912c9cc8f 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -92,8 +92,7 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, void *udata) { if (!tb->mem_only) { - plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], - cb, flags, udata); + plugin_register_dyn_cb__udata(&tb->cbs, cb, flags, udata); } } @@ -104,8 +103,7 @@ void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( uint64_t imm) { if (!tb->mem_only) { - plugin_register_inline_op_on_entry( - &tb->cbs[PLUGIN_CB_INLINE], 0, op, entry, imm); + plugin_register_inline_op_on_entry(&tb->cbs, 0, op, entry, imm); } } @@ -115,8 +113,7 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, void *udata) { if (!insn->mem_only) { - plugin_register_dyn_cb__udata( - &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], cb, flags, udata); + plugin_register_dyn_cb__udata(&insn->insn_cbs, cb, flags, udata); } } @@ -127,8 +124,7 @@ void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( uint64_t imm) { if (!insn->mem_only) { - plugin_register_inline_op_on_entry( - &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], 0, op, entry, imm); + plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm); } } @@ -143,8 +139,7 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_mem_rw rw, void *udata) { - plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], - cb, flags, rw, udata); + plugin_register_vcpu_mem_cb(&insn->mem_cbs, cb, flags, rw, udata); } void qemu_plugin_register_vcpu_mem_inline_per_vcpu( @@ -154,8 +149,7 @@ void qemu_plugin_register_vcpu_mem_inline_per_vcpu( qemu_plugin_u64 entry, uint64_t imm) { - plugin_register_inline_op_on_entry( - &insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], rw, op, entry, imm); + plugin_register_inline_op_on_entry(&insn->mem_cbs, rw, op, entry, imm); } void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, From ccd8f17e02d70c4e9e1d7f3b8ee0f80dd58dc979 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Mar 2024 08:06:44 -1000 Subject: [PATCH 0337/2884] plugins: Introduce PLUGIN_CB_MEM_REGULAR Use different enumerators for vcpu_udata and vcpu_mem callbacks. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 2 +- include/qemu/plugin.h | 1 + plugins/core.c | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index e77ff2a565..c545303956 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -361,7 +361,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) if (cb->rw & rw) { switch (cb->type) { - case PLUGIN_CB_REGULAR: + case PLUGIN_CB_MEM_REGULAR: gen_mem_cb(cb, meminfo, addr); break; case PLUGIN_CB_INLINE: diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index b679a22bae..f5a08b1220 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -68,6 +68,7 @@ union qemu_plugin_cb_sig { enum plugin_dyn_cb_type { PLUGIN_CB_REGULAR, + PLUGIN_CB_MEM_REGULAR, PLUGIN_CB_INLINE, }; diff --git a/plugins/core.c b/plugins/core.c index b0615f1e7f..0213513ec6 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -391,7 +391,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr, struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); dyn_cb->userp = udata; - dyn_cb->type = PLUGIN_CB_REGULAR; + dyn_cb->type = PLUGIN_CB_MEM_REGULAR; dyn_cb->rw = rw; dyn_cb->regular.f.vcpu_mem = cb; @@ -547,7 +547,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, break; } switch (cb->type) { - case PLUGIN_CB_REGULAR: + case PLUGIN_CB_MEM_REGULAR: cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), vaddr, cb->userp); break; From b384c734ecb6d6a4f121d5716f7275e8e350a6fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Mar 2024 10:33:49 -1000 Subject: [PATCH 0338/2884] plugins: Replace pr_ops with a proper debug dump flag The DEBUG_PLUGIN_GEN_OPS ifdef is replaced with "-d op_plugin". The second pr_ops call can be obtained with "-d op". Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 67 +++++++----------------------------------- include/qemu/log.h | 1 + include/tcg/tcg.h | 1 + tcg/tcg.c | 29 +++++++++++++++++- util/log.c | 4 +++ 5 files changed, 45 insertions(+), 57 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index c545303956..49d9b07438 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -44,6 +44,7 @@ */ #include "qemu/osdep.h" #include "qemu/plugin.h" +#include "qemu/log.h" #include "cpu.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" @@ -186,66 +187,21 @@ static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, tcg_temp_free_i32(cpu_index); } -/* #define DEBUG_PLUGIN_GEN_OPS */ -static void pr_ops(void) -{ -#ifdef DEBUG_PLUGIN_GEN_OPS - TCGOp *op; - int i = 0; - - QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { - const char *name = ""; - const char *type = ""; - - if (op->opc == INDEX_op_plugin_cb_start) { - switch (op->args[0]) { - case PLUGIN_GEN_FROM_TB: - name = "tb"; - break; - case PLUGIN_GEN_FROM_INSN: - name = "insn"; - break; - case PLUGIN_GEN_FROM_MEM: - name = "mem"; - break; - case PLUGIN_GEN_AFTER_INSN: - name = "after insn"; - break; - default: - break; - } - switch (op->args[1]) { - case PLUGIN_GEN_CB_UDATA: - type = "udata"; - break; - case PLUGIN_GEN_CB_INLINE: - type = "inline"; - break; - case PLUGIN_GEN_CB_MEM: - type = "mem"; - break; - case PLUGIN_GEN_ENABLE_MEM_HELPER: - type = "enable mem helper"; - break; - case PLUGIN_GEN_DISABLE_MEM_HELPER: - type = "disable mem helper"; - break; - default: - break; - } - } - printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type); - i++; - } -#endif -} - static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { TCGOp *op, *next; int insn_idx = -1; - pr_ops(); + if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN) + && qemu_log_in_addr_range(plugin_tb->vaddr))) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "OP before plugin injection:\n"); + tcg_dump_ops(tcg_ctx, logfile, false); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); + } + } /* * While injecting code, we cannot afford to reuse any ebb temps @@ -383,7 +339,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) break; } } - pr_ops(); } bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, diff --git a/include/qemu/log.h b/include/qemu/log.h index df59bfabcd..e10e24cd4f 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -36,6 +36,7 @@ bool qemu_log_separate(void); #define LOG_STRACE (1 << 19) #define LOG_PER_THREAD (1 << 20) #define CPU_LOG_TB_VPU (1 << 21) +#define LOG_TB_OP_PLUGIN (1 << 22) /* Lock/unlock output. */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 196e3b7ba1..135e36d729 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -1070,5 +1070,6 @@ static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n) } bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned); +void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs); #endif /* TCG_H */ diff --git a/tcg/tcg.c b/tcg/tcg.c index cfcf9122d6..d827c6d431 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2540,6 +2540,15 @@ static const char bswap_flag_name[][6] = { [TCG_BSWAP_IZ | TCG_BSWAP_OS] = "iz,os", }; +#ifdef CONFIG_PLUGIN +static const char * const plugin_from_name[] = { + "from-tb", + "from-insn", + "after-insn", + "after-tb", +}; +#endif + static inline bool tcg_regset_single(TCGRegSet d) { return (d & (d - 1)) == 0; @@ -2558,7 +2567,7 @@ static inline TCGReg tcg_regset_first(TCGRegSet d) #define ne_fprintf(...) \ ({ int ret_ = fprintf(__VA_ARGS__); ret_ >= 0 ? ret_ : 0; }) -static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) +void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) { char buf[128]; TCGOp *op; @@ -2714,6 +2723,24 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) i = k = 1; } break; +#ifdef CONFIG_PLUGIN + case INDEX_op_plugin_cb: + { + TCGArg from = op->args[k++]; + const char *name = NULL; + + if (from < ARRAY_SIZE(plugin_from_name)) { + name = plugin_from_name[from]; + } + if (name) { + col += ne_fprintf(f, "%s", name); + } else { + col += ne_fprintf(f, "$0x%" TCG_PRIlx, from); + } + i = 1; + } + break; +#endif default: i = 0; break; diff --git a/util/log.c b/util/log.c index d36c98da0b..6219819855 100644 --- a/util/log.c +++ b/util/log.c @@ -466,6 +466,10 @@ const QEMULogItem qemu_log_items[] = { "show micro ops after optimization" }, { CPU_LOG_TB_OP_IND, "op_ind", "show micro ops before indirect lowering" }, +#ifdef CONFIG_PLUGIN + { LOG_TB_OP_PLUGIN, "op_plugin", + "show micro ops before plugin injection" }, +#endif { CPU_LOG_INT, "int", "show interrupts/exceptions in short format" }, { CPU_LOG_EXEC, "exec", From 7e53aa213e319ce905f26c64164f6eaf8360e73d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Mar 2024 11:06:57 -1000 Subject: [PATCH 0339/2884] plugins: Split out common cb expanders Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 84 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 49d9b07438..5b63b93114 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -187,6 +187,37 @@ static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, tcg_temp_free_i32(cpu_index); } +static void inject_cb(struct qemu_plugin_dyn_cb *cb) + +{ + switch (cb->type) { + case PLUGIN_CB_REGULAR: + gen_udata_cb(cb); + break; + case PLUGIN_CB_INLINE: + gen_inline_cb(cb); + break; + default: + g_assert_not_reached(); + } +} + +static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb, + enum qemu_plugin_mem_rw rw, + qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) +{ + if (cb->rw & rw) { + switch (cb->type) { + case PLUGIN_CB_MEM_REGULAR: + gen_mem_cb(cb, meminfo, addr); + break; + default: + inject_cb(cb); + break; + } + } +} + static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { TCGOp *op, *next; @@ -248,19 +279,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) cbs = plugin_tb->cbs; for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - - switch (cb->type) { - case PLUGIN_CB_REGULAR: - gen_udata_cb(cb); - break; - case PLUGIN_CB_INLINE: - gen_inline_cb(cb); - break; - default: - g_assert_not_reached(); - } + inject_cb( + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i)); } break; @@ -271,19 +291,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) cbs = insn->insn_cbs; for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - - switch (cb->type) { - case PLUGIN_CB_REGULAR: - gen_udata_cb(cb); - break; - case PLUGIN_CB_INLINE: - gen_inline_cb(cb); - break; - default: - g_assert_not_reached(); - } + inject_cb( + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i)); } break; @@ -300,33 +309,22 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0])); qemu_plugin_meminfo_t meminfo = op->args[1]; + enum qemu_plugin_mem_rw rw = + (qemu_plugin_mem_is_store(meminfo) + ? QEMU_PLUGIN_MEM_W : QEMU_PLUGIN_MEM_R); struct qemu_plugin_insn *insn; const GArray *cbs; - int i, n, rw; + int i, n; assert(insn_idx >= 0); insn = g_ptr_array_index(plugin_tb->insns, insn_idx); - rw = qemu_plugin_mem_is_store(meminfo) ? 2 : 1; tcg_ctx->emit_before_op = op; cbs = insn->mem_cbs; for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - - if (cb->rw & rw) { - switch (cb->type) { - case PLUGIN_CB_MEM_REGULAR: - gen_mem_cb(cb, meminfo, addr); - break; - case PLUGIN_CB_INLINE: - gen_inline_cb(cb); - break; - default: - g_assert_not_reached(); - } - } + inject_mem_cb(&g_array_index(cbs, struct qemu_plugin_dyn_cb, i), + rw, meminfo, addr); } tcg_ctx->emit_before_op = NULL; From 5e379b08bceb04631401fda674c4c9f7ab1e3f94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 19:27:28 -1000 Subject: [PATCH 0340/2884] plugins: Merge qemu_plugin_tb_insn_get to plugin-gen.c Merge qemu_plugin_insn_alloc and qemu_plugin_tb_insn_get into plugin_gen_insn_start, since it is used nowhere else. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 39 ++++++++++++++++++++++++++++++++------- include/qemu/plugin.h | 39 --------------------------------------- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 5b63b93114..c0cbc26984 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -373,11 +373,34 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - struct qemu_plugin_insn *pinsn; + struct qemu_plugin_insn *insn; + size_t n = db->num_insns; + vaddr pc; - pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next); - tcg_ctx->plugin_insn = pinsn; - plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); + assert(n >= 1); + ptb->n = n; + if (n <= ptb->insns->len) { + insn = g_ptr_array_index(ptb->insns, n - 1); + g_byte_array_set_size(insn->data, 0); + } else { + assert(n - 1 == ptb->insns->len); + insn = g_new0(struct qemu_plugin_insn, 1); + insn->data = g_byte_array_sized_new(4); + g_ptr_array_add(ptb->insns, insn); + } + + tcg_ctx->plugin_insn = insn; + insn->calls_helpers = false; + insn->mem_helper = false; + if (insn->insn_cbs) { + g_array_set_size(insn->insn_cbs, 0); + } + if (insn->mem_cbs) { + g_array_set_size(insn->mem_cbs, 0); + } + + pc = db->pc_next; + insn->vaddr = pc; /* * Detect page crossing to get the new host address. @@ -385,16 +408,18 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) * fetching instructions from a region not backed by RAM. */ if (ptb->haddr1 == NULL) { - pinsn->haddr = NULL; + insn->haddr = NULL; } else if (is_same_page(db, db->pc_next)) { - pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr; + insn->haddr = ptb->haddr1 + pc - ptb->vaddr; } else { if (ptb->vaddr2 == -1) { ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first); get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2); } - pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2; + insn->haddr = ptb->haddr2 + pc - ptb->vaddr2; } + + plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); } void plugin_gen_insn_end(void) diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index f5a08b1220..18062528c1 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -129,14 +129,6 @@ static inline void qemu_plugin_insn_cleanup_fn(gpointer data) g_byte_array_free(insn->data, true); } -static inline struct qemu_plugin_insn *qemu_plugin_insn_alloc(void) -{ - struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1); - - insn->data = g_byte_array_sized_new(4); - return insn; -} - /* Internal context for this TranslationBlock */ struct qemu_plugin_tb { GPtrArray *insns; @@ -153,37 +145,6 @@ struct qemu_plugin_tb { GArray *cbs; }; -/** - * qemu_plugin_tb_insn_get(): get next plugin record for translation. - * @tb: the internal tb context - * @pc: address of instruction - */ -static inline -struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb, - uint64_t pc) -{ - struct qemu_plugin_insn *insn; - - if (unlikely(tb->n == tb->insns->len)) { - struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc(); - g_ptr_array_add(tb->insns, new_insn); - } - - insn = g_ptr_array_index(tb->insns, tb->n++); - g_byte_array_set_size(insn->data, 0); - insn->calls_helpers = false; - insn->mem_helper = false; - insn->vaddr = pc; - if (insn->insn_cbs) { - g_array_set_size(insn->insn_cbs, 0); - } - if (insn->mem_cbs) { - g_array_set_size(insn->mem_cbs, 0); - } - - return insn; -} - /** * struct CPUPluginState - per-CPU state for plugins * @event_mask: plugin event bitmap. Modified only via async work. From 5c48b011bb417627752031cb67e4f2652d7cd378 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 19:41:40 -1000 Subject: [PATCH 0341/2884] plugins: Inline plugin_gen_empty_callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each caller can use tcg_gen_plugin_cb directly. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index c0cbc26984..d914d64de0 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -60,19 +60,6 @@ enum plugin_gen_from { PLUGIN_GEN_AFTER_TB, }; -static void plugin_gen_empty_callback(enum plugin_gen_from from) -{ - switch (from) { - case PLUGIN_GEN_AFTER_INSN: - case PLUGIN_GEN_FROM_TB: - case PLUGIN_GEN_FROM_INSN: - tcg_gen_plugin_cb(from); - break; - default: - g_assert_not_reached(); - } -} - /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ void plugin_gen_disable_mem_helpers(void) { @@ -362,7 +349,7 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, ptb->mem_only = mem_only; ptb->mem_helper = false; - plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); + tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); } tcg_ctx->plugin_insn = NULL; @@ -419,12 +406,12 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) insn->haddr = ptb->haddr2 + pc - ptb->vaddr2; } - plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); + tcg_gen_plugin_cb(PLUGIN_GEN_FROM_INSN); } void plugin_gen_insn_end(void) { - plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN); + tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_INSN); } /* From 917d7f8d948d706e275c9f33169b9dd0149ded1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 14 Mar 2024 19:47:31 -1000 Subject: [PATCH 0342/2884] plugins: Update the documentation block for plugin-gen.c Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index d914d64de0..3db74ae9bf 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -14,33 +14,10 @@ * Injecting the desired instrumentation could be done with a second * translation pass that combined the instrumentation requests, but that * would be ugly and inefficient since we would decode the guest code twice. - * Instead, during TB translation we add "empty" instrumentation calls for all - * possible instrumentation events, and then once we collect the instrumentation - * requests from plugins, we either "fill in" those empty events or remove them - * if they have no requests. - * - * When "filling in" an event we first copy the empty callback's TCG ops. This - * might seem unnecessary, but it is done to support an arbitrary number - * of callbacks per event. Take for example a regular instruction callback. - * We first generate a callback to an empty helper function. Then, if two - * plugins register one callback each for this instruction, we make two copies - * of the TCG ops generated for the empty callback, substituting the function - * pointer that points to the empty helper function with the plugins' desired - * callback functions. After that we remove the empty callback's ops. - * - * Note that the location in TCGOp.args[] of the pointer to a helper function - * varies across different guest and host architectures. Instead of duplicating - * the logic that figures this out, we rely on the fact that the empty - * callbacks point to empty functions that are unique pointers in the program. - * Thus, to find the right location we just have to look for a match in - * TCGOp.args[]. This is the main reason why we first copy an empty callback's - * TCG ops and then fill them in; regardless of whether we have one or many - * callbacks for that event, the logic to add all of them is the same. - * - * When generating more than one callback per event, we make a small - * optimization to avoid generating redundant operations. For instance, for the - * second and all subsequent callbacks of an event, we do not need to reload the - * CPU's index into a TCG temp, since the first callback did it already. + * Instead, during TB translation we add "plugin_cb" marker opcodes + * for all possible instrumentation events, and then once we collect the + * instrumentation requests from plugins, we generate code for those markers + * or remove them if they have no requests. */ #include "qemu/osdep.h" #include "qemu/plugin.h" From 50761a5a9aeaed928807229a86d619b5251598ff Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:42 +0200 Subject: [PATCH 0343/2884] qga: guest-get-fsinfo: add optional 'total-bytes-privileged' field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the commit 25b5ff1a86 ("qga: add mountpoint usage info to GuestFilesystemInfo") we have 2 values reported in guest-get-fsinfo: used = (f_blocks - f_bfree), total = (f_blocks - f_bfree + f_bavail) as returned by statvfs(3). While on Windows guests that's all we can get with GetDiskFreeSpaceExA(), on POSIX guests we might also be interested in total file system size, as it's visible for root user. Let's add an optional field 'total-bytes-privileged' to GuestFilesystemInfo struct, which'd only be reported on POSIX and represent f_blocks value as returned by statvfs(3). While here, also tweak the docs to reflect better where those values come from. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-2-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 2 ++ qga/commands-win32.c | 1 + qga/qapi-schema.json | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 26008db497..7df2d72e9f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1569,8 +1569,10 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, nonroot_total = used + buf.f_bavail; fs->used_bytes = used * fr_size; fs->total_bytes = nonroot_total * fr_size; + fs->total_bytes_privileged = buf.f_blocks * fr_size; fs->has_total_bytes = true; + fs->has_total_bytes_privileged = true; fs->has_used_bytes = true; } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 6242737b00..6fee0e1e6f 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1143,6 +1143,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); fs->has_total_bytes = false; + fs->has_total_bytes_privileged = false; fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d5af155007..1b9039e4f5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1026,7 +1026,10 @@ # # @used-bytes: file system used bytes (since 3.0) # -# @total-bytes: non-root file system total bytes (since 3.0) +# @total-bytes: filesystem capacity in bytes for unprivileged users (since 3.0) +# +# @total-bytes-privileged: filesystem capacity in bytes for privileged users +# (since 9.1) # # @disk: an array of disk hardware information that the volume lies # on, which may be empty if the disk type is not supported @@ -1036,7 +1039,7 @@ { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', '*used-bytes': 'uint64', '*total-bytes': 'uint64', - 'disk': ['GuestDiskAddress']} } + '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']} } ## # @guest-get-fsinfo: From c3f32c13a325f1ca9a0b08c19fefe9e5cc04289d Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:43 +0200 Subject: [PATCH 0344/2884] qga: introduce ga_run_command() helper for guest cmd execution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When executing guest commands in *nix environment, we repeat the same fork/exec pattern multiple times. Let's just separate it into a single helper which would also be able to feed input data into the launched process' stdin. This way we can avoid code duplication. To keep the history more bisectable, let's replace qmp commands implementations one by one. Also add G_GNUC_UNUSED attribute to the helper and remove it in the next commit. Originally-by: Yuri Pudgorodskiy Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-3-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 150 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7df2d72e9f..9b1bdf194c 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -76,6 +76,156 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp) g_assert(rpid == pid); } +static ssize_t ga_pipe_read_str(int fd[2], char **str) +{ + ssize_t n, len = 0; + char buf[1024]; + + close(fd[1]); + fd[1] = -1; + while ((n = read(fd[0], buf, sizeof(buf))) != 0) { + if (n < 0) { + if (errno == EINTR) { + continue; + } else { + len = -errno; + break; + } + } + *str = g_realloc(*str, len + n + 1); + memcpy(*str + len, buf, n); + len += n; + *str[len] = '\0'; + } + close(fd[0]); + fd[0] = -1; + + return len; +} + +/* + * Helper to run command with input/output redirection, + * sending string to stdin and taking error message from + * stdout/err. + */ +G_GNUC_UNUSED +static int ga_run_command(const char *argv[], const char *in_str, + const char *action, Error **errp) +{ + pid_t pid; + int status; + int retcode = -1; + int infd[2] = { -1, -1 }; + int outfd[2] = { -1, -1 }; + char *str = NULL; + ssize_t len = 0; + + if ((in_str && !g_unix_open_pipe(infd, FD_CLOEXEC, NULL)) || + !g_unix_open_pipe(outfd, FD_CLOEXEC, NULL)) { + error_setg(errp, "cannot create pipe FDs"); + goto out; + } + + pid = fork(); + if (pid == 0) { + char *cherr = NULL; + + setsid(); + + if (in_str) { + /* Redirect stdin to infd. */ + close(infd[1]); + dup2(infd[0], 0); + close(infd[0]); + } else { + reopen_fd_to_null(0); + } + + /* Redirect stdout/stderr to outfd. */ + close(outfd[0]); + dup2(outfd[1], 1); + dup2(outfd[1], 2); + close(outfd[1]); + + execvp(argv[0], (char *const *)argv); + + /* Write the cause of failed exec to pipe for the parent to read it. */ + cherr = g_strdup_printf("failed to exec '%s'", argv[0]); + perror(cherr); + g_free(cherr); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + + if (in_str) { + close(infd[0]); + infd[0] = -1; + if (qemu_write_full(infd[1], in_str, strlen(in_str)) != + strlen(in_str)) { + error_setg_errno(errp, errno, "%s: cannot write to stdin pipe", + action); + goto out; + } + close(infd[1]); + infd[1] = -1; + } + + len = ga_pipe_read_str(outfd, &str); + if (len < 0) { + error_setg_errno(errp, -len, "%s: cannot read from stdout/stderr pipe", + action); + goto out; + } + + ga_wait_child(pid, &status, errp); + if (*errp) { + goto out; + } + + if (!WIFEXITED(status)) { + if (len) { + error_setg(errp, "child process has terminated abnormally: %s", + str); + } else { + error_setg(errp, "child process has terminated abnormally"); + } + goto out; + } + + retcode = WEXITSTATUS(status); + + if (WEXITSTATUS(status)) { + if (len) { + error_setg(errp, "child process has failed to %s: %s", + action, str); + } else { + error_setg(errp, "child process has failed to %s: exit status %d", + action, WEXITSTATUS(status)); + } + goto out; + } + +out: + g_free(str); + + if (infd[0] != -1) { + close(infd[0]); + } + if (infd[1] != -1) { + close(infd[1]); + } + if (outfd[0] != -1) { + close(outfd[0]); + } + if (outfd[1] != -1) { + close(outfd[1]); + } + + return retcode; +} + void qmp_guest_shutdown(const char *mode, Error **errp) { const char *shutdown_flag; From 123f040a6ba3530c4c427a27522d4f1ec013b4d8 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:44 +0200 Subject: [PATCH 0345/2884] qga/commands-posix: qmp_guest_shutdown: use ga_run_command helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also remove the G_GNUC_UNUSED attribute added in the previous commit from the helper. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-4-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9b1bdf194c..cb9eed9a0b 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -108,7 +108,6 @@ static ssize_t ga_pipe_read_str(int fd[2], char **str) * sending string to stdin and taking error message from * stdout/err. */ -G_GNUC_UNUSED static int ga_run_command(const char *argv[], const char *in_str, const char *action, Error **errp) { @@ -230,8 +229,6 @@ void qmp_guest_shutdown(const char *mode, Error **errp) { const char *shutdown_flag; Error *local_err = NULL; - pid_t pid; - int status; #ifdef CONFIG_SOLARIS const char *powerdown_flag = "-i5"; @@ -260,46 +257,22 @@ void qmp_guest_shutdown(const char *mode, Error **errp) return; } - pid = fork(); - if (pid == 0) { - /* child, start the shutdown */ - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - + const char *argv[] = {"/sbin/shutdown", #ifdef CONFIG_SOLARIS - execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y", - "hypervisor initiated shutdown", (char *)NULL); + shutdown_flag, "-g0", "-y", #elif defined(CONFIG_BSD) - execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", - "hypervisor initiated shutdown", (char *)NULL); + shutdown_flag, "+0", #else - execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", - "hypervisor initiated shutdown", (char *)NULL); + "-h", shutdown_flag, "+0", #endif - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } + "hypervisor initiated shutdown", (char *) NULL}; - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, NULL, "shutdown", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - return; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to shutdown"); - return; - } - /* succeeded */ } From 1fce82bc407dae3a7a2a7c58a8955fc3598ed25f Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:45 +0200 Subject: [PATCH 0346/2884] qga/commands-posix: qmp_guest_set_time: use ga_run_command helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no need to check for the existence of "/sbin/hwclock", the exec() call will do that for us. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-5-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index cb9eed9a0b..545f3c99dc 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -279,21 +279,9 @@ void qmp_guest_shutdown(const char *mode, Error **errp) void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { int ret; - int status; - pid_t pid; Error *local_err = NULL; struct timeval tv; - static const char hwclock_path[] = "/sbin/hwclock"; - static int hwclock_available = -1; - - if (hwclock_available < 0) { - hwclock_available = (access(hwclock_path, X_OK) == 0); - } - - if (!hwclock_available) { - error_setg(errp, QERR_UNSUPPORTED); - return; - } + const char *argv[] = {"/sbin/hwclock", has_time ? "-w" : "-s", NULL}; /* If user has passed a time, validate and set it. */ if (has_time) { @@ -324,37 +312,12 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) * just need to synchronize the hardware clock. However, if no time was * passed, user is requesting the opposite: set the system time from the * hardware clock (RTC). */ - pid = fork(); - if (pid == 0) { - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - /* Use '/sbin/hwclock -w' to set RTC from the system time, - * or '/sbin/hwclock -s' to set the system time from RTC. */ - execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL); - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, NULL, "set hardware clock to system time", + &local_err); if (local_err) { error_propagate(errp, local_err); return; } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - return; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "hwclock failed to set hardware clock to system time"); - return; - } } typedef enum { From 8ef383b460826e1ab138e892559f7ed2305e049d Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:46 +0200 Subject: [PATCH 0347/2884] qga/commands-posix: execute_fsfreeze_hook: use ga_run_command helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no need to check for the existence of the hook executable, as the exec() call will do that for us. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-6-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 545f3c99dc..9b993772f5 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -736,8 +736,6 @@ static const char *fsfreeze_hook_arg_string[] = { static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) { - int status; - pid_t pid; const char *hook; const char *arg_str = fsfreeze_hook_arg_string[arg]; Error *local_err = NULL; @@ -746,42 +744,15 @@ static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) if (!hook) { return; } - if (access(hook, X_OK) != 0) { - error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook); - return; - } + + const char *argv[] = {hook, arg_str, NULL}; slog("executing fsfreeze hook with arg '%s'", arg_str); - pid = fork(); - if (pid == 0) { - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - execl(hook, hook, arg_str, NULL); - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, NULL, "execute fsfreeze hook", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - - if (!WIFEXITED(status)) { - error_setg(errp, "fsfreeze hook has terminated abnormally"); - return; - } - - status = WEXITSTATUS(status); - if (status) { - error_setg(errp, "fsfreeze hook has failed with status %d", status); - return; - } } /* From 2048129625bdca60bc76d3b8c3ee51c08aacedc2 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:47 +0200 Subject: [PATCH 0348/2884] qga/commands-posix: don't do fork()/exec() when suspending via sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 246d76eba ("qga: guest_suspend: decoupling pm-utils and sys logic") pm-utils logic is running in a separate child from the sysfs logic. Now when suspending via sysfs we don't really need to do that in a separate process as we only need to perform one write to /sys/power/state. Let's just use g_file_set_contents() to simplify things here. Suggested-by: Daniel P. Berrangé Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-7-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 41 +++++------------------------------------ 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9b993772f5..9910957ff5 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1928,52 +1928,21 @@ static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) static void linux_sys_state_suspend(SuspendMode mode, Error **errp) { - Error *local_err = NULL; + g_autoptr(GError) local_gerr = NULL; const char *sysfile_strs[3] = {"disk", "mem", NULL}; const char *sysfile_str = sysfile_strs[mode]; - pid_t pid; - int status; if (!sysfile_str) { error_setg(errp, "unknown guest suspend mode"); return; } - pid = fork(); - if (!pid) { - /* child */ - int fd; - - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); - if (fd < 0) { - _exit(EXIT_FAILURE); - } - - if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); + if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str, + -1, &local_gerr)) { + error_setg(errp, "suspend: cannot write to '%s': %s", + LINUX_SYS_STATE_FILE, local_gerr->message); return; } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to suspend"); - } - } static void guest_suspend(SuspendMode mode, Error **errp) From 0e5b75a3902f2325cbdb07954e2c2ca2f7fcb9dd Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Wed, 20 Mar 2024 18:16:48 +0200 Subject: [PATCH 0349/2884] qga/commands-posix: qmp_guest_set_user_password: use ga_run_command helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no need to check for the existence of the "chpasswd", "pw" executables, as the exec() call will do that for us. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-8-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 96 ++++++-------------------------------------- 1 file changed, 13 insertions(+), 83 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9910957ff5..7a065c4085 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2151,14 +2151,8 @@ void qmp_guest_set_user_password(const char *username, Error **errp) { Error *local_err = NULL; - char *passwd_path = NULL; - pid_t pid; - int status; - int datafd[2] = { -1, -1 }; - char *rawpasswddata = NULL; + g_autofree char *rawpasswddata = NULL; size_t rawpasswdlen; - char *chpasswddata = NULL; - size_t chpasswdlen; rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp); if (!rawpasswddata) { @@ -2169,95 +2163,31 @@ void qmp_guest_set_user_password(const char *username, if (strchr(rawpasswddata, '\n')) { error_setg(errp, "forbidden characters in raw password"); - goto out; + return; } if (strchr(username, '\n') || strchr(username, ':')) { error_setg(errp, "forbidden characters in username"); - goto out; + return; } #ifdef __FreeBSD__ - chpasswddata = g_strdup(rawpasswddata); - passwd_path = g_find_program_in_path("pw"); + g_autofree char *chpasswdata = g_strdup(rawpasswddata); + const char *crypt_flag = crypted ? "-H" : "-h"; + const char *argv[] = {"pw", "usermod", "-n", username, + crypt_flag, "0", NULL}; #else - chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); - passwd_path = g_find_program_in_path("chpasswd"); + g_autofree char *chpasswddata = g_strdup_printf("%s:%s\n", username, + rawpasswddata); + const char *crypt_flag = crypted ? "-e" : NULL; + const char *argv[] = {"chpasswd", crypt_flag, NULL}; #endif - chpasswdlen = strlen(chpasswddata); - - if (!passwd_path) { - error_setg(errp, "cannot find 'passwd' program in PATH"); - goto out; - } - - if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { - error_setg(errp, "cannot create pipe FDs"); - goto out; - } - - pid = fork(); - if (pid == 0) { - close(datafd[1]); - /* child */ - setsid(); - dup2(datafd[0], 0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - -#ifdef __FreeBSD__ - const char *h_arg; - h_arg = (crypted) ? "-H" : "-h"; - execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL); -#else - if (crypted) { - execl(passwd_path, "chpasswd", "-e", NULL); - } else { - execl(passwd_path, "chpasswd", NULL); - } -#endif - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - close(datafd[0]); - datafd[0] = -1; - - if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) { - error_setg_errno(errp, errno, "cannot write new account password"); - goto out; - } - close(datafd[1]); - datafd[1] = -1; - - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, chpasswddata, "set user password", &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to set user password"); - goto out; - } - -out: - g_free(chpasswddata); - g_free(rawpasswddata); - g_free(passwd_path); - if (datafd[0] != -1) { - close(datafd[0]); - } - if (datafd[1] != -1) { - close(datafd[1]); + return; } } #else /* __linux__ || __FreeBSD__ */ From 1cc9932700339042e83c6c54114734231630548c Mon Sep 17 00:00:00 2001 From: aidaleuc Date: Wed, 24 Apr 2024 08:40:28 -0600 Subject: [PATCH 0350/2884] qga: Refactor common SSH functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: <20240424144029.30665-2-aidan_leuck@selinc.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit In preparation of a Windows implementation, move the non-POSIX specific code to commands-common-ssh. Signed-off-by: Aidan Leuck Reviewed-by: Philippe Mathieu-Daudé Tested-by: Dehan Meng Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240424144029.30665-2-aidan_leuck@selinc.com Signed-off-by: Konstantin Kostiuk --- qga/commands-common-ssh.c | 50 +++++++++++++++++++++++++++++++++++++++ qga/commands-common-ssh.h | 10 ++++++++ qga/commands-posix-ssh.c | 47 +----------------------------------- qga/meson.build | 3 ++- 4 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 qga/commands-common-ssh.c create mode 100644 qga/commands-common-ssh.h diff --git a/qga/commands-common-ssh.c b/qga/commands-common-ssh.c new file mode 100644 index 0000000000..537869fb98 --- /dev/null +++ b/qga/commands-common-ssh.c @@ -0,0 +1,50 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "commands-common-ssh.h" + +GStrv read_authkeys(const char *path, Error **errp) +{ + g_autoptr(GError) err = NULL; + g_autofree char *contents = NULL; + + if (!g_file_get_contents(path, &contents, NULL, &err)) { + error_setg(errp, "failed to read '%s': %s", path, err->message); + return NULL; + } + + return g_strsplit(contents, "\n", -1); +} + +bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) +{ + size_t n = 0; + strList *k; + + for (k = keys; k != NULL; k = k->next) { + if (!check_openssh_pub_key(k->value, errp)) { + return false; + } + n++; + } + + if (nkeys) { + *nkeys = n; + } + return true; +} + +bool check_openssh_pub_key(const char *key, Error **errp) +{ + /* simple sanity-check, we may want more? */ + if (!key || key[0] == '#' || strchr(key, '\n')) { + error_setg(errp, "invalid OpenSSH public key: '%s'", key); + return false; + } + + return true; +} diff --git a/qga/commands-common-ssh.h b/qga/commands-common-ssh.h new file mode 100644 index 0000000000..14d955fa84 --- /dev/null +++ b/qga/commands-common-ssh.h @@ -0,0 +1,10 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qapi/qapi-builtin-types.h" + +GStrv read_authkeys(const char *path, Error **errp); +bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp); +bool check_openssh_pub_key(const char *key, Error **errp); diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index 236f80de44..dd2ecb453a 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -9,6 +9,7 @@ #include #include +#include "commands-common-ssh.h" #include "qapi/error.h" #include "qga-qapi-commands.h" @@ -80,37 +81,6 @@ mkdir_for_user(const char *path, const struct passwd *p, return true; } -static bool -check_openssh_pub_key(const char *key, Error **errp) -{ - /* simple sanity-check, we may want more? */ - if (!key || key[0] == '#' || strchr(key, '\n')) { - error_setg(errp, "invalid OpenSSH public key: '%s'", key); - return false; - } - - return true; -} - -static bool -check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) -{ - size_t n = 0; - strList *k; - - for (k = keys; k != NULL; k = k->next) { - if (!check_openssh_pub_key(k->value, errp)) { - return false; - } - n++; - } - - if (nkeys) { - *nkeys = n; - } - return true; -} - static bool write_authkeys(const char *path, const GStrv keys, const struct passwd *p, Error **errp) @@ -139,21 +109,6 @@ write_authkeys(const char *path, const GStrv keys, return true; } -static GStrv -read_authkeys(const char *path, Error **errp) -{ - g_autoptr(GError) err = NULL; - g_autofree char *contents = NULL; - - if (!g_file_get_contents(path, &contents, NULL, &err)) { - error_setg(errp, "failed to read '%s': %s", path, err->message); - return NULL; - } - - return g_strsplit(contents, "\n", -1); - -} - void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, bool has_reset, bool reset, diff --git a/qga/meson.build b/qga/meson.build index 46c1d83d7f..bc5ffb54ba 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -66,6 +66,7 @@ qga_ss.add(files( 'guest-agent-command-state.c', 'main.c', 'cutils.c', + 'commands-common-ssh.c' )) if host_os == 'windows' qga_ss.add(files( @@ -186,7 +187,7 @@ test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) # this when an alternative is implemented or when the underlying glib # issue is identified/fix if host_os != 'windows' and not get_option('fuzzing') - srcs = [files('commands-posix-ssh.c')] + srcs = [files('commands-common-ssh.c', 'commands-posix-ssh.c')] i = 0 foreach output: qga_qapi_outputs if output.startswith('qga-qapi-types') or output.startswith('qga-qapi-visit') From 6b9296ba7a9cf7adb157c51c124ca522d2180739 Mon Sep 17 00:00:00 2001 From: aidaleuc Date: Wed, 24 Apr 2024 08:40:29 -0600 Subject: [PATCH 0351/2884] qga: Implement SSH commands for Windows Signed-off-by: Aidan Leuck Tested-by: Dehan Meng Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240424144029.30665-3-aidan_leuck@selinc.com Signed-off-by: Konstantin Kostiuk --- qga/commands-windows-ssh.c | 712 +++++++++++++++++++++++++++++++++++++ qga/commands-windows-ssh.h | 26 ++ qga/meson.build | 5 +- qga/qapi-schema.json | 17 +- 4 files changed, 749 insertions(+), 11 deletions(-) create mode 100644 qga/commands-windows-ssh.c create mode 100644 qga/commands-windows-ssh.h diff --git a/qga/commands-windows-ssh.c b/qga/commands-windows-ssh.c new file mode 100644 index 0000000000..6a642e3ba8 --- /dev/null +++ b/qga/commands-windows-ssh.c @@ -0,0 +1,712 @@ +/* + * QEMU Guest Agent win32-specific command implementations for SSH keys. + * The implementation is opinionated and expects the SSH implementation to + * be OpenSSH. + * + * Copyright Schweitzer Engineering Laboratories. 2024 + * + * Authors: + * Aidan Leuck + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include + +#include "commands-common-ssh.h" +#include "commands-windows-ssh.h" +#include "guest-agent-core.h" +#include "limits.h" +#include "lmaccess.h" +#include "lmapibuf.h" +#include "lmerr.h" +#include "qapi/error.h" + +#include "qga-qapi-commands.h" +#include "sddl.h" +#include "shlobj.h" +#include "userenv.h" + +#define AUTHORIZED_KEY_FILE "authorized_keys" +#define AUTHORIZED_KEY_FILE_ADMIN "administrators_authorized_keys" +#define LOCAL_SYSTEM_SID "S-1-5-18" +#define ADMIN_SID "S-1-5-32-544" + +/* + * Frees userInfo structure. This implements the g_auto cleanup + * for the structure. + */ +void free_userInfo(PWindowsUserInfo info) +{ + g_free(info->sshDirectory); + g_free(info->authorizedKeyFile); + LocalFree(info->SSID); + g_free(info->username); + g_free(info); +} + +/* + * Gets the admin SSH folder for OpenSSH. OpenSSH does not store + * the authorized_key file in the users home directory for security reasons and + * instead stores it at %PROGRAMDATA%/ssh. This function returns the path to + * that directory on the users machine + * + * parameters: + * errp -> error structure to set when an error occurs + * returns: The path to the ssh folder in %PROGRAMDATA% or NULL if an error + * occurred. + */ +static char *get_admin_ssh_folder(Error **errp) +{ + /* Allocate memory for the program data path */ + g_autofree char *programDataPath = NULL; + char *authkeys_path = NULL; + PWSTR pgDataW = NULL; + g_autoptr(GError) gerr = NULL; + + /* Get the KnownFolderPath on the machine. */ + HRESULT folderResult = + SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &pgDataW); + if (folderResult != S_OK) { + error_setg(errp, "Failed to retrieve ProgramData folder"); + return NULL; + } + + /* Convert from a wide string back to a standard character string. */ + programDataPath = g_utf16_to_utf8(pgDataW, -1, NULL, NULL, &gerr); + CoTaskMemFree(pgDataW); + if (!programDataPath) { + error_setg(errp, + "Failed converting ProgramData folder path to UTF-16 %s", + gerr->message); + return NULL; + } + + /* Build the path to the file. */ + authkeys_path = g_build_filename(programDataPath, "ssh", NULL); + return authkeys_path; +} + +/* + * Gets the path to the SSH folder for the specified user. If the user is an + * admin it returns the ssh folder located at %PROGRAMDATA%/ssh. If the user is + * not an admin it returns %USERPROFILE%/.ssh + * + * parameters: + * username -> Username to get the SSH folder for + * isAdmin -> Whether the user is an admin or not + * errp -> Error structure to set any errors that occur. + * returns: path to the ssh folder as a string. + */ +static char *get_ssh_folder(const char *username, const bool isAdmin, + Error **errp) +{ + DWORD maxSize = MAX_PATH; + g_autofree char *profilesDir = g_new0(char, maxSize); + + if (isAdmin) { + return get_admin_ssh_folder(errp); + } + + /* If not an Admin the SSH key is in the user directory. */ + /* Get the user profile directory on the machine. */ + BOOL ret = GetProfilesDirectory(profilesDir, &maxSize); + if (!ret) { + error_setg_win32(errp, GetLastError(), + "failed to retrieve profiles directory"); + return NULL; + } + + /* Builds the filename */ + return g_build_filename(profilesDir, username, ".ssh", NULL); +} + +/* + * Creates an entry for the user so they can access the ssh folder in their + * userprofile. + * + * parameters: + * userInfo -> Information about the current user + * pACL -> Pointer to an ACL structure + * errp -> Error structure to set any errors that occur + * returns -> 1 on success, 0 otherwise + */ +static bool create_acl_user(PWindowsUserInfo userInfo, PACL *pACL, Error **errp) +{ + const int aclSize = 1; + PACL newACL = NULL; + EXPLICIT_ACCESS eAccess[1]; + PSID userPSID = NULL; + + /* Get a pointer to the internal SID object in Windows */ + bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID", + userInfo->username); + goto error; + } + + /* Set the permissions for the user. */ + eAccess[0].grfAccessPermissions = GENERIC_ALL; + eAccess[0].grfAccessMode = SET_ACCESS; + eAccess[0].grfInheritance = NO_INHERITANCE; + eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER; + eAccess[0].Trustee.ptstrName = (LPTSTR)userPSID; + + /* Set the ACL entries */ + DWORD setResult; + + /* + * If we are given a pointer that is already initialized, then we can merge + * the existing entries instead of overwriting them. + */ + if (*pACL) { + setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &newACL); + } else { + setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &newACL); + } + + if (setResult != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set ACL entries for user %s %lu", + userInfo->username, setResult); + goto error; + } + + /* Free any old memory since we are going to overwrite the users pointer. */ + LocalFree(*pACL); + *pACL = newACL; + + LocalFree(userPSID); + return true; +error: + LocalFree(userPSID); + return false; +} + +/* + * Creates a base ACL for both normal users and admins to share + * pACL -> Pointer to an ACL structure + * errp -> Error structure to set any errors that occur + * returns: 1 on success, 0 otherwise + */ +static bool create_acl_base(PACL *pACL, Error **errp) +{ + PSID adminGroupPSID = NULL; + PSID systemPSID = NULL; + + const int aclSize = 2; + EXPLICIT_ACCESS eAccess[2]; + + /* Create an entry for the system user. */ + const char *systemSID = LOCAL_SYSTEM_SID; + bool converted = ConvertStringSidToSid(systemSID, &systemPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve system SID"); + goto error; + } + + /* set permissions for system user */ + eAccess[0].grfAccessPermissions = GENERIC_ALL; + eAccess[0].grfAccessMode = SET_ACCESS; + eAccess[0].grfInheritance = NO_INHERITANCE; + eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER; + eAccess[0].Trustee.ptstrName = (LPTSTR)systemPSID; + + /* Create an entry for the admin user. */ + const char *adminSID = ADMIN_SID; + converted = ConvertStringSidToSid(adminSID, &adminGroupPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID"); + goto error; + } + + /* Set permissions for admin group. */ + eAccess[1].grfAccessPermissions = GENERIC_ALL; + eAccess[1].grfAccessMode = SET_ACCESS; + eAccess[1].grfInheritance = NO_INHERITANCE; + eAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + eAccess[1].Trustee.ptstrName = (LPTSTR)adminGroupPSID; + + /* Put the entries in an ACL object. */ + PACL pNewACL = NULL; + DWORD setResult; + + /* + *If we are given a pointer that is already initialized, then we can merge + *the existing entries instead of overwriting them. + */ + if (*pACL) { + setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL); + } else { + setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL); + } + + if (setResult != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set base ACL entries for system user and " + "admin group %lu", + setResult); + goto error; + } + + LocalFree(adminGroupPSID); + LocalFree(systemPSID); + + /* Free any old memory since we are going to overwrite the users pointer. */ + LocalFree(*pACL); + + *pACL = pNewACL; + + return true; + +error: + LocalFree(adminGroupPSID); + LocalFree(systemPSID); + return false; +} + +/* + * Sets the access control on the authorized_keys file and any ssh folders that + * need to be created. For administrators the required permissions on the + * file/folders are that only administrators and the LocalSystem account can + * access the folders. For normal user accounts only the specified user, + * LocalSystem and Administrators can have access to the key. + * + * parameters: + * userInfo -> pointer to structure that contains information about the user + * PACL -> pointer to an access control structure that will be set upon + * successful completion of the function. + * errp -> error structure that will be set upon error. + * returns: 1 upon success 0 upon failure. + */ +static bool create_acl(PWindowsUserInfo userInfo, PACL *pACL, Error **errp) +{ + /* + * Creates a base ACL that both admins and users will share + * This adds the Administrators group and the SYSTEM group + */ + if (!create_acl_base(pACL, errp)) { + return false; + } + + /* + * If the user is not an admin give the user creating the key permission to + * access the file. + */ + if (!userInfo->isAdmin) { + if (!create_acl_user(userInfo, pACL, errp)) { + return false; + } + + return true; + } + + return true; +} +/* + * Create the SSH directory for the user and d sets appropriate permissions. + * In general the directory will be %PROGRAMDATA%/ssh if the user is an admin. + * %USERPOFILE%/.ssh if not an admin + * + * parameters: + * userInfo -> Contains information about the user + * errp -> Structure that will contain errors if the function fails. + * returns: zero upon failure, 1 upon success + */ +static bool create_ssh_directory(WindowsUserInfo *userInfo, Error **errp) +{ + PACL pNewACL = NULL; + g_autofree PSECURITY_DESCRIPTOR pSD = NULL; + + /* Gets the appropriate ACL for the user */ + if (!create_acl(userInfo, &pNewACL, errp)) { + goto error; + } + + /* Allocate memory for a security descriptor */ + pSD = g_malloc(SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { + error_setg_win32(errp, GetLastError(), + "Failed to initialize security descriptor"); + goto error; + } + + /* Associate the security descriptor with the ACL permissions. */ + if (!SetSecurityDescriptorDacl(pSD, TRUE, pNewACL, FALSE)) { + error_setg_win32(errp, GetLastError(), + "Failed to set security descriptor ACL"); + goto error; + } + + /* Set the security attributes on the folder */ + SECURITY_ATTRIBUTES sAttr; + sAttr.bInheritHandle = FALSE; + sAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + sAttr.lpSecurityDescriptor = pSD; + + /* Create the directory with the created permissions */ + BOOL created = CreateDirectory(userInfo->sshDirectory, &sAttr); + if (!created) { + error_setg_win32(errp, GetLastError(), "failed to create directory %s", + userInfo->sshDirectory); + goto error; + } + + /* Free memory */ + LocalFree(pNewACL); + return true; +error: + LocalFree(pNewACL); + return false; +} + +/* + * Sets permissions on the authorized_key_file that is created. + * + * parameters: userInfo -> Information about the user + * errp -> error structure that will contain errors upon failure + * returns: 1 upon success, zero upon failure. + */ +static bool set_file_permissions(PWindowsUserInfo userInfo, Error **errp) +{ + PACL pACL = NULL; + PSID userPSID; + + /* Creates the access control structure */ + if (!create_acl(userInfo, &pACL, errp)) { + goto error; + } + + /* Get the PSID structure for the user based off the string SID. */ + bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID", + userInfo->username); + goto error; + } + + /* Prevents permissions from being inherited and use the DACL provided. */ + const SE_OBJECT_TYPE securityBitFlags = + DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; + + /* Set the ACL on the file. */ + if (SetNamedSecurityInfo(userInfo->authorizedKeyFile, SE_FILE_OBJECT, + securityBitFlags, userPSID, NULL, pACL, + NULL) != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set file security for file %s", + userInfo->authorizedKeyFile); + goto error; + } + + LocalFree(pACL); + LocalFree(userPSID); + return true; + +error: + LocalFree(pACL); + LocalFree(userPSID); + + return false; +} + +/* + * Writes the specified keys to the authenticated keys file. + * parameters: + * userInfo: Information about the user we are writing the authkeys file to. + * authkeys: Array of keys to write to disk + * errp: Error structure that will contain any errors if they occur. + * returns: 1 if successful, 0 otherwise. + */ +static bool write_authkeys(WindowsUserInfo *userInfo, GStrv authkeys, + Error **errp) +{ + g_autofree char *contents = NULL; + g_autoptr(GError) err = NULL; + + contents = g_strjoinv("\n", authkeys); + + if (!g_file_set_contents(userInfo->authorizedKeyFile, contents, -1, &err)) { + error_setg(errp, "failed to write to '%s': %s", + userInfo->authorizedKeyFile, err->message); + return false; + } + + if (!set_file_permissions(userInfo, errp)) { + return false; + } + + return true; +} + +/* + * Retrieves information about a Windows user by their username + * + * parameters: + * userInfo -> Double pointer to a WindowsUserInfo structure. Upon success, it + * will be allocated with information about the user and need to be freed. + * username -> Name of the user to lookup. + * errp -> Contains any errors that occur. + * returns: 1 upon success, 0 upon failure. + */ +static bool get_user_info(PWindowsUserInfo *userInfo, const char *username, + Error **errp) +{ + DWORD infoLevel = 4; + LPUSER_INFO_4 uBuf = NULL; + g_autofree wchar_t *wideUserName = NULL; + g_autoptr(GError) gerr = NULL; + PSID psid = NULL; + + /* + * Converts a string to a Windows wide string since the GetNetUserInfo + * function requires it. + */ + wideUserName = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr); + if (!wideUserName) { + goto error; + } + + /* allocate data */ + PWindowsUserInfo uData = g_new0(WindowsUserInfo, 1); + + /* Set pointer so it can be cleaned up by the callee, even upon error. */ + *userInfo = uData; + + /* Find the information */ + NET_API_STATUS result = + NetUserGetInfo(NULL, wideUserName, infoLevel, (LPBYTE *)&uBuf); + if (result != NERR_Success) { + /* Give a friendlier error message if the user was not found. */ + if (result == NERR_UserNotFound) { + error_setg(errp, "User %s was not found", username); + goto error; + } + + error_setg(errp, + "Received unexpected error when asking for user info: Error " + "Code %lu", + result); + goto error; + } + + /* Get information from the buffer returned by NetUserGetInfo. */ + uData->username = g_strdup(username); + uData->isAdmin = uBuf->usri4_priv == USER_PRIV_ADMIN; + psid = uBuf->usri4_user_sid; + + char *sidStr = NULL; + + /* + * We store the string representation of the SID not SID structure in + * memory. Callees wanting to use the SID structure should call + * ConvertStringSidToSID. + */ + if (!ConvertSidToStringSid(psid, &sidStr)) { + error_setg_win32(errp, GetLastError(), + "failed to get SID string for user %s", username); + goto error; + } + + /* Store the SSID */ + uData->SSID = sidStr; + + /* Get the SSH folder for the user. */ + char *sshFolder = get_ssh_folder(username, uData->isAdmin, errp); + if (sshFolder == NULL) { + goto error; + } + + /* Get the authorized key file path */ + const char *authorizedKeyFile = + uData->isAdmin ? AUTHORIZED_KEY_FILE_ADMIN : AUTHORIZED_KEY_FILE; + char *authorizedKeyPath = + g_build_filename(sshFolder, authorizedKeyFile, NULL); + uData->sshDirectory = sshFolder; + uData->authorizedKeyFile = authorizedKeyPath; + + /* Free */ + NetApiBufferFree(uBuf); + return true; +error: + if (uBuf) { + NetApiBufferFree(uBuf); + } + + return false; +} + +/* + * Gets the list of authorized keys for a user. + * + * parameters: + * username -> Username to retrieve the keys for. + * errp -> Error structure that will display any errors through QMP. + * returns: List of keys associated with the user. + */ +GuestAuthorizedKeys *qmp_guest_ssh_get_authorized_keys(const char *username, + Error **errp) +{ + GuestAuthorizedKeys *keys = NULL; + g_auto(GStrv) authKeys = NULL; + g_autoptr(GuestAuthorizedKeys) ret = NULL; + g_auto(PWindowsUserInfo) userInfo = NULL; + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return NULL; + } + + /* Reads authkeys for the user */ + authKeys = read_authkeys(userInfo->authorizedKeyFile, errp); + if (authKeys == NULL) { + return NULL; + } + + /* Set the GuestAuthorizedKey struct with keys from the file */ + ret = g_new0(GuestAuthorizedKeys, 1); + for (int i = 0; authKeys[i] != NULL; i++) { + g_strstrip(authKeys[i]); + if (!authKeys[i][0] || authKeys[i][0] == '#') { + continue; + } + + QAPI_LIST_PREPEND(ret->keys, g_strdup(authKeys[i])); + } + + /* + * Steal the pointer because it is up for the callee to deallocate the + * memory. + */ + keys = g_steal_pointer(&ret); + return keys; +} + +/* + * Adds an ssh key for a user. + * + * parameters: + * username -> User to add the SSH key to + * strList -> Array of keys to add to the list + * has_reset -> Whether the keys have been reset + * reset -> Boolean to reset the keys (If this is set the existing list will be + * cleared) and the other key reset. errp -> Pointer to an error structure that + * will get returned over QMP if anything goes wrong. + */ +void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, + bool has_reset, bool reset, Error **errp) +{ + g_auto(PWindowsUserInfo) userInfo = NULL; + g_auto(GStrv) authkeys = NULL; + strList *k; + size_t nkeys, nauthkeys; + + /* Make sure the keys given are valid */ + if (!check_openssh_pub_keys(keys, &nkeys, errp)) { + return; + } + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return; + } + + /* Determine whether we should reset the keys */ + reset = has_reset && reset; + if (!reset) { + /* Read existing keys into memory */ + authkeys = read_authkeys(userInfo->authorizedKeyFile, NULL); + } + + /* Check that the SSH key directory exists for the user. */ + if (!g_file_test(userInfo->sshDirectory, G_FILE_TEST_IS_DIR)) { + BOOL success = create_ssh_directory(userInfo, errp); + if (!success) { + return; + } + } + + /* Reallocates the buffer to fit the new keys. */ + nauthkeys = authkeys ? g_strv_length(authkeys) : 0; + authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *)); + + /* zero out the memory for the reallocated buffer */ + memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *)); + + /* Adds the keys */ + for (k = keys; k != NULL; k = k->next) { + /* Check that the key doesn't already exist */ + if (g_strv_contains((const gchar *const *)authkeys, k->value)) { + continue; + } + + authkeys[nauthkeys++] = g_strdup(k->value); + } + + /* Write the authkeys to the file. */ + write_authkeys(userInfo, authkeys, errp); +} + +/* + * Removes an SSH key for a user + * + * parameters: + * username -> Username to remove the key from + * strList -> List of strings to remove + * errp -> Contains any errors that occur. + */ +void qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys, + Error **errp) +{ + g_auto(PWindowsUserInfo) userInfo = NULL; + g_autofree struct passwd *p = NULL; + g_autofree GStrv new_keys = NULL; /* do not own the strings */ + g_auto(GStrv) authkeys = NULL; + GStrv a; + size_t nkeys = 0; + + /* Validates the keys passed in by the user */ + if (!check_openssh_pub_keys(keys, NULL, errp)) { + return; + } + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return; + } + + /* Reads the authkeys for the user */ + authkeys = read_authkeys(userInfo->authorizedKeyFile, errp); + if (authkeys == NULL) { + return; + } + + /* Create a new buffer to hold the keys */ + new_keys = g_new0(char *, g_strv_length(authkeys) + 1); + for (a = authkeys; *a != NULL; a++) { + strList *k; + + /* Filters out keys that are equal to ones the user specified. */ + for (k = keys; k != NULL; k = k->next) { + if (g_str_equal(k->value, *a)) { + break; + } + } + + if (k != NULL) { + continue; + } + + new_keys[nkeys++] = *a; + } + + /* Write the new authkeys to the file. */ + write_authkeys(userInfo, new_keys, errp); +} diff --git a/qga/commands-windows-ssh.h b/qga/commands-windows-ssh.h new file mode 100644 index 0000000000..40ac67c4d9 --- /dev/null +++ b/qga/commands-windows-ssh.h @@ -0,0 +1,26 @@ +/* + * Header file for commands-windows-ssh.c + * + * Copyright Schweitzer Engineering Laboratories. 2024 + * + * Authors: + * Aidan Leuck + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +typedef struct WindowsUserInfo { + char *sshDirectory; + char *authorizedKeyFile; + char *username; + char *SSID; + bool isAdmin; +} WindowsUserInfo; + +typedef WindowsUserInfo *PWindowsUserInfo; + +void free_userInfo(PWindowsUserInfo info); +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(PWindowsUserInfo, free_userInfo, NULL); diff --git a/qga/meson.build b/qga/meson.build index bc5ffb54ba..587ec4e5e8 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -73,7 +73,8 @@ if host_os == 'windows' 'channel-win32.c', 'commands-win32.c', 'service-win32.c', - 'vss-win32.c' + 'vss-win32.c', + 'commands-windows-ssh.c' )) else qga_ss.add(files( @@ -94,7 +95,7 @@ gen_tlb = [] qga_libs = [] if host_os == 'windows' qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32', - '-lsetupapi', '-lcfgmgr32'] + '-lsetupapi', '-lcfgmgr32', '-luserenv'] if have_qga_vss qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup'] subdir('vss-win32') diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 1b9039e4f5..b3de1fb6b3 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1570,9 +1570,8 @@ { 'struct': 'GuestAuthorizedKeys', 'data': { 'keys': ['str'] - }, - 'if': 'CONFIG_POSIX' } - + } +} ## # @guest-ssh-get-authorized-keys: @@ -1588,8 +1587,8 @@ ## { 'command': 'guest-ssh-get-authorized-keys', 'data': { 'username': 'str' }, - 'returns': 'GuestAuthorizedKeys', - 'if': 'CONFIG_POSIX' } + 'returns': 'GuestAuthorizedKeys' +} ## # @guest-ssh-add-authorized-keys: @@ -1607,8 +1606,8 @@ # Since: 5.2 ## { 'command': 'guest-ssh-add-authorized-keys', - 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }, - 'if': 'CONFIG_POSIX' } + 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } +} ## # @guest-ssh-remove-authorized-keys: @@ -1625,8 +1624,8 @@ # Since: 5.2 ## { 'command': 'guest-ssh-remove-authorized-keys', - 'data': { 'username': 'str', 'keys': ['str'] }, - 'if': 'CONFIG_POSIX' } + 'data': { 'username': 'str', 'keys': ['str'] } +} ## # @GuestDiskStats: From 6a5a63f74ba5c5355b7a8468d3d814bfffe928fb Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 15 Apr 2024 14:45:21 +0800 Subject: [PATCH 0352/2884] target/i386: Give IRQs a chance when resetting HF_INHIBIT_IRQ_MASK When emulated with QEMU, interrupts will never come in the following loop. However, if the NOP instruction is uncommented, interrupts will fire as normal. loop: cli call do_sti jmp loop do_sti: sti # nop ret This behavior is different from that of a real processor. For example, if KVM is enabled, interrupts will always fire regardless of whether the NOP instruction is commented or not. Also, the Intel Software Developer Manual states that after the STI instruction is executed, the interrupt inhibit should end as soon as the next instruction (e.g., the RET instruction if the NOP instruction is commented) is executed. This problem is caused because the previous code may choose not to end the TB even if the HF_INHIBIT_IRQ_MASK has just been reset (e.g., in the case where the STI instruction is immediately followed by the RET instruction), so that IRQs may not have a change to trigger. This commit fixes the problem by always terminating the current TB to give IRQs a chance to trigger when HF_INHIBIT_IRQ_MASK is reset. Reviewed-by: Richard Henderson Signed-off-by: Ruihan Li Message-ID: <20240415064518.4951-4-lrh2000@pku.edu.cn> Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index c05d9e5225..051ffb5e1f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2798,13 +2798,17 @@ static void gen_bnd_jmp(DisasContext *s) static void do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) { + bool inhibit_reset; + gen_update_cc_op(s); /* If several instructions disable interrupts, only the first does it. */ - if (inhibit && !(s->flags & HF_INHIBIT_IRQ_MASK)) { - gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); - } else { + inhibit_reset = false; + if (s->flags & HF_INHIBIT_IRQ_MASK) { gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); + inhibit_reset = true; + } else if (inhibit) { + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); } if (s->base.tb->flags & HF_RF_MASK) { @@ -2815,7 +2819,9 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) tcg_gen_exit_tb(NULL, 0); } else if (s->flags & HF_TF_MASK) { gen_helper_single_step(tcg_env); - } else if (jr) { + } else if (jr && + /* give irqs a chance to happen */ + !inhibit_reset) { tcg_gen_lookup_and_goto_ptr(); } else { tcg_gen_exit_tb(NULL, 0); From 5ce77fcb1b8480f35a1008d5500197d4b73f9ef6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Apr 2024 21:16:31 +0200 Subject: [PATCH 0353/2884] Kconfig: kvm: allow building without any board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM code might have to call functions on the PCIDevice that is passed to kvm_arch_fixup_msi_route(). This fails in the case where --without-default-devices is used and no board is configured. While this is not really a useful configuration, and therefore setting up stubs for CONFIG_PCI is overkill, failing the build is impolite. Just include the PCI subsystem if kvm_arch_fixup_msi_route() requires it, as is the case for ARM and x86. Reported-by: Philippe Mathieu-Daudé Tested-by: Fabiano Rosas Signed-off-by: Paolo Bonzini --- target/arm/Kconfig | 2 ++ target/i386/Kconfig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/target/arm/Kconfig b/target/arm/Kconfig index bf57d739cd..5847c5a74a 100644 --- a/target/arm/Kconfig +++ b/target/arm/Kconfig @@ -9,3 +9,5 @@ config ARM config AARCH64 bool select ARM + # kvm_arch_fixup_msi_route() needs to access PCIDevice + select PCI if KVM diff --git a/target/i386/Kconfig b/target/i386/Kconfig index ce6968906e..4689894639 100644 --- a/target/i386/Kconfig +++ b/target/i386/Kconfig @@ -1,5 +1,7 @@ config I386 bool + # kvm_arch_fixup_msi_route() needs to access PCIDevice + select PCI if KVM config X86_64 bool From 61653b4a97b1c08b0f1d090da1ed981362a3961a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Apr 2024 11:11:04 +0200 Subject: [PATCH 0354/2884] accel/nvmm: Fix NULL dereference in nvmm_init_vcpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When mechanically moving the @dirty field to AccelCPUState in commit 79f1926b2d, we neglected cpu->accel is still NULL when we want to dereference it. Reported-by: Volker Rümelin Suggested-by: Volker Rümelin Fixes: 79f1926b2d ("accel/nvmm: Use accel-specific per-vcpu @dirty field") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240429091918.27429-3-philmd@linaro.org> --- target/i386/nvmm/nvmm-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index f9cced53b3..65768aca03 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -982,7 +982,7 @@ nvmm_init_vcpu(CPUState *cpu) } } - cpu->accel->dirty = true; + qcpu->dirty = true; cpu->accel = qcpu; return 0; From 083367dbbf6e5ac086c32e64db6701f493928e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Apr 2024 11:10:53 +0200 Subject: [PATCH 0355/2884] accel/whpx: Fix NULL dereference in whpx_init_vcpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When mechanically moving the @dirty field to AccelCPUState in commit 9ad49538c7, we neglected cpu->accel is still NULL when we want to dereference it. Fixes: 9ad49538c7 ("accel/whpx: Use accel-specific per-vcpu @dirty field") Reported-by: Volker Rümelin Suggested-by: Volker Rümelin Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240429091918.27429-2-philmd@linaro.org> --- target/i386/whpx/whpx-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index b08e644517..a6674a826d 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2236,7 +2236,7 @@ int whpx_init_vcpu(CPUState *cpu) } vcpu->interruptable = true; - cpu->accel->dirty = true; + vcpu->dirty = true; cpu->accel = vcpu; max_vcpu_index = max(max_vcpu_index, cpu->cpu_index); qemu_add_vm_change_state_handler(whpx_cpu_update_state, env); From 969ce22123681694a9c241aed3f649185a9e067b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 May 2024 10:48:56 +0200 Subject: [PATCH 0356/2884] tests/qtest: skip m48t59-test if the machine is absent Together with the series at https://patchew.org/QEMU/20240423131612.28362-1-pbonzini@redhat.com/, this allows adding sparc-softmmu to the target list of the build-without-defaults CI job. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- tests/qtest/m48t59-test.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index b9cd209165..605797ab78 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -262,11 +262,12 @@ int main(int argc, char **argv) base_setup(); g_test_init(&argc, &argv, NULL); - - if (g_test_slow()) { - /* Do not run this in timing-sensitive environments */ - qtest_add_func("/rtc/bcd-check-time", bcd_check_time); + if (qtest_has_machine(base_machine)) { + if (g_test_slow()) { + /* Do not run this in timing-sensitive environments */ + qtest_add_func("/rtc/bcd-check-time", bcd_check_time); + } + qtest_add_func("/rtc/fuzz-registers", fuzz_registers); } - qtest_add_func("/rtc/fuzz-registers", fuzz_registers); return g_test_run(); } From 957f583b7cb4d317fb694ca45350de6d9d2bd1b8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 May 2024 12:45:00 +0200 Subject: [PATCH 0357/2884] gitlab-ci: adjust msys2-64bit to be able to run qtest sparc-softmmu is able to run a subset of qtests when compiled --without-default-devices, so use it instead of x86_64-softmmu for the msys2 run. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/windows.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 94834269ec..d26dbdd0c0 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -24,10 +24,7 @@ msys2-64bit: # changed to compile QEMU with the --without-default-devices switch # for this job, because otherwise the build could not complete within # the project timeout. - CONFIGURE_ARGS: --target-list=x86_64-softmmu --without-default-devices -Ddebug=false -Doptimization=0 - # qTests don't run successfully with "--without-default-devices", - # so let's exclude the qtests from CI for now. - TEST_ARGS: --no-suite qtest + CONFIGURE_ARGS: --target-list=sparc-softmmu --without-default-devices -Ddebug=false -Doptimization=0 # The Windows git is a bit older so override the default GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet artifacts: From 566abdb4d90d73728f37cd5dcced0fbef84a63db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 May 2024 09:35:33 +0200 Subject: [PATCH 0358/2884] kvm: ppc: disable sPAPR code if CONFIG_PSERIES is disabled target/ppc/kvm.c calls out to code in hw/ppc/spapr*.c; that code is not present and fails to link if CONFIG_PSERIES is not enabled. Adjust kvm.c to depend on CONFIG_PSERIES instead of TARGET_PPC64, and compile out anything that requires cap_papr, because only the pseries machine will call kvmppc_set_papr(). Signed-off-by: Paolo Bonzini --- target/ppc/kvm.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 63930d4a77..46fccff786 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -49,6 +49,8 @@ #include "elf.h" #include "sysemu/kvm_int.h" +#include CONFIG_DEVICES + #define PROC_DEVTREE_CPU "/proc/device-tree/cpus/" #define DEBUG_RETURN_GUEST 0 @@ -71,7 +73,6 @@ static int cap_hior; static int cap_one_reg; static int cap_epr; static int cap_ppc_watchdog; -static int cap_papr; static int cap_htab_fd; static int cap_fixup_hcalls; static int cap_htm; /* Hardware transactional memory support */ @@ -90,6 +91,12 @@ static int cap_fwnmi; static int cap_rpt_invalidate; static int cap_ail_mode_3; +#ifdef CONFIG_PSERIES +static int cap_papr; +#else +#define cap_papr (0) +#endif + static uint32_t debug_inst_opcode; /* @@ -1668,7 +1675,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) trace_kvm_handle_halt(); ret = kvmppc_handle_halt(cpu); break; -#if defined(TARGET_PPC64) +#if defined(CONFIG_PSERIES) case KVM_EXIT_PAPR_HCALL: trace_kvm_handle_papr_hcall(run->papr_hcall.nr); run->papr_hcall.ret = spapr_hypercall(cpu, @@ -1698,7 +1705,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = 0; break; -#if defined(TARGET_PPC64) +#if defined(CONFIG_PSERIES) case KVM_EXIT_NMI: trace_kvm_handle_nmi_exception(); ret = kvm_handle_nmi(cpu, run); @@ -2054,6 +2061,7 @@ void kvmppc_enable_h_rpt_invalidate(void) kvmppc_enable_hcall(kvm_state, H_RPT_INVALIDATE); } +#ifdef CONFIG_PSERIES void kvmppc_set_papr(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -2075,6 +2083,7 @@ void kvmppc_set_papr(PowerPCCPU *cpu) */ cap_papr = 1; } +#endif int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr) { @@ -2837,7 +2846,7 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) return data & 0xffff; } -#if defined(TARGET_PPC64) +#if defined(CONFIG_PSERIES) int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run) { uint16_t flags = run->flags & KVM_RUN_PPC_NMI_DISP_MASK; From 7e10ce2706e2dbed6a59825dc0286b3810395afa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 14:00:34 +0100 Subject: [PATCH 0359/2884] configs: list "implied" device groups in the default configs Match the optional device groups to what is actually included in the config-devices.mak files. Signed-off-by: Paolo Bonzini --- configs/devices/arm-softmmu/default.mak | 2 ++ configs/devices/loongarch64-softmmu/default.mak | 3 +++ configs/devices/or1k-softmmu/default.mak | 4 ++++ configs/devices/ppc-softmmu/default.mak | 4 ++++ configs/devices/riscv32-softmmu/default.mak | 4 ++-- configs/devices/riscv64-softmmu/default.mak | 4 ++-- configs/devices/xtensa-softmmu/default.mak | 4 ++++ 7 files changed, 21 insertions(+), 4 deletions(-) diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak index 6ee31bc1ab..c1cfb3bcf7 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -1,5 +1,7 @@ # Default configuration for arm-softmmu +# Uncomment the following lines to disable these optional devices: +# CONFIG_I2C_DEVICES=n # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak index 928bc117ef..0893112b81 100644 --- a/configs/devices/loongarch64-softmmu/default.mak +++ b/configs/devices/loongarch64-softmmu/default.mak @@ -1,3 +1,6 @@ # Default configuration for loongarch64-softmmu +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n + CONFIG_LOONGARCH_VIRT=y diff --git a/configs/devices/or1k-softmmu/default.mak b/configs/devices/or1k-softmmu/default.mak index 89c39e3123..3aecdf9d73 100644 --- a/configs/devices/or1k-softmmu/default.mak +++ b/configs/devices/or1k-softmmu/default.mak @@ -1,5 +1,9 @@ # Default configuration for or1k-softmmu +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n + # Boards: # CONFIG_OR1K_SIM=y diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak index b85fd2bcd7..3061b26749 100644 --- a/configs/devices/ppc-softmmu/default.mak +++ b/configs/devices/ppc-softmmu/default.mak @@ -1,5 +1,9 @@ # Default configuration for ppc-softmmu +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n + # For embedded PPCs: CONFIG_E500PLAT=y CONFIG_MPC8544DS=y diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/riscv32-softmmu/default.mak index 94a236c9c2..07e4fd26df 100644 --- a/configs/devices/riscv32-softmmu/default.mak +++ b/configs/devices/riscv32-softmmu/default.mak @@ -1,8 +1,8 @@ # Default configuration for riscv32-softmmu # Uncomment the following lines to disable these optional devices: -# -#CONFIG_PCI_DEVICES=n +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n # Boards: # diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index 3f68059448..221963d4c5 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -1,8 +1,8 @@ # Default configuration for riscv64-softmmu # Uncomment the following lines to disable these optional devices: -# -#CONFIG_PCI_DEVICES=n +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n # Boards: # diff --git a/configs/devices/xtensa-softmmu/default.mak b/configs/devices/xtensa-softmmu/default.mak index 49e4c9da88..f650cad760 100644 --- a/configs/devices/xtensa-softmmu/default.mak +++ b/configs/devices/xtensa-softmmu/default.mak @@ -1,5 +1,9 @@ # Default configuration for Xtensa +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n + # Boards: # CONFIG_XTENSA_SIM=y From 01ef1c0dc83cc40522b45f289cd5ed669b0f3da4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0360/2884] alpha: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Start with Alpha. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/alpha-softmmu/default.mak | 5 ++--- hw/alpha/Kconfig | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 6394b8f41e..c6c9c242c5 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -661,7 +661,7 @@ build-without-defaults: --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: avr-softmmu s390x-softmmu sh4-softmmu + TARGETS: alpha-softmmu avr-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/alpha-softmmu/default.mak b/configs/devices/alpha-softmmu/default.mak index d186fe8e9b..3de6a9f577 100644 --- a/configs/devices/alpha-softmmu/default.mak +++ b/configs/devices/alpha-softmmu/default.mak @@ -5,6 +5,5 @@ #CONFIG_PCI_DEVICES=n #CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_DP264=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_DP264=n diff --git a/hw/alpha/Kconfig b/hw/alpha/Kconfig index 9af650c94e..7f3455ce1e 100644 --- a/hw/alpha/Kconfig +++ b/hw/alpha/Kconfig @@ -1,5 +1,7 @@ config DP264 bool + default y + depends on ALPHA imply PCI_DEVICES imply TEST_DEVICES imply E1000_PCI From 1a67aed81707f8bd97a4cf21b0c99af057be28bd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0361/2884] arm: switch boards to "default y" For ARM targets, boards that require TCG are already using "default y". Switch ARM_VIRT to the same selection mechanism. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 1 + configs/devices/arm-softmmu/default.mak | 3 ++- hw/arm/Kconfig | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index c6c9c242c5..3a03cdb015 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -648,6 +648,7 @@ build-tci: - make check-tcg # Check our reduced build configurations +# requires libfdt: aarch64, arm build-without-defaults: extends: .native_build_job_template needs: diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak index c1cfb3bcf7..31f77c2026 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -5,7 +5,8 @@ # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n -CONFIG_ARM_VIRT=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_ARM_VIRT=n # These are selected by default when TCG is enabled, uncomment them to # keep out of the build. diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index fe1f9643bd..98c264ed21 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -1,5 +1,7 @@ config ARM_VIRT bool + default y + depends on ARM imply PCI_DEVICES imply TEST_DEVICES imply VFIO_AMD_XGBE From e2ee238664a621c13cb22cf1375835507031a20a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0362/2884] avr: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with AVR. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- configs/devices/avr-softmmu/default.mak | 5 ++--- hw/avr/Kconfig | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/configs/devices/avr-softmmu/default.mak b/configs/devices/avr-softmmu/default.mak index 80218add98..4207e7b3ce 100644 --- a/configs/devices/avr-softmmu/default.mak +++ b/configs/devices/avr-softmmu/default.mak @@ -1,5 +1,4 @@ # Default configuration for avr-softmmu -# Boards: -# -CONFIG_ARDUINO=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_ARDUINO=n diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig index d31298c3cc..b29937be41 100644 --- a/hw/avr/Kconfig +++ b/hw/avr/Kconfig @@ -5,5 +5,8 @@ config AVR_ATMEGA_MCU select AVR_POWER config ARDUINO + bool + default y + depends on AVR select AVR_ATMEGA_MCU select UNIMP From 86280d86d67832f3d0aea80871374c05238ef3ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0363/2884] cris: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with CRIS. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 3 ++- configs/devices/cris-softmmu/default.mak | 5 ++--- hw/cris/Kconfig | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 3a03cdb015..f4dc566646 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -662,7 +662,8 @@ build-without-defaults: --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: alpha-softmmu avr-softmmu s390x-softmmu sh4-softmmu + TARGETS: alpha-softmmu avr-softmmu cris-softmmu + s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/cris-softmmu/default.mak b/configs/devices/cris-softmmu/default.mak index 5932cf4d06..ff73cd4084 100644 --- a/configs/devices/cris-softmmu/default.mak +++ b/configs/devices/cris-softmmu/default.mak @@ -1,5 +1,4 @@ # Default configuration for cris-softmmu -# Boards: -# -CONFIG_AXIS=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_AXIS=n diff --git a/hw/cris/Kconfig b/hw/cris/Kconfig index 884ad2cbc0..26c7eef743 100644 --- a/hw/cris/Kconfig +++ b/hw/cris/Kconfig @@ -1,5 +1,7 @@ config AXIS bool + default y + depends on CRIS select ETRAXFS select PFLASH_CFI02 select NAND From 9e6190aecd37510dc111f7a41fed8f08ff14630c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0364/2884] hppa: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with PARISC. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/hppa-softmmu/default.mak | 5 ++--- hw/hppa/Kconfig | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index f4dc566646..6531758d96 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -662,7 +662,7 @@ build-without-defaults: --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: alpha-softmmu avr-softmmu cris-softmmu + TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/hppa-softmmu/default.mak b/configs/devices/hppa-softmmu/default.mak index b0364bb88f..059510cdbb 100644 --- a/configs/devices/hppa-softmmu/default.mak +++ b/configs/devices/hppa-softmmu/default.mak @@ -4,6 +4,5 @@ # #CONFIG_PCI_DEVICES=n -# Boards: -# -CONFIG_HPPA_B160L=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_HPPA_B160L=n diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig index ee7ffd2bfb..d4d457f4ab 100644 --- a/hw/hppa/Kconfig +++ b/hw/hppa/Kconfig @@ -1,5 +1,7 @@ config HPPA_B160L bool + default y + depends on HPPA imply PCI_DEVICES imply E1000_PCI imply USB_OHCI_PCI From 4921d0a7535bc33415564d487ea17ba91a34082f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0365/2884] i386: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with i386. No changes to generated config-devices.mak files, other than adding CONFIG_I386 to the x86_64-softmmu target. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 3 ++- configs/devices/i386-softmmu/default.mak | 11 +++++------ hw/i386/Kconfig | 10 +++++++++- target/i386/Kconfig | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 6531758d96..75222c4450 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -648,7 +648,8 @@ build-tci: - make check-tcg # Check our reduced build configurations -# requires libfdt: aarch64, arm +# requires libfdt: aarch64, arm, i386, x86_64 +# does not build without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template needs: diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index 598c6646df..448e3e3b1b 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -24,9 +24,8 @@ #CONFIG_VTD=n #CONFIG_SGX=n -# Boards: -# -CONFIG_ISAPC=y -CONFIG_I440FX=y -CONFIG_Q35=y -CONFIG_MICROVM=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_ISAPC=n +# CONFIG_I440FX=n +# CONFIG_Q35=n +# CONFIG_MICROVM=n diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index a6ee052f9a..4362164962 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -66,6 +66,8 @@ config PC_ACPI config I440FX bool + default y + depends on I386 imply E1000_PCI imply VMPORT imply VMMOUSE @@ -81,6 +83,8 @@ config I440FX config ISAPC bool + default y + depends on I386 imply VGA_ISA select ISA_BUS select PC @@ -91,6 +95,8 @@ config ISAPC config Q35 bool + default y + depends on I386 imply VTD imply AMD_IOMMU imply E1000E_PCI_EXPRESS @@ -108,6 +114,8 @@ config Q35 config MICROVM bool + default y + depends on I386 select SERIAL_ISA # for serial_hds_isa_init() select ISA_BUS select APIC @@ -142,4 +150,4 @@ config VMMOUSE config XEN_EMU bool default y - depends on KVM && (I386 || X86_64) + depends on KVM && I386 diff --git a/target/i386/Kconfig b/target/i386/Kconfig index 4689894639..ad9291d3b8 100644 --- a/target/i386/Kconfig +++ b/target/i386/Kconfig @@ -5,3 +5,4 @@ config I386 config X86_64 bool + select I386 From 2f856b2861e58ffebd544558c045b32fff04920f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0366/2884] loongarch: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with Loongarch. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- configs/devices/loongarch64-softmmu/default.mak | 3 ++- hw/loongarch/Kconfig | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 75222c4450..a82848ba55 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -648,8 +648,8 @@ build-tci: - make check-tcg # Check our reduced build configurations -# requires libfdt: aarch64, arm, i386, x86_64 -# does not build without boards: i386, x86_64 +# requires libfdt: aarch64, arm, i386, loongarch64, x86_64 +# does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template needs: diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak index 0893112b81..ffe705836f 100644 --- a/configs/devices/loongarch64-softmmu/default.mak +++ b/configs/devices/loongarch64-softmmu/default.mak @@ -3,4 +3,5 @@ # Uncomment the following lines to disable these optional devices: # CONFIG_PCI_DEVICES=n -CONFIG_LOONGARCH_VIRT=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_LOONGARCH_VIRT=n diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 5727efed6d..7864050563 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -1,5 +1,7 @@ config LOONGARCH_VIRT bool + default y + depends on LOONGARCH64 select PCI select PCI_EXPRESS_GENERIC_BRIDGE imply VIRTIO_VGA From 9f6ece49d5a1e79e9d6c8f3308c12fad66c481c0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0367/2884] m68k: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with m68k. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/m68k-softmmu/default.mak | 13 ++++++------- hw/m68k/Kconfig | 10 ++++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index a82848ba55..a91e8d359d 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -663,7 +663,7 @@ build-without-defaults: --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu + TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/m68k-softmmu/default.mak b/configs/devices/m68k-softmmu/default.mak index 8dcaa28ed3..3ceda6b041 100644 --- a/configs/devices/m68k-softmmu/default.mak +++ b/configs/devices/m68k-softmmu/default.mak @@ -1,9 +1,8 @@ # Default configuration for m68k-softmmu -# Boards: -# -CONFIG_AN5206=y -CONFIG_MCF5208=y -CONFIG_NEXTCUBE=y -CONFIG_Q800=y -CONFIG_M68K_VIRT=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_AN5206=n +# CONFIG_MCF5208=n +# CONFIG_NEXTCUBE=n +# CONFIG_Q800=n +# CONFIG_M68K_VIRT=n diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index d88741ec9d..0092cda4e9 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -1,20 +1,28 @@ config AN5206 bool + default y + depends on M68K select COLDFIRE select PTIMER config MCF5208 bool + default y + depends on M68K select COLDFIRE select PTIMER config NEXTCUBE bool + default y + depends on M68K select FRAMEBUFFER select ESCC config Q800 bool + default y + depends on M68K select MAC_VIA select NUBUS select MACFB @@ -29,6 +37,8 @@ config Q800 config M68K_VIRT bool + default y + depends on M68K select M68K_IRQC select VIRT_CTRL select GOLDFISH_PIC From a75b180f415a7d95754364b2a584d69b16bd141b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0368/2884] microblaze: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with Microblaze. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 3 ++- configs/devices/microblaze-softmmu/default.mak | 9 ++++----- hw/microblaze/Kconfig | 6 ++++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index a91e8d359d..e2e92f25c5 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -648,7 +648,8 @@ build-tci: - make check-tcg # Check our reduced build configurations -# requires libfdt: aarch64, arm, i386, loongarch64, x86_64 +# requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, +# x86_64 # does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template diff --git a/configs/devices/microblaze-softmmu/default.mak b/configs/devices/microblaze-softmmu/default.mak index db8c6e4bba..583e3959bb 100644 --- a/configs/devices/microblaze-softmmu/default.mak +++ b/configs/devices/microblaze-softmmu/default.mak @@ -1,7 +1,6 @@ # Default configuration for microblaze-softmmu -# Boards: -# -CONFIG_PETALOGIX_S3ADSP1800=y -CONFIG_PETALOGIX_ML605=y -CONFIG_XLNX_ZYNQMP_PMU=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_PETALOGIX_S3ADSP1800=n +# CONFIG_PETALOGIX_ML605=n +# CONFIG_XLNX_ZYNQMP_PMU=n diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig index e2697ced9c..d78ba843fa 100644 --- a/hw/microblaze/Kconfig +++ b/hw/microblaze/Kconfig @@ -1,5 +1,7 @@ config PETALOGIX_S3ADSP1800 bool + default y + depends on MICROBLAZE select PFLASH_CFI01 select XILINX select XILINX_AXI @@ -8,6 +10,8 @@ config PETALOGIX_S3ADSP1800 config PETALOGIX_ML605 bool + default y + depends on MICROBLAZE select PFLASH_CFI01 select SERIAL select SSI_M25P80 @@ -18,4 +22,6 @@ config PETALOGIX_ML605 config XLNX_ZYNQMP_PMU bool + default y + depends on MICROBLAZE select XLNX_ZYNQMP From bae3e3a5c6b81677b1d4ec231ad0844e65990f3d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Jan 2024 11:53:17 +0100 Subject: [PATCH 0369/2884] meson: make target endianneess available to Kconfig Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. MIPS boards may only be available for big-endian or only for little-endian emulators, add a symbol so that this can be described with a "depends on" clause. Signed-off-by: Paolo Bonzini --- meson.build | 12 +++++++----- target/Kconfig | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 5db2dbc12e..43da492372 100644 --- a/meson.build +++ b/meson.build @@ -3005,7 +3005,7 @@ foreach target : target_dirs } endif - accel_kconfig = [] + target_kconfig = [] foreach sym: accelerators if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, []) config_target += { sym: 'y' } @@ -3015,10 +3015,10 @@ foreach target : target_dirs else config_target += { 'CONFIG_TCG_BUILTIN': 'y' } endif - accel_kconfig += [ sym + '=y' ] + target_kconfig += [ sym + '=y' ] endif endforeach - if accel_kconfig.length() == 0 + if target_kconfig.length() == 0 if default_targets continue endif @@ -3078,6 +3078,9 @@ foreach target : target_dirs configuration: config_target_data)} if target.endswith('-softmmu') + target_kconfig += 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y' + target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN'] + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( @@ -3088,8 +3091,7 @@ foreach target : target_dirs command: [minikconf, get_option('default_devices') ? '--defconfig' : '--allnoconfig', config_devices_mak, '@DEPFILE@', '@INPUT@', - host_kconfig, accel_kconfig, - 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y']) + host_kconfig, target_kconfig]) config_devices_data = configuration_data() config_devices = keyval.load(config_devices_mak) diff --git a/target/Kconfig b/target/Kconfig index 5275a93ad0..7f64112e9e 100644 --- a/target/Kconfig +++ b/target/Kconfig @@ -17,3 +17,6 @@ source sh4/Kconfig source sparc/Kconfig source tricore/Kconfig source xtensa/Kconfig + +config TARGET_BIG_ENDIAN + bool From 8a1f6d0ebd21efad6ea4843796d550ce4c14e81c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0370/2884] mips: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with MIPS. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 3 ++- configs/devices/mips-softmmu/common.mak | 5 +++-- configs/devices/mips64-softmmu/default.mak | 4 +++- configs/devices/mips64el-softmmu/default.mak | 10 ++++++---- hw/mips/Kconfig | 12 ++++++++++++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e2e92f25c5..811132443a 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -649,7 +649,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# x86_64 +# mips64el, x86_64 # does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template @@ -665,6 +665,7 @@ build-without-defaults: --disable-qom-cast-debug --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu + mips-softmmu mips64-softmmu mipsel-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak index 416a5d353e..b50107feaf 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -4,5 +4,6 @@ # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n -CONFIG_MALTA=y -CONFIG_MIPSSIM=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_MALTA=n +# CONFIG_MIPSSIM=n diff --git a/configs/devices/mips64-softmmu/default.mak b/configs/devices/mips64-softmmu/default.mak index 566672f3c2..1b8d7ced1c 100644 --- a/configs/devices/mips64-softmmu/default.mak +++ b/configs/devices/mips64-softmmu/default.mak @@ -1,4 +1,6 @@ # Default configuration for mips64-softmmu include ../mips-softmmu/common.mak -CONFIG_JAZZ=y + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_JAZZ=n diff --git a/configs/devices/mips64el-softmmu/default.mak b/configs/devices/mips64el-softmmu/default.mak index 88a37cf27f..9dce346c4f 100644 --- a/configs/devices/mips64el-softmmu/default.mak +++ b/configs/devices/mips64el-softmmu/default.mak @@ -1,7 +1,9 @@ # Default configuration for mips64el-softmmu include ../mips-softmmu/common.mak -CONFIG_FULOONG=y -CONFIG_LOONGSON3V=y -CONFIG_JAZZ=y -CONFIG_MIPS_BOSTON=y + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_FULOONG=n +# CONFIG_LOONGSON3V=n +# CONFIG_JAZZ=n +# CONFIG_MIPS_BOSTON=n diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 5c83ef49cf..9bccb363eb 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -1,5 +1,7 @@ config MALTA bool + default y + depends on MIPS imply PCNET_PCI imply PCI_DEVICES imply TEST_DEVICES @@ -13,11 +15,15 @@ config MALTA config MIPSSIM bool + default y + depends on MIPS select SERIAL select MIPSNET config JAZZ bool + default y + depends on MIPS64 select ISA_BUS select RC4030 select I8259 @@ -38,6 +44,8 @@ config JAZZ config FULOONG bool + default y + depends on MIPS64 && !TARGET_BIG_ENDIAN imply PCI_DEVICES imply TEST_DEVICES imply ATI_VGA @@ -48,6 +56,8 @@ config FULOONG config LOONGSON3V bool + default y + depends on MIPS64 && !TARGET_BIG_ENDIAN imply PCI_DEVICES imply TEST_DEVICES imply VIRTIO_PCI @@ -69,6 +79,8 @@ config MIPS_CPS config MIPS_BOSTON bool + default y + depends on MIPS64 && !TARGET_BIG_ENDIAN imply PCI_DEVICES imply TEST_DEVICES select FITLOADER From c8b39c9b5b901b173fbbb0ec3f147e9694ece48c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0371/2884] openrisc: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with OpenRISC. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/or1k-softmmu/default.mak | 5 ++--- hw/openrisc/Kconfig | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 811132443a..49cd50c354 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -649,7 +649,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# mips64el, x86_64 +# mips64el, or1k, x86_64 # does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template diff --git a/configs/devices/or1k-softmmu/default.mak b/configs/devices/or1k-softmmu/default.mak index 3aecdf9d73..efe3bc278b 100644 --- a/configs/devices/or1k-softmmu/default.mak +++ b/configs/devices/or1k-softmmu/default.mak @@ -5,6 +5,5 @@ # CONFIG_TEST_DEVICES=n # Boards: -# -CONFIG_OR1K_SIM=y -CONFIG_OR1K_VIRT=y +# CONFIG_OR1K_SIM=n +# CONFIG_OR1K_VIRT=n diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig index 97af258b55..9c9015e0a5 100644 --- a/hw/openrisc/Kconfig +++ b/hw/openrisc/Kconfig @@ -1,5 +1,7 @@ config OR1K_SIM bool + default y + depends on OPENRISC select SERIAL select OPENCORES_ETH select OMPIC @@ -7,6 +9,8 @@ config OR1K_SIM config OR1K_VIRT bool + default y + depends on OPENRISC imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES From bf616ce47be6802bbe7d6c7bae212e5b57da57eb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0372/2884] ppc: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with PowerPC/POWER. No changes to generated config-devices.mak files, other than adding CONFIG_PPC to the ppc64-softmmu target. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/ppc-softmmu/default.mak | 26 ++++++++++++----------- configs/devices/ppc64-softmmu/default.mak | 8 +++---- hw/ppc/Kconfig | 26 +++++++++++++++++++++++ target/ppc/Kconfig | 1 + 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 49cd50c354..a5f4b4d379 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -649,7 +649,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# mips64el, or1k, x86_64 +# mips64el, or1k, ppc, ppc64, x86_64 # does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak index 3061b26749..460d15e676 100644 --- a/configs/devices/ppc-softmmu/default.mak +++ b/configs/devices/ppc-softmmu/default.mak @@ -4,22 +4,24 @@ # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n -# For embedded PPCs: -CONFIG_E500PLAT=y -CONFIG_MPC8544DS=y -CONFIG_PPC405=y -CONFIG_PPC440=y -CONFIG_VIRTEX=y +# Boards are selected by default, uncomment to keep out of the build. + +# Embedded PPCs: +# CONFIG_E500PLAT=n +# CONFIG_MPC8544DS=n +# CONFIG_PPC405=n +# CONFIG_PPC440=n +# CONFIG_VIRTEX=n # For Sam460ex -CONFIG_SAM460EX=y +# CONFIG_SAM460EX=n # For Macs -CONFIG_MAC_OLDWORLD=y -CONFIG_MAC_NEWWORLD=y +# CONFIG_MAC_OLDWORLD=n +# CONFIG_MAC_NEWWORLD=n -CONFIG_AMIGAONE=y -CONFIG_PEGASOS2=y +# CONFIG_AMIGAONE=n +# CONFIG_PEGASOS2=n # For PReP -CONFIG_PREP=y +# CONFIG_PREP=n diff --git a/configs/devices/ppc64-softmmu/default.mak b/configs/devices/ppc64-softmmu/default.mak index b90e5bf455..e8ad260313 100644 --- a/configs/devices/ppc64-softmmu/default.mak +++ b/configs/devices/ppc64-softmmu/default.mak @@ -3,8 +3,6 @@ # Include all 32-bit boards include ../ppc-softmmu/default.mak -# For PowerNV -CONFIG_POWERNV=y - -# For pSeries -CONFIG_PSERIES=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_POWERNV=n +# CONFIG_PSERIES=n diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 37ccf9cdca..78f83e78ce 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -1,5 +1,7 @@ config PSERIES bool + default y + depends on PPC64 imply USB_OHCI_PCI imply PCI_DEVICES imply TEST_DEVICES @@ -23,6 +25,8 @@ config SPAPR_RNG config POWERNV bool + default y + depends on PPC64 imply PCI_DEVICES imply TEST_DEVICES select ISA_IPMI_BT @@ -38,6 +42,8 @@ config POWERNV config PPC405 bool + default y + depends on PPC select M48T59 select PFLASH_CFI02 select PPC4XX @@ -45,6 +51,8 @@ config PPC405 config PPC440 bool + default y + depends on PPC imply PCI_DEVICES imply TEST_DEVICES imply E1000_PCI @@ -62,6 +70,8 @@ config PPC4XX config SAM460EX bool + default y + depends on PPC select PFLASH_CFI01 select IDE_SII3112 select M41T80 @@ -75,6 +85,8 @@ config SAM460EX config AMIGAONE bool + default y + depends on PPC imply ATI_VGA select ARTICIA select VT82C686 @@ -82,6 +94,8 @@ config AMIGAONE config PEGASOS2 bool + default y + depends on PPC imply ATI_VGA select MV64361 select VT82C686 @@ -90,6 +104,8 @@ config PEGASOS2 config PREP bool + default y + depends on PPC imply PCI_DEVICES imply TEST_DEVICES select CS4231A @@ -106,6 +122,8 @@ config RS6000_MC config MAC_OLDWORLD bool + default y + depends on PPC imply PCI_DEVICES imply SUNGEM imply TEST_DEVICES @@ -117,6 +135,8 @@ config MAC_OLDWORLD config MAC_NEWWORLD bool + default y + depends on PPC imply PCI_DEVICES imply SUNGEM imply TEST_DEVICES @@ -147,14 +167,20 @@ config E500 config E500PLAT bool + default y + depends on PPC select E500 config MPC8544DS bool + default y + depends on PPC select E500 config VIRTEX bool + default y + depends on PPC select PPC4XX select PFLASH_CFI01 select SERIAL diff --git a/target/ppc/Kconfig b/target/ppc/Kconfig index 3ff152051a..0283711673 100644 --- a/target/ppc/Kconfig +++ b/target/ppc/Kconfig @@ -3,3 +3,4 @@ config PPC config PPC64 bool + select PPC From a980c33deaaf07d9076266ffb536a133a5de7b2b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0373/2884] riscv: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with RISC-V. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/riscv32-softmmu/default.mak | 13 ++++++------- configs/devices/riscv64-softmmu/default.mak | 15 +++++++-------- hw/riscv/Kconfig | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index a5f4b4d379..a65b5fc956 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -649,7 +649,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# mips64el, or1k, ppc, ppc64, x86_64 +# mips64el, or1k, ppc, ppc64, riscv32, riscv64, x86_64 # does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/riscv32-softmmu/default.mak index 07e4fd26df..c2cd86ce05 100644 --- a/configs/devices/riscv32-softmmu/default.mak +++ b/configs/devices/riscv32-softmmu/default.mak @@ -4,10 +4,9 @@ # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_SPIKE=y -CONFIG_SIFIVE_E=y -CONFIG_SIFIVE_U=y -CONFIG_RISCV_VIRT=y -CONFIG_OPENTITAN=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SPIKE=n +# CONFIG_SIFIVE_E=n +# CONFIG_SIFIVE_U=n +# CONFIG_RISCV_VIRT=n +# CONFIG_OPENTITAN=n diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index 221963d4c5..39ed3a0061 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -4,11 +4,10 @@ # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_SPIKE=y -CONFIG_SIFIVE_E=y -CONFIG_SIFIVE_U=y -CONFIG_RISCV_VIRT=y -CONFIG_MICROCHIP_PFSOC=y -CONFIG_SHAKTI_C=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SPIKE=n +# CONFIG_SIFIVE_E=n +# CONFIG_SIFIVE_U=n +# CONFIG_RISCV_VIRT=n +# CONFIG_MICROCHIP_PFSOC=n +# CONFIG_SHAKTI_C=n diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index fc72ef0379..5f5f9e31bb 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -8,6 +8,8 @@ config IBEX config MICROCHIP_PFSOC bool + default y + depends on RISCV64 select CADENCE_SDHCI select CPU_CLUSTER select MCHP_PFSOC_DMC @@ -21,12 +23,16 @@ config MICROCHIP_PFSOC config OPENTITAN bool + default y + depends on RISCV32 select IBEX select SIFIVE_PLIC select UNIMP config RISCV_VIRT bool + default y + depends on RISCV32 || RISCV64 imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES @@ -51,6 +57,8 @@ config RISCV_VIRT config SHAKTI_C bool + default y + depends on RISCV64 select RISCV_ACLINT select SHAKTI_UART select SIFIVE_PLIC @@ -58,6 +66,8 @@ config SHAKTI_C config SIFIVE_E bool + default y + depends on RISCV32 || RISCV64 select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PLIC @@ -68,6 +78,8 @@ config SIFIVE_E config SIFIVE_U bool + default y + depends on RISCV32 || RISCV64 select CADENCE select CPU_CLUSTER select RISCV_ACLINT @@ -85,6 +97,8 @@ config SIFIVE_U config SPIKE bool + default y + depends on RISCV32 || RISCV64 select RISCV_NUMA select HTIF select RISCV_ACLINT From 4852f70e4b5cea187cc0f159a47376567a75c46b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0374/2884] rx: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with RX. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/rx-softmmu/default.mak | 3 ++- hw/rx/Kconfig | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index a65b5fc956..13f505f20d 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -649,7 +649,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# mips64el, or1k, ppc, ppc64, riscv32, riscv64, x86_64 +# mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 # does not build without boards: i386, loongarch64, x86_64 build-without-defaults: extends: .native_build_job_template diff --git a/configs/devices/rx-softmmu/default.mak b/configs/devices/rx-softmmu/default.mak index df2b4e4f42..e7caebe197 100644 --- a/configs/devices/rx-softmmu/default.mak +++ b/configs/devices/rx-softmmu/default.mak @@ -1,3 +1,4 @@ # Default configuration for rx-softmmu -CONFIG_RX_GDBSIM=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_RX_GDBSIM=n diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig index 2b297c5a6a..b2fa2b7eec 100644 --- a/hw/rx/Kconfig +++ b/hw/rx/Kconfig @@ -7,4 +7,6 @@ config RX62N_MCU config RX_GDBSIM bool + default y + depends on RX select RX62N_MCU From d70fb7cf34d9de439b2b1ee9d43edb3c784db536 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0375/2884] s390x: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with s390. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- configs/devices/s390x-softmmu/default.mak | 5 ++--- hw/s390x/Kconfig | 2 ++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 13f505f20d..2475262c5c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, loongarch64, x86_64 +# does not build without boards: i386, loongarch64, s390x, x86_64 build-without-defaults: extends: .native_build_job_template needs: @@ -666,7 +666,7 @@ build-without-defaults: --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu mips-softmmu mips64-softmmu mipsel-softmmu - s390x-softmmu sh4-softmmu + sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/s390x-softmmu/default.mak b/configs/devices/s390x-softmmu/default.mak index 6d87bc8b4b..340c109292 100644 --- a/configs/devices/s390x-softmmu/default.mak +++ b/configs/devices/s390x-softmmu/default.mak @@ -9,6 +9,5 @@ #CONFIG_WDT_DIAG288=n #CONFIG_PCIE_DEVICES=n -# Boards: -# -CONFIG_S390_CCW_VIRTIO=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_S390_CCW_VIRTIO=n diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig index 26ad104485..3bbf4ae56e 100644 --- a/hw/s390x/Kconfig +++ b/hw/s390x/Kconfig @@ -1,5 +1,7 @@ config S390_CCW_VIRTIO bool + default y + depends on S390X imply VIRTIO_PCI imply TERMINAL3270 imply VFIO_AP From 09c94e6167e1b38b205cda472df4583daab69690 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0376/2884] sh4: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with SH. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 3 +-- configs/devices/sh4-softmmu/default.mak | 7 +++---- hw/sh4/Kconfig | 4 ++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 2475262c5c..fd66593184 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, loongarch64, s390x, x86_64 +# does not build without boards: i386, loongarch64, s390x, sh4, sh4eb, x86_64 build-without-defaults: extends: .native_build_job_template needs: @@ -666,7 +666,6 @@ build-without-defaults: --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu mips-softmmu mips64-softmmu mipsel-softmmu - sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/sh4-softmmu/default.mak b/configs/devices/sh4-softmmu/default.mak index 565e8b0b5d..c06a427053 100644 --- a/configs/devices/sh4-softmmu/default.mak +++ b/configs/devices/sh4-softmmu/default.mak @@ -5,7 +5,6 @@ #CONFIG_PCI_DEVICES=n #CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_R2D=y -CONFIG_SHIX=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_R2D=n +# CONFIG_SHIX=n diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig index e0c4ecd1a5..99a76a94c3 100644 --- a/hw/sh4/Kconfig +++ b/hw/sh4/Kconfig @@ -1,5 +1,7 @@ config R2D bool + default y + depends on SH4 imply PCI_DEVICES imply TEST_DEVICES imply RTL8139_PCI @@ -13,6 +15,8 @@ config R2D config SHIX bool + default y + depends on SH4 select SH7750 select TC58128 From d399fddcd46a4e15aa9fd2e0adb2d54a277cb9f2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0377/2884] sparc: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with SPARC and SPARC64. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 5 +++-- configs/devices/sparc-softmmu/default.mak | 7 +++---- configs/devices/sparc64-softmmu/default.mak | 7 +++---- hw/sparc/Kconfig | 4 ++++ hw/sparc64/Kconfig | 4 ++++ 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index fd66593184..5d2ce16118 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -665,8 +665,9 @@ build-without-defaults: --disable-qom-cast-debug --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu - mips-softmmu mips64-softmmu mipsel-softmmu - sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user + mips-softmmu mips64-softmmu mipsel-softmmu sparc-softmmu + sparc64-softmmu + hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check build-libvhost-user: diff --git a/configs/devices/sparc-softmmu/default.mak b/configs/devices/sparc-softmmu/default.mak index ee85218115..87668fda5e 100644 --- a/configs/devices/sparc-softmmu/default.mak +++ b/configs/devices/sparc-softmmu/default.mak @@ -5,7 +5,6 @@ #CONFIG_TCX=n #CONFIG_CG3=n -# Boards: -# -CONFIG_SUN4M=y -CONFIG_LEON3=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SUN4M=n +# CONFIG_LEON3=n diff --git a/configs/devices/sparc64-softmmu/default.mak b/configs/devices/sparc64-softmmu/default.mak index e50030a229..fa82f39a20 100644 --- a/configs/devices/sparc64-softmmu/default.mak +++ b/configs/devices/sparc64-softmmu/default.mak @@ -6,7 +6,6 @@ #CONFIG_SUNHME=n #CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_SUN4U=y -CONFIG_NIAGARA=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SUN4U=n +# CONFIG_NIAGARA=n diff --git a/hw/sparc/Kconfig b/hw/sparc/Kconfig index 79d58beb7a..3cc165dbfb 100644 --- a/hw/sparc/Kconfig +++ b/hw/sparc/Kconfig @@ -1,5 +1,7 @@ config SUN4M bool + default y + depends on SPARC && !SPARC64 imply TCX imply CG3 select CS4231 @@ -18,6 +20,8 @@ config SUN4M config LEON3 bool + default y + depends on SPARC && !SPARC64 select GRLIB config GRLIB diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig index 7e557ad17b..3b948a2290 100644 --- a/hw/sparc64/Kconfig +++ b/hw/sparc64/Kconfig @@ -1,5 +1,7 @@ config SUN4U bool + default y + depends on SPARC64 imply PCI_DEVICES imply SUNHME imply TEST_DEVICES @@ -16,6 +18,8 @@ config SUN4U config NIAGARA bool + default y + depends on SPARC64 select EMPTY_SLOT select SUN4V_RTC select UNIMP From 021cd4c6e27185343de093865a6363504a8507ca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0378/2884] tricore: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with TriCore. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/tricore-softmmu/default.mak | 7 +++++-- hw/tricore/Kconfig | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 5d2ce16118..bdd4dc49b1 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -666,7 +666,7 @@ build-without-defaults: --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu mips-softmmu mips64-softmmu mipsel-softmmu sparc-softmmu - sparc64-softmmu + sparc64-softmmu tricore-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/tricore-softmmu/default.mak b/configs/devices/tricore-softmmu/default.mak index cb8fc286eb..c7ab542244 100644 --- a/configs/devices/tricore-softmmu/default.mak +++ b/configs/devices/tricore-softmmu/default.mak @@ -1,2 +1,5 @@ -CONFIG_TRICORE_TESTBOARD=y -CONFIG_TRIBOARD=y +# Default configuration for tricore-softmmu + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_TRICORE_TESTBOARD=n +# CONFIG_TRIBOARD=n diff --git a/hw/tricore/Kconfig b/hw/tricore/Kconfig index 33c1e852c3..6c04f64949 100644 --- a/hw/tricore/Kconfig +++ b/hw/tricore/Kconfig @@ -1,8 +1,12 @@ config TRICORE_TESTBOARD + default y + depends on TRICORE bool config TRIBOARD bool + default y + depends on TRICORE select TC27X_SOC config TC27X_SOC From 0a12e7b752e87a0c92a7cc2e8a76eaf669760f12 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 13:36:37 +0100 Subject: [PATCH 0379/2884] xtensa: switch boards to "default y" Some targets use "default y" for boards to filter out those that require TCG. For consistency we are switching all other targets to do the same. Continue with Xtensa. No changes to generated config-devices.mak file. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- configs/devices/xtensa-softmmu/default.mak | 9 ++++----- hw/xtensa/Kconfig | 6 ++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index bdd4dc49b1..e9402a68a7 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -666,7 +666,7 @@ build-without-defaults: --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu mips-softmmu mips64-softmmu mipsel-softmmu sparc-softmmu - sparc64-softmmu tricore-softmmu + sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/devices/xtensa-softmmu/default.mak b/configs/devices/xtensa-softmmu/default.mak index f650cad760..fbc3079a94 100644 --- a/configs/devices/xtensa-softmmu/default.mak +++ b/configs/devices/xtensa-softmmu/default.mak @@ -4,8 +4,7 @@ # #CONFIG_PCI_DEVICES=n -# Boards: -# -CONFIG_XTENSA_SIM=y -CONFIG_XTENSA_VIRT=y -CONFIG_XTENSA_XTFPGA=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_XTENSA_SIM=n +# CONFIG_XTENSA_VIRT=n +# CONFIG_XTENSA_XTFPGA=n diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig index 0740657ea5..443b415c2b 100644 --- a/hw/xtensa/Kconfig +++ b/hw/xtensa/Kconfig @@ -1,14 +1,20 @@ config XTENSA_SIM + default y + depends on XTENSA bool config XTENSA_VIRT bool + default y + depends on XTENSA select XTENSA_SIM select PCI_EXPRESS_GENERIC_BRIDGE select PCI_DEVICES config XTENSA_XTFPGA bool + default y + depends on XTENSA select OPENCORES_ETH select PFLASH_CFI01 select SERIAL From 7cdfcea693c0bbbfd59ca55f216f6b330f095d19 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Mar 2024 11:02:17 +0100 Subject: [PATCH 0380/2884] docs: document new convention for Kconfig board symbols Boards have been switched to use "default y" and are now listed in default-configs/*.mak only for convenience. Document this change and the new possibilities that it allows. Signed-off-by: Paolo Bonzini --- docs/devel/kconfig.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index ccb9a46bd7..52d4b905f6 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -211,6 +211,8 @@ declares its dependencies in different ways: config SUN4M bool + default y + depends on SPARC && !SPARC64 imply TCX imply CG3 select CS4231 @@ -228,8 +230,16 @@ declares its dependencies in different ways: directives. A device should be listed under ``select`` if the board cannot be started at all without it. It should be listed under ``imply`` if (depending on the QEMU command line) the board may or - may not be started without it. Boards also default to false; they are - enabled by the ``default-configs/*.mak`` for the target they apply to. + may not be started without it. Boards default to true, but also + have a ``depends on`` clause to limit them to the appropriate targets. + For some targets, not all boards may be supported by hardware + virtualization, in which case they also depend on the ``TCG`` symbol, + Other symbols that are commonly used as dependencies for boards + include libraries (such as ``FDT``) or ``TARGET_BIG_ENDIAN`` + (possibly negated). + + Boards are listed for convenience in the ``default-configs/*.mak`` + for the target they apply to. **internal elements** From b10b2481738304db13d28252e86c10555121a5b3 Mon Sep 17 00:00:00 2001 From: Lei Wang Date: Wed, 24 Apr 2024 03:29:12 -0400 Subject: [PATCH 0381/2884] target/i386: Introduce SapphireRapids-v3 to add missing features Add the missing features(ss, tsc-adjust, cldemote, movdiri, movdir64b) in the SapphireRapids-v3 CPU model. Signed-off-by: Lei Wang Message-ID: <20240424072912.43188-1-lei4.wang@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index aa3b2d8391..e5723f232c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3970,6 +3970,17 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .props = (PropValue[]) { + { "ss", "on" }, + { "tsc-adjust", "on" }, + { "cldemote", "on" }, + { "movdiri", "on" }, + { "movdir64b", "on" }, + { /* end of list */ } + } + }, { /* end of list */ } } }, From 5e9efe31f1de05c8c65a7383fcbf211ebded6a22 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:18:18 +0200 Subject: [PATCH 0382/2884] bitmap: Use g_try_new0/g_new0/g_renew Avoids an explicit use of sizeof(). The GLib allocation macros ensure that the multiplication by the size of the element uses the right type and does not overflow. While at it, change bitmap_new() to use g_new0 directly. Its current impl of calling bitmap_try_new() followed by a plain abort() has worse diagnostics than g_new0, which uses g_error to report the actual allocation size that failed. Cc: qemu-trivial@nongnu.org Cc: Roman Kiryanov Reviewed-by: Daniel Berrange Signed-off-by: Paolo Bonzini --- include/qemu/bitmap.h | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 97806811ee..1cf288445f 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -92,17 +92,14 @@ long slow_bitmap_count_one(const unsigned long *bitmap, long nbits); static inline unsigned long *bitmap_try_new(long nbits) { - long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); - return g_try_malloc0(len); + long nelem = BITS_TO_LONGS(nbits); + return g_try_new0(unsigned long, nelem); } static inline unsigned long *bitmap_new(long nbits) { - unsigned long *ptr = bitmap_try_new(nbits); - if (ptr == NULL) { - abort(); - } - return ptr; + long nelem = BITS_TO_LONGS(nbits); + return g_new0(unsigned long, nelem); } static inline void bitmap_zero(unsigned long *dst, long nbits) @@ -265,10 +262,10 @@ unsigned long bitmap_find_next_zero_area(unsigned long *map, static inline unsigned long *bitmap_zero_extend(unsigned long *old, long old_nbits, long new_nbits) { - long new_len = BITS_TO_LONGS(new_nbits) * sizeof(unsigned long); - unsigned long *new = g_realloc(old, new_len); - bitmap_clear(new, old_nbits, new_nbits - old_nbits); - return new; + long new_nelem = BITS_TO_LONGS(new_nbits); + unsigned long *ptr = g_renew(unsigned long, old, new_nelem); + bitmap_clear(ptr, old_nbits, new_nbits - old_nbits); + return ptr; } void bitmap_to_le(unsigned long *dst, const unsigned long *src, From 8a161d08c92f3fee01e97ede54917abbf7085b24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 18:50:14 +0200 Subject: [PATCH 0383/2884] build: do not build virtio-vga-gl if virgl/opengl not available If virgl and opengl are not available, the build process creates a useless libvirtio-vga-gl module that does not have any device in it. Follow the example of virtio-vga-rutabaga and do not build the module at all in that case. Signed-off-by: Paolo Bonzini --- hw/display/meson.build | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/display/meson.build b/hw/display/meson.build index 7893b94c8e..7db05eace9 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -125,12 +125,14 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA') if_false: files('acpi-vga-stub.c')) hw_display_modules += {'virtio-vga': virtio_vga_ss} - virtio_vga_gl_ss = ss.source_set() - virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl], - if_true: [files('virtio-vga-gl.c'), pixman]) - virtio_vga_gl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), - if_false: files('acpi-vga-stub.c')) - hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss} + if virgl.found() and opengl.found() + virtio_vga_gl_ss = ss.source_set() + virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl], + if_true: [files('virtio-vga-gl.c'), pixman]) + virtio_vga_gl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + if_false: files('acpi-vga-stub.c')) + hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss} + endif if rutabaga.found() virtio_vga_rutabaga_ss = ss.source_set() From c71a42b51a17978c4d13dca12365b651a95aaa8a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:57:08 +0200 Subject: [PATCH 0384/2884] fw_cfg: remove useless declarations from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only FWCfgState is used as part of APIs such as acpi_ghes_add_fw_cfg. Everything else need not be in typedefs.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/hw/nvram/fw_cfg.h | 2 ++ include/qemu/typedefs.h | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index c1f81a5f13..d173998803 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -59,6 +59,8 @@ typedef struct fw_cfg_dma_access FWCfgDmaAccess; typedef void (*FWCfgCallback)(void *opaque); typedef void (*FWCfgWriteCallback)(void *opaque, off_t start, size_t len); +typedef struct FWCfgEntry FWCfgEntry; + struct FWCfgState { /*< private >*/ SysBusDevice parent_obj; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 50c277cf0b..949d3e1daf 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -56,9 +56,6 @@ typedef struct DumpState DumpState; typedef struct Error Error; typedef struct EventNotifier EventNotifier; typedef struct FlatView FlatView; -typedef struct FWCfgEntry FWCfgEntry; -typedef struct FWCfgIoState FWCfgIoState; -typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; typedef struct GraphicHwOps GraphicHwOps; typedef struct HostMemoryBackend HostMemoryBackend; From 667cdad031bf54d5ea9c1f3833280b2e8674d788 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:57:08 +0200 Subject: [PATCH 0385/2884] qdev-core: remove DeviceListener from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is needed in very few places, which already depend on other parts of qdev-core.h files. The benefit of having it in typedefs.h is small. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/hw/qdev-core.h | 1 + include/qemu/typedefs.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 9228e96c87..5336728a23 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -294,6 +294,7 @@ struct DeviceState { MemReentrancyGuard mem_reentrancy_guard; }; +typedef struct DeviceListener DeviceListener; struct DeviceListener { void (*realize)(DeviceListener *listener, DeviceState *dev); void (*unrealize)(DeviceListener *listener, DeviceState *dev); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 949d3e1daf..66f0b146c8 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -47,7 +47,6 @@ typedef struct CpuInfoFast CpuInfoFast; typedef struct CPUJumpCache CPUJumpCache; typedef struct CPUState CPUState; typedef struct CPUTLBEntryFull CPUTLBEntryFull; -typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; typedef struct DisplayChangeListener DisplayChangeListener; From 6b30674dadc010d97bf5154d2ce417978e51f608 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:54:27 +0200 Subject: [PATCH 0386/2884] numa: remove types from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exactly nobody needs them there. Place the typedef in the header that defines the struct. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/qemu/typedefs.h | 2 -- include/sysemu/numa.h | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 66f0b146c8..e0a0bc31e7 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -81,8 +81,6 @@ typedef struct MSIMessage MSIMessage; typedef struct NetClientState NetClientState; typedef struct NetFilterState NetFilterState; typedef struct NICInfo NICInfo; -typedef struct NodeInfo NodeInfo; -typedef struct NumaNodeMem NumaNodeMem; typedef struct Object Object; typedef struct ObjectClass ObjectClass; typedef struct PCIBridge PCIBridge; diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index 825cfe86bc..0467614147 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -36,7 +36,7 @@ enum { #define UINT16_BITS 16 -struct NodeInfo { +typedef struct NodeInfo { uint64_t node_mem; struct HostMemoryBackend *node_memdev; bool present; @@ -45,12 +45,12 @@ struct NodeInfo { uint8_t lb_info_provided; uint16_t initiator; uint8_t distance[MAX_NODES]; -}; +} NodeInfo; -struct NumaNodeMem { +typedef struct NumaNodeMem { uint64_t node_mem; uint64_t node_plugged_mem; -}; +} NumaNodeMem; struct HMAT_LB_Data { uint8_t initiator; From 2d3f409631f087a7caa26d03c3b22fbcafc55cc8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:55:22 +0200 Subject: [PATCH 0387/2884] net: remove AnnounceTimer from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exactly nobody needs it there. Place the typedef in the header that defines the struct. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/net/announce.h | 4 ++-- include/qemu/typedefs.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/net/announce.h b/include/net/announce.h index 3d90c83c23..72e7e501f7 100644 --- a/include/net/announce.h +++ b/include/net/announce.h @@ -12,12 +12,12 @@ #include "qapi/qapi-types-net.h" #include "qemu/timer.h" -struct AnnounceTimer { +typedef struct AnnounceTimer { QEMUTimer *tm; AnnounceParameters params; QEMUClockType type; int round; -}; +} AnnounceTimer; /* Returns: update the timer to the next time point */ int64_t qemu_announce_timer_step(AnnounceTimer *timer); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index e0a0bc31e7..520f421397 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -27,7 +27,6 @@ typedef struct AdapterInfo AdapterInfo; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; typedef struct Aml Aml; -typedef struct AnnounceTimer AnnounceTimer; typedef struct ArchCPU ArchCPU; typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; From 0f73e49e3718fe198bfad0d5143482708bf086a1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:32:25 +0200 Subject: [PATCH 0388/2884] qemu-option: remove QemuOpt from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QemuOpt is basically an internal data structure. It has no business being defined except if you need functions from include/qemu/option.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/qemu/option.h | 2 ++ include/qemu/typedefs.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/qemu/option.h b/include/qemu/option.h index b349828782..01e673ae03 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -54,6 +54,8 @@ enum QemuOptType { QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */ }; +typedef struct QemuOpt QemuOpt; + typedef struct QemuOptDesc { const char *name; enum QemuOptType type; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 520f421397..4519f0cd61 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -108,7 +108,6 @@ typedef struct QEMUCursor QEMUCursor; typedef struct QEMUFile QEMUFile; typedef struct QemuLockable QemuLockable; typedef struct QemuMutex QemuMutex; -typedef struct QemuOpt QemuOpt; typedef struct QemuOpts QemuOpts; typedef struct QemuOptsList QemuOptsList; typedef struct QEMUSGList QEMUSGList; From a42706dbe4f14416df5c65de1a90d72cbc338530 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:32:25 +0200 Subject: [PATCH 0389/2884] intc: remove PICCommonState from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move it to the existing "PIC related things" header, hw/intc/i8259.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/hw/intc/i8259.h | 2 ++ include/qemu/typedefs.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/intc/i8259.h b/include/hw/intc/i8259.h index c412575775..1f2420231f 100644 --- a/include/hw/intc/i8259.h +++ b/include/hw/intc/i8259.h @@ -3,6 +3,8 @@ /* i8259.c */ +typedef struct PICCommonState PICCommonState; + extern PICCommonState *isa_pic; /* diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 4519f0cd61..090e219248 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -96,7 +96,6 @@ typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; typedef struct PCIHostState PCIHostState; -typedef struct PICCommonState PICCommonState; typedef struct PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; typedef struct PropertyInfo PropertyInfo; From 13d110944831e8fb15087697d540d0922ea355f4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:32:25 +0200 Subject: [PATCH 0390/2884] lockable: remove QemuLockable from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using QemuLockable almost always requires going through QEMU_MAKE_LOCKABLE(). Therefore, there is little point in having the typedef always present. Move it to lockable.h, with only a small adjustment to coroutine.h (which has a tricky co-dependency with lockable.h due to defining CoMutex *and* using QemuLockable as a part of the CoQueue API). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/qemu/coroutine.h | 4 ++-- include/qemu/lockable.h | 4 ++-- include/qemu/typedefs.h | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index e6aff45301..ff3084538b 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -84,6 +84,8 @@ static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex) mutex->holder == qemu_coroutine_self()); } +#include "qemu/lockable.h" + /** * CoQueues are a mechanism to queue coroutines in order to continue executing * them later. They are similar to condition variables, but they need help @@ -281,8 +283,6 @@ void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size); */ void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size); -#include "qemu/lockable.h" - /** * Sends a (part of) iovec down a socket, yielding when the socket is full, or * Receives data into a (part of) iovec from a socket, diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 9823220446..62110d2eb7 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -18,11 +18,11 @@ typedef void QemuLockUnlockFunc(void *); -struct QemuLockable { +typedef struct QemuLockable { void *object; QemuLockUnlockFunc *lock; QemuLockUnlockFunc *unlock; -}; +} QemuLockable; static inline __attribute__((__always_inline__)) QemuLockable * qemu_make_lockable(void *x, QemuLockable *lockable) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 090e219248..ab24ca2876 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -105,7 +105,6 @@ typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; typedef struct QEMUCursor QEMUCursor; typedef struct QEMUFile QEMUFile; -typedef struct QemuLockable QemuLockable; typedef struct QemuMutex QemuMutex; typedef struct QemuOpts QemuOpts; typedef struct QemuOptsList QemuOptsList; From a0d645100eba45a96f009528ec0fec14b4b35956 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 17:18:25 +0200 Subject: [PATCH 0391/2884] migration: remove PostcopyDiscardState from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is defined and referred to exclusively from a .c file. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/qemu/typedefs.h | 1 - migration/postcopy-ram.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index ab24ca2876..2b1948a19a 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -96,7 +96,6 @@ typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; typedef struct PCIHostState PCIHostState; -typedef struct PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; typedef struct PropertyInfo PropertyInfo; typedef struct QBool QBool; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index eccff499cb..3419779548 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -44,7 +44,7 @@ */ #define MAX_DISCARDS_PER_COMMAND 12 -struct PostcopyDiscardState { +typedef struct PostcopyDiscardState { const char *ramblock_name; uint16_t cur_entry; /* @@ -54,7 +54,7 @@ struct PostcopyDiscardState { uint64_t length_list[MAX_DISCARDS_PER_COMMAND]; unsigned int nsentwords; unsigned int nsentcmds; -}; +} PostcopyDiscardState; static NotifierWithReturnList postcopy_notifier_list; From f37c6c2e89d3b8d3376ddc74b5357e56f49ccb9c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 17:09:39 +0200 Subject: [PATCH 0392/2884] monitor: remove MonitorDef from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MonitorDef is defined by hmp-target.h, and all users except one already include it; the reason why the stubs do not include it, is because hmp-target.h currently can only be used in files that are compiled per target. However, that is easily fixed. Because the benefit of having MonitorDef in typedefs.h is very small, do it and remove the type from typedefs.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- include/monitor/hmp-target.h | 11 +++++++---- include/qemu/typedefs.h | 1 - stubs/target-monitor-defs.c | 3 +-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index d78e979f05..b679aaebbf 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -25,11 +25,10 @@ #ifndef MONITOR_HMP_TARGET_H #define MONITOR_HMP_TARGET_H +typedef struct MonitorDef MonitorDef; + +#ifdef COMPILING_PER_TARGET #include "cpu.h" - -#define MD_TLONG 0 -#define MD_I32 1 - struct MonitorDef { const char *name; int offset; @@ -37,6 +36,10 @@ struct MonitorDef { int val); int type; }; +#endif + +#define MD_TLONG 0 +#define MD_I32 1 const MonitorDef *target_monitor_defs(void); int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 2b1948a19a..b71a36d02b 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -75,7 +75,6 @@ typedef struct MemoryRegionSection MemoryRegionSection; typedef struct MigrationIncomingState MigrationIncomingState; typedef struct MigrationState MigrationState; typedef struct Monitor Monitor; -typedef struct MonitorDef MonitorDef; typedef struct MSIMessage MSIMessage; typedef struct NetClientState NetClientState; typedef struct NetFilterState NetFilterState; diff --git a/stubs/target-monitor-defs.c b/stubs/target-monitor-defs.c index ac07b19064..35a0a34277 100644 --- a/stubs/target-monitor-defs.c +++ b/stubs/target-monitor-defs.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" - -const MonitorDef *target_monitor_defs(void); +#include "monitor/hmp-target.h" const MonitorDef *target_monitor_defs(void) { From 8fb1435c2291e9061024461fac00e1cf54777f44 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:57:08 +0200 Subject: [PATCH 0393/2884] qapi/machine: remove types from typedefs.h They are needed in very few places, which already depends on other generated QAPI files. The benefit of having these types in typedefs.h is small. Signed-off-by: Paolo Bonzini --- include/hw/core/cpu.h | 1 + include/qemu/typedefs.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 46b99a7ea5..a23d39f6a0 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -28,6 +28,7 @@ #include "exec/memattrs.h" #include "exec/mmu-access-type.h" #include "exec/tlb-common.h" +#include "qapi/qapi-types-machine.h" #include "qapi/qapi-types-run-state.h" #include "qemu/bitmap.h" #include "qemu/rcu_queue.h" diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index b71a36d02b..78598f27f3 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -37,12 +37,10 @@ typedef struct BusClass BusClass; typedef struct BusState BusState; typedef struct Chardev Chardev; typedef struct Clock Clock; -typedef struct CompatProperty CompatProperty; typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUArchState CPUArchState; typedef struct CPUPluginState CPUPluginState; -typedef struct CpuInfoFast CpuInfoFast; typedef struct CPUJumpCache CPUJumpCache; typedef struct CPUState CPUState; typedef struct CPUTLBEntryFull CPUTLBEntryFull; From da4b248178b51b8dff26de5e3c4ea8ea4b53f5d1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 16:49:46 +0200 Subject: [PATCH 0394/2884] display: remove GraphicHwOps from typedefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basically all uses of GraphicHwOps are defining an instance of it, which requires the full definition of the struct. It is pointless to have it in typedefs.h. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/display/vga_int.h | 1 + include/qemu/typedefs.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 876a1d3697..f77c1c1145 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -25,6 +25,7 @@ #ifndef HW_VGA_INT_H #define HW_VGA_INT_H +#include "ui/console.h" #include "exec/ioport.h" #include "exec/memory.h" diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 78598f27f3..7e3597e94c 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -53,7 +53,6 @@ typedef struct Error Error; typedef struct EventNotifier EventNotifier; typedef struct FlatView FlatView; typedef struct FWCfgState FWCfgState; -typedef struct GraphicHwOps GraphicHwOps; typedef struct HostMemoryBackend HostMemoryBackend; typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; From 15d62536a9ec78db9ab07b113e5e07a6e02e52fb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 17:14:42 +0200 Subject: [PATCH 0395/2884] tcg: remove CPU* types from typedefs.h hw/core/cpu.h is already using struct forward declarations in some cases to avoid inclusions, and otherwise CPUAddressSpace and CPUJumpCache are only used together with their definition. CPUTLBEntryFull is always used when their definition is available. Remove all three from typedefs.h. Signed-off-by: Paolo Bonzini --- accel/tcg/tb-jmp-cache.h | 4 ++-- include/hw/core/cpu.h | 10 ++++++++-- include/qemu/typedefs.h | 3 --- system/physmem.c | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h index 184bb3e3e2..c3a505e394 100644 --- a/accel/tcg/tb-jmp-cache.h +++ b/accel/tcg/tb-jmp-cache.h @@ -22,12 +22,12 @@ * non-NULL value of 'tb'. Strictly speaking pc is only needed for * CF_PCREL, but it's used always for simplicity. */ -struct CPUJumpCache { +typedef struct CPUJumpCache { struct rcu_head rcu; struct { TranslationBlock *tb; vaddr pc; } array[TB_JMP_CACHE_SIZE]; -}; +} CPUJumpCache; #endif /* ACCEL_TCG_TB_JMP_CACHE_H */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index a23d39f6a0..1382a98615 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -85,6 +85,12 @@ DECLARE_CLASS_CHECKERS(CPUClass, CPU, typedef struct CPUWatchpoint CPUWatchpoint; +/* see physmem.c */ +struct CPUAddressSpace; + +/* see accel/tcg/tb-jmp-cache.h */ +struct CPUJumpCache; + /* see accel-cpu.h */ struct AccelCPUClass; @@ -473,12 +479,12 @@ struct CPUState { QemuMutex work_mutex; QSIMPLEQ_HEAD(, qemu_work_item) work_list; - CPUAddressSpace *cpu_ases; + struct CPUAddressSpace *cpu_ases; int num_ases; AddressSpace *as; MemoryRegion *memory; - CPUJumpCache *tb_jmp_cache; + struct CPUJumpCache *tb_jmp_cache; GArray *gdb_regs; int gdb_num_regs; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 7e3597e94c..d23020ed23 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -38,12 +38,9 @@ typedef struct BusState BusState; typedef struct Chardev Chardev; typedef struct Clock Clock; typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; -typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUArchState CPUArchState; typedef struct CPUPluginState CPUPluginState; -typedef struct CPUJumpCache CPUJumpCache; typedef struct CPUState CPUState; -typedef struct CPUTLBEntryFull CPUTLBEntryFull; typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; typedef struct DisplayChangeListener DisplayChangeListener; diff --git a/system/physmem.c b/system/physmem.c index 1a81c226ba..6dc58b34bb 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -158,12 +158,12 @@ static void tcg_commit(MemoryListener *listener); * @memory_dispatch: its dispatch pointer (cached, RCU protected) * @tcg_as_listener: listener for tracking changes to the AddressSpace */ -struct CPUAddressSpace { +typedef struct CPUAddressSpace { CPUState *cpu; AddressSpace *as; struct AddressSpaceDispatch *memory_dispatch; MemoryListener tcg_as_listener; -}; +} CPUAddressSpace; struct DirtyBitmapSnapshot { ram_addr_t start; From fe5943fecc7c9a55d975e9e55caf527057a94c37 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 17:17:04 +0200 Subject: [PATCH 0396/2884] pci: remove some types from typedefs.h For types that are embedded in structs defined by pci.h, the definition is pretty much required to be available. Remove them from typedefs.h. Signed-off-by: Paolo Bonzini --- include/hw/pci/pcie.h | 3 +++ include/hw/pci/pcie_aer.h | 38 ++++++++++++++++++------------------- include/hw/pci/pcie_sriov.h | 8 ++++---- include/qemu/typedefs.h | 5 ----- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 11f5a91bbb..5eddb90976 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -27,6 +27,9 @@ #include "hw/pci/pcie_sriov.h" #include "hw/hotplug.h" +typedef struct PCIEPort PCIEPort; +typedef struct PCIESlot PCIESlot; + typedef enum { /* these bits must match the bits in Slot Control/Status registers. * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h index 4a9f0ea69d..4d8c0e0507 100644 --- a/include/hw/pci/pcie_aer.h +++ b/include/hw/pci/pcie_aer.h @@ -25,8 +25,23 @@ /* definitions which PCIExpressDevice uses */ +/* error */ +typedef struct PCIEAERErr { + uint32_t status; /* error status bits */ + uint16_t source_id; /* bdf */ + +#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ +#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ +#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ +#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ + uint16_t flags; + + uint32_t header[4]; /* TLP header */ + uint32_t prefix[4]; /* TLP header prefix */ +} PCIEAERErr; + /* AER log */ -struct PCIEAERLog { +typedef struct PCIEAERLog { /* This structure is saved/loaded. So explicitly size them instead of unsigned int */ @@ -48,11 +63,11 @@ struct PCIEAERLog { /* Error log. log_max-sized array */ PCIEAERErr *log; -}; +} PCIEAERLog; /* aer error message: error signaling message has only error severity and source id. See 2.2.8.3 error signaling messages */ -struct PCIEAERMsg { +typedef struct PCIEAERMsg { /* * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE} @@ -60,7 +75,7 @@ struct PCIEAERMsg { uint32_t severity; uint16_t source_id; /* bdf */ -}; +} PCIEAERMsg; static inline bool pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) @@ -69,21 +84,6 @@ pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN; } -/* error */ -struct PCIEAERErr { - uint32_t status; /* error status bits */ - uint16_t source_id; /* bdf */ - -#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ -#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ -#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ -#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ - uint16_t flags; - - uint32_t header[4]; /* TLP header */ - uint32_t prefix[4]; /* TLP header prefix */ -}; - extern const VMStateDescription vmstate_pcie_aer_log; int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index b77eb7bf58..450cbef6c2 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -15,17 +15,17 @@ #include "hw/pci/pci.h" -struct PCIESriovPF { +typedef struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ const char *vfname; /* Reference to the device type used for the VFs */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ -}; +} PCIESriovPF; -struct PCIESriovVF { +typedef struct PCIESriovVF { PCIDevice *pf; /* Pointer back to owner physical function */ uint16_t vf_number; /* Logical VF number of this function */ -}; +} PCIESriovVF; void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index d23020ed23..5d999e20d7 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -78,13 +78,8 @@ typedef struct ObjectClass ObjectClass; typedef struct PCIBridge PCIBridge; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; -typedef struct PCIEAERErr PCIEAERErr; -typedef struct PCIEAERLog PCIEAERLog; -typedef struct PCIEAERMsg PCIEAERMsg; typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; -typedef struct PCIESriovPF PCIESriovPF; -typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; From 48663349813144224d2b1cb6a85a893c2aa901ad Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 May 2024 17:48:26 +0200 Subject: [PATCH 0397/2884] kvm: move target-dependent interrupt routing out of kvm-all.c Let hw/hyperv/hyperv.c and hw/intc/s390_flic.c handle (respectively) SynIC and adapter routes, removing the code from target-independent files. This also removes the only occurrence of AdapterInfo outside s390 code, so remove that from typedefs.h. Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 62 ++------------------------------------ hw/hyperv/hyperv.c | 25 +++++++++++++++ hw/intc/s390_flic_kvm.c | 28 +++++++++++++++++ include/hw/s390x/adapter.h | 4 +-- include/qemu/typedefs.h | 1 - include/sysemu/kvm.h | 5 +-- 6 files changed, 61 insertions(+), 64 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d7281b93f3..c0be9f5eed 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1909,8 +1909,8 @@ void kvm_irqchip_commit_routes(KVMState *s) assert(ret == 0); } -static void kvm_add_routing_entry(KVMState *s, - struct kvm_irq_routing_entry *entry) +void kvm_add_routing_entry(KVMState *s, + struct kvm_irq_routing_entry *entry) { struct kvm_irq_routing_entry *new; int n, size; @@ -2007,7 +2007,7 @@ void kvm_irqchip_change_notify(void) notifier_list_notify(&kvm_irqchip_change_notifiers, NULL); } -static int kvm_irqchip_get_virq(KVMState *s) +int kvm_irqchip_get_virq(KVMState *s) { int next_virq; @@ -2165,62 +2165,6 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event, return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); } -int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) -{ - struct kvm_irq_routing_entry kroute = {}; - int virq; - - if (!kvm_gsi_routing_enabled()) { - return -ENOSYS; - } - - virq = kvm_irqchip_get_virq(s); - if (virq < 0) { - return virq; - } - - kroute.gsi = virq; - kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; - kroute.flags = 0; - kroute.u.adapter.summary_addr = adapter->summary_addr; - kroute.u.adapter.ind_addr = adapter->ind_addr; - kroute.u.adapter.summary_offset = adapter->summary_offset; - kroute.u.adapter.ind_offset = adapter->ind_offset; - kroute.u.adapter.adapter_id = adapter->adapter_id; - - kvm_add_routing_entry(s, &kroute); - - return virq; -} - -int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint) -{ - struct kvm_irq_routing_entry kroute = {}; - int virq; - - if (!kvm_gsi_routing_enabled()) { - return -ENOSYS; - } - if (!kvm_check_extension(s, KVM_CAP_HYPERV_SYNIC)) { - return -ENOSYS; - } - virq = kvm_irqchip_get_virq(s); - if (virq < 0) { - return virq; - } - - kroute.gsi = virq; - kroute.type = KVM_IRQ_ROUTING_HV_SINT; - kroute.flags = 0; - kroute.u.hv_sint.vcpu = vcpu; - kroute.u.hv_sint.sint = sint; - - kvm_add_routing_entry(s, &kroute); - kvm_irqchip_commit_routes(s); - - return virq; -} - #else /* !KVM_CAP_IRQ_ROUTING */ void kvm_init_irq_routing(KVMState *s) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 3ea54ba818..483dcca308 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -373,6 +373,31 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) return ret; } +static int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint) +{ + struct kvm_irq_routing_entry kroute = {}; + int virq; + + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + virq = kvm_irqchip_get_virq(s); + if (virq < 0) { + return virq; + } + + kroute.gsi = virq; + kroute.type = KVM_IRQ_ROUTING_HV_SINT; + kroute.flags = 0; + kroute.u.hv_sint.vcpu = vcpu; + kroute.u.hv_sint.sint = sint; + + kvm_add_routing_entry(s, &kroute); + kvm_irqchip_commit_routes(s); + + return virq; +} + HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintMsgCb cb, void *cb_data) { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index baaa30dcb7..330f08dfdc 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -324,6 +324,34 @@ static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, return r ? -errno : 0; } +static int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) +{ + struct kvm_irq_routing_entry kroute = {}; + int virq; + + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + + virq = kvm_irqchip_get_virq(s); + if (virq < 0) { + return virq; + } + + kroute.gsi = virq; + kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; + kroute.flags = 0; + kroute.u.adapter.summary_addr = adapter->summary_addr; + kroute.u.adapter.ind_addr = adapter->ind_addr; + kroute.u.adapter.summary_offset = adapter->summary_offset; + kroute.u.adapter.ind_offset = adapter->ind_offset; + kroute.u.adapter.adapter_id = adapter->adapter_id; + + kvm_add_routing_entry(s, &kroute); + + return virq; +} + static int kvm_s390_add_adapter_routes(S390FLICState *fs, AdapterRoutes *routes) { diff --git a/include/hw/s390x/adapter.h b/include/hw/s390x/adapter.h index 7f1703508c..d4fadc4f7f 100644 --- a/include/hw/s390x/adapter.h +++ b/include/hw/s390x/adapter.h @@ -12,12 +12,12 @@ #ifndef S390X_ADAPTER_H #define S390X_ADAPTER_H -struct AdapterInfo { +typedef struct AdapterInfo { uint64_t ind_addr; uint64_t summary_addr; uint64_t ind_offset; uint32_t summary_offset; uint32_t adapter_id; -}; +} AdapterInfo; #endif diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 5d999e20d7..2ff50bf597 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -23,7 +23,6 @@ */ typedef struct AccelCPUState AccelCPUState; typedef struct AccelState AccelState; -typedef struct AdapterInfo AdapterInfo; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; typedef struct Aml Aml; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index eaf801bc93..c31d9c7356 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -470,10 +470,11 @@ static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c) } } +int kvm_irqchip_get_virq(KVMState *s); void kvm_irqchip_release_virq(KVMState *s, int virq); -int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter); -int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint); +void kvm_add_routing_entry(KVMState *s, + struct kvm_irq_routing_entry *entry); int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq); From 9608723abb11b2cf81cdaa33e13cc888c1d9fe85 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 May 2024 10:57:41 +0200 Subject: [PATCH 0398/2884] migration: do not include coroutine_int.h Migration code needs no private fields of the coroutine backend. Include the "regular" coroutine.h header. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- migration/migration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration.h b/migration/migration.h index 8045e39c26..6c612c0381 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -19,7 +19,7 @@ #include "qapi/qapi-types-migration.h" #include "qapi/qmp/json-writer.h" #include "qemu/thread.h" -#include "qemu/coroutine_int.h" +#include "qemu/coroutine.h" #include "io/channel.h" #include "io/channel-buffer.h" #include "net/announce.h" From 8a917b99d5394d34ffcd851c8b287ced6eb48133 Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Tue, 6 Feb 2024 23:48:04 +0300 Subject: [PATCH 0399/2884] util/bufferiszero: Remove SSE4.1 variant The SSE4.1 variant is virtually identical to the SSE2 variant, except for using 'PTEST+JNZ' in place of 'PCMPEQB+PMOVMSKB+CMP+JNE' for testing if an SSE register is all zeroes. The PTEST instruction decodes to two uops, so it can be handled only by the complex decoder, and since CMP+JNE are macro-fused, both sequences decode to three uops. The uops comprising the PTEST instruction dispatch to p0 and p5 on Intel CPUs, so PCMPEQB+PMOVMSKB is comparatively more flexible from dispatch standpoint. Hence, the use of PTEST brings no benefit from throughput standpoint. Its latency is not important, since it feeds only a conditional jump, which terminates the dependency chain. I never observed PTEST variants to be faster on real hardware. Signed-off-by: Alexander Monakov Signed-off-by: Mikhail Romanov Reviewed-by: Richard Henderson Message-Id: <20240206204809.9859-2-amonakov@ispras.ru> --- util/bufferiszero.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 3e6a5dfd63..f5a3634f9a 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -100,34 +100,6 @@ buffer_zero_sse2(const void *buf, size_t len) } #ifdef CONFIG_AVX2_OPT -static bool __attribute__((target("sse4"))) -buffer_zero_sse4(const void *buf, size_t len) -{ - __m128i t = _mm_loadu_si128(buf); - __m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16); - __m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16); - - /* Loop over 16-byte aligned blocks of 64. */ - while (likely(p <= e)) { - __builtin_prefetch(p); - if (unlikely(!_mm_testz_si128(t, t))) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } - - /* Finish the aligned tail. */ - t |= e[-3]; - t |= e[-2]; - t |= e[-1]; - - /* Finish the unaligned tail. */ - t |= _mm_loadu_si128(buf + len - 16); - - return _mm_testz_si128(t, t); -} - static bool __attribute__((target("avx2"))) buffer_zero_avx2(const void *buf, size_t len) { @@ -221,7 +193,6 @@ select_accel_cpuinfo(unsigned info) #endif #ifdef CONFIG_AVX2_OPT { CPUINFO_AVX2, 128, buffer_zero_avx2 }, - { CPUINFO_SSE4, 64, buffer_zero_sse4 }, #endif { CPUINFO_SSE2, 64, buffer_zero_sse2 }, { CPUINFO_ALWAYS, 0, buffer_zero_int }, From d018425c324704949c7f65230def9586e71f07f5 Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Wed, 14 Feb 2024 10:34:24 -1000 Subject: [PATCH 0400/2884] util/bufferiszero: Remove AVX512 variant Thanks to early checks in the inline buffer_is_zero wrapper, the SIMD routines are invoked much more rarely in normal use when most buffers are non-zero. This makes use of AVX512 unprofitable, as it incurs extra frequency and voltage transition periods during which the CPU operates at reduced performance, as described in https://travisdowns.github.io/blog/2020/01/17/avxfreq1.html Signed-off-by: Mikhail Romanov Signed-off-by: Alexander Monakov Reviewed-by: Richard Henderson Message-Id: <20240206204809.9859-4-amonakov@ispras.ru> Signed-off-by: Richard Henderson --- util/bufferiszero.c | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index f5a3634f9a..641d5f9b9e 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -64,7 +64,7 @@ buffer_zero_int(const void *buf, size_t len) } } -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__) +#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) #include /* Note that each of these vectorized functions require len >= 64. */ @@ -128,41 +128,12 @@ buffer_zero_avx2(const void *buf, size_t len) } #endif /* CONFIG_AVX2_OPT */ -#ifdef CONFIG_AVX512F_OPT -static bool __attribute__((target("avx512f"))) -buffer_zero_avx512(const void *buf, size_t len) -{ - /* Begin with an unaligned head of 64 bytes. */ - __m512i t = _mm512_loadu_si512(buf); - __m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64); - __m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64); - - /* Loop over 64-byte aligned blocks of 256. */ - while (p <= e) { - __builtin_prefetch(p); - if (unlikely(_mm512_test_epi64_mask(t, t))) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } - - t |= _mm512_loadu_si512(buf + len - 4 * 64); - t |= _mm512_loadu_si512(buf + len - 3 * 64); - t |= _mm512_loadu_si512(buf + len - 2 * 64); - t |= _mm512_loadu_si512(buf + len - 1 * 64); - - return !_mm512_test_epi64_mask(t, t); - -} -#endif /* CONFIG_AVX512F_OPT */ - /* * Make sure that these variables are appropriately initialized when * SSE2 is enabled on the compiler command-line, but the compiler is * too old to support CONFIG_AVX2_OPT. */ -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) +#if defined(CONFIG_AVX2_OPT) # define INIT_USED 0 # define INIT_LENGTH 0 # define INIT_ACCEL buffer_zero_int @@ -188,9 +159,6 @@ select_accel_cpuinfo(unsigned info) unsigned len; bool (*fn)(const void *, size_t); } all[] = { -#ifdef CONFIG_AVX512F_OPT - { CPUINFO_AVX512F, 256, buffer_zero_avx512 }, -#endif #ifdef CONFIG_AVX2_OPT { CPUINFO_AVX2, 128, buffer_zero_avx2 }, #endif @@ -208,7 +176,7 @@ select_accel_cpuinfo(unsigned info) return 0; } -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) +#if defined(CONFIG_AVX2_OPT) static void __attribute__((constructor)) init_accel(void) { used_accel = select_accel_cpuinfo(cpuinfo_init()); From cbe3d5264631aa193fd2705820cbde6c5a602abb Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Tue, 6 Feb 2024 23:48:05 +0300 Subject: [PATCH 0401/2884] util/bufferiszero: Reorganize for early test for acceleration Test for length >= 256 inline, where is is often a constant. Before calling into the accelerated routine, sample three bytes from the buffer, which handles most non-zero buffers. Signed-off-by: Alexander Monakov Signed-off-by: Mikhail Romanov Message-Id: <20240206204809.9859-3-amonakov@ispras.ru> [rth: Use __builtin_constant_p; move the indirect call out of line.] Signed-off-by: Richard Henderson --- include/qemu/cutils.h | 32 ++++++++++++++++- util/bufferiszero.c | 84 +++++++++++++++++-------------------------- 2 files changed, 63 insertions(+), 53 deletions(-) diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 92c927a6a3..741dade7cf 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -187,9 +187,39 @@ char *freq_to_str(uint64_t freq_hz); /* used to print char* safely */ #define STR_OR_NULL(str) ((str) ? (str) : "null") -bool buffer_is_zero(const void *buf, size_t len); +/* + * Check if a buffer is all zeroes. + */ + +bool buffer_is_zero_ool(const void *vbuf, size_t len); +bool buffer_is_zero_ge256(const void *vbuf, size_t len); bool test_buffer_is_zero_next_accel(void); +static inline bool buffer_is_zero_sample3(const char *buf, size_t len) +{ + /* + * For any reasonably sized buffer, these three samples come from + * three different cachelines. In qemu-img usage, we find that + * each byte eliminates more than half of all buffer testing. + * It is therefore critical to performance that the byte tests + * short-circuit, so that we do not pull in additional cache lines. + * Do not "optimize" this to !(a | b | c). + */ + return !buf[0] && !buf[len - 1] && !buf[len / 2]; +} + +#ifdef __OPTIMIZE__ +static inline bool buffer_is_zero(const void *buf, size_t len) +{ + return (__builtin_constant_p(len) && len >= 256 + ? buffer_is_zero_sample3(buf, len) && + buffer_is_zero_ge256(buf, len) + : buffer_is_zero_ool(buf, len)); +} +#else +#define buffer_is_zero buffer_is_zero_ool +#endif + /* * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) * Input is limited to 14-bit numbers diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 641d5f9b9e..972f394cbd 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -26,8 +26,9 @@ #include "qemu/bswap.h" #include "host/cpuinfo.h" -static bool -buffer_zero_int(const void *buf, size_t len) +static bool (*buffer_is_zero_accel)(const void *, size_t); + +static bool buffer_is_zero_integer(const void *buf, size_t len) { if (unlikely(len < 8)) { /* For a very small buffer, simply accumulate all the bytes. */ @@ -128,60 +129,38 @@ buffer_zero_avx2(const void *buf, size_t len) } #endif /* CONFIG_AVX2_OPT */ -/* - * Make sure that these variables are appropriately initialized when - * SSE2 is enabled on the compiler command-line, but the compiler is - * too old to support CONFIG_AVX2_OPT. - */ -#if defined(CONFIG_AVX2_OPT) -# define INIT_USED 0 -# define INIT_LENGTH 0 -# define INIT_ACCEL buffer_zero_int -#else -# ifndef __SSE2__ -# error "ISA selection confusion" -# endif -# define INIT_USED CPUINFO_SSE2 -# define INIT_LENGTH 64 -# define INIT_ACCEL buffer_zero_sse2 -#endif - -static unsigned used_accel = INIT_USED; -static unsigned length_to_accel = INIT_LENGTH; -static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; - static unsigned __attribute__((noinline)) select_accel_cpuinfo(unsigned info) { /* Array is sorted in order of algorithm preference. */ static const struct { unsigned bit; - unsigned len; bool (*fn)(const void *, size_t); } all[] = { #ifdef CONFIG_AVX2_OPT - { CPUINFO_AVX2, 128, buffer_zero_avx2 }, + { CPUINFO_AVX2, buffer_zero_avx2 }, #endif - { CPUINFO_SSE2, 64, buffer_zero_sse2 }, - { CPUINFO_ALWAYS, 0, buffer_zero_int }, + { CPUINFO_SSE2, buffer_zero_sse2 }, + { CPUINFO_ALWAYS, buffer_is_zero_integer }, }; for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) { if (info & all[i].bit) { - length_to_accel = all[i].len; - buffer_accel = all[i].fn; + buffer_is_zero_accel = all[i].fn; return all[i].bit; } } return 0; } -#if defined(CONFIG_AVX2_OPT) +static unsigned used_accel; + static void __attribute__((constructor)) init_accel(void) { used_accel = select_accel_cpuinfo(cpuinfo_init()); } -#endif /* CONFIG_AVX2_OPT */ + +#define INIT_ACCEL NULL bool test_buffer_is_zero_next_accel(void) { @@ -194,36 +173,37 @@ bool test_buffer_is_zero_next_accel(void) used_accel |= used; return used; } - -static bool select_accel_fn(const void *buf, size_t len) -{ - if (likely(len >= length_to_accel)) { - return buffer_accel(buf, len); - } - return buffer_zero_int(buf, len); -} - #else -#define select_accel_fn buffer_zero_int bool test_buffer_is_zero_next_accel(void) { return false; } + +#define INIT_ACCEL buffer_is_zero_integer #endif -/* - * Checks if a buffer is all zeroes - */ -bool buffer_is_zero(const void *buf, size_t len) +static bool (*buffer_is_zero_accel)(const void *, size_t) = INIT_ACCEL; + +bool buffer_is_zero_ool(const void *buf, size_t len) { if (unlikely(len == 0)) { return true; } + if (!buffer_is_zero_sample3(buf, len)) { + return false; + } + /* All bytes are covered for any len <= 3. */ + if (unlikely(len <= 3)) { + return true; + } - /* Fetch the beginning of the buffer while we select the accelerator. */ - __builtin_prefetch(buf); - - /* Use an optimized zero check if possible. Note that this also - includes a check for an unrolled loop over 64-bit integers. */ - return select_accel_fn(buf, len); + if (likely(len >= 256)) { + return buffer_is_zero_accel(buf, len); + } + return buffer_is_zero_integer(buf, len); +} + +bool buffer_is_zero_ge256(const void *buf, size_t len) +{ + return buffer_is_zero_accel(buf, len); } From 93a6085618f16fb2cd316d1e84f1a638b7e2d8ff Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Tue, 6 Feb 2024 23:48:07 +0300 Subject: [PATCH 0402/2884] util/bufferiszero: Remove useless prefetches Use of prefetching in bufferiszero.c is quite questionable: - prefetches are issued just a few CPU cycles before the corresponding line would be hit by demand loads; - they are done for simple access patterns, i.e. where hardware prefetchers can perform better; - they compete for load ports in loops that should be limited by load port throughput rather than ALU throughput. Signed-off-by: Alexander Monakov Signed-off-by: Mikhail Romanov Reviewed-by: Richard Henderson Message-Id: <20240206204809.9859-5-amonakov@ispras.ru> --- util/bufferiszero.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 972f394cbd..00118d649e 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -50,7 +50,6 @@ static bool buffer_is_zero_integer(const void *buf, size_t len) const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8); for (; p + 8 <= e; p += 8) { - __builtin_prefetch(p + 8); if (t) { return false; } @@ -80,7 +79,6 @@ buffer_zero_sse2(const void *buf, size_t len) /* Loop over 16-byte aligned blocks of 64. */ while (likely(p <= e)) { - __builtin_prefetch(p); t = _mm_cmpeq_epi8(t, zero); if (unlikely(_mm_movemask_epi8(t) != 0xFFFF)) { return false; @@ -111,7 +109,6 @@ buffer_zero_avx2(const void *buf, size_t len) /* Loop over 32-byte aligned blocks of 128. */ while (p <= e) { - __builtin_prefetch(p); if (unlikely(!_mm256_testz_si256(t, t))) { return false; } From f28e0bbefa41fe643cce2f107e868abff312ced9 Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Tue, 6 Feb 2024 23:48:08 +0300 Subject: [PATCH 0403/2884] util/bufferiszero: Optimize SSE2 and AVX2 variants Increase unroll factor in SIMD loops from 4x to 8x in order to move their bottlenecks from ALU port contention to load issue rate (two loads per cycle on popular x86 implementations). Avoid using out-of-bounds pointers in loop boundary conditions. Follow SSE2 implementation strategy in the AVX2 variant. Avoid use of PTEST, which is not profitable there (like in the removed SSE4 variant). Signed-off-by: Alexander Monakov Signed-off-by: Mikhail Romanov Reviewed-by: Richard Henderson Message-Id: <20240206204809.9859-6-amonakov@ispras.ru> --- util/bufferiszero.c | 111 +++++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 38 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 00118d649e..02df82b4ff 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -67,62 +67,97 @@ static bool buffer_is_zero_integer(const void *buf, size_t len) #if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) #include -/* Note that each of these vectorized functions require len >= 64. */ +/* Helper for preventing the compiler from reassociating + chains of binary vector operations. */ +#define SSE_REASSOC_BARRIER(vec0, vec1) asm("" : "+x"(vec0), "+x"(vec1)) + +/* Note that these vectorized functions may assume len >= 256. */ static bool __attribute__((target("sse2"))) buffer_zero_sse2(const void *buf, size_t len) { - __m128i t = _mm_loadu_si128(buf); - __m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16); - __m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16); - __m128i zero = _mm_setzero_si128(); + /* Unaligned loads at head/tail. */ + __m128i v = *(__m128i_u *)(buf); + __m128i w = *(__m128i_u *)(buf + len - 16); + /* Align head/tail to 16-byte boundaries. */ + const __m128i *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const __m128i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); + __m128i zero = { 0 }; - /* Loop over 16-byte aligned blocks of 64. */ - while (likely(p <= e)) { - t = _mm_cmpeq_epi8(t, zero); - if (unlikely(_mm_movemask_epi8(t) != 0xFFFF)) { + /* Collect a partial block at tail end. */ + v |= e[-1]; w |= e[-2]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-3]; w |= e[-4]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-5]; w |= e[-6]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-7]; v |= w; + + /* + * Loop over complete 128-byte blocks. + * With the head and tail removed, e - p >= 14, so the loop + * must iterate at least once. + */ + do { + v = _mm_cmpeq_epi8(v, zero); + if (unlikely(_mm_movemask_epi8(v) != 0xFFFF)) { return false; } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } + v = p[0]; w = p[1]; + SSE_REASSOC_BARRIER(v, w); + v |= p[2]; w |= p[3]; + SSE_REASSOC_BARRIER(v, w); + v |= p[4]; w |= p[5]; + SSE_REASSOC_BARRIER(v, w); + v |= p[6]; w |= p[7]; + SSE_REASSOC_BARRIER(v, w); + v |= w; + p += 8; + } while (p < e - 7); - /* Finish the aligned tail. */ - t |= e[-3]; - t |= e[-2]; - t |= e[-1]; - - /* Finish the unaligned tail. */ - t |= _mm_loadu_si128(buf + len - 16); - - return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0xFFFF; + return _mm_movemask_epi8(_mm_cmpeq_epi8(v, zero)) == 0xFFFF; } #ifdef CONFIG_AVX2_OPT static bool __attribute__((target("avx2"))) buffer_zero_avx2(const void *buf, size_t len) { - /* Begin with an unaligned head of 32 bytes. */ - __m256i t = _mm256_loadu_si256(buf); - __m256i *p = (__m256i *)(((uintptr_t)buf + 5 * 32) & -32); - __m256i *e = (__m256i *)(((uintptr_t)buf + len) & -32); + /* Unaligned loads at head/tail. */ + __m256i v = *(__m256i_u *)(buf); + __m256i w = *(__m256i_u *)(buf + len - 32); + /* Align head/tail to 32-byte boundaries. */ + const __m256i *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32); + const __m256i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32); + __m256i zero = { 0 }; - /* Loop over 32-byte aligned blocks of 128. */ - while (p <= e) { - if (unlikely(!_mm256_testz_si256(t, t))) { + /* Collect a partial block at tail end. */ + v |= e[-1]; w |= e[-2]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-3]; w |= e[-4]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-5]; w |= e[-6]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-7]; v |= w; + + /* Loop over complete 256-byte blocks. */ + for (; p < e - 7; p += 8) { + /* PTEST is not profitable here. */ + v = _mm256_cmpeq_epi8(v, zero); + if (unlikely(_mm256_movemask_epi8(v) != 0xFFFFFFFF)) { return false; } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } ; + v = p[0]; w = p[1]; + SSE_REASSOC_BARRIER(v, w); + v |= p[2]; w |= p[3]; + SSE_REASSOC_BARRIER(v, w); + v |= p[4]; w |= p[5]; + SSE_REASSOC_BARRIER(v, w); + v |= p[6]; w |= p[7]; + SSE_REASSOC_BARRIER(v, w); + v |= w; + } - /* Finish the last block of 128 unaligned. */ - t |= _mm256_loadu_si256(buf + len - 4 * 32); - t |= _mm256_loadu_si256(buf + len - 3 * 32); - t |= _mm256_loadu_si256(buf + len - 2 * 32); - t |= _mm256_loadu_si256(buf + len - 1 * 32); - - return _mm256_testz_si256(t, t); + return _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, zero)) == 0xFFFFFFFF; } #endif /* CONFIG_AVX2_OPT */ From 7ae6399a85f6a0818a532d9f3c6e200691f6ef68 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 6 Apr 2024 14:40:32 -1000 Subject: [PATCH 0404/2884] util/bufferiszero: Improve scalar variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split less-than and greater-than 256 cases. Use unaligned accesses for head and tail. Avoid using out-of-bounds pointers in loop boundary conditions. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- util/bufferiszero.c | 85 +++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 02df82b4ff..c9a7ded016 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -28,40 +28,57 @@ static bool (*buffer_is_zero_accel)(const void *, size_t); -static bool buffer_is_zero_integer(const void *buf, size_t len) +static bool buffer_is_zero_int_lt256(const void *buf, size_t len) { - if (unlikely(len < 8)) { - /* For a very small buffer, simply accumulate all the bytes. */ - const unsigned char *p = buf; - const unsigned char *e = buf + len; - unsigned char t = 0; + uint64_t t; + const uint64_t *p, *e; - do { - t |= *p++; - } while (p < e); - - return t == 0; - } else { - /* Otherwise, use the unaligned memory access functions to - handle the beginning and end of the buffer, with a couple - of loops handling the middle aligned section. */ - uint64_t t = ldq_he_p(buf); - const uint64_t *p = (uint64_t *)(((uintptr_t)buf + 8) & -8); - const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8); - - for (; p + 8 <= e; p += 8) { - if (t) { - return false; - } - t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; - } - while (p < e) { - t |= *p++; - } - t |= ldq_he_p(buf + len - 8); - - return t == 0; + /* + * Use unaligned memory access functions to handle + * the beginning and end of the buffer. + */ + if (unlikely(len <= 8)) { + return (ldl_he_p(buf) | ldl_he_p(buf + len - 4)) == 0; } + + t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); + p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); + e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); + + /* Read 0 to 31 aligned words from the middle. */ + while (p < e) { + t |= *p++; + } + return t == 0; +} + +static bool buffer_is_zero_int_ge256(const void *buf, size_t len) +{ + /* + * Use unaligned memory access functions to handle + * the beginning and end of the buffer. + */ + uint64_t t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); + const uint64_t *p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); + const uint64_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); + + /* Collect a partial block at the tail end. */ + t |= e[-7] | e[-6] | e[-5] | e[-4] | e[-3] | e[-2] | e[-1]; + + /* + * Loop over 64 byte blocks. + * With the head and tail removed, e - p >= 30, + * so the loop must iterate at least 3 times. + */ + do { + if (t) { + return false; + } + t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; + p += 8; + } while (p < e - 7); + + return t == 0; } #if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) @@ -173,7 +190,7 @@ select_accel_cpuinfo(unsigned info) { CPUINFO_AVX2, buffer_zero_avx2 }, #endif { CPUINFO_SSE2, buffer_zero_sse2 }, - { CPUINFO_ALWAYS, buffer_is_zero_integer }, + { CPUINFO_ALWAYS, buffer_is_zero_int_ge256 }, }; for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) { @@ -211,7 +228,7 @@ bool test_buffer_is_zero_next_accel(void) return false; } -#define INIT_ACCEL buffer_is_zero_integer +#define INIT_ACCEL buffer_is_zero_int_ge256 #endif static bool (*buffer_is_zero_accel)(const void *, size_t) = INIT_ACCEL; @@ -232,7 +249,7 @@ bool buffer_is_zero_ool(const void *buf, size_t len) if (likely(len >= 256)) { return buffer_is_zero_accel(buf, len); } - return buffer_is_zero_integer(buf, len); + return buffer_is_zero_int_lt256(buf, len); } bool buffer_is_zero_ge256(const void *buf, size_t len) From 0100ce2b49725e6ba2fbe8301855978d5d3dc790 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 14 Feb 2024 20:56:52 -1000 Subject: [PATCH 0405/2884] util/bufferiszero: Introduce biz_accel_fn typedef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- util/bufferiszero.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index c9a7ded016..f9af7841ba 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -26,7 +26,8 @@ #include "qemu/bswap.h" #include "host/cpuinfo.h" -static bool (*buffer_is_zero_accel)(const void *, size_t); +typedef bool (*biz_accel_fn)(const void *, size_t); +static biz_accel_fn buffer_is_zero_accel; static bool buffer_is_zero_int_lt256(const void *buf, size_t len) { @@ -184,7 +185,7 @@ select_accel_cpuinfo(unsigned info) /* Array is sorted in order of algorithm preference. */ static const struct { unsigned bit; - bool (*fn)(const void *, size_t); + biz_accel_fn fn; } all[] = { #ifdef CONFIG_AVX2_OPT { CPUINFO_AVX2, buffer_zero_avx2 }, @@ -231,7 +232,7 @@ bool test_buffer_is_zero_next_accel(void) #define INIT_ACCEL buffer_is_zero_int_ge256 #endif -static bool (*buffer_is_zero_accel)(const void *, size_t) = INIT_ACCEL; +static biz_accel_fn buffer_is_zero_accel = INIT_ACCEL; bool buffer_is_zero_ool(const void *buf, size_t len) { From bf67aa3dd2d8b28d7618d8ec62cd9f6055366751 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 14 Feb 2024 21:10:56 -1000 Subject: [PATCH 0406/2884] util/bufferiszero: Simplify test_buffer_is_zero_next_accel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the three alternatives are monotonic, we don't need to keep a couple of bitmasks, just identify the strongest alternative at startup. Generalize test_buffer_is_zero_next_accel and init_accel by always defining an accel_table array. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- util/bufferiszero.c | 81 ++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index f9af7841ba..7218154a13 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -27,7 +27,6 @@ #include "host/cpuinfo.h" typedef bool (*biz_accel_fn)(const void *, size_t); -static biz_accel_fn buffer_is_zero_accel; static bool buffer_is_zero_int_lt256(const void *buf, size_t len) { @@ -179,60 +178,35 @@ buffer_zero_avx2(const void *buf, size_t len) } #endif /* CONFIG_AVX2_OPT */ -static unsigned __attribute__((noinline)) -select_accel_cpuinfo(unsigned info) -{ - /* Array is sorted in order of algorithm preference. */ - static const struct { - unsigned bit; - biz_accel_fn fn; - } all[] = { +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_zero_sse2, #ifdef CONFIG_AVX2_OPT - { CPUINFO_AVX2, buffer_zero_avx2 }, + buffer_zero_avx2, #endif - { CPUINFO_SSE2, buffer_zero_sse2 }, - { CPUINFO_ALWAYS, buffer_is_zero_int_ge256 }, - }; +}; - for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) { - if (info & all[i].bit) { - buffer_is_zero_accel = all[i].fn; - return all[i].bit; - } +static unsigned best_accel(void) +{ + unsigned info = cpuinfo_init(); + +#ifdef CONFIG_AVX2_OPT + if (info & CPUINFO_AVX2) { + return 2; } - return 0; +#endif + return info & CPUINFO_SSE2 ? 1 : 0; } -static unsigned used_accel; - -static void __attribute__((constructor)) init_accel(void) -{ - used_accel = select_accel_cpuinfo(cpuinfo_init()); -} - -#define INIT_ACCEL NULL - -bool test_buffer_is_zero_next_accel(void) -{ - /* - * Accumulate the accelerators that we've already tested, and - * remove them from the set to test this round. We'll get back - * a zero from select_accel_cpuinfo when there are no more. - */ - unsigned used = select_accel_cpuinfo(cpuinfo & ~used_accel); - used_accel |= used; - return used; -} #else -bool test_buffer_is_zero_next_accel(void) -{ - return false; -} - -#define INIT_ACCEL buffer_is_zero_int_ge256 +#define best_accel() 0 +static biz_accel_fn const accel_table[1] = { + buffer_is_zero_int_ge256 +}; #endif -static biz_accel_fn buffer_is_zero_accel = INIT_ACCEL; +static biz_accel_fn buffer_is_zero_accel; +static unsigned accel_index; bool buffer_is_zero_ool(const void *buf, size_t len) { @@ -257,3 +231,18 @@ bool buffer_is_zero_ge256(const void *buf, size_t len) { return buffer_is_zero_accel(buf, len); } + +bool test_buffer_is_zero_next_accel(void) +{ + if (accel_index != 0) { + buffer_is_zero_accel = accel_table[--accel_index]; + return true; + } + return false; +} + +static void __attribute__((constructor)) init_accel(void) +{ + accel_index = best_accel(); + buffer_is_zero_accel = accel_table[accel_index]; +} From 22437b4de94c37e6104d90d46b31d80cf14358d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 10 Feb 2024 00:02:17 +0000 Subject: [PATCH 0407/2884] util/bufferiszero: Add simd acceleration for aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because non-embedded aarch64 is expected to have AdvSIMD enabled, merely double-check with the compiler flags for __ARM_NEON and don't bother with a runtime check. Otherwise, model the loop after the x86 SSE2 function. Use UMAXV for the vector reduction. This is 3 cycles on cortex-a76 and 2 cycles on neoverse-n1. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- util/bufferiszero.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 7218154a13..74864f7b78 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -198,6 +198,73 @@ static unsigned best_accel(void) return info & CPUINFO_SSE2 ? 1 : 0; } +#elif defined(__aarch64__) && defined(__ARM_NEON) +#include + +/* + * Helper for preventing the compiler from reassociating + * chains of binary vector operations. + */ +#define REASSOC_BARRIER(vec0, vec1) asm("" : "+w"(vec0), "+w"(vec1)) + +static bool buffer_is_zero_simd(const void *buf, size_t len) +{ + uint32x4_t t0, t1, t2, t3; + + /* Align head/tail to 16-byte boundaries. */ + const uint32x4_t *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const uint32x4_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); + + /* Unaligned loads at head/tail. */ + t0 = vld1q_u32(buf) | vld1q_u32(buf + len - 16); + + /* Collect a partial block at tail end. */ + t1 = e[-7] | e[-6]; + t2 = e[-5] | e[-4]; + t3 = e[-3] | e[-2]; + t0 |= e[-1]; + REASSOC_BARRIER(t0, t1); + REASSOC_BARRIER(t2, t3); + t0 |= t1; + t2 |= t3; + REASSOC_BARRIER(t0, t2); + t0 |= t2; + + /* + * Loop over complete 128-byte blocks. + * With the head and tail removed, e - p >= 14, so the loop + * must iterate at least once. + */ + do { + /* + * Reduce via UMAXV. Whatever the actual result, + * it will only be zero if all input bytes are zero. + */ + if (unlikely(vmaxvq_u32(t0) != 0)) { + return false; + } + + t0 = p[0] | p[1]; + t1 = p[2] | p[3]; + t2 = p[4] | p[5]; + t3 = p[6] | p[7]; + REASSOC_BARRIER(t0, t1); + REASSOC_BARRIER(t2, t3); + t0 |= t1; + t2 |= t3; + REASSOC_BARRIER(t0, t2); + t0 |= t2; + p += 8; + } while (p < e - 7); + + return vmaxvq_u32(t0) == 0; +} + +#define best_accel() 1 +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_is_zero_simd, +}; #else #define best_accel() 0 static biz_accel_fn const accel_table[1] = { From a06d9eddb015a9f5895161b0a3958a2e4be21579 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 16 Feb 2024 13:59:36 -1000 Subject: [PATCH 0408/2884] tests/bench: Add bufferiszero-bench MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmark each acceleration function vs an aligned buffer of zeros. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tests/bench/bufferiszero-bench.c | 47 ++++++++++++++++++++++++++++++++ tests/bench/meson.build | 1 + 2 files changed, 48 insertions(+) create mode 100644 tests/bench/bufferiszero-bench.c diff --git a/tests/bench/bufferiszero-bench.c b/tests/bench/bufferiszero-bench.c new file mode 100644 index 0000000000..222695c1fa --- /dev/null +++ b/tests/bench/bufferiszero-bench.c @@ -0,0 +1,47 @@ +/* + * QEMU buffer_is_zero speed benchmark + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/units.h" + +static void test(const void *opaque) +{ + size_t max = 64 * KiB; + void *buf = g_malloc0(max); + int accel_index = 0; + + do { + if (accel_index != 0) { + g_test_message("%s", ""); /* gnu_printf Werror for simple "" */ + } + for (size_t len = 1 * KiB; len <= max; len *= 4) { + double total = 0.0; + + g_test_timer_start(); + do { + buffer_is_zero_ge256(buf, len); + total += len; + } while (g_test_timer_elapsed() < 0.5); + + total /= MiB; + g_test_message("buffer_is_zero #%d: %2zuKB %8.0f MB/sec", + accel_index, len / (size_t)KiB, + total / g_test_timer_last()); + } + accel_index++; + } while (test_buffer_is_zero_next_accel()); + + g_free(buf); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_data_func("/cutils/bufferiszero/speed", NULL, test); + return g_test_run(); +} diff --git a/tests/bench/meson.build b/tests/bench/meson.build index 7e76338a52..4cd7a2f6b5 100644 --- a/tests/bench/meson.build +++ b/tests/bench/meson.build @@ -21,6 +21,7 @@ benchs = {} if have_block benchs += { + 'bufferiszero-bench': [], 'benchmark-crypto-hash': [crypto], 'benchmark-crypto-hmac': [crypto], 'benchmark-crypto-cipher': [crypto], From f184f3856e82a6f4e96df6a77118d6a2e1a9059b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 18:04:09 +0200 Subject: [PATCH 0409/2884] exec: Include missing license in 'exec/cpu-common.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1ad2134f91 ("Hardware convenience library") extracted "cpu-common.h" from "cpu-all.h", which uses the LGPL-2.1+ license. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-5-philmd@linaro.org> --- include/exec/cpu-common.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 6d5318895a..8812ba744d 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -1,8 +1,13 @@ +/* + * CPU interfaces that are target independent. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1+ + */ #ifndef CPU_COMMON_H #define CPU_COMMON_H -/* CPU interfaces that are target independent. */ - #include "exec/vaddr.h" #ifndef CONFIG_USER_ONLY #include "exec/hwaddr.h" From 22879b66800d4f84ff48f151867369e76e33f9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Mar 2024 11:10:04 +0100 Subject: [PATCH 0410/2884] user: Move 'abitypes.h' from 'exec/user' to 'user' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep all user emulation headers under the same user/ directory. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240503125202.35667-1-philmd@linaro.org> --- bsd-user/qemu.h | 2 +- include/exec/cpu-all.h | 2 +- include/exec/user/thunk.h | 2 +- include/{exec => }/user/abitypes.h | 4 ++-- include/user/syscall-trace.h | 2 +- linux-user/qemu.h | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename include/{exec => }/user/abitypes.h (97%) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 8629f0dcde..a0c1ad7efa 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -22,7 +22,7 @@ #include "exec/cpu_ldst.h" #include "exec/exec-all.h" -#include "exec/user/abitypes.h" +#include "user/abitypes.h" extern char **environ; diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index e75ec13cd0..032c6d990e 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -64,7 +64,7 @@ /* MMU memory access macros */ #if defined(CONFIG_USER_ONLY) -#include "exec/user/abitypes.h" +#include "user/abitypes.h" /* * If non-zero, the guest virtual address space is a contiguous subset diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index 2ebfecf58e..9f35c888f9 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -21,7 +21,7 @@ #define THUNK_H #include "cpu.h" -#include "exec/user/abitypes.h" +#include "user/abitypes.h" /* types enums definitions */ diff --git a/include/exec/user/abitypes.h b/include/user/abitypes.h similarity index 97% rename from include/exec/user/abitypes.h rename to include/user/abitypes.h index 3ec1969368..5c9a955631 100644 --- a/include/exec/user/abitypes.h +++ b/include/user/abitypes.h @@ -1,5 +1,5 @@ -#ifndef EXEC_USER_ABITYPES_H -#define EXEC_USER_ABITYPES_H +#ifndef USER_ABITYPES_H +#define USER_ABITYPES_H #ifndef CONFIG_USER_ONLY #error Cannot include this header from system emulation diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h index b48b2b2d0a..9bd7ca19c8 100644 --- a/include/user/syscall-trace.h +++ b/include/user/syscall-trace.h @@ -10,7 +10,7 @@ #ifndef SYSCALL_TRACE_H #define SYSCALL_TRACE_H -#include "exec/user/abitypes.h" +#include "user/abitypes.h" #include "gdbstub/user.h" #include "qemu/plugin.h" #include "trace/trace-root.h" diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 4777856b52..263f445ff1 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -4,7 +4,7 @@ #include "cpu.h" #include "exec/cpu_ldst.h" -#include "exec/user/abitypes.h" +#include "user/abitypes.h" #include "syscall_defs.h" #include "target_syscall.h" From 4e111653168acc058044885c679015d50fcaf474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Mar 2024 11:08:12 +0100 Subject: [PATCH 0411/2884] user: Move 'thunk.h' from 'exec/user' to 'user' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep all user emulation headers under the same user/ directory. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-2-philmd@linaro.org> --- MAINTAINERS | 1 - bsd-user/qemu.h | 2 +- include/{exec => }/user/thunk.h | 8 ++++++-- linux-user/thunk.c | 2 +- linux-user/user-internals.h | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) rename include/{exec => }/user/thunk.h (97%) diff --git a/MAINTAINERS b/MAINTAINERS index 302b6fd00c..96411e6adf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3692,7 +3692,6 @@ Overall usermode emulation M: Riku Voipio S: Maintained F: accel/tcg/user-exec*.c -F: include/exec/user/ F: include/user/ F: common-user/ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index a0c1ad7efa..a916724de9 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -26,7 +26,7 @@ extern char **environ; -#include "exec/user/thunk.h" +#include "user/thunk.h" #include "target_arch.h" #include "syscall_defs.h" #include "target_syscall.h" diff --git a/include/exec/user/thunk.h b/include/user/thunk.h similarity index 97% rename from include/exec/user/thunk.h rename to include/user/thunk.h index 9f35c888f9..2a2104b568 100644 --- a/include/exec/user/thunk.h +++ b/include/user/thunk.h @@ -17,8 +17,12 @@ * License along with this library; if not, see . */ -#ifndef THUNK_H -#define THUNK_H +#ifndef USER_THUNK_H +#define USER_THUNK_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif #include "cpu.h" #include "user/abitypes.h" diff --git a/linux-user/thunk.c b/linux-user/thunk.c index 071aad4b5f..3cd19e79c6 100644 --- a/linux-user/thunk.c +++ b/linux-user/thunk.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qemu.h" -#include "exec/user/thunk.h" +#include "user/thunk.h" //#define DEBUG diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index ce11d9e21c..5c7f173ceb 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -18,7 +18,7 @@ #ifndef LINUX_USER_USER_INTERNALS_H #define LINUX_USER_USER_INTERNALS_H -#include "exec/user/thunk.h" +#include "user/thunk.h" #include "exec/exec-all.h" #include "exec/tb-flush.h" #include "qemu/log.h" From bf0bcac890cc7b4c9e52c9e94817897ce73b69a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Mar 2024 11:13:22 +0100 Subject: [PATCH 0412/2884] coverity: Update user emulation regexp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All user emulation headers are now under include/user/. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-3-philmd@linaro.org> --- scripts/coverity-scan/COMPONENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 91be8d1c36..1537e49cd5 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -121,7 +121,7 @@ usb ~ (/qemu)?(/hw/usb/.*|/include/hw/usb/.*) user - ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/exec/user/.*) + ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/user/.*) util ~ (/qemu)?(/util/.*|/include/qemu/.*) From 155fb465b1a6c87d8fc002a670b6517a6790fad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Mar 2024 12:08:42 +0100 Subject: [PATCH 0413/2884] plugins/api: Only include 'exec/ram_addr.h' with system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/ram_addr.h" shouldn't be used with user emulation. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20240427155714.53669-4-philmd@linaro.org> --- plugins/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/api.c b/plugins/api.c index 8fa5a600ac..eaee344d8e 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -42,10 +42,10 @@ #include "tcg/tcg.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" -#include "exec/ram_addr.h" #include "disas/disas.h" #include "plugin.h" #ifndef CONFIG_USER_ONLY +#include "exec/ram_addr.h" #include "qemu/plugin-memory.h" #include "hw/boards.h" #else From e096d370ad877f8573e20266f7e843084f9611d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 09:01:30 +0200 Subject: [PATCH 0414/2884] plugins: Update stale comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "plugin_mask" was renamed as "event_mask" in commit c006147122 ("plugins: create CPUPluginState and migrate plugin_mask"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-3-philmd@linaro.org> --- plugins/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/core.c b/plugins/core.c index 11ca20e626..09c98382f5 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -373,7 +373,7 @@ void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb) struct qemu_plugin_cb *cb, *next; enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS; - /* no plugin_mask check here; caller should have checked */ + /* no plugin_state->event_mask check here; caller should have checked */ QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans; From a0dbef9f337062eaf8af37bf904dba181469d550 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Mon, 29 Apr 2024 16:49:38 +0100 Subject: [PATCH 0415/2884] MAINTAINERS: Update my email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anthony PERARD Acked-by: Paul Durrant Acked-by: Stefano Stabellini Message-ID: <20240429154938.19340-1-anthony.perard@citrix.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 96411e6adf..2f08cc528e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -532,7 +532,7 @@ Guest CPU Cores (Xen) --------------------- X86 Xen CPUs M: Stefano Stabellini -M: Anthony Perard +M: Anthony PERARD M: Paul Durrant L: xen-devel@lists.xenproject.org S: Supported From c365e6b0705788866a65e7b8206bd4c5332595cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Apr 2024 12:41:53 +0200 Subject: [PATCH 0416/2884] target/sh4: Fix ADDV opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation says: ADDV Rm, Rn Rn + Rm -> Rn, overflow -> T But QEMU implementation was: ADDV Rm, Rn Rn + Rm -> Rm, overflow -> T Fix by filling the correct Rm register. Add tests provided by Paul Cercueil. Cc: qemu-stable@nongnu.org Fixes: ad8d25a11f ("target-sh4: implement addv and subv using TCG") Reported-by: Paul Cercueil Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2317 Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: <20240430163125.77430-2-philmd@linaro.org> --- target/sh4/translate.c | 2 +- tests/tcg/sh4/Makefile.target | 3 +++ tests/tcg/sh4/test-addv.c | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/sh4/test-addv.c diff --git a/target/sh4/translate.c b/target/sh4/translate.c index ebb6c901bf..4a1dd0d1f4 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -714,7 +714,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); tcg_gen_andc_i32(cpu_sr_t, t1, t2); tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); - tcg_gen_mov_i32(REG(B7_4), t0); + tcg_gen_mov_i32(REG(B11_8), t0); } return; case 0x2009: /* and Rm,Rn */ diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 4d09291c0c..521b8b0a76 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -17,3 +17,6 @@ TESTS += test-macl test-macw: CFLAGS += -O -g TESTS += test-macw + +test-addv: CFLAGS += -O -g +TESTS += test-addv diff --git a/tests/tcg/sh4/test-addv.c b/tests/tcg/sh4/test-addv.c new file mode 100644 index 0000000000..ca87fe746a --- /dev/null +++ b/tests/tcg/sh4/test-addv.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +static void addv(const int a, const int b, const int res, const int carry) +{ + int o = a, c; + + asm volatile("addv %2,%0\n" + "movt %1\n" + : "+r"(o), "=r"(c) : "r"(b) : ); + + if (c != carry || o != res) { + printf("ADDV %d, %d = %d/%d [T = %d/%d]\n", a, b, o, res, c, carry); + abort(); + } +} + +int main(void) +{ + addv(INT_MAX, 1, INT_MIN, 1); + addv(INT_MAX - 1, 1, INT_MAX, 0); + + return 0; +} From e88a856efd1d3c3ffa8e53da4831eff8da290808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Apr 2024 13:10:19 +0200 Subject: [PATCH 0417/2884] target/sh4: Fix SUBV opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation says: SUBV Rm, Rn Rn - Rm -> Rn, underflow -> T The overflow / underflow can be calculated as: T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 However we were using the incorrect: T = ((Rn ^ Rm) & (Result ^ Rm)) >> 31 Fix by using the Rn register instead of Rm. Add tests provided by Paul Cercueil. Cc: qemu-stable@nongnu.org Fixes: ad8d25a11f ("target-sh4: implement addv and subv using TCG") Reported-by: Paul Cercueil Suggested-by: Paul Cercueil Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2318 Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Yoshinori Sato Message-Id: <20240430163125.77430-3-philmd@linaro.org> --- target/sh4/translate.c | 2 +- tests/tcg/sh4/Makefile.target | 3 +++ tests/tcg/sh4/test-subv.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/sh4/test-subv.c diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 4a1dd0d1f4..3e013b7c7c 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -933,7 +933,7 @@ static void _decode_opc(DisasContext * ctx) t0 = tcg_temp_new(); tcg_gen_sub_i32(t0, REG(B11_8), REG(B7_4)); t1 = tcg_temp_new(); - tcg_gen_xor_i32(t1, t0, REG(B7_4)); + tcg_gen_xor_i32(t1, t0, REG(B11_8)); t2 = tcg_temp_new(); tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); tcg_gen_and_i32(t1, t1, t2); diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 521b8b0a76..7852fa62d8 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -20,3 +20,6 @@ TESTS += test-macw test-addv: CFLAGS += -O -g TESTS += test-addv + +test-subv: CFLAGS += -O -g +TESTS += test-subv diff --git a/tests/tcg/sh4/test-subv.c b/tests/tcg/sh4/test-subv.c new file mode 100644 index 0000000000..a3c2db96e4 --- /dev/null +++ b/tests/tcg/sh4/test-subv.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +static void subv(const int a, const int b, const int res, const int carry) +{ + int o = a, c; + + asm volatile("subv %2,%0\n" + "movt %1\n" + : "+r"(o), "=r"(c) : "r"(b) : ); + + if (c != carry || o != res) { + printf("SUBV %d, %d = %d/%d [T = %d/%d]\n", a, b, o, res, c, carry); + abort(); + } +} + +int main(void) +{ + subv(INT_MIN, 1, INT_MAX, 1); + subv(INT_MAX, -1, INT_MIN, 1); + subv(INT_MAX, 1, INT_MAX - 1, 0); + subv(0, 1, -1, 0); + subv(-1, -1, 0, 0); + + return 0; +} From 40ed073f893b1aaebbd7f2ef1259bab9a0cea46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Apr 2024 16:43:05 +0200 Subject: [PATCH 0418/2884] target/sh4: Rename TCGv variables as manual for ADDV opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To easily compare with the SH4 manual, rename: REG(B11_8) -> Rn REG(B7_4) -> Rm t0 -> result Mention how overflow is calculated. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Yoshinori Sato Message-Id: <20240430163125.77430-4-philmd@linaro.org> --- target/sh4/translate.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 3e013b7c7c..47c0f3404e 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -705,16 +705,20 @@ static void _decode_opc(DisasContext * ctx) return; case 0x300f: /* addv Rm,Rn */ { - TCGv t0, t1, t2; - t0 = tcg_temp_new(); - tcg_gen_add_i32(t0, REG(B7_4), REG(B11_8)); + TCGv Rn = REG(B11_8); + TCGv Rm = REG(B7_4); + TCGv result, t1, t2; + + result = tcg_temp_new(); t1 = tcg_temp_new(); - tcg_gen_xor_i32(t1, t0, REG(B11_8)); t2 = tcg_temp_new(); - tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); + tcg_gen_add_i32(result, Rm, Rn); + /* T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 */ + tcg_gen_xor_i32(t1, result, Rn); + tcg_gen_xor_i32(t2, Rm, Rn); tcg_gen_andc_i32(cpu_sr_t, t1, t2); tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); - tcg_gen_mov_i32(REG(B11_8), t0); + tcg_gen_mov_i32(Rn, result); } return; case 0x2009: /* and Rm,Rn */ From 942ba09d7cfc11b8a149011a201d274902731333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 30 Apr 2024 16:43:09 +0200 Subject: [PATCH 0419/2884] target/sh4: Rename TCGv variables as manual for SUBV opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To easily compare with the SH4 manual, rename: REG(B11_8) -> Rn REG(B7_4) -> Rm t0 -> result Mention how underflow is calculated. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240430163125.77430-5-philmd@linaro.org> --- target/sh4/translate.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 47c0f3404e..e599ab9d1a 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -933,16 +933,20 @@ static void _decode_opc(DisasContext * ctx) return; case 0x300b: /* subv Rm,Rn */ { - TCGv t0, t1, t2; - t0 = tcg_temp_new(); - tcg_gen_sub_i32(t0, REG(B11_8), REG(B7_4)); + TCGv Rn = REG(B11_8); + TCGv Rm = REG(B7_4); + TCGv result, t1, t2; + + result = tcg_temp_new(); t1 = tcg_temp_new(); - tcg_gen_xor_i32(t1, t0, REG(B11_8)); t2 = tcg_temp_new(); - tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); + tcg_gen_sub_i32(result, Rn, Rm); + /* T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 */ + tcg_gen_xor_i32(t1, result, Rn); + tcg_gen_xor_i32(t2, Rn, Rm); tcg_gen_and_i32(t1, t1, t2); tcg_gen_shri_i32(cpu_sr_t, t1, 31); - tcg_gen_mov_i32(REG(B11_8), t0); + tcg_gen_mov_i32(Rn, result); } return; case 0x2008: /* tst Rm,Rn */ From 2d27c91e2b72ac7a65504ac207c89262d92464eb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 2 May 2024 15:29:04 +0100 Subject: [PATCH 0420/2884] ui/cocoa.m: Drop old macOS-10.12-and-earlier compat ifdefs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only support the most recent two versions of macOS (currently macOS 13 Ventura and macOS 14 Sonoma), and our ui/cocoa.m code already assumes at least macOS 12 Monterey or better, because it uses NSScreen safeAreaInsets, which is 12.0-or-newer. Remove the ifdefs that were providing backwards compatibility for building on 10.12 and earlier versions. Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Message-ID: <20240502142904.62644-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- ui/cocoa.m | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 25e0db9dd0..981615a8b9 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -50,23 +50,10 @@ #include #include "hw/core/cpu.h" -#ifndef MAC_OS_X_VERSION_10_13 -#define MAC_OS_X_VERSION_10_13 101300 -#endif - #ifndef MAC_OS_VERSION_14_0 #define MAC_OS_VERSION_14_0 140000 #endif -/* 10.14 deprecates NSOnState and NSOffState in favor of - * NSControlStateValueOn/Off, which were introduced in 10.13. - * Define for older versions - */ -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 -#define NSControlStateValueOn NSOnState -#define NSControlStateValueOff NSOffState -#endif - //#define DEBUG #ifdef DEBUG From deb686ef0e609ceaec0daa5dc88eb5b3dd9701b0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 3 May 2024 19:13:03 +0200 Subject: [PATCH 0421/2884] qga/commands-posix: fix typo in qmp_guest_set_user_password qga/commands-posix.c does not compile on FreeBSD due to a confusion between "chpasswdata" (wrong) and "chpasswddata" (used in the #else branch). Fixes: 0e5b75a390 ("qga/commands-posix: qmp_guest_set_user_password: use ga_run_command helper") Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- qga/commands-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7a065c4085..7f05996495 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2173,7 +2173,7 @@ void qmp_guest_set_user_password(const char *username, } #ifdef __FreeBSD__ - g_autofree char *chpasswdata = g_strdup(rawpasswddata); + g_autofree char *chpasswddata = g_strdup(rawpasswddata); const char *crypt_flag = crypted ? "-H" : "-h"; const char *argv[] = {"pw", "usermod", "-n", username, crypt_flag, "0", NULL}; From ab709f13b8d44466787f4f53e6333b747d813afb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:05 +0200 Subject: [PATCH 0422/2884] target/alpha: Use cpu_env in preference to ALPHA_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ALPHA_CPU has a dynamic object type assert, which is unnecessary considering that these are all class hooks. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-2-philmd@linaro.org> --- target/alpha/cpu.c | 15 ++++++--------- target/alpha/helper.c | 8 ++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 05f9ee41e9..f98d022671 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -28,25 +28,22 @@ static void alpha_cpu_set_pc(CPUState *cs, vaddr value) { - AlphaCPU *cpu = ALPHA_CPU(cs); - - cpu->env.pc = value; + CPUAlphaState *env = cpu_env(cs); + env->pc = value; } static vaddr alpha_cpu_get_pc(CPUState *cs) { - AlphaCPU *cpu = ALPHA_CPU(cs); - - return cpu->env.pc; + CPUAlphaState *env = cpu_env(cs); + return env->pc; } static void alpha_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - AlphaCPU *cpu = ALPHA_CPU(cs); - - cpu->env.pc = data[0]; + CPUAlphaState *env = cpu_env(cs); + env->pc = data[0]; } static bool alpha_cpu_has_work(CPUState *cs) diff --git a/target/alpha/helper.c b/target/alpha/helper.c index d6d4353edd..c5e4958f8b 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -124,7 +124,7 @@ void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, MMUAccessType access_type, bool maperr, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = cpu_env(cs); target_ulong mmcsr, cause; /* Assuming !maperr, infer the missing protection. */ @@ -155,9 +155,9 @@ void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, } /* Record the arguments that PALcode would give to the kernel. */ - cpu->env.trap_arg0 = address; - cpu->env.trap_arg1 = mmcsr; - cpu->env.trap_arg2 = cause; + env->trap_arg0 = address; + env->trap_arg1 = mmcsr; + env->trap_arg2 = cause; } #else /* Returns the OSF/1 entMM failure indication, or -1 on success. */ From 1bcae46aac60ae0efbeb3957f7679da9e7e50f30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:06 +0200 Subject: [PATCH 0423/2884] target/alpha: Hoist branch shift to initial decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-3-philmd@linaro.org> --- target/alpha/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index a97cd54f0c..52c2e6248b 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -432,7 +432,7 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t dest) static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) { - uint64_t dest = ctx->base.pc_next + (disp << 2); + uint64_t dest = ctx->base.pc_next + disp; if (ra != 31) { tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next); @@ -455,7 +455,7 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, TCGv cmp, uint64_t imm, int32_t disp) { - uint64_t dest = ctx->base.pc_next + (disp << 2); + uint64_t dest = ctx->base.pc_next + disp; TCGLabel *lab_true = gen_new_label(); if (use_goto_tb(ctx, dest)) { @@ -1382,7 +1382,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) real_islit = islit = extract32(insn, 12, 1); lit = extract32(insn, 13, 8); - disp21 = sextract32(insn, 0, 21); + disp21 = sextract32(insn, 0, 21) * 4; disp16 = sextract32(insn, 0, 16); disp12 = sextract32(insn, 0, 12); From 0cda93c9b55b1f865f92a2a2e189dcbaab485bb2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:07 +0200 Subject: [PATCH 0424/2884] target/alpha: Use DISAS_NEXT definition instead of magic '0' value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Message-Id: <20240424234436.995410-4-richard.henderson@linaro.org> [PMD: Split bigger patch, part 1/5] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-4-philmd@linaro.org> --- target/alpha/translate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 52c2e6248b..9ad7bf6e5f 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -440,8 +440,10 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) /* Notice branch-to-next; used to initialize RA with the PC. */ if (disp == 0) { - return 0; - } else if (use_goto_tb(ctx, dest)) { + return DISAS_NEXT; + } + + if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, dest); tcg_gen_exit_tb(ctx->base.tb, 0); From c0fcd5612e84810fbc62b17fb99a0a4dd847b251 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:08 +0200 Subject: [PATCH 0425/2884] target/alpha: Inline DISAS_PC_UPDATED and return DISAS_NORETURN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline DISAS_PC_UPDATED switch case from alpha_tr_tb_stop(): switch (ctx->base.is_jmp) { ... case DISAS_PC_UPDATED: tcg_gen_lookup_and_goto_ptr(); break; Signed-off-by: Richard Henderson Message-Id: <20240424234436.995410-4-richard.henderson@linaro.org> [PMD: Split bigger patch, part 2/5] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-5-philmd@linaro.org> --- target/alpha/translate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 9ad7bf6e5f..01914e7b56 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -450,7 +450,8 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) return DISAS_NORETURN; } else { tcg_gen_movi_i64(cpu_pc, dest); - return DISAS_PC_UPDATED; + tcg_gen_lookup_and_goto_ptr(); + return DISAS_NORETURN; } } @@ -479,7 +480,8 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, TCGv_i64 p = tcg_constant_i64(ctx->base.pc_next); tcg_gen_movcond_i64(cond, cpu_pc, cmp, i, d, p); - return DISAS_PC_UPDATED; + tcg_gen_lookup_and_goto_ptr(); + return DISAS_NORETURN; } } From 9804ab26d0f25cb9c97e34f96bb0fb2be0a9f677 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:09 +0200 Subject: [PATCH 0426/2884] target/alpha: Return DISAS_NORETURN once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial change to make next commits easier to understand. Signed-off-by: Richard Henderson Message-Id: <20240424234436.995410-4-richard.henderson@linaro.org> [PMD: Split bigger patch, part 3/5] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-6-philmd@linaro.org> --- target/alpha/translate.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 01914e7b56..41151f002e 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -447,12 +447,12 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, dest); tcg_gen_exit_tb(ctx->base.tb, 0); - return DISAS_NORETURN; } else { tcg_gen_movi_i64(cpu_pc, dest); tcg_gen_lookup_and_goto_ptr(); - return DISAS_NORETURN; } + + return DISAS_NORETURN; } static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, @@ -472,8 +472,6 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, tcg_gen_goto_tb(1); tcg_gen_movi_i64(cpu_pc, dest); tcg_gen_exit_tb(ctx->base.tb, 1); - - return DISAS_NORETURN; } else { TCGv_i64 i = tcg_constant_i64(imm); TCGv_i64 d = tcg_constant_i64(dest); @@ -481,8 +479,9 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, tcg_gen_movcond_i64(cond, cpu_pc, cmp, i, d, p); tcg_gen_lookup_and_goto_ptr(); - return DISAS_NORETURN; } + + return DISAS_NORETURN; } static DisasJumpType gen_bcond(DisasContext *ctx, TCGCond cond, int ra, From 39482c2edcc9864cf694b8ea525b49693443f90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 May 2024 09:20:10 +0200 Subject: [PATCH 0427/2884] target/alpha: Simplify gen_bcond_internal() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Richard Henderson explained on IRC: bcond_internal() used to insist that both branch destination and branch fallthrough are use_goto_tb; if not, we'd use movcond to compute an indirect jump. But it's perfectly fine for e.g. the branch fallthrough to use_goto_tb, and the branch destination to use an indirect branch. Signed-off-by: Richard Henderson Message-Id: <20240424234436.995410-4-richard.henderson@linaro.org> [PMD: Split bigger patch, part 4/5] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-7-philmd@linaro.org> --- target/alpha/translate.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 41151f002e..b7b94cc378 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -461,23 +461,22 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, uint64_t dest = ctx->base.pc_next + disp; TCGLabel *lab_true = gen_new_label(); - if (use_goto_tb(ctx, dest)) { - tcg_gen_brcondi_i64(cond, cmp, imm, lab_true); - + tcg_gen_brcondi_i64(cond, cmp, imm, lab_true); + if (use_goto_tb(ctx, ctx->base.pc_next)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); tcg_gen_exit_tb(ctx->base.tb, 0); - - gen_set_label(lab_true); + } else { + tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + tcg_gen_lookup_and_goto_ptr(); + } + gen_set_label(lab_true); + if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(1); tcg_gen_movi_i64(cpu_pc, dest); tcg_gen_exit_tb(ctx->base.tb, 1); } else { - TCGv_i64 i = tcg_constant_i64(imm); - TCGv_i64 d = tcg_constant_i64(dest); - TCGv_i64 p = tcg_constant_i64(ctx->base.pc_next); - - tcg_gen_movcond_i64(cond, cpu_pc, cmp, i, d, p); + tcg_gen_movi_i64(cpu_pc, dest); tcg_gen_lookup_and_goto_ptr(); } From 82b60d2509ddb9123c48ea773c58886b7dad22d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:11 +0200 Subject: [PATCH 0428/2884] target/alpha: Split out gen_goto_tb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Message-Id: <20240424234436.995410-4-richard.henderson@linaro.org> [PMD: Split bigger patch, part 5/5] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-8-philmd@linaro.org> --- target/alpha/translate.c | 53 ++++++++++++---------------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index b7b94cc378..c1a55e5153 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -425,15 +425,22 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, return DISAS_NEXT; } -static bool use_goto_tb(DisasContext *ctx, uint64_t dest) +static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp) { - return translator_use_goto_tb(&ctx->base, dest); + uint64_t dest = ctx->base.pc_next + disp; + + if (translator_use_goto_tb(&ctx->base, dest)) { + tcg_gen_goto_tb(idx); + tcg_gen_movi_i64(cpu_pc, dest); + tcg_gen_exit_tb(ctx->base.tb, idx); + } else { + tcg_gen_movi_i64(cpu_pc, dest); + tcg_gen_lookup_and_goto_ptr(); + } } static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) { - uint64_t dest = ctx->base.pc_next + disp; - if (ra != 31) { tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next); } @@ -442,43 +449,19 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) if (disp == 0) { return DISAS_NEXT; } - - if (use_goto_tb(ctx, dest)) { - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, 0); - } else { - tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } - + gen_goto_tb(ctx, 0, disp); return DISAS_NORETURN; } static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, TCGv cmp, uint64_t imm, int32_t disp) { - uint64_t dest = ctx->base.pc_next + disp; TCGLabel *lab_true = gen_new_label(); tcg_gen_brcondi_i64(cond, cmp, imm, lab_true); - if (use_goto_tb(ctx, ctx->base.pc_next)) { - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb(ctx->base.tb, 0); - } else { - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_lookup_and_goto_ptr(); - } + gen_goto_tb(ctx, 0, 0); gen_set_label(lab_true); - if (use_goto_tb(ctx, dest)) { - tcg_gen_goto_tb(1); - tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, 1); - } else { - tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } + gen_goto_tb(ctx, 1, disp); return DISAS_NORETURN; } @@ -2922,12 +2905,8 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_NORETURN: break; case DISAS_TOO_MANY: - if (use_goto_tb(ctx, ctx->base.pc_next)) { - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb(ctx->base.tb, 0); - } - /* FALLTHRU */ + gen_goto_tb(ctx, 0, 0); + break; case DISAS_PC_STALE: tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); /* FALLTHRU */ From b1a3eacf314ba829506f65c227b57676db4ddbac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:12 +0200 Subject: [PATCH 0429/2884] target/alpha: Split out gen_pc_disp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for pcrel by not modifying cpu_pc before use, in the case of JSR. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-9-philmd@linaro.org> --- target/alpha/translate.c | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index c1a55e5153..86402d96d5 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -252,6 +252,11 @@ static void st_flag_byte(TCGv val, unsigned shift) tcg_gen_st8_i64(val, tcg_env, get_flag_ofs(shift)); } +static void gen_pc_disp(DisasContext *ctx, TCGv dest, int32_t disp) +{ + tcg_gen_movi_i64(dest, ctx->base.pc_next + disp); +} + static void gen_excp_1(int exception, int error_code) { TCGv_i32 tmp1, tmp2; @@ -263,7 +268,7 @@ static void gen_excp_1(int exception, int error_code) static DisasJumpType gen_excp(DisasContext *ctx, int exception, int error_code) { - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + gen_pc_disp(ctx, cpu_pc, 0); gen_excp_1(exception, error_code); return DISAS_NORETURN; } @@ -427,14 +432,12 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp) { - uint64_t dest = ctx->base.pc_next + disp; - - if (translator_use_goto_tb(&ctx->base, dest)) { + if (translator_use_goto_tb(&ctx->base, ctx->base.pc_next + disp)) { tcg_gen_goto_tb(idx); - tcg_gen_movi_i64(cpu_pc, dest); + gen_pc_disp(ctx, cpu_pc, disp); tcg_gen_exit_tb(ctx->base.tb, idx); } else { - tcg_gen_movi_i64(cpu_pc, dest); + gen_pc_disp(ctx, cpu_pc, disp); tcg_gen_lookup_and_goto_ptr(); } } @@ -442,7 +445,7 @@ static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp) static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) { if (ra != 31) { - tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next); + gen_pc_disp(ctx, ctx->ir[ra], 0); } /* Notice branch-to-next; used to initialize RA with the PC. */ @@ -1091,7 +1094,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) } /* Allow interrupts to be recognized right away. */ - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + gen_pc_disp(ctx, cpu_pc, 0); return DISAS_PC_UPDATED_NOCHAIN; case 0x36: @@ -1138,19 +1141,17 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) #else { TCGv tmp = tcg_temp_new(); - uint64_t exc_addr = ctx->base.pc_next; - uint64_t entry = ctx->palbr; + uint64_t entry; + gen_pc_disp(ctx, tmp, 0); if (ctx->tbflags & ENV_FLAG_PAL_MODE) { - exc_addr |= 1; + tcg_gen_ori_i64(tmp, tmp, 1); } else { - tcg_gen_movi_i64(tmp, 1); - st_flag_byte(tmp, ENV_FLAG_PAL_SHIFT); + st_flag_byte(tcg_constant_i64(1), ENV_FLAG_PAL_SHIFT); } - - tcg_gen_movi_i64(tmp, exc_addr); tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUAlphaState, exc_addr)); + entry = ctx->palbr; entry += (palcode & 0x80 ? 0x2000 + (palcode - 0x80) * 64 : 0x1000 + palcode * 64); @@ -2344,9 +2345,13 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch prediction stack action, which of course we don't implement. */ vb = load_gpr(ctx, rb); - tcg_gen_andi_i64(cpu_pc, vb, ~3); if (ra != 31) { - tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next); + tmp = tcg_temp_new(); + tcg_gen_andi_i64(tmp, vb, ~3); + gen_pc_disp(ctx, ctx->ir[ra], 0); + tcg_gen_mov_i64(cpu_pc, tmp); + } else { + tcg_gen_andi_i64(cpu_pc, vb, ~3); } ret = DISAS_PC_UPDATED; break; @@ -2908,7 +2913,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_goto_tb(ctx, 0, 0); break; case DISAS_PC_STALE: - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + gen_pc_disp(ctx, cpu_pc, 0); /* FALLTHRU */ case DISAS_PC_UPDATED: tcg_gen_lookup_and_goto_ptr(); From 23bb086350c0de390f77dd034d775742314cabd7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 May 2024 09:20:13 +0200 Subject: [PATCH 0430/2884] target/alpha: Implement CF_PCREL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240503072014.24751-10-philmd@linaro.org> --- target/alpha/cpu.c | 23 ++++++++++++++++++++++- target/alpha/translate.c | 29 +++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index f98d022671..0e2fbcb397 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -38,12 +38,27 @@ static vaddr alpha_cpu_get_pc(CPUState *cs) return env->pc; } +static void alpha_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) +{ + /* The program counter is always up to date with CF_PCREL. */ + if (!(tb_cflags(tb) & CF_PCREL)) { + CPUAlphaState *env = cpu_env(cs); + env->pc = tb->pc; + } +} + static void alpha_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { CPUAlphaState *env = cpu_env(cs); - env->pc = data[0]; + + if (tb_cflags(tb) & CF_PCREL) { + env->pc = (env->pc & TARGET_PAGE_MASK) | data[0]; + } else { + env->pc = data[0]; + } } static bool alpha_cpu_has_work(CPUState *cs) @@ -78,6 +93,11 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) AlphaCPUClass *acc = ALPHA_CPU_GET_CLASS(dev); Error *local_err = NULL; +#ifndef CONFIG_USER_ONLY + /* Use pc-relative instructions in system-mode */ + cs->tcg_cflags |= CF_PCREL; +#endif + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -190,6 +210,7 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { static const TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, + .synchronize_from_tb = alpha_cpu_synchronize_from_tb, .restore_state_to_opc = alpha_restore_state_to_opc, #ifdef CONFIG_USER_ONLY diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 86402d96d5..db847e7a23 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -54,6 +54,9 @@ struct DisasContext { uint32_t tbflags; int mem_idx; + /* True if generating pc-relative code. */ + bool pcrel; + /* implver and amask values for this CPU. */ int implver; int amask; @@ -254,7 +257,12 @@ static void st_flag_byte(TCGv val, unsigned shift) static void gen_pc_disp(DisasContext *ctx, TCGv dest, int32_t disp) { - tcg_gen_movi_i64(dest, ctx->base.pc_next + disp); + uint64_t addr = ctx->base.pc_next + disp; + if (ctx->pcrel) { + tcg_gen_addi_i64(dest, cpu_pc, addr - ctx->base.pc_first); + } else { + tcg_gen_movi_i64(dest, addr); + } } static void gen_excp_1(int exception, int error_code) @@ -433,8 +441,14 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp) { if (translator_use_goto_tb(&ctx->base, ctx->base.pc_next + disp)) { - tcg_gen_goto_tb(idx); - gen_pc_disp(ctx, cpu_pc, disp); + /* With PCREL, PC must always be up-to-date. */ + if (ctx->pcrel) { + gen_pc_disp(ctx, cpu_pc, disp); + tcg_gen_goto_tb(idx); + } else { + tcg_gen_goto_tb(idx); + gen_pc_disp(ctx, cpu_pc, disp); + } tcg_gen_exit_tb(ctx->base.tb, idx); } else { gen_pc_disp(ctx, cpu_pc, disp); @@ -2852,6 +2866,7 @@ static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) ctx->tbflags = ctx->base.tb->flags; ctx->mem_idx = alpha_env_mmu_index(env); + ctx->pcrel = ctx->base.tb->cflags & CF_PCREL; ctx->implver = env->implver; ctx->amask = env->amask; @@ -2887,7 +2902,13 @@ static void alpha_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void alpha_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { - tcg_gen_insn_start(dcbase->pc_next); + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + if (ctx->pcrel) { + tcg_gen_insn_start(dcbase->pc_next & ~TARGET_PAGE_MASK); + } else { + tcg_gen_insn_start(dcbase->pc_next); + } } static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) From 6b568e3f1dc22e839cd56b47e22c2aa5ece21367 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Apr 2024 10:48:09 +0200 Subject: [PATCH 0431/2884] target/sparc/cpu: Rename the CPU models with a "+" in their names Commit b447378e12 ("qom/object: Limit type names to alphanumerical ...") cut down the amount of allowed characters for QOM types to a saner set. The "+" character was meant to be included in this set, so we had to add a hack there to still allow the legacy names of POWER and Sparc64 CPUs. However, instead of putting such a hack in the common QOM code, there is a much better place to do this: The sparc_cpu_class_by_name() function which is used to look up the names of all Sparc CPUs. Thus let's finally get rid of the "+" in the Sparc CPU names, and provide backward compatibility for the old names via some simple checks in the sparc_cpu_class_by_name() function. Reviewed-by: Mark Cave-Ayland Signed-off-by: Thomas Huth Reviewed-by: Richard Henderson Message-Id: <20240419084812.504779-2-thuth@redhat.com> Signed-off-by: Mark Cave-Ayland --- qom/object.c | 8 -------- target/sparc/cpu.c | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/qom/object.c b/qom/object.c index 44ec8f6460..157a45c5f8 100644 --- a/qom/object.c +++ b/qom/object.c @@ -157,14 +157,6 @@ static bool type_name_is_valid(const char *name) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789-_."); - /* Allow some legacy names with '+' in it for compatibility reasons */ - if (name[plen] == '+') { - if (plen >= 17 && g_str_has_prefix(name, "Sun-UltraSparc-I")) { - /* Allow "Sun-UltraSparc-IV+" and "Sun-UltraSparc-IIIi+" */ - return true; - } - } - return plen == slen; } diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 485d416925..7487ae034d 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -314,7 +314,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IV+", + .name = "Sun UltraSparc IV plus", .iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -323,7 +323,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT, }, { - .name = "Sun UltraSparc IIIi+", + .name = "Sun UltraSparc IIIi plus", .iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_3, @@ -762,6 +762,16 @@ static ObjectClass *sparc_cpu_class_by_name(const char *cpu_model) char *typename; typename = sparc_cpu_type_name(cpu_model); + + /* Fix up legacy names with '+' in it */ + if (g_str_equal(typename, SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IV+"))) { + g_free(typename); + typename = g_strdup(SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IV-plus")); + } else if (g_str_equal(typename, SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IIIi+"))) { + g_free(typename); + typename = g_strdup(SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IIIi-plus")); + } + oc = object_class_by_name(typename); g_free(typename); return oc; From 4a7bdec3a6f3f6d9f75715e420a4c826135a1065 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Apr 2024 10:48:10 +0200 Subject: [PATCH 0432/2884] target/sparc/cpu: Avoid spaces by default in the CPU names The output of "-cpu help" is currently rather confusing to the users: It might not be fully clear which part of the output defines the CPU names since the CPU names contain white spaces (which we later have to convert into dashes internally). At best it's at least a nuisance since the users might need to specify the CPU names with quoting on the command line if they are not aware of the fact that the CPU names could be written with dashes instead. So let's finally clean up this mess by using dashes instead of white spaces for the CPU names, like we're doing it internally later (and like we're doing it in most other targets of QEMU). Note that it is still possible to pass the CPU names with spaces to the "-cpu" option, since sparc_cpu_type_name() still translates those to "-". Fixes: https://gitlab.com/qemu-project/qemu/-/issues/2141 Reviewed-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Signed-off-by: Thomas Huth Message-Id: <20240419084812.504779-3-thuth@redhat.com> Signed-off-by: Mark Cave-Ayland --- target/sparc/cpu.c | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 7487ae034d..c2be4a00b6 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -206,7 +206,7 @@ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu) static const sparc_def_t sparc_defs[] = { #ifdef TARGET_SPARC64 { - .name = "Fujitsu Sparc64", + .name = "Fujitsu-Sparc64", .iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -215,7 +215,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu Sparc64 III", + .name = "Fujitsu-Sparc64-III", .iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -224,7 +224,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu Sparc64 IV", + .name = "Fujitsu-Sparc64-IV", .iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -233,7 +233,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu Sparc64 V", + .name = "Fujitsu-Sparc64-V", .iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -242,7 +242,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc I", + .name = "TI-UltraSparc-I", .iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -251,7 +251,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc II", + .name = "TI-UltraSparc-II", .iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -260,7 +260,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc IIi", + .name = "TI-UltraSparc-IIi", .iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -269,7 +269,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc IIe", + .name = "TI-UltraSparc-IIe", .iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -278,7 +278,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc III", + .name = "Sun-UltraSparc-III", .iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -287,7 +287,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc III Cu", + .name = "Sun-UltraSparc-III-Cu", .iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_3, @@ -296,7 +296,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IIIi", + .name = "Sun-UltraSparc-IIIi", .iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -305,7 +305,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IV", + .name = "Sun-UltraSparc-IV", .iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_4, @@ -314,7 +314,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IV plus", + .name = "Sun-UltraSparc-IV-plus", .iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -323,7 +323,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT, }, { - .name = "Sun UltraSparc IIIi plus", + .name = "Sun-UltraSparc-IIIi-plus", .iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_3, @@ -332,7 +332,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc T1", + .name = "Sun-UltraSparc-T1", /* defined in sparc_ifu_fdp.v and ctu.h */ .iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)), .fpu_version = 0x00000000, @@ -343,7 +343,7 @@ static const sparc_def_t sparc_defs[] = { | CPU_FEATURE_GL, }, { - .name = "Sun UltraSparc T2", + .name = "Sun-UltraSparc-T2", /* defined in tlu_asi_ctl.v and n2_revid_cust.v */ .iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)), .fpu_version = 0x00000000, @@ -354,7 +354,7 @@ static const sparc_def_t sparc_defs[] = { | CPU_FEATURE_GL, }, { - .name = "NEC UltraSparc I", + .name = "NEC-UltraSparc-I", .iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -364,7 +364,7 @@ static const sparc_def_t sparc_defs[] = { }, #else { - .name = "Fujitsu MB86904", + .name = "Fujitsu-MB86904", .iu_version = 0x04 << 24, /* Impl 0, ver 4 */ .fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */ .mmu_version = 0x04 << 24, /* Impl 0, ver 4 */ @@ -377,7 +377,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu MB86907", + .name = "Fujitsu-MB86907", .iu_version = 0x05 << 24, /* Impl 0, ver 5 */ .fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */ .mmu_version = 0x05 << 24, /* Impl 0, ver 5 */ @@ -390,7 +390,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI MicroSparc I", + .name = "TI-MicroSparc-I", .iu_version = 0x41000000, .fpu_version = 4 << FSR_VER_SHIFT, .mmu_version = 0x41000000, @@ -403,7 +403,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_FEATURE_MUL | CPU_FEATURE_DIV, }, { - .name = "TI MicroSparc II", + .name = "TI-MicroSparc-II", .iu_version = 0x42000000, .fpu_version = 4 << FSR_VER_SHIFT, .mmu_version = 0x02000000, @@ -416,7 +416,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI MicroSparc IIep", + .name = "TI-MicroSparc-IIep", .iu_version = 0x42000000, .fpu_version = 4 << FSR_VER_SHIFT, .mmu_version = 0x04000000, @@ -429,7 +429,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 40", /* STP1020NPGA */ + .name = "TI-SuperSparc-40", /* STP1020NPGA */ .iu_version = 0x41000000, /* SuperSPARC 2.x */ .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x00000800, /* SuperSPARC 2.x, no MXCC */ @@ -442,7 +442,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 50", /* STP1020PGA */ + .name = "TI-SuperSparc-50", /* STP1020PGA */ .iu_version = 0x40000000, /* SuperSPARC 3.x */ .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */ @@ -455,7 +455,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 51", + .name = "TI-SuperSparc-51", .iu_version = 0x40000000, /* SuperSPARC 3.x */ .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */ @@ -469,7 +469,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 60", /* STP1020APGA */ + .name = "TI-SuperSparc-60", /* STP1020APGA */ .iu_version = 0x40000000, /* SuperSPARC 3.x */ .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */ @@ -482,7 +482,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 61", + .name = "TI-SuperSparc-61", .iu_version = 0x44000000, /* SuperSPARC 3.x */ .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */ @@ -496,7 +496,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc II", + .name = "TI-SuperSparc-II", .iu_version = 0x40000000, /* SuperSPARC II 1.x */ .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x08000000, /* SuperSPARC II 1.x, MXCC */ From 49ce37e3d024260072bdfa4c5569859d1f338cb2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Apr 2024 10:48:11 +0200 Subject: [PATCH 0433/2884] docs/system/target-sparc: Improve the Sparc documentation Add some words about how to enable or disable boolean features, and remove the note about a Linux kernel being available on the QEMU website (they have been removed long ago already), and the note about NetBSD and OpenBSD still having issues (they should work fine nowadays). Fixes: https://gitlab.com/qemu-project/qemu/-/issues/2141 Signed-off-by: Thomas Huth Reviewed-by: Mark Cave-Ayland Reviewed-by: Richard Henderson Message-Id: <20240419084812.504779-4-thuth@redhat.com> Signed-off-by: Mark Cave-Ayland --- docs/system/target-sparc.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/system/target-sparc.rst b/docs/system/target-sparc.rst index 9ec8c90c14..4116cac493 100644 --- a/docs/system/target-sparc.rst +++ b/docs/system/target-sparc.rst @@ -27,6 +27,11 @@ architecture machines: The emulation is somewhat complete. SMP up to 16 CPUs is supported, but Linux limits the number of usable CPUs to 4. +The list of available CPUs can be viewed by starting QEMU with ``-cpu help``. +Optional boolean features can be added with a "+" in front of the feature name, +or disabled with a "-" in front of the name, for example +``-cpu TI-SuperSparc-II,+float128``. + QEMU emulates the following sun4m peripherals: - IOMMU @@ -55,8 +60,5 @@ OpenBIOS is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. -A sample Linux 2.6 series kernel and ram disk image are available on the -QEMU web site. There are still issues with NetBSD and OpenBSD, but most -kernel versions work. Please note that currently older Solaris kernels -don't work probably due to interface issues between OpenBIOS and -Solaris. +Please note that currently older Solaris kernels don't work; this is probably +due to interface issues between OpenBIOS and Solaris. From 029171b5d62fe11a0dce52f64efb356f7fea144d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 19 Apr 2024 10:48:12 +0200 Subject: [PATCH 0434/2884] docs/about: Deprecate the old "UltraSparc" CPU names that contain a "+" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency we should drop the names with a "+" in it in the long run. Reviewed-by: Mark Cave-Ayland Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240419084812.504779-5-thuth@redhat.com> Signed-off-by: Mark Cave-Ayland --- docs/about/deprecated.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 7b8aafa15b..03f8b1b655 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -194,6 +194,15 @@ in the QEMU object model anymore. ``power5+``, ``power5+_v2.1``, an alias, but for consistency these will get removed in a future release, too. Use ``power5p_v2.1`` and ``power7p_v2.1`` instead. +``Sun-UltraSparc-IIIi+`` and ``Sun-UltraSparc-IV+`` CPU names (since 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The character "+" in device (and thus also CPU) names is not allowed +in the QEMU object model anymore. ``Sun-UltraSparc-IIIi+`` and +``Sun-UltraSparc-IV+`` are currently still supported via a workaround, +but for consistency these will get removed in a future release, too. +Use ``Sun-UltraSparc-IIIi-plus`` and ``Sun-UltraSparc-IV-plus`` instead. + CRIS CPU architecture (since 9.0) ''''''''''''''''''''''''''''''''' From 7c420a4d7c080002018af20aed56ceb0bf81ad43 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 18 Apr 2024 21:57:30 +0100 Subject: [PATCH 0435/2884] hw/sparc64: set iommu_platform=on for virtio devices attached to the sun4u machine The sun4u machine has an IOMMU and therefore it is possible to program it such that the virtio-device IOVA does not map directly to the CPU physical address. This is not a problem with Linux which always maps the IOVA directly to the CPU physical address, however it is required for the NetBSD virtio driver where this is not the case. Set the sun4u machine defaults for all virtio devices so that disable-legacy=on and iommu_platform=on to ensure a default configuration will allow virtio devices to function correctly on both Linux and NetBSD. Signed-off-by: Mark Cave-Ayland Message-Id: <20240418205730.31396-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Mark Cave-Ayland --- hw/sparc64/sun4u.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index cff6d5abaf..4ece1ac1ff 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -793,6 +793,12 @@ static void sun4v_init(MachineState *machine) sun4uv_init(get_system_memory(), machine, &hwdefs[1]); } +static GlobalProperty hw_compat_sparc64[] = { + { "virtio-pci", "disable-legacy", "on", .optional = true }, + { "virtio-device", "iommu_platform", "on" }, +}; +static const size_t hw_compat_sparc64_len = G_N_ELEMENTS(hw_compat_sparc64); + static void sun4u_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -810,6 +816,7 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->default_nic = "sunhme"; mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); fwc->get_dev_path = sun4u_fw_dev_path; + compat_props_add(mc->compat_props, hw_compat_sparc64, hw_compat_sparc64_len); } static const TypeInfo sun4u_type = { From 1cde1a2a89e7c6ac1c8240b09f9b3d066cd01270 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:22 -0700 Subject: [PATCH 0436/2884] linux-user/sparc: Add more hwcap bits for sparc64 Supply HWCAP_SPARC_V8PLUS, HWCAP_SPARC_MUL32, HWCAP_SPARC_DIV32, HWCAP_SPARC_POPC, HWCAP_SPARC_FSMULD, HWCAP_SPARC_VIS, HWCAP_SPARC_VIS2. Signed-off-by: Richard Henderson Message-Id: <20240502165528.244004-2-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- linux-user/elfload.c | 48 +++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f9461d2844..14f08b64a1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -968,24 +968,44 @@ const char *elf_hwcap2_str(uint32_t bit) #endif /* TARGET_ARM */ #ifdef TARGET_SPARC -#ifdef TARGET_SPARC64 -#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ - | HWCAP_SPARC_MULDIV | HWCAP_SPARC_V9) -#ifndef TARGET_ABI32 -#define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS ) +#ifndef TARGET_SPARC64 +# define ELF_CLASS ELFCLASS32 +# define ELF_ARCH EM_SPARC +#elif defined(TARGET_ABI32) +# define ELF_CLASS ELFCLASS32 +# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else -#define elf_check_arch(x) ( (x) == EM_SPARC32PLUS || (x) == EM_SPARC ) +# define ELF_CLASS ELFCLASS64 +# define ELF_ARCH EM_SPARCV9 #endif -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_SPARCV9 -#else -#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ - | HWCAP_SPARC_MULDIV) -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_SPARC -#endif /* TARGET_SPARC64 */ +#include "elf.h" + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + /* There are not many sparc32 hwcap bits -- we have all of them. */ + uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | + HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; + +#ifdef TARGET_SPARC64 + CPUSPARCState *env = cpu_env(thread_cpu); + uint32_t features = env->def.features; + + r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; + /* 32x32 multiply and divide are efficient. */ + r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; + /* We don't have an internal feature bit for this. */ + r |= HWCAP_SPARC_POPC; + r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; + r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; + r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; +#endif + + return r; +} static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) From 7b616f36de0bde126e1ba6b0793ed26fc414a1ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:23 -0700 Subject: [PATCH 0437/2884] target/sparc: Fix FEXPAND MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a 2-operand instruction, not 3-operand. Worse, we took the source from the wrong operand. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240502165528.244004-3-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- target/sparc/helper.h | 2 +- target/sparc/insns.decode | 2 +- target/sparc/translate.c | 20 +++++++++++++++++++- target/sparc/vis_helper.c | 6 +++--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index b8087d0d2b..57ab755ffd 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -102,7 +102,7 @@ DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(fexpand, TCG_CALL_NO_RWG_SE, i64, i32) DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 2d26404cb2..e2d8a07dc4 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -352,7 +352,7 @@ FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @r_r_r FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @r_r_r - FEXPAND 10 ..... 110110 ..... 0 0100 1101 ..... @r_r_r + FEXPAND 10 ..... 110110 00000 0 0100 1101 ..... @r_r2 FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 571b3e3f03..dfcfe855a1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4358,6 +4358,25 @@ TRANS(FSQRTd, ALL, do_env_dd, a, gen_helper_fsqrtd) TRANS(FxTOd, 64, do_env_dd, a, gen_helper_fxtod) TRANS(FdTOx, 64, do_env_dd, a, gen_helper_fdtox) +static bool do_df(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_i32)) +{ + TCGv_i64 dst; + TCGv_i32 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src = gen_load_fpr_F(dc, a->rs); + func(dst, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FEXPAND, VIS1, do_df, a, gen_helper_fexpand) + static bool do_env_df(DisasContext *dc, arg_r_r *a, void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) { @@ -4589,7 +4608,6 @@ TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) TRANS(FMULD8SUx16, VIS1, do_ddd, a, gen_helper_fmuld8sux16) TRANS(FMULD8ULx16, VIS1, do_ddd, a, gen_helper_fmuld8ulx16) TRANS(FPMERGE, VIS1, do_ddd, a, gen_helper_fpmerge) -TRANS(FEXPAND, VIS1, do_ddd, a, gen_helper_fexpand) TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64) TRANS(FPADD32, VIS1, do_ddd, a, tcg_gen_vec_add32_i64) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 7763b16c24..db2e6dd6c1 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -260,13 +260,13 @@ uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2) return d.ll; } -uint64_t helper_fexpand(uint64_t src1, uint64_t src2) +uint64_t helper_fexpand(uint32_t src2) { VIS32 s; VIS64 d; - s.l = (uint32_t)src1; - d.ll = src2; + s.l = src2; + d.ll = 0; d.VIS_W64(0) = s.VIS_B32(0) << 4; d.VIS_W64(1) = s.VIS_B32(1) << 4; d.VIS_W64(2) = s.VIS_B32(2) << 4; From 9157dccc7e71f7c94581c38f38acbef9a21bbe9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:24 -0700 Subject: [PATCH 0438/2884] target/sparc: Fix FMUL8x16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This instruction has f32 as source1, which alters the decoding of the register number, which means we've been passing the wrong data for odd register numbers. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240502165528.244004-4-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- target/sparc/helper.h | 2 +- target/sparc/translate.c | 21 ++++++++++++++++++++- target/sparc/vis_helper.c | 9 +++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 57ab755ffd..27dc604cac 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -95,7 +95,7 @@ DEF_HELPER_FLAGS_2(fdtox, TCG_CALL_NO_WG, s64, env, f64) DEF_HELPER_FLAGS_2(fqtox, TCG_CALL_NO_WG, s64, env, i128) DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i32, i64) DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index dfcfe855a1..c4adc148d2 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4583,6 +4583,26 @@ TRANS(FSUBs, ALL, do_env_fff, a, gen_helper_fsubs) TRANS(FMULs, ALL, do_env_fff, a, gen_helper_fmuls) TRANS(FDIVs, ALL, do_env_fff, a, gen_helper_fdivs) +static bool do_dfd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i32, TCGv_i64)) +{ + TCGv_i64 dst, src2; + TCGv_i32 src1; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMUL8x16, VIS1, do_dfd, a, gen_helper_fmul8x16) + static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -4600,7 +4620,6 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a, return advance_pc(dc); } -TRANS(FMUL8x16, VIS1, do_ddd, a, gen_helper_fmul8x16) TRANS(FMUL8x16AU, VIS1, do_ddd, a, gen_helper_fmul8x16au) TRANS(FMUL8x16AL, VIS1, do_ddd, a, gen_helper_fmul8x16al) TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index db2e6dd6c1..7728ffe9c6 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -94,16 +94,17 @@ uint64_t helper_fpmerge(uint64_t src1, uint64_t src2) return d.ll; } -uint64_t helper_fmul8x16(uint64_t src1, uint64_t src2) +uint64_t helper_fmul8x16(uint32_t src1, uint64_t src2) { - VIS64 s, d; + VIS64 d; + VIS32 s; uint32_t tmp; - s.ll = src1; + s.l = src1; d.ll = src2; #define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \ + tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B32(r); \ if ((tmp & 0xff) > 0x7f) { \ tmp += 0x100; \ } \ From a859602c746baf4892cc8ca1ce003e92411d1716 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:25 -0700 Subject: [PATCH 0439/2884] target/sparc: Fix FMUL8x16A{U,L} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These instructions have f32 inputs, which changes the decode of the register numbers. While we're fixing things, use a common helper for both insns, extracting the 16-bit scalar in tcg beforehand. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240502165528.244004-5-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- target/sparc/helper.h | 3 +-- target/sparc/translate.c | 38 +++++++++++++++++++++++++++---- target/sparc/vis_helper.c | 47 +++++++++++---------------------------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 27dc604cac..9cde2b69a5 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -96,8 +96,7 @@ DEF_HELPER_FLAGS_2(fqtox, TCG_CALL_NO_WG, s64, env, i128) DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i32, i64) -DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8x16a, TCG_CALL_NO_RWG_SE, i64, i32, s32) DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index c4adc148d2..a8ada6934a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -45,6 +45,7 @@ # define gen_helper_clear_softint(E, S) qemu_build_not_reached() # define gen_helper_done(E) qemu_build_not_reached() # define gen_helper_flushw(E) qemu_build_not_reached() +# define gen_helper_fmul8x16a(D, S1, S2) qemu_build_not_reached() # define gen_helper_rdccr(D, E) qemu_build_not_reached() # define gen_helper_rdcwp(D, E) qemu_build_not_reached() # define gen_helper_restored(E) qemu_build_not_reached() @@ -72,8 +73,6 @@ # define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8ulx16 ({ qemu_build_not_reached(); NULL; }) -# define gen_helper_fmul8x16al ({ qemu_build_not_reached(); NULL; }) -# define gen_helper_fmul8x16au ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) @@ -719,6 +718,18 @@ static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) #endif } +static void gen_op_fmul8x16al(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) +{ + tcg_gen_ext16s_i32(src2, src2); + gen_helper_fmul8x16a(dst, src1, src2); +} + +static void gen_op_fmul8x16au(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) +{ + tcg_gen_sari_i32(src2, src2, 16); + gen_helper_fmul8x16a(dst, src1, src2); +} + static void finishing_insn(DisasContext *dc) { /* @@ -4583,6 +4594,27 @@ TRANS(FSUBs, ALL, do_env_fff, a, gen_helper_fsubs) TRANS(FMULs, ALL, do_env_fff, a, gen_helper_fmuls) TRANS(FDIVs, ALL, do_env_fff, a, gen_helper_fdivs) +static bool do_dff(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i32, TCGv_i32)) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + func(dst, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMUL8x16AU, VIS1, do_dff, a, gen_op_fmul8x16au) +TRANS(FMUL8x16AL, VIS1, do_dff, a, gen_op_fmul8x16al) + static bool do_dfd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i32, TCGv_i64)) { @@ -4620,8 +4652,6 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a, return advance_pc(dc); } -TRANS(FMUL8x16AU, VIS1, do_ddd, a, gen_helper_fmul8x16au) -TRANS(FMUL8x16AL, VIS1, do_ddd, a, gen_helper_fmul8x16al) TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) TRANS(FMULD8SUx16, VIS1, do_ddd, a, gen_helper_fmuld8sux16) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 7728ffe9c6..ff2f43c23f 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -119,44 +119,23 @@ uint64_t helper_fmul8x16(uint32_t src1, uint64_t src2) return d.ll; } -uint64_t helper_fmul8x16al(uint64_t src1, uint64_t src2) +uint64_t helper_fmul8x16a(uint32_t src1, int32_t src2) { - VIS64 s, d; + VIS32 s; + VIS64 d; uint32_t tmp; - s.ll = src1; - d.ll = src2; + s.l = src1; + d.ll = 0; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL - - return d.ll; -} - -uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2) -{ - VIS64 s, d; - uint32_t tmp; - - s.ll = src1; - d.ll = src2; - -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; +#define PMUL(r) \ + do { \ + tmp = src2 * (int32_t)s.VIS_B32(r); \ + if ((tmp & 0xff) > 0x7f) { \ + tmp += 0x100; \ + } \ + d.VIS_W64(r) = tmp >> 8; \ + } while (0) PMUL(0); PMUL(1); From be8998e046c2a7e434494b75cf468ffd9d536025 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:26 -0700 Subject: [PATCH 0440/2884] target/sparc: Fix FMULD8*X16 Not only do these instructions have f32 inputs, they also do not perform rounding. Since these are relatively simple, implement them properly inline. Signed-off-by: Richard Henderson Message-Id: <20240502165528.244004-6-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- target/sparc/helper.h | 2 -- target/sparc/translate.c | 48 +++++++++++++++++++++++++++++++++++---- target/sparc/vis_helper.c | 46 ------------------------------------- 3 files changed, 44 insertions(+), 52 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 9cde2b69a5..fcb9c617b7 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -99,8 +99,6 @@ DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i32, i64) DEF_HELPER_FLAGS_2(fmul8x16a, TCG_CALL_NO_RWG_SE, i64, i32, s32) DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_1(fexpand, TCG_CALL_NO_RWG_SE, i64, i32) DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index a8ada6934a..8a2894bb9f 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -74,8 +74,6 @@ # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; }) -# define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) -# define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fqtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) @@ -730,6 +728,48 @@ static void gen_op_fmul8x16au(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) gen_helper_fmul8x16a(dst, src1, src2); } +static void gen_op_fmuld8ulx16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_ext8u_i32(t0, src1); + tcg_gen_ext16s_i32(t1, src2); + tcg_gen_mul_i32(t0, t0, t1); + + tcg_gen_extract_i32(t1, src1, 16, 8); + tcg_gen_sextract_i32(t2, src2, 16, 16); + tcg_gen_mul_i32(t1, t1, t2); + + tcg_gen_concat_i32_i64(dst, t0, t1); +} + +static void gen_op_fmuld8sux16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + /* + * The insn description talks about extracting the upper 8 bits + * of the signed 16-bit input rs1, performing the multiply, then + * shifting left by 8 bits. Instead, zap the lower 8 bits of + * the rs1 input, which avoids the need for two shifts. + */ + tcg_gen_ext16s_i32(t0, src1); + tcg_gen_andi_i32(t0, t0, ~0xff); + tcg_gen_ext16s_i32(t1, src2); + tcg_gen_mul_i32(t0, t0, t1); + + tcg_gen_sextract_i32(t1, src1, 16, 16); + tcg_gen_andi_i32(t1, t1, ~0xff); + tcg_gen_sextract_i32(t2, src2, 16, 16); + tcg_gen_mul_i32(t1, t1, t2); + + tcg_gen_concat_i32_i64(dst, t0, t1); +} + static void finishing_insn(DisasContext *dc) { /* @@ -4614,6 +4654,8 @@ static bool do_dff(DisasContext *dc, arg_r_r_r *a, TRANS(FMUL8x16AU, VIS1, do_dff, a, gen_op_fmul8x16au) TRANS(FMUL8x16AL, VIS1, do_dff, a, gen_op_fmul8x16al) +TRANS(FMULD8SUx16, VIS1, do_dff, a, gen_op_fmuld8sux16) +TRANS(FMULD8ULx16, VIS1, do_dff, a, gen_op_fmuld8ulx16) static bool do_dfd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i32, TCGv_i64)) @@ -4654,8 +4696,6 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a, TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) -TRANS(FMULD8SUx16, VIS1, do_ddd, a, gen_helper_fmuld8sux16) -TRANS(FMULD8ULx16, VIS1, do_ddd, a, gen_helper_fmuld8ulx16) TRANS(FPMERGE, VIS1, do_ddd, a, gen_helper_fpmerge) TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index ff2f43c23f..61c61c7fea 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -194,52 +194,6 @@ uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2) return d.ll; } -uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2) -{ - VIS64 s, d; - uint32_t tmp; - - s.ll = src1; - d.ll = src2; - -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_L64(r) = tmp; - - /* Reverse calculation order to handle overlap */ - PMUL(1); - PMUL(0); -#undef PMUL - - return d.ll; -} - -uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2) -{ - VIS64 s, d; - uint32_t tmp; - - s.ll = src1; - d.ll = src2; - -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_L64(r) = tmp; - - /* Reverse calculation order to handle overlap */ - PMUL(1); - PMUL(0); -#undef PMUL - - return d.ll; -} - uint64_t helper_fexpand(uint32_t src2) { VIS32 s; From d3ef26afde77fbdedd5b30282134ff99d0fe5cc5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:27 -0700 Subject: [PATCH 0441/2884] target/sparc: Fix FPMERGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This instruction has f32 inputs, which changes the decode of the register numbers. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240502165528.244004-7-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- target/sparc/helper.h | 2 +- target/sparc/translate.c | 2 +- target/sparc/vis_helper.c | 27 ++++++++++++++------------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index fcb9c617b7..97fbf6f66c 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -94,7 +94,7 @@ DEF_HELPER_FLAGS_2(fstox, TCG_CALL_NO_WG, s64, env, f32) DEF_HELPER_FLAGS_2(fdtox, TCG_CALL_NO_WG, s64, env, f64) DEF_HELPER_FLAGS_2(fqtox, TCG_CALL_NO_WG, s64, env, i128) -DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i32, i32) DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i32, i64) DEF_HELPER_FLAGS_2(fmul8x16a, TCG_CALL_NO_RWG_SE, i64, i32, s32) DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 8a2894bb9f..99c6f3cc72 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4656,6 +4656,7 @@ TRANS(FMUL8x16AU, VIS1, do_dff, a, gen_op_fmul8x16au) TRANS(FMUL8x16AL, VIS1, do_dff, a, gen_op_fmul8x16al) TRANS(FMULD8SUx16, VIS1, do_dff, a, gen_op_fmuld8sux16) TRANS(FMULD8ULx16, VIS1, do_dff, a, gen_op_fmuld8ulx16) +TRANS(FPMERGE, VIS1, do_dff, a, gen_helper_fpmerge) static bool do_dfd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i32, TCGv_i64)) @@ -4696,7 +4697,6 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a, TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) -TRANS(FPMERGE, VIS1, do_ddd, a, gen_helper_fpmerge) TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64) TRANS(FPADD32, VIS1, do_ddd, a, tcg_gen_vec_add32_i64) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 61c61c7fea..14c665cad6 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -74,22 +74,23 @@ typedef union { float32 f; } VIS32; -uint64_t helper_fpmerge(uint64_t src1, uint64_t src2) +uint64_t helper_fpmerge(uint32_t src1, uint32_t src2) { - VIS64 s, d; + VIS32 s1, s2; + VIS64 d; - s.ll = src1; - d.ll = src2; + s1.l = src1; + s2.l = src2; + d.ll = 0; - /* Reverse calculation order to handle overlap */ - d.VIS_B64(7) = s.VIS_B64(3); - d.VIS_B64(6) = d.VIS_B64(3); - d.VIS_B64(5) = s.VIS_B64(2); - d.VIS_B64(4) = d.VIS_B64(2); - d.VIS_B64(3) = s.VIS_B64(1); - d.VIS_B64(2) = d.VIS_B64(1); - d.VIS_B64(1) = s.VIS_B64(0); - /* d.VIS_B64(0) = d.VIS_B64(0); */ + d.VIS_B64(7) = s1.VIS_B32(3); + d.VIS_B64(6) = s2.VIS_B32(3); + d.VIS_B64(5) = s1.VIS_B32(2); + d.VIS_B64(4) = s2.VIS_B32(2); + d.VIS_B64(3) = s1.VIS_B32(1); + d.VIS_B64(2) = s2.VIS_B32(1); + d.VIS_B64(1) = s1.VIS_B32(0); + d.VIS_B64(0) = s2.VIS_B32(0); return d.ll; } From d6f898cf85c92389182d22f0bcc3a11d7194fc94 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 May 2024 09:55:28 -0700 Subject: [PATCH 0442/2884] target/sparc: Split out do_ms16b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unit operation for fmul8x16 and friends is described in the manual as "MS16b". Split that out for clarity. Improve rounding with an unconditional addition of 0.5 as a fixed-point integer. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240502165528.244004-8-richard.henderson@linaro.org> Signed-off-by: Mark Cave-Ayland --- target/sparc/vis_helper.c | 78 ++++++++++++--------------------------- 1 file changed, 24 insertions(+), 54 deletions(-) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 14c665cad6..e15c6bb34e 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -44,6 +44,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) #if HOST_BIG_ENDIAN #define VIS_B64(n) b[7 - (n)] +#define VIS_SB64(n) sb[7 - (n)] #define VIS_W64(n) w[3 - (n)] #define VIS_SW64(n) sw[3 - (n)] #define VIS_L64(n) l[1 - (n)] @@ -51,6 +52,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) #define VIS_W32(n) w[1 - (n)] #else #define VIS_B64(n) b[n] +#define VIS_SB64(n) sb[n] #define VIS_W64(n) w[n] #define VIS_SW64(n) sw[n] #define VIS_L64(n) l[n] @@ -60,6 +62,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) typedef union { uint8_t b[8]; + int8_t sb[8]; uint16_t w[4]; int16_t sw[4]; uint32_t l[2]; @@ -95,27 +98,23 @@ uint64_t helper_fpmerge(uint32_t src1, uint32_t src2) return d.ll; } +static inline int do_ms16b(int x, int y) +{ + return ((x * y) + 0x80) >> 8; +} + uint64_t helper_fmul8x16(uint32_t src1, uint64_t src2) { VIS64 d; VIS32 s; - uint32_t tmp; s.l = src1; d.ll = src2; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B32(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_B32(0), d.VIS_SW64(0)); + d.VIS_W64(1) = do_ms16b(s.VIS_B32(1), d.VIS_SW64(1)); + d.VIS_W64(2) = do_ms16b(s.VIS_B32(2), d.VIS_SW64(2)); + d.VIS_W64(3) = do_ms16b(s.VIS_B32(3), d.VIS_SW64(3)); return d.ll; } @@ -124,25 +123,14 @@ uint64_t helper_fmul8x16a(uint32_t src1, int32_t src2) { VIS32 s; VIS64 d; - uint32_t tmp; s.l = src1; d.ll = 0; -#define PMUL(r) \ - do { \ - tmp = src2 * (int32_t)s.VIS_B32(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; \ - } while (0) - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_B32(0), src2); + d.VIS_W64(1) = do_ms16b(s.VIS_B32(1), src2); + d.VIS_W64(2) = do_ms16b(s.VIS_B32(2), src2); + d.VIS_W64(3) = do_ms16b(s.VIS_B32(3), src2); return d.ll; } @@ -150,23 +138,14 @@ uint64_t helper_fmul8x16a(uint32_t src1, int32_t src2) uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2) { VIS64 s, d; - uint32_t tmp; s.ll = src1; d.ll = src2; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_SB64(1), d.VIS_SW64(0)); + d.VIS_W64(1) = do_ms16b(s.VIS_SB64(3), d.VIS_SW64(1)); + d.VIS_W64(2) = do_ms16b(s.VIS_SB64(5), d.VIS_SW64(2)); + d.VIS_W64(3) = do_ms16b(s.VIS_SB64(7), d.VIS_SW64(3)); return d.ll; } @@ -174,23 +153,14 @@ uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2) uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2) { VIS64 s, d; - uint32_t tmp; s.ll = src1; d.ll = src2; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_B64(0), d.VIS_SW64(0)); + d.VIS_W64(1) = do_ms16b(s.VIS_B64(2), d.VIS_SW64(1)); + d.VIS_W64(2) = do_ms16b(s.VIS_B64(4), d.VIS_SW64(2)); + d.VIS_W64(3) = do_ms16b(s.VIS_B64(6), d.VIS_SW64(3)); return d.ll; } From 76eaa97157f6204e04fa1d79529420760f7a408a Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 1 Feb 2024 03:33:38 -0700 Subject: [PATCH 0443/2884] Hexagon (target/hexagon) Analyze reads before writes We divide gen_analyze_funcs.py into 3 phases Declare the operands Analyze the register reads Analyze the register writes We also create special versions of ctx_log_*_read for new operands Check that the operand is written before the read This is a precursor to improving the analysis for short-circuiting the packet semantics in a subsequent commit Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240201103340.119081-2-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/README | 9 +++-- target/hexagon/gen_analyze_funcs.py | 34 ++++++++++------ target/hexagon/hex_common.py | 63 +++++++++++++++-------------- target/hexagon/translate.h | 26 +++++++++++- 4 files changed, 83 insertions(+), 49 deletions(-) diff --git a/target/hexagon/README b/target/hexagon/README index 746ebec378..c1d8c8d0ab 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -183,10 +183,11 @@ when the override is present. } We also generate an analyze_ function for each instruction. Currently, -these functions record the writes to registers by calling ctx_log_*. During -gen_start_packet, we invoke the analyze_ function for each instruction in -the packet, and we mark the implicit writes. After the analysis is performed, -we initialize the result register for each of the predicated assignments. +these functions record the reads and writes to registers by calling ctx_log_*. +During gen_start_packet, we invoke the analyze_ function for each instruction in +the packet, and we mark the implicit writes. The analysis determines if the packet +semantics can be short-circuited. If not, we initialize the result register for each +of the predicated assignments. In addition to instruction semantics, we use a generator to create the decode tree. This generation is a four step process. diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index a9af666cef..890e6a3a95 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -44,15 +44,25 @@ def gen_analyze_func(f, tag, regs, imms): f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") - i = 0 - ## Analyze all the registers - for regtype, regid in regs: - reg = hex_common.get_register(tag, regtype, regid) + ## Declare all the registers + for regno, register in enumerate(regs): + reg_type, reg_id = register + reg = hex_common.get_register(tag, reg_type, reg_id) + reg.decl_reg_num(f, regno) + + ## Analyze the register reads + for regno, register in enumerate(regs): + reg_type, reg_id = register + reg = hex_common.get_register(tag, reg_type, reg_id) + if reg.is_read(): + reg.analyze_read(f, regno) + + ## Analyze the register writes + for regno, register in enumerate(regs): + reg_type, reg_id = register + reg = hex_common.get_register(tag, reg_type, reg_id) if reg.is_written(): - reg.analyze_write(f, tag, i) - else: - reg.analyze_read(f, i) - i += 1 + reg.analyze_write(f, tag, regno) has_generated_helper = not hex_common.skip_qemu_helper( tag @@ -89,13 +99,13 @@ def main(): tagimms = hex_common.get_tagimms() with open(sys.argv[-1], "w") as f: - f.write("#ifndef HEXAGON_TCG_FUNCS_H\n") - f.write("#define HEXAGON_TCG_FUNCS_H\n\n") + f.write("#ifndef HEXAGON_ANALYZE_FUNCS_C_INC\n") + f.write("#define HEXAGON_ANALYZE_FUNCS_C_INC\n\n") for tag in hex_common.tags: gen_analyze_func(f, tag, tagregs[tag], tagimms[tag]) - f.write("#endif /* HEXAGON_TCG_FUNCS_H */\n") + f.write("#endif /* HEXAGON_ANALYZE_FUNCS_C_INC */\n") if __name__ == "__main__": diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 195620c7ec..33801e4bd7 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -425,7 +425,6 @@ class GprDest(Register, Single, Dest): gen_log_reg_write(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ ctx_log_reg_write(ctx, {self.reg_num}, {predicated}); @@ -438,7 +437,6 @@ class GprSource(Register, Single, OldSource): TCGv {self.reg_tcg()} = hex_gpr[{self.reg_num}]; """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_reg_read(ctx, {self.reg_num}); """)) @@ -449,9 +447,8 @@ class GprNewSource(Register, Single, NewSource): TCGv {self.reg_tcg()} = get_result_gpr(ctx, insn->regno[{regno}]); """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ - ctx_log_reg_read(ctx, {self.reg_num}); + ctx_log_reg_read_new(ctx, {self.reg_num}); """)) class GprReadWrite(Register, Single, ReadWrite): @@ -471,8 +468,11 @@ class GprReadWrite(Register, Single, ReadWrite): f.write(code_fmt(f"""\ gen_log_reg_write(ctx, {self.reg_num}, {self.reg_tcg()}); """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_reg_read(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ ctx_log_reg_write(ctx, {self.reg_num}, {predicated}); @@ -493,7 +493,6 @@ class ControlDest(Register, Single, Dest): gen_write_ctrl_reg(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ ctx_log_reg_write(ctx, {self.reg_num}, {predicated}); @@ -511,7 +510,6 @@ class ControlSource(Register, Single, OldSource): gen_read_ctrl_reg(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_reg_read(ctx, {self.reg_num}); """)) @@ -532,7 +530,6 @@ class ModifierSource(Register, Single, OldSource): declared.append(self.reg_tcg()) declared.append("CS") def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_reg_read(ctx, {self.reg_num}); """)) @@ -548,7 +545,6 @@ class PredDest(Register, Single, Dest): gen_log_pred_write(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_pred_write(ctx, {self.reg_num}); """)) @@ -560,7 +556,6 @@ class PredSource(Register, Single, OldSource): TCGv {self.reg_tcg()} = hex_pred[{self.reg_num}]; """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_pred_read(ctx, {self.reg_num}); """)) @@ -571,9 +566,8 @@ class PredNewSource(Register, Single, NewSource): TCGv {self.reg_tcg()} = get_result_pred(ctx, insn->regno[{regno}]); """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ - ctx_log_pred_read(ctx, {self.reg_num}); + ctx_log_pred_read_new(ctx, {self.reg_num}); """)) class PredReadWrite(Register, Single, ReadWrite): @@ -587,8 +581,11 @@ class PredReadWrite(Register, Single, ReadWrite): f.write(code_fmt(f"""\ gen_log_pred_write(ctx, {self.reg_num}, {self.reg_tcg()}); """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_pred_read(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_pred_write(ctx, {self.reg_num}); """)) @@ -605,7 +602,6 @@ class PairDest(Register, Pair, Dest): gen_log_reg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated}); @@ -621,7 +617,6 @@ class PairSource(Register, Pair, OldSource): hex_gpr[{self.reg_num} + 1]); """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_reg_read_pair(ctx, {self.reg_num}); """)) @@ -640,8 +635,11 @@ class PairReadWrite(Register, Pair, ReadWrite): f.write(code_fmt(f"""\ gen_log_reg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_reg_read_pair(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated}); @@ -663,7 +661,6 @@ class ControlPairDest(Register, Pair, Dest): gen_write_ctrl_reg_pair(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated}); @@ -681,7 +678,6 @@ class ControlPairSource(Register, Pair, OldSource): gen_read_ctrl_reg_pair(ctx, {self.reg_num}, {self.reg_tcg()}); """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_reg_read_pair(ctx, {self.reg_num}); """)) @@ -705,7 +701,6 @@ class VRegDest(Register, Hvx, Dest): /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ @@ -728,7 +723,6 @@ class VRegSource(Register, Hvx, OldSource): /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_vreg_read(ctx, {self.reg_num}); """)) @@ -746,9 +740,8 @@ class VRegNewSource(Register, Hvx, NewSource): /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ - ctx_log_vreg_read(ctx, {self.reg_num}); + ctx_log_vreg_read_new(ctx, {self.reg_num}); """)) class VRegReadWrite(Register, Hvx, ReadWrite): @@ -772,8 +765,11 @@ class VRegReadWrite(Register, Hvx, ReadWrite): f.write(code_fmt(f"""\ /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ @@ -803,8 +799,11 @@ class VRegTmp(Register, Hvx, ReadWrite): f.write(code_fmt(f"""\ /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ @@ -830,7 +829,6 @@ class VRegPairDest(Register, Hvx, Dest): /* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */ """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ @@ -860,7 +858,6 @@ class VRegPairSource(Register, Hvx, OldSource): /* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */ """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_vreg_read_pair(ctx, {self.reg_num}); """)) @@ -892,8 +889,11 @@ class VRegPairReadWrite(Register, Hvx, ReadWrite): f.write(code_fmt(f"""\ /* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */ """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_vreg_read_pair(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ @@ -919,7 +919,6 @@ class QRegDest(Register, Hvx, Dest): /* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */ """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_qreg_write(ctx, {self.reg_num}); """)) @@ -941,7 +940,6 @@ class QRegSource(Register, Hvx, OldSource): /* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */ """)) def analyze_read(self, f, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_qreg_read(ctx, {self.reg_num}); """)) @@ -967,8 +965,11 @@ class QRegReadWrite(Register, Hvx, ReadWrite): f.write(code_fmt(f"""\ /* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */ """)) + def analyze_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_qreg_read(ctx, {self.reg_num}); + """)) def analyze_write(self, f, tag, regno): - self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ ctx_log_qreg_write(ctx, {self.reg_num}); """)) diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 4dd59c6726..f06d71fc53 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -75,6 +75,8 @@ typedef struct DisasContext { TCGv dczero_addr; } DisasContext; +bool is_gather_store_insn(DisasContext *ctx); + static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) { if (!test_bit(pnum, ctx->pregs_written)) { @@ -89,6 +91,12 @@ static inline void ctx_log_pred_read(DisasContext *ctx, int pnum) set_bit(pnum, ctx->pregs_read); } +static inline void ctx_log_pred_read_new(DisasContext *ctx, int pnum) +{ + g_assert(test_bit(pnum, ctx->pregs_written)); + set_bit(pnum, ctx->pregs_read); +} + static inline void ctx_log_reg_write(DisasContext *ctx, int rnum, bool is_predicated) { @@ -120,6 +128,12 @@ static inline void ctx_log_reg_read(DisasContext *ctx, int rnum) set_bit(rnum, ctx->regs_read); } +static inline void ctx_log_reg_read_new(DisasContext *ctx, int rnum) +{ + g_assert(test_bit(rnum, ctx->regs_written)); + set_bit(rnum, ctx->regs_read); +} + static inline void ctx_log_reg_read_pair(DisasContext *ctx, int rnum) { ctx_log_reg_read(ctx, rnum); @@ -171,6 +185,15 @@ static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum) set_bit(rnum, ctx->vregs_read); } +static inline void ctx_log_vreg_read_new(DisasContext *ctx, int rnum) +{ + g_assert(is_gather_store_insn(ctx) || + test_bit(rnum, ctx->vregs_updated) || + test_bit(rnum, ctx->vregs_select) || + test_bit(rnum, ctx->vregs_updated_tmp)); + set_bit(rnum, ctx->vregs_read); +} + static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum) { ctx_log_vreg_read(ctx, rnum ^ 0); @@ -205,7 +228,6 @@ extern TCGv hex_vstore_addr[VSTORES_MAX]; extern TCGv hex_vstore_size[VSTORES_MAX]; extern TCGv hex_vstore_pending[VSTORES_MAX]; -bool is_gather_store_insn(DisasContext *ctx); void process_store(DisasContext *ctx, int slot_num); FIELD(PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, 0, 2) From bd983f68acad895d75678828d23e90ffeac58eea Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 1 Feb 2024 03:33:39 -0700 Subject: [PATCH 0444/2884] Hexagon (target/hexagon) Enable more short-circuit packets (scalar core) Look for read-after-write instead of overlap of reads and writes Here is an example with overalp but no read-after-write: 0x000200fc: 0x38103876 { R0 = add(R0,R1); R6 = add(R6,R7) } BEFORE: ---- 00000000000200fc mov_i32 loc2,$0x0 mov_i32 loc2,r0 add_i32 loc3,loc2,r1 mov_i32 loc2,loc3 mov_i32 loc4,$0x0 mov_i32 loc4,r6 add_i32 loc5,loc4,r7 mov_i32 loc4,loc5 mov_i32 r0,loc2 mov_i32 r6,loc4 AFTER: ---- 00000000000200fc add_i32 loc2,r0,r1 mov_i32 r0,loc2 add_i32 loc3,r6,r7 mov_i32 r6,loc3 We can also short-circuit packets with .new values by reading from the real destination instead of the temporary. 0x00020100: 0x78005ff3 { R19 = #0xff 0x00020104: 0x2002e204 if (cmp.eq(N19.new,R2)) jump:t PC+8 } BEFORE: ---- 0000000000020100 mov_i32 pc,$0x20108 mov_i32 loc8,$0x0 mov_i32 loc8,$0xff setcond_i32 loc10,loc8,r2,eq mov_i32 loc6,loc10 mov_i32 r19,loc8 add_i32 pkt_cnt,pkt_cnt,$0x2 add_i32 insn_cnt,insn_cnt,$0x4 brcond_i32 loc6,$0x0,eq,$L1 goto_tb $0x0 mov_i32 pc,$0x20108 exit_tb $0x7fbb54000040 set_label $L1 goto_tb $0x1 exit_tb $0x7fbb54000041 set_label $L0 exit_tb $0x7fbb54000043 AFTER: ---- 0000000000020100 mov_i32 pc,$0x20108 mov_i32 r19,$0xff setcond_i32 loc7,r19,r2,eq mov_i32 loc4,loc7 add_i32 pkt_cnt,pkt_cnt,$0x2 add_i32 insn_cnt,insn_cnt,$0x4 brcond_i32 loc4,$0x0,eq,$L1 goto_tb $0x0 mov_i32 pc,$0x20108 exit_tb $0x7f9764000040 set_label $L1 goto_tb $0x1 exit_tb $0x7f9764000041 set_label $L0 exit_tb $0x7f9764000043 Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240201103340.119081-3-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/translate.c | 21 ++++----------------- target/hexagon/translate.h | 13 +++++++------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 47a870f42d..8d42ebd91c 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -396,20 +396,8 @@ static bool need_commit(DisasContext *ctx) } } - /* Check for overlap between register reads and writes */ - for (int i = 0; i < ctx->reg_log_idx; i++) { - int rnum = ctx->reg_log[i]; - if (test_bit(rnum, ctx->regs_read)) { - return true; - } - } - - /* Check for overlap between predicate reads and writes */ - for (int i = 0; i < ctx->preg_log_idx; i++) { - int pnum = ctx->preg_log[i]; - if (test_bit(pnum, ctx->pregs_read)) { - return true; - } + if (ctx->read_after_write) { + return true; } /* Check for overlap between HVX reads and writes */ @@ -468,6 +456,7 @@ static void analyze_packet(DisasContext *ctx) { Packet *pkt = ctx->pkt; ctx->has_hvx_helper = false; + ctx->read_after_write = false; for (int i = 0; i < pkt->num_insns; i++) { Insn *insn = &pkt->insn[i]; ctx->insn = insn; @@ -492,11 +481,9 @@ static void gen_start_packet(DisasContext *ctx) ctx->next_PC = next_PC; ctx->reg_log_idx = 0; bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS); - bitmap_zero(ctx->regs_read, TOTAL_PER_THREAD_REGS); bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); - bitmap_zero(ctx->pregs_read, NUM_PREGS); ctx->future_vregs_idx = 0; ctx->tmp_vregs_idx = 0; ctx->vreg_log_idx = 0; diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index f06d71fc53..d5e7f49ad8 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -38,12 +38,10 @@ typedef struct DisasContext { int reg_log[REG_WRITES_MAX]; int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); - DECLARE_BITMAP(regs_read, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); int preg_log[PRED_WRITES_MAX]; int preg_log_idx; DECLARE_BITMAP(pregs_written, NUM_PREGS); - DECLARE_BITMAP(pregs_read, NUM_PREGS); uint8_t store_width[STORES_MAX]; bool s1_store_processed; int future_vregs_idx; @@ -68,6 +66,7 @@ typedef struct DisasContext { bool is_tight_loop; bool short_circuit; bool has_hvx_helper; + bool read_after_write; TCGv new_value[TOTAL_PER_THREAD_REGS]; TCGv new_pred_value[NUM_PREGS]; TCGv pred_written; @@ -88,13 +87,14 @@ static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) static inline void ctx_log_pred_read(DisasContext *ctx, int pnum) { - set_bit(pnum, ctx->pregs_read); + if (test_bit(pnum, ctx->pregs_written)) { + ctx->read_after_write = true; + } } static inline void ctx_log_pred_read_new(DisasContext *ctx, int pnum) { g_assert(test_bit(pnum, ctx->pregs_written)); - set_bit(pnum, ctx->pregs_read); } static inline void ctx_log_reg_write(DisasContext *ctx, int rnum, @@ -125,13 +125,14 @@ static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum, static inline void ctx_log_reg_read(DisasContext *ctx, int rnum) { - set_bit(rnum, ctx->regs_read); + if (test_bit(rnum, ctx->regs_written)) { + ctx->read_after_write = true; + } } static inline void ctx_log_reg_read_new(DisasContext *ctx, int rnum) { g_assert(test_bit(rnum, ctx->regs_written)); - set_bit(rnum, ctx->regs_read); } static inline void ctx_log_reg_read_pair(DisasContext *ctx, int rnum) From 763d2ce7c4f5bc6617b5c17a40f2faaf03fecb8e Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Thu, 1 Feb 2024 03:33:40 -0700 Subject: [PATCH 0445/2884] Hexagon (target/hexagon) Enable more short-circuit packets (HVX) Look for read-after-write instead of overlap of reads and writes HVX instructions with helpers have pass-by-reference semantics, so we check for overlaps of reads and writes within the same instruction. Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240201103340.119081-4-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/gen_analyze_funcs.py | 19 ++++--- target/hexagon/hex_common.py | 45 ++++++++++----- target/hexagon/translate.c | 58 ++----------------- target/hexagon/translate.h | 88 +++++++++++++++++++++++------ 4 files changed, 115 insertions(+), 95 deletions(-) diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index 890e6a3a95..81e1d9cfa3 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py @@ -43,6 +43,16 @@ def gen_analyze_func(f, tag, regs, imms): f.write("{\n") f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") + if (hex_common.is_hvx_insn(tag)): + if hex_common.has_hvx_helper(tag): + f.write( + " const bool G_GNUC_UNUSED insn_has_hvx_helper = true;\n" + ) + f.write(" ctx_start_hvx_insn(ctx);\n") + else: + f.write( + " const bool G_GNUC_UNUSED insn_has_hvx_helper = false;\n" + ) ## Declare all the registers for regno, register in enumerate(regs): @@ -64,15 +74,6 @@ def gen_analyze_func(f, tag, regs, imms): if reg.is_written(): reg.analyze_write(f, tag, regno) - has_generated_helper = not hex_common.skip_qemu_helper( - tag - ) and not hex_common.is_idef_parser_enabled(tag) - - ## Mark HVX instructions with generated helpers - if (has_generated_helper and - "A_CVI" in hex_common.attribdict[tag]): - f.write(" ctx->has_hvx_helper = true;\n") - f.write("}\n\n") diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 33801e4bd7..9e7f613e3c 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -241,6 +241,16 @@ def is_idef_parser_enabled(tag): return tag in idef_parser_enabled +def is_hvx_insn(tag): + return "A_CVI" in attribdict[tag] + + +def has_hvx_helper(tag): + return (is_hvx_insn(tag) and + not skip_qemu_helper(tag) and + not is_idef_parser_enabled(tag)) + + def imm_name(immlett): return f"{immlett}iV" @@ -704,7 +714,8 @@ class VRegDest(Register, Hvx, Dest): newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ - ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}); + ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}, + insn_has_hvx_helper); """)) class VRegSource(Register, Hvx, OldSource): @@ -724,7 +735,7 @@ class VRegSource(Register, Hvx, OldSource): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_vreg_read(ctx, {self.reg_num}); + ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); """)) class VRegNewSource(Register, Hvx, NewSource): @@ -741,7 +752,7 @@ class VRegNewSource(Register, Hvx, NewSource): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_vreg_read_new(ctx, {self.reg_num}); + ctx_log_vreg_read_new(ctx, {self.reg_num}, insn_has_hvx_helper); """)) class VRegReadWrite(Register, Hvx, ReadWrite): @@ -767,13 +778,14 @@ class VRegReadWrite(Register, Hvx, ReadWrite): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_vreg_read(ctx, {self.reg_num}); + ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); """)) def analyze_write(self, f, tag, regno): newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ - ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}); + ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}, + insn_has_hvx_helper); """)) class VRegTmp(Register, Hvx, ReadWrite): @@ -801,13 +813,14 @@ class VRegTmp(Register, Hvx, ReadWrite): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_vreg_read(ctx, {self.reg_num}); + ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); """)) def analyze_write(self, f, tag, regno): newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ - ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}); + ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}, + insn_has_hvx_helper); """)) class VRegPairDest(Register, Hvx, Dest): @@ -832,7 +845,8 @@ class VRegPairDest(Register, Hvx, Dest): newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ - ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated}); + ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated}, + insn_has_hvx_helper); """)) class VRegPairSource(Register, Hvx, OldSource): @@ -859,7 +873,7 @@ class VRegPairSource(Register, Hvx, OldSource): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_vreg_read_pair(ctx, {self.reg_num}); + ctx_log_vreg_read_pair(ctx, {self.reg_num}, insn_has_hvx_helper); """)) class VRegPairReadWrite(Register, Hvx, ReadWrite): @@ -891,13 +905,14 @@ class VRegPairReadWrite(Register, Hvx, ReadWrite): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_vreg_read_pair(ctx, {self.reg_num}); + ctx_log_vreg_read_pair(ctx, {self.reg_num}, insn_has_hvx_helper); """)) def analyze_write(self, f, tag, regno): newv = hvx_newv(tag) predicated = "true" if is_predicated(tag) else "false" f.write(code_fmt(f"""\ - ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated}); + ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated}, + insn_has_hvx_helper); """)) class QRegDest(Register, Hvx, Dest): @@ -920,7 +935,7 @@ class QRegDest(Register, Hvx, Dest): """)) def analyze_write(self, f, tag, regno): f.write(code_fmt(f"""\ - ctx_log_qreg_write(ctx, {self.reg_num}); + ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); """)) class QRegSource(Register, Hvx, OldSource): @@ -941,7 +956,7 @@ class QRegSource(Register, Hvx, OldSource): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_qreg_read(ctx, {self.reg_num}); + ctx_log_qreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); """)) class QRegReadWrite(Register, Hvx, ReadWrite): @@ -967,11 +982,11 @@ class QRegReadWrite(Register, Hvx, ReadWrite): """)) def analyze_read(self, f, regno): f.write(code_fmt(f"""\ - ctx_log_qreg_read(ctx, {self.reg_num}); + ctx_log_qreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); """)) def analyze_write(self, f, tag, regno): f.write(code_fmt(f"""\ - ctx_log_qreg_write(ctx, {self.reg_num}); + ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); """)) def init_registers(): diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 8d42ebd91c..0904dc2d38 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -380,60 +380,10 @@ static bool need_commit(DisasContext *ctx) return true; } - if (pkt->num_insns == 1) { - if (pkt->pkt_has_hvx) { - /* - * The HVX instructions with generated helpers use - * pass-by-reference, so they need the read/write overlap - * check below. - * The HVX instructions with overrides are OK. - */ - if (!ctx->has_hvx_helper) { - return false; - } - } else { - return false; - } - } - - if (ctx->read_after_write) { + if (ctx->read_after_write || ctx->has_hvx_overlap) { return true; } - /* Check for overlap between HVX reads and writes */ - for (int i = 0; i < ctx->vreg_log_idx; i++) { - int vnum = ctx->vreg_log[i]; - if (test_bit(vnum, ctx->vregs_read)) { - return true; - } - } - if (!bitmap_empty(ctx->vregs_updated_tmp, NUM_VREGS)) { - int i = find_first_bit(ctx->vregs_updated_tmp, NUM_VREGS); - while (i < NUM_VREGS) { - if (test_bit(i, ctx->vregs_read)) { - return true; - } - i = find_next_bit(ctx->vregs_updated_tmp, NUM_VREGS, i + 1); - } - } - if (!bitmap_empty(ctx->vregs_select, NUM_VREGS)) { - int i = find_first_bit(ctx->vregs_select, NUM_VREGS); - while (i < NUM_VREGS) { - if (test_bit(i, ctx->vregs_read)) { - return true; - } - i = find_next_bit(ctx->vregs_select, NUM_VREGS, i + 1); - } - } - - /* Check for overlap between HVX predicate reads and writes */ - for (int i = 0; i < ctx->qreg_log_idx; i++) { - int qnum = ctx->qreg_log[i]; - if (test_bit(qnum, ctx->qregs_read)) { - return true; - } - } - return false; } @@ -455,8 +405,8 @@ static void mark_implicit_pred_reads(DisasContext *ctx) static void analyze_packet(DisasContext *ctx) { Packet *pkt = ctx->pkt; - ctx->has_hvx_helper = false; ctx->read_after_write = false; + ctx->has_hvx_overlap = false; for (int i = 0; i < pkt->num_insns; i++) { Insn *insn = &pkt->insn[i]; ctx->insn = insn; @@ -487,13 +437,13 @@ static void gen_start_packet(DisasContext *ctx) ctx->future_vregs_idx = 0; ctx->tmp_vregs_idx = 0; ctx->vreg_log_idx = 0; + bitmap_zero(ctx->vregs_written, NUM_VREGS); bitmap_zero(ctx->vregs_updated_tmp, NUM_VREGS); bitmap_zero(ctx->vregs_updated, NUM_VREGS); bitmap_zero(ctx->vregs_select, NUM_VREGS); bitmap_zero(ctx->predicated_future_vregs, NUM_VREGS); bitmap_zero(ctx->predicated_tmp_vregs, NUM_VREGS); - bitmap_zero(ctx->vregs_read, NUM_VREGS); - bitmap_zero(ctx->qregs_read, NUM_QREGS); + bitmap_zero(ctx->qregs_written, NUM_QREGS); ctx->qreg_log_idx = 0; for (i = 0; i < STORES_MAX; i++) { ctx->store_width[i] = 0; diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index d5e7f49ad8..00cc2bcd63 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -50,23 +50,27 @@ typedef struct DisasContext { int tmp_vregs_num[VECTOR_TEMPS_MAX]; int vreg_log[NUM_VREGS]; int vreg_log_idx; + DECLARE_BITMAP(vregs_written, NUM_VREGS); + DECLARE_BITMAP(insn_vregs_written, NUM_VREGS); DECLARE_BITMAP(vregs_updated_tmp, NUM_VREGS); DECLARE_BITMAP(vregs_updated, NUM_VREGS); DECLARE_BITMAP(vregs_select, NUM_VREGS); DECLARE_BITMAP(predicated_future_vregs, NUM_VREGS); DECLARE_BITMAP(predicated_tmp_vregs, NUM_VREGS); - DECLARE_BITMAP(vregs_read, NUM_VREGS); + DECLARE_BITMAP(insn_vregs_read, NUM_VREGS); int qreg_log[NUM_QREGS]; int qreg_log_idx; - DECLARE_BITMAP(qregs_read, NUM_QREGS); + DECLARE_BITMAP(qregs_written, NUM_QREGS); + DECLARE_BITMAP(insn_qregs_written, NUM_QREGS); + DECLARE_BITMAP(insn_qregs_read, NUM_QREGS); bool pre_commit; bool need_commit; TCGCond branch_cond; target_ulong branch_dest; bool is_tight_loop; bool short_circuit; - bool has_hvx_helper; bool read_after_write; + bool has_hvx_overlap; TCGv new_value[TOTAL_PER_THREAD_REGS]; TCGv new_pred_value[NUM_PREGS]; TCGv pred_written; @@ -146,10 +150,25 @@ intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum, intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, int num, bool alloc_ok); +static inline void ctx_start_hvx_insn(DisasContext *ctx) +{ + bitmap_zero(ctx->insn_vregs_written, NUM_VREGS); + bitmap_zero(ctx->insn_vregs_read, NUM_VREGS); + bitmap_zero(ctx->insn_qregs_written, NUM_QREGS); + bitmap_zero(ctx->insn_qregs_read, NUM_QREGS); +} + static inline void ctx_log_vreg_write(DisasContext *ctx, int rnum, VRegWriteType type, - bool is_predicated) + bool is_predicated, bool has_helper) { + if (has_helper) { + set_bit(rnum, ctx->insn_vregs_written); + if (test_bit(rnum, ctx->insn_vregs_read)) { + ctx->has_hvx_overlap = true; + } + } + set_bit(rnum, ctx->vregs_written); if (type != EXT_TMP) { if (!test_bit(rnum, ctx->vregs_updated)) { ctx->vreg_log[ctx->vreg_log_idx] = rnum; @@ -175,42 +194,77 @@ static inline void ctx_log_vreg_write(DisasContext *ctx, static inline void ctx_log_vreg_write_pair(DisasContext *ctx, int rnum, VRegWriteType type, - bool is_predicated) + bool is_predicated, bool has_helper) { - ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated); - ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated); + ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated, has_helper); + ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated, has_helper); } -static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum) +static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum, + bool has_helper) { - set_bit(rnum, ctx->vregs_read); + if (has_helper) { + set_bit(rnum, ctx->insn_vregs_read); + if (test_bit(rnum, ctx->insn_vregs_written)) { + ctx->has_hvx_overlap = true; + } + } + if (test_bit(rnum, ctx->vregs_written)) { + ctx->read_after_write = true; + } } -static inline void ctx_log_vreg_read_new(DisasContext *ctx, int rnum) +static inline void ctx_log_vreg_read_new(DisasContext *ctx, int rnum, + bool has_helper) { g_assert(is_gather_store_insn(ctx) || test_bit(rnum, ctx->vregs_updated) || test_bit(rnum, ctx->vregs_select) || test_bit(rnum, ctx->vregs_updated_tmp)); - set_bit(rnum, ctx->vregs_read); + if (has_helper) { + set_bit(rnum, ctx->insn_vregs_read); + if (test_bit(rnum, ctx->insn_vregs_written)) { + ctx->has_hvx_overlap = true; + } + } + if (is_gather_store_insn(ctx)) { + ctx->read_after_write = true; + } } -static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum) +static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum, + bool has_helper) { - ctx_log_vreg_read(ctx, rnum ^ 0); - ctx_log_vreg_read(ctx, rnum ^ 1); + ctx_log_vreg_read(ctx, rnum ^ 0, has_helper); + ctx_log_vreg_read(ctx, rnum ^ 1, has_helper); } static inline void ctx_log_qreg_write(DisasContext *ctx, - int rnum) + int rnum, bool has_helper) { + if (has_helper) { + set_bit(rnum, ctx->insn_qregs_written); + if (test_bit(rnum, ctx->insn_qregs_read)) { + ctx->has_hvx_overlap = true; + } + } + set_bit(rnum, ctx->qregs_written); ctx->qreg_log[ctx->qreg_log_idx] = rnum; ctx->qreg_log_idx++; } -static inline void ctx_log_qreg_read(DisasContext *ctx, int qnum) +static inline void ctx_log_qreg_read(DisasContext *ctx, + int qnum, bool has_helper) { - set_bit(qnum, ctx->qregs_read); + if (has_helper) { + set_bit(qnum, ctx->insn_qregs_read); + if (test_bit(qnum, ctx->insn_qregs_written)) { + ctx->has_hvx_overlap = true; + } + } + if (test_bit(qnum, ctx->qregs_written)) { + ctx->read_after_write = true; + } } extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; From 850d06225b599eda8c519681cb39f5dec0dbe9a0 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 13 Feb 2024 21:27:24 -0700 Subject: [PATCH 0446/2884] Hexagon (target/hexagon) Pass P0 explicitly to helpers that need it Rather than reading P0 from the env, pass it explicitly Signed-off-by: Taylor Simpson Reviewed-by: Anton Johansson Tested-by: Anton Johansson Reviewed-by: Brian Cain Message-Id: <20240214042726.19290-2-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/hex_common.py | 10 ++++++++++ target/hexagon/macros.h | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 9e7f613e3c..63d18f73ad 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -197,6 +197,10 @@ def get_tagimms(): return dict(zip(tags, list(map(compute_tag_immediates, tags)))) +def need_p0(tag): + return "A_IMPLICIT_READS_P0" in attribdict[tag] + + def need_slot(tag): if ( "A_CVI_SCATTER" not in attribdict[tag] @@ -1134,6 +1138,12 @@ def helper_args(tag, regs, imms): "tcg_constant_tl(ctx->next_PC)", "target_ulong next_PC" )) + if need_p0(tag): + args.append(HelperArg( + "i32", + "hex_pred[0]", + "uint32_t P0" + )) if need_slot(tag): args.append(HelperArg( "i32", diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 1376d6ccc1..aedc863fab 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -358,7 +358,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #endif #define fREAD_PC() (PC) -#define fREAD_P0() (env->pred[0]) +#define fREAD_P0() (P0) #define fCHECK_PCALIGN(A) From f7be65fbbfe3a88619a80181d79993aa8d4dc645 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 13 Feb 2024 21:27:25 -0700 Subject: [PATCH 0447/2884] Hexagon (target/hexagon) Pass SP explicitly to helpers that need it Rather than reading SP from the env, pass it explicitly Signed-off-by: Taylor Simpson Reviewed-by: Anton Johansson Tested-by: Anton Johansson Reviewed-by: Brian Cain Message-Id: <20240214042726.19290-3-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/attribs_def.h.inc | 3 ++- target/hexagon/hex_common.py | 11 +++++++++++ target/hexagon/macros.h | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 87942d46f4..9e3a05f882 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -117,6 +117,7 @@ DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1 register", "", "") DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "") DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "") DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "") +DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "") DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "") DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "") DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "") diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 63d18f73ad..03c9ce1d8a 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -101,6 +101,7 @@ def calculate_attribs(): add_qemu_macro_attrib('fLSBNEW1', 'A_IMPLICIT_READS_P1') add_qemu_macro_attrib('fLSBNEW1NOT', 'A_IMPLICIT_READS_P1') add_qemu_macro_attrib('fREAD_P3', 'A_IMPLICIT_READS_P3') + add_qemu_macro_attrib('fREAD_SP', 'A_IMPLICIT_READS_SP') # Recurse down macros, find attributes from sub-macros macroValues = list(macros.values()) @@ -201,6 +202,10 @@ def need_p0(tag): return "A_IMPLICIT_READS_P0" in attribdict[tag] +def need_sp(tag): + return "A_IMPLICIT_READS_SP" in attribdict[tag] + + def need_slot(tag): if ( "A_CVI_SCATTER" not in attribdict[tag] @@ -1144,6 +1149,12 @@ def helper_args(tag, regs, imms): "hex_pred[0]", "uint32_t P0" )) + if need_sp(tag): + args.append(HelperArg( + "i32", + "hex_gpr[HEX_REG_SP]", + "uint32_t SP" + )) if need_slot(tag): args.append(HelperArg( "i32", diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index aedc863fab..feb798c6c0 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -343,7 +343,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fREAD_LR() (env->gpr[HEX_REG_LR]) -#define fREAD_SP() (env->gpr[HEX_REG_SP]) +#define fREAD_SP() (SP) #define fREAD_LC0 (env->gpr[HEX_REG_LC0]) #define fREAD_LC1 (env->gpr[HEX_REG_LC1]) #define fREAD_SA0 (env->gpr[HEX_REG_SA0]) From 2f0a771ddcb55cea39046502e8e834eb44b5fff7 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Tue, 13 Feb 2024 21:27:26 -0700 Subject: [PATCH 0448/2884] Hexagon (target/hexagon) Only pass env to generated helper when needed Currently, we pass env to every generated helper. When the semantics of the instruction only depend on the arguments, this is unnecessary and adds extra overhead to the helper call. We add the TCG_CALL_NO_RWG_SE flag to any non-HVX helpers that don't get the ptr to env. The A2_nop and SA1_setin1 instructions end up with no arguments. This results in a "old-style function definition" error from the compiler, so we write overrides for them. With this change, the number of helpers with env argument is idef-parser enabled: 329 total, 23 with env idef-parser disabled: 1543 total, 550 with env Signed-off-by: Taylor Simpson Reviewed-by: Anton Johansson Tested-by: Anton Johansson Message-Id: <20240214042726.19290-4-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/gen_helper_protos.py | 12 ++++++++++-- target/hexagon/gen_tcg.h | 5 ++++- target/hexagon/hex_common.py | 23 ++++++++++++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index c82b0f54e4..f8578d5033 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -40,7 +40,15 @@ def gen_helper_prototype(f, tag, tagregs, tagimms): declared.append(arg.proto_arg) arguments = ", ".join(declared) - f.write(f"DEF_HELPER_{len(declared) - 1}({tag}, {arguments})\n") + + ## Add the TCG_CALL_NO_RWG_SE flag to helpers that don't take the env + ## argument and aren't HVX instructions. Since HVX instructions take + ## pointers to their arguments, they will have side effects. + if hex_common.need_env(tag) or hex_common.is_hvx_insn(tag): + f.write(f"DEF_HELPER_{len(declared) - 1}({tag}, {arguments})\n") + else: + f.write(f"DEF_HELPER_FLAGS_{len(declared) - 1}({tag}, " + f"TCG_CALL_NO_RWG_SE, {arguments})\n") def main(): diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index 1c4391b415..3fc1f4e281 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1369,3 +1369,6 @@ gen_helper_raise_exception(tcg_env, excp); \ } while (0) #endif + +#define fGEN_TCG_A2_nop(SHORTCODE) do { } while (0) +#define fGEN_TCG_SA1_setin1(SHORTCODE) tcg_gen_movi_tl(RdV, -1) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 03c9ce1d8a..c09b48bb36 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -206,6 +206,18 @@ def need_sp(tag): return "A_IMPLICIT_READS_SP" in attribdict[tag] +def is_hvx_insn(tag): + return "A_CVI" in attribdict[tag] + + +def need_env(tag): + return ("A_STORE" in attribdict[tag] or + "A_LOAD" in attribdict[tag] or + "A_CVI_GATHER" in attribdict[tag] or + "A_CVI_SCATTER" in attribdict[tag] or + "A_IMPLICIT_WRITES_USR" in attribdict[tag]) + + def need_slot(tag): if ( "A_CVI_SCATTER" not in attribdict[tag] @@ -1085,11 +1097,12 @@ def helper_args(tag, regs, imms): args = [] ## First argument is the CPU state - args.append(HelperArg( - "env", - "tcg_env", - "CPUHexagonState *env" - )) + if need_env(tag): + args.append(HelperArg( + "env", + "tcg_env", + "CPUHexagonState *env" + )) ## For predicated instructions, we pass in the destination register if is_predicated(tag): From 2720bd1dbd390da8bfbbc84c9293433c82dda88b Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:19 -0700 Subject: [PATCH 0449/2884] Hexagon (target/hexagon) Add is_old/is_new to Register class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Taylor Simpson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-2-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/hex_common.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index c09b48bb36..f6f187968a 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -428,10 +428,18 @@ class Source: class OldSource(Source): def reg_tcg(self): return f"{self.regtype}{self.regid}V" + def is_old(self): + return True + def is_new(self): + return False class NewSource(Source): def reg_tcg(self): return f"{self.regtype}{self.regid}N" + def is_old(self): + return False + def is_new(self): + return True class ReadWrite: def reg_tcg(self): @@ -444,6 +452,10 @@ class ReadWrite: return True def is_readwrite(self): return True + def is_old(self): + return True + def is_new(self): + return False class GprDest(Register, Single, Dest): def decl_tcg(self, f, tag, regno): From 9196381993a3fd365fd47fe056754ff550a3b67d Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:20 -0700 Subject: [PATCH 0450/2884] Hexagon (target/hexagon) Mark new_read_idx in trans functions Check that the value matches opcode_reginfo Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-3-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/decode.c | 2 ++ target/hexagon/gen_trans_funcs.py | 15 ++++++++++----- target/hexagon/insn.h | 3 ++- target/hexagon/mmvec/decode_ext_mmvec.c | 2 ++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index a40210ca1e..4595e30384 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -131,6 +131,8 @@ decode_fill_newvalue_regno(Packet *packet) use_regidx = strchr(opcode_reginfo[use_opcode], 's') - opcode_reginfo[use_opcode]; } + g_assert(packet->insn[i].new_read_idx != -1 && + packet->insn[i].new_read_idx == use_regidx); /* * What's encoded at the N-field is the offset to who's producing diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py index 53e844a44b..8acecdb993 100755 --- a/target/hexagon/gen_trans_funcs.py +++ b/target/hexagon/gen_trans_funcs.py @@ -68,6 +68,7 @@ def mark_which_imm_extended(f, tag): ## insn->regno[0] = args->Rd; ## insn->regno[1] = args->Rs; ## insn->regno[2] = args->Rt; +## insn->new_read_idx = -1; ## return true; ## } ## @@ -84,14 +85,14 @@ def gen_trans_funcs(f): insn->opcode = {tag}; """)) - regno = 0 - for reg in regs: - reg_type = reg[0] - reg_id = reg[1] + new_read_idx = -1 + for regno, (reg_type, reg_id, *_) in enumerate(regs): + reg = hex_common.get_register(tag, reg_type, reg_id) f.write(code_fmt(f"""\ insn->regno[{regno}] = args->{reg_type}{reg_id}; """)) - regno += 1 + if reg.is_read() and reg.is_new(): + new_read_idx = regno if len(imms) != 0: mark_which_imm_extended(f, tag) @@ -112,6 +113,9 @@ def gen_trans_funcs(f): insn->immed[{immno}] = args->{imm_type}{imm_letter}; """)) + f.write(code_fmt(f"""\ + insn->new_read_idx = {new_read_idx}; + """)) f.write(textwrap.dedent(f"""\ return true; {close_curly} @@ -120,5 +124,6 @@ def gen_trans_funcs(f): if __name__ == "__main__": hex_common.read_semantics_file(sys.argv[1]) + hex_common.init_registers() with open(sys.argv[2], "w") as f: gen_trans_funcs(f) diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index 3e7a22c91e..36502bf056 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +39,7 @@ struct Instruction { uint32_t slot:3; uint32_t which_extended:1; /* If has an extender, which immediate */ uint32_t new_value_producer_slot:4; + int32_t new_read_idx; bool part1; /* * cmp-jumps are split into two insns. diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c index 202d84c7c0..e9007f5d71 100644 --- a/target/hexagon/mmvec/decode_ext_mmvec.c +++ b/target/hexagon/mmvec/decode_ext_mmvec.c @@ -41,6 +41,8 @@ check_new_value(Packet *pkt) GET_ATTRIB(use_opcode, A_STORE)) { int use_regidx = strchr(opcode_reginfo[use_opcode], 's') - opcode_reginfo[use_opcode]; + g_assert(pkt->insn[i].new_read_idx != -1 && + pkt->insn[i].new_read_idx == use_regidx); /* * What's encoded at the N-field is the offset to who's producing * the value. From 325a64af065d855a022696f5e4180f2813b439c8 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:21 -0700 Subject: [PATCH 0451/2884] Hexagon (target/hexagon) Mark dest_idx in trans functions Check that the value matches opcode_reginfo/opcode_wregs Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-4-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/decode.c | 2 ++ target/hexagon/gen_trans_funcs.py | 6 ++++++ target/hexagon/insn.h | 1 + target/hexagon/mmvec/decode_ext_mmvec.c | 2 ++ 4 files changed, 11 insertions(+) diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 4595e30384..a4d8500fea 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -184,6 +184,8 @@ decode_fill_newvalue_regno(Packet *packet) /* Now patch up the consumer with the register number */ dst_idx = dststr - opcode_reginfo[def_opcode]; + g_assert(packet->insn[def_idx].dest_idx != -1 && + packet->insn[def_idx].dest_idx == dst_idx); packet->insn[i].regno[use_regidx] = packet->insn[def_idx].regno[dst_idx]; /* diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py index 8acecdb993..1201172dda 100755 --- a/target/hexagon/gen_trans_funcs.py +++ b/target/hexagon/gen_trans_funcs.py @@ -69,6 +69,7 @@ def mark_which_imm_extended(f, tag): ## insn->regno[1] = args->Rs; ## insn->regno[2] = args->Rt; ## insn->new_read_idx = -1; +## insn->dest_idx = 0; ## return true; ## } ## @@ -86,6 +87,7 @@ def gen_trans_funcs(f): """)) new_read_idx = -1 + dest_idx = -1 for regno, (reg_type, reg_id, *_) in enumerate(regs): reg = hex_common.get_register(tag, reg_type, reg_id) f.write(code_fmt(f"""\ @@ -93,6 +95,9 @@ def gen_trans_funcs(f): """)) if reg.is_read() and reg.is_new(): new_read_idx = regno + # dest_idx should be the first destination, so check for -1 + if reg.is_written() and dest_idx == -1: + dest_idx = regno if len(imms) != 0: mark_which_imm_extended(f, tag) @@ -115,6 +120,7 @@ def gen_trans_funcs(f): f.write(code_fmt(f"""\ insn->new_read_idx = {new_read_idx}; + insn->dest_idx = {dest_idx}; """)) f.write(textwrap.dedent(f"""\ return true; diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index 36502bf056..a770379958 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -40,6 +40,7 @@ struct Instruction { uint32_t which_extended:1; /* If has an extender, which immediate */ uint32_t new_value_producer_slot:4; int32_t new_read_idx; + int32_t dest_idx; bool part1; /* * cmp-jumps are split into two insns. diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c index e9007f5d71..c1320406df 100644 --- a/target/hexagon/mmvec/decode_ext_mmvec.c +++ b/target/hexagon/mmvec/decode_ext_mmvec.c @@ -86,6 +86,8 @@ check_new_value(Packet *pkt) /* still not there, we have a bad packet */ g_assert_not_reached(); } + g_assert(pkt->insn[def_idx].dest_idx != -1 && + pkt->insn[def_idx].dest_idx == dststr - reginfo); int def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; /* Now patch up the consumer with the register number */ pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg; From 4614b8f36a319ca77c004b8c5002e68d9ae25061 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:22 -0700 Subject: [PATCH 0452/2884] Hexagon (target/hexagon) Mark has_pred_dest in trans functions Check that the value matches opcode_wregs Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-5-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/decode.c | 3 +++ target/hexagon/gen_trans_funcs.py | 5 +++++ target/hexagon/insn.h | 1 + 3 files changed, 9 insertions(+) diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index a4d8500fea..84a3899556 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -366,6 +366,9 @@ static void decode_shuffle_for_execution(Packet *packet) for (flag = false, i = 0; i < last_insn + 1; i++) { int opcode = packet->insn[i].opcode; + g_assert(packet->insn[i].has_pred_dest == + (strstr(opcode_wregs[opcode], "Pd4") || + strstr(opcode_wregs[opcode], "Pe4"))); if ((strstr(opcode_wregs[opcode], "Pd4") || strstr(opcode_wregs[opcode], "Pe4")) && GET_ATTRIB(opcode, A_STORE) == 0) { diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py index 1201172dda..9f86b4edbd 100755 --- a/target/hexagon/gen_trans_funcs.py +++ b/target/hexagon/gen_trans_funcs.py @@ -70,6 +70,7 @@ def mark_which_imm_extended(f, tag): ## insn->regno[2] = args->Rt; ## insn->new_read_idx = -1; ## insn->dest_idx = 0; +## insn->has_pred_dest = false; ## return true; ## } ## @@ -88,6 +89,7 @@ def gen_trans_funcs(f): new_read_idx = -1 dest_idx = -1 + has_pred_dest = "false" for regno, (reg_type, reg_id, *_) in enumerate(regs): reg = hex_common.get_register(tag, reg_type, reg_id) f.write(code_fmt(f"""\ @@ -98,6 +100,8 @@ def gen_trans_funcs(f): # dest_idx should be the first destination, so check for -1 if reg.is_written() and dest_idx == -1: dest_idx = regno + if reg_type == "P" and reg.is_written() and not reg.is_read(): + has_pred_dest = "true" if len(imms) != 0: mark_which_imm_extended(f, tag) @@ -121,6 +125,7 @@ def gen_trans_funcs(f): f.write(code_fmt(f"""\ insn->new_read_idx = {new_read_idx}; insn->dest_idx = {dest_idx}; + insn->has_pred_dest = {has_pred_dest}; """)) f.write(textwrap.dedent(f"""\ return true; diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index a770379958..24dcf7fe9f 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -41,6 +41,7 @@ struct Instruction { uint32_t new_value_producer_slot:4; int32_t new_read_idx; int32_t dest_idx; + bool has_pred_dest; bool part1; /* * cmp-jumps are split into two insns. From b887b6b71c0008413314e2b5e7d691f4812776f3 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:23 -0700 Subject: [PATCH 0453/2884] Hexagon (tests/tcg/hexagon) Test HVX .new read from high half of pair Make sure the decoding of HVX .new is correctly handling this case Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-6-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- tests/tcg/hexagon/hvx_misc.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index b45170acd1..1fe14b5158 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2021-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -231,6 +231,7 @@ static void test_masked_store(bool invert) static void test_new_value_store(void) { void *p0 = buffer0; + void *p1 = buffer1; void *pout = output; asm("{\n\t" @@ -242,6 +243,19 @@ static void test_new_value_store(void) expect[0] = buffer0[0]; check_output_w(__LINE__, 1); + + /* Test the .new read from the high half of a pair */ + asm("v7 = vmem(%0 + #0)\n\t" + "v12 = vmem(%1 + #0)\n\t" + "{\n\t" + " v5:4 = vcombine(v12, v7)\n\t" + " vmem(%2 + #0) = v5.new\n\t" + "}\n\t" + : : "r"(p0), "r"(p1), "r"(pout) : "v4", "v5", "v7", "v12", "memory"); + + expect[0] = buffer1[0]; + + check_output_w(__LINE__, 1); } static void test_max_temps() From 09a7e7db0f12ac6eb0502e8c27e10a5907ef99cb Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:24 -0700 Subject: [PATCH 0454/2884] Hexagon (target/hexagon) Remove uses of op_regs_generated.h.inc Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-7-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/decode.c | 55 +++---------------------- target/hexagon/mmvec/decode_ext_mmvec.c | 34 ++++----------- target/hexagon/opcodes.c | 28 ------------- target/hexagon/opcodes.h | 4 -- 4 files changed, 12 insertions(+), 109 deletions(-) diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 84a3899556..23deba2426 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -115,24 +115,13 @@ static void decode_fill_newvalue_regno(Packet *packet) { int i, use_regidx, offset, def_idx, dst_idx; - uint16_t def_opcode, use_opcode; - char *dststr; for (i = 1; i < packet->num_insns; i++) { if (GET_ATTRIB(packet->insn[i].opcode, A_DOTNEWVALUE) && !GET_ATTRIB(packet->insn[i].opcode, A_EXTENSION)) { - use_opcode = packet->insn[i].opcode; - /* It's a store, so we're adjusting the Nt field */ - if (GET_ATTRIB(use_opcode, A_STORE)) { - use_regidx = strchr(opcode_reginfo[use_opcode], 't') - - opcode_reginfo[use_opcode]; - } else { /* It's a Jump, so we're adjusting the Ns field */ - use_regidx = strchr(opcode_reginfo[use_opcode], 's') - - opcode_reginfo[use_opcode]; - } - g_assert(packet->insn[i].new_read_idx != -1 && - packet->insn[i].new_read_idx == use_regidx); + g_assert(packet->insn[i].new_read_idx != -1); + use_regidx = packet->insn[i].new_read_idx; /* * What's encoded at the N-field is the offset to who's producing @@ -153,39 +142,9 @@ decode_fill_newvalue_regno(Packet *packet) */ g_assert(!((def_idx < 0) || (def_idx > (packet->num_insns - 1)))); - /* - * packet->insn[def_idx] is the producer - * Figure out which type of destination it produces - * and the corresponding index in the reginfo - */ - def_opcode = packet->insn[def_idx].opcode; - dststr = strstr(opcode_wregs[def_opcode], "Rd"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'd'); - } else { - dststr = strstr(opcode_wregs[def_opcode], "Rx"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'x'); - } else { - dststr = strstr(opcode_wregs[def_opcode], "Re"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'e'); - } else { - dststr = strstr(opcode_wregs[def_opcode], "Ry"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'y'); - } else { - g_assert_not_reached(); - } - } - } - } - g_assert(dststr != NULL); - /* Now patch up the consumer with the register number */ - dst_idx = dststr - opcode_reginfo[def_opcode]; - g_assert(packet->insn[def_idx].dest_idx != -1 && - packet->insn[def_idx].dest_idx == dst_idx); + g_assert(packet->insn[def_idx].dest_idx != -1); + dst_idx = packet->insn[def_idx].dest_idx; packet->insn[i].regno[use_regidx] = packet->insn[def_idx].regno[dst_idx]; /* @@ -366,11 +325,7 @@ static void decode_shuffle_for_execution(Packet *packet) for (flag = false, i = 0; i < last_insn + 1; i++) { int opcode = packet->insn[i].opcode; - g_assert(packet->insn[i].has_pred_dest == - (strstr(opcode_wregs[opcode], "Pd4") || - strstr(opcode_wregs[opcode], "Pe4"))); - if ((strstr(opcode_wregs[opcode], "Pd4") || - strstr(opcode_wregs[opcode], "Pe4")) && + if (packet->insn[i].has_pred_dest && GET_ATTRIB(opcode, A_STORE) == 0) { /* This should be a compare (not a store conditional) */ if (flag) { diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c index c1320406df..f850d0154d 100644 --- a/target/hexagon/mmvec/decode_ext_mmvec.c +++ b/target/hexagon/mmvec/decode_ext_mmvec.c @@ -28,21 +28,15 @@ check_new_value(Packet *pkt) { /* .new value for a MMVector store */ int i, j; - const char *reginfo; - const char *destletters; - const char *dststr = NULL; uint16_t def_opcode; - char letter; for (i = 1; i < pkt->num_insns; i++) { uint16_t use_opcode = pkt->insn[i].opcode; if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) && GET_ATTRIB(use_opcode, A_CVI) && GET_ATTRIB(use_opcode, A_STORE)) { - int use_regidx = strchr(opcode_reginfo[use_opcode], 's') - - opcode_reginfo[use_opcode]; - g_assert(pkt->insn[i].new_read_idx != -1 && - pkt->insn[i].new_read_idx == use_regidx); + int use_regidx = pkt->insn[i].new_read_idx; + g_assert(pkt->insn[i].new_read_idx != -1); /* * What's encoded at the N-field is the offset to who's producing * the value. @@ -70,33 +64,19 @@ check_new_value(Packet *pkt) /* def_idx is the index of the producer */ def_opcode = pkt->insn[def_idx].opcode; - reginfo = opcode_reginfo[def_opcode]; - destletters = "dexy"; - for (j = 0; (letter = destletters[j]) != 0; j++) { - dststr = strchr(reginfo, letter); - if (dststr != NULL) { - break; - } - } - if ((dststr == NULL) && GET_ATTRIB(def_opcode, A_CVI_GATHER)) { + if ((pkt->insn[def_idx].dest_idx == -1) && + GET_ATTRIB(def_opcode, A_CVI_GATHER)) { pkt->insn[i].regno[use_regidx] = def_oreg; pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot; } else { - if (dststr == NULL) { + if (pkt->insn[def_idx].dest_idx == -1) { /* still not there, we have a bad packet */ g_assert_not_reached(); } - g_assert(pkt->insn[def_idx].dest_idx != -1 && - pkt->insn[def_idx].dest_idx == dststr - reginfo); - int def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; + int def_regnum = + pkt->insn[def_idx].regno[pkt->insn[def_idx].dest_idx]; /* Now patch up the consumer with the register number */ pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg; - /* special case for (Vx,Vy) */ - dststr = strchr(reginfo, 'y'); - if (def_oreg && strchr(reginfo, 'x') && dststr) { - def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; - pkt->insn[i].regno[use_regidx] = def_regnum; - } /* * We need to remember who produces this value to later * check if it was dynamically cancelled diff --git a/target/hexagon/opcodes.c b/target/hexagon/opcodes.c index 1f7f3def38..02ae9cf787 100644 --- a/target/hexagon/opcodes.c +++ b/target/hexagon/opcodes.c @@ -36,34 +36,6 @@ const char * const opcode_names[] = { #undef OPCODE }; -const char * const opcode_reginfo[] = { -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#define REGINFO(TAG, REGINFO, RREGS, WREGS) REGINFO, -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - - -const char * const opcode_rregs[] = { -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#define REGINFO(TAG, REGINFO, RREGS, WREGS) RREGS, -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - - -const char * const opcode_wregs[] = { -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#define REGINFO(TAG, REGINFO, RREGS, WREGS) WREGS, -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; const char * const opcode_short_semantics[] = { #define DEF_SHORTCODE(TAG, SHORTCODE) [TAG] = #SHORTCODE, diff --git a/target/hexagon/opcodes.h b/target/hexagon/opcodes.h index fa7e321950..0ee11bd445 100644 --- a/target/hexagon/opcodes.h +++ b/target/hexagon/opcodes.h @@ -40,10 +40,6 @@ typedef enum { extern const char * const opcode_names[]; -extern const char * const opcode_reginfo[]; -extern const char * const opcode_rregs[]; -extern const char * const opcode_wregs[]; - typedef struct { const char * const encoding; const EncClass enc_class; From b45c1b51248470220131235f43670d93286a9346 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:25 -0700 Subject: [PATCH 0455/2884] Hexagon (target/hexagon) Remove gen_op_regs.py Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240307032327.4799-8-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/README | 1 - target/hexagon/gen_op_regs.py | 125 ---------------------------------- target/hexagon/meson.build | 14 +--- 3 files changed, 2 insertions(+), 138 deletions(-) delete mode 100755 target/hexagon/gen_op_regs.py diff --git a/target/hexagon/README b/target/hexagon/README index c1d8c8d0ab..224a3f9206 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -43,7 +43,6 @@ target/hexagon/gen_semantics.c. This step produces That file is consumed by the following python scripts to produce the indicated header files in /target/hexagon gen_opcodes_def.py -> opcodes_def_generated.h.inc - gen_op_regs.py -> op_regs_generated.h.inc gen_printinsn.py -> printinsn_generated.h.inc gen_op_attribs.py -> op_attribs_generated.h.inc gen_helper_protos.py -> helper_protos_generated.h.inc diff --git a/target/hexagon/gen_op_regs.py b/target/hexagon/gen_op_regs.py deleted file mode 100755 index 7b7b33895a..0000000000 --- a/target/hexagon/gen_op_regs.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 - -## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program 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 General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, see . -## - -import sys -import re -import string -import hex_common - - -## -## Generate the register and immediate operands for each instruction -## -def calculate_regid_reg(tag): - def letter_inc(x): - return chr(ord(x) + 1) - - ordered_implregs = ["SP", "FP", "LR"] - srcdst_lett = "X" - src_lett = "S" - dst_lett = "D" - retstr = "" - mapdict = {} - for reg in ordered_implregs: - reg_rd = 0 - reg_wr = 0 - if ("A_IMPLICIT_WRITES_" + reg) in hex_common.attribdict[tag]: - reg_wr = 1 - if reg_rd and reg_wr: - retstr += srcdst_lett - mapdict[srcdst_lett] = reg - srcdst_lett = letter_inc(srcdst_lett) - elif reg_rd: - retstr += src_lett - mapdict[src_lett] = reg - src_lett = letter_inc(src_lett) - elif reg_wr: - retstr += dst_lett - mapdict[dst_lett] = reg - dst_lett = letter_inc(dst_lett) - return retstr, mapdict - - -def calculate_regid_letters(tag): - retstr, mapdict = calculate_regid_reg(tag) - return retstr - - -def strip_reg_prefix(x): - y = x.replace("UREG.", "") - y = y.replace("MREG.", "") - return y.replace("GREG.", "") - - -def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.init_registers() - tagregs = hex_common.get_tagregs(full=True) - tagimms = hex_common.get_tagimms() - - with open(sys.argv[3], "w") as f: - for tag in hex_common.tags: - regs = tagregs[tag] - rregs = [] - wregs = [] - regids = "" - for regtype, regid, _, numregs in regs: - reg = hex_common.get_register(tag, regtype, regid) - if reg.is_read(): - if regid[0] not in regids: - regids += regid[0] - rregs.append(regtype + regid + numregs) - if reg.is_written(): - wregs.append(regtype + regid + numregs) - if regid[0] not in regids: - regids += regid[0] - for attrib in hex_common.attribdict[tag]: - if hex_common.attribinfo[attrib]["rreg"]: - rregs.append(strip_reg_prefix(attribinfo[attrib]["rreg"])) - if hex_common.attribinfo[attrib]["wreg"]: - wregs.append(strip_reg_prefix(attribinfo[attrib]["wreg"])) - regids += calculate_regid_letters(tag) - f.write( - f'REGINFO({tag},"{regids}",\t/*RD:*/\t"{",".join(rregs)}",' - f'\t/*WR:*/\t"{",".join(wregs)}")\n' - ) - - for tag in hex_common.tags: - imms = tagimms[tag] - f.write(f"IMMINFO({tag}") - if not imms: - f.write(""",'u',0,0,'U',0,0""") - for sign, size, shamt in imms: - if sign == "r": - sign = "s" - if not shamt: - shamt = "0" - f.write(f""",'{sign}',{size},{shamt}""") - if len(imms) == 1: - if sign.isupper(): - myu = "u" - else: - myu = "U" - f.write(f""",'{myu}',0,0""") - f.write(")\n") - - -if __name__ == "__main__": - main() diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index fb480afc03..b3a0944d3b 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -1,5 +1,5 @@ ## -## Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -45,7 +45,6 @@ hexagon_ss.add(semantics_generated) # shortcode_generated.h.inc # tcg_func_table_generated.c.inc # printinsn_generated.h.inc -# op_regs_generated.h.inc # op_attribs_generated.h.inc # opcodes_def_generated.h.inc # @@ -76,15 +75,6 @@ printinsn_generated = custom_target( ) hexagon_ss.add(printinsn_generated) -op_regs_generated = custom_target( - 'op_regs_generated.h.inc', - output: 'op_regs_generated.h.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_op_regs.py'), semantics_generated, attribs_def, '@OUTPUT@'], -) -hexagon_ss.add(op_regs_generated) - op_attribs_generated = custom_target( 'op_attribs_generated.h.inc', output: 'op_attribs_generated.h.inc', @@ -110,7 +100,7 @@ hexagon_ss.add(opcodes_def_generated) # gen_dectree_import = executable( 'gen_dectree_import', - 'gen_dectree_import.c', opcodes_def_generated, op_regs_generated, + 'gen_dectree_import.c', opcodes_def_generated, native: true, build_by_default: false) iset_py = custom_target( From 582c59efaecf01c08e4d034b7140b2b5e5ac9480 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:26 -0700 Subject: [PATCH 0456/2884] Hexagon (target/hexagon) Remove gen_shortcode.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This data structure is not used Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240307032327.4799-9-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/README | 1 - target/hexagon/gen_shortcode.py | 63 --------------------------------- target/hexagon/meson.build | 10 ------ target/hexagon/opcodes.c | 7 ---- 4 files changed, 81 deletions(-) delete mode 100755 target/hexagon/gen_shortcode.py diff --git a/target/hexagon/README b/target/hexagon/README index 224a3f9206..7ffd517d70 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -46,7 +46,6 @@ header files in /target/hexagon gen_printinsn.py -> printinsn_generated.h.inc gen_op_attribs.py -> op_attribs_generated.h.inc gen_helper_protos.py -> helper_protos_generated.h.inc - gen_shortcode.py -> shortcode_generated.h.inc gen_tcg_funcs.py -> tcg_funcs_generated.c.inc gen_tcg_func_table.py -> tcg_func_table_generated.c.inc gen_helper_funcs.py -> helper_funcs_generated.c.inc diff --git a/target/hexagon/gen_shortcode.py b/target/hexagon/gen_shortcode.py deleted file mode 100755 index deb94446c4..0000000000 --- a/target/hexagon/gen_shortcode.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 - -## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program 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 General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, see . -## - -import sys -import re -import string -import hex_common - - -def gen_shortcode(f, tag): - f.write(f"DEF_SHORTCODE({tag}, {hex_common.semdict[tag]})\n") - - -def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.calculate_attribs() - tagregs = hex_common.get_tagregs() - tagimms = hex_common.get_tagimms() - - with open(sys.argv[3], "w") as f: - f.write("#ifndef DEF_SHORTCODE\n") - f.write("#define DEF_SHORTCODE(TAG,SHORTCODE) /* Nothing */\n") - f.write("#endif\n") - - for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": - continue - - gen_shortcode(f, tag) - - f.write("#undef DEF_SHORTCODE\n") - - -if __name__ == "__main__": - main() diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index b3a0944d3b..988e7489ba 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -42,21 +42,11 @@ hexagon_ss.add(semantics_generated) # # Step 2 # We use Python scripts to generate the following files -# shortcode_generated.h.inc # tcg_func_table_generated.c.inc # printinsn_generated.h.inc # op_attribs_generated.h.inc # opcodes_def_generated.h.inc # -shortcode_generated = custom_target( - 'shortcode_generated.h.inc', - output: 'shortcode_generated.h.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_shortcode.py'), semantics_generated, attribs_def, '@OUTPUT@'], -) -hexagon_ss.add(shortcode_generated) - tcg_func_table_generated = custom_target( 'tcg_func_table_generated.c.inc', output: 'tcg_func_table_generated.c.inc', diff --git a/target/hexagon/opcodes.c b/target/hexagon/opcodes.c index 02ae9cf787..c8bde2f9e9 100644 --- a/target/hexagon/opcodes.c +++ b/target/hexagon/opcodes.c @@ -37,13 +37,6 @@ const char * const opcode_names[] = { }; -const char * const opcode_short_semantics[] = { -#define DEF_SHORTCODE(TAG, SHORTCODE) [TAG] = #SHORTCODE, -#include "shortcode_generated.h.inc" -#undef DEF_SHORTCODE - NULL -}; - DECLARE_BITMAP(opcode_attribs[XX_LAST_OPCODE], A_ZZ_LASTATTRIB); static void init_attribs(int tag, ...) From a4696661491cac8c1c08e7d482d751f808ce3143 Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Wed, 6 Mar 2024 20:23:27 -0700 Subject: [PATCH 0457/2884] Hexagon (target/hexagon) Remove hex_common.read_attribs_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The attribinfo data structure is not used Adjust the command-line arguments to the python scripts Add hex_common.read_common_files for TCG/helper generation scripts Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240307032327.4799-10-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain --- target/hexagon/gen_analyze_funcs.py | 19 +------------- target/hexagon/gen_helper_funcs.py | 21 ++------------- target/hexagon/gen_helper_protos.py | 19 +------------- target/hexagon/gen_idef_parser_funcs.py | 5 ++-- target/hexagon/gen_op_attribs.py | 5 ++-- target/hexagon/gen_opcodes_def.py | 4 +-- target/hexagon/gen_printinsn.py | 5 ++-- target/hexagon/gen_tcg_func_table.py | 5 ++-- target/hexagon/gen_tcg_funcs.py | 21 ++------------- target/hexagon/hex_common.py | 35 +++++++++++++++---------- target/hexagon/meson.build | 31 +++++++++++----------- 11 files changed, 52 insertions(+), 118 deletions(-) diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index 81e1d9cfa3..54bac19724 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py @@ -78,24 +78,7 @@ def gen_analyze_func(f, tag, regs, imms): def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) - hex_common.calculate_attribs() - hex_common.init_registers() + hex_common.read_common_files() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index 9cc3d69c49..e9685bff2f 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -102,24 +102,7 @@ def gen_helper_function(f, tag, tagregs, tagimms): def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) - hex_common.calculate_attribs() - hex_common.init_registers() + hex_common.read_common_files() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index f8578d5033..fd2bfd0f36 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -52,24 +52,7 @@ def gen_helper_prototype(f, tag, tagregs, tagimms): def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) - hex_common.calculate_attribs() - hex_common.init_registers() + hex_common.read_common_files() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py index 550a48cb7b..eb494abba8 100644 --- a/target/hexagon/gen_idef_parser_funcs.py +++ b/target/hexagon/gen_idef_parser_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved. +## Copyright(c) 2019-2024 rev.ng Labs Srl. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -44,13 +44,12 @@ import hex_common ## def main(): hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) hex_common.calculate_attribs() hex_common.init_registers() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], "w") as f: + with open(sys.argv[-1], "w") as f: f.write('#include "macros.inc"\n\n') for tag in hex_common.tags: diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py index 41074b8573..99448220da 100755 --- a/target/hexagon/gen_op_attribs.py +++ b/target/hexagon/gen_op_attribs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -25,13 +25,12 @@ import hex_common def main(): hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) hex_common.calculate_attribs() ## ## Generate all the attributes associated with each instruction ## - with open(sys.argv[3], "w") as f: + with open(sys.argv[-1], "w") as f: for tag in hex_common.tags: f.write( f"OP_ATTRIB({tag},ATTRIBS(" diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py index cddd868fe3..536f0eb68a 100755 --- a/target/hexagon/gen_opcodes_def.py +++ b/target/hexagon/gen_opcodes_def.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ def main(): ## ## Generate a list of all the opcodes ## - with open(sys.argv[3], "w") as f: + with open(sys.argv[-1], "w") as f: for tag in hex_common.tags: f.write(f"OPCODE({tag}),\n") diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py index e570bd7c6a..8bf4d0985c 100755 --- a/target/hexagon/gen_printinsn.py +++ b/target/hexagon/gen_printinsn.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -97,11 +97,10 @@ def spacify(s): def main(): hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) immext_casere = re.compile(r"IMMEXT\(([A-Za-z])") - with open(sys.argv[3], "w") as f: + with open(sys.argv[-1], "w") as f: for tag in hex_common.tags: if not hex_common.behdict[tag]: continue diff --git a/target/hexagon/gen_tcg_func_table.py b/target/hexagon/gen_tcg_func_table.py index f998ef0992..978ac1819b 100755 --- a/target/hexagon/gen_tcg_func_table.py +++ b/target/hexagon/gen_tcg_func_table.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -25,12 +25,11 @@ import hex_common def main(): hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], "w") as f: + with open(sys.argv[-1], "w") as f: f.write("#ifndef HEXAGON_FUNC_TABLE_H\n") f.write("#define HEXAGON_FUNC_TABLE_H\n\n") diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 3d8e3cb6a2..05aa0a7855 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -108,24 +108,7 @@ def gen_def_tcg_func(f, tag, tagregs, tagimms): def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - hex_common.calculate_attribs() - hex_common.init_registers() - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) + is_idef_parser_enabled = hex_common.read_common_files() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index f6f187968a..15ed4980e4 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -26,7 +26,6 @@ behdict = {} # tag ->behavior semdict = {} # tag -> semantics attribdict = {} # tag -> attributes macros = {} # macro -> macro information... -attribinfo = {} # Register information and misc registers = {} # register -> register functions new_registers = {} tags = [] # list of all tags @@ -288,19 +287,6 @@ def read_semantics_file(name): eval_line = "" -def read_attribs_file(name): - attribre = re.compile( - r"DEF_ATTRIB\(([A-Za-z0-9_]+), ([^,]*), " - + r'"([A-Za-z0-9_\.]*)", "([A-Za-z0-9_\.]*)"\)' - ) - for line in open(name, "rt").readlines(): - if not attribre.match(line): - continue - (attrib_base, descr, rreg, wreg) = attribre.findall(line)[0] - attrib_base = "A_" + attrib_base - attribinfo[attrib_base] = {"rreg": rreg, "wreg": wreg, "descr": descr} - - def read_overrides_file(name): overridere = re.compile(r"#define fGEN_TCG_([A-Za-z0-9_]+)\(.*") for line in open(name, "rt").readlines(): @@ -1193,3 +1179,24 @@ def helper_args(tag, regs, imms): "uint32_t part1" )) return args + + +def read_common_files(): + read_semantics_file(sys.argv[1]) + read_overrides_file(sys.argv[2]) + read_overrides_file(sys.argv[3]) + ## Whether or not idef-parser is enabled is + ## determined by the number of arguments to + ## this script: + ## + ## 4 args. -> not enabled, + ## 5 args. -> idef-parser enabled. + ## + ## The 5:th arg. then holds a list of the successfully + ## parsed instructions. + is_idef_parser_enabled = len(sys.argv) > 5 + if is_idef_parser_enabled: + read_idef_parser_enabled_file(sys.argv[4]) + calculate_attribs() + init_registers() + return is_idef_parser_enabled diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index 988e7489ba..b0b253aa6b 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -18,7 +18,6 @@ hexagon_ss = ss.source_set() hex_common_py = 'hex_common.py' -attribs_def = meson.current_source_dir() / 'attribs_def.h.inc' gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h' gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h' idef_parser_dir = meson.current_source_dir() / 'idef-parser' @@ -51,8 +50,8 @@ tcg_func_table_generated = custom_target( 'tcg_func_table_generated.c.inc', output: 'tcg_func_table_generated.c.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_tcg_func_table.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_tcg_func_table.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(tcg_func_table_generated) @@ -60,8 +59,8 @@ printinsn_generated = custom_target( 'printinsn_generated.h.inc', output: 'printinsn_generated.h.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_printinsn.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_printinsn.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(printinsn_generated) @@ -69,8 +68,8 @@ op_attribs_generated = custom_target( 'op_attribs_generated.h.inc', output: 'op_attribs_generated.h.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_op_attribs.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_op_attribs.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(op_attribs_generated) @@ -78,8 +77,8 @@ opcodes_def_generated = custom_target( 'opcodes_def_generated.h.inc', output: 'opcodes_def_generated.h.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_opcodes_def.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_opcodes_def.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(opcodes_def_generated) @@ -278,7 +277,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs output: 'idef_parser_input.h.inc', depends: [semantics_generated], depend_files: [hex_common_py], - command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, attribs_def, '@OUTPUT@'], + command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, '@OUTPUT@'], ) preprocessed_idef_parser_input_generated = custom_target( @@ -347,12 +346,12 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg] - helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list] else # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated] - helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h] endif # @@ -366,7 +365,7 @@ helper_protos_generated = custom_target( 'helper_protos_generated.h.inc', output: 'helper_protos_generated.h.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], command: [python, files('gen_helper_protos.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(helper_protos_generated) @@ -375,7 +374,7 @@ helper_funcs_generated = custom_target( 'helper_funcs_generated.c.inc', output: 'helper_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], command: [python, files('gen_helper_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(helper_funcs_generated) @@ -384,7 +383,7 @@ tcg_funcs_generated = custom_target( 'tcg_funcs_generated.c.inc', output: 'tcg_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], command: [python, files('gen_tcg_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(tcg_funcs_generated) @@ -393,7 +392,7 @@ analyze_funcs_generated = custom_target( 'analyze_funcs_generated.c.inc', output: 'analyze_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], command: [python, files('gen_analyze_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(analyze_funcs_generated) From 1072f927f0966d37b37c52084b4eb957288b2704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 2 Dec 2023 15:24:15 +0100 Subject: [PATCH 0458/2884] exec/cpu: Indent TARGET_PAGE_foo definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TARGET_PAGE_foo definitions are defined with multiple level of #ifdef'ry. Indent it a bit for clarity. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-6-philmd@linaro.org> --- include/exec/cpu-all.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 032c6d990e..14fd40046d 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -139,19 +139,20 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val #ifdef TARGET_PAGE_BITS_VARY # include "exec/page-vary.h" extern const TargetPageBits target_page; -#ifdef CONFIG_DEBUG_TCG -#define TARGET_PAGE_BITS ({ assert(target_page.decided); target_page.bits; }) -#define TARGET_PAGE_MASK ({ assert(target_page.decided); \ - (target_long)target_page.mask; }) +# ifdef CONFIG_DEBUG_TCG +# define TARGET_PAGE_BITS ({ assert(target_page.decided); \ + target_page.bits; }) +# define TARGET_PAGE_MASK ({ assert(target_page.decided); \ + (target_long)target_page.mask; }) +# else +# define TARGET_PAGE_BITS target_page.bits +# define TARGET_PAGE_MASK ((target_long)target_page.mask) +# endif +# define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) #else -#define TARGET_PAGE_BITS target_page.bits -#define TARGET_PAGE_MASK ((target_long)target_page.mask) -#endif -#define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) -#else -#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS -#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) -#define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS) +# define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS +# define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) +# define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS) #endif #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) From 86b7c5518232c8e5cda7951cbe62b0b23fc0b4e5 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 5 May 2024 14:10:08 +0200 Subject: [PATCH 0459/2884] exec/cpu: Rename PAGE_BITS macro to PAGE_RWX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro can be used to abbreviate PAGE_READ | PAGE_WRITE | PAGE_EXEC for which PAGE_RWX is a better name and renaming it also shows it is not related to TARGET_PAGE_BITS. Signed-off-by: BALATON Zoltan Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240505121008.44A0D4E602D@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- accel/tcg/user-exec.c | 2 +- bsd-user/mmap.c | 6 +++--- include/exec/cpu-common.h | 2 +- linux-user/elfload.c | 2 +- linux-user/mmap.c | 2 +- target/cris/mmu.c | 4 ++-- target/microblaze/helper.c | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 1c621477ad..a81e3cc920 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -765,7 +765,7 @@ int page_unprotect(target_ulong address, uintptr_t pc) if (prot & PAGE_EXEC) { prot = (prot & ~PAGE_EXEC) | PAGE_READ; } - mprotect((void *)g2h_untagged(start), len, prot & PAGE_BITS); + mprotect((void *)g2h_untagged(start), len, prot & PAGE_RWX); } mmap_unlock(); diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 3ef11b2807..c785615392 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -96,7 +96,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) end = host_end; } ret = mprotect(g2h_untagged(host_start), - qemu_host_page_size, prot1 & PAGE_BITS); + qemu_host_page_size, prot1 & PAGE_RWX); if (ret != 0) goto error; host_start += qemu_host_page_size; @@ -107,7 +107,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) prot1 |= page_get_flags(addr); } ret = mprotect(g2h_untagged(host_end - qemu_host_page_size), - qemu_host_page_size, prot1 & PAGE_BITS); + qemu_host_page_size, prot1 & PAGE_RWX); if (ret != 0) goto error; host_end -= qemu_host_page_size; @@ -174,7 +174,7 @@ static int mmap_frag(abi_ulong real_start, return -1; prot1 = prot; } - prot1 &= PAGE_BITS; + prot1 &= PAGE_RWX; prot_new = prot | prot1; if (fd != -1) { diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 8812ba744d..a4bb4e6680 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -212,7 +212,7 @@ G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); #define PAGE_READ 0x0001 #define PAGE_WRITE 0x0002 #define PAGE_EXEC 0x0004 -#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) +#define PAGE_RWX (PAGE_READ | PAGE_WRITE | PAGE_EXEC) #define PAGE_VALID 0x0008 /* * Original state of the write flag (used when tracking self-modifying code) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f9461d2844..41fae2b520 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2361,7 +2361,7 @@ static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, if (start_bss < align_bss) { int flags = page_get_flags(start_bss); - if (!(flags & PAGE_BITS)) { + if (!(flags & PAGE_RWX)) { /* * The whole address space of the executable was reserved * at the start, therefore all pages will be VALID. diff --git a/linux-user/mmap.c b/linux-user/mmap.c index be3b9a68eb..66a1631094 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -117,7 +117,7 @@ static void shm_region_rm_complete(abi_ptr start, abi_ptr last) static int validate_prot_to_pageflags(int prot) { int valid = PROT_READ | PROT_WRITE | PROT_EXEC | TARGET_PROT_SEM; - int page_flags = (prot & PAGE_BITS) | PAGE_VALID; + int page_flags = (prot & PAGE_RWX) | PAGE_VALID; #ifdef TARGET_AARCH64 { diff --git a/target/cris/mmu.c b/target/cris/mmu.c index b574ec6e5b..c25c31c9f8 100644 --- a/target/cris/mmu.c +++ b/target/cris/mmu.c @@ -333,7 +333,7 @@ int cris_mmu_translate(struct cris_mmu_result *res, if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) { res->phy = vaddr; - res->prot = PAGE_BITS; + res->prot = PAGE_RWX; goto done; } @@ -344,7 +344,7 @@ int cris_mmu_translate(struct cris_mmu_result *res, miss = 0; base = cris_mmu_translate_seg(env, seg); res->phy = base | (0x0fffffff & vaddr); - res->prot = PAGE_BITS; + res->prot = PAGE_RWX; } else { miss = cris_mmu_translate_page(res, env, vaddr, access_type, is_user, debug); diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index d25c9eb4d3..ff5f86ddc2 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -51,7 +51,7 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (mmu_idx == MMU_NOMMU_IDX) { /* MMU disabled or not available. */ address &= TARGET_PAGE_MASK; - prot = PAGE_BITS; + prot = PAGE_RWX; tlb_set_page_with_attrs(cs, address, address, attrs, prot, mmu_idx, TARGET_PAGE_SIZE); return true; From a4f06b1a056b17336666c5fb218231259934dace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Mar 2024 20:06:31 +0100 Subject: [PATCH 0460/2884] exec/cpu: Remove obsolete PAGE_RESERVED definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We stopped using the PAGE_RESERVED definition in commit 50d25c8aec ("accel/tcg: Drop PAGE_RESERVED for CONFIG_BSD"). This completes commit 2e9a5713f0 ("Remove PAGE_RESERVED"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-7-philmd@linaro.org> --- include/exec/cpu-all.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 14fd40046d..104c5dd2da 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -157,10 +157,6 @@ extern const TargetPageBits target_page; #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) -#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) -/* FIXME: Code that sets/uses this is broken and needs to go away. */ -#define PAGE_RESERVED 0x0100 -#endif /* * For linux-user, indicates that the page is mapped with the same semantics * in both guest and host. From 7dd1259b374ee32bf2a967697053e5401369c29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 21 Mar 2024 20:03:13 +0100 Subject: [PATCH 0461/2884] exec/cpu: Remove duplicated PAGE_PASSTHROUGH definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in commit 58771921af ("include/exec: Move PAGE_* macros to common header"), PAGE_PASSTHROUGH ended being defined twice. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-8-philmd@linaro.org> --- include/exec/cpu-all.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 104c5dd2da..c4dada5b44 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -157,12 +157,6 @@ extern const TargetPageBits target_page; #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) -/* - * For linux-user, indicates that the page is mapped with the same semantics - * in both guest and host. - */ -#define PAGE_PASSTHROUGH 0x0800 - #if defined(CONFIG_USER_ONLY) void page_dump(FILE *f); From 74781c0888e819552538593c0932d98ea16c766b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 6 Dec 2023 20:27:32 +0100 Subject: [PATCH 0462/2884] exec/cpu: Extract page-protection definitions to page-protection.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract page-protection definitions from "exec/cpu-all.h" to "exec/page-protection.h". The list of files requiring the new header was generated using: $ git grep -wE \ 'PAGE_(READ|WRITE|EXEC|RWX|VALID|ANON|RESERVED|TARGET_.|PASSTHROUGH)' Signed-off-by: Philippe Mathieu-Daudé Acked-by: Nicholas Piggin Acked-by: Richard Henderson Message-Id: <20240427155714.53669-3-philmd@linaro.org> --- MAINTAINERS | 1 + accel/tcg/cputlb.c | 1 + accel/tcg/tb-maint.c | 1 + accel/tcg/user-exec.c | 1 + bsd-user/bsd-mem.h | 1 + bsd-user/mmap.c | 1 + bsd-user/qemu.h | 1 + bsd-user/signal.c | 1 + cpu-target.c | 1 + hw/ppc/ppc440_bamboo.c | 1 + hw/ppc/sam460ex.c | 1 + hw/ppc/virtex_ml507.c | 1 + include/exec/cpu-all.h | 1 + include/exec/cpu-common.h | 31 +-------------------- include/exec/page-protection.h | 41 ++++++++++++++++++++++++++++ include/semihosting/uaccess.h | 1 + linux-user/arm/cpu_loop.c | 1 + linux-user/elfload.c | 1 + linux-user/mmap.c | 1 + linux-user/signal.c | 1 + linux-user/syscall.c | 1 + system/physmem.c | 1 + target/alpha/helper.c | 1 + target/arm/cpu.h | 1 + target/arm/ptw.c | 1 + target/arm/tcg/m_helper.c | 1 + target/arm/tcg/mte_helper.c | 1 + target/arm/tcg/sve_helper.c | 1 + target/avr/helper.c | 1 + target/cris/mmu.c | 1 + target/hppa/mem_helper.c | 1 + target/hppa/translate.c | 1 + target/i386/tcg/sysemu/excp_helper.c | 1 + target/loongarch/tcg/tlb_helper.c | 1 + target/m68k/helper.c | 1 + target/microblaze/helper.c | 1 + target/microblaze/mmu.c | 1 + target/mips/sysemu/physaddr.c | 1 + target/mips/tcg/sysemu/tlb_helper.c | 1 + target/openrisc/mmu.c | 1 + target/ppc/internal.h | 1 + target/ppc/mmu-hash32.c | 1 + target/ppc/mmu-hash64.c | 1 + target/ppc/mmu-radix64.c | 1 + target/ppc/mmu-radix64.h | 2 ++ target/ppc/mmu_common.c | 1 + target/ppc/mmu_helper.c | 1 + target/riscv/cpu_helper.c | 1 + target/riscv/pmp.c | 1 + target/riscv/vector_helper.c | 1 + target/rx/cpu.c | 1 + target/s390x/mmu_helper.c | 1 + target/s390x/tcg/mem_helper.c | 1 + target/sh4/helper.c | 1 + target/sparc/ldst_helper.c | 1 + target/sparc/mmu_helper.c | 1 + target/tricore/helper.c | 1 + target/xtensa/mmu_helper.c | 1 + target/xtensa/op_helper.c | 1 + 59 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 include/exec/page-protection.h diff --git a/MAINTAINERS b/MAINTAINERS index 2f08cc528e..595808fc96 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -167,6 +167,7 @@ F: include/exec/target_long.h F: include/exec/helper*.h F: include/exec/helper*.h.inc F: include/exec/helper-info.c.inc +F: include/exec/page-protection.h F: include/sysemu/cpus.h F: include/sysemu/tcg.h F: include/hw/core/tcg-cpu-ops.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 953c437ba9..cdb3e12dfb 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -21,6 +21,7 @@ #include "qemu/main-loop.h" #include "hw/core/tcg-cpu-ops.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/memory.h" #include "exec/cpu_ldst.h" #include "exec/cputlb.h" diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index da39a43bd8..19ae6793f3 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "exec/log.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/tb-flush.h" #include "exec/translate-all.h" #include "sysemu/tcg.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index a81e3cc920..d34313a612 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -25,6 +25,7 @@ #include "qemu/rcu.h" #include "exec/cpu_ldst.h" #include "exec/translate-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" #include "trace/trace-root.h" diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h index 21d9bab889..eef6b222d9 100644 --- a/bsd-user/bsd-mem.h +++ b/bsd-user/bsd-mem.h @@ -56,6 +56,7 @@ #include #include "qemu-bsd.h" +#include "exec/page-protection.h" extern struct bsd_shm_regions bsd_shm_regions[]; extern abi_ulong target_brk; diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index c785615392..f3a4f1712d 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "exec/page-protection.h" #include "qemu.h" diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index a916724de9..322177de16 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -34,6 +34,7 @@ extern char **environ; #include "target_os_signal.h" #include "target.h" #include "exec/gdbstub.h" +#include "exec/page-protection.h" #include "qemu/clang-tsa.h" #include "qemu-os.h" diff --git a/bsd-user/signal.c b/bsd-user/signal.c index b2faf1d0dd..8b6654b91d 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu.h" +#include "exec/page-protection.h" #include "user/tswap-target.h" #include "gdbstub/user.h" #include "signal-common.h" diff --git a/cpu-target.c b/cpu-target.c index f88649c299..5af120e8aa 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -21,6 +21,7 @@ #include "qapi/error.h" #include "exec/target_page.h" +#include "exec/page-protection.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index e18f57efce..73f80cf706 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -15,6 +15,7 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qemu/error-report.h" +#include "exec/page-protection.h" #include "net/net.h" #include "hw/pci/pci.h" #include "hw/boards.h" diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index d42b677898..8dc75fb9f0 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -21,6 +21,7 @@ #include "kvm_ppc.h" #include "sysemu/device_tree.h" #include "sysemu/block-backend.h" +#include "exec/page-protection.h" #include "hw/loader.h" #include "elf.h" #include "exec/memory.h" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index d02f330650..c49da1f46f 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "exec/page-protection.h" #include "cpu.h" #include "hw/sysbus.h" #include "hw/char/serial.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index c4dada5b44..6f09b86e7f 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -19,6 +19,7 @@ #ifndef CPU_ALL_H #define CPU_ALL_H +#include "exec/page-protection.h" #include "exec/cpu-common.h" #include "exec/memory.h" #include "exec/tswap.h" diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index a4bb4e6680..78f2c381b1 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -14,6 +14,7 @@ #endif #include "hw/core/cpu.h" #include "tcg/debug-assert.h" +#include "exec/page-protection.h" #define EXCP_INTERRUPT 0x10000 /* async interruption */ #define EXCP_HLT 0x10001 /* hlt instruction reached */ @@ -208,36 +209,6 @@ G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); G_NORETURN void cpu_loop_exit(CPUState *cpu); G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); -/* same as PROT_xxx */ -#define PAGE_READ 0x0001 -#define PAGE_WRITE 0x0002 -#define PAGE_EXEC 0x0004 -#define PAGE_RWX (PAGE_READ | PAGE_WRITE | PAGE_EXEC) -#define PAGE_VALID 0x0008 -/* - * Original state of the write flag (used when tracking self-modifying code) - */ -#define PAGE_WRITE_ORG 0x0010 -/* - * Invalidate the TLB entry immediately, helpful for s390x - * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs() - */ -#define PAGE_WRITE_INV 0x0020 -/* For use with page_set_flags: page is being replaced; target_data cleared. */ -#define PAGE_RESET 0x0040 -/* For linux-user, indicates that the page is MAP_ANON. */ -#define PAGE_ANON 0x0080 - -/* Target-specific bits that will be used via page_get_flags(). */ -#define PAGE_TARGET_1 0x0200 -#define PAGE_TARGET_2 0x0400 - -/* - * For linux-user, indicates that the page is mapped with the same semantics - * in both guest and host. - */ -#define PAGE_PASSTHROUGH 0x0800 - /* accel/tcg/cpu-exec.c */ int cpu_exec(CPUState *cpu); diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h new file mode 100644 index 0000000000..c43231af8b --- /dev/null +++ b/include/exec/page-protection.h @@ -0,0 +1,41 @@ +/* + * QEMU page protection definitions. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#ifndef EXEC_PAGE_PROT_COMMON_H +#define EXEC_PAGE_PROT_COMMON_H + +/* same as PROT_xxx */ +#define PAGE_READ 0x0001 +#define PAGE_WRITE 0x0002 +#define PAGE_EXEC 0x0004 +#define PAGE_RWX (PAGE_READ | PAGE_WRITE | PAGE_EXEC) +#define PAGE_VALID 0x0008 +/* + * Original state of the write flag (used when tracking self-modifying code) + */ +#define PAGE_WRITE_ORG 0x0010 +/* + * Invalidate the TLB entry immediately, helpful for s390x + * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs() + */ +#define PAGE_WRITE_INV 0x0020 +/* For use with page_set_flags: page is being replaced; target_data cleared. */ +#define PAGE_RESET 0x0040 +/* For linux-user, indicates that the page is MAP_ANON. */ +#define PAGE_ANON 0x0080 + +/* Target-specific bits that will be used via page_get_flags(). */ +#define PAGE_TARGET_1 0x0200 +#define PAGE_TARGET_2 0x0400 + +/* + * For linux-user, indicates that the page is mapped with the same semantics + * in both guest and host. + */ +#define PAGE_PASSTHROUGH 0x0800 + +#endif diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h index dd289af8dd..c2fa5a655d 100644 --- a/include/semihosting/uaccess.h +++ b/include/semihosting/uaccess.h @@ -17,6 +17,7 @@ #include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/tswap.h" +#include "exec/page-protection.h" #define get_user_u64(val, addr) \ ({ uint64_t val_ = 0; \ diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index db1a41e27f..ec665862d9 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -24,6 +24,7 @@ #include "cpu_loop-common.h" #include "signal-common.h" #include "semihosting/common-semi.h" +#include "exec/page-protection.h" #include "target/arm/syndrome.h" #define get_user_code_u32(x, gaddr, env) \ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 41fae2b520..746e22b275 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -8,6 +8,7 @@ #include "qemu.h" #include "user/tswap-target.h" +#include "exec/page-protection.h" #include "user/guest-base.h" #include "user-internals.h" #include "signal-common.h" diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 66a1631094..72b30279a2 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -20,6 +20,7 @@ #include #include "trace.h" #include "exec/log.h" +#include "exec/page-protection.h" #include "qemu.h" #include "user-internals.h" #include "user-mmap.h" diff --git a/linux-user/signal.c b/linux-user/signal.c index 05dc4afb52..63ac2df53b 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "gdbstub/user.h" +#include "exec/page-protection.h" #include "hw/core/tcg-cpu-ops.h" #include diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 41659b63f5..6a492c9d35 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -25,6 +25,7 @@ #include "qemu/plugin.h" #include "tcg/startup.h" #include "target_mman.h" +#include "exec/page-protection.h" #include #include #include diff --git a/system/physmem.c b/system/physmem.c index 1a81c226ba..44e477a1a5 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -31,6 +31,7 @@ #endif /* CONFIG_TCG */ #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/target_page.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" diff --git a/target/alpha/helper.c b/target/alpha/helper.c index c5e4958f8b..2f1000c99f 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "fpu/softfloat-types.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a550bcd25f..c17264c239 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -26,6 +26,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #include "exec/gdbstub.h" +#include "exec/page-protection.h" #include "qapi/qapi-types-common.h" #include "target/arm/multiprocessing.h" #include "target/arm/gtimer.h" diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 31ae43f60e..4476b32ff5 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -11,6 +11,7 @@ #include "qemu/range.h" #include "qemu/main-loop.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "cpu.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index d1f1e02acc..23d7f73035 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -16,6 +16,7 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #ifdef CONFIG_TCG #include "exec/cpu_ldst.h" #include "semihosting/common-semi.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index d971b81370..037ac6dd60 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "internals.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/ram_addr.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 6853f58c19..dd49e67d7a 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "internals.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" diff --git a/target/avr/helper.c b/target/avr/helper.c index eeca415c43..345708a1b3 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -24,6 +24,7 @@ #include "cpu.h" #include "hw/core/tcg-cpu-ops.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "exec/address-spaces.h" #include "exec/helper-proto.h" diff --git a/target/cris/mmu.c b/target/cris/mmu.c index c25c31c9f8..d51008c541 100644 --- a/target/cris/mmu.c +++ b/target/cris/mmu.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "mmu.h" #ifdef DEBUG diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 84785b5a5c..d09877afd7 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #include "hw/core/cpu.h" #include "trace.h" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 42fa480950..6d45611888 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -22,6 +22,7 @@ #include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/helper-proto.h" diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 7a57b7dd10..8fb05b1f53 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 57f5308632..d6331f9b0b 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -13,6 +13,7 @@ #include "internals.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "cpu-csr.h" diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 7a91f33b17..7967ad13cb 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" #include "gdbstub/helpers.h" diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index ff5f86ddc2..5d3259ce31 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/host-utils.h" #include "exec/log.h" diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 234006634e..2423ac6172 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -22,6 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" static unsigned int tlb_decode_size(unsigned int f) { diff --git a/target/mips/sysemu/physaddr.c b/target/mips/sysemu/physaddr.c index 5c5184e136..505781d84c 100644 --- a/target/mips/sysemu/physaddr.c +++ b/target/mips/sysemu/physaddr.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "../internal.h" static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 119eae771e..3ba6d369a6 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "internal.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "exec/helper-proto.h" diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 603c26715e..c632d5230b 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -22,6 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "hw/loader.h" diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 601c0b533f..98b41a970c 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -20,6 +20,7 @@ #include "exec/breakpoint.h" #include "hw/registerfields.h" +#include "exec/page-protection.h" /* PM instructions */ typedef enum { diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 3976416840..6dfedab11d 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "internal.h" diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index d645c0bb94..5a0d80feda 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -21,6 +21,7 @@ #include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "sysemu/hw_accel.h" diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 690dff7a49..8daf71d2db 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index 4c768aa5cc..c5c04a1527 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -3,6 +3,8 @@ #ifndef CONFIG_USER_ONLY +#include "exec/page-protection.h" + /* Radix Quadrants */ #define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF #define R_EADDR_VALID_MASK 0xC00FFFFFFFFFFFFF diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 751403f1c8..4fde7fd3bf 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -25,6 +25,7 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index c071b4d5e2..b35a93c198 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -25,6 +25,7 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index fc090d729a..8ad546a45a 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -24,6 +24,7 @@ #include "internals.h" #include "pmu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "instmap.h" #include "tcg/tcg-op.h" #include "trace.h" diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 2a76b611a0..9eea397e72 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "trace.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, uint8_t val); diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index fa139040f8..1b4d5a8e37 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -23,6 +23,7 @@ #include "exec/memop.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" diff --git a/target/rx/cpu.c b/target/rx/cpu.c index e3dfb09722..c1a592e893 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "hw/loader.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index fbb2f1b4d4..f3a2f25a5c 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -24,6 +24,7 @@ #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "trace.h" #include "hw/hw.h" #include "hw/s390x/storage-keys.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 557831def4..6a308c5553 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -25,6 +25,7 @@ #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "hw/core/tcg-cpu-ops.h" #include "qemu/int128.h" diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 7c6f9d374a..6702910627 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/log.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 2846a86cc4..7bdf99e0c0 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -23,6 +23,7 @@ #include "tcg/tcg.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "asi.h" diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index ad1591d9fd..9ff06026b8 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/qemu-print.h" #include "trace.h" diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 76bd226370..7014255f77 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -20,6 +20,7 @@ #include "hw/registerfields.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "fpu/softfloat-helpers.h" #include "qemu/qemu-print.h" diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 47063b0a57..997b21d389 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -33,6 +33,7 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #define XTENSA_MPU_SEGMENT_MASK 0x0000001f #define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00 diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index 496754ba57..028d4e0a1c 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/page-protection.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "qemu/atomic.h" From 0650fc1ea33de8db48375664ae1dd1dc7ed72662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 11:25:52 +0200 Subject: [PATCH 0463/2884] accel/tcg: Use cpu_loop_exit_requested() in cpu_loop_exec_tb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not open-code cpu_loop_exit_requested(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428214915.10339-9-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 225e5fbd3e..c18a7e2b85 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -900,8 +900,6 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, vaddr pc, TranslationBlock **last_tb, int *tb_exit) { - int32_t insns_left; - trace_exec_tb(tb, pc); tb = cpu_tb_exec(cpu, tb, tb_exit); if (*tb_exit != TB_EXIT_REQUESTED) { @@ -910,8 +908,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, } *last_tb = NULL; - insns_left = qatomic_read(&cpu->neg.icount_decr.u32); - if (insns_left < 0) { + if (cpu_loop_exit_requested(cpu)) { /* Something asked us to stop executing chained TBs; just * continue round the main loop. Whatever requested the exit * will also have set something else (eg exit_request or @@ -928,7 +925,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, /* Ensure global icount has gone forward */ icount_update(cpu); /* Refill decrementer and continue execution. */ - insns_left = MIN(0xffff, cpu->icount_budget); + int32_t insns_left = MIN(0xffff, cpu->icount_budget); cpu->neg.icount_decr.u16.low = insns_left; cpu->icount_extra = cpu->icount_budget - insns_left; From b254c342cfa4058257ded993fdb17870dcfa81b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 10 Jan 2024 18:09:56 +0100 Subject: [PATCH 0464/2884] accel/tcg: Access tcg_cflags with getter / setter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Access the CPUState::tcg_cflags via tcg_cflags_has() and tcg_cflags_set() helpers. Mechanical change using the following Coccinelle spatch script: @@ expression cpu; expression flags; @@ - cpu->tcg_cflags & flags + tcg_cflags_has(cpu, flags) @@ expression cpu; expression flags; @@ - (tcg_cflags_has(cpu, flags)) + tcg_cflags_has(cpu, flags) @@ expression cpu; expression flags; @@ - cpu->tcg_cflags |= flags; + tcg_cflags_set(cpu, flags); Then manually moving the declarations, and adding both tcg_cflags_has() and tcg_cflags_set() definitions. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240427155714.53669-15-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 10 ++++++++++ accel/tcg/internal-common.h | 3 ++- accel/tcg/tcg-accel-ops.c | 2 +- include/exec/cpu-common.h | 7 +++++++ include/exec/exec-all.h | 3 --- linux-user/mmap.c | 8 ++++---- linux-user/syscall.c | 4 ++-- target/arm/cpu.c | 2 +- target/avr/cpu.c | 2 +- target/hexagon/cpu.c | 2 +- target/hppa/cpu.c | 2 +- target/i386/cpu.c | 2 +- target/i386/helper.c | 2 +- target/loongarch/cpu.c | 2 +- target/microblaze/cpu.c | 2 +- target/mips/tcg/exception.c | 2 +- target/mips/tcg/sysemu/special_helper.c | 2 +- target/openrisc/cpu.c | 2 +- target/riscv/tcg/tcg-cpu.c | 4 ++-- target/rx/cpu.c | 2 +- target/sh4/cpu.c | 4 ++-- target/sparc/cpu.c | 2 +- target/tricore/cpu.c | 2 +- 23 files changed, 44 insertions(+), 29 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c18a7e2b85..9af66bc191 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -147,6 +147,16 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ +bool tcg_cflags_has(CPUState *cpu, uint32_t flags) +{ + return cpu->tcg_cflags & flags; +} + +void tcg_cflags_set(CPUState *cpu, uint32_t flags) +{ + cpu->tcg_cflags |= flags; +} + uint32_t curr_cflags(CPUState *cpu) { uint32_t cflags = cpu->tcg_cflags; diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index edefd0dcb7..ead53cb8a5 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -9,6 +9,7 @@ #ifndef ACCEL_TCG_INTERNAL_COMMON_H #define ACCEL_TCG_INTERNAL_COMMON_H +#include "exec/cpu-common.h" #include "exec/translation-block.h" extern int64_t max_delay; @@ -20,7 +21,7 @@ extern int64_t max_advance; */ static inline bool cpu_in_serial_context(CPUState *cs) { - return !(cs->tcg_cflags & CF_PARALLEL) || cpu_in_exclusive_context(cs); + return !tcg_cflags_has(cs, CF_PARALLEL) || cpu_in_exclusive_context(cs); } #endif diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 2c7b0cc09e..1433e38f40 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -62,7 +62,7 @@ void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) cflags |= parallel ? CF_PARALLEL : 0; cflags |= icount_enabled() ? CF_USE_ICOUNT : 0; - cpu->tcg_cflags |= cflags; + tcg_cflags_set(cpu, cflags); } void tcg_cpu_destroy(CPUState *cpu) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 78f2c381b1..8bc397e251 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -178,6 +178,13 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, void list_cpus(void); #ifdef CONFIG_TCG + +bool tcg_cflags_has(CPUState *cpu, uint32_t flags); +void tcg_cflags_set(CPUState *cpu, uint32_t flags); + +/* current cflags for hashing/comparison */ +uint32_t curr_cflags(CPUState *cpu); + /** * cpu_unwind_state_data: * @cpu: the cpu context diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 4c5e470581..2cd7b8f61b 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -510,9 +510,6 @@ static inline void tb_set_page_addr1(TranslationBlock *tb, #endif } -/* current cflags for hashing/comparison */ -uint32_t curr_cflags(CPUState *cpu); - /* TranslationBlock invalidate API */ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last); diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 72b30279a2..4d09a72fad 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -960,8 +960,8 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, */ if (ret != -1 && (flags & MAP_TYPE) != MAP_PRIVATE) { CPUState *cpu = thread_cpu; - if (!(cpu->tcg_cflags & CF_PARALLEL)) { - cpu->tcg_cflags |= CF_PARALLEL; + if (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, CF_PARALLEL); tb_flush(cpu); } } @@ -1400,8 +1400,8 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, * supported by the host -- anything that requires EXCP_ATOMIC will not * be atomic with respect to an external process. */ - if (!(cpu->tcg_cflags & CF_PARALLEL)) { - cpu->tcg_cflags |= CF_PARALLEL; + if (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, CF_PARALLEL); tb_flush(cpu); } diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6a492c9d35..1b42e80f9a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6583,8 +6583,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, * generate code for parallel execution and flush old translations. * Do this now so that the copy gets CF_PARALLEL too. */ - if (!(cpu->tcg_cflags & CF_PARALLEL)) { - cpu->tcg_cflags |= CF_PARALLEL; + if (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, CF_PARALLEL); tb_flush(cpu); } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index fdc3eda318..77f8c9c748 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1941,7 +1941,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) /* Use pc-relative instructions in system-mode */ - cs->tcg_cflags |= CF_PCREL; + tcg_cflags_set(cs, CF_PCREL); #endif /* If we needed to query the host kernel for the CPU features diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 71ce62a4c2..f53e1192b1 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -55,7 +55,7 @@ static int avr_cpu_mmu_index(CPUState *cs, bool ifetch) static void avr_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu_env(cs)->pc_w = tb->pc / 2; /* internally PC points to words */ } diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index a56bb4b075..64cc05cca7 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -257,7 +257,7 @@ static vaddr hexagon_cpu_get_pc(CPUState *cs) static void hexagon_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 3831cb6db2..393a81988d 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -48,7 +48,7 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, { HPPACPU *cpu = HPPA_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); #ifdef CONFIG_USER_ONLY cpu->env.iaoq_f = tb->pc; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index aa3b2d8391..25c0702ca1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7371,7 +7371,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) /* Use pc-relative instructions in system-mode */ - cs->tcg_cflags |= CF_PCREL; + tcg_cflags_set(cs, CF_PCREL); #endif if (cpu->apic_id == UNASSIGNED_APIC_ID) { diff --git a/target/i386/helper.c b/target/i386/helper.c index 23ccb23a5b..48d1513a35 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -523,7 +523,7 @@ static inline target_ulong get_memio_eip(CPUX86State *env) } /* Per x86_restore_state_to_opc. */ - if (cs->tcg_cflags & CF_PCREL) { + if (tcg_cflags_has(cs, CF_PCREL)) { return (env->eip & TARGET_PAGE_MASK) | data[0]; } else { return data[0] - env->segs[R_CS].base; diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 1ebba043f4..96da1a685e 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -336,7 +336,7 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) static void loongarch_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); set_pc(cpu_env(cs), tb->pc); } diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 9eb7374ccd..41ad47d04c 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -99,7 +99,7 @@ static void mb_cpu_synchronize_from_tb(CPUState *cs, { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.iflags = tb->flags & IFLAGS_TB_MASK; } diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index 13275d1ded..4886d087b2 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -81,7 +81,7 @@ void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { CPUMIPSState *env = cpu_env(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); env->active_tc.PC = tb->pc; env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags |= tb->flags & MIPS_HFLAG_BMASK; diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/sysemu/special_helper.c index 5baa25348e..9ce5e2ceac 100644 --- a/target/mips/tcg/sysemu/special_helper.c +++ b/target/mips/tcg/sysemu/special_helper.c @@ -93,7 +93,7 @@ bool mips_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb) CPUMIPSState *env = cpu_env(cs); if ((env->hflags & MIPS_HFLAG_BMASK) != 0 - && !(cs->tcg_cflags & CF_PCREL) && env->active_tc.PC != tb->pc) { + && !tcg_cflags_has(cs, CF_PCREL) && env->active_tc.PC != tb->pc) { env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); env->hflags &= ~MIPS_HFLAG_BMASK; return true; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index d711035cf5..fdaaa09fc8 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -45,7 +45,7 @@ static void openrisc_cpu_synchronize_from_tb(CPUState *cs, { OpenRISCCPU *cpu = OPENRISC_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index b5b95e052d..40054a391a 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -96,7 +96,7 @@ static void riscv_cpu_synchronize_from_tb(CPUState *cs, CPURISCVState *env = &cpu->env; RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); if (xl == MXL_RV32) { env->pc = (int32_t) tb->pc; @@ -890,7 +890,7 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) CPURISCVState *env = &cpu->env; Error *local_err = NULL; - CPU(cs)->tcg_cflags |= CF_PCREL; + tcg_cflags_set(CPU(cs), CF_PCREL); if (cpu->cfg.ext_sstc) { riscv_timer_init(cpu); diff --git a/target/rx/cpu.c b/target/rx/cpu.c index c1a592e893..8a584f0a11 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -46,7 +46,7 @@ static void rx_cpu_synchronize_from_tb(CPUState *cs, { RXCPU *cpu = RX_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 43e35ec2ca..618aa7154e 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -47,7 +47,7 @@ static void superh_cpu_synchronize_from_tb(CPUState *cs, { SuperHCPU *cpu = SUPERH_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK; } @@ -74,7 +74,7 @@ static bool superh_io_recompile_replay_branch(CPUState *cs, CPUSH4State *env = cpu_env(cs); if ((env->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND)) - && !(cs->tcg_cflags & CF_PCREL) && env->pc != tb->pc) { + && !tcg_cflags_has(cs, CF_PCREL) && env->pc != tb->pc) { env->pc -= 2; env->flags &= ~(TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND); return true; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 485d416925..685485c654 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -702,7 +702,7 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs, { SPARCCPU *cpu = SPARC_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.npc = tb->cs_base; } diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 8f9b72c3a0..bdefb84511 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -47,7 +47,7 @@ static vaddr tricore_cpu_get_pc(CPUState *cs) static void tricore_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu_env(cs)->PC = tb->pc; } From 40ab89f37498ae28b06e491d0d6fa3ecbf494363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Apr 2024 11:09:52 +0200 Subject: [PATCH 0465/2884] accel/tcg: Move user definition of cpu_interrupt() to user-exec.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-4-philmd@linaro.org> --- accel/tcg/translate-all.c | 9 --------- accel/tcg/user-exec.c | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 83cc14fbde..fdf6d8ac19 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -644,15 +644,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cpu_loop_exit_noexc(cpu); } -#else /* CONFIG_USER_ONLY */ - -void cpu_interrupt(CPUState *cpu, int mask) -{ - g_assert(bql_locked()); - cpu->interrupt_request |= mask; - qatomic_set(&cpu->neg.icount_decr.u16.high, -1); -} - #endif /* CONFIG_USER_ONLY */ /* diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index d34313a612..80d24540ed 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -24,6 +24,7 @@ #include "qemu/bitops.h" #include "qemu/rcu.h" #include "exec/cpu_ldst.h" +#include "qemu/main-loop.h" #include "exec/translate-all.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" @@ -38,6 +39,13 @@ __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL +void cpu_interrupt(CPUState *cpu, int mask) +{ + g_assert(bql_locked()); + cpu->interrupt_request |= mask; + qatomic_set(&cpu->neg.icount_decr.u16.high, -1); +} + /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ From b3e7bdeb78825b2aa050e2db7f122534a49d85e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 28 Apr 2024 22:23:19 +0200 Subject: [PATCH 0466/2884] accel/tcg: Update CPUNegativeOffsetState::can_do_io field documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The @can_do_io field got moved from CPUState to CPUNegativeOffsetState in commit 464dacf609 ("accel/tcg: Move can_do_io to CPUNegativeOffsetState"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-14-philmd@linaro.org> --- include/hw/core/cpu.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 46b99a7ea5..173349b0bd 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -338,9 +338,10 @@ typedef union IcountDecr { } u16; } IcountDecr; -/* - * Elements of CPUState most efficiently accessed from CPUArchState, - * via small negative offsets. +/** + * CPUNegativeOffsetState: Elements of CPUState most efficiently accessed + * from CPUArchState, via small negative offsets. + * @can_do_io: True if memory-mapped IO is allowed. */ typedef struct CPUNegativeOffsetState { CPUTLB tlb; @@ -400,7 +401,6 @@ struct qemu_work_item; * @crash_occurred: Indicates the OS reported a crash (panic) for this CPU * @singlestep_enabled: Flags for single-stepping. * @icount_extra: Instructions until next timer event. - * @neg.can_do_io: True if memory-mapped IO is allowed. * @cpu_ases: Pointer to array of CPUAddressSpaces (which define the * AddressSpaces this CPU has) * @num_ases: number of CPUAddressSpaces in @cpu_ases From 57d828429e5f1d849bf808387d947d7a62f0322e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Apr 2024 23:12:39 +0200 Subject: [PATCH 0467/2884] accel/tcg: Restrict qemu_plugin_vcpu_exit_hook() to TCG plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_plugin_vcpu_exit_hook() is specific to TCG plugins, so must be restricted to it in cpu_common_unrealizefn(), similarly to how qemu_plugin_create_vcpu_state() is restricted in the cpu_common_realizefn() counterpart. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240429213050.55177-2-philmd@linaro.org> --- hw/core/cpu-common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index a72d48d9e1..0f0a247f56 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -30,7 +30,9 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "trace.h" +#ifdef CONFIG_PLUGIN #include "qemu/plugin.h" +#endif CPUState *cpu_by_arch_id(int64_t id) { @@ -236,9 +238,11 @@ static void cpu_common_unrealizefn(DeviceState *dev) CPUState *cpu = CPU(dev); /* Call the plugin hook before clearing the cpu is fully unrealized */ +#ifdef CONFIG_PLUGIN if (tcg_enabled()) { qemu_plugin_vcpu_exit_hook(cpu); } +#endif /* NOTE: latest generic point before the cpu is fully unrealized */ cpu_exec_unrealizefn(cpu); From fc44d592db69547ca2fc1ec9ee41e6ea81734400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 29 Apr 2024 16:01:18 +0200 Subject: [PATCH 0468/2884] accel/tcg: Restrict cpu_plugin_mem_cbs_enabled() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far cpu_plugin_mem_cbs_enabled() is only called from TCG, so reduce it to accel/tcg/. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <5f59c754-44e5-4743-a2dd-87ef8e13eadf@linaro.org> --- accel/tcg/internal-common.h | 17 +++++++++++++++++ include/hw/core/cpu.h | 17 ----------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index ead53cb8a5..cbeff39e3e 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -24,4 +24,21 @@ static inline bool cpu_in_serial_context(CPUState *cs) return !tcg_cflags_has(cs, CF_PARALLEL) || cpu_in_exclusive_context(cs); } +/** + * cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled? + * @cs: CPUState pointer + * + * The memory callbacks are installed if a plugin has instrumented an + * instruction for memory. This can be useful to know if you want to + * force a slow path for a series of memory accesses. + */ +static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) +{ +#ifdef CONFIG_PLUGIN + return !!cpu->plugin_mem_cbs; +#else + return false; +#endif +} + #endif diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 173349b0bd..a001bafcf8 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1111,23 +1111,6 @@ void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint); void cpu_watchpoint_remove_all(CPUState *cpu, int mask); #endif -/** - * cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled? - * @cs: CPUState pointer - * - * The memory callbacks are installed if a plugin has instrumented an - * instruction for memory. This can be useful to know if you want to - * force a slow path for a series of memory accesses. - */ -static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) -{ -#ifdef CONFIG_PLUGIN - return !!cpu->plugin_mem_cbs; -#else - return false; -#endif -} - /** * cpu_get_address_space: * @cpu: CPU to get address space from From 80f034c5b2040b3cfea978361dfd7d813e3c75d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 9 Jan 2024 23:38:04 +0100 Subject: [PATCH 0469/2884] accel/tcg: Move @plugin_mem_cbs from CPUState to CPUNegativeOffsetState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @plugin_mem_cbs is accessed by tcg generated code, move it to CPUNegativeOffsetState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240429213050.55177-4-philmd@linaro.org> --- accel/tcg/internal-common.h | 2 +- accel/tcg/plugin-gen.c | 6 +++--- include/hw/core/cpu.h | 13 +++++++------ include/qemu/plugin.h | 2 +- plugins/core.c | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index cbeff39e3e..cff43d221b 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -35,7 +35,7 @@ static inline bool cpu_in_serial_context(CPUState *cs) static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) { #ifdef CONFIG_PLUGIN - return !!cpu->plugin_mem_cbs; + return !!cpu->neg.plugin_mem_cbs; #else return false; #endif diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 3db74ae9bf..49f5d1c2e4 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -55,7 +55,7 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, * Tracking memory accesses performed from helpers requires extra work. * If an instruction is emulated with helpers, we do two things: * (1) copy the CB descriptors, and keep track of it so that they can be - * freed later on, and (2) point CPUState.plugin_mem_cbs to the + * freed later on, and (2) point CPUState.neg.plugin_mem_cbs to the * descriptors, so that we can read them at run-time * (i.e. when the helper executes). * This run-time access is performed from qemu_plugin_vcpu_mem_cb. @@ -90,14 +90,14 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, qemu_plugin_add_dyn_cb_arr(arr); tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, - offsetof(CPUState, plugin_mem_cbs) - + offsetof(CPUState, neg.plugin_mem_cbs) - offsetof(ArchCPU, env)); } static void gen_disable_mem_helper(void) { tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, - offsetof(CPUState, plugin_mem_cbs) - + offsetof(CPUState, neg.plugin_mem_cbs) - offsetof(ArchCPU, env)); } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index a001bafcf8..6efd7353be 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -342,9 +342,16 @@ typedef union IcountDecr { * CPUNegativeOffsetState: Elements of CPUState most efficiently accessed * from CPUArchState, via small negative offsets. * @can_do_io: True if memory-mapped IO is allowed. + * @plugin_mem_cbs: active plugin memory callbacks */ typedef struct CPUNegativeOffsetState { CPUTLB tlb; +#ifdef CONFIG_PLUGIN + /* + * The callback pointer are accessed via TCG (see gen_empty_mem_helper). + */ + GArray *plugin_mem_cbs; +#endif IcountDecr icount_decr; bool can_do_io; } CPUNegativeOffsetState; @@ -416,7 +423,6 @@ struct qemu_work_item; * @kvm_fd: vCPU file descriptor for KVM. * @work_mutex: Lock to prevent multiple access to @work_list. * @work_list: List of pending asynchronous work. - * @plugin_mem_cbs: active plugin memory callbacks * @plugin_state: per-CPU plugin state * @ignore_memory_transaction_failures: Cached copy of the MachineState * flag of the same name: allows the board to suppress calling of the @@ -511,11 +517,6 @@ struct CPUState { QemuLockCnt in_ioctl_lock; #ifdef CONFIG_PLUGIN - /* - * The callback pointer stays in the main CPUState as it is - * accessed via TCG (see gen_empty_mem_helper). - */ - GArray *plugin_mem_cbs; CPUPluginState *plugin_state; #endif diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 18062528c1..b535bfd5de 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -180,7 +180,7 @@ void qemu_plugin_add_dyn_cb_arr(GArray *arr); static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu) { - cpu->plugin_mem_cbs = NULL; + cpu->neg.plugin_mem_cbs = NULL; } /** diff --git a/plugins/core.c b/plugins/core.c index 081323dafc..1e58a57bf1 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -533,7 +533,7 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, MemOpIdx oi, enum qemu_plugin_mem_rw rw) { - GArray *arr = cpu->plugin_mem_cbs; + GArray *arr = cpu->neg.plugin_mem_cbs; size_t i; if (arr == NULL) { From 1d067e3953e76af28ba20c995b176fcbcb7a10aa Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 20:32:41 +0100 Subject: [PATCH 0470/2884] qapi: New QAPISchemaBranches, QAPISchemaAlternatives QAPISchemaVariants represents either a union type's branches, or an alternate type's alternatives. Much of its code is conditional on which one it actually is. Create QAPISchemaBranches for branches, and QAPISchemaAlternatives for alternatives, both subtypes of QAPISchemaVariants. Replace QAPISchemaVariants by one of them where possible. Keep it only where we actually deal with either of them. QAPISchemaVariants.__init__() takes @tag_name and @tag_member, where exactly one must be None: @tag_name for alternatives, @tag_member for branches. Let QAPISchemaBranches.__init__() take just @tag_name, and QAPISchemaAlternatives.__init__() take just @tag_member. A later patch will move the conditional code to the subtypes. Signed-off-by: Markus Armbruster --- scripts/qapi/introspect.py | 7 ++++--- scripts/qapi/schema.py | 32 ++++++++++++++++++++++++-------- scripts/qapi/types.py | 6 ++++-- scripts/qapi/visit.py | 11 ++++++----- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 4679b1bc2c..b866517942 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -26,6 +26,8 @@ from .common import c_name, mcgen from .gen import QAPISchemaMonolithicCVisitor from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaArrayType, QAPISchemaBuiltinType, QAPISchemaEntity, @@ -36,7 +38,6 @@ from .schema import ( QAPISchemaObjectTypeMember, QAPISchemaType, QAPISchemaVariant, - QAPISchemaVariants, ) from .source import QAPISourceInfo @@ -335,7 +336,7 @@ const QLitObject %(c_name)s = %(c_string)s; ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> None: + variants: Optional[QAPISchemaBranches]) -> None: obj: SchemaInfoObject = { 'members': [self._gen_object_member(m) for m in members] } @@ -347,7 +348,7 @@ const QLitObject %(c_name)s = %(c_string)s; def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants) -> None: + variants: QAPISchemaAlternatives) -> None: self._gen_tree( name, 'alternate', {'members': [Annotated({'type': self._use_type(m.type)}, diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 5924947fc3..5cdedfc2c8 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -215,7 +215,7 @@ class QAPISchemaVisitor: features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants], + variants: Optional[QAPISchemaBranches], ) -> None: pass @@ -226,7 +226,7 @@ class QAPISchemaVisitor: ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants], + variants: Optional[QAPISchemaBranches], ) -> None: pass @@ -236,7 +236,7 @@ class QAPISchemaVisitor: info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants, + variants: QAPISchemaAlternatives, ) -> None: pass @@ -524,7 +524,7 @@ class QAPISchemaObjectType(QAPISchemaType): features: Optional[List[QAPISchemaFeature]], base: Optional[str], local_members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants], + variants: Optional[QAPISchemaBranches], ): # struct has local_members, optional base, and no variants # union has base, variants, and no local_members @@ -651,7 +651,7 @@ class QAPISchemaAlternateType(QAPISchemaType): doc: Optional[QAPIDoc], ifcond: Optional[QAPISchemaIfCond], features: List[QAPISchemaFeature], - variants: QAPISchemaVariants, + variants: QAPISchemaAlternatives, ): super().__init__(name, info, doc, ifcond, features) assert variants.tag_member @@ -833,6 +833,22 @@ class QAPISchemaVariants: v.type.check_clash(info, dict(seen)) +class QAPISchemaBranches(QAPISchemaVariants): + def __init__(self, + info: QAPISourceInfo, + variants: List[QAPISchemaVariant], + tag_name: str): + super().__init__(tag_name, info, None, variants) + + +class QAPISchemaAlternatives(QAPISchemaVariants): + def __init__(self, + info: QAPISourceInfo, + variants: List[QAPISchemaVariant], + tag_member: QAPISchemaObjectTypeMember): + super().__init__(None, info, tag_member, variants) + + class QAPISchemaMember: """ Represents object members, enum members and features """ role = 'member' @@ -1388,8 +1404,8 @@ class QAPISchema: self._def_definition( QAPISchemaObjectType(name, info, expr.doc, ifcond, features, base, members, - QAPISchemaVariants( - tag_name, info, None, variants))) + QAPISchemaBranches( + info, variants, tag_name))) def _def_alternate_type(self, expr: QAPIExpression) -> None: name = expr['alternate'] @@ -1407,7 +1423,7 @@ class QAPISchema: self._def_definition( QAPISchemaAlternateType( name, info, expr.doc, ifcond, features, - QAPISchemaVariants(None, info, tag_member, variants))) + QAPISchemaAlternatives(info, variants, tag_member))) def _def_command(self, expr: QAPIExpression) -> None: name = expr['command'] diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index c39d054d2c..23cdf3e83e 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -23,6 +23,8 @@ from .gen import ( ) from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaEnumMember, QAPISchemaFeature, QAPISchemaIfCond, @@ -348,7 +350,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> None: + variants: Optional[QAPISchemaBranches]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -369,7 +371,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants) -> None: + variants: QAPISchemaAlternatives) -> None: with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index a21b7b1468..990685498f 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -28,6 +28,8 @@ from .gen import ( ) from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaEnumMember, QAPISchemaEnumType, QAPISchemaFeature, @@ -35,7 +37,6 @@ from .schema import ( QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, - QAPISchemaVariants, ) from .source import QAPISourceInfo @@ -63,7 +64,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); def gen_visit_object_members(name: str, base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> str: + variants: Optional[QAPISchemaBranches]) -> str: ret = mcgen(''' bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) @@ -222,7 +223,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, c_name=c_name(name)) -def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str: +def gen_visit_alternate(name: str, variants: QAPISchemaAlternatives) -> str: ret = mcgen(''' bool visit_type_%(c_name)s(Visitor *v, const char *name, @@ -393,7 +394,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> None: + variants: Optional[QAPISchemaBranches]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -413,7 +414,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants) -> None: + variants: QAPISchemaAlternatives) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_alternate(name, variants)) From d1da8af897340ed3773c09add93c3b9f494f2c2b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 16:28:22 +0100 Subject: [PATCH 0471/2884] qapi: Rename visitor parameter @variants to @branches The previous commit narrowed the type of .visit_object_type() parameter @variants from QAPISchemaVariants to QAPISchemaBranches. Rename it to @branches. Same for .visit_object_type_flat(). A few of these pass @branches to helper functions: QAPISchemaGenRSTVisitor.visit_object_type() to ._nodes_for_members() and ._nodes_for_variant_when(), and QAPISchemaGenVisitVisitor.visit_object_type() to gen_visit_object_members(). Rename the helpers' @variants parameters to @branches as well. Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 18 +++++++++--------- scripts/qapi/introspect.py | 8 ++++---- scripts/qapi/schema.py | 4 ++-- scripts/qapi/types.py | 4 ++-- scripts/qapi/visit.py | 12 ++++++------ tests/qapi-schema/test-qapi.py | 4 ++-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 8d428c64b0..71362ba929 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -145,22 +145,22 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): term.extend(self._nodes_for_ifcond(member.ifcond)) return term - def _nodes_for_variant_when(self, variants, variant): + def _nodes_for_variant_when(self, branches, variant): """Return list of Text, literal nodes for variant 'when' clause Return a list of doctree nodes which give text like 'when tagname is variant (If: ...)' suitable for use in - the 'variants' part of a definition list. + the 'branches' part of a definition list. """ term = [nodes.Text(' when '), - nodes.literal('', variants.tag_member.name), + nodes.literal('', branches.tag_member.name), nodes.Text(' is '), nodes.literal('', '"%s"' % variant.name)] if variant.ifcond.is_present(): term.extend(self._nodes_for_ifcond(variant.ifcond)) return term - def _nodes_for_members(self, doc, what, base=None, variants=None): + def _nodes_for_members(self, doc, what, base=None, branches=None): """Return list of doctree nodes for the table of members""" dlnode = nodes.definition_list() for section in doc.args.values(): @@ -178,14 +178,14 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): nodes.literal('', base.doc_type())], None) - if variants: - for v in variants.variants: + if branches: + for v in branches.variants: if v.type.name == 'q_empty': continue assert not v.type.is_implicit() term = [nodes.Text('The members of '), nodes.literal('', v.type.doc_type())] - term.extend(self._nodes_for_variant_when(variants, v)) + term.extend(self._nodes_for_variant_when(branches, v)) dlnode += self._make_dlitem(term, None) if not dlnode.children: @@ -308,12 +308,12 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): + self._nodes_for_if_section(ifcond)) def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + base, members, branches): doc = self._cur_doc if base and base.is_implicit(): base = None self._add_doc('Object', - self._nodes_for_members(doc, 'Members', base, variants) + self._nodes_for_members(doc, 'Members', base, branches) + self._nodes_for_features(doc) + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index b866517942..7852591490 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -336,13 +336,13 @@ const QLitObject %(c_name)s = %(c_string)s; ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches]) -> None: + branches: Optional[QAPISchemaBranches]) -> None: obj: SchemaInfoObject = { 'members': [self._gen_object_member(m) for m in members] } - if variants: - obj['tag'] = variants.tag_member.name - obj['variants'] = [self._gen_variant(v) for v in variants.variants] + if branches: + obj['tag'] = branches.tag_member.name + obj['variants'] = [self._gen_variant(v) for v in branches.variants] self._gen_tree(name, 'object', obj, ifcond, features) def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 5cdedfc2c8..65c82dd4f1 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -215,7 +215,7 @@ class QAPISchemaVisitor: features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches], + branches: Optional[QAPISchemaBranches], ) -> None: pass @@ -226,7 +226,7 @@ class QAPISchemaVisitor: ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches], + branches: Optional[QAPISchemaBranches], ) -> None: pass diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 23cdf3e83e..0abb78f3a8 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -350,13 +350,13 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches]) -> None: + branches: Optional[QAPISchemaBranches]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, ifcond, base, members, variants)) + self._genh.add(gen_object(name, ifcond, base, members, branches)) with ifcontext(ifcond, self._genh, self._genc): if base and not base.is_implicit(): self._genh.add(gen_upcast(name, base)) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 990685498f..fbae5f3e4e 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -64,7 +64,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); def gen_visit_object_members(name: str, base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches]) -> str: + branches: Optional[QAPISchemaBranches]) -> str: ret = mcgen(''' bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) @@ -132,8 +132,8 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''') ret += memb.ifcond.gen_endif() - if variants: - tag_member = variants.tag_member + if branches: + tag_member = branches.tag_member assert isinstance(tag_member.type, QAPISchemaEnumType) ret += mcgen(''' @@ -141,7 +141,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', c_name=c_name(tag_member.name)) - for var in variants.variants: + for var in branches.variants: case_str = c_enum_const(tag_member.type.name, var.name, tag_member.type.prefix) ret += var.ifcond.gen_if() @@ -394,14 +394,14 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches]) -> None: + branches: Optional[QAPISchemaBranches]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_members_decl(name)) self._genc.add(gen_visit_object_members(name, base, - members, variants)) + members, branches)) # TODO Worth changing the visitor signature, so we could # directly use rather than repeat type.is_implicit()? if not name.startswith('q_'): diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 40095431ae..7c67ad8d9b 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -48,7 +48,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + base, members, branches): print('object %s' % name) if base: print(' base %s' % base.name) @@ -57,7 +57,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): % (m.name, m.type.name, m.optional)) self._print_if(m.ifcond, 8) self._print_features(m.features, indent=8) - self._print_variants(variants) + self._print_variants(branches) self._print_if(ifcond) self._print_features(features) From 41d0ad1d045a1af51200ff5f2a1309e4aada0a96 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 16 Mar 2024 07:43:36 +0100 Subject: [PATCH 0472/2884] qapi: Rename visitor parameter @variants to @alternatives A previous commit narrowed the type of .visit_alternate_type() parameter @variants from QAPISchemaVariants to QAPISchemaAlternatives. Rename it to @alternatives. One of them passes @alternatives to helper function gen_visit_alternate(). Rename its @variants parameter to @alternatives as well. Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 3 ++- scripts/qapi/introspect.py | 4 ++-- scripts/qapi/schema.py | 2 +- scripts/qapi/types.py | 4 ++-- scripts/qapi/visit.py | 9 +++++---- tests/qapi-schema/test-qapi.py | 5 +++-- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 71362ba929..f270b494f0 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -318,7 +318,8 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type(self, name, info, ifcond, features, + alternatives): doc = self._cur_doc self._add_doc('Alternate', self._nodes_for_members(doc, 'Members') diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 7852591490..86c075a6ad 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -348,12 +348,12 @@ const QLitObject %(c_name)s = %(c_string)s; def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaAlternatives) -> None: + alternatives: QAPISchemaAlternatives) -> None: self._gen_tree( name, 'alternate', {'members': [Annotated({'type': self._use_type(m.type)}, m.ifcond) - for m in variants.variants]}, + for m in alternatives.variants]}, ifcond, features ) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 65c82dd4f1..2b67992aee 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -236,7 +236,7 @@ class QAPISchemaVisitor: info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaAlternatives, + alternatives: QAPISchemaAlternatives, ) -> None: pass diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 0abb78f3a8..69f5f6ffd0 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -371,11 +371,11 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaAlternatives) -> None: + alternatives: QAPISchemaAlternatives) -> None: with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, - [variants.tag_member], variants)) + [alternatives.tag_member], alternatives)) with ifcontext(ifcond, self._genh, self._genc): self._gen_type_cleanup(name) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index fbae5f3e4e..e766acaac9 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -223,7 +223,8 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, c_name=c_name(name)) -def gen_visit_alternate(name: str, variants: QAPISchemaAlternatives) -> str: +def gen_visit_alternate(name: str, + alternatives: QAPISchemaAlternatives) -> str: ret = mcgen(''' bool visit_type_%(c_name)s(Visitor *v, const char *name, @@ -245,7 +246,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, ''', c_name=c_name(name)) - for var in variants.variants: + for var in alternatives.variants: ret += var.ifcond.gen_if() ret += mcgen(''' case %(case)s: @@ -414,10 +415,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaAlternatives) -> None: + alternatives: QAPISchemaAlternatives) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_alternate(name, variants)) + self._genc.add(gen_visit_alternate(name, alternatives)) def gen_visit(schema: QAPISchema, diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 7c67ad8d9b..7e3f9f4aa1 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -61,9 +61,10 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) self._print_features(features) - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type(self, name, info, ifcond, features, + alternatives): print('alternate %s' % name) - self._print_variants(variants) + self._print_variants(alternatives) self._print_if(ifcond) self._print_features(features) From 3ff2a5a35c387a4deb86101474c7e181b36e82f2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 16:33:23 +0100 Subject: [PATCH 0473/2884] qapi: Rename QAPISchemaObjectType.variants to .branches A previous commit narrowed the type of QAPISchemaObjectType.variants from QAPISchemaVariants to QAPISchemaBranches. Rename it to .branches. Same for .__init__() parameter @variants. Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 2 +- scripts/qapi/events.py | 2 +- scripts/qapi/gen.py | 2 +- scripts/qapi/schema.py | 36 ++++++++++++++++++------------------ scripts/qapi/types.py | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index d1fdf4182c..79951a841f 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -64,7 +64,7 @@ def gen_call(name: str, assert arg_type argstr = '&arg, ' elif arg_type: - assert not arg_type.variants + assert not arg_type.branches for memb in arg_type.members: assert not memb.ifcond.is_present() if memb.need_has(): diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 3cf01e96b6..d1f639981a 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -51,7 +51,7 @@ def gen_param_var(typ: QAPISchemaObjectType) -> str: Initialize it with the function arguments defined in `gen_event_send`. """ - assert not typ.variants + assert not typ.branches ret = mcgen(''' %(c_name)s param = { ''', diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 5412716617..6a8abe0041 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -118,7 +118,7 @@ def build_params(arg_type: Optional[QAPISchemaObjectType], ret += '%s arg' % arg_type.c_param_type() sep = ', ' elif arg_type: - assert not arg_type.variants + assert not arg_type.branches for memb in arg_type.members: assert not memb.ifcond.is_present() ret += sep diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 2b67992aee..c9ff794d0c 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -524,20 +524,20 @@ class QAPISchemaObjectType(QAPISchemaType): features: Optional[List[QAPISchemaFeature]], base: Optional[str], local_members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaBranches], + branches: Optional[QAPISchemaBranches], ): - # struct has local_members, optional base, and no variants - # union has base, variants, and no local_members + # struct has local_members, optional base, and no branches + # union has base, branches, and no local_members super().__init__(name, info, doc, ifcond, features) - self.meta = 'union' if variants else 'struct' + self.meta = 'union' if branches else 'struct' for m in local_members: m.set_defined_in(name) - if variants is not None: - variants.set_defined_in(name) + if branches is not None: + branches.set_defined_in(name) self._base_name = base self.base = None self.local_members = local_members - self.variants = variants + self.branches = branches self.members: List[QAPISchemaObjectTypeMember] self._check_complete = False @@ -561,7 +561,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.base = schema.resolve_type(self._base_name, self.info, "'base'") if (not isinstance(self.base, QAPISchemaObjectType) - or self.base.variants): + or self.base.branches): raise QAPISemError( self.info, "'base' requires a struct type, %s isn't" @@ -577,9 +577,9 @@ class QAPISchemaObjectType(QAPISchemaType): # Cast down to the subtype. members = cast(List[QAPISchemaObjectTypeMember], list(seen.values())) - if self.variants: - self.variants.check(schema, seen) - self.variants.check_clash(self.info, seen) + if self.branches: + self.branches.check(schema, seen) + self.branches.check_clash(self.info, seen) self.members = members self._check_complete = True # mark completed @@ -595,8 +595,8 @@ class QAPISchemaObjectType(QAPISchemaType): assert self._checked for m in self.members: m.check_clash(info, seen) - if self.variants: - self.variants.check_clash(info, seen) + if self.branches: + self.branches.check_clash(info, seen) def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) @@ -612,7 +612,7 @@ class QAPISchemaObjectType(QAPISchemaType): return self.name.startswith('q_') def is_empty(self) -> bool: - return not self.members and not self.variants + return not self.members and not self.branches def has_conditional_members(self) -> bool: return any(m.ifcond.is_present() for m in self.members) @@ -635,10 +635,10 @@ class QAPISchemaObjectType(QAPISchemaType): super().visit(visitor) visitor.visit_object_type( self.name, self.info, self.ifcond, self.features, - self.base, self.local_members, self.variants) + self.base, self.local_members, self.branches) visitor.visit_object_type_flat( self.name, self.info, self.ifcond, self.features, - self.members, self.variants) + self.members, self.branches) class QAPISchemaAlternateType(QAPISchemaType): @@ -1035,7 +1035,7 @@ class QAPISchemaCommand(QAPISchemaDefinition): "command's 'data' cannot take %s" % arg_type.describe()) self.arg_type = arg_type - if self.arg_type.variants and not self.boxed: + if self.arg_type.branches and not self.boxed: raise QAPISemError( self.info, "command's 'data' can take %s only with 'boxed': true" @@ -1103,7 +1103,7 @@ class QAPISchemaEvent(QAPISchemaDefinition): "event's 'data' cannot take %s" % typ.describe()) self.arg_type = typ - if self.arg_type.variants and not self.boxed: + if self.arg_type.branches and not self.boxed: raise QAPISemError( self.info, "event's 'data' can take %s only with 'boxed': true" diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 69f5f6ffd0..0dd0b00ada 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -171,7 +171,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond, if not isinstance(obj, QAPISchemaObjectType): continue ret += gen_object(obj.name, obj.ifcond, obj.base, - obj.local_members, obj.variants) + obj.local_members, obj.branches) ret += mcgen(''' From e0a28f39b4602de56d3c0f66a386ededd25ea109 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 16:36:02 +0100 Subject: [PATCH 0474/2884] qapi: Rename QAPISchemaAlternateType.variants to .alternatives A previous commit narrowed the type of QAPISchemaAlternateType.variants from QAPISchemaVariants to QAPISchemaAlternatives. Rename it to .alternatives. Same for .__init__() parameter @variants. Signed-off-by: Markus Armbruster --- scripts/qapi/schema.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index c9ff794d0c..9bdbfd52b2 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -651,25 +651,25 @@ class QAPISchemaAlternateType(QAPISchemaType): doc: Optional[QAPIDoc], ifcond: Optional[QAPISchemaIfCond], features: List[QAPISchemaFeature], - variants: QAPISchemaAlternatives, + alternatives: QAPISchemaAlternatives, ): super().__init__(name, info, doc, ifcond, features) - assert variants.tag_member - variants.set_defined_in(name) - variants.tag_member.set_defined_in(self.name) - self.variants = variants + assert alternatives.tag_member + alternatives.set_defined_in(name) + alternatives.tag_member.set_defined_in(self.name) + self.alternatives = alternatives def check(self, schema: QAPISchema) -> None: super().check(schema) - self.variants.tag_member.check(schema) - # Not calling self.variants.check_clash(), because there's nothing - # to clash with - self.variants.check(schema, {}) + self.alternatives.tag_member.check(schema) + # Not calling self.alternatives.check_clash(), because there's + # nothing to clash with + self.alternatives.check(schema, {}) # Alternate branch names have no relation to the tag enum values; # so we have to check for potential name collisions ourselves. seen: Dict[str, QAPISchemaMember] = {} types_seen: Dict[str, str] = {} - for v in self.variants.variants: + for v in self.alternatives.variants: v.check_clash(self.info, seen) qtype = v.type.alternate_qtype() if not qtype: @@ -700,7 +700,7 @@ class QAPISchemaAlternateType(QAPISchemaType): def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc - for v in self.variants.variants: + for v in self.alternatives.variants: v.connect_doc(doc) def c_type(self) -> str: @@ -712,7 +712,8 @@ class QAPISchemaAlternateType(QAPISchemaType): def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_alternate_type( - self.name, self.info, self.ifcond, self.features, self.variants) + self.name, self.info, self.ifcond, self.features, + self.alternatives) class QAPISchemaVariants: From 8152bc7de6d4377d5104078115aa61986b436f44 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 15 Mar 2024 20:57:56 +0100 Subject: [PATCH 0475/2884] qapi: Move conditional code from QAPISchemaVariants to its subtypes QAPISchemaVariants.check()'s code is almost entirely conditional on union vs. alternate type. Move the conditional code to QAPISchemaBranches.check() and QAPISchemaAlternatives.check(), where the conditions are always satisfied. Attribute QAPISchemaVariants.tag_name is now only used by QAPISchemaBranches. Move it there. Refactor the three types' .__init__() to make them a bit simpler. Signed-off-by: Markus Armbruster --- scripts/qapi/schema.py | 138 ++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 9bdbfd52b2..c5b824f1fd 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -719,20 +719,11 @@ class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaVariants: def __init__( self, - tag_name: Optional[str], info: QAPISourceInfo, - tag_member: Optional[QAPISchemaObjectTypeMember], variants: List[QAPISchemaVariant], ): - # Unions pass tag_name but not tag_member. - # Alternates pass tag_member but not tag_name. - # After check(), tag_member is always set. - assert bool(tag_member) != bool(tag_name) - assert (isinstance(tag_name, str) or - isinstance(tag_member, QAPISchemaObjectTypeMember)) - self._tag_name = tag_name self.info = info - self._tag_member = tag_member + self._tag_member: Optional[QAPISchemaObjectTypeMember] = None self.variants = variants @property @@ -749,58 +740,66 @@ class QAPISchemaVariants: v.set_defined_in(name) def check( - self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] ) -> None: - if self._tag_name: # union - # We need to narrow the member type: - tmp = seen.get(c_name(self._tag_name)) - assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember) - self._tag_member = tmp + for v in self.variants: + v.check(schema) - base = "'base'" - # Pointing to the base type when not implicit would be - # nice, but we don't know it here - if not self._tag_member or self._tag_name != self._tag_member.name: - raise QAPISemError( - self.info, - "discriminator '%s' is not a member of %s" - % (self._tag_name, base)) - # Here we do: - assert self.tag_member.defined_in - base_type = schema.lookup_type(self.tag_member.defined_in) - assert base_type - if not base_type.is_implicit(): - base = "base type '%s'" % self.tag_member.defined_in - if not isinstance(self.tag_member.type, QAPISchemaEnumType): - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must be of enum type" - % (self._tag_name, base)) - if self.tag_member.optional: - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must not be optional" - % (self._tag_name, base)) - if self.tag_member.ifcond.is_present(): - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must not be conditional" - % (self._tag_name, base)) - else: # alternate - assert self._tag_member - assert isinstance(self.tag_member.type, QAPISchemaEnumType) - assert not self.tag_member.optional - assert not self.tag_member.ifcond.is_present() - if self._tag_name: # union - # branches that are not explicitly covered get an empty type - assert self.tag_member.defined_in - cases = {v.name for v in self.variants} - for m in self.tag_member.type.members: - if m.name not in cases: - v = QAPISchemaVariant(m.name, self.info, - 'q_empty', m.ifcond) - v.set_defined_in(self.tag_member.defined_in) - self.variants.append(v) + +class QAPISchemaBranches(QAPISchemaVariants): + def __init__(self, + info: QAPISourceInfo, + variants: List[QAPISchemaVariant], + tag_name: str): + super().__init__(info, variants) + self._tag_name = tag_name + + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: + # We need to narrow the member type: + tmp = seen.get(c_name(self._tag_name)) + assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember) + self._tag_member = tmp + + base = "'base'" + # Pointing to the base type when not implicit would be + # nice, but we don't know it here + if not self._tag_member or self._tag_name != self._tag_member.name: + raise QAPISemError( + self.info, + "discriminator '%s' is not a member of %s" + % (self._tag_name, base)) + # Here we do: + assert self.tag_member.defined_in + base_type = schema.lookup_type(self.tag_member.defined_in) + assert base_type + if not base_type.is_implicit(): + base = "base type '%s'" % self.tag_member.defined_in + if not isinstance(self.tag_member.type, QAPISchemaEnumType): + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must be of enum type" + % (self._tag_name, base)) + if self.tag_member.optional: + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must not be optional" + % (self._tag_name, base)) + if self.tag_member.ifcond.is_present(): + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must not be conditional" + % (self._tag_name, base)) + # branches that are not explicitly covered get an empty type + assert self.tag_member.defined_in + cases = {v.name for v in self.variants} + for m in self.tag_member.type.members: + if m.name not in cases: + v = QAPISchemaVariant(m.name, self.info, + 'q_empty', m.ifcond) + v.set_defined_in(self.tag_member.defined_in) + self.variants.append(v) if not self.variants: raise QAPISemError(self.info, "union has no branches") for v in self.variants: @@ -834,20 +833,21 @@ class QAPISchemaVariants: v.type.check_clash(info, dict(seen)) -class QAPISchemaBranches(QAPISchemaVariants): - def __init__(self, - info: QAPISourceInfo, - variants: List[QAPISchemaVariant], - tag_name: str): - super().__init__(tag_name, info, None, variants) - - class QAPISchemaAlternatives(QAPISchemaVariants): def __init__(self, info: QAPISourceInfo, variants: List[QAPISchemaVariant], tag_member: QAPISchemaObjectTypeMember): - super().__init__(None, info, tag_member, variants) + super().__init__(info, variants) + self._tag_member = tag_member + + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: + super().check(schema, seen) + assert isinstance(self.tag_member.type, QAPISchemaEnumType) + assert not self.tag_member.optional + assert not self.tag_member.ifcond.is_present() class QAPISchemaMember: From 285a8f209af7b4992aa91e8bea03a303fb6406ab Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 16 Mar 2024 08:46:12 +0100 Subject: [PATCH 0476/2884] qapi: Simplify QAPISchemaVariants @tag_member For union types, the tag member is known only after .check(). We used to code this in a simple way: QAPISchemaVariants attribute .tag_member was None for union types until .check(). Since this complicated typing, recent commit "qapi/schema: fix typing for QAPISchemaVariants.tag_member" hid it behind a property. The previous commit lets us treat .tag_member just like the other attributes that become known only in .check(): declare, but don't initialize it in .__init__(). Signed-off-by: Markus Armbruster --- scripts/qapi/schema.py | 44 +++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index c5b824f1fd..721c470d2b 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -723,18 +723,9 @@ class QAPISchemaVariants: variants: List[QAPISchemaVariant], ): self.info = info - self._tag_member: Optional[QAPISchemaObjectTypeMember] = None + self.tag_member: QAPISchemaObjectTypeMember self.variants = variants - @property - def tag_member(self) -> QAPISchemaObjectTypeMember: - if self._tag_member is None: - raise RuntimeError( - "QAPISchemaVariants has no tag_member property until " - "after check() has been run." - ) - return self._tag_member - def set_defined_in(self, name: str) -> None: for v in self.variants: v.set_defined_in(name) @@ -758,47 +749,48 @@ class QAPISchemaBranches(QAPISchemaVariants): self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] ) -> None: # We need to narrow the member type: - tmp = seen.get(c_name(self._tag_name)) - assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember) - self._tag_member = tmp + tag_member = seen.get(c_name(self._tag_name)) + assert (tag_member is None + or isinstance(tag_member, QAPISchemaObjectTypeMember)) base = "'base'" # Pointing to the base type when not implicit would be # nice, but we don't know it here - if not self._tag_member or self._tag_name != self._tag_member.name: + if not tag_member or self._tag_name != tag_member.name: raise QAPISemError( self.info, "discriminator '%s' is not a member of %s" % (self._tag_name, base)) + self.tag_member = tag_member # Here we do: - assert self.tag_member.defined_in - base_type = schema.lookup_type(self.tag_member.defined_in) + assert tag_member.defined_in + base_type = schema.lookup_type(tag_member.defined_in) assert base_type if not base_type.is_implicit(): - base = "base type '%s'" % self.tag_member.defined_in - if not isinstance(self.tag_member.type, QAPISchemaEnumType): + base = "base type '%s'" % tag_member.defined_in + if not isinstance(tag_member.type, QAPISchemaEnumType): raise QAPISemError( self.info, "discriminator member '%s' of %s must be of enum type" % (self._tag_name, base)) - if self.tag_member.optional: + if tag_member.optional: raise QAPISemError( self.info, "discriminator member '%s' of %s must not be optional" % (self._tag_name, base)) - if self.tag_member.ifcond.is_present(): + if tag_member.ifcond.is_present(): raise QAPISemError( self.info, "discriminator member '%s' of %s must not be conditional" % (self._tag_name, base)) # branches that are not explicitly covered get an empty type - assert self.tag_member.defined_in + assert tag_member.defined_in cases = {v.name for v in self.variants} - for m in self.tag_member.type.members: + for m in tag_member.type.members: if m.name not in cases: v = QAPISchemaVariant(m.name, self.info, 'q_empty', m.ifcond) - v.set_defined_in(self.tag_member.defined_in) + v.set_defined_in(tag_member.defined_in) self.variants.append(v) if not self.variants: raise QAPISemError(self.info, "union has no branches") @@ -807,11 +799,11 @@ class QAPISchemaBranches(QAPISchemaVariants): # Union names must match enum values; alternate names are # checked separately. Use 'seen' to tell the two apart. if seen: - if v.name not in self.tag_member.type.member_names(): + if v.name not in tag_member.type.member_names(): raise QAPISemError( self.info, "branch '%s' is not a value of %s" - % (v.name, self.tag_member.type.describe())) + % (v.name, tag_member.type.describe())) if not isinstance(v.type, QAPISchemaObjectType): raise QAPISemError( self.info, @@ -839,7 +831,7 @@ class QAPISchemaAlternatives(QAPISchemaVariants): variants: List[QAPISchemaVariant], tag_member: QAPISchemaObjectTypeMember): super().__init__(info, variants) - self._tag_member = tag_member + self.tag_member = tag_member def check( self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] From ef932e21bd83c1beab94b10989bf6e8424a886c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 10 Jan 2024 12:41:55 +0100 Subject: [PATCH 0477/2884] user: Forward declare TaskState type definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forward declare TaskState in "qemu/typedefs.h" so we can use it in generic headers like "hw/cpu/core.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-9-philmd@linaro.org> --- bsd-user/qemu.h | 4 ++-- include/qemu/typedefs.h | 1 + linux-user/qemu.h | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 322177de16..1780f485d6 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -76,7 +76,7 @@ struct emulated_sigtable { /* * NOTE: we force a big alignment so that the stack stored after is aligned too */ -typedef struct TaskState { +struct TaskState { pid_t ts_tid; /* tid (or pid) of this task */ struct TaskState *next; @@ -114,7 +114,7 @@ typedef struct TaskState { /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; -} __attribute__((aligned(16))) TaskState; +} __attribute__((aligned(16))); static inline TaskState *get_task_state(CPUState *cs) { diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 50c277cf0b..36f2825725 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -134,6 +134,7 @@ typedef struct SHPCDevice SHPCDevice; typedef struct SSIBus SSIBus; typedef struct TCGCPUOps TCGCPUOps; typedef struct TCGHelperInfo TCGHelperInfo; +typedef struct TaskState TaskState; typedef struct TranslationBlock TranslationBlock; typedef struct VirtIODevice VirtIODevice; typedef struct Visitor Visitor; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 263f445ff1..7df4645c2b 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -95,7 +95,7 @@ struct emulated_sigtable { target_siginfo_t info; }; -typedef struct TaskState { +struct TaskState { pid_t ts_tid; /* tid (or pid) of this task */ #ifdef TARGET_ARM # ifdef TARGET_ABI32 @@ -158,7 +158,7 @@ typedef struct TaskState { /* Start time of task after system boot in clock ticks */ uint64_t start_boottime; -} TaskState; +}; static inline TaskState *get_task_state(CPUState *cs) { From 8019601324159e76ccced4eb8d27093ec0011a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 25 Apr 2024 11:11:49 +0200 Subject: [PATCH 0478/2884] user: Declare get_task_state() once in 'accel/tcg/vcpu-state.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While each user emulation implentation defines its own TaskState structure, both use the same get_task_state() declaration, in particular in common code (such gdbstub). Declare the method once in "accel/tcg/vcpu-state.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-10-philmd@linaro.org> --- accel/tcg/vcpu-state.h | 18 ++++++++++++++++++ bsd-user/qemu.h | 6 +----- linux-user/qemu.h | 6 +----- 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 accel/tcg/vcpu-state.h diff --git a/accel/tcg/vcpu-state.h b/accel/tcg/vcpu-state.h new file mode 100644 index 0000000000..e407d914df --- /dev/null +++ b/accel/tcg/vcpu-state.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileContributor: Philippe Mathieu-Daudé + * SPDX-FileCopyrightText: 2023 Linaro Ltd. + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ACCEL_TCG_VCPU_STATE_H +#define ACCEL_TCG_VCPU_STATE_H + +#include "hw/core/cpu.h" + +#ifdef CONFIG_USER_ONLY +static inline TaskState *get_task_state(const CPUState *cs) +{ + return cs->opaque; +} +#endif + +#endif diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 1780f485d6..9d2fc7148e 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -36,6 +36,7 @@ extern char **environ; #include "exec/gdbstub.h" #include "exec/page-protection.h" #include "qemu/clang-tsa.h" +#include "accel/tcg/vcpu-state.h" #include "qemu-os.h" /* @@ -116,11 +117,6 @@ struct TaskState { struct target_sigaltstack sigaltstack_used; } __attribute__((aligned(16))); -static inline TaskState *get_task_state(CPUState *cs) -{ - return cs->opaque; -} - void stop_all_tasks(void); extern const char *interp_prefix; extern const char *qemu_uname_release; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 7df4645c2b..2e90a97175 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -8,6 +8,7 @@ #include "syscall_defs.h" #include "target_syscall.h" +#include "accel/tcg/vcpu-state.h" /* * This is the size of the host kernel's sigset_t, needed where we make @@ -160,11 +161,6 @@ struct TaskState { uint64_t start_boottime; }; -static inline TaskState *get_task_state(CPUState *cs) -{ - return cs->opaque; -} - abi_long do_brk(abi_ulong new_brk); int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int flags, mode_t mode, bool safe); From 59272469bd1365564fe0bb2c10d8c1d25acd51a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 25 Apr 2024 11:12:19 +0200 Subject: [PATCH 0479/2884] user: Use get_task_state() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get the TaskState pointer calling get_task_state(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20240428221450.26460-11-philmd@linaro.org> --- gdbstub/gdbstub.c | 3 ++- gdbstub/user-target.c | 4 ++-- linux-user/syscall.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 9c2b8b5d0a..b3574997ea 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -32,6 +32,7 @@ #include "exec/gdbstub.h" #include "gdbstub/syscalls.h" #ifdef CONFIG_USER_ONLY +#include "accel/tcg/vcpu-state.h" #include "gdbstub/user.h" #else #include "hw/cpu/cluster.h" @@ -1661,7 +1662,7 @@ static void handle_query_supported(GArray *params, void *user_ctx) #if defined(CONFIG_USER_ONLY) #if defined(CONFIG_LINUX) - if (gdbserver_state.c_cpu->opaque) { + if (get_task_state(gdbserver_state.c_cpu)) { g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+"); } g_string_append(gdbserver_state.str_buf, ";QCatchSyscalls+"); diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c index 6646684a4c..a9c6c64512 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -216,7 +216,7 @@ void gdb_handle_query_offsets(GArray *params, void *user_ctx) { TaskState *ts; - ts = gdbserver_state.c_cpu->opaque; + ts = get_task_state(gdbserver_state.c_cpu); g_string_printf(gdbserver_state.str_buf, "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx @@ -252,7 +252,7 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) offset = get_param(params, 0)->val_ul; len = get_param(params, 1)->val_ul; - ts = gdbserver_state.c_cpu->opaque; + ts = get_task_state(gdbserver_state.c_cpu); saved_auxv = ts->info->saved_auxv; auxv_len = ts->info->auxv_len; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1b42e80f9a..b9b5a387b3 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6463,7 +6463,7 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_GET_TID_ADDRESS: { - TaskState *ts = env_cpu(env)->opaque; + TaskState *ts = get_task_state(env_cpu(env)); return put_user_ual(ts->child_tidptr, arg2); } @@ -8124,7 +8124,7 @@ static int open_self_maps_2(void *opaque, target_ulong guest_start, static int open_self_maps_1(CPUArchState *env, int fd, bool smaps) { struct open_self_maps_data d = { - .ts = env_cpu(env)->opaque, + .ts = get_task_state(env_cpu(env)), .host_maps = read_self_maps(), .fd = fd, .smaps = smaps From a99dd3375c1280b350b36527e0e8756ce44c4e8a Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 5 Oct 2023 11:17:13 -0700 Subject: [PATCH 0480/2884] system: let qemu_map_ram_ptr() use qemu_ram_ptr_length() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_map_ram_ptr() and qemu_ram_ptr_length() share quite some code, so modify qemu_ram_ptr_length() a little bit and use it for qemu_map_ram_ptr(), too. Signed-off-by: Juergen Gross Signed-off-by: Vikram Garhwal Reviewed-by: Stefano Stabellini Reviewed-by: Alex Bennée Message-Id: <20240227223501.28475-4-vikram.garhwal@amd.com> Reviewed-by: Edgar E. Iglesias Signed-off-by: Edgar E. Iglesias Acked-by: David Hildenbrand Reviewed-by: Peter Xu Message-ID: <20240430164939.925307-2-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 56 ++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 44e477a1a5..8278e31c1a 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2189,43 +2189,17 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) } #endif /* !_WIN32 */ -/* Return a host pointer to ram allocated with qemu_ram_alloc. - * This should not be used for general purpose DMA. Use address_space_map - * or address_space_rw instead. For local memory (e.g. video ram) that the - * device owns, use memory_region_get_ram_ptr. - * - * Called within RCU critical section. - */ -void *qemu_map_ram_ptr(RAMBlock *block, ram_addr_t addr) -{ - if (block == NULL) { - block = qemu_get_ram_block(addr); - addr -= block->offset; - } - - if (xen_enabled() && block->host == NULL) { - /* We need to check if the requested address is in the RAM - * because we don't want to map the entire memory in QEMU. - * In that case just map until the end of the page. - */ - if (block->offset == 0) { - return xen_map_cache(addr, 0, 0, false); - } - - block->host = xen_map_cache(block->offset, block->max_length, 1, false); - } - return ramblock_ptr(block, addr); -} - -/* Return a host pointer to guest's ram. Similar to qemu_map_ram_ptr - * but takes a size argument. +/* + * Return a host pointer to guest's ram. * * Called within RCU critical section. */ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, hwaddr *size, bool lock) { - if (*size == 0) { + hwaddr len = 0; + + if (size && *size == 0) { return NULL; } @@ -2233,7 +2207,10 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, block = qemu_get_ram_block(addr); addr -= block->offset; } - *size = MIN(*size, block->max_length - addr); + if (size) { + *size = MIN(*size, block->max_length - addr); + len = *size; + } if (xen_enabled() && block->host == NULL) { /* We need to check if the requested address is in the RAM @@ -2241,7 +2218,7 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, * In that case just map the requested area. */ if (block->offset == 0) { - return xen_map_cache(addr, *size, lock, lock); + return xen_map_cache(addr, len, lock, lock); } block->host = xen_map_cache(block->offset, block->max_length, 1, lock); @@ -2250,6 +2227,19 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, return ramblock_ptr(block, addr); } +/* + * Return a host pointer to ram allocated with qemu_ram_alloc. + * This should not be used for general purpose DMA. Use address_space_map + * or address_space_rw instead. For local memory (e.g. video ram) that the + * device owns, use memory_region_get_ram_ptr. + * + * Called within RCU critical section. + */ +void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) +{ + return qemu_ram_ptr_length(ram_block, addr, NULL, false); +} + /* Return the offset of a hostpointer within a ramblock */ ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host) { From 337265dbf2c35bdfc26f19ed05a71d225318660b Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 5 Oct 2023 11:18:01 -0700 Subject: [PATCH 0481/2884] xen: let xen_ram_addr_from_mapcache() return -1 in case of not found entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today xen_ram_addr_from_mapcache() will either abort() or return 0 in case it can't find a matching entry for a pointer value. Both cases are bad, so change that to return an invalid address instead. Signed-off-by: Juergen Gross Reviewed-by: Stefano Stabellini Message-Id: <20231005181629.4046-5-vikram.garhwal@amd.com> Signed-off-by: Edgar E. Iglesias Reviewed-by: Alex Bennée Reviewed-by: Edgar E. Iglesias Message-ID: <20240430164939.925307-3-edgar.iglesias@gmail.com> [PMD: Keep xen_ram_addr_from_mapcache_not_found trace event] Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 7f59080ba7..7771c6cb91 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -395,12 +395,8 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } if (!found) { trace_xen_ram_addr_from_mapcache_not_found(ptr); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - trace_xen_ram_addr_from_mapcache_found(reventry->paddr_index, - reventry->vaddr_req); - } - abort(); - return 0; + mapcache_unlock(); + return RAM_ADDR_INVALID; } entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; @@ -409,7 +405,7 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } if (!entry) { trace_xen_ram_addr_from_mapcache_not_in_cache(ptr); - raddr = 0; + raddr = RAM_ADDR_INVALID; } else { raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); From efb0c6caefca19a4c9150306013927c0a2ca828e Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:25 +0200 Subject: [PATCH 0482/2884] xen: mapcache: Refactor lock functions for multi-instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the lock functions take MapCache * as argument. This is in preparation for supporting multiple caches. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefano Stabellini Message-ID: <20240430164939.925307-4-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 7771c6cb91..c27be6abee 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -74,14 +74,14 @@ typedef struct MapCache { static MapCache *mapcache; -static inline void mapcache_lock(void) +static inline void mapcache_lock(MapCache *mc) { - qemu_mutex_lock(&mapcache->lock); + qemu_mutex_lock(&mc->lock); } -static inline void mapcache_unlock(void) +static inline void mapcache_unlock(MapCache *mc) { - qemu_mutex_unlock(&mapcache->lock); + qemu_mutex_unlock(&mc->lock); } static inline int test_bits(int nr, int size, const unsigned long *addr) @@ -369,9 +369,9 @@ uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, { uint8_t *p; - mapcache_lock(); + mapcache_lock(mapcache); p = xen_map_cache_unlocked(phys_addr, size, lock, dma); - mapcache_unlock(); + mapcache_unlock(mapcache); return p; } @@ -384,7 +384,7 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) ram_addr_t raddr; int found = 0; - mapcache_lock(); + mapcache_lock(mapcache); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { if (reventry->vaddr_req == ptr) { paddr_index = reventry->paddr_index; @@ -395,7 +395,7 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } if (!found) { trace_xen_ram_addr_from_mapcache_not_found(ptr); - mapcache_unlock(); + mapcache_unlock(mapcache); return RAM_ADDR_INVALID; } @@ -410,7 +410,7 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); } - mapcache_unlock(); + mapcache_unlock(mapcache); return raddr; } @@ -481,9 +481,9 @@ static void xen_invalidate_map_cache_entry_bh(void *opaque) { XenMapCacheData *data = opaque; - mapcache_lock(); + mapcache_lock(mapcache); xen_invalidate_map_cache_entry_unlocked(data->buffer); - mapcache_unlock(); + mapcache_unlock(mapcache); aio_co_wake(data->co); } @@ -499,9 +499,9 @@ void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) xen_invalidate_map_cache_entry_bh, &data); qemu_coroutine_yield(); } else { - mapcache_lock(); + mapcache_lock(mapcache); xen_invalidate_map_cache_entry_unlocked(buffer); - mapcache_unlock(); + mapcache_unlock(mapcache); } } @@ -513,7 +513,7 @@ void xen_invalidate_map_cache(void) /* Flush pending AIO before destroying the mapcache */ bdrv_drain_all(); - mapcache_lock(); + mapcache_lock(mapcache); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { if (!reventry->dma) { @@ -547,7 +547,7 @@ void xen_invalidate_map_cache(void) mapcache->last_entry = NULL; - mapcache_unlock(); + mapcache_unlock(mapcache); } static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, @@ -607,8 +607,8 @@ uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, { uint8_t *p; - mapcache_lock(); + mapcache_lock(mapcache); p = xen_replace_cache_entry_unlocked(old_phys_addr, new_phys_addr, size); - mapcache_unlock(); + mapcache_unlock(mapcache); return p; } From eda3a8cd2e1ba6328ef8a72c498f87e4e11d059e Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:26 +0200 Subject: [PATCH 0483/2884] xen: mapcache: Refactor xen_map_cache for multi-instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make xen_map_cache take a MapCache as argument. This is in prepaparation to support multiple map caches. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefano Stabellini Message-ID: <20240430164939.925307-5-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index c27be6abee..9e0a56b41b 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -240,7 +240,8 @@ static void xen_remap_bucket(MapCacheEntry *entry, g_free(err); } -static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, +static uint8_t *xen_map_cache_unlocked(MapCache *mc, + hwaddr phys_addr, hwaddr size, uint8_t lock, bool dma) { MapCacheEntry *entry, *pentry = NULL, @@ -269,16 +270,16 @@ tryagain: test_bit_size = XC_PAGE_SIZE; } - if (mapcache->last_entry != NULL && - mapcache->last_entry->paddr_index == address_index && + if (mc->last_entry != NULL && + mc->last_entry->paddr_index == address_index && !lock && !size && test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, - mapcache->last_entry->valid_mapping)) { + mc->last_entry->valid_mapping)) { trace_xen_map_cache_return( - mapcache->last_entry->vaddr_base + address_offset + mc->last_entry->vaddr_base + address_offset ); - return mapcache->last_entry->vaddr_base + address_offset; + return mc->last_entry->vaddr_base + address_offset; } /* size is always a multiple of MCACHE_BUCKET_SIZE */ @@ -291,7 +292,7 @@ tryagain: cache_size = MCACHE_BUCKET_SIZE; } - entry = &mapcache->entry[address_index % mapcache->nr_buckets]; + entry = &mc->entry[address_index % mc->nr_buckets]; while (entry && (lock || entry->lock) && entry->vaddr_base && (entry->paddr_index != address_index || entry->size != cache_size || @@ -326,10 +327,10 @@ tryagain: if(!test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - mapcache->last_entry = NULL; + mc->last_entry = NULL; #ifdef XEN_COMPAT_PHYSMAP - if (!translated && mapcache->phys_offset_to_gaddr) { - phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size); + if (!translated && mc->phys_offset_to_gaddr) { + phys_addr = mc->phys_offset_to_gaddr(phys_addr, size); translated = true; goto tryagain; } @@ -342,7 +343,7 @@ tryagain: return NULL; } - mapcache->last_entry = entry; + mc->last_entry = entry; if (lock) { MapCacheRev *reventry = g_new0(MapCacheRev, 1); entry->lock++; @@ -352,16 +353,16 @@ tryagain: abort(); } reventry->dma = dma; - reventry->vaddr_req = mapcache->last_entry->vaddr_base + address_offset; - reventry->paddr_index = mapcache->last_entry->paddr_index; + reventry->vaddr_req = mc->last_entry->vaddr_base + address_offset; + reventry->paddr_index = mc->last_entry->paddr_index; reventry->size = entry->size; - QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next); + QTAILQ_INSERT_HEAD(&mc->locked_entries, reventry, next); } trace_xen_map_cache_return( - mapcache->last_entry->vaddr_base + address_offset + mc->last_entry->vaddr_base + address_offset ); - return mapcache->last_entry->vaddr_base + address_offset; + return mc->last_entry->vaddr_base + address_offset; } uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, @@ -370,7 +371,7 @@ uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, uint8_t *p; mapcache_lock(mapcache); - p = xen_map_cache_unlocked(phys_addr, size, lock, dma); + p = xen_map_cache_unlocked(mapcache, phys_addr, size, lock, dma); mapcache_unlock(mapcache); return p; } From 9b1f33fa63e1cf696273a77c98bb8a1efcdc048c Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:27 +0200 Subject: [PATCH 0484/2884] xen: mapcache: Refactor xen_remap_bucket for multi-instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MapCache argument to xen_remap_bucket in preparation to support multiple map caches. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430164939.925307-6-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 9e0a56b41b..6bb3e0b362 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -139,7 +139,8 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) mapcache->entry = g_malloc0(size); } -static void xen_remap_bucket(MapCacheEntry *entry, +static void xen_remap_bucket(MapCache *mc, + MapCacheEntry *entry, void *vaddr, hwaddr size, hwaddr address_index, @@ -313,14 +314,14 @@ tryagain: if (!entry) { entry = g_new0(MapCacheEntry, 1); pentry->next = entry; - xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); + xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy); } else if (!entry->lock) { if (!entry->vaddr_base || entry->paddr_index != address_index || entry->size != cache_size || !test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); + xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy); } } @@ -588,7 +589,7 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); - xen_remap_bucket(entry, entry->vaddr_base, + xen_remap_bucket(mapcache, entry, entry->vaddr_base, cache_size, address_index, false); if (!test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, From 9b005553548f2872ec913d3cf66db22d5b7c205a Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:28 +0200 Subject: [PATCH 0485/2884] xen: mapcache: Break out xen_ram_addr_from_mapcache_single MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break out xen_ram_addr_from_mapcache_single(), a multi-cache aware version of xen_ram_addr_from_mapcache. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430164939.925307-7-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 6bb3e0b362..1927334e9f 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -377,7 +377,7 @@ uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, return p; } -ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr) { MapCacheEntry *entry = NULL; MapCacheRev *reventry; @@ -386,8 +386,8 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) ram_addr_t raddr; int found = 0; - mapcache_lock(mapcache); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + mapcache_lock(mc); + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (reventry->vaddr_req == ptr) { paddr_index = reventry->paddr_index; size = reventry->size; @@ -397,11 +397,11 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } if (!found) { trace_xen_ram_addr_from_mapcache_not_found(ptr); - mapcache_unlock(mapcache); + mapcache_unlock(mc); return RAM_ADDR_INVALID; } - entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; + entry = &mc->entry[paddr_index % mc->nr_buckets]; while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { entry = entry->next; } @@ -412,10 +412,15 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); } - mapcache_unlock(mapcache); + mapcache_unlock(mc); return raddr; } +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + return xen_ram_addr_from_mapcache_single(mapcache, ptr); +} + static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) { MapCacheEntry *entry = NULL, *pentry = NULL; From 8be27f50ac7a788f1e7f7e0b4d519d3916a74ec8 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:29 +0200 Subject: [PATCH 0486/2884] xen: mapcache: Refactor xen_replace_cache_entry_unlocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MapCache argument to xen_replace_cache_entry_unlocked in preparation for supporting multiple map caches. No functional change. Signed-off-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430164939.925307-8-edgar.iglesias@gmail.com> [PMD: Remove last global mapcache pointer, reported by sstabellini] Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 1927334e9f..96c422981e 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -557,7 +557,8 @@ void xen_invalidate_map_cache(void) mapcache_unlock(mapcache); } -static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, +static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, + hwaddr old_phys_addr, hwaddr new_phys_addr, hwaddr size) { @@ -579,7 +580,7 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); } - entry = &mapcache->entry[address_index % mapcache->nr_buckets]; + entry = &mc->entry[address_index % mc->nr_buckets]; while (entry && !(entry->paddr_index == address_index && entry->size == cache_size)) { entry = entry->next; @@ -594,7 +595,7 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); - xen_remap_bucket(mapcache, entry, entry->vaddr_base, + xen_remap_bucket(mc, entry, entry->vaddr_base, cache_size, address_index, false); if (!test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, @@ -615,7 +616,8 @@ uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, uint8_t *p; mapcache_lock(mapcache); - p = xen_replace_cache_entry_unlocked(old_phys_addr, new_phys_addr, size); + p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr, + new_phys_addr, size); mapcache_unlock(mapcache); return p; } From 87b5a05a853c70756fc94f53e68587c00370aa0d Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:30 +0200 Subject: [PATCH 0487/2884] xen: mapcache: Refactor xen_invalidate_map_cache_entry_unlocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MapCache argument to xen_invalidate_map_cache_entry_unlocked. This is in preparation for supporting multiple map caches. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430164939.925307-9-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 96c422981e..3e6a1a0a93 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -421,7 +421,8 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) return xen_ram_addr_from_mapcache_single(mapcache, ptr); } -static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) +static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, + uint8_t *buffer) { MapCacheEntry *entry = NULL, *pentry = NULL; MapCacheRev *reventry; @@ -429,7 +430,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) hwaddr size; int found = 0; - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (reventry->vaddr_req == buffer) { paddr_index = reventry->paddr_index; size = reventry->size; @@ -439,7 +440,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) } if (!found) { trace_xen_invalidate_map_cache_entry_unlocked_not_found(buffer); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { trace_xen_invalidate_map_cache_entry_unlocked_found( reventry->paddr_index, reventry->vaddr_req @@ -447,15 +448,15 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) } return; } - QTAILQ_REMOVE(&mapcache->locked_entries, reventry, next); + QTAILQ_REMOVE(&mc->locked_entries, reventry, next); g_free(reventry); - if (mapcache->last_entry != NULL && - mapcache->last_entry->paddr_index == paddr_index) { - mapcache->last_entry = NULL; + if (mc->last_entry != NULL && + mc->last_entry->paddr_index == paddr_index) { + mc->last_entry = NULL; } - entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; + entry = &mc->entry[paddr_index % mc->nr_buckets]; while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { pentry = entry; entry = entry->next; @@ -489,7 +490,7 @@ static void xen_invalidate_map_cache_entry_bh(void *opaque) XenMapCacheData *data = opaque; mapcache_lock(mapcache); - xen_invalidate_map_cache_entry_unlocked(data->buffer); + xen_invalidate_map_cache_entry_unlocked(mapcache, data->buffer); mapcache_unlock(mapcache); aio_co_wake(data->co); @@ -507,7 +508,7 @@ void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) qemu_coroutine_yield(); } else { mapcache_lock(mapcache); - xen_invalidate_map_cache_entry_unlocked(buffer); + xen_invalidate_map_cache_entry_unlocked(mapcache, buffer); mapcache_unlock(mapcache); } } From 946b4c9bc319fd8a36dad8fad4f301856315ba8f Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:31 +0200 Subject: [PATCH 0488/2884] xen: mapcache: Break out xen_invalidate_map_cache_single() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break out xen_invalidate_map_cache_single(). No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430164939.925307-10-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 3e6a1a0a93..c8a0f4fbc2 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -513,17 +513,14 @@ void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) } } -void xen_invalidate_map_cache(void) +static void xen_invalidate_map_cache_single(MapCache *mc) { unsigned long i; MapCacheRev *reventry; - /* Flush pending AIO before destroying the mapcache */ - bdrv_drain_all(); + mapcache_lock(mc); - mapcache_lock(mapcache); - - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (!reventry->dma) { continue; } @@ -531,8 +528,8 @@ void xen_invalidate_map_cache(void) reventry->vaddr_req); } - for (i = 0; i < mapcache->nr_buckets; i++) { - MapCacheEntry *entry = &mapcache->entry[i]; + for (i = 0; i < mc->nr_buckets; i++) { + MapCacheEntry *entry = &mc->entry[i]; if (entry->vaddr_base == NULL) { continue; @@ -553,9 +550,17 @@ void xen_invalidate_map_cache(void) entry->valid_mapping = NULL; } - mapcache->last_entry = NULL; + mc->last_entry = NULL; - mapcache_unlock(mapcache); + mapcache_unlock(mc); +} + +void xen_invalidate_map_cache(void) +{ + /* Flush pending AIO before destroying the mapcache */ + bdrv_drain_all(); + + xen_invalidate_map_cache_single(mapcache); } static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, From 886e5ade919647e0dc7276a40b2d6cd6e2f9c85c Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:32 +0200 Subject: [PATCH 0489/2884] xen: mapcache: Break out xen_map_cache_init_single() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break out xen_map_cache_init_single() in preparation for adding multiple map caches. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Message-ID: <20240430164939.925307-11-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 57 ++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index c8a0f4fbc2..6fb2db2612 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -93,23 +93,44 @@ static inline int test_bits(int nr, int size, const unsigned long *addr) return 0; } -void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) +static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f, + void *opaque, + unsigned long max_size) { unsigned long size; + MapCache *mc; + + mc = g_new0(MapCache, 1); + + mc->phys_offset_to_gaddr = f; + mc->opaque = opaque; + qemu_mutex_init(&mc->lock); + + QTAILQ_INIT(&mc->locked_entries); + + mc->max_mcache_size = max_size; + + mc->nr_buckets = + (((mc->max_mcache_size >> XC_PAGE_SHIFT) + + (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> + (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); + + size = mc->nr_buckets * sizeof(MapCacheEntry); + size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); + trace_xen_map_cache_init(mc->nr_buckets, size); + mc->entry = g_malloc0(size); + return mc; +} + +void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) +{ struct rlimit rlimit_as; - - mapcache = g_new0(MapCache, 1); - - mapcache->phys_offset_to_gaddr = f; - mapcache->opaque = opaque; - qemu_mutex_init(&mapcache->lock); - - QTAILQ_INIT(&mapcache->locked_entries); + unsigned long max_mcache_size; if (geteuid() == 0) { rlimit_as.rlim_cur = RLIM_INFINITY; rlimit_as.rlim_max = RLIM_INFINITY; - mapcache->max_mcache_size = MCACHE_MAX_SIZE; + max_mcache_size = MCACHE_MAX_SIZE; } else { getrlimit(RLIMIT_AS, &rlimit_as); rlimit_as.rlim_cur = rlimit_as.rlim_max; @@ -119,24 +140,14 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) " memory is not infinity"); } if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) { - mapcache->max_mcache_size = rlimit_as.rlim_max - - NON_MCACHE_MEMORY_SIZE; + max_mcache_size = rlimit_as.rlim_max - NON_MCACHE_MEMORY_SIZE; } else { - mapcache->max_mcache_size = MCACHE_MAX_SIZE; + max_mcache_size = MCACHE_MAX_SIZE; } } + mapcache = xen_map_cache_init_single(f, opaque, max_mcache_size); setrlimit(RLIMIT_AS, &rlimit_as); - - mapcache->nr_buckets = - (((mapcache->max_mcache_size >> XC_PAGE_SHIFT) + - (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> - (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); - - size = mapcache->nr_buckets * sizeof (MapCacheEntry); - size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); - trace_xen_map_cache_init(mapcache->nr_buckets, size); - mapcache->entry = g_malloc0(size); } static void xen_remap_bucket(MapCache *mc, From 5a5585f45dcf32fde57bd1b4015fd2f00c52867c Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:35 +0200 Subject: [PATCH 0490/2884] system: Pass RAM MemoryRegion and is_write in xen_map_cache() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate MR and is_write to xen_map_cache(). This is in preparation for adding support for grant mappings. No functional change. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé Acked-by: Peter Xu Reviewed-by: David Hildenbrand Message-ID: <20240430164939.925307-14-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 10 ++++++---- include/sysemu/xen-mapcache.h | 11 +++++++---- system/physmem.c | 31 +++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 6fb2db2612..fa6813b1ad 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -254,7 +254,7 @@ static void xen_remap_bucket(MapCache *mc, static uint8_t *xen_map_cache_unlocked(MapCache *mc, hwaddr phys_addr, hwaddr size, - uint8_t lock, bool dma) + uint8_t lock, bool dma, bool is_write) { MapCacheEntry *entry, *pentry = NULL, *free_entry = NULL, *free_pentry = NULL; @@ -377,13 +377,15 @@ tryagain: return mc->last_entry->vaddr_base + address_offset; } -uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, - uint8_t lock, bool dma) +uint8_t *xen_map_cache(MemoryRegion *mr, + hwaddr phys_addr, hwaddr size, + uint8_t lock, bool dma, + bool is_write) { uint8_t *p; mapcache_lock(mapcache); - p = xen_map_cache_unlocked(mapcache, phys_addr, size, lock, dma); + p = xen_map_cache_unlocked(mapcache, phys_addr, size, lock, dma, is_write); mapcache_unlock(mapcache); return p; } diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index 10c2e3082a..1ec9e66752 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -18,8 +18,9 @@ typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque); -uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, - uint8_t lock, bool dma); +uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, + uint8_t lock, bool dma, + bool is_write); ram_addr_t xen_ram_addr_from_mapcache(void *ptr); void xen_invalidate_map_cache_entry(uint8_t *buffer); void xen_invalidate_map_cache(void); @@ -33,10 +34,12 @@ static inline void xen_map_cache_init(phys_offset_to_gaddr_t f, { } -static inline uint8_t *xen_map_cache(hwaddr phys_addr, +static inline uint8_t *xen_map_cache(MemoryRegion *mr, + hwaddr phys_addr, hwaddr size, uint8_t lock, - bool dma) + bool dma, + bool is_write) { abort(); } diff --git a/system/physmem.c b/system/physmem.c index 8278e31c1a..79d46054c5 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2191,11 +2191,22 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) /* * Return a host pointer to guest's ram. + * For Xen, foreign mappings get created if they don't already exist. + * + * @block: block for the RAM to lookup (optional and may be NULL). + * @addr: address within the memory region. + * @size: pointer to requested size (optional and may be NULL). + * size may get modified and return a value smaller than + * what was requested. + * @lock: wether to lock the mapping in xen-mapcache until invalidated. + * @is_write: hint wether to map RW or RO in the xen-mapcache. + * (optional and may always be set to true). * * Called within RCU critical section. */ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, - hwaddr *size, bool lock) + hwaddr *size, bool lock, + bool is_write) { hwaddr len = 0; @@ -2218,10 +2229,13 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, * In that case just map the requested area. */ if (block->offset == 0) { - return xen_map_cache(addr, len, lock, lock); + return xen_map_cache(block->mr, addr, len, lock, lock, + is_write); } - block->host = xen_map_cache(block->offset, block->max_length, 1, lock); + block->host = xen_map_cache(block->mr, block->offset, + block->max_length, 1, + lock, is_write); } return ramblock_ptr(block, addr); @@ -2237,7 +2251,7 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, */ void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) { - return qemu_ram_ptr_length(ram_block, addr, NULL, false); + return qemu_ram_ptr_length(ram_block, addr, NULL, false, true); } /* Return the offset of a hostpointer within a ramblock */ @@ -2747,7 +2761,7 @@ static MemTxResult flatview_write_continue_step(MemTxAttrs attrs, } else { /* RAM case */ uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, - false); + false, true); memmove(ram_ptr, buf, *l); invalidate_and_set_dirty(mr, mr_addr, *l); @@ -2840,7 +2854,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, } else { /* RAM case */ uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, - false); + false, false); memcpy(buf, ram_ptr, *l); @@ -3234,7 +3248,7 @@ void *address_space_map(AddressSpace *as, *plen = flatview_extend_translation(fv, addr, len, mr, xlat, l, is_write, attrs); fuzz_dma_read_cb(addr, *plen, mr); - return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); + return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true, is_write); } /* Unmaps a memory region previously mapped by address_space_map(). @@ -3330,7 +3344,8 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, l = flatview_extend_translation(cache->fv, addr, len, mr, cache->xlat, l, is_write, MEMTXATTRS_UNSPECIFIED); - cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); + cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true, + is_write); } else { cache->ptr = NULL; } From 45c577f380a89b2e4e09f369e5648ff5b451de96 Mon Sep 17 00:00:00 2001 From: Aleksandar Rikalo Date: Fri, 9 Feb 2024 07:21:47 +0100 Subject: [PATCH 0491/2884] MAINTAINERS: Update Aleksandar Rikalo email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Syrmia LLC has been acquired recently and the syrmia.com domain will disappear soon, so updating my email in the MAINTAINERS file. Signed-off-by: Aleksandar Rikalo Message-ID: <20240209062147.62453-1-aleksandar.rikalo@syrmia.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 595808fc96..63ada48bb4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -285,7 +285,7 @@ MIPS TCG CPUs M: Philippe Mathieu-Daudé R: Aurelien Jarno R: Jiaxun Yang -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Odd Fixes F: target/mips/ F: disas/*mips.c @@ -1335,7 +1335,7 @@ F: include/hw/mips/ Jazz M: Hervé Poussineau -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Maintained F: hw/mips/jazz.c F: hw/display/g364fb.c @@ -1357,7 +1357,7 @@ F: tests/avocado/linux_ssh_mips_malta.py F: tests/avocado/machine_mips_malta.py Mipssim -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Orphan F: hw/mips/mipssim.c F: hw/net/mipsnet.c @@ -1385,7 +1385,7 @@ F: tests/avocado/machine_mips_loongson3v.py Boston M: Paul Burton -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Odd Fixes F: hw/core/loader-fit.c F: hw/mips/boston.c @@ -3762,7 +3762,7 @@ M: Philippe Mathieu-Daudé R: Aurelien Jarno R: Huacai Chen R: Jiaxun Yang -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Odd Fixes F: tcg/mips/ From 8372c3a0cbc5d41458ab3582164cfbcac9b434d4 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 5 May 2024 15:23:12 +0800 Subject: [PATCH 0492/2884] MAINTAINERS: Update my email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old Wind River email address (bin.meng@windriver.com) is no longer available due to an internal infrastructure change within the company. While a new email address (bin.meng.cn@windriver.com) has been assigned to me, I am unable to find a way to send this patch directly from the new address. Presumably, the basic authentication with client submission (SMTP AUTH) [1] has been disabled by the company's IT. Switch to use my personal email address instead. Signed-off-by: Bin Meng Signed-off-by: Bin Meng [1] https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/how-to-set-up-a-multifunction-device-or-application-to-send-email-using-microsoft-365-or-office-365 Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240505072312.2776074-1-bmeng.cn@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 63ada48bb4..84391777db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -320,7 +320,7 @@ F: tests/tcg/ppc*/* RISC-V TCG CPUs M: Palmer Dabbelt M: Alistair Francis -M: Bin Meng +M: Bin Meng R: Weiwei Li R: Daniel Henrique Barboza R: Liu Zhiwei @@ -1603,7 +1603,7 @@ F: include/hw/riscv/opentitan.h F: include/hw/*/ibex_*.h Microchip PolarFire SoC Icicle Kit -M: Bin Meng +M: Bin Meng L: qemu-riscv@nongnu.org S: Supported F: docs/system/riscv/microchip-icicle-kit.rst @@ -1630,7 +1630,7 @@ F: include/hw/char/shakti_uart.h SiFive Machines M: Alistair Francis -M: Bin Meng +M: Bin Meng M: Palmer Dabbelt L: qemu-riscv@nongnu.org S: Supported @@ -2126,7 +2126,7 @@ F: hw/ssi/xilinx_* SD (Secure Card) M: Philippe Mathieu-Daudé -M: Bin Meng +M: Bin Meng L: qemu-block@nongnu.org S: Odd Fixes F: include/hw/sd/sd* From b3ee719e6499987a635332d012f08dc80cd277e0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 11 Mar 2024 11:26:59 -1000 Subject: [PATCH 0493/2884] tcg: Add write_aofs to GVecGen3i MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 2 ++ tcg/tcg-op-gvec.c | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index 4db8a58c14..65553f5f97 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -183,6 +183,8 @@ typedef struct { bool prefer_i64; /* Load dest as a 3rd source operand. */ bool load_dest; + /* Write aofs as a 2nd dest operand. */ + bool write_aofs; } GVecGen3i; typedef struct { diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index bb88943f79..0308732d9b 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -785,7 +785,8 @@ static void expand_3_i32(uint32_t dofs, uint32_t aofs, } static void expand_3i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, int32_t c, bool load_dest, + uint32_t oprsz, int32_t c, + bool load_dest, bool write_aofs, void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t)) { TCGv_i32 t0 = tcg_temp_new_i32(); @@ -801,6 +802,9 @@ static void expand_3i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, } fni(t2, t0, t1, c); tcg_gen_st_i32(t2, tcg_env, dofs + i); + if (write_aofs) { + tcg_gen_st_i32(t0, tcg_env, aofs + i); + } } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -944,7 +948,8 @@ static void expand_3_i64(uint32_t dofs, uint32_t aofs, } static void expand_3i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, int64_t c, bool load_dest, + uint32_t oprsz, int64_t c, + bool load_dest, bool write_aofs, void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t)) { TCGv_i64 t0 = tcg_temp_new_i64(); @@ -960,6 +965,9 @@ static void expand_3i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, } fni(t2, t0, t1, c); tcg_gen_st_i64(t2, tcg_env, dofs + i); + if (write_aofs) { + tcg_gen_st_i64(t0, tcg_env, aofs + i); + } } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -1102,7 +1110,8 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, */ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t tysz, - TCGType type, int64_t c, bool load_dest, + TCGType type, int64_t c, + bool load_dest, bool write_aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t)) { @@ -1118,6 +1127,9 @@ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, } fni(vece, t2, t0, t1, c); tcg_gen_st_vec(t2, tcg_env, dofs + i); + if (write_aofs) { + tcg_gen_st_vec(t0, tcg_env, aofs + i); + } } } @@ -1471,7 +1483,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, */ some = QEMU_ALIGN_DOWN(oprsz, 32); expand_3i_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, - c, g->load_dest, g->fniv); + c, g->load_dest, g->write_aofs, g->fniv); if (some == oprsz) { break; } @@ -1483,18 +1495,20 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, /* fallthru */ case TCG_TYPE_V128: expand_3i_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, - c, g->load_dest, g->fniv); + c, g->load_dest, g->write_aofs, g->fniv); break; case TCG_TYPE_V64: expand_3i_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, - c, g->load_dest, g->fniv); + c, g->load_dest, g->write_aofs, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_3i_i64(dofs, aofs, bofs, oprsz, c, g->load_dest, g->fni8); + expand_3i_i64(dofs, aofs, bofs, oprsz, c, + g->load_dest, g->write_aofs, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_3i_i32(dofs, aofs, bofs, oprsz, c, g->load_dest, g->fni4); + expand_3i_i32(dofs, aofs, bofs, oprsz, c, + g->load_dest, g->write_aofs, g->fni4); } else { assert(g->fno != NULL); tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, c, g->fno); From 2623ca6ac11dd1c15ec1c2e87aa2e7f22f0adec8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 12 Mar 2024 14:28:27 -1000 Subject: [PATCH 0494/2884] tcg/i386: Simplify immediate 8-bit logical vector shifts The x86 isa does not have this operation, so we need an expansion. Use the same algorithm that we use for expanding this vector operation with integers: perform the shift with a wider type and then mask the bits that must be zero. This reduces the instruction count from 5 to 2. Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 61 +++++++++------------------------------ 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index c6ba498623..6837c519b0 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3769,49 +3769,20 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } } -static void expand_vec_shi(TCGType type, unsigned vece, TCGOpcode opc, +static void expand_vec_shi(TCGType type, unsigned vece, bool right, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { - TCGv_vec t1, t2; + uint8_t mask; tcg_debug_assert(vece == MO_8); - - t1 = tcg_temp_new_vec(type); - t2 = tcg_temp_new_vec(type); - - /* - * Unpack to W, shift, and repack. Tricky bits: - * (1) Use punpck*bw x,x to produce DDCCBBAA, - * i.e. duplicate in other half of the 16-bit lane. - * (2) For right-shift, add 8 so that the high half of the lane - * becomes zero. For left-shift, and left-rotate, we must - * shift up and down again. - * (3) Step 2 leaves high half zero such that PACKUSWB - * (pack with unsigned saturation) does not modify - * the quantity. - */ - vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, - tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); - vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, - tcgv_vec_arg(t2), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); - - if (opc != INDEX_op_rotli_vec) { - imm += 8; - } - if (opc == INDEX_op_shri_vec) { - tcg_gen_shri_vec(MO_16, t1, t1, imm); - tcg_gen_shri_vec(MO_16, t2, t2, imm); + if (right) { + mask = 0xff >> imm; + tcg_gen_shri_vec(MO_16, v0, v1, imm); } else { - tcg_gen_shli_vec(MO_16, t1, t1, imm); - tcg_gen_shli_vec(MO_16, t2, t2, imm); - tcg_gen_shri_vec(MO_16, t1, t1, 8); - tcg_gen_shri_vec(MO_16, t2, t2, 8); + mask = 0xff << imm; + tcg_gen_shli_vec(MO_16, v0, v1, imm); } - - vec_gen_3(INDEX_op_x86_packus_vec, type, MO_8, - tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t2)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); + tcg_gen_and_vec(MO_8, v0, v0, tcg_constant_vec(type, MO_8, mask)); } static void expand_vec_sari(TCGType type, unsigned vece, @@ -3821,7 +3792,7 @@ static void expand_vec_sari(TCGType type, unsigned vece, switch (vece) { case MO_8: - /* Unpack to W, shift, and repack, as in expand_vec_shi. */ + /* Unpack to 16-bit, shift, and repack. */ t1 = tcg_temp_new_vec(type); t2 = tcg_temp_new_vec(type); vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, @@ -3874,12 +3845,7 @@ static void expand_vec_rotli(TCGType type, unsigned vece, { TCGv_vec t; - if (vece == MO_8) { - expand_vec_shi(type, vece, INDEX_op_rotli_vec, v0, v1, imm); - return; - } - - if (have_avx512vbmi2) { + if (vece != MO_8 && have_avx512vbmi2) { vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v1), imm); return; @@ -4155,10 +4121,11 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, switch (opc) { case INDEX_op_shli_vec: - case INDEX_op_shri_vec: - expand_vec_shi(type, vece, opc, v0, v1, a2); + expand_vec_shi(type, vece, false, v0, v1, a2); + break; + case INDEX_op_shri_vec: + expand_vec_shi(type, vece, true, v0, v1, a2); break; - case INDEX_op_sari_vec: expand_vec_sari(type, vece, v0, v1, a2); break; From 19517b8397940955c2638700f9cad3dbdb90c4c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 14:48:36 -1000 Subject: [PATCH 0495/2884] tcg/i386: Optimize setcond of TST{EQ,NE} with 0xffffffff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This may be treated as a 32-bit EQ/NE comparison against 0, which is in turn treated as a LTU/GEU comparison against 1. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 6837c519b0..59235b4f38 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1658,6 +1658,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, TCGArg dest, TCGArg arg1, TCGArg arg2, int const_arg2, bool neg) { + int cmp_rexw = rexw; bool inv = false; bool cleared; int jcc; @@ -1674,6 +1675,18 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, } break; + case TCG_COND_TSTNE: + inv = true; + /* fall through */ + case TCG_COND_TSTEQ: + /* If arg2 is -1, convert to LTU/GEU vs 1. */ + if (const_arg2 && arg2 == 0xffffffffu) { + arg2 = 1; + cmp_rexw = 0; + goto do_ltu; + } + break; + case TCG_COND_LEU: inv = true; /* fall through */ @@ -1697,7 +1710,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, * We can then use NEG or INC to produce the desired result. * This is always smaller than the SETCC expansion. */ - tcg_out_cmp(s, TCG_COND_LTU, arg1, arg2, const_arg2, rexw); + tcg_out_cmp(s, TCG_COND_LTU, arg1, arg2, const_arg2, cmp_rexw); /* X - X - C = -C = (C ? -1 : 0) */ tgen_arithr(s, ARITH_SBB + (neg ? rexw : 0), dest, dest); @@ -1744,7 +1757,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, cleared = true; } - jcc = tcg_out_cmp(s, cond, arg1, arg2, const_arg2, rexw); + jcc = tcg_out_cmp(s, cond, arg1, arg2, const_arg2, cmp_rexw); tcg_out_modrm(s, OPC_SETCC | jcc, 0, dest); if (!cleared) { From 8d65cda7284edf31998778f92813bc6ef1e6ab77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 16:00:40 -1000 Subject: [PATCH 0496/2884] tcg/optimize: Optimize setcond with zmask If we can show that high bits of an input are zero, then we may optimize away some comparisons. Signed-off-by: Richard Henderson --- tcg/optimize.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index 2e9e5725a9..8886f7037a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2099,6 +2099,108 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) return false; } +static bool fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) +{ + uint64_t a_zmask, b_val; + TCGCond cond; + + if (!arg_is_const(op->args[2])) { + return false; + } + + a_zmask = arg_info(op->args[1])->z_mask; + b_val = arg_info(op->args[2])->val; + cond = op->args[3]; + + if (ctx->type == TCG_TYPE_I32) { + a_zmask = (uint32_t)a_zmask; + b_val = (uint32_t)b_val; + } + + /* + * A with only low bits set vs B with high bits set means that A < B. + */ + if (a_zmask < b_val) { + bool inv = false; + + switch (cond) { + case TCG_COND_NE: + case TCG_COND_LEU: + case TCG_COND_LTU: + inv = true; + /* fall through */ + case TCG_COND_GTU: + case TCG_COND_GEU: + case TCG_COND_EQ: + return tcg_opt_gen_movi(ctx, op, op->args[0], neg ? -inv : inv); + default: + break; + } + } + + /* + * A with only lsb set is already boolean. + */ + if (a_zmask <= 1) { + bool convert = false; + bool inv = false; + + switch (cond) { + case TCG_COND_EQ: + inv = true; + /* fall through */ + case TCG_COND_NE: + convert = (b_val == 0); + break; + case TCG_COND_LTU: + case TCG_COND_TSTEQ: + inv = true; + /* fall through */ + case TCG_COND_GEU: + case TCG_COND_TSTNE: + convert = (b_val == 1); + break; + default: + break; + } + if (convert) { + TCGOpcode add_opc, xor_opc, neg_opc; + + if (!inv && !neg) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + + switch (ctx->type) { + case TCG_TYPE_I32: + add_opc = INDEX_op_add_i32; + neg_opc = INDEX_op_neg_i32; + xor_opc = INDEX_op_xor_i32; + break; + case TCG_TYPE_I64: + add_opc = INDEX_op_add_i64; + neg_opc = INDEX_op_neg_i64; + xor_opc = INDEX_op_xor_i64; + break; + default: + g_assert_not_reached(); + } + + if (!inv) { + op->opc = neg_opc; + } else if (neg) { + op->opc = add_opc; + op->args[2] = arg_new_constant(ctx, -1); + } else { + op->opc = xor_opc; + op->args[2] = arg_new_constant(ctx, 1); + } + return false; + } + } + + return false; +} + static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { TCGOpcode and_opc, sub_opc, xor_opc, neg_opc, shr_opc; @@ -2200,6 +2302,10 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op) if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); } + + if (fold_setcond_zmask(ctx, op, false)) { + return true; + } fold_setcond_tst_pow2(ctx, op, false); ctx->z_mask = 1; @@ -2214,6 +2320,10 @@ static bool fold_negsetcond(OptContext *ctx, TCGOp *op) if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], -i); } + + if (fold_setcond_zmask(ctx, op, true)) { + return true; + } fold_setcond_tst_pow2(ctx, op, true); /* Value is {0,-1} so all bits are repetitions of the sign. */ From d828b92b8a61204d8a7aaa87a24e48ac7ab69143 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 16:54:11 -1000 Subject: [PATCH 0497/2884] accel/tcg: Introduce CF_BP_PAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record the fact that we've found a breakpoint on the page in which a TranslationBlock is running. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 +- include/exec/translation-block.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 225e5fbd3e..6a764f527b 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -371,7 +371,7 @@ static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc, * breakpoints are removed. */ if (match_page) { - *cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | 1; + *cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | CF_BP_PAGE | 1; } return false; } diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index 48211c890a..a6d1af6e9b 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -77,6 +77,7 @@ struct TranslationBlock { #define CF_PARALLEL 0x00008000 /* Generate code for a parallel context */ #define CF_NOIRQ 0x00010000 /* Generate an uninterruptible TB */ #define CF_PCREL 0x00020000 /* Opcodes in TB are PC-relative */ +#define CF_BP_PAGE 0x00040000 /* Breakpoint present in code page */ #define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ #define CF_CLUSTER_SHIFT 24 From ca51921158e3cc07520a0ef5eb33739e5852ac6e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 May 2024 12:52:46 -0700 Subject: [PATCH 0498/2884] target/sh4: Update DisasContextBase.insn_start Match the extra inserts of INDEX_op_insn_start, fixing the db->num_insns != 1 assert in translator_loop. Fixes: dcd092a0636 ("accel/tcg: Improve can_do_io management") Signed-off-by: Richard Henderson --- target/sh4/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index e599ab9d1a..b3282f3ac7 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2189,6 +2189,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) */ for (i = 1; i < max_insns; ++i) { tcg_gen_insn_start(pc + i * 2, ctx->envflags); + ctx->base.insn_start = tcg_last_op(); } } #endif From a55a1f77b6c4270f6c19a74e9aa4c83c3bb04e09 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 May 2024 12:52:46 -0700 Subject: [PATCH 0499/2884] gitlab: Drop --disable-libssh from ubuntu-22.04-s390x.yml This was a workaround for ubuntu 20.04. Suggested-by: Thomas Huth Signed-off-by: Richard Henderson --- .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index 105981879f..2b9e1ff749 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -35,7 +35,7 @@ ubuntu-22.04-s390x-all: script: - mkdir build - cd build - - ../configure --disable-libssh + - ../configure || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check @@ -57,7 +57,7 @@ ubuntu-22.04-s390x-alldbg: script: - mkdir build - cd build - - ../configure --enable-debug --disable-libssh + - ../configure --enable-debug || { cat config.log meson-logs/meson-log.txt; exit 1; } - make clean - make --output-sync -j`nproc` @@ -80,7 +80,7 @@ ubuntu-22.04-s390x-clang: script: - mkdir build - cd build - - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers + - ../configure --cc=clang --cxx=clang++ --enable-sanitizers || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check @@ -101,7 +101,7 @@ ubuntu-22.04-s390x-tci: script: - mkdir build - cd build - - ../configure --disable-libssh --enable-tcg-interpreter + - ../configure --enable-tcg-interpreter || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` @@ -122,7 +122,7 @@ ubuntu-22.04-s390x-notcg: script: - mkdir build - cd build - - ../configure --disable-libssh --disable-tcg + - ../configure --disable-tcg || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check From 22e8db9deb96d2cd88492adf4047087c9d9d575d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 May 2024 13:13:04 -0700 Subject: [PATCH 0500/2884] gitlab: Drop --static from s390x linux-user build The host does not have the correct libraries installed for static pie, which causes host/guest address space interference for some tests. There's no real gain from linking statically, so drop it. Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson --- .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index 2b9e1ff749..0a2d2bd332 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -2,7 +2,7 @@ # setup by the scripts/ci/setup/build-environment.yml task # "Install basic packages to build QEMU on Ubuntu 22.04" -ubuntu-22.04-s390x-all-linux-static: +ubuntu-22.04-s390x-all-linux: extends: .custom_runner_template needs: [] stage: build @@ -15,7 +15,7 @@ ubuntu-22.04-s390x-all-linux-static: script: - mkdir build - cd build - - ../configure --enable-debug --static --disable-system + - ../configure --enable-debug --disable-system || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync check-tcg From f578b66e8c70ddea71d44db6e2c7abbcd757d684 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 May 2024 13:21:36 -0700 Subject: [PATCH 0501/2884] gitlab: Streamline ubuntu-22.04-s390x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have one job to build user binaries and one job for system. Disable tools and docs in the user job, and disable building the user binaries in the system job. Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index 0a2d2bd332..25935048e2 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -15,13 +15,13 @@ ubuntu-22.04-s390x-all-linux: script: - mkdir build - cd build - - ../configure --enable-debug --disable-system + - ../configure --enable-debug --disable-system --disable-tools --disable-docs || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync check-tcg - make --output-sync -j`nproc` check -ubuntu-22.04-s390x-all: +ubuntu-22.04-s390x-all-system: extends: .custom_runner_template needs: [] stage: build @@ -35,7 +35,7 @@ ubuntu-22.04-s390x-all: script: - mkdir build - cd build - - ../configure + - ../configure --disable-user || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - make --output-sync -j`nproc` check From b776569a53f70ab4661ed627756385b05dcaf75e Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Fri, 3 May 2024 12:46:30 -0500 Subject: [PATCH 0502/2884] target/i386: Fix CPUID encoding of Fn8000001E_ECX Observed the following failure while booting the SEV-SNP guest and the guest fails to boot with the smp parameters: "-smp 192,sockets=1,dies=12,cores=8,threads=2". qemu-system-x86_64: sev_snp_launch_update: SNP_LAUNCH_UPDATE ret=-5 fw_error=22 'Invalid parameter' qemu-system-x86_64: SEV-SNP: CPUID validation failed for function 0x8000001e, index: 0x0. provided: eax:0x00000000, ebx: 0x00000100, ecx: 0x00000b00, edx: 0x00000000 expected: eax:0x00000000, ebx: 0x00000100, ecx: 0x00000300, edx: 0x00000000 qemu-system-x86_64: SEV-SNP: failed update CPUID page Reason for the failure is due to overflowing of bits used for "Node per processor" in CPUID Fn8000001E_ECX. This field's width is 3 bits wide and can hold maximum value 0x7. With dies=12 (0xB), it overflows and spills over into the reserved bits. In the case of SEV-SNP, this causes CPUID enforcement failure and guest fails to boot. The PPR documentation for CPUID_Fn8000001E_ECX [Node Identifiers] ================================================================= Bits Description 31:11 Reserved. 10:8 NodesPerProcessor: Node per processor. Read-only. ValidValues: Value Description 0h 1 node per processor. 7h-1h Reserved. 7:0 NodeId: Node ID. Read-only. Reset: Fixed,XXh. ================================================================= As in the spec, the valid value for "node per processor" is 0 and rest are reserved. Looking back at the history of decoding of CPUID_Fn8000001E_ECX, noticed that there were cases where "node per processor" can be more than 1. It is valid only for pre-F17h (pre-EPYC) architectures. For EPYC or later CPUs, the linux kernel does not use this information to build the L3 topology. Also noted that the CPUID Function 0x8000001E_ECX is available only when TOPOEXT feature is enabled. This feature is enabled only for EPYC(F17h) or later processors. So, previous generation of processors do not not enumerate 0x8000001E_ECX leaf. There could be some corner cases where the older guests could enable the TOPOEXT feature by running with -cpu host, in which case legacy guests might notice the topology change. To address those cases introduced a new CPU property "legacy-multi-node". It will be true for older machine types to maintain compatibility. By default, it will be false, so new decoding will be used going forward. The documentation is taken from Preliminary Processor Programming Reference (PPR) for AMD Family 19h Model 11h, Revision B1 Processors 55901 Rev 0.25 - Oct 6, 2022. Cc: qemu-stable@nongnu.org Fixes: 31ada106d891 ("Simplify CPUID_8000_001E for AMD") Link: https://bugzilla.kernel.org/show_bug.cgi?id=206537 Reviewed-by: Zhao Liu Signed-off-by: Babu Moger Message-ID: <0ee4b0a8293188a53970a2b0e4f4ef713425055e.1714757834.git.babu.moger@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/cpu.c | 18 ++++++++++-------- target/i386/cpu.h | 6 ++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 08c7de416f..46235466d7 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -81,6 +81,7 @@ GlobalProperty pc_compat_9_0[] = { { TYPE_X86_CPU, "guest-phys-bits", "0" }, { "sev-guest", "legacy-vm-type", "true" }, + { TYPE_X86_CPU, "legacy-multi-node", "on" }, }; const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3ef30a765c..1058b6803f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -398,12 +398,9 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * 31:11 Reserved. * 10:8 NodesPerProcessor: Node per processor. Read-only. Reset: XXXb. * ValidValues: - * Value Description - * 000b 1 node per processor. - * 001b 2 nodes per processor. - * 010b Reserved. - * 011b 4 nodes per processor. - * 111b-100b Reserved. + * Value Description + * 0h 1 node per processor. + * 7h-1h Reserved. * 7:0 NodeId: Node ID. Read-only. Reset: XXh. * * NOTE: Hardware reserves 3 bits for number of nodes per processor. @@ -412,8 +409,12 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * NodeId is combination of node and socket_id which is already decoded * in apic_id. Just use it by shifting. */ - *ecx = ((topo_info->dies_per_pkg - 1) << 8) | - ((cpu->apic_id >> apicid_die_offset(topo_info)) & 0xFF); + if (cpu->legacy_multi_node) { + *ecx = ((topo_info->dies_per_pkg - 1) << 8) | + ((cpu->apic_id >> apicid_die_offset(topo_info)) & 0xFF); + } else { + *ecx = (cpu->apic_id >> apicid_pkg_offset(topo_info)) & 0xFF; + } *edx = 0; } @@ -8084,6 +8085,7 @@ static Property x86_cpu_properties[] = { * own cache information (see x86_cpu_load_def()). */ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + DEFINE_PROP_BOOL("legacy-multi-node", X86CPU, legacy_multi_node, false), DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), /* diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 565c7a98c3..1e0d2c915f 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1994,6 +1994,12 @@ struct ArchCPU { */ bool legacy_cache; + /* Compatibility bits for old machine types. + * If true decode the CPUID Function 0x8000001E_ECX to support multiple + * nodes per processor + */ + bool legacy_multi_node; + /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; From 15957eb9efe2da67c796612cead95cba28ba9bda Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Oct 2023 05:57:31 +0200 Subject: [PATCH 0503/2884] target/i386: use TSTEQ/TSTNE to test low bits When testing the sign bit or equality to zero of a partial register, it is useful to use a single TSTEQ or TSTNE operation. It can also be used to test the parity flag, using bit 0 of the population count. Do not do this for target_ulong-sized values however; the optimizer would produce a comparison against zero anyway, and it avoids shifts by 64 which are undefined behavior. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 5 ++--- target/i386/tcg/translate.c | 28 ++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 6bcf88ecd7..0e00f6635d 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1209,7 +1209,7 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec [JCC_Z] = TCG_COND_EQ, [JCC_BE] = TCG_COND_LEU, [JCC_S] = TCG_COND_LT, /* test sign bit by comparing against 0 */ - [JCC_P] = TCG_COND_EQ, /* even parity - tests low bit of popcount */ + [JCC_P] = TCG_COND_TSTEQ, /* even parity - tests low bit of popcount */ [JCC_L] = TCG_COND_LT, [JCC_LE] = TCG_COND_LE, }; @@ -1260,8 +1260,7 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec case JCC_P: tcg_gen_ext8u_tl(s->tmp0, s->T0); tcg_gen_ctpop_tl(s->tmp0, s->tmp0); - tcg_gen_andi_tl(s->tmp0, s->tmp0, 1); - cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0); + cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(1); break; case JCC_S: diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 051ffb5e1f..4735f084d4 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -928,11 +928,21 @@ typedef struct CCPrepare { bool no_setcond; } CCPrepare; +static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size) +{ + if (size == MO_TL) { + return (CCPrepare) { .cond = TCG_COND_LT, .reg = src, .mask = -1 }; + } else { + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = src, .mask = -1, + .imm = 1ull << ((8 << size) - 1) }; + } +} + /* compute eflags.C to reg */ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) { TCGv t0, t1; - int size, shift; + MemOp size; switch (s->cc_op) { case CC_OP_SUBB ... CC_OP_SUBQ: @@ -967,9 +977,7 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) case CC_OP_SHLB ... CC_OP_SHLQ: /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ size = s->cc_op - CC_OP_SHLB; - shift = (8 << size) - 1; - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = (target_ulong)1 << shift }; + return gen_prepare_sign_nz(cpu_cc_src, size); case CC_OP_MULB ... CC_OP_MULQ: return (CCPrepare) { .cond = TCG_COND_NE, @@ -1029,8 +1037,7 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true); - return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 }; + return gen_prepare_sign_nz(cpu_cc_dst, size); } } } @@ -1077,8 +1084,13 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + if (size == MO_TL) { + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst, + .mask = -1 }; + } else { + return (CCPrepare) { .cond = TCG_COND_TSTEQ, .reg = cpu_cc_dst, + .mask = -1, .imm = (1ull << (8 << size)) - 1 }; + } } } } From 9309b53e835f5d1d7795d81399cda7ecf99321e1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Oct 2023 04:17:09 +0200 Subject: [PATCH 0504/2884] target/i386: use TSTEQ/TSTNE to check flags The new conditions obviously come in handy when testing individual bits of EFLAGS, and they make it possible to remove the .mask field of CCPrepare. Lowering to shift+and is done by the optimizer if necessary. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4735f084d4..62ba21c1d7 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -996,8 +996,8 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) case CC_OP_EFLAGS: case CC_OP_SARB ... CC_OP_SARQ: /* CC_SRC & 1 */ - return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = CC_C }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, + .reg = cpu_cc_src, .mask = -1, .imm = CC_C }; default: /* The need to compute only C from CC_OP_DYNAMIC is important @@ -1014,8 +1014,8 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) { gen_compute_eflags(s); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_P }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .mask = -1, .imm = CC_P }; } /* compute eflags.S to reg */ @@ -1029,8 +1029,8 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) case CC_OP_ADCX: case CC_OP_ADOX: case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_S }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .mask = -1, .imm = CC_S }; case CC_OP_CLR: case CC_OP_POPCNT: return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; @@ -1058,8 +1058,8 @@ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) .reg = cpu_cc_src, .mask = -1 }; default: gen_compute_eflags(s); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_O }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .mask = -1, .imm = CC_O }; } } @@ -1074,8 +1074,8 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) case CC_OP_ADCX: case CC_OP_ADOX: case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_Z }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .mask = -1, .imm = CC_Z }; case CC_OP_CLR: return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; case CC_OP_POPCNT: @@ -1153,8 +1153,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) break; case JCC_BE: gen_compute_eflags(s); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_Z | CC_C }; + cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .mask = -1, .imm = CC_Z | CC_C }; break; case JCC_S: cc = gen_prepare_eflags_s(s, reg); @@ -1168,8 +1168,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) reg = s->tmp0; } tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_O }; + cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, + .mask = -1, .imm = CC_O }; break; default: case JCC_LE: @@ -1178,8 +1178,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) reg = s->tmp0; } tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_O | CC_Z }; + cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, + .mask = -1, .imm = CC_O | CC_Z }; break; } break; From e995f3f9442ce0cb869737ce100c3ebbba651809 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 27 Oct 2023 04:17:09 +0200 Subject: [PATCH 0505/2884] target/i386: remove mask from CCPrepare With the introduction of TSTEQ and TSTNE the .mask field is always -1, so remove all the now-unnecessary code. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 81 +++++++++++++------------------------ 1 file changed, 27 insertions(+), 54 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 62ba21c1d7..9aecd415b3 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -923,7 +923,6 @@ typedef struct CCPrepare { TCGv reg; TCGv reg2; target_ulong imm; - target_ulong mask; bool use_reg2; bool no_setcond; } CCPrepare; @@ -931,9 +930,9 @@ typedef struct CCPrepare { static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size) { if (size == MO_TL) { - return (CCPrepare) { .cond = TCG_COND_LT, .reg = src, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_LT, .reg = src }; } else { - return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = src, .mask = -1, + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = src, .imm = 1ull << ((8 << size) - 1) }; } } @@ -962,17 +961,17 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); add_sub: return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, - .reg2 = t1, .mask = -1, .use_reg2 = true }; + .reg2 = t1, .use_reg2 = true }; case CC_OP_LOGICB ... CC_OP_LOGICQ: case CC_OP_CLR: case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NEVER }; case CC_OP_INCB ... CC_OP_INCQ: case CC_OP_DECB ... CC_OP_DECQ: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; case CC_OP_SHLB ... CC_OP_SHLQ: /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ @@ -981,23 +980,23 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) case CC_OP_MULB ... CC_OP_MULQ: return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = -1 }; + .reg = cpu_cc_src }; case CC_OP_BMILGB ... CC_OP_BMILGQ: size = s->cc_op - CC_OP_BMILGB; t0 = gen_ext_tl(reg, cpu_cc_src, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0 }; case CC_OP_ADCX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; case CC_OP_EFLAGS: case CC_OP_SARB ... CC_OP_SARQ: /* CC_SRC & 1 */ return (CCPrepare) { .cond = TCG_COND_TSTNE, - .reg = cpu_cc_src, .mask = -1, .imm = CC_C }; + .reg = cpu_cc_src, .imm = CC_C }; default: /* The need to compute only C from CC_OP_DYNAMIC is important @@ -1006,7 +1005,7 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_op); return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; } } @@ -1015,7 +1014,7 @@ static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) { gen_compute_eflags(s); return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, - .mask = -1, .imm = CC_P }; + .imm = CC_P }; } /* compute eflags.S to reg */ @@ -1030,10 +1029,10 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) case CC_OP_ADOX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, - .mask = -1, .imm = CC_S }; + .imm = CC_S }; case CC_OP_CLR: case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NEVER }; default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; @@ -1049,17 +1048,16 @@ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) case CC_OP_ADOX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; case CC_OP_CLR: case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NEVER }; case CC_OP_MULB ... CC_OP_MULQ: - return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src }; default: gen_compute_eflags(s); return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, - .mask = -1, .imm = CC_O }; + .imm = CC_O }; } } @@ -1075,21 +1073,19 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) case CC_OP_ADOX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, - .mask = -1, .imm = CC_Z }; + .imm = CC_Z }; case CC_OP_CLR: - return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_ALWAYS }; case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src, - .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src }; default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; if (size == MO_TL) { - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst, - .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst }; } else { return (CCPrepare) { .cond = TCG_COND_TSTEQ, .reg = cpu_cc_dst, - .mask = -1, .imm = (1ull << (8 << size)) - 1 }; + .imm = (1ull << (8 << size)) - 1 }; } } } @@ -1117,7 +1113,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) gen_extu(size, s->tmp4); t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4, - .reg2 = t0, .mask = -1, .use_reg2 = true }; + .reg2 = t0, .use_reg2 = true }; break; case JCC_L: @@ -1130,7 +1126,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) gen_exts(size, s->tmp4); t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true); cc = (CCPrepare) { .cond = cond, .reg = s->tmp4, - .reg2 = t0, .mask = -1, .use_reg2 = true }; + .reg2 = t0, .use_reg2 = true }; break; default: @@ -1154,7 +1150,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) case JCC_BE: gen_compute_eflags(s); cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, - .mask = -1, .imm = CC_Z | CC_C }; + .imm = CC_Z | CC_C }; break; case JCC_S: cc = gen_prepare_eflags_s(s, reg); @@ -1169,7 +1165,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) } tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, - .mask = -1, .imm = CC_O }; + .imm = CC_O }; break; default: case JCC_LE: @@ -1179,7 +1175,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) } tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, - .mask = -1, .imm = CC_O | CC_Z }; + .imm = CC_O | CC_Z }; break; } break; @@ -1204,16 +1200,6 @@ static void gen_setcc1(DisasContext *s, int b, TCGv reg) return; } - if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 && - cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) { - tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask)); - tcg_gen_andi_tl(reg, reg, 1); - return; - } - if (cc.mask != -1) { - tcg_gen_andi_tl(reg, cc.reg, cc.mask); - cc.reg = reg; - } if (cc.use_reg2) { tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); } else { @@ -1232,10 +1218,6 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, s->T0); - if (cc.mask != -1) { - tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); - cc.reg = s->T0; - } if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); } else { @@ -1251,10 +1233,6 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) CCPrepare cc = gen_prepare_cc(s, b, s->T0); gen_update_cc_op(s); - if (cc.mask != -1) { - tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); - cc.reg = s->T0; - } set_cc_op(s, CC_OP_DYNAMIC); if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); @@ -2519,11 +2497,6 @@ static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) { CCPrepare cc = gen_prepare_cc(s, b, s->T1); - if (cc.mask != -1) { - TCGv t0 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cc.reg, cc.mask); - cc.reg = t0; - } if (!cc.use_reg2) { cc.reg2 = tcg_constant_tl(cc.imm); } From 64ddadc6bb80376da2a818b38ae6a51fe1b7f5f2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Apr 2024 12:29:52 +0200 Subject: [PATCH 0506/2884] target/i386: cc_op is not dynamic in gen_jcc1 Resetting cc_op to CC_OP_DYNAMIC should be done at control flow junctions, which is not the case here. This translation block is ending and the only effect of calling set_cc_op() would be a discard of s->cc_srcT. This discard is useless (it's a temporary, not a global) and in fact prevents gen_prepare_cc from returning s->cc_srcT. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 9aecd415b3..3f1d2858fc 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1227,13 +1227,13 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) /* Generate a conditional jump to label 'l1' according to jump opcode value 'b'. In the fast case, T0 is guaranteed not to be used. - A translation block must end soon. */ + One or both of the branches will call gen_jmp_rel, so ensure + cc_op is clean. */ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, s->T0); gen_update_cc_op(s); - set_cc_op(s, CC_OP_DYNAMIC); if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); } else { From bbba9594e84f60707558cce9cd3e4d70b9bd0fec Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Apr 2024 12:29:52 +0200 Subject: [PATCH 0507/2884] target/i386: cleanup cc_op changes for REP/REPZ/REPNZ gen_update_cc_op must be called before control flow splits. Do it where the jump on ECX!=0 is translated. On the other hand, remove the call before gen_jcc1, which takes care of it already, and explain why REPZ/REPNZ need not use CC_OP_DYNAMIC---the translation block ends before any control-flow-dependent cc_op could be observed. Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3f1d2858fc..466fee38c0 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1242,11 +1242,15 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) } /* XXX: does not work with gdbstub "ice" single step - not a - serious problem */ + serious problem. The caller can jump to the returned label + to stop the REP but, if the flags have changed, it has to call + gen_update_cc_op before doing so. */ static TCGLabel *gen_jz_ecx_string(DisasContext *s) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); + + gen_update_cc_op(s); gen_op_jnz_ecx(s, l1); gen_set_label(l2); gen_jmp_rel_csize(s, 0, 1); @@ -1342,7 +1346,6 @@ static void gen_repz(DisasContext *s, MemOp ot, void (*fn)(DisasContext *s, MemOp ot)) { TCGLabel *l2; - gen_update_cc_op(s); l2 = gen_jz_ecx_string(s); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); @@ -1364,15 +1367,18 @@ static void gen_repz2(DisasContext *s, MemOp ot, int nz, void (*fn)(DisasContext *s, MemOp ot)) { TCGLabel *l2; - gen_update_cc_op(s); l2 = gen_jz_ecx_string(s); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_update_cc_op(s); gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); if (s->repz_opt) { gen_op_jz_ecx(s, l2); } + /* + * Only one iteration is done at a time, so the translation + * block ends unconditionally after this instruction and there + * is no control flow junction - no need to set CC_OP_DYNAMIC. + */ gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } From dd17322be7c6aa07ba792d661f1920c717ad5c94 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Apr 2024 12:29:52 +0200 Subject: [PATCH 0508/2884] target/i386: pull cc_op update to callers of gen_jmp_rel{,_csize} gen_update_cc_op must be called before control flow splits. Doing it in gen_jmp_rel{,_csize} may hide bugs, instead assert that cc_op is clean---even if that means a few more calls to gen_update_cc_op(). With this new invariant, setting cc_op to CC_OP_DYNAMIC is unnecessary since the caller should have done it. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 466fee38c0..024da6d88e 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2853,6 +2853,8 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) target_ulong new_pc = s->pc + diff; target_ulong new_eip = new_pc - s->cs_base; + assert(!s->cc_op_dirty); + /* In 64-bit mode, operand size is fixed at 64 bits. */ if (!CODE64(s)) { if (ot == MO_16) { @@ -2866,9 +2868,6 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) } new_eip &= mask; - gen_update_cc_op(s); - set_cc_op(s, CC_OP_DYNAMIC); - if (tb_cflags(s->base.tb) & CF_PCREL) { tcg_gen_addi_tl(cpu_eip, cpu_eip, new_pc - s->pc_save); /* @@ -5146,6 +5145,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) : (int16_t)insn_get(env, s, MO_16)); gen_push_v(s, eip_next_tl(s)); gen_bnd_jmp(s); + gen_update_cc_op(s); gen_jmp_rel(s, dflag, diff, 0); } break; @@ -5169,6 +5169,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) ? (int32_t)insn_get(env, s, MO_32) : (int16_t)insn_get(env, s, MO_16)); gen_bnd_jmp(s); + gen_update_cc_op(s); gen_jmp_rel(s, dflag, diff, 0); } break; @@ -5189,6 +5190,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) case 0xeb: /* jmp Jb */ { int diff = (int8_t)insn_get(env, s, MO_8); + gen_update_cc_op(s); gen_jmp_rel(s, dflag, diff, 0); } break; From bccb0c138e5138c86169216bce280b1ebde44e1b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 21 Oct 2023 09:35:58 +0200 Subject: [PATCH 0509/2884] target/i386: extend cc_* when using them to compute flags Instead of using s->tmp0 or s->tmp4 as the result, just extend the cc_* registers in place. It is harmless and, if multiple setcc instructions are used, the optimizer will be able to remove the redundant ones. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 44 +++++++++++++++---------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 024da6d88e..7292908adc 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -940,28 +940,24 @@ static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size) /* compute eflags.C to reg */ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) { - TCGv t0, t1; MemOp size; switch (s->cc_op) { case CC_OP_SUBB ... CC_OP_SUBQ: /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ size = s->cc_op - CC_OP_SUBB; - t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - /* If no temporary was used, be careful not to alias t1 and t0. */ - t0 = t1 == cpu_cc_src ? s->tmp0 : reg; - tcg_gen_mov_tl(t0, s->cc_srcT); - gen_extu(size, t0); - goto add_sub; + gen_ext_tl(s->cc_srcT, s->cc_srcT, size, false); + gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false); + return (CCPrepare) { .cond = TCG_COND_LTU, .reg = s->cc_srcT, + .reg2 = cpu_cc_src, .use_reg2 = true }; case CC_OP_ADDB ... CC_OP_ADDQ: /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ size = s->cc_op - CC_OP_ADDB; - t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); - add_sub: - return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, - .reg2 = t1, .use_reg2 = true }; + gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size, false); + gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false); + return (CCPrepare) { .cond = TCG_COND_LTU, .reg = cpu_cc_dst, + .reg2 = cpu_cc_src, .use_reg2 = true }; case CC_OP_LOGICB ... CC_OP_LOGICQ: case CC_OP_CLR: @@ -984,8 +980,8 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) case CC_OP_BMILGB ... CC_OP_BMILGQ: size = s->cc_op - CC_OP_BMILGB; - t0 = gen_ext_tl(reg, cpu_cc_src, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0 }; + gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false); + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src }; case CC_OP_ADCX: case CC_OP_ADCOX: @@ -1098,7 +1094,6 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) int inv, jcc_op, cond; MemOp size; CCPrepare cc; - TCGv t0; inv = b & 1; jcc_op = (b >> 1) & 7; @@ -1109,24 +1104,21 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) size = s->cc_op - CC_OP_SUBB; switch (jcc_op) { case JCC_BE: - tcg_gen_mov_tl(s->tmp4, s->cc_srcT); - gen_extu(size, s->tmp4); - t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4, - .reg2 = t0, .use_reg2 = true }; + gen_ext_tl(s->cc_srcT, s->cc_srcT, size, false); + gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false); + cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->cc_srcT, + .reg2 = cpu_cc_src, .use_reg2 = true }; break; - case JCC_L: cond = TCG_COND_LT; goto fast_jcc_l; case JCC_LE: cond = TCG_COND_LE; fast_jcc_l: - tcg_gen_mov_tl(s->tmp4, s->cc_srcT); - gen_exts(size, s->tmp4); - t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true); - cc = (CCPrepare) { .cond = cond, .reg = s->tmp4, - .reg2 = t0, .use_reg2 = true }; + gen_ext_tl(s->cc_srcT, s->cc_srcT, size, true); + gen_ext_tl(cpu_cc_src, cpu_cc_src, size, true); + cc = (CCPrepare) { .cond = cond, .reg = s->cc_srcT, + .reg2 = cpu_cc_src, .use_reg2 = true }; break; default: From 89e4e65ac0efd8d55d47fb7150d801ca4586872e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Apr 2024 11:57:15 +0200 Subject: [PATCH 0510/2884] target/i386: do not use s->T0 and s->T1 as scratch registers for CCPrepare Instead of using s->T0 or s->T1, create a scratch register when computing the C, NC, L or LE conditions. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 7292908adc..dae9553fca 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -998,6 +998,9 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) /* The need to compute only C from CC_OP_DYNAMIC is important in efficiently implementing e.g. INC at the start of a TB. */ gen_update_cc_op(s); + if (!reg) { + reg = tcg_temp_new(); + } gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_op); return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, @@ -1152,8 +1155,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) break; case JCC_L: gen_compute_eflags(s); - if (reg == cpu_cc_src) { - reg = s->tmp0; + if (!reg || reg == cpu_cc_src) { + reg = tcg_temp_new(); } tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, @@ -1162,8 +1165,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) default: case JCC_LE: gen_compute_eflags(s); - if (reg == cpu_cc_src) { - reg = s->tmp0; + if (!reg || reg == cpu_cc_src) { + reg = tcg_temp_new(); } tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, @@ -1208,7 +1211,7 @@ static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) value 'b'. In the fast case, T0 is guaranteed not to be used. */ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) { - CCPrepare cc = gen_prepare_cc(s, b, s->T0); + CCPrepare cc = gen_prepare_cc(s, b, NULL); if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); @@ -1223,7 +1226,7 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) cc_op is clean. */ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) { - CCPrepare cc = gen_prepare_cc(s, b, s->T0); + CCPrepare cc = gen_prepare_cc(s, b, NULL); gen_update_cc_op(s); if (cc.use_reg2) { @@ -2493,7 +2496,7 @@ static void gen_jcc(DisasContext *s, int b, int diff) static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) { - CCPrepare cc = gen_prepare_cc(s, b, s->T1); + CCPrepare cc = gen_prepare_cc(s, b, NULL); if (!cc.use_reg2) { cc.reg2 = tcg_constant_tl(cc.imm); From ccfabc00e01d41a296502a437ac8aa50139720fc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Apr 2024 11:21:01 +0200 Subject: [PATCH 0511/2884] target/i386: clarify the "reg" argument of functions returning CCPrepare Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index dae9553fca..8e0289ca41 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -937,7 +937,7 @@ static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size) } } -/* compute eflags.C to reg */ +/* compute eflags.C, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) { MemOp size; @@ -1008,7 +1008,7 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) } } -/* compute eflags.P to reg */ +/* compute eflags.P, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) { gen_compute_eflags(s); @@ -1016,7 +1016,7 @@ static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) .imm = CC_P }; } -/* compute eflags.S to reg */ +/* compute eflags.S, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) { switch (s->cc_op) { @@ -1040,7 +1040,7 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) } } -/* compute eflags.O to reg */ +/* compute eflags.O, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) { switch (s->cc_op) { @@ -1060,7 +1060,7 @@ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) } } -/* compute eflags.Z to reg */ +/* compute eflags.Z, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) { switch (s->cc_op) { @@ -1090,8 +1090,9 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) } } -/* perform a conditional store into register 'reg' according to jump opcode - value 'b'. In the fast case, T0 is guaranteed not to be used. */ +/* return how to compute jump opcode 'b'. 'reg' can be clobbered + * if needed; it may be used for CCPrepare.reg if that will + * provide more freedom in the translation of a subsequent setcond. */ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) { int inv, jcc_op, cond; From 8b5de7ea562ff142b324d996bad4118248b7274f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Apr 2024 14:14:10 +0200 Subject: [PATCH 0512/2884] target/i386: cleanup *gen_eob* Create a new wrapper for syscall/sysret, and do not go through multiple layers of wrappers. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 8e0289ca41..f018d6303a 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2783,7 +2783,7 @@ static void gen_bnd_jmp(DisasContext *s) If RECHECK_TF, emit a rechecking helper for #DB, ignoring the state of S->TF. This is used by the syscall/sysret insns. */ static void -do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) +gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) { bool inhibit_reset; @@ -2817,28 +2817,27 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) } static inline void -gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf) +gen_eob_syscall(DisasContext *s) { - do_gen_eob_worker(s, inhibit, recheck_tf, false); + gen_eob_worker(s, false, true, false); } -/* End of block. - If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. */ -static void gen_eob_inhibit_irq(DisasContext *s, bool inhibit) +/* End of block. Set HF_INHIBIT_IRQ_MASK if it isn't already set. */ +static void gen_eob_inhibit_irq(DisasContext *s) { - gen_eob_worker(s, inhibit, false); + gen_eob_worker(s, true, false, false); } /* End of block, resetting the inhibit irq flag. */ static void gen_eob(DisasContext *s) { - gen_eob_worker(s, false, false); + gen_eob_worker(s, false, false, false); } /* Jump to register */ static void gen_jr(DisasContext *s) { - do_gen_eob_worker(s, false, false, true); + gen_eob_worker(s, false, false, true); } /* Jump to eip+diff, truncating the result to OT. */ @@ -5591,7 +5590,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) gen_set_eflags(s, IF_MASK); /* interruptions are enabled only the first insn after sti */ gen_update_eip_next(s); - gen_eob_inhibit_irq(s, true); + gen_eob_inhibit_irq(s); } break; case 0x62: /* bound */ @@ -5725,7 +5724,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) /* TF handling for the syscall insn is different. The TF bit is checked after the syscall insn completes. This allows #DB to not be generated after one has entered CPL0 if TF is set in FMASK. */ - gen_eob_worker(s, false, true); + gen_eob_syscall(s); break; case 0x107: /* sysret */ /* For Intel SYSRET is only valid in long mode */ @@ -5744,7 +5743,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) checked after the sysret insn completes. This allows #DB to be generated "as if" the syscall insn in userspace has just completed. */ - gen_eob_worker(s, false, true); + gen_eob_syscall(s); } break; case 0x1a2: /* cpuid */ @@ -7059,7 +7058,7 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_EOB_INHIBIT_IRQ: gen_update_cc_op(dc); gen_update_eip_cur(dc); - gen_eob_inhibit_irq(dc, true); + gen_eob_inhibit_irq(dc); break; case DISAS_JUMP: gen_jr(dc); From 445457693cbb5bf50765d5d29ca35dd357f8dbaa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 11 Sep 2022 11:23:55 +0200 Subject: [PATCH 0513/2884] target/i386: reintroduce debugging mechanism Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 3 +++ target/i386/tcg/translate.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 426c459412..3fc6485d74 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1689,6 +1689,9 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) X86DecodeFunc decode_func = decode_root; uint8_t cc_live; +#ifdef CONFIG_USER_ONLY + if (limit) { --limit; } +#endif s->has_modrm = false; next_byte: diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index f018d6303a..6a0c74c225 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2969,6 +2969,9 @@ static void gen_sty_env_A0(DisasContext *s, int offset, bool align) tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop); } +static bool first = true; +static unsigned long limit; + #include "decode-new.h" #include "emit.c.inc" #include "decode-new.c.inc" @@ -3124,15 +3127,39 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) prefixes = 0; + if (first) { + const char *limit_str = getenv("QEMU_I386_LIMIT"); + limit = limit_str ? atol(limit_str) : -1; + first = false; + } + bool use_new = true; +#ifdef CONFIG_USER_ONLY + use_new &= limit > 0; +#endif + next_byte: s->prefix = prefixes; b = x86_ldub_code(env, s); /* Collect prefixes. */ switch (b) { default: +#ifndef CONFIG_USER_ONLY + use_new &= b <= limit; +#endif + if (use_new && 0) { + disas_insn_new(s, cpu, b); + return true; + } break; case 0x0f: b = x86_ldub_code(env, s) + 0x100; +#ifndef CONFIG_USER_ONLY + use_new &= b <= limit; +#endif + if (use_new && 0) { + disas_insn_new(s, cpu, b); + return true; + } break; case 0xf3: prefixes |= PREFIX_REPZ; From cc1d28bdbe0f0abe75ff94518458e090137c5ea6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 20 Oct 2023 00:30:21 +0200 Subject: [PATCH 0514/2884] target/i386: move 00-5F opcodes to new decoder Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 120 ++++++++++++++++++ target/i386/tcg/emit.c.inc | 202 +++++++++++++++++++++++++++++++ target/i386/tcg/translate.c | 2 +- 3 files changed, 323 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 3fc6485d74..1e792426ff 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -121,6 +121,8 @@ #define X86_OP_GROUP2(op, op0, s0, op1, s1, ...) \ X86_OP_GROUP3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) +#define X86_OP_GROUPw(op, op0, s0, ...) \ + X86_OP_GROUP3(op, op0, s0, None, None, None, None, ## __VA_ARGS__) #define X86_OP_GROUP0(op, ...) \ X86_OP_GROUP3(op, None, None, None, None, None, None, ## __VA_ARGS__) @@ -140,12 +142,23 @@ .op3 = X86_TYPE_I, .s3 = X86_SIZE_b, \ ## __VA_ARGS__) +/* + * Short forms that are mostly useful for ALU opcodes and other + * one-byte opcodes. For vector instructions it is usually + * clearer to write all three operands explicitly, because the + * corresponding gen_* function will use OP_PTRn rather than s->T0 + * and s->T1. + */ +#define X86_OP_ENTRYrr(op, op0, s0, op1, s1, ...) \ + X86_OP_ENTRY3(op, None, None, op0, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_ENTRY2(op, op0, s0, op1, s1, ...) \ X86_OP_ENTRY3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_ENTRYw(op, op0, s0, ...) \ X86_OP_ENTRY3(op, op0, s0, None, None, None, None, ## __VA_ARGS__) #define X86_OP_ENTRYr(op, op0, s0, ...) \ X86_OP_ENTRY3(op, None, None, None, None, op0, s0, ## __VA_ARGS__) +#define X86_OP_ENTRY1(op, op0, s0, ...) \ + X86_OP_ENTRY3(op, op0, s0, 2op, s0, None, None, ## __VA_ARGS__) #define X86_OP_ENTRY0(op, ...) \ X86_OP_ENTRY3(op, None, None, None, None, None, None, ## __VA_ARGS__) @@ -1096,7 +1109,114 @@ static void decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint } static const X86OpEntry opcodes_root[256] = { + [0x00] = X86_OP_ENTRY2(ADD, E,b, G,b, lock), + [0x01] = X86_OP_ENTRY2(ADD, E,v, G,v, lock), + [0x02] = X86_OP_ENTRY2(ADD, G,b, E,b, lock), + [0x03] = X86_OP_ENTRY2(ADD, G,v, E,v, lock), + [0x04] = X86_OP_ENTRY2(ADD, 0,b, I,b, lock), /* AL, Ib */ + [0x05] = X86_OP_ENTRY2(ADD, 0,v, I,z, lock), /* rAX, Iz */ + [0x06] = X86_OP_ENTRYr(PUSH, ES, w, chk(i64)), + [0x07] = X86_OP_ENTRYw(POP, ES, w, chk(i64)), + + [0x10] = X86_OP_ENTRY2(ADC, E,b, G,b, lock), + [0x11] = X86_OP_ENTRY2(ADC, E,v, G,v, lock), + [0x12] = X86_OP_ENTRY2(ADC, G,b, E,b, lock), + [0x13] = X86_OP_ENTRY2(ADC, G,v, E,v, lock), + [0x14] = X86_OP_ENTRY2(ADC, 0,b, I,b, lock), /* AL, Ib */ + [0x15] = X86_OP_ENTRY2(ADC, 0,v, I,z, lock), /* rAX, Iz */ + [0x16] = X86_OP_ENTRYr(PUSH, SS, w, chk(i64)), + [0x17] = X86_OP_ENTRYw(POP, SS, w, chk(i64)), + + [0x20] = X86_OP_ENTRY2(AND, E,b, G,b, lock), + [0x21] = X86_OP_ENTRY2(AND, E,v, G,v, lock), + [0x22] = X86_OP_ENTRY2(AND, G,b, E,b, lock), + [0x23] = X86_OP_ENTRY2(AND, G,v, E,v, lock), + [0x24] = X86_OP_ENTRY2(AND, 0,b, I,b, lock), /* AL, Ib */ + [0x25] = X86_OP_ENTRY2(AND, 0,v, I,z, lock), /* rAX, Iz */ + [0x26] = {}, + [0x27] = X86_OP_ENTRY0(DAA, chk(i64)), + + [0x30] = X86_OP_ENTRY2(XOR, E,b, G,b, lock), + [0x31] = X86_OP_ENTRY2(XOR, E,v, G,v, lock), + [0x32] = X86_OP_ENTRY2(XOR, G,b, E,b, lock), + [0x33] = X86_OP_ENTRY2(XOR, G,v, E,v, lock), + [0x34] = X86_OP_ENTRY2(XOR, 0,b, I,b, lock), /* AL, Ib */ + [0x35] = X86_OP_ENTRY2(XOR, 0,v, I,z, lock), /* rAX, Iz */ + [0x36] = {}, + [0x37] = X86_OP_ENTRY0(AAA, chk(i64)), + + [0x40] = X86_OP_ENTRY1(INC, 0,v, chk(i64)), + [0x41] = X86_OP_ENTRY1(INC, 1,v, chk(i64)), + [0x42] = X86_OP_ENTRY1(INC, 2,v, chk(i64)), + [0x43] = X86_OP_ENTRY1(INC, 3,v, chk(i64)), + [0x44] = X86_OP_ENTRY1(INC, 4,v, chk(i64)), + [0x45] = X86_OP_ENTRY1(INC, 5,v, chk(i64)), + [0x46] = X86_OP_ENTRY1(INC, 6,v, chk(i64)), + [0x47] = X86_OP_ENTRY1(INC, 7,v, chk(i64)), + + [0x50] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x51] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x52] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x53] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x54] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x55] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x56] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x57] = X86_OP_ENTRYr(PUSH, LoBits,d64), + + + [0x08] = X86_OP_ENTRY2(OR, E,b, G,b, lock), + [0x09] = X86_OP_ENTRY2(OR, E,v, G,v, lock), + [0x0A] = X86_OP_ENTRY2(OR, G,b, E,b, lock), + [0x0B] = X86_OP_ENTRY2(OR, G,v, E,v, lock), + [0x0C] = X86_OP_ENTRY2(OR, 0,b, I,b, lock), /* AL, Ib */ + [0x0D] = X86_OP_ENTRY2(OR, 0,v, I,z, lock), /* rAX, Iz */ + [0x0E] = X86_OP_ENTRYr(PUSH, CS, w, chk(i64)), [0x0F] = X86_OP_GROUP0(0F), + + [0x18] = X86_OP_ENTRY2(SBB, E,b, G,b, lock), + [0x19] = X86_OP_ENTRY2(SBB, E,v, G,v, lock), + [0x1A] = X86_OP_ENTRY2(SBB, G,b, E,b, lock), + [0x1B] = X86_OP_ENTRY2(SBB, G,v, E,v, lock), + [0x1C] = X86_OP_ENTRY2(SBB, 0,b, I,b, lock), /* AL, Ib */ + [0x1D] = X86_OP_ENTRY2(SBB, 0,v, I,z, lock), /* rAX, Iz */ + [0x1E] = X86_OP_ENTRYr(PUSH, DS, w, chk(i64)), + [0x1F] = X86_OP_ENTRYw(POP, DS, w, chk(i64)), + + [0x28] = X86_OP_ENTRY2(SUB, E,b, G,b, lock), + [0x29] = X86_OP_ENTRY2(SUB, E,v, G,v, lock), + [0x2A] = X86_OP_ENTRY2(SUB, G,b, E,b, lock), + [0x2B] = X86_OP_ENTRY2(SUB, G,v, E,v, lock), + [0x2C] = X86_OP_ENTRY2(SUB, 0,b, I,b, lock), /* AL, Ib */ + [0x2D] = X86_OP_ENTRY2(SUB, 0,v, I,z, lock), /* rAX, Iz */ + [0x2E] = {}, + [0x2F] = X86_OP_ENTRY0(DAS, chk(i64)), + + [0x38] = X86_OP_ENTRYrr(SUB, E,b, G,b), + [0x39] = X86_OP_ENTRYrr(SUB, E,v, G,v), + [0x3A] = X86_OP_ENTRYrr(SUB, G,b, E,b), + [0x3B] = X86_OP_ENTRYrr(SUB, G,v, E,v), + [0x3C] = X86_OP_ENTRYrr(SUB, 0,b, I,b), /* AL, Ib */ + [0x3D] = X86_OP_ENTRYrr(SUB, 0,v, I,z), /* rAX, Iz */ + [0x3E] = {}, + [0x3F] = X86_OP_ENTRY0(AAS, chk(i64)), + + [0x48] = X86_OP_ENTRY1(DEC, 0,v, chk(i64)), + [0x49] = X86_OP_ENTRY1(DEC, 1,v, chk(i64)), + [0x4A] = X86_OP_ENTRY1(DEC, 2,v, chk(i64)), + [0x4B] = X86_OP_ENTRY1(DEC, 3,v, chk(i64)), + [0x4C] = X86_OP_ENTRY1(DEC, 4,v, chk(i64)), + [0x4D] = X86_OP_ENTRY1(DEC, 5,v, chk(i64)), + [0x4E] = X86_OP_ENTRY1(DEC, 6,v, chk(i64)), + [0x4F] = X86_OP_ENTRY1(DEC, 7,v, chk(i64)), + + [0x58] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x59] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5A] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5B] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5C] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5D] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5E] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5F] = X86_OP_ENTRYw(POP, LoBits,d64), }; #undef mmx diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 0e00f6635d..a64186b895 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -352,6 +352,20 @@ static void prepare_update2_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op) decode->cc_op = op; } +static void prepare_update_cc_incdec(X86DecodedInsn *decode, DisasContext *s, CCOp op) +{ + gen_compute_eflags_c(s, s->T1); + prepare_update2_cc(decode, s, op); +} + +static void prepare_update3_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op, TCGv reg) +{ + decode->cc_src2 = reg; + decode->cc_src = s->T1; + decode->cc_dst = s->T0; + decode->cc_op = op; +} + static void gen_store_sse(DisasContext *s, X86DecodedInsn *decode, int src_ofs) { MemOp ot = decode->op[0].ot; @@ -1040,6 +1054,37 @@ static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod VSIB_AVX(VPGATHERD, vpgatherd) VSIB_AVX(VPGATHERQ, vpgatherq) +static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_aaa(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_AAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_aas(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_ADC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv c_in = tcg_temp_new(); + + gen_compute_eflags_c(s, c_in); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(s->T0, c_in, s->T1); + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s->T0, s->T0, s->T1); + tcg_gen_add_tl(s->T0, s->T0, c_in); + } + prepare_update3_cc(decode, s, CC_OP_ADCB + ot, c_in); +} + /* ADCX/ADOX do not have memory operands and can use set_cc_op. */ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) { @@ -1093,11 +1138,37 @@ static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADCX); } +static void gen_ADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s->T0, s->T0, s->T1); + } + prepare_update2_cc(decode, s, CC_OP_ADDB + ot); +} + static void gen_ADOX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADOX); } +static void gen_AND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_and_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_and_tl(s->T0, s->T0, s->T1); + } + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); +} + static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1331,6 +1402,34 @@ static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } +static void gen_DAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_daa(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_DAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_das(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_DEC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + tcg_gen_movi_tl(s->T1, -1); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s->T0, s->T0, s->T1); + } + prepare_update_cc_incdec(decode, s, CC_OP_DECB + ot); +} + static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_helper_emms(tcg_env); @@ -1349,6 +1448,20 @@ static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); } +static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + tcg_gen_movi_tl(s->T1, 1); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s->T0, s->T0, s->T1); + } + prepare_update_cc_incdec(decode, s, CC_OP_INCB + ot); +} + static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); @@ -1501,6 +1614,19 @@ static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_or_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_or_tl(s->T0, s->T0, s->T1); + } + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); +} + static void gen_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -1744,6 +1870,18 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } +static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = gen_pop_T0(s); + if (decode->op[0].has_ea) { + /* NOTE: order is important for MMU exceptions */ + gen_op_st_v(s, ot, s->T0, s->A0); + decode->op[0].unit = X86_OP_SKIP; + } + /* NOTE: writing back registers after update is important for pop %sp */ + gen_pop_update(s, ot); +} + static void gen_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -1890,6 +2028,11 @@ static void gen_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } +static void gen_PUSH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_push_v(s, s->T1); +} + static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1924,6 +2067,28 @@ static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_sar_tl(s->T0, s->T0, s->T1); } +static void gen_SBB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv c_in = tcg_temp_new(); + + gen_compute_eflags_c(s, c_in); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(s->T0, s->T1, c_in); + tcg_gen_neg_tl(s->T0, s->T0); + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + /* + * TODO: SBB reg, reg could use gen_prepare_eflags_c followed by + * negsetcond, and CC_OP_SUBB as the cc_op. + */ + tcg_gen_sub_tl(s->T0, s->T0, s->T1); + tcg_gen_sub_tl(s->T0, s->T0, c_in); + } + prepare_update3_cc(decode, s, CC_OP_SBBB + ot, c_in); +} + static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); @@ -2011,6 +2176,22 @@ static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); } +static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_neg_tl(s->T0, s->T1); + tcg_gen_atomic_fetch_add_tl(s->cc_srcT, s->A0, s->T0, + s->mem_index, ot | MO_LE); + tcg_gen_sub_tl(s->T0, s->cc_srcT, s->T1); + } else { + tcg_gen_mov_tl(s->cc_srcT, s->T0); + tcg_gen_sub_tl(s->T0, s->T0, s->T1); + } + prepare_update2_cc(decode, s, CC_OP_SUBB + ot); +} + static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { assert(!s->vex_l); @@ -2490,3 +2671,24 @@ static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *de tcg_gen_gvec_dup_imm(MO_64, offset, 16, 16, 0); } } + +static void gen_XOR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + /* special case XOR reg, reg */ + if (decode->op[1].unit == X86_OP_INT && + decode->op[2].unit == X86_OP_INT && + decode->op[1].n == decode->op[2].n) { + tcg_gen_movi_tl(s->T0, 0); + decode->cc_op = CC_OP_CLR; + } else { + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_xor_tl(s->T0, s->T0, s->T1); + } + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); + } +} diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 6a0c74c225..8bc1828a6b 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3146,7 +3146,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) #ifndef CONFIG_USER_ONLY use_new &= b <= limit; #endif - if (use_new && 0) { + if (use_new && b <= 0x5f) { disas_insn_new(s, cpu, b); return true; } From 442e38c4fb9b89fb2b3c9ac3e661c93aa1dc40f9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Oct 2023 08:49:12 +0200 Subject: [PATCH 0515/2884] target/i386: extract gen_far_call/jmp, reordering temporaries Extract the code into new functions, and swap T0/T1 so that T0 corresponds to the first immediate in the instruction stream. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 93 +++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 8bc1828a6b..e5672df9b9 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2512,12 +2512,13 @@ static inline void gen_op_movl_T0_seg(DisasContext *s, X86Seg seg_reg) offsetof(CPUX86State,segs[seg_reg].selector)); } -static inline void gen_op_movl_seg_T0_vm(DisasContext *s, X86Seg seg_reg) +static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) { - tcg_gen_ext16u_tl(s->T0, s->T0); - tcg_gen_st32_tl(s->T0, tcg_env, + TCGv selector = tcg_temp_new(); + tcg_gen_ext16u_tl(selector, seg); + tcg_gen_st32_tl(selector, tcg_env, offsetof(CPUX86State,segs[seg_reg].selector)); - tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4); + tcg_gen_shli_tl(cpu_seg_base[seg_reg], selector, 4); } /* move T0 to seg_reg and compute if the CPU state may change. Never @@ -2537,13 +2538,45 @@ static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg) s->base.is_jmp = DISAS_EOB_NEXT; } } else { - gen_op_movl_seg_T0_vm(s, seg_reg); + gen_op_movl_seg_real(s, seg_reg, s->T0); if (seg_reg == R_SS) { s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } } } +static void gen_far_call(DisasContext *s) +{ + TCGv_i32 new_cs = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(new_cs, s->T1); + if (PE(s) && !VM86(s)) { + gen_helper_lcall_protected(tcg_env, new_cs, s->T0, + tcg_constant_i32(s->dflag - 1), + eip_next_tl(s)); + } else { + TCGv_i32 new_eip = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(new_eip, s->T0); + gen_helper_lcall_real(tcg_env, new_cs, new_eip, + tcg_constant_i32(s->dflag - 1), + eip_next_i32(s)); + } + s->base.is_jmp = DISAS_JUMP; +} + +static void gen_far_jmp(DisasContext *s) +{ + if (PE(s) && !VM86(s)) { + TCGv_i32 new_cs = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(new_cs, s->T1); + gen_helper_ljmp_protected(tcg_env, new_cs, s->T0, + eip_next_tl(s)); + } else { + gen_op_movl_seg_real(s, R_CS, s->T1); + gen_op_jmp_v(s, s->T0); + } + s->base.is_jmp = DISAS_JUMP; +} + static void gen_svm_check_intercept(DisasContext *s, uint32_t type) { /* no SVM activated; fast case */ @@ -3654,23 +3687,10 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (mod == 3) { goto illegal_op; } - gen_op_ld_v(s, ot, s->T1, s->A0); + gen_op_ld_v(s, ot, s->T0, s->A0); gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T0, s->A0); - do_lcall: - if (PE(s) && !VM86(s)) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lcall_protected(tcg_env, s->tmp2_i32, s->T1, - tcg_constant_i32(dflag - 1), - eip_next_tl(s)); - } else { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_lcall_real(tcg_env, s->tmp2_i32, s->tmp3_i32, - tcg_constant_i32(dflag - 1), - eip_next_i32(s)); - } - s->base.is_jmp = DISAS_JUMP; + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_far_call(s); break; case 4: /* jmp Ev */ if (dflag == MO_16) { @@ -3684,19 +3704,10 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (mod == 3) { goto illegal_op; } - gen_op_ld_v(s, ot, s->T1, s->A0); + gen_op_ld_v(s, ot, s->T0, s->A0); gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T0, s->A0); - do_ljmp: - if (PE(s) && !VM86(s)) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_ljmp_protected(tcg_env, s->tmp2_i32, s->T1, - eip_next_tl(s)); - } else { - gen_op_movl_seg_T0_vm(s, R_CS); - gen_op_jmp_v(s, s->T1); - } - s->base.is_jmp = DISAS_JUMP; + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_far_jmp(s); break; case 6: /* push Ev */ gen_push_v(s, s->T0); @@ -5136,7 +5147,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) /* pop selector */ gen_add_A0_im(s, 1 << dflag); gen_op_ld_v(s, dflag, s->T0, s->A0); - gen_op_movl_seg_T0_vm(s, R_CS); + gen_op_movl_seg_real(s, R_CS, s->T0); /* add stack offset */ gen_stack_update(s, val + (2 << dflag)); } @@ -5181,10 +5192,11 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) offset = insn_get(env, s, ot); selector = insn_get(env, s, MO_16); - tcg_gen_movi_tl(s->T0, selector); - tcg_gen_movi_tl(s->T1, offset); + tcg_gen_movi_tl(s->T0, offset); + tcg_gen_movi_tl(s->T1, selector); } - goto do_lcall; + gen_far_call(s); + break; case 0xe9: /* jmp im */ { int diff = (dflag != MO_16 @@ -5205,10 +5217,11 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) offset = insn_get(env, s, ot); selector = insn_get(env, s, MO_16); - tcg_gen_movi_tl(s->T0, selector); - tcg_gen_movi_tl(s->T1, offset); + tcg_gen_movi_tl(s->T0, offset); + tcg_gen_movi_tl(s->T1, selector); } - goto do_ljmp; + gen_far_jmp(s); + break; case 0xeb: /* jmp Jb */ { int diff = (int8_t)insn_get(env, s, MO_8); From 2666fbd271fdaae8b2956baf0a096e77c9b3c793 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Oct 2023 08:41:39 +0200 Subject: [PATCH 0516/2884] target/i386: allow instructions with more than one immediate While keeping decode->immediate for convenience and for 4-operand instructions, store the immediate in X86DecodedOp as well. This enables instructions with more than one immediate such as ENTER. It can also be used for far calls and jumps. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 2 +- target/i386/tcg/decode-new.h | 17 ++++++++++++----- target/i386/tcg/emit.c.inc | 4 +++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 1e792426ff..c6fd7a053b 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1473,7 +1473,7 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, case X86_TYPE_I: /* Immediate */ case X86_TYPE_J: /* Relative offset for a jump */ op->unit = X86_OP_IMM; - decode->immediate = insn_get_signed(env, s, op->ot); + decode->immediate = op->imm = insn_get_signed(env, s, op->ot); break; case X86_TYPE_L: /* The upper 4 bits of the immediate select a 128-bit register */ diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 15e6bfef4b..8ffde8d1cd 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -271,16 +271,23 @@ typedef struct X86DecodedOp { bool has_ea; int offset; /* For MMX and SSE */ - /* - * This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR, - * do not access directly! - */ - TCGv_ptr v_ptr; + union { + target_ulong imm; + /* + * This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR, + * do not access directly! + */ + TCGv_ptr v_ptr; + }; } X86DecodedOp; struct X86DecodedInsn { X86OpEntry e; X86DecodedOp op[3]; + /* + * Rightmost immediate, for convenience since most instructions have + * one (and also for 4-operand instructions). + */ target_ulong immediate; AddressParts mem; diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index a64186b895..fc065caae7 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -259,7 +259,7 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) } break; case X86_OP_IMM: - tcg_gen_movi_tl(v, decode->immediate); + tcg_gen_movi_tl(v, op->imm); break; case X86_OP_MMX: @@ -283,6 +283,8 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) static TCGv_ptr op_ptr(X86DecodedInsn *decode, int opn) { X86DecodedOp *op = &decode->op[opn]; + + assert(op->unit == X86_OP_MMX || op->unit == X86_OP_SSE); if (op->v_ptr) { return op->v_ptr; } From 5e9e21bcc4d22dbd4473cab494a1beefbd3786a9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Oct 2023 10:55:16 +0200 Subject: [PATCH 0517/2884] target/i386: move 60-BF opcodes to new decoder Compared to the old decoder, the main differences in translation are for the little-used ARPL instruction. IMUL is adjusted a bit to share more code to produce flags, but is otherwise very similar. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 185 ++++++++++++++++++ target/i386/tcg/decode-new.h | 3 + target/i386/tcg/emit.c.inc | 323 +++++++++++++++++++++++++++++++ target/i386/tcg/translate.c | 9 +- 4 files changed, 518 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index c6fd7a053b..55fc0173a4 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -33,6 +33,22 @@ * ("cannot encode 16-bit or 32-bit size in 64-bit mode") as modifiers of the * "v" or "z" sizes. The decoder simply makes them separate operand sizes. * + * The manual lists immediate far destinations as Ap (technically an implicit + * argument). The decoder splits them into two immediates, using "Ip" for + * the offset part (that comes first in the instruction stream) and "Iw" for + * the segment/selector part. The size of the offset is given by s->dflag + * and the instructions are illegal in 64-bit mode, so the choice of "Ip" + * is somewhat arbitrary; "Iv" or "Iz" would work just as well. + * + * Operand types + * ------------- + * + * Immediates are almost always signed or masked away in helpers. Two + * common exceptions are IN/OUT and absolute jumps. For these, there is + * an additional custom operand type "I_unsigned". Alternatively, the + * mask could be applied (and the original sign-extended value would be + * optimized away by TCG) in the emitter function. + * * Vector operands * --------------- * @@ -151,6 +167,8 @@ */ #define X86_OP_ENTRYrr(op, op0, s0, op1, s1, ...) \ X86_OP_ENTRY3(op, None, None, op0, s0, op1, s1, ## __VA_ARGS__) +#define X86_OP_ENTRYwr(op, op0, s0, op1, s1, ...) \ + X86_OP_ENTRY3(op, op0, s0, None, None, op1, s1, ## __VA_ARGS__) #define X86_OP_ENTRY2(op, op0, s0, op1, s1, ...) \ X86_OP_ENTRY3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_ENTRYw(op, op0, s0, ...) \ @@ -163,6 +181,7 @@ X86_OP_ENTRY3(op, None, None, None, None, None, None, ## __VA_ARGS__) #define cpuid(feat) .cpuid = X86_FEAT_##feat, +#define noseg .special = X86_SPECIAL_NoSeg, #define xchg .special = X86_SPECIAL_Locked, #define lock .special = X86_SPECIAL_HasLock, #define mmx .special = X86_SPECIAL_MMX, @@ -209,6 +228,8 @@ #define p_66_f3_f2 .valid_prefix = P_66 | P_F3 | P_F2, #define p_00_66_f3_f2 .valid_prefix = P_00 | P_66 | P_F3 | P_F2, +#define UNKNOWN_OPCODE ((X86OpEntry) {}) + static uint8_t get_modrm(DisasContext *s, CPUX86State *env) { if (!s->has_modrm) { @@ -1108,6 +1129,51 @@ static void decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint do_decode_0F(s, env, entry, b); } +static void decode_63(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry arpl = X86_OP_ENTRY2(ARPL, E,w, G,w, chk(prot)); + static const X86OpEntry mov = X86_OP_ENTRY3(MOV, G,v, E,v, None, None); + static const X86OpEntry movsxd = X86_OP_ENTRY3(MOV, G,v, E,d, None, None, sextT0); + if (!CODE64(s)) { + *entry = arpl; + } else if (REX_W(s)) { + *entry = movsxd; + } else { + *entry = mov; + } +} + +static void decode_group1(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86GenFunc group1_gen[8] = { + gen_ADD, gen_OR, gen_ADC, gen_SBB, gen_AND, gen_SUB, gen_XOR, gen_SUB, + }; + int op = (get_modrm(s, env) >> 3) & 7; + entry->gen = group1_gen[op]; + + if (op == 7) { + /* prevent writeback for CMP */ + entry->op1 = entry->op0; + entry->op0 = X86_TYPE_None; + entry->s0 = X86_SIZE_None; + } else { + entry->special = X86_SPECIAL_HasLock; + } +} + +static void decode_group1A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + int op = (get_modrm(s, env) >> 3) & 7; + if (op != 0) { + /* could be XOP prefix too */ + *entry = UNKNOWN_OPCODE; + } else { + entry->gen = gen_POP; + /* The address must use the value of ESP after the pop. */ + s->popl_esp_hack = 1 << mo_pushpop(s, s->dflag); + } +} + static const X86OpEntry opcodes_root[256] = { [0x00] = X86_OP_ENTRY2(ADD, E,b, G,b, lock), [0x01] = X86_OP_ENTRY2(ADD, E,v, G,v, lock), @@ -1163,6 +1229,60 @@ static const X86OpEntry opcodes_root[256] = { [0x56] = X86_OP_ENTRYr(PUSH, LoBits,d64), [0x57] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x60] = X86_OP_ENTRY0(PUSHA, chk(i64)), + [0x61] = X86_OP_ENTRY0(POPA, chk(i64)), + [0x62] = X86_OP_ENTRYrr(BOUND, G,v, M,a, chk(i64)), + [0x63] = X86_OP_GROUP0(63), + [0x64] = {}, + [0x65] = {}, + [0x66] = {}, + [0x67] = {}, + + [0x70] = X86_OP_ENTRYr(Jcc, J,b), + [0x71] = X86_OP_ENTRYr(Jcc, J,b), + [0x72] = X86_OP_ENTRYr(Jcc, J,b), + [0x73] = X86_OP_ENTRYr(Jcc, J,b), + [0x74] = X86_OP_ENTRYr(Jcc, J,b), + [0x75] = X86_OP_ENTRYr(Jcc, J,b), + [0x76] = X86_OP_ENTRYr(Jcc, J,b), + [0x77] = X86_OP_ENTRYr(Jcc, J,b), + + [0x80] = X86_OP_GROUP2(group1, E,b, I,b), + [0x81] = X86_OP_GROUP2(group1, E,v, I,z), + [0x82] = X86_OP_GROUP2(group1, E,b, I,b, chk(i64)), + [0x83] = X86_OP_GROUP2(group1, E,v, I,b), + [0x84] = X86_OP_ENTRYrr(AND, E,b, G,b), + [0x85] = X86_OP_ENTRYrr(AND, E,v, G,v), + [0x86] = X86_OP_ENTRY2(XCHG, E,b, G,b, xchg), + [0x87] = X86_OP_ENTRY2(XCHG, E,v, G,v, xchg), + + [0x90] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x91] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x92] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x93] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x94] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x95] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x96] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x97] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + + [0xA0] = X86_OP_ENTRY3(MOV, 0,b, O,b, None, None), /* AL, Ob */ + [0xA1] = X86_OP_ENTRY3(MOV, 0,v, O,v, None, None), /* rAX, Ov */ + [0xA2] = X86_OP_ENTRY3(MOV, O,b, 0,b, None, None), /* Ob, AL */ + [0xA3] = X86_OP_ENTRY3(MOV, O,v, 0,v, None, None), /* Ov, rAX */ + [0xA4] = X86_OP_ENTRYrr(MOVS, Y,b, X,b), + [0xA5] = X86_OP_ENTRYrr(MOVS, Y,v, X,v), + [0xA6] = X86_OP_ENTRYrr(CMPS, Y,b, X,b), + [0xA7] = X86_OP_ENTRYrr(CMPS, Y,v, X,v), + + [0xB0] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB1] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB2] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB3] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB4] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB5] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB6] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB7] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0x08] = X86_OP_ENTRY2(OR, E,b, G,b, lock), [0x09] = X86_OP_ENTRY2(OR, E,v, G,v, lock), @@ -1217,6 +1337,61 @@ static const X86OpEntry opcodes_root[256] = { [0x5D] = X86_OP_ENTRYw(POP, LoBits,d64), [0x5E] = X86_OP_ENTRYw(POP, LoBits,d64), [0x5F] = X86_OP_ENTRYw(POP, LoBits,d64), + + [0x68] = X86_OP_ENTRYr(PUSH, I,z), + [0x69] = X86_OP_ENTRY3(IMUL3, G,v, E,v, I,z, sextT0), + [0x6A] = X86_OP_ENTRYr(PUSH, I,b), + [0x6B] = X86_OP_ENTRY3(IMUL3, G,v, E,v, I,b, sextT0), + [0x6C] = X86_OP_ENTRYrr(INS, Y,b, 2,w), /* DX */ + [0x6D] = X86_OP_ENTRYrr(INS, Y,z, 2,w), /* DX */ + [0x6E] = X86_OP_ENTRYrr(OUTS, X,b, 2,w), /* DX */ + [0x6F] = X86_OP_ENTRYrr(OUTS, X,z, 2,w), /* DX */ + + [0x78] = X86_OP_ENTRYr(Jcc, J,b), + [0x79] = X86_OP_ENTRYr(Jcc, J,b), + [0x7A] = X86_OP_ENTRYr(Jcc, J,b), + [0x7B] = X86_OP_ENTRYr(Jcc, J,b), + [0x7C] = X86_OP_ENTRYr(Jcc, J,b), + [0x7D] = X86_OP_ENTRYr(Jcc, J,b), + [0x7E] = X86_OP_ENTRYr(Jcc, J,b), + [0x7F] = X86_OP_ENTRYr(Jcc, J,b), + + [0x88] = X86_OP_ENTRY3(MOV, E,b, G,b, None, None), + [0x89] = X86_OP_ENTRY3(MOV, E,v, G,v, None, None), + [0x8A] = X86_OP_ENTRY3(MOV, G,b, E,b, None, None), + [0x8B] = X86_OP_ENTRY3(MOV, G,v, E,v, None, None), + [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None), + [0x8D] = X86_OP_ENTRY3(LEA, G,v, M,v, None, None, noseg), + [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,v, None, None), + [0x8F] = X86_OP_GROUPw(group1A, E,v), + + [0x98] = X86_OP_ENTRY1(CBW, 0,v), /* rAX */ + [0x99] = X86_OP_ENTRY3(CWD, 2,v, 0,v, None, None), /* rDX, rAX */ + [0x9A] = X86_OP_ENTRYrr(CALLF, I_unsigned,p, I_unsigned,w, chk(i64)), + [0x9B] = X86_OP_ENTRY0(WAIT), + [0x9C] = X86_OP_ENTRY0(PUSHF, chk(vm86_iopl) svm(PUSHF)), + [0x9D] = X86_OP_ENTRY0(POPF, chk(vm86_iopl) svm(POPF)), + [0x9E] = X86_OP_ENTRY0(SAHF), + [0x9F] = X86_OP_ENTRY0(LAHF), + + [0xA8] = X86_OP_ENTRYrr(AND, 0,b, I,b), /* AL, Ib */ + [0xA9] = X86_OP_ENTRYrr(AND, 0,v, I,z), /* rAX, Iz */ + [0xAA] = X86_OP_ENTRY3(STOS, Y,b, 0,b, None, None), + [0xAB] = X86_OP_ENTRY3(STOS, Y,v, 0,v, None, None), + /* Manual writeback because REP LODS (!) has to write EAX/RAX after every LODS. */ + [0xAC] = X86_OP_ENTRYr(LODS, X,b), + [0xAD] = X86_OP_ENTRYr(LODS, X,v), + [0xAE] = X86_OP_ENTRYrr(SCAS, 0,b, Y,b), + [0xAF] = X86_OP_ENTRYrr(SCAS, 0,v, Y,v), + + [0xB8] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xB9] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xBA] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xBB] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xBC] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xBD] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xBE] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xBF] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), }; #undef mmx @@ -1476,6 +1651,11 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, decode->immediate = op->imm = insn_get_signed(env, s, op->ot); break; + case X86_TYPE_I_unsigned: /* Immediate */ + op->unit = X86_OP_IMM; + decode->immediate = op->imm = insn_get(env, s, op->ot); + break; + case X86_TYPE_L: /* The upper 4 bits of the immediate select a 128-bit register */ op->n = insn_get(env, s, op->ot) >> 4; break; @@ -2037,6 +2217,11 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) assert(decode.op[1].unit == X86_OP_INT); break; + case X86_SPECIAL_NoSeg: + decode.mem.def_seg = -1; + s->override = -1; + break; + default: break; } diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 8ffde8d1cd..790ad5e1d0 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -48,6 +48,7 @@ typedef enum X86OpType { /* Custom */ X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */ + X86_TYPE_I_unsigned, /* Immediate, zero-extended */ X86_TYPE_2op, /* 2-operand RMW instruction */ X86_TYPE_LoBits, /* encoded in bits 0-2 of the operand + REX.B */ X86_TYPE_0, /* Hard-coded GPRs (RAX..RDI) */ @@ -165,6 +166,8 @@ typedef enum X86InsnSpecial { /* Always locked if it has a memory operand (XCHG) */ X86_SPECIAL_Locked, + /* Do not apply segment base to effective address */ + X86_SPECIAL_NoSeg, /* * Rd/Mb or Rd/Mw in the manual: register operand 0 is treated as 32 bits * (and writeback zero-extends it to 64 bits if applicable). PREFIX_DATA diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index fc065caae7..5834f8ea0f 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1179,6 +1179,27 @@ static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } +static void gen_ARPL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv zf = tcg_temp_new(); + TCGv flags = tcg_temp_new(); + + gen_mov_eflags(s, flags); + + /* Compute adjusted DST in T1, merging in SRC[RPL]. */ + tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 0, 2); + + /* Z flag set if DST[RPL] < SRC[RPL] */ + tcg_gen_setcond_tl(TCG_COND_LTU, zf, s->T0, s->T1); + tcg_gen_deposit_tl(flags, flags, zf, ctz32(CC_Z), 1); + + /* Place maximum RPL in DST */ + tcg_gen_umax_tl(s->T0, s->T0, s->T1); + + decode->cc_src = flags; + decode->cc_op = CC_OP_EFLAGS; +} + static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1243,6 +1264,17 @@ static void gen_BLSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) set_cc_op(s, CC_OP_BMILGB + ot); } +static void gen_BOUND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 op = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(op, s->T0); + if (decode->op[1].ot == MO_16) { + gen_helper_boundw(tcg_env, s->A0, op); + } else { + gen_helper_boundl(tcg_env, s->A0, op); + } +} + static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1263,6 +1295,18 @@ static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } +static void gen_CALLF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_far_call(s); +} + +static void gen_CBW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp src_ot = decode->op[0].ot - 1; + + tcg_gen_ext_tl(s->T0, s->T0, src_ot | MO_SIGN); +} + static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGLabel *label_top = gen_new_label(); @@ -1366,6 +1410,18 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec decode->cc_op = CC_OP_SUBB + ot; } +static void gen_CMPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & PREFIX_REPNZ) { + gen_repz_cmps(s, ot, 1); + } else if (s->prefix & PREFIX_REPZ) { + gen_repz_cmps(s, ot, 0); + } else { + gen_cmps(s, ot); + } +} + static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1404,6 +1460,13 @@ static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } +static void gen_CWD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int shift = 8 << decode->op[0].ot; + + tcg_gen_sextract_tl(s->T0, s->T0, shift - 1, 1); +} + static void gen_DAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_update_cc_op(s); @@ -1450,6 +1513,69 @@ static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); } +static void gen_IMUL3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv cc_src_rhs; + + switch (ot) { + case MO_16: + /* s->T0 already sign-extended */ + tcg_gen_ext16s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext16s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_32: +#ifdef TARGET_X86_64 + if (TCG_TARGET_REG_BITS == 64) { + /* + * This produces fewer TCG ops, and better code if flags are needed, + * but it requires a 64-bit multiply even if they are not. Use it + * only if the target has 64-bits registers. + * + * s->T0 is already sign-extended. + */ + tcg_gen_ext32s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext32s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + } else { + /* Variant that only needs a 32-bit widening multiply. */ + TCGv_i32 hi = tcg_temp_new_i32(); + TCGv_i32 lo = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(lo, s->T0); + tcg_gen_trunc_tl_i32(hi, s->T1); + tcg_gen_muls2_i32(lo, hi, lo, hi); + tcg_gen_extu_i32_tl(s->T0, lo); + + cc_src_rhs = tcg_temp_new(); + tcg_gen_extu_i32_tl(cc_src_rhs, hi); + /* Compare the high part to the sign bit of the truncated result */ + tcg_gen_sari_i32(lo, lo, 31); + tcg_gen_extu_i32_tl(s->T1, lo); + } + break; + + case MO_64: +#endif + cc_src_rhs = tcg_temp_new(); + tcg_gen_muls2_tl(s->T0, cc_src_rhs, s->T0, s->T1); + /* Compare the high part to the sign bit of the truncated result */ + tcg_gen_sari_tl(s->T1, s->T0, TARGET_LONG_BITS - 1); + break; + + default: + g_assert_not_reached(); + } + + tcg_gen_sub_tl(s->T1, s->T1, cc_src_rhs); + prepare_update2_cc(decode, s, CC_OP_MULB + ot); +} + static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1464,6 +1590,26 @@ static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update_cc_incdec(decode, s, CC_OP_INCB + ot); } +static void gen_INS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, + SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) { + return; + } + + translator_io_start(&s->base); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_ins(s, ot); + } else { + gen_ins(s, ot); + } +} + static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); @@ -1477,12 +1623,50 @@ static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec gen_helper_insertq_r(tcg_env, OP_PTR0, OP_PTR2); } +static void gen_Jcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_bnd_jmp(s); + gen_jcc(s, decode->b & 0xf, decode->immediate); +} + +static void gen_LAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { + return gen_illegal_opcode(s); + } + gen_compute_eflags(s); + /* Note: gen_compute_eflags() only gives the condition codes */ + tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); + tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); +} + static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); } +static void gen_LEA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_mov_tl(s->T0, s->A0); +} + +static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_lods(s, ot); + } else { + gen_lods(s, ot); + } +} + +static void gen_MOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + /* nothing to do! */ +} +#define gen_NOP gen_MOV + static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_DS, s->override); @@ -1590,6 +1774,16 @@ static void gen_MOVq_dq(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod return gen_MOVQ(s, env, decode); } +static void gen_MOVS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_movs(s, ot); + } else { + gen_movs(s, ot); + } +} + static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1629,6 +1823,25 @@ static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } +static void gen_OUTS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, SVM_IOIO_STR_MASK)) { + return; + } + + translator_io_start(&s->base); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_outs(s, ot); + } else { + gen_outs(s, ot); + } +} + static void gen_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -1884,6 +2097,33 @@ static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_pop_update(s, ot); } +static void gen_POPA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_popa(s); +} + +static void gen_POPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot; + int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK; + + if (CPL(s) == 0) { + mask |= IF_MASK | IOPL_MASK; + } else if (CPL(s) <= IOPL(s)) { + mask |= IF_MASK; + } + if (s->dflag == MO_16) { + mask &= 0xffff; + } + + ot = gen_pop_T0(s); + gen_helper_write_eflags(tcg_env, s->T0, tcg_constant_i32(mask)); + gen_pop_update(s, ot); + set_cc_op(s, CC_OP_EFLAGS); + /* abort translation because TF/AC flag may change */ + s->base.is_jmp = DISAS_EOB_NEXT; +} + static void gen_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -2035,6 +2275,18 @@ static void gen_PUSH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_push_v(s, s->T1); } +static void gen_PUSHA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pusha(s); +} + +static void gen_PUSHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_read_eflags(s->T0, tcg_env); + gen_push_v(s, s->T0); +} + static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2059,6 +2311,18 @@ static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_SAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { + return gen_illegal_opcode(s); + } + tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8); + gen_compute_eflags(s); + tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); + tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); + tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); +} + static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2091,6 +2355,18 @@ static void gen_SBB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update3_cc(decode, s, CC_OP_SBBB + ot, c_in); } +static void gen_SCAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & PREFIX_REPNZ) { + gen_repz_scas(s, ot, 1); + } else if (s->prefix & PREFIX_REPZ) { + gen_repz_scas(s, ot, 0); + } else { + gen_scas(s, ot); + } +} + static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); @@ -2178,6 +2454,16 @@ static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); } +static void gen_STOS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_stos(s, ot); + } else { + gen_stos(s, ot); + } +} + static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -2674,6 +2960,43 @@ static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *de } } +static void gen_WAIT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == (HF_MP_MASK | HF_TS_MASK)) { + gen_NM_exception(s); + } else { + /* needs to be treated as I/O because of ferr_irq */ + translator_io_start(&s->base); + gen_helper_fwait(tcg_env); + } +} + +static void gen_XCHG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (decode->b == 0x90 && !REX_B(s)) { + if (s->prefix & PREFIX_REPZ) { + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_pause(tcg_env, cur_insn_len_i32(s)); + s->base.is_jmp = DISAS_NORETURN; + } + /* No writeback. */ + decode->op[0].unit = X86_OP_SKIP; + return; + } + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_xchg_tl(s->T0, s->A0, s->T1, + s->mem_index, decode->op[0].ot | MO_LE); + /* now store old value into register operand */ + gen_op_mov_reg_v(s, decode->op[2].ot, decode->op[2].n, s->T0); + } else { + /* move destination value into source operand, source preserved in T1 */ + gen_op_mov_reg_v(s, decode->op[2].ot, decode->op[2].n, s->T0); + tcg_gen_mov_tl(s->T0, s->T1); + } +} + static void gen_XOR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { /* special case XOR reg, reg */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index e5672df9b9..4f47c40fb0 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1288,7 +1288,11 @@ static void gen_cmps(DisasContext *s, MemOp ot) gen_string_movl_A0_EDI(s); gen_op_ld_v(s, ot, s->T1, s->A0); gen_string_movl_A0_ESI(s); - gen_op(s, OP_CMPL, ot, OR_TMP0); + gen_op_ld_v(s, ot, s->T0, s->A0); + tcg_gen_mov_tl(cpu_cc_src, s->T1); + tcg_gen_mov_tl(s->cc_srcT, s->T0); + tcg_gen_sub_tl(cpu_cc_dst, s->T0, s->T1); + set_cc_op(s, CC_OP_SUBB + ot); dshift = gen_compute_Dshift(s, ot); gen_op_add_reg(s, s->aflag, R_ESI, dshift); @@ -3122,6 +3126,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) s->pc = s->base.pc_next; s->override = -1; + s->popl_esp_hack = 0; #ifdef TARGET_X86_64 s->rex_r = 0; s->rex_x = 0; @@ -3179,7 +3184,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) #ifndef CONFIG_USER_ONLY use_new &= b <= limit; #endif - if (use_new && b <= 0x5f) { + if (use_new && b <= 0xbf) { disas_insn_new(s, cpu, b); return true; } From b603136402d2ae217b5051cd041a8591f09b04ba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 28 Feb 2024 11:15:43 +0100 Subject: [PATCH 0518/2884] target/i386: generalize gen_movl_seg_T0 In the new decoder it is sometimes easier to put the segment in T1 instead of T0, usually because another operand was loaded by common code in T0. Genrealize gen_movl_seg_T0 to allow using any source. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 4 ++-- target/i386/tcg/translate.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 5834f8ea0f..56ce0d2a9f 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -306,8 +306,8 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv case X86_OP_SKIP: break; case X86_OP_SEG: - /* Note that gen_movl_seg_T0 takes care of interrupt shadow and TF. */ - gen_movl_seg_T0(s, op->n); + /* Note that gen_movl_seg takes care of interrupt shadow and TF. */ + gen_movl_seg(s, op->n, s->T0); break; case X86_OP_INT: if (op->has_ea) { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4f47c40fb0..e09bfdaa39 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2525,12 +2525,12 @@ static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) tcg_gen_shli_tl(cpu_seg_base[seg_reg], selector, 4); } -/* move T0 to seg_reg and compute if the CPU state may change. Never +/* move SRC to seg_reg and compute if the CPU state may change. Never call this function with seg_reg == R_CS */ -static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg) +static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src) { if (PE(s) && !VM86(s)) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, src); gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), s->tmp2_i32); /* abort translation because the addseg value may change or because ss32 may change. For R_SS, translation must always @@ -2542,7 +2542,7 @@ static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg) s->base.is_jmp = DISAS_EOB_NEXT; } } else { - gen_op_movl_seg_real(s, seg_reg, s->T0); + gen_op_movl_seg_real(s, seg_reg, src); if (seg_reg == R_SS) { s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } @@ -4084,13 +4084,13 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; reg = b >> 3; ot = gen_pop_T0(s); - gen_movl_seg_T0(s, reg); + gen_movl_seg(s, reg, s->T0); gen_pop_update(s, ot); break; case 0x1a1: /* pop fs */ case 0x1a9: /* pop gs */ ot = gen_pop_T0(s); - gen_movl_seg_T0(s, (b >> 3) & 7); + gen_movl_seg(s, (b >> 3) & 7, s->T0); gen_pop_update(s, ot); break; @@ -4137,7 +4137,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) if (reg >= 6 || reg == R_CS) goto illegal_op; gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_movl_seg_T0(s, reg); + gen_movl_seg(s, reg, s->T0); break; case 0x8c: /* mov Gv, seg */ modrm = x86_ldub_code(env, s); @@ -4323,7 +4323,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) gen_add_A0_im(s, 1 << ot); /* load the segment first to handle exceptions properly */ gen_op_ld_v(s, MO_16, s->T0, s->A0); - gen_movl_seg_T0(s, op); + gen_movl_seg(s, op, s->T0); /* then put the data */ gen_op_mov_reg_v(s, ot, reg, s->T1); break; From d7c41a60d0c5228d5adfc73c83facb1307a1d45e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 21 Oct 2023 17:36:34 +0200 Subject: [PATCH 0519/2884] target/i386: move C0-FF opcodes to new decoder (except for x87) The shift instructions are rewritten instead of reusing code from the old decoder. Rotates use CC_OP_ADCOX more extensively and generally rely more on the optimizer, so that the code generators are shared between the immediate-count and variable-count cases. In particular, this makes gen_RCL and gen_RCR pretty efficient for the count == 1 case, which becomes (apart from a few extra movs) something like: (compute_cc_all if needed) // save old value for OF calculation mov cc_src2, T0 // the bulk of RCL is just this! deposit T0, cc_src, T0, 1, TARGET_LONG_BITS - 1 // compute carry shr cc_dst, cc_src2, length - 1 and cc_dst, cc_dst, 1 // compute overflow xor cc_src2, cc_src2, T0 extract cc_src2, cc_src2, length - 1, 1 32-bit MUL and IMUL are also slightly more efficient on 64-bit hosts. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 153 +++++ target/i386/tcg/decode-new.h | 2 + target/i386/tcg/emit.c.inc | 1021 +++++++++++++++++++++++++++++- target/i386/tcg/translate.c | 23 +- 4 files changed, 1188 insertions(+), 11 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 55fc0173a4..b145af7a56 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -43,6 +43,12 @@ * Operand types * ------------- * + * For memory-only operands, if the emitter functions wants to rely on + * generic load and writeback, the decoder needs to know the type of the + * operand. Therefore, M is often replaced by the more specific EM and WM + * (respectively selecting an ALU operand, like the operand type E, or a + * vector operand like the operand type W). + * * Immediates are almost always signed or masked away in helpers. Two * common exceptions are IN/OUT and absolute jumps. For these, there is * an additional custom operand type "I_unsigned". Alternatively, the @@ -135,6 +141,8 @@ ## __VA_ARGS__ \ } +#define X86_OP_GROUP1(op, op0, s0, ...) \ + X86_OP_GROUP3(op, op0, s0, 2op, s0, None, None, ## __VA_ARGS__) #define X86_OP_GROUP2(op, op0, s0, op1, s1, ...) \ X86_OP_GROUP3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_GROUPw(op, op0, s0, ...) \ @@ -1174,6 +1182,83 @@ static void decode_group1A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, } } +static void decode_group2(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86GenFunc group2_gen[8] = { + gen_ROL, gen_ROR, gen_RCL, gen_RCR, + gen_SHL, gen_SHR, gen_SHL /* SAL, undocumented */, gen_SAR, + }; + int op = (get_modrm(s, env) >> 3) & 7; + entry->gen = group2_gen[op]; + if (op == 7) { + entry->special = X86_SPECIAL_SExtT0; + } else { + entry->special = X86_SPECIAL_ZExtT0; + } +} + +static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_grp3[16] = { + /* 0xf6 */ + [0x00] = X86_OP_ENTRYrr(AND, E,b, I,b), + [0x02] = X86_OP_ENTRY1(NOT, E,b, lock), + [0x03] = X86_OP_ENTRY1(NEG, E,b, lock), + [0x04] = X86_OP_ENTRYrr(MUL, E,b, 0,b, zextT0), + [0x05] = X86_OP_ENTRYrr(IMUL,E,b, 0,b, sextT0), + [0x06] = X86_OP_ENTRYr(DIV, E,b), + [0x07] = X86_OP_ENTRYr(IDIV, E,b), + + /* 0xf7 */ + [0x08] = X86_OP_ENTRYrr(AND, E,v, I,z), + [0x0a] = X86_OP_ENTRY1(NOT, E,v, lock), + [0x0b] = X86_OP_ENTRY1(NEG, E,v, lock), + [0x0c] = X86_OP_ENTRYrr(MUL, E,v, 0,v, zextT0), + [0x0d] = X86_OP_ENTRYrr(IMUL,E,v, 0,v, sextT0), + [0x0e] = X86_OP_ENTRYr(DIV, E,v), + [0x0f] = X86_OP_ENTRYr(IDIV, E,v), + }; + + int w = (*b & 1); + int reg = (get_modrm(s, env) >> 3) & 7; + + *entry = opcodes_grp3[(w << 3) | reg]; +} + +static void decode_group4_5(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_grp4_5[16] = { + /* 0xfe */ + [0x00] = X86_OP_ENTRY1(INC, E,b, lock), + [0x01] = X86_OP_ENTRY1(DEC, E,b, lock), + + /* 0xff */ + [0x08] = X86_OP_ENTRY1(INC, E,v, lock), + [0x09] = X86_OP_ENTRY1(DEC, E,v, lock), + [0x0a] = X86_OP_ENTRY3(CALL_m, None, None, E,f64, None, None, zextT0), + [0x0b] = X86_OP_ENTRYr(CALLF_m, M,p), + [0x0c] = X86_OP_ENTRY3(JMP_m, None, None, E,f64, None, None, zextT0), + [0x0d] = X86_OP_ENTRYr(JMPF_m, M,p), + [0x0e] = X86_OP_ENTRYr(PUSH, E,f64), + }; + + int w = (*b & 1); + int reg = (get_modrm(s, env) >> 3) & 7; + + *entry = opcodes_grp4_5[(w << 3) | reg]; +} + + +static void decode_group11(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + int op = (get_modrm(s, env) >> 3) & 7; + if (op != 0) { + *entry = UNKNOWN_OPCODE; + } else { + entry->gen = gen_MOV; + } +} + static const X86OpEntry opcodes_root[256] = { [0x00] = X86_OP_ENTRY2(ADD, E,b, G,b, lock), [0x01] = X86_OP_ENTRY2(ADD, E,v, G,v, lock), @@ -1283,6 +1368,38 @@ static const X86OpEntry opcodes_root[256] = { [0xB6] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), [0xB7] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xC0] = X86_OP_GROUP2(group2, E,b, I,b), + [0xC1] = X86_OP_GROUP2(group2, E,v, I,b), + [0xC2] = X86_OP_ENTRYr(RET, I,w), + [0xC3] = X86_OP_ENTRY0(RET), + [0xC4] = X86_OP_ENTRY3(LES, G,z, EM,p, None, None, chk(i64)), + [0xC5] = X86_OP_ENTRY3(LDS, G,z, EM,p, None, None, chk(i64)), + [0xC6] = X86_OP_GROUP3(group11, E,b, I,b, None, None), /* reg=000b */ + [0xC7] = X86_OP_GROUP3(group11, E,v, I,z, None, None), /* reg=000b */ + + [0xD0] = X86_OP_GROUP1(group2, E,b), + [0xD1] = X86_OP_GROUP1(group2, E,v), + [0xD2] = X86_OP_GROUP2(group2, E,b, 1,b), /* CL */ + [0xD3] = X86_OP_GROUP2(group2, E,v, 1,b), /* CL */ + [0xD4] = X86_OP_ENTRYr(AAM, I,b), + [0xD5] = X86_OP_ENTRYr(AAD, I,b), + [0xD6] = X86_OP_ENTRYw(SALC, 0,b), + [0xD7] = X86_OP_ENTRY1(XLAT, 0,b, zextT0), /* AL read/written */ + + [0xE0] = X86_OP_ENTRYr(LOOPNE, J,b), /* implicit: CX with aflag size */ + [0xE1] = X86_OP_ENTRYr(LOOPE, J,b), /* implicit: CX with aflag size */ + [0xE2] = X86_OP_ENTRYr(LOOP, J,b), /* implicit: CX with aflag size */ + [0xE3] = X86_OP_ENTRYr(JCXZ, J,b), /* implicit: CX with aflag size */ + [0xE4] = X86_OP_ENTRYwr(IN, 0,b, I_unsigned,b), /* AL */ + [0xE5] = X86_OP_ENTRYwr(IN, 0,v, I_unsigned,b), /* AX/EAX */ + [0xE6] = X86_OP_ENTRYrr(OUT, 0,b, I_unsigned,b), /* AL */ + [0xE7] = X86_OP_ENTRYrr(OUT, 0,v, I_unsigned,b), /* AX/EAX */ + + [0xF1] = X86_OP_ENTRY0(INT1, svm(ICEBP)), + [0xF4] = X86_OP_ENTRY0(HLT, chk(cpl0)), + [0xF5] = X86_OP_ENTRY0(CMC), + [0xF6] = X86_OP_GROUP1(group3, E,b), + [0xF7] = X86_OP_GROUP1(group3, E,v), [0x08] = X86_OP_ENTRY2(OR, E,b, G,b, lock), [0x09] = X86_OP_ENTRY2(OR, E,v, G,v, lock), @@ -1392,6 +1509,33 @@ static const X86OpEntry opcodes_root[256] = { [0xBD] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), [0xBE] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), [0xBF] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + + [0xC8] = X86_OP_ENTRYrr(ENTER, I,w, I,b), + [0xC9] = X86_OP_ENTRY1(LEAVE, A,d64), + [0xCA] = X86_OP_ENTRYr(RETF, I,w), + [0xCB] = X86_OP_ENTRY0(RETF), + [0xCC] = X86_OP_ENTRY0(INT3), + [0xCD] = X86_OP_ENTRYr(INT, I,b, chk(vm86_iopl)), + [0xCE] = X86_OP_ENTRY0(INTO), + [0xCF] = X86_OP_ENTRY0(IRET, chk(vm86_iopl) svm(IRET)), + + [0xE8] = X86_OP_ENTRYr(CALL, J,z_f64), + [0xE9] = X86_OP_ENTRYr(JMP, J,z_f64), + [0xEA] = X86_OP_ENTRYrr(JMPF, I_unsigned,p, I_unsigned,w, chk(i64)), + [0xEB] = X86_OP_ENTRYr(JMP, J,b), + [0xEC] = X86_OP_ENTRYwr(IN, 0,b, 2,w), /* AL, DX */ + [0xED] = X86_OP_ENTRYwr(IN, 0,v, 2,w), /* AX/EAX, DX */ + [0xEE] = X86_OP_ENTRYrr(OUT, 0,b, 2,w), /* DX, AL */ + [0xEF] = X86_OP_ENTRYrr(OUT, 0,v, 2,w), /* DX, AX/EAX */ + + [0xF8] = X86_OP_ENTRY0(CLC), + [0xF9] = X86_OP_ENTRY0(STC), + [0xFA] = X86_OP_ENTRY0(CLI, chk(iopl)), + [0xFB] = X86_OP_ENTRY0(STI, chk(iopl)), + [0xFC] = X86_OP_ENTRY0(CLD), + [0xFD] = X86_OP_ENTRY0(STD), + [0xFE] = X86_OP_GROUP1(group4_5, E,b), + [0xFF] = X86_OP_GROUP1(group4_5, E,v), }; #undef mmx @@ -1471,6 +1615,10 @@ static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp *ot = s->dflag == MO_16 ? MO_16 : MO_32; return true; + case X86_SIZE_z_f64: /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */ + *ot = !CODE64(s) && s->dflag == MO_16 ? MO_16 : MO_32; + return true; + case X86_SIZE_dq: /* SSE/AVX 128-bit */ if (e->special == X86_SPECIAL_MMX && !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { @@ -1610,8 +1758,13 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, case X86_TYPE_WM: /* modrm byte selects an XMM/YMM memory operand */ op->unit = X86_OP_SSE; + goto get_modrm_mem; + + case X86_TYPE_EM: /* modrm byte selects an ALU memory operand */ + op->unit = X86_OP_INT; /* fall through */ case X86_TYPE_M: /* modrm byte selects a memory operand */ + get_modrm_mem: modrm = get_modrm(s, env); if ((modrm >> 6) == 3) { return false; diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 790ad5e1d0..e86a5c2cb5 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -47,6 +47,7 @@ typedef enum X86OpType { X86_TYPE_Y, /* string destination */ /* Custom */ + X86_TYPE_EM, /* modrm byte selects an ALU memory operand */ X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */ X86_TYPE_I_unsigned, /* Immediate, zero-extended */ X86_TYPE_2op, /* 2-operand RMW instruction */ @@ -89,6 +90,7 @@ typedef enum X86OpSize { X86_SIZE_x, /* 128/256-bit, based on operand size */ X86_SIZE_y, /* 32/64-bit, based on operand size */ X86_SIZE_z, /* 16-bit for 16-bit operand size, else 32-bit */ + X86_SIZE_z_f64, /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */ /* Custom */ X86_SIZE_d64, diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 56ce0d2a9f..2c848fab62 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -19,6 +19,21 @@ * License along with this library; if not, see . */ +/* + * Sometimes, knowing what the backend has can produce better code. + * The exact opcode to check depends on 32- vs. 64-bit. + */ +#ifdef TARGET_X86_64 +#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 +#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i64_valid +#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i64_valid +#else +#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 +#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i32_valid +#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i32_valid +#endif + + #define ZMM_OFFSET(reg) offsetof(CPUX86State, xmm_regs[reg]) typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); @@ -45,6 +60,9 @@ typedef void (*SSEFunc_0_eppppii)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 even, TCGv_i32 odd); +static void gen_JMP_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); +static void gen_JMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); + static inline TCGv_i32 tcg_constant8u_i32(uint8_t val) { return tcg_constant_i32(val); @@ -330,6 +348,7 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv default: g_assert_not_reached(); } + op->unit = X86_OP_SKIP; } static inline int vector_len(DisasContext *s, X86DecodedInsn *decode) @@ -1063,6 +1082,22 @@ static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) set_cc_op(s, CC_OP_EFLAGS); } +static void gen_AAD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_aad(tcg_env, tcg_constant_i32(decode->immediate)); + set_cc_op(s, CC_OP_LOGICB); +} + +static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (decode->immediate == 0) { + gen_exception(s, EXCP00_DIVZ); + } else { + gen_helper_aam(tcg_env, tcg_constant_i32(decode->immediate)); + set_cc_op(s, CC_OP_LOGICB); + } +} + static void gen_AAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_update_cc_op(s); @@ -1295,11 +1330,33 @@ static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } +static void gen_CALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_push_v(s, eip_next_tl(s)); + gen_JMP(s, env, decode); +} + +static void gen_CALL_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_push_v(s, eip_next_tl(s)); + gen_JMP_m(s, env, decode); +} + static void gen_CALLF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_far_call(s); } +static void gen_CALLF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_far_call(s); +} + static void gen_CBW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp src_ot = decode->op[0].ot - 1; @@ -1307,6 +1364,28 @@ static void gen_CBW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_ext_tl(s->T0, s->T0, src_ot | MO_SIGN); } +static void gen_CLC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_compute_eflags(s); + tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); +} + +static void gen_CLD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, offsetof(CPUX86State, df)); +} + +static void gen_CLI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_reset_eflags(s, IF_MASK); +} + +static void gen_CMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_compute_eflags(s); + tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); +} + static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGLabel *label_top = gen_new_label(); @@ -1495,11 +1574,39 @@ static void gen_DEC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update_cc_incdec(decode, s, CC_OP_DECB + ot); } +static void gen_DIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + switch(ot) { + case MO_8: + gen_helper_divb_AL(tcg_env, s->T1); + break; + case MO_16: + gen_helper_divw_AX(tcg_env, s->T1); + break; + default: + case MO_32: + gen_helper_divl_EAX(tcg_env, s->T1); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_divq_EAX(tcg_env, s->T1); + break; +#endif + } +} + static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_helper_emms(tcg_env); } +static void gen_ENTER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_enter(s, decode->op[1].imm, decode->op[2].imm); +} + static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); @@ -1513,6 +1620,39 @@ static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); } +static void gen_HLT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ +#ifdef CONFIG_SYSTEM_ONLY + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_hlt(tcg_env, cur_insn_len_i32(s)); + s->base.is_jmp = DISAS_NORETURN; +#endif +} + +static void gen_IDIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + switch(ot) { + case MO_8: + gen_helper_idivb_AL(tcg_env, s->T1); + break; + case MO_16: + gen_helper_idivw_AX(tcg_env, s->T1); + break; + default: + case MO_32: + gen_helper_idivl_EAX(tcg_env, s->T1); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_idivq_EAX(tcg_env, s->T1); + break; +#endif + } +} + static void gen_IMUL3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1576,6 +1716,80 @@ static void gen_IMUL3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_MULB + ot); } +static void gen_IMUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv cc_src_rhs; + + switch (ot) { + case MO_8: + /* s->T0 already sign-extended */ + tcg_gen_ext8s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext8s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_16: + /* s->T0 already sign-extended */ + tcg_gen_ext16s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_shri_tl(s->T1, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T1); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext16s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_32: +#ifdef TARGET_X86_64 + /* s->T0 already sign-extended */ + tcg_gen_ext32s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + tcg_gen_ext32u_tl(cpu_regs[R_EAX], s->T0); + tcg_gen_shri_tl(cpu_regs[R_EDX], s->T0, 32); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext32s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_64: +#endif + tcg_gen_muls2_tl(s->T0, cpu_regs[R_EDX], s->T0, s->T1); + tcg_gen_mov_tl(cpu_regs[R_EAX], s->T0); + + /* Compare the high part to the sign bit of the truncated result */ + tcg_gen_negsetcondi_tl(TCG_COND_LT, s->T1, s->T0, 0); + cc_src_rhs = cpu_regs[R_EDX]; + break; + + default: + g_assert_not_reached(); + } + + tcg_gen_sub_tl(s->T1, s->T1, cc_src_rhs); + prepare_update2_cc(decode, s, CC_OP_MULB + ot); +} + +static void gen_IN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv_i32 port = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, SVM_IOIO_TYPE_MASK)) { + return; + } + translator_io_start(&s->base); + gen_helper_in_func(ot, s->T0, port); + gen_writeback(s, decode, 0, s->T0); + gen_bpt_io(s, port, ot); +} + static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1623,12 +1837,83 @@ static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec gen_helper_insertq_r(tcg_env, OP_PTR0, OP_PTR2); } +static void gen_INT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_interrupt(s, decode->immediate); +} + +static void gen_INT1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_exception(s, EXCP01_DB); +} + +static void gen_INT3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_interrupt(s, EXCP03_INT3); +} + +static void gen_INTO(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_into(tcg_env, cur_insn_len_i32(s)); +} + +static void gen_IRET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (!PE(s) || VM86(s)) { + gen_helper_iret_real(tcg_env, tcg_constant_i32(s->dflag - 1)); + } else { + gen_helper_iret_protected(tcg_env, tcg_constant_i32(s->dflag - 1), + eip_next_i32(s)); + } + set_cc_op(s, CC_OP_EFLAGS); + s->base.is_jmp = DISAS_EOB_ONLY; +} + static void gen_Jcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_bnd_jmp(s); gen_jcc(s, decode->b & 0xf, decode->immediate); } +static void gen_JCXZ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_jz_ecx(s, taken); + gen_conditional_jump_labels(s, decode->immediate, NULL, taken); +} + +static void gen_JMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_jmp_rel(s, s->dflag, decode->immediate, 0); +} + +static void gen_JMP_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_op_jmp_v(s, s->T0); + gen_bnd_jmp(s); + s->base.is_jmp = DISAS_JUMP; +} + +static void gen_JMPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_far_jmp(s); +} + +static void gen_JMPF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_far_jmp(s); +} + static void gen_LAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { @@ -1646,11 +1931,38 @@ static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); } +static void gen_lxx_seg(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, int seg) +{ + MemOp ot = decode->op[0].ot; + + /* Offset already in s->T0. */ + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + + /* load the segment here to handle exceptions properly */ + gen_movl_seg(s, seg, s->T1); +} + +static void gen_LDS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, env, decode, R_DS); +} + static void gen_LEA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { tcg_gen_mov_tl(s->T0, s->A0); } +static void gen_LEAVE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_leave(s); +} + +static void gen_LES(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, env, decode, R_ES); +} + static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1661,6 +1973,40 @@ static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_LOOP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jnz_ecx(s, taken); + gen_conditional_jump_labels(s, decode->immediate, NULL, taken); +} + +static void gen_LOOPE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + TCGLabel *not_taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jz_ecx(s, not_taken); + gen_jcc1(s, (JCC_Z << 1), taken); /* jz taken */ + gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); +} + +static void gen_LOOPNE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + TCGLabel *not_taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jz_ecx(s, not_taken); + gen_jcc1(s, (JCC_Z << 1) | 1, taken); /* jnz taken */ + gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); +} + static void gen_MOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { /* nothing to do! */ @@ -1784,6 +2130,57 @@ static void gen_MOVS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_MUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + switch (ot) { + case MO_8: + /* s->T0 already zero-extended */ + tcg_gen_ext8u_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_andi_tl(s->T1, s->T0, 0xff00); + decode->cc_dst = s->T0; + decode->cc_src = s->T1; + break; + + case MO_16: + /* s->T0 already zero-extended */ + tcg_gen_ext16u_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_shri_tl(s->T1, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T1); + decode->cc_dst = s->T0; + decode->cc_src = s->T1; + break; + + case MO_32: +#ifdef TARGET_X86_64 + /* s->T0 already zero-extended */ + tcg_gen_ext32u_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + tcg_gen_ext32u_tl(cpu_regs[R_EAX], s->T0); + tcg_gen_shri_tl(cpu_regs[R_EDX], s->T0, 32); + decode->cc_dst = cpu_regs[R_EAX]; + decode->cc_src = cpu_regs[R_EDX]; + break; + + case MO_64: +#endif + tcg_gen_mulu2_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->T0, s->T1); + decode->cc_dst = cpu_regs[R_EAX]; + decode->cc_src = cpu_regs[R_EDX]; + break; + + default: + g_assert_not_reached(); + } + + decode->cc_op = CC_OP_MULB + ot; +} + static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1810,6 +2207,46 @@ static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_NEG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv oldv = tcg_temp_new(); + + if (s->prefix & PREFIX_LOCK) { + TCGv newv = tcg_temp_new(); + TCGv cmpv = tcg_temp_new(); + TCGLabel *label1 = gen_new_label(); + + gen_set_label(label1); + gen_op_ld_v(s, ot, oldv, s->A0); + tcg_gen_neg_tl(newv, oldv); + tcg_gen_atomic_cmpxchg_tl(cmpv, s->A0, oldv, newv, + s->mem_index, ot | MO_LE); + tcg_gen_brcond_tl(TCG_COND_NE, oldv, cmpv, label1); + } else { + tcg_gen_mov_tl(oldv, s->T0); + } + tcg_gen_neg_tl(s->T0, oldv); + + decode->cc_dst = s->T0; + decode->cc_src = oldv; + tcg_gen_movi_tl(s->cc_srcT, 0); + decode->cc_op = CC_OP_SUBB + ot; +} + +static void gen_NOT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_movi_tl(s->T0, ~0); + tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_not_tl(s->T0, s->T0); + } +} + static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1823,6 +2260,23 @@ static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } +static void gen_OUT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); + TCGv_i32 value = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, 0)) { + return; + } + tcg_gen_trunc_tl_i32(value, s->T0); + translator_io_start(&s->base); + gen_helper_out_func(ot, port, value); + gen_bpt_io(s, port, ot); +} + static void gen_OUTS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -2035,12 +2489,6 @@ static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s) tcg_gen_or_vec(vece, d, d, t); } -#ifdef TARGET_X86_64 -#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 -#else -#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 -#endif - static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; @@ -2287,6 +2735,438 @@ static void gen_PUSHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_push_v(s, s->T0); } +static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, + bool *can_be_zero, TCGv *count) +{ + MemOp ot = decode->op[0].ot; + int mask = (ot <= MO_32 ? 0x1f : 0x3f); + + *can_be_zero = false; + switch (decode->op[2].unit) { + case X86_OP_INT: + *count = tcg_temp_new(); + tcg_gen_andi_tl(*count, s->T1, mask); + *can_be_zero = true; + break; + + case X86_OP_IMM: + if ((decode->immediate & mask) == 0) { + *count = NULL; + break; + } + *count = tcg_temp_new(); + tcg_gen_movi_tl(*count, decode->immediate & mask); + break; + + case X86_OP_SKIP: + *count = tcg_temp_new(); + tcg_gen_movi_tl(*count, 1); + break; + + default: + g_assert_not_reached(); + } + + return ot; +} + +/* + * Compute existing flags in decode->cc_src, for gen_* functions that wants + * to set the cc_op set to CC_OP_ADCOX. In particular, this allows rotate + * operations to compute the carry in decode->cc_dst and the overflow in + * decode->cc_src2. + * + * If need_flags is true, decode->cc_dst and decode->cc_src2 are preloaded + * with the value of CF and OF before the instruction, so that it is possible + * to keep the flags unmodified. + * + * Return true if carry could be made available cheaply as a 1-bit value in + * decode->cc_dst (trying a bit harder if want_carry is true). If false is + * returned, decode->cc_dst is uninitialized and the carry is only available + * as bit 0 of decode->cc_src. + */ +static bool gen_eflags_adcox(DisasContext *s, X86DecodedInsn *decode, bool want_carry, bool need_flags) +{ + bool got_cf = false; + bool got_of = false; + + decode->cc_dst = tcg_temp_new(); + decode->cc_src = tcg_temp_new(); + decode->cc_src2 = tcg_temp_new(); + decode->cc_op = CC_OP_ADCOX; + + /* A lot more cc_ops could be "optimized" to avoid the extracts at + * the end (INC/DEC, BMILG, MUL), but they are all really unlikely + * to be followed by rotations within the same basic block. + */ + switch (s->cc_op) { + case CC_OP_ADCOX: + /* No need to compute the full EFLAGS, CF/OF are already isolated. */ + tcg_gen_mov_tl(decode->cc_src, cpu_cc_src); + if (need_flags) { + tcg_gen_mov_tl(decode->cc_src2, cpu_cc_src2); + got_of = true; + } + if (want_carry || need_flags) { + tcg_gen_mov_tl(decode->cc_dst, cpu_cc_dst); + got_cf = true; + } + break; + + case CC_OP_LOGICB ... CC_OP_LOGICQ: + /* CF and OF are zero, do it just because it's easy. */ + gen_mov_eflags(s, decode->cc_src); + if (need_flags) { + tcg_gen_movi_tl(decode->cc_src2, 0); + got_of = true; + } + if (want_carry || need_flags) { + tcg_gen_movi_tl(decode->cc_dst, 0); + got_cf = true; + } + break; + + case CC_OP_SARB ... CC_OP_SARQ: + /* + * SHR/RCR/SHR/RCR/... is a relatively common occurrence of RCR. + * By computing CF without using eflags, the calls to cc_compute_all + * can be eliminated as dead code (except for the last RCR). + */ + if (want_carry || need_flags) { + tcg_gen_andi_tl(decode->cc_dst, cpu_cc_src, 1); + got_cf = true; + } + gen_mov_eflags(s, decode->cc_src); + break; + + case CC_OP_SHLB ... CC_OP_SHLQ: + /* + * Likewise for SHL/RCL/SHL/RCL/... but, if CF is not in the sign + * bit, we might as well fish CF out of EFLAGS and save a shift. + */ + if (want_carry && (!need_flags || s->cc_op == CC_OP_SHLB + MO_TL)) { + tcg_gen_shri_tl(decode->cc_dst, cpu_cc_src, (8 << (s->cc_op - CC_OP_SHLB)) - 1); + got_cf = true; + } + gen_mov_eflags(s, decode->cc_src); + break; + + default: + gen_mov_eflags(s, decode->cc_src); + break; + } + + if (need_flags) { + /* If the flags could be left unmodified, always load them. */ + if (!got_of) { + tcg_gen_extract_tl(decode->cc_src2, decode->cc_src, ctz32(CC_O), 1); + got_of = true; + } + if (!got_cf) { + tcg_gen_extract_tl(decode->cc_dst, decode->cc_src, ctz32(CC_C), 1); + got_cf = true; + } + } + return got_cf; +} + +static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, TCGv count) +{ + MemOp ot = decode->op[0].ot; + TCGv temp = count ? tcg_temp_new() : decode->cc_src2; + + tcg_gen_xor_tl(temp, old, result); + tcg_gen_extract_tl(temp, temp, (8 << ot) - 1, 1); + if (count) { + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src2, count, tcg_constant_tl(0), + decode->cc_src2, temp); + } +} + +/* + * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands, + * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further. + */ +static void gen_rotc_mod(MemOp ot, TCGv count) +{ + TCGv temp; + + switch (ot) { + case MO_8: + temp = tcg_temp_new(); + tcg_gen_subi_tl(temp, count, 18); + tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); + tcg_gen_subi_tl(temp, count, 9); + tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); + break; + + case MO_16: + temp = tcg_temp_new(); + tcg_gen_subi_tl(temp, count, 17); + tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); + break; + + default: + break; + } +} + +/* + * The idea here is that the bit to the right of the new bit 0 is the + * new carry, and the bit to the right of the old bit 0 is the old carry. + * Just like a regular rotation, the result of the rotation is composed + * from a right shifted part and a left shifted part of s->T0. The new carry + * is extracted from the right-shifted portion, and the old carry is + * inserted at the end of the left-shifted portion. + * + * Because of the separate shifts involving the carry, gen_RCL and gen_RCR + * mostly operate on count-1. This also comes in handy when computing + * length - count, because (length-1) - (count-1) can be computed with + * a XOR, and that is commutative unlike subtraction. + */ +static void gen_RCL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool have_1bit_cin, can_be_zero; + TCGv count; + TCGLabel *zero_label = NULL; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + TCGv low, high, low_count; + + if (!count) { + return; + } + + low = tcg_temp_new(); + high = tcg_temp_new(); + low_count = tcg_temp_new(); + + gen_rotc_mod(ot, count); + have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero); + if (can_be_zero) { + zero_label = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, count, 0, zero_label); + } + + /* Compute high part, including incoming carry. */ + if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) { + /* high = (T0 << 1) | cin */ + TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src; + tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1); + } else { + /* Same as above but without deposit; cin in cc_dst. */ + tcg_gen_add_tl(high, s->T0, decode->cc_dst); + tcg_gen_add_tl(high, high, s->T0); + } + tcg_gen_subi_tl(count, count, 1); + tcg_gen_shl_tl(high, high, count); + + /* Compute low part and outgoing carry, incoming s->T0 is zero extended */ + tcg_gen_xori_tl(low_count, count, (8 << ot) - 1); /* LENGTH - 1 - (count - 1) */ + tcg_gen_shr_tl(low, s->T0, low_count); + tcg_gen_andi_tl(decode->cc_dst, low, 1); + tcg_gen_shri_tl(low, low, 1); + + /* Compute result and outgoing overflow */ + tcg_gen_mov_tl(decode->cc_src2, s->T0); + tcg_gen_or_tl(s->T0, low, high); + gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL); + + if (zero_label) { + gen_set_label(zero_label); + } +} + +static void gen_RCR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool have_1bit_cin, can_be_zero; + TCGv count; + TCGLabel *zero_label = NULL; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + TCGv low, high, high_count; + + if (!count) { + return; + } + + low = tcg_temp_new(); + high = tcg_temp_new(); + high_count = tcg_temp_new(); + + gen_rotc_mod(ot, count); + have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero); + if (can_be_zero) { + zero_label = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, count, 0, zero_label); + } + + /* Save incoming carry into high, it will be shifted later. */ + if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) { + TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src; + tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1); + } else { + /* Same as above but without deposit; cin in cc_dst. */ + tcg_gen_add_tl(high, s->T0, decode->cc_dst); + tcg_gen_add_tl(high, high, s->T0); + } + + /* Compute low part and outgoing carry, incoming s->T0 is zero extended */ + tcg_gen_subi_tl(count, count, 1); + tcg_gen_shr_tl(low, s->T0, count); + tcg_gen_andi_tl(decode->cc_dst, low, 1); + tcg_gen_shri_tl(low, low, 1); + + /* Move high part to the right position */ + tcg_gen_xori_tl(high_count, count, (8 << ot) - 1); /* LENGTH - 1 - (count - 1) */ + tcg_gen_shl_tl(high, high, high_count); + + /* Compute result and outgoing overflow */ + tcg_gen_mov_tl(decode->cc_src2, s->T0); + tcg_gen_or_tl(s->T0, low, high); + gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL); + + if (zero_label) { + gen_set_label(zero_label); + } +} + +static void gen_RET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; + + MemOp ot = gen_pop_T0(s); + gen_stack_update(s, adjust + (1 << ot)); + gen_op_jmp_v(s, s->T0); + gen_bnd_jmp(s); + s->base.is_jmp = DISAS_JUMP; +} + +static void gen_RETF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; + + if (!PE(s) || VM86(s)) { + gen_stack_A0(s); + /* pop offset */ + gen_op_ld_v(s, s->dflag, s->T0, s->A0); + /* NOTE: keeping EIP updated is not a problem in case of + exception */ + gen_op_jmp_v(s, s->T0); + /* pop selector */ + gen_add_A0_im(s, 1 << s->dflag); + gen_op_ld_v(s, s->dflag, s->T0, s->A0); + gen_op_movl_seg_real(s, R_CS, s->T0); + /* add stack offset */ + gen_stack_update(s, adjust + (2 << s->dflag)); + } else { + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_lret_protected(tcg_env, tcg_constant_i32(s->dflag - 1), + tcg_constant_i32(adjust)); + } + s->base.is_jmp = DISAS_EOB_ONLY; +} + +/* + * Return non-NULL if a 32-bit rotate works, after possibly replicating the input. + * The input has already been zero-extended upon operand decode. + */ +static TCGv_i32 gen_rot_replicate(MemOp ot, TCGv in) +{ + TCGv_i32 temp; + switch (ot) { + case MO_8: + temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, in); + tcg_gen_muli_i32(temp, temp, 0x01010101); + return temp; + + case MO_16: + temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, in); + tcg_gen_deposit_i32(temp, temp, temp, 16, 16); + return temp; + +#ifdef TARGET_X86_64 + case MO_32: + temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, in); + return temp; +#endif + + default: + return NULL; + } +} + +static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, TCGv count, int bit) +{ + if (count == NULL) { + tcg_gen_extract_tl(decode->cc_dst, result, bit, 1); + } else { + TCGv temp = tcg_temp_new(); + tcg_gen_extract_tl(temp, result, bit, 1); + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0), + decode->cc_dst, temp); + } +} + +static void gen_ROL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + TCGv_i32 temp32, count32; + TCGv old = tcg_temp_new(); + + if (!count) { + return; + } + + gen_eflags_adcox(s, decode, false, can_be_zero); + tcg_gen_mov_tl(old, s->T0); + temp32 = gen_rot_replicate(ot, s->T0); + if (temp32) { + count32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(count32, count); + tcg_gen_rotl_i32(temp32, temp32, count32); + /* Zero extend to facilitate later optimization. */ + tcg_gen_extu_i32_tl(s->T0, temp32); + } else { + tcg_gen_rotl_tl(s->T0, s->T0, count); + } + gen_rot_carry(decode, s->T0, count, 0); + gen_rot_overflow(decode, s->T0, old, count); +} + +static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + TCGv_i32 temp32, count32; + TCGv old = tcg_temp_new(); + + if (!count) { + return; + } + + gen_eflags_adcox(s, decode, false, can_be_zero); + tcg_gen_mov_tl(old, s->T0); + temp32 = gen_rot_replicate(ot, s->T0); + if (temp32) { + count32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(count32, count); + tcg_gen_rotr_i32(temp32, temp32, count32); + /* Zero extend to facilitate later optimization. */ + tcg_gen_extu_i32_tl(s->T0, temp32); + gen_rot_carry(decode, s->T0, count, 31); + } else { + tcg_gen_rotr_tl(s->T0, s->T0, count); + gen_rot_carry(decode, s->T0, count, TARGET_LONG_BITS - 1); + } + gen_rot_overflow(decode, s->T0, old, count); +} + static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2323,6 +3203,64 @@ static void gen_SAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); } +static void gen_SALC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_compute_eflags_c(s, s->T0); + tcg_gen_neg_tl(s->T0, s->T0); +} + +static void gen_shift_dynamic_flags(DisasContext *s, X86DecodedInsn *decode, TCGv count, CCOp cc_op) +{ + TCGv_i32 count32 = tcg_temp_new_i32(); + TCGv_i32 old_cc_op; + + decode->cc_op = CC_OP_DYNAMIC; + decode->cc_op_dynamic = tcg_temp_new_i32(); + + assert(decode->cc_dst == s->T0); + if (cc_op_live[s->cc_op] & USES_CC_DST) { + decode->cc_dst = tcg_temp_new(); + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0), + cpu_cc_dst, s->T0); + } + + if (cc_op_live[s->cc_op] & USES_CC_SRC) { + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src, count, tcg_constant_tl(0), + cpu_cc_src, decode->cc_src); + } + + tcg_gen_trunc_tl_i32(count32, count); + if (s->cc_op == CC_OP_DYNAMIC) { + old_cc_op = cpu_cc_op; + } else { + old_cc_op = tcg_constant_i32(s->cc_op); + } + tcg_gen_movcond_i32(TCG_COND_EQ, decode->cc_op_dynamic, count32, tcg_constant_i32(0), + old_cc_op, tcg_constant_i32(cc_op)); +} + +static void gen_SAR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = tcg_temp_new(); + tcg_gen_subi_tl(decode->cc_src, count, 1); + tcg_gen_sar_tl(decode->cc_src, s->T0, decode->cc_src); + tcg_gen_sar_tl(s->T0, s->T0, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); + } else { + decode->cc_op = CC_OP_SARB + ot; + } +} + static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2421,6 +3359,28 @@ static void gen_SHA256RNDS2(DisasContext *s, CPUX86State *env, X86DecodedInsn *d gen_helper_sha256rnds2(OP_PTR0, OP_PTR1, OP_PTR2, wk0, wk1); } +static void gen_SHL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = tcg_temp_new(); + tcg_gen_subi_tl(decode->cc_src, count, 1); + tcg_gen_shl_tl(decode->cc_src, s->T0, decode->cc_src); + tcg_gen_shl_tl(s->T0, s->T0, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot); + } else { + decode->cc_op = CC_OP_SHLB + ot; + } +} + static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2431,6 +3391,28 @@ static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_shl_tl(s->T0, s->T0, s->T1); } +static void gen_SHR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = tcg_temp_new(); + tcg_gen_subi_tl(decode->cc_src, count, 1); + tcg_gen_shr_tl(decode->cc_src, s->T0, decode->cc_src); + tcg_gen_shr_tl(s->T0, s->T0, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); + } else { + decode->cc_op = CC_OP_SARB + ot; + } +} + static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2441,6 +3423,25 @@ static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_shr_tl(s->T0, s->T0, s->T1); } +static void gen_STC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_compute_eflags(s); + tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); +} + +static void gen_STD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_st_i32(tcg_constant_i32(-1), tcg_env, offsetof(CPUX86State, df)); +} + +static void gen_STI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_set_eflags(s, IF_MASK); + /* interruptions are enabled only the first insn after sti */ + gen_update_eip_next(s); + gen_eob_inhibit_irq(s); +} + static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -2997,6 +3998,14 @@ static void gen_XCHG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_XLAT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + /* AL is already zero-extended into s->T0. */ + tcg_gen_add_tl(s->A0, cpu_regs[R_EBX], s->T0); + gen_add_A0_ds_seg(s); + gen_op_ld_v(s, MO_8, s->T0, s->A0); +} + static void gen_XOR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { /* special case XOR reg, reg */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index e09bfdaa39..f83204bd1e 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -38,6 +38,9 @@ #include "exec/helper-info.c.inc" #undef HELPER_H +/* Fixes for Windows namespace pollution. */ +#undef IN +#undef OUT #define PREFIX_REPZ 0x01 #define PREFIX_REPNZ 0x02 @@ -2489,14 +2492,24 @@ static inline int insn_const_size(MemOp ot) } } +static void gen_conditional_jump_labels(DisasContext *s, target_long diff, + TCGLabel *not_taken, TCGLabel *taken) +{ + if (not_taken) { + gen_set_label(not_taken); + } + gen_jmp_rel_csize(s, 0, 1); + + gen_set_label(taken); + gen_jmp_rel(s, s->dflag, diff, 0); +} + static void gen_jcc(DisasContext *s, int b, int diff) { TCGLabel *l1 = gen_new_label(); gen_jcc1(s, b, l1); - gen_jmp_rel_csize(s, 0, 1); - gen_set_label(l1); - gen_jmp_rel(s, s->dflag, diff, 0); + gen_conditional_jump_labels(s, diff, NULL, l1); } static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) @@ -2753,7 +2766,7 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) /* an interrupt is different from an exception because of the privilege checks */ -static void gen_interrupt(DisasContext *s, int intno) +static void gen_interrupt(DisasContext *s, uint8_t intno) { gen_update_cc_op(s); gen_update_eip_cur(s); @@ -3184,7 +3197,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) #ifndef CONFIG_USER_ONLY use_new &= b <= limit; #endif - if (use_new && b <= 0xbf) { + if (use_new && (b < 0xd8 || b >= 0xe0)) { disas_insn_new(s, cpu, b); return true; } From 40c4b92f1c8df3b501484511f319d7220fd72eca Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 15 Feb 2024 13:56:40 +0100 Subject: [PATCH 0520/2884] target/i386: merge and enlarge a few ranges for call to disas_insn_new Since new opcodes are not going to be added in translate.c, round the case labels that call to disas_insn_new(), including whole sets of eight opcodes when possible. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index f83204bd1e..3060fdbee9 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -6869,9 +6869,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; case 0x10e ... 0x117: case 0x128 ... 0x12f: - case 0x138 ... 0x13a: - case 0x150 ... 0x179: - case 0x17c ... 0x17f: + case 0x138 ... 0x13f: + case 0x150 ... 0x17f: case 0x1c2: case 0x1c4 ... 0x1c6: case 0x1d0 ... 0x1fe: From 2b8046f3618756dee9c8169bc389b890c43f7190 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Oct 2023 11:51:58 +0200 Subject: [PATCH 0521/2884] target/i386: move remaining conditional operations to new decoder Move long-displacement Jcc, SETcc and CMOVcc to the new decoder. While filling in the tables makes the code seem longer, the new emitters are all just one line of code. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 56 ++++++++++++++++++++++++++++++++ target/i386/tcg/decode-new.h | 1 + target/i386/tcg/emit.c.inc | 10 ++++++ target/i386/tcg/translate.c | 2 +- 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index b145af7a56..c70a783691 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -999,6 +999,15 @@ static const X86OpEntry opcodes_0F[256] = { /* Incorrectly listed as Mq,Vq in the manual */ [0x17] = X86_OP_ENTRY3(VMOVHPx_st, M,q, None,None, V,dq, vex5 p_00_66), + [0x40] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x41] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x42] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x43] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x44] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x45] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x46] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x47] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x50] = X86_OP_ENTRY3(MOVMSK, G,y, None,None, U,x, vex7 p_00_66), [0x51] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), /* sqrtps */ [0x52] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex4_rep5 p_00_f3), /* rsqrtps */ @@ -1026,6 +1035,24 @@ static const X86OpEntry opcodes_0F[256] = { [0x76] = X86_OP_ENTRY3(PCMPEQD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0x77] = X86_OP_GROUP0(0F77), + [0x80] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x81] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x82] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x83] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x84] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x85] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x86] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x87] = X86_OP_ENTRYr(Jcc, J,z_f64), + + [0x90] = X86_OP_ENTRYw(SETcc, E,b), + [0x91] = X86_OP_ENTRYw(SETcc, E,b), + [0x92] = X86_OP_ENTRYw(SETcc, E,b), + [0x93] = X86_OP_ENTRYw(SETcc, E,b), + [0x94] = X86_OP_ENTRYw(SETcc, E,b), + [0x95] = X86_OP_ENTRYw(SETcc, E,b), + [0x96] = X86_OP_ENTRYw(SETcc, E,b), + [0x97] = X86_OP_ENTRYw(SETcc, E,b), + [0x28] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1 p_00_66), /* MOVAPS */ [0x29] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 p_00_66), /* MOVAPS */ [0x2A] = X86_OP_GROUP0(0F2A), @@ -1038,6 +1065,15 @@ static const X86OpEntry opcodes_0F[256] = { [0x38] = X86_OP_GROUP0(0F38), [0x3a] = X86_OP_GROUP0(0F3A), + [0x48] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x49] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4a] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4b] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4c] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4d] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4e] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4f] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x58] = X86_OP_ENTRY3(VADD, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), [0x59] = X86_OP_ENTRY3(VMUL, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), [0x5a] = X86_OP_GROUP0(0F5A), @@ -1063,6 +1099,24 @@ static const X86OpEntry opcodes_0F[256] = { [0x7e] = X86_OP_GROUP0(0F7E), [0x7f] = X86_OP_GROUP0(0F7F), + [0x88] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x89] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8a] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8b] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8c] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8d] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8e] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8f] = X86_OP_ENTRYr(Jcc, J,z_f64), + + [0x98] = X86_OP_ENTRYw(SETcc, E,b), + [0x99] = X86_OP_ENTRYw(SETcc, E,b), + [0x9a] = X86_OP_ENTRYw(SETcc, E,b), + [0x9b] = X86_OP_ENTRYw(SETcc, E,b), + [0x9c] = X86_OP_ENTRYw(SETcc, E,b), + [0x9d] = X86_OP_ENTRYw(SETcc, E,b), + [0x9e] = X86_OP_ENTRYw(SETcc, E,b), + [0x9f] = X86_OP_ENTRYw(SETcc, E,b), + [0xae] = X86_OP_GROUP0(group15), [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), @@ -1929,6 +1983,8 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) switch (cpuid) { case X86_FEAT_None: return true; + case X86_FEAT_CMOV: + return (s->cpuid_features & CPUID_CMOV); case X86_FEAT_F16C: return (s->cpuid_ext_features & CPUID_EXT_F16C); case X86_FEAT_FMA: diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index e86a5c2cb5..2ea06b4478 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -107,6 +107,7 @@ typedef enum X86CPUIDFeature { X86_FEAT_AVX2, X86_FEAT_BMI1, X86_FEAT_BMI2, + X86_FEAT_CMOV, X86_FEAT_CMPCCXADD, X86_FEAT_F16C, X86_FEAT_FMA, diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2c848fab62..a9e32516af 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1386,6 +1386,11 @@ static void gen_CMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); } +static void gen_CMOVcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_cmovcc1(s, decode->b & 0xf, s->T0, s->T1); +} + static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGLabel *label_top = gen_new_label(); @@ -3305,6 +3310,11 @@ static void gen_SCAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_SETcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_setcc1(s, decode->b & 0xf, s->T0); +} + static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3060fdbee9..b1c1aecf71 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3207,7 +3207,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) #ifndef CONFIG_USER_ONLY use_new &= b <= limit; #endif - if (use_new && 0) { + if (use_new && (b >= 0x138 && b <= 0x19f)) { disas_insn_new(s, cpu, b); return true; } From 37861fa519a97324dbca67ee4c5b23bec21c24db Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Oct 2023 08:49:04 +0200 Subject: [PATCH 0522/2884] target/i386: move BSWAP to new decoder Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 9 +++++++++ target/i386/tcg/emit.c.inc | 11 +++++++++++ target/i386/tcg/translate.c | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index c70a783691..16e23ec3d8 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1124,6 +1124,15 @@ static const X86OpEntry opcodes_0F[256] = { [0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66), [0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66), + [0xc8] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xc9] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xca] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcb] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcc] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcd] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xce] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcf] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xd0] = X86_OP_ENTRY3(VADDSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), [0xd1] = X86_OP_ENTRY3(PSRLW_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index a9e32516af..befde6677b 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1310,6 +1310,17 @@ static void gen_BOUND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } +static void gen_BSWAP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ +#ifdef TARGET_X86_64 + if (s->dflag == MO_64) { + tcg_gen_bswap64_i64(s->T0, s->T0); + return; + } +#endif + tcg_gen_bswap32_tl(s->T0, s->T0, TCG_BSWAP_OZ); +} + static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index b1c1aecf71..751ec5d0f1 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3207,7 +3207,9 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) #ifndef CONFIG_USER_ONLY use_new &= b <= limit; #endif - if (use_new && (b >= 0x138 && b <= 0x19f)) { + if (use_new && + ((b >= 0x138 && b <= 0x19f) || + (b >= 0x1c8 && b <= 0x1cf))) { disas_insn_new(s, cpu, b); return true; } From 3519b813e1f1657d06b5af73bcbe50f6ba176a29 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 21 Oct 2023 17:51:23 +0200 Subject: [PATCH 0523/2884] target/i386: port extensions of one-byte opcodes to new decoder A few two-byte opcodes are simple extensions of existing one-byte opcodes; they are easy to decode and need no change to emit.c.inc. Port them to the new decoder. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 20 ++++++++++++++++++++ target/i386/tcg/emit.c.inc | 15 +++++++++++++++ target/i386/tcg/translate.c | 4 ++++ 3 files changed, 39 insertions(+) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 16e23ec3d8..f9a966943f 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1053,6 +1053,9 @@ static const X86OpEntry opcodes_0F[256] = { [0x96] = X86_OP_ENTRYw(SETcc, E,b), [0x97] = X86_OP_ENTRYw(SETcc, E,b), + [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), + [0xa1] = X86_OP_ENTRYw(POP, FS, w), + [0x28] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1 p_00_66), /* MOVAPS */ [0x29] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 p_00_66), /* MOVAPS */ [0x2A] = X86_OP_GROUP0(0F2A), @@ -1117,9 +1120,26 @@ static const X86OpEntry opcodes_0F[256] = { [0x9e] = X86_OP_ENTRYw(SETcc, E,b), [0x9f] = X86_OP_ENTRYw(SETcc, E,b), + [0xa8] = X86_OP_ENTRYr(PUSH, GS, w), + [0xa9] = X86_OP_ENTRYw(POP, GS, w), [0xae] = X86_OP_GROUP0(group15), + /* + * It's slightly more efficient to put Ev operand in T0 and allow gen_IMUL3 + * to assume sextT0. Multiplication is commutative anyway. + */ + [0xaf] = X86_OP_ENTRY3(IMUL3, G,v, E,v, 2op,v, sextT0), + + [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), + [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), + [0xb5] = X86_OP_ENTRY3(LGS, G,v, EM,p, None, None), + [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ + [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ + + [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */ + [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */ [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0xc3] = X86_OP_ENTRY3(MOV, EM,y,G,y, None,None, cpuid(SSE2)), /* MOVNTI */ [0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66), [0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66), [0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66), diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index befde6677b..58f255873f 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1979,6 +1979,16 @@ static void gen_LES(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_lxx_seg(s, env, decode, R_ES); } +static void gen_LFS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, env, decode, R_FS); +} + +static void gen_LGS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, env, decode, R_GS); +} + static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -2023,6 +2033,11 @@ static void gen_LOOPNE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); } +static void gen_LSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, env, decode, R_SS); +} + static void gen_MOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { /* nothing to do! */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 751ec5d0f1..4fbd91e70d 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3209,6 +3209,10 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) #endif if (use_new && ((b >= 0x138 && b <= 0x19f) || + (b & ~9) == 0x1a0 || + b == 0x1af || b == 0x1b2 || + (b >= 0x1b4 && b <= 0x1b7) || + b == 0x1be || b == 0x1bf || b == 0x1c3 || (b >= 0x1c8 && b <= 0x1cf))) { disas_insn_new(s, cpu, b); return true; From aef4f4affde2c8d644e8a509171d5995a3983f56 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 11 Apr 2024 10:22:36 +0200 Subject: [PATCH 0524/2884] target/i386: remove now-converted opcodes from old decoder Send all converted opcodes to disas_insn_new() directly from the big decoding switch statement; once more, the debugging/bisecting logic disappears. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/helper.h | 11 - target/i386/tcg/decode-new.c.inc | 3 - target/i386/tcg/int_helper.c | 34 - target/i386/tcg/shift_helper_template.h.inc | 108 - target/i386/tcg/translate.c | 2175 +------------------ 5 files changed, 11 insertions(+), 2320 deletions(-) delete mode 100644 target/i386/tcg/shift_helper_template.h.inc diff --git a/target/i386/helper.h b/target/i386/helper.h index ac2b04abd6..3c207ac62d 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -207,15 +207,4 @@ DEF_HELPER_1(emms, void, env) #define SHIFT 2 #include "tcg/ops_sse_header.h.inc" -DEF_HELPER_3(rclb, tl, env, tl, tl) -DEF_HELPER_3(rclw, tl, env, tl, tl) -DEF_HELPER_3(rcll, tl, env, tl, tl) -DEF_HELPER_3(rcrb, tl, env, tl, tl) -DEF_HELPER_3(rcrw, tl, env, tl, tl) -DEF_HELPER_3(rcrl, tl, env, tl, tl) -#ifdef TARGET_X86_64 -DEF_HELPER_3(rclq, tl, env, tl, tl) -DEF_HELPER_3(rcrq, tl, env, tl, tl) -#endif - DEF_HELPER_1(rdrand, tl, env) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index f9a966943f..dabc987b99 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2227,9 +2227,6 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) X86DecodeFunc decode_func = decode_root; uint8_t cc_live; -#ifdef CONFIG_USER_ONLY - if (limit) { --limit; } -#endif s->has_modrm = false; next_byte: diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index ab85dc5540..df16130f5d 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -29,22 +29,6 @@ //#define DEBUG_MULDIV -/* modulo 9 table */ -static const uint8_t rclb_table[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 0, 1, 2, 3, 4, 5, - 6, 7, 8, 0, 1, 2, 3, 4, -}; - -/* modulo 17 table */ -static const uint8_t rclw_table[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, -}; - /* division, flags are undefined */ void helper_divb_AL(CPUX86State *env, target_ulong t0) @@ -447,24 +431,6 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) return dest; } -#define SHIFT 0 -#include "shift_helper_template.h.inc" -#undef SHIFT - -#define SHIFT 1 -#include "shift_helper_template.h.inc" -#undef SHIFT - -#define SHIFT 2 -#include "shift_helper_template.h.inc" -#undef SHIFT - -#ifdef TARGET_X86_64 -#define SHIFT 3 -#include "shift_helper_template.h.inc" -#undef SHIFT -#endif - /* Test that BIT is enabled in CR4. If not, raise an illegal opcode exception. This reduces the requirements for rare CR4 bits being mapped into HFLAGS. */ diff --git a/target/i386/tcg/shift_helper_template.h.inc b/target/i386/tcg/shift_helper_template.h.inc deleted file mode 100644 index 54f15d6e05..0000000000 --- a/target/i386/tcg/shift_helper_template.h.inc +++ /dev/null @@ -1,108 +0,0 @@ -/* - * x86 shift helpers - * - * Copyright (c) 2008 Fabrice Bellard - * - * 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.1 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 . - */ - -#define DATA_BITS (1 << (3 + SHIFT)) -#define SHIFT_MASK (DATA_BITS - 1) -#if DATA_BITS <= 32 -#define SHIFT1_MASK 0x1f -#else -#define SHIFT1_MASK 0x3f -#endif - -#if DATA_BITS == 8 -#define SUFFIX b -#define DATA_MASK 0xff -#elif DATA_BITS == 16 -#define SUFFIX w -#define DATA_MASK 0xffff -#elif DATA_BITS == 32 -#define SUFFIX l -#define DATA_MASK 0xffffffff -#elif DATA_BITS == 64 -#define SUFFIX q -#define DATA_MASK 0xffffffffffffffffULL -#else -#error unhandled operand size -#endif - -target_ulong glue(helper_rcl, SUFFIX)(CPUX86State *env, target_ulong t0, - target_ulong t1) -{ - int count, eflags; - target_ulong src; - target_long res; - - count = t1 & SHIFT1_MASK; -#if DATA_BITS == 16 - count = rclw_table[count]; -#elif DATA_BITS == 8 - count = rclb_table[count]; -#endif - if (count) { - eflags = env->cc_src; - t0 &= DATA_MASK; - src = t0; - res = (t0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1)); - if (count > 1) { - res |= t0 >> (DATA_BITS + 1 - count); - } - t0 = res; - env->cc_src = (eflags & ~(CC_C | CC_O)) | - (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | - ((src >> (DATA_BITS - count)) & CC_C); - } - return t0; -} - -target_ulong glue(helper_rcr, SUFFIX)(CPUX86State *env, target_ulong t0, - target_ulong t1) -{ - int count, eflags; - target_ulong src; - target_long res; - - count = t1 & SHIFT1_MASK; -#if DATA_BITS == 16 - count = rclw_table[count]; -#elif DATA_BITS == 8 - count = rclb_table[count]; -#endif - if (count) { - eflags = env->cc_src; - t0 &= DATA_MASK; - src = t0; - res = (t0 >> count) | - ((target_ulong)(eflags & CC_C) << (DATA_BITS - count)); - if (count > 1) { - res |= t0 << (DATA_BITS + 1 - count); - } - t0 = res; - env->cc_src = (eflags & ~(CC_C | CC_O)) | - (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | - ((src >> (count - 1)) & CC_C); - } - return t0; -} - -#undef DATA_BITS -#undef SHIFT_MASK -#undef SHIFT1_MASK -#undef DATA_TYPE -#undef DATA_MASK -#undef SUFFIX diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4fbd91e70d..f4e5843d5f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -215,7 +215,6 @@ typedef struct DisasContext { #ifdef CONFIG_USER_ONLY STUB_HELPER(clgi, TCGv_env env) STUB_HELPER(flush_page, TCGv_env env, TCGv addr) -STUB_HELPER(hlt, TCGv_env env, TCGv_i32 pc_ofs) STUB_HELPER(inb, TCGv ret, TCGv_env env, TCGv_i32 port) STUB_HELPER(inw, TCGv ret, TCGv_env env, TCGv_i32 port) STUB_HELPER(inl, TCGv ret, TCGv_env env, TCGv_i32 port) @@ -242,21 +241,8 @@ static void gen_eob(DisasContext *s); static void gen_jr(DisasContext *s); static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num); static void gen_jmp_rel_csize(DisasContext *s, int diff, int tb_num); -static void gen_op(DisasContext *s1, int op, MemOp ot, int d); static void gen_exception_gpf(DisasContext *s); -/* i386 arith/logic operations */ -enum { - OP_ADDL, - OP_ORL, - OP_ADCL, - OP_SBBL, - OP_ANDL, - OP_SUBL, - OP_XORL, - OP_CMPL, -}; - /* i386 shift ops */ enum { OP_ROL, @@ -442,13 +428,6 @@ static inline MemOp mo_b_d(int b, MemOp ot) return b & 1 ? ot : MO_8; } -/* Select size 8 if lsb of B is clear, else OT capped at 32. - Used for decoding operand size of port opcodes. */ -static inline MemOp mo_b_d32(int b, MemOp ot) -{ - return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; -} - /* Compute the result of writing t0 to the OT-sized register REG. * * If DEST is NULL, store the result into the register and return the @@ -851,25 +830,6 @@ static void gen_op_update2_cc(DisasContext *s) tcg_gen_mov_tl(cpu_cc_dst, s->T0); } -static void gen_op_update3_cc(DisasContext *s, TCGv reg) -{ - tcg_gen_mov_tl(cpu_cc_src2, reg); - tcg_gen_mov_tl(cpu_cc_src, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static inline void gen_op_testl_T0_T1_cc(DisasContext *s) -{ - tcg_gen_and_tl(cpu_cc_dst, s->T0, s->T1); -} - -static void gen_op_update_neg_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_neg_tl(cpu_cc_src, s->T0); - tcg_gen_movi_tl(s->cc_srcT, 0); -} - /* compute all eflags to reg */ static void gen_mov_eflags(DisasContext *s, TCGv reg) { @@ -1484,165 +1444,6 @@ static bool check_cpl0(DisasContext *s) return false; } -/* If vm86, check for iopl == 3; if not, raise #GP and return false. */ -static bool check_vm86_iopl(DisasContext *s) -{ - if (!VM86(s) || IOPL(s) == 3) { - return true; - } - gen_exception_gpf(s); - return false; -} - -/* Check for iopl allowing access; if not, raise #GP and return false. */ -static bool check_iopl(DisasContext *s) -{ - if (VM86(s) ? IOPL(s) == 3 : CPL(s) <= IOPL(s)) { - return true; - } - gen_exception_gpf(s); - return false; -} - -/* if d == OR_TMP0, it means memory operand (address in A0) */ -static void gen_op(DisasContext *s1, int op, MemOp ot, int d) -{ - /* Invalid lock prefix when destination is not memory or OP_CMPL. */ - if ((d != OR_TMP0 || op == OP_CMPL) && s1->prefix & PREFIX_LOCK) { - gen_illegal_opcode(s1); - return; - } - - if (d != OR_TMP0) { - gen_op_mov_v_reg(s1, ot, s1->T0, d); - } else if (!(s1->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s1, ot, s1->T0, s1->A0); - } - switch(op) { - case OP_ADCL: - gen_compute_eflags_c(s1, s1->tmp4); - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(s1->T0, s1->tmp4, s1->T1); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); - tcg_gen_add_tl(s1->T0, s1->T0, s1->tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update3_cc(s1, s1->tmp4); - set_cc_op(s1, CC_OP_ADCB + ot); - break; - case OP_SBBL: - gen_compute_eflags_c(s1, s1->tmp4); - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(s1->T0, s1->T1, s1->tmp4); - tcg_gen_neg_tl(s1->T0, s1->T0); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); - tcg_gen_sub_tl(s1->T0, s1->T0, s1->tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update3_cc(s1, s1->tmp4); - set_cc_op(s1, CC_OP_SBBB + ot); - break; - case OP_ADDL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update2_cc(s1); - set_cc_op(s1, CC_OP_ADDB + ot); - break; - case OP_SUBL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_neg_tl(s1->T0, s1->T1); - tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1); - } else { - tcg_gen_mov_tl(s1->cc_srcT, s1->T0); - tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update2_cc(s1); - set_cc_op(s1, CC_OP_SUBB + ot); - break; - default: - case OP_ANDL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_and_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_and_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_ORL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_or_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_or_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_XORL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_xor_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_xor_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_CMPL: - tcg_gen_mov_tl(cpu_cc_src, s1->T1); - tcg_gen_mov_tl(s1->cc_srcT, s1->T0); - tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1); - set_cc_op(s1, CC_OP_SUBB + ot); - break; - } -} - -/* if d == OR_TMP0, it means memory operand (address in A0) */ -static void gen_inc(DisasContext *s1, MemOp ot, int d, int c) -{ - if (s1->prefix & PREFIX_LOCK) { - if (d != OR_TMP0) { - /* Lock prefix when destination is not memory */ - gen_illegal_opcode(s1); - return; - } - tcg_gen_movi_tl(s1->T0, c > 0 ? 1 : -1); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - if (d != OR_TMP0) { - gen_op_mov_v_reg(s1, ot, s1->T0, d); - } else { - gen_op_ld_v(s1, ot, s1->T0, s1->A0); - } - tcg_gen_addi_tl(s1->T0, s1->T0, (c > 0 ? 1 : -1)); - gen_op_st_rm_T0_A0(s1, ot, d); - } - - gen_compute_eflags_c(s1, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, s1->T0); - set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot); -} - static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, TCGv shm1, TCGv count, bool is_right) { @@ -1685,298 +1486,6 @@ static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, set_cc_op(s, CC_OP_DYNAMIC); } -static void gen_shift_rm_T1(DisasContext *s, MemOp ot, int op1, - int is_right, int is_arith) -{ - target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - tcg_gen_andi_tl(s->T1, s->T1, mask); - tcg_gen_subi_tl(s->tmp0, s->T1, 1); - - if (is_right) { - if (is_arith) { - gen_exts(ot, s->T0); - tcg_gen_sar_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_sar_tl(s->T0, s->T0, s->T1); - } else { - gen_extu(ot, s->T0); - tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_shr_tl(s->T0, s->T0, s->T1); - } - } else { - tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_shl_tl(s->T0, s->T0, s->T1); - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - gen_shift_flags(s, ot, s->T0, s->tmp0, s->T1, is_right); -} - -static void gen_shift_rm_im(DisasContext *s, MemOp ot, int op1, int op2, - int is_right, int is_arith) -{ - int mask = (ot == MO_64 ? 0x3f : 0x1f); - - /* load */ - if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, s->T0, s->A0); - else - gen_op_mov_v_reg(s, ot, s->T0, op1); - - op2 &= mask; - if (op2 != 0) { - if (is_right) { - if (is_arith) { - gen_exts(ot, s->T0); - tcg_gen_sari_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_sari_tl(s->T0, s->T0, op2); - } else { - gen_extu(ot, s->T0); - tcg_gen_shri_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_shri_tl(s->T0, s->T0, op2); - } - } else { - tcg_gen_shli_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_shli_tl(s->T0, s->T0, op2); - } - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - /* update eflags if non zero shift */ - if (op2 != 0) { - tcg_gen_mov_tl(cpu_cc_src, s->tmp4); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); - } -} - -static void gen_rot_rm_T1(DisasContext *s, MemOp ot, int op1, int is_right) -{ - target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); - TCGv_i32 t0, t1; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - tcg_gen_andi_tl(s->T1, s->T1, mask); - - switch (ot) { - case MO_8: - /* Replicate the 8-bit input so that a 32-bit rotate works. */ - tcg_gen_ext8u_tl(s->T0, s->T0); - tcg_gen_muli_tl(s->T0, s->T0, 0x01010101); - goto do_long; - case MO_16: - /* Replicate the 16-bit input so that a 32-bit rotate works. */ - tcg_gen_deposit_tl(s->T0, s->T0, s->T0, 16, 16); - goto do_long; - do_long: -#ifdef TARGET_X86_64 - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - if (is_right) { - tcg_gen_rotr_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - } else { - tcg_gen_rotl_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - } - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; -#endif - default: - if (is_right) { - tcg_gen_rotr_tl(s->T0, s->T0, s->T1); - } else { - tcg_gen_rotl_tl(s->T0, s->T0, s->T1); - } - break; - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - /* We'll need the flags computed into CC_SRC. */ - gen_compute_eflags(s); - - /* The value that was "rotated out" is now present at the other end - of the word. Compute C into CC_DST and O into CC_SRC2. Note that - since we've computed the flags into CC_SRC, these variables are - currently dead. */ - if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); - } else { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); - } - tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); - tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - - /* Now conditionally store the new CC_OP value. If the shift count - is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live. - Otherwise reuse CC_OP_ADCOX which have the C and O flags split out - exactly as we computed above. */ - t0 = tcg_constant_i32(0); - t1 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t1, s->T1); - tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX); - tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, - s->tmp2_i32, s->tmp3_i32); - - /* The CC_OP value is no longer predictable. */ - set_cc_op(s, CC_OP_DYNAMIC); -} - -static void gen_rot_rm_im(DisasContext *s, MemOp ot, int op1, int op2, - int is_right) -{ - int mask = (ot == MO_64 ? 0x3f : 0x1f); - int shift; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - op2 &= mask; - if (op2 != 0) { - switch (ot) { -#ifdef TARGET_X86_64 - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - if (is_right) { - tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, op2); - } else { - tcg_gen_rotli_i32(s->tmp2_i32, s->tmp2_i32, op2); - } - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; -#endif - default: - if (is_right) { - tcg_gen_rotri_tl(s->T0, s->T0, op2); - } else { - tcg_gen_rotli_tl(s->T0, s->T0, op2); - } - break; - case MO_8: - mask = 7; - goto do_shifts; - case MO_16: - mask = 15; - do_shifts: - shift = op2 & mask; - if (is_right) { - shift = mask + 1 - shift; - } - gen_extu(ot, s->T0); - tcg_gen_shli_tl(s->tmp0, s->T0, shift); - tcg_gen_shri_tl(s->T0, s->T0, mask + 1 - shift); - tcg_gen_or_tl(s->T0, s->T0, s->tmp0); - break; - } - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - if (op2 != 0) { - /* Compute the flags into CC_SRC. */ - gen_compute_eflags(s); - - /* The value that was "rotated out" is now present at the other end - of the word. Compute C into CC_DST and O into CC_SRC2. Note that - since we've computed the flags into CC_SRC, these variables are - currently dead. */ - if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); - } else { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); - } - tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); - tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - set_cc_op(s, CC_OP_ADCOX); - } -} - -/* XXX: add faster immediate = 1 case */ -static void gen_rotc_rm_T1(DisasContext *s, MemOp ot, int op1, - int is_right) -{ - gen_compute_eflags(s); - assert(s->cc_op == CC_OP_EFLAGS); - - /* load */ - if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, s->T0, s->A0); - else - gen_op_mov_v_reg(s, ot, s->T0, op1); - - if (is_right) { - switch (ot) { - case MO_8: - gen_helper_rcrb(s->T0, tcg_env, s->T0, s->T1); - break; - case MO_16: - gen_helper_rcrw(s->T0, tcg_env, s->T0, s->T1); - break; - case MO_32: - gen_helper_rcrl(s->T0, tcg_env, s->T0, s->T1); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_rcrq(s->T0, tcg_env, s->T0, s->T1); - break; -#endif - default: - g_assert_not_reached(); - } - } else { - switch (ot) { - case MO_8: - gen_helper_rclb(s->T0, tcg_env, s->T0, s->T1); - break; - case MO_16: - gen_helper_rclw(s->T0, tcg_env, s->T0, s->T1); - break; - case MO_32: - gen_helper_rcll(s->T0, tcg_env, s->T0, s->T1); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_rclq(s->T0, tcg_env, s->T0, s->T1); - break; -#endif - default: - g_assert_not_reached(); - } - } - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); -} - /* XXX: add faster immediate case */ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, bool is_right, TCGv count_in) @@ -2061,63 +1570,6 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, gen_shift_flags(s, ot, s->T0, s->tmp0, count, is_right); } -static void gen_shift(DisasContext *s1, int op, MemOp ot, int d, int s) -{ - if (s != OR_TMP1) - gen_op_mov_v_reg(s1, ot, s1->T1, s); - switch(op) { - case OP_ROL: - gen_rot_rm_T1(s1, ot, d, 0); - break; - case OP_ROR: - gen_rot_rm_T1(s1, ot, d, 1); - break; - case OP_SHL: - case OP_SHL1: - gen_shift_rm_T1(s1, ot, d, 0, 0); - break; - case OP_SHR: - gen_shift_rm_T1(s1, ot, d, 1, 0); - break; - case OP_SAR: - gen_shift_rm_T1(s1, ot, d, 1, 1); - break; - case OP_RCL: - gen_rotc_rm_T1(s1, ot, d, 0); - break; - case OP_RCR: - gen_rotc_rm_T1(s1, ot, d, 1); - break; - } -} - -static void gen_shifti(DisasContext *s1, int op, MemOp ot, int d, int c) -{ - switch(op) { - case OP_ROL: - gen_rot_rm_im(s1, ot, d, c, 0); - break; - case OP_ROR: - gen_rot_rm_im(s1, ot, d, c, 1); - break; - case OP_SHL: - case OP_SHL1: - gen_shift_rm_im(s1, ot, d, c, 0, 0); - break; - case OP_SHR: - gen_shift_rm_im(s1, ot, d, c, 1, 0); - break; - case OP_SAR: - gen_shift_rm_im(s1, ot, d, c, 1, 1); - break; - default: - /* currently not optimized */ - tcg_gen_movi_tl(s1->T1, c); - gen_shift(s1, op, ot, d, OR_TMP1); - break; - } -} - #define X86_MAX_INSN_LENGTH 15 static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) @@ -2153,11 +1605,6 @@ static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s) return translator_ldub(env, &s->base, advance_pc(env, s, 1)); } -static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s) -{ - return translator_lduw(env, &s->base, advance_pc(env, s, 2)); -} - static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s) { return translator_lduw(env, &s->base, advance_pc(env, s, 2)); @@ -2483,15 +1930,6 @@ static target_long insn_get_signed(CPUX86State *env, DisasContext *s, MemOp ot) return ret; } -static inline int insn_const_size(MemOp ot) -{ - if (ot <= MO_32) { - return 1 << ot; - } else { - return 4; - } -} - static void gen_conditional_jump_labels(DisasContext *s, target_long diff, TCGLabel *not_taken, TCGLabel *taken) { @@ -2523,12 +1961,6 @@ static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) tcg_gen_movcond_tl(cc.cond, dest, cc.reg, cc.reg2, src, dest); } -static inline void gen_op_movl_T0_seg(DisasContext *s, X86Seg seg_reg) -{ - tcg_gen_ld32u_tl(s->T0, tcg_env, - offsetof(CPUX86State,segs[seg_reg].selector)); -} - static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) { TCGv selector = tcg_temp_new(); @@ -3019,9 +2451,6 @@ static void gen_sty_env_A0(DisasContext *s, int offset, bool align) tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop); } -static bool first = true; -static unsigned long limit; - #include "decode-new.h" #include "emit.c.inc" #include "decode-new.c.inc" @@ -3178,45 +2607,13 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) prefixes = 0; - if (first) { - const char *limit_str = getenv("QEMU_I386_LIMIT"); - limit = limit_str ? atol(limit_str) : -1; - first = false; - } - bool use_new = true; -#ifdef CONFIG_USER_ONLY - use_new &= limit > 0; -#endif - next_byte: s->prefix = prefixes; b = x86_ldub_code(env, s); /* Collect prefixes. */ switch (b) { - default: -#ifndef CONFIG_USER_ONLY - use_new &= b <= limit; -#endif - if (use_new && (b < 0xd8 || b >= 0xe0)) { - disas_insn_new(s, cpu, b); - return true; - } - break; case 0x0f: b = x86_ldub_code(env, s) + 0x100; -#ifndef CONFIG_USER_ONLY - use_new &= b <= limit; -#endif - if (use_new && - ((b >= 0x138 && b <= 0x19f) || - (b & ~9) == 0x1a0 || - b == 0x1af || b == 0x1b2 || - (b >= 0x1b4 && b <= 0x1b7) || - b == 0x1be || b == 0x1bf || b == 0x1c3 || - (b >= 0x1c8 && b <= 0x1cf))) { - disas_insn_new(s, cpu, b); - return true; - } break; case 0xf3: prefixes |= PREFIX_REPZ; @@ -3314,558 +2711,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) switch (b) { /**************************/ /* arith & logic */ - case 0x00 ... 0x05: - case 0x08 ... 0x0d: - case 0x10 ... 0x15: - case 0x18 ... 0x1d: - case 0x20 ... 0x25: - case 0x28 ... 0x2d: - case 0x30 ... 0x35: - case 0x38 ... 0x3d: - { - int f; - op = (b >> 3) & 7; - f = (b >> 1) & 3; - - ot = mo_b_d(b, dflag); - - switch(f) { - case 0: /* OP Ev, Gv */ - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else if (op == OP_XORL && rm == reg) { - xor_zero: - /* xor reg, reg optimisation */ - set_cc_op(s, CC_OP_CLR); - tcg_gen_movi_tl(s->T0, 0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - } else { - opreg = rm; - } - gen_op_mov_v_reg(s, ot, s->T1, reg); - gen_op(s, op, ot, opreg); - break; - case 1: /* OP Gv, Ev */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - reg = ((modrm >> 3) & 7) | REX_R(s); - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, s->T1, s->A0); - } else if (op == OP_XORL && rm == reg) { - goto xor_zero; - } else { - gen_op_mov_v_reg(s, ot, s->T1, rm); - } - gen_op(s, op, ot, reg); - break; - case 2: /* OP A, Iv */ - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, OR_EAX); - break; - } - } - break; - - case 0x82: - if (CODE64(s)) - goto illegal_op; - /* fall through */ - case 0x80: /* GRP1 */ - case 0x81: - case 0x83: - { - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - - if (mod != 3) { - if (b == 0x83) - s->rip_offset = 1; - else - s->rip_offset = insn_const_size(ot); - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = rm; - } - - switch(b) { - default: - case 0x80: - case 0x81: - case 0x82: - val = insn_get(env, s, ot); - break; - case 0x83: - val = (int8_t)insn_get(env, s, MO_8); - break; - } - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, opreg); - } - break; - - /**************************/ - /* inc, dec, and other misc arith */ - case 0x40 ... 0x47: /* inc Gv */ - ot = dflag; - gen_inc(s, ot, OR_EAX + (b & 7), 1); - break; - case 0x48 ... 0x4f: /* dec Gv */ - ot = dflag; - gen_inc(s, ot, OR_EAX + (b & 7), -1); - break; - case 0xf6: /* GRP3 */ - case 0xf7: - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - if (mod != 3) { - if (op == 0) { - s->rip_offset = insn_const_size(ot); - } - gen_lea_modrm(env, s, modrm); - /* For those below that handle locked memory, don't load here. */ - if (!(s->prefix & PREFIX_LOCK) - || op != 2) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - - switch(op) { - case 0: /* test */ - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - case 2: /* not */ - if (s->prefix & PREFIX_LOCK) { - if (mod == 3) { - goto illegal_op; - } - tcg_gen_movi_tl(s->T0, ~0); - tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, - s->mem_index, ot | MO_LE); - } else { - tcg_gen_not_tl(s->T0, s->T0); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - break; - case 3: /* neg */ - if (s->prefix & PREFIX_LOCK) { - TCGLabel *label1; - TCGv a0, t0, t1, t2; - - if (mod == 3) { - goto illegal_op; - } - a0 = s->A0; - t0 = s->T0; - label1 = gen_new_label(); - - gen_set_label(label1); - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - tcg_gen_mov_tl(t2, t0); - tcg_gen_neg_tl(t1, t0); - tcg_gen_atomic_cmpxchg_tl(t0, a0, t0, t1, - s->mem_index, ot | MO_LE); - tcg_gen_brcond_tl(TCG_COND_NE, t0, t2, label1); - - tcg_gen_neg_tl(s->T0, t0); - } else { - tcg_gen_neg_tl(s->T0, s->T0); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - gen_op_update_neg_cc(s); - set_cc_op(s, CC_OP_SUBB + ot); - break; - case 4: /* mul */ - switch(ot) { - case MO_8: - gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); - tcg_gen_ext8u_tl(s->T0, s->T0); - tcg_gen_ext8u_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_andi_tl(cpu_cc_src, s->T0, 0xff00); - set_cc_op(s, CC_OP_MULB); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); - tcg_gen_ext16u_tl(s->T0, s->T0); - tcg_gen_ext16u_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_shri_tl(s->T0, s->T0, 16); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - tcg_gen_mov_tl(cpu_cc_src, s->T0); - set_cc_op(s, CC_OP_MULW); - break; - default: - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULL); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - s->T0, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULQ); - break; -#endif - } - break; - case 5: /* imul */ - switch(ot) { - case MO_8: - gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); - tcg_gen_ext8s_tl(s->T0, s->T0); - tcg_gen_ext8s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext8s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - set_cc_op(s, CC_OP_MULB); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_ext16s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext16s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - tcg_gen_shri_tl(s->T0, s->T0, 16); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - set_cc_op(s, CC_OP_MULW); - break; - default: - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); - tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); - tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); - set_cc_op(s, CC_OP_MULL); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - s->T0, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULQ); - break; -#endif - } - break; - case 6: /* div */ - switch(ot) { - case MO_8: - gen_helper_divb_AL(tcg_env, s->T0); - break; - case MO_16: - gen_helper_divw_AX(tcg_env, s->T0); - break; - default: - case MO_32: - gen_helper_divl_EAX(tcg_env, s->T0); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_divq_EAX(tcg_env, s->T0); - break; -#endif - } - break; - case 7: /* idiv */ - switch(ot) { - case MO_8: - gen_helper_idivb_AL(tcg_env, s->T0); - break; - case MO_16: - gen_helper_idivw_AX(tcg_env, s->T0); - break; - default: - case MO_32: - gen_helper_idivl_EAX(tcg_env, s->T0); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_idivq_EAX(tcg_env, s->T0); - break; -#endif - } - break; - default: - goto unknown_op; - } - break; - - case 0xfe: /* GRP4 */ - case 0xff: /* GRP5 */ - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - if (op >= 2 && b == 0xfe) { - goto unknown_op; - } - if (CODE64(s)) { - if (op == 2 || op == 4) { - /* operand size for jumps is 64 bit */ - ot = MO_64; - } else if (op == 3 || op == 5) { - ot = dflag != MO_16 ? MO_32 + REX_W(s) : MO_16; - } else if (op == 6) { - /* default push size is 64 bit */ - ot = mo_pushpop(s, dflag); - } - } - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - if (op >= 2 && op != 3 && op != 5) - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - - switch(op) { - case 0: /* inc Ev */ - if (mod != 3) - opreg = OR_TMP0; - else - opreg = rm; - gen_inc(s, ot, opreg, 1); - break; - case 1: /* dec Ev */ - if (mod != 3) - opreg = OR_TMP0; - else - opreg = rm; - gen_inc(s, ot, opreg, -1); - break; - case 2: /* call Ev */ - /* XXX: optimize if memory (no 'and' is necessary) */ - if (dflag == MO_16) { - tcg_gen_ext16u_tl(s->T0, s->T0); - } - gen_push_v(s, eip_next_tl(s)); - gen_op_jmp_v(s, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; - break; - case 3: /* lcall Ev */ - if (mod == 3) { - goto illegal_op; - } - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T1, s->A0); - gen_far_call(s); - break; - case 4: /* jmp Ev */ - if (dflag == MO_16) { - tcg_gen_ext16u_tl(s->T0, s->T0); - } - gen_op_jmp_v(s, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; - break; - case 5: /* ljmp Ev */ - if (mod == 3) { - goto illegal_op; - } - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T1, s->A0); - gen_far_jmp(s); - break; - case 6: /* push Ev */ - gen_push_v(s, s->T0); - break; - default: - goto unknown_op; - } - break; - - case 0x84: /* test Ev, Gv */ - case 0x85: - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_v_reg(s, ot, s->T1, reg); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0xa8: /* test eAX, Iv */ - case 0xa9: - ot = mo_b_d(b, dflag); - val = insn_get(env, s, ot); - - gen_op_mov_v_reg(s, ot, s->T0, OR_EAX); - tcg_gen_movi_tl(s->T1, val); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0x98: /* CWDE/CBW */ - switch (dflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - tcg_gen_ext32s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_64, R_EAX, s->T0); - break; -#endif - case MO_32: - gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_32, R_EAX, s->T0); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_8, s->T0, R_EAX); - tcg_gen_ext8s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - break; - default: - g_assert_not_reached(); - } - break; - case 0x99: /* CDQ/CWD */ - switch (dflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_mov_v_reg(s, MO_64, s->T0, R_EAX); - tcg_gen_sari_tl(s->T0, s->T0, 63); - gen_op_mov_reg_v(s, MO_64, R_EDX, s->T0); - break; -#endif - case MO_32: - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - tcg_gen_ext32s_tl(s->T0, s->T0); - tcg_gen_sari_tl(s->T0, s->T0, 31); - gen_op_mov_reg_v(s, MO_32, R_EDX, s->T0); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_sari_tl(s->T0, s->T0, 15); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - break; - default: - g_assert_not_reached(); - } - break; - case 0x1af: /* imul Gv, Ev */ - case 0x69: /* imul Gv, Ev, I */ - case 0x6b: - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - if (b == 0x69) - s->rip_offset = insn_const_size(ot); - else if (b == 0x6b) - s->rip_offset = 1; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - if (b == 0x69) { - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - } else if (b == 0x6b) { - val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T1, val); - } else { - gen_op_mov_v_reg(s, ot, s->T1, reg); - } - switch (ot) { -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_muls2_i64(cpu_regs[reg], s->T1, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, s->T1); - break; -#endif - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); - break; - default: - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_ext16s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext16s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - } - set_cc_op(s, CC_OP_MULB + ot); - break; case 0x1c0: case 0x1c1: /* xadd Ev, Gv */ ot = mo_b_d(b, dflag); @@ -4023,375 +2868,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; /**************************/ - /* push/pop */ - case 0x50 ... 0x57: /* push */ - gen_op_mov_v_reg(s, MO_32, s->T0, (b & 7) | REX_B(s)); - gen_push_v(s, s->T0); - break; - case 0x58 ... 0x5f: /* pop */ - ot = gen_pop_T0(s); - /* NOTE: order is important for pop %sp */ - gen_pop_update(s, ot); - gen_op_mov_reg_v(s, ot, (b & 7) | REX_B(s), s->T0); - break; - case 0x60: /* pusha */ - if (CODE64(s)) - goto illegal_op; - gen_pusha(s); - break; - case 0x61: /* popa */ - if (CODE64(s)) - goto illegal_op; - gen_popa(s); - break; - case 0x68: /* push Iv */ - case 0x6a: - ot = mo_pushpop(s, dflag); - if (b == 0x68) - val = insn_get(env, s, ot); - else - val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T0, val); - gen_push_v(s, s->T0); - break; - case 0x8f: /* pop Ev */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - ot = gen_pop_T0(s); - if (mod == 3) { - /* NOTE: order is important for pop %sp */ - gen_pop_update(s, ot); - rm = (modrm & 7) | REX_B(s); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - /* NOTE: order is important too for MMU exceptions */ - s->popl_esp_hack = 1 << ot; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - s->popl_esp_hack = 0; - gen_pop_update(s, ot); - } - break; - case 0xc8: /* enter */ - { - int level; - val = x86_lduw_code(env, s); - level = x86_ldub_code(env, s); - gen_enter(s, val, level); - } - break; - case 0xc9: /* leave */ - gen_leave(s); - break; - case 0x06: /* push es */ - case 0x0e: /* push cs */ - case 0x16: /* push ss */ - case 0x1e: /* push ds */ - if (CODE64(s)) - goto illegal_op; - gen_op_movl_T0_seg(s, b >> 3); - gen_push_v(s, s->T0); - break; - case 0x1a0: /* push fs */ - case 0x1a8: /* push gs */ - gen_op_movl_T0_seg(s, (b >> 3) & 7); - gen_push_v(s, s->T0); - break; - case 0x07: /* pop es */ - case 0x17: /* pop ss */ - case 0x1f: /* pop ds */ - if (CODE64(s)) - goto illegal_op; - reg = b >> 3; - ot = gen_pop_T0(s); - gen_movl_seg(s, reg, s->T0); - gen_pop_update(s, ot); - break; - case 0x1a1: /* pop fs */ - case 0x1a9: /* pop gs */ - ot = gen_pop_T0(s); - gen_movl_seg(s, (b >> 3) & 7, s->T0); - gen_pop_update(s, ot); - break; - - /**************************/ - /* mov */ - case 0x88: - case 0x89: /* mov Gv, Ev */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - /* generate a generic store */ - gen_ldst_modrm(env, s, modrm, ot, reg, 1); - break; - case 0xc6: - case 0xc7: /* mov Ev, Iv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod != 3) { - s->rip_offset = insn_const_size(ot); - gen_lea_modrm(env, s, modrm); - } - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T0, val); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, (modrm & 7) | REX_B(s), s->T0); - } - break; - case 0x8a: - case 0x8b: /* mov Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0x8e: /* mov seg, Gv */ - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - if (reg >= 6 || reg == R_CS) - goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_movl_seg(s, reg, s->T0); - break; - case 0x8c: /* mov Gv, seg */ - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - if (reg >= 6) - goto illegal_op; - gen_op_movl_T0_seg(s, reg); - ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - break; - - case 0x1b6: /* movzbS Gv, Eb */ - case 0x1b7: /* movzwS Gv, Eb */ - case 0x1be: /* movsbS Gv, Eb */ - case 0x1bf: /* movswS Gv, Eb */ - { - MemOp d_ot; - MemOp s_ot; - - /* d_ot is the size of destination */ - d_ot = dflag; - /* ot is the size of source */ - ot = (b & 1) + MO_8; - /* s_ot is the sign+size of source */ - s_ot = b & 8 ? MO_SIGN | ot : ot; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - - if (mod == 3) { - if (s_ot == MO_SB && byte_reg_is_xH(s, rm)) { - tcg_gen_sextract_tl(s->T0, cpu_regs[rm - 4], 8, 8); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - switch (s_ot) { - case MO_UB: - tcg_gen_ext8u_tl(s->T0, s->T0); - break; - case MO_SB: - tcg_gen_ext8s_tl(s->T0, s->T0); - break; - case MO_UW: - tcg_gen_ext16u_tl(s->T0, s->T0); - break; - default: - case MO_SW: - tcg_gen_ext16s_tl(s->T0, s->T0); - break; - } - } - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, s_ot, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } - } - break; - - case 0x8d: /* lea */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - reg = ((modrm >> 3) & 7) | REX_R(s); - { - AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a, false); - gen_lea_v_seg(s, s->aflag, ea, -1, -1); - gen_op_mov_reg_v(s, dflag, reg, s->A0); - } - break; - - case 0xa0: /* mov EAX, Ov */ - case 0xa1: - case 0xa2: /* mov Ov, EAX */ - case 0xa3: - { - target_ulong offset_addr; - - ot = mo_b_d(b, dflag); - offset_addr = insn_get_addr(env, s, s->aflag); - tcg_gen_movi_tl(s->A0, offset_addr); - gen_add_A0_ds_seg(s); - if ((b & 2) == 0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_op_mov_reg_v(s, ot, R_EAX, s->T0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, R_EAX); - gen_op_st_v(s, ot, s->T0, s->A0); - } - } - break; - case 0xd7: /* xlat */ - tcg_gen_mov_tl(s->A0, cpu_regs[R_EBX]); - tcg_gen_ext8u_tl(s->T0, cpu_regs[R_EAX]); - tcg_gen_add_tl(s->A0, s->A0, s->T0); - gen_add_A0_ds_seg(s); - gen_op_ld_v(s, MO_8, s->T0, s->A0); - gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); - break; - case 0xb0 ... 0xb7: /* mov R, Ib */ - val = insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T0, val); - gen_op_mov_reg_v(s, MO_8, (b & 7) | REX_B(s), s->T0); - break; - case 0xb8 ... 0xbf: /* mov R, Iv */ -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - uint64_t tmp; - /* 64 bit case */ - tmp = x86_ldq_code(env, s); - reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(s->T0, tmp); - gen_op_mov_reg_v(s, MO_64, reg, s->T0); - } else -#endif - { - ot = dflag; - val = insn_get(env, s, ot); - reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(s->T0, val); - gen_op_mov_reg_v(s, ot, reg, s->T0); - } - break; - - case 0x91 ... 0x97: /* xchg R, EAX */ - do_xchg_reg_eax: - ot = dflag; - reg = (b & 7) | REX_B(s); - rm = R_EAX; - goto do_xchg_reg; - case 0x86: - case 0x87: /* xchg Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - do_xchg_reg: - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_mov_v_reg(s, ot, s->T1, rm); - gen_op_mov_reg_v(s, ot, rm, s->T0); - gen_op_mov_reg_v(s, ot, reg, s->T1); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(s, ot, s->T0, reg); - /* for xchg, lock is implicit */ - tcg_gen_atomic_xchg_tl(s->T1, s->A0, s->T0, - s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(s, ot, reg, s->T1); - } - break; - case 0xc4: /* les Gv */ - /* In CODE64 this is VEX3; see above. */ - op = R_ES; - goto do_lxx; - case 0xc5: /* lds Gv */ - /* In CODE64 this is VEX2; see above. */ - op = R_DS; - goto do_lxx; - case 0x1b2: /* lss Gv */ - op = R_SS; - goto do_lxx; - case 0x1b4: /* lfs Gv */ - op = R_FS; - goto do_lxx; - case 0x1b5: /* lgs Gv */ - op = R_GS; - do_lxx: - ot = dflag != MO_16 ? MO_32 : MO_16; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - /* load the segment first to handle exceptions properly */ - gen_op_ld_v(s, MO_16, s->T0, s->A0); - gen_movl_seg(s, op, s->T0); - /* then put the data */ - gen_op_mov_reg_v(s, ot, reg, s->T1); - break; - - /************************/ /* shifts */ - case 0xc0: - case 0xc1: - /* shift Ev,Ib */ - shift = 2; - grp2: - { - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - - if (mod != 3) { - if (shift == 2) { - s->rip_offset = 1; - } - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = (modrm & 7) | REX_B(s); - } - - /* simpler op */ - if (shift == 0) { - gen_shift(s, op, ot, opreg, OR_ECX); - } else { - if (shift == 2) { - shift = x86_ldub_code(env, s); - } - gen_shifti(s, op, ot, opreg, shift); - } - } - break; - case 0xd0: - case 0xd1: - /* shift Ev,1 */ - shift = 1; - goto grp2; - case 0xd2: - case 0xd3: - /* shift Ev,cl */ - shift = 0; - goto grp2; - case 0x1a4: /* shld imm */ op = 0; shift = 1; @@ -4988,374 +3465,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } } break; - /************************/ - /* string ops */ - - case 0xa4: /* movsS */ - case 0xa5: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_movs(s, ot); - } else { - gen_movs(s, ot); - } - break; - - case 0xaa: /* stosS */ - case 0xab: - ot = mo_b_d(b, dflag); - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_stos(s, ot); - } else { - gen_stos(s, ot); - } - break; - case 0xac: /* lodsS */ - case 0xad: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_lods(s, ot); - } else { - gen_lods(s, ot); - } - break; - case 0xae: /* scasS */ - case 0xaf: - ot = mo_b_d(b, dflag); - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - if (prefixes & PREFIX_REPNZ) { - gen_repz_scas(s, ot, 1); - } else if (prefixes & PREFIX_REPZ) { - gen_repz_scas(s, ot, 0); - } else { - gen_scas(s, ot); - } - break; - - case 0xa6: /* cmpsS */ - case 0xa7: - ot = mo_b_d(b, dflag); - if (prefixes & PREFIX_REPNZ) { - gen_repz_cmps(s, ot, 1); - } else if (prefixes & PREFIX_REPZ) { - gen_repz_cmps(s, ot, 0); - } else { - gen_cmps(s, ot); - } - break; - case 0x6c: /* insS */ - case 0x6d: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - if (!gen_check_io(s, ot, s->tmp2_i32, - SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) { - break; - } - translator_io_start(&s->base); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_ins(s, ot); - } else { - gen_ins(s, ot); - } - break; - case 0x6e: /* outsS */ - case 0x6f: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_STR_MASK)) { - break; - } - translator_io_start(&s->base); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_outs(s, ot); - } else { - gen_outs(s, ot); - } - break; - - /************************/ - /* port I/O */ - - case 0xe4: - case 0xe5: - ot = mo_b_d32(b, dflag); - val = x86_ldub_code(env, s); - tcg_gen_movi_i32(s->tmp2_i32, val); - if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) { - break; - } - translator_io_start(&s->base); - gen_helper_in_func(ot, s->T1, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, R_EAX, s->T1); - gen_bpt_io(s, s->tmp2_i32, ot); - break; - case 0xe6: - case 0xe7: - ot = mo_b_d32(b, dflag); - val = x86_ldub_code(env, s); - tcg_gen_movi_i32(s->tmp2_i32, val); - if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { - break; - } - translator_io_start(&s->base); - gen_op_mov_v_reg(s, ot, s->T1, R_EAX); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_bpt_io(s, s->tmp2_i32, ot); - break; - case 0xec: - case 0xed: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) { - break; - } - translator_io_start(&s->base); - gen_helper_in_func(ot, s->T1, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, R_EAX, s->T1); - gen_bpt_io(s, s->tmp2_i32, ot); - break; - case 0xee: - case 0xef: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { - break; - } - translator_io_start(&s->base); - gen_op_mov_v_reg(s, ot, s->T1, R_EAX); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_bpt_io(s, s->tmp2_i32, ot); - break; - - /************************/ - /* control */ - case 0xc2: /* ret im */ - val = x86_ldsw_code(env, s); - ot = gen_pop_T0(s); - gen_stack_update(s, val + (1 << ot)); - /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(s, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; - break; - case 0xc3: /* ret */ - ot = gen_pop_T0(s); - gen_pop_update(s, ot); - /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(s, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; - break; - case 0xca: /* lret im */ - val = x86_ldsw_code(env, s); - do_lret: - if (PE(s) && !VM86(s)) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_lret_protected(tcg_env, tcg_constant_i32(dflag - 1), - tcg_constant_i32(val)); - } else { - gen_stack_A0(s); - /* pop offset */ - gen_op_ld_v(s, dflag, s->T0, s->A0); - /* NOTE: keeping EIP updated is not a problem in case of - exception */ - gen_op_jmp_v(s, s->T0); - /* pop selector */ - gen_add_A0_im(s, 1 << dflag); - gen_op_ld_v(s, dflag, s->T0, s->A0); - gen_op_movl_seg_real(s, R_CS, s->T0); - /* add stack offset */ - gen_stack_update(s, val + (2 << dflag)); - } - s->base.is_jmp = DISAS_EOB_ONLY; - break; - case 0xcb: /* lret */ - val = 0; - goto do_lret; - case 0xcf: /* iret */ - gen_svm_check_intercept(s, SVM_EXIT_IRET); - if (!PE(s) || VM86(s)) { - /* real mode or vm86 mode */ - if (!check_vm86_iopl(s)) { - break; - } - gen_helper_iret_real(tcg_env, tcg_constant_i32(dflag - 1)); - } else { - gen_helper_iret_protected(tcg_env, tcg_constant_i32(dflag - 1), - eip_next_i32(s)); - } - set_cc_op(s, CC_OP_EFLAGS); - s->base.is_jmp = DISAS_EOB_ONLY; - break; - case 0xe8: /* call im */ - { - int diff = (dflag != MO_16 - ? (int32_t)insn_get(env, s, MO_32) - : (int16_t)insn_get(env, s, MO_16)); - gen_push_v(s, eip_next_tl(s)); - gen_bnd_jmp(s); - gen_update_cc_op(s); - gen_jmp_rel(s, dflag, diff, 0); - } - break; - case 0x9a: /* lcall im */ - { - unsigned int selector, offset; - - if (CODE64(s)) - goto illegal_op; - ot = dflag; - offset = insn_get(env, s, ot); - selector = insn_get(env, s, MO_16); - - tcg_gen_movi_tl(s->T0, offset); - tcg_gen_movi_tl(s->T1, selector); - } - gen_far_call(s); - break; - case 0xe9: /* jmp im */ - { - int diff = (dflag != MO_16 - ? (int32_t)insn_get(env, s, MO_32) - : (int16_t)insn_get(env, s, MO_16)); - gen_bnd_jmp(s); - gen_update_cc_op(s); - gen_jmp_rel(s, dflag, diff, 0); - } - break; - case 0xea: /* ljmp im */ - { - unsigned int selector, offset; - - if (CODE64(s)) - goto illegal_op; - ot = dflag; - offset = insn_get(env, s, ot); - selector = insn_get(env, s, MO_16); - - tcg_gen_movi_tl(s->T0, offset); - tcg_gen_movi_tl(s->T1, selector); - } - gen_far_jmp(s); - break; - case 0xeb: /* jmp Jb */ - { - int diff = (int8_t)insn_get(env, s, MO_8); - gen_update_cc_op(s); - gen_jmp_rel(s, dflag, diff, 0); - } - break; - case 0x70 ... 0x7f: /* jcc Jb */ - { - int diff = (int8_t)insn_get(env, s, MO_8); - gen_bnd_jmp(s); - gen_jcc(s, b, diff); - } - break; - case 0x180 ... 0x18f: /* jcc Jv */ - { - int diff = (dflag != MO_16 - ? (int32_t)insn_get(env, s, MO_32) - : (int16_t)insn_get(env, s, MO_16)); - gen_bnd_jmp(s); - gen_jcc(s, b, diff); - } - break; - - case 0x190 ... 0x19f: /* setcc Gv */ - modrm = x86_ldub_code(env, s); - gen_setcc1(s, b, s->T0); - gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); - break; - case 0x140 ... 0x14f: /* cmov Gv, Ev */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_cmovcc1(s, b ^ 1, s->T0, cpu_regs[reg]); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - /************************/ - /* flags */ - case 0x9c: /* pushf */ - gen_svm_check_intercept(s, SVM_EXIT_PUSHF); - if (check_vm86_iopl(s)) { - gen_update_cc_op(s); - gen_helper_read_eflags(s->T0, tcg_env); - gen_push_v(s, s->T0); - } - break; - case 0x9d: /* popf */ - gen_svm_check_intercept(s, SVM_EXIT_POPF); - if (check_vm86_iopl(s)) { - int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK; - - if (CPL(s) == 0) { - mask |= IF_MASK | IOPL_MASK; - } else if (CPL(s) <= IOPL(s)) { - mask |= IF_MASK; - } - if (dflag == MO_16) { - mask &= 0xffff; - } - - ot = gen_pop_T0(s); - gen_helper_write_eflags(tcg_env, s->T0, tcg_constant_i32(mask)); - gen_pop_update(s, ot); - set_cc_op(s, CC_OP_EFLAGS); - /* abort translation because TF/AC flag may change */ - s->base.is_jmp = DISAS_EOB_NEXT; - } - break; - case 0x9e: /* sahf */ - if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) - goto illegal_op; - tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8); - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); - tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); - break; - case 0x9f: /* lahf */ - if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) - goto illegal_op; - gen_compute_eflags(s); - /* Note: gen_compute_eflags() only gives the condition codes */ - tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); - tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); - break; - case 0xf5: /* cmc */ - gen_compute_eflags(s); - tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); - break; - case 0xf8: /* clc */ - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); - break; - case 0xf9: /* stc */ - gen_compute_eflags(s); - tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); - break; - case 0xfc: /* cld */ - tcg_gen_movi_i32(s->tmp2_i32, 1); - tcg_gen_st_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, df)); - break; - case 0xfd: /* std */ - tcg_gen_movi_i32(s->tmp2_i32, -1); - tcg_gen_st_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, df)); - break; /************************/ /* bit operations */ @@ -5546,188 +3655,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_op_mov_reg_v(s, ot, reg, s->T0); break; - /************************/ - /* bcd */ - case 0x27: /* daa */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_daa(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x2f: /* das */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_das(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x37: /* aaa */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_aaa(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x3f: /* aas */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_aas(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0xd4: /* aam */ - if (CODE64(s)) - goto illegal_op; - val = x86_ldub_code(env, s); - if (val == 0) { - gen_exception(s, EXCP00_DIVZ); - } else { - gen_helper_aam(tcg_env, tcg_constant_i32(val)); - set_cc_op(s, CC_OP_LOGICB); - } - break; - case 0xd5: /* aad */ - if (CODE64(s)) - goto illegal_op; - val = x86_ldub_code(env, s); - gen_helper_aad(tcg_env, tcg_constant_i32(val)); - set_cc_op(s, CC_OP_LOGICB); - break; - /************************/ - /* misc */ - case 0x90: /* nop */ - /* XXX: correct lock test for all insn */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - /* If REX_B is set, then this is xchg eax, r8d, not a nop. */ - if (REX_B(s)) { - goto do_xchg_reg_eax; - } - if (prefixes & PREFIX_REPZ) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_pause(tcg_env, cur_insn_len_i32(s)); - s->base.is_jmp = DISAS_NORETURN; - } - break; - case 0x9b: /* fwait */ - if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == - (HF_MP_MASK | HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX); - } else { - /* needs to be treated as I/O because of ferr_irq */ - translator_io_start(&s->base); - gen_helper_fwait(tcg_env); - } - break; - case 0xcc: /* int3 */ - gen_interrupt(s, EXCP03_INT3); - break; - case 0xcd: /* int N */ - val = x86_ldub_code(env, s); - if (check_vm86_iopl(s)) { - gen_interrupt(s, val); - } - break; - case 0xce: /* into */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_into(tcg_env, cur_insn_len_i32(s)); - break; -#ifdef WANT_ICEBP - case 0xf1: /* icebp (undocumented, exits to external debugger) */ - gen_svm_check_intercept(s, SVM_EXIT_ICEBP); - gen_debug(s); - break; -#endif - case 0xfa: /* cli */ - if (check_iopl(s)) { - gen_reset_eflags(s, IF_MASK); - } - break; - case 0xfb: /* sti */ - if (check_iopl(s)) { - gen_set_eflags(s, IF_MASK); - /* interruptions are enabled only the first insn after sti */ - gen_update_eip_next(s); - gen_eob_inhibit_irq(s); - } - break; - case 0x62: /* bound */ - if (CODE64(s)) - goto illegal_op; - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_lea_modrm(env, s, modrm); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - if (ot == MO_16) { - gen_helper_boundw(tcg_env, s->A0, s->tmp2_i32); - } else { - gen_helper_boundl(tcg_env, s->A0, s->tmp2_i32); - } - break; - case 0x1c8 ... 0x1cf: /* bswap reg */ - reg = (b & 7) | REX_B(s); -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - tcg_gen_bswap64_i64(cpu_regs[reg], cpu_regs[reg]); - break; - } -#endif - tcg_gen_bswap32_tl(cpu_regs[reg], cpu_regs[reg], TCG_BSWAP_OZ); - break; - case 0xd6: /* salc */ - if (CODE64(s)) - goto illegal_op; - gen_compute_eflags_c(s, s->T0); - tcg_gen_neg_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); - break; - case 0xe0: /* loopnz */ - case 0xe1: /* loopz */ - case 0xe2: /* loop */ - case 0xe3: /* jecxz */ - { - TCGLabel *l1, *l2; - int diff = (int8_t)insn_get(env, s, MO_8); - - l1 = gen_new_label(); - l2 = gen_new_label(); - gen_update_cc_op(s); - b &= 3; - switch(b) { - case 0: /* loopnz */ - case 1: /* loopz */ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_op_jz_ecx(s, l2); - gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); - break; - case 2: /* loop */ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_op_jnz_ecx(s, l1); - break; - default: - case 3: /* jcxz */ - gen_op_jz_ecx(s, l1); - break; - } - - gen_set_label(l2); - gen_jmp_rel_csize(s, 0, 1); - - gen_set_label(l1); - gen_jmp_rel(s, dflag, diff, 0); - } - break; case 0x130: /* wrmsr */ case 0x132: /* rdmsr */ if (check_cpl0(s)) { @@ -5815,14 +3742,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) gen_update_eip_cur(s); gen_helper_cpuid(tcg_env); break; - case 0xf4: /* hlt */ - if (check_cpl0(s)) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_hlt(tcg_env, cur_insn_len_i32(s)); - s->base.is_jmp = DISAS_NORETURN; - } - break; case 0x100: modrm = x86_ldub_code(env, s); mod = (modrm >> 6) & 3; @@ -6227,72 +4146,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) /* nothing to do */ } break; - case 0x63: /* arpl or movslS (x86_64) */ -#ifdef TARGET_X86_64 - if (CODE64(s)) { - int d_ot; - /* d_ot is the size of destination */ - d_ot = dflag; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - - if (mod == 3) { - gen_op_mov_v_reg(s, MO_32, s->T0, rm); - /* sign extend */ - if (d_ot == MO_64) { - tcg_gen_ext32s_tl(s->T0, s->T0); - } - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32 | MO_SIGN, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } - } else -#endif - { - TCGLabel *label1; - TCGv t0, t1, t2; - - if (!PE(s) || VM86(s)) - goto illegal_op; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - ot = MO_16; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - rm = modrm & 7; - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, t0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, t0, rm); - } - gen_op_mov_v_reg(s, ot, t1, reg); - tcg_gen_andi_tl(s->tmp0, t0, 3); - tcg_gen_andi_tl(t1, t1, 3); - tcg_gen_movi_tl(t2, 0); - label1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GE, s->tmp0, t1, label1); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_or_tl(t0, t0, t1); - tcg_gen_movi_tl(t2, CC_Z); - gen_set_label(label1); - if (mod != 3) { - gen_op_st_v(s, ot, t0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, t0); - } - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); - } - break; case 0x102: /* lar */ case 0x103: /* lsl */ { @@ -6619,18 +4472,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } break; /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ - case 0x1c3: /* MOVNTI reg, mem */ - if (!(s->cpuid_features & CPUID_SSE2)) - goto illegal_op; - ot = mo_64_32(dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - reg = ((modrm >> 3) & 7) | REX_R(s); - /* generate a generic store */ - gen_ldst_modrm(env, s, modrm, ot, reg, 1); - break; case 0x1ae: modrm = x86_ldub_code(env, s); switch (modrm) { @@ -6873,13 +4714,19 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) set_cc_op(s, CC_OP_POPCNT); break; + case 0 ... 0xd7: + case 0xe0 ... 0xff: case 0x10e ... 0x117: case 0x128 ... 0x12f: - case 0x138 ... 0x13f: - case 0x150 ... 0x17f: - case 0x1c2: - case 0x1c4 ... 0x1c6: - case 0x1d0 ... 0x1fe: + case 0x138 ... 0x19f: + case 0x1a0 ... 0x1a1: + case 0x1a8 ... 0x1a9: + case 0x1af: + case 0x1b2: + case 0x1b4 ... 0x1b7: + case 0x1be ... 0x1bf: + case 0x1c2 ... 0x1c6: + case 0x1c8 ... 0x1ff: disas_insn_new(s, cpu, b); break; default: From 7795b455ddc491946c90953358b36915d42d386a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Oct 2023 08:42:58 +0200 Subject: [PATCH 0525/2884] target/i386: decode x87 instructions in a separate function These are unlikely to be converted to the table-based decoding soon (perhaps there could be generic ESC decoding in decode-new.c.inc for the Mod/RM byte, but not operand decoding), so keep them separate from the remaining legacy-decoded instructions. Acked-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 1120 ++++++++++++++++++----------------- 1 file changed, 566 insertions(+), 554 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index f4e5843d5f..973bf07ef2 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2553,6 +2553,570 @@ static void gen_cmpxchg16b(DisasContext *s, CPUX86State *env, int modrm) } #endif +static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) +{ + CPUX86State *env = cpu_env(cpu); + bool update_fip = true; + int modrm, mod, rm, op; + + if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { + /* if CR0.EM or CR0.TS are set, generate an FPU exception */ + /* XXX: what to do if illegal op ? */ + gen_exception(s, EXCP07_PREX); + return true; + } + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + rm = modrm & 7; + op = ((b & 7) << 3) | ((modrm >> 3) & 7); + if (mod != 3) { + /* memory op */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(s, a, false); + TCGv last_addr = tcg_temp_new(); + bool update_fdp = true; + + tcg_gen_mov_tl(last_addr, ea); + gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); + + switch (op) { + case 0x00 ... 0x07: /* fxxxs */ + case 0x10 ... 0x17: /* fixxxl */ + case 0x20 ... 0x27: /* fxxxl */ + case 0x30 ... 0x37: /* fixxx */ + { + int op1; + op1 = op & 7; + + switch (op >> 4) { + case 0: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_flds_FT0(tcg_env, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fldl_FT0(tcg_env, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); + break; + } + + gen_helper_fp_arith_ST0_FT0(op1); + if (op1 == 3) { + /* fcomp needs pop */ + gen_helper_fpop(tcg_env); + } + } + break; + case 0x08: /* flds */ + case 0x0a: /* fsts */ + case 0x0b: /* fstps */ + case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ + case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ + case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ + switch (op & 7) { + case 0: + switch (op >> 4) { + case 0: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_flds_ST0(tcg_env, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fldl_ST0(tcg_env, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); + break; + } + break; + case 1: + /* XXX: the corresponding CPUID bit must be tested ! */ + switch (op >> 4) { + case 1: + gen_helper_fisttl_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 2: + gen_helper_fisttll_ST0(s->tmp1_i64, tcg_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + break; + case 3: + default: + gen_helper_fistt_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + gen_helper_fpop(tcg_env); + break; + default: + switch (op >> 4) { + case 0: + gen_helper_fsts_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 1: + gen_helper_fistl_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 2: + gen_helper_fstl_ST0(s->tmp1_i64, tcg_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + break; + case 3: + default: + gen_helper_fist_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + if ((op & 7) == 3) { + gen_helper_fpop(tcg_env); + } + break; + } + break; + case 0x0c: /* fldenv mem */ + gen_helper_fldenv(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x0d: /* fldcw mem */ + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + gen_helper_fldcw(tcg_env, s->tmp2_i32); + update_fip = update_fdp = false; + break; + case 0x0e: /* fnstenv mem */ + gen_helper_fstenv(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x0f: /* fnstcw mem */ + gen_helper_fnstcw(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + update_fip = update_fdp = false; + break; + case 0x1d: /* fldt mem */ + gen_helper_fldt_ST0(tcg_env, s->A0); + break; + case 0x1f: /* fstpt mem */ + gen_helper_fstt_ST0(tcg_env, s->A0); + gen_helper_fpop(tcg_env); + break; + case 0x2c: /* frstor mem */ + gen_helper_frstor(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x2e: /* fnsave mem */ + gen_helper_fsave(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x2f: /* fnstsw mem */ + gen_helper_fnstsw(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + update_fip = update_fdp = false; + break; + case 0x3c: /* fbld */ + gen_helper_fbld_ST0(tcg_env, s->A0); + break; + case 0x3e: /* fbstp */ + gen_helper_fbst_ST0(tcg_env, s->A0); + gen_helper_fpop(tcg_env); + break; + case 0x3d: /* fildll */ + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fildll_ST0(tcg_env, s->tmp1_i64); + break; + case 0x3f: /* fistpll */ + gen_helper_fistll_ST0(s->tmp1_i64, tcg_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fpop(tcg_env); + break; + default: + return false; + } + + if (update_fdp) { + int last_seg = s->override >= 0 ? s->override : a.def_seg; + + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, + segs[last_seg].selector)); + tcg_gen_st16_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, fpds)); + tcg_gen_st_tl(last_addr, tcg_env, + offsetof(CPUX86State, fpdp)); + } + } else { + /* register float ops */ + int opreg = rm; + + switch (op) { + case 0x08: /* fld sti */ + gen_helper_fpush(tcg_env); + gen_helper_fmov_ST0_STN(tcg_env, + tcg_constant_i32((opreg + 1) & 7)); + break; + case 0x09: /* fxchg sti */ + case 0x29: /* fxchg4 sti, undocumented op */ + case 0x39: /* fxchg7 sti, undocumented op */ + gen_helper_fxchg_ST0_STN(tcg_env, tcg_constant_i32(opreg)); + break; + case 0x0a: /* grp d9/2 */ + switch (rm) { + case 0: /* fnop */ + /* + * check exceptions (FreeBSD FPU probe) + * needs to be treated as I/O because of ferr_irq + */ + translator_io_start(&s->base); + gen_helper_fwait(tcg_env); + update_fip = false; + break; + default: + return false; + } + break; + case 0x0c: /* grp d9/4 */ + switch (rm) { + case 0: /* fchs */ + gen_helper_fchs_ST0(tcg_env); + break; + case 1: /* fabs */ + gen_helper_fabs_ST0(tcg_env); + break; + case 4: /* ftst */ + gen_helper_fldz_FT0(tcg_env); + gen_helper_fcom_ST0_FT0(tcg_env); + break; + case 5: /* fxam */ + gen_helper_fxam_ST0(tcg_env); + break; + default: + return false; + } + break; + case 0x0d: /* grp d9/5 */ + { + switch (rm) { + case 0: + gen_helper_fpush(tcg_env); + gen_helper_fld1_ST0(tcg_env); + break; + case 1: + gen_helper_fpush(tcg_env); + gen_helper_fldl2t_ST0(tcg_env); + break; + case 2: + gen_helper_fpush(tcg_env); + gen_helper_fldl2e_ST0(tcg_env); + break; + case 3: + gen_helper_fpush(tcg_env); + gen_helper_fldpi_ST0(tcg_env); + break; + case 4: + gen_helper_fpush(tcg_env); + gen_helper_fldlg2_ST0(tcg_env); + break; + case 5: + gen_helper_fpush(tcg_env); + gen_helper_fldln2_ST0(tcg_env); + break; + case 6: + gen_helper_fpush(tcg_env); + gen_helper_fldz_ST0(tcg_env); + break; + default: + return false; + } + } + break; + case 0x0e: /* grp d9/6 */ + switch (rm) { + case 0: /* f2xm1 */ + gen_helper_f2xm1(tcg_env); + break; + case 1: /* fyl2x */ + gen_helper_fyl2x(tcg_env); + break; + case 2: /* fptan */ + gen_helper_fptan(tcg_env); + break; + case 3: /* fpatan */ + gen_helper_fpatan(tcg_env); + break; + case 4: /* fxtract */ + gen_helper_fxtract(tcg_env); + break; + case 5: /* fprem1 */ + gen_helper_fprem1(tcg_env); + break; + case 6: /* fdecstp */ + gen_helper_fdecstp(tcg_env); + break; + default: + case 7: /* fincstp */ + gen_helper_fincstp(tcg_env); + break; + } + break; + case 0x0f: /* grp d9/7 */ + switch (rm) { + case 0: /* fprem */ + gen_helper_fprem(tcg_env); + break; + case 1: /* fyl2xp1 */ + gen_helper_fyl2xp1(tcg_env); + break; + case 2: /* fsqrt */ + gen_helper_fsqrt(tcg_env); + break; + case 3: /* fsincos */ + gen_helper_fsincos(tcg_env); + break; + case 5: /* fscale */ + gen_helper_fscale(tcg_env); + break; + case 4: /* frndint */ + gen_helper_frndint(tcg_env); + break; + case 6: /* fsin */ + gen_helper_fsin(tcg_env); + break; + default: + case 7: /* fcos */ + gen_helper_fcos(tcg_env); + break; + } + break; + case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ + case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ + case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ + { + int op1; + + op1 = op & 7; + if (op >= 0x20) { + gen_helper_fp_arith_STN_ST0(op1, opreg); + if (op >= 0x30) { + gen_helper_fpop(tcg_env); + } + } else { + gen_helper_fmov_FT0_STN(tcg_env, + tcg_constant_i32(opreg)); + gen_helper_fp_arith_ST0_FT0(op1); + } + } + break; + case 0x02: /* fcom */ + case 0x22: /* fcom2, undocumented op */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcom_ST0_FT0(tcg_env); + break; + case 0x03: /* fcomp */ + case 0x23: /* fcomp3, undocumented op */ + case 0x32: /* fcomp5, undocumented op */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + break; + case 0x15: /* da/5 */ + switch (rm) { + case 1: /* fucompp */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + gen_helper_fpop(tcg_env); + break; + default: + return false; + } + break; + case 0x1c: + switch (rm) { + case 0: /* feni (287 only, just do nop here) */ + break; + case 1: /* fdisi (287 only, just do nop here) */ + break; + case 2: /* fclex */ + gen_helper_fclex(tcg_env); + update_fip = false; + break; + case 3: /* fninit */ + gen_helper_fninit(tcg_env); + update_fip = false; + break; + case 4: /* fsetpm (287 only, just do nop here) */ + break; + default: + return false; + } + break; + case 0x1d: /* fucomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucomi_ST0_FT0(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x1e: /* fcomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcomi_ST0_FT0(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x28: /* ffree sti */ + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); + break; + case 0x2a: /* fst sti */ + gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); + break; + case 0x2b: /* fstp sti */ + case 0x0b: /* fstp1 sti, undocumented op */ + case 0x3a: /* fstp8 sti, undocumented op */ + case 0x3b: /* fstp9 sti, undocumented op */ + gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fpop(tcg_env); + break; + case 0x2c: /* fucom st(i) */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucom_ST0_FT0(tcg_env); + break; + case 0x2d: /* fucomp st(i) */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + break; + case 0x33: /* de/3 */ + switch (rm) { + case 1: /* fcompp */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); + gen_helper_fcom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + gen_helper_fpop(tcg_env); + break; + default: + return false; + } + break; + case 0x38: /* ffreep sti, undocumented op */ + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fpop(tcg_env); + break; + case 0x3c: /* df/4 */ + switch (rm) { + case 0: + gen_helper_fnstsw(s->tmp2_i32, tcg_env); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + break; + default: + return false; + } + break; + case 0x3d: /* fucomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucomi_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x3e: /* fcomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcomi_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x10 ... 0x13: /* fcmovxx */ + case 0x18 ... 0x1b: + { + int op1; + TCGLabel *l1; + static const uint8_t fcmov_cc[8] = { + (JCC_B << 1), + (JCC_Z << 1), + (JCC_BE << 1), + (JCC_P << 1), + }; + + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); + l1 = gen_new_label(); + gen_jcc1_noeob(s, op1, l1); + gen_helper_fmov_ST0_STN(tcg_env, + tcg_constant_i32(opreg)); + gen_set_label(l1); + } + break; + default: + return false; + } + } + + if (update_fip) { + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, segs[R_CS].selector)); + tcg_gen_st16_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, fpcs)); + tcg_gen_st_tl(eip_cur_tl(s), + tcg_env, offsetof(CPUX86State, fpip)); + } + return true; + + illegal_op: + gen_illegal_opcode(s); + return true; +} + /* convert one instruction. s->base.is_jmp is set if the translation must be stopped. Return the next pc value */ static bool disas_insn(DisasContext *s, CPUState *cpu) @@ -2909,560 +3473,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) /************************/ /* floats */ case 0xd8 ... 0xdf: - { - bool update_fip = true; - - if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { - /* if CR0.EM or CR0.TS are set, generate an FPU exception */ - /* XXX: what to do if illegal op ? */ - gen_exception(s, EXCP07_PREX); - break; - } - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = modrm & 7; - op = ((b & 7) << 3) | ((modrm >> 3) & 7); - if (mod != 3) { - /* memory op */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a, false); - TCGv last_addr = tcg_temp_new(); - bool update_fdp = true; - - tcg_gen_mov_tl(last_addr, ea); - gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); - - switch (op) { - case 0x00 ... 0x07: /* fxxxs */ - case 0x10 ... 0x17: /* fixxxl */ - case 0x20 ... 0x27: /* fxxxl */ - case 0x30 ... 0x37: /* fixxx */ - { - int op1; - op1 = op & 7; - - switch (op >> 4) { - case 0: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_flds_FT0(tcg_env, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fldl_FT0(tcg_env, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); - break; - } - - gen_helper_fp_arith_ST0_FT0(op1); - if (op1 == 3) { - /* fcomp needs pop */ - gen_helper_fpop(tcg_env); - } - } - break; - case 0x08: /* flds */ - case 0x0a: /* fsts */ - case 0x0b: /* fstps */ - case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ - case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ - case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ - switch (op & 7) { - case 0: - switch (op >> 4) { - case 0: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_flds_ST0(tcg_env, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fldl_ST0(tcg_env, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); - break; - } - break; - case 1: - /* XXX: the corresponding CPUID bit must be tested ! */ - switch (op >> 4) { - case 1: - gen_helper_fisttl_ST0(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 2: - gen_helper_fisttll_ST0(s->tmp1_i64, tcg_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - break; - case 3: - default: - gen_helper_fistt_ST0(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - } - gen_helper_fpop(tcg_env); - break; - default: - switch (op >> 4) { - case 0: - gen_helper_fsts_ST0(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 1: - gen_helper_fistl_ST0(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 2: - gen_helper_fstl_ST0(s->tmp1_i64, tcg_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - break; - case 3: - default: - gen_helper_fist_ST0(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - } - if ((op & 7) == 3) { - gen_helper_fpop(tcg_env); - } - break; - } - break; - case 0x0c: /* fldenv mem */ - gen_helper_fldenv(tcg_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x0d: /* fldcw mem */ - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - gen_helper_fldcw(tcg_env, s->tmp2_i32); - update_fip = update_fdp = false; - break; - case 0x0e: /* fnstenv mem */ - gen_helper_fstenv(tcg_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x0f: /* fnstcw mem */ - gen_helper_fnstcw(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - update_fip = update_fdp = false; - break; - case 0x1d: /* fldt mem */ - gen_helper_fldt_ST0(tcg_env, s->A0); - break; - case 0x1f: /* fstpt mem */ - gen_helper_fstt_ST0(tcg_env, s->A0); - gen_helper_fpop(tcg_env); - break; - case 0x2c: /* frstor mem */ - gen_helper_frstor(tcg_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x2e: /* fnsave mem */ - gen_helper_fsave(tcg_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x2f: /* fnstsw mem */ - gen_helper_fnstsw(s->tmp2_i32, tcg_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - update_fip = update_fdp = false; - break; - case 0x3c: /* fbld */ - gen_helper_fbld_ST0(tcg_env, s->A0); - break; - case 0x3e: /* fbstp */ - gen_helper_fbst_ST0(tcg_env, s->A0); - gen_helper_fpop(tcg_env); - break; - case 0x3d: /* fildll */ - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fildll_ST0(tcg_env, s->tmp1_i64); - break; - case 0x3f: /* fistpll */ - gen_helper_fistll_ST0(s->tmp1_i64, tcg_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fpop(tcg_env); - break; - default: - goto unknown_op; - } - - if (update_fdp) { - int last_seg = s->override >= 0 ? s->override : a.def_seg; - - tcg_gen_ld_i32(s->tmp2_i32, tcg_env, - offsetof(CPUX86State, - segs[last_seg].selector)); - tcg_gen_st16_i32(s->tmp2_i32, tcg_env, - offsetof(CPUX86State, fpds)); - tcg_gen_st_tl(last_addr, tcg_env, - offsetof(CPUX86State, fpdp)); - } - } else { - /* register float ops */ - opreg = rm; - - switch (op) { - case 0x08: /* fld sti */ - gen_helper_fpush(tcg_env); - gen_helper_fmov_ST0_STN(tcg_env, - tcg_constant_i32((opreg + 1) & 7)); - break; - case 0x09: /* fxchg sti */ - case 0x29: /* fxchg4 sti, undocumented op */ - case 0x39: /* fxchg7 sti, undocumented op */ - gen_helper_fxchg_ST0_STN(tcg_env, tcg_constant_i32(opreg)); - break; - case 0x0a: /* grp d9/2 */ - switch (rm) { - case 0: /* fnop */ - /* - * check exceptions (FreeBSD FPU probe) - * needs to be treated as I/O because of ferr_irq - */ - translator_io_start(&s->base); - gen_helper_fwait(tcg_env); - update_fip = false; - break; - default: - goto unknown_op; - } - break; - case 0x0c: /* grp d9/4 */ - switch (rm) { - case 0: /* fchs */ - gen_helper_fchs_ST0(tcg_env); - break; - case 1: /* fabs */ - gen_helper_fabs_ST0(tcg_env); - break; - case 4: /* ftst */ - gen_helper_fldz_FT0(tcg_env); - gen_helper_fcom_ST0_FT0(tcg_env); - break; - case 5: /* fxam */ - gen_helper_fxam_ST0(tcg_env); - break; - default: - goto unknown_op; - } - break; - case 0x0d: /* grp d9/5 */ - { - switch (rm) { - case 0: - gen_helper_fpush(tcg_env); - gen_helper_fld1_ST0(tcg_env); - break; - case 1: - gen_helper_fpush(tcg_env); - gen_helper_fldl2t_ST0(tcg_env); - break; - case 2: - gen_helper_fpush(tcg_env); - gen_helper_fldl2e_ST0(tcg_env); - break; - case 3: - gen_helper_fpush(tcg_env); - gen_helper_fldpi_ST0(tcg_env); - break; - case 4: - gen_helper_fpush(tcg_env); - gen_helper_fldlg2_ST0(tcg_env); - break; - case 5: - gen_helper_fpush(tcg_env); - gen_helper_fldln2_ST0(tcg_env); - break; - case 6: - gen_helper_fpush(tcg_env); - gen_helper_fldz_ST0(tcg_env); - break; - default: - goto unknown_op; - } - } - break; - case 0x0e: /* grp d9/6 */ - switch (rm) { - case 0: /* f2xm1 */ - gen_helper_f2xm1(tcg_env); - break; - case 1: /* fyl2x */ - gen_helper_fyl2x(tcg_env); - break; - case 2: /* fptan */ - gen_helper_fptan(tcg_env); - break; - case 3: /* fpatan */ - gen_helper_fpatan(tcg_env); - break; - case 4: /* fxtract */ - gen_helper_fxtract(tcg_env); - break; - case 5: /* fprem1 */ - gen_helper_fprem1(tcg_env); - break; - case 6: /* fdecstp */ - gen_helper_fdecstp(tcg_env); - break; - default: - case 7: /* fincstp */ - gen_helper_fincstp(tcg_env); - break; - } - break; - case 0x0f: /* grp d9/7 */ - switch (rm) { - case 0: /* fprem */ - gen_helper_fprem(tcg_env); - break; - case 1: /* fyl2xp1 */ - gen_helper_fyl2xp1(tcg_env); - break; - case 2: /* fsqrt */ - gen_helper_fsqrt(tcg_env); - break; - case 3: /* fsincos */ - gen_helper_fsincos(tcg_env); - break; - case 5: /* fscale */ - gen_helper_fscale(tcg_env); - break; - case 4: /* frndint */ - gen_helper_frndint(tcg_env); - break; - case 6: /* fsin */ - gen_helper_fsin(tcg_env); - break; - default: - case 7: /* fcos */ - gen_helper_fcos(tcg_env); - break; - } - break; - case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ - case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ - case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ - { - int op1; - - op1 = op & 7; - if (op >= 0x20) { - gen_helper_fp_arith_STN_ST0(op1, opreg); - if (op >= 0x30) { - gen_helper_fpop(tcg_env); - } - } else { - gen_helper_fmov_FT0_STN(tcg_env, - tcg_constant_i32(opreg)); - gen_helper_fp_arith_ST0_FT0(op1); - } - } - break; - case 0x02: /* fcom */ - case 0x22: /* fcom2, undocumented op */ - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fcom_ST0_FT0(tcg_env); - break; - case 0x03: /* fcomp */ - case 0x23: /* fcomp3, undocumented op */ - case 0x32: /* fcomp5, undocumented op */ - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fcom_ST0_FT0(tcg_env); - gen_helper_fpop(tcg_env); - break; - case 0x15: /* da/5 */ - switch (rm) { - case 1: /* fucompp */ - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); - gen_helper_fucom_ST0_FT0(tcg_env); - gen_helper_fpop(tcg_env); - gen_helper_fpop(tcg_env); - break; - default: - goto unknown_op; - } - break; - case 0x1c: - switch (rm) { - case 0: /* feni (287 only, just do nop here) */ - break; - case 1: /* fdisi (287 only, just do nop here) */ - break; - case 2: /* fclex */ - gen_helper_fclex(tcg_env); - update_fip = false; - break; - case 3: /* fninit */ - gen_helper_fninit(tcg_env); - update_fip = false; - break; - case 4: /* fsetpm (287 only, just do nop here) */ - break; - default: - goto unknown_op; - } - break; - case 0x1d: /* fucomi */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fucomi_ST0_FT0(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x1e: /* fcomi */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fcomi_ST0_FT0(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x28: /* ffree sti */ - gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); - break; - case 0x2a: /* fst sti */ - gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); - break; - case 0x2b: /* fstp sti */ - case 0x0b: /* fstp1 sti, undocumented op */ - case 0x3a: /* fstp8 sti, undocumented op */ - case 0x3b: /* fstp9 sti, undocumented op */ - gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fpop(tcg_env); - break; - case 0x2c: /* fucom st(i) */ - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fucom_ST0_FT0(tcg_env); - break; - case 0x2d: /* fucomp st(i) */ - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fucom_ST0_FT0(tcg_env); - gen_helper_fpop(tcg_env); - break; - case 0x33: /* de/3 */ - switch (rm) { - case 1: /* fcompp */ - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); - gen_helper_fcom_ST0_FT0(tcg_env); - gen_helper_fpop(tcg_env); - gen_helper_fpop(tcg_env); - break; - default: - goto unknown_op; - } - break; - case 0x38: /* ffreep sti, undocumented op */ - gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fpop(tcg_env); - break; - case 0x3c: /* df/4 */ - switch (rm) { - case 0: - gen_helper_fnstsw(s->tmp2_i32, tcg_env); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - break; - default: - goto unknown_op; - } - break; - case 0x3d: /* fucomip */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fucomi_ST0_FT0(tcg_env); - gen_helper_fpop(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x3e: /* fcomip */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); - gen_helper_fcomi_ST0_FT0(tcg_env); - gen_helper_fpop(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x10 ... 0x13: /* fcmovxx */ - case 0x18 ... 0x1b: - { - int op1; - TCGLabel *l1; - static const uint8_t fcmov_cc[8] = { - (JCC_B << 1), - (JCC_Z << 1), - (JCC_BE << 1), - (JCC_P << 1), - }; - - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); - l1 = gen_new_label(); - gen_jcc1_noeob(s, op1, l1); - gen_helper_fmov_ST0_STN(tcg_env, - tcg_constant_i32(opreg)); - gen_set_label(l1); - } - break; - default: - goto unknown_op; - } - } - - if (update_fip) { - tcg_gen_ld_i32(s->tmp2_i32, tcg_env, - offsetof(CPUX86State, segs[R_CS].selector)); - tcg_gen_st16_i32(s->tmp2_i32, tcg_env, - offsetof(CPUX86State, fpcs)); - tcg_gen_st_tl(eip_cur_tl(s), - tcg_env, offsetof(CPUX86State, fpip)); - } + if (!disas_insn_x87(s, cpu, b)) { + goto unknown_op; } break; From ef309ec2a6fab1265950c831b16c3e6a9e72a9c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Dec 2023 17:30:06 +0100 Subject: [PATCH 0526/2884] target/i386: split legacy decoder into a separate function Split the bits that have some duplication with disas_insn_new, from those that should be the main topic of the conversion. This is the first step towards removing duplicate decoding of prefixes between disas_insn and disas_insn_new. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 58 +++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 973bf07ef2..eb0e37e148 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3117,15 +3117,15 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) return true; } +static void disas_insn_old(DisasContext *s, CPUState *cpu, int b); + /* convert one instruction. s->base.is_jmp is set if the translation must be stopped. Return the next pc value */ static bool disas_insn(DisasContext *s, CPUState *cpu) { CPUX86State *env = cpu_env(cpu); int b, prefixes; - int shift; - MemOp ot, aflag, dflag; - int modrm, reg, rm, mod, op, opreg, val; + MemOp aflag, dflag; bool orig_cc_op_dirty = s->cc_op_dirty; CCOp orig_cc_op = s->cc_op; target_ulong orig_pc_save = s->pc_save; @@ -3271,6 +3271,38 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) s->aflag = aflag; s->dflag = dflag; + switch (b) { + case 0 ... 0xd7: + case 0xe0 ... 0xff: + case 0x10e ... 0x117: + case 0x128 ... 0x12f: + case 0x138 ... 0x19f: + case 0x1a0 ... 0x1a1: + case 0x1a8 ... 0x1a9: + case 0x1af: + case 0x1b2: + case 0x1b4 ... 0x1b7: + case 0x1be ... 0x1bf: + case 0x1c2 ... 0x1c6: + case 0x1c8 ... 0x1ff: + disas_insn_new(s, cpu, b); + break; + default: + disas_insn_old(s, cpu, b); + break; + } + return true; +} + +static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) +{ + CPUX86State *env = cpu_env(cpu); + int prefixes = s->prefix; + MemOp dflag = s->dflag; + int shift; + MemOp ot; + int modrm, reg, rm, mod, op, opreg, val; + /* now check op code */ switch (b) { /**************************/ @@ -4726,31 +4758,15 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) set_cc_op(s, CC_OP_POPCNT); break; - case 0 ... 0xd7: - case 0xe0 ... 0xff: - case 0x10e ... 0x117: - case 0x128 ... 0x12f: - case 0x138 ... 0x19f: - case 0x1a0 ... 0x1a1: - case 0x1a8 ... 0x1a9: - case 0x1af: - case 0x1b2: - case 0x1b4 ... 0x1b7: - case 0x1be ... 0x1bf: - case 0x1c2 ... 0x1c6: - case 0x1c8 ... 0x1ff: - disas_insn_new(s, cpu, b); - break; default: goto unknown_op; } - return true; + return; illegal_op: gen_illegal_opcode(s); - return true; + return; unknown_op: gen_unknown_opcode(env, s); - return true; } void tcg_x86_init(void) From d4e6d40c36071c287199c072cd5d296091ee5968 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Apr 2024 17:31:23 +0200 Subject: [PATCH 0527/2884] target/i386: remove duplicate prefix decoding Now that a bulk of opcodes go through the new decoder, it is sensible to do some cleanup. Go immediately through disas_insn_new and only jump back after parsing the prefixes. disas_insn() now only contains the three sigsetjmp cases, and they are more easily managed if they are inlined into i386_tr_translate_insn. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 63 ++++++-- target/i386/tcg/translate.c | 259 +++++++------------------------ 2 files changed, 103 insertions(+), 219 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index dabc987b99..0e1811399f 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2219,22 +2219,31 @@ illegal: * Convert one instruction. s->base.is_jmp is set if the translation must * be stopped. */ -static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) +static void disas_insn(DisasContext *s, CPUState *cpu) { CPUX86State *env = cpu_env(cpu); - bool first = true; X86DecodedInsn decode; X86DecodeFunc decode_func = decode_root; - uint8_t cc_live; + uint8_t cc_live, b; + s->pc = s->base.pc_next; + s->override = -1; + s->popl_esp_hack = 0; +#ifdef TARGET_X86_64 + s->rex_r = 0; + s->rex_x = 0; + s->rex_b = 0; +#endif + s->rip_offset = 0; /* for relative ip address */ + s->vex_l = 0; + s->vex_v = 0; + s->vex_w = false; s->has_modrm = false; + s->prefix = 0; next_byte: - if (first) { - first = false; - } else { - b = x86_ldub_code(env, s); - } + b = x86_ldub_code(env, s); + /* Collect prefixes. */ switch (b) { case 0xf3: @@ -2346,10 +2355,6 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } break; default: - if (b >= 0x100) { - b -= 0x100; - decode_func = do_decode_0F; - } break; } @@ -2378,6 +2383,40 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } } + /* Go back to old decoder for unconverted opcodes. */ + if (!(s->prefix & PREFIX_VEX)) { + if ((b & ~7) == 0xd8) { + if (!disas_insn_x87(s, cpu, b)) { + goto unknown_op; + } + return; + } + + if (b == 0x0f) { + b = x86_ldub_code(env, s); + switch (b) { + case 0x00 ... 0x03: /* mostly privileged instructions */ + case 0x05 ... 0x09: + case 0x0d: /* 3DNow! prefetch */ + case 0x18 ... 0x23: /* prefetch, MPX, mov from/to CR and DR */ + case 0x30 ... 0x35: /* more privileged instructions */ + case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */ + case 0xaa ... 0xae: /* RSM, SHRD, grp15 */ + case 0xb0 ... 0xb1: /* cmpxchg */ + case 0xb3: /* btr */ + case 0xb8: /* integer ops */ + case 0xba ... 0xbd: /* integer ops */ + case 0xc0 ... 0xc1: /* xadd */ + case 0xc7: /* grp9 */ + disas_insn_old(s, cpu, b + 0x100); + return; + default: + decode_func = do_decode_0F; + break; + } + } + } + memset(&decode, 0, sizeof(decode)); decode.cc_op = -1; decode.b = b; diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index eb0e37e148..3842b29484 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2451,10 +2451,6 @@ static void gen_sty_env_A0(DisasContext *s, int offset, bool align) tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop); } -#include "decode-new.h" -#include "emit.c.inc" -#include "decode-new.c.inc" - static void gen_cmpxchg8b(DisasContext *s, CPUX86State *env, int modrm) { TCGv_i64 cmp, val, old; @@ -3117,183 +3113,6 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) return true; } -static void disas_insn_old(DisasContext *s, CPUState *cpu, int b); - -/* convert one instruction. s->base.is_jmp is set if the translation must - be stopped. Return the next pc value */ -static bool disas_insn(DisasContext *s, CPUState *cpu) -{ - CPUX86State *env = cpu_env(cpu); - int b, prefixes; - MemOp aflag, dflag; - bool orig_cc_op_dirty = s->cc_op_dirty; - CCOp orig_cc_op = s->cc_op; - target_ulong orig_pc_save = s->pc_save; - - s->pc = s->base.pc_next; - s->override = -1; - s->popl_esp_hack = 0; -#ifdef TARGET_X86_64 - s->rex_r = 0; - s->rex_x = 0; - s->rex_b = 0; -#endif - s->rip_offset = 0; /* for relative ip address */ - s->vex_l = 0; - s->vex_v = 0; - s->vex_w = false; - switch (sigsetjmp(s->jmpbuf, 0)) { - case 0: - break; - case 1: - gen_exception_gpf(s); - return true; - case 2: - /* Restore state that may affect the next instruction. */ - s->pc = s->base.pc_next; - /* - * TODO: These save/restore can be removed after the table-based - * decoder is complete; we will be decoding the insn completely - * before any code generation that might affect these variables. - */ - s->cc_op_dirty = orig_cc_op_dirty; - s->cc_op = orig_cc_op; - s->pc_save = orig_pc_save; - /* END TODO */ - s->base.num_insns--; - tcg_remove_ops_after(s->prev_insn_end); - s->base.insn_start = s->prev_insn_start; - s->base.is_jmp = DISAS_TOO_MANY; - return false; - default: - g_assert_not_reached(); - } - - prefixes = 0; - - next_byte: - s->prefix = prefixes; - b = x86_ldub_code(env, s); - /* Collect prefixes. */ - switch (b) { - case 0x0f: - b = x86_ldub_code(env, s) + 0x100; - break; - case 0xf3: - prefixes |= PREFIX_REPZ; - prefixes &= ~PREFIX_REPNZ; - goto next_byte; - case 0xf2: - prefixes |= PREFIX_REPNZ; - prefixes &= ~PREFIX_REPZ; - goto next_byte; - case 0xf0: - prefixes |= PREFIX_LOCK; - goto next_byte; - case 0x2e: - s->override = R_CS; - goto next_byte; - case 0x36: - s->override = R_SS; - goto next_byte; - case 0x3e: - s->override = R_DS; - goto next_byte; - case 0x26: - s->override = R_ES; - goto next_byte; - case 0x64: - s->override = R_FS; - goto next_byte; - case 0x65: - s->override = R_GS; - goto next_byte; - case 0x66: - prefixes |= PREFIX_DATA; - goto next_byte; - case 0x67: - prefixes |= PREFIX_ADR; - goto next_byte; -#ifdef TARGET_X86_64 - case 0x40 ... 0x4f: - if (CODE64(s)) { - /* REX prefix */ - prefixes |= PREFIX_REX; - s->vex_w = (b >> 3) & 1; - s->rex_r = (b & 0x4) << 1; - s->rex_x = (b & 0x2) << 2; - s->rex_b = (b & 0x1) << 3; - goto next_byte; - } - break; -#endif - case 0xc5: /* 2-byte VEX */ - case 0xc4: /* 3-byte VEX */ - if (CODE32(s) && !VM86(s)) { - int vex2 = x86_ldub_code(env, s); - s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ - - if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { - /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, - otherwise the instruction is LES or LDS. */ - break; - } - disas_insn_new(s, cpu, b); - return s->pc; - } - break; - } - - /* Post-process prefixes. */ - if (CODE64(s)) { - /* In 64-bit mode, the default data size is 32-bit. Select 64-bit - data with rex_w, and 16-bit data with 0x66; rex_w takes precedence - over 0x66 if both are present. */ - dflag = (REX_W(s) ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32); - /* In 64-bit mode, 0x67 selects 32-bit addressing. */ - aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64); - } else { - /* In 16/32-bit mode, 0x66 selects the opposite data size. */ - if (CODE32(s) ^ ((prefixes & PREFIX_DATA) != 0)) { - dflag = MO_32; - } else { - dflag = MO_16; - } - /* In 16/32-bit mode, 0x67 selects the opposite addressing. */ - if (CODE32(s) ^ ((prefixes & PREFIX_ADR) != 0)) { - aflag = MO_32; - } else { - aflag = MO_16; - } - } - - s->prefix = prefixes; - s->aflag = aflag; - s->dflag = dflag; - - switch (b) { - case 0 ... 0xd7: - case 0xe0 ... 0xff: - case 0x10e ... 0x117: - case 0x128 ... 0x12f: - case 0x138 ... 0x19f: - case 0x1a0 ... 0x1a1: - case 0x1a8 ... 0x1a9: - case 0x1af: - case 0x1b2: - case 0x1b4 ... 0x1b7: - case 0x1be ... 0x1bf: - case 0x1c2 ... 0x1c6: - case 0x1c8 ... 0x1ff: - disas_insn_new(s, cpu, b); - break; - default: - disas_insn_old(s, cpu, b); - break; - } - return true; -} - static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) { CPUX86State *env = cpu_env(cpu); @@ -3502,14 +3321,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } break; - /************************/ - /* floats */ - case 0xd8 ... 0xdf: - if (!disas_insn_x87(s, cpu, b)) { - goto unknown_op; - } - break; - /************************/ /* bit operations */ case 0x1ba: /* bt/bts/btr/btc Gv, im */ @@ -4759,7 +4570,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) set_cc_op(s, CC_OP_POPCNT); break; default: - goto unknown_op; + g_assert_not_reached(); } return; illegal_op: @@ -4769,6 +4580,10 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_unknown_opcode(env, s); } +#include "decode-new.h" +#include "emit.c.inc" +#include "decode-new.c.inc" + void tcg_x86_init(void) { static const char reg_names[CPU_NB_REGS][4] = { @@ -4890,7 +4705,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cc_op = CC_OP_DYNAMIC; dc->cc_op_dirty = false; - dc->popl_esp_hack = 0; /* select memory access functions */ dc->mem_index = cpu_mmu_index(cpu, false); dc->cpuid_features = env->features[FEAT_1_EDX]; @@ -4942,6 +4756,9 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); + bool orig_cc_op_dirty = dc->cc_op_dirty; + CCOp orig_cc_op = dc->cc_op; + target_ulong orig_pc_save = dc->pc_save; #ifdef TARGET_VSYSCALL_PAGE /* @@ -4954,23 +4771,51 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } #endif - if (disas_insn(dc, cpu)) { - target_ulong pc_next = dc->pc; - dc->base.pc_next = pc_next; + switch (sigsetjmp(dc->jmpbuf, 0)) { + case 0: + disas_insn(dc, cpu); + break; + case 1: + gen_exception_gpf(dc); + break; + case 2: + /* Restore state that may affect the next instruction. */ + dc->pc = dc->base.pc_next; + /* + * TODO: These save/restore can be removed after the table-based + * decoder is complete; we will be decoding the insn completely + * before any code generation that might affect these variables. + */ + dc->cc_op_dirty = orig_cc_op_dirty; + dc->cc_op = orig_cc_op; + dc->pc_save = orig_pc_save; + /* END TODO */ + dc->base.num_insns--; + tcg_remove_ops_after(dc->prev_insn_end); + dc->base.insn_start = dc->prev_insn_start; + dc->base.is_jmp = DISAS_TOO_MANY; + return; + default: + g_assert_not_reached(); + } - if (dc->base.is_jmp == DISAS_NEXT) { - if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) { - /* - * If single step mode, we generate only one instruction and - * generate an exception. - * If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear - * the flag and abort the translation to give the irqs a - * chance to happen. - */ - dc->base.is_jmp = DISAS_EOB_NEXT; - } else if (!is_same_page(&dc->base, pc_next)) { - dc->base.is_jmp = DISAS_TOO_MANY; - } + /* + * Instruction decoding completed (possibly with #GP if the + * 15-byte boundary was exceeded). + */ + dc->base.pc_next = dc->pc; + if (dc->base.is_jmp == DISAS_NEXT) { + if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) { + /* + * If single step mode, we generate only one instruction and + * generate an exception. + * If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear + * the flag and abort the translation to give the irqs a + * chance to happen. + */ + dc->base.is_jmp = DISAS_EOB_NEXT; + } else if (!is_same_page(&dc->base, dc->base.pc_next)) { + dc->base.is_jmp = DISAS_TOO_MANY; } } } From 62663f08a75dbbddb745c80a00b09e298ede641a Mon Sep 17 00:00:00 2001 From: Will Gyda Date: Thu, 25 Apr 2024 17:12:07 +0530 Subject: [PATCH 0528/2884] migration/ram.c: API Conversion qemu_mutex_lock(), and qemu_mutex_unlock() to WITH_QEMU_LOCK_GUARD macro migration/ram.c: API Conversion qemu_mutex_lock(), and qemu_mutex_unlock() to WITH_QEMU_LOCK_GUARD macro Signed-off-by: Will Gyda Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/ram.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index a975c5af16..50df1e9cd2 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1066,14 +1066,14 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) trace_migration_bitmap_sync_start(); memory_global_dirty_log_sync(last_stage); - qemu_mutex_lock(&rs->bitmap_mutex); - WITH_RCU_READ_LOCK_GUARD() { - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - ramblock_sync_dirty_bitmap(rs, block); + WITH_QEMU_LOCK_GUARD(&rs->bitmap_mutex) { + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ramblock_sync_dirty_bitmap(rs, block); + } + stat64_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining()); } - stat64_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining()); } - qemu_mutex_unlock(&rs->bitmap_mutex); memory_global_after_dirty_log_sync(); trace_migration_bitmap_sync_end(rs->num_dirty_pages_period); From d4a17b8f1da567b274f7c7e4846496c564c860c4 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 30 Apr 2024 11:56:42 +0300 Subject: [PATCH 0529/2884] migration: move trace-point from migrate_fd_error to migrate_set_error Cover more cases by trace-point. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 4 +++- migration/trace-events | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index b5af6b5105..2dc6a063e9 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1421,6 +1421,9 @@ static void migrate_fd_cleanup_bh(void *opaque) void migrate_set_error(MigrationState *s, const Error *error) { QEMU_LOCK_GUARD(&s->error_mutex); + + trace_migrate_error(error_get_pretty(error)); + if (!s->error) { s->error = error_copy(error); } @@ -1444,7 +1447,6 @@ static void migrate_error_free(MigrationState *s) static void migrate_fd_error(MigrationState *s, const Error *error) { - trace_migrate_fd_error(error_get_pretty(error)); assert(s->to_dst_file == NULL); migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); diff --git a/migration/trace-events b/migration/trace-events index f0e1cb80c7..d0c44c3853 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -152,7 +152,7 @@ multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostnam # migration.c migrate_set_state(const char *new_state) "new state %s" migrate_fd_cleanup(void) "" -migrate_fd_error(const char *error_desc) "error=%s" +migrate_error(const char *error_desc) "error=%s" migrate_fd_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" From 246f54e0ccc288239fae78ec9e401ba070667b0b Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 30 Apr 2024 11:56:43 +0300 Subject: [PATCH 0530/2884] migration: process_incoming_migration_co(): complete cleanup on failure Make call to migration_incoming_state_destroy(), instead of doing only partial of it. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 2dc6a063e9..0d26db47f7 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -799,10 +799,7 @@ process_incoming_migration_co(void *opaque) fail: migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); - qemu_fclose(mis->from_src_file); - - multifd_recv_cleanup(); - compress_threads_load_cleanup(); + migration_incoming_state_destroy(); exit(EXIT_FAILURE); } From 30116e9079e3f395ef186960d986c7d073d7eb8a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 30 Apr 2024 11:56:44 +0300 Subject: [PATCH 0531/2884] migration: process_incoming_migration_co(): fix reporting s->error It's bad idea to leave critical section with error object freed, but s->error still set, this theoretically may lead to use-after-free crash. Let's avoid it. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration.c b/migration/migration.c index 0d26db47f7..b307a4bc59 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -784,6 +784,7 @@ process_incoming_migration_co(void *opaque) if (migrate_has_error(s)) { WITH_QEMU_LOCK_GUARD(&s->error_mutex) { error_report_err(s->error); + s->error = NULL; } } error_report("load of migration failed: %s", strerror(-ret)); From f84eaa9ffdcf00a81922fc1380870775a60792bf Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 30 Apr 2024 11:56:45 +0300 Subject: [PATCH 0532/2884] migration: process_incoming_migration_co(): rework error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify error reporting in the function. This simplifies the following commit, which will not-exit-on-error behavior variant to the function. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index b307a4bc59..a9599838e6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -735,14 +735,16 @@ static void process_incoming_migration_bh(void *opaque) static void coroutine_fn process_incoming_migration_co(void *opaque) { + MigrationState *s = migrate_get_current(); MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyState ps; int ret; + Error *local_err = NULL; assert(mis->from_src_file); if (compress_threads_load_setup(mis->from_src_file)) { - error_report("Failed to setup decompress threads"); + error_setg(&local_err, "Failed to setup decompress threads"); goto fail; } @@ -779,19 +781,12 @@ process_incoming_migration_co(void *opaque) } if (ret < 0) { - MigrationState *s = migrate_get_current(); - - if (migrate_has_error(s)) { - WITH_QEMU_LOCK_GUARD(&s->error_mutex) { - error_report_err(s->error); - s->error = NULL; - } - } - error_report("load of migration failed: %s", strerror(-ret)); + error_setg(&local_err, "load of migration failed: %s", strerror(-ret)); goto fail; } if (colo_incoming_co() < 0) { + error_setg(&local_err, "colo incoming failed"); goto fail; } @@ -800,8 +795,16 @@ process_incoming_migration_co(void *opaque) fail: migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); + migrate_set_error(s, local_err); + error_free(local_err); + migration_incoming_state_destroy(); + WITH_QEMU_LOCK_GUARD(&s->error_mutex) { + error_report_err(s->error); + s->error = NULL; + } + exit(EXIT_FAILURE); } From dbea1c89dad37a6ab96befd017d33edaa50ded0a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 30 Apr 2024 11:56:46 +0300 Subject: [PATCH 0533/2884] qapi: introduce exit-on-error parameter for migrate-incoming Now we do set MIGRATION_FAILED state, but don't give a chance to orchestrator to query migration state and get the error. Let's provide a possibility for QMP-based orchestrators to get an error like with outgoing migration. For hmp_migrate_incoming(), let's enable the new behavior: HMP is not and ABI, it's mostly intended to use by developer and it makes sense not to stop the process. For x-exit-preconfig, let's keep the old behavior: - it's called from init(), so here we want to keep current behavior by default - it does exit on error by itself as well So, if we want to change the behavior of x-exit-preconfig, it should be another patch. Signed-off-by: Vladimir Sementsov-Ogievskiy Acked-by: Markus Armbruster Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 2 +- migration/migration.c | 33 +++++++++++++++++++++++++++------ migration/migration.h | 3 +++ qapi/migration.json | 7 ++++++- system/vl.c | 3 ++- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 7e96ae6ffd..23181bbee1 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -466,7 +466,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) } QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); - qmp_migrate_incoming(NULL, true, caps, &err); + qmp_migrate_incoming(NULL, true, caps, true, false, &err); qapi_free_MigrationChannelList(caps); end: diff --git a/migration/migration.c b/migration/migration.c index a9599838e6..289afa8d85 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -72,6 +72,8 @@ #define NOTIFIER_ELEM_INIT(array, elem) \ [elem] = NOTIFIER_WITH_RETURN_LIST_INITIALIZER((array)[elem]) +#define INMIGRATE_DEFAULT_EXIT_ON_ERROR true + static NotifierWithReturnList migration_state_notifiers[] = { NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL), NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT), @@ -234,6 +236,8 @@ void migration_object_init(void) qemu_cond_init(¤t_incoming->page_request_cond); current_incoming->page_requested = g_tree_new(page_request_addr_cmp); + current_incoming->exit_on_error = INMIGRATE_DEFAULT_EXIT_ON_ERROR; + migration_object_check(current_migration, &error_fatal); blk_mig_init(); @@ -800,12 +804,14 @@ fail: migration_incoming_state_destroy(); - WITH_QEMU_LOCK_GUARD(&s->error_mutex) { - error_report_err(s->error); - s->error = NULL; - } + if (mis->exit_on_error) { + WITH_QEMU_LOCK_GUARD(&s->error_mutex) { + error_report_err(s->error); + s->error = NULL; + } - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); + } } /** @@ -1314,6 +1320,15 @@ static void fill_destination_migration_info(MigrationInfo *info) break; } info->status = mis->state; + + if (!info->error_desc) { + MigrationState *s = migrate_get_current(); + QEMU_LOCK_GUARD(&s->error_mutex); + + if (s->error) { + info->error_desc = g_strdup(error_get_pretty(s->error)); + } + } } MigrationInfo *qmp_query_migrate(Error **errp) @@ -1797,10 +1812,13 @@ void migrate_del_blocker(Error **reasonp) } void qmp_migrate_incoming(const char *uri, bool has_channels, - MigrationChannelList *channels, Error **errp) + MigrationChannelList *channels, + bool has_exit_on_error, bool exit_on_error, + Error **errp) { Error *local_err = NULL; static bool once = true; + MigrationIncomingState *mis = migration_incoming_get_current(); if (!once) { error_setg(errp, "The incoming migration has already been started"); @@ -1815,6 +1833,9 @@ void qmp_migrate_incoming(const char *uri, bool has_channels, return; } + mis->exit_on_error = + has_exit_on_error ? exit_on_error : INMIGRATE_DEFAULT_EXIT_ON_ERROR; + qemu_start_incoming_migration(uri, has_channels, channels, &local_err); if (local_err) { diff --git a/migration/migration.h b/migration/migration.h index 6c612c0381..f3406c43c8 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -227,6 +227,9 @@ struct MigrationIncomingState { * is needed as this field is updated serially. */ unsigned int switchover_ack_pending_num; + + /* Do exit on incoming migration failure */ + bool exit_on_error; }; MigrationIncomingState *migration_incoming_get_current(void); diff --git a/qapi/migration.json b/qapi/migration.json index 8c65b90328..9feed413b5 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1837,6 +1837,10 @@ # @channels: list of migration stream channels with each stream in the # list connected to a destination interface endpoint. # +# @exit-on-error: Exit on incoming migration failure. Default true. +# When set to false, the failure triggers a MIGRATION event, and +# error details could be retrieved with query-migrate. (since 9.1) +# # Since: 2.3 # # Notes: @@ -1889,7 +1893,8 @@ ## { 'command': 'migrate-incoming', 'data': {'*uri': 'str', - '*channels': [ 'MigrationChannel' ] } } + '*channels': [ 'MigrationChannel' ], + '*exit-on-error': 'bool' } } ## # @xen-save-devices-state: diff --git a/system/vl.c b/system/vl.c index 7756eac81e..79cd498395 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2723,7 +2723,8 @@ void qmp_x_exit_preconfig(Error **errp) if (incoming) { Error *local_err = NULL; if (strcmp(incoming, "defer") != 0) { - qmp_migrate_incoming(incoming, false, NULL, &local_err); + qmp_migrate_incoming(incoming, false, NULL, true, true, + &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); From f7b1cd3c2ebbc102b8edfacd5c5cd10c04dc887d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 30 Apr 2024 11:27:32 -0300 Subject: [PATCH 0534/2884] migration: Remove 'skipped' field from MigrationStats The 'skipped' field of the MigrationStats struct has been deprecated in 8.1. Time to remove it. Deprecation commit 7b24d32634 ("migration: skipped field is really obsolete."). Reviewed-by: Markus Armbruster Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/about/deprecated.rst | 6 ------ docs/about/removed-features.rst | 6 ++++++ migration/migration-hmp-cmds.c | 2 -- migration/migration.c | 2 -- qapi/migration.json | 8 -------- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 03f8b1b655..94d3e53513 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -477,12 +477,6 @@ option). Migration --------- -``skipped`` MigrationStats field (since 8.1) -'''''''''''''''''''''''''''''''''''''''''''' - -``skipped`` field in Migration stats has been deprecated. It hasn't -been used for more than 10 years. - ``inc`` migrate command option (since 8.2) '''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 53ca08aba9..c4cb2692d0 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -614,6 +614,12 @@ was superseded by ``sections``. Member ``section-size`` in the return value of ``query-sgx-capabilities`` was superseded by ``sections``. +``query-migrate`` return value member ``skipped`` (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``skipped`` of the ``MigrationStats`` struct hasn't been used +for more than 10 years. Removed with no replacement. + Human Monitor Protocol (HMP) commands ------------------------------------- diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 23181bbee1..b6b2035f64 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -105,8 +105,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->ram->total >> 10); monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", info->ram->duplicate); - monitor_printf(mon, "skipped: %" PRIu64 " pages\n", - info->ram->skipped); monitor_printf(mon, "normal: %" PRIu64 " pages\n", info->ram->normal); monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", diff --git a/migration/migration.c b/migration/migration.c index 289afa8d85..a4be929e40 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1156,8 +1156,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->transferred = migration_transferred_bytes(); info->ram->total = ram_bytes_total(); info->ram->duplicate = stat64_get(&mig_stats.zero_pages); - /* legacy value. It is not used anymore */ - info->ram->skipped = 0; info->ram->normal = stat64_get(&mig_stats.normal_pages); info->ram->normal_bytes = info->ram->normal * page_size; info->ram->mbps = s->mbps; diff --git a/qapi/migration.json b/qapi/migration.json index 9feed413b5..2dd70f1c0e 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -23,9 +23,6 @@ # # @duplicate: number of duplicate (zero) pages (since 1.2) # -# @skipped: number of skipped zero pages. Always zero, only provided -# for compatibility (since 1.5) -# # @normal: number of normal pages (since 1.2) # # @normal-bytes: number of normal bytes sent (since 1.2) @@ -63,16 +60,11 @@ # between 0 and @dirty-sync-count * @multifd-channels. (since # 7.1) # -# Features: -# -# @deprecated: Member @skipped is always zero since 1.5.3 -# # Since: 0.14 ## { 'struct': 'MigrationStats', 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , 'duplicate': 'int', - 'skipped': { 'type': 'int', 'features': [ 'deprecated' ] }, 'normal': 'int', 'normal-bytes': 'int', 'dirty-pages-rate': 'int', 'mbps': 'number', 'dirty-sync-count': 'int', From 61c4e39f7301f67408024346ad3eb5335aec0311 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 30 Apr 2024 11:27:33 -0300 Subject: [PATCH 0535/2884] migration: Remove 'inc' option from migrate command The block incremental option for block migration has been deprecated in 8.2 in favor of using the block-mirror feature. Remove it now. Deprecation commit 40101f320d ("migration: migrate 'inc' command option is deprecated."). Reviewed-by: Markus Armbruster Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/about/deprecated.rst | 9 ------ docs/about/removed-features.rst | 14 +++++++++ hmp-commands.hx | 13 +++----- migration/block.c | 1 - migration/migration-hmp-cmds.c | 18 ++--------- migration/migration.c | 24 +++++---------- migration/options.c | 30 +----------------- migration/options.h | 5 --- qapi/migration.json | 54 +++++++-------------------------- 9 files changed, 39 insertions(+), 129 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 94d3e53513..1dfbd5fad4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -477,15 +477,6 @@ option). Migration --------- -``inc`` migrate command option (since 8.2) -'''''''''''''''''''''''''''''''''''''''''' - -Use blockdev-mirror with NBD instead. - -As an intermediate step the ``inc`` functionality can be achieved by -setting the ``block-incremental`` migration parameter to ``true``. -But this parameter is also deprecated. - ``blk`` migrate command option (since 8.2) '''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index c4cb2692d0..7da4b3df14 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -620,6 +620,13 @@ was superseded by ``sections``. Member ``skipped`` of the ``MigrationStats`` struct hasn't been used for more than 10 years. Removed with no replacement. +``migrate`` command option ``inc`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + Human Monitor Protocol (HMP) commands ------------------------------------- @@ -680,6 +687,13 @@ This command didn't produce any output already. Removed with no replacement. The ``singlestep`` command has been replaced by the ``one-insn-per-tb`` command, which has the same behaviour but a less misleading name. +``migrate`` command ``-i`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + Host Architectures ------------------ diff --git a/hmp-commands.hx b/hmp-commands.hx index 2e2a3bcf98..7978302949 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -909,26 +909,21 @@ ERST { .name = "migrate", - .args_type = "detach:-d,blk:-b,inc:-i,resume:-r,uri:s", - .params = "[-d] [-b] [-i] [-r] uri", + .args_type = "detach:-d,blk:-b,resume:-r,uri:s", + .params = "[-d] [-b] [-r] uri", .help = "migrate to URI (using -d to not wait for completion)" "\n\t\t\t -b for migration without shared storage with" - " full copy of disk\n\t\t\t -i for migration without " - "shared storage with incremental copy of disk " - "(base image shared between src and destination)" - "\n\t\t\t -r to resume a paused migration", + " full copy of disk\n\t\t\t -r to resume a paused migration", .cmd = hmp_migrate, }, SRST -``migrate [-d] [-b] [-i]`` *uri* +``migrate [-d] [-b]`` *uri* Migrate to *uri* (using -d to not wait for completion). ``-b`` for migration with full copy of disk - ``-i`` - for migration with incremental copy of disk (base image is shared) ERST { diff --git a/migration/block.c b/migration/block.c index bae6e94891..87ec1a7e68 100644 --- a/migration/block.c +++ b/migration/block.c @@ -419,7 +419,6 @@ static int init_blk_migration(QEMUFile *f, Error **errp) bmds->bulk_completed = 0; bmds->total_sectors = sectors; bmds->completed_sectors = 0; - bmds->shared_base = migrate_block_incremental(); assert(i < num_bs); bmds_bs[i].bmds = bmds; diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index b6b2035f64..8446c0721a 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -332,10 +332,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u ms\n", MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY), params->x_checkpoint_delay); - assert(params->has_block_incremental); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), - params->block_incremental ? "on" : "off"); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), params->multifd_channels); @@ -616,10 +612,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_x_checkpoint_delay = true; visit_type_uint32(v, param, &p->x_checkpoint_delay, &err); break; - case MIGRATION_PARAMETER_BLOCK_INCREMENTAL: - p->has_block_incremental = true; - visit_type_bool(v, param, &p->block_incremental, &err); - break; case MIGRATION_PARAMETER_MULTIFD_CHANNELS: p->has_multifd_channels = true; visit_type_uint8(v, param, &p->multifd_channels, &err); @@ -767,18 +759,12 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) { bool detach = qdict_get_try_bool(qdict, "detach", false); bool blk = qdict_get_try_bool(qdict, "blk", false); - bool inc = qdict_get_try_bool(qdict, "inc", false); bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; g_autoptr(MigrationChannelList) caps = NULL; g_autoptr(MigrationChannel) channel = NULL; - if (inc) { - warn_report("option '-i' is deprecated;" - " use blockdev-mirror with NBD instead"); - } - if (blk) { warn_report("option '-b' is deprecated;" " use blockdev-mirror with NBD instead"); @@ -790,8 +776,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) } QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); - qmp_migrate(NULL, true, caps, !!blk, blk, !!inc, inc, - false, false, true, resume, &err); + qmp_migrate(NULL, true, caps, !!blk, blk, false, false, + true, resume, &err); if (hmp_handle_error(mon, err)) { return; } diff --git a/migration/migration.c b/migration/migration.c index a4be929e40..11a13fa20c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1935,14 +1935,9 @@ bool migration_is_blocked(Error **errp) } /* Returns true if continue to migrate, or false if error detected */ -static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, - bool resume, Error **errp) +static bool migrate_prepare(MigrationState *s, bool blk, bool resume, + Error **errp) { - if (blk_inc) { - warn_report("parameter 'inc' is deprecated;" - " use blockdev-mirror with NBD instead"); - } - if (blk) { warn_report("parameter 'blk' is deprecated;" " use blockdev-mirror with NBD instead"); @@ -2032,12 +2027,12 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, } } - if (blk || blk_inc) { + if (blk) { if (migrate_colo()) { error_setg(errp, "No disk migration is required in COLO mode"); return false; } - if (migrate_block() || migrate_block_incremental()) { + if (migrate_block()) { error_setg(errp, "Command options are incompatible with " "current migration capabilities"); return false; @@ -2048,10 +2043,6 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, s->must_remove_block_options = true; } - if (blk_inc) { - migrate_set_block_incremental(true); - } - if (migrate_init(s, errp)) { return false; } @@ -2061,8 +2052,8 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, void qmp_migrate(const char *uri, bool has_channels, MigrationChannelList *channels, bool has_blk, bool blk, - bool has_inc, bool inc, bool has_detach, bool detach, - bool has_resume, bool resume, Error **errp) + bool has_detach, bool detach, bool has_resume, bool resume, + Error **errp) { bool resume_requested; Error *local_err = NULL; @@ -2101,8 +2092,7 @@ void qmp_migrate(const char *uri, bool has_channels, } resume_requested = has_resume && resume; - if (!migrate_prepare(s, has_blk && blk, has_inc && inc, - resume_requested, errp)) { + if (!migrate_prepare(s, has_blk && blk, resume_requested, errp)) { /* Error detected, put into errp */ return; } diff --git a/migration/options.c b/migration/options.c index 239f5ecfb4..5b16d99bd9 100644 --- a/migration/options.c +++ b/migration/options.c @@ -486,7 +486,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) #ifndef CONFIG_LIVE_BLOCK_MIGRATION if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { - error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " + error_setg(errp, "QEMU compiled without old-style (blk/-b) " "block migration"); error_append_hint(errp, "Use blockdev-mirror with NBD instead.\n"); return false; @@ -763,13 +763,6 @@ bool migrate_has_block_bitmap_mapping(void) return s->parameters.has_block_bitmap_mapping; } -bool migrate_block_incremental(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.block_incremental; -} - uint32_t migrate_checkpoint_delay(void) { MigrationState *s = migrate_get_current(); @@ -948,15 +941,6 @@ ZeroPageDetection migrate_zero_page_detection(void) return s->parameters.zero_page_detection; } -/* parameter setters */ - -void migrate_set_block_incremental(bool value) -{ - MigrationState *s = migrate_get_current(); - - s->parameters.block_incremental = value; -} - /* parameters helpers */ void block_cleanup_parameters(void) @@ -966,7 +950,6 @@ void block_cleanup_parameters(void) if (s->must_remove_block_options) { /* setting to false can never fail */ migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, false, &error_abort); - migrate_set_block_incremental(false); s->must_remove_block_options = false; } } @@ -1020,8 +1003,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->downtime_limit = s->parameters.downtime_limit; params->has_x_checkpoint_delay = true; params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; - params->has_block_incremental = true; - params->block_incremental = s->parameters.block_incremental; params->has_multifd_channels = true; params->multifd_channels = s->parameters.multifd_channels; params->has_multifd_compression = true; @@ -1081,7 +1062,6 @@ void migrate_params_init(MigrationParameters *params) params->has_max_bandwidth = true; params->has_downtime_limit = true; params->has_x_checkpoint_delay = true; - params->has_block_incremental = true; params->has_multifd_channels = true; params->has_multifd_compression = true; params->has_multifd_zlib_level = true; @@ -1359,9 +1339,6 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->x_checkpoint_delay = params->x_checkpoint_delay; } - if (params->has_block_incremental) { - dest->block_incremental = params->block_incremental; - } if (params->has_multifd_channels) { dest->multifd_channels = params->multifd_channels; } @@ -1502,11 +1479,6 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) colo_checkpoint_delay_set(); } - if (params->has_block_incremental) { - warn_report("block migration is deprecated;" - " use blockdev-mirror with NBD instead"); - s->parameters.block_incremental = params->block_incremental; - } if (params->has_multifd_channels) { s->parameters.multifd_channels = params->multifd_channels; } diff --git a/migration/options.h b/migration/options.h index ab8199e207..fa5eb177df 100644 --- a/migration/options.h +++ b/migration/options.h @@ -67,7 +67,6 @@ bool migrate_cap_set(int cap, bool value, Error **errp); const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void); bool migrate_has_block_bitmap_mapping(void); -bool migrate_block_incremental(void); uint32_t migrate_checkpoint_delay(void); int migrate_compress_level(void); int migrate_compress_threads(void); @@ -92,10 +91,6 @@ const char *migrate_tls_hostname(void); uint64_t migrate_xbzrle_cache_size(void); ZeroPageDetection migrate_zero_page_detection(void); -/* parameters setters */ - -void migrate_set_block_incremental(bool value); - /* parameters helpers */ bool migrate_params_check(MigrationParameters *params, Error **errp); diff --git a/qapi/migration.json b/qapi/migration.json index 2dd70f1c0e..b7d3ad015a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -839,13 +839,6 @@ # @x-checkpoint-delay: The delay time (in ms) between two COLO # checkpoints in periodic mode. (Since 2.8) # -# @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at the -# destination; when true, only the active qcow2 layer is migrated -# and the destination must already have access to the same backing -# chain as was used on the source. (since 2.10) -# # @multifd-channels: Number of channels used to migrate data in # parallel. This is the same number that the number of sockets # used for migration. The default value is 2 (since 4.0) @@ -908,10 +901,9 @@ # # Features: # -# @deprecated: Member @block-incremental is deprecated. Use -# blockdev-mirror with NBD instead. Members @compress-level, -# @compress-threads, @decompress-threads and @compress-wait-thread -# are deprecated because @compression is deprecated. +# @deprecated: Members @compress-level, @compress-threads, +# @decompress-threads and @compress-wait-thread are deprecated +# because @compression is deprecated. # # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. @@ -931,7 +923,6 @@ 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', 'avail-switchover-bandwidth', 'downtime-limit', { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] }, - { 'name': 'block-incremental', 'features': [ 'deprecated' ] }, 'multifd-channels', 'xbzrle-cache-size', 'max-postcopy-bandwidth', 'max-cpu-throttle', 'multifd-compression', @@ -1047,13 +1038,6 @@ # @x-checkpoint-delay: The delay time (in ms) between two COLO # checkpoints in periodic mode. (Since 2.8) # -# @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at the -# destination; when true, only the active qcow2 layer is migrated -# and the destination must already have access to the same backing -# chain as was used on the source. (since 2.10) -# # @multifd-channels: Number of channels used to migrate data in # parallel. This is the same number that the number of sockets # used for migration. The default value is 2 (since 4.0) @@ -1116,10 +1100,9 @@ # # Features: # -# @deprecated: Member @block-incremental is deprecated. Use -# blockdev-mirror with NBD instead. Members @compress-level, -# @compress-threads, @decompress-threads and @compress-wait-thread -# are deprecated because @compression is deprecated. +# @deprecated: Members @compress-level, @compress-threads, +# @decompress-threads and @compress-wait-thread are deprecated +# because @compression is deprecated. # # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. @@ -1154,8 +1137,6 @@ '*downtime-limit': 'uint64', '*x-checkpoint-delay': { 'type': 'uint32', 'features': [ 'unstable' ] }, - '*block-incremental': { 'type': 'bool', - 'features': [ 'deprecated' ] }, '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', @@ -1279,13 +1260,6 @@ # @x-checkpoint-delay: the delay time between two COLO checkpoints. # (Since 2.8) # -# @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at the -# destination; when true, only the active qcow2 layer is migrated -# and the destination must already have access to the same backing -# chain as was used on the source. (since 2.10) -# # @multifd-channels: Number of channels used to migrate data in # parallel. This is the same number that the number of sockets # used for migration. The default value is 2 (since 4.0) @@ -1348,10 +1322,9 @@ # # Features: # -# @deprecated: Member @block-incremental is deprecated. Use -# blockdev-mirror with NBD instead. Members @compress-level, -# @compress-threads, @decompress-threads and @compress-wait-thread -# are deprecated because @compression is deprecated. +# @deprecated: Members @compress-level, @compress-threads, +# @decompress-threads and @compress-wait-thread are deprecated +# because @compression is deprecated. # # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. @@ -1383,8 +1356,6 @@ '*downtime-limit': 'uint64', '*x-checkpoint-delay': { 'type': 'uint32', 'features': [ 'unstable' ] }, - '*block-incremental': { 'type': 'bool', - 'features': [ 'deprecated' ] }, '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', @@ -1736,8 +1707,6 @@ # # @blk: do block migration (full disk copy) # -# @inc: incremental disk copy migration -# # @detach: this argument exists only for compatibility reasons and is # ignored by QEMU # @@ -1745,8 +1714,8 @@ # # Features: # -# @deprecated: Members @inc and @blk are deprecated. Use -# blockdev-mirror with NBD instead. +# @deprecated: Member @blk is deprecated. Use blockdev-mirror with +# NBD instead. # # Since: 0.14 # @@ -1814,7 +1783,6 @@ 'data': {'*uri': 'str', '*channels': [ 'MigrationChannel' ], '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, - '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } ## From 18d154f57583dd06f0ce3e69e4952044490b2bc4 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 30 Apr 2024 11:27:34 -0300 Subject: [PATCH 0536/2884] migration: Remove 'blk/-b' option from migrate commands The block migration is considered obsolete and has been deprecated in 8.2. Remove the migrate command option that enables it. This only affects the QMP and HMP commands, the feature can still be accessed by setting the migration 'block' capability. The whole feature will be removed in a future patch. Deprecation commit 8846b5bfca ("migration: migrate 'blk' command option is deprecated."). Reviewed-by: Markus Armbruster Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- .gitlab-ci.d/buildtest.yml | 2 +- docs/about/deprecated.rst | 9 -- docs/about/removed-features.rst | 14 +++ hmp-commands.hx | 12 +-- migration/migration-hmp-cmds.c | 9 +- migration/migration.c | 31 +------ migration/migration.h | 4 - migration/options.c | 14 +-- migration/options.h | 2 - qapi/migration.json | 8 -- tests/qemu-iotests/183 | 147 ------------------------------- tests/qemu-iotests/183.out | 66 -------------- tests/qemu-iotests/common.filter | 7 -- 13 files changed, 25 insertions(+), 300 deletions(-) delete mode 100755 tests/qemu-iotests/183 delete mode 100644 tests/qemu-iotests/183.out diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e9402a68a7..1bf76713d7 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -342,7 +342,7 @@ build-tcg-disabled: - cd tests/qemu-iotests/ - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163 - 170 171 183 184 192 194 208 221 226 227 236 253 277 image-fleecing + 170 171 184 192 194 208 221 226 227 236 253 277 image-fleecing - ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122 124 132 139 142 144 145 151 152 155 157 165 194 196 200 202 208 209 216 218 227 234 246 247 248 250 254 255 257 258 diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 1dfbd5fad4..08d8ef37a7 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -477,15 +477,6 @@ option). Migration --------- -``blk`` migrate command option (since 8.2) -'''''''''''''''''''''''''''''''''''''''''' - -Use blockdev-mirror with NBD instead. - -As an intermediate step the ``blk`` functionality can be achieved by -setting the ``block`` migration capability to ``true``. But this -capability is also deprecated. - block migration (since 8.2) ''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 7da4b3df14..a491c66660 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -627,6 +627,13 @@ Use blockdev-mirror with NBD instead. See "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst for a detailed explanation. +``migrate`` command option ``blk`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + Human Monitor Protocol (HMP) commands ------------------------------------- @@ -694,6 +701,13 @@ Use blockdev-mirror with NBD instead. See "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst for a detailed explanation. +``migrate`` command ``-b`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + Host Architectures ------------------ diff --git a/hmp-commands.hx b/hmp-commands.hx index 7978302949..ebca2cdced 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -909,21 +909,17 @@ ERST { .name = "migrate", - .args_type = "detach:-d,blk:-b,resume:-r,uri:s", - .params = "[-d] [-b] [-r] uri", + .args_type = "detach:-d,resume:-r,uri:s", + .params = "[-d] [-r] uri", .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -b for migration without shared storage with" - " full copy of disk\n\t\t\t -r to resume a paused migration", + "\n\t\t\t -r to resume a paused migration", .cmd = hmp_migrate, }, SRST -``migrate [-d] [-b]`` *uri* +``migrate [-d]`` *uri* Migrate to *uri* (using -d to not wait for completion). - - ``-b`` - for migration with full copy of disk ERST { diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 8446c0721a..734c1d29ce 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -758,26 +758,19 @@ static void hmp_migrate_status_cb(void *opaque) void hmp_migrate(Monitor *mon, const QDict *qdict) { bool detach = qdict_get_try_bool(qdict, "detach", false); - bool blk = qdict_get_try_bool(qdict, "blk", false); bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; g_autoptr(MigrationChannelList) caps = NULL; g_autoptr(MigrationChannel) channel = NULL; - if (blk) { - warn_report("option '-b' is deprecated;" - " use blockdev-mirror with NBD instead"); - } - if (!migrate_uri_parse(uri, &channel, &err)) { hmp_handle_error(mon, err); return; } QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); - qmp_migrate(NULL, true, caps, !!blk, blk, false, false, - true, resume, &err); + qmp_migrate(NULL, true, caps, false, false, true, resume, &err); if (hmp_handle_error(mon, err)) { return; } diff --git a/migration/migration.c b/migration/migration.c index 11a13fa20c..91327b98c5 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1935,14 +1935,8 @@ bool migration_is_blocked(Error **errp) } /* Returns true if continue to migrate, or false if error detected */ -static bool migrate_prepare(MigrationState *s, bool blk, bool resume, - Error **errp) +static bool migrate_prepare(MigrationState *s, bool resume, Error **errp) { - if (blk) { - warn_report("parameter 'blk' is deprecated;" - " use blockdev-mirror with NBD instead"); - } - if (resume) { if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { error_setg(errp, "Cannot resume if there is no " @@ -2027,22 +2021,6 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool resume, } } - if (blk) { - if (migrate_colo()) { - error_setg(errp, "No disk migration is required in COLO mode"); - return false; - } - if (migrate_block()) { - error_setg(errp, "Command options are incompatible with " - "current migration capabilities"); - return false; - } - if (!migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, true, errp)) { - return false; - } - s->must_remove_block_options = true; - } - if (migrate_init(s, errp)) { return false; } @@ -2051,9 +2029,8 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool resume, } void qmp_migrate(const char *uri, bool has_channels, - MigrationChannelList *channels, bool has_blk, bool blk, - bool has_detach, bool detach, bool has_resume, bool resume, - Error **errp) + MigrationChannelList *channels, bool has_detach, bool detach, + bool has_resume, bool resume, Error **errp) { bool resume_requested; Error *local_err = NULL; @@ -2092,7 +2069,7 @@ void qmp_migrate(const char *uri, bool has_channels, } resume_requested = has_resume && resume; - if (!migrate_prepare(s, has_blk && blk, resume_requested, errp)) { + if (!migrate_prepare(s, resume_requested, errp)) { /* Error detected, put into errp */ return; } diff --git a/migration/migration.h b/migration/migration.h index f3406c43c8..a02c2ec782 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -379,10 +379,6 @@ struct MigrationState { /* mutex to protect errp */ QemuMutex error_mutex; - /* Do we have to clean up -b/-i from old migrate parameters */ - /* This feature is deprecated and will be removed */ - bool must_remove_block_options; - /* * Global switch on whether we need to store the global state * during migration. diff --git a/migration/options.c b/migration/options.c index 5b16d99bd9..856b2fa33c 100644 --- a/migration/options.c +++ b/migration/options.c @@ -486,8 +486,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) #ifndef CONFIG_LIVE_BLOCK_MIGRATION if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { - error_setg(errp, "QEMU compiled without old-style (blk/-b) " - "block migration"); + error_setg(errp, "QEMU compiled without old-style block migration"); error_append_hint(errp, "Use blockdev-mirror with NBD instead.\n"); return false; } @@ -943,17 +942,6 @@ ZeroPageDetection migrate_zero_page_detection(void) /* parameters helpers */ -void block_cleanup_parameters(void) -{ - MigrationState *s = migrate_get_current(); - - if (s->must_remove_block_options) { - /* setting to false can never fail */ - migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, false, &error_abort); - s->must_remove_block_options = false; - } -} - AnnounceParameters *migrate_announce_params(void) { static AnnounceParameters ap; diff --git a/migration/options.h b/migration/options.h index fa5eb177df..e9cb60444f 100644 --- a/migration/options.h +++ b/migration/options.h @@ -95,6 +95,4 @@ ZeroPageDetection migrate_zero_page_detection(void); bool migrate_params_check(MigrationParameters *params, Error **errp); void migrate_params_init(MigrationParameters *params); -void block_cleanup_parameters(void); - #endif diff --git a/qapi/migration.json b/qapi/migration.json index b7d3ad015a..381b52f680 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1705,18 +1705,11 @@ # @channels: list of migration stream channels with each stream in the # list connected to a destination interface endpoint. # -# @blk: do block migration (full disk copy) -# # @detach: this argument exists only for compatibility reasons and is # ignored by QEMU # # @resume: resume one paused migration, default "off". (since 3.0) # -# Features: -# -# @deprecated: Member @blk is deprecated. Use blockdev-mirror with -# NBD instead. -# # Since: 0.14 # # Notes: @@ -1782,7 +1775,6 @@ { 'command': 'migrate', 'data': {'*uri': 'str', '*channels': [ 'MigrationChannel' ], - '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } ## diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 deleted file mode 100755 index b85770458e..0000000000 --- a/tests/qemu-iotests/183 +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env bash -# group: rw migration quick -# -# Test old-style block migration (migrate -b) -# -# Copyright (C) 2017 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -status=1 # failure is the default! - -MIG_SOCKET="${SOCK_DIR}/migrate" - -_cleanup() -{ - rm -f "${MIG_SOCKET}" - _rm_test_img "${TEST_IMG}.dest" - _cleanup_test_img - _cleanup_qemu -} -trap "_cleanup; exit \$status" 0 1 2 3 15 - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter -. ./common.qemu - -_supported_os Linux FreeBSD NetBSD -_supported_fmt qcow2 raw qed quorum -_supported_proto file fuse - -size=64M -_make_test_img $size -TEST_IMG="${TEST_IMG}.dest" _make_test_img $size - -echo -echo === Starting VMs === -echo - -qemu_comm_method="qmp" - -_launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk -src=$QEMU_HANDLE -_send_qemu_cmd $src "{ 'execute': 'qmp_capabilities' }" 'return' - -_launch_qemu \ - -drive file="${TEST_IMG}.dest",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk \ - -incoming "unix:${MIG_SOCKET}" -dest=$QEMU_HANDLE -_send_qemu_cmd $dest "{ 'execute': 'qmp_capabilities' }" 'return' - -echo -echo === Write something on the source === -echo - -_send_qemu_cmd $src \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"write -P 0x55 0 64k\"' } }" \ - 'return' -_send_qemu_cmd $src \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"read -P 0x55 0 64k\"' } }" \ - 'return' - -echo -echo === Do block migration to destination === -echo - -reply="$(_send_qemu_cmd $src \ - "{ 'execute': 'migrate', - 'arguments': { 'uri': 'unix:${MIG_SOCKET}', 'blk': true } }" \ - 'return\|error' | _filter_migration_block_deprecated)" -echo "$reply" -if echo "$reply" | grep "compiled without old-style" > /dev/null; then - _notrun "migrate -b support not compiled in" -fi - -timeout_comm=$QEMU_COMM_TIMEOUT -if [ "${VALGRIND_QEMU}" == "y" ]; then - QEMU_COMM_TIMEOUT=4 -else - QEMU_COMM_TIMEOUT=0.1 -fi -qemu_cmd_repeat=50 silent=yes \ - _send_qemu_cmd $src "{ 'execute': 'query-migrate' }" '"status": "completed"' -QEMU_COMM_TIMEOUT=$timeout_comm -_send_qemu_cmd $src "{ 'execute': 'query-status' }" "return" - -echo -echo === Do some I/O on the destination === -echo - -# It is important that we use the BlockBackend of the guest device here instead -# of the node name, which would create a new BlockBackend and not test whether -# the guest has the necessary permissions to access the image now -silent=yes _send_qemu_cmd $dest "" "100 %" -_send_qemu_cmd $dest "{ 'execute': 'query-status' }" "return" -_send_qemu_cmd $dest \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"read -P 0x55 0 64k\"' } }" \ - 'return' -_send_qemu_cmd $dest \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"write -P 0x66 1M 64k\"' } }" \ - 'return' - -echo -echo === Shut down and check image === -echo - -_send_qemu_cmd $src '{"execute":"quit"}' 'return' -_send_qemu_cmd $dest '{"execute":"quit"}' 'return' -wait=1 _cleanup_qemu - -_check_test_img -TEST_IMG="${TEST_IMG}.dest" _check_test_img - -$QEMU_IO -c 'write -P 0x66 1M 64k' "$TEST_IMG" | _filter_qemu_io -$QEMU_IMG compare "$TEST_IMG.dest" "$TEST_IMG" - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out deleted file mode 100644 index 8aef74a25d..0000000000 --- a/tests/qemu-iotests/183.out +++ /dev/null @@ -1,66 +0,0 @@ -QA output created by 183 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.dest', fmt=IMGFMT size=67108864 - -=== Starting VMs === - -{ 'execute': 'qmp_capabilities' } -{"return": {}} -{ 'execute': 'qmp_capabilities' } -{"return": {}} - -=== Write something on the source === - -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "write -P 0x55 0 64k"' } } -wrote 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "read -P 0x55 0 64k"' } } -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} - -=== Do block migration to destination === - -{ 'execute': 'migrate', - 'arguments': { 'uri': 'unix:SOCK_DIR/migrate', 'blk': true } } -{"return": {}} -{ 'execute': 'query-status' } -{"return": {"status": "postmigrate", "running": false}} - -=== Do some I/O on the destination === - -{ 'execute': 'query-status' } -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} -{"return": {"status": "running", "running": true}} -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "read -P 0x55 0 64k"' } } -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "write -P 0x66 1M 64k"' } } -wrote 65536/65536 bytes at offset 1048576 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} - -=== Shut down and check image === - -{"execute":"quit"} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -{"return": {}} -{"execute":"quit"} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -{"return": {}} -No errors were found on the image. -No errors were found on the image. -wrote 65536/65536 bytes at offset 1048576 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Images are identical. -*** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 2846c83808..fc3c64bcb8 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -359,12 +359,5 @@ _filter_qcow2_compression_type_bit() -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/' } -# filter warnings caused for block migration deprecation -_filter_migration_block_deprecated() -{ - gsed -e '/warning: parameter .blk. is deprecated; use blockdev-mirror with NBD instead/d' \ - -e '/warning: block migration is deprecated; use blockdev-mirror with NBD instead/d' -} - # make sure this script returns success true From eef0bae3a75fa33921ac859f70fd154310915ad4 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 30 Apr 2024 11:27:35 -0300 Subject: [PATCH 0537/2884] migration: Remove block migration The block migration has been considered obsolete since QEMU 8.2 in favor of the more flexible storage migration provided by the blockdev-mirror driver. Two releases have passed so now it's time to remove it. Deprecation commit 66db46ca83 ("migration: Deprecate block migration"). Reviewed-by: Markus Armbruster Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- MAINTAINERS | 1 - docs/about/deprecated.rst | 10 - docs/about/removed-features.rst | 14 + docs/devel/migration/main.rst | 2 +- include/migration/misc.h | 6 - meson.build | 2 - meson_options.txt | 2 - migration/block.c | 1018 ------------------------------- migration/block.h | 52 -- migration/colo.c | 1 - migration/meson.build | 3 - migration/migration-hmp-cmds.c | 25 - migration/migration.c | 15 - migration/options.c | 25 - migration/options.h | 1 - migration/ram.c | 15 - migration/savevm.c | 5 - qapi/migration.json | 61 +- scripts/meson-buildoptions.sh | 4 - 19 files changed, 26 insertions(+), 1236 deletions(-) delete mode 100644 migration/block.c delete mode 100644 migration/block.h diff --git a/MAINTAINERS b/MAINTAINERS index 84391777db..e4990ea42e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2866,7 +2866,6 @@ F: util/aio-*.h F: util/defer-call.c F: util/fdmon-*.c F: block/io.c -F: migration/block* F: include/block/aio.h F: include/block/aio-wait.h F: include/qemu/defer-call.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 08d8ef37a7..3d324930f3 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -477,16 +477,6 @@ option). Migration --------- -block migration (since 8.2) -''''''''''''''''''''''''''' - -Block migration is too inflexible. It needs to migrate all block -devices or none. - -Please see "QMP invocation for live storage migration with -``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst -for a detailed explanation. - old compression method (since 8.2) '''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index a491c66660..9e7d8ee4ff 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -634,6 +634,13 @@ Use blockdev-mirror with NBD instead. See "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst for a detailed explanation. +``migrate-set-capabilities`` ``block`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Block migration has been removed. For a replacement, see "QMP +invocation for live storage migration with ``blockdev-mirror`` + NBD" +in docs/interop/live-block-operations.rst. + Human Monitor Protocol (HMP) commands ------------------------------------- @@ -708,6 +715,13 @@ Use blockdev-mirror with NBD instead. See "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst for a detailed explanation. +``migrate_set_capability`` ``block`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Block migration has been removed. For a replacement, see "QMP +invocation for live storage migration with ``blockdev-mirror`` + NBD" +in docs/interop/live-block-operations.rst. + Host Architectures ------------------ diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index 54385a23e5..495cdcb112 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -454,7 +454,7 @@ Examples of such API functions are: Iterative device migration -------------------------- -Some devices, such as RAM, Block storage or certain platform devices, +Some devices, such as RAM or certain platform devices, have large amounts of data that would mean that the CPUs would be paused for too long if they were sent in one section. For these devices an *iterative* approach is taken. diff --git a/include/migration/misc.h b/include/migration/misc.h index c9e200f4eb..bf7339cc1e 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -45,12 +45,6 @@ bool migrate_ram_is_ignored(RAMBlock *block); /* migration/block.c */ -#ifdef CONFIG_LIVE_BLOCK_MIGRATION -void blk_mig_init(void); -#else -static inline void blk_mig_init(void) {} -#endif - AnnounceParameters *migrate_announce_params(void); /* migration/savevm.c */ diff --git a/meson.build b/meson.build index 43da492372..83ae4347c7 100644 --- a/meson.build +++ b/meson.build @@ -2351,7 +2351,6 @@ config_host_data.set('CONFIG_DEBUG_MUTEX', get_option('debug_mutex')) config_host_data.set('CONFIG_DEBUG_STACK_USAGE', get_option('debug_stack_usage')) config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg')) config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap')) -config_host_data.set('CONFIG_LIVE_BLOCK_MIGRATION', get_option('live_block_migration').allowed()) config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug')) config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed()) @@ -4302,7 +4301,6 @@ if have_block summary_info += {'Use block whitelist in tools': get_option('block_drv_whitelist_in_tools')} summary_info += {'VirtFS (9P) support': have_virtfs} summary_info += {'VirtFS (9P) Proxy Helper support (deprecated)': have_virtfs_proxy_helper} - summary_info += {'Live block migration': config_host_data.get('CONFIG_LIVE_BLOCK_MIGRATION')} summary_info += {'replication support': config_host_data.get('CONFIG_REPLICATION')} summary_info += {'bochs support': get_option('bochs').allowed()} summary_info += {'cloop support': get_option('cloop').allowed()} diff --git a/meson_options.txt b/meson_options.txt index adc77bae0c..4c1583eb40 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -314,8 +314,6 @@ option('fdt', type: 'combo', value: 'auto', option('selinux', type: 'feature', value: 'auto', description: 'SELinux support in qemu-nbd') -option('live_block_migration', type: 'feature', value: 'auto', - description: 'block migration in the main migration stream') option('replication', type: 'feature', value: 'auto', description: 'replication support') option('colo_proxy', type: 'feature', value: 'auto', diff --git a/migration/block.c b/migration/block.c deleted file mode 100644 index 87ec1a7e68..0000000000 --- a/migration/block.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* - * QEMU live block migration - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Liran Schour - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "qemu/cutils.h" -#include "qemu/queue.h" -#include "block.h" -#include "block/dirty-bitmap.h" -#include "migration/misc.h" -#include "migration.h" -#include "migration-stats.h" -#include "migration/register.h" -#include "qemu-file.h" -#include "migration/vmstate.h" -#include "sysemu/block-backend.h" -#include "trace.h" -#include "options.h" - -#define BLK_MIG_BLOCK_SIZE (1ULL << 20) -#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLK_MIG_BLOCK_SIZE >> BDRV_SECTOR_BITS) - -#define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 -#define BLK_MIG_FLAG_EOS 0x02 -#define BLK_MIG_FLAG_PROGRESS 0x04 -#define BLK_MIG_FLAG_ZERO_BLOCK 0x08 - -#define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE) - -#define MAX_IO_BUFFERS 512 -#define MAX_PARALLEL_IO 16 - -typedef struct BlkMigDevState { - /* Written during setup phase. Can be read without a lock. */ - BlockBackend *blk; - char *blk_name; - int shared_base; - int64_t total_sectors; - QSIMPLEQ_ENTRY(BlkMigDevState) entry; - Error *blocker; - - /* Only used by migration thread. Does not need a lock. */ - int bulk_completed; - int64_t cur_sector; - int64_t cur_dirty; - - /* Data in the aio_bitmap is protected by block migration lock. - * Allocation and free happen during setup and cleanup respectively. - */ - unsigned long *aio_bitmap; - - /* Protected by block migration lock. */ - int64_t completed_sectors; - - /* During migration this is protected by bdrv_dirty_bitmap_lock(). - * Allocation and free happen during setup and cleanup respectively. - */ - BdrvDirtyBitmap *dirty_bitmap; -} BlkMigDevState; - -typedef struct BlkMigBlock { - /* Only used by migration thread. */ - uint8_t *buf; - BlkMigDevState *bmds; - int64_t sector; - int nr_sectors; - QEMUIOVector qiov; - BlockAIOCB *aiocb; - - /* Protected by block migration lock. */ - int ret; - QSIMPLEQ_ENTRY(BlkMigBlock) entry; -} BlkMigBlock; - -typedef struct BlkMigState { - QSIMPLEQ_HEAD(, BlkMigDevState) bmds_list; - int64_t total_sector_sum; - bool zero_blocks; - - /* Protected by lock. */ - QSIMPLEQ_HEAD(, BlkMigBlock) blk_list; - int submitted; - int read_done; - - /* Only used by migration thread. Does not need a lock. */ - int transferred; - int prev_progress; - int bulk_completed; - - /* Lock must be taken _inside_ the BQL. */ - QemuMutex lock; -} BlkMigState; - -static BlkMigState block_mig_state; - -static void blk_mig_lock(void) -{ - qemu_mutex_lock(&block_mig_state.lock); -} - -static void blk_mig_unlock(void) -{ - qemu_mutex_unlock(&block_mig_state.lock); -} - -/* Must run outside of the BQL during the bulk phase, - * or the VM will stall. - */ - -static void blk_send(QEMUFile *f, BlkMigBlock * blk) -{ - int len; - uint64_t flags = BLK_MIG_FLAG_DEVICE_BLOCK; - - if (block_mig_state.zero_blocks && - buffer_is_zero(blk->buf, BLK_MIG_BLOCK_SIZE)) { - flags |= BLK_MIG_FLAG_ZERO_BLOCK; - } - - /* sector number and flags */ - qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS) - | flags); - - /* device name */ - len = strlen(blk->bmds->blk_name); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *) blk->bmds->blk_name, len); - - /* if a block is zero we need to flush here since the network - * bandwidth is now a lot higher than the storage device bandwidth. - * thus if we queue zero blocks we slow down the migration */ - if (flags & BLK_MIG_FLAG_ZERO_BLOCK) { - qemu_fflush(f); - return; - } - - qemu_put_buffer(f, blk->buf, BLK_MIG_BLOCK_SIZE); -} - -int blk_mig_active(void) -{ - return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list); -} - -int blk_mig_bulk_active(void) -{ - return blk_mig_active() && !block_mig_state.bulk_completed; -} - -uint64_t blk_mig_bytes_transferred(void) -{ - BlkMigDevState *bmds; - uint64_t sum = 0; - - blk_mig_lock(); - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - sum += bmds->completed_sectors; - } - blk_mig_unlock(); - return sum << BDRV_SECTOR_BITS; -} - -uint64_t blk_mig_bytes_remaining(void) -{ - return blk_mig_bytes_total() - blk_mig_bytes_transferred(); -} - -uint64_t blk_mig_bytes_total(void) -{ - BlkMigDevState *bmds; - uint64_t sum = 0; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - sum += bmds->total_sectors; - } - return sum << BDRV_SECTOR_BITS; -} - - -/* Called with migration lock held. */ - -static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) -{ - int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; - - if (sector < bmds->total_sectors) { - return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] & - (1UL << (chunk % (sizeof(unsigned long) * 8)))); - } else { - return 0; - } -} - -/* Called with migration lock held. */ - -static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, - int nb_sectors, int set) -{ - int64_t start, end; - unsigned long val, idx, bit; - - start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; - end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; - - for (; start <= end; start++) { - idx = start / (sizeof(unsigned long) * 8); - bit = start % (sizeof(unsigned long) * 8); - val = bmds->aio_bitmap[idx]; - if (set) { - val |= 1UL << bit; - } else { - val &= ~(1UL << bit); - } - bmds->aio_bitmap[idx] = val; - } -} - -static void alloc_aio_bitmap(BlkMigDevState *bmds) -{ - int64_t bitmap_size; - - bitmap_size = bmds->total_sectors + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; - bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; - - bmds->aio_bitmap = g_malloc0(bitmap_size); -} - -/* Never hold migration lock when yielding to the main loop! */ - -static void blk_mig_read_cb(void *opaque, int ret) -{ - BlkMigBlock *blk = opaque; - - blk_mig_lock(); - blk->ret = ret; - - QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); - bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); - - block_mig_state.submitted--; - block_mig_state.read_done++; - assert(block_mig_state.submitted >= 0); - blk_mig_unlock(); -} - -/* Called with no lock taken. */ - -static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) -{ - int64_t total_sectors = bmds->total_sectors; - int64_t cur_sector = bmds->cur_sector; - BlockBackend *bb = bmds->blk; - BlkMigBlock *blk; - int nr_sectors; - int64_t count; - - if (bmds->shared_base) { - bql_lock(); - /* Skip unallocated sectors; intentionally treats failure or - * partial sector as an allocated sector */ - while (cur_sector < total_sectors && - !bdrv_is_allocated(blk_bs(bb), cur_sector * BDRV_SECTOR_SIZE, - MAX_IS_ALLOCATED_SEARCH, &count)) { - if (count < BDRV_SECTOR_SIZE) { - break; - } - cur_sector += count >> BDRV_SECTOR_BITS; - } - bql_unlock(); - } - - if (cur_sector >= total_sectors) { - bmds->cur_sector = bmds->completed_sectors = total_sectors; - return 1; - } - - bmds->completed_sectors = cur_sector; - - cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1); - - /* we are going to transfer a full block even if it is not allocated */ - nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; - - if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { - nr_sectors = total_sectors - cur_sector; - } - - blk = g_new(BlkMigBlock, 1); - blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE); - blk->bmds = bmds; - blk->sector = cur_sector; - blk->nr_sectors = nr_sectors; - - qemu_iovec_init_buf(&blk->qiov, blk->buf, nr_sectors * BDRV_SECTOR_SIZE); - - blk_mig_lock(); - block_mig_state.submitted++; - blk_mig_unlock(); - - /* - * The migration thread does not have an AioContext. Lock the BQL so that - * I/O runs in the main loop AioContext (see - * qemu_get_current_aio_context()). - */ - bql_lock(); - bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE); - blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov, - 0, blk_mig_read_cb, blk); - bql_unlock(); - - bmds->cur_sector = cur_sector + nr_sectors; - return (bmds->cur_sector >= total_sectors); -} - -/* Called with the BQL taken. */ - -static int set_dirty_tracking(void) -{ - BlkMigDevState *bmds; - int ret; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bmds->dirty_bitmap = bdrv_create_dirty_bitmap(blk_bs(bmds->blk), - BLK_MIG_BLOCK_SIZE, - NULL, NULL); - if (!bmds->dirty_bitmap) { - ret = -errno; - goto fail; - } - } - return 0; - -fail: - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (bmds->dirty_bitmap) { - bdrv_release_dirty_bitmap(bmds->dirty_bitmap); - } - } - return ret; -} - -/* Called with the BQL taken. */ - -static void unset_dirty_tracking(void) -{ - BlkMigDevState *bmds; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (bmds->dirty_bitmap) { - bdrv_release_dirty_bitmap(bmds->dirty_bitmap); - } - } -} - -static int init_blk_migration(QEMUFile *f, Error **errp) -{ - BlockDriverState *bs; - BlkMigDevState *bmds; - int64_t sectors; - BdrvNextIterator it; - int i, num_bs = 0; - struct { - BlkMigDevState *bmds; - BlockDriverState *bs; - } *bmds_bs; - int ret; - - GRAPH_RDLOCK_GUARD_MAINLOOP(); - - block_mig_state.submitted = 0; - block_mig_state.read_done = 0; - block_mig_state.transferred = 0; - block_mig_state.total_sector_sum = 0; - block_mig_state.prev_progress = -1; - block_mig_state.bulk_completed = 0; - block_mig_state.zero_blocks = migrate_zero_blocks(); - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - num_bs++; - } - bmds_bs = g_malloc0(num_bs * sizeof(*bmds_bs)); - - for (i = 0, bs = bdrv_first(&it); bs; bs = bdrv_next(&it), i++) { - if (bdrv_is_read_only(bs)) { - continue; - } - - sectors = bdrv_nb_sectors(bs); - if (sectors == 0) { - continue; - } - if (sectors < 0) { - error_setg(errp, "Error getting length of block device %s", - bdrv_get_device_name(bs)); - ret = sectors; - bdrv_next_cleanup(&it); - goto out; - } - - bmds = g_new0(BlkMigDevState, 1); - bmds->blk = blk_new(qemu_get_aio_context(), - BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); - bmds->blk_name = g_strdup(bdrv_get_device_name(bs)); - bmds->bulk_completed = 0; - bmds->total_sectors = sectors; - bmds->completed_sectors = 0; - - assert(i < num_bs); - bmds_bs[i].bmds = bmds; - bmds_bs[i].bs = bs; - - block_mig_state.total_sector_sum += sectors; - - if (bmds->shared_base) { - trace_migration_block_init_shared(bdrv_get_device_name(bs)); - } else { - trace_migration_block_init_full(bdrv_get_device_name(bs)); - } - - QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); - } - - /* Can only insert new BDSes now because doing so while iterating block - * devices may end up in a deadlock (iterating the new BDSes, too). */ - for (i = 0; i < num_bs; i++) { - bmds = bmds_bs[i].bmds; - bs = bmds_bs[i].bs; - - if (bmds) { - ret = blk_insert_bs(bmds->blk, bs, errp); - if (ret < 0) { - goto out; - } - - alloc_aio_bitmap(bmds); - error_setg(&bmds->blocker, "block device is in use by migration"); - bdrv_op_block_all(bs, bmds->blocker); - } - } - - ret = 0; -out: - g_free(bmds_bs); - return ret; -} - -/* Called with no lock taken. */ - -static int blk_mig_save_bulked_block(QEMUFile *f) -{ - int64_t completed_sector_sum = 0; - BlkMigDevState *bmds; - int progress; - int ret = 0; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (bmds->bulk_completed == 0) { - if (mig_save_device_bulk(f, bmds) == 1) { - /* completed bulk section for this device */ - bmds->bulk_completed = 1; - } - completed_sector_sum += bmds->completed_sectors; - ret = 1; - break; - } else { - completed_sector_sum += bmds->completed_sectors; - } - } - - if (block_mig_state.total_sector_sum != 0) { - progress = completed_sector_sum * 100 / - block_mig_state.total_sector_sum; - } else { - progress = 100; - } - if (progress != block_mig_state.prev_progress) { - block_mig_state.prev_progress = progress; - qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) - | BLK_MIG_FLAG_PROGRESS); - trace_migration_block_progression(progress); - } - - return ret; -} - -static void blk_mig_reset_dirty_cursor(void) -{ - BlkMigDevState *bmds; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bmds->cur_dirty = 0; - } -} - -/* Called with the BQL taken. */ - -static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, - int is_async) -{ - BlkMigBlock *blk; - int64_t total_sectors = bmds->total_sectors; - int64_t sector; - int nr_sectors; - int ret = -EIO; - - for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { - blk_mig_lock(); - if (bmds_aio_inflight(bmds, sector)) { - blk_mig_unlock(); - blk_drain(bmds->blk); - } else { - blk_mig_unlock(); - } - bdrv_dirty_bitmap_lock(bmds->dirty_bitmap); - if (bdrv_dirty_bitmap_get_locked(bmds->dirty_bitmap, - sector * BDRV_SECTOR_SIZE)) { - if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { - nr_sectors = total_sectors - sector; - } else { - nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; - } - bdrv_reset_dirty_bitmap_locked(bmds->dirty_bitmap, - sector * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE); - bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); - - blk = g_new(BlkMigBlock, 1); - blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE); - blk->bmds = bmds; - blk->sector = sector; - blk->nr_sectors = nr_sectors; - - if (is_async) { - qemu_iovec_init_buf(&blk->qiov, blk->buf, - nr_sectors * BDRV_SECTOR_SIZE); - - blk->aiocb = blk_aio_preadv(bmds->blk, - sector * BDRV_SECTOR_SIZE, - &blk->qiov, 0, blk_mig_read_cb, - blk); - - blk_mig_lock(); - block_mig_state.submitted++; - bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); - blk_mig_unlock(); - } else { - ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE, blk->buf, 0); - if (ret < 0) { - goto error; - } - blk_send(f, blk); - - g_free(blk->buf); - g_free(blk); - } - - sector += nr_sectors; - bmds->cur_dirty = sector; - break; - } - - bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); - sector += BDRV_SECTORS_PER_DIRTY_CHUNK; - bmds->cur_dirty = sector; - } - - return (bmds->cur_dirty >= bmds->total_sectors); - -error: - trace_migration_block_save_device_dirty(sector); - g_free(blk->buf); - g_free(blk); - return ret; -} - -/* Called with the BQL taken. - * - * return value: - * 0: too much data for max_downtime - * 1: few enough data for max_downtime -*/ -static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) -{ - BlkMigDevState *bmds; - int ret = 1; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - ret = mig_save_device_dirty(f, bmds, is_async); - if (ret <= 0) { - break; - } - } - - return ret; -} - -/* Called with no locks taken. */ - -static int flush_blks(QEMUFile *f) -{ - BlkMigBlock *blk; - int ret = 0; - - trace_migration_block_flush_blks("Enter", block_mig_state.submitted, - block_mig_state.read_done, - block_mig_state.transferred); - - blk_mig_lock(); - while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { - if (migration_rate_exceeded(f)) { - break; - } - if (blk->ret < 0) { - ret = blk->ret; - break; - } - - QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); - blk_mig_unlock(); - blk_send(f, blk); - blk_mig_lock(); - - g_free(blk->buf); - g_free(blk); - - block_mig_state.read_done--; - block_mig_state.transferred++; - assert(block_mig_state.read_done >= 0); - } - blk_mig_unlock(); - - trace_migration_block_flush_blks("Exit", block_mig_state.submitted, - block_mig_state.read_done, - block_mig_state.transferred); - return ret; -} - -/* Called with the BQL taken. */ - -static int64_t get_remaining_dirty(void) -{ - BlkMigDevState *bmds; - int64_t dirty = 0; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bdrv_dirty_bitmap_lock(bmds->dirty_bitmap); - dirty += bdrv_get_dirty_count(bmds->dirty_bitmap); - bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); - } - - return dirty; -} - - - -/* Called with the BQL taken. */ -static void block_migration_cleanup_bmds(void) -{ - BlkMigDevState *bmds; - BlockDriverState *bs; - - unset_dirty_tracking(); - - while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { - QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); - - bs = blk_bs(bmds->blk); - if (bs) { - bdrv_op_unblock_all(bs, bmds->blocker); - } - error_free(bmds->blocker); - blk_unref(bmds->blk); - g_free(bmds->blk_name); - g_free(bmds->aio_bitmap); - g_free(bmds); - } -} - -/* Called with the BQL taken. */ -static void block_migration_cleanup(void *opaque) -{ - BlkMigBlock *blk; - - bdrv_drain_all(); - - block_migration_cleanup_bmds(); - - blk_mig_lock(); - while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { - QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); - g_free(blk->buf); - g_free(blk); - } - blk_mig_unlock(); -} - -static int block_save_setup(QEMUFile *f, void *opaque, Error **errp) -{ - int ret; - - trace_migration_block_save("setup", block_mig_state.submitted, - block_mig_state.transferred); - - warn_report("block migration is deprecated;" - " use blockdev-mirror with NBD instead"); - - ret = init_blk_migration(f, errp); - if (ret < 0) { - return ret; - } - - /* start track dirty blocks */ - ret = set_dirty_tracking(); - if (ret) { - error_setg_errno(errp, -ret, "Failed to start block dirty tracking"); - return ret; - } - - ret = flush_blks(f); - if (ret) { - error_setg_errno(errp, -ret, "Flushing block failed"); - return ret; - } - blk_mig_reset_dirty_cursor(); - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - - return ret; -} - -static int block_save_iterate(QEMUFile *f, void *opaque) -{ - int ret; - uint64_t last_bytes = qemu_file_transferred(f); - - trace_migration_block_save("iterate", block_mig_state.submitted, - block_mig_state.transferred); - - ret = flush_blks(f); - if (ret) { - return ret; - } - - blk_mig_reset_dirty_cursor(); - - /* control the rate of transfer */ - blk_mig_lock(); - while (block_mig_state.read_done * BLK_MIG_BLOCK_SIZE < - migration_rate_get() && - block_mig_state.submitted < MAX_PARALLEL_IO && - (block_mig_state.submitted + block_mig_state.read_done) < - MAX_IO_BUFFERS) { - blk_mig_unlock(); - if (block_mig_state.bulk_completed == 0) { - /* first finish the bulk phase */ - if (blk_mig_save_bulked_block(f) == 0) { - /* finished saving bulk on all devices */ - block_mig_state.bulk_completed = 1; - } - ret = 0; - } else { - /* Always called with the BQL taken for - * simplicity, block_save_complete also calls it. - */ - bql_lock(); - ret = blk_mig_save_dirty_block(f, 1); - bql_unlock(); - } - if (ret < 0) { - return ret; - } - blk_mig_lock(); - if (ret != 0) { - /* no more dirty blocks */ - break; - } - } - blk_mig_unlock(); - - ret = flush_blks(f); - if (ret) { - return ret; - } - - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - uint64_t delta_bytes = qemu_file_transferred(f) - last_bytes; - return (delta_bytes > 0); -} - -/* Called with the BQL taken. */ - -static int block_save_complete(QEMUFile *f, void *opaque) -{ - int ret; - - trace_migration_block_save("complete", block_mig_state.submitted, - block_mig_state.transferred); - - ret = flush_blks(f); - if (ret) { - return ret; - } - - blk_mig_reset_dirty_cursor(); - - /* we know for sure that save bulk is completed and - all async read completed */ - blk_mig_lock(); - assert(block_mig_state.submitted == 0); - blk_mig_unlock(); - - do { - ret = blk_mig_save_dirty_block(f, 0); - if (ret < 0) { - return ret; - } - } while (ret == 0); - - /* report completion */ - qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); - - trace_migration_block_save_complete(); - - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - - /* Make sure that our BlockBackends are gone, so that the block driver - * nodes can be inactivated. */ - block_migration_cleanup_bmds(); - - return 0; -} - -static void block_state_pending(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) -{ - /* Estimate pending number of bytes to send */ - uint64_t pending; - - bql_lock(); - pending = get_remaining_dirty(); - bql_unlock(); - - blk_mig_lock(); - pending += block_mig_state.submitted * BLK_MIG_BLOCK_SIZE + - block_mig_state.read_done * BLK_MIG_BLOCK_SIZE; - blk_mig_unlock(); - - /* Report at least one block pending during bulk phase */ - if (!pending && !block_mig_state.bulk_completed) { - pending = BLK_MIG_BLOCK_SIZE; - } - - trace_migration_block_state_pending(pending); - /* We don't do postcopy */ - *must_precopy += pending; -} - -static int block_load(QEMUFile *f, void *opaque, int version_id) -{ - static int banner_printed; - int len, flags; - char device_name[256]; - int64_t addr; - BlockBackend *blk, *blk_prev = NULL; - Error *local_err = NULL; - uint8_t *buf; - int64_t total_sectors = 0; - int nr_sectors; - int ret; - BlockDriverInfo bdi; - int cluster_size = BLK_MIG_BLOCK_SIZE; - - do { - addr = qemu_get_be64(f); - - flags = addr & (BDRV_SECTOR_SIZE - 1); - addr >>= BDRV_SECTOR_BITS; - - if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { - /* get device name */ - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)device_name, len); - device_name[len] = '\0'; - - blk = blk_by_name(device_name); - if (!blk) { - fprintf(stderr, "Error unknown block device %s\n", - device_name); - return -EINVAL; - } - - if (blk != blk_prev) { - blk_prev = blk; - total_sectors = blk_nb_sectors(blk); - if (total_sectors <= 0) { - error_report("Error getting length of block device %s", - device_name); - return -EINVAL; - } - - blk_activate(blk, &local_err); - if (local_err) { - error_report_err(local_err); - return -EINVAL; - } - - ret = bdrv_get_info(blk_bs(blk), &bdi); - if (ret == 0 && bdi.cluster_size > 0 && - bdi.cluster_size <= BLK_MIG_BLOCK_SIZE && - BLK_MIG_BLOCK_SIZE % bdi.cluster_size == 0) { - cluster_size = bdi.cluster_size; - } else { - cluster_size = BLK_MIG_BLOCK_SIZE; - } - } - - if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { - nr_sectors = total_sectors - addr; - } else { - nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; - } - - if (flags & BLK_MIG_FLAG_ZERO_BLOCK) { - ret = blk_pwrite_zeroes(blk, addr * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE, - BDRV_REQ_MAY_UNMAP); - } else { - int i; - int64_t cur_addr; - uint8_t *cur_buf; - - buf = g_malloc(BLK_MIG_BLOCK_SIZE); - qemu_get_buffer(f, buf, BLK_MIG_BLOCK_SIZE); - for (i = 0; i < BLK_MIG_BLOCK_SIZE / cluster_size; i++) { - cur_addr = addr * BDRV_SECTOR_SIZE + i * cluster_size; - cur_buf = buf + i * cluster_size; - - if ((!block_mig_state.zero_blocks || - cluster_size < BLK_MIG_BLOCK_SIZE) && - buffer_is_zero(cur_buf, cluster_size)) { - ret = blk_pwrite_zeroes(blk, cur_addr, - cluster_size, - BDRV_REQ_MAY_UNMAP); - } else { - ret = blk_pwrite(blk, cur_addr, cluster_size, cur_buf, - 0); - } - if (ret < 0) { - break; - } - } - g_free(buf); - } - - if (ret < 0) { - return ret; - } - } else if (flags & BLK_MIG_FLAG_PROGRESS) { - if (!banner_printed) { - printf("Receiving block device images\n"); - banner_printed = 1; - } - printf("Completed %d %%%c", (int)addr, - (addr == 100) ? '\n' : '\r'); - fflush(stdout); - } else if (!(flags & BLK_MIG_FLAG_EOS)) { - fprintf(stderr, "Unknown block migration flags: 0x%x\n", flags); - return -EINVAL; - } - ret = qemu_file_get_error(f); - if (ret != 0) { - return ret; - } - } while (!(flags & BLK_MIG_FLAG_EOS)); - - return 0; -} - -static bool block_is_active(void *opaque) -{ - return migrate_block(); -} - -static SaveVMHandlers savevm_block_handlers = { - .save_setup = block_save_setup, - .save_live_iterate = block_save_iterate, - .save_live_complete_precopy = block_save_complete, - .state_pending_exact = block_state_pending, - .state_pending_estimate = block_state_pending, - .load_state = block_load, - .save_cleanup = block_migration_cleanup, - .is_active = block_is_active, -}; - -void blk_mig_init(void) -{ - QSIMPLEQ_INIT(&block_mig_state.bmds_list); - QSIMPLEQ_INIT(&block_mig_state.blk_list); - qemu_mutex_init(&block_mig_state.lock); - - register_savevm_live("block", 0, 1, &savevm_block_handlers, - &block_mig_state); -} diff --git a/migration/block.h b/migration/block.h deleted file mode 100644 index 3178609dbd..0000000000 --- a/migration/block.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * QEMU live block migration - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Liran Schour - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef MIGRATION_BLOCK_H -#define MIGRATION_BLOCK_H - -#ifdef CONFIG_LIVE_BLOCK_MIGRATION -int blk_mig_active(void); -int blk_mig_bulk_active(void); -uint64_t blk_mig_bytes_transferred(void); -uint64_t blk_mig_bytes_remaining(void); -uint64_t blk_mig_bytes_total(void); - -#else -static inline int blk_mig_active(void) -{ - return false; -} - -static inline int blk_mig_bulk_active(void) -{ - return false; -} - -static inline uint64_t blk_mig_bytes_transferred(void) -{ - return 0; -} - -static inline uint64_t blk_mig_bytes_remaining(void) -{ - return 0; -} - -static inline uint64_t blk_mig_bytes_total(void) -{ - return 0; -} -#endif /* CONFIG_LIVE_BLOCK_MIGRATION */ - -void migrate_set_block_enabled(bool value, Error **errp); -#endif /* MIGRATION_BLOCK_H */ diff --git a/migration/colo.c b/migration/colo.c index 5600a43d78..e2b450c132 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -18,7 +18,6 @@ #include "qemu-file.h" #include "savevm.h" #include "migration/colo.h" -#include "block.h" #include "io/channel-buffer.h" #include "trace.h" #include "qemu/error-report.h" diff --git a/migration/meson.build b/migration/meson.build index f76b1ba328..d7e118f584 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -39,9 +39,6 @@ else endif system_ss.add(when: rdma, if_true: files('rdma.c')) -if get_option('live_block_migration').allowed() - system_ss.add(files('block.c')) -endif system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 734c1d29ce..a497ef95d7 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -145,15 +145,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } } - if (info->disk) { - monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", - info->disk->transferred >> 10); - monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", - info->disk->remaining >> 10); - monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", - info->disk->total >> 10); - } - if (info->xbzrle_cache) { monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", info->xbzrle_cache->cache_size); @@ -726,24 +717,8 @@ static void hmp_migrate_status_cb(void *opaque) info = qmp_query_migrate(NULL); if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || info->status == MIGRATION_STATUS_SETUP) { - if (info->disk) { - int progress; - - if (info->disk->remaining) { - progress = info->disk->transferred * 100 / info->disk->total; - } else { - progress = 100; - } - - monitor_printf(status->mon, "Completed %d %%\r", progress); - monitor_flush(status->mon); - } - timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); } else { - if (migrate_block()) { - monitor_printf(status->mon, "\n"); - } if (info->error_desc) { error_report("%s", info->error_desc); } diff --git a/migration/migration.c b/migration/migration.c index 91327b98c5..6f209601d3 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -46,7 +46,6 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qnull.h" #include "qemu/rcu.h" -#include "block.h" #include "postcopy-ram.h" #include "qemu/thread.h" #include "trace.h" @@ -240,7 +239,6 @@ void migration_object_init(void) migration_object_check(current_migration, &error_fatal); - blk_mig_init(); ram_mig_init(); dirty_bitmap_mig_init(); } @@ -1206,16 +1204,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) } } -static void populate_disk_info(MigrationInfo *info) -{ - if (blk_mig_active()) { - info->disk = g_malloc0(sizeof(*info->disk)); - info->disk->transferred = blk_mig_bytes_transferred(); - info->disk->remaining = blk_mig_bytes_remaining(); - info->disk->total = blk_mig_bytes_total(); - } -} - static void fill_source_migration_info(MigrationInfo *info) { MigrationState *s = migrate_get_current(); @@ -1258,7 +1246,6 @@ static void fill_source_migration_info(MigrationInfo *info) /* TODO add some postcopy stats */ populate_time_info(info, s); populate_ram_info(info, s); - populate_disk_info(info); migration_populate_vfio_info(info); break; case MIGRATION_STATUS_COLO: @@ -1423,7 +1410,6 @@ static void migrate_fd_cleanup(MigrationState *s) type = migration_has_failed(s) ? MIG_EVENT_PRECOPY_FAILED : MIG_EVENT_PRECOPY_DONE; migration_call_notifiers(s, type, NULL); - block_cleanup_parameters(); yank_unregister_instance(MIGRATION_YANK_INSTANCE); } @@ -2102,7 +2088,6 @@ void qmp_migrate(const char *uri, bool has_channels, "a valid migration protocol"); migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); - block_cleanup_parameters(); } if (local_err) { diff --git a/migration/options.c b/migration/options.c index 856b2fa33c..cc2599ae4a 100644 --- a/migration/options.c +++ b/migration/options.c @@ -195,7 +195,6 @@ Property migration_properties[] = { MIGRATION_CAPABILITY_POSTCOPY_PREEMPT), DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO), DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM), - DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK), DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH), DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD), DEFINE_PROP_MIG_CAP("x-background-snapshot", @@ -225,13 +224,6 @@ bool migrate_background_snapshot(void) return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; } -bool migrate_block(void) -{ - MigrationState *s = migrate_get_current(); - - return s->capabilities[MIGRATION_CAPABILITY_BLOCK]; -} - bool migrate_colo(void) { MigrationState *s = migrate_get_current(); @@ -484,18 +476,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) ERRP_GUARD(); MigrationIncomingState *mis = migration_incoming_get_current(); -#ifndef CONFIG_LIVE_BLOCK_MIGRATION - if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { - error_setg(errp, "QEMU compiled without old-style block migration"); - error_append_hint(errp, "Use blockdev-mirror with NBD instead.\n"); - return false; - } -#endif - if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { - warn_report("block migration is deprecated;" - " use blockdev-mirror with NBD instead"); - } - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { warn_report("old compression method is deprecated;" " use multifd compression methods instead"); @@ -706,11 +686,6 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) int i; for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { -#ifndef CONFIG_LIVE_BLOCK_MIGRATION - if (i == MIGRATION_CAPABILITY_BLOCK) { - continue; - } -#endif caps = g_malloc0(sizeof(*caps)); caps->capability = i; caps->state = s->capabilities[i]; diff --git a/migration/options.h b/migration/options.h index e9cb60444f..3c56d70d07 100644 --- a/migration/options.h +++ b/migration/options.h @@ -25,7 +25,6 @@ extern Property migration_properties[]; /* capabilities */ bool migrate_auto_converge(void); -bool migrate_block(void); bool migrate_colo(void); bool migrate_compress(void); bool migrate_dirty_bitmaps(void); diff --git a/migration/ram.c b/migration/ram.c index 50df1e9cd2..a5320aa889 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -53,7 +53,6 @@ #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "migration/colo.h" -#include "block.h" #include "sysemu/cpu-throttle.h" #include "savevm.h" #include "qemu/iov.h" @@ -1025,13 +1024,6 @@ static void migration_trigger_throttle(RAMState *rs) uint64_t bytes_dirty_period = rs->num_dirty_pages_period * TARGET_PAGE_SIZE; uint64_t bytes_dirty_threshold = bytes_xfer_period * threshold / 100; - /* During block migration the auto-converge logic incorrectly detects - * that ram migration makes no progress. Avoid this by disabling the - * throttling logic during the bulk phase of block migration. */ - if (blk_mig_bulk_active()) { - return; - } - /* * The following detection logic can be refined later. For now: * Check to see if the ratio between dirtied bytes and the approx. @@ -3230,13 +3222,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) int64_t t0; int done = 0; - if (blk_mig_bulk_active()) { - /* Avoid transferring ram during bulk phase of block migration as - * the bulk phase will usually take a long time and transferring - * ram updates during that time is pointless. */ - goto out; - } - /* * We'll take this lock a little bit long, but it's okay for two reasons. * Firstly, the only possible other thread to take it is who calls diff --git a/migration/savevm.c b/migration/savevm.c index 4509482ec4..6c789bd54b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1711,11 +1711,6 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) return -EINVAL; } - if (migrate_block()) { - error_setg(errp, "Block migration and snapshots are incompatible"); - return -EINVAL; - } - ret = migrate_init(ms, errp); if (ret) { return ret; diff --git a/qapi/migration.json b/qapi/migration.json index 381b52f680..f721039c6e 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -193,9 +193,6 @@ # @ram: @MigrationStats containing detailed migration status, only # returned if status is 'active' or 'completed'(since 1.2) # -# @disk: @MigrationStats containing detailed disk migration status, -# only returned if status is 'active' and it is a block migration -# # @xbzrle-cache: @XBZRLECacheStats containing detailed XBZRLE # migration statistics, only returned if XBZRLE feature is on and # status is 'active' or 'completed' (since 1.2) @@ -262,17 +259,15 @@ # # Features: # -# @deprecated: Member @disk is deprecated because block migration is. -# Member @compression is deprecated because it is unreliable and -# untested. It is recommended to use multifd migration, which -# offers an alternative compression implementation that is -# reliable and tested. +# @deprecated: Member @compression is deprecated because it is +# unreliable and untested. It is recommended to use multifd +# migration, which offers an alternative compression +# implementation that is reliable and tested. # # Since: 0.14 ## { 'struct': 'MigrationInfo', 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats', - '*disk': { 'type': 'MigrationStats', 'features': [ 'deprecated' ] }, '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', @@ -294,8 +289,7 @@ # # Returns information about current migration process. If migration # is active there will be another json-object with RAM migration -# status and if block migration is active another one with block -# migration status. +# status. # # Returns: @MigrationInfo # @@ -333,7 +327,7 @@ # -> { "execute": "query-migrate" } # <- { "return": { "status": "failed" } } # -# 4. Migration is being performed and is not a block migration: +# 4. Migration is being performed: # # -> { "execute": "query-migrate" } # <- { @@ -354,33 +348,7 @@ # } # } # -# 5. Migration is being performed and is a block migration: -# -# -> { "execute": "query-migrate" } -# <- { -# "return":{ -# "status":"active", -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, -# "ram":{ -# "total":1057024, -# "remaining":1053304, -# "transferred":3720, -# "duplicate":123, -# "normal":123, -# "normal-bytes":123456, -# "dirty-sync-count":15 -# }, -# "disk":{ -# "total":20971520, -# "remaining":20880384, -# "transferred":91136 -# } -# } -# } -# -# 6. Migration is being performed and XBZRLE is active: +# 5. Migration is being performed and XBZRLE is active: # # -> { "execute": "query-migrate" } # <- { @@ -460,11 +428,6 @@ # @release-ram: if enabled, qemu will free the migrated ram pages on # the source during postcopy-ram migration. (since 2.9) # -# @block: If enabled, QEMU will also migrate the contents of all block -# devices. Default is disabled. A possible alternative uses -# mirror jobs to a builtin NBD server on the destination, which -# offers more flexibility. (Since 2.10) -# # @return-path: If enabled, migration will use the return path even # for precopy. (since 2.10) # @@ -528,11 +491,10 @@ # # Features: # -# @deprecated: Member @block is deprecated. Use blockdev-mirror with -# NBD instead. Member @compress is deprecated because it is -# unreliable and untested. It is recommended to use multifd -# migration, which offers an alternative compression -# implementation that is reliable and tested. +# @deprecated: Member @compress is deprecated because it is unreliable +# and untested. It is recommended to use multifd migration, which +# offers an alternative compression implementation that is +# reliable and tested. # # @unstable: Members @x-colo and @x-ignore-shared are experimental. # @@ -544,7 +506,6 @@ 'events', 'postcopy-ram', { 'name': 'x-colo', 'features': [ 'unstable' ] }, 'release-ram', - { 'name': 'block', 'features': [ 'deprecated' ] }, 'return-path', 'pause-before-switchover', 'multifd', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 0a29d35fdb..6ce5a8b72a 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -143,8 +143,6 @@ meson_options_help() { printf "%s\n" ' libvduse build VDUSE Library' printf "%s\n" ' linux-aio Linux AIO support' printf "%s\n" ' linux-io-uring Linux io_uring support' - printf "%s\n" ' live-block-migration' - printf "%s\n" ' block migration in the main migration stream' printf "%s\n" ' lzfse lzfse support for DMG images' printf "%s\n" ' lzo lzo compression support' printf "%s\n" ' malloc-trim enable libc malloc_trim() for memory optimization' @@ -382,8 +380,6 @@ _meson_option_parse() { --disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;; --enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;; --disable-linux-io-uring) printf "%s" -Dlinux_io_uring=disabled ;; - --enable-live-block-migration) printf "%s" -Dlive_block_migration=enabled ;; - --disable-live-block-migration) printf "%s" -Dlive_block_migration=disabled ;; --localedir=*) quote_sh "-Dlocaledir=$2" ;; --localstatedir=*) quote_sh "-Dlocalstatedir=$2" ;; --enable-lzfse) printf "%s" -Dlzfse=enabled ;; From 0222111a22b2d3e08c62edb6b18bd8bdea4b64d5 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 30 Apr 2024 11:27:36 -0300 Subject: [PATCH 0538/2884] migration: Remove non-multifd compression The 'compress' migration capability enables the old compression code which has shown issues over the years and is thought to be less stable and tested than the more recent multifd-based compression. The old compression code has been deprecated in 8.2 and now is time to remove it. Deprecation commit 864128df46 ("migration: Deprecate old compression method"). Acked-by: Markus Armbruster Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/about/deprecated.rst | 11 - docs/about/removed-features.rst | 55 ++++ hw/core/machine.c | 1 - migration/meson.build | 1 - migration/migration-hmp-cmds.c | 47 --- migration/migration.c | 13 - migration/migration.h | 7 - migration/options.c | 164 ---------- migration/options.h | 5 - migration/qemu-file.c | 78 ----- migration/qemu-file.h | 4 - migration/ram-compress.c | 564 -------------------------------- migration/ram-compress.h | 77 ----- migration/ram.c | 154 +-------- qapi/migration.json | 112 ------- tests/qtest/migration-test.c | 139 -------- 16 files changed, 64 insertions(+), 1368 deletions(-) delete mode 100644 migration/ram-compress.c delete mode 100644 migration/ram-compress.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 3d324930f3..64b8f838be 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -473,14 +473,3 @@ both, older and future versions of QEMU. The ``blacklist`` config file option has been renamed to ``block-rpcs`` (to be in sync with the renaming of the corresponding command line option). - -Migration ---------- - -old compression method (since 8.2) -'''''''''''''''''''''''''''''''''' - -Compression method fails too much. Too many races. We are going to -remove it if nobody fixes it. For starters, migration-test -compression tests are disabled because they fail randomly. If you need -compression, use multifd compression methods. diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 9e7d8ee4ff..fba0cfb0b0 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -505,6 +505,11 @@ configurations (e.g. -smp 8,sockets=0) is removed since 9.0, users have to ensure that all the topology members described with -smp are greater than zero. +``-global migration.decompress-error-check`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Removed along with the ``compression`` migration capability. + User-mode emulator command line arguments ----------------------------------------- @@ -641,6 +646,31 @@ Block migration has been removed. For a replacement, see "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst. +``migrate-set-parameter`` ``compress-level`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-zlib-level`` or ``multifd-zstd-level`` instead. + +``migrate-set-parameter`` ``compress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate-set-parameter`` ``compress-wait-thread`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Removed with no replacement. + +``migrate-set-parameter`` ``decompress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate-set-capability`` ``compress`` option (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-compression`` instead. + Human Monitor Protocol (HMP) commands ------------------------------------- @@ -722,6 +752,31 @@ Block migration has been removed. For a replacement, see "QMP invocation for live storage migration with ``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst. +``migrate_set_parameter`` ``compress-level`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-zlib-level`` or ``multifd-zstd-level`` instead. + +``migrate_set_parameter`` ``compress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate_set_parameter`` ``compress-wait-thread`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Removed with no replacement. + +``migrate_set_parameter`` ``decompress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate_set_capability`` ``compress`` option (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-compression`` instead. + Host Architectures ------------------ diff --git a/hw/core/machine.c b/hw/core/machine.c index 4ff60911e7..c7ceb11501 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -192,7 +192,6 @@ GlobalProperty hw_compat_3_0[] = {}; const size_t hw_compat_3_0_len = G_N_ELEMENTS(hw_compat_3_0); GlobalProperty hw_compat_2_12[] = { - { "migration", "decompress-error-check", "off" }, { "hda-audio", "use-timer", "false" }, { "cirrus-vga", "global-vmstate", "true" }, { "VGA", "global-vmstate", "true" }, diff --git a/migration/meson.build b/migration/meson.build index d7e118f584..8815f80837 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -23,7 +23,6 @@ system_ss.add(files( 'multifd.c', 'multifd-zlib.c', 'multifd-zero-page.c', - 'ram-compress.c', 'options.c', 'postcopy-ram.c', 'savevm.c', diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index a497ef95d7..9f0e8029e0 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -46,8 +46,6 @@ static void migration_global_dump(Monitor *mon) ms->send_configuration ? "on" : "off"); monitor_printf(mon, "send-section-footer: %s\n", ms->send_section_footer ? "on" : "off"); - monitor_printf(mon, "decompress-error-check: %s\n", - ms->decompress_error_check ? "on" : "off"); monitor_printf(mon, "clear-bitmap-shift: %u\n", ms->clear_bitmap_shift); } @@ -162,19 +160,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->xbzrle_cache->overflow); } - if (info->compression) { - monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", - info->compression->pages); - monitor_printf(mon, "compression busy: %" PRIu64 "\n", - info->compression->busy); - monitor_printf(mon, "compression busy rate: %0.2f\n", - info->compression->busy_rate); - monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n", - info->compression->compressed_size >> 10); - monitor_printf(mon, "compression rate: %0.2f\n", - info->compression->compression_rate); - } - if (info->has_cpu_throttle_percentage) { monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", info->cpu_throttle_percentage); @@ -263,22 +248,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 " ms\n", MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP), params->announce_step); - assert(params->has_compress_level); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL), - params->compress_level); - assert(params->has_compress_threads); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), - params->compress_threads); - assert(params->has_compress_wait_thread); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), - params->compress_wait_thread ? "on" : "off"); - assert(params->has_decompress_threads); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), - params->decompress_threads); assert(params->has_throttle_trigger_threshold); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), @@ -520,22 +489,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } switch (val) { - case MIGRATION_PARAMETER_COMPRESS_LEVEL: - p->has_compress_level = true; - visit_type_uint8(v, param, &p->compress_level, &err); - break; - case MIGRATION_PARAMETER_COMPRESS_THREADS: - p->has_compress_threads = true; - visit_type_uint8(v, param, &p->compress_threads, &err); - break; - case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: - p->has_compress_wait_thread = true; - visit_type_bool(v, param, &p->compress_wait_thread, &err); - break; - case MIGRATION_PARAMETER_DECOMPRESS_THREADS: - p->has_decompress_threads = true; - visit_type_uint8(v, param, &p->decompress_threads, &err); - break; case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: p->has_throttle_trigger_threshold = true; visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err); diff --git a/migration/migration.c b/migration/migration.c index 6f209601d3..e88b24f1e6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -27,7 +27,6 @@ #include "sysemu/cpu-throttle.h" #include "rdma.h" #include "ram.h" -#include "ram-compress.h" #include "migration/global_state.h" #include "migration/misc.h" #include "migration.h" @@ -356,7 +355,6 @@ void migration_incoming_state_destroy(void) struct MigrationIncomingState *mis = migration_incoming_get_current(); multifd_recv_cleanup(); - compress_threads_load_cleanup(); if (mis->to_src_file) { /* Tell source that we are done */ @@ -649,10 +647,6 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, } #ifdef CONFIG_RDMA } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { - if (migrate_compress()) { - error_setg(errp, "RDMA and compression can't be used together"); - return; - } if (migrate_xbzrle()) { error_setg(errp, "RDMA and XBZRLE can't be used together"); return; @@ -745,11 +739,6 @@ process_incoming_migration_co(void *opaque) assert(mis->from_src_file); - if (compress_threads_load_setup(mis->from_src_file)) { - error_setg(&local_err, "Failed to setup decompress threads"); - goto fail; - } - mis->largest_page_size = qemu_ram_pagesize_largest(); postcopy_state_set(POSTCOPY_INCOMING_NONE); migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, @@ -1181,8 +1170,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->xbzrle_cache->overflow = xbzrle_counters.overflow; } - populate_compress(info); - if (cpu_throttle_active()) { info->has_cpu_throttle_percentage = true; info->cpu_throttle_percentage = cpu_throttle_get_percentage(); diff --git a/migration/migration.h b/migration/migration.h index a02c2ec782..6af01362d4 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -392,13 +392,6 @@ struct MigrationState { /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; - /* - * Whether we abort the migration if decompression errors are - * detected at the destination. It is left at false for qemu - * older than 3.0, since only newer qemu sends streams that - * do not trigger spurious decompression errors. - */ - bool decompress_error_check; /* * This variable only affects behavior when postcopy preempt mode is * enabled. diff --git a/migration/options.c b/migration/options.c index cc2599ae4a..5ab5b6d85d 100644 --- a/migration/options.c +++ b/migration/options.c @@ -40,13 +40,6 @@ * for sending the last part */ #define DEFAULT_MIGRATE_SET_DOWNTIME 300 -/* Default compression thread count */ -#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8 -/* Default decompression thread count, usually decompression is at - * least 4 times as fast as compression.*/ -#define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 -/*0: means nocompress, 1: best speed, ... 9: best compress ratio */ -#define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 /* Define default autoconverge cpu throttle migration parameters */ #define DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD 50 #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 @@ -92,8 +85,6 @@ Property migration_properties[] = { send_configuration, true), DEFINE_PROP_BOOL("send-section-footer", MigrationState, send_section_footer, true), - DEFINE_PROP_BOOL("decompress-error-check", MigrationState, - decompress_error_check, true), DEFINE_PROP_BOOL("multifd-flush-after-each-section", MigrationState, multifd_flush_after_each_section, false), DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState, @@ -102,17 +93,6 @@ Property migration_properties[] = { preempt_pre_7_2, false), /* Migration parameters */ - DEFINE_PROP_UINT8("x-compress-level", MigrationState, - parameters.compress_level, - DEFAULT_MIGRATE_COMPRESS_LEVEL), - DEFINE_PROP_UINT8("x-compress-threads", MigrationState, - parameters.compress_threads, - DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT), - DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState, - parameters.compress_wait_thread, true), - DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, - parameters.decompress_threads, - DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, parameters.throttle_trigger_threshold, DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD), @@ -188,7 +168,6 @@ Property migration_properties[] = { DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL), DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE), DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS), - DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS), DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS), DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM), DEFINE_PROP_MIG_CAP("x-postcopy-preempt", @@ -231,13 +210,6 @@ bool migrate_colo(void) return s->capabilities[MIGRATION_CAPABILITY_X_COLO]; } -bool migrate_compress(void) -{ - MigrationState *s = migrate_get_current(); - - return s->capabilities[MIGRATION_CAPABILITY_COMPRESS]; -} - bool migrate_dirty_bitmaps(void) { MigrationState *s = migrate_get_current(); @@ -451,7 +423,6 @@ INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, MIGRATION_CAPABILITY_AUTO_CONVERGE, MIGRATION_CAPABILITY_RELEASE_RAM, MIGRATION_CAPABILITY_RDMA_PIN_ALL, - MIGRATION_CAPABILITY_COMPRESS, MIGRATION_CAPABILITY_XBZRLE, MIGRATION_CAPABILITY_X_COLO, MIGRATION_CAPABILITY_VALIDATE_UUID, @@ -476,11 +447,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) ERRP_GUARD(); MigrationIncomingState *mis = migration_incoming_get_current(); - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - warn_report("old compression method is deprecated;" - " use multifd compression methods instead"); - } - #ifndef CONFIG_REPLICATION if (new_caps[MIGRATION_CAPABILITY_X_COLO]) { error_setg(errp, "QEMU compiled without replication module" @@ -549,7 +515,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) #ifdef CONFIG_LINUX if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND] && (!new_caps[MIGRATION_CAPABILITY_MULTIFD] || - new_caps[MIGRATION_CAPABILITY_COMPRESS] || new_caps[MIGRATION_CAPABILITY_XBZRLE] || migrate_multifd_compression() || migrate_tls())) { @@ -571,17 +536,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) return false; } - /* - * Preempt mode requires urgent pages to be sent in separate - * channel, OTOH compression logic will disorder all pages into - * different compression channels, which is not compatible with the - * preempt assumptions on channel assignments. - */ - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, "Postcopy preempt not compatible with compress"); - return false; - } - if (migrate_incoming_started()) { error_setg(errp, "Postcopy preempt must be set before incoming starts"); @@ -590,10 +544,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, "Multifd is not compatible with compress"); - return false; - } if (migrate_incoming_started()) { error_setg(errp, "Multifd must be set before incoming starts"); return false; @@ -628,13 +578,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } } - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { - error_setg(errp, "Compression is not compatible with xbzrle"); - return false; - } - } - if (new_caps[MIGRATION_CAPABILITY_MAPPED_RAM]) { if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { error_setg(errp, @@ -642,12 +585,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) return false; } - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, - "Mapped-ram migration is incompatible with compression"); - return false; - } - if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { error_setg(errp, "Mapped-ram migration is incompatible with postcopy"); @@ -744,27 +681,6 @@ uint32_t migrate_checkpoint_delay(void) return s->parameters.x_checkpoint_delay; } -int migrate_compress_level(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.compress_level; -} - -int migrate_compress_threads(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.compress_threads; -} - -int migrate_compress_wait_thread(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.compress_wait_thread; -} - uint8_t migrate_cpu_throttle_increment(void) { MigrationState *s = migrate_get_current(); @@ -786,13 +702,6 @@ bool migrate_cpu_throttle_tailslow(void) return s->parameters.cpu_throttle_tailslow; } -int migrate_decompress_threads(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.decompress_threads; -} - uint64_t migrate_downtime_limit(void) { MigrationState *s = migrate_get_current(); @@ -938,14 +847,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) /* TODO use QAPI_CLONE() instead of duplicating it inline */ params = g_malloc0(sizeof(*params)); - params->has_compress_level = true; - params->compress_level = s->parameters.compress_level; - params->has_compress_threads = true; - params->compress_threads = s->parameters.compress_threads; - params->has_compress_wait_thread = true; - params->compress_wait_thread = s->parameters.compress_wait_thread; - params->has_decompress_threads = true; - params->decompress_threads = s->parameters.decompress_threads; params->has_throttle_trigger_threshold = true; params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold; params->has_cpu_throttle_initial = true; @@ -1014,10 +915,6 @@ void migrate_params_init(MigrationParameters *params) params->tls_creds = g_strdup(""); /* Set has_* up only for parameter checks */ - params->has_compress_level = true; - params->has_compress_threads = true; - params->has_compress_wait_thread = true; - params->has_decompress_threads = true; params->has_throttle_trigger_threshold = true; params->has_cpu_throttle_initial = true; params->has_cpu_throttle_increment = true; @@ -1050,27 +947,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) { ERRP_GUARD(); - if (params->has_compress_level && - (params->compress_level > 9)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", - "a value between 0 and 9"); - return false; - } - - if (params->has_compress_threads && (params->compress_threads < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "compress_threads", - "a value between 1 and 255"); - return false; - } - - if (params->has_decompress_threads && (params->decompress_threads < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "decompress_threads", - "a value between 1 and 255"); - return false; - } - if (params->has_throttle_trigger_threshold && (params->throttle_trigger_threshold < 1 || params->throttle_trigger_threshold > 100)) { @@ -1244,22 +1120,6 @@ static void migrate_params_test_apply(MigrateSetParameters *params, /* TODO use QAPI_CLONE() instead of duplicating it inline */ - if (params->has_compress_level) { - dest->compress_level = params->compress_level; - } - - if (params->has_compress_threads) { - dest->compress_threads = params->compress_threads; - } - - if (params->has_compress_wait_thread) { - dest->compress_wait_thread = params->compress_wait_thread; - } - - if (params->has_decompress_threads) { - dest->decompress_threads = params->decompress_threads; - } - if (params->has_throttle_trigger_threshold) { dest->throttle_trigger_threshold = params->throttle_trigger_threshold; } @@ -1364,30 +1224,6 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) /* TODO use QAPI_CLONE() instead of duplicating it inline */ - if (params->has_compress_level) { - warn_report("old compression is deprecated;" - " use multifd compression methods instead"); - s->parameters.compress_level = params->compress_level; - } - - if (params->has_compress_threads) { - warn_report("old compression is deprecated;" - " use multifd compression methods instead"); - s->parameters.compress_threads = params->compress_threads; - } - - if (params->has_compress_wait_thread) { - warn_report("old compression is deprecated;" - " use multifd compression methods instead"); - s->parameters.compress_wait_thread = params->compress_wait_thread; - } - - if (params->has_decompress_threads) { - warn_report("old compression is deprecated;" - " use multifd compression methods instead"); - s->parameters.decompress_threads = params->decompress_threads; - } - if (params->has_throttle_trigger_threshold) { s->parameters.throttle_trigger_threshold = params->throttle_trigger_threshold; } diff --git a/migration/options.h b/migration/options.h index 3c56d70d07..4b21cc2669 100644 --- a/migration/options.h +++ b/migration/options.h @@ -26,7 +26,6 @@ extern Property migration_properties[]; bool migrate_auto_converge(void); bool migrate_colo(void); -bool migrate_compress(void); bool migrate_dirty_bitmaps(void); bool migrate_events(void); bool migrate_mapped_ram(void); @@ -67,13 +66,9 @@ const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void); bool migrate_has_block_bitmap_mapping(void); uint32_t migrate_checkpoint_delay(void); -int migrate_compress_level(void); -int migrate_compress_threads(void); -int migrate_compress_wait_thread(void); uint8_t migrate_cpu_throttle_increment(void); uint8_t migrate_cpu_throttle_initial(void); bool migrate_cpu_throttle_tailslow(void); -int migrate_decompress_threads(void); uint64_t migrate_downtime_limit(void); uint8_t migrate_max_cpu_throttle(void); uint64_t migrate_max_bandwidth(void); diff --git a/migration/qemu-file.c b/migration/qemu-file.c index a10882d47f..9ccbbb0099 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -778,84 +778,6 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } -/* return the size after compression, or negative value on error */ -static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) -{ - int err; - - err = deflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = source_len; - stream->next_in = (uint8_t *)source; - stream->avail_out = dest_len; - stream->next_out = dest; - - err = deflate(stream, Z_FINISH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->next_out - dest; -} - -/* Compress size bytes of data start at p and store the compressed - * data to the buffer of f. - * - * Since the file is dummy file with empty_ops, return -1 if f has no space to - * save the compressed data. - */ -ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, - const uint8_t *p, size_t size) -{ - ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); - - if (blen < compressBound(size)) { - return -1; - } - - blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), - blen, p, size); - if (blen < 0) { - return -1; - } - - qemu_put_be32(f, blen); - add_buf_to_iovec(f, blen); - return blen + sizeof(int32_t); -} - -/* Put the data in the buffer of f_src to the buffer of f_des, and - * then reset the buf_index of f_src to 0. - */ - -int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src) -{ - int len = 0; - - if (f_src->buf_index > 0) { - len = f_src->buf_index; - qemu_put_buffer(f_des, f_src->buf, f_src->buf_index); - f_src->buf_index = 0; - f_src->iovcnt = 0; - } - return len; -} - -/* - * Check if the writable buffer is empty - */ - -bool qemu_file_buffer_empty(QEMUFile *file) -{ - assert(qemu_file_is_writable(file)); - - return !file->iovcnt; -} - /* * Get a string whose length is determined by a single preceding byte * A preallocated 256 byte buffer must be passed in. diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 32fd4a34fd..11c2120edd 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -54,10 +54,6 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, size_t coroutine_mixed_fn qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); size_t coroutine_mixed_fn qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); -ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, - const uint8_t *p, size_t size); -int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); -bool qemu_file_buffer_empty(QEMUFile *file); /* * Note that you can only peek continuous bytes from where the current pointer diff --git a/migration/ram-compress.c b/migration/ram-compress.c deleted file mode 100644 index fa4388f6a6..0000000000 --- a/migration/ram-compress.c +++ /dev/null @@ -1,564 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2011-2015 Red Hat Inc - * - * Authors: - * Juan Quintela - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" - -#include "ram-compress.h" - -#include "qemu/error-report.h" -#include "qemu/stats64.h" -#include "migration.h" -#include "options.h" -#include "io/channel-null.h" -#include "exec/target_page.h" -#include "exec/ramblock.h" -#include "ram.h" -#include "migration-stats.h" - -static struct { - int64_t pages; - int64_t busy; - double busy_rate; - int64_t compressed_size; - double compression_rate; - /* compression statistics since the beginning of the period */ - /* amount of count that no free thread to compress data */ - uint64_t compress_thread_busy_prev; - /* amount bytes after compression */ - uint64_t compressed_size_prev; - /* amount of compressed pages */ - uint64_t compress_pages_prev; -} compression_counters; - -static CompressParam *comp_param; -static QemuThread *compress_threads; -/* comp_done_cond is used to wake up the migration thread when - * one of the compression threads has finished the compression. - * comp_done_lock is used to co-work with comp_done_cond. - */ -static QemuMutex comp_done_lock; -static QemuCond comp_done_cond; - -struct DecompressParam { - bool done; - bool quit; - QemuMutex mutex; - QemuCond cond; - void *des; - uint8_t *compbuf; - int len; - z_stream stream; -}; -typedef struct DecompressParam DecompressParam; - -static QEMUFile *decomp_file; -static DecompressParam *decomp_param; -static QemuThread *decompress_threads; -static QemuMutex decomp_done_lock; -static QemuCond decomp_done_cond; - -static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream, - RAMBlock *block, ram_addr_t offset, - uint8_t *source_buf); - -static void *do_data_compress(void *opaque) -{ - CompressParam *param = opaque; - RAMBlock *block; - ram_addr_t offset; - CompressResult result; - - qemu_mutex_lock(¶m->mutex); - while (!param->quit) { - if (param->trigger) { - block = param->block; - offset = param->offset; - param->trigger = false; - qemu_mutex_unlock(¶m->mutex); - - result = do_compress_ram_page(param->file, ¶m->stream, - block, offset, param->originbuf); - - qemu_mutex_lock(&comp_done_lock); - param->done = true; - param->result = result; - qemu_cond_signal(&comp_done_cond); - qemu_mutex_unlock(&comp_done_lock); - - qemu_mutex_lock(¶m->mutex); - } else { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - } - qemu_mutex_unlock(¶m->mutex); - - return NULL; -} - -void compress_threads_save_cleanup(void) -{ - int i, thread_count; - - if (!migrate_compress() || !comp_param) { - return; - } - - thread_count = migrate_compress_threads(); - for (i = 0; i < thread_count; i++) { - /* - * we use it as a indicator which shows if the thread is - * properly init'd or not - */ - if (!comp_param[i].file) { - break; - } - - qemu_mutex_lock(&comp_param[i].mutex); - comp_param[i].quit = true; - qemu_cond_signal(&comp_param[i].cond); - qemu_mutex_unlock(&comp_param[i].mutex); - - qemu_thread_join(compress_threads + i); - qemu_mutex_destroy(&comp_param[i].mutex); - qemu_cond_destroy(&comp_param[i].cond); - deflateEnd(&comp_param[i].stream); - g_free(comp_param[i].originbuf); - qemu_fclose(comp_param[i].file); - comp_param[i].file = NULL; - } - qemu_mutex_destroy(&comp_done_lock); - qemu_cond_destroy(&comp_done_cond); - g_free(compress_threads); - g_free(comp_param); - compress_threads = NULL; - comp_param = NULL; -} - -int compress_threads_save_setup(void) -{ - int i, thread_count; - - if (!migrate_compress()) { - return 0; - } - thread_count = migrate_compress_threads(); - compress_threads = g_new0(QemuThread, thread_count); - comp_param = g_new0(CompressParam, thread_count); - qemu_cond_init(&comp_done_cond); - qemu_mutex_init(&comp_done_lock); - for (i = 0; i < thread_count; i++) { - comp_param[i].originbuf = g_try_malloc(qemu_target_page_size()); - if (!comp_param[i].originbuf) { - goto exit; - } - - if (deflateInit(&comp_param[i].stream, - migrate_compress_level()) != Z_OK) { - g_free(comp_param[i].originbuf); - goto exit; - } - - /* comp_param[i].file is just used as a dummy buffer to save data, - * set its ops to empty. - */ - comp_param[i].file = qemu_file_new_output( - QIO_CHANNEL(qio_channel_null_new())); - comp_param[i].done = true; - comp_param[i].quit = false; - qemu_mutex_init(&comp_param[i].mutex); - qemu_cond_init(&comp_param[i].cond); - qemu_thread_create(compress_threads + i, "compress", - do_data_compress, comp_param + i, - QEMU_THREAD_JOINABLE); - } - return 0; - -exit: - compress_threads_save_cleanup(); - return -1; -} - -static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream, - RAMBlock *block, ram_addr_t offset, - uint8_t *source_buf) -{ - uint8_t *p = block->host + offset; - size_t page_size = qemu_target_page_size(); - int ret; - - assert(qemu_file_buffer_empty(f)); - - if (buffer_is_zero(p, page_size)) { - return RES_ZEROPAGE; - } - - /* - * copy it to a internal buffer to avoid it being modified by VM - * so that we can catch up the error during compression and - * decompression - */ - memcpy(source_buf, p, page_size); - ret = qemu_put_compression_data(f, stream, source_buf, page_size); - if (ret < 0) { - qemu_file_set_error(migrate_get_current()->to_dst_file, ret); - error_report("compressed data failed!"); - qemu_fflush(f); - return RES_NONE; - } - return RES_COMPRESS; -} - -static inline void compress_reset_result(CompressParam *param) -{ - param->result = RES_NONE; - param->block = NULL; - param->offset = 0; -} - -void compress_flush_data(void) -{ - int thread_count = migrate_compress_threads(); - - if (!migrate_compress()) { - return; - } - - qemu_mutex_lock(&comp_done_lock); - for (int i = 0; i < thread_count; i++) { - while (!comp_param[i].done) { - qemu_cond_wait(&comp_done_cond, &comp_done_lock); - } - } - qemu_mutex_unlock(&comp_done_lock); - - for (int i = 0; i < thread_count; i++) { - qemu_mutex_lock(&comp_param[i].mutex); - if (!comp_param[i].quit) { - CompressParam *param = &comp_param[i]; - compress_send_queued_data(param); - assert(qemu_file_buffer_empty(param->file)); - compress_reset_result(param); - } - qemu_mutex_unlock(&comp_param[i].mutex); - } -} - -static inline void set_compress_params(CompressParam *param, RAMBlock *block, - ram_addr_t offset) -{ - param->block = block; - param->offset = offset; - param->trigger = true; -} - -/* - * Return true when it compress a page - */ -bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, - int (send_queued_data(CompressParam *))) -{ - int thread_count; - bool wait = migrate_compress_wait_thread(); - - thread_count = migrate_compress_threads(); - qemu_mutex_lock(&comp_done_lock); - - while (true) { - for (int i = 0; i < thread_count; i++) { - if (comp_param[i].done) { - CompressParam *param = &comp_param[i]; - qemu_mutex_lock(¶m->mutex); - param->done = false; - send_queued_data(param); - assert(qemu_file_buffer_empty(param->file)); - compress_reset_result(param); - set_compress_params(param, block, offset); - - qemu_cond_signal(¶m->cond); - qemu_mutex_unlock(¶m->mutex); - qemu_mutex_unlock(&comp_done_lock); - return true; - } - } - if (!wait) { - qemu_mutex_unlock(&comp_done_lock); - compression_counters.busy++; - return false; - } - /* - * wait for a free thread if the user specifies - * 'compress-wait-thread', otherwise we will post the page out - * in the main thread as normal page. - */ - qemu_cond_wait(&comp_done_cond, &comp_done_lock); - } -} - -/* return the size after decompression, or negative value on error */ -static int -qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) -{ - int err; - - err = inflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = source_len; - stream->next_in = (uint8_t *)source; - stream->avail_out = dest_len; - stream->next_out = dest; - - err = inflate(stream, Z_NO_FLUSH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->total_out; -} - -static void *do_data_decompress(void *opaque) -{ - DecompressParam *param = opaque; - unsigned long pagesize; - uint8_t *des; - int len, ret; - - qemu_mutex_lock(¶m->mutex); - while (!param->quit) { - if (param->des) { - des = param->des; - len = param->len; - param->des = 0; - qemu_mutex_unlock(¶m->mutex); - - pagesize = qemu_target_page_size(); - - ret = qemu_uncompress_data(¶m->stream, des, pagesize, - param->compbuf, len); - if (ret < 0 && migrate_get_current()->decompress_error_check) { - error_report("decompress data failed"); - qemu_file_set_error(decomp_file, ret); - } - - qemu_mutex_lock(&decomp_done_lock); - param->done = true; - qemu_cond_signal(&decomp_done_cond); - qemu_mutex_unlock(&decomp_done_lock); - - qemu_mutex_lock(¶m->mutex); - } else { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - } - qemu_mutex_unlock(¶m->mutex); - - return NULL; -} - -int wait_for_decompress_done(void) -{ - if (!migrate_compress()) { - return 0; - } - - int thread_count = migrate_decompress_threads(); - qemu_mutex_lock(&decomp_done_lock); - for (int i = 0; i < thread_count; i++) { - while (!decomp_param[i].done) { - qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); - } - } - qemu_mutex_unlock(&decomp_done_lock); - return qemu_file_get_error(decomp_file); -} - -void compress_threads_load_cleanup(void) -{ - int i, thread_count; - - if (!migrate_compress()) { - return; - } - thread_count = migrate_decompress_threads(); - for (i = 0; i < thread_count; i++) { - /* - * we use it as a indicator which shows if the thread is - * properly init'd or not - */ - if (!decomp_param[i].compbuf) { - break; - } - - qemu_mutex_lock(&decomp_param[i].mutex); - decomp_param[i].quit = true; - qemu_cond_signal(&decomp_param[i].cond); - qemu_mutex_unlock(&decomp_param[i].mutex); - } - for (i = 0; i < thread_count; i++) { - if (!decomp_param[i].compbuf) { - break; - } - - qemu_thread_join(decompress_threads + i); - qemu_mutex_destroy(&decomp_param[i].mutex); - qemu_cond_destroy(&decomp_param[i].cond); - inflateEnd(&decomp_param[i].stream); - g_free(decomp_param[i].compbuf); - decomp_param[i].compbuf = NULL; - } - g_free(decompress_threads); - g_free(decomp_param); - decompress_threads = NULL; - decomp_param = NULL; - decomp_file = NULL; -} - -int compress_threads_load_setup(QEMUFile *f) -{ - int i, thread_count; - - if (!migrate_compress()) { - return 0; - } - - /* - * set compression_counters memory to zero for a new migration - */ - memset(&compression_counters, 0, sizeof(compression_counters)); - - thread_count = migrate_decompress_threads(); - decompress_threads = g_new0(QemuThread, thread_count); - decomp_param = g_new0(DecompressParam, thread_count); - qemu_mutex_init(&decomp_done_lock); - qemu_cond_init(&decomp_done_cond); - decomp_file = f; - for (i = 0; i < thread_count; i++) { - if (inflateInit(&decomp_param[i].stream) != Z_OK) { - goto exit; - } - - size_t compbuf_size = compressBound(qemu_target_page_size()); - decomp_param[i].compbuf = g_malloc0(compbuf_size); - qemu_mutex_init(&decomp_param[i].mutex); - qemu_cond_init(&decomp_param[i].cond); - decomp_param[i].done = true; - decomp_param[i].quit = false; - qemu_thread_create(decompress_threads + i, "decompress", - do_data_decompress, decomp_param + i, - QEMU_THREAD_JOINABLE); - } - return 0; -exit: - compress_threads_load_cleanup(); - return -1; -} - -void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len) -{ - int thread_count = migrate_decompress_threads(); - QEMU_LOCK_GUARD(&decomp_done_lock); - while (true) { - for (int i = 0; i < thread_count; i++) { - if (decomp_param[i].done) { - decomp_param[i].done = false; - qemu_mutex_lock(&decomp_param[i].mutex); - qemu_get_buffer(f, decomp_param[i].compbuf, len); - decomp_param[i].des = host; - decomp_param[i].len = len; - qemu_cond_signal(&decomp_param[i].cond); - qemu_mutex_unlock(&decomp_param[i].mutex); - return; - } - } - qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); - } -} - -void populate_compress(MigrationInfo *info) -{ - if (!migrate_compress()) { - return; - } - info->compression = g_malloc0(sizeof(*info->compression)); - info->compression->pages = compression_counters.pages; - info->compression->busy = compression_counters.busy; - info->compression->busy_rate = compression_counters.busy_rate; - info->compression->compressed_size = compression_counters.compressed_size; - info->compression->compression_rate = compression_counters.compression_rate; -} - -uint64_t compress_ram_pages(void) -{ - return compression_counters.pages; -} - -void update_compress_thread_counts(const CompressParam *param, int bytes_xmit) -{ - ram_transferred_add(bytes_xmit); - - if (param->result == RES_ZEROPAGE) { - stat64_add(&mig_stats.zero_pages, 1); - return; - } - - /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */ - compression_counters.compressed_size += bytes_xmit - 8; - compression_counters.pages++; -} - -void compress_update_rates(uint64_t page_count) -{ - if (!migrate_compress()) { - return; - } - compression_counters.busy_rate = (double)(compression_counters.busy - - compression_counters.compress_thread_busy_prev) / page_count; - compression_counters.compress_thread_busy_prev = - compression_counters.busy; - - double compressed_size = compression_counters.compressed_size - - compression_counters.compressed_size_prev; - if (compressed_size) { - double uncompressed_size = (compression_counters.pages - - compression_counters.compress_pages_prev) * - qemu_target_page_size(); - - /* Compression-Ratio = Uncompressed-size / Compressed-size */ - compression_counters.compression_rate = - uncompressed_size / compressed_size; - - compression_counters.compress_pages_prev = - compression_counters.pages; - compression_counters.compressed_size_prev = - compression_counters.compressed_size; - } -} diff --git a/migration/ram-compress.h b/migration/ram-compress.h deleted file mode 100644 index 0d89a2f55e..0000000000 --- a/migration/ram-compress.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2011-2015 Red Hat Inc - * - * Authors: - * Juan Quintela - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_MIGRATION_COMPRESS_H -#define QEMU_MIGRATION_COMPRESS_H - -#include "qemu-file.h" -#include "qapi/qapi-types-migration.h" - -enum CompressResult { - RES_NONE = 0, - RES_ZEROPAGE = 1, - RES_COMPRESS = 2 -}; -typedef enum CompressResult CompressResult; - -struct CompressParam { - bool done; - bool quit; - bool trigger; - CompressResult result; - QEMUFile *file; - QemuMutex mutex; - QemuCond cond; - RAMBlock *block; - ram_addr_t offset; - - /* internally used fields */ - z_stream stream; - uint8_t *originbuf; -}; -typedef struct CompressParam CompressParam; - -void compress_threads_save_cleanup(void); -int compress_threads_save_setup(void); - -bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, - int (send_queued_data(CompressParam *))); - -int wait_for_decompress_done(void); -void compress_threads_load_cleanup(void); -int compress_threads_load_setup(QEMUFile *f); -void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len); - -void populate_compress(MigrationInfo *info); -uint64_t compress_ram_pages(void); -void update_compress_thread_counts(const CompressParam *param, int bytes_xmit); -void compress_update_rates(uint64_t page_count); -int compress_send_queued_data(CompressParam *param); -void compress_flush_data(void); - -#endif diff --git a/migration/ram.c b/migration/ram.c index a5320aa889..ceea586b06 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -33,7 +33,6 @@ #include "qemu/madvise.h" #include "qemu/main-loop.h" #include "xbzrle.h" -#include "ram-compress.h" #include "ram.h" #include "migration.h" #include "migration-stats.h" @@ -77,9 +76,10 @@ * worked for pages that were filled with the same char. We switched * it to only search for the zero value. And to avoid confusion with * RAM_SAVE_FLAG_COMPRESS_PAGE just rename it. - */ -/* - * RAM_SAVE_FLAG_FULL was obsoleted in 2009, it can be reused now + * + * RAM_SAVE_FLAG_FULL was obsoleted in 2009. + * + * RAM_SAVE_FLAG_COMPRESS_PAGE (0x100) was removed in QEMU 9.1. */ #define RAM_SAVE_FLAG_FULL 0x01 #define RAM_SAVE_FLAG_ZERO 0x02 @@ -89,7 +89,6 @@ #define RAM_SAVE_FLAG_CONTINUE 0x20 #define RAM_SAVE_FLAG_XBZRLE 0x40 /* 0x80 is reserved in rdma.h for RAM_SAVE_FLAG_HOOK */ -#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 #define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 /* We can't use any flag that is bigger than 0x200 */ @@ -690,8 +689,7 @@ static int save_xbzrle_page(RAMState *rs, PageSearchStatus *pss, qemu_put_buffer(file, XBZRLE.encoded_buf, encoded_len); bytes_xbzrle += encoded_len + 1 + 2; /* - * Like compressed_size (please see update_compress_thread_counts), - * the xbzrle encoded bytes don't count the 8 byte header with + * The xbzrle encoded bytes don't count the 8 byte header with * RAM_SAVE_FLAG_CONTINUE. */ xbzrle_counters.bytes += bytes_xbzrle - 8; @@ -949,7 +947,7 @@ uint64_t ram_get_total_transferred_pages(void) { return stat64_get(&mig_stats.normal_pages) + stat64_get(&mig_stats.zero_pages) + - compress_ram_pages() + xbzrle_counters.pages; + xbzrle_counters.pages; } static void migration_update_rates(RAMState *rs, int64_t end_time) @@ -982,7 +980,6 @@ static void migration_update_rates(RAMState *rs, int64_t end_time) rs->xbzrle_pages_prev = xbzrle_counters.pages; rs->xbzrle_bytes_prev = xbzrle_counters.bytes; } - compress_update_rates(page_count); } /* @@ -1288,41 +1285,6 @@ static int ram_save_multifd_page(RAMBlock *block, ram_addr_t offset) return 1; } -int compress_send_queued_data(CompressParam *param) -{ - PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_PRECOPY]; - MigrationState *ms = migrate_get_current(); - QEMUFile *file = ms->to_dst_file; - int len = 0; - - RAMBlock *block = param->block; - ram_addr_t offset = param->offset; - - if (param->result == RES_NONE) { - return 0; - } - - assert(block == pss->last_sent_block); - - if (param->result == RES_ZEROPAGE) { - assert(qemu_file_buffer_empty(param->file)); - len += save_page_header(pss, file, block, offset | RAM_SAVE_FLAG_ZERO); - qemu_put_byte(file, 0); - len += 1; - ram_release_page(block->idstr, offset); - } else if (param->result == RES_COMPRESS) { - assert(!qemu_file_buffer_empty(param->file)); - len += save_page_header(pss, file, block, - offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - len += qemu_put_qemu_file(file, param->file); - } else { - abort(); - } - - update_compress_thread_counts(param, len); - - return len; -} #define PAGE_ALL_CLEAN 0 #define PAGE_TRY_AGAIN 1 @@ -1374,16 +1336,6 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) qemu_fflush(f); } } - /* - * If memory migration starts over, we will meet a dirtied page - * which may still exists in compression threads's ring, so we - * should flush the compressed data to make sure the new page - * is not overwritten by the old one in the destination. - * - * Also If xbzrle is on, stop using the data compression at this - * point. In theory, xbzrle can do better than compression. - */ - compress_flush_data(); /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); @@ -2034,37 +1986,6 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, return 0; } -/* - * try to compress the page before posting it out, return true if the page - * has been properly handled by compression, otherwise needs other - * paths to handle it - */ -static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, - ram_addr_t offset) -{ - if (!migrate_compress()) { - return false; - } - - /* - * When starting the process of a new block, the first page of - * the block should be sent out before other pages in the same - * block, and all the pages in last block should have been sent - * out, keeping this order is important, because the 'cont' flag - * is used to avoid resending the block name. - * - * We post the fist page as normal page as compression will take - * much CPU resource. - */ - if (pss->block != pss->last_sent_block) { - compress_flush_data(); - return false; - } - - return compress_page_with_multi_thread(pss->block, offset, - compress_send_queued_data); -} - /** * ram_save_target_page_legacy: save one target page * @@ -2082,10 +2003,6 @@ static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss) return res; } - if (save_compress_page(rs, pss, offset)) { - return 1; - } - if (save_zero_page(rs, pss, offset)) { return 1; } @@ -2470,7 +2387,6 @@ static void ram_save_cleanup(void *opaque) ram_bitmaps_destroy(); xbzrle_cleanup(); - compress_threads_save_cleanup(); ram_state_cleanup(rsp); g_free(migration_ops); migration_ops = NULL; @@ -3089,15 +3005,9 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) RAMBlock *block; int ret, max_hg_page_size; - if (compress_threads_save_setup()) { - error_setg(errp, "%s: failed to start compress threads", __func__); - return -1; - } - /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { if (ram_init_all(rsp, errp) != 0) { - compress_threads_save_cleanup(); return -1; } } @@ -3268,14 +3178,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) rs->target_page_count += pages; - /* - * During postcopy, it is necessary to make sure one whole host - * page is sent in one chunk. - */ - if (migrate_postcopy_ram()) { - compress_flush_data(); - } - /* * we want to check in the 1st loop, just in case it was the 1st * time and we had to sync the dirty bitmap. @@ -3374,8 +3276,6 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } qemu_mutex_unlock(&rs->bitmap_mutex); - compress_flush_data(); - ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); if (ret < 0) { qemu_file_set_error(f, ret); @@ -3789,7 +3689,6 @@ int ram_load_postcopy(QEMUFile *f, int channel) void *place_source = NULL; RAMBlock *block = NULL; uint8_t ch; - int len; addr = qemu_get_be64(f); @@ -3806,8 +3705,7 @@ int ram_load_postcopy(QEMUFile *f, int channel) addr &= TARGET_PAGE_MASK; trace_ram_load_postcopy_loop(channel, (uint64_t)addr, flags); - if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | - RAM_SAVE_FLAG_COMPRESS_PAGE)) { + if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE)) { block = ram_block_from_stream(mis, f, flags, channel); if (!block) { ret = -EINVAL; @@ -3902,16 +3800,6 @@ int ram_load_postcopy(QEMUFile *f, int channel) TARGET_PAGE_SIZE); } break; - case RAM_SAVE_FLAG_COMPRESS_PAGE: - tmp_page->all_zero = false; - len = qemu_get_be32(f); - if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { - error_report("Invalid compressed data length: %d", len); - ret = -EINVAL; - break; - } - decompress_data_with_multi_threads(f, page_buffer, len); - break; case RAM_SAVE_FLAG_MULTIFD_FLUSH: multifd_recv_sync_main(); break; @@ -3929,11 +3817,6 @@ int ram_load_postcopy(QEMUFile *f, int channel) break; } - /* Got the whole host page, wait for decompress before placing. */ - if (place_needed) { - ret |= wait_for_decompress_done(); - } - /* Detect for any possible file errors */ if (!ret && qemu_file_get_error(f)) { ret = qemu_file_get_error(f); @@ -4238,11 +4121,7 @@ static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes) static int ram_load_precopy(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); - int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0; - - if (!migrate_compress()) { - invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE; - } + int flags = 0, ret = 0, invalid_flags = 0, i = 0; if (migrate_mapped_ram()) { invalid_flags |= (RAM_SAVE_FLAG_HOOK | RAM_SAVE_FLAG_MULTIFD_FLUSH | @@ -4279,16 +4158,12 @@ static int ram_load_precopy(QEMUFile *f) if (flags & invalid_flags) { error_report("Unexpected RAM flags: %d", flags & invalid_flags); - if (flags & invalid_flags & RAM_SAVE_FLAG_COMPRESS_PAGE) { - error_report("Received an unexpected compressed page"); - } - ret = -EINVAL; break; } if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | - RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { + RAM_SAVE_FLAG_XBZRLE)) { RAMBlock *block = ram_block_from_stream(mis, f, flags, RAM_CHANNEL_PRECOPY); @@ -4357,16 +4232,6 @@ static int ram_load_precopy(QEMUFile *f) qemu_get_buffer(f, host, TARGET_PAGE_SIZE); break; - case RAM_SAVE_FLAG_COMPRESS_PAGE: - len = qemu_get_be32(f); - if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { - error_report("Invalid compressed data length: %d", len); - ret = -EINVAL; - break; - } - decompress_data_with_multi_threads(f, host, len); - break; - case RAM_SAVE_FLAG_XBZRLE: if (load_xbzrle(f, addr, host) < 0) { error_report("Failed to decompress XBZRLE page at " @@ -4408,7 +4273,6 @@ static int ram_load_precopy(QEMUFile *f) } } - ret |= wait_for_decompress_done(); return ret; } diff --git a/qapi/migration.json b/qapi/migration.json index f721039c6e..a351fd3714 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -229,10 +229,6 @@ # This is only present when the postcopy-blocktime migration # capability is enabled. (Since 3.0) # -# @compression: migration compression statistics, only returned if -# compression feature is on and status is 'active' or 'completed' -# (Since 3.1) -# # @socket-address: Only used for tcp, to know what the real port is # (Since 4.0) # @@ -257,13 +253,6 @@ # average memory load of the virtual CPU indirectly. Note that # zero means guest doesn't dirty memory. (Since 8.1) # -# Features: -# -# @deprecated: Member @compression is deprecated because it is -# unreliable and untested. It is recommended to use multifd -# migration, which offers an alternative compression -# implementation that is reliable and tested. -# # Since: 0.14 ## { 'struct': 'MigrationInfo', @@ -279,7 +268,6 @@ '*blocked-reasons': ['str'], '*postcopy-blocktime': 'uint32', '*postcopy-vcpu-blocktime': ['uint32'], - '*compression': { 'type': 'CompressionStats', 'features': [ 'deprecated' ] }, '*socket-address': ['SocketAddress'], '*dirty-limit-throttle-time-per-round': 'uint64', '*dirty-limit-ring-full-time': 'uint64'} } @@ -401,14 +389,6 @@ # capability on the source VM. The feature is disabled by default. # (since 1.6) # -# @compress: Use multiple compression threads to accelerate live -# migration. This feature can help to reduce the migration -# traffic, by sending compressed pages. Please note that if -# compress and xbzrle are both on, compress only takes effect in -# the ram bulk stage, after that, it will be disabled and only -# xbzrle takes effect, this can help to minimize migration -# traffic. The feature is disabled by default. (since 2.4) -# # @events: generate events for each migration state change (since 2.4) # # @auto-converge: If enabled, QEMU will automatically throttle down @@ -491,18 +471,12 @@ # # Features: # -# @deprecated: Member @compress is deprecated because it is unreliable -# and untested. It is recommended to use multifd migration, which -# offers an alternative compression implementation that is -# reliable and tested. -# # @unstable: Members @x-colo and @x-ignore-shared are experimental. # # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - { 'name': 'compress', 'features': [ 'deprecated' ] }, 'events', 'postcopy-ram', { 'name': 'x-colo', 'features': [ 'unstable' ] }, 'release-ram', @@ -562,7 +536,6 @@ # {"state": false, "capability": "rdma-pin-all"}, # {"state": false, "capability": "auto-converge"}, # {"state": false, "capability": "zero-blocks"}, -# {"state": false, "capability": "compress"}, # {"state": true, "capability": "events"}, # {"state": false, "capability": "postcopy-ram"}, # {"state": false, "capability": "x-colo"} @@ -710,27 +683,6 @@ # @announce-step: Increase in delay (in milliseconds) between # subsequent packets in the announcement (Since 4.0) # -# @compress-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 9, -# where 0 means no compression, 1 means the best compression -# speed, and 9 means best compression ratio which will consume -# more CPU. -# -# @compress-threads: Set compression thread count to be used in live -# migration, the compression thread count is an integer between 1 -# and 255. -# -# @compress-wait-thread: Controls behavior when all compression -# threads are currently busy. If true (default), wait for a free -# compression thread to become available; otherwise, send the page -# uncompressed. (Since 3.1) -# -# @decompress-threads: Set decompression thread count to be used in -# live migration, the decompression thread count is an integer -# between 1 and 255. Usually, decompression is at least 4 times as -# fast as compression, so set the decompress-threads to the number -# about 1/4 of compress-threads is adequate. -# # @throttle-trigger-threshold: The ratio of bytes_dirty_period and # bytes_xfer_period to trigger throttling. It is expressed as # percentage. The default value is 50. (Since 5.0) @@ -862,10 +814,6 @@ # # Features: # -# @deprecated: Members @compress-level, @compress-threads, -# @decompress-threads and @compress-wait-thread are deprecated -# because @compression is deprecated. -# # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. # @@ -874,10 +822,6 @@ { 'enum': 'MigrationParameter', 'data': ['announce-initial', 'announce-max', 'announce-rounds', 'announce-step', - { 'name': 'compress-level', 'features': [ 'deprecated' ] }, - { 'name': 'compress-threads', 'features': [ 'deprecated' ] }, - { 'name': 'decompress-threads', 'features': [ 'deprecated' ] }, - { 'name': 'compress-wait-thread', 'features': [ 'deprecated' ] }, 'throttle-trigger-threshold', 'cpu-throttle-initial', 'cpu-throttle-increment', 'cpu-throttle-tailslow', @@ -909,27 +853,6 @@ # @announce-step: Increase in delay (in milliseconds) between # subsequent packets in the announcement (Since 4.0) # -# @compress-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 9, -# where 0 means no compression, 1 means the best compression -# speed, and 9 means best compression ratio which will consume -# more CPU. -# -# @compress-threads: Set compression thread count to be used in live -# migration, the compression thread count is an integer between 1 -# and 255. -# -# @compress-wait-thread: Controls behavior when all compression -# threads are currently busy. If true (default), wait for a free -# compression thread to become available; otherwise, send the page -# uncompressed. (Since 3.1) -# -# @decompress-threads: Set decompression thread count to be used in -# live migration, the decompression thread count is an integer -# between 1 and 255. Usually, decompression is at least 4 times as -# fast as compression, so set the decompress-threads to the number -# about 1/4 of compress-threads is adequate. -# # @throttle-trigger-threshold: The ratio of bytes_dirty_period and # bytes_xfer_period to trigger throttling. It is expressed as # percentage. The default value is 50. (Since 5.0) @@ -1061,10 +984,6 @@ # # Features: # -# @deprecated: Members @compress-level, @compress-threads, -# @decompress-threads and @compress-wait-thread are deprecated -# because @compression is deprecated. -# # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. # @@ -1078,14 +997,6 @@ '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': { 'type': 'uint8', - 'features': [ 'deprecated' ] }, - '*compress-threads': { 'type': 'uint8', - 'features': [ 'deprecated' ] }, - '*compress-wait-thread': { 'type': 'bool', - 'features': [ 'deprecated' ] }, - '*decompress-threads': { 'type': 'uint8', - 'features': [ 'deprecated' ] }, '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -1145,17 +1056,6 @@ # @announce-step: Increase in delay (in milliseconds) between # subsequent packets in the announcement (Since 4.0) # -# @compress-level: compression level -# -# @compress-threads: compression thread count -# -# @compress-wait-thread: Controls behavior when all compression -# threads are currently busy. If true (default), wait for a free -# compression thread to become available; otherwise, send the page -# uncompressed. (Since 3.1) -# -# @decompress-threads: decompression thread count -# # @throttle-trigger-threshold: The ratio of bytes_dirty_period and # bytes_xfer_period to trigger throttling. It is expressed as # percentage. The default value is 50. (Since 5.0) @@ -1283,10 +1183,6 @@ # # Features: # -# @deprecated: Members @compress-level, @compress-threads, -# @decompress-threads and @compress-wait-thread are deprecated -# because @compression is deprecated. -# # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. # @@ -1297,14 +1193,6 @@ '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': { 'type': 'uint8', - 'features': [ 'deprecated' ] }, - '*compress-threads': { 'type': 'uint8', - 'features': [ 'deprecated' ] }, - '*compress-wait-thread': { 'type': 'bool', - 'features': [ 'deprecated' ] }, - '*decompress-threads': { 'type': 'uint8', - 'features': [ 'deprecated' ] }, '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 5d6d8cd634..7a1345f80f 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -427,38 +427,6 @@ static void migrate_set_parameter_str(QTestState *who, const char *parameter, migrate_check_parameter_str(who, parameter, value); } -static long long migrate_get_parameter_bool(QTestState *who, - const char *parameter) -{ - QDict *rsp; - int result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_bool(rsp, parameter); - qobject_unref(rsp); - return !!result; -} - -static void migrate_check_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - int result; - - result = migrate_get_parameter_bool(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %i } }", - parameter, value); - migrate_check_parameter_bool(who, parameter, value); -} - static void migrate_ensure_non_converge(QTestState *who) { /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ @@ -1240,36 +1208,6 @@ test_migrate_tls_x509_finish(QTestState *from, #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ -static void * -test_migrate_compress_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "compress-level", 1); - migrate_set_parameter_int(from, "compress-threads", 4); - migrate_set_parameter_bool(from, "compress-wait-thread", true); - migrate_set_parameter_int(to, "decompress-threads", 4); - - migrate_set_capability(from, "compress", true); - migrate_set_capability(to, "compress", true); - - return NULL; -} - -static void * -test_migrate_compress_nowait_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "compress-level", 9); - migrate_set_parameter_int(from, "compress-threads", 1); - migrate_set_parameter_bool(from, "compress-wait-thread", false); - migrate_set_parameter_int(to, "decompress-threads", 1); - - migrate_set_capability(from, "compress", true); - migrate_set_capability(to, "compress", true); - - return NULL; -} - static int migrate_postcopy_prepare(QTestState **from_ptr, QTestState **to_ptr, MigrateCommon *args) @@ -1370,15 +1308,6 @@ static void test_postcopy_suspend(void) test_postcopy_common(&args); } -static void test_postcopy_compress(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_compress_start - }; - - test_postcopy_common(&args); -} - static void test_postcopy_preempt(void) { MigrateCommon args = { @@ -1561,15 +1490,6 @@ static void test_postcopy_recovery(void) test_postcopy_recovery_common(&args); } -static void test_postcopy_recovery_compress(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_compress_start - }; - - test_postcopy_recovery_common(&args); -} - #ifndef _WIN32 static void test_postcopy_recovery_double_fail(void) { @@ -2027,48 +1947,6 @@ static void test_precopy_unix_xbzrle(void) test_precopy_common(&args); } -static void test_precopy_unix_compress(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_compress_start, - /* - * Test that no invalid thread state is left over from - * the previous iteration. - */ - .iterations = 2, - /* - * We make sure the compressor can always work well even if guest - * memory is changing. See commit 34ab9e9743 where we used to fix - * a bug when only trigger-able with guest memory changing. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_compress_nowait(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_compress_nowait_start, - /* - * Test that no invalid thread state is left over from - * the previous iteration. - */ - .iterations = 2, - /* Same reason for the wait version of precopy compress test */ - .live = true, - }; - - test_precopy_common(&args); -} - static void test_precopy_file(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -3597,12 +3475,6 @@ int main(int argc, char **argv) test_postcopy_preempt); migration_test_add("/migration/postcopy/preempt/recovery/plain", test_postcopy_preempt_recovery); - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/postcopy/compress/plain", - test_postcopy_compress); - migration_test_add("/migration/postcopy/recovery/compress/plain", - test_postcopy_recovery_compress); - } #ifndef _WIN32 migration_test_add("/migration/postcopy/recovery/double-failures", test_postcopy_recovery_double_fail); @@ -3623,17 +3495,6 @@ int main(int argc, char **argv) test_precopy_unix_plain); migration_test_add("/migration/precopy/unix/xbzrle", test_precopy_unix_xbzrle); - /* - * Compression fails from time to time. - * Put test here but don't enable it until everything is fixed. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/precopy/unix/compress/wait", - test_precopy_unix_compress); - migration_test_add("/migration/precopy/unix/compress/nowait", - test_precopy_unix_compress_nowait); - } - migration_test_add("/migration/precopy/file", test_precopy_file); migration_test_add("/migration/precopy/file/offset", From c55deb860ce54cc5ecebb5f170fea85969cb6aad Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 30 Apr 2024 11:27:37 -0300 Subject: [PATCH 0539/2884] migration: Deprecate fd: for file migration The fd: URI can currently trigger two different types of migration, a TCP migration using sockets and a file migration using a plain file. This is in conflict with the recently introduced (8.2) QMP migrate API that takes structured data as JSON-like format. We cannot keep the same backend for both types of migration because with the new API the code is more tightly coupled to the type of transport. This means a TCP migration must use the 'socket' transport and a file migration must use the 'file' transport. If we keep allowing fd: when using a file, this creates an issue when the user converts the old-style (fd:) to the new style ("transport": "socket") invocation because the file descriptor in question has previously been allowed to be either a plain file or a socket. To avoid creating too much confusion, we can simply deprecate the fd: + file usage, which is thought to be rarely used currently and instead establish a 1:1 correspondence between fd: URI and socket transport, and file: URI and file transport. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/about/deprecated.rst | 14 ++++++++++++++ migration/fd.c | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 64b8f838be..f5f111495b 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -473,3 +473,17 @@ both, older and future versions of QEMU. The ``blacklist`` config file option has been renamed to ``block-rpcs`` (to be in sync with the renaming of the corresponding command line option). + +Migration +--------- + +``fd:`` URI when used for file migration (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``fd:`` URI can currently provide a file descriptor that +references either a socket or a plain file. These are two different +types of migration. In order to reduce ambiguity, the ``fd:`` URI +usage of providing a file descriptor to a plain file has been +deprecated in favor of explicitly using the ``file:`` URI with the +file descriptor being passed as an ``fdset``. Refer to the ``add-fd`` +command documentation for details on the ``fdset`` usage. diff --git a/migration/fd.c b/migration/fd.c index 449adaa2de..aab5189eac 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -20,6 +20,8 @@ #include "file.h" #include "migration.h" #include "monitor/monitor.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" #include "io/channel-util.h" #include "trace.h" @@ -32,6 +34,11 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** return; } + if (!fd_is_socket(fd)) { + warn_report("fd: migration to a file is deprecated." + " Use file: instead."); + } + trace_migration_fd_outgoing(fd); ioc = qio_channel_new_fd(fd, errp); if (!ioc) { @@ -61,6 +68,11 @@ void fd_start_incoming_migration(const char *fdname, Error **errp) return; } + if (!fd_is_socket(fd)) { + warn_report("fd: migration to a file is deprecated." + " Use file: instead."); + } + trace_migration_fd_incoming(fd); ioc = qio_channel_new_fd(fd, errp); From db8cb7b6e73690233bc7c6abbb90979af3a18143 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 3 May 2024 10:41:26 -0400 Subject: [PATCH 0540/2884] hmp/migration: Fix "migrate" command's documentation Peter missed the Sphinx HMP document for the "resume/-r" flag in commit 7a4da28b26 ("qmp: hmp: add migrate "resume" option"). Add it. When at it, slightly cleanup the lines around: - Move "detach/-d" to a separate section rather than appending it at the end of the command description. Add a hint for how to query the migration results in detached mode. - Add "postcopy" keyword to "resume/-r" help messages, as it only applies to postcopy. Cc: Dr. David Alan Gilbert Cc: Fabiano Rosas Fixes: 7a4da28b26 ("qmp: hmp: add migrate "resume" option") Reported-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- hmp-commands.hx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index ebca2cdced..06746f0afc 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -912,14 +912,20 @@ ERST .args_type = "detach:-d,resume:-r,uri:s", .params = "[-d] [-r] uri", .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -r to resume a paused migration", + "\n\t\t\t -r to resume a paused postcopy migration", .cmd = hmp_migrate, }, SRST -``migrate [-d]`` *uri* - Migrate to *uri* (using -d to not wait for completion). +``migrate [-d] [-r]`` *uri* + Migrate the VM to *uri*. + + ``-d`` + Start the migration process, but do not wait for its completion. To + query an ongoing migration process, use "info migrate". + ``-r`` + Resume a paused postcopy migration. ERST { From f9cc8cfdf346cadc92db8fce32c8b5d7f1095163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Sep 2021 11:59:27 +0200 Subject: [PATCH 0541/2884] block/qcow2-bitmap: Replace g_memdup() by g_memdup2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538 The old API took the size of the memory to duplicate as a guint, whereas most memory functions take memory sizes as a gsize. This made it easy to accidentally pass a gsize to g_memdup(). For large values, that would lead to a silent truncation of the size from 64 to 32 bits, and result in a heap area being returned which is significantly smaller than what the caller expects. This can likely be exploited in various modules to cause a heap buffer overflow. Replace g_memdup() by the safer g_memdup2() wrapper. Trivially safe because the argument was directly from sizeof. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Blake Message-Id: <20210903174510.751630-6-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- block/qcow2-bitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 874ea56948..256ec99878 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1609,7 +1609,7 @@ bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, name); goto fail; } - tb = g_memdup(&bm->table, sizeof(bm->table)); + tb = g_memdup2(&bm->table, sizeof(bm->table)); bm->table.offset = 0; bm->table.size = 0; QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); From 40fed8c1d3a2bcef81c8de3f55d7e1abe1397347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Sep 2021 12:05:51 +0200 Subject: [PATCH 0542/2884] target/ppc: Replace g_memdup() by g_memdup2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538 The old API took the size of the memory to duplicate as a guint, whereas most memory functions take memory sizes as a gsize. This made it easy to accidentally pass a gsize to g_memdup(). For large values, that would lead to a silent truncation of the size from 64 to 32 bits, and result in a heap area being returned which is significantly smaller than what the caller expects. This can likely be exploited in various modules to cause a heap buffer overflow. Replace g_memdup() by the safer g_memdup2() wrapper. Trivially safe because the argument was directly from sizeof. Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-Id: <20210903174510.751630-27-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- target/ppc/mmu-hash64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 5a0d80feda..0966422a55 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1188,7 +1188,7 @@ void ppc_hash64_init(PowerPCCPU *cpu) return; } - cpu->hash64_opts = g_memdup(pcc->hash64_opts, sizeof(*cpu->hash64_opts)); + cpu->hash64_opts = g_memdup2(pcc->hash64_opts, sizeof(*cpu->hash64_opts)); } void ppc_hash64_finalize(PowerPCCPU *cpu) From 0572f01117c897bef5ece0b367bc6700d4fbd161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Sep 2021 12:01:20 +0200 Subject: [PATCH 0543/2884] hw/hppa/machine: Replace g_memdup() by g_memdup2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538 The old API took the size of the memory to duplicate as a guint, whereas most memory functions take memory sizes as a gsize. This made it easy to accidentally pass a gsize to g_memdup(). For large values, that would lead to a silent truncation of the size from 64 to 32 bits, and result in a heap area being returned which is significantly smaller than what the caller expects. This can likely be exploited in various modules to cause a heap buffer overflow. Replace g_memdup() by the safer g_memdup2() wrapper. Trivially safe because the argument was directly from sizeof. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210903174510.751630-12-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/hppa/machine.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 37ee6387e0..5d0a8739de 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -207,37 +207,37 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, val = cpu_to_le64(MIN_SEABIOS_HPPA_VERSION); fw_cfg_add_file(fw_cfg, "/etc/firmware-min-version", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&val, sizeof(val)), sizeof(val)); val = cpu_to_le64(HPPA_TLB_ENTRIES - btlb_entries); fw_cfg_add_file(fw_cfg, "/etc/cpu/tlb_entries", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&val, sizeof(val)), sizeof(val)); val = cpu_to_le64(btlb_entries); fw_cfg_add_file(fw_cfg, "/etc/cpu/btlb_entries", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&val, sizeof(val)), sizeof(val)); len = strlen(mc->name) + 1; fw_cfg_add_file(fw_cfg, "/etc/hppa/machine", - g_memdup(mc->name, len), len); + g_memdup2(mc->name, len), len); val = cpu_to_le64(soft_power_reg); fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&val, sizeof(val)), sizeof(val)); val = cpu_to_le64(CPU_HPA + 16); fw_cfg_add_file(fw_cfg, "/etc/hppa/rtc-addr", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&val, sizeof(val)), sizeof(val)); val = cpu_to_le64(CPU_HPA + 24); fw_cfg_add_file(fw_cfg, "/etc/hppa/DebugOutputPort", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&val, sizeof(val)), sizeof(val)); fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_config.order[0]); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); fw_cfg_add_file(fw_cfg, "/etc/qemu-version", - g_memdup(qemu_version, sizeof(qemu_version)), + g_memdup2(qemu_version, sizeof(qemu_version)), sizeof(qemu_version)); fw_cfg_add_extra_pci_roots(pci_bus, fw_cfg); From 09d98a241caf12e0de5ab738cfa5c911af97fbd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Sep 2021 12:05:50 +0200 Subject: [PATCH 0544/2884] hw/ppc/spapr_pci: Replace g_memdup() by g_memdup2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538 The old API took the size of the memory to duplicate as a guint, whereas most memory functions take memory sizes as a gsize. This made it easy to accidentally pass a gsize to g_memdup(). For large values, that would lead to a silent truncation of the size from 64 to 32 bits, and result in a heap area being returned which is significantly smaller than what the caller expects. This can likely be exploited in various modules to cause a heap buffer overflow. Replace g_memdup() by the safer g_memdup2() wrapper. Trivially safe because the argument was directly from sizeof. Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-Id: <20210903174510.751630-17-philmd@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/spapr_pci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 72cfba419a..7cf9904c35 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -2188,10 +2188,9 @@ static int spapr_pci_post_load(void *opaque, int version_id) int i; for (i = 0; i < sphb->msi_devs_num; ++i) { - key = g_memdup(&sphb->msi_devs[i].key, - sizeof(sphb->msi_devs[i].key)); - value = g_memdup(&sphb->msi_devs[i].value, - sizeof(sphb->msi_devs[i].value)); + key = g_memdup2(&sphb->msi_devs[i].key, sizeof(sphb->msi_devs[i].key)); + value = g_memdup2(&sphb->msi_devs[i].value, + sizeof(sphb->msi_devs[i].value)); g_hash_table_insert(sphb->msi, key, value); } g_free(sphb->msi_devs); From e6578f1f68a0e90789a841ada532c3e494c9a04c Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Wed, 23 Aug 2023 02:29:30 -0700 Subject: [PATCH 0545/2884] hw/remote/vfio-user: Fix config space access byte order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI config space is little-endian, so on a big-endian host we need to perform byte swaps for values as they are passed to and received from the generic PCI config space access machinery. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Reviewed-by: Jagannathan Raman Signed-off-by: Mattias Nissler Message-ID: <20240507094210.300566-6-mnissler@rivosinc.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/remote/vfio-user-obj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index d9b879e056..8dbafafb9e 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -281,7 +281,7 @@ static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf, while (bytes > 0) { len = (bytes > pci_access_width) ? pci_access_width : bytes; if (is_write) { - memcpy(&val, ptr, len); + val = ldn_le_p(ptr, len); pci_host_config_write_common(o->pci_dev, offset, pci_config_size(o->pci_dev), val, len); @@ -289,7 +289,7 @@ static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf, } else { val = pci_host_config_read_common(o->pci_dev, offset, pci_config_size(o->pci_dev), len); - memcpy(ptr, &val, len); + stn_le_p(ptr, len, val); trace_vfu_cfg_read(offset, val); } offset += len; From d5e268197aa2ba89bc0540717c72be2c69568b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 7 May 2024 14:12:46 +0200 Subject: [PATCH 0546/2884] system/physmem: Replace qemu_mutex_lock() calls with QEMU_LOCK_GUARD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify cpu_[un]register_map_client() and cpu_notify_map_clients() by replacing the pair of qemu_mutex_lock/qemu_mutex_unlock calls by the WITH_QEMU_LOCK_GUARD() macro. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Mattias Nissler Reviewed-by: Peter Xu Message-Id: <20240507123025.93391-2-philmd@linaro.org> --- system/physmem.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index d3a3d8a45c..26f42f4a6f 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -25,6 +25,7 @@ #include "qemu/cacheflush.h" #include "qemu/hbitmap.h" #include "qemu/madvise.h" +#include "qemu/lockable.h" #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" @@ -3086,7 +3087,7 @@ void cpu_register_map_client(QEMUBH *bh) { MapClient *client = g_malloc(sizeof(*client)); - qemu_mutex_lock(&map_client_list_lock); + QEMU_LOCK_GUARD(&map_client_list_lock); client->bh = bh; QLIST_INSERT_HEAD(&map_client_list, client, link); /* Write map_client_list before reading in_use. */ @@ -3094,7 +3095,6 @@ void cpu_register_map_client(QEMUBH *bh) if (!qatomic_read(&bounce.in_use)) { cpu_notify_map_clients_locked(); } - qemu_mutex_unlock(&map_client_list_lock); } void cpu_exec_init_all(void) @@ -3117,21 +3117,19 @@ void cpu_unregister_map_client(QEMUBH *bh) { MapClient *client; - qemu_mutex_lock(&map_client_list_lock); + QEMU_LOCK_GUARD(&map_client_list_lock); QLIST_FOREACH(client, &map_client_list, link) { if (client->bh == bh) { cpu_unregister_map_client_do(client); break; } } - qemu_mutex_unlock(&map_client_list_lock); } static void cpu_notify_map_clients(void) { - qemu_mutex_lock(&map_client_list_lock); + QEMU_LOCK_GUARD(&map_client_list_lock); cpu_notify_map_clients_locked(); - qemu_mutex_unlock(&map_client_list_lock); } static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, From 5c62719710bab66a98f68ebdba333e2240ed6668 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Thu, 7 Sep 2023 06:04:23 -0700 Subject: [PATCH 0547/2884] system/physmem: Propagate AddressSpace to MapClient helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate AddressSpace handler to following helpers: - register_map_client() - unregister_map_client() - notify_map_clients[_locked]() Rename them using 'address_space_' prefix instead of 'cpu_'. The AddressSpace argument will be used in the next commit. Reviewed-by: Peter Xu Tested-by: Jonathan Cameron Signed-off-by: Mattias Nissler Message-ID: <20240507094210.300566-2-mnissler@rivosinc.com> [PMD: Split patch, part 1/2] Signed-off-by: Philippe Mathieu-Daudé --- include/exec/cpu-common.h | 2 -- include/exec/memory.h | 26 ++++++++++++++++++++++++-- system/dma-helpers.c | 4 ++-- system/physmem.c | 24 ++++++++++++------------ 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 8bc397e251..815342d043 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -147,8 +147,6 @@ void *cpu_physical_memory_map(hwaddr addr, bool is_write); void cpu_physical_memory_unmap(void *buffer, hwaddr len, bool is_write, hwaddr access_len); -void cpu_register_map_client(QEMUBH *bh); -void cpu_unregister_map_client(QEMUBH *bh); bool cpu_physical_memory_is_io(hwaddr phys_addr); diff --git a/include/exec/memory.h b/include/exec/memory.h index dadb5cd65a..e1e0c5a3de 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2946,8 +2946,8 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, hwaddr len, * May return %NULL and set *@plen to zero(0), if resources needed to perform * the mapping are exhausted. * Use only for reads OR writes - not for read-modify-write operations. - * Use cpu_register_map_client() to know when retrying the map operation is - * likely to succeed. + * Use address_space_register_map_client() to know when retrying the map + * operation is likely to succeed. * * @as: #AddressSpace to be accessed * @addr: address within that address space @@ -2972,6 +2972,28 @@ void *address_space_map(AddressSpace *as, hwaddr addr, void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, bool is_write, hwaddr access_len); +/* + * address_space_register_map_client: Register a callback to invoke when + * resources for address_space_map() are available again. + * + * address_space_map may fail when there are not enough resources available, + * such as when bounce buffer memory would exceed the limit. The callback can + * be used to retry the address_space_map operation. Note that the callback + * gets automatically removed after firing. + * + * @as: #AddressSpace to be accessed + * @bh: callback to invoke when address_space_map() retry is appropriate + */ +void address_space_register_map_client(AddressSpace *as, QEMUBH *bh); + +/* + * address_space_unregister_map_client: Unregister a callback that has + * previously been registered and not fired yet. + * + * @as: #AddressSpace to be accessed + * @bh: callback to unregister + */ +void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh); /* Internal functions, part of the implementation of address_space_read. */ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, diff --git a/system/dma-helpers.c b/system/dma-helpers.c index 9b221cf94e..74013308f5 100644 --- a/system/dma-helpers.c +++ b/system/dma-helpers.c @@ -169,7 +169,7 @@ static void dma_blk_cb(void *opaque, int ret) if (dbs->iov.size == 0) { trace_dma_map_wait(dbs); dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs); - cpu_register_map_client(dbs->bh); + address_space_register_map_client(dbs->sg->as, dbs->bh); return; } @@ -197,7 +197,7 @@ static void dma_aio_cancel(BlockAIOCB *acb) } if (dbs->bh) { - cpu_unregister_map_client(dbs->bh); + address_space_unregister_map_client(dbs->sg->as, dbs->bh); qemu_bh_delete(dbs->bh); dbs->bh = NULL; } diff --git a/system/physmem.c b/system/physmem.c index 26f42f4a6f..b76b3d2184 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3066,24 +3066,24 @@ QemuMutex map_client_list_lock; static QLIST_HEAD(, MapClient) map_client_list = QLIST_HEAD_INITIALIZER(map_client_list); -static void cpu_unregister_map_client_do(MapClient *client) +static void address_space_unregister_map_client_do(MapClient *client) { QLIST_REMOVE(client, link); g_free(client); } -static void cpu_notify_map_clients_locked(void) +static void address_space_notify_map_clients_locked(AddressSpace *as) { MapClient *client; while (!QLIST_EMPTY(&map_client_list)) { client = QLIST_FIRST(&map_client_list); qemu_bh_schedule(client->bh); - cpu_unregister_map_client_do(client); + address_space_unregister_map_client_do(client); } } -void cpu_register_map_client(QEMUBH *bh) +void address_space_register_map_client(AddressSpace *as, QEMUBH *bh) { MapClient *client = g_malloc(sizeof(*client)); @@ -3093,7 +3093,7 @@ void cpu_register_map_client(QEMUBH *bh) /* Write map_client_list before reading in_use. */ smp_mb(); if (!qatomic_read(&bounce.in_use)) { - cpu_notify_map_clients_locked(); + address_space_notify_map_clients_locked(as); } } @@ -3113,23 +3113,23 @@ void cpu_exec_init_all(void) qemu_mutex_init(&map_client_list_lock); } -void cpu_unregister_map_client(QEMUBH *bh) +void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh) { MapClient *client; QEMU_LOCK_GUARD(&map_client_list_lock); QLIST_FOREACH(client, &map_client_list, link) { if (client->bh == bh) { - cpu_unregister_map_client_do(client); + address_space_unregister_map_client_do(client); break; } } } -static void cpu_notify_map_clients(void) +static void address_space_notify_map_clients(AddressSpace *as) { QEMU_LOCK_GUARD(&map_client_list_lock); - cpu_notify_map_clients_locked(); + address_space_notify_map_clients_locked(as); } static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, @@ -3196,8 +3196,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, * May map a subset of the requested range, given by and returned in *plen. * May return NULL if resources needed to perform the mapping are exhausted. * Use only for reads OR writes - not for read-modify-write operations. - * Use cpu_register_map_client() to know when retrying the map operation is - * likely to succeed. + * Use address_space_register_map_client() to know when retrying the map + * operation is likely to succeed. */ void *address_space_map(AddressSpace *as, hwaddr addr, @@ -3280,7 +3280,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, memory_region_unref(bounce.mr); /* Clear in_use before reading map_client_list. */ qatomic_set_mb(&bounce.in_use, false); - cpu_notify_map_clients(); + address_space_notify_map_clients(as); } void *cpu_physical_memory_map(hwaddr addr, From 69e78f1b3484e429274352a464a94fa1d78be339 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Thu, 7 Sep 2023 06:04:23 -0700 Subject: [PATCH 0548/2884] system/physmem: Per-AddressSpace bounce buffering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using a single global bounce buffer, give each AddressSpace its own bounce buffer. The MapClient callback mechanism moves to AddressSpace accordingly. This is in preparation for generalizing bounce buffer handling further to allow multiple bounce buffers, with a total allocation limit configured per AddressSpace. Reviewed-by: Peter Xu Tested-by: Jonathan Cameron Signed-off-by: Mattias Nissler Message-ID: <20240507094210.300566-2-mnissler@rivosinc.com> Reviewed-by: Philippe Mathieu-Daudé [PMD: Split patch, part 2/2] Signed-off-by: Philippe Mathieu-Daudé --- include/exec/memory.h | 19 +++++++++++ system/memory.c | 7 +++++ system/physmem.c | 73 ++++++++++++++++--------------------------- 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index e1e0c5a3de..d417d7f363 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1112,6 +1112,19 @@ struct MemoryListener { QTAILQ_ENTRY(MemoryListener) link_as; }; +typedef struct AddressSpaceMapClient { + QEMUBH *bh; + QLIST_ENTRY(AddressSpaceMapClient) link; +} AddressSpaceMapClient; + +typedef struct { + MemoryRegion *mr; + void *buffer; + hwaddr addr; + hwaddr len; + bool in_use; +} BounceBuffer; + /** * struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects */ @@ -1129,6 +1142,12 @@ struct AddressSpace { struct MemoryRegionIoeventfd *ioeventfds; QTAILQ_HEAD(, MemoryListener) listeners; QTAILQ_ENTRY(AddressSpace) address_spaces_link; + + /* Bounce buffer to use for this address space. */ + BounceBuffer bounce; + /* List of callbacks to invoke when buffers free up */ + QemuMutex map_client_list_lock; + QLIST_HEAD(, AddressSpaceMapClient) map_client_list; }; typedef struct AddressSpaceDispatch AddressSpaceDispatch; diff --git a/system/memory.c b/system/memory.c index 49f1cb2c38..642a449f8c 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3174,6 +3174,9 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) as->ioeventfds = NULL; QTAILQ_INIT(&as->listeners); QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link); + as->bounce.in_use = false; + qemu_mutex_init(&as->map_client_list_lock); + QLIST_INIT(&as->map_client_list); as->name = g_strdup(name ? name : "anonymous"); address_space_update_topology(as); address_space_update_ioeventfds(as); @@ -3181,6 +3184,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) static void do_address_space_destroy(AddressSpace *as) { + assert(!qatomic_read(&as->bounce.in_use)); + assert(QLIST_EMPTY(&as->map_client_list)); + qemu_mutex_destroy(&as->map_client_list_lock); + assert(QTAILQ_EMPTY(&as->listeners)); flatview_unref(as->current_map); diff --git a/system/physmem.c b/system/physmem.c index b76b3d2184..342b7a8fd4 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3047,26 +3047,8 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len) NULL, len, FLUSH_CACHE); } -typedef struct { - MemoryRegion *mr; - void *buffer; - hwaddr addr; - hwaddr len; - bool in_use; -} BounceBuffer; - -static BounceBuffer bounce; - -typedef struct MapClient { - QEMUBH *bh; - QLIST_ENTRY(MapClient) link; -} MapClient; - -QemuMutex map_client_list_lock; -static QLIST_HEAD(, MapClient) map_client_list - = QLIST_HEAD_INITIALIZER(map_client_list); - -static void address_space_unregister_map_client_do(MapClient *client) +static void +address_space_unregister_map_client_do(AddressSpaceMapClient *client) { QLIST_REMOVE(client, link); g_free(client); @@ -3074,10 +3056,10 @@ static void address_space_unregister_map_client_do(MapClient *client) static void address_space_notify_map_clients_locked(AddressSpace *as) { - MapClient *client; + AddressSpaceMapClient *client; - while (!QLIST_EMPTY(&map_client_list)) { - client = QLIST_FIRST(&map_client_list); + while (!QLIST_EMPTY(&as->map_client_list)) { + client = QLIST_FIRST(&as->map_client_list); qemu_bh_schedule(client->bh); address_space_unregister_map_client_do(client); } @@ -3085,14 +3067,14 @@ static void address_space_notify_map_clients_locked(AddressSpace *as) void address_space_register_map_client(AddressSpace *as, QEMUBH *bh) { - MapClient *client = g_malloc(sizeof(*client)); + AddressSpaceMapClient *client = g_malloc(sizeof(*client)); - QEMU_LOCK_GUARD(&map_client_list_lock); + QEMU_LOCK_GUARD(&as->map_client_list_lock); client->bh = bh; - QLIST_INSERT_HEAD(&map_client_list, client, link); + QLIST_INSERT_HEAD(&as->map_client_list, client, link); /* Write map_client_list before reading in_use. */ smp_mb(); - if (!qatomic_read(&bounce.in_use)) { + if (!qatomic_read(&as->bounce.in_use)) { address_space_notify_map_clients_locked(as); } } @@ -3110,15 +3092,14 @@ void cpu_exec_init_all(void) finalize_target_page_bits(); io_mem_init(); memory_map_init(); - qemu_mutex_init(&map_client_list_lock); } void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh) { - MapClient *client; + AddressSpaceMapClient *client; - QEMU_LOCK_GUARD(&map_client_list_lock); - QLIST_FOREACH(client, &map_client_list, link) { + QEMU_LOCK_GUARD(&as->map_client_list_lock); + QLIST_FOREACH(client, &as->map_client_list, link) { if (client->bh == bh) { address_space_unregister_map_client_do(client); break; @@ -3128,7 +3109,7 @@ void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh) static void address_space_notify_map_clients(AddressSpace *as) { - QEMU_LOCK_GUARD(&map_client_list_lock); + QEMU_LOCK_GUARD(&as->map_client_list_lock); address_space_notify_map_clients_locked(as); } @@ -3220,25 +3201,25 @@ void *address_space_map(AddressSpace *as, mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { - if (qatomic_xchg(&bounce.in_use, true)) { + if (qatomic_xchg(&as->bounce.in_use, true)) { *plen = 0; return NULL; } /* Avoid unbounded allocations */ l = MIN(l, TARGET_PAGE_SIZE); - bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l); - bounce.addr = addr; - bounce.len = l; + as->bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l); + as->bounce.addr = addr; + as->bounce.len = l; memory_region_ref(mr); - bounce.mr = mr; + as->bounce.mr = mr; if (!is_write) { flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED, - bounce.buffer, l); + as->bounce.buffer, l); } *plen = l; - return bounce.buffer; + return as->bounce.buffer; } @@ -3256,7 +3237,7 @@ void *address_space_map(AddressSpace *as, void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, bool is_write, hwaddr access_len) { - if (buffer != bounce.buffer) { + if (buffer != as->bounce.buffer) { MemoryRegion *mr; ram_addr_t addr1; @@ -3272,14 +3253,14 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, return; } if (is_write) { - address_space_write(as, bounce.addr, MEMTXATTRS_UNSPECIFIED, - bounce.buffer, access_len); + address_space_write(as, as->bounce.addr, MEMTXATTRS_UNSPECIFIED, + as->bounce.buffer, access_len); } - qemu_vfree(bounce.buffer); - bounce.buffer = NULL; - memory_region_unref(bounce.mr); + qemu_vfree(as->bounce.buffer); + as->bounce.buffer = NULL; + memory_region_unref(as->bounce.mr); /* Clear in_use before reading map_client_list. */ - qatomic_set_mb(&bounce.in_use, false); + qatomic_set_mb(&as->bounce.in_use, false); address_space_notify_map_clients(as); } From 64436c5c1739abf58ef3b768c20f312355c370fc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 25 Apr 2024 20:43:13 +0200 Subject: [PATCH 0549/2884] hw/i386/pc: Allow to compile without CONFIG_FDC_ISA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The q35 machine can work without FDC. But to be able to also link a QEMU binary that does not include the FDC code, we have to make it possible to disable the spots that call into the FDC code. Signed-off-by: Thomas Huth Acked-by: Philippe Mathieu-Daudé Message-ID: <20240425184315.553329-2-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 46235466d7..505ea750f4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -440,16 +440,19 @@ static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy) { - int val, nb, i; + int val, nb; FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE, FLOPPY_DRIVE_TYPE_NONE }; +#ifdef CONFIG_FDC_ISA /* floppy type */ if (floppy) { - for (i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) { fd_type[i] = isa_fdc_get_drive_type(floppy, i); } } +#endif + val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | cmos_get_fd_drive_type(fd_type[1]); mc146818rtc_set_cmos_data(rtc_state, 0x10, val); @@ -1133,7 +1136,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, int i; DriveInfo *fd[MAX_FD]; qemu_irq *a20_line; - ISADevice *fdc, *i8042, *port92, *vmmouse; + ISADevice *i8042, *port92, *vmmouse; serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); @@ -1143,11 +1146,13 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, create_fdctrl |= !!fd[i]; } if (create_fdctrl) { - fdc = isa_new(TYPE_ISA_FDC); +#ifdef CONFIG_FDC_ISA + ISADevice *fdc = isa_new(TYPE_ISA_FDC); if (fdc) { isa_realize_and_unref(fdc, isa_bus, &error_fatal); isa_fdc_init_drives(fdc, fd); } +#endif } if (!create_i8042) { From 77af05946e450a804b55b4a10b0b1fbd4f838fa4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 25 Apr 2024 20:43:14 +0200 Subject: [PATCH 0550/2884] hw/i386/Kconfig: Allow to compile Q35 without FDC_ISA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The q35 machine can be used without floppy disk controller (FDC), but due to our current Kconfig setup, the FDC code is still always included in the binary. To fix this, the "PC" config option should only imply the "FDC_ISA" instead of always selecting it. The i440fx and the isa-pc machine currently always instantiate the FDC, so we have to add the select statements now there instead. Signed-off-by: Thomas Huth Acked-by: Philippe Mathieu-Daudé Message-ID: <20240425184315.553329-3-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 4362164962..58ca8f246d 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -32,7 +32,7 @@ config PC imply VGA_PCI imply VIRTIO_VGA imply NVDIMM - select FDC_ISA + imply FDC_ISA select I8259 select I8254 select PCKBD @@ -72,6 +72,7 @@ config I440FX imply VMPORT imply VMMOUSE select ACPI_PIIX4 + select FDC_ISA select PC_PCI select PC_ACPI select PCI_I440FX @@ -87,6 +88,7 @@ config ISAPC depends on I386 imply VGA_ISA select ISA_BUS + select FDC_ISA select PC select IDE_ISA # FIXME: it is in the same file as i440fx, and does not compile From 8793d601f38b417d63820456582e1e22e51c2d34 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 25 Apr 2024 20:43:15 +0200 Subject: [PATCH 0551/2884] hw/i386: Add the possibility to use i440fx and isapc without FDC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i440fx and the isapc machines can be used in binaries without FDC, too. We just have to make sure that they don't try to instantiate the FDC when it is not available. Signed-off-by: Thomas Huth Acked-by: Philippe Mathieu-Daudé Message-ID: <20240425184315.553329-4-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/Kconfig | 2 -- hw/i386/pc_piix.c | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 58ca8f246d..40b1e44580 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -72,7 +72,6 @@ config I440FX imply VMPORT imply VMMOUSE select ACPI_PIIX4 - select FDC_ISA select PC_PCI select PC_ACPI select PCI_I440FX @@ -88,7 +87,6 @@ config ISAPC depends on I386 imply VGA_ISA select ISA_BUS - select FDC_ISA select PC select IDE_ISA # FIXME: it is in the same file as i440fx, and does not compile diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8850c49c66..99efb3c45c 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -317,8 +317,8 @@ static void pc_init1(MachineState *machine, const char *pci_type) } /* init basic PC hardware */ - pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, true, - 0x4); + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 0x4); pc_nic_init(pcmc, isa_bus, pcms->pcibus); @@ -501,6 +501,7 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; m->default_nic = "e1000"; + m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); @@ -931,6 +932,7 @@ static void isapc_machine_options(MachineClass *m) pcmc->has_reserved_memory = false; m->default_nic = "ne2k_isa"; m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); } From 014dbdac8798799d081abc9dff3e4876ca54f49e Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 30 Apr 2024 17:06:38 +0200 Subject: [PATCH 0552/2884] hw/i386/x86: Eliminate two if statements in x86_bios_rom_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given that memory_region_set_readonly() is a no-op when the readonlyness is already as requested it is possible to simplify the pattern if (condition) { foo(true); } to foo(condition); which is shorter and allows to see the invariant of the code more easily. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430150643.111976-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/x86.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 3d5b51e92d..2a4f3ee285 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1163,9 +1163,7 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, load_image_size(filename, ptr, bios_size); x86_firmware_configure(ptr, bios_size); } else { - if (!isapc_ram_fw) { - memory_region_set_readonly(bios, true); - } + memory_region_set_readonly(bios, !isapc_ram_fw); ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); if (ret != 0) { goto bios_error; @@ -1182,9 +1180,7 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, 0x100000 - isa_bios_size, isa_bios, 1); - if (!isapc_ram_fw) { - memory_region_set_readonly(isa_bios, true); - } + memory_region_set_readonly(isa_bios, !isapc_ram_fw); /* map all the bios at the top of memory */ memory_region_add_subregion(rom_memory, From 848351840148f8c3b53ddf6210194506547d3ffd Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 30 Apr 2024 17:06:39 +0200 Subject: [PATCH 0553/2884] hw/i386: Have x86_bios_rom_init() take X86MachineState rather than MachineState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function creates and leaks two MemoryRegion objects regarding the BIOS which will be moved into X86MachineState in the next steps to avoid the leakage. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240430150643.111976-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/microvm.c | 2 +- hw/i386/pc_sysfw.c | 4 ++-- hw/i386/x86.c | 4 ++-- include/hw/i386/x86.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 61a772dfe6..fec63cacfa 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -278,7 +278,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) default_firmware = x86_machine_is_acpi_enabled(x86ms) ? MICROVM_BIOS_FILENAME : MICROVM_QBOOT_FILENAME; - x86_bios_rom_init(MACHINE(mms), default_firmware, get_system_memory(), true); + x86_bios_rom_init(x86ms, default_firmware, get_system_memory(), true); } static void microvm_memory_init(MicrovmMachineState *mms) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 87b5bf59d6..59c7a81692 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -205,7 +205,7 @@ void pc_system_firmware_init(PCMachineState *pcms, BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; if (!pcmc->pci_enabled) { - x86_bios_rom_init(MACHINE(pcms), "bios.bin", rom_memory, true); + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); return; } @@ -226,7 +226,7 @@ void pc_system_firmware_init(PCMachineState *pcms, if (!pflash_blk[0]) { /* Machine property pflash0 not set, use ROM mode */ - x86_bios_rom_init(MACHINE(pcms), "bios.bin", rom_memory, false); + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false); } else { if (kvm_enabled() && !kvm_readonly_mem_enabled()) { /* diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 2a4f3ee285..6d3c72f124 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1128,7 +1128,7 @@ void x86_load_linux(X86MachineState *x86ms, nb_option_roms++; } -void x86_bios_rom_init(MachineState *ms, const char *default_firmware, +void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, MemoryRegion *rom_memory, bool isapc_ram_fw) { const char *bios_name; @@ -1138,7 +1138,7 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, ssize_t ret; /* BIOS load */ - bios_name = ms->firmware ?: default_firmware; + bios_name = MACHINE(x86ms)->firmware ?: default_firmware; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { bios_size = get_image_size(filename); diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 4dc30dcb4d..cb07618d19 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -116,7 +116,7 @@ void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void x86_bios_rom_init(MachineState *ms, const char *default_firmware, +void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, MemoryRegion *rom_memory, bool isapc_ram_fw); void x86_load_linux(X86MachineState *x86ms, From 32d3ee87a17fc91e981a23dba94855bff89f5920 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 May 2024 19:55:04 +0200 Subject: [PATCH 0554/2884] hw/i386/x86: Don't leak "isa-bios" memory regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the leaking in x86_bios_rom_init() and pc_isa_bios_init() by adding an "isa_bios" attribute to X86MachineState. Suggested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Message-ID: <20240508175507.22270-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_sysfw.c | 7 +++---- hw/i386/x86.c | 9 ++++----- include/hw/i386/x86.h | 7 +++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 59c7a81692..82d37cb376 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -40,11 +40,10 @@ #define FLASH_SECTOR_SIZE 4096 -static void pc_isa_bios_init(MemoryRegion *rom_memory, +static void pc_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *rom_memory, MemoryRegion *flash_mem) { int isa_bios_size; - MemoryRegion *isa_bios; uint64_t flash_size; void *flash_ptr, *isa_bios_ptr; @@ -52,7 +51,6 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = MIN(flash_size, 128 * KiB); - isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, &error_fatal); memory_region_add_subregion_overlap(rom_memory, @@ -136,6 +134,7 @@ void pc_system_flash_cleanup_unused(PCMachineState *pcms) static void pc_system_flash_map(PCMachineState *pcms, MemoryRegion *rom_memory) { + X86MachineState *x86ms = X86_MACHINE(pcms); hwaddr total_size = 0; int i; BlockBackend *blk; @@ -185,7 +184,7 @@ static void pc_system_flash_map(PCMachineState *pcms, if (i == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); - pc_isa_bios_init(rom_memory, flash_mem); + pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem); /* Encrypt the pflash boot ROM */ if (sev_enabled()) { diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 6d3c72f124..457e8a34a5 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1133,7 +1133,7 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, { const char *bios_name; char *filename; - MemoryRegion *bios, *isa_bios; + MemoryRegion *bios; int bios_size, isa_bios_size; ssize_t ret; @@ -1173,14 +1173,13 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = MIN(bios_size, 128 * KiB); - isa_bios = g_malloc(sizeof(*isa_bios)); - memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, + memory_region_init_alias(&x86ms->isa_bios, NULL, "isa-bios", bios, bios_size - isa_bios_size, isa_bios_size); memory_region_add_subregion_overlap(rom_memory, 0x100000 - isa_bios_size, - isa_bios, + &x86ms->isa_bios, 1); - memory_region_set_readonly(isa_bios, !isapc_ram_fw); + memory_region_set_readonly(&x86ms->isa_bios, !isapc_ram_fw); /* map all the bios at the top of memory */ memory_region_add_subregion(rom_memory, diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index cb07618d19..a07de79167 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -18,6 +18,7 @@ #define HW_I386_X86_H #include "exec/hwaddr.h" +#include "exec/memory.h" #include "hw/boards.h" #include "hw/intc/ioapic.h" @@ -52,6 +53,12 @@ struct X86MachineState { GMappedFile *initrd_mapped_file; HotplugHandler *acpi_dev; + /* + * Map the upper 128 KiB of the BIOS just underneath the 1 MiB address + * boundary. + */ + MemoryRegion isa_bios; + /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; From 865d95321ffc8d9941e33000b10140550f094556 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 May 2024 19:55:05 +0200 Subject: [PATCH 0555/2884] hw/i386/x86: Don't leak "pc.bios" memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the leaking in x86_bios_rom_init() by adding a "bios" attribute to X86MachineState. Note that it is only used in the -bios case. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Message-ID: <20240508175507.22270-5-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/x86.c | 13 ++++++------- include/hw/i386/x86.h | 6 ++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 457e8a34a5..29167de97d 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1133,7 +1133,6 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, { const char *bios_name; char *filename; - MemoryRegion *bios; int bios_size, isa_bios_size; ssize_t ret; @@ -1149,8 +1148,8 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, (bios_size % 65536) != 0) { goto bios_error; } - bios = g_malloc(sizeof(*bios)); - memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); + memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, + &error_fatal); if (sev_enabled()) { /* * The concept of a "reset" simply doesn't exist for @@ -1159,11 +1158,11 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, * the firmware as rom to properly re-initialize on reset. * Just go for a straight file load instead. */ - void *ptr = memory_region_get_ram_ptr(bios); + void *ptr = memory_region_get_ram_ptr(&x86ms->bios); load_image_size(filename, ptr, bios_size); x86_firmware_configure(ptr, bios_size); } else { - memory_region_set_readonly(bios, !isapc_ram_fw); + memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); if (ret != 0) { goto bios_error; @@ -1173,7 +1172,7 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = MIN(bios_size, 128 * KiB); - memory_region_init_alias(&x86ms->isa_bios, NULL, "isa-bios", bios, + memory_region_init_alias(&x86ms->isa_bios, NULL, "isa-bios", &x86ms->bios, bios_size - isa_bios_size, isa_bios_size); memory_region_add_subregion_overlap(rom_memory, 0x100000 - isa_bios_size, @@ -1184,7 +1183,7 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, /* map all the bios at the top of memory */ memory_region_add_subregion(rom_memory, (uint32_t)(-bios_size), - bios); + &x86ms->bios); return; bios_error: diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index a07de79167..55c6809ae0 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -53,6 +53,12 @@ struct X86MachineState { GMappedFile *initrd_mapped_file; HotplugHandler *acpi_dev; + /* + * Map the whole BIOS just underneath the 4 GiB address boundary. Only used + * in the ROM (-bios) case. + */ + MemoryRegion bios; + /* * Map the upper 128 KiB of the BIOS just underneath the 1 MiB address * boundary. From 5c5ffec12c30d2017cbdee6798f54d8fad3f9656 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 May 2024 19:55:06 +0200 Subject: [PATCH 0556/2884] hw/i386/x86: Extract x86_isa_bios_init() from x86_bios_rom_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is inspired by pc_isa_bios_init() and should eventually replace it. Using x86_isa_bios_init() rather than pc_isa_bios_init() fixes pflash commands to work in the isa-bios region. While at it convert the magic number 0x100000 (== 1MiB) to increase readability. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Bernhard Beschow Message-ID: <20240508175507.22270-6-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/x86.c | 25 ++++++++++++++++--------- include/hw/i386/x86.h | 2 ++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 29167de97d..c61f4ebfa6 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1128,12 +1128,25 @@ void x86_load_linux(X86MachineState *x86ms, nb_option_roms++; } +void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, + MemoryRegion *bios, bool read_only) +{ + uint64_t bios_size = memory_region_size(bios); + uint64_t isa_bios_size = MIN(bios_size, 128 * KiB); + + memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size, + isa_bios, 1); + memory_region_set_readonly(isa_bios, read_only); +} + void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, MemoryRegion *rom_memory, bool isapc_ram_fw) { const char *bios_name; char *filename; - int bios_size, isa_bios_size; + int bios_size; ssize_t ret; /* BIOS load */ @@ -1171,14 +1184,8 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, g_free(filename); /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = MIN(bios_size, 128 * KiB); - memory_region_init_alias(&x86ms->isa_bios, NULL, "isa-bios", &x86ms->bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(rom_memory, - 0x100000 - isa_bios_size, - &x86ms->isa_bios, - 1); - memory_region_set_readonly(&x86ms->isa_bios, !isapc_ram_fw); + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, + !isapc_ram_fw); /* map all the bios at the top of memory */ memory_region_add_subregion(rom_memory, diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 55c6809ae0..d7b7d3f3ce 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -129,6 +129,8 @@ void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); +void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, + MemoryRegion *bios, bool read_only); void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, MemoryRegion *rom_memory, bool isapc_ram_fw); From f94b1871aa6ca64114dadea877ee6518809ddb9d Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 5 May 2024 18:14:41 +0100 Subject: [PATCH 0557/2884] hw/usb/dev-network: Remove unused struct 'rndis_config_parameter' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as I can tell it was never used. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240505171444.333302-5-dave@treblig.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/dev-network.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 2c33e36cad..d00d68b21d 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -475,14 +475,6 @@ struct rndis_packet_msg_type { le32 Reserved; }; -struct rndis_config_parameter { - le32 ParameterNameOffset; - le32 ParameterNameLength; - le32 ParameterType; - le32 ParameterValueOffset; - le32 ParameterValueLength; -}; - /* implementation specific */ enum rndis_state { From 1f3cabd3409c4eef0174f58211c0ced196f11446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 7 May 2024 20:55:39 +0200 Subject: [PATCH 0558/2884] hw/gpio: Handle clock migration in STM32L4x5 gpios MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STM32L4x5 GPIO wasn't migrating its clock. Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240507185854.34572-3-ines.varhol@telecom-paris.fr> Signed-off-by: Philippe Mathieu-Daudé --- hw/gpio/stm32l4x5_gpio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c index 71bf5fddb2..30d8d6cba4 100644 --- a/hw/gpio/stm32l4x5_gpio.c +++ b/hw/gpio/stm32l4x5_gpio.c @@ -20,6 +20,7 @@ #include "qemu/log.h" #include "hw/gpio/stm32l4x5_gpio.h" #include "hw/irq.h" +#include "hw/clock.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "qapi/visitor.h" @@ -426,8 +427,8 @@ static void stm32l4x5_gpio_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_stm32l4x5_gpio = { .name = TYPE_STM32L4X5_GPIO, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]){ VMSTATE_UINT32(moder, Stm32l4x5GpioState), VMSTATE_UINT32(otyper, Stm32l4x5GpioState), @@ -441,6 +442,7 @@ static const VMStateDescription vmstate_stm32l4x5_gpio = { VMSTATE_UINT32(ascr, Stm32l4x5GpioState), VMSTATE_UINT16(disconnected_pins, Stm32l4x5GpioState), VMSTATE_UINT16(pins_connected_high, Stm32l4x5GpioState), + VMSTATE_CLOCK(clk, Stm32l4x5GpioState), VMSTATE_END_OF_LIST() } }; From ed95bdd1e52a0a5f4f8e07468f6c1aa766efc4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 May 2024 14:33:32 +0200 Subject: [PATCH 0559/2884] hw/ppc: Deprecate 'ref405ep' machine and 405 CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ref405ep' machine and PPC 405 CPU have no known users, firmware images are not available, OpenWRT dropped support in 2019, U-Boot in 2017, Linux also is dropping support in 2024. It is time to let go of this ancient hardware and focus on newer CPUs and platforms. Signed-off-by: Cédric Le Goater Acked-by: Nicholas Piggin Message-ID: <20240507123332.641708-1-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/about/deprecated.rst | 8 ++++++++ hw/ppc/ppc405_boards.c | 1 + 2 files changed, 9 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 03f8b1b655..e22acb17f2 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -258,6 +258,14 @@ dropping the ``cheetah`` OMAP1 board, because we don't have any test images for it and don't know of anybody who does; the ``sx1`` and ``sx1-v1`` OMAP1 machines remain supported for now. +PPC 405 ``ref405ep`` machine (since 9.1) +'''''''''''''''''''''''''''''''''''''''' + +The ``ref405ep`` machine and PPC 405 CPU have no known users, firmware +images are not available, OpenWRT dropped support in 2019, U-Boot in +2017, Linux also is dropping support in 2024. It is time to let go of +this ancient hardware and focus on newer CPUs and platforms. + Backend options --------------- diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 4092ebc1ab..c44e7ed162 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -350,6 +350,7 @@ static void ppc405_machine_class_init(ObjectClass *oc, void *data) mc->init = ppc405_init; mc->default_ram_size = 128 * MiB; mc->default_ram_id = "ppc405.ram"; + mc->deprecation_reason = "machine is old and unmaintained"; } static const TypeInfo ppc405_machine_type = { From 72674db080bcdf2fd66b2a538379ee6000f5b113 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 16:51:35 +0200 Subject: [PATCH 0560/2884] hw/loongarch: move memory map to boot.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure that it can be used even if virt.c is not included in the build, as is the case for --without-default-devices. Signed-off-by: Paolo Bonzini Acked-by: Richard Henderson Message-ID: <20240507145135.270803-1-pbonzini@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- .gitlab-ci.d/buildtest.yml | 2 +- hw/loongarch/boot.c | 3 +++ hw/loongarch/virt.c | 3 --- include/hw/loongarch/boot.h | 10 ++++++++++ include/hw/loongarch/virt.h | 10 ---------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e9402a68a7..bab6194564 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, loongarch64, s390x, sh4, sh4eb, x86_64 +# does not build without boards: i386, s390x, sh4, sh4eb, x86_64 build-without-defaults: extends: .native_build_job_template needs: diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 7d1630b2e7..03f6301a77 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -15,6 +15,9 @@ #include "sysemu/reset.h" #include "sysemu/qtest.h" +struct memmap_entry *memmap_table; +unsigned memmap_entries; + ram_addr_t initrd_offset; uint64_t initrd_size; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index c0999878df..504e1fb349 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -516,9 +516,6 @@ static void virt_powerdown_req(Notifier *notifier, void *opaque) acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); } -struct memmap_entry *memmap_table; -unsigned memmap_entries; - static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) { /* Ensure there are no duplicate entries. */ diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index 4ebcc89dcf..b3b870df1f 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -104,6 +104,16 @@ struct loongarch_boot_info { uint64_t a0, a1, a2; }; +extern struct memmap_entry *memmap_table; +extern unsigned memmap_entries; + +struct memmap_entry { + uint64_t address; + uint64_t length; + uint32_t type; + uint32_t reserved; +}; + void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info); #endif /* HW_LOONGARCH_BOOT_H */ diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 4e14bf6060..fdbd2b146f 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -37,16 +37,6 @@ #define FDT_BASE 0x100000 -extern struct memmap_entry *memmap_table; -extern unsigned memmap_entries; - -struct memmap_entry { - uint64_t address; - uint64_t length; - uint32_t type; - uint32_t reserved; -}; - struct LoongArchMachineState { /*< private >*/ MachineState parent_obj; From 54c52ec719fb8c83bbde54cb87b58688ab27c166 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 7 May 2024 10:22:39 +0800 Subject: [PATCH 0561/2884] hw/loongarch/virt: Fix memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The char pointer 'ramName' point to a block of memory, but never free it. Use 'g_autofree' to automatically free it. Resolves: Coverity CID 1544773 Fixes: 0cf1478d6 ("hw/loongarch: Add numa support") Signed-off-by: Song Gao Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240507022239.3113987-1-gaosong@loongson.cn> Signed-off-by: Philippe Mathieu-Daudé --- hw/loongarch/virt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 504e1fb349..69924a8734 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -884,7 +884,6 @@ static void loongarch_init(MachineState *machine) const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); CPUState *cpu; - char *ramName = NULL; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -943,7 +942,7 @@ static void loongarch_init(MachineState *machine) for (i = 1; i < nb_numa_nodes; i++) { MemoryRegion *nodemem = g_new(MemoryRegion, 1); - ramName = g_strdup_printf("loongarch.node%d.ram", i); + g_autofree char *ramName = g_strdup_printf("loongarch.node%d.ram", i); memory_region_init_alias(nodemem, NULL, ramName, machine->ram, offset, numa_info[i].node_mem); memory_region_add_subregion(address_space_mem, phyAddr, nodemem); From df0d93c1e20db0c2b683a823a8e95208f5dc09ff Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 8 May 2024 11:11:06 +0800 Subject: [PATCH 0562/2884] hw/loongarch: Rename LOONGARCH_MACHINE with LOONGARCH_VIRT_MACHINE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On LoongArch system, there is only virt machine type now, name LOONGARCH_MACHINE is confused, rename it with LOONGARCH_VIRT_MACHINE. Machine name about Other real hw boards can be added in future. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240508031110.2507477-2-maobibo@loongson.cn> Signed-off-by: Philippe Mathieu-Daudé --- hw/loongarch/acpi-build.c | 8 ++++---- hw/loongarch/boot.c | 2 +- hw/loongarch/virt.c | 19 +++++++++---------- include/hw/loongarch/virt.h | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index e5ab1080af..c7150cc0c4 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -167,7 +167,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) int i, arch_id, node_id; uint64_t mem_len, mem_base; int nb_numa_nodes = machine->numa_state->num_nodes; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(lams); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, @@ -279,7 +279,7 @@ static void build_la_ged_aml(Aml *dsdt, MachineState *machine) { uint32_t event; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); build_ged_aml(dsdt, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(lams->acpi_ged), @@ -391,7 +391,7 @@ static void build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) { Aml *dsdt, *scope, *pkg; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id, .oem_table_id = lams->oem_table_id }; @@ -421,7 +421,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) static void acpi_build(AcpiBuildTables *tables, MachineState *machine) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); GArray *table_offsets; AcpiFadtData fadt_data; unsigned facs, rsdt, dsdt; diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 03f6301a77..e37512729d 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -319,7 +319,7 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(ms); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(ms); int i; /* register reset function */ diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 69924a8734..d7de80baf8 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -877,7 +877,7 @@ static void loongarch_init(MachineState *machine) ram_addr_t ram_size = machine->ram_size; uint64_t highram_size = 0, phyAddr = 0; MemoryRegion *address_space_mem = get_system_memory(); - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); int nb_numa_nodes = machine->numa_state->num_nodes; NodeInfo *numa_info = machine->numa_state->nodes; int i; @@ -1028,7 +1028,7 @@ bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(obj); OnOffAuto acpi = lams->acpi; visit_type_OnOffAuto(v, name, &acpi, errp); @@ -1037,14 +1037,14 @@ static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(obj); visit_type_OnOffAuto(v, name, &lams->acpi, errp); } static void loongarch_machine_initfn(Object *obj) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(obj); lams->acpi = ON_OFF_AUTO_AUTO; lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); @@ -1076,7 +1076,7 @@ static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); /* the acpi ged is always exist */ hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev, @@ -1094,7 +1094,7 @@ static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, static void virt_mem_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp); pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams)); @@ -1112,7 +1112,7 @@ static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, static void virt_mem_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); pc_dimm_plug(PC_DIMM(dev), MACHINE(lams)); hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged), @@ -1122,7 +1122,7 @@ static void virt_mem_plug(HotplugHandler *hotplug_dev, static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); MachineClass *mc = MACHINE_GET_CLASS(lams); if (device_is_dynamic_sysbus(mc, dev)) { @@ -1204,7 +1204,6 @@ static void loongarch_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - mc->desc = "Loongson-3A5000 LS7A1000 machine"; mc->init = loongarch_init; mc->default_ram_size = 1 * GiB; mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); @@ -1241,7 +1240,7 @@ static void loongarch_class_init(ObjectClass *oc, void *data) static const TypeInfo loongarch_machine_types[] = { { - .name = TYPE_LOONGARCH_MACHINE, + .name = TYPE_LOONGARCH_VIRT_MACHINE, .parent = TYPE_MACHINE, .instance_size = sizeof(LoongArchMachineState), .class_init = loongarch_class_init, diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index fdbd2b146f..5b1416d7bc 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -63,8 +63,8 @@ struct LoongArchMachineState { struct loongarch_boot_info bootinfo; }; -#define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE) +#define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_VIRT_MACHINE) bool loongarch_is_acpi_enabled(LoongArchMachineState *lams); void loongarch_acpi_setup(LoongArchMachineState *lams); #endif From d804ad98f51cc6ae795ff9c9890db66bae513214 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 8 May 2024 11:11:07 +0800 Subject: [PATCH 0563/2884] hw/loongarch: Rename LoongArchMachineState with LoongArchVirtMachineState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename LoongArchMachineState with LoongArchVirtMachineState, and change variable name LoongArchMachineState *lams with LoongArchVirtMachineState *lvms. Rename function specific for virtmachine loongarch_xxx() with virt_xxx(). However some common functions keep unchanged such as loongarch_acpi_setup()/loongarch_load_kernel(), since there functions can be used for real hw boards. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240508031110.2507477-3-maobibo@loongson.cn> Signed-off-by: Philippe Mathieu-Daudé --- hw/loongarch/acpi-build.c | 89 +++++----- hw/loongarch/boot.c | 10 +- hw/loongarch/fw_cfg.c | 2 +- hw/loongarch/fw_cfg.h | 2 +- hw/loongarch/virt.c | 340 ++++++++++++++++++------------------ include/hw/loongarch/virt.h | 7 +- 6 files changed, 226 insertions(+), 224 deletions(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index c7150cc0c4..5ef010d4da 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -105,14 +105,15 @@ build_facs(GArray *table_data) /* build MADT */ static void -build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) +build_madt(GArray *table_data, BIOSLinker *linker, + LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); int i, arch_id; - AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; + AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, + .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); @@ -167,11 +168,11 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) int i, arch_id, node_id; uint64_t mem_len, mem_base; int nb_numa_nodes = machine->numa_state->num_nodes; - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); - MachineClass *mc = MACHINE_GET_CLASS(lams); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(lvms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); - AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; + AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id, + .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); build_append_int_noprefix(table_data, 1, 4); /* Reserved */ @@ -279,13 +280,13 @@ static void build_la_ged_aml(Aml *dsdt, MachineState *machine) { uint32_t event; - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); build_ged_aml(dsdt, "\\_SB."GED_DEVICE, - HOTPLUG_HANDLER(lams->acpi_ged), + HOTPLUG_HANDLER(lvms->acpi_ged), VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, VIRT_GED_EVT_ADDR); - event = object_property_get_uint(OBJECT(lams->acpi_ged), + event = object_property_get_uint(OBJECT(lvms->acpi_ged), "ged-event", &error_abort); if (event & ACPI_GED_MEM_HOTPLUG_EVT) { build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL, @@ -295,7 +296,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) acpi_dsdt_add_power_button(dsdt); } -static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) +static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms) { struct GPEXConfig cfg = { .mmio64.base = VIRT_PCI_MEM_BASE, @@ -305,13 +306,13 @@ static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) .ecam.base = VIRT_PCI_CFG_BASE, .ecam.size = VIRT_PCI_CFG_SIZE, .irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS, - .bus = lams->pci_bus, + .bus = lvms->pci_bus, }; acpi_dsdt_add_gpex(scope, &cfg); } -static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) +static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms) { Aml *dev, *crs; MemoryRegion *flash_mem; @@ -322,11 +323,11 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) hwaddr flash1_base; hwaddr flash1_size; - flash_mem = pflash_cfi01_get_memory(lams->flash[0]); + flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); flash0_base = flash_mem->addr; flash0_size = memory_region_size(flash_mem); - flash_mem = pflash_cfi01_get_memory(lams->flash[1]); + flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); flash1_base = flash_mem->addr; flash1_size = memory_region_size(flash_mem); @@ -352,7 +353,7 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) } #ifdef CONFIG_TPM -static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms) +static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms) { PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; @@ -391,18 +392,18 @@ static void build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) { Aml *dsdt, *scope, *pkg; - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); - AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id, + .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); build_uart_device_aml(dsdt); - build_pci_device_aml(dsdt, lams); + build_pci_device_aml(dsdt, lvms); build_la_ged_aml(dsdt, machine); - build_flash_aml(dsdt, lams); + build_flash_aml(dsdt, lvms); #ifdef CONFIG_TPM - acpi_dsdt_add_tpm(dsdt, lams); + acpi_dsdt_add_tpm(dsdt, lvms); #endif /* System State Package */ scope = aml_scope("\\"); @@ -421,7 +422,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) static void acpi_build(AcpiBuildTables *tables, MachineState *machine) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); GArray *table_offsets; AcpiFadtData fadt_data; unsigned facs, rsdt, dsdt; @@ -455,14 +456,14 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) fadt_data.dsdt_tbl_offset = &dsdt; fadt_data.xdsdt_tbl_offset = &dsdt; build_fadt(tables_blob, tables->linker, &fadt_data, - lams->oem_id, lams->oem_table_id); + lvms->oem_id, lvms->oem_table_id); acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, lams); + build_madt(tables_blob, tables->linker, lvms); acpi_add_table(table_offsets, tables_blob); build_pptt(tables_blob, tables->linker, machine, - lams->oem_id, lams->oem_table_id); + lvms->oem_id, lvms->oem_table_id); acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); @@ -470,13 +471,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) if (machine->numa_state->num_nodes) { if (machine->numa_state->have_numa_distance) { acpi_add_table(table_offsets, tables_blob); - build_slit(tables_blob, tables->linker, machine, lams->oem_id, - lams->oem_table_id); + build_slit(tables_blob, tables->linker, machine, lvms->oem_id, + lvms->oem_table_id); } if (machine->numa_state->hmat_enabled) { acpi_add_table(table_offsets, tables_blob); build_hmat(tables_blob, tables->linker, machine->numa_state, - lams->oem_id, lams->oem_table_id); + lvms->oem_id, lvms->oem_table_id); } } @@ -486,8 +487,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) .base = cpu_to_le64(VIRT_PCI_CFG_BASE), .size = cpu_to_le64(VIRT_PCI_CFG_SIZE), }; - build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id, - lams->oem_table_id); + build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, + lvms->oem_table_id); } #ifdef CONFIG_TPM @@ -495,8 +496,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { acpi_add_table(table_offsets, tables_blob); build_tpm2(tables_blob, tables->linker, - tables->tcpalog, lams->oem_id, - lams->oem_table_id); + tables->tcpalog, lvms->oem_id, + lvms->oem_table_id); } #endif /* Add tables supplied by user (if any) */ @@ -510,13 +511,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; build_rsdt(tables_blob, tables->linker, table_offsets, - lams->oem_id, lams->oem_table_id); + lvms->oem_id, lvms->oem_table_id); /* RSDP is in FSEG memory, so allocate it separately */ { AcpiRsdpData rsdp_data = { .revision = 0, - .oem_id = lams->oem_id, + .oem_id = lvms->oem_id, .xsdt_tbl_offset = NULL, .rsdt_tbl_offset = &rsdt, }; @@ -593,17 +594,25 @@ static const VMStateDescription vmstate_acpi_build = { }, }; -void loongarch_acpi_setup(LoongArchMachineState *lams) +static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +void loongarch_acpi_setup(LoongArchVirtMachineState *lvms) { AcpiBuildTables tables; AcpiBuildState *build_state; - if (!lams->fw_cfg) { + if (!lvms->fw_cfg) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; } - if (!loongarch_is_acpi_enabled(lams)) { + if (!loongarch_is_acpi_enabled(lvms)) { ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); return; } @@ -611,7 +620,7 @@ void loongarch_acpi_setup(LoongArchMachineState *lams) build_state = g_malloc0(sizeof *build_state); acpi_build_tables_init(&tables); - acpi_build(&tables, MACHINE(lams)); + acpi_build(&tables, MACHINE(lvms)); /* Now expose it all to Guest */ build_state->table_mr = acpi_add_rom_blob(acpi_build_update, diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index e37512729d..b8e1aa18d5 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -259,10 +259,10 @@ static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, } } -static void loongarch_firmware_boot(LoongArchMachineState *lams, +static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms, struct loongarch_boot_info *info) { - fw_cfg_add_kernel_info(info, lams->fw_cfg); + fw_cfg_add_kernel_info(info, lvms->fw_cfg); } static void init_boot_rom(struct loongarch_boot_info *info, void *p) @@ -319,7 +319,7 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(ms); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); int i; /* register reset function */ @@ -331,8 +331,8 @@ void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) info->kernel_cmdline = ms->kernel_cmdline; info->initrd_filename = ms->initrd_filename; - if (lams->bios_loaded) { - loongarch_firmware_boot(lams, info); + if (lvms->bios_loaded) { + loongarch_firmware_boot(lvms, info); } else { loongarch_direct_kernel_boot(info); } diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c index f15a17416c..35aeb2decb 100644 --- a/hw/loongarch/fw_cfg.c +++ b/hw/loongarch/fw_cfg.c @@ -17,7 +17,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } -FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) +FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) { FWCfgState *fw_cfg; int max_cpus = ms->smp.max_cpus; diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h index 7c0de4db4a..27ee68286e 100644 --- a/hw/loongarch/fw_cfg.h +++ b/hw/loongarch/fw_cfg.h @@ -11,5 +11,5 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" -FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); +FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); #endif diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index d7de80baf8..51e0aca39b 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -46,7 +46,7 @@ #include "hw/block/flash.h" #include "qemu/error-report.h" -static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, +static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms, const char *name, const char *alias_prop_name) { @@ -61,16 +61,16 @@ static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, qdev_prop_set_uint16(dev, "id2", 0x00); qdev_prop_set_uint16(dev, "id3", 0x00); qdev_prop_set_string(dev, "name", name); - object_property_add_child(OBJECT(lams), name, OBJECT(dev)); - object_property_add_alias(OBJECT(lams), alias_prop_name, + object_property_add_child(OBJECT(lvms), name, OBJECT(dev)); + object_property_add_alias(OBJECT(lvms), alias_prop_name, OBJECT(dev), "drive"); return PFLASH_CFI01(dev); } -static void virt_flash_create(LoongArchMachineState *lams) +static void virt_flash_create(LoongArchVirtMachineState *lvms) { - lams->flash[0] = virt_flash_create1(lams, "virt.flash0", "pflash0"); - lams->flash[1] = virt_flash_create1(lams, "virt.flash1", "pflash1"); + lvms->flash[0] = virt_flash_create1(lvms, "virt.flash0", "pflash0"); + lvms->flash[1] = virt_flash_create1(lvms, "virt.flash1", "pflash1"); } static void virt_flash_map1(PFlashCFI01 *flash, @@ -96,20 +96,20 @@ static void virt_flash_map1(PFlashCFI01 *flash, sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); } -static void virt_flash_map(LoongArchMachineState *lams, +static void virt_flash_map(LoongArchVirtMachineState *lvms, MemoryRegion *sysmem) { - PFlashCFI01 *flash0 = lams->flash[0]; - PFlashCFI01 *flash1 = lams->flash[1]; + PFlashCFI01 *flash0 = lvms->flash[0]; + PFlashCFI01 *flash1 = lvms->flash[1]; virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem); virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); } -static void fdt_add_cpuic_node(LoongArchMachineState *lams, +static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms, uint32_t *cpuintc_phandle) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); char *nodename; *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt); @@ -123,11 +123,11 @@ static void fdt_add_cpuic_node(LoongArchMachineState *lams, g_free(nodename); } -static void fdt_add_eiointc_node(LoongArchMachineState *lams, +static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms, uint32_t *cpuintc_phandle, uint32_t *eiointc_phandle) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); char *nodename; hwaddr extioi_base = APIC_BASE; hwaddr extioi_size = EXTIOI_SIZE; @@ -148,11 +148,11 @@ static void fdt_add_eiointc_node(LoongArchMachineState *lams, g_free(nodename); } -static void fdt_add_pch_pic_node(LoongArchMachineState *lams, +static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms, uint32_t *eiointc_phandle, uint32_t *pch_pic_phandle) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); char *nodename; hwaddr pch_pic_base = VIRT_PCH_REG_BASE; hwaddr pch_pic_size = VIRT_PCH_REG_SIZE; @@ -173,11 +173,11 @@ static void fdt_add_pch_pic_node(LoongArchMachineState *lams, g_free(nodename); } -static void fdt_add_pch_msi_node(LoongArchMachineState *lams, +static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms, uint32_t *eiointc_phandle, uint32_t *pch_msi_phandle) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); char *nodename; hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW; hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE; @@ -201,9 +201,9 @@ static void fdt_add_pch_msi_node(LoongArchMachineState *lams, g_free(nodename); } -static void fdt_add_flash_node(LoongArchMachineState *lams) +static void fdt_add_flash_node(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); char *nodename; MemoryRegion *flash_mem; @@ -213,11 +213,11 @@ static void fdt_add_flash_node(LoongArchMachineState *lams) hwaddr flash1_base; hwaddr flash1_size; - flash_mem = pflash_cfi01_get_memory(lams->flash[0]); + flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); flash0_base = flash_mem->addr; flash0_size = memory_region_size(flash_mem); - flash_mem = pflash_cfi01_get_memory(lams->flash[1]); + flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); flash1_base = flash_mem->addr; flash1_size = memory_region_size(flash_mem); @@ -231,13 +231,13 @@ static void fdt_add_flash_node(LoongArchMachineState *lams) g_free(nodename); } -static void fdt_add_rtc_node(LoongArchMachineState *lams, +static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, uint32_t *pch_pic_phandle) { char *nodename; hwaddr base = VIRT_RTC_REG_BASE; hwaddr size = VIRT_RTC_LEN; - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); nodename = g_strdup_printf("/rtc@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -251,13 +251,13 @@ static void fdt_add_rtc_node(LoongArchMachineState *lams, g_free(nodename); } -static void fdt_add_uart_node(LoongArchMachineState *lams, +static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, uint32_t *pch_pic_phandle) { char *nodename; hwaddr base = VIRT_UART_BASE; hwaddr size = VIRT_UART_SIZE; - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); nodename = g_strdup_printf("/serial@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -272,11 +272,11 @@ static void fdt_add_uart_node(LoongArchMachineState *lams, g_free(nodename); } -static void create_fdt(LoongArchMachineState *lams) +static void create_fdt(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); - ms->fdt = create_device_tree(&lams->fdt_size); + ms->fdt = create_device_tree(&lvms->fdt_size); if (!ms->fdt) { error_report("create_device_tree() failed"); exit(1); @@ -290,10 +290,10 @@ static void create_fdt(LoongArchMachineState *lams) qemu_fdt_add_subnode(ms->fdt, "/chosen"); } -static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) +static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) { int num; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); int smp_cpus = ms->smp.cpus; qemu_fdt_add_subnode(ms->fdt, "/cpus"); @@ -347,11 +347,11 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) } } -static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) +static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms) { char *nodename; hwaddr base = VIRT_FWCFG_BASE; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -363,7 +363,7 @@ static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) g_free(nodename); } -static void fdt_add_pcie_irq_map_node(const LoongArchMachineState *lams, +static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, char *nodename, uint32_t *pch_pic_phandle) { @@ -371,7 +371,7 @@ static void fdt_add_pcie_irq_map_node(const LoongArchMachineState *lams, uint32_t irq_map_stride = 0; uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {}; uint32_t *irq_map = full_irq_map; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); /* This code creates a standard swizzle of interrupts such that * each device's first interrupt is based on it's PCI_SLOT number. @@ -416,7 +416,7 @@ static void fdt_add_pcie_irq_map_node(const LoongArchMachineState *lams, 0x1800, 0, 0, 0x7); } -static void fdt_add_pcie_node(const LoongArchMachineState *lams, +static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms, uint32_t *pch_pic_phandle, uint32_t *pch_msi_phandle) { @@ -429,7 +429,7 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams, hwaddr size_pcie = VIRT_PCI_CFG_SIZE; hwaddr base = base_pcie; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); nodename = g_strdup_printf("/pcie@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -452,7 +452,7 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams, qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", 0, *pch_msi_phandle, 0, 0x10000); - fdt_add_pcie_irq_map_node(lams, nodename, pch_pic_phandle); + fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle); g_free(nodename); } @@ -473,15 +473,15 @@ static void fdt_add_memory_node(MachineState *ms, g_free(nodename); } -static void virt_build_smbios(LoongArchMachineState *lams) +static void virt_build_smbios(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); - MachineClass *mc = MACHINE_GET_CLASS(lams); + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(lvms); uint8_t *smbios_tables, *smbios_anchor; size_t smbios_tables_len, smbios_anchor_len; const char *product = "QEMU Virtual Machine"; - if (!lams->fw_cfg) { + if (!lvms->fw_cfg) { return; } @@ -493,26 +493,26 @@ static void virt_build_smbios(LoongArchMachineState *lams) &smbios_anchor, &smbios_anchor_len, &error_fatal); if (smbios_anchor) { - fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables", + fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-tables", smbios_tables, smbios_tables_len); - fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor", + fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-anchor", smbios_anchor, smbios_anchor_len); } } -static void virt_machine_done(Notifier *notifier, void *data) +static void virt_done(Notifier *notifier, void *data) { - LoongArchMachineState *lams = container_of(notifier, - LoongArchMachineState, machine_done); - virt_build_smbios(lams); - loongarch_acpi_setup(lams); + LoongArchVirtMachineState *lvms = container_of(notifier, + LoongArchVirtMachineState, machine_done); + virt_build_smbios(lvms); + loongarch_acpi_setup(lvms); } static void virt_powerdown_req(Notifier *notifier, void *opaque) { - LoongArchMachineState *s = container_of(notifier, - LoongArchMachineState, powerdown_notifier); + LoongArchVirtMachineState *s; + s = container_of(notifier, LoongArchVirtMachineState, powerdown_notifier); acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); } @@ -532,10 +532,11 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) memmap_entries++; } -static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams) +static DeviceState *create_acpi_ged(DeviceState *pch_pic, + LoongArchVirtMachineState *lvms) { DeviceState *dev; - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); uint32_t event = ACPI_GED_PWR_DOWN_EVT; if (ms->ram_slots) { @@ -582,12 +583,12 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) return dev; } -static void loongarch_devices_init(DeviceState *pch_pic, - LoongArchMachineState *lams, +static void virt_devices_init(DeviceState *pch_pic, + LoongArchVirtMachineState *lvms, uint32_t *pch_pic_phandle, uint32_t *pch_msi_phandle) { - MachineClass *mc = MACHINE_GET_CLASS(lams); + MachineClass *mc = MACHINE_GET_CLASS(lvms); DeviceState *gpex_dev; SysBusDevice *d; PCIBus *pci_bus; @@ -599,7 +600,7 @@ static void loongarch_devices_init(DeviceState *pch_pic, d = SYS_BUS_DEVICE(gpex_dev); sysbus_realize_and_unref(d, &error_fatal); pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus; - lams->pci_bus = pci_bus; + lvms->pci_bus = pci_bus; /* Map only part size_ecam bytes of ECAM space */ ecam_alias = g_new0(MemoryRegion, 1); @@ -632,13 +633,13 @@ static void loongarch_devices_init(DeviceState *pch_pic, } /* Add pcie node */ - fdt_add_pcie_node(lams, pch_pic_phandle, pch_msi_phandle); + fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle); serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0, qdev_get_gpio_in(pch_pic, VIRT_UART_IRQ - VIRT_GSI_BASE), 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - fdt_add_uart_node(lams, pch_pic_phandle); + fdt_add_uart_node(lvms, pch_pic_phandle); /* Network init */ pci_init_nic_devices(pci_bus, mc->default_nic); @@ -651,17 +652,17 @@ static void loongarch_devices_init(DeviceState *pch_pic, sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, qdev_get_gpio_in(pch_pic, VIRT_RTC_IRQ - VIRT_GSI_BASE)); - fdt_add_rtc_node(lams, pch_pic_phandle); + fdt_add_rtc_node(lvms, pch_pic_phandle); /* acpi ged */ - lams->acpi_ged = create_acpi_ged(pch_pic, lams); + lvms->acpi_ged = create_acpi_ged(pch_pic, lvms); /* platform bus */ - lams->platform_bus_dev = create_platform_bus(pch_pic); + lvms->platform_bus_dev = create_platform_bus(pch_pic); } -static void loongarch_irq_init(LoongArchMachineState *lams) +static void virt_irq_init(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); DeviceState *pch_pic, *pch_msi, *cpudev; DeviceState *ipi, *extioi; SysBusDevice *d; @@ -699,20 +700,20 @@ static void loongarch_irq_init(LoongArchMachineState *lams) sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); /* IPI iocsr memory region */ - memory_region_add_subregion(&lams->system_iocsr, SMP_IPI_MAILBOX, + memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); - memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR, + memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); /* Add cpu interrupt-controller */ - fdt_add_cpuic_node(lams, &cpuintc_phandle); + fdt_add_cpuic_node(lvms, &cpuintc_phandle); for (cpu = 0; cpu < ms->smp.cpus; cpu++) { cpu_state = qemu_get_cpu(cpu); cpudev = DEVICE(cpu_state); lacpu = LOONGARCH_CPU(cpu_state); env = &(lacpu->env); - env->address_space_iocsr = &lams->as_iocsr; + env->address_space_iocsr = &lvms->as_iocsr; /* connect ipi irq to cpu irq */ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); @@ -723,7 +724,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); - memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, + memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); /* @@ -739,7 +740,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) } /* Add Extend I/O Interrupt Controller node */ - fdt_add_eiointc_node(lams, &cpuintc_phandle, &eiointc_phandle); + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); num = VIRT_PCH_PIC_IRQ_NUM; @@ -761,7 +762,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) } /* Add PCH PIC node */ - fdt_add_pch_pic_node(lams, &eiointc_phandle, &pch_pic_phandle); + fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); start = num; @@ -778,30 +779,30 @@ static void loongarch_irq_init(LoongArchMachineState *lams) } /* Add PCH MSI node */ - fdt_add_pch_msi_node(lams, &eiointc_phandle, &pch_msi_phandle); + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); - loongarch_devices_init(pch_pic, lams, &pch_pic_phandle, &pch_msi_phandle); + virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle); } -static void loongarch_firmware_init(LoongArchMachineState *lams) +static void virt_firmware_init(LoongArchVirtMachineState *lvms) { - char *filename = MACHINE(lams)->firmware; + char *filename = MACHINE(lvms)->firmware; char *bios_name = NULL; int bios_size, i; BlockBackend *pflash_blk0; MemoryRegion *mr; - lams->bios_loaded = false; + lvms->bios_loaded = false; /* Map legacy -drive if=pflash to machine properties */ - for (i = 0; i < ARRAY_SIZE(lams->flash); i++) { - pflash_cfi01_legacy_drive(lams->flash[i], + for (i = 0; i < ARRAY_SIZE(lvms->flash); i++) { + pflash_cfi01_legacy_drive(lvms->flash[i], drive_get(IF_PFLASH, 0, i)); } - virt_flash_map(lams, get_system_memory()); + virt_flash_map(lvms, get_system_memory()); - pflash_blk0 = pflash_cfi01_get_blk(lams->flash[0]); + pflash_blk0 = pflash_cfi01_get_blk(lvms->flash[0]); if (pflash_blk0) { if (filename) { @@ -809,7 +810,7 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) "options at once"); exit(1); } - lams->bios_loaded = true; + lvms->bios_loaded = true; return; } @@ -820,24 +821,24 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) exit(1); } - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lams->flash[0]), 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lvms->flash[0]), 0); bios_size = load_image_mr(bios_name, mr); if (bios_size < 0) { error_report("Could not load ROM image '%s'", bios_name); exit(1); } g_free(bios_name); - lams->bios_loaded = true; + lvms->bios_loaded = true; } } -static void loongarch_qemu_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static void virt_iocsr_misc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) { } -static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t virt_iocsr_misc_read(void *opaque, hwaddr addr, unsigned size) { switch (addr) { case VERSION_REG: @@ -855,9 +856,9 @@ static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) return 0ULL; } -static const MemoryRegionOps loongarch_qemu_ops = { - .read = loongarch_qemu_read, - .write = loongarch_qemu_write, +static const MemoryRegionOps virt_iocsr_misc_ops = { + .read = virt_iocsr_misc_read, + .write = virt_iocsr_misc_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, @@ -869,7 +870,7 @@ static const MemoryRegionOps loongarch_qemu_ops = { }, }; -static void loongarch_init(MachineState *machine) +static void virt_init(MachineState *machine) { LoongArchCPU *lacpu; const char *cpu_model = machine->cpu_type; @@ -877,7 +878,7 @@ static void loongarch_init(MachineState *machine) ram_addr_t ram_size = machine->ram_size; uint64_t highram_size = 0, phyAddr = 0; MemoryRegion *address_space_mem = get_system_memory(); - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(machine); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); int nb_numa_nodes = machine->numa_state->num_nodes; NodeInfo *numa_info = machine->numa_state->nodes; int i; @@ -893,16 +894,16 @@ static void loongarch_init(MachineState *machine) error_report("ram_size must be greater than 1G."); exit(1); } - create_fdt(lams); + create_fdt(lvms); /* Create IOCSR space */ - memory_region_init_io(&lams->system_iocsr, OBJECT(machine), NULL, + memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL, machine, "iocsr", UINT64_MAX); - address_space_init(&lams->as_iocsr, &lams->system_iocsr, "IOCSR"); - memory_region_init_io(&lams->iocsr_mem, OBJECT(machine), - &loongarch_qemu_ops, + address_space_init(&lvms->as_iocsr, &lvms->system_iocsr, "IOCSR"); + memory_region_init_io(&lvms->iocsr_mem, OBJECT(machine), + &virt_iocsr_misc_ops, machine, "iocsr_misc", 0x428); - memory_region_add_subregion(&lams->system_iocsr, 0, &lams->iocsr_mem); + memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem); /* Init CPUs */ possible_cpus = mc->possible_cpu_arch_ids(machine); @@ -913,14 +914,14 @@ static void loongarch_init(MachineState *machine) lacpu = LOONGARCH_CPU(cpu); lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; } - fdt_add_cpu_nodes(lams); + fdt_add_cpu_nodes(lvms); /* Node0 memory */ memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0); - memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram", + memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.node0.lowram", machine->ram, offset, VIRT_LOWMEM_SIZE); - memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem); + memory_region_add_subregion(address_space_mem, phyAddr, &lvms->lowmem); offset += VIRT_LOWMEM_SIZE; if (nb_numa_nodes > 0) { @@ -932,9 +933,9 @@ static void loongarch_init(MachineState *machine) phyAddr = VIRT_HIGHMEM_BASE; memmap_add_entry(phyAddr, highram_size, 1); fdt_add_memory_node(machine, phyAddr, highram_size, 0); - memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram", + memory_region_init_alias(&lvms->highmem, NULL, "loongarch.node0.highram", machine->ram, offset, highram_size); - memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem); + memory_region_add_subregion(address_space_mem, phyAddr, &lvms->highmem); /* Node1 - Nodemax memory */ offset += highram_size; @@ -975,30 +976,30 @@ static void loongarch_init(MachineState *machine) } /* load the BIOS image. */ - loongarch_firmware_init(lams); + virt_firmware_init(lvms); /* fw_cfg init */ - lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine); - rom_set_fw(lams->fw_cfg); - if (lams->fw_cfg != NULL) { - fw_cfg_add_file(lams->fw_cfg, "etc/memmap", + lvms->fw_cfg = virt_fw_cfg_init(ram_size, machine); + rom_set_fw(lvms->fw_cfg); + if (lvms->fw_cfg != NULL) { + fw_cfg_add_file(lvms->fw_cfg, "etc/memmap", memmap_table, sizeof(struct memmap_entry) * (memmap_entries)); } - fdt_add_fw_cfg_node(lams); - fdt_add_flash_node(lams); + fdt_add_fw_cfg_node(lvms); + fdt_add_flash_node(lvms); /* Initialize the IO interrupt subsystem */ - loongarch_irq_init(lams); + virt_irq_init(lvms); platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", VIRT_PLATFORM_BUS_BASEADDRESS, VIRT_PLATFORM_BUS_SIZE, VIRT_PLATFORM_BUS_IRQ); - lams->machine_done.notify = virt_machine_done; - qemu_add_machine_init_done_notifier(&lams->machine_done); + lvms->machine_done.notify = virt_done; + qemu_add_machine_init_done_notifier(&lvms->machine_done); /* connect powerdown request */ - lams->powerdown_notifier.notify = virt_powerdown_req; - qemu_register_powerdown_notifier(&lams->powerdown_notifier); + lvms->powerdown_notifier.notify = virt_powerdown_req; + qemu_register_powerdown_notifier(&lvms->powerdown_notifier); /* * Since lowmem region starts from 0 and Linux kernel legacy start address @@ -1007,49 +1008,41 @@ static void loongarch_init(MachineState *machine) * Put the FDT into the memory map as a ROM image: this will ensure * the FDT is copied again upon reset, even if addr points into RAM. */ - qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size); - rom_add_blob_fixed_as("fdt", machine->fdt, lams->fdt_size, FDT_BASE, + qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); + rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, - rom_ptr_for_as(&address_space_memory, FDT_BASE, lams->fdt_size)); + rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); - lams->bootinfo.ram_size = ram_size; - loongarch_load_kernel(machine, &lams->bootinfo); + lvms->bootinfo.ram_size = ram_size; + loongarch_load_kernel(machine, &lvms->bootinfo); } -bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) +static void virt_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - if (lams->acpi == ON_OFF_AUTO_OFF) { - return false; - } - return true; -} - -static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(obj); - OnOffAuto acpi = lams->acpi; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto acpi = lvms->acpi; visit_type_OnOffAuto(v, name, &acpi, errp); } -static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, +static void virt_set_acpi(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(obj); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); - visit_type_OnOffAuto(v, name, &lams->acpi, errp); + visit_type_OnOffAuto(v, name, &lvms->acpi, errp); } -static void loongarch_machine_initfn(Object *obj) +static void virt_initfn(Object *obj) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(obj); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); - lams->acpi = ON_OFF_AUTO_AUTO; - lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); - lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); - virt_flash_create(lams); + lvms->acpi = ON_OFF_AUTO_AUTO; + lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); + lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); + virt_flash_create(lvms); } static bool memhp_type_supported(DeviceState *dev) @@ -1065,7 +1058,7 @@ static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); } -static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, +static void virt_device_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { @@ -1076,14 +1069,14 @@ static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); /* the acpi ged is always exist */ - hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev, + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); } -static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, +static void virt_device_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { @@ -1094,14 +1087,14 @@ static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, static void virt_mem_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp); - pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams)); + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); + pc_dimm_unplug(PC_DIMM(dev), MACHINE(lvms)); qdev_unrealize(dev); } -static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, +static void virt_device_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { @@ -1112,31 +1105,32 @@ static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, static void virt_mem_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - pc_dimm_plug(PC_DIMM(dev), MACHINE(lams)); - hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged), + pc_dimm_plug(PC_DIMM(dev), MACHINE(lvms)); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); } -static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev, +static void virt_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_VIRT_MACHINE(hotplug_dev); - MachineClass *mc = MACHINE_GET_CLASS(lams); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + MachineClass *mc = MACHINE_GET_CLASS(lvms); + PlatformBusDevice *pbus; if (device_is_dynamic_sysbus(mc, dev)) { - if (lams->platform_bus_dev) { - platform_bus_link_device(PLATFORM_BUS_DEVICE(lams->platform_bus_dev), - SYS_BUS_DEVICE(dev)); + if (lvms->platform_bus_dev) { + pbus = PLATFORM_BUS_DEVICE(lvms->platform_bus_dev); + platform_bus_link_device(pbus, SYS_BUS_DEVICE(dev)); } } else if (memhp_type_supported(dev)) { virt_mem_plug(hotplug_dev, dev, errp); } } -static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, - DeviceState *dev) +static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -1176,8 +1170,8 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } -static CpuInstanceProperties -virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +static CpuInstanceProperties virt_cpu_index_to_props(MachineState *ms, + unsigned cpu_index) { MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); @@ -1199,12 +1193,12 @@ static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) return nidx; } -static void loongarch_class_init(ObjectClass *oc, void *data) +static void virt_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - mc->init = loongarch_init; + mc->init = virt_init; mc->default_ram_size = 1 * GiB; mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); mc->default_ram_id = "loongarch.ram"; @@ -1220,15 +1214,15 @@ static void loongarch_class_init(ObjectClass *oc, void *data) mc->numa_mem_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; - mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + mc->get_hotplug_handler = virt_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; - hc->plug = loongarch_machine_device_plug_cb; - hc->pre_plug = virt_machine_device_pre_plug; - hc->unplug_request = virt_machine_device_unplug_request; - hc->unplug = virt_machine_device_unplug; + hc->plug = virt_device_plug_cb; + hc->pre_plug = virt_device_pre_plug; + hc->unplug_request = virt_device_unplug_request; + hc->unplug = virt_device_unplug; object_class_property_add(oc, "acpi", "OnOffAuto", - loongarch_get_acpi, loongarch_set_acpi, + virt_get_acpi, virt_set_acpi, NULL, NULL); object_class_property_set_description(oc, "acpi", "Enable ACPI"); @@ -1238,13 +1232,13 @@ static void loongarch_class_init(ObjectClass *oc, void *data) #endif } -static const TypeInfo loongarch_machine_types[] = { +static const TypeInfo virt_machine_types[] = { { .name = TYPE_LOONGARCH_VIRT_MACHINE, .parent = TYPE_MACHINE, - .instance_size = sizeof(LoongArchMachineState), - .class_init = loongarch_class_init, - .instance_init = loongarch_machine_initfn, + .instance_size = sizeof(LoongArchVirtMachineState), + .class_init = virt_class_init, + .instance_init = virt_initfn, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } @@ -1252,4 +1246,4 @@ static const TypeInfo loongarch_machine_types[] = { } }; -DEFINE_TYPES(loongarch_machine_types) +DEFINE_TYPES(virt_machine_types) diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 5b1416d7bc..d8a4ddb936 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -37,7 +37,7 @@ #define FDT_BASE 0x100000 -struct LoongArchMachineState { +struct LoongArchVirtMachineState { /*< private >*/ MachineState parent_obj; @@ -64,7 +64,6 @@ struct LoongArchMachineState { }; #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_VIRT_MACHINE) -bool loongarch_is_acpi_enabled(LoongArchMachineState *lams); -void loongarch_acpi_setup(LoongArchMachineState *lams); +OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) +void loongarch_acpi_setup(LoongArchVirtMachineState *lvms); #endif From 5b1a3b9f8c0fbcd2420977678601948d7573c809 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 May 2024 10:31:09 +0100 Subject: [PATCH 0564/2884] hw/mips/loongson3_virt: Emulate suspend function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suspend function is emulated as what hardware actually do. Doorbell register fields are updates to include suspend value, suspend vector is encoded in firmware blob and fw_cfg is updated to include S3 bits as what x86 did. Signed-off-by: Jiaxun Yang Message-ID: <20240508-loongson3v-suspend-v1-1-186725524a39@flygoat.com> [PMD: Use g_memdup2(), constify suspend array] Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/loongson3_bootp.c | 1 + hw/mips/loongson3_virt.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index f99af22932..03a10b63c1 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -148,4 +148,5 @@ void init_reset_system(struct efi_reset_system_t *reset) reset->Shutdown = cpu_to_le64(0xffffffffbfc000a8); reset->ResetCold = cpu_to_le64(0xffffffffbfc00080); reset->ResetWarm = cpu_to_le64(0xffffffffbfc00080); + reset->DoSuspend = cpu_to_le64(0xffffffffbfc000d0); } diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index b10a611a98..440268a074 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -127,6 +127,9 @@ static void loongson3_pm_write(void *opaque, hwaddr addr, case 0x00: qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; + case 0x01: + qemu_system_suspend_request(); + return; case 0xff: qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); return; @@ -250,6 +253,17 @@ static void init_boot_rom(void) 0x240D00FF, /* li t1, 0xff */ 0xA18D0000, /* sb t1, (t0) */ 0x1000FFFF, /* 1: b 1b */ + 0x00000000, /* nop */ + /* Suspend */ + 0x3C0C9000, /* dli t0, 0x9000000010080010 */ + 0x358C0000, + 0x000C6438, + 0x358C1008, + 0x000C6438, + 0x358C0010, + 0x240D0001, /* li t1, 0x01 */ + 0xA18D0000, /* sb t1, (t0) */ + 0x03e00008, /* jr ra */ 0x00000000 /* nop */ }; @@ -265,6 +279,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, static void fw_conf_init(unsigned long ram_size) { + static const uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; FWCfgState *fw_cfg; hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base; @@ -274,6 +289,10 @@ static void fw_conf_init(unsigned long ram_size) fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz()); + + fw_cfg_add_file(fw_cfg, "etc/system-states", + g_memdup2(suspend, sizeof(suspend)), sizeof(suspend)); + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } @@ -553,6 +572,7 @@ static void mips_loongson3_virt_init(MachineState *machine) machine->ram, 0, virt_memmap[VIRT_LOWMEM].size); memory_region_init_io(iomem, NULL, &loongson3_pm_ops, NULL, "loongson3_pm", virt_memmap[VIRT_PM].size); + qemu_register_wakeup_support(); memory_region_add_subregion(address_space_mem, virt_memmap[VIRT_LOWMEM].base, ram); From 39b3ae11b0763d8d8f4df9b03fc4b3ed5fa155ee Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 May 2024 14:06:46 +0100 Subject: [PATCH 0565/2884] hw/intc/loongarch_ipi: Remove pointless MAX_CPU check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since cpuid will be checked by ipi_getcpu anyway, there is no point to enforce MAX_CPU here. This also saved us from including loongarch board header. Signed-off-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240508-loongson3-ipi-v1-1-1a7b67704664@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/loongarch_ipi.c | 19 ++----------------- hw/intc/trace-events | 2 -- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index a184112b09..44b3b9c138 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "hw/boards.h" #include "hw/sysbus.h" #include "hw/intc/loongarch_ipi.h" #include "hw/irq.h" @@ -13,9 +14,8 @@ #include "qapi/error.h" #include "qemu/log.h" #include "exec/address-spaces.h" -#include "hw/loongarch/virt.h" #include "migration/vmstate.h" -#include "target/loongarch/internals.h" +#include "target/loongarch/cpu.h" #include "trace.h" static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, @@ -122,11 +122,6 @@ static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) CPUState *cs; cpuid = extract32(val, 16, 10); - if (cpuid >= LOONGARCH_MAX_CPUS) { - trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); - return MEMTX_DECODE_ERROR; - } - cs = ipi_getcpu(cpuid); if (cs == NULL) { return MEMTX_DECODE_ERROR; @@ -146,11 +141,6 @@ static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) CPUState *cs; cpuid = extract32(val, 16, 10); - if (cpuid >= LOONGARCH_MAX_CPUS) { - trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); - return MEMTX_DECODE_ERROR; - } - cs = ipi_getcpu(cpuid); if (cs == NULL) { return MEMTX_DECODE_ERROR; @@ -201,11 +191,6 @@ static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, break; case IOCSR_IPI_SEND: cpuid = extract32(val, 16, 10); - if (cpuid >= LOONGARCH_MAX_CPUS) { - trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); - return MEMTX_DECODE_ERROR; - } - /* IPI status vector */ vector = extract8(val, 0, 5); cs = ipi_getcpu(cpuid); diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 47340b5bc1..a979784f9b 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -294,8 +294,6 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" # loongarch_ipi.c loongarch_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 loongarch_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 -loongarch_ipi_unsupported_cpuid(const char *s, uint32_t cpuid) "%s unsupported cpuid 0x%" PRIx32 - # loongarch_pch_pic.c loongarch_pch_pic_irq_handler(int irq, int level) "irq %d level %d" loongarch_pch_pic_low_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 From b4a12dfc2132dc22c587ee2ecbd7b8d48ec381a1 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 May 2024 14:06:47 +0100 Subject: [PATCH 0566/2884] hw/intc/loongarch_ipi: Rename as loongson_ipi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device will be shared among LoongArch and MIPS based Loongson machine, rename it as loongson_ipi to reflect this nature. Signed-off-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240508-loongson3-ipi-v1-2-1a7b67704664@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 + hw/intc/Kconfig | 2 +- hw/intc/loongson_ipi.c | 347 ++++++++++++++++++ hw/intc/meson.build | 2 +- hw/intc/trace-events | 6 +- hw/loongarch/Kconfig | 2 +- hw/loongarch/virt.c | 4 +- .../intc/{loongarch_ipi.h => loongson_ipi.h} | 12 +- include/hw/loongarch/virt.h | 2 +- 9 files changed, 366 insertions(+), 15 deletions(-) create mode 100644 hw/intc/loongson_ipi.c rename include/hw/intc/{loongarch_ipi.h => loongson_ipi.h} (84%) diff --git a/MAINTAINERS b/MAINTAINERS index 84391777db..7097f31245 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1242,7 +1242,9 @@ F: configs/devices/loongarch64-softmmu/default.mak F: hw/loongarch/ F: include/hw/loongarch/virt.h F: include/hw/intc/loongarch_*.h +F: include/hw/intc/loongson_ipi.h F: hw/intc/loongarch_*.c +F: hw/intc/loongson_ipi.c F: include/hw/pci-host/ls7a.h F: hw/rtc/ls7a_rtc.c F: gdb-xml/loongarch*.xml @@ -1376,10 +1378,12 @@ Loongson-3 virtual platforms M: Huacai Chen R: Jiaxun Yang S: Maintained +F: hw/intc/loongson_ipi.c F: hw/intc/loongson_liointc.c F: hw/mips/loongson3_bootp.c F: hw/mips/loongson3_bootp.h F: hw/mips/loongson3_virt.c +F: include/hw/intc/loongson_ipi.h F: include/hw/intc/loongson_liointc.h F: tests/avocado/machine_mips_loongson3v.py diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index ad59abebaa..58b6d3a710 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -87,7 +87,7 @@ config GOLDFISH_PIC config M68K_IRQC bool -config LOONGARCH_IPI +config LOONGSON_IPI bool config LOONGARCH_PCH_PIC diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c new file mode 100644 index 0000000000..8c888da3b2 --- /dev/null +++ b/hw/intc/loongson_ipi.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson ipi interrupt support + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "hw/sysbus.h" +#include "hw/intc/loongson_ipi.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" +#include "target/loongarch/cpu.h" +#include "trace.h" + +static MemTxResult loongson_ipi_readl(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + IPICore *s; + LoongsonIPI *ipi = opaque; + uint64_t ret = 0; + int index = 0; + + s = &ipi->cpu[attrs.requester_id]; + addr &= 0xff; + switch (addr) { + case CORE_STATUS_OFF: + ret = s->status; + break; + case CORE_EN_OFF: + ret = s->en; + break; + case CORE_SET_OFF: + ret = 0; + break; + case CORE_CLEAR_OFF: + ret = 0; + break; + case CORE_BUF_20 ... CORE_BUF_38 + 4: + index = (addr - CORE_BUF_20) >> 2; + ret = s->buf[index]; + break; + default: + qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); + break; + } + + trace_loongson_ipi_read(size, (uint64_t)addr, ret); + *data = ret; + return MEMTX_OK; +} + +static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, + MemTxAttrs attrs) +{ + int i, mask = 0, data = 0; + + /* + * bit 27-30 is mask for byte writing, + * if the mask is 0, we need not to do anything. + */ + if ((val >> 27) & 0xf) { + data = address_space_ldl(env->address_space_iocsr, addr, + attrs, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ + if (val & (0x1 << (27 + i))) { + mask |= 0xff << (i * 8); + } + } + } + + data &= mask; + data |= (val >> 32) & ~mask; + address_space_stl(env->address_space_iocsr, addr, + data, attrs, NULL); +} + +static int archid_cmp(const void *a, const void *b) +{ + CPUArchId *archid_a = (CPUArchId *)a; + CPUArchId *archid_b = (CPUArchId *)b; + + return archid_a->arch_id - archid_b->arch_id; +} + +static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) +{ + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = id; + found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, + ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), + archid_cmp); + + return found_cpu; +} + +static CPUState *ipi_getcpu(int arch_id) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + CPUArchId *archid; + + archid = find_cpu_by_archid(machine, arch_id); + if (archid) { + return CPU(archid->cpu); + } + + return NULL; +} + +static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) +{ + uint32_t cpuid; + hwaddr addr; + CPUState *cs; + + cpuid = extract32(val, 16, 10); + cs = ipi_getcpu(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); + attrs.requester_id = cs->cpu_index; + send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); + return MEMTX_OK; +} + +static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) +{ + uint32_t cpuid; + hwaddr addr; + CPUState *cs; + + cpuid = extract32(val, 16, 10); + cs = ipi_getcpu(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = val & 0xffff; + attrs.requester_id = cs->cpu_index; + send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); + return MEMTX_OK; +} + +static MemTxResult loongson_ipi_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + LoongsonIPI *ipi = opaque; + IPICore *s; + int index = 0; + uint32_t cpuid; + uint8_t vector; + CPUState *cs; + + s = &ipi->cpu[attrs.requester_id]; + addr &= 0xff; + trace_loongson_ipi_write(size, (uint64_t)addr, val); + switch (addr) { + case CORE_STATUS_OFF: + qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); + break; + case CORE_EN_OFF: + s->en = val; + break; + case CORE_SET_OFF: + s->status |= val; + if (s->status != 0 && (s->status & s->en) != 0) { + qemu_irq_raise(s->irq); + } + break; + case CORE_CLEAR_OFF: + s->status &= ~val; + if (s->status == 0 && s->en != 0) { + qemu_irq_lower(s->irq); + } + break; + case CORE_BUF_20 ... CORE_BUF_38 + 4: + index = (addr - CORE_BUF_20) >> 2; + s->buf[index] = val; + break; + case IOCSR_IPI_SEND: + cpuid = extract32(val, 16, 10); + /* IPI status vector */ + vector = extract8(val, 0, 5); + cs = ipi_getcpu(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + attrs.requester_id = cs->cpu_index; + loongson_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); + break; + default: + qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); + break; + } + + return MEMTX_OK; +} + +static const MemoryRegionOps loongson_ipi_ops = { + .read_with_attrs = loongson_ipi_readl, + .write_with_attrs = loongson_ipi_writel, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* mail send and any send only support writeq */ +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + MemTxResult ret = MEMTX_OK; + + addr &= 0xfff; + switch (addr) { + case MAIL_SEND_OFFSET: + ret = mail_send(val, attrs); + break; + case ANY_SEND_OFFSET: + ret = any_send(val, attrs); + break; + default: + break; + } + + return ret; +} + +static const MemoryRegionOps loongson_ipi64_ops = { + .write_with_attrs = loongson_ipi_writeq, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongson_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongsonIPI *s = LOONGSON_IPI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } + + memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongson_ipi_ops, + s, "loongson_ipi_iocsr", 0x48); + + /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */ + s->ipi_iocsr_mem.disable_reentrancy_guard = true; + + sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); + + memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), + &loongson_ipi64_ops, + s, "loongson_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); + + s->cpu = g_new0(IPICore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for ExtIOICore faile"); + return; + } + + for (i = 0; i < s->num_cpu; i++) { + qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); + } +} + +static const VMStateDescription vmstate_ipi_core = { + .name = "ipi-single", + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(status, IPICore), + VMSTATE_UINT32(en, IPICore), + VMSTATE_UINT32(set, IPICore), + VMSTATE_UINT32(clear, IPICore), + VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_loongson_ipi = { + .name = TYPE_LOONGSON_IPI, + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPI, num_cpu, + vmstate_ipi_core, IPICore), + VMSTATE_END_OF_LIST() + } +}; + +static Property ipi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongsonIPI, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void loongson_ipi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = loongson_ipi_realize; + device_class_set_props(dc, ipi_properties); + dc->vmsd = &vmstate_loongson_ipi; +} + +static void loongson_ipi_finalize(Object *obj) +{ + LoongsonIPI *s = LOONGSON_IPI(obj); + + g_free(s->cpu); +} + +static const TypeInfo loongson_ipi_info = { + .name = TYPE_LOONGSON_IPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongsonIPI), + .class_init = loongson_ipi_class_init, + .instance_finalize = loongson_ipi_finalize, +}; + +static void loongson_ipi_register_types(void) +{ + type_register_static(&loongson_ipi_info); +} + +type_init(loongson_ipi_register_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 58140da5f2..f4b540e6a8 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -68,7 +68,7 @@ specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) -specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) +specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index a979784f9b..b815cea129 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -291,9 +291,9 @@ sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PR sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx" sh_intc_set(int id, int enable) "setting interrupt group %d to %d" -# loongarch_ipi.c -loongarch_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 -loongarch_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 +# loongson_ipi.c +loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 +loongson_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 # loongarch_pch_pic.c loongarch_pch_pic_irq_handler(int irq, int level) "irq %d level %d" loongarch_pch_pic_low_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 7864050563..ad77502445 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -10,7 +10,7 @@ config LOONGARCH_VIRT select SERIAL select VIRTIO_PCI select PLATFORM_BUS - select LOONGARCH_IPI + select LOONGSON_IPI select LOONGARCH_PCH_PIC select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 51e0aca39b..852036467a 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -21,7 +21,7 @@ #include "net/net.h" #include "hw/loader.h" #include "elf.h" -#include "hw/intc/loongarch_ipi.h" +#include "hw/intc/loongson_ipi.h" #include "hw/intc/loongarch_extioi.h" #include "hw/intc/loongarch_pch_pic.h" #include "hw/intc/loongarch_pch_msi.h" @@ -695,7 +695,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) */ /* Create IPI device */ - ipi = qdev_new(TYPE_LOONGARCH_IPI); + ipi = qdev_new(TYPE_LOONGSON_IPI); qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongson_ipi.h similarity index 84% rename from include/hw/intc/loongarch_ipi.h rename to include/hw/intc/loongson_ipi.h index 1c1e834849..2c0e8820f5 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * LoongArch ipi interrupt header files + * Loongson ipi interrupt header files * * Copyright (C) 2021 Loongson Technology Corporation Limited */ -#ifndef HW_LOONGARCH_IPI_H -#define HW_LOONGARCH_IPI_H +#ifndef HW_LOONGSON_IPI_H +#define HW_LOONGSON_IPI_H #include "hw/sysbus.h" @@ -30,8 +30,8 @@ #define IPI_MBX_NUM 4 -#define TYPE_LOONGARCH_IPI "loongarch_ipi" -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI) +#define TYPE_LOONGSON_IPI "loongson_ipi" +OBJECT_DECLARE_SIMPLE_TYPE(LoongsonIPI, LOONGSON_IPI) typedef struct IPICore { uint32_t status; @@ -43,7 +43,7 @@ typedef struct IPICore { qemu_irq irq; } IPICore; -struct LoongArchIPI { +struct LoongsonIPI { SysBusDevice parent_obj; MemoryRegion ipi_iocsr_mem; MemoryRegion ipi64_iocsr_mem; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index d8a4ddb936..2c4f5cf9c8 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -11,7 +11,7 @@ #include "target/loongarch/cpu.h" #include "hw/boards.h" #include "qemu/queue.h" -#include "hw/intc/loongarch_ipi.h" +#include "hw/intc/loongson_ipi.h" #include "hw/block/flash.h" #include "hw/loongarch/boot.h" From 91d0b151de4cb433ae31b7e2678fdb19850ad772 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 May 2024 14:06:48 +0100 Subject: [PATCH 0567/2884] hw/intc/loongson_ipi: Implement IOCSR address space for MIPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement IOCSR address space get functions for MIPS/Loongson CPUs. For MIPS/Loongson without IOCSR (i.e. Loongson-3A1000), get_cpu_iocsr_as will return as null, and send_ipi_data will fail with MEMTX_DECODE_ERROR, which matches expected behavior on hardware. Signed-off-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240508-loongson3-ipi-v1-3-1a7b67704664@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/loongson_ipi.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 8c888da3b2..93cc50a37a 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -15,7 +15,12 @@ #include "qemu/log.h" #include "exec/address-spaces.h" #include "migration/vmstate.h" +#ifdef TARGET_LOONGARCH64 #include "target/loongarch/cpu.h" +#endif +#ifdef TARGET_MIPS +#include "target/mips/cpu.h" +#endif #include "trace.h" static MemTxResult loongson_ipi_readl(void *opaque, hwaddr addr, @@ -56,18 +61,35 @@ static MemTxResult loongson_ipi_readl(void *opaque, hwaddr addr, return MEMTX_OK; } -static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, +static AddressSpace *get_cpu_iocsr_as(CPUState *cpu) +{ +#ifdef TARGET_LOONGARCH64 + return LOONGARCH_CPU(cpu)->env.address_space_iocsr; +#endif +#ifdef TARGET_MIPS + if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) { + return &MIPS_CPU(cpu)->env.iocsr.as; + } +#endif + return NULL; +} + +static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr, MemTxAttrs attrs) { int i, mask = 0, data = 0; + AddressSpace *iocsr_as = get_cpu_iocsr_as(cpu); + + if (!iocsr_as) { + return MEMTX_DECODE_ERROR; + } /* * bit 27-30 is mask for byte writing, * if the mask is 0, we need not to do anything. */ if ((val >> 27) & 0xf) { - data = address_space_ldl(env->address_space_iocsr, addr, - attrs, NULL); + data = address_space_ldl(iocsr_as, addr, attrs, NULL); for (i = 0; i < 4; i++) { /* get mask for byte writing */ if (val & (0x1 << (27 + i))) { @@ -78,8 +100,9 @@ static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, data &= mask; data |= (val >> 32) & ~mask; - address_space_stl(env->address_space_iocsr, addr, - data, attrs, NULL); + address_space_stl(iocsr_as, addr, data, attrs, NULL); + + return MEMTX_OK; } static int archid_cmp(const void *a, const void *b) @@ -130,8 +153,7 @@ static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) /* override requester_id */ addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); attrs.requester_id = cs->cpu_index; - send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); - return MEMTX_OK; + return send_ipi_data(cs, val, addr, attrs); } static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) @@ -149,8 +171,7 @@ static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) /* override requester_id */ addr = val & 0xffff; attrs.requester_id = cs->cpu_index; - send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); - return MEMTX_OK; + return send_ipi_data(cs, val, addr, attrs); } static MemTxResult loongson_ipi_writel(void *opaque, hwaddr addr, uint64_t val, From 8b4d80bb53af30db5de91749216d0bb73fa93cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 7 May 2024 16:05:48 +0200 Subject: [PATCH 0568/2884] misc: Use QEMU header path relative to include/ directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU headers are relative to the include/ directory, not to the project root directory. Remove "include/". See also: https://www.qemu.org/docs/master/devel/style.html#include-directives Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20240507142737.95735-1-philmd@linaro.org> --- hw/audio/virtio-snd.c | 2 +- hw/rtc/ls7a_rtc.c | 2 +- target/i386/gdbstub.c | 2 +- tests/qtest/nvme-test.c | 2 +- tests/qtest/ufs-test.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 6a2ee085c0..7d09800d1f 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -19,7 +19,7 @@ #include "qemu/iov.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "include/qemu/lockable.h" +#include "qemu/lockable.h" #include "exec/tswap.h" #include "sysemu/runstate.h" #include "trace.h" diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index ac28c1165b..052201c2cd 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/irq.h" -#include "include/hw/register.h" +#include "hw/register.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index ebb000df6a..4acf485879 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "include/gdbstub/helpers.h" +#include "gdbstub/helpers.h" #ifdef TARGET_X86_64 static const int gpr_map[16] = { diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c index 008d189b0f..5ad6821f7a 100644 --- a/tests/qtest/nvme-test.c +++ b/tests/qtest/nvme-test.c @@ -13,7 +13,7 @@ #include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" -#include "include/block/nvme.h" +#include "block/nvme.h" typedef struct QNvme QNvme; diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 95e82f9472..82ec3f0671 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -13,7 +13,7 @@ #include "libqos/qgraph.h" #include "libqos/pci.h" #include "scsi/constants.h" -#include "include/block/ufs.h" +#include "block/ufs.h" /* Test images sizes in Bytes */ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) From 36fa7c686e9eac490002ffc439c4affaa352c17c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 May 2024 10:53:56 -0700 Subject: [PATCH 0569/2884] gitlab: Update msys2-64bit runner tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitlab has deprecated and removed support for windows-1809 and shared-windows. Update to saas-windows-medium-amd64 per https://about.gitlab.com/blog/2024/01/22/windows-2022-support-for-gitlab-saas-runners/ Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Tested-by: Thomas Huth Message-Id: <20240507175356.281618-1-richard.henderson@linaro.org> --- .gitlab-ci.d/windows.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index d26dbdd0c0..a83f23a786 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -1,9 +1,7 @@ msys2-64bit: extends: .base_job_template tags: - - shared-windows - - windows - - windows-1809 + - saas-windows-medium-amd64 cache: key: "$CI_JOB_NAME" paths: From f532cf013133320045b8588c2f62fc847902fd18 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 19 Mar 2024 10:26:06 +0800 Subject: [PATCH 0570/2884] hw/loongarch: Refine default numa id calculation With numa_test test case, there is subcase named test_def_cpu_split(), there are 8 sockets and 2 numa nodes. Here is command line: "-machine smp.cpus=8,smp.sockets=8 -numa node,memdev=ram -numa node" The required result is: node 0 cpus: 0 2 4 6 node 1 cpus: 1 3 5 7 Test case numa_test fails on LoongArch, since the actual result is: node 0 cpus: 0 1 2 3 node 1 cpus: 4 5 6 7 It will be better if all the cpus in one socket share the same numa node. Here socket id is used to calculate numa id in function virt_get_default_cpu_node_id(). Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240319022606.2994565-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index c0999878df..12d20055fe 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1192,15 +1192,14 @@ virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) { - int64_t nidx = 0; + int64_t socket_id; if (ms->numa_state->num_nodes) { - nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); - if (ms->numa_state->num_nodes <= nidx) { - nidx = ms->numa_state->num_nodes - 1; - } + socket_id = ms->possible_cpus->cpus[idx].props.socket_id; + return socket_id % ms->numa_state->num_nodes; + } else { + return 0; } - return nidx; } static void loongarch_class_init(ObjectClass *oc, void *data) From 6f703a48410db455057cc90c170e908de83b23fc Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 6 May 2024 09:19:12 +0800 Subject: [PATCH 0571/2884] target/loongarch: Add TCG macro in structure CPUArchState In structure CPUArchState some struct elements are only used in TCG mode, and it is not used in KVM mode. Macro CONFIG_TCG is added to make it simpiler in KVM mode, also there is the same modification in c code when these structure elements are used. When VM runs in KVM mode, TLB entries are not used and do not need migrate. It is only useful when it runs in TCG mode. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson Message-Id: <20240506011912.2108842-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 7 +++++-- target/loongarch/cpu.h | 16 ++++++++++------ target/loongarch/cpu_helper.c | 9 +++++++++ target/loongarch/machine.c | 30 +++++++++++++++++++++++++----- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 96da1a685e..a0cad53676 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -505,7 +505,9 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) lacc->parent_phases.hold(obj, type); } +#ifdef CONFIG_TCG env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; +#endif env->fcsr0 = 0x0; int n; @@ -550,7 +552,9 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) #ifndef CONFIG_USER_ONLY env->pc = 0x1c000000; +#ifdef CONFIG_TCG memset(env->tlb, 0, sizeof(env->tlb)); +#endif if (kvm_enabled()) { kvm_arch_reset_vcpu(env); } @@ -686,8 +690,7 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) int i; qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); - qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, - get_float_exception_flags(&env->fp_status)); + qemu_fprintf(f, " FCSR0 0x%08x\n", env->fcsr0); /* gpr */ for (i = 0; i < 32; i++) { diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index c5722670f5..41b8e6d96d 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -270,6 +270,7 @@ union fpr_t { VReg vreg; }; +#ifdef CONFIG_TCG struct LoongArchTLB { uint64_t tlb_misc; /* Fields corresponding to CSR_TLBELO0/1 */ @@ -277,23 +278,18 @@ struct LoongArchTLB { uint64_t tlb_entry1; }; typedef struct LoongArchTLB LoongArchTLB; +#endif typedef struct CPUArchState { uint64_t gpr[32]; uint64_t pc; fpr_t fpr[32]; - float_status fp_status; bool cf[8]; - uint32_t fcsr0; - uint32_t fcsr0_mask; uint32_t cpucfg[21]; - uint64_t lladdr; /* LL virtual address compared against SC */ - uint64_t llval; - /* LoongArch CSRs */ uint64_t CSR_CRMD; uint64_t CSR_PRMD; @@ -350,8 +346,16 @@ typedef struct CPUArchState { uint64_t CSR_DERA; uint64_t CSR_DSAVE; +#ifdef CONFIG_TCG + float_status fp_status; + uint32_t fcsr0_mask; + uint64_t lladdr; /* LL virtual address compared against SC */ + uint64_t llval; +#endif #ifndef CONFIG_USER_ONLY +#ifdef CONFIG_TCG LoongArchTLB tlb[LOONGARCH_TLB_MAX]; +#endif AddressSpace *address_space_iocsr; bool load_elf; diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 960eec9567..580362ac3e 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -11,6 +11,7 @@ #include "internals.h" #include "cpu-csr.h" +#ifdef CONFIG_TCG static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, int access_type, int index, int mmu_idx) @@ -154,6 +155,14 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } +#else +static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + return TLBRET_NOMATCH; +} +#endif static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, target_ulong dmw) diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c index c7029fb9b4..9cd9e848d6 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "migration/cpu.h" +#include "sysemu/tcg.h" #include "vec.h" static const VMStateDescription vmstate_fpu_reg = { @@ -109,9 +110,15 @@ static const VMStateDescription vmstate_lasx = { }, }; +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +static bool tlb_needed(void *opaque) +{ + return tcg_enabled(); +} + /* TLB state */ -const VMStateDescription vmstate_tlb = { - .name = "cpu/tlb", +static const VMStateDescription vmstate_tlb_entry = { + .name = "cpu/tlb_entry", .version_id = 0, .minimum_version_id = 0, .fields = (const VMStateField[]) { @@ -122,6 +129,19 @@ const VMStateDescription vmstate_tlb = { } }; +static const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", + .version_id = 0, + .minimum_version_id = 0, + .needed = tlb_needed, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, + 0, vmstate_tlb_entry, LoongArchTLB), + VMSTATE_END_OF_LIST() + } +}; +#endif + /* LoongArch CPU state */ const VMStateDescription vmstate_loongarch_cpu = { .name = "cpu", @@ -187,9 +207,6 @@ const VMStateDescription vmstate_loongarch_cpu = { VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU), VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU), VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU), - /* TLB */ - VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, - 0, vmstate_tlb, LoongArchTLB), VMSTATE_END_OF_LIST() }, @@ -197,6 +214,9 @@ const VMStateDescription vmstate_loongarch_cpu = { &vmstate_fpu, &vmstate_lsx, &vmstate_lasx, +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) + &vmstate_tlb, +#endif NULL } }; From 5872966db7abaa7f8753541b7a9f242df9752b50 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Sun, 28 Apr 2024 11:16:51 +0800 Subject: [PATCH 0572/2884] target/loongarch: Put cpucfg operation before CSR register On Loongarch, cpucfg is register for cpu feature, some other registers depend on cpucfg feature such as perf CSR registers. Here put cpucfg read/write operations before CSR register, so that KVM knows how many perf CSR registers are valid from pre-set cpucfg feature information. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240428031651.1354587-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/kvm/kvm.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 8224d94333..bc75552d0f 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -587,6 +587,11 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + ret = kvm_loongarch_get_cpucfg(cs); + if (ret) { + return ret; + } + ret = kvm_loongarch_get_csr(cs); if (ret) { return ret; @@ -598,11 +603,6 @@ int kvm_arch_get_registers(CPUState *cs) } ret = kvm_loongarch_get_mpstate(cs); - if (ret) { - return ret; - } - - ret = kvm_loongarch_get_cpucfg(cs); return ret; } @@ -615,6 +615,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + ret = kvm_loongarch_put_cpucfg(cs); + if (ret) { + return ret; + } + ret = kvm_loongarch_put_csr(cs, level); if (ret) { return ret; @@ -626,11 +631,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) } ret = kvm_loongarch_put_mpstate(cs); - if (ret) { - return ret; - } - - ret = kvm_loongarch_put_cpucfg(cs); return ret; } From c990c1f35b80de53570f11433d71624aca185763 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 30 Apr 2024 21:08:43 +0200 Subject: [PATCH 0573/2884] hw/s390x: Attach the sclpconsole to /machine/sclp/s390-sclp-event-facility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sclpconsole currently does not have a proper parent in the QOM tree, so it shows up under /machine/unattached - which is somewhat ugly. We should rather attach it to /machine/sclp/s390-sclp-event-facility where the other devices of type TYPE_SCLP_EVENT already reside. Message-ID: <20240430190843.453903-1-thuth@redhat.com> Reviewed-by: Eric Farman Reviewed-by: Cédric Le Goater Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4dcc213820..726c2ab436 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -239,11 +239,13 @@ static void s390_create_virtio_net(BusState *bus, const char *name) static void s390_create_sclpconsole(const char *type, Chardev *chardev) { + BusState *ev_fac_bus = sclp_get_event_facility_bus(); DeviceState *dev; dev = qdev_new(type); + object_property_add_child(OBJECT(ev_fac_bus->parent), type, OBJECT(dev)); qdev_prop_set_chr(dev, "chardev", chardev); - qdev_realize_and_unref(dev, sclp_get_event_facility_bus(), &error_fatal); + qdev_realize_and_unref(dev, ev_fac_bus, &error_fatal); } static void ccw_init(MachineState *machine) From b350f6c8ed4fd796454d0f26482f3e9d9285fda1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 2 May 2024 15:15:31 +0200 Subject: [PATCH 0574/2884] s390x: Introduce a SCLPDevice pointer under the machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize directly SCLPDevice from the machine init handler and remove s390_sclp_init(). We will use the SCLPDevice pointer later to create the consoles. Signed-off-by: Cédric Le Goater Message-ID: <20240502131533.377719-2-clg@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 6 +++++- hw/s390x/sclp.c | 10 ---------- include/hw/s390x/s390-virtio-ccw.h | 3 +++ include/hw/s390x/sclp.h | 2 -- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 726c2ab436..159f67d05c 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -251,11 +251,15 @@ static void s390_create_sclpconsole(const char *type, Chardev *chardev) static void ccw_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); + S390CcwMachineState *ms = S390_CCW_MACHINE(machine); int ret; VirtualCssBus *css_bus; DeviceState *dev; - s390_sclp_init(); + ms->sclp = SCLP(object_new(TYPE_SCLP)); + object_property_add_child(OBJECT(machine), TYPE_SCLP, OBJECT(ms->sclp)); + qdev_realize_and_unref(DEVICE(ms->sclp), NULL, &error_fatal); + /* init memory + setup max page size. Required for the CPU model */ s390_memory_init(machine->ram); diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 893e71a41b..d236dbaf0b 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -378,16 +378,6 @@ void sclp_service_interrupt(uint32_t sccb) } /* qemu object creation and initialization functions */ - -void s390_sclp_init(void) -{ - Object *new = object_new(TYPE_SCLP); - - object_property_add_child(qdev_get_machine(), TYPE_SCLP, new); - object_unref(new); - qdev_realize(DEVICE(new), NULL, &error_fatal); -} - static void sclp_realize(DeviceState *dev, Error **errp) { MachineState *machine = MACHINE(qdev_get_machine()); diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index c1d46e78af..7605d06bff 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -13,6 +13,7 @@ #include "hw/boards.h" #include "qom/object.h" +#include "hw/s390x/sclp.h" #define TYPE_S390_CCW_MACHINE "s390-ccw-machine" @@ -28,6 +29,8 @@ struct S390CcwMachineState { bool dea_key_wrap; bool pv; uint8_t loadparm[8]; + + SCLPDevice *sclp; }; #define S390_PTF_REASON_NONE (0x00 << 8) diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index b405a387b6..d32f6180e0 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -221,8 +221,6 @@ static inline int sccb_data_len(SCCB *sccb) return be16_to_cpu(sccb->h.length) - sizeof(sccb->h); } - -void s390_sclp_init(void); void sclp_service_interrupt(uint32_t sccb); void raise_irq_cpu_hotplug(void); int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code); From af4a3e32f3e1284662ebc1fd50a8c5e1776ebece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 2 May 2024 15:15:32 +0200 Subject: [PATCH 0575/2884] s390x/event-facility: Simplify sclp_get_event_facility_bus() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sclp_get_event_facility_bus() scans the whole machine to find a TYPE_SCLP_EVENTS_BUS object. The SCLPDevice instance is now available under the machine state, use it to simplify the lookup and adjust the creation of the consoles. Signed-off-by: Cédric Le Goater Message-ID: <20240502131533.377719-3-clg@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- hw/s390x/event-facility.c | 13 ++----------- hw/s390x/s390-virtio-ccw.c | 12 +++++++----- include/hw/s390x/event-facility.h | 2 +- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index f9829de953..06c1da0ece 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -523,16 +523,7 @@ static void register_types(void) type_init(register_types) -BusState *sclp_get_event_facility_bus(void) +BusState *sclp_get_event_facility_bus(SCLPEventFacility *ef) { - Object *busobj; - SCLPEventsBus *sbus; - - busobj = object_resolve_path_type("", TYPE_SCLP_EVENTS_BUS, NULL); - sbus = OBJECT_CHECK(SCLPEventsBus, busobj, TYPE_SCLP_EVENTS_BUS); - if (!sbus) { - return NULL; - } - - return &sbus->qbus; + return BUS(&ef->sbus); } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 159f67d05c..2afaf45ce6 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -237,13 +237,15 @@ static void s390_create_virtio_net(BusState *bus, const char *name) } } -static void s390_create_sclpconsole(const char *type, Chardev *chardev) +static void s390_create_sclpconsole(SCLPDevice *sclp, + const char *type, Chardev *chardev) { - BusState *ev_fac_bus = sclp_get_event_facility_bus(); + SCLPEventFacility *ef = sclp->event_facility; + BusState *ev_fac_bus = sclp_get_event_facility_bus(ef); DeviceState *dev; dev = qdev_new(type); - object_property_add_child(OBJECT(ev_fac_bus->parent), type, OBJECT(dev)); + object_property_add_child(OBJECT(ef), type, OBJECT(dev)); qdev_prop_set_chr(dev, "chardev", chardev); qdev_realize_and_unref(dev, ev_fac_bus, &error_fatal); } @@ -308,10 +310,10 @@ static void ccw_init(MachineState *machine) /* init consoles */ if (serial_hd(0)) { - s390_create_sclpconsole("sclpconsole", serial_hd(0)); + s390_create_sclpconsole(ms->sclp, "sclpconsole", serial_hd(0)); } if (serial_hd(1)) { - s390_create_sclpconsole("sclplmconsole", serial_hd(1)); + s390_create_sclpconsole(ms->sclp, "sclplmconsole", serial_hd(1)); } /* init the TOD clock */ diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 3ffd575d8f..ff874e792d 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -203,6 +203,6 @@ struct SCLPEventFacilityClass { bool (*event_pending)(SCLPEventFacility *ef); }; -BusState *sclp_get_event_facility_bus(void); +BusState *sclp_get_event_facility_bus(SCLPEventFacility *ef); #endif From 3d9836e46dbe1e46c39fe76a62d3085a71ddbf7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 2 May 2024 15:15:33 +0200 Subject: [PATCH 0576/2884] s390x/sclp: Simplify get_sclp_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_sclp_device() scans the whole machine to find a TYPE_SCLP object. Now that the SCLPDevice instance is available under the machine state, use it to simplify the lookup. While at it, remove the inline to let the compiler decide on how to optimize. Signed-off-by: Cédric Le Goater Message-ID: <20240502131533.377719-4-clg@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- hw/s390x/sclp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index d236dbaf0b..e725dcd5fd 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -21,13 +21,14 @@ #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/ipl.h" #include "hw/s390x/cpu-topology.h" +#include "hw/s390x/s390-virtio-ccw.h" -static inline SCLPDevice *get_sclp_device(void) +static SCLPDevice *get_sclp_device(void) { static SCLPDevice *sclp; if (!sclp) { - sclp = SCLP(object_resolve_path_type("", TYPE_SCLP, NULL)); + sclp = S390_CCW_MACHINE(qdev_get_machine())->sclp; } return sclp; } From 8aa2211e855df79ddd363e5f0d8c4d7d4c376e16 Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 29 Apr 2024 15:10:58 -0400 Subject: [PATCH 0577/2884] target/s390x: report deprecated-props in cpu-model-expansion reply Retain a list of deprecated features disjoint from any particular CPU model. A query-cpu-model-expansion reply will now provide a list of properties (i.e. features) that are flagged as deprecated. Example: { "return": { "model": { "name": "z14.2-base", "deprecated-props": [ "bpb", "csske" ], "props": { "pfmfi": false, "exrl": true, ...a lot more props... "skey": false, "vxpdeh2": false } } } } It is recommended that s390 guests operate with these features explicitly disabled to ensure compatibility with future hardware. Signed-off-by: Collin Walling Acked-by: Markus Armbruster Reviewed-by: David Hildenbrand Message-ID: <20240429191059.11806-2-walling@linux.ibm.com> Signed-off-by: Thomas Huth --- qapi/machine-target.json | 7 ++++++- target/s390x/cpu_features.c | 14 ++++++++++++++ target/s390x/cpu_features.h | 1 + target/s390x/cpu_models_sysemu.c | 8 ++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 29e695aa06..2942853092 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -20,11 +20,16 @@ # # @props: a dictionary of QOM properties to be applied # +# @deprecated-props: a list of properties that are flagged as deprecated +# by the CPU vendor. These props are a subset of the full model's +# definition list of properties. (since 9.1) +# # Since: 2.8 ## { 'struct': 'CpuModelInfo', 'data': { 'name': 'str', - '*props': 'any' } } + '*props': 'any', + '*deprecated-props': ['str'] } } ## # @CpuModelExpansionType: diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index d28eb65845..efafc9711c 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -212,6 +212,20 @@ void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque, }; } +void s390_get_deprecated_features(S390FeatBitmap features) +{ + static const int feats[] = { + /* CSSKE is deprecated on newer generations */ + S390_FEAT_CONDITIONAL_SSKE, + S390_FEAT_BPB, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(feats); i++) { + set_bit(feats[i], features); + } +} + #define FEAT_GROUP_INIT(_name, _group, _desc) \ { \ .name = _name, \ diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h index a9bd68a2e1..661a8cd6db 100644 --- a/target/s390x/cpu_features.h +++ b/target/s390x/cpu_features.h @@ -69,6 +69,7 @@ void s390_add_from_feat_block(S390FeatBitmap features, S390FeatType type, uint8_t *data); void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque, void (*fn)(const char *name, void *opaque)); +void s390_get_deprecated_features(S390FeatBitmap features); /* Definition of a CPU feature group */ typedef struct { diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index 15be729c3d..977fbc6522 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -206,6 +206,14 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, } else { info->props = QOBJECT(qdict); } + + /* features flagged as deprecated */ + bitmap_zero(bitmap, S390_FEAT_MAX); + s390_get_deprecated_features(bitmap); + + bitmap_and(bitmap, bitmap, model->def->full_feat, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(bitmap, &info->deprecated_props, list_add_feat); + info->has_deprecated_props = !!info->deprecated_props; } CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, From 6e55b32d45976a8e78cbd3bbdf6ed1148cb2662a Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Mon, 29 Apr 2024 15:10:59 -0400 Subject: [PATCH 0578/2884] target/s390x: flag te and cte as deprecated Add the CONSTRAINT_TRANSACTIONAL_EXE (cte) and TRANSACTIONAL_EXE (te) to the list of deprecated features. Signed-off-by: Collin Walling Reviewed-by: David Hildenbrand Message-ID: <20240429191059.11806-3-walling@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_features.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index efafc9711c..cb4e2b8920 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -218,6 +218,9 @@ void s390_get_deprecated_features(S390FeatBitmap features) /* CSSKE is deprecated on newer generations */ S390_FEAT_CONDITIONAL_SSKE, S390_FEAT_BPB, + /* Deprecated on z16 */ + S390_FEAT_CONSTRAINT_TRANSACTIONAL_EXE, + S390_FEAT_TRANSACTIONAL_EXE }; int i; From 95e0fb0afac678b13a70b476b4ad564569d6df8c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 6 May 2024 13:20:58 +0200 Subject: [PATCH 0579/2884] qemu-options: Deprecate "-runas" and introduce "-run-with user=..." instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old "-runas" option has the disadvantage that it is not visible in the QAPI schema, so it is not available via the normal introspection mechanisms. We've recently introduced the "-run-with" option for exactly this purpose, which is meant to handle the options that affect the runtime behavior. Thus let's introduce a "user=..." parameter here now and deprecate the old "-runas" option. Message-ID: <20240506112058.51446-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- docs/about/deprecated.rst | 6 ++++++ qemu-options.hx | 15 +++++++++++---- system/vl.c | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index e22acb17f2..6f709746db 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -61,6 +61,12 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is marked deprecated since 9.0, users have to ensure that all the topology members described with -smp are supported by the target machine. +``-runas`` (since 9.1) +---------------------- + +Use ``-run-with user=..`` instead. + + User-mode emulator command line arguments ----------------------------------------- diff --git a/qemu-options.hx b/qemu-options.hx index cf61f6b863..f5c01eeeb4 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4824,7 +4824,8 @@ DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ SRST ``-runas user`` Immediately before starting guest execution, drop root privileges, - switching to the specified user. + switching to the specified user. This option is deprecated, use + ``-run-with user=...`` instead. ERST DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env, @@ -4990,13 +4991,15 @@ DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL) #ifdef CONFIG_POSIX DEF("run-with", HAS_ARG, QEMU_OPTION_run_with, - "-run-with [async-teardown=on|off][,chroot=dir]\n" + "-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]\n" " Set miscellaneous QEMU process lifecycle options:\n" " async-teardown=on enables asynchronous teardown (Linux only)\n" - " chroot=dir chroot to dir just before starting the VM\n", + " chroot=dir chroot to dir just before starting the VM\n" + " user=username switch to the specified user before starting the VM\n" + " user=uid:gid ditto, but use specified user-ID and group-ID instead\n", QEMU_ARCH_ALL) SRST -``-run-with [async-teardown=on|off][,chroot=dir]`` +``-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]`` Set QEMU process lifecycle options. ``async-teardown=on`` enables asynchronous teardown. A new process called @@ -5013,6 +5016,10 @@ SRST ``chroot=dir`` can be used for doing a chroot to the specified directory immediately before starting the guest execution. This is especially useful in combination with -runas. + + ``user=username`` or ``user=uid:gid`` can be used to drop root privileges + by switching to the specified user (via username) or user and group + (via uid:gid) immediately before starting guest execution. ERST #endif diff --git a/system/vl.c b/system/vl.c index 7756eac81e..b031427440 100644 --- a/system/vl.c +++ b/system/vl.c @@ -773,6 +773,10 @@ static QemuOptsList qemu_run_with_opts = { .name = "chroot", .type = QEMU_OPT_STRING, }, + { + .name = "user", + .type = QEMU_OPT_STRING, + }, { /* end of list */ } }, }; @@ -3586,6 +3590,7 @@ void qemu_init(int argc, char **argv) break; #if defined(CONFIG_POSIX) case QEMU_OPTION_runas: + warn_report("-runas is deprecated, use '-run-with user=...' instead"); if (!os_set_runas(optarg)) { error_report("User \"%s\" doesn't exist" " (and is not :)", @@ -3612,6 +3617,16 @@ void qemu_init(int argc, char **argv) if (str) { os_set_chroot(str); } + str = qemu_opt_get(opts, "user"); + if (str) { + if (!os_set_runas(str)) { + error_report("User \"%s\" doesn't exist" + " (and is not :)", + optarg); + exit(1); + } + } + break; } #endif /* CONFIG_POSIX */ From 0d497106a71a3b17b0228cb87922ef794296cb24 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 9 May 2024 16:47:45 +0800 Subject: [PATCH 0580/2884] tests/qtest: Add some test cases support on LoongArch Add boot-serial-test and filter test cases support on LoongArch system. Signed-off-by: Bibo Mao Message-ID: <20240509084745.2514607-1-maobibo@loongson.cn> Signed-off-by: Thomas Huth --- tests/qtest/boot-serial-test.c | 10 ++++++++++ tests/qtest/meson.build | 3 +++ 2 files changed, 13 insertions(+) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index e3b7d65fe5..df389adeeb 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -26,6 +26,14 @@ static const uint8_t bios_avr[] = { 0x80, 0x93, 0xc6, 0x00, /* sts 0x00C6, r24 ; Output 'T' */ }; +static const uint8_t bios_loongarch64[] = { + 0x0c, 0xc0, 0x3f, 0x14, /* lu12i.w $t0, 0x1fe00 */ + 0x8c, 0x81, 0x87, 0x03, /* ori $t0, $t0, 0x1e0 */ + 0x0d, 0x50, 0x81, 0x03, /* li.w $t1, 'T' */ + 0x8d, 0x01, 0x00, 0x29, /* st.b $t1, $t0, 0 */ + 0xff, 0xf3, 0xff, 0x53, /* b -16 # loop */ +}; + static const uint8_t kernel_mcf5208[] = { 0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */ 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ @@ -167,6 +175,8 @@ static const testdef_t tests[] = { { "sparc", "SS-600MP", "", "TMS390Z55" }, { "sparc64", "sun4u", "", "UltraSPARC" }, { "s390x", "s390-ccw-virtio", "", "device" }, + { "loongarch64", "virt", "-cpu max", "TT", sizeof(bios_loongarch64), + NULL, bios_loongarch64 }, { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, { "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube }, { "microblaze", "petalogix-s3adsp1800", "", "TT", diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 6f2f594ace..86293051dc 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -139,6 +139,9 @@ qtests_hppa = ['boot-serial-test'] + \ qtests_filter + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) +qtests_loongarch64 = qtests_filter + \ + ['boot-serial-test'] + qtests_m68k = ['boot-serial-test'] + \ qtests_filter From 9f07e47a5e96c88c1d2892fbdcbc8ff0437b7ac3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 17:44:12 +0200 Subject: [PATCH 0581/2884] target/i386: remove PCOMMIT from TCG, deprecate property The PCOMMIT instruction was never included in any physical processor. TCG implements it as a no-op instruction, but its utility is debatable to say the least. Drop it from the decoder since it is only available with "-cpu max", which does not guarantee migration compatibility across versions, and deprecate the property just in case someone is using it as "pcommit=off". Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 8 ++++++++ target/i386/cpu.c | 2 +- target/i386/cpu.h | 2 -- target/i386/tcg/translate.c | 12 +----------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 5c3ca47801..40585ca7d5 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -432,6 +432,14 @@ Backend ``memory`` (since 9.0) CPU device properties ''''''''''''''''''''' +``pcommit`` on x86 (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The PCOMMIT instruction was never included in any physical processor. +It was implemented as a no-op instruction in TCG up to QEMU 9.0, but +only with ``-cpu max`` (which does not guarantee migration compatibility +across versions). + ``pmu-num=n`` on RISC-V CPUs (since 8.2) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1058b6803f..79372de8c5 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -713,7 +713,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #endif #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ - CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ + CPUID_7_0_EBX_CLFLUSHOPT | \ CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \ CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_RDSEED | \ CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_KERNEL_FEATURES) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1e0d2c915f..ccccb62fc3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -816,8 +816,6 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EBX_SMAP (1U << 20) /* AVX-512 Integer Fused Multiply Add */ #define CPUID_7_0_EBX_AVX512IFMA (1U << 21) -/* Persistent Commit */ -#define CPUID_7_0_EBX_PCOMMIT (1U << 22) /* Flush a Cache Line Optimized */ #define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23) /* Cache Line Write Back */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3842b29484..7d9f6b5c55 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4487,17 +4487,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } goto unknown_op; - case 0xf8: /* sfence / pcommit */ - if (prefixes & PREFIX_DATA) { - /* pcommit */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - break; - } - /* fallthru */ - case 0xf9 ... 0xff: /* sfence */ + case 0xf8 ... 0xff: /* sfence */ if (!(s->cpuid_features & CPUID_SSE) || (prefixes & PREFIX_LOCK)) { goto illegal_op; From 41c685dc59bb611096f3bb6a663cfa82e4cba97b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 12:38:10 +0200 Subject: [PATCH 0582/2884] target/i386: fix operand size for DATA16 REX.W POPCNT According to the manual, 32-bit vs 64-bit is governed by REX.W and REX ignores the 0x66 prefix. This can be confirmed with this program: #include int main() { int x = 0x12340000; int y; asm("popcntl %1, %0" : "=r" (y) : "r" (x)); printf("%x\n", y); asm("mov $-1, %0; .byte 0x66; popcntl %1, %0" : "+r" (y) : "r" (x)); printf("%x\n", y); asm("mov $-1, %0; .byte 0x66; popcntq %q1, %q0" : "+r" (y) : "r" (x)); printf("%x\n", y); } which prints 5/ffff0000/5 on real hardware and 5/ffff0000/ffff0000 on QEMU. Cc: qemu-stable@nongnu.org Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 7d9f6b5c55..5366dc32dd 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -411,16 +411,6 @@ static inline MemOp mo_stacksize(DisasContext *s) return CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16; } -/* Select only size 64 else 32. Used for SSE operand sizes. */ -static inline MemOp mo_64_32(MemOp ot) -{ -#ifdef TARGET_X86_64 - return ot == MO_64 ? MO_64 : MO_32; -#else - return MO_32; -#endif -} - /* Select size 8 if lsb of B is clear, else OT. Used for decoding byte vs word opcodes. */ static inline MemOp mo_b_d(int b, MemOp ot) @@ -4545,12 +4535,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - if (s->prefix & PREFIX_DATA) { - ot = MO_16; - } else { - ot = mo_64_32(dflag); - } - + ot = dflag; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); gen_extu(ot, s->T0); tcg_gen_mov_tl(cpu_cc_src, s->T0); From 40a3ec7b5ffde500789d016660a171057d6b467c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 15:55:47 +0200 Subject: [PATCH 0583/2884] target/i386: rdpkru/wrpkru are no-prefix instructions Reject 0x66/0xf3/0xf2 in front of them. Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5366dc32dd..3da4fdf64c 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3907,7 +3907,8 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); break; case 0xee: /* rdpkru */ - if (prefixes & PREFIX_LOCK) { + if (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ)) { goto illegal_op; } tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); @@ -3915,7 +3916,8 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); break; case 0xef: /* wrpkru */ - if (prefixes & PREFIX_LOCK) { + if (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ)) { goto illegal_op; } tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], From 3fabbe0b7d458d6380f4b3246b8b32400f6bd1d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 6 May 2024 17:34:55 +0200 Subject: [PATCH 0584/2884] target/i386: move prefetch and multi-byte UD/NOP to new decoder These are trivial to add, and moving them to the new decoder fixes some corner cases: raising #UD instead of an instruction fetch page fault for the undefined opcodes, and incorrectly rejecting 0F 18 prefetches with register operands (which are treated as reserved NOPs). Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 24 +++++++++++++++++++++--- target/i386/tcg/decode-new.h | 1 + target/i386/tcg/emit.c.inc | 5 +++++ target/i386/tcg/translate.c | 30 ------------------------------ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 0e1811399f..141ab2bc56 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -55,6 +55,10 @@ * mask could be applied (and the original sign-extended value would be * optimized away by TCG) in the emitter function. * + * Finally, a "nop" operand type is used for multi-byte NOPs. It accepts + * any value of mod including 11b (unlike M) but it does not try to + * interpret the operand (like M). + * * Vector operands * --------------- * @@ -1056,6 +1060,16 @@ static const X86OpEntry opcodes_0F[256] = { [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), [0xa1] = X86_OP_ENTRYw(POP, FS, w), + [0x0b] = X86_OP_ENTRY0(UD), /* UD2 */ + [0x0d] = X86_OP_ENTRY1(NOP, M,v), /* 3DNow! prefetch */ + + [0x18] = X86_OP_ENTRY1(NOP, nop,v), /* prefetch/reserved NOP */ + [0x19] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1c] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1d] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1e] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1f] = X86_OP_ENTRY1(NOP, nop,v), /* NOP/reserved NOP */ + [0x28] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1 p_00_66), /* MOVAPS */ [0x29] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 p_00_66), /* MOVAPS */ [0x2A] = X86_OP_GROUP0(0F2A), @@ -1135,6 +1149,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ + /* decoded as modrm, which is visible as a difference between page fault and #UD */ + [0xb9] = X86_OP_ENTRYr(UD, nop,v), /* UD1 */ [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */ [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */ @@ -1206,7 +1222,7 @@ static const X86OpEntry opcodes_0F[256] = { [0xfc] = X86_OP_ENTRY3(PADDB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xfd] = X86_OP_ENTRY3(PADDW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xfe] = X86_OP_ENTRY3(PADDD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - /* 0xff = UD0 */ + [0xff] = X86_OP_ENTRYr(UD, nop,v), /* UD0 */ }; static void do_decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) @@ -1852,6 +1868,8 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, if ((modrm >> 6) == 3) { return false; } + /* fall through */ + case X86_TYPE_nop: /* modrm operand decoded but not fetched */ get_modrm: decode_modrm(s, env, decode, op, type); break; @@ -2397,8 +2415,8 @@ static void disas_insn(DisasContext *s, CPUState *cpu) switch (b) { case 0x00 ... 0x03: /* mostly privileged instructions */ case 0x05 ... 0x09: - case 0x0d: /* 3DNow! prefetch */ - case 0x18 ... 0x23: /* prefetch, MPX, mov from/to CR and DR */ + case 0x1a ... 0x1b: /* MPX */ + case 0x20 ... 0x23: /* mov from/to CR and DR */ case 0x30 ... 0x35: /* more privileged instructions */ case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */ case 0xaa ... 0xae: /* RSM, SHRD, grp15 */ diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 2ea06b4478..51ef0e621b 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -50,6 +50,7 @@ typedef enum X86OpType { X86_TYPE_EM, /* modrm byte selects an ALU memory operand */ X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */ X86_TYPE_I_unsigned, /* Immediate, zero-extended */ + X86_TYPE_nop, /* modrm operand decoded but not loaded into s->T{0,1} */ X86_TYPE_2op, /* 2-operand RMW instruction */ X86_TYPE_LoBits, /* encoded in bits 0-2 of the operand + REX.B */ X86_TYPE_0, /* Hard-coded GPRs (RAX..RDI) */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 58f255873f..2dee33dd48 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3517,6 +3517,11 @@ static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_SUBB + ot); } +static void gen_UD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_illegal_opcode(s); +} + static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { assert(!s->vex_l); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 3da4fdf64c..de87775016 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4019,25 +4019,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) set_cc_op(s, CC_OP_EFLAGS); } break; - case 0x118: - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* prefetchnta */ - case 1: /* prefetchnt0 */ - case 2: /* prefetchnt0 */ - case 3: /* prefetchnt0 */ - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - /* nothing more to do */ - break; - default: /* nop (multi byte) */ - gen_nop_modrm(env, s, modrm); - break; - } - break; case 0x11a: modrm = x86_ldub_code(env, s); if (s->flags & HF_MPX_EN_MASK) { @@ -4229,10 +4210,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_nop_modrm(env, s, modrm); break; - case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ - modrm = x86_ldub_code(env, s); - gen_nop_modrm(env, s, modrm); - break; case 0x120: /* mov reg, crN */ case 0x122: /* mov crN, reg */ @@ -4506,13 +4483,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } break; - case 0x10d: /* 3DNow! prefetch(w) */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - break; case 0x1aa: /* rsm */ gen_svm_check_intercept(s, SVM_EXIT_RSM); if (!(s->flags & HF_SMM_MASK)) From fe01af5d47d4cf7fdf90c54d43f784e5068c8d72 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 11:10:54 +0200 Subject: [PATCH 0585/2884] target/i386: fix feature dependency for WAITPKG The VMX feature bit depends on general availability of WAITPKG, not the other way round. Fixes: 33cc88261c3 ("target/i386: add support for VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE", 2023-08-28) Cc: qemu-stable@nongnu.org Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 79372de8c5..cfe7c92d6b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1551,8 +1551,8 @@ static FeatureDep feature_dependencies[] = { .to = { FEAT_SVM, ~0ull }, }, { - .from = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, - .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + .from = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + .to = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, }, }; From ff5b5739f97d08d9ca984ec8016b54487a76401b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 12:41:38 +0200 Subject: [PATCH 0586/2884] tests/tcg: cover lzcnt/tzcnt/popcnt Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- tests/tcg/i386/test-i386.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index 864c4e620d..ce3bf74b5a 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -715,6 +715,30 @@ void test_mul(void) printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\ } +void test_xcnt(void) +{ + TEST_BSX(tzcntw, "w", 0); + TEST_BSX(tzcntw, "w", 0x12340128); + TEST_BSX(lzcntw, "w", 0); + TEST_BSX(lzcntw, "w", 0x12340128); + TEST_BSX(popcntw, "w", 0); + TEST_BSX(popcntw, "w", 0x12340128); + TEST_BSX(tzcntl, "k", 0); + TEST_BSX(tzcntl, "k", 0x00340128); + TEST_BSX(lzcntl, "k", 0); + TEST_BSX(lzcntl, "k", 0x00340128); + TEST_BSX(popcntl, "k", 0); + TEST_BSX(popcntl, "k", 0x00340128); +#if defined(__x86_64__) + TEST_BSX(tzcntq, "", 0); + TEST_BSX(tzcntq, "", 0x003401281234); + TEST_BSX(lzcntq, "", 0); + TEST_BSX(lzcntq, "", 0x003401281234); + TEST_BSX(popcntq, "", 0); + TEST_BSX(popcntq, "", 0x003401281234); +#endif +} + void test_bsx(void) { TEST_BSX(bsrw, "w", 0); @@ -2162,6 +2186,7 @@ int main(int argc, char **argv) func(); } test_bsx(); + test_xcnt(); test_mul(); test_jcc(); test_loop(); From 23b1f53c2c8990ed745acede171e49645af3d6d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 12:48:22 +0200 Subject: [PATCH 0587/2884] configure: quote -D options that are passed through to meson Ensure that they go through unmodified, instead of removing one layer of quoting. -D is a pretty specialized option and most options that can have spaces do not need it (for example, c_args is covered by --extra-cflags). Therefore it's unlikely that this causes actual trouble. However, a somewhat realistic failure case would be with -Dpkg_config_path and a pkg-config directory that contains spaces. Cc: qemu-stable@nongnu.org Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 1dca3d94c0..330664786d 100755 --- a/configure +++ b/configure @@ -762,7 +762,7 @@ for opt do --*) meson_option_parse "$opt" "$optarg" ;; # Pass through -Dxxxx options to meson - -D*) meson_options="$meson_options $opt" + -D*) meson_option_add "$opt" ;; esac done From d1b223dd07637621f0439701d7d248f87995a275 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 11:29:31 +0200 Subject: [PATCH 0588/2884] sh4: select correct components for no-board build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alex Bennée Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 5 +++-- hw/sh4/meson.build | 2 +- target/sh4/Kconfig | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 9836d81c24..786c8f9209 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, s390x, sh4, sh4eb, x86_64 +# does not build without boards: i386, s390x, x86_64 build-without-defaults: extends: .native_build_job_template needs: @@ -665,7 +665,8 @@ build-without-defaults: --disable-qom-cast-debug --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu - mips-softmmu mips64-softmmu mipsel-softmmu sparc-softmmu + mips-softmmu mips64-softmmu mipsel-softmmu + sh4-softmmu sh4eb-softmmu sparc-softmmu sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/hw/sh4/meson.build b/hw/sh4/meson.build index 424d5674de..70e814c3a2 100644 --- a/hw/sh4/meson.build +++ b/hw/sh4/meson.build @@ -1,5 +1,5 @@ sh4_ss = ss.source_set() -sh4_ss.add(files( +sh4_ss.add(when: 'CONFIG_SH7750', if_true: files( 'sh7750.c', 'sh7750_regnames.c', )) diff --git a/target/sh4/Kconfig b/target/sh4/Kconfig index 2397c86028..93b92f1e48 100644 --- a/target/sh4/Kconfig +++ b/target/sh4/Kconfig @@ -1,2 +1,4 @@ config SH4 bool + # needed for sh_intc_get_pending_vector + select SH_INTC From ef7c4a97bfdfdb913b03720d3be3c49579cf1835 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:32 +0200 Subject: [PATCH 0589/2884] s390x: move s390_cpu_addr2state to target/s390x/sigp.c This function has no dependency on the virtio-ccw machine type, though it assumes that the CPU address corresponds to the core_id and the index. If there is any need of something different or more fancy (unlikely) S390 can include a MachineClass subclass and implement it there. For now, move it to sigp.c for simplicity. Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/s390-virtio-ccw.c | 16 ---------------- target/s390x/sigp.c | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 2afaf45ce6..42628fc45d 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -50,22 +50,6 @@ static Error *pv_mig_blocker; -S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) -{ - static MachineState *ms; - - if (!ms) { - ms = MACHINE(qdev_get_machine()); - g_assert(ms->possible_cpus); - } - - /* CPU address corresponds to the core_id and the index */ - if (cpu_addr >= ms->possible_cpus->len) { - return NULL; - } - return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu); -} - static S390CPU *s390x_new_cpu(const char *typename, uint32_t core_id, Error **errp) { diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 9dd977349a..ad0ad61177 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" +#include "hw/boards.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "exec/address-spaces.h" @@ -435,6 +436,22 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param, return SIGP_CC_STATUS_STORED; } +S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) +{ + static MachineState *ms; + + if (!ms) { + ms = MACHINE(qdev_get_machine()); + g_assert(ms->possible_cpus); + } + + /* CPU address corresponds to the core_id and the index */ + if (cpu_addr >= ms->possible_cpus->len) { + return NULL; + } + return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu); +} + int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3) { uint64_t *status_reg = &env->regs[r1]; From 9d1b0f5bf515a0dd8e4174d9f3aca2c549727ef6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:33 +0200 Subject: [PATCH 0590/2884] s390_flic: add migration-enabled property Instead of mucking with css_migration_enabled(), add a property specific to the FLIC device, similar to what is done for TYPE_S390_STATTRIB. Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-3-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/intc/s390_flic.c | 6 +++++- hw/s390x/s390-virtio-ccw.c | 1 + include/hw/s390x/s390_flic.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index f4a848460b..7f93080087 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -405,6 +405,8 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) static Property s390_flic_common_properties[] = { DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState, adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), + DEFINE_PROP_BOOL("migration-enabled", S390FLICState, + migration_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -457,7 +459,9 @@ type_init(qemu_s390_flic_register_types) static bool adapter_info_so_needed(void *opaque) { - return css_migration_enabled(); + S390FLICState *fs = S390_FLIC_COMMON(opaque); + + return fs->migration_enabled; } const VMStateDescription vmstate_adapter_info_so = { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 42628fc45d..756b4f40ae 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1182,6 +1182,7 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); static GlobalProperty compat[] = { { TYPE_S390_STATTRIB, "migration-enabled", "off", }, + { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", }, }; ccw_machine_2_10_class_options(mc); diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 3907a13d07..bcb081def5 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -47,6 +47,7 @@ struct S390FLICState { /* to limit AdapterRoutes.num_routes for compat */ uint32_t adapter_routes_max_batch; bool ais_supported; + bool migration_enabled; }; From a55ae46683f289c46a0c0a807dbb89229a1e00cd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:34 +0200 Subject: [PATCH 0591/2884] s390: move css_migration_enabled from machine to css.c The CSS subsystem uses global variables, just face the truth and use a variable also for whether the CSS vmstate is in use; remove the indirection of fetching it from the machine type, which makes the TCG code depend unnecessarily on the virtio-ccw machine. Signed-off-by: Paolo Bonzini Message-ID: <20240509170044.190795-4-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/css.c | 10 +++++++--- hw/s390x/s390-virtio-ccw.c | 15 +++------------ include/hw/s390x/css.h | 6 ++++++ include/hw/s390x/s390-virtio-ccw.h | 7 ------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 295530963a..b2d5327dbf 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -23,6 +23,8 @@ #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-ccw.h" +bool css_migration_enabled = true; + typedef struct CrwContainer { CRW crw; QTAILQ_ENTRY(CrwContainer) sibling; @@ -180,7 +182,7 @@ static const VMStateDescription vmstate_orb = { static bool vmstate_schdev_orb_needed(void *opaque) { - return css_migration_enabled(); + return css_migration_enabled; } static const VMStateDescription vmstate_schdev_orb = { @@ -388,7 +390,7 @@ static int subch_dev_post_load(void *opaque, int version_id) css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s); } - if (css_migration_enabled()) { + if (css_migration_enabled) { /* No compat voodoo to do ;) */ return 0; } @@ -412,7 +414,9 @@ static int subch_dev_post_load(void *opaque, int version_id) void css_register_vmstate(void) { - vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); + if (css_migration_enabled) { + vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); + } } IndAddr *get_indicator(hwaddr ind_addr, int len) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 756b4f40ae..3d0bc3e7f2 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -283,11 +283,9 @@ static void ccw_init(MachineState *machine) s390_enable_css_support(s390_cpu_addr2state(0)); ret = css_create_css_image(VIRTUAL_CSSID, true); - assert(ret == 0); - if (css_migration_enabled()) { - css_register_vmstate(); - } + + css_register_vmstate(); /* Create VirtIO network adapters */ s390_create_virtio_net(BUS(css_bus), mc->default_nic); @@ -749,7 +747,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) s390mc->ri_allowed = true; s390mc->cpu_model_allowed = true; - s390mc->css_migration_enabled = true; s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; mc->init = ccw_init; @@ -819,11 +816,6 @@ static const TypeInfo ccw_machine_info = { }, }; -bool css_migration_enabled(void) -{ - return get_machine_class()->css_migration_enabled; -} - #define DEFINE_CCW_MACHINE(suffix, verstr, latest) \ static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \ void *data) \ @@ -1179,7 +1171,6 @@ static void ccw_machine_2_9_instance_options(MachineState *machine) static void ccw_machine_2_9_class_options(MachineClass *mc) { - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); static GlobalProperty compat[] = { { TYPE_S390_STATTRIB, "migration-enabled", "off", }, { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", }, @@ -1188,7 +1179,7 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) ccw_machine_2_10_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - s390mc->css_migration_enabled = false; + css_migration_enabled = false; } DEFINE_CCW_MACHINE(2_9, "2.9", false); diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index ba72ee3dd2..8289e45837 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -333,4 +333,10 @@ static inline int ccw_dstream_read_buf(CcwDataStream *cds, void *buff, int len) #define ccw_dstream_read(cds, v) ccw_dstream_read_buf((cds), &(v), sizeof(v)) #define ccw_dstream_write(cds, v) ccw_dstream_write_buf((cds), &(v), sizeof(v)) +/** + * true if (vmstate based) migration of the channel subsystem + * is enabled, false if it is disabled. + */ +extern bool css_migration_enabled; + #endif diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 7605d06bff..996864a34e 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -46,7 +46,6 @@ struct S390CcwMachineClass { /*< public >*/ bool ri_allowed; bool cpu_model_allowed; - bool css_migration_enabled; bool hpage_1m_allowed; int max_threads; }; @@ -58,10 +57,4 @@ bool cpu_model_allowed(void); /* 1M huge page mappings allowed by the machine */ bool hpage_1m_allowed(void); -/** - * Returns true if (vmstate based) migration of the channel subsystem - * is enabled, false if it is disabled. - */ -bool css_migration_enabled(void); - #endif From e799b65faef129f2905bd9bf66c30aaaa7115dac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:35 +0200 Subject: [PATCH 0592/2884] s390x: select correct components for no-board build Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-5-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- target/s390x/Kconfig | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 786c8f9209..8ca3e0586c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, s390x, x86_64 +# does not build without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template needs: @@ -666,7 +666,7 @@ build-without-defaults: --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu mips-softmmu mips64-softmmu mipsel-softmmu - sh4-softmmu sh4eb-softmmu sparc-softmmu + s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/target/s390x/Kconfig b/target/s390x/Kconfig index 72da48136c..d886be48b4 100644 --- a/target/s390x/Kconfig +++ b/target/s390x/Kconfig @@ -1,2 +1,4 @@ config S390X bool + select PCI + select S390_FLIC From fa5365e8b6eff888e99f31cd65afefd3643e5d0e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:36 +0200 Subject: [PATCH 0593/2884] tests/qtest: s390x: fix operation in a build without any boards or devices Do the bare minimum to ensure that at least a vanilla --without-default-devices build works for all targets except i386, x86_64 and ppc64. In particular this fixes s390x-softmmu; i386 and x86_64 have about a dozen failing tests that do not pass -M and therefore require a default machine type; ppc64 has the same issue, though only with numa-test. If we can for now ignore the cases where boards and devices are picked by hand, drive_del-test however can be fixed easily; almost all tests check for the virtio-blk or virtio-scsi device that they use, and are already skipped. Only one didn't get the memo; plus another one does not need a machine at all and can be run with -M none. Signed-off-by: Paolo Bonzini Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-6-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- tests/qtest/drive_del-test.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 8a6f3ac963..7b67a4bbee 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -173,7 +173,7 @@ static void test_drive_without_dev(void) QTestState *qts; /* Start with an empty drive */ - qts = qtest_init("-drive if=none,id=drive0"); + qts = qtest_init("-drive if=none,id=drive0 -M none"); /* Delete the drive */ drive_del(qts); @@ -192,6 +192,11 @@ static void test_after_failed_device_add(void) QDict *response; QTestState *qts; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + snprintf(driver, sizeof(driver), "virtio-blk-%s", qvirtio_get_dev_type()); From 0973996fe47bfca8f0bf552728e36682d642c9cc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:37 +0200 Subject: [PATCH 0594/2884] xen: initialize legacy backends from xen_bus_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for moving the calls to xen_be_register() under the control of xen_bus_init(), using the normal xen_backend_init() method that is used by the "modern" backends. This requires the xenstore global variable to be initialized, which is done by xen_be_init(). To ensure that everything is ready at the time the xen_backend_init() functions are called, remove the xen_be_init() function from all the boards and place it directly in xen_bus_init(). Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240509170044.190795-7-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 - hw/xen/xen-bus.c | 4 ++++ hw/xen/xen-hvm-common.c | 2 -- hw/xenpv/xen_machine_pv.c | 5 +---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 505ea750f4..19f21953b4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1250,7 +1250,6 @@ void pc_basic_device_init(struct PCMachineState *pcms, pci_create_simple(pcms->pcibus, -1, "xen-platform"); } xen_bus_init(); - xen_be_init(); } #endif diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index fb82cc33e4..95b207ac8b 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -13,6 +13,7 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" #include "hw/xen/xen-backend.h" +#include "hw/xen/xen-legacy-backend.h" /* xen_be_init() */ #include "hw/xen/xen-bus.h" #include "hw/xen/xen-bus-helper.h" #include "monitor/monitor.h" @@ -329,6 +330,9 @@ static void xen_bus_realize(BusState *bus, Error **errp) goto fail; } + /* Initialize legacy backend core & drivers */ + xen_be_init(); + if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ "domid", NULL, "%u", &domid) == 1) { xenbus->backend_id = domid; diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index 1627da7398..2d1b032121 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -872,8 +872,6 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, xen_bus_init(); - xen_be_init(); - return; err: diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 1130d1a147..b500ce0989 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -34,8 +34,7 @@ static void xen_init_pv(MachineState *machine) { setup_xen_backend_ops(); - /* Initialize backend core & drivers */ - xen_be_init(); + xen_bus_init(); switch (xen_mode) { case XEN_ATTACH: @@ -60,8 +59,6 @@ static void xen_init_pv(MachineState *machine) vga_interface_created = true; } - xen_bus_init(); - /* config cleanup hook */ atexit(xen_config_cleanup); } From 88f5ed7017897bc3108463e75e3b4828032a3992 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:38 +0200 Subject: [PATCH 0595/2884] xen: register legacy backends via xen_backend_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is okay to register legacy backends in the middle of xen_bus_init(). All that the registration does is record the existence of the backend in xenstore. This makes it possible to remove them from the build without introducing undefined symbols in xen_be_init(). It also removes the need for the backend_register callback, whose only purpose is to avoid registering nonfunctional backends. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240509170044.190795-8-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/9pfs/xen-9p-backend.c | 8 +++++++- hw/display/xenfb.c | 8 +++++++- hw/usb/xen-usb.c | 14 ++++---------- hw/xen/xen-legacy-backend.c | 16 ---------------- include/hw/xen/xen-legacy-backend.h | 14 ++------------ include/hw/xen/xen_pvdev.h | 1 - 6 files changed, 20 insertions(+), 41 deletions(-) diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 4aa9c8c736..a3ac53f989 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -513,7 +513,7 @@ static void xen_9pfs_alloc(struct XenLegacyDevice *xendev) xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); } -struct XenDevOps xen_9pfs_ops = { +static struct XenDevOps xen_9pfs_ops = { .size = sizeof(Xen9pfsDev), .flags = DEVOPS_FLAG_NEED_GNTDEV, .alloc = xen_9pfs_alloc, @@ -522,3 +522,9 @@ struct XenDevOps xen_9pfs_ops = { .disconnect = xen_9pfs_disconnect, .free = xen_9pfs_free, }; + +static void xen_9pfs_register_backend(void) +{ + xen_be_register("9pfs", &xen_9pfs_ops); +} +xen_backend_init(xen_9pfs_register_backend); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index b2130a0d70..27536bfce0 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -972,7 +972,7 @@ static void fb_event(struct XenLegacyDevice *xendev) /* -------------------------------------------------------------------- */ -struct XenDevOps xen_kbdmouse_ops = { +static struct XenDevOps xen_kbdmouse_ops = { .size = sizeof(struct XenInput), .init = input_init, .initialise = input_initialise, @@ -995,3 +995,9 @@ static const GraphicHwOps xenfb_ops = { .gfx_update = xenfb_update, .ui_info = xenfb_ui_info, }; + +static void xen_vkbd_register_backend(void) +{ + xen_be_register("vkbd", &xen_kbdmouse_ops); +} +xen_backend_init(xen_vkbd_register_backend); diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 09ec326aea..416623f956 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -1083,7 +1083,7 @@ static void usbback_event(struct XenLegacyDevice *xendev) qemu_bh_schedule(usbif->bh); } -struct XenDevOps xen_usb_ops = { +static struct XenDevOps xen_usb_ops = { .size = sizeof(struct usbback_info), .flags = DEVOPS_FLAG_NEED_GNTDEV, .init = usbback_init, @@ -1095,15 +1095,9 @@ struct XenDevOps xen_usb_ops = { .event = usbback_event, }; -#else /* USBIF_SHORT_NOT_OK */ - -static int usbback_not_supported(void) +static void xen_usb_register_backend(void) { - return -EINVAL; + xen_be_register("qusb", &xen_usb_ops); } - -struct XenDevOps xen_usb_ops = { - .backend_register = usbback_not_supported, -}; - +xen_backend_init(xen_usb_register_backend); #endif diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 124dd5f3d6..6f0b300a42 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -622,27 +622,11 @@ void xen_be_init(void) qbus_set_bus_hotplug_handler(xen_sysbus); xen_set_dynamic_sysbus(); - - xen_be_register("vkbd", &xen_kbdmouse_ops); -#ifdef CONFIG_VIRTFS - xen_be_register("9pfs", &xen_9pfs_ops); -#endif -#ifdef CONFIG_USB_LIBUSB - xen_be_register("qusb", &xen_usb_ops); -#endif } int xen_be_register(const char *type, struct XenDevOps *ops) { char path[50]; - int rc; - - if (ops->backend_register) { - rc = ops->backend_register(); - if (rc) { - return rc; - } - } snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid, type); diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 2cca174778..979c4ea04c 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -66,18 +66,8 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, return xen_be_unmap_grant_refs(xendev, ptr, &ref, 1); } -/* actual backend drivers */ -extern struct XenDevOps xen_console_ops; /* xen_console.c */ -extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ -extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ -extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ -#ifdef CONFIG_VIRTFS -extern struct XenDevOps xen_9pfs_ops; /* xen-9p-backend.c */ -#endif -extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ -#ifdef CONFIG_USB_LIBUSB -extern struct XenDevOps xen_usb_ops; /* xen-usb.c */ -#endif +/* backend drivers not included in all machines */ +extern struct XenDevOps xen_framebuffer_ops; /* xenfb.c */ /* configuration (aka xenbus setup) */ void xen_config_cleanup(void); diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index ddad4b9f36..fdf84f47af 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -29,7 +29,6 @@ struct XenDevOps { const char *node); void (*frontend_changed)(struct XenLegacyDevice *xendev, const char *node); - int (*backend_register)(void); }; struct XenLegacyDevice { From 7974e51342775c87f6e759a8c525db1045ddfa24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:39 +0200 Subject: [PATCH 0596/2884] i386: correctly select code in hw/i386 that depends on other components fw_cfg.c and vapic.c are currently included unconditionally but depend on other components. vapic.c depends on the local APIC, while fw_cfg.c includes a piece of AML builder code that depends on CONFIG_ACPI. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-9-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/fw_cfg.c | 2 ++ hw/i386/meson.build | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d802d2787f..6e0d9945d0 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -203,6 +203,7 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) fw_cfg_add_file(fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); } +#ifdef CONFIG_ACPI void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) { /* @@ -229,3 +230,4 @@ void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } +#endif diff --git a/hw/i386/meson.build b/hw/i386/meson.build index d8b70ef3e9..d9da676038 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -1,12 +1,12 @@ i386_ss = ss.source_set() i386_ss.add(files( 'fw_cfg.c', - 'vapic.c', 'e820_memory_layout.c', 'multiboot.c', 'x86.c', )) +i386_ss.add(when: 'CONFIG_APIC', if_true: files('vapic.c')) i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), if_false: files('x86-iommu-stub.c')) i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), From b348fdcdac9f9fc70be9ae56c54e41765e9aae24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:40 +0200 Subject: [PATCH 0597/2884] i386: pc: remove unnecessary MachineClass overrides There is no need to override these fields of MachineClass because they are already set to the right value in the superclass. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-10-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 3 --- hw/i386/x86.c | 6 +++--- include/hw/i386/x86.h | 4 ---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 19f21953b4..bfb46e9b54 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1826,9 +1826,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; mc->hotplug_allowed = pc_hotplug_allowed; - mc->cpu_index_to_instance_props = x86_cpu_index_to_props; - mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; - mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index c61f4ebfa6..fcef652c1e 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -443,7 +443,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, numa_cpu_pre_plug(cpu_slot, dev, errp); } -CpuInstanceProperties +static CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -453,7 +453,7 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) return possible_cpus->cpus[cpu_index].props; } -int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) +static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { X86CPUTopoIDs topo_ids; X86MachineState *x86ms = X86_MACHINE(ms); @@ -467,7 +467,7 @@ int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) return topo_ids.pkg_id % ms->numa_state->num_nodes; } -const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) +static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { X86MachineState *x86ms = X86_MACHINE(ms); unsigned int max_cpus = ms->smp.max_cpus; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index d7b7d3f3ce..c2062db13f 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -114,10 +114,6 @@ uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp); void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); -CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, - unsigned cpu_index); -int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx); -const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms); CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx); void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count); void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, From b061f0598b9231f7992aff4fcdf3f336f9747d11 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:41 +0200 Subject: [PATCH 0598/2884] hw/i386: split x86.c in multiple parts Keep the basic X86MachineState definition in x86.c. Move out functions that are only needed by other files: x86-common.c for the pc and microvm machines, x86-cpu.c for those used by accelerator code. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-11-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 4 +- hw/i386/x86-common.c | 1007 +++++++++++++++++++++++++++++++++++++++ hw/i386/x86-cpu.c | 97 ++++ hw/i386/x86.c | 1052 +---------------------------------------- include/hw/i386/x86.h | 6 +- 5 files changed, 1113 insertions(+), 1053 deletions(-) create mode 100644 hw/i386/x86-common.c create mode 100644 hw/i386/x86-cpu.c diff --git a/hw/i386/meson.build b/hw/i386/meson.build index d9da676038..3437da0aad 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -4,6 +4,7 @@ i386_ss.add(files( 'e820_memory_layout.c', 'multiboot.c', 'x86.c', + 'x86-cpu.c', )) i386_ss.add(when: 'CONFIG_APIC', if_true: files('vapic.c')) @@ -12,7 +13,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) -i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) +i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) @@ -22,6 +23,7 @@ i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c','sgx.c'), i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c')) i386_ss.add(when: 'CONFIG_PC', if_true: files( + 'x86-common.c', 'pc.c', 'pc_sysfw.c', 'acpi-build.c', diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c new file mode 100644 index 0000000000..67b03c913a --- /dev/null +++ b/hw/i386/x86-common.c @@ -0,0 +1,1007 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019, 2024 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/cutils.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qapi/error.h" +#include "sysemu/numa.h" +#include "sysemu/sysemu.h" +#include "sysemu/xen.h" +#include "trace.h" + +#include "hw/i386/x86.h" +#include "target/i386/cpu.h" +#include "hw/rtc/mc146818rtc.h" +#include "target/i386/sev.h" + +#include "hw/acpi/cpu_hotplug.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "multiboot.h" +#include "elf.h" +#include "standard-headers/asm-x86/bootparam.h" +#include CONFIG_DEVICES +#include "kvm/kvm_i386.h" + +#ifdef CONFIG_XEN_EMU +#include "hw/xen/xen.h" +#include "hw/i386/kvm/xen_evtchn.h" +#endif + +/* Physical Address of PVH entry point read from kernel ELF NOTE */ +static size_t pvh_start_addr; + +static void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) +{ + Object *cpu = object_new(MACHINE(x86ms)->cpu_type); + + if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) { + goto out; + } + qdev_realize(DEVICE(cpu), NULL, errp); + +out: + object_unref(cpu); +} + +void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) +{ + int i; + const CPUArchIdList *possible_cpus; + MachineState *ms = MACHINE(x86ms); + MachineClass *mc = MACHINE_GET_CLASS(x86ms); + + x86_cpu_set_default_version(default_cpu_version); + + /* + * Calculates the limit to CPU APIC ID values + * + * Limit for the APIC ID value, so that all + * CPU APIC IDs are < x86ms->apic_id_limit. + * + * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). + */ + x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, + ms->smp.max_cpus - 1) + 1; + + /* + * Can we support APIC ID 255 or higher? With KVM, that requires + * both in-kernel lapic and X2APIC userspace API. + * + * kvm_enabled() must go first to ensure that kvm_* references are + * not emitted for the linker to consume (kvm_enabled() is + * a literal `0` in configurations where kvm_* aren't defined) + */ + if (kvm_enabled() && x86ms->apic_id_limit > 255 && + kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) { + error_report("current -smp configuration requires kernel " + "irqchip and X2APIC API support."); + exit(EXIT_FAILURE); + } + + if (kvm_enabled()) { + kvm_set_max_apic_id(x86ms->apic_id_limit); + } + + if (!kvm_irqchip_in_kernel()) { + apic_set_max_apic_id(x86ms->apic_id_limit); + } + + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (i = 0; i < ms->smp.cpus; i++) { + x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); + } +} + +void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) +{ + MC146818RtcState *rtc = MC146818_RTC(s); + + if (cpus_count > 0xff) { + /* + * If the number of CPUs can't be represented in 8 bits, the + * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just + * to make old BIOSes fail more predictably. + */ + mc146818rtc_set_cmos_data(rtc, 0x5f, 0); + } else { + mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); + } +} + +static int x86_apic_cmp(const void *a, const void *b) +{ + CPUArchId *apic_a = (CPUArchId *)a; + CPUArchId *apic_b = (CPUArchId *)b; + + return apic_a->arch_id - apic_b->arch_id; +} + +/* + * returns pointer to CPUArchId descriptor that matches CPU's apic_id + * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no + * entry corresponding to CPU's apic_id returns NULL. + */ +static CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) +{ + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = id; + found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, + ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), + x86_apic_cmp); + if (found_cpu && idx) { + *idx = found_cpu - ms->possible_cpus->cpus; + } + return found_cpu; +} + +void x86_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *found_cpu; + Error *local_err = NULL; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + if (x86ms->acpi_dev) { + hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); + if (local_err) { + goto out; + } + } + + /* increment the number of CPUs */ + x86ms->boot_cpus++; + if (x86ms->rtc) { + x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + } + if (x86ms->fw_cfg) { + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); + } + + found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); + found_cpu->cpu = CPU(dev); +out: + error_propagate(errp, local_err); +} + +void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx = -1; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + if (!x86ms->acpi_dev) { + error_setg(errp, "CPU hot unplug not supported without ACPI"); + return; + } + + x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); + assert(idx != -1); + if (idx == 0) { + error_setg(errp, "Boot CPU is unpluggable"); + return; + } + + hotplug_handler_unplug_request(x86ms->acpi_dev, dev, + errp); +} + +void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *found_cpu; + Error *local_err = NULL; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); + if (local_err) { + goto out; + } + + found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); + found_cpu->cpu = NULL; + qdev_unrealize(dev); + + /* decrement the number of CPUs */ + x86ms->boot_cpus--; + /* Update the number of CPUs in CMOS */ + x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); + out: + error_propagate(errp, local_err); +} + +void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx; + CPUState *cs; + CPUArchId *cpu_slot; + X86CPUTopoIDs topo_ids; + X86CPU *cpu = X86_CPU(dev); + CPUX86State *env = &cpu->env; + MachineState *ms = MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + unsigned int smp_cores = ms->smp.cores; + unsigned int smp_threads = ms->smp.threads; + X86CPUTopoInfo topo_info; + + if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { + error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", + ms->cpu_type); + return; + } + + if (x86ms->acpi_dev) { + Error *local_err = NULL; + + hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + init_topo_info(&topo_info, x86ms); + + env->nr_dies = ms->smp.dies; + + /* + * If APIC ID is not set, + * set it based on socket/die/core/thread properties. + */ + if (cpu->apic_id == UNASSIGNED_APIC_ID) { + int max_socket = (ms->smp.max_cpus - 1) / + smp_threads / smp_cores / ms->smp.dies; + + /* + * die-id was optional in QEMU 4.0 and older, so keep it optional + * if there's only one die per socket. + */ + if (cpu->die_id < 0 && ms->smp.dies == 1) { + cpu->die_id = 0; + } + + if (cpu->socket_id < 0) { + error_setg(errp, "CPU socket-id is not set"); + return; + } else if (cpu->socket_id > max_socket) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, max_socket); + return; + } + if (cpu->die_id < 0) { + error_setg(errp, "CPU die-id is not set"); + return; + } else if (cpu->die_id > ms->smp.dies - 1) { + error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", + cpu->die_id, ms->smp.dies - 1); + return; + } + if (cpu->core_id < 0) { + error_setg(errp, "CPU core-id is not set"); + return; + } else if (cpu->core_id > (smp_cores - 1)) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0) { + error_setg(errp, "CPU thread-id is not set"); + return; + } else if (cpu->thread_id > (smp_threads - 1)) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo_ids.pkg_id = cpu->socket_id; + topo_ids.die_id = cpu->die_id; + topo_ids.core_id = cpu->core_id; + topo_ids.smt_id = cpu->thread_id; + cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); + } + + cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); + if (!cpu_slot) { + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + error_setg(errp, + "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" + " APIC ID %" PRIu32 ", valid index range 0:%d", + topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, + cpu->apic_id, ms->possible_cpus->len - 1); + return; + } + + if (cpu_slot->cpu) { + error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", + idx, cpu->apic_id); + return; + } + + /* if 'address' properties socket-id/core-id/thread-id are not set, set them + * so that machine_query_hotpluggable_cpus would show correct values + */ + /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() + * once -smp refactoring is complete and there will be CPU private + * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set apic-id:" + " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, + topo_ids.pkg_id); + return; + } + cpu->socket_id = topo_ids.pkg_id; + + if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { + error_setg(errp, "property die-id: %u doesn't match set apic-id:" + " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); + return; + } + cpu->die_id = topo_ids.die_id; + + if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { + error_setg(errp, "property core-id: %u doesn't match set apic-id:" + " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, + topo_ids.core_id); + return; + } + cpu->core_id = topo_ids.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set apic-id:" + " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, + topo_ids.smt_id); + return; + } + cpu->thread_id = topo_ids.smt_id; + + /* + * kvm_enabled() must go first to ensure that kvm_* references are + * not emitted for the linker to consume (kvm_enabled() is + * a literal `0` in configurations where kvm_* aren't defined) + */ + if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && + !kvm_hv_vpindex_settable()) { + error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); + return; + } + + cs = CPU(cpu); + cs->cpu_index = idx; + + numa_cpu_pre_plug(cpu_slot, dev, errp); +} + +static long get_file_size(FILE *f) +{ + long where, size; + + /* XXX: on Unix systems, using fstat() probably makes more sense */ + + where = ftell(f); + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, where, SEEK_SET); + + return size; +} + +void gsi_handler(void *opaque, int n, int level) +{ + GSIState *s = opaque; + + trace_x86_gsi_interrupt(n, level); + switch (n) { + case 0 ... ISA_NUM_IRQS - 1: + if (s->i8259_irq[n]) { + /* Under KVM, Kernel will forward to both PIC and IOAPIC */ + qemu_set_irq(s->i8259_irq[n], level); + } + /* fall through */ + case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: +#ifdef CONFIG_XEN_EMU + /* + * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC + * routing actually works properly under Xen). And then to + * *either* the PIRQ handling or the I/OAPIC depending on + * whether the former wants it. + */ + if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { + break; + } +#endif + qemu_set_irq(s->ioapic_irq[n], level); + break; + case IO_APIC_SECONDARY_IRQBASE + ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: + qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); + break; + } +} + +void ioapic_init_gsi(GSIState *gsi_state, Object *parent) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + assert(parent); + if (kvm_ioapic_in_kernel()) { + dev = qdev_new(TYPE_KVM_IOAPIC); + } else { + dev = qdev_new(TYPE_IOAPIC); + } + object_property_add_child(parent, "ioapic", OBJECT(dev)); + d = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); + } +} + +DeviceState *ioapic_init_secondary(GSIState *gsi_state) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + dev = qdev_new(TYPE_IOAPIC); + d = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); + } + return dev; +} + +/* + * The entry point into the kernel for PVH boot is different from + * the native entry point. The PVH entry is defined by the x86/HVM + * direct boot ABI and is available in an ELFNOTE in the kernel binary. + * + * This function is passed to load_elf() when it is called from + * load_elfboot() which then additionally checks for an ELF Note of + * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to + * parse the PVH entry address from the ELF Note. + * + * Due to trickery in elf_opts.h, load_elf() is actually available as + * load_elf32() or load_elf64() and this routine needs to be able + * to deal with being called as 32 or 64 bit. + * + * The address of the PVH entry point is saved to the 'pvh_start_addr' + * global variable. (although the entry point is 32-bit, the kernel + * binary can be either 32-bit or 64-bit). + */ +static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) +{ + size_t *elf_note_data_addr; + + /* Check if ELF Note header passed in is valid */ + if (arg1 == NULL) { + return 0; + } + + if (is64) { + struct elf64_note *nhdr64 = (struct elf64_note *)arg1; + uint64_t nhdr_size64 = sizeof(struct elf64_note); + uint64_t phdr_align = *(uint64_t *)arg2; + uint64_t nhdr_namesz = nhdr64->n_namesz; + + elf_note_data_addr = + ((void *)nhdr64) + nhdr_size64 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + + pvh_start_addr = *elf_note_data_addr; + } else { + struct elf32_note *nhdr32 = (struct elf32_note *)arg1; + uint32_t nhdr_size32 = sizeof(struct elf32_note); + uint32_t phdr_align = *(uint32_t *)arg2; + uint32_t nhdr_namesz = nhdr32->n_namesz; + + elf_note_data_addr = + ((void *)nhdr32) + nhdr_size32 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + + pvh_start_addr = *(uint32_t *)elf_note_data_addr; + } + + return pvh_start_addr; +} + +static bool load_elfboot(const char *kernel_filename, + int kernel_file_size, + uint8_t *header, + size_t pvh_xen_start_addr, + FWCfgState *fw_cfg) +{ + uint32_t flags = 0; + uint32_t mh_load_addr = 0; + uint32_t elf_kernel_size = 0; + uint64_t elf_entry; + uint64_t elf_low, elf_high; + int kernel_size; + + if (ldl_p(header) != 0x464c457f) { + return false; /* no elfboot */ + } + + bool elf_is64 = header[EI_CLASS] == ELFCLASS64; + flags = elf_is64 ? + ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; + + if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ + error_report("elfboot unsupported flags = %x", flags); + exit(1); + } + + uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; + kernel_size = load_elf(kernel_filename, read_pvh_start_addr, + NULL, &elf_note_type, &elf_entry, + &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, + 0, 0); + + if (kernel_size < 0) { + error_report("Error while loading elf kernel"); + exit(1); + } + mh_load_addr = elf_low; + elf_kernel_size = elf_high - elf_low; + + if (pvh_start_addr == 0) { + error_report("Error loading uncompressed kernel without PVH ELF Note"); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); + + return true; +} + +void x86_load_linux(X86MachineState *x86ms, + FWCfgState *fw_cfg, + int acpi_data_size, + bool pvh_enabled) +{ + bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; + uint16_t protocol; + int setup_size, kernel_size, cmdline_size; + int dtb_size, setup_data_offset; + uint32_t initrd_max; + uint8_t header[8192], *setup, *kernel; + hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; + FILE *f; + char *vmode; + MachineState *machine = MACHINE(x86ms); + struct setup_data *setup_data; + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + const char *dtb_filename = machine->dtb; + const char *kernel_cmdline = machine->kernel_cmdline; + SevKernelLoaderContext sev_load_ctx = {}; + + /* Align to 16 bytes as a paranoia measure */ + cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; + + /* load the kernel header */ + f = fopen(kernel_filename, "rb"); + if (!f) { + fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + kernel_size = get_file_size(f); + if (!kernel_size || + fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != + MIN(ARRAY_SIZE(header), kernel_size)) { + fprintf(stderr, "qemu: could not load kernel '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + /* kernel protocol version */ + if (ldl_p(header + 0x202) == 0x53726448) { + protocol = lduw_p(header + 0x206); + } else { + /* + * This could be a multiboot kernel. If it is, let's stop treating it + * like a Linux kernel. + * Note: some multiboot images could be in the ELF format (the same of + * PVH), so we try multiboot first since we check the multiboot magic + * header before to load it. + */ + if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, + kernel_cmdline, kernel_size, header)) { + return; + } + /* + * Check if the file is an uncompressed kernel file (ELF) and load it, + * saving the PVH entry point used by the x86/HVM direct boot ABI. + * If load_elfboot() is successful, populate the fw_cfg info. + */ + if (pvh_enabled && + load_elfboot(kernel_filename, kernel_size, + header, pvh_start_addr, fw_cfg)) { + fclose(f); + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, + header, sizeof(header)); + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + x86ms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, + initrd_size); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "pvh.bin"; + nb_option_roms++; + + return; + } + protocol = 0; + } + + if (protocol < 0x200 || !(header[0x211] & 0x01)) { + /* Low kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x10000; + } else if (protocol < 0x202) { + /* High but ancient kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x100000; + } else { + /* High and recent kernel */ + real_addr = 0x10000; + cmdline_addr = 0x20000; + prot_addr = 0x100000; + } + + /* highest address for loading the initrd */ + if (protocol >= 0x20c && + lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { + /* + * Linux has supported initrd up to 4 GB for a very long time (2007, + * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), + * though it only sets initrd_max to 2 GB to "work around bootloader + * bugs". Luckily, QEMU firmware(which does something like bootloader) + * has supported this. + * + * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can + * be loaded into any address. + * + * In addition, initrd_max is uint32_t simply because QEMU doesn't + * support the 64-bit boot protocol (specifically the ext_ramdisk_image + * field). + * + * Therefore here just limit initrd_max to UINT32_MAX simply as well. + */ + initrd_max = UINT32_MAX; + } else if (protocol >= 0x203) { + initrd_max = ldl_p(header + 0x22c); + } else { + initrd_max = 0x37ffffff; + } + + if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; + } + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + sev_load_ctx.cmdline_data = (char *)kernel_cmdline; + sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; + + if (protocol >= 0x202) { + stl_p(header + 0x228, cmdline_addr); + } else { + stw_p(header + 0x20, 0xA33F); + stw_p(header + 0x22, cmdline_addr - real_addr); + } + + /* handle vga= parameter */ + vmode = strstr(kernel_cmdline, "vga="); + if (vmode) { + unsigned int video_mode; + const char *end; + int ret; + /* skip "vga=" */ + vmode += 4; + if (!strncmp(vmode, "normal", 6)) { + video_mode = 0xffff; + } else if (!strncmp(vmode, "ext", 3)) { + video_mode = 0xfffe; + } else if (!strncmp(vmode, "ask", 3)) { + video_mode = 0xfffd; + } else { + ret = qemu_strtoui(vmode, &end, 0, &video_mode); + if (ret != 0 || (*end && *end != ' ')) { + fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); + exit(1); + } + } + stw_p(header + 0x1fa, video_mode); + } + + /* loader type */ + /* + * High nybble = B reserved for QEMU; low nybble is revision number. + * If this code is substantially changed, you may want to consider + * incrementing the revision. + */ + if (protocol >= 0x200) { + header[0x210] = 0xB0; + } + /* heap */ + if (protocol >= 0x201) { + header[0x211] |= 0x80; /* CAN_USE_HEAP */ + stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); + } + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + if (protocol < 0x200) { + fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); + exit(1); + } + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + x86ms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); + sev_load_ctx.initrd_data = initrd_data; + sev_load_ctx.initrd_size = initrd_size; + + stl_p(header + 0x218, initrd_addr); + stl_p(header + 0x21c, initrd_size); + } + + /* load kernel and setup */ + setup_size = header[0x1f1]; + if (setup_size == 0) { + setup_size = 4; + } + setup_size = (setup_size + 1) * 512; + if (setup_size > kernel_size) { + fprintf(stderr, "qemu: invalid kernel header\n"); + exit(1); + } + kernel_size -= setup_size; + + setup = g_malloc(setup_size); + kernel = g_malloc(kernel_size); + fseek(f, 0, SEEK_SET); + if (fread(setup, 1, setup_size, f) != setup_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + if (fread(kernel, 1, kernel_size, f) != kernel_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + fclose(f); + + /* append dtb to kernel */ + if (dtb_filename) { + if (protocol < 0x209) { + fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); + exit(1); + } + + dtb_size = get_image_size(dtb_filename); + if (dtb_size <= 0) { + fprintf(stderr, "qemu: error reading dtb %s: %s\n", + dtb_filename, strerror(errno)); + exit(1); + } + + setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); + kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; + kernel = g_realloc(kernel, kernel_size); + + stq_p(header + 0x250, prot_addr + setup_data_offset); + + setup_data = (struct setup_data *)(kernel + setup_data_offset); + setup_data->next = 0; + setup_data->type = cpu_to_le32(SETUP_DTB); + setup_data->len = cpu_to_le32(dtb_size); + + load_image_size(dtb_filename, setup_data->data, dtb_size); + } + + /* + * If we're starting an encrypted VM, it will be OVMF based, which uses the + * efi stub for booting and doesn't require any values to be placed in the + * kernel header. We therefore don't update the header so the hash of the + * kernel on the other side of the fw_cfg interface matches the hash of the + * file the user passed in. + */ + if (!sev_enabled()) { + memcpy(setup, header, MIN(sizeof(header), setup_size)); + } + + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); + sev_load_ctx.kernel_data = (char *)kernel; + sev_load_ctx.kernel_size = kernel_size; + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); + sev_load_ctx.setup_data = (char *)setup; + sev_load_ctx.setup_size = setup_size; + + if (sev_enabled()) { + sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "linuxboot.bin"; + if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { + option_rom[nb_option_roms].name = "linuxboot_dma.bin"; + } + nb_option_roms++; +} + +void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, + MemoryRegion *bios, bool read_only) +{ + uint64_t bios_size = memory_region_size(bios); + uint64_t isa_bios_size = MIN(bios_size, 128 * KiB); + + memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size, + isa_bios, 1); + memory_region_set_readonly(isa_bios, read_only); +} + +void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, + MemoryRegion *rom_memory, bool isapc_ram_fw) +{ + const char *bios_name; + char *filename; + int bios_size; + ssize_t ret; + + /* BIOS load */ + bios_name = MACHINE(x86ms)->firmware ?: default_firmware; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } + if (bios_size <= 0 || + (bios_size % 65536) != 0) { + goto bios_error; + } + memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, + &error_fatal); + if (sev_enabled()) { + /* + * The concept of a "reset" simply doesn't exist for + * confidential computing guests, we have to destroy and + * re-launch them instead. So there is no need to register + * the firmware as rom to properly re-initialize on reset. + * Just go for a straight file load instead. + */ + void *ptr = memory_region_get_ram_ptr(&x86ms->bios); + load_image_size(filename, ptr, bios_size); + x86_firmware_configure(ptr, bios_size); + } else { + memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); + if (ret != 0) { + goto bios_error; + } + } + g_free(filename); + + /* map the last 128KB of the BIOS in ISA space */ + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, + !isapc_ram_fw); + + /* map all the bios at the top of memory */ + memory_region_add_subregion(rom_memory, + (uint32_t)(-bios_size), + &x86ms->bios); + return; + +bios_error: + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); + exit(1); +} diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c new file mode 100644 index 0000000000..ab2920522d --- /dev/null +++ b/hw/i386/x86-cpu.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019, 2024 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "sysemu/whpx.h" +#include "sysemu/cpu-timers.h" +#include "trace.h" + +#include "hw/i386/x86.h" +#include "target/i386/cpu.h" +#include "hw/intc/i8259.h" +#include "hw/irq.h" +#include "sysemu/kvm.h" + +/* TSC handling */ +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpus_get_elapsed_ticks(); +} + +/* IRQ handling */ +static void pic_irq_request(void *opaque, int irq, int level) +{ + CPUState *cs = first_cpu; + X86CPU *cpu = X86_CPU(cs); + + trace_x86_pic_interrupt(irq, level); + if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && + !whpx_apic_in_platform()) { + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); + if (apic_accept_pic_intr(cpu->apic_state)) { + apic_deliver_pic_intr(cpu->apic_state, level); + } + } + } else { + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +qemu_irq x86_allocate_cpu_irq(void) +{ + return qemu_allocate_irq(pic_irq_request, NULL, 0); +} + +int cpu_get_pic_interrupt(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + int intno; + + if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { + intno = apic_get_interrupt(cpu->apic_state); + if (intno >= 0) { + return intno; + } + /* read the irq from the PIC */ + if (!apic_accept_pic_intr(cpu->apic_state)) { + return -1; + } + } + + intno = pic_read_irq(isa_pic); + return intno; +} + +DeviceState *cpu_get_current_apic(void) +{ + if (current_cpu) { + X86CPU *cpu = X86_CPU(current_cpu); + return cpu->apic_state; + } else { + return NULL; + } +} diff --git a/hw/i386/x86.c b/hw/i386/x86.c index fcef652c1e..0b5cc59956 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -22,52 +22,25 @@ */ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/cutils.h" #include "qemu/units.h" -#include "qemu/datadir.h" #include "qapi/error.h" #include "qapi/qapi-visit-common.h" -#include "qapi/clone-visitor.h" #include "qapi/qapi-visit-machine.h" #include "qapi/visitor.h" #include "sysemu/qtest.h" -#include "sysemu/whpx.h" #include "sysemu/numa.h" -#include "sysemu/replay.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/xen.h" #include "trace.h" +#include "hw/acpi/aml-build.h" #include "hw/i386/x86.h" -#include "target/i386/cpu.h" #include "hw/i386/topology.h" -#include "hw/i386/fw_cfg.h" -#include "hw/intc/i8259.h" -#include "hw/rtc/mc146818rtc.h" -#include "target/i386/sev.h" -#include "hw/acpi/cpu_hotplug.h" -#include "hw/irq.h" #include "hw/nmi.h" -#include "hw/loader.h" -#include "multiboot.h" -#include "elf.h" -#include "standard-headers/asm-x86/bootparam.h" -#include CONFIG_DEVICES #include "kvm/kvm_i386.h" -#ifdef CONFIG_XEN_EMU -#include "hw/xen/xen.h" -#include "hw/i386/kvm/xen_evtchn.h" -#endif -/* Physical Address of PVH entry point read from kernel ELF NOTE */ -static size_t pvh_start_addr; - -static void init_topo_info(X86CPUTopoInfo *topo_info, - const X86MachineState *x86ms) +void init_topo_info(X86CPUTopoInfo *topo_info, + const X86MachineState *x86ms) { MachineState *ms = MACHINE(x86ms); @@ -94,355 +67,6 @@ uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, return x86_apicid_from_cpu_idx(&topo_info, cpu_index); } - -void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) -{ - Object *cpu = object_new(MACHINE(x86ms)->cpu_type); - - if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) { - goto out; - } - qdev_realize(DEVICE(cpu), NULL, errp); - -out: - object_unref(cpu); -} - -void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) -{ - int i; - const CPUArchIdList *possible_cpus; - MachineState *ms = MACHINE(x86ms); - MachineClass *mc = MACHINE_GET_CLASS(x86ms); - - x86_cpu_set_default_version(default_cpu_version); - - /* - * Calculates the limit to CPU APIC ID values - * - * Limit for the APIC ID value, so that all - * CPU APIC IDs are < x86ms->apic_id_limit. - * - * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). - */ - x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, - ms->smp.max_cpus - 1) + 1; - - /* - * Can we support APIC ID 255 or higher? With KVM, that requires - * both in-kernel lapic and X2APIC userspace API. - * - * kvm_enabled() must go first to ensure that kvm_* references are - * not emitted for the linker to consume (kvm_enabled() is - * a literal `0` in configurations where kvm_* aren't defined) - */ - if (kvm_enabled() && x86ms->apic_id_limit > 255 && - kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) { - error_report("current -smp configuration requires kernel " - "irqchip and X2APIC API support."); - exit(EXIT_FAILURE); - } - - if (kvm_enabled()) { - kvm_set_max_apic_id(x86ms->apic_id_limit); - } - - if (!kvm_irqchip_in_kernel()) { - apic_set_max_apic_id(x86ms->apic_id_limit); - } - - possible_cpus = mc->possible_cpu_arch_ids(ms); - for (i = 0; i < ms->smp.cpus; i++) { - x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); - } -} - -void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) -{ - MC146818RtcState *rtc = MC146818_RTC(s); - - if (cpus_count > 0xff) { - /* - * If the number of CPUs can't be represented in 8 bits, the - * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just - * to make old BIOSes fail more predictably. - */ - mc146818rtc_set_cmos_data(rtc, 0x5f, 0); - } else { - mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); - } -} - -static int x86_apic_cmp(const void *a, const void *b) -{ - CPUArchId *apic_a = (CPUArchId *)a; - CPUArchId *apic_b = (CPUArchId *)b; - - return apic_a->arch_id - apic_b->arch_id; -} - -/* - * returns pointer to CPUArchId descriptor that matches CPU's apic_id - * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no - * entry corresponding to CPU's apic_id returns NULL. - */ -CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) -{ - CPUArchId apic_id, *found_cpu; - - apic_id.arch_id = id; - found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, - ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), - x86_apic_cmp); - if (found_cpu && idx) { - *idx = found_cpu - ms->possible_cpus->cpus; - } - return found_cpu; -} - -void x86_cpu_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - CPUArchId *found_cpu; - Error *local_err = NULL; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - if (x86ms->acpi_dev) { - hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); - if (local_err) { - goto out; - } - } - - /* increment the number of CPUs */ - x86ms->boot_cpus++; - if (x86ms->rtc) { - x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - } - if (x86ms->fw_cfg) { - fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); - } - - found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = CPU(dev); -out: - error_propagate(errp, local_err); -} - -void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - int idx = -1; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - if (!x86ms->acpi_dev) { - error_setg(errp, "CPU hot unplug not supported without ACPI"); - return; - } - - x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); - assert(idx != -1); - if (idx == 0) { - error_setg(errp, "Boot CPU is unpluggable"); - return; - } - - hotplug_handler_unplug_request(x86ms->acpi_dev, dev, - errp); -} - -void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - CPUArchId *found_cpu; - Error *local_err = NULL; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); - if (local_err) { - goto out; - } - - found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = NULL; - qdev_unrealize(dev); - - /* decrement the number of CPUs */ - x86ms->boot_cpus--; - /* Update the number of CPUs in CMOS */ - x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); - out: - error_propagate(errp, local_err); -} - -void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - int idx; - CPUState *cs; - CPUArchId *cpu_slot; - X86CPUTopoIDs topo_ids; - X86CPU *cpu = X86_CPU(dev); - CPUX86State *env = &cpu->env; - MachineState *ms = MACHINE(hotplug_dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - unsigned int smp_cores = ms->smp.cores; - unsigned int smp_threads = ms->smp.threads; - X86CPUTopoInfo topo_info; - - if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { - error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", - ms->cpu_type); - return; - } - - if (x86ms->acpi_dev) { - Error *local_err = NULL; - - hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - init_topo_info(&topo_info, x86ms); - - env->nr_dies = ms->smp.dies; - - /* - * If APIC ID is not set, - * set it based on socket/die/core/thread properties. - */ - if (cpu->apic_id == UNASSIGNED_APIC_ID) { - int max_socket = (ms->smp.max_cpus - 1) / - smp_threads / smp_cores / ms->smp.dies; - - /* - * die-id was optional in QEMU 4.0 and older, so keep it optional - * if there's only one die per socket. - */ - if (cpu->die_id < 0 && ms->smp.dies == 1) { - cpu->die_id = 0; - } - - if (cpu->socket_id < 0) { - error_setg(errp, "CPU socket-id is not set"); - return; - } else if (cpu->socket_id > max_socket) { - error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", - cpu->socket_id, max_socket); - return; - } - if (cpu->die_id < 0) { - error_setg(errp, "CPU die-id is not set"); - return; - } else if (cpu->die_id > ms->smp.dies - 1) { - error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", - cpu->die_id, ms->smp.dies - 1); - return; - } - if (cpu->core_id < 0) { - error_setg(errp, "CPU core-id is not set"); - return; - } else if (cpu->core_id > (smp_cores - 1)) { - error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", - cpu->core_id, smp_cores - 1); - return; - } - if (cpu->thread_id < 0) { - error_setg(errp, "CPU thread-id is not set"); - return; - } else if (cpu->thread_id > (smp_threads - 1)) { - error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", - cpu->thread_id, smp_threads - 1); - return; - } - - topo_ids.pkg_id = cpu->socket_id; - topo_ids.die_id = cpu->die_id; - topo_ids.core_id = cpu->core_id; - topo_ids.smt_id = cpu->thread_id; - cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); - } - - cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); - if (!cpu_slot) { - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); - error_setg(errp, - "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" - " APIC ID %" PRIu32 ", valid index range 0:%d", - topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, - cpu->apic_id, ms->possible_cpus->len - 1); - return; - } - - if (cpu_slot->cpu) { - error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", - idx, cpu->apic_id); - return; - } - - /* if 'address' properties socket-id/core-id/thread-id are not set, set them - * so that machine_query_hotpluggable_cpus would show correct values - */ - /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() - * once -smp refactoring is complete and there will be CPU private - * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); - if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { - error_setg(errp, "property socket-id: %u doesn't match set apic-id:" - " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, - topo_ids.pkg_id); - return; - } - cpu->socket_id = topo_ids.pkg_id; - - if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { - error_setg(errp, "property die-id: %u doesn't match set apic-id:" - " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); - return; - } - cpu->die_id = topo_ids.die_id; - - if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { - error_setg(errp, "property core-id: %u doesn't match set apic-id:" - " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, - topo_ids.core_id); - return; - } - cpu->core_id = topo_ids.core_id; - - if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { - error_setg(errp, "property thread-id: %u doesn't match set apic-id:" - " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, - topo_ids.smt_id); - return; - } - cpu->thread_id = topo_ids.smt_id; - - /* - * kvm_enabled() must go first to ensure that kvm_* references are - * not emitted for the linker to consume (kvm_enabled() is - * a literal `0` in configurations where kvm_* aren't defined) - */ - if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && - !kvm_hv_vpindex_settable()) { - error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); - return; - } - - cs = CPU(cpu); - cs->cpu_index = idx; - - numa_cpu_pre_plug(cpu_slot, dev, errp); -} - static CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { @@ -528,676 +152,6 @@ static void x86_nmi(NMIState *n, int cpu_index, Error **errp) } } -static long get_file_size(FILE *f) -{ - long where, size; - - /* XXX: on Unix systems, using fstat() probably makes more sense */ - - where = ftell(f); - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, where, SEEK_SET); - - return size; -} - -/* TSC handling */ -uint64_t cpu_get_tsc(CPUX86State *env) -{ - return cpus_get_elapsed_ticks(); -} - -/* IRQ handling */ -static void pic_irq_request(void *opaque, int irq, int level) -{ - CPUState *cs = first_cpu; - X86CPU *cpu = X86_CPU(cs); - - trace_x86_pic_interrupt(irq, level); - if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && - !whpx_apic_in_platform()) { - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - if (apic_accept_pic_intr(cpu->apic_state)) { - apic_deliver_pic_intr(cpu->apic_state, level); - } - } - } else { - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - -qemu_irq x86_allocate_cpu_irq(void) -{ - return qemu_allocate_irq(pic_irq_request, NULL, 0); -} - -int cpu_get_pic_interrupt(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - int intno; - - if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { - intno = apic_get_interrupt(cpu->apic_state); - if (intno >= 0) { - return intno; - } - /* read the irq from the PIC */ - if (!apic_accept_pic_intr(cpu->apic_state)) { - return -1; - } - } - - intno = pic_read_irq(isa_pic); - return intno; -} - -DeviceState *cpu_get_current_apic(void) -{ - if (current_cpu) { - X86CPU *cpu = X86_CPU(current_cpu); - return cpu->apic_state; - } else { - return NULL; - } -} - -void gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - trace_x86_gsi_interrupt(n, level); - switch (n) { - case 0 ... ISA_NUM_IRQS - 1: - if (s->i8259_irq[n]) { - /* Under KVM, Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); - } - /* fall through */ - case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: -#ifdef CONFIG_XEN_EMU - /* - * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC - * routing actually works properly under Xen). And then to - * *either* the PIRQ handling or the I/OAPIC depending on - * whether the former wants it. - */ - if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { - break; - } -#endif - qemu_set_irq(s->ioapic_irq[n], level); - break; - case IO_APIC_SECONDARY_IRQBASE - ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: - qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); - break; - } -} - -void ioapic_init_gsi(GSIState *gsi_state, Object *parent) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - assert(parent); - if (kvm_ioapic_in_kernel()) { - dev = qdev_new(TYPE_KVM_IOAPIC); - } else { - dev = qdev_new(TYPE_IOAPIC); - } - object_property_add_child(parent, "ioapic", OBJECT(dev)); - d = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); - } -} - -DeviceState *ioapic_init_secondary(GSIState *gsi_state) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - dev = qdev_new(TYPE_IOAPIC); - d = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); - } - return dev; -} - -/* - * The entry point into the kernel for PVH boot is different from - * the native entry point. The PVH entry is defined by the x86/HVM - * direct boot ABI and is available in an ELFNOTE in the kernel binary. - * - * This function is passed to load_elf() when it is called from - * load_elfboot() which then additionally checks for an ELF Note of - * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to - * parse the PVH entry address from the ELF Note. - * - * Due to trickery in elf_opts.h, load_elf() is actually available as - * load_elf32() or load_elf64() and this routine needs to be able - * to deal with being called as 32 or 64 bit. - * - * The address of the PVH entry point is saved to the 'pvh_start_addr' - * global variable. (although the entry point is 32-bit, the kernel - * binary can be either 32-bit or 64-bit). - */ -static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) -{ - size_t *elf_note_data_addr; - - /* Check if ELF Note header passed in is valid */ - if (arg1 == NULL) { - return 0; - } - - if (is64) { - struct elf64_note *nhdr64 = (struct elf64_note *)arg1; - uint64_t nhdr_size64 = sizeof(struct elf64_note); - uint64_t phdr_align = *(uint64_t *)arg2; - uint64_t nhdr_namesz = nhdr64->n_namesz; - - elf_note_data_addr = - ((void *)nhdr64) + nhdr_size64 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - - pvh_start_addr = *elf_note_data_addr; - } else { - struct elf32_note *nhdr32 = (struct elf32_note *)arg1; - uint32_t nhdr_size32 = sizeof(struct elf32_note); - uint32_t phdr_align = *(uint32_t *)arg2; - uint32_t nhdr_namesz = nhdr32->n_namesz; - - elf_note_data_addr = - ((void *)nhdr32) + nhdr_size32 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - - pvh_start_addr = *(uint32_t *)elf_note_data_addr; - } - - return pvh_start_addr; -} - -static bool load_elfboot(const char *kernel_filename, - int kernel_file_size, - uint8_t *header, - size_t pvh_xen_start_addr, - FWCfgState *fw_cfg) -{ - uint32_t flags = 0; - uint32_t mh_load_addr = 0; - uint32_t elf_kernel_size = 0; - uint64_t elf_entry; - uint64_t elf_low, elf_high; - int kernel_size; - - if (ldl_p(header) != 0x464c457f) { - return false; /* no elfboot */ - } - - bool elf_is64 = header[EI_CLASS] == ELFCLASS64; - flags = elf_is64 ? - ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; - - if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ - error_report("elfboot unsupported flags = %x", flags); - exit(1); - } - - uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; - kernel_size = load_elf(kernel_filename, read_pvh_start_addr, - NULL, &elf_note_type, &elf_entry, - &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, - 0, 0); - - if (kernel_size < 0) { - error_report("Error while loading elf kernel"); - exit(1); - } - mh_load_addr = elf_low; - elf_kernel_size = elf_high - elf_low; - - if (pvh_start_addr == 0) { - error_report("Error loading uncompressed kernel without PVH ELF Note"); - exit(1); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); - - return true; -} - -void x86_load_linux(X86MachineState *x86ms, - FWCfgState *fw_cfg, - int acpi_data_size, - bool pvh_enabled) -{ - bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; - uint16_t protocol; - int setup_size, kernel_size, cmdline_size; - int dtb_size, setup_data_offset; - uint32_t initrd_max; - uint8_t header[8192], *setup, *kernel; - hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; - FILE *f; - char *vmode; - MachineState *machine = MACHINE(x86ms); - struct setup_data *setup_data; - const char *kernel_filename = machine->kernel_filename; - const char *initrd_filename = machine->initrd_filename; - const char *dtb_filename = machine->dtb; - const char *kernel_cmdline = machine->kernel_cmdline; - SevKernelLoaderContext sev_load_ctx = {}; - - /* Align to 16 bytes as a paranoia measure */ - cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; - - /* load the kernel header */ - f = fopen(kernel_filename, "rb"); - if (!f) { - fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - kernel_size = get_file_size(f); - if (!kernel_size || - fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != - MIN(ARRAY_SIZE(header), kernel_size)) { - fprintf(stderr, "qemu: could not load kernel '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - /* kernel protocol version */ - if (ldl_p(header + 0x202) == 0x53726448) { - protocol = lduw_p(header + 0x206); - } else { - /* - * This could be a multiboot kernel. If it is, let's stop treating it - * like a Linux kernel. - * Note: some multiboot images could be in the ELF format (the same of - * PVH), so we try multiboot first since we check the multiboot magic - * header before to load it. - */ - if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, - kernel_cmdline, kernel_size, header)) { - return; - } - /* - * Check if the file is an uncompressed kernel file (ELF) and load it, - * saving the PVH entry point used by the x86/HVM direct boot ABI. - * If load_elfboot() is successful, populate the fw_cfg info. - */ - if (pvh_enabled && - load_elfboot(kernel_filename, kernel_size, - header, pvh_start_addr, fw_cfg)) { - fclose(f); - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, - header, sizeof(header)); - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - x86ms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, - initrd_size); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "pvh.bin"; - nb_option_roms++; - - return; - } - protocol = 0; - } - - if (protocol < 0x200 || !(header[0x211] & 0x01)) { - /* Low kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x10000; - } else if (protocol < 0x202) { - /* High but ancient kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x100000; - } else { - /* High and recent kernel */ - real_addr = 0x10000; - cmdline_addr = 0x20000; - prot_addr = 0x100000; - } - - /* highest address for loading the initrd */ - if (protocol >= 0x20c && - lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { - /* - * Linux has supported initrd up to 4 GB for a very long time (2007, - * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), - * though it only sets initrd_max to 2 GB to "work around bootloader - * bugs". Luckily, QEMU firmware(which does something like bootloader) - * has supported this. - * - * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can - * be loaded into any address. - * - * In addition, initrd_max is uint32_t simply because QEMU doesn't - * support the 64-bit boot protocol (specifically the ext_ramdisk_image - * field). - * - * Therefore here just limit initrd_max to UINT32_MAX simply as well. - */ - initrd_max = UINT32_MAX; - } else if (protocol >= 0x203) { - initrd_max = ldl_p(header + 0x22c); - } else { - initrd_max = 0x37ffffff; - } - - if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { - initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; - } - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - sev_load_ctx.cmdline_data = (char *)kernel_cmdline; - sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; - - if (protocol >= 0x202) { - stl_p(header + 0x228, cmdline_addr); - } else { - stw_p(header + 0x20, 0xA33F); - stw_p(header + 0x22, cmdline_addr - real_addr); - } - - /* handle vga= parameter */ - vmode = strstr(kernel_cmdline, "vga="); - if (vmode) { - unsigned int video_mode; - const char *end; - int ret; - /* skip "vga=" */ - vmode += 4; - if (!strncmp(vmode, "normal", 6)) { - video_mode = 0xffff; - } else if (!strncmp(vmode, "ext", 3)) { - video_mode = 0xfffe; - } else if (!strncmp(vmode, "ask", 3)) { - video_mode = 0xfffd; - } else { - ret = qemu_strtoui(vmode, &end, 0, &video_mode); - if (ret != 0 || (*end && *end != ' ')) { - fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); - exit(1); - } - } - stw_p(header + 0x1fa, video_mode); - } - - /* loader type */ - /* - * High nybble = B reserved for QEMU; low nybble is revision number. - * If this code is substantially changed, you may want to consider - * incrementing the revision. - */ - if (protocol >= 0x200) { - header[0x210] = 0xB0; - } - /* heap */ - if (protocol >= 0x201) { - header[0x211] |= 0x80; /* CAN_USE_HEAP */ - stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); - } - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - if (protocol < 0x200) { - fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); - exit(1); - } - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - x86ms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); - sev_load_ctx.initrd_data = initrd_data; - sev_load_ctx.initrd_size = initrd_size; - - stl_p(header + 0x218, initrd_addr); - stl_p(header + 0x21c, initrd_size); - } - - /* load kernel and setup */ - setup_size = header[0x1f1]; - if (setup_size == 0) { - setup_size = 4; - } - setup_size = (setup_size + 1) * 512; - if (setup_size > kernel_size) { - fprintf(stderr, "qemu: invalid kernel header\n"); - exit(1); - } - kernel_size -= setup_size; - - setup = g_malloc(setup_size); - kernel = g_malloc(kernel_size); - fseek(f, 0, SEEK_SET); - if (fread(setup, 1, setup_size, f) != setup_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - if (fread(kernel, 1, kernel_size, f) != kernel_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - fclose(f); - - /* append dtb to kernel */ - if (dtb_filename) { - if (protocol < 0x209) { - fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); - exit(1); - } - - dtb_size = get_image_size(dtb_filename); - if (dtb_size <= 0) { - fprintf(stderr, "qemu: error reading dtb %s: %s\n", - dtb_filename, strerror(errno)); - exit(1); - } - - setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); - kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; - kernel = g_realloc(kernel, kernel_size); - - stq_p(header + 0x250, prot_addr + setup_data_offset); - - setup_data = (struct setup_data *)(kernel + setup_data_offset); - setup_data->next = 0; - setup_data->type = cpu_to_le32(SETUP_DTB); - setup_data->len = cpu_to_le32(dtb_size); - - load_image_size(dtb_filename, setup_data->data, dtb_size); - } - - /* - * If we're starting an encrypted VM, it will be OVMF based, which uses the - * efi stub for booting and doesn't require any values to be placed in the - * kernel header. We therefore don't update the header so the hash of the - * kernel on the other side of the fw_cfg interface matches the hash of the - * file the user passed in. - */ - if (!sev_enabled()) { - memcpy(setup, header, MIN(sizeof(header), setup_size)); - } - - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); - sev_load_ctx.kernel_data = (char *)kernel; - sev_load_ctx.kernel_size = kernel_size; - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - sev_load_ctx.setup_data = (char *)setup; - sev_load_ctx.setup_size = setup_size; - - if (sev_enabled()) { - sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "linuxboot.bin"; - if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { - option_rom[nb_option_roms].name = "linuxboot_dma.bin"; - } - nb_option_roms++; -} - -void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, - MemoryRegion *bios, bool read_only) -{ - uint64_t bios_size = memory_region_size(bios); - uint64_t isa_bios_size = MIN(bios_size, 128 * KiB); - - memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size, - isa_bios, 1); - memory_region_set_readonly(isa_bios, read_only); -} - -void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, - MemoryRegion *rom_memory, bool isapc_ram_fw) -{ - const char *bios_name; - char *filename; - int bios_size; - ssize_t ret; - - /* BIOS load */ - bios_name = MACHINE(x86ms)->firmware ?: default_firmware; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } - if (bios_size <= 0 || - (bios_size % 65536) != 0) { - goto bios_error; - } - memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, - &error_fatal); - if (sev_enabled()) { - /* - * The concept of a "reset" simply doesn't exist for - * confidential computing guests, we have to destroy and - * re-launch them instead. So there is no need to register - * the firmware as rom to properly re-initialize on reset. - * Just go for a straight file load instead. - */ - void *ptr = memory_region_get_ram_ptr(&x86ms->bios); - load_image_size(filename, ptr, bios_size); - x86_firmware_configure(ptr, bios_size); - } else { - memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - goto bios_error; - } - } - g_free(filename); - - /* map the last 128KB of the BIOS in ISA space */ - x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, - !isapc_ram_fw); - - /* map all the bios at the top of memory */ - memory_region_add_subregion(rom_memory, - (uint32_t)(-bios_size), - &x86ms->bios); - return; - -bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); -} - bool x86_machine_is_smm_enabled(const X86MachineState *x86ms) { bool smm_available = false; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index c2062db13f..b006f16b8d 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -21,6 +21,7 @@ #include "exec/memory.h" #include "hw/boards.h" +#include "hw/i386/topology.h" #include "hw/intc/ioapic.h" #include "hw/isa/isa.h" #include "qom/object.h" @@ -109,12 +110,11 @@ struct X86MachineState { #define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE) -uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, +void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); +uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index); -void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp); void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); -CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx); void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count); void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); From d0be0ac2c37eb978ed51c822b4a8a7dc9015e1e0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:42 +0200 Subject: [PATCH 0599/2884] hw/i386: move rtc-reset-reinjection command out of hw/rtc The rtc-reset-reinjection QMP command is specific to x86, other boards do not have the ACK tracking functionality that is needed for RTC interrupt reinjection. Therefore the QMP command is only included in x86, but qmp_rtc_reset_reinjection() is implemented by hw/rtc/mc146818rtc.c and requires tracking of all created RTC devices. Move the implementation to hw/i386, so that 1) it is available even if no RTC device exist 2) the only RTC that exists is easily found in x86ms->rtc. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-12-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 1 + hw/i386/monitor.c | 46 ++++++++++++++++++++++++++++++++++++ hw/rtc/mc146818rtc.c | 12 ++-------- include/hw/rtc/mc146818rtc.h | 2 +- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 hw/i386/monitor.c diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 3437da0aad..03aad10df7 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -2,6 +2,7 @@ i386_ss = ss.source_set() i386_ss.add(files( 'fw_cfg.c', 'e820_memory_layout.c', + 'monitor.c', 'multiboot.c', 'x86.c', 'x86-cpu.c', diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c new file mode 100644 index 0000000000..1ebd3564bf --- /dev/null +++ b/hw/i386/monitor.c @@ -0,0 +1,46 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor/monitor.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" +#include "hw/i386/x86.h" +#include "hw/rtc/mc146818rtc.h" + +#include CONFIG_DEVICES + +void qmp_rtc_reset_reinjection(Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(qdev_get_machine()); + +#ifdef CONFIG_MC146818RTC + if (x86ms->rtc) { + rtc_reset_reinjection(MC146818_RTC(x86ms->rtc)); + } +#else + assert(!x86ms->rtc); +#endif +} diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 3379f92748..8ccee9a385 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -104,16 +104,9 @@ static void rtc_coalesced_timer_update(MC146818RtcState *s) } } -static QLIST_HEAD(, MC146818RtcState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -void qmp_rtc_reset_reinjection(Error **errp) +void rtc_reset_reinjection(MC146818RtcState *rtc) { - MC146818RtcState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } + rtc->irq_coalesced = 0; } static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s) @@ -941,7 +934,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) object_property_add_tm(OBJECT(s), "date", rtc_get_date); qdev_init_gpio_out(dev, &s->irq, 1); - QLIST_INSERT_HEAD(&rtc_devices, s, link); } MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 97cec0b3e8..64893be151 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -55,6 +55,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val); int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr); -void qmp_rtc_reset_reinjection(Error **errp); +void rtc_reset_reinjection(MC146818RtcState *rtc); #endif /* HW_RTC_MC146818RTC_H */ From 1b1badf3c523105d4cf5e6ccf8413d1be1dbadc4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:43 +0200 Subject: [PATCH 0600/2884] i386: select correct components for no-board build The local APIC is a part of the CPU and has callbacks that are invoked from multiple accelerators. The IOAPIC on the other hand is optional, but ioapic_eoi_broadcast is used by common x86 code to implement the IOAPIC's implicit EOI mode. Add a stub in case the IOAPIC device is not included but the APIC is. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu Message-ID: <20240509170044.190795-13-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 2 +- hw/intc/ioapic-stub.c | 29 +++++++++++++++++++++++++++++ hw/intc/meson.build | 2 +- target/i386/Kconfig | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 hw/intc/ioapic-stub.c diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 8ca3e0586c..7af5c1ce8a 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -650,7 +650,7 @@ build-tci: # Check our reduced build configurations # requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, # mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 -# does not build without boards: i386, x86_64 +# fails qtest without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template needs: diff --git a/hw/intc/ioapic-stub.c b/hw/intc/ioapic-stub.c new file mode 100644 index 0000000000..4dcd86248d --- /dev/null +++ b/hw/intc/ioapic-stub.c @@ -0,0 +1,29 @@ +/* + * ioapic.c IOAPIC emulation logic + * + * Copyright (c) 2004-2005 Fabrice Bellard + * + * Split the ioapic logic from apic.c + * Xiantao Zhang + * + * 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.1 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/ioapic.h" + +void ioapic_eoi_broadcast(int vector) +{ +} diff --git a/hw/intc/meson.build b/hw/intc/meson.build index f4b540e6a8..0d1b7d0a43 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -20,7 +20,7 @@ system_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) system_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) system_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) -system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) +system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'), if_false: files('ioapic-stub.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) diff --git a/target/i386/Kconfig b/target/i386/Kconfig index ad9291d3b8..6b0feef029 100644 --- a/target/i386/Kconfig +++ b/target/i386/Kconfig @@ -1,5 +1,6 @@ config I386 bool + select APIC # kvm_arch_fixup_msi_route() needs to access PCIDevice select PCI if KVM From 6bd92a7c62648eb37053ccc809c3cab7d7629584 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 19:00:44 +0200 Subject: [PATCH 0601/2884] tests/qtest: arm: fix operation in a build without any boards or devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARM/aarch64 are easy to fix because they already have to pass a machine type by hand. Just guard the tests with a check that the machine actually exists. Signed-off-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20240509170044.190795-14-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- tests/qtest/arm-cpu-features.c | 4 ++++ tests/qtest/migration-test.c | 6 ++++++ tests/qtest/numa-test.c | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 9d6e6190d5..966c65d5c3 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -632,6 +632,10 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + if (!qtest_has_machine("virt")) { + goto out; + } + if (qtest_has_accel("tcg")) { qtest_add_data_func("/arm/query-cpu-model-expansion", NULL, test_query_cpu_model_expansion); diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 7a1345f80f..e8d3555f56 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -781,6 +781,12 @@ static int test_migrate_start(QTestState **from, QTestState **to, kvm_opts = ",dirty-ring-size=4096"; } + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return -1; + } + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, QEMU_ENV_DST); diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 4f4404a4b1..7aa262dbb9 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -558,6 +558,9 @@ int main(int argc, char **argv) } if (g_str_equal(arch, "aarch64")) { + if (!qtest_has_machine("virt")) { + goto out; + } g_string_append(args, " -machine virt"); } @@ -590,5 +593,6 @@ int main(int argc, char **argv) aarch64_numa_cpu); } +out: return g_test_run(); } From 727bb5b477e66b6cd8a558bf2cdf9a666b914f27 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 12:22:31 +0200 Subject: [PATCH 0602/2884] meson: pick libfdt from common_ss when building target-specific files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid having to list dependencies such as libfdt twice, both on common_ss and specific_ss. Instead, just take all the dependencies in common_ss and allow the target-specific libqemu-*.fa library to use them. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/arm/meson.build | 2 +- hw/loongarch/meson.build | 2 +- hw/mips/meson.build | 2 +- hw/openrisc/meson.build | 4 ++-- hw/ppc/meson.build | 4 +--- hw/riscv/meson.build | 2 +- meson.build | 14 +++++++++++--- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 6808135c1f..aefde0c69a 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -1,5 +1,5 @@ arm_ss = ss.source_set() -arm_ss.add(files('boot.c'), fdt) +arm_ss.add(files('boot.c')) arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index d306d82c2e..bce7ebac97 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -3,7 +3,7 @@ loongarch_ss.add(files( 'fw_cfg.c', 'boot.c', )) -loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt]) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c')) loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/mips/meson.build b/hw/mips/meson.build index f06d88f343..ca37c42d90 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -9,7 +9,7 @@ if 'CONFIG_TCG' in config_all_accel mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) -mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt]) +mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c')) endif hw_arch += {'mips': mips_ss} diff --git a/hw/openrisc/meson.build b/hw/openrisc/meson.build index 2dbc6365bb..82f1f0ef1c 100644 --- a/hw/openrisc/meson.build +++ b/hw/openrisc/meson.build @@ -1,7 +1,7 @@ openrisc_ss = ss.source_set() openrisc_ss.add(files('cputimer.c')) openrisc_ss.add(files('boot.c')) -openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: [files('openrisc_sim.c'), fdt]) -openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: [files('virt.c'), fdt]) +openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: files('openrisc_sim.c')) +openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: files('virt.c')) hw_arch += {'openrisc': openrisc_ss} diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index d096636ee7..3ebbf329bc 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -3,9 +3,7 @@ ppc_ss.add(files( 'ppc.c', 'ppc_booke.c', )) -ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: [files( - 'fdt.c', -), fdt]) +ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: files('fdt.c')) ppc_ss.add(when: 'CONFIG_FW_CFG_PPC', if_true: files('fw_cfg.c')) # IBM pSeries (sPAPR) diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 2f7ee81be3..f872674093 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -1,5 +1,5 @@ riscv_ss = ss.source_set() -riscv_ss.add(files('boot.c'), fdt) +riscv_ss.add(files('boot.c')) riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) diff --git a/meson.build b/meson.build index 83ae4347c7..ab1f44b25c 100644 --- a/meson.build +++ b/meson.build @@ -3867,15 +3867,23 @@ foreach target : target_dirs target_common = common_ss.apply(config_target, strict: false) objects = common_all.extract_objects(target_common.sources()) - deps = target_common.dependencies() + arch_deps += target_common.dependencies() target_specific = specific_ss.apply(config_target, strict: false) arch_srcs += target_specific.sources() arch_deps += target_specific.dependencies() + # allow using headers from the dependencies but do not include the sources, + # because this emulator only needs those in "objects". For external + # dependencies, the full dependency is included below in the executable. + lib_deps = [] + foreach dep : arch_deps + lib_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + lib = static_library('qemu-' + target, sources: arch_srcs + genh, - dependencies: arch_deps, + dependencies: lib_deps, objects: objects, include_directories: target_inc, c_args: c_args, @@ -3923,7 +3931,7 @@ foreach target : target_dirs emulator = executable(exe_name, exe['sources'], install: true, c_args: c_args, - dependencies: arch_deps + deps + exe['dependencies'], + dependencies: arch_deps + exe['dependencies'], objects: lib.extract_all_objects(recursive: true), link_depends: [block_syms, qemu_syms], link_args: link_args, From 7a6f3343b6f140c945b62a972b36d22efa14bdba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jan 2024 12:22:57 +0100 Subject: [PATCH 0603/2884] meson: move libfdt together with other dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the libfdt detection code together with other dependencies instead of keeping it with subprojects. This has the disadvantage of performing the detection even if no target requires libfdt; but it has the advantage that Kconfig will be able to observe the availability of the library. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- meson.build | 79 ++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/meson.build b/meson.build index ab1f44b25c..dd4a28f8f8 100644 --- a/meson.build +++ b/meson.build @@ -1858,6 +1858,34 @@ if numa.found() and not cc.links(''' endif endif +fdt = not_found +fdt_opt = get_option('fdt') +if fdt_opt == 'enabled' and get_option('wrap_mode') == 'nodownload' + fdt_opt = 'system' +endif +if fdt_opt in ['enabled', 'system'] or (fdt_opt == 'auto' and have_system) + fdt = cc.find_library('fdt', required: fdt_opt == 'system') + if fdt.found() and cc.links(''' + #include + #include + int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', + dependencies: fdt) + fdt_opt = 'system' + elif fdt_opt != 'system' + fdt_opt = get_option('wrap_mode') == 'nodownload' ? 'disabled' : 'internal' + fdt = not_found + else + error('system libfdt is too old (1.5.1 or newer required)') + endif +endif +if fdt_opt == 'internal' + assert(not fdt.found()) + libfdt_proj = subproject('dtc', required: true, + default_options: ['tools=false', 'yaml=disabled', + 'python=disabled', 'default_library=static']) + fdt = libfdt_proj.get_variable('libfdt_dep') +endif + rdma = not_found if not get_option('rdma').auto() or have_system libumad = cc.find_library('ibumad', required: get_option('rdma')) @@ -2199,6 +2227,7 @@ config_host_data.set('CONFIG_BSD', host_os in bsd_oses) config_host_data.set('CONFIG_CAPSTONE', capstone.found()) config_host_data.set('CONFIG_COCOA', cocoa.found()) config_host_data.set('CONFIG_DARWIN', host_os == 'darwin') +config_host_data.set('CONFIG_FDT', fdt.found()) config_host_data.set('CONFIG_FUZZ', get_option('fuzzing')) config_host_data.set('CONFIG_GCOV', get_option('b_coverage')) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) @@ -3024,14 +3053,16 @@ foreach target : target_dirs error('No accelerator available for target @0@'.format(target)) endif - actual_target_dirs += target config_target += keyval.load('configs/targets' / target + '.mak') config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } - if 'TARGET_NEED_FDT' in config_target + if 'TARGET_NEED_FDT' in config_target and not fdt.found() fdt_required += target + continue endif + actual_target_dirs += target + # Add default keys if 'TARGET_BASE_ARCH' not in config_target config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']} @@ -3119,6 +3150,10 @@ genh += custom_target('config-poison.h', command: [find_program('scripts/make-config-poison.sh'), target_configs_h]) +if fdt_required.length() > 0 + error('fdt disabled but required by targets ' + ', '.join(fdt_required)) +endif + ############### # Subprojects # ############### @@ -3129,44 +3164,6 @@ if have_system and vfio_user_server_allowed libvfio_user_dep = libvfio_user_proj.get_variable('libvfio_user_dep') endif -fdt = not_found -fdt_opt = get_option('fdt') -if fdt_required.length() > 0 or fdt_opt == 'enabled' - if fdt_opt == 'disabled' - error('fdt disabled but required by targets ' + ', '.join(fdt_required)) - endif - - if fdt_opt in ['enabled', 'auto', 'system'] - if get_option('wrap_mode') == 'nodownload' - fdt_opt = 'system' - endif - fdt = cc.find_library('fdt', required: fdt_opt == 'system') - if fdt.found() and cc.links(''' - #include - #include - int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', - dependencies: fdt) - fdt_opt = 'system' - elif fdt_opt == 'system' - error('system libfdt requested, but it is too old (1.5.1 or newer required)') - else - fdt_opt = 'internal' - fdt = not_found - endif - endif - if not fdt.found() - assert(fdt_opt == 'internal') - libfdt_proj = subproject('dtc', required: true, - default_options: ['tools=false', 'yaml=disabled', - 'python=disabled', 'default_library=static']) - fdt = libfdt_proj.get_variable('libfdt_dep') - endif -else - fdt_opt = 'disabled' -endif - -config_host_data.set('CONFIG_FDT', fdt.found()) - vhost_user = not_found if host_os == 'linux' and have_vhost_user libvhost_user = subproject('libvhost-user') @@ -4417,7 +4414,7 @@ summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} summary_info += {'RDMA support': rdma} -summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} +summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} summary_info += {'rbd support': rbd} From 1935b7ead18dbcbf459dbe7a8fd4253fbe1ed4d0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 7 May 2024 14:13:46 +0200 Subject: [PATCH 0604/2884] kconfig: allow compiling out QEMU device tree code per target Introduce a new Kconfig symbol, CONFIG_DEVICE_TREE, that specifies whether to include the common device tree code in system/device_tree.c and to link to libfdt. For now, include it unconditionally if libfdt is available. Signed-off-by: Paolo Bonzini --- Kconfig.host | 4 ++++ hw/core/Kconfig | 9 ++++++++- hw/core/meson.build | 2 +- include/monitor/hmp.h | 1 + include/sysemu/device_tree.h | 1 - meson.build | 1 + monitor/hmp-cmds.c | 17 +++++++++++++++++ system/device_tree-stub.c | 10 ++++++++++ system/device_tree.c | 14 -------------- system/meson.build | 4 +++- 10 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 system/device_tree-stub.c diff --git a/Kconfig.host b/Kconfig.host index f6a2a131e6..a0d4a52131 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -23,6 +23,10 @@ config IVSHMEM config TPM bool +config FDT + bool + select DEVICE_TREE + config VHOST_USER bool diff --git a/hw/core/Kconfig b/hw/core/Kconfig index 9397503656..24411f5930 100644 --- a/hw/core/Kconfig +++ b/hw/core/Kconfig @@ -4,8 +4,14 @@ config EMPTY_SLOT config PTIMER bool +config DEVICE_TREE + bool + # fail the build if libfdt not found + depends on FDT + config FITLOADER bool + depends on DEVICE_TREE config GENERIC_LOADER bool @@ -14,13 +20,14 @@ config GENERIC_LOADER config GUEST_LOADER bool default y - depends on TCG + depends on TCG && DEVICE_TREE config OR_IRQ bool config PLATFORM_BUS bool + depends on DEVICE_TREE config REGISTER bool diff --git a/hw/core/meson.build b/hw/core/meson.build index f20d4143f7..a3d9bab9f4 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -16,7 +16,7 @@ common_ss.add(files('cpu-common.c')) common_ss.add(files('machine-smp.c')) system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) -system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) +system_ss.add(when: 'CONFIG_GUEST_LOADER', if_true: files('guest-loader.c')) system_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) system_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index f4cf8f6717..954f3c83ad 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -180,5 +180,6 @@ void hmp_ioport_write(Monitor *mon, const QDict *qdict); void hmp_boot_set(Monitor *mon, const QDict *qdict); void hmp_info_mtree(Monitor *mon, const QDict *qdict); void hmp_info_cryptodev(Monitor *mon, const QDict *qdict); +void hmp_dumpdtb(Monitor *mon, const QDict *qdict); #endif diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index 8eab395934..eb601522f8 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -134,7 +134,6 @@ int qemu_fdt_add_path(void *fdt, const char *path); } while (0) void qemu_fdt_dumpdtb(void *fdt, int size); -void hmp_dumpdtb(Monitor *mon, const QDict *qdict); /** * qemu_fdt_setprop_sized_cells_from_array: diff --git a/meson.build b/meson.build index dd4a28f8f8..8b90ea6cf7 100644 --- a/meson.build +++ b/meson.build @@ -2989,6 +2989,7 @@ host_kconfig = \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \ (x11.found() ? ['CONFIG_X11=y'] : []) + \ + (fdt.found() ? ['CONFIG_FDT=y'] : []) + \ (have_vhost_user ? ['CONFIG_VHOST_USER=y'] : []) + \ (have_vhost_vdpa ? ['CONFIG_VHOST_VDPA=y'] : []) + \ (have_vhost_kernel ? ['CONFIG_VHOST_KERNEL=y'] : []) + \ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 871898ac46..ea79148ee8 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -22,6 +22,7 @@ #include "monitor/monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" #include "qemu/cutils.h" @@ -443,3 +444,19 @@ void hmp_info_mtree(Monitor *mon, const QDict *qdict) mtree_info(flatview, dispatch_tree, owner, disabled); } + +#if defined(CONFIG_FDT) +void hmp_dumpdtb(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + Error *local_err = NULL; + + qmp_dumpdtb(filename, &local_err); + + if (hmp_handle_error(mon, local_err)) { + return; + } + + monitor_printf(mon, "dtb dumped to %s", filename); +} +#endif diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c new file mode 100644 index 0000000000..bddda6fa37 --- /dev/null +++ b/system/device_tree-stub.c @@ -0,0 +1,10 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +#ifdef CONFIG_FDT +void qmp_dumpdtb(const char *filename, Error **errp) +{ + error_setg(errp, "This machine doesn't have a FDT"); +} +#endif diff --git a/system/device_tree.c b/system/device_tree.c index eb5166ca36..2e38259d34 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -668,20 +668,6 @@ void qmp_dumpdtb(const char *filename, Error **errp) } } -void hmp_dumpdtb(Monitor *mon, const QDict *qdict) -{ - const char *filename = qdict_get_str(qdict, "filename"); - Error *local_err = NULL; - - qmp_dumpdtb(filename, &local_err); - - if (hmp_handle_error(mon, local_err)) { - return; - } - - info_report("dtb dumped to %s", filename); -} - void qemu_fdt_randomize_seeds(void *fdt) { int noffset, poffset, len; diff --git a/system/meson.build b/system/meson.build index 25e2117250..a296270cb0 100644 --- a/system/meson.build +++ b/system/meson.build @@ -32,7 +32,9 @@ if have_tpm endif system_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) -system_ss.add(when: fdt, if_true: files('device_tree.c')) +system_ss.add(when: 'CONFIG_DEVICE_TREE', + if_true: [fdt, files('device_tree.c')], + if_false: files('device_tree-stub.c')) if host_os == 'linux' system_ss.add(files('async-teardown.c')) endif From d641ec30be4963f43bffe033f5265a57cb8f4c90 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 Apr 2024 21:08:15 +0200 Subject: [PATCH 0605/2884] kconfig: express dependency of individual boards on libfdt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that boards are enabled by default and the "CONFIG_FOO=y" entries are gone from configs/devices/, there cannot be any more a conflicts between the default contents of configs/devices/ and a failed "depends on" clause. With this change, each individual board or target can express whether it needs FDT. It can then include the common code in the build via "select DEVICE_TREE", which will also as tell meson to link with libfdt. This allows building non-microvm x86 emulators without having libfdt available. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- Kconfig.host | 1 - hw/arm/Kconfig | 5 +++++ hw/i386/Kconfig | 3 ++- hw/loongarch/Kconfig | 3 ++- hw/mips/Kconfig | 3 ++- hw/openrisc/Kconfig | 2 ++ hw/ppc/Kconfig | 15 ++++++++------- hw/riscv/Kconfig | 4 ++++ hw/rx/Kconfig | 3 ++- hw/xtensa/Kconfig | 1 + target/arm/Kconfig | 2 ++ target/microblaze/Kconfig | 1 + target/openrisc/Kconfig | 1 + target/riscv/Kconfig | 2 ++ 14 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Kconfig.host b/Kconfig.host index a0d4a52131..17f405004b 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -25,7 +25,6 @@ config TPM config FDT bool - select DEVICE_TREE config VHOST_USER bool diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 98c264ed21..8b97683a45 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -15,6 +15,7 @@ config ARM_VIRT select ACPI select ARM_SMMUV3 select GPIO_KEY + select DEVICE_TREE select FW_CFG_DMA select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE @@ -265,6 +266,7 @@ config SBSA_REF default y depends on TCG && AARCH64 imply PCI_DEVICES + select DEVICE_TREE select AHCI select ARM_SMMUV3 select GPIO_KEY @@ -347,6 +349,7 @@ config VEXPRESS bool default y depends on TCG && ARM + select DEVICE_TREE select A9MPCORE select A15MPCORE select ARM_MPTIMER @@ -492,6 +495,7 @@ config XLNX_ZYNQMP_ARM select CPU_CLUSTER select DDC select DPCD + select DEVICE_TREE select SDHCI select SSI select SSI_M25P80 @@ -509,6 +513,7 @@ config XLNX_VERSAL depends on TCG && AARCH64 select ARM_GIC select CPU_CLUSTER + select DEVICE_TREE select PL011 select CADENCE select VIRTIO_MMIO diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 40b1e44580..f4a33b6c08 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -115,7 +115,8 @@ config Q35 config MICROVM bool default y - depends on I386 + depends on I386 && FDT + select DEVICE_TREE select SERIAL_ISA # for serial_hds_isa_init() select ISA_BUS select APIC diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index ad77502445..90a0dba9d5 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -1,7 +1,8 @@ config LOONGARCH_VIRT bool default y - depends on LOONGARCH64 + depends on LOONGARCH64 && FDT + select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE imply VIRTIO_VGA diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 9bccb363eb..a7f26edebe 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -80,9 +80,10 @@ config MIPS_CPS config MIPS_BOSTON bool default y - depends on MIPS64 && !TARGET_BIG_ENDIAN + depends on MIPS64 && !TARGET_BIG_ENDIAN && FDT imply PCI_DEVICES imply TEST_DEVICES + select DEVICE_TREE select FITLOADER select MIPS_CPS select PCI_EXPRESS_XILINX diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig index 9c9015e0a5..76b953c62c 100644 --- a/hw/openrisc/Kconfig +++ b/hw/openrisc/Kconfig @@ -2,6 +2,7 @@ config OR1K_SIM bool default y depends on OPENRISC + select DEVICE_TREE select SERIAL select OPENCORES_ETH select OMPIC @@ -14,6 +15,7 @@ config OR1K_VIRT imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES + select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE select GOLDFISH_RTC diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 78f83e78ce..347212f4db 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -1,7 +1,7 @@ config PSERIES bool default y - depends on PPC64 + depends on PPC64 && FDT imply USB_OHCI_PCI imply PCI_DEVICES imply TEST_DEVICES @@ -26,7 +26,7 @@ config SPAPR_RNG config POWERNV bool default y - depends on PPC64 + depends on PPC64 && FDT imply PCI_DEVICES imply TEST_DEVICES select ISA_IPMI_BT @@ -52,7 +52,7 @@ config PPC405 config PPC440 bool default y - depends on PPC + depends on PPC && FDT imply PCI_DEVICES imply TEST_DEVICES imply E1000_PCI @@ -71,7 +71,7 @@ config PPC4XX config SAM460EX bool default y - depends on PPC + depends on PPC && FDT select PFLASH_CFI01 select IDE_SII3112 select M41T80 @@ -168,19 +168,19 @@ config E500 config E500PLAT bool default y - depends on PPC + depends on PPC && FDT select E500 config MPC8544DS bool default y - depends on PPC + depends on PPC && FDT select E500 config VIRTEX bool default y - depends on PPC + depends on PPC && FDT select PPC4XX select PFLASH_CFI01 select SERIAL @@ -193,6 +193,7 @@ config FW_CFG_PPC bool config FDT_PPC + select DEVICE_TREE bool config VOF diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 5f5f9e31bb..a2030e3a6f 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -12,6 +12,7 @@ config MICROCHIP_PFSOC depends on RISCV64 select CADENCE_SDHCI select CPU_CLUSTER + select DEVICE_TREE select MCHP_PFSOC_DMC select MCHP_PFSOC_IOSCB select MCHP_PFSOC_MMUART @@ -37,6 +38,7 @@ config RISCV_VIRT imply VIRTIO_VGA imply TEST_DEVICES imply TPM_TIS_SYSBUS + select DEVICE_TREE select RISCV_NUMA select GOLDFISH_RTC select PCI @@ -82,6 +84,7 @@ config SIFIVE_U depends on RISCV32 || RISCV64 select CADENCE select CPU_CLUSTER + select DEVICE_TREE select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PDMA @@ -99,6 +102,7 @@ config SPIKE bool default y depends on RISCV32 || RISCV64 + select DEVICE_TREE select RISCV_NUMA select HTIF select RISCV_ACLINT diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig index b2fa2b7eec..aa9242d1ef 100644 --- a/hw/rx/Kconfig +++ b/hw/rx/Kconfig @@ -8,5 +8,6 @@ config RX62N_MCU config RX_GDBSIM bool default y - depends on RX + depends on RX && FDT + select DEVICE_TREE select RX62N_MCU diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig index 443b415c2b..8ea283a7a3 100644 --- a/hw/xtensa/Kconfig +++ b/hw/xtensa/Kconfig @@ -15,6 +15,7 @@ config XTENSA_XTFPGA bool default y depends on XTENSA + imply DEVICE_TREE select OPENCORES_ETH select PFLASH_CFI01 select SERIAL diff --git a/target/arm/Kconfig b/target/arm/Kconfig index 5847c5a74a..7f8a2217ae 100644 --- a/target/arm/Kconfig +++ b/target/arm/Kconfig @@ -6,6 +6,8 @@ config ARM # translate.c v7m helpers under ARM_V7M. select ARM_V7M if TCG + select DEVICE_TREE # needed by boot.c + config AARCH64 bool select ARM diff --git a/target/microblaze/Kconfig b/target/microblaze/Kconfig index a5410d9218..e91d58d88f 100644 --- a/target/microblaze/Kconfig +++ b/target/microblaze/Kconfig @@ -1,2 +1,3 @@ config MICROBLAZE bool + select DEVICE_TREE # needed by boot.c diff --git a/target/openrisc/Kconfig b/target/openrisc/Kconfig index e0da4ac1df..cd66c2e3b6 100644 --- a/target/openrisc/Kconfig +++ b/target/openrisc/Kconfig @@ -1,2 +1,3 @@ config OPENRISC bool + select DEVICE_TREE # needed by boot.c diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig index adb7de3f37..5f30df22f2 100644 --- a/target/riscv/Kconfig +++ b/target/riscv/Kconfig @@ -1,7 +1,9 @@ config RISCV32 bool select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() + select DEVICE_TREE # needed by boot.c config RISCV64 bool select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() + select DEVICE_TREE # needed by boot.c From 47771d67563dc48753d275249d0764144d56357d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Jan 2024 10:19:17 +0100 Subject: [PATCH 0606/2884] hw/xtensa: require libfdt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other boards require libfdt if it can be used (including for example i386/x86_64), so change the "imply" to "select" and always allow -dtb in qemu-system-xtensa. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/xtensa/Kconfig | 4 ++-- hw/xtensa/xtfpga.c | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig index 8ea283a7a3..fc5c785cfa 100644 --- a/hw/xtensa/Kconfig +++ b/hw/xtensa/Kconfig @@ -14,8 +14,8 @@ config XTENSA_VIRT config XTENSA_XTFPGA bool default y - depends on XTENSA - imply DEVICE_TREE + depends on XTENSA && FDT + select DEVICE_TREE select OPENCORES_ETH select PFLASH_CFI01 select SERIAL diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index f49e6591dc..955e8867a3 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -356,7 +356,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_tagptr = put_tag(cur_tagptr, BP_TAG_COMMAND_LINE, strlen(kernel_cmdline) + 1, kernel_cmdline); } -#ifdef CONFIG_FDT if (dtb_filename) { int fdt_size; void *fdt = load_device_tree(dtb_filename, &fdt_size); @@ -373,14 +372,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB); g_free(fdt); } -#else - if (dtb_filename) { - error_report("could not load DTB '%s': " - "FDT support is not configured in QEMU", - dtb_filename); - exit(EXIT_FAILURE); - } -#endif if (initrd_filename) { BpMemInfo initrd_location = { 0 }; int initrd_size = load_ramdisk(initrd_filename, cur_lowmem, From 9b089d254a5623baef01f7d0ec37331c1155f1ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 09:25:48 +0200 Subject: [PATCH 0607/2884] configs: disable emulators that require it if libfdt is not found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since boards can express their dependency on libfdt and system/device_tree.c, only leave TARGET_NEED_FDT if the target has a hard dependency. Those emulators will be skipped if libfdt is disabled, or if it is "auto" and not found and --disable-download is passed; unless the target is mentioned explicitly in --target-list, in which case the build will fail. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 8 ++++---- configs/targets/aarch64-softmmu.mak | 1 + configs/targets/arm-softmmu.mak | 1 + configs/targets/i386-softmmu.mak | 1 - configs/targets/loongarch64-softmmu.mak | 1 + configs/targets/microblaze-softmmu.mak | 1 + configs/targets/microblazeel-softmmu.mak | 1 + configs/targets/mips64el-softmmu.mak | 1 - configs/targets/or1k-softmmu.mak | 1 + configs/targets/ppc-softmmu.mak | 1 - configs/targets/ppc64-softmmu.mak | 1 + configs/targets/riscv32-softmmu.mak | 1 + configs/targets/riscv64-softmmu.mak | 1 + configs/targets/rx-softmmu.mak | 1 + configs/targets/x86_64-softmmu.mak | 1 - meson.build | 6 +++++- 16 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 7af5c1ce8a..372404fc85 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -648,8 +648,8 @@ build-tci: - make check-tcg # Check our reduced build configurations -# requires libfdt: aarch64, arm, i386, loongarch64, microblaze, microblazeel, -# mips64el, or1k, ppc, ppc64, riscv32, riscv64, rx, x86_64 +# requires libfdt: aarch64, arm, loongarch64, microblaze, microblazeel, +# or1k, ppc64, riscv32, riscv64, rx # fails qtest without boards: i386, x86_64 build-without-defaults: extends: .native_build_job_template @@ -665,8 +665,8 @@ build-without-defaults: --disable-qom-cast-debug --disable-strip TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu - mips-softmmu mips64-softmmu mipsel-softmmu - s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu + mips-softmmu mips64-softmmu mipsel-softmmu mips64el-softmmu + ppc-softmmu s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu hexagon-linux-user i386-linux-user s390x-linux-user MAKE_CHECK_ARGS: check diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index 83c22391a6..84cb32dc2f 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -3,4 +3,5 @@ TARGET_BASE_ARCH=arm TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index 92c8349b96..bf390b7a8d 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=arm TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index d61b507613..2ac69d5ba3 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=i386 TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index f23780fdd8..84beb19b90 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index e84c0cc728..eea266d4f3 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -1,5 +1,6 @@ TARGET_ARCH=microblaze TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +# needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index 9b688036bd..77b968acad 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=microblaze TARGET_SUPPORTS_MTTCG=y +# needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak index 8d9ab3ddc4..3864daa736 100644 --- a/configs/targets/mips64el-softmmu.mak +++ b/configs/targets/mips64el-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips -TARGET_NEED_FDT=y diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 432f855a30..0341cb2a6b 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=openrisc TARGET_SUPPORTS_MTTCG=y TARGET_BIG_ENDIAN=y +# needed by boot.c and all boards TARGET_NEED_FDT=y diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index f3ea9c98f7..53120dab41 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -2,4 +2,3 @@ TARGET_ARCH=ppc TARGET_BIG_ENDIAN=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml -TARGET_NEED_FDT=y diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index 1db8d8381d..40881d9396 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -4,4 +4,5 @@ TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak index d8b71cddcd..338182d5b8 100644 --- a/configs/targets/riscv32-softmmu.mak +++ b/configs/targets/riscv32-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index 7c0e7eeb42..f688ffa7bc 100644 --- a/configs/targets/riscv64-softmmu.mak +++ b/configs/targets/riscv64-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak index 0c458b2d07..706bbe6062 100644 --- a/configs/targets/rx-softmmu.mak +++ b/configs/targets/rx-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=rx TARGET_XML_FILES= gdb-xml/rx-core.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index c5f882e5ba..e12ac3dc59 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/meson.build b/meson.build index 8b90ea6cf7..9c14192514 100644 --- a/meson.build +++ b/meson.build @@ -3058,7 +3058,11 @@ foreach target : target_dirs config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } if 'TARGET_NEED_FDT' in config_target and not fdt.found() - fdt_required += target + if default_targets + warning('Disabling ' + target + ' due to missing libfdt') + else + fdt_required += target + endif continue endif From 371d60dfdb47dd18d163a7759968ba138089371e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 May 2024 13:40:10 +0200 Subject: [PATCH 0608/2884] configure: Fix error message when C compiler is not working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you try to run the configure script on a system without a working C compiler, you get a very misleading error message: ERROR: Unrecognized host OS (uname -s reports 'Linux') Some people already opened bug tickets because of this problem: https://gitlab.com/qemu-project/qemu/-/issues/2057 https://gitlab.com/qemu-project/qemu/-/issues/2288 We should rather tell the user that we were not able to use the C compiler instead, otherwise they will have a hard time to figure out what was going wrong. While we're at it, let's also suppress the "unrecognized host CPU" message in this case since it is rather misleading than helpful. Fixes: 264b803721 ("configure: remove compiler sanity check") Message-ID: <20240513114010.51608-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- configure | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 330664786d..38ee257701 100755 --- a/configure +++ b/configure @@ -411,7 +411,9 @@ else # Using uname is really broken, but it is just a fallback for architectures # that are going to use TCI anyway cpu=$(uname -m) - echo "WARNING: unrecognized host CPU, proceeding with 'uname -m' output '$cpu'" + if test "$host_os" != "bogus"; then + echo "WARNING: unrecognized host CPU, proceeding with 'uname -m' output '$cpu'" + fi fi # Normalise host CPU name to the values used by Meson cross files and in source @@ -894,6 +896,13 @@ EOF exit 0 fi +# Now that we are sure that the user did not only want to print the --help +# information, we should double-check that the C compiler really works: +write_c_skeleton +if ! compile_object ; then + error_exit "C compiler \"$cc\" either does not exist or does not work." +fi + # Remove old dependency files to make sure that they get properly regenerated rm -f ./*/config-devices.mak.d From bad7a2759c69417a5558f0f19d4ede58c08705e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 13 May 2024 12:15:49 +0100 Subject: [PATCH 0609/2884] dockerfiles: add 'MAKE' env variable to remaining containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the lcitool generated containers define a "MAKE" env. It will be convenient for later patches if all containers do this. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240513111551.488088-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/docker/dockerfiles/debian-all-test-cross.docker | 1 + tests/docker/dockerfiles/debian-hexagon-cross.docker | 1 + tests/docker/dockerfiles/debian-legacy-test-cross.docker | 1 + tests/docker/dockerfiles/debian-loongarch-cross.docker | 1 + tests/docker/dockerfiles/debian-tricore-cross.docker | 1 + tests/docker/dockerfiles/debian-xtensa-cross.docker | 1 + tests/docker/dockerfiles/fedora-cris-cross.docker | 1 + 7 files changed, 7 insertions(+) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 2cc7a24d4d..6cc38a3633 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -68,6 +68,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sparc64-linux-user # As a final step configure the user (if env is defined) +ENV MAKE /usr/bin/make ARG USER ARG UID RUN if [ "${USER}" ]; then \ diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker index 60bd8faa20..f2d40f2dee 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -45,6 +45,7 @@ ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN curl -#SL "$TOOLCHAIN_URL" | tar -xJC "$TOOLCHAIN_INSTALL" ENV PATH $PATH:${TOOLCHAIN_INSTALL}/${TOOLCHAIN_BASENAME}/x86_64-linux-gnu/bin +ENV MAKE /usr/bin/make # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-legacy-test-cross.docker b/tests/docker/dockerfiles/debian-legacy-test-cross.docker index 8cc68bc912..d75e0b85e2 100644 --- a/tests/docker/dockerfiles/debian-legacy-test-cross.docker +++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker @@ -42,6 +42,7 @@ RUN /usr/bin/pip3 install tomli ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools ENV DEF_TARGET_LIST alpha-linux-user,sh4-linux-user +ENV MAKE /usr/bin/make # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker index b25e779a2c..6a9197528b 100644 --- a/tests/docker/dockerfiles/debian-loongarch-cross.docker +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -44,6 +44,7 @@ ENV LD_LIBRARY_PATH /opt/cross-tools/lib:/opt/cross-tools/loongarch64-unknown-li ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch-softmmu +ENV MAKE /usr/bin/make # As a final step configure the user (if env is defined) ARG USER diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index c597f8e16b..16276aa21d 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -44,6 +44,7 @@ RUN curl -#SL https://github.com/bkoppelmann/package_940/releases/download/trico # This image can only build a very minimal QEMU as well as the tests ENV DEF_TARGET_LIST tricore-softmmu ENV QEMU_CONFIGURE_OPTS --disable-user --disable-tools --disable-fdt +ENV MAKE /usr/bin/make # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker index 72c25d63d9..413881899b 100644 --- a/tests/docker/dockerfiles/debian-xtensa-cross.docker +++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker @@ -27,6 +27,7 @@ RUN for cpu in $CPU_LIST; do \ done ENV PATH $PATH:/opt/$TOOLCHAIN_RELEASE/xtensa-dc232b-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dc233c-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-de233_fpu-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dsp3400-elf/bin +ENV MAKE /usr/bin/make # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker index f2899af410..97c9d37ede 100644 --- a/tests/docker/dockerfiles/fedora-cris-cross.docker +++ b/tests/docker/dockerfiles/fedora-cris-cross.docker @@ -4,6 +4,7 @@ FROM registry.fedoraproject.org/fedora:33 ENV PACKAGES gcc-cris-linux-gnu +ENV MAKE /usr/bin/make RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt # As a final step configure the user (if env is defined) From c53f7a107879a2b7e719b07692a05289bf603fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 13 May 2024 12:15:50 +0100 Subject: [PATCH 0610/2884] gitlab: use $MAKE instead of 'make' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lcitool generated containers have '$MAKE' set to the path of the right 'make' binary. Using the env variable makes it possible to override the choice per job. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240513111551.488088-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 22045add80..278a5ea966 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -26,10 +26,10 @@ then pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ; fi || exit 1; - - make -j"$JOBS" + - $MAKE -j"$JOBS" - if test -n "$MAKE_CHECK_ARGS"; then - make -j"$JOBS" $MAKE_CHECK_ARGS ; + $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; fi - ccache --show-stats @@ -60,7 +60,7 @@ - cd build - find . -type f -exec touch {} + # Avoid recompiling by hiding ninja with NINJA=":" - - make NINJA=":" $MAKE_CHECK_ARGS + - $MAKE NINJA=":" $MAKE_CHECK_ARGS .native_test_job_template: extends: .common_test_job_template From b563959b906db53fb4bcaef1351f11a51c4b9582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 13 May 2024 12:15:51 +0100 Subject: [PATCH 0611/2884] gitlab: use 'setarch -R' to workaround tsan bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TSAN job started failing when gitlab rolled out their latest release. The root cause is a change in the Google COS version used on shared runners. This brings a kernel running with vm.mmap_rnd_bits = 31 which is incompatible with TSAN in LLVM < 18, which only supports upto '28'. LLVM 18 can support upto '30', and failing that will re-exec itself to turn off VA randomization. Our LLVM is too old for now, but we can run with 'setarch -R make ..' to turn off VA randomization ourselves. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20240513111551.488088-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 372404fc85..91c57efded 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -575,6 +575,9 @@ tsan-build: CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++ --enable-trace-backends=ust --disable-slirp TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user + # Remove when we switch to a distro with clang >= 18 + # https://github.com/google/sanitizers/issues/1716 + MAKE: setarch -R make # gcov is a GCC features gcov: From 0d8caac9042667edd4198144035cff770b3691cf Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:50 +0200 Subject: [PATCH 0612/2884] Bump minimum glib version to v2.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we dropped support for CentOS 8 and Ubuntu 20.04, we can look into bumping the glib version to a new minimum for further clean-ups. According to repology.org, available versions are: CentOS Stream 9: 2.66.7 Debian 11: 2.66.8 Fedora 38: 2.74.1 Freebsd: 2.78.4 Homebrew: 2.80.0 Openbsd: 2.78.4 OpenSuse leap 15.5: 2.70.5 pkgsrc_current: 2.78.4 Ubuntu 22.04: 2.72.1 Thus it should be safe to bump the minimum glib version to 2.66 now. Version 2.66 comes with new functions for URI parsing which will allow further clean-ups in the following patches. Reviewed-by: Daniel P. Berrangé Message-ID: <20240418101056.302103-8-thuth@redhat.com> Signed-off-by: Thomas Huth --- include/glib-compat.h | 27 ++------------------------- meson.build | 16 +--------------- qga/commands-posix-ssh.c | 4 ++-- 3 files changed, 5 insertions(+), 42 deletions(-) diff --git a/include/glib-compat.h b/include/glib-compat.h index 43a562974d..86be439ba0 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -19,12 +19,12 @@ /* Ask for warnings for anything that was marked deprecated in * the defined version, or before. It is a candidate for rewrite. */ -#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56 +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66 /* Ask for warnings if code tries to use function that did not * exist in the defined version. These risk breaking builds */ -#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56 +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -105,29 +105,6 @@ static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size) } #define g_memdup2(m, s) g_memdup2_qemu(m, s) -#if defined(G_OS_UNIX) -/* - * Note: The fallback implementation is not MT-safe, and it returns a copy of - * the libc passwd (must be g_free() after use) but not the content. Because of - * these important differences the caller must be aware of, it's not #define for - * GLib API substitution. - */ -static inline struct passwd * -g_unix_get_passwd_entry_qemu(const gchar *user_name, GError **error) -{ -#if GLIB_CHECK_VERSION(2, 64, 0) - return g_unix_get_passwd_entry(user_name, error); -#else - struct passwd *p = getpwnam(user_name); - if (!p) { - g_set_error_literal(error, G_UNIX_ERROR, 0, g_strerror(errno)); - return NULL; - } - return (struct passwd *)g_memdup(p, sizeof(*p)); -#endif -} -#endif /* G_OS_UNIX */ - static inline bool qemu_g_test_slow(void) { diff --git a/meson.build b/meson.build index 9c14192514..a9de71d450 100644 --- a/meson.build +++ b/meson.build @@ -869,7 +869,7 @@ have_xen_pci_passthrough = get_option('xen_pci_passthrough') \ # When bumping glib minimum version, please check also whether to increase # the _WIN32_WINNT setting in osdep.h according to the value from glib -glib_req_ver = '>=2.56.0' +glib_req_ver = '>=2.66.0' glib_pc = dependency('glib-2.0', version: glib_req_ver, required: true, method: 'pkg-config') glib_cflags = [] @@ -910,20 +910,6 @@ if not cc.compiles(''' to the right pkg-config files for your build target.''') endif -# Silence clang warnings triggered by glib < 2.57.2 -if not cc.compiles(''' - #include - typedef struct Foo { - int i; - } Foo; - static void foo_free(Foo *f) - { - g_free(f); - } - G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free) - int main(void) { return 0; }''', dependencies: glib_pc, args: ['-Wunused-function', '-Werror']) - glib_cflags += cc.get_supported_arguments('-Wno-unused-function') -endif glib = declare_dependency(dependencies: [glib_pc, gmodule], compile_args: glib_cflags, version: glib_pc.version()) diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index dd2ecb453a..866afe6741 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -36,7 +36,7 @@ test_get_passwd_entry(const gchar *user_name, GError **error) return p; } -#define g_unix_get_passwd_entry_qemu(username, err) \ +#define g_unix_get_passwd_entry(username, err) \ test_get_passwd_entry(username, err) #endif @@ -46,7 +46,7 @@ get_passwd_entry(const char *username, Error **errp) g_autoptr(GError) err = NULL; struct passwd *p; - p = g_unix_get_passwd_entry_qemu(username, &err); + p = g_unix_get_passwd_entry(username, &err); if (p == NULL) { error_setg(errp, "failed to lookup user '%s': %s", username, err->message); From 82fe5d08fd8b83173e506d266e614d0dca5f2088 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:51 +0200 Subject: [PATCH 0613/2884] Remove glib compatibility code that is not required anymore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we bumped the minimum glib version to 2.66, we can drop the old code. Suggested-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Message-ID: <20240418101056.302103-9-thuth@redhat.com> Signed-off-by: Thomas Huth --- qga/commands-posix-ssh.c | 8 -------- util/error-report.c | 10 ---------- 2 files changed, 18 deletions(-) diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index 866afe6741..246171d323 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -243,7 +243,6 @@ qmp_guest_ssh_get_authorized_keys(const char *username, Error **errp) } #ifdef QGA_BUILD_UNIT_TEST -#if GLIB_CHECK_VERSION(2, 60, 0) static const strList test_key2 = { .value = (char *)"algo key2 comments" }; @@ -439,11 +438,4 @@ int main(int argc, char *argv[]) return g_test_run(); } -#else -int main(int argc, char *argv[]) -{ - g_test_message("test skipped, needs glib >= 2.60"); - return 0; -} -#endif /* GLIB_2_60 */ #endif /* BUILD_UNIT_TEST */ diff --git a/util/error-report.c b/util/error-report.c index 6e44a55732..1b17c11de1 100644 --- a/util/error-report.c +++ b/util/error-report.c @@ -172,18 +172,8 @@ static void print_loc(void) static char * real_time_iso8601(void) { -#if GLIB_CHECK_VERSION(2,62,0) g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); - /* ignore deprecation warning, since GLIB_VERSION_MAX_ALLOWED is 2.56 */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" return g_date_time_format_iso8601(dt); -#pragma GCC diagnostic pop -#else - GTimeVal tv; - g_get_current_time(&tv); - return g_time_val_to_iso8601(&tv); -#endif } /* From 3413c6628b71c865da354590c3b05aa1f55a0729 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 4 Mar 2024 13:49:38 +0100 Subject: [PATCH 0614/2884] block/gluster: Use URI parsing code from glib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since version 2.66, glib has useful URI parsing functions, too. Use those instead of the QEMU-internal ones to be finally able to get rid of the latter. Since g_uri_get_path() returns a const pointer, we also need to tweak the parameter of parse_volume_options() (where we use the result of g_uri_get_path() as input). Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrangé Message-Id: <20240418101056.302103-10-thuth@redhat.com> Signed-off-by: Thomas Huth --- block/gluster.c | 69 ++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/block/gluster.c b/block/gluster.c index 4253c8db5e..d0999903df 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -17,7 +17,6 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" -#include "qemu/uri.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" @@ -289,9 +288,9 @@ static void glfs_clear_preopened(glfs_t *fs) } } -static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path) +static int parse_volume_options(BlockdevOptionsGluster *gconf, const char *path) { - char *p, *q; + const char *p, *q; if (!path) { return -EINVAL; @@ -349,13 +348,13 @@ static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path) static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf, const char *filename) { + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + g_autoptr(GHashTable) qp = NULL; SocketAddress *gsconf; - URI *uri; - QueryParams *qp = NULL; bool is_unix = false; - int ret = 0; + const char *uri_scheme, *uri_query, *uri_server; + int uri_port, ret; - uri = uri_parse(filename); if (!uri) { return -EINVAL; } @@ -364,54 +363,54 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf, QAPI_LIST_PREPEND(gconf->server, gsconf); /* transport */ - if (!uri->scheme || !strcmp(uri->scheme, "gluster")) { + uri_scheme = g_uri_get_scheme(uri); + if (!uri_scheme || !strcmp(uri_scheme, "gluster")) { gsconf->type = SOCKET_ADDRESS_TYPE_INET; - } else if (!strcmp(uri->scheme, "gluster+tcp")) { + } else if (!strcmp(uri_scheme, "gluster+tcp")) { gsconf->type = SOCKET_ADDRESS_TYPE_INET; - } else if (!strcmp(uri->scheme, "gluster+unix")) { + } else if (!strcmp(uri_scheme, "gluster+unix")) { gsconf->type = SOCKET_ADDRESS_TYPE_UNIX; is_unix = true; } else { - ret = -EINVAL; - goto out; + return -EINVAL; } - ret = parse_volume_options(gconf, uri->path); + ret = parse_volume_options(gconf, g_uri_get_path(uri)); if (ret < 0) { - goto out; + return ret; } - qp = query_params_parse(uri->query); - if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { - ret = -EINVAL; - goto out; + uri_query = g_uri_get_query(uri); + if (uri_query) { + qp = g_uri_parse_params(uri_query, -1, "&", G_URI_PARAMS_NONE, NULL); + if (!qp) { + return -EINVAL; + } + ret = g_hash_table_size(qp); + if (ret > 1 || (is_unix && !ret) || (!is_unix && ret)) { + return -EINVAL; + } } + uri_server = g_uri_get_host(uri); + uri_port = g_uri_get_port(uri); + if (is_unix) { - if (uri->server || uri->port) { - ret = -EINVAL; - goto out; + char *uri_socket = g_hash_table_lookup(qp, "socket"); + if (uri_server || uri_port != -1 || !uri_socket) { + return -EINVAL; } - if (strcmp(qp->p[0].name, "socket")) { - ret = -EINVAL; - goto out; - } - gsconf->u.q_unix.path = g_strdup(qp->p[0].value); + gsconf->u.q_unix.path = g_strdup(uri_socket); } else { - gsconf->u.inet.host = g_strdup(uri->server ? uri->server : "localhost"); - if (uri->port) { - gsconf->u.inet.port = g_strdup_printf("%d", uri->port); + gsconf->u.inet.host = g_strdup(uri_server ? uri_server : "localhost"); + if (uri_port > 0) { + gsconf->u.inet.port = g_strdup_printf("%d", uri_port); } else { gsconf->u.inet.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT); } } -out: - if (qp) { - query_params_free(qp); - } - uri_free(uri); - return ret; + return 0; } static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, From ad97c011dcb0e5b5e1eae50c0f8ddb89281056e4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:53 +0200 Subject: [PATCH 0615/2884] block/nbd: Use URI parsing code from glib Since version 2.66, glib has useful URI parsing functions, too. Use those instead of the QEMU-internal ones to be finally able to get rid of the latter. The g_uri_get_host() also takes care of removing the square brackets from IPv6 addresses, so we can drop that part of the QEMU code now, too. Reviewed-by: Richard W.M. Jones Reviewed-by: Eric Blake Message-ID: <20240418101056.302103-11-thuth@redhat.com> Signed-off-by: Thomas Huth --- block/nbd.c | 76 ++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index ef05f7cdfd..589d28af83 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -31,7 +31,6 @@ #include "qemu/osdep.h" #include "trace.h" -#include "qemu/uri.h" #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" @@ -1514,30 +1513,31 @@ static void nbd_client_close(BlockDriverState *bs) static int nbd_parse_uri(const char *filename, QDict *options) { - URI *uri; + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + g_autoptr(GHashTable) qp = NULL; const char *p; - QueryParams *qp = NULL; - int ret = 0; + int qp_n; bool is_unix; + const char *uri_scheme, *uri_query, *uri_server; + int uri_port; - uri = uri_parse(filename); if (!uri) { return -EINVAL; } /* transport */ - if (!g_strcmp0(uri->scheme, "nbd")) { + uri_scheme = g_uri_get_scheme(uri); + if (!g_strcmp0(uri_scheme, "nbd")) { is_unix = false; - } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) { + } else if (!g_strcmp0(uri_scheme, "nbd+tcp")) { is_unix = false; - } else if (!g_strcmp0(uri->scheme, "nbd+unix")) { + } else if (!g_strcmp0(uri_scheme, "nbd+unix")) { is_unix = true; } else { - ret = -EINVAL; - goto out; + return -EINVAL; } - p = uri->path ? uri->path : ""; + p = g_uri_get_path(uri) ?: ""; if (p[0] == '/') { p++; } @@ -1545,52 +1545,50 @@ static int nbd_parse_uri(const char *filename, QDict *options) qdict_put_str(options, "export", p); } - qp = query_params_parse(uri->query); - if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { - ret = -EINVAL; - goto out; + uri_query = g_uri_get_query(uri); + if (uri_query) { + qp = g_uri_parse_params(uri_query, -1, "&", G_URI_PARAMS_NONE, NULL); + if (!qp) { + return -EINVAL; + } + qp_n = g_hash_table_size(qp); + if (qp_n > 1 || (is_unix && !qp_n) || (!is_unix && qp_n)) { + return -EINVAL; + } + } + + uri_server = g_uri_get_host(uri); + if (uri_server && !uri_server[0]) { + uri_server = NULL; } + uri_port = g_uri_get_port(uri); if (is_unix) { /* nbd+unix:///export?socket=path */ - if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { - ret = -EINVAL; - goto out; + const char *uri_socket = g_hash_table_lookup(qp, "socket"); + if (uri_server || uri_port != -1 || !uri_socket) { + return -EINVAL; } qdict_put_str(options, "server.type", "unix"); - qdict_put_str(options, "server.path", qp->p[0].value); + qdict_put_str(options, "server.path", uri_socket); } else { - QString *host; char *port_str; /* nbd[+tcp]://host[:port]/export */ - if (!uri->server) { - ret = -EINVAL; - goto out; - } - - /* strip braces from literal IPv6 address */ - if (uri->server[0] == '[') { - host = qstring_from_substr(uri->server, 1, - strlen(uri->server) - 1); - } else { - host = qstring_from_str(uri->server); + if (!uri_server) { + return -EINVAL; } qdict_put_str(options, "server.type", "inet"); - qdict_put(options, "server.host", host); + qdict_put_str(options, "server.host", uri_server); - port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); + port_str = g_strdup_printf("%d", uri_port > 0 ? uri_port + : NBD_DEFAULT_PORT); qdict_put_str(options, "server.port", port_str); g_free(port_str); } -out: - if (qp) { - query_params_free(qp); - } - uri_free(uri); - return ret; + return 0; } static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) From f8b74fc55dcceacf793bd72afb1c6432b33655ca Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:54 +0200 Subject: [PATCH 0616/2884] block/nfs: Use URI parsing code from glib Since version 2.66, glib has useful URI parsing functions, too. Use those instead of the QEMU-internal ones to be finally able to get rid of the latter. While we're at it, slightly rephrase one of the error messages: Use "Invalid value..." instead of "Illegal value..." since the latter rather sounds like the users were breaking a law here. Reviewed-by: Eric Blake Message-ID: <20240418101056.302103-12-thuth@redhat.com> Signed-off-by: Thomas Huth --- block/nfs.c | 108 ++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/block/nfs.c b/block/nfs.c index f737e19cd3..60240a8733 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -38,7 +38,6 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" -#include "qemu/uri.h" #include "qemu/cutils.h" #include "sysemu/replay.h" #include "qapi/qapi-visit-block-core.h" @@ -79,77 +78,76 @@ typedef struct NFSRPC { static int nfs_parse_uri(const char *filename, QDict *options, Error **errp) { - URI *uri = NULL; - QueryParams *qp = NULL; - int ret = -EINVAL, i; + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + GUriParamsIter qp; + const char *uri_server, *uri_path, *uri_query; + char *qp_name, *qp_value; + GError *gerror = NULL; - uri = uri_parse(filename); if (!uri) { error_setg(errp, "Invalid URI specified"); - goto out; + return -EINVAL; } - if (g_strcmp0(uri->scheme, "nfs") != 0) { + if (!g_str_equal(g_uri_get_scheme(uri), "nfs")) { error_setg(errp, "URI scheme must be 'nfs'"); - goto out; + return -EINVAL; } - if (!uri->server) { + uri_server = g_uri_get_host(uri); + if (!uri_server || !uri_server[0]) { error_setg(errp, "missing hostname in URI"); - goto out; + return -EINVAL; } - if (!uri->path) { + uri_path = g_uri_get_path(uri); + if (!uri_path || !uri_path[0]) { error_setg(errp, "missing file path in URI"); - goto out; + return -EINVAL; } - qp = query_params_parse(uri->query); - if (!qp) { - error_setg(errp, "could not parse query parameters"); - goto out; - } - - qdict_put_str(options, "server.host", uri->server); + qdict_put_str(options, "server.host", uri_server); qdict_put_str(options, "server.type", "inet"); - qdict_put_str(options, "path", uri->path); + qdict_put_str(options, "path", uri_path); - for (i = 0; i < qp->n; i++) { - uint64_t val; - if (!qp->p[i].value) { - error_setg(errp, "Value for NFS parameter expected: %s", - qp->p[i].name); - goto out; - } - if (parse_uint_full(qp->p[i].value, 0, &val)) { - error_setg(errp, "Illegal value for NFS parameter: %s", - qp->p[i].name); - goto out; - } - if (!strcmp(qp->p[i].name, "uid")) { - qdict_put_str(options, "user", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "gid")) { - qdict_put_str(options, "group", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) { - qdict_put_str(options, "tcp-syn-count", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "readahead")) { - qdict_put_str(options, "readahead-size", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "pagecache")) { - qdict_put_str(options, "page-cache-size", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "debug")) { - qdict_put_str(options, "debug", qp->p[i].value); - } else { - error_setg(errp, "Unknown NFS parameter name: %s", - qp->p[i].name); - goto out; + uri_query = g_uri_get_query(uri); + if (uri_query) { + g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE); + while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) { + uint64_t val; + if (!qp_name || gerror) { + error_setg(errp, "Failed to parse NFS parameter"); + return -EINVAL; + } + if (!qp_value) { + error_setg(errp, "Value for NFS parameter expected: %s", + qp_name); + return -EINVAL; + } + if (parse_uint_full(qp_value, 0, &val)) { + error_setg(errp, "Invalid value for NFS parameter: %s", + qp_name); + return -EINVAL; + } + if (g_str_equal(qp_name, "uid")) { + qdict_put_str(options, "user", qp_value); + } else if (g_str_equal(qp_name, "gid")) { + qdict_put_str(options, "group", qp_value); + } else if (g_str_equal(qp_name, "tcp-syncnt")) { + qdict_put_str(options, "tcp-syn-count", qp_value); + } else if (g_str_equal(qp_name, "readahead")) { + qdict_put_str(options, "readahead-size", qp_value); + } else if (g_str_equal(qp_name, "pagecache")) { + qdict_put_str(options, "page-cache-size", qp_value); + } else if (g_str_equal(qp_name, "debug")) { + qdict_put_str(options, "debug", qp_value); + } else { + error_setg(errp, "Unknown NFS parameter name: %s", qp_name); + return -EINVAL; + } } } - ret = 0; -out: - if (qp) { - query_params_free(qp); - } - uri_free(uri); - return ret; + + return 0; } static bool nfs_has_filename_options_conflict(QDict *options, Error **errp) From a22a97d8056841f1d6d8e52d39281c8c01f4117b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:55 +0200 Subject: [PATCH 0617/2884] block/ssh: Use URI parsing code from glib Since version 2.66, glib has useful URI parsing functions, too. Use those instead of the QEMU-internal ones to be finally able to get rid of the latter. While we're at it, also emit a warning when encountering unknown parameters in the URI, so that the users have a chance to detect their typos or other mistakes. Reviewed-by: Richard W.M. Jones Message-ID: <20240418101056.302103-13-thuth@redhat.com> Signed-off-by: Thomas Huth --- block/ssh.c | 73 ++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/block/ssh.c b/block/ssh.c index 2748253d4a..a88171d4b5 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -37,7 +37,6 @@ #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/sockets.h" -#include "qemu/uri.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" @@ -181,65 +180,71 @@ static void sftp_error_trace(BDRVSSHState *s, const char *op) static int parse_uri(const char *filename, QDict *options, Error **errp) { - URI *uri = NULL; - QueryParams *qp; + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + const char *uri_host, *uri_path, *uri_user, *uri_query; char *port_str; - int i; + int port; + g_autoptr(GError) gerror = NULL; + char *qp_name, *qp_value; + GUriParamsIter qp; - uri = uri_parse(filename); if (!uri) { return -EINVAL; } - if (g_strcmp0(uri->scheme, "ssh") != 0) { + if (g_strcmp0(g_uri_get_scheme(uri), "ssh") != 0) { error_setg(errp, "URI scheme must be 'ssh'"); - goto err; + return -EINVAL; } - if (!uri->server || strcmp(uri->server, "") == 0) { + uri_host = g_uri_get_host(uri); + if (!uri_host || g_str_equal(uri_host, "")) { error_setg(errp, "missing hostname in URI"); - goto err; + return -EINVAL; } - if (!uri->path || strcmp(uri->path, "") == 0) { + uri_path = g_uri_get_path(uri); + if (!uri_path || g_str_equal(uri_path, "")) { error_setg(errp, "missing remote path in URI"); - goto err; + return -EINVAL; } - qp = query_params_parse(uri->query); - if (!qp) { - error_setg(errp, "could not parse query parameters"); - goto err; + uri_user = g_uri_get_user(uri); + if (uri_user && !g_str_equal(uri_user, "")) { + qdict_put_str(options, "user", uri_user); } - if(uri->user && strcmp(uri->user, "") != 0) { - qdict_put_str(options, "user", uri->user); - } + qdict_put_str(options, "server.host", uri_host); - qdict_put_str(options, "server.host", uri->server); - - port_str = g_strdup_printf("%d", uri->port ?: 22); + port = g_uri_get_port(uri); + port_str = g_strdup_printf("%d", port > 0 ? port : 22); qdict_put_str(options, "server.port", port_str); g_free(port_str); - qdict_put_str(options, "path", uri->path); + qdict_put_str(options, "path", uri_path); - /* Pick out any query parameters that we understand, and ignore - * the rest. - */ - for (i = 0; i < qp->n; ++i) { - if (strcmp(qp->p[i].name, "host_key_check") == 0) { - qdict_put_str(options, "host_key_check", qp->p[i].value); + uri_query = g_uri_get_query(uri); + if (uri_query) { + g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE); + while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) { + if (!qp_name || !qp_value || gerror) { + warn_report("Failed to parse SSH URI parameters '%s'", + uri_query); + break; + } + /* + * Pick out the query parameters that we understand, and ignore + * (or rather warn about) the rest. + */ + if (g_str_equal(qp_name, "host_key_check")) { + qdict_put_str(options, "host_key_check", qp_value); + } else { + warn_report("Unsupported parameter '%s' in URI", qp_name); + } } } - query_params_free(qp); - uri_free(uri); return 0; - - err: - uri_free(uri); - return -EINVAL; } static bool ssh_has_filename_options_conflict(QDict *options, Error **errp) From da79537e0c8cef007d30298343d05acb0ba8b427 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 18 Apr 2024 12:10:56 +0200 Subject: [PATCH 0618/2884] util/uri: Remove the old URI parsing code Now that we switched all consumers of the URI code to use the URI parsing functions from glib instead, we can remove our internal URI parsing code since it is not used anymore. Reviewed-by: Eric Blake Message-ID: <20240418101056.302103-14-thuth@redhat.com> Signed-off-by: Thomas Huth --- include/qemu/uri.h | 99 --- util/meson.build | 2 +- util/uri.c | 1466 -------------------------------------------- 3 files changed, 1 insertion(+), 1566 deletions(-) delete mode 100644 include/qemu/uri.h delete mode 100644 util/uri.c diff --git a/include/qemu/uri.h b/include/qemu/uri.h deleted file mode 100644 index 255e61f452..0000000000 --- a/include/qemu/uri.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Summary: library of generic URI related routines - * Description: library of generic URI related routines - * Implements RFC 2396 - * - * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Daniel Veillard shall not - * be used in advertising or otherwise to promote the sale, use or other - * dealings in this Software without prior written authorization from him. - * - * Author: Daniel Veillard - ** - * Copyright (C) 2007 Red Hat, Inc. - * - * 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.1 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 . - * - * Authors: - * Richard W.M. Jones - * - * Utility functions to help parse and assemble query strings. - */ - -#ifndef QEMU_URI_H -#define QEMU_URI_H - -/** - * URI: - * - * A parsed URI reference. This is a struct containing the various fields - * as described in RFC 2396 but separated for further processing. - */ -typedef struct URI { - char *scheme; /* the URI scheme */ - char *opaque; /* opaque part */ - char *authority; /* the authority part */ - char *server; /* the server part */ - char *user; /* the user part */ - int port; /* the port number */ - char *path; /* the path string */ - char *fragment; /* the fragment identifier */ - int cleanup; /* parsing potentially unclean URI */ - char *query; /* the query string (as it appears in the URI) */ -} URI; - -URI *uri_new(void); -URI *uri_parse(const char *str); -URI *uri_parse_raw(const char *str, int raw); -int uri_parse_into(URI *uri, const char *str); -char *uri_to_string(URI *uri); -void uri_free(URI *uri); - -/* Single web service query parameter 'name=value'. */ -typedef struct QueryParam { - char *name; /* Name (unescaped). */ - char *value; /* Value (unescaped). */ - int ignore; /* Ignore this field in qparam_get_query */ -} QueryParam; - -/* Set of parameters. */ -typedef struct QueryParams { - int n; /* number of parameters used */ - int alloc; /* allocated space */ - QueryParam *p; /* array of parameters */ -} QueryParams; - -QueryParams *query_params_new(int init_alloc); -QueryParams *query_params_parse(const char *query); -void query_params_free(QueryParams *ps); - -#endif /* QEMU_URI_H */ diff --git a/util/meson.build b/util/meson.build index 2ad57b10ba..72b505df11 100644 --- a/util/meson.build +++ b/util/meson.build @@ -93,7 +93,7 @@ if have_block util_ss.add(files('hbitmap.c')) util_ss.add(files('hexdump.c')) util_ss.add(files('iova-tree.c')) - util_ss.add(files('iov.c', 'uri.c')) + util_ss.add(files('iov.c')) util_ss.add(files('nvdimm-utils.c')) util_ss.add(files('block-helpers.c')) util_ss.add(files('qemu-coroutine-sleep.c')) diff --git a/util/uri.c b/util/uri.c deleted file mode 100644 index 573174bf47..0000000000 --- a/util/uri.c +++ /dev/null @@ -1,1466 +0,0 @@ -/** - * uri.c: set of generic URI related routines - * - * Reference: RFCs 3986, 2732 and 2373 - * - * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Daniel Veillard shall not - * be used in advertising or otherwise to promote the sale, use or other - * dealings in this Software without prior written authorization from him. - * - * daniel@veillard.com - * - ** - * - * Copyright (C) 2007, 2009-2010 Red Hat, Inc. - * - * 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.1 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 . - * - * Authors: - * Richard W.M. Jones - * - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" - -#include "qemu/uri.h" - -static void uri_clean(URI *uri); - -/* - * Old rule from 2396 used in legacy handling code - * alpha = lowalpha | upalpha - */ -#define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x)) - -/* - * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | - * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | - * "u" | "v" | "w" | "x" | "y" | "z" - */ - -#define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z')) - -/* - * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | - * "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | - * "U" | "V" | "W" | "X" | "Y" | "Z" - */ -#define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z')) - -#ifdef IS_DIGIT -#undef IS_DIGIT -#endif -/* - * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" - */ -#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9')) - -/* - * alphanum = alpha | digit - */ - -#define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) - -/* - * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" - */ - -#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ - ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ - ((x) == '(') || ((x) == ')')) - -/* - * unwise = "{" | "}" | "|" | "\" | "^" | "`" - */ - -#define IS_UNWISE(p) \ - (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ - ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ - ((*(p) == ']')) || ((*(p) == '`'))) -/* - * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | - * "[" | "]" - */ - -#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ - ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ - ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ - ((x) == ']')) - -/* - * unreserved = alphanum | mark - */ - -#define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) - -/* - * Skip to next pointer char, handle escaped sequences - */ - -#define NEXT(p) ((*p == '%') ? p += 3 : p++) - -/* - * Productions from the spec. - * - * authority = server | reg_name - * reg_name = 1*( unreserved | escaped | "$" | "," | - * ";" | ":" | "@" | "&" | "=" | "+" ) - * - * path = [ abs_path | opaque_part ] - */ - -/************************************************************************ - * * - * RFC 3986 parser * - * * - ************************************************************************/ - -#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9')) -#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \ - ((*(p) >= 'A') && (*(p) <= 'Z'))) -#define ISA_HEXDIG(p) \ - (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \ - ((*(p) >= 'A') && (*(p) <= 'F'))) - -/* - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -#define ISA_SUB_DELIM(p) \ - (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \ - ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \ - ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \ - ((*(p) == '=')) || ((*(p) == '\''))) - -/* - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - */ -#define ISA_UNRESERVED(p) \ - ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ - ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) - -/* - * pct-encoded = "%" HEXDIG HEXDIG - */ -#define ISA_PCT_ENCODED(p) \ - ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2))) - -/* - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - */ -#define ISA_PCHAR(p) \ - (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ - ((*(p) == ':')) || ((*(p) == '@'))) - -/** - * rfc3986_parse_scheme: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI scheme - * - * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_scheme(URI *uri, const char **str) -{ - const char *cur; - - if (str == NULL) { - return -1; - } - - cur = *str; - if (!ISA_ALPHA(cur)) { - return 2; - } - cur++; - while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') || - (*cur == '.')) { - cur++; - } - if (uri != NULL) { - g_free(uri->scheme); - uri->scheme = g_strndup(*str, cur - *str); - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_fragment: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse the query part of an URI - * - * fragment = *( pchar / "/" / "?" ) - * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']' - * in the fragment identifier but this is used very broadly for - * xpointer scheme selection, so we are allowing it here to not break - * for example all the DocBook processing chains. - * - * Returns 0 or the error code - */ -static int rfc3986_parse_fragment(URI *uri, const char **str) -{ - const char *cur; - - if (str == NULL) { - return -1; - } - - cur = *str; - - while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || - (*cur == '[') || (*cur == ']') || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { - NEXT(cur); - } - if (uri != NULL) { - g_free(uri->fragment); - if (uri->cleanup & 2) { - uri->fragment = g_strndup(*str, cur - *str); - } else { - uri->fragment = g_uri_unescape_segment(*str, cur, NULL); - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_query: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse the query part of an URI - * - * query = *uric - * - * Returns 0 or the error code - */ -static int rfc3986_parse_query(URI *uri, const char **str) -{ - const char *cur; - - if (str == NULL) { - return -1; - } - - cur = *str; - - while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { - NEXT(cur); - } - if (uri != NULL) { - g_free(uri->query); - uri->query = g_strndup(*str, cur - *str); - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_port: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse a port part and fills in the appropriate fields - * of the @uri structure - * - * port = *DIGIT - * - * Returns 0 or the error code - */ -static int rfc3986_parse_port(URI *uri, const char **str) -{ - const char *cur = *str; - int port = 0; - - if (ISA_DIGIT(cur)) { - while (ISA_DIGIT(cur)) { - port = port * 10 + (*cur - '0'); - if (port > 65535) { - return 1; - } - cur++; - } - if (uri) { - uri->port = port; - } - *str = cur; - return 0; - } - return 1; -} - -/** - * rfc3986_parse_user_info: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse a user information part and fill in the appropriate fields - * of the @uri structure - * - * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_user_info(URI *uri, const char **str) -{ - const char *cur; - - cur = *str; - while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur) || - (*cur == ':')) { - NEXT(cur); - } - if (*cur == '@') { - if (uri != NULL) { - g_free(uri->user); - if (uri->cleanup & 2) { - uri->user = g_strndup(*str, cur - *str); - } else { - uri->user = g_uri_unescape_segment(*str, cur, NULL); - } - } - *str = cur; - return 0; - } - return 1; -} - -/** - * rfc3986_parse_dec_octet: - * @str: the string to analyze - * - * dec-octet = DIGIT ; 0-9 - * / %x31-39 DIGIT ; 10-99 - * / "1" 2DIGIT ; 100-199 - * / "2" %x30-34 DIGIT ; 200-249 - * / "25" %x30-35 ; 250-255 - * - * Skip a dec-octet. - * - * Returns 0 if found and skipped, 1 otherwise - */ -static int rfc3986_parse_dec_octet(const char **str) -{ - const char *cur = *str; - - if (!(ISA_DIGIT(cur))) { - return 1; - } - if (!ISA_DIGIT(cur + 1)) { - cur++; - } else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur + 2))) { - cur += 2; - } else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) { - cur += 3; - } else if ((*cur == '2') && (*(cur + 1) >= '0') && (*(cur + 1) <= '4') && - (ISA_DIGIT(cur + 2))) { - cur += 3; - } else if ((*cur == '2') && (*(cur + 1) == '5') && (*(cur + 2) >= '0') && - (*(cur + 1) <= '5')) { - cur += 3; - } else { - return 1; - } - *str = cur; - return 0; -} -/** - * rfc3986_parse_host: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an host part and fills in the appropriate fields - * of the @uri structure - * - * host = IP-literal / IPv4address / reg-name - * IP-literal = "[" ( IPv6address / IPvFuture ) "]" - * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet - * reg-name = *( unreserved / pct-encoded / sub-delims ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_host(URI *uri, const char **str) -{ - const char *cur = *str; - const char *host; - - host = cur; - /* - * IPv6 and future addressing scheme are enclosed between brackets - */ - if (*cur == '[') { - cur++; - while ((*cur != ']') && (*cur != 0)) { - cur++; - } - if (*cur != ']') { - return 1; - } - cur++; - goto found; - } - /* - * try to parse an IPv4 - */ - if (ISA_DIGIT(cur)) { - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - if (*cur != '.') { - goto not_ipv4; - } - cur++; - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - if (*cur != '.') { - goto not_ipv4; - } - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - if (*cur != '.') { - goto not_ipv4; - } - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - goto found; - not_ipv4: - cur = *str; - } - /* - * then this should be a hostname which can be empty - */ - while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) { - NEXT(cur); - } -found: - if (uri != NULL) { - g_free(uri->authority); - uri->authority = NULL; - g_free(uri->server); - if (cur != host) { - if (uri->cleanup & 2) { - uri->server = g_strndup(host, cur - host); - } else { - uri->server = g_uri_unescape_segment(host, cur, NULL); - } - } else { - uri->server = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_authority: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an authority part and fills in the appropriate fields - * of the @uri structure - * - * authority = [ userinfo "@" ] host [ ":" port ] - * - * Returns 0 or the error code - */ -static int rfc3986_parse_authority(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - /* - * try to parse a userinfo and check for the trailing @ - */ - ret = rfc3986_parse_user_info(uri, &cur); - if ((ret != 0) || (*cur != '@')) { - cur = *str; - } else { - cur++; - } - ret = rfc3986_parse_host(uri, &cur); - if (ret != 0) { - return ret; - } - if (*cur == ':') { - cur++; - ret = rfc3986_parse_port(uri, &cur); - if (ret != 0) { - return ret; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_segment: - * @str: the string to analyze - * @forbid: an optional forbidden character - * @empty: allow an empty segment - * - * Parse a segment and fills in the appropriate fields - * of the @uri structure - * - * segment = *pchar - * segment-nz = 1*pchar - * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) - * ; non-zero-length segment without any colon ":" - * - * Returns 0 or the error code - */ -static int rfc3986_parse_segment(const char **str, char forbid, int empty) -{ - const char *cur; - - cur = *str; - if (!ISA_PCHAR(cur)) { - if (empty) { - return 0; - } - return 1; - } - while (ISA_PCHAR(cur) && (*cur != forbid)) { - NEXT(cur); - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_ab_empty: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path absolute or empty and fills in the appropriate fields - * of the @uri structure - * - * path-abempty = *( "/" segment ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_ab_empty(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - if (uri != NULL) { - g_free(uri->path); - if (*str != cur) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = g_uri_unescape_segment(*str, cur, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_absolute: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path absolute and fills in the appropriate fields - * of the @uri structure - * - * path-absolute = "/" [ segment-nz *( "/" segment ) ] - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_absolute(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - if (*cur != '/') { - return 1; - } - cur++; - ret = rfc3986_parse_segment(&cur, 0, 0); - if (ret == 0) { - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - } - if (uri != NULL) { - g_free(uri->path); - if (cur != *str) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = g_uri_unescape_segment(*str, cur, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_rootless: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path without root and fills in the appropriate fields - * of the @uri structure - * - * path-rootless = segment-nz *( "/" segment ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_rootless(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - ret = rfc3986_parse_segment(&cur, 0, 0); - if (ret != 0) { - return ret; - } - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - if (uri != NULL) { - g_free(uri->path); - if (cur != *str) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = g_uri_unescape_segment(*str, cur, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_no_scheme: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path which is not a scheme and fills in the appropriate fields - * of the @uri structure - * - * path-noscheme = segment-nz-nc *( "/" segment ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_no_scheme(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - ret = rfc3986_parse_segment(&cur, ':', 0); - if (ret != 0) { - return ret; - } - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - if (uri != NULL) { - g_free(uri->path); - if (cur != *str) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = g_uri_unescape_segment(*str, cur, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_hier_part: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an hierarchical part and fills in the appropriate fields - * of the @uri structure - * - * hier-part = "//" authority path-abempty - * / path-absolute - * / path-rootless - * / path-empty - * - * Returns 0 or the error code - */ -static int rfc3986_parse_hier_part(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - if ((*cur == '/') && (*(cur + 1) == '/')) { - cur += 2; - ret = rfc3986_parse_authority(uri, &cur); - if (ret != 0) { - return ret; - } - ret = rfc3986_parse_path_ab_empty(uri, &cur); - if (ret != 0) { - return ret; - } - *str = cur; - return 0; - } else if (*cur == '/') { - ret = rfc3986_parse_path_absolute(uri, &cur); - if (ret != 0) { - return ret; - } - } else if (ISA_PCHAR(cur)) { - ret = rfc3986_parse_path_rootless(uri, &cur); - if (ret != 0) { - return ret; - } - } else { - /* path-empty is effectively empty */ - if (uri != NULL) { - g_free(uri->path); - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_relative_ref: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI string and fills in the appropriate fields - * of the @uri structure - * - * relative-ref = relative-part [ "?" query ] [ "#" fragment ] - * relative-part = "//" authority path-abempty - * / path-absolute - * / path-noscheme - * / path-empty - * - * Returns 0 or the error code - */ -static int rfc3986_parse_relative_ref(URI *uri, const char *str) -{ - int ret; - - if ((*str == '/') && (*(str + 1) == '/')) { - str += 2; - ret = rfc3986_parse_authority(uri, &str); - if (ret != 0) { - return ret; - } - ret = rfc3986_parse_path_ab_empty(uri, &str); - if (ret != 0) { - return ret; - } - } else if (*str == '/') { - ret = rfc3986_parse_path_absolute(uri, &str); - if (ret != 0) { - return ret; - } - } else if (ISA_PCHAR(str)) { - ret = rfc3986_parse_path_no_scheme(uri, &str); - if (ret != 0) { - return ret; - } - } else { - /* path-empty is effectively empty */ - if (uri != NULL) { - g_free(uri->path); - uri->path = NULL; - } - } - - if (*str == '?') { - str++; - ret = rfc3986_parse_query(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str == '#') { - str++; - ret = rfc3986_parse_fragment(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str != 0) { - uri_clean(uri); - return 1; - } - return 0; -} - -/** - * rfc3986_parse: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI string and fills in the appropriate fields - * of the @uri structure - * - * scheme ":" hier-part [ "?" query ] [ "#" fragment ] - * - * Returns 0 or the error code - */ -static int rfc3986_parse(URI *uri, const char *str) -{ - int ret; - - ret = rfc3986_parse_scheme(uri, &str); - if (ret != 0) { - return ret; - } - if (*str != ':') { - return 1; - } - str++; - ret = rfc3986_parse_hier_part(uri, &str); - if (ret != 0) { - return ret; - } - if (*str == '?') { - str++; - ret = rfc3986_parse_query(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str == '#') { - str++; - ret = rfc3986_parse_fragment(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str != 0) { - uri_clean(uri); - return 1; - } - return 0; -} - -/** - * rfc3986_parse_uri_reference: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI reference string and fills in the appropriate fields - * of the @uri structure - * - * URI-reference = URI / relative-ref - * - * Returns 0 or the error code - */ -static int rfc3986_parse_uri_reference(URI *uri, const char *str) -{ - int ret; - - if (str == NULL) { - return -1; - } - uri_clean(uri); - - /* - * Try first to parse absolute refs, then fallback to relative if - * it fails. - */ - ret = rfc3986_parse(uri, str); - if (ret != 0) { - uri_clean(uri); - ret = rfc3986_parse_relative_ref(uri, str); - if (ret != 0) { - uri_clean(uri); - return ret; - } - } - return 0; -} - -/** - * uri_parse: - * @str: the URI string to analyze - * - * Parse an URI based on RFC 3986 - * - * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] - * - * Returns a newly built URI or NULL in case of error - */ -URI *uri_parse(const char *str) -{ - URI *uri; - int ret; - - if (str == NULL) { - return NULL; - } - uri = uri_new(); - ret = rfc3986_parse_uri_reference(uri, str); - if (ret) { - uri_free(uri); - return NULL; - } - return uri; -} - -/** - * uri_parse_into: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI reference string based on RFC 3986 and fills in the - * appropriate fields of the @uri structure - * - * URI-reference = URI / relative-ref - * - * Returns 0 or the error code - */ -int uri_parse_into(URI *uri, const char *str) -{ - return rfc3986_parse_uri_reference(uri, str); -} - -/** - * uri_parse_raw: - * @str: the URI string to analyze - * @raw: if 1 unescaping of URI pieces are disabled - * - * Parse an URI but allows to keep intact the original fragments. - * - * URI-reference = URI / relative-ref - * - * Returns a newly built URI or NULL in case of error - */ -URI *uri_parse_raw(const char *str, int raw) -{ - URI *uri; - int ret; - - if (str == NULL) { - return NULL; - } - uri = uri_new(); - if (raw) { - uri->cleanup |= 2; - } - ret = uri_parse_into(uri, str); - if (ret) { - uri_free(uri); - return NULL; - } - return uri; -} - -/************************************************************************ - * * - * Generic URI structure functions * - * * - ************************************************************************/ - -/** - * uri_new: - * - * Simply creates an empty URI - * - * Returns the new structure or NULL in case of error - */ -URI *uri_new(void) -{ - return g_new0(URI, 1); -} - -/** - * realloc2n: - * - * Function to handle properly a reallocation when saving an URI - * Also imposes some limit on the length of an URI string output - */ -static char *realloc2n(char *ret, int *max) -{ - char *temp; - int tmp; - - tmp = *max * 2; - temp = g_realloc(ret, (tmp + 1)); - *max = tmp; - return temp; -} - -/** - * uri_to_string: - * @uri: pointer to an URI - * - * Save the URI as an escaped string - * - * Returns a new string (to be deallocated by caller) - */ -char *uri_to_string(URI *uri) -{ - char *ret = NULL; - char *temp; - const char *p; - int len; - int max; - - if (uri == NULL) { - return NULL; - } - - max = 80; - ret = g_malloc(max + 1); - len = 0; - - if (uri->scheme != NULL) { - p = uri->scheme; - while (*p != 0) { - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - } - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = ':'; - } - if (uri->opaque != NULL) { - p = uri->opaque; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } else { - if (uri->server != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '/'; - ret[len++] = '/'; - if (uri->user != NULL) { - p = uri->user; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || ((*(p) == ';')) || - ((*(p) == ':')) || ((*(p) == '&')) || ((*(p) == '=')) || - ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ','))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '@'; - } - p = uri->server; - while (*p != 0) { - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - } - if (uri->port > 0) { - if (len + 10 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - len += snprintf(&ret[len], max - len, ":%d", uri->port); - } - } else if (uri->authority != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '/'; - ret[len++] = '/'; - p = uri->authority; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || ((*(p) == '$')) || - ((*(p) == ',')) || ((*(p) == ';')) || ((*(p) == ':')) || - ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) || - ((*(p) == '+'))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } else if (uri->scheme != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '/'; - ret[len++] = '/'; - } - if (uri->path != NULL) { - p = uri->path; - /* - * the colon in file:///d: should not be escaped or - * Windows accesses fail later. - */ - if ((uri->scheme != NULL) && (p[0] == '/') && - (((p[1] >= 'a') && (p[1] <= 'z')) || - ((p[1] >= 'A') && (p[1] <= 'Z'))) && - (p[2] == ':') && (!strcmp(uri->scheme, "file"))) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - ret[len++] = *p++; - ret[len++] = *p++; - } - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || - ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || - ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || - ((*(p) == ','))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } - if (uri->query != NULL) { - if (len + 1 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '?'; - p = uri->query; - while (*p != 0) { - if (len + 1 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - } - } - } - if (uri->fragment != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '#'; - p = uri->fragment; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len] = 0; - return ret; -} - -/** - * uri_clean: - * @uri: pointer to an URI - * - * Make sure the URI struct is free of content - */ -static void uri_clean(URI *uri) -{ - if (uri == NULL) { - return; - } - - g_free(uri->scheme); - uri->scheme = NULL; - g_free(uri->server); - uri->server = NULL; - g_free(uri->user); - uri->user = NULL; - g_free(uri->path); - uri->path = NULL; - g_free(uri->fragment); - uri->fragment = NULL; - g_free(uri->opaque); - uri->opaque = NULL; - g_free(uri->authority); - uri->authority = NULL; - g_free(uri->query); - uri->query = NULL; -} - -/** - * uri_free: - * @uri: pointer to an URI, NULL is ignored - * - * Free up the URI struct - */ -void uri_free(URI *uri) -{ - uri_clean(uri); - g_free(uri); -} - -/************************************************************************ - * * - * Public functions * - * * - ************************************************************************/ - -/* - * Utility functions to help parse and assemble query strings. - */ - -struct QueryParams *query_params_new(int init_alloc) -{ - struct QueryParams *ps; - - if (init_alloc <= 0) { - init_alloc = 1; - } - - ps = g_new(QueryParams, 1); - ps->n = 0; - ps->alloc = init_alloc; - ps->p = g_new(QueryParam, ps->alloc); - - return ps; -} - -/* Ensure there is space to store at least one more parameter - * at the end of the set. - */ -static int query_params_append(struct QueryParams *ps, const char *name, - const char *value) -{ - if (ps->n >= ps->alloc) { - ps->p = g_renew(QueryParam, ps->p, ps->alloc * 2); - ps->alloc *= 2; - } - - ps->p[ps->n].name = g_strdup(name); - ps->p[ps->n].value = g_strdup(value); - ps->p[ps->n].ignore = 0; - ps->n++; - - return 0; -} - -void query_params_free(struct QueryParams *ps) -{ - int i; - - for (i = 0; i < ps->n; ++i) { - g_free(ps->p[i].name); - g_free(ps->p[i].value); - } - g_free(ps->p); - g_free(ps); -} - -struct QueryParams *query_params_parse(const char *query) -{ - struct QueryParams *ps; - const char *end, *eq; - - ps = query_params_new(0); - if (!query || query[0] == '\0') { - return ps; - } - - while (*query) { - char *name = NULL, *value = NULL; - - /* Find the next separator, or end of the string. */ - end = strchr(query, '&'); - if (!end) { - end = qemu_strchrnul(query, ';'); - } - - /* Find the first '=' character between here and end. */ - eq = strchr(query, '='); - if (eq && eq >= end) { - eq = NULL; - } - - /* Empty section (eg. "&&"). */ - if (end == query) { - goto next; - } - - /* If there is no '=' character, then we have just "name" - * and consistent with CGI.pm we assume value is "". - */ - else if (!eq) { - name = g_uri_unescape_segment(query, end, NULL); - value = NULL; - } - /* Or if we have "name=" here (works around annoying - * problem when calling uri_string_unescape with len = 0). - */ - else if (eq + 1 == end) { - name = g_uri_unescape_segment(query, eq, NULL); - value = g_new0(char, 1); - } - /* If the '=' character is at the beginning then we have - * "=value" and consistent with CGI.pm we _ignore_ this. - */ - else if (query == eq) { - goto next; - } - - /* Otherwise it's "name=value". */ - else { - name = g_uri_unescape_segment(query, eq, NULL); - value = g_uri_unescape_segment(eq + 1, end, NULL); - } - - /* Append to the parameter set. */ - query_params_append(ps, name, value); - g_free(name); - g_free(value); - - next: - query = end; - if (*query) { - query++; /* skip '&' separator */ - } - } - - return ps; -} From 41582637b1577c261f19634bdd1bb5e7ed6e258d Mon Sep 17 00:00:00 2001 From: Sergii Zasenko Date: Mon, 24 Jul 2023 13:03:53 +0300 Subject: [PATCH 0619/2884] Allow UNIX socket option for VNC websocket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unix socket option limitation for VNC websocket - Reflect websocket option changes in documentation Signed-off-by: Sergii Zasenko Reviewed-by: Marc-André Lureau Message-Id: <20230724100353.16628-1-sergii@zasenko.name> --- qemu-options.hx | 4 ++++ ui/vnc.c | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index f5c01eeeb4..4d19660336 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2516,6 +2516,10 @@ SRST host. It is possible to control the websocket listen address independently, using the syntax ``websocket``\ =host:port. + Websocket could be allowed over UNIX domain socket, using the syntax + ``websocket``\ =unix:path, where path is the location of a unix socket + to listen for connections on. + If no TLS credentials are provided, the websocket connection runs in unencrypted mode. If TLS credentials are provided, the websocket connection requires encrypted client connections. diff --git a/ui/vnc.c b/ui/vnc.c index b3fd78022b..dd530f04e5 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3734,11 +3734,6 @@ static int vnc_display_get_address(const char *addrstr, addr->type = SOCKET_ADDRESS_TYPE_UNIX; addr->u.q_unix.path = g_strdup(addrstr + 5); - if (websocket) { - error_setg(errp, "UNIX sockets not supported with websock"); - goto cleanup; - } - if (to) { error_setg(errp, "Port range not support with UNIX socket"); goto cleanup; From 77bf310084dad38b3a2badf01766c659056f1cf2 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Fri, 26 Apr 2024 15:50:59 -0700 Subject: [PATCH 0620/2884] ui/gtk: Draw guest frame at refresh cycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Draw routine needs to be manually invoked in the next refresh if there is a scanout blob from the guest. This is to prevent a situation where there is a scheduled draw event but it won't happen bacause the window is currently in inactive state (minimized or tabified). If draw is not done for a long time, gl_block timeout and/or fence timeout (on the guest) will happen eventually. v2: Use gd_gl_area_draw(vc) in gtk-gl-area.c Suggested-by: Vivek Kasireddy Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Daniel P. Berrangé Signed-off-by: Dongwon Kim Acked-by: Marc-André Lureau Message-Id: <20240426225059.3871283-1-dongwon.kim@intel.com> --- ui/gtk-egl.c | 1 + ui/gtk-gl-area.c | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 3af5ac5bcf..75f6b9011a 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -150,6 +150,7 @@ void gd_egl_refresh(DisplayChangeListener *dcl) vc, vc->window ? vc->window : vc->gfx.drawing_area); if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + gd_egl_draw(vc); return; } diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 52dcac161e..4fff957c3f 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -126,6 +126,7 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + gd_gl_area_draw(vc); return; } From e4e62514e3cc2fc9dbae44af8b80f61c730beab4 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 8 May 2024 10:53:58 -0700 Subject: [PATCH 0621/2884] ui/gtk: Check if fence_fd is equal to or greater than 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'fence_fd' needs to be validated always before being referenced And the passing condition should include '== 0' as 0 is a valid value for the file descriptor. Suggested-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Cc: Philippe Mathieu-Daudé Cc: Daniel P. Berrangé Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20240508175403.3399895-2-dongwon.kim@intel.com> --- ui/gtk-egl.c | 2 +- ui/gtk-gl-area.c | 2 +- ui/gtk.c | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 75f6b9011a..bceeeb0352 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -99,7 +99,7 @@ void gd_egl_draw(VirtualConsole *vc) #ifdef CONFIG_GBM if (dmabuf) { egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd > 0) { + if (dmabuf->fence_fd >= 0) { qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); return; } diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 4fff957c3f..b490727402 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -86,7 +86,7 @@ void gd_gl_area_draw(VirtualConsole *vc) #ifdef CONFIG_GBM if (dmabuf) { egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd > 0) { + if (dmabuf->fence_fd >= 0) { qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); return; } diff --git a/ui/gtk.c b/ui/gtk.c index 810d7fc796..7819a86321 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -597,10 +597,12 @@ void gd_hw_gl_flushed(void *vcon) VirtualConsole *vc = vcon; QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; - qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL); - close(dmabuf->fence_fd); - dmabuf->fence_fd = -1; - graphic_hw_gl_block(vc->gfx.dcl.con, false); + if (dmabuf->fence_fd >= 0) { + qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL); + close(dmabuf->fence_fd); + dmabuf->fence_fd = -1; + graphic_hw_gl_block(vc->gfx.dcl.con, false); + } } /** DisplayState Callbacks (opengl version) **/ From 6e6ae491dad9a7ba813c7f1e70ce972c35c2d4ab Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 8 May 2024 10:53:59 -0700 Subject: [PATCH 0622/2884] ui/console: new dmabuf.h and dmabuf.c for QemuDmaBuf struct and helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New header and source files are added for containing QemuDmaBuf struct definition and newly introduced helpers for creating/freeing the struct and accessing its data. v10: Change the license type for both dmabuf.h and dmabuf.c from MIT to GPL to be in line with QEMU's default license v11: -- Added new helpers, qemu_dmabuf_close for closing dmabuf->fd, qemu_dmabuf_dup_fd for duplicating dmabuf->fd (Daniel P. Berrangé ) -- Let qemu_dmabuf_fee to call qemu_dmabuf_close before freeing the struct to make sure fd is closed. (Daniel P. Berrangé ) v12: Not closing fd in qemu_dmabuf_free because there are cases fd should still be available even after the struct is destroyed (e.g. virtio-gpu: res->dmabuf_fd). Suggested-by: Marc-André Lureau Reviewed-by: Marc-André Lureau Cc: Philippe Mathieu-Daudé Cc: Daniel P. Berrangé Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20240508175403.3399895-3-dongwon.kim@intel.com> --- include/ui/console.h | 20 +---- include/ui/dmabuf.h | 66 ++++++++++++++ ui/dmabuf.c | 210 +++++++++++++++++++++++++++++++++++++++++++ ui/meson.build | 1 + 4 files changed, 278 insertions(+), 19 deletions(-) create mode 100644 include/ui/dmabuf.h create mode 100644 ui/dmabuf.c diff --git a/include/ui/console.h b/include/ui/console.h index 0bc7a00ac0..a208a68b88 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -7,6 +7,7 @@ #include "qapi/qapi-types-ui.h" #include "ui/input.h" #include "ui/surface.h" +#include "ui/dmabuf.h" #define TYPE_QEMU_CONSOLE "qemu-console" OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE) @@ -185,25 +186,6 @@ struct QEMUGLParams { int minor_ver; }; -typedef struct QemuDmaBuf { - int fd; - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t fourcc; - uint64_t modifier; - uint32_t texture; - uint32_t x; - uint32_t y; - uint32_t backing_width; - uint32_t backing_height; - bool y0_top; - void *sync; - int fence_fd; - bool allow_fences; - bool draw_submitted; -} QemuDmaBuf; - enum display_scanout { SCANOUT_NONE, SCANOUT_SURFACE, diff --git a/include/ui/dmabuf.h b/include/ui/dmabuf.h new file mode 100644 index 0000000000..4198cdf85a --- /dev/null +++ b/include/ui/dmabuf.h @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QemuDmaBuf struct and helpers used for accessing its data + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef DMABUF_H +#define DMABUF_H + +typedef struct QemuDmaBuf { + int fd; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t fourcc; + uint64_t modifier; + uint32_t texture; + uint32_t x; + uint32_t y; + uint32_t backing_width; + uint32_t backing_height; + bool y0_top; + void *sync; + int fence_fd; + bool allow_fences; + bool draw_submitted; +} QemuDmaBuf; + +QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, + uint32_t stride, uint32_t x, + uint32_t y, uint32_t backing_width, + uint32_t backing_height, uint32_t fourcc, + uint64_t modifier, int dmabuf_fd, + bool allow_fences, bool y0_top); +void qemu_dmabuf_free(QemuDmaBuf *dmabuf); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuDmaBuf, qemu_dmabuf_free); + +int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf); +int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf); +void qemu_dmabuf_close(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf); +uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_x(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_y(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_backing_width(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_backing_height(QemuDmaBuf *dmabuf); +bool qemu_dmabuf_get_y0_top(QemuDmaBuf *dmabuf); +void *qemu_dmabuf_get_sync(QemuDmaBuf *dmabuf); +int32_t qemu_dmabuf_get_fence_fd(QemuDmaBuf *dmabuf); +bool qemu_dmabuf_get_allow_fences(QemuDmaBuf *dmabuf); +bool qemu_dmabuf_get_draw_submitted(QemuDmaBuf *dmabuf); +void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture); +void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd); +void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync); +void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted); +void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd); + +#endif diff --git a/ui/dmabuf.c b/ui/dmabuf.c new file mode 100644 index 0000000000..e047d5ca26 --- /dev/null +++ b/ui/dmabuf.c @@ -0,0 +1,210 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QemuDmaBuf struct and helpers used for accessing its data + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "ui/dmabuf.h" + +QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, + uint32_t stride, uint32_t x, + uint32_t y, uint32_t backing_width, + uint32_t backing_height, uint32_t fourcc, + uint64_t modifier, int32_t dmabuf_fd, + bool allow_fences, bool y0_top) { + QemuDmaBuf *dmabuf; + + dmabuf = g_new0(QemuDmaBuf, 1); + + dmabuf->width = width; + dmabuf->height = height; + dmabuf->stride = stride; + dmabuf->x = x; + dmabuf->y = y; + dmabuf->backing_width = backing_width; + dmabuf->backing_height = backing_height; + dmabuf->fourcc = fourcc; + dmabuf->modifier = modifier; + dmabuf->fd = dmabuf_fd; + dmabuf->allow_fences = allow_fences; + dmabuf->y0_top = y0_top; + dmabuf->fence_fd = -1; + + return dmabuf; +} + +void qemu_dmabuf_free(QemuDmaBuf *dmabuf) +{ + if (dmabuf == NULL) { + return; + } + + g_free(dmabuf); +} + +int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->fd; +} + +int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + if (dmabuf->fd >= 0) { + return dup(dmabuf->fd); + } else { + return -1; + } +} + +void qemu_dmabuf_close(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + if (dmabuf->fd >= 0) { + close(dmabuf->fd); + dmabuf->fd = -1; + } +} + +uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->width; +} + +uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->height; +} + +uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->stride; +} + +uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->fourcc; +} + +uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->modifier; +} + +uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->texture; +} + +uint32_t qemu_dmabuf_get_x(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->x; +} + +uint32_t qemu_dmabuf_get_y(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->y; +} + +uint32_t qemu_dmabuf_get_backing_width(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->backing_width; +} + +uint32_t qemu_dmabuf_get_backing_height(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->backing_height; +} + +bool qemu_dmabuf_get_y0_top(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->y0_top; +} + +void *qemu_dmabuf_get_sync(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->sync; +} + +int32_t qemu_dmabuf_get_fence_fd(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->fence_fd; +} + +bool qemu_dmabuf_get_allow_fences(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->allow_fences; +} + +bool qemu_dmabuf_get_draw_submitted(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->draw_submitted; +} + +void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture) +{ + assert(dmabuf != NULL); + dmabuf->texture = texture; +} + +void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd) +{ + assert(dmabuf != NULL); + dmabuf->fence_fd = fence_fd; +} + +void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync) +{ + assert(dmabuf != NULL); + dmabuf->sync = sync; +} + +void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted) +{ + assert(dmabuf != NULL); + dmabuf->draw_submitted = draw_submitted; +} + +void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd) +{ + assert(dmabuf != NULL); + dmabuf->fd = fd; +} diff --git a/ui/meson.build b/ui/meson.build index a5ce22a678..5d89986b0e 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -7,6 +7,7 @@ system_ss.add(files( 'clipboard.c', 'console.c', 'cursor.c', + 'dmabuf.c', 'input-keymap.c', 'input-legacy.c', 'input-barrier.c', From 6779a3076f295fafe52d43049fa954426c1d594a Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 8 May 2024 10:54:00 -0700 Subject: [PATCH 0623/2884] ui/console: Use qemu_dmabuf_get_..() helpers instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates all instances where fields within the QemuDmaBuf struct are directly accessed, replacing them with calls to these new helper functions. v6: fix typos in helper names in ui/spice-display.c v7: removed prefix, "dpy_gl_" from all helpers v8: Introduction of helpers was removed as those were already added by the previous commit v11: -- Use new qemu_dmabuf_close() instead of close(qemu_dmabuf_get_fd()). (Daniel P. Berrangé ) -- Use new qemu_dmabuf_dup_fd() instead of dup(qemu_dmabuf_get_fd()). (Daniel P. Berrangé ) Suggested-by: Marc-André Lureau Reviewed-by: Marc-André Lureau Cc: Philippe Mathieu-Daudé Cc: Daniel P. Berrangé Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20240508175403.3399895-4-dongwon.kim@intel.com> --- hw/display/vhost-user-gpu.c | 5 +--- hw/display/virtio-gpu-udmabuf.c | 7 +++-- hw/vfio/display.c | 12 +++++--- ui/console.c | 4 +-- ui/dbus-console.c | 9 ++++-- ui/dbus-listener.c | 43 +++++++++++++++++----------- ui/egl-headless.c | 23 ++++++++++----- ui/egl-helpers.c | 47 ++++++++++++++++++------------- ui/gtk-egl.c | 48 ++++++++++++++++++++----------- ui/gtk-gl-area.c | 37 ++++++++++++++++-------- ui/gtk.c | 6 ++-- ui/spice-display.c | 50 +++++++++++++++++++-------------- 12 files changed, 181 insertions(+), 110 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 709c8a02a1..454e5afcff 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -262,10 +262,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) g->parent_obj.enable = 1; con = g->parent_obj.scanout[m->scanout_id].con; dmabuf = &g->dmabuf[m->scanout_id]; - if (dmabuf->fd >= 0) { - close(dmabuf->fd); - dmabuf->fd = -1; - } + qemu_dmabuf_close(dmabuf); dpy_gl_release_dmabuf(con, dmabuf); if (fd == -1) { dpy_gl_scanout_disable(con); diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index d51184d658..c90eba281e 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -206,6 +206,7 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, { struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id]; VGPUDMABuf *new_primary, *old_primary = NULL; + uint32_t width, height; new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r); if (!new_primary) { @@ -216,10 +217,10 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, old_primary = g->dmabuf.primary[scanout_id]; } + width = qemu_dmabuf_get_width(&new_primary->buf); + height = qemu_dmabuf_get_height(&new_primary->buf); g->dmabuf.primary[scanout_id] = new_primary; - qemu_console_resize(scanout->con, - new_primary->buf.width, - new_primary->buf.height); + qemu_console_resize(scanout->con, width, height); dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); if (old_primary) { diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 1aa440c663..7784502b53 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -260,8 +260,9 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) { QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); + + qemu_dmabuf_close(&dmabuf->buf); dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); - close(dmabuf->buf.fd); g_free(dmabuf); } @@ -286,6 +287,7 @@ static void vfio_display_dmabuf_update(void *opaque) VFIOPCIDevice *vdev = opaque; VFIODisplay *dpy = vdev->dpy; VFIODMABuf *primary, *cursor; + uint32_t width, height; bool free_bufs = false, new_cursor = false; primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); @@ -296,10 +298,12 @@ static void vfio_display_dmabuf_update(void *opaque) return; } + width = qemu_dmabuf_get_width(&primary->buf); + height = qemu_dmabuf_get_height(&primary->buf); + if (dpy->dmabuf.primary != primary) { dpy->dmabuf.primary = primary; - qemu_console_resize(dpy->con, - primary->buf.width, primary->buf.height); + qemu_console_resize(dpy->con, width, height); dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); free_bufs = true; } @@ -328,7 +332,7 @@ static void vfio_display_dmabuf_update(void *opaque) cursor->pos_updates = 0; } - dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); + dpy_gl_update(dpy->con, 0, 0, width, height); if (free_bufs) { vfio_display_free_dmabufs(vdev); diff --git a/ui/console.c b/ui/console.c index 43226c5c14..1b2cd0c736 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1459,7 +1459,7 @@ int qemu_console_get_width(QemuConsole *con, int fallback) } switch (con->scanout.kind) { case SCANOUT_DMABUF: - return con->scanout.dmabuf->width; + return qemu_dmabuf_get_width(con->scanout.dmabuf); case SCANOUT_TEXTURE: return con->scanout.texture.width; case SCANOUT_SURFACE: @@ -1476,7 +1476,7 @@ int qemu_console_get_height(QemuConsole *con, int fallback) } switch (con->scanout.kind) { case SCANOUT_DMABUF: - return con->scanout.dmabuf->height; + return qemu_dmabuf_get_height(con->scanout.dmabuf); case SCANOUT_TEXTURE: return con->scanout.texture.height; case SCANOUT_SURFACE: diff --git a/ui/dbus-console.c b/ui/dbus-console.c index 49da9ccc83..578b67f62b 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -110,11 +110,14 @@ static void dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { + uint32_t width, height; + DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); - dbus_display_console_set_size(ddc, - dmabuf->width, - dmabuf->height); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + + dbus_display_console_set_size(ddc, width, height); } static void diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 4a0a5d78f9..62d1e2d3f9 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -278,29 +278,33 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); g_autoptr(GError) err = NULL; g_autoptr(GUnixFDList) fd_list = NULL; + int fd; + uint32_t width, height, stride, fourcc; + uint64_t modifier; + bool y0_top; + fd = qemu_dmabuf_get_fd(dmabuf); fd_list = g_unix_fd_list_new(); - if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) { + if (g_unix_fd_list_append(fd_list, fd, &err) != 0) { error_report("Failed to setup dmabuf fdlist: %s", err->message); return; } ddl_discard_pending_messages(ddl); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + stride = qemu_dmabuf_get_stride(dmabuf); + fourcc = qemu_dmabuf_get_fourcc(dmabuf); + modifier = qemu_dmabuf_get_modifier(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); + /* FIXME: add missing x/y/w/h support */ qemu_dbus_display1_listener_call_scanout_dmabuf( - ddl->proxy, - g_variant_new_handle(0), - dmabuf->width, - dmabuf->height, - dmabuf->stride, - dmabuf->fourcc, - dmabuf->modifier, - dmabuf->y0_top, - G_DBUS_CALL_FLAGS_NONE, - -1, - fd_list, - NULL, NULL, NULL); + ddl->proxy, g_variant_new_handle(0), + width, height, stride, fourcc, modifier, + y0_top, G_DBUS_CALL_FLAGS_NONE, + -1, fd_list, NULL, NULL, NULL); } #endif /* GBM */ #endif /* OPENGL */ @@ -488,6 +492,7 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, DisplaySurface *ds; GVariant *v_data = NULL; egl_fb cursor_fb = EGL_FB_INIT; + uint32_t width, height, texture; if (!dmabuf) { qemu_dbus_display1_listener_call_mouse_set( @@ -497,12 +502,16 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, } egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); - ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height); + + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + + egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false); + ds = qemu_create_displaysurface(width, height); egl_fb_read(ds, &cursor_fb); v_data = g_variant_new_from_data( diff --git a/ui/egl-headless.c b/ui/egl-headless.c index d5637dadb2..6187249c73 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -85,29 +85,38 @@ static void egl_scanout_texture(DisplayChangeListener *dcl, static void egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { + uint32_t width, height, texture; + egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_scanout_texture(dcl, dmabuf->texture, - false, dmabuf->width, dmabuf->height, - 0, 0, dmabuf->width, dmabuf->height, NULL); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + + egl_scanout_texture(dcl, texture, false, width, height, 0, 0, + width, height, NULL); } static void egl_cursor_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf, bool have_hot, uint32_t hot_x, uint32_t hot_y) { + uint32_t width, height, texture; egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); if (dmabuf) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); + + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false); } else { egl_fb_destroy(&edpy->cursor_fb); } diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 3d19dbe382..3f96e63d25 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -146,10 +146,10 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) glViewport(0, 0, dst->width, dst->height); if (src->dmabuf) { - x1 = src->dmabuf->x; - y1 = src->dmabuf->y; - w = src->dmabuf->width; - h = src->dmabuf->height; + x1 = qemu_dmabuf_get_x(src->dmabuf); + y1 = qemu_dmabuf_get_y(src->dmabuf); + w = qemu_dmabuf_get_width(src->dmabuf); + h = qemu_dmabuf_get_height(src->dmabuf); } w = (x1 + w) > src->width ? src->width - x1 : w; @@ -308,30 +308,33 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) EGLImageKHR image = EGL_NO_IMAGE_KHR; EGLint attrs[64]; int i = 0; + uint64_t modifier; + uint32_t texture = qemu_dmabuf_get_texture(dmabuf); - if (dmabuf->texture != 0) { + if (texture != 0) { return; } attrs[i++] = EGL_WIDTH; - attrs[i++] = dmabuf->backing_width; + attrs[i++] = qemu_dmabuf_get_backing_width(dmabuf); attrs[i++] = EGL_HEIGHT; - attrs[i++] = dmabuf->backing_height; + attrs[i++] = qemu_dmabuf_get_backing_height(dmabuf); attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; - attrs[i++] = dmabuf->fourcc; + attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attrs[i++] = dmabuf->fd; + attrs[i++] = qemu_dmabuf_get_fd(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attrs[i++] = dmabuf->stride; + attrs[i++] = qemu_dmabuf_get_stride(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attrs[i++] = 0; #ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT - if (dmabuf->modifier) { + modifier = qemu_dmabuf_get_modifier(dmabuf); + if (modifier) { attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attrs[i++] = (dmabuf->modifier >> 0) & 0xffffffff; + attrs[i++] = (modifier >> 0) & 0xffffffff; attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff; + attrs[i++] = (modifier >> 32) & 0xffffffff; } #endif attrs[i++] = EGL_NONE; @@ -346,7 +349,8 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) } glGenTextures(1, &dmabuf->texture); - glBindTexture(GL_TEXTURE_2D, dmabuf->texture); + texture = qemu_dmabuf_get_texture(dmabuf); + glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -356,11 +360,14 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf) { - if (dmabuf->texture == 0) { + uint32_t texture; + + texture = qemu_dmabuf_get_texture(dmabuf); + if (texture == 0) { return; } - glDeleteTextures(1, &dmabuf->texture); + glDeleteTextures(1, &texture); dmabuf->texture = 0; } @@ -382,10 +389,12 @@ void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf) { - if (dmabuf->sync) { + void *sync = qemu_dmabuf_get_sync(dmabuf); + + if (sync) { dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display, - dmabuf->sync); - eglDestroySyncKHR(qemu_egl_display, dmabuf->sync); + sync); + eglDestroySyncKHR(qemu_egl_display, sync); dmabuf->sync = NULL; } } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index bceeeb0352..5d7744ae4d 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -70,6 +70,7 @@ void gd_egl_draw(VirtualConsole *vc) QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif int ww, wh, ws; + int fence_fd; if (!vc->gfx.gls) { return; @@ -83,7 +84,7 @@ void gd_egl_draw(VirtualConsole *vc) if (vc->gfx.scanout_mode) { #ifdef CONFIG_GBM if (dmabuf) { - if (!dmabuf->draw_submitted) { + if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { return; } else { dmabuf->draw_submitted = false; @@ -99,8 +100,9 @@ void gd_egl_draw(VirtualConsole *vc) #ifdef CONFIG_GBM if (dmabuf) { egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd >= 0) { - qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + if (fence_fd >= 0) { + qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc); return; } graphic_hw_gl_block(vc->gfx.dcl.con, false); @@ -149,7 +151,8 @@ void gd_egl_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate( vc, vc->window ? vc->window : vc->gfx.drawing_area); - if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { gd_egl_draw(vc); return; } @@ -265,22 +268,30 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, { #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + uint32_t x, y, width, height, backing_width, backing_height, texture; + bool y0_top; eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - gd_egl_scanout_texture(dcl, dmabuf->texture, - dmabuf->y0_top, - dmabuf->backing_width, dmabuf->backing_height, - dmabuf->x, dmabuf->y, dmabuf->width, - dmabuf->height, NULL); + x = qemu_dmabuf_get_x(dmabuf); + y = qemu_dmabuf_get_y(dmabuf); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); - if (dmabuf->allow_fences) { + gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height, + x, y, width, height, NULL); + + if (qemu_dmabuf_get_allow_fences(dmabuf)) { vc->gfx.guest_fb.dmabuf = dmabuf; } #endif @@ -292,15 +303,19 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, { #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + uint32_t backing_width, backing_height, texture; if (dmabuf) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&vc->gfx.cursor_fb, - dmabuf->backing_width, dmabuf->backing_height, - dmabuf->texture, false); + + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + egl_fb_setup_for_tex(&vc->gfx.cursor_fb, backing_width, backing_height, + texture, false); } else { egl_fb_destroy(&vc->gfx.cursor_fb); } @@ -364,7 +379,8 @@ void gd_egl_flush(DisplayChangeListener *dcl, VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GtkWidget *area = vc->gfx.drawing_area; - if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { graphic_hw_gl_block(vc->gfx.dcl.con, true); vc->gfx.guest_fb.dmabuf->draw_submitted = true; gtk_egl_set_scanout_mode(vc, true); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index b490727402..86360fe79b 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -60,7 +60,7 @@ void gd_gl_area_draw(VirtualConsole *vc) #ifdef CONFIG_GBM if (dmabuf) { - if (!dmabuf->draw_submitted) { + if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { return; } else { dmabuf->draw_submitted = false; @@ -85,9 +85,11 @@ void gd_gl_area_draw(VirtualConsole *vc) glFlush(); #ifdef CONFIG_GBM if (dmabuf) { + int fence_fd; egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd >= 0) { - qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + if (fence_fd >= 0) { + qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc); return; } graphic_hw_gl_block(vc->gfx.dcl.con, false); @@ -125,7 +127,8 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); - if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { gd_gl_area_draw(vc); return; } @@ -286,7 +289,8 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { graphic_hw_gl_block(vc->gfx.dcl.con, true); vc->gfx.guest_fb.dmabuf->draw_submitted = true; gtk_gl_area_set_scanout_mode(vc, true); @@ -299,20 +303,29 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, { #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + uint32_t x, y, width, height, backing_width, backing_height, texture; + bool y0_top; gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - gd_gl_area_scanout_texture(dcl, dmabuf->texture, - dmabuf->y0_top, - dmabuf->backing_width, dmabuf->backing_height, - dmabuf->x, dmabuf->y, dmabuf->width, - dmabuf->height, NULL); + x = qemu_dmabuf_get_x(dmabuf); + y = qemu_dmabuf_get_y(dmabuf); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); - if (dmabuf->allow_fences) { + gd_gl_area_scanout_texture(dcl, texture, y0_top, + backing_width, backing_height, + x, y, width, height, NULL); + + if (qemu_dmabuf_get_allow_fences(dmabuf)) { vc->gfx.guest_fb.dmabuf = dmabuf; } #endif diff --git a/ui/gtk.c b/ui/gtk.c index 7819a86321..237c913b26 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -596,10 +596,12 @@ void gd_hw_gl_flushed(void *vcon) { VirtualConsole *vc = vcon; QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; + int fence_fd; if (dmabuf->fence_fd >= 0) { - qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL); - close(dmabuf->fence_fd); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + qemu_set_fd_handler(fence_fd, NULL, NULL, NULL); + close(fence_fd); dmabuf->fence_fd = -1; graphic_hw_gl_block(vc->gfx.dcl.con, false); } diff --git a/ui/spice-display.c b/ui/spice-display.c index 6eb98a5a5c..8a8472d029 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -976,6 +976,7 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, uint32_t hot_x, uint32_t hot_y) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + uint32_t width, height, texture; ssd->have_hot = have_hot; ssd->hot_x = hot_x; @@ -984,11 +985,13 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot); if (dmabuf) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&ssd->cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false); } else { egl_fb_destroy(&ssd->cursor_fb); } @@ -1026,6 +1029,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, bool y_0_top = false; /* FIXME */ uint64_t cookie; int fd; + uint32_t width, height, texture; if (!ssd->have_scanout) { return; @@ -1042,41 +1046,45 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, if (ssd->guest_dmabuf_refresh) { QemuDmaBuf *dmabuf = ssd->guest_dmabuf; + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + if (render_cursor) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } /* source framebuffer */ - egl_fb_setup_for_tex(&ssd->guest_fb, - dmabuf->width, dmabuf->height, - dmabuf->texture, false); + egl_fb_setup_for_tex(&ssd->guest_fb, width, height, + texture, false); /* dest framebuffer */ - if (ssd->blit_fb.width != dmabuf->width || - ssd->blit_fb.height != dmabuf->height) { - trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, dmabuf->width, - dmabuf->height); + if (ssd->blit_fb.width != width || + ssd->blit_fb.height != height) { + trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width, + height); egl_fb_destroy(&ssd->blit_fb); egl_fb_setup_new_tex(&ssd->blit_fb, - dmabuf->width, dmabuf->height); + width, height); fd = egl_get_fd_for_texture(ssd->blit_fb.texture, &stride, &fourcc, NULL); - spice_qxl_gl_scanout(&ssd->qxl, fd, - dmabuf->width, dmabuf->height, + spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, stride, fourcc, false); } } else { - trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, - dmabuf->width, dmabuf->height); + stride = qemu_dmabuf_get_stride(dmabuf); + fourcc = qemu_dmabuf_get_fourcc(dmabuf); + y_0_top = qemu_dmabuf_get_y0_top(dmabuf); + fd = qemu_dmabuf_dup_fd(dmabuf); + + trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height); /* note: spice server will close the fd, so hand over a dup */ - spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd), - dmabuf->width, dmabuf->height, - dmabuf->stride, dmabuf->fourcc, - dmabuf->y0_top); + spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, + stride, fourcc, y_0_top); } - qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height); + qemu_spice_gl_monitor_config(ssd, 0, 0, width, height); ssd->guest_dmabuf_refresh = false; } From fa6426805b124400cfb700b75e8fe4a89dd2ed7a Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 8 May 2024 10:54:01 -0700 Subject: [PATCH 0624/2884] ui/console: Use qemu_dmabuf_set_..() helpers instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates all occurrences where these fields were set directly have been updated to utilize helper functions. v7: removed prefix, "dpy_gl_" from all helpers v8: Introduction of helpers was removed as those were already added by the previous commit Suggested-by: Marc-André Lureau Reviewed-by: Marc-André Lureau Cc: Philippe Mathieu-Daudé Cc: Daniel P. Berrangé Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20240508175403.3399895-5-dongwon.kim@intel.com> --- ui/egl-helpers.c | 16 +++++++++------- ui/gtk-egl.c | 4 ++-- ui/gtk-gl-area.c | 4 ++-- ui/gtk.c | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 3f96e63d25..99b2ebbe23 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -348,8 +348,8 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) return; } - glGenTextures(1, &dmabuf->texture); - texture = qemu_dmabuf_get_texture(dmabuf); + glGenTextures(1, &texture); + qemu_dmabuf_set_texture(dmabuf, texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -368,7 +368,7 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf) } glDeleteTextures(1, &texture); - dmabuf->texture = 0; + qemu_dmabuf_set_texture(dmabuf, 0); } void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) @@ -382,7 +382,7 @@ void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) sync = eglCreateSyncKHR(qemu_egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); if (sync != EGL_NO_SYNC_KHR) { - dmabuf->sync = sync; + qemu_dmabuf_set_sync(dmabuf, sync); } } } @@ -390,12 +390,14 @@ void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf) { void *sync = qemu_dmabuf_get_sync(dmabuf); + int fence_fd; if (sync) { - dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display, - sync); + fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display, + sync); + qemu_dmabuf_set_fence_fd(dmabuf, fence_fd); eglDestroySyncKHR(qemu_egl_display, sync); - dmabuf->sync = NULL; + qemu_dmabuf_set_sync(dmabuf, NULL); } } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 5d7744ae4d..0473f689c9 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -87,7 +87,7 @@ void gd_egl_draw(VirtualConsole *vc) if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { return; } else { - dmabuf->draw_submitted = false; + qemu_dmabuf_set_draw_submitted(dmabuf, false); } } #endif @@ -382,7 +382,7 @@ void gd_egl_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { graphic_hw_gl_block(vc->gfx.dcl.con, true); - vc->gfx.guest_fb.dmabuf->draw_submitted = true; + qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_egl_set_scanout_mode(vc, true); gtk_widget_queue_draw_area(area, x, y, w, h); return; diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 86360fe79b..b628b35451 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -63,7 +63,7 @@ void gd_gl_area_draw(VirtualConsole *vc) if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { return; } else { - dmabuf->draw_submitted = false; + qemu_dmabuf_set_draw_submitted(dmabuf, false); } } #endif @@ -292,7 +292,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { graphic_hw_gl_block(vc->gfx.dcl.con, true); - vc->gfx.guest_fb.dmabuf->draw_submitted = true; + qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_gl_area_set_scanout_mode(vc, true); } gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); diff --git a/ui/gtk.c b/ui/gtk.c index 237c913b26..3a6832eb1b 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -598,11 +598,11 @@ void gd_hw_gl_flushed(void *vcon) QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; int fence_fd; - if (dmabuf->fence_fd >= 0) { - fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + if (fence_fd >= 0) { qemu_set_fd_handler(fence_fd, NULL, NULL, NULL); close(fence_fd); - dmabuf->fence_fd = -1; + qemu_dmabuf_set_fence_fd(dmabuf, -1); graphic_hw_gl_block(vc->gfx.dcl.con, false); } } From c0fcd6334ff673b571ac068f28b6b779bdade8a2 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 8 May 2024 10:54:02 -0700 Subject: [PATCH 0625/2884] ui/console: Use qemu_dmabuf_new() and free() helpers instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces utility functions for the creation and deallocation of QemuDmaBuf instances. Additionally, it updates all relevant sections of the codebase to utilize these new utility functions. v7: remove prefix, "dpy_gl_" from all helpers qemu_dmabuf_free() returns without doing anything if input is null (Daniel P. Berrangé ) call G_DEFINE_AUTOPTR_CLEANUP_FUNC for qemu_dmabuf_free() (Daniel P. Berrangé ) v8: Introduction of helpers was removed as those were already added by the previous commit v9: set dmabuf->allow_fences to 'true' when dmabuf is created in virtio_gpu_create_dmabuf()/virtio-gpu-udmabuf.c removed unnecessary spaces were accidently added in the patch, 'ui/console: Use qemu_dmabuf_new() a...' v11: Calling qemu_dmabuf_close was removed as closing dmabuf->fd will be done in qemu_dmabuf_free anyway. (Daniel P. Berrangé ) v12: --- Calling qemu_dmabuf_close separately as qemu_dmabuf_free doesn't do it. --- 'dmabuf' is now allocated space so it should be freed at the end of dbus_scanout_texture v13: --- Immediately free dmabuf after it is released to prevent possible leaking of the ptr (Marc-André Lureau ) --- Use g_autoptr macro to define *dmabuf for auto clean up instead of calling qemu_dmabuf_free (Marc-André Lureau ) v14: --- (vhost-user-gpu) Change qemu_dmabuf_free back to g_clear_pointer as it was done because of some misunderstanding (v13). --- (vhost-user-gpu) g->dmabuf[m->scanout_id] needs to be set to NULL to prevent freed dmabuf to be accessed again in case if(fd==-1)break; happens (before new dmabuf is allocated). Otherwise, it would cause invalid memory access when the same function is executed. Also NULL check should be done before qemu_dmabuf_close (it asserts dmabuf!=NULL.). (Marc-André Lureau ) Suggested-by: Marc-André Lureau Cc: Philippe Mathieu-Daudé Cc: Daniel P. Berrangé Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20240508175403.3399895-6-dongwon.kim@intel.com> --- hw/display/vhost-user-gpu.c | 43 +++++++++++++++++++-------------- hw/display/virtio-gpu-udmabuf.c | 24 +++++++----------- hw/vfio/display.c | 26 +++++++++----------- include/hw/vfio/vfio-common.h | 2 +- include/hw/virtio/virtio-gpu.h | 4 +-- ui/dbus-listener.c | 28 +++++++++------------ 6 files changed, 61 insertions(+), 66 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 454e5afcff..e4b398d26c 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -249,6 +249,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) case VHOST_USER_GPU_DMABUF_SCANOUT: { VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout; int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr); + uint64_t modifier = 0; QemuDmaBuf *dmabuf; if (m->scanout_id >= g->parent_obj.conf.max_outputs) { @@ -261,27 +262,33 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) g->parent_obj.enable = 1; con = g->parent_obj.scanout[m->scanout_id].con; - dmabuf = &g->dmabuf[m->scanout_id]; - qemu_dmabuf_close(dmabuf); - dpy_gl_release_dmabuf(con, dmabuf); - if (fd == -1) { - dpy_gl_scanout_disable(con); - break; - } - *dmabuf = (QemuDmaBuf) { - .fd = fd, - .width = m->fd_width, - .height = m->fd_height, - .stride = m->fd_stride, - .fourcc = m->fd_drm_fourcc, - .y0_top = m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, - }; - if (msg->request == VHOST_USER_GPU_DMABUF_SCANOUT2) { - VhostUserGpuDMABUFScanout2 *m2 = &msg->payload.dmabuf_scanout2; - dmabuf->modifier = m2->modifier; + dmabuf = g->dmabuf[m->scanout_id]; + + if (dmabuf) { + qemu_dmabuf_close(dmabuf); + dpy_gl_release_dmabuf(con, dmabuf); + g_clear_pointer(&dmabuf, qemu_dmabuf_free); } + if (fd == -1) { + dpy_gl_scanout_disable(con); + g->dmabuf[m->scanout_id] = NULL; + break; + } + + if (msg->request == VHOST_USER_GPU_DMABUF_SCANOUT2) { + VhostUserGpuDMABUFScanout2 *m2 = &msg->payload.dmabuf_scanout2; + modifier = m2->modifier; + } + + dmabuf = qemu_dmabuf_new(m->fd_width, m->fd_height, + m->fd_stride, 0, 0, 0, 0, + m->fd_drm_fourcc, modifier, + fd, false, m->fd_flags & + VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP); + dpy_gl_scanout_dmabuf(con, dmabuf); + g->dmabuf[m->scanout_id] = dmabuf; break; } case VHOST_USER_GPU_DMABUF_UPDATE: { diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index c90eba281e..c02ec6d37d 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -162,7 +162,8 @@ static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) struct virtio_gpu_scanout *scanout; scanout = &g->parent_obj.scanout[dmabuf->scanout_id]; - dpy_gl_release_dmabuf(scanout->con, &dmabuf->buf); + dpy_gl_release_dmabuf(scanout->con, dmabuf->buf); + g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next); g_free(dmabuf); } @@ -181,17 +182,10 @@ static VGPUDMABuf } dmabuf = g_new0(VGPUDMABuf, 1); - dmabuf->buf.width = r->width; - dmabuf->buf.height = r->height; - dmabuf->buf.stride = fb->stride; - dmabuf->buf.x = r->x; - dmabuf->buf.y = r->y; - dmabuf->buf.backing_width = fb->width; - dmabuf->buf.backing_height = fb->height; - dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); - dmabuf->buf.fd = res->dmabuf_fd; - dmabuf->buf.allow_fences = true; - dmabuf->buf.draw_submitted = false; + dmabuf->buf = qemu_dmabuf_new(r->width, r->height, fb->stride, + r->x, r->y, fb->width, fb->height, + qemu_pixman_to_drm_format(fb->format), + 0, res->dmabuf_fd, true, false); dmabuf->scanout_id = scanout_id; QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); @@ -217,11 +211,11 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, old_primary = g->dmabuf.primary[scanout_id]; } - width = qemu_dmabuf_get_width(&new_primary->buf); - height = qemu_dmabuf_get_height(&new_primary->buf); + width = qemu_dmabuf_get_width(new_primary->buf); + height = qemu_dmabuf_get_height(new_primary->buf); g->dmabuf.primary[scanout_id] = new_primary; qemu_console_resize(scanout->con, width, height); - dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); + dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf); if (old_primary) { virtio_gpu_free_dmabuf(g, old_primary); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 7784502b53..fe624a6c9b 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -241,14 +241,11 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, dmabuf = g_new0(VFIODMABuf, 1); dmabuf->dmabuf_id = plane.dmabuf_id; - dmabuf->buf.width = plane.width; - dmabuf->buf.height = plane.height; - dmabuf->buf.backing_width = plane.width; - dmabuf->buf.backing_height = plane.height; - dmabuf->buf.stride = plane.stride; - dmabuf->buf.fourcc = plane.drm_format; - dmabuf->buf.modifier = plane.drm_format_mod; - dmabuf->buf.fd = fd; + dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height, + plane.stride, 0, 0, plane.width, + plane.height, plane.drm_format, + plane.drm_format_mod, fd, false, false); + if (plane_type == DRM_PLANE_TYPE_CURSOR) { vfio_display_update_cursor(dmabuf, &plane); } @@ -261,8 +258,9 @@ static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) { QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); - qemu_dmabuf_close(&dmabuf->buf); - dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); + qemu_dmabuf_close(dmabuf->buf); + dpy_gl_release_dmabuf(dpy->con, dmabuf->buf); + g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); g_free(dmabuf); } @@ -298,13 +296,13 @@ static void vfio_display_dmabuf_update(void *opaque) return; } - width = qemu_dmabuf_get_width(&primary->buf); - height = qemu_dmabuf_get_height(&primary->buf); + width = qemu_dmabuf_get_width(primary->buf); + height = qemu_dmabuf_get_height(primary->buf); if (dpy->dmabuf.primary != primary) { dpy->dmabuf.primary = primary; qemu_console_resize(dpy->con, width, height); - dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); + dpy_gl_scanout_dmabuf(dpy->con, primary->buf); free_bufs = true; } @@ -318,7 +316,7 @@ static void vfio_display_dmabuf_update(void *opaque) if (cursor && (new_cursor || cursor->hot_updates)) { bool have_hot = (cursor->hot_x != 0xffffffff && cursor->hot_y != 0xffffffff); - dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, + dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot, cursor->hot_x, cursor->hot_y); cursor->hot_updates = 0; } else if (!cursor && new_cursor) { diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index b9da6c08ef..d66e27db02 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -148,7 +148,7 @@ typedef struct VFIOGroup { } VFIOGroup; typedef struct VFIODMABuf { - QemuDmaBuf buf; + QemuDmaBuf *buf; uint32_t pos_x, pos_y, pos_updates; uint32_t hot_x, hot_y, hot_updates; int dmabuf_id; diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index ed44cdad6b..56d6e821bf 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -169,7 +169,7 @@ struct VirtIOGPUBaseClass { DEFINE_PROP_UINT32("yres", _state, _conf.yres, 800) typedef struct VGPUDMABuf { - QemuDmaBuf buf; + QemuDmaBuf *buf; uint32_t scanout_id; QTAILQ_ENTRY(VGPUDMABuf) next; } VGPUDMABuf; @@ -238,7 +238,7 @@ struct VhostUserGPU { VhostUserBackend *vhost; int vhost_gpu_fd; /* closed by the chardev */ CharBackend vhost_chr; - QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; + QemuDmaBuf *dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; bool backend_blocked; }; diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 62d1e2d3f9..5490088043 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -442,28 +442,24 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, trace_dbus_scanout_texture(tex_id, backing_y_0_top, backing_width, backing_height, x, y, w, h); #ifdef CONFIG_GBM - QemuDmaBuf dmabuf = { - .width = w, - .height = h, - .y0_top = backing_y_0_top, - .x = x, - .y = y, - .backing_width = backing_width, - .backing_height = backing_height, - }; + g_autoptr(QemuDmaBuf) dmabuf = NULL; + int fd; + uint32_t stride, fourcc; + uint64_t modifier; assert(tex_id); - dmabuf.fd = egl_get_fd_for_texture( - tex_id, (EGLint *)&dmabuf.stride, - (EGLint *)&dmabuf.fourcc, - &dmabuf.modifier); - if (dmabuf.fd < 0) { + fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc, + &modifier); + if (fd < 0) { error_report("%s: failed to get fd for texture", __func__); return; } + dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width, + backing_height, fourcc, modifier, fd, + false, backing_y_0_top); - dbus_scanout_dmabuf(dcl, &dmabuf); - close(dmabuf.fd); + dbus_scanout_dmabuf(dcl, dmabuf); + qemu_dmabuf_close(dmabuf); #endif #ifdef WIN32 From db81dd6bdc3425145398c6634c329c2182144d6d Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 8 May 2024 10:54:03 -0700 Subject: [PATCH 0626/2884] ui/console: move QemuDmaBuf struct def to dmabuf.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To complete privatizing process of QemuDmaBuf, QemuDmaBuf struct def is moved to dmabuf.c Suggested-by: Marc-André Lureau Reviewed-by: Marc-André Lureau Cc: Philippe Mathieu-Daudé Cc: Daniel P. Berrangé Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Message-Id: <20240508175403.3399895-7-dongwon.kim@intel.com> --- include/ui/dmabuf.h | 19 +------------------ ui/dmabuf.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/ui/dmabuf.h b/include/ui/dmabuf.h index 4198cdf85a..dc74ba895a 100644 --- a/include/ui/dmabuf.h +++ b/include/ui/dmabuf.h @@ -10,24 +10,7 @@ #ifndef DMABUF_H #define DMABUF_H -typedef struct QemuDmaBuf { - int fd; - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t fourcc; - uint64_t modifier; - uint32_t texture; - uint32_t x; - uint32_t y; - uint32_t backing_width; - uint32_t backing_height; - bool y0_top; - void *sync; - int fence_fd; - bool allow_fences; - bool draw_submitted; -} QemuDmaBuf; +typedef struct QemuDmaBuf QemuDmaBuf; QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, uint32_t stride, uint32_t x, diff --git a/ui/dmabuf.c b/ui/dmabuf.c index e047d5ca26..df7a09703f 100644 --- a/ui/dmabuf.c +++ b/ui/dmabuf.c @@ -10,6 +10,25 @@ #include "qemu/osdep.h" #include "ui/dmabuf.h" +struct QemuDmaBuf { + int fd; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t fourcc; + uint64_t modifier; + uint32_t texture; + uint32_t x; + uint32_t y; + uint32_t backing_width; + uint32_t backing_height; + bool y0_top; + void *sync; + int fence_fd; + bool allow_fences; + bool draw_submitted; +}; + QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, uint32_t stride, uint32_t x, uint32_t y, uint32_t backing_width, From 36b8e6b4e17cb1e45c902689d1366fa93571bfbd Mon Sep 17 00:00:00 2001 From: hikalium Date: Sun, 12 May 2024 20:14:34 +0900 Subject: [PATCH 0627/2884] ui/gtk: Add gd_motion_event trace event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add gd_motion_event trace event for making it easy to debug gd_motion_event related issues. Signed-off-by: hikalium Reviewed-by: Marc-André Lureau Message-Id: <20240512111435.30121-2-hikalium@hikalium.com> --- ui/gtk.c | 2 ++ ui/trace-events | 1 + 2 files changed, 3 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index 3a6832eb1b..7ce73b0798 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -915,6 +915,8 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, x = (motion->x - mx) / vc->gfx.scale_x * ws; y = (motion->y - my) / vc->gfx.scale_y * ws; + trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y); + if (qemu_input_is_absolute(vc->gfx.dcl.con)) { if (x < 0 || y < 0 || x >= surface_width(vc->gfx.ds) || diff --git a/ui/trace-events b/ui/trace-events index e6a2894303..69ff22955d 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -28,6 +28,7 @@ gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s" gd_keymap_windowing(const char *name) "backend=%s" gd_gl_area_create_context(void *ctx, int major, int minor) "ctx=%p, major=%d, minor=%d" gd_gl_area_destroy_context(void *ctx, void *current_ctx) "ctx=%p, current_ctx=%p" +gd_motion_event(int ww, int wh, int ws, int x, int y) "ww=%d, wh=%d, ws=%d, x=%d, y=%d" # vnc-auth-sasl.c # vnc-auth-vencrypt.c From 37e91415018db3656b46cdea8f9e4d47b3ff130d Mon Sep 17 00:00:00 2001 From: hikalium Date: Sun, 12 May 2024 20:14:35 +0900 Subject: [PATCH 0628/2884] ui/gtk: Fix mouse/motion event scaling issue with GTK display backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove gtk_widget_get_scale_factor() usage from the calculation of the motion events in the GTK backend to make it work correctly on environments that have `gtk_widget_get_scale_factor() != 1`. This scale factor usage had been introduced in the commit f14aab420c and at that time the window size was used for calculating the things and it was working correctly. However, in the commit 2f31663ed4 the logic switched to use the widget size instead of window size and because of the change the usage of scale factor becomes invalid (since widgets use `vc->gfx.scale_{x, y}` for scaling). Tested on Crostini on ChromeOS (15823.51.0) with an external display. Fixes: 2f31663ed4 ("ui/gtk: use widget size for cursor motion event") Fixes: f14aab420c ("ui: fix incorrect pointer position on highdpi with gtk") Signed-off-by: hikalium Acked-by: Marc-André Lureau Message-Id: <20240512111435.30121-3-hikalium@hikalium.com> --- ui/gtk.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 7ce73b0798..93b13b7a30 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -891,7 +891,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, int x, y; int mx, my; int fbh, fbw; - int ww, wh, ws; + int ww, wh; if (!vc->gfx.ds) { return TRUE; @@ -899,11 +899,15 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - ww = gtk_widget_get_allocated_width(widget); wh = gtk_widget_get_allocated_height(widget); - ws = gtk_widget_get_scale_factor(widget); + /* + * `widget` may not have the same size with the frame buffer. + * In such cases, some paddings are needed around the `vc`. + * To achieve that, `vc` will be displayed at (mx, my) + * so that it is displayed at the center of the widget. + */ mx = my = 0; if (ww > fbw) { mx = (ww - fbw) / 2; @@ -912,8 +916,12 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, my = (wh - fbh) / 2; } - x = (motion->x - mx) / vc->gfx.scale_x * ws; - y = (motion->y - my) / vc->gfx.scale_y * ws; + /* + * `motion` is reported in `widget` coordinates + * so translating it to the coordinates in `vc`. + */ + x = (motion->x - mx) / vc->gfx.scale_x; + y = (motion->y - my) / vc->gfx.scale_y; trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y); From 2e701e6785cd8cc048c608751c6e4f6253c67ab6 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 12 May 2024 11:59:45 +0200 Subject: [PATCH 0629/2884] ui/sdl2: Allow host to power down screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, SDL disables the screen saver which prevents the host from powering down the screen even if the screen is locked. This results in draining the battery needlessly when the host isn't connected to a wall charger. Fix that by enabling the screen saver. Signed-off-by: Bernhard Beschow Acked-by: Marc-André Lureau Message-ID: <20240512095945.1879-1-shentey@gmail.com> --- ui/sdl2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/sdl2.c b/ui/sdl2.c index 4971963f00..0a0eb5a42d 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -874,6 +874,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); #endif SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1"); + SDL_EnableScreenSaver(); memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); From 80189472303e0209deb1c483915df87c0a8310bf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 15:13:58 -1000 Subject: [PATCH 0630/2884] accel/tcg: Use vaddr in translator_ld* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 15 ++++++++------- include/exec/translator.h | 21 +++++++++------------ target/hexagon/translate.c | 1 + target/microblaze/translate.c | 1 + 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 6832e55135..53225290b1 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -14,6 +14,7 @@ #include "exec/translator.h" #include "exec/cpu_ldst.h" #include "exec/plugin-gen.h" +#include "exec/cpu_ldst.h" #include "tcg/tcg-op-common.h" #include "internal-target.h" @@ -294,11 +295,11 @@ static void *translator_access(CPUArchState *env, DisasContextBase *db, return host + (pc - base); } -static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) +static void plugin_insn_append(vaddr pc, const void *from, size_t size) { #ifdef CONFIG_PLUGIN struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; - abi_ptr off; + size_t off; if (insn == NULL) { return; @@ -315,7 +316,7 @@ static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) #endif } -uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) { uint8_t ret; void *p = translator_access(env, db, pc, sizeof(ret)); @@ -329,7 +330,7 @@ uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) return ret; } -uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) { uint16_t ret, plug; void *p = translator_access(env, db, pc, sizeof(ret)); @@ -344,7 +345,7 @@ uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) return ret; } -uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) { uint32_t ret, plug; void *p = translator_access(env, db, pc, sizeof(ret)); @@ -359,7 +360,7 @@ uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) return ret; } -uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) { uint64_t ret, plug; void *p = translator_access(env, db, pc, sizeof(ret)); @@ -374,7 +375,7 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) return ret; } -void translator_fake_ldb(uint8_t insn8, abi_ptr pc) +void translator_fake_ldb(uint8_t insn8, vaddr pc) { plugin_insn_append(pc, &insn8, sizeof(insn8)); } diff --git a/include/exec/translator.h b/include/exec/translator.h index 6cd937ac5c..51489c181c 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -19,10 +19,7 @@ */ #include "qemu/bswap.h" -#include "exec/cpu-common.h" -#include "exec/cpu-defs.h" -#include "exec/abi_ptr.h" -#include "cpu.h" +#include "exec/vaddr.h" /** * gen_intermediate_code @@ -185,14 +182,14 @@ bool translator_io_start(DisasContextBase *db); * the relevant information at translation time. */ -uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc); -uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc); -uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc); -uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc); +uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc); static inline uint16_t translator_lduw_swap(CPUArchState *env, DisasContextBase *db, - abi_ptr pc, bool do_swap) + vaddr pc, bool do_swap) { uint16_t ret = translator_lduw(env, db, pc); if (do_swap) { @@ -203,7 +200,7 @@ translator_lduw_swap(CPUArchState *env, DisasContextBase *db, static inline uint32_t translator_ldl_swap(CPUArchState *env, DisasContextBase *db, - abi_ptr pc, bool do_swap) + vaddr pc, bool do_swap) { uint32_t ret = translator_ldl(env, db, pc); if (do_swap) { @@ -214,7 +211,7 @@ translator_ldl_swap(CPUArchState *env, DisasContextBase *db, static inline uint64_t translator_ldq_swap(CPUArchState *env, DisasContextBase *db, - abi_ptr pc, bool do_swap) + vaddr pc, bool do_swap) { uint64_t ret = translator_ldq(env, db, pc); if (do_swap) { @@ -233,7 +230,7 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, * re-synthesised for s390x "ex"). It ensures we update other areas of * the translator with details of the executed instruction. */ -void translator_fake_ldb(uint8_t insn8, abi_ptr pc); +void translator_fake_ldb(uint8_t insn8, vaddr pc); /* * Return whether addr is on the same page as where disassembly started. diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 0904dc2d38..fcba82f7dc 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -25,6 +25,7 @@ #include "exec/translation-block.h" #include "exec/cpu_ldst.h" #include "exec/log.h" +#include "exec/cpu_ldst.h" #include "internal.h" #include "attribs.h" #include "insn.h" diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 6d89c1a175..84cca04962 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -26,6 +26,7 @@ #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "qemu/qemu-print.h" From 66f3b79e85149f42465a580c2530638e27c4a4bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 15:56:39 -1000 Subject: [PATCH 0631/2884] accel/tcg: Hide in_same_page outside of a target-specific context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While there are other methods that could be used to replace TARGET_PAGE_MASK, the function is not really required outside the context of target-specific translation. This makes the header usable by target independent code. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/translator.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/exec/translator.h b/include/exec/translator.h index 51489c181c..212362f5a0 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -232,6 +232,7 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, */ void translator_fake_ldb(uint8_t insn8, vaddr pc); +#ifdef COMPILING_PER_TARGET /* * Return whether addr is on the same page as where disassembly started. * Translators can use this to enforce the rule that only single-insn @@ -241,5 +242,6 @@ static inline bool is_same_page(const DisasContextBase *db, vaddr addr) { return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; } +#endif #endif /* EXEC__TRANSLATOR_H */ From 99977aefd07d85da791e0d851ba2d10d9d5c3094 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 15:27:14 -1000 Subject: [PATCH 0632/2884] accel/tcg: Pass DisasContextBase to translator_fake_ldb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 2 +- include/exec/translator.h | 5 +++-- target/s390x/tcg/translate.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 53225290b1..3456455fa5 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -375,7 +375,7 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) return ret; } -void translator_fake_ldb(uint8_t insn8, vaddr pc) +void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8) { plugin_insn_append(pc, &insn8, sizeof(insn8)); } diff --git a/include/exec/translator.h b/include/exec/translator.h index 212362f5a0..348985c3a3 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -222,15 +222,16 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, /** * translator_fake_ldb - fake instruction load - * @insn8: byte of instruction + * @db: Disassembly context * @pc: program counter of instruction + * @insn8: byte of instruction * * This is a special case helper used where the instruction we are * about to translate comes from somewhere else (e.g. being * re-synthesised for s390x "ex"). It ensures we update other areas of * the translator with details of the executed instruction. */ -void translator_fake_ldb(uint8_t insn8, vaddr pc); +void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8); #ifdef COMPILING_PER_TARGET /* diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 90a74ee795..6d7f6e7064 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6203,7 +6203,7 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) /* Register insn bytes with translator so plugins work. */ for (int i = 0; i < ilen; i++) { uint8_t byte = extract64(insn, 56 - (i * 8), 8); - translator_fake_ldb(byte, pc + i); + translator_fake_ldb(&s->base, pc + i, byte); } op = insn >> 56; } else { From 04f8ed114545f01a2fee1121e862e270cdd46741 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 18:26:33 -1000 Subject: [PATCH 0633/2884] accel/tcg: Reorg translator_ld* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorg translator_access into translator_ld, with a more memcpy-ish interface. If both pages are in ram, do not go through the caller's slow path. Assert that the access is within the two pages that we are prepared to protect, per TranslationBlock. Allow access prior to pc_first, so long as it is within the first page. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 189 ++++++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 88 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 3456455fa5..0848026935 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -230,69 +230,88 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, } } -static void *translator_access(CPUArchState *env, DisasContextBase *db, - vaddr pc, size_t len) +static bool translator_ld(CPUArchState *env, DisasContextBase *db, + void *dest, vaddr pc, size_t len) { + TranslationBlock *tb = db->tb; + vaddr last = pc + len - 1; void *host; - vaddr base, end; - TranslationBlock *tb; - - tb = db->tb; + vaddr base; /* Use slow path if first page is MMIO. */ if (unlikely(tb_page_addr0(tb) == -1)) { - return NULL; + return false; } - end = pc + len - 1; - if (likely(is_same_page(db, end))) { - host = db->host_addr[0]; - base = db->pc_first; - } else { + host = db->host_addr[0]; + base = db->pc_first; + + if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) { + /* Entire read is from the first page. */ + memcpy(dest, host + (pc - base), len); + return true; + } + + if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) { + /* Read begins on the first page and extends to the second. */ + size_t len0 = -(pc | TARGET_PAGE_MASK); + memcpy(dest, host + (pc - base), len0); + pc += len0; + dest += len0; + len -= len0; + } + + /* + * The read must conclude on the second page and not extend to a third. + * + * TODO: We could allow the two pages to be virtually discontiguous, + * since we already allow the two pages to be physically discontiguous. + * The only reasonable use case would be executing an insn at the end + * of the address space wrapping around to the beginning. For that, + * we would need to know the current width of the address space. + * In the meantime, assert. + */ + base = (base & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + assert(((base ^ pc) & TARGET_PAGE_MASK) == 0); + assert(((base ^ last) & TARGET_PAGE_MASK) == 0); + host = db->host_addr[1]; + + if (host == NULL) { + tb_page_addr_t page0, old_page1, new_page1; + + new_page1 = get_page_addr_code_hostp(env, base, &db->host_addr[1]); + + /* + * If the second page is MMIO, treat as if the first page + * was MMIO as well, so that we do not cache the TB. + */ + if (unlikely(new_page1 == -1)) { + tb_unlock_pages(tb); + tb_set_page_addr0(tb, -1); + return false; + } + + /* + * If this is not the first time around, and page1 matches, + * then we already have the page locked. Alternately, we're + * not doing anything to prevent the PTE from changing, so + * we might wind up with a different page, requiring us to + * re-do the locking. + */ + old_page1 = tb_page_addr1(tb); + if (likely(new_page1 != old_page1)) { + page0 = tb_page_addr0(tb); + if (unlikely(old_page1 != -1)) { + tb_unlock_page1(page0, old_page1); + } + tb_set_page_addr1(tb, new_page1); + tb_lock_page1(page0, new_page1); + } host = db->host_addr[1]; - base = TARGET_PAGE_ALIGN(db->pc_first); - if (host == NULL) { - tb_page_addr_t page0, old_page1, new_page1; - - new_page1 = get_page_addr_code_hostp(env, base, &db->host_addr[1]); - - /* - * If the second page is MMIO, treat as if the first page - * was MMIO as well, so that we do not cache the TB. - */ - if (unlikely(new_page1 == -1)) { - tb_unlock_pages(tb); - tb_set_page_addr0(tb, -1); - return NULL; - } - - /* - * If this is not the first time around, and page1 matches, - * then we already have the page locked. Alternately, we're - * not doing anything to prevent the PTE from changing, so - * we might wind up with a different page, requiring us to - * re-do the locking. - */ - old_page1 = tb_page_addr1(tb); - if (likely(new_page1 != old_page1)) { - page0 = tb_page_addr0(tb); - if (unlikely(old_page1 != -1)) { - tb_unlock_page1(page0, old_page1); - } - tb_set_page_addr1(tb, new_page1); - tb_lock_page1(page0, new_page1); - } - host = db->host_addr[1]; - } - - /* Use slow path when crossing pages. */ - if (is_same_page(db, pc)) { - return NULL; - } } - tcg_debug_assert(pc >= base); - return host + (pc - base); + memcpy(dest, host + (pc - base), len); + return true; } static void plugin_insn_append(vaddr pc, const void *from, size_t size) @@ -318,61 +337,55 @@ static void plugin_insn_append(vaddr pc, const void *from, size_t size) uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint8_t ret; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint8_t raw; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldub_p(p); + if (!translator_ld(env, db, &raw, pc, sizeof(raw))) { + raw = cpu_ldub_code(env, pc); } - ret = cpu_ldub_code(env, pc); - plugin_insn_append(pc, &ret, sizeof(ret)); - return ret; + plugin_insn_append(pc, &raw, sizeof(raw)); + return raw; } uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint16_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint16_t raw, tgt; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return lduw_p(p); + if (translator_ld(env, db, &raw, pc, sizeof(raw))) { + tgt = tswap16(raw); + } else { + tgt = cpu_lduw_code(env, pc); + raw = tswap16(tgt); } - ret = cpu_lduw_code(env, pc); - plug = tswap16(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + plugin_insn_append(pc, &raw, sizeof(raw)); + return tgt; } uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint32_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint32_t raw, tgt; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldl_p(p); + if (translator_ld(env, db, &raw, pc, sizeof(raw))) { + tgt = tswap32(raw); + } else { + tgt = cpu_ldl_code(env, pc); + raw = tswap32(tgt); } - ret = cpu_ldl_code(env, pc); - plug = tswap32(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + plugin_insn_append(pc, &raw, sizeof(raw)); + return tgt; } uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint64_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint64_t raw, tgt; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldq_p(p); + if (translator_ld(env, db, &raw, pc, sizeof(raw))) { + tgt = tswap64(raw); + } else { + tgt = cpu_ldq_code(env, pc); + raw = tswap64(tgt); } - ret = cpu_ldq_code(env, pc); - plug = tswap64(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + plugin_insn_append(pc, &raw, sizeof(raw)); + return tgt; } void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8) From ddfa9f11766c532209d4ce848c12761d28730338 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 19:28:20 -1000 Subject: [PATCH 0634/2884] accel/tcg: Cap the translation block when we encounter mmio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not allow translation to proceed beyond one insn with mmio, as we will not be caching the TranslationBlock. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 0848026935..18138c61e1 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -240,6 +240,8 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db, /* Use slow path if first page is MMIO. */ if (unlikely(tb_page_addr0(tb) == -1)) { + /* We capped translation with first page MMIO in tb_gen_code. */ + tcg_debug_assert(db->max_insns == 1); return false; } @@ -288,6 +290,8 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db, if (unlikely(new_page1 == -1)) { tb_unlock_pages(tb); tb_set_page_addr0(tb, -1); + /* Require that this be the final insn. */ + db->max_insns = db->num_insns; return false; } From ba3fb2a735963cf3988ef9476d134a8418baa436 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 19:46:33 -1000 Subject: [PATCH 0635/2884] accel/tcg: Record mmio bytes during translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be able to replace plugin_insn_append, and will be usable for disassembly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 41 +++++++++++++++++++++++++++++++++++++++ include/exec/translator.h | 12 ++++++++++++ 2 files changed, 53 insertions(+) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 18138c61e1..86a1fe17a0 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -132,6 +132,8 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, db->insn_start = NULL; db->host_addr[0] = host_pc; db->host_addr[1] = NULL; + db->record_start = 0; + db->record_len = 0; ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ @@ -318,6 +320,39 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db, return true; } +static void record_save(DisasContextBase *db, vaddr pc, + const void *from, int size) +{ + int offset; + + /* Do not record probes before the start of TB. */ + if (pc < db->pc_first) { + return; + } + + /* + * In translator_access, we verified that pc is within 2 pages + * of pc_first, thus this will never overflow. + */ + offset = pc - db->pc_first; + + /* + * Either the first or second page may be I/O. If it is the second, + * then the first byte we need to record will be at a non-zero offset. + * In either case, we should not need to record but a single insn. + */ + if (db->record_len == 0) { + db->record_start = offset; + db->record_len = size; + } else { + assert(offset == db->record_start + db->record_len); + assert(db->record_len + size <= sizeof(db->record)); + db->record_len += size; + } + + memcpy(db->record + (offset - db->record_start), from, size); +} + static void plugin_insn_append(vaddr pc, const void *from, size_t size) { #ifdef CONFIG_PLUGIN @@ -345,6 +380,7 @@ uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) if (!translator_ld(env, db, &raw, pc, sizeof(raw))) { raw = cpu_ldub_code(env, pc); + record_save(db, pc, &raw, sizeof(raw)); } plugin_insn_append(pc, &raw, sizeof(raw)); return raw; @@ -359,6 +395,7 @@ uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) } else { tgt = cpu_lduw_code(env, pc); raw = tswap16(tgt); + record_save(db, pc, &raw, sizeof(raw)); } plugin_insn_append(pc, &raw, sizeof(raw)); return tgt; @@ -373,6 +410,7 @@ uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) } else { tgt = cpu_ldl_code(env, pc); raw = tswap32(tgt); + record_save(db, pc, &raw, sizeof(raw)); } plugin_insn_append(pc, &raw, sizeof(raw)); return tgt; @@ -387,6 +425,7 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) } else { tgt = cpu_ldq_code(env, pc); raw = tswap64(tgt); + record_save(db, pc, &raw, sizeof(raw)); } plugin_insn_append(pc, &raw, sizeof(raw)); return tgt; @@ -394,5 +433,7 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8) { + assert(pc >= db->pc_first); + record_save(db, pc, &insn8, sizeof(insn8)); plugin_insn_append(pc, &insn8, sizeof(insn8)); } diff --git a/include/exec/translator.h b/include/exec/translator.h index 348985c3a3..4a86907ecc 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -90,6 +90,18 @@ typedef struct DisasContextBase { bool plugin_enabled; struct TCGOp *insn_start; void *host_addr[2]; + + /* + * Record insn data that we cannot read directly from host memory. + * There are only two reasons we cannot use host memory: + * (1) We are executing from I/O, + * (2) We are executing a synthetic instruction (s390x EX). + * In both cases we need record exactly one instruction, + * and thus the maximum amount of data we record is limited. + */ + int record_start; + int record_len; + uint8_t record[32]; } DisasContextBase; /** From b3f05b8ce24a2dae2e37bd6e7910f19652848656 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 08:26:18 -1000 Subject: [PATCH 0636/2884] accel/tcg: Record when translator_fake_ldb is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove left-over comment from commit dcd092a063 ("accel/tcg: Improve can_do_io management"). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 2 ++ include/exec/translator.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 86a1fe17a0..aa9f36aaa0 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -130,6 +130,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, db->max_insns = *max_insns; db->singlestep_enabled = cflags & CF_SINGLE_STEP; db->insn_start = NULL; + db->fake_insn = false; db->host_addr[0] = host_pc; db->host_addr[1] = NULL; db->record_start = 0; @@ -434,6 +435,7 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8) { assert(pc >= db->pc_first); + db->fake_insn = true; record_save(db, pc, &insn8, sizeof(insn8)); plugin_insn_append(pc, &insn8, sizeof(insn8)); } diff --git a/include/exec/translator.h b/include/exec/translator.h index 4a86907ecc..70cef2c0be 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -72,8 +72,8 @@ typedef enum DisasJumpType { * @num_insns: Number of translated instructions (including current). * @max_insns: Maximum number of instructions to be translated in this TB. * @singlestep_enabled: "Hardware" single stepping enabled. - * @saved_can_do_io: Known value of cpu->neg.can_do_io, or -1 for unknown. * @plugin_enabled: TCG plugin enabled in this TB. + * @fake_insn: True if translator_fake_ldb used. * @insn_start: The last op emitted by the insn_start hook, * which is expected to be INDEX_op_insn_start. * @@ -88,6 +88,7 @@ typedef struct DisasContextBase { int max_insns; bool singlestep_enabled; bool plugin_enabled; + bool fake_insn; struct TCGOp *insn_start; void *host_addr[2]; From bf4bb074b72a7dc6c9ee5379a1d6b693dcd0a5a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 19:59:49 -1000 Subject: [PATCH 0637/2884] accel/tcg: Record DisasContextBase in tcg_ctx for plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 1 + include/tcg/tcg.h | 1 + 2 files changed, 2 insertions(+) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 49f5d1c2e4..842da97204 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -329,6 +329,7 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); } + tcg_ctx->plugin_db = db; tcg_ctx->plugin_insn = NULL; return ret; diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 135e36d729..2a1c080bab 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -537,6 +537,7 @@ struct TCGContext { * space for instructions (for variable-instruction-length ISAs). */ struct qemu_plugin_tb *plugin_tb; + const struct DisasContextBase *plugin_db; /* descriptor of the instruction being translated */ struct qemu_plugin_insn *plugin_insn; From 4abc892362f8282450f18c4e45c5b0534461d01e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 21:24:52 -1000 Subject: [PATCH 0638/2884] plugins: Copy memory in qemu_plugin_insn_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of returning a host pointer, copy the data into storage provided by the caller. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- contrib/plugins/execlog.c | 5 +++-- contrib/plugins/howvec.c | 4 ++-- include/qemu/qemu-plugin.h | 15 +++++++-------- plugins/api.c | 7 +++++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index fab18113d4..371db97eb1 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -258,8 +258,9 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) NULL); } } else { - uint32_t insn_opcode; - insn_opcode = *((uint32_t *)qemu_plugin_insn_data(insn)); + uint32_t insn_opcode = 0; + qemu_plugin_insn_data(insn, &insn_opcode, sizeof(insn_opcode)); + char *output = g_strdup_printf("0x%"PRIx64", 0x%"PRIx32", \"%s\"", insn_vaddr, insn_opcode, insn_disas); diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 94bbc53820..9be67f7453 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -252,7 +252,7 @@ static struct qemu_plugin_scoreboard *find_counter( { int i; uint64_t *cnt = NULL; - uint32_t opcode; + uint32_t opcode = 0; InsnClassExecCount *class = NULL; /* @@ -261,7 +261,7 @@ static struct qemu_plugin_scoreboard *find_counter( * They would probably benefit from a more tailored plugin. * However we can fall back to individual instruction counting. */ - opcode = *((uint32_t *)qemu_plugin_insn_data(insn)); + qemu_plugin_insn_data(insn, &opcode, sizeof(opcode)); for (i = 0; !cnt && i < class_table_sz; i++) { class = &class_table[i]; diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 4fc6c3739b..5f36c2d1ac 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -61,7 +61,7 @@ typedef uint64_t qemu_plugin_id_t; extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; -#define QEMU_PLUGIN_VERSION 2 +#define QEMU_PLUGIN_VERSION 3 /** * struct qemu_info_t - system information for plugins @@ -394,17 +394,16 @@ struct qemu_plugin_insn * qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx); /** - * qemu_plugin_insn_data() - return ptr to instruction data + * qemu_plugin_insn_data() - copy instruction data * @insn: opaque instruction handle from qemu_plugin_tb_get_insn() + * @dest: destination into which data is copied + * @len: length of dest * - * Note: data is only valid for duration of callback. See - * qemu_plugin_insn_size() to calculate size of stream. - * - * Returns: pointer to a stream of bytes containing the value of this - * instructions opcode. + * Returns the number of bytes copied, minimum of @len and insn size. */ QEMU_PLUGIN_API -const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn); +size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, + void *dest, size_t len); /** * qemu_plugin_insn_size() - return size of instruction diff --git a/plugins/api.c b/plugins/api.c index 2144da1fe8..5ff4e9d325 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -216,9 +216,12 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) * instruction being translated. */ -const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) +size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, + void *dest, size_t len) { - return insn->data->data; + len = MIN(len, insn->data->len); + memcpy(dest, insn->data->data, len); + return len; } size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) From 3a247368e6fc12304e73451c63254313e2ebd60e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 22:10:50 -1000 Subject: [PATCH 0639/2884] accel/tcg: Implement translator_st MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy data out of a completed translation. This will be used for both plugins and disassembly. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 55 +++++++++++++++++++++++++++++++++++++++ include/exec/translator.h | 23 ++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index aa9f36aaa0..01e1f0977b 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -354,6 +354,61 @@ static void record_save(DisasContextBase *db, vaddr pc, memcpy(db->record + (offset - db->record_start), from, size); } +size_t translator_st_len(const DisasContextBase *db) +{ + return db->fake_insn ? db->record_len : db->tb->size; +} + +bool translator_st(const DisasContextBase *db, void *dest, + vaddr addr, size_t len) +{ + size_t offset, offset_end; + + if (addr < db->pc_first) { + return false; + } + offset = addr - db->pc_first; + offset_end = offset + len; + if (offset_end > translator_st_len(db)) { + return false; + } + + if (!db->fake_insn) { + size_t offset_page1 = -(db->pc_first | TARGET_PAGE_MASK); + + /* Get all the bytes from the first page. */ + if (db->host_addr[0]) { + if (offset_end <= offset_page1) { + memcpy(dest, db->host_addr[0] + offset, len); + return true; + } + if (offset < offset_page1) { + size_t len0 = offset_page1 - offset; + memcpy(dest, db->host_addr[0] + offset, len0); + offset += len0; + dest += len0; + } + } + + /* Get any bytes from the second page. */ + if (db->host_addr[1] && offset >= offset_page1) { + memcpy(dest, db->host_addr[1] + (offset - offset_page1), + offset_end - offset); + return true; + } + } + + /* Else get recorded bytes. */ + if (db->record_len != 0 && + offset >= db->record_start && + offset_end <= db->record_start + db->record_len) { + memcpy(dest, db->record + (offset - db->record_start), + offset_end - offset); + return true; + } + return false; +} + static void plugin_insn_append(vaddr pc, const void *from, size_t size) { #ifdef CONFIG_PLUGIN diff --git a/include/exec/translator.h b/include/exec/translator.h index 70cef2c0be..fff857a0cc 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -246,6 +246,29 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, */ void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8); +/** + * translator_st + * @db: disassembly context + * @dest: address to copy into + * @addr: virtual address within TB + * @len: length + * + * Copy @len bytes from @addr into @dest. + * All bytes must have been read during translation. + * Return true on success or false on failure. + */ +bool translator_st(const DisasContextBase *db, void *dest, + vaddr addr, size_t len); + +/** + * translator_st_len + * @db: disassembly context + * + * Return the number of bytes available to copy from the + * current translation block with translator_st. + */ +size_t translator_st_len(const DisasContextBase *db); + #ifdef COMPILING_PER_TARGET /* * Return whether addr is on the same page as where disassembly started. From 36bc99bc789fd564facea93feb2a22e4942b84d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 09:20:06 -1000 Subject: [PATCH 0640/2884] plugins: Use translator_st for qemu_plugin_insn_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the bytes that we record for the entire TB, rather than a per-insn GByteArray. Record the length of the insn in plugin_gen_insn_end rather than infering from the length of the array. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 7 +++++-- accel/tcg/translator.c | 26 -------------------------- include/qemu/plugin.h | 14 +------------- plugins/api.c | 12 +++++++----- tcg/tcg.c | 3 +-- 5 files changed, 14 insertions(+), 48 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 842da97204..716c8ec753 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -346,11 +346,9 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) ptb->n = n; if (n <= ptb->insns->len) { insn = g_ptr_array_index(ptb->insns, n - 1); - g_byte_array_set_size(insn->data, 0); } else { assert(n - 1 == ptb->insns->len); insn = g_new0(struct qemu_plugin_insn, 1); - insn->data = g_byte_array_sized_new(4); g_ptr_array_add(ptb->insns, insn); } @@ -389,6 +387,11 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) void plugin_gen_insn_end(void) { + const DisasContextBase *db = tcg_ctx->plugin_db; + struct qemu_plugin_insn *pinsn = tcg_ctx->plugin_insn; + + pinsn->len = db->fake_insn ? db->record_len : db->pc_next - pinsn->vaddr; + tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_INSN); } diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 01e1f0977b..986045154c 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -409,27 +409,6 @@ bool translator_st(const DisasContextBase *db, void *dest, return false; } -static void plugin_insn_append(vaddr pc, const void *from, size_t size) -{ -#ifdef CONFIG_PLUGIN - struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; - size_t off; - - if (insn == NULL) { - return; - } - off = pc - insn->vaddr; - if (off < insn->data->len) { - g_byte_array_set_size(insn->data, off); - } else if (off > insn->data->len) { - /* we have an unexpected gap */ - g_assert_not_reached(); - } - - insn->data = g_byte_array_append(insn->data, from, size); -#endif -} - uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) { uint8_t raw; @@ -438,7 +417,6 @@ uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) raw = cpu_ldub_code(env, pc); record_save(db, pc, &raw, sizeof(raw)); } - plugin_insn_append(pc, &raw, sizeof(raw)); return raw; } @@ -453,7 +431,6 @@ uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) raw = tswap16(tgt); record_save(db, pc, &raw, sizeof(raw)); } - plugin_insn_append(pc, &raw, sizeof(raw)); return tgt; } @@ -468,7 +445,6 @@ uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) raw = tswap32(tgt); record_save(db, pc, &raw, sizeof(raw)); } - plugin_insn_append(pc, &raw, sizeof(raw)); return tgt; } @@ -483,7 +459,6 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) raw = tswap64(tgt); record_save(db, pc, &raw, sizeof(raw)); } - plugin_insn_append(pc, &raw, sizeof(raw)); return tgt; } @@ -492,5 +467,4 @@ void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8) assert(pc >= db->pc_first); db->fake_insn = true; record_save(db, pc, &insn8, sizeof(insn8)); - plugin_insn_append(pc, &insn8, sizeof(insn8)); } diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index b535bfd5de..c8dd2c42fa 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -98,11 +98,11 @@ struct qemu_plugin_dyn_cb { /* Internal context for instrumenting an instruction */ struct qemu_plugin_insn { - GByteArray *data; uint64_t vaddr; void *haddr; GArray *insn_cbs; GArray *mem_cbs; + uint8_t len; bool calls_helpers; /* if set, the instruction calls helpers that might access guest memory */ @@ -117,18 +117,6 @@ struct qemu_plugin_scoreboard { QLIST_ENTRY(qemu_plugin_scoreboard) entry; }; -/* - * qemu_plugin_insn allocate and cleanup functions. We don't expect to - * cleanup many of these structures. They are reused for each fresh - * translation. - */ - -static inline void qemu_plugin_insn_cleanup_fn(gpointer data) -{ - struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data; - g_byte_array_free(insn->data, true); -} - /* Internal context for this TranslationBlock */ struct qemu_plugin_tb { GPtrArray *insns; diff --git a/plugins/api.c b/plugins/api.c index 5ff4e9d325..15467acdfd 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -42,6 +42,7 @@ #include "tcg/tcg.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" +#include "exec/translator.h" #include "disas/disas.h" #include "plugin.h" #ifndef CONFIG_USER_ONLY @@ -219,14 +220,15 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, void *dest, size_t len) { - len = MIN(len, insn->data->len); - memcpy(dest, insn->data->data, len); - return len; + const DisasContextBase *db = tcg_ctx->plugin_db; + + len = MIN(len, insn->len); + return translator_st(db, dest, insn->vaddr, len) ? len : 0; } size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) { - return insn->data->len; + return insn->len; } uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) @@ -242,7 +244,7 @@ void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) { CPUState *cpu = current_cpu; - return plugin_disas(cpu, insn->vaddr, insn->data->len); + return plugin_disas(cpu, insn->vaddr, insn->len); } const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) diff --git a/tcg/tcg.c b/tcg/tcg.c index d827c6d431..71daa5d268 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -765,8 +765,7 @@ static void alloc_tcg_plugin_context(TCGContext *s) { #ifdef CONFIG_PLUGIN s->plugin_tb = g_new0(struct qemu_plugin_tb, 1); - s->plugin_tb->insns = - g_ptr_array_new_with_free_func(qemu_plugin_insn_cleanup_fn); + s->plugin_tb->insns = g_ptr_array_new(); #endif } From e501325991815e09297a048ffb0be81411bbe34a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 09:31:49 -1000 Subject: [PATCH 0641/2884] plugins: Read mem_only directly from TB cflags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not pass around a boolean between multiple structures, just read it from the TranslationBlock in the TCGContext. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 4 +--- accel/tcg/translator.c | 2 +- include/exec/plugin-gen.h | 7 +++---- include/qemu/plugin.h | 3 --- plugins/api.c | 14 +++++++++----- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 716c8ec753..2aa1e08c17 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -303,8 +303,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) } } -bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, - bool mem_only) +bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db) { bool ret = false; @@ -323,7 +322,6 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, ptb->vaddr2 = -1; ptb->haddr1 = db->host_addr[0]; ptb->haddr2 = NULL; - ptb->mem_only = mem_only; ptb->mem_helper = false; tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 986045154c..157b447810 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -144,7 +144,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, ops->tb_start(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ - plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); + plugin_enabled = plugin_gen_tb_start(cpu, db); db->plugin_enabled = plugin_enabled; while (true) { diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index f333f33198..cbb2ca2131 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -18,8 +18,7 @@ struct DisasContextBase; #ifdef CONFIG_PLUGIN -bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, - bool supress); +bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db); void plugin_gen_tb_end(CPUState *cpu, size_t num_insns); void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db); void plugin_gen_insn_end(void); @@ -28,8 +27,8 @@ void plugin_gen_disable_mem_helpers(void); #else /* !CONFIG_PLUGIN */ -static inline bool -plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, bool sup) +static inline +bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db) { return false; } diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index c8dd2c42fa..c28d0ca31c 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -107,8 +107,6 @@ struct qemu_plugin_insn { /* if set, the instruction calls helpers that might access guest memory */ bool mem_helper; - - bool mem_only; }; /* A scoreboard is an array of values, indexed by vcpu_index */ @@ -125,7 +123,6 @@ struct qemu_plugin_tb { uint64_t vaddr2; void *haddr1; void *haddr2; - bool mem_only; /* if set, the TB calls helpers that might access guest memory */ bool mem_helper; diff --git a/plugins/api.c b/plugins/api.c index 15467acdfd..9e4aa9d2d9 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -87,12 +87,17 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); } +static bool tb_is_mem_only(void) +{ + return tb_cflags(tcg_ctx->gen_tb) & CF_MEMI_ONLY; +} + void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, void *udata) { - if (!tb->mem_only) { + if (!tb_is_mem_only()) { plugin_register_dyn_cb__udata(&tb->cbs, cb, flags, udata); } } @@ -103,7 +108,7 @@ void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( qemu_plugin_u64 entry, uint64_t imm) { - if (!tb->mem_only) { + if (!tb_is_mem_only()) { plugin_register_inline_op_on_entry(&tb->cbs, 0, op, entry, imm); } } @@ -113,7 +118,7 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_cb_flags flags, void *udata) { - if (!insn->mem_only) { + if (!tb_is_mem_only()) { plugin_register_dyn_cb__udata(&insn->insn_cbs, cb, flags, udata); } } @@ -124,7 +129,7 @@ void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( qemu_plugin_u64 entry, uint64_t imm) { - if (!insn->mem_only) { + if (!tb_is_mem_only()) { plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm); } } @@ -206,7 +211,6 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) return NULL; } insn = g_ptr_array_index(tb->insns, idx); - insn->mem_only = tb->mem_only; return insn; } From d3ace105900c43d1eb034b81ce0951e6110ab990 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 09:54:43 -1000 Subject: [PATCH 0642/2884] plugins: Use DisasContextBase for qemu_plugin_insn_haddr We can delay the computation of haddr until the plugin actually requests it. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 20 -------------------- include/qemu/plugin.h | 4 ---- plugins/api.c | 25 ++++++++++++++++++++++++- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 2aa1e08c17..c36632e8df 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -319,9 +319,6 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db) ret = true; ptb->vaddr = db->pc_first; - ptb->vaddr2 = -1; - ptb->haddr1 = db->host_addr[0]; - ptb->haddr2 = NULL; ptb->mem_helper = false; tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); @@ -363,23 +360,6 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) pc = db->pc_next; insn->vaddr = pc; - /* - * Detect page crossing to get the new host address. - * Note that we skip this when haddr1 == NULL, e.g. when we're - * fetching instructions from a region not backed by RAM. - */ - if (ptb->haddr1 == NULL) { - insn->haddr = NULL; - } else if (is_same_page(db, db->pc_next)) { - insn->haddr = ptb->haddr1 + pc - ptb->vaddr; - } else { - if (ptb->vaddr2 == -1) { - ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first); - get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2); - } - insn->haddr = ptb->haddr2 + pc - ptb->vaddr2; - } - tcg_gen_plugin_cb(PLUGIN_GEN_FROM_INSN); } diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index c28d0ca31c..da0f37e269 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -99,7 +99,6 @@ struct qemu_plugin_dyn_cb { /* Internal context for instrumenting an instruction */ struct qemu_plugin_insn { uint64_t vaddr; - void *haddr; GArray *insn_cbs; GArray *mem_cbs; uint8_t len; @@ -120,9 +119,6 @@ struct qemu_plugin_tb { GPtrArray *insns; size_t n; uint64_t vaddr; - uint64_t vaddr2; - void *haddr1; - void *haddr2; /* if set, the TB calls helpers that might access guest memory */ bool mem_helper; diff --git a/plugins/api.c b/plugins/api.c index 9e4aa9d2d9..0ae19774df 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -242,7 +242,30 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) { - return insn->haddr; + const DisasContextBase *db = tcg_ctx->plugin_db; + vaddr page0_last = db->pc_first | ~TARGET_PAGE_MASK; + + if (db->fake_insn) { + return NULL; + } + + /* + * ??? The return value is not intended for use of host memory, + * but as a proxy for address space and physical address. + * Thus we are only interested in the first byte and do not + * care about spanning pages. + */ + if (insn->vaddr <= page0_last) { + if (db->host_addr[0] == NULL) { + return NULL; + } + return db->host_addr[0] + insn->vaddr - db->pc_first; + } else { + if (db->host_addr[1] == NULL) { + return NULL; + } + return db->host_addr[1] + insn->vaddr - (page0_last + 1); + } } char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) From e763953a2445770bb5d1653d0c258dca5a6de5e2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 09:59:18 -1000 Subject: [PATCH 0643/2884] plugins: Use DisasContextBase for qemu_plugin_tb_vaddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not need to separately record the start of the TB. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 3 +-- include/qemu/plugin.h | 1 - plugins/api.c | 3 ++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index c36632e8df..b54494712a 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -188,7 +188,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) int insn_idx = -1; if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN) - && qemu_log_in_addr_range(plugin_tb->vaddr))) { + && qemu_log_in_addr_range(tcg_ctx->plugin_db->pc_first))) { FILE *logfile = qemu_log_trylock(); if (logfile) { fprintf(logfile, "OP before plugin injection:\n"); @@ -318,7 +318,6 @@ bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db) ret = true; - ptb->vaddr = db->pc_first; ptb->mem_helper = false; tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index da0f37e269..7fda6ef126 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -118,7 +118,6 @@ struct qemu_plugin_scoreboard { struct qemu_plugin_tb { GPtrArray *insns; size_t n; - uint64_t vaddr; /* if set, the TB calls helpers that might access guest memory */ bool mem_helper; diff --git a/plugins/api.c b/plugins/api.c index 0ae19774df..02014d4c6e 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -200,7 +200,8 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) { - return tb->vaddr; + const DisasContextBase *db = tcg_ctx->plugin_db; + return db->pc_first; } struct qemu_plugin_insn * From 34e5e1dde5b9760936a6e2426f5ff0d55a0695b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 13:35:43 -1000 Subject: [PATCH 0644/2884] plugins: Merge alloc_tcg_plugin_context into plugin_gen_tb_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need to allocate plugin context at startup, we can wait until we actually use it. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/plugin-gen.c | 36 ++++++++++++++++++++---------------- tcg/tcg.c | 11 ----------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index b54494712a..54b08ffc9e 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -305,28 +305,32 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db) { - bool ret = false; + struct qemu_plugin_tb *ptb; - if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_state->event_mask)) { - struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - - /* reset callbacks */ - if (ptb->cbs) { - g_array_set_size(ptb->cbs, 0); - } - ptb->n = 0; - - ret = true; - - ptb->mem_helper = false; - - tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); + if (!test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, + cpu->plugin_state->event_mask)) { + return false; } tcg_ctx->plugin_db = db; tcg_ctx->plugin_insn = NULL; + ptb = tcg_ctx->plugin_tb; - return ret; + if (ptb) { + /* Reset callbacks */ + if (ptb->cbs) { + g_array_set_size(ptb->cbs, 0); + } + ptb->n = 0; + ptb->mem_helper = false; + } else { + ptb = g_new0(struct qemu_plugin_tb, 1); + tcg_ctx->plugin_tb = ptb; + ptb->insns = g_ptr_array_new(); + } + + tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); + return true; } void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) diff --git a/tcg/tcg.c b/tcg/tcg.c index 71daa5d268..34e3056380 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -761,14 +761,6 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - < MIN_TLB_MASK_TABLE_OFS); #endif -static void alloc_tcg_plugin_context(TCGContext *s) -{ -#ifdef CONFIG_PLUGIN - s->plugin_tb = g_new0(struct qemu_plugin_tb, 1); - s->plugin_tb->insns = g_ptr_array_new(); -#endif -} - /* * All TCG threads except the parent (i.e. the one that called tcg_context_init * and registered the target's TCG globals) must register with this function @@ -813,7 +805,6 @@ void tcg_register_thread(void) qatomic_set(&tcg_ctxs[n], s); if (n > 0) { - alloc_tcg_plugin_context(s); tcg_region_initial_alloc(s); } @@ -1360,8 +1351,6 @@ static void tcg_context_init(unsigned max_cpus) indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[i]; } - alloc_tcg_plugin_context(s); - tcg_ctx = s; /* * In user-mode we simply share the init context among threads, since we From 962a145cdcd118f19597c08939ca24fd6227bc36 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 22:57:16 -1000 Subject: [PATCH 0645/2884] accel/tcg: Provide default implementation of disas_log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Almost all of the disas_log implementations are identical. Unify them within translator_loop. Drop extra Priv/Virt logging from target/riscv. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 9 ++++++++- target/alpha/translate.c | 9 --------- target/arm/tcg/translate-a64.c | 11 ----------- target/arm/tcg/translate.c | 12 ------------ target/avr/translate.c | 8 -------- target/cris/translate.c | 11 ----------- target/hexagon/translate.c | 9 --------- target/hppa/translate.c | 6 ++++-- target/i386/tcg/translate.c | 11 ----------- target/loongarch/tcg/translate.c | 8 -------- target/m68k/translate.c | 9 --------- target/microblaze/translate.c | 9 --------- target/mips/tcg/translate.c | 9 --------- target/openrisc/translate.c | 11 ----------- target/ppc/translate.c | 9 --------- target/riscv/translate.c | 18 ------------------ target/rx/translate.c | 8 -------- target/sh4/translate.c | 9 --------- target/sparc/translate.c | 9 --------- target/tricore/translate.c | 9 --------- target/xtensa/translate.c | 9 --------- 21 files changed, 12 insertions(+), 191 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 157b447810..98d2500c53 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -17,6 +17,7 @@ #include "exec/cpu_ldst.h" #include "tcg/tcg-op-common.h" #include "internal-target.h" +#include "disas/disas.h" static void set_can_do_io(DisasContextBase *db, bool val) { @@ -226,7 +227,13 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, FILE *logfile = qemu_log_trylock(); if (logfile) { fprintf(logfile, "----------------\n"); - ops->disas_log(db, cpu, logfile); + + if (ops->disas_log) { + ops->disas_log(db, cpu, logfile); + } else { + fprintf(logfile, "IN: %s\n", lookup_symbol(db->pc_first)); + target_disas(logfile, cpu, db->pc_first, db->tb->size); + } fprintf(logfile, "\n"); qemu_log_unlock(logfile); } diff --git a/target/alpha/translate.c b/target/alpha/translate.c index db847e7a23..fb6cac4b53 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "sysemu/cpus.h" -#include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" @@ -2947,20 +2946,12 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void alpha_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps alpha_tr_ops = { .init_disas_context = alpha_tr_init_disas_context, .tb_start = alpha_tr_tb_start, .insn_start = alpha_tr_insn_start, .translate_insn = alpha_tr_translate_insn, .tb_stop = alpha_tr_tb_stop, - .disas_log = alpha_tr_disas_log, }; void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 976094a5c8..4126aaa27e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -22,7 +22,6 @@ #include "translate.h" #include "translate-a64.h" #include "qemu/log.h" -#include "disas/disas.h" #include "arm_ldst.h" #include "semihosting/semihost.h" #include "cpregs.h" @@ -14382,20 +14381,10 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void aarch64_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); -} - const TranslatorOps aarch64_translator_ops = { .init_disas_context = aarch64_tr_init_disas_context, .tb_start = aarch64_tr_tb_start, .insn_start = aarch64_tr_insn_start, .translate_insn = aarch64_tr_translate_insn, .tb_stop = aarch64_tr_tb_stop, - .disas_log = aarch64_tr_disas_log, }; diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index dc49a8d806..d605e10f11 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -23,7 +23,6 @@ #include "translate.h" #include "translate-a32.h" #include "qemu/log.h" -#include "disas/disas.h" #include "arm_ldst.h" #include "semihosting/semihost.h" #include "cpregs.h" @@ -9663,22 +9662,12 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void arm_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); -} - static const TranslatorOps arm_translator_ops = { .init_disas_context = arm_tr_init_disas_context, .tb_start = arm_tr_tb_start, .insn_start = arm_tr_insn_start, .translate_insn = arm_tr_translate_insn, .tb_stop = arm_tr_tb_stop, - .disas_log = arm_tr_disas_log, }; static const TranslatorOps thumb_translator_ops = { @@ -9687,7 +9676,6 @@ static const TranslatorOps thumb_translator_ops = { .insn_start = arm_tr_insn_start, .translate_insn = thumb_tr_translate_insn, .tb_stop = arm_tr_tb_stop, - .disas_log = arm_tr_disas_log, }; /* generate intermediate code for basic block 'tb'. */ diff --git a/target/avr/translate.c b/target/avr/translate.c index 87e2bd5ef1..6df93d4c77 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -2787,20 +2787,12 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void avr_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps avr_tr_ops = { .init_disas_context = avr_tr_init_disas_context, .tb_start = avr_tr_tb_start, .insn_start = avr_tr_insn_start, .translate_insn = avr_tr_translate_insn, .tb_stop = avr_tr_tb_stop, - .disas_log = avr_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/cris/translate.c b/target/cris/translate.c index b3a4d61d0a..b5410189d4 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" @@ -3148,22 +3147,12 @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void cris_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - if (!DISAS_CRIS) { - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); - } -} - static const TranslatorOps cris_tr_ops = { .init_disas_context = cris_tr_init_disas_context, .tb_start = cris_tr_tb_start, .insn_start = cris_tr_insn_start, .translate_insn = cris_tr_translate_insn, .tb_stop = cris_tr_tb_stop, - .disas_log = cris_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index fcba82f7dc..61302d4f46 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1076,21 +1076,12 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void hexagon_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - - static const TranslatorOps hexagon_tr_ops = { .init_disas_context = hexagon_tr_init_disas_context, .tb_start = hexagon_tr_tb_start, .insn_start = hexagon_tr_insn_start, .translate_insn = hexagon_tr_translate_packet, .tb_stop = hexagon_tr_tb_stop, - .disas_log = hexagon_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6d45611888..1a806a9d09 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4816,12 +4816,12 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } +#ifdef CONFIG_USER_ONLY static void hppa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs, FILE *logfile) { target_ulong pc = dcbase->pc_first; -#ifdef CONFIG_USER_ONLY switch (pc) { case 0x00: fprintf(logfile, "IN:\n0x00000000: (null)\n"); @@ -4836,11 +4836,11 @@ static void hppa_tr_disas_log(const DisasContextBase *dcbase, fprintf(logfile, "IN:\n0x00000100: syscall\n"); return; } -#endif fprintf(logfile, "IN: %s\n", lookup_symbol(pc)); target_disas(logfile, cs, pc, dcbase->tb->size); } +#endif static const TranslatorOps hppa_tr_ops = { .init_disas_context = hppa_tr_init_disas_context, @@ -4848,7 +4848,9 @@ static const TranslatorOps hppa_tr_ops = { .insn_start = hppa_tr_insn_start, .translate_insn = hppa_tr_translate_insn, .tb_stop = hppa_tr_tb_stop, +#ifdef CONFIG_USER_ONLY .disas_log = hppa_tr_disas_log, +#endif }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index de87775016..ed601474a9 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -20,7 +20,6 @@ #include "qemu/host-utils.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" @@ -4798,22 +4797,12 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void i386_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); -} - static const TranslatorOps i386_tr_ops = { .init_disas_context = i386_tr_init_disas_context, .tb_start = i386_tr_tb_start, .insn_start = i386_tr_insn_start, .translate_insn = i386_tr_translate_insn, .tb_stop = i386_tr_tb_stop, - .disas_log = i386_tr_disas_log, }; /* generate intermediate code for basic block 'tb'. */ diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index 7567712655..1fca4afc73 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -325,20 +325,12 @@ static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void loongarch_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps loongarch_tr_ops = { .init_disas_context = loongarch_tr_init_disas_context, .tb_start = loongarch_tr_tb_start, .insn_start = loongarch_tr_insn_start, .translate_insn = loongarch_tr_translate_insn, .tb_stop = loongarch_tr_tb_stop, - .disas_log = loongarch_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 169927552a..445966fb6a 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "qemu/log.h" @@ -6105,20 +6104,12 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void m68k_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps m68k_tr_ops = { .init_disas_context = m68k_tr_init_disas_context, .tb_start = m68k_tr_tb_start, .insn_start = m68k_tr_insn_start, .translate_insn = m68k_tr_translate_insn, .tb_stop = m68k_tr_tb_stop, - .disas_log = m68k_tr_disas_log, }; void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 84cca04962..9746a6d479 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "tcg/tcg-op.h" @@ -1772,20 +1771,12 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) } } -static void mb_tr_disas_log(const DisasContextBase *dcb, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcb->pc_first)); - target_disas(logfile, cs, dcb->pc_first, dcb->tb->size); -} - static const TranslatorOps mb_tr_ops = { .init_disas_context = mb_tr_init_disas_context, .tb_start = mb_tr_tb_start, .insn_start = mb_tr_insn_start, .translate_insn = mb_tr_translate_insn, .tb_stop = mb_tr_tb_stop, - .disas_log = mb_tr_disas_log, }; void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 06c108cc9c..333469b268 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -29,7 +29,6 @@ #include "exec/translation-block.h" #include "semihosting/semihost.h" #include "trace.h" -#include "disas/disas.h" #include "fpu_helper.h" #define HELPER_H "helper.h" @@ -15475,20 +15474,12 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void mips_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps mips_tr_ops = { .init_disas_context = mips_tr_init_disas_context, .tb_start = mips_tr_tb_start, .insn_start = mips_tr_insn_start, .translate_insn = mips_tr_translate_insn, .tb_stop = mips_tr_tb_stop, - .disas_log = mips_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 23fff46084..ca566847cb 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "disas/disas.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/bitops.h" @@ -1638,22 +1637,12 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void openrisc_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - DisasContext *s = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(s->base.pc_first)); - target_disas(logfile, cs, s->base.pc_first, s->base.tb->size); -} - static const TranslatorOps openrisc_tr_ops = { .init_disas_context = openrisc_tr_init_disas_context, .tb_start = openrisc_tr_tb_start, .insn_start = openrisc_tr_insn_start, .translate_insn = openrisc_tr_translate_insn, .tb_stop = openrisc_tr_tb_stop, - .disas_log = openrisc_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 93ffec787c..49dee6cab0 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" @@ -7405,20 +7404,12 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void ppc_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps ppc_tr_ops = { .init_disas_context = ppc_tr_init_disas_context, .tb_start = ppc_tr_tb_start, .insn_start = ppc_tr_insn_start, .translate_insn = ppc_tr_translate_insn, .tb_stop = ppc_tr_tb_stop, - .disas_log = ppc_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 9ff09ebdb6..c999e942e1 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -20,7 +20,6 @@ #include "qemu/log.h" #include "cpu.h" #include "tcg/tcg-op.h" -#include "disas/disas.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -1270,29 +1269,12 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void riscv_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ -#ifndef CONFIG_USER_ONLY - RISCVCPU *rvcpu = RISCV_CPU(cpu); - CPURISCVState *env = &rvcpu->env; -#endif - - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); -#ifndef CONFIG_USER_ONLY - fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: %d\n", - env->priv, env->virt_enabled); -#endif - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps riscv_tr_ops = { .init_disas_context = riscv_tr_init_disas_context, .tb_start = riscv_tr_tb_start, .insn_start = riscv_tr_insn_start, .translate_insn = riscv_tr_translate_insn, .tb_stop = riscv_tr_tb_stop, - .disas_log = riscv_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/rx/translate.c b/target/rx/translate.c index f6e9e0ec90..92fb2b43ad 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -2247,20 +2247,12 @@ static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void rx_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps rx_tr_ops = { .init_disas_context = rx_tr_init_disas_context, .tb_start = rx_tr_tb_start, .insn_start = rx_tr_insn_start, .translate_insn = rx_tr_translate_insn, .tb_stop = rx_tr_tb_stop, - .disas_log = rx_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/sh4/translate.c b/target/sh4/translate.c index b3282f3ac7..53b092175d 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" @@ -2310,20 +2309,12 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void sh4_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps sh4_tr_ops = { .init_disas_context = sh4_tr_init_disas_context, .tb_start = sh4_tr_tb_start, .insn_start = sh4_tr_insn_start, .translate_insn = sh4_tr_translate_insn, .tb_stop = sh4_tr_tb_stop, - .disas_log = sh4_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 99c6f3cc72..dca072888a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" @@ -5149,20 +5148,12 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void sparc_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps sparc_tr_ops = { .init_disas_context = sparc_tr_init_disas_context, .tb_start = sparc_tr_tb_start, .insn_start = sparc_tr_insn_start, .translate_insn = sparc_tr_translate_insn, .tb_stop = sparc_tr_tb_stop, - .disas_log = sparc_tr_disas_log, }; void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, diff --git a/target/tricore/translate.c b/target/tricore/translate.c index c45e1d992e..a46a03e1fd 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" @@ -8453,20 +8452,12 @@ static void tricore_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void tricore_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps tricore_tr_ops = { .init_disas_context = tricore_tr_init_disas_context, .tb_start = tricore_tr_tb_start, .insn_start = tricore_tr_insn_start, .translate_insn = tricore_tr_translate_insn, .tb_stop = tricore_tr_tb_stop, - .disas_log = tricore_tr_disas_log, }; diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index b206d57fc4..42109d33ad 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -32,7 +32,6 @@ #include "cpu.h" #include "exec/exec-all.h" -#include "disas/disas.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" @@ -1221,20 +1220,12 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void xtensa_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps xtensa_translator_ops = { .init_disas_context = xtensa_tr_init_disas_context, .tb_start = xtensa_tr_tb_start, .insn_start = xtensa_tr_insn_start, .translate_insn = xtensa_tr_translate_insn, .tb_stop = xtensa_tr_tb_stop, - .disas_log = xtensa_tr_disas_log, }; void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, From b67c567b79f7f659814d102579d2b503b6d40ed4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 23:07:53 -1000 Subject: [PATCH 0646/2884] accel/tcg: Return bool from TranslatorOps.disas_log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have eliminated most uses of this hook. Reduce further by allowing the hook to handle only the special cases, returning false for normal processing. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 5 ++--- include/exec/translator.h | 2 +- target/hppa/translate.c | 15 ++++++--------- target/s390x/tcg/translate.c | 8 +++----- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 98d2500c53..ccd22dcd95 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -228,9 +228,8 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, if (logfile) { fprintf(logfile, "----------------\n"); - if (ops->disas_log) { - ops->disas_log(db, cpu, logfile); - } else { + if (!ops->disas_log || + !ops->disas_log(db, cpu, logfile)) { fprintf(logfile, "IN: %s\n", lookup_symbol(db->pc_first)); target_disas(logfile, cpu, db->pc_first, db->tb->size); } diff --git a/include/exec/translator.h b/include/exec/translator.h index fff857a0cc..31c39ab63c 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -135,7 +135,7 @@ typedef struct TranslatorOps { void (*insn_start)(DisasContextBase *db, CPUState *cpu); void (*translate_insn)(DisasContextBase *db, CPUState *cpu); void (*tb_stop)(DisasContextBase *db, CPUState *cpu); - void (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f); + bool (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f); } TranslatorOps; /** diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1a806a9d09..7287e1debf 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "exec/page-protection.h" @@ -4817,7 +4816,7 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } #ifdef CONFIG_USER_ONLY -static void hppa_tr_disas_log(const DisasContextBase *dcbase, +static bool hppa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs, FILE *logfile) { target_ulong pc = dcbase->pc_first; @@ -4825,20 +4824,18 @@ static void hppa_tr_disas_log(const DisasContextBase *dcbase, switch (pc) { case 0x00: fprintf(logfile, "IN:\n0x00000000: (null)\n"); - return; + return true; case 0xb0: fprintf(logfile, "IN:\n0x000000b0: light-weight-syscall\n"); - return; + return true; case 0xe0: fprintf(logfile, "IN:\n0x000000e0: set-thread-pointer-syscall\n"); - return; + return true; case 0x100: fprintf(logfile, "IN:\n0x00000100: syscall\n"); - return; + return true; } - - fprintf(logfile, "IN: %s\n", lookup_symbol(pc)); - target_disas(logfile, cs, pc, dcbase->tb->size); + return false; } #endif diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 6d7f6e7064..d74939389a 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -31,7 +31,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" @@ -6520,7 +6519,7 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void s390x_tr_disas_log(const DisasContextBase *dcbase, +static bool s390x_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs, FILE *logfile) { DisasContext *dc = container_of(dcbase, DisasContext, base); @@ -6528,10 +6527,9 @@ static void s390x_tr_disas_log(const DisasContextBase *dcbase, if (unlikely(dc->ex_value)) { /* ??? Unfortunately target_disas can't use host memory. */ fprintf(logfile, "IN: EXECUTE %016" PRIx64, dc->ex_value); - } else { - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cs, dc->base.pc_first, dc->base.tb->size); + return true; } + return false; } static const TranslatorOps s390x_tr_ops = { From c0d691ab844db8cdf2be8f6cf43887cfff56e386 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 20:46:32 -1000 Subject: [PATCH 0647/2884] disas: Split disas.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The routines in disas-common.c are also used from disas-mon.c. Otherwise the rest of disassembly is only used from tcg. While we're at it, put host and target code into separate files. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- disas/disas-common.c | 118 ++++++++++++++ disas/disas-host.c | 129 ++++++++++++++++ disas/disas-internal.h | 4 + disas/disas-target.c | 84 ++++++++++ disas/disas.c | 338 ----------------------------------------- disas/meson.build | 8 +- disas/objdump.c | 37 +++++ include/disas/disas.h | 4 + 8 files changed, 382 insertions(+), 340 deletions(-) create mode 100644 disas/disas-common.c create mode 100644 disas/disas-host.c create mode 100644 disas/disas-target.c delete mode 100644 disas/disas.c create mode 100644 disas/objdump.c diff --git a/disas/disas-common.c b/disas/disas-common.c new file mode 100644 index 0000000000..ce9f82b711 --- /dev/null +++ b/disas/disas-common.c @@ -0,0 +1,118 @@ +/* + * Common routines for disassembly. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "disas/capstone.h" +#include "hw/core/cpu.h" +#include "exec/tswap.h" +#include "exec/memory.h" +#include "disas-internal.h" + + +/* Filled in by elfload.c. Simplistic, but will do for now. */ +struct syminfo *syminfos = NULL; + +/* + * Get LENGTH bytes from info's buffer, at target address memaddr. + * Transfer them to myaddr. + */ +static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + CPUDebug *s = container_of(info, CPUDebug, info); + int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); + return r ? EIO : 0; +} + +/* + * Print an error message. We can assume that this is in response to + * an error return from {host,target}_read_memory. + */ +static void perror_memory(int status, bfd_vma memaddr, + struct disassemble_info *info) +{ + if (status != EIO) { + /* Can't happen. */ + info->fprintf_func(info->stream, "Unknown error %d\n", status); + } else { + /* Address between memaddr and memaddr + len was out of bounds. */ + info->fprintf_func(info->stream, + "Address 0x%" PRIx64 " is out of bounds.\n", + memaddr); + } +} + +/* Print address in hex. */ +static void print_address(bfd_vma addr, struct disassemble_info *info) +{ + info->fprintf_func(info->stream, "0x%" PRIx64, addr); +} + +/* Stub prevents some fruitless earching in optabs disassemblers. */ +static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) +{ + return 1; +} + +void disas_initialize_debug(CPUDebug *s) +{ + memset(s, 0, sizeof(*s)); + s->info.arch = bfd_arch_unknown; + s->info.cap_arch = -1; + s->info.cap_insn_unit = 4; + s->info.cap_insn_split = 4; + s->info.memory_error_func = perror_memory; + s->info.symbol_at_address_func = symbol_at_address; +} + +void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) +{ + disas_initialize_debug(s); + + s->cpu = cpu; + s->info.read_memory_func = target_read_memory; + s->info.print_address_func = print_address; + if (target_words_bigendian()) { + s->info.endian = BFD_ENDIAN_BIG; + } else { + s->info.endian = BFD_ENDIAN_LITTLE; + } + + CPUClass *cc = CPU_GET_CLASS(cpu); + if (cc->disas_set_info) { + cc->disas_set_info(cpu, &s->info); + } +} + +int disas_gstring_printf(FILE *stream, const char *fmt, ...) +{ + /* We abuse the FILE parameter to pass a GString. */ + GString *s = (GString *)stream; + int initial_len = s->len; + va_list va; + + va_start(va, fmt); + g_string_append_vprintf(s, fmt, va); + va_end(va); + + return s->len - initial_len; +} + +/* Look up symbol for debugging purpose. Returns "" if unknown. */ +const char *lookup_symbol(uint64_t orig_addr) +{ + const char *symbol = ""; + struct syminfo *s; + + for (s = syminfos; s; s = s->next) { + symbol = s->lookup_symbol(s, orig_addr); + if (symbol[0] != '\0') { + break; + } + } + + return symbol; +} diff --git a/disas/disas-host.c b/disas/disas-host.c new file mode 100644 index 0000000000..8146fafe80 --- /dev/null +++ b/disas/disas-host.c @@ -0,0 +1,129 @@ +/* + * Routines for host instruction disassembly. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "disas/capstone.h" +#include "disas-internal.h" + + +/* + * Get LENGTH bytes from info's buffer, at host address memaddr. + * Transfer them to myaddr. + */ +static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + if (memaddr < info->buffer_vma + || memaddr + length > info->buffer_vma + info->buffer_length) { + /* Out of bounds. Use EIO because GDB uses it. */ + return EIO; + } + memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); + return 0; +} + +/* Print address in hex, truncated to the width of a host virtual address. */ +static void host_print_address(bfd_vma addr, struct disassemble_info *info) +{ + info->fprintf_func(info->stream, "0x%" PRIxPTR, (uintptr_t)addr); +} + +static void initialize_debug_host(CPUDebug *s) +{ + disas_initialize_debug(s); + + s->info.read_memory_func = host_read_memory; + s->info.print_address_func = host_print_address; +#if HOST_BIG_ENDIAN + s->info.endian = BFD_ENDIAN_BIG; +#else + s->info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(CONFIG_TCG_INTERPRETER) + s->info.print_insn = print_insn_tci; +#elif defined(__i386__) + s->info.mach = bfd_mach_i386_i386; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_32; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(__x86_64__) + s->info.mach = bfd_mach_x86_64; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_64; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(_ARCH_PPC) + s->info.cap_arch = CS_ARCH_PPC; +# ifdef _ARCH_PPC64 + s->info.cap_mode = CS_MODE_64; +# endif +#elif defined(__riscv) +#if defined(_ILP32) || (__riscv_xlen == 32) + s->info.print_insn = print_insn_riscv32; +#elif defined(_LP64) + s->info.print_insn = print_insn_riscv64; +#else +#error unsupported RISC-V ABI +#endif +#elif defined(__aarch64__) + s->info.cap_arch = CS_ARCH_ARM64; +#elif defined(__alpha__) + s->info.print_insn = print_insn_alpha; +#elif defined(__sparc__) + s->info.print_insn = print_insn_sparc; + s->info.mach = bfd_mach_sparc_v9b; +#elif defined(__arm__) + /* TCG only generates code for arm mode. */ + s->info.cap_arch = CS_ARCH_ARM; +#elif defined(__MIPSEB__) + s->info.print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + s->info.print_insn = print_insn_little_mips; +#elif defined(__m68k__) + s->info.print_insn = print_insn_m68k; +#elif defined(__s390__) + s->info.cap_arch = CS_ARCH_SYSZ; + s->info.cap_insn_unit = 2; + s->info.cap_insn_split = 6; +#elif defined(__hppa__) + s->info.print_insn = print_insn_hppa; +#elif defined(__loongarch__) + s->info.print_insn = print_insn_loongarch; +#endif +} + +/* Disassemble this for me please... (debugging). */ +void disas(FILE *out, const void *code, size_t size) +{ + uintptr_t pc; + int count; + CPUDebug s; + + initialize_debug_host(&s); + s.info.fprintf_func = fprintf; + s.info.stream = out; + s.info.buffer = code; + s.info.buffer_vma = (uintptr_t)code; + s.info.buffer_length = size; + s.info.show_opcodes = true; + + if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { + return; + } + + if (s.info.print_insn == NULL) { + s.info.print_insn = print_insn_od_host; + } + for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { + fprintf(out, "0x%08" PRIxPTR ": ", pc); + count = s.info.print_insn(pc, &s.info); + fprintf(out, "\n"); + if (count < 0) { + break; + } + } +} diff --git a/disas/disas-internal.h b/disas/disas-internal.h index 84a01f126f..ed32e704cc 100644 --- a/disas/disas-internal.h +++ b/disas/disas-internal.h @@ -14,8 +14,12 @@ typedef struct CPUDebug { CPUState *cpu; } CPUDebug; +void disas_initialize_debug(CPUDebug *s); void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu); int disas_gstring_printf(FILE *stream, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +int print_insn_od_host(bfd_vma pc, disassemble_info *info); +int print_insn_od_target(bfd_vma pc, disassemble_info *info); + #endif diff --git a/disas/disas-target.c b/disas/disas-target.c new file mode 100644 index 0000000000..82313b2a67 --- /dev/null +++ b/disas/disas-target.c @@ -0,0 +1,84 @@ +/* + * Routines for target instruction disassembly. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "disas/capstone.h" +#include "disas-internal.h" + + +void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size) +{ + uint64_t pc; + int count; + CPUDebug s; + + disas_initialize_debug_target(&s, cpu); + s.info.fprintf_func = fprintf; + s.info.stream = out; + s.info.buffer_vma = code; + s.info.buffer_length = size; + s.info.show_opcodes = true; + + if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { + return; + } + + if (s.info.print_insn == NULL) { + s.info.print_insn = print_insn_od_target; + } + + for (pc = code; size > 0; pc += count, size -= count) { + fprintf(out, "0x%08" PRIx64 ": ", pc); + count = s.info.print_insn(pc, &s.info); + fprintf(out, "\n"); + if (count < 0) { + break; + } + if (size < count) { + fprintf(out, + "Disassembler disagrees with translator over instruction " + "decoding\n" + "Please report this to qemu-devel@nongnu.org\n"); + break; + } + } +} + +#ifdef CONFIG_PLUGIN +static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) +{ + /* does nothing */ +} + +/* + * We should only be dissembling one instruction at a time here. If + * there is left over it usually indicates the front end has read more + * bytes than it needed. + */ +char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) +{ + CPUDebug s; + GString *ds = g_string_new(NULL); + + disas_initialize_debug_target(&s, cpu); + s.info.fprintf_func = disas_gstring_printf; + s.info.stream = (FILE *)ds; /* abuse this slot */ + s.info.buffer_vma = addr; + s.info.buffer_length = size; + s.info.print_address_func = plugin_print_address; + + if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { + ; /* done */ + } else if (s.info.print_insn) { + s.info.print_insn(addr, &s.info); + } else { + ; /* cannot disassemble -- return empty string */ + } + + /* Return the buffer, freeing the GString container. */ + return g_string_free(ds, false); +} +#endif /* CONFIG_PLUGIN */ diff --git a/disas/disas.c b/disas/disas.c deleted file mode 100644 index ec14715ecd..0000000000 --- a/disas/disas.c +++ /dev/null @@ -1,338 +0,0 @@ -/* General "disassemble this chunk" code. Used for debugging. */ -#include "qemu/osdep.h" -#include "disas/disas-internal.h" -#include "elf.h" -#include "qemu/qemu-print.h" -#include "disas/disas.h" -#include "disas/capstone.h" -#include "hw/core/cpu.h" -#include "exec/tswap.h" -#include "exec/memory.h" - -/* Filled in by elfload.c. Simplistic, but will do for now. */ -struct syminfo *syminfos = NULL; - -/* - * Get LENGTH bytes from info's buffer, at host address memaddr. - * Transfer them to myaddr. - */ -static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) -{ - if (memaddr < info->buffer_vma - || memaddr + length > info->buffer_vma + info->buffer_length) { - /* Out of bounds. Use EIO because GDB uses it. */ - return EIO; - } - memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); - return 0; -} - -/* - * Get LENGTH bytes from info's buffer, at target address memaddr. - * Transfer them to myaddr. - */ -static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) -{ - CPUDebug *s = container_of(info, CPUDebug, info); - int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); - return r ? EIO : 0; -} - -/* - * Print an error message. We can assume that this is in response to - * an error return from {host,target}_read_memory. - */ -static void perror_memory(int status, bfd_vma memaddr, - struct disassemble_info *info) -{ - if (status != EIO) { - /* Can't happen. */ - info->fprintf_func(info->stream, "Unknown error %d\n", status); - } else { - /* Address between memaddr and memaddr + len was out of bounds. */ - info->fprintf_func(info->stream, - "Address 0x%" PRIx64 " is out of bounds.\n", - memaddr); - } -} - -/* Print address in hex. */ -static void print_address(bfd_vma addr, struct disassemble_info *info) -{ - info->fprintf_func(info->stream, "0x%" PRIx64, addr); -} - -/* Print address in hex, truncated to the width of a host virtual address. */ -static void host_print_address(bfd_vma addr, struct disassemble_info *info) -{ - print_address((uintptr_t)addr, info); -} - -/* Stub prevents some fruitless earching in optabs disassemblers. */ -static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) -{ - return 1; -} - -static int print_insn_objdump(bfd_vma pc, disassemble_info *info, - const char *prefix) -{ - int i, n = info->buffer_length; - g_autofree uint8_t *buf = g_malloc(n); - - if (info->read_memory_func(pc, buf, n, info) == 0) { - for (i = 0; i < n; ++i) { - if (i % 32 == 0) { - info->fprintf_func(info->stream, "\n%s: ", prefix); - } - info->fprintf_func(info->stream, "%02x", buf[i]); - } - } else { - info->fprintf_func(info->stream, "unable to read memory"); - } - return n; -} - -static int print_insn_od_host(bfd_vma pc, disassemble_info *info) -{ - return print_insn_objdump(pc, info, "OBJD-H"); -} - -static int print_insn_od_target(bfd_vma pc, disassemble_info *info) -{ - return print_insn_objdump(pc, info, "OBJD-T"); -} - -static void initialize_debug(CPUDebug *s) -{ - memset(s, 0, sizeof(*s)); - s->info.arch = bfd_arch_unknown; - s->info.cap_arch = -1; - s->info.cap_insn_unit = 4; - s->info.cap_insn_split = 4; - s->info.memory_error_func = perror_memory; - s->info.symbol_at_address_func = symbol_at_address; -} - -void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) -{ - initialize_debug(s); - - s->cpu = cpu; - s->info.read_memory_func = target_read_memory; - s->info.print_address_func = print_address; - if (target_words_bigendian()) { - s->info.endian = BFD_ENDIAN_BIG; - } else { - s->info.endian = BFD_ENDIAN_LITTLE; - } - - CPUClass *cc = CPU_GET_CLASS(cpu); - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s->info); - } -} - -static void initialize_debug_host(CPUDebug *s) -{ - initialize_debug(s); - - s->info.read_memory_func = host_read_memory; - s->info.print_address_func = host_print_address; -#if HOST_BIG_ENDIAN - s->info.endian = BFD_ENDIAN_BIG; -#else - s->info.endian = BFD_ENDIAN_LITTLE; -#endif -#if defined(CONFIG_TCG_INTERPRETER) - s->info.print_insn = print_insn_tci; -#elif defined(__i386__) - s->info.mach = bfd_mach_i386_i386; - s->info.cap_arch = CS_ARCH_X86; - s->info.cap_mode = CS_MODE_32; - s->info.cap_insn_unit = 1; - s->info.cap_insn_split = 8; -#elif defined(__x86_64__) - s->info.mach = bfd_mach_x86_64; - s->info.cap_arch = CS_ARCH_X86; - s->info.cap_mode = CS_MODE_64; - s->info.cap_insn_unit = 1; - s->info.cap_insn_split = 8; -#elif defined(_ARCH_PPC) - s->info.cap_arch = CS_ARCH_PPC; -# ifdef _ARCH_PPC64 - s->info.cap_mode = CS_MODE_64; -# endif -#elif defined(__riscv) -#if defined(_ILP32) || (__riscv_xlen == 32) - s->info.print_insn = print_insn_riscv32; -#elif defined(_LP64) - s->info.print_insn = print_insn_riscv64; -#else -#error unsupported RISC-V ABI -#endif -#elif defined(__aarch64__) - s->info.cap_arch = CS_ARCH_ARM64; -#elif defined(__alpha__) - s->info.print_insn = print_insn_alpha; -#elif defined(__sparc__) - s->info.print_insn = print_insn_sparc; - s->info.mach = bfd_mach_sparc_v9b; -#elif defined(__arm__) - /* TCG only generates code for arm mode. */ - s->info.cap_arch = CS_ARCH_ARM; -#elif defined(__MIPSEB__) - s->info.print_insn = print_insn_big_mips; -#elif defined(__MIPSEL__) - s->info.print_insn = print_insn_little_mips; -#elif defined(__m68k__) - s->info.print_insn = print_insn_m68k; -#elif defined(__s390__) - s->info.cap_arch = CS_ARCH_SYSZ; - s->info.cap_insn_unit = 2; - s->info.cap_insn_split = 6; -#elif defined(__hppa__) - s->info.print_insn = print_insn_hppa; -#elif defined(__loongarch__) - s->info.print_insn = print_insn_loongarch; -#endif -} - -/* Disassemble this for me please... (debugging). */ -void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size) -{ - uint64_t pc; - int count; - CPUDebug s; - - disas_initialize_debug_target(&s, cpu); - s.info.fprintf_func = fprintf; - s.info.stream = out; - s.info.buffer_vma = code; - s.info.buffer_length = size; - s.info.show_opcodes = true; - - if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { - return; - } - - if (s.info.print_insn == NULL) { - s.info.print_insn = print_insn_od_target; - } - - for (pc = code; size > 0; pc += count, size -= count) { - fprintf(out, "0x%08" PRIx64 ": ", pc); - count = s.info.print_insn(pc, &s.info); - fprintf(out, "\n"); - if (count < 0) { - break; - } - if (size < count) { - fprintf(out, - "Disassembler disagrees with translator over instruction " - "decoding\n" - "Please report this to qemu-devel@nongnu.org\n"); - break; - } - } -} - -int disas_gstring_printf(FILE *stream, const char *fmt, ...) -{ - /* We abuse the FILE parameter to pass a GString. */ - GString *s = (GString *)stream; - int initial_len = s->len; - va_list va; - - va_start(va, fmt); - g_string_append_vprintf(s, fmt, va); - va_end(va); - - return s->len - initial_len; -} - -static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) -{ - /* does nothing */ -} - - -/* - * We should only be dissembling one instruction at a time here. If - * there is left over it usually indicates the front end has read more - * bytes than it needed. - */ -char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) -{ - CPUDebug s; - GString *ds = g_string_new(NULL); - - disas_initialize_debug_target(&s, cpu); - s.info.fprintf_func = disas_gstring_printf; - s.info.stream = (FILE *)ds; /* abuse this slot */ - s.info.buffer_vma = addr; - s.info.buffer_length = size; - s.info.print_address_func = plugin_print_address; - - if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { - ; /* done */ - } else if (s.info.print_insn) { - s.info.print_insn(addr, &s.info); - } else { - ; /* cannot disassemble -- return empty string */ - } - - /* Return the buffer, freeing the GString container. */ - return g_string_free(ds, false); -} - -/* Disassemble this for me please... (debugging). */ -void disas(FILE *out, const void *code, size_t size) -{ - uintptr_t pc; - int count; - CPUDebug s; - - initialize_debug_host(&s); - s.info.fprintf_func = fprintf; - s.info.stream = out; - s.info.buffer = code; - s.info.buffer_vma = (uintptr_t)code; - s.info.buffer_length = size; - s.info.show_opcodes = true; - - if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { - return; - } - - if (s.info.print_insn == NULL) { - s.info.print_insn = print_insn_od_host; - } - for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { - fprintf(out, "0x%08" PRIxPTR ": ", pc); - count = s.info.print_insn(pc, &s.info); - fprintf(out, "\n"); - if (count < 0) { - break; - } - } - -} - -/* Look up symbol for debugging purpose. Returns "" if unknown. */ -const char *lookup_symbol(uint64_t orig_addr) -{ - const char *symbol = ""; - struct syminfo *s; - - for (s = syminfos; s; s = s->next) { - symbol = s->lookup_symbol(s, orig_addr); - if (symbol[0] != '\0') { - break; - } - } - - return symbol; -} diff --git a/disas/meson.build b/disas/meson.build index 5c8073beb3..20d6aef9a7 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -14,7 +14,11 @@ common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) common_ss.add(when: capstone, if_true: [files('capstone.c'), capstone]) -common_ss.add(files('disas.c')) - +common_ss.add(when: 'CONFIG_TCG', if_true: files( + 'disas-host.c', + 'disas-target.c', + 'objdump.c' +)) +common_ss.add(files('disas-common.c')) system_ss.add(files('disas-mon.c')) specific_ss.add(capstone) diff --git a/disas/objdump.c b/disas/objdump.c new file mode 100644 index 0000000000..9859f23419 --- /dev/null +++ b/disas/objdump.c @@ -0,0 +1,37 @@ +/* + * Dump disassembly as text, for processing by scripts/disas-objdump.pl. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas-internal.h" + + +static int print_insn_objdump(bfd_vma pc, disassemble_info *info, + const char *prefix) +{ + int i, n = info->buffer_length; + g_autofree uint8_t *buf = g_malloc(n); + + if (info->read_memory_func(pc, buf, n, info) == 0) { + for (i = 0; i < n; ++i) { + if (i % 32 == 0) { + info->fprintf_func(info->stream, "\n%s: ", prefix); + } + info->fprintf_func(info->stream, "%02x", buf[i]); + } + } else { + info->fprintf_func(info->stream, "unable to read memory"); + } + return n; +} + +int print_insn_od_host(bfd_vma pc, disassemble_info *info) +{ + return print_insn_objdump(pc, info, "OBJD-H"); +} + +int print_insn_od_target(bfd_vma pc, disassemble_info *info) +{ + return print_insn_objdump(pc, info, "OBJD-T"); +} diff --git a/include/disas/disas.h b/include/disas/disas.h index 176775eff7..54a5e68443 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -2,13 +2,17 @@ #define QEMU_DISAS_H /* Disassemble this for me please... (debugging). */ +#ifdef CONFIG_TCG void disas(FILE *out, const void *code, size_t size); void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size); +#endif void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc, int nb_insn, bool is_physical); +#ifdef CONFIG_PLUGIN char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size); +#endif /* Look up symbol for debugging purpose. Returns "" if unknown. */ const char *lookup_symbol(uint64_t orig_addr); From 4c833c60e0479d4eec5384fa9ede11ebab78a304 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 23:44:53 -1000 Subject: [PATCH 0648/2884] disas: Use translator_st to get disassembly data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read from already translated pages, or saved mmio data. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 2 +- disas/disas-common.c | 14 -------------- disas/disas-mon.c | 15 +++++++++++++++ disas/disas-target.c | 19 +++++++++++++++++-- include/disas/disas.h | 5 +++-- include/exec/translator.h | 4 ++-- include/qemu/typedefs.h | 1 + plugins/api.c | 4 ++-- 8 files changed, 41 insertions(+), 23 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index ccd22dcd95..00322c6fd9 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -231,7 +231,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, if (!ops->disas_log || !ops->disas_log(db, cpu, logfile)) { fprintf(logfile, "IN: %s\n", lookup_symbol(db->pc_first)); - target_disas(logfile, cpu, db->pc_first, db->tb->size); + target_disas(logfile, cpu, db); } fprintf(logfile, "\n"); qemu_log_unlock(logfile); diff --git a/disas/disas-common.c b/disas/disas-common.c index ce9f82b711..de61f6d8a1 100644 --- a/disas/disas-common.c +++ b/disas/disas-common.c @@ -8,25 +8,12 @@ #include "disas/capstone.h" #include "hw/core/cpu.h" #include "exec/tswap.h" -#include "exec/memory.h" #include "disas-internal.h" /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos = NULL; -/* - * Get LENGTH bytes from info's buffer, at target address memaddr. - * Transfer them to myaddr. - */ -static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) -{ - CPUDebug *s = container_of(info, CPUDebug, info); - int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); - return r ? EIO : 0; -} - /* * Print an error message. We can assume that this is in response to * an error return from {host,target}_read_memory. @@ -73,7 +60,6 @@ void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) disas_initialize_debug(s); s->cpu = cpu; - s->info.read_memory_func = target_read_memory; s->info.print_address_func = print_address; if (target_words_bigendian()) { s->info.endian = BFD_ENDIAN_BIG; diff --git a/disas/disas-mon.c b/disas/disas-mon.c index 5d6d9aa02d..37bf16ac79 100644 --- a/disas/disas-mon.c +++ b/disas/disas-mon.c @@ -11,6 +11,19 @@ #include "hw/core/cpu.h" #include "monitor/monitor.h" +/* + * Get LENGTH bytes from info's buffer, at target address memaddr. + * Transfer them to myaddr. + */ +static int +virtual_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + CPUDebug *s = container_of(info, CPUDebug, info); + int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); + return r ? EIO : 0; +} + static int physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info) @@ -38,6 +51,8 @@ void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc, if (is_physical) { s.info.read_memory_func = physical_read_memory; + } else { + s.info.read_memory_func = virtual_read_memory; } s.info.buffer_vma = pc; diff --git a/disas/disas-target.c b/disas/disas-target.c index 82313b2a67..48f3a365dc 100644 --- a/disas/disas-target.c +++ b/disas/disas-target.c @@ -6,16 +6,28 @@ #include "qemu/osdep.h" #include "disas/disas.h" #include "disas/capstone.h" +#include "exec/translator.h" #include "disas-internal.h" -void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size) +static int translator_read_memory(bfd_vma memaddr, bfd_byte *myaddr, + int length, struct disassemble_info *info) { + const DisasContextBase *db = info->application_data; + return translator_st(db, myaddr, memaddr, length) ? 0 : EIO; +} + +void target_disas(FILE *out, CPUState *cpu, const struct DisasContextBase *db) +{ + uint64_t code = db->pc_first; + size_t size = translator_st_len(db); uint64_t pc; int count; CPUDebug s; disas_initialize_debug_target(&s, cpu); + s.info.read_memory_func = translator_read_memory; + s.info.application_data = (void *)db; s.info.fprintf_func = fprintf; s.info.stream = out; s.info.buffer_vma = code; @@ -58,12 +70,15 @@ static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) * there is left over it usually indicates the front end has read more * bytes than it needed. */ -char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) +char *plugin_disas(CPUState *cpu, const DisasContextBase *db, + uint64_t addr, size_t size) { CPUDebug s; GString *ds = g_string_new(NULL); disas_initialize_debug_target(&s, cpu); + s.info.read_memory_func = translator_read_memory; + s.info.application_data = (void *)db; s.info.fprintf_func = disas_gstring_printf; s.info.stream = (FILE *)ds; /* abuse this slot */ s.info.buffer_vma = addr; diff --git a/include/disas/disas.h b/include/disas/disas.h index 54a5e68443..c702b1effc 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -4,14 +4,15 @@ /* Disassemble this for me please... (debugging). */ #ifdef CONFIG_TCG void disas(FILE *out, const void *code, size_t size); -void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size); +void target_disas(FILE *out, CPUState *cpu, const DisasContextBase *db); #endif void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc, int nb_insn, bool is_physical); #ifdef CONFIG_PLUGIN -char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size); +char *plugin_disas(CPUState *cpu, const DisasContextBase *db, + uint64_t addr, size_t size); #endif /* Look up symbol for debugging purpose. Returns "" if unknown. */ diff --git a/include/exec/translator.h b/include/exec/translator.h index 31c39ab63c..411ce2b47e 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -79,7 +79,7 @@ typedef enum DisasJumpType { * * Architecture-agnostic disassembly context. */ -typedef struct DisasContextBase { +struct DisasContextBase { TranslationBlock *tb; vaddr pc_first; vaddr pc_next; @@ -103,7 +103,7 @@ typedef struct DisasContextBase { int record_start; int record_len; uint8_t record[32]; -} DisasContextBase; +}; /** * TranslatorOps: diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index b47e7179e2..9d222dc376 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -42,6 +42,7 @@ typedef struct CPUPluginState CPUPluginState; typedef struct CPUState CPUState; typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; +typedef struct DisasContextBase DisasContextBase; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DriveInfo DriveInfo; typedef struct DumpState DumpState; diff --git a/plugins/api.c b/plugins/api.c index 02014d4c6e..b04c5e1928 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -271,8 +271,8 @@ void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) { - CPUState *cpu = current_cpu; - return plugin_disas(cpu, insn->vaddr, insn->len); + return plugin_disas(tcg_ctx->cpu, tcg_ctx->plugin_db, + insn->vaddr, insn->len); } const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) From 4c6163eaf2ca64391f885ca9625947371a4a4834 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 07:29:37 -1000 Subject: [PATCH 0649/2884] accel/tcg: Introduce translator_fake_ld MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace translator_fake_ldb, which required multiple calls, with translator_fake_ld, which can take all data at once. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 5 ++--- include/exec/translator.h | 8 ++++---- target/s390x/tcg/translate.c | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 00322c6fd9..c56967eecd 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -468,9 +468,8 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) return tgt; } -void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8) +void translator_fake_ld(DisasContextBase *db, const void *data, size_t len) { - assert(pc >= db->pc_first); db->fake_insn = true; - record_save(db, pc, &insn8, sizeof(insn8)); + record_save(db, db->pc_first, data, len); } diff --git a/include/exec/translator.h b/include/exec/translator.h index 411ce2b47e..25004dfb76 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -234,17 +234,17 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, } /** - * translator_fake_ldb - fake instruction load + * translator_fake_ld - fake instruction load * @db: Disassembly context - * @pc: program counter of instruction - * @insn8: byte of instruction + * @data: bytes of instruction + * @len: number of bytes * * This is a special case helper used where the instruction we are * about to translate comes from somewhere else (e.g. being * re-synthesised for s390x "ex"). It ensures we update other areas of * the translator with details of the executed instruction. */ -void translator_fake_ldb(DisasContextBase *db, vaddr pc, uint8_t insn8); +void translator_fake_ld(DisasContextBase *db, const void *data, size_t len); /** * translator_st diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index d74939389a..2eb787e401 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6191,6 +6191,8 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) const DisasInsn *info; if (unlikely(s->ex_value)) { + uint64_t be_insn; + /* Drop the EX data now, so that it's clear on exception paths. */ tcg_gen_st_i64(tcg_constant_i64(0), tcg_env, offsetof(CPUS390XState, ex_value)); @@ -6200,10 +6202,8 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) ilen = s->ex_value & 0xf; /* Register insn bytes with translator so plugins work. */ - for (int i = 0; i < ilen; i++) { - uint8_t byte = extract64(insn, 56 - (i * 8), 8); - translator_fake_ldb(&s->base, pc + i, byte); - } + be_insn = cpu_to_be64(insn); + translator_fake_ld(&s->base, &be_insn, ilen); op = insn >> 56; } else { insn = ld_code2(env, s, pc); From 171ce939812d5e09ff3b9e24e1cc995368258768 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 07:39:31 -1000 Subject: [PATCH 0650/2884] target/s390x: Fix translator_fake_ld length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ilen value extracted from ex_value is the length of the EXECUTE instruction itself, and so is the increment to the pc. However, the length of the synthetic insn is located in the opcode like all other instructions. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/tcg/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 2eb787e401..95d4d6ebc3 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6200,11 +6200,11 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) /* Extract the values saved by EXECUTE. */ insn = s->ex_value & 0xffffffffffff0000ull; ilen = s->ex_value & 0xf; + op = insn >> 56; /* Register insn bytes with translator so plugins work. */ be_insn = cpu_to_be64(insn); - translator_fake_ld(&s->base, &be_insn, ilen); - op = insn >> 56; + translator_fake_ld(&s->base, &be_insn, get_ilen(op)); } else { insn = ld_code2(env, s, pc); op = (insn >> 8) & 0xff; From 74e98b9b6f2c914ea61938378956a177b5985773 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2024 08:05:09 -1000 Subject: [PATCH 0651/2884] target/s390x: Disassemble EXECUTEd instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/tcg/translate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 95d4d6ebc3..bac033c63c 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6525,8 +6525,9 @@ static bool s390x_tr_disas_log(const DisasContextBase *dcbase, DisasContext *dc = container_of(dcbase, DisasContext, base); if (unlikely(dc->ex_value)) { - /* ??? Unfortunately target_disas can't use host memory. */ - fprintf(logfile, "IN: EXECUTE %016" PRIx64, dc->ex_value); + /* The ex_value has been recorded with translator_fake_ld. */ + fprintf(logfile, "IN: EXECUTE\n"); + target_disas(logfile, cs, &dc->base); return true; } return false; From e8939e801cd8f57fd46ece03fc3c4a38378d13fe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 09:24:14 -1000 Subject: [PATCH 0652/2884] target/hexagon: Use translator_ldl in pkt_crosses_page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hexagon/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 61302d4f46..4b1bee3c6d 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -25,7 +25,6 @@ #include "exec/translation-block.h" #include "exec/cpu_ldst.h" #include "exec/log.h" -#include "exec/cpu_ldst.h" #include "internal.h" #include "attribs.h" #include "insn.h" @@ -1023,7 +1022,7 @@ static bool pkt_crosses_page(CPUHexagonState *env, DisasContext *ctx) int nwords; for (nwords = 0; !found_end && nwords < PACKET_WORDS_MAX; nwords++) { - uint32_t word = cpu_ldl_code(env, + uint32_t word = translator_ldl(env, &ctx->base, ctx->base.pc_next + nwords * sizeof(uint32_t)); found_end = is_packet_end(word); } From 1561fee691a012de041cd087c594392f7c02c4c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2024 09:20:35 -1000 Subject: [PATCH 0653/2884] target/microblaze: Use translator_ldl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 9746a6d479..4beaf69e76 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -25,7 +25,6 @@ #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "qemu/qemu-print.h" @@ -1637,7 +1636,7 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) dc->tb_flags_to_set = 0; - ir = cpu_ldl_code(cpu_env(cs), dc->base.pc_next); + ir = translator_ldl(cpu_env(cs), &dc->base, dc->base.pc_next); if (!decode(dc, ir)) { trap_illegal(dc, true); } From dfc7228be3829c28584c54f703cdda18ae56d805 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:01:59 -1000 Subject: [PATCH 0654/2884] target/i386: Use translator_ldub for everything MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/translate.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ed601474a9..76be742580 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -23,7 +23,6 @@ #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "fpu/softfloat.h" @@ -1579,9 +1578,8 @@ static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) * This can happen even if the operand is only one byte long! */ if (((s->pc - 1) ^ (pc - 1)) & TARGET_PAGE_MASK) { - volatile uint8_t unused = - cpu_ldub_code(env, (s->pc - 1) & TARGET_PAGE_MASK); - (void) unused; + (void)translator_ldub(env, &s->base, + (s->pc - 1) & TARGET_PAGE_MASK); } siglongjmp(s->jmpbuf, 1); } @@ -2177,7 +2175,7 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) fprintf(logfile, "ILLOPC: " TARGET_FMT_lx ":", pc); for (; pc < end; ++pc) { - fprintf(logfile, " %02x", cpu_ldub_code(env, pc)); + fprintf(logfile, " %02x", translator_ldub(env, &s->base, pc)); } fprintf(logfile, "\n"); qemu_log_unlock(logfile); From 6c1992072512d1a005b24201a2f7673dd80216ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:03:49 -1000 Subject: [PATCH 0655/2884] target/avr: Use translator_lduw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/avr/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/avr/translate.c b/target/avr/translate.c index 6df93d4c77..2d51892115 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -24,7 +24,6 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" @@ -173,7 +172,7 @@ static int to_regs_00_30_by_two(DisasContext *ctx, int indx) static uint16_t next_word(DisasContext *ctx) { - return cpu_lduw_code(ctx->env, ctx->npc++ * 2); + return translator_lduw(ctx->env, &ctx->base, ctx->npc++ * 2); } static int append_16(DisasContext *ctx, int x) From 32e07f7deceb640ad200f58e6a93b598549fa4bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:11:25 -1000 Subject: [PATCH 0656/2884] target/cris: Use translator_ld* in cris_fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/cris/translate.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/target/cris/translate.c b/target/cris/translate.c index b5410189d4..bb2d6612ba 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -222,37 +222,28 @@ static int sign_extend(unsigned int val, unsigned int width) } static int cris_fetch(CPUCRISState *env, DisasContext *dc, uint32_t addr, - unsigned int size, unsigned int sign) + unsigned int size, bool sign) { int r; switch (size) { case 4: - { - r = cpu_ldl_code(env, addr); + r = translator_ldl(env, &dc->base, addr); break; - } case 2: - { + r = translator_lduw(env, &dc->base, addr); if (sign) { - r = cpu_ldsw_code(env, addr); - } else { - r = cpu_lduw_code(env, addr); + r = (int16_t)r; } break; - } case 1: - { + r = translator_ldub(env, &dc->base, addr); if (sign) { - r = cpu_ldsb_code(env, addr); - } else { - r = cpu_ldub_code(env, addr); + r = (int8_t)r; } break; - } default: - cpu_abort(CPU(dc->cpu), "Invalid fetch size %d\n", size); - break; + g_assert_not_reached(); } return r; } @@ -2868,7 +2859,7 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) int i; /* Load a halfword onto the instruction register. */ - dc->ir = cris_fetch(env, dc, dc->pc, 2, 0); + dc->ir = cris_fetch(env, dc, dc->pc, 2, 0); /* Now decode it. */ dc->opcode = EXTRACT_FIELD(dc->ir, 4, 11); From 1b1138cabeb6fd55438721c6d5fe373ba4fd3cd4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:17:02 -1000 Subject: [PATCH 0657/2884] target/cris: Use cris_fetch in translate_v10.c.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/cris/translate.c | 1 - target/cris/translate_v10.c.inc | 30 +++++++++--------------------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/target/cris/translate.c b/target/cris/translate.c index bb2d6612ba..a30c67eb07 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -29,7 +29,6 @@ #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "mmu.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "crisv32-decode.h" #include "qemu/qemu-print.h" diff --git a/target/cris/translate_v10.c.inc b/target/cris/translate_v10.c.inc index 73fc27c15d..c15ff47505 100644 --- a/target/cris/translate_v10.c.inc +++ b/target/cris/translate_v10.c.inc @@ -165,20 +165,7 @@ static int dec10_prep_move_m(CPUCRISState *env, DisasContext *dc, /* Load [$rs] onto T1. */ if (is_imm) { - if (memsize != 4) { - if (s_ext) { - if (memsize == 1) - imm = cpu_ldsb_code(env, dc->pc + 2); - else - imm = cpu_ldsw_code(env, dc->pc + 2); - } else { - if (memsize == 1) - imm = cpu_ldub_code(env, dc->pc + 2); - else - imm = cpu_lduw_code(env, dc->pc + 2); - } - } else - imm = cpu_ldl_code(env, dc->pc + 2); + imm = cris_fetch(env, dc, dc->pc + 2, memsize, s_ext); tcg_gen_movi_tl(dst, imm); @@ -929,10 +916,11 @@ static int dec10_dip(CPUCRISState *env, DisasContext *dc) LOG_DIS("dip pc=%x opcode=%d r%d r%d\n", dc->pc, dc->opcode, dc->src, dc->dst); if (dc->src == 15) { - imm = cpu_ldl_code(env, dc->pc + 2); + imm = cris_fetch(env, dc, dc->pc + 2, 4, 0); tcg_gen_movi_tl(cpu_PR[PR_PREFIX], imm); - if (dc->postinc) + if (dc->postinc) { insn_len += 4; + } tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len - 2); } else { gen_load(dc, cpu_PR[PR_PREFIX], cpu_R[dc->src], 4, 0); @@ -1095,10 +1083,10 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) if (dc->src == 15) { LOG_DIS("jump.%d %d r%d r%d direct\n", size, dc->opcode, dc->src, dc->dst); - imm = cpu_ldl_code(env, dc->pc + 2); - if (dc->mode == CRISV10_MODE_AUTOINC) + imm = cris_fetch(env, dc, dc->pc + 2, size, 0); + if (dc->mode == CRISV10_MODE_AUTOINC) { insn_len += size; - + } c = tcg_constant_tl(dc->pc + insn_len); t_gen_mov_preg_TN(dc, dc->dst, c); dc->jmp_pc = imm; @@ -1164,7 +1152,7 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) case CRISV10_IND_BCC_M: cris_cc_mask(dc, 0); - simm = cpu_ldsw_code(env, dc->pc + 2); + simm = cris_fetch(env, dc, dc->pc + 2, 2, 1); simm += 4; LOG_DIS("bcc_m: b%s %x\n", cc_name(dc->cond), dc->pc + simm); @@ -1185,7 +1173,7 @@ static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc) unsigned int insn_len = 2; /* Load a halfword onto the instruction register. */ - dc->ir = cpu_lduw_code(env, dc->pc); + dc->ir = cris_fetch(env, dc, dc->pc, 2, 0); /* Now decode it. */ dc->opcode = EXTRACT_FIELD(dc->ir, 6, 9); From 1f9c4462334f424d844b52c3476fb3b0ec62ab46 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:22:27 -1000 Subject: [PATCH 0658/2884] target/riscv: Use translator_ld* for everything MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/riscv/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index c999e942e1..2c27fd4ce1 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -20,7 +20,6 @@ #include "qemu/log.h" #include "cpu.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -1082,7 +1081,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) CPUState *cpu = ctx->cs; CPURISCVState *env = cpu_env(cpu); - return cpu_ldl_code(env, pc); + return translator_ldl(env, &ctx->base, pc); } /* Include insn module translation function */ @@ -1243,7 +1242,8 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK; if (page_ofs > TARGET_PAGE_SIZE - MAX_INSN_LEN) { - uint16_t next_insn = cpu_lduw_code(env, ctx->base.pc_next); + uint16_t next_insn = + translator_lduw(env, &ctx->base, ctx->base.pc_next); int len = insn_len(next_insn); if (!is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { From 104cf5524e6694a439891ba4dc071a1427f0ace3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:42:29 -1000 Subject: [PATCH 0659/2884] target/rx: Use translator_ld* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Yoshinori Sato Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/rx/translate.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 92fb2b43ad..9b81cf20b3 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" @@ -75,10 +74,10 @@ static TCGv_i64 cpu_acc; /* decoder helper */ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, - int i, int n) + int i, int n) { while (++i <= n) { - uint8_t b = cpu_ldub_code(ctx->env, ctx->base.pc_next++); + uint8_t b = translator_ldub(ctx->env, &ctx->base, ctx->base.pc_next++); insn |= b << (32 - i * 8); } return insn; @@ -90,22 +89,24 @@ static uint32_t li(DisasContext *ctx, int sz) CPURXState *env = ctx->env; addr = ctx->base.pc_next; - tcg_debug_assert(sz < 4); switch (sz) { case 1: ctx->base.pc_next += 1; - return cpu_ldsb_code(env, addr); + return (int8_t)translator_ldub(env, &ctx->base, addr); case 2: ctx->base.pc_next += 2; - return cpu_ldsw_code(env, addr); + return (int16_t)translator_lduw(env, &ctx->base, addr); case 3: ctx->base.pc_next += 3; - tmp = cpu_ldsb_code(env, addr + 2) << 16; - tmp |= cpu_lduw_code(env, addr) & 0xffff; + tmp = (int8_t)translator_ldub(env, &ctx->base, addr + 2); + tmp <<= 16; + tmp |= translator_lduw(env, &ctx->base, addr); return tmp; case 0: ctx->base.pc_next += 4; - return cpu_ldl_code(env, addr); + return translator_ldl(env, &ctx->base, addr); + default: + g_assert_not_reached(); } return 0; } @@ -190,22 +191,22 @@ static inline TCGv rx_index_addr(DisasContext *ctx, TCGv mem, { uint32_t dsp; - tcg_debug_assert(ld < 3); switch (ld) { case 0: return cpu_regs[reg]; case 1: - dsp = cpu_ldub_code(ctx->env, ctx->base.pc_next) << size; + dsp = translator_ldub(ctx->env, &ctx->base, ctx->base.pc_next) << size; tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); ctx->base.pc_next += 1; return mem; case 2: - dsp = cpu_lduw_code(ctx->env, ctx->base.pc_next) << size; + dsp = translator_lduw(ctx->env, &ctx->base, ctx->base.pc_next) << size; tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); ctx->base.pc_next += 2; return mem; + default: + g_assert_not_reached(); } - return NULL; } static inline MemOp mi_to_mop(unsigned mi) From a41cd1e6c3475932f9a3ca51a3fc2cc7a697b44e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 4 Apr 2024 23:53:24 -1000 Subject: [PATCH 0660/2884] target/xtensa: Use translator_ldub in xtensa_insn_len MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/xtensa/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 42109d33ad..75b7bfda4c 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -35,7 +35,6 @@ #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" -#include "exec/cpu_ldst.h" #include "semihosting/semihost.h" #include "exec/translator.h" @@ -1118,7 +1117,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) { - uint8_t b0 = cpu_ldub_code(env, dc->pc); + uint8_t b0 = translator_ldub(env, &dc->base, dc->pc); return xtensa_op0_insn_len(dc, b0); } From 763f2413e0e37688d9cf707542cb112142fe23f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Apr 2024 00:00:59 -1000 Subject: [PATCH 0661/2884] target/s390x: Use translator_lduw in get_next_pc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/tcg/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index bac033c63c..ebd96abe6c 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -36,7 +36,6 @@ #include "tcg/tcg-op-gvec.h" #include "qemu/log.h" #include "qemu/host-utils.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -6471,7 +6470,7 @@ static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) static target_ulong get_next_pc(CPUS390XState *env, DisasContext *s, uint64_t pc) { - uint64_t insn = cpu_lduw_code(env, pc); + uint64_t insn = translator_lduw(env, &s->base, pc); return pc + get_ilen((insn >> 8) & 0xff); } From 7c211c1cb47c33f855d3206b46943e6f4d1a3b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 5 Apr 2024 15:15:32 +0200 Subject: [PATCH 0662/2884] accel/tcg: Remove cpu_ldsb_code / cpu_ldsw_code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous commits replaced them by translator_ld* calls. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240405131532.40913-1-philmd@linaro.org> Signed-off-by: Richard Henderson --- include/exec/cpu_ldst.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 11ba3778ba..71009f84f5 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -355,16 +355,6 @@ uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr); uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr); uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr); -static inline int cpu_ldsb_code(CPUArchState *env, abi_ptr addr) -{ - return (int8_t)cpu_ldub_code(env, addr); -} - -static inline int cpu_ldsw_code(CPUArchState *env, abi_ptr addr) -{ - return (int16_t)cpu_lduw_code(env, addr); -} - /** * tlb_vaddr_to_host: * @env: CPUArchState From c9290dfebfdba5c13baa5e1f10e13a1c876b0643 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 May 2024 09:03:41 +0000 Subject: [PATCH 0663/2884] tcg/loongarch64: Fill out tcg_out_{ld,st} for vector regs TCG register spill/fill uses tcg_out_ld/st with all types, not necessarily going through INDEX_op_{ld,st}_vec. Cc: qemu-stable@nongnu.org Fixes: 16288ded944 ("tcg/loongarch64: Lower basic tcg vec ops to LSX") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2336 Signed-off-by: Richard Henderson Reviewed-by: Song Gao Tested-by: Song Gao --- tcg/loongarch64/tcg-target.c.inc | 103 ++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 69c5b8ac4f..06ca1ab11c 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -808,18 +808,88 @@ static void tcg_out_ldst(TCGContext *s, LoongArchInsn opc, TCGReg data, } } -static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, intptr_t offset) { - bool is_32bit = type == TCG_TYPE_I32; - tcg_out_ldst(s, is_32bit ? OPC_LD_W : OPC_LD_D, arg, arg1, arg2); + switch (type) { + case TCG_TYPE_I32: + if (dest < TCG_REG_V0) { + tcg_out_ldst(s, OPC_LD_W, dest, base, offset); + } else { + tcg_out_dupm_vec(s, TCG_TYPE_I128, MO_32, dest, base, offset); + } + break; + case TCG_TYPE_I64: + if (dest < TCG_REG_V0) { + tcg_out_ldst(s, OPC_LD_D, dest, base, offset); + } else { + tcg_out_dupm_vec(s, TCG_TYPE_I128, MO_64, dest, base, offset); + } + break; + case TCG_TYPE_V128: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_vld(s, dest, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_vldx(s, dest, base, TCG_REG_TMP0); + } + break; + default: + g_assert_not_reached(); + } } -static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg src, + TCGReg base, intptr_t offset) { - bool is_32bit = type == TCG_TYPE_I32; - tcg_out_ldst(s, is_32bit ? OPC_ST_W : OPC_ST_D, arg, arg1, arg2); + switch (type) { + case TCG_TYPE_I32: + if (src < TCG_REG_V0) { + tcg_out_ldst(s, OPC_ST_W, src, base, offset); + } else { + /* TODO: Could use fst_s, fstx_s */ + if (offset < -0x100 || offset > 0xff || (offset & 3)) { + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_addi_d(s, TCG_REG_TMP0, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_add_d(s, TCG_REG_TMP0, TCG_REG_TMP0, base); + } + base = TCG_REG_TMP0; + offset = 0; + } + tcg_out_opc_vstelm_w(s, src, base, offset, 0); + } + break; + case TCG_TYPE_I64: + if (src < TCG_REG_V0) { + tcg_out_ldst(s, OPC_ST_D, src, base, offset); + } else { + /* TODO: Could use fst_d, fstx_d */ + if (offset < -0x100 || offset > 0xff || (offset & 7)) { + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_addi_d(s, TCG_REG_TMP0, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_add_d(s, TCG_REG_TMP0, TCG_REG_TMP0, base); + } + base = TCG_REG_TMP0; + offset = 0; + } + tcg_out_opc_vstelm_d(s, src, base, offset, 0); + } + break; + case TCG_TYPE_V128: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_vst(s, src, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_vstx(s, src, base, TCG_REG_TMP0); + } + break; + default: + g_assert_not_reached(); + } } static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, @@ -1740,7 +1810,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, { TCGType type = vecl + TCG_TYPE_V64; TCGArg a0, a1, a2, a3; - TCGReg temp = TCG_REG_TMP0; TCGReg temp_vec = TCG_VEC_TMP0; static const LoongArchInsn cmp_vec_insn[16][4] = { @@ -1820,22 +1889,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, switch (opc) { case INDEX_op_st_vec: - /* Try to fit vst imm */ - if (-0x800 <= a2 && a2 <= 0x7ff) { - tcg_out_opc_vst(s, a0, a1, a2); - } else { - tcg_out_movi(s, TCG_TYPE_I64, temp, a2); - tcg_out_opc_vstx(s, a0, a1, temp); - } + tcg_out_st(s, type, a0, a1, a2); break; case INDEX_op_ld_vec: - /* Try to fit vld imm */ - if (-0x800 <= a2 && a2 <= 0x7ff) { - tcg_out_opc_vld(s, a0, a1, a2); - } else { - tcg_out_movi(s, TCG_TYPE_I64, temp, a2); - tcg_out_opc_vldx(s, a0, a1, temp); - } + tcg_out_ld(s, type, a0, a1, a2); break; case INDEX_op_and_vec: tcg_out_opc_vand_v(s, a0, a1, a2); From b61603bfcb21a2dc4cec87a4ebd65916d1670058 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 16:53:57 -1000 Subject: [PATCH 0664/2884] target/hppa: Move cpu_get_tb_cpu_state out of line Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 42 ++++++++++++++++++++++++++++++++++++++++++ target/hppa/cpu.h | 43 ++----------------------------------------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 393a81988d..582036b31e 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -43,6 +43,48 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) return cpu->env.iaoq_f; } +void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + uint32_t flags = env->psw_n * PSW_N; + + /* TB lookup assumes that PC contains the complete virtual address. + If we leave space+offset separate, we'll get ITLB misses to an + incomplete virtual address. This also means that we must separate + out current cpu privilege from the low bits of IAOQ_F. */ +#ifdef CONFIG_USER_ONLY + *pc = env->iaoq_f & -4; + *cs_base = env->iaoq_b & -4; + flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; +#else + /* ??? E, T, H, L, B bits need to be here, when implemented. */ + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); + flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; + + *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); + *cs_base = env->iasq_f; + + /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero + low 32-bits of CS_BASE. This will succeed for all direct branches, + which is the primary case we care about -- using goto_tb within a page. + Failure is indicated by a zero difference. */ + if (env->iasq_f == env->iasq_b) { + target_long diff = env->iaoq_b - env->iaoq_f; + if (diff == (int32_t)diff) { + *cs_base |= (uint32_t)diff; + } + } + if ((env->sr[4] == env->sr[5]) + & (env->sr[4] == env->sr[6]) + & (env->sr[4] == env->sr[7])) { + flags |= TB_FLAG_SR_SAME; + } +#endif + + *pflags = flags; +} + static void hppa_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index fb2e4c4a98..61f1353133 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -314,47 +314,8 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 -static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - uint32_t flags = env->psw_n * PSW_N; - - /* TB lookup assumes that PC contains the complete virtual address. - If we leave space+offset separate, we'll get ITLB misses to an - incomplete virtual address. This also means that we must separate - out current cpu privilege from the low bits of IAOQ_F. */ -#ifdef CONFIG_USER_ONLY - *pc = env->iaoq_f & -4; - *cs_base = env->iaoq_b & -4; - flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#else - /* ??? E, T, H, L, B bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); - flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - - *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), - env->iaoq_f & -4); - *cs_base = env->iasq_f; - - /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero - low 32-bits of CS_BASE. This will succeed for all direct branches, - which is the primary case we care about -- using goto_tb within a page. - Failure is indicated by a zero difference. */ - if (env->iasq_f == env->iasq_b) { - target_long diff = env->iaoq_b - env->iaoq_f; - if (diff == (int32_t)diff) { - *cs_base |= (uint32_t)diff; - } - } - if ((env->sr[4] == env->sr[5]) - & (env->sr[4] == env->sr[6]) - & (env->sr[4] == env->sr[7])) { - flags |= TB_FLAG_SR_SAME; - } -#endif - - *pflags = flags; -} +void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags); target_ulong cpu_hppa_get_psw(CPUHPPAState *env); void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); From 19da5d125842e56f115873a4af06d59559db9bb6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 19:09:05 -1000 Subject: [PATCH 0665/2884] target/hppa: Use hppa_form_gva_psw in hppa_cpu_get_pc This function is for log_pc(), which needs to produce a similar result to cpu_get_tb_cpu_state(). Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 582036b31e..be8c558014 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -38,9 +38,10 @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value) static vaddr hppa_cpu_get_pc(CPUState *cs) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); - return cpu->env.iaoq_f; + return hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); } void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, @@ -61,8 +62,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), - env->iaoq_f & -4); + *pc = hppa_cpu_get_pc(env_cpu(env)); *cs_base = env->iasq_f; /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero From 4e31e68bb6e77a48e57aa3a27549313ea0f19f69 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Mar 2024 09:23:35 -1000 Subject: [PATCH 0666/2884] target/hppa: Move constant destination check into use_goto_tb Share this check between gen_goto_tb and hppa_tr_translate_insn. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6d45611888..398803981c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -662,9 +662,10 @@ static bool gen_illegal(DisasContext *ctx) } while (0) #endif -static bool use_goto_tb(DisasContext *ctx, uint64_t dest) +static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) { - return translator_use_goto_tb(&ctx->base, dest); + return (bofs != -1 && nofs != -1 && + translator_use_goto_tb(&ctx->base, bofs)); } /* If the next insn is to be nullified, and it's on the same page, @@ -678,16 +679,16 @@ static bool use_nullify_skip(DisasContext *ctx) } static void gen_goto_tb(DisasContext *ctx, int which, - uint64_t f, uint64_t b) + uint64_t b, uint64_t n) { - if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { + if (use_goto_tb(ctx, b, n)) { tcg_gen_goto_tb(which); - copy_iaoq_entry(ctx, cpu_iaoq_f, f, NULL); - copy_iaoq_entry(ctx, cpu_iaoq_b, b, NULL); + copy_iaoq_entry(ctx, cpu_iaoq_f, b, NULL); + copy_iaoq_entry(ctx, cpu_iaoq_b, n, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, f, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, b, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, n, ctx->iaoq_n_var); tcg_gen_lookup_and_goto_ptr(); } } @@ -4744,8 +4745,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Advance the insn queue. Note that this check also detects a priority change within the instruction queue. */ if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) { - if (ctx->iaoq_b != -1 && ctx->iaoq_n != -1 - && use_goto_tb(ctx, ctx->iaoq_b) + if (use_goto_tb(ctx, ctx->iaoq_b, ctx->iaoq_n) && (ctx->null_cond.c == TCG_COND_NEVER || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); From 2644f80ba5e0628af058072ed44b14e8d0bee115 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Mar 2024 20:02:42 -1000 Subject: [PATCH 0667/2884] target/hppa: Pass displacement to do_dbranch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass a displacement instead of an absolute value. In trans_be, remove the user-only do_dbranch case. The branch we are attempting to optimize is to the zero page, which is perforce on a different page than the code currently executing, which means that we will *not* use a goto_tb. Use a plain indirect branch instead, which is what we got out of the attempted direct branch anyway. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 398803981c..4c42b518c5 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1766,9 +1766,11 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt, /* Emit an unconditional branch to a direct target, which may or may not have already had nullification handled. */ -static bool do_dbranch(DisasContext *ctx, uint64_t dest, +static bool do_dbranch(DisasContext *ctx, int64_t disp, unsigned link, bool is_n) { + uint64_t dest = iaoq_dest(ctx, disp); + if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (link != 0) { copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); @@ -1815,10 +1817,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, /* Handle TRUE and NEVER as direct branches. */ if (c == TCG_COND_ALWAYS) { - return do_dbranch(ctx, dest, 0, is_n && disp >= 0); - } - if (c == TCG_COND_NEVER) { - return do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0); + return do_dbranch(ctx, disp, 0, is_n && disp >= 0); } taken = gen_new_label(); @@ -3914,22 +3913,6 @@ static bool trans_be(DisasContext *ctx, arg_be *a) { TCGv_i64 tmp; -#ifdef CONFIG_USER_ONLY - /* ??? It seems like there should be a good way of using - "be disp(sr2, r0)", the canonical gateway entry mechanism - to our advantage. But that appears to be inconvenient to - manage along side branch delay slots. Therefore we handle - entry into the gateway page via absolute address. */ - /* Since we don't implement spaces, just branch. Do notice the special - case of "be disp(*,r0)" using a direct branch to disp, so that we can - goto_tb to the TB containing the syscall. */ - if (a->b == 0) { - return do_dbranch(ctx, a->disp, a->l, a->n); - } -#else - nullify_over(ctx); -#endif - tmp = tcg_temp_new_i64(); tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); @@ -3939,6 +3922,8 @@ static bool trans_be(DisasContext *ctx, arg_be *a) #else TCGv_i64 new_spc = tcg_temp_new_i64(); + nullify_over(ctx); + load_spr(ctx, new_spc, a->sp); if (a->l) { copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); @@ -3968,7 +3953,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) static bool trans_bl(DisasContext *ctx, arg_bl *a) { - return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n); + return do_dbranch(ctx, a->disp, a->l, a->n); } static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) @@ -4022,7 +4007,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) save_gpr(ctx, a->l, tmp); } - return do_dbranch(ctx, dest, 0, a->n); + return do_dbranch(ctx, dest - iaoq_dest(ctx, 0), 0, a->n); } static bool trans_blr(DisasContext *ctx, arg_blr *a) @@ -4035,7 +4020,7 @@ static bool trans_blr(DisasContext *ctx, arg_blr *a) return do_ibranch(ctx, tmp, a->l, a->n); } else { /* BLR R0,RX is a good way to load PC+8 into RX. */ - return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n); + return do_dbranch(ctx, 0, a->l, a->n); } } From d582c1faa3dcf6a87736383536b38335796fa214 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 14:33:28 -1000 Subject: [PATCH 0668/2884] target/hppa: Allow prior nullification in do_ibranch Simplify the function by not attempting a conditional move on the branch destination -- just use nullify_over normally. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 73 +++++++++++------------------------------ 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4c42b518c5..140dfb747a 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1871,17 +1871,15 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, unsigned link, bool is_n) { - TCGv_i64 a0, a1, next, tmp; - TCGCond c; + TCGv_i64 next; - assert(ctx->null_lab == NULL); + if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { + next = tcg_temp_new_i64(); + tcg_gen_mov_i64(next, dest); - if (ctx->null_cond.c == TCG_COND_NEVER) { if (link != 0) { copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } - next = tcg_temp_new_i64(); - tcg_gen_mov_i64(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); @@ -1895,60 +1893,29 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, } ctx->iaoq_n = -1; ctx->iaoq_n_var = next; - } else if (is_n && use_nullify_skip(ctx)) { - /* The (conditional) branch, B, nullifies the next insn, N, - and we're allowed to skip execution N (no single-step or - tracepoint in effect). Since the goto_ptr that we must use - for the indirect branch consumes no special resources, we - can (conditionally) skip B and continue execution. */ - /* The use_nullify_skip test implies we have a known control path. */ - tcg_debug_assert(ctx->iaoq_b != -1); - tcg_debug_assert(ctx->iaoq_n != -1); + return true; + } - /* We do have to handle the non-local temporary, DEST, before - branching. Since IOAQ_F is not really live at this point, we - can simply store DEST optimistically. Similarly with IAOQ_B. */ + nullify_over(ctx); + + if (is_n && use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); next = tcg_temp_new_i64(); tcg_gen_addi_i64(next, dest, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); - - nullify_over(ctx); - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } - tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx); + nullify_set(ctx, 0); } else { - c = ctx->null_cond.c; - a0 = ctx->null_cond.a0; - a1 = ctx->null_cond.a1; - - tmp = tcg_temp_new_i64(); - next = tcg_temp_new_i64(); - - copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_movcond_i64(c, next, a0, a1, tmp, dest); - ctx->iaoq_n = -1; - ctx->iaoq_n_var = next; - - if (link != 0) { - tcg_gen_movcond_i64(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); - } - - if (is_n) { - /* The branch nullifies the next insn, which means the state of N - after the branch is the inverse of the state of N that applied - to the branch. */ - tcg_gen_setcond_i64(tcg_invert_cond(c), cpu_psw_n, a0, a1); - cond_free(&ctx->null_cond); - ctx->null_cond = cond_make_n(); - ctx->psw_n_nonzero = true; - } else { - cond_free(&ctx->null_cond); - } + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); + nullify_set(ctx, is_n); } - return true; + if (link != 0) { + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + } + + tcg_gen_lookup_and_goto_ptr(); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); } /* Implement From f9b11bc2e75de806f189f7a5d1e63ba735ef4207 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 16:59:40 -1000 Subject: [PATCH 0669/2884] target/hppa: Use CF_BP_PAGE instead of cpu_breakpoint_test The generic tcg driver will have already checked for breakpoints. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 140dfb747a..d272be0e6e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -674,8 +674,9 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) executing a TB that merely branches to the next TB. */ static bool use_nullify_skip(DisasContext *ctx) { - return (((ctx->iaoq_b ^ ctx->iaoq_f) & TARGET_PAGE_MASK) == 0 - && !cpu_breakpoint_test(ctx->cs, ctx->iaoq_b, BP_ANY)); + return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) + && ctx->iaoq_b != -1 + && is_same_page(&ctx->base, ctx->iaoq_b)); } static void gen_goto_tb(DisasContext *ctx, int which, From 85e6cda0082c6e8107b9141bfcfb041f7f2a738e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 18:40:54 -1000 Subject: [PATCH 0670/2884] target/hppa: Add install_iaq_entries Instead of two separate cpu_iaoq_entry calls, use one call to update both IAQ_Front and IAQ_Back. Simplify with an argument combination that automatically handles a simple increment from Front to Back. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 64 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index d272be0e6e..08d5e2a4bc 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -617,6 +617,23 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, } } +static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, + uint64_t ni, TCGv_i64 nv) +{ + copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); + + /* Allow ni variable, with nv null, to indicate a trivial advance. */ + if (ni != -1 || nv) { + copy_iaoq_entry(ctx, cpu_iaoq_b, ni, nv); + } else if (bi != -1) { + copy_iaoq_entry(ctx, cpu_iaoq_b, bi + 4, NULL); + } else { + tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, 4); + tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, + gva_offset_mask(ctx->tb_flags)); + } +} + static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) { return ctx->iaoq_f + disp + 8; @@ -629,8 +646,7 @@ static void gen_excp_1(int exception) static void gen_excp(DisasContext *ctx, int exception) { - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -684,12 +700,10 @@ static void gen_goto_tb(DisasContext *ctx, int which, { if (use_goto_tb(ctx, b, n)) { tcg_gen_goto_tb(which); - copy_iaoq_entry(ctx, cpu_iaoq_f, b, NULL); - copy_iaoq_entry(ctx, cpu_iaoq_b, n, NULL); + install_iaq_entries(ctx, b, NULL, n, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, b, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, n, ctx->iaoq_n_var); + install_iaq_entries(ctx, b, cpu_iaoq_b, n, ctx->iaoq_n_var); tcg_gen_lookup_and_goto_ptr(); } } @@ -1883,9 +1897,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, } if (is_n) { if (use_nullify_skip(ctx)) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); - tcg_gen_addi_i64(next, next, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); + install_iaq_entries(ctx, -1, next, -1, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1900,14 +1912,10 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, nullify_over(ctx); if (is_n && use_nullify_skip(ctx)) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); - next = tcg_temp_new_i64(); - tcg_gen_addi_i64(next, dest, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); + install_iaq_entries(ctx, -1, dest, -1, NULL); nullify_set(ctx, 0); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); nullify_set(ctx, is_n); } if (link != 0) { @@ -1998,9 +2006,7 @@ static void do_page_zero(DisasContext *ctx) tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); tmp = tcg_temp_new_i64(); tcg_gen_ori_i64(tmp, cpu_gr[31], 3); - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); - tcg_gen_addi_i64(tmp, tmp, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); + install_iaq_entries(ctx, -1, tmp, -1, NULL); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -2744,8 +2750,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) nullify_over(ctx); /* Advance the instruction queue. */ - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, + ctx->iaoq_n, ctx->iaoq_n_var); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ @@ -3898,18 +3904,15 @@ static bool trans_be(DisasContext *ctx, arg_be *a) tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); } if (a->n && use_nullify_skip(ctx)) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); - tcg_gen_addi_i64(tmp, tmp, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); + install_iaq_entries(ctx, -1, tmp, -1, NULL); tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); nullify_set(ctx, 0); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, tmp); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, a->n); } @@ -4018,11 +4021,10 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) nullify_over(ctx); dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); if (a->l) { copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); @@ -4721,8 +4723,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: if (ctx->iaoq_f == -1) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + install_iaq_entries(ctx, -1, cpu_iaoq_b, + ctx->iaoq_n, ctx->iaoq_n_var); #ifndef CONFIG_USER_ONLY tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); #endif @@ -4751,8 +4753,8 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_TOO_MANY: case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, + ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); /* FALLTHRU */ case DISAS_IAQ_N_UPDATED: From 43541db0c40dad6291ce1dcb76b7729f55704893 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 20:30:12 -1000 Subject: [PATCH 0671/2884] target/hppa: Add install_link Add a common routine for writing the return address. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 55 ++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 08d5e2a4bc..220665d98e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -634,6 +634,24 @@ static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, } } +static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) +{ + tcg_debug_assert(ctx->null_cond.c == TCG_COND_NEVER); + if (!link) { + return; + } + if (ctx->iaoq_b == -1) { + tcg_gen_addi_i64(cpu_gr[link], cpu_iaoq_b, 4); + } else { + tcg_gen_movi_i64(cpu_gr[link], ctx->iaoq_b + 4); + } +#ifndef CONFIG_USER_ONLY + if (with_sr0) { + tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); + } +#endif +} + static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) { return ctx->iaoq_f + disp + 8; @@ -1787,9 +1805,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, uint64_t dest = iaoq_dest(ctx, disp); if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } + install_link(ctx, link, false); ctx->iaoq_n = dest; if (is_n) { ctx->null_cond.c = TCG_COND_ALWAYS; @@ -1797,10 +1813,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, } else { nullify_over(ctx); - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } - + install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); gen_goto_tb(ctx, 0, dest, dest + 4); @@ -1892,9 +1905,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } + install_link(ctx, link, false); if (is_n) { if (use_nullify_skip(ctx)) { install_iaq_entries(ctx, -1, next, -1, NULL); @@ -1911,16 +1922,17 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, nullify_over(ctx); + next = tcg_temp_new_i64(); + tcg_gen_mov_i64(next, dest); + + install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, dest, -1, NULL); + install_iaq_entries(ctx, -1, next, -1, NULL); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, next); nullify_set(ctx, is_n); } - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -3899,10 +3911,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) nullify_over(ctx); load_spr(ctx, new_spc, a->sp); - if (a->l) { - copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); - } + install_link(ctx, a->l, true); if (a->n && use_nullify_skip(ctx)) { install_iaq_entries(ctx, -1, tmp, -1, NULL); tcg_gen_mov_i64(cpu_iasq_f, new_spc); @@ -4019,16 +4028,16 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) return do_ibranch(ctx, dest, a->l, a->n); #else nullify_over(ctx); - dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); + dest = tcg_temp_new_i64(); + tcg_gen_mov_i64(dest, load_gpr(ctx, a->b)); + dest = do_ibranch_priv(ctx, dest); + install_link(ctx, a->l, false); install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); - if (a->l) { - copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); - } nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; From 0dcd6640e49c3cbd534e5a1d39338bcd6e691e18 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 15:38:32 -1000 Subject: [PATCH 0672/2884] target/hppa: Delay computation of IAQ_Next We no longer have to allocate a temp and perform an addition before translation of the rest of the insn. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 220665d98e..9189e0350b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1807,6 +1807,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { install_link(ctx, link, false); ctx->iaoq_n = dest; + ctx->iaoq_n_var = NULL; if (is_n) { ctx->null_cond.c = TCG_COND_ALWAYS; } @@ -1863,11 +1864,6 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, ctx->null_lab = NULL; } nullify_set(ctx, n); - if (ctx->iaoq_n == -1) { - /* The temporary iaoq_n_var died at the branch above. - Regenerate it here instead of saving it. */ - tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); - } gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); } @@ -4631,8 +4627,6 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); #endif - ctx->iaoq_n = -1; - ctx->iaoq_n_var = NULL; ctx->zero = tcg_constant_i64(0); @@ -4684,14 +4678,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Set up the IA queue for the next insn. This will be overwritten by a branch. */ - if (ctx->iaoq_b == -1) { - ctx->iaoq_n = -1; - ctx->iaoq_n_var = tcg_temp_new_i64(); - tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); - } else { - ctx->iaoq_n = ctx->iaoq_b + 4; - ctx->iaoq_n_var = NULL; - } + ctx->iaoq_n_var = NULL; + ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4742,7 +4730,13 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ? DISAS_EXIT : DISAS_IAQ_N_UPDATED); } else if (ctx->iaoq_b == -1) { - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); + if (ctx->iaoq_n_var) { + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); + } else { + tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_b, 4); + tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, + gva_offset_mask(ctx->tb_flags)); + } } break; From d08ad0e0f08d3b6561be83beb992b9835aa47895 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 13:07:06 -0700 Subject: [PATCH 0673/2884] target/hppa: Skip nullified insns in unconditional dbranch path Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 9189e0350b..6c5efb0023 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1806,11 +1806,17 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { install_link(ctx, link, false); - ctx->iaoq_n = dest; - ctx->iaoq_n_var = NULL; if (is_n) { + if (use_nullify_skip(ctx)) { + nullify_set(ctx, 0); + gen_goto_tb(ctx, 0, dest, dest + 4); + ctx->base.is_jmp = DISAS_NORETURN; + return true; + } ctx->null_cond.c = TCG_COND_ALWAYS; } + ctx->iaoq_n = dest; + ctx->iaoq_n_var = NULL; } else { nullify_over(ctx); From dbdccbdf8167881be9974fc582b86ffc6f69ffa1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 20:13:40 -1000 Subject: [PATCH 0674/2884] target/hppa: Simplify TB end Minimize the amount of code in hppa_tr_translate_insn advancing the insn queue for the next insn. Move the goto_tb path to hppa_tr_tb_stop. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 109 +++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6c5efb0023..b79c44bd49 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4700,54 +4700,31 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } } - /* Advance the insn queue. Note that this check also detects - a priority change within the instruction queue. */ - if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) { - if (use_goto_tb(ctx, ctx->iaoq_b, ctx->iaoq_n) - && (ctx->null_cond.c == TCG_COND_NEVER - || ctx->null_cond.c == TCG_COND_ALWAYS)) { - nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); - gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); - ctx->base.is_jmp = ret = DISAS_NORETURN; - } else { - ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE; - } + /* If the TranslationBlock must end, do so. */ + ctx->base.pc_next += 4; + if (ret != DISAS_NEXT) { + return; } + /* Note this also detects a priority change. */ + if (ctx->iaoq_b != ctx->iaoq_f + 4) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + return; + } + + /* + * Advance the insn queue. + * The only exit now is DISAS_TOO_MANY from the translator loop. + */ ctx->iaoq_f = ctx->iaoq_b; ctx->iaoq_b = ctx->iaoq_n; - ctx->base.pc_next += 4; - - switch (ret) { - case DISAS_NORETURN: - case DISAS_IAQ_N_UPDATED: - break; - - case DISAS_NEXT: - case DISAS_IAQ_N_STALE: - case DISAS_IAQ_N_STALE_EXIT: - if (ctx->iaoq_f == -1) { - install_iaq_entries(ctx, -1, cpu_iaoq_b, - ctx->iaoq_n, ctx->iaoq_n_var); -#ifndef CONFIG_USER_ONLY - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); -#endif - nullify_save(ctx); - ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT - ? DISAS_EXIT - : DISAS_IAQ_N_UPDATED); - } else if (ctx->iaoq_b == -1) { - if (ctx->iaoq_n_var) { - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); - } else { - tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_b, 4); - tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, - gva_offset_mask(ctx->tb_flags)); - } + if (ctx->iaoq_b == -1) { + if (ctx->iaoq_n_var) { + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); + } else { + tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_b, 4); + tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, + gva_offset_mask(ctx->tb_flags)); } - break; - - default: - g_assert_not_reached(); } } @@ -4755,23 +4732,51 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasJumpType is_jmp = ctx->base.is_jmp; + uint64_t fi, bi; + TCGv_i64 fv, bv; + TCGv_i64 fs; + + /* Assume the insn queue has not been advanced. */ + fi = ctx->iaoq_b; + fv = cpu_iaoq_b; + fs = fi == -1 ? cpu_iasq_b : NULL; + bi = ctx->iaoq_n; + bv = ctx->iaoq_n_var; switch (is_jmp) { case DISAS_NORETURN: break; case DISAS_TOO_MANY: - case DISAS_IAQ_N_STALE: - case DISAS_IAQ_N_STALE_EXIT: - install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, - ctx->iaoq_b, cpu_iaoq_b); - nullify_save(ctx); + /* The insn queue has not been advanced. */ + bi = fi; + bv = fv; + fi = ctx->iaoq_f; + fv = NULL; + fs = NULL; /* FALLTHRU */ - case DISAS_IAQ_N_UPDATED: - if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_lookup_and_goto_ptr(); + case DISAS_IAQ_N_STALE: + if (use_goto_tb(ctx, fi, bi) + && (ctx->null_cond.c == TCG_COND_NEVER + || ctx->null_cond.c == TCG_COND_ALWAYS)) { + nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); + gen_goto_tb(ctx, 0, fi, bi); break; } /* FALLTHRU */ + case DISAS_IAQ_N_STALE_EXIT: + install_iaq_entries(ctx, fi, fv, bi, bv); + if (fs) { + tcg_gen_mov_i64(cpu_iasq_f, fs); + } + nullify_save(ctx); + if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { + tcg_gen_exit_tb(NULL, 0); + break; + } + /* FALLTHRU */ + case DISAS_IAQ_N_UPDATED: + tcg_gen_lookup_and_goto_ptr(); + break; case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); break; From 142faf5fab337b809b84890579157c9064668011 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 20:45:21 -1000 Subject: [PATCH 0675/2884] target/hppa: Add IASQ entries to DisasContext Add variable to track space changes to IAQ. So far, no such changes are introduced, but the new checks vs ctx->iasq_b may eliminate an unnecessary copy to cpu_iasq_f with e.g. BLR. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b79c44bd49..b4e384baa3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -50,6 +50,13 @@ typedef struct DisasContext { uint64_t iaoq_b; uint64_t iaoq_n; TCGv_i64 iaoq_n_var; + /* + * Null when IASQ_Back unchanged from IASQ_Front, + * or cpu_iasq_b, when IASQ_Back has been changed. + */ + TCGv_i64 iasq_b; + /* Null when IASQ_Next unchanged from IASQ_Back, or set by branch. */ + TCGv_i64 iasq_n; DisasCond null_cond; TCGLabel *null_lab; @@ -3917,12 +3924,12 @@ static bool trans_be(DisasContext *ctx, arg_be *a) if (a->n && use_nullify_skip(ctx)) { install_iaq_entries(ctx, -1, tmp, -1, NULL); tcg_gen_mov_i64(cpu_iasq_f, new_spc); - tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); + tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, 0); } else { install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, tmp); - if (ctx->iaoq_b == -1) { - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); + if (ctx->iasq_b) { + tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); } tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, a->n); @@ -4036,8 +4043,8 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) install_link(ctx, a->l, false); install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); - if (ctx->iaoq_b == -1) { - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); + if (ctx->iasq_b) { + tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); } tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); nullify_set(ctx, a->n); @@ -4618,6 +4625,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->mmu_idx = MMU_USER_IDX; ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; + ctx->iasq_b = NULL; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; @@ -4632,6 +4640,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); + ctx->iasq_b = (diff ? NULL : cpu_iasq_b); #endif ctx->zero = tcg_constant_i64(0); @@ -4684,6 +4693,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Set up the IA queue for the next insn. This will be overwritten by a branch. */ + ctx->iasq_n = NULL; ctx->iaoq_n_var = NULL; ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; @@ -4706,7 +4716,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) return; } /* Note this also detects a priority change. */ - if (ctx->iaoq_b != ctx->iaoq_f + 4) { + if (ctx->iaoq_b != ctx->iaoq_f + 4 || ctx->iasq_b) { ctx->base.is_jmp = DISAS_IAQ_N_STALE; return; } @@ -4726,6 +4736,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) gva_offset_mask(ctx->tb_flags)); } } + if (ctx->iasq_n) { + tcg_gen_mov_i64(cpu_iasq_b, ctx->iasq_n); + ctx->iasq_b = cpu_iasq_b; + } } static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) @@ -4734,14 +4748,15 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) DisasJumpType is_jmp = ctx->base.is_jmp; uint64_t fi, bi; TCGv_i64 fv, bv; - TCGv_i64 fs; + TCGv_i64 fs, bs; /* Assume the insn queue has not been advanced. */ fi = ctx->iaoq_b; fv = cpu_iaoq_b; - fs = fi == -1 ? cpu_iasq_b : NULL; + fs = ctx->iasq_b; bi = ctx->iaoq_n; bv = ctx->iaoq_n_var; + bs = ctx->iasq_n; switch (is_jmp) { case DISAS_NORETURN: @@ -4750,12 +4765,15 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* The insn queue has not been advanced. */ bi = fi; bv = fv; + bs = fs; fi = ctx->iaoq_f; fv = NULL; fs = NULL; /* FALLTHRU */ case DISAS_IAQ_N_STALE: - if (use_goto_tb(ctx, fi, bi) + if (fs == NULL + && bs == NULL + && use_goto_tb(ctx, fi, bi) && (ctx->null_cond.c == TCG_COND_NEVER || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); @@ -4768,6 +4786,9 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) if (fs) { tcg_gen_mov_i64(cpu_iasq_f, fs); } + if (bs) { + tcg_gen_mov_i64(cpu_iasq_b, bs); + } nullify_save(ctx); if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { tcg_gen_exit_tb(NULL, 0); From 588deeda6e7cc2c401ca3cbf22383f019a69ec14 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 21:04:24 -1000 Subject: [PATCH 0676/2884] target/hppa: Add space arguments to install_iaq_entries Move space assighments to a central location. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 58 +++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b4e384baa3..eed0f92db4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -624,8 +624,9 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, } } -static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, - uint64_t ni, TCGv_i64 nv) +static void install_iaq_entries(DisasContext *ctx, + uint64_t bi, TCGv_i64 bv, TCGv_i64 bs, + uint64_t ni, TCGv_i64 nv, TCGv_i64 ns) { copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); @@ -639,6 +640,12 @@ static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, gva_offset_mask(ctx->tb_flags)); } + if (bs) { + tcg_gen_mov_i64(cpu_iasq_f, bs); + } + if (ns || bs) { + tcg_gen_mov_i64(cpu_iasq_b, ns ? ns : bs); + } } static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) @@ -671,7 +678,8 @@ static void gen_excp_1(int exception) static void gen_excp(DisasContext *ctx, int exception) { - install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, NULL, + ctx->iaoq_b, cpu_iaoq_b, NULL); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -725,10 +733,11 @@ static void gen_goto_tb(DisasContext *ctx, int which, { if (use_goto_tb(ctx, b, n)) { tcg_gen_goto_tb(which); - install_iaq_entries(ctx, b, NULL, n, NULL); + install_iaq_entries(ctx, b, NULL, NULL, n, NULL, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - install_iaq_entries(ctx, b, cpu_iaoq_b, n, ctx->iaoq_n_var); + install_iaq_entries(ctx, b, cpu_iaoq_b, ctx->iasq_b, + n, ctx->iaoq_n_var, ctx->iasq_n); tcg_gen_lookup_and_goto_ptr(); } } @@ -1917,7 +1926,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, install_link(ctx, link, false); if (is_n) { if (use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, -1, NULL); + install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1936,10 +1945,11 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, -1, NULL); + install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, next); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + -1, next, NULL); nullify_set(ctx, is_n); } @@ -2027,7 +2037,7 @@ static void do_page_zero(DisasContext *ctx) tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); tmp = tcg_temp_new_i64(); tcg_gen_ori_i64(tmp, cpu_gr[31], 3); - install_iaq_entries(ctx, -1, tmp, -1, NULL); + install_iaq_entries(ctx, -1, tmp, NULL, -1, NULL, NULL); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -2771,8 +2781,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) nullify_over(ctx); /* Advance the instruction queue. */ - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, - ctx->iaoq_n, ctx->iaoq_n_var); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + ctx->iaoq_n, ctx->iaoq_n_var, ctx->iasq_n); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ @@ -3922,16 +3932,11 @@ static bool trans_be(DisasContext *ctx, arg_be *a) load_spr(ctx, new_spc, a->sp); install_link(ctx, a->l, true); if (a->n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, tmp, -1, NULL); - tcg_gen_mov_i64(cpu_iasq_f, new_spc); - tcg_gen_mov_i64(cpu_iasq_b, new_spc); + install_iaq_entries(ctx, -1, tmp, new_spc, -1, NULL, new_spc); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, tmp); - if (ctx->iasq_b) { - tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); - } - tcg_gen_mov_i64(cpu_iasq_b, new_spc); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + -1, tmp, new_spc); nullify_set(ctx, a->n); } tcg_gen_lookup_and_goto_ptr(); @@ -4042,11 +4047,8 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) dest = do_ibranch_priv(ctx, dest); install_link(ctx, a->l, false); - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); - if (ctx->iasq_b) { - tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); - } - tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + -1, dest, space_select(ctx, 0, dest)); nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -4782,13 +4784,7 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } /* FALLTHRU */ case DISAS_IAQ_N_STALE_EXIT: - install_iaq_entries(ctx, fi, fv, bi, bv); - if (fs) { - tcg_gen_mov_i64(cpu_iasq_f, fs); - } - if (bs) { - tcg_gen_mov_i64(cpu_iasq_b, bs); - } + install_iaq_entries(ctx, fi, fv, fs, bi, bv, bs); nullify_save(ctx); if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { tcg_gen_exit_tb(NULL, 0); From 019f41591d95ffd7f1d49ace4626aec2cbec19d8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 21:29:47 -1000 Subject: [PATCH 0677/2884] target/hppa: Add space argument to do_ibranch This allows unification of BE, BLR, BV, BVE with a common helper. Since we can now track space with IAQ_Next, we can now let the TranslationBlock continue across the delay slot with BE, BVE. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 76 ++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index eed0f92db4..1758c6e1d4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1914,8 +1914,8 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, /* Emit an unconditional branch to an indirect target. This handles nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, - unsigned link, bool is_n) +static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, + unsigned link, bool with_sr0, bool is_n) { TCGv_i64 next; @@ -1923,10 +1923,10 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); - install_link(ctx, link, false); + install_link(ctx, link, with_sr0); if (is_n) { if (use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); + install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1935,6 +1935,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, } ctx->iaoq_n = -1; ctx->iaoq_n_var = next; + ctx->iasq_n = dspc; return true; } @@ -1943,13 +1944,13 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); - install_link(ctx, link, false); + install_link(ctx, link, with_sr0); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); + install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); nullify_set(ctx, 0); } else { install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, next, NULL); + -1, next, dspc); nullify_set(ctx, is_n); } @@ -3916,33 +3917,18 @@ static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a) static bool trans_be(DisasContext *ctx, arg_be *a) { - TCGv_i64 tmp; + TCGv_i64 dest = tcg_temp_new_i64(); + TCGv_i64 space = NULL; - tmp = tcg_temp_new_i64(); - tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); - tmp = do_ibranch_priv(ctx, tmp); + tcg_gen_addi_i64(dest, load_gpr(ctx, a->b), a->disp); + dest = do_ibranch_priv(ctx, dest); -#ifdef CONFIG_USER_ONLY - return do_ibranch(ctx, tmp, a->l, a->n); -#else - TCGv_i64 new_spc = tcg_temp_new_i64(); - - nullify_over(ctx); - - load_spr(ctx, new_spc, a->sp); - install_link(ctx, a->l, true); - if (a->n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, tmp, new_spc, -1, NULL, new_spc); - nullify_set(ctx, 0); - } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, tmp, new_spc); - nullify_set(ctx, a->n); - } - tcg_gen_lookup_and_goto_ptr(); - ctx->base.is_jmp = DISAS_NORETURN; - return nullify_end(ctx); +#ifndef CONFIG_USER_ONLY + space = tcg_temp_new_i64(); + load_spr(ctx, space, a->sp); #endif + + return do_ibranch(ctx, dest, space, a->l, true, a->n); } static bool trans_bl(DisasContext *ctx, arg_bl *a) @@ -4011,7 +3997,7 @@ static bool trans_blr(DisasContext *ctx, arg_blr *a) tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ - return do_ibranch(ctx, tmp, a->l, a->n); + return do_ibranch(ctx, tmp, NULL, a->l, false, a->n); } else { /* BLR R0,RX is a good way to load PC+8 into RX. */ return do_dbranch(ctx, 0, a->l, a->n); @@ -4030,30 +4016,20 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); - return do_ibranch(ctx, dest, 0, a->n); + return do_ibranch(ctx, dest, NULL, 0, false, a->n); } static bool trans_bve(DisasContext *ctx, arg_bve *a) { - TCGv_i64 dest; + TCGv_i64 b = load_gpr(ctx, a->b); + TCGv_i64 dest = do_ibranch_priv(ctx, b); + TCGv_i64 space = NULL; -#ifdef CONFIG_USER_ONLY - dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - return do_ibranch(ctx, dest, a->l, a->n); -#else - nullify_over(ctx); - dest = tcg_temp_new_i64(); - tcg_gen_mov_i64(dest, load_gpr(ctx, a->b)); - dest = do_ibranch_priv(ctx, dest); - - install_link(ctx, a->l, false); - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, dest, space_select(ctx, 0, dest)); - nullify_set(ctx, a->n); - tcg_gen_lookup_and_goto_ptr(); - ctx->base.is_jmp = DISAS_NORETURN; - return nullify_end(ctx); +#ifndef CONFIG_USER_ONLY + space = space_select(ctx, 0, b); #endif + + return do_ibranch(ctx, dest, space, a->l, false, a->n); } static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) From 0bb0202962a5f77d1e4cac4533628bb7a566de54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 12:53:06 -1000 Subject: [PATCH 0678/2884] target/hppa: Use umax in do_ibranch_priv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using umax is clearer than the same operation using movcond. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1758c6e1d4..e9ba792065 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1982,7 +1982,7 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) dest = tcg_temp_new_i64(); tcg_gen_andi_i64(dest, offset, -4); tcg_gen_ori_i64(dest, dest, ctx->privilege); - tcg_gen_movcond_i64(TCG_COND_GTU, dest, dest, offset, dest, offset); + tcg_gen_umax_i64(dest, dest, offset); break; } return dest; From 1874e6c2fdb351120c234e6840729c9553d77d05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 12:50:07 -1000 Subject: [PATCH 0679/2884] target/hppa: Always make a copy in do_ibranch_priv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies callers, which might otherwise have to make another copy. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e9ba792065..1ede4bd725 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1968,18 +1968,17 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, */ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) { - TCGv_i64 dest; + TCGv_i64 dest = tcg_temp_new_i64(); switch (ctx->privilege) { case 0: /* Privilege 0 is maximum and is allowed to decrease. */ - return offset; + tcg_gen_mov_i64(dest, offset); + break; case 3: /* Privilege 3 is minimum and is never allowed to increase. */ - dest = tcg_temp_new_i64(); tcg_gen_ori_i64(dest, offset, 3); break; default: - dest = tcg_temp_new_i64(); tcg_gen_andi_i64(dest, offset, -4); tcg_gen_ori_i64(dest, dest, ctx->privilege); tcg_gen_umax_i64(dest, dest, offset); From bc921866cefb3ec4031714aeb4569e0e7622dfba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 13:56:44 -1000 Subject: [PATCH 0680/2884] target/hppa: Introduce and use DisasIAQE for branch management Wrap offset and space together in one structure, ensuring that they're copied together as required. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 378 +++++++++++++++++++++------------------- 1 file changed, 198 insertions(+), 180 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1ede4bd725..e0e4db75ee 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -42,21 +42,23 @@ typedef struct DisasCond { TCGv_i64 a0, a1; } DisasCond; +typedef struct DisasIAQE { + /* IASQ; may be null for no change from TB. */ + TCGv_i64 space; + /* IAOQ base; may be null for immediate absolute address. */ + TCGv_i64 base; + /* IAOQ addend; absolute immedate address if base is null. */ + int64_t disp; +} DisasIAQE; + typedef struct DisasContext { DisasContextBase base; CPUState *cs; - uint64_t iaoq_f; - uint64_t iaoq_b; - uint64_t iaoq_n; - TCGv_i64 iaoq_n_var; - /* - * Null when IASQ_Back unchanged from IASQ_Front, - * or cpu_iasq_b, when IASQ_Back has been changed. - */ - TCGv_i64 iasq_b; - /* Null when IASQ_Next unchanged from IASQ_Back, or set by branch. */ - TCGv_i64 iasq_n; + /* IAQ_Front, IAQ_Back. */ + DisasIAQE iaq_f, iaq_b; + /* IAQ_Next, for jumps, otherwise null for simple advance. */ + DisasIAQE iaq_j, *iaq_n; DisasCond null_cond; TCGLabel *null_lab; @@ -602,49 +604,67 @@ static bool nullify_end(DisasContext *ctx) return true; } +static bool iaqe_variable(const DisasIAQE *e) +{ + return e->base || e->space; +} + +static DisasIAQE iaqe_incr(const DisasIAQE *e, int64_t disp) +{ + return (DisasIAQE){ + .space = e->space, + .base = e->base, + .disp = e->disp + disp, + }; +} + +static DisasIAQE iaqe_branchi(DisasContext *ctx, int64_t disp) +{ + return (DisasIAQE){ + .space = ctx->iaq_b.space, + .disp = ctx->iaq_f.disp + 8 + disp, + }; +} + +static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var) +{ + return (DisasIAQE){ + .space = ctx->iaq_b.space, + .base = var, + }; +} + static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, - uint64_t ival, TCGv_i64 vval) + const DisasIAQE *src) { uint64_t mask = gva_offset_mask(ctx->tb_flags); - if (ival != -1) { - tcg_gen_movi_i64(dest, ival & mask); - return; - } - tcg_debug_assert(vval != NULL); - - /* - * We know that the IAOQ is already properly masked. - * This optimization is primarily for "iaoq_f = iaoq_b". - */ - if (vval == cpu_iaoq_f || vval == cpu_iaoq_b) { - tcg_gen_mov_i64(dest, vval); + if (src->base == NULL) { + tcg_gen_movi_i64(dest, src->disp & mask); + } else if (src->disp == 0) { + tcg_gen_andi_i64(dest, src->base, mask); } else { - tcg_gen_andi_i64(dest, vval, mask); + tcg_gen_addi_i64(dest, src->base, src->disp); + tcg_gen_andi_i64(dest, dest, mask); } } -static void install_iaq_entries(DisasContext *ctx, - uint64_t bi, TCGv_i64 bv, TCGv_i64 bs, - uint64_t ni, TCGv_i64 nv, TCGv_i64 ns) +static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, + const DisasIAQE *b) { - copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); + DisasIAQE b_next; - /* Allow ni variable, with nv null, to indicate a trivial advance. */ - if (ni != -1 || nv) { - copy_iaoq_entry(ctx, cpu_iaoq_b, ni, nv); - } else if (bi != -1) { - copy_iaoq_entry(ctx, cpu_iaoq_b, bi + 4, NULL); - } else { - tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, 4); - tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, - gva_offset_mask(ctx->tb_flags)); + if (b == NULL) { + b_next = iaqe_incr(f, 4); + b = &b_next; } - if (bs) { - tcg_gen_mov_i64(cpu_iasq_f, bs); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + copy_iaoq_entry(ctx, cpu_iaoq_b, b); + if (f->space) { + tcg_gen_mov_i64(cpu_iasq_f, f->space); } - if (ns || bs) { - tcg_gen_mov_i64(cpu_iasq_b, ns ? ns : bs); + if (b->space || f->space) { + tcg_gen_mov_i64(cpu_iasq_b, b->space ? : f->space); } } @@ -654,10 +674,11 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) if (!link) { return; } - if (ctx->iaoq_b == -1) { - tcg_gen_addi_i64(cpu_gr[link], cpu_iaoq_b, 4); + if (ctx->iaq_b.base) { + tcg_gen_addi_i64(cpu_gr[link], ctx->iaq_b.base, + ctx->iaq_b.disp + 4); } else { - tcg_gen_movi_i64(cpu_gr[link], ctx->iaoq_b + 4); + tcg_gen_movi_i64(cpu_gr[link], ctx->iaq_b.disp + 4); } #ifndef CONFIG_USER_ONLY if (with_sr0) { @@ -666,11 +687,6 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) #endif } -static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) -{ - return ctx->iaoq_f + disp + 8; -} - static void gen_excp_1(int exception) { gen_helper_excp(tcg_env, tcg_constant_i32(exception)); @@ -678,8 +694,7 @@ static void gen_excp_1(int exception) static void gen_excp(DisasContext *ctx, int exception) { - install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, NULL, - ctx->iaoq_b, cpu_iaoq_b, NULL); + install_iaq_entries(ctx, &ctx->iaq_f, &ctx->iaq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -711,10 +726,12 @@ static bool gen_illegal(DisasContext *ctx) } while (0) #endif -static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) +static bool use_goto_tb(DisasContext *ctx, const DisasIAQE *f, + const DisasIAQE *b) { - return (bofs != -1 && nofs != -1 && - translator_use_goto_tb(&ctx->base, bofs)); + return (!iaqe_variable(f) && + (b == NULL || !iaqe_variable(b)) && + translator_use_goto_tb(&ctx->base, f->disp)); } /* If the next insn is to be nullified, and it's on the same page, @@ -724,20 +741,19 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) static bool use_nullify_skip(DisasContext *ctx) { return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) - && ctx->iaoq_b != -1 - && is_same_page(&ctx->base, ctx->iaoq_b)); + && !iaqe_variable(&ctx->iaq_b) + && is_same_page(&ctx->base, ctx->iaq_b.disp)); } static void gen_goto_tb(DisasContext *ctx, int which, - uint64_t b, uint64_t n) + const DisasIAQE *f, const DisasIAQE *b) { - if (use_goto_tb(ctx, b, n)) { + if (use_goto_tb(ctx, f, b)) { tcg_gen_goto_tb(which); - install_iaq_entries(ctx, b, NULL, NULL, n, NULL, NULL); + install_iaq_entries(ctx, f, b); tcg_gen_exit_tb(ctx->base.tb, which); } else { - install_iaq_entries(ctx, b, cpu_iaoq_b, ctx->iasq_b, - n, ctx->iaoq_n_var, ctx->iasq_n); + install_iaq_entries(ctx, f, b); tcg_gen_lookup_and_goto_ptr(); } } @@ -1818,37 +1834,35 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt, static bool do_dbranch(DisasContext *ctx, int64_t disp, unsigned link, bool is_n) { - uint64_t dest = iaoq_dest(ctx, disp); + ctx->iaq_j = iaqe_branchi(ctx, disp); if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { install_link(ctx, link, false); if (is_n) { if (use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 0, dest, dest + 4); + gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); ctx->base.is_jmp = DISAS_NORETURN; return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } - ctx->iaoq_n = dest; - ctx->iaoq_n_var = NULL; + ctx->iaq_n = &ctx->iaq_j; } else { nullify_over(ctx); install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 0, dest, dest + 4); + gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); } else { nullify_set(ctx, is_n); - gen_goto_tb(ctx, 0, ctx->iaoq_b, dest); + gen_goto_tb(ctx, 0, &ctx->iaq_b, &ctx->iaq_j); } - nullify_end(ctx); nullify_set(ctx, 0); - gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n); + gen_goto_tb(ctx, 1, &ctx->iaq_b, NULL); ctx->base.is_jmp = DISAS_NORETURN; } return true; @@ -1859,7 +1873,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, DisasCond *cond) { - uint64_t dest = iaoq_dest(ctx, disp); + DisasIAQE next; TCGLabel *taken = NULL; TCGCond c = cond->c; bool n; @@ -1879,26 +1893,29 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, n = is_n && disp < 0; if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 0, ctx->iaoq_n, ctx->iaoq_n + 4); + next = iaqe_incr(&ctx->iaq_b, 4); + gen_goto_tb(ctx, 0, &next, NULL); } else { if (!n && ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; } nullify_set(ctx, n); - gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); + gen_goto_tb(ctx, 0, &ctx->iaq_b, NULL); } gen_set_label(taken); /* Taken: Condition satisfied; nullify on forward branches. */ n = is_n && disp >= 0; + + next = iaqe_branchi(ctx, disp); if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 1, dest, dest + 4); + gen_goto_tb(ctx, 1, &next, NULL); } else { nullify_set(ctx, n); - gen_goto_tb(ctx, 1, ctx->iaoq_b, dest); + gen_goto_tb(ctx, 1, &ctx->iaq_b, &next); } /* Not taken: the branch itself was nullified. */ @@ -1912,45 +1929,36 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, return true; } -/* Emit an unconditional branch to an indirect target. This handles - nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, - unsigned link, bool with_sr0, bool is_n) +/* + * Emit an unconditional branch to an indirect target, in ctx->iaq_j. + * This handles nullification of the branch itself. + */ +static bool do_ibranch(DisasContext *ctx, unsigned link, + bool with_sr0, bool is_n) { - TCGv_i64 next; - if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { - next = tcg_temp_new_i64(); - tcg_gen_mov_i64(next, dest); - install_link(ctx, link, with_sr0); if (is_n) { if (use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); + install_iaq_entries(ctx, &ctx->iaq_j, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } - ctx->iaoq_n = -1; - ctx->iaoq_n_var = next; - ctx->iasq_n = dspc; + ctx->iaq_n = &ctx->iaq_j; return true; } nullify_over(ctx); - next = tcg_temp_new_i64(); - tcg_gen_mov_i64(next, dest); - install_link(ctx, link, with_sr0); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); + install_iaq_entries(ctx, &ctx->iaq_j, NULL); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, next, dspc); + install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j); nullify_set(ctx, is_n); } @@ -1997,8 +2005,6 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { - TCGv_i64 tmp; - /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the next insn within the privileged page. */ @@ -2018,11 +2024,11 @@ static void do_page_zero(DisasContext *ctx) non-sequential instruction execution. Normally the PSW[B] bit detects this by disallowing the B,GATE instruction to execute under such conditions. */ - if (ctx->iaoq_b != ctx->iaoq_f + 4) { + if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { goto do_sigill; } - switch (ctx->iaoq_f & -4) { + switch (ctx->iaq_f.disp & -4) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); ctx->base.is_jmp = DISAS_NORETURN; @@ -2034,11 +2040,15 @@ static void do_page_zero(DisasContext *ctx) break; case 0xe0: /* SET_THREAD_POINTER */ - tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); - tmp = tcg_temp_new_i64(); - tcg_gen_ori_i64(tmp, cpu_gr[31], 3); - install_iaq_entries(ctx, -1, tmp, NULL, -1, NULL, NULL); - ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + { + DisasIAQE next = { .base = tcg_temp_new_i64() }; + + tcg_gen_st_i64(cpu_gr[26], tcg_env, + offsetof(CPUHPPAState, cr[27])); + tcg_gen_ori_i64(next.base, cpu_gr[31], 3); + install_iaq_entries(ctx, &next, NULL); + ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + } break; case 0x100: /* SYSCALL */ @@ -2077,11 +2087,12 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a) static bool trans_mfia(DisasContext *ctx, arg_mfia *a) { - unsigned rt = a->t; - TCGv_i64 tmp = dest_gpr(ctx, rt); - tcg_gen_movi_i64(tmp, ctx->iaoq_f & ~3ULL); - save_gpr(ctx, rt, tmp); + TCGv_i64 dest = dest_gpr(ctx, a->t); + copy_iaoq_entry(ctx, dest, &ctx->iaq_f); + tcg_gen_andi_i64(dest, dest, -4); + + save_gpr(ctx, a->t, dest); cond_free(&ctx->null_cond); return true; } @@ -2781,8 +2792,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) nullify_over(ctx); /* Advance the instruction queue. */ - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - ctx->iaoq_n, ctx->iaoq_n_var, ctx->iasq_n); + install_iaq_entries(ctx, &ctx->iaq_b, NULL); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ @@ -3916,18 +3926,18 @@ static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a) static bool trans_be(DisasContext *ctx, arg_be *a) { - TCGv_i64 dest = tcg_temp_new_i64(); - TCGv_i64 space = NULL; - - tcg_gen_addi_i64(dest, load_gpr(ctx, a->b), a->disp); - dest = do_ibranch_priv(ctx, dest); - #ifndef CONFIG_USER_ONLY - space = tcg_temp_new_i64(); - load_spr(ctx, space, a->sp); + ctx->iaq_j.space = tcg_temp_new_i64(); + load_spr(ctx, ctx->iaq_j.space, a->sp); #endif - return do_ibranch(ctx, dest, space, a->l, true, a->n); + ctx->iaq_j.base = tcg_temp_new_i64(); + ctx->iaq_j.disp = 0; + + tcg_gen_addi_i64(ctx->iaq_j.base, load_gpr(ctx, a->b), a->disp); + ctx->iaq_j.base = do_ibranch_priv(ctx, ctx->iaq_j.base); + + return do_ibranch(ctx, a->l, true, a->n); } static bool trans_bl(DisasContext *ctx, arg_bl *a) @@ -3937,7 +3947,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *a) static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { - uint64_t dest = iaoq_dest(ctx, a->disp); + int64_t disp = a->disp; nullify_over(ctx); @@ -3952,7 +3962,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) * b evil * in which instructions at evil would run with increased privs. */ - if (ctx->iaoq_b == -1 || ctx->iaoq_b != ctx->iaoq_f + 4) { + if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { return gen_illegal(ctx); } @@ -3970,10 +3980,11 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) } /* No change for non-gateway pages or for priv decrease. */ if (type >= 4 && type - 4 < ctx->privilege) { - dest = deposit64(dest, 0, 2, type - 4); + disp -= ctx->privilege; + disp += type - 4; } } else { - dest &= -4; /* priv = 0 */ + disp -= ctx->privilege; /* priv = 0 */ } #endif @@ -3986,17 +3997,23 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) save_gpr(ctx, a->l, tmp); } - return do_dbranch(ctx, dest - iaoq_dest(ctx, 0), 0, a->n); + return do_dbranch(ctx, disp, 0, a->n); } static bool trans_blr(DisasContext *ctx, arg_blr *a) { if (a->x) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); - tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); + DisasIAQE next = iaqe_incr(&ctx->iaq_f, 8); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + /* The computation here never changes privilege level. */ - return do_ibranch(ctx, tmp, NULL, a->l, false, a->n); + copy_iaoq_entry(ctx, t0, &next); + tcg_gen_shli_i64(t1, load_gpr(ctx, a->x), 3); + tcg_gen_add_i64(t0, t0, t1); + + ctx->iaq_j = iaqe_next_absv(ctx, t0); + return do_ibranch(ctx, a->l, false, a->n); } else { /* BLR R0,RX is a good way to load PC+8 into RX. */ return do_dbranch(ctx, 0, a->l, a->n); @@ -4015,20 +4032,22 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); - return do_ibranch(ctx, dest, NULL, 0, false, a->n); + ctx->iaq_j = iaqe_next_absv(ctx, dest); + + return do_ibranch(ctx, 0, false, a->n); } static bool trans_bve(DisasContext *ctx, arg_bve *a) { TCGv_i64 b = load_gpr(ctx, a->b); - TCGv_i64 dest = do_ibranch_priv(ctx, b); - TCGv_i64 space = NULL; #ifndef CONFIG_USER_ONLY - space = space_select(ctx, 0, b); + ctx->iaq_j.space = space_select(ctx, 0, b); #endif + ctx->iaq_j.base = do_ibranch_priv(ctx, b); + ctx->iaq_j.disp = 0; - return do_ibranch(ctx, dest, space, a->l, false, a->n); + return do_ibranch(ctx, a->l, false, a->n); } static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) @@ -4600,9 +4619,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #ifdef CONFIG_USER_ONLY ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); ctx->mmu_idx = MMU_USER_IDX; - ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; - ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; - ctx->iasq_b = NULL; + ctx->iaq_f.disp = ctx->base.pc_first | ctx->privilege; + ctx->iaq_b.disp = ctx->base.tb->cs_base | ctx->privilege; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; @@ -4615,9 +4633,13 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) uint64_t iasq_f = cs_base & ~0xffffffffull; int32_t diff = cs_base; - ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; - ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); - ctx->iasq_b = (diff ? NULL : cpu_iasq_b); + ctx->iaq_f.disp = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; + if (diff) { + ctx->iaq_b.disp = ctx->iaq_f.disp + diff; + } else { + ctx->iaq_b.base = cpu_iaoq_b; + ctx->iaq_b.space = cpu_iasq_b; + } #endif ctx->zero = tcg_constant_i64(0); @@ -4645,7 +4667,10 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b, 0); + tcg_debug_assert(!iaqe_variable(&ctx->iaq_f)); + tcg_gen_insn_start(ctx->iaq_f.disp, + iaqe_variable(&ctx->iaq_b) ? -1 : ctx->iaq_b.disp, + 0); ctx->insn_start_updated = false; } @@ -4668,11 +4693,12 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) the page permissions for execute. */ uint32_t insn = translator_ldl(env, &ctx->base, ctx->base.pc_next); - /* Set up the IA queue for the next insn. - This will be overwritten by a branch. */ - ctx->iasq_n = NULL; - ctx->iaoq_n_var = NULL; - ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; + /* + * Set up the IA queue for the next insn. + * This will be overwritten by a branch. + */ + ctx->iaq_n = NULL; + memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j)); if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4693,7 +4719,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) return; } /* Note this also detects a priority change. */ - if (ctx->iaoq_b != ctx->iaoq_f + 4 || ctx->iasq_b) { + if (iaqe_variable(&ctx->iaq_b) + || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { ctx->base.is_jmp = DISAS_IAQ_N_STALE; return; } @@ -4702,20 +4729,25 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) * Advance the insn queue. * The only exit now is DISAS_TOO_MANY from the translator loop. */ - ctx->iaoq_f = ctx->iaoq_b; - ctx->iaoq_b = ctx->iaoq_n; - if (ctx->iaoq_b == -1) { - if (ctx->iaoq_n_var) { - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); - } else { - tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_b, 4); - tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, - gva_offset_mask(ctx->tb_flags)); - } + ctx->iaq_f.disp = ctx->iaq_b.disp; + if (!ctx->iaq_n) { + ctx->iaq_b.disp += 4; + return; } - if (ctx->iasq_n) { - tcg_gen_mov_i64(cpu_iasq_b, ctx->iasq_n); - ctx->iasq_b = cpu_iasq_b; + /* + * If IAQ_Next is variable in any way, we need to copy into the + * IAQ_Back globals, in case the next insn raises an exception. + */ + if (ctx->iaq_n->base) { + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaq_n); + ctx->iaq_b.base = cpu_iaoq_b; + ctx->iaq_b.disp = 0; + } else { + ctx->iaq_b.disp = ctx->iaq_n->disp; + } + if (ctx->iaq_n->space) { + tcg_gen_mov_i64(cpu_iasq_b, ctx->iaq_n->space); + ctx->iaq_b.space = cpu_iasq_b; } } @@ -4723,43 +4755,29 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasJumpType is_jmp = ctx->base.is_jmp; - uint64_t fi, bi; - TCGv_i64 fv, bv; - TCGv_i64 fs, bs; - /* Assume the insn queue has not been advanced. */ - fi = ctx->iaoq_b; - fv = cpu_iaoq_b; - fs = ctx->iasq_b; - bi = ctx->iaoq_n; - bv = ctx->iaoq_n_var; - bs = ctx->iasq_n; + DisasIAQE *f = &ctx->iaq_b; + DisasIAQE *b = ctx->iaq_n; switch (is_jmp) { case DISAS_NORETURN: break; case DISAS_TOO_MANY: /* The insn queue has not been advanced. */ - bi = fi; - bv = fv; - bs = fs; - fi = ctx->iaoq_f; - fv = NULL; - fs = NULL; + f = &ctx->iaq_f; + b = &ctx->iaq_b; /* FALLTHRU */ case DISAS_IAQ_N_STALE: - if (fs == NULL - && bs == NULL - && use_goto_tb(ctx, fi, bi) + if (use_goto_tb(ctx, f, b) && (ctx->null_cond.c == TCG_COND_NEVER || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); - gen_goto_tb(ctx, 0, fi, bi); + gen_goto_tb(ctx, 0, f, b); break; } /* FALLTHRU */ case DISAS_IAQ_N_STALE_EXIT: - install_iaq_entries(ctx, fi, fv, fs, bi, bv, bs); + install_iaq_entries(ctx, f, b); nullify_save(ctx); if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { tcg_gen_exit_tb(NULL, 0); @@ -4815,6 +4833,6 @@ static const TranslatorOps hppa_tr_ops = { void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc) { - DisasContext ctx; + DisasContext ctx = { }; translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base); } From 0d89cb7c29d9030d96c32ea4cdde7b4ee6f3dcf4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 16:47:14 -1000 Subject: [PATCH 0681/2884] target/hppa: Use displacements in DisasIAQE This is a first step in enabling CF_PCREL, but for now we regenerate the absolute address before writeback. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 43 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e0e4db75ee..de077e7a57 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -45,9 +45,9 @@ typedef struct DisasCond { typedef struct DisasIAQE { /* IASQ; may be null for no change from TB. */ TCGv_i64 space; - /* IAOQ base; may be null for immediate absolute address. */ + /* IAOQ base; may be null for relative address. */ TCGv_i64 base; - /* IAOQ addend; absolute immedate address if base is null. */ + /* IAOQ addend; if base is null, relative to ctx->iaoq_first. */ int64_t disp; } DisasIAQE; @@ -60,6 +60,9 @@ typedef struct DisasContext { /* IAQ_Next, for jumps, otherwise null for simple advance. */ DisasIAQE iaq_j, *iaq_n; + /* IAOQ_Front at entry to TB. */ + uint64_t iaoq_first; + DisasCond null_cond; TCGLabel *null_lab; @@ -640,7 +643,7 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, uint64_t mask = gva_offset_mask(ctx->tb_flags); if (src->base == NULL) { - tcg_gen_movi_i64(dest, src->disp & mask); + tcg_gen_movi_i64(dest, (ctx->iaoq_first + src->disp) & mask); } else if (src->disp == 0) { tcg_gen_andi_i64(dest, src->base, mask); } else { @@ -674,12 +677,8 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) if (!link) { return; } - if (ctx->iaq_b.base) { - tcg_gen_addi_i64(cpu_gr[link], ctx->iaq_b.base, - ctx->iaq_b.disp + 4); - } else { - tcg_gen_movi_i64(cpu_gr[link], ctx->iaq_b.disp + 4); - } + DisasIAQE next = iaqe_incr(&ctx->iaq_b, 4); + copy_iaoq_entry(ctx, cpu_gr[link], &next); #ifndef CONFIG_USER_ONLY if (with_sr0) { tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); @@ -731,7 +730,7 @@ static bool use_goto_tb(DisasContext *ctx, const DisasIAQE *f, { return (!iaqe_variable(f) && (b == NULL || !iaqe_variable(b)) && - translator_use_goto_tb(&ctx->base, f->disp)); + translator_use_goto_tb(&ctx->base, ctx->iaoq_first + f->disp)); } /* If the next insn is to be nullified, and it's on the same page, @@ -742,7 +741,8 @@ static bool use_nullify_skip(DisasContext *ctx) { return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) && !iaqe_variable(&ctx->iaq_b) - && is_same_page(&ctx->base, ctx->iaq_b.disp)); + && (((ctx->iaoq_first + ctx->iaq_b.disp) ^ ctx->iaoq_first) + & TARGET_PAGE_MASK) == 0); } static void gen_goto_tb(DisasContext *ctx, int which, @@ -2005,6 +2005,8 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { + assert(ctx->iaq_f.disp == 0); + /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the next insn within the privileged page. */ @@ -2024,11 +2026,11 @@ static void do_page_zero(DisasContext *ctx) non-sequential instruction execution. Normally the PSW[B] bit detects this by disallowing the B,GATE instruction to execute under such conditions. */ - if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { + if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != 4) { goto do_sigill; } - switch (ctx->iaq_f.disp & -4) { + switch (ctx->base.pc_first) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); ctx->base.is_jmp = DISAS_NORETURN; @@ -4619,8 +4621,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #ifdef CONFIG_USER_ONLY ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); ctx->mmu_idx = MMU_USER_IDX; - ctx->iaq_f.disp = ctx->base.pc_first | ctx->privilege; - ctx->iaq_b.disp = ctx->base.tb->cs_base | ctx->privilege; + ctx->iaoq_first = ctx->base.pc_first | ctx->privilege; + ctx->iaq_b.disp = ctx->base.tb->cs_base - ctx->base.pc_first; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; @@ -4633,9 +4635,10 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) uint64_t iasq_f = cs_base & ~0xffffffffull; int32_t diff = cs_base; - ctx->iaq_f.disp = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; + ctx->iaoq_first = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; + if (diff) { - ctx->iaq_b.disp = ctx->iaq_f.disp + diff; + ctx->iaq_b.disp = diff; } else { ctx->iaq_b.base = cpu_iaoq_b; ctx->iaq_b.space = cpu_iasq_b; @@ -4668,9 +4671,9 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); tcg_debug_assert(!iaqe_variable(&ctx->iaq_f)); - tcg_gen_insn_start(ctx->iaq_f.disp, - iaqe_variable(&ctx->iaq_b) ? -1 : ctx->iaq_b.disp, - 0); + tcg_gen_insn_start(ctx->iaoq_first + ctx->iaq_f.disp, + (iaqe_variable(&ctx->iaq_b) ? -1 : + ctx->iaoq_first + ctx->iaq_b.disp), 0); ctx->insn_start_updated = false; } From 4c42fd0d4e543aaf8981c737f971d27e7c9d4df0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 07:30:19 -1000 Subject: [PATCH 0682/2884] target/hppa: Rename cond_make_* helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use 'v' for a variable that needs copying, 't' for a temp that doesn't need copying, and 'i' for an immediate, and use this naming for both arguments of the comparison. So: cond_make_tmp -> cond_make_tt cond_make_0_tmp -> cond_make_ti cond_make_0 -> cond_make_vi cond_make -> cond_make_vv Pass 0 explictly, rather than implicitly in the function name. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index de077e7a57..07ba35001b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -345,32 +345,32 @@ static DisasCond cond_make_n(void) }; } -static DisasCond cond_make_tmp(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) +static DisasCond cond_make_tt(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 }; } -static DisasCond cond_make_0_tmp(TCGCond c, TCGv_i64 a0) +static DisasCond cond_make_ti(TCGCond c, TCGv_i64 a0, uint64_t imm) { - return cond_make_tmp(c, a0, tcg_constant_i64(0)); + return cond_make_tt(c, a0, tcg_constant_i64(imm)); } -static DisasCond cond_make_0(TCGCond c, TCGv_i64 a0) +static DisasCond cond_make_vi(TCGCond c, TCGv_i64 a0, uint64_t imm) { TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_mov_i64(tmp, a0); - return cond_make_0_tmp(c, tmp); + return cond_make_ti(c, tmp, imm); } -static DisasCond cond_make(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) +static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); tcg_gen_mov_i64(t0, a0); tcg_gen_mov_i64(t1, a1); - return cond_make_tmp(c, t0, t1); + return cond_make_tt(c, t0, t1); } static void cond_free(DisasCond *cond) @@ -789,7 +789,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_ext32u_i64(tmp, res); res = tmp; } - cond = cond_make_0(TCG_COND_EQ, res); + cond = cond_make_vi(TCG_COND_EQ, res, 0); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new_i64(); @@ -797,7 +797,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, if (!d) { tcg_gen_ext32s_i64(tmp, tmp); } - cond = cond_make_0_tmp(TCG_COND_LT, tmp); + cond = cond_make_ti(TCG_COND_LT, tmp, 0); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ /* @@ -819,10 +819,10 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_sari_i64(tmp, tmp, 63); tcg_gen_and_i64(tmp, tmp, res); } - cond = cond_make_0_tmp(TCG_COND_EQ, tmp); + cond = cond_make_ti(TCG_COND_EQ, tmp, 0); break; case 4: /* NUV / UV (!UV / UV) */ - cond = cond_make_0(TCG_COND_EQ, uv); + cond = cond_make_vi(TCG_COND_EQ, uv, 0); break; case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */ tmp = tcg_temp_new_i64(); @@ -830,7 +830,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, if (!d) { tcg_gen_ext32u_i64(tmp, tmp); } - cond = cond_make_0_tmp(TCG_COND_EQ, tmp); + cond = cond_make_ti(TCG_COND_EQ, tmp, 0); break; case 6: /* SV / NSV (V / !V) */ if (!d) { @@ -838,12 +838,12 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_ext32s_i64(tmp, sv); sv = tmp; } - cond = cond_make_0(TCG_COND_LT, sv); + cond = cond_make_ti(TCG_COND_LT, sv, 0); break; case 7: /* OD / EV */ tmp = tcg_temp_new_i64(); tcg_gen_andi_i64(tmp, res, 1); - cond = cond_make_0_tmp(TCG_COND_NE, tmp); + cond = cond_make_ti(TCG_COND_NE, tmp, 0); break; default: g_assert_not_reached(); @@ -905,9 +905,9 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_ext32s_i64(t1, in1); tcg_gen_ext32s_i64(t2, in2); } - return cond_make_tmp(tc, t1, t2); + return cond_make_tt(tc, t1, t2); } - return cond_make(tc, in1, in2); + return cond_make_vv(tc, in1, in2); } /* @@ -979,9 +979,9 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, } else { tcg_gen_ext32s_i64(tmp, res); } - return cond_make_0_tmp(tc, tmp); + return cond_make_ti(tc, tmp, 0); } - return cond_make_0(tc, res); + return cond_make_vi(tc, res, 0); } /* Similar, but for shift/extract/deposit conditions. */ @@ -1040,7 +1040,7 @@ static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res) tcg_gen_andc_i64(tmp, tmp, res); tcg_gen_andi_i64(tmp, tmp, sgns); - return cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp); + return cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp, 0); } static TCGv_i64 get_carry(DisasContext *ctx, bool d, @@ -1454,7 +1454,7 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } tcg_gen_andi_i64(cb, cb, test_cb); - cond = cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb); + cond = cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb, 0); } if (is_tc) { @@ -3543,7 +3543,7 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) tcg_gen_shl_i64(tmp, tcg_r, tmp); } - cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3560,7 +3560,7 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) p = a->p | (a->d ? 0 : 32); tcg_gen_shli_i64(tmp, tcg_r, p); - cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -4364,7 +4364,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) switch (a->c) { case 0: /* simple */ tcg_gen_andi_i64(t, t, 0x4000000); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); + ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); goto done; case 2: /* rej */ inv = true; @@ -4394,16 +4394,16 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) if (inv) { TCGv_i64 c = tcg_constant_i64(mask); tcg_gen_or_i64(t, t, c); - ctx->null_cond = cond_make(TCG_COND_EQ, t, c); + ctx->null_cond = cond_make_tt(TCG_COND_EQ, t, c); } else { tcg_gen_andi_i64(t, t, mask); - ctx->null_cond = cond_make_0(TCG_COND_EQ, t); + ctx->null_cond = cond_make_ti(TCG_COND_EQ, t, 0); } } else { unsigned cbit = (a->y ^ 1) - 1; tcg_gen_extract_i64(t, t, 21 - cbit, 1); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); + ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); } done: From d6d46be1bf3876db6168d155ed273866d5f595cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 10:27:12 -1000 Subject: [PATCH 0683/2884] target/hppa: Use TCG_COND_TST* in do_cond We can directly test bits of a 32-bit comparison without zero or sign-extending an intermediate result. We can directly test bit 0 for odd/even. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 64 ++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 07ba35001b..813f1571e9 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -776,28 +776,36 @@ static bool cond_need_cb(int c) static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, TCGv_i64 res, TCGv_i64 uv, TCGv_i64 sv) { + TCGCond sign_cond, zero_cond; + uint64_t sign_imm, zero_imm; DisasCond cond; TCGv_i64 tmp; + if (d) { + /* 64-bit condition. */ + sign_imm = 0; + sign_cond = TCG_COND_LT; + zero_imm = 0; + zero_cond = TCG_COND_EQ; + } else { + /* 32-bit condition. */ + sign_imm = 1ull << 31; + sign_cond = TCG_COND_TSTNE; + zero_imm = UINT32_MAX; + zero_cond = TCG_COND_TSTEQ; + } + switch (cf >> 1) { case 0: /* Never / TR (0 / 1) */ cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ - if (!d) { - tmp = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(tmp, res); - res = tmp; - } - cond = cond_make_vi(TCG_COND_EQ, res, 0); + cond = cond_make_vi(zero_cond, res, zero_imm); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, res, sv); - if (!d) { - tcg_gen_ext32s_i64(tmp, tmp); - } - cond = cond_make_ti(TCG_COND_LT, tmp, 0); + cond = cond_make_ti(sign_cond, tmp, sign_imm); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ /* @@ -805,21 +813,15 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, * (N ^ V) | Z * ((res < 0) ^ (sv < 0)) | !res * ((res ^ sv) < 0) | !res - * (~(res ^ sv) >= 0) | !res - * !(~(res ^ sv) >> 31) | !res - * !(~(res ^ sv) >> 31 & res) + * ((res ^ sv) < 0 ? 1 : !res) + * !((res ^ sv) < 0 ? 0 : res) */ tmp = tcg_temp_new_i64(); - tcg_gen_eqv_i64(tmp, res, sv); - if (!d) { - tcg_gen_sextract_i64(tmp, tmp, 31, 1); - tcg_gen_and_i64(tmp, tmp, res); - tcg_gen_ext32u_i64(tmp, tmp); - } else { - tcg_gen_sari_i64(tmp, tmp, 63); - tcg_gen_and_i64(tmp, tmp, res); - } - cond = cond_make_ti(TCG_COND_EQ, tmp, 0); + tcg_gen_xor_i64(tmp, res, sv); + tcg_gen_movcond_i64(sign_cond, tmp, + tmp, tcg_constant_i64(sign_imm), + ctx->zero, res); + cond = cond_make_ti(zero_cond, tmp, zero_imm); break; case 4: /* NUV / UV (!UV / UV) */ cond = cond_make_vi(TCG_COND_EQ, uv, 0); @@ -827,23 +829,13 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */ tmp = tcg_temp_new_i64(); tcg_gen_movcond_i64(TCG_COND_EQ, tmp, uv, ctx->zero, ctx->zero, res); - if (!d) { - tcg_gen_ext32u_i64(tmp, tmp); - } - cond = cond_make_ti(TCG_COND_EQ, tmp, 0); + cond = cond_make_ti(zero_cond, tmp, zero_imm); break; case 6: /* SV / NSV (V / !V) */ - if (!d) { - tmp = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(tmp, sv); - sv = tmp; - } - cond = cond_make_ti(TCG_COND_LT, sv, 0); + cond = cond_make_vi(sign_cond, sv, sign_imm); break; case 7: /* OD / EV */ - tmp = tcg_temp_new_i64(); - tcg_gen_andi_i64(tmp, res, 1); - cond = cond_make_ti(TCG_COND_NE, tmp, 0); + cond = cond_make_vi(TCG_COND_TSTNE, res, 1); break; default: g_assert_not_reached(); From fbe65c648d46efd5b77f30e9d60a14f09535ea1a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:00:36 -1000 Subject: [PATCH 0684/2884] target/hppa: Use TCG_COND_TST* in do_log_cond We can directly test bits of a 32-bit comparison without zero or sign-extending an intermediate result. We can directly test bit 0 for odd/even. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 78 ++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 813f1571e9..62cc3c3117 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -915,65 +915,41 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, TCGv_i64 res) { TCGCond tc; - bool ext_uns; + uint64_t imm; - switch (cf) { - case 0: /* never */ - case 9: /* undef, C */ - case 11: /* undef, C & !Z */ - case 12: /* undef, V */ - return cond_make_f(); - - case 1: /* true */ - case 8: /* undef, !C */ - case 10: /* undef, !C | Z */ - case 13: /* undef, !V */ - return cond_make_t(); - - case 2: /* == */ - tc = TCG_COND_EQ; - ext_uns = true; + switch (cf >> 1) { + case 0: /* never / always */ + case 4: /* undef, C */ + case 5: /* undef, C & !Z */ + case 6: /* undef, V */ + return cf & 1 ? cond_make_t() : cond_make_f(); + case 1: /* == / <> */ + tc = d ? TCG_COND_EQ : TCG_COND_TSTEQ; + imm = d ? 0 : UINT32_MAX; break; - case 3: /* <> */ - tc = TCG_COND_NE; - ext_uns = true; + case 2: /* < / >= */ + tc = d ? TCG_COND_LT : TCG_COND_TSTNE; + imm = d ? 0 : 1ull << 31; break; - case 4: /* < */ - tc = TCG_COND_LT; - ext_uns = false; + case 3: /* <= / > */ + tc = cf & 1 ? TCG_COND_GT : TCG_COND_LE; + if (!d) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(tmp, res); + return cond_make_ti(tc, tmp, 0); + } + return cond_make_vi(tc, res, 0); + case 7: /* OD / EV */ + tc = TCG_COND_TSTNE; + imm = 1; break; - case 5: /* >= */ - tc = TCG_COND_GE; - ext_uns = false; - break; - case 6: /* <= */ - tc = TCG_COND_LE; - ext_uns = false; - break; - case 7: /* > */ - tc = TCG_COND_GT; - ext_uns = false; - break; - - case 14: /* OD */ - case 15: /* EV */ - return do_cond(ctx, cf, d, res, NULL, NULL); - default: g_assert_not_reached(); } - - if (!d) { - TCGv_i64 tmp = tcg_temp_new_i64(); - - if (ext_uns) { - tcg_gen_ext32u_i64(tmp, res); - } else { - tcg_gen_ext32s_i64(tmp, res); - } - return cond_make_ti(tc, tmp, 0); + if (cf & 1) { + tc = tcg_invert_cond(tc); } - return cond_make_vi(tc, res, 0); + return cond_make_vi(tc, res, imm); } /* Similar, but for shift/extract/deposit conditions. */ From 25f97be7236cc96bccce35f27999b0289c2b221f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:04:06 -1000 Subject: [PATCH 0685/2884] target/hppa: Use TCG_COND_TST* in do_unit_zero_cond Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 62cc3c3117..b19d7c64fe 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1006,9 +1006,8 @@ static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res) tmp = tcg_temp_new_i64(); tcg_gen_subi_i64(tmp, res, ones); tcg_gen_andc_i64(tmp, tmp, res); - tcg_gen_andi_i64(tmp, tmp, sgns); - return cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp, 0); + return cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, tmp, sgns); } static TCGv_i64 get_carry(DisasContext *ctx, bool d, From 3289ea0e8f4894a67f827540d674e3e199596f33 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:05:46 -1000 Subject: [PATCH 0686/2884] target/hppa: Use TCG_COND_TST* in do_unit_addsub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b19d7c64fe..4e49bd2b67 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1420,8 +1420,8 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, tcg_gen_shri_i64(cb, cb, 1); } - tcg_gen_andi_i64(cb, cb, test_cb); - cond = cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb, 0); + cond = cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + cb, test_cb); } if (is_tc) { From b041ec9d7173ec893c1a5bc0bfd25fe860b4fcb0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:22:59 -1000 Subject: [PATCH 0687/2884] target/hppa: Use TCG_COND_TST* in trans_bb_imm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4e49bd2b67..af6be5100c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3516,18 +3516,12 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { - TCGv_i64 tmp, tcg_r; DisasCond cond; - int p; + int p = a->p | (a->d ? 0 : 32); nullify_over(ctx); - - tmp = tcg_temp_new_i64(); - tcg_r = load_gpr(ctx, a->r); - p = a->p | (a->d ? 0 : 32); - tcg_gen_shli_i64(tmp, tcg_r, p); - - cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); + cond = cond_make_vi(a->c ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + load_gpr(ctx, a->r), 1ull << (63 - p)); return do_cbranch(ctx, a->disp, a->n, &cond); } From f33a22c1a28f0ba7b9c2fef1953aa6f593da870d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 12:20:31 -1000 Subject: [PATCH 0688/2884] target/hppa: Use registerfields.h for FPSR Define all of the context dependent field definitions. Use FIELD_EX32 and FIELD_DP32 with named fields instead of extract32 and deposit32 with raw constants. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 25 +++++++++++++++++++++++++ target/hppa/fpu_helper.c | 26 +++++++++++++------------- target/hppa/translate.c | 18 ++++++++---------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 61f1353133..c37b4e12fb 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -24,6 +24,7 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "qemu/interval-tree.h" +#include "hw/registerfields.h" #define MMU_ABS_W_IDX 6 #define MMU_ABS_IDX 7 @@ -152,6 +153,30 @@ #define CR_IPSW 22 #define CR_EIRR 23 +FIELD(FPSR, ENA_I, 0, 1) +FIELD(FPSR, ENA_U, 1, 1) +FIELD(FPSR, ENA_O, 2, 1) +FIELD(FPSR, ENA_Z, 3, 1) +FIELD(FPSR, ENA_V, 4, 1) +FIELD(FPSR, ENABLES, 0, 5) +FIELD(FPSR, D, 5, 1) +FIELD(FPSR, T, 6, 1) +FIELD(FPSR, RM, 9, 2) +FIELD(FPSR, CQ, 11, 11) +FIELD(FPSR, CQ0_6, 15, 7) +FIELD(FPSR, CQ0_4, 17, 5) +FIELD(FPSR, CQ0_2, 19, 3) +FIELD(FPSR, CQ0, 21, 1) +FIELD(FPSR, CA, 15, 7) +FIELD(FPSR, CA0, 21, 1) +FIELD(FPSR, C, 26, 1) +FIELD(FPSR, FLG_I, 27, 1) +FIELD(FPSR, FLG_U, 28, 1) +FIELD(FPSR, FLG_O, 29, 1) +FIELD(FPSR, FLG_Z, 30, 1) +FIELD(FPSR, FLG_V, 31, 1) +FIELD(FPSR, FLAGS, 27, 5) + typedef struct HPPATLBEntry { union { IntervalTreeNode itree; diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 576f283b04..deaed2b65d 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -30,7 +30,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) env->fr0_shadow = shadow; - switch (extract32(shadow, 9, 2)) { + switch (FIELD_EX32(shadow, FPSR, RM)) { default: rm = float_round_nearest_even; break; @@ -46,7 +46,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) } set_float_rounding_mode(rm, &env->fp_status); - d = extract32(shadow, 5, 1); + d = FIELD_EX32(shadow, FPSR, D); set_flush_to_zero(d, &env->fp_status); set_flush_inputs_to_zero(d, &env->fp_status); } @@ -57,7 +57,7 @@ void cpu_hppa_loaded_fr0(CPUHPPAState *env) } #define CONVERT_BIT(X, SRC, DST) \ - ((SRC) > (DST) \ + ((unsigned)(SRC) > (unsigned)(DST) \ ? (X) / ((SRC) / (DST)) & (DST) \ : ((X) & (SRC)) * ((DST) / (SRC))) @@ -73,12 +73,12 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) } set_float_exception_flags(0, &env->fp_status); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); - shadow |= hard_exp << (32 - 5); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, R_FPSR_ENA_I_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK); + shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); env->fr0_shadow = shadow; env->fr[0] = (uint64_t)shadow << 32; @@ -378,15 +378,15 @@ static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, if (y) { /* targeted comparison */ /* set fpsr[ca[y - 1]] to current compare */ - shadow = deposit32(shadow, 21 - (y - 1), 1, c); + shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c); } else { /* queued comparison */ /* shift cq right by one place */ - shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); + shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK); /* move fpsr[c] to fpsr[cq[0]] */ - shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); + shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C)); /* set fpsr[c] to current compare */ - shadow = deposit32(shadow, 26, 1, c); + shadow = FIELD_DP32(shadow, FPSR, C, c); } env->fr0_shadow = shadow; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index af6be5100c..efd4398437 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4324,29 +4324,28 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) switch (a->c) { case 0: /* simple */ - tcg_gen_andi_i64(t, t, 0x4000000); - ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); - goto done; + mask = R_FPSR_C_MASK; + break; case 2: /* rej */ inv = true; /* fallthru */ case 1: /* acc */ - mask = 0x43ff800; + mask = R_FPSR_C_MASK | R_FPSR_CQ_MASK; break; case 6: /* rej8 */ inv = true; /* fallthru */ case 5: /* acc8 */ - mask = 0x43f8000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_6_MASK; break; case 9: /* acc6 */ - mask = 0x43e0000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_4_MASK; break; case 13: /* acc4 */ - mask = 0x4380000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_2_MASK; break; case 17: /* acc2 */ - mask = 0x4200000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_MASK; break; default: gen_illegal(ctx); @@ -4363,11 +4362,10 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) } else { unsigned cbit = (a->y ^ 1) - 1; - tcg_gen_extract_i64(t, t, 21 - cbit, 1); + tcg_gen_extract_i64(t, t, R_FPSR_CA0_SHIFT - cbit, 1); ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); } - done: return nullify_end(ctx); } From 3692ad21f558f1e3425a5d6cecc241d2601db022 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 13:26:10 -1000 Subject: [PATCH 0689/2884] target/hppa: Use TCG_COND_TST* in trans_ftest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index efd4398437..11d74bb2aa 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4311,6 +4311,8 @@ static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a) static bool trans_ftest(DisasContext *ctx, arg_ftest *a) { + TCGCond tc = TCG_COND_TSTNE; + uint32_t mask; TCGv_i64 t; nullify_over(ctx); @@ -4319,21 +4321,18 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) tcg_gen_ld32u_i64(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); if (a->y == 1) { - int mask; - bool inv = false; - switch (a->c) { case 0: /* simple */ mask = R_FPSR_C_MASK; break; case 2: /* rej */ - inv = true; + tc = TCG_COND_TSTEQ; /* fallthru */ case 1: /* acc */ mask = R_FPSR_C_MASK | R_FPSR_CQ_MASK; break; case 6: /* rej8 */ - inv = true; + tc = TCG_COND_TSTEQ; /* fallthru */ case 5: /* acc8 */ mask = R_FPSR_C_MASK | R_FPSR_CQ0_6_MASK; @@ -4351,21 +4350,12 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) gen_illegal(ctx); return true; } - if (inv) { - TCGv_i64 c = tcg_constant_i64(mask); - tcg_gen_or_i64(t, t, c); - ctx->null_cond = cond_make_tt(TCG_COND_EQ, t, c); - } else { - tcg_gen_andi_i64(t, t, mask); - ctx->null_cond = cond_make_ti(TCG_COND_EQ, t, 0); - } } else { unsigned cbit = (a->y ^ 1) - 1; - - tcg_gen_extract_i64(t, t, R_FPSR_CA0_SHIFT - cbit, 1); - ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); + mask = R_FPSR_CA0_MASK >> cbit; } + ctx->null_cond = cond_make_ti(tc, t, mask); return nullify_end(ctx); } From e0137378ed4f3eecc44b7ac9b435f9d91ca7e8fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 13:50:11 -1000 Subject: [PATCH 0690/2884] target/hppa: Remove cond_free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we do not need to free tcg temporaries, the only thing cond_free does is reset the condition to never. Instead, simply write a new condition over the old, which may be simply cond_make_f() for the never condition. The do_*_cond functions do the right thing with c or cf == 0, so there's no need for a special case anymore. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 102 +++++++++++----------------------------- 1 file changed, 27 insertions(+), 75 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 11d74bb2aa..81a75ddf95 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -373,21 +373,6 @@ static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) return cond_make_tt(c, t0, t1); } -static void cond_free(DisasCond *cond) -{ - switch (cond->c) { - default: - cond->a0 = NULL; - cond->a1 = NULL; - /* fallthru */ - case TCG_COND_ALWAYS: - cond->c = TCG_COND_NEVER; - break; - case TCG_COND_NEVER: - break; - } -} - static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { @@ -537,7 +522,7 @@ static void nullify_over(DisasContext *ctx) tcg_gen_brcond_i64(ctx->null_cond.c, ctx->null_cond.a0, ctx->null_cond.a1, ctx->null_lab); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); } } @@ -555,7 +540,7 @@ static void nullify_save(DisasContext *ctx) ctx->null_cond.a0, ctx->null_cond.a1); ctx->psw_n_nonzero = true; } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); } /* Set a PSW[N] to X. The intention is that this is used immediately @@ -1166,7 +1151,6 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1263,7 +1247,6 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1318,7 +1301,6 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1333,10 +1315,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (cf) { - ctx->null_cond = do_log_cond(ctx, cf, d, dest); - } + ctx->null_cond = do_log_cond(ctx, cf, d, dest); } static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, @@ -1431,7 +1410,6 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } save_gpr(ctx, rt, dest); - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1854,7 +1832,6 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, taken = gen_new_label(); tcg_gen_brcond_i64(c, cond->a0, cond->a1, taken); - cond_free(cond); /* Not taken: Condition not satisfied; nullify on backward branches. */ n = is_n && disp < 0; @@ -2036,7 +2013,7 @@ static void do_page_zero(DisasContext *ctx) static bool trans_nop(DisasContext *ctx, arg_nop *a) { - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2050,7 +2027,7 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a) /* No point in nullifying the memory barrier. */ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2062,7 +2039,7 @@ static bool trans_mfia(DisasContext *ctx, arg_mfia *a) tcg_gen_andi_i64(dest, dest, -4); save_gpr(ctx, a->t, dest); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2077,7 +2054,7 @@ static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) save_gpr(ctx, rt, t0); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2122,7 +2099,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) save_gpr(ctx, rt, tmp); done: - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2162,7 +2139,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) tcg_gen_andi_i64(tmp, reg, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2236,7 +2213,7 @@ static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) tcg_gen_andi_i64(tmp, tmp, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2253,7 +2230,7 @@ static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) #endif save_gpr(ctx, a->t, dest); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2415,7 +2392,7 @@ static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) tcg_gen_add_i64(dest, src1, src2); save_gpr(ctx, a->b, dest); } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2657,7 +2634,7 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a) since the entire address space is coherent. */ save_gpr(ctx, a->t, ctx->zero); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2734,7 +2711,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) unsigned rt = a->t; if (rt == 0) { /* NOP */ - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } if (r2 == 0) { /* COPY */ @@ -2745,7 +2722,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) } else { save_gpr(ctx, rt, cpu_gr[r1]); } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } #ifndef CONFIG_USER_ONLY @@ -2810,11 +2787,7 @@ static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a) tcg_gen_xor_i64(dest, tcg_r1, tcg_r2); save_gpr(ctx, a->t, dest); - cond_free(&ctx->null_cond); - if (a->cf) { - ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); - } - + ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); return nullify_end(ctx); } @@ -2840,7 +2813,7 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) tcg_gen_subi_i64(tmp, tmp, 1); } save_gpr(ctx, a->t, tmp); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3366,7 +3339,7 @@ static bool trans_ldil(DisasContext *ctx, arg_ldil *a) tcg_gen_movi_i64(tcg_rt, a->i); save_gpr(ctx, a->t, tcg_rt); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3377,7 +3350,7 @@ static bool trans_addil(DisasContext *ctx, arg_addil *a) tcg_gen_addi_i64(tcg_r1, tcg_rt, a->i); save_gpr(ctx, 1, tcg_r1); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3393,7 +3366,7 @@ static bool trans_ldo(DisasContext *ctx, arg_ldo *a) tcg_gen_addi_i64(tcg_rt, cpu_gr[a->b], a->i); } save_gpr(ctx, a->t, tcg_rt); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3619,10 +3592,7 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3662,10 +3632,7 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3707,10 +3674,7 @@ static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3743,10 +3707,7 @@ static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3783,10 +3744,7 @@ static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3819,10 +3777,7 @@ static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3856,10 +3811,7 @@ static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(ctx, c, d, dest); - } + ctx->null_cond = do_sed_cond(ctx, c, d, dest); return nullify_end(ctx); } From 806030074b25a980f879e43df4e9c06fc45f308c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 12:47:11 -1000 Subject: [PATCH 0691/2884] target/hppa: Introduce DisasDelayException Allow an exception to be emitted at the end of the TranslationBlock, leaving only the conditional branch inline. Use it for simple exception instructions like break, which happen to be nullified. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 60 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 81a75ddf95..706537ea59 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -51,6 +51,17 @@ typedef struct DisasIAQE { int64_t disp; } DisasIAQE; +typedef struct DisasDelayException { + struct DisasDelayException *next; + TCGLabel *lab; + uint32_t insn; + bool set_iir; + int8_t set_n; + uint8_t excp; + /* Saved state at parent insn. */ + DisasIAQE iaq_f, iaq_b; +} DisasDelayException; + typedef struct DisasContext { DisasContextBase base; CPUState *cs; @@ -66,6 +77,7 @@ typedef struct DisasContext { DisasCond null_cond; TCGLabel *null_lab; + DisasDelayException *delay_excp_list; TCGv_i64 zero; uint32_t insn; @@ -684,13 +696,38 @@ static void gen_excp(DisasContext *ctx, int exception) ctx->base.is_jmp = DISAS_NORETURN; } +static DisasDelayException *delay_excp(DisasContext *ctx, uint8_t excp) +{ + DisasDelayException *e = tcg_malloc(sizeof(DisasDelayException)); + + memset(e, 0, sizeof(*e)); + e->next = ctx->delay_excp_list; + ctx->delay_excp_list = e; + + e->lab = gen_new_label(); + e->insn = ctx->insn; + e->set_iir = true; + e->set_n = ctx->psw_n_nonzero ? 0 : -1; + e->excp = excp; + e->iaq_f = ctx->iaq_f; + e->iaq_b = ctx->iaq_b; + + return e; +} + static bool gen_excp_iir(DisasContext *ctx, int exc) { - nullify_over(ctx); - tcg_gen_st_i64(tcg_constant_i64(ctx->insn), - tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); - gen_excp(ctx, exc); - return nullify_end(ctx); + if (ctx->null_cond.c == TCG_COND_NEVER) { + tcg_gen_st_i64(tcg_constant_i64(ctx->insn), + tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); + gen_excp(ctx, exc); + } else { + DisasDelayException *e = delay_excp(ctx, exc); + tcg_gen_brcond_i64(tcg_invert_cond(ctx->null_cond.c), + ctx->null_cond.a0, ctx->null_cond.a1, e->lab); + ctx->null_cond = cond_make_f(); + } + return true; } static bool gen_illegal(DisasContext *ctx) @@ -4697,6 +4734,19 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) default: g_assert_not_reached(); } + + for (DisasDelayException *e = ctx->delay_excp_list; e ; e = e->next) { + gen_set_label(e->lab); + if (e->set_n >= 0) { + tcg_gen_movi_i64(cpu_psw_n, e->set_n); + } + if (e->set_iir) { + tcg_gen_st_i64(tcg_constant_i64(e->insn), tcg_env, + offsetof(CPUHPPAState, cr[CR_IIR])); + } + install_iaq_entries(ctx, &e->iaq_f, &e->iaq_b); + gen_excp_1(e->excp); + } } static void hppa_tr_disas_log(const DisasContextBase *dcbase, From 269ca0a9ccaafd023b7c1fc914c1386128796fb0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 13:16:15 -1000 Subject: [PATCH 0692/2884] target/hppa: Use delay_excp for conditional traps Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.h | 1 - target/hppa/int_helper.c | 2 +- target/hppa/op_helper.c | 7 ------- target/hppa/translate.c | 41 ++++++++++++++++++++++++++++++---------- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 5900fd70bc..3d0d143aed 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,6 +1,5 @@ DEF_HELPER_2(excp, noreturn, env, int) DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index a667ee380d..1aa3e88ef1 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -134,13 +134,13 @@ void hppa_cpu_do_interrupt(CPUState *cs) switch (i) { case EXCP_ILL: case EXCP_BREAK: + case EXCP_COND: case EXCP_PRIV_REG: case EXCP_PRIV_OPR: /* IIR set via translate.c. */ break; case EXCP_OVERFLOW: - case EXCP_COND: case EXCP_ASSIST: case EXCP_DTLB_MISS: case EXCP_NA_ITLB_MISS: diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 6cf49f33b7..a8b69fd481 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -49,13 +49,6 @@ void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) } } -void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) -{ - if (unlikely(cond)) { - hppa_dynamic_excp(env, EXCP_COND, GETPC()); - } -} - static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, uint32_t val, uint32_t mask, uintptr_t ra) { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 706537ea59..ae291124f2 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1117,6 +1117,25 @@ static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, return sv; } +static void gen_tc(DisasContext *ctx, DisasCond *cond) +{ + DisasDelayException *e; + + switch (cond->c) { + case TCG_COND_NEVER: + break; + case TCG_COND_ALWAYS: + gen_excp_iir(ctx, EXCP_COND); + break; + default: + e = delay_excp(ctx, EXCP_COND); + tcg_gen_brcond_i64(cond->c, cond->a0, cond->a1, e->lab); + /* In the non-trap path, the condition is known false. */ + *cond = cond_make_f(); + break; + } +} + static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, TCGv_i64 in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) @@ -1175,9 +1194,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, /* Emit any conditional trap before any writeback. */ cond = do_cond(ctx, cf, d, dest, uv, sv); if (is_tc) { - tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1196,6 +1213,10 @@ static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a, { TCGv_i64 tcg_r1, tcg_r2; + if (unlikely(is_tc && a->cf == 1)) { + /* Unconditional trap on condition. */ + return gen_excp_iir(ctx, EXCP_COND); + } if (a->cf) { nullify_over(ctx); } @@ -1211,6 +1232,10 @@ static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, { TCGv_i64 tcg_im, tcg_r2; + if (unlikely(is_tc && a->cf == 1)) { + /* Unconditional trap on condition. */ + return gen_excp_iir(ctx, EXCP_COND); + } if (a->cf) { nullify_over(ctx); } @@ -1225,7 +1250,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, TCGv_i64 in2, bool is_tsv, bool is_b, bool is_tc, unsigned cf, bool d) { - TCGv_i64 dest, sv, cb, cb_msb, tmp; + TCGv_i64 dest, sv, cb, cb_msb; unsigned c = cf >> 1; DisasCond cond; @@ -1273,9 +1298,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, /* Emit any conditional trap before any writeback. */ if (is_tc) { - tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1441,9 +1464,7 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } if (is_tc) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + gen_tc(ctx, &cond); } save_gpr(ctx, rt, dest); From a0ea4becca28c1c14de5c3b8bff8343ab184070c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 13:54:01 -1000 Subject: [PATCH 0693/2884] target/hppa: Use delay_excp for conditional trap on overflow Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.h | 1 - target/hppa/int_helper.c | 2 +- target/hppa/op_helper.c | 7 ------- target/hppa/translate.c | 21 +++++++++++++-------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 3d0d143aed..c12b48a04a 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,5 +1,4 @@ DEF_HELPER_2(excp, noreturn, env, int) -DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 1aa3e88ef1..97e5f0b9a7 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -134,13 +134,13 @@ void hppa_cpu_do_interrupt(CPUState *cs) switch (i) { case EXCP_ILL: case EXCP_BREAK: + case EXCP_OVERFLOW: case EXCP_COND: case EXCP_PRIV_REG: case EXCP_PRIV_OPR: /* IIR set via translate.c. */ break; - case EXCP_OVERFLOW: case EXCP_ASSIST: case EXCP_DTLB_MISS: case EXCP_NA_ITLB_MISS: diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index a8b69fd481..66cad78a57 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -42,13 +42,6 @@ G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) cpu_loop_exit_restore(cs, ra); } -void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) -{ - if (unlikely((target_long)cond < 0)) { - hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC()); - } -} - static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, uint32_t val, uint32_t mask, uintptr_t ra) { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ae291124f2..16b25a9ede 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1136,6 +1136,17 @@ static void gen_tc(DisasContext *ctx, DisasCond *cond) } } +static void gen_tsv(DisasContext *ctx, TCGv_i64 *sv, bool d) +{ + DisasCond cond = do_cond(ctx, /* SV */ 12, d, NULL, NULL, *sv); + DisasDelayException *e = delay_excp(ctx, EXCP_OVERFLOW); + + tcg_gen_brcond_i64(cond.c, cond.a0, cond.a1, e->lab); + + /* In the non-trap path, V is known zero. */ + *sv = tcg_constant_i64(0); +} + static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, TCGv_i64 in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) @@ -1178,10 +1189,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, if (is_tsv || cond_need_sv(c)) { sv = do_add_sv(ctx, dest, in1, in2, orig_in1, shift, d); if (is_tsv) { - if (!d) { - tcg_gen_ext32s_i64(sv, sv); - } - gen_helper_tsv(tcg_env, sv); + gen_tsv(ctx, &sv, d); } } @@ -1282,10 +1290,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, if (is_tsv || cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); if (is_tsv) { - if (!d) { - tcg_gen_ext32s_i64(sv, sv); - } - gen_helper_tsv(tcg_env, sv); + gen_tsv(ctx, &sv, d); } } From 3c13b0ffe76057e93e007bedbad3cc556146e3ed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 10:54:06 -1000 Subject: [PATCH 0694/2884] linux-user/hppa: Force all code addresses to PRIV_USER The kernel does this along the return path to user mode. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- linux-user/elfload.c | 4 ++-- linux-user/hppa/cpu_loop.c | 14 +++++++------- linux-user/hppa/signal.c | 6 ++++-- linux-user/hppa/target_cpu.h | 4 ++-- target/hppa/cpu.c | 7 +++++-- target/hppa/cpu.h | 3 +++ target/hppa/gdbstub.c | 6 ++++++ target/hppa/translate.c | 4 ++-- 8 files changed, 31 insertions(+), 17 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index b473cda6b4..c1e1511ff2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1887,8 +1887,8 @@ static inline void init_thread(struct target_pt_regs *regs, static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { - regs->iaoq[0] = infop->entry; - regs->iaoq[1] = infop->entry + 4; + regs->iaoq[0] = infop->entry | PRIV_USER; + regs->iaoq[1] = regs->iaoq[0] + 4; regs->gr[23] = 0; regs->gr[24] = infop->argv; regs->gr[25] = infop->argc; diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index d5232f37fe..bc093b8fe8 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -129,8 +129,8 @@ void cpu_loop(CPUHPPAState *env) default: env->gr[28] = ret; /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; break; case -QEMU_ERESTARTSYS: case -QEMU_ESIGRETURN: @@ -140,8 +140,8 @@ void cpu_loop(CPUHPPAState *env) case EXCP_SYSCALL_LWS: env->gr[21] = hppa_lws(env); /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; break; case EXCP_IMP: force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, env->iaoq_f); @@ -152,9 +152,9 @@ void cpu_loop(CPUHPPAState *env) case EXCP_PRIV_OPR: /* check for glibc ABORT_INSTRUCTION "iitlbp %r0,(%sr0, %r0)" */ if (env->cr[CR_IIR] == 0x04000000) { - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); } else { - force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); } break; case EXCP_PRIV_REG: @@ -170,7 +170,7 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); break; case EXCP_BREAK: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f & ~3); + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); break; case EXCP_DEBUG: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 682ba25922..f6f094c960 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -101,7 +101,9 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) cpu_hppa_loaded_fr0(env); __get_user(env->iaoq_f, &sc->sc_iaoq[0]); + env->iaoq_f |= PRIV_USER; __get_user(env->iaoq_b, &sc->sc_iaoq[1]); + env->iaoq_b |= PRIV_USER; __get_user(env->cr[CR_SAR], &sc->sc_sar); } @@ -162,8 +164,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, unlock_user(fdesc, haddr, 0); haddr = dest; } - env->iaoq_f = haddr; - env->iaoq_b = haddr + 4; + env->iaoq_f = haddr | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; env->psw_n = 0; return; diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h index aacf3e9e02..4b84422a90 100644 --- a/linux-user/hppa/target_cpu.h +++ b/linux-user/hppa/target_cpu.h @@ -28,8 +28,8 @@ static inline void cpu_clone_regs_child(CPUHPPAState *env, target_ulong newsp, /* Indicate child in return value. */ env->gr[28] = 0; /* Return from the syscall. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; } static inline void cpu_clone_regs_parent(CPUHPPAState *env, unsigned flags) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index be8c558014..a007de5521 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -32,6 +32,9 @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value) { HPPACPU *cpu = HPPA_CPU(cs); +#ifdef CONFIG_USER_ONLY + value |= PRIV_USER; +#endif cpu->env.iaoq_f = value; cpu->env.iaoq_b = value + 4; } @@ -93,8 +96,8 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); #ifdef CONFIG_USER_ONLY - cpu->env.iaoq_f = tb->pc; - cpu->env.iaoq_b = tb->cs_base; + cpu->env.iaoq_f = tb->pc | PRIV_USER; + cpu->env.iaoq_b = tb->cs_base | PRIV_USER; #else /* Recover the IAOQ values from the GVA + PRIV. */ uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index c37b4e12fb..5a1e720bb6 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -42,6 +42,9 @@ #define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) #define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) +#define PRIV_KERNEL 0 +#define PRIV_USER 3 + #define TARGET_INSN_START_EXTRA_WORDS 2 /* No need to flush MMU_ABS*_IDX */ diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 4a965b38d7..0daa52f7af 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -163,12 +163,18 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->cr[CR_SAR] = val & (hppa_is_pa20(env) ? 63 : 31); break; case 33: +#ifdef CONFIG_USER_ONLY + val |= PRIV_USER; +#endif env->iaoq_f = val; break; case 34: env->iasq_f = (uint64_t)val << 32; break; case 35: +#ifdef CONFIG_USER_ONLY + val |= PRIV_USER; +#endif env->iaoq_b = val; break; case 36: diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 16b25a9ede..12359bafb6 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2054,7 +2054,7 @@ static void do_page_zero(DisasContext *ctx) tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); - tcg_gen_ori_i64(next.base, cpu_gr[31], 3); + tcg_gen_ori_i64(next.base, cpu_gr[31], PRIV_USER); install_iaq_entries(ctx, &next, NULL); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; } @@ -4583,7 +4583,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); #ifdef CONFIG_USER_ONLY - ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); + ctx->privilege = PRIV_USER; ctx->mmu_idx = MMU_USER_IDX; ctx->iaoq_first = ctx->base.pc_first | ctx->privilege; ctx->iaq_b.disp = ctx->base.tb->cs_base - ctx->base.pc_first; From 9dfcd2434989bb09b1ca11258180d9095c1d7ba8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 11:52:21 -1000 Subject: [PATCH 0695/2884] target/hppa: Store full iaoq_f and page offset of iaoq_b in TB In preparation for CF_PCREL. store the iaoq_f in 3 parts: high bits in cs_base, middle bits in pc, and low bits in priv. For iaoq_b, set a bit for either of space or page differing, else the page offset. Install iaq entries before goto_tb. The change to not record the full direct branch difference in TB means that we have to store at least iaoq_b before goto_tb. But since a later change to enable CF_PCREL will require both iaoq_f and iaoq_b to be updated before goto_tb, go ahead and update both fields now. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 72 ++++++++++++++++++----------------------- target/hppa/cpu.h | 2 ++ target/hppa/translate.c | 29 +++++++++-------- 3 files changed, 48 insertions(+), 55 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index a007de5521..003af63e20 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -48,36 +48,43 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) } void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) + uint64_t *pcsbase, uint32_t *pflags) { uint32_t flags = env->psw_n * PSW_N; + uint64_t cs_base = 0; + + /* + * TB lookup assumes that PC contains the complete virtual address. + * If we leave space+offset separate, we'll get ITLB misses to an + * incomplete virtual address. This also means that we must separate + * out current cpu privilege from the low bits of IAOQ_F. + */ + *pc = hppa_cpu_get_pc(env_cpu(env)); + flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; + + if (hppa_is_pa20(env)) { + cs_base = env->iaoq_f & MAKE_64BIT_MASK(32, 32); + } + + /* + * The only really interesting case is if IAQ_Back is on the same page + * as IAQ_Front, so that we can use goto_tb between the blocks. In all + * other cases, we'll be ending the TranslationBlock with one insn and + * not linking between them. + */ + if (env->iasq_f != env->iasq_b) { + cs_base |= CS_BASE_DIFFSPACE; + } else if ((env->iaoq_f ^ env->iaoq_b) & TARGET_PAGE_MASK) { + cs_base |= CS_BASE_DIFFPAGE; + } else { + cs_base |= env->iaoq_b & ~TARGET_PAGE_MASK; + } - /* TB lookup assumes that PC contains the complete virtual address. - If we leave space+offset separate, we'll get ITLB misses to an - incomplete virtual address. This also means that we must separate - out current cpu privilege from the low bits of IAOQ_F. */ #ifdef CONFIG_USER_ONLY - *pc = env->iaoq_f & -4; - *cs_base = env->iaoq_b & -4; flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #else /* ??? E, T, H, L, B bits need to be here, when implemented. */ flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); - flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - - *pc = hppa_cpu_get_pc(env_cpu(env)); - *cs_base = env->iasq_f; - - /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero - low 32-bits of CS_BASE. This will succeed for all direct branches, - which is the primary case we care about -- using goto_tb within a page. - Failure is indicated by a zero difference. */ - if (env->iasq_f == env->iasq_b) { - target_long diff = env->iaoq_b - env->iaoq_f; - if (diff == (int32_t)diff) { - *cs_base |= (uint32_t)diff; - } - } if ((env->sr[4] == env->sr[5]) & (env->sr[4] == env->sr[6]) & (env->sr[4] == env->sr[7])) { @@ -85,6 +92,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, } #endif + *pcsbase = cs_base; *pflags = flags; } @@ -93,25 +101,7 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, { HPPACPU *cpu = HPPA_CPU(cs); - tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); - -#ifdef CONFIG_USER_ONLY - cpu->env.iaoq_f = tb->pc | PRIV_USER; - cpu->env.iaoq_b = tb->cs_base | PRIV_USER; -#else - /* Recover the IAOQ values from the GVA + PRIV. */ - uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3; - target_ulong cs_base = tb->cs_base; - target_ulong iasq_f = cs_base & ~0xffffffffull; - int32_t diff = cs_base; - - cpu->env.iasq_f = iasq_f; - cpu->env.iaoq_f = (tb->pc & ~iasq_f) + priv; - if (diff) { - cpu->env.iaoq_b = cpu->env.iaoq_f + diff; - } -#endif - + /* IAQ is always up-to-date before goto_tb. */ cpu->env.psw_n = (tb->flags & PSW_N) != 0; } diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 5a1e720bb6..1232a4cef2 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -341,6 +341,8 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); #define TB_FLAG_SR_SAME PSW_I #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 +#define CS_BASE_DIFFPAGE (1 << 12) +#define CS_BASE_DIFFSPACE (1 << 13) void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, uint64_t *cs_base, uint32_t *pflags); diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 12359bafb6..70df42f558 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -770,12 +770,11 @@ static bool use_nullify_skip(DisasContext *ctx) static void gen_goto_tb(DisasContext *ctx, int which, const DisasIAQE *f, const DisasIAQE *b) { + install_iaq_entries(ctx, f, b); if (use_goto_tb(ctx, f, b)) { tcg_gen_goto_tb(which); - install_iaq_entries(ctx, f, b); tcg_gen_exit_tb(ctx->base.tb, which); } else { - install_iaq_entries(ctx, f, b); tcg_gen_lookup_and_goto_ptr(); } } @@ -4576,6 +4575,7 @@ static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint64_t cs_base, iaoq_f, iaoq_b; int bound; ctx->cs = cs; @@ -4585,29 +4585,30 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #ifdef CONFIG_USER_ONLY ctx->privilege = PRIV_USER; ctx->mmu_idx = MMU_USER_IDX; - ctx->iaoq_first = ctx->base.pc_first | ctx->privilege; - ctx->iaq_b.disp = ctx->base.tb->cs_base - ctx->base.pc_first; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; ctx->mmu_idx = (ctx->tb_flags & PSW_D ? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P) : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); +#endif /* Recover the IAOQ values from the GVA + PRIV. */ - uint64_t cs_base = ctx->base.tb->cs_base; - uint64_t iasq_f = cs_base & ~0xffffffffull; - int32_t diff = cs_base; + cs_base = ctx->base.tb->cs_base; + iaoq_f = cs_base & MAKE_64BIT_MASK(32, 32); + iaoq_f |= ctx->base.pc_first & MAKE_64BIT_MASK(2, 30); + iaoq_f |= ctx->privilege; + ctx->iaoq_first = iaoq_f; - ctx->iaoq_first = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; - - if (diff) { - ctx->iaq_b.disp = diff; - } else { - ctx->iaq_b.base = cpu_iaoq_b; + if (unlikely(cs_base & CS_BASE_DIFFSPACE)) { ctx->iaq_b.space = cpu_iasq_b; + ctx->iaq_b.base = cpu_iaoq_b; + } else if (unlikely(cs_base & CS_BASE_DIFFPAGE)) { + ctx->iaq_b.base = cpu_iaoq_b; + } else { + iaoq_b = (iaoq_f & TARGET_PAGE_MASK) | (cs_base & ~TARGET_PAGE_MASK); + ctx->iaq_b.disp = iaoq_b - iaoq_f; } -#endif ctx->zero = tcg_constant_i64(0); From 081a0ed188d8d9d9038f00337d331d185a7ae331 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 13:04:00 -1000 Subject: [PATCH 0696/2884] target/hppa: Do not mask in copy_iaoq_entry As with loads and stores, code offsets are kept intact until the full gva is formed. In qemu, this is in cpu_get_tb_cpu_state. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 70df42f558..ab8dd161ad 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -637,15 +637,10 @@ static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var) static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, const DisasIAQE *src) { - uint64_t mask = gva_offset_mask(ctx->tb_flags); - if (src->base == NULL) { - tcg_gen_movi_i64(dest, (ctx->iaoq_first + src->disp) & mask); - } else if (src->disp == 0) { - tcg_gen_andi_i64(dest, src->base, mask); + tcg_gen_movi_i64(dest, ctx->iaoq_first + src->disp); } else { tcg_gen_addi_i64(dest, src->base, src->disp); - tcg_gen_andi_i64(dest, dest, mask); } } From d2e22fde144ea5f32980031537a7e1bb88e8bcf1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Mar 2024 11:06:38 -1000 Subject: [PATCH 0697/2884] target/hppa: Improve hppa_cpu_dump_state Print both raw IAQ_Front and IAQ_Back as well as the GVAs. Print control registers in system mode. Print floating point registers if CPU_DUMP_FPU. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.c | 60 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 9d217d051c..7d22c248fb 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -102,6 +102,19 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) { +#ifndef CONFIG_USER_ONLY + static const char cr_name[32][5] = { + "RC", "CR1", "CR2", "CR3", + "CR4", "CR5", "CR6", "CR7", + "PID1", "PID2", "CCR", "SAR", + "PID3", "PID4", "IVA", "EIEM", + "ITMR", "ISQF", "IOQF", "IIR", + "ISR", "IOR", "IPSW", "EIRR", + "TR0", "TR1", "TR2", "TR3", + "TR4", "TR5", "TR6", "TR7", + }; +#endif + CPUHPPAState *env = cpu_env(cs); target_ulong psw = cpu_hppa_get_psw(env); target_ulong psw_cb; @@ -117,11 +130,12 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) m = UINT32_MAX; } - qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx - " IIR %0*" PRIx64 "\n", + qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n" + "IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n", + env->iasq_f >> 32, w, m & env->iaoq_f, hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f), - hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b), - w, m & env->cr[CR_IIR]); + env->iasq_b >> 32, w, m & env->iaoq_b, + hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b)); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); @@ -154,12 +168,46 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) (i & 3) == 3 ? '\n' : ' '); } #ifndef CONFIG_USER_ONLY + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "%-4s %0*" PRIx64 "%c", + cr_name[i], w, m & env->cr[i], + (i & 3) == 3 ? '\n' : ' '); + } + qemu_fprintf(f, "ISQB %0*" PRIx64 " IOQB %0*" PRIx64 "\n", + w, m & env->cr_back[0], w, m & env->cr_back[1]); for (i = 0; i < 8; i++) { qemu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32), (i & 3) == 3 ? '\n' : ' '); } #endif - qemu_fprintf(f, "\n"); - /* ??? FR */ + if (flags & CPU_DUMP_FPU) { + static const char rm[4][4] = { "RN", "RZ", "R+", "R-" }; + char flg[6], ena[6]; + uint32_t fpsr = env->fr0_shadow; + + flg[0] = (fpsr & R_FPSR_FLG_V_MASK ? 'V' : '-'); + flg[1] = (fpsr & R_FPSR_FLG_Z_MASK ? 'Z' : '-'); + flg[2] = (fpsr & R_FPSR_FLG_O_MASK ? 'O' : '-'); + flg[3] = (fpsr & R_FPSR_FLG_U_MASK ? 'U' : '-'); + flg[4] = (fpsr & R_FPSR_FLG_I_MASK ? 'I' : '-'); + flg[5] = '\0'; + + ena[0] = (fpsr & R_FPSR_ENA_V_MASK ? 'V' : '-'); + ena[1] = (fpsr & R_FPSR_ENA_Z_MASK ? 'Z' : '-'); + ena[2] = (fpsr & R_FPSR_ENA_O_MASK ? 'O' : '-'); + ena[3] = (fpsr & R_FPSR_ENA_U_MASK ? 'U' : '-'); + ena[4] = (fpsr & R_FPSR_ENA_I_MASK ? 'I' : '-'); + ena[5] = '\0'; + + qemu_fprintf(f, "FPSR %08x flag %s enable %s %s\n", + fpsr, flg, ena, rm[FIELD_EX32(fpsr, FPSR, RM)]); + + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "FR%02d %016" PRIx64 "%c", + i, env->fr[i], (i & 3) == 3 ? '\n' : ' '); + } + } + + qemu_fprintf(f, "\n"); } From ebc9401a4067fd61afea811d1d059d8ac0fc5db9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 20:23:16 -0700 Subject: [PATCH 0698/2884] target/hppa: Split PSW X and B into their own field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generally, both of these bits are cleared at the end of each instruction. By separating these, we will be able to clear both with a single insn, instead of 2 or 3. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 3 ++- target/hppa/helper.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 1232a4cef2..f247ad56d7 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -208,7 +208,8 @@ typedef struct CPUArchState { uint64_t fr[32]; uint64_t sr[8]; /* stored shifted into place for gva */ - target_ulong psw; /* All psw bits except the following: */ + uint32_t psw; /* All psw bits except the following: */ + uint32_t psw_xb; /* X and B, in their normal positions */ target_ulong psw_n; /* boolean */ target_long psw_v; /* in most significant bit */ diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 7d22c248fb..b79ddd8184 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -54,7 +54,7 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env) psw |= env->psw_n * PSW_N; psw |= (env->psw_v < 0) * PSW_V; - psw |= env->psw; + psw |= env->psw | env->psw_xb; return psw; } @@ -76,8 +76,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) } psw &= ~reserved; - env->psw = psw & (uint32_t)~(PSW_N | PSW_V | PSW_CB); - + env->psw = psw & (uint32_t)~(PSW_B | PSW_N | PSW_V | PSW_X | PSW_CB); + env->psw_xb = psw & (PSW_X | PSW_B); env->psw_n = (psw / PSW_N) & 1; env->psw_v = -((psw / PSW_V) & 1); From d27fe7c3af30a9b6281e7aafb5d603efe64ff939 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 20:43:00 -0700 Subject: [PATCH 0699/2884] target/hppa: Manage PSW_X and PSW_B in translator PSW_X is cleared after every instruction, and only set by RFI. PSW_B is cleared after every non-branch, or branch not taken, and only set by taken branches. We can clear both bits with a single store, at most once per TB. Taken branches set PSW_B, at most once per TB. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 10 ++++++--- target/hppa/translate.c | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 003af63e20..5f0df0697a 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -50,7 +50,7 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, uint64_t *pcsbase, uint32_t *pflags) { - uint32_t flags = env->psw_n * PSW_N; + uint32_t flags = 0; uint64_t cs_base = 0; /* @@ -80,11 +80,14 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, cs_base |= env->iaoq_b & ~TARGET_PAGE_MASK; } + /* ??? E, T, H, L bits need to be here, when implemented. */ + flags |= env->psw_n * PSW_N; + flags |= env->psw_xb; + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); + #ifdef CONFIG_USER_ONLY flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #else - /* ??? E, T, H, L, B bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); if ((env->sr[4] == env->sr[5]) & (env->sr[4] == env->sr[6]) & (env->sr[4] == env->sr[7])) { @@ -103,6 +106,7 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, /* IAQ is always up-to-date before goto_tb. */ cpu->env.psw_n = (tb->flags & PSW_N) != 0; + cpu->env.psw_xb = tb->flags & (PSW_X | PSW_B); } static void hppa_restore_state_to_opc(CPUState *cs, diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ab8dd161ad..f7d54f4009 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -84,7 +84,9 @@ typedef struct DisasContext { uint32_t tb_flags; int mmu_idx; int privilege; + uint32_t psw_xb; bool psw_n_nonzero; + bool psw_b_next; bool is_pa20; bool insn_start_updated; @@ -263,6 +265,7 @@ static TCGv_i64 cpu_psw_n; static TCGv_i64 cpu_psw_v; static TCGv_i64 cpu_psw_cb; static TCGv_i64 cpu_psw_cb_msb; +static TCGv_i32 cpu_psw_xb; void hppa_translate_init(void) { @@ -315,6 +318,9 @@ void hppa_translate_init(void) *v->var = tcg_global_mem_new(tcg_env, v->ofs, v->name); } + cpu_psw_xb = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHPPAState, psw_xb), + "psw_xb"); cpu_iasq_f = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHPPAState, iasq_f), "iasq_f"); @@ -509,6 +515,25 @@ static void load_spr(DisasContext *ctx, TCGv_i64 dest, unsigned reg) #endif } +/* + * Write a value to psw_xb, bearing in mind the known value. + * To be used just before exiting the TB, so do not update the known value. + */ +static void store_psw_xb(DisasContext *ctx, uint32_t xb) +{ + tcg_debug_assert(xb == 0 || xb == PSW_B); + if (ctx->psw_xb != xb) { + tcg_gen_movi_i32(cpu_psw_xb, xb); + } +} + +/* Write a value to psw_xb, and update the known value. */ +static void set_psw_xb(DisasContext *ctx, uint32_t xb) +{ + store_psw_xb(ctx, xb); + ctx->psw_xb = xb; +} + /* Skip over the implementation of an insn that has been nullified. Use this when the insn is too complex for a conditional move. */ static void nullify_over(DisasContext *ctx) @@ -576,6 +601,8 @@ static bool nullify_end(DisasContext *ctx) /* For NEXT, NORETURN, STALE, we can easily continue (or exit). For UPDATED, we cannot update on the nullified path. */ assert(status != DISAS_IAQ_N_UPDATED); + /* Taken branches are handled manually. */ + assert(!ctx->psw_b_next); if (likely(null_lab == NULL)) { /* The current insn wasn't conditional or handled the condition @@ -1843,6 +1870,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, if (is_n) { if (use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -1850,20 +1878,24 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, ctx->null_cond.c = TCG_COND_ALWAYS; } ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; } else { nullify_over(ctx); install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); } else { nullify_set(ctx, is_n); + store_psw_xb(ctx, PSW_B); gen_goto_tb(ctx, 0, &ctx->iaq_b, &ctx->iaq_j); } nullify_end(ctx); nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 1, &ctx->iaq_b, NULL); ctx->base.is_jmp = DISAS_NORETURN; } @@ -1894,6 +1926,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, n = is_n && disp < 0; if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); next = iaqe_incr(&ctx->iaq_b, 4); gen_goto_tb(ctx, 0, &next, NULL); } else { @@ -1902,6 +1935,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, ctx->null_lab = NULL; } nullify_set(ctx, n); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 0, &ctx->iaq_b, NULL); } @@ -1913,9 +1947,11 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, next = iaqe_branchi(ctx, disp); if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 1, &next, NULL); } else { nullify_set(ctx, n); + store_psw_xb(ctx, PSW_B); gen_goto_tb(ctx, 1, &ctx->iaq_b, &next); } @@ -1949,6 +1985,7 @@ static bool do_ibranch(DisasContext *ctx, unsigned link, ctx->null_cond.c = TCG_COND_ALWAYS; } ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; return true; } @@ -1958,9 +1995,11 @@ static bool do_ibranch(DisasContext *ctx, unsigned link, if (is_n && use_nullify_skip(ctx)) { install_iaq_entries(ctx, &ctx->iaq_j, NULL); nullify_set(ctx, 0); + store_psw_xb(ctx, 0); } else { install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j); nullify_set(ctx, is_n); + store_psw_xb(ctx, PSW_B); } tcg_gen_lookup_and_goto_ptr(); @@ -2387,6 +2426,7 @@ static bool trans_halt(DisasContext *ctx, arg_halt *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY + set_psw_xb(ctx, 0); nullify_over(ctx); gen_helper_halt(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; @@ -2398,6 +2438,7 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY + set_psw_xb(ctx, 0); nullify_over(ctx); gen_helper_reset(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; @@ -2792,6 +2833,9 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */ /* No need to check for supervisor, as userland can only pause until the next timer interrupt. */ + + set_psw_xb(ctx, 0); + nullify_over(ctx); /* Advance the instruction queue. */ @@ -4576,6 +4620,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cs = cs; ctx->tb_flags = ctx->base.tb->flags; ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); + ctx->psw_xb = ctx->tb_flags & (PSW_X | PSW_B); #ifdef CONFIG_USER_ONLY ctx->privilege = PRIV_USER; @@ -4662,6 +4707,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) */ ctx->iaq_n = NULL; memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j)); + ctx->psw_b_next = false; if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4674,6 +4720,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ret = ctx->base.is_jmp; assert(ctx->null_lab == NULL); } + + if (ret != DISAS_NORETURN) { + set_psw_xb(ctx, ctx->psw_b_next ? PSW_B : 0); + } } /* If the TranslationBlock must end, do so. */ From 5ae8adbb01cdb0bd2f3c1c444b3e543b38737102 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 20:50:30 -0700 Subject: [PATCH 0700/2884] target/hppa: Implement PSW_B PSW_B causes B,GATE to trap as an illegal instruction, removing our previous sequential execution test that was merely an approximation. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f7d54f4009..f40ac92e98 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2062,11 +2062,8 @@ static void do_page_zero(DisasContext *ctx) g_assert_not_reached(); } - /* Check that we didn't arrive here via some means that allowed - non-sequential instruction execution. Normally the PSW[B] bit - detects this by disallowing the B,GATE instruction to execute - under such conditions. */ - if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != 4) { + /* If PSW[B] is set, the B,GATE insn would trap. */ + if (ctx->psw_xb & PSW_B) { goto do_sigill; } @@ -3965,23 +3962,13 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { int64_t disp = a->disp; - nullify_over(ctx); - - /* Make sure the caller hasn't done something weird with the queue. - * ??? This is not quite the same as the PSW[B] bit, which would be - * expensive to track. Real hardware will trap for - * b gateway - * b gateway+4 (in delay slot of first branch) - * However, checking for a non-sequential instruction queue *will* - * diagnose the security hole - * b gateway - * b evil - * in which instructions at evil would run with increased privs. - */ - if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { + /* Trap if PSW[B] is set. */ + if (ctx->psw_xb & PSW_B) { return gen_illegal(ctx); } + nullify_over(ctx); + #ifndef CONFIG_USER_ONLY if (ctx->tb_flags & PSW_C) { int type = hppa_artype_for_page(cpu_env(ctx->cs), ctx->base.pc_next); From d8bc1381250b39079d5ed33d2be7eee801ca41c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 17:39:25 -0700 Subject: [PATCH 0701/2884] target/hppa: Implement PSW_X Use PAGE_WRITE_INV to temporarily enable write permission on for a given page, driven by PSW_X being set. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/mem_helper.c | 46 +++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index d09877afd7..ca7bbe0a7c 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -296,30 +296,38 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, goto egress; } - /* In reverse priority order, check for conditions which raise faults. - As we go, remove PROT bits that cover the condition we want to check. - In this way, the resulting PROT will force a re-check of the - architectural TLB entry for the next access. */ - if (unlikely(!ent->d)) { - if (type & PAGE_WRITE) { - /* The D bit is not set -- TLB Dirty Bit Fault. */ - ret = EXCP_TLB_DIRTY; - } - prot &= PAGE_READ | PAGE_EXEC; - } - if (unlikely(ent->b)) { - if (type & PAGE_WRITE) { - /* The B bit is set -- Data Memory Break Fault. */ - ret = EXCP_DMB; - } - prot &= PAGE_READ | PAGE_EXEC; - } + /* + * In priority order, check for conditions which raise faults. + * Remove PROT bits that cover the condition we want to check, + * so that the resulting PROT will force a re-check of the + * architectural TLB entry for the next access. + */ if (unlikely(ent->t)) { + prot &= PAGE_EXEC; if (!(type & PAGE_EXEC)) { /* The T bit is set -- Page Reference Fault. */ ret = EXCP_PAGE_REF; } - prot &= PAGE_EXEC; + } else if (!ent->d) { + prot &= PAGE_READ | PAGE_EXEC; + if (type & PAGE_WRITE) { + /* The D bit is not set -- TLB Dirty Bit Fault. */ + ret = EXCP_TLB_DIRTY; + } + } else if (unlikely(ent->b)) { + prot &= PAGE_READ | PAGE_EXEC; + if (type & PAGE_WRITE) { + /* + * The B bit is set -- Data Memory Break Fault. + * Except when PSW_X is set, allow this single access to succeed. + * The write bit will be invalidated for subsequent accesses. + */ + if (env->psw_xb & PSW_X) { + prot |= PAGE_WRITE_INV; + } else { + ret = EXCP_DMB; + } + } } egress: From 190d7fa5721dced9704717d64b247552a9152482 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 16:50:58 -0700 Subject: [PATCH 0702/2884] target/hppa: Drop tlb_entry return from hppa_get_physical_address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return-by-reference is never used. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 3 +-- target/hppa/int_helper.c | 2 +- target/hppa/mem_helper.c | 19 ++++--------------- target/hppa/op_helper.c | 3 +-- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index f247ad56d7..78ab0adcd0 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -371,8 +371,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, - int type, hwaddr *pphys, int *pprot, - HPPATLBEntry **tlb_entry); + int type, hwaddr *pphys, int *pprot); void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 97e5f0b9a7..b82f32fd12 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -167,7 +167,7 @@ void hppa_cpu_do_interrupt(CPUState *cs) vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr); t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX, - 0, &paddr, &prot, NULL); + 0, &paddr, &prot); if (t >= 0) { /* We can't re-load the instruction. */ env->cr[CR_IIR] = 0; diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index ca7bbe0a7c..2929226874 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -197,18 +197,13 @@ static int match_prot_id64(CPUHPPAState *env, uint32_t access_id) } int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, - int type, hwaddr *pphys, int *pprot, - HPPATLBEntry **tlb_entry) + int type, hwaddr *pphys, int *pprot) { hwaddr phys; int prot, r_prot, w_prot, x_prot, priv; HPPATLBEntry *ent; int ret = -1; - if (tlb_entry) { - *tlb_entry = NULL; - } - /* Virtual translation disabled. Map absolute to physical. */ if (MMU_IDX_MMU_DISABLED(mmu_idx)) { switch (mmu_idx) { @@ -238,10 +233,6 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, goto egress; } - if (tlb_entry) { - *tlb_entry = ent; - } - /* We now know the physical address. */ phys = ent->pa + (addr - ent->itree.start); @@ -350,7 +341,7 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) cpu->env.psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0, - &phys, &prot, NULL); + &phys, &prot); /* Since we're translating for debugging, the only error that is a hard error is no translation at all. Otherwise, while a real cpu @@ -432,7 +423,6 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; - HPPATLBEntry *ent; int prot, excp, a_prot; hwaddr phys; @@ -448,8 +438,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, break; } - excp = hppa_get_physical_address(env, addr, mmu_idx, - a_prot, &phys, &prot, &ent); + excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, &phys, &prot); if (unlikely(excp >= 0)) { if (probe) { return false; @@ -690,7 +679,7 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) int prot, excp; excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0, - &phys, &prot, NULL); + &phys, &prot); if (excp >= 0) { if (excp == EXCP_DTLB_MISS) { excp = EXCP_NA_DTLB_MISS; diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 66cad78a57..7f79196fff 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -334,8 +334,7 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, } mmu_idx = PRIV_P_TO_MMU_IDX(level, env->psw & PSW_P); - excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, - &prot, NULL); + excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, &prot); if (excp >= 0) { cpu_restore_state(env_cpu(env), GETPC()); hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx)); From 804cd52d3a314799adfa7d931e00c85856c54206 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 20:39:15 -0700 Subject: [PATCH 0703/2884] target/hppa: Adjust priv for B,GATE at runtime Do not compile in the priv change based on the first translation; look up the PTE at execution time. This is required for CF_PCREL, where a page may be mapped multiple times with different attributes. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 1 - target/hppa/helper.h | 1 + target/hppa/mem_helper.c | 34 +++++++++++++++++++++++++++------- target/hppa/translate.c | 36 +++++++++++++++++++----------------- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 78ab0adcd0..2bcb3b602b 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -380,7 +380,6 @@ void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, extern const MemoryRegionOps hppa_io_eir_ops; extern const VMStateDescription vmstate_hppa_cpu; void hppa_cpu_alarm_timer(void *); -int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr); #endif G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); diff --git a/target/hppa/helper.h b/target/hppa/helper.h index c12b48a04a..de411923d9 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -86,6 +86,7 @@ DEF_HELPER_1(halt, noreturn, env) DEF_HELPER_1(reset, noreturn, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(rfi_r, void, env) +DEF_HELPER_FLAGS_2(b_gate_priv, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tl, env, tl) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 2929226874..b984f730aa 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -691,13 +691,6 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) return phys; } -/* Return the ar_type of the TLB at VADDR, or -1. */ -int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr) -{ - HPPATLBEntry *ent = hppa_find_tlb(env, vaddr); - return ent ? ent->ar_type : -1; -} - /* * diag_btlb() emulates the PDC PDC_BLOCK_TLB firmware call to * allow operating systems to modify the Block TLB (BTLB) entries. @@ -793,3 +786,30 @@ void HELPER(diag_btlb)(CPUHPPAState *env) break; } } + +uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f) +{ + uint64_t gva = hppa_form_gva(env, env->iasq_f, iaoq_f); + HPPATLBEntry *ent = hppa_find_tlb(env, gva); + + if (ent == NULL) { + raise_exception_with_ior(env, EXCP_ITLB_MISS, GETPC(), gva, false); + } + + /* + * There should be no need to check page permissions, as that will + * already have been done by tb_lookup via get_page_addr_code. + * All we need at this point is to check the ar_type. + * + * No change for non-gateway pages or for priv decrease. + */ + if (ent->ar_type & 4) { + int old_priv = iaoq_f & 3; + int new_priv = ent->ar_type & 3; + + if (new_priv < old_priv) { + iaoq_f = (iaoq_f & -4) | new_priv; + } + } + return iaoq_f; +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f40ac92e98..c2cee89a6a 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3961,6 +3961,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *a) static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { int64_t disp = a->disp; + bool indirect = false; /* Trap if PSW[B] is set. */ if (ctx->psw_xb & PSW_B) { @@ -3970,24 +3971,22 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) nullify_over(ctx); #ifndef CONFIG_USER_ONLY - if (ctx->tb_flags & PSW_C) { - int type = hppa_artype_for_page(cpu_env(ctx->cs), ctx->base.pc_next); - /* If we could not find a TLB entry, then we need to generate an - ITLB miss exception so the kernel will provide it. - The resulting TLB fill operation will invalidate this TB and - we will re-translate, at which point we *will* be able to find - the TLB entry and determine if this is in fact a gateway page. */ - if (type < 0) { - gen_excp(ctx, EXCP_ITLB_MISS); - return true; - } - /* No change for non-gateway pages or for priv decrease. */ - if (type >= 4 && type - 4 < ctx->privilege) { - disp -= ctx->privilege; - disp += type - 4; - } + if (ctx->privilege == 0) { + /* Privilege cannot decrease. */ + } else if (!(ctx->tb_flags & PSW_C)) { + /* With paging disabled, priv becomes 0. */ + disp -= ctx->privilege; } else { - disp -= ctx->privilege; /* priv = 0 */ + /* Adjust the dest offset for the privilege change from the PTE. */ + TCGv_i64 off = tcg_temp_new_i64(); + + gen_helper_b_gate_priv(off, tcg_env, + tcg_constant_i64(ctx->iaoq_first + + ctx->iaq_f.disp)); + + ctx->iaq_j.base = off; + ctx->iaq_j.disp = disp + 8; + indirect = true; } #endif @@ -4000,6 +3999,9 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) save_gpr(ctx, a->l, tmp); } + if (indirect) { + return do_ibranch(ctx, 0, false, a->n); + } return do_dbranch(ctx, disp, 0, a->n); } From 6dd9b145f65ea425b220426f276b9074bbd429fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 20:57:13 -0700 Subject: [PATCH 0704/2884] target/hppa: Implement CF_PCREL Now that the groundwork has been laid, enabling CF_PCREL within the translator proper is a simple matter of updating copy_iaoq_entry and install_iaq_entries. We also need to modify the unwind info, since we no longer have absolute addresses to install. As expected, this reduces the runtime overhead of compilation when running a Linux kernel with address space randomization enabled. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 19 ++++++------ target/hppa/translate.c | 68 ++++++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 5f0df0697a..f0507874ce 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -62,10 +62,6 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, *pc = hppa_cpu_get_pc(env_cpu(env)); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - if (hppa_is_pa20(env)) { - cs_base = env->iaoq_f & MAKE_64BIT_MASK(32, 32); - } - /* * The only really interesting case is if IAQ_Back is on the same page * as IAQ_Front, so that we can use goto_tb between the blocks. In all @@ -113,19 +109,19 @@ static void hppa_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); - cpu->env.iaoq_f = data[0]; - if (data[1] != (target_ulong)-1) { - cpu->env.iaoq_b = data[1]; + env->iaoq_f = (env->iaoq_f & TARGET_PAGE_MASK) | data[0]; + if (data[1] != INT32_MIN) { + env->iaoq_b = env->iaoq_f + data[1]; } - cpu->env.unwind_breg = data[2]; + env->unwind_breg = data[2]; /* * Since we were executing the instruction at IAOQ_F, and took some * sort of action that provoked the cpu_restore_state, we can infer * that the instruction was not nullified. */ - cpu->env.psw_n = 0; + env->psw_n = 0; } static bool hppa_cpu_has_work(CPUState *cs) @@ -191,6 +187,9 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) hppa_ptlbe(&cpu->env); } #endif + + /* Use pc-relative instructions always to simplify the translator. */ + tcg_cflags_set(cs, CF_PCREL); } static void hppa_cpu_initfn(Object *obj) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c2cee89a6a..c0920a3c29 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -47,7 +47,7 @@ typedef struct DisasIAQE { TCGv_i64 space; /* IAOQ base; may be null for relative address. */ TCGv_i64 base; - /* IAOQ addend; if base is null, relative to ctx->iaoq_first. */ + /* IAOQ addend; if base is null, relative to cpu_iaoq_f. */ int64_t disp; } DisasIAQE; @@ -664,11 +664,7 @@ static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var) static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, const DisasIAQE *src) { - if (src->base == NULL) { - tcg_gen_movi_i64(dest, ctx->iaoq_first + src->disp); - } else { - tcg_gen_addi_i64(dest, src->base, src->disp); - } + tcg_gen_addi_i64(dest, src->base ? : cpu_iaoq_f, src->disp); } static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, @@ -680,8 +676,28 @@ static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, b_next = iaqe_incr(f, 4); b = &b_next; } - copy_iaoq_entry(ctx, cpu_iaoq_f, f); - copy_iaoq_entry(ctx, cpu_iaoq_b, b); + + /* + * There is an edge case + * bv r0(rN) + * b,l disp,r0 + * for which F will use cpu_iaoq_b (from the indirect branch), + * and B will use cpu_iaoq_f (from the direct branch). + * In this case we need an extra temporary. + */ + if (f->base != cpu_iaoq_b) { + copy_iaoq_entry(ctx, cpu_iaoq_b, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + } else if (f->base == b->base) { + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, b->disp - f->disp); + } else { + TCGv_i64 tmp = tcg_temp_new_i64(); + copy_iaoq_entry(ctx, tmp, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + tcg_gen_mov_i64(cpu_iaoq_b, tmp); + } + if (f->space) { tcg_gen_mov_i64(cpu_iasq_f, f->space); } @@ -3980,9 +3996,8 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) /* Adjust the dest offset for the privilege change from the PTE. */ TCGv_i64 off = tcg_temp_new_i64(); - gen_helper_b_gate_priv(off, tcg_env, - tcg_constant_i64(ctx->iaoq_first - + ctx->iaq_f.disp)); + copy_iaoq_entry(ctx, off, &ctx->iaq_f); + gen_helper_b_gate_priv(off, tcg_env, off); ctx->iaq_j.base = off; ctx->iaq_j.disp = disp + 8; @@ -4603,7 +4618,7 @@ static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - uint64_t cs_base, iaoq_f, iaoq_b; + uint64_t cs_base; int bound; ctx->cs = cs; @@ -4622,12 +4637,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); #endif - /* Recover the IAOQ values from the GVA + PRIV. */ cs_base = ctx->base.tb->cs_base; - iaoq_f = cs_base & MAKE_64BIT_MASK(32, 32); - iaoq_f |= ctx->base.pc_first & MAKE_64BIT_MASK(2, 30); - iaoq_f |= ctx->privilege; - ctx->iaoq_first = iaoq_f; + ctx->iaoq_first = ctx->base.pc_first + ctx->privilege; if (unlikely(cs_base & CS_BASE_DIFFSPACE)) { ctx->iaq_b.space = cpu_iasq_b; @@ -4635,8 +4646,9 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) } else if (unlikely(cs_base & CS_BASE_DIFFPAGE)) { ctx->iaq_b.base = cpu_iaoq_b; } else { - iaoq_b = (iaoq_f & TARGET_PAGE_MASK) | (cs_base & ~TARGET_PAGE_MASK); - ctx->iaq_b.disp = iaoq_b - iaoq_f; + uint64_t iaoq_f_pgofs = ctx->iaoq_first & ~TARGET_PAGE_MASK; + uint64_t iaoq_b_pgofs = cs_base & ~TARGET_PAGE_MASK; + ctx->iaq_b.disp = iaoq_b_pgofs - iaoq_f_pgofs; } ctx->zero = tcg_constant_i64(0); @@ -4663,11 +4675,23 @@ static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint64_t iaoq_f, iaoq_b; + int64_t diff; tcg_debug_assert(!iaqe_variable(&ctx->iaq_f)); - tcg_gen_insn_start(ctx->iaoq_first + ctx->iaq_f.disp, - (iaqe_variable(&ctx->iaq_b) ? -1 : - ctx->iaoq_first + ctx->iaq_b.disp), 0); + + iaoq_f = ctx->iaoq_first + ctx->iaq_f.disp; + if (iaqe_variable(&ctx->iaq_b)) { + diff = INT32_MIN; + } else { + iaoq_b = ctx->iaoq_first + ctx->iaq_b.disp; + diff = iaoq_b - iaoq_f; + /* Direct branches can only produce a 24-bit displacement. */ + tcg_debug_assert(diff == (int32_t)diff); + tcg_debug_assert(diff != INT32_MIN); + } + + tcg_gen_insn_start(iaoq_f & ~TARGET_PAGE_MASK, diff, 0); ctx->insn_start_updated = false; } From 12959fcdcf87dd97aabf844f57329d46e9c54da3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 21:27:56 -0700 Subject: [PATCH 0705/2884] target/hppa: Log cpu state at interrupt This contains all of the information logged before, plus more. Signed-off-by: Richard Henderson --- target/hppa/int_helper.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index b82f32fd12..391f32f27d 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -241,21 +241,22 @@ void hppa_cpu_do_interrupt(CPUState *cs) [EXCP_SYSCALL_LWS] = "syscall-lws", [EXCP_TOC] = "TOC (transfer of control)", }; - static int count; - const char *name = NULL; - char unknown[16]; - if (i >= 0 && i < ARRAY_SIZE(names)) { - name = names[i]; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + const char *name = NULL; + + if (i >= 0 && i < ARRAY_SIZE(names)) { + name = names[i]; + } + if (name) { + fprintf(logfile, "INT: cpu %d %s\n", cs->cpu_index, name); + } else { + fprintf(logfile, "INT: cpu %d unknown %d\n", cs->cpu_index, i); + } + hppa_cpu_dump_state(cs, logfile, 0); + qemu_log_unlock(logfile); } - if (!name) { - snprintf(unknown, sizeof(unknown), "unknown %d", i); - name = unknown; - } - qemu_log("INT %6d: %s @ " TARGET_FMT_lx ":" TARGET_FMT_lx - " for " TARGET_FMT_lx ":" TARGET_FMT_lx "\n", - ++count, name, env->cr[CR_IIASQ], env->cr[CR_IIAOQ], - env->cr[CR_ISR], env->cr[CR_IOR]); } cs->exception_index = -1; } From 9e035f00788c52a6f51529c54371a611d9f8b089 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 21:32:24 -0700 Subject: [PATCH 0706/2884] target/hppa: Log cpu state on return-from-interrupt Inverse of the logging on taking an interrupt. Signed-off-by: Richard Henderson --- target/hppa/sys_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 22d6c89964..9b43b556fd 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -93,6 +94,17 @@ void HELPER(rfi)(CPUHPPAState *env) env->iaoq_b = env->cr_back[1]; env->iasq_f = (env->cr[CR_IIASQ] << 32) & ~(env->iaoq_f & mask); env->iasq_b = (env->cr_back[0] << 32) & ~(env->iaoq_b & mask); + + if (qemu_loglevel_mask(CPU_LOG_INT)) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + CPUState *cs = env_cpu(env); + + fprintf(logfile, "RFI: cpu %d\n", cs->cpu_index); + hppa_cpu_dump_state(cs, logfile, 0); + qemu_log_unlock(logfile); + } + } } static void getshadowregs(CPUHPPAState *env) From 4985d876ee681368e86163637c1cbd3451d4c514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 14 May 2024 18:42:43 +0100 Subject: [PATCH 0707/2884] tests/tcg: don't append QEMU_OPTS for armv6m-undef test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't want to build on the default machine setup here but define a custom one for the microbit. Reviewed-by: Pierrick Bouvier Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-2-alex.bennee@linaro.org> --- tests/tcg/arm/Makefile.softmmu-target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target index 4c9264057f..39e01ce49d 100644 --- a/tests/tcg/arm/Makefile.softmmu-target +++ b/tests/tcg/arm/Makefile.softmmu-target @@ -16,7 +16,7 @@ test-armv6m-undef: test-armv6m-undef.S $< -o $@ -nostdlib -N -static \ -T $(ARM_SRC)/$@.ld -run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel +run-test-armv6m-undef: QEMU_OPTS=-semihosting-config enable=on,target=native,chardev=output -M microbit -kernel ARM_TESTS+=test-armv6m-undef From b51ddd937f11f76614d4b36d14d8778df242661c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 14 May 2024 18:42:44 +0100 Subject: [PATCH 0708/2884] scripts/update-linux-header.sh: be more src tree friendly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running "install_headers" in the Linux source tree is fairly unfriendly as out-of-tree builds will start complaining about the kernel source being non-pristine. As we have a temporary directory for the install we should also do the build step here. So now we have: $tmpdir/ $blddir/ $hdrdir/ Reviewed-by: Pierrick Bouvier Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-3-alex.bennee@linaro.org> --- scripts/update-linux-headers.sh | 80 +++++++++++++++++---------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 36f3e91fe4..8963c39189 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -27,6 +27,8 @@ # types like "__u64". This work is done in the cp_portable function. tmpdir=$(mktemp -d) +hdrdir="$tmpdir/headers" +blddir="$tmpdir/build" linux="$1" output="$2" @@ -110,56 +112,56 @@ for arch in $ARCHLIST; do arch_var=ARCH fi - make -C "$linux" INSTALL_HDR_PATH="$tmpdir" $arch_var=$arch headers_install + make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" for header in kvm.h unistd.h bitsperlong.h mman.h; do - cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" + cp "$hdrdir/include/asm/$header" "$output/linux-headers/asm-$arch" done if [ $arch = mips ]; then - cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" - cp "$tmpdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/" - cp "$tmpdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/" - cp "$tmpdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/" fi if [ $arch = powerpc ]; then - cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/" - cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/" fi rm -rf "$output/include/standard-headers/asm-$arch" mkdir -p "$output/include/standard-headers/asm-$arch" if [ $arch = s390 ]; then - cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" - cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" - cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" + cp_portable "$hdrdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" fi if [ $arch = arm ]; then - cp "$tmpdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" - cp "$tmpdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/" - cp "$tmpdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" + cp "$hdrdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" + cp "$hdrdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/" + cp "$hdrdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" fi if [ $arch = arm64 ]; then - cp "$tmpdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/" + cp "$hdrdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/" fi if [ $arch = x86 ]; then - cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" - cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" - cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" - cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" + cp "$hdrdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + cp_portable "$hdrdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" # Remove everything except the macros from bootparam.h avoiding the # unnecessary import of several video/ist/etc headers sed -e '/__ASSEMBLY__/,/__ASSEMBLY__/d' \ - "$tmpdir/include/asm/bootparam.h" > "$tmpdir/bootparam.h" - cp_portable "$tmpdir/bootparam.h" \ + "$hdrdir/include/asm/bootparam.h" > "$hdrdir/bootparam.h" + cp_portable "$hdrdir/bootparam.h" \ "$output/include/standard-headers/asm-$arch" - cp_portable "$tmpdir/include/asm/setup_data.h" \ + cp_portable "$hdrdir/include/asm/setup_data.h" \ "$output/standard-headers/asm-x86" fi if [ $arch = riscv ]; then - cp "$tmpdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" + cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" fi done arch= @@ -169,13 +171,13 @@ mkdir -p "$output/linux-headers/linux" for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \ vduse.h iommufd.h bits.h; do - cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" + cp "$hdrdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" mkdir -p "$output/linux-headers/asm-generic" for header in unistd.h bitsperlong.h mman-common.h mman.h hugetlb_encode.h; do - cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" + cp "$hdrdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" done if [ -L "$linux/source" ]; then @@ -210,23 +212,23 @@ EOF rm -rf "$output/include/standard-headers/linux" mkdir -p "$output/include/standard-headers/linux" -for i in "$tmpdir"/include/linux/*virtio*.h \ - "$tmpdir/include/linux/qemu_fw_cfg.h" \ - "$tmpdir/include/linux/fuse.h" \ - "$tmpdir/include/linux/input.h" \ - "$tmpdir/include/linux/input-event-codes.h" \ - "$tmpdir/include/linux/udmabuf.h" \ - "$tmpdir/include/linux/pci_regs.h" \ - "$tmpdir/include/linux/ethtool.h" \ - "$tmpdir/include/linux/const.h" \ - "$tmpdir/include/linux/kernel.h" \ - "$tmpdir/include/linux/vhost_types.h" \ - "$tmpdir/include/linux/sysinfo.h" \ - "$tmpdir/include/misc/pvpanic.h"; do +for i in "$hdrdir"/include/linux/*virtio*.h \ + "$hdrdir/include/linux/qemu_fw_cfg.h" \ + "$hdrdir/include/linux/fuse.h" \ + "$hdrdir/include/linux/input.h" \ + "$hdrdir/include/linux/input-event-codes.h" \ + "$hdrdir/include/linux/udmabuf.h" \ + "$hdrdir/include/linux/pci_regs.h" \ + "$hdrdir/include/linux/ethtool.h" \ + "$hdrdir/include/linux/const.h" \ + "$hdrdir/include/linux/kernel.h" \ + "$hdrdir/include/linux/vhost_types.h" \ + "$hdrdir/include/linux/sysinfo.h" \ + "$hdrdir/include/misc/pvpanic.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done mkdir -p "$output/include/standard-headers/drm" -cp_portable "$tmpdir/include/drm/drm_fourcc.h" \ +cp_portable "$hdrdir/include/drm/drm_fourcc.h" \ "$output/include/standard-headers/drm" cat <$output/include/standard-headers/linux/types.h From 21032784e56e9687f8c9d4abb75db4b15629c9b1 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:45 +0100 Subject: [PATCH 0709/2884] plugins: prepare introduction of new inline ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, only add_u64 was available, and all functions assumed this or were named uniquely. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-4-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 6 +++--- include/qemu/plugin.h | 2 +- plugins/core.c | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 54b08ffc9e..f1becf18ac 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -113,7 +113,7 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb) tcg_temp_free_i32(cpu_index); } -static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb) +static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb) { GArray *arr = cb->inline_insn.entry.score->data; size_t offset = cb->inline_insn.entry.offset; @@ -158,8 +158,8 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb) case PLUGIN_CB_REGULAR: gen_udata_cb(cb); break; - case PLUGIN_CB_INLINE: - gen_inline_cb(cb); + case PLUGIN_CB_INLINE_ADD_U64: + gen_inline_add_u64_cb(cb); break; default: g_assert_not_reached(); diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 7fda6ef126..7d1d3bd283 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -69,7 +69,7 @@ union qemu_plugin_cb_sig { enum plugin_dyn_cb_type { PLUGIN_CB_REGULAR, PLUGIN_CB_MEM_REGULAR, - PLUGIN_CB_INLINE, + PLUGIN_CB_INLINE_ADD_U64, }; /* diff --git a/plugins/core.c b/plugins/core.c index 1e58a57bf1..59771eda8f 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -316,6 +316,16 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1); } +static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op) +{ + switch (op) { + case QEMU_PLUGIN_INLINE_ADD_U64: + return PLUGIN_CB_INLINE_ADD_U64; + default: + g_assert_not_reached(); + } +} + void plugin_register_inline_op_on_entry(GArray **arr, enum qemu_plugin_mem_rw rw, enum qemu_plugin_op op, @@ -326,7 +336,7 @@ void plugin_register_inline_op_on_entry(GArray **arr, dyn_cb = plugin_get_dyn_cb(arr); dyn_cb->userp = NULL; - dyn_cb->type = PLUGIN_CB_INLINE; + dyn_cb->type = op_to_cb_type(op); dyn_cb->rw = rw; dyn_cb->inline_insn.entry = entry; dyn_cb->inline_insn.op = op; @@ -551,7 +561,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), vaddr, cb->userp); break; - case PLUGIN_CB_INLINE: + case PLUGIN_CB_INLINE_ADD_U64: exec_inline_op(cb, cpu->cpu_index); break; default: From 299c82b8ae31ef446cad9ef5b04efd49e1d02528 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:46 +0100 Subject: [PATCH 0710/2884] plugins: extract generate ptr for qemu_plugin_u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plugin operations can access a scoreboard. This function factorizes code generation for accessing entry associated to a given vcpu. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-3-pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-5-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index f1becf18ac..f2edeac8f1 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -113,24 +113,33 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb) tcg_temp_free_i32(cpu_index); } -static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb) +static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry) { - GArray *arr = cb->inline_insn.entry.score->data; - size_t offset = cb->inline_insn.entry.offset; - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - TCGv_i64 val = tcg_temp_ebb_new_i64(); TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); + GArray *arr = entry.score->data; + char *base_ptr = arr->data + entry.offset; + size_t entry_size = g_array_get_element_size(arr); + + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr)); + tcg_gen_muli_i32(cpu_index, cpu_index, entry_size); tcg_gen_ext_i32_ptr(ptr, cpu_index); tcg_temp_free_i32(cpu_index); + tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr); - tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data); - tcg_gen_ld_i64(val, ptr, offset); + return ptr; +} + +static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb) +{ + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + + tcg_gen_ld_i64(val, ptr, 0); tcg_gen_addi_i64(val, val, cb->inline_insn.imm); - tcg_gen_st_i64(val, ptr, offset); + tcg_gen_st_i64(val, ptr, 0); tcg_temp_free_i64(val); tcg_temp_free_ptr(ptr); From 36a1d8e7102c22e7def1d4146a6b824ec98b3a89 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:47 +0100 Subject: [PATCH 0711/2884] plugins: add new inline op STORE_U64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new operation can store an immediate u64 value to a given scoreboard. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-4-pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-6-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 13 +++++++++++++ include/qemu/plugin.h | 1 + include/qemu/qemu-plugin.h | 4 ++-- plugins/core.c | 6 ++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index f2edeac8f1..2cd0e36187 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -145,6 +145,16 @@ static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb) tcg_temp_free_ptr(ptr); } +static void gen_inline_store_u64_cb(struct qemu_plugin_dyn_cb *cb) +{ + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry); + TCGv_i64 val = tcg_constant_i64(cb->inline_insn.imm); + + tcg_gen_st_i64(val, ptr, 0); + + tcg_temp_free_ptr(ptr); +} + static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { @@ -170,6 +180,9 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb) case PLUGIN_CB_INLINE_ADD_U64: gen_inline_add_u64_cb(cb); break; + case PLUGIN_CB_INLINE_STORE_U64: + gen_inline_store_u64_cb(cb); + break; default: g_assert_not_reached(); } diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 7d1d3bd283..aaa4b830fb 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -70,6 +70,7 @@ enum plugin_dyn_cb_type { PLUGIN_CB_REGULAR, PLUGIN_CB_MEM_REGULAR, PLUGIN_CB_INLINE_ADD_U64, + PLUGIN_CB_INLINE_STORE_U64, }; /* diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 5f36c2d1ac..29242d4fb5 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -305,12 +305,12 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, * enum qemu_plugin_op - describes an inline op * * @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t - * - * Note: currently only a single inline op is supported. + * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t */ enum qemu_plugin_op { QEMU_PLUGIN_INLINE_ADD_U64, + QEMU_PLUGIN_INLINE_STORE_U64, }; /** diff --git a/plugins/core.c b/plugins/core.c index 59771eda8f..848d482fc4 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -321,6 +321,8 @@ static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op) switch (op) { case QEMU_PLUGIN_INLINE_ADD_U64: return PLUGIN_CB_INLINE_ADD_U64; + case QEMU_PLUGIN_INLINE_STORE_U64: + return PLUGIN_CB_INLINE_STORE_U64; default: g_assert_not_reached(); } @@ -535,6 +537,9 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) case QEMU_PLUGIN_INLINE_ADD_U64: *val += cb->inline_insn.imm; break; + case QEMU_PLUGIN_INLINE_STORE_U64: + *val = cb->inline_insn.imm; + break; default: g_assert_not_reached(); } @@ -562,6 +567,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, vaddr, cb->userp); break; case PLUGIN_CB_INLINE_ADD_U64: + case PLUGIN_CB_INLINE_STORE_U64: exec_inline_op(cb, cpu->cpu_index); break; default: From a1c9bf2514d82b757288ee61584e667ddcb19a4f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:48 +0100 Subject: [PATCH 0712/2884] tests/plugin/inline: add test for STORE_U64 inline op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-5-pierrick.bouvier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-7-alex.bennee@linaro.org> --- tests/plugin/inline.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c index 0163e9b51c..103c3a22f6 100644 --- a/tests/plugin/inline.c +++ b/tests/plugin/inline.c @@ -22,6 +22,12 @@ typedef struct { uint64_t count_mem_inline; } CPUCount; +typedef struct { + uint64_t data_insn; + uint64_t data_tb; + uint64_t data_mem; +} CPUData; + static struct qemu_plugin_scoreboard *counts; static qemu_plugin_u64 count_tb; static qemu_plugin_u64 count_tb_inline; @@ -29,6 +35,10 @@ static qemu_plugin_u64 count_insn; static qemu_plugin_u64 count_insn_inline; static qemu_plugin_u64 count_mem; static qemu_plugin_u64 count_mem_inline; +static struct qemu_plugin_scoreboard *data; +static qemu_plugin_u64 data_insn; +static qemu_plugin_u64 data_tb; +static qemu_plugin_u64 data_mem; static uint64_t global_count_tb; static uint64_t global_count_insn; @@ -109,11 +119,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) stats_mem(); qemu_plugin_scoreboard_free(counts); + qemu_plugin_scoreboard_free(data); } static void vcpu_tb_exec(unsigned int cpu_index, void *udata) { qemu_plugin_u64_add(count_tb, cpu_index, 1); + g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata); g_mutex_lock(&tb_lock); max_cpu_index = MAX(max_cpu_index, cpu_index); global_count_tb++; @@ -123,6 +135,7 @@ static void vcpu_tb_exec(unsigned int cpu_index, void *udata) static void vcpu_insn_exec(unsigned int cpu_index, void *udata) { qemu_plugin_u64_add(count_insn, cpu_index, 1); + g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata); g_mutex_lock(&insn_lock); global_count_insn++; g_mutex_unlock(&insn_lock); @@ -131,9 +144,10 @@ static void vcpu_insn_exec(unsigned int cpu_index, void *udata) static void vcpu_mem_access(unsigned int cpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, - void *userdata) + void *udata) { qemu_plugin_u64_add(count_mem, cpu_index, 1); + g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata); g_mutex_lock(&mem_lock); global_count_mem++; g_mutex_unlock(&mem_lock); @@ -141,20 +155,34 @@ static void vcpu_mem_access(unsigned int cpu_index, static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { + void *tb_store = tb; + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store); qemu_plugin_register_vcpu_tb_exec_cb( - tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0); + tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store); qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1); for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx); + void *insn_store = insn; + void *mem_store = (char *)insn_store + 0xff; + + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn, + (uintptr_t) insn_store); qemu_plugin_register_vcpu_insn_exec_cb( - insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0); + insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store); qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1); + + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, QEMU_PLUGIN_MEM_RW, + QEMU_PLUGIN_INLINE_STORE_U64, + data_mem, (uintptr_t) mem_store); qemu_plugin_register_vcpu_mem_cb(insn, &vcpu_mem_access, QEMU_PLUGIN_CB_NO_REGS, - QEMU_PLUGIN_MEM_RW, 0); + QEMU_PLUGIN_MEM_RW, mem_store); qemu_plugin_register_vcpu_mem_inline_per_vcpu( insn, QEMU_PLUGIN_MEM_RW, QEMU_PLUGIN_INLINE_ADD_U64, @@ -179,6 +207,11 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, counts, CPUCount, count_insn_inline); count_mem_inline = qemu_plugin_scoreboard_u64_in_struct( counts, CPUCount, count_mem_inline); + data = qemu_plugin_scoreboard_new(sizeof(CPUData)); + data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn); + data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb); + data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); From 7de77d37880d7267a491cb32a1b2232017d1e545 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:49 +0100 Subject: [PATCH 0713/2884] plugins: conditional callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend plugins API to support callback called with a given criteria (evaluated inline). Added functions: - qemu_plugin_register_vcpu_tb_exec_cond_cb - qemu_plugin_register_vcpu_insn_exec_cond_cb They expect as parameter a condition, a qemu_plugin_u64_t (op1) and an immediate (op2). Callback is called if op1 |cond| op2 is true. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-6-pierrick.bouvier@linaro.org> Reviewed-by: Michael S. Tsirkin [AJB: fix re-base conflict with tb_is_mem_only()] Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-8-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 48 +++++++++++++++++++++++ include/qemu/plugin.h | 8 ++++ include/qemu/qemu-plugin.h | 76 ++++++++++++++++++++++++++++++++++++ plugins/api.c | 39 ++++++++++++++++++ plugins/core.c | 32 +++++++++++++++ plugins/plugin.h | 8 ++++ plugins/qemu-plugins.symbols | 2 + 7 files changed, 213 insertions(+) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 2cd0e36187..14b6603871 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -132,6 +132,51 @@ static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry) return ptr; } +static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond cond) +{ + switch (cond) { + case QEMU_PLUGIN_COND_EQ: + return TCG_COND_EQ; + case QEMU_PLUGIN_COND_NE: + return TCG_COND_NE; + case QEMU_PLUGIN_COND_LT: + return TCG_COND_LTU; + case QEMU_PLUGIN_COND_LE: + return TCG_COND_LEU; + case QEMU_PLUGIN_COND_GT: + return TCG_COND_GTU; + case QEMU_PLUGIN_COND_GE: + return TCG_COND_GEU; + default: + /* ALWAYS and NEVER conditions should never reach */ + g_assert_not_reached(); + } +} + +static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb *cb) +{ + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->cond.entry); + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + TCGLabel *after_cb = gen_new_label(); + + /* Condition should be negated, as calling the cb is the "else" path */ + TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond.cond)); + + tcg_gen_ld_i64(val, ptr, 0); + tcg_gen_brcondi_i64(cond, val, cb->cond.imm, after_cb); + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + tcg_gen_call2(cb->cond.f.vcpu_udata, cb->cond.info, NULL, + tcgv_i32_temp(cpu_index), + tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + gen_set_label(after_cb); + + tcg_temp_free_i64(val); + tcg_temp_free_i32(cpu_index); + tcg_temp_free_ptr(ptr); +} + static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb) { TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry); @@ -177,6 +222,9 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb) case PLUGIN_CB_REGULAR: gen_udata_cb(cb); break; + case PLUGIN_CB_COND: + gen_udata_cond_cb(cb); + break; case PLUGIN_CB_INLINE_ADD_U64: gen_inline_add_u64_cb(cb); break; diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index aaa4b830fb..2b126e1884 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -68,6 +68,7 @@ union qemu_plugin_cb_sig { enum plugin_dyn_cb_type { PLUGIN_CB_REGULAR, + PLUGIN_CB_COND, PLUGIN_CB_MEM_REGULAR, PLUGIN_CB_INLINE_ADD_U64, PLUGIN_CB_INLINE_STORE_U64, @@ -89,6 +90,13 @@ struct qemu_plugin_dyn_cb { union qemu_plugin_cb_sig f; TCGHelperInfo *info; } regular; + struct { + union qemu_plugin_cb_sig f; + TCGHelperInfo *info; + qemu_plugin_u64 entry; + enum qemu_plugin_cond cond; + uint64_t imm; + } cond; struct { qemu_plugin_u64 entry; enum qemu_plugin_op op; diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 29242d4fb5..95703d8fec 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -262,6 +262,29 @@ enum qemu_plugin_mem_rw { QEMU_PLUGIN_MEM_RW, }; +/** + * enum qemu_plugin_cond - condition to enable callback + * + * @QEMU_PLUGIN_COND_NEVER: false + * @QEMU_PLUGIN_COND_ALWAYS: true + * @QEMU_PLUGIN_COND_EQ: is equal? + * @QEMU_PLUGIN_COND_NE: is not equal? + * @QEMU_PLUGIN_COND_LT: is less than? + * @QEMU_PLUGIN_COND_LE: is less than or equal? + * @QEMU_PLUGIN_COND_GT: is greater than? + * @QEMU_PLUGIN_COND_GE: is greater than or equal? + */ +enum qemu_plugin_cond { + QEMU_PLUGIN_COND_NEVER, + QEMU_PLUGIN_COND_ALWAYS, + QEMU_PLUGIN_COND_EQ, + QEMU_PLUGIN_COND_NE, + QEMU_PLUGIN_COND_LT, + QEMU_PLUGIN_COND_LE, + QEMU_PLUGIN_COND_GT, + QEMU_PLUGIN_COND_GE, +}; + /** * typedef qemu_plugin_vcpu_tb_trans_cb_t - translation callback * @id: unique plugin id @@ -301,6 +324,32 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, enum qemu_plugin_cb_flags flags, void *userdata); +/** + * qemu_plugin_register_vcpu_tb_exec_cond_cb() - register conditional callback + * @tb: the opaque qemu_plugin_tb handle for the translation + * @cb: callback function + * @cond: condition to enable callback + * @entry: first operand for condition + * @imm: second operand for condition + * @flags: does the plugin read or write the CPU's registers? + * @userdata: any plugin data to pass to the @cb? + * + * The @cb function is called when a translated unit executes if + * entry @cond imm is true. + * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and + * this function is equivalent to qemu_plugin_register_vcpu_tb_exec_cb. + * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and + * callback is never installed. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *userdata); + /** * enum qemu_plugin_op - describes an inline op * @@ -344,6 +393,33 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_cb_flags flags, void *userdata); +/** + * qemu_plugin_register_vcpu_insn_exec_cond_cb() - conditional insn execution cb + * @insn: the opaque qemu_plugin_insn handle for an instruction + * @cb: callback function + * @flags: does the plugin read or write the CPU's registers? + * @cond: condition to enable callback + * @entry: first operand for condition + * @imm: second operand for condition + * @userdata: any plugin data to pass to the @cb? + * + * The @cb function is called when an instruction executes if + * entry @cond imm is true. + * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and + * this function is equivalent to qemu_plugin_register_vcpu_insn_exec_cb. + * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and + * callback is never installed. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_insn_exec_cond_cb( + struct qemu_plugin_insn *insn, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *userdata); + /** * qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu() - insn exec inline op * @insn: the opaque qemu_plugin_insn handle for an instruction diff --git a/plugins/api.c b/plugins/api.c index b04c5e1928..5a0a7f8c71 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -102,6 +102,25 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, } } +void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata) +{ + if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) { + return; + } + if (cond == QEMU_PLUGIN_COND_ALWAYS) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, cb, flags, udata); + return; + } + plugin_register_dyn_cond_cb__udata(&tb->cbs, cb, flags, + cond, entry, imm, udata); +} + void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( struct qemu_plugin_tb *tb, enum qemu_plugin_op op, @@ -123,6 +142,26 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, } } +void qemu_plugin_register_vcpu_insn_exec_cond_cb( + struct qemu_plugin_insn *insn, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata) +{ + if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) { + return; + } + if (cond == QEMU_PLUGIN_COND_ALWAYS) { + qemu_plugin_register_vcpu_insn_exec_cb(insn, cb, flags, udata); + return; + } + plugin_register_dyn_cond_cb__udata(&insn->insn_cbs, cb, flags, + cond, entry, imm, udata); +} + void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( struct qemu_plugin_insn *insn, enum qemu_plugin_op op, diff --git a/plugins/core.c b/plugins/core.c index 848d482fc4..332474a5bc 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -371,6 +371,38 @@ void plugin_register_dyn_cb__udata(GArray **arr, dyn_cb->regular.info = &info[flags]; } +void plugin_register_dyn_cond_cb__udata(GArray **arr, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata) +{ + static TCGHelperInfo info[3] = { + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + /* + * Match qemu_plugin_vcpu_udata_cb_t: + * void (*)(uint32_t, void *) + */ + [0 ... 2].typemask = (dh_typemask(void, 0) | + dh_typemask(i32, 1) | + dh_typemask(ptr, 2)) + }; + + struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); + dyn_cb->userp = udata; + dyn_cb->type = PLUGIN_CB_COND; + dyn_cb->cond.f.vcpu_udata = cb; + dyn_cb->cond.cond = cond; + dyn_cb->cond.entry = entry; + dyn_cb->cond.imm = imm; + + assert((unsigned)flags < ARRAY_SIZE(info)); + dyn_cb->cond.info = &info[flags]; +} + void plugin_register_vcpu_mem_cb(GArray **arr, void *cb, enum qemu_plugin_cb_flags flags, diff --git a/plugins/plugin.h b/plugins/plugin.h index 7c34f23cfc..7d4b4e21f7 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -93,6 +93,14 @@ plugin_register_dyn_cb__udata(GArray **arr, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, void *udata); +void +plugin_register_dyn_cond_cb__udata(GArray **arr, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata); void plugin_register_vcpu_mem_cb(GArray **arr, void *cb, diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index a9fac056c7..aa0a77a319 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -27,6 +27,7 @@ qemu_plugin_register_vcpu_idle_cb; qemu_plugin_register_vcpu_init_cb; qemu_plugin_register_vcpu_insn_exec_cb; + qemu_plugin_register_vcpu_insn_exec_cond_cb; qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu; qemu_plugin_register_vcpu_mem_cb; qemu_plugin_register_vcpu_mem_inline_per_vcpu; @@ -34,6 +35,7 @@ qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_ret_cb; qemu_plugin_register_vcpu_tb_exec_cb; + qemu_plugin_register_vcpu_tb_exec_cond_cb; qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu; qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_reset; From 544595e73007c824b7435b52519cc578586783a6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:50 +0100 Subject: [PATCH 0714/2884] tests/plugin/inline: add test for conditional callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Count number of tb and insn executed using a conditional callback. We ensure the callback has been called expected number of time (per vcpu). Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-7-pierrick.bouvier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-9-alex.bennee@linaro.org> --- tests/plugin/inline.c | 89 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c index 103c3a22f6..cd63827b7d 100644 --- a/tests/plugin/inline.c +++ b/tests/plugin/inline.c @@ -20,8 +20,14 @@ typedef struct { uint64_t count_insn_inline; uint64_t count_mem; uint64_t count_mem_inline; + uint64_t tb_cond_num_trigger; + uint64_t tb_cond_track_count; + uint64_t insn_cond_num_trigger; + uint64_t insn_cond_track_count; } CPUCount; +static const uint64_t cond_trigger_limit = 100; + typedef struct { uint64_t data_insn; uint64_t data_tb; @@ -35,6 +41,10 @@ static qemu_plugin_u64 count_insn; static qemu_plugin_u64 count_insn_inline; static qemu_plugin_u64 count_mem; static qemu_plugin_u64 count_mem_inline; +static qemu_plugin_u64 tb_cond_num_trigger; +static qemu_plugin_u64 tb_cond_track_count; +static qemu_plugin_u64 insn_cond_num_trigger; +static qemu_plugin_u64 insn_cond_track_count; static struct qemu_plugin_scoreboard *data; static qemu_plugin_u64 data_insn; static qemu_plugin_u64 data_tb; @@ -56,12 +66,19 @@ static void stats_insn(void) const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn); const uint64_t inl_per_vcpu = qemu_plugin_u64_sum(count_insn_inline); + const uint64_t cond_num_trigger = + qemu_plugin_u64_sum(insn_cond_num_trigger); + const uint64_t cond_track_left = qemu_plugin_u64_sum(insn_cond_track_count); + const uint64_t conditional = + cond_num_trigger * cond_trigger_limit + cond_track_left; printf("insn: %" PRIu64 "\n", expected); printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu); printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + printf("insn: %" PRIu64 " (cond cb)\n", conditional); g_assert(expected > 0); g_assert(per_vcpu == expected); g_assert(inl_per_vcpu == expected); + g_assert(conditional == expected); } static void stats_tb(void) @@ -70,12 +87,18 @@ static void stats_tb(void) const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb); const uint64_t inl_per_vcpu = qemu_plugin_u64_sum(count_tb_inline); + const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger); + const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count); + const uint64_t conditional = + cond_num_trigger * cond_trigger_limit + cond_track_left; printf("tb: %" PRIu64 "\n", expected); printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu); printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + printf("tb: %" PRIu64 " (conditional cb)\n", conditional); g_assert(expected > 0); g_assert(per_vcpu == expected); g_assert(inl_per_vcpu == expected); + g_assert(conditional == expected); } static void stats_mem(void) @@ -104,14 +127,35 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i); const uint64_t mem = qemu_plugin_u64_get(count_mem, i); const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i); - printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | " - "insn (%" PRIu64 ", %" PRIu64 ") | " + const uint64_t tb_cond_trigger = + qemu_plugin_u64_get(tb_cond_num_trigger, i); + const uint64_t tb_cond_left = + qemu_plugin_u64_get(tb_cond_track_count, i); + const uint64_t insn_cond_trigger = + qemu_plugin_u64_get(insn_cond_num_trigger, i); + const uint64_t insn_cond_left = + qemu_plugin_u64_get(insn_cond_track_count, i); + printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 + ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 + ") | " + "insn (%" PRIu64 ", %" PRIu64 + ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 + ") | " "mem (%" PRIu64 ", %" PRIu64 ")" "\n", - i, tb, tb_inline, insn, insn_inline, mem, mem_inline); + i, + tb, tb_inline, + tb_cond_trigger, cond_trigger_limit, tb_cond_left, + insn, insn_inline, + insn_cond_trigger, cond_trigger_limit, insn_cond_left, + mem, mem_inline); g_assert(tb == tb_inline); g_assert(insn == insn_inline); g_assert(mem == mem_inline); + g_assert(tb_cond_trigger == tb / cond_trigger_limit); + g_assert(tb_cond_left == tb % cond_trigger_limit); + g_assert(insn_cond_trigger == insn / cond_trigger_limit); + g_assert(insn_cond_left == insn % cond_trigger_limit); } stats_tb(); @@ -132,6 +176,24 @@ static void vcpu_tb_exec(unsigned int cpu_index, void *udata) g_mutex_unlock(&tb_lock); } +static void vcpu_tb_cond_exec(unsigned int cpu_index, void *udata) +{ + g_assert(qemu_plugin_u64_get(tb_cond_track_count, cpu_index) == + cond_trigger_limit); + g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata); + qemu_plugin_u64_set(tb_cond_track_count, cpu_index, 0); + qemu_plugin_u64_add(tb_cond_num_trigger, cpu_index, 1); +} + +static void vcpu_insn_cond_exec(unsigned int cpu_index, void *udata) +{ + g_assert(qemu_plugin_u64_get(insn_cond_track_count, cpu_index) == + cond_trigger_limit); + g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata); + qemu_plugin_u64_set(insn_cond_track_count, cpu_index, 0); + qemu_plugin_u64_add(insn_cond_num_trigger, cpu_index, 1); +} + static void vcpu_insn_exec(unsigned int cpu_index, void *udata) { qemu_plugin_u64_add(count_insn, cpu_index, 1); @@ -163,6 +225,12 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, tb_cond_track_count, 1); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, vcpu_tb_cond_exec, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, tb_cond_track_count, cond_trigger_limit, tb_store); + for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx); void *insn_store = insn; @@ -176,6 +244,13 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_cond_track_count, 1); + qemu_plugin_register_vcpu_insn_exec_cond_cb( + insn, vcpu_insn_cond_exec, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, insn_cond_track_count, cond_trigger_limit, + insn_store); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( insn, QEMU_PLUGIN_MEM_RW, QEMU_PLUGIN_INLINE_STORE_U64, @@ -207,6 +282,14 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, counts, CPUCount, count_insn_inline); count_mem_inline = qemu_plugin_scoreboard_u64_in_struct( counts, CPUCount, count_mem_inline); + tb_cond_num_trigger = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, tb_cond_num_trigger); + tb_cond_track_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, tb_cond_track_count); + insn_cond_num_trigger = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, insn_cond_num_trigger); + insn_cond_track_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, insn_cond_track_count); data = qemu_plugin_scoreboard_new(sizeof(CPUData)); data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn); data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb); From f86fd4d8721073fa834845c5b76bf1f829b5f9b5 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:51 +0100 Subject: [PATCH 0715/2884] plugins: distinct types for callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To prevent errors when writing new types of callbacks or inline operations, we split callbacks data to distinct types. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-8-pierrick.bouvier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-10-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 60 ++++++++++++++++++--------------- include/qemu/plugin.h | 46 ++++++++++++++----------- plugins/core.c | 76 ++++++++++++++++++++++-------------------- plugins/plugin.h | 2 +- 4 files changed, 99 insertions(+), 85 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 14b6603871..81b33a5391 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -101,13 +101,13 @@ static void gen_disable_mem_helper(void) offsetof(ArchCPU, env)); } -static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb) +static void gen_udata_cb(struct qemu_plugin_regular_cb *cb) { TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL, + tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); tcg_temp_free_i32(cpu_index); @@ -153,21 +153,21 @@ static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond cond) } } -static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb *cb) +static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb) { - TCGv_ptr ptr = gen_plugin_u64_ptr(cb->cond.entry); + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); TCGv_i64 val = tcg_temp_ebb_new_i64(); TCGLabel *after_cb = gen_new_label(); /* Condition should be negated, as calling the cb is the "else" path */ - TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond.cond)); + TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond)); tcg_gen_ld_i64(val, ptr, 0); - tcg_gen_brcondi_i64(cond, val, cb->cond.imm, after_cb); + tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb); tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - tcg_gen_call2(cb->cond.f.vcpu_udata, cb->cond.info, NULL, + tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); gen_set_label(after_cb); @@ -177,37 +177,37 @@ static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb *cb) tcg_temp_free_ptr(ptr); } -static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb) +static void gen_inline_add_u64_cb(struct qemu_plugin_inline_cb *cb) { - TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry); + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); TCGv_i64 val = tcg_temp_ebb_new_i64(); tcg_gen_ld_i64(val, ptr, 0); - tcg_gen_addi_i64(val, val, cb->inline_insn.imm); + tcg_gen_addi_i64(val, val, cb->imm); tcg_gen_st_i64(val, ptr, 0); tcg_temp_free_i64(val); tcg_temp_free_ptr(ptr); } -static void gen_inline_store_u64_cb(struct qemu_plugin_dyn_cb *cb) +static void gen_inline_store_u64_cb(struct qemu_plugin_inline_cb *cb) { - TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry); - TCGv_i64 val = tcg_constant_i64(cb->inline_insn.imm); + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_constant_i64(cb->imm); tcg_gen_st_i64(val, ptr, 0); tcg_temp_free_ptr(ptr); } -static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, +static void gen_mem_cb(struct qemu_plugin_regular_cb *cb, qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - tcg_gen_call4(cb->regular.f.vcpu_mem, cb->regular.info, NULL, + tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_i32_temp(tcg_constant_i32(meminfo)), tcgv_i64_temp(addr), @@ -220,16 +220,16 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb) { switch (cb->type) { case PLUGIN_CB_REGULAR: - gen_udata_cb(cb); + gen_udata_cb(&cb->regular); break; case PLUGIN_CB_COND: - gen_udata_cond_cb(cb); + gen_udata_cond_cb(&cb->cond); break; case PLUGIN_CB_INLINE_ADD_U64: - gen_inline_add_u64_cb(cb); + gen_inline_add_u64_cb(&cb->inline_insn); break; case PLUGIN_CB_INLINE_STORE_U64: - gen_inline_store_u64_cb(cb); + gen_inline_store_u64_cb(&cb->inline_insn); break; default: g_assert_not_reached(); @@ -240,15 +240,21 @@ static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb, enum qemu_plugin_mem_rw rw, qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { - if (cb->rw & rw) { - switch (cb->type) { - case PLUGIN_CB_MEM_REGULAR: - gen_mem_cb(cb, meminfo, addr); - break; - default: - inject_cb(cb); - break; + switch (cb->type) { + case PLUGIN_CB_MEM_REGULAR: + if (rw && cb->regular.rw) { + gen_mem_cb(&cb->regular, meminfo, addr); } + break; + case PLUGIN_CB_INLINE_ADD_U64: + case PLUGIN_CB_INLINE_STORE_U64: + if (rw && cb->inline_insn.rw) { + inject_cb(cb); + } + break; + default: + g_assert_not_reached(); + break; } } diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 2b126e1884..0c9e4f981e 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -74,34 +74,40 @@ enum plugin_dyn_cb_type { PLUGIN_CB_INLINE_STORE_U64, }; +struct qemu_plugin_regular_cb { + union qemu_plugin_cb_sig f; + TCGHelperInfo *info; + void *userp; + enum qemu_plugin_mem_rw rw; +}; + +struct qemu_plugin_inline_cb { + qemu_plugin_u64 entry; + enum qemu_plugin_op op; + uint64_t imm; + enum qemu_plugin_mem_rw rw; +}; + +struct qemu_plugin_conditional_cb { + union qemu_plugin_cb_sig f; + TCGHelperInfo *info; + void *userp; + qemu_plugin_u64 entry; + enum qemu_plugin_cond cond; + uint64_t imm; +}; + /* * A dynamic callback has an insertion point that is determined at run-time. * Usually the insertion point is somewhere in the code cache; think for * instance of a callback to be called upon the execution of a particular TB. */ struct qemu_plugin_dyn_cb { - void *userp; enum plugin_dyn_cb_type type; - /* @rw applies to mem callbacks only (both regular and inline) */ - enum qemu_plugin_mem_rw rw; - /* fields specific to each dyn_cb type go here */ union { - struct { - union qemu_plugin_cb_sig f; - TCGHelperInfo *info; - } regular; - struct { - union qemu_plugin_cb_sig f; - TCGHelperInfo *info; - qemu_plugin_u64 entry; - enum qemu_plugin_cond cond; - uint64_t imm; - } cond; - struct { - qemu_plugin_u64 entry; - enum qemu_plugin_op op; - uint64_t imm; - } inline_insn; + struct qemu_plugin_regular_cb regular; + struct qemu_plugin_conditional_cb cond; + struct qemu_plugin_inline_cb inline_insn; }; }; diff --git a/plugins/core.c b/plugins/core.c index 332474a5bc..1c85edc5e5 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -336,13 +336,13 @@ void plugin_register_inline_op_on_entry(GArray **arr, { struct qemu_plugin_dyn_cb *dyn_cb; + struct qemu_plugin_inline_cb inline_cb = { .rw = rw, + .entry = entry, + .op = op, + .imm = imm }; dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = NULL; dyn_cb->type = op_to_cb_type(op); - dyn_cb->rw = rw; - dyn_cb->inline_insn.entry = entry; - dyn_cb->inline_insn.op = op; - dyn_cb->inline_insn.imm = imm; + dyn_cb->inline_insn = inline_cb; } void plugin_register_dyn_cb__udata(GArray **arr, @@ -361,14 +361,14 @@ void plugin_register_dyn_cb__udata(GArray **arr, dh_typemask(i32, 1) | dh_typemask(ptr, 2)) }; + assert((unsigned)flags < ARRAY_SIZE(info)); struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = udata; + struct qemu_plugin_regular_cb regular_cb = { .f.vcpu_udata = cb, + .userp = udata, + .info = &info[flags] }; dyn_cb->type = PLUGIN_CB_REGULAR; - dyn_cb->regular.f.vcpu_udata = cb; - - assert((unsigned)flags < ARRAY_SIZE(info)); - dyn_cb->regular.info = &info[flags]; + dyn_cb->regular = regular_cb; } void plugin_register_dyn_cond_cb__udata(GArray **arr, @@ -390,17 +390,17 @@ void plugin_register_dyn_cond_cb__udata(GArray **arr, dh_typemask(i32, 1) | dh_typemask(ptr, 2)) }; + assert((unsigned)flags < ARRAY_SIZE(info)); struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = udata; + struct qemu_plugin_conditional_cb cond_cb = { .userp = udata, + .f.vcpu_udata = cb, + .cond = cond, + .entry = entry, + .imm = imm, + .info = &info[flags] }; dyn_cb->type = PLUGIN_CB_COND; - dyn_cb->cond.f.vcpu_udata = cb; - dyn_cb->cond.cond = cond; - dyn_cb->cond.entry = entry; - dyn_cb->cond.imm = imm; - - assert((unsigned)flags < ARRAY_SIZE(info)); - dyn_cb->cond.info = &info[flags]; + dyn_cb->cond = cond_cb; } void plugin_register_vcpu_mem_cb(GArray **arr, @@ -432,15 +432,15 @@ void plugin_register_vcpu_mem_cb(GArray **arr, dh_typemask(i64, 3) | dh_typemask(ptr, 4)) }; + assert((unsigned)flags < ARRAY_SIZE(info)); struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = udata; + struct qemu_plugin_regular_cb regular_cb = { .userp = udata, + .rw = rw, + .f.vcpu_mem = cb, + .info = &info[flags] }; dyn_cb->type = PLUGIN_CB_MEM_REGULAR; - dyn_cb->rw = rw; - dyn_cb->regular.f.vcpu_mem = cb; - - assert((unsigned)flags < ARRAY_SIZE(info)); - dyn_cb->regular.info = &info[flags]; + dyn_cb->regular = regular_cb; } /* @@ -557,20 +557,20 @@ void qemu_plugin_flush_cb(void) plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH); } -void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) +void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index) { - char *ptr = cb->inline_insn.entry.score->data->data; + char *ptr = cb->entry.score->data->data; size_t elem_size = g_array_get_element_size( - cb->inline_insn.entry.score->data); - size_t offset = cb->inline_insn.entry.offset; + cb->entry.score->data); + size_t offset = cb->entry.offset; uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); - switch (cb->inline_insn.op) { + switch (cb->op) { case QEMU_PLUGIN_INLINE_ADD_U64: - *val += cb->inline_insn.imm; + *val += cb->imm; break; case QEMU_PLUGIN_INLINE_STORE_U64: - *val = cb->inline_insn.imm; + *val = cb->imm; break; default: g_assert_not_reached(); @@ -590,17 +590,19 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, struct qemu_plugin_dyn_cb *cb = &g_array_index(arr, struct qemu_plugin_dyn_cb, i); - if (!(rw & cb->rw)) { - break; - } switch (cb->type) { case PLUGIN_CB_MEM_REGULAR: - cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), - vaddr, cb->userp); + if (rw && cb->regular.rw) { + cb->regular.f.vcpu_mem(cpu->cpu_index, + make_plugin_meminfo(oi, rw), + vaddr, cb->regular.userp); + } break; case PLUGIN_CB_INLINE_ADD_U64: case PLUGIN_CB_INLINE_STORE_U64: - exec_inline_op(cb, cpu->cpu_index); + if (rw && cb->inline_insn.rw) { + exec_inline_op(&cb->inline_insn, cpu->cpu_index); + } break; default: g_assert_not_reached(); diff --git a/plugins/plugin.h b/plugins/plugin.h index 7d4b4e21f7..80d5daa917 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -108,7 +108,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr, enum qemu_plugin_mem_rw rw, void *udata); -void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index); +void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index); int plugin_num_vcpus(void); From b95b78dc1617c435f7031fe4a2dcca37f4f4a3ce Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:52 +0100 Subject: [PATCH 0716/2884] plugins: extract cpu_index generate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factorizes function to access current cpu index for a given vcpu. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-9-pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-11-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 81b33a5391..cc1634e7a6 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -101,12 +101,17 @@ static void gen_disable_mem_helper(void) offsetof(ArchCPU, env)); } -static void gen_udata_cb(struct qemu_plugin_regular_cb *cb) +static TCGv_i32 gen_cpu_index(void) { TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + return cpu_index; +} + +static void gen_udata_cb(struct qemu_plugin_regular_cb *cb) +{ + TCGv_i32 cpu_index = gen_cpu_index(); tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); @@ -121,9 +126,7 @@ static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry) char *base_ptr = arr->data + entry.offset; size_t entry_size = g_array_get_element_size(arr); - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + TCGv_i32 cpu_index = gen_cpu_index(); tcg_gen_muli_i32(cpu_index, cpu_index, entry_size); tcg_gen_ext_i32_ptr(ptr, cpu_index); tcg_temp_free_i32(cpu_index); @@ -156,7 +159,6 @@ static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond cond) static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb) { TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); TCGv_i64 val = tcg_temp_ebb_new_i64(); TCGLabel *after_cb = gen_new_label(); @@ -165,15 +167,14 @@ static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb) tcg_gen_ld_i64(val, ptr, 0); tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb); - tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + TCGv_i32 cpu_index = gen_cpu_index(); tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_temp_free_i32(cpu_index); gen_set_label(after_cb); tcg_temp_free_i64(val); - tcg_temp_free_i32(cpu_index); tcg_temp_free_ptr(ptr); } @@ -203,10 +204,7 @@ static void gen_inline_store_u64_cb(struct qemu_plugin_inline_cb *cb) static void gen_mem_cb(struct qemu_plugin_regular_cb *cb, qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - - tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + TCGv_i32 cpu_index = gen_cpu_index(); tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_i32_temp(tcg_constant_i32(meminfo)), From 09afe9677e6aeb7629eeeab5abccc17f67cb4875 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 14 May 2024 18:42:53 +0100 Subject: [PATCH 0717/2884] plugins: remove op from qemu_plugin_inline_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This field is not needed as the callback type already holds this information. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240502211522.346467-10-pierrick.bouvier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Alex Bennée Message-Id: <20240514174253.694591-12-alex.bennee@linaro.org> --- include/qemu/plugin.h | 1 - plugins/core.c | 13 +++++++------ plugins/plugin.h | 4 +++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 0c9e4f981e..bc5aef979e 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -83,7 +83,6 @@ struct qemu_plugin_regular_cb { struct qemu_plugin_inline_cb { qemu_plugin_u64 entry; - enum qemu_plugin_op op; uint64_t imm; enum qemu_plugin_mem_rw rw; }; diff --git a/plugins/core.c b/plugins/core.c index 1c85edc5e5..0726bc7f25 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -338,7 +338,6 @@ void plugin_register_inline_op_on_entry(GArray **arr, struct qemu_plugin_inline_cb inline_cb = { .rw = rw, .entry = entry, - .op = op, .imm = imm }; dyn_cb = plugin_get_dyn_cb(arr); dyn_cb->type = op_to_cb_type(op); @@ -557,7 +556,9 @@ void qemu_plugin_flush_cb(void) plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH); } -void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index) +void exec_inline_op(enum plugin_dyn_cb_type type, + struct qemu_plugin_inline_cb *cb, + int cpu_index) { char *ptr = cb->entry.score->data->data; size_t elem_size = g_array_get_element_size( @@ -565,11 +566,11 @@ void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index) size_t offset = cb->entry.offset; uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); - switch (cb->op) { - case QEMU_PLUGIN_INLINE_ADD_U64: + switch (type) { + case PLUGIN_CB_INLINE_ADD_U64: *val += cb->imm; break; - case QEMU_PLUGIN_INLINE_STORE_U64: + case PLUGIN_CB_INLINE_STORE_U64: *val = cb->imm; break; default: @@ -601,7 +602,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, case PLUGIN_CB_INLINE_ADD_U64: case PLUGIN_CB_INLINE_STORE_U64: if (rw && cb->inline_insn.rw) { - exec_inline_op(&cb->inline_insn, cpu->cpu_index); + exec_inline_op(cb->type, &cb->inline_insn, cpu->cpu_index); } break; default: diff --git a/plugins/plugin.h b/plugins/plugin.h index 80d5daa917..30e2299a54 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -108,7 +108,9 @@ void plugin_register_vcpu_mem_cb(GArray **arr, enum qemu_plugin_mem_rw rw, void *udata); -void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index); +void exec_inline_op(enum plugin_dyn_cb_type type, + struct qemu_plugin_inline_cb *cb, + int cpu_index); int plugin_num_vcpus(void); From 836bb308685007ca7cccb891ec213a33f478af18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:50 +0200 Subject: [PATCH 0718/2884] vfio: Add Error** argument to .set_dirty_page_tracking() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will use the Error object to improve error reporting in the .log_global*() handlers of VFIO. Add documentation while at it. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Avihai Horon Reviewed-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 4 ++-- hw/vfio/container-base.c | 4 ++-- hw/vfio/container.c | 6 +++--- include/hw/vfio/vfio-container-base.h | 18 ++++++++++++++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 8f9cbdc026..485e539164 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1076,7 +1076,7 @@ static bool vfio_listener_log_global_start(MemoryListener *listener, if (vfio_devices_all_device_dirty_tracking(bcontainer)) { ret = vfio_devices_dma_logging_start(bcontainer); } else { - ret = vfio_container_set_dirty_page_tracking(bcontainer, true); + ret = vfio_container_set_dirty_page_tracking(bcontainer, true, NULL); } if (ret) { @@ -1096,7 +1096,7 @@ static void vfio_listener_log_global_stop(MemoryListener *listener) if (vfio_devices_all_device_dirty_tracking(bcontainer)) { vfio_devices_dma_logging_stop(bcontainer); } else { - ret = vfio_container_set_dirty_page_tracking(bcontainer, false); + ret = vfio_container_set_dirty_page_tracking(bcontainer, false, NULL); } if (ret) { diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 913ae49077..7c0764121d 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -53,14 +53,14 @@ void vfio_container_del_section_window(VFIOContainerBase *bcontainer, } int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, - bool start) + bool start, Error **errp) { if (!bcontainer->dirty_pages_supported) { return 0; } g_assert(bcontainer->ops->set_dirty_page_tracking); - return bcontainer->ops->set_dirty_page_tracking(bcontainer, start); + return bcontainer->ops->set_dirty_page_tracking(bcontainer, start, errp); } int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 77bdec276e..c35221fbe7 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -209,7 +209,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, static int vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, - bool start) + bool start, Error **errp) { const VFIOContainer *container = container_of(bcontainer, VFIOContainer, bcontainer); @@ -227,8 +227,8 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); if (ret) { ret = -errno; - error_report("Failed to set dirty tracking flag 0x%x errno: %d", - dirty.flags, errno); + error_setg_errno(errp, errno, "Failed to set dirty tracking flag 0x%x", + dirty.flags); } return ret; diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 3582d5f97a..326ceea52a 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -82,7 +82,7 @@ int vfio_container_add_section_window(VFIOContainerBase *bcontainer, void vfio_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section); int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, - bool start); + bool start, Error **errp); int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size); @@ -121,9 +121,23 @@ struct VFIOIOMMUClass { int (*attach_device)(const char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); void (*detach_device)(VFIODevice *vbasedev); + /* migration feature */ + + /** + * @set_dirty_page_tracking + * + * Start or stop dirty pages tracking on VFIO container + * + * @bcontainer: #VFIOContainerBase on which to de/activate dirty + * page tracking + * @start: indicates whether to start or stop dirty pages tracking + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer, - bool start); + bool start, Error **errp); int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size); From 0f21358f33f0b9aa1b8dd5e33de92118186999db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:51 +0200 Subject: [PATCH 0719/2884] vfio: Add Error** argument to vfio_devices_dma_logging_start() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to update the Error argument of the VFIO log_global_start() handler. Errors for container based logging will also be propagated to qemu_savevm_state_setup() when the ram save_setup() handler is executed. Also, errors from vfio_container_set_dirty_page_tracking() are now collected and reported. The vfio_set_migration_error() call becomes redundant in vfio_listener_log_global_start(). Remove it. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Avihai Horon Reviewed-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 485e539164..b5102f54a6 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1027,7 +1027,8 @@ static void vfio_device_feature_dma_logging_start_destroy( g_free(feature); } -static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer) +static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer, + Error **errp) { struct vfio_device_feature *feature; VFIODirtyRanges ranges; @@ -1038,6 +1039,7 @@ static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer) feature = vfio_device_feature_dma_logging_start_create(bcontainer, &ranges); if (!feature) { + error_setg_errno(errp, errno, "Failed to prepare DMA logging"); return -errno; } @@ -1049,8 +1051,8 @@ static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer) ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature); if (ret) { ret = -errno; - error_report("%s: Failed to start DMA logging, err %d (%s)", - vbasedev->name, ret, strerror(errno)); + error_setg_errno(errp, errno, "%s: Failed to start DMA logging", + vbasedev->name); goto out; } vbasedev->dirty_tracking = true; @@ -1069,20 +1071,19 @@ out: static bool vfio_listener_log_global_start(MemoryListener *listener, Error **errp) { + ERRP_GUARD(); VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener); int ret; if (vfio_devices_all_device_dirty_tracking(bcontainer)) { - ret = vfio_devices_dma_logging_start(bcontainer); + ret = vfio_devices_dma_logging_start(bcontainer, errp); } else { - ret = vfio_container_set_dirty_page_tracking(bcontainer, true, NULL); + ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp); } if (ret) { - error_report("vfio: Could not start dirty page tracking, err: %d (%s)", - ret, strerror(-ret)); - vfio_set_migration_error(ret); + error_prepend(errp, "vfio: Could not start dirty page tracking - "); } return !ret; } @@ -1091,17 +1092,20 @@ static void vfio_listener_log_global_stop(MemoryListener *listener) { VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener); + Error *local_err = NULL; int ret = 0; if (vfio_devices_all_device_dirty_tracking(bcontainer)) { vfio_devices_dma_logging_stop(bcontainer); } else { - ret = vfio_container_set_dirty_page_tracking(bcontainer, false, NULL); + ret = vfio_container_set_dirty_page_tracking(bcontainer, false, + &local_err); } if (ret) { - error_report("vfio: Could not stop dirty page tracking, err: %d (%s)", - ret, strerror(-ret)); + error_prepend(&local_err, + "vfio: Could not stop dirty page tracking - "); + error_report_err(local_err); vfio_set_migration_error(ret); } } From 019d9e6cc408f402dd27d6884ac7b742e0e4f99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:52 +0200 Subject: [PATCH 0720/2884] migration: Extend migration_file_set_error() with Error* argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use it to update the current error of the migration stream if available and if not, simply print out the error. Next changes will update with an error to report. Reviewed-by: Avihai Horon Acked-by: Fabiano Rosas Reviewed-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 4 ++-- hw/vfio/migration.c | 4 ++-- include/migration/misc.h | 2 +- migration/migration.c | 6 ++++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index b5102f54a6..2c97de6c73 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -147,10 +147,10 @@ bool vfio_viommu_preset(VFIODevice *vbasedev) return vbasedev->bcontainer->space->as != &address_space_memory; } -static void vfio_set_migration_error(int err) +static void vfio_set_migration_error(int ret) { if (migration_is_setup_or_active()) { - migration_file_set_error(err); + migration_file_set_error(ret, NULL); } } diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 06ae40969b..bf2fd0759b 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -726,7 +726,7 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running, * Migration should be aborted in this case, but vm_state_notify() * currently does not support reporting failures. */ - migration_file_set_error(ret); + migration_file_set_error(ret, NULL); } trace_vfio_vmstate_change_prepare(vbasedev->name, running, @@ -756,7 +756,7 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state) * Migration should be aborted in this case, but vm_state_notify() * currently does not support reporting failures. */ - migration_file_set_error(ret); + migration_file_set_error(ret, NULL); } trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state), diff --git a/include/migration/misc.h b/include/migration/misc.h index bf7339cc1e..bfadc5613b 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -97,7 +97,7 @@ void migration_add_notifier_mode(NotifierWithReturn *notify, void migration_remove_notifier(NotifierWithReturn *notify); bool migration_is_running(void); -void migration_file_set_error(int err); +void migration_file_set_error(int ret, Error *err); /* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */ bool migration_in_incoming_postcopy(void); diff --git a/migration/migration.c b/migration/migration.c index e88b24f1e6..70d66a441b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2994,13 +2994,15 @@ static MigThrError postcopy_pause(MigrationState *s) } } -void migration_file_set_error(int err) +void migration_file_set_error(int ret, Error *err) { MigrationState *s = current_migration; WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { if (s->to_dst_file) { - qemu_file_set_error(s->to_dst_file, err); + qemu_file_set_error_obj(s->to_dst_file, ret, err); + } else if (err) { + error_report_err(err); } } } From fbd2469a66e3fd3f3457170928daf8098f207427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:53 +0200 Subject: [PATCH 0721/2884] vfio/migration: Add an Error** argument to vfio_migration_set_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an Error** argument to vfio_migration_set_state() and adjust callers, including vfio_save_setup(). The error will be propagated up to qemu_savevm_state_setup() where the save_setup() handler is executed. Modify vfio_vmstate_change_prepare() and vfio_vmstate_change() to store a reported error under the migration stream if a migration is in progress. Reviewed-by: Avihai Horon Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 81 +++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index bf2fd0759b..43fed0dbdb 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -82,7 +82,8 @@ static const char *mig_state_to_str(enum vfio_device_mig_state state) static int vfio_migration_set_state(VFIODevice *vbasedev, enum vfio_device_mig_state new_state, - enum vfio_device_mig_state recover_state) + enum vfio_device_mig_state recover_state, + Error **errp) { VFIOMigration *migration = vbasedev->migration; uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + @@ -92,6 +93,9 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, struct vfio_device_feature_mig_state *mig_state = (struct vfio_device_feature_mig_state *)feature->data; int ret; + g_autofree char *error_prefix = + g_strdup_printf("%s: Failed setting device state to %s.", + vbasedev->name, mig_state_to_str(new_state)); feature->argsz = sizeof(buf); feature->flags = @@ -102,22 +106,24 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, ret = -errno; if (recover_state == VFIO_DEVICE_STATE_ERROR) { - error_report("%s: Failed setting device state to %s, err: %s. " - "Recover state is ERROR. Resetting device", - vbasedev->name, mig_state_to_str(new_state), - strerror(errno)); + error_setg_errno(errp, errno, + "%s Recover state is ERROR. Resetting device", + error_prefix); goto reset_device; } - error_report( - "%s: Failed setting device state to %s, err: %s. Setting device in recover state %s", - vbasedev->name, mig_state_to_str(new_state), - strerror(errno), mig_state_to_str(recover_state)); + error_setg_errno(errp, errno, + "%s Setting device in recover state %s", + error_prefix, mig_state_to_str(recover_state)); mig_state->device_state = recover_state; if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { ret = -errno; + /* + * If setting the device in recover state fails, report + * the error here and propagate the first error. + */ error_report( "%s: Failed setting device in recover state, err: %s. Resetting device", vbasedev->name, strerror(errno)); @@ -137,7 +143,7 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, * This can happen if the device is asynchronously reset and * terminates a data transfer. */ - error_report("%s: data_fd out of sync", vbasedev->name); + error_setg(errp, "%s: data_fd out of sync", vbasedev->name); close(mig_state->data_fd); return -EBADF; @@ -168,10 +174,11 @@ reset_device: */ static int vfio_migration_set_state_or_reset(VFIODevice *vbasedev, - enum vfio_device_mig_state new_state) + enum vfio_device_mig_state new_state, + Error **errp) { return vfio_migration_set_state(vbasedev, new_state, - VFIO_DEVICE_STATE_ERROR); + VFIO_DEVICE_STATE_ERROR, errp); } static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev, @@ -399,10 +406,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque, Error **errp) switch (migration->device_state) { case VFIO_DEVICE_STATE_RUNNING: ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_PRE_COPY, - VFIO_DEVICE_STATE_RUNNING); + VFIO_DEVICE_STATE_RUNNING, errp); if (ret) { - error_setg(errp, "%s: Failed to set new PRE_COPY state", - vbasedev->name); return ret; } @@ -435,13 +440,20 @@ static void vfio_save_cleanup(void *opaque) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; + Error *local_err = NULL; + int ret; /* * Changing device state from STOP_COPY to STOP can take time. Do it here, * after migration has completed, so it won't increase downtime. */ if (migration->device_state == VFIO_DEVICE_STATE_STOP_COPY) { - vfio_migration_set_state_or_reset(vbasedev, VFIO_DEVICE_STATE_STOP); + ret = vfio_migration_set_state_or_reset(vbasedev, + VFIO_DEVICE_STATE_STOP, + &local_err); + if (ret) { + error_report_err(local_err); + } } g_free(migration->data_buffer); @@ -549,11 +561,13 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) VFIODevice *vbasedev = opaque; ssize_t data_size; int ret; + Error *local_err = NULL; /* We reach here with device state STOP or STOP_COPY only */ ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY, - VFIO_DEVICE_STATE_STOP); + VFIO_DEVICE_STATE_STOP, &local_err); if (ret) { + error_report_err(local_err); return ret; } @@ -591,14 +605,9 @@ static void vfio_save_state(QEMUFile *f, void *opaque) static int vfio_load_setup(QEMUFile *f, void *opaque, Error **errp) { VFIODevice *vbasedev = opaque; - int ret; - ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, - vbasedev->migration->device_state); - if (ret) { - error_setg(errp, "%s: Failed to set RESUMING state", vbasedev->name); - } - return ret; + return vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, + vbasedev->migration->device_state, errp); } static int vfio_load_cleanup(void *opaque) @@ -714,19 +723,20 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running, VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; enum vfio_device_mig_state new_state; + Error *local_err = NULL; int ret; new_state = migration->device_state == VFIO_DEVICE_STATE_PRE_COPY ? VFIO_DEVICE_STATE_PRE_COPY_P2P : VFIO_DEVICE_STATE_RUNNING_P2P; - ret = vfio_migration_set_state_or_reset(vbasedev, new_state); + ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err); if (ret) { /* * Migration should be aborted in this case, but vm_state_notify() * currently does not support reporting failures. */ - migration_file_set_error(ret, NULL); + migration_file_set_error(ret, local_err); } trace_vfio_vmstate_change_prepare(vbasedev->name, running, @@ -738,6 +748,7 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state) { VFIODevice *vbasedev = opaque; enum vfio_device_mig_state new_state; + Error *local_err = NULL; int ret; if (running) { @@ -750,13 +761,13 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state) VFIO_DEVICE_STATE_STOP; } - ret = vfio_migration_set_state_or_reset(vbasedev, new_state); + ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err); if (ret) { /* * Migration should be aborted in this case, but vm_state_notify() * currently does not support reporting failures. */ - migration_file_set_error(ret, NULL); + migration_file_set_error(ret, local_err); } trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state), @@ -769,11 +780,23 @@ static int vfio_migration_state_notifier(NotifierWithReturn *notifier, VFIOMigration *migration = container_of(notifier, VFIOMigration, migration_state); VFIODevice *vbasedev = migration->vbasedev; + Error *local_err = NULL; + int ret; trace_vfio_migration_state_notifier(vbasedev->name, e->type); if (e->type == MIG_EVENT_PRECOPY_FAILED) { - vfio_migration_set_state_or_reset(vbasedev, VFIO_DEVICE_STATE_RUNNING); + /* + * MigrationNotifyFunc may not return an error code and an Error + * object for MIG_EVENT_PRECOPY_FAILED. Hence, report the error + * locally and ignore the errp argument. + */ + ret = vfio_migration_set_state_or_reset(vbasedev, + VFIO_DEVICE_STATE_RUNNING, + &local_err); + if (ret) { + error_report_err(local_err); + } } return 0; } From 3783f814e7e2e11cbd4e18e51e710db37900a1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:54 +0200 Subject: [PATCH 0722/2884] vfio/migration: Add Error** argument to .vfio_save_config() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use vmstate_save_state_with_err() to improve error reporting in the callers and store a reported error under the migration stream. Add documentation while at it. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Reviewed-by: Avihai Horon Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 25 ++++++++++++++++++------- hw/vfio/pci.c | 5 +++-- include/hw/vfio/vfio-common.h | 25 ++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 43fed0dbdb..5d91364f3b 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -193,21 +193,30 @@ static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev, return ret; } -static int vfio_save_device_config_state(QEMUFile *f, void *opaque) +static int vfio_save_device_config_state(QEMUFile *f, void *opaque, + Error **errp) { VFIODevice *vbasedev = opaque; + int ret; qemu_put_be64(f, VFIO_MIG_FLAG_DEV_CONFIG_STATE); if (vbasedev->ops && vbasedev->ops->vfio_save_config) { - vbasedev->ops->vfio_save_config(vbasedev, f); + ret = vbasedev->ops->vfio_save_config(vbasedev, f, errp); + if (ret) { + return ret; + } } qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); trace_vfio_save_device_config_state(vbasedev->name); - return qemu_file_get_error(f); + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to save state"); + } + return ret; } static int vfio_load_device_config_state(QEMUFile *f, void *opaque) @@ -592,13 +601,15 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) static void vfio_save_state(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; + Error *local_err = NULL; int ret; - ret = vfio_save_device_config_state(f, opaque); + ret = vfio_save_device_config_state(f, opaque, &local_err); if (ret) { - error_report("%s: Failed to save device config space", - vbasedev->name); - qemu_file_set_error(f, ret); + error_prepend(&local_err, + "vfio: Failed to save device config space of %s - ", + vbasedev->name); + qemu_file_set_error_obj(f, ret, local_err); } } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 64780d1b79..fc6e54e871 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2586,11 +2586,12 @@ static const VMStateDescription vmstate_vfio_pci_config = { } }; -static void vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f) +static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL); + return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev, NULL, + errp); } static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index d66e27db02..3ff633ad3b 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -133,7 +133,30 @@ struct VFIODeviceOps { int (*vfio_hot_reset_multi)(VFIODevice *vdev); void (*vfio_eoi)(VFIODevice *vdev); Object *(*vfio_get_object)(VFIODevice *vdev); - void (*vfio_save_config)(VFIODevice *vdev, QEMUFile *f); + + /** + * @vfio_save_config + * + * Save device config state + * + * @vdev: #VFIODevice for which to save the config + * @f: #QEMUFile where to send the data + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*vfio_save_config)(VFIODevice *vdev, QEMUFile *f, Error **errp); + + /** + * @vfio_load_config + * + * Load device config state + * + * @vdev: #VFIODevice for which to load the config + * @f: #QEMUFile where to get the data + * + * Returns zero to indicate success and negative for error + */ int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f); }; From 94d12088409b9544dd4e191e2971c47ecec4c315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:55 +0200 Subject: [PATCH 0723/2884] vfio: Reverse test on vfio_get_xlat_addr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will simplify the changes coming after. Reviewed-by: Avihai Horon Reviewed-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 2c97de6c73..c7f274fb5c 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1224,16 +1224,20 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) } rcu_read_lock(); - if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) { - ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, - translated_addr); - if (ret) { - error_report("vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%s)", - bcontainer, iova, iotlb->addr_mask + 1, ret, - strerror(-ret)); - } + if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) { + goto out_unlock; } + + ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, + translated_addr); + if (ret) { + error_report("vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, iotlb->addr_mask + 1, ret, + strerror(-ret)); + } + +out_unlock: rcu_read_unlock(); out: From ebb481c03c22eaf052b02c37ddd53989a078b771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:56 +0200 Subject: [PATCH 0724/2884] memory: Add Error** argument to memory_get_xlat_addr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let the callers do the reporting. This will be useful in vfio_iommu_map_dirty_notify(). Cc: Michael S. Tsirkin Cc: Paolo Bonzini Cc: David Hildenbrand Reviewed-by: Peter Xu Reviewed-by: Eric Auger Reviewed-by: Avihai Horon Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 13 +++++++++---- hw/virtio/vhost-vdpa.c | 5 ++++- include/exec/memory.h | 15 ++++++++++++++- system/memory.c | 10 +++++----- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index c7f274fb5c..7313043f1d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -253,12 +253,13 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) /* Called with rcu_read_lock held. */ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only) + ram_addr_t *ram_addr, bool *read_only, + Error **errp) { bool ret, mr_has_discard_manager; ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only, - &mr_has_discard_manager); + &mr_has_discard_manager, errp); if (ret && mr_has_discard_manager) { /* * Malicious VMs might trigger discarding of IOMMU-mapped memory. The @@ -288,6 +289,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) hwaddr iova = iotlb->iova + giommu->iommu_offset; void *vaddr; int ret; + Error *local_err = NULL; trace_vfio_iommu_map_notify(iotlb->perm == IOMMU_NONE ? "UNMAP" : "MAP", iova, iova + iotlb->addr_mask); @@ -304,7 +306,8 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { bool read_only; - if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only)) { + if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, &local_err)) { + error_report_err(local_err); goto out; } /* @@ -1213,6 +1216,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) VFIOContainerBase *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; ram_addr_t translated_addr; + Error *local_err = NULL; int ret = -EINVAL; trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask); @@ -1224,7 +1228,8 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) } rcu_read_lock(); - if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) { + if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, &local_err)) { + error_report_err(local_err); goto out_unlock; } diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index e827b9175f..ed99ab8745 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -208,6 +208,7 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) void *vaddr; int ret; Int128 llend; + Error *local_err = NULL; if (iotlb->target_as != &address_space_memory) { error_report("Wrong target AS \"%s\", only system memory is allowed", @@ -227,7 +228,9 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { bool read_only; - if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL)) { + if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL, + &local_err)) { + error_report_err(local_err); return; } ret = vhost_vdpa_dma_map(s, VHOST_VDPA_GUEST_PA_ASID, iova, diff --git a/include/exec/memory.h b/include/exec/memory.h index d417d7f363..9cdd64e9c6 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -774,9 +774,22 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm, void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl); +/** + * memory_get_xlat_addr: Extract addresses from a TLB entry + * + * @iotlb: pointer to an #IOMMUTLBEntry + * @vaddr: virtual address + * @ram_addr: RAM address + * @read_only: indicates if writes are allowed + * @mr_has_discard_manager: indicates memory is controlled by a + * RamDiscardManager + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. + */ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, ram_addr_t *ram_addr, bool *read_only, - bool *mr_has_discard_manager); + bool *mr_has_discard_manager, Error **errp); typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; diff --git a/system/memory.c b/system/memory.c index 642a449f8c..9540caa8a1 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2179,7 +2179,7 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, /* Called with rcu_read_lock held. */ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, ram_addr_t *ram_addr, bool *read_only, - bool *mr_has_discard_manager) + bool *mr_has_discard_manager, Error **errp) { MemoryRegion *mr; hwaddr xlat; @@ -2197,7 +2197,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, mr = address_space_translate(&address_space_memory, iotlb->translated_addr, &xlat, &len, writable, MEMTXATTRS_UNSPECIFIED); if (!memory_region_is_ram(mr)) { - error_report("iommu map to non memory area %" HWADDR_PRIx "", xlat); + error_setg(errp, "iommu map to non memory area %" HWADDR_PRIx "", xlat); return false; } else if (memory_region_has_ram_discard_manager(mr)) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr); @@ -2216,8 +2216,8 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, * were already restored before IOMMUs are restored. */ if (!ram_discard_manager_is_populated(rdm, &tmp)) { - error_report("iommu map to discarded memory (e.g., unplugged via" - " virtio-mem): %" HWADDR_PRIx "", + error_setg(errp, "iommu map to discarded memory (e.g., unplugged" + " via virtio-mem): %" HWADDR_PRIx "", iotlb->translated_addr); return false; } @@ -2228,7 +2228,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, * check that it did not truncate too much. */ if (len & iotlb->addr_mask) { - error_report("iommu has granularity incompatible with target AS"); + error_setg(errp, "iommu has granularity incompatible with target AS"); return false; } From 2da5f9e4d86c1a6cf5ff35cdb7b87e993e947fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:57 +0200 Subject: [PATCH 0725/2884] vfio: Add Error** argument to .get_dirty_bitmap() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let the callers do the error reporting. Add documentation while at it. Reviewed-by: Eric Auger Reviewed-by: Avihai Horon Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 60 +++++++++++++++++---------- hw/vfio/container-base.c | 6 +-- hw/vfio/container.c | 14 ++++--- include/hw/vfio/vfio-common.h | 5 +-- include/hw/vfio/vfio-container-base.h | 19 +++++++-- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 7313043f1d..1fbd10801d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1140,8 +1140,7 @@ static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, } int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, - hwaddr size) + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { VFIODevice *vbasedev; int ret; @@ -1150,10 +1149,10 @@ int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, ret = vfio_device_dma_logging_report(vbasedev, iova, size, vbmap->bitmap); if (ret) { - error_report("%s: Failed to get DMA logging report, iova: " - "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx - ", err: %d (%s)", - vbasedev->name, iova, size, ret, strerror(-ret)); + error_setg_errno(errp, -ret, + "%s: Failed to get DMA logging report, iova: " + "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx, + vbasedev->name, iova, size); return ret; } @@ -1163,7 +1162,7 @@ int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, } int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, - uint64_t size, ram_addr_t ram_addr) + uint64_t size, ram_addr_t ram_addr, Error **errp) { bool all_device_dirty_tracking = vfio_devices_all_device_dirty_tracking(bcontainer); @@ -1180,13 +1179,17 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, ret = vfio_bitmap_alloc(&vbmap, size); if (ret) { + error_setg_errno(errp, -ret, + "Failed to allocate dirty tracking bitmap"); return ret; } if (all_device_dirty_tracking) { - ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size); + ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); } else { - ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size); + ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); } if (ret) { @@ -1234,12 +1237,13 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) } ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, - translated_addr); + translated_addr, &local_err); if (ret) { - error_report("vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%s)", - bcontainer, iova, iotlb->addr_mask + 1, ret, - strerror(-ret)); + error_prepend(&local_err, + "vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") failed - ", bcontainer, iova, + iotlb->addr_mask + 1); + error_report_err(local_err); } out_unlock: @@ -1259,12 +1263,19 @@ static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, const ram_addr_t ram_addr = memory_region_get_ram_addr(section->mr) + section->offset_within_region; VFIORamDiscardListener *vrdl = opaque; + Error *local_err = NULL; + int ret; /* * Sync the whole mapped region (spanning multiple individual mappings) * in one go. */ - return vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr); + ret = vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr, + &local_err); + if (ret) { + error_report_err(local_err); + } + return ret; } static int @@ -1296,7 +1307,7 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, } static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, - MemoryRegionSection *section) + MemoryRegionSection *section, Error **errp) { ram_addr_t ram_addr; @@ -1327,7 +1338,14 @@ static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, } return 0; } else if (memory_region_has_ram_discard_manager(section->mr)) { - return vfio_sync_ram_discard_listener_dirty_bitmap(bcontainer, section); + int ret; + + ret = vfio_sync_ram_discard_listener_dirty_bitmap(bcontainer, section); + if (ret) { + error_setg(errp, + "Failed to sync dirty bitmap with RAM discard listener"); + } + return ret; } ram_addr = memory_region_get_ram_addr(section->mr) + @@ -1335,7 +1353,7 @@ static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, return vfio_get_dirty_bitmap(bcontainer, REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), - int128_get64(section->size), ram_addr); + int128_get64(section->size), ram_addr, errp); } static void vfio_listener_log_sync(MemoryListener *listener, @@ -1344,16 +1362,16 @@ static void vfio_listener_log_sync(MemoryListener *listener, VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener); int ret; + Error *local_err = NULL; if (vfio_listener_skipped_section(section)) { return; } if (vfio_devices_all_dirty_tracking(bcontainer)) { - ret = vfio_sync_dirty_bitmap(bcontainer, section); + ret = vfio_sync_dirty_bitmap(bcontainer, section, &local_err); if (ret) { - error_report("vfio: Failed to sync dirty bitmap, err: %d (%s)", ret, - strerror(-ret)); + error_report_err(local_err); vfio_set_migration_error(ret); } } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 7c0764121d..26f4bb464a 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -64,11 +64,11 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, } int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, - hwaddr iova, hwaddr size) + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { g_assert(bcontainer->ops->query_dirty_bitmap); - return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size); + return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size, + errp); } void vfio_container_init(VFIOContainerBase *bcontainer, VFIOAddressSpace *space, diff --git a/hw/vfio/container.c b/hw/vfio/container.c index c35221fbe7..9534120d4a 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -130,6 +130,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, }; bool need_dirty_sync = false; int ret; + Error *local_err = NULL; if (iotlb && vfio_devices_all_running_and_mig_active(bcontainer)) { if (!vfio_devices_all_device_dirty_tracking(bcontainer) && @@ -165,8 +166,9 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, if (need_dirty_sync) { ret = vfio_get_dirty_bitmap(bcontainer, iova, size, - iotlb->translated_addr); + iotlb->translated_addr, &local_err); if (ret) { + error_report_err(local_err); return ret; } } @@ -235,8 +237,7 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, } static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, - hwaddr iova, hwaddr size) + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { const VFIOContainer *container = container_of(bcontainer, VFIOContainer, bcontainer); @@ -264,9 +265,10 @@ static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap); if (ret) { ret = -errno; - error_report("Failed to get dirty bitmap for iova: 0x%"PRIx64 - " size: 0x%"PRIx64" err: %d", (uint64_t)range->iova, - (uint64_t)range->size, errno); + error_setg_errno(errp, errno, + "Failed to get dirty bitmap for iova: 0x%"PRIx64 + " size: 0x%"PRIx64, (uint64_t)range->iova, + (uint64_t)range->size); } g_free(dbitmap); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 3ff633ad3b..b6ac249536 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -273,10 +273,9 @@ vfio_devices_all_running_and_mig_active(const VFIOContainerBase *bcontainer); bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, - hwaddr size); + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, - uint64_t size, ram_addr_t ram_addr); + uint64_t size, ram_addr_t ram_addr, Error **errp); /* Returns 0 on success, or a negative errno. */ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp); diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 326ceea52a..b04057ad1a 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -84,8 +84,7 @@ void vfio_container_del_section_window(VFIOContainerBase *bcontainer, int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp); int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, - hwaddr iova, hwaddr size); + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); void vfio_container_init(VFIOContainerBase *bcontainer, VFIOAddressSpace *space, @@ -138,9 +137,21 @@ struct VFIOIOMMUClass { */ int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer, bool start, Error **errp); + /** + * @query_dirty_bitmap + * + * Get bitmap of dirty pages from container + * + * @bcontainer: #VFIOContainerBase from which to get dirty pages + * @vbmap: #VFIOBitmap internal bitmap structure + * @iova: iova base address + * @size: size of iova range + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, - hwaddr iova, hwaddr size); + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); /* PCI specific */ int (*pci_hot_reset)(VFIODevice *vbasedev, bool single); From 33dc04b0722974ae140527d9c44eb442f8ac463e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 16 May 2024 14:46:58 +0200 Subject: [PATCH 0726/2884] vfio: Also trace event failures in vfio_save_complete_precopy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_save_complete_precopy() currently returns before doing the trace event. Change that. Reviewed-by: Avihai Horon Reviewed-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 5d91364f3b..c4403a38dd 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -589,9 +589,6 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); ret = qemu_file_get_error(f); - if (ret) { - return ret; - } trace_vfio_save_complete_precopy(vbasedev->name, ret); From ae7aca14bd531922e27767e90902b673f2cf786a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 25 Apr 2024 11:02:11 +0200 Subject: [PATCH 0727/2884] vfio/ap: Use g_autofree variable in vfio_ap_register_irq_notifier() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Markus Armbruster Reviewed-by: Anthony Krowiak Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 7c4caa5938..03f8ffaa5e 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -77,7 +77,7 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, size_t argsz; IOHandler *fd_read; EventNotifier *notifier; - struct vfio_irq_info *irq_info; + g_autofree struct vfio_irq_info *irq_info = NULL; VFIODevice *vdev = &vapdev->vdev; switch (irq) { @@ -104,14 +104,14 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info) < 0 || irq_info->count < 1) { error_setg_errno(errp, errno, "vfio: Error getting irq info"); - goto out_free_info; + return; } if (event_notifier_init(notifier, 0)) { error_setg_errno(errp, errno, "vfio: Unable to init event notifier for irq (%d)", irq); - goto out_free_info; + return; } fd = event_notifier_get_fd(notifier); @@ -122,10 +122,6 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, qemu_set_fd_handler(fd, NULL, NULL, vapdev); event_notifier_cleanup(notifier); } - -out_free_info: - g_free(irq_info); - } static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, From cbd470f0aac51103e010e45e1b10cb3bfedb4f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 25 Apr 2024 11:02:12 +0200 Subject: [PATCH 0728/2884] vfio/ap: Make vfio_ap_register_irq_notifier() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_ap_register_irq_notifier() takes and 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. Reviewed-by: Markus Armbruster Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 03f8ffaa5e..8bb024e2fd 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -70,7 +70,7 @@ static void vfio_ap_req_notifier_handler(void *opaque) } } -static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, +static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, unsigned int irq, Error **errp) { int fd; @@ -87,13 +87,13 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, break; default: error_setg(errp, "vfio: Unsupported device irq(%d)", irq); - return; + return false; } if (vdev->num_irqs < irq + 1) { error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", irq, vdev->num_irqs); - return; + return false; } argsz = sizeof(*irq_info); @@ -104,14 +104,14 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info) < 0 || irq_info->count < 1) { error_setg_errno(errp, errno, "vfio: Error getting irq info"); - return; + return false; } if (event_notifier_init(notifier, 0)) { error_setg_errno(errp, errno, "vfio: Unable to init event notifier for irq (%d)", irq); - return; + return false; } fd = event_notifier_get_fd(notifier); @@ -122,6 +122,8 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, qemu_set_fd_handler(fd, NULL, NULL, vapdev); event_notifier_cleanup(notifier); } + + return true; } static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, @@ -167,8 +169,7 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) goto error; } - vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err); - if (err) { + if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err)) { /* * Report this error, but do not make it a failing condition. * Lack of this IRQ in the host does not prevent normal operation. From 04f8e4f29b5264f855be015852497b3c1dcdb3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 25 Apr 2024 11:02:13 +0200 Subject: [PATCH 0729/2884] vfio/ccw: Use g_autofree variable in vfio_ccw_register_irq_notifier() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Markus Armbruster Reviewed-by: Eric Farman Signed-off-by: Cédric Le Goater --- hw/vfio/ccw.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 90e4a53437..6764388bc4 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -384,7 +384,7 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, Error **errp) { VFIODevice *vdev = &vcdev->vdev; - struct vfio_irq_info *irq_info; + g_autofree struct vfio_irq_info *irq_info = NULL; size_t argsz; int fd; EventNotifier *notifier; @@ -421,14 +421,14 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info) < 0 || irq_info->count < 1) { error_setg_errno(errp, errno, "vfio: Error getting irq info"); - goto out_free_info; + return; } if (event_notifier_init(notifier, 0)) { error_setg_errno(errp, errno, "vfio: Unable to init event notifier for irq (%d)", irq); - goto out_free_info; + return; } fd = event_notifier_get_fd(notifier); @@ -439,9 +439,6 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, qemu_set_fd_handler(fd, NULL, NULL, vcdev); event_notifier_cleanup(notifier); } - -out_free_info: - g_free(irq_info); } static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, From 8aaeff97acee1ad0c96b6c229cd02c8f3e96dcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 25 Apr 2024 11:02:14 +0200 Subject: [PATCH 0730/2884] vfio/ccw: Make vfio_ccw_register_irq_notifier() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_ccw_register_irq_notifier() takes an 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. Reviewed-by: Markus Armbruster Reviewed-by: Eric Farman Signed-off-by: Cédric Le Goater --- hw/vfio/ccw.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 6764388bc4..1c630f6e9a 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -379,7 +379,7 @@ read_err: css_inject_io_interrupt(sch); } -static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, +static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, unsigned int irq, Error **errp) { @@ -405,13 +405,13 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, break; default: error_setg(errp, "vfio: Unsupported device irq(%d)", irq); - return; + return false; } if (vdev->num_irqs < irq + 1) { error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", irq, vdev->num_irqs); - return; + return false; } argsz = sizeof(*irq_info); @@ -421,14 +421,14 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info) < 0 || irq_info->count < 1) { error_setg_errno(errp, errno, "vfio: Error getting irq info"); - return; + return false; } if (event_notifier_init(notifier, 0)) { error_setg_errno(errp, errno, "vfio: Unable to init event notifier for irq (%d)", irq); - return; + return false; } fd = event_notifier_get_fd(notifier); @@ -439,6 +439,8 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, qemu_set_fd_handler(fd, NULL, NULL, vcdev); event_notifier_cleanup(notifier); } + + return true; } static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, @@ -602,20 +604,18 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_region_err; } - vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err); - if (err) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err)) { goto out_io_notifier_err; } if (vcdev->crw_region) { - vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); - if (err) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, + &err)) { goto out_irq_notifier_err; } } - vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err); - if (err) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err)) { /* * Report this error, but do not make it a failing condition. * Lack of this IRQ in the host does not prevent normal operation. From 187716feeba406b5a3879db66a7bafd687472a1f Mon Sep 17 00:00:00 2001 From: Vinayak Kale Date: Fri, 3 May 2024 20:21:42 +0530 Subject: [PATCH 0731/2884] vfio/pci: migration: Skip config space check for Vendor Specific Information in VSC during restore/load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of migration, during restore operation, qemu checks config space of the pci device with the config space in the migration stream captured during save operation. In case of config space data mismatch, restore operation is failed. config space check is done in function get_pci_config_device(). By default VSC (vendor-specific-capability) in config space is checked. Due to qemu's config space check for VSC, live migration is broken across NVIDIA vGPU devices in situation where source and destination host driver is different. In this situation, Vendor Specific Information in VSC varies on the destination to ensure vGPU feature capabilities exposed to the guest driver are compatible with destination host. If a vfio-pci device is migration capable and vfio-pci vendor driver is OK with volatile Vendor Specific Info in VSC then qemu should exempt config space check for Vendor Specific Info. It is vendor driver's responsibility to ensure that VSC is consistent across migration. Here consistency could mean that VSC format should be same on source and destination, however actual Vendor Specific Info may not be byte-to-byte identical. This patch skips the check for Vendor Specific Information in VSC for VFIO-PCI device by clearing pdev->cmask[] offsets. Config space check is still enforced for 3 byte VSC header. If cmask[] is not set for an offset, then qemu skips config space check for that offset. VSC check is skipped for machine types >= 9.1. The check would be enforced on older machine types (<= 9.0). Cc: Alex Williamson Cc: Michael S. Tsirkin Cc: Cédric Le Goater Signed-off-by: Vinayak Kale Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/core/machine.c | 1 + hw/vfio/pci.c | 26 ++++++++++++++++++++++++++ hw/vfio/pci.h | 1 + 3 files changed, 28 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index c7ceb11501..3442f31f94 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -35,6 +35,7 @@ GlobalProperty hw_compat_9_0[] = { {"arm-cpu", "backcompat-cntfrq", "true" }, + {"vfio-pci", "skip-vsc-check", "false" }, }; const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index fc6e54e871..4789d43c0f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2134,6 +2134,28 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) } } +static int vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, + uint8_t size, Error **errp) +{ + PCIDevice *pdev = &vdev->pdev; + + pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp); + if (pos < 0) { + return pos; + } + + /* + * Exempt config space check for Vendor Specific Information during + * restore/load. + * Config space check is still enforced for 3 byte VSC header. + */ + if (vdev->skip_vsc_check && size > 3) { + memset(pdev->cmask + pos + 3, 0, size - 3); + } + + return pos; +} + static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) { ERRP_GUARD(); @@ -2202,6 +2224,9 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) vfio_check_af_flr(vdev, pos); ret = pci_add_capability(pdev, cap_id, pos, size, errp); break; + case PCI_CAP_ID_VNDR: + ret = vfio_add_vendor_specific_cap(vdev, pos, size, errp); + break; default: ret = pci_add_capability(pdev, cap_id, pos, size, errp); break; @@ -3391,6 +3416,7 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_LINK("iommufd", VFIOPCIDevice, vbasedev.iommufd, TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), #endif + DEFINE_PROP_BOOL("skip-vsc-check", VFIOPCIDevice, skip_vsc_check, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 6e64a2654e..92cd62d115 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -177,6 +177,7 @@ struct VFIOPCIDevice { OnOffAuto ramfb_migrate; bool defer_kvm_irq_routing; bool clear_parent_atomics_on_exit; + bool skip_vsc_check; VFIODisplay *dpy; Notifier irqchip_change_notifier; }; From a0359b56ecb2002fbecd249b1ea71df618dbe4a2 Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 15 May 2024 16:21:35 +0300 Subject: [PATCH 0732/2884] qapi/vfio: Add VFIO migration QAPI event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new QAPI event for VFIO migration. This event will be emitted when a VFIO device changes its migration state, for example, during migration or when stopping/starting the guest. This event can be used by management applications to get updates on the current state of the VFIO device for their own purposes. Note that this new event is introduced since VFIO devices have a unique set of migration states which cannot be described as accurately by other existing events such as run state or migration status. Signed-off-by: Avihai Horon Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + qapi/vfio.json | 67 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 qapi/vfio.json diff --git a/MAINTAINERS b/MAINTAINERS index 1b79767d61..448dc951c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2164,6 +2164,7 @@ F: hw/vfio/* F: include/hw/vfio/ F: docs/igd-assign.txt F: docs/devel/migration/vfio.rst +F: qapi/vfio.json vfio-ccw M: Eric Farman diff --git a/qapi/meson.build b/qapi/meson.build index c92af6e063..e7bc54e5d0 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -52,6 +52,7 @@ qapi_all_modules = [ 'stats', 'trace', 'transaction', + 'vfio', 'virtio', 'yank', ] diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 5e33da7228..b1581988e4 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -78,5 +78,6 @@ { 'include': 'pci.json' } { 'include': 'stats.json' } { 'include': 'virtio.json' } +{ 'include': 'vfio.json' } { 'include': 'cryptodev.json' } { 'include': 'cxl.json' } diff --git a/qapi/vfio.json b/qapi/vfio.json new file mode 100644 index 0000000000..a0e5013188 --- /dev/null +++ b/qapi/vfio.json @@ -0,0 +1,67 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# + +## +# = VFIO devices +## + +## +# @VfioMigrationState: +# +# An enumeration of the VFIO device migration states. +# +# @stop: The device is stopped. +# +# @running: The device is running. +# +# @stop-copy: The device is stopped and its internal state is available +# for reading. +# +# @resuming: The device is stopped and its internal state is available +# for writing. +# +# @running-p2p: The device is running in the P2P quiescent state. +# +# @pre-copy: The device is running, tracking its internal state and its +# internal state is available for reading. +# +# @pre-copy-p2p: The device is running in the P2P quiescent state, +# tracking its internal state and its internal state is available +# for reading. +# +# Since: 9.1 +## +{ 'enum': 'VfioMigrationState', + 'data': [ 'stop', 'running', 'stop-copy', 'resuming', 'running-p2p', + 'pre-copy', 'pre-copy-p2p' ], + 'prefix': 'QAPI_VFIO_MIGRATION_STATE' } + +## +# @VFIO_MIGRATION: +# +# This event is emitted when a VFIO device migration state is changed. +# +# @device-id: The device's id, if it has one. +# +# @qom-path: The device's QOM path. +# +# @device-state: The new changed device migration state. +# +# Since: 9.1 +# +# Example: +# +# <- { "timestamp": { "seconds": 1713771323, "microseconds": 212268 }, +# "event": "VFIO_MIGRATION", +# "data": { +# "device-id": "vfio_dev1", +# "qom-path": "/machine/peripheral/vfio_dev1", +# "device-state": "stop" } } +## +{ 'event': 'VFIO_MIGRATION', + 'data': { + 'device-id': 'str', + 'qom-path': 'str', + 'device-state': 'VfioMigrationState' + } } From 5e1f8905ca72775452f714008c3906c9d86158ae Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 15 May 2024 16:21:36 +0300 Subject: [PATCH 0733/2884] vfio/migration: Emit VFIO migration QAPI event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emit VFIO migration QAPI event when a VFIO device changes its migration state. This can be used by management applications to get updates on the current state of the VFIO device for their own purposes. A new per VFIO device capability, "migration-events", is added so events can be enabled only for the required devices. It is disabled by default. Signed-off-by: Avihai Horon Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 59 +++++++++++++++++++++++++++++++++-- hw/vfio/pci.c | 2 ++ include/hw/vfio/vfio-common.h | 1 + 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index c4403a38dd..af579b868d 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -24,6 +24,7 @@ #include "migration/register.h" #include "migration/blocker.h" #include "qapi/error.h" +#include "qapi/qapi-events-vfio.h" #include "exec/ramlist.h" #include "exec/ram_addr.h" #include "pci.h" @@ -80,6 +81,58 @@ static const char *mig_state_to_str(enum vfio_device_mig_state state) } } +static VfioMigrationState +mig_state_to_qapi_state(enum vfio_device_mig_state state) +{ + switch (state) { + case VFIO_DEVICE_STATE_STOP: + return QAPI_VFIO_MIGRATION_STATE_STOP; + case VFIO_DEVICE_STATE_RUNNING: + return QAPI_VFIO_MIGRATION_STATE_RUNNING; + case VFIO_DEVICE_STATE_STOP_COPY: + return QAPI_VFIO_MIGRATION_STATE_STOP_COPY; + case VFIO_DEVICE_STATE_RESUMING: + return QAPI_VFIO_MIGRATION_STATE_RESUMING; + case VFIO_DEVICE_STATE_RUNNING_P2P: + return QAPI_VFIO_MIGRATION_STATE_RUNNING_P2P; + case VFIO_DEVICE_STATE_PRE_COPY: + return QAPI_VFIO_MIGRATION_STATE_PRE_COPY; + case VFIO_DEVICE_STATE_PRE_COPY_P2P: + return QAPI_VFIO_MIGRATION_STATE_PRE_COPY_P2P; + default: + g_assert_not_reached(); + } +} + +static void vfio_migration_send_event(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + DeviceState *dev = vbasedev->dev; + g_autofree char *qom_path = NULL; + Object *obj; + + if (!vbasedev->migration_events) { + return; + } + + g_assert(vbasedev->ops->vfio_get_object); + obj = vbasedev->ops->vfio_get_object(vbasedev); + g_assert(obj); + qom_path = object_get_canonical_path(obj); + + qapi_event_send_vfio_migration( + dev->id, qom_path, mig_state_to_qapi_state(migration->device_state)); +} + +static void vfio_migration_set_device_state(VFIODevice *vbasedev, + enum vfio_device_mig_state state) +{ + VFIOMigration *migration = vbasedev->migration; + + migration->device_state = state; + vfio_migration_send_event(vbasedev); +} + static int vfio_migration_set_state(VFIODevice *vbasedev, enum vfio_device_mig_state new_state, enum vfio_device_mig_state recover_state, @@ -131,12 +184,12 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, goto reset_device; } - migration->device_state = recover_state; + vfio_migration_set_device_state(vbasedev, recover_state); return ret; } - migration->device_state = new_state; + vfio_migration_set_device_state(vbasedev, new_state); if (mig_state->data_fd != -1) { if (migration->data_fd != -1) { /* @@ -162,7 +215,7 @@ reset_device: strerror(errno)); } - migration->device_state = VFIO_DEVICE_STATE_RUNNING; + vfio_migration_set_device_state(vbasedev, VFIO_DEVICE_STATE_RUNNING); return ret; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4789d43c0f..b5d1d398b1 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3388,6 +3388,8 @@ static Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, vbasedev.enable_migration, ON_OFF_AUTO_AUTO), + DEFINE_PROP_BOOL("migration-events", VFIOPCIDevice, + vbasedev.migration_events, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice, vbasedev.ram_block_discard_allowed, false), diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index b6ac249536..878e34a128 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -115,6 +115,7 @@ typedef struct VFIODevice { bool no_mmap; bool ram_block_discard_allowed; OnOffAuto enable_migration; + bool migration_events; VFIODeviceOps *ops; unsigned int num_irqs; unsigned int num_regions; From 64366eddf15a6224263c1152f9b0a1a97965e932 Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 15 May 2024 16:21:37 +0300 Subject: [PATCH 0734/2884] vfio/migration: Don't emit STOP_COPY VFIO migration QAPI event twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When migrating a VFIO device that supports pre-copy, it is transitioned to STOP_COPY twice: once in vfio_vmstate_change() and second time in vfio_save_complete_precopy(). The second transition is harmless, as it's a STOP_COPY->STOP_COPY no-op transition. However, with the newly added VFIO migration QAPI event, the STOP_COPY event is undesirably emitted twice. Prevent this by returning early in vfio_migration_set_state() if new_state is the same as current device state. Note that the STOP_COPY transition in vfio_save_complete_precopy() is essential for VFIO devices that don't support pre-copy, for migrating an already stopped guest and for snapshots. Signed-off-by: Avihai Horon Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index af579b868d..56edffaf62 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -150,6 +150,10 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, g_strdup_printf("%s: Failed setting device state to %s.", vbasedev->name, mig_state_to_str(new_state)); + if (new_state == migration->device_state) { + return 0; + } + feature->argsz = sizeof(buf); feature->flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE; From 11ebce2a5544cacab31e8ff169c6f0ceef37f4e8 Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Wed, 15 May 2024 16:21:38 +0300 Subject: [PATCH 0735/2884] vfio/migration: Enhance VFIO migration state tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move trace_vfio_migration_set_state() to the top of the function, add recover_state to it, and add a new trace event to vfio_migration_set_device_state(). This improves tracing of device state changes as state changes are now also logged when vfio_migration_set_state() fails (covering recover state and device reset transitions) and in no-op state transitions to the same state. Suggested-by: Cédric Le Goater Signed-off-by: Avihai Horon Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 8 ++++++-- hw/vfio/trace-events | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 56edffaf62..34d4be2ce1 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -129,6 +129,9 @@ static void vfio_migration_set_device_state(VFIODevice *vbasedev, { VFIOMigration *migration = vbasedev->migration; + trace_vfio_migration_set_device_state(vbasedev->name, + mig_state_to_str(state)); + migration->device_state = state; vfio_migration_send_event(vbasedev); } @@ -150,6 +153,9 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, g_strdup_printf("%s: Failed setting device state to %s.", vbasedev->name, mig_state_to_str(new_state)); + trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state), + mig_state_to_str(recover_state)); + if (new_state == migration->device_state) { return 0; } @@ -209,8 +215,6 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, migration->data_fd = mig_state->data_fd; } - trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state)); - return 0; reset_device: diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index f0474b244b..64161bf6f4 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -152,7 +152,8 @@ vfio_load_device_config_state(const char *name) " (%s)" vfio_load_state(const char *name, uint64_t data) " (%s) data 0x%"PRIx64 vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size 0x%"PRIx64" ret %d" vfio_migration_realize(const char *name) " (%s)" -vfio_migration_set_state(const char *name, const char *state) " (%s) state %s" +vfio_migration_set_device_state(const char *name, const char *state) " (%s) state %s" +vfio_migration_set_state(const char *name, const char *new_state, const char *recover_state) " (%s) new state %s, recover state %s" vfio_migration_state_notifier(const char *name, int state) " (%s) state %d" vfio_save_block(const char *name, int data_size) " (%s) data_size %d" vfio_save_cleanup(const char *name) " (%s)" From 81987bd58b558be71dc4d8adf163f8c787a1c3e9 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:42 +0800 Subject: [PATCH 0736/2884] vfio/pci: Use g_autofree in vfio_realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Local pointer name is allocated before vfio_attach_device() call and freed after the call. Same for tmp when calling realpath(). Use 'g_autofree' to avoid the g_free() calls. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b5d1d398b1..84f7bff664 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2972,12 +2972,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) ERRP_GUARD(); VFIOPCIDevice *vdev = VFIO_PCI(pdev); VFIODevice *vbasedev = &vdev->vbasedev; - char *tmp, *subsys; + char *subsys; Error *err = NULL; int i, ret; bool is_mdev; char uuid[UUID_STR_LEN]; - char *name; + g_autofree char *name = NULL; + g_autofree char *tmp = NULL; if (vbasedev->fd < 0 && !vbasedev->sysfsdev) { if (!(~vdev->host.domain || ~vdev->host.bus || @@ -3008,7 +3009,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) */ tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); subsys = realpath(tmp, NULL); - g_free(tmp); is_mdev = subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); free(subsys); @@ -3029,7 +3029,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) ret = vfio_attach_device(name, vbasedev, pci_device_iommu_address_space(pdev), errp); - g_free(name); if (ret) { goto error; } From f3758413b77d396dd4db68643f7068fb49ec77d9 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:43 +0800 Subject: [PATCH 0737/2884] vfio/pci: Use g_autofree in iommufd_cdev_get_info_iova_range() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Local pointer info is freed before return from iommufd_cdev_get_info_iova_range(). Use 'g_autofree' to avoid the g_free() calls. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 8827ffe636..c644127972 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -258,7 +258,7 @@ static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, uint32_t ioas_id, Error **errp) { VFIOContainerBase *bcontainer = &container->bcontainer; - struct iommu_ioas_iova_ranges *info; + g_autofree struct iommu_ioas_iova_ranges *info = NULL; struct iommu_iova_range *iova_ranges; int ret, sz, fd = container->be->fd; @@ -291,12 +291,10 @@ static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, } bcontainer->pgsizes = info->out_iova_alignment; - g_free(info); return 0; error: ret = -errno; - g_free(info); error_setg_errno(errp, errno, "Cannot get IOVA ranges"); return ret; } From b77548355ab5d8c8377ba8f981c7c597507de37a Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:44 +0800 Subject: [PATCH 0738/2884] vfio: Make VFIOIOMMUClass::attach_device() and its wrapper return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make VFIOIOMMUClass::attach_device() and its wrapper function vfio_attach_device() return bool. This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 6 ++---- hw/vfio/ccw.c | 6 ++---- hw/vfio/common.c | 4 ++-- hw/vfio/container.c | 14 +++++++------- hw/vfio/iommufd.c | 11 +++++------ hw/vfio/pci.c | 5 ++--- hw/vfio/platform.c | 7 +++---- include/hw/vfio/vfio-common.h | 4 ++-- include/hw/vfio/vfio-container-base.h | 4 ++-- 9 files changed, 27 insertions(+), 34 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 8bb024e2fd..ba653ef70f 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -154,7 +154,6 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, static void vfio_ap_realize(DeviceState *dev, Error **errp) { ERRP_GUARD(); - int ret; Error *err = NULL; VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); VFIODevice *vbasedev = &vapdev->vdev; @@ -163,9 +162,8 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) return; } - ret = vfio_attach_device(vbasedev->name, vbasedev, - &address_space_memory, errp); - if (ret) { + if (!vfio_attach_device(vbasedev->name, vbasedev, + &address_space_memory, errp)) { goto error; } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 1c630f6e9a..89bb980167 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -579,7 +579,6 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); VFIODevice *vbasedev = &vcdev->vdev; Error *err = NULL; - int ret; /* Call the class init function for subchannel. */ if (cdc->realize) { @@ -593,9 +592,8 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) return; } - ret = vfio_attach_device(cdev->mdevid, vbasedev, - &address_space_memory, errp); - if (ret) { + if (!vfio_attach_device(cdev->mdevid, vbasedev, + &address_space_memory, errp)) { goto out_attach_dev_err; } diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 1fbd10801d..c04a259ffd 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1523,8 +1523,8 @@ retry: return info; } -int vfio_attach_device(char *name, VFIODevice *vbasedev, - AddressSpace *as, Error **errp) +bool vfio_attach_device(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) { const VFIOIOMMUClass *ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 9534120d4a..e7c4167747 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -910,8 +910,8 @@ static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) * @name and @vbasedev->name are likely to be different depending * on the type of the device, hence the need for passing @name */ -static int vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, - AddressSpace *as, Error **errp) +static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) { int groupid = vfio_device_groupid(vbasedev, errp); VFIODevice *vbasedev_iter; @@ -920,27 +920,27 @@ static int vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, int ret; if (groupid < 0) { - return groupid; + return false; } trace_vfio_attach_device(vbasedev->name, groupid); group = vfio_get_group(groupid, as, errp); if (!group) { - return -ENOENT; + return false; } QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { error_setg(errp, "device is already attached"); vfio_put_group(group); - return -EBUSY; + return false; } } ret = vfio_get_device(group, name, vbasedev, errp); if (ret) { vfio_put_group(group); - return ret; + return false; } bcontainer = &group->container->bcontainer; @@ -948,7 +948,7 @@ static int vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next); QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); - return ret; + return true; } static void vfio_legacy_detach_device(VFIODevice *vbasedev) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index c644127972..4c6992fca1 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -299,8 +299,8 @@ error: return ret; } -static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, - AddressSpace *as, Error **errp) +static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) { VFIOContainerBase *bcontainer; VFIOIOMMUFDContainer *container; @@ -315,7 +315,7 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, if (vbasedev->fd < 0) { devfd = iommufd_cdev_getfd(vbasedev->sysfsdev, errp); if (devfd < 0) { - return devfd; + return false; } vbasedev->fd = devfd; } else { @@ -392,7 +392,6 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, memory_listener_register(&bcontainer->listener, bcontainer->space->as); if (bcontainer->error) { - ret = -1; error_propagate_prepend(errp, bcontainer->error, "memory listener initialization failed: "); goto err_listener_register; @@ -431,7 +430,7 @@ found_container: trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs, vbasedev->num_regions, vbasedev->flags); - return 0; + return true; err_listener_register: iommufd_cdev_ram_block_discard_disable(false); @@ -444,7 +443,7 @@ err_alloc_ioas: iommufd_cdev_unbind_and_disconnect(vbasedev); err_connect_bind: close(vbasedev->fd); - return ret; + return false; } static void iommufd_cdev_detach(VFIODevice *vbasedev) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 84f7bff664..c1adef5cf8 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3027,9 +3027,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) name = g_strdup(vbasedev->name); } - ret = vfio_attach_device(name, vbasedev, - pci_device_iommu_address_space(pdev), errp); - if (ret) { + if (!vfio_attach_device(name, vbasedev, + pci_device_iommu_address_space(pdev), errp)) { goto error; } diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index dcd2365fb3..2bd16096bb 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -552,10 +552,9 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) return ret; } - ret = vfio_attach_device(vbasedev->name, vbasedev, - &address_space_memory, errp); - if (ret) { - return ret; + if (!vfio_attach_device(vbasedev->name, vbasedev, + &address_space_memory, errp)) { + return -EINVAL; } ret = vfio_populate_device(vbasedev, errp); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 878e34a128..e85817e65e 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -222,8 +222,8 @@ void vfio_region_exit(VFIORegion *region); void vfio_region_finalize(VFIORegion *region); void vfio_reset_handler(void *opaque); struct vfio_device_info *vfio_get_device_info(int fd); -int vfio_attach_device(char *name, VFIODevice *vbasedev, - AddressSpace *as, Error **errp); +bool vfio_attach_device(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); void vfio_detach_device(VFIODevice *vbasedev); int vfio_kvm_device_add_fd(int fd, Error **errp); diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index b04057ad1a..44927ca8c3 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -117,8 +117,8 @@ struct VFIOIOMMUClass { int (*dma_unmap)(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb); - int (*attach_device)(const char *name, VFIODevice *vbasedev, - AddressSpace *as, Error **errp); + bool (*attach_device)(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); void (*detach_device)(VFIODevice *vbasedev); /* migration feature */ From 35b25cf40e4228e922cc9830bd61a7341623023c Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:45 +0800 Subject: [PATCH 0739/2884] vfio: Make VFIOIOMMUClass::setup() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 10 +++++----- hw/vfio/spapr.c | 12 +++++------- include/hw/vfio/vfio-container-base.h | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index e7c4167747..f2e9560a19 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -507,7 +507,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, } } -static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) +static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) { VFIOContainer *container = container_of(bcontainer, VFIOContainer, bcontainer); @@ -517,7 +517,7 @@ static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) ret = vfio_get_iommu_info(container, &info); if (ret) { error_setg_errno(errp, -ret, "Failed to get VFIO IOMMU info"); - return ret; + return false; } if (info->flags & VFIO_IOMMU_INFO_PGSIZES) { @@ -533,7 +533,7 @@ static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) vfio_get_info_iova_range(info, bcontainer); vfio_get_iommu_info_migration(container, info); - return 0; + return true; } static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, @@ -635,8 +635,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, assert(bcontainer->ops->setup); - ret = bcontainer->ops->setup(bcontainer, errp); - if (ret) { + if (!bcontainer->ops->setup(bcontainer, errp)) { + ret = -EINVAL; goto enable_discards_exit; } diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 0d949bb728..148b257c9c 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -458,8 +458,8 @@ static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) } } -static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer, - Error **errp) +static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, + Error **errp) { VFIOContainer *container = container_of(bcontainer, VFIOContainer, bcontainer); @@ -480,7 +480,7 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer, ret = ioctl(fd, VFIO_IOMMU_ENABLE); if (ret) { error_setg_errno(errp, errno, "failed to enable container"); - return -errno; + return false; } } else { scontainer->prereg_listener = vfio_prereg_listener; @@ -488,7 +488,6 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer, memory_listener_register(&scontainer->prereg_listener, &address_space_memory); if (bcontainer->error) { - ret = -1; error_propagate_prepend(errp, bcontainer->error, "RAM memory listener initialization failed: "); goto listener_unregister_exit; @@ -500,7 +499,6 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer, if (ret) { error_setg_errno(errp, errno, "VFIO_IOMMU_SPAPR_TCE_GET_INFO failed"); - ret = -errno; goto listener_unregister_exit; } @@ -527,13 +525,13 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer, 0x1000); } - return 0; + return true; listener_unregister_exit: if (v2) { memory_listener_unregister(&scontainer->prereg_listener); } - return ret; + return false; } static void vfio_iommu_spapr_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 44927ca8c3..202e23cb6b 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -110,7 +110,7 @@ struct VFIOIOMMUClass { InterfaceClass parent_class; /* basic feature */ - int (*setup)(VFIOContainerBase *bcontainer, Error **errp); + bool (*setup)(VFIOContainerBase *bcontainer, Error **errp); int (*dma_map)(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); From 33e4c22fd1dd2aa1e1e6f45e8e89c9b961fb72d1 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:46 +0800 Subject: [PATCH 0740/2884] vfio: Make VFIOIOMMUClass::add_window() and its wrapper return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make VFIOIOMMUClass::add_window() and its wrapper function vfio_container_add_section_window() return bool. This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 2 +- hw/vfio/container-base.c | 8 ++++---- hw/vfio/spapr.c | 16 ++++++++-------- include/hw/vfio/vfio-container-base.h | 12 ++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index c04a259ffd..f9619a1dfb 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -588,7 +588,7 @@ static void vfio_listener_region_add(MemoryListener *listener, return; } - if (vfio_container_add_section_window(bcontainer, section, &err)) { + if (!vfio_container_add_section_window(bcontainer, section, &err)) { goto fail; } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 26f4bb464a..760d9d0622 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -31,12 +31,12 @@ int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, return bcontainer->ops->dma_unmap(bcontainer, iova, size, iotlb); } -int vfio_container_add_section_window(VFIOContainerBase *bcontainer, - MemoryRegionSection *section, - Error **errp) +bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp) { if (!bcontainer->ops->add_window) { - return 0; + return true; } return bcontainer->ops->add_window(bcontainer, section, errp); diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 148b257c9c..47b040f1bc 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -323,7 +323,7 @@ static int vfio_spapr_create_window(VFIOContainer *container, return 0; } -static int +static bool vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section, Error **errp) @@ -351,13 +351,13 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, error_setg(errp, "Container %p can't map guest IOVA region" " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); - return -EINVAL; + return false; } - return 0; + return true; } if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { - return 0; + return true; } /* For now intersections are not allowed, we may relax this later */ @@ -373,14 +373,14 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, section->offset_within_address_space + int128_get64(section->size) - 1, hostwin->min_iova, hostwin->max_iova); - return -EINVAL; + return false; } } ret = vfio_spapr_create_window(container, section, &pgsize); if (ret) { error_setg_errno(errp, -ret, "Failed to create SPAPR window"); - return ret; + return false; } vfio_host_win_add(scontainer, section->offset_within_address_space, @@ -406,14 +406,14 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, "vfio: failed GROUP_SET_SPAPR_TCE for " "KVM VFIO device %d and group fd %d", param.tablefd, param.groupfd); - return -errno; + return false; } trace_vfio_spapr_group_attach(param.groupfd, param.tablefd); } } } #endif - return 0; + return true; } static void diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 202e23cb6b..2776481fc9 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -76,9 +76,9 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer, int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb); -int vfio_container_add_section_window(VFIOContainerBase *bcontainer, - MemoryRegionSection *section, - Error **errp); +bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp); void vfio_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section); int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, @@ -156,9 +156,9 @@ struct VFIOIOMMUClass { int (*pci_hot_reset)(VFIODevice *vbasedev, bool single); /* SPAPR specific */ - int (*add_window)(VFIOContainerBase *bcontainer, - MemoryRegionSection *section, - Error **errp); + bool (*add_window)(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp); void (*del_window)(VFIOContainerBase *bcontainer, MemoryRegionSection *section); void (*release)(VFIOContainerBase *bcontainer); From f6c12eaca52ca014ae1ec57ecee51c57187c2ef6 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:47 +0800 Subject: [PATCH 0741/2884] vfio/container: Make vfio_connect_container() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index f2e9560a19..802828ddff 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -536,8 +536,8 @@ static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) return true; } -static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, - Error **errp) +static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, + Error **errp) { VFIOContainer *container; VFIOContainerBase *bcontainer; @@ -589,19 +589,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, error_report("vfio: error disconnecting group %d from" " container", group->groupid); } - return ret; + return false; } group->container = container; QLIST_INSERT_HEAD(&container->group_list, group, container_next); vfio_kvm_device_add_group(group); - return 0; + return true; } } fd = qemu_open_old("/dev/vfio/vfio", O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio"); - ret = -errno; goto put_space_exit; } @@ -609,7 +608,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, if (ret != VFIO_API_VERSION) { error_setg(errp, "supported vfio version: %d, " "reported version: %d", VFIO_API_VERSION, ret); - ret = -EINVAL; goto close_fd_exit; } @@ -636,7 +634,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, assert(bcontainer->ops->setup); if (!bcontainer->ops->setup(bcontainer, errp)) { - ret = -EINVAL; goto enable_discards_exit; } @@ -652,7 +649,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, memory_listener_register(&bcontainer->listener, bcontainer->space->as); if (bcontainer->error) { - ret = -1; error_propagate_prepend(errp, bcontainer->error, "memory listener initialization failed: "); goto listener_release_exit; @@ -660,7 +656,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, bcontainer->initialized = true; - return 0; + return true; listener_release_exit: QLIST_REMOVE(group, container_next); QLIST_REMOVE(bcontainer, next); @@ -685,7 +681,7 @@ close_fd_exit: put_space_exit: vfio_put_address_space(space); - return ret; + return false; } static void vfio_disconnect_container(VFIOGroup *group) @@ -772,7 +768,7 @@ static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) group->groupid = groupid; QLIST_INIT(&group->device_list); - if (vfio_connect_container(group, as, errp)) { + if (!vfio_connect_container(group, as, errp)) { error_prepend(errp, "failed to setup container for group %d: ", groupid); goto close_fd_exit; From 534ed2e4725225e522bc5d69c219b574c9f164de Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:48 +0800 Subject: [PATCH 0742/2884] vfio/container: Make vfio_set_iommu() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 802828ddff..e330b28974 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -393,21 +393,20 @@ static const VFIOIOMMUClass *vfio_get_iommu_class(int iommu_type, Error **errp) return VFIO_IOMMU_CLASS(klass); } -static int vfio_set_iommu(VFIOContainer *container, int group_fd, - VFIOAddressSpace *space, Error **errp) +static bool vfio_set_iommu(VFIOContainer *container, int group_fd, + VFIOAddressSpace *space, Error **errp) { - int iommu_type, ret; + int iommu_type; const VFIOIOMMUClass *vioc; iommu_type = vfio_get_iommu_type(container, errp); if (iommu_type < 0) { - return iommu_type; + return false; } - ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd); - if (ret) { + if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { error_setg_errno(errp, errno, "Failed to set group container"); - return -errno; + return false; } while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) { @@ -422,7 +421,7 @@ static int vfio_set_iommu(VFIOContainer *container, int group_fd, continue; } error_setg_errno(errp, errno, "Failed to set iommu for container"); - return -errno; + return false; } container->iommu_type = iommu_type; @@ -430,11 +429,11 @@ static int vfio_set_iommu(VFIOContainer *container, int group_fd, vioc = vfio_get_iommu_class(iommu_type, errp); if (!vioc) { error_setg(errp, "No available IOMMU models"); - return -EINVAL; + return false; } vfio_container_init(&container->bcontainer, space, vioc); - return 0; + return true; } static int vfio_get_iommu_info(VFIOContainer *container, @@ -615,8 +614,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->fd = fd; bcontainer = &container->bcontainer; - ret = vfio_set_iommu(container, group->fd, space, errp); - if (ret) { + if (!vfio_set_iommu(container, group->fd, space, errp)) { goto free_container_exit; } From be1ff306bb31fbecfe3593a2a37493407cee87d8 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:49 +0800 Subject: [PATCH 0743/2884] vfio/container: Make vfio_get_device() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index e330b28974..53649e397c 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -802,8 +802,8 @@ static void vfio_put_group(VFIOGroup *group) g_free(group); } -static int vfio_get_device(VFIOGroup *group, const char *name, - VFIODevice *vbasedev, Error **errp) +static bool vfio_get_device(VFIOGroup *group, const char *name, + VFIODevice *vbasedev, Error **errp) { g_autofree struct vfio_device_info *info = NULL; int fd; @@ -815,14 +815,14 @@ static int vfio_get_device(VFIOGroup *group, const char *name, error_append_hint(errp, "Verify all devices in group %d are bound to vfio- " "or pci-stub and not already in use\n", group->groupid); - return fd; + return false; } info = vfio_get_device_info(fd); if (!info) { error_setg_errno(errp, errno, "error getting device info"); close(fd); - return -1; + return false; } /* @@ -837,7 +837,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, error_setg(errp, "Inconsistent setting of support for discarding " "RAM (e.g., balloon) within group"); close(fd); - return -1; + return false; } if (!group->ram_block_discard_allowed) { @@ -858,7 +858,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET); - return 0; + return true; } static void vfio_put_base_device(VFIODevice *vbasedev) @@ -911,7 +911,6 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, VFIODevice *vbasedev_iter; VFIOGroup *group; VFIOContainerBase *bcontainer; - int ret; if (groupid < 0) { return false; @@ -931,8 +930,7 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, return false; } } - ret = vfio_get_device(group, name, vbasedev, errp); - if (ret) { + if (!vfio_get_device(group, name, vbasedev, errp)) { vfio_put_group(group); return false; } From 45d0d8c4044d330225ef684894e2cdcc05ec0823 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:50 +0800 Subject: [PATCH 0744/2884] vfio/iommufd: Make iommufd_cdev_*() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. The changed functions include: iommufd_cdev_kvm_device_add iommufd_cdev_connect_and_bind iommufd_cdev_attach_ioas_hwpt iommufd_cdev_detach_ioas_hwpt iommufd_cdev_attach_container iommufd_cdev_get_info_iova_range After the change, all functions in hw/vfio/iommufd.c follows the standand. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 88 +++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 4c6992fca1..84c86b970e 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -49,9 +49,9 @@ static int iommufd_cdev_unmap(const VFIOContainerBase *bcontainer, container->ioas_id, iova, size); } -static int iommufd_cdev_kvm_device_add(VFIODevice *vbasedev, Error **errp) +static bool iommufd_cdev_kvm_device_add(VFIODevice *vbasedev, Error **errp) { - return vfio_kvm_device_add_fd(vbasedev->fd, errp); + return !vfio_kvm_device_add_fd(vbasedev->fd, errp); } static void iommufd_cdev_kvm_device_del(VFIODevice *vbasedev) @@ -63,18 +63,16 @@ static void iommufd_cdev_kvm_device_del(VFIODevice *vbasedev) } } -static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) +static bool iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) { IOMMUFDBackend *iommufd = vbasedev->iommufd; struct vfio_device_bind_iommufd bind = { .argsz = sizeof(bind), .flags = 0, }; - int ret; - ret = iommufd_backend_connect(iommufd, errp); - if (ret) { - return ret; + if (iommufd_backend_connect(iommufd, errp)) { + return false; } /* @@ -82,15 +80,13 @@ static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) * in KVM. Especially for some emulated devices, it requires * to have kvm information in the device open. */ - ret = iommufd_cdev_kvm_device_add(vbasedev, errp); - if (ret) { + if (!iommufd_cdev_kvm_device_add(vbasedev, errp)) { goto err_kvm_device_add; } /* Bind device to iommufd */ bind.iommufd = iommufd->fd; - ret = ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind); - if (ret) { + if (ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind)) { error_setg_errno(errp, errno, "error bind device fd=%d to iommufd=%d", vbasedev->fd, bind.iommufd); goto err_bind; @@ -99,12 +95,12 @@ static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) vbasedev->devid = bind.out_devid; trace_iommufd_cdev_connect_and_bind(bind.iommufd, vbasedev->name, vbasedev->fd, vbasedev->devid); - return ret; + return true; err_bind: iommufd_cdev_kvm_device_del(vbasedev); err_kvm_device_add: iommufd_backend_disconnect(iommufd); - return ret; + return false; } static void iommufd_cdev_unbind_and_disconnect(VFIODevice *vbasedev) @@ -176,10 +172,10 @@ out: return ret; } -static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, +static bool iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, Error **errp) { - int ret, iommufd = vbasedev->iommufd->fd; + int iommufd = vbasedev->iommufd->fd; struct vfio_device_attach_iommufd_pt attach_data = { .argsz = sizeof(attach_data), .flags = 0, @@ -187,38 +183,38 @@ static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, }; /* Attach device to an IOAS or hwpt within iommufd */ - ret = ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data); - if (ret) { + if (ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data)) { error_setg_errno(errp, errno, "[iommufd=%d] error attach %s (%d) to id=%d", iommufd, vbasedev->name, vbasedev->fd, id); - } else { - trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name, - vbasedev->fd, id); + return false; } - return ret; + + trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name, + vbasedev->fd, id); + return true; } -static int iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) +static bool iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) { - int ret, iommufd = vbasedev->iommufd->fd; + int iommufd = vbasedev->iommufd->fd; struct vfio_device_detach_iommufd_pt detach_data = { .argsz = sizeof(detach_data), .flags = 0, }; - ret = ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data); - if (ret) { + if (ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data)) { error_setg_errno(errp, errno, "detach %s failed", vbasedev->name); - } else { - trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name); + return false; } - return ret; + + trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name); + return true; } -static int iommufd_cdev_attach_container(VFIODevice *vbasedev, - VFIOIOMMUFDContainer *container, - Error **errp) +static bool iommufd_cdev_attach_container(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container, + Error **errp) { return iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); } @@ -228,7 +224,7 @@ static void iommufd_cdev_detach_container(VFIODevice *vbasedev, { Error *err = NULL; - if (iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) { + if (!iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) { error_report_err(err); } } @@ -254,20 +250,19 @@ static int iommufd_cdev_ram_block_discard_disable(bool state) return ram_block_uncoordinated_discard_disable(state); } -static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, - uint32_t ioas_id, Error **errp) +static bool iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, + uint32_t ioas_id, Error **errp) { VFIOContainerBase *bcontainer = &container->bcontainer; g_autofree struct iommu_ioas_iova_ranges *info = NULL; struct iommu_iova_range *iova_ranges; - int ret, sz, fd = container->be->fd; + int sz, fd = container->be->fd; info = g_malloc0(sizeof(*info)); info->size = sizeof(*info); info->ioas_id = ioas_id; - ret = ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info); - if (ret && errno != EMSGSIZE) { + if (ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info) && errno != EMSGSIZE) { goto error; } @@ -275,8 +270,7 @@ static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, info = g_realloc(info, sizeof(*info) + sz); info->allowed_iovas = (uintptr_t)(info + 1); - ret = ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info); - if (ret) { + if (ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info)) { goto error; } @@ -291,12 +285,11 @@ static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, } bcontainer->pgsizes = info->out_iova_alignment; - return 0; + return true; error: - ret = -errno; error_setg_errno(errp, errno, "Cannot get IOVA ranges"); - return ret; + return false; } static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, @@ -322,8 +315,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, devfd = vbasedev->fd; } - ret = iommufd_cdev_connect_and_bind(vbasedev, errp); - if (ret) { + if (!iommufd_cdev_connect_and_bind(vbasedev, errp)) { goto err_connect_bind; } @@ -336,7 +328,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, vbasedev->iommufd != container->be) { continue; } - if (iommufd_cdev_attach_container(vbasedev, container, &err)) { + if (!iommufd_cdev_attach_container(vbasedev, container, &err)) { const char *msg = error_get_pretty(err); trace_iommufd_cdev_fail_attach_existing_container(msg); @@ -369,8 +361,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, vfio_container_init(bcontainer, space, iommufd_vioc); QLIST_INSERT_HEAD(&space->containers, bcontainer, next); - ret = iommufd_cdev_attach_container(vbasedev, container, errp); - if (ret) { + if (!iommufd_cdev_attach_container(vbasedev, container, errp)) { goto err_attach_container; } @@ -379,8 +370,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_discard_disable; } - ret = iommufd_cdev_get_info_iova_range(container, ioas_id, &err); - if (ret) { + if (!iommufd_cdev_get_info_iova_range(container, ioas_id, &err)) { error_append_hint(&err, "Fallback to default 64bit IOVA range and 4K page size\n"); warn_report_err(err); From f38f5dd1d454e41def9cdba899d0b059dd8855d9 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:51 +0800 Subject: [PATCH 0745/2884] vfio/cpr: Make vfio_cpr_register_container() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 3 +-- hw/vfio/cpr.c | 4 ++-- hw/vfio/iommufd.c | 3 +-- include/hw/vfio/vfio-common.h | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 53649e397c..096cc97258 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -618,8 +618,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, goto free_container_exit; } - ret = vfio_cpr_register_container(bcontainer, errp); - if (ret) { + if (!vfio_cpr_register_container(bcontainer, errp)) { goto free_container_exit; } diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 392c2dd95d..87e51fcee1 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -25,12 +25,12 @@ static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, return 0; } -int vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp) +bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp) { migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, vfio_cpr_reboot_notifier, MIG_MODE_CPR_REBOOT); - return 0; + return true; } void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 84c86b970e..6a446b16dc 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -396,8 +396,7 @@ found_container: goto err_listener_register; } - ret = vfio_cpr_register_container(bcontainer, errp); - if (ret) { + if (!vfio_cpr_register_container(bcontainer, errp)) { goto err_listener_register; } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index e85817e65e..b7bb4f5304 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -229,7 +229,7 @@ void vfio_detach_device(VFIODevice *vbasedev); int vfio_kvm_device_add_fd(int fd, Error **errp); int vfio_kvm_device_del_fd(int fd, Error **errp); -int vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); +bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); extern const MemoryRegionOps vfio_region_ops; From 9067d50dff29b1b96ef0d4ab7448dbd7b636e55c Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Tue, 7 May 2024 14:42:52 +0800 Subject: [PATCH 0746/2884] backends/iommufd: Make iommufd_backend_*() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand to return bool if 'Error **' is used to pass error. The changed functions include: iommufd_backend_connect iommufd_backend_alloc_ioas By this chance, simplify the functions a bit by avoiding duplicate recordings, e.g., log through either error interface or trace, not both. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 29 +++++++++++++---------------- backends/trace-events | 4 ++-- hw/vfio/iommufd.c | 5 ++--- include/sysemu/iommufd.h | 6 +++--- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index 76a0204852..c506afbdac 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -72,24 +72,22 @@ static void iommufd_backend_class_init(ObjectClass *oc, void *data) object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd); } -int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) +bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) { - int fd, ret = 0; + int fd; if (be->owned && !be->users) { fd = qemu_open_old("/dev/iommu", O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "/dev/iommu opening failed"); - ret = fd; - goto out; + return false; } be->fd = fd; } be->users++; -out: - trace_iommufd_backend_connect(be->fd, be->owned, - be->users, ret); - return ret; + + trace_iommufd_backend_connect(be->fd, be->owned, be->users); + return true; } void iommufd_backend_disconnect(IOMMUFDBackend *be) @@ -106,25 +104,24 @@ out: trace_iommufd_backend_disconnect(be->fd, be->users); } -int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, - Error **errp) +bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, + Error **errp) { - int ret, fd = be->fd; + int fd = be->fd; struct iommu_ioas_alloc alloc_data = { .size = sizeof(alloc_data), .flags = 0, }; - ret = ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data); - if (ret) { + if (ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data)) { error_setg_errno(errp, errno, "Failed to allocate ioas"); - return ret; + return false; } *ioas_id = alloc_data.out_ioas_id; - trace_iommufd_backend_alloc_ioas(fd, *ioas_id, ret); + trace_iommufd_backend_alloc_ioas(fd, *ioas_id); - return ret; + return true; } void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id) diff --git a/backends/trace-events b/backends/trace-events index d45c6e31a6..211e6f374a 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -7,11 +7,11 @@ dbus_vmstate_loading(const char *id) "id: %s" dbus_vmstate_saving(const char *id) "id: %s" # iommufd.c -iommufd_backend_connect(int fd, bool owned, uint32_t users, int ret) "fd=%d owned=%d users=%d (%d)" +iommufd_backend_connect(int fd, bool owned, uint32_t users) "fd=%d owned=%d users=%d" iommufd_backend_disconnect(int fd, uint32_t users) "fd=%d users=%d" iommu_backend_set_fd(int fd) "pre-opened /dev/iommu fd=%d" iommufd_backend_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, void *vaddr, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" addr=%p readonly=%d (%d)" iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" -iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas, int ret) " iommufd=%d ioas=%d (%d)" +iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d" iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 6a446b16dc..554f9a6292 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -71,7 +71,7 @@ static bool iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) .flags = 0, }; - if (iommufd_backend_connect(iommufd, errp)) { + if (!iommufd_backend_connect(iommufd, errp)) { return false; } @@ -346,8 +346,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, } /* Need to allocate a new dedicated container */ - ret = iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp); - if (ret < 0) { + if (!iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp)) { goto err_alloc_ioas; } diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index 9af27ebd6c..293bfbe967 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -23,11 +23,11 @@ struct IOMMUFDBackend { /*< public >*/ }; -int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); +bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); void iommufd_backend_disconnect(IOMMUFDBackend *be); -int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, - Error **errp); +bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, + Error **errp); void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id); int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); From 4d8698e692cca96bbf20505a90e42d10141d2b94 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 May 2024 10:40:55 +0200 Subject: [PATCH 0747/2884] tests/lcitool/refresh: Treat the output of lcitool as text, not as bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case lcitool fails (e.g. with a python backtrace), this makes the output of lcitool much more readable. Suggested-by: Daniel P. Berrangé Message-ID: <20240516084059.511463-2-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/lcitool/refresh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 24a735a3f2..174818d9c9 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -43,12 +43,12 @@ def atomic_write(filename, content): def generate(filename, cmd, trailer): print("Generate %s" % filename) - lcitool = subprocess.run(cmd, capture_output=True) + lcitool = subprocess.run(cmd, capture_output=True, encoding='utf8') if lcitool.returncode != 0: raise Exception("Failed to generate %s: %s" % (filename, lcitool.stderr)) - content = lcitool.stdout.decode("utf8") + content = lcitool.stdout if trailer is not None: content += trailer atomic_write(filename, content) From 9ebe09e633773bebd2dc52b151fb48c1b9b0542a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 May 2024 10:40:56 +0200 Subject: [PATCH 0748/2884] tests/lcitool: Remove 'xfsprogs' from QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU's commit a5730b8bd3 ("block/file-posix: Simplify the XFS_IOC_DIOINFO handling") removed the need for the 'xfsprogs' package. Signed-off-by: Philippe Mathieu-Daudé [thuth: Adjusted the patch from the lcitools repo to QEMU's repo] Message-ID: <20240516084059.511463-3-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/lcitool/projects/qemu.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 149b15de57..9173d1e36e 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -121,7 +121,6 @@ packages: - vte - which - xen - - xfsprogs - xorriso - zstdtools - zlib From fd77b25bbde962395b93335ade09b6d3127a6537 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 May 2024 10:40:57 +0200 Subject: [PATCH 0749/2884] tests/lcitool: Remove g++ from the containers (except for the MinGW one) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need C++ for the normal QEMU builds anymore, so installing g++ in each and every container seems to be a waste of time and disk space. The only container that still needs it is the Fedora MinGW container that builds the only remaining C++ code in ./qga/vss-win32/ and we can install it there with an extra project yml file instead. Message-ID: <20240516084059.511463-4-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/lcitool/projects/qemu-minimal.yml | 1 - tests/lcitool/projects/qemu-win-installer.yml | 4 ++++ tests/lcitool/projects/qemu.yml | 1 - tests/lcitool/refresh | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/lcitool/projects/qemu-win-installer.yml diff --git a/tests/lcitool/projects/qemu-minimal.yml b/tests/lcitool/projects/qemu-minimal.yml index d44737dc1d..6bc232a1c3 100644 --- a/tests/lcitool/projects/qemu-minimal.yml +++ b/tests/lcitool/projects/qemu-minimal.yml @@ -7,7 +7,6 @@ packages: - ccache - findutils - flex - - g++ - gcc - gcc-native - glib2 diff --git a/tests/lcitool/projects/qemu-win-installer.yml b/tests/lcitool/projects/qemu-win-installer.yml new file mode 100644 index 0000000000..86aa22297c --- /dev/null +++ b/tests/lcitool/projects/qemu-win-installer.yml @@ -0,0 +1,4 @@ +# Additional packages that are required to build the code in qga/vss-win32/ +--- +packages: + - g++ diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 9173d1e36e..b63b6bd850 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -22,7 +22,6 @@ packages: - findutils - flex - fuse3 - - g++ - gcc - gcc-native - gcovr diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 174818d9c9..789acefb75 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -192,6 +192,7 @@ try: "s390x-softmmu,s390x-linux-user")) generate_dockerfile("fedora-win64-cross", "fedora-38", + project='qemu,qemu-win-installer', cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", "x86_64-softmmu")) From d2f213cc069d3013b3204993d957a6eff59c220c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 May 2024 10:40:58 +0200 Subject: [PATCH 0750/2884] tests/lcitool/projects/qemu.yml: Sort entries alphabetically again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's try to keep the entries in alphabetical order here! Message-ID: <20240516084059.511463-5-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/lcitool/projects/qemu.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index b63b6bd850..7511ec7ccb 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -35,8 +35,8 @@ packages: - hostname - json-c - libaio - - libattr - libasan + - libattr - libbpf - libc-static - libcacard @@ -54,6 +54,7 @@ packages: - libjpeg - libnfs - libnuma + - libpipewire-dev - libpmem - libpng - librbd @@ -73,27 +74,26 @@ packages: - llvm - lttng-ust - lzo + - make + - mesa-libgbm + - meson - mtools + - ncursesw - netcat - nettle - ninja - nsis - - make - - mesa-libgbm - - meson - - ncursesw - pam - pcre-static - pixman - - libpipewire-dev - pkg-config - pulseaudio - python3 - - python3-PyYAML - python3-numpy - python3-opencv - python3-pillow - python3-pip + - python3-PyYAML - python3-sphinx - python3-sphinx-rtd-theme - python3-sqlite3 @@ -121,6 +121,6 @@ packages: - which - xen - xorriso - - zstdtools - zlib - zlib-static + - zstdtools From e4b6adae7aa7ab440e4dd98de3c1c532f48eda60 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 16 May 2024 10:40:59 +0200 Subject: [PATCH 0751/2884] tests/docker/dockerfiles: Update container files with "lcitool-refresh" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run "make lcitool-refresh" after the previous changes to the lcitool files. This removes the g++ and xfslibs-dev packages from the dockerfiles (except for the fedora-win64-cross dockerfile where we keep the C++ compiler). Message-ID: <20240516084059.511463-6-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/docker/dockerfiles/alpine.docker | 4 ---- tests/docker/dockerfiles/centos9.docker | 4 ---- tests/docker/dockerfiles/debian-amd64-cross.docker | 4 ---- tests/docker/dockerfiles/debian-arm64-cross.docker | 4 ---- tests/docker/dockerfiles/debian-armel-cross.docker | 4 ---- tests/docker/dockerfiles/debian-armhf-cross.docker | 4 ---- tests/docker/dockerfiles/debian-i686-cross.docker | 4 ---- tests/docker/dockerfiles/debian-mips64el-cross.docker | 4 ---- tests/docker/dockerfiles/debian-mipsel-cross.docker | 4 ---- tests/docker/dockerfiles/debian-ppc64el-cross.docker | 4 ---- tests/docker/dockerfiles/debian-riscv64-cross.docker | 3 --- tests/docker/dockerfiles/debian-s390x-cross.docker | 4 ---- tests/docker/dockerfiles/debian.docker | 4 ---- tests/docker/dockerfiles/fedora-win64-cross.docker | 2 +- tests/docker/dockerfiles/fedora.docker | 4 ---- tests/docker/dockerfiles/opensuse-leap.docker | 4 ---- tests/docker/dockerfiles/ubuntu2204.docker | 4 ---- 17 files changed, 1 insertion(+), 64 deletions(-) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index cd9d7af1ce..554464f31e 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -32,7 +32,6 @@ RUN apk update && \ findutils \ flex \ fuse3-dev \ - g++ \ gcc \ gcovr \ gettext \ @@ -110,7 +109,6 @@ RUN apk update && \ vte3-dev \ which \ xen-dev \ - xfsprogs-dev \ xorriso \ zlib-dev \ zlib-static \ @@ -119,10 +117,8 @@ RUN apk update && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ apk list --installed | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index 6cf47ce786..0256865b9e 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -34,7 +34,6 @@ RUN dnf distro-sync -y && \ flex \ fuse3-devel \ gcc \ - gcc-c++ \ gettext \ git \ glib2-devel \ @@ -115,7 +114,6 @@ RUN dnf distro-sync -y && \ util-linux \ vte291-devel \ which \ - xfsprogs-devel \ xorriso \ zlib-devel \ zlib-static \ @@ -125,10 +123,8 @@ RUN dnf distro-sync -y && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index d0b0e9778e..f8c61d1191 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-x86-64-linux-gnu \ gcc-x86-64-linux-gnu \ libaio-dev:amd64 \ libasan6:amd64 \ @@ -149,7 +148,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:amd64 \ nettle-dev:amd64 \ systemtap-sdt-dev:amd64 \ - xfslibs-dev:amd64 \ zlib1g-dev:amd64 && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -167,9 +165,7 @@ cpu = 'x86_64'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/x86_64-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-gcc ENV ABI "x86_64-linux-gnu" diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 8cb225740e..6510872279 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-aarch64-linux-gnu \ gcc-aarch64-linux-gnu \ libaio-dev:arm64 \ libasan6:arm64 \ @@ -148,7 +147,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:arm64 \ nettle-dev:arm64 \ systemtap-sdt-dev:arm64 \ - xfslibs-dev:arm64 \ zlib1g-dev:arm64 && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -166,9 +164,7 @@ cpu = 'aarch64'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/aarch64-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-gcc ENV ABI "aarch64-linux-gnu" diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index e6f37418ed..f227d42987 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-arm-linux-gnueabi \ gcc-arm-linux-gnueabi \ libaio-dev:armel \ libasan6:armel \ @@ -149,7 +148,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:armel \ nettle-dev:armel \ systemtap-sdt-dev:armel \ - xfslibs-dev:armel \ zlib1g-dev:armel && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -167,9 +165,7 @@ cpu = 'arm'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabi && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-gcc ENV ABI "arm-linux-gnueabi" diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 407a014f57..58bdf07223 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-arm-linux-gnueabihf \ gcc-arm-linux-gnueabihf \ libaio-dev:armhf \ libasan6:armhf \ @@ -148,7 +147,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:armhf \ nettle-dev:armhf \ systemtap-sdt-dev:armhf \ - xfslibs-dev:armhf \ zlib1g-dev:armhf && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -166,9 +164,7 @@ cpu = 'armhf'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabihf && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-gcc ENV ABI "arm-linux-gnueabihf" diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index bdc9566b67..9f4102be8f 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-i686-linux-gnu \ gcc-i686-linux-gnu \ libaio-dev:i386 \ libasan6:i386 \ @@ -149,7 +148,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:i386 \ nettle-dev:i386 \ systemtap-sdt-dev:i386 \ - xfslibs-dev:i386 \ zlib1g-dev:i386 && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -167,9 +165,7 @@ cpu = 'i686'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/i686-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-gcc ENV ABI "i686-linux-gnu" diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 4d995d0b12..c861c3bd5b 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-mips64el-linux-gnuabi64 \ gcc-mips64el-linux-gnuabi64 \ libaio-dev:mips64el \ libasound2-dev:mips64el \ @@ -147,7 +146,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:mips64el \ nettle-dev:mips64el \ systemtap-sdt-dev:mips64el \ - xfslibs-dev:mips64el \ zlib1g-dev:mips64el && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -165,9 +163,7 @@ cpu = 'mips64el'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/mips64el-linux-gnuabi64 && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-gcc ENV ABI "mips64el-linux-gnuabi64" diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 0cf803bda5..fe9415395e 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-mipsel-linux-gnu \ gcc-mipsel-linux-gnu \ libaio-dev:mipsel \ libasound2-dev:mipsel \ @@ -147,7 +146,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:mipsel \ nettle-dev:mipsel \ systemtap-sdt-dev:mipsel \ - xfslibs-dev:mipsel \ zlib1g-dev:mipsel && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -165,9 +163,7 @@ cpu = 'mipsel'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/mipsel-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-gcc ENV ABI "mipsel-linux-gnu" diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 6180ec08c3..35c8ff0864 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-powerpc64le-linux-gnu \ gcc-powerpc64le-linux-gnu \ libaio-dev:ppc64el \ libasan6:ppc64el \ @@ -147,7 +146,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:ppc64el \ nettle-dev:ppc64el \ systemtap-sdt-dev:ppc64el \ - xfslibs-dev:ppc64el \ zlib1g-dev:ppc64el && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -165,9 +163,7 @@ cpu = 'powerpc64le'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/powerpc64le-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-gcc ENV ABI "powerpc64le-linux-gnu" diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 591572ae94..4d8ca83cb3 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -51,7 +51,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-riscv64-linux-gnu \ gcc-riscv64-linux-gnu \ libc6-dev:riscv64 \ libfdt-dev:riscv64 \ @@ -74,9 +73,7 @@ cpu = 'riscv64'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/riscv64-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-gcc ENV ABI "riscv64-linux-gnu" diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 90c8d3c7b8..bef9dff17a 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-s390x-linux-gnu \ gcc-s390x-linux-gnu \ libaio-dev:s390x \ libasan6:s390x \ @@ -146,7 +145,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libzstd-dev:s390x \ nettle-dev:s390x \ systemtap-sdt-dev:s390x \ - xfslibs-dev:s390x \ zlib1g-dev:s390x && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ @@ -164,9 +162,7 @@ cpu = 's390x'\n\ endian = 'big'\n" > /usr/local/share/meson/cross/s390x-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-gcc ENV ABI "s390x-linux-gnu" diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 5722482e4c..63d7aac616 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -25,7 +25,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ - g++ \ gcc \ gcovr \ gettext \ @@ -129,7 +128,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - xfslibs-dev \ xorriso \ zlib1g-dev \ zstd && \ @@ -140,10 +138,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index d1a480fa71..0f78711876 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 qemu +# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 qemu,qemu-win-installer # # https://gitlab.com/libvirt/libvirt-ci diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 7e6ab0308a..098c894d10 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -41,7 +41,6 @@ exec "$@"\n' > /usr/bin/nosync && \ flex \ fuse3-devel \ gcc \ - gcc-c++ \ gcovr \ gettext \ git \ @@ -130,7 +129,6 @@ exec "$@"\n' > /usr/bin/nosync && \ vte291-devel \ which \ xen-devel \ - xfsprogs-devel \ xorriso \ zlib-devel \ zlib-static \ @@ -140,10 +138,8 @@ exec "$@"\n' > /usr/bin/nosync && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index c4055bdd10..836f531ac1 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -26,7 +26,6 @@ RUN zypper update -y && \ flex \ fuse3-devel \ gcc \ - gcc-c++ \ gcovr \ gettext-runtime \ git \ @@ -113,7 +112,6 @@ RUN zypper update -y && \ vte-devel \ which \ xen-devel \ - xfsprogs-devel \ xorriso \ zlib-devel \ zlib-devel-static \ @@ -122,10 +120,8 @@ RUN zypper update -y && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc RUN /usr/bin/pip3.11 install \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index b8e78331db..febd25b320 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -25,7 +25,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ - g++ \ gcc \ gcovr \ gettext \ @@ -129,7 +128,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - xfslibs-dev \ xorriso \ zlib1g-dev \ zstd && \ @@ -140,10 +138,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" From bebe9603fcb072dcdb7fb22005781b3582a4d701 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 17 May 2024 08:15:53 +0200 Subject: [PATCH 0752/2884] hw/intc/s390_flic: Fix crash that occurs when saving the machine state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adapter_info_so_needed() treats its "opaque" parameter as a S390FLICState, but the function belongs to a VMStateDescription that is attached to a TYPE_VIRTIO_CCW_BUS device. This is currently causing a crash when the user tries to save or migrate the VM state. Fix it by using s390_get_flic() to get the correct device here instead. Reported-by: Marc Hartmayer Fixes: 9d1b0f5bf5 ("s390_flic: add migration-enabled property") Message-ID: <20240517061553.564529-1-thuth@redhat.com> Reviewed-by: Cédric Le Goater Tested-by: Marc Hartmayer Signed-off-by: Thomas Huth --- hw/intc/s390_flic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 7f93080087..6771645699 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -459,7 +459,7 @@ type_init(qemu_s390_flic_register_types) static bool adapter_info_so_needed(void *opaque) { - S390FLICState *fs = S390_FLIC_COMMON(opaque); + S390FLICState *fs = s390_get_flic(); return fs->migration_enabled; } From 3dba3c0b2501e6afa9f722608be6a343465895da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 15 May 2024 12:05:20 +0200 Subject: [PATCH 0753/2884] ui/console: Only declare variable fence_fd when CONFIG_GBM is defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This to avoid a build breakage : ../ui/gtk-egl.c: In function ‘gd_egl_draw’: ../ui/gtk-egl.c:73:9: error: unused variable ‘fence_fd’ [-Werror=unused-variable] 73 | int fence_fd; | ^~~~~~~~ Fixes: fa6426805b12 ("ui/console: Use qemu_dmabuf_set_..() helpers instead") Cc: Dongwon Kim Cc: Marc-André Lureau Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240515100520.574383-1-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- ui/gtk-egl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 0473f689c9..9831c10e1b 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -68,9 +68,9 @@ void gd_egl_draw(VirtualConsole *vc) GdkWindow *window; #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; + int fence_fd; #endif int ww, wh, ws; - int fence_fd; if (!vc->gfx.gls) { return; From 2563be6317fa9b5e661d79581538c704ecb90a1a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 May 2024 14:12:37 +0200 Subject: [PATCH 0754/2884] hw/pflash: fix block write start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the pflash_blk_write_start() call. We need the offset of the first data write, not the offset for the setup (number-of-bytes) write. Without this fix u-boot can do block writes to the first flash block only. While being at it drop a leftover FIXME. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2343 Fixes: 284a7ee2e290 ("hw/pflash: implement update buffer for block writes") Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240516121237.534875-1-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/block/pflash_cfi01.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 1bda8424b9..c8f1cf5a87 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -518,10 +518,6 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, break; case 0xe8: /* Write to buffer */ trace_pflash_write(pfl->name, "write to buffer"); - /* FIXME should save @offset, @width for case 1+ */ - qemu_log_mask(LOG_UNIMP, - "%s: Write to buffer emulation is flawed\n", - __func__); pfl->status |= 0x80; /* Ready! */ break; case 0xf0: /* Probe for AMD flash */ @@ -574,7 +570,6 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, } pfl->counter = value; pfl->wcycle++; - pflash_blk_write_start(pfl, offset); break; case 0x60: if (cmd == 0xd0) { @@ -605,6 +600,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, switch (pfl->cmd) { case 0xe8: /* Block write */ /* FIXME check @offset, @width */ + if (pfl->blk_offset == -1 && pfl->counter) { + pflash_blk_write_start(pfl, offset); + } if (!pfl->ro && (pfl->blk_offset != -1)) { pflash_data_write(pfl, offset, value, width, be); } else { From 9d7950edb0cdf8f4e5746e220e6e8a9e713bad16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 13 May 2024 13:33:57 +0100 Subject: [PATCH 0755/2884] hw/core: allow parameter=1 for SMP topology on any machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This effectively reverts commit 54c4ea8f3ae614054079395842128a856a73dbf9 Author: Zhao Liu Date: Sat Mar 9 00:01:37 2024 +0800 hw/core/machine-smp: Deprecate unsupported "parameter=1" SMP configurations but is not done as a 'git revert' since the part of the changes to the file hw/core/machine-smp.c which add 'has_XXX' checks remain desirable. Furthermore, we have to tweak the subsequently added unit test to account for differing warning message. The rationale for the original deprecation was: "Currently, it was allowed for users to specify the unsupported topology parameter as "1". For example, x86 PC machine doesn't support drawer/book/cluster topology levels, but user could specify "-smp drawers=1,books=1,clusters=1". This is meaningless and confusing, so that the support for this kind of configurations is marked deprecated since 9.0." There are varying POVs on the topic of 'unsupported' topology levels. It is common to say that on a system without hyperthreading, that there is always 1 thread. Likewise when new CPUs introduced a concept of multiple "dies', it was reasonable to say that all historical CPUs before that implicitly had 1 'die'. Likewise for the more recently introduced 'modules' and 'clusters' parameter'. From this POV, it is valid to set 'parameter=1' on the -smp command line for any machine, only a value > 1 is strictly an error condition. It doesn't cause any functional difficulty for QEMU, because internally the QEMU code is itself assuming that all "unsupported" parameters implicitly have a value of '1'. At the libvirt level, we've allowed applications to set 'parameter=1' when configuring a guest, and pass that through to QEMU. Deprecating this creates extra difficulty for because there's no info exposed from QEMU about which machine types "support" which parameters. Thus, libvirt can't know whether it is valid to pass 'parameter=1' for a given machine type, or whether it will trigger deprecation messages. Since there's no apparent functional benefit to deleting this deprecated behaviour from QEMU, and it creates problems for consumers of QEMU, remove this deprecation. Signed-off-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Ján Tomko Message-ID: <20240513123358.612355-2-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine-smp.c | 84 ++++++++++++------------------------- tests/unit/test-smp-parse.c | 8 ++-- 2 files changed, 31 insertions(+), 61 deletions(-) diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 2b93fa99c9..5d8d7edcbd 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -118,76 +118,46 @@ void machine_parse_smp_config(MachineState *ms, } /* - * If not supported by the machine, a topology parameter must be - * omitted. + * If not supported by the machine, a topology parameter must + * not be set to a value greater than 1. */ - if (!mc->smp_props.modules_supported && config->has_modules) { - if (config->modules > 1) { - error_setg(errp, "modules not supported by this " - "machine's CPU topology"); - return; - } else { - /* Here modules only equals 1 since we've checked zero case. */ - warn_report("Deprecated CPU topology (considered invalid): " - "Unsupported modules parameter mustn't be " - "specified as 1"); - } + if (!mc->smp_props.modules_supported && + config->has_modules && config->modules > 1) { + error_setg(errp, + "modules > 1 not supported by this machine's CPU topology"); + return; } modules = modules > 0 ? modules : 1; - if (!mc->smp_props.clusters_supported && config->has_clusters) { - if (config->clusters > 1) { - error_setg(errp, "clusters not supported by this " - "machine's CPU topology"); - return; - } else { - /* Here clusters only equals 1 since we've checked zero case. */ - warn_report("Deprecated CPU topology (considered invalid): " - "Unsupported clusters parameter mustn't be " - "specified as 1"); - } + if (!mc->smp_props.clusters_supported && + config->has_clusters && config->clusters > 1) { + error_setg(errp, + "clusters > 1 not supported by this machine's CPU topology"); + return; } clusters = clusters > 0 ? clusters : 1; - if (!mc->smp_props.dies_supported && config->has_dies) { - if (config->dies > 1) { - error_setg(errp, "dies not supported by this " - "machine's CPU topology"); - return; - } else { - /* Here dies only equals 1 since we've checked zero case. */ - warn_report("Deprecated CPU topology (considered invalid): " - "Unsupported dies parameter mustn't be " - "specified as 1"); - } + if (!mc->smp_props.dies_supported && + config->has_dies && config->dies > 1) { + error_setg(errp, + "dies > 1 not supported by this machine's CPU topology"); + return; } dies = dies > 0 ? dies : 1; - if (!mc->smp_props.books_supported && config->has_books) { - if (config->books > 1) { - error_setg(errp, "books not supported by this " - "machine's CPU topology"); - return; - } else { - /* Here books only equals 1 since we've checked zero case. */ - warn_report("Deprecated CPU topology (considered invalid): " - "Unsupported books parameter mustn't be " - "specified as 1"); - } + if (!mc->smp_props.books_supported && + config->has_books && config->books > 1) { + error_setg(errp, + "books > 1 not supported by this machine's CPU topology"); + return; } books = books > 0 ? books : 1; - if (!mc->smp_props.drawers_supported && config->has_drawers) { - if (config->drawers > 1) { - error_setg(errp, "drawers not supported by this " - "machine's CPU topology"); - return; - } else { - /* Here drawers only equals 1 since we've checked zero case. */ - warn_report("Deprecated CPU topology (considered invalid): " - "Unsupported drawers parameter mustn't be " - "specified as 1"); - } + if (!mc->smp_props.drawers_supported && + config->has_drawers && config->drawers > 1) { + error_setg(errp, + "drawers > 1 not supported by this machine's CPU topology"); + return; } drawers = drawers > 0 ? drawers : 1; diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 8994337e12..56165e6644 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -337,21 +337,21 @@ static const struct SMPTestData data_generic_invalid[] = { { /* config: -smp 2,dies=2 */ .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), - .expect_error = "dies not supported by this machine's CPU topology", + .expect_error = "dies > 1 not supported by this machine's CPU topology", }, { /* config: -smp 2,clusters=2 */ .config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), - .expect_error = "clusters not supported by this machine's CPU topology", + .expect_error = "clusters > 1 not supported by this machine's CPU topology", }, { /* config: -smp 2,books=2 */ .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0, F, 0), - .expect_error = "books not supported by this machine's CPU topology", + .expect_error = "books > 1 not supported by this machine's CPU topology", }, { /* config: -smp 2,drawers=2 */ .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, T, 2, F, 0, F, 0, F, 0, F, 0, F, 0), - .expect_error = "drawers not supported by this machine's CPU topology", + .expect_error = "drawers > 1 not supported by this machine's CPU topology", }, { /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */ .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8), From e68dcbb07923df0886802727edc3b21a10b0d342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 13 May 2024 13:33:58 +0100 Subject: [PATCH 0756/2884] tests: add testing of parameter=1 for SMP topology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validate that it is possible to pass 'parameter=1' for any SMP topology parameter, since unsupported parameters are implicitly considered to always have a value of 1. Signed-off-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Ján Tomko Message-ID: <20240513123358.612355-3-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/test-smp-parse.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 56165e6644..9fdba24fce 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -330,6 +330,14 @@ static const struct SMPTestData data_generic_valid[] = { .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16), .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + }, { + /* + * Unsupported parameters are always allowed to be set to '1' + * config: -smp 8,books=1,drawers=1,sockets=2,modules=1,dies=1,cores=2,threads=2,maxcpus=8 + * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_WITH_FULL_TOPO(8, 1, 1, 2, 1, 1, 2, 2, 8), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), }, }; From 93a3048dcf4565c73f2aa1d751f7197e296f1f1f Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Wed, 15 May 2024 17:31:31 +0000 Subject: [PATCH 0757/2884] tests: Gently exit from GDB when tests complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB commit a207f6b3a38 ('Rewrite "python" command exception handling') changed how exit() called from Python scripts loaded by GDB behave, turning it into an exception instead of a generic error code that is returned. This change caused several QEMU tests to crash with the following exception: Python Exception : 0 Error occurred in Python: 0 This happens because in tests/guest-debug/test_gdbstub.py exit is called after the tests have completed. This commit fixes it by politely asking GDB to exit via gdb.execute, passing the proper fail_count to be reported to 'make', instead of abruptly calling exit() from the Python script. Signed-off-by: Gustavo Romero Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240515173132.2462201-4-gustavo.romero@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- tests/guest-debug/test_gdbstub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/guest-debug/test_gdbstub.py b/tests/guest-debug/test_gdbstub.py index 7f71d34da1..46fbf98f0c 100644 --- a/tests/guest-debug/test_gdbstub.py +++ b/tests/guest-debug/test_gdbstub.py @@ -57,4 +57,4 @@ def main(test, expected_arch=None): pass print("All tests complete: {} failures".format(fail_count)) - exit(fail_count) + gdb.execute(f"exit {fail_count}") From 9442d8af674c80a2f8a7358977e1fc7ed43d2776 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:39:56 +0800 Subject: [PATCH 0758/2884] vfio/display: Fix error path in call site of ramfb_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_display_dmabuf_init() and vfio_display_region_init() calls ramfb_setup() without checking its return value. So we may run into a situation that vfio_display_probe() succeed but errp is set. This is risky and may lead to assert failure in error_setv(). Cc: Gerd Hoffmann Fixes: b290659fc3d ("hw/vfio/display: add ramfb support") Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/display.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/vfio/display.c b/hw/vfio/display.c index fe624a6c9b..d28b724102 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -361,6 +361,9 @@ static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) vdev); if (vdev->enable_ramfb) { vdev->dpy->ramfb = ramfb_setup(errp); + if (!vdev->dpy->ramfb) { + return -EINVAL; + } } vfio_display_edid_init(vdev); return 0; @@ -488,6 +491,9 @@ static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) vdev); if (vdev->enable_ramfb) { vdev->dpy->ramfb = ramfb_setup(errp); + if (!vdev->dpy->ramfb) { + return -EINVAL; + } } return 0; } From 455c009dc4ec13dab51c3764332433013b5cf3cb Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:39:57 +0800 Subject: [PATCH 0759/2884] vfio/display: Make vfio_display_*() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/display.c | 20 ++++++++++---------- hw/vfio/pci.c | 3 +-- hw/vfio/pci.h | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/hw/vfio/display.c b/hw/vfio/display.c index d28b724102..661e921616 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -348,11 +348,11 @@ static const GraphicHwOps vfio_display_dmabuf_ops = { .ui_info = vfio_display_edid_ui_info, }; -static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) { if (!display_opengl) { error_setg(errp, "vfio-display-dmabuf: opengl not available"); - return -1; + return false; } vdev->dpy = g_new0(VFIODisplay, 1); @@ -362,11 +362,11 @@ static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) if (vdev->enable_ramfb) { vdev->dpy->ramfb = ramfb_setup(errp); if (!vdev->dpy->ramfb) { - return -EINVAL; + return false; } } vfio_display_edid_init(vdev); - return 0; + return true; } static void vfio_display_dmabuf_exit(VFIODisplay *dpy) @@ -483,7 +483,7 @@ static const GraphicHwOps vfio_display_region_ops = { .gfx_update = vfio_display_region_update, }; -static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) { vdev->dpy = g_new0(VFIODisplay, 1); vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, @@ -492,10 +492,10 @@ static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) if (vdev->enable_ramfb) { vdev->dpy->ramfb = ramfb_setup(errp); if (!vdev->dpy->ramfb) { - return -EINVAL; + return false; } } - return 0; + return true; } static void vfio_display_region_exit(VFIODisplay *dpy) @@ -510,7 +510,7 @@ static void vfio_display_region_exit(VFIODisplay *dpy) /* ---------------------------------------------------------------------- */ -int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) +bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) { struct vfio_device_gfx_plane_info probe; int ret; @@ -533,11 +533,11 @@ int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) if (vdev->display == ON_OFF_AUTO_AUTO) { /* not an error in automatic mode */ - return 0; + return true; } error_setg(errp, "vfio: device doesn't support any (known) display method"); - return -1; + return false; } void vfio_display_finalize(VFIOPCIDevice *vdev) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c1adef5cf8..a447013a1d 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3200,8 +3200,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } if (vdev->display != ON_OFF_AUTO_OFF) { - ret = vfio_display_probe(vdev, errp); - if (ret) { + if (!vfio_display_probe(vdev, errp)) { goto out_deregister; } } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 92cd62d115..a5ac9efd4b 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -232,7 +232,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, Error **errp); void vfio_display_reset(VFIOPCIDevice *vdev); -int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); +bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); void vfio_display_finalize(VFIOPCIDevice *vdev); extern const VMStateDescription vfio_display_vmstate; From 50b632b64ce460844089ddd5061460d8b119df5d Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:39:58 +0800 Subject: [PATCH 0760/2884] vfio/helpers: Use g_autofree in vfio_set_irq_signaling() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Local pointer irq_set is freed before return from vfio_set_irq_signaling(). Use 'g_autofree' to avoid the g_free() calls. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/helpers.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 47b4096c05..1f3bdd9bf0 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -111,7 +111,7 @@ int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, int action, int fd, Error **errp) { ERRP_GUARD(); - struct vfio_irq_set *irq_set; + g_autofree struct vfio_irq_set *irq_set = NULL; int argsz, ret = 0; const char *name; int32_t *pfd; @@ -130,7 +130,6 @@ int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, if (ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { ret = -errno; } - g_free(irq_set); if (!ret) { return 0; From 84e37d02969ca1c7a6a8670e7d1da8e4ca5d56b9 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:39:59 +0800 Subject: [PATCH 0761/2884] vfio/helpers: Make vfio_set_irq_signaling() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 8 +++---- hw/vfio/ccw.c | 8 +++---- hw/vfio/helpers.c | 18 ++++++---------- hw/vfio/pci.c | 40 ++++++++++++++++++----------------- hw/vfio/platform.c | 18 +++++++--------- include/hw/vfio/vfio-common.h | 4 ++-- 6 files changed, 46 insertions(+), 50 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index ba653ef70f..d8a9615fee 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -117,8 +117,8 @@ static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, fd = event_notifier_get_fd(notifier); qemu_set_fd_handler(fd, fd_read, NULL, vapdev); - if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, - errp)) { + if (!vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, + errp)) { qemu_set_fd_handler(fd, NULL, NULL, vapdev); event_notifier_cleanup(notifier); } @@ -141,8 +141,8 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, return; } - if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vapdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 89bb980167..1f578a3c75 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -434,8 +434,8 @@ static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, fd = event_notifier_get_fd(notifier); qemu_set_fd_handler(fd, fd_read, NULL, vcdev); - if (vfio_set_irq_signaling(vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { + if (!vfio_set_irq_signaling(vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vcdev); event_notifier_cleanup(notifier); } @@ -464,8 +464,8 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, return; } - if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vcdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 1f3bdd9bf0..9edbc96688 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -107,12 +107,12 @@ static const char *index_to_str(VFIODevice *vbasedev, int index) } } -int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp) +bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp) { ERRP_GUARD(); g_autofree struct vfio_irq_set *irq_set = NULL; - int argsz, ret = 0; + int argsz; const char *name; int32_t *pfd; @@ -127,15 +127,11 @@ int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, pfd = (int32_t *)&irq_set->data; *pfd = fd; - if (ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { - ret = -errno; + if (!ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { + return true; } - if (!ret) { - return 0; - } - - error_setg_errno(errp, -ret, "VFIO_DEVICE_SET_IRQS failure"); + error_setg_errno(errp, errno, "VFIO_DEVICE_SET_IRQS failure"); name = index_to_str(vbasedev, index); if (name) { @@ -146,7 +142,7 @@ int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, error_prepend(errp, "Failed to %s %s eventfd signaling for interrupt ", fd < 0 ? "tear down" : "set up", action_to_str(action)); - return ret; + return false; } /* diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a447013a1d..358da4497b 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -147,10 +147,10 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) goto fail_irqfd; } - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_UNMASK, - event_notifier_get_fd(&vdev->intx.unmask), - errp)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_UNMASK, + event_notifier_get_fd(&vdev->intx.unmask), + errp)) { goto fail_vfio; } @@ -295,8 +295,8 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, vfio_intx_interrupt, NULL, vdev); - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->intx.interrupt); return -errno; @@ -590,9 +590,10 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, fd = event_notifier_get_fd(&vector->interrupt); } - if (vfio_set_irq_signaling(&vdev->vbasedev, - VFIO_PCI_MSIX_IRQ_INDEX, nr, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, + VFIO_PCI_MSIX_IRQ_INDEX, nr, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, + &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } } @@ -634,8 +635,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) int32_t fd = event_notifier_get_fd(&vector->interrupt); Error *err = NULL; - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, + nr, VFIO_IRQ_SET_ACTION_TRIGGER, fd, + &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } } @@ -2873,8 +2875,8 @@ static void vfio_register_err_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->err_notifier); qemu_set_fd_handler(fd, vfio_err_notifier_handler, NULL, vdev); - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->err_notifier); @@ -2890,8 +2892,8 @@ static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev) return; } - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier), @@ -2938,8 +2940,8 @@ static void vfio_register_req_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->req_notifier); qemu_set_fd_handler(fd, vfio_req_notifier_handler, NULL, vdev); - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->req_notifier); @@ -2956,8 +2958,8 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) return; } - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier), diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 2bd16096bb..3233ca8691 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -115,18 +115,17 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp, VFIODevice *vbasedev = &intp->vdev->vbasedev; int32_t fd = event_notifier_get_fd(intp->interrupt); Error *err = NULL; - int ret; qemu_set_fd_handler(fd, (IOHandler *)handler, NULL, intp); - ret = vfio_set_irq_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err); - if (ret) { + if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); qemu_set_fd_handler(fd, NULL, NULL, NULL); + return -EINVAL; } - return ret; + return 0; } /* @@ -355,15 +354,14 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp) int32_t fd = event_notifier_get_fd(intp->unmask); VFIODevice *vbasedev = &intp->vdev->vbasedev; Error *err = NULL; - int ret; qemu_set_fd_handler(fd, NULL, NULL, NULL); - ret = vfio_set_irq_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_UNMASK, fd, &err); - if (ret) { + if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0, + VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); + return -EINVAL; } - return ret; + return 0; } /** diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index b7bb4f5304..b712799caf 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -207,8 +207,8 @@ void vfio_spapr_container_deinit(VFIOContainer *container); void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); -int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp); +bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp); void vfio_region_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); uint64_t vfio_region_read(void *opaque, From c6c6cf91c0ad2a98d2566de162c3e089b75c2fc4 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:00 +0800 Subject: [PATCH 0762/2884] vfio/helpers: Make vfio_device_get_name() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 2 +- hw/vfio/ccw.c | 2 +- hw/vfio/helpers.c | 8 ++++---- hw/vfio/pci.c | 2 +- hw/vfio/platform.c | 5 ++--- include/hw/vfio/vfio-common.h | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index d8a9615fee..c12531a788 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -158,7 +158,7 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); VFIODevice *vbasedev = &vapdev->vdev; - if (vfio_device_get_name(vbasedev, errp) < 0) { + if (!vfio_device_get_name(vbasedev, errp)) { return; } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 1f578a3c75..8850ca17c8 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -588,7 +588,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) } } - if (vfio_device_get_name(vbasedev, errp) < 0) { + if (!vfio_device_get_name(vbasedev, errp)) { return; } diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 9edbc96688..4b079dc383 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -607,7 +607,7 @@ bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) return ret; } -int vfio_device_get_name(VFIODevice *vbasedev, Error **errp) +bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) { ERRP_GUARD(); struct stat st; @@ -616,7 +616,7 @@ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp) if (stat(vbasedev->sysfsdev, &st) < 0) { error_setg_errno(errp, errno, "no such host device"); error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev); - return -errno; + return false; } /* User may specify a name, e.g: VFIO platform device */ if (!vbasedev->name) { @@ -625,7 +625,7 @@ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp) } else { if (!vbasedev->iommufd) { error_setg(errp, "Use FD passing only with iommufd backend"); - return -EINVAL; + return false; } /* * Give a name with fd so any function printing out vbasedev->name @@ -636,7 +636,7 @@ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp) } } - return 0; + return true; } void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 358da4497b..aad012c348 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2999,7 +2999,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vdev->host.slot, vdev->host.function); } - if (vfio_device_get_name(vbasedev, errp) < 0) { + if (!vfio_device_get_name(vbasedev, errp)) { return; } diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 3233ca8691..e1a32863d9 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -545,9 +545,8 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) vbasedev->name); } - ret = vfio_device_get_name(vbasedev, errp); - if (ret) { - return ret; + if (!vfio_device_get_name(vbasedev, errp)) { + return -EINVAL; } if (!vfio_attach_device(vbasedev->name, vbasedev, diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index b712799caf..4cb1ab8645 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -279,7 +279,7 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); /* Returns 0 on success, or a negative errno. */ -int vfio_device_get_name(VFIODevice *vbasedev, Error **errp); +bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp); void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp); void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, DeviceState *dev, bool ram_discard); From 958609cfeb0502d3e0a59921b5d039c5c77d57ff Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:01 +0800 Subject: [PATCH 0763/2884] vfio/platform: Make vfio_populate_device() and vfio_base_device_init() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/platform.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index e1a32863d9..a85c199c76 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -441,7 +441,7 @@ static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev) * @errp: error object * */ -static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) +static bool vfio_populate_device(VFIODevice *vbasedev, Error **errp) { VFIOINTp *intp, *tmp; int i, ret = -1; @@ -450,7 +450,7 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) { error_setg(errp, "this isn't a platform device"); - return ret; + return false; } vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions); @@ -487,12 +487,11 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) irq.flags); intp = vfio_init_intp(vbasedev, irq, errp); if (!intp) { - ret = -1; goto irq_err; } } } - return 0; + return true; irq_err: timer_del(vdev->mmap_timer); QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) { @@ -507,7 +506,7 @@ reg_error: g_free(vdev->regions[i]); } g_free(vdev->regions); - return ret; + return false; } /* specialized functions for VFIO Platform devices */ @@ -527,10 +526,8 @@ static VFIODeviceOps vfio_platform_ops = { * fd retrieval, resource query. * Precondition: the device name must be initialized */ -static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) +static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp) { - int ret; - /* @fd takes precedence over @sysfsdev which takes precedence over @host */ if (vbasedev->fd < 0 && vbasedev->sysfsdev) { g_free(vbasedev->name); @@ -538,7 +535,7 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) } else if (vbasedev->fd < 0) { if (!vbasedev->name || strchr(vbasedev->name, '/')) { error_setg(errp, "wrong host device name"); - return -EINVAL; + return false; } vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s", @@ -546,20 +543,20 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) } if (!vfio_device_get_name(vbasedev, errp)) { - return -EINVAL; + return false; } if (!vfio_attach_device(vbasedev->name, vbasedev, &address_space_memory, errp)) { - return -EINVAL; + return false; } - ret = vfio_populate_device(vbasedev, errp); - if (ret) { - vfio_detach_device(vbasedev); + if (vfio_populate_device(vbasedev, errp)) { + return true; } - return ret; + vfio_detach_device(vbasedev); + return false; } /** @@ -576,7 +573,7 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); VFIODevice *vbasedev = &vdev->vbasedev; - int i, ret; + int i; qemu_mutex_init(&vdev->intp_mutex); @@ -584,9 +581,8 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) vbasedev->sysfsdev : vbasedev->name, vdev->compat); - ret = vfio_base_device_init(vbasedev, errp); - if (ret) { - goto out; + if (!vfio_base_device_init(vbasedev, errp)) { + goto init_err; } if (!vdev->compat) { @@ -618,11 +614,9 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) } sysbus_init_mmio(sbdev, vdev->regions[i]->mem); } -out: - if (!ret) { - return; - } + return; +init_err: if (vdev->vbasedev.name) { error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); } else { From 040f8d105057383b42c162424a99a468f67a97f4 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:02 +0800 Subject: [PATCH 0764/2884] vfio/ccw: Make vfio_ccw_get_region() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_populate_device() takes an 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/ccw.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 8850ca17c8..2600e62e37 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -474,7 +474,7 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, event_notifier_cleanup(notifier); } -static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) +static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) { VFIODevice *vdev = &vcdev->vdev; struct vfio_region_info *info; @@ -483,7 +483,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) /* Sanity check device */ if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) { error_setg(errp, "vfio: Um, this isn't a vfio-ccw device"); - return; + return false; } /* @@ -493,13 +493,13 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { error_setg(errp, "vfio: too few regions (%u), expected at least %u", vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); - return; + return false; } ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info); if (ret) { error_setg_errno(errp, -ret, "vfio: Error getting config info"); - return; + return false; } vcdev->io_region_size = info->size; @@ -553,7 +553,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) g_free(info); } - return; + return true; out_err: g_free(vcdev->crw_region); @@ -561,7 +561,7 @@ out_err: g_free(vcdev->async_cmd_region); g_free(vcdev->io_region); g_free(info); - return; + return false; } static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) @@ -597,8 +597,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_attach_dev_err; } - vfio_ccw_get_region(vcdev, &err); - if (err) { + if (!vfio_ccw_get_region(vcdev, &err)) { goto out_region_err; } From 44cd660a992df4bee1723fb8899c1d9bb9f8de0b Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:03 +0800 Subject: [PATCH 0765/2884] vfio/pci: Make vfio_intx_enable_kvm() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_intx_enable_kvm() takes an 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index aad012c348..12fb534d79 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -116,7 +116,7 @@ static void vfio_intx_eoi(VFIODevice *vbasedev) vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } -static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) { #ifdef CONFIG_KVM int irq_fd = event_notifier_get_fd(&vdev->intx.interrupt); @@ -124,7 +124,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || vdev->intx.route.mode != PCI_INTX_ENABLED || !kvm_resamplefds_enabled()) { - return; + return true; } /* Get to a known interrupt state */ @@ -161,7 +161,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) trace_vfio_intx_enable_kvm(vdev->vbasedev.name); - return; + return true; fail_vfio: kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, @@ -171,6 +171,9 @@ fail_irqfd: fail: qemu_set_fd_handler(irq_fd, vfio_intx_interrupt, NULL, vdev); vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + return false; +#else + return true; #endif } @@ -226,8 +229,7 @@ static void vfio_intx_update(VFIOPCIDevice *vdev, PCIINTxRoute *route) return; } - vfio_intx_enable_kvm(vdev, &err); - if (err) { + if (!vfio_intx_enable_kvm(vdev, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } @@ -302,8 +304,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) return -errno; } - vfio_intx_enable_kvm(vdev, &err); - if (err) { + if (!vfio_intx_enable_kvm(vdev, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } From 713b59a674b407b894161ada0b6541ce4e0f68d7 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:04 +0800 Subject: [PATCH 0766/2884] vfio/pci: Make vfio_pci_relocate_msix() and vfio_msix_early_setup() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_pci_relocate_msix() and vfio_msix_early_setup() takes an 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. By this chance, pass errp directly to vfio_msix_early_setup() to avoid calling error_propagate(). Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 12fb534d79..4fb5fd0c9f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1450,13 +1450,13 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) } } -static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) { int target_bar = -1; size_t msix_sz; if (!vdev->msix || vdev->msix_relo == OFF_AUTOPCIBAR_OFF) { - return; + return true; } /* The actual minimum size of MSI-X structures */ @@ -1479,7 +1479,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) if (target_bar < 0) { error_setg(errp, "No automatic MSI-X relocation available for " "device %04x:%04x", vdev->vendor_id, vdev->device_id); - return; + return false; } } else { target_bar = (int)(vdev->msix_relo - OFF_AUTOPCIBAR_BAR0); @@ -1489,7 +1489,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) if (vdev->bars[target_bar].ioport) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "I/O port BAR", target_bar); - return; + return false; } /* Cannot use a BAR in the "shadow" of a 64-bit BAR */ @@ -1497,7 +1497,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) target_bar > 0 && vdev->bars[target_bar - 1].mem64) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "consumed by 64-bit BAR %d", target_bar, target_bar - 1); - return; + return false; } /* 2GB max size for 32-bit BARs, cannot double if already > 1G */ @@ -1505,7 +1505,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) !vdev->bars[target_bar].mem64) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "no space to extend 32-bit BAR", target_bar); - return; + return false; } /* @@ -1540,6 +1540,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) trace_vfio_msix_relo(vdev->vbasedev.name, vdev->msix->table_bar, vdev->msix->table_offset); + return true; } /* @@ -1550,7 +1551,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) * need to first look for where the MSI-X table lives. So we * unfortunately split MSI-X setup across two functions. */ -static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) { uint8_t pos; uint16_t ctrl; @@ -1562,25 +1563,25 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); if (!pos) { - return; + return true; } if (pread(fd, &ctrl, sizeof(ctrl), vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) { error_setg_errno(errp, errno, "failed to read PCI MSIX FLAGS"); - return; + return false; } if (pread(fd, &table, sizeof(table), vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) { error_setg_errno(errp, errno, "failed to read PCI MSIX TABLE"); - return; + return false; } if (pread(fd, &pba, sizeof(pba), vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) { error_setg_errno(errp, errno, "failed to read PCI MSIX PBA"); - return; + return false; } ctrl = le16_to_cpu(ctrl); @@ -1598,7 +1599,7 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) if (ret < 0) { error_setg_errno(errp, -ret, "failed to get MSI-X irq info"); g_free(msix); - return; + return false; } msix->noresize = !!(irq_info.flags & VFIO_IRQ_INFO_NORESIZE); @@ -1630,7 +1631,7 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) error_setg(errp, "hardware reports invalid configuration, " "MSIX PBA outside of specified BAR"); g_free(msix); - return; + return false; } } @@ -1641,7 +1642,7 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) vfio_pci_fixup_msix_region(vdev); - vfio_pci_relocate_msix(vdev, errp); + return vfio_pci_relocate_msix(vdev, errp); } static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) @@ -3130,9 +3131,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bars_prepare(vdev); - vfio_msix_early_setup(vdev, &err); - if (err) { - error_propagate(errp, err); + if (!vfio_msix_early_setup(vdev, errp)) { goto error; } From e942d8f08dd54c5b4ca309e8ccd22193192543a5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:05 +0800 Subject: [PATCH 0767/2884] vfio/pci: Make vfio_populate_device() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_populate_device() takes an 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. By this chance, pass errp directly to vfio_populate_device() to avoid calling error_propagate(). Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4fb5fd0c9f..46d3c61859 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2740,7 +2740,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) return 0; } -static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info *reg_info; @@ -2750,18 +2750,18 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) /* Sanity check device */ if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) { error_setg(errp, "this isn't a PCI device"); - return; + return false; } if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { error_setg(errp, "unexpected number of io regions %u", vbasedev->num_regions); - return; + return false; } if (vbasedev->num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) { error_setg(errp, "unexpected number of irqs %u", vbasedev->num_irqs); - return; + return false; } for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) { @@ -2773,7 +2773,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) if (ret) { error_setg_errno(errp, -ret, "failed to get region %d info", i); - return; + return false; } QLIST_INIT(&vdev->bars[i].quirks); @@ -2783,7 +2783,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) VFIO_PCI_CONFIG_REGION_INDEX, ®_info); if (ret) { error_setg_errno(errp, -ret, "failed to get config info"); - return; + return false; } trace_vfio_populate_device_config(vdev->vbasedev.name, @@ -2804,7 +2804,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) if (ret) { error_append_hint(errp, "device does not support " "requested feature x-vga\n"); - return; + return false; } } @@ -2821,6 +2821,8 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) "Could not enable error recovery for the device", vbasedev->name); } + + return true; } static void vfio_pci_put_device(VFIOPCIDevice *vdev) @@ -2977,7 +2979,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) VFIOPCIDevice *vdev = VFIO_PCI(pdev); VFIODevice *vbasedev = &vdev->vbasedev; char *subsys; - Error *err = NULL; int i, ret; bool is_mdev; char uuid[UUID_STR_LEN]; @@ -3036,9 +3037,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto error; } - vfio_populate_device(vdev, &err); - if (err) { - error_propagate(errp, err); + if (!vfio_populate_device(vdev, errp)) { goto error; } From c32bab074ebac4dc872dabe801efe6581f73daba Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:06 +0800 Subject: [PATCH 0768/2884] vfio/pci: Make vfio_intx_enable() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 46d3c61859..7f35cb8a29 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -261,7 +261,7 @@ static void vfio_irqchip_change(Notifier *notify, void *data) vfio_intx_update(vdev, &vdev->intx.route); } -static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); Error *err = NULL; @@ -270,7 +270,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) if (!pin) { - return 0; + return true; } vfio_disable_interrupts(vdev); @@ -292,7 +292,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) ret = event_notifier_init(&vdev->intx.interrupt, 0); if (ret) { error_setg_errno(errp, -ret, "event_notifier_init failed"); - return ret; + return false; } fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, vfio_intx_interrupt, NULL, vdev); @@ -301,7 +301,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->intx.interrupt); - return -errno; + return false; } if (!vfio_intx_enable_kvm(vdev, &err)) { @@ -311,7 +311,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) vdev->interrupt = VFIO_INT_INTx; trace_vfio_intx_enable(vdev->vbasedev.name); - return 0; + return true; } static void vfio_intx_disable(VFIOPCIDevice *vdev) @@ -836,8 +836,7 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); vfio_msi_disable_common(vdev); - vfio_intx_enable(vdev, &err); - if (err) { + if (!vfio_intx_enable(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } @@ -2450,8 +2449,7 @@ void vfio_pci_post_reset(VFIOPCIDevice *vdev) Error *err = NULL; int nr; - vfio_intx_enable(vdev, &err); - if (err) { + if (!vfio_intx_enable(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } @@ -3194,8 +3192,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_intx_routing_notifier); vdev->irqchip_change_notifier.notify = vfio_irqchip_change; kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); - ret = vfio_intx_enable(vdev, errp); - if (ret) { + if (!vfio_intx_enable(vdev, errp)) { goto out_deregister; } } From 64410a741d57b62f736562a89b89d0497b3033ce Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:07 +0800 Subject: [PATCH 0769/2884] vfio/pci: Make vfio_populate_vga() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 2 +- hw/vfio/pci.c | 11 +++++------ hw/vfio/pci.h | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index b31ee79c60..ffe57c5954 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -478,7 +478,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * try to enable it. Probably shouldn't be using legacy mode without VGA, * but also no point in us enabling VGA if disabled in hardware. */ - if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) { + if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); error_report("IGD device %s failed to enable VGA access, " "legacy mode disabled", vdev->vbasedev.name); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7f35cb8a29..ab8f74299e 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2670,7 +2670,7 @@ static VFIODeviceOps vfio_pci_ops = { .vfio_load_config = vfio_pci_load_config, }; -int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) +bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info *reg_info; @@ -2681,7 +2681,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) error_setg_errno(errp, -ret, "failed getting region info for VGA region index %d", VFIO_PCI_VGA_REGION_INDEX); - return ret; + return false; } if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) || @@ -2691,7 +2691,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) (unsigned long)reg_info->flags, (unsigned long)reg_info->size); g_free(reg_info); - return -EINVAL; + return false; } vdev->vga = g_new0(VFIOVGA, 1); @@ -2735,7 +2735,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); - return 0; + return true; } static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) @@ -2798,8 +2798,7 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) g_free(reg_info); if (vdev->features & VFIO_FEATURE_ENABLE_VGA) { - ret = vfio_populate_vga(vdev, errp); - if (ret) { + if (!vfio_populate_vga(vdev, errp)) { error_append_hint(errp, "device does not support " "requested feature x-vga\n"); return false; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index a5ac9efd4b..7914f019d5 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -225,7 +225,7 @@ bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name); int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, struct vfio_pci_hot_reset_info **info_p); -int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); +bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, struct vfio_region_info *info, From b771a40f9eecbffee70a7425e5a96feb58f85d99 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:08 +0800 Subject: [PATCH 0770/2884] vfio/pci: Make capability related functions return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions operating on capability don't have a consistent return style. Below functions are in bool-valued functions style: vfio_msi_setup() vfio_msix_setup() vfio_add_std_cap() vfio_add_capabilities() Below two are integer-valued functions: vfio_add_vendor_specific_cap() vfio_setup_pcie_cap() But the returned integer is only used for check succeed/failure. Change them all to return bool so now all capability related functions follow the coding standand in qapi/error.h to return bool. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 77 ++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index ab8f74299e..c3323912dd 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1339,7 +1339,7 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) } } -static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) +static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { uint16_t ctrl; bool msi_64bit, msi_maskbit; @@ -1349,7 +1349,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl), vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { error_setg_errno(errp, errno, "failed reading MSI PCI_CAP_FLAGS"); - return -errno; + return false; } ctrl = le16_to_cpu(ctrl); @@ -1362,14 +1362,14 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err); if (ret < 0) { if (ret == -ENOTSUP) { - return 0; + return true; } error_propagate_prepend(errp, err, "msi_init failed: "); - return ret; + return false; } vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); - return 0; + return true; } static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) @@ -1644,7 +1644,7 @@ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) return vfio_pci_relocate_msix(vdev, errp); } -static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) +static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { int ret; Error *err = NULL; @@ -1660,11 +1660,11 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) if (ret < 0) { if (ret == -ENOTSUP) { warn_report_err(err); - return 0; + return true; } error_propagate(errp, err); - return ret; + return false; } /* @@ -1698,7 +1698,7 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false); } - return 0; + return true; } static void vfio_teardown_msi(VFIOPCIDevice *vdev) @@ -1977,8 +1977,8 @@ static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) } } -static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, - Error **errp) +static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, + Error **errp) { uint16_t flags; uint8_t type; @@ -1992,7 +1992,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, error_setg(errp, "assignment of PCIe type 0x%x " "devices is not currently supported", type); - return -EINVAL; + return false; } if (!pci_bus_is_express(pci_get_bus(&vdev->pdev))) { @@ -2025,7 +2025,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, } if (pci_bus_is_express(bus)) { - return 0; + return true; } } else if (pci_bus_is_root(pci_get_bus(&vdev->pdev))) { @@ -2063,7 +2063,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, * Legacy endpoints don't belong on the root complex. Windows * seems to be happier with devices if we skip the capability. */ - return 0; + return true; } } else { @@ -2099,12 +2099,12 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size, errp); if (pos < 0) { - return pos; + return false; } vdev->pdev.exp.exp_cap = pos; - return pos; + return true; } static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) @@ -2137,14 +2137,14 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) } } -static int vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, - uint8_t size, Error **errp) +static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, + uint8_t size, Error **errp) { PCIDevice *pdev = &vdev->pdev; pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp); if (pos < 0) { - return pos; + return false; } /* @@ -2156,15 +2156,15 @@ static int vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, memset(pdev->cmask + pos + 3, 0, size - 3); } - return pos; + return true; } -static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) +static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) { ERRP_GUARD(); PCIDevice *pdev = &vdev->pdev; uint8_t cap_id, next, size; - int ret; + bool ret; cap_id = pdev->config[pos]; next = pdev->config[pos + PCI_CAP_LIST_NEXT]; @@ -2185,9 +2185,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) * will be changed as we unwind the stack. */ if (next) { - ret = vfio_add_std_cap(vdev, next, errp); - if (ret) { - return ret; + if (!vfio_add_std_cap(vdev, next, errp)) { + return false; } } else { /* Begin the rebuild, use QEMU emulated list bits */ @@ -2197,7 +2196,7 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) ret = vfio_add_virt_caps(vdev, errp); if (ret) { - return ret; + return false; } } @@ -2221,28 +2220,27 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) case PCI_CAP_ID_PM: vfio_check_pm_reset(vdev, pos); vdev->pm_cap = pos; - ret = pci_add_capability(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; break; case PCI_CAP_ID_AF: vfio_check_af_flr(vdev, pos); - ret = pci_add_capability(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; break; case PCI_CAP_ID_VNDR: ret = vfio_add_vendor_specific_cap(vdev, pos, size, errp); break; default: - ret = pci_add_capability(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; break; } - if (ret < 0) { + if (!ret) { error_prepend(errp, "failed to add PCI capability 0x%x[0x%x]@0x%x: ", cap_id, size, pos); - return ret; } - return 0; + return ret; } static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) @@ -2388,23 +2386,21 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) return; } -static int vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; - int ret; if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || !pdev->config[PCI_CAPABILITY_LIST]) { - return 0; /* Nothing to add */ + return true; /* Nothing to add */ } - ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp); - if (ret) { - return ret; + if (!vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp)) { + return false; } vfio_add_ext_cap(vdev); - return 0; + return true; } void vfio_pci_pre_reset(VFIOPCIDevice *vdev) @@ -3133,8 +3129,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bars_register(vdev); - ret = vfio_add_capabilities(vdev, errp); - if (ret) { + if (!vfio_add_capabilities(vdev, errp)) { goto out_teardown; } From 514855e18fcdc3378fb925046d89a0f1493336db Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:09 +0800 Subject: [PATCH 0771/2884] vfio/pci: Use g_autofree for vfio_region_info pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointer opregion is freed after vfio_pci_igd_opregion_init(). Use 'g_autofree' to avoid the g_free() calls. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c3323912dd..8379d2284a 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3143,7 +3143,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (!vdev->igd_opregion && vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) { - struct vfio_region_info *opregion; + g_autofree struct vfio_region_info *opregion = NULL; if (vdev->pdev.qdev.hotplugged) { error_setg(errp, @@ -3162,7 +3162,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } ret = vfio_pci_igd_opregion_init(vdev, opregion, errp); - g_free(opregion); if (ret) { goto out_teardown; } From d3c6a18bc7affd3b89903d98d8a3125a3e1c9861 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:10 +0800 Subject: [PATCH 0772/2884] vfio/pci-quirks: Make vfio_pci_igd_opregion_init() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 3 +-- hw/vfio/pci-quirks.c | 8 ++++---- hw/vfio/pci.c | 3 +-- hw/vfio/pci.h | 6 +++--- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index ffe57c5954..402fc5ce1d 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -502,8 +502,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } /* Setup OpRegion access */ - ret = vfio_pci_igd_opregion_init(vdev, opregion, &err); - if (ret) { + if (!vfio_pci_igd_opregion_init(vdev, opregion, &err)) { error_append_hint(&err, "IGD legacy mode disabled\n"); error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); goto out; diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 496fd1ee86..ca27917159 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1169,8 +1169,8 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) * the table and to write the base address of that memory to the ASLS register * of the IGD device. */ -int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info, Error **errp) +bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info, Error **errp) { int ret; @@ -1181,7 +1181,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, error_setg(errp, "failed to read IGD OpRegion"); g_free(vdev->igd_opregion); vdev->igd_opregion = NULL; - return -EINVAL; + return false; } /* @@ -1206,7 +1206,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); - return 0; + return true; } /* diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 8379d2284a..76a3931dba 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3161,8 +3161,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto out_teardown; } - ret = vfio_pci_igd_opregion_init(vdev, opregion, errp); - if (ret) { + if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) { goto out_teardown; } } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 7914f019d5..f158681072 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -227,9 +227,9 @@ int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); -int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info, - Error **errp); +bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info, + Error **errp); void vfio_display_reset(VFIOPCIDevice *vdev); bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); From 0a0bda0acd529161d5cbed6c603ab89674a9af8c Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:11 +0800 Subject: [PATCH 0773/2884] vfio/pci-quirks: Make vfio_add_*_cap() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to follow the coding standand in qapi/error.h to return bool for bool-valued functions. Include below functions: vfio_add_virt_caps() vfio_add_nv_gpudirect_cap() vfio_add_vmd_shadow_cap() Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 42 +++++++++++++++++++----------------------- hw/vfio/pci.c | 3 +-- hw/vfio/pci.h | 2 +- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index ca27917159..39dae72497 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1536,7 +1536,7 @@ static bool is_valid_std_cap_offset(uint8_t pos) pos <= (PCI_CFG_SPACE_SIZE - PCI_CAP_SIZEOF)); } -static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) { ERRP_GUARD(); PCIDevice *pdev = &vdev->pdev; @@ -1545,18 +1545,18 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) uint8_t tmp; if (vdev->nv_gpudirect_clique == 0xFF) { - return 0; + return true; } if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID)) { error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid device vendor"); - return -EINVAL; + return false; } if (pci_get_byte(pdev->config + PCI_CLASS_DEVICE + 1) != PCI_BASE_CLASS_DISPLAY) { error_setg(errp, "NVIDIA GPUDirect Clique ID: unsupported PCI class"); - return -EINVAL; + return false; } /* @@ -1572,7 +1572,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) vdev->config_offset + PCI_CAPABILITY_LIST); if (ret != 1 || !is_valid_std_cap_offset(tmp)) { error_setg(errp, "NVIDIA GPUDirect Clique ID: error getting cap list"); - return -EINVAL; + return false; } do { @@ -1590,13 +1590,13 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) pos = 0xD4; } else { error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid config space"); - return -EINVAL; + return false; } ret = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, 8, errp); if (ret < 0) { error_prepend(errp, "Failed to add NVIDIA GPUDirect cap: "); - return ret; + return false; } memset(vdev->emulated_config_bits + pos, 0xFF, 8); @@ -1608,7 +1608,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) pci_set_byte(pdev->config + pos++, vdev->nv_gpudirect_clique << 3); pci_set_byte(pdev->config + pos, 0); - return 0; + return true; } /* @@ -1629,7 +1629,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) */ #define VMD_SHADOW_CAP_VER 1 #define VMD_SHADOW_CAP_LEN 24 -static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) { ERRP_GUARD(); uint8_t membar_phys[16]; @@ -1639,7 +1639,7 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x467F) || vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x4C3D) || vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x9A0B))) { - return 0; + return true; } ret = pread(vdev->vbasedev.fd, membar_phys, 16, @@ -1647,14 +1647,14 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) if (ret != 16) { error_report("VMD %s cannot read MEMBARs (%d)", vdev->vbasedev.name, ret); - return -EFAULT; + return false; } ret = pci_add_capability(&vdev->pdev, PCI_CAP_ID_VNDR, pos, VMD_SHADOW_CAP_LEN, errp); if (ret < 0) { error_prepend(errp, "Failed to add VMD MEMBAR Shadow cap: "); - return ret; + return false; } memset(vdev->emulated_config_bits + pos, 0xFF, VMD_SHADOW_CAP_LEN); @@ -1664,22 +1664,18 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) pci_set_long(vdev->pdev.config + pos, 0x53484457); /* SHDW */ memcpy(vdev->pdev.config + pos + 4, membar_phys, 16); - return 0; + return true; } -int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp) +bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp) { - int ret; - - ret = vfio_add_nv_gpudirect_cap(vdev, errp); - if (ret) { - return ret; + if (!vfio_add_nv_gpudirect_cap(vdev, errp)) { + return false; } - ret = vfio_add_vmd_shadow_cap(vdev, errp); - if (ret) { - return ret; + if (!vfio_add_vmd_shadow_cap(vdev, errp)) { + return false; } - return 0; + return true; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 76a3931dba..35ad9b582f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2194,8 +2194,7 @@ static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) vdev->emulated_config_bits[PCI_CAPABILITY_LIST] = 0xff; vdev->emulated_config_bits[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - ret = vfio_add_virt_caps(vdev, errp); - if (ret) { + if (!vfio_add_virt_caps(vdev, errp)) { return false; } } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index f158681072..bf67df2fbc 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -212,7 +212,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); -int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); void vfio_quirk_reset(VFIOPCIDevice *vdev); VFIOQuirk *vfio_quirk_alloc(int nr_mem); void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr); From 0d3e89bea8ff2fb74b829d80a8e3ebe05b80614a Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:12 +0800 Subject: [PATCH 0774/2884] vfio: Use g_autofree in all call site of vfio_get_region_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some exceptions when pointer to vfio_region_info is reused. In that case, the pointed memory is freed manually. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/helpers.c | 7 ++----- hw/vfio/igd.c | 5 ++--- hw/vfio/pci.c | 13 +++---------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 4b079dc383..27ea26aa48 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -343,7 +343,7 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region, int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, int index, const char *name) { - struct vfio_region_info *info; + g_autofree struct vfio_region_info *info = NULL; int ret; ret = vfio_get_region_info(vbasedev, index, &info); @@ -376,8 +376,6 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, } } - g_free(info); - trace_vfio_region_setup(vbasedev->name, index, name, region->flags, region->fd_offset, region->size); return 0; @@ -594,14 +592,13 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) { - struct vfio_region_info *info = NULL; + g_autofree struct vfio_region_info *info = NULL; bool ret = false; if (!vfio_get_region_info(vbasedev, region, &info)) { if (vfio_get_region_info_cap(info, cap_type)) { ret = true; } - g_free(info); } return ret; diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 402fc5ce1d..1e79202f2b 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -367,8 +367,8 @@ static const MemoryRegionOps vfio_igd_index_quirk = { void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) { - struct vfio_region_info *rom = NULL, *opregion = NULL, - *host = NULL, *lpc = NULL; + g_autofree struct vfio_region_info *rom = NULL; + struct vfio_region_info *opregion = NULL, *host = NULL, *lpc = NULL; VFIOQuirk *quirk; VFIOIGDQuirk *igd; PCIDevice *lpc_bridge; @@ -609,7 +609,6 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); out: - g_free(rom); g_free(opregion); g_free(host); g_free(lpc); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 35ad9b582f..74a79bdf61 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -879,7 +879,7 @@ static void vfio_update_msi(VFIOPCIDevice *vdev) static void vfio_pci_load_rom(VFIOPCIDevice *vdev) { - struct vfio_region_info *reg_info; + g_autofree struct vfio_region_info *reg_info = NULL; uint64_t size; off_t off = 0; ssize_t bytes; @@ -897,8 +897,6 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) vdev->rom_size = size = reg_info->size; vdev->rom_offset = reg_info->offset; - g_free(reg_info); - if (!vdev->rom_size) { vdev->rom_read_failed = true; error_report("vfio-pci: Cannot read device rom at " @@ -2668,7 +2666,7 @@ static VFIODeviceOps vfio_pci_ops = { bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; - struct vfio_region_info *reg_info; + g_autofree struct vfio_region_info *reg_info = NULL; int ret; ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); @@ -2685,7 +2683,6 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) error_setg(errp, "unexpected VGA info, flags 0x%lx, size 0x%lx", (unsigned long)reg_info->flags, (unsigned long)reg_info->size); - g_free(reg_info); return false; } @@ -2694,8 +2691,6 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) vdev->vga->fd_offset = reg_info->offset; vdev->vga->fd = vdev->vbasedev.fd; - g_free(reg_info); - vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks); @@ -2736,7 +2731,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; - struct vfio_region_info *reg_info; + g_autofree struct vfio_region_info *reg_info = NULL; struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; int i, ret = -1; @@ -2790,8 +2785,6 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) } vdev->config_offset = reg_info->offset; - g_free(reg_info); - if (vdev->features & VFIO_FEATURE_ENABLE_VGA) { if (!vfio_populate_vga(vdev, errp)) { error_append_hint(errp, "device does not support " From b4e1670c494165d4186930d56f692857f4fec89b Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 12:40:13 +0800 Subject: [PATCH 0775/2884] vfio/igd: Use g_autofree in vfio_probe_igd_bar4_quirk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointer opregion, host and lpc are allocated and freed in vfio_probe_igd_bar4_quirk(). Use g_autofree to automatically free them. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 1e79202f2b..d320d032a7 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -368,7 +368,9 @@ static const MemoryRegionOps vfio_igd_index_quirk = { void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) { g_autofree struct vfio_region_info *rom = NULL; - struct vfio_region_info *opregion = NULL, *host = NULL, *lpc = NULL; + g_autofree struct vfio_region_info *opregion = NULL; + g_autofree struct vfio_region_info *host = NULL; + g_autofree struct vfio_region_info *lpc = NULL; VFIOQuirk *quirk; VFIOIGDQuirk *igd; PCIDevice *lpc_bridge; @@ -426,7 +428,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if ((ret || !rom->size) && !vdev->pdev.romfile) { error_report("IGD device %s has no ROM, legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* @@ -437,7 +439,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) error_report("IGD device %s hotplugged, ROM disabled, " "legacy mode disabled", vdev->vbasedev.name); vdev->rom_read_failed = true; - goto out; + return; } /* @@ -450,7 +452,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s does not support OpRegion access," "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } ret = vfio_get_dev_region_info(&vdev->vbasedev, @@ -459,7 +461,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s does not support host bridge access," "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } ret = vfio_get_dev_region_info(&vdev->vbasedev, @@ -468,7 +470,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s does not support LPC bridge access," "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); @@ -482,7 +484,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); error_report("IGD device %s failed to enable VGA access, " "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* Create our LPC/ISA bridge */ @@ -490,7 +492,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s failed to create LPC bridge, " "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* Stuff some host values into the VM PCI host bridge */ @@ -498,14 +500,14 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s failed to modify host bridge, " "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* Setup OpRegion access */ if (!vfio_pci_igd_opregion_init(vdev, opregion, &err)) { error_append_hint(&err, "IGD legacy mode disabled\n"); error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - goto out; + return; } /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ @@ -607,9 +609,4 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); - -out: - g_free(opregion); - g_free(host); - g_free(lpc); } From d0414d71f612651699de019b911a772b07d0ac4a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 May 2024 14:39:14 +0200 Subject: [PATCH 0776/2884] target/i386: generate simpler code for ROL/ROR with immediate count gen_rot_carry and gen_rot_overflow are meant to be called with count == NULL if the count cannot be zero. However this is not done in gen_ROL and gen_ROR, and writing everywhere "can_be_zero ? count : NULL" is burdensome and less readable. Just pass can_be_zero as a separate argument. gen_RCL and gen_RCR use a conditional branch to skip the computation if count is zero, so they can pass false unconditionally to gen_rot_overflow. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240522123914.608516-1-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2dee33dd48..33cb59e54b 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2901,14 +2901,15 @@ static bool gen_eflags_adcox(DisasContext *s, X86DecodedInsn *decode, bool want_ return got_cf; } -static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, TCGv count) +static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, + bool can_be_zero, TCGv count) { MemOp ot = decode->op[0].ot; - TCGv temp = count ? tcg_temp_new() : decode->cc_src2; + TCGv temp = can_be_zero ? tcg_temp_new() : decode->cc_src2; tcg_gen_xor_tl(temp, old, result); tcg_gen_extract_tl(temp, temp, (8 << ot) - 1, 1); - if (count) { + if (can_be_zero) { tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src2, count, tcg_constant_tl(0), decode->cc_src2, temp); } @@ -3000,7 +3001,7 @@ static void gen_RCL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) /* Compute result and outgoing overflow */ tcg_gen_mov_tl(decode->cc_src2, s->T0); tcg_gen_or_tl(s->T0, low, high); - gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL); + gen_rot_overflow(decode, s->T0, decode->cc_src2, false, NULL); if (zero_label) { gen_set_label(zero_label); @@ -3053,7 +3054,7 @@ static void gen_RCR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) /* Compute result and outgoing overflow */ tcg_gen_mov_tl(decode->cc_src2, s->T0); tcg_gen_or_tl(s->T0, low, high); - gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL); + gen_rot_overflow(decode, s->T0, decode->cc_src2, false, NULL); if (zero_label) { gen_set_label(zero_label); @@ -3129,9 +3130,10 @@ static TCGv_i32 gen_rot_replicate(MemOp ot, TCGv in) } } -static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, TCGv count, int bit) +static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, + bool can_be_zero, TCGv count, int bit) { - if (count == NULL) { + if (!can_be_zero) { tcg_gen_extract_tl(decode->cc_dst, result, bit, 1); } else { TCGv temp = tcg_temp_new(); @@ -3165,8 +3167,8 @@ static void gen_ROL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } else { tcg_gen_rotl_tl(s->T0, s->T0, count); } - gen_rot_carry(decode, s->T0, count, 0); - gen_rot_overflow(decode, s->T0, old, count); + gen_rot_carry(decode, s->T0, can_be_zero, count, 0); + gen_rot_overflow(decode, s->T0, old, can_be_zero, count); } static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) @@ -3190,12 +3192,12 @@ static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_rotr_i32(temp32, temp32, count32); /* Zero extend to facilitate later optimization. */ tcg_gen_extu_i32_tl(s->T0, temp32); - gen_rot_carry(decode, s->T0, count, 31); + gen_rot_carry(decode, s->T0, can_be_zero, count, 31); } else { tcg_gen_rotr_tl(s->T0, s->T0, count); - gen_rot_carry(decode, s->T0, count, TARGET_LONG_BITS - 1); + gen_rot_carry(decode, s->T0, can_be_zero, count, TARGET_LONG_BITS - 1); } - gen_rot_overflow(decode, s->T0, old, count); + gen_rot_overflow(decode, s->T0, old, can_be_zero, count); } static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) From ec56891984e682c0e6a7bbf5a51372648a60a353 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 May 2024 14:39:12 +0200 Subject: [PATCH 0777/2884] target/i386: clean up AAM/AAD The 32-bit AAM/AAD opcodes are using helpers that read and write flags and env->regs[R_EAX]. Clean them up so that the table correctly includes AX as a 16-bit input and output. No real reason to do it to be honest, but they are nice one-output helpers and it removes the masking of env->regs[R_EAX] that generic load/writeback code already does. Signed-off-by: Paolo Bonzini Reviewed-by: Richard Henderson Message-ID: <20240522123912.608497-1-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- target/i386/helper.h | 4 ++-- target/i386/tcg/decode-new.c.inc | 4 ++-- target/i386/tcg/emit.c.inc | 8 ++++---- target/i386/tcg/int_helper.c | 19 ++++++++----------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/target/i386/helper.h b/target/i386/helper.h index 3c207ac62d..a52a1bf0f2 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -22,8 +22,8 @@ DEF_HELPER_FLAGS_5(bndstx32, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) DEF_HELPER_FLAGS_5(bndstx64, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) DEF_HELPER_1(bnd_jmp, void, env) -DEF_HELPER_2(aam, void, env, int) -DEF_HELPER_2(aad, void, env, int) +DEF_HELPER_FLAGS_2(aam, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aad, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_1(aaa, void, env) DEF_HELPER_1(aas, void, env) DEF_HELPER_1(daa, void, env) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 141ab2bc56..27dc1bb146 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1480,8 +1480,8 @@ static const X86OpEntry opcodes_root[256] = { [0xD1] = X86_OP_GROUP1(group2, E,v), [0xD2] = X86_OP_GROUP2(group2, E,b, 1,b), /* CL */ [0xD3] = X86_OP_GROUP2(group2, E,v, 1,b), /* CL */ - [0xD4] = X86_OP_ENTRYr(AAM, I,b), - [0xD5] = X86_OP_ENTRYr(AAD, I,b), + [0xD4] = X86_OP_ENTRY2(AAM, 0,w, I,b), + [0xD5] = X86_OP_ENTRY2(AAD, 0,w, I,b), [0xD6] = X86_OP_ENTRYw(SALC, 0,b), [0xD7] = X86_OP_ENTRY1(XLAT, 0,b, zextT0), /* AL read/written */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 33cb59e54b..c78e35b1e2 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1084,8 +1084,8 @@ static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) static void gen_AAD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { - gen_helper_aad(tcg_env, tcg_constant_i32(decode->immediate)); - set_cc_op(s, CC_OP_LOGICB); + gen_helper_aad(s->T0, s->T0, s->T1); + prepare_update1_cc(decode, s, CC_OP_LOGICB); } static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) @@ -1093,8 +1093,8 @@ static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) if (decode->immediate == 0) { gen_exception(s, EXCP00_DIVZ); } else { - gen_helper_aam(tcg_env, tcg_constant_i32(decode->immediate)); - set_cc_op(s, CC_OP_LOGICB); + gen_helper_aam(s->T0, s->T0, s->T1); + prepare_update1_cc(decode, s, CC_OP_LOGICB); } } diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index df16130f5d..4cc59f1520 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -145,27 +145,24 @@ void helper_idivl_EAX(CPUX86State *env, target_ulong t0) /* bcd */ -/* XXX: exception */ -void helper_aam(CPUX86State *env, int base) +target_ulong helper_aam(target_ulong al, target_ulong base) { - int al, ah; + int ah; - al = env->regs[R_EAX] & 0xff; + al &= 0xff; ah = al / base; al = al % base; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); - CC_DST = al; + return al | (ah << 8); } -void helper_aad(CPUX86State *env, int base) +target_ulong helper_aad(target_ulong ax, target_ulong base) { int al, ah; - al = env->regs[R_EAX] & 0xff; - ah = (env->regs[R_EAX] >> 8) & 0xff; + al = ax & 0xff; + ah = (ax >> 8) & 0xff; al = ((ah * base) + al) & 0xff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al; - CC_DST = al; + return al; } void helper_aaa(CPUX86State *env) From a44ea3fa7f2aa1d809fdca1b84a52695b53d8ad0 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 8 May 2024 19:55:07 +0200 Subject: [PATCH 0778/2884] hw/i386/pc_sysfw: Alias rather than copy isa-bios region In the -bios case the "isa-bios" memory region is an alias to the BIOS mapped to the top of the 4G memory boundary. Do the same in the -pflash case, but only for new machine versions for migration compatibility. This establishes common behavior and makes pflash commands work in the "isa-bios" region which some real-world legacy bioses rely on. Note that in the sev_enabled() case, the "isa-bios" memory region in the -pflash case will now also point to encrypted memory, just like it already does in the -bios case. When running `info mtree` before and after this commit with `qemu-system-x86_64 -S -drive \ if=pflash,format=raw,readonly=on,file=/usr/share/qemu/bios-256k.bin` and running `diff -u before.mtree after.mtree` results in the following changes in the memory tree: --- before.mtree +++ after.mtree @@ -71,7 +71,7 @@ 0000000000000000-ffffffffffffffff (prio -1, i/o): pci 00000000000a0000-00000000000bffff (prio 1, i/o): vga-lowmem 00000000000c0000-00000000000dffff (prio 1, rom): pc.rom - 00000000000e0000-00000000000fffff (prio 1, rom): isa-bios + 00000000000e0000-00000000000fffff (prio 1, romd): alias isa-bios @system.flash0 0000000000020000-000000000003ffff 00000000000a0000-00000000000bffff (prio 1, i/o): alias smram-region @pci 00000000000a0000-00000000000bffff 00000000000c0000-00000000000c3fff (prio 1, i/o): alias pam-pci @pci 00000000000c0000-00000000000c3fff 00000000000c4000-00000000000c7fff (prio 1, i/o): alias pam-pci @pci 00000000000c4000-00000000000c7fff @@ -108,7 +108,7 @@ 0000000000000000-ffffffffffffffff (prio -1, i/o): pci 00000000000a0000-00000000000bffff (prio 1, i/o): vga-lowmem 00000000000c0000-00000000000dffff (prio 1, rom): pc.rom - 00000000000e0000-00000000000fffff (prio 1, rom): isa-bios + 00000000000e0000-00000000000fffff (prio 1, romd): alias isa-bios @system.flash0 0000000000020000-000000000003ffff 00000000000a0000-00000000000bffff (prio 1, i/o): alias smram-region @pci 00000000000a0000-00000000000bffff 00000000000c0000-00000000000c3fff (prio 1, i/o): alias pam-pci @pci 00000000000c0000-00000000000c3fff 00000000000c4000-00000000000c7fff (prio 1, i/o): alias pam-pci @pci 00000000000c4000-00000000000c7fff @@ -131,11 +131,14 @@ memory-region: pc.ram 0000000000000000-0000000007ffffff (prio 0, ram): pc.ram +memory-region: system.flash0 + 00000000fffc0000-00000000ffffffff (prio 0, romd): system.flash0 + memory-region: pci 0000000000000000-ffffffffffffffff (prio -1, i/o): pci 00000000000a0000-00000000000bffff (prio 1, i/o): vga-lowmem 00000000000c0000-00000000000dffff (prio 1, rom): pc.rom - 00000000000e0000-00000000000fffff (prio 1, rom): isa-bios + 00000000000e0000-00000000000fffff (prio 1, romd): alias isa-bios @system.flash0 0000000000020000-000000000003ffff memory-region: smram 00000000000a0000-00000000000bffff (prio 0, ram): alias smram-low @pc.ram 00000000000a0000-00000000000bffff Note that in both cases the "system" memory region contains the entry 00000000fffc0000-00000000ffffffff (prio 0, romd): system.flash0 but the "system.flash0" memory region only appears standalone when "isa-bios" is an alias. Signed-off-by: Bernhard Beschow Message-ID: <20240508175507.22270-7-shentey@gmail.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + hw/i386/pc_piix.c | 3 +++ hw/i386/pc_q35.c | 2 ++ hw/i386/pc_sysfw.c | 8 +++++++- include/hw/i386/pc.h | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index bfb46e9b54..4a2d6f5a97 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1816,6 +1816,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->has_reserved_memory = true; pcmc->enforce_aligned_dimm = true; pcmc->enforce_amd_1tb_hole = true; + pcmc->isa_bios_alias = true; /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported * to be used at the moment, 32K should be enough for a while. */ pcmc->acpi_data_size = 0x20000 + 0x8000; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 99efb3c45c..ebb51de380 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -526,12 +526,15 @@ DEFINE_I440FX_MACHINE(v9_1, "pc-i440fx-9.1", NULL, static void pc_i440fx_9_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_i440fx_9_1_machine_options(m); m->alias = NULL; m->is_default = false; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); + pcmc->isa_bios_alias = false; } DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index bb53a51ac1..bd7db4abac 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -378,10 +378,12 @@ DEFINE_Q35_MACHINE(v9_1, "pc-q35-9.1", NULL, static void pc_q35_9_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_9_1_machine_options(m); m->alias = NULL; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); + pcmc->isa_bios_alias = false; } DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL, diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 82d37cb376..ac88ad4eb9 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -135,6 +135,7 @@ static void pc_system_flash_map(PCMachineState *pcms, MemoryRegion *rom_memory) { X86MachineState *x86ms = X86_MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); hwaddr total_size = 0; int i; BlockBackend *blk; @@ -184,7 +185,12 @@ static void pc_system_flash_map(PCMachineState *pcms, if (i == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); - pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem); + if (pcmc->isa_bios_alias) { + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem, + true); + } else { + pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem); + } /* Encrypt the pflash boot ROM */ if (sev_enabled()) { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index e52290916c..ad9c3d9ba8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -119,6 +119,7 @@ struct PCMachineClass { bool enforce_aligned_dimm; bool broken_reserved_end; bool enforce_amd_1tb_hole; + bool isa_bios_alias; /* generate legacy CPU hotplug AML */ bool legacy_cpu_hotplug; From ba6780905943696d790cc880c8e5684b51f027fe Mon Sep 17 00:00:00 2001 From: Robert Hoo Date: Fri, 12 Jan 2024 14:00:41 +0800 Subject: [PATCH 0779/2884] target/i386: add support for LAM in CPUID enumeration Linear Address Masking (LAM) is a new Intel CPU feature, which allows software to use of the untranslated address bits for metadata. The bit definition: CPUID.(EAX=7,ECX=1):EAX[26] Add CPUID definition for LAM. Note LAM feature is not supported for TCG of target-i386, LAM CPIUD bit will not be added to TCG_7_1_EAX_FEATURES. More info can be found in Intel ISE Chapter "LINEAR ADDRESS MASKING(LAM)" https://cdrdv2.intel.com/v1/dl/getContent/671368 Signed-off-by: Robert Hoo Co-developed-by: Binbin Wu Signed-off-by: Binbin Wu Tested-by: Xuelian Guo Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-ID: <20240112060042.19925-2-binbin.wu@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index cfe7c92d6b..de1ad7270c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -969,7 +969,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "fsrc", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "amx-fp16", NULL, "avx-ifma", - NULL, NULL, NULL, NULL, + NULL, NULL, "lam", NULL, NULL, NULL, NULL, NULL, }, .cpuid = { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ccccb62fc3..107f263429 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -927,6 +927,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_1_EAX_AMX_FP16 (1U << 21) /* Support for VPMADD52[H,L]UQ */ #define CPUID_7_1_EAX_AVX_IFMA (1U << 23) +/* Linear Address Masking */ +#define CPUID_7_1_EAX_LAM (1U << 26) /* Support for VPDPB[SU,UU,SS]D[,S] */ #define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4) From 0117067131f99acaab4f4d2cca0290c5510e37cf Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 12 Jan 2024 14:00:42 +0800 Subject: [PATCH 0780/2884] target/i386: add control bits support for LAM LAM uses CR3[61] and CR3[62] to configure/enable LAM on user pointers. LAM uses CR4[28] to configure/enable LAM on supervisor pointers. For CR3 LAM bits, no additional handling needed: - TCG LAM is not supported for TCG of target-i386. helper_write_crN() and helper_vmrun() check max physical address bits before calling cpu_x86_update_cr3(), no change needed, i.e. CR3 LAM bits are not allowed to be set in TCG. - gdbstub x86_cpu_gdb_write_register() will call cpu_x86_update_cr3() to update cr3. Allow gdb to set the LAM bit(s) to CR3, if vcpu doesn't support LAM, KVM_SET_SREGS will fail as other reserved bits. For CR4 LAM bit, its reservation depends on vcpu supporting LAM feature or not. - TCG LAM is not supported for TCG of target-i386. helper_write_crN() and helper_vmrun() check CR4 reserved bit before calling cpu_x86_update_cr4(), i.e. CR4 LAM bit is not allowed to be set in TCG. - gdbstub x86_cpu_gdb_write_register() will call cpu_x86_update_cr4() to update cr4. Mask out LAM bit on CR4 if vcpu doesn't support LAM. - x86_cpu_reset_hold() doesn't need special handling. Signed-off-by: Binbin Wu Tested-by: Xuelian Guo Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-ID: <20240112060042.19925-3-binbin.wu@linux.intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 7 ++++++- target/i386/helper.c | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 107f263429..ccf6811794 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -258,6 +258,7 @@ typedef enum X86Seg { #define CR4_SMAP_MASK (1U << 21) #define CR4_PKE_MASK (1U << 22) #define CR4_PKS_MASK (1U << 24) +#define CR4_LAM_SUP_MASK (1U << 28) #define CR4_RESERVED_MASK \ (~(target_ulong)(CR4_VME_MASK | CR4_PVI_MASK | CR4_TSD_MASK \ @@ -266,7 +267,8 @@ typedef enum X86Seg { | CR4_OSFXSR_MASK | CR4_OSXMMEXCPT_MASK | CR4_UMIP_MASK \ | CR4_LA57_MASK \ | CR4_FSGSBASE_MASK | CR4_PCIDE_MASK | CR4_OSXSAVE_MASK \ - | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK)) + | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK \ + | CR4_LAM_SUP_MASK)) #define DR6_BD (1 << 13) #define DR6_BS (1 << 14) @@ -2563,6 +2565,9 @@ static inline uint64_t cr4_reserved_bits(CPUX86State *env) if (!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS)) { reserved_bits |= CR4_PKS_MASK; } + if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { + reserved_bits |= CR4_LAM_SUP_MASK; + } return reserved_bits; } diff --git a/target/i386/helper.c b/target/i386/helper.c index 48d1513a35..f9d1381f90 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -219,6 +219,10 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) new_cr4 &= ~CR4_PKS_MASK; } + if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { + new_cr4 &= ~CR4_LAM_SUP_MASK; + } + env->cr[4] = new_cr4; env->hflags = hflags; From 12f6b8280fa3a89db853bef8373ddc949dbfde6b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:13 +0800 Subject: [PATCH 0781/2884] i386/cpu: Fix i/d-cache topology to core level for Intel CPU For i-cache and d-cache, current QEMU hardcodes the maximum IDs for CPUs sharing cache (CPUID.04H.00H:EAX[bits 25:14] and CPUID.04H.01H:EAX[bits 25:14]) to 0, and this means i-cache and d-cache are shared in the SMT level. This is correct if there's single thread per core, but is wrong for the hyper threading case (one core contains multiple threads) since the i-cache and d-cache are shared in the core level other than SMT level. For AMD CPU, commit 8f4202fb1080 ("i386: Populate AMD Processor Cache Information for cpuid 0x8000001D") has already introduced i/d cache topology as core level by default. Therefore, in order to be compatible with both multi-threaded and single-threaded situations, we should set i-cache and d-cache be shared at the core level by default. This fix changes the default i/d cache topology from per-thread to per-core. Potentially, this change in L1 cache topology may affect the performance of the VM if the user does not specifically specify the topology or bind the vCPU. However, the way to achieve optimal performance should be to create a reasonable topology and set the appropriate vCPU affinity without relying on QEMU's default topology structure. Fixes: 7e3482f82480 ("i386: Helpers to encode cache information consistently") Suggested-by: Robert Hoo Signed-off-by: Zhao Liu Reviewed-by: Xiaoyao Li Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Message-ID: <20240424154929.1487382-6-zhao1.liu@intel.com> [Add compat property. - Paolo] Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/cpu.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4a2d6f5a97..6126bfdd2a 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -79,6 +79,7 @@ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, GlobalProperty pc_compat_9_0[] = { + { TYPE_X86_CPU, "x-l1-cache-per-thread", "false" }, { TYPE_X86_CPU, "guest-phys-bits", "0" }, { "sev-guest", "legacy-vm-type", "true" }, { TYPE_X86_CPU, "legacy-multi-node", "on" }, diff --git a/target/i386/cpu.c b/target/i386/cpu.c index de1ad7270c..3c66242f6d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6258,15 +6258,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; } else { *eax = 0; + int apic_ids_sharing_l1 = cpu->l1_cache_per_core ? cs->nr_threads : 1; switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - 1, cs->nr_cores, + apic_ids_sharing_l1, cs->nr_cores, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - 1, cs->nr_cores, + apic_ids_sharing_l1, cs->nr_cores, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ @@ -8105,6 +8106,7 @@ static Property x86_cpu_properties[] = { false), DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, true), + DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), DEFINE_PROP_END_OF_LIST() }; From 88dd4ca06c8392155289e5462cd26af3762a1b04 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:14 +0800 Subject: [PATCH 0782/2884] i386/cpu: Use APIC ID info to encode cache topo in CPUID[4] Refer to the fixes of cache_info_passthrough ([1], [2]) and SDM, the CPUID.04H:EAX[bits 25:14] and CPUID.04H:EAX[bits 31:26] should use the nearest power-of-2 integer. The nearest power-of-2 integer can be calculated by pow2ceil() or by using APIC ID offset/width (like L3 topology using 1 << die_offset [3]). But in fact, CPUID.04H:EAX[bits 25:14] and CPUID.04H:EAX[bits 31:26] are associated with APIC ID. For example, in linux kernel, the field "num_threads_sharing" (Bits 25 - 14) is parsed with APIC ID. And for another example, on Alder Lake P, the CPUID.04H:EAX[bits 31:26] is not matched with actual core numbers and it's calculated by: "(1 << (pkg_offset - core_offset)) - 1". Therefore the topology information of APIC ID should be preferred to calculate nearest power-of-2 integer for CPUID.04H:EAX[bits 25:14] and CPUID.04H:EAX[bits 31:26]: 1. d/i cache is shared in a core, 1 << core_offset should be used instead of "cs->nr_threads" in encode_cache_cpuid4() for CPUID.04H.00H:EAX[bits 25:14] and CPUID.04H.01H:EAX[bits 25:14]. 2. L2 cache is supposed to be shared in a core as for now, thereby 1 << core_offset should also be used instead of "cs->nr_threads" in encode_cache_cpuid4() for CPUID.04H.02H:EAX[bits 25:14]. 3. Similarly, the value for CPUID.04H:EAX[bits 31:26] should also be calculated with the bit width between the package and SMT levels in the APIC ID (1 << (pkg_offset - core_offset) - 1). In addition, use APIC ID bits calculations to replace "pow2ceil()" for cache_info_passthrough case. [1]: efb3934adf9e ("x86: cpu: make sure number of addressable IDs for processor cores meets the spec") [2]: d7caf13b5fcf ("x86: cpu: fixup number of addressable IDs for logical processors sharing cache") [3]: d65af288a84d ("i386: Update new x86_apicid parsing rules with die_offset support") Fixes: 7e3482f82480 ("i386: Helpers to encode cache information consistently") Suggested-by: Robert Hoo Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-7-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 50 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3c66242f6d..f3d2b8053b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6162,7 +6162,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, { X86CPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - uint32_t die_offset; uint32_t limit; uint32_t signature[3]; X86CPUTopoInfo topo_info; @@ -6234,7 +6233,18 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); break; - case 4: + case 4: { + /* + * CPUID.04H:EAX[bits 25:14]: Maximum number of addressable IDs for + * logical processors sharing this cache. + */ + int addressable_threads_width; + /* + * CPUID.04H:EAX[bits 31:26]: Maximum number of addressable IDs for + * processor cores in the physical package. + */ + int addressable_cores_width; + /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); @@ -6246,40 +6256,59 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); int vcpus_per_socket = cs->nr_cores * cs->nr_threads; if (cs->nr_cores > 1) { + addressable_cores_width = apicid_pkg_offset(&topo_info) - + apicid_core_offset(&topo_info); + *eax &= ~0xFC000000; - *eax |= (pow2ceil(cs->nr_cores) - 1) << 26; + *eax |= ((1 << addressable_cores_width) - 1) << 26; } if (host_vcpus_per_cache > vcpus_per_socket) { + /* Share the cache at package level. */ + addressable_threads_width = apicid_pkg_offset(&topo_info); + *eax &= ~0x3FFC000; - *eax |= (pow2ceil(vcpus_per_socket) - 1) << 14; + *eax |= ((1 << addressable_threads_width) - 1) << 14; } } } else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) { *eax = *ebx = *ecx = *edx = 0; } else { *eax = 0; - int apic_ids_sharing_l1 = cpu->l1_cache_per_core ? cs->nr_threads : 1; + addressable_cores_width = apicid_pkg_offset(&topo_info) - + apicid_core_offset(&topo_info); + switch (count) { case 0: /* L1 dcache info */ + addressable_threads_width = cpu->l1_cache_per_core + ? apicid_core_offset(&topo_info) + : 0; encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - apic_ids_sharing_l1, cs->nr_cores, + (1 << addressable_threads_width), + (1 << addressable_cores_width), eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ + addressable_threads_width = cpu->l1_cache_per_core + ? apicid_core_offset(&topo_info) + : 0; encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - apic_ids_sharing_l1, cs->nr_cores, + (1 << addressable_threads_width), + (1 << addressable_cores_width), eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ + addressable_threads_width = apicid_core_offset(&topo_info); encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - cs->nr_threads, cs->nr_cores, + (1 << addressable_threads_width), + (1 << addressable_cores_width), eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - die_offset = apicid_die_offset(&topo_info); if (cpu->enable_l3_cache) { + addressable_threads_width = apicid_die_offset(&topo_info); encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - (1 << die_offset), cs->nr_cores, + (1 << addressable_threads_width), + (1 << addressable_cores_width), eax, ebx, ecx, edx); break; } @@ -6290,6 +6319,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } break; + } case 5: /* MONITOR/MWAIT Leaf */ *eax = cpu->mwait.eax; /* Smallest monitor-line size in bytes */ From 9a085c4b4adeec648d6506316b8a42fbd0cf7c3c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:15 +0800 Subject: [PATCH 0783/2884] i386/cpu: Use APIC ID info get NumSharingCache for CPUID[0x8000001D].EAX[bits 25:14] The commit 8f4202fb1080 ("i386: Populate AMD Processor Cache Information for cpuid 0x8000001D") adds the cache topology for AMD CPU by encoding the number of sharing threads directly. From AMD's APM, NumSharingCache (CPUID[0x8000001D].EAX[bits 25:14]) means [1]: The number of logical processors sharing this cache is the value of this field incremented by 1. To determine which logical processors are sharing a cache, determine a Share Id for each processor as follows: ShareId = LocalApicId >> log2(NumSharingCache+1) Logical processors with the same ShareId then share a cache. If NumSharingCache+1 is not a power of two, round it up to the next power of two. From the description above, the calculation of this field should be same as CPUID[4].EAX[bits 25:14] for Intel CPUs. So also use the offsets of APIC ID to calculate this field. [1]: APM, vol.3, appendix.E.4.15 Function 8000_001Dh--Cache Topology Information Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Reviewed-by: Babu Moger Tested-by: Babu Moger Reviewed-by: Xiaoyao Li Message-ID: <20240424154929.1487382-8-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index f3d2b8053b..22a8ca1c9b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -331,7 +331,7 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { - uint32_t l3_threads; + uint32_t num_sharing_cache; assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); @@ -340,11 +340,11 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, /* L3 is shared among multiple cores */ if (cache->level == 3) { - l3_threads = topo_info->cores_per_die * topo_info->threads_per_core; - *eax |= (l3_threads - 1) << 14; + num_sharing_cache = 1 << apicid_die_offset(topo_info); } else { - *eax |= ((topo_info->threads_per_core - 1) << 14); + num_sharing_cache = 1 << apicid_core_offset(topo_info); } + *eax |= (num_sharing_cache - 1) << 14; assert(cache->line_size > 0); assert(cache->partitions > 0); From 2613747a794c4de8cd04e4a24001765220e91f1b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:16 +0800 Subject: [PATCH 0784/2884] i386/cpu: Consolidate the use of topo_info in cpu_x86_cpuid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cpu_x86_cpuid(), there are many variables in representing the cpu topology, e.g., topo_info, cs->nr_cores and cs->nr_threads. Since the names of cs->nr_cores and cs->nr_threads do not accurately represent its meaning, the use of cs->nr_cores or cs->nr_threads is prone to confusion and mistakes. And the structure X86CPUTopoInfo names its members clearly, thus the variable "topo_info" should be preferred. In addition, in cpu_x86_cpuid(), to uniformly use the topology variable, replace env->dies with topo_info.dies_per_pkg as well. Suggested-by: Robert Hoo Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Reviewed-by: Xiaoyao Li Reviewed-by: Philippe Mathieu-Daudé Tested-by: Babu Moger Message-ID: <20240424154929.1487382-9-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 22a8ca1c9b..363f5ee4be 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6165,11 +6165,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t limit; uint32_t signature[3]; X86CPUTopoInfo topo_info; + uint32_t cores_per_pkg; + uint32_t threads_per_pkg; topo_info.dies_per_pkg = env->nr_dies; topo_info.cores_per_die = cs->nr_cores / env->nr_dies; topo_info.threads_per_core = cs->nr_threads; + cores_per_pkg = topo_info.cores_per_die * topo_info.dies_per_pkg; + threads_per_pkg = cores_per_pkg * topo_info.threads_per_core; + /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { limit = env->cpuid_xlevel2; @@ -6205,8 +6210,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= CPUID_EXT_OSXSAVE; } *edx = env->features[FEAT_1_EDX]; - if (cs->nr_cores * cs->nr_threads > 1) { - *ebx |= (cs->nr_cores * cs->nr_threads) << 16; + if (threads_per_pkg > 1) { + *ebx |= threads_per_pkg << 16; *edx |= CPUID_HT; } if (!cpu->enable_pmu) { @@ -6254,15 +6259,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, */ if (*eax & 31) { int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); - int vcpus_per_socket = cs->nr_cores * cs->nr_threads; - if (cs->nr_cores > 1) { + + if (cores_per_pkg > 1) { addressable_cores_width = apicid_pkg_offset(&topo_info) - apicid_core_offset(&topo_info); *eax &= ~0xFC000000; *eax |= ((1 << addressable_cores_width) - 1) << 26; } - if (host_vcpus_per_cache > vcpus_per_socket) { + if (host_vcpus_per_cache > threads_per_pkg) { /* Share the cache at package level. */ addressable_threads_width = apicid_pkg_offset(&topo_info); @@ -6412,12 +6417,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: *eax = apicid_core_offset(&topo_info); - *ebx = cs->nr_threads; + *ebx = topo_info.threads_per_core; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: *eax = apicid_pkg_offset(&topo_info); - *ebx = cs->nr_cores * cs->nr_threads; + *ebx = threads_per_pkg; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; default: @@ -6437,7 +6442,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x1F: /* V2 Extended Topology Enumeration Leaf */ - if (env->nr_dies < 2) { + if (topo_info.dies_per_pkg < 2) { *eax = *ebx = *ecx = *edx = 0; break; } @@ -6447,7 +6452,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: *eax = apicid_core_offset(&topo_info); - *ebx = cs->nr_threads; + *ebx = topo_info.threads_per_core; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: @@ -6457,7 +6462,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 2: *eax = apicid_pkg_offset(&topo_info); - *ebx = cs->nr_cores * cs->nr_threads; + *ebx = threads_per_pkg; *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; break; default: @@ -6685,7 +6690,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * discards multiple thread information if it is set. * So don't set it here for Intel to make Linux guests happy. */ - if (cs->nr_cores * cs->nr_threads > 1) { + if (threads_per_pkg > 1) { if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { @@ -6752,7 +6757,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax |= (cpu->guest_phys_bits << 16); } *ebx = env->features[FEAT_8000_0008_EBX]; - if (cs->nr_cores * cs->nr_threads > 1) { + if (threads_per_pkg > 1) { /* * Bits 15:12 is "The number of bits in the initial * Core::X86::Apic::ApicId[ApicId] value that indicate @@ -6760,7 +6765,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * Bits 7:0 is "The number of threads in the package is NC+1" */ *ecx = (apicid_pkg_offset(&topo_info) << 12) | - ((cs->nr_cores * cs->nr_threads) - 1); + (threads_per_pkg - 1); } else { *ecx = 0; } From 6ddeb0ec8c29d51be49d5336c6d6508972b6d49c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:17 +0800 Subject: [PATCH 0785/2884] i386/cpu: Introduce bitmap to cache available CPU topology levels Currently, QEMU checks the specify number of topology domains to detect if there's extended topology levels (e.g., checking nr_dies). With this bitmap, the extended CPU topology (the levels other than SMT, core and package) could be easier to detect without touching the topology details. This is also in preparation for the follow-up to decouple CPUID[0x1F] subleaf with specific topology level. Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Reviewed-by: Xiaoyao Li Message-ID: <20240424154929.1487382-10-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 5 ++++- include/hw/i386/topology.h | 23 +++++++++++++++++++++++ target/i386/cpu.c | 18 +++++++++++++++--- target/i386/cpu.h | 4 ++++ target/i386/kvm/kvm.c | 3 ++- 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 67b03c913a..7d4f9b20f2 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -271,7 +271,10 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, init_topo_info(&topo_info, x86ms); - env->nr_dies = ms->smp.dies; + if (ms->smp.dies > 1) { + env->nr_dies = ms->smp.dies; + set_bit(CPU_TOPO_LEVEL_DIE, env->avail_cpu_topo); + } /* * If APIC ID is not set, diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index d4eeb7ab82..befeb92b0b 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -60,6 +60,21 @@ typedef struct X86CPUTopoInfo { unsigned threads_per_core; } X86CPUTopoInfo; +/* + * CPUTopoLevel is the general i386 topology hierarchical representation, + * ordered by increasing hierarchical relationship. + * Its enumeration value is not bound to the type value of Intel (CPUID[0x1F]) + * or AMD (CPUID[0x80000026]). + */ +enum CPUTopoLevel { + CPU_TOPO_LEVEL_INVALID, + CPU_TOPO_LEVEL_SMT, + CPU_TOPO_LEVEL_CORE, + CPU_TOPO_LEVEL_DIE, + CPU_TOPO_LEVEL_PACKAGE, + CPU_TOPO_LEVEL_MAX, +}; + /* Return the bit width needed for 'count' IDs */ static unsigned apicid_bitwidth_for_count(unsigned count) { @@ -168,4 +183,12 @@ static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info, return x86_apicid_from_topo_ids(topo_info, &topo_ids); } +/* + * Check whether there's extended topology level (die)? + */ +static inline bool x86_has_extended_topo(unsigned long *topo_bitmap) +{ + return test_bit(CPU_TOPO_LEVEL_DIE, topo_bitmap); +} + #endif /* HW_I386_TOPOLOGY_H */ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 363f5ee4be..8419055006 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6442,7 +6442,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x1F: /* V2 Extended Topology Enumeration Leaf */ - if (topo_info.dies_per_pkg < 2) { + if (!x86_has_extended_topo(env->avail_cpu_topo)) { *eax = *ebx = *ecx = *edx = 0; break; } @@ -7275,7 +7275,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) * cpu->vendor_cpuid_only has been unset for compatibility with older * machine types. */ - if ((env->nr_dies > 1) && + if (x86_has_extended_topo(env->avail_cpu_topo) && (IS_INTEL_CPU(env) || !cpu->vendor_cpuid_only)) { x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F); } @@ -7798,13 +7798,25 @@ static void x86_cpu_post_initfn(Object *obj) accel_cpu_instance_init(CPU(obj)); } +static void x86_cpu_init_default_topo(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + + env->nr_dies = 1; + + /* SMT, core and package levels are set by default. */ + set_bit(CPU_TOPO_LEVEL_SMT, env->avail_cpu_topo); + set_bit(CPU_TOPO_LEVEL_CORE, env->avail_cpu_topo); + set_bit(CPU_TOPO_LEVEL_PACKAGE, env->avail_cpu_topo); +} + static void x86_cpu_initfn(Object *obj) { X86CPU *cpu = X86_CPU(obj); X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); CPUX86State *env = &cpu->env; - env->nr_dies = 1; + x86_cpu_init_default_topo(cpu); object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo", x86_cpu_get_feature_words, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ccf6811794..9e7b9e918e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -24,6 +24,7 @@ #include "cpu-qom.h" #include "kvm/hyperv-proto.h" #include "exec/cpu-defs.h" +#include "hw/i386/topology.h" #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" #include "qemu/timer.h" @@ -1891,6 +1892,9 @@ typedef struct CPUArchState { /* Number of dies within this CPU package. */ unsigned nr_dies; + + /* Bitmap of available CPU topology levels for this CPU. */ + DECLARE_BITMAP(avail_cpu_topo, CPU_TOPO_LEVEL_MAX); } CPUX86State; struct kvm_msrs; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c5943605ee..6c864e4611 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -51,6 +51,7 @@ #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" #include "hw/i386/intel_iommu.h" +#include "hw/i386/topology.h" #include "hw/i386/x86-iommu.h" #include "hw/i386/e820_memory_layout.h" @@ -1791,7 +1792,7 @@ static uint32_t kvm_x86_build_cpuid(CPUX86State *env, break; } case 0x1f: - if (env->nr_dies < 2) { + if (!x86_has_extended_topo(env->avail_cpu_topo)) { cpuid_i--; break; } From 0f6ed7ba135a45a4a28bddda74d1bf0061174b98 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:18 +0800 Subject: [PATCH 0786/2884] i386: Split topology types of CPUID[0x1F] from the definitions of CPUID[0xB] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPUID[0xB] defines SMT, Core and Invalid types, and this leaf is shared by Intel and AMD CPUs. But for extended topology levels, Intel CPU (in CPUID[0x1F]) and AMD CPU (in CPUID[0x80000026]) have the different definitions with different enumeration values. Though CPUID[0x80000026] hasn't been implemented in QEMU, to avoid possible misunderstanding, split topology types of CPUID[0x1F] from the definitions of CPUID[0xB] and introduce CPUID[0x1F]-specific topology types. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé Tested-by: Babu Moger Message-ID: <20240424154929.1487382-11-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 14 +++++++------- target/i386/cpu.h | 13 +++++++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8419055006..d350eb8a73 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6418,17 +6418,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 0: *eax = apicid_core_offset(&topo_info); *ebx = topo_info.threads_per_core; - *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; + *ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8; break; case 1: *eax = apicid_pkg_offset(&topo_info); *ebx = threads_per_pkg; - *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; + *ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8; break; default: *eax = 0; *ebx = 0; - *ecx |= CPUID_TOPOLOGY_LEVEL_INVALID; + *ecx |= CPUID_B_ECX_TOPO_LEVEL_INVALID << 8; } assert(!(*eax & ~0x1f)); @@ -6453,22 +6453,22 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 0: *eax = apicid_core_offset(&topo_info); *ebx = topo_info.threads_per_core; - *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; + *ecx |= CPUID_1F_ECX_TOPO_LEVEL_SMT << 8; break; case 1: *eax = apicid_die_offset(&topo_info); *ebx = topo_info.cores_per_die * topo_info.threads_per_core; - *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; + *ecx |= CPUID_1F_ECX_TOPO_LEVEL_CORE << 8; break; case 2: *eax = apicid_pkg_offset(&topo_info); *ebx = threads_per_pkg; - *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; + *ecx |= CPUID_1F_ECX_TOPO_LEVEL_DIE << 8; break; default: *eax = 0; *ebx = 0; - *ecx |= CPUID_TOPOLOGY_LEVEL_INVALID; + *ecx |= CPUID_1F_ECX_TOPO_LEVEL_INVALID << 8; } assert(!(*eax & ~0x1f)); *ebx &= 0xffff; /* The count doesn't need to be reliable. */ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9e7b9e918e..8c83900202 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1016,10 +1016,15 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */ /* CPUID[0xB].ECX level types */ -#define CPUID_TOPOLOGY_LEVEL_INVALID (0U << 8) -#define CPUID_TOPOLOGY_LEVEL_SMT (1U << 8) -#define CPUID_TOPOLOGY_LEVEL_CORE (2U << 8) -#define CPUID_TOPOLOGY_LEVEL_DIE (5U << 8) +#define CPUID_B_ECX_TOPO_LEVEL_INVALID 0 +#define CPUID_B_ECX_TOPO_LEVEL_SMT 1 +#define CPUID_B_ECX_TOPO_LEVEL_CORE 2 + +/* COUID[0x1F].ECX level types */ +#define CPUID_1F_ECX_TOPO_LEVEL_INVALID CPUID_B_ECX_TOPO_LEVEL_INVALID +#define CPUID_1F_ECX_TOPO_LEVEL_SMT CPUID_B_ECX_TOPO_LEVEL_SMT +#define CPUID_1F_ECX_TOPO_LEVEL_CORE CPUID_B_ECX_TOPO_LEVEL_CORE +#define CPUID_1F_ECX_TOPO_LEVEL_DIE 5 /* MSR Feature Bits */ #define MSR_ARCH_CAP_RDCL_NO (1U << 0) From 822bce9f58df7ab46f70abc9c350341d5280c91a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:19 +0800 Subject: [PATCH 0787/2884] i386/cpu: Decouple CPUID[0x1F] subleaf with specific topology level At present, the subleaf 0x02 of CPUID[0x1F] is bound to the "die" level. In fact, the specific topology level exposed in 0x1F depends on the platform's support for extension levels (module, tile and die). To help expose "module" level in 0x1F, decouple CPUID[0x1F] subleaf with specific topology level. Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Reviewed-by: Xiaoyao Li Message-ID: <20240424154929.1487382-12-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 135 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 25 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d350eb8a73..f95d539eef 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -269,6 +269,115 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache, (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); } +static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, + enum CPUTopoLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPO_LEVEL_SMT: + return 1; + case CPU_TOPO_LEVEL_CORE: + return topo_info->threads_per_core; + case CPU_TOPO_LEVEL_DIE: + return topo_info->threads_per_core * topo_info->cores_per_die; + case CPU_TOPO_LEVEL_PACKAGE: + return topo_info->threads_per_core * topo_info->cores_per_die * + topo_info->dies_per_pkg; + default: + g_assert_not_reached(); + } + return 0; +} + +static uint32_t apicid_offset_by_topo_level(X86CPUTopoInfo *topo_info, + enum CPUTopoLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPO_LEVEL_SMT: + return 0; + case CPU_TOPO_LEVEL_CORE: + return apicid_core_offset(topo_info); + case CPU_TOPO_LEVEL_DIE: + return apicid_die_offset(topo_info); + case CPU_TOPO_LEVEL_PACKAGE: + return apicid_pkg_offset(topo_info); + default: + g_assert_not_reached(); + } + return 0; +} + +static uint32_t cpuid1f_topo_type(enum CPUTopoLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPO_LEVEL_INVALID: + return CPUID_1F_ECX_TOPO_LEVEL_INVALID; + case CPU_TOPO_LEVEL_SMT: + return CPUID_1F_ECX_TOPO_LEVEL_SMT; + case CPU_TOPO_LEVEL_CORE: + return CPUID_1F_ECX_TOPO_LEVEL_CORE; + case CPU_TOPO_LEVEL_DIE: + return CPUID_1F_ECX_TOPO_LEVEL_DIE; + default: + /* Other types are not supported in QEMU. */ + g_assert_not_reached(); + } + return 0; +} + +static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, + X86CPUTopoInfo *topo_info, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + X86CPU *cpu = env_archcpu(env); + unsigned long level, next_level; + uint32_t num_threads_next_level, offset_next_level; + + assert(count + 1 < CPU_TOPO_LEVEL_MAX); + + /* + * Find the No.(count + 1) topology level in avail_cpu_topo bitmap. + * The search starts from bit 1 (CPU_TOPO_LEVEL_INVALID + 1). + */ + level = CPU_TOPO_LEVEL_INVALID; + for (int i = 0; i <= count; i++) { + level = find_next_bit(env->avail_cpu_topo, + CPU_TOPO_LEVEL_PACKAGE, + level + 1); + + /* + * CPUID[0x1f] doesn't explicitly encode the package level, + * and it just encodes the invalid level (all fields are 0) + * into the last subleaf of 0x1f. + */ + if (level == CPU_TOPO_LEVEL_PACKAGE) { + level = CPU_TOPO_LEVEL_INVALID; + break; + } + } + + if (level == CPU_TOPO_LEVEL_INVALID) { + num_threads_next_level = 0; + offset_next_level = 0; + } else { + next_level = find_next_bit(env->avail_cpu_topo, + CPU_TOPO_LEVEL_PACKAGE, + level + 1); + num_threads_next_level = num_threads_by_topo_level(topo_info, + next_level); + offset_next_level = apicid_offset_by_topo_level(topo_info, + next_level); + } + + *eax = offset_next_level; + /* The count (bits 15-00) doesn't need to be reliable. */ + *ebx = num_threads_next_level & 0xffff; + *ecx = (count & 0xff) | (cpuid1f_topo_type(level) << 8); + *edx = cpu->apic_id; + + assert(!(*eax & ~0x1f)); +} + /* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) { @@ -6447,31 +6556,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } - *ecx = count & 0xff; - *edx = cpu->apic_id; - switch (count) { - case 0: - *eax = apicid_core_offset(&topo_info); - *ebx = topo_info.threads_per_core; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_SMT << 8; - break; - case 1: - *eax = apicid_die_offset(&topo_info); - *ebx = topo_info.cores_per_die * topo_info.threads_per_core; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_CORE << 8; - break; - case 2: - *eax = apicid_pkg_offset(&topo_info); - *ebx = threads_per_pkg; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_DIE << 8; - break; - default: - *eax = 0; - *ebx = 0; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_INVALID << 8; - } - assert(!(*eax & ~0x1f)); - *ebx &= 0xffff; /* The count doesn't need to be reliable. */ + encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx); break; case 0xD: { /* Processor Extended State */ From 81c392ab5c7489955d7e2b515b7186a4cd174c71 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:20 +0800 Subject: [PATCH 0788/2884] i386: Introduce module level cpu topology to CPUX86State Intel CPUs implement module level on hybrid client products (e.g., ADL-N, MTL, etc) and E-core server products. A module contains a set of cores that share certain resources (in current products, the resource usually includes L2 cache, as well as module scoped features and MSRs). Module level support is the prerequisite for L2 cache topology on module level. With module level, we can implement the Guest's CPU topology and future cache topology to be consistent with the Host's on Intel hybrid client/E-core server platforms. Tested-by: Yongwei Ma Co-developed-by: Zhuocheng Ding Signed-off-by: Zhuocheng Ding Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-13-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 5 +++++ target/i386/cpu.c | 1 + target/i386/cpu.h | 3 +++ 3 files changed, 9 insertions(+) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 7d4f9b20f2..994f842488 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -271,6 +271,11 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, init_topo_info(&topo_info, x86ms); + if (ms->smp.modules > 1) { + env->nr_modules = ms->smp.modules; + /* TODO: Expose module level in CPUID[0x1F]. */ + } + if (ms->smp.dies > 1) { env->nr_dies = ms->smp.dies; set_bit(CPU_TOPO_LEVEL_DIE, env->avail_cpu_topo); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index f95d539eef..eb1642c253 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7887,6 +7887,7 @@ static void x86_cpu_init_default_topo(X86CPU *cpu) { CPUX86State *env = &cpu->env; + env->nr_modules = 1; env->nr_dies = 1; /* SMT, core and package levels are set by default. */ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8c83900202..e79293158a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1898,6 +1898,9 @@ typedef struct CPUArchState { /* Number of dies within this CPU package. */ unsigned nr_dies; + /* Number of modules within one die. */ + unsigned nr_modules; + /* Bitmap of available CPU topology levels for this CPU. */ DECLARE_BITMAP(avail_cpu_topo, CPU_TOPO_LEVEL_MAX); } CPUX86State; From 3568adc995b3906b5cc134753a829363f08bf6e1 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:21 +0800 Subject: [PATCH 0789/2884] i386: Support modules_per_die in X86CPUTopoInfo Support module level in i386 cpu topology structure "X86CPUTopoInfo". Since x86 does not yet support the "modules" parameter in "-smp", X86CPUTopoInfo.modules_per_die is currently always 1. Therefore, the module level width in APIC ID, which can be calculated by "apicid_bitwidth_for_count(topo_info->modules_per_die)", is always 0 for now, so we can directly add APIC ID related helpers to support module level parsing. In addition, update topology structure in test-x86-topo.c. Tested-by: Yongwei Ma Co-developed-by: Zhuocheng Ding Signed-off-by: Zhuocheng Ding Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-14-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86.c | 9 +++++++- include/hw/i386/topology.h | 22 +++++++++++++++---- target/i386/cpu.c | 13 ++++++----- tests/unit/test-x86-topo.c | 45 ++++++++++++++++++++------------------ 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 0b5cc59956..d5d668e814 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -45,7 +45,14 @@ void init_topo_info(X86CPUTopoInfo *topo_info, MachineState *ms = MACHINE(x86ms); topo_info->dies_per_pkg = ms->smp.dies; - topo_info->cores_per_die = ms->smp.cores; + /* + * Though smp.modules means the number of modules in one cluster, + * i386 doesn't support cluster level so that the smp.clusters + * always defaults to 1, therefore using smp.modules directly is + * fine here. + */ + topo_info->modules_per_die = ms->smp.modules; + topo_info->cores_per_module = ms->smp.cores; topo_info->threads_per_core = ms->smp.threads; } diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index befeb92b0b..7622d80693 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -56,7 +56,8 @@ typedef struct X86CPUTopoIDs { typedef struct X86CPUTopoInfo { unsigned dies_per_pkg; - unsigned cores_per_die; + unsigned modules_per_die; + unsigned cores_per_module; unsigned threads_per_core; } X86CPUTopoInfo; @@ -92,7 +93,13 @@ static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info) /* Bit width of the Core_ID field */ static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(topo_info->cores_per_die); + return apicid_bitwidth_for_count(topo_info->cores_per_module); +} + +/* Bit width of the Module_ID field */ +static inline unsigned apicid_module_width(X86CPUTopoInfo *topo_info) +{ + return apicid_bitwidth_for_count(topo_info->modules_per_die); } /* Bit width of the Die_ID field */ @@ -107,10 +114,16 @@ static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info) return apicid_smt_width(topo_info); } +/* Bit offset of the Module_ID field */ +static inline unsigned apicid_module_offset(X86CPUTopoInfo *topo_info) +{ + return apicid_core_offset(topo_info) + apicid_core_width(topo_info); +} + /* Bit offset of the Die_ID field */ static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) { - return apicid_core_offset(topo_info) + apicid_core_width(topo_info); + return apicid_module_offset(topo_info) + apicid_module_width(topo_info); } /* Bit offset of the Pkg_ID (socket ID) field */ @@ -142,7 +155,8 @@ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, X86CPUTopoIDs *topo_ids) { unsigned nr_dies = topo_info->dies_per_pkg; - unsigned nr_cores = topo_info->cores_per_die; + unsigned nr_cores = topo_info->cores_per_module * + topo_info->modules_per_die; unsigned nr_threads = topo_info->threads_per_core; topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index eb1642c253..92aa449855 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -278,10 +278,11 @@ static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, case CPU_TOPO_LEVEL_CORE: return topo_info->threads_per_core; case CPU_TOPO_LEVEL_DIE: - return topo_info->threads_per_core * topo_info->cores_per_die; + return topo_info->threads_per_core * topo_info->cores_per_module * + topo_info->modules_per_die; case CPU_TOPO_LEVEL_PACKAGE: - return topo_info->threads_per_core * topo_info->cores_per_die * - topo_info->dies_per_pkg; + return topo_info->threads_per_core * topo_info->cores_per_module * + topo_info->modules_per_die * topo_info->dies_per_pkg; default: g_assert_not_reached(); } @@ -6278,10 +6279,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t threads_per_pkg; topo_info.dies_per_pkg = env->nr_dies; - topo_info.cores_per_die = cs->nr_cores / env->nr_dies; + topo_info.modules_per_die = env->nr_modules; + topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules; topo_info.threads_per_core = cs->nr_threads; - cores_per_pkg = topo_info.cores_per_die * topo_info.dies_per_pkg; + cores_per_pkg = topo_info.cores_per_module * topo_info.modules_per_die * + topo_info.dies_per_pkg; threads_per_pkg = cores_per_pkg * topo_info.threads_per_core; /* Calculate & apply limits for different index ranges */ diff --git a/tests/unit/test-x86-topo.c b/tests/unit/test-x86-topo.c index 2b104f86d7..f21b8a5d95 100644 --- a/tests/unit/test-x86-topo.c +++ b/tests/unit/test-x86-topo.c @@ -30,13 +30,16 @@ static void test_topo_bits(void) { X86CPUTopoInfo topo_info = {0}; - /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ - topo_info = (X86CPUTopoInfo) {1, 1, 1}; + /* + * simple tests for 1 thread per core, 1 core per module, + * 1 module per die, 1 die per package + */ + topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - topo_info = (X86CPUTopoInfo) {1, 1, 1}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); @@ -45,39 +48,39 @@ static void test_topo_bits(void) /* Test field width calculation for multiple values */ - topo_info = (X86CPUTopoInfo) {1, 1, 2}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 2}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {1, 1, 3}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 3}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {1, 1, 4}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 4}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {1, 1, 14}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 14}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 15}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 15}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 16}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 16}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 17}; + topo_info = (X86CPUTopoInfo) {1, 1, 1, 17}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 30, 2}; + topo_info = (X86CPUTopoInfo) {1, 1, 30, 2}; g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 31, 2}; + topo_info = (X86CPUTopoInfo) {1, 1, 31, 2}; g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 32, 2}; + topo_info = (X86CPUTopoInfo) {1, 1, 32, 2}; g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 33, 2}; + topo_info = (X86CPUTopoInfo) {1, 1, 33, 2}; g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); - topo_info = (X86CPUTopoInfo) {1, 30, 2}; + topo_info = (X86CPUTopoInfo) {1, 1, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - topo_info = (X86CPUTopoInfo) {2, 30, 2}; + topo_info = (X86CPUTopoInfo) {2, 1, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {3, 30, 2}; + topo_info = (X86CPUTopoInfo) {3, 1, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {4, 30, 2}; + topo_info = (X86CPUTopoInfo) {4, 1, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); /* build a weird topology and see if IDs are calculated correctly @@ -85,18 +88,18 @@ static void test_topo_bits(void) /* This will use 2 bits for thread ID and 3 bits for core ID */ - topo_info = (X86CPUTopoInfo) {1, 6, 3}; + topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 6, 3}; + topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); - topo_info = (X86CPUTopoInfo) {1, 6, 3}; + topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, (1 << 2) | 0); g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, From 5304873acd12f7a4ddb07d3560ac5ac02a0e1060 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:22 +0800 Subject: [PATCH 0790/2884] i386: Expose module level in CPUID[0x1F] Linux kernel (from v6.4, with commit edc0a2b595765 ("x86/topology: Fix erroneous smp_num_siblings on Intel Hybrid platforms") is able to handle platforms with Module level enumerated via CPUID.1F. Expose the module level in CPUID[0x1F] if the machine has more than 1 modules. Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-15-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 2 +- include/hw/i386/topology.h | 6 ++++-- target/i386/cpu.c | 6 ++++++ target/i386/cpu.h | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 994f842488..34dfa89487 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -273,7 +273,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, if (ms->smp.modules > 1) { env->nr_modules = ms->smp.modules; - /* TODO: Expose module level in CPUID[0x1F]. */ + set_bit(CPU_TOPO_LEVEL_MODULE, env->avail_cpu_topo); } if (ms->smp.dies > 1) { diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 7622d80693..ea87104577 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -71,6 +71,7 @@ enum CPUTopoLevel { CPU_TOPO_LEVEL_INVALID, CPU_TOPO_LEVEL_SMT, CPU_TOPO_LEVEL_CORE, + CPU_TOPO_LEVEL_MODULE, CPU_TOPO_LEVEL_DIE, CPU_TOPO_LEVEL_PACKAGE, CPU_TOPO_LEVEL_MAX, @@ -198,11 +199,12 @@ static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info, } /* - * Check whether there's extended topology level (die)? + * Check whether there's extended topology level (module or die)? */ static inline bool x86_has_extended_topo(unsigned long *topo_bitmap) { - return test_bit(CPU_TOPO_LEVEL_DIE, topo_bitmap); + return test_bit(CPU_TOPO_LEVEL_MODULE, topo_bitmap) || + test_bit(CPU_TOPO_LEVEL_DIE, topo_bitmap); } #endif /* HW_I386_TOPOLOGY_H */ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 92aa449855..4aea6e3dd2 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -277,6 +277,8 @@ static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, return 1; case CPU_TOPO_LEVEL_CORE: return topo_info->threads_per_core; + case CPU_TOPO_LEVEL_MODULE: + return topo_info->threads_per_core * topo_info->cores_per_module; case CPU_TOPO_LEVEL_DIE: return topo_info->threads_per_core * topo_info->cores_per_module * topo_info->modules_per_die; @@ -297,6 +299,8 @@ static uint32_t apicid_offset_by_topo_level(X86CPUTopoInfo *topo_info, return 0; case CPU_TOPO_LEVEL_CORE: return apicid_core_offset(topo_info); + case CPU_TOPO_LEVEL_MODULE: + return apicid_module_offset(topo_info); case CPU_TOPO_LEVEL_DIE: return apicid_die_offset(topo_info); case CPU_TOPO_LEVEL_PACKAGE: @@ -316,6 +320,8 @@ static uint32_t cpuid1f_topo_type(enum CPUTopoLevel topo_level) return CPUID_1F_ECX_TOPO_LEVEL_SMT; case CPU_TOPO_LEVEL_CORE: return CPUID_1F_ECX_TOPO_LEVEL_CORE; + case CPU_TOPO_LEVEL_MODULE: + return CPUID_1F_ECX_TOPO_LEVEL_MODULE; case CPU_TOPO_LEVEL_DIE: return CPUID_1F_ECX_TOPO_LEVEL_DIE; default: diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e79293158a..196b068614 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1024,6 +1024,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_1F_ECX_TOPO_LEVEL_INVALID CPUID_B_ECX_TOPO_LEVEL_INVALID #define CPUID_1F_ECX_TOPO_LEVEL_SMT CPUID_B_ECX_TOPO_LEVEL_SMT #define CPUID_1F_ECX_TOPO_LEVEL_CORE CPUID_B_ECX_TOPO_LEVEL_CORE +#define CPUID_1F_ECX_TOPO_LEVEL_MODULE 3 #define CPUID_1F_ECX_TOPO_LEVEL_DIE 5 /* MSR Feature Bits */ From b17a26bc4b0289ca7415f821bb0a89a8a333cea5 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:23 +0800 Subject: [PATCH 0791/2884] i386: Support module_id in X86CPUTopoIDs Add module_id member in X86CPUTopoIDs. module_id can be parsed from APIC ID, so also update APIC ID parsing rule to support module level. With this support, the conversions with module level between X86CPUTopoIDs, X86CPUTopoInfo and APIC ID are completed. module_id can be also generated from cpu topology, and before i386 supports "modules" in smp, the default "modules per die" (modules * clusters) is only 1, thus the module_id generated in this way is 0, so that it will not conflict with the module_id generated by APIC ID. Tested-by: Yongwei Ma Signed-off-by: Zhuocheng Ding Co-developed-by: Zhuocheng Ding Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-16-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 27 +++++++++++++++++---------- hw/i386/x86.c | 4 ++++ include/hw/i386/topology.h | 17 +++++++++++++---- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 34dfa89487..4d27279901 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -283,12 +283,9 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, /* * If APIC ID is not set, - * set it based on socket/die/core/thread properties. + * set it based on socket/die/module/core/thread properties. */ if (cpu->apic_id == UNASSIGNED_APIC_ID) { - int max_socket = (ms->smp.max_cpus - 1) / - smp_threads / smp_cores / ms->smp.dies; - /* * die-id was optional in QEMU 4.0 and older, so keep it optional * if there's only one die per socket. @@ -300,9 +297,9 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, if (cpu->socket_id < 0) { error_setg(errp, "CPU socket-id is not set"); return; - } else if (cpu->socket_id > max_socket) { + } else if (cpu->socket_id > ms->smp.sockets - 1) { error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", - cpu->socket_id, max_socket); + cpu->socket_id, ms->smp.sockets - 1); return; } if (cpu->die_id < 0) { @@ -334,17 +331,27 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, topo_ids.die_id = cpu->die_id; topo_ids.core_id = cpu->core_id; topo_ids.smt_id = cpu->thread_id; + + /* + * TODO: This is the temporary initialization for topo_ids.module_id to + * avoid "maybe-uninitialized" compilation errors. Will remove when + * X86CPU supports module_id. + */ + topo_ids.module_id = 0; + cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); } cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); if (!cpu_slot) { x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + error_setg(errp, - "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" - " APIC ID %" PRIu32 ", valid index range 0:%d", - topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, - cpu->apic_id, ms->possible_cpus->len - 1); + "Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]" + " with APIC ID %" PRIu32 ", valid index range 0:%d", + topo_ids.pkg_id, topo_ids.die_id, topo_ids.module_id, + topo_ids.core_id, topo_ids.smt_id, cpu->apic_id, + ms->possible_cpus->len - 1); return; } diff --git a/hw/i386/x86.c b/hw/i386/x86.c index d5d668e814..a4aa8e0810 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -135,6 +135,10 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->cpus[i].props.has_die_id = true; ms->possible_cpus->cpus[i].props.die_id = topo_ids.die_id; } + if (ms->smp.modules > 1) { + ms->possible_cpus->cpus[i].props.has_module_id = true; + ms->possible_cpus->cpus[i].props.module_id = topo_ids.module_id; + } ms->possible_cpus->cpus[i].props.has_core_id = true; ms->possible_cpus->cpus[i].props.core_id = topo_ids.core_id; ms->possible_cpus->cpus[i].props.has_thread_id = true; diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index ea87104577..dff49fce11 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -50,6 +50,7 @@ typedef uint32_t apic_id_t; typedef struct X86CPUTopoIDs { unsigned pkg_id; unsigned die_id; + unsigned module_id; unsigned core_id; unsigned smt_id; } X86CPUTopoIDs; @@ -143,6 +144,7 @@ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, { return (topo_ids->pkg_id << apicid_pkg_offset(topo_info)) | (topo_ids->die_id << apicid_die_offset(topo_info)) | + (topo_ids->module_id << apicid_module_offset(topo_info)) | (topo_ids->core_id << apicid_core_offset(topo_info)) | topo_ids->smt_id; } @@ -156,12 +158,16 @@ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, X86CPUTopoIDs *topo_ids) { unsigned nr_dies = topo_info->dies_per_pkg; - unsigned nr_cores = topo_info->cores_per_module * - topo_info->modules_per_die; + unsigned nr_modules = topo_info->modules_per_die; + unsigned nr_cores = topo_info->cores_per_module; unsigned nr_threads = topo_info->threads_per_core; - topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); - topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; + topo_ids->pkg_id = cpu_index / (nr_dies * nr_modules * + nr_cores * nr_threads); + topo_ids->die_id = cpu_index / (nr_modules * nr_cores * + nr_threads) % nr_dies; + topo_ids->module_id = cpu_index / (nr_cores * nr_threads) % + nr_modules; topo_ids->core_id = cpu_index / nr_threads % nr_cores; topo_ids->smt_id = cpu_index % nr_threads; } @@ -179,6 +185,9 @@ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, topo_ids->core_id = (apicid >> apicid_core_offset(topo_info)) & ~(0xFFFFFFFFUL << apicid_core_width(topo_info)); + topo_ids->module_id = + (apicid >> apicid_module_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_module_width(topo_info)); topo_ids->die_id = (apicid >> apicid_die_offset(topo_info)) & ~(0xFFFFFFFFUL << apicid_die_width(topo_info)); From 588208346f92e6030ae1d4bee94096ca0e070b60 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:24 +0800 Subject: [PATCH 0792/2884] i386/cpu: Introduce module-id to X86CPU Introduce module-id to be consistent with the module-id field in CpuInstanceProperties. Following the legacy smp check rules, also add the module_id validity into x86_cpu_pre_plug(). Tested-by: Yongwei Ma Co-developed-by: Zhuocheng Ding Signed-off-by: Zhuocheng Ding Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-17-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 33 +++++++++++++++++++++++++-------- target/i386/cpu.c | 2 ++ target/i386/cpu.h | 1 + 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 4d27279901..ee9046d9a8 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -294,6 +294,14 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, cpu->die_id = 0; } + /* + * module-id was optional in QEMU 9.0 and older, so keep it optional + * if there's only one module per die. + */ + if (cpu->module_id < 0 && ms->smp.modules == 1) { + cpu->module_id = 0; + } + if (cpu->socket_id < 0) { error_setg(errp, "CPU socket-id is not set"); return; @@ -310,6 +318,14 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, cpu->die_id, ms->smp.dies - 1); return; } + if (cpu->module_id < 0) { + error_setg(errp, "CPU module-id is not set"); + return; + } else if (cpu->module_id > ms->smp.modules - 1) { + error_setg(errp, "Invalid CPU module-id: %u must be in range 0:%u", + cpu->module_id, ms->smp.modules - 1); + return; + } if (cpu->core_id < 0) { error_setg(errp, "CPU core-id is not set"); return; @@ -329,16 +345,9 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, topo_ids.pkg_id = cpu->socket_id; topo_ids.die_id = cpu->die_id; + topo_ids.module_id = cpu->module_id; topo_ids.core_id = cpu->core_id; topo_ids.smt_id = cpu->thread_id; - - /* - * TODO: This is the temporary initialization for topo_ids.module_id to - * avoid "maybe-uninitialized" compilation errors. Will remove when - * X86CPU supports module_id. - */ - topo_ids.module_id = 0; - cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); } @@ -383,6 +392,14 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, } cpu->die_id = topo_ids.die_id; + if (cpu->module_id != -1 && cpu->module_id != topo_ids.module_id) { + error_setg(errp, "property module-id: %u doesn't match set apic-id:" + " 0x%x (module-id: %u)", cpu->module_id, cpu->apic_id, + topo_ids.module_id); + return; + } + cpu->module_id = topo_ids.module_id; + if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { error_setg(errp, "property core-id: %u doesn't match set apic-id:" " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4aea6e3dd2..4d811130b1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8118,12 +8118,14 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, 0), DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, 0), DEFINE_PROP_INT32("core-id", X86CPU, core_id, 0), + DEFINE_PROP_INT32("module-id", X86CPU, module_id, 0), DEFINE_PROP_INT32("die-id", X86CPU, die_id, 0), DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, 0), #else DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, UNASSIGNED_APIC_ID), DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, -1), DEFINE_PROP_INT32("core-id", X86CPU, core_id, -1), + DEFINE_PROP_INT32("module-id", X86CPU, module_id, -1), DEFINE_PROP_INT32("die-id", X86CPU, die_id, -1), DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, -1), #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 196b068614..269c30c291 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2064,6 +2064,7 @@ struct ArchCPU { int32_t node_id; /* NUMA node this CPU belongs to */ int32_t socket_id; int32_t die_id; + int32_t module_id; int32_t core_id; int32_t thread_id; From 321d2599ebf9d27fbc41c672858048087f6a052d Mon Sep 17 00:00:00 2001 From: Zhuocheng Ding Date: Wed, 24 Apr 2024 23:49:25 +0800 Subject: [PATCH 0793/2884] tests: Add test case of APIC ID for module level parsing After i386 supports module level, it's time to add the test for module level's parsing. Signed-off-by: Zhuocheng Ding Co-developed-by: Zhao Liu Signed-off-by: Zhao Liu Reviewed-by: Yanan Wang Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Message-ID: <20240424154929.1487382-18-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- tests/unit/test-x86-topo.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/unit/test-x86-topo.c b/tests/unit/test-x86-topo.c index f21b8a5d95..55b731ccae 100644 --- a/tests/unit/test-x86-topo.c +++ b/tests/unit/test-x86-topo.c @@ -37,6 +37,7 @@ static void test_topo_bits(void) topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 0); g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; @@ -74,13 +75,22 @@ static void test_topo_bits(void) topo_info = (X86CPUTopoInfo) {1, 1, 33, 2}; g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); - topo_info = (X86CPUTopoInfo) {1, 1, 30, 2}; + topo_info = (X86CPUTopoInfo) {1, 6, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3); + topo_info = (X86CPUTopoInfo) {1, 7, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3); + topo_info = (X86CPUTopoInfo) {1, 8, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3); + topo_info = (X86CPUTopoInfo) {1, 9, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 4); + + topo_info = (X86CPUTopoInfo) {1, 6, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - topo_info = (X86CPUTopoInfo) {2, 1, 30, 2}; + topo_info = (X86CPUTopoInfo) {2, 6, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {3, 1, 30, 2}; + topo_info = (X86CPUTopoInfo) {3, 6, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {4, 1, 30, 2}; + topo_info = (X86CPUTopoInfo) {4, 6, 30, 2}; g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); /* build a weird topology and see if IDs are calculated correctly @@ -91,6 +101,7 @@ static void test_topo_bits(void) topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); + g_assert_cmpuint(apicid_module_offset(&topo_info), ==, 5); g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); From 6807487474165ddffd1016af1bf167e5c15e71bf Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:26 +0800 Subject: [PATCH 0794/2884] hw/i386/pc: Support smp.modules for x86 PC machine As module-level topology support is added to X86CPU, now we can enable the support for the modules parameter on PC machines. With this support, we can define a 5-level x86 CPU topology with "-smp": -smp cpus=*,maxcpus=*,sockets=*,dies=*,modules=*,cores=*,threads=*. So, add the 5-level topology example in description of "-smp". Additionally, add the missed drawers and books options in previous example. Tested-by: Yongwei Ma Co-developed-by: Zhuocheng Ding Signed-off-by: Zhuocheng Ding Signed-off-by: Zhao Liu Tested-by: Babu Moger Reviewed-by: Babu Moger Message-ID: <20240424154929.1487382-19-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + qemu-options.hx | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 6126bfdd2a..7b638da7aa 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1843,6 +1843,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = true; mc->smp_props.dies_supported = true; + mc->smp_props.modules_supported = true; mc->default_ram_id = "pc.ram"; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; diff --git a/qemu-options.hx b/qemu-options.hx index 4d19660336..8ca7f34ef0 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -281,7 +281,8 @@ ERST DEF("smp", HAS_ARG, QEMU_OPTION_smp, "-smp [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets]\n" - " [,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]\n" + " [,dies=dies][,clusters=clusters][,modules=modules][,cores=cores]\n" + " [,threads=threads]\n" " set the number of initial CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total CPUs, including\n" " offline CPUs for hotplug, etc\n" @@ -290,7 +291,8 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp, " sockets= number of sockets in one book\n" " dies= number of dies in one socket\n" " clusters= number of clusters in one die\n" - " cores= number of cores in one cluster\n" + " modules= number of modules in one cluster\n" + " cores= number of cores in one module\n" " threads= number of threads in one core\n" "Note: Different machines may have different subsets of the CPU topology\n" " parameters supported, so the actual meaning of the supported parameters\n" @@ -306,7 +308,7 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp, " must be set as 1 in the purpose of correct parsing.\n", QEMU_ARCH_ALL) SRST -``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]`` +``-smp [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets][,dies=dies][,clusters=clusters][,modules=modules][,cores=cores][,threads=threads]`` Simulate a SMP system with '\ ``n``\ ' CPUs initially present on the machine type board. On boards supporting CPU hotplug, the optional '\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be @@ -345,14 +347,14 @@ SRST -smp 8,sockets=2,cores=2,threads=2,maxcpus=8 The following sub-option defines a CPU topology hierarchy (2 sockets - totally on the machine, 2 dies per socket, 2 cores per die, 2 threads - per core) for PC machines which support sockets/dies/cores/threads. - Some members of the option can be omitted but their values will be - automatically computed: + totally on the machine, 2 dies per socket, 2 modules per die, 2 cores per + module, 2 threads per core) for PC machines which support sockets/dies + /modules/cores/threads. Some members of the option can be omitted but + their values will be automatically computed: :: - -smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 + -smp 32,sockets=2,dies=2,modules=2,cores=2,threads=2,maxcpus=32 The following sub-option defines a CPU topology hierarchy (2 sockets totally on the machine, 2 clusters per socket, 2 cores per cluster, From 9fcba76ab9c264d06394696c304f2462d9296918 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:27 +0800 Subject: [PATCH 0795/2884] i386: Add cache topology info in CPUCacheInfo Currently, by default, the cache topology is encoded as: 1. i/d cache is shared in one core. 2. L2 cache is shared in one core. 3. L3 cache is shared in one die. This default general setting has caused a misunderstanding, that is, the cache topology is completely equated with a specific cpu topology, such as the connection between L2 cache and core level, and the connection between L3 cache and die level. In fact, the settings of these topologies depend on the specific platform and are not static. For example, on Alder Lake-P, every four Atom cores share the same L2 cache. Thus, we should explicitly define the corresponding cache topology for different cache models to increase scalability. Except legacy_l2_cache_cpuid2 (its default topo level is CPU_TOPO_LEVEL_UNKNOW), explicitly set the corresponding topology level for all other cache models. In order to be compatible with the existing cache topology, set the CPU_TOPO_LEVEL_CORE level for the i/d cache, set the CPU_TOPO_LEVEL_CORE level for L2 cache, and set the CPU_TOPO_LEVEL_DIE level for L3 cache. The field for CPUID[4].EAX[bits 25:14] or CPUID[0x8000001D].EAX[bits 25:14] will be set based on CPUCacheInfo.share_level. Signed-off-by: Zhao Liu Tested-by: Babu Moger Tested-by: Yongwei Ma Acked-by: Michael S. Tsirkin Message-ID: <20240424154929.1487382-20-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 36 ++++++++++++++++++++++++++++++++++++ target/i386/cpu.h | 7 +++++++ 2 files changed, 43 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4d811130b1..656b65ad33 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -552,6 +552,7 @@ static CPUCacheInfo legacy_l1d_cache = { .sets = 64, .partitions = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }; /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ @@ -566,6 +567,7 @@ static CPUCacheInfo legacy_l1d_cache_amd = { .partitions = 1, .lines_per_tag = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }; /* L1 instruction cache: */ @@ -579,6 +581,7 @@ static CPUCacheInfo legacy_l1i_cache = { .sets = 64, .partitions = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }; /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ @@ -593,6 +596,7 @@ static CPUCacheInfo legacy_l1i_cache_amd = { .partitions = 1, .lines_per_tag = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }; /* Level 2 unified cache: */ @@ -606,6 +610,7 @@ static CPUCacheInfo legacy_l2_cache = { .sets = 4096, .partitions = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }; /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ @@ -615,6 +620,7 @@ static CPUCacheInfo legacy_l2_cache_cpuid2 = { .size = 2 * MiB, .line_size = 64, .associativity = 8, + .share_level = CPU_TOPO_LEVEL_INVALID, }; @@ -628,6 +634,7 @@ static CPUCacheInfo legacy_l2_cache_amd = { .associativity = 16, .sets = 512, .partitions = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }; /* Level 3 unified cache: */ @@ -643,6 +650,7 @@ static CPUCacheInfo legacy_l3_cache = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPO_LEVEL_DIE, }; /* TLB definitions: */ @@ -1941,6 +1949,7 @@ static const CPUCaches epyc_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -1953,6 +1962,7 @@ static const CPUCaches epyc_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1963,6 +1973,7 @@ static const CPUCaches epyc_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1976,6 +1987,7 @@ static const CPUCaches epyc_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; @@ -1991,6 +2003,7 @@ static CPUCaches epyc_v4_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2003,6 +2016,7 @@ static CPUCaches epyc_v4_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2013,6 +2027,7 @@ static CPUCaches epyc_v4_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2026,6 +2041,7 @@ static CPUCaches epyc_v4_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; @@ -2041,6 +2057,7 @@ static const CPUCaches epyc_rome_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2053,6 +2070,7 @@ static const CPUCaches epyc_rome_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2063,6 +2081,7 @@ static const CPUCaches epyc_rome_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2076,6 +2095,7 @@ static const CPUCaches epyc_rome_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; @@ -2091,6 +2111,7 @@ static const CPUCaches epyc_rome_v3_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2103,6 +2124,7 @@ static const CPUCaches epyc_rome_v3_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2113,6 +2135,7 @@ static const CPUCaches epyc_rome_v3_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2126,6 +2149,7 @@ static const CPUCaches epyc_rome_v3_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; @@ -2141,6 +2165,7 @@ static const CPUCaches epyc_milan_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2153,6 +2178,7 @@ static const CPUCaches epyc_milan_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2163,6 +2189,7 @@ static const CPUCaches epyc_milan_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2176,6 +2203,7 @@ static const CPUCaches epyc_milan_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; @@ -2191,6 +2219,7 @@ static const CPUCaches epyc_milan_v2_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2203,6 +2232,7 @@ static const CPUCaches epyc_milan_v2_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2213,6 +2243,7 @@ static const CPUCaches epyc_milan_v2_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2226,6 +2257,7 @@ static const CPUCaches epyc_milan_v2_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; @@ -2241,6 +2273,7 @@ static const CPUCaches epyc_genoa_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2253,6 +2286,7 @@ static const CPUCaches epyc_genoa_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2263,6 +2297,7 @@ static const CPUCaches epyc_genoa_cache_info = { .partitions = 1, .sets = 2048, .lines_per_tag = 1, + .share_level = CPU_TOPO_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2276,6 +2311,7 @@ static const CPUCaches epyc_genoa_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPO_LEVEL_DIE, }, }; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 269c30c291..9f152812b6 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1589,6 +1589,13 @@ typedef struct CPUCacheInfo { * address bits. CPUID[4].EDX[bit 2]. */ bool complex_indexing; + + /* + * Cache Topology. The level that cache is shared in. + * Used to encode CPUID[4].EAX[bits 25:14] or + * CPUID[0x8000001D].EAX[bits 25:14]. + */ + enum CPUTopoLevel share_level; } CPUCacheInfo; From f602eb925ac5d51d09de6c4b32ba8a5142055492 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:28 +0800 Subject: [PATCH 0796/2884] i386/cpu: Use CPUCacheInfo.share_level to encode CPUID[4] CPUID[4].EAX[bits 25:14] is used to represent the cache topology for Intel CPUs. After cache models have topology information, we can use CPUCacheInfo.share_level to decide which topology level to be encoded into CPUID[4].EAX[bits 25:14]. And since with the helper max_processor_ids_for_cache(), the filed CPUID[4].EAX[bits 25:14] (original virable "num_apic_ids") is parsed based on cpu topology levels, which are verified when parsing -smp, it's no need to check this value by "assert(num_apic_ids > 0)" again, so remove this assert(). Additionally, wrap the encoding of CPUID[4].EAX[bits 31:26] into a helper to make the code cleaner. Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Message-ID: <20240424154929.1487382-21-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 94 +++++++++++++++++++++++++---------------------- target/i386/cpu.h | 5 +++ 2 files changed, 56 insertions(+), 43 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 656b65ad33..f91e150026 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -235,22 +235,53 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ 0 /* Invalid value */) +static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, + enum CPUTopoLevel share_level) +{ + uint32_t num_ids = 0; + + switch (share_level) { + case CPU_TOPO_LEVEL_CORE: + num_ids = 1 << apicid_core_offset(topo_info); + break; + case CPU_TOPO_LEVEL_DIE: + num_ids = 1 << apicid_die_offset(topo_info); + break; + case CPU_TOPO_LEVEL_PACKAGE: + num_ids = 1 << apicid_pkg_offset(topo_info); + break; + default: + /* + * Currently there is no use case for SMT and MODULE, so use + * assert directly to facilitate debugging. + */ + g_assert_not_reached(); + } + + return num_ids - 1; +} + +static uint32_t max_core_ids_in_package(X86CPUTopoInfo *topo_info) +{ + uint32_t num_cores = 1 << (apicid_pkg_offset(topo_info) - + apicid_core_offset(topo_info)); + return num_cores - 1; +} /* Encode cache info for CPUID[4] */ static void encode_cache_cpuid4(CPUCacheInfo *cache, - int num_apic_ids, int num_cores, + X86CPUTopoInfo *topo_info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); - assert(num_apic_ids > 0); *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | - ((num_cores - 1) << 26) | - ((num_apic_ids - 1) << 14); + (max_core_ids_in_package(topo_info) << 26) | + (max_thread_ids_for_cache(topo_info, cache->share_level) << 14); assert(cache->line_size > 0); assert(cache->partitions > 0); @@ -6392,18 +6423,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); break; - case 4: { - /* - * CPUID.04H:EAX[bits 25:14]: Maximum number of addressable IDs for - * logical processors sharing this cache. - */ - int addressable_threads_width; - /* - * CPUID.04H:EAX[bits 31:26]: Maximum number of addressable IDs for - * processor cores in the physical package. - */ - int addressable_cores_width; - + case 4: /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); @@ -6415,59 +6435,48 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); if (cores_per_pkg > 1) { - addressable_cores_width = apicid_pkg_offset(&topo_info) - - apicid_core_offset(&topo_info); - *eax &= ~0xFC000000; - *eax |= ((1 << addressable_cores_width) - 1) << 26; + *eax |= max_core_ids_in_package(&topo_info) << 26; } if (host_vcpus_per_cache > threads_per_pkg) { - /* Share the cache at package level. */ - addressable_threads_width = apicid_pkg_offset(&topo_info); - *eax &= ~0x3FFC000; - *eax |= ((1 << addressable_threads_width) - 1) << 14; + + /* Share the cache at package level. */ + *eax |= max_thread_ids_for_cache(&topo_info, + CPU_TOPO_LEVEL_PACKAGE) << 14; } } } else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) { *eax = *ebx = *ecx = *edx = 0; } else { *eax = 0; - addressable_cores_width = apicid_pkg_offset(&topo_info) - - apicid_core_offset(&topo_info); switch (count) { case 0: /* L1 dcache info */ - addressable_threads_width = cpu->l1_cache_per_core - ? apicid_core_offset(&topo_info) - : 0; encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - (1 << addressable_threads_width), - (1 << addressable_cores_width), + &topo_info, eax, ebx, ecx, edx); + if (!cpu->l1_cache_per_core) { + *eax &= ~MAKE_64BIT_MASK(14, 12); + } break; case 1: /* L1 icache info */ - addressable_threads_width = cpu->l1_cache_per_core - ? apicid_core_offset(&topo_info) - : 0; encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - (1 << addressable_threads_width), - (1 << addressable_cores_width), + &topo_info, eax, ebx, ecx, edx); + if (!cpu->l1_cache_per_core) { + *eax &= ~MAKE_64BIT_MASK(14, 12); + } break; case 2: /* L2 cache info */ - addressable_threads_width = apicid_core_offset(&topo_info); encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - (1 << addressable_threads_width), - (1 << addressable_cores_width), + &topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ if (cpu->enable_l3_cache) { - addressable_threads_width = apicid_die_offset(&topo_info); encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - (1 << addressable_threads_width), - (1 << addressable_cores_width), + &topo_info, eax, ebx, ecx, edx); break; } @@ -6478,7 +6487,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } break; - } case 5: /* MONITOR/MWAIT Leaf */ *eax = cpu->mwait.eax; /* Smallest monitor-line size in bytes */ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9f152812b6..c500a69a69 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2011,6 +2011,11 @@ struct ArchCPU { */ bool enable_l3_cache; + /* Compatibility bits for old machine types. + * If true present L1 cache as per-thread, not per-core. + */ + bool l1_cache_per_core; + /* Compatibility bits for old machine types. * If true present the old cache topology information */ From 5eb608a13b2dfb772c831d02000a3514d1f137aa Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 24 Apr 2024 23:49:29 +0800 Subject: [PATCH 0797/2884] i386/cpu: Use CPUCacheInfo.share_level to encode CPUID[0x8000001D].EAX[bits 25:14] CPUID[0x8000001D].EAX[bits 25:14] NumSharingCache: number of logical processors sharing cache. The number of logical processors sharing this cache is NumSharingCache + 1. After cache models have topology information, we can use CPUCacheInfo.share_level to decide which topology level to be encoded into CPUID[0x8000001D].EAX[bits 25:14]. Tested-by: Yongwei Ma Signed-off-by: Zhao Liu Tested-by: Babu Moger Reviewed-by: Babu Moger Message-ID: <20240424154929.1487382-22-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index f91e150026..bc2dceb647 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -478,20 +478,12 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { - uint32_t num_sharing_cache; assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); - - /* L3 is shared among multiple cores */ - if (cache->level == 3) { - num_sharing_cache = 1 << apicid_die_offset(topo_info); - } else { - num_sharing_cache = 1 << apicid_core_offset(topo_info); - } - *eax |= (num_sharing_cache - 1) << 14; + *eax |= max_thread_ids_for_cache(topo_info, cache->share_level) << 14; assert(cache->line_size > 0); assert(cache->partitions > 0); From 84d4b72854869821eb89813c195927fdd3078c12 Mon Sep 17 00:00:00 2001 From: donsheng Date: Wed, 22 May 2024 04:01:14 +0800 Subject: [PATCH 0798/2884] target-i386: hyper-v: Correct kvm_hv_handle_exit return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This bug fix addresses the incorrect return value of kvm_hv_handle_exit for KVM_EXIT_HYPERV_SYNIC, which should be EXCP_INTERRUPT. Handling of KVM_EXIT_HYPERV_SYNIC in QEMU needs to be synchronous. This means that async_synic_update should run in the current QEMU vCPU thread before returning to KVM, returning EXCP_INTERRUPT to guarantee this. Returning 0 can cause async_synic_update to run asynchronously. One problem (kvm-unit-tests's hyperv_synic test fails with timeout error) caused by this bug: When a guest VM writes to the HV_X64_MSR_SCONTROL MSR to enable Hyper-V SynIC, a VM exit is triggered and processed by the kvm_hv_handle_exit function of the QEMU vCPU. This function then calls the async_synic_update function to set synic->sctl_enabled to true. A true value of synic->sctl_enabled is required before creating SINT routes using the hyperv_sint_route_new() function. If kvm_hv_handle_exit returns 0 for KVM_EXIT_HYPERV_SYNIC, the current QEMU vCPU thread may return to KVM and enter the guest VM before running async_synic_update. In such case, the hyperv_synic test’s subsequent call to synic_ctl(HV_TEST_DEV_SINT_ROUTE_CREATE, ...) immediately after writing to HV_X64_MSR_SCONTROL can cause QEMU’s hyperv_sint_route_new() function to return prematurely (because synic->sctl_enabled is false). If the SINT route is not created successfully, the SINT interrupt will not be fired, resulting in a timeout error in the hyperv_synic test. Fixes: 267e071bd6d6 (“hyperv: make overlay pages for SynIC”) Suggested-by: Chao Gao Signed-off-by: Dongsheng Zhang Message-ID: <20240521200114.11588-1-dongsheng.x.zhang@intel.com> Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/kvm/hyperv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index f2a3fe650a..b94f12acc2 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -81,7 +81,7 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) */ async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL); - return 0; + return EXCP_INTERRUPT; case KVM_EXIT_HYPERV_HCALL: { uint16_t code = exit->u.hcall.input & 0xffff; bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST; From 55a331655dbc8feee198f379eaafa3e75e744b7b Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 16 May 2024 11:45:15 +0800 Subject: [PATCH 0799/2884] migration/colo: Minor fix for colo error message - Explicitly show the missing module name: replication - Fix capability name to x-colo Reviewed-by: Peter Xu Reviewed-by: Zhang Chen Signed-off-by: Li Zhijian Suggested-by: Michael Tokarev [fixed mangled author email address] Signed-off-by: Fabiano Rosas --- migration/migration.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index e88b24f1e6..995f0ca923 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -513,13 +513,13 @@ void migration_incoming_disable_colo(void) int migration_incoming_enable_colo(void) { #ifndef CONFIG_REPLICATION - error_report("ENABLE_COLO command come in migration stream, but COLO " - "module is not built in"); + error_report("ENABLE_COLO command come in migration stream, but the " + "replication module is not built in"); return -ENOTSUP; #endif if (!migrate_colo()) { - error_report("ENABLE_COLO command come in migration stream, but c-colo " + error_report("ENABLE_COLO command come in migration stream, but x-colo " "capability is not set"); return -EINVAL; } From 787ea49e80df0e7af922586f6076da94410cdd08 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 16 May 2024 11:45:16 +0800 Subject: [PATCH 0800/2884] migration/colo: make colo_incoming_co() return void Currently, it always returns 0, no need to check the return value at all. In addition, enter colo coroutine only if migration_incoming_colo_enabled() is true. Once the destination side enters the COLO* state, the COLO process will take over the remaining processes until COLO exits. Cc: Fabiano Rosas Reviewed-by: Peter Xu Reviewed-by: Zhang Chen Signed-off-by: Li Zhijian [fixed mangled author email address] Signed-off-by: Fabiano Rosas --- include/migration/colo.h | 2 +- migration/colo-stubs.c | 3 +-- migration/colo.c | 9 ++------- migration/migration.c | 6 +++--- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/include/migration/colo.h b/include/migration/colo.h index eaac07f26d..43222ef5ae 100644 --- a/include/migration/colo.h +++ b/include/migration/colo.h @@ -49,7 +49,7 @@ void colo_checkpoint_delay_set(void); * * Called with BQL locked, may temporary release BQL. */ -int coroutine_fn colo_incoming_co(void); +void coroutine_fn colo_incoming_co(void); void colo_shutdown(void); #endif diff --git a/migration/colo-stubs.c b/migration/colo-stubs.c index f8c069b739..e22ce65234 100644 --- a/migration/colo-stubs.c +++ b/migration/colo-stubs.c @@ -9,9 +9,8 @@ void colo_shutdown(void) { } -int coroutine_fn colo_incoming_co(void) +void coroutine_fn colo_incoming_co(void) { - return 0; } void colo_checkpoint_delay_set(void) diff --git a/migration/colo.c b/migration/colo.c index e2b450c132..ca37b932ac 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -928,16 +928,13 @@ out: return NULL; } -int coroutine_fn colo_incoming_co(void) +void coroutine_fn colo_incoming_co(void) { MigrationIncomingState *mis = migration_incoming_get_current(); QemuThread th; assert(bql_locked()); - - if (!migration_incoming_colo_enabled()) { - return 0; - } + assert(migration_incoming_colo_enabled()); qemu_thread_create(&th, "COLO incoming", colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); @@ -953,6 +950,4 @@ int coroutine_fn colo_incoming_co(void) /* We hold the global BQL, so it is safe here */ colo_release_ram_cache(); - - return 0; } diff --git a/migration/migration.c b/migration/migration.c index 995f0ca923..c004637d29 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -776,9 +776,9 @@ process_incoming_migration_co(void *opaque) goto fail; } - if (colo_incoming_co() < 0) { - error_setg(&local_err, "colo incoming failed"); - goto fail; + if (migration_incoming_colo_enabled()) { + /* yield until COLO exit */ + colo_incoming_co(); } migration_bh_schedule(process_incoming_migration_bh, mis); From 3dc27fac2547623d837826bbda5369d7b240c78e Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 16 May 2024 11:45:17 +0800 Subject: [PATCH 0801/2884] migration/colo: Tidy up bql_unlock() around bdrv_activate_all() Make the code more tight. Suggested-by: Michael Tokarev Reviewed-by: Peter Xu Reviewed-by: Zhang Chen Signed-off-by: Li Zhijian Signed-off-by: Michael Tokarev [fixed mangled author email address] Signed-off-by: Fabiano Rosas --- migration/colo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index ca37b932ac..f96c2ee069 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -837,12 +837,11 @@ static void *colo_process_incoming_thread(void *opaque) /* Make sure all file formats throw away their mutable metadata */ bql_lock(); bdrv_activate_all(&local_err); + bql_unlock(); if (local_err) { - bql_unlock(); error_report_err(local_err); return NULL; } - bql_unlock(); failover_init_state(); From 3f879f2f319379daeb65faa3677191ba4240b45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 16 May 2024 12:40:20 +0400 Subject: [PATCH 0802/2884] migration: add "exists" info to load-state-field trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Peter Xu Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Signed-off-by: Fabiano Rosas --- migration/trace-events | 2 +- migration/vmstate.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/migration/trace-events b/migration/trace-events index d0c44c3853..0b7c3324fb 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -58,7 +58,7 @@ postcopy_page_req_sync(void *host_addr) "sync page req %p" vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d" vmstate_load_state(const char *name, int version_id) "%s v%d" vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d" -vmstate_load_state_field(const char *name, const char *field) "%s:%s" +vmstate_load_state_field(const char *name, const char *field, bool exists) "%s:%s exists=%d" vmstate_n_elems(const char *name, int n_elems) "%s: %d" vmstate_subsection_load(const char *parent) "%s" vmstate_subsection_load_bad(const char *parent, const char *sub, const char *sub2) "%s: %s/%s" diff --git a/migration/vmstate.c b/migration/vmstate.c index ef26f26ccd..b51212a75b 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -128,8 +128,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } } while (field->name) { - trace_vmstate_load_state_field(vmsd->name, field->name); - if (vmstate_field_exists(vmsd, field, opaque, version_id)) { + bool exists = vmstate_field_exists(vmsd, field, opaque, version_id); + trace_vmstate_load_state_field(vmsd->name, field->name, exists); + if (exists) { void *first_elem = opaque + field->offset; int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); From f0937ec669f9f39f5ece39f62d048606b9ee3877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 16 May 2024 12:40:21 +0400 Subject: [PATCH 0803/2884] migration: fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Signed-off-by: Fabiano Rosas --- migration/vmstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/vmstate.c b/migration/vmstate.c index b51212a75b..ff5d589a6d 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -479,7 +479,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, len = qemu_peek_byte(f, 1); if (len < strlen(vmsd->name) + 1) { - /* subsection name has be be "section_name/a" */ + /* subsection name has to be "section_name/a" */ trace_vmstate_subsection_load_bad(vmsd->name, "(short)", ""); return 0; } From 40a23ef643664b5c1021a9789f9d680b6294fb50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 16 May 2024 12:40:22 +0400 Subject: [PATCH 0804/2884] virtio-gpu: fix v2 migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit dfcf74fa ("virtio-gpu: fix scanout migration post-load") broke forward/backward version migration. Versioning of nested VMSD structures is not straightforward, as the wire format doesn't have nested structures versions. Introduce x-scanout-vmstate-version and a field test to save/load appropriately according to the machine version. Fixes: dfcf74fa ("virtio-gpu: fix scanout migration post-load") Signed-off-by: Marc-André Lureau Signed-off-by: Peter Xu Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner [fixed long lines] Signed-off-by: Fabiano Rosas --- hw/core/machine.c | 1 + hw/display/virtio-gpu.c | 30 ++++++++++++++++++++++-------- include/hw/virtio/virtio-gpu.h | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index c7ceb11501..8d6dc69f0e 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -42,6 +42,7 @@ GlobalProperty hw_compat_8_2[] = { { "migration", "zero-page-detection", "legacy"}, { TYPE_VIRTIO_IOMMU_PCI, "granule", "4k" }, { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "64" }, + { "virtio-gpu-device", "x-scanout-vmstate-version", "1" }, }; const size_t hw_compat_8_2_len = G_N_ELEMENTS(hw_compat_8_2); diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index ae831b6b3e..d60b1b2973 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1166,10 +1166,17 @@ static void virtio_gpu_cursor_bh(void *opaque) virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq); } +static bool scanout_vmstate_after_v2(void *opaque, int version) +{ + struct VirtIOGPUBase *base = container_of(opaque, VirtIOGPUBase, scanout); + struct VirtIOGPU *gpu = container_of(base, VirtIOGPU, parent_obj); + + return gpu->scanout_vmstate_version >= 2; +} + static const VMStateDescription vmstate_virtio_gpu_scanout = { .name = "virtio-gpu-one-scanout", - .version_id = 2, - .minimum_version_id = 1, + .version_id = 1, .fields = (const VMStateField[]) { VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout), VMSTATE_UINT32(width, struct virtio_gpu_scanout), @@ -1181,12 +1188,18 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout), VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout), VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), - VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2), - VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2), - VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2), - VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2), - VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2), - VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.stride, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.offset, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), VMSTATE_END_OF_LIST() }, }; @@ -1659,6 +1672,7 @@ static Property virtio_gpu_properties[] = { DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_BLOB_ENABLED, false), DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0), + DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 56d6e821bf..7a59379f5a 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -177,6 +177,7 @@ typedef struct VGPUDMABuf { struct VirtIOGPU { VirtIOGPUBase parent_obj; + uint8_t scanout_vmstate_version; uint64_t conf_max_hostmem; VirtQueue *ctrl_vq; From 9710401276a0eb2fc6d467d9abea1f5e3fe2c362 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 17 May 2024 09:53:36 +0200 Subject: [PATCH 0805/2884] hw/core/machine: move compatibility flags for VirtIO-net USO to machine 8.1 Migration from an 8.2 or 9.0 binary to an 8.1 binary with machine version 8.1 can fail with: > kvm: Features 0x1c0010130afffa7 unsupported. Allowed features: 0x10179bfffe7 > kvm: Failed to load virtio-net:virtio > kvm: error while loading state for instance 0x0 of device '0000:00:12.0/virtio-net' > kvm: load of migration failed: Operation not permitted The series 53da8b5a99 virtio-net: Add support for USO features 9da1684954 virtio-net: Add USO flags to vhost support. f03e0cf63b tap: Add check for USO features 2ab0ec3121 tap: Add USO support to tap device. only landed in QEMU 8.2, so the compatibility flags should be part of machine version 8.1. Moving the flags unfortunately breaks forward migration with machine version 8.1 from a binary without this patch to a binary with this patch. Fixes: 53da8b5a99 ("virtio-net: Add support for USO features") Signed-off-by: Fiona Ebner Reviewed-by: Fabiano Rosas Acked-by: Jason Wang Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- hw/core/machine.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 8d6dc69f0e..f5dffea33d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -51,15 +51,15 @@ GlobalProperty hw_compat_8_1[] = { { "ramfb", "x-migrate", "off" }, { "vfio-pci-nohotplug", "x-ramfb-migrate", "off" }, { "igb", "x-pcie-flr-init", "off" }, + { TYPE_VIRTIO_NET, "host_uso", "off"}, + { TYPE_VIRTIO_NET, "guest_uso4", "off"}, + { TYPE_VIRTIO_NET, "guest_uso6", "off"}, }; const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1); GlobalProperty hw_compat_8_0[] = { { "migration", "multifd-flush-after-each-section", "on"}, { TYPE_PCI_DEVICE, "x-pcie-ari-nextfn-1", "on" }, - { TYPE_VIRTIO_NET, "host_uso", "off"}, - { TYPE_VIRTIO_NET, "guest_uso4", "off"}, - { TYPE_VIRTIO_NET, "guest_uso6", "off"}, }; const size_t hw_compat_8_0_len = G_N_ELEMENTS(hw_compat_8_0); From 648536550b6f319cae2e7a9f1b09851759c227b4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 22 May 2024 11:12:55 +0200 Subject: [PATCH 0806/2884] tests/qtest/migration-test: Run some basic tests on s390x and ppc64 with TCG, too On s390x, we recently had a regression that broke migration / savevm (see commit bebe9603fc ("hw/intc/s390_flic: Fix crash that occurs when saving the machine state"). The problem was merged without being noticed since we currently do not run any migration / savevm related tests on x86 hosts. While we currently cannot run all migration tests for the s390x target on x86 hosts yet (due to some unresolved issues with TCG), we can at least run some of the non-live tests to avoid such problems in the future. Thus enable the "analyze-script" and the "bad_dest" tests before checking for KVM on s390x or ppc64 (this also fixes the problem that the "analyze-script" test was not run on s390x at all anymore since it got disabled again by accident in a previous refactoring of the code). Signed-off-by: Thomas Huth Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 53 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e8d3555f56..5b4eca2b20 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -3437,26 +3437,6 @@ int main(int argc, char **argv) arch = qtest_get_arch(); is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); - /* - * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG - * is touchy due to race conditions on dirty bits (especially on PPC for - * some reason) - */ - if (g_str_equal(arch, "ppc64") && - (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { - g_test_message("Skipping test: kvm_hv not available"); - return g_test_run(); - } - - /* - * Similar to ppc64, s390x seems to be touchy with TCG, so disable it - * there until the problems are resolved - */ - if (g_str_equal(arch, "s390x") && !has_kvm) { - g_test_message("Skipping test: s390x host with KVM is required"); - return g_test_run(); - } - tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); if (!tmpfs) { g_test_message("Can't create temporary directory in %s: %s", @@ -3466,6 +3446,31 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); + migration_test_add("/migration/bad_dest", test_baddest); +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif + + /* + * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG + * is touchy due to race conditions on dirty bits (especially on PPC for + * some reason) + */ + if (g_str_equal(arch, "ppc64") && + (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { + g_test_message("Skipping tests: kvm_hv not available"); + goto test_add_done; + } + + /* + * Similar to ppc64, s390x seems to be touchy with TCG, so disable it + * there until the problems are resolved + */ + if (g_str_equal(arch, "s390x") && !has_kvm) { + g_test_message("Skipping tests: s390x host with KVM is required"); + goto test_add_done; + } + if (is_x86) { migration_test_add("/migration/precopy/unix/suspend/live", test_precopy_unix_suspend_live); @@ -3491,12 +3496,6 @@ int main(int argc, char **argv) } } - migration_test_add("/migration/bad_dest", test_baddest); -#ifndef _WIN32 - if (!g_str_equal(arch, "s390x")) { - migration_test_add("/migration/analyze-script", test_analyze_script); - } -#endif migration_test_add("/migration/precopy/unix/plain", test_precopy_unix_plain); migration_test_add("/migration/precopy/unix/xbzrle", @@ -3653,6 +3652,8 @@ int main(int argc, char **argv) test_vcpu_dirty_limit); } +test_add_done: + ret = g_test_run(); g_assert_cmpint(ret, ==, 0); From 8f023a0bd946bb0c122543c64fe2b34bad0dd048 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 22 May 2024 11:23:01 +0200 Subject: [PATCH 0807/2884] tests/qtest/migration-test: Fix the check for a successful run of analyze-migration.py If analyze-migration.py cannot be run or crashes, the error is currently ignored since the code only checks for nonzero values in case the child exited properly. For example, if you run the test with a non-existing Python interpreter, it still succeeds: $ PYTHON=wrongpython QTEST_QEMU_BINARY=./qemu-system-x86_64 tests/qtest/migration-test ... # Running /x86_64/migration/analyze-script # Using machine type: pc-q35-9.1 # starting QEMU: exec ./qemu-system-x86_64 -qtest unix:/tmp/qtest-417639.sock -qtest-log /dev/null -chardev socket,path=/tmp/qtest-417639.qmp,id=char0 -mon chardev=char0,mode=control -display none -audio none -accel kvm -accel tcg -machine pc-q35-9.1, -name source,debug-threads=on -m 150M -serial file:/tmp/migration-test-XPLUN2/src_serial -drive if=none,id=d0,file=/tmp/migration-test-XPLUN2/bootsect,format=raw -device ide-hd,drive=d0,secs=1,cyls=1,heads=1 -uuid 11111111-1111-1111-1111-111111111111 -accel qtest # starting QEMU: exec ./qemu-system-x86_64 -qtest unix:/tmp/qtest-417639.sock -qtest-log /dev/null -chardev socket,path=/tmp/qtest-417639.qmp,id=char0 -mon chardev=char0,mode=control -display none -audio none -accel kvm -accel tcg -machine pc-q35-9.1, -name target,debug-threads=on -m 150M -serial file:/tmp/migration-test-XPLUN2/dest_serial -incoming tcp:127.0.0.1:0 -drive if=none,id=d0,file=/tmp/migration-test-XPLUN2/bootsect,format=raw -device ide-hd,drive=d0,secs=1,cyls=1,heads=1 -accel qtest ** ERROR:../../devel/qemu/tests/qtest/migration-test.c:1603:test_analyze_script: code should not be reached migration-test: ../../devel/qemu/tests/qtest/libqtest.c:240: qtest_wait_qemu: Assertion `pid == s->qemu_pid' failed. migration-test: ../../devel/qemu/tests/qtest/libqtest.c:240: qtest_wait_qemu: Assertion `pid == s->qemu_pid' failed. ok 2 /x86_64/migration/analyze-script ... Let's better fail the test in case the child did not exit properly, too. Signed-off-by: Thomas Huth Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 5b4eca2b20..b7e3406471 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1604,7 +1604,7 @@ static void test_analyze_script(void) } g_assert(waitpid(pid, &wstatus, 0) == pid); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) { + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { g_test_message("Failed to analyze the migration stream"); g_test_fail(); } From 0eb285c3627b5406dd91bfa3b47402e5de92123f Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 8 May 2024 10:47:32 +0800 Subject: [PATCH 0808/2884] target/loongarch/kvm: Fix VM recovery from disk failures vmstate does not save kvm_state_conter, which can cause VM recovery from disk to fail. Cc: qemu-stable@nongnu.org Signed-off-by: Song Gao Acked-by: Peter Xu Message-Id: <20240508024732.3127792-1-gaosong@loongson.cn> --- target/loongarch/machine.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c index 9cd9e848d6..08a7fa5370 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -145,8 +145,8 @@ static const VMStateDescription vmstate_tlb = { /* LoongArch CPU state */ const VMStateDescription vmstate_loongarch_cpu = { .name = "cpu", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), VMSTATE_UINTTL(env.pc, LoongArchCPU), @@ -208,6 +208,8 @@ const VMStateDescription vmstate_loongarch_cpu = { VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU), VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU), + VMSTATE_UINT64(kvm_state_counter, LoongArchCPU), + VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { From 07c0866103d4aa2dd83c7c3e7898843e28e3893a Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 14 May 2024 19:07:52 +0800 Subject: [PATCH 0809/2884] target/loongarch/kvm: fpu save the vreg registers high 192bit On kvm side, get_fpu/set_fpu save the vreg registers high 192bits, but QEMU missing. Cc: qemu-stable@nongnu.org Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240514110752.989572-1-gaosong@loongson.cn> --- target/loongarch/kvm/kvm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index bc75552d0f..8e6e27c8bf 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -436,6 +436,9 @@ static int kvm_loongarch_get_regs_fp(CPUState *cs) env->fcsr0 = fpu.fcsr; for (i = 0; i < 32; i++) { env->fpr[i].vreg.UD[0] = fpu.fpr[i].val64[0]; + env->fpr[i].vreg.UD[1] = fpu.fpr[i].val64[1]; + env->fpr[i].vreg.UD[2] = fpu.fpr[i].val64[2]; + env->fpr[i].vreg.UD[3] = fpu.fpr[i].val64[3]; } for (i = 0; i < 8; i++) { env->cf[i] = fpu.fcc & 0xFF; @@ -455,6 +458,9 @@ static int kvm_loongarch_put_regs_fp(CPUState *cs) fpu.fcc = 0; for (i = 0; i < 32; i++) { fpu.fpr[i].val64[0] = env->fpr[i].vreg.UD[0]; + fpu.fpr[i].val64[1] = env->fpr[i].vreg.UD[1]; + fpu.fpr[i].val64[2] = env->fpr[i].vreg.UD[2]; + fpu.fpr[i].val64[3] = env->fpr[i].vreg.UD[3]; } for (i = 0; i < 8; i++) { From a7701b61f62ecef787ec4c64569bf5a1e892197c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 14 May 2024 10:51:09 +0800 Subject: [PATCH 0810/2884] hw/loongarch: Add VM mode in IOCSR feature register in kvm mode If VM runs in kvm mode, VM mode is added in IOCSR feature register. So guest can detect kvm hypervisor type and enable possible pv functions. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240514025109.3238398-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index f0640d2d80..95f9ed5cae 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -10,6 +10,7 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/char/serial.h" +#include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" @@ -840,18 +841,23 @@ static void virt_iocsr_misc_write(void *opaque, hwaddr addr, static uint64_t virt_iocsr_misc_read(void *opaque, hwaddr addr, unsigned size) { + uint64_t ret; + switch (addr) { case VERSION_REG: return 0x11ULL; case FEATURE_REG: - return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | - 1ULL << IOCSRF_CSRIPI; + ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI); + if (kvm_enabled()) { + ret |= BIT(IOCSRF_VM); + } + return ret; case VENDOR_REG: return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ case CPUNAME_REG: return 0x303030354133ULL; /* "3A5000" */ case MISC_FUNC_REG: - return 1ULL << IOCSRM_EXTIOI_EN; + return BIT_ULL(IOCSRM_EXTIOI_EN); } return 0ULL; } From fc100011f38d2f58e2212a056173def3a7cf3512 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 15 May 2024 17:39:22 +0800 Subject: [PATCH 0811/2884] hw/loongarch: Refine acpi srat table for numa memory One LoongArch virt machine platform, there is limitation for memory map information. The minimum memory size is 256M and minimum memory size for numa node0 is 256M also. With qemu numa qtest, it is possible that memory size of numa node0 is 128M. Limitations for minimum memory size for both total memory and numa node0 is removed for acpi srat table creation. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240515093927.3453674-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/acpi-build.c | 56 +++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 5ef010d4da..af45ce526d 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -166,8 +166,9 @@ static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { int i, arch_id, node_id; - uint64_t mem_len, mem_base; - int nb_numa_nodes = machine->numa_state->num_nodes; + hwaddr len, base, gap; + NodeInfo *numa_info; + int nodes, nb_numa_nodes = machine->numa_state->num_nodes; LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(lvms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); @@ -196,35 +197,44 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } - /* Node0 */ - build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, - 0, MEM_AFFINITY_ENABLED); - mem_base = VIRT_HIGHMEM_BASE; - if (!nb_numa_nodes) { - mem_len = machine->ram_size - VIRT_LOWMEM_SIZE; - } else { - mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE; + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + numa_info = machine->numa_state->nodes; + nodes = nb_numa_nodes; + if (!nodes) { + nodes = 1; } - if (mem_len) - build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED); - /* Node1 - Nodemax */ - if (nb_numa_nodes) { - mem_base += mem_len; - for (i = 1; i < nb_numa_nodes; ++i) { - if (machine->numa_state->nodes[i].node_mem > 0) { - build_srat_memory(table_data, mem_base, - machine->numa_state->nodes[i].node_mem, i, - MEM_AFFINITY_ENABLED); - mem_base += machine->numa_state->nodes[i].node_mem; - } + for (i = 0; i < nodes; i++) { + if (nb_numa_nodes) { + len = numa_info[i].node_mem; + } else { + len = machine->ram_size; + } + + /* + * memory for the node splited into two part + * lowram: [base, +gap) + * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) + */ + if (len >= gap) { + build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); + len -= gap; + base = VIRT_HIGHMEM_BASE; + gap = machine->ram_size - VIRT_LOWMEM_SIZE; + } + + if (len) { + build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); + base += len; + gap -= len; } } if (machine->device_memory) { build_srat_memory(table_data, machine->device_memory->base, memory_region_size(&machine->device_memory->mr), - nb_numa_nodes - 1, + nodes - 1, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } From 09ec65794f8e1a344ccb231b53e7c39b664f4fa8 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 15 May 2024 17:39:23 +0800 Subject: [PATCH 0812/2884] hw/loongarch: Refine fadt memory table for numa memory One LoongArch virt machine platform, there is limitation for memory map information. The minimum memory size is 256M and minimum memory size for numa node0 is 256M also. With qemu numa qtest, it is possible that memory size of numa node0 is 128M. Limitations for minimum memory size for both total memory and numa node0 is removed for fadt numa memory table creation. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240515093927.3453674-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 46 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 95f9ed5cae..850729202f 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -474,6 +474,48 @@ static void fdt_add_memory_node(MachineState *ms, g_free(nodename); } +static void fdt_add_memory_nodes(MachineState *ms) +{ + hwaddr base, size, ram_size, gap; + int i, nb_numa_nodes, nodes; + NodeInfo *numa_info; + + ram_size = ms->ram_size; + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + nodes = nb_numa_nodes = ms->numa_state->num_nodes; + numa_info = ms->numa_state->nodes; + if (!nodes) { + nodes = 1; + } + + for (i = 0; i < nodes; i++) { + if (nb_numa_nodes) { + size = numa_info[i].node_mem; + } else { + size = ram_size; + } + + /* + * memory for the node splited into two part + * lowram: [base, +gap) + * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) + */ + if (size >= gap) { + fdt_add_memory_node(ms, base, gap, i); + size -= gap; + base = VIRT_HIGHMEM_BASE; + gap = ram_size - VIRT_LOWMEM_SIZE; + } + + if (size) { + fdt_add_memory_node(ms, base, size, i); + base += size; + gap -= size; + } + } +} + static void virt_build_smbios(LoongArchVirtMachineState *lvms) { MachineState *ms = MACHINE(lvms); @@ -921,10 +963,10 @@ static void virt_init(MachineState *machine) lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; } fdt_add_cpu_nodes(lvms); + fdt_add_memory_nodes(machine); /* Node0 memory */ memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); - fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0); memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.node0.lowram", machine->ram, offset, VIRT_LOWMEM_SIZE); memory_region_add_subregion(address_space_mem, phyAddr, &lvms->lowmem); @@ -938,7 +980,6 @@ static void virt_init(MachineState *machine) } phyAddr = VIRT_HIGHMEM_BASE; memmap_add_entry(phyAddr, highram_size, 1); - fdt_add_memory_node(machine, phyAddr, highram_size, 0); memory_region_init_alias(&lvms->highmem, NULL, "loongarch.node0.highram", machine->ram, offset, highram_size); memory_region_add_subregion(address_space_mem, phyAddr, &lvms->highmem); @@ -954,7 +995,6 @@ static void virt_init(MachineState *machine) offset, numa_info[i].node_mem); memory_region_add_subregion(address_space_mem, phyAddr, nodemem); memmap_add_entry(phyAddr, numa_info[i].node_mem, 1); - fdt_add_memory_node(machine, phyAddr, numa_info[i].node_mem, i); offset += numa_info[i].node_mem; phyAddr += numa_info[i].node_mem; } From 3cc451cbcec746f2736bdc6b14acaf4936c83371 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 15 May 2024 17:39:24 +0800 Subject: [PATCH 0813/2884] hw/loongarch: Refine fwcfg memory map Memory map table for fwcfg is used for UEFI BIOS, UEFI BIOS uses the first entry from fwcfg memory map as the first memory HOB, the second memory HOB will be used if the first memory HOB is used up. Memory map table for fwcfg does not care about numa node, however in generic the first memory HOB is part of numa node0, so that runtime memory of UEFI which is allocated from the first memory HOB is located at numa node0. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240515093927.3453674-4-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 60 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 850729202f..449050cba5 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -918,6 +918,62 @@ static const MemoryRegionOps virt_iocsr_misc_ops = { }, }; +static void fw_cfg_add_memory(MachineState *ms) +{ + hwaddr base, size, ram_size, gap; + int nb_numa_nodes, nodes; + NodeInfo *numa_info; + + ram_size = ms->ram_size; + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + nodes = nb_numa_nodes = ms->numa_state->num_nodes; + numa_info = ms->numa_state->nodes; + if (!nodes) { + nodes = 1; + } + + /* add fw_cfg memory map of node0 */ + if (nb_numa_nodes) { + size = numa_info[0].node_mem; + } else { + size = ram_size; + } + + if (size >= gap) { + memmap_add_entry(base, gap, 1); + size -= gap; + base = VIRT_HIGHMEM_BASE; + gap = ram_size - VIRT_LOWMEM_SIZE; + } + + if (size) { + memmap_add_entry(base, size, 1); + base += size; + } + + if (nodes < 2) { + return; + } + + /* add fw_cfg memory map of other nodes */ + size = ram_size - numa_info[0].node_mem; + gap = VIRT_LOWMEM_BASE + VIRT_LOWMEM_SIZE; + if (base < gap && (base + size) > gap) { + /* + * memory map for the maining nodes splited into two part + * lowram: [base, +(gap - base)) + * highram: [VIRT_HIGHMEM_BASE, +(size - (gap - base))) + */ + memmap_add_entry(base, gap - base, 1); + size -= gap - base; + base = VIRT_HIGHMEM_BASE; + } + + if (size) + memmap_add_entry(base, size, 1); +} + static void virt_init(MachineState *machine) { LoongArchCPU *lacpu; @@ -964,9 +1020,9 @@ static void virt_init(MachineState *machine) } fdt_add_cpu_nodes(lvms); fdt_add_memory_nodes(machine); + fw_cfg_add_memory(machine); /* Node0 memory */ - memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.node0.lowram", machine->ram, offset, VIRT_LOWMEM_SIZE); memory_region_add_subregion(address_space_mem, phyAddr, &lvms->lowmem); @@ -979,7 +1035,6 @@ static void virt_init(MachineState *machine) highram_size = ram_size - VIRT_LOWMEM_SIZE; } phyAddr = VIRT_HIGHMEM_BASE; - memmap_add_entry(phyAddr, highram_size, 1); memory_region_init_alias(&lvms->highmem, NULL, "loongarch.node0.highram", machine->ram, offset, highram_size); memory_region_add_subregion(address_space_mem, phyAddr, &lvms->highmem); @@ -994,7 +1049,6 @@ static void virt_init(MachineState *machine) memory_region_init_alias(nodemem, NULL, ramName, machine->ram, offset, numa_info[i].node_mem); memory_region_add_subregion(address_space_mem, phyAddr, nodemem); - memmap_add_entry(phyAddr, numa_info[i].node_mem, 1); offset += numa_info[i].node_mem; phyAddr += numa_info[i].node_mem; } From 8d96788cb1fbaa361b29e98954e5254acf6d0435 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 15 May 2024 17:39:25 +0800 Subject: [PATCH 0814/2884] hw/loongarch: Refine system dram memory region For system dram memory region, it is not necessary to use numa node information. There is only low memory region and high memory region. Remove numa node information for ddr memory region here, it can reduce memory region number on LoongArch virt machine. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240515093927.3453674-5-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 51 ++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 449050cba5..4ec2b9a061 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -978,14 +978,10 @@ static void virt_init(MachineState *machine) { LoongArchCPU *lacpu; const char *cpu_model = machine->cpu_type; - ram_addr_t offset = 0; - ram_addr_t ram_size = machine->ram_size; - uint64_t highram_size = 0, phyAddr = 0; MemoryRegion *address_space_mem = get_system_memory(); LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); - int nb_numa_nodes = machine->numa_state->num_nodes; - NodeInfo *numa_info = machine->numa_state->nodes; int i; + hwaddr base, size, ram_size = machine->ram_size; const CPUArchIdList *possible_cpus; MachineClass *mc = MACHINE_GET_CLASS(machine); CPUState *cpu; @@ -1023,40 +1019,27 @@ static void virt_init(MachineState *machine) fw_cfg_add_memory(machine); /* Node0 memory */ - memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.node0.lowram", - machine->ram, offset, VIRT_LOWMEM_SIZE); - memory_region_add_subregion(address_space_mem, phyAddr, &lvms->lowmem); - - offset += VIRT_LOWMEM_SIZE; - if (nb_numa_nodes > 0) { - assert(numa_info[0].node_mem > VIRT_LOWMEM_SIZE); - highram_size = numa_info[0].node_mem - VIRT_LOWMEM_SIZE; - } else { - highram_size = ram_size - VIRT_LOWMEM_SIZE; + size = ram_size; + base = VIRT_LOWMEM_BASE; + if (size > VIRT_LOWMEM_SIZE) { + size = VIRT_LOWMEM_SIZE; } - phyAddr = VIRT_HIGHMEM_BASE; - memory_region_init_alias(&lvms->highmem, NULL, "loongarch.node0.highram", - machine->ram, offset, highram_size); - memory_region_add_subregion(address_space_mem, phyAddr, &lvms->highmem); - /* Node1 - Nodemax memory */ - offset += highram_size; - phyAddr += highram_size; - - for (i = 1; i < nb_numa_nodes; i++) { - MemoryRegion *nodemem = g_new(MemoryRegion, 1); - g_autofree char *ramName = g_strdup_printf("loongarch.node%d.ram", i); - memory_region_init_alias(nodemem, NULL, ramName, machine->ram, - offset, numa_info[i].node_mem); - memory_region_add_subregion(address_space_mem, phyAddr, nodemem); - offset += numa_info[i].node_mem; - phyAddr += numa_info[i].node_mem; + memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.lowram", + machine->ram, base, size); + memory_region_add_subregion(address_space_mem, base, &lvms->lowmem); + base += size; + if (ram_size - size) { + base = VIRT_HIGHMEM_BASE; + memory_region_init_alias(&lvms->highmem, NULL, "loongarch.highram", + machine->ram, VIRT_LOWMEM_BASE + size, ram_size - size); + memory_region_add_subregion(address_space_mem, base, &lvms->highmem); + base += ram_size - size; } /* initialize device memory address space */ if (machine->ram_size < machine->maxram_size) { ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; - hwaddr device_mem_base; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported amount of memory slots: %"PRIu64, @@ -1070,9 +1053,7 @@ static void virt_init(MachineState *machine) "%d bytes", TARGET_PAGE_SIZE); exit(EXIT_FAILURE); } - /* device memory base is the top of high memory address. */ - device_mem_base = ROUND_UP(VIRT_HIGHMEM_BASE + highram_size, 1 * GiB); - machine_memory_devices_init(machine, device_mem_base, device_mem_size); + machine_memory_devices_init(machine, base, device_mem_size); } /* load the BIOS image. */ From ac551dbd586cd13a78d26933e2a9ee3b0867ccb0 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 15 May 2024 17:39:26 +0800 Subject: [PATCH 0815/2884] hw/loongarch: Remove minimum and default memory size Some qtest test cases such as numa use default memory size of generic machine class, which is 128M by fault. Here generic default memory size is used, and also remove minimum memory size which is 1G originally. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240515093927.3453674-6-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 4ec2b9a061..e3bdf085b5 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -990,10 +990,6 @@ static void virt_init(MachineState *machine) cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); } - if (ram_size < 1 * GiB) { - error_report("ram_size must be greater than 1G."); - exit(1); - } create_fdt(lvms); /* Create IOCSR space */ @@ -1279,7 +1275,6 @@ static void virt_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = virt_init; - mc->default_ram_size = 1 * GiB; mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); mc->default_ram_id = "loongarch.ram"; mc->max_cpus = LOONGARCH_MAX_CPUS; From f83434f3dc6df21c890ff1537c47905eedf1d69c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 21 May 2024 16:05:48 +0800 Subject: [PATCH 0816/2884] target/loongarch: Add loongarch vector property unconditionally Currently LSX/LASX vector property is decided by the default value. Instead vector property should be added unconditionally, and it is irrelative with its default value. If vector is disabled by default, vector also can be enabled from command line. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20240521080549.434197-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index a0cad53676..b5c1ec94af 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -645,16 +645,10 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp) void loongarch_cpu_post_init(Object *obj) { - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { - object_property_add_bool(obj, "lsx", loongarch_get_lsx, - loongarch_set_lsx); - } - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { - object_property_add_bool(obj, "lasx", loongarch_get_lasx, - loongarch_set_lasx); - } + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); } static void loongarch_cpu_init(Object *obj) From 6204af704a071ea68d3af55c0502b112a7af9546 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 20 May 2024 22:06:31 +0100 Subject: [PATCH 0817/2884] hw/loongarch/virt: Fix FDT memory node address width Higher bits for memory nodes were omitted at qemu_fdt_setprop_cells. Cc: qemu-stable@nongnu.org Signed-off-by: Jiaxun Yang Reviewed-by: Song Gao Message-Id: <20240520-loongarch-fdt-memnode-v1-1-5ea9be93911e@flygoat.com> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index e3bdf085b5..3e6e93edf3 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -464,7 +464,8 @@ static void fdt_add_memory_node(MachineState *ms, char *nodename = g_strdup_printf("/memory@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, 0, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base, + size >> 32, size); qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); if (ms->numa_state && ms->numa_state->num_nodes) { From af8c14a25477e0ea127ca66d5d9c0710da854906 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 May 2024 12:49:01 +0200 Subject: [PATCH 0818/2884] tcg: Introduce TCG_TARGET_HAS_tst_vec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prelude to supporting TCG_COND_TST* in vector comparisons. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 1 + tcg/aarch64/tcg-target.h | 1 + tcg/arm/tcg-target.h | 1 + tcg/i386/tcg-target.h | 1 + tcg/loongarch64/tcg-target.h | 1 + tcg/ppc/tcg-target.h | 1 + tcg/s390x/tcg-target.h | 1 + 7 files changed, 7 insertions(+) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 2a1c080bab..21d5884741 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -155,6 +155,7 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_minmax_vec 0 #define TCG_TARGET_HAS_bitsel_vec 0 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 #else #define TCG_TARGET_MAYBE_vec 1 #endif diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 85d5746e47..138bafb9da 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -167,6 +167,7 @@ typedef enum { #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index a43875cb09..434a892e07 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -150,6 +150,7 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index a10d4e1fce..2f67a97e05 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -224,6 +224,7 @@ typedef enum { #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec have_avx512vl #define TCG_TARGET_HAS_cmpsel_vec -1 +#define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_deposit_i32_valid(ofs, len) \ (((ofs) == 0 && ((len) == 8 || (len) == 16)) || \ diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index fede627bf7..29e4860d20 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -194,6 +194,7 @@ typedef enum { #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_DEFAULT_MO (0) diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 04a7aba4d3..e154fb14df 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -173,6 +173,7 @@ typedef enum { #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec have_vsx #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index ae448c3a3a..62ce9d792a 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -163,6 +163,7 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 8 From 6975cc45c3c68205865071795e52ad205598e421 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 May 2024 15:08:16 +0200 Subject: [PATCH 0819/2884] tcg: Expand TCG_COND_TST* if not TCG_TARGET_HAS_tst_vec Signed-off-by: Richard Henderson --- tcg/tcg-op-vec.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 094298bb27..84af210bc0 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -508,9 +508,11 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGTemp *rt = tcgv_vec_temp(r); TCGTemp *at = tcgv_vec_temp(a); TCGTemp *bt = tcgv_vec_temp(b); + TCGTemp *tt = NULL; TCGArg ri = temp_arg(rt); TCGArg ai = temp_arg(at); TCGArg bi = temp_arg(bt); + TCGArg ti; TCGType type = rt->base_type; int can; @@ -518,6 +520,18 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, tcg_debug_assert(bt->base_type >= type); tcg_assert_listed_vecop(INDEX_op_cmp_vec); can = tcg_can_emit_vec_op(INDEX_op_cmp_vec, type, vece); + + if (!TCG_TARGET_HAS_tst_vec && is_tst_cond(cond)) { + tt = tcg_temp_new_internal(type, TEMP_EBB); + ti = temp_arg(tt); + vec_gen_3(INDEX_op_and_vec, type, 0, ti, ai, bi); + at = tt; + ai = ti; + bt = tcg_constant_internal(type, 0); + bi = temp_arg(bt); + cond = tcg_tst_eqne_cond(cond); + } + if (can > 0) { vec_gen_4(INDEX_op_cmp_vec, type, vece, ri, ai, bi, cond); } else { @@ -526,6 +540,10 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, tcg_expand_vec_op(INDEX_op_cmp_vec, type, vece, ri, ai, bi, cond); tcg_swap_vecop_list(hold_list); } + + if (tt) { + tcg_temp_free_internal(tt); + } } static bool do_op3(unsigned vece, TCGv_vec r, TCGv_vec a, From b04574d44fed253ad638a1a2437df21b9831ba83 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 May 2024 15:19:53 +0200 Subject: [PATCH 0820/2884] tcg/aarch64: Support TCG_TARGET_HAS_tst_vec Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 26 ++++++++++++++++++++++++-- tcg/aarch64/tcg-target.h | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 56fc9cb9e0..ffa8a3e519 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2737,7 +2737,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, TCGCond cond = args[3]; AArch64Insn insn; - if (cond == TCG_COND_NE) { + switch (cond) { + case TCG_COND_NE: if (const_args[2]) { if (is_scalar) { tcg_out_insn(s, 3611, CMTST, vece, a0, a1, a1); @@ -2752,7 +2753,27 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } tcg_out_insn(s, 3617, NOT, is_q, 0, a0, a0); } - } else { + break; + + case TCG_COND_TSTNE: + case TCG_COND_TSTEQ: + if (const_args[2]) { + /* (x & 0) == 0 */ + tcg_out_dupi_vec(s, type, MO_8, a0, + -(cond == TCG_COND_TSTEQ)); + break; + } + if (is_scalar) { + tcg_out_insn(s, 3611, CMTST, vece, a0, a1, a2); + } else { + tcg_out_insn(s, 3616, CMTST, is_q, vece, a0, a1, a2); + } + if (cond == TCG_COND_TSTEQ) { + tcg_out_insn(s, 3617, NOT, is_q, 0, a0, a0); + } + break; + + default: if (const_args[2]) { if (is_scalar) { insn = cmp0_scalar_insn[cond]; @@ -2791,6 +2812,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } tcg_out_insn_3616(s, insn, is_q, vece, a0, a1, a2); } + break; } } break; diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 138bafb9da..8bd9e6a5eb 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -167,7 +167,7 @@ typedef enum { #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 -#define TCG_TARGET_HAS_tst_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS From f230c793a528a70903684b617fed1a593e169446 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 May 2024 14:14:11 +0000 Subject: [PATCH 0821/2884] tcg/arm: Support TCG_TARGET_HAS_tst_vec Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 23 ++++++++++++++++++++--- tcg/arm/tcg-target.h | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 6a04c73c76..3de5f50b62 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2740,17 +2740,33 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_cmp_vec: { TCGCond cond = args[3]; + ARMInsn insn; - if (cond == TCG_COND_NE) { + switch (cond) { + case TCG_COND_NE: if (const_args[2]) { tcg_out_vreg3(s, INSN_VTST, q, vece, a0, a1, a1); } else { tcg_out_vreg3(s, INSN_VCEQ, q, vece, a0, a1, a2); tcg_out_vreg2(s, INSN_VMVN, q, 0, a0, a0); } - } else { - ARMInsn insn; + break; + case TCG_COND_TSTNE: + case TCG_COND_TSTEQ: + if (const_args[2]) { + /* (x & 0) == 0 */ + tcg_out_dupi_vec(s, type, MO_8, a0, + -(cond == TCG_COND_TSTEQ)); + break; + } + tcg_out_vreg3(s, INSN_VTST, q, vece, a0, a1, a2); + if (cond == TCG_COND_TSTEQ) { + tcg_out_vreg2(s, INSN_VMVN, q, 0, a0, a0); + } + break; + + default: if (const_args[2]) { insn = vec_cmp0_insn[cond]; if (insn) { @@ -2769,6 +2785,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_debug_assert(insn != 0); } tcg_out_vreg3(s, insn, q, vece, a0, a1, a2); + break; } } return; diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 434a892e07..fb7261499b 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -150,7 +150,7 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 -#define TCG_TARGET_HAS_tst_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS From bfd43cccab9fb77b8405ca556fc2f2ed3b2920a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 21 May 2024 13:56:14 -0700 Subject: [PATCH 0822/2884] accel/tcg: Init tb size and icount before plugin_gen_tb_end When passing disassembly data to plugin callbacks, translator_st_len relies on db->tb->size having been set. Fixes: 4c833c60e047 ("disas: Use translator_st to get disassembly data") Reported-by: Bernhard Beschow Signed-off-by: Richard Henderson Tested-by: Bernhard Beschow Reviewed-by: Pierrick Bouvier --- accel/tcg/translator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index c56967eecd..113edcffe3 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -214,14 +214,14 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, set_can_do_io(db, true); tcg_ctx->emit_before_op = NULL; + /* May be used by disas_log or plugin callbacks. */ + tb->size = db->pc_next - db->pc_first; + tb->icount = db->num_insns; + if (plugin_enabled) { plugin_gen_tb_end(cpu, db->num_insns); } - /* The disas_log hook may use these values rather than recompute. */ - tb->size = db->pc_next - db->pc_first; - tb->icount = db->num_insns; - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(db->pc_first)) { FILE *logfile = qemu_log_trylock(); From c700b5e162208a0fa4211fc6d9dab271b1342640 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 21 Feb 2024 20:08:31 +1000 Subject: [PATCH 0823/2884] spapr: avoid overhead of finding vhyp class in critical operations PPC_VIRTUAL_HYPERVISOR_GET_CLASS is used in critical operations like interrupts and TLB misses and is quite costly. Running the kvm-unit-tests sieve program with radix MMU enabled thrashes the TCG TLB and spends a lot of time in TLB and page table walking code. The test takes 67 seconds to complete with a lot of time being spent in code related to finding the vhyp class: 12.01% [.] g_str_hash 8.94% [.] g_hash_table_lookup 8.06% [.] object_class_dynamic_cast 6.21% [.] address_space_ldq 4.94% [.] __strcmp_avx2 4.28% [.] tlb_set_page_full 4.08% [.] address_space_translate_internal 3.17% [.] object_class_dynamic_cast_assert 2.84% [.] ppc_radix64_xlate Keep a pointer to the class and avoid this lookup. This reduces the execution time to 40 seconds. Reviewed-by: Harsh Prateek Bora Signed-off-by: Nicholas Piggin --- hw/ppc/pegasos2.c | 1 + target/ppc/cpu.h | 3 ++- target/ppc/cpu_init.c | 9 +++------ target/ppc/excp_helper.c | 16 ++++------------ target/ppc/kvm.c | 4 +--- target/ppc/mmu-book3s-v3.h | 4 +--- target/ppc/mmu-hash64.c | 16 ++++------------ target/ppc/mmu-radix64.c | 4 +--- 8 files changed, 17 insertions(+), 40 deletions(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 04d6decb2b..c22e8b336d 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -400,6 +400,7 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) machine->fdt = fdt; pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); + pm->cpu->vhyp_class = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(pm->cpu->vhyp); } enum pegasos2_rtas_tokens { diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0ac55d6b25..a5f46d0b10 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1435,6 +1435,7 @@ struct ArchCPU { int vcpu_id; uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; + PPCVirtualHypervisorClass *vhyp_class; void *machine_data; int32_t node_id; /* NUMA node this CPU belongs to */ PPCHash64Options *hash64_opts; @@ -1532,7 +1533,7 @@ DECLARE_OBJ_CHECKERS(PPCVirtualHypervisor, PPCVirtualHypervisorClass, static inline bool vhyp_cpu_in_nested(PowerPCCPU *cpu) { - return PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp)->cpu_in_nested(cpu); + return cpu->vhyp_class->cpu_in_nested(cpu); } #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index c11a69fd90..914c6e0f18 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6661,6 +6661,7 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) CPUPPCState *env = &cpu->env; cpu->vhyp = vhyp; + cpu->vhyp_class = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(vhyp); /* * With a virtual hypervisor mode we never allow the CPU to go @@ -7248,9 +7249,7 @@ static void ppc_cpu_exec_enter(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->cpu_exec_enter(cpu->vhyp, cpu); + cpu->vhyp_class->cpu_exec_enter(cpu->vhyp, cpu); } } @@ -7259,9 +7258,7 @@ static void ppc_cpu_exec_exit(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->cpu_exec_exit(cpu->vhyp, cpu); + cpu->vhyp_class->cpu_exec_exit(cpu->vhyp, cpu); } } #endif /* CONFIG_TCG */ diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0712098cf7..9df17f93bf 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -794,9 +794,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) * HV mode, we need to keep hypercall support. */ if (lev == 1 && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -946,9 +944,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) * HV mode, we need to keep hypercall support. */ if (lev == 1 && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -1437,9 +1433,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) /* "PAPR mode" built-in hypercall emulation */ if (lev == 1 && books_vhyp_handles_hcall(cpu)) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -1574,10 +1568,8 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) } if ((new_msr & MSR_HVB) && books_vhyp_handles_hv_excp(cpu)) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); /* Deliver interrupt to L1 by returning from the H_ENTER_NESTED call */ - vhc->deliver_hv_excp(cpu, excp); + cpu->vhyp_class->deliver_hv_excp(cpu, excp); powerpc_reset_excp_state(cpu); } else { /* Sanity check */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 46fccff786..005f2239f3 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -865,9 +865,7 @@ int kvmppc_put_books_sregs(PowerPCCPU *cpu) sregs.pvr = env->spr[SPR_PVR]; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - sregs.u.s.sdr1 = vhc->encode_hpt_for_kvm_pr(cpu->vhyp); + sregs.u.s.sdr1 = cpu->vhyp_class->encode_hpt_for_kvm_pr(cpu->vhyp); } else { sregs.u.s.sdr1 = env->spr[SPR_SDR1]; } diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 674377a19e..f3f7993958 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -108,9 +108,7 @@ static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) uint64_t base; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->hpt_mask(cpu->vhyp); + return cpu->vhyp_class->hpt_mask(cpu->vhyp); } if (cpu->env.mmu_model == POWERPC_MMU_3_00) { ppc_v3_pate_t pate; diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 0966422a55..accbf0b2d8 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -517,9 +517,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->map_hptes(cpu->vhyp, ptex, n); + return cpu->vhyp_class->map_hptes(cpu->vhyp, ptex, n); } base = ppc_hash64_hpt_base(cpu); @@ -539,9 +537,7 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n) { if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->unmap_hptes(cpu->vhyp, hptes, ptex, n); + cpu->vhyp_class->unmap_hptes(cpu->vhyp, hptes, ptex, n); return; } @@ -821,9 +817,7 @@ static void ppc_hash64_set_r(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_R; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hpte_set_r(cpu->vhyp, ptex, pte1); + cpu->vhyp_class->hpte_set_r(cpu->vhyp, ptex, pte1); return; } base = ppc_hash64_hpt_base(cpu); @@ -838,9 +832,7 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_C; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hpte_set_c(cpu->vhyp, ptex, pte1); + cpu->vhyp_class->hpte_set_c(cpu->vhyp, ptex, pte1); return; } base = ppc_hash64_hpt_base(cpu); diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 8daf71d2db..fefa55a5f1 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -678,9 +678,7 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, /* Get Partition Table */ if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc; - vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - if (!vhc->get_pate(cpu->vhyp, cpu, lpid, &pate)) { + if (!cpu->vhyp_class->get_pate(cpu->vhyp, cpu, lpid, &pate)) { if (guest_visible) { ppc_radix64_raise_hsi(cpu, access_type, eaddr, eaddr, DSISR_R_BADCONFIG); From 95912ce1ebe4303d17118219691573ae6227b0e2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 22 Jan 2024 16:21:15 +1000 Subject: [PATCH 0824/2884] ppc/spapr: Add ibm,pi-features The ibm,pi-features property has a bit to say whether or not msgsndp should be used. Linux checks if it is being run under KVM and avoids msgsndp anyway, but it would be preferable to rely on this bit. Reviewed-by: Harsh Prateek Bora Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d2d1e310a3..4345764bce 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -353,6 +353,32 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); } +static void spapr_dt_pi_features(SpaprMachineState *spapr, + PowerPCCPU *cpu, + void *fdt, int offset) +{ + uint8_t pi_features[] = { 1, 0, + 0x00 }; + + if (kvm_enabled() && ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, + 0, cpu->compat_pvr)) { + /* + * POWER9 and later CPUs with KVM run in LPAR-per-thread mode where + * all threads are essentially independent CPUs, and msgsndp does not + * work (because it is physically-addressed) and therefore is + * emulated by KVM, so disable it here to ensure XIVE will be used. + * This is both KVM and CPU implementation-specific behaviour so a KVM + * cap would be cleanest, but for now this works. If KVM ever permits + * native msgsndp execution by guests, a cap could be added at that + * time. + */ + pi_features[2] |= 0x08; /* 4: No msgsndp */ + } + + _FDT((fdt_setprop(fdt, offset, "ibm,pi-features", pi_features, + sizeof(pi_features)))); +} + static hwaddr spapr_node0_size(MachineState *machine) { if (machine->numa_state->num_nodes) { @@ -815,6 +841,8 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, spapr_dt_pa_features(spapr, cpu, fdt, offset); + spapr_dt_pi_features(spapr, cpu, fdt, offset); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); From 82676f1fc4b1511a5fe32256aaec885d200ffbf6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 26 Mar 2024 23:20:43 +1000 Subject: [PATCH 0825/2884] target/ppc: Fix broadcast tlbie synchronisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With mttcg, broadcast tlbie instructions do not wait until other vCPUs have been kicked out of TCG execution before they complete (including necessary subsequent tlbsync, etc., instructions). This is contrary to the ISA, and it permits other vCPUs to use translations after the TLB flush. For example: CPU0 // *memP is initially 0, memV maps to memP with *pte *pte = 0; ptesync ; tlbie ; eieio ; tlbsync ; ptesync *memP = 1; CPU1 assert(*memV == 0); It is possible for the assertion to fail because CPU1 translates memV using the TLB after CPU0 has stored 1 to the underlying memory. This race was observed with a careful test case where CPU1 checks run in a very large expensive TB so it can run for the entire CPU0 period between clearing the pte and storing the memory, but host vCPU thread preemption could cause the race to hit anywhere. As explained in commit 4ddc104689b ("target/ppc: Fix tlbie"), it is not enough to just use tlb_flush_all_cpus_synced(), because that does not execute until the calling CPU has finished its TB. It is also required that the TB is ended at the point where the TLB flush must subsequently take effect. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Nicholas Piggin --- target/ppc/helper_regs.c | 2 +- target/ppc/mmu_helper.c | 2 +- target/ppc/translate.c | 7 +++++++ target/ppc/translate/storage-ctrl-impl.c.inc | 7 +++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 25258986e3..9094ae5004 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -334,7 +334,7 @@ void check_tlb_flush(CPUPPCState *env, bool global) if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; - tlb_flush_all_cpus(cs); + tlb_flush_all_cpus_synced(cs); return; } diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index b35a93c198..d9d950e220 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -534,7 +534,7 @@ void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, if (local) { tlb_flush_page(env_cpu(env), addr); } else { - tlb_flush_page_all_cpus(env_cpu(env), addr); + tlb_flush_page_all_cpus_synced(env_cpu(env), addr); } return; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 49dee6cab0..24461c2d1b 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3494,6 +3494,13 @@ static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) gen_helper_check_tlb_flush_local(tcg_env); } gen_set_label(l); + if (global) { + /* + * Global TLB flush uses async-work which must run before the + * next instruction, so this must be the last in the TB. + */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } } #else static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { } diff --git a/target/ppc/translate/storage-ctrl-impl.c.inc b/target/ppc/translate/storage-ctrl-impl.c.inc index 74c23a4191..b8b4454663 100644 --- a/target/ppc/translate/storage-ctrl-impl.c.inc +++ b/target/ppc/translate/storage-ctrl-impl.c.inc @@ -224,6 +224,13 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) a->prs << TLBIE_F_PRS_SHIFT | a->r << TLBIE_F_R_SHIFT | local << TLBIE_F_LOCAL_SHIFT)); + if (!local) { + /* + * Global TLB flush uses async-work which must run before the + * next instruction, so this must be the last in the TB. + */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } return true; #endif From 99cd12ced16d15a1ffde055f842497747f070f91 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 27 Mar 2024 00:04:20 +1000 Subject: [PATCH 0826/2884] tcg/cputlb: Remove non-synced variants of global TLB flushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are no longer used. tlb_flush_all_cpus: removed by previous commit. tlb_flush_page_all_cpus: removed by previous commit. tlb_flush_page_bits_by_mmuidx_all_cpus: never used. tlb_flush_page_by_mmuidx_all_cpus: never used. tlb_flush_page_bits_by_mmuidx_all_cpus: never used, thus: tlb_flush_range_by_mmuidx_all_cpus: never used. tlb_flush_by_mmuidx_all_cpus: never used. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Nicholas Piggin --- accel/tcg/cputlb.c | 103 -------------------------------- docs/devel/multi-thread-tcg.rst | 13 ++-- include/exec/exec-all.h | 101 ++++++------------------------- 3 files changed, 21 insertions(+), 196 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index cdb3e12dfb..45799869eb 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -431,21 +431,6 @@ void tlb_flush(CPUState *cpu) tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS); } -void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap) -{ - const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; - - tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap); - - flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); - fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap)); -} - -void tlb_flush_all_cpus(CPUState *src_cpu) -{ - tlb_flush_by_mmuidx_all_cpus(src_cpu, ALL_MMUIDX_BITS); -} - void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) { const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; @@ -656,46 +641,6 @@ void tlb_flush_page(CPUState *cpu, vaddr addr) tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS); } -void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, vaddr addr, - uint16_t idxmap) -{ - tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); - - /* This should already be page aligned */ - addr &= TARGET_PAGE_MASK; - - /* - * Allocate memory to hold addr+idxmap only when needed. - * See tlb_flush_page_by_mmuidx for details. - */ - if (idxmap < TARGET_PAGE_SIZE) { - flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, - RUN_ON_CPU_TARGET_PTR(addr | idxmap)); - } else { - CPUState *dst_cpu; - - /* Allocate a separate data block for each destination cpu. */ - CPU_FOREACH(dst_cpu) { - if (dst_cpu != src_cpu) { - TLBFlushPageByMMUIdxData *d - = g_new(TLBFlushPageByMMUIdxData, 1); - - d->addr = addr; - d->idxmap = idxmap; - async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, - RUN_ON_CPU_HOST_PTR(d)); - } - } - } - - tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap); -} - -void tlb_flush_page_all_cpus(CPUState *src, vaddr addr) -{ - tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS); -} - void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, uint16_t idxmap) @@ -887,54 +832,6 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); } -void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu, - vaddr addr, vaddr len, - uint16_t idxmap, unsigned bits) -{ - TLBFlushRangeData d; - CPUState *dst_cpu; - - /* - * If all bits are significant, and len is small, - * this devolves to tlb_flush_page. - */ - if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) { - tlb_flush_page_by_mmuidx_all_cpus(src_cpu, addr, idxmap); - return; - } - /* If no page bits are significant, this devolves to tlb_flush. */ - if (bits < TARGET_PAGE_BITS) { - tlb_flush_by_mmuidx_all_cpus(src_cpu, idxmap); - return; - } - - /* This should already be page aligned */ - d.addr = addr & TARGET_PAGE_MASK; - d.len = len; - d.idxmap = idxmap; - d.bits = bits; - - /* Allocate a separate data block for each destination cpu. */ - CPU_FOREACH(dst_cpu) { - if (dst_cpu != src_cpu) { - TLBFlushRangeData *p = g_memdup(&d, sizeof(d)); - async_run_on_cpu(dst_cpu, - tlb_flush_range_by_mmuidx_async_1, - RUN_ON_CPU_HOST_PTR(p)); - } - } - - tlb_flush_range_by_mmuidx_async_0(src_cpu, d); -} - -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu, - vaddr addr, uint16_t idxmap, - unsigned bits) -{ - tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE, - idxmap, bits); -} - void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, vaddr len, diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index 1420789fff..d706c27ea7 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -205,15 +205,10 @@ DESIGN REQUIREMENTS: (Current solution) -We have updated cputlb.c to defer operations when a cross-vCPU -operation with async_run_on_cpu() which ensures each vCPU sees a -coherent state when it next runs its work (in a few instructions -time). - -A new set up operations (tlb_flush_*_all_cpus) take an additional flag -which when set will force synchronisation by setting the source vCPUs -work as "safe work" and exiting the cpu run loop. This ensure by the -time execution restarts all flush operations have completed. +A new set of tlb flush operations (tlb_flush_*_all_cpus_synced) force +synchronisation by setting the source vCPUs work as "safe work" and +exiting the cpu run loop. This ensures that by the time execution +restarts all flush operations have completed. TLB flag updates are all done atomically and are also protected by the corresponding page lock. diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 2cd7b8f61b..b6b46ad13c 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -67,25 +67,16 @@ void tlb_destroy(CPUState *cpu); * MMU indexes. */ void tlb_flush_page(CPUState *cpu, vaddr addr); -/** - * tlb_flush_page_all_cpus: - * @cpu: src CPU of the flush - * @addr: virtual address of page to be flushed - * - * Flush one page from the TLB of the specified CPU, for all - * MMU indexes. - */ -void tlb_flush_page_all_cpus(CPUState *src, vaddr addr); /** * tlb_flush_page_all_cpus_synced: * @cpu: src CPU of the flush * @addr: virtual address of page to be flushed * - * Flush one page from the TLB of the specified CPU, for all MMU - * indexes like tlb_flush_page_all_cpus except the source vCPUs work - * is scheduled as safe work meaning all flushes will be complete once - * the source vCPUs safe work is complete. This will depend on when - * the guests translation ends the TB. + * Flush one page from the TLB of all CPUs, for all + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); /** @@ -98,19 +89,14 @@ void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); * use one of the other functions for efficiency. */ void tlb_flush(CPUState *cpu); -/** - * tlb_flush_all_cpus: - * @cpu: src CPU of the flush - */ -void tlb_flush_all_cpus(CPUState *src_cpu); /** * tlb_flush_all_cpus_synced: * @cpu: src CPU of the flush * - * Like tlb_flush_all_cpus except this except the source vCPUs work is - * scheduled as safe work meaning all flushes will be complete once - * the source vCPUs safe work is complete. This will depend on when - * the guests translation ends the TB. + * Flush the entire TLB for all CPUs, for all MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_all_cpus_synced(CPUState *src_cpu); /** @@ -125,27 +111,16 @@ void tlb_flush_all_cpus_synced(CPUState *src_cpu); void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap); /** - * tlb_flush_page_by_mmuidx_all_cpus: + * tlb_flush_page_by_mmuidx_all_cpus_synced: * @cpu: Originating CPU of the flush * @addr: virtual address of page to be flushed * @idxmap: bitmap of MMU indexes to flush * * Flush one page from the TLB of all CPUs, for the specified * MMU indexes. - */ -void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - uint16_t idxmap); -/** - * tlb_flush_page_by_mmuidx_all_cpus_synced: - * @cpu: Originating CPU of the flush - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of MMU indexes to flush * - * Flush one page from the TLB of all CPUs, for the specified MMU - * indexes like tlb_flush_page_by_mmuidx_all_cpus except the source - * vCPUs work is scheduled as safe work meaning all flushes will be - * complete once the source vCPUs safe work is complete. This will - * depend on when the guests translation ends the TB. + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap); @@ -159,25 +134,16 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, * MMU indexes. */ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); -/** - * tlb_flush_by_mmuidx_all_cpus: - * @cpu: Originating CPU of the flush - * @idxmap: bitmap of MMU indexes to flush - * - * Flush all entries from all TLBs of all CPUs, for the specified - * MMU indexes. - */ -void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap); /** * tlb_flush_by_mmuidx_all_cpus_synced: * @cpu: Originating CPU of the flush * @idxmap: bitmap of MMU indexes to flush * - * Flush all entries from all TLBs of all CPUs, for the specified - * MMU indexes like tlb_flush_by_mmuidx_all_cpus except except the source - * vCPUs work is scheduled as safe work meaning all flushes will be - * complete once the source vCPUs safe work is complete. This will - * depend on when the guests translation ends the TB. + * Flush all entries from the TLB of all CPUs, for the specified + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); @@ -194,8 +160,6 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); /* Similarly, with broadcast and syncing. */ -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits); void tlb_flush_page_bits_by_mmuidx_all_cpus_synced (CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); @@ -215,9 +179,6 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, unsigned bits); /* Similarly, with broadcast and syncing. */ -void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits); void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, vaddr len, @@ -290,18 +251,12 @@ static inline void tlb_destroy(CPUState *cpu) static inline void tlb_flush_page(CPUState *cpu, vaddr addr) { } -static inline void tlb_flush_page_all_cpus(CPUState *src, vaddr addr) -{ -} static inline void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr) { } static inline void tlb_flush(CPUState *cpu) { } -static inline void tlb_flush_all_cpus(CPUState *src_cpu) -{ -} static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu) { } @@ -313,20 +268,11 @@ static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { } -static inline void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - uint16_t idxmap) -{ -} static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap) { } -static inline void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap) -{ -} - static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { @@ -337,12 +283,6 @@ static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, unsigned bits) { } -static inline void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - uint16_t idxmap, - unsigned bits) -{ -} static inline void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits) @@ -353,13 +293,6 @@ static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, unsigned bits) { } -static inline void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits) -{ -} static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, vaddr len, From 30933c4fb4f3df95ae44c4c3c86a5df049852c01 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 27 Mar 2024 00:18:14 +1000 Subject: [PATCH 0827/2884] tcg/cputlb: remove other-cpu capability from TLB flushing Some TLB flush operations can flush other CPUs. The problem with this is they used non-synced variants of flushes (i.e., that return before the destination has completed the flush). Since all TLB flush users need the _synced variants, and that last user (ppc) of the non-synced flush was buggy, this is a footgun waiting to go off. There do not seem to be any callers that flush other CPUs, so remove the capability. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- accel/tcg/cputlb.c | 42 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 45799869eb..117b516739 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -418,12 +418,9 @@ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); - if (cpu->created && !qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work, - RUN_ON_CPU_HOST_INT(idxmap)); - } else { - tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); - } + assert_cpu_is_self(cpu); + + tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); } void tlb_flush(CPUState *cpu) @@ -612,28 +609,12 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap) { tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap); + assert_cpu_is_self(cpu); + /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; - if (qemu_cpu_is_self(cpu)) { - tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); - } else if (idxmap < TARGET_PAGE_SIZE) { - /* - * Most targets have only a few mmu_idx. In the case where - * we can stuff idxmap into the low TARGET_PAGE_BITS, avoid - * allocating memory for this operation. - */ - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1, - RUN_ON_CPU_TARGET_PTR(addr | idxmap)); - } else { - TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1); - - /* Otherwise allocate a structure, freed by the worker. */ - d->addr = addr; - d->idxmap = idxmap; - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2, - RUN_ON_CPU_HOST_PTR(d)); - } + tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); } void tlb_flush_page(CPUState *cpu, vaddr addr) @@ -796,6 +777,8 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, { TLBFlushRangeData d; + assert_cpu_is_self(cpu); + /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. @@ -816,14 +799,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, d.idxmap = idxmap; d.bits = bits; - if (qemu_cpu_is_self(cpu)) { - tlb_flush_range_by_mmuidx_async_0(cpu, d); - } else { - /* Otherwise allocate a structure, freed by the worker. */ - TLBFlushRangeData *p = g_memdup(&d, sizeof(d)); - async_run_on_cpu(cpu, tlb_flush_range_by_mmuidx_async_1, - RUN_ON_CPU_HOST_PTR(p)); - } + tlb_flush_range_by_mmuidx_async_0(cpu, d); } void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, From 13f50867837874892f33c32a4452843d9ce7144c Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 1 May 2024 23:04:32 +1000 Subject: [PATCH 0828/2884] target/ppc: Move sync instructions to decodetree This tries to faithfully reproduce the odd BookE logic. Note the e206 check in gen_msync_4xx() is always false, so not carried over. It does change the handling of non-zero reserved bits outside the defined fields from being illegal to being ignored, which the architecture specifies ot help with backward compatibility of new fields. The existing behaviour causes illegal instruction exceptions when using new POWER10 sync variants that add new fields, after this the instructions are accepted and are implemented as supersets of the new behaviour, as intended. Reviewed-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 7 ++ target/ppc/translate.c | 102 +-------------------- target/ppc/translate/misc-impl.c.inc | 130 +++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 100 deletions(-) create mode 100644 target/ppc/translate/misc-impl.c.inc diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index eada59f59f..6b89804b15 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -998,3 +998,10 @@ MSGSND 011111 ----- ----- ..... 0011001110 - @X_rb MSGCLRP 011111 ----- ----- ..... 0010101110 - @X_rb MSGSNDP 011111 ----- ----- ..... 0010001110 - @X_rb MSGSYNC 011111 ----- ----- ----- 1101110110 - + +# Memory Barrier Instructions + +&X_sync l +@X_sync ...... ... l:2 ..... ..... .......... . &X_sync +SYNC 011111 --- .. ----- ----- 1001010110 - @X_sync +EIEIO 011111 ----- ----- ----- 1101010110 - diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 24461c2d1b..a70c5ed951 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3422,59 +3422,6 @@ static void gen_stswx(DisasContext *ctx) gen_helper_stsw(tcg_env, t0, t1, t2); } -/*** Memory synchronisation ***/ -/* eieio */ -static void gen_eieio(DisasContext *ctx) -{ - TCGBar bar = TCG_MO_ALL; - - /* - * eieio has complex semanitcs. It provides memory ordering between - * operations in the set: - * - loads from CI memory. - * - stores to CI memory. - * - stores to WT memory. - * - * It separately also orders memory for operations in the set: - * - stores to cacheble memory. - * - * It also serializes instructions: - * - dcbt and dcbst. - * - * It separately serializes: - * - tlbie and tlbsync. - * - * And separately serializes: - * - slbieg, slbiag, and slbsync. - * - * The end result is that CI memory ordering requires TCG_MO_ALL - * and it is not possible to special-case more relaxed ordering for - * cacheable accesses. TCG_BAR_SC is required to provide this - * serialization. - */ - - /* - * POWER9 has a eieio instruction variant using bit 6 as a hint to - * tell the CPU it is a store-forwarding barrier. - */ - if (ctx->opcode & 0x2000000) { - /* - * ISA says that "Reserved fields in instructions are ignored - * by the processor". So ignore the bit 6 on non-POWER9 CPU but - * as this is not an instruction software should be using, - * complain to the user. - */ - if (!(ctx->insns_flags2 & PPC2_ISA300)) { - qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" - TARGET_FMT_lx "\n", ctx->cia); - } else { - bar = TCG_MO_ST_LD; - } - } - - tcg_gen_mb(bar | TCG_BAR_SC); -} - #if !defined(CONFIG_USER_ONLY) static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { @@ -3883,31 +3830,6 @@ static void gen_stqcx_(DisasContext *ctx) } #endif /* defined(TARGET_PPC64) */ -/* sync */ -static void gen_sync(DisasContext *ctx) -{ - TCGBar bar = TCG_MO_ALL; - uint32_t l = (ctx->opcode >> 21) & 3; - - if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { - bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; - } - - /* - * We may need to check for a pending TLB flush. - * - * We do this on ptesync (l == 2) on ppc64 and any sync pn ppc32. - * - * Additionally, this can only happen in kernel mode however so - * check MSR_PR as well. - */ - if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { - gen_check_tlb_flush(ctx, true); - } - - tcg_gen_mb(bar | TCG_BAR_SC); -} - /* wait */ static void gen_wait(DisasContext *ctx) { @@ -6016,23 +5938,6 @@ static void gen_dlmzb(DisasContext *ctx) cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); } -/* mbar replaces eieio on 440 */ -static void gen_mbar(DisasContext *ctx) -{ - /* interpreted as no-op */ -} - -/* msync replaces sync on 440 */ -static void gen_msync_4xx(DisasContext *ctx) -{ - /* Only e500 seems to treat reserved bits as invalid */ - if ((ctx->insns_flags2 & PPC2_BOOKE206) && - (ctx->opcode & 0x03FFF801)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - } - /* otherwise interpreted as no-op */ -} - /* icbt */ static void gen_icbt_440(DisasContext *ctx) { @@ -6370,6 +6275,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/storage-ctrl-impl.c.inc" +#include "translate/misc-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { @@ -6498,7 +6405,6 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING), GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), -GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x01FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), @@ -6516,7 +6422,6 @@ GEN_HANDLER_E(lqarx, 0x1F, 0x14, 0x08, 0, PPC_NONE, PPC2_LSQ_ISA207), GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), GEN_HANDLER_E(stqcx_, 0x1F, 0x16, 0x05, 0, PPC_NONE, PPC2_LSQ_ISA207), #endif -GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), /* ISA v3.0 changed the extended opcode from 62 to 30 */ GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x039FF801, PPC_WAIT), GEN_HANDLER_E(wait, 0x1F, 0x1E, 0x00, 0x039CF801, PPC_NONE, PPC2_ISA300), @@ -6639,9 +6544,6 @@ GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001, GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE), GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE), GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC), -GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, - PPC_BOOKE, PPC2_BOOKE206), -GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x039FF801, PPC_BOOKE), GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc new file mode 100644 index 0000000000..cb1a2b707e --- /dev/null +++ b/target/ppc/translate/misc-impl.c.inc @@ -0,0 +1,130 @@ +/* + * Power ISA decode for misc instructions + * + * Copyright (c) 2024, IBM Corporation. + * + * 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.1 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 . + */ + +/* + * Memory Barrier Instructions + */ + +static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) +{ + TCGBar bar = TCG_MO_ALL; + uint32_t l = a->l; + + /* + * BookE uses the msync mnemonic. This means hwsync, except in the + * 440, where it an execution serialisation point that requires all + * previous storage accesses to have been performed to memory (which + * doesn't matter for TCG). + */ + if (!(ctx->insns_flags & PPC_MEM_SYNC)) { + if (ctx->insns_flags & PPC_BOOKE) { + /* msync replaces sync on 440, interpreted as nop */ + /* XXX: this also catches e200 */ + return true; + } + + return false; + } + + if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { + bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + } + + /* + * We may need to check for a pending TLB flush. + * + * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. + * + * Additionally, this can only happen in kernel mode however so + * check MSR_PR as well. + */ + if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { + gen_check_tlb_flush(ctx, true); + } + + tcg_gen_mb(bar | TCG_BAR_SC); + + return true; +} + +static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) +{ + TCGBar bar = TCG_MO_ALL; + + /* + * BookE uses the mbar instruction instead of eieio, which is basically + * full hwsync memory barrier, but is not execution synchronising. For + * the purpose of TCG the distinction is not relevant. + */ + if (!(ctx->insns_flags & PPC_MEM_EIEIO)) { + if ((ctx->insns_flags & PPC_BOOKE) || + (ctx->insns_flags2 & PPC2_BOOKE206)) { + return true; + } + return false; + } + + /* + * eieio has complex semanitcs. It provides memory ordering between + * operations in the set: + * - loads from CI memory. + * - stores to CI memory. + * - stores to WT memory. + * + * It separately also orders memory for operations in the set: + * - stores to cacheble memory. + * + * It also serializes instructions: + * - dcbt and dcbst. + * + * It separately serializes: + * - tlbie and tlbsync. + * + * And separately serializes: + * - slbieg, slbiag, and slbsync. + * + * The end result is that CI memory ordering requires TCG_MO_ALL + * and it is not possible to special-case more relaxed ordering for + * cacheable accesses. TCG_BAR_SC is required to provide this + * serialization. + */ + + /* + * POWER9 has a eieio instruction variant using bit 6 as a hint to + * tell the CPU it is a store-forwarding barrier. + */ + if (ctx->opcode & 0x2000000) { + /* + * ISA says that "Reserved fields in instructions are ignored + * by the processor". So ignore the bit 6 on non-POWER9 CPU but + * as this is not an instruction software should be using, + * complain to the user. + */ + if (!(ctx->insns_flags2 & PPC2_ISA300)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" + TARGET_FMT_lx "\n", ctx->cia); + } else { + bar = TCG_MO_ST_LD; + } + } + + tcg_gen_mb(bar | TCG_BAR_SC); + + return true; +} From ab4f174baee4b28b454fc94a7de2978c13a423ac Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 1 May 2024 23:04:33 +1000 Subject: [PATCH 0829/2884] target/ppc: Fix embedded memory barriers Memory barriers are supposed to do something on BookE systems, these were probably just missed during MTTCG enablement, maybe no targets support SMP. Either way, add proper BookE implementations. Reviewed-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/translate/misc-impl.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc index cb1a2b707e..7574317600 100644 --- a/target/ppc/translate/misc-impl.c.inc +++ b/target/ppc/translate/misc-impl.c.inc @@ -34,8 +34,7 @@ static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) */ if (!(ctx->insns_flags & PPC_MEM_SYNC)) { if (ctx->insns_flags & PPC_BOOKE) { - /* msync replaces sync on 440, interpreted as nop */ - /* XXX: this also catches e200 */ + tcg_gen_mb(bar | TCG_BAR_SC); return true; } @@ -75,6 +74,7 @@ static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) if (!(ctx->insns_flags & PPC_MEM_EIEIO)) { if ((ctx->insns_flags & PPC_BOOKE) || (ctx->insns_flags2 & PPC2_BOOKE206)) { + tcg_gen_mb(bar | TCG_BAR_SC); return true; } return false; From b3cfa2dd2b4dc517b9423bf568a358ac5fdd2752 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 1 May 2024 23:04:34 +1000 Subject: [PATCH 0830/2884] target/ppc: Add ISA v3.1 variants of sync instruction POWER10 adds a new field to sync for store-store syncs, and some new variants of the existing syncs that include persistent memory. Implement the store-store syncs and plwsync/phwsync. Reviewed-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 6 ++-- target/ppc/translate/misc-impl.c.inc | 41 ++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 6b89804b15..a180380750 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1001,7 +1001,7 @@ MSGSYNC 011111 ----- ----- ----- 1101110110 - # Memory Barrier Instructions -&X_sync l -@X_sync ...... ... l:2 ..... ..... .......... . &X_sync -SYNC 011111 --- .. ----- ----- 1001010110 - @X_sync +&X_sync l sc +@X_sync ...... .. l:3 ... sc:2 ..... .......... . &X_sync +SYNC 011111 -- ... --- .. ----- 1001010110 - @X_sync EIEIO 011111 ----- ----- ----- 1101010110 - diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc index 7574317600..c1661d2f43 100644 --- a/target/ppc/translate/misc-impl.c.inc +++ b/target/ppc/translate/misc-impl.c.inc @@ -25,6 +25,7 @@ static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) { TCGBar bar = TCG_MO_ALL; uint32_t l = a->l; + uint32_t sc = a->sc; /* * BookE uses the msync mnemonic. This means hwsync, except in the @@ -41,20 +42,36 @@ static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) return false; } - if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { - bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + /* + * In ISA v3.1, the L field grew one bit. Mask that out to ignore it in + * older processors. It also added the SC field, zero this to ignore + * it too. + */ + if (!(ctx->insns_flags2 & PPC2_ISA310)) { + l &= 0x3; + sc = 0; } - /* - * We may need to check for a pending TLB flush. - * - * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. - * - * Additionally, this can only happen in kernel mode however so - * check MSR_PR as well. - */ - if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { - gen_check_tlb_flush(ctx, true); + if (sc) { + /* Store syncs [stsync, stcisync, stncisync]. These ignore L. */ + bar = TCG_MO_ST_ST; + } else { + if (((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) || (l == 5)) { + /* lwsync, or plwsync on POWER10 and later */ + bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + } + + /* + * We may need to check for a pending TLB flush. + * + * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. + * + * Additionally, this can only happen in kernel mode however so + * check MSR_PR as well. + */ + if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { + gen_check_tlb_flush(ctx, true); + } } tcg_gen_mb(bar | TCG_BAR_SC); From 5747926fec6c65b60f9f7aca6d4df525c79eae8e Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Fri, 15 Mar 2024 12:14:21 +0530 Subject: [PATCH 0831/2884] target/ppc: Merge various fpu helpers This patch merges the definitions of the following set of fpu helper methods, which are similar, using macros : 1. f{add, sub, mul, div}(s) 2. fre(s) 3. frsqrte(s) Reviewed-by: Nicholas Piggin Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/fpu_helper.c | 221 +++++++++++----------------------------- 1 file changed, 62 insertions(+), 159 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 4b3dcad5d1..8d0cbe27e7 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -490,54 +490,12 @@ static void float_invalid_op_addsub(CPUPPCState *env, int flags, } } -/* fadd - fadd. */ -float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void addsub_flags_handler(CPUPPCState *env, int flags, + uintptr_t ra) { - float64 ret = float64_add(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); + float_invalid_op_addsub(env, flags, 1, ra); } - - return ret; -} - -/* fadds - fadds. */ -float64 helper_fadds(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_add(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - return ret; -} - -/* fsub - fsub. */ -float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64_sub(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - - return ret; -} - -/* fsubs - fsubs. */ -float64 helper_fsubs(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_sub(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - return ret; } static void float_invalid_op_mul(CPUPPCState *env, int flags, @@ -550,29 +508,11 @@ static void float_invalid_op_mul(CPUPPCState *env, int flags, } } -/* fmul - fmul. */ -float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void mul_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - float64 ret = float64_mul(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_mul(env, flags, 1, GETPC()); + float_invalid_op_mul(env, flags, 1, ra); } - - return ret; -} - -/* fmuls - fmuls. */ -float64 helper_fmuls(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_mul(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_mul(env, flags, 1, GETPC()); - } - return ret; } static void float_invalid_op_div(CPUPPCState *env, int flags, @@ -587,36 +527,14 @@ static void float_invalid_op_div(CPUPPCState *env, int flags, } } -/* fdiv - fdiv. */ -float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void div_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - float64 ret = float64_div(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_div(env, flags, 1, GETPC()); + float_invalid_op_div(env, flags, 1, ra); } if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); + float_zero_divide_excp(env, ra); } - - return ret; -} - -/* fdivs - fdivs. */ -float64 helper_fdivs(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_div(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_div(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - } - - return ret; } static uint64_t float_invalid_cvt(CPUPPCState *env, int flags, @@ -812,81 +730,66 @@ float64 helper_##name(CPUPPCState *env, float64 arg) \ FPU_FSQRT(FSQRT, float64_sqrt) FPU_FSQRT(FSQRTS, float64r32_sqrt) -/* fre - fre. */ -float64 helper_fre(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 ret = float64_div(float64_one, arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid_snan)) { - float_invalid_op_vxsnan(env, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - /* For FPSCR.ZE == 0, the result is 1/2. */ - ret = float64_set_sign(float64_half, float64_is_neg(arg)); - } - - return ret; +#define FPU_FRE(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + /* "Estimate" the reciprocal with actual division. */ \ + float64 ret = op(float64_one, arg, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + \ + if (unlikely(flags & float_flag_invalid_snan)) { \ + float_invalid_op_vxsnan(env, GETPC()); \ + } \ + if (unlikely(flags & float_flag_divbyzero)) { \ + float_zero_divide_excp(env, GETPC()); \ + /* For FPSCR.ZE == 0, the result is 1/2. */ \ + ret = float64_set_sign(float64_half, float64_is_neg(arg)); \ + } \ + \ + return ret; \ } -/* fres - fres. */ -uint64_t helper_fres(CPUPPCState *env, uint64_t arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 ret = float64r32_div(float64_one, arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid_snan)) { - float_invalid_op_vxsnan(env, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - /* For FPSCR.ZE == 0, the result is 1/2. */ - ret = float64_set_sign(float64_half, float64_is_neg(arg)); - } - - return ret; +#define FPU_FRSQRTE(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + /* "Estimate" the reciprocal with actual division. */ \ + float64 rets = float64_sqrt(arg, &env->fp_status); \ + float64 retd = op(float64_one, rets, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + \ + if (unlikely(flags & float_flag_invalid)) { \ + float_invalid_op_sqrt(env, flags, 1, GETPC()); \ + } \ + if (unlikely(flags & float_flag_divbyzero)) { \ + /* Reciprocal of (square root of) zero. */ \ + float_zero_divide_excp(env, GETPC()); \ + } \ + \ + return retd; \ } -/* frsqrte - frsqrte. */ -float64 helper_frsqrte(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 rets = float64_sqrt(arg, &env->fp_status); - float64 retd = float64_div(float64_one, rets, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_sqrt(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - /* Reciprocal of (square root of) zero. */ - float_zero_divide_excp(env, GETPC()); - } - - return retd; +#define FPU_HELPER(name, op, flags_handler) \ +float64 helper_##name(CPUPPCState *env, float64 arg1, float64 arg2) \ +{ \ + float64 ret = op(arg1, arg2, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + uintptr_t ra = GETPC(); \ + flags_handler(env, flags, ra); \ + return ret; \ } -/* frsqrtes - frsqrtes. */ -float64 helper_frsqrtes(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 rets = float64_sqrt(arg, &env->fp_status); - float64 retd = float64r32_div(float64_one, rets, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_sqrt(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - /* Reciprocal of (square root of) zero. */ - float_zero_divide_excp(env, GETPC()); - } - - return retd; -} +FPU_FRE(fre, float64_div) +FPU_FRE(fres, float64r32_div) +FPU_FRSQRTE(frsqrte, float64_div) +FPU_FRSQRTE(frsqrtes, float64r32_div) +FPU_HELPER(fadd, float64_add, addsub_flags_handler) +FPU_HELPER(fadds, float64r32_add, addsub_flags_handler) +FPU_HELPER(fsub, float64_sub, addsub_flags_handler) +FPU_HELPER(fsubs, float64r32_sub, addsub_flags_handler) +FPU_HELPER(fmul, float64_mul, mul_flags_handler) +FPU_HELPER(fmuls, float64r32_mul, mul_flags_handler) +FPU_HELPER(fdiv, float64_div, div_flags_handler) +FPU_HELPER(fdivs, float64r32_div, div_flags_handler) /* fsel - fsel. */ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) From 177fcc06dc579749ec4515174b62ba4cdb775474 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Fri, 15 Mar 2024 12:14:22 +0530 Subject: [PATCH 0832/2884] target/ppc: Move floating-point arithmetic instructions to decodetree. This patch moves the below instructions to decodetree specification : f{add, sub, mul, div, re, rsqrte, madd, msub, nmadd, nmsub}[s][.] : A-form ft{div, sqrt} : X-form With this patch, all the floating-point arithmetic instructions have been moved to decodetree. The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Nicholas Piggin Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/fpu_helper.c | 38 ++-- target/ppc/helper.h | 44 ++--- target/ppc/insn32.decode | 42 +++++ target/ppc/translate/fp-impl.c.inc | 285 +++++++++++------------------ target/ppc/translate/fp-ops.c.inc | 31 ---- 5 files changed, 192 insertions(+), 248 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 8d0cbe27e7..51bce99fd5 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -673,7 +673,7 @@ static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b, uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \ uint64_t arg2, uint64_t arg3) \ { return do_fmadd(env, arg1, arg2, arg3, madd_flags, GETPC()); } \ - uint64_t helper_##op##s(CPUPPCState *env, uint64_t arg1, \ + uint64_t helper_##op##S(CPUPPCState *env, uint64_t arg1, \ uint64_t arg2, uint64_t arg3) \ { return do_fmadds(env, arg1, arg2, arg3, madd_flags, GETPC()); } @@ -682,10 +682,10 @@ static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b, #define NMADD_FLGS float_muladd_negate_result #define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result) -FPU_FMADD(fmadd, MADD_FLGS) -FPU_FMADD(fnmadd, NMADD_FLGS) -FPU_FMADD(fmsub, MSUB_FLGS) -FPU_FMADD(fnmsub, NMSUB_FLGS) +FPU_FMADD(FMADD, MADD_FLGS) +FPU_FMADD(FNMADD, NMADD_FLGS) +FPU_FMADD(FMSUB, MSUB_FLGS) +FPU_FMADD(FNMSUB, NMSUB_FLGS) /* frsp - frsp. */ static uint64_t do_frsp(CPUPPCState *env, uint64_t arg, uintptr_t retaddr) @@ -778,18 +778,18 @@ float64 helper_##name(CPUPPCState *env, float64 arg1, float64 arg2) \ return ret; \ } -FPU_FRE(fre, float64_div) -FPU_FRE(fres, float64r32_div) -FPU_FRSQRTE(frsqrte, float64_div) -FPU_FRSQRTE(frsqrtes, float64r32_div) -FPU_HELPER(fadd, float64_add, addsub_flags_handler) -FPU_HELPER(fadds, float64r32_add, addsub_flags_handler) -FPU_HELPER(fsub, float64_sub, addsub_flags_handler) -FPU_HELPER(fsubs, float64r32_sub, addsub_flags_handler) -FPU_HELPER(fmul, float64_mul, mul_flags_handler) -FPU_HELPER(fmuls, float64r32_mul, mul_flags_handler) -FPU_HELPER(fdiv, float64_div, div_flags_handler) -FPU_HELPER(fdivs, float64r32_div, div_flags_handler) +FPU_FRE(FRE, float64_div) +FPU_FRE(FRES, float64r32_div) +FPU_FRSQRTE(FRSQRTE, float64_div) +FPU_FRSQRTE(FRSQRTES, float64r32_div) +FPU_HELPER(FADD, float64_add, addsub_flags_handler) +FPU_HELPER(FADDS, float64r32_add, addsub_flags_handler) +FPU_HELPER(FSUB, float64_sub, addsub_flags_handler) +FPU_HELPER(FSUBS, float64r32_sub, addsub_flags_handler) +FPU_HELPER(FMUL, float64_mul, mul_flags_handler) +FPU_HELPER(FMULS, float64r32_mul, mul_flags_handler) +FPU_HELPER(FDIV, float64_div, div_flags_handler) +FPU_HELPER(FDIVS, float64r32_div, div_flags_handler) /* fsel - fsel. */ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) @@ -806,7 +806,7 @@ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) } } -uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) +uint32_t helper_FTDIV(uint64_t fra, uint64_t frb) { int fe_flag = 0; int fg_flag = 0; @@ -842,7 +842,7 @@ uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); } -uint32_t helper_ftsqrt(uint64_t frb) +uint32_t helper_FTSQRT(uint64_t frb) { int fe_flag = 0; int fg_flag = 0; diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 86f97ee1e7..f177d5b906 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -110,32 +110,32 @@ DEF_HELPER_2(friz, i64, env, i64) DEF_HELPER_2(frip, i64, env, i64) DEF_HELPER_2(frim, i64, env, i64) -DEF_HELPER_3(fadd, f64, env, f64, f64) -DEF_HELPER_3(fadds, f64, env, f64, f64) -DEF_HELPER_3(fsub, f64, env, f64, f64) -DEF_HELPER_3(fsubs, f64, env, f64, f64) -DEF_HELPER_3(fmul, f64, env, f64, f64) -DEF_HELPER_3(fmuls, f64, env, f64, f64) -DEF_HELPER_3(fdiv, f64, env, f64, f64) -DEF_HELPER_3(fdivs, f64, env, f64, f64) -DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) -DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) -DEF_HELPER_4(fmadds, i64, env, i64, i64, i64) -DEF_HELPER_4(fmsubs, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmadds, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmsubs, i64, env, i64, i64, i64) +DEF_HELPER_3(FADD, f64, env, f64, f64) +DEF_HELPER_3(FADDS, f64, env, f64, f64) +DEF_HELPER_3(FSUB, f64, env, f64, f64) +DEF_HELPER_3(FSUBS, f64, env, f64, f64) +DEF_HELPER_3(FMUL, f64, env, f64, f64) +DEF_HELPER_3(FMULS, f64, env, f64, f64) +DEF_HELPER_3(FDIV, f64, env, f64, f64) +DEF_HELPER_3(FDIVS, f64, env, f64, f64) +DEF_HELPER_4(FMADD, i64, env, i64, i64, i64) +DEF_HELPER_4(FMSUB, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMADD, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMSUB, i64, env, i64, i64, i64) +DEF_HELPER_4(FMADDS, i64, env, i64, i64, i64) +DEF_HELPER_4(FMSUBS, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMADDS, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMSUBS, i64, env, i64, i64, i64) DEF_HELPER_2(FSQRT, f64, env, f64) DEF_HELPER_2(FSQRTS, f64, env, f64) -DEF_HELPER_2(fre, i64, env, i64) -DEF_HELPER_2(fres, i64, env, i64) -DEF_HELPER_2(frsqrte, i64, env, i64) -DEF_HELPER_2(frsqrtes, i64, env, i64) +DEF_HELPER_2(FRE, i64, env, i64) +DEF_HELPER_2(FRES, i64, env, i64) +DEF_HELPER_2(FRSQRTE, i64, env, i64) +DEF_HELPER_2(FRSQRTES, i64, env, i64) DEF_HELPER_FLAGS_3(FSEL, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) -DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64) -DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) +DEF_HELPER_FLAGS_2(FTDIV, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_1(FTSQRT, TCG_CALL_NO_RWG_SE, i32, i64) #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index a180380750..e9d6595168 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -20,6 +20,12 @@ &A frt fra frb frc rc:bool @A ...... frt:5 fra:5 frb:5 frc:5 ..... rc:1 &A +&A_tab frt fra frb rc:bool +@A_tab ...... frt:5 fra:5 frb:5 ..... ..... rc:1 &A_tab + +&A_tac frt fra frc rc:bool +@A_tac ...... frt:5 fra:5 ..... frc:5 ..... rc:1 &A_tac + &A_tb frt frb rc:bool @A_tb ...... frt:5 ..... frb:5 ..... ..... rc:1 &A_tb @@ -124,6 +130,9 @@ &X_bf bf ra rb @X_bf ...... bf:3 .. ra:5 rb:5 .......... . &X_bf +&X_bf_b bf rb +@X_bf_b ...... bf:3 .. ..... rb:5 .......... . &X_bf_b + @X_bf_ap_bp ...... bf:3 .. ....0 ....0 .......... . &X_bf ra=%x_frap rb=%x_frbp @X_bf_a_bp ...... bf:3 .. ra:5 ....0 .......... . &X_bf rb=%x_frbp @@ -400,9 +409,42 @@ STFDUX 011111 ..... ...... .... 1011110111 - @X ### Floating-Point Arithmetic Instructions +FADD 111111 ..... ..... ..... ----- 10101 . @A_tab +FADDS 111011 ..... ..... ..... ----- 10101 . @A_tab + +FSUB 111111 ..... ..... ..... ----- 10100 . @A_tab +FSUBS 111011 ..... ..... ..... ----- 10100 . @A_tab + +FMUL 111111 ..... ..... ----- ..... 11001 . @A_tac +FMULS 111011 ..... ..... ----- ..... 11001 . @A_tac + +FDIV 111111 ..... ..... ..... ----- 10010 . @A_tab +FDIVS 111011 ..... ..... ..... ----- 10010 . @A_tab + FSQRT 111111 ..... ----- ..... ----- 10110 . @A_tb FSQRTS 111011 ..... ----- ..... ----- 10110 . @A_tb +FRE 111111 ..... ----- ..... ----- 11000 . @A_tb +FRES 111011 ..... ----- ..... ----- 11000 . @A_tb + +FRSQRTE 111111 ..... ----- ..... ----- 11010 . @A_tb +FRSQRTES 111011 ..... ----- ..... ----- 11010 . @A_tb + +FTDIV 111111 ... -- ..... ..... 0010000000 - @X_bf +FTSQRT 111111 ... -- ----- ..... 0010100000 - @X_bf_b + +FMADD 111111 ..... ..... ..... ..... 11101 . @A +FMADDS 111011 ..... ..... ..... ..... 11101 . @A + +FMSUB 111111 ..... ..... ..... ..... 11100 . @A +FMSUBS 111011 ..... ..... ..... ..... 11100 . @A + +FNMADD 111111 ..... ..... ..... ..... 11111 . @A +FNMADDS 111011 ..... ..... ..... ..... 11111 . @A + +FNMSUB 111111 ..... ..... ..... ..... 11110 . @A +FNMSUBS 111011 ..... ..... ..... ..... 11110 . @A + ### Floating-Point Select Instruction FSEL 111111 ..... ..... ..... ..... 10111 . @A diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 189cd8c979..a66b83398b 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -30,96 +30,73 @@ static void gen_set_cr1_from_fpscr(DisasContext *ctx) #endif /*** Floating-Point arithmetic ***/ -#define _GEN_FLOAT_ACB(name, op1, op2, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - TCGv_i64 t3; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - t3 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rC(ctx->opcode)); \ - get_fpr(t2, rB(ctx->opcode)); \ - gen_helper_f##name(t3, tcg_env, t0, t1, t2); \ - set_fpr(rD(ctx->opcode), t3); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t3); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_helper_acb(DisasContext *ctx, arg_A *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t0, t1, t2, t3; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + t3 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frc); + get_fpr(t2, a->frb); + helper(t3, tcg_env, t0, t1, t2); + set_fpr(a->frt, t3); + gen_compute_fprf_float64(t3); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ -_GEN_FLOAT_ACB(name, 0x3F, op2, set_fprf, type); \ -_GEN_FLOAT_ACB(name##s, 0x3B, op2, set_fprf, type); - -#define _GEN_FLOAT_AB(name, op1, op2, inval, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rB(ctx->opcode)); \ - gen_helper_f##name(t2, tcg_env, t0, t1); \ - set_fpr(rD(ctx->opcode), t2); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t2); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_helper_ab(DisasContext *ctx, arg_A_tab *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64)) +{ + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frb); + helper(t2, tcg_env, t0, t1); + set_fpr(a->frt, t2); + gen_compute_fprf_float64(t2); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AB(name, 0x3F, op2, inval, set_fprf, type); \ -_GEN_FLOAT_AB(name##s, 0x3B, op2, inval, set_fprf, type); -#define _GEN_FLOAT_AC(name, op1, op2, inval, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rC(ctx->opcode)); \ - gen_helper_f##name(t2, tcg_env, t0, t1); \ - set_fpr(rD(ctx->opcode), t2); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t2); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_helper_ac(DisasContext *ctx, arg_A_tac *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64)) +{ + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frc); + helper(t2, tcg_env, t0, t1); + set_fpr(a->frt, t2); + gen_compute_fprf_float64(t2); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AC(name, 0x3F, op2, inval, set_fprf, type); \ -_GEN_FLOAT_AC(name##s, 0x3B, op2, inval, set_fprf, type); #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ @@ -145,64 +122,22 @@ static void gen_f##name(DisasContext *ctx) \ } \ } -#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rB(ctx->opcode)); \ - gen_helper_f##name(t1, tcg_env, t0); \ - set_fpr(rD(ctx->opcode), t1); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t1); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ -} - -/* fadd - fadds */ -GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT); -/* fdiv - fdivs */ -GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT); -/* fmul - fmuls */ -GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT); - -/* fre */ -GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT); - -/* fres */ -GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES); - -/* frsqrte */ -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE); - -/* frsqrtes */ -static void gen_frsqrtes(DisasContext *ctx) +static bool do_helper_bs(DisasContext *ctx, arg_A_tb *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64)) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_frsqrtes(t1, tcg_env, t0); - set_fpr(rD(ctx->opcode), t1); + get_fpr(t0, a->frb); + helper(t1, tcg_env, t0); + set_fpr(a->frt, t1); gen_compute_fprf_float64(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { + if (unlikely(a->rc)) { gen_set_cr1_from_fpscr(ctx); } + return true; } static bool trans_FSEL(DisasContext *ctx, arg_A *a) @@ -228,10 +163,6 @@ static bool trans_FSEL(DisasContext *ctx, arg_A *a) return true; } -/* fsub - fsubs */ -GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); -/* Optional: */ - static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64)) { @@ -254,19 +185,33 @@ static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, return true; } +TRANS(FADD, do_helper_ab, gen_helper_FADD); +TRANS(FADDS, do_helper_ab, gen_helper_FADDS); +TRANS(FSUB, do_helper_ab, gen_helper_FSUB); +TRANS(FSUBS, do_helper_ab, gen_helper_FSUBS); +TRANS(FDIV, do_helper_ab, gen_helper_FDIV); +TRANS(FDIVS, do_helper_ab, gen_helper_FDIVS); +TRANS(FMUL, do_helper_ac, gen_helper_FMUL); +TRANS(FMULS, do_helper_ac, gen_helper_FMULS); + +TRANS(FMADD, do_helper_acb, gen_helper_FMADD); +TRANS(FMADDS, do_helper_acb, gen_helper_FMADDS); +TRANS(FMSUB, do_helper_acb, gen_helper_FMSUB); +TRANS(FMSUBS, do_helper_acb, gen_helper_FMSUBS); + +TRANS(FNMADD, do_helper_acb, gen_helper_FNMADD); +TRANS(FNMADDS, do_helper_acb, gen_helper_FNMADDS); +TRANS(FNMSUB, do_helper_acb, gen_helper_FNMSUB); +TRANS(FNMSUBS, do_helper_acb, gen_helper_FNMSUBS); + +TRANS_FLAGS(FLOAT_EXT, FRE, do_helper_bs, gen_helper_FRE); +TRANS_FLAGS(FLOAT_FRES, FRES, do_helper_bs, gen_helper_FRES); +TRANS_FLAGS(FLOAT_FRSQRTE, FRSQRTE, do_helper_bs, gen_helper_FRSQRTE); +TRANS_FLAGS(FLOAT_FRSQRTES, FRSQRTES, do_helper_bs, gen_helper_FRSQRTES); + TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT); TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS); -/*** Floating-Point multiply-and-add ***/ -/* fmadd - fmadds */ -GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT); -/* fmsub - fmsubs */ -GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT); -/* fnmadd - fnmadds */ -GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT); -/* fnmsub - fnmsubs */ -GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); - /*** Floating-Point round & convert ***/ /* fctiw */ GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); @@ -304,35 +249,30 @@ GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); /* frim */ GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); -static void gen_ftdiv(DisasContext *ctx) +static bool trans_FTDIV(DisasContext *ctx, arg_X_bf *a) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - get_fpr(t0, rA(ctx->opcode)); - get_fpr(t1, rB(ctx->opcode)); - gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], t0, t1); + get_fpr(t0, a->ra); + get_fpr(t1, a->rb); + gen_helper_FTDIV(cpu_crf[a->bf], t0, t1); + return true; } -static void gen_ftsqrt(DisasContext *ctx) +static bool trans_FTSQRT(DisasContext *ctx, arg_X_bf_b *a) { TCGv_i64 t0; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], t0); + get_fpr(t0, a->rb); + gen_helper_FTSQRT(cpu_crf[a->bf], t0); + return true; } - - /*** Floating-Point compare ***/ /* fcmpo */ @@ -1111,14 +1051,7 @@ TRANS(STFDX, do_lsfp_X, false, true, false) TRANS(STFDUX, do_lsfp_X, true, true, false) TRANS(PSTFD, do_lsfp_PLS_D, false, true, false) -#undef _GEN_FLOAT_ACB -#undef GEN_FLOAT_ACB -#undef _GEN_FLOAT_AB -#undef GEN_FLOAT_AB -#undef _GEN_FLOAT_AC -#undef GEN_FLOAT_AC #undef GEN_FLOAT_B -#undef GEN_FLOAT_BS #undef GEN_LDF #undef GEN_LDUF diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index d4c6c4bed1..cef4b5dfcb 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -1,36 +1,6 @@ -#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type) -#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ -_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type), \ -_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type) -#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) -#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ -_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) -#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) -#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ -_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type) -#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type) -GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT), -GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT), -GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES), -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE), -GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT), -GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT), -GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT), -GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT), -GEN_HANDLER_E(ftdiv, 0x3F, 0x00, 0x04, 1, PPC_NONE, PPC2_FP_TST_ISA206), -GEN_HANDLER_E(ftsqrt, 0x3F, 0x00, 0x05, 1, PPC_NONE, PPC2_FP_TST_ISA206), GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), @@ -61,7 +31,6 @@ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES), GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT), GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT), GEN_HANDLER(fabs, 0x3F, 0x08, 0x08, 0x001F0000, PPC_FLOAT), From a1faff873ab1b808126a110aa6b3bc6050baa0f1 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:27 +0530 Subject: [PATCH 0833/2884] target/ppc: Move mul{li, lw, lwo, hw, hwu} instructions to decodetree. Moving the following instructions to decodetree specification : mulli : D-form mul{lw, lwo, hw, hwu}[.] : XO-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Also cleaned up code for mullw[o][.] as per review comments while keeping the logic of the tcg ops generated semantically same. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 9 +++ target/ppc/translate.c | 89 ---------------------- target/ppc/translate/fixedpoint-impl.c.inc | 66 ++++++++++++++++ 3 files changed, 75 insertions(+), 89 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e9d6595168..bb0f1dbd75 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -202,6 +202,9 @@ &XO_ta rt ra oe:bool rc:bool @XO_ta ...... rt:5 ra:5 ..... oe:1 ......... rc:1 &XO_ta +&XO_tab_rc rt ra rb rc:bool +@XO_tab_rc ...... rt:5 ra:5 rb:5 . ......... rc:1 &XO_tab_rc + %xx_xt 0:1 21:5 %xx_xb 1:1 11:5 %xx_xa 2:1 16:5 @@ -362,6 +365,12 @@ SUBFE 011111 ..... ..... ..... . 010001000 . @XO SUBFME 011111 ..... ..... ----- . 011101000 . @XO_ta SUBFZE 011111 ..... ..... ----- . 011001000 . @XO_ta +MULLI 000111 ..... ..... ................ @D +MULLW 011111 ..... ..... ..... 0 011101011 . @XO_tab_rc +MULLWO 011111 ..... ..... ..... 1 011101011 . @XO_tab_rc +MULHW 011111 ..... ..... ..... - 001001011 . @XO_tab_rc +MULHWU 011111 ..... ..... ..... - 000001011 . @XO_tab_rc + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a70c5ed951..cb10e33ceb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1947,90 +1947,6 @@ GEN_INT_ARITH_MODD(modud, 0x08, 0); GEN_INT_ARITH_MODD(modsd, 0x18, 1); #endif -/* mulhw mulhw. */ -static void gen_mulhw(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_muls2_i32(t0, t1, t0, t1); - tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulhwu mulhwu. */ -static void gen_mulhwu(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mulu2_i32(t0, t1, t0, t1); - tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mullw mullw. */ -static void gen_mullw(DisasContext *ctx) -{ -#if defined(TARGET_PPC64) - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); -#else - tcg_gen_mul_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -#endif - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mullwo mullwo. */ -static void gen_mullwo(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_muls2_i32(t0, t1, t0, t1); -#if defined(TARGET_PPC64) - tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); -#else - tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], t0); -#endif - - tcg_gen_sari_i32(t0, t0, 31); - tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t1); - tcg_gen_extu_i32_tl(cpu_ov, t0); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ov32, cpu_ov); - } - tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulli */ -static void gen_mulli(DisasContext *ctx) -{ - tcg_gen_muli_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - SIMM(ctx->opcode)); -} - #if defined(TARGET_PPC64) /* mulhd mulhd. */ static void gen_mulhd(DisasContext *ctx) @@ -6343,11 +6259,6 @@ GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), -GEN_HANDLER(mulhw, 0x1F, 0x0B, 0x02, 0x00000400, PPC_INTEGER), -GEN_HANDLER(mulhwu, 0x1F, 0x0B, 0x00, 0x00000400, PPC_INTEGER), -GEN_HANDLER(mullw, 0x1F, 0x0B, 0x07, 0x00000000, PPC_INTEGER), -GEN_HANDLER(mullwo, 0x1F, 0x0B, 0x17, 0x00000000, PPC_INTEGER), -GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), #if defined(TARGET_PPC64) GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), #endif diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 0c66465d96..1a2ad58929 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -395,6 +395,72 @@ TRANS(SUBFE, do_subf_XO, true, true) TRANS(SUBFME, do_subf_const_XO, tcg_constant_tl(-1LL), true, true) TRANS(SUBFZE, do_subf_const_XO, tcg_constant_tl(0), true, true) +static bool trans_MULLI(DisasContext *ctx, arg_MULLI *a) +{ + tcg_gen_muli_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], a->si); + return true; +} + +static bool trans_MULLW(DisasContext *ctx, arg_MULLW *a) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_ext32s_tl(t0, cpu_gpr[a->ra]); + tcg_gen_ext32s_tl(t1, cpu_gpr[a->rb]); + tcg_gen_mul_tl(cpu_gpr[a->rt], t0, t1); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +static bool trans_MULLWO(DisasContext *ctx, arg_MULLWO *a) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + +#if defined(TARGET_PPC64) + tcg_gen_ext32s_i64(t0, cpu_gpr[a->ra]); + tcg_gen_ext32s_i64(t1, cpu_gpr[a->rb]); + tcg_gen_mul_i64(cpu_gpr[a->rt], t0, t1); + tcg_gen_sextract_i64(t0, cpu_gpr[a->rt], 31, 1); + tcg_gen_sari_i64(t1, cpu_gpr[a->rt], 32); +#else + tcg_gen_muls2_i32(cpu_gpr[a->rt], t1, cpu_gpr[a->ra], cpu_gpr[a->rb]); + tcg_gen_sari_i32(t0, cpu_gpr[a->rt], 31); +#endif + tcg_gen_setcond_tl(TCG_COND_NE, cpu_ov, t0, t1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ov32, cpu_ov); + } + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +static bool do_mulhw(DisasContext *ctx, arg_XO_tab_rc *a, + void (*helper)(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, + TCGv_i32 arg2)) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[a->ra]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[a->rb]); + helper(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[a->rt], t1); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS(MULHW, do_mulhw, tcg_gen_muls2_i32) +TRANS(MULHWU, do_mulhw, tcg_gen_mulu2_i32) + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From 86e6202a57b1ea44e6bd1fdb0faa0ce5aa3d4aab Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:28 +0530 Subject: [PATCH 0834/2884] target/ppc: Make divw[u] handler method decodetree compatible. The handler methods for divw[u] instructions internally use Rc(ctx->opcode), for extraction of Rc field of instructions, which poses a problem if we move the above said instructions to decodetree, as the ctx->opcode field is not popluated in decodetree. Hence, making it decodetree compatible, so that the mentioned insns can be safely move to decodetree specs. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/translate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cb10e33ceb..277d96aa94 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1737,8 +1737,9 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, } } -static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int sign, int compute_ov) +static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, + TCGv arg1, TCGv arg2, bool sign, + bool compute_ov, bool compute_rc0) { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -1772,7 +1773,7 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } - if (unlikely(Rc(ctx->opcode) != 0)) { + if (unlikely(compute_rc0)) { gen_set_Rc0(ctx, ret); } } @@ -1782,7 +1783,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ { \ gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov); \ + sign, compute_ov, Rc(ctx->opcode)); \ } /* divwu divwu. divwuo divwuo. */ GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0); From 2871921d857d3137e160dcb57ae0b48ddc98822f Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:29 +0530 Subject: [PATCH 0835/2884] target/ppc: Move divw[u, e, eu] instructions to decodetree. Moving the following instructions to decodetree specification : divw[u, e, eu][o][.] : XO-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 4 +-- target/ppc/insn32.decode | 5 ++++ target/ppc/int_helper.c | 4 +-- target/ppc/translate.c | 31 ---------------------- target/ppc/translate/fixedpoint-impl.c.inc | 24 +++++++++++++++++ 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index f177d5b906..6f5c0c20eb 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -55,8 +55,8 @@ DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) DEF_HELPER_4(divde, i64, env, i64, i64, i32) #endif -DEF_HELPER_4(divweu, tl, env, tl, tl, i32) -DEF_HELPER_4(divwe, tl, env, tl, tl, i32) +DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) +DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index bb0f1dbd75..c5291504dd 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -371,6 +371,11 @@ MULLWO 011111 ..... ..... ..... 1 011101011 . @XO_tab_rc MULHW 011111 ..... ..... ..... - 001001011 . @XO_tab_rc MULHWU 011111 ..... ..... ..... - 000001011 . @XO_tab_rc +DIVW 011111 ..... ..... ..... . 111101011 . @XO +DIVWU 011111 ..... ..... ..... . 111001011 . @XO +DIVWE 011111 ..... ..... ..... . 110101011 . @XO +DIVWEU 011111 ..... ..... ..... . 110001011 . @XO + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 0a5c3e78a4..dc1f72ff38 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -44,7 +44,7 @@ static inline void helper_update_ov_legacy(CPUPPCState *env, int ov) } } -target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, +target_ulong helper_DIVWEU(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) { uint64_t rt = 0; @@ -71,7 +71,7 @@ target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, return (target_ulong)rt; } -target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, +target_ulong helper_DIVWE(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) { int64_t rt = 0; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 277d96aa94..59248ae3fb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1777,21 +1777,6 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, gen_set_Rc0(ctx, ret); } } -/* Div functions */ -#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov, Rc(ctx->opcode)); \ -} -/* divwu divwu. divwuo divwuo. */ -GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0); -GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1); -/* divw divw. divwo divwo. */ -GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0); -GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); - /* div[wd]eu[o][.] */ #define GEN_DIVE(name, hlpr, compute_ov) \ static void gen_##name(DisasContext *ctx) \ @@ -1804,11 +1789,6 @@ static void gen_##name(DisasContext *ctx) \ } \ } -GEN_DIVE(divweu, divweu, 0); -GEN_DIVE(divweuo, divweu, 1); -GEN_DIVE(divwe, divwe, 0); -GEN_DIVE(divweo, divwe, 1); - #if defined(TARGET_PPC64) static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) @@ -6470,17 +6450,6 @@ GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), #endif -#undef GEN_INT_ARITH_DIVW -#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x0B, opc3, 0x00000000, PPC_INTEGER) -GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0), -GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1), -GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0), -GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1), -GEN_HANDLER_E(divwe, 0x1F, 0x0B, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweo, 0x1F, 0x0B, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweu, 0x1F, 0x0B, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweuo, 0x1F, 0x0B, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), GEN_HANDLER_E(modsw, 0x1F, 0x0B, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(moduw, 0x1F, 0x0B, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 1a2ad58929..3265c77aa6 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -461,6 +461,30 @@ static bool do_mulhw(DisasContext *ctx, arg_XO_tab_rc *a, TRANS(MULHW, do_mulhw, tcg_gen_muls2_i32) TRANS(MULHWU, do_mulhw, tcg_gen_mulu2_i32) +static bool do_divw(DisasContext *ctx, arg_XO *a, int sign) +{ + gen_op_arith_divw(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign, a->oe, a->rc); + return true; +} + +static bool do_divwe(DisasContext *ctx, arg_XO *a, + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv, TCGv_i32)) +{ + REQUIRE_INSNS_FLAGS2(ctx, DIVE_ISA206); + helper(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], + tcg_constant_i32(a->oe)); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS(DIVW, do_divw, 1); +TRANS(DIVWU, do_divw, 0); +TRANS(DIVWE, do_divwe, gen_helper_DIVWE); +TRANS(DIVWEU, do_divwe, gen_helper_DIVWEU); + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From a81b5c186730fe5a92b645c84e538444a64b93f5 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:30 +0530 Subject: [PATCH 0836/2884] target/ppc: Move neg, darn, mod{sw, uw} to decodetree. Moving the below instructions to decodetree specification : neg[o][.] : XO-form mod{sw, uw}, darn : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 4 +- target/ppc/insn32.decode | 8 ++++ target/ppc/int_helper.c | 4 +- target/ppc/translate.c | 56 ---------------------- target/ppc/translate/fixedpoint-impl.c.inc | 48 +++++++++++++++++++ 5 files changed, 60 insertions(+), 60 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6f5c0c20eb..5a97429f1e 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -71,8 +71,8 @@ DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) -DEF_HELPER_FLAGS_0(darn32, TCG_CALL_NO_RWG, tl) -DEF_HELPER_FLAGS_0(darn64, TCG_CALL_NO_RWG, tl) +DEF_HELPER_FLAGS_0(DARN32, TCG_CALL_NO_RWG, tl) +DEF_HELPER_FLAGS_0(DARN64, TCG_CALL_NO_RWG, tl) #endif DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_NO_RWG_SE, i32, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index c5291504dd..3051792a56 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -196,6 +196,9 @@ &X_a ra @X_a ...... ra:3 .. ..... ..... .......... . &X_a +&X_tl rt l +@X_tl ...... rt:5 ... l:2 ..... .......... . &X_tl + &XO rt ra rb oe:bool rc:bool @XO ...... rt:5 ra:5 rb:5 oe:1 ......... rc:1 &XO @@ -376,6 +379,11 @@ DIVWU 011111 ..... ..... ..... . 111001011 . @XO DIVWE 011111 ..... ..... ..... . 110101011 . @XO DIVWEU 011111 ..... ..... ..... . 110001011 . @XO +MODSW 011111 ..... ..... ..... 1100001011 - @X +MODUW 011111 ..... ..... ..... 0100001011 - @X +DARN 011111 ..... --- .. ----- 1011110011 - @X_tl +NEG 011111 ..... ..... ----- . 001101000 . @XO_ta + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index dc1f72ff38..bc25d5b062 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -171,7 +171,7 @@ uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) /* * Return a random number. */ -uint64_t helper_darn32(void) +uint64_t helper_DARN32(void) { Error *err = NULL; uint32_t ret; @@ -186,7 +186,7 @@ uint64_t helper_darn32(void) return ret; } -uint64_t helper_darn64(void) +uint64_t helper_DARN64(void) { Error *err = NULL; uint64_t ret; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 59248ae3fb..3756b0c495 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1877,17 +1877,6 @@ static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, } } -#define GEN_INT_ARITH_MODW(name, opc3, sign) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_modw(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign); \ -} - -GEN_INT_ARITH_MODW(moduw, 0x08, 0); -GEN_INT_ARITH_MODW(modsw, 0x18, 1); - #if defined(TARGET_PPC64) static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign) @@ -2054,27 +2043,6 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } } -/* neg neg. nego nego. */ -static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov) -{ - TCGv zero = tcg_constant_tl(0); - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - zero, 0, 0, compute_ov, Rc(ctx->opcode)); -} - -static void gen_neg(DisasContext *ctx) -{ - tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode))) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -static void gen_nego(DisasContext *ctx) -{ - gen_op_arith_neg(ctx, 1); -} - /*** Integer logical ***/ #define GEN_LOGICAL2(name, tcg_op, opc, type) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -2400,24 +2368,6 @@ static void gen_cnttzd(DisasContext *ctx) gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } } - -/* darn */ -static void gen_darn(DisasContext *ctx) -{ - int l = L(ctx->opcode); - - if (l > 2) { - tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); - } else { - translator_io_start(&ctx->base); - if (l == 0) { - gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); - } else { - /* Return 64-bit random for both CRN and RRN */ - gen_helper_darn64(cpu_gpr[rD(ctx->opcode)]); - } - } -} #endif /*** Integer rotate ***/ @@ -6243,8 +6193,6 @@ GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), #if defined(TARGET_PPC64) GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), #endif -GEN_HANDLER(neg, 0x1F, 0x08, 0x03, 0x0000F800, PPC_INTEGER), -GEN_HANDLER(nego, 0x1F, 0x08, 0x13, 0x0000F800, PPC_INTEGER), GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6265,7 +6213,6 @@ GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), GEN_HANDLER_E(cnttzd, 0x1F, 0x1A, 0x11, 0x00000000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(darn, 0x1F, 0x13, 0x17, 0x001CF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), #endif @@ -6450,9 +6397,6 @@ GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), #endif -GEN_HANDLER_E(modsw, 0x1F, 0x0B, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(moduw, 0x1F, 0x0B, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), - #if defined(TARGET_PPC64) #undef GEN_INT_ARITH_DIVD #define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 3265c77aa6..584cc4bfb2 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -485,6 +485,54 @@ TRANS(DIVWU, do_divw, 0); TRANS(DIVWE, do_divwe, gen_helper_DIVWE); TRANS(DIVWEU, do_divwe, gen_helper_DIVWEU); +static bool do_modw(DisasContext *ctx, arg_X *a, bool sign) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + gen_op_arith_modw(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign); + return true; +} + +TRANS(MODUW, do_modw, false); +TRANS(MODSW, do_modw, true); + +static bool trans_NEG(DisasContext *ctx, arg_NEG *a) +{ + if (a->oe) { + TCGv zero = tcg_constant_tl(0); + gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], zero, + false, false, true, a->rc); + } else { + tcg_gen_neg_tl(cpu_gpr[a->rt], cpu_gpr[a->ra]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + } + return true; +} + +static bool trans_DARN(DisasContext *ctx, arg_DARN *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + if (a->l > 2) { + tcg_gen_movi_i64(cpu_gpr[a->rt], -1); + } else { + translator_io_start(&ctx->base); + if (a->l == 0) { + gen_helper_DARN32(cpu_gpr[a->rt]); + } else { + /* Return 64-bit random for both CRN and RRN */ + gen_helper_DARN64(cpu_gpr[a->rt]); + } + } +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From 703e88f72325c46daa1a47c28469d814dd850d4c Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:31 +0530 Subject: [PATCH 0837/2884] target/ppc: Move multiply fixed-point insns (64-bit operands) to decodetree. Moving the following instructions to decodetree : mul{ld, ldo, hd, hdu}[.] : XO-form madd{hd, hdu, ld} : VA-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 9 ++ target/ppc/translate.c | 101 -------------------- target/ppc/translate/fixedpoint-impl.c.inc | 105 +++++++++++++++++++++ 3 files changed, 114 insertions(+), 101 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 3051792a56..46199488f7 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -384,6 +384,15 @@ MODUW 011111 ..... ..... ..... 0100001011 - @X DARN 011111 ..... --- .. ----- 1011110011 - @X_tl NEG 011111 ..... ..... ----- . 001101000 . @XO_ta +MULLD 011111 ..... ..... ..... 0 011101001 . @XO_tab_rc +MULLDO 011111 ..... ..... ..... 1 011101001 . @XO_tab_rc +MULHD 011111 ..... ..... ..... - 001001001 . @XO_tab_rc +MULHDU 011111 ..... ..... ..... - 000001001 . @XO_tab_rc + +MADDLD 000100 ..... ..... ..... ..... 110011 @VA +MADDHD 000100 ..... ..... ..... ..... 110000 @VA +MADDHDU 000100 ..... ..... ..... ..... 110001 @VA + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3756b0c495..c76c4c9054 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1917,62 +1917,6 @@ GEN_INT_ARITH_MODD(modud, 0x08, 0); GEN_INT_ARITH_MODD(modsd, 0x18, 1); #endif -#if defined(TARGET_PPC64) -/* mulhd mulhd. */ -static void gen_mulhd(DisasContext *ctx) -{ - TCGv lo = tcg_temp_new(); - tcg_gen_muls2_tl(lo, cpu_gpr[rD(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulhdu mulhdu. */ -static void gen_mulhdu(DisasContext *ctx) -{ - TCGv lo = tcg_temp_new(); - tcg_gen_mulu2_tl(lo, cpu_gpr[rD(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulld mulld. */ -static void gen_mulld(DisasContext *ctx) -{ - tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulldo mulldo. */ -static void gen_mulldo(DisasContext *ctx) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - tcg_gen_muls2_i64(t0, t1, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mov_i64(cpu_gpr[rD(ctx->opcode)], t0); - - tcg_gen_sari_i64(t0, t0, 63); - tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ov32, cpu_ov); - } - tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} -#endif - /* Common subf function */ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, bool add_ca, bool compute_ca, @@ -5795,36 +5739,6 @@ static void gen_icbt_440(DisasContext *ctx) */ } -#if defined(TARGET_PPC64) -static void gen_maddld(DisasContext *ctx) -{ - TCGv_i64 t1 = tcg_temp_new_i64(); - - tcg_gen_mul_i64(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - tcg_gen_add_i64(cpu_gpr[rD(ctx->opcode)], t1, cpu_gpr[rC(ctx->opcode)]); -} - -/* maddhd maddhdu */ -static void gen_maddhd_maddhdu(DisasContext *ctx) -{ - TCGv_i64 lo = tcg_temp_new_i64(); - TCGv_i64 hi = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - if (Rc(ctx->opcode)) { - tcg_gen_mulu2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_movi_i64(t1, 0); - } else { - tcg_gen_muls2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_sari_i64(t1, cpu_gpr[rC(ctx->opcode)], 63); - } - tcg_gen_add2_i64(t1, cpu_gpr[rD(ctx->opcode)], lo, hi, - cpu_gpr[rC(ctx->opcode)], t1); -} -#endif /* defined(TARGET_PPC64) */ - static void gen_tbegin(DisasContext *ctx) { if (unlikely(!ctx->tm_enabled)) { @@ -6190,9 +6104,6 @@ GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), -#if defined(TARGET_PPC64) -GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), -#endif GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6391,11 +6302,6 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, - PPC2_ISA300), -GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), -#endif #if defined(TARGET_PPC64) #undef GEN_INT_ARITH_DIVD @@ -6412,13 +6318,6 @@ GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), GEN_HANDLER_E(modsd, 0x1F, 0x09, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(modud, 0x1F, 0x09, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), - -#undef GEN_INT_ARITH_MUL_HELPER -#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ -GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) -GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00), -GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02), -GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17), #endif #undef GEN_LOGICAL1 diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 584cc4bfb2..077c938b30 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -533,6 +533,111 @@ static bool trans_DARN(DisasContext *ctx, arg_DARN *a) return true; } +static bool trans_MULLD(DisasContext *ctx, arg_MULLD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + tcg_gen_mul_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MULLDO(DisasContext *ctx, arg_MULLD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(t0, t1, cpu_gpr[a->ra], cpu_gpr[a->rb]); + tcg_gen_mov_i64(cpu_gpr[a->rt], t0); + + tcg_gen_sari_i64(t0, t0, 63); + tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ov32, cpu_ov); + } + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_mulhd(DisasContext *ctx, arg_XO_tab_rc *a, + void (*helper)(TCGv, TCGv, TCGv, TCGv)) +{ + TCGv lo = tcg_temp_new(); + helper(lo, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS64(MULHD, do_mulhd, tcg_gen_muls2_tl); +TRANS64(MULHDU, do_mulhd, tcg_gen_mulu2_tl); + +static bool trans_MADDLD(DisasContext *ctx, arg_MADDLD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t1, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_add_i64(cpu_gpr[a->vrt], t1, cpu_gpr[a->rc]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MADDHD(DisasContext *ctx, arg_MADDHD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(lo, hi, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_sari_i64(t1, cpu_gpr[a->rc], 63); + tcg_gen_add2_i64(t1, cpu_gpr[a->vrt], lo, hi, cpu_gpr[a->rc], t1); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MADDHDU(DisasContext *ctx, arg_MADDHDU *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mulu2_i64(lo, hi, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_add2_i64(t1, cpu_gpr[a->vrt], lo, hi, cpu_gpr[a->rc], + tcg_constant_i64(0)); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From f424bc10ebf2a935a2c20400996d665434ec9e17 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:32 +0530 Subject: [PATCH 0838/2884] target/ppc: Move div/mod fixed-point insns (64 bits operands) to decodetree. Moving the below instructions to decodetree specification : divd[u, e, eu][o][.] : XO-form mod{sd, ud} : X-form With this patch, all the fixed-point arithmetic instructions have been moved to decodetree. The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Also, remaned do_divwe method in fixedpoint-impl.c.inc to do_dive because it is now used to divide doubleword operands as well, and not just words. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 4 +- target/ppc/insn32.decode | 8 +++ target/ppc/int_helper.c | 4 +- target/ppc/translate.c | 63 ++-------------------- target/ppc/translate/fixedpoint-impl.c.inc | 58 ++++++++++++++++++-- 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5a97429f1e..b5a76f1365 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -52,8 +52,8 @@ DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) -DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) -DEF_HELPER_4(divde, i64, env, i64, i64, i32) +DEF_HELPER_4(DIVDEU, i64, env, i64, i64, i32) +DEF_HELPER_4(DIVDE, i64, env, i64, i64, i32) #endif DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 46199488f7..c7cb6e7f37 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -393,6 +393,14 @@ MADDLD 000100 ..... ..... ..... ..... 110011 @VA MADDHD 000100 ..... ..... ..... ..... 110000 @VA MADDHDU 000100 ..... ..... ..... ..... 110001 @VA +DIVD 011111 ..... ..... ..... . 111101001 . @XO +DIVDU 011111 ..... ..... ..... . 111001001 . @XO +DIVDE 011111 ..... ..... ..... . 110101001 . @XO +DIVDEU 011111 ..... ..... ..... . 110001001 . @XO + +MODSD 011111 ..... ..... ..... 1100001001 - @X +MODUD 011111 ..... ..... ..... 0100001001 - @X + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index bc25d5b062..585c2b65d3 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -101,7 +101,7 @@ target_ulong helper_DIVWE(CPUPPCState *env, target_ulong ra, target_ulong rb, #if defined(TARGET_PPC64) -uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) +uint64_t helper_DIVDEU(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) { uint64_t rt = 0; int overflow = 0; @@ -120,7 +120,7 @@ uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) return rt; } -uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) +uint64_t helper_DIVDE(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) { uint64_t rt = 0; int64_t ra = (int64_t)rau; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c76c4c9054..ba7c1fdf43 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1777,21 +1777,11 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, gen_set_Rc0(ctx, ret); } } -/* div[wd]eu[o][.] */ -#define GEN_DIVE(name, hlpr, compute_ov) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_i32 t0 = tcg_constant_i32(compute_ov); \ - gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], tcg_env, \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ - } \ -} #if defined(TARGET_PPC64) -static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int sign, int compute_ov) +static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, + TCGv arg1, TCGv arg2, bool sign, + bool compute_ov, bool compute_rc0) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -1827,25 +1817,6 @@ static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, gen_set_Rc0(ctx, ret); } } - -#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_divd(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov); \ -} -/* divdu divdu. divduo divduo. */ -GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0); -GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1); -/* divd divd. divdo divdo. */ -GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0); -GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); - -GEN_DIVE(divdeu, divdeu, 0); -GEN_DIVE(divdeuo, divdeu, 1); -GEN_DIVE(divde, divde, 0); -GEN_DIVE(divdeo, divde, 1); #endif static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, @@ -1904,17 +1875,6 @@ static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_remu_i64(ret, t0, t1); } } - -#define GEN_INT_ARITH_MODD(name, opc3, sign) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_modd(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign); \ -} - -GEN_INT_ARITH_MODD(modud, 0x08, 0); -GEN_INT_ARITH_MODD(modsd, 0x18, 1); #endif /* Common subf function */ @@ -6303,23 +6263,6 @@ GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -#if defined(TARGET_PPC64) -#undef GEN_INT_ARITH_DIVD -#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) -GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0), -GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1), -GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0), -GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1), - -GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(modsd, 0x1F, 0x09, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(modud, 0x1F, 0x09, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), -#endif - #undef GEN_LOGICAL1 #undef GEN_LOGICAL2 #define GEN_LOGICAL2(name, tcg_op, opc, type) \ diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 077c938b30..2ada7473ea 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -468,7 +468,7 @@ static bool do_divw(DisasContext *ctx, arg_XO *a, int sign) return true; } -static bool do_divwe(DisasContext *ctx, arg_XO *a, +static bool do_dive(DisasContext *ctx, arg_XO *a, void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv, TCGv_i32)) { REQUIRE_INSNS_FLAGS2(ctx, DIVE_ISA206); @@ -482,8 +482,8 @@ static bool do_divwe(DisasContext *ctx, arg_XO *a, TRANS(DIVW, do_divw, 1); TRANS(DIVWU, do_divw, 0); -TRANS(DIVWE, do_divwe, gen_helper_DIVWE); -TRANS(DIVWEU, do_divwe, gen_helper_DIVWEU); +TRANS(DIVWE, do_dive, gen_helper_DIVWE); +TRANS(DIVWEU, do_dive, gen_helper_DIVWEU); static bool do_modw(DisasContext *ctx, arg_X *a, bool sign) { @@ -638,6 +638,58 @@ static bool trans_MADDHDU(DisasContext *ctx, arg_MADDHDU *a) return true; } +static bool do_divd(DisasContext *ctx, arg_XO *a, bool sign) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + gen_op_arith_divd(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign, a->oe, a->rc); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_modd(DisasContext *ctx, arg_X *a, bool sign) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + gen_op_arith_modd(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign); +#else + qemu_build_not_reached(); +#endif + return true; +} + +TRANS64(DIVD, do_divd, true); +TRANS64(DIVDU, do_divd, false); + +static bool trans_DIVDE(DisasContext *ctx, arg_DIVDE *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + return do_dive(ctx, a, gen_helper_DIVDE); +#else + qemu_build_not_reached(); +#endif +} + +static bool trans_DIVDEU(DisasContext *ctx, arg_DIVDEU *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + return do_dive(ctx, a, gen_helper_DIVDEU); +#else + qemu_build_not_reached(); +#endif + return true; +} + +TRANS64(MODSD, do_modd, true); +TRANS64(MODUD, do_modd, false); + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From ae556c6a49d47208e4cbc70efab3dfd5bb2ac309 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:33 +0530 Subject: [PATCH 0839/2884] target/ppc: Move cmp{rb, eqb}, tw[i], td[i], isel instructions to decodetree. Moving the following instructions to decodetree specification : cmp{rb, eqb}, t{w, d} : X-form t{w, d}i : D-form isel : A-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Also for CMPRB, following review comments : Replaced repetition of arithmetic right shifting (tcg_gen_shri_i32) followed by extraction of last 8 bits (tcg_gen_ext8u_i32) with extraction of the required bits using offsets (tcg_gen_extract_i32). Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 4 +- target/ppc/helper.h | 6 +- target/ppc/insn32.decode | 16 +++ target/ppc/int_helper.c | 2 +- target/ppc/translate.c | 133 +-------------------- target/ppc/translate/fixedpoint-impl.c.inc | 132 ++++++++++++++++++++ 6 files changed, 157 insertions(+), 136 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 9df17f93bf..2e3f36a3ef 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2742,7 +2742,7 @@ void helper_rfmci(CPUPPCState *env) } #endif /* !CONFIG_USER_ONLY */ -void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, +void helper_TW(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || @@ -2756,7 +2756,7 @@ void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, } #ifdef TARGET_PPC64 -void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, +void helper_TD(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || diff --git a/target/ppc/helper.h b/target/ppc/helper.h index b5a76f1365..b8af2cf878 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -1,8 +1,8 @@ DEF_HELPER_FLAGS_3(raise_exception_err, TCG_CALL_NO_WG, noreturn, env, i32, i32) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_FLAGS_4(tw, TCG_CALL_NO_WG, void, env, tl, tl, i32) +DEF_HELPER_FLAGS_4(TW, TCG_CALL_NO_WG, void, env, tl, tl, i32) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_4(td, TCG_CALL_NO_WG, void, env, tl, tl, i32) +DEF_HELPER_FLAGS_4(TD, TCG_CALL_NO_WG, void, env, tl, tl, i32) #endif DEF_HELPER_4(HASHST, void, env, tl, tl, tl) DEF_HELPER_4(HASHCHK, void, env, tl, tl, tl) @@ -67,7 +67,7 @@ DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_1(CDTBCD, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(CBCDTD, TCG_CALL_NO_RWG_SE, tl, tl) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) +DEF_HELPER_FLAGS_2(CMPEQB, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index c7cb6e7f37..cb1e4bd307 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -29,6 +29,9 @@ &A_tb frt frb rc:bool @A_tb ...... frt:5 ..... frb:5 ..... ..... rc:1 &A_tb +&A_tab_bc rt ra rb bc +@A_tab_bc ...... rt:5 ra:5 rb:5 bc:5 ..... . &A_tab_bc + &D rt ra si:int64_t @D ...... rt:5 ra:5 si:s16 &D @@ -340,6 +343,19 @@ CMP 011111 ... - . ..... ..... 0000000000 - @X_bfl CMPL 011111 ... - . ..... ..... 0000100000 - @X_bfl CMPI 001011 ... - . ..... ................ @D_bfs CMPLI 001010 ... - . ..... ................ @D_bfu +CMPRB 011111 ... - . ..... ..... 0011000000 - @X_bfl +CMPEQB 011111 ... -- ..... ..... 0011100000 - @X_bf + +### Fixed-Point Trap Instructions + +TW 011111 ..... ..... ..... 0000000100 - @X +TD 011111 ..... ..... ..... 0001000100 - @X +TWI 000011 ..... ..... ................ @D +TDI 000010 ..... ..... ................ @D + +### Fixed-Point Select Instruction + +ISEL 011111 ..... ..... ..... ..... 01111 - @A_tab_bc ### Fixed-Point Arithmetic Instructions diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 585c2b65d3..d12dcc28e1 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -159,7 +159,7 @@ uint64_t helper_DIVDE(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) /* When you XOR the pattern and there is a match, that byte will be zero */ #define hasvalue(x, n) (haszero((x) ^ pattern(n))) -uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) +uint32_t helper_CMPEQB(target_ulong ra, target_ulong rb) { return hasvalue(rb, ra) ? CRF_GT : 0; } diff --git a/target/ppc/translate.c b/target/ppc/translate.c index ba7c1fdf43..e1ccb82f10 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1563,66 +1563,6 @@ static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) } } -/* cmprb - range comparison: isupper, isaplha, islower*/ -static void gen_cmprb(DisasContext *ctx) -{ - TCGv_i32 src1 = tcg_temp_new_i32(); - TCGv_i32 src2 = tcg_temp_new_i32(); - TCGv_i32 src2lo = tcg_temp_new_i32(); - TCGv_i32 src2hi = tcg_temp_new_i32(); - TCGv_i32 crf = cpu_crf[crfD(ctx->opcode)]; - - tcg_gen_trunc_tl_i32(src1, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(src2, cpu_gpr[rB(ctx->opcode)]); - - tcg_gen_andi_i32(src1, src1, 0xFF); - tcg_gen_ext8u_i32(src2lo, src2); - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2hi, src2); - - tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); - tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); - tcg_gen_and_i32(crf, src2lo, src2hi); - - if (ctx->opcode & 0x00200000) { - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2lo, src2); - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2hi, src2); - tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); - tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); - tcg_gen_and_i32(src2lo, src2lo, src2hi); - tcg_gen_or_i32(crf, crf, src2lo); - } - tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); -} - -#if defined(TARGET_PPC64) -/* cmpeqb */ -static void gen_cmpeqb(DisasContext *ctx) -{ - gen_helper_cmpeqb(cpu_crf[crfD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -} -#endif - -/* isel (PowerPC 2.03 specification) */ -static void gen_isel(DisasContext *ctx) -{ - uint32_t bi = rC(ctx->opcode); - uint32_t mask = 0x08 >> (bi & 0x03); - TCGv t0 = tcg_temp_new(); - TCGv zr; - - tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); - tcg_gen_andi_tl(t0, t0, mask); - - zr = tcg_constant_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rD(ctx->opcode)], t0, zr, - rA(ctx->opcode) ? cpu_gpr[rA(ctx->opcode)] : zr, - cpu_gpr[rB(ctx->opcode)]); -} - /* cmpb: PowerPC 2.05 specification */ static void gen_cmpb(DisasContext *ctx) { @@ -4187,76 +4127,20 @@ static void gen_scv(DisasContext *ctx) /*** Trap ***/ /* Check for unconditional traps (always or never) */ -static bool check_unconditional_trap(DisasContext *ctx) +static bool check_unconditional_trap(DisasContext *ctx, int to) { /* Trap never */ - if (TO(ctx->opcode) == 0) { + if (to == 0) { return true; } /* Trap always */ - if (TO(ctx->opcode) == 31) { + if (to == 31) { gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP); return true; } return false; } -/* tw */ -static void gen_tw(DisasContext *ctx) -{ - TCGv_i32 t0; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_tw(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - t0); -} - -/* twi */ -static void gen_twi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_tl(SIMM(ctx->opcode)); - t1 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_tw(tcg_env, cpu_gpr[rA(ctx->opcode)], t0, t1); -} - -#if defined(TARGET_PPC64) -/* td */ -static void gen_td(DisasContext *ctx) -{ - TCGv_i32 t0; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_td(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - t0); -} - -/* tdi */ -static void gen_tdi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_tl(SIMM(ctx->opcode)); - t1 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_td(tcg_env, cpu_gpr[rA(ctx->opcode)], t0, t1); -} -#endif - /*** Processor control ***/ /* mcrxr */ @@ -6058,12 +5942,7 @@ GEN_HANDLER_E(brw, 0x1F, 0x1B, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA310), GEN_HANDLER_E(brh, 0x1F, 0x1B, 0x06, 0x0000F801, PPC_NONE, PPC2_ISA310), #endif GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), -#endif GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6160,12 +6039,6 @@ GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H), /* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */ GEN_HANDLER(sc, 0x11, 0x11, 0xFF, 0x03FFF01D, PPC_FLOW), GEN_HANDLER(sc, 0x11, 0x01, 0xFF, 0x03FFF01D, PPC_FLOW), -GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW), -GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW), -#if defined(TARGET_PPC64) -GEN_HANDLER(td, 0x1F, 0x04, 0x02, 0x00000001, PPC_64B), -GEN_HANDLER(tdi, 0x02, 0xFF, 0xFF, 0x00000000, PPC_64B), -#endif GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC), GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x00000801, PPC_MISC), GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC), diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 2ada7473ea..872fed664d 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -289,6 +289,50 @@ TRANS(CMPL, do_cmp_X, false); TRANS(CMPI, do_cmp_D, true); TRANS(CMPLI, do_cmp_D, false); +static bool trans_CMPRB(DisasContext *ctx, arg_CMPRB *a) +{ + TCGv_i32 src1 = tcg_temp_new_i32(); + TCGv_i32 src2 = tcg_temp_new_i32(); + TCGv_i32 src2lo = tcg_temp_new_i32(); + TCGv_i32 src2hi = tcg_temp_new_i32(); + TCGv_i32 crf = cpu_crf[a->bf]; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + tcg_gen_trunc_tl_i32(src1, cpu_gpr[a->ra]); + tcg_gen_trunc_tl_i32(src2, cpu_gpr[a->rb]); + + tcg_gen_andi_i32(src1, src1, 0xFF); + tcg_gen_ext8u_i32(src2lo, src2); + tcg_gen_extract_i32(src2hi, src2, 8, 8); + + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(crf, src2lo, src2hi); + + if (a->l) { + tcg_gen_extract_i32(src2lo, src2, 16, 8); + tcg_gen_extract_i32(src2hi, src2, 24, 8); + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(src2lo, src2lo, src2hi); + tcg_gen_or_i32(crf, crf, src2lo); + } + tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); + return true; +} + +static bool trans_CMPEQB(DisasContext *ctx, arg_CMPEQB *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + gen_helper_CMPEQB(cpu_crf[a->bf], cpu_gpr[a->ra], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + /* * Fixed-Point Arithmetic Instructions */ @@ -690,6 +734,94 @@ static bool trans_DIVDEU(DisasContext *ctx, arg_DIVDEU *a) TRANS64(MODSD, do_modd, true); TRANS64(MODUD, do_modd, false); +/* + * Fixed-Point Select Instructions + */ + +static bool trans_ISEL(DisasContext *ctx, arg_ISEL *a) +{ + REQUIRE_INSNS_FLAGS(ctx, ISEL); + uint32_t bi = a->bc; + uint32_t mask = 0x08 >> (bi & 0x03); + TCGv t0 = tcg_temp_new(); + TCGv zr; + + tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); + tcg_gen_andi_tl(t0, t0, mask); + + zr = tcg_constant_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[a->rt], t0, zr, + a->ra ? cpu_gpr[a->ra] : zr, + cpu_gpr[a->rb]); + return true; +} + +/* + * Fixed-Point Trap Instructions + */ + +static bool trans_TW(DisasContext *ctx, arg_TW *a) +{ + TCGv_i32 t0; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_i32(a->rt); + gen_helper_TW(tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], t0); + return true; +} + +static bool trans_TWI(DisasContext *ctx, arg_TWI *a) +{ + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_tl(a->si); + t1 = tcg_constant_i32(a->rt); + gen_helper_TW(tcg_env, cpu_gpr[a->ra], t0, t1); + return true; +} + +static bool trans_TD(DisasContext *ctx, arg_TD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv_i32 t0; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_i32(a->rt); + gen_helper_TD(tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], t0); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_TDI(DisasContext *ctx, arg_TDI *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_tl(a->si); + t1 = tcg_constant_i32(a->rt); + gen_helper_TD(tcg_env, cpu_gpr[a->ra], t0, t1); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From 948e257c48cf8e387e12b81f4b96195d52b68455 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:34 +0530 Subject: [PATCH 0840/2884] target/ppc: Move logical fixed-point instructions to decodetree. Moving the below instructions to decodetree specification : andi[s]., {ori, xori}[s] : D-form {and, andc, nand, or, orc, nor, xor, eqv}[.], exts{b, h, w}[.], cnt{l, t}z{w, d}[.], popcnt{b, w, d}, prty{w, d}, cmp, bpermd : X-form With this patch, all the fixed-point logical instructions have been moved to decodetree. The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 8 +- target/ppc/insn32.decode | 38 +++ target/ppc/int_helper.c | 10 +- target/ppc/translate.c | 359 --------------------- target/ppc/translate/fixedpoint-impl.c.inc | 300 +++++++++++++++++ 5 files changed, 347 insertions(+), 368 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index b8af2cf878..4267917615 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -58,8 +58,8 @@ DEF_HELPER_4(DIVDE, i64, env, i64, i64, i32) DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) -DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_1(POPCNTB, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(CMPB, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(CFUGED, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) @@ -68,8 +68,8 @@ DEF_HELPER_FLAGS_1(CDTBCD, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(CBCDTD, TCG_CALL_NO_RWG_SE, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(CMPEQB, TCG_CALL_NO_RWG_SE, i32, tl, tl) -DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(POPCNTW, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(BPERMD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) DEF_HELPER_FLAGS_0(DARN32, TCG_CALL_NO_RWG, tl) DEF_HELPER_FLAGS_0(DARN64, TCG_CALL_NO_RWG, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index cb1e4bd307..dc62bc90aa 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -35,6 +35,9 @@ &D rt ra si:int64_t @D ...... rt:5 ra:5 si:s16 &D +&D_ui rt ra ui:uint64_t +@D_ui ...... rt:5 ra:5 ui:16 &D_ui + &D_bf bf l:bool ra imm @D_bfs ...... bf:3 . l:1 ra:5 imm:s16 &D_bf @D_bfu ...... bf:3 . l:1 ra:5 imm:16 &D_bf @@ -102,6 +105,9 @@ &X_sa rs ra @X_sa ...... rs:5 ra:5 ..... .......... . &X_sa +&X_sa_rc rs ra rc +@X_sa_rc ...... rs:5 ra:5 ..... .......... rc:1 &X_sa_rc + %x_frtp 22:4 !function=times_2 %x_frap 17:4 !function=times_2 %x_frbp 12:4 !function=times_2 @@ -419,6 +425,38 @@ MODUD 011111 ..... ..... ..... 0100001001 - @X ## Fixed-Point Logical Instructions +ANDI_ 011100 ..... ..... ................ @D_ui +ANDIS_ 011101 ..... ..... ................ @D_ui +ORI 011000 ..... ..... ................ @D_ui +ORIS 011001 ..... ..... ................ @D_ui +XORI 011010 ..... ..... ................ @D_ui +XORIS 011011 ..... ..... ................ @D_ui + +AND 011111 ..... ..... ..... 0000011100 . @X_rc +ANDC 011111 ..... ..... ..... 0000111100 . @X_rc +NAND 011111 ..... ..... ..... 0111011100 . @X_rc +OR 011111 ..... ..... ..... 0110111100 . @X_rc +ORC 011111 ..... ..... ..... 0110011100 . @X_rc +NOR 011111 ..... ..... ..... 0001111100 . @X_rc +XOR 011111 ..... ..... ..... 0100111100 . @X_rc +EQV 011111 ..... ..... ..... 0100011100 . @X_rc +CMPB 011111 ..... ..... ..... 0111111100 . @X_rc + +EXTSB 011111 ..... ..... ----- 1110111010 . @X_sa_rc +EXTSH 011111 ..... ..... ----- 1110011010 . @X_sa_rc +EXTSW 011111 ..... ..... ----- 1111011010 . @X_sa_rc +CNTLZW 011111 ..... ..... ----- 0000011010 . @X_sa_rc +CNTTZW 011111 ..... ..... ----- 1000011010 . @X_sa_rc +CNTLZD 011111 ..... ..... ----- 0000111010 . @X_sa_rc +CNTTZD 011111 ..... ..... ----- 1000111010 . @X_sa_rc +POPCNTB 011111 ..... ..... ----- 0001111010 . @X_sa_rc + +POPCNTW 011111 ..... ..... ----- 0101111010 - @X_sa +POPCNTD 011111 ..... ..... ----- 0111111010 - @X_sa +PRTYW 011111 ..... ..... ----- 0010011010 - @X_sa +PRTYD 011111 ..... ..... ----- 0010111010 - @X_sa + +BPERMD 011111 ..... ..... ..... 0011111100 - @X CFUGED 011111 ..... ..... ..... 0011011100 - @X CNTLZDM 011111 ..... ..... ..... 0000111011 - @X CNTTZDM 011111 ..... ..... ..... 1000111011 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d12dcc28e1..2c6b633d65 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -201,7 +201,7 @@ uint64_t helper_DARN64(void) return ret; } -uint64_t helper_bpermd(uint64_t rs, uint64_t rb) +uint64_t helper_BPERMD(uint64_t rs, uint64_t rb) { int i; uint64_t ra = 0; @@ -219,7 +219,7 @@ uint64_t helper_bpermd(uint64_t rs, uint64_t rb) #endif -target_ulong helper_cmpb(target_ulong rs, target_ulong rb) +target_ulong helper_CMPB(target_ulong rs, target_ulong rb) { target_ulong mask = 0xff; target_ulong ra = 0; @@ -288,7 +288,7 @@ target_ulong helper_srad(CPUPPCState *env, target_ulong value, #endif #if defined(TARGET_PPC64) -target_ulong helper_popcntb(target_ulong val) +target_ulong helper_POPCNTB(target_ulong val) { /* Note that we don't fold past bytes */ val = (val & 0x5555555555555555ULL) + ((val >> 1) & @@ -300,7 +300,7 @@ target_ulong helper_popcntb(target_ulong val) return val; } -target_ulong helper_popcntw(target_ulong val) +target_ulong helper_POPCNTW(target_ulong val) { /* Note that we don't fold past words. */ val = (val & 0x5555555555555555ULL) + ((val >> 1) & @@ -316,7 +316,7 @@ target_ulong helper_popcntw(target_ulong val) return val; } #else -target_ulong helper_popcntb(target_ulong val) +target_ulong helper_POPCNTB(target_ulong val) { /* Note that we don't fold past bytes */ val = (val & 0x55555555) + ((val >> 1) & 0x55555555); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e1ccb82f10..2cfa7d37ee 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1563,13 +1563,6 @@ static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) } } -/* cmpb: PowerPC 2.05 specification */ -static void gen_cmpb(DisasContext *ctx) -{ - gen_helper_cmpb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -} - /*** Integer arithmetic ***/ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, @@ -1888,82 +1881,6 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } /*** Integer logical ***/ -#define GEN_LOGICAL2(name, tcg_op, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], \ - cpu_gpr[rB(ctx->opcode)]); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ -} - -#define GEN_LOGICAL1(name, tcg_op, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ -} - -/* and & and. */ -GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER); -/* andc & andc. */ -GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER); - -/* andi. */ -static void gen_andi_(DisasContext *ctx) -{ - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - UIMM(ctx->opcode)); - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); -} - -/* andis. */ -static void gen_andis_(DisasContext *ctx) -{ - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - UIMM(ctx->opcode) << 16); - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); -} - -/* cntlzw */ -static void gen_cntlzw(DisasContext *ctx) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_clzi_i32(t, t, 32); - tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* cnttzw */ -static void gen_cnttzw(DisasContext *ctx) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_ctzi_i32(t, t, 32); - tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* eqv & eqv. */ -GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER); -/* extsb & extsb. */ -GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER); -/* extsh & extsh. */ -GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER); -/* nand & nand. */ -GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER); -/* nor & nor. */ -GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER); #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) static void gen_pause(DisasContext *ctx) @@ -1977,243 +1894,6 @@ static void gen_pause(DisasContext *ctx) } #endif /* defined(TARGET_PPC64) */ -/* or & or. */ -static void gen_or(DisasContext *ctx) -{ - int rs, ra, rb; - - rs = rS(ctx->opcode); - ra = rA(ctx->opcode); - rb = rB(ctx->opcode); - /* Optimisation for mr. ri case */ - if (rs != ra || rs != rb) { - if (rs != rb) { - tcg_gen_or_tl(cpu_gpr[ra], cpu_gpr[rs], cpu_gpr[rb]); - } else { - tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rs]); - } - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[ra]); - } - } else if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rs]); -#if defined(TARGET_PPC64) - } else if (rs != 0) { /* 0 is nop */ - int prio = 0; - - switch (rs) { - case 1: - /* Set process priority to low */ - prio = 2; - break; - case 6: - /* Set process priority to medium-low */ - prio = 3; - break; - case 2: - /* Set process priority to normal */ - prio = 4; - break; -#if !defined(CONFIG_USER_ONLY) - case 31: - if (!ctx->pr) { - /* Set process priority to very low */ - prio = 1; - } - break; - case 5: - if (!ctx->pr) { - /* Set process priority to medium-hight */ - prio = 5; - } - break; - case 3: - if (!ctx->pr) { - /* Set process priority to high */ - prio = 6; - } - break; - case 7: - if (ctx->hv && !ctx->pr) { - /* Set process priority to very high */ - prio = 7; - } - break; -#endif - default: - break; - } - if (prio) { - TCGv t0 = tcg_temp_new(); - gen_load_spr(t0, SPR_PPR); - tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); - tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); - gen_store_spr(SPR_PPR, t0); - } -#if !defined(CONFIG_USER_ONLY) - /* - * Pause out of TCG otherwise spin loops with smt_low eat too - * much CPU and the kernel hangs. This applies to all - * encodings other than no-op, e.g., miso(rs=26), yield(27), - * mdoio(29), mdoom(30), and all currently undefined. - */ - gen_pause(ctx); -#endif -#endif - } -} -/* orc & orc. */ -GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER); - -/* xor & xor. */ -static void gen_xor(DisasContext *ctx) -{ - /* Optimisation for "set to zero" case */ - if (rS(ctx->opcode) != rB(ctx->opcode)) { - tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - } else { - tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); - } - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* ori */ -static void gen_ori(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - return; - } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); -} - -/* oris */ -static void gen_oris(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - uimm << 16); -} - -/* xori */ -static void gen_xori(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); -} - -/* xoris */ -static void gen_xoris(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - uimm << 16); -} - -/* popcntb : PowerPC 2.03 specification */ -static void gen_popcntb(DisasContext *ctx) -{ - gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -} - -static void gen_popcntw(DisasContext *ctx) -{ -#if defined(TARGET_PPC64) - gen_helper_popcntw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#else - tcg_gen_ctpop_i32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#endif -} - -#if defined(TARGET_PPC64) -/* popcntd: PowerPC 2.06 specification */ -static void gen_popcntd(DisasContext *ctx) -{ - tcg_gen_ctpop_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -} -#endif - -/* prtyw: PowerPC 2.05 specification */ -static void gen_prtyw(DisasContext *ctx) -{ - TCGv ra = cpu_gpr[rA(ctx->opcode)]; - TCGv rs = cpu_gpr[rS(ctx->opcode)]; - TCGv t0 = tcg_temp_new(); - tcg_gen_shri_tl(t0, rs, 16); - tcg_gen_xor_tl(ra, rs, t0); - tcg_gen_shri_tl(t0, ra, 8); - tcg_gen_xor_tl(ra, ra, t0); - tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); -} - -#if defined(TARGET_PPC64) -/* prtyd: PowerPC 2.05 specification */ -static void gen_prtyd(DisasContext *ctx) -{ - TCGv ra = cpu_gpr[rA(ctx->opcode)]; - TCGv rs = cpu_gpr[rS(ctx->opcode)]; - TCGv t0 = tcg_temp_new(); - tcg_gen_shri_tl(t0, rs, 32); - tcg_gen_xor_tl(ra, rs, t0); - tcg_gen_shri_tl(t0, ra, 16); - tcg_gen_xor_tl(ra, ra, t0); - tcg_gen_shri_tl(t0, ra, 8); - tcg_gen_xor_tl(ra, ra, t0); - tcg_gen_andi_tl(ra, ra, 1); -} -#endif - -#if defined(TARGET_PPC64) -/* bpermd */ -static void gen_bpermd(DisasContext *ctx) -{ - gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); -} -#endif - -#if defined(TARGET_PPC64) -/* extsw & extsw. */ -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); - -/* cntlzd */ -static void gen_cntlzd(DisasContext *ctx) -{ - tcg_gen_clzi_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], 64); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* cnttzd */ -static void gen_cnttzd(DisasContext *ctx) -{ - tcg_gen_ctzi_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], 64); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} -#endif - /*** Integer rotate ***/ /* rlwimi & rlwimi. */ @@ -5942,30 +5622,9 @@ GEN_HANDLER_E(brw, 0x1F, 0x1B, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA310), GEN_HANDLER_E(brh, 0x1F, 0x1B, 0x06, 0x0000F801, PPC_NONE, PPC2_ISA310), #endif GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), -GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), -GEN_HANDLER_E(cnttzw, 0x1F, 0x1A, 0x10, 0x00000000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(copy, 0x1F, 0x06, 0x18, 0x03C00001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cp_abort, 0x1F, 0x06, 0x1A, 0x03FFF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(paste, 0x1F, 0x06, 0x1C, 0x03C00000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER), -GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(popcntb, 0x1F, 0x1A, 0x03, 0x0000F801, PPC_POPCNTB), -GEN_HANDLER(popcntw, 0x1F, 0x1A, 0x0b, 0x0000F801, PPC_POPCNTWD), -GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), -#if defined(TARGET_PPC64) -GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), -GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), -GEN_HANDLER_E(cnttzd, 0x1F, 0x1A, 0x11, 0x00000000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), -#endif GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -6136,24 +5795,6 @@ GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -#undef GEN_LOGICAL1 -#undef GEN_LOGICAL2 -#define GEN_LOGICAL2(name, tcg_op, opc, type) \ -GEN_HANDLER(name, 0x1F, 0x1C, opc, 0x00000000, type) -#define GEN_LOGICAL1(name, tcg_op, opc, type) \ -GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, type) -GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER), -GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER), -GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER), -GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER), -GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER), -GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER), -GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER), -GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER), -#if defined(TARGET_PPC64) -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B), -#endif - #if defined(TARGET_PPC64) #undef GEN_PPC64_R2 #undef GEN_PPC64_R4 diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 872fed664d..fa0191e866 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -856,6 +856,285 @@ TRANS(SETBCR, do_set_bool_cond, false, true) TRANS(SETNBC, do_set_bool_cond, true, false) TRANS(SETNBCR, do_set_bool_cond, true, true) +/* + * Fixed-Point Logical Instructions + */ + +static bool do_addi_(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + tcg_gen_andi_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + return true; +} + +static bool do_ori(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + if (a->rt == a->ra && a->ui == 0) { + /* NOP */ + return true; + } + tcg_gen_ori_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + return true; +} + +static bool do_xori(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + if (a->rt == a->ra && a->ui == 0) { + /* NOP */ + return true; + } + tcg_gen_xori_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + return true; +} + +static bool do_logical1(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv, TCGv)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rs]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool do_logical2(DisasContext *ctx, arg_X_rc *a, + void (*helper)(TCGv, TCGv, TCGv)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool trans_OR(DisasContext *ctx, arg_OR *a) +{ + /* Optimisation for mr. ri case */ + if (a->rt != a->ra || a->rt != a->rb) { + if (a->rt != a->rb) { + tcg_gen_or_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + } else { + tcg_gen_mov_tl(cpu_gpr[a->ra], cpu_gpr[a->rt]); + } + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + } else if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); +#if defined(TARGET_PPC64) + } else if (a->rt != 0) { /* 0 is nop */ + int prio = 0; + + switch (a->rt) { + case 1: + /* Set process priority to low */ + prio = 2; + break; + case 6: + /* Set process priority to medium-low */ + prio = 3; + break; + case 2: + /* Set process priority to normal */ + prio = 4; + break; +#if !defined(CONFIG_USER_ONLY) + case 31: + if (!ctx->pr) { + /* Set process priority to very low */ + prio = 1; + } + break; + case 5: + if (!ctx->pr) { + /* Set process priority to medium-hight */ + prio = 5; + } + break; + case 3: + if (!ctx->pr) { + /* Set process priority to high */ + prio = 6; + } + break; + case 7: + if (ctx->hv && !ctx->pr) { + /* Set process priority to very high */ + prio = 7; + } + break; +#endif + default: + break; + } + if (prio) { + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_PPR); + tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); + tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); + gen_store_spr(SPR_PPR, t0); + } +#if !defined(CONFIG_USER_ONLY) + /* + * Pause out of TCG otherwise spin loops with smt_low eat too + * much CPU and the kernel hangs. This applies to all + * encodings other than no-op, e.g., miso(rs=26), yield(27), + * mdoio(29), mdoom(30), and all currently undefined. + */ + gen_pause(ctx); +#endif +#endif + } + + return true; +} + +static bool trans_XOR(DisasContext *ctx, arg_XOR *a) +{ + /* Optimisation for "set to zero" case */ + if (a->rt != a->rb) { + tcg_gen_xor_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + } else { + tcg_gen_movi_tl(cpu_gpr[a->ra], 0); + } + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool trans_CMPB(DisasContext *ctx, arg_CMPB *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + gen_helper_CMPB(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + return true; +} + +static bool do_cntzw(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv_i32, TCGv_i32, uint32_t)) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t, cpu_gpr[a->rs]); + helper(t, t, 32); + tcg_gen_extu_i32_tl(cpu_gpr[a->ra], t); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +#if defined(TARGET_PPC64) +static bool do_cntzd(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv_i64, TCGv_i64, uint64_t)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rs], 64); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} +#endif + +static bool trans_CNTLZD(DisasContext *ctx, arg_CNTLZD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + do_cntzd(ctx, a, tcg_gen_clzi_i64); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_CNTTZD(DisasContext *ctx, arg_CNTTZD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + do_cntzd(ctx, a, tcg_gen_ctzi_i64); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_POPCNTB(DisasContext *ctx, arg_POPCNTB *a) +{ + REQUIRE_INSNS_FLAGS(ctx, POPCNTB); + gen_helper_POPCNTB(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} + +static bool trans_POPCNTW(DisasContext *ctx, arg_POPCNTW *a) +{ + REQUIRE_INSNS_FLAGS(ctx, POPCNTWD); +#if defined(TARGET_PPC64) + gen_helper_POPCNTW(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#else + tcg_gen_ctpop_i32(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#endif + return true; +} + +static bool trans_POPCNTD(DisasContext *ctx, arg_POPCNTD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, POPCNTWD); +#if defined(TARGET_PPC64) + tcg_gen_ctpop_i64(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_PRTYW(DisasContext *ctx, arg_PRTYW *a) +{ + TCGv ra = cpu_gpr[a->ra]; + TCGv rs = cpu_gpr[a->rs]; + TCGv t0 = tcg_temp_new(); + + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + tcg_gen_shri_tl(t0, rs, 16); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); + return true; +} + +static bool trans_PRTYD(DisasContext *ctx, arg_PRTYD *a) +{ + TCGv ra = cpu_gpr[a->ra]; + TCGv rs = cpu_gpr[a->rs]; + TCGv t0 = tcg_temp_new(); + + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + tcg_gen_shri_tl(t0, rs, 32); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 16); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, 1); + return true; +} + +static bool trans_BPERMD(DisasContext *ctx, arg_BPERMD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, PERM_ISA206); +#if defined(TARGET_PPC64) + gen_helper_BPERMD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_CFUGED(DisasContext *ctx, arg_X *a) { REQUIRE_64BIT(ctx); @@ -944,6 +1223,27 @@ static bool trans_PEXTD(DisasContext *ctx, arg_X *a) return true; } +TRANS(ANDI_, do_addi_, false); +TRANS(ANDIS_, do_addi_, true); +TRANS(ORI, do_ori, false); +TRANS(ORIS, do_ori, true); +TRANS(XORI, do_xori, false); +TRANS(XORIS, do_xori, true); + +TRANS(AND, do_logical2, tcg_gen_and_tl); +TRANS(ANDC, do_logical2, tcg_gen_andc_tl); +TRANS(NAND, do_logical2, tcg_gen_nand_tl); +TRANS(ORC, do_logical2, tcg_gen_orc_tl); +TRANS(NOR, do_logical2, tcg_gen_nor_tl); +TRANS(EQV, do_logical2, tcg_gen_eqv_tl); +TRANS(EXTSB, do_logical1, tcg_gen_ext8s_tl); +TRANS(EXTSH, do_logical1, tcg_gen_ext16s_tl); + +TRANS(CNTLZW, do_cntzw, tcg_gen_clzi_i32); +TRANS_FLAGS2(ISA300, CNTTZW, do_cntzw, tcg_gen_ctzi_i32); + +TRANS64(EXTSW, do_logical1, tcg_gen_ext32s_tl); + static bool trans_ADDG6S(DisasContext *ctx, arg_X *a) { const target_ulong carry_bits = (target_ulong)-1 / 0xf; From 21b5f5464f97f68f025c86330146d038d2ee79ad Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Mon, 29 Apr 2024 10:43:15 +0530 Subject: [PATCH 0841/2884] target/ppc: Move VMX storage access instructions to decodetree Moving the following instructions to decodetree specification : {l,st}ve{b,h,w}x, {l,st}v{x,xl}, lvs{l,r} : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 12 +- target/ppc/insn32.decode | 17 +++ target/ppc/mem_helper.c | 12 +- target/ppc/translate.c | 2 - target/ppc/translate/vmx-impl.c.inc | 219 ++++++++++++---------------- target/ppc/translate/vmx-ops.c.inc | 19 --- 6 files changed, 119 insertions(+), 162 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 4267917615..6d6f31366c 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -267,12 +267,12 @@ DEF_HELPER_5(VMSUMSHS, void, env, avr, avr, avr, avr) DEF_HELPER_FLAGS_5(VMLADDUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_2(mtvscr, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_1(mfvscr, TCG_CALL_NO_RWG, i32, env) -DEF_HELPER_3(lvebx, void, env, avr, tl) -DEF_HELPER_3(lvehx, void, env, avr, tl) -DEF_HELPER_3(lvewx, void, env, avr, tl) -DEF_HELPER_3(stvebx, void, env, avr, tl) -DEF_HELPER_3(stvehx, void, env, avr, tl) -DEF_HELPER_3(stvewx, void, env, avr, tl) +DEF_HELPER_3(LVEBX, void, env, avr, tl) +DEF_HELPER_3(LVEHX, void, env, avr, tl) +DEF_HELPER_3(LVEWX, void, env, avr, tl) +DEF_HELPER_3(STVEBX, void, env, avr, tl) +DEF_HELPER_3(STVEHX, void, env, avr, tl) +DEF_HELPER_3(STVEWX, void, env, avr, tl) #if defined(TARGET_PPC64) DEF_HELPER_4(lxvl, void, env, tl, vsr, tl) DEF_HELPER_4(lxvll, void, env, tl, vsr, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index dc62bc90aa..11be21d230 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -661,6 +661,23 @@ DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc VPMSUMD 000100 ..... ..... ..... 10011001000 @VX +## Vector Load/Store Instructions + +LVEBX 011111 ..... ..... ..... 0000000111 - @X +LVEHX 011111 ..... ..... ..... 0000100111 - @X +LVEWX 011111 ..... ..... ..... 0001000111 - @X +LVX 011111 ..... ..... ..... 0001100111 - @X +LVXL 011111 ..... ..... ..... 0101100111 - @X + +STVEBX 011111 ..... ..... ..... 0010000111 - @X +STVEHX 011111 ..... ..... ..... 0010100111 - @X +STVEWX 011111 ..... ..... ..... 0011000111 - @X +STVX 011111 ..... ..... ..... 0011100111 - @X +STVXL 011111 ..... ..... ..... 0111100111 - @X + +LVSL 011111 ..... ..... ..... 0000000110 - @X +LVSR 011111 ..... ..... ..... 0000100110 - @X + ## Vector Integer Instructions VCMPEQUB 000100 ..... ..... ..... . 0000000110 @VC diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index ea7e8443a8..f88155ad45 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -404,9 +404,9 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, } \ } #define I(x) (x) -LVE(lvebx, cpu_ldub_data_ra, I, u8) -LVE(lvehx, cpu_lduw_data_ra, bswap16, u16) -LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) +LVE(LVEBX, cpu_ldub_data_ra, I, u8) +LVE(LVEHX, cpu_lduw_data_ra, bswap16, u16) +LVE(LVEWX, cpu_ldl_data_ra, bswap32, u32) #undef I #undef LVE @@ -432,9 +432,9 @@ LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) } \ } #define I(x) (x) -STVE(stvebx, cpu_stb_data_ra, I, u8) -STVE(stvehx, cpu_stw_data_ra, bswap16, u16) -STVE(stvewx, cpu_stl_data_ra, bswap32, u32) +STVE(STVEBX, cpu_stb_data_ra, I, u8) +STVE(STVEHX, cpu_stw_data_ra, bswap16, u16) +STVE(STVEWX, cpu_stl_data_ra, bswap32, u32) #undef I #undef LVE diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 2cfa7d37ee..2c39605273 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5790,8 +5790,6 @@ GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_440_SPEC), -GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), -GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index b56e615c24..4d5e743cfe 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -14,125 +14,88 @@ static inline TCGv_ptr gen_avr_ptr(int reg) return r; } -#define GEN_VR_LDX(name, opc2, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 avr; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - avr = tcg_temp_new_i64(); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - tcg_gen_andi_tl(EA, EA, ~0xf); \ - /* \ - * We only need to swap high and low halves. gen_qemu_ld64_i64 \ - * does necessary 64-bit byteswap already. \ - */ \ - if (ctx->le_mode) { \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, false); \ - tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, true); \ - } else { \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, true); \ - tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, false); \ - } \ +static bool trans_LVX(DisasContext *ctx, arg_X *a) +{ + TCGv EA; + TCGv_i64 avr; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + avr = tcg_temp_new_i64(); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_andi_tl(EA, EA, ~0xf); + /* + * We only need to swap high and low halves. gen_qemu_ld64_i64 + * does necessary 64-bit byteswap already. + */ + gen_qemu_ld64_i64(ctx, avr, EA); + set_avr64(a->rt, avr, !ctx->le_mode); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, avr, EA); + set_avr64(a->rt, avr, ctx->le_mode); + return true; } -#define GEN_VR_STX(name, opc2, opc3) \ -static void gen_st##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 avr; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - avr = tcg_temp_new_i64(); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - tcg_gen_andi_tl(EA, EA, ~0xf); \ - /* \ - * We only need to swap high and low halves. gen_qemu_st64_i64 \ - * does necessary 64-bit byteswap already. \ - */ \ - if (ctx->le_mode) { \ - get_avr64(avr, rD(ctx->opcode), false); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - tcg_gen_addi_tl(EA, EA, 8); \ - get_avr64(avr, rD(ctx->opcode), true); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - } else { \ - get_avr64(avr, rD(ctx->opcode), true); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - tcg_gen_addi_tl(EA, EA, 8); \ - get_avr64(avr, rD(ctx->opcode), false); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - } \ -} - -#define GEN_VR_LVE(name, opc2, opc3, size) \ -static void gen_lve##name(DisasContext *ctx) \ - { \ - TCGv EA; \ - TCGv_ptr rs; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - if (size > 1) { \ - tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ - } \ - rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_lve##name(tcg_env, rs, EA); \ - } - -#define GEN_VR_STVE(name, opc2, opc3, size) \ -static void gen_stve##name(DisasContext *ctx) \ - { \ - TCGv EA; \ - TCGv_ptr rs; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - if (size > 1) { \ - tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ - } \ - rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_stve##name(tcg_env, rs, EA); \ - } - -GEN_VR_LDX(lvx, 0x07, 0x03); /* As we don't emulate the cache, lvxl is strictly equivalent to lvx */ -GEN_VR_LDX(lvxl, 0x07, 0x0B); +QEMU_FLATTEN +static bool trans_LVXL(DisasContext *ctx, arg_LVXL *a) +{ + return trans_LVX(ctx, a); +} -GEN_VR_LVE(bx, 0x07, 0x00, 1); -GEN_VR_LVE(hx, 0x07, 0x01, 2); -GEN_VR_LVE(wx, 0x07, 0x02, 4); +static bool trans_STVX(DisasContext *ctx, arg_STVX *a) +{ + TCGv EA; + TCGv_i64 avr; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + avr = tcg_temp_new_i64(); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_andi_tl(EA, EA, ~0xf); + /* + * We only need to swap high and low halves. gen_qemu_st64_i64 + * does necessary 64-bit byteswap already. + */ + get_avr64(avr, a->rt, !ctx->le_mode); + gen_qemu_st64_i64(ctx, avr, EA); + tcg_gen_addi_tl(EA, EA, 8); + get_avr64(avr, a->rt, ctx->le_mode); + gen_qemu_st64_i64(ctx, avr, EA); + return true; +} -GEN_VR_STX(svx, 0x07, 0x07); /* As we don't emulate the cache, stvxl is strictly equivalent to stvx */ -GEN_VR_STX(svxl, 0x07, 0x0F); +QEMU_FLATTEN +static bool trans_STVXL(DisasContext *ctx, arg_STVXL *a) +{ + return trans_STVX(ctx, a); +} -GEN_VR_STVE(bx, 0x07, 0x04, 1); -GEN_VR_STVE(hx, 0x07, 0x05, 2); -GEN_VR_STVE(wx, 0x07, 0x06, 4); +static bool do_ldst_ve_X(DisasContext *ctx, arg_X *a, int size, + void (*helper)(TCGv_env, TCGv_ptr, TCGv)) +{ + TCGv EA; + TCGv_ptr vrt; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + if (size > 1) { + tcg_gen_andi_tl(EA, EA, ~(size - 1)); + } + vrt = gen_avr_ptr(a->rt); + helper(tcg_env, vrt, EA); + return true; +} + +TRANS(LVEBX, do_ldst_ve_X, 1, gen_helper_LVEBX); +TRANS(LVEHX, do_ldst_ve_X, 2, gen_helper_LVEHX); +TRANS(LVEWX, do_ldst_ve_X, 4, gen_helper_LVEWX); + +TRANS(STVEBX, do_ldst_ve_X, 1, gen_helper_STVEBX); +TRANS(STVEHX, do_ldst_ve_X, 2, gen_helper_STVEHX); +TRANS(STVEWX, do_ldst_ve_X, 4, gen_helper_STVEWX); static void gen_mfvscr(DisasContext *ctx) { @@ -460,15 +423,17 @@ static void trans_vmrgow(DisasContext *ctx) * Let X be the 32-byte value 0x00 || 0x01 || 0x02 || ... || 0x1E || 0x1F. * Bytes sh:sh+15 of X are placed into vD. */ -static void trans_lvsl(DisasContext *ctx) +static bool trans_LVSL(DisasContext *ctx, arg_LVSL *a) { - int VT = rD(ctx->opcode); TCGv_i64 result = tcg_temp_new_i64(); TCGv_i64 sh = tcg_temp_new_i64(); TCGv EA = tcg_temp_new(); + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + /* Get sh(from description) by anding EA with 0xf. */ - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_extu_tl_i64(sh, EA); tcg_gen_andi_i64(sh, sh, 0xfULL); @@ -478,13 +443,14 @@ static void trans_lvsl(DisasContext *ctx) */ tcg_gen_muli_i64(sh, sh, 0x0101010101010101ULL); tcg_gen_addi_i64(result, sh, 0x0001020304050607ull); - set_avr64(VT, result, true); + set_avr64(a->rt, result, true); /* * Create bytes sh+8:sh+15 of X(from description) and place them in * lower doubleword of vD. */ tcg_gen_addi_i64(result, sh, 0x08090a0b0c0d0e0fULL); - set_avr64(VT, result, false); + set_avr64(a->rt, result, false); + return true; } /* @@ -494,16 +460,17 @@ static void trans_lvsl(DisasContext *ctx) * Let X be the 32-byte value 0x00 || 0x01 || 0x02 || ... || 0x1E || 0x1F. * Bytes (16-sh):(31-sh) of X are placed into vD. */ -static void trans_lvsr(DisasContext *ctx) +static bool trans_LVSR(DisasContext *ctx, arg_LVSR *a) { - int VT = rD(ctx->opcode); TCGv_i64 result = tcg_temp_new_i64(); TCGv_i64 sh = tcg_temp_new_i64(); TCGv EA = tcg_temp_new(); + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); /* Get sh(from description) by anding EA with 0xf. */ - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_extu_tl_i64(sh, EA); tcg_gen_andi_i64(sh, sh, 0xfULL); @@ -513,13 +480,14 @@ static void trans_lvsr(DisasContext *ctx) */ tcg_gen_muli_i64(sh, sh, 0x0101010101010101ULL); tcg_gen_subfi_i64(result, 0x1011121314151617ULL, sh); - set_avr64(VT, result, true); + set_avr64(a->rt, result, true); /* * Create bytes (24-sh):(32-sh) of X(from description) and place them in * lower doubleword of vD. */ tcg_gen_subfi_i64(result, 0x18191a1b1c1d1e1fULL, sh); - set_avr64(VT, result, false); + set_avr64(a->rt, result, false); + return true; } /* @@ -1158,8 +1126,6 @@ GEN_VXFORM_TRANS_DUAL(vmrgow, PPC_NONE, PPC2_ALTIVEC_207, GEN_VXFORM_HETRO(vextubrx, 6, 28) GEN_VXFORM_HETRO(vextuhrx, 6, 29) GEN_VXFORM_HETRO(vextuwrx, 6, 30) -GEN_VXFORM_TRANS(lvsl, 6, 31) -GEN_VXFORM_TRANS(lvsr, 6, 32) GEN_VXFORM_TRANS_DUAL(vmrgew, PPC_NONE, PPC2_ALTIVEC_207, vextuwrx, PPC_NONE, PPC2_ISA300) @@ -3365,11 +3331,6 @@ TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) #undef DIVS64 #undef DIVU64 -#undef GEN_VR_LDX -#undef GEN_VR_STX -#undef GEN_VR_LVE -#undef GEN_VR_STVE - #undef GEN_VX_LOGICAL #undef GEN_VX_LOGICAL_207 #undef GEN_VXFORM diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 33fec8aca4..672fba3796 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -1,22 +1,3 @@ -#define GEN_VR_LDX(name, opc2, opc3) \ -GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_STX(name, opc2, opc3) \ -GEN_HANDLER(st##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_LVE(name, opc2, opc3) \ - GEN_HANDLER(lve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_STVE(name, opc2, opc3) \ - GEN_HANDLER(stve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -GEN_VR_LDX(lvx, 0x07, 0x03), -GEN_VR_LDX(lvxl, 0x07, 0x0B), -GEN_VR_LVE(bx, 0x07, 0x00), -GEN_VR_LVE(hx, 0x07, 0x01), -GEN_VR_LVE(wx, 0x07, 0x02), -GEN_VR_STX(svx, 0x07, 0x07), -GEN_VR_STX(svxl, 0x07, 0x0F), -GEN_VR_STVE(bx, 0x07, 0x04), -GEN_VR_STVE(hx, 0x07, 0x05), -GEN_VR_STVE(wx, 0x07, 0x06), - #define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) From 664eb39ec94d6fc6e8a96949b0a27deb0c32f50d Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Mon, 29 Apr 2024 10:43:16 +0530 Subject: [PATCH 0842/2884] target/ppc: Move VMX integer logical instructions to decodetree. Moving the following instructions to decodetree specification: v{and, andc, nand, or, orc, nor, xor, eqv} : VX-form The changes were verified by validating that the tcp ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 11 +++++++++++ target/ppc/translate/vmx-impl.c.inc | 22 ++++++++++------------ target/ppc/translate/vmx-ops.c.inc | 15 --------------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 11be21d230..16f3711073 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -709,6 +709,17 @@ VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf +## Vector Integer Logical Instructions + +VAND 000100 ..... ..... ..... 10000000100 @VX +VANDC 000100 ..... ..... ..... 10001000100 @VX +VNAND 000100 ..... ..... ..... 10110000100 @VX +VOR 000100 ..... ..... ..... 10010000100 @VX +VORC 000100 ..... ..... ..... 10101000100 @VX +VNOR 000100 ..... ..... ..... 10100000100 @VX +VXOR 000100 ..... ..... ..... 10011000100 @VX +VEQV 000100 ..... ..... ..... 11010000100 @VX + ## Vector Integer Average Instructions VAVGSB 000100 ..... ..... ..... 10100000010 @VX diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 4d5e743cfe..cefe04127c 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -205,16 +205,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ 16, 16); \ } -/* Logical operations */ -GEN_VXFORM_V(vand, MO_64, tcg_gen_gvec_and, 2, 16); -GEN_VXFORM_V(vandc, MO_64, tcg_gen_gvec_andc, 2, 17); -GEN_VXFORM_V(vor, MO_64, tcg_gen_gvec_or, 2, 18); -GEN_VXFORM_V(vxor, MO_64, tcg_gen_gvec_xor, 2, 19); -GEN_VXFORM_V(vnor, MO_64, tcg_gen_gvec_nor, 2, 20); -GEN_VXFORM_V(veqv, MO_64, tcg_gen_gvec_eqv, 2, 26); -GEN_VXFORM_V(vnand, MO_64, tcg_gen_gvec_nand, 2, 22); -GEN_VXFORM_V(vorc, MO_64, tcg_gen_gvec_orc, 2, 21); - #define GEN_VXFORM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ @@ -727,6 +717,16 @@ TRANS_FLAGS(ALTIVEC, VRLH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_rotlv) TRANS_FLAGS(ALTIVEC, VRLW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_rotlv) TRANS_FLAGS2(ALTIVEC_207, VRLD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_rotlv) +/* Logical operations */ +TRANS_FLAGS(ALTIVEC, VAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_and); +TRANS_FLAGS(ALTIVEC, VANDC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_andc); +TRANS_FLAGS(ALTIVEC, VOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_or); +TRANS_FLAGS(ALTIVEC, VXOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_xor); +TRANS_FLAGS(ALTIVEC, VNOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nor); +TRANS_FLAGS2(ALTIVEC_207, VEQV, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_eqv); +TRANS_FLAGS2(ALTIVEC_207, VNAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nand); +TRANS_FLAGS2(ALTIVEC_207, VORC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_orc); + static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) { TCGv_vec t0 = tcg_temp_new_vec_matching(vrb), @@ -3331,8 +3331,6 @@ TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) #undef DIVS64 #undef DIVU64 -#undef GEN_VX_LOGICAL -#undef GEN_VX_LOGICAL_207 #undef GEN_VXFORM #undef GEN_VXFORM_207 #undef GEN_VXFORM_DUAL diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 672fba3796..80c5217749 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -1,18 +1,3 @@ -#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ -GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) - -#define GEN_VX_LOGICAL_207(name, tcg_op, opc2, opc3) \ -GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) - -GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16), -GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17), -GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18), -GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19), -GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20), -GEN_VX_LOGICAL_207(veqv, tcg_gen_eqv_i64, 2, 26), -GEN_VX_LOGICAL_207(vnand, tcg_gen_nand_i64, 2, 22), -GEN_VX_LOGICAL_207(vorc, tcg_gen_orc_i64, 2, 21), - #define GEN_VXFORM(name, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) From 687a30ad3c0f219bb372d806575eb47fae0cd27c Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Mon, 29 Apr 2024 10:43:17 +0530 Subject: [PATCH 0843/2884] target/ppc: Move VMX integer max/min instructions to decodetree. Moving the following instructions to decodetree specification : v{max, min}{u, s}{b, h, w, d} : VX-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 22 +++++++++++++++++ target/ppc/translate/vmx-impl.c.inc | 37 ++++++++++++++++------------- target/ppc/translate/vmx-ops.c.inc | 16 ------------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 16f3711073..05c1d8c12d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -852,6 +852,28 @@ VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb VNEGD 000100 ..... 00111 ..... 11000000010 @VX_tb VNEGW 000100 ..... 00110 ..... 11000000010 @VX_tb +## Vector Integer Maximum/Minimum Instructions + +VMAXUB 000100 ..... ..... ..... 00000000010 @VX +VMAXUH 000100 ..... ..... ..... 00001000010 @VX +VMAXUW 000100 ..... ..... ..... 00010000010 @VX +VMAXUD 000100 ..... ..... ..... 00011000010 @VX + +VMAXSB 000100 ..... ..... ..... 00100000010 @VX +VMAXSH 000100 ..... ..... ..... 00101000010 @VX +VMAXSW 000100 ..... ..... ..... 00110000010 @VX +VMAXSD 000100 ..... ..... ..... 00111000010 @VX + +VMINUB 000100 ..... ..... ..... 01000000010 @VX +VMINUH 000100 ..... ..... ..... 01001000010 @VX +VMINUW 000100 ..... ..... ..... 01010000010 @VX +VMINUD 000100 ..... ..... ..... 01011000010 @VX + +VMINSB 000100 ..... ..... ..... 01100000010 @VX +VMINSH 000100 ..... ..... ..... 01101000010 @VX +VMINSW 000100 ..... ..... ..... 01110000010 @VX +VMINSD 000100 ..... ..... ..... 01111000010 @VX + ## Vector Mask Manipulation Instructions MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index cefe04127c..8084af75cc 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -342,22 +342,6 @@ GEN_VXFORM_V(vsububm, MO_8, tcg_gen_gvec_sub, 0, 16); GEN_VXFORM_V(vsubuhm, MO_16, tcg_gen_gvec_sub, 0, 17); GEN_VXFORM_V(vsubuwm, MO_32, tcg_gen_gvec_sub, 0, 18); GEN_VXFORM_V(vsubudm, MO_64, tcg_gen_gvec_sub, 0, 19); -GEN_VXFORM_V(vmaxub, MO_8, tcg_gen_gvec_umax, 1, 0); -GEN_VXFORM_V(vmaxuh, MO_16, tcg_gen_gvec_umax, 1, 1); -GEN_VXFORM_V(vmaxuw, MO_32, tcg_gen_gvec_umax, 1, 2); -GEN_VXFORM_V(vmaxud, MO_64, tcg_gen_gvec_umax, 1, 3); -GEN_VXFORM_V(vmaxsb, MO_8, tcg_gen_gvec_smax, 1, 4); -GEN_VXFORM_V(vmaxsh, MO_16, tcg_gen_gvec_smax, 1, 5); -GEN_VXFORM_V(vmaxsw, MO_32, tcg_gen_gvec_smax, 1, 6); -GEN_VXFORM_V(vmaxsd, MO_64, tcg_gen_gvec_smax, 1, 7); -GEN_VXFORM_V(vminub, MO_8, tcg_gen_gvec_umin, 1, 8); -GEN_VXFORM_V(vminuh, MO_16, tcg_gen_gvec_umin, 1, 9); -GEN_VXFORM_V(vminuw, MO_32, tcg_gen_gvec_umin, 1, 10); -GEN_VXFORM_V(vminud, MO_64, tcg_gen_gvec_umin, 1, 11); -GEN_VXFORM_V(vminsb, MO_8, tcg_gen_gvec_smin, 1, 12); -GEN_VXFORM_V(vminsh, MO_16, tcg_gen_gvec_smin, 1, 13); -GEN_VXFORM_V(vminsw, MO_32, tcg_gen_gvec_smin, 1, 14); -GEN_VXFORM_V(vminsd, MO_64, tcg_gen_gvec_smin, 1, 15); GEN_VXFORM(vmrghb, 6, 0); GEN_VXFORM(vmrghh, 6, 1); GEN_VXFORM(vmrghw, 6, 2); @@ -727,6 +711,27 @@ TRANS_FLAGS2(ALTIVEC_207, VEQV, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_eqv); TRANS_FLAGS2(ALTIVEC_207, VNAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nand); TRANS_FLAGS2(ALTIVEC_207, VORC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_orc); +/* Integer Max/Min operations */ +TRANS_FLAGS(ALTIVEC, VMAXUB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_umax); +TRANS_FLAGS(ALTIVEC, VMAXUH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_umax); +TRANS_FLAGS(ALTIVEC, VMAXUW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_umax); +TRANS_FLAGS2(ALTIVEC_207, VMAXUD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_umax); + +TRANS_FLAGS(ALTIVEC, VMAXSB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_smax); +TRANS_FLAGS(ALTIVEC, VMAXSH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_smax); +TRANS_FLAGS(ALTIVEC, VMAXSW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_smax); +TRANS_FLAGS2(ALTIVEC_207, VMAXSD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_smax); + +TRANS_FLAGS(ALTIVEC, VMINUB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_umin); +TRANS_FLAGS(ALTIVEC, VMINUH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_umin); +TRANS_FLAGS(ALTIVEC, VMINUW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_umin); +TRANS_FLAGS2(ALTIVEC_207, VMINUD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_umin); + +TRANS_FLAGS(ALTIVEC, VMINSB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_smin); +TRANS_FLAGS(ALTIVEC, VMINSH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_smin); +TRANS_FLAGS(ALTIVEC, VMINSW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_smin); +TRANS_FLAGS2(ALTIVEC_207, VMINSD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_smin); + static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) { TCGv_vec t0 = tcg_temp_new_vec_matching(vrb), diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 80c5217749..7bb11b0549 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -33,22 +33,6 @@ GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vsubuwm, bcdus, 0, 18, PPC_ALTIVEC, PPC2_ISA300), GEN_VXFORM_DUAL(vsubudm, bcds, 0, 19, PPC2_ALTIVEC_207, PPC2_ISA300), GEN_VXFORM_300(bcds, 0, 27), -GEN_VXFORM(vmaxub, 1, 0), -GEN_VXFORM(vmaxuh, 1, 1), -GEN_VXFORM(vmaxuw, 1, 2), -GEN_VXFORM_207(vmaxud, 1, 3), -GEN_VXFORM(vmaxsb, 1, 4), -GEN_VXFORM(vmaxsh, 1, 5), -GEN_VXFORM(vmaxsw, 1, 6), -GEN_VXFORM_207(vmaxsd, 1, 7), -GEN_VXFORM(vminub, 1, 8), -GEN_VXFORM(vminuh, 1, 9), -GEN_VXFORM(vminuw, 1, 10), -GEN_VXFORM_207(vminud, 1, 11), -GEN_VXFORM(vminsb, 1, 12), -GEN_VXFORM(vminsh, 1, 13), -GEN_VXFORM(vminsw, 1, 14), -GEN_VXFORM_207(vminsd, 1, 15), GEN_VXFORM(vmrghb, 6, 0), GEN_VXFORM(vmrghh, 6, 1), GEN_VXFORM(vmrghw, 6, 2), From a7138e28a242680ae25b52ed44842cde235103f0 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:29 +1000 Subject: [PATCH 0844/2884] target/ppc: Add new hflags to support BHRB This commit is preparatory to the addition of Branch History Rolling Buffer (BHRB) functionality, which is being provided today starting with the P8 processor. BHRB uses several SPR register fields to control whether or not a branch instruction's address (and sometimes target address) should be recorded. Checking each of these fields with each branch instruction using jitted code would lead to a significant decrease in performance. Therefore, it was decided that BHRB configuration bits that are not expected to change frequently should have their state summarized in an hflag so that the amount of checking done by jitted code can be reduced. This commit contains the changes for summarizing the state of the following register fields in the HFLAGS_BHRB_ENABLE hflag: MMCR0[FCP] - Determines if BHRB recording is frozen in the problem state MMCR0[FCPC] - A modifier for MMCR0[FCP] MMCRA[BHRBRD] - Disables all BHRB recording for a thread Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 5 +++++ target/ppc/cpu_init.c | 4 ++-- target/ppc/helper.h | 1 + target/ppc/helper_regs.c | 37 ++++++++++++++++++++++++++++++++ target/ppc/machine.c | 2 +- target/ppc/power8-pmu-regs.c.inc | 5 +++++ target/ppc/power8-pmu.c | 15 +++++++++---- target/ppc/power8-pmu.h | 4 ++-- target/ppc/spr_common.h | 1 + target/ppc/translate.c | 2 ++ 10 files changed, 67 insertions(+), 9 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index a5f46d0b10..195d4be2b7 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -533,6 +533,8 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR0_FC56 PPC_BIT(59) /* PMC Freeze Counters 5-6 bit */ #define MMCR0_PMC1CE PPC_BIT(48) /* MMCR0 PMC1 Condition Enabled */ #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */ +#define MMCR0_FCP PPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */ +#define MMCR0_FCPC PPC_BIT(51) /* Condition for FCP bit */ /* MMCR0 userspace r/w mask */ #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) /* MMCR2 userspace r/w mask */ @@ -545,6 +547,8 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \ MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) +#define MMCRA_BHRBRD PPC_BIT(26) /* BHRB Recording Disable */ + #define MMCR1_EVT_SIZE 8 /* extract64() does a right shift before extracting */ #define MMCR1_PMC1SEL_START 32 @@ -797,6 +801,7 @@ enum { HFLAGS_PMCJCE = 17, /* MMCR0 PMCjCE bit */ HFLAGS_PMC_OTHER = 18, /* PMC other than PMC5-6 is enabled */ HFLAGS_INSN_CNT = 19, /* PMU instruction count enabled */ + HFLAGS_BHRB_ENABLE = 20, /* Summary flag for enabling BHRB */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 914c6e0f18..366ea8568b 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5152,7 +5152,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env) KVM_REG_PPC_MMCR1, 0x00000000); spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_MMCRA, KVM_REG_PPC_MMCRA, 0x00000000); spr_register_kvm(env, SPR_POWER_PMC1, "PMC1", SPR_NOACCESS, SPR_NOACCESS, @@ -7196,7 +7196,7 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) if (env->mmu_model != POWERPC_MMU_REAL) { ppc_tlb_invalidate_all(env); } - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); } /* clean any pending stop state */ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6d6f31366c..7b07cd1a49 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -30,6 +30,7 @@ DEF_HELPER_2(store_dawr0, void, env, tl) DEF_HELPER_2(store_dawrx0, void, env, tl) DEF_HELPER_2(store_mmcr0, void, env, tl) DEF_HELPER_2(store_mmcr1, void, env, tl) +DEF_HELPER_2(store_mmcrA, void, env, tl) DEF_HELPER_3(store_pmc, void, env, i32, i64) DEF_HELPER_2(read_pmc, tl, env, i32) DEF_HELPER_2(insns_inc, void, env, i32) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 9094ae5004..16b43702d5 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -47,6 +47,39 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) env->tgpr[3] = tmp; } +#if defined(TARGET_PPC64) +static bool hreg_check_bhrb_enable(CPUPPCState *env) +{ + bool pr = !!(env->msr & (1 << MSR_PR)); + target_long mmcr0; + bool fcp; + bool hv; + + /* ISA 3.1 adds the PMCRA[BRHBRD] and problem state checks */ + if ((env->insns_flags2 & PPC2_ISA310) && + ((env->spr[SPR_POWER_MMCRA] & MMCRA_BHRBRD) || !pr)) { + return false; + } + + /* Check for BHRB "frozen" conditions */ + mmcr0 = env->spr[SPR_POWER_MMCR0]; + fcp = !!(mmcr0 & MMCR0_FCP); + if (mmcr0 & MMCR0_FCPC) { + hv = !!(env->msr & (1ull << MSR_HV)); + if (fcp) { + if (hv && pr) { + return false; + } + } else if (!hv && pr) { + return false; + } + } else if (fcp && pr) { + return false; + } + return true; +} +#endif + static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) { uint32_t hflags = 0; @@ -61,6 +94,9 @@ static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { hflags |= 1 << HFLAGS_PMCJCE; } + if (hreg_check_bhrb_enable(env)) { + hflags |= 1 << HFLAGS_BHRB_ENABLE; + } #ifndef CONFIG_USER_ONLY if (env->pmc_ins_cnt) { @@ -85,6 +121,7 @@ static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) hflags_mask |= 1 << HFLAGS_PMCJCE; hflags_mask |= 1 << HFLAGS_INSN_CNT; hflags_mask |= 1 << HFLAGS_PMC_OTHER; + hflags_mask |= 1 << HFLAGS_BHRB_ENABLE; #endif return hflags_mask; } diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 203fe28e01..6b6c31d903 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -333,7 +333,7 @@ static int cpu_post_load(void *opaque, int version_id) * triggered types (including HDEC) would need to carry more state. */ cpu_ppc_store_decr(env, env->spr[SPR_DECR]); - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); } return 0; diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index 4956a8b350..652cf20704 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -175,6 +175,11 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) gen_store_spr(SPR_POWER_MMCR2, masked_gprn); } +void spr_write_MMCRA(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_mmcrA(tcg_env, cpu_gpr[gprn]); +} + void spr_read_PMC(DisasContext *ctx, int gprn, int sprn) { TCGv_i32 t_sprn = tcg_constant_i32(sprn); diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index cbc5889d91..6f5d4e1256 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -82,7 +82,7 @@ static void pmu_update_summaries(CPUPPCState *env) env->pmc_cyc_cnt = cyc_cnt; } -void pmu_mmcr01_updated(CPUPPCState *env) +void pmu_mmcr01a_updated(CPUPPCState *env) { PowerPCCPU *cpu = env_archcpu(env); @@ -260,7 +260,7 @@ void helper_store_mmcr0(CPUPPCState *env, target_ulong value) env->spr[SPR_POWER_MMCR0] = value; - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); /* Update cycle overflow timers with the current MMCR0 state */ pmu_update_overflow_timers(env); @@ -272,7 +272,14 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) env->spr[SPR_POWER_MMCR1] = value; - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); +} + +void helper_store_mmcrA(CPUPPCState *env, uint64_t value) +{ + env->spr[SPR_POWER_MMCRA] = value; + + pmu_mmcr01a_updated(env); } target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) @@ -301,7 +308,7 @@ static void perfm_alert(PowerPCCPU *cpu) env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; /* Changing MMCR0_FC requires summaries and hflags update */ - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); /* * Delete all pending timers if we need to freeze diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index 775e640053..87fa8c9334 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -18,10 +18,10 @@ #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL void cpu_ppc_pmu_init(CPUPPCState *env); -void pmu_mmcr01_updated(CPUPPCState *env); +void pmu_mmcr01a_updated(CPUPPCState *env); #else static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } -static inline void pmu_mmcr01_updated(CPUPPCState *env) { } +static inline void pmu_mmcr01a_updated(CPUPPCState *env) { } #endif #endif diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 8a9d6cd994..eb2561f593 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -85,6 +85,7 @@ void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCRA(DisasContext *ctx, int sprn, int gprn); void spr_write_PMC(DisasContext *ctx, int sprn, int gprn); void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn); void spr_read_xer(DisasContext *ctx, int gprn, int sprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 2c39605273..a85f596d65 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -193,6 +193,7 @@ struct DisasContext { bool mmcr0_pmcjce; bool pmc_other; bool pmu_insn_cnt; + bool bhrb_enable; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -6345,6 +6346,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->mmcr0_pmcjce = (hflags >> HFLAGS_PMCJCE) & 1; ctx->pmc_other = (hflags >> HFLAGS_PMC_OTHER) & 1; ctx->pmu_insn_cnt = (hflags >> HFLAGS_INSN_CNT) & 1; + ctx->bhrb_enable = (hflags >> HFLAGS_BHRB_ENABLE) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { From 4de4a4705f234861176b32292374021ee96e004e Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:33 +1000 Subject: [PATCH 0845/2884] target/ppc: Add recording of taken branches to BHRB This commit continues adding support for the Branch History Rolling Buffer (BHRB) as is provided starting with the P8 processor and continuing with its successors. This commit is limited to the recording and filtering of taken branches. The following changes were made: - Enabled functionality on P10 processors only due to performance impact seen with P8 and P9 where it is not disabled for non problem state branches. - Added a BHRB buffer for storing branch instruction and target addresses for taken branches - Renamed gen_update_cfar to gen_update_branch_history and added a 'target' parameter to hold the branch target address and 'inst_type' parameter to use for filtering - Added TCG code to gen_update_branch_history that stores data to the BHRB and updates the BHRB offset. - Added BHRB resource initialization and reset functions Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 17 +++++ target/ppc/cpu_init.c | 37 +++++++++- target/ppc/power8-pmu.c | 33 +++++++++ target/ppc/power8-pmu.h | 7 ++ target/ppc/translate.c | 97 ++++++++++++++++++++++++-- target/ppc/translate/branch-impl.c.inc | 2 +- 6 files changed, 185 insertions(+), 8 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 195d4be2b7..2f91d7dc33 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -548,6 +548,8 @@ FIELD(MSR, LE, MSR_LE, 1) MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) #define MMCRA_BHRBRD PPC_BIT(26) /* BHRB Recording Disable */ +#define MMCRA_IFM_MASK PPC_BITMASK(32, 33) /* BHRB Instruction Filtering */ +#define MMCRA_IFM_SHIFT PPC_BIT_NR(33) #define MMCR1_EVT_SIZE 8 /* extract64() does a right shift before extracting */ @@ -774,6 +776,8 @@ enum { POWERPC_FLAG_SMT = 0x00400000, /* Using "LPAR per core" mode (as opposed to per-thread) */ POWERPC_FLAG_SMT_1LPAR = 0x00800000, + /* Has BHRB */ + POWERPC_FLAG_BHRB = 0x01000000, }; /* @@ -1215,6 +1219,9 @@ struct pnv_tod_tbst { #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 +#define BHRB_MAX_NUM_ENTRIES_LOG2 (5) +#define BHRB_MAX_NUM_ENTRIES (1 << BHRB_MAX_NUM_ENTRIES_LOG2) + struct CPUArchState { /* Most commonly used resources during translated code execution first */ target_ulong gpr[32]; /* general purpose registers */ @@ -1311,6 +1318,16 @@ struct CPUArchState { int dcache_line_size; int icache_line_size; +#ifdef TARGET_PPC64 + /* Branch History Rolling Buffer (BHRB) resources */ + target_ulong bhrb_num_entries; + intptr_t bhrb_base; + target_ulong bhrb_filter; + target_ulong bhrb_offset; + target_ulong bhrb_offset_mask; + uint64_t bhrb[BHRB_MAX_NUM_ENTRIES]; +#endif + /* These resources are used during exception processing */ /* CPU model definition */ target_ulong msr_mask; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 366ea8568b..1ec84b5ddc 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6142,6 +6142,28 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } +static void bhrb_init_state(CPUPPCState *env, target_long num_entries_log2) +{ + if (env->flags & POWERPC_FLAG_BHRB) { + if (num_entries_log2 > BHRB_MAX_NUM_ENTRIES_LOG2) { + num_entries_log2 = BHRB_MAX_NUM_ENTRIES_LOG2; + } + env->bhrb_num_entries = 1 << num_entries_log2; + env->bhrb_base = (intptr_t)&env->bhrb[0]; + env->bhrb_offset_mask = (env->bhrb_num_entries * sizeof(uint64_t)) - 1; + } +} + +static void bhrb_reset_state(CPUPPCState *env) +{ + if (env->flags & POWERPC_FLAG_BHRB) { + env->bhrb_offset = 0; + env->bhrb_filter = 0; + memset(env->bhrb, 0, sizeof(env->bhrb)); + } +} + +#define POWER8_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER8(CPUPPCState *env) { /* Common Registers */ @@ -6183,6 +6205,8 @@ static void init_proc_POWER8(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER8_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER8(env); ppcPOWER7_irq_init(env_archcpu(env)); @@ -6307,6 +6331,7 @@ static struct ppc_radix_page_info POWER9_radix_page_info = { }; #endif /* CONFIG_USER_ONLY */ +#define POWER9_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER9(CPUPPCState *env) { /* Common Registers */ @@ -6357,6 +6382,8 @@ static void init_proc_POWER9(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER9_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER9(env); ppcPOWER9_irq_init(env_archcpu(env)); @@ -6497,6 +6524,7 @@ static struct ppc_radix_page_info POWER10_radix_page_info = { }; #endif /* !CONFIG_USER_ONLY */ +#define POWER10_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER10(CPUPPCState *env) { /* Common Registers */ @@ -6546,6 +6574,8 @@ static void init_proc_POWER10(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER10_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER10(env); ppcPOWER9_irq_init(env_archcpu(env)); @@ -6650,7 +6680,8 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | POWERPC_FLAG_BE | POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | - POWERPC_FLAG_VSX | POWERPC_FLAG_SCV; + POWERPC_FLAG_VSX | POWERPC_FLAG_SCV | + POWERPC_FLAG_BHRB; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; } @@ -7222,6 +7253,10 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) } env->spr[i] = spr->default_value; } + +#if defined(TARGET_PPC64) + bhrb_reset_state(env); +#endif } #ifndef CONFIG_USER_ONLY diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index 6f5d4e1256..db9ee8e96b 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -82,6 +82,37 @@ static void pmu_update_summaries(CPUPPCState *env) env->pmc_cyc_cnt = cyc_cnt; } +static void hreg_bhrb_filter_update(CPUPPCState *env) +{ + target_long ifm; + + if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) { + /* disable recording to BHRB */ + env->bhrb_filter = BHRB_TYPE_NORECORD; + return; + } + + ifm = (env->spr[SPR_POWER_MMCRA] & MMCRA_IFM_MASK) >> MMCRA_IFM_SHIFT; + switch (ifm) { + case 0: + /* record all branches */ + env->bhrb_filter = -1; + break; + case 1: + /* only record calls (LK = 1) */ + env->bhrb_filter = BHRB_TYPE_CALL; + break; + case 2: + /* only record indirect branches */ + env->bhrb_filter = BHRB_TYPE_INDIRECT; + break; + case 3: + /* only record conditional branches */ + env->bhrb_filter = BHRB_TYPE_COND; + break; + } +} + void pmu_mmcr01a_updated(CPUPPCState *env) { PowerPCCPU *cpu = env_archcpu(env); @@ -95,6 +126,8 @@ void pmu_mmcr01a_updated(CPUPPCState *env) ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 0); } + hreg_bhrb_filter_update(env); + /* * Should this update overflow timers (if mmcr0 is updated) so they * get set in cpu_post_load? diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index 87fa8c9334..3f79cfc45b 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -13,6 +13,13 @@ #ifndef POWER8_PMU_H #define POWER8_PMU_H +#define BHRB_TYPE_NORECORD 0x00 +#define BHRB_TYPE_CALL 0x01 +#define BHRB_TYPE_INDIRECT 0x02 +#define BHRB_TYPE_COND 0x04 +#define BHRB_TYPE_OTHER 0x08 +#define BHRB_TYPE_XL_FORM 0x10 + #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a85f596d65..8aa2439700 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -180,6 +180,7 @@ struct DisasContext { #if defined(TARGET_PPC64) bool sf_mode; bool has_cfar; + bool has_bhrb; #endif bool fpu_enabled; bool altivec_enabled; @@ -3371,14 +3372,85 @@ static void gen_rvwinkle(DisasContext *ctx) gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ } + +static inline TCGv gen_write_bhrb(TCGv_ptr base, TCGv offset, TCGv mask, TCGv value) +{ + TCGv_ptr tmp = tcg_temp_new_ptr(); + + /* add base and offset to get address of bhrb entry */ + tcg_gen_add_ptr(tmp, base, (TCGv_ptr)offset); + + /* store value into bhrb at bhrb_offset */ + tcg_gen_st_i64(value, tmp, 0); + + /* add 8 to current bhrb_offset */ + tcg_gen_addi_tl(offset, offset, 8); + + /* apply offset mask */ + tcg_gen_and_tl(offset, offset, mask); + + return offset; +} #endif /* #if defined(TARGET_PPC64) */ -static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) +static inline void gen_update_branch_history(DisasContext *ctx, + target_ulong nip, + TCGv target, + target_long inst_type) { #if defined(TARGET_PPC64) + TCGv_ptr base; + TCGv tmp; + TCGv offset; + TCGv mask; + TCGLabel *no_update; + if (ctx->has_cfar) { tcg_gen_movi_tl(cpu_cfar, nip); } + + if (!ctx->has_bhrb || + !ctx->bhrb_enable || + inst_type == BHRB_TYPE_NORECORD) { + return; + } + + tmp = tcg_temp_new(); + no_update = gen_new_label(); + + /* check for bhrb filtering */ + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUPPCState, bhrb_filter)); + tcg_gen_andi_tl(tmp, tmp, inst_type); + tcg_gen_brcondi_tl(TCG_COND_EQ, tmp, 0, no_update); + + base = tcg_temp_new_ptr(); + offset = tcg_temp_new(); + mask = tcg_temp_new(); + + /* load bhrb base address */ + tcg_gen_ld_ptr(base, tcg_env, offsetof(CPUPPCState, bhrb_base)); + + /* load current bhrb_offset */ + tcg_gen_ld_tl(offset, tcg_env, offsetof(CPUPPCState, bhrb_offset)); + + /* load a BHRB offset mask */ + tcg_gen_ld_tl(mask, tcg_env, offsetof(CPUPPCState, bhrb_offset_mask)); + + offset = gen_write_bhrb(base, offset, mask, tcg_constant_i64(nip)); + + /* Also record the target address for XL-Form branches */ + if (inst_type & BHRB_TYPE_XL_FORM) { + + /* Set the 'T' bit for target entries */ + tcg_gen_ori_tl(tmp, target, 0x2); + + offset = gen_write_bhrb(base, offset, mask, tmp); + } + + /* save updated bhrb_offset for next time */ + tcg_gen_st_tl(offset, tcg_env, offsetof(CPUPPCState, bhrb_offset)); + + gen_set_label(no_update); #endif } @@ -3508,8 +3580,10 @@ static void gen_b(DisasContext *ctx) } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_CALL); + } else { + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_OTHER); } - gen_update_cfar(ctx, ctx->cia); gen_goto_tb(ctx, 0, target); ctx->base.is_jmp = DISAS_NORETURN; } @@ -3524,6 +3598,7 @@ static void gen_bcond(DisasContext *ctx, int type) uint32_t bo = BO(ctx->opcode); TCGLabel *l1; TCGv target; + target_long bhrb_type = BHRB_TYPE_OTHER; if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_new(); @@ -3534,11 +3609,16 @@ static void gen_bcond(DisasContext *ctx, int type) } else { tcg_gen_mov_tl(target, cpu_lr); } + if (!LK(ctx->opcode)) { + bhrb_type |= BHRB_TYPE_INDIRECT; + } + bhrb_type |= BHRB_TYPE_XL_FORM; } else { target = NULL; } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); + bhrb_type |= BHRB_TYPE_CALL; } l1 = gen_new_label(); if ((bo & 0x4) == 0) { @@ -3589,6 +3669,7 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); } } + bhrb_type |= BHRB_TYPE_COND; } if ((bo & 0x10) == 0) { /* Test CR */ @@ -3603,8 +3684,11 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask); tcg_gen_brcondi_i32(TCG_COND_NE, temp, 0, l1); } + bhrb_type |= BHRB_TYPE_COND; } - gen_update_cfar(ctx, ctx->cia); + + gen_update_branch_history(ctx, ctx->cia, target, bhrb_type); + if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -3720,7 +3804,7 @@ static void gen_rfi(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfi(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -3735,7 +3819,7 @@ static void gen_rfid(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -3750,7 +3834,7 @@ static void gen_rfscv(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfscv(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -6330,6 +6414,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #if defined(TARGET_PPC64) ctx->sf_mode = (hflags >> HFLAGS_64) & 1; ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); + ctx->has_bhrb = !!(env->flags & POWERPC_FLAG_BHRB); #endif ctx->lazy_tlb_flush = env->mmu_model == POWERPC_MMU_32B || env->mmu_model & POWERPC_MMU_64; diff --git a/target/ppc/translate/branch-impl.c.inc b/target/ppc/translate/branch-impl.c.inc index fb0fcf30cc..9ade0c659a 100644 --- a/target/ppc/translate/branch-impl.c.inc +++ b/target/ppc/translate/branch-impl.c.inc @@ -17,7 +17,7 @@ static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg) REQUIRE_INSNS_FLAGS2(ctx, ISA207S); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfebb(tcg_env, cpu_gpr[arg->s]); ctx->base.is_jmp = DISAS_CHAIN; From 6bfcf1dc239dba752307f0312e8a320470c26655 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:35 +1000 Subject: [PATCH 0846/2884] target/ppc: Add clrbhrb and mfbhrbe instructions Add support for the clrbhrb and mfbhrbe instructions. Since neither instruction is believed to be critical to performance, both instructions were implemented using helper functions. Access to both instructions is controlled by bits in the HFSCR (for privileged state) and MMCR0 (for problem state). A new function, helper_mmcr0_facility_check, was added for checking MMCR0[BHRBA] and raising a facility_unavailable exception if required. NOTE: For P8 and P9, due to a performance issue, branch history will not be kept, but the instructions will be allowed to execute as normal with the exception that the mfbhrbe instruction will always return a zero value. Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 2 ++ target/ppc/helper.h | 7 ++++ target/ppc/insn32.decode | 8 +++++ target/ppc/misc_helper.c | 50 ++++++++++++++++++++++++++++ target/ppc/translate.c | 2 ++ target/ppc/translate/bhrb-impl.c.inc | 43 ++++++++++++++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 target/ppc/translate/bhrb-impl.c.inc diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2f91d7dc33..c358927211 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -535,6 +535,7 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */ #define MMCR0_FCP PPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */ #define MMCR0_FCPC PPC_BIT(51) /* Condition for FCP bit */ +#define MMCR0_BHRBA_NR PPC_BIT_NR(42) /* BHRB Available */ /* MMCR0 userspace r/w mask */ #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) /* MMCR2 userspace r/w mask */ @@ -634,6 +635,7 @@ FIELD(MSR, LE, MSR_LE, 1) /* HFSCR bits */ #define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */ +#define HFSCR_BHRB PPC_BIT(59) /* BHRB Instructions */ #define HFSCR_IC_MSGP 0xA #define DBCR0_ICMP (1 << 27) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 7b07cd1a49..55293e20a9 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -820,3 +820,10 @@ DEF_HELPER_4(DSCLIQ, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) + +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +DEF_HELPER_1(clrbhrb, void, env) +DEF_HELPER_FLAGS_2(mfbhrbe, TCG_CALL_NO_WG, i64, env, i32) +#endif +#endif diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 05c1d8c12d..d4dd022df4 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1190,3 +1190,11 @@ MSGSYNC 011111 ----- ----- ----- 1101110110 - @X_sync ...... .. l:3 ... sc:2 ..... .......... . &X_sync SYNC 011111 -- ... --- .. ----- 1001010110 - @X_sync EIEIO 011111 ----- ----- ----- 1101010110 - + +# Branch History Rolling Buffer (BHRB) Instructions + +&XFX_bhrbe rt bhrbe +@XFX_bhrbe ...... rt:5 bhrbe:10 .......... - &XFX_bhrbe + +MFBHRBE 011111 ..... ..... ..... 0100101110 - @XFX_bhrbe +CLRBHRB 011111 ----- ----- ----- 0110101110 - diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 58e808dc96..6f419c9346 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -150,6 +150,17 @@ void helper_msr_facility_check(CPUPPCState *env, uint32_t bit, #if !defined(CONFIG_USER_ONLY) +#ifdef TARGET_PPC64 +static void helper_mmcr0_facility_check(CPUPPCState *env, uint32_t bit, + uint32_t sprn, uint32_t cause) +{ + if (FIELD_EX64(env->msr, MSR, PR) && + !(env->spr[SPR_POWER_MMCR0] & (1ULL << bit))) { + raise_fu_exception(env, bit, sprn, cause, GETPC()); + } +} +#endif + void helper_store_sdr1(CPUPPCState *env, target_ulong val) { if (env->spr[SPR_SDR1] != val) { @@ -363,3 +374,42 @@ void helper_fixup_thrm(CPUPPCState *env) env->spr[i] = v; } } + +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +void helper_clrbhrb(CPUPPCState *env) +{ + helper_hfscr_facility_check(env, HFSCR_BHRB, "clrbhrb", FSCR_IC_BHRB); + + helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB); + + if (env->flags & POWERPC_FLAG_BHRB) { + memset(env->bhrb, 0, sizeof(env->bhrb)); + } +} + +uint64_t helper_mfbhrbe(CPUPPCState *env, uint32_t bhrbe) +{ + unsigned int index; + + helper_hfscr_facility_check(env, HFSCR_BHRB, "mfbhrbe", FSCR_IC_BHRB); + + helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB); + + if (!(env->flags & POWERPC_FLAG_BHRB) || + (bhrbe >= env->bhrb_num_entries) || + (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) { + return 0; + } + + /* + * Note: bhrb_offset is the byte offset for writing the + * next entry (over the oldest entry), which is why we + * must offset bhrbe by 1 to get to the 0th entry. + */ + index = ((env->bhrb_offset / sizeof(uint64_t)) - (bhrbe + 1)) % + env->bhrb_num_entries; + return env->bhrb[index]; +} +#endif +#endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 8aa2439700..546f1754de 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5647,6 +5647,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/misc-impl.c.inc" +#include "translate/bhrb-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { diff --git a/target/ppc/translate/bhrb-impl.c.inc b/target/ppc/translate/bhrb-impl.c.inc new file mode 100644 index 0000000000..3a19bc4555 --- /dev/null +++ b/target/ppc/translate/bhrb-impl.c.inc @@ -0,0 +1,43 @@ +/* + * Power ISA Decode For BHRB Instructions + * + * Copyright IBM Corp. 2023 + * + * Authors: + * Glenn Miles + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +static bool trans_MFBHRBE(DisasContext *ctx, arg_XFX_bhrbe *arg) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + TCGv_i32 bhrbe = tcg_constant_i32(arg->bhrbe); + gen_helper_mfbhrbe(cpu_gpr[arg->rt], tcg_env, bhrbe); + return true; +} + +static bool trans_CLRBHRB(DisasContext *ctx, arg_CLRBHRB *arg) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + gen_helper_clrbhrb(tcg_env); + return true; +} + +#else + +static bool trans_MFBHRBE(DisasContext *ctx, arg_XFX_bhrbe *arg) +{ + gen_invalid(ctx); + return true; +} + +static bool trans_CLRBHRB(DisasContext *ctx, arg_CLRBHRB *arg) +{ + gen_invalid(ctx); + return true; +} +#endif From dabd6d3c3ae1ba52662d2056a8d59b01d9ee8e24 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:37 +1000 Subject: [PATCH 0847/2884] target/ppc: Add migration support for BHRB Adds migration support for Branch History Rolling Buffer (BHRB) internal state. Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/machine.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 6b6c31d903..731dd8df35 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -711,6 +711,26 @@ static const VMStateDescription vmstate_reservation = { } }; +#ifdef TARGET_PPC64 +static bool bhrb_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + return (cpu->env.flags & POWERPC_FLAG_BHRB) != 0; +} + +static const VMStateDescription vmstate_bhrb = { + .name = "cpu/bhrb", + .version_id = 1, + .minimum_version_id = 1, + .needed = bhrb_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.bhrb_offset, PowerPCCPU), + VMSTATE_UINT64_ARRAY(env.bhrb, PowerPCCPU, BHRB_MAX_NUM_ENTRIES), + VMSTATE_END_OF_LIST() + } +}; +#endif + const VMStateDescription vmstate_ppc_cpu = { .name = "cpu", .version_id = 5, @@ -756,6 +776,7 @@ const VMStateDescription vmstate_ppc_cpu = { #ifdef TARGET_PPC64 &vmstate_tm, &vmstate_slb, + &vmstate_bhrb, #endif /* TARGET_PPC64 */ &vmstate_tlb6xx, &vmstate_tlbemb, From 21cfc36a6c2190932ea55edbce241e6b6b7b9c8f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 10:56:50 +1000 Subject: [PATCH 0848/2884] target/ppc: larx/stcx generation need only apply DEF_MEMOP() once Use DEF_MEMOP() consistently in larx and stcx. generation, and apply it once when it's used rather than where the macros are expanded, to reduce typing. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/translate.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 546f1754de..6c103dafe0 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2849,7 +2849,7 @@ static void gen_load_locked(DisasContext *ctx, MemOp memop) gen_set_access_type(ctx, ACCESS_RES); gen_addr_reg_index(ctx, t0); - tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); + tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, DEF_MEMOP(memop) | MO_ALIGN); tcg_gen_mov_tl(cpu_reserve, t0); tcg_gen_movi_tl(cpu_reserve_length, memop_size(memop)); tcg_gen_mov_tl(cpu_reserve_val, gpr); @@ -2862,9 +2862,9 @@ static void gen_##name(DisasContext *ctx) \ } /* lwarx */ -LARX(lbarx, DEF_MEMOP(MO_UB)) -LARX(lharx, DEF_MEMOP(MO_UW)) -LARX(lwarx, DEF_MEMOP(MO_UL)) +LARX(lbarx, MO_UB) +LARX(lharx, MO_UW) +LARX(lwarx, MO_UL) static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, TCGv EA, TCGCond cond, int addend) @@ -3109,15 +3109,15 @@ static void gen_##name(DisasContext *ctx) \ gen_conditional_store(ctx, memop); \ } -STCX(stbcx_, DEF_MEMOP(MO_UB)) -STCX(sthcx_, DEF_MEMOP(MO_UW)) -STCX(stwcx_, DEF_MEMOP(MO_UL)) +STCX(stbcx_, MO_UB) +STCX(sthcx_, MO_UW) +STCX(stwcx_, MO_UL) #if defined(TARGET_PPC64) /* ldarx */ -LARX(ldarx, DEF_MEMOP(MO_UQ)) +LARX(ldarx, MO_UQ) /* stdcx. */ -STCX(stdcx_, DEF_MEMOP(MO_UQ)) +STCX(stdcx_, MO_UQ) /* lqarx */ static void gen_lqarx(DisasContext *ctx) From c10c6ce032959b8b4ada8b8d755a10cad7b5f1a7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 10:58:51 +1000 Subject: [PATCH 0849/2884] target/ppc: Remove redundant MEMOP_GET_SIZE macro There is a memop_size() function for this. Reviewed-by: BALATON Zoltan Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/translate.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 6c103dafe0..cf42dfcc9d 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2840,8 +2840,6 @@ static void gen_isync(DisasContext *ctx) ctx->base.is_jmp = DISAS_EXIT_UPDATE; } -#define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) - static void gen_load_locked(DisasContext *ctx, MemOp memop) { TCGv gpr = cpu_gpr[rD(ctx->opcode)]; @@ -2874,7 +2872,7 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, TCGv u = tcg_temp_new(); tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); - tcg_gen_addi_tl(t2, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_addi_tl(t2, EA, memop_size(memop)); tcg_gen_qemu_ld_tl(t2, t2, ctx->mem_idx, memop); tcg_gen_addi_tl(u, t, addend); @@ -2884,7 +2882,7 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, tcg_gen_qemu_st_tl(u, EA, ctx->mem_idx, memop); /* RT = (t != t2 ? t : u = 1<<(s*8-1)) */ - tcg_gen_movi_tl(u, 1 << (MEMOP_GET_SIZE(memop) * 8 - 1)); + tcg_gen_movi_tl(u, 1 << (memop_size(memop) * 8 - 1)); tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, u); } @@ -3046,7 +3044,7 @@ static void gen_st_atomic(DisasContext *ctx, MemOp memop) TCGv ea_plus_s = tcg_temp_new(); tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); - tcg_gen_addi_tl(ea_plus_s, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_addi_tl(ea_plus_s, EA, memop_size(memop)); tcg_gen_qemu_ld_tl(t2, ea_plus_s, ctx->mem_idx, memop); tcg_gen_movcond_tl(TCG_COND_EQ, s, t, t2, src, t); tcg_gen_movcond_tl(TCG_COND_EQ, s2, t, t2, src, t2); From cce7aee8dd934dca3040eb8ea1ad1973701d7c96 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 Jun 2023 03:02:15 +1000 Subject: [PATCH 0850/2884] target/ppc: Make checkstop actually stop the system checkstop state does not halt the system, interrupts continue to be serviced, and other CPUs run. Make it stop the machine with qemu_system_guest_panicked. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 2e3f36a3ef..fd00c044b5 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,6 +19,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "sysemu/sysemu.h" +#include "sysemu/runstate.h" #include "cpu.h" #include "exec/exec-all.h" #include "internal.h" @@ -425,6 +427,8 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, static void powerpc_mcheck_checkstop(CPUPPCState *env) { + /* KVM guests always have MSR[ME] enabled */ +#ifdef CONFIG_TCG CPUState *cs = env_cpu(env); if (FIELD_EX64(env->msr, MSR, ME)) { @@ -437,9 +441,15 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env) if (qemu_log_separate()) { qemu_log("Machine check while not allowed. " "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); + + /* + * This stops the machine and logs CPU state without killing QEMU + * (like cpu_abort()) so the machine can still be debugged (because + * it is often a guest error). + */ + qemu_system_guest_panicked(NULL); + cpu_loop_exit_noexc(cs); +#endif } static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) From 9728fb5c22a51b5f2f65ba36fdcd8fd0999be6fc Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 12 Mar 2024 04:33:18 +1000 Subject: [PATCH 0851/2884] target/ppc: improve checkstop logging Change the logging not to print to stderr as well, because a checkstop is a guest error (or perhaps a simulated machine error) rather than a QEMU error, so send it to the log. Update the checkstop message, and log CPU registers too. Reviewed-by: Richard Henderson Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index fd00c044b5..a283c97717 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -430,17 +430,19 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env) /* KVM guests always have MSR[ME] enabled */ #ifdef CONFIG_TCG CPUState *cs = env_cpu(env); + FILE *f; if (FIELD_EX64(env->msr, MSR, ME)) { return; } - /* Machine check exception is not enabled. Enter checkstop state. */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); + f = qemu_log_trylock(); + if (f) { + fprintf(f, "Entering checkstop state: " + "machine check with MSR[ME]=0\n"); + cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_unlock(f); + } /* * This stops the machine and logs CPU state without killing QEMU From 45693f94dd11f26b673541c0c9a270f8462d8633 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 18 Jun 2023 19:39:13 +1000 Subject: [PATCH 0852/2884] target/ppc: Implement attn instruction on BookS 64-bit processors attn is an implementation-specific instruction that on POWER (and G5/ 970) can be enabled with a HID bit (disabled = illegal), and executing it causes the host processor to stop and the service processor to be notified. Generally used for debugging. Implement attn and make it checkstop the system, which should be good enough for QEMU debugging. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 12 +++++ target/ppc/cpu_init.c | 69 ++++++++++++++++++++++++++++ target/ppc/excp_helper.c | 43 +++++++++++++---- target/ppc/helper.h | 1 + target/ppc/insn32.decode | 4 ++ target/ppc/translate/misc-impl.c.inc | 10 ++++ 6 files changed, 130 insertions(+), 9 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c358927211..2532408be0 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1375,6 +1375,9 @@ struct CPUArchState { /* Power management */ int (*check_pow)(CPUPPCState *env); + /* attn instruction enable */ + int (*check_attn)(CPUPPCState *env); + #if !defined(CONFIG_USER_ONLY) void *load_info; /* holds boot loading state */ #endif @@ -1523,6 +1526,7 @@ struct PowerPCCPUClass { int n_host_threads; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); + int (*check_attn)(CPUPPCState *env); }; ObjectClass *ppc_cpu_class_by_name(const char *name); @@ -2320,6 +2324,8 @@ void ppc_compat_add_property(Object *obj, const char *name, #define HID0_NAP (1 << 22) /* pre-2.06 */ #define HID0_HILE PPC_BIT(19) /* POWER8 */ #define HID0_POWER9_HILE PPC_BIT(4) +#define HID0_ENABLE_ATTN PPC_BIT(31) /* POWER8 */ +#define HID0_POWER9_ENABLE_ATTN PPC_BIT(3) /*****************************************************************************/ /* PowerPC Instructions types definitions */ @@ -3025,6 +3031,12 @@ static inline int check_pow_nocheck(CPUPPCState *env) return 1; } +/* attn enable check */ +static inline int check_attn_none(CPUPPCState *env) +{ + return 0; +} + /*****************************************************************************/ /* PowerPC implementations definitions */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1ec84b5ddc..ee01415c32 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2107,6 +2107,26 @@ static int check_pow_hid0_74xx(CPUPPCState *env) return 0; } +#if defined(TARGET_PPC64) +static int check_attn_hid0(CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & HID0_ENABLE_ATTN) { + return 1; + } + + return 0; +} + +static int check_attn_hid0_power9(CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & HID0_POWER9_ENABLE_ATTN) { + return 1; + } + + return 0; +} +#endif + static void init_proc_405(CPUPPCState *env) { register_40x_sprs(env); @@ -2138,6 +2158,7 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) dc->desc = "PowerPC 405"; pcc->init_proc = init_proc_405; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_DCR | PPC_WRTEE | PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | @@ -2210,6 +2231,7 @@ POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440 EP"; pcc->init_proc = init_proc_440EP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -2248,6 +2270,7 @@ POWERPC_FAMILY(460EX)(ObjectClass *oc, void *data) dc->desc = "PowerPC 460 EX"; pcc->init_proc = init_proc_440EP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -2308,6 +2331,7 @@ POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440 GP"; pcc->init_proc = init_proc_440GP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | PPC_CACHE | PPC_CACHE_ICBI | @@ -2382,6 +2406,7 @@ POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440x5"; pcc->init_proc = init_proc_440x5; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_DCR | PPC_WRTEE | PPC_RFMCI | PPC_CACHE | PPC_CACHE_ICBI | @@ -2417,6 +2442,7 @@ POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440x5 with double precision FPU"; pcc->init_proc = init_proc_440x5; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FSQRT | PPC_FLOAT_STFIWX | @@ -2465,6 +2491,7 @@ POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) dc->desc = "Freescale 5xx cores (aka RCPU)"; pcc->init_proc = init_proc_MPC5xx; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MEM_EIEIO | PPC_MEM_SYNC | PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | @@ -2507,6 +2534,7 @@ POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) dc->desc = "Freescale 8xx cores (aka PowerQUICC)"; pcc->init_proc = init_proc_MPC8xx; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MEM_EIEIO | PPC_MEM_SYNC | PPC_CACHE_ICBI | PPC_MFTB; @@ -2557,6 +2585,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) dc->desc = "PowerPC G2"; pcc->init_proc = init_proc_G2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -2595,6 +2624,7 @@ POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) dc->desc = "PowerPC G2LE"; pcc->init_proc = init_proc_G2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -2741,6 +2771,7 @@ POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) dc->desc = "e200 core"; pcc->init_proc = init_proc_e200; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; /* * XXX: unimplemented instructions: * dcblc @@ -3029,6 +3060,7 @@ POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) dc->desc = "e500v1 core"; pcc->init_proc = init_proc_e500v1; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_SPE | PPC_SPE_SINGLE | PPC_WRTEE | PPC_RFDI | @@ -3072,6 +3104,7 @@ POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) dc->desc = "e500v2 core"; pcc->init_proc = init_proc_e500v2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | PPC_WRTEE | PPC_RFDI | @@ -3115,6 +3148,7 @@ POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) dc->desc = "e500mc core"; pcc->init_proc = init_proc_e500mc; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3161,6 +3195,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) dc->desc = "e5500 core"; pcc->init_proc = init_proc_e5500; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3209,6 +3244,7 @@ POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) dc->desc = "e6500 core"; pcc->init_proc = init_proc_e6500; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3271,6 +3307,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) dc->desc = "PowerPC 603"; pcc->init_proc = init_proc_603; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3310,6 +3347,7 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) dc->desc = "PowerPC 603e"; pcc->init_proc = init_proc_603; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3355,6 +3393,7 @@ POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) dc->desc = "e300 core"; pcc->init_proc = init_proc_e300; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -3410,6 +3449,7 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) dc->desc = "PowerPC 604"; pcc->init_proc = init_proc_604; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3455,6 +3495,7 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) dc->desc = "PowerPC 604E"; pcc->init_proc = init_proc_604E; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3511,6 +3552,7 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) dc->desc = "PowerPC 740"; pcc->init_proc = init_proc_740; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3576,6 +3618,7 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750"; pcc->init_proc = init_proc_750; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3722,6 +3765,7 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750 CL"; pcc->init_proc = init_proc_750cl; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; /* * XXX: not implemented: * cache lock instructions: @@ -3829,6 +3873,7 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750CX"; pcc->init_proc = init_proc_750cx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3901,6 +3946,7 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750FX"; pcc->init_proc = init_proc_750fx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3973,6 +4019,7 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750GX"; pcc->init_proc = init_proc_750gx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4032,6 +4079,7 @@ POWERPC_FAMILY(745)(ObjectClass *oc, void *data) dc->desc = "PowerPC 745"; pcc->init_proc = init_proc_745; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4077,6 +4125,7 @@ POWERPC_FAMILY(755)(ObjectClass *oc, void *data) dc->desc = "PowerPC 755"; pcc->init_proc = init_proc_755; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4143,6 +4192,7 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7400 (aka G4)"; pcc->init_proc = init_proc_7400; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4222,6 +4272,7 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7410 (aka G4)"; pcc->init_proc = init_proc_7410; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4322,6 +4373,7 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7440 (aka G4)"; pcc->init_proc = init_proc_7440; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4444,6 +4496,7 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7450 (aka G4)"; pcc->init_proc = init_proc_7450; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4573,6 +4626,7 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7445 (aka G4)"; pcc->init_proc = init_proc_7445; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4704,6 +4758,7 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7455 (aka G4)"; pcc->init_proc = init_proc_7455; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4855,6 +4910,7 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7457 (aka G4)"; pcc->init_proc = init_proc_7457; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4989,6 +5045,7 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) dc->desc = "PowerPC e600"; pcc->init_proc = init_proc_e600; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5904,6 +5961,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) dc->desc = "PowerPC 970"; pcc->init_proc = init_proc_970; pcc->check_pow = check_pow_970; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5979,6 +6037,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) dc->desc = "POWER5+"; pcc->init_proc = init_proc_power5plus; pcc->check_pow = check_pow_970; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6086,6 +6145,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6247,6 +6307,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6439,6 +6500,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER9; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0_power9; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6618,6 +6680,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER10; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0_power9; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6856,6 +6919,11 @@ static void init_ppc_proc(PowerPCCPU *cpu) warn_report("no power management check handler registered." " Attempt QEMU to crash very soon !"); } + + if (env->check_attn == NULL) { + warn_report("no attn check handler registered." + " Attempt QEMU to crash very soon !"); + } } @@ -7317,6 +7385,7 @@ static void ppc_cpu_instance_init(Object *obj) env->flags = pcc->flags; env->bfd_mach = pcc->bfd_mach; env->check_pow = pcc->check_pow; + env->check_attn = pcc->check_attn; /* * Mark HV mode as supported if the CPU has an MSR_HV bit in the diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a283c97717..f48eb2eac8 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -154,6 +154,7 @@ static uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) return insn; } + #endif static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) @@ -425,21 +426,20 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, env->reserve_addr = -1; } -static void powerpc_mcheck_checkstop(CPUPPCState *env) -{ - /* KVM guests always have MSR[ME] enabled */ #ifdef CONFIG_TCG +/* + * This stops the machine and logs CPU state without killing QEMU (like + * cpu_abort()) because it is often a guest error as opposed to a QEMU error, + * so the machine can still be debugged. + */ +static G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason) +{ CPUState *cs = env_cpu(env); FILE *f; - if (FIELD_EX64(env->msr, MSR, ME)) { - return; - } - f = qemu_log_trylock(); if (f) { - fprintf(f, "Entering checkstop state: " - "machine check with MSR[ME]=0\n"); + fprintf(f, "Entering checkstop state: %s\n", reason); cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); qemu_log_unlock(f); } @@ -451,6 +451,31 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env) */ qemu_system_guest_panicked(NULL); cpu_loop_exit_noexc(cs); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void helper_attn(CPUPPCState *env) +{ + /* POWER attn is unprivileged when enabled by HID, otherwise illegal */ + if ((*env->check_attn)(env)) { + powerpc_checkstop(env, "host executed attn"); + } else { + raise_exception_err(env, POWERPC_EXCP_HV_EMU, + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); + } +} +#endif +#endif /* CONFIG_TCG */ + +static void powerpc_mcheck_checkstop(CPUPPCState *env) +{ + /* KVM guests always have MSR[ME] enabled */ +#ifdef CONFIG_TCG + if (FIELD_EX64(env->msr, MSR, ME)) { + return; + } + + powerpc_checkstop(env, "machine check with MSR[ME]=0"); #endif } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 55293e20a9..09d50f9b76 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -825,5 +825,6 @@ DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) #if defined(TARGET_PPC64) DEF_HELPER_1(clrbhrb, void, env) DEF_HELPER_FLAGS_2(mfbhrbe, TCG_CALL_NO_WG, i64, env, i32) +DEF_HELPER_1(attn, noreturn, env) #endif #endif diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index d4dd022df4..ee33141476 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1198,3 +1198,7 @@ EIEIO 011111 ----- ----- ----- 1101010110 - MFBHRBE 011111 ..... ..... ..... 0100101110 - @XFX_bhrbe CLRBHRB 011111 ----- ----- ----- 0110101110 - + +## Misc POWER instructions + +ATTN 000000 00000 00000 00000 0100000000 0 diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc index c1661d2f43..cbf82b1ea0 100644 --- a/target/ppc/translate/misc-impl.c.inc +++ b/target/ppc/translate/misc-impl.c.inc @@ -145,3 +145,13 @@ static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) return true; } + +static bool trans_ATTN(DisasContext *ctx, arg_ATTN *a) +{ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + gen_helper_attn(tcg_env); + return true; +#else + return false; +#endif +} From e89294b27e6fb4e976c08768a4a2ca0e8820a4ca Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 16 May 2023 01:28:35 +1000 Subject: [PATCH 0853/2884] target/ppc: BookE DECAR SPR is 32-bit The DECAR SPR is 32-bits width. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index ee01415c32..927721d49a 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -792,7 +792,7 @@ static void register_BookE_sprs(CPUPPCState *env, uint64_t ivor_mask) 0x00000000); spr_register(env, SPR_BOOKE_DECAR, "DECAR", SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic32, 0x00000000); /* SPRGs */ spr_register(env, SPR_USPRG0, "USPRG0", From 1cbcbcb8d6f10d742aa7cf6ad7bc768492e6407e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 11 Sep 2023 13:02:35 +1000 Subject: [PATCH 0854/2884] target/ppc: Add PPR32 SPR PPR32 provides access to the upper half of PPR. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 1 + target/ppc/cpu_init.c | 12 ++++++++++++ target/ppc/spr_common.h | 2 ++ target/ppc/translate.c | 24 ++++++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2532408be0..141cbefb4c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2120,6 +2120,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_POWER_MMCRS (0x37E) #define SPR_WORT (0x37F) #define SPR_PPR (0x380) +#define SPR_PPR32 (0x382) #define SPR_750_GQR0 (0x390) #define SPR_440_DNV0 (0x390) #define SPR_750_GQR1 (0x391) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 927721d49a..6baf7555a7 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5606,6 +5606,14 @@ static void register_HEIR64_spr(CPUPPCState *env) 0x00000000); } +static void register_power7_common_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_PPR32, "PPR32", + &spr_read_ppr32, &spr_write_ppr32, + &spr_read_ppr32, &spr_write_ppr32, + 0x00000000); +} + static void register_power8_tce_address_control_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_TAR, "TAR", @@ -6101,6 +6109,7 @@ static void init_proc_POWER7(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power7_book4_sprs(env); /* env variables */ @@ -6247,6 +6256,7 @@ static void init_proc_POWER8(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6414,6 +6424,7 @@ static void init_proc_POWER9(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6608,6 +6619,7 @@ static void init_proc_POWER10(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR64_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index eb2561f593..9e40b3b608 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -203,6 +203,8 @@ void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn); void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); +void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); #endif void register_low_BATs(CPUPPCState *env); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cf42dfcc9d..fb05047d95 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1352,6 +1352,30 @@ void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn) gen_load_spr(t0, sprn + 16); tcg_gen_ext32u_tl(cpu_gpr[gprn], t0); } + +/* The PPR32 SPR accesses the upper 32-bits of PPR */ +void spr_read_ppr32(DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], SPR_PPR); + tcg_gen_shri_tl(cpu_gpr[gprn], cpu_gpr[gprn], 32); + spr_load_dump_spr(SPR_PPR); +} + +void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + /* + * Don't clobber the low 32-bits of the PPR. These are all reserved bits + * but TCG does implement them, so it would be surprising to zero them + * here. "Priority nops" are similarly careful not to clobber reserved + * bits. + */ + gen_load_spr(t0, SPR_PPR); + tcg_gen_deposit_tl(t0, t0, cpu_gpr[gprn], 32, 32); + gen_store_spr(SPR_PPR, t0); + spr_store_dump_spr(SPR_PPR); +} #endif #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ From 5fa7efe4730586648412f59f13bd370c40f372ff Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 27 May 2023 03:17:38 +1000 Subject: [PATCH 0855/2884] target/ppc: add helper to write per-LPAR SPRs An SPR can be either per-thread, per-core, or per-LPAR. Per-LPAR means per-thread or per-core, depending on 1LPAR mode. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/spr_common.h | 2 ++ target/ppc/translate.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 9e40b3b608..85f73b860b 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -83,6 +83,8 @@ void spr_read_generic(DisasContext *ctx, int gprn, int sprn); void spr_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn); +void spr_core_write_generic32(DisasContext *ctx, int sprn, int gprn); +void spr_core_lpar_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCRA(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index fb05047d95..7b525020ef 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -473,6 +473,34 @@ void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn) spr_store_dump_spr(sprn); } +void spr_core_write_generic32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0; + + if (!(ctx->flags & POWERPC_FLAG_SMT)) { + spr_write_generic32(ctx, sprn, gprn); + return; + } + + if (!gen_serialize(ctx)) { + return; + } + + t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_helper_spr_core_write_generic(tcg_env, tcg_constant_i32(sprn), t0); + spr_store_dump_spr(sprn); +} + +void spr_core_lpar_write_generic(DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->flags & POWERPC_FLAG_SMT_1LPAR) { + spr_core_write_generic(ctx, sprn, gprn); + } else { + spr_write_generic(ctx, sprn, gprn); + } +} + static void spr_write_CTRL_ST(DisasContext *ctx, int sprn, int gprn) { /* This does not implement >1 thread */ From e5c2ac9dc1da8cfb7830a44cb4dcb94ba983c1ad Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Jul 2023 22:54:40 +1000 Subject: [PATCH 0856/2884] target/ppc: Add SMT support to simple SPRs AMOR, MMCRC, HRMOR, TSCR, HMEER, RPR SPRs are per-core or per-LPAR registers with simple (generic) implementations. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6baf7555a7..415cc7a4e2 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -246,7 +246,7 @@ static void register_amr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_AMOR, "AMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_lpar_write_generic, 0); #endif /* !CONFIG_USER_ONLY */ } @@ -5472,7 +5472,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_MMCRC, "MMCRC", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_MMCRH, "MMCRH", SPR_NOACCESS, SPR_NOACCESS, @@ -5512,7 +5512,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HRMOR, "HRMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000000); } @@ -5740,7 +5740,7 @@ static void register_power_common_book4_sprs(CPUPPCState *env) spr_register_hv(env, SPR_TSCR, "TSCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_HMER, "HMER", SPR_NOACCESS, SPR_NOACCESS, @@ -5750,7 +5750,7 @@ static void register_power_common_book4_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HMEER, "HMEER", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000000); spr_register_hv(env, SPR_TFMR, "TFMR", SPR_NOACCESS, SPR_NOACCESS, @@ -5826,7 +5826,7 @@ static void register_power8_rpr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_RPR, "RPR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000103070F1F3F); #endif } From 4d2b0ad32a593ac24757b66f64efe2fb84161345 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Jul 2023 23:02:00 +1000 Subject: [PATCH 0857/2884] target/ppc: Add SMT support to PTCR SPR PTCR is a per-core register. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/misc_helper.c | 16 ++++++++++++++-- target/ppc/translate.c | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 6f419c9346..a67930d031 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -173,6 +173,7 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) void helper_store_ptcr(CPUPPCState *env, target_ulong val) { if (env->spr[SPR_PTCR] != val) { + CPUState *cs = env_cpu(env); PowerPCCPU *cpu = env_archcpu(env); target_ulong ptcr_mask = PTCR_PATB | PTCR_PATS; target_ulong patbsize = val & PTCR_PATS; @@ -194,8 +195,19 @@ void helper_store_ptcr(CPUPPCState *env, target_ulong val) return; } - env->spr[SPR_PTCR] = val; - tlb_flush(env_cpu(env)); + if (cs->nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + env->spr[SPR_PTCR] = val; + tlb_flush(cs); + } else { + CPUState *ccs; + + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + CPUPPCState *cenv = &ccpu->env; + cenv->spr[SPR_PTCR] = val; + tlb_flush(ccs); + } + } } } diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 7b525020ef..64131bf15c 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -909,6 +909,10 @@ void spr_write_hior(DisasContext *ctx, int sprn, int gprn) } void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_store_ptcr(tcg_env, cpu_gpr[gprn]); } From c9d5aedf40cfa0d68a6a4c020d14f8e6116a369e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 22 May 2023 16:23:21 +1000 Subject: [PATCH 0858/2884] target/ppc: Implement LDBAR, TTR SPRs LDBAR, TTR are a Power-specific SPRs. These simple implementations are enough for IBM proprietary firmware for now. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 2 ++ target/ppc/cpu_init.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 141cbefb4c..823be85d03 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2098,6 +2098,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_DEXCR (0x33C) #define SPR_IC (0x350) #define SPR_VTB (0x351) +#define SPR_LDBAR (0x352) #define SPR_MMCRC (0x353) #define SPR_PSSCR (0x357) #define SPR_440_INV0 (0x370) @@ -2144,6 +2145,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_440_IVLIM (0x399) #define SPR_TSCR (0x399) #define SPR_750_DMAU (0x39A) +#define SPR_POWER_TTR (0x39A) #define SPR_750_DMAL (0x39B) #define SPR_440_RSTCFG (0x39B) #define SPR_BOOKE_DCDBTRL (0x39C) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 415cc7a4e2..d181adc5f1 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5767,6 +5767,16 @@ static void register_power_common_book4_sprs(CPUPPCState *env) &spr_access_nop, &spr_write_generic, &spr_access_nop, &spr_write_generic, 0x00000000); + spr_register_hv(env, SPR_LDBAR, "LDBAR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_lpar_write_generic, + 0x00000000); + spr_register_hv(env, SPR_POWER_TTR, "TTR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_write_generic, + 0x00000000); #endif } From 2736432ffc30b74fc72858854e62b62253b685ff Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 18 Jun 2023 19:37:07 +1000 Subject: [PATCH 0859/2884] target/ppc: Implement SPRC/SPRD SPRs This implements the POWER SPRC/SPRD SPRs, and SCRATCH0-7 registers that can be accessed via these indirect SPRs. SCRATCH registers only provide storage, but they are used by firmware for low level crash and progress data, so this implementation logs writes to the registers to help with analysis. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 7 +++-- target/ppc/cpu_init.c | 10 ++++++ target/ppc/helper.h | 3 ++ target/ppc/misc_helper.c | 66 ++++++++++++++++++++++++++++++++++++++++ target/ppc/spr_common.h | 3 ++ target/ppc/translate.c | 18 +++++++++++ 6 files changed, 105 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 823be85d03..e4c342b17d 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1264,6 +1264,9 @@ struct CPUArchState { ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ struct CPUBreakpoint *ciabr_breakpoint; struct CPUWatchpoint *dawr0_watchpoint; + + /* POWER CPU regs/state */ + target_ulong scratch[8]; /* SCRATCH registers (shared across core) */ #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ @@ -1806,9 +1809,9 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_SPRG2 (0x112) #define SPR_SPRG3 (0x113) #define SPR_SPRG4 (0x114) -#define SPR_SCOMC (0x114) +#define SPR_POWER_SPRC (0x114) #define SPR_SPRG5 (0x115) -#define SPR_SCOMD (0x115) +#define SPR_POWER_SPRD (0x115) #define SPR_SPRG6 (0x116) #define SPR_SPRG7 (0x117) #define SPR_ASR (0x118) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index d181adc5f1..8cfaee61d9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5777,6 +5777,16 @@ static void register_power_common_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_core_write_generic, 0x00000000); + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sprc, + 0x00000000); + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sprd, &spr_write_sprd, + 0x00000000); #endif } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 09d50f9b76..57bf8354e7 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -730,6 +730,9 @@ DEF_HELPER_2(book3s_msgsndp, void, env, tl) DEF_HELPER_2(book3s_msgclrp, void, env, tl) DEF_HELPER_1(load_tfmr, tl, env) DEF_HELPER_2(store_tfmr, void, env, tl) +DEF_HELPER_FLAGS_2(store_sprc, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_1(load_sprd, TCG_CALL_NO_RWG_SE, tl, env) +DEF_HELPER_FLAGS_2(store_sprd, TCG_CALL_NO_RWG, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index a67930d031..fa47be2298 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -307,6 +307,72 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) } bql_unlock(); } + +/* Indirect SCOM (SPRC/SPRD) access to SCRATCH0-7 are implemented. */ +void helper_store_sprc(CPUPPCState *env, target_ulong val) +{ + if (val & ~0x3f8ULL) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid SPRC register value " + TARGET_FMT_lx"\n", val); + return; + } + env->spr[SPR_POWER_SPRC] = val; +} + +target_ulong helper_load_sprd(CPUPPCState *env) +{ + target_ulong sprc = env->spr[SPR_POWER_SPRC]; + + switch (sprc & 0x3c0) { + case 0: /* SCRATCH0-7 */ + return env->scratch[(sprc >> 3) & 0x7]; + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } + return 0; +} + +static void do_store_scratch(CPUPPCState *env, int nr, target_ulong val) +{ + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + /* + * Log stores to SCRATCH, because some firmware uses these for debugging + * and logging, but they would normally be read by the BMC, which is + * not implemented in QEMU yet. This gives a way to get at the information. + * Could also dump these upon checkstop. + */ + qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); + + if (nr_threads == 1) { + env->scratch[nr] = val; + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cenv->scratch[nr] = val; + } +} + +void helper_store_sprd(CPUPPCState *env, target_ulong val) +{ + target_ulong sprc = env->spr[SPR_POWER_SPRC]; + + switch (sprc & 0x3c0) { + case 0: /* SCRATCH0-7 */ + do_store_scratch(env, (sprc >> 3) & 0x7, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } +} #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 85f73b860b..01aff449bc 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -207,6 +207,9 @@ void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn); +void spr_read_sprd(DisasContext *ctx, int sprn, int gprn); +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn); #endif void register_low_BATs(CPUPPCState *env); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 64131bf15c..0bc16d7251 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1301,6 +1301,24 @@ void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) gen_helper_store_tfmr(tcg_env, cpu_gpr[gprn]); } +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_sprc(tcg_env, cpu_gpr[gprn]); +} + +void spr_read_sprd(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_sprd(cpu_gpr[gprn], tcg_env); +} + +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn) +{ + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_store_sprd(tcg_env, cpu_gpr[gprn]); +} + void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); From 0dfe59fe77ed571f23aefb70a3a226c3e1779862 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 20:05:36 +1000 Subject: [PATCH 0860/2884] target/ppc: add SMT support to msgsnd broadcast msgsnd has a broadcast mode that sends hypervisor doorbells to all threads belonging to the same core as the target. A "subcore" mode sends to all or one thread depending on 1LPAR mode. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 6 +- target/ppc/excp_helper.c | 61 +++++++++++++------ target/ppc/helper.h | 2 +- .../ppc/translate/processor-ctrl-impl.c.inc | 2 +- 4 files changed, 48 insertions(+), 23 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index e4c342b17d..e201b7f6c2 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1163,7 +1163,11 @@ FIELD(FPSCR, FI, FPSCR_FI, 1) #define DBELL_TYPE_DBELL_SERVER (0x05 << DBELL_TYPE_SHIFT) -#define DBELL_BRDCAST PPC_BIT(37) +#define DBELL_BRDCAST_MASK PPC_BITMASK(37, 38) +#define DBELL_BRDCAST_SHIFT 25 +#define DBELL_BRDCAST_SUBPROC (0x1 << DBELL_BRDCAST_SHIFT) +#define DBELL_BRDCAST_CORE (0x2 << DBELL_BRDCAST_SHIFT) + #define DBELL_LPIDTAG_SHIFT 14 #define DBELL_LPIDTAG_MASK (0xfff << DBELL_LPIDTAG_SHIFT) #define DBELL_PIRTAG_MASK 0x3fff diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index f48eb2eac8..0cd542675f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2969,7 +2969,7 @@ void helper_msgsnd(target_ulong rb) PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *cenv = &cpu->env; - if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { + if ((rb & DBELL_BRDCAST_MASK) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { ppc_set_irq(cpu, irq, 1); } } @@ -2988,6 +2988,16 @@ static bool dbell_type_server(target_ulong rb) return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; } +static inline bool dbell_bcast_core(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_CORE; +} + +static inline bool dbell_bcast_subproc(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; +} + void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { if (!dbell_type_server(rb)) { @@ -2997,32 +3007,43 @@ void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); } -static void book3s_msgsnd_common(int pir, int irq) -{ - CPUState *cs; - - bql_lock(); - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *cenv = &cpu->env; - - /* TODO: broadcast message to all threads of the same processor */ - if (cenv->spr_cb[SPR_PIR].default_value == pir) { - ppc_set_irq(cpu, irq, 1); - } - } - bql_unlock(); -} - -void helper_book3s_msgsnd(target_ulong rb) +void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) { int pir = rb & DBELL_PROCIDTAG_MASK; + bool brdcast = false; + CPUState *cs, *ccs; + PowerPCCPU *cpu; if (!dbell_type_server(rb)) { return; } - book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); + cpu = ppc_get_vcpu_by_pir(pir); + if (!cpu) { + return; + } + cs = CPU(cpu); + + if (dbell_bcast_core(rb) || (dbell_bcast_subproc(rb) && + (env->flags & POWERPC_FLAG_SMT_1LPAR))) { + brdcast = true; + } + + if (cs->nr_threads == 1 || !brdcast) { + ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); + return; + } + + /* + * Why is bql needed for walking CPU list? Answer seems to be because ppc + * irq handling needs it, but ppc_set_irq takes the lock itself if needed, + * so could this be removed? + */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + ppc_set_irq(POWERPC_CPU(ccs), PPC_INTERRUPT_HDOORBELL, 1); + } + bql_unlock(); } #ifdef TARGET_PPC64 diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 57bf8354e7..dd92c6a937 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -695,7 +695,7 @@ DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_1(msgsnd, void, tl) DEF_HELPER_2(msgclr, void, env, tl) -DEF_HELPER_1(book3s_msgsnd, void, tl) +DEF_HELPER_2(book3s_msgsnd, void, env, tl) DEF_HELPER_2(book3s_msgclr, void, env, tl) #endif diff --git a/target/ppc/translate/processor-ctrl-impl.c.inc b/target/ppc/translate/processor-ctrl-impl.c.inc index 0142801985..8abbb89630 100644 --- a/target/ppc/translate/processor-ctrl-impl.c.inc +++ b/target/ppc/translate/processor-ctrl-impl.c.inc @@ -59,7 +59,7 @@ static bool trans_MSGSND(DisasContext *ctx, arg_X_rb *a) #if !defined(CONFIG_USER_ONLY) if (is_book3s_arch2x(ctx)) { - gen_helper_book3s_msgsnd(cpu_gpr[a->rb]); + gen_helper_book3s_msgsnd(tcg_env, cpu_gpr[a->rb]); } else { gen_helper_msgsnd(cpu_gpr[a->rb]); } From 41e9a098d175fdaeb025fe39be940940d1edd979 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 13 May 2024 01:27:33 +0200 Subject: [PATCH 0861/2884] target/ppc: Remove unused struct 'mmu_ctx_hash32' I think it's use was removed by Commit 5883d8b296 ("mmu-hash*: Don't use full ppc_hash{32, 64}_translate() path for get_phys_page_debug()") Reviewed-by: BALATON Zoltan Signed-off-by: Dr. David Alan Gilbert Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu-hash32.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 6dfedab11d..da6e8b293c 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -37,12 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -struct mmu_ctx_hash32 { - hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ - int key; /* Access key */ -}; - static int ppc_hash32_pp_prot(int key, int pp, int nx) { int prot; From 2b92822acc3e8cb7ce22c75a115b53219f0458b9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:34 +0200 Subject: [PATCH 0862/2884] target/ppc: Remove unused helper_rac() The helper_rac function is defined but not used, remove it. Fixes: 005b69fdcc (target/ppc: Remove PowerPC 601 CPUs) Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 2 -- target/ppc/mmu_helper.c | 24 ------------------------ 2 files changed, 26 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index dd92c6a937..76b8f25c77 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -701,8 +701,6 @@ DEF_HELPER_2(book3s_msgclr, void, env, tl) DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(rac, tl, env, tl) - DEF_HELPER_2(load_dcr, tl, env, tl) DEF_HELPER_3(store_dcr, void, env, tl, tl) #endif diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index d9d950e220..d4388e66be 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -596,30 +596,6 @@ void helper_6xx_tlbi(CPUPPCState *env, target_ulong EPN) do_6xx_tlb(env, EPN, 1); } -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ - -target_ulong helper_rac(CPUPPCState *env, target_ulong addr) -{ - mmu_ctx_t ctx; - int nb_BATs; - target_ulong ret = 0; - - /* - * We don't have to generate many instances of this instruction, - * as rac is supervisor only. - * - * XXX: FIX THIS: Pretend we have no BAT - */ - nb_BATs = env->nb_BATs; - env->nb_BATs = 0; - if (get_physical_address_wtlb(env, &ctx, addr, 0, ACCESS_INT, 0) == 0) { - ret = ctx.raddr; - } - env->nb_BATs = nb_BATs; - return ret; -} - static inline target_ulong booke_tlb_to_page_size(int size) { return 1024 << (2 * size); From 3f520078deba9b5aaf5f5896c5b836341f5fc652 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:35 +0200 Subject: [PATCH 0863/2884] target/ppc: Move calculation of a value closer to its usage in booke tlb checks In mmubooke_check_tlb() and mmubooke206_check_tlb() prot2 is calculated first but only used after an unrelated check that can return before tha value is used. Move the calculation after the check, closer to where it is used, to keep them together and avoid computing it when not needed. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 4fde7fd3bf..f79e390306 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -635,12 +635,6 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } - if (FIELD_EX64(env->msr, MSR, PR)) { - prot2 = tlb->prot & 0xF; - } else { - prot2 = (tlb->prot >> 4) & 0xF; - } - /* Check the address space */ if ((access_type == MMU_INST_FETCH ? FIELD_EX64(env->msr, MSR, IR) : @@ -649,6 +643,11 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } + if (FIELD_EX64(env->msr, MSR, PR)) { + prot2 = tlb->prot & 0xF; + } else { + prot2 = (tlb->prot >> 4) & 0xF; + } *prot = prot2; if (prot2 & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); @@ -830,6 +829,18 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, found_tlb: + /* Check the address space and permissions */ + if (access_type == MMU_INST_FETCH) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); + as = FIELD_EX64(env->msr, MSR, IR); + } + + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + if (pr) { if (tlb->mas7_3 & MAS3_UR) { prot2 |= PAGE_READ; @@ -851,19 +862,6 @@ found_tlb: prot2 |= PAGE_EXEC; } } - - /* Check the address space and permissions */ - if (access_type == MMU_INST_FETCH) { - /* There is no way to fetch code using epid load */ - assert(!use_epid); - as = FIELD_EX64(env->msr, MSR, IR); - } - - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - *prot = prot2; if (prot2 & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); From 750fbe334237e03056fda45ab36c8b155f65d1b5 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:36 +0200 Subject: [PATCH 0864/2884] target/ppc: Remove unneeded local variable from booke tlb checks In mmubooke_check_tlb() and mmubooke206_check_tlb() we can assign the value of prot2 directly to the destination, no need to have a separate local variable for it. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index f79e390306..09cbeb0052 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -628,8 +628,6 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddr, int *prot, target_ulong address, MMUAccessType access_type, int i) { - int prot2; - if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); return -1; @@ -644,17 +642,16 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, } if (FIELD_EX64(env->msr, MSR, PR)) { - prot2 = tlb->prot & 0xF; + *prot = tlb->prot & 0xF; } else { - prot2 = (tlb->prot >> 4) & 0xF; + *prot = (tlb->prot >> 4) & 0xF; } - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { + if (*prot & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2); + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); return access_type == MMU_INST_FETCH ? -3 : -2; } @@ -795,7 +792,6 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, target_ulong address, MMUAccessType access_type, int mmu_idx) { - int prot2 = 0; uint32_t epid; bool as, pr; bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); @@ -841,34 +837,34 @@ found_tlb: return -1; } + *prot = 0; if (pr) { if (tlb->mas7_3 & MAS3_UR) { - prot2 |= PAGE_READ; + *prot |= PAGE_READ; } if (tlb->mas7_3 & MAS3_UW) { - prot2 |= PAGE_WRITE; + *prot |= PAGE_WRITE; } if (tlb->mas7_3 & MAS3_UX) { - prot2 |= PAGE_EXEC; + *prot |= PAGE_EXEC; } } else { if (tlb->mas7_3 & MAS3_SR) { - prot2 |= PAGE_READ; + *prot |= PAGE_READ; } if (tlb->mas7_3 & MAS3_SW) { - prot2 |= PAGE_WRITE; + *prot |= PAGE_WRITE; } if (tlb->mas7_3 & MAS3_SX) { - prot2 |= PAGE_EXEC; + *prot |= PAGE_EXEC; } } - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { + if (*prot & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2); + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); return access_type == MMU_INST_FETCH ? -3 : -2; } From fef517cd8a3fb6baba15405448c4cb1dcd1b677a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:37 +0200 Subject: [PATCH 0865/2884] target/ppc: Simplify checking for real mode in get_physical_address_wtlb() In get_physical_address_wtlb() the real_mode flag depends on either the MSR[IR] or MSR[DR] bit depending on access_type. Extract just the needed bit in a more straight forward way instead of doing unnecessary computation. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 09cbeb0052..886fb6a657 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1184,8 +1184,10 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, int mmu_idx) { int ret = -1; - bool real_mode = (type == ACCESS_CODE && !FIELD_EX64(env->msr, MSR, IR)) || - (type != ACCESS_CODE && !FIELD_EX64(env->msr, MSR, DR)); + bool real_mode; + + real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR); switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: From cfd5c128320ab36ca5fa330a9e1968bf4d3cd2b6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:38 +0200 Subject: [PATCH 0866/2884] target/ppc: Drop cases for unimplemented MPC8xx MMU Drop MPC8xx cases from get_physical_address_wtlb() and ppc_jumbo_xlate(). The default case would still catch this and abort the same way and there is still a warning about it in ppc_tlb_invalidate_all() which is called in ppc_cpu_reset_hold() so likely we never get here but to make sure add a case to ppc_xlate() to the same effect. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 886fb6a657..3391df61cb 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1219,10 +1219,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, mmu_idx); break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env_cpu(env), "MPC8xx MMU model is not implemented\n"); - break; case POWERPC_MMU_REAL: if (real_mode) { ret = check_physical(env, ctx, eaddr, access_type); @@ -1353,8 +1349,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); break; - case POWERPC_MMU_MPC8xx: - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1427,9 +1421,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); case POWERPC_MMU_BOOKE206: booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); /* fall through */ @@ -1539,7 +1530,8 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_32B: return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); - + case POWERPC_MMU_MPC8xx: + cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); From 269d6f006b855266bb60b3e027a143ae1a654179 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:39 +0200 Subject: [PATCH 0867/2884] target/ppc: Introduce mmu6xx_get_physical_address() Repurpose get_segment_6xx_tlb() to do the whole address translation for POWERPC_MMU_SOFT_6xx MMU model by moving the BAT check there and renaming it to match other similar functions. These are only called once together so no need to keep these separate functions and combining them simplifies the caller allowing further restructuring. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 3391df61cb..b7c07cf515 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -360,19 +360,23 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -/* Perform segment based translation */ -static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type, - int type) +static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, + MMUAccessType access_type, int type) { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; - target_ulong vsid; + target_ulong vsid, sr, pgidx; int ds, target_page_bits; bool pr; int ret; - target_ulong sr, pgidx; + /* First try to find a BAT entry if there are any */ + if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { + return 0; + } + + /* Perform segment based translation when no BATs matched */ pr = FIELD_EX64(env->msr, MSR, PR); ctx->eaddr = eaddr; @@ -1194,14 +1198,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, if (real_mode) { ret = check_physical(env, ctx, eaddr, access_type); } else { - /* Try to find a BAT */ - if (env->nb_BATs != 0) { - ret = get_bat_6xx_tlb(env, ctx, eaddr, access_type); - } - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment_6xx_tlb(env, ctx, eaddr, access_type, type); - } + ret = mmu6xx_get_physical_address(env, ctx, eaddr, access_type, + type); } break; From f1418bdeb09d201ea636d061fa6edf1175074a09 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:40 +0200 Subject: [PATCH 0868/2884] target/ppc: Move else branch to avoid large if block in mmu6xx_get_physical_address() In mmu6xx_get_physical_address() we have a large if block with a two line else branch that effectively returns. Invert the condition and move the else there to allow deindenting the large if block to make the flow easier to follow. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 67 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index b7c07cf515..ba60b4902b 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -405,47 +405,44 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || ctx->nx == 0) { - /* Page address translation */ - qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx - " htab_mask " HWADDR_FMT_plx - " hash " HWADDR_FMT_plx "\n", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; + if (type == ACCESS_CODE && ctx->nx) { + qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); + return -3; + } + /* Page address translation */ + qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx " htab_mask " + HWADDR_FMT_plx " hash " HWADDR_FMT_plx "\n", + ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + /* Software TLB search */ + ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); #if defined(DUMP_PAGE_TABLES) - if (qemu_loglevel_mask(CPU_LOG_MMU)) { - CPUState *cs = env_cpu(env); - hwaddr curaddr; - uint32_t a0, a1, a2, a3; + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = env_cpu(env); + hwaddr curaddr; + uint32_t a0, a1, a2, a3; - qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx - "\n", ppc_hash32_hpt_base(cpu), - ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - curaddr < (ppc_hash32_hpt_base(cpu) - + ppc_hash32_hpt_mask(cpu) + 0x80); - curaddr += 16) { - a0 = ldl_phys(cs->as, curaddr); - a1 = ldl_phys(cs->as, curaddr + 4); - a2 = ldl_phys(cs->as, curaddr + 8); - a3 = ldl_phys(cs->as, curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } + qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", + ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu) + 0x80); + for (curaddr = ppc_hash32_hpt_base(cpu); + curaddr < (ppc_hash32_hpt_base(cpu) + + ppc_hash32_hpt_mask(cpu) + 0x80); + curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + a2 = ldl_phys(cs->as, curaddr + 8); + a3 = ldl_phys(cs->as, curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); } } -#endif - } else { - qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); - ret = -3; } +#endif } else { qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ From 0af20f35d254bc87689a9d5ab2c5e45a677467dc Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:41 +0200 Subject: [PATCH 0869/2884] target/ppc: Move some debug logging in ppc6xx_tlb_check() Move the debug logging within ppc6xx_tlb_check() from after its only call to simplify the caller. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 52 ++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ba60b4902b..89bfd9aa45 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -225,17 +225,14 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, access_type == MMU_INST_FETCH ? 'I' : 'D'); switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, 0, access_type)) { - case -3: - /* TLB inconsistency */ - return -1; case -2: /* Access violation */ ret = -2; best = nr; break; - case -1: + case -1: /* No match */ + case -3: /* TLB inconsistency */ default: - /* No match */ break; case 0: /* access granted */ @@ -251,14 +248,34 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, } } if (best != -1) { - done: +done: qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " HWADDR_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); } +#if defined(DUMP_PAGE_TABLES) + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = env_cpu(env); + hwaddr base = ppc_hash32_hpt_base(env_archcpu(env)); + hwaddr len = ppc_hash32_hpt_mask(env_archcpu(env)) + 0x80; + uint32_t a0, a1, a2, a3; + qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", + base, len); + for (hwaddr curaddr = base; curaddr < base + len; curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + a2 = ldl_phys(cs->as, curaddr + 8); + a3 = ldl_phys(cs->as, curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif return ret; } @@ -420,29 +437,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); -#if defined(DUMP_PAGE_TABLES) - if (qemu_loglevel_mask(CPU_LOG_MMU)) { - CPUState *cs = env_cpu(env); - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - curaddr < (ppc_hash32_hpt_base(cpu) - + ppc_hash32_hpt_mask(cpu) + 0x80); - curaddr += 16) { - a0 = ldl_phys(cs->as, curaddr); - a1 = ldl_phys(cs->as, curaddr + 4); - a2 = ldl_phys(cs->as, curaddr + 8); - a3 = ldl_phys(cs->as, curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif } else { qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ From f3f66a315714a0bf10c1b82df3324834c9b484fc Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:42 +0200 Subject: [PATCH 0870/2884] target/ppc: Eliminate ret from mmu6xx_get_physical_address() Return directly, which is simpler than dragging a return value through multpile if and else blocks. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 84 +++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 89bfd9aa45..03d9e6bfda 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -386,7 +386,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong vsid, sr, pgidx; int ds, target_page_bits; bool pr; - int ret; /* First try to find a BAT entry if there are any */ if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { @@ -419,7 +418,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); - ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ if (type == ACCESS_CODE && ctx->nx) { @@ -436,51 +434,47 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); - } else { - qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ - - switch (type) { - case ACCESS_INT: - /* Integer load/store : only access allowed */ - break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; - case ACCESS_CACHE: - /* - * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi - * - * Should make the instruction do no-op. As it already do - * no-op, it's quite easy :-) - */ - ctx->raddr = eaddr; - return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " - "address translation\n"); - return -4; - } - if ((access_type == MMU_DATA_STORE || ctx->key != 1) && - (access_type == MMU_DATA_LOAD || ctx->key != 0)) { - ctx->raddr = eaddr; - ret = 2; - } else { - ret = -2; - } + return ppc6xx_tlb_check(env, ctx, eaddr, access_type); } - return ret; + /* Direct-store segment : absolutely *BUGGY* for now */ + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* + * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi + * + * Should make the instruction do no-op. As it already do + * no-op, it's quite easy :-) + */ + ctx->raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need address" + " translation\n"); + return -4; + } + if ((access_type == MMU_DATA_STORE || ctx->key != 1) && + (access_type == MMU_DATA_LOAD || ctx->key != 0)) { + ctx->raddr = eaddr; + return 2; + } + return -2; } /* Generic TLB check function for embedded PowerPC implementations */ From 279fe98d0d3057daa4045faa6e2119288d7b7f07 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:43 +0200 Subject: [PATCH 0871/2884] target/ppc: Split out BookE xlate cases before checking real mode BookE does not have real mode so split off and handle it first in get_physical_address_wtlb() before checking for real mode for other MMU models. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 03d9e6bfda..9f177b6976 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1175,6 +1175,13 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, int ret = -1; bool real_mode; + if (env->mmu_model == POWERPC_MMU_BOOKE) { + return mmubooke_get_physical_address(env, ctx, eaddr, access_type); + } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { + return mmubooke206_get_physical_address(env, ctx, eaddr, access_type, + mmu_idx); + } + real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) : !FIELD_EX64(env->msr, MSR, DR); @@ -1195,13 +1202,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); } break; - case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, access_type); - break; - case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, - mmu_idx); - break; case POWERPC_MMU_REAL: if (real_mode) { ret = check_physical(env, ctx, eaddr, access_type); From 549685161da1d4c948eee0a4a3da6f9a6b879e83 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:44 +0200 Subject: [PATCH 0872/2884] target/ppc: Split off real mode cases in get_physical_address_wtlb() The real mode handling is identical in the remaining switch cases. Split off these common real mode cases into a separate conditional to leave only the else branches in the switch that are different. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 9f177b6976..b13150ce23 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1172,7 +1172,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, MMUAccessType access_type, int type, int mmu_idx) { - int ret = -1; bool real_mode; if (env->mmu_model == POWERPC_MMU_BOOKE) { @@ -1184,38 +1183,23 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) : !FIELD_EX64(env->msr, MSR, DR); + if (real_mode && (env->mmu_model == POWERPC_MMU_SOFT_6xx || + env->mmu_model == POWERPC_MMU_SOFT_4xx || + env->mmu_model == POWERPC_MMU_REAL)) { + return check_physical(env, ctx, eaddr, access_type); + } switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - ret = mmu6xx_get_physical_address(env, ctx, eaddr, access_type, - type); - } - break; - + return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); case POWERPC_MMU_SOFT_4xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); - } - break; + return mmu40x_get_physical_address(env, ctx, eaddr, access_type); case POWERPC_MMU_REAL: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - cpu_abort(env_cpu(env), - "PowerPC in real mode do not do any translation\n"); - } - return -1; + cpu_abort(env_cpu(env), + "PowerPC in real mode do not do any translation\n"); default: cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); - return -1; } - - return ret; } static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, From 77d9607d711b723b4873764051c4d5dc84894422 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:45 +0200 Subject: [PATCH 0873/2884] target/ppc: Inline and remove check_physical() This function just does two assignments and and unnecessary check that is always true so inline it in the only caller left and remove it. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index b13150ce23..2f412dd7c5 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1145,28 +1145,6 @@ void dump_mmu(CPUPPCState *env) } } -static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type) -{ - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC; - - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_REAL: - case POWERPC_MMU_BOOKE: - ctx->prot |= PAGE_WRITE; - break; - - default: - /* Caller's checks mean we should never get here for other models */ - g_assert_not_reached(); - } - - return 0; -} - int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, MMUAccessType access_type, int type, @@ -1186,7 +1164,9 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, if (real_mode && (env->mmu_model == POWERPC_MMU_SOFT_6xx || env->mmu_model == POWERPC_MMU_SOFT_4xx || env->mmu_model == POWERPC_MMU_REAL)) { - return check_physical(env, ctx, eaddr, access_type); + ctx->raddr = eaddr; + ctx->prot = PAGE_RWX; + return 0; } switch (env->mmu_model) { From 47bededc299c5cd0cbbf10660405d7076361fbaa Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:47 +0200 Subject: [PATCH 0874/2884] target/ppc: Fix misindented qemu_log_mask() calls Fix several qemu_log_mask() calls that are misindented. Acked-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 2f412dd7c5..124148b3da 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -315,8 +315,8 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, int ret = -1; bool ifetch = access_type == MMU_INST_FETCH; - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', virtual); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', virtual); if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; @@ -330,9 +330,9 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " - TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " + TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); if ((virtual & 0xF0000000) == BEPIu && ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { /* BAT matches */ @@ -364,12 +364,11 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; bl = (*BATu & 0x00001FFC) << 15; - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " - TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, ifetch ? 'I' : 'D', i, virtual, - *BATu, *BATl, BEPIu, BEPIl, bl); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx + " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx + "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " + TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', + i, virtual, *BATu, *BATl, BEPIu, BEPIl, bl); } } } @@ -415,9 +414,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, hash = vsid ^ pgidx; ctx->ptem = (vsid << 7) | (pgidx >> 10); - qemu_log_mask(CPU_LOG_MMU, - "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ds, ctx->nx, vsid); + qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " + TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); if (!ds) { /* Check if instruction fetch is allowed, if needed */ if (type == ACCESS_CODE && ctx->nx) { @@ -583,9 +581,9 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return 0; } } - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); + qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx + " => " HWADDR_FMT_plx " %d %d\n", + __func__, address, raddr, ctx->prot, ret); return ret; } @@ -704,11 +702,11 @@ int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, } mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx - " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" - HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", - __func__, address, pid, tlb->mas1, tlb->mas2, mask, - tlb->mas7_3, tlb->mas8); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx + " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" + HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); /* Check PID */ tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; From 9e9ca54cdb493721f8444030e6dcf680400c8d0b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:48 +0200 Subject: [PATCH 0875/2884] target/ppc: Deindent ppc_jumbo_xlate() Instead of putting a large block of code in an if, invert the condition and return early to be able to deindent the code block. Acked-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 331 ++++++++++++++++++++-------------------- 1 file changed, 165 insertions(+), 166 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 124148b3da..f40481b4b1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1264,187 +1264,186 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, *protp = ctx.prot; *psizep = TARGET_PAGE_BITS; return true; + } else if (!guest_visible) { + return false; } - if (guest_visible) { - log_cpu_state_mask(CPU_LOG_MMU, cs, 0); - if (type == ACCESS_CODE) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); - break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = eaddr; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; + case POWERPC_MMU_SOFT_4xx: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); + break; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); - } - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x08000000; - } - break; - case -3: - /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } - cs->exception_index = POWERPC_EXCP_ISI; - break; - case -4: - /* Direct store exception */ - /* No code fetch is allowed in direct-store areas */ - cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } - break; + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); - env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); - break; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_DTLB; + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->error_code = 0; + } else { + env->error_code = 0x08000000; + } + break; + case -3: + /* No execute protection violation */ + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_ESR] = 0x00000000; + env->error_code = 0; + } else { + env->error_code = 0x10000000; + } + cs->exception_index = POWERPC_EXCP_ISI; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + cs->exception_index = POWERPC_EXCP_ISI; + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->error_code = 0; + } else { + env->error_code = 0x10000000; + } + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] = 0x00800000; - } else { - env->spr[SPR_40x_ESR] = 0x00000000; - } - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); + } + env->spr[SPR_DMISS] = eaddr; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[0]); + env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[1]); + break; + case POWERPC_MMU_SOFT_4xx: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; } break; - case -2: - /* Access rights violation */ + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + } else { + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + break; + case -4: + /* Direct store exception */ + switch (type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = eaddr; + break; + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06000000; } else { - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } + env->spr[SPR_DSISR] = 0x04000000; } break; - case -4: - /* Direct store exception */ - switch (type) { - case ACCESS_FLOAT: - /* Floating point load/store */ - cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = eaddr; - break; - case ACCESS_RES: - /* lwarx, ldarx or stwcx. */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06000000; - } else { - env->spr[SPR_DSISR] = 0x04000000; - } - break; - case ACCESS_EXT: - /* eciwx or ecowx */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06100000; - } else { - env->spr[SPR_DSISR] = 0x04100000; - } - break; - default: - printf("DSI: invalid exception (%d)\n", ret); - cs->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = eaddr; - break; + case ACCESS_EXT: + /* eciwx or ecowx */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; } break; + default: + printf("DSI: invalid exception (%d)\n", ret); + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = eaddr; + break; } + break; } } return false; From f178e4f8949ec75d0e1e34f9b1ace646d1e6a031 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:49 +0200 Subject: [PATCH 0876/2884] target/ppc: Replace hard coded constants in ppc_jumbo_xlate() The "2" in booke206_update_mas_tlb_miss() call corresponds to MMU_INST_FETCH which is the value of access_type in this branch; mmubooke206_esr() only checks for MMU_DATA_STORE and it's called from code access so using MMU_DATA_LOAD here seems wrong so replace it with access_type here as well that yields the same result. This also makes these calls the same as the data access branch further down. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index f40481b4b1..6570b280ca 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1287,13 +1287,13 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_ITLB; env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " From 5cc867a679d4b5032284d30d22dad8e81195e60d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:50 +0200 Subject: [PATCH 0877/2884] target/ppc: Don't use mmu_ctx_t for mmu40x_get_physical_address() mmu40x_get_physical_address() only uses the raddr and prot fields from mmu_ctx_t. Pass these directly instead of using a ctx struct. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 6570b280ca..adce6cceb8 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -519,20 +519,18 @@ int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) return -1; } -static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, +static int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, MMUAccessType access_type) { ppcemb_tlb_t *tlb; - hwaddr raddr; int i, ret, zsel, zpr, pr; ret = -1; - raddr = (hwaddr)-1ULL; pr = FIELD_EX64(env->msr, MSR, PR); for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - if (!ppcemb_tlb_check(env, tlb, &raddr, address, + if (!ppcemb_tlb_check(env, tlb, raddr, address, env->spr[SPR_40x_PID], i)) { continue; } @@ -550,40 +548,34 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* fall through */ case 0x3: /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *prot = PAGE_RWX; ret = 0; break; + case 0x0: if (pr != 0) { /* Raise Zone protection fault. */ env->spr[SPR_40x_ESR] = 1 << 22; - ctx->prot = 0; + *prot = 0; ret = -2; break; } /* fall through */ case 0x1: - check_perms: +check_perms: /* Check from TLB entry */ - ctx->prot = tlb->prot; - ret = check_prot(ctx->prot, access_type); + *prot = tlb->prot; + ret = check_prot(*prot, access_type); if (ret == -2) { env->spr[SPR_40x_ESR] = 0; } break; } - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - return 0; - } } - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", - __func__, address, raddr, ctx->prot, ret); + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? 0 : *raddr, *prot, ret); return ret; } @@ -1171,7 +1163,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, case POWERPC_MMU_SOFT_6xx: return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); case POWERPC_MMU_SOFT_4xx: - return mmu40x_get_physical_address(env, ctx, eaddr, access_type); + return mmu40x_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, + access_type); case POWERPC_MMU_REAL: cpu_abort(env_cpu(env), "PowerPC in real mode do not do any translation\n"); From ecff3394a81f536fc537878cb9bfbc48b3f8690e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:51 +0200 Subject: [PATCH 0878/2884] target/ppc: Don't use mmu_ctx_t in mmubooke_get_physical_address() mmubooke_get_physical_address() only uses the raddr and prot fields from mmu_ctx_t. Pass these directly instead of using a ctx struct. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index adce6cceb8..12dac9e63a 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -634,36 +634,25 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, return access_type == MMU_INST_FETCH ? -3 : -2; } -static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, +static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, MMUAccessType access_type) { ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret; + int i, ret = -1; - ret = -1; - raddr = (hwaddr)-1ULL; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, + ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, access_type, i); if (ret != -1) { break; } } - - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, - address, ctx->raddr, ctx->prot, ret); - } else { - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, - address, raddr, ctx->prot, ret); - } - + qemu_log_mask(CPU_LOG_MMU, + "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx + " %d %d\n", __func__, ret < 0 ? "refused" : "granted", + address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); return ret; } @@ -1143,7 +1132,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, bool real_mode; if (env->mmu_model == POWERPC_MMU_BOOKE) { - return mmubooke_get_physical_address(env, ctx, eaddr, access_type); + return mmubooke_get_physical_address(env, &ctx->raddr, &ctx->prot, + eaddr, access_type); } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { return mmubooke206_get_physical_address(env, ctx, eaddr, access_type, mmu_idx); From e8a9c0fbffe80946ae1a6004b77a18a030cce6f1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:52 +0200 Subject: [PATCH 0879/2884] target/ppc: Don't use mmu_ctx_t in mmubooke206_get_physical_address() mmubooke206_get_physical_address() only uses the raddr and prot fields from mmu_ctx_t. Pass these directly instead of using a ctx struct. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 12dac9e63a..004ea2111d 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -836,27 +836,22 @@ found_tlb: return access_type == MMU_INST_FETCH ? -3 : -2; } -static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, +static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx) { ppcmas_tlb_t *tlb; - hwaddr raddr; - int i, j, ret; - - ret = -1; - raddr = (hwaddr)-1ULL; + int i, j, ret = -1; for (i = 0; i < BOOKE206_MAX_TLBN; i++) { int ways = booke206_tlb_ways(env, i); - for (j = 0; j < ways; j++) { tlb = booke206_get_tlbm(env, i, address, j); if (!tlb) { continue; } - ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, access_type, mmu_idx); if (ret != -1) { goto found_tlb; @@ -866,17 +861,10 @@ static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, found_tlb: - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, address, - ctx->raddr, ctx->prot, ret); - } else { - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, address, - raddr, ctx->prot, ret); - } - + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); return ret; } @@ -1135,8 +1123,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, return mmubooke_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, access_type); } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { - return mmubooke206_get_physical_address(env, ctx, eaddr, access_type, - mmu_idx); + return mmubooke206_get_physical_address(env, &ctx->raddr, &ctx->prot, + eaddr, access_type, mmu_idx); } real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) From aa30aa7d8e9232cb1bfdc4feb12b03095c2ff519 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:53 +0200 Subject: [PATCH 0880/2884] target/ppc: Remove BookE from direct store handling As BookE never returns -4 we can drop BookE from the direct store case in ppc_jumbo_xlate(). Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 004ea2111d..6c6c7c55b6 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1298,12 +1298,7 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, /* Direct store exception */ /* No code fetch is allowed in direct-store areas */ cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } + env->error_code = 0x10000000; break; } } else { From ba91e5d0276607fd6f862b498603f94c16ec0e07 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:54 +0200 Subject: [PATCH 0881/2884] target/ppc: Split off BookE handling from ppc_jumbo_xlate() Introduce ppc_booke_xlate() to handle BookE and BookE 2.06 cases to reduce ppc_jumbo_xlate() further. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 146 ++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 6c6c7c55b6..09a780bb7a 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1117,21 +1117,9 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, MMUAccessType access_type, int type, int mmu_idx) { - bool real_mode; - - if (env->mmu_model == POWERPC_MMU_BOOKE) { - return mmubooke_get_physical_address(env, &ctx->raddr, &ctx->prot, - eaddr, access_type); - } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { - return mmubooke206_get_physical_address(env, &ctx->raddr, &ctx->prot, - eaddr, access_type, mmu_idx); - } - - real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) - : !FIELD_EX64(env->msr, MSR, DR); - if (real_mode && (env->mmu_model == POWERPC_MMU_SOFT_6xx || - env->mmu_model == POWERPC_MMU_SOFT_4xx || - env->mmu_model == POWERPC_MMU_REAL)) { + bool real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR); + if (real_mode) { ctx->raddr = eaddr; ctx->prot = PAGE_RWX; return 0; @@ -1205,6 +1193,93 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; } +static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + hwaddr raddr; + int prot, ret; + + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, + access_type, mmu_idx); + } else { + ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, + access_type); + } + if (ret == 0) { + *raddrp = raddr; + *protp = prot; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (access_type == MMU_INST_FETCH) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + default: + g_assert_not_reached(); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0; + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + env->error_code = 0; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + default: + g_assert_not_reached(); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + } + } + return false; +} + /* Perform address translation */ /* TODO: Split this by mmu_model. */ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, @@ -1257,15 +1332,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_DEAR] = eaddr; env->spr[SPR_40x_ESR] = 0x00000000; break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1276,23 +1342,12 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x08000000; - } + env->error_code = 0x08000000; break; case -3: /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; break; case -4: /* Direct store exception */ @@ -1333,15 +1388,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1358,10 +1404,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, if (access_type == MMU_DATA_STORE) { env->spr[SPR_40x_ESR] |= 0x00800000; } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); } else { env->spr[SPR_DAR] = eaddr; if (access_type == MMU_DATA_STORE) { @@ -1440,6 +1482,10 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_32B: return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_BOOKE: + case POWERPC_MMU_BOOKE206: + return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); case POWERPC_MMU_MPC8xx: cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: From aa20e1c8c642a1986f8e949af311d9bd2ee70f8e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:55 +0200 Subject: [PATCH 0882/2884] target/ppc: Simplify ppc_booke_xlate() part 1 Move setting error_code that appears in every case out in front and hoist the common fall through case for BOOKE206 as well which allows removing the nested switches. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 09a780bb7a..611092966b 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1220,58 +1220,41 @@ static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, } log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + env->error_code = 0; + if (ret == -1) { + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + } + } if (access_type == MMU_INST_FETCH) { switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - default: - g_assert_not_reached(); - } + cs->exception_index = POWERPC_EXCP_ITLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0; break; case -3: /* No execute protection violation */ cs->exception_index = POWERPC_EXCP_ISI; env->spr[SPR_BOOKE_ESR] = 0; - env->error_code = 0; break; } } else { switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - default: - g_assert_not_reached(); - } + cs->exception_index = POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; From b18489b326c0677ed7d0178361a65df867b4a16f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:56 +0200 Subject: [PATCH 0883/2884] target/ppc: Simplify ppc_booke_xlate() part 2 Merge the code fetch and data access cases in a common switch. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 52 ++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 611092966b..8599106f75 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1221,45 +1221,33 @@ static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, log_cpu_state_mask(CPU_LOG_MMU, cs, 0); env->error_code = 0; - if (ret == -1) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ if (env->mmu_model == POWERPC_MMU_BOOKE206) { booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); } - } - if (access_type == MMU_INST_FETCH) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - cs->exception_index = POWERPC_EXCP_ITLB; + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case -2: + /* Access rights violation */ + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; + if (access_type != MMU_INST_FETCH) { env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_ISI; - break; - case -3: - /* No execute protection violation */ - cs->exception_index = POWERPC_EXCP_ISI; - env->spr[SPR_BOOKE_ESR] = 0; - break; - } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - cs->exception_index = POWERPC_EXCP_DTLB; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_DSI; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; } + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + break; } + return false; } From c29f808af5b14d24ec8591be46c37441c7c15663 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:57 +0200 Subject: [PATCH 0884/2884] target/ppc: Split off real mode handling from get_physical_address_wtlb() Add ppc_real_mode_xlate() to handle real mode translation and allow removing this case from ppc_jumbo_xlate(). Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 46 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 8599106f75..ab912da821 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1117,23 +1117,12 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, MMUAccessType access_type, int type, int mmu_idx) { - bool real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) - : !FIELD_EX64(env->msr, MSR, DR); - if (real_mode) { - ctx->raddr = eaddr; - ctx->prot = PAGE_RWX; - return 0; - } - switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); case POWERPC_MMU_SOFT_4xx: return mmu40x_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, access_type); - case POWERPC_MMU_REAL: - cpu_abort(env_cpu(env), - "PowerPC in real mode do not do any translation\n"); default: cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); } @@ -1251,6 +1240,24 @@ static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, return false; } +static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp) +{ + CPUPPCState *env = &cpu->env; + + if (access_type == MMU_INST_FETCH ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR)) { + *raddrp = eaddr; + *protp = PAGE_RWX; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (env->mmu_model == POWERPC_MMU_REAL) { + cpu_abort(CPU(cpu), "PowerPC in real mode shold not do translation\n"); + } + return false; +} + /* Perform address translation */ /* TODO: Split this by mmu_model. */ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, @@ -1264,6 +1271,10 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, int type; int ret; + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; + } + if (access_type == MMU_INST_FETCH) { /* code access */ type = ACCESS_CODE; @@ -1303,11 +1314,8 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_DEAR] = eaddr; env->spr[SPR_40x_ESR] = 0x00000000; break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); + g_assert_not_reached(); } break; case -2: @@ -1359,11 +1367,8 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); + g_assert_not_reached(); } break; case -2: @@ -1457,6 +1462,9 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_BOOKE206: return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_REAL: + return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, + protp); case POWERPC_MMU_MPC8xx: cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: From 58b0132553139b481a4b6ea1c597465152381f66 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:58 +0200 Subject: [PATCH 0885/2884] target/ppc: Split off 40x cases from ppc_jumbo_xlate() Introduce ppc_40x_xlate() to split off 40x handlning leaving only 6xx in ppc_jumbo_xlate() now. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 150 +++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 57 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ab912da821..ddb014e0aa 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1258,6 +1258,74 @@ static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, return false; } +static bool ppc_40x_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int ret; + + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; + } + + ret = mmu40x_get_physical_address(env, raddrp, protp, eaddr, access_type); + if (ret == 0) { + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (access_type == MMU_INST_FETCH) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + default: + g_assert_not_reached(); + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + break; + default: + g_assert_not_reached(); + } + } + return false; +} + /* Perform address translation */ /* TODO: Split this by mmu_model. */ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, @@ -1301,23 +1369,11 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - default: - g_assert_not_reached(); - } - break; + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = eaddr; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; @@ -1339,54 +1395,31 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); - env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); - break; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_DTLB; + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] = 0x00800000; - } else { - env->spr[SPR_40x_ESR] = 0x00000000; - } - break; - default: - g_assert_not_reached(); } + env->spr[SPR_DMISS] = eaddr; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; +tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[0]); + env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[1]); break; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0A000000; } else { - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } + env->spr[SPR_DSISR] = 0x08000000; } break; case -4: @@ -1462,6 +1495,9 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_BOOKE206: return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_SOFT_4xx: + return ppc_40x_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); case POWERPC_MMU_REAL: return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp); From 6b9ea7f3452ecf58582392eefae85db24fc6003f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:59 +0200 Subject: [PATCH 0886/2884] target/ppc: Transform ppc_jumbo_xlate() into ppc_6xx_xlate() Now that only 6xx cases left in ppc_jumbo_xlate() we can change it to ppc_6xx_xlate() also removing get_physical_address_wtlb(). Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/internal.h | 5 +---- target/ppc/mmu_common.c | 38 ++++++++++++-------------------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 98b41a970c..4a4f9b9ec8 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -262,10 +262,7 @@ typedef struct mmu_ctx_t mmu_ctx_t; bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx); + /* Software driven TLB helpers */ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code); diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ddb014e0aa..961062bca1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1112,22 +1112,6 @@ void dump_mmu(CPUPPCState *env) } } -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx) -{ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); - case POWERPC_MMU_SOFT_4xx: - return mmu40x_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, - access_type); - default: - cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); - } -} - static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, MMUAccessType access_type, int mmu_idx) { @@ -1326,12 +1310,10 @@ static bool ppc_40x_xlate(PowerPCCPU *cpu, vaddr eaddr, return false; } -/* Perform address translation */ -/* TODO: Split this by mmu_model. */ -static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) +static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -1353,8 +1335,10 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, type = ACCESS_INT; } - ret = get_physical_address_wtlb(env, &ctx, eaddr, access_type, - type, mmu_idx); + ctx.prot = 0; + ctx.hash[0] = 0; + ctx.hash[1] = 0; + ret = mmu6xx_get_physical_address(env, &ctx, eaddr, access_type, type); if (ret == 0) { *raddrp = ctx.raddr; *protp = ctx.prot; @@ -1498,14 +1482,16 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_SOFT_4xx: return ppc_40x_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_SOFT_6xx: + return ppc_6xx_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); case POWERPC_MMU_REAL: return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp); case POWERPC_MMU_MPC8xx: cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: - return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, - psizep, protp, mmu_idx, guest_visible); + cpu_abort(CPU(cpu), "Unknown or invalid MMU model\n"); } } From 306b5320307d13e95e1d43b585879cb56b163f66 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:00 +0200 Subject: [PATCH 0887/2884] target/ppc: Move mmu_ctx_t type to mmu_common.c Remove mmu_ctx_t definition from internal.h as this type is only used within mmu_common.c. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/internal.h | 12 ------------ target/ppc/mmu_common.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 4a4f9b9ec8..4a90dd2584 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -257,8 +257,6 @@ static inline int prot_for_access_type(MMUAccessType access_type) /* PowerPC MMU emulation */ -typedef struct mmu_ctx_t mmu_ctx_t; - bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -266,16 +264,6 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* Software driven TLB helpers */ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code); -/* Context used internally during MMU translations */ -struct mmu_ctx_t { - hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - int nx; /* Non-execute area */ -}; #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 961062bca1..34200d9cb1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -36,6 +36,17 @@ /* #define DUMP_PAGE_TABLES */ +/* Context used internally during MMU translations */ +typedef struct { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + int prot; /* Protection bits */ + hwaddr hash[2]; /* Pagetable hash values */ + target_ulong ptem; /* Virtual segment ID | API */ + int key; /* Access key */ + int nx; /* Non-execute area */ +} mmu_ctx_t; + void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); From 5fd257f5994335c9446b0fa8b6cfd6102c2f74ca Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:02 +0200 Subject: [PATCH 0888/2884] target/ppc: Remove id_tlbs flag from CPU env This flag for split instruction/data TLBs is only set for 6xx soft TLB MMU model and not used otherwise so no need to have a separate flag for that. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- hw/ppc/pegasos2.c | 2 +- target/ppc/cpu.h | 5 ++++- target/ppc/cpu_init.c | 19 +++++-------------- target/ppc/helper_regs.c | 1 - target/ppc/mmu_common.c | 10 ++-------- target/ppc/mmu_helper.c | 12 ++---------- 6 files changed, 14 insertions(+), 35 deletions(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index c22e8b336d..c1bd8dfa21 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -985,7 +985,7 @@ static void *build_fdt(MachineState *machine, int *fdt_size) cpu->env.icache_line_size); qemu_fdt_setprop_cell(fdt, cp, "i-cache-line-size", cpu->env.icache_line_size); - if (cpu->env.id_tlbs) { + if (ppc_is_split_tlb(cpu)) { qemu_fdt_setprop_cell(fdt, cp, "i-tlb-sets", cpu->env.nb_ways); qemu_fdt_setprop_cell(fdt, cp, "i-tlb-size", cpu->env.tlb_per_way); qemu_fdt_setprop_cell(fdt, cp, "d-tlb-sets", cpu->env.nb_ways); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index e201b7f6c2..95cc11dff7 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1281,7 +1281,6 @@ struct CPUArchState { int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */ int nb_ways; /* Number of ways in the TLB set */ int last_way; /* Last used way used to allocate TLB in a LRU way */ - int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ int nb_pids; /* Number of available PID registers */ int tlb_type; /* Type of TLB we're dealing with */ ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ @@ -2897,6 +2896,10 @@ static inline void booke206_fixed_size_tlbn(CPUPPCState *env, const int tlbn, tlb->mas1 |= ((uint32_t)tsize) << MAS1_TSIZE_SHIFT; } +static inline bool ppc_is_split_tlb(PowerPCCPU *cpu) +{ + return cpu->env.tlb_type == TLB_6XX; +} #endif static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 8cfaee61d9..9401eb28c3 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2137,7 +2137,6 @@ static void init_proc_405(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_4xx(env); @@ -2211,7 +2210,6 @@ static void init_proc_440EP(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); @@ -2311,7 +2309,6 @@ static void init_proc_440GP(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); @@ -2386,7 +2383,6 @@ static void init_proc_440x5(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); @@ -2754,7 +2750,6 @@ static void init_proc_e200(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_e200(env, 0xFFFF0000UL); @@ -2874,7 +2869,6 @@ static void init_proc_e500(CPUPPCState *env, int version) /* Memory management */ env->nb_pids = 3; env->nb_ways = 2; - env->id_tlbs = 0; switch (version) { case fsl_e500v1: tlbncfg[0] = register_tlbncfg(2, 1, 1, 0, 256); @@ -6927,20 +6921,17 @@ static void init_ppc_proc(PowerPCCPU *cpu) } /* Allocate TLBs buffer when needed */ #if !defined(CONFIG_USER_ONLY) - if (env->nb_tlb != 0) { - int nb_tlb = env->nb_tlb; - if (env->id_tlbs != 0) { - nb_tlb *= 2; - } + if (env->nb_tlb) { switch (env->tlb_type) { case TLB_6XX: - env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, nb_tlb); + /* 6xx has separate TLBs for instructions and data hence times 2 */ + env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, 2 * env->nb_tlb); break; case TLB_EMB: - env->tlb.tlbe = g_new0(ppcemb_tlb_t, nb_tlb); + env->tlb.tlbe = g_new0(ppcemb_tlb_t, env->nb_tlb); break; case TLB_MAS: - env->tlb.tlbm = g_new0(ppcmas_tlb_t, nb_tlb); + env->tlb.tlbm = g_new0(ppcmas_tlb_t, env->nb_tlb); break; } /* Pre-compute some useful values */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 16b43702d5..02076e96fb 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -730,7 +730,6 @@ void register_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; - env->id_tlbs = 1; env->tlb_type = TLB_6XX; spr_register(env, SPR_DMISS, "DMISS", SPR_NOACCESS, SPR_NOACCESS, diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 34200d9cb1..78bdbd506c 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -128,8 +128,8 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); /* Select TLB way */ nr += env->tlb_per_way * way; - /* 6xx have separate TLBs for instructions and data */ - if (is_code && env->id_tlbs == 1) { + /* 6xx has separate TLBs for instructions and data */ + if (is_code) { nr += env->nb_tlb; } @@ -1065,13 +1065,7 @@ static void mmu6xx_dump_mmu(CPUPPCState *env) mmu6xx_dump_BATs(env, ACCESS_INT); mmu6xx_dump_BATs(env, ACCESS_CODE); - if (env->id_tlbs != 1) { - qemu_printf("ERROR: 6xx MMU should have separated TLB" - " for code and data\n"); - } - qemu_printf("\nTLBs [EPN EPN + SIZE]\n"); - for (type = 0; type < 2; type++) { for (way = 0; way < env->nb_ways; way++) { for (entry = env->nb_tlb * type + env->tlb_per_way * way; diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index d4388e66be..238407a7f1 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -45,14 +45,8 @@ static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) { ppc6xx_tlb_t *tlb; - int nr, max; + int nr, max = 2 * env->nb_tlb; - /* LOG_SWTLB("Invalidate all TLBs\n"); */ - /* Invalidate all defined software TLB */ - max = env->nb_tlb; - if (env->id_tlbs == 1) { - max *= 2; - } for (nr = 0; nr < max; nr++) { tlb = &env->tlb.tlb6[nr]; pte_invalidate(&tlb->pte0); @@ -308,9 +302,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: ppc6xx_tlb_invalidate_virt(env, addr, 0); - if (env->id_tlbs == 1) { - ppc6xx_tlb_invalidate_virt(env, addr, 1); - } + ppc6xx_tlb_invalidate_virt(env, addr, 1); break; case POWERPC_MMU_32B: /* From 581eea5d656b73c6532109f4ced4c73fd4e5fd47 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:03 +0200 Subject: [PATCH 0889/2884] target/ppc: Split off common embedded TLB init Several 4xx CPUs and e200 share the same TLB settings enclosed in an ifdef. Split it off in a common function to reduce code duplication and the number of ifdefs. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 46 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9401eb28c3..b1ea301e22 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2127,18 +2127,22 @@ static int check_attn_hid0_power9(CPUPPCState *env) } #endif +static void init_tlbs_emb(CPUPPCState *env) +{ +#ifndef CONFIG_USER_ONLY + env->nb_tlb = 64; + env->nb_ways = 1; + env->tlb_type = TLB_EMB; +#endif +} + static void init_proc_405(CPUPPCState *env) { register_40x_sprs(env); register_405_sprs(env); register_usprgh_sprs(env); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + init_tlbs_emb(env); init_excp_4xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2206,12 +2210,8 @@ static void init_proc_440EP(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2305,12 +2305,7 @@ static void init_proc_440GP(CPUPPCState *env) register_440_sprs(env); register_usprgh_sprs(env); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2379,12 +2374,8 @@ static void init_proc_440x5(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2747,11 +2738,8 @@ static void init_proc_e200(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_e200(env, 0xFFFF0000UL); env->dcache_line_size = 32; env->icache_line_size = 32; From e89b0629b99e3df96ef1c190139f77169c58d27b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:05 +0200 Subject: [PATCH 0890/2884] target/ppc/mmu-hash32.c: Drop a local variable In ppc_hash32_xlate() the value of need_prop is checked in two places but precalculating it does not help because when we reach the first check we always return and not reach the second place so the value will only be used once. We can drop the local variable and calculate it when needed, which makes these checks using it similar to other places with such checks. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu-hash32.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index da6e8b293c..3abaf16e78 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -386,7 +386,6 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr pte_offset; ppc_hash_pte32_t pte; int prot; - int need_prot; hwaddr raddr; /* There are no hash32 large pages. */ @@ -400,13 +399,11 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, return true; } - need_prot = prot_for_access_type(access_type); - /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx); if (raddr != -1) { - if (need_prot & ~*protp) { + if (prot_for_access_type(access_type) & ~*protp) { if (guest_visible) { if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; @@ -474,7 +471,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); - if (need_prot & ~prot) { + if (prot_for_access_type(access_type) & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (guest_visible) { From 950251ee7b459d160fd3adfa2792326aa29b5d6c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:06 +0200 Subject: [PATCH 0891/2884] target/ppc/mmu-radix64.c: Drop a local variable The value is only used once so no need to introduce a local variable for it. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu-radix64.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index fefa55a5f1..c1e4f00335 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -185,7 +185,6 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, int mmu_idx, bool partition_scoped) { CPUPPCState *env = &cpu->env; - int need_prot; /* Check Page Attributes (pte58:59) */ if ((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO && access_type == MMU_INST_FETCH) { @@ -210,8 +209,8 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, } /* Check if requested access type is allowed */ - need_prot = prot_for_access_type(access_type); - if (need_prot & ~*prot) { /* Page Protected for that Access */ + if (prot_for_access_type(access_type) & ~*prot) { + /* Page Protected for that Access */ *fault_cause |= access_type == MMU_INST_FETCH ? SRR1_NOEXEC_GUARD : DSISR_PROTFAULT; return true; From cd1038ec1d316aeca52a0c5d18da055d5aa014c9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:07 +0200 Subject: [PATCH 0892/2884] target/ppc: Add a function to check for page protection bit Checking if a page protection bit is set for a given access type is a common operation. Add a function to avoid repeating the same check at multiple places. As this relies on access type and page protection bit values having certain relation also add an assert to ensure that this assumption holds. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 5 +++++ target/ppc/internal.h | 25 ++++++------------------- target/ppc/mmu-hash32.c | 6 +++--- target/ppc/mmu-hash64.c | 2 +- target/ppc/mmu-radix64.c | 2 +- target/ppc/mmu_common.c | 26 +++++++++++++------------- 6 files changed, 29 insertions(+), 37 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index b1ea301e22..01e358a4a5 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7521,6 +7521,11 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &ppc_sysemu_ops; INTERRUPT_STATS_PROVIDER_CLASS(oc)->get_statistics = ppc_get_irq_stats; + + /* check_prot_access_type relies on MMU access and PAGE bits relations */ + qemu_build_assert(MMU_DATA_LOAD == 0 && MMU_DATA_STORE == 1 && + MMU_INST_FETCH == 2 && PAGE_READ == 1 && + PAGE_WRITE == 2 && PAGE_EXEC == 4); #endif cc->gdb_num_core_regs = 71; diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 4a90dd2584..20fb2ec593 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -234,27 +234,14 @@ void destroy_ppc_opcodes(PowerPCCPU *cpu); void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc); const gchar *ppc_gdb_arch_name(CPUState *cs); -/** - * prot_for_access_type: - * @access_type: Access type - * - * Return the protection bit required for the given access type. - */ -static inline int prot_for_access_type(MMUAccessType access_type) -{ - switch (access_type) { - case MMU_INST_FETCH: - return PAGE_EXEC; - case MMU_DATA_LOAD: - return PAGE_READ; - case MMU_DATA_STORE: - return PAGE_WRITE; - } - g_assert_not_reached(); -} - #ifndef CONFIG_USER_ONLY +/* Check if permission bit required for the access_type is set in prot */ +static inline int check_prot_access_type(int prot, MMUAccessType access_type) +{ + return prot & (1 << access_type); +} + /* PowerPC MMU emulation */ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 3abaf16e78..1e8f1df0f0 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -252,7 +252,7 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, } *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; - if (*prot & prot_for_access_type(access_type)) { + if (check_prot_access_type(*prot, access_type)) { *raddr = eaddr; return true; } @@ -403,7 +403,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (env->nb_BATs != 0) { raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx); if (raddr != -1) { - if (prot_for_access_type(access_type) & ~*protp) { + if (!check_prot_access_type(*protp, access_type)) { if (guest_visible) { if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; @@ -471,7 +471,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); - if (prot_for_access_type(access_type) & ~prot) { + if (!check_prot_access_type(prot, access_type)) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (guest_visible) { diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index accbf0b2d8..cbc8efa0c3 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1089,7 +1089,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = exec_prot & pp_prot & amr_prot; - need_prot = prot_for_access_type(access_type); + need_prot = check_prot_access_type(PAGE_RWX, access_type); if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index c1e4f00335..5a02e4963b 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -209,7 +209,7 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, } /* Check if requested access type is allowed */ - if (prot_for_access_type(access_type) & ~*prot) { + if (!check_prot_access_type(*prot, access_type)) { /* Page Protected for that Access */ *fault_cause |= access_type == MMU_INST_FETCH ? SRR1_NOEXEC_GUARD : DSISR_PROTFAULT; diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 78bdbd506c..5414a14aad 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -114,11 +114,6 @@ static int pp_check(int key, int pp, int nx) return access; } -static int check_prot(int prot, MMUAccessType access_type) -{ - return prot & prot_for_access_type(access_type) ? 0 : -2; -} - int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code) { @@ -165,13 +160,14 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, /* Keep the matching PTE information */ ctx->raddr = pte1; ctx->prot = access; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { + if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + ret = 0; } else { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + ret = -2; } } } @@ -354,12 +350,14 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, (virtual & 0x0001F000); /* Compute access rights */ ctx->prot = prot; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { + if (check_prot_access_type(ctx->prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', ctx->prot & PAGE_WRITE ? 'W' : '-'); + ret = 0; + } else { + ret = -2; } break; } @@ -576,9 +574,11 @@ static int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, check_perms: /* Check from TLB entry */ *prot = tlb->prot; - ret = check_prot(*prot, access_type); - if (ret == -2) { + if (check_prot_access_type(*prot, access_type)) { + ret = 0; + } else { env->spr[SPR_40x_ESR] = 0; + ret = -2; } break; } @@ -636,7 +636,7 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, } else { *prot = (tlb->prot >> 4) & 0xF; } - if (*prot & prot_for_access_type(access_type)) { + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } @@ -838,7 +838,7 @@ found_tlb: *prot |= PAGE_EXEC; } } - if (*prot & prot_for_access_type(access_type)) { + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } From e7baac649bb3d9d72a3e79fc43e360d7ac99aead Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:08 +0200 Subject: [PATCH 0893/2884] target/ppc: Move out BookE and related MMU functions from mmu_common.c Add a new mmu-booke.c file for BookE and related MMU bits from mmu_common.c. Acked-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 4 - target/ppc/meson.build | 1 + target/ppc/mmu-booke.c | 531 ++++++++++++++++++++++++++++++++++++++++ target/ppc/mmu-booke.h | 17 ++ target/ppc/mmu_common.c | 507 +------------------------------------- target/ppc/mmu_helper.c | 1 + 6 files changed, 551 insertions(+), 510 deletions(-) create mode 100644 target/ppc/mmu-booke.c create mode 100644 target/ppc/mmu-booke.h diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 95cc11dff7..2015e603d4 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1642,10 +1642,6 @@ void ppc_tlb_invalidate_all(CPUPPCState *env); void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr); void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); void cpu_ppc_set_1lpar(PowerPCCPU *cpu); -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid); -int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 0b89f9b89f..db3b7a0c33 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -37,6 +37,7 @@ ppc_system_ss.add(files( 'arch_dump.c', 'machine.c', 'mmu-hash32.c', + 'mmu-booke.c', 'mmu_common.c', 'ppc-qmp-cmds.c', )) diff --git a/target/ppc/mmu-booke.c b/target/ppc/mmu-booke.c new file mode 100644 index 0000000000..55e5dd7c6b --- /dev/null +++ b/target/ppc/mmu-booke.c @@ -0,0 +1,531 @@ +/* + * PowerPC BookE MMU, TLB emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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.1 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 . + */ + +#include "qemu/osdep.h" +#include "exec/page-protection.h" +#include "exec/log.h" +#include "cpu.h" +#include "internal.h" +#include "mmu-booke.h" + +/* Generic TLB check function for embedded PowerPC implementations */ +static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int i) +{ + target_ulong mask; + + /* Check valid flag */ + if (!(tlb->prot & PAGE_VALID)) { + return false; + } + mask = ~(tlb->size - 1); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx + " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n", + __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != pid) { + return false; + } + /* Check effective address */ + if ((address & mask) != tlb->EPN) { + return false; + } + *raddrp = (tlb->RPN & mask) | (address & ~mask); + return true; +} + +/* Generic TLB search function for PowerPC embedded implementations */ +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) { + return i; + } + } + return -1; +} + +int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + int i, ret, zsel, zpr, pr; + + ret = -1; + pr = FIELD_EX64(env->msr, MSR, PR); + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (!ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_40x_PID], i)) { + continue; + } + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; + qemu_log_mask(CPU_LOG_MMU, + "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", + __func__, i, zsel, zpr, access_type, tlb->attr); + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) { + goto check_perms; + } + /* fall through */ + case 0x3: + /* All accesses granted */ + *prot = PAGE_RWX; + ret = 0; + break; + + case 0x0: + if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; + *prot = 0; + ret = -2; + break; + } + /* fall through */ + case 0x1: +check_perms: + /* Check from TLB entry */ + *prot = tlb->prot; + if (check_prot_access_type(*prot, access_type)) { + ret = 0; + } else { + env->spr[SPR_40x_ESR] = 0; + ret = -2; + } + break; + } + } + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? 0 : *raddr, *prot, ret); + + return ret; +} + +static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, target_ulong addr, int i) +{ + if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { + if (!env->nb_pids) { + /* Extend the physical address to 36 bits */ + *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; + } + return true; + } else if (!env->nb_pids) { + return false; + } + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { + return true; + } + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { + return true; + } + return false; +} + +static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, int *prot, target_ulong address, + MMUAccessType access_type, int i) +{ + if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { + qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); + return -1; + } + + /* Check the address space */ + if ((access_type == MMU_INST_FETCH ? + FIELD_EX64(env->msr, MSR, IR) : + FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + + if (FIELD_EX64(env->msr, MSR, PR)) { + *prot = tlb->prot & 0xF; + } else { + *prot = (tlb->prot >> 4) & 0xF; + } + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + return 0; + } + + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + int i, ret = -1; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, + access_type, i); + if (ret != -1) { + break; + } + } + qemu_log_mask(CPU_LOG_MMU, + "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx + " %d %d\n", __func__, ret < 0 ? "refused" : "granted", + address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); + return ret; +} + +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) +{ + int tlbm_size; + + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + + return 1024ULL << tlbm_size; +} + +/* TLB check function for MAS based SoftTLBs */ +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid) +{ + hwaddr mask; + uint32_t tlb_pid; + + if (!FIELD_EX64(env->msr, MSR, CM)) { + /* In 32bit mode we can only address 32bit EAs */ + address = (uint32_t)address; + } + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx + " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" + HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + + if (raddrp) { + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + } + + return 0; +} + +static bool is_epid_mmu(int mmu_idx) +{ + return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; +} + +static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) +{ + uint32_t esr = 0; + if (access_type == MMU_DATA_STORE) { + esr |= ESR_ST; + } + if (is_epid_mmu(mmu_idx)) { + esr |= ESR_EPID; + } + return esr; +} + +/* + * Get EPID register given the mmu_idx. If this is regular load, + * construct the EPID access bits from current processor state + * + * Get the effective AS and PR bits and the PID. The PID is returned + * only if EPID load is requested, otherwise the caller must detect + * the correct EPID. Return true if valid EPID is returned. + */ +static bool mmubooke206_get_as(CPUPPCState *env, + int mmu_idx, uint32_t *epid_out, + bool *as_out, bool *pr_out) +{ + if (is_epid_mmu(mmu_idx)) { + uint32_t epidr; + if (mmu_idx == PPC_TLB_EPID_STORE) { + epidr = env->spr[SPR_BOOKE_EPSC]; + } else { + epidr = env->spr[SPR_BOOKE_EPLC]; + } + *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; + *as_out = !!(epidr & EPID_EAS); + *pr_out = !!(epidr & EPID_EPR); + return true; + } else { + *as_out = FIELD_EX64(env->msr, MSR, DS); + *pr_out = FIELD_EX64(env->msr, MSR, PR); + return false; + } +} + +/* Check if the tlb found by hashing really matches */ +static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (!use_epid) { + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + } else { + if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { + goto found_tlb; + } + } + + qemu_log_mask(CPU_LOG_MMU, "%s: No TLB entry found for effective address " + "0x" TARGET_FMT_lx "\n", __func__, address); + return -1; + +found_tlb: + + /* Check the address space and permissions */ + if (access_type == MMU_INST_FETCH) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); + as = FIELD_EX64(env->msr, MSR, IR); + } + + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = 0; + if (pr) { + if (tlb->mas7_3 & MAS3_UR) { + *prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + *prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + *prot |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + *prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + *prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + *prot |= PAGE_EXEC; + } + } + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + return 0; + } + + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, + MMUAccessType access_type, + int mmu_idx) +{ + ppcmas_tlb_t *tlb; + int i, j, ret = -1; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, + access_type, mmu_idx); + if (ret != -1) { + goto found_tlb; + } + } + } + +found_tlb: + + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); + return ret; +} + +static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + uint32_t missed_tid = 0; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (access_type == MMU_INST_FETCH) { + as = FIELD_EX64(env->msr, MSR, IR); + } + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (as) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + if (!use_epid) { + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + missed_tid = env->spr[SPR_BOOKE_PID]; + break; + case MAS4_TIDSELD_PID1: + missed_tid = env->spr[SPR_BOOKE_PID1]; + break; + case MAS4_TIDSELD_PID2: + missed_tid = env->spr[SPR_BOOKE_PID2]; + break; + } + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + } else { + missed_tid = epid; + env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; + } + env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); + + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, + bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + hwaddr raddr; + int prot, ret; + + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, + access_type, mmu_idx); + } else { + ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, + access_type); + } + if (ret == 0) { + *raddrp = raddr; + *protp = prot; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + env->error_code = 0; + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + } + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case -2: + /* Access rights violation */ + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; + if (access_type != MMU_INST_FETCH) { + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + } + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + break; + } + + return false; +} diff --git a/target/ppc/mmu-booke.h b/target/ppc/mmu-booke.h new file mode 100644 index 0000000000..f972843bbb --- /dev/null +++ b/target/ppc/mmu-booke.h @@ -0,0 +1,17 @@ +#ifndef PPC_MMU_BOOKE_H +#define PPC_MMU_BOOKE_H + +#include "cpu.h" + +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); +int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type); +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid); +bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, + bool guest_visible); + +#endif diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 5414a14aad..2c75e53250 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -33,6 +33,7 @@ #include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" +#include "mmu-booke.h" /* #define DUMP_PAGE_TABLES */ @@ -484,401 +485,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return -2; } -/* Generic TLB check function for embedded PowerPC implementations */ -static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int i) -{ - target_ulong mask; - - /* Check valid flag */ - if (!(tlb->prot & PAGE_VALID)) { - return false; - } - mask = ~(tlb->size - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx - " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n", - __func__, i, address, pid, tlb->EPN, - mask, (uint32_t)tlb->PID, tlb->prot); - /* Check PID */ - if (tlb->PID != 0 && tlb->PID != pid) { - return false; - } - /* Check effective address */ - if ((address & mask) != tlb->EPN) { - return false; - } - *raddrp = (tlb->RPN & mask) | (address & ~mask); - return true; -} - -/* Generic TLB search function for PowerPC embedded implementations */ -int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) { - return i; - } - } - return -1; -} - -static int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, - int *prot, target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - int i, ret, zsel, zpr, pr; - - ret = -1; - pr = FIELD_EX64(env->msr, MSR, PR); - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (!ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_40x_PID], i)) { - continue; - } - zsel = (tlb->attr >> 4) & 0xF; - zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; - qemu_log_mask(CPU_LOG_MMU, - "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", - __func__, i, zsel, zpr, access_type, tlb->attr); - /* Check execute enable bit */ - switch (zpr) { - case 0x2: - if (pr != 0) { - goto check_perms; - } - /* fall through */ - case 0x3: - /* All accesses granted */ - *prot = PAGE_RWX; - ret = 0; - break; - - case 0x0: - if (pr != 0) { - /* Raise Zone protection fault. */ - env->spr[SPR_40x_ESR] = 1 << 22; - *prot = 0; - ret = -2; - break; - } - /* fall through */ - case 0x1: -check_perms: - /* Check from TLB entry */ - *prot = tlb->prot; - if (check_prot_access_type(*prot, access_type)) { - ret = 0; - } else { - env->spr[SPR_40x_ESR] = 0; - ret = -2; - } - break; - } - } - qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " - HWADDR_FMT_plx " %d %d\n", __func__, - ret < 0 ? "refused" : "granted", address, - ret < 0 ? 0 : *raddr, *prot, ret); - - return ret; -} - -static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, target_ulong addr, int i) -{ - if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { - if (!env->nb_pids) { - /* Extend the physical address to 36 bits */ - *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - return true; - } else if (!env->nb_pids) { - return false; - } - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { - return true; - } - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { - return true; - } - return false; -} - -static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, int *prot, target_ulong address, - MMUAccessType access_type, int i) -{ - if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); - return -1; - } - - /* Check the address space */ - if ((access_type == MMU_INST_FETCH ? - FIELD_EX64(env->msr, MSR, IR) : - FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - - if (FIELD_EX64(env->msr, MSR, PR)) { - *prot = tlb->prot & 0xF; - } else { - *prot = (tlb->prot >> 4) & 0xF; - } - if (check_prot_access_type(*prot, access_type)) { - qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); - return 0; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, - int *prot, target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - int i, ret = -1; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, - access_type, i); - if (ret != -1) { - break; - } - } - qemu_log_mask(CPU_LOG_MMU, - "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx - " %d %d\n", __func__, ret < 0 ? "refused" : "granted", - address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); - return ret; -} - -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) -{ - int tlbm_size; - - tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - - return 1024ULL << tlbm_size; -} - -/* TLB check function for MAS based SoftTLBs */ -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid) -{ - hwaddr mask; - uint32_t tlb_pid; - - if (!FIELD_EX64(env->msr, MSR, CM)) { - /* In 32bit mode we can only address 32bit EAs */ - address = (uint32_t)address; - } - - /* Check valid flag */ - if (!(tlb->mas1 & MAS1_VALID)) { - return -1; - } - - mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx - " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" - HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", - __func__, address, pid, tlb->mas1, tlb->mas2, mask, - tlb->mas7_3, tlb->mas8); - - /* Check PID */ - tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; - if (tlb_pid != 0 && tlb_pid != pid) { - return -1; - } - - /* Check effective address */ - if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { - return -1; - } - - if (raddrp) { - *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); - } - - return 0; -} - -static bool is_epid_mmu(int mmu_idx) -{ - return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; -} - -static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) -{ - uint32_t esr = 0; - if (access_type == MMU_DATA_STORE) { - esr |= ESR_ST; - } - if (is_epid_mmu(mmu_idx)) { - esr |= ESR_EPID; - } - return esr; -} - -/* - * Get EPID register given the mmu_idx. If this is regular load, - * construct the EPID access bits from current processor state - * - * Get the effective AS and PR bits and the PID. The PID is returned - * only if EPID load is requested, otherwise the caller must detect - * the correct EPID. Return true if valid EPID is returned. - */ -static bool mmubooke206_get_as(CPUPPCState *env, - int mmu_idx, uint32_t *epid_out, - bool *as_out, bool *pr_out) -{ - if (is_epid_mmu(mmu_idx)) { - uint32_t epidr; - if (mmu_idx == PPC_TLB_EPID_STORE) { - epidr = env->spr[SPR_BOOKE_EPSC]; - } else { - epidr = env->spr[SPR_BOOKE_EPLC]; - } - *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; - *as_out = !!(epidr & EPID_EAS); - *pr_out = !!(epidr & EPID_EPR); - return true; - } else { - *as_out = FIELD_EX64(env->msr, MSR, DS); - *pr_out = FIELD_EX64(env->msr, MSR, PR); - return false; - } -} - -/* Check if the tlb found by hashing really matches */ -static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddr, int *prot, - target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - uint32_t epid; - bool as, pr; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (!use_epid) { - if (ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2]) >= 0) { - goto found_tlb; - } - } else { - if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { - goto found_tlb; - } - } - - qemu_log_mask(CPU_LOG_MMU, "%s: No TLB entry found for effective address " - "0x" TARGET_FMT_lx "\n", __func__, address); - return -1; - -found_tlb: - - /* Check the address space and permissions */ - if (access_type == MMU_INST_FETCH) { - /* There is no way to fetch code using epid load */ - assert(!use_epid); - as = FIELD_EX64(env->msr, MSR, IR); - } - - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = 0; - if (pr) { - if (tlb->mas7_3 & MAS3_UR) { - *prot |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_UW) { - *prot |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_UX) { - *prot |= PAGE_EXEC; - } - } else { - if (tlb->mas7_3 & MAS3_SR) { - *prot |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_SW) { - *prot |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_SX) { - *prot |= PAGE_EXEC; - } - } - if (check_prot_access_type(*prot, access_type)) { - qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); - return 0; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, - int *prot, target_ulong address, - MMUAccessType access_type, - int mmu_idx) -{ - ppcmas_tlb_t *tlb; - int i, j, ret = -1; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - if (!tlb) { - continue; - } - ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, - access_type, mmu_idx); - if (ret != -1) { - goto found_tlb; - } - } - } - -found_tlb: - - qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " - HWADDR_FMT_plx " %d %d\n", __func__, - ret < 0 ? "refused" : "granted", address, - ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); - return ret; -} - static const char *book3e_tsize_to_str[32] = { "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", @@ -1117,117 +723,6 @@ void dump_mmu(CPUPPCState *env) } } -static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - uint32_t epid; - bool as, pr; - uint32_t missed_tid = 0; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (access_type == MMU_INST_FETCH) { - as = FIELD_EX64(env->msr, MSR, IR); - } - env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; - env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; - env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; - env->spr[SPR_BOOKE_MAS3] = 0; - env->spr[SPR_BOOKE_MAS6] = 0; - env->spr[SPR_BOOKE_MAS7] = 0; - - /* AS */ - if (as) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; - } - - env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; - - if (!use_epid) { - switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { - case MAS4_TIDSELD_PID0: - missed_tid = env->spr[SPR_BOOKE_PID]; - break; - case MAS4_TIDSELD_PID1: - missed_tid = env->spr[SPR_BOOKE_PID1]; - break; - case MAS4_TIDSELD_PID2: - missed_tid = env->spr[SPR_BOOKE_PID2]; - break; - } - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; - } else { - missed_tid = epid; - env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; - } - env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); - - - /* next victim logic */ - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; - env->last_way++; - env->last_way &= booke206_tlb_ways(env, 0) - 1; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; -} - -static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - hwaddr raddr; - int prot, ret; - - if (env->mmu_model == POWERPC_MMU_BOOKE206) { - ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, - access_type, mmu_idx); - } else { - ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, - access_type); - } - if (ret == 0) { - *raddrp = raddr; - *protp = prot; - *psizep = TARGET_PAGE_BITS; - return true; - } else if (!guest_visible) { - return false; - } - - log_cpu_state_mask(CPU_LOG_MMU, cs, 0); - env->error_code = 0; - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - if (env->mmu_model == POWERPC_MMU_BOOKE206) { - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - } - cs->exception_index = (access_type == MMU_INST_FETCH) ? - POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case -2: - /* Access rights violation */ - cs->exception_index = (access_type == MMU_INST_FETCH) ? - POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; - if (access_type != MMU_INST_FETCH) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - } - break; - case -3: - /* No execute protection violation */ - cs->exception_index = POWERPC_EXCP_ISI; - env->spr[SPR_BOOKE_ESR] = 0; - break; - } - - return false; -} static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 238407a7f1..b0a0676beb 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -33,6 +33,7 @@ #include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" +#include "mmu-booke.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" From e48fb4c590a23d81ee1d2f09ee9bcf5dd5f98e43 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:09 +0200 Subject: [PATCH 0894/2884] target/ppc: Remove pp_check() and reuse ppc_hash32_pp_prot() The ppc_hash32_pp_prot() function in mmu-hash32.c is the same as pp_check() in mmu_common.c, merge these to remove duplicated code. Define the common function as static lnline otherwise exporting the function from mmu-hash32.c would stop the compiler inlining it which results in slightly lower performance. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan [np: move ppc_hash32_pp_prot inline without changing it] Signed-off-by: Nicholas Piggin --- target/ppc/mmu-hash32.c | 45 ----------------------------------------- target/ppc/mmu-hash32.h | 45 +++++++++++++++++++++++++++++++++++++++++ target/ppc/mmu_common.c | 44 ++-------------------------------------- 3 files changed, 47 insertions(+), 87 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 1e8f1df0f0..d5f2057eb1 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -37,51 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -static int ppc_hash32_pp_prot(int key, int pp, int nx) -{ - int prot; - - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - case 0x3: - prot = PAGE_READ; - break; - - default: - abort(); - } - } else { - switch (pp) { - case 0x0: - prot = 0; - break; - - case 0x1: - case 0x3: - prot = PAGE_READ; - break; - - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - default: - abort(); - } - } - if (nx == 0) { - prot |= PAGE_EXEC; - } - - return prot; -} - static int ppc_hash32_pte_prot(int mmu_idx, target_ulong sr, ppc_hash_pte32_t pte) { diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 7119a63d97..f0ce6951b4 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -102,6 +102,51 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } +static inline int ppc_hash32_pp_prot(bool key, int pp, bool nx) +{ + int prot; + + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + case 0x3: + prot = PAGE_READ; + break; + + default: + abort(); + } + } else { + switch (pp) { + case 0x0: + prot = 0; + break; + + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + default: + abort(); + } + } + if (nx == 0) { + prot |= PAGE_EXEC; + } + + return prot; +} + typedef struct { uint32_t pte0, pte1; } ppc_hash_pte32_t; diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 2c75e53250..e2542694f0 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -77,44 +77,6 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) /*****************************************************************************/ /* PowerPC MMU emulation */ -static int pp_check(int key, int pp, int nx) -{ - int access; - - /* Compute access rights */ - access = 0; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - access |= PAGE_WRITE; - /* fall through */ - case 0x3: - access |= PAGE_READ; - break; - } - } else { - switch (pp) { - case 0x0: - access = 0; - break; - case 0x1: - case 0x3: - access = PAGE_READ; - break; - case 0x2: - access = PAGE_READ | PAGE_WRITE; - break; - } - } - if (nx == 0) { - access |= PAGE_EXEC; - } - - return access; -} - int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code) { @@ -137,7 +99,7 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, MMUAccessType access_type) { target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; + int ret, pteh, ptev, pp; ret = -1; /* Check validity and table match */ @@ -156,11 +118,9 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, return -3; } } - /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); /* Keep the matching PTE information */ ctx->raddr = pte1; - ctx->prot = access; + ctx->prot = ppc_hash32_pp_prot(ctx->key, pp, ctx->nx); if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); From c2bf2ccb266dc9ae4a6da75b845f54535417e109 Mon Sep 17 00:00:00 2001 From: Artyom Kunakovsky Date: Thu, 23 May 2024 08:11:18 +0300 Subject: [PATCH 0895/2884] configure: move -mcx16 flag out of CPU_CFLAGS The point of CPU_CFLAGS is really just to select the appropriate multilib, for example for library linking tests, and -mcx16 is not needed for that purpose. Furthermore, if -mcx16 is part of QEMU's choice of a basic x86_64 instruction set, it should be applied to cross-compiled x86_64 code too; it is plausible that tests/tcg would want to cover cmpxchg16b as well, for example. In the end this makes just as much sense as a per sub-build tweak, so move the flag to meson.build and cross_cc_cflags_x86_64. This leaves out contrib/plugins, which would fail when attempting to use __sync_val_compare_and_swap_16 (note it does not do yet); while minor, this *is* a disadvantage of this change. But building contrib/plugins with a Makefile instead of meson.build is something self-inflicted just for the sake of showing that it can be done, and if this kind of papercut started becoming a problem we could make the directory part of the meson build. Until then, we can live with the limitation. Signed-off-by: Artyom Kunakovsky Message-ID: <20240523051118.29367-1-artyomkunakovsky@gmail.com> [rewrite commit message, remove from configure. - Paolo] Signed-off-by: Paolo Bonzini --- configure | 7 ++----- meson.build | 7 +++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/configure b/configure index 38ee257701..4d01a42ba6 100755 --- a/configure +++ b/configure @@ -512,10 +512,7 @@ case "$cpu" in cpu="x86_64" host_arch=x86_64 linux_arch=x86 - # ??? Only extremely old AMD cpus do not have cmpxchg16b. - # If we truly care, we should simply detect this case at - # runtime and generate the fallback to serial emulation. - CPU_CFLAGS="-m64 -mcx16" + CPU_CFLAGS="-m64" ;; esac @@ -1203,7 +1200,7 @@ fi : ${cross_cc_cflags_sparc64="-m64 -mcpu=ultrasparc"} : ${cross_cc_sparc="$cross_cc_sparc64"} : ${cross_cc_cflags_sparc="-m32 -mcpu=supersparc"} -: ${cross_cc_cflags_x86_64="-m64"} +: ${cross_cc_cflags_x86_64="-m64 -mcx16"} compute_target_variable() { eval "$2=" diff --git a/meson.build b/meson.build index a9de71d450..7fd82b5f48 100644 --- a/meson.build +++ b/meson.build @@ -336,6 +336,13 @@ if host_arch == 'i386' and not cc.links(''' qemu_common_flags = ['-march=i486'] + qemu_common_flags endif +# ??? Only extremely old AMD cpus do not have cmpxchg16b. +# If we truly care, we should simply detect this case at +# runtime and generate the fallback to serial emulation. +if host_arch == 'x86_64' + qemu_common_flags = ['-mcx16'] + qemu_common_flags +endif + if get_option('prefer_static') qemu_ldflags += get_option('b_pie') ? '-static-pie' : '-static' endif From 8225bff7c5db504f50e54ef66b079854635dba70 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 17:17:47 +0200 Subject: [PATCH 0896/2884] target/i386: disable jmp_opt if EFLAGS.RF is 1 If EFLAGS.RF is 1, special processing in gen_eob_worker() is needed and therefore goto_tb cannot be used. Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 76be742580..ebcff8766c 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4660,7 +4660,7 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cpuid_7_1_eax_features = env->features[FEAT_7_1_EAX]; dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; dc->jmp_opt = !((cflags & CF_NO_GOTO_TB) || - (flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); + (flags & (HF_RF_MASK | HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); /* * If jmp_opt, we want to handle each string instruction individually. * For icount also disable repz optimization so that each iteration From f0f0136abba688a6516647a79cc91e03fad6d5d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 May 2024 10:03:22 +0200 Subject: [PATCH 0897/2884] target/i386: no single-step exception after MOV or POP SS Intel SDM 18.3.1.4 "If an occurrence of the MOV or POP instruction loads the SS register executes with EFLAGS.TF = 1, no single-step debug exception occurs following the MOV or POP instruction." Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ebcff8766c..9782250b20 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2273,7 +2273,7 @@ gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) if (recheck_tf) { gen_helper_rechecking_single_step(tcg_env); tcg_gen_exit_tb(NULL, 0); - } else if (s->flags & HF_TF_MASK) { + } else if ((s->flags & HF_TF_MASK) && !inhibit) { gen_helper_single_step(tcg_env); } else if (jr && /* give irqs a chance to happen */ From 69d728126223941de298b34798eb433be28b2734 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 18:43:44 +0200 Subject: [PATCH 0898/2884] target/i386: cleanup eob handling of RSM gen_helper_rsm cannot generate an exception, and reloads the flags. So there's no need to spill cc_op and update cpu_eip, but on the other hand cc_op must be reset to CC_OP_EFLAGS before returning. It all works by chance, because by spilling cc_op before the call to the helper, it becomes non-dirty and gen_eob will not overwrite the CC_OP_EFLAGS value that is placed there by the helper. But let's clean it up. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 9782250b20..849864d1aa 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4488,9 +4488,8 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) /* we should not be in SMM mode */ g_assert_not_reached(); #else - gen_update_cc_op(s); - gen_update_eip_next(s); gen_helper_rsm(tcg_env); + set_cc_op(s, CC_OP_EFLAGS); #endif /* CONFIG_USER_ONLY */ s->base.is_jmp = DISAS_EOB_ONLY; break; From f6ac77eab66e8e8e6ba04d58bc262b60698ecd1d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 18:38:02 +0200 Subject: [PATCH 0899/2884] target/i386: remove unnecessary gen_update_cc_op before gen_eob* This is already handled in gen_eob(). Before adding another DISAS_* case, remove the double calls. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 849864d1aa..920d854c2b 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4775,14 +4775,12 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_jmp_rel_csize(dc, 0, 0); break; case DISAS_EOB_NEXT: - gen_update_cc_op(dc); gen_update_eip_cur(dc); /* fall through */ case DISAS_EOB_ONLY: gen_eob(dc); break; case DISAS_EOB_INHIBIT_IRQ: - gen_update_cc_op(dc); gen_update_eip_cur(dc); gen_eob_inhibit_irq(dc); break; From a0625efd4d61a7e1e3f0435aa6a83c6330b72b9a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 23:08:40 +0200 Subject: [PATCH 0900/2884] target/i386: cpu_load_eflags already sets cc_op No need to set it again at the end of the translation block, cc_op_dirty can be set to false. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 2 +- target/i386/tcg/translate.c | 37 ++++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index c78e35b1e2..3f2ae0aa7e 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1883,7 +1883,7 @@ static void gen_IRET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_helper_iret_protected(tcg_env, tcg_constant_i32(s->dflag - 1), eip_next_i32(s)); } - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); s->base.is_jmp = DISAS_EOB_ONLY; } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 920d854c2b..25c973e20c 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -309,7 +309,7 @@ static const uint8_t cc_op_live[CC_OP_NB] = { [CC_OP_POPCNT] = USES_CC_SRC, }; -static void set_cc_op(DisasContext *s, CCOp op) +static void set_cc_op_1(DisasContext *s, CCOp op, bool dirty) { int dead; @@ -332,20 +332,27 @@ static void set_cc_op(DisasContext *s, CCOp op) tcg_gen_discard_tl(s->cc_srcT); } - if (op == CC_OP_DYNAMIC) { - /* The DYNAMIC setting is translator only, and should never be - stored. Thus we always consider it clean. */ - s->cc_op_dirty = false; - } else { - /* Discard any computed CC_OP value (see shifts). */ - if (s->cc_op == CC_OP_DYNAMIC) { - tcg_gen_discard_i32(cpu_cc_op); - } - s->cc_op_dirty = true; + if (dirty && s->cc_op == CC_OP_DYNAMIC) { + tcg_gen_discard_i32(cpu_cc_op); } + s->cc_op_dirty = dirty; s->cc_op = op; } +static void set_cc_op(DisasContext *s, CCOp op) +{ + /* + * The DYNAMIC setting is translator only, everything else + * will be spilled later. + */ + set_cc_op_1(s, op, op != CC_OP_DYNAMIC); +} + +static void assume_cc_op(DisasContext *s, CCOp op) +{ + set_cc_op_1(s, op, false); +} + static void gen_update_cc_op(DisasContext *s) { if (s->cc_op_dirty) { @@ -3554,6 +3561,10 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_update_cc_op(s); gen_update_eip_cur(s); gen_helper_syscall(tcg_env, cur_insn_len_i32(s)); + /* condition codes are modified only in long mode */ + if (LMA(s)) { + assume_cc_op(s, CC_OP_EFLAGS); + } /* TF handling for the syscall insn is different. The TF bit is checked after the syscall insn completes. This allows #DB to not be generated after one has entered CPL0 if TF is set in FMASK. */ @@ -3570,7 +3581,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_helper_sysret(tcg_env, tcg_constant_i32(dflag - 1)); /* condition codes are modified only in long mode */ if (LMA(s)) { - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } /* TF handling for the sysret insn is different. The TF bit is checked after the sysret insn completes. This allows #DB to be @@ -4489,7 +4500,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) g_assert_not_reached(); #else gen_helper_rsm(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); #endif /* CONFIG_USER_ONLY */ s->base.is_jmp = DISAS_EOB_ONLY; break; From abdcc5c8eff0879c76aeb9f16d0c13044bfecbda Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 23:04:28 +0200 Subject: [PATCH 0901/2884] target/i386: set CC_OP in helpers if they want CC_OP_EFLAGS Mark cc_op as clean and do not spill it at the end of the translation block. Technically this is a tiny bit less efficient, but: * it results in translations that are a tiny bit smaller * for most of these instructions, it is not unlikely that they are close to the end of the basic block, in which case cc_op would not be overwritten * anyway the cost is probably dwarfed by that of computing flags. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/ops_sse.h | 8 ++++++++ target/i386/tcg/emit.c.inc | 22 +++++++++++----------- target/i386/tcg/fpu_helper.c | 2 ++ target/i386/tcg/int_helper.c | 13 +++++++++---- target/i386/tcg/seg_helper.c | 16 ++++++++-------- target/i386/tcg/translate.c | 12 ++++++------ 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index 6a465a35fd..f0aa1894aa 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -1111,6 +1111,7 @@ void helper_ucomiss(CPUX86State *env, Reg *d, Reg *s) s1 = s->ZMM_S(0); ret = float32_compare_quiet(s0, s1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } void helper_comiss(CPUX86State *env, Reg *d, Reg *s) @@ -1122,6 +1123,7 @@ void helper_comiss(CPUX86State *env, Reg *d, Reg *s) s1 = s->ZMM_S(0); ret = float32_compare(s0, s1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } void helper_ucomisd(CPUX86State *env, Reg *d, Reg *s) @@ -1133,6 +1135,7 @@ void helper_ucomisd(CPUX86State *env, Reg *d, Reg *s) d1 = s->ZMM_D(0); ret = float64_compare_quiet(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } void helper_comisd(CPUX86State *env, Reg *d, Reg *s) @@ -1144,6 +1147,7 @@ void helper_comisd(CPUX86State *env, Reg *d, Reg *s) d1 = s->ZMM_D(0); ret = float64_compare(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } #endif @@ -1610,6 +1614,7 @@ void glue(helper_ptest, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) cf |= (s->Q(i) & ~d->Q(i)); } CC_SRC = (zf ? 0 : CC_Z) | (cf ? 0 : CC_C); + CC_OP = CC_OP_EFLAGS; } #define FMOVSLDUP(i) s->L((i) & ~1) @@ -1966,6 +1971,7 @@ static inline unsigned pcmpxstrx(CPUX86State *env, Reg *d, Reg *s, validd--; CC_SRC = (valids < upper ? CC_Z : 0) | (validd < upper ? CC_S : 0); + CC_OP = CC_OP_EFLAGS; switch ((ctrl >> 2) & 3) { case 0: @@ -2297,6 +2303,7 @@ void glue(helper_vtestps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) cf |= (s->L(i) & ~d->L(i)); } CC_SRC = ((zf >> 31) ? 0 : CC_Z) | ((cf >> 31) ? 0 : CC_C); + CC_OP = CC_OP_EFLAGS; } void glue(helper_vtestpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -2309,6 +2316,7 @@ void glue(helper_vtestpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) cf |= (s->Q(i) & ~d->Q(i)); } CC_SRC = ((zf >> 63) ? 0 : CC_Z) | ((cf >> 63) ? 0 : CC_C); + CC_OP = CC_OP_EFLAGS; } void glue(helper_vpmaskmovd_st, SUFFIX)(CPUX86State *env, diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 3f2ae0aa7e..e0ac21abe2 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -918,7 +918,7 @@ static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } else { \ gen_helper_##lname##_ymm(tcg_env, OP_PTR1, OP_PTR2); \ } \ - set_cc_op(s, CC_OP_EFLAGS); \ + assume_cc_op(s, CC_OP_EFLAGS); \ } UNARY_CMP_SSE(VPTEST, ptest) UNARY_CMP_SSE(VTESTPS, vtestps) @@ -1079,7 +1079,7 @@ static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_aaa(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_AAD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) @@ -1102,7 +1102,7 @@ static void gen_AAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_aas(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_ADC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) @@ -1566,14 +1566,14 @@ static void gen_DAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_daa(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_DAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_das(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_DEC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) @@ -2353,14 +2353,14 @@ static void gen_PCMPESTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpestri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_PCMPESTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpestrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); if ((s->prefix & PREFIX_VEX) && !s->vex_l) { tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_regs[0].ZMM_X(1)), 16, 16, 0); @@ -2371,14 +2371,14 @@ static void gen_PCMPISTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpistri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_PCMPISTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpistrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); if ((s->prefix & PREFIX_VEX) && !s->vex_l) { tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_regs[0].ZMM_X(1)), 16, 16, 0); @@ -3595,7 +3595,7 @@ static void gen_VCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) SSEFunc_0_epp fn; fn = s->prefix & PREFIX_DATA ? gen_helper_comisd : gen_helper_comiss; fn(tcg_env, OP_PTR1, OP_PTR2); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_VCVTPD2PS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) @@ -3982,7 +3982,7 @@ static void gen_VUCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode SSEFunc_0_epp fn; fn = s->prefix & PREFIX_DATA ? gen_helper_ucomisd : gen_helper_ucomiss; fn(tcg_env, OP_PTR1, OP_PTR2); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); } static void gen_VZEROALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index ece22a3553..8df8cae631 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -487,6 +487,7 @@ void helper_fcomi_ST0_FT0(CPUX86State *env) ret = floatx80_compare(ST0, FT0, &env->fp_status); eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); CC_SRC = eflags | fcomi_ccval[ret + 1]; + CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } @@ -499,6 +500,7 @@ void helper_fucomi_ST0_FT0(CPUX86State *env) ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); CC_SRC = eflags | fcomi_ccval[ret + 1]; + CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index 4cc59f1520..e1f9240528 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -187,6 +187,7 @@ void helper_aaa(CPUX86State *env) } env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_aas(CPUX86State *env) @@ -211,6 +212,7 @@ void helper_aas(CPUX86State *env) } env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_daa(CPUX86State *env) @@ -238,6 +240,7 @@ void helper_daa(CPUX86State *env) eflags |= parity_table[al]; /* pf */ eflags |= (al & 0x80); /* sf */ CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_das(CPUX86State *env) @@ -269,6 +272,7 @@ void helper_das(CPUX86State *env) eflags |= parity_table[al]; /* pf */ eflags |= (al & 0x80); /* sf */ CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } #ifdef TARGET_X86_64 @@ -449,10 +453,11 @@ target_ulong HELPER(rdrand)(CPUX86State *env) error_free(err); /* Failure clears CF and all other flags, and returns 0. */ env->cc_src = 0; - return 0; + ret = 0; + } else { + /* Success sets CF and clears all others. */ + env->cc_src = CC_C; } - - /* Success sets CF and clears all others. */ - env->cc_src = CC_C; + env->cc_op = CC_OP_EFLAGS; return ret; } diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 34ccabd8ce..0301459004 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -2326,7 +2326,7 @@ void helper_verr(CPUX86State *env, target_ulong selector1) int rpl, dpl, cpl; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env); + eflags = cpu_cc_compute_all(env) | CC_Z; if ((selector & 0xfffc) == 0) { goto fail; } @@ -2351,11 +2351,11 @@ void helper_verr(CPUX86State *env, target_ulong selector1) } else { if (dpl < cpl || dpl < rpl) { fail: - CC_SRC = eflags & ~CC_Z; - return; + eflags &= ~CC_Z; } } - CC_SRC = eflags | CC_Z; + CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_verw(CPUX86State *env, target_ulong selector1) @@ -2364,7 +2364,7 @@ void helper_verw(CPUX86State *env, target_ulong selector1) int rpl, dpl, cpl; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env); + eflags = cpu_cc_compute_all(env) | CC_Z; if ((selector & 0xfffc) == 0) { goto fail; } @@ -2385,9 +2385,9 @@ void helper_verw(CPUX86State *env, target_ulong selector1) } if (!(e2 & DESC_W_MASK)) { fail: - CC_SRC = eflags & ~CC_Z; - return; + eflags &= ~CC_Z; } } - CC_SRC = eflags | CC_Z; + CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 25c973e20c..1b0485e01b 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2984,7 +2984,7 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) gen_update_cc_op(s); gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); gen_helper_fucomi_ST0_FT0(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); break; case 0x1e: /* fcomi */ if (!(s->cpuid_features & CPUID_CMOV)) { @@ -2993,7 +2993,7 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) gen_update_cc_op(s); gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); gen_helper_fcomi_ST0_FT0(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); break; case 0x28: /* ffree sti */ gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); @@ -3052,7 +3052,7 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); gen_helper_fucomi_ST0_FT0(tcg_env); gen_helper_fpop(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); break; case 0x3e: /* fcomip */ if (!(s->cpuid_features & CPUID_CMOV)) { @@ -3062,7 +3062,7 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); gen_helper_fcomi_ST0_FT0(tcg_env); gen_helper_fpop(tcg_env); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); break; case 0x10 ... 0x13: /* fcmovxx */ case 0x18 ... 0x1b: @@ -3268,7 +3268,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_helper_rdrand(s->T0, tcg_env); rm = (modrm & 7) | REX_B(s); gen_op_mov_reg_v(s, dflag, rm, s->T0); - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); break; default: @@ -3655,7 +3655,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } else { gen_helper_verw(tcg_env, s->T0); } - set_cc_op(s, CC_OP_EFLAGS); + assume_cc_op(s, CC_OP_EFLAGS); break; default: goto unknown_op; From 9594b593315a56ca459b8bc2de1cad5a91597f37 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 19:04:36 +0200 Subject: [PATCH 0902/2884] target/i386: document and group DISAS_* constants Place DISAS_* constants that update cpu_eip first, and the "jump" ones last. Add comments explaining the differences and usage. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 1b0485e01b..1246118e42 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -144,9 +144,28 @@ typedef struct DisasContext { TCGOp *prev_insn_end; } DisasContext; -#define DISAS_EOB_ONLY DISAS_TARGET_0 -#define DISAS_EOB_NEXT DISAS_TARGET_1 -#define DISAS_EOB_INHIBIT_IRQ DISAS_TARGET_2 +/* + * Point EIP to next instruction before ending translation. + * For instructions that can change hflags. + */ +#define DISAS_EOB_NEXT DISAS_TARGET_0 + +/* + * Point EIP to next instruction and set HF_INHIBIT_IRQ if not + * already set. For instructions that activate interrupt shadow. + */ +#define DISAS_EOB_INHIBIT_IRQ DISAS_TARGET_1 + +/* + * Return to the main loop; EIP might have already been updated + * but even in that case do not use lookup_and_goto_ptr(). + */ +#define DISAS_EOB_ONLY DISAS_TARGET_2 + +/* + * EIP has already been updated. For jumps that wish to use + * lookup_and_goto_ptr() + */ #define DISAS_JUMP DISAS_TARGET_3 /* The environment in which user-only runs is constrained. */ From c8494cb8b1fd73a1e6a3a6a022a7e4d7e481bdc1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 18:46:55 +0200 Subject: [PATCH 0903/2884] target/i386: avoid calling gen_eob_syscall before tb_stop syscall and sysret only have one exit, so they do not need to generate the end-of-translation code inline. It can be deferred to tb_stop. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 1246118e42..06aaaa00b4 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -168,6 +168,12 @@ typedef struct DisasContext { */ #define DISAS_JUMP DISAS_TARGET_3 +/* + * EIP has already been updated. Use updated value of + * EFLAGS.TF to determine singlestep trap (SYSCALL/SYSRET). + */ +#define DISAS_EOB_RECHECK_TF DISAS_TARGET_4 + /* The environment in which user-only runs is constrained. */ #ifdef CONFIG_USER_ONLY #define PE(S) true @@ -3587,7 +3593,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) /* TF handling for the syscall insn is different. The TF bit is checked after the syscall insn completes. This allows #DB to not be generated after one has entered CPL0 if TF is set in FMASK. */ - gen_eob_syscall(s); + s->base.is_jmp = DISAS_EOB_RECHECK_TF; break; case 0x107: /* sysret */ /* For Intel SYSRET is only valid in long mode */ @@ -3606,7 +3612,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) checked after the sysret insn completes. This allows #DB to be generated "as if" the syscall insn in userspace has just completed. */ - gen_eob_syscall(s); + s->base.is_jmp = DISAS_EOB_RECHECK_TF; } break; case 0x1a2: /* cpuid */ @@ -4810,6 +4816,9 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_EOB_ONLY: gen_eob(dc); break; + case DISAS_EOB_RECHECK_TF: + gen_eob_syscall(dc); + break; case DISAS_EOB_INHIBIT_IRQ: gen_update_eip_cur(dc); gen_eob_inhibit_irq(dc); From 2512f786bfe0a63fbf59cf0354d2b2ae40198a6f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 18:35:55 +0200 Subject: [PATCH 0904/2884] target/i386: avoid calling gen_eob_inhibit_irq before tb_stop sti only has one exit, so it does not need to generate the end-of-translation code inline. It can be deferred to tb_stop. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 4 +--- target/i386/tcg/translate.c | 13 ------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index e0ac21abe2..88bcb9699c 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3475,9 +3475,7 @@ static void gen_STD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) static void gen_STI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { gen_set_eflags(s, IF_MASK); - /* interruptions are enabled only the first insn after sti */ - gen_update_eip_next(s); - gen_eob_inhibit_irq(s); + s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 06aaaa00b4..a7493b5ccf 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -564,19 +564,6 @@ static void gen_update_eip_cur(DisasContext *s) s->pc_save = s->base.pc_next; } -static void gen_update_eip_next(DisasContext *s) -{ - assert(s->pc_save != -1); - if (tb_cflags(s->base.tb) & CF_PCREL) { - tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); - } else if (CODE64(s)) { - tcg_gen_movi_tl(cpu_eip, s->pc); - } else { - tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base)); - } - s->pc_save = s->pc; -} - static int cur_insn_len(DisasContext *s) { return s->pc - s->base.pc_next; From ad8f2ad77eb5b78d4478f900518d832af005ad40 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 18:38:32 +0200 Subject: [PATCH 0905/2884] target/i386: assert that gen_update_eip_cur and gen_update_eip_next are the same in tb_stop This is an invariant now that there are no calls to gen_eob_inhibit_irq() outside tb_stop. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index a7493b5ccf..fcb7934efa 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4798,6 +4798,7 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_jmp_rel_csize(dc, 0, 0); break; case DISAS_EOB_NEXT: + assert(dc->base.pc_next == dc->pc); gen_update_eip_cur(dc); /* fall through */ case DISAS_EOB_ONLY: @@ -4807,6 +4808,7 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_eob_syscall(dc); break; case DISAS_EOB_INHIBIT_IRQ: + assert(dc->base.pc_next == dc->pc); gen_update_eip_cur(dc); gen_eob_inhibit_irq(dc); break; From f5bd6a48ee129f7bc8335ad75e18d2f88b131745 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 16 May 2024 23:29:53 +0200 Subject: [PATCH 0906/2884] target/i386: raze the gen_eob* jungle Make gen_eob take the DISAS_* constant as an argument, so that it is not necessary to have wrappers around it. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 62 +++++++++---------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index fcb7934efa..46c452032b 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -260,8 +260,6 @@ STUB_HELPER(write_crN, TCGv_env env, TCGv_i32 reg, TCGv val) STUB_HELPER(wrmsr, TCGv_env env) #endif -static void gen_eob(DisasContext *s); -static void gen_jr(DisasContext *s); static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num); static void gen_jmp_rel_csize(DisasContext *s, int diff, int tb_num); static void gen_exception_gpf(DisasContext *s); @@ -2266,12 +2264,13 @@ static void gen_bnd_jmp(DisasContext *s) } } -/* Generate an end of block. Trace exception is also generated if needed. - If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. - If RECHECK_TF, emit a rechecking helper for #DB, ignoring the state of - S->TF. This is used by the syscall/sysret insns. */ +/* + * Generate an end of block, including common tasks such as generating + * single step traps, resetting the RF flag, and handling the interrupt + * shadow. + */ static void -gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) +gen_eob(DisasContext *s, int mode) { bool inhibit_reset; @@ -2282,52 +2281,29 @@ gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) if (s->flags & HF_INHIBIT_IRQ_MASK) { gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); inhibit_reset = true; - } else if (inhibit) { + } else if (mode == DISAS_EOB_INHIBIT_IRQ) { gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); } if (s->base.tb->flags & HF_RF_MASK) { gen_reset_eflags(s, RF_MASK); } - if (recheck_tf) { + if (mode == DISAS_EOB_RECHECK_TF) { gen_helper_rechecking_single_step(tcg_env); tcg_gen_exit_tb(NULL, 0); - } else if ((s->flags & HF_TF_MASK) && !inhibit) { + } else if ((s->flags & HF_TF_MASK) && mode != DISAS_EOB_INHIBIT_IRQ) { gen_helper_single_step(tcg_env); - } else if (jr && + } else if (mode == DISAS_JUMP && /* give irqs a chance to happen */ !inhibit_reset) { tcg_gen_lookup_and_goto_ptr(); } else { tcg_gen_exit_tb(NULL, 0); } + s->base.is_jmp = DISAS_NORETURN; } -static inline void -gen_eob_syscall(DisasContext *s) -{ - gen_eob_worker(s, false, true, false); -} - -/* End of block. Set HF_INHIBIT_IRQ_MASK if it isn't already set. */ -static void gen_eob_inhibit_irq(DisasContext *s) -{ - gen_eob_worker(s, true, false, false); -} - -/* End of block, resetting the inhibit irq flag. */ -static void gen_eob(DisasContext *s) -{ - gen_eob_worker(s, false, false, false); -} - -/* Jump to register */ -static void gen_jr(DisasContext *s) -{ - gen_eob_worker(s, false, false, true); -} - /* Jump to eip+diff, truncating the result to OT. */ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) { @@ -2379,9 +2355,9 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) tcg_gen_movi_tl(cpu_eip, new_eip); } if (s->jmp_opt) { - gen_jr(s); /* jump to another page */ + gen_eob(s, DISAS_JUMP); /* jump to another page */ } else { - gen_eob(s); /* exit to main loop */ + gen_eob(s, DISAS_EOB_ONLY); /* exit to main loop */ } } } @@ -4798,22 +4774,14 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_jmp_rel_csize(dc, 0, 0); break; case DISAS_EOB_NEXT: + case DISAS_EOB_INHIBIT_IRQ: assert(dc->base.pc_next == dc->pc); gen_update_eip_cur(dc); /* fall through */ case DISAS_EOB_ONLY: - gen_eob(dc); - break; case DISAS_EOB_RECHECK_TF: - gen_eob_syscall(dc); - break; - case DISAS_EOB_INHIBIT_IRQ: - assert(dc->base.pc_next == dc->pc); - gen_update_eip_cur(dc); - gen_eob_inhibit_irq(dc); - break; case DISAS_JUMP: - gen_jr(dc); + gen_eob(dc, dc->base.is_jmp); break; default: g_assert_not_reached(); From b3c49e654a0d382cca34dbb3bbf1022cffff8d0f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Apr 2024 15:04:13 +0200 Subject: [PATCH 0907/2884] target/i386: reg in gen_ldst_modrm is always OR_TMP0 Values other than OR_TMP0 were only ever used by MOV and MOVNTI opcodes. Now that these have been converted to the new decoder, remove the argument. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 46c452032b..4bb932af16 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1828,10 +1828,9 @@ static void gen_add_A0_ds_seg(DisasContext *s) gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); } -/* generate modrm memory load or store of 'reg'. TMP0 is used if reg == - OR_TMP0 */ +/* generate modrm memory load or store of 'reg'. */ static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, - MemOp ot, int reg, int is_store) + MemOp ot, int is_store) { int mod, rm; @@ -1839,24 +1838,16 @@ static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, rm = (modrm & 7) | REX_B(s); if (mod == 3) { if (is_store) { - if (reg != OR_TMP0) - gen_op_mov_v_reg(s, ot, s->T0, reg); gen_op_mov_reg_v(s, ot, rm, s->T0); } else { gen_op_mov_v_reg(s, ot, s->T0, rm); - if (reg != OR_TMP0) - gen_op_mov_reg_v(s, ot, reg, s->T0); } } else { gen_lea_modrm(env, s, modrm); if (is_store) { - if (reg != OR_TMP0) - gen_op_mov_v_reg(s, ot, s->T0, reg); gen_op_st_v(s, ot, s->T0, s->A0); } else { gen_op_ld_v(s, ot, s->T0, s->A0); - if (reg != OR_TMP0) - gen_op_mov_reg_v(s, ot, reg, s->T0); } } } @@ -3447,7 +3438,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) ot = dflag; modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, ot, 0); gen_extu(ot, s->T0); /* Note that lzcnt and tzcnt are in different extensions. */ @@ -3598,14 +3589,14 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, ldt.selector)); ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + gen_ldst_modrm(env, s, modrm, ot, 1); break; case 2: /* lldt */ if (!PE(s) || VM86(s)) goto illegal_op; if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_LDTR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, MO_16, 0); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); gen_helper_lldt(tcg_env, s->tmp2_i32); } @@ -3620,14 +3611,14 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, tr.selector)); ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + gen_ldst_modrm(env, s, modrm, ot, 1); break; case 3: /* ltr */ if (!PE(s) || VM86(s)) goto illegal_op; if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_TR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, MO_16, 0); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); gen_helper_ltr(tcg_env, s->tmp2_i32); } @@ -3636,7 +3627,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) case 5: /* verw */ if (!PE(s) || VM86(s)) goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, MO_16, 0); gen_update_cc_op(s); if (op == 4) { gen_helper_verr(tcg_env, s->T0); @@ -3900,7 +3891,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) */ mod = (modrm >> 6) & 3; ot = (mod != 3 ? MO_16 : s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + gen_ldst_modrm(env, s, modrm, ot, 1); break; case 0xee: /* rdpkru */ if (s->prefix & (PREFIX_LOCK | PREFIX_DATA @@ -3927,7 +3918,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) break; } gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, MO_16, 0); /* * Only the 4 lower bits of CR0 are modified. * PE cannot be set to zero if already set to one. @@ -3999,7 +3990,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) ot = dflag != MO_16 ? MO_32 : MO_16; modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, MO_16, 0); t0 = tcg_temp_new(); gen_update_cc_op(s); if (b == 0x102) { @@ -4503,7 +4494,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) reg = ((modrm >> 3) & 7) | REX_R(s); ot = dflag; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_ldst_modrm(env, s, modrm, ot, 0); gen_extu(ot, s->T0); tcg_gen_mov_tl(cpu_cc_src, s->T0); tcg_gen_ctpop_tl(s->T0, s->T0); From 420d60caad92644a0ebebb8a2b41dedc6635f530 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Apr 2024 15:04:13 +0200 Subject: [PATCH 0908/2884] target/i386: split gen_ldst_modrm for load and store The is_store argument of gen_ldst_modrm has only ever been passed a constant. Just split the function in two. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 52 +++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4bb932af16..afbed87056 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1828,27 +1828,33 @@ static void gen_add_A0_ds_seg(DisasContext *s) gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); } -/* generate modrm memory load or store of 'reg'. */ -static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, - MemOp ot, int is_store) +/* generate modrm load of memory or register. */ +static void gen_ld_modrm(CPUX86State *env, DisasContext *s, int modrm, MemOp ot) { int mod, rm; mod = (modrm >> 6) & 3; rm = (modrm & 7) | REX_B(s); if (mod == 3) { - if (is_store) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } + gen_op_mov_v_reg(s, ot, s->T0, rm); } else { gen_lea_modrm(env, s, modrm); - if (is_store) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_ld_v(s, ot, s->T0, s->A0); - } + gen_op_ld_v(s, ot, s->T0, s->A0); + } +} + +/* generate modrm store of memory or register. */ +static void gen_st_modrm(CPUX86State *env, DisasContext *s, int modrm, MemOp ot) +{ + int mod, rm; + + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod == 3) { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + gen_lea_modrm(env, s, modrm); + gen_op_st_v(s, ot, s->T0, s->A0); } } @@ -3438,7 +3444,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) ot = dflag; modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ldst_modrm(env, s, modrm, ot, 0); + gen_ld_modrm(env, s, modrm, ot); gen_extu(ot, s->T0); /* Note that lzcnt and tzcnt are in different extensions. */ @@ -3589,14 +3595,14 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, ldt.selector)); ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, 1); + gen_st_modrm(env, s, modrm, ot); break; case 2: /* lldt */ if (!PE(s) || VM86(s)) goto illegal_op; if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_LDTR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, 0); + gen_ld_modrm(env, s, modrm, MO_16); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); gen_helper_lldt(tcg_env, s->tmp2_i32); } @@ -3611,14 +3617,14 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, tr.selector)); ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, 1); + gen_st_modrm(env, s, modrm, ot); break; case 3: /* ltr */ if (!PE(s) || VM86(s)) goto illegal_op; if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_TR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, 0); + gen_ld_modrm(env, s, modrm, MO_16); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); gen_helper_ltr(tcg_env, s->tmp2_i32); } @@ -3627,7 +3633,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) case 5: /* verw */ if (!PE(s) || VM86(s)) goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, 0); + gen_ld_modrm(env, s, modrm, MO_16); gen_update_cc_op(s); if (op == 4) { gen_helper_verr(tcg_env, s->T0); @@ -3891,7 +3897,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) */ mod = (modrm >> 6) & 3; ot = (mod != 3 ? MO_16 : s->dflag); - gen_ldst_modrm(env, s, modrm, ot, 1); + gen_st_modrm(env, s, modrm, ot); break; case 0xee: /* rdpkru */ if (s->prefix & (PREFIX_LOCK | PREFIX_DATA @@ -3918,7 +3924,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) break; } gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); - gen_ldst_modrm(env, s, modrm, MO_16, 0); + gen_ld_modrm(env, s, modrm, MO_16); /* * Only the 4 lower bits of CR0 are modified. * PE cannot be set to zero if already set to one. @@ -3990,7 +3996,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) ot = dflag != MO_16 ? MO_32 : MO_16; modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ldst_modrm(env, s, modrm, MO_16, 0); + gen_ld_modrm(env, s, modrm, MO_16); t0 = tcg_temp_new(); gen_update_cc_op(s); if (b == 0x102) { @@ -4494,7 +4500,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) reg = ((modrm >> 3) & 7) | REX_R(s); ot = dflag; - gen_ldst_modrm(env, s, modrm, ot, 0); + gen_ld_modrm(env, s, modrm, ot); gen_extu(ot, s->T0); tcg_gen_mov_tl(cpu_cc_src, s->T0); tcg_gen_ctpop_tl(s->T0, s->T0); From d0e31d6d3740726db067e2e49d0593b4ce85ccfe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Apr 2024 15:31:58 +0200 Subject: [PATCH 0909/2884] target/i386: inline gen_add_A0_ds_seg It is only used in MONITOR, where a direct call of gen_lea_v_seg is simpler, and in XLAT. Inline it in the latter. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 2 +- target/i386/tcg/translate.c | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 88bcb9699c..01ad57629e 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -4043,7 +4043,7 @@ static void gen_XLAT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { /* AL is already zero-extended into s->T0. */ tcg_gen_add_tl(s->A0, cpu_regs[R_EBX], s->T0); - gen_add_A0_ds_seg(s); + gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); gen_op_ld_v(s, MO_8, s->T0, s->A0); } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index afbed87056..2039ccf283 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1822,12 +1822,6 @@ static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, gen_helper_bndck(tcg_env, s->tmp2_i32); } -/* used for LEA and MOV AX, mem */ -static void gen_add_A0_ds_seg(DisasContext *s) -{ - gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); -} - /* generate modrm load of memory or register. */ static void gen_ld_modrm(CPUX86State *env, DisasContext *s, int modrm, MemOp ot) { @@ -3674,8 +3668,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_update_cc_op(s); gen_update_eip_cur(s); - tcg_gen_mov_tl(s->A0, cpu_regs[R_EAX]); - gen_add_A0_ds_seg(s); + gen_lea_v_seg(s, s->aflag, cpu_regs[R_EAX], R_DS, s->override); gen_helper_monitor(tcg_env, s->A0); break; From 20237d4070e8fc5dae743f0286dda11375b7c474 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Apr 2024 16:00:54 +0200 Subject: [PATCH 0910/2884] target/i386: use mo_stacksize more Use mo_stacksize for all stack accesses, including when a 64-bit code segment is impossible and the code is therefore checking only for SS32(s). Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 2039ccf283..2a20f9bafb 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2075,12 +2075,12 @@ static inline void gen_pop_update(DisasContext *s, MemOp ot) static inline void gen_stack_A0(DisasContext *s) { - gen_lea_v_seg(s, SS32(s) ? MO_32 : MO_16, cpu_regs[R_ESP], R_SS, -1); + gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1); } static void gen_pusha(DisasContext *s) { - MemOp s_ot = SS32(s) ? MO_32 : MO_16; + MemOp s_ot = mo_stacksize(s); MemOp d_ot = s->dflag; int size = 1 << d_ot; int i; @@ -2096,7 +2096,7 @@ static void gen_pusha(DisasContext *s) static void gen_popa(DisasContext *s) { - MemOp s_ot = SS32(s) ? MO_32 : MO_16; + MemOp s_ot = mo_stacksize(s); MemOp d_ot = s->dflag; int size = 1 << d_ot; int i; @@ -2118,7 +2118,7 @@ static void gen_popa(DisasContext *s) static void gen_enter(DisasContext *s, int esp_addend, int level) { MemOp d_ot = mo_pushpop(s, s->dflag); - MemOp a_ot = CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16; + MemOp a_ot = mo_stacksize(s); int size = 1 << d_ot; /* Push BP; compute FrameTemp into T1. */ From f0e754d3ce52b25e6bea0ff04cecd8f0b111c1d6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Apr 2024 16:58:15 +0200 Subject: [PATCH 0911/2884] target/i386: introduce gen_lea_ss_ofs Generalize gen_stack_A0() to include an initial add and to use an arbitrary destination. This is a common pattern and it is not a huge burden to add the extra arguments to the only caller of gen_stack_A0(). Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 2 +- target/i386/tcg/translate.c | 51 +++++++++++++++---------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 01ad57629e..0a13be4989 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3077,7 +3077,7 @@ static void gen_RETF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; if (!PE(s) || VM86(s)) { - gen_stack_A0(s); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], 0); /* pop offset */ gen_op_ld_v(s, s->dflag, s->T0, s->A0); /* NOTE: keeping EIP updated is not a problem in case of diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 2a20f9bafb..15993f8302 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2035,24 +2035,27 @@ static inline void gen_stack_update(DisasContext *s, int addend) gen_op_add_reg_im(s, mo_stacksize(s), R_ESP, addend); } +static void gen_lea_ss_ofs(DisasContext *s, TCGv dest, TCGv src, target_ulong offset) +{ + if (offset) { + tcg_gen_addi_tl(dest, src, offset); + src = dest; + } + gen_lea_v_seg_dest(s, mo_stacksize(s), dest, src, R_SS, -1); +} + /* Generate a push. It depends on ss32, addseg and dflag. */ static void gen_push_v(DisasContext *s, TCGv val) { MemOp d_ot = mo_pushpop(s, s->dflag); MemOp a_ot = mo_stacksize(s); int size = 1 << d_ot; - TCGv new_esp = s->A0; + TCGv new_esp = tcg_temp_new(); - tcg_gen_subi_tl(s->A0, cpu_regs[R_ESP], size); - - if (!CODE64(s)) { - if (ADDSEG(s)) { - new_esp = tcg_temp_new(); - tcg_gen_mov_tl(new_esp, s->A0); - } - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); - } + tcg_gen_subi_tl(new_esp, cpu_regs[R_ESP], size); + /* Now reduce the value to the address size and apply SS base. */ + gen_lea_ss_ofs(s, s->A0, new_esp, 0); gen_op_st_v(s, d_ot, val, s->A0); gen_op_mov_reg_v(s, a_ot, R_ESP, new_esp); } @@ -2062,7 +2065,7 @@ static MemOp gen_pop_T0(DisasContext *s) { MemOp d_ot = mo_pushpop(s, s->dflag); - gen_lea_v_seg_dest(s, mo_stacksize(s), s->T0, cpu_regs[R_ESP], R_SS, -1); + gen_lea_ss_ofs(s, s->T0, cpu_regs[R_ESP], 0); gen_op_ld_v(s, d_ot, s->T0, s->T0); return d_ot; @@ -2073,21 +2076,14 @@ static inline void gen_pop_update(DisasContext *s, MemOp ot) gen_stack_update(s, 1 << ot); } -static inline void gen_stack_A0(DisasContext *s) -{ - gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1); -} - static void gen_pusha(DisasContext *s) { - MemOp s_ot = mo_stacksize(s); MemOp d_ot = s->dflag; int size = 1 << d_ot; int i; for (i = 0; i < 8; i++) { - tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], (i - 8) * size); - gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], (i - 8) * size); gen_op_st_v(s, d_ot, cpu_regs[7 - i], s->A0); } @@ -2096,7 +2092,6 @@ static void gen_pusha(DisasContext *s) static void gen_popa(DisasContext *s) { - MemOp s_ot = mo_stacksize(s); MemOp d_ot = s->dflag; int size = 1 << d_ot; int i; @@ -2106,8 +2101,7 @@ static void gen_popa(DisasContext *s) if (7 - i == R_ESP) { continue; } - tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], i * size); - gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], i * size); gen_op_ld_v(s, d_ot, s->T0, s->A0); gen_op_mov_reg_v(s, d_ot, 7 - i, s->T0); } @@ -2123,7 +2117,7 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) /* Push BP; compute FrameTemp into T1. */ tcg_gen_subi_tl(s->T1, cpu_regs[R_ESP], size); - gen_lea_v_seg(s, a_ot, s->T1, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, s->T1, 0); gen_op_st_v(s, d_ot, cpu_regs[R_EBP], s->A0); level &= 31; @@ -2132,18 +2126,15 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) /* Copy level-1 pointers from the previous frame. */ for (i = 1; i < level; ++i) { - tcg_gen_subi_tl(s->A0, cpu_regs[R_EBP], size * i); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], -size * i); gen_op_ld_v(s, d_ot, s->tmp0, s->A0); - tcg_gen_subi_tl(s->A0, s->T1, size * i); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, s->T1, -size * i); gen_op_st_v(s, d_ot, s->tmp0, s->A0); } /* Push the current FrameTemp as the last level. */ - tcg_gen_subi_tl(s->A0, s->T1, size * level); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, s->T1, -size * level); gen_op_st_v(s, d_ot, s->T1, s->A0); } @@ -2160,7 +2151,7 @@ static void gen_leave(DisasContext *s) MemOp d_ot = mo_pushpop(s, s->dflag); MemOp a_ot = mo_stacksize(s); - gen_lea_v_seg(s, a_ot, cpu_regs[R_EBP], R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], 0); gen_op_ld_v(s, d_ot, s->T0, s->A0); tcg_gen_addi_tl(s->T1, cpu_regs[R_EBP], 1 << d_ot); From 6605817b1ad734d29efebcdca7c4baeeec3a65c7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Apr 2024 16:49:09 +0200 Subject: [PATCH 0912/2884] target/i386: clean up repeated string operations Do not bother generating inline wrappers for gen_repz and gen_repz2; use s->prefix to separate REPZ from REPNZ in the case of SCAS and CMPS. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 22 +++++++++------------- target/i386/tcg/translate.c | 22 ++++------------------ 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 0a13be4989..377d2201c9 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1508,10 +1508,8 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec static void gen_CMPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; - if (s->prefix & PREFIX_REPNZ) { - gen_repz_cmps(s, ot, 1); - } else if (s->prefix & PREFIX_REPZ) { - gen_repz_cmps(s, ot, 0); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_nz(s, ot, gen_cmps); } else { gen_cmps(s, ot); } @@ -1834,7 +1832,7 @@ static void gen_INS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) translator_io_start(&s->base); if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_ins(s, ot); + gen_repz(s, ot, gen_ins); } else { gen_ins(s, ot); } @@ -1993,7 +1991,7 @@ static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_lods(s, ot); + gen_repz(s, ot, gen_lods); } else { gen_lods(s, ot); } @@ -2155,7 +2153,7 @@ static void gen_MOVS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_movs(s, ot); + gen_repz(s, ot, gen_movs); } else { gen_movs(s, ot); } @@ -2321,7 +2319,7 @@ static void gen_OUTS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) translator_io_start(&s->base); if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_outs(s, ot); + gen_repz(s, ot, gen_outs); } else { gen_outs(s, ot); } @@ -3329,10 +3327,8 @@ static void gen_SBB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) static void gen_SCAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; - if (s->prefix & PREFIX_REPNZ) { - gen_repz_scas(s, ot, 1); - } else if (s->prefix & PREFIX_REPZ) { - gen_repz_scas(s, ot, 0); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_nz(s, ot, gen_scas); } else { gen_scas(s, ot); } @@ -3495,7 +3491,7 @@ static void gen_STOS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_stos(s, ot); + gen_repz(s, ot, gen_stos); } else { gen_stos(s, ot); } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 15993f8302..7dd7ebf60d 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1327,14 +1327,12 @@ static void gen_repz(DisasContext *s, MemOp ot, gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } -#define GEN_REPZ(op) \ - static inline void gen_repz_ ## op(DisasContext *s, MemOp ot) \ - { gen_repz(s, ot, gen_##op); } - -static void gen_repz2(DisasContext *s, MemOp ot, int nz, - void (*fn)(DisasContext *s, MemOp ot)) +static void gen_repz_nz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot)) { TCGLabel *l2; + int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; + l2 = gen_jz_ecx_string(s); fn(s, ot); gen_op_add_reg_im(s, s->aflag, R_ECX, -1); @@ -1350,18 +1348,6 @@ static void gen_repz2(DisasContext *s, MemOp ot, int nz, gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } -#define GEN_REPZ2(op) \ - static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, int nz) \ - { gen_repz2(s, ot, nz, gen_##op); } - -GEN_REPZ(movs) -GEN_REPZ(stos) -GEN_REPZ(lods) -GEN_REPZ(ins) -GEN_REPZ(outs) -GEN_REPZ2(scas) -GEN_REPZ2(cmps) - static void gen_helper_fp_arith_ST0_FT0(int op) { switch (op) { From 56da568f88cc5537d49e0bfc793b0ca105605947 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 16:59:34 +0200 Subject: [PATCH 0913/2884] target/i386: remove aflag argument of gen_lea_v_seg It is always s->aflag. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 6 +++--- target/i386/tcg/translate.c | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 377d2201c9..e990141454 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -76,7 +76,7 @@ static void gen_NM_exception(DisasContext *s) static void gen_load_ea(DisasContext *s, AddressParts *mem, bool is_vsib) { TCGv ea = gen_lea_modrm_1(s, *mem, is_vsib); - gen_lea_v_seg(s, s->aflag, ea, mem->def_seg, s->override); + gen_lea_v_seg(s, ea, mem->def_seg, s->override); } static inline int mmx_offset(MemOp ot) @@ -2044,7 +2044,7 @@ static void gen_MOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { - gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_DS, s->override); + gen_lea_v_seg(s, cpu_regs[R_EDI], R_DS, s->override); if (s->prefix & PREFIX_DATA) { gen_helper_maskmov_xmm(tcg_env, OP_PTR1, OP_PTR2, s->A0); @@ -4039,7 +4039,7 @@ static void gen_XLAT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { /* AL is already zero-extended into s->T0. */ tcg_gen_add_tl(s->A0, cpu_regs[R_EBX], s->T0); - gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); + gen_lea_v_seg(s, s->A0, R_DS, s->override); gen_op_ld_v(s, MO_8, s->T0, s->A0); } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 7dd7ebf60d..6dedfe94c0 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -680,20 +680,20 @@ static void gen_lea_v_seg_dest(DisasContext *s, MemOp aflag, TCGv dest, TCGv a0, } } -static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, +static void gen_lea_v_seg(DisasContext *s, TCGv a0, int def_seg, int ovr_seg) { - gen_lea_v_seg_dest(s, aflag, s->A0, a0, def_seg, ovr_seg); + gen_lea_v_seg_dest(s, s->aflag, s->A0, a0, def_seg, ovr_seg); } static inline void gen_string_movl_A0_ESI(DisasContext *s) { - gen_lea_v_seg(s, s->aflag, cpu_regs[R_ESI], R_DS, s->override); + gen_lea_v_seg(s, cpu_regs[R_ESI], R_DS, s->override); } static inline void gen_string_movl_A0_EDI(DisasContext *s) { - gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); + gen_lea_v_seg(s, cpu_regs[R_EDI], R_ES, -1); } static inline TCGv gen_compute_Dshift(DisasContext *s, MemOp ot) @@ -1784,7 +1784,7 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) { AddressParts a = gen_lea_modrm_0(env, s, modrm); TCGv ea = gen_lea_modrm_1(s, a, false); - gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); + gen_lea_v_seg(s, ea, a.def_seg, s->override); } static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) @@ -2523,7 +2523,7 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) bool update_fdp = true; tcg_gen_mov_tl(last_addr, ea); - gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); + gen_lea_v_seg(s, ea, a.def_seg, s->override); switch (op) { case 0x00 ... 0x07: /* fxxxs */ @@ -3320,7 +3320,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) tcg_gen_sari_tl(s->tmp0, s->T1, 3 + ot); tcg_gen_shli_tl(s->tmp0, s->tmp0, ot); tcg_gen_add_tl(s->A0, gen_lea_modrm_1(s, a, false), s->tmp0); - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->A0, a.def_seg, s->override); if (!(s->prefix & PREFIX_LOCK)) { gen_op_ld_v(s, ot, s->T0, s->A0); } @@ -3645,7 +3645,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_lea_v_seg(s, s->aflag, cpu_regs[R_EAX], R_DS, s->override); + gen_lea_v_seg(s, cpu_regs[R_EAX], R_DS, s->override); gen_helper_monitor(tcg_env, s->A0); break; @@ -4051,7 +4051,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } else { tcg_gen_movi_tl(s->A0, 0); } - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->A0, a.def_seg, s->override); if (a.index >= 0) { tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); } else { @@ -4156,7 +4156,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } else { tcg_gen_movi_tl(s->A0, 0); } - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->A0, a.def_seg, s->override); if (a.index >= 0) { tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); } else { From 0241e0fedab48a3da15b12a65818120ba566536f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 11:32:27 +0200 Subject: [PATCH 0914/2884] meson: remove unnecessary reference to libm libm is linked into all targets via libqemuutil, no need to specify it explicitly. Signed-off-by: Paolo Bonzini --- block/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/meson.build b/block/meson.build index e1f03fd773..8993055c75 100644 --- a/block/meson.build +++ b/block/meson.build @@ -110,7 +110,7 @@ foreach m : [ [blkio, 'blkio', files('blkio.c')], [curl, 'curl', files('curl.c')], [glusterfs, 'gluster', files('gluster.c')], - [libiscsi, 'iscsi', [files('iscsi.c'), libm]], + [libiscsi, 'iscsi', files('iscsi.c')], [libnfs, 'nfs', files('nfs.c')], [libssh, 'ssh', files('ssh.c')], [rbd, 'rbd', files('rbd.c')], From 2bfd3c4860a0058ff65a5da16f4d2609ac70b580 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 12:06:10 +0200 Subject: [PATCH 0915/2884] meson: remove unnecessary dependency The dbus_display1_dep is not really used since all occurrences also request gio independently. Just list the generated sources and drop dbus_display1_dep. Signed-off-by: Paolo Bonzini --- audio/meson.build | 4 ++-- tests/qtest/meson.build | 2 +- ui/meson.build | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/audio/meson.build b/audio/meson.build index 608f35e6af..59f0a431d5 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -30,8 +30,8 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: [gio, dbus_display1_dep, pixman], - if_true: files('dbusaudio.c')) + module_ss.add(when: [gio, pixman], + if_true: [dbus_display1, files('dbusaudio.c')]) audio_modules += {'dbus': module_ss} endif diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 86293051dc..b98fae6a6d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -354,7 +354,7 @@ if vnc.found() endif if dbus_display - qtests += {'dbus-display-test': [dbus_display1_dep, gio]} + qtests += {'dbus-display-test': [dbus_display1, gio]} endif qtest_executables = {} diff --git a/ui/meson.build b/ui/meson.build index 5d89986b0e..cfbf29428d 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -91,8 +91,7 @@ if dbus_display '--interface-prefix', 'org.qemu.', '--c-namespace', 'QemuDBus', '--generate-c-code', '@BASENAME@']) - dbus_display1_dep = declare_dependency(sources: dbus_display1, dependencies: gio) - dbus_ss.add(when: [gio, dbus_display1_dep], + dbus_ss.add(when: gio, if_true: [files( 'dbus-chardev.c', 'dbus-clipboard.c', @@ -100,7 +99,7 @@ if dbus_display 'dbus-error.c', 'dbus-listener.c', 'dbus.c', - ), opengl, gbm, pixman]) + ), opengl, gbm, pixman, dbus_display1]) ui_modules += {'dbus' : dbus_ss} endif From 61306156d976b1235e26dc1658ccaae2170ce185 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 10:54:50 +0200 Subject: [PATCH 0916/2884] tcg: include dependencies in static_library() This ensures that for example libffi can be reached even if it is not in /usr/include. Signed-off-by: Paolo Bonzini --- tcg/meson.build | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tcg/meson.build b/tcg/meson.build index 8251589fd4..ffbe754d8b 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -32,19 +32,19 @@ tcg_ss = tcg_ss.apply({}) libtcg_user = static_library('tcg_user', tcg_ss.sources() + genh, name_suffix: 'fa', + dependencies: tcg_ss.dependencies(), c_args: '-DCONFIG_USER_ONLY', build_by_default: false) -tcg_user = declare_dependency(link_with: libtcg_user, - dependencies: tcg_ss.dependencies()) +tcg_user = declare_dependency(link_with: libtcg_user) user_ss.add(tcg_user) libtcg_system = static_library('tcg_system', tcg_ss.sources() + genh, name_suffix: 'fa', + dependencies: tcg_ss.dependencies(), c_args: '-DCONFIG_SOFTMMU', build_by_default: false) -tcg_system = declare_dependency(link_with: libtcg_system, - dependencies: tcg_ss.dependencies()) +tcg_system = declare_dependency(link_with: libtcg_system) system_ss.add(tcg_system) From ae9433fb1140f40143d877623b3c0f030d96a99f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 11:21:35 +0200 Subject: [PATCH 0917/2884] meson: do not query modules before they are processed Signed-off-by: Paolo Bonzini --- block/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/meson.build b/block/meson.build index 8993055c75..158dc3b89d 100644 --- a/block/meson.build +++ b/block/meson.build @@ -119,7 +119,7 @@ foreach m : [ module_ss = ss.source_set() module_ss.add(when: m[0], if_true: m[2]) if enable_modules - modsrc += module_ss.all_sources() + modsrc += m[2] endif block_modules += {m[1] : module_ss} endif From 70eb5fde05bdd051c087669ffcf2aee39e0c8170 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 18:16:08 +0200 Subject: [PATCH 0918/2884] migration: remove unnecessary zlib dependency zlib code is only used by the emulators, not by the tests. Signed-off-by: Paolo Bonzini --- meson.build | 2 +- migration/dirtyrate.c | 1 - migration/meson.build | 2 +- migration/qemu-file.c | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 7fd82b5f48..6386607144 100644 --- a/meson.build +++ b/meson.build @@ -3696,7 +3696,7 @@ libmigration = static_library('migration', sources: migration_files + genh, name_suffix: 'fa', build_by_default: false) migration = declare_dependency(link_with: libmigration, - dependencies: [zlib, qom, io]) + dependencies: [qom, io]) system_ss.add(migration) block_ss = block_ss.apply({}) diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index d02d70b7b4..1d9db81299 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include #include "hw/core/cpu.h" #include "qapi/error.h" #include "exec/ramblock.h" diff --git a/migration/meson.build b/migration/meson.build index 8815f80837..bdc3244bce 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -29,7 +29,7 @@ system_ss.add(files( 'socket.c', 'tls.c', 'threadinfo.c', -), gnutls) +), gnutls, zlib) if get_option('replication').allowed() system_ss.add(files('colo-failover.c', 'colo.c')) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 9ccbbb0099..b6d2f588bd 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include #include "qemu/madvise.h" #include "qemu/error-report.h" #include "qemu/iov.h" From 24f68139247fd5a265874c743c46f293bd3432fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 7 Apr 2024 23:26:18 -1000 Subject: [PATCH 0919/2884] target/i386: Add tcg/access.[ch] Provide a method to amortize page lookup across large blocks. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/access.c | 169 ++++++++++++++++++++++++++++++++++++ target/i386/tcg/access.h | 40 +++++++++ target/i386/tcg/meson.build | 1 + 3 files changed, 210 insertions(+) create mode 100644 target/i386/tcg/access.c create mode 100644 target/i386/tcg/access.h diff --git a/target/i386/tcg/access.c b/target/i386/tcg/access.c new file mode 100644 index 0000000000..56a1181ea5 --- /dev/null +++ b/target/i386/tcg/access.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Access guest memory in blocks. */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "access.h" + + +void access_prepare_mmu(X86Access *ret, CPUX86State *env, + vaddr vaddr, unsigned size, + MMUAccessType type, int mmu_idx, uintptr_t ra) +{ + int size1, size2; + void *haddr1, *haddr2; + + assert(size > 0 && size <= TARGET_PAGE_SIZE); + + size1 = MIN(size, -(vaddr | TARGET_PAGE_MASK)), + size2 = size - size1; + + memset(ret, 0, sizeof(*ret)); + ret->vaddr = vaddr; + ret->size = size; + ret->size1 = size1; + ret->mmu_idx = mmu_idx; + ret->env = env; + ret->ra = ra; + + haddr1 = probe_access(env, vaddr, size1, type, mmu_idx, ra); + ret->haddr1 = haddr1; + + if (unlikely(size2)) { + haddr2 = probe_access(env, vaddr + size1, size2, type, mmu_idx, ra); + if (haddr2 == haddr1 + size1) { + ret->size1 = size; + } else { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + ret->haddr2 = haddr2; +#endif + } + } +} + +void access_prepare(X86Access *ret, CPUX86State *env, vaddr vaddr, + unsigned size, MMUAccessType type, uintptr_t ra) +{ + int mmu_idx = cpu_mmu_index(env_cpu(env), false); + access_prepare_mmu(ret, env, vaddr, size, type, mmu_idx, ra); +} + +static void *access_ptr(X86Access *ac, vaddr addr, unsigned len) +{ + vaddr offset = addr - ac->vaddr; + + assert(addr >= ac->vaddr); + +#ifdef CONFIG_USER_ONLY + assert(offset <= ac->size1 - len); + return ac->haddr1 + offset; +#else + if (likely(offset <= ac->size1 - len)) { + return ac->haddr1 + offset; + } + assert(offset <= ac->size - len); + /* + * If the address is not naturally aligned, it might span both pages. + * Only return ac->haddr2 if the area is entirely within the second page, + * otherwise fall back to slow accesses. + */ + if (likely(offset >= ac->size1)) { + return ac->haddr2 + (offset - ac->size1); + } + return NULL; +#endif +} + +#ifdef CONFIG_USER_ONLY +# define test_ptr(p) true +#else +# define test_ptr(p) likely(p) +#endif + +uint8_t access_ldb(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint8_t)); + + if (test_ptr(p)) { + return ldub_p(p); + } + return cpu_ldub_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +uint16_t access_ldw(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint16_t)); + + if (test_ptr(p)) { + return lduw_le_p(p); + } + return cpu_lduw_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +uint32_t access_ldl(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint32_t)); + + if (test_ptr(p)) { + return ldl_le_p(p); + } + return cpu_ldl_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +uint64_t access_ldq(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint64_t)); + + if (test_ptr(p)) { + return ldq_le_p(p); + } + return cpu_ldq_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +void access_stb(X86Access *ac, vaddr addr, uint8_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint8_t)); + + if (test_ptr(p)) { + stb_p(p, val); + } else { + cpu_stb_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} + +void access_stw(X86Access *ac, vaddr addr, uint16_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint16_t)); + + if (test_ptr(p)) { + stw_le_p(p, val); + } else { + cpu_stw_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} + +void access_stl(X86Access *ac, vaddr addr, uint32_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint32_t)); + + if (test_ptr(p)) { + stl_le_p(p, val); + } else { + cpu_stl_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} + +void access_stq(X86Access *ac, vaddr addr, uint64_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint64_t)); + + if (test_ptr(p)) { + stq_le_p(p, val); + } else { + cpu_stq_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} diff --git a/target/i386/tcg/access.h b/target/i386/tcg/access.h new file mode 100644 index 0000000000..d70808a3a3 --- /dev/null +++ b/target/i386/tcg/access.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Access guest memory in blocks. */ + +#ifndef X86_TCG_ACCESS_H +#define X86_TCG_ACCESS_H + +/* An access covers at most sizeof(X86XSaveArea), at most 2 pages. */ +typedef struct X86Access { + target_ulong vaddr; + void *haddr1; + void *haddr2; + uint16_t size; + uint16_t size1; + /* + * If we can't access the host page directly, we'll have to do I/O access + * via ld/st helpers. These are internal details, so we store the rest + * to do the access here instead of passing it around in the helpers. + */ + int mmu_idx; + CPUX86State *env; + uintptr_t ra; +} X86Access; + +void access_prepare_mmu(X86Access *ret, CPUX86State *env, + vaddr vaddr, unsigned size, + MMUAccessType type, int mmu_idx, uintptr_t ra); +void access_prepare(X86Access *ret, CPUX86State *env, vaddr vaddr, + unsigned size, MMUAccessType type, uintptr_t ra); + +uint8_t access_ldb(X86Access *ac, vaddr addr); +uint16_t access_ldw(X86Access *ac, vaddr addr); +uint32_t access_ldl(X86Access *ac, vaddr addr); +uint64_t access_ldq(X86Access *ac, vaddr addr); + +void access_stb(X86Access *ac, vaddr addr, uint8_t val); +void access_stw(X86Access *ac, vaddr addr, uint16_t val); +void access_stl(X86Access *ac, vaddr addr, uint32_t val); +void access_stq(X86Access *ac, vaddr addr, uint64_t val); + +#endif diff --git a/target/i386/tcg/meson.build b/target/i386/tcg/meson.build index f9110e890c..1105b35d92 100644 --- a/target/i386/tcg/meson.build +++ b/target/i386/tcg/meson.build @@ -1,4 +1,5 @@ i386_ss.add(when: 'CONFIG_TCG', if_true: files( + 'access.c', 'bpt_helper.c', 'cc_helper.c', 'excp_helper.c', From d3e8b648ab7f94f0e9235ade9977954e426991a0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 7 Apr 2024 23:50:46 -1000 Subject: [PATCH 0920/2884] target/i386: Convert do_fldt, do_fstt to X86Access Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 44 +++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index ece22a3553..1662643a8f 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -27,6 +27,7 @@ #include "fpu/softfloat.h" #include "fpu/softfloat-macros.h" #include "helper-tcg.h" +#include "access.h" /* float macros */ #define FT0 (env->ft0) @@ -84,23 +85,22 @@ static inline void fpop(CPUX86State *env) env->fpstt = (env->fpstt + 1) & 7; } -static floatx80 do_fldt(CPUX86State *env, target_ulong ptr, uintptr_t retaddr) +static floatx80 do_fldt(X86Access *ac, target_ulong ptr) { CPU_LDoubleU temp; - temp.l.lower = cpu_ldq_data_ra(env, ptr, retaddr); - temp.l.upper = cpu_lduw_data_ra(env, ptr + 8, retaddr); + temp.l.lower = access_ldq(ac, ptr); + temp.l.upper = access_ldw(ac, ptr + 8); return temp.d; } -static void do_fstt(CPUX86State *env, floatx80 f, target_ulong ptr, - uintptr_t retaddr) +static void do_fstt(X86Access *ac, target_ulong ptr, floatx80 f) { CPU_LDoubleU temp; temp.d = f; - cpu_stq_data_ra(env, ptr, temp.l.lower, retaddr); - cpu_stw_data_ra(env, ptr + 8, temp.l.upper, retaddr); + access_stq(ac, ptr, temp.l.lower); + access_stw(ac, ptr + 8, temp.l.upper); } /* x87 FPU helpers */ @@ -382,16 +382,22 @@ int64_t helper_fisttll_ST0(CPUX86State *env) void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) { int new_fpstt; + X86Access ac; + + access_prepare(&ac, env, ptr, 10, MMU_DATA_LOAD, GETPC()); new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = do_fldt(env, ptr, GETPC()); + env->fpregs[new_fpstt].d = do_fldt(&ac, ptr); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) { - do_fstt(env, ST0, ptr, GETPC()); + X86Access ac; + + access_prepare(&ac, env, ptr, 10, MMU_DATA_STORE, GETPC()); + do_fstt(&ac, ptr, ST0); } void helper_fpush(CPUX86State *env) @@ -2460,15 +2466,18 @@ void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, uintptr_t retaddr) { + X86Access ac; floatx80 tmp; int i; do_fstenv(env, ptr, data32, retaddr); ptr += (target_ulong)14 << data32; + access_prepare(&ac, env, ptr, 80, MMU_DATA_STORE, retaddr); + for (i = 0; i < 8; i++) { tmp = ST(i); - do_fstt(env, tmp, ptr, retaddr); + do_fstt(&ac, ptr, tmp); ptr += 10; } @@ -2483,14 +2492,17 @@ void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, uintptr_t retaddr) { + X86Access ac; floatx80 tmp; int i; do_fldenv(env, ptr, data32, retaddr); ptr += (target_ulong)14 << data32; + access_prepare(&ac, env, ptr, 80, MMU_DATA_LOAD, retaddr); + for (i = 0; i < 8; i++) { - tmp = do_fldt(env, ptr, retaddr); + tmp = do_fldt(&ac, ptr); ST(i) = tmp; ptr += 10; } @@ -2507,6 +2519,7 @@ static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) { int fpus, fptag, i; target_ulong addr; + X86Access ac; fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; @@ -2525,9 +2538,11 @@ static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) cpu_stq_data_ra(env, ptr + XO(legacy.fpdp), 0, ra); /* edp+sel; rdp */ addr = ptr + XO(legacy.fpregs); + access_prepare(&ac, env, addr, 8 * 16, MMU_DATA_STORE, ra); + for (i = 0; i < 8; i++) { floatx80 tmp = ST(i); - do_fstt(env, tmp, addr, ra); + do_fstt(&ac, addr, tmp); addr += 16; } } @@ -2700,6 +2715,7 @@ static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) { int i, fpuc, fpus, fptag; target_ulong addr; + X86Access ac; fpuc = cpu_lduw_data_ra(env, ptr + XO(legacy.fcw), ra); fpus = cpu_lduw_data_ra(env, ptr + XO(legacy.fsw), ra); @@ -2712,8 +2728,10 @@ static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) } addr = ptr + XO(legacy.fpregs); + access_prepare(&ac, env, addr, 8 * 16, MMU_DATA_LOAD, ra); + for (i = 0; i < 8; i++) { - floatx80 tmp = do_fldt(env, addr, ra); + floatx80 tmp = do_fldt(&ac, addr); ST(i) = tmp; addr += 16; } From 4526f58a2727a37c829c21c3f095226601099552 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 7 Apr 2024 23:58:17 -1000 Subject: [PATCH 0921/2884] target/i386: Convert helper_{fbld,fbst}_ST0 to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 1662643a8f..6237cd8383 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -773,18 +773,21 @@ void helper_fninit(CPUX86State *env) void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) { + X86Access ac; floatx80 tmp; uint64_t val; unsigned int v; int i; + access_prepare(&ac, env, ptr, 10, MMU_DATA_LOAD, GETPC()); + val = 0; for (i = 8; i >= 0; i--) { - v = cpu_ldub_data_ra(env, ptr + i, GETPC()); + v = access_ldb(&ac, ptr + i); val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); } tmp = int64_to_floatx80(val, &env->fp_status); - if (cpu_ldub_data_ra(env, ptr + 9, GETPC()) & 0x80) { + if (access_ldb(&ac, ptr + 9) & 0x80) { tmp = floatx80_chs(tmp); } fpush(env); @@ -798,7 +801,9 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) target_ulong mem_ref, mem_end; int64_t val; CPU_LDoubleU temp; + X86Access ac; + access_prepare(&ac, env, ptr, 10, MMU_DATA_STORE, GETPC()); temp.d = ST0; val = floatx80_to_int64(ST0, &env->fp_status); @@ -806,20 +811,20 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) if (val >= 1000000000000000000LL || val <= -1000000000000000000LL) { set_float_exception_flags(float_flag_invalid, &env->fp_status); while (mem_ref < ptr + 7) { - cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); + access_stb(&ac, mem_ref++, 0); } - cpu_stb_data_ra(env, mem_ref++, 0xc0, GETPC()); - cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); - cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); + access_stb(&ac, mem_ref++, 0xc0); + access_stb(&ac, mem_ref++, 0xff); + access_stb(&ac, mem_ref++, 0xff); merge_exception_flags(env, old_flags); return; } mem_end = mem_ref + 9; if (SIGND(temp)) { - cpu_stb_data_ra(env, mem_end, 0x80, GETPC()); + access_stb(&ac, mem_end, 0x80); val = -val; } else { - cpu_stb_data_ra(env, mem_end, 0x00, GETPC()); + access_stb(&ac, mem_end, 0x00); } while (mem_ref < mem_end) { if (val == 0) { @@ -828,10 +833,10 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) v = val % 100; val = val / 100; v = ((v / 10) << 4) | (v % 10); - cpu_stb_data_ra(env, mem_ref++, v, GETPC()); + access_stb(&ac, mem_ref++, v); } while (mem_ref < mem_end) { - cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); + access_stb(&ac, mem_ref++, 0); } merge_exception_flags(env, old_flags); } From bc13c2dd01288d26bfb38bdc958ad58b58661ac0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 00:14:48 -1000 Subject: [PATCH 0922/2884] target/i386: Convert do_fldenv to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 6237cd8383..5ad6e04639 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2442,20 +2442,15 @@ static void cpu_set_fpus(CPUX86State *env, uint16_t fpus) #endif } -static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_fldenv(X86Access *ac, target_ulong ptr, int data32) { int i, fpus, fptag; + CPUX86State *env = ac->env; + + cpu_set_fpuc(env, access_ldw(ac, ptr)); + fpus = access_ldw(ac, ptr + (2 << data32)); + fptag = access_ldw(ac, ptr + (4 << data32)); - if (data32) { - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 4, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 8, retaddr); - } else { - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); - } cpu_set_fpus(env, fpus); for (i = 0; i < 8; i++) { env->fptags[i] = ((fptag & 3) == 3); @@ -2465,7 +2460,10 @@ static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) { - do_fldenv(env, ptr, data32, GETPC()); + X86Access ac; + + access_prepare(&ac, env, ptr, 14 << data32, MMU_DATA_STORE, GETPC()); + do_fldenv(&ac, ptr, data32); } static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, @@ -2499,12 +2497,12 @@ static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, { X86Access ac; floatx80 tmp; - int i; + int i, envsize = 14 << data32; - do_fldenv(env, ptr, data32, retaddr); - ptr += (target_ulong)14 << data32; + access_prepare(&ac, env, ptr, envsize + 80, MMU_DATA_LOAD, retaddr); - access_prepare(&ac, env, ptr, 80, MMU_DATA_LOAD, retaddr); + do_fldenv(&ac, ptr, data32); + ptr += envsize; for (i = 0; i < 8; i++) { tmp = do_fldt(&ac, ptr); From 505e2ef744b3ebd0a28a94ed9b00f99595b0cf6a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 00:23:22 -1000 Subject: [PATCH 0923/2884] target/i386: Convert do_fstenv to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 45 +++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 5ad6e04639..01e9a1fbbf 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2373,9 +2373,9 @@ void helper_fxam_ST0(CPUX86State *env) } } -static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_fstenv(X86Access *ac, target_ulong ptr, int data32) { + CPUX86State *env = ac->env; int fpus, fptag, exp, i; uint64_t mant; CPU_LDoubleU tmp; @@ -2402,28 +2402,31 @@ static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, } if (data32) { /* 32 bit */ - cpu_stl_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stl_data_ra(env, ptr + 4, fpus, retaddr); - cpu_stl_data_ra(env, ptr + 8, fptag, retaddr); - cpu_stl_data_ra(env, ptr + 12, env->fpip, retaddr); /* fpip */ - cpu_stl_data_ra(env, ptr + 16, env->fpcs, retaddr); /* fpcs */ - cpu_stl_data_ra(env, ptr + 20, env->fpdp, retaddr); /* fpoo */ - cpu_stl_data_ra(env, ptr + 24, env->fpds, retaddr); /* fpos */ + access_stl(ac, ptr, env->fpuc); + access_stl(ac, ptr + 4, fpus); + access_stl(ac, ptr + 8, fptag); + access_stl(ac, ptr + 12, env->fpip); /* fpip */ + access_stl(ac, ptr + 16, env->fpcs); /* fpcs */ + access_stl(ac, ptr + 20, env->fpdp); /* fpoo */ + access_stl(ac, ptr + 24, env->fpds); /* fpos */ } else { /* 16 bit */ - cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); - cpu_stw_data_ra(env, ptr + 4, fptag, retaddr); - cpu_stw_data_ra(env, ptr + 6, env->fpip, retaddr); - cpu_stw_data_ra(env, ptr + 8, env->fpcs, retaddr); - cpu_stw_data_ra(env, ptr + 10, env->fpdp, retaddr); - cpu_stw_data_ra(env, ptr + 12, env->fpds, retaddr); + access_stw(ac, ptr, env->fpuc); + access_stw(ac, ptr + 2, fpus); + access_stw(ac, ptr + 4, fptag); + access_stw(ac, ptr + 6, env->fpip); + access_stw(ac, ptr + 8, env->fpcs); + access_stw(ac, ptr + 10, env->fpdp); + access_stw(ac, ptr + 12, env->fpds); } } void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) { - do_fstenv(env, ptr, data32, GETPC()); + X86Access ac; + + access_prepare(&ac, env, ptr, 14 << data32, MMU_DATA_STORE, GETPC()); + do_fstenv(&ac, ptr, data32); } static void cpu_set_fpus(CPUX86State *env, uint16_t fpus) @@ -2471,12 +2474,12 @@ static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, { X86Access ac; floatx80 tmp; - int i; + int i, envsize = 14 << data32; - do_fstenv(env, ptr, data32, retaddr); + access_prepare(&ac, env, ptr, envsize + 80, MMU_DATA_STORE, retaddr); - ptr += (target_ulong)14 << data32; - access_prepare(&ac, env, ptr, 80, MMU_DATA_STORE, retaddr); + do_fstenv(&ac, ptr, data32); + ptr += envsize; for (i = 0; i < 8; i++) { tmp = ST(i); From 94f60f8f1c07de1449f798141cf13ba93f07d875 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 00:35:06 -1000 Subject: [PATCH 0924/2884] target/i386: Convert do_fsave, do_frstor to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 60 ++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 01e9a1fbbf..df12eac71e 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2469,21 +2469,16 @@ void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) do_fldenv(&ac, ptr, data32); } -static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_fsave(X86Access *ac, target_ulong ptr, int data32) { - X86Access ac; - floatx80 tmp; - int i, envsize = 14 << data32; + CPUX86State *env = ac->env; - access_prepare(&ac, env, ptr, envsize + 80, MMU_DATA_STORE, retaddr); + do_fstenv(ac, ptr, data32); + ptr += 14 << data32; - do_fstenv(&ac, ptr, data32); - ptr += envsize; - - for (i = 0; i < 8; i++) { - tmp = ST(i); - do_fstt(&ac, ptr, tmp); + for (int i = 0; i < 8; i++) { + floatx80 tmp = ST(i); + do_fstt(ac, ptr, tmp); ptr += 10; } @@ -2492,23 +2487,22 @@ static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) { - do_fsave(env, ptr, data32, GETPC()); + int size = (14 << data32) + 80; + X86Access ac; + + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, GETPC()); + do_fsave(&ac, ptr, data32); } -static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_frstor(X86Access *ac, target_ulong ptr, int data32) { - X86Access ac; - floatx80 tmp; - int i, envsize = 14 << data32; + CPUX86State *env = ac->env; - access_prepare(&ac, env, ptr, envsize + 80, MMU_DATA_LOAD, retaddr); + do_fldenv(ac, ptr, data32); + ptr += 14 << data32; - do_fldenv(&ac, ptr, data32); - ptr += envsize; - - for (i = 0; i < 8; i++) { - tmp = do_fldt(&ac, ptr); + for (int i = 0; i < 8; i++) { + floatx80 tmp = do_fldt(ac, ptr); ST(i) = tmp; ptr += 10; } @@ -2516,7 +2510,11 @@ static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) { - do_frstor(env, ptr, data32, GETPC()); + int size = (14 << data32) + 80; + X86Access ac; + + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, GETPC()); + do_frstor(&ac, ptr, data32); } #define XO(X) offsetof(X86XSaveArea, X) @@ -2972,12 +2970,20 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) #if defined(CONFIG_USER_ONLY) void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) { - do_fsave(env, ptr, data32, 0); + int size = (14 << data32) + 80; + X86Access ac; + + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, 0); + do_fsave(&ac, ptr, data32); } void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) { - do_frstor(env, ptr, data32, 0); + int size = (14 << data32) + 80; + X86Access ac; + + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, 0); + do_frstor(&ac, ptr, data32); } void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) From b7e6d3ad30b53121ff184e93d33dcf61e354c9bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 07:58:42 -1000 Subject: [PATCH 0925/2884] target/i386: Convert do_xsave_{fpu,mxcr,sse} to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 52 +++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index df12eac71e..8fbe6e00ce 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2519,11 +2519,11 @@ void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) #define XO(X) offsetof(X86XSaveArea, X) -static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_fpu(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int fpus, fptag, i; target_ulong addr; - X86Access ac; fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; @@ -2531,35 +2531,37 @@ static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) fptag |= (env->fptags[i] << i); } - cpu_stw_data_ra(env, ptr + XO(legacy.fcw), env->fpuc, ra); - cpu_stw_data_ra(env, ptr + XO(legacy.fsw), fpus, ra); - cpu_stw_data_ra(env, ptr + XO(legacy.ftw), fptag ^ 0xff, ra); + access_stw(ac, ptr + XO(legacy.fcw), env->fpuc); + access_stw(ac, ptr + XO(legacy.fsw), fpus); + access_stw(ac, ptr + XO(legacy.ftw), fptag ^ 0xff); /* In 32-bit mode this is eip, sel, dp, sel. In 64-bit mode this is rip, rdp. But in either case we don't write actual data, just zeros. */ - cpu_stq_data_ra(env, ptr + XO(legacy.fpip), 0, ra); /* eip+sel; rip */ - cpu_stq_data_ra(env, ptr + XO(legacy.fpdp), 0, ra); /* edp+sel; rdp */ + access_stq(ac, ptr + XO(legacy.fpip), 0); /* eip+sel; rip */ + access_stq(ac, ptr + XO(legacy.fpdp), 0); /* edp+sel; rdp */ addr = ptr + XO(legacy.fpregs); - access_prepare(&ac, env, addr, 8 * 16, MMU_DATA_STORE, ra); for (i = 0; i < 8; i++) { floatx80 tmp = ST(i); - do_fstt(&ac, addr, tmp); + do_fstt(ac, addr, tmp); addr += 16; } } -static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_mxcsr(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; + update_mxcsr_from_sse_status(env); - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr), env->mxcsr, ra); - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra); + access_stl(ac, ptr + XO(legacy.mxcsr), env->mxcsr); + access_stl(ac, ptr + XO(legacy.mxcsr_mask), 0x0000ffff); } -static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_sse(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; target_ulong addr; @@ -2571,8 +2573,8 @@ static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) addr = ptr + XO(legacy.xmm_regs); for (i = 0; i < nb_xmm_regs; i++) { - cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), ra); - cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), ra); + access_stq(ac, addr, env->xmm_regs[i].ZMM_Q(0)); + access_stq(ac, addr + 8, env->xmm_regs[i].ZMM_Q(1)); addr += 16; } } @@ -2619,20 +2621,24 @@ static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) static void do_fxsave(CPUX86State *env, target_ulong ptr, uintptr_t ra) { + X86Access ac; + /* The operand must be 16 byte aligned */ if (ptr & 0xf) { raise_exception_ra(env, EXCP0D_GPF, ra); } - do_xsave_fpu(env, ptr, ra); + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_STORE, ra); + do_xsave_fpu(&ac, ptr); if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xsave_mxcsr(env, ptr, ra); + do_xsave_mxcsr(&ac, ptr); /* Fast FXSAVE leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - do_xsave_sse(env, ptr, ra); + do_xsave_sse(&ac, ptr); } } } @@ -2660,6 +2666,7 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uint64_t inuse, uint64_t opt, uintptr_t ra) { uint64_t old_bv, new_bv; + X86Access ac; /* The OS must have enabled XSAVE. */ if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { @@ -2675,15 +2682,18 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, rfbm &= env->xcr0; opt &= rfbm; + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_STORE, ra); + if (opt & XSTATE_FP_MASK) { - do_xsave_fpu(env, ptr, ra); + do_xsave_fpu(&ac, ptr); } if (rfbm & XSTATE_SSE_MASK) { /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ - do_xsave_mxcsr(env, ptr, ra); + do_xsave_mxcsr(&ac, ptr); } if (opt & XSTATE_SSE_MASK) { - do_xsave_sse(env, ptr, ra); + do_xsave_sse(&ac, ptr); } if (opt & XSTATE_YMM_MASK) { do_xsave_ymmh(env, ptr + XO(avx_state), ra); From e41d2eaf17f1bcd0b5c085b5c9b6151b592ee620 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 08:33:29 -1000 Subject: [PATCH 0926/2884] target/i386: Convert do_xrstor_{fpu,mxcr,sse} to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 46 ++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 8fbe6e00ce..f21cdb45ea 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2725,39 +2725,41 @@ void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); } -static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_fpu(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, fpuc, fpus, fptag; target_ulong addr; - X86Access ac; - fpuc = cpu_lduw_data_ra(env, ptr + XO(legacy.fcw), ra); - fpus = cpu_lduw_data_ra(env, ptr + XO(legacy.fsw), ra); - fptag = cpu_lduw_data_ra(env, ptr + XO(legacy.ftw), ra); + fpuc = access_ldw(ac, ptr + XO(legacy.fcw)); + fpus = access_ldw(ac, ptr + XO(legacy.fsw)); + fptag = access_ldw(ac, ptr + XO(legacy.ftw)); cpu_set_fpuc(env, fpuc); cpu_set_fpus(env, fpus); + fptag ^= 0xff; for (i = 0; i < 8; i++) { env->fptags[i] = ((fptag >> i) & 1); } addr = ptr + XO(legacy.fpregs); - access_prepare(&ac, env, addr, 8 * 16, MMU_DATA_LOAD, ra); for (i = 0; i < 8; i++) { - floatx80 tmp = do_fldt(&ac, addr); + floatx80 tmp = do_fldt(ac, addr); ST(i) = tmp; addr += 16; } } -static void do_xrstor_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_mxcsr(X86Access *ac, target_ulong ptr) { - cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + XO(legacy.mxcsr), ra)); + CPUX86State *env = ac->env; + cpu_set_mxcsr(env, access_ldl(ac, ptr + XO(legacy.mxcsr))); } -static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_sse(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; target_ulong addr; @@ -2769,8 +2771,8 @@ static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) addr = ptr + XO(legacy.xmm_regs); for (i = 0; i < nb_xmm_regs; i++) { - env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, ra); - env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, ra); + env->xmm_regs[i].ZMM_Q(0) = access_ldq(ac, addr); + env->xmm_regs[i].ZMM_Q(1) = access_ldq(ac, addr + 8); addr += 16; } } @@ -2850,20 +2852,24 @@ static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) static void do_fxrstor(CPUX86State *env, target_ulong ptr, uintptr_t ra) { + X86Access ac; + /* The operand must be 16 byte aligned */ if (ptr & 0xf) { raise_exception_ra(env, EXCP0D_GPF, ra); } - do_xrstor_fpu(env, ptr, ra); + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_LOAD, ra); + do_xrstor_fpu(&ac, ptr); if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xrstor_mxcsr(env, ptr, ra); + do_xrstor_mxcsr(&ac, ptr); /* Fast FXRSTOR leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - do_xrstor_sse(env, ptr, ra); + do_xrstor_sse(&ac, ptr); } } } @@ -2876,6 +2882,7 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr) static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr_t ra) { uint64_t xstate_bv, xcomp_bv, reserve0; + X86Access ac; rfbm &= env->xcr0; @@ -2914,9 +2921,12 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr raise_exception_ra(env, EXCP0D_GPF, ra); } + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_LOAD, ra); + if (rfbm & XSTATE_FP_MASK) { if (xstate_bv & XSTATE_FP_MASK) { - do_xrstor_fpu(env, ptr, ra); + do_xrstor_fpu(&ac, ptr); } else { do_fninit(env); memset(env->fpregs, 0, sizeof(env->fpregs)); @@ -2925,9 +2935,9 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr if (rfbm & XSTATE_SSE_MASK) { /* Note that the standard form of XRSTOR loads MXCSR from memory whether or not the XSTATE_BV bit is set. */ - do_xrstor_mxcsr(env, ptr, ra); + do_xrstor_mxcsr(&ac, ptr); if (xstate_bv & XSTATE_SSE_MASK) { - do_xrstor_sse(env, ptr, ra); + do_xrstor_sse(&ac, ptr); } else { do_clear_sse(env); } From 6d030aab29f8713776aa2fec31bc94bb98a96e55 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 08:44:37 -1000 Subject: [PATCH 0927/2884] tagret/i386: Convert do_fxsave, do_fxrstor to X86Access Move the alignment fault from do_* to helper_*, as it need not apply to usage from within user-only signal handling. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 84 ++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index f21cdb45ea..4dcb0b92ff 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2619,8 +2619,25 @@ static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) cpu_stq_data_ra(env, ptr, env->pkru, ra); } -static void do_fxsave(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_fxsave(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; + + do_xsave_fpu(ac, ptr); + if (env->cr[4] & CR4_OSFXSR_MASK) { + do_xsave_mxcsr(ac, ptr); + /* Fast FXSAVE leaves out the XMM registers */ + if (!(env->efer & MSR_EFER_FFXSR) + || (env->hflags & HF_CPL_MASK) + || !(env->hflags & HF_LMA_MASK)) { + do_xsave_sse(ac, ptr); + } + } +} + +void helper_fxsave(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); X86Access ac; /* The operand must be 16 byte aligned */ @@ -2630,22 +2647,7 @@ static void do_fxsave(CPUX86State *env, target_ulong ptr, uintptr_t ra) access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), MMU_DATA_STORE, ra); - do_xsave_fpu(&ac, ptr); - - if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xsave_mxcsr(&ac, ptr); - /* Fast FXSAVE leaves out the XMM registers */ - if (!(env->efer & MSR_EFER_FFXSR) - || (env->hflags & HF_CPL_MASK) - || !(env->hflags & HF_LMA_MASK)) { - do_xsave_sse(&ac, ptr); - } - } -} - -void helper_fxsave(CPUX86State *env, target_ulong ptr) -{ - do_fxsave(env, ptr, GETPC()); + do_fxsave(&ac, ptr); } static uint64_t get_xinuse(CPUX86State *env) @@ -2850,8 +2852,25 @@ static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) env->pkru = cpu_ldq_data_ra(env, ptr, ra); } -static void do_fxrstor(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_fxrstor(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; + + do_xrstor_fpu(ac, ptr); + if (env->cr[4] & CR4_OSFXSR_MASK) { + do_xrstor_mxcsr(ac, ptr); + /* Fast FXRSTOR leaves out the XMM registers */ + if (!(env->efer & MSR_EFER_FFXSR) + || (env->hflags & HF_CPL_MASK) + || !(env->hflags & HF_LMA_MASK)) { + do_xrstor_sse(ac, ptr); + } + } +} + +void helper_fxrstor(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); X86Access ac; /* The operand must be 16 byte aligned */ @@ -2861,22 +2880,7 @@ static void do_fxrstor(CPUX86State *env, target_ulong ptr, uintptr_t ra) access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), MMU_DATA_LOAD, ra); - do_xrstor_fpu(&ac, ptr); - - if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xrstor_mxcsr(&ac, ptr); - /* Fast FXRSTOR leaves out the XMM registers */ - if (!(env->efer & MSR_EFER_FFXSR) - || (env->hflags & HF_CPL_MASK) - || !(env->hflags & HF_LMA_MASK)) { - do_xrstor_sse(&ac, ptr); - } - } -} - -void helper_fxrstor(CPUX86State *env, target_ulong ptr) -{ - do_fxrstor(env, ptr, GETPC()); + do_fxrstor(&ac, ptr); } static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr_t ra) @@ -3008,12 +3012,20 @@ void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) { - do_fxsave(env, ptr, 0); + X86Access ac; + + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_STORE, 0); + do_fxsave(&ac, ptr); } void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) { - do_fxrstor(env, ptr, 0); + X86Access ac; + + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_LOAD, 0); + do_fxrstor(&ac, ptr); } void cpu_x86_xsave(CPUX86State *env, target_ulong ptr) From 6b1b736bae9b89882ed293d0256f2a0de1d03f9c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 09:01:58 -1000 Subject: [PATCH 0928/2884] target/i386: Convert do_xsave_* to X86Access The body of do_xsave is now fully converted. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 47 ++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 4dcb0b92ff..356397a4ab 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2579,8 +2579,9 @@ static void do_xsave_sse(X86Access *ac, target_ulong ptr) } } -static void do_xsave_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_ymmh(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; if (env->hflags & HF_CS64_MASK) { @@ -2590,33 +2591,36 @@ static void do_xsave_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) } for (i = 0; i < nb_xmm_regs; i++, ptr += 16) { - cpu_stq_data_ra(env, ptr, env->xmm_regs[i].ZMM_Q(2), ra); - cpu_stq_data_ra(env, ptr + 8, env->xmm_regs[i].ZMM_Q(3), ra); + access_stq(ac, ptr, env->xmm_regs[i].ZMM_Q(2)); + access_stq(ac, ptr + 8, env->xmm_regs[i].ZMM_Q(3)); } } -static void do_xsave_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_bndregs(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); int i; for (i = 0; i < 4; i++, addr += 16) { - cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra); - cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra); + access_stq(ac, addr, env->bnd_regs[i].lb); + access_stq(ac, addr + 8, env->bnd_regs[i].ub); } } -static void do_xsave_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_bndcsr(X86Access *ac, target_ulong ptr) { - cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), - env->bndcs_regs.cfgu, ra); - cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), - env->bndcs_regs.sts, ra); + CPUX86State *env = ac->env; + + access_stq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), + env->bndcs_regs.cfgu); + access_stq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), + env->bndcs_regs.sts); } -static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_pkru(X86Access *ac, target_ulong ptr) { - cpu_stq_data_ra(env, ptr, env->pkru, ra); + access_stq(ac, ptr, ac->env->pkru); } static void do_fxsave(X86Access *ac, target_ulong ptr) @@ -2669,6 +2673,7 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, { uint64_t old_bv, new_bv; X86Access ac; + unsigned size; /* The OS must have enabled XSAVE. */ if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { @@ -2684,8 +2689,8 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, rfbm &= env->xcr0; opt &= rfbm; - access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), - MMU_DATA_STORE, ra); + size = xsave_area_size(opt, false); + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, ra); if (opt & XSTATE_FP_MASK) { do_xsave_fpu(&ac, ptr); @@ -2698,22 +2703,22 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, do_xsave_sse(&ac, ptr); } if (opt & XSTATE_YMM_MASK) { - do_xsave_ymmh(env, ptr + XO(avx_state), ra); + do_xsave_ymmh(&ac, ptr + XO(avx_state)); } if (opt & XSTATE_BNDREGS_MASK) { - do_xsave_bndregs(env, ptr + XO(bndreg_state), ra); + do_xsave_bndregs(&ac, ptr + XO(bndreg_state)); } if (opt & XSTATE_BNDCSR_MASK) { - do_xsave_bndcsr(env, ptr + XO(bndcsr_state), ra); + do_xsave_bndcsr(&ac, ptr + XO(bndcsr_state)); } if (opt & XSTATE_PKRU_MASK) { - do_xsave_pkru(env, ptr + XO(pkru_state), ra); + do_xsave_pkru(&ac, ptr + XO(pkru_state)); } /* Update the XSTATE_BV field. */ - old_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); + old_bv = access_ldq(&ac, ptr + XO(header.xstate_bv)); new_bv = (old_bv & ~rfbm) | (inuse & rfbm); - cpu_stq_data_ra(env, ptr + XO(header.xstate_bv), new_bv, ra); + access_stq(&ac, ptr + XO(header.xstate_bv), new_bv); } void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) From 58955a96d9ce59ada80af88e4ba7c8ecfb79c87f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 09:14:47 -1000 Subject: [PATCH 0929/2884] target/i386: Convert do_xrstor_* to X86Access The body of do_xrstor is now fully converted. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 356397a4ab..7796688514 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2800,8 +2800,9 @@ static void do_clear_sse(CPUX86State *env) } } -static void do_xrstor_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_ymmh(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; if (env->hflags & HF_CS64_MASK) { @@ -2811,8 +2812,8 @@ static void do_xrstor_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) } for (i = 0; i < nb_xmm_regs; i++, ptr += 16) { - env->xmm_regs[i].ZMM_Q(2) = cpu_ldq_data_ra(env, ptr, ra); - env->xmm_regs[i].ZMM_Q(3) = cpu_ldq_data_ra(env, ptr + 8, ra); + env->xmm_regs[i].ZMM_Q(2) = access_ldq(ac, ptr); + env->xmm_regs[i].ZMM_Q(3) = access_ldq(ac, ptr + 8); } } @@ -2832,29 +2833,32 @@ static void do_clear_ymmh(CPUX86State *env) } } -static void do_xrstor_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_bndregs(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); int i; for (i = 0; i < 4; i++, addr += 16) { - env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra); - env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra); + env->bnd_regs[i].lb = access_ldq(ac, addr); + env->bnd_regs[i].ub = access_ldq(ac, addr + 8); } } -static void do_xrstor_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_bndcsr(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; + /* FIXME: Extend highest implemented bit of linear address. */ env->bndcs_regs.cfgu - = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), ra); + = access_ldq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu)); env->bndcs_regs.sts - = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), ra); + = access_ldq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.sts)); } -static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_pkru(X86Access *ac, target_ulong ptr) { - env->pkru = cpu_ldq_data_ra(env, ptr, ra); + ac->env->pkru = access_ldq(ac, ptr); } static void do_fxrstor(X86Access *ac, target_ulong ptr) @@ -2892,6 +2896,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr { uint64_t xstate_bv, xcomp_bv, reserve0; X86Access ac; + unsigned size, size_ext; rfbm &= env->xcr0; @@ -2905,7 +2910,10 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr raise_exception_ra(env, EXCP0D_GPF, ra); } - xstate_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); + size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, ra); + + xstate_bv = access_ldq(&ac, ptr + XO(header.xstate_bv)); if ((int64_t)xstate_bv < 0) { /* FIXME: Compact form. */ @@ -2924,14 +2932,17 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr describes only XCOMP_BV, but the description of the standard form of XRSTOR (Vol 1, Sec 13.8.1) checks bytes 23:8 for zero, which includes the next 64-bit field. */ - xcomp_bv = cpu_ldq_data_ra(env, ptr + XO(header.xcomp_bv), ra); - reserve0 = cpu_ldq_data_ra(env, ptr + XO(header.reserve0), ra); + xcomp_bv = access_ldq(&ac, ptr + XO(header.xcomp_bv)); + reserve0 = access_ldq(&ac, ptr + XO(header.reserve0)); if (xcomp_bv || reserve0) { raise_exception_ra(env, EXCP0D_GPF, ra); } - access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), - MMU_DATA_LOAD, ra); + size_ext = xsave_area_size(rfbm & xstate_bv, false); + if (size < size_ext) { + /* TODO: See if existing page probe has covered extra size. */ + access_prepare(&ac, env, ptr, size_ext, MMU_DATA_LOAD, ra); + } if (rfbm & XSTATE_FP_MASK) { if (xstate_bv & XSTATE_FP_MASK) { @@ -2953,14 +2964,14 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr } if (rfbm & XSTATE_YMM_MASK) { if (xstate_bv & XSTATE_YMM_MASK) { - do_xrstor_ymmh(env, ptr + XO(avx_state), ra); + do_xrstor_ymmh(&ac, ptr + XO(avx_state)); } else { do_clear_ymmh(env); } } if (rfbm & XSTATE_BNDREGS_MASK) { if (xstate_bv & XSTATE_BNDREGS_MASK) { - do_xrstor_bndregs(env, ptr + XO(bndreg_state), ra); + do_xrstor_bndregs(&ac, ptr + XO(bndreg_state)); env->hflags |= HF_MPX_IU_MASK; } else { memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); @@ -2969,7 +2980,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr } if (rfbm & XSTATE_BNDCSR_MASK) { if (xstate_bv & XSTATE_BNDCSR_MASK) { - do_xrstor_bndcsr(env, ptr + XO(bndcsr_state), ra); + do_xrstor_bndcsr(&ac, ptr + XO(bndcsr_state)); } else { memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); } @@ -2978,7 +2989,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr if (rfbm & XSTATE_PKRU_MASK) { uint64_t old_pkru = env->pkru; if (xstate_bv & XSTATE_PKRU_MASK) { - do_xrstor_pkru(env, ptr + XO(pkru_state), ra); + do_xrstor_pkru(&ac, ptr + XO(pkru_state)); } else { env->pkru = 0; } From a8f68831c6dfd1903555e4402addd5138f78db97 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 09:45:13 -1000 Subject: [PATCH 0930/2884] target/i386: Split out do_xsave_chk This path is not required by user-only, and can in fact be shared between xsave and xrstor. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 51 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 7796688514..6a319dadf2 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2675,16 +2675,6 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, X86Access ac; unsigned size; - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); - } - - /* The operand must be 64 byte aligned. */ - if (ptr & 63) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - /* Never save anything not enabled by XCR0. */ rfbm &= env->xcr0; opt &= rfbm; @@ -2721,15 +2711,35 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, access_stq(&ac, ptr + XO(header.xstate_bv), new_bv); } +static void do_xsave_chk(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } +} + void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); + uintptr_t ra = GETPC(); + + do_xsave_chk(env, ptr, ra); + do_xsave(env, ptr, rfbm, get_xinuse(env), -1, ra); } void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - uint64_t inuse = get_xinuse(env); - do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); + uintptr_t ra = GETPC(); + uint64_t inuse; + + do_xsave_chk(env, ptr, ra); + inuse = get_xinuse(env); + do_xsave(env, ptr, rfbm, inuse, inuse, ra); } static void do_xrstor_fpu(X86Access *ac, target_ulong ptr) @@ -2900,16 +2910,6 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr rfbm &= env->xcr0; - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); - } - - /* The operand must be 64 byte aligned. */ - if (ptr & 63) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, ra); @@ -3004,7 +3004,10 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xrstor(env, ptr, rfbm, GETPC()); + uintptr_t ra = GETPC(); + + do_xsave_chk(env, ptr, ra); + do_xrstor(env, ptr, rfbm, ra); } #if defined(CONFIG_USER_ONLY) From a2d64d61c1fa1826344fef02e5cc7e331d307e0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 09:48:26 -1000 Subject: [PATCH 0931/2884] target/i386: Add rbfm argument to cpu_x86_{xsave,xrstor} For now, continue to pass all 1's from signal.c. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 4 ++-- target/i386/cpu.h | 4 ++-- target/i386/tcg/fpu_helper.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 990048f42a..824375d42a 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -268,7 +268,7 @@ static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs /* Zero the header, XSAVE *adds* features to an existing save state. */ memset(fxsave->xfeatures, 0, 64); - cpu_x86_xsave(env, fxsave_addr); + cpu_x86_xsave(env, fxsave_addr, -1); __put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1); __put_user(extended_size, &fxsave->sw_reserved.extended_size); __put_user(env->xcr0, &fxsave->sw_reserved.xfeatures); @@ -569,7 +569,7 @@ static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs return 1; } if (tswapl(*(uint32_t *) &fxsave->xfeatures[xfeatures_size]) == TARGET_FP_XSTATE_MAGIC2) { - cpu_x86_xrstor(env, fxsave_addr); + cpu_x86_xrstor(env, fxsave_addr, -1); return 0; } } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c500a69a69..91170a088a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2262,8 +2262,8 @@ void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32); void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr); void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr); -void cpu_x86_xsave(CPUX86State *s, target_ulong ptr); -void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr); +void cpu_x86_xsave(CPUX86State *s, target_ulong ptr, uint64_t rbfm); +void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr, uint64_t rbfm); /* cpu.c */ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 6a319dadf2..a09d6aaf07 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -3047,14 +3047,14 @@ void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) do_fxrstor(&ac, ptr); } -void cpu_x86_xsave(CPUX86State *env, target_ulong ptr) +void cpu_x86_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xsave(env, ptr, -1, get_xinuse(env), -1, 0); + do_xsave(env, ptr, rfbm, get_xinuse(env), -1, 0); } -void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr) +void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xrstor(env, ptr, -1, 0); + do_xrstor(env, ptr, rfbm, 0); } #endif From 6dba8b471cb2b40140b672a9ae9b965a7a132409 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 09:55:21 -1000 Subject: [PATCH 0932/2884] target/i386: Add {hw,sw}_reserved to X86LegacyXSaveArea This completes the 512 byte structure, allowing the union to be removed. Assert that the structure layout is as expected. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/cpu.h | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 91170a088a..fdd318963a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1425,23 +1425,34 @@ typedef struct { */ #define UNASSIGNED_APIC_ID 0xFFFFFFFF -typedef union X86LegacyXSaveArea { - struct { - uint16_t fcw; - uint16_t fsw; - uint8_t ftw; - uint8_t reserved; - uint16_t fpop; - uint64_t fpip; - uint64_t fpdp; - uint32_t mxcsr; - uint32_t mxcsr_mask; - FPReg fpregs[8]; - uint8_t xmm_regs[16][16]; +typedef struct X86LegacyXSaveArea { + uint16_t fcw; + uint16_t fsw; + uint8_t ftw; + uint8_t reserved; + uint16_t fpop; + union { + struct { + uint64_t fpip; + uint64_t fpdp; + }; + struct { + uint32_t fip; + uint32_t fcs; + uint32_t foo; + uint32_t fos; + }; }; - uint8_t data[512]; + uint32_t mxcsr; + uint32_t mxcsr_mask; + FPReg fpregs[8]; + uint8_t xmm_regs[16][16]; + uint32_t hw_reserved[12]; + uint32_t sw_reserved[12]; } X86LegacyXSaveArea; +QEMU_BUILD_BUG_ON(sizeof(X86LegacyXSaveArea) != 512); + typedef struct X86XSaveHeader { uint64_t xstate_bv; uint64_t xcomp_bv; From 077c43eb0d30a257ee33f1b48ea5b29eafcf4eb5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 10:22:32 -1000 Subject: [PATCH 0933/2884] linux-user/i386: Drop xfeatures_size from sigcontext arithmetic This is subtracting sizeof(target_fpstate_fxsave) in TARGET_FXSAVE_SIZE, then adding it again via &fxsave->xfeatures. Perform the same computation using xstate_size alone. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 824375d42a..89048ed069 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -253,7 +253,6 @@ static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs __put_user(0, &fxsave->sw_reserved.magic1); } else { uint32_t xstate_size = xsave_area_size(env->xcr0, false); - uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE; /* * extended_size is the offset from fpstate_addr to right after the end @@ -273,7 +272,8 @@ static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs __put_user(extended_size, &fxsave->sw_reserved.extended_size); __put_user(env->xcr0, &fxsave->sw_reserved.xfeatures); __put_user(xstate_size, &fxsave->sw_reserved.xstate_size); - __put_user(TARGET_FP_XSTATE_MAGIC2, (uint32_t *) &fxsave->xfeatures[xfeatures_size]); + __put_user(TARGET_FP_XSTATE_MAGIC2, + (uint32_t *)((void *)fxsave + xstate_size)); } } @@ -559,7 +559,6 @@ static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { uint32_t extended_size = tswapl(fxsave->sw_reserved.extended_size); uint32_t xstate_size = tswapl(fxsave->sw_reserved.xstate_size); - uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE; /* Linux checks MAGIC2 using xstate_size, not extended_size. */ if (tswapl(fxsave->sw_reserved.magic1) == TARGET_FP_XSTATE_MAGIC1 && @@ -568,7 +567,7 @@ static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) { return 1; } - if (tswapl(*(uint32_t *) &fxsave->xfeatures[xfeatures_size]) == TARGET_FP_XSTATE_MAGIC2) { + if (tswapl(*(uint32_t *)((void *)fxsave + xstate_size)) == TARGET_FP_XSTATE_MAGIC2) { cpu_x86_xrstor(env, fxsave_addr, -1); return 0; } From fcc9b64d0767563adb03c933a208e7ba5250c6f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 10:25:47 -1000 Subject: [PATCH 0934/2884] linux-user/i386: Remove xfeatures from target_fpstate_fxsave This is easily computed by advancing past the structure. At the same time, replace the magic number "64". Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 89048ed069..f8064691c4 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -67,7 +67,6 @@ struct target_fpstate_fxsave { uint32_t xmm_space[64]; uint32_t hw_reserved[12]; struct target_fpx_sw_bytes sw_reserved; - uint8_t xfeatures[]; }; #define TARGET_FXSAVE_SIZE sizeof(struct target_fpstate_fxsave) QEMU_BUILD_BUG_ON(TARGET_FXSAVE_SIZE != 512); @@ -266,7 +265,7 @@ static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs assert(!(fxsave_addr & 0x3f)); /* Zero the header, XSAVE *adds* features to an existing save state. */ - memset(fxsave->xfeatures, 0, 64); + memset(fxsave + 1, 0, sizeof(X86XSaveHeader)); cpu_x86_xsave(env, fxsave_addr, -1); __put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1); __put_user(extended_size, &fxsave->sw_reserved.extended_size); From 3b6e9491e3e8a51e7eb8d25316b75ca4a58b22ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 10:33:54 -1000 Subject: [PATCH 0935/2884] linux-user/i386: Replace target_fpstate_fxsave with X86LegacyXSaveArea Use the structure definition from target/i386/cpu.h. The only minor quirk is re-casting the sw_reserved area to the OS specific struct target_fpx_sw_bytes. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 71 +++++++++++++++------------------------- 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index f8064691c4..5b1c570bff 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -34,16 +34,6 @@ struct target_fpreg { uint16_t exponent; }; -struct target_fpxreg { - uint16_t significand[4]; - uint16_t exponent; - uint16_t padding[3]; -}; - -struct target_xmmreg { - uint32_t element[4]; -}; - struct target_fpx_sw_bytes { uint32_t magic1; uint32_t extended_size; @@ -53,25 +43,6 @@ struct target_fpx_sw_bytes { }; QEMU_BUILD_BUG_ON(sizeof(struct target_fpx_sw_bytes) != 12*4); -struct target_fpstate_fxsave { - /* FXSAVE format */ - uint16_t cw; - uint16_t sw; - uint16_t twd; - uint16_t fop; - uint64_t rip; - uint64_t rdp; - uint32_t mxcsr; - uint32_t mxcsr_mask; - uint32_t st_space[32]; - uint32_t xmm_space[64]; - uint32_t hw_reserved[12]; - struct target_fpx_sw_bytes sw_reserved; -}; -#define TARGET_FXSAVE_SIZE sizeof(struct target_fpstate_fxsave) -QEMU_BUILD_BUG_ON(TARGET_FXSAVE_SIZE != 512); -QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_fxsave, sw_reserved) != 464); - struct target_fpstate_32 { /* Regular FPU environment */ uint32_t cw; @@ -84,7 +55,7 @@ struct target_fpstate_32 { struct target_fpreg st[8]; uint16_t status; uint16_t magic; /* 0xffff = regular FPU data only */ - struct target_fpstate_fxsave fxsave; + X86LegacyXSaveArea fxsave; }; /* @@ -97,7 +68,7 @@ QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxsave) & 15); # define target_fpstate target_fpstate_32 # define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxsave) #else -# define target_fpstate target_fpstate_fxsave +# define target_fpstate X86LegacyXSaveArea # define TARGET_FPSTATE_FXSAVE_OFFSET 0 #endif @@ -241,15 +212,17 @@ struct rt_sigframe { * Set up a signal frame. */ -static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave, +static void xsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, abi_ulong fxsave_addr) { + struct target_fpx_sw_bytes *sw = (void *)&fxsave->sw_reserved; + if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { /* fxsave_addr must be 16 byte aligned for fxsave */ assert(!(fxsave_addr & 0xf)); cpu_x86_fxsave(env, fxsave_addr); - __put_user(0, &fxsave->sw_reserved.magic1); + __put_user(0, &sw->magic1); } else { uint32_t xstate_size = xsave_area_size(env->xcr0, false); @@ -267,10 +240,10 @@ static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxs /* Zero the header, XSAVE *adds* features to an existing save state. */ memset(fxsave + 1, 0, sizeof(X86XSaveHeader)); cpu_x86_xsave(env, fxsave_addr, -1); - __put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1); - __put_user(extended_size, &fxsave->sw_reserved.extended_size); - __put_user(env->xcr0, &fxsave->sw_reserved.xfeatures); - __put_user(xstate_size, &fxsave->sw_reserved.xstate_size); + __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1); + __put_user(extended_size, &sw->extended_size); + __put_user(env->xcr0, &sw->xfeatures); + __put_user(xstate_size, &sw->xstate_size); __put_user(TARGET_FP_XSTATE_MAGIC2, (uint32_t *)((void *)fxsave + xstate_size)); } @@ -384,9 +357,9 @@ get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t fxsave_offset } if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { - return (esp - (fxsave_offset + TARGET_FXSAVE_SIZE)) & -8ul; + return (esp - (fxsave_offset + sizeof(X86LegacyXSaveArea))) & -8ul; } else if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { - return ((esp - TARGET_FXSAVE_SIZE) & -16ul) - fxsave_offset; + return ((esp - sizeof(X86LegacyXSaveArea)) & -16ul) - fxsave_offset; } else { size_t xstate_size = xsave_area_size(env->xcr0, false) + TARGET_FP_XSTATE_MAGIC2_SIZE; @@ -552,21 +525,29 @@ give_sigsegv: force_sigsegv(sig); } -static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave, +static int xrstor_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, abi_ulong fxsave_addr) { + struct target_fpx_sw_bytes *sw = (void *)&fxsave->sw_reserved; + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { - uint32_t extended_size = tswapl(fxsave->sw_reserved.extended_size); - uint32_t xstate_size = tswapl(fxsave->sw_reserved.xstate_size); + uint32_t magic1 = tswapl(sw->magic1); + uint32_t extended_size = tswapl(sw->extended_size); + uint32_t xstate_size = tswapl(sw->xstate_size); + uint32_t minimum_size = (TARGET_FPSTATE_FXSAVE_OFFSET + + TARGET_FP_XSTATE_MAGIC2_SIZE + + xstate_size); + uint32_t magic2; /* Linux checks MAGIC2 using xstate_size, not extended_size. */ - if (tswapl(fxsave->sw_reserved.magic1) == TARGET_FP_XSTATE_MAGIC1 && - extended_size >= TARGET_FPSTATE_FXSAVE_OFFSET + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE) { + if (magic1 == TARGET_FP_XSTATE_MAGIC1 + && extended_size >= minimum_size) { if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr, extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) { return 1; } - if (tswapl(*(uint32_t *)((void *)fxsave + xstate_size)) == TARGET_FP_XSTATE_MAGIC2) { + magic2 = tswapl(*(uint32_t *)((void *)fxsave + xstate_size)); + if (magic2 == TARGET_FP_XSTATE_MAGIC2) { cpu_x86_xrstor(env, fxsave_addr, -1); return 0; } From 5cc77ebe9b9e07fcd06011dc23162069ef8c5eff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 10:53:55 -1000 Subject: [PATCH 0936/2884] linux-user/i386: Split out struct target_fregs_state Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 43 +++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 5b1c570bff..3271ebd333 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -34,6 +34,23 @@ struct target_fpreg { uint16_t exponent; }; +/* Legacy x87 fpu state format for FSAVE/FRESTOR. */ +struct target_fregs_state { + uint32_t cwd; + uint32_t swd; + uint32_t twd; + uint32_t fip; + uint32_t fcs; + uint32_t foo; + uint32_t fos; + struct target_fpreg st[8]; + + /* Software status information [not touched by FSAVE]. */ + uint16_t status; + uint16_t magic; /* 0xffff: FPU data only, 0x0000: FXSR FPU data */ +}; +QEMU_BUILD_BUG_ON(sizeof(struct target_fregs_state) != 32 + 80); + struct target_fpx_sw_bytes { uint32_t magic1; uint32_t extended_size; @@ -44,29 +61,19 @@ struct target_fpx_sw_bytes { QEMU_BUILD_BUG_ON(sizeof(struct target_fpx_sw_bytes) != 12*4); struct target_fpstate_32 { - /* Regular FPU environment */ - uint32_t cw; - uint32_t sw; - uint32_t tag; - uint32_t ipoff; - uint32_t cssel; - uint32_t dataoff; - uint32_t datasel; - struct target_fpreg st[8]; - uint16_t status; - uint16_t magic; /* 0xffff = regular FPU data only */ - X86LegacyXSaveArea fxsave; + struct target_fregs_state fpstate; + X86LegacyXSaveArea fxstate; }; /* * For simplicity, setup_frame aligns struct target_fpstate_32 to * 16 bytes, so ensure that the FXSAVE area is also aligned. */ -QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxsave) & 15); +QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxstate) & 15); #ifndef TARGET_X86_64 # define target_fpstate target_fpstate_32 -# define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxsave) +# define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxstate) #else # define target_fpstate X86LegacyXSaveArea # define TARGET_FPSTATE_FXSAVE_OFFSET 0 @@ -279,15 +286,15 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); cpu_x86_fsave(env, fpstate_addr, 1); - fpstate->status = fpstate->sw; + fpstate->fpstate.status = fpstate->fpstate.swd; if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { magic = 0xffff; } else { - xsave_sigcontext(env, &fpstate->fxsave, + xsave_sigcontext(env, &fpstate->fxstate, fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); magic = 0; } - __put_user(magic, &fpstate->magic); + __put_user(magic, &fpstate->fpstate.magic); #else __put_user(env->regs[R_EDI], &sc->rdi); __put_user(env->regs[R_ESI], &sc->rsi); @@ -623,7 +630,7 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) cpu_x86_frstor(env, fpstate_addr, 1); err = 0; } else { - err = xrstor_sigcontext(env, &fpstate->fxsave, + err = xrstor_sigcontext(env, &fpstate->fxstate, fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); } #else From bae0455ce3ded1df80dee4e844194568063ad093 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 11:51:53 -1000 Subject: [PATCH 0937/2884] linux-user/i386: Fix -mregparm=3 for signal delivery Since v2.6.19, the kernel has supported -mregparm=3. Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 3271ebd333..6763b4bda8 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -405,8 +405,6 @@ void setup_frame(int sig, struct target_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - __put_user(sig, &frame->sig); - setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], frame_addr + offsetof(struct sigframe, fpstate)); @@ -428,6 +426,13 @@ void setup_frame(int sig, struct target_sigaction *ka, env->regs[R_ESP] = frame_addr; env->eip = ka->_sa_handler; + /* Store argument for both -mregparm=3 and standard. */ + env->regs[R_EAX] = sig; + __put_user(sig, &frame->sig); + /* The kernel clears EDX and ECX even though there is only one arg. */ + env->regs[R_EDX] = 0; + env->regs[R_ECX] = 0; + cpu_x86_load_seg(env, R_DS, __USER_DS); cpu_x86_load_seg(env, R_ES, __USER_DS); cpu_x86_load_seg(env, R_SS, __USER_DS); @@ -449,9 +454,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUX86State *env) { abi_ulong frame_addr; -#ifndef TARGET_X86_64 - abi_ulong addr; -#endif struct rt_sigframe *frame; int i; @@ -461,14 +463,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - /* These fields are only in rt_sigframe on 32 bit */ -#ifndef TARGET_X86_64 - __put_user(sig, &frame->sig); - addr = frame_addr + offsetof(struct rt_sigframe, info); - __put_user(addr, &frame->pinfo); - addr = frame_addr + offsetof(struct rt_sigframe, uc); - __put_user(addr, &frame->puc); -#endif if (ka->sa_flags & TARGET_SA_SIGINFO) { frame->info = *info; } @@ -508,9 +502,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->eip = ka->_sa_handler; #ifndef TARGET_X86_64 + /* Store arguments for both -mregparm=3 and standard. */ env->regs[R_EAX] = sig; + __put_user(sig, &frame->sig); env->regs[R_EDX] = frame_addr + offsetof(struct rt_sigframe, info); + __put_user(env->regs[R_EDX], &frame->pinfo); env->regs[R_ECX] = frame_addr + offsetof(struct rt_sigframe, uc); + __put_user(env->regs[R_ECX], &frame->puc); #else env->regs[R_EAX] = 0; env->regs[R_EDI] = sig; From c536f9b77ccb771fc480ec8d3c1cefac243eac73 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 13:07:54 -1000 Subject: [PATCH 0938/2884] linux-user/i386: Return boolean success from restore_sigcontext Invert the sense of the return value and use bool. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 51 ++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 6763b4bda8..9e6d883ea1 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -564,12 +564,12 @@ static int xrstor_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, return 0; } -static int -restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) +static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) { - int err = 1; abi_ulong fpstate_addr; unsigned int tmpflags; + struct target_fpstate *fpstate; + bool ok; #ifndef TARGET_X86_64 cpu_x86_load_seg(env, R_GS, tswap16(sc->gs)); @@ -617,29 +617,27 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) // regs->orig_eax = -1; /* disable syscall checks */ fpstate_addr = tswapl(sc->fpstate); - if (fpstate_addr != 0) { - struct target_fpstate *fpstate; - if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr, - sizeof(struct target_fpstate))) { - return err; - } -#ifndef TARGET_X86_64 - if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { - cpu_x86_frstor(env, fpstate_addr, 1); - err = 0; - } else { - err = xrstor_sigcontext(env, &fpstate->fxstate, - fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); - } -#else - err = xrstor_sigcontext(env, fpstate, fpstate_addr); -#endif - unlock_user_struct(fpstate, fpstate_addr, 0); - } else { - err = 0; + if (fpstate_addr == 0) { + return true; } + if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr, + sizeof(struct target_fpstate))) { + return false; + } +#ifndef TARGET_X86_64 + if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { + cpu_x86_frstor(env, fpstate_addr, 1); + ok = true; + } else { + ok = !xrstor_sigcontext(env, &fpstate->fxstate, + fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); + } +#else + ok = !xrstor_sigcontext(env, fpstate, fpstate_addr); +#endif + unlock_user_struct(fpstate, fpstate_addr, 0); - return err; + return ok; } /* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ @@ -665,8 +663,9 @@ long do_sigreturn(CPUX86State *env) set_sigmask(&set); /* restore registers */ - if (restore_sigcontext(env, &frame->sc)) + if (!restore_sigcontext(env, &frame->sc)) { goto badframe; + } unlock_user_struct(frame, frame_addr, 0); return -QEMU_ESIGRETURN; @@ -690,7 +689,7 @@ long do_rt_sigreturn(CPUX86State *env) target_to_host_sigset(&set, &frame->uc.tuc_sigmask); set_sigmask(&set); - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + if (!restore_sigcontext(env, &frame->uc.tuc_mcontext)) { goto badframe; } From 9e9b7d4c15b1cbefc608487a38e10c8f708fb187 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 13:15:03 -1000 Subject: [PATCH 0939/2884] linux-user/i386: Return boolean success from xrstor_sigcontext Invert the sense of the return value and use bool. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 9e6d883ea1..03031ef9e5 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -530,8 +530,8 @@ give_sigsegv: force_sigsegv(sig); } -static int xrstor_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, - abi_ulong fxsave_addr) +static bool xrstor_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, + abi_ulong fxsave_addr) { struct target_fpx_sw_bytes *sw = (void *)&fxsave->sw_reserved; @@ -549,19 +549,19 @@ static int xrstor_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, && extended_size >= minimum_size) { if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr, extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) { - return 1; + return false; } magic2 = tswapl(*(uint32_t *)((void *)fxsave + xstate_size)); if (magic2 == TARGET_FP_XSTATE_MAGIC2) { cpu_x86_xrstor(env, fxsave_addr, -1); - return 0; + return true; } } /* fall through to fxrstor */ } cpu_x86_fxrstor(env, fxsave_addr); - return 0; + return true; } static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) @@ -629,11 +629,11 @@ static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) cpu_x86_frstor(env, fpstate_addr, 1); ok = true; } else { - ok = !xrstor_sigcontext(env, &fpstate->fxstate, - fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); + ok = xrstor_sigcontext(env, &fpstate->fxstate, + fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); } #else - ok = !xrstor_sigcontext(env, fpstate, fpstate_addr); + ok = xrstor_sigcontext(env, fpstate, fpstate_addr); #endif unlock_user_struct(fpstate, fpstate_addr, 0); From a7365e984d27b961f381cf3be46682e4da5ab6f7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 14:30:30 -1000 Subject: [PATCH 0940/2884] linux-user/i386: Fix allocation and alignment of fp state For modern cpus, the kernel uses xsave to store all extra cpu state across the signal handler. For xsave/xrstor to work, the pointer must be 64 byte aligned. Moreover, the regular part of the signal frame must be 16 byte aligned. Attempt to mirror the kernel code as much as possible. Use enum FPStateKind instead of use_xsave() and use_fxsr(). Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1648 Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 558 +++++++++++++++++++------------ tests/tcg/x86_64/Makefile.target | 1 + tests/tcg/x86_64/test-1648.c | 33 ++ 3 files changed, 377 insertions(+), 215 deletions(-) create mode 100644 tests/tcg/x86_64/test-1648.c diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 03031ef9e5..47e6c0ff0d 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -65,20 +65,6 @@ struct target_fpstate_32 { X86LegacyXSaveArea fxstate; }; -/* - * For simplicity, setup_frame aligns struct target_fpstate_32 to - * 16 bytes, so ensure that the FXSAVE area is also aligned. - */ -QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxstate) & 15); - -#ifndef TARGET_X86_64 -# define target_fpstate target_fpstate_32 -# define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxstate) -#else -# define target_fpstate X86LegacyXSaveArea -# define TARGET_FPSTATE_FXSAVE_OFFSET 0 -#endif - struct target_sigcontext_32 { uint16_t gs, __gsh; uint16_t fs, __fsh; @@ -161,24 +147,16 @@ struct sigframe { int sig; struct target_sigcontext sc; /* - * The actual fpstate is placed after retcode[] below, to make - * room for the variable-sized xsave data. The older unused fpstate - * has to be kept to avoid changing the offset of extramask[], which + * The actual fpstate is placed after retcode[] below, to make room + * for the variable-sized xsave data. The older unused fpstate has + * to be kept to avoid changing the offset of extramask[], which * is part of the ABI. */ - struct target_fpstate fpstate_unused; + struct target_fpstate_32 fpstate_unused; abi_ulong extramask[TARGET_NSIG_WORDS-1]; char retcode[8]; - - /* - * This field will be 16-byte aligned in memory. Applying QEMU_ALIGNED - * to it ensures that the base of the frame has an appropriate alignment - * too. - */ - struct target_fpstate fpstate QEMU_ALIGNED(8); + /* fp state follows here */ }; -#define TARGET_SIGFRAME_FXSAVE_OFFSET ( \ - offsetof(struct sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) struct rt_sigframe { abi_ulong pretcode; @@ -188,10 +166,8 @@ struct rt_sigframe { struct target_siginfo info; struct target_ucontext uc; char retcode[8]; - struct target_fpstate fpstate QEMU_ALIGNED(8); + /* fp state follows here */ }; -#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \ - offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) /* * Verify that vdso-asmoffset.h constants match. @@ -209,66 +185,178 @@ struct rt_sigframe { abi_ulong pretcode; struct target_ucontext uc; struct target_siginfo info; - struct target_fpstate fpstate QEMU_ALIGNED(16); + /* fp state follows here */ }; -#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \ - offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) #endif +typedef enum { +#ifndef TARGET_X86_64 + FPSTATE_FSAVE, +#endif + FPSTATE_FXSAVE, + FPSTATE_XSAVE +} FPStateKind; + +static FPStateKind get_fpstate_kind(CPUX86State *env) +{ + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { + return FPSTATE_XSAVE; + } +#ifdef TARGET_X86_64 + return FPSTATE_FXSAVE; +#else + if (env->features[FEAT_1_EDX] & CPUID_FXSR) { + return FPSTATE_FXSAVE; + } + return FPSTATE_FSAVE; +#endif +} + +static unsigned get_fpstate_size(CPUX86State *env, FPStateKind fpkind) +{ + /* + * Kernel: + * fpu__alloc_mathframe + * xstate_sigframe_size(current->thread.fpu.fpstate); + * size = fpstate->user_size + * use_xsave() ? size + FP_XSTATE_MAGIC2_SIZE : size + * where fpstate->user_size is computed at init in + * fpu__init_system_xstate_size_legacy and + * fpu__init_system_xstate. + * + * Here we have no place to pre-compute, so inline it all. + */ + switch (fpkind) { + case FPSTATE_XSAVE: + return (xsave_area_size(env->xcr0, false) + + TARGET_FP_XSTATE_MAGIC2_SIZE); + case FPSTATE_FXSAVE: + return sizeof(X86LegacyXSaveArea); +#ifndef TARGET_X86_64 + case FPSTATE_FSAVE: + return sizeof(struct target_fregs_state); +#endif + } + g_assert_not_reached(); +} + +static abi_ptr get_sigframe(struct target_sigaction *ka, CPUX86State *env, + unsigned frame_size, FPStateKind fpkind, + abi_ptr *fpstate, abi_ptr *fxstate, abi_ptr *fpend) +{ + abi_ptr sp; + unsigned math_size; + + /* Default to using normal stack */ + sp = get_sp_from_cpustate(env); +#ifdef TARGET_X86_64 + sp -= 128; /* this is the redzone */ +#endif + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + sp = target_sigsp(sp, ka); + } else { +#ifndef TARGET_X86_64 + /* This is the legacy signal stack switching. */ + if ((env->segs[R_SS].selector & 0xffff) != __USER_DS + && !(ka->sa_flags & TARGET_SA_RESTORER) + && ka->sa_restorer) { + sp = ka->sa_restorer; + } +#endif + } + + math_size = get_fpstate_size(env, fpkind); + sp = ROUND_DOWN(sp - math_size, 64); + *fpend = sp + math_size; + *fxstate = sp; +#ifndef TARGET_X86_64 + if (fpkind != FPSTATE_FSAVE) { + sp -= sizeof(struct target_fregs_state); + } +#endif + *fpstate = sp; + + sp -= frame_size; + /* + * Align the stack pointer according to the ABI, i.e. so that on + * function entry ((sp + sizeof(return_addr)) & 15) == 0. + */ + sp += sizeof(target_ulong); + sp = ROUND_DOWN(sp, 16); + sp -= sizeof(target_ulong); + + return sp; +} + /* * Set up a signal frame. */ -static void xsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, - abi_ulong fxsave_addr) +static void fxsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr) { - struct target_fpx_sw_bytes *sw = (void *)&fxsave->sw_reserved; + struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; - if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { - /* fxsave_addr must be 16 byte aligned for fxsave */ - assert(!(fxsave_addr & 0xf)); - - cpu_x86_fxsave(env, fxsave_addr); - __put_user(0, &sw->magic1); - } else { - uint32_t xstate_size = xsave_area_size(env->xcr0, false); - - /* - * extended_size is the offset from fpstate_addr to right after the end - * of the extended save states. On 32-bit that includes the legacy - * FSAVE area. - */ - uint32_t extended_size = TARGET_FPSTATE_FXSAVE_OFFSET - + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE; - - /* fxsave_addr must be 64 byte aligned for xsave */ - assert(!(fxsave_addr & 0x3f)); - - /* Zero the header, XSAVE *adds* features to an existing save state. */ - memset(fxsave + 1, 0, sizeof(X86XSaveHeader)); - cpu_x86_xsave(env, fxsave_addr, -1); - __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1); - __put_user(extended_size, &sw->extended_size); - __put_user(env->xcr0, &sw->xfeatures); - __put_user(xstate_size, &sw->xstate_size); - __put_user(TARGET_FP_XSTATE_MAGIC2, - (uint32_t *)((void *)fxsave + xstate_size)); - } + /* fxstate_addr must be 16 byte aligned for fxsave */ + assert(!(fxstate_addr & 0xf)); + cpu_x86_fxsave(env, fxstate_addr); + __put_user(0, &sw->magic1); } -static void setup_sigcontext(struct target_sigcontext *sc, - struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, - abi_ulong fpstate_addr) +static void xsave_sigcontext(CPUX86State *env, + X86LegacyXSaveArea *fxstate, + abi_ptr fpstate_addr, + abi_ptr xstate_addr, + abi_ptr fpend_addr) +{ + struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; + /* + * extended_size is the offset from fpstate_addr to right after + * the end of the extended save states. On 32-bit that includes + * the legacy FSAVE area. + */ + uint32_t extended_size = fpend_addr - fpstate_addr; + /* Recover xstate_size by removing magic2. */ + uint32_t xstate_size = (fpend_addr - xstate_addr + - TARGET_FP_XSTATE_MAGIC2_SIZE); + /* magic2 goes just after xstate. */ + uint32_t *magic2 = (void *)fxstate + xstate_size; + + /* xstate_addr must be 64 byte aligned for xsave */ + assert(!(xstate_addr & 0x3f)); + + /* Zero the header, XSAVE *adds* features to an existing save state. */ + memset(fxstate + 1, 0, sizeof(X86XSaveHeader)); + cpu_x86_xsave(env, xstate_addr, -1); + + __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1); + __put_user(extended_size, &sw->extended_size); + __put_user(env->xcr0, &sw->xfeatures); + __put_user(xstate_size, &sw->xstate_size); + __put_user(TARGET_FP_XSTATE_MAGIC2, magic2); +} + +static void setup_sigcontext(CPUX86State *env, + struct target_sigcontext *sc, + abi_ulong mask, FPStateKind fpkind, + struct target_fregs_state *fpstate, + abi_ptr fpstate_addr, + X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr, + abi_ptr fpend_addr) { CPUState *cs = env_cpu(env); + #ifndef TARGET_X86_64 uint16_t magic; /* already locked in setup_frame() */ - __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs); - __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs); - __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es); - __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds); + __put_user(env->segs[R_GS].selector, (uint32_t *)&sc->gs); + __put_user(env->segs[R_FS].selector, (uint32_t *)&sc->fs); + __put_user(env->segs[R_ES].selector, (uint32_t *)&sc->es); + __put_user(env->segs[R_DS].selector, (uint32_t *)&sc->ds); __put_user(env->regs[R_EDI], &sc->edi); __put_user(env->regs[R_ESI], &sc->esi); __put_user(env->regs[R_EBP], &sc->ebp); @@ -280,21 +368,15 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user(cs->exception_index, &sc->trapno); __put_user(env->error_code, &sc->err); __put_user(env->eip, &sc->eip); - __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs); + __put_user(env->segs[R_CS].selector, (uint32_t *)&sc->cs); __put_user(env->eflags, &sc->eflags); __put_user(env->regs[R_ESP], &sc->esp_at_signal); - __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); + __put_user(env->segs[R_SS].selector, (uint32_t *)&sc->ss); cpu_x86_fsave(env, fpstate_addr, 1); - fpstate->fpstate.status = fpstate->fpstate.swd; - if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { - magic = 0xffff; - } else { - xsave_sigcontext(env, &fpstate->fxstate, - fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); - magic = 0; - } - __put_user(magic, &fpstate->fpstate.magic); + fpstate->status = fpstate->swd; + magic = (fpkind == FPSTATE_FSAVE ? 0 : 0xffff); + __put_user(magic, &fpstate->magic); #else __put_user(env->regs[R_EDI], &sc->rdi); __put_user(env->regs[R_ESI], &sc->rsi); @@ -323,57 +405,25 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user((uint16_t)0, &sc->gs); __put_user((uint16_t)0, &sc->fs); __put_user(env->segs[R_SS].selector, &sc->ss); - - xsave_sigcontext(env, fpstate, fpstate_addr); #endif - __put_user(fpstate_addr, &sc->fpstate); + switch (fpkind) { + case FPSTATE_XSAVE: + xsave_sigcontext(env, fxstate, fpstate_addr, fxstate_addr, fpend_addr); + break; + case FPSTATE_FXSAVE: + fxsave_sigcontext(env, fxstate, fxstate_addr); + break; + default: + break; + } + __put_user(fpstate_addr, &sc->fpstate); /* non-iBCS2 extensions.. */ __put_user(mask, &sc->oldmask); __put_user(env->cr[2], &sc->cr2); } -/* - * Determine which stack to use.. - */ - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t fxsave_offset) -{ - unsigned long esp; - - /* Default to using normal stack */ - esp = get_sp_from_cpustate(env); -#ifdef TARGET_X86_64 - esp -= 128; /* this is the redzone */ -#endif - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa_flags & TARGET_SA_ONSTACK) { - esp = target_sigsp(esp, ka); - } else { -#ifndef TARGET_X86_64 - /* This is the legacy signal stack switching. */ - if ((env->segs[R_SS].selector & 0xffff) != __USER_DS && - !(ka->sa_flags & TARGET_SA_RESTORER) && - ka->sa_restorer) { - esp = (unsigned long) ka->sa_restorer; - } -#endif - } - - if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { - return (esp - (fxsave_offset + sizeof(X86LegacyXSaveArea))) & -8ul; - } else if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { - return ((esp - sizeof(X86LegacyXSaveArea)) & -16ul) - fxsave_offset; - } else { - size_t xstate_size = - xsave_area_size(env->xcr0, false) + TARGET_FP_XSTATE_MAGIC2_SIZE; - return ((esp - xstate_size) & -64ul) - fxsave_offset; - } -} - #ifndef TARGET_X86_64 static void install_sigtramp(void *tramp) { @@ -395,20 +445,36 @@ static void install_rt_sigtramp(void *tramp) void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUX86State *env) { - abi_ulong frame_addr; + abi_ptr frame_addr, fpstate_addr, fxstate_addr, fpend_addr; struct sigframe *frame; - int i; + struct target_fregs_state *fpstate; + X86LegacyXSaveArea *fxstate; + unsigned total_size; + FPStateKind fpkind; - frame_addr = get_sigframe(ka, env, TARGET_SIGFRAME_FXSAVE_OFFSET); + fpkind = get_fpstate_kind(env); + frame_addr = get_sigframe(ka, env, sizeof(struct sigframe), fpkind, + &fpstate_addr, &fxstate_addr, &fpend_addr); trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; + total_size = fpend_addr - frame_addr; + frame = lock_user(VERIFY_WRITE, frame_addr, total_size, 0); + if (!frame) { + force_sigsegv(sig); + return; + } - setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], - frame_addr + offsetof(struct sigframe, fpstate)); + fxstate = (void *)frame + (fxstate_addr - frame_addr); +#ifdef TARGET_X86_64 + fpstate = NULL; +#else + fpstate = (void *)frame + (fpstate_addr - frame_addr); +#endif - for (i = 1; i < TARGET_NSIG_WORDS; i++) { + setup_sigcontext(env, &frame->sc, set->sig[0], fpkind, + fpstate, fpstate_addr, fxstate, fxstate_addr, fpend_addr); + + for (int i = 1; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->extramask[i - 1]); } @@ -421,6 +487,7 @@ void setup_frame(int sig, struct target_sigaction *ka, install_sigtramp(frame->retcode); __put_user(default_sigreturn, &frame->pretcode); } + unlock_user(frame, frame_addr, total_size); /* Set up registers for signal handler */ env->regs[R_ESP] = frame_addr; @@ -438,13 +505,6 @@ void setup_frame(int sig, struct target_sigaction *ka, cpu_x86_load_seg(env, R_SS, __USER_DS); cpu_x86_load_seg(env, R_CS, __USER_CS); env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - - return; - -give_sigsegv: - force_sigsegv(sig); } #endif @@ -453,37 +513,51 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUX86State *env) { - abi_ulong frame_addr; + abi_ptr frame_addr, fpstate_addr, fxstate_addr, fpend_addr; struct rt_sigframe *frame; - int i; + X86LegacyXSaveArea *fxstate; + struct target_fregs_state *fpstate; + unsigned total_size; + FPStateKind fpkind; - frame_addr = get_sigframe(ka, env, TARGET_RT_SIGFRAME_FXSAVE_OFFSET); + fpkind = get_fpstate_kind(env); + frame_addr = get_sigframe(ka, env, sizeof(struct rt_sigframe), fpkind, + &fpstate_addr, &fxstate_addr, &fpend_addr); trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + total_size = fpend_addr - frame_addr; + frame = lock_user(VERIFY_WRITE, frame_addr, total_size, 0); + if (!frame) { goto give_sigsegv; + } if (ka->sa_flags & TARGET_SA_SIGINFO) { frame->info = *info; } /* Create the ucontext. */ - if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { - __put_user(1, &frame->uc.tuc_flags); - } else { - __put_user(0, &frame->uc.tuc_flags); - } + __put_user(fpkind == FPSTATE_XSAVE, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); target_save_altstack(&frame->uc.tuc_stack, env); - setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, - set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { + fxstate = (void *)frame + (fxstate_addr - frame_addr); +#ifdef TARGET_X86_64 + fpstate = NULL; +#else + fpstate = (void *)frame + (fpstate_addr - frame_addr); +#endif + + setup_sigcontext(env, &frame->uc.tuc_mcontext, set->sig[0], fpkind, + fpstate, fpstate_addr, fxstate, fxstate_addr, fpend_addr); + + for (int i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ + /* + * Set up to return from userspace. If provided, use a stub + * already in userspace. + */ if (ka->sa_flags & TARGET_SA_RESTORER) { __put_user(ka->sa_restorer, &frame->pretcode); } else { @@ -515,60 +589,113 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->regs[R_ESI] = frame_addr + offsetof(struct rt_sigframe, info); env->regs[R_EDX] = frame_addr + offsetof(struct rt_sigframe, uc); #endif + unlock_user(frame, frame_addr, total_size); cpu_x86_load_seg(env, R_DS, __USER_DS); cpu_x86_load_seg(env, R_ES, __USER_DS); cpu_x86_load_seg(env, R_CS, __USER_CS); cpu_x86_load_seg(env, R_SS, __USER_DS); env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - return; give_sigsegv: force_sigsegv(sig); } -static bool xrstor_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxsave, - abi_ulong fxsave_addr) +/* + * Restore a signal frame. + */ + +static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, + X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr) { - struct target_fpx_sw_bytes *sw = (void *)&fxsave->sw_reserved; + struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; + uint32_t magic1, magic2; + uint32_t extended_size, xstate_size, min_size, max_size; - if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { - uint32_t magic1 = tswapl(sw->magic1); - uint32_t extended_size = tswapl(sw->extended_size); - uint32_t xstate_size = tswapl(sw->xstate_size); - uint32_t minimum_size = (TARGET_FPSTATE_FXSAVE_OFFSET - + TARGET_FP_XSTATE_MAGIC2_SIZE - + xstate_size); - uint32_t magic2; + switch (fpkind) { + case FPSTATE_XSAVE: + magic1 = tswap32(sw->magic1); + extended_size = tswap32(sw->extended_size); + xstate_size = tswap32(sw->xstate_size); + min_size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); + max_size = xsave_area_size(env->xcr0, false); - /* Linux checks MAGIC2 using xstate_size, not extended_size. */ - if (magic1 == TARGET_FP_XSTATE_MAGIC1 - && extended_size >= minimum_size) { - if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr, - extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) { - return false; - } - magic2 = tswapl(*(uint32_t *)((void *)fxsave + xstate_size)); - if (magic2 == TARGET_FP_XSTATE_MAGIC2) { - cpu_x86_xrstor(env, fxsave_addr, -1); - return true; - } + /* Check for the first magic field and other error scenarios. */ + if (magic1 != TARGET_FP_XSTATE_MAGIC1 || + xstate_size < min_size || + xstate_size > max_size || + xstate_size > extended_size) { + break; } - /* fall through to fxrstor */ + if (!access_ok(env_cpu(env), VERIFY_READ, fxstate_addr, + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE)) { + return false; + } + /* + * Check for the presence of second magic word at the end of memory + * layout. This detects the case where the user just copied the legacy + * fpstate layout with out copying the extended state information + * in the memory layout. + */ + if (get_user_u32(magic2, fxstate_addr + xstate_size)) { + return false; + } + if (magic2 != TARGET_FP_XSTATE_MAGIC2) { + break; + } + cpu_x86_xrstor(env, fxstate_addr, -1); + return true; + + default: + break; } - cpu_x86_fxrstor(env, fxsave_addr); + cpu_x86_fxrstor(env, fxstate_addr); return true; } +#ifndef TARGET_X86_64 +static bool frstor_sigcontext(CPUX86State *env, FPStateKind fpkind, + struct target_fregs_state *fpstate, + abi_ptr fpstate_addr, + X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr) +{ + switch (fpkind) { + case FPSTATE_XSAVE: + if (!xrstor_sigcontext(env, fpkind, fxstate, fxstate_addr)) { + return false; + } + break; + case FPSTATE_FXSAVE: + cpu_x86_fxrstor(env, fxstate_addr); + break; + case FPSTATE_FSAVE: + break; + default: + g_assert_not_reached(); + } + + /* + * Copy the legacy state because the FP portion of the FX frame has + * to be ignored for histerical raisins. The kernel folds the two + * states together and then performs a single load; here we perform + * the merge within ENV by loading XSTATE/FXSTATE first, then + * overriding with the FSTATE afterward. + */ + cpu_x86_frstor(env, fpstate_addr, 1); + return true; +} +#endif + static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) { - abi_ulong fpstate_addr; - unsigned int tmpflags; - struct target_fpstate *fpstate; + abi_ptr fpstate_addr; + unsigned tmpflags, math_size; + FPStateKind fpkind; + void *fpstate; bool ok; #ifndef TARGET_X86_64 @@ -614,29 +741,33 @@ static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) tmpflags = tswapl(sc->eflags); env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); - // regs->orig_eax = -1; /* disable syscall checks */ fpstate_addr = tswapl(sc->fpstate); if (fpstate_addr == 0) { return true; } - if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr, - sizeof(struct target_fpstate))) { + + fpkind = get_fpstate_kind(env); + math_size = get_fpstate_size(env, fpkind); +#ifndef TARGET_X86_64 + if (fpkind != FPSTATE_FSAVE) { + math_size += sizeof(struct target_fregs_state); + } +#endif + fpstate = lock_user(VERIFY_READ, fpstate_addr, math_size, 1); + if (!fpstate) { return false; } -#ifndef TARGET_X86_64 - if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { - cpu_x86_frstor(env, fpstate_addr, 1); - ok = true; - } else { - ok = xrstor_sigcontext(env, &fpstate->fxstate, - fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); - } -#else - ok = xrstor_sigcontext(env, fpstate, fpstate_addr); -#endif - unlock_user_struct(fpstate, fpstate_addr, 0); +#ifdef TARGET_X86_64 + ok = xrstor_sigcontext(env, fpkind, fpstate, fpstate_addr); +#else + ok = frstor_sigcontext(env, fpkind, fpstate, fpstate_addr, + fpstate + sizeof(struct target_fregs_state), + fpstate_addr + sizeof(struct target_fregs_state)); +#endif + + unlock_user(fpstate, fpstate_addr, 0); return ok; } @@ -648,30 +779,27 @@ long do_sigreturn(CPUX86State *env) abi_ulong frame_addr = env->regs[R_ESP] - 8; target_sigset_t target_set; sigset_t set; - int i; trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - /* set blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + force_sig(TARGET_SIGSEGV); + return -QEMU_ESIGRETURN; } + /* Set blocked signals. */ + __get_user(target_set.sig[0], &frame->sc.oldmask); + for (int i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } target_to_host_sigset_internal(&set, &target_set); set_sigmask(&set); - /* restore registers */ + /* Restore registers */ if (!restore_sigcontext(env, &frame->sc)) { - goto badframe; + force_sig(TARGET_SIGSEGV); } - unlock_user_struct(frame, frame_addr, 0); - return -QEMU_ESIGRETURN; -badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); return -QEMU_ESIGRETURN; } #endif diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index e64aab1b81..5fedf22117 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -13,6 +13,7 @@ X86_64_TESTS += vsyscall X86_64_TESTS += noexec X86_64_TESTS += cmpxchg X86_64_TESTS += adox +X86_64_TESTS += test-1648 TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else TESTS=$(MULTIARCH_TESTS) diff --git a/tests/tcg/x86_64/test-1648.c b/tests/tcg/x86_64/test-1648.c new file mode 100644 index 0000000000..fd0644a8ce --- /dev/null +++ b/tests/tcg/x86_64/test-1648.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/1648 */ + +#include + +__attribute__((noinline)) +void bar(void) +{ + /* Success! Continue through sigreturn. */ +} + +/* + * Because of the change of ABI between foo and bar, the compiler is + * required to save XMM6-XMM15. The compiler will use MOVAPS or MOVDQA, + * which will trap if the stack frame is not 16 byte aligned. + */ +__attribute__((noinline, ms_abi)) +void foo(void) +{ + bar(); +} + +void sighandler(int num) +{ + foo(); +} + +int main(void) +{ + signal(SIGUSR1, sighandler); + raise(SIGUSR1); + return 0; +} From 7973eb943e670ea66a19e04868e01803c7594246 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 14:51:54 -1000 Subject: [PATCH 0941/2884] linux-user/i386: Honor xfeatures in xrstor_sigcontext Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 47e6c0ff0d..e716ec8989 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -613,6 +613,7 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; uint32_t magic1, magic2; uint32_t extended_size, xstate_size, min_size, max_size; + uint64_t xfeatures; switch (fpkind) { case FPSTATE_XSAVE: @@ -629,10 +630,25 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, xstate_size > extended_size) { break; } + + /* + * Restore the features indicated in the frame, masked by + * those currently enabled. Re-check the frame size. + * ??? It is not clear where the kernel does this, but it + * is not in check_xstate_in_sigframe, and so (probably) + * does not fall back to fxrstor. + */ + xfeatures = tswap64(sw->xfeatures) & env->xcr0; + min_size = xsave_area_size(xfeatures, false); + if (xstate_size < min_size) { + return false; + } + if (!access_ok(env_cpu(env), VERIFY_READ, fxstate_addr, xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE)) { return false; } + /* * Check for the presence of second magic word at the end of memory * layout. This detects the case where the user just copied the legacy @@ -645,7 +661,8 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, if (magic2 != TARGET_FP_XSTATE_MAGIC2) { break; } - cpu_x86_xrstor(env, fxstate_addr, -1); + + cpu_x86_xrstor(env, fxstate_addr, xfeatures); return true; default: From c6e6d1508ac309e39fec827c3719c86c10cd8975 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 15:05:28 -1000 Subject: [PATCH 0942/2884] target/i386: Convert do_xsave to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 2 +- target/i386/tcg/fpu_helper.c | 72 +++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index e716ec8989..ab760db5ea 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -329,7 +329,7 @@ static void xsave_sigcontext(CPUX86State *env, /* Zero the header, XSAVE *adds* features to an existing save state. */ memset(fxstate + 1, 0, sizeof(X86XSaveHeader)); - cpu_x86_xsave(env, xstate_addr, -1); + cpu_x86_xsave(env, xstate_addr, env->xcr0); __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1); __put_user(extended_size, &sw->extended_size); diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index a09d6aaf07..f5748b72b8 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2668,47 +2668,38 @@ static uint64_t get_xinuse(CPUX86State *env) return inuse; } -static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, - uint64_t inuse, uint64_t opt, uintptr_t ra) +static void do_xsave_access(X86Access *ac, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt) { uint64_t old_bv, new_bv; - X86Access ac; - unsigned size; - - /* Never save anything not enabled by XCR0. */ - rfbm &= env->xcr0; - opt &= rfbm; - - size = xsave_area_size(opt, false); - access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, ra); if (opt & XSTATE_FP_MASK) { - do_xsave_fpu(&ac, ptr); + do_xsave_fpu(ac, ptr); } if (rfbm & XSTATE_SSE_MASK) { /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ - do_xsave_mxcsr(&ac, ptr); + do_xsave_mxcsr(ac, ptr); } if (opt & XSTATE_SSE_MASK) { - do_xsave_sse(&ac, ptr); + do_xsave_sse(ac, ptr); } if (opt & XSTATE_YMM_MASK) { - do_xsave_ymmh(&ac, ptr + XO(avx_state)); + do_xsave_ymmh(ac, ptr + XO(avx_state)); } if (opt & XSTATE_BNDREGS_MASK) { - do_xsave_bndregs(&ac, ptr + XO(bndreg_state)); + do_xsave_bndregs(ac, ptr + XO(bndreg_state)); } if (opt & XSTATE_BNDCSR_MASK) { - do_xsave_bndcsr(&ac, ptr + XO(bndcsr_state)); + do_xsave_bndcsr(ac, ptr + XO(bndcsr_state)); } if (opt & XSTATE_PKRU_MASK) { - do_xsave_pkru(&ac, ptr + XO(pkru_state)); + do_xsave_pkru(ac, ptr + XO(pkru_state)); } /* Update the XSTATE_BV field. */ - old_bv = access_ldq(&ac, ptr + XO(header.xstate_bv)); + old_bv = access_ldq(ac, ptr + XO(header.xstate_bv)); new_bv = (old_bv & ~rfbm) | (inuse & rfbm); - access_stq(&ac, ptr + XO(header.xstate_bv), new_bv); + access_stq(ac, ptr + XO(header.xstate_bv), new_bv); } static void do_xsave_chk(CPUX86State *env, target_ulong ptr, uintptr_t ra) @@ -2724,22 +2715,32 @@ static void do_xsave_chk(CPUX86State *env, target_ulong ptr, uintptr_t ra) } } -void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt, uintptr_t ra) { - uintptr_t ra = GETPC(); + X86Access ac; + unsigned size; do_xsave_chk(env, ptr, ra); - do_xsave(env, ptr, rfbm, get_xinuse(env), -1, ra); + + /* Never save anything not enabled by XCR0. */ + rfbm &= env->xcr0; + opt &= rfbm; + size = xsave_area_size(opt, false); + + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, ra); + do_xsave_access(&ac, ptr, rfbm, inuse, opt); +} + +void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + do_xsave(env, ptr, rfbm, get_xinuse(env), rfbm, GETPC()); } void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - uintptr_t ra = GETPC(); - uint64_t inuse; - - do_xsave_chk(env, ptr, ra); - inuse = get_xinuse(env); - do_xsave(env, ptr, rfbm, inuse, inuse, ra); + uint64_t inuse = get_xinuse(env); + do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); } static void do_xrstor_fpu(X86Access *ac, target_ulong ptr) @@ -3049,7 +3050,18 @@ void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) void cpu_x86_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xsave(env, ptr, rfbm, get_xinuse(env), -1, 0); + X86Access ac; + unsigned size; + + /* + * Since this is only called from user-level signal handling, + * we should have done the job correctly there. + */ + assert((rfbm & ~env->xcr0) == 0); + size = xsave_area_size(rfbm, false); + + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, 0); + do_xsave_access(&ac, ptr, rfbm, get_xinuse(env), rfbm); } void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) From d5dc3a927ae7e64bc998d9aa29020426b4e97f8a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 16:07:05 -1000 Subject: [PATCH 0943/2884] target/i386: Convert do_xrstor to X86Access Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- target/i386/tcg/fpu_helper.c | 106 +++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index f5748b72b8..1ac61c5d7d 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2903,51 +2903,38 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr) do_fxrstor(&ac, ptr); } -static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr_t ra) +static bool valid_xrstor_header(X86Access *ac, uint64_t *pxsbv, + target_ulong ptr) { uint64_t xstate_bv, xcomp_bv, reserve0; - X86Access ac; - unsigned size, size_ext; - rfbm &= env->xcr0; + xstate_bv = access_ldq(ac, ptr + XO(header.xstate_bv)); + xcomp_bv = access_ldq(ac, ptr + XO(header.xcomp_bv)); + reserve0 = access_ldq(ac, ptr + XO(header.reserve0)); + *pxsbv = xstate_bv; - size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); - access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, ra); - - xstate_bv = access_ldq(&ac, ptr + XO(header.xstate_bv)); - - if ((int64_t)xstate_bv < 0) { - /* FIXME: Compact form. */ - raise_exception_ra(env, EXCP0D_GPF, ra); + /* + * XCOMP_BV bit 63 indicates compact form, which we do not support, + * and thus must raise #GP. That leaves us in standard form. + * In standard form, bytes 23:8 must be zero -- which is both + * XCOMP_BV and the following 64-bit field. + */ + if (xcomp_bv || reserve0) { + return false; } - /* Standard form. */ - /* The XSTATE_BV field must not set bits not present in XCR0. */ - if (xstate_bv & ~env->xcr0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } + return (xstate_bv & ~ac->env->xcr0) == 0; +} - /* The XCOMP_BV field must be zero. Note that, as of the April 2016 - revision, the description of the XSAVE Header (Vol 1, Sec 13.4.2) - describes only XCOMP_BV, but the description of the standard form - of XRSTOR (Vol 1, Sec 13.8.1) checks bytes 23:8 for zero, which - includes the next 64-bit field. */ - xcomp_bv = access_ldq(&ac, ptr + XO(header.xcomp_bv)); - reserve0 = access_ldq(&ac, ptr + XO(header.reserve0)); - if (xcomp_bv || reserve0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - size_ext = xsave_area_size(rfbm & xstate_bv, false); - if (size < size_ext) { - /* TODO: See if existing page probe has covered extra size. */ - access_prepare(&ac, env, ptr, size_ext, MMU_DATA_LOAD, ra); - } +static void do_xrstor(X86Access *ac, target_ulong ptr, + uint64_t rfbm, uint64_t xstate_bv) +{ + CPUX86State *env = ac->env; if (rfbm & XSTATE_FP_MASK) { if (xstate_bv & XSTATE_FP_MASK) { - do_xrstor_fpu(&ac, ptr); + do_xrstor_fpu(ac, ptr); } else { do_fninit(env); memset(env->fpregs, 0, sizeof(env->fpregs)); @@ -2956,23 +2943,23 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr if (rfbm & XSTATE_SSE_MASK) { /* Note that the standard form of XRSTOR loads MXCSR from memory whether or not the XSTATE_BV bit is set. */ - do_xrstor_mxcsr(&ac, ptr); + do_xrstor_mxcsr(ac, ptr); if (xstate_bv & XSTATE_SSE_MASK) { - do_xrstor_sse(&ac, ptr); + do_xrstor_sse(ac, ptr); } else { do_clear_sse(env); } } if (rfbm & XSTATE_YMM_MASK) { if (xstate_bv & XSTATE_YMM_MASK) { - do_xrstor_ymmh(&ac, ptr + XO(avx_state)); + do_xrstor_ymmh(ac, ptr + XO(avx_state)); } else { do_clear_ymmh(env); } } if (rfbm & XSTATE_BNDREGS_MASK) { if (xstate_bv & XSTATE_BNDREGS_MASK) { - do_xrstor_bndregs(&ac, ptr + XO(bndreg_state)); + do_xrstor_bndregs(ac, ptr + XO(bndreg_state)); env->hflags |= HF_MPX_IU_MASK; } else { memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); @@ -2981,7 +2968,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr } if (rfbm & XSTATE_BNDCSR_MASK) { if (xstate_bv & XSTATE_BNDCSR_MASK) { - do_xrstor_bndcsr(&ac, ptr + XO(bndcsr_state)); + do_xrstor_bndcsr(ac, ptr + XO(bndcsr_state)); } else { memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); } @@ -2990,7 +2977,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr if (rfbm & XSTATE_PKRU_MASK) { uint64_t old_pkru = env->pkru; if (xstate_bv & XSTATE_PKRU_MASK) { - do_xrstor_pkru(&ac, ptr + XO(pkru_state)); + do_xrstor_pkru(ac, ptr + XO(pkru_state)); } else { env->pkru = 0; } @@ -3006,9 +2993,27 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { uintptr_t ra = GETPC(); + X86Access ac; + uint64_t xstate_bv; + unsigned size, size_ext; do_xsave_chk(env, ptr, ra); - do_xrstor(env, ptr, rfbm, ra); + + /* Begin with just the minimum size to validate the header. */ + size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, ra); + if (!valid_xrstor_header(&ac, &xstate_bv, ptr)) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + rfbm &= env->xcr0; + size_ext = xsave_area_size(rfbm & xstate_bv, false); + if (size < size_ext) { + /* TODO: See if existing page probe has covered extra size. */ + access_prepare(&ac, env, ptr, size_ext, MMU_DATA_LOAD, ra); + } + + do_xrstor(&ac, ptr, rfbm, xstate_bv); } #if defined(CONFIG_USER_ONLY) @@ -3066,7 +3071,24 @@ void cpu_x86_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xrstor(env, ptr, rfbm, 0); + X86Access ac; + uint64_t xstate_bv; + unsigned size; + + /* + * Since this is only called from user-level signal handling, + * we should have done the job correctly there. + */ + assert((rfbm & ~env->xcr0) == 0); + size = xsave_area_size(rfbm, false); + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, 0); + + if (!valid_xrstor_header(&ac, &xstate_bv, ptr)) { + /* TODO: Report failure to caller. */ + xstate_bv &= env->xcr0; + } + + do_xrstor(&ac, ptr, rfbm, xstate_bv); } #endif From 76d8d0f85caf629b4df314e656d20ad6565bab9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 17:31:05 -1000 Subject: [PATCH 0944/2884] target/i386: Pass host pointer and size to cpu_x86_{fsave,frstor} We have already validated the memory region in the course of validating the signal frame. No need to do it again within the helper function. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 4 ++-- target/i386/cpu.h | 10 ++++++---- target/i386/tcg/fpu_helper.c | 26 ++++++++++++++++---------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index ab760db5ea..dfbb811b56 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -373,7 +373,7 @@ static void setup_sigcontext(CPUX86State *env, __put_user(env->regs[R_ESP], &sc->esp_at_signal); __put_user(env->segs[R_SS].selector, (uint32_t *)&sc->ss); - cpu_x86_fsave(env, fpstate_addr, 1); + cpu_x86_fsave(env, fpstate, sizeof(*fpstate)); fpstate->status = fpstate->swd; magic = (fpkind == FPSTATE_FSAVE ? 0 : 0xffff); __put_user(magic, &fpstate->magic); @@ -702,7 +702,7 @@ static bool frstor_sigcontext(CPUX86State *env, FPStateKind fpkind, * the merge within ENV by loading XSTATE/FXSTATE first, then * overriding with the FSTATE afterward. */ - cpu_x86_frstor(env, fpstate_addr, 1); + cpu_x86_frstor(env, fpstate, sizeof(*fpstate)); return true; } #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index fdd318963a..f6020e0b6b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2266,11 +2266,13 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, /* used for debug or cpu save/restore */ /* cpu-exec.c */ -/* the following helpers are only usable in user mode simulation as - they can trigger unexpected exceptions */ +/* + * The following helpers are only usable in user mode simulation. + * The host pointers should come from lock_user(). + */ void cpu_x86_load_seg(CPUX86State *s, X86Seg seg_reg, int selector); -void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32); -void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); +void cpu_x86_fsave(CPUX86State *s, void *host, size_t len); +void cpu_x86_frstor(CPUX86State *s, void *host, size_t len); void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr); void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr); void cpu_x86_xsave(CPUX86State *s, target_ulong ptr, uint64_t rbfm); diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 1ac61c5d7d..05db16a152 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -3017,22 +3017,28 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) } #if defined(CONFIG_USER_ONLY) -void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) +void cpu_x86_fsave(CPUX86State *env, void *host, size_t len) { - int size = (14 << data32) + 80; - X86Access ac; + X86Access ac = { + .haddr1 = host, + .size = 4 * 7 + 8 * 10, + .env = env, + }; - access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, 0); - do_fsave(&ac, ptr, data32); + assert(ac.size <= len); + do_fsave(&ac, 0, true); } -void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) +void cpu_x86_frstor(CPUX86State *env, void *host, size_t len) { - int size = (14 << data32) + 80; - X86Access ac; + X86Access ac = { + .haddr1 = host, + .size = 4 * 7 + 8 * 10, + .env = env, + }; - access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, 0); - do_frstor(&ac, ptr, data32); + assert(ac.size <= len); + do_frstor(&ac, 0, true); } void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) From 9c2fb9e1d589fbda266d8db611b9d3a38ab96a3c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 17:57:11 -1000 Subject: [PATCH 0945/2884] target/i386: Pass host pointer and size to cpu_x86_{fxsave,fxrstor} We have already validated the memory region in the course of validating the signal frame. No need to do it again within the helper function. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 13 +++++-------- target/i386/cpu.h | 4 ++-- target/i386/tcg/fpu_helper.c | 26 ++++++++++++++++---------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index dfbb811b56..2e2972002b 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -294,14 +294,11 @@ static abi_ptr get_sigframe(struct target_sigaction *ka, CPUX86State *env, * Set up a signal frame. */ -static void fxsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxstate, - abi_ptr fxstate_addr) +static void fxsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxstate) { struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; - /* fxstate_addr must be 16 byte aligned for fxsave */ - assert(!(fxstate_addr & 0xf)); - cpu_x86_fxsave(env, fxstate_addr); + cpu_x86_fxsave(env, fxstate, sizeof(*fxstate)); __put_user(0, &sw->magic1); } @@ -412,7 +409,7 @@ static void setup_sigcontext(CPUX86State *env, xsave_sigcontext(env, fxstate, fpstate_addr, fxstate_addr, fpend_addr); break; case FPSTATE_FXSAVE: - fxsave_sigcontext(env, fxstate, fxstate_addr); + fxsave_sigcontext(env, fxstate); break; default: break; @@ -669,7 +666,7 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, break; } - cpu_x86_fxrstor(env, fxstate_addr); + cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate)); return true; } @@ -687,7 +684,7 @@ static bool frstor_sigcontext(CPUX86State *env, FPStateKind fpkind, } break; case FPSTATE_FXSAVE: - cpu_x86_fxrstor(env, fxstate_addr); + cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate)); break; case FPSTATE_FSAVE: break; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index f6020e0b6b..257cd5a617 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2273,8 +2273,8 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, void cpu_x86_load_seg(CPUX86State *s, X86Seg seg_reg, int selector); void cpu_x86_fsave(CPUX86State *s, void *host, size_t len); void cpu_x86_frstor(CPUX86State *s, void *host, size_t len); -void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr); -void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr); +void cpu_x86_fxsave(CPUX86State *s, void *host, size_t len); +void cpu_x86_fxrstor(CPUX86State *s, void *host, size_t len); void cpu_x86_xsave(CPUX86State *s, target_ulong ptr, uint64_t rbfm); void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr, uint64_t rbfm); diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 05db16a152..0e5368951f 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -3041,22 +3041,28 @@ void cpu_x86_frstor(CPUX86State *env, void *host, size_t len) do_frstor(&ac, 0, true); } -void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) +void cpu_x86_fxsave(CPUX86State *env, void *host, size_t len) { - X86Access ac; + X86Access ac = { + .haddr1 = host, + .size = sizeof(X86LegacyXSaveArea), + .env = env, + }; - access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), - MMU_DATA_STORE, 0); - do_fxsave(&ac, ptr); + assert(ac.size <= len); + do_fxsave(&ac, 0); } -void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) +void cpu_x86_fxrstor(CPUX86State *env, void *host, size_t len) { - X86Access ac; + X86Access ac = { + .haddr1 = host, + .size = sizeof(X86LegacyXSaveArea), + .env = env, + }; - access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), - MMU_DATA_LOAD, 0); - do_fxrstor(&ac, ptr); + assert(ac.size <= len); + do_fxrstor(&ac, 0); } void cpu_x86_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) From 701890bdd09b289fd9cb852e714e91373088b0f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Apr 2024 18:04:19 -1000 Subject: [PATCH 0946/2884] target/i386: Pass host pointer and size to cpu_x86_{xsave,xrstor} We have already validated the memory region in the course of validating the signal frame. No need to do it again within the helper function. In addition, return failure when the header contains invalid xstate_bv. The kernel handles this via exception handling within XSTATE_OP within xrstor_from_user_sigframe. Reviewed-by: Paolo Bonzini Signed-off-by: Richard Henderson --- linux-user/i386/signal.c | 20 ++++++++++++-------- target/i386/cpu.h | 4 ++-- target/i386/tcg/fpu_helper.c | 36 +++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 2e2972002b..cb90711834 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -326,7 +326,7 @@ static void xsave_sigcontext(CPUX86State *env, /* Zero the header, XSAVE *adds* features to an existing save state. */ memset(fxstate + 1, 0, sizeof(X86XSaveHeader)); - cpu_x86_xsave(env, xstate_addr, env->xcr0); + cpu_x86_xsave(env, fxstate, fpend_addr - xstate_addr, env->xcr0); __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1); __put_user(extended_size, &sw->extended_size); @@ -611,6 +611,8 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, uint32_t magic1, magic2; uint32_t extended_size, xstate_size, min_size, max_size; uint64_t xfeatures; + void *xstate; + bool ok; switch (fpkind) { case FPSTATE_XSAVE: @@ -641,8 +643,10 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, return false; } - if (!access_ok(env_cpu(env), VERIFY_READ, fxstate_addr, - xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE)) { + /* Re-lock the entire xstate area, with the extensions and magic. */ + xstate = lock_user(VERIFY_READ, fxstate_addr, + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE, 1); + if (!xstate) { return false; } @@ -652,15 +656,15 @@ static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, * fpstate layout with out copying the extended state information * in the memory layout. */ - if (get_user_u32(magic2, fxstate_addr + xstate_size)) { - return false; - } + magic2 = tswap32(*(uint32_t *)(xstate + xstate_size)); if (magic2 != TARGET_FP_XSTATE_MAGIC2) { + unlock_user(xstate, fxstate_addr, 0); break; } - cpu_x86_xrstor(env, fxstate_addr, xfeatures); - return true; + ok = cpu_x86_xrstor(env, xstate, xstate_size, xfeatures); + unlock_user(xstate, fxstate_addr, 0); + return ok; default: break; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 257cd5a617..c64ef0c1a2 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2275,8 +2275,8 @@ void cpu_x86_fsave(CPUX86State *s, void *host, size_t len); void cpu_x86_frstor(CPUX86State *s, void *host, size_t len); void cpu_x86_fxsave(CPUX86State *s, void *host, size_t len); void cpu_x86_fxrstor(CPUX86State *s, void *host, size_t len); -void cpu_x86_xsave(CPUX86State *s, target_ulong ptr, uint64_t rbfm); -void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr, uint64_t rbfm); +void cpu_x86_xsave(CPUX86State *s, void *host, size_t len, uint64_t rbfm); +bool cpu_x86_xrstor(CPUX86State *s, void *host, size_t len, uint64_t rbfm); /* cpu.c */ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 0e5368951f..c17eaaa22b 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -3065,42 +3065,44 @@ void cpu_x86_fxrstor(CPUX86State *env, void *host, size_t len) do_fxrstor(&ac, 0); } -void cpu_x86_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +void cpu_x86_xsave(CPUX86State *env, void *host, size_t len, uint64_t rfbm) { - X86Access ac; - unsigned size; + X86Access ac = { + .haddr1 = host, + .env = env, + }; /* * Since this is only called from user-level signal handling, * we should have done the job correctly there. */ assert((rfbm & ~env->xcr0) == 0); - size = xsave_area_size(rfbm, false); - - access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, 0); - do_xsave_access(&ac, ptr, rfbm, get_xinuse(env), rfbm); + ac.size = xsave_area_size(rfbm, false); + assert(ac.size <= len); + do_xsave_access(&ac, 0, rfbm, get_xinuse(env), rfbm); } -void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +bool cpu_x86_xrstor(CPUX86State *env, void *host, size_t len, uint64_t rfbm) { - X86Access ac; + X86Access ac = { + .haddr1 = host, + .env = env, + }; uint64_t xstate_bv; - unsigned size; /* * Since this is only called from user-level signal handling, * we should have done the job correctly there. */ assert((rfbm & ~env->xcr0) == 0); - size = xsave_area_size(rfbm, false); - access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, 0); + ac.size = xsave_area_size(rfbm, false); + assert(ac.size <= len); - if (!valid_xrstor_header(&ac, &xstate_bv, ptr)) { - /* TODO: Report failure to caller. */ - xstate_bv &= env->xcr0; + if (!valid_xrstor_header(&ac, &xstate_bv, 0)) { + return false; } - - do_xrstor(&ac, ptr, rfbm, xstate_bv); + do_xrstor(&ac, 0, rfbm, xstate_bv); + return true; } #endif From 540d91b40c390faa565e613a85bc0950ca7e0775 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 May 2024 16:16:58 +0200 Subject: [PATCH 0947/2884] block: Improve error message when external snapshot can't flush MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit external_snapshot_action() reports bdrv_flush() failure to its caller as An IO error has occurred The errno code returned by bdrv_flush() is lost. Improve this to Write to node '' failed: Signed-off-by: Markus Armbruster Message-ID: <20240513141703.549874-2-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- blockdev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index 08eccc9052..528db3452f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1406,8 +1406,10 @@ static void external_snapshot_action(TransactionAction *action, } if (!bdrv_is_read_only(state->old_bs)) { - if (bdrv_flush(state->old_bs)) { - error_setg(errp, QERR_IO_ERROR); + ret = bdrv_flush(state->old_bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Write to node '%s' failed", + bdrv_get_device_or_node_name(state->old_bs)); return; } } From 21c06f57808c7236cca68ccc7d8df845c22cd998 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 May 2024 16:16:59 +0200 Subject: [PATCH 0948/2884] dump/win_dump: Improve error messages on write error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit create_win_dump() and write_run report qemu_write_full() failure to their callers as An IO error has occurred The errno set by qemu_write_full() is lost. Improve this to win-dump: failed to write header: and win-dump: failed to save memory: This matches how dump.c reports similar errors. Signed-off-by: Markus Armbruster Message-ID: <20240513141703.549874-3-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- dump/win_dump.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dump/win_dump.c b/dump/win_dump.c index b7bfaff379..0e4fe692ce 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -12,7 +12,6 @@ #include "sysemu/dump.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qapi/qmp/qerror.h" #include "exec/cpu-defs.h" #include "hw/core/cpu.h" #include "qemu/win_dump_defs.h" @@ -52,6 +51,7 @@ static size_t write_run(uint64_t base_page, uint64_t page_count, uint64_t addr = base_page << TARGET_PAGE_BITS; uint64_t size = page_count << TARGET_PAGE_BITS; uint64_t len, l; + int eno; size_t total = 0; while (size) { @@ -65,9 +65,10 @@ static size_t write_run(uint64_t base_page, uint64_t page_count, } l = qemu_write_full(fd, buf, len); + eno = errno; cpu_physical_memory_unmap(buf, addr, false, len); if (l != len) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, eno, "win-dump: failed to save memory"); return 0; } @@ -459,7 +460,7 @@ void create_win_dump(DumpState *s, Error **errp) s->written_size = qemu_write_full(s->fd, h, hdr_size); if (s->written_size != hdr_size) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, errno, "win-dump: failed to write header"); goto out_restore; } From 29ad187c1c89fcf82d4e73e90bec9722d548efed Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 May 2024 16:17:00 +0200 Subject: [PATCH 0949/2884] block/vmdk: Improve error messages on extent write error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vmdk_init_extent() reports blk_co_pwrite() failure to its caller as An IO error has occurred The errno code returned by blk_co_pwrite() is lost. Improve this to failed to write VMDK : Signed-off-by: Markus Armbruster Message-ID: <20240513141703.549874-4-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- block/vmdk.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 3b82979fdf..78f6433607 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -28,7 +28,6 @@ #include "block/block_int.h" #include "sysemu/block-backend.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" @@ -2278,12 +2277,12 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, /* write all the data */ ret = blk_co_pwrite(blk, 0, sizeof(magic), &magic, 0); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, -ret, "failed to write VMDK magic"); goto exit; } ret = blk_co_pwrite(blk, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, -ret, "failed to write VMDK header"); goto exit; } @@ -2303,7 +2302,7 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, ret = blk_co_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, gd_buf_size, gd_buf, 0); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, -ret, "failed to write VMDK grain directory"); goto exit; } @@ -2315,7 +2314,8 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, ret = blk_co_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, gd_buf_size, gd_buf, 0); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, -ret, + "failed to write VMDK backup grain directory"); } ret = 0; From c59fb13be7fd937c087ed103abad4c2d9d2bcfab Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 May 2024 16:17:01 +0200 Subject: [PATCH 0950/2884] cpus: Improve error messages on memsave, pmemsave write error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmp_memsave() and qmp_pmemsave() report fwrite() error as An IO error has occurred Improve this to writing memory to '' failed Signed-off-by: Markus Armbruster Message-ID: <20240513141703.549874-5-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- system/cpus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/system/cpus.c b/system/cpus.c index 68d161d96b..f8fa78f33d 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -813,7 +813,8 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, goto exit; } if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "writing memory to '%s' failed", + filename); goto exit; } addr += l; @@ -843,7 +844,8 @@ void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, l = size; cpu_physical_memory_read(addr, buf, l); if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "writing memory to '%s' failed", + filename); goto exit; } addr += l; From fdac62dbd3de7e65c5a79798097894a29125c60e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 May 2024 16:17:02 +0200 Subject: [PATCH 0951/2884] migration: Rephrase message on failure to save / load Xen device state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions that use an Error **errp parameter to return errors should not also report them to the user, because reporting is the caller's job. When the caller does, the error is reported twice. When it doesn't (because it recovered from the error), there is no error to report, i.e. the report is bogus. qmp_xen_save_devices_state() and qmp_xen_load_devices_state() violate this principle: they call qemu_save_device_state() and qemu_loadvm_state(), which call error_report_err(). I wish I could clean this up now, but migration's error reporting is too complicated (confused?) for me to mess with it. Instead, I'm merely improving the error reported by qmp_xen_load_devices_state() and qmp_xen_load_devices_state() to the QMP core from An IO error has occurred to saving Xen device state failed and loading Xen device state failed respectively. Signed-off-by: Markus Armbruster Message-ID: <20240513141703.549874-6-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Acked-by: Fabiano Rosas Acked-by: Peter Xu --- migration/savevm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 6c789bd54b..c621f2359b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -45,7 +45,6 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-builtin-visit.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "sysemu/cpus.h" #include "exec/memory.h" @@ -3203,7 +3202,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, object_unref(OBJECT(ioc)); ret = qemu_save_device_state(f); if (ret < 0 || qemu_fclose(f) < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "saving Xen device state failed"); } else { /* libxl calls the QMP command "stop" before calling * "xen-save-devices-state" and in case of migration failure, libxl @@ -3252,7 +3251,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "loading Xen device state failed"); } migration_incoming_state_destroy(); } From 5b957bf6d378cfa0c196b2be119449e086647612 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 13 May 2024 16:17:03 +0200 Subject: [PATCH 0952/2884] qerror: QERR_IO_ERROR is no longer used, drop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Message-ID: <20240513141703.549874-7-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- include/qapi/qmp/qerror.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 00b18e9082..bc9116f76a 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -20,9 +20,6 @@ #define QERR_INVALID_PARAMETER_VALUE \ "Parameter '%s' expects %s" -#define QERR_IO_ERROR \ - "An IO error has occurred" - #define QERR_MISSING_PARAMETER \ "Parameter '%s' is missing" From ecfc9890c41541fb08ce07a6ff26e7bd14f74967 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 14 May 2024 12:58:26 +0200 Subject: [PATCH 0953/2884] qga-win32: Improve guest-set-user-password, guest-file-open errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When guest-set-user-password's argument @password can't be converted from UTF-8 to UTF-16, we report something like Guest agent command failed, error was 'Invalid sequence in conversion input' Improve this to can't convert 'password' to UTF-16: Invalid sequence in conversion input Likewise for argument @username, and guest-file-open argument @path, even though I'm not sure you can actually get invalid input past the QMP core there. Signed-off-by: Markus Armbruster Message-ID: <20240514105829.729342-2-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Konstantin Kostiuk --- qga/commands-win32.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 6fee0e1e6f..ed31077457 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -217,6 +217,9 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp) w_path = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr); if (!w_path) { + error_setg(errp, "can't convert 'path' to UTF-16: %s", + gerr->message); + g_error_free(gerr); goto done; } @@ -244,10 +247,6 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp) slog("guest-file-open, handle: % " PRId64, fd); done: - if (gerr) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); - g_error_free(gerr); - } g_free(w_path); return fd; } @@ -1946,11 +1945,17 @@ void qmp_guest_set_user_password(const char *username, user = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr); if (!user) { + error_setg(errp, "can't convert 'username' to UTF-16: %s", + gerr->message); + g_error_free(gerr); goto done; } wpass = g_utf8_to_utf16(rawpasswddata, -1, NULL, NULL, &gerr); if (!wpass) { + error_setg(errp, "can't convert 'password' to UTF-16: %s", + gerr->message); + g_error_free(gerr); goto done; } @@ -1966,10 +1971,6 @@ void qmp_guest_set_user_password(const char *username, } done: - if (gerr) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); - g_error_free(gerr); - } g_free(user); g_free(wpass); g_free(rawpasswddata); From cec07c79a41827689f9b9ea7be47fd1ae17a1242 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 14 May 2024 12:58:27 +0200 Subject: [PATCH 0954/2884] qga: Shorten several error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some, but not all error messages are of the form Guest agent command failed, error was '' For instance, command guest-exec can fail with an error message like Guest agent command failed, error was 'Failed to execute child process “/bin/invalid-cmd42” (No such file or directory)' Shorten this to just just the actual error message. The guest-exec example becomes Failed to execute child process “/bin/invalid-cmd42” (No such file or directory) Signed-off-by: Markus Armbruster Message-ID: <20240514105829.729342-3-armbru@redhat.com> [Superfluous #include "qapi/qmp/qerror.h" deleted] Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Konstantin Kostiuk --- qga/commands-win32.c | 24 ++++++++---------------- qga/commands.c | 6 ++---- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index ed31077457..0d1b836e87 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -278,8 +278,7 @@ static void acquire_privilege(const char *name, Error **errp) TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "no luid for requested privilege"); + error_setg(errp, "no luid for requested privilege"); goto out; } @@ -287,14 +286,12 @@ static void acquire_privilege(const char *name, Error **errp) priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "unable to acquire requested privilege"); + error_setg(errp, "unable to acquire requested privilege"); goto out; } } else { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "failed to open privilege token"); + error_setg(errp, "failed to open privilege token"); } out: @@ -308,8 +305,7 @@ static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, { HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL); if (!thread) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "failed to dispatch asynchronous command"); + error_setg(errp, "failed to dispatch asynchronous command"); } } @@ -1418,22 +1414,19 @@ static void check_suspend_mode(GuestSuspendMode mode, Error **errp) ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); if (!GetPwrCapabilities(&sys_pwr_caps)) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "failed to determine guest suspend capabilities"); + error_setg(errp, "failed to determine guest suspend capabilities"); return; } switch (mode) { case GUEST_SUSPEND_MODE_DISK: if (!sys_pwr_caps.SystemS4) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "suspend-to-disk not supported by OS"); + error_setg(errp, "suspend-to-disk not supported by OS"); } break; case GUEST_SUSPEND_MODE_RAM: if (!sys_pwr_caps.SystemS3) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "suspend-to-ram not supported by OS"); + error_setg(errp, "suspend-to-ram not supported by OS"); } break; default: @@ -2175,8 +2168,7 @@ static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) HMODULE module = GetModuleHandle("ntdll"); PVOID fun = GetProcAddress(module, "RtlGetVersion"); if (fun == NULL) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "Failed to get address of RtlGetVersion"); + error_setg(errp, "Failed to get address of RtlGetVersion"); return; } diff --git a/qga/commands.c b/qga/commands.c index 88c1c99fe5..5a5fad31f8 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -15,7 +15,6 @@ #include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/base64.h" #include "qemu/cutils.h" #include "commands-common.h" @@ -475,7 +474,7 @@ GuestExec *qmp_guest_exec(const char *path, guest_exec_task_setup, &has_merge, &pid, input_data ? &in_fd : NULL, has_output ? &out_fd : NULL, has_output ? &err_fd : NULL, &gerr); if (!ret) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); + error_setg(errp, "%s", gerr->message); g_error_free(gerr); goto done; } @@ -586,8 +585,7 @@ GuestTimezone *qmp_guest_get_timezone(Error **errp) info = g_new0(GuestTimezone, 1); tz = g_time_zone_new_local(); if (tz == NULL) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "Couldn't retrieve local timezone"); + error_setg(errp, "Couldn't retrieve local timezone"); goto error; } From 2020337239b054447d0756ea75e970b35f7a541b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 14 May 2024 12:58:28 +0200 Subject: [PATCH 0955/2884] qerror: QERR_QGA_COMMAND_FAILED is no longer used, drop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Message-ID: <20240514105829.729342-4-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Konstantin Kostiuk --- include/qapi/qmp/qerror.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index bc9116f76a..38e89762b3 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -26,9 +26,6 @@ #define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" -#define QERR_QGA_COMMAND_FAILED \ - "Guest agent command failed, error was '%s'" - #define QERR_UNSUPPORTED \ "this feature or command is not currently supported" From 7d99ae59a20c4448732a3fb204e31915816048d6 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Thu, 4 Apr 2024 11:11:36 +0200 Subject: [PATCH 0956/2884] blockcommit: Reopen base image as RO after abort If a blockcommit is aborted the base image remains in RW mode, that leads to a fail of subsequent live migration. How to reproduce: $ virsh snapshot-create-as vm snp1 --disk-only *** write something to the disk inside the guest *** $ virsh blockcommit vm vda --active --shallow && virsh blockjob vm vda --abort $ lsof /vzt/vm.qcow2 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME qemu-syst 433203 root 45u REG 253,0 1724776448 133 /vzt/vm.qcow2 $ cat /proc/433203/fdinfo/45 pos: 0 flags: 02140002 <==== The last 2 means RW mode If the base image is in RW mode at the end of blockcommit and was in RO mode before blockcommit, reopen the base BDS in RO. Signed-off-by: Alexander Ivanov Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20240404091136.129811-1-alexander.ivanov@virtuozzo.com> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/mirror.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 1bdce3b657..61f0a717b7 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -93,6 +93,7 @@ typedef struct MirrorBlockJob { int64_t active_write_bytes_in_flight; bool prepared; bool in_drain; + bool base_ro; } MirrorBlockJob; typedef struct MirrorBDSOpaque { @@ -794,6 +795,10 @@ static int mirror_exit_common(Job *job) bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); bdrv_graph_wrunlock(); + if (abort && s->base_ro && !bdrv_is_read_only(target_bs)) { + bdrv_reopen_set_read_only(target_bs, true, NULL); + } + bdrv_drained_end(target_bs); bdrv_unref(target_bs); @@ -1717,6 +1722,7 @@ static BlockJob *mirror_start_job( bool is_none_mode, BlockDriverState *base, bool auto_complete, const char *filter_node_name, bool is_mirror, MirrorCopyMode copy_mode, + bool base_ro, Error **errp) { MirrorBlockJob *s; @@ -1800,6 +1806,7 @@ static BlockJob *mirror_start_job( bdrv_unref(mirror_top_bs); s->mirror_top_bs = mirror_top_bs; + s->base_ro = base_ro; /* No resize for the target either; while the mirror is still running, a * consistent read isn't necessarily possible. We could possibly allow @@ -2029,7 +2036,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, speed, granularity, buf_size, backing_mode, zero_target, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, - filter_node_name, true, copy_mode, errp); + filter_node_name, true, copy_mode, false, errp); } BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, @@ -2058,7 +2065,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, - errp); + base_read_only, errp); if (!job) { goto error_restore_flags; } From 137b4d4bd506bd42c18718f3f8b43a2d7c3346a8 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 13 Mar 2024 18:28:18 +0300 Subject: [PATCH 0957/2884] block/copy-before-write: fix permission In case when source node does not have any parents, the condition still works as required: backup job do create the parent by block_job_create -> block_job_add_bdrv -> bdrv_root_attach_child Still, in this case checking @perm variable doesn't work, as backup job creates the root blk with empty permissions (as it rely on CBW filter to require correct permissions and don't want to create extra conflicts). So, we should not check @perm. The hack may be dropped entirely when transactional insertion of filter (when we don't try to recalculate permissions in intermediate state, when filter does conflict with original parent of the source node) merged (old big series "[PATCH v5 00/45] Transactional block-graph modifying API"[1] and it's current in-flight part is "[PATCH v8 0/7] blockdev-replace"[2]) [1] https://patchew.org/QEMU/20220330212902.590099-1-vsementsov@openvz.org/ [2] https://patchew.org/QEMU/20231017184444.932733-1-vsementsov@yandex-team.ru/ Signed-off-by: Vladimir Sementsov-Ogievskiy Tested-by: Fiona Ebner Message-Id: <20240313152822.626493-2-vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/copy-before-write.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 8aba27a71d..3e3af30c08 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -364,9 +364,13 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, perm, shared, nperm, nshared); if (!QLIST_EMPTY(&bs->parents)) { - if (perm & BLK_PERM_WRITE) { - *nperm = *nperm | BLK_PERM_CONSISTENT_READ; - } + /* + * Note, that source child may be shared with backup job. Backup job + * does create own blk parent on copy-before-write node, so this + * works even if source node does not have any parents before backup + * start + */ + *nperm = *nperm | BLK_PERM_CONSISTENT_READ; *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); } } From 507175197b06a24beb2369abe95b00172741c457 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 13 Mar 2024 18:28:19 +0300 Subject: [PATCH 0958/2884] block/copy-before-write: support unligned snapshot-discard First thing that crashes on unligned access here is bdrv_reset_dirty_bitmap(). Correct way is to align-down the snapshot-discard request. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Message-Id: <20240313152822.626493-3-vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/copy-before-write.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 3e3af30c08..6d89af0b29 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -325,14 +325,24 @@ static int coroutine_fn GRAPH_RDLOCK cbw_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) { BDRVCopyBeforeWriteState *s = bs->opaque; + uint32_t cluster_size = block_copy_cluster_size(s->bcs); + int64_t aligned_offset = QEMU_ALIGN_UP(offset, cluster_size); + int64_t aligned_end = QEMU_ALIGN_DOWN(offset + bytes, cluster_size); + int64_t aligned_bytes; + + if (aligned_end <= aligned_offset) { + return 0; + } + aligned_bytes = aligned_end - aligned_offset; WITH_QEMU_LOCK_GUARD(&s->lock) { - bdrv_reset_dirty_bitmap(s->access_bitmap, offset, bytes); + bdrv_reset_dirty_bitmap(s->access_bitmap, aligned_offset, + aligned_bytes); } - block_copy_reset(s->bcs, offset, bytes); + block_copy_reset(s->bcs, aligned_offset, aligned_bytes); - return bdrv_co_pdiscard(s->target, offset, bytes); + return bdrv_co_pdiscard(s->target, aligned_offset, aligned_bytes); } static void GRAPH_RDLOCK cbw_refresh_filename(BlockDriverState *bs) From 006e845b5a4cbb1f93a2e8ed22fa648b9d7e4182 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 13 Mar 2024 18:28:20 +0300 Subject: [PATCH 0959/2884] block/copy-before-write: create block_copy bitmap in filter node Currently block_copy creates copy_bitmap in source node. But that is in bad relation with .independent_close=true of copy-before-write filter: source node may be detached and removed before .bdrv_close() handler called, which should call block_copy_state_free(), which in turn should remove copy_bitmap. That's all not ideal: it would be better if internal bitmap of block-copy object is not attached to any node. But that is not possible now. The simplest solution is just create copy_bitmap in filter node, where anyway two other bitmaps are created. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Message-Id: <20240313152822.626493-4-vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/block-copy.c | 3 +- block/copy-before-write.c | 2 +- include/block/block-copy.h | 1 + tests/qemu-iotests/257.out | 112 ++++++++++++++++++------------------- 4 files changed, 60 insertions(+), 58 deletions(-) diff --git a/block/block-copy.c b/block/block-copy.c index 9ee3dd7ef5..8fca2c3698 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -351,6 +351,7 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, } BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + BlockDriverState *copy_bitmap_bs, const BdrvDirtyBitmap *bitmap, Error **errp) { @@ -367,7 +368,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, return NULL; } - copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL, + copy_bitmap = bdrv_create_dirty_bitmap(copy_bitmap_bs, cluster_size, NULL, errp); if (!copy_bitmap) { return NULL; diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 6d89af0b29..ed2c228da7 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -468,7 +468,7 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); - s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp); + s->bcs = block_copy_state_new(bs->file, s->target, bs, bitmap, errp); if (!s->bcs) { error_prepend(errp, "Cannot create block-copy-state: "); return -EINVAL; diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 0700953ab8..8b41643bfa 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -25,6 +25,7 @@ typedef struct BlockCopyState BlockCopyState; typedef struct BlockCopyCallState BlockCopyCallState; BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + BlockDriverState *copy_bitmap_bs, const BdrvDirtyBitmap *bitmap, Error **errp); diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out index aa76131ca9..c33dd7f3a9 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -120,16 +120,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -596,16 +596,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -865,16 +865,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -1341,16 +1341,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -1610,16 +1610,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -2086,16 +2086,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -2355,16 +2355,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -2831,16 +2831,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -3100,16 +3100,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -3576,16 +3576,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -3845,16 +3845,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -4321,16 +4321,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -4590,16 +4590,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -5066,16 +5066,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, From 0fd05c8d807dc7cd25b2f9bf32dd4135c94acb7a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 13 Mar 2024 18:28:21 +0300 Subject: [PATCH 0960/2884] qapi: blockdev-backup: add discard-source parameter Add a parameter that enables discard-after-copy. That is mostly useful in "push backup with fleecing" scheme, when source is snapshot-access format driver node, based on copy-before-write filter snapshot-access API: [guest] [snapshot-access] ~~ blockdev-backup ~~> [backup target] | | | root | file v v [copy-before-write] | | | file | target v v [active disk] [temp.img] In this case discard-after-copy does two things: - discard data in temp.img to save disk space - avoid further copy-before-write operation in discarded area Note that we have to declare WRITE permission on source in copy-before-write filter, for discard to work. Still we can't take it unconditionally, as it will break normal backup from RO source. So, we have to add a parameter and pass it thorough bdrv_open flags. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Acked-by: Markus Armbruster Message-Id: <20240313152822.626493-5-vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/backup.c | 5 +++-- block/block-copy.c | 9 +++++++++ block/copy-before-write.c | 15 +++++++++++++-- block/copy-before-write.h | 1 + block/replication.c | 4 ++-- blockdev.c | 2 +- include/block/block-common.h | 2 ++ include/block/block-copy.h | 1 + include/block/block_int-global-state.h | 2 +- qapi/block-core.json | 4 ++++ 10 files changed, 37 insertions(+), 8 deletions(-) diff --git a/block/backup.c b/block/backup.c index ec29d6b810..3dd2e229d2 100644 --- a/block/backup.c +++ b/block/backup.c @@ -356,7 +356,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockDriverState *target, int64_t speed, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, BitmapSyncMode bitmap_mode, - bool compress, + bool compress, bool discard_source, const char *filter_node_name, BackupPerf *perf, BlockdevOnError on_source_error, @@ -457,7 +457,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } - cbw = bdrv_cbw_append(bs, target, filter_node_name, &bcs, errp); + cbw = bdrv_cbw_append(bs, target, filter_node_name, discard_source, + &bcs, errp); if (!cbw) { goto error; } diff --git a/block/block-copy.c b/block/block-copy.c index 8fca2c3698..7e3b378528 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -137,6 +137,7 @@ typedef struct BlockCopyState { CoMutex lock; int64_t in_flight_bytes; BlockCopyMethod method; + bool discard_source; BlockReqList reqs; QLIST_HEAD(, BlockCopyCallState) calls; /* @@ -353,6 +354,7 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, BlockDriverState *copy_bitmap_bs, const BdrvDirtyBitmap *bitmap, + bool discard_source, Error **errp) { ERRP_GUARD(); @@ -418,6 +420,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, cluster_size), }; + s->discard_source = discard_source; block_copy_set_copy_opts(s, false, false); ratelimit_init(&s->rate_limit); @@ -589,6 +592,12 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) co_put_to_shres(s->mem, t->req.bytes); block_copy_task_end(t, ret); + if (s->discard_source && ret == 0) { + int64_t nbytes = + MIN(t->req.offset + t->req.bytes, s->len) - t->req.offset; + bdrv_co_pdiscard(s->source, t->req.offset, nbytes); + } + return ret; } diff --git a/block/copy-before-write.c b/block/copy-before-write.c index ed2c228da7..cd65524e26 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -44,6 +44,7 @@ typedef struct BDRVCopyBeforeWriteState { BdrvChild *target; OnCbwError on_cbw_error; uint32_t cbw_timeout_ns; + bool discard_source; /* * @lock: protects access to @access_bitmap, @done_bitmap and @@ -357,6 +358,8 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { + BDRVCopyBeforeWriteState *s = bs->opaque; + if (!(role & BDRV_CHILD_FILTERED)) { /* * Target child @@ -381,6 +384,10 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, * start */ *nperm = *nperm | BLK_PERM_CONSISTENT_READ; + if (s->discard_source) { + *nperm = *nperm | BLK_PERM_WRITE; + } + *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); } } @@ -468,7 +475,9 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); - s->bcs = block_copy_state_new(bs->file, s->target, bs, bitmap, errp); + s->discard_source = flags & BDRV_O_CBW_DISCARD_SOURCE; + s->bcs = block_copy_state_new(bs->file, s->target, bs, bitmap, + flags & BDRV_O_CBW_DISCARD_SOURCE, errp); if (!s->bcs) { error_prepend(errp, "Cannot create block-copy-state: "); return -EINVAL; @@ -535,12 +544,14 @@ static BlockDriver bdrv_cbw_filter = { BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, + bool discard_source, BlockCopyState **bcs, Error **errp) { BDRVCopyBeforeWriteState *state; BlockDriverState *top; QDict *opts; + int flags = BDRV_O_RDWR | (discard_source ? BDRV_O_CBW_DISCARD_SOURCE : 0); assert(source->total_sectors == target->total_sectors); GLOBAL_STATE_CODE(); @@ -553,7 +564,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, qdict_put_str(opts, "file", bdrv_get_node_name(source)); qdict_put_str(opts, "target", bdrv_get_node_name(target)); - top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp); + top = bdrv_insert_node(source, opts, flags, errp); if (!top) { return NULL; } diff --git a/block/copy-before-write.h b/block/copy-before-write.h index 6e72bb25e9..01af0cd3c4 100644 --- a/block/copy-before-write.h +++ b/block/copy-before-write.h @@ -39,6 +39,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, + bool discard_source, BlockCopyState **bcs, Error **errp); void bdrv_cbw_drop(BlockDriverState *bs); diff --git a/block/replication.c b/block/replication.c index ca6bd0a720..0415a5e8b7 100644 --- a/block/replication.c +++ b/block/replication.c @@ -582,8 +582,8 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, s->backup_job = backup_job_create( NULL, s->secondary_disk->bs, s->hidden_disk->bs, - 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL, - &perf, + 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, false, + NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); diff --git a/blockdev.c b/blockdev.c index 528db3452f..835064ed03 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2728,7 +2728,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, bmap, backup->bitmap_mode, - backup->compress, + backup->compress, backup->discard_source, backup->filter_node_name, &perf, backup->on_source_error, diff --git a/include/block/block-common.h b/include/block/block-common.h index a846023a09..338fe5ff7a 100644 --- a/include/block/block-common.h +++ b/include/block/block-common.h @@ -243,6 +243,8 @@ typedef enum { read-write fails */ #define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ +#define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */ + #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 8b41643bfa..bdc703bacd 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -27,6 +27,7 @@ typedef struct BlockCopyCallState BlockCopyCallState; BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, BlockDriverState *copy_bitmap_bs, const BdrvDirtyBitmap *bitmap, + bool discard_source, Error **errp); /* Function should be called prior any actual copy request */ diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index d2201e27f4..eb2d92a226 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -193,7 +193,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, BitmapSyncMode bitmap_mode, - bool compress, + bool compress, bool discard_source, const char *filter_node_name, BackupPerf *perf, BlockdevOnError on_source_error, diff --git a/qapi/block-core.json b/qapi/block-core.json index 746d1694c2..df5e07debd 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1610,6 +1610,9 @@ # node specified by @drive. If this option is not given, a node # name is autogenerated. (Since: 4.2) # +# @discard-source: Discard blocks on source which have already been +# copied to the target. (Since 9.1) +# # @x-perf: Performance options. (Since 6.0) # # Features: @@ -1631,6 +1634,7 @@ '*on-target-error': 'BlockdevOnError', '*auto-finalize': 'bool', '*auto-dismiss': 'bool', '*filter-node-name': 'str', + '*discard-source': 'bool', '*x-perf': { 'type': 'BackupPerf', 'features': [ 'unstable' ] } } } From 3a2242bf99a246e8199f8aa29bd8832c80b847bd Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 13 Mar 2024 18:28:22 +0300 Subject: [PATCH 0961/2884] iotests: add backup-discard-source Add test for a new backup option: discard-source. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Message-Id: <20240313152822.626493-6-vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy --- .../qemu-iotests/tests/backup-discard-source | 152 ++++++++++++++++++ .../tests/backup-discard-source.out | 5 + 2 files changed, 157 insertions(+) create mode 100755 tests/qemu-iotests/tests/backup-discard-source create mode 100644 tests/qemu-iotests/tests/backup-discard-source.out diff --git a/tests/qemu-iotests/tests/backup-discard-source b/tests/qemu-iotests/tests/backup-discard-source new file mode 100755 index 0000000000..2391b12acd --- /dev/null +++ b/tests/qemu-iotests/tests/backup-discard-source @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# +# Test backup discard-source parameter +# +# Copyright (c) Virtuozzo International GmbH. +# Copyright (c) Yandex +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os + +import iotests +from iotests import qemu_img_create, qemu_img_map, qemu_io + + +temp_img = os.path.join(iotests.test_dir, 'temp') +source_img = os.path.join(iotests.test_dir, 'source') +target_img = os.path.join(iotests.test_dir, 'target') +size = '1M' + + +def get_actual_size(vm, node_name): + nodes = vm.cmd('query-named-block-nodes', flat=True) + node = next(n for n in nodes if n['node-name'] == node_name) + return node['image']['actual-size'] + + +class TestBackup(iotests.QMPTestCase): + def setUp(self): + qemu_img_create('-f', iotests.imgfmt, source_img, size) + qemu_img_create('-f', iotests.imgfmt, temp_img, size) + qemu_img_create('-f', iotests.imgfmt, target_img, size) + qemu_io('-c', 'write 0 1M', source_img) + + self.vm = iotests.VM() + self.vm.launch() + + self.vm.cmd('blockdev-add', { + 'node-name': 'cbw', + 'driver': 'copy-before-write', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img, + } + }, + 'target': { + 'driver': iotests.imgfmt, + 'discard': 'unmap', + 'node-name': 'temp', + 'file': { + 'driver': 'file', + 'filename': temp_img + } + } + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'access', + 'discard': 'unmap', + 'driver': 'snapshot-access', + 'file': 'cbw' + }) + + self.vm.cmd('blockdev-add', { + 'driver': iotests.imgfmt, + 'node-name': 'target', + 'file': { + 'driver': 'file', + 'filename': target_img + } + }) + + self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024) + + def tearDown(self): + # That should fail, because region is discarded + self.vm.hmp_qemu_io('access', 'read 0 1M') + + self.vm.shutdown() + + self.assertTrue('read failed: Permission denied' in self.vm.get_log()) + + # Final check that temp image is empty + mapping = qemu_img_map(temp_img) + self.assertEqual(len(mapping), 1) + self.assertEqual(mapping[0]['start'], 0) + self.assertEqual(mapping[0]['length'], 1024 * 1024) + self.assertEqual(mapping[0]['data'], False) + + os.remove(temp_img) + os.remove(source_img) + os.remove(target_img) + + def do_backup(self): + self.vm.cmd('blockdev-backup', device='access', + sync='full', target='target', + job_id='backup0', + discard_source=True) + + self.vm.event_wait(name='BLOCK_JOB_COMPLETED') + + def test_discard_written(self): + """ + 1. Guest writes + 2. copy-before-write operation, data is stored to temp + 3. start backup(discard_source=True), check that data is + removed from temp + """ + # Trigger copy-before-write operation + result = self.vm.hmp_qemu_io('cbw', 'write 0 1M') + self.assert_qmp(result, 'return', '') + + # Check that data is written to temporary image + self.assertGreater(get_actual_size(self.vm, 'temp'), 1024 * 1024) + + self.do_backup() + + def test_discard_cbw(self): + """ + 1. do backup(discard_source=True), which should inform + copy-before-write that data is not needed anymore + 2. Guest writes + 3. Check that copy-before-write operation is not done + """ + self.do_backup() + + # Try trigger copy-before-write operation + result = self.vm.hmp_qemu_io('cbw', 'write 0 1M') + self.assert_qmp(result, 'return', '') + + # Check that data is not written to temporary image, as region + # is discarded from copy-before-write process + self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024) + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/backup-discard-source.out b/tests/qemu-iotests/tests/backup-discard-source.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/tests/backup-discard-source.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK From a149401048481247bcbaf6035a7a1308974fb464 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 30 Apr 2024 12:08:06 +0300 Subject: [PATCH 0962/2884] iotests/pylintrc: allow up to 10 similar lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to have similar QMP objects in different tests. Reworking these objects to make common parts by calling some helper functions doesn't seem good. It's a lot more comfortable to see the whole QAPI request in one place. So, let's increase the limit, to unblock further commit "iotests: add backup-discard-source" Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index de2e0c2781..05b75ee59b 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -55,4 +55,4 @@ max-line-length=79 [SIMILARITIES] -min-similarity-lines=6 +min-similarity-lines=10 From fdf029762f50101a3d7927d8db1be015e00f441c Mon Sep 17 00:00:00 2001 From: Alexandra Diupina Date: Thu, 23 May 2024 16:06:19 +0100 Subject: [PATCH 0963/2884] xlnx_dpdma: fix descriptor endianness bug Add xlnx_dpdma_read_descriptor() and xlnx_dpdma_write_descriptor() functions. xlnx_dpdma_read_descriptor() combines reading a descriptor from desc_addr by calling dma_memory_read() and swapping the desc fields from guest memory order to host memory order. xlnx_dpdma_write_descriptor() performs similar actions when writing a descriptor. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: d3c6369a96 ("introduce xlnx-dpdma") Signed-off-by: Alexandra Diupina [PMM: tweaked indent, dropped behaviour change for write-failure case] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/dma/xlnx_dpdma.c | 68 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index 530717d188..dde4aeca40 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -614,6 +614,65 @@ static void xlnx_dpdma_register_types(void) type_register_static(&xlnx_dpdma_info); } +static MemTxResult xlnx_dpdma_read_descriptor(XlnxDPDMAState *s, + uint64_t desc_addr, + DPDMADescriptor *desc) +{ + MemTxResult res = dma_memory_read(&address_space_memory, desc_addr, + &desc, sizeof(DPDMADescriptor), + MEMTXATTRS_UNSPECIFIED); + if (res) { + return res; + } + + /* Convert from LE into host endianness. */ + desc->control = le32_to_cpu(desc->control); + desc->descriptor_id = le32_to_cpu(desc->descriptor_id); + desc->xfer_size = le32_to_cpu(desc->xfer_size); + desc->line_size_stride = le32_to_cpu(desc->line_size_stride); + desc->timestamp_lsb = le32_to_cpu(desc->timestamp_lsb); + desc->timestamp_msb = le32_to_cpu(desc->timestamp_msb); + desc->address_extension = le32_to_cpu(desc->address_extension); + desc->next_descriptor = le32_to_cpu(desc->next_descriptor); + desc->source_address = le32_to_cpu(desc->source_address); + desc->address_extension_23 = le32_to_cpu(desc->address_extension_23); + desc->address_extension_45 = le32_to_cpu(desc->address_extension_45); + desc->source_address2 = le32_to_cpu(desc->source_address2); + desc->source_address3 = le32_to_cpu(desc->source_address3); + desc->source_address4 = le32_to_cpu(desc->source_address4); + desc->source_address5 = le32_to_cpu(desc->source_address5); + desc->crc = le32_to_cpu(desc->crc); + + return res; +} + +static MemTxResult xlnx_dpdma_write_descriptor(uint64_t desc_addr, + DPDMADescriptor *desc) +{ + DPDMADescriptor tmp_desc = *desc; + + /* Convert from host endianness into LE. */ + tmp_desc.control = cpu_to_le32(tmp_desc.control); + tmp_desc.descriptor_id = cpu_to_le32(tmp_desc.descriptor_id); + tmp_desc.xfer_size = cpu_to_le32(tmp_desc.xfer_size); + tmp_desc.line_size_stride = cpu_to_le32(tmp_desc.line_size_stride); + tmp_desc.timestamp_lsb = cpu_to_le32(tmp_desc.timestamp_lsb); + tmp_desc.timestamp_msb = cpu_to_le32(tmp_desc.timestamp_msb); + tmp_desc.address_extension = cpu_to_le32(tmp_desc.address_extension); + tmp_desc.next_descriptor = cpu_to_le32(tmp_desc.next_descriptor); + tmp_desc.source_address = cpu_to_le32(tmp_desc.source_address); + tmp_desc.address_extension_23 = cpu_to_le32(tmp_desc.address_extension_23); + tmp_desc.address_extension_45 = cpu_to_le32(tmp_desc.address_extension_45); + tmp_desc.source_address2 = cpu_to_le32(tmp_desc.source_address2); + tmp_desc.source_address3 = cpu_to_le32(tmp_desc.source_address3); + tmp_desc.source_address4 = cpu_to_le32(tmp_desc.source_address4); + tmp_desc.source_address5 = cpu_to_le32(tmp_desc.source_address5); + tmp_desc.crc = cpu_to_le32(tmp_desc.crc); + + return dma_memory_write(&address_space_memory, desc_addr, &tmp_desc, + sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); +} + size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, bool one_desc) { @@ -651,8 +710,7 @@ size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, desc_addr = xlnx_dpdma_descriptor_next_address(s, channel); } - if (dma_memory_read(&address_space_memory, desc_addr, &desc, - sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED)) { + if (xlnx_dpdma_read_descriptor(s, desc_addr, &desc)) { s->registers[DPDMA_EISR] |= ((1 << 1) << channel); xlnx_dpdma_update_irq(s); s->operation_finished[channel] = true; @@ -755,8 +813,10 @@ size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, /* The descriptor need to be updated when it's completed. */ DPRINTF("update the descriptor with the done flag set.\n"); xlnx_dpdma_desc_set_done(&desc); - dma_memory_write(&address_space_memory, desc_addr, &desc, - sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); + if (xlnx_dpdma_write_descriptor(desc_addr, &desc)) { + DPRINTF("Can't write the descriptor.\n"); + /* TODO: check hardware behaviour for memory write failure */ + } } if (xlnx_dpdma_desc_completion_interrupt(&desc)) { From 19ed42e8adc87a3c739f61608b66a046bb9237e2 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Thu, 23 May 2024 16:06:19 +0100 Subject: [PATCH 0964/2884] hvf: arm: Fix encodings for ID_AA64PFR1_EL1 and debug System registers We wrongly encoded ID_AA64PFR1_EL1 using {3,0,0,4,2} in hvf_sreg_match[] so we fail to get the expected ARMCPRegInfo from cp_regs hash table with the wrong key. Fix it with the correct encoding {3,0,0,4,1}. With that fixed, the Linux guest can properly detect FEAT_SSBS2 on my M1 HW. All DBG{B,W}{V,C}R_EL1 registers are also wrongly encoded with op0 == 14. It happens to work because HVF_SYSREG(CRn, CRm, 14, op1, op2) equals to HVF_SYSREG(CRn, CRm, 2, op1, op2), by definition. But we shouldn't rely on it. Cc: qemu-stable@nongnu.org Fixes: a1477da3ddeb ("hvf: Add Apple Silicon support") Signed-off-by: Zenghui Yu Reviewed-by: Alexander Graf Message-id: 20240503153453.54389-1-zenghui.yu@linux.dev Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 130 +++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 08d0757438..45e2218be5 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -396,85 +396,85 @@ struct hvf_sreg_match { }; static struct hvf_sreg_match hvf_sreg_match[] = { - { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 7) }, #ifdef SYNC_NO_RAW_REGS /* @@ -486,7 +486,7 @@ static struct hvf_sreg_match hvf_sreg_match[] = { { HV_SYS_REG_MPIDR_EL1, HVF_SYSREG(0, 0, 3, 0, 5) }, { HV_SYS_REG_ID_AA64PFR0_EL1, HVF_SYSREG(0, 4, 3, 0, 0) }, #endif - { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 2) }, + { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 1) }, { HV_SYS_REG_ID_AA64DFR0_EL1, HVF_SYSREG(0, 5, 3, 0, 0) }, { HV_SYS_REG_ID_AA64DFR1_EL1, HVF_SYSREG(0, 5, 3, 0, 1) }, { HV_SYS_REG_ID_AA64ISAR0_EL1, HVF_SYSREG(0, 6, 3, 0, 0) }, From 03935f9272e757724cd99ca3842be3ddbfeecc77 Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Thu, 23 May 2024 16:06:20 +0100 Subject: [PATCH 0965/2884] hw/arm/npcm7xx: remove setting of mp-affinity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value of the mp-affinity property being set in npcm7xx_realize is always the same as the default value it would have when arm_cpu_realizefn is called if the property is not set here. So there is no need to set the property value in npcm7xx_realize function. Signed-off-by: Dorjoy Chowdhury Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240504141733.14813-1-dorjoychy111@gmail.com Signed-off-by: Peter Maydell --- hw/arm/npcm7xx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 9f2d96c733..cb7791301b 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -487,9 +487,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) /* CPUs */ for (i = 0; i < nc->num_cpus; i++) { - object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity", - arm_build_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS), - &error_abort); object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", NPCM7XX_GIC_CPU_IF_ADDR, &error_abort); object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true, From cd2a2788a92c39aa6405e2ff7a95aca02d036757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Thu, 23 May 2024 16:06:20 +0100 Subject: [PATCH 0966/2884] hw/char: Correct STM32L4x5 usart register CR2 field ADD_0 size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Message-id: 20240505141613.387508-1-ines.varhol@telecom-paris.fr Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 02f666308c..fc5dcac0c4 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -56,7 +56,7 @@ REG32(CR1, 0x00) FIELD(CR1, UE, 0, 1) /* USART enable */ REG32(CR2, 0x04) FIELD(CR2, ADD_1, 28, 4) /* ADD[7:4] */ - FIELD(CR2, ADD_0, 24, 1) /* ADD[3:0] */ + FIELD(CR2, ADD_0, 24, 4) /* ADD[3:0] */ FIELD(CR2, RTOEN, 23, 1) /* Receiver timeout enable */ FIELD(CR2, ABRMOD, 21, 2) /* Auto baud rate mode */ FIELD(CR2, ABREN, 20, 1) /* Auto baud rate enable */ From daafa78b297291fea36fb4daeed526705fa7c035 Mon Sep 17 00:00:00 2001 From: Andrey Shumilin Date: Thu, 23 May 2024 16:06:20 +0100 Subject: [PATCH 0967/2884] hw/intc/arm_gic: Fix handling of NS view of GICC_APR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In gic_cpu_read() and gic_cpu_write(), we delegate the handling of reading and writing the Non-Secure view of the GICC_APR registers to functions gic_apr_ns_view() and gic_apr_write_ns_view(). Unfortunately we got the order of the arguments wrong, swapping the CPU number and the register number (which the compiler doesn't catch because they're both integers). Most guests probably didn't notice this bug because directly accessing the APR registers is typically something only done by firmware when it is doing state save for going into a sleep mode. Correct the mismatched call arguments. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: qemu-stable@nongnu.org Fixes: 51fd06e0ee ("hw/intc/arm_gic: Fix handling of GICC_APR, GICC_NSAPR registers") Signed-off-by: Andrey Shumilin [PMM: Rewrote commit message] Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée --- hw/intc/arm_gic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 074cf50af2..e4b8437f8b 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -1658,7 +1658,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, *data = s->h_apr[gic_get_vcpu_real_id(cpu)]; } else if (gic_cpu_ns_access(s, cpu, attrs)) { /* NS view of GICC_APR is the top half of GIC_NSAPR */ - *data = gic_apr_ns_view(s, regno, cpu); + *data = gic_apr_ns_view(s, cpu, regno); } else { *data = s->apr[regno][cpu]; } @@ -1746,7 +1746,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, s->h_apr[gic_get_vcpu_real_id(cpu)] = value; } else if (gic_cpu_ns_access(s, cpu, attrs)) { /* NS view of GICC_APR is the top half of GIC_NSAPR */ - gic_apr_write_ns_view(s, regno, cpu, value); + gic_apr_write_ns_view(s, cpu, regno, value); } else { s->apr[regno][cpu] = value; } From 84ce4b9b9943d26c9d9c7ea8abd924033c125535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 May 2024 16:06:20 +0100 Subject: [PATCH 0968/2884] hw/input/tsc2005: Fix -Wchar-subscripts warning in tsc2005_txrx() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the function index is in range and use an unsigned variable to avoid the following warning with GCC 13.2.0: [666/5358] Compiling C object libcommon.fa.p/hw_input_tsc2005.c.o hw/input/tsc2005.c: In function 'tsc2005_timer_tick': hw/input/tsc2005.c:416:26: warning: array subscript has type 'char' [-Wchar-subscripts] 416 | s->dav |= mode_regs[s->function]; | ~^~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Message-id: 20240508143513.44996-1-philmd@linaro.org Reviewed-by: Peter Maydell [PMM: fixed missing ')'] Signed-off-by: Peter Maydell --- hw/input/tsc2005.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index 941f163d36..ac7f54eeaf 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -406,6 +406,9 @@ uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len) static void tsc2005_timer_tick(void *opaque) { TSC2005State *s = opaque; + unsigned int function = s->function; + + assert(function < ARRAY_SIZE(mode_regs)); /* Timer ticked -- a set of conversions has been finished. */ @@ -413,7 +416,7 @@ static void tsc2005_timer_tick(void *opaque) return; s->busy = false; - s->dav |= mode_regs[s->function]; + s->dav |= mode_regs[function]; s->function = -1; tsc2005_pin_update(s); } From d0a040a8c95c0cec8fab026a5d0a1aa5c7143d74 Mon Sep 17 00:00:00 2001 From: Tanmay Patil Date: Thu, 23 May 2024 16:06:21 +0100 Subject: [PATCH 0969/2884] hw: arm: Remove use of tabs in some source files Some of the source files for older devices use hardcoded tabs instead of our current coding standard's required spaces. Fix these in the following files: - hw/arm/boot.c - hw/char/omap_uart.c - hw/gpio/zaurus.c - hw/input/tsc2005.c This commit is mostly whitespace-only changes; it also adds curly-braces to some 'if' statements. This addresses part of https://gitlab.com/qemu-project/qemu/-/issues/373 but some other files remain to be handled. Signed-off-by: Tanmay Patil Message-id: 20240508081502.88375-1-tanmaynpatil105@gmail.com Reviewed-by: Peter Maydell [PMM: tweaked commit message] Signed-off-by: Peter Maydell --- hw/arm/boot.c | 8 +-- hw/char/omap_uart.c | 49 +++++++++-------- hw/gpio/zaurus.c | 59 ++++++++++---------- hw/input/tsc2005.c | 130 ++++++++++++++++++++++++-------------------- 4 files changed, 130 insertions(+), 116 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 84ea6a807a..d480a7da02 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -347,13 +347,13 @@ static void set_kernel_args_old(const struct arm_boot_info *info, WRITE_WORD(p, info->ram_size / 4096); /* ramdisk_size */ WRITE_WORD(p, 0); -#define FLAG_READONLY 1 -#define FLAG_RDLOAD 4 -#define FLAG_RDPROMPT 8 +#define FLAG_READONLY 1 +#define FLAG_RDLOAD 4 +#define FLAG_RDPROMPT 8 /* flags */ WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); /* rootdev */ - WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ + WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ /* video_num_cols */ WRITE_WORD(p, 0); /* video_num_rows */ diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 6848bddb4e..c2ef4c137e 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -61,7 +61,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base, s->fclk = fclk; s->irq = irq; s->serial = serial_mm_init(get_system_memory(), base, 2, irq, - omap_clk_getrate(fclk)/16, + omap_clk_getrate(fclk) / 16, chr ?: qemu_chr_new(label, "null", NULL), DEVICE_NATIVE_ENDIAN); return s; @@ -76,27 +76,27 @@ static uint64_t omap_uart_read(void *opaque, hwaddr addr, unsigned size) } switch (addr) { - case 0x20: /* MDR1 */ + case 0x20: /* MDR1 */ return s->mdr[0]; - case 0x24: /* MDR2 */ + case 0x24: /* MDR2 */ return s->mdr[1]; - case 0x40: /* SCR */ + case 0x40: /* SCR */ return s->scr; - case 0x44: /* SSR */ + case 0x44: /* SSR */ return 0x0; - case 0x48: /* EBLR (OMAP2) */ + case 0x48: /* EBLR (OMAP2) */ return s->eblr; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ + case 0x4C: /* OSC_12M_SEL (OMAP1) */ return s->clksel; - case 0x50: /* MVR */ + case 0x50: /* MVR */ return 0x30; - case 0x54: /* SYSC (OMAP2) */ + case 0x54: /* SYSC (OMAP2) */ return s->syscontrol; - case 0x58: /* SYSS (OMAP2) */ + case 0x58: /* SYSS (OMAP2) */ return 1; - case 0x5c: /* WER (OMAP2) */ + case 0x5c: /* WER (OMAP2) */ return s->wkup; - case 0x60: /* CFPS (OMAP2) */ + case 0x60: /* CFPS (OMAP2) */ return s->cfps; } @@ -115,35 +115,36 @@ static void omap_uart_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x20: /* MDR1 */ + case 0x20: /* MDR1 */ s->mdr[0] = value & 0x7f; break; - case 0x24: /* MDR2 */ + case 0x24: /* MDR2 */ s->mdr[1] = value & 0xff; break; - case 0x40: /* SCR */ + case 0x40: /* SCR */ s->scr = value & 0xff; break; - case 0x48: /* EBLR (OMAP2) */ + case 0x48: /* EBLR (OMAP2) */ s->eblr = value & 0xff; break; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ + case 0x4C: /* OSC_12M_SEL (OMAP1) */ s->clksel = value & 1; break; - case 0x44: /* SSR */ - case 0x50: /* MVR */ - case 0x58: /* SYSS (OMAP2) */ + case 0x44: /* SSR */ + case 0x50: /* MVR */ + case 0x58: /* SYSS (OMAP2) */ OMAP_RO_REG(addr); break; - case 0x54: /* SYSC (OMAP2) */ + case 0x54: /* SYSC (OMAP2) */ s->syscontrol = value & 0x1d; - if (value & 2) + if (value & 2) { omap_uart_reset(s); + } break; - case 0x5c: /* WER (OMAP2) */ + case 0x5c: /* WER (OMAP2) */ s->wkup = value & 0x7f; break; - case 0x60: /* CFPS (OMAP2) */ + case 0x60: /* CFPS (OMAP2) */ s->cfps = value & 0xff; break; default: diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index 5884804c58..7342440b95 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -49,19 +49,20 @@ struct ScoopInfo { uint16_t isr; }; -#define SCOOP_MCR 0x00 -#define SCOOP_CDR 0x04 -#define SCOOP_CSR 0x08 -#define SCOOP_CPR 0x0c -#define SCOOP_CCR 0x10 -#define SCOOP_IRR_IRM 0x14 -#define SCOOP_IMR 0x18 -#define SCOOP_ISR 0x1c -#define SCOOP_GPCR 0x20 -#define SCOOP_GPWR 0x24 -#define SCOOP_GPRR 0x28 +#define SCOOP_MCR 0x00 +#define SCOOP_CDR 0x04 +#define SCOOP_CSR 0x08 +#define SCOOP_CPR 0x0c +#define SCOOP_CCR 0x10 +#define SCOOP_IRR_IRM 0x14 +#define SCOOP_IMR 0x18 +#define SCOOP_ISR 0x1c +#define SCOOP_GPCR 0x20 +#define SCOOP_GPWR 0x24 +#define SCOOP_GPRR 0x28 -static inline void scoop_gpio_handler_update(ScoopInfo *s) { +static inline void scoop_gpio_handler_update(ScoopInfo *s) +{ uint32_t level, diff; int bit; level = s->gpio_level & s->gpio_dir; @@ -125,8 +126,9 @@ static void scoop_write(void *opaque, hwaddr addr, break; case SCOOP_CPR: s->power = value; - if (value & 0x80) + if (value & 0x80) { s->power |= 0x8040; + } break; case SCOOP_CCR: s->ccr = value; @@ -145,7 +147,7 @@ static void scoop_write(void *opaque, hwaddr addr, scoop_gpio_handler_update(s); break; case SCOOP_GPWR: - case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ + case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ s->gpio_level = value & s->gpio_dir; scoop_gpio_handler_update(s); break; @@ -166,10 +168,11 @@ static void scoop_gpio_set(void *opaque, int line, int level) { ScoopInfo *s = (ScoopInfo *) opaque; - if (level) + if (level) { s->gpio_level |= (1 << line); - else + } else { s->gpio_level &= ~(1 << line); + } } static void scoop_init(Object *obj) @@ -203,7 +206,7 @@ static int scoop_post_load(void *opaque, int version_id) return 0; } -static bool is_version_0 (void *opaque, int version_id) +static bool is_version_0(void *opaque, int version_id) { return version_id == 0; } @@ -265,7 +268,7 @@ type_init(scoop_register_types) /* Write the bootloader parameters memory area. */ -#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) +#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) static struct QEMU_PACKED sl_param_info { uint32_t comadj_keyword; @@ -286,16 +289,16 @@ static struct QEMU_PACKED sl_param_info { uint32_t phad_keyword; int32_t phadadj; } zaurus_bootparam = { - .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), - .comadj = 125, - .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), - .uuid = { -1 }, - .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), - .touch_xp = -1, - .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), - .adadj = -1, - .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), - .phadadj = 0x01, + .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), + .comadj = 125, + .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), + .uuid = { -1 }, + .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), + .touch_xp = -1, + .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), + .adadj = -1, + .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), + .phadadj = 0x01, }; void sl_bootparam_write(hwaddr ptr) diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index ac7f54eeaf..54a15d2441 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -28,10 +28,10 @@ #include "migration/vmstate.h" #include "trace.h" -#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) +#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) typedef struct { - qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */ + qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */ QEMUTimer *timer; uint16_t model; @@ -63,7 +63,7 @@ typedef struct { } TSC2005State; enum { - TSC_MODE_XYZ_SCAN = 0x0, + TSC_MODE_XYZ_SCAN = 0x0, TSC_MODE_XY_SCAN, TSC_MODE_X, TSC_MODE_Y, @@ -82,100 +82,100 @@ enum { }; static const uint16_t mode_regs[16] = { - 0xf000, /* X, Y, Z scan */ - 0xc000, /* X, Y scan */ - 0x8000, /* X */ - 0x4000, /* Y */ - 0x3000, /* Z */ - 0x0800, /* AUX */ - 0x0400, /* TEMP1 */ - 0x0200, /* TEMP2 */ - 0x0800, /* AUX scan */ - 0x0040, /* X test */ - 0x0020, /* Y test */ - 0x0080, /* Short-circuit test */ - 0x0000, /* Reserved */ - 0x0000, /* X+, X- drivers */ - 0x0000, /* Y+, Y- drivers */ - 0x0000, /* Y+, X- drivers */ + 0xf000, /* X, Y, Z scan */ + 0xc000, /* X, Y scan */ + 0x8000, /* X */ + 0x4000, /* Y */ + 0x3000, /* Z */ + 0x0800, /* AUX */ + 0x0400, /* TEMP1 */ + 0x0200, /* TEMP2 */ + 0x0800, /* AUX scan */ + 0x0040, /* X test */ + 0x0020, /* Y test */ + 0x0080, /* Short-circuit test */ + 0x0000, /* Reserved */ + 0x0000, /* X+, X- drivers */ + 0x0000, /* Y+, Y- drivers */ + 0x0000, /* Y+, X- drivers */ }; -#define X_TRANSFORM(s) \ +#define X_TRANSFORM(s) \ ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) -#define Y_TRANSFORM(s) \ +#define Y_TRANSFORM(s) \ ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) -#define Z1_TRANSFORM(s) \ +#define Z1_TRANSFORM(s) \ ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) -#define Z2_TRANSFORM(s) \ +#define Z2_TRANSFORM(s) \ ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) -#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */ -#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */ -#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */ +#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */ +#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */ +#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */ static uint16_t tsc2005_read(TSC2005State *s, int reg) { uint16_t ret; switch (reg) { - case 0x0: /* X */ + case 0x0: /* X */ s->dav &= ~mode_regs[TSC_MODE_X]; return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + (s->noise & 3); - case 0x1: /* Y */ + case 0x1: /* Y */ s->dav &= ~mode_regs[TSC_MODE_Y]; - s->noise ++; + s->noise++; return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ (s->noise & 3); - case 0x2: /* Z1 */ + case 0x2: /* Z1 */ s->dav &= 0xdfff; return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - (s->noise & 3); - case 0x3: /* Z2 */ + case 0x3: /* Z2 */ s->dav &= 0xefff; return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | (s->noise & 3); - case 0x4: /* AUX */ + case 0x4: /* AUX */ s->dav &= ~mode_regs[TSC_MODE_AUX]; return TSC_CUT_RESOLUTION(AUX_VAL, s->precision); - case 0x5: /* TEMP1 */ + case 0x5: /* TEMP1 */ s->dav &= ~mode_regs[TSC_MODE_TEMP1]; return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - (s->noise & 5); - case 0x6: /* TEMP2 */ + case 0x6: /* TEMP2 */ s->dav &= 0xdfff; s->dav &= ~mode_regs[TSC_MODE_TEMP2]; return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ (s->noise & 3); - case 0x7: /* Status */ + case 0x7: /* Status */ ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0; s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] | mode_regs[TSC_MODE_TS_TEST]); s->reset = true; return ret; - case 0x8: /* AUX high threshold */ + case 0x8: /* AUX high threshold */ return s->aux_thr[1]; - case 0x9: /* AUX low threshold */ + case 0x9: /* AUX low threshold */ return s->aux_thr[0]; - case 0xa: /* TEMP high threshold */ + case 0xa: /* TEMP high threshold */ return s->temp_thr[1]; - case 0xb: /* TEMP low threshold */ + case 0xb: /* TEMP low threshold */ return s->temp_thr[0]; - case 0xc: /* CFR0 */ + case 0xc: /* CFR0 */ return (s->pressure << 15) | ((!s->busy) << 14) | - (s->nextprecision << 13) | s->timing[0]; - case 0xd: /* CFR1 */ + (s->nextprecision << 13) | s->timing[0]; + case 0xd: /* CFR1 */ return s->timing[1]; - case 0xe: /* CFR2 */ + case 0xe: /* CFR2 */ return (s->pin_func << 14) | s->filter; - case 0xf: /* Function select status */ + case 0xf: /* Function select status */ return s->function >= 0 ? 1 << s->function : 0; } @@ -200,13 +200,14 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) s->temp_thr[0] = data; break; - case 0xc: /* CFR0 */ + case 0xc: /* CFR0 */ s->host_mode = (data >> 15) != 0; if (s->enabled != !(data & 0x4000)) { s->enabled = !(data & 0x4000); trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); - if (s->busy && !s->enabled) + if (s->busy && !s->enabled) { timer_del(s->timer); + } s->busy = s->busy && s->enabled; } s->nextprecision = (data >> 13) & 1; @@ -216,10 +217,10 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) "tsc2005_write: illegal conversion clock setting\n"); } break; - case 0xd: /* CFR1 */ + case 0xd: /* CFR1 */ s->timing[1] = data & 0xf07; break; - case 0xe: /* CFR2 */ + case 0xe: /* CFR2 */ s->pin_func = (data >> 14) & 3; s->filter = data & 0x3fff; break; @@ -258,10 +259,12 @@ static void tsc2005_pin_update(TSC2005State *s) switch (s->nextfunction) { case TSC_MODE_XYZ_SCAN: case TSC_MODE_XY_SCAN: - if (!s->host_mode && s->dav) + if (!s->host_mode && s->dav) { s->enabled = false; - if (!s->pressure) + } + if (!s->pressure) { return; + } /* Fall through */ case TSC_MODE_AUX_SCAN: break; @@ -269,8 +272,9 @@ static void tsc2005_pin_update(TSC2005State *s) case TSC_MODE_X: case TSC_MODE_Y: case TSC_MODE_Z: - if (!s->pressure) + if (!s->pressure) { return; + } /* Fall through */ case TSC_MODE_AUX: case TSC_MODE_TEMP1: @@ -278,8 +282,9 @@ static void tsc2005_pin_update(TSC2005State *s) case TSC_MODE_X_TEST: case TSC_MODE_Y_TEST: case TSC_MODE_TS_TEST: - if (s->dav) + if (s->dav) { s->enabled = false; + } break; case TSC_MODE_RESERVED: @@ -290,13 +295,14 @@ static void tsc2005_pin_update(TSC2005State *s) return; } - if (!s->enabled || s->busy) + if (!s->enabled || s->busy) { return; + } s->busy = true; s->precision = s->nextprecision; s->function = s->nextfunction; - s->pdst = !s->pnd0; /* Synchronised on internal clock */ + s->pdst = !s->pnd0; /* Synchronised on internal clock */ expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (NANOSECONDS_PER_SECOND >> 7); timer_mod(s->timer, expires); @@ -331,7 +337,7 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) TSC2005State *s = opaque; uint32_t ret = 0; - switch (s->state ++) { + switch (s->state++) { case 0: if (value & 0x80) { /* Command */ @@ -343,8 +349,9 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) if (s->enabled != !(value & 1)) { s->enabled = !(value & 1); trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); - if (s->busy && !s->enabled) + if (s->busy && !s->enabled) { timer_del(s->timer); + } s->busy = s->busy && s->enabled; } tsc2005_pin_update(s); @@ -368,10 +375,11 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) break; case 1: - if (s->command) + if (s->command) { ret = (s->data >> 8) & 0xff; - else + } else { s->data |= value << 8; + } break; case 2: @@ -412,8 +420,9 @@ static void tsc2005_timer_tick(void *opaque) /* Timer ticked -- a set of conversions has been finished. */ - if (!s->busy) + if (!s->busy) { return; + } s->busy = false; s->dav |= mode_regs[function]; @@ -438,8 +447,9 @@ static void tsc2005_touchscreen_event(void *opaque, * signaling TS events immediately, but for now we simulate * the first conversion delay for sake of correctness. */ - if (p != s->pressure) + if (p != s->pressure) { tsc2005_pin_update(s); + } } static int tsc2005_post_load(void *opaque, int version_id) From 2fda0e776ae5360910f3521ec2608b535f338d90 Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Thu, 23 May 2024 16:06:21 +0100 Subject: [PATCH 0970/2884] docs/system: Remove ADC from raspi documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit None of the RPi boards have ADC on-board. In real life, an external ADC chip is required to operate on analog signals. Signed-off-by: Rayhan Faizel Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240512085716.222326-1-rayhan.faizel@gmail.com Signed-off-by: Peter Maydell --- docs/system/arm/raspi.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/system/arm/raspi.rst b/docs/system/arm/raspi.rst index fbec1da6a1..44eec3f1c3 100644 --- a/docs/system/arm/raspi.rst +++ b/docs/system/arm/raspi.rst @@ -40,7 +40,6 @@ Implemented devices Missing devices --------------- - * Analog to Digital Converter (ADC) * Pulse Width Modulation (PWM) * PCIE Root Port (raspi4b) * GENET Ethernet Controller (raspi4b) From db36e14501e8745a6e23f611f917d40a27cc5dee Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:16 -0700 Subject: [PATCH 0971/2884] target/arm: Use PLD, PLDW, PLI not NOP for t32 This fixes a bug in that neither PLI nor PLDW are present in ARMv6T2, but are introduced with ARMv7 and ARMv7MP respectively. For clarity, do not use NOP for PLD. Note that there is no PLDW (literal). Architecturally in the T1 encoding of "PLD (literal)" bit 5 is "(0)", which means that it should be zero and if it is not then the behaviour is CONSTRAINED UNPREDICTABLE (might UNDEF, NOP, or ignore the value of the bit). In our implementation we have patterns for both: + PLD 1111 1000 -001 1111 1111 ------------ # (literal) + PLD 1111 1000 -011 1111 1111 ------------ # (literal) and so we effectively ignore the value of bit 5. (This is a permitted option for this CONSTRAINED UNPREDICTABLE.) This isn't a behaviour change in this commit, since we previously had NOP lines for both those patterns. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240524232121.284515-3-richard.henderson@linaro.org [PMM: adjusted commit message to note that PLD (lit) T1 bit 5 being 1 is an UNPREDICTABLE case.] Signed-off-by: Peter Maydell --- target/arm/tcg/t32.decode | 25 ++++++++++++------------- target/arm/tcg/translate.c | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/t32.decode b/target/arm/tcg/t32.decode index f21ad0167a..d327178829 100644 --- a/target/arm/tcg/t32.decode +++ b/target/arm/tcg/t32.decode @@ -458,41 +458,41 @@ STR_ri 1111 1000 1100 .... .... ............ @ldst_ri_pos # Note that Load, unsigned (literal) overlaps all other load encodings. { { - NOP 1111 1000 -001 1111 1111 ------------ # PLD + PLD 1111 1000 -001 1111 1111 ------------ # (literal) LDRB_ri 1111 1000 .001 1111 .... ............ @ldst_ri_lit } { - NOP 1111 1000 1001 ---- 1111 ------------ # PLD + PLD 1111 1000 1001 ---- 1111 ------------ # (immediate T1) LDRB_ri 1111 1000 1001 .... .... ............ @ldst_ri_pos } LDRB_ri 1111 1000 0001 .... .... 1..1 ........ @ldst_ri_idx { - NOP 1111 1000 0001 ---- 1111 1100 -------- # PLD + PLD 1111 1000 0001 ---- 1111 1100 -------- # (immediate T2) LDRB_ri 1111 1000 0001 .... .... 1100 ........ @ldst_ri_neg } LDRBT_ri 1111 1000 0001 .... .... 1110 ........ @ldst_ri_unp { - NOP 1111 1000 0001 ---- 1111 000000 -- ---- # PLD + PLD 1111 1000 0001 ---- 1111 000000 -- ---- # (register) LDRB_rr 1111 1000 0001 .... .... 000000 .. .... @ldst_rr } } { { - NOP 1111 1000 -011 1111 1111 ------------ # PLD + PLD 1111 1000 -011 1111 1111 ------------ # (literal) LDRH_ri 1111 1000 .011 1111 .... ............ @ldst_ri_lit } { - NOP 1111 1000 1011 ---- 1111 ------------ # PLDW + PLDW 1111 1000 1011 ---- 1111 ------------ # (immediate T1) LDRH_ri 1111 1000 1011 .... .... ............ @ldst_ri_pos } LDRH_ri 1111 1000 0011 .... .... 1..1 ........ @ldst_ri_idx { - NOP 1111 1000 0011 ---- 1111 1100 -------- # PLDW + PLDW 1111 1000 0011 ---- 1111 1100 -------- # (immediate T2) LDRH_ri 1111 1000 0011 .... .... 1100 ........ @ldst_ri_neg } LDRHT_ri 1111 1000 0011 .... .... 1110 ........ @ldst_ri_unp { - NOP 1111 1000 0011 ---- 1111 000000 -- ---- # PLDW + PLDW 1111 1000 0011 ---- 1111 000000 -- ---- # (register) LDRH_rr 1111 1000 0011 .... .... 000000 .. .... @ldst_rr } } @@ -504,24 +504,23 @@ STR_ri 1111 1000 1100 .... .... ............ @ldst_ri_pos LDRT_ri 1111 1000 0101 .... .... 1110 ........ @ldst_ri_unp LDR_rr 1111 1000 0101 .... .... 000000 .. .... @ldst_rr } -# NOPs here are PLI. { { - NOP 1111 1001 -001 1111 1111 ------------ + PLI 1111 1001 -001 1111 1111 ------------ # (literal T3) LDRSB_ri 1111 1001 .001 1111 .... ............ @ldst_ri_lit } { - NOP 1111 1001 1001 ---- 1111 ------------ + PLI 1111 1001 1001 ---- 1111 ------------ # (immediate T1) LDRSB_ri 1111 1001 1001 .... .... ............ @ldst_ri_pos } LDRSB_ri 1111 1001 0001 .... .... 1..1 ........ @ldst_ri_idx { - NOP 1111 1001 0001 ---- 1111 1100 -------- + PLI 1111 1001 0001 ---- 1111 1100 -------- # (immediate T2) LDRSB_ri 1111 1001 0001 .... .... 1100 ........ @ldst_ri_neg } LDRSBT_ri 1111 1001 0001 .... .... 1110 ........ @ldst_ri_unp { - NOP 1111 1001 0001 ---- 1111 000000 -- ---- + PLI 1111 1001 0001 ---- 1111 000000 -- ---- # (register) LDRSB_rr 1111 1001 0001 .... .... 000000 .. .... @ldst_rr } } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index d605e10f11..187eacffd9 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -8765,12 +8765,12 @@ static bool trans_PLD(DisasContext *s, arg_PLD *a) return ENABLE_ARCH_5TE; } -static bool trans_PLDW(DisasContext *s, arg_PLD *a) +static bool trans_PLDW(DisasContext *s, arg_PLDW *a) { return arm_dc_feature(s, ARM_FEATURE_V7MP); } -static bool trans_PLI(DisasContext *s, arg_PLD *a) +static bool trans_PLI(DisasContext *s, arg_PLI *a) { return ENABLE_ARCH_7; } From fe84877ed44040dc128362b61dbfd923b851d6ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:18 -0700 Subject: [PATCH 0972/2884] target/arm: Zero-extend writeback for fp16 FCVTZS (scalar, integer) Fixes RISU mismatch for "fcvtzs h31, h0, #14". Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240524232121.284515-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 4126aaa27e..d97acdbaf9 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8707,6 +8707,9 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, read_vec_element_i32(s, tcg_op, rn, pass, size); fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); if (is_scalar) { + if (size == MO_16 && !is_u) { + tcg_gen_ext16u_i32(tcg_op, tcg_op); + } write_fp_sreg(s, rd, tcg_op); } else { write_vec_element_i32(s, tcg_op, rd, pass, size); From c0ca7ed049c468647e9e5239f4275565dcc39179 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:19 -0700 Subject: [PATCH 0973/2884] target/arm: Fix decode of FMOV (hp) vs MOVI The decode of FMOV (vector, immediate, half-precision) vs invalid cases of MOVI are incorrect. Fixes RISU mismatch for invalid insn 0x2f01fd31. Fixes: 70b4e6a4457 ("arm/translate-a64: add FP16 FMOV to simd_mod_imm") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240524232121.284515-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d97acdbaf9..5455ae3685 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -7904,27 +7904,31 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) bool is_q = extract32(insn, 30, 1); uint64_t imm = 0; - if (o2 != 0 || ((cmode == 0xf) && is_neg && !is_q)) { - /* Check for FMOV (vector, immediate) - half-precision */ - if (!(dc_isar_feature(aa64_fp16, s) && o2 && cmode == 0xf)) { + if (o2) { + if (cmode != 0xf || is_neg) { unallocated_encoding(s); return; } - } - - if (!fp_access_check(s)) { - return; - } - - if (cmode == 15 && o2 && !is_neg) { /* FMOV (vector, immediate) - half-precision */ + if (!dc_isar_feature(aa64_fp16, s)) { + unallocated_encoding(s); + return; + } imm = vfp_expand_imm(MO_16, abcdefgh); /* now duplicate across the lanes */ imm = dup_const(MO_16, imm); } else { + if (cmode == 0xf && is_neg && !is_q) { + unallocated_encoding(s); + return; + } imm = asimd_imm_const(abcdefgh, cmode, is_neg); } + if (!fp_access_check(s)) { + return; + } + if (!((cmode & 0x9) == 0x1 || (cmode & 0xd) == 0x9)) { /* MOVI or MVNI, with MVNI negation handled above. */ tcg_gen_gvec_dup_imm(MO_64, vec_full_reg_offset(s, rd), is_q ? 16 : 8, From 5d874e5da23846c40dcb6d73a4c47bb95ff54372 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:20 -0700 Subject: [PATCH 0974/2884] target/arm: Verify sz=0 for Advanced SIMD scalar pairwise (fp16) All of these insns have "if sz == '1' then UNDEFINED" in their pseudocode. Fixes a RISU miscompare for invalid insn 0x5ef0c87a. Fixes: 5c36d89567c ("arm/translate-a64: add all FP16 ops in simd_scalar_pairwise") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240524232121.284515-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5455ae3685..0bdddb8517 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8006,7 +8006,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0x2f: /* FMINP */ /* FP op, size[0] is 32 or 64 bit*/ if (!u) { - if (!dc_isar_feature(aa64_fp16, s)) { + if ((size & 1) || !dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } else { From 09a52d854aec39b1f6aa12a6f034dbdb424419cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:21 -0700 Subject: [PATCH 0975/2884] target/arm: Split out gengvec.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 1612 ++++++++++++++++++++++++++++++++++++ target/arm/tcg/meson.build | 1 + target/arm/tcg/translate.c | 1588 ----------------------------------- target/arm/tcg/translate.h | 5 + 4 files changed, 1618 insertions(+), 1588 deletions(-) create mode 100644 target/arm/tcg/gengvec.c diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c new file mode 100644 index 0000000000..7a1856253f --- /dev/null +++ b/target/arm/tcg/gengvec.c @@ -0,0 +1,1612 @@ +/* + * ARM generic vector expansion + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2005-2007 CodeSourcery + * Copyright (c) 2007 OpenedHand, Ltd. + * + * 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.1 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 . + */ + +#include "qemu/osdep.h" +#include "translate.h" + + +static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz, + gen_helper_gvec_3_ptr *fn) +{ + TCGv_ptr qc_ptr = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc)); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, qc_ptr, + opr_sz, max_sz, 0, fn); +} + +void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_gvec_qrdmlah_s16, gen_helper_gvec_qrdmlah_s32 + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_gvec_qrdmlsh_s16, gen_helper_gvec_qrdmlsh_s32 + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +#define GEN_CMP0(NAME, COND) \ + void NAME(unsigned vece, uint32_t d, uint32_t m, \ + uint32_t opr_sz, uint32_t max_sz) \ + { tcg_gen_gvec_cmpi(COND, vece, d, m, 0, opr_sz, max_sz); } + +GEN_CMP0(gen_gvec_ceq0, TCG_COND_EQ) +GEN_CMP0(gen_gvec_cle0, TCG_COND_LE) +GEN_CMP0(gen_gvec_cge0, TCG_COND_GE) +GEN_CMP0(gen_gvec_clt0, TCG_COND_LT) +GEN_CMP0(gen_gvec_cgt0, TCG_COND_GT) + +#undef GEN_CMP0 + +static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_sar8i_i64(a, a, shift); + tcg_gen_vec_add8_i64(d, d, a); +} + +static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_sar16i_i64(a, a, shift); + tcg_gen_vec_add16_i64(d, d, a); +} + +static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_sari_i32(a, a, shift); + tcg_gen_add_i32(d, d, a); +} + +static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_sari_i64(a, a, shift); + tcg_gen_add_i64(d, d, a); +} + +static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + tcg_gen_sari_vec(vece, a, a, sh); + tcg_gen_add_vec(vece, d, d, a); +} + +void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_ssra8_i64, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_ssra16_i64, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_ssra32_i32, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_ssra64_i64, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize]. */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* + * Shifts larger than the element size are architecturally valid. + * Signed results in all sign bits. + */ + shift = MIN(shift, (8 << vece) - 1); + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); +} + +static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_shr8i_i64(a, a, shift); + tcg_gen_vec_add8_i64(d, d, a); +} + +static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_shr16i_i64(a, a, shift); + tcg_gen_vec_add16_i64(d, d, a); +} + +static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_shri_i32(a, a, shift); + tcg_gen_add_i32(d, d, a); +} + +static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_shri_i64(a, a, shift); + tcg_gen_add_i64(d, d, a); +} + +static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + tcg_gen_shri_vec(vece, a, a, sh); + tcg_gen_add_vec(vece, d, d, a); +} + +void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_usra8_i64, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8, }, + { .fni8 = gen_usra16_i64, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16, }, + { .fni4 = gen_usra32_i32, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32, }, + { .fni8 = gen_usra64_i64, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64, }, + }; + + /* tszimm encoding produces immediates in the range [1..esize]. */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* + * Shifts larger than the element size are architecturally valid. + * Unsigned results in all zeros as input to accumulate: nop. + */ + if (shift < (8 << vece)) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } else { + /* Nop, but we do need to clear the tail. */ + tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); + } +} + +/* + * Shift one less than the requested amount, and the low bit is + * the rounding bit. For the 8 and 16-bit operations, because we + * mask the low bit, we can perform a normal integer shift instead + * of a vector shift. + */ +static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_sar8i_i64(d, a, sh); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_sar16i_i64(d, a, sh); + tcg_gen_vec_add16_i64(d, d, t); +} + +void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t; + + /* Handle shift by the input size for the benefit of trans_SRSHR_ri */ + if (sh == 32) { + tcg_gen_movi_i32(d, 0); + return; + } + t = tcg_temp_new_i32(); + tcg_gen_extract_i32(t, a, sh - 1, 1); + tcg_gen_sari_i32(d, a, sh); + tcg_gen_add_i32(d, d, t); +} + + void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t, a, sh - 1, 1); + tcg_gen_sari_i64(d, a, sh); + tcg_gen_add_i64(d, d, t); +} + +static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec ones = tcg_temp_new_vec_matching(d); + + tcg_gen_shri_vec(vece, t, a, sh - 1); + tcg_gen_dupi_vec(vece, ones, 1); + tcg_gen_and_vec(vece, t, t, ones); + tcg_gen_sari_vec(vece, d, a, sh); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_srshr8_i64, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_srshr16_i64, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_srshr32_i32, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_srshr64_i64, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + if (shift == (8 << vece)) { + /* + * Shifts larger than the element size are architecturally valid. + * Signed results in all sign bits. With rounding, this produces + * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. + * I.e. always zero. + */ + tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_srshr8_i64(t, a, sh); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_srshr16_i64(t, a, sh); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + gen_srshr32_i32(t, a, sh); + tcg_gen_add_i32(d, d, t); +} + +static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_srshr64_i64(t, a, sh); + tcg_gen_add_i64(d, d, t); +} + +static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + gen_srshr_vec(vece, t, a, sh); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_srsra8_i64, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fni8 = gen_srsra16_i64, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_srsra32_i32, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_srsra64_i64, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* + * Shifts larger than the element size are architecturally valid. + * Signed results in all sign bits. With rounding, this produces + * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. + * I.e. always zero. With accumulation, this leaves D unchanged. + */ + if (shift == (8 << vece)) { + /* Nop, but we do need to clear the tail. */ + tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_shr8i_i64(d, a, sh); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_shr16i_i64(d, a, sh); + tcg_gen_vec_add16_i64(d, d, t); +} + +void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t; + + /* Handle shift by the input size for the benefit of trans_URSHR_ri */ + if (sh == 32) { + tcg_gen_extract_i32(d, a, sh - 1, 1); + return; + } + t = tcg_temp_new_i32(); + tcg_gen_extract_i32(t, a, sh - 1, 1); + tcg_gen_shri_i32(d, a, sh); + tcg_gen_add_i32(d, d, t); +} + +void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t, a, sh - 1, 1); + tcg_gen_shri_i64(d, a, sh); + tcg_gen_add_i64(d, d, t); +} + +static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec ones = tcg_temp_new_vec_matching(d); + + tcg_gen_shri_vec(vece, t, a, shift - 1); + tcg_gen_dupi_vec(vece, ones, 1); + tcg_gen_and_vec(vece, t, t, ones); + tcg_gen_shri_vec(vece, d, a, shift); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_urshr8_i64, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_urshr16_i64, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_urshr32_i32, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_urshr64_i64, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + if (shift == (8 << vece)) { + /* + * Shifts larger than the element size are architecturally valid. + * Unsigned results in zero. With rounding, this produces a + * copy of the most significant bit. + */ + tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift - 1, opr_sz, max_sz); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + if (sh == 8) { + tcg_gen_vec_shr8i_i64(t, a, 7); + } else { + gen_urshr8_i64(t, a, sh); + } + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + if (sh == 16) { + tcg_gen_vec_shr16i_i64(t, a, 15); + } else { + gen_urshr16_i64(t, a, sh); + } + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + if (sh == 32) { + tcg_gen_shri_i32(t, a, 31); + } else { + gen_urshr32_i32(t, a, sh); + } + tcg_gen_add_i32(d, d, t); +} + +static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + if (sh == 64) { + tcg_gen_shri_i64(t, a, 63); + } else { + gen_urshr64_i64(t, a, sh); + } + tcg_gen_add_i64(d, d, t); +} + +static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + if (sh == (8 << vece)) { + tcg_gen_shri_vec(vece, t, a, sh - 1); + } else { + gen_urshr_vec(vece, t, a, sh); + } + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_ursra8_i64, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fni8 = gen_ursra16_i64, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_ursra32_i32, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_ursra64_i64, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); +} + +static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_8, 0xff >> shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_16, 0xffff >> shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_shri_i32(a, a, shift); + tcg_gen_deposit_i32(d, d, a, 0, 32 - shift); +} + +static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_shri_i64(a, a, shift); + tcg_gen_deposit_i64(d, d, a, 0, 64 - shift); +} + +static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_temp_new_vec_matching(d); + + tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK((8 << vece) - sh, sh)); + tcg_gen_shri_vec(vece, t, a, sh); + tcg_gen_and_vec(vece, d, d, m); + tcg_gen_or_vec(vece, d, d, t); +} + +void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_shri_vec, 0 }; + const GVecGen2i ops[4] = { + { .fni8 = gen_shr8_ins_i64, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shr16_ins_i64, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shr32_ins_i32, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_shr64_ins_i64, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize]. */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* Shift of esize leaves destination unchanged. */ + if (shift < (8 << vece)) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } else { + /* Nop, but we do need to clear the tail. */ + tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); + } +} + +static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_8, 0xff << shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_16, 0xffff << shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_deposit_i32(d, d, a, shift, 32 - shift); +} + +static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_deposit_i64(d, d, a, shift, 64 - shift); +} + +static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_temp_new_vec_matching(d); + + tcg_gen_shli_vec(vece, t, a, sh); + tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK(0, sh)); + tcg_gen_and_vec(vece, d, d, m); + tcg_gen_or_vec(vece, d, d, t); +} + +void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; + const GVecGen2i ops[4] = { + { .fni8 = gen_shl8_ins_i64, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shl16_ins_i64, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shl32_ins_i32, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_shl64_ins_i64, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [0..esize-1]. */ + tcg_debug_assert(shift >= 0); + tcg_debug_assert(shift < (8 << vece)); + + if (shift == 0) { + tcg_gen_gvec_mov(vece, rd_ofs, rm_ofs, opr_sz, max_sz); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u8(a, a, b); + gen_helper_neon_add_u8(d, d, a); +} + +static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u8(a, a, b); + gen_helper_neon_sub_u8(d, d, a); +} + +static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u16(a, a, b); + gen_helper_neon_add_u16(d, d, a); +} + +static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u16(a, a, b); + gen_helper_neon_sub_u16(d, d, a); +} + +static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mul_i32(a, a, b); + tcg_gen_add_i32(d, d, a); +} + +static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mul_i32(a, a, b); + tcg_gen_sub_i32(d, d, a); +} + +static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mul_i64(a, a, b); + tcg_gen_add_i64(d, d, a); +} + +static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mul_i64(a, a, b); + tcg_gen_sub_i64(d, d, a); +} + +static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_mul_vec(vece, a, a, b); + tcg_gen_add_vec(vece, d, d, a); +} + +static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_mul_vec(vece, a, a, b); + tcg_gen_sub_vec(vece, d, d, a); +} + +/* Note that while NEON does not support VMLA and VMLS as 64-bit ops, + * these tables are shared with AArch64 which does support them. + */ +void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fni4 = gen_mla8_i32, + .fniv = gen_mla_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni4 = gen_mla16_i32, + .fniv = gen_mla_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_mla32_i32, + .fniv = gen_mla_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_mla64_i64, + .fniv = gen_mla_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fni4 = gen_mls8_i32, + .fniv = gen_mls_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni4 = gen_mls16_i32, + .fniv = gen_mls_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_mls32_i32, + .fniv = gen_mls_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_mls64_i64, + .fniv = gen_mls_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +/* CMTST : test is "if (X & Y != 0)". */ +static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_and_i32(d, a, b); + tcg_gen_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0)); +} + +void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_and_i64(d, a, b); + tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0)); +} + +static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_and_vec(vece, d, a, b); + tcg_gen_dupi_vec(vece, a, 0); + tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a); +} + +void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_cmp_vec, 0 }; + static const GVecGen3 ops[4] = { + { .fni4 = gen_helper_neon_tst_u8, + .fniv = gen_cmtst_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_tst_u16, + .fniv = gen_cmtst_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_cmtst_i32, + .fniv = gen_cmtst_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_cmtst_i64, + .fniv = gen_cmtst_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) +{ + TCGv_i32 lval = tcg_temp_new_i32(); + TCGv_i32 rval = tcg_temp_new_i32(); + TCGv_i32 lsh = tcg_temp_new_i32(); + TCGv_i32 rsh = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 max = tcg_constant_i32(32); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i32(lsh, shift); + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_shl_i32(lval, src, lsh); + tcg_gen_shr_i32(rval, src, rsh); + tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero); + tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst); +} + +void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) +{ + TCGv_i64 lval = tcg_temp_new_i64(); + TCGv_i64 rval = tcg_temp_new_i64(); + TCGv_i64 lsh = tcg_temp_new_i64(); + TCGv_i64 rsh = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 max = tcg_constant_i64(64); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i64(lsh, shift); + tcg_gen_neg_i64(rsh, lsh); + tcg_gen_shl_i64(lval, src, lsh); + tcg_gen_shr_i64(rval, src, rsh); + tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero); + tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst); +} + +static void gen_ushl_vec(unsigned vece, TCGv_vec dst, + TCGv_vec src, TCGv_vec shift) +{ + TCGv_vec lval = tcg_temp_new_vec_matching(dst); + TCGv_vec rval = tcg_temp_new_vec_matching(dst); + TCGv_vec lsh = tcg_temp_new_vec_matching(dst); + TCGv_vec rsh = tcg_temp_new_vec_matching(dst); + TCGv_vec msk, max; + + tcg_gen_neg_vec(vece, rsh, shift); + if (vece == MO_8) { + tcg_gen_mov_vec(lsh, shift); + } else { + msk = tcg_temp_new_vec_matching(dst); + tcg_gen_dupi_vec(vece, msk, 0xff); + tcg_gen_and_vec(vece, lsh, shift, msk); + tcg_gen_and_vec(vece, rsh, rsh, msk); + } + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_shlv_vec(vece, lval, src, lsh); + tcg_gen_shrv_vec(vece, rval, src, rsh); + + max = tcg_temp_new_vec_matching(dst); + tcg_gen_dupi_vec(vece, max, 8 << vece); + + /* + * The choice of LT (signed) and GEU (unsigned) are biased toward + * the instructions of the x86_64 host. For MO_8, the whole byte + * is significant so we must use an unsigned compare; otherwise we + * have already masked to a byte and so a signed compare works. + * Other tcg hosts have a full set of comparisons and do not care. + */ + if (vece == MO_8) { + tcg_gen_cmp_vec(TCG_COND_GEU, vece, lsh, lsh, max); + tcg_gen_cmp_vec(TCG_COND_GEU, vece, rsh, rsh, max); + tcg_gen_andc_vec(vece, lval, lval, lsh); + tcg_gen_andc_vec(vece, rval, rval, rsh); + } else { + tcg_gen_cmp_vec(TCG_COND_LT, vece, lsh, lsh, max); + tcg_gen_cmp_vec(TCG_COND_LT, vece, rsh, rsh, max); + tcg_gen_and_vec(vece, lval, lval, lsh); + tcg_gen_and_vec(vece, rval, rval, rsh); + } + tcg_gen_or_vec(vece, dst, lval, rval); +} + +void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_shlv_vec, + INDEX_op_shrv_vec, INDEX_op_cmp_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_ushl_vec, + .fno = gen_helper_gvec_ushl_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_ushl_vec, + .fno = gen_helper_gvec_ushl_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_ushl_i32, + .fniv = gen_ushl_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_ushl_i64, + .fniv = gen_ushl_vec, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) +{ + TCGv_i32 lval = tcg_temp_new_i32(); + TCGv_i32 rval = tcg_temp_new_i32(); + TCGv_i32 lsh = tcg_temp_new_i32(); + TCGv_i32 rsh = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 max = tcg_constant_i32(31); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i32(lsh, shift); + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_shl_i32(lval, src, lsh); + tcg_gen_umin_i32(rsh, rsh, max); + tcg_gen_sar_i32(rval, src, rsh); + tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero); + tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval); +} + +void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) +{ + TCGv_i64 lval = tcg_temp_new_i64(); + TCGv_i64 rval = tcg_temp_new_i64(); + TCGv_i64 lsh = tcg_temp_new_i64(); + TCGv_i64 rsh = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 max = tcg_constant_i64(63); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i64(lsh, shift); + tcg_gen_neg_i64(rsh, lsh); + tcg_gen_shl_i64(lval, src, lsh); + tcg_gen_umin_i64(rsh, rsh, max); + tcg_gen_sar_i64(rval, src, rsh); + tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero); + tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval); +} + +static void gen_sshl_vec(unsigned vece, TCGv_vec dst, + TCGv_vec src, TCGv_vec shift) +{ + TCGv_vec lval = tcg_temp_new_vec_matching(dst); + TCGv_vec rval = tcg_temp_new_vec_matching(dst); + TCGv_vec lsh = tcg_temp_new_vec_matching(dst); + TCGv_vec rsh = tcg_temp_new_vec_matching(dst); + TCGv_vec tmp = tcg_temp_new_vec_matching(dst); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_neg_vec(vece, rsh, shift); + if (vece == MO_8) { + tcg_gen_mov_vec(lsh, shift); + } else { + tcg_gen_dupi_vec(vece, tmp, 0xff); + tcg_gen_and_vec(vece, lsh, shift, tmp); + tcg_gen_and_vec(vece, rsh, rsh, tmp); + } + + /* Bound rsh so out of bound right shift gets -1. */ + tcg_gen_dupi_vec(vece, tmp, (8 << vece) - 1); + tcg_gen_umin_vec(vece, rsh, rsh, tmp); + tcg_gen_cmp_vec(TCG_COND_GT, vece, tmp, lsh, tmp); + + tcg_gen_shlv_vec(vece, lval, src, lsh); + tcg_gen_sarv_vec(vece, rval, src, rsh); + + /* Select in-bound left shift. */ + tcg_gen_andc_vec(vece, lval, lval, tmp); + + /* Select between left and right shift. */ + if (vece == MO_8) { + tcg_gen_dupi_vec(vece, tmp, 0); + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, rval, lval); + } else { + tcg_gen_dupi_vec(vece, tmp, 0x80); + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, lval, rval); + } +} + +void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec, + INDEX_op_sarv_vec, INDEX_op_cmp_vec, INDEX_op_cmpsel_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_sshl_vec, + .fno = gen_helper_gvec_sshl_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_sshl_vec, + .fno = gen_helper_gvec_sshl_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_sshl_i32, + .fniv = gen_sshl_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_sshl_i64, + .fniv = gen_sshl_vec, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_add_vec(vece, x, a, b); + tcg_gen_usadd_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); +} + +void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_usadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_b, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_h, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_s, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_d, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_add_vec(vece, x, a, b); + tcg_gen_ssadd_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); +} + +void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_ssadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_sub_vec(vece, x, a, b); + tcg_gen_ussub_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); +} + +void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_ussub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_sub_vec(vece, x, a, b); + tcg_gen_sssub_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); +} + +void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sssub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_sub_i32(d, b, a); + tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t); +} + +static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_sub_i64(d, b, a); + tcg_gen_movcond_i64(TCG_COND_LT, d, a, b, d, t); +} + +static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_smin_vec(vece, t, a, b); + tcg_gen_smax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_sabd_i32, + .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_sabd_i64, + .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_sub_i32(d, b, a); + tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, d, t); +} + +static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_sub_i64(d, b, a); + tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, d, t); +} + +static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_umin_vec(vece, t, a, b); + tcg_gen_umax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_uabd_i32, + .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_uabd_i64, + .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_saba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + gen_sabd_i32(t, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_saba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + gen_sabd_i64(t, a, b); + tcg_gen_add_i64(d, d, t); +} + +static void gen_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + gen_sabd_vec(vece, t, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_add_vec, + INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_saba_i32, + .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_saba_i64, + .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_uaba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + gen_uabd_i32(t, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + gen_uabd_i64(t, a, b); + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + gen_uabd_vec(vece, t, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_add_vec, + INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_uaba_i32, + .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_uaba_i64, + .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 3b1a9f0fc5..bdb5c7352f 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -24,6 +24,7 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: gen_a64) arm_ss.add(files( 'cpu32.c', + 'gengvec.c', 'translate.c', 'translate-m-nocp.c', 'translate-mve.c', diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 187eacffd9..c5bc691d92 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -2912,1594 +2912,6 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc) gen_rfe(s, pc, load_cpu_field(spsr)); } -static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz, - gen_helper_gvec_3_ptr *fn) -{ - TCGv_ptr qc_ptr = tcg_temp_new_ptr(); - - tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc)); - tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, qc_ptr, - opr_sz, max_sz, 0, fn); -} - -void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static gen_helper_gvec_3_ptr * const fns[2] = { - gen_helper_gvec_qrdmlah_s16, gen_helper_gvec_qrdmlah_s32 - }; - tcg_debug_assert(vece >= 1 && vece <= 2); - gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); -} - -void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static gen_helper_gvec_3_ptr * const fns[2] = { - gen_helper_gvec_qrdmlsh_s16, gen_helper_gvec_qrdmlsh_s32 - }; - tcg_debug_assert(vece >= 1 && vece <= 2); - gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); -} - -#define GEN_CMP0(NAME, COND) \ - void NAME(unsigned vece, uint32_t d, uint32_t m, \ - uint32_t opr_sz, uint32_t max_sz) \ - { tcg_gen_gvec_cmpi(COND, vece, d, m, 0, opr_sz, max_sz); } - -GEN_CMP0(gen_gvec_ceq0, TCG_COND_EQ) -GEN_CMP0(gen_gvec_cle0, TCG_COND_LE) -GEN_CMP0(gen_gvec_cge0, TCG_COND_GE) -GEN_CMP0(gen_gvec_clt0, TCG_COND_LT) -GEN_CMP0(gen_gvec_cgt0, TCG_COND_GT) - -#undef GEN_CMP0 - -static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_sar8i_i64(a, a, shift); - tcg_gen_vec_add8_i64(d, d, a); -} - -static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_sar16i_i64(a, a, shift); - tcg_gen_vec_add16_i64(d, d, a); -} - -static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_sari_i32(a, a, shift); - tcg_gen_add_i32(d, d, a); -} - -static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_sari_i64(a, a, shift); - tcg_gen_add_i64(d, d, a); -} - -static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - tcg_gen_sari_vec(vece, a, a, sh); - tcg_gen_add_vec(vece, d, d, a); -} - -void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sari_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_ssra8_i64, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_ssra16_i64, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_ssra32_i32, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_ssra64_i64, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize]. */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* - * Shifts larger than the element size are architecturally valid. - * Signed results in all sign bits. - */ - shift = MIN(shift, (8 << vece) - 1); - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); -} - -static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_shr8i_i64(a, a, shift); - tcg_gen_vec_add8_i64(d, d, a); -} - -static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_shr16i_i64(a, a, shift); - tcg_gen_vec_add16_i64(d, d, a); -} - -static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_shri_i32(a, a, shift); - tcg_gen_add_i32(d, d, a); -} - -static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_shri_i64(a, a, shift); - tcg_gen_add_i64(d, d, a); -} - -static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - tcg_gen_shri_vec(vece, a, a, sh); - tcg_gen_add_vec(vece, d, d, a); -} - -void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_usra8_i64, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8, }, - { .fni8 = gen_usra16_i64, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16, }, - { .fni4 = gen_usra32_i32, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32, }, - { .fni8 = gen_usra64_i64, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64, }, - }; - - /* tszimm encoding produces immediates in the range [1..esize]. */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* - * Shifts larger than the element size are architecturally valid. - * Unsigned results in all zeros as input to accumulate: nop. - */ - if (shift < (8 << vece)) { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } else { - /* Nop, but we do need to clear the tail. */ - tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); - } -} - -/* - * Shift one less than the requested amount, and the low bit is - * the rounding bit. For the 8 and 16-bit operations, because we - * mask the low bit, we can perform a normal integer shift instead - * of a vector shift. - */ -static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); - tcg_gen_vec_sar8i_i64(d, a, sh); - tcg_gen_vec_add8_i64(d, d, t); -} - -static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); - tcg_gen_vec_sar16i_i64(d, a, sh); - tcg_gen_vec_add16_i64(d, d, t); -} - -static void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) -{ - TCGv_i32 t; - - /* Handle shift by the input size for the benefit of trans_SRSHR_ri */ - if (sh == 32) { - tcg_gen_movi_i32(d, 0); - return; - } - t = tcg_temp_new_i32(); - tcg_gen_extract_i32(t, a, sh - 1, 1); - tcg_gen_sari_i32(d, a, sh); - tcg_gen_add_i32(d, d, t); -} - -static void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_extract_i64(t, a, sh - 1, 1); - tcg_gen_sari_i64(d, a, sh); - tcg_gen_add_i64(d, d, t); -} - -static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec ones = tcg_temp_new_vec_matching(d); - - tcg_gen_shri_vec(vece, t, a, sh - 1); - tcg_gen_dupi_vec(vece, ones, 1); - tcg_gen_and_vec(vece, t, t, ones); - tcg_gen_sari_vec(vece, d, a, sh); - tcg_gen_add_vec(vece, d, d, t); -} - -void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_srshr8_i64, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_srshr16_i64, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_srshr32_i32, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_srshr64_i64, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - if (shift == (8 << vece)) { - /* - * Shifts larger than the element size are architecturally valid. - * Signed results in all sign bits. With rounding, this produces - * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. - * I.e. always zero. - */ - tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - gen_srshr8_i64(t, a, sh); - tcg_gen_vec_add8_i64(d, d, t); -} - -static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - gen_srshr16_i64(t, a, sh); - tcg_gen_vec_add16_i64(d, d, t); -} - -static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - gen_srshr32_i32(t, a, sh); - tcg_gen_add_i32(d, d, t); -} - -static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - gen_srshr64_i64(t, a, sh); - tcg_gen_add_i64(d, d, t); -} - -static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - gen_srshr_vec(vece, t, a, sh); - tcg_gen_add_vec(vece, d, d, t); -} - -void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_srsra8_i64, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fni8 = gen_srsra16_i64, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_srsra32_i32, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_srsra64_i64, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* - * Shifts larger than the element size are architecturally valid. - * Signed results in all sign bits. With rounding, this produces - * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. - * I.e. always zero. With accumulation, this leaves D unchanged. - */ - if (shift == (8 << vece)) { - /* Nop, but we do need to clear the tail. */ - tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); - tcg_gen_vec_shr8i_i64(d, a, sh); - tcg_gen_vec_add8_i64(d, d, t); -} - -static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); - tcg_gen_vec_shr16i_i64(d, a, sh); - tcg_gen_vec_add16_i64(d, d, t); -} - -static void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) -{ - TCGv_i32 t; - - /* Handle shift by the input size for the benefit of trans_URSHR_ri */ - if (sh == 32) { - tcg_gen_extract_i32(d, a, sh - 1, 1); - return; - } - t = tcg_temp_new_i32(); - tcg_gen_extract_i32(t, a, sh - 1, 1); - tcg_gen_shri_i32(d, a, sh); - tcg_gen_add_i32(d, d, t); -} - -static void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_extract_i64(t, a, sh - 1, 1); - tcg_gen_shri_i64(d, a, sh); - tcg_gen_add_i64(d, d, t); -} - -static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec ones = tcg_temp_new_vec_matching(d); - - tcg_gen_shri_vec(vece, t, a, shift - 1); - tcg_gen_dupi_vec(vece, ones, 1); - tcg_gen_and_vec(vece, t, t, ones); - tcg_gen_shri_vec(vece, d, a, shift); - tcg_gen_add_vec(vece, d, d, t); -} - -void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_urshr8_i64, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_urshr16_i64, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_urshr32_i32, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_urshr64_i64, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - if (shift == (8 << vece)) { - /* - * Shifts larger than the element size are architecturally valid. - * Unsigned results in zero. With rounding, this produces a - * copy of the most significant bit. - */ - tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift - 1, opr_sz, max_sz); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - if (sh == 8) { - tcg_gen_vec_shr8i_i64(t, a, 7); - } else { - gen_urshr8_i64(t, a, sh); - } - tcg_gen_vec_add8_i64(d, d, t); -} - -static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - if (sh == 16) { - tcg_gen_vec_shr16i_i64(t, a, 15); - } else { - gen_urshr16_i64(t, a, sh); - } - tcg_gen_vec_add16_i64(d, d, t); -} - -static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - if (sh == 32) { - tcg_gen_shri_i32(t, a, 31); - } else { - gen_urshr32_i32(t, a, sh); - } - tcg_gen_add_i32(d, d, t); -} - -static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - if (sh == 64) { - tcg_gen_shri_i64(t, a, 63); - } else { - gen_urshr64_i64(t, a, sh); - } - tcg_gen_add_i64(d, d, t); -} - -static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - if (sh == (8 << vece)) { - tcg_gen_shri_vec(vece, t, a, sh - 1); - } else { - gen_urshr_vec(vece, t, a, sh); - } - tcg_gen_add_vec(vece, d, d, t); -} - -void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_ursra8_i64, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fni8 = gen_ursra16_i64, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_ursra32_i32, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_ursra64_i64, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); -} - -static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_8, 0xff >> shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); -} - -static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_16, 0xffff >> shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); -} - -static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_shri_i32(a, a, shift); - tcg_gen_deposit_i32(d, d, a, 0, 32 - shift); -} - -static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_shri_i64(a, a, shift); - tcg_gen_deposit_i64(d, d, a, 0, 64 - shift); -} - -static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_temp_new_vec_matching(d); - - tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK((8 << vece) - sh, sh)); - tcg_gen_shri_vec(vece, t, a, sh); - tcg_gen_and_vec(vece, d, d, m); - tcg_gen_or_vec(vece, d, d, t); -} - -void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_shri_vec, 0 }; - const GVecGen2i ops[4] = { - { .fni8 = gen_shr8_ins_i64, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_shr16_ins_i64, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_shr32_ins_i32, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_shr64_ins_i64, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize]. */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* Shift of esize leaves destination unchanged. */ - if (shift < (8 << vece)) { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } else { - /* Nop, but we do need to clear the tail. */ - tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); - } -} - -static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_8, 0xff << shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); -} - -static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_16, 0xffff << shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); -} - -static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_deposit_i32(d, d, a, shift, 32 - shift); -} - -static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_deposit_i64(d, d, a, shift, 64 - shift); -} - -static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_temp_new_vec_matching(d); - - tcg_gen_shli_vec(vece, t, a, sh); - tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK(0, sh)); - tcg_gen_and_vec(vece, d, d, m); - tcg_gen_or_vec(vece, d, d, t); -} - -void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; - const GVecGen2i ops[4] = { - { .fni8 = gen_shl8_ins_i64, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_shl16_ins_i64, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_shl32_ins_i32, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_shl64_ins_i64, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [0..esize-1]. */ - tcg_debug_assert(shift >= 0); - tcg_debug_assert(shift < (8 << vece)); - - if (shift == 0) { - tcg_gen_gvec_mov(vece, rd_ofs, rm_ofs, opr_sz, max_sz); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u8(a, a, b); - gen_helper_neon_add_u8(d, d, a); -} - -static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u8(a, a, b); - gen_helper_neon_sub_u8(d, d, a); -} - -static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u16(a, a, b); - gen_helper_neon_add_u16(d, d, a); -} - -static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u16(a, a, b); - gen_helper_neon_sub_u16(d, d, a); -} - -static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mul_i32(a, a, b); - tcg_gen_add_i32(d, d, a); -} - -static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mul_i32(a, a, b); - tcg_gen_sub_i32(d, d, a); -} - -static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mul_i64(a, a, b); - tcg_gen_add_i64(d, d, a); -} - -static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mul_i64(a, a, b); - tcg_gen_sub_i64(d, d, a); -} - -static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_mul_vec(vece, a, a, b); - tcg_gen_add_vec(vece, d, d, a); -} - -static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_mul_vec(vece, a, a, b); - tcg_gen_sub_vec(vece, d, d, a); -} - -/* Note that while NEON does not support VMLA and VMLS as 64-bit ops, - * these tables are shared with AArch64 which does support them. - */ -void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_mul_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fni4 = gen_mla8_i32, - .fniv = gen_mla_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni4 = gen_mla16_i32, - .fniv = gen_mla_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_mla32_i32, - .fniv = gen_mla_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_mla64_i64, - .fniv = gen_mla_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_mul_vec, INDEX_op_sub_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fni4 = gen_mls8_i32, - .fniv = gen_mls_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni4 = gen_mls16_i32, - .fniv = gen_mls_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_mls32_i32, - .fniv = gen_mls_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_mls64_i64, - .fniv = gen_mls_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -/* CMTST : test is "if (X & Y != 0)". */ -static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_and_i32(d, a, b); - tcg_gen_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0)); -} - -void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_and_i64(d, a, b); - tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0)); -} - -static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_and_vec(vece, d, a, b); - tcg_gen_dupi_vec(vece, a, 0); - tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a); -} - -void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_cmp_vec, 0 }; - static const GVecGen3 ops[4] = { - { .fni4 = gen_helper_neon_tst_u8, - .fniv = gen_cmtst_vec, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni4 = gen_helper_neon_tst_u16, - .fniv = gen_cmtst_vec, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_cmtst_i32, - .fniv = gen_cmtst_vec, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_cmtst_i64, - .fniv = gen_cmtst_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) -{ - TCGv_i32 lval = tcg_temp_new_i32(); - TCGv_i32 rval = tcg_temp_new_i32(); - TCGv_i32 lsh = tcg_temp_new_i32(); - TCGv_i32 rsh = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_constant_i32(0); - TCGv_i32 max = tcg_constant_i32(32); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i32(lsh, shift); - tcg_gen_neg_i32(rsh, lsh); - tcg_gen_shl_i32(lval, src, lsh); - tcg_gen_shr_i32(rval, src, rsh); - tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero); - tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst); -} - -void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) -{ - TCGv_i64 lval = tcg_temp_new_i64(); - TCGv_i64 rval = tcg_temp_new_i64(); - TCGv_i64 lsh = tcg_temp_new_i64(); - TCGv_i64 rsh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); - TCGv_i64 max = tcg_constant_i64(64); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i64(lsh, shift); - tcg_gen_neg_i64(rsh, lsh); - tcg_gen_shl_i64(lval, src, lsh); - tcg_gen_shr_i64(rval, src, rsh); - tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero); - tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst); -} - -static void gen_ushl_vec(unsigned vece, TCGv_vec dst, - TCGv_vec src, TCGv_vec shift) -{ - TCGv_vec lval = tcg_temp_new_vec_matching(dst); - TCGv_vec rval = tcg_temp_new_vec_matching(dst); - TCGv_vec lsh = tcg_temp_new_vec_matching(dst); - TCGv_vec rsh = tcg_temp_new_vec_matching(dst); - TCGv_vec msk, max; - - tcg_gen_neg_vec(vece, rsh, shift); - if (vece == MO_8) { - tcg_gen_mov_vec(lsh, shift); - } else { - msk = tcg_temp_new_vec_matching(dst); - tcg_gen_dupi_vec(vece, msk, 0xff); - tcg_gen_and_vec(vece, lsh, shift, msk); - tcg_gen_and_vec(vece, rsh, rsh, msk); - } - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_shlv_vec(vece, lval, src, lsh); - tcg_gen_shrv_vec(vece, rval, src, rsh); - - max = tcg_temp_new_vec_matching(dst); - tcg_gen_dupi_vec(vece, max, 8 << vece); - - /* - * The choice of LT (signed) and GEU (unsigned) are biased toward - * the instructions of the x86_64 host. For MO_8, the whole byte - * is significant so we must use an unsigned compare; otherwise we - * have already masked to a byte and so a signed compare works. - * Other tcg hosts have a full set of comparisons and do not care. - */ - if (vece == MO_8) { - tcg_gen_cmp_vec(TCG_COND_GEU, vece, lsh, lsh, max); - tcg_gen_cmp_vec(TCG_COND_GEU, vece, rsh, rsh, max); - tcg_gen_andc_vec(vece, lval, lval, lsh); - tcg_gen_andc_vec(vece, rval, rval, rsh); - } else { - tcg_gen_cmp_vec(TCG_COND_LT, vece, lsh, lsh, max); - tcg_gen_cmp_vec(TCG_COND_LT, vece, rsh, rsh, max); - tcg_gen_and_vec(vece, lval, lval, lsh); - tcg_gen_and_vec(vece, rval, rval, rsh); - } - tcg_gen_or_vec(vece, dst, lval, rval); -} - -void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_neg_vec, INDEX_op_shlv_vec, - INDEX_op_shrv_vec, INDEX_op_cmp_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_ushl_vec, - .fno = gen_helper_gvec_ushl_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_ushl_vec, - .fno = gen_helper_gvec_ushl_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_ushl_i32, - .fniv = gen_ushl_vec, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_ushl_i64, - .fniv = gen_ushl_vec, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) -{ - TCGv_i32 lval = tcg_temp_new_i32(); - TCGv_i32 rval = tcg_temp_new_i32(); - TCGv_i32 lsh = tcg_temp_new_i32(); - TCGv_i32 rsh = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_constant_i32(0); - TCGv_i32 max = tcg_constant_i32(31); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i32(lsh, shift); - tcg_gen_neg_i32(rsh, lsh); - tcg_gen_shl_i32(lval, src, lsh); - tcg_gen_umin_i32(rsh, rsh, max); - tcg_gen_sar_i32(rval, src, rsh); - tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero); - tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval); -} - -void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) -{ - TCGv_i64 lval = tcg_temp_new_i64(); - TCGv_i64 rval = tcg_temp_new_i64(); - TCGv_i64 lsh = tcg_temp_new_i64(); - TCGv_i64 rsh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); - TCGv_i64 max = tcg_constant_i64(63); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i64(lsh, shift); - tcg_gen_neg_i64(rsh, lsh); - tcg_gen_shl_i64(lval, src, lsh); - tcg_gen_umin_i64(rsh, rsh, max); - tcg_gen_sar_i64(rval, src, rsh); - tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero); - tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval); -} - -static void gen_sshl_vec(unsigned vece, TCGv_vec dst, - TCGv_vec src, TCGv_vec shift) -{ - TCGv_vec lval = tcg_temp_new_vec_matching(dst); - TCGv_vec rval = tcg_temp_new_vec_matching(dst); - TCGv_vec lsh = tcg_temp_new_vec_matching(dst); - TCGv_vec rsh = tcg_temp_new_vec_matching(dst); - TCGv_vec tmp = tcg_temp_new_vec_matching(dst); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_neg_vec(vece, rsh, shift); - if (vece == MO_8) { - tcg_gen_mov_vec(lsh, shift); - } else { - tcg_gen_dupi_vec(vece, tmp, 0xff); - tcg_gen_and_vec(vece, lsh, shift, tmp); - tcg_gen_and_vec(vece, rsh, rsh, tmp); - } - - /* Bound rsh so out of bound right shift gets -1. */ - tcg_gen_dupi_vec(vece, tmp, (8 << vece) - 1); - tcg_gen_umin_vec(vece, rsh, rsh, tmp); - tcg_gen_cmp_vec(TCG_COND_GT, vece, tmp, lsh, tmp); - - tcg_gen_shlv_vec(vece, lval, src, lsh); - tcg_gen_sarv_vec(vece, rval, src, rsh); - - /* Select in-bound left shift. */ - tcg_gen_andc_vec(vece, lval, lval, tmp); - - /* Select between left and right shift. */ - if (vece == MO_8) { - tcg_gen_dupi_vec(vece, tmp, 0); - tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, rval, lval); - } else { - tcg_gen_dupi_vec(vece, tmp, 0x80); - tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, lval, rval); - } -} - -void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec, - INDEX_op_sarv_vec, INDEX_op_cmp_vec, INDEX_op_cmpsel_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_sshl_vec, - .fno = gen_helper_gvec_sshl_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_sshl_vec, - .fno = gen_helper_gvec_sshl_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_sshl_i32, - .fniv = gen_sshl_vec, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_sshl_i64, - .fniv = gen_sshl_vec, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_add_vec(vece, x, a, b); - tcg_gen_usadd_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); -} - -void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_usadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_b, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_h, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_s, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_d, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_add_vec(vece, x, a, b); - tcg_gen_ssadd_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); -} - -void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_ssadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_b, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_8 }, - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_h, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_16 }, - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_s, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_32 }, - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_d, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_sub_vec(vece, x, a, b); - tcg_gen_ussub_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); -} - -void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_ussub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_b, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_8 }, - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_h, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_16 }, - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_s, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_32 }, - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_d, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_sub_vec(vece, x, a, b); - tcg_gen_sssub_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); -} - -void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sssub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_b, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_8 }, - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_h, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_16 }, - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_s, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_32 }, - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_d, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - tcg_gen_sub_i32(t, a, b); - tcg_gen_sub_i32(d, b, a); - tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t); -} - -static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_sub_i64(t, a, b); - tcg_gen_sub_i64(d, b, a); - tcg_gen_movcond_i64(TCG_COND_LT, d, a, b, d, t); -} - -static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - tcg_gen_smin_vec(vece, t, a, b); - tcg_gen_smax_vec(vece, d, a, b); - tcg_gen_sub_vec(vece, d, d, t); -} - -void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_sabd_i32, - .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_sabd_i64, - .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - tcg_gen_sub_i32(t, a, b); - tcg_gen_sub_i32(d, b, a); - tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, d, t); -} - -static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_sub_i64(t, a, b); - tcg_gen_sub_i64(d, b, a); - tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, d, t); -} - -static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - tcg_gen_umin_vec(vece, t, a, b); - tcg_gen_umax_vec(vece, d, a, b); - tcg_gen_sub_vec(vece, d, d, t); -} - -void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_uabd_i32, - .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_uabd_i64, - .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_saba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 t = tcg_temp_new_i32(); - gen_sabd_i32(t, a, b); - tcg_gen_add_i32(d, d, t); -} - -static void gen_saba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 t = tcg_temp_new_i64(); - gen_sabd_i64(t, a, b); - tcg_gen_add_i64(d, d, t); -} - -static void gen_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - gen_sabd_vec(vece, t, a, b); - tcg_gen_add_vec(vece, d, d, t); -} - -void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_add_vec, - INDEX_op_smin_vec, INDEX_op_smax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_saba_i32, - .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_saba_i64, - .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uaba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 t = tcg_temp_new_i32(); - gen_uabd_i32(t, a, b); - tcg_gen_add_i32(d, d, t); -} - -static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 t = tcg_temp_new_i64(); - gen_uabd_i64(t, a, b); - tcg_gen_add_i64(d, d, t); -} - -static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - gen_uabd_vec(vece, t, a, b); - tcg_gen_add_vec(vece, d, d, t); -} - -void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_add_vec, - INDEX_op_umin_vec, INDEX_op_umax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_uaba_i32, - .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_uaba_i64, - .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - static bool aa32_cpreg_encoding_in_impdef_space(uint8_t crn, uint8_t crm) { static const uint16_t mask[3] = { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index dc66ff2190..80e85096a8 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -445,6 +445,11 @@ void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh); +void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh); +void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh); +void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh); + void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, int64_t shift, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, From a11efe30b9fc33ecc38255019d7ed7c750ec27ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:22 -0700 Subject: [PATCH 0976/2884] target/arm: Split out gengvec64.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split some routines out of translate-a64.c and translate-sve.c that are used by both. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec64.c | 190 +++++++++++++++++++++++++++++++++ target/arm/tcg/meson.build | 1 + target/arm/tcg/translate-a64.c | 26 ----- target/arm/tcg/translate-a64.h | 4 + target/arm/tcg/translate-sve.c | 145 +------------------------ 5 files changed, 197 insertions(+), 169 deletions(-) create mode 100644 target/arm/tcg/gengvec64.c diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c new file mode 100644 index 0000000000..093b498b13 --- /dev/null +++ b/target/arm/tcg/gengvec64.c @@ -0,0 +1,190 @@ +/* + * AArch64 generic vector expansion + * + * Copyright (c) 2013 Alexander Graf + * + * 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.1 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 . + */ + +#include "qemu/osdep.h" +#include "translate.h" +#include "translate-a64.h" + + +static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + tcg_gen_rotli_i64(d, m, 1); + tcg_gen_xor_i64(d, d, n); +} + +static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m) +{ + tcg_gen_rotli_vec(vece, d, m, 1); + tcg_gen_xor_vec(vece, d, d, n); +} + +void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_rotli_vec, 0 }; + static const GVecGen3 op = { + .fni8 = gen_rax1_i64, + .fniv = gen_rax1_vec, + .opt_opc = vecop_list, + .fno = gen_helper_crypto_rax1, + .vece = MO_64, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op); +} + +static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + uint64_t mask = dup_const(MO_8, 0xff >> sh); + + tcg_gen_xor_i64(t, n, m); + tcg_gen_shri_i64(d, t, sh); + tcg_gen_shli_i64(t, t, 8 - sh); + tcg_gen_andi_i64(d, d, mask); + tcg_gen_andi_i64(t, t, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + uint64_t mask = dup_const(MO_16, 0xffff >> sh); + + tcg_gen_xor_i64(t, n, m); + tcg_gen_shri_i64(d, t, sh); + tcg_gen_shli_i64(t, t, 16 - sh); + tcg_gen_andi_i64(d, d, mask); + tcg_gen_andi_i64(t, t, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_xar_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, int32_t sh) +{ + tcg_gen_xor_i32(d, n, m); + tcg_gen_rotri_i32(d, d, sh); +} + +static void gen_xar_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) +{ + tcg_gen_xor_i64(d, n, m); + tcg_gen_rotri_i64(d, d, sh); +} + +static void gen_xar_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, int64_t sh) +{ + tcg_gen_xor_vec(vece, d, n, m); + tcg_gen_rotri_vec(vece, d, d, sh); +} + +void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, int64_t shift, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop[] = { INDEX_op_rotli_vec, 0 }; + static const GVecGen3i ops[4] = { + { .fni8 = gen_xar8_i64, + .fniv = gen_xar_vec, + .fno = gen_helper_sve2_xar_b, + .opt_opc = vecop, + .vece = MO_8 }, + { .fni8 = gen_xar16_i64, + .fniv = gen_xar_vec, + .fno = gen_helper_sve2_xar_h, + .opt_opc = vecop, + .vece = MO_16 }, + { .fni4 = gen_xar_i32, + .fniv = gen_xar_vec, + .fno = gen_helper_sve2_xar_s, + .opt_opc = vecop, + .vece = MO_32 }, + { .fni8 = gen_xar_i64, + .fniv = gen_xar_vec, + .fno = gen_helper_gvec_xar_d, + .opt_opc = vecop, + .vece = MO_64 } + }; + int esize = 8 << vece; + + /* The SVE2 range is 1 .. esize; the AdvSIMD range is 0 .. esize-1. */ + tcg_debug_assert(shift >= 0); + tcg_debug_assert(shift <= esize); + shift &= esize - 1; + + if (shift == 0) { + /* xar with no rotate devolves to xor. */ + tcg_gen_gvec_xor(vece, rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz); + } else { + tcg_gen_gvec_3i(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, + shift, &ops[vece]); + } +} + +static void gen_eor3_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) +{ + tcg_gen_xor_i64(d, n, m); + tcg_gen_xor_i64(d, d, k); +} + +static void gen_eor3_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec k) +{ + tcg_gen_xor_vec(vece, d, n, m); + tcg_gen_xor_vec(vece, d, d, k); +} + +void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen4 op = { + .fni8 = gen_eor3_i64, + .fniv = gen_eor3_vec, + .fno = gen_helper_sve2_eor3, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); +} + +static void gen_bcax_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) +{ + tcg_gen_andc_i64(d, m, k); + tcg_gen_xor_i64(d, d, n); +} + +static void gen_bcax_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec k) +{ + tcg_gen_andc_vec(vece, d, m, k); + tcg_gen_xor_vec(vece, d, d, n); +} + +void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen4 op = { + .fni8 = gen_bcax_i64, + .fniv = gen_bcax_vec, + .fno = gen_helper_sve2_bcax, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); +} + diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index bdb5c7352f..508932a249 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -43,6 +43,7 @@ arm_ss.add(files( arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c', + 'gengvec64.c', 'translate-a64.c', 'translate-sve.c', 'translate-sme.c', diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0bdddb8517..8842ff634d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -13623,32 +13623,6 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn) gen_gvec_op2_ool(s, true, rd, rn, 0, genfn); } -static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) -{ - tcg_gen_rotli_i64(d, m, 1); - tcg_gen_xor_i64(d, d, n); -} - -static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m) -{ - tcg_gen_rotli_vec(vece, d, m, 1); - tcg_gen_xor_vec(vece, d, d, n); -} - -void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_rotli_vec, 0 }; - static const GVecGen3 op = { - .fni8 = gen_rax1_i64, - .fniv = gen_rax1_vec, - .opt_opc = vecop_list, - .fno = gen_helper_crypto_rax1, - .vece = MO_64, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op); -} - /* Crypto three-reg SHA512 * 31 21 20 16 15 14 13 12 11 10 9 5 4 0 * +-----------------------+------+---+---+-----+--------+------+------+ diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 7b811b8ac5..91750f0ca9 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -193,6 +193,10 @@ void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz); +void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz); void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index ada05aa530..798ab2bfb1 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -527,94 +527,6 @@ TRANS_FEAT(ORR_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_or, a) TRANS_FEAT(EOR_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_xor, a) TRANS_FEAT(BIC_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_andc, a) -static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - uint64_t mask = dup_const(MO_8, 0xff >> sh); - - tcg_gen_xor_i64(t, n, m); - tcg_gen_shri_i64(d, t, sh); - tcg_gen_shli_i64(t, t, 8 - sh); - tcg_gen_andi_i64(d, d, mask); - tcg_gen_andi_i64(t, t, ~mask); - tcg_gen_or_i64(d, d, t); -} - -static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - uint64_t mask = dup_const(MO_16, 0xffff >> sh); - - tcg_gen_xor_i64(t, n, m); - tcg_gen_shri_i64(d, t, sh); - tcg_gen_shli_i64(t, t, 16 - sh); - tcg_gen_andi_i64(d, d, mask); - tcg_gen_andi_i64(t, t, ~mask); - tcg_gen_or_i64(d, d, t); -} - -static void gen_xar_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, int32_t sh) -{ - tcg_gen_xor_i32(d, n, m); - tcg_gen_rotri_i32(d, d, sh); -} - -static void gen_xar_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) -{ - tcg_gen_xor_i64(d, n, m); - tcg_gen_rotri_i64(d, d, sh); -} - -static void gen_xar_vec(unsigned vece, TCGv_vec d, TCGv_vec n, - TCGv_vec m, int64_t sh) -{ - tcg_gen_xor_vec(vece, d, n, m); - tcg_gen_rotri_vec(vece, d, d, sh); -} - -void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, int64_t shift, - uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop[] = { INDEX_op_rotli_vec, 0 }; - static const GVecGen3i ops[4] = { - { .fni8 = gen_xar8_i64, - .fniv = gen_xar_vec, - .fno = gen_helper_sve2_xar_b, - .opt_opc = vecop, - .vece = MO_8 }, - { .fni8 = gen_xar16_i64, - .fniv = gen_xar_vec, - .fno = gen_helper_sve2_xar_h, - .opt_opc = vecop, - .vece = MO_16 }, - { .fni4 = gen_xar_i32, - .fniv = gen_xar_vec, - .fno = gen_helper_sve2_xar_s, - .opt_opc = vecop, - .vece = MO_32 }, - { .fni8 = gen_xar_i64, - .fniv = gen_xar_vec, - .fno = gen_helper_gvec_xar_d, - .opt_opc = vecop, - .vece = MO_64 } - }; - int esize = 8 << vece; - - /* The SVE2 range is 1 .. esize; the AdvSIMD range is 0 .. esize-1. */ - tcg_debug_assert(shift >= 0); - tcg_debug_assert(shift <= esize); - shift &= esize - 1; - - if (shift == 0) { - /* xar with no rotate devolves to xor. */ - tcg_gen_gvec_xor(vece, rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz); - } else { - tcg_gen_gvec_3i(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, - shift, &ops[vece]); - } -} - static bool trans_XAR(DisasContext *s, arg_rrri_esz *a) { if (a->esz < 0 || !dc_isar_feature(aa64_sve2, s)) { @@ -629,61 +541,8 @@ static bool trans_XAR(DisasContext *s, arg_rrri_esz *a) return true; } -static void gen_eor3_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) -{ - tcg_gen_xor_i64(d, n, m); - tcg_gen_xor_i64(d, d, k); -} - -static void gen_eor3_vec(unsigned vece, TCGv_vec d, TCGv_vec n, - TCGv_vec m, TCGv_vec k) -{ - tcg_gen_xor_vec(vece, d, n, m); - tcg_gen_xor_vec(vece, d, d, k); -} - -static void gen_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, - uint32_t a, uint32_t oprsz, uint32_t maxsz) -{ - static const GVecGen4 op = { - .fni8 = gen_eor3_i64, - .fniv = gen_eor3_vec, - .fno = gen_helper_sve2_eor3, - .vece = MO_64, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - }; - tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); -} - -TRANS_FEAT(EOR3, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_eor3, a) - -static void gen_bcax_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) -{ - tcg_gen_andc_i64(d, m, k); - tcg_gen_xor_i64(d, d, n); -} - -static void gen_bcax_vec(unsigned vece, TCGv_vec d, TCGv_vec n, - TCGv_vec m, TCGv_vec k) -{ - tcg_gen_andc_vec(vece, d, m, k); - tcg_gen_xor_vec(vece, d, d, n); -} - -static void gen_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, - uint32_t a, uint32_t oprsz, uint32_t maxsz) -{ - static const GVecGen4 op = { - .fni8 = gen_bcax_i64, - .fniv = gen_bcax_vec, - .fno = gen_helper_sve2_bcax, - .vece = MO_64, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - }; - tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); -} - -TRANS_FEAT(BCAX, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_bcax, a) +TRANS_FEAT(EOR3, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_gvec_eor3, a) +TRANS_FEAT(BCAX, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_gvec_bcax, a) static void gen_bsl(unsigned vece, uint32_t d, uint32_t n, uint32_t m, uint32_t a, uint32_t oprsz, uint32_t maxsz) From 8424801eb583d1d27b343062dc70a0f61aadc213 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:23 -0700 Subject: [PATCH 0977/2884] target/arm: Convert Cryptographic AES to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 21 +++++++-- target/arm/tcg/translate-a64.c | 86 +++++++++++++++------------------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 0e7656fd15..1de09903dc 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -19,11 +19,17 @@ # This file is processed by scripts/decodetree.py # -&r rn -&ri rd imm -&rri_sf rd rn imm sf -&i imm +%rd 0:5 +&r rn +&ri rd imm +&rri_sf rd rn imm sf +&i imm +&qrr_e q rd rn esz +&qrrr_e q rd rn rm esz + +@rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0 +@r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0 ### Data Processing - Immediate @@ -590,3 +596,10 @@ CPYFE 00 011 0 01100 ..... .... 01 ..... ..... @cpy CPYP 00 011 1 01000 ..... .... 01 ..... ..... @cpy CPYM 00 011 1 01010 ..... .... 01 ..... ..... @cpy CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy + +### Cryptographic AES + +AESE 01001110 00 10100 00100 10 ..... ..... @r2r_q1e0 +AESD 01001110 00 10100 00101 10 ..... ..... @r2r_q1e0 +AESMC 01001110 00 10100 00110 10 ..... ..... @rr_q1e0 +AESIMC 01001110 00 10100 00111 10 ..... ..... @rr_q1e0 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8842ff634d..3894db4bee 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1313,6 +1313,34 @@ bool sme_enabled_check_with_svcr(DisasContext *s, unsigned req) return true; } +/* + * Expanders for AdvSIMD translation functions. + */ + +static bool do_gvec_op2_ool(DisasContext *s, arg_qrr_e *a, int data, + gen_helper_gvec_2 *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_op2_ool(s, a->q, a->rd, a->rn, data, fn); + } + return true; +} + +static bool do_gvec_op3_ool(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3 *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_op3_ool(s, a->q, a->rd, a->rn, a->rm, data, fn); + } + return true; +} + /* * This utility function is for doing register extension with an * optional shift. You will likely want to pass a temporary for the @@ -4560,6 +4588,15 @@ static bool trans_EXTR(DisasContext *s, arg_extract *a) return true; } +/* + * Cryptographic AES + */ + +TRANS_FEAT(AESE, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aese) +TRANS_FEAT(AESD, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aesd) +TRANS_FEAT(AESMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesmc) +TRANS_FEAT(AESIMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesimc) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -13460,54 +13497,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto AES - * 31 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----------------+------+-----------+--------+-----+------+------+ - * | 0 1 0 0 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd | - * +-----------------+------+-----------+--------+-----+------+------+ - */ -static void disas_crypto_aes(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - gen_helper_gvec_2 *genfn2 = NULL; - gen_helper_gvec_3 *genfn3 = NULL; - - if (!dc_isar_feature(aa64_aes, s) || size != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0x4: /* AESE */ - genfn3 = gen_helper_crypto_aese; - break; - case 0x6: /* AESMC */ - genfn2 = gen_helper_crypto_aesmc; - break; - case 0x5: /* AESD */ - genfn3 = gen_helper_crypto_aesd; - break; - case 0x7: /* AESIMC */ - genfn2 = gen_helper_crypto_aesimc; - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - if (genfn2) { - gen_gvec_op2_ool(s, true, rd, rn, 0, genfn2); - } else { - gen_gvec_op3_ool(s, true, rd, rd, rn, 0, genfn3); - } -} - /* Crypto three-reg SHA * 31 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0 * +-----------------+------+---+------+---+--------+-----+------+------+ @@ -13917,7 +13906,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0x4e280800, 0xff3e0c00, disas_crypto_aes }, { 0x5e000000, 0xff208c00, disas_crypto_three_reg_sha }, { 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha }, { 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 }, From c5fb9b4fad2a7c9577561264cb6364c546f66bb4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:24 -0700 Subject: [PATCH 0978/2884] target/arm: Convert Cryptographic 3-register SHA to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 11 +++++ target/arm/tcg/translate-a64.c | 78 +++++----------------------------- 2 files changed, 21 insertions(+), 68 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 1de09903dc..7590659ee6 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -30,6 +30,7 @@ @rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0 @r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0 +@rrr_q1e0 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=0 ### Data Processing - Immediate @@ -603,3 +604,13 @@ AESE 01001110 00 10100 00100 10 ..... ..... @r2r_q1e0 AESD 01001110 00 10100 00101 10 ..... ..... @r2r_q1e0 AESMC 01001110 00 10100 00110 10 ..... ..... @rr_q1e0 AESIMC 01001110 00 10100 00111 10 ..... ..... @rr_q1e0 + +### Cryptographic three-register SHA + +SHA1C 0101 1110 000 ..... 000000 ..... ..... @rrr_q1e0 +SHA1P 0101 1110 000 ..... 000100 ..... ..... @rrr_q1e0 +SHA1M 0101 1110 000 ..... 001000 ..... ..... @rrr_q1e0 +SHA1SU0 0101 1110 000 ..... 001100 ..... ..... @rrr_q1e0 +SHA256H 0101 1110 000 ..... 010000 ..... ..... @rrr_q1e0 +SHA256H2 0101 1110 000 ..... 010100 ..... ..... @rrr_q1e0 +SHA256SU1 0101 1110 000 ..... 011000 ..... ..... @rrr_q1e0 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3894db4bee..5bef39d4e7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4589,7 +4589,7 @@ static bool trans_EXTR(DisasContext *s, arg_extract *a) } /* - * Cryptographic AES + * Cryptographic AES, SHA */ TRANS_FEAT(AESE, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aese) @@ -4597,6 +4597,15 @@ TRANS_FEAT(AESD, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aesd) TRANS_FEAT(AESMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesmc) TRANS_FEAT(AESIMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesimc) +TRANS_FEAT(SHA1C, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1c) +TRANS_FEAT(SHA1P, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1p) +TRANS_FEAT(SHA1M, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1m) +TRANS_FEAT(SHA1SU0, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1su0) + +TRANS_FEAT(SHA256H, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h) +TRANS_FEAT(SHA256H2, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h2) +TRANS_FEAT(SHA256SU1, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256su1) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -13497,72 +13506,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto three-reg SHA - * 31 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0 - * +-----------------+------+---+------+---+--------+-----+------+------+ - * | 0 1 0 1 1 1 1 0 | size | 0 | Rm | 0 | opcode | 0 0 | Rn | Rd | - * +-----------------+------+---+------+---+--------+-----+------+------+ - */ -static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 3); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - gen_helper_gvec_3 *genfn; - bool feature; - - if (size != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0: /* SHA1C */ - genfn = gen_helper_crypto_sha1c; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 1: /* SHA1P */ - genfn = gen_helper_crypto_sha1p; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 2: /* SHA1M */ - genfn = gen_helper_crypto_sha1m; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 3: /* SHA1SU0 */ - genfn = gen_helper_crypto_sha1su0; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 4: /* SHA256H */ - genfn = gen_helper_crypto_sha256h; - feature = dc_isar_feature(aa64_sha256, s); - break; - case 5: /* SHA256H2 */ - genfn = gen_helper_crypto_sha256h2; - feature = dc_isar_feature(aa64_sha256, s); - break; - case 6: /* SHA256SU1 */ - genfn = gen_helper_crypto_sha256su1; - feature = dc_isar_feature(aa64_sha256, s); - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - gen_gvec_op3_ool(s, true, rd, rn, rm, 0, genfn); -} - /* Crypto two-reg SHA * 31 24 23 22 21 17 16 12 11 10 9 5 4 0 * +-----------------+------+-----------+--------+-----+------+------+ @@ -13906,7 +13849,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0x5e000000, 0xff208c00, disas_crypto_three_reg_sha }, { 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha }, { 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 }, { 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 }, From 66d1e1a402642cc6c9a0fda815add18e7164db78 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:25 -0700 Subject: [PATCH 0979/2884] target/arm: Convert Cryptographic 2-register SHA to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 6 ++++ target/arm/tcg/translate-a64.c | 54 +++------------------------------- 2 files changed, 10 insertions(+), 50 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7590659ee6..350afabc77 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -614,3 +614,9 @@ SHA1SU0 0101 1110 000 ..... 001100 ..... ..... @rrr_q1e0 SHA256H 0101 1110 000 ..... 010000 ..... ..... @rrr_q1e0 SHA256H2 0101 1110 000 ..... 010100 ..... ..... @rrr_q1e0 SHA256SU1 0101 1110 000 ..... 011000 ..... ..... @rrr_q1e0 + +### Cryptographic two-register SHA + +SHA1H 0101 1110 0010 1000 0000 10 ..... ..... @rr_q1e0 +SHA1SU1 0101 1110 0010 1000 0001 10 ..... ..... @rr_q1e0 +SHA256SU0 0101 1110 0010 1000 0010 10 ..... ..... @rr_q1e0 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5bef39d4e7..1d20bf0c35 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4606,6 +4606,10 @@ TRANS_FEAT(SHA256H, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256 TRANS_FEAT(SHA256H2, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h2) TRANS_FEAT(SHA256SU1, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256su1) +TRANS_FEAT(SHA1H, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1h) +TRANS_FEAT(SHA1SU1, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1su1) +TRANS_FEAT(SHA256SU0, aa64_sha256, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha256su0) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -13506,55 +13510,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto two-reg SHA - * 31 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----------------+------+-----------+--------+-----+------+------+ - * | 0 1 0 1 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd | - * +-----------------+------+-----------+--------+-----+------+------+ - */ -static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - gen_helper_gvec_2 *genfn; - bool feature; - - if (size != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0: /* SHA1H */ - feature = dc_isar_feature(aa64_sha1, s); - genfn = gen_helper_crypto_sha1h; - break; - case 1: /* SHA1SU1 */ - feature = dc_isar_feature(aa64_sha1, s); - genfn = gen_helper_crypto_sha1su1; - break; - case 2: /* SHA256SU0 */ - feature = dc_isar_feature(aa64_sha256, s); - genfn = gen_helper_crypto_sha256su0; - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - gen_gvec_op2_ool(s, true, rd, rn, 0, genfn); -} - /* Crypto three-reg SHA512 * 31 21 20 16 15 14 13 12 11 10 9 5 4 0 * +-----------------------+------+---+---+-----+--------+------+------+ @@ -13849,7 +13804,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha }, { 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 }, { 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 }, { 0xce000000, 0xff808000, disas_crypto_four_reg }, From 7010e36676bb369da14f1e8be9ed0885555f0c5c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:26 -0700 Subject: [PATCH 0980/2884] target/arm: Convert Cryptographic 3-register SHA512 to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 11 ++++ target/arm/tcg/translate-a64.c | 97 ++++++++-------------------------- 2 files changed, 32 insertions(+), 76 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 350afabc77..c342c27608 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -31,6 +31,7 @@ @rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0 @r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0 @rrr_q1e0 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=0 +@rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3 ### Data Processing - Immediate @@ -620,3 +621,13 @@ SHA256SU1 0101 1110 000 ..... 011000 ..... ..... @rrr_q1e0 SHA1H 0101 1110 0010 1000 0000 10 ..... ..... @rr_q1e0 SHA1SU1 0101 1110 0010 1000 0001 10 ..... ..... @rr_q1e0 SHA256SU0 0101 1110 0010 1000 0010 10 ..... ..... @rr_q1e0 + +### Cryptographic three-register SHA512 + +SHA512H 1100 1110 011 ..... 100000 ..... ..... @rrr_q1e0 +SHA512H2 1100 1110 011 ..... 100001 ..... ..... @rrr_q1e0 +SHA512SU1 1100 1110 011 ..... 100010 ..... ..... @rrr_q1e0 +RAX1 1100 1110 011 ..... 100011 ..... ..... @rrr_q1e3 +SM3PARTW1 1100 1110 011 ..... 110000 ..... ..... @rrr_q1e0 +SM3PARTW2 1100 1110 011 ..... 110001 ..... ..... @rrr_q1e0 +SM4EKEY 1100 1110 011 ..... 110010 ..... ..... @rrr_q1e0 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1d20bf0c35..77b24cd52e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1341,6 +1341,17 @@ static bool do_gvec_op3_ool(DisasContext *s, arg_qrrr_e *a, int data, return true; } +static bool do_gvec_fn3(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn3(s, a->q, a->rd, a->rn, a->rm, fn, a->esz); + } + return true; +} + /* * This utility function is for doing register extension with an * optional shift. You will likely want to pass a temporary for the @@ -4589,7 +4600,7 @@ static bool trans_EXTR(DisasContext *s, arg_extract *a) } /* - * Cryptographic AES, SHA + * Cryptographic AES, SHA, SHA512 */ TRANS_FEAT(AESE, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aese) @@ -4610,6 +4621,15 @@ TRANS_FEAT(SHA1H, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1h) TRANS_FEAT(SHA1SU1, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1su1) TRANS_FEAT(SHA256SU0, aa64_sha256, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha256su0) +TRANS_FEAT(SHA512H, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512h) +TRANS_FEAT(SHA512H2, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512h2) +TRANS_FEAT(SHA512SU1, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512su1) +TRANS_FEAT(RAX1, aa64_sha3, do_gvec_fn3, a, gen_gvec_rax1) +TRANS_FEAT(SM3PARTW1, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw1) +TRANS_FEAT(SM3PARTW2, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw2) +TRANS_FEAT(SM4EKEY, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4ekey) + + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -13510,80 +13530,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto three-reg SHA512 - * 31 21 20 16 15 14 13 12 11 10 9 5 4 0 - * +-----------------------+------+---+---+-----+--------+------+------+ - * | 1 1 0 0 1 1 1 0 0 1 1 | Rm | 1 | O | 0 0 | opcode | Rn | Rd | - * +-----------------------+------+---+---+-----+--------+------+------+ - */ -static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 10, 2); - int o = extract32(insn, 14, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; - gen_helper_gvec_3 *oolfn = NULL; - GVecGen3Fn *gvecfn = NULL; - - if (o == 0) { - switch (opcode) { - case 0: /* SHA512H */ - feature = dc_isar_feature(aa64_sha512, s); - oolfn = gen_helper_crypto_sha512h; - break; - case 1: /* SHA512H2 */ - feature = dc_isar_feature(aa64_sha512, s); - oolfn = gen_helper_crypto_sha512h2; - break; - case 2: /* SHA512SU1 */ - feature = dc_isar_feature(aa64_sha512, s); - oolfn = gen_helper_crypto_sha512su1; - break; - case 3: /* RAX1 */ - feature = dc_isar_feature(aa64_sha3, s); - gvecfn = gen_gvec_rax1; - break; - default: - g_assert_not_reached(); - } - } else { - switch (opcode) { - case 0: /* SM3PARTW1 */ - feature = dc_isar_feature(aa64_sm3, s); - oolfn = gen_helper_crypto_sm3partw1; - break; - case 1: /* SM3PARTW2 */ - feature = dc_isar_feature(aa64_sm3, s); - oolfn = gen_helper_crypto_sm3partw2; - break; - case 2: /* SM4EKEY */ - feature = dc_isar_feature(aa64_sm4, s); - oolfn = gen_helper_crypto_sm4ekey; - break; - default: - unallocated_encoding(s); - return; - } - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (oolfn) { - gen_gvec_op3_ool(s, true, rd, rn, rm, 0, oolfn); - } else { - gen_gvec_fn3(s, true, rd, rn, rm, gvecfn, MO_64); - } -} - /* Crypto two-reg SHA512 * 31 12 11 10 9 5 4 0 * +-----------------------------------------+--------+------+------+ @@ -13804,7 +13750,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 }, { 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 }, { 0xce000000, 0xff808000, disas_crypto_four_reg }, { 0xce800000, 0xffe00000, disas_crypto_xar }, From 54b8230107341d027a3b1ac1eeb6f55ad7feb70d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:27 -0700 Subject: [PATCH 0981/2884] target/arm: Convert Cryptographic 2-register SHA512 to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 ++++ target/arm/tcg/translate-a64.c | 50 ++-------------------------------- 2 files changed, 8 insertions(+), 47 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c342c27608..5a46205751 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -631,3 +631,8 @@ RAX1 1100 1110 011 ..... 100011 ..... ..... @rrr_q1e3 SM3PARTW1 1100 1110 011 ..... 110000 ..... ..... @rrr_q1e0 SM3PARTW2 1100 1110 011 ..... 110001 ..... ..... @rrr_q1e0 SM4EKEY 1100 1110 011 ..... 110010 ..... ..... @rrr_q1e0 + +### Cryptographic two-register SHA512 + +SHA512SU0 1100 1110 110 00000 100000 ..... ..... @rr_q1e0 +SM4E 1100 1110 110 00000 100001 ..... ..... @r2r_q1e0 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 77b24cd52e..eed0abe912 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4629,6 +4629,9 @@ TRANS_FEAT(SM3PARTW1, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3part TRANS_FEAT(SM3PARTW2, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw2) TRANS_FEAT(SM4EKEY, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4ekey) +TRANS_FEAT(SHA512SU0, aa64_sha512, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha512su0) +TRANS_FEAT(SM4E, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4e) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the @@ -13530,52 +13533,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto two-reg SHA512 - * 31 12 11 10 9 5 4 0 - * +-----------------------------------------+--------+------+------+ - * | 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 | opcode | Rn | Rd | - * +-----------------------------------------+--------+------+------+ - */ -static void disas_crypto_two_reg_sha512(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 10, 2); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; - - switch (opcode) { - case 0: /* SHA512SU0 */ - feature = dc_isar_feature(aa64_sha512, s); - break; - case 1: /* SM4E */ - feature = dc_isar_feature(aa64_sm4, s); - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0: /* SHA512SU0 */ - gen_gvec_op2_ool(s, true, rd, rn, 0, gen_helper_crypto_sha512su0); - break; - case 1: /* SM4E */ - gen_gvec_op3_ool(s, true, rd, rd, rn, 0, gen_helper_crypto_sm4e); - break; - default: - g_assert_not_reached(); - } -} - /* Crypto four-register * 31 23 22 21 20 16 15 14 10 9 5 4 0 * +-------------------+-----+------+---+------+------+------+ @@ -13750,7 +13707,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 }, { 0xce000000, 0xff808000, disas_crypto_four_reg }, { 0xce800000, 0xffe00000, disas_crypto_xar }, { 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 }, From 50941556ff062f3a41aab04ef449c2ed5f5a8331 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:28 -0700 Subject: [PATCH 0982/2884] target/arm: Convert Cryptographic 4-register to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 8 ++ target/arm/tcg/translate-a64.c | 132 +++++++++++---------------------- 2 files changed, 51 insertions(+), 89 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5a46205751..ef6902e86a 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -27,11 +27,13 @@ &i imm &qrr_e q rd rn esz &qrrr_e q rd rn rm esz +&qrrrr_e q rd rn rm ra esz @rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0 @r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0 @rrr_q1e0 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=0 @rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3 +@rrrr_q1e3 ........ ... rm:5 . ra:5 rn:5 rd:5 &qrrrr_e q=1 esz=3 ### Data Processing - Immediate @@ -636,3 +638,9 @@ SM4EKEY 1100 1110 011 ..... 110010 ..... ..... @rrr_q1e0 SHA512SU0 1100 1110 110 00000 100000 ..... ..... @rr_q1e0 SM4E 1100 1110 110 00000 100001 ..... ..... @r2r_q1e0 + +### Cryptographic four-register + +EOR3 1100 1110 000 ..... 0 ..... ..... ..... @rrrr_q1e3 +BCAX 1100 1110 001 ..... 0 ..... ..... ..... @rrrr_q1e3 +SM3SS1 1100 1110 010 ..... 0 ..... ..... ..... @rrrr_q1e3 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index eed0abe912..2951e7eb59 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1352,6 +1352,17 @@ static bool do_gvec_fn3(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) return true; } +static bool do_gvec_fn4(DisasContext *s, arg_qrrrr_e *a, GVecGen4Fn *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn4(s, a->q, a->rd, a->rn, a->rm, a->ra, fn, a->esz); + } + return true; +} + /* * This utility function is for doing register extension with an * optional shift. You will likely want to pass a temporary for the @@ -4632,6 +4643,38 @@ TRANS_FEAT(SM4EKEY, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4ekey) TRANS_FEAT(SHA512SU0, aa64_sha512, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha512su0) TRANS_FEAT(SM4E, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4e) +TRANS_FEAT(EOR3, aa64_sha3, do_gvec_fn4, a, gen_gvec_eor3) +TRANS_FEAT(BCAX, aa64_sha3, do_gvec_fn4, a, gen_gvec_bcax) + +static bool trans_SM3SS1(DisasContext *s, arg_SM3SS1 *a) +{ + if (!dc_isar_feature(aa64_sm3, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 tcg_op1 = tcg_temp_new_i32(); + TCGv_i32 tcg_op2 = tcg_temp_new_i32(); + TCGv_i32 tcg_op3 = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + unsigned vsz, dofs; + + read_vec_element_i32(s, tcg_op1, a->rn, 3, MO_32); + read_vec_element_i32(s, tcg_op2, a->rm, 3, MO_32); + read_vec_element_i32(s, tcg_op3, a->ra, 3, MO_32); + + tcg_gen_rotri_i32(tcg_res, tcg_op1, 20); + tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2); + tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3); + tcg_gen_rotri_i32(tcg_res, tcg_res, 25); + + /* Clear the whole register first, then store bits [127:96]. */ + vsz = vec_full_reg_size(s); + dofs = vec_full_reg_offset(s, a->rd); + tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0); + write_vec_element_i32(s, tcg_res, a->rd, 3, MO_32); + } + return true; +} /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the @@ -13533,94 +13576,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto four-register - * 31 23 22 21 20 16 15 14 10 9 5 4 0 - * +-------------------+-----+------+---+------+------+------+ - * | 1 1 0 0 1 1 1 0 0 | Op0 | Rm | 0 | Ra | Rn | Rd | - * +-------------------+-----+------+---+------+------+------+ - */ -static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) -{ - int op0 = extract32(insn, 21, 2); - int rm = extract32(insn, 16, 5); - int ra = extract32(insn, 10, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; - - switch (op0) { - case 0: /* EOR3 */ - case 1: /* BCAX */ - feature = dc_isar_feature(aa64_sha3, s); - break; - case 2: /* SM3SS1 */ - feature = dc_isar_feature(aa64_sm3, s); - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (op0 < 2) { - TCGv_i64 tcg_op1, tcg_op2, tcg_op3, tcg_res[2]; - int pass; - - tcg_op1 = tcg_temp_new_i64(); - tcg_op2 = tcg_temp_new_i64(); - tcg_op3 = tcg_temp_new_i64(); - tcg_res[0] = tcg_temp_new_i64(); - tcg_res[1] = tcg_temp_new_i64(); - - for (pass = 0; pass < 2; pass++) { - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - read_vec_element(s, tcg_op3, ra, pass, MO_64); - - if (op0 == 0) { - /* EOR3 */ - tcg_gen_xor_i64(tcg_res[pass], tcg_op2, tcg_op3); - } else { - /* BCAX */ - tcg_gen_andc_i64(tcg_res[pass], tcg_op2, tcg_op3); - } - tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1); - } - write_vec_element(s, tcg_res[0], rd, 0, MO_64); - write_vec_element(s, tcg_res[1], rd, 1, MO_64); - } else { - TCGv_i32 tcg_op1, tcg_op2, tcg_op3, tcg_res, tcg_zero; - - tcg_op1 = tcg_temp_new_i32(); - tcg_op2 = tcg_temp_new_i32(); - tcg_op3 = tcg_temp_new_i32(); - tcg_res = tcg_temp_new_i32(); - tcg_zero = tcg_constant_i32(0); - - read_vec_element_i32(s, tcg_op1, rn, 3, MO_32); - read_vec_element_i32(s, tcg_op2, rm, 3, MO_32); - read_vec_element_i32(s, tcg_op3, ra, 3, MO_32); - - tcg_gen_rotri_i32(tcg_res, tcg_op1, 20); - tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2); - tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3); - tcg_gen_rotri_i32(tcg_res, tcg_res, 25); - - write_vec_element_i32(s, tcg_zero, rd, 0, MO_32); - write_vec_element_i32(s, tcg_zero, rd, 1, MO_32); - write_vec_element_i32(s, tcg_zero, rd, 2, MO_32); - write_vec_element_i32(s, tcg_res, rd, 3, MO_32); - } -} - /* Crypto XAR * 31 21 20 16 15 10 9 5 4 0 * +-----------------------+------+--------+------+------+ @@ -13707,7 +13662,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0xce000000, 0xff808000, disas_crypto_four_reg }, { 0xce800000, 0xffe00000, disas_crypto_xar }, { 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 }, { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, From 376bb8a45dca7693b58831e3776038f43e450e0c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:29 -0700 Subject: [PATCH 0983/2884] target/arm: Convert Cryptographic 3-register, imm2 to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 ++++++++ target/arm/tcg/translate-a64.c | 43 ++++++++++------------------------ 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index ef6902e86a..1292312a7f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -644,3 +644,13 @@ SM4E 1100 1110 110 00000 100001 ..... ..... @r2r_q1e0 EOR3 1100 1110 000 ..... 0 ..... ..... ..... @rrrr_q1e3 BCAX 1100 1110 001 ..... 0 ..... ..... ..... @rrrr_q1e3 SM3SS1 1100 1110 010 ..... 0 ..... ..... ..... @rrrr_q1e3 + +### Cryptographic three-register, imm2 + +&crypto3i rd rn rm imm +@crypto3i ........ ... rm:5 .. imm:2 .. rn:5 rd:5 &crypto3i + +SM3TT1A 11001110 010 ..... 10 .. 00 ..... ..... @crypto3i +SM3TT1B 11001110 010 ..... 10 .. 01 ..... ..... @crypto3i +SM3TT2A 11001110 010 ..... 10 .. 10 ..... ..... @crypto3i +SM3TT2B 11001110 010 ..... 10 .. 11 ..... ..... @crypto3i diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2951e7eb59..cf3a7dfa99 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4676,6 +4676,18 @@ static bool trans_SM3SS1(DisasContext *s, arg_SM3SS1 *a) return true; } +static bool do_crypto3i(DisasContext *s, arg_crypto3i *a, gen_helper_gvec_3 *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op3_ool(s, true, a->rd, a->rn, a->rm, a->imm, fn); + } + return true; +} +TRANS_FEAT(SM3TT1A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1a) +TRANS_FEAT(SM3TT1B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1b) +TRANS_FEAT(SM3TT2A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2a) +TRANS_FEAT(SM3TT2B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2b) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -13604,36 +13616,6 @@ static void disas_crypto_xar(DisasContext *s, uint32_t insn) vec_full_reg_size(s)); } -/* Crypto three-reg imm2 - * 31 21 20 16 15 14 13 12 11 10 9 5 4 0 - * +-----------------------+------+-----+------+--------+------+------+ - * | 1 1 0 0 1 1 1 0 0 1 0 | Rm | 1 0 | imm2 | opcode | Rn | Rd | - * +-----------------------+------+-----+------+--------+------+------+ - */ -static void disas_crypto_three_reg_imm2(DisasContext *s, uint32_t insn) -{ - static gen_helper_gvec_3 * const fns[4] = { - gen_helper_crypto_sm3tt1a, gen_helper_crypto_sm3tt1b, - gen_helper_crypto_sm3tt2a, gen_helper_crypto_sm3tt2b, - }; - int opcode = extract32(insn, 10, 2); - int imm2 = extract32(insn, 12, 2); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (!dc_isar_feature(aa64_sm3, s)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - gen_gvec_op3_ool(s, true, rd, rn, rm, imm2, fns[opcode]); -} - /* C3.6 Data processing - SIMD, inc Crypto * * As the decode gets a little complex we are using a table based @@ -13663,7 +13645,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, { 0xce800000, 0xffe00000, disas_crypto_xar }, - { 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 }, { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x5e400400, 0xdf60c400, disas_simd_scalar_three_reg_same_fp16 }, From d90a473363c722b209b201e6cd3484b2d1819201 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:30 -0700 Subject: [PATCH 0984/2884] target/arm: Convert XAR to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 43 +++++++++++----------------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 1292312a7f..7f354af25d 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -654,3 +654,7 @@ SM3TT1A 11001110 010 ..... 10 .. 00 ..... ..... @crypto3i SM3TT1B 11001110 010 ..... 10 .. 01 ..... ..... @crypto3i SM3TT2A 11001110 010 ..... 10 .. 10 ..... ..... @crypto3i SM3TT2B 11001110 010 ..... 10 .. 11 ..... ..... @crypto3i + +### Cryptographic XAR + +XAR 1100 1110 100 rm:5 imm:6 rn:5 rd:5 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index cf3a7dfa99..75f1e6a7b9 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4688,6 +4688,20 @@ TRANS_FEAT(SM3TT1B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1b) TRANS_FEAT(SM3TT2A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2a) TRANS_FEAT(SM3TT2B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2b) +static bool trans_XAR(DisasContext *s, arg_XAR *a) +{ + if (!dc_isar_feature(aa64_sha3, s)) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_xar(MO_64, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), a->imm, 16, + vec_full_reg_size(s)); + } + return true; +} + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -13588,34 +13602,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } } -/* Crypto XAR - * 31 21 20 16 15 10 9 5 4 0 - * +-----------------------+------+--------+------+------+ - * | 1 1 0 0 1 1 1 0 1 0 0 | Rm | imm6 | Rn | Rd | - * +-----------------------+------+--------+------+------+ - */ -static void disas_crypto_xar(DisasContext *s, uint32_t insn) -{ - int rm = extract32(insn, 16, 5); - int imm6 = extract32(insn, 10, 6); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (!dc_isar_feature(aa64_sha3, s)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - gen_gvec_xar(MO_64, vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), imm6, 16, - vec_full_reg_size(s)); -} - /* C3.6 Data processing - SIMD, inc Crypto * * As the decode gets a little complex we are using a table based @@ -13644,7 +13630,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0xce800000, 0xffe00000, disas_crypto_xar }, { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x5e400400, 0xdf60c400, disas_simd_scalar_three_reg_same_fp16 }, From d6edf915c73d447a626d527fb75f43c60ebe6147 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:31 -0700 Subject: [PATCH 0985/2884] target/arm: Convert Advanced SIMD copy to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 13 + target/arm/tcg/translate-a64.c | 426 +++++++++++---------------------- 2 files changed, 152 insertions(+), 287 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7f354af25d..d5bfeae7a8 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -658,3 +658,16 @@ SM3TT2B 11001110 010 ..... 10 .. 11 ..... ..... @crypto3i ### Cryptographic XAR XAR 1100 1110 100 rm:5 imm:6 rn:5 rd:5 + +### Advanced SIMD scalar copy + +DUP_element_s 0101 1110 000 imm:5 0 0000 1 rn:5 rd:5 + +### Advanced SIMD copy + +DUP_element_v 0 q:1 00 1110 000 imm:5 0 0000 1 rn:5 rd:5 +DUP_general 0 q:1 00 1110 000 imm:5 0 0001 1 rn:5 rd:5 +INS_general 0 1 00 1110 000 imm:5 0 0011 1 rn:5 rd:5 +SMOV 0 q:1 00 1110 000 imm:5 0 0101 1 rn:5 rd:5 +UMOV 0 q:1 00 1110 000 imm:5 0 0111 1 rn:5 rd:5 +INS_element 0 1 10 1110 000 di:5 0 si:4 1 rn:5 rd:5 diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 75f1e6a7b9..1a12bf22fd 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4702,6 +4702,145 @@ static bool trans_XAR(DisasContext *s, arg_XAR *a) return true; } +/* + * Advanced SIMD copy + */ + +static bool decode_esz_idx(int imm, MemOp *pesz, unsigned *pidx) +{ + unsigned esz = ctz32(imm); + if (esz <= MO_64) { + *pesz = esz; + *pidx = imm >> (esz + 1); + return true; + } + return false; +} + +static bool trans_DUP_element_s(DisasContext *s, arg_DUP_element_s *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (fp_access_check(s)) { + /* + * This instruction just extracts the specified element and + * zero-extends it into the bottom of the destination register. + */ + TCGv_i64 tmp = tcg_temp_new_i64(); + read_vec_element(s, tmp, a->rn, idx, esz); + write_fp_dreg(s, a->rd, tmp); + } + return true; +} + +static bool trans_DUP_element_v(DisasContext *s, arg_DUP_element_v *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (esz == MO_64 && !a->q) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_dup_mem(esz, vec_full_reg_offset(s, a->rd), + vec_reg_offset(s, a->rn, idx, esz), + a->q ? 16 : 8, vec_full_reg_size(s)); + } + return true; +} + +static bool trans_DUP_general(DisasContext *s, arg_DUP_general *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (esz == MO_64 && !a->q) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), + a->q ? 16 : 8, vec_full_reg_size(s), + cpu_reg(s, a->rn)); + } + return true; +} + +static bool do_smov_umov(DisasContext *s, arg_SMOV *a, MemOp is_signed) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (is_signed) { + if (esz == MO_64 || (esz == MO_32 && !a->q)) { + return false; + } + } else { + if (esz == MO_64 ? !a->q : a->q) { + return false; + } + } + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + read_vec_element(s, tcg_rd, a->rn, idx, esz | is_signed); + if (is_signed && !a->q) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } + return true; +} + +TRANS(SMOV, do_smov_umov, a, MO_SIGN) +TRANS(UMOV, do_smov_umov, a, 0) + +static bool trans_INS_general(DisasContext *s, arg_INS_general *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (fp_access_check(s)) { + write_vec_element(s, cpu_reg(s, a->rn), a->rd, idx, esz); + clear_vec_high(s, true, a->rd); + } + return true; +} + +static bool trans_INS_element(DisasContext *s, arg_INS_element *a) +{ + MemOp esz; + unsigned didx, sidx; + + if (!decode_esz_idx(a->di, &esz, &didx)) { + return false; + } + sidx = a->si >> esz; + if (fp_access_check(s)) { + TCGv_i64 tmp = tcg_temp_new_i64(); + + read_vec_element(s, tmp, a->rn, sidx, esz); + write_vec_element(s, tmp, a->rd, didx, esz); + + /* INS is considered a 128-bit write for SVE. */ + clear_vec_high(s, true, a->rd); + } + return true; +} + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -7760,268 +7899,6 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) write_fp_dreg(s, rd, tcg_res); } -/* DUP (Element, Vector) - * - * 31 30 29 21 20 16 15 10 9 5 4 0 - * +---+---+-------------------+--------+-------------+------+------+ - * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd | - * +---+---+-------------------+--------+-------------+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - */ -static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn, - int imm5) -{ - int size = ctz32(imm5); - int index; - - if (size > 3 || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - index = imm5 >> (size + 1); - tcg_gen_gvec_dup_mem(size, vec_full_reg_offset(s, rd), - vec_reg_offset(s, rn, index, size), - is_q ? 16 : 8, vec_full_reg_size(s)); -} - -/* DUP (element, scalar) - * 31 21 20 16 15 10 9 5 4 0 - * +-----------------------+--------+-------------+------+------+ - * | 0 1 0 1 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd | - * +-----------------------+--------+-------------+------+------+ - */ -static void handle_simd_dupes(DisasContext *s, int rd, int rn, - int imm5) -{ - int size = ctz32(imm5); - int index; - TCGv_i64 tmp; - - if (size > 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - index = imm5 >> (size + 1); - - /* This instruction just extracts the specified element and - * zero-extends it into the bottom of the destination register. - */ - tmp = tcg_temp_new_i64(); - read_vec_element(s, tmp, rn, index, size); - write_fp_dreg(s, rd, tmp); -} - -/* DUP (General) - * - * 31 30 29 21 20 16 15 10 9 5 4 0 - * +---+---+-------------------+--------+-------------+------+------+ - * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 1 1 | Rn | Rd | - * +---+---+-------------------+--------+-------------+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - */ -static void handle_simd_dupg(DisasContext *s, int is_q, int rd, int rn, - int imm5) -{ - int size = ctz32(imm5); - uint32_t dofs, oprsz, maxsz; - - if (size > 3 || ((size == 3) && !is_q)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - dofs = vec_full_reg_offset(s, rd); - oprsz = is_q ? 16 : 8; - maxsz = vec_full_reg_size(s); - - tcg_gen_gvec_dup_i64(size, dofs, oprsz, maxsz, cpu_reg(s, rn)); -} - -/* INS (Element) - * - * 31 21 20 16 15 14 11 10 9 5 4 0 - * +-----------------------+--------+------------+---+------+------+ - * | 0 1 1 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd | - * +-----------------------+--------+------------+---+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - * index: encoded in imm5<4:size+1> - */ -static void handle_simd_inse(DisasContext *s, int rd, int rn, - int imm4, int imm5) -{ - int size = ctz32(imm5); - int src_index, dst_index; - TCGv_i64 tmp; - - if (size > 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - dst_index = extract32(imm5, 1+size, 5); - src_index = extract32(imm4, size, 4); - - tmp = tcg_temp_new_i64(); - - read_vec_element(s, tmp, rn, src_index, size); - write_vec_element(s, tmp, rd, dst_index, size); - - /* INS is considered a 128-bit write for SVE. */ - clear_vec_high(s, true, rd); -} - - -/* INS (General) - * - * 31 21 20 16 15 10 9 5 4 0 - * +-----------------------+--------+-------------+------+------+ - * | 0 1 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 1 1 1 | Rn | Rd | - * +-----------------------+--------+-------------+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - * index: encoded in imm5<4:size+1> - */ -static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5) -{ - int size = ctz32(imm5); - int idx; - - if (size > 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - idx = extract32(imm5, 1 + size, 4 - size); - write_vec_element(s, cpu_reg(s, rn), rd, idx, size); - - /* INS is considered a 128-bit write for SVE. */ - clear_vec_high(s, true, rd); -} - -/* - * UMOV (General) - * SMOV (General) - * - * 31 30 29 21 20 16 15 12 10 9 5 4 0 - * +---+---+-------------------+--------+-------------+------+------+ - * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 1 U 1 1 | Rn | Rd | - * +---+---+-------------------+--------+-------------+------+------+ - * - * U: unsigned when set - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - */ -static void handle_simd_umov_smov(DisasContext *s, int is_q, int is_signed, - int rn, int rd, int imm5) -{ - int size = ctz32(imm5); - int element; - TCGv_i64 tcg_rd; - - /* Check for UnallocatedEncodings */ - if (is_signed) { - if (size > 2 || (size == 2 && !is_q)) { - unallocated_encoding(s); - return; - } - } else { - if (size > 3 - || (size < 3 && is_q) - || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - } - - if (!fp_access_check(s)) { - return; - } - - element = extract32(imm5, 1+size, 4); - - tcg_rd = cpu_reg(s, rd); - read_vec_element(s, tcg_rd, rn, element, size | (is_signed ? MO_SIGN : 0)); - if (is_signed && !is_q) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } -} - -/* AdvSIMD copy - * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0 - * +---+---+----+-----------------+------+---+------+---+------+------+ - * | 0 | Q | op | 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd | - * +---+---+----+-----------------+------+---+------+---+------+------+ - */ -static void disas_simd_copy(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm4 = extract32(insn, 11, 4); - int op = extract32(insn, 29, 1); - int is_q = extract32(insn, 30, 1); - int imm5 = extract32(insn, 16, 5); - - if (op) { - if (is_q) { - /* INS (element) */ - handle_simd_inse(s, rd, rn, imm4, imm5); - } else { - unallocated_encoding(s); - } - } else { - switch (imm4) { - case 0: - /* DUP (element - vector) */ - handle_simd_dupe(s, is_q, rd, rn, imm5); - break; - case 1: - /* DUP (general) */ - handle_simd_dupg(s, is_q, rd, rn, imm5); - break; - case 3: - if (is_q) { - /* INS (general) */ - handle_simd_insg(s, rd, rn, imm5); - } else { - unallocated_encoding(s); - } - break; - case 5: - case 7: - /* UMOV/SMOV (is_q indicates 32/64; imm4 indicates signedness) */ - handle_simd_umov_smov(s, is_q, (imm4 == 5), rn, rd, imm5); - break; - default: - unallocated_encoding(s); - break; - } - } -} - /* AdvSIMD modified immediate * 31 30 29 28 19 18 16 15 12 11 10 9 5 4 0 * +---+---+----+---------------------+-----+-------+----+---+-------+------+ @@ -8085,29 +7962,6 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) } } -/* AdvSIMD scalar copy - * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0 - * +-----+----+-----------------+------+---+------+---+------+------+ - * | 0 1 | op | 1 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd | - * +-----+----+-----------------+------+---+------+---+------+------+ - */ -static void disas_simd_scalar_copy(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm4 = extract32(insn, 11, 4); - int imm5 = extract32(insn, 16, 5); - int op = extract32(insn, 29, 1); - - if (op != 0 || imm4 != 0) { - unallocated_encoding(s); - return; - } - - /* DUP (element, scalar) */ - handle_simd_dupes(s, rd, rn, imm5); -} - /* AdvSIMD scalar pairwise * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 * +-----+---+-----------+------+-----------+--------+-----+------+------+ @@ -13614,7 +13468,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes }, - { 0x0e000400, 0x9fe08400, disas_simd_copy }, { 0x0f000000, 0x9f000400, disas_simd_indexed }, /* vector indexed */ /* simd_mod_imm decode is a subset of simd_shift_imm, so must precede it */ { 0x0f000400, 0x9ff80400, disas_simd_mod_imm }, @@ -13627,7 +13480,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise }, - { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, From cb1c77feef6578ffb7bffeed69c4d9b5f01abf03 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:32 -0700 Subject: [PATCH 0986/2884] target/arm: Convert FMULX to decodetree Convert all forms (scalar, vector, scalar indexed, vector indexed), which allows us to remove switch table entries elsewhere. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 45 +++++++ target/arm/tcg/helper-a64.h | 8 ++ target/arm/tcg/translate-a64.c | 221 +++++++++++++++++++++++++++------ target/arm/tcg/vec_helper.c | 39 +++--- 4 files changed, 259 insertions(+), 54 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d5bfeae7a8..2e0e01be01 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -20,21 +20,44 @@ # %rd 0:5 +%esz_sd 22:1 !function=plus_2 +%hl 11:1 21:1 +%hlm 11:1 20:2 &r rn &ri rd imm &rri_sf rd rn imm sf &i imm +&rrr_e rd rn rm esz +&rrx_e rd rn rm idx esz &qrr_e q rd rn esz &qrrr_e q rd rn rm esz +&qrrx_e q rd rn rm idx esz &qrrrr_e q rd rn rm ra esz +@rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 +@rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd + +@rrx_h ........ .. .. rm:4 .... . . rn:5 rd:5 &rrx_e esz=1 idx=%hlm +@rrx_s ........ .. . rm:5 .... . . rn:5 rd:5 &rrx_e esz=2 idx=%hl +@rrx_d ........ .. . rm:5 .... idx:1 . rn:5 rd:5 &rrx_e esz=3 + @rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0 @r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0 @rrr_q1e0 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=0 @rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3 @rrrr_q1e3 ........ ... rm:5 . ra:5 rn:5 rd:5 &qrrrr_e q=1 esz=3 +@qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1 +@qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd + +@qrrx_h . q:1 .. .... .. .. rm:4 .... . . rn:5 rd:5 \ + &qrrx_e esz=1 idx=%hlm +@qrrx_s . q:1 .. .... .. . rm:5 .... . . rn:5 rd:5 \ + &qrrx_e esz=2 idx=%hl +@qrrx_d . q:1 .. .... .. . rm:5 .... idx:1 . rn:5 rd:5 \ + &qrrx_e esz=3 + ### Data Processing - Immediate # PC-rel addressing @@ -671,3 +694,25 @@ INS_general 0 1 00 1110 000 imm:5 0 0011 1 rn:5 rd:5 SMOV 0 q:1 00 1110 000 imm:5 0 0101 1 rn:5 rd:5 UMOV 0 q:1 00 1110 000 imm:5 0 0111 1 rn:5 rd:5 INS_element 0 1 10 1110 000 di:5 0 si:4 1 rn:5 rd:5 + +### Advanced SIMD scalar three same + +FMULX_s 0101 1110 010 ..... 00011 1 ..... ..... @rrr_h +FMULX_s 0101 1110 0.1 ..... 11011 1 ..... ..... @rrr_sd + +### Advanced SIMD three same + +FMULX_v 0.00 1110 010 ..... 00011 1 ..... ..... @qrrr_h +FMULX_v 0.00 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd + +### Advanced SIMD scalar x indexed element + +FMULX_si 0111 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h +FMULX_si 0111 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s +FMULX_si 0111 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d + +### Advanced SIMD vector x indexed element + +FMULX_vi 0.10 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h +FMULX_vi 0.10 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s +FMULX_vi 0.10 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 0518165399..b79751a717 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -132,3 +132,11 @@ DEF_HELPER_4(cpye, void, env, i32, i32, i32) DEF_HELPER_4(cpyfp, void, env, i32, i32, i32) DEF_HELPER_4(cpyfm, void, env, i32, i32, i32) DEF_HELPER_4(cpyfe, void, env, i32, i32, i32) + +DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1a12bf22fd..8cbe6cd70f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4841,6 +4841,178 @@ static bool trans_INS_element(DisasContext *s, arg_INS_element *a) return true; } +/* + * Advanced SIMD three same + */ + +typedef struct FPScalar { + void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_s)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); +} FPScalar; + +static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = read_fp_dreg(s, a->rm); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rn); + TCGv_i32 t1 = read_fp_sreg(s, a->rm); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rn); + TCGv_i32 t1 = read_fp_hreg(s, a->rm); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + return false; + } + return true; +} + +static const FPScalar f_scalar_fmulx = { + gen_helper_advsimd_mulxh, + gen_helper_vfp_mulxs, + gen_helper_vfp_mulxd, +}; +TRANS(FMULX_s, do_fp3_scalar, a, &f_scalar_fmulx) + +static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, + gen_helper_gvec_3_ptr * const fns[3]) +{ + MemOp esz = a->esz; + + switch (esz) { + case MO_64: + if (!a->q) { + return false; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + return false; + } + if (fp_access_check(s)) { + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16, 0, fns[esz - 1]); + } + return true; +} + +static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = { + gen_helper_gvec_fmulx_h, + gen_helper_gvec_fmulx_s, + gen_helper_gvec_fmulx_d, +}; +TRANS(FMULX_v, do_fp3_vector, a, f_vector_fmulx) + +/* + * Advanced SIMD scalar/vector x indexed element + */ + +static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t1, a->rm, a->idx, MO_64); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rn); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t1, a->rm, a->idx, MO_32); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rn); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t1, a->rm, a->idx, MO_16); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + g_assert_not_reached(); + } + return true; +} + +TRANS(FMULX_si, do_fp3_scalar_idx, a, &f_scalar_fmulx) + +static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_3_ptr * const fns[3]) +{ + MemOp esz = a->esz; + + switch (esz) { + case MO_64: + if (!a->q) { + return false; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + g_assert_not_reached(); + } + if (fp_access_check(s)) { + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16, a->idx, fns[esz - 1]); + } + return true; +} + +static gen_helper_gvec_3_ptr * const f_vector_idx_fmulx[3] = { + gen_helper_gvec_fmulx_idx_h, + gen_helper_gvec_fmulx_idx_s, + gen_helper_gvec_fmulx_idx_d, +}; +TRANS(FMULX_vi, do_fp3_vector_idx, a, f_vector_idx_fmulx) + + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -9011,9 +9183,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x1a: /* FADD */ gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1b: /* FMULX */ - gen_helper_vfp_mulxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9058,6 +9227,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_neon_acgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x1b: /* FMULX */ g_assert_not_reached(); } @@ -9084,9 +9254,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x1a: /* FADD */ gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1b: /* FMULX */ - gen_helper_vfp_mulxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9134,6 +9301,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_neon_acgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x1b: /* FMULX */ g_assert_not_reached(); } @@ -9172,7 +9340,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) /* Floating point: U, size[1] and opcode indicate operation */ int fpopcode = opcode | (extract32(size, 1, 1) << 5) | (u << 6); switch (fpopcode) { - case 0x1b: /* FMULX */ case 0x1f: /* FRECPS */ case 0x3f: /* FRSQRTS */ case 0x5d: /* FACGE */ @@ -9183,6 +9350,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x7a: /* FABD */ break; default: + case 0x1b: /* FMULX */ unallocated_encoding(s); return; } @@ -9335,7 +9503,6 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, TCGv_i32 tcg_res; switch (fpopcode) { - case 0x03: /* FMULX */ case 0x04: /* FCMEQ (reg) */ case 0x07: /* FRECPS */ case 0x0f: /* FRSQRTS */ @@ -9346,6 +9513,7 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, case 0x1d: /* FACGT */ break; default: + case 0x03: /* FMULX */ unallocated_encoding(s); return; } @@ -9365,9 +9533,6 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, tcg_res = tcg_temp_new_i32(); switch (fpopcode) { - case 0x03: /* FMULX */ - gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x04: /* FCMEQ (reg) */ gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9394,6 +9559,7 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x03: /* FMULX */ g_assert_not_reached(); } @@ -11051,7 +11217,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) handle_simd_3same_pair(s, is_q, 0, fpopcode, size ? MO_64 : MO_32, rn, rm, rd); return; - case 0x1b: /* FMULX */ case 0x1f: /* FRECPS */ case 0x3f: /* FRSQRTS */ case 0x5d: /* FACGE */ @@ -11097,6 +11262,7 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) return; default: + case 0x1b: /* FMULX */ unallocated_encoding(s); return; } @@ -11441,7 +11607,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x0: /* FMAXNM */ case 0x1: /* FMLA */ case 0x2: /* FADD */ - case 0x3: /* FMULX */ case 0x4: /* FCMEQ */ case 0x6: /* FMAX */ case 0x7: /* FRECPS */ @@ -11467,6 +11632,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) pairwise = true; break; default: + case 0x3: /* FMULX */ unallocated_encoding(s); return; } @@ -11543,9 +11709,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x2: /* FADD */ gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x3: /* FMULX */ - gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x4: /* FCMEQ */ gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -11597,6 +11760,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x3: /* FMULX */ g_assert_not_reached(); } @@ -12816,7 +12980,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x01: /* FMLA */ case 0x05: /* FMLS */ case 0x09: /* FMUL */ - case 0x19: /* FMULX */ is_fp = 1; break; case 0x1d: /* SQRDMLAH */ @@ -12885,6 +13048,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) /* is_fp, but we pass tcg_env not fp_status. */ break; default: + case 0x19: /* FMULX */ unallocated_encoding(s); return; } @@ -13108,10 +13272,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x09: /* FMUL */ gen_helper_vfp_muld(tcg_res, tcg_op, tcg_idx, fpst); break; - case 0x19: /* FMULX */ - gen_helper_vfp_mulxd(tcg_res, tcg_op, tcg_idx, fpst); - break; default: + case 0x19: /* FMULX */ g_assert_not_reached(); } @@ -13224,24 +13386,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) g_assert_not_reached(); } break; - case 0x19: /* FMULX */ - switch (size) { - case 1: - if (is_scalar) { - gen_helper_advsimd_mulxh(tcg_res, tcg_op, - tcg_idx, fpst); - } else { - gen_helper_advsimd_mulx2h(tcg_res, tcg_op, - tcg_idx, fpst); - } - break; - case 2: - gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst); - break; - default: - g_assert_not_reached(); - } - break; case 0x0c: /* SQDMULH */ if (size == 1) { gen_helper_neon_qdmulh_s16(tcg_res, tcg_env, @@ -13283,6 +13427,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } break; default: + case 0x19: /* FMULX */ g_assert_not_reached(); } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 1f93510b85..8684581923 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1248,6 +1248,9 @@ DO_3OP(gvec_rsqrts_nf_h, float16_rsqrts_nf, float16) DO_3OP(gvec_rsqrts_nf_s, float32_rsqrts_nf, float32) #ifdef TARGET_AARCH64 +DO_3OP(gvec_fmulx_h, helper_advsimd_mulxh, float16) +DO_3OP(gvec_fmulx_s, helper_vfp_mulxs, float32) +DO_3OP(gvec_fmulx_d, helper_vfp_mulxd, float64) DO_3OP(gvec_recps_h, helper_recpsf_f16, float16) DO_3OP(gvec_recps_s, helper_recpsf_f32, float32) @@ -1385,7 +1388,7 @@ DO_MLA_IDX(gvec_mls_idx_d, uint64_t, -, H8) #undef DO_MLA_IDX -#define DO_FMUL_IDX(NAME, ADD, TYPE, H) \ +#define DO_FMUL_IDX(NAME, ADD, MUL, TYPE, H) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ { \ intptr_t i, j, oprsz = simd_oprsz(desc); \ @@ -1395,33 +1398,37 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ TYPE mm = m[H(i + idx)]; \ for (j = 0; j < segment; j++) { \ - d[i + j] = TYPE##_##ADD(d[i + j], \ - TYPE##_mul(n[i + j], mm, stat), stat); \ + d[i + j] = ADD(d[i + j], MUL(n[i + j], mm, stat), stat); \ } \ } \ clear_tail(d, oprsz, simd_maxsz(desc)); \ } -#define float16_nop(N, M, S) (M) -#define float32_nop(N, M, S) (M) -#define float64_nop(N, M, S) (M) +#define nop(N, M, S) (M) -DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16, H2) -DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32, H4) -DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64, H8) +DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16_mul, float16, H2) +DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32_mul, float32, H4) +DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64_mul, float64, H8) + +#ifdef TARGET_AARCH64 + +DO_FMUL_IDX(gvec_fmulx_idx_h, nop, helper_advsimd_mulxh, float16, H2) +DO_FMUL_IDX(gvec_fmulx_idx_s, nop, helper_vfp_mulxs, float32, H4) +DO_FMUL_IDX(gvec_fmulx_idx_d, nop, helper_vfp_mulxd, float64, H8) + +#endif + +#undef nop /* * Non-fused multiply-accumulate operations, for Neon. NB that unlike * the fused ops below they assume accumulate both from and into Vd. */ -DO_FMUL_IDX(gvec_fmla_nf_idx_h, add, float16, H2) -DO_FMUL_IDX(gvec_fmla_nf_idx_s, add, float32, H4) -DO_FMUL_IDX(gvec_fmls_nf_idx_h, sub, float16, H2) -DO_FMUL_IDX(gvec_fmls_nf_idx_s, sub, float32, H4) +DO_FMUL_IDX(gvec_fmla_nf_idx_h, float16_add, float16_mul, float16, H2) +DO_FMUL_IDX(gvec_fmla_nf_idx_s, float32_add, float32_mul, float32, H4) +DO_FMUL_IDX(gvec_fmls_nf_idx_h, float16_sub, float16_mul, float16, H2) +DO_FMUL_IDX(gvec_fmls_nf_idx_s, float32_sub, float32_mul, float32, H4) -#undef float16_nop -#undef float32_nop -#undef float64_nop #undef DO_FMUL_IDX #define DO_FMLA_IDX(NAME, TYPE, H) \ From e0300a9a5e104753c8a81bd93d66e95de4dfbc77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:33 -0700 Subject: [PATCH 0987/2884] target/arm: Convert FADD, FSUB, FDIV, FMUL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 27 +++++ target/arm/tcg/helper-a64.h | 4 + target/arm/tcg/translate-a64.c | 205 +++++++++++++++++---------------- target/arm/tcg/translate.h | 5 + target/arm/tcg/vec_helper.c | 4 + 5 files changed, 143 insertions(+), 102 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 2e0e01be01..82daafbef5 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -21,6 +21,7 @@ %rd 0:5 %esz_sd 22:1 !function=plus_2 +%esz_hsd 22:2 !function=xor_2 %hl 11:1 21:1 %hlm 11:1 20:2 @@ -37,6 +38,7 @@ @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 @rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd +@rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd @rrx_h ........ .. .. rm:4 .... . . rn:5 rd:5 &rrx_e esz=1 idx=%hlm @rrx_s ........ .. . rm:5 .... . . rn:5 rd:5 &rrx_e esz=2 idx=%hl @@ -697,22 +699,47 @@ INS_element 0 1 10 1110 000 di:5 0 si:4 1 rn:5 rd:5 ### Advanced SIMD scalar three same +FADD_s 0001 1110 ..1 ..... 0010 10 ..... ..... @rrr_hsd +FSUB_s 0001 1110 ..1 ..... 0011 10 ..... ..... @rrr_hsd +FDIV_s 0001 1110 ..1 ..... 0001 10 ..... ..... @rrr_hsd +FMUL_s 0001 1110 ..1 ..... 0000 10 ..... ..... @rrr_hsd + FMULX_s 0101 1110 010 ..... 00011 1 ..... ..... @rrr_h FMULX_s 0101 1110 0.1 ..... 11011 1 ..... ..... @rrr_sd ### Advanced SIMD three same +FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h +FADD_v 0.00 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd + +FSUB_v 0.00 1110 110 ..... 00010 1 ..... ..... @qrrr_h +FSUB_v 0.00 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd + +FDIV_v 0.10 1110 010 ..... 00111 1 ..... ..... @qrrr_h +FDIV_v 0.10 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd + +FMUL_v 0.10 1110 010 ..... 00011 1 ..... ..... @qrrr_h +FMUL_v 0.10 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd + FMULX_v 0.00 1110 010 ..... 00011 1 ..... ..... @qrrr_h FMULX_v 0.00 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd ### Advanced SIMD scalar x indexed element +FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h +FMUL_si 0101 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s +FMUL_si 0101 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d + FMULX_si 0111 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h FMULX_si 0111 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s FMULX_si 0111 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d ### Advanced SIMD vector x indexed element +FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h +FMUL_vi 0.00 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s +FMUL_vi 0.00 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d + FMULX_vi 0.10 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h FMULX_vi 0.10 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s FMULX_vi 0.10 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index b79751a717..371388f61b 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -133,6 +133,10 @@ DEF_HELPER_4(cpyfp, void, env, i32, i32, i32) DEF_HELPER_4(cpyfm, void, env, i32, i32, i32) DEF_HELPER_4(cpyfe, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8cbe6cd70f..97c3d758d6 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4887,6 +4887,34 @@ static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) return true; } +static const FPScalar f_scalar_fadd = { + gen_helper_vfp_addh, + gen_helper_vfp_adds, + gen_helper_vfp_addd, +}; +TRANS(FADD_s, do_fp3_scalar, a, &f_scalar_fadd) + +static const FPScalar f_scalar_fsub = { + gen_helper_vfp_subh, + gen_helper_vfp_subs, + gen_helper_vfp_subd, +}; +TRANS(FSUB_s, do_fp3_scalar, a, &f_scalar_fsub) + +static const FPScalar f_scalar_fdiv = { + gen_helper_vfp_divh, + gen_helper_vfp_divs, + gen_helper_vfp_divd, +}; +TRANS(FDIV_s, do_fp3_scalar, a, &f_scalar_fdiv) + +static const FPScalar f_scalar_fmul = { + gen_helper_vfp_mulh, + gen_helper_vfp_muls, + gen_helper_vfp_muld, +}; +TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul) + static const FPScalar f_scalar_fmulx = { gen_helper_advsimd_mulxh, gen_helper_vfp_mulxs, @@ -4922,6 +4950,34 @@ static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, return true; } +static gen_helper_gvec_3_ptr * const f_vector_fadd[3] = { + gen_helper_gvec_fadd_h, + gen_helper_gvec_fadd_s, + gen_helper_gvec_fadd_d, +}; +TRANS(FADD_v, do_fp3_vector, a, f_vector_fadd) + +static gen_helper_gvec_3_ptr * const f_vector_fsub[3] = { + gen_helper_gvec_fsub_h, + gen_helper_gvec_fsub_s, + gen_helper_gvec_fsub_d, +}; +TRANS(FSUB_v, do_fp3_vector, a, f_vector_fsub) + +static gen_helper_gvec_3_ptr * const f_vector_fdiv[3] = { + gen_helper_gvec_fdiv_h, + gen_helper_gvec_fdiv_s, + gen_helper_gvec_fdiv_d, +}; +TRANS(FDIV_v, do_fp3_vector, a, f_vector_fdiv) + +static gen_helper_gvec_3_ptr * const f_vector_fmul[3] = { + gen_helper_gvec_fmul_h, + gen_helper_gvec_fmul_s, + gen_helper_gvec_fmul_d, +}; +TRANS(FMUL_v, do_fp3_vector, a, f_vector_fmul) + static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = { gen_helper_gvec_fmulx_h, gen_helper_gvec_fmulx_s, @@ -4975,6 +5031,7 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) return true; } +TRANS(FMUL_si, do_fp3_scalar_idx, a, &f_scalar_fmul) TRANS(FMULX_si, do_fp3_scalar_idx, a, &f_scalar_fmulx) static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, @@ -5005,6 +5062,13 @@ static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, return true; } +static gen_helper_gvec_3_ptr * const f_vector_idx_fmul[3] = { + gen_helper_gvec_fmul_idx_h, + gen_helper_gvec_fmul_idx_s, + gen_helper_gvec_fmul_idx_d, +}; +TRANS(FMUL_vi, do_fp3_vector_idx, a, f_vector_idx_fmul) + static gen_helper_gvec_3_ptr * const f_vector_idx_fmulx[3] = { gen_helper_gvec_fmulx_idx_h, gen_helper_gvec_fmulx_idx_s, @@ -6827,18 +6891,6 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, tcg_op2 = read_fp_sreg(s, rm); switch (opcode) { - case 0x0: /* FMUL */ - gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FDIV */ - gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2: /* FADD */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FSUB */ - gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x4: /* FMAX */ gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -6855,6 +6907,12 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); gen_helper_vfp_negs(tcg_res, tcg_res); break; + default: + case 0x0: /* FMUL */ + case 0x1: /* FDIV */ + case 0x2: /* FADD */ + case 0x3: /* FSUB */ + g_assert_not_reached(); } write_fp_sreg(s, rd, tcg_res); @@ -6875,18 +6933,6 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, tcg_op2 = read_fp_dreg(s, rm); switch (opcode) { - case 0x0: /* FMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FDIV */ - gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2: /* FADD */ - gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FSUB */ - gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x4: /* FMAX */ gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -6903,6 +6949,12 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); gen_helper_vfp_negd(tcg_res, tcg_res); break; + default: + case 0x0: /* FMUL */ + case 0x1: /* FDIV */ + case 0x2: /* FADD */ + case 0x3: /* FSUB */ + g_assert_not_reached(); } write_fp_dreg(s, rd, tcg_res); @@ -6923,18 +6975,6 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, tcg_op2 = read_fp_hreg(s, rm); switch (opcode) { - case 0x0: /* FMUL */ - gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FDIV */ - gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2: /* FADD */ - gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FSUB */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x4: /* FMAX */ gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -6952,6 +6992,10 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000); break; default: + case 0x0: /* FMUL */ + case 0x1: /* FDIV */ + case 0x2: /* FADD */ + case 0x3: /* FSUB */ g_assert_not_reached(); } @@ -9180,9 +9224,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x18: /* FMAXNM */ gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1a: /* FADD */ - gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9195,27 +9236,18 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x38: /* FMINNM */ gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x3a: /* FSUB */ - gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x3e: /* FMIN */ gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x5b: /* FMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x5c: /* FCMGE */ gen_helper_neon_cge_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x5d: /* FACGE */ gen_helper_neon_acge_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x5f: /* FDIV */ - gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x7a: /* FABD */ gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); gen_helper_vfp_absd(tcg_res, tcg_res); @@ -9227,7 +9259,11 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_neon_acgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x3a: /* FSUB */ + case 0x5b: /* FMUL */ + case 0x5f: /* FDIV */ g_assert_not_reached(); } @@ -9251,9 +9287,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_res, fpst); break; - case 0x1a: /* FADD */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9269,27 +9302,18 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x38: /* FMINNM */ gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x3a: /* FSUB */ - gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x3e: /* FMIN */ gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x5b: /* FMUL */ - gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x5c: /* FCMGE */ gen_helper_neon_cge_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x5d: /* FACGE */ gen_helper_neon_acge_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x5f: /* FDIV */ - gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x7a: /* FABD */ gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); gen_helper_vfp_abss(tcg_res, tcg_res); @@ -9301,7 +9325,11 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_neon_acgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x3a: /* FSUB */ + case 0x5b: /* FMUL */ + case 0x5f: /* FDIV */ g_assert_not_reached(); } @@ -11224,15 +11252,11 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x19: /* FMLA */ case 0x39: /* FMLS */ case 0x18: /* FMAXNM */ - case 0x1a: /* FADD */ case 0x1c: /* FCMEQ */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ - case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ - case 0x5b: /* FMUL */ case 0x5c: /* FCMGE */ - case 0x5f: /* FDIV */ case 0x7a: /* FABD */ case 0x7c: /* FCMGT */ if (!fp_access_check(s)) { @@ -11262,7 +11286,11 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) return; default: + case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x3a: /* FSUB */ + case 0x5b: /* FMUL */ + case 0x5f: /* FDIV */ unallocated_encoding(s); return; } @@ -11606,19 +11634,15 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) switch (fpopcode) { case 0x0: /* FMAXNM */ case 0x1: /* FMLA */ - case 0x2: /* FADD */ case 0x4: /* FCMEQ */ case 0x6: /* FMAX */ case 0x7: /* FRECPS */ case 0x8: /* FMINNM */ case 0x9: /* FMLS */ - case 0xa: /* FSUB */ case 0xe: /* FMIN */ case 0xf: /* FRSQRTS */ - case 0x13: /* FMUL */ case 0x14: /* FCMGE */ case 0x15: /* FACGE */ - case 0x17: /* FDIV */ case 0x1a: /* FABD */ case 0x1c: /* FCMGT */ case 0x1d: /* FACGT */ @@ -11632,7 +11656,11 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) pairwise = true; break; default: + case 0x2: /* FADD */ case 0x3: /* FMULX */ + case 0xa: /* FSUB */ + case 0x13: /* FMUL */ + case 0x17: /* FDIV */ unallocated_encoding(s); return; } @@ -11706,9 +11734,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, fpst); break; - case 0x2: /* FADD */ - gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x4: /* FCMEQ */ gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -11728,27 +11753,18 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, fpst); break; - case 0xa: /* FSUB */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0xe: /* FMIN */ gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0xf: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x13: /* FMUL */ - gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x14: /* FCMGE */ gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x15: /* FACGE */ gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x17: /* FDIV */ - gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1a: /* FABD */ gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); @@ -11760,7 +11776,11 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x2: /* FADD */ case 0x3: /* FMULX */ + case 0xa: /* FSUB */ + case 0x13: /* FMUL */ + case 0x17: /* FDIV */ g_assert_not_reached(); } @@ -12979,7 +12999,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; case 0x01: /* FMLA */ case 0x05: /* FMLS */ - case 0x09: /* FMUL */ is_fp = 1; break; case 0x1d: /* SQRDMLAH */ @@ -13048,6 +13067,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) /* is_fp, but we pass tcg_env not fp_status. */ break; default: + case 0x09: /* FMUL */ case 0x19: /* FMULX */ unallocated_encoding(s); return; @@ -13269,10 +13289,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element(s, tcg_res, rd, pass, MO_64); gen_helper_vfp_muladdd(tcg_res, tcg_op, tcg_idx, tcg_res, fpst); break; - case 0x09: /* FMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op, tcg_idx, fpst); - break; default: + case 0x09: /* FMUL */ case 0x19: /* FMULX */ g_assert_not_reached(); } @@ -13368,24 +13386,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) g_assert_not_reached(); } break; - case 0x09: /* FMUL */ - switch (size) { - case 1: - if (is_scalar) { - gen_helper_advsimd_mulh(tcg_res, tcg_op, - tcg_idx, fpst); - } else { - gen_helper_advsimd_mul2h(tcg_res, tcg_op, - tcg_idx, fpst); - } - break; - case 2: - gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst); - break; - default: - g_assert_not_reached(); - } - break; case 0x0c: /* SQDMULH */ if (size == 1) { gen_helper_neon_qdmulh_s16(tcg_res, tcg_env, @@ -13427,6 +13427,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } break; default: + case 0x09: /* FMUL */ case 0x19: /* FMULX */ g_assert_not_reached(); } diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 80e85096a8..ecfa242eef 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -252,6 +252,11 @@ static inline int shl_12(DisasContext *s, int x) return x << 12; } +static inline int xor_2(DisasContext *s, int x) +{ + return x ^ 2; +} + static inline int neon_3same_fp_size(DisasContext *s, int x) { /* Convert 0==fp32, 1==fp16 into a MO_* value */ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 8684581923..4106536371 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1248,6 +1248,10 @@ DO_3OP(gvec_rsqrts_nf_h, float16_rsqrts_nf, float16) DO_3OP(gvec_rsqrts_nf_s, float32_rsqrts_nf, float32) #ifdef TARGET_AARCH64 +DO_3OP(gvec_fdiv_h, float16_div, float16) +DO_3OP(gvec_fdiv_s, float32_div, float32) +DO_3OP(gvec_fdiv_d, float64_div, float64) + DO_3OP(gvec_fmulx_h, helper_advsimd_mulxh, float16) DO_3OP(gvec_fmulx_s, helper_vfp_mulxs, float32) DO_3OP(gvec_fmulx_d, helper_vfp_mulxd, float64) From a1e250fc7177be91107e90d441ec6fd13c26d2d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:34 -0700 Subject: [PATCH 0988/2884] target/arm: Convert FMAX, FMIN, FMAXNM, FMINNM to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 4 + target/arm/tcg/a64.decode | 17 ++++ target/arm/tcg/translate-a64.c | 168 +++++++++++++++++---------------- target/arm/tcg/vec_helper.c | 4 + 4 files changed, 113 insertions(+), 80 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 2b02733305..7ee15b9651 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -748,15 +748,19 @@ DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 82daafbef5..e2678d919e 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -704,6 +704,11 @@ FSUB_s 0001 1110 ..1 ..... 0011 10 ..... ..... @rrr_hsd FDIV_s 0001 1110 ..1 ..... 0001 10 ..... ..... @rrr_hsd FMUL_s 0001 1110 ..1 ..... 0000 10 ..... ..... @rrr_hsd +FMAX_s 0001 1110 ..1 ..... 0100 10 ..... ..... @rrr_hsd +FMIN_s 0001 1110 ..1 ..... 0101 10 ..... ..... @rrr_hsd +FMAXNM_s 0001 1110 ..1 ..... 0110 10 ..... ..... @rrr_hsd +FMINNM_s 0001 1110 ..1 ..... 0111 10 ..... ..... @rrr_hsd + FMULX_s 0101 1110 010 ..... 00011 1 ..... ..... @rrr_h FMULX_s 0101 1110 0.1 ..... 11011 1 ..... ..... @rrr_sd @@ -721,6 +726,18 @@ FDIV_v 0.10 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd FMUL_v 0.10 1110 010 ..... 00011 1 ..... ..... @qrrr_h FMUL_v 0.10 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd +FMAX_v 0.00 1110 010 ..... 00110 1 ..... ..... @qrrr_h +FMAX_v 0.00 1110 0.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMIN_v 0.00 1110 110 ..... 00110 1 ..... ..... @qrrr_h +FMIN_v 0.00 1110 1.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMAXNM_v 0.00 1110 010 ..... 00000 1 ..... ..... @qrrr_h +FMAXNM_v 0.00 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd + +FMINNM_v 0.00 1110 110 ..... 00000 1 ..... ..... @qrrr_h +FMINNM_v 0.00 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd + FMULX_v 0.00 1110 010 ..... 00011 1 ..... ..... @qrrr_h FMULX_v 0.00 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 97c3d758d6..6f8207d842 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4915,6 +4915,34 @@ static const FPScalar f_scalar_fmul = { }; TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul) +static const FPScalar f_scalar_fmax = { + gen_helper_advsimd_maxh, + gen_helper_vfp_maxs, + gen_helper_vfp_maxd, +}; +TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax) + +static const FPScalar f_scalar_fmin = { + gen_helper_advsimd_minh, + gen_helper_vfp_mins, + gen_helper_vfp_mind, +}; +TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin) + +static const FPScalar f_scalar_fmaxnm = { + gen_helper_advsimd_maxnumh, + gen_helper_vfp_maxnums, + gen_helper_vfp_maxnumd, +}; +TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm) + +static const FPScalar f_scalar_fminnm = { + gen_helper_advsimd_minnumh, + gen_helper_vfp_minnums, + gen_helper_vfp_minnumd, +}; +TRANS(FMINNM_s, do_fp3_scalar, a, &f_scalar_fminnm) + static const FPScalar f_scalar_fmulx = { gen_helper_advsimd_mulxh, gen_helper_vfp_mulxs, @@ -4978,6 +5006,34 @@ static gen_helper_gvec_3_ptr * const f_vector_fmul[3] = { }; TRANS(FMUL_v, do_fp3_vector, a, f_vector_fmul) +static gen_helper_gvec_3_ptr * const f_vector_fmax[3] = { + gen_helper_gvec_fmax_h, + gen_helper_gvec_fmax_s, + gen_helper_gvec_fmax_d, +}; +TRANS(FMAX_v, do_fp3_vector, a, f_vector_fmax) + +static gen_helper_gvec_3_ptr * const f_vector_fmin[3] = { + gen_helper_gvec_fmin_h, + gen_helper_gvec_fmin_s, + gen_helper_gvec_fmin_d, +}; +TRANS(FMIN_v, do_fp3_vector, a, f_vector_fmin) + +static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[3] = { + gen_helper_gvec_fmaxnum_h, + gen_helper_gvec_fmaxnum_s, + gen_helper_gvec_fmaxnum_d, +}; +TRANS(FMAXNM_v, do_fp3_vector, a, f_vector_fmaxnm) + +static gen_helper_gvec_3_ptr * const f_vector_fminnm[3] = { + gen_helper_gvec_fminnum_h, + gen_helper_gvec_fminnum_s, + gen_helper_gvec_fminnum_d, +}; +TRANS(FMINNM_v, do_fp3_vector, a, f_vector_fminnm) + static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = { gen_helper_gvec_fmulx_h, gen_helper_gvec_fmulx_s, @@ -6891,18 +6947,6 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, tcg_op2 = read_fp_sreg(s, rm); switch (opcode) { - case 0x4: /* FMAX */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5: /* FMIN */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAXNM */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FMINNM */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x8: /* FNMUL */ gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); gen_helper_vfp_negs(tcg_res, tcg_res); @@ -6912,6 +6956,10 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, case 0x1: /* FDIV */ case 0x2: /* FADD */ case 0x3: /* FSUB */ + case 0x4: /* FMAX */ + case 0x5: /* FMIN */ + case 0x6: /* FMAXNM */ + case 0x7: /* FMINNM */ g_assert_not_reached(); } @@ -6933,18 +6981,6 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, tcg_op2 = read_fp_dreg(s, rm); switch (opcode) { - case 0x4: /* FMAX */ - gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5: /* FMIN */ - gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAXNM */ - gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FMINNM */ - gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x8: /* FNMUL */ gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); gen_helper_vfp_negd(tcg_res, tcg_res); @@ -6954,6 +6990,10 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, case 0x1: /* FDIV */ case 0x2: /* FADD */ case 0x3: /* FSUB */ + case 0x4: /* FMAX */ + case 0x5: /* FMIN */ + case 0x6: /* FMAXNM */ + case 0x7: /* FMINNM */ g_assert_not_reached(); } @@ -6975,18 +7015,6 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, tcg_op2 = read_fp_hreg(s, rm); switch (opcode) { - case 0x4: /* FMAX */ - gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5: /* FMIN */ - gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAXNM */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FMINNM */ - gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x8: /* FNMUL */ gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000); @@ -6996,6 +7024,10 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, case 0x1: /* FDIV */ case 0x2: /* FADD */ case 0x3: /* FSUB */ + case 0x4: /* FMAX */ + case 0x5: /* FMIN */ + case 0x6: /* FMAXNM */ + case 0x7: /* FMINNM */ g_assert_not_reached(); } @@ -9221,24 +9253,12 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_res, fpst); break; - case 0x18: /* FMAXNM */ - gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1e: /* FMAX */ - gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1f: /* FRECPS */ gen_helper_recpsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x38: /* FMINNM */ - gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3e: /* FMIN */ - gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9259,9 +9279,13 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_neon_acgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x18: /* FMAXNM */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x1e: /* FMAX */ + case 0x38: /* FMINNM */ case 0x3a: /* FSUB */ + case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ case 0x5f: /* FDIV */ g_assert_not_reached(); @@ -9290,21 +9314,9 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1e: /* FMAX */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1f: /* FRECPS */ gen_helper_recpsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x18: /* FMAXNM */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x38: /* FMINNM */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3e: /* FMIN */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9325,9 +9337,13 @@ static void handle_3same_float(DisasContext *s, int size, int elements, gen_helper_neon_acgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x18: /* FMAXNM */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x1e: /* FMAX */ + case 0x38: /* FMINNM */ case 0x3a: /* FSUB */ + case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ case 0x5f: /* FDIV */ g_assert_not_reached(); @@ -11251,11 +11267,7 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x7d: /* FACGT */ case 0x19: /* FMLA */ case 0x39: /* FMLS */ - case 0x18: /* FMAXNM */ case 0x1c: /* FCMEQ */ - case 0x1e: /* FMAX */ - case 0x38: /* FMINNM */ - case 0x3e: /* FMIN */ case 0x5c: /* FCMGE */ case 0x7a: /* FABD */ case 0x7c: /* FCMGT */ @@ -11286,9 +11298,13 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) return; default: + case 0x18: /* FMAXNM */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x1e: /* FMAX */ + case 0x38: /* FMINNM */ case 0x3a: /* FSUB */ + case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ case 0x5f: /* FDIV */ unallocated_encoding(s); @@ -11632,14 +11648,10 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) int pass; switch (fpopcode) { - case 0x0: /* FMAXNM */ case 0x1: /* FMLA */ case 0x4: /* FCMEQ */ - case 0x6: /* FMAX */ case 0x7: /* FRECPS */ - case 0x8: /* FMINNM */ case 0x9: /* FMLS */ - case 0xe: /* FMIN */ case 0xf: /* FRSQRTS */ case 0x14: /* FCMGE */ case 0x15: /* FACGE */ @@ -11656,9 +11668,13 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) pairwise = true; break; default: + case 0x0: /* FMAXNM */ case 0x2: /* FADD */ case 0x3: /* FMULX */ + case 0x6: /* FMAX */ + case 0x8: /* FMINNM */ case 0xa: /* FSUB */ + case 0xe: /* FMIN */ case 0x13: /* FMUL */ case 0x17: /* FDIV */ unallocated_encoding(s); @@ -11726,9 +11742,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op2, rm, pass, MO_16); switch (fpopcode) { - case 0x0: /* FMAXNM */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1: /* FMLA */ read_vec_element_i32(s, tcg_res, rd, pass, MO_16); gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, @@ -11737,15 +11750,9 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x4: /* FCMEQ */ gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x6: /* FMAX */ - gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x7: /* FRECPS */ gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x8: /* FMINNM */ - gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x9: /* FMLS */ /* As usual for ARM, separate negation for fused multiply-add */ tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); @@ -11753,9 +11760,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, fpst); break; - case 0xe: /* FMIN */ - gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0xf: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -11776,9 +11780,13 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0x0: /* FMAXNM */ case 0x2: /* FADD */ case 0x3: /* FMULX */ + case 0x6: /* FMAX */ + case 0x8: /* FMINNM */ case 0xa: /* FSUB */ + case 0xe: /* FMIN */ case 0x13: /* FMUL */ case 0x17: /* FDIV */ g_assert_not_reached(); diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 4106536371..99ef676071 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1231,15 +1231,19 @@ DO_3OP(gvec_facgt_s, float32_acgt, float32) DO_3OP(gvec_fmax_h, float16_max, float16) DO_3OP(gvec_fmax_s, float32_max, float32) +DO_3OP(gvec_fmax_d, float64_max, float64) DO_3OP(gvec_fmin_h, float16_min, float16) DO_3OP(gvec_fmin_s, float32_min, float32) +DO_3OP(gvec_fmin_d, float64_min, float64) DO_3OP(gvec_fmaxnum_h, float16_maxnum, float16) DO_3OP(gvec_fmaxnum_s, float32_maxnum, float32) +DO_3OP(gvec_fmaxnum_d, float64_maxnum, float64) DO_3OP(gvec_fminnum_h, float16_minnum, float16) DO_3OP(gvec_fminnum_s, float32_minnum, float32) +DO_3OP(gvec_fminnum_d, float64_minnum, float64) DO_3OP(gvec_recps_nf_h, float16_recps_nf, float16) DO_3OP(gvec_recps_nf_s, float32_recps_nf, float32) From 3938f94175283ab709b5082704dbf772c8c4be61 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:35 -0700 Subject: [PATCH 0989/2884] target/arm: Introduce vfp_load_reg16 Load and zero-extend float16 into a TCGv_i32 before all scalar operations. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240524232121.284515-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-vfp.c | 39 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index b9af03b7c3..8e755fcde8 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -48,6 +48,12 @@ static inline void vfp_store_reg32(TCGv_i32 var, int reg) tcg_gen_st_i32(var, tcg_env, vfp_reg_offset(false, reg)); } +static inline void vfp_load_reg16(TCGv_i32 var, int reg) +{ + tcg_gen_ld16u_i32(var, tcg_env, + vfp_reg_offset(false, reg) + HOST_BIG_ENDIAN * 2); +} + /* * The imm8 encodes the sign bit, enough bits to represent an exponent in * the range 01....1xx to 10....0xx, and the most significant 4 bits of @@ -902,8 +908,7 @@ static bool trans_VMOV_half(DisasContext *s, arg_VMOV_single *a) if (a->l) { /* VFP to general purpose register */ tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vn); - tcg_gen_andi_i32(tmp, tmp, 0xffff); + vfp_load_reg16(tmp, a->vn); store_reg(s, a->rt, tmp); } else { /* general purpose register to VFP */ @@ -1453,11 +1458,11 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, fd = tcg_temp_new_i32(); fpst = fpstatus_ptr(FPST_FPCR_F16); - vfp_load_reg32(f0, vn); - vfp_load_reg32(f1, vm); + vfp_load_reg16(f0, vn); + vfp_load_reg16(f1, vm); if (reads_vd) { - vfp_load_reg32(fd, vd); + vfp_load_reg16(fd, vd); } fn(fd, f0, f1, fpst); vfp_store_reg32(fd, vd); @@ -1633,7 +1638,7 @@ static bool do_vfp_2op_hp(DisasContext *s, VFPGen2OpSPFn *fn, int vd, int vm) } f0 = tcg_temp_new_i32(); - vfp_load_reg32(f0, vm); + vfp_load_reg16(f0, vm); fn(f0, f0); vfp_store_reg32(f0, vd); @@ -2106,13 +2111,13 @@ static bool do_vfm_hp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i32(); - vfp_load_reg32(vn, a->vn); - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vn, a->vn); + vfp_load_reg16(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ gen_helper_vfp_negh(vn, vn); } - vfp_load_reg32(vd, a->vd); + vfp_load_reg16(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ gen_helper_vfp_negh(vd, vd); @@ -2456,11 +2461,11 @@ static bool trans_VCMP_hp(DisasContext *s, arg_VCMP_sp *a) vd = tcg_temp_new_i32(); vm = tcg_temp_new_i32(); - vfp_load_reg32(vd, a->vd); + vfp_load_reg16(vd, a->vd); if (a->z) { tcg_gen_movi_i32(vm, 0); } else { - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vm, a->vm); } if (a->e) { @@ -2700,7 +2705,7 @@ static bool trans_VRINTR_hp(DisasContext *s, arg_VRINTR_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); + vfp_load_reg16(tmp, a->vm); fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_rinth(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); @@ -2773,7 +2778,7 @@ static bool trans_VRINTZ_hp(DisasContext *s, arg_VRINTZ_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); + vfp_load_reg16(tmp, a->vm); fpst = fpstatus_ptr(FPST_FPCR_F16); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rinth(tmp, tmp, fpst); @@ -2853,7 +2858,7 @@ static bool trans_VRINTX_hp(DisasContext *s, arg_VRINTX_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); + vfp_load_reg16(tmp, a->vm); fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_rinth_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); @@ -3270,7 +3275,7 @@ static bool trans_VCVT_hp_int(DisasContext *s, arg_VCVT_sp_int *a) fpst = fpstatus_ptr(FPST_FPCR_F16); vm = tcg_temp_new_i32(); - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vm, a->vm); if (a->s) { if (a->rz) { @@ -3383,8 +3388,8 @@ static bool trans_VINS(DisasContext *s, arg_VINS *a) /* Insert low half of Vm into high half of Vd */ rm = tcg_temp_new_i32(); rd = tcg_temp_new_i32(); - vfp_load_reg32(rm, a->vm); - vfp_load_reg32(rd, a->vd); + vfp_load_reg16(rm, a->vm); + vfp_load_reg16(rd, a->vd); tcg_gen_deposit_i32(rd, rd, rm, 16, 16); vfp_store_reg32(rd, a->vd); return true; From 21e885aff407a3d7c74408387e80e9a38fae8fbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:36 -0700 Subject: [PATCH 0990/2884] target/arm: Expand vfp neg and abs inline Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 6 ---- target/arm/tcg/translate-a64.c | 44 +++++++++++++-------------- target/arm/tcg/translate-vfp.c | 54 +++++++++++++++++----------------- target/arm/tcg/translate.h | 30 +++++++++++++++++++ target/arm/vfp_helper.c | 30 ------------------- 5 files changed, 79 insertions(+), 85 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 7ee15b9651..0fd01c9c52 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -132,12 +132,6 @@ DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, ptr) DEF_HELPER_3(vfp_minnumh, f16, f16, f16, ptr) DEF_HELPER_3(vfp_minnums, f32, f32, f32, ptr) DEF_HELPER_3(vfp_minnumd, f64, f64, f64, ptr) -DEF_HELPER_1(vfp_negh, f16, f16) -DEF_HELPER_1(vfp_negs, f32, f32) -DEF_HELPER_1(vfp_negd, f64, f64) -DEF_HELPER_1(vfp_absh, f16, f16) -DEF_HELPER_1(vfp_abss, f32, f32) -DEF_HELPER_1(vfp_absd, f64, f64) DEF_HELPER_2(vfp_sqrth, f16, f16, env) DEF_HELPER_2(vfp_sqrts, f32, f32, env) DEF_HELPER_2(vfp_sqrtd, f64, f64, env) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 6f8207d842..878f83298f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6591,10 +6591,10 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) tcg_gen_mov_i32(tcg_res, tcg_op); break; case 0x1: /* FABS */ - tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); + gen_vfp_absh(tcg_res, tcg_op); break; case 0x2: /* FNEG */ - tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); + gen_vfp_negh(tcg_res, tcg_op); break; case 0x3: /* FSQRT */ fpst = fpstatus_ptr(FPST_FPCR_F16); @@ -6645,10 +6645,10 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) tcg_gen_mov_i32(tcg_res, tcg_op); goto done; case 0x1: /* FABS */ - gen_helper_vfp_abss(tcg_res, tcg_op); + gen_vfp_abss(tcg_res, tcg_op); goto done; case 0x2: /* FNEG */ - gen_helper_vfp_negs(tcg_res, tcg_op); + gen_vfp_negs(tcg_res, tcg_op); goto done; case 0x3: /* FSQRT */ gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); @@ -6720,10 +6720,10 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) switch (opcode) { case 0x1: /* FABS */ - gen_helper_vfp_absd(tcg_res, tcg_op); + gen_vfp_absd(tcg_res, tcg_op); goto done; case 0x2: /* FNEG */ - gen_helper_vfp_negd(tcg_res, tcg_op); + gen_vfp_negd(tcg_res, tcg_op); goto done; case 0x3: /* FSQRT */ gen_helper_vfp_sqrtd(tcg_res, tcg_op, tcg_env); @@ -6949,7 +6949,7 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, switch (opcode) { case 0x8: /* FNMUL */ gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_negs(tcg_res, tcg_res); + gen_vfp_negs(tcg_res, tcg_res); break; default: case 0x0: /* FMUL */ @@ -6983,7 +6983,7 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, switch (opcode) { case 0x8: /* FNMUL */ gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_negd(tcg_res, tcg_res); + gen_vfp_negd(tcg_res, tcg_res); break; default: case 0x0: /* FMUL */ @@ -7017,7 +7017,7 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, switch (opcode) { case 0x8: /* FNMUL */ gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000); + gen_vfp_negh(tcg_res, tcg_res); break; default: case 0x0: /* FMUL */ @@ -7102,11 +7102,11 @@ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1, * flipped if it is a negated-input. */ if (o1 == true) { - gen_helper_vfp_negs(tcg_op3, tcg_op3); + gen_vfp_negs(tcg_op3, tcg_op3); } if (o0 != o1) { - gen_helper_vfp_negs(tcg_op1, tcg_op1); + gen_vfp_negs(tcg_op1, tcg_op1); } gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); @@ -7134,11 +7134,11 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, * flipped if it is a negated-input. */ if (o1 == true) { - gen_helper_vfp_negd(tcg_op3, tcg_op3); + gen_vfp_negd(tcg_op3, tcg_op3); } if (o0 != o1) { - gen_helper_vfp_negd(tcg_op1, tcg_op1); + gen_vfp_negd(tcg_op1, tcg_op1); } gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); @@ -9246,7 +9246,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, switch (fpopcode) { case 0x39: /* FMLS */ /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negd(tcg_op1, tcg_op1); + gen_vfp_negd(tcg_op1, tcg_op1); /* fall through */ case 0x19: /* FMLA */ read_vec_element(s, tcg_res, rd, pass, MO_64); @@ -9270,7 +9270,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, break; case 0x7a: /* FABD */ gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_absd(tcg_res, tcg_res); + gen_vfp_absd(tcg_res, tcg_res); break; case 0x7c: /* FCMGT */ gen_helper_neon_cgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); @@ -9304,7 +9304,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, switch (fpopcode) { case 0x39: /* FMLS */ /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negs(tcg_op1, tcg_op1); + gen_vfp_negs(tcg_op1, tcg_op1); /* fall through */ case 0x19: /* FMLA */ read_vec_element_i32(s, tcg_res, rd, pass, MO_32); @@ -9328,7 +9328,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, break; case 0x7a: /* FABD */ gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_abss(tcg_res, tcg_res); + gen_vfp_abss(tcg_res, tcg_res); break; case 0x7c: /* FCMGT */ gen_helper_neon_cgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); @@ -9741,10 +9741,10 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, } break; case 0x2f: /* FABS */ - gen_helper_vfp_absd(tcg_rd, tcg_rn); + gen_vfp_absd(tcg_rd, tcg_rn); break; case 0x6f: /* FNEG */ - gen_helper_vfp_negd(tcg_rd, tcg_rn); + gen_vfp_negd(tcg_rd, tcg_rn); break; case 0x7f: /* FSQRT */ gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_env); @@ -12567,10 +12567,10 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } break; case 0x2f: /* FABS */ - gen_helper_vfp_abss(tcg_res, tcg_op); + gen_vfp_abss(tcg_res, tcg_op); break; case 0x6f: /* FNEG */ - gen_helper_vfp_negs(tcg_res, tcg_op); + gen_vfp_negs(tcg_res, tcg_op); break; case 0x7f: /* FSQRT */ gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); @@ -13291,7 +13291,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) switch (16 * u + opcode) { case 0x05: /* FMLS */ /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negd(tcg_op, tcg_op); + gen_vfp_negd(tcg_op, tcg_op); /* fall through */ case 0x01: /* FMLA */ read_vec_element(s, tcg_res, rd, pass, MO_64); diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 8e755fcde8..39ec971ff7 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -1768,7 +1768,7 @@ static void gen_VMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_mulh(tmp, vn, vm, fpst); - gen_helper_vfp_negh(tmp, tmp); + gen_vfp_negh(tmp, tmp); gen_helper_vfp_addh(vd, vd, tmp, fpst); } @@ -1786,7 +1786,7 @@ static void gen_VMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_muls(tmp, vn, vm, fpst); - gen_helper_vfp_negs(tmp, tmp); + gen_vfp_negs(tmp, tmp); gen_helper_vfp_adds(vd, vd, tmp, fpst); } @@ -1804,7 +1804,7 @@ static void gen_VMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) TCGv_i64 tmp = tcg_temp_new_i64(); gen_helper_vfp_muld(tmp, vn, vm, fpst); - gen_helper_vfp_negd(tmp, tmp); + gen_vfp_negd(tmp, tmp); gen_helper_vfp_addd(vd, vd, tmp, fpst); } @@ -1824,7 +1824,7 @@ static void gen_VNMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_mulh(tmp, vn, vm, fpst); - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(vd, vd); gen_helper_vfp_addh(vd, vd, tmp, fpst); } @@ -1844,7 +1844,7 @@ static void gen_VNMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_muls(tmp, vn, vm, fpst); - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(vd, vd); gen_helper_vfp_adds(vd, vd, tmp, fpst); } @@ -1864,7 +1864,7 @@ static void gen_VNMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) TCGv_i64 tmp = tcg_temp_new_i64(); gen_helper_vfp_muld(tmp, vn, vm, fpst); - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(vd, vd); gen_helper_vfp_addd(vd, vd, tmp, fpst); } @@ -1879,8 +1879,8 @@ static void gen_VNMLA_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_mulh(tmp, vn, vm, fpst); - gen_helper_vfp_negh(tmp, tmp); - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(tmp, tmp); + gen_vfp_negh(vd, vd); gen_helper_vfp_addh(vd, vd, tmp, fpst); } @@ -1895,8 +1895,8 @@ static void gen_VNMLA_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_muls(tmp, vn, vm, fpst); - gen_helper_vfp_negs(tmp, tmp); - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(tmp, tmp); + gen_vfp_negs(vd, vd); gen_helper_vfp_adds(vd, vd, tmp, fpst); } @@ -1911,8 +1911,8 @@ static void gen_VNMLA_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) TCGv_i64 tmp = tcg_temp_new_i64(); gen_helper_vfp_muld(tmp, vn, vm, fpst); - gen_helper_vfp_negd(tmp, tmp); - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(tmp, tmp); + gen_vfp_negd(vd, vd); gen_helper_vfp_addd(vd, vd, tmp, fpst); } @@ -1940,7 +1940,7 @@ static void gen_VNMUL_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) { /* VNMUL: -(fn * fm) */ gen_helper_vfp_mulh(vd, vn, vm, fpst); - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(vd, vd); } static bool trans_VNMUL_hp(DisasContext *s, arg_VNMUL_sp *a) @@ -1952,7 +1952,7 @@ static void gen_VNMUL_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) { /* VNMUL: -(fn * fm) */ gen_helper_vfp_muls(vd, vn, vm, fpst); - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(vd, vd); } static bool trans_VNMUL_sp(DisasContext *s, arg_VNMUL_sp *a) @@ -1964,7 +1964,7 @@ static void gen_VNMUL_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) { /* VNMUL: -(fn * fm) */ gen_helper_vfp_muld(vd, vn, vm, fpst); - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(vd, vd); } static bool trans_VNMUL_dp(DisasContext *s, arg_VNMUL_dp *a) @@ -2115,12 +2115,12 @@ static bool do_vfm_hp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) vfp_load_reg16(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ - gen_helper_vfp_negh(vn, vn); + gen_vfp_negh(vn, vn); } vfp_load_reg16(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(vd, vd); } fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_vfp_muladdh(vd, vn, vm, vd, fpst); @@ -2174,12 +2174,12 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) vfp_load_reg32(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ - gen_helper_vfp_negs(vn, vn); + gen_vfp_negs(vn, vn); } vfp_load_reg32(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(vd, vd); } fpst = fpstatus_ptr(FPST_FPCR); gen_helper_vfp_muladds(vd, vn, vm, vd, fpst); @@ -2239,12 +2239,12 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) vfp_load_reg64(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ - gen_helper_vfp_negd(vn, vn); + gen_vfp_negd(vn, vn); } vfp_load_reg64(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(vd, vd); } fpst = fpstatus_ptr(FPST_FPCR); gen_helper_vfp_muladdd(vd, vn, vm, vd, fpst); @@ -2414,13 +2414,13 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) DO_VFP_VMOV(VMOV_reg, sp, tcg_gen_mov_i32) DO_VFP_VMOV(VMOV_reg, dp, tcg_gen_mov_i64) -DO_VFP_2OP(VABS, hp, gen_helper_vfp_absh, aa32_fp16_arith) -DO_VFP_2OP(VABS, sp, gen_helper_vfp_abss, aa32_fpsp_v2) -DO_VFP_2OP(VABS, dp, gen_helper_vfp_absd, aa32_fpdp_v2) +DO_VFP_2OP(VABS, hp, gen_vfp_absh, aa32_fp16_arith) +DO_VFP_2OP(VABS, sp, gen_vfp_abss, aa32_fpsp_v2) +DO_VFP_2OP(VABS, dp, gen_vfp_absd, aa32_fpdp_v2) -DO_VFP_2OP(VNEG, hp, gen_helper_vfp_negh, aa32_fp16_arith) -DO_VFP_2OP(VNEG, sp, gen_helper_vfp_negs, aa32_fpsp_v2) -DO_VFP_2OP(VNEG, dp, gen_helper_vfp_negd, aa32_fpdp_v2) +DO_VFP_2OP(VNEG, hp, gen_vfp_negh, aa32_fp16_arith) +DO_VFP_2OP(VNEG, sp, gen_vfp_negs, aa32_fpsp_v2) +DO_VFP_2OP(VNEG, dp, gen_vfp_negd, aa32_fpdp_v2) static void gen_VSQRT_hp(TCGv_i32 vd, TCGv_i32 vm) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index ecfa242eef..b05a9eb668 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -406,6 +406,36 @@ static inline void gen_swstep_exception(DisasContext *s, int isv, int ex) */ uint64_t vfp_expand_imm(int size, uint8_t imm8); +static inline void gen_vfp_absh(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_andi_i32(d, s, INT16_MAX); +} + +static inline void gen_vfp_abss(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_andi_i32(d, s, INT32_MAX); +} + +static inline void gen_vfp_absd(TCGv_i64 d, TCGv_i64 s) +{ + tcg_gen_andi_i64(d, s, INT64_MAX); +} + +static inline void gen_vfp_negh(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_xori_i32(d, s, 1u << 15); +} + +static inline void gen_vfp_negs(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_xori_i32(d, s, 1u << 31); +} + +static inline void gen_vfp_negd(TCGv_i64 d, TCGv_i64 s) +{ + tcg_gen_xori_i64(d, s, 1ull << 63); +} + /* Vector operations shared between ARM and AArch64. */ void gen_gvec_ceq0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 3e5e37abbe..ce26b8a71a 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -281,36 +281,6 @@ VFP_BINOP(minnum) VFP_BINOP(maxnum) #undef VFP_BINOP -dh_ctype_f16 VFP_HELPER(neg, h)(dh_ctype_f16 a) -{ - return float16_chs(a); -} - -float32 VFP_HELPER(neg, s)(float32 a) -{ - return float32_chs(a); -} - -float64 VFP_HELPER(neg, d)(float64 a) -{ - return float64_chs(a); -} - -dh_ctype_f16 VFP_HELPER(abs, h)(dh_ctype_f16 a) -{ - return float16_abs(a); -} - -float32 VFP_HELPER(abs, s)(float32 a) -{ - return float32_abs(a); -} - -float64 VFP_HELPER(abs, d)(float64 a) -{ - return float64_abs(a); -} - dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, CPUARMState *env) { return float16_sqrt(a, &env->vfp.fp_status_f16); From 69cefabcac501c4837605ceb87b5f6527de547d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:37 -0700 Subject: [PATCH 0991/2884] target/arm: Convert FNMUL to decodetree This is the last instruction within disas_fp_2src, so remove that and its subroutines. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 1 + target/arm/tcg/translate-a64.c | 177 +++++---------------------------- 2 files changed, 27 insertions(+), 151 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index e2678d919e..cde4b86303 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -703,6 +703,7 @@ FADD_s 0001 1110 ..1 ..... 0010 10 ..... ..... @rrr_hsd FSUB_s 0001 1110 ..1 ..... 0011 10 ..... ..... @rrr_hsd FDIV_s 0001 1110 ..1 ..... 0001 10 ..... ..... @rrr_hsd FMUL_s 0001 1110 ..1 ..... 0000 10 ..... ..... @rrr_hsd +FNMUL_s 0001 1110 ..1 ..... 1000 10 ..... ..... @rrr_hsd FMAX_s 0001 1110 ..1 ..... 0100 10 ..... ..... @rrr_hsd FMIN_s 0001 1110 ..1 ..... 0101 10 ..... ..... @rrr_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 878f83298f..5ba30ba7c8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4950,6 +4950,31 @@ static const FPScalar f_scalar_fmulx = { }; TRANS(FMULX_s, do_fp3_scalar, a, &f_scalar_fmulx) +static void gen_fnmul_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_mulh(d, n, m, s); + gen_vfp_negh(d, d); +} + +static void gen_fnmul_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_muls(d, n, m, s); + gen_vfp_negs(d, d); +} + +static void gen_fnmul_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_muld(d, n, m, s); + gen_vfp_negd(d, d); +} + +static const FPScalar f_scalar_fnmul = { + gen_fnmul_h, + gen_fnmul_s, + gen_fnmul_d, +}; +TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -6932,156 +6957,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) } } -/* Floating-point data-processing (2 source) - single precision */ -static void handle_fp_2src_single(DisasContext *s, int opcode, - int rd, int rn, int rm) -{ - TCGv_i32 tcg_op1; - TCGv_i32 tcg_op2; - TCGv_i32 tcg_res; - TCGv_ptr fpst; - - tcg_res = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR); - tcg_op1 = read_fp_sreg(s, rn); - tcg_op2 = read_fp_sreg(s, rm); - - switch (opcode) { - case 0x8: /* FNMUL */ - gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - gen_vfp_negs(tcg_res, tcg_res); - break; - default: - case 0x0: /* FMUL */ - case 0x1: /* FDIV */ - case 0x2: /* FADD */ - case 0x3: /* FSUB */ - case 0x4: /* FMAX */ - case 0x5: /* FMIN */ - case 0x6: /* FMAXNM */ - case 0x7: /* FMINNM */ - g_assert_not_reached(); - } - - write_fp_sreg(s, rd, tcg_res); -} - -/* Floating-point data-processing (2 source) - double precision */ -static void handle_fp_2src_double(DisasContext *s, int opcode, - int rd, int rn, int rm) -{ - TCGv_i64 tcg_op1; - TCGv_i64 tcg_op2; - TCGv_i64 tcg_res; - TCGv_ptr fpst; - - tcg_res = tcg_temp_new_i64(); - fpst = fpstatus_ptr(FPST_FPCR); - tcg_op1 = read_fp_dreg(s, rn); - tcg_op2 = read_fp_dreg(s, rm); - - switch (opcode) { - case 0x8: /* FNMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - gen_vfp_negd(tcg_res, tcg_res); - break; - default: - case 0x0: /* FMUL */ - case 0x1: /* FDIV */ - case 0x2: /* FADD */ - case 0x3: /* FSUB */ - case 0x4: /* FMAX */ - case 0x5: /* FMIN */ - case 0x6: /* FMAXNM */ - case 0x7: /* FMINNM */ - g_assert_not_reached(); - } - - write_fp_dreg(s, rd, tcg_res); -} - -/* Floating-point data-processing (2 source) - half precision */ -static void handle_fp_2src_half(DisasContext *s, int opcode, - int rd, int rn, int rm) -{ - TCGv_i32 tcg_op1; - TCGv_i32 tcg_op2; - TCGv_i32 tcg_res; - TCGv_ptr fpst; - - tcg_res = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR_F16); - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - - switch (opcode) { - case 0x8: /* FNMUL */ - gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - gen_vfp_negh(tcg_res, tcg_res); - break; - default: - case 0x0: /* FMUL */ - case 0x1: /* FDIV */ - case 0x2: /* FADD */ - case 0x3: /* FSUB */ - case 0x4: /* FMAX */ - case 0x5: /* FMIN */ - case 0x6: /* FMAXNM */ - case 0x7: /* FMINNM */ - g_assert_not_reached(); - } - - write_fp_sreg(s, rd, tcg_res); -} - -/* Floating point data-processing (2 source) - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | opcode | 1 0 | Rn | Rd | - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - */ -static void disas_fp_2src(DisasContext *s, uint32_t insn) -{ - int mos = extract32(insn, 29, 3); - int type = extract32(insn, 22, 2); - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int opcode = extract32(insn, 12, 4); - - if (opcode > 8 || mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - if (!fp_access_check(s)) { - return; - } - handle_fp_2src_single(s, opcode, rd, rn, rm); - break; - case 1: - if (!fp_access_check(s)) { - return; - } - handle_fp_2src_double(s, opcode, rd, rn, rm); - break; - case 3: - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_fp_2src_half(s, opcode, rd, rn, rm); - break; - default: - unallocated_encoding(s); - } -} - /* Floating-point data-processing (3 source) - single precision */ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1, int rd, int rn, int rm, int ra) @@ -7685,7 +7560,7 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn) break; case 2: /* Floating point data-processing (2 source) */ - disas_fp_2src(s, insn); + unallocated_encoding(s); /* in decodetree */ break; case 3: /* Floating point conditional select */ From 2d558efbf53ad008e56124ff9e5dad94504a620d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:38 -0700 Subject: [PATCH 0992/2884] target/arm: Convert FMLA, FMLS to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 + target/arm/tcg/a64.decode | 22 +++ target/arm/tcg/translate-a64.c | 241 +++++++++++++++++---------------- target/arm/tcg/vec_helper.c | 14 ++ 4 files changed, 163 insertions(+), 116 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 0fd01c9c52..e021c18517 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -770,9 +770,11 @@ DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index cde4b86303..11527bb5e5 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -742,12 +742,26 @@ FMINNM_v 0.00 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd FMULX_v 0.00 1110 010 ..... 00011 1 ..... ..... @qrrr_h FMULX_v 0.00 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd +FMLA_v 0.00 1110 010 ..... 00001 1 ..... ..... @qrrr_h +FMLA_v 0.00 1110 0.1 ..... 11001 1 ..... ..... @qrrr_sd + +FMLS_v 0.00 1110 110 ..... 00001 1 ..... ..... @qrrr_h +FMLS_v 0.00 1110 1.1 ..... 11001 1 ..... ..... @qrrr_sd + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h FMUL_si 0101 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s FMUL_si 0101 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d +FMLA_si 0101 1111 00 .. .... 0001 . 0 ..... ..... @rrx_h +FMLA_si 0101 1111 10 .. .... 0001 . 0 ..... ..... @rrx_s +FMLA_si 0101 1111 11 0. .... 0001 . 0 ..... ..... @rrx_d + +FMLS_si 0101 1111 00 .. .... 0101 . 0 ..... ..... @rrx_h +FMLS_si 0101 1111 10 .. .... 0101 . 0 ..... ..... @rrx_s +FMLS_si 0101 1111 11 0. .... 0101 . 0 ..... ..... @rrx_d + FMULX_si 0111 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h FMULX_si 0111 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s FMULX_si 0111 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d @@ -758,6 +772,14 @@ FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h FMUL_vi 0.00 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s FMUL_vi 0.00 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d +FMLA_vi 0.00 1111 00 .. .... 0001 . 0 ..... ..... @qrrx_h +FMLA_vi 0.00 1111 10 . ..... 0001 . 0 ..... ..... @qrrx_s +FMLA_vi 0.00 1111 11 0 ..... 0001 . 0 ..... ..... @qrrx_d + +FMLS_vi 0.00 1111 00 .. .... 0101 . 0 ..... ..... @qrrx_h +FMLS_vi 0.00 1111 10 . ..... 0101 . 0 ..... ..... @qrrx_s +FMLS_vi 0.00 1111 11 0 ..... 0101 . 0 ..... ..... @qrrx_d + FMULX_vi 0.10 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h FMULX_vi 0.10 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s FMULX_vi 0.10 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5ba30ba7c8..f84c12378d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5066,6 +5066,20 @@ static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = { }; TRANS(FMULX_v, do_fp3_vector, a, f_vector_fmulx) +static gen_helper_gvec_3_ptr * const f_vector_fmla[3] = { + gen_helper_gvec_vfma_h, + gen_helper_gvec_vfma_s, + gen_helper_gvec_vfma_d, +}; +TRANS(FMLA_v, do_fp3_vector, a, f_vector_fmla) + +static gen_helper_gvec_3_ptr * const f_vector_fmls[3] = { + gen_helper_gvec_vfms_h, + gen_helper_gvec_vfms_s, + gen_helper_gvec_vfms_d, +}; +TRANS(FMLS_v, do_fp3_vector, a, f_vector_fmls) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5115,6 +5129,64 @@ static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) TRANS(FMUL_si, do_fp3_scalar_idx, a, &f_scalar_fmul) TRANS(FMULX_si, do_fp3_scalar_idx, a, &f_scalar_fmulx) +static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rd); + TCGv_i64 t1 = read_fp_dreg(s, a->rn); + TCGv_i64 t2 = tcg_temp_new_i64(); + + read_vec_element(s, t2, a->rm, a->idx, MO_64); + if (neg) { + gen_vfp_negd(t1, t1); + } + gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rd); + TCGv_i32 t1 = read_fp_sreg(s, a->rn); + TCGv_i32 t2 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t2, a->rm, a->idx, MO_32); + if (neg) { + gen_vfp_negs(t1, t1); + } + gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rd); + TCGv_i32 t1 = read_fp_hreg(s, a->rn); + TCGv_i32 t2 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t2, a->rm, a->idx, MO_16); + if (neg) { + gen_vfp_negh(t1, t1); + } + gen_helper_advsimd_muladdh(t0, t1, t2, t0, + fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + g_assert_not_reached(); + } + return true; +} + +TRANS(FMLA_si, do_fmla_scalar_idx, a, false) +TRANS(FMLS_si, do_fmla_scalar_idx, a, true) + static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5157,6 +5229,42 @@ static gen_helper_gvec_3_ptr * const f_vector_idx_fmulx[3] = { }; TRANS(FMULX_vi, do_fp3_vector_idx, a, f_vector_idx_fmulx) +static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) +{ + static gen_helper_gvec_4_ptr * const fns[3] = { + gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, + gen_helper_gvec_fmla_idx_d, + }; + MemOp esz = a->esz; + + switch (esz) { + case MO_64: + if (!a->q) { + return false; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + g_assert_not_reached(); + } + if (fp_access_check(s)) { + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + esz == MO_16, (a->idx << 1) | neg, + fns[esz - 1]); + } + return true; +} + +TRANS(FMLA_vi, do_fmla_vector_idx, a, false) +TRANS(FMLS_vi, do_fmla_vector_idx, a, true) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the @@ -9119,15 +9227,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, read_vec_element(s, tcg_op2, rm, pass, MO_64); switch (fpopcode) { - case 0x39: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_vfp_negd(tcg_op1, tcg_op1); - /* fall through */ - case 0x19: /* FMLA */ - read_vec_element(s, tcg_res, rd, pass, MO_64); - gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, - tcg_res, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9155,10 +9254,12 @@ static void handle_3same_float(DisasContext *s, int size, int elements, break; default: case 0x18: /* FMAXNM */ + case 0x19: /* FMLA */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ + case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ @@ -9177,15 +9278,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); switch (fpopcode) { - case 0x39: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_vfp_negs(tcg_op1, tcg_op1); - /* fall through */ - case 0x19: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, MO_32); - gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, - tcg_res, fpst); - break; case 0x1c: /* FCMEQ */ gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -9213,10 +9305,12 @@ static void handle_3same_float(DisasContext *s, int size, int elements, break; default: case 0x18: /* FMAXNM */ + case 0x19: /* FMLA */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ + case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ @@ -11140,8 +11234,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x3f: /* FRSQRTS */ case 0x5d: /* FACGE */ case 0x7d: /* FACGT */ - case 0x19: /* FMLA */ - case 0x39: /* FMLS */ case 0x1c: /* FCMEQ */ case 0x5c: /* FCMGE */ case 0x7a: /* FABD */ @@ -11174,10 +11266,12 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) default: case 0x18: /* FMAXNM */ + case 0x19: /* FMLA */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ + case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ @@ -11523,10 +11617,8 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) int pass; switch (fpopcode) { - case 0x1: /* FMLA */ case 0x4: /* FCMEQ */ case 0x7: /* FRECPS */ - case 0x9: /* FMLS */ case 0xf: /* FRSQRTS */ case 0x14: /* FCMGE */ case 0x15: /* FACGE */ @@ -11544,10 +11636,12 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) break; default: case 0x0: /* FMAXNM */ + case 0x1: /* FMLA */ case 0x2: /* FADD */ case 0x3: /* FMULX */ case 0x6: /* FMAX */ case 0x8: /* FMINNM */ + case 0x9: /* FMLS */ case 0xa: /* FSUB */ case 0xe: /* FMIN */ case 0x13: /* FMUL */ @@ -11617,24 +11711,12 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op2, rm, pass, MO_16); switch (fpopcode) { - case 0x1: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, MO_16); - gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, - fpst); - break; case 0x4: /* FCMEQ */ gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x7: /* FRECPS */ gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x9: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); - read_vec_element_i32(s, tcg_res, rd, pass, MO_16); - gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, - fpst); - break; case 0xf: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -11656,10 +11738,12 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) break; default: case 0x0: /* FMAXNM */ + case 0x1: /* FMLA */ case 0x2: /* FADD */ case 0x3: /* FMULX */ case 0x6: /* FMAX */ case 0x8: /* FMINNM */ + case 0x9: /* FMLS */ case 0xa: /* FSUB */ case 0xe: /* FMIN */ case 0x13: /* FMUL */ @@ -12880,10 +12964,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0c: /* SQDMULH */ case 0x0d: /* SQRDMULH */ break; - case 0x01: /* FMLA */ - case 0x05: /* FMLS */ - is_fp = 1; - break; case 0x1d: /* SQRDMLAH */ case 0x1f: /* SQRDMLSH */ if (!dc_isar_feature(aa64_rdm, s)) { @@ -12950,6 +13030,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) /* is_fp, but we pass tcg_env not fp_status. */ break; default: + case 0x01: /* FMLA */ + case 0x05: /* FMLS */ case 0x09: /* FMUL */ case 0x19: /* FMULX */ unallocated_encoding(s); @@ -12958,20 +13040,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) switch (is_fp) { case 1: /* normal fp */ - /* convert insn encoded size to MemOp size */ - switch (size) { - case 0: /* half-precision */ - size = MO_16; - is_fp16 = true; - break; - case MO_32: /* single precision */ - case MO_64: /* double precision */ - break; - default: - unallocated_encoding(s); - return; - } - break; + unallocated_encoding(s); /* in decodetree */ + return; case 2: /* complex fp */ /* Each indexable element is a complex pair. */ @@ -13150,38 +13220,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } if (size == 3) { - TCGv_i64 tcg_idx = tcg_temp_new_i64(); - int pass; - - assert(is_fp && is_q && !is_long); - - read_vec_element(s, tcg_idx, rm, index, MO_64); - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - - switch (16 * u + opcode) { - case 0x05: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_vfp_negd(tcg_op, tcg_op); - /* fall through */ - case 0x01: /* FMLA */ - read_vec_element(s, tcg_res, rd, pass, MO_64); - gen_helper_vfp_muladdd(tcg_res, tcg_op, tcg_idx, tcg_res, fpst); - break; - default: - case 0x09: /* FMUL */ - case 0x19: /* FMULX */ - g_assert_not_reached(); - } - - write_vec_element(s, tcg_res, rd, pass, MO_64); - } - - clear_vec_high(s, !is_scalar, rd); + g_assert_not_reached(); } else if (!is_long) { /* 32 bit floating point, or 16 or 32 bit integer. * For the 16 bit scalar case we use the usual Neon helpers and @@ -13237,38 +13276,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) genfn(tcg_res, tcg_op, tcg_res); break; } - case 0x05: /* FMLS */ - case 0x01: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, - is_scalar ? size : MO_32); - switch (size) { - case 1: - if (opcode == 0x5) { - /* As usual for ARM, separate negation for fused - * multiply-add */ - tcg_gen_xori_i32(tcg_op, tcg_op, 0x80008000); - } - if (is_scalar) { - gen_helper_advsimd_muladdh(tcg_res, tcg_op, tcg_idx, - tcg_res, fpst); - } else { - gen_helper_advsimd_muladd2h(tcg_res, tcg_op, tcg_idx, - tcg_res, fpst); - } - break; - case 2: - if (opcode == 0x5) { - /* As usual for ARM, separate negation for - * fused multiply-add */ - tcg_gen_xori_i32(tcg_op, tcg_op, 0x80000000); - } - gen_helper_vfp_muladds(tcg_res, tcg_op, tcg_idx, - tcg_res, fpst); - break; - default: - g_assert_not_reached(); - } - break; case 0x0c: /* SQDMULH */ if (size == 1) { gen_helper_neon_qdmulh_s16(tcg_res, tcg_env, @@ -13310,6 +13317,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } break; default: + case 0x01: /* FMLA */ + case 0x05: /* FMLS */ case 0x09: /* FMUL */ case 0x19: /* FMULX */ g_assert_not_reached(); diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 99ef676071..b925b9f21b 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1309,6 +1309,12 @@ static float32 float32_muladd_f(float32 dest, float32 op1, float32 op2, return float32_muladd(op1, op2, dest, 0, stat); } +static float64 float64_muladd_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(op1, op2, dest, 0, stat); +} + static float16 float16_mulsub_f(float16 dest, float16 op1, float16 op2, float_status *stat) { @@ -1321,6 +1327,12 @@ static float32 float32_mulsub_f(float32 dest, float32 op1, float32 op2, return float32_muladd(float32_chs(op1), op2, dest, 0, stat); } +static float64 float64_mulsub_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(float64_chs(op1), op2, dest, 0, stat); +} + #define DO_MULADD(NAME, FUNC, TYPE) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ { \ @@ -1340,9 +1352,11 @@ DO_MULADD(gvec_fmls_s, float32_mulsub_nf, float32) DO_MULADD(gvec_vfma_h, float16_muladd_f, float16) DO_MULADD(gvec_vfma_s, float32_muladd_f, float32) +DO_MULADD(gvec_vfma_d, float64_muladd_f, float64) DO_MULADD(gvec_vfms_h, float16_mulsub_f, float16) DO_MULADD(gvec_vfms_s, float32_mulsub_f, float32) +DO_MULADD(gvec_vfms_d, float64_mulsub_f, float64) /* For the indexed ops, SVE applies the index per 128-bit vector segment. * For AdvSIMD, there is of course only one such vector segment. From 4fe068fac000fe8182a334c3bd5bb4bfe7a0f1e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:39 -0700 Subject: [PATCH 0993/2884] target/arm: Convert FCMEQ, FCMGE, FCMGT, FACGE, FACGT to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 5 + target/arm/tcg/a64.decode | 30 ++++++ target/arm/tcg/translate-a64.c | 188 +++++++++++++++++++-------------- target/arm/tcg/vec_helper.c | 30 ++++++ 4 files changed, 174 insertions(+), 79 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index e021c18517..8d076011c1 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -727,18 +727,23 @@ DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 11527bb5e5..7fc3277be6 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -713,6 +713,21 @@ FMINNM_s 0001 1110 ..1 ..... 0111 10 ..... ..... @rrr_hsd FMULX_s 0101 1110 010 ..... 00011 1 ..... ..... @rrr_h FMULX_s 0101 1110 0.1 ..... 11011 1 ..... ..... @rrr_sd +FCMEQ_s 0101 1110 010 ..... 00100 1 ..... ..... @rrr_h +FCMEQ_s 0101 1110 0.1 ..... 11100 1 ..... ..... @rrr_sd + +FCMGE_s 0111 1110 010 ..... 00100 1 ..... ..... @rrr_h +FCMGE_s 0111 1110 0.1 ..... 11100 1 ..... ..... @rrr_sd + +FCMGT_s 0111 1110 110 ..... 00100 1 ..... ..... @rrr_h +FCMGT_s 0111 1110 1.1 ..... 11100 1 ..... ..... @rrr_sd + +FACGE_s 0111 1110 010 ..... 00101 1 ..... ..... @rrr_h +FACGE_s 0111 1110 0.1 ..... 11101 1 ..... ..... @rrr_sd + +FACGT_s 0111 1110 110 ..... 00101 1 ..... ..... @rrr_h +FACGT_s 0111 1110 1.1 ..... 11101 1 ..... ..... @rrr_sd + ### Advanced SIMD three same FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h @@ -748,6 +763,21 @@ FMLA_v 0.00 1110 0.1 ..... 11001 1 ..... ..... @qrrr_sd FMLS_v 0.00 1110 110 ..... 00001 1 ..... ..... @qrrr_h FMLS_v 0.00 1110 1.1 ..... 11001 1 ..... ..... @qrrr_sd +FCMEQ_v 0.00 1110 010 ..... 00100 1 ..... ..... @qrrr_h +FCMEQ_v 0.00 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd + +FCMGE_v 0.10 1110 010 ..... 00100 1 ..... ..... @qrrr_h +FCMGE_v 0.10 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd + +FCMGT_v 0.10 1110 110 ..... 00100 1 ..... ..... @qrrr_h +FCMGT_v 0.10 1110 1.1 ..... 11100 1 ..... ..... @qrrr_sd + +FACGE_v 0.10 1110 010 ..... 00101 1 ..... ..... @qrrr_h +FACGE_v 0.10 1110 0.1 ..... 11101 1 ..... ..... @qrrr_sd + +FACGT_v 0.10 1110 110 ..... 00101 1 ..... ..... @qrrr_h +FACGT_v 0.10 1110 1.1 ..... 11101 1 ..... ..... @qrrr_sd + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f84c12378d..75b0c1a005 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4975,6 +4975,41 @@ static const FPScalar f_scalar_fnmul = { }; TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul) +static const FPScalar f_scalar_fcmeq = { + gen_helper_advsimd_ceq_f16, + gen_helper_neon_ceq_f32, + gen_helper_neon_ceq_f64, +}; +TRANS(FCMEQ_s, do_fp3_scalar, a, &f_scalar_fcmeq) + +static const FPScalar f_scalar_fcmge = { + gen_helper_advsimd_cge_f16, + gen_helper_neon_cge_f32, + gen_helper_neon_cge_f64, +}; +TRANS(FCMGE_s, do_fp3_scalar, a, &f_scalar_fcmge) + +static const FPScalar f_scalar_fcmgt = { + gen_helper_advsimd_cgt_f16, + gen_helper_neon_cgt_f32, + gen_helper_neon_cgt_f64, +}; +TRANS(FCMGT_s, do_fp3_scalar, a, &f_scalar_fcmgt) + +static const FPScalar f_scalar_facge = { + gen_helper_advsimd_acge_f16, + gen_helper_neon_acge_f32, + gen_helper_neon_acge_f64, +}; +TRANS(FACGE_s, do_fp3_scalar, a, &f_scalar_facge) + +static const FPScalar f_scalar_facgt = { + gen_helper_advsimd_acgt_f16, + gen_helper_neon_acgt_f32, + gen_helper_neon_acgt_f64, +}; +TRANS(FACGT_s, do_fp3_scalar, a, &f_scalar_facgt) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5080,6 +5115,41 @@ static gen_helper_gvec_3_ptr * const f_vector_fmls[3] = { }; TRANS(FMLS_v, do_fp3_vector, a, f_vector_fmls) +static gen_helper_gvec_3_ptr * const f_vector_fcmeq[3] = { + gen_helper_gvec_fceq_h, + gen_helper_gvec_fceq_s, + gen_helper_gvec_fceq_d, +}; +TRANS(FCMEQ_v, do_fp3_vector, a, f_vector_fcmeq) + +static gen_helper_gvec_3_ptr * const f_vector_fcmge[3] = { + gen_helper_gvec_fcge_h, + gen_helper_gvec_fcge_s, + gen_helper_gvec_fcge_d, +}; +TRANS(FCMGE_v, do_fp3_vector, a, f_vector_fcmge) + +static gen_helper_gvec_3_ptr * const f_vector_fcmgt[3] = { + gen_helper_gvec_fcgt_h, + gen_helper_gvec_fcgt_s, + gen_helper_gvec_fcgt_d, +}; +TRANS(FCMGT_v, do_fp3_vector, a, f_vector_fcmgt) + +static gen_helper_gvec_3_ptr * const f_vector_facge[3] = { + gen_helper_gvec_facge_h, + gen_helper_gvec_facge_s, + gen_helper_gvec_facge_d, +}; +TRANS(FACGE_v, do_fp3_vector, a, f_vector_facge) + +static gen_helper_gvec_3_ptr * const f_vector_facgt[3] = { + gen_helper_gvec_facgt_h, + gen_helper_gvec_facgt_s, + gen_helper_gvec_facgt_d, +}; +TRANS(FACGT_v, do_fp3_vector, a, f_vector_facgt) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -9227,43 +9297,33 @@ static void handle_3same_float(DisasContext *s, int size, int elements, read_vec_element(s, tcg_op2, rm, pass, MO_64); switch (fpopcode) { - case 0x1c: /* FCMEQ */ - gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1f: /* FRECPS */ gen_helper_recpsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x5c: /* FCMGE */ - gen_helper_neon_cge_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5d: /* FACGE */ - gen_helper_neon_acge_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x7a: /* FABD */ gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); gen_vfp_absd(tcg_res, tcg_res); break; - case 0x7c: /* FCMGT */ - gen_helper_neon_cgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7d: /* FACGT */ - gen_helper_neon_acgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; default: case 0x18: /* FMAXNM */ case 0x19: /* FMLA */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x1c: /* FCMEQ */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ + case 0x5c: /* FCMGE */ + case 0x5d: /* FACGE */ case 0x5f: /* FDIV */ + case 0x7c: /* FCMGT */ + case 0x7d: /* FACGT */ g_assert_not_reached(); } @@ -9278,43 +9338,33 @@ static void handle_3same_float(DisasContext *s, int size, int elements, read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); switch (fpopcode) { - case 0x1c: /* FCMEQ */ - gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1f: /* FRECPS */ gen_helper_recpsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x5c: /* FCMGE */ - gen_helper_neon_cge_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5d: /* FACGE */ - gen_helper_neon_acge_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x7a: /* FABD */ gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); gen_vfp_abss(tcg_res, tcg_res); break; - case 0x7c: /* FCMGT */ - gen_helper_neon_cgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7d: /* FACGT */ - gen_helper_neon_acgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; default: case 0x18: /* FMAXNM */ case 0x19: /* FMLA */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x1c: /* FCMEQ */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ + case 0x5c: /* FCMGE */ + case 0x5d: /* FACGE */ case 0x5f: /* FDIV */ + case 0x7c: /* FCMGT */ + case 0x7d: /* FACGT */ g_assert_not_reached(); } @@ -9355,15 +9405,15 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) switch (fpopcode) { case 0x1f: /* FRECPS */ case 0x3f: /* FRSQRTS */ + case 0x7a: /* FABD */ + break; + default: + case 0x1b: /* FMULX */ case 0x5d: /* FACGE */ case 0x7d: /* FACGT */ case 0x1c: /* FCMEQ */ case 0x5c: /* FCMGE */ case 0x7c: /* FCMGT */ - case 0x7a: /* FABD */ - break; - default: - case 0x1b: /* FMULX */ unallocated_encoding(s); return; } @@ -9516,17 +9566,17 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, TCGv_i32 tcg_res; switch (fpopcode) { - case 0x04: /* FCMEQ (reg) */ case 0x07: /* FRECPS */ case 0x0f: /* FRSQRTS */ - case 0x14: /* FCMGE (reg) */ - case 0x15: /* FACGE */ case 0x1a: /* FABD */ - case 0x1c: /* FCMGT (reg) */ - case 0x1d: /* FACGT */ break; default: case 0x03: /* FMULX */ + case 0x04: /* FCMEQ (reg) */ + case 0x14: /* FCMGE (reg) */ + case 0x15: /* FACGE */ + case 0x1c: /* FCMGT (reg) */ + case 0x1d: /* FACGT */ unallocated_encoding(s); return; } @@ -9546,33 +9596,23 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, tcg_res = tcg_temp_new_i32(); switch (fpopcode) { - case 0x04: /* FCMEQ (reg) */ - gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x07: /* FRECPS */ gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0x0f: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x14: /* FCMGE (reg) */ - gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x15: /* FACGE */ - gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1a: /* FABD */ gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); break; - case 0x1c: /* FCMGT (reg) */ - gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1d: /* FACGT */ - gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; default: case 0x03: /* FMULX */ + case 0x04: /* FCMEQ (reg) */ + case 0x14: /* FCMGE (reg) */ + case 0x15: /* FACGE */ + case 0x1c: /* FCMGT (reg) */ + case 0x1d: /* FACGT */ g_assert_not_reached(); } @@ -11232,12 +11272,7 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) return; case 0x1f: /* FRECPS */ case 0x3f: /* FRSQRTS */ - case 0x5d: /* FACGE */ - case 0x7d: /* FACGT */ - case 0x1c: /* FCMEQ */ - case 0x5c: /* FCMGE */ case 0x7a: /* FABD */ - case 0x7c: /* FCMGT */ if (!fp_access_check(s)) { return; } @@ -11269,13 +11304,18 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x19: /* FMLA */ case 0x1a: /* FADD */ case 0x1b: /* FMULX */ + case 0x1c: /* FCMEQ */ case 0x1e: /* FMAX */ case 0x38: /* FMINNM */ case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x5b: /* FMUL */ + case 0x5c: /* FCMGE */ + case 0x5d: /* FACGE */ case 0x5f: /* FDIV */ + case 0x7d: /* FACGT */ + case 0x7c: /* FCMGT */ unallocated_encoding(s); return; } @@ -11617,14 +11657,9 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) int pass; switch (fpopcode) { - case 0x4: /* FCMEQ */ case 0x7: /* FRECPS */ case 0xf: /* FRSQRTS */ - case 0x14: /* FCMGE */ - case 0x15: /* FACGE */ case 0x1a: /* FABD */ - case 0x1c: /* FCMGT */ - case 0x1d: /* FACGT */ pairwise = false; break; case 0x10: /* FMAXNMP */ @@ -11639,13 +11674,18 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x1: /* FMLA */ case 0x2: /* FADD */ case 0x3: /* FMULX */ + case 0x4: /* FCMEQ */ case 0x6: /* FMAX */ case 0x8: /* FMINNM */ case 0x9: /* FMLS */ case 0xa: /* FSUB */ case 0xe: /* FMIN */ case 0x13: /* FMUL */ + case 0x14: /* FCMGE */ + case 0x15: /* FACGE */ case 0x17: /* FDIV */ + case 0x1c: /* FCMGT */ + case 0x1d: /* FACGT */ unallocated_encoding(s); return; } @@ -11711,43 +11751,33 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op2, rm, pass, MO_16); switch (fpopcode) { - case 0x4: /* FCMEQ */ - gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x7: /* FRECPS */ gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; case 0xf: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x14: /* FCMGE */ - gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x15: /* FACGE */ - gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0x1a: /* FABD */ gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); break; - case 0x1c: /* FCMGT */ - gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1d: /* FACGT */ - gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; default: case 0x0: /* FMAXNM */ case 0x1: /* FMLA */ case 0x2: /* FADD */ case 0x3: /* FMULX */ + case 0x4: /* FCMEQ */ case 0x6: /* FMAX */ case 0x8: /* FMINNM */ case 0x9: /* FMLS */ case 0xa: /* FSUB */ case 0xe: /* FMIN */ case 0x13: /* FMUL */ + case 0x14: /* FCMGE */ + case 0x15: /* FACGE */ case 0x17: /* FDIV */ + case 0x1c: /* FCMGT */ + case 0x1d: /* FACGT */ g_assert_not_reached(); } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index b925b9f21b..dabefa3526 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -971,6 +971,11 @@ static uint32_t float32_ceq(float32 op1, float32 op2, float_status *stat) return -float32_eq_quiet(op1, op2, stat); } +static uint64_t float64_ceq(float64 op1, float64 op2, float_status *stat) +{ + return -float64_eq_quiet(op1, op2, stat); +} + static uint16_t float16_cge(float16 op1, float16 op2, float_status *stat) { return -float16_le(op2, op1, stat); @@ -981,6 +986,11 @@ static uint32_t float32_cge(float32 op1, float32 op2, float_status *stat) return -float32_le(op2, op1, stat); } +static uint64_t float64_cge(float64 op1, float64 op2, float_status *stat) +{ + return -float64_le(op2, op1, stat); +} + static uint16_t float16_cgt(float16 op1, float16 op2, float_status *stat) { return -float16_lt(op2, op1, stat); @@ -991,6 +1001,11 @@ static uint32_t float32_cgt(float32 op1, float32 op2, float_status *stat) return -float32_lt(op2, op1, stat); } +static uint64_t float64_cgt(float64 op1, float64 op2, float_status *stat) +{ + return -float64_lt(op2, op1, stat); +} + static uint16_t float16_acge(float16 op1, float16 op2, float_status *stat) { return -float16_le(float16_abs(op2), float16_abs(op1), stat); @@ -1001,6 +1016,11 @@ static uint32_t float32_acge(float32 op1, float32 op2, float_status *stat) return -float32_le(float32_abs(op2), float32_abs(op1), stat); } +static uint64_t float64_acge(float64 op1, float64 op2, float_status *stat) +{ + return -float64_le(float64_abs(op2), float64_abs(op1), stat); +} + static uint16_t float16_acgt(float16 op1, float16 op2, float_status *stat) { return -float16_lt(float16_abs(op2), float16_abs(op1), stat); @@ -1011,6 +1031,11 @@ static uint32_t float32_acgt(float32 op1, float32 op2, float_status *stat) return -float32_lt(float32_abs(op2), float32_abs(op1), stat); } +static uint64_t float64_acgt(float64 op1, float64 op2, float_status *stat) +{ + return -float64_lt(float64_abs(op2), float64_abs(op1), stat); +} + static int16_t vfp_tosszh(float16 x, void *fpstp) { float_status *fpst = fpstp; @@ -1216,18 +1241,23 @@ DO_3OP(gvec_fabd_s, float32_abd, float32) DO_3OP(gvec_fceq_h, float16_ceq, float16) DO_3OP(gvec_fceq_s, float32_ceq, float32) +DO_3OP(gvec_fceq_d, float64_ceq, float64) DO_3OP(gvec_fcge_h, float16_cge, float16) DO_3OP(gvec_fcge_s, float32_cge, float32) +DO_3OP(gvec_fcge_d, float64_cge, float64) DO_3OP(gvec_fcgt_h, float16_cgt, float16) DO_3OP(gvec_fcgt_s, float32_cgt, float32) +DO_3OP(gvec_fcgt_d, float64_cgt, float64) DO_3OP(gvec_facge_h, float16_acge, float16) DO_3OP(gvec_facge_s, float32_acge, float32) +DO_3OP(gvec_facge_d, float64_acge, float64) DO_3OP(gvec_facgt_h, float16_acgt, float16) DO_3OP(gvec_facgt_s, float32_acgt, float32) +DO_3OP(gvec_facgt_d, float64_acgt, float64) DO_3OP(gvec_fmax_h, float16_max, float16) DO_3OP(gvec_fmax_s, float32_max, float32) From 43454734c49e8289c4068f19a7f10beac6ba953c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:40 -0700 Subject: [PATCH 0994/2884] target/arm: Convert FABD to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-27-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 1 + target/arm/tcg/a64.decode | 6 ++++ target/arm/tcg/translate-a64.c | 60 ++++++++++++++++++++++------------ target/arm/tcg/vec_helper.c | 6 ++++ 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 8d076011c1..ff6e3094f4 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -724,6 +724,7 @@ DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7fc3277be6..a852b5f06f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -728,6 +728,9 @@ FACGE_s 0111 1110 0.1 ..... 11101 1 ..... ..... @rrr_sd FACGT_s 0111 1110 110 ..... 00101 1 ..... ..... @rrr_h FACGT_s 0111 1110 1.1 ..... 11101 1 ..... ..... @rrr_sd +FABD_s 0111 1110 110 ..... 00010 1 ..... ..... @rrr_h +FABD_s 0111 1110 1.1 ..... 11010 1 ..... ..... @rrr_sd + ### Advanced SIMD three same FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h @@ -778,6 +781,9 @@ FACGE_v 0.10 1110 0.1 ..... 11101 1 ..... ..... @qrrr_sd FACGT_v 0.10 1110 110 ..... 00101 1 ..... ..... @qrrr_h FACGT_v 0.10 1110 1.1 ..... 11101 1 ..... ..... @qrrr_sd +FABD_v 0.10 1110 110 ..... 00010 1 ..... ..... @qrrr_h +FABD_v 0.10 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 75b0c1a005..633384d2a5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5010,6 +5010,31 @@ static const FPScalar f_scalar_facgt = { }; TRANS(FACGT_s, do_fp3_scalar, a, &f_scalar_facgt) +static void gen_fabd_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subh(d, n, m, s); + gen_vfp_absh(d, d); +} + +static void gen_fabd_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subs(d, n, m, s); + gen_vfp_abss(d, d); +} + +static void gen_fabd_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_subd(d, n, m, s); + gen_vfp_absd(d, d); +} + +static const FPScalar f_scalar_fabd = { + gen_fabd_h, + gen_fabd_s, + gen_fabd_d, +}; +TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5150,6 +5175,13 @@ static gen_helper_gvec_3_ptr * const f_vector_facgt[3] = { }; TRANS(FACGT_v, do_fp3_vector, a, f_vector_facgt) +static gen_helper_gvec_3_ptr * const f_vector_fabd[3] = { + gen_helper_gvec_fabd_h, + gen_helper_gvec_fabd_s, + gen_helper_gvec_fabd_d, +}; +TRANS(FABD_v, do_fp3_vector, a, f_vector_fabd) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -9303,10 +9335,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x7a: /* FABD */ - gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - gen_vfp_absd(tcg_res, tcg_res); - break; default: case 0x18: /* FMAXNM */ case 0x19: /* FMLA */ @@ -9322,6 +9350,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x5c: /* FCMGE */ case 0x5d: /* FACGE */ case 0x5f: /* FDIV */ + case 0x7a: /* FABD */ case 0x7c: /* FCMGT */ case 0x7d: /* FACGT */ g_assert_not_reached(); @@ -9344,10 +9373,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x3f: /* FRSQRTS */ gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x7a: /* FABD */ - gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - gen_vfp_abss(tcg_res, tcg_res); - break; default: case 0x18: /* FMAXNM */ case 0x19: /* FMLA */ @@ -9363,6 +9388,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, case 0x5c: /* FCMGE */ case 0x5d: /* FACGE */ case 0x5f: /* FDIV */ + case 0x7a: /* FABD */ case 0x7c: /* FCMGT */ case 0x7d: /* FACGT */ g_assert_not_reached(); @@ -9405,7 +9431,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) switch (fpopcode) { case 0x1f: /* FRECPS */ case 0x3f: /* FRSQRTS */ - case 0x7a: /* FABD */ break; default: case 0x1b: /* FMULX */ @@ -9413,6 +9438,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x7d: /* FACGT */ case 0x1c: /* FCMEQ */ case 0x5c: /* FCMGE */ + case 0x7a: /* FABD */ case 0x7c: /* FCMGT */ unallocated_encoding(s); return; @@ -9568,13 +9594,13 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, switch (fpopcode) { case 0x07: /* FRECPS */ case 0x0f: /* FRSQRTS */ - case 0x1a: /* FABD */ break; default: case 0x03: /* FMULX */ case 0x04: /* FCMEQ (reg) */ case 0x14: /* FCMGE (reg) */ case 0x15: /* FACGE */ + case 0x1a: /* FABD */ case 0x1c: /* FCMGT (reg) */ case 0x1d: /* FACGT */ unallocated_encoding(s); @@ -9602,15 +9628,12 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, case 0x0f: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1a: /* FABD */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); - break; default: case 0x03: /* FMULX */ case 0x04: /* FCMEQ (reg) */ case 0x14: /* FCMGE (reg) */ case 0x15: /* FACGE */ + case 0x1a: /* FABD */ case 0x1c: /* FCMGT (reg) */ case 0x1d: /* FACGT */ g_assert_not_reached(); @@ -11272,7 +11295,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) return; case 0x1f: /* FRECPS */ case 0x3f: /* FRSQRTS */ - case 0x7a: /* FABD */ if (!fp_access_check(s)) { return; } @@ -11314,6 +11336,7 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x5c: /* FCMGE */ case 0x5d: /* FACGE */ case 0x5f: /* FDIV */ + case 0x7a: /* FABD */ case 0x7d: /* FACGT */ case 0x7c: /* FCMGT */ unallocated_encoding(s); @@ -11659,7 +11682,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) switch (fpopcode) { case 0x7: /* FRECPS */ case 0xf: /* FRSQRTS */ - case 0x1a: /* FABD */ pairwise = false; break; case 0x10: /* FMAXNMP */ @@ -11684,6 +11706,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x14: /* FCMGE */ case 0x15: /* FACGE */ case 0x17: /* FDIV */ + case 0x1a: /* FABD */ case 0x1c: /* FCMGT */ case 0x1d: /* FACGT */ unallocated_encoding(s); @@ -11757,10 +11780,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0xf: /* FRSQRTS */ gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0x1a: /* FABD */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); - break; default: case 0x0: /* FMAXNM */ case 0x1: /* FMLA */ @@ -11776,6 +11795,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x14: /* FCMGE */ case 0x15: /* FACGE */ case 0x17: /* FDIV */ + case 0x1a: /* FABD */ case 0x1c: /* FCMGT */ case 0x1d: /* FACGT */ g_assert_not_reached(); diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index dabefa3526..e9d7922f30 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1154,6 +1154,11 @@ static float32 float32_abd(float32 op1, float32 op2, float_status *stat) return float32_abs(float32_sub(op1, op2, stat)); } +static float64 float64_abd(float64 op1, float64 op2, float_status *stat) +{ + return float64_abs(float64_sub(op1, op2, stat)); +} + /* * Reciprocal step. These are the AArch32 version which uses a * non-fused multiply-and-subtract. @@ -1238,6 +1243,7 @@ DO_3OP(gvec_ftsmul_d, float64_ftsmul, float64) DO_3OP(gvec_fabd_h, float16_abd, float16) DO_3OP(gvec_fabd_s, float32_abd, float32) +DO_3OP(gvec_fabd_d, float64_abd, float64) DO_3OP(gvec_fceq_h, float16_ceq, float16) DO_3OP(gvec_fceq_s, float32_ceq, float32) From 13db93bce56c894fb42d0bf7e45aa1db98ce8377 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:41 -0700 Subject: [PATCH 0995/2884] target/arm: Convert FRECPS, FRSQRTS to decodetree These are the last instructions within handle_3same_float and disas_simd_scalar_three_reg_same_fp16 so remove them. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-28-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 12 ++ target/arm/tcg/translate-a64.c | 293 ++++----------------------------- 2 files changed, 46 insertions(+), 259 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index a852b5f06f..84cb38f1dd 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -731,6 +731,12 @@ FACGT_s 0111 1110 1.1 ..... 11101 1 ..... ..... @rrr_sd FABD_s 0111 1110 110 ..... 00010 1 ..... ..... @rrr_h FABD_s 0111 1110 1.1 ..... 11010 1 ..... ..... @rrr_sd +FRECPS_s 0101 1110 010 ..... 00111 1 ..... ..... @rrr_h +FRECPS_s 0101 1110 0.1 ..... 11111 1 ..... ..... @rrr_sd + +FRSQRTS_s 0101 1110 110 ..... 00111 1 ..... ..... @rrr_h +FRSQRTS_s 0101 1110 1.1 ..... 11111 1 ..... ..... @rrr_sd + ### Advanced SIMD three same FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h @@ -784,6 +790,12 @@ FACGT_v 0.10 1110 1.1 ..... 11101 1 ..... ..... @qrrr_sd FABD_v 0.10 1110 110 ..... 00010 1 ..... ..... @qrrr_h FABD_v 0.10 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd +FRECPS_v 0.00 1110 010 ..... 00111 1 ..... ..... @qrrr_h +FRECPS_v 0.00 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd + +FRSQRTS_v 0.00 1110 110 ..... 00111 1 ..... ..... @qrrr_h +FRSQRTS_v 0.00 1110 1.1 ..... 11111 1 ..... ..... @qrrr_sd + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 633384d2a5..a7537a5104 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5035,6 +5035,20 @@ static const FPScalar f_scalar_fabd = { }; TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd) +static const FPScalar f_scalar_frecps = { + gen_helper_recpsf_f16, + gen_helper_recpsf_f32, + gen_helper_recpsf_f64, +}; +TRANS(FRECPS_s, do_fp3_scalar, a, &f_scalar_frecps) + +static const FPScalar f_scalar_frsqrts = { + gen_helper_rsqrtsf_f16, + gen_helper_rsqrtsf_f32, + gen_helper_rsqrtsf_f64, +}; +TRANS(FRSQRTS_s, do_fp3_scalar, a, &f_scalar_frsqrts) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5182,6 +5196,20 @@ static gen_helper_gvec_3_ptr * const f_vector_fabd[3] = { }; TRANS(FABD_v, do_fp3_vector, a, f_vector_fabd) +static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = { + gen_helper_gvec_recps_h, + gen_helper_gvec_recps_s, + gen_helper_gvec_recps_d, +}; +TRANS(FRECPS_v, do_fp3_vector, a, f_vector_frecps) + +static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = { + gen_helper_gvec_rsqrts_h, + gen_helper_gvec_rsqrts_s, + gen_helper_gvec_rsqrts_d, +}; +TRANS(FRSQRTS_v, do_fp3_vector, a, f_vector_frsqrts) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -9308,107 +9336,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, } } -/* Handle the 3-same-operands float operations; shared by the scalar - * and vector encodings. The caller must filter out any encodings - * not allocated for the encoding it is dealing with. - */ -static void handle_3same_float(DisasContext *s, int size, int elements, - int fpopcode, int rd, int rn, int rm) -{ - int pass; - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - for (pass = 0; pass < elements; pass++) { - if (size) { - /* Double */ - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - - switch (fpopcode) { - case 0x1f: /* FRECPS */ - gen_helper_recpsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3f: /* FRSQRTS */ - gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - case 0x18: /* FMAXNM */ - case 0x19: /* FMLA */ - case 0x1a: /* FADD */ - case 0x1b: /* FMULX */ - case 0x1c: /* FCMEQ */ - case 0x1e: /* FMAX */ - case 0x38: /* FMINNM */ - case 0x39: /* FMLS */ - case 0x3a: /* FSUB */ - case 0x3e: /* FMIN */ - case 0x5b: /* FMUL */ - case 0x5c: /* FCMGE */ - case 0x5d: /* FACGE */ - case 0x5f: /* FDIV */ - case 0x7a: /* FABD */ - case 0x7c: /* FCMGT */ - case 0x7d: /* FACGT */ - g_assert_not_reached(); - } - - write_vec_element(s, tcg_res, rd, pass, MO_64); - } else { - /* Single */ - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op1, rn, pass, MO_32); - read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); - - switch (fpopcode) { - case 0x1f: /* FRECPS */ - gen_helper_recpsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3f: /* FRSQRTS */ - gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - case 0x18: /* FMAXNM */ - case 0x19: /* FMLA */ - case 0x1a: /* FADD */ - case 0x1b: /* FMULX */ - case 0x1c: /* FCMEQ */ - case 0x1e: /* FMAX */ - case 0x38: /* FMINNM */ - case 0x39: /* FMLS */ - case 0x3a: /* FSUB */ - case 0x3e: /* FMIN */ - case 0x5b: /* FMUL */ - case 0x5c: /* FCMGE */ - case 0x5d: /* FACGE */ - case 0x5f: /* FDIV */ - case 0x7a: /* FABD */ - case 0x7c: /* FCMGT */ - case 0x7d: /* FACGT */ - g_assert_not_reached(); - } - - if (elements == 1) { - /* scalar single so clear high part */ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - tcg_gen_extu_i32_i64(tcg_tmp, tcg_res); - write_vec_element(s, tcg_tmp, rd, pass, MO_64); - } else { - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } - } - - clear_vec_high(s, elements * (size ? 8 : 4) > 8, rd); -} - /* AdvSIMD scalar three same * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0 * +-----+---+-----------+------+---+------+--------+---+------+------+ @@ -9425,33 +9352,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) bool u = extract32(insn, 29, 1); TCGv_i64 tcg_rd; - if (opcode >= 0x18) { - /* Floating point: U, size[1] and opcode indicate operation */ - int fpopcode = opcode | (extract32(size, 1, 1) << 5) | (u << 6); - switch (fpopcode) { - case 0x1f: /* FRECPS */ - case 0x3f: /* FRSQRTS */ - break; - default: - case 0x1b: /* FMULX */ - case 0x5d: /* FACGE */ - case 0x7d: /* FACGT */ - case 0x1c: /* FCMEQ */ - case 0x5c: /* FCMGE */ - case 0x7a: /* FABD */ - case 0x7c: /* FCMGT */ - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - handle_3same_float(s, extract32(size, 0, 1), 1, fpopcode, rd, rn, rm); - return; - } - switch (opcode) { case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ @@ -9568,80 +9468,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) write_fp_dreg(s, rd, tcg_rd); } -/* AdvSIMD scalar three same FP16 - * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0 - * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+ - * | 0 1 | U | 1 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd | - * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+ - * v: 0101 1110 0100 0000 0000 0100 0000 0000 => 5e400400 - * m: 1101 1111 0110 0000 1100 0100 0000 0000 => df60c400 - */ -static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, - uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 3); - int rm = extract32(insn, 16, 5); - bool u = extract32(insn, 29, 1); - bool a = extract32(insn, 23, 1); - int fpopcode = opcode | (a << 3) | (u << 4); - TCGv_ptr fpst; - TCGv_i32 tcg_op1; - TCGv_i32 tcg_op2; - TCGv_i32 tcg_res; - - switch (fpopcode) { - case 0x07: /* FRECPS */ - case 0x0f: /* FRSQRTS */ - break; - default: - case 0x03: /* FMULX */ - case 0x04: /* FCMEQ (reg) */ - case 0x14: /* FCMGE (reg) */ - case 0x15: /* FACGE */ - case 0x1a: /* FABD */ - case 0x1c: /* FCMGT (reg) */ - case 0x1d: /* FACGT */ - unallocated_encoding(s); - return; - } - - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - } - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(FPST_FPCR_F16); - - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - tcg_res = tcg_temp_new_i32(); - - switch (fpopcode) { - case 0x07: /* FRECPS */ - gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x0f: /* FRSQRTS */ - gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - case 0x03: /* FMULX */ - case 0x04: /* FCMEQ (reg) */ - case 0x14: /* FCMGE (reg) */ - case 0x15: /* FACGE */ - case 0x1a: /* FABD */ - case 0x1c: /* FCMGT (reg) */ - case 0x1d: /* FACGT */ - g_assert_not_reached(); - } - - write_fp_sreg(s, rd, tcg_res); -} - /* AdvSIMD scalar three same extra * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 * +-----+---+-----------+------+---+------+---+--------+---+----+----+ @@ -11114,7 +10940,7 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) /* Pairwise op subgroup of C3.6.16. * - * This is called directly or via the handle_3same_float for float pairwise + * This is called directly for float pairwise * operations where the opcode and size are calculated differently. */ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, @@ -11271,10 +11097,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - int datasize = is_q ? 128 : 64; - int esize = 32 << size; - int elements = datasize / esize; - if (size == 1 && !is_q) { unallocated_encoding(s); return; @@ -11293,13 +11115,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) handle_simd_3same_pair(s, is_q, 0, fpopcode, size ? MO_64 : MO_32, rn, rm, rd); return; - case 0x1f: /* FRECPS */ - case 0x3f: /* FRSQRTS */ - if (!fp_access_check(s)) { - return; - } - handle_3same_float(s, size, elements, fpopcode, rd, rn, rm); - return; case 0x1d: /* FMLAL */ case 0x3d: /* FMLSL */ @@ -11328,10 +11143,12 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x1b: /* FMULX */ case 0x1c: /* FCMEQ */ case 0x1e: /* FMAX */ + case 0x1f: /* FRECPS */ case 0x38: /* FMINNM */ case 0x39: /* FMLS */ case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ + case 0x3f: /* FRSQRTS */ case 0x5b: /* FMUL */ case 0x5c: /* FCMGE */ case 0x5d: /* FACGE */ @@ -11673,17 +11490,11 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) * together indicate the operation. */ int fpopcode = opcode | (a << 3) | (u << 4); - int datasize = is_q ? 128 : 64; - int elements = datasize / 16; bool pairwise; TCGv_ptr fpst; int pass; switch (fpopcode) { - case 0x7: /* FRECPS */ - case 0xf: /* FRSQRTS */ - pairwise = false; - break; case 0x10: /* FMAXNMP */ case 0x12: /* FADDP */ case 0x16: /* FMAXP */ @@ -11698,10 +11509,12 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0x3: /* FMULX */ case 0x4: /* FCMEQ */ case 0x6: /* FMAX */ + case 0x7: /* FRECPS */ case 0x8: /* FMINNM */ case 0x9: /* FMLS */ case 0xa: /* FSUB */ case 0xe: /* FMIN */ + case 0xf: /* FRSQRTS */ case 0x13: /* FMUL */ case 0x14: /* FCMGE */ case 0x15: /* FACGE */ @@ -11765,44 +11578,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_16); } } else { - for (pass = 0; pass < elements; pass++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op1, rn, pass, MO_16); - read_vec_element_i32(s, tcg_op2, rm, pass, MO_16); - - switch (fpopcode) { - case 0x7: /* FRECPS */ - gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FRSQRTS */ - gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - case 0x0: /* FMAXNM */ - case 0x1: /* FMLA */ - case 0x2: /* FADD */ - case 0x3: /* FMULX */ - case 0x4: /* FCMEQ */ - case 0x6: /* FMAX */ - case 0x8: /* FMINNM */ - case 0x9: /* FMLS */ - case 0xa: /* FSUB */ - case 0xe: /* FMIN */ - case 0x13: /* FMUL */ - case 0x14: /* FCMGE */ - case 0x15: /* FACGE */ - case 0x17: /* FDIV */ - case 0x1a: /* FABD */ - case 0x1c: /* FCMGT */ - case 0x1d: /* FACGT */ - g_assert_not_reached(); - } - - write_vec_element_i32(s, tcg_res, rd, pass, MO_16); - } + g_assert_not_reached(); } clear_vec_high(s, is_q, rd); @@ -13572,7 +13348,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, - { 0x5e400400, 0xdf60c400, disas_simd_scalar_three_reg_same_fp16 }, { 0x00000000, 0x00000000, NULL } }; From 57801ca0ea91d224a10c579d430d4e5426837802 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:42 -0700 Subject: [PATCH 0996/2884] target/arm: Convert FADDP to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-29-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 4 ++ target/arm/tcg/a64.decode | 12 +++++ target/arm/tcg/translate-a64.c | 87 ++++++++++++++++++++++++++-------- target/arm/tcg/vec_helper.c | 23 +++++++++ 4 files changed, 105 insertions(+), 21 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index ff6e3094f4..8441b49d1f 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1048,6 +1048,10 @@ DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" #include "tcg/helper-sve.h" diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 84cb38f1dd..d2a02365e1 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -29,6 +29,7 @@ &ri rd imm &rri_sf rd rn imm sf &i imm +&rr_e rd rn esz &rrr_e rd rn rm esz &rrx_e rd rn rm idx esz &qrr_e q rd rn esz @@ -36,6 +37,9 @@ &qrrx_e q rd rn rm idx esz &qrrrr_e q rd rn rm ra esz +@rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1 +@rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd + @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 @rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd @rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd @@ -737,6 +741,11 @@ FRECPS_s 0101 1110 0.1 ..... 11111 1 ..... ..... @rrr_sd FRSQRTS_s 0101 1110 110 ..... 00111 1 ..... ..... @rrr_h FRSQRTS_s 0101 1110 1.1 ..... 11111 1 ..... ..... @rrr_sd +### Advanced SIMD scalar pairwise + +FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h +FADDP_s 0111 1110 0.11 0000 1101 10 ..... ..... @rr_sd + ### Advanced SIMD three same FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h @@ -796,6 +805,9 @@ FRECPS_v 0.00 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd FRSQRTS_v 0.00 1110 110 ..... 00111 1 ..... ..... @qrrr_h FRSQRTS_v 0.00 1110 1.1 ..... 11111 1 ..... ..... @qrrr_sd +FADDP_v 0.10 1110 010 ..... 00010 1 ..... ..... @qrrr_h +FADDP_v 0.10 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a7537a5104..78949ab34f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5210,6 +5210,13 @@ static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = { }; TRANS(FRSQRTS_v, do_fp3_vector, a, f_vector_frsqrts) +static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = { + gen_helper_gvec_faddp_h, + gen_helper_gvec_faddp_s, + gen_helper_gvec_faddp_d, +}; +TRANS(FADDP_v, do_fp3_vector, a, f_vector_faddp) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5395,6 +5402,56 @@ static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) TRANS(FMLA_vi, do_fmla_vector_idx, a, false) TRANS(FMLS_vi, do_fmla_vector_idx, a, true) +/* + * Advanced SIMD scalar pairwise + */ + +static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rn, 1, MO_64); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, MO_32); + read_vec_element_i32(s, t1, a->rn, 1, MO_32); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, MO_16); + read_vec_element_i32(s, t1, a->rn, 1, MO_16); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + g_assert_not_reached(); + } + return true; +} + +TRANS(FADDP_s, do_fp3_scalar_pair, a, &f_scalar_fadd) /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the @@ -8357,7 +8414,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) fpst = NULL; break; case 0xc: /* FMAXNMP */ - case 0xd: /* FADDP */ case 0xf: /* FMAXP */ case 0x2c: /* FMINNMP */ case 0x2f: /* FMINP */ @@ -8380,6 +8436,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); break; default: + case 0xd: /* FADDP */ unallocated_encoding(s); return; } @@ -8399,9 +8456,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0xc: /* FMAXNMP */ gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0xd: /* FADDP */ - gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0xf: /* FMAXP */ gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -8412,6 +8466,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0xd: /* FADDP */ g_assert_not_reached(); } @@ -8429,9 +8484,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0xc: /* FMAXNMP */ gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0xd: /* FADDP */ - gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0xf: /* FMAXP */ gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -8442,6 +8494,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0xd: /* FADDP */ g_assert_not_reached(); } } else { @@ -8449,9 +8502,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0xc: /* FMAXNMP */ gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); break; - case 0xd: /* FADDP */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; case 0xf: /* FMAXP */ gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); break; @@ -8462,6 +8512,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); break; default: + case 0xd: /* FADDP */ g_assert_not_reached(); } } @@ -10982,9 +11033,6 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, case 0x58: /* FMAXNMP */ gen_helper_vfp_maxnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; - case 0x5a: /* FADDP */ - gen_helper_vfp_addd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; case 0x5e: /* FMAXP */ gen_helper_vfp_maxd(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; @@ -10995,6 +11043,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, gen_helper_vfp_mind(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; default: + case 0x5a: /* FADDP */ g_assert_not_reached(); } } @@ -11052,9 +11101,6 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, case 0x58: /* FMAXNMP */ gen_helper_vfp_maxnums(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; - case 0x5a: /* FADDP */ - gen_helper_vfp_adds(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; case 0x5e: /* FMAXP */ gen_helper_vfp_maxs(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; @@ -11065,6 +11111,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, gen_helper_vfp_mins(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; default: + case 0x5a: /* FADDP */ g_assert_not_reached(); } @@ -11104,7 +11151,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) switch (fpopcode) { case 0x58: /* FMAXNMP */ - case 0x5a: /* FADDP */ case 0x5e: /* FMAXP */ case 0x78: /* FMINNMP */ case 0x7e: /* FMINP */ @@ -11149,6 +11195,7 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x3f: /* FRSQRTS */ + case 0x5a: /* FADDP */ case 0x5b: /* FMUL */ case 0x5c: /* FCMGE */ case 0x5d: /* FACGE */ @@ -11496,7 +11543,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) switch (fpopcode) { case 0x10: /* FMAXNMP */ - case 0x12: /* FADDP */ case 0x16: /* FMAXP */ case 0x18: /* FMINNMP */ case 0x1e: /* FMINP */ @@ -11515,6 +11561,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) case 0xa: /* FSUB */ case 0xe: /* FMIN */ case 0xf: /* FRSQRTS */ + case 0x12: /* FADDP */ case 0x13: /* FMUL */ case 0x14: /* FCMGE */ case 0x15: /* FACGE */ @@ -11556,9 +11603,6 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_maxnumh(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; - case 0x12: /* FADDP */ - gen_helper_advsimd_addh(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; case 0x16: /* FMAXP */ gen_helper_advsimd_maxh(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; @@ -11570,6 +11614,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) gen_helper_advsimd_minh(tcg_res[pass], tcg_op1, tcg_op2, fpst); break; default: + case 0x12: /* FADDP */ g_assert_not_reached(); } } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index e9d7922f30..28989c7d7a 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2237,6 +2237,29 @@ DO_NEON_PAIRWISE(neon_pmin, min) #undef DO_NEON_PAIRWISE +#define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t half = oprsz / sizeof(TYPE) / 2; \ + TYPE *d = vd, *n = vn, *m = vm; \ + if (unlikely(d == m)) { \ + m = memcpy(&scratch, m, oprsz); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)], stat); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)], stat); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_3OP_PAIR(gvec_faddp_h, float16_add, float16, H2) +DO_3OP_PAIR(gvec_faddp_s, float32_add, float32, H4) +DO_3OP_PAIR(gvec_faddp_d, float64_add, float64, ) + #define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ { \ From a13f9fb5bf645fc1a6d63f346ab6c9072de6f7d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:43 -0700 Subject: [PATCH 0997/2884] target/arm: Convert FMAXP, FMINP, FMAXNMP, FMINNMP to decodetree These are the last instructions within disas_simd_three_reg_same_fp16, so remove it. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 16 ++ target/arm/tcg/a64.decode | 24 +++ target/arm/tcg/translate-a64.c | 296 ++++++--------------------------- target/arm/tcg/vec_helper.c | 16 ++ 4 files changed, 107 insertions(+), 245 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 8441b49d1f..3268477329 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1052,6 +1052,22 @@ DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" #include "tcg/helper-sve.h" diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index d2a02365e1..43557fdccc 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -746,6 +746,18 @@ FRSQRTS_s 0101 1110 1.1 ..... 11111 1 ..... ..... @rrr_sd FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h FADDP_s 0111 1110 0.11 0000 1101 10 ..... ..... @rr_sd +FMAXP_s 0101 1110 0011 0000 1111 10 ..... ..... @rr_h +FMAXP_s 0111 1110 0.11 0000 1111 10 ..... ..... @rr_sd + +FMINP_s 0101 1110 1011 0000 1111 10 ..... ..... @rr_h +FMINP_s 0111 1110 1.11 0000 1111 10 ..... ..... @rr_sd + +FMAXNMP_s 0101 1110 0011 0000 1100 10 ..... ..... @rr_h +FMAXNMP_s 0111 1110 0.11 0000 1100 10 ..... ..... @rr_sd + +FMINNMP_s 0101 1110 1011 0000 1100 10 ..... ..... @rr_h +FMINNMP_s 0111 1110 1.11 0000 1100 10 ..... ..... @rr_sd + ### Advanced SIMD three same FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h @@ -808,6 +820,18 @@ FRSQRTS_v 0.00 1110 1.1 ..... 11111 1 ..... ..... @qrrr_sd FADDP_v 0.10 1110 010 ..... 00010 1 ..... ..... @qrrr_h FADDP_v 0.10 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd +FMAXP_v 0.10 1110 010 ..... 00110 1 ..... ..... @qrrr_h +FMAXP_v 0.10 1110 0.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMINP_v 0.10 1110 110 ..... 00110 1 ..... ..... @qrrr_h +FMINP_v 0.10 1110 1.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMAXNMP_v 0.10 1110 010 ..... 00000 1 ..... ..... @qrrr_h +FMAXNMP_v 0.10 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd + +FMINNMP_v 0.10 1110 110 ..... 00000 1 ..... ..... @qrrr_h +FMINNMP_v 0.10 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 78949ab34f..07415bd285 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5217,6 +5217,34 @@ static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = { }; TRANS(FADDP_v, do_fp3_vector, a, f_vector_faddp) +static gen_helper_gvec_3_ptr * const f_vector_fmaxp[3] = { + gen_helper_gvec_fmaxp_h, + gen_helper_gvec_fmaxp_s, + gen_helper_gvec_fmaxp_d, +}; +TRANS(FMAXP_v, do_fp3_vector, a, f_vector_fmaxp) + +static gen_helper_gvec_3_ptr * const f_vector_fminp[3] = { + gen_helper_gvec_fminp_h, + gen_helper_gvec_fminp_s, + gen_helper_gvec_fminp_d, +}; +TRANS(FMINP_v, do_fp3_vector, a, f_vector_fminp) + +static gen_helper_gvec_3_ptr * const f_vector_fmaxnmp[3] = { + gen_helper_gvec_fmaxnump_h, + gen_helper_gvec_fmaxnump_s, + gen_helper_gvec_fmaxnump_d, +}; +TRANS(FMAXNMP_v, do_fp3_vector, a, f_vector_fmaxnmp) + +static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = { + gen_helper_gvec_fminnump_h, + gen_helper_gvec_fminnump_s, + gen_helper_gvec_fminnump_d, +}; +TRANS(FMINNMP_v, do_fp3_vector, a, f_vector_fminnmp) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5452,6 +5480,10 @@ static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) } TRANS(FADDP_s, do_fp3_scalar_pair, a, &f_scalar_fadd) +TRANS(FMAXP_s, do_fp3_scalar_pair, a, &f_scalar_fmax) +TRANS(FMINP_s, do_fp3_scalar_pair, a, &f_scalar_fmin) +TRANS(FMAXNMP_s, do_fp3_scalar_pair, a, &f_scalar_fmaxnm) +TRANS(FMINNMP_s, do_fp3_scalar_pair, a, &f_scalar_fminnm) /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the @@ -8393,7 +8425,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) int opcode = extract32(insn, 12, 5); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - TCGv_ptr fpst; /* For some ops (the FP ones), size[1] is part of the encoding. * For ADDP strictly it is not but size[1] is always 1 for valid @@ -8410,33 +8441,13 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) if (!fp_access_check(s)) { return; } - - fpst = NULL; break; + default: case 0xc: /* FMAXNMP */ + case 0xd: /* FADDP */ case 0xf: /* FMAXP */ case 0x2c: /* FMINNMP */ case 0x2f: /* FMINP */ - /* FP op, size[0] is 32 or 64 bit*/ - if (!u) { - if ((size & 1) || !dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } else { - size = MO_16; - } - } else { - size = extract32(size, 0, 1) ? MO_64 : MO_32; - } - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - break; - default: - case 0xd: /* FADDP */ unallocated_encoding(s); return; } @@ -8453,71 +8464,18 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0x3b: /* ADDP */ tcg_gen_add_i64(tcg_res, tcg_op1, tcg_op2); break; - case 0xc: /* FMAXNMP */ - gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); - break; default: + case 0xc: /* FMAXNMP */ case 0xd: /* FADDP */ + case 0xf: /* FMAXP */ + case 0x2c: /* FMINNMP */ + case 0x2f: /* FMINP */ g_assert_not_reached(); } write_fp_dreg(s, rd, tcg_res); } else { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op1, rn, 0, size); - read_vec_element_i32(s, tcg_op2, rn, 1, size); - - if (size == MO_16) { - switch (opcode) { - case 0xc: /* FMAXNMP */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - case 0xd: /* FADDP */ - g_assert_not_reached(); - } - } else { - switch (opcode) { - case 0xc: /* FMAXNMP */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - case 0xd: /* FADDP */ - g_assert_not_reached(); - } - } - - write_fp_sreg(s, rd, tcg_res); + g_assert_not_reached(); } } @@ -10997,16 +10955,8 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, int size, int rn, int rm, int rd) { - TCGv_ptr fpst; int pass; - /* Floating point operations need fpst */ - if (opcode >= 0x58) { - fpst = fpstatus_ptr(FPST_FPCR); - } else { - fpst = NULL; - } - if (!fp_access_check(s)) { return; } @@ -11030,20 +10980,12 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, case 0x17: /* ADDP */ tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2); break; - case 0x58: /* FMAXNMP */ - gen_helper_vfp_maxnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x5e: /* FMAXP */ - gen_helper_vfp_maxd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x78: /* FMINNMP */ - gen_helper_vfp_minnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x7e: /* FMINP */ - gen_helper_vfp_mind(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; default: + case 0x58: /* FMAXNMP */ case 0x5a: /* FADDP */ + case 0x5e: /* FMAXP */ + case 0x78: /* FMINNMP */ + case 0x7e: /* FMINP */ g_assert_not_reached(); } } @@ -11097,21 +11039,12 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, genfn = fns[size][u]; break; } - /* The FP operations are all on single floats (32 bit) */ - case 0x58: /* FMAXNMP */ - gen_helper_vfp_maxnums(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x5e: /* FMAXP */ - gen_helper_vfp_maxs(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x78: /* FMINNMP */ - gen_helper_vfp_minnums(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x7e: /* FMINP */ - gen_helper_vfp_mins(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; default: + case 0x58: /* FMAXNMP */ case 0x5a: /* FADDP */ + case 0x5e: /* FMAXP */ + case 0x78: /* FMINNMP */ + case 0x7e: /* FMINP */ g_assert_not_reached(); } @@ -11150,18 +11083,6 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) } switch (fpopcode) { - case 0x58: /* FMAXNMP */ - case 0x5e: /* FMAXP */ - case 0x78: /* FMINNMP */ - case 0x7e: /* FMINP */ - if (size && !is_q) { - unallocated_encoding(s); - return; - } - handle_simd_3same_pair(s, is_q, 0, fpopcode, size ? MO_64 : MO_32, - rn, rm, rd); - return; - case 0x1d: /* FMLAL */ case 0x3d: /* FMLSL */ case 0x59: /* FMLAL2 */ @@ -11195,14 +11116,18 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) case 0x3a: /* FSUB */ case 0x3e: /* FMIN */ case 0x3f: /* FRSQRTS */ + case 0x58: /* FMAXNMP */ case 0x5a: /* FADDP */ case 0x5b: /* FMUL */ case 0x5c: /* FCMGE */ case 0x5d: /* FACGE */ + case 0x5e: /* FMAXP */ case 0x5f: /* FDIV */ + case 0x78: /* FMINNMP */ case 0x7a: /* FABD */ case 0x7d: /* FACGT */ case 0x7c: /* FCMGT */ + case 0x7e: /* FMINP */ unallocated_encoding(s); return; } @@ -11511,124 +11436,6 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) } } -/* - * Advanced SIMD three same (ARMv8.2 FP16 variants) - * - * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0 - * +---+---+---+-----------+---------+------+-----+--------+---+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd | - * +---+---+---+-----------+---------+------+-----+--------+---+------+------+ - * - * This includes FMULX, FCMEQ (register), FRECPS, FRSQRTS, FCMGE - * (register), FACGE, FABD, FCMGT (register) and FACGT. - * - */ -static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 11, 3); - int u = extract32(insn, 29, 1); - int a = extract32(insn, 23, 1); - int is_q = extract32(insn, 30, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - /* - * For these floating point ops, the U, a and opcode bits - * together indicate the operation. - */ - int fpopcode = opcode | (a << 3) | (u << 4); - bool pairwise; - TCGv_ptr fpst; - int pass; - - switch (fpopcode) { - case 0x10: /* FMAXNMP */ - case 0x16: /* FMAXP */ - case 0x18: /* FMINNMP */ - case 0x1e: /* FMINP */ - pairwise = true; - break; - default: - case 0x0: /* FMAXNM */ - case 0x1: /* FMLA */ - case 0x2: /* FADD */ - case 0x3: /* FMULX */ - case 0x4: /* FCMEQ */ - case 0x6: /* FMAX */ - case 0x7: /* FRECPS */ - case 0x8: /* FMINNM */ - case 0x9: /* FMLS */ - case 0xa: /* FSUB */ - case 0xe: /* FMIN */ - case 0xf: /* FRSQRTS */ - case 0x12: /* FADDP */ - case 0x13: /* FMUL */ - case 0x14: /* FCMGE */ - case 0x15: /* FACGE */ - case 0x17: /* FDIV */ - case 0x1a: /* FABD */ - case 0x1c: /* FCMGT */ - case 0x1d: /* FACGT */ - unallocated_encoding(s); - return; - } - - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(FPST_FPCR_F16); - - if (pairwise) { - int maxpass = is_q ? 8 : 4; - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res[8]; - - for (pass = 0; pass < maxpass; pass++) { - int passreg = pass < (maxpass / 2) ? rn : rm; - int passelt = (pass << 1) & (maxpass - 1); - - read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_16); - read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_16); - tcg_res[pass] = tcg_temp_new_i32(); - - switch (fpopcode) { - case 0x10: /* FMAXNMP */ - gen_helper_advsimd_maxnumh(tcg_res[pass], tcg_op1, tcg_op2, - fpst); - break; - case 0x16: /* FMAXP */ - gen_helper_advsimd_maxh(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x18: /* FMINNMP */ - gen_helper_advsimd_minnumh(tcg_res[pass], tcg_op1, tcg_op2, - fpst); - break; - case 0x1e: /* FMINP */ - gen_helper_advsimd_minh(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - default: - case 0x12: /* FADDP */ - g_assert_not_reached(); - } - } - - for (pass = 0; pass < maxpass; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_16); - } - } else { - g_assert_not_reached(); - } - - clear_vec_high(s, is_q, rd); -} - /* AdvSIMD three same extra * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ @@ -13391,7 +13198,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x00000000, 0x00000000, NULL } }; diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 28989c7d7a..79e1fdcaa9 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2260,6 +2260,22 @@ DO_3OP_PAIR(gvec_faddp_h, float16_add, float16, H2) DO_3OP_PAIR(gvec_faddp_s, float32_add, float32, H4) DO_3OP_PAIR(gvec_faddp_d, float64_add, float64, ) +DO_3OP_PAIR(gvec_fmaxp_h, float16_max, float16, H2) +DO_3OP_PAIR(gvec_fmaxp_s, float32_max, float32, H4) +DO_3OP_PAIR(gvec_fmaxp_d, float64_max, float64, ) + +DO_3OP_PAIR(gvec_fminp_h, float16_min, float16, H2) +DO_3OP_PAIR(gvec_fminp_s, float32_min, float32, H4) +DO_3OP_PAIR(gvec_fminp_d, float64_min, float64, ) + +DO_3OP_PAIR(gvec_fmaxnump_h, float16_maxnum, float16, H2) +DO_3OP_PAIR(gvec_fmaxnump_s, float32_maxnum, float32, H4) +DO_3OP_PAIR(gvec_fmaxnump_d, float64_maxnum, float64, ) + +DO_3OP_PAIR(gvec_fminnump_h, float16_minnum, float16, H2) +DO_3OP_PAIR(gvec_fminnump_s, float32_minnum, float32, H4) +DO_3OP_PAIR(gvec_fminnump_d, float64_minnum, float64, ) + #define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ { \ From c43a23e1aa2cdcef058a3486d23f5206908023e1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:44 -0700 Subject: [PATCH 0998/2884] target/arm: Use gvec for neon faddp, fmaxp, fminp Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-31-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 7 ----- target/arm/tcg/translate-neon.c | 55 ++------------------------------- target/arm/tcg/vec_helper.c | 45 --------------------------- 3 files changed, 3 insertions(+), 104 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 3268477329..065460ea80 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -650,13 +650,6 @@ DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_paddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pmaxh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pminh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_padds, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pmaxs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pmins, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 144f18ba22..2326a05a0a 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -1144,6 +1144,9 @@ DO_3S_FP_GVEC(VFMA, gen_helper_gvec_vfma_s, gen_helper_gvec_vfma_h) DO_3S_FP_GVEC(VFMS, gen_helper_gvec_vfms_s, gen_helper_gvec_vfms_h) DO_3S_FP_GVEC(VRECPS, gen_helper_gvec_recps_nf_s, gen_helper_gvec_recps_nf_h) DO_3S_FP_GVEC(VRSQRTS, gen_helper_gvec_rsqrts_nf_s, gen_helper_gvec_rsqrts_nf_h) +DO_3S_FP_GVEC(VPADD, gen_helper_gvec_faddp_s, gen_helper_gvec_faddp_h) +DO_3S_FP_GVEC(VPMAX, gen_helper_gvec_fmaxp_s, gen_helper_gvec_fmaxp_h) +DO_3S_FP_GVEC(VPMIN, gen_helper_gvec_fminp_s, gen_helper_gvec_fminp_h) WRAP_FP_GVEC(gen_VMAXNM_fp32_3s, FPST_STD, gen_helper_gvec_fmaxnum_s) WRAP_FP_GVEC(gen_VMAXNM_fp16_3s, FPST_STD_F16, gen_helper_gvec_fmaxnum_h) @@ -1180,58 +1183,6 @@ static bool trans_VMINNM_fp_3s(DisasContext *s, arg_3same *a) return do_3same(s, a, gen_VMINNM_fp32_3s); } -static bool do_3same_fp_pair(DisasContext *s, arg_3same *a, - gen_helper_gvec_3_ptr *fn) -{ - /* FP pairwise operations */ - TCGv_ptr fpstatus; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - assert(a->q == 0); /* enforced by decode patterns */ - - - fpstatus = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd), - vfp_reg_offset(1, a->vn), - vfp_reg_offset(1, a->vm), - fpstatus, 8, 8, 0, fn); - - return true; -} - -/* - * For all the functions using this macro, size == 1 means fp16, - * which is an architecture extension we don't implement yet. - */ -#define DO_3S_FP_PAIR(INSN,FUNC) \ - static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size == MO_16) { \ - if (!dc_isar_feature(aa32_fp16_arith, s)) { \ - return false; \ - } \ - return do_3same_fp_pair(s, a, FUNC##h); \ - } \ - return do_3same_fp_pair(s, a, FUNC##s); \ - } - -DO_3S_FP_PAIR(VPADD, gen_helper_neon_padd) -DO_3S_FP_PAIR(VPMAX, gen_helper_neon_pmax) -DO_3S_FP_PAIR(VPMIN, gen_helper_neon_pmin) - static bool do_vector_2sh(DisasContext *s, arg_2reg_shift *a, GVecGen2iFn *fn) { /* Handle a 2-reg-shift insn which can be vectorized. */ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 79e1fdcaa9..26a9ca9c14 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2192,51 +2192,6 @@ DO_ABA(gvec_uaba_d, uint64_t) #undef DO_ABA -#define DO_NEON_PAIRWISE(NAME, OP) \ - void HELPER(NAME##s)(void *vd, void *vn, void *vm, \ - void *stat, uint32_t oprsz) \ - { \ - float_status *fpst = stat; \ - float32 *d = vd; \ - float32 *n = vn; \ - float32 *m = vm; \ - float32 r0, r1; \ - \ - /* Read all inputs before writing outputs in case vm == vd */ \ - r0 = float32_##OP(n[H4(0)], n[H4(1)], fpst); \ - r1 = float32_##OP(m[H4(0)], m[H4(1)], fpst); \ - \ - d[H4(0)] = r0; \ - d[H4(1)] = r1; \ - } \ - \ - void HELPER(NAME##h)(void *vd, void *vn, void *vm, \ - void *stat, uint32_t oprsz) \ - { \ - float_status *fpst = stat; \ - float16 *d = vd; \ - float16 *n = vn; \ - float16 *m = vm; \ - float16 r0, r1, r2, r3; \ - \ - /* Read all inputs before writing outputs in case vm == vd */ \ - r0 = float16_##OP(n[H2(0)], n[H2(1)], fpst); \ - r1 = float16_##OP(n[H2(2)], n[H2(3)], fpst); \ - r2 = float16_##OP(m[H2(0)], m[H2(1)], fpst); \ - r3 = float16_##OP(m[H2(2)], m[H2(3)], fpst); \ - \ - d[H2(0)] = r0; \ - d[H2(1)] = r1; \ - d[H2(2)] = r2; \ - d[H2(3)] = r3; \ - } - -DO_NEON_PAIRWISE(neon_padd, add) -DO_NEON_PAIRWISE(neon_pmax, max) -DO_NEON_PAIRWISE(neon_pmin, min) - -#undef DO_NEON_PAIRWISE - #define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ { \ From a7e4eec6fbcc12aabc50486f50097950036e1faf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:45 -0700 Subject: [PATCH 0999/2884] target/arm: Convert ADDP to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-32-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 5 ++ target/arm/tcg/a64.decode | 6 ++ target/arm/tcg/gengvec.c | 12 ++++ target/arm/tcg/translate-a64.c | 128 ++++++--------------------------- target/arm/tcg/translate.h | 3 + target/arm/tcg/vec_helper.c | 30 ++++++++ 6 files changed, 77 insertions(+), 107 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 065460ea80..d3579a101f 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1061,6 +1061,11 @@ DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" #include "tcg/helper-sve.h" diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 43557fdccc..84f5bcc0e0 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -38,6 +38,7 @@ &qrrrr_e q rd rn rm ra esz @rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1 +@rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 @rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 @@ -56,6 +57,7 @@ @qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1 @qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd +@qrrr_e . q:1 ...... esz:2 . rm:5 ...... rn:5 rd:5 &qrrr_e @qrrx_h . q:1 .. .... .. .. rm:4 .... . . rn:5 rd:5 \ &qrrx_e esz=1 idx=%hlm @@ -758,6 +760,8 @@ FMAXNMP_s 0111 1110 0.11 0000 1100 10 ..... ..... @rr_sd FMINNMP_s 0101 1110 1011 0000 1100 10 ..... ..... @rr_h FMINNMP_s 0111 1110 1.11 0000 1100 10 ..... ..... @rr_sd +ADDP_s 0101 1110 1111 0001 1011 10 ..... ..... @rr_d + ### Advanced SIMD three same FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h @@ -832,6 +836,8 @@ FMAXNMP_v 0.10 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd FMINNMP_v 0.10 1110 110 ..... 00000 1 ..... ..... @qrrr_h FMINNMP_v 0.10 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd +ADDP_v 0.00 1110 ..1 ..... 10111 1 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 7a1856253f..f010dd5a0e 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1610,3 +1610,15 @@ void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, }; tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } + +void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_addp_b, + gen_helper_gvec_addp_h, + gen_helper_gvec_addp_s, + gen_helper_gvec_addp_d, + }; + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 07415bd285..b8add91112 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5245,6 +5245,8 @@ static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = { }; TRANS(FMINNMP_v, do_fp3_vector, a, f_vector_fminnmp) +TRANS(ADDP_v, do_gvec_fn3, a, gen_gvec_addp) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5485,6 +5487,20 @@ TRANS(FMINP_s, do_fp3_scalar_pair, a, &f_scalar_fmin) TRANS(FMAXNMP_s, do_fp3_scalar_pair, a, &f_scalar_fmaxnm) TRANS(FMINNMP_s, do_fp3_scalar_pair, a, &f_scalar_fminnm) +static bool trans_ADDP_s(DisasContext *s, arg_rr_e *a) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rn, 1, MO_64); + tcg_gen_add_i64(t0, t0, t1); + write_fp_dreg(s, a->rd, t0); + } + return true; +} + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -8412,73 +8428,6 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) } } -/* AdvSIMD scalar pairwise - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd | - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) -{ - int u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - /* For some ops (the FP ones), size[1] is part of the encoding. - * For ADDP strictly it is not but size[1] is always 1 for valid - * encodings. - */ - opcode |= (extract32(size, 1, 1) << 5); - - switch (opcode) { - case 0x3b: /* ADDP */ - if (u || size != 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - break; - default: - case 0xc: /* FMAXNMP */ - case 0xd: /* FADDP */ - case 0xf: /* FMAXP */ - case 0x2c: /* FMINNMP */ - case 0x2f: /* FMINP */ - unallocated_encoding(s); - return; - } - - if (size == MO_64) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, 0, MO_64); - read_vec_element(s, tcg_op2, rn, 1, MO_64); - - switch (opcode) { - case 0x3b: /* ADDP */ - tcg_gen_add_i64(tcg_res, tcg_op1, tcg_op2); - break; - default: - case 0xc: /* FMAXNMP */ - case 0xd: /* FADDP */ - case 0xf: /* FMAXP */ - case 0x2c: /* FMINNMP */ - case 0x2f: /* FMINP */ - g_assert_not_reached(); - } - - write_fp_dreg(s, rd, tcg_res); - } else { - g_assert_not_reached(); - } -} - /* * Common SSHR[RA]/USHR[RA] - Shift right (optional rounding/accumulate) * @@ -10965,34 +10914,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, * adjacent elements being operated on to produce an element in the result. */ if (size == 3) { - TCGv_i64 tcg_res[2]; - - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - int passreg = (pass == 0) ? rn : rm; - - read_vec_element(s, tcg_op1, passreg, 0, MO_64); - read_vec_element(s, tcg_op2, passreg, 1, MO_64); - tcg_res[pass] = tcg_temp_new_i64(); - - switch (opcode) { - case 0x17: /* ADDP */ - tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2); - break; - default: - case 0x58: /* FMAXNMP */ - case 0x5a: /* FADDP */ - case 0x5e: /* FMAXP */ - case 0x78: /* FMINNMP */ - case 0x7e: /* FMINP */ - g_assert_not_reached(); - } - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } + g_assert_not_reached(); } else { int maxpass = is_q ? 4 : 2; TCGv_i32 tcg_res[4]; @@ -11009,16 +10931,6 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, tcg_res[pass] = tcg_temp_new_i32(); switch (opcode) { - case 0x17: /* ADDP */ - { - static NeonGenTwoOpFn * const fns[3] = { - gen_helper_neon_padd_u8, - gen_helper_neon_padd_u16, - tcg_gen_add_i32, - }; - genfn = fns[size]; - break; - } case 0x14: /* SMAXP, UMAXP */ { static NeonGenTwoOpFn * const fns[3][2] = { @@ -11040,6 +10952,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, break; } default: + case 0x17: /* ADDP */ case 0x58: /* FMAXNMP */ case 0x5a: /* FADDP */ case 0x5e: /* FMAXP */ @@ -11401,7 +11314,6 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) case 0x3: /* logic ops */ disas_simd_3same_logic(s, insn); break; - case 0x17: /* ADDP */ case 0x14: /* SMAXP, UMAXP */ case 0x15: /* SMINP, UMINP */ { @@ -11433,6 +11345,9 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) default: disas_simd_3same_int(s, insn); break; + case 0x17: /* ADDP */ + unallocated_encoding(s); + break; } } @@ -13195,7 +13110,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x5e008400, 0xdf208400, disas_simd_scalar_three_reg_same_extra }, { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, - { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index b05a9eb668..04771f483b 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -514,6 +514,9 @@ void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + /* * Forward to the isar_feature_* tests given a DisasContext pointer. */ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 26a9ca9c14..5069899415 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2231,6 +2231,36 @@ DO_3OP_PAIR(gvec_fminnump_h, float16_minnum, float16, H2) DO_3OP_PAIR(gvec_fminnump_s, float32_minnum, float32, H4) DO_3OP_PAIR(gvec_fminnump_d, float64_minnum, float64, ) +#undef DO_3OP_PAIR + +#define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t half = oprsz / sizeof(TYPE) / 2; \ + TYPE *d = vd, *n = vn, *m = vm; \ + if (unlikely(d == m)) { \ + m = memcpy(&scratch, m, oprsz); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)]); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)]); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +#define ADD(A, B) (A + B) +DO_3OP_PAIR(gvec_addp_b, ADD, uint8_t, H1) +DO_3OP_PAIR(gvec_addp_h, ADD, uint16_t, H2) +DO_3OP_PAIR(gvec_addp_s, ADD, uint32_t, H4) +DO_3OP_PAIR(gvec_addp_d, ADD, uint64_t, ) +#undef ADD + +#undef DO_3OP_PAIR + #define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ { \ From a11e54ed298f97200f3c1a1ecccf058aeecab714 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:46 -0700 Subject: [PATCH 1000/2884] target/arm: Use gvec for neon padd Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-33-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 -- target/arm/tcg/neon_helper.c | 5 ----- target/arm/tcg/translate-neon.c | 3 +-- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index d3579a101f..51ed49aa50 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -354,8 +354,6 @@ DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) DEF_HELPER_2(neon_add_u8, i32, i32, i32) DEF_HELPER_2(neon_add_u16, i32, i32, i32) -DEF_HELPER_2(neon_padd_u8, i32, i32, i32) -DEF_HELPER_2(neon_padd_u16, i32, i32, i32) DEF_HELPER_2(neon_sub_u8, i32, i32, i32) DEF_HELPER_2(neon_sub_u16, i32, i32, i32) DEF_HELPER_2(neon_mul_u8, i32, i32, i32) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index bc6c4a54e9..a0b51c8809 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -745,11 +745,6 @@ uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b) return (a + b) ^ mask; } -#define NEON_FN(dest, src1, src2) dest = src1 + src2 -NEON_POP(padd_u8, neon_u8, 4) -NEON_POP(padd_u16, neon_u16, 2) -#undef NEON_FN - #define NEON_FN(dest, src1, src2) dest = src1 - src2 NEON_VOP(sub_u8, neon_u8, 4) NEON_VOP(sub_u16, neon_u16, 2) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 2326a05a0a..6c5a7a98e1 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -830,6 +830,7 @@ DO_3SAME_NO_SZ_3(VABD_S, gen_gvec_sabd) DO_3SAME_NO_SZ_3(VABA_S, gen_gvec_saba) DO_3SAME_NO_SZ_3(VABD_U, gen_gvec_uabd) DO_3SAME_NO_SZ_3(VABA_U, gen_gvec_uaba) +DO_3SAME_NO_SZ_3(VPADD, gen_gvec_addp) #define DO_3SAME_CMP(INSN, COND) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ @@ -1070,13 +1071,11 @@ static bool do_3same_pair(DisasContext *s, arg_3same *a, NeonGenTwoOpFn *fn) #define gen_helper_neon_pmax_u32 tcg_gen_umax_i32 #define gen_helper_neon_pmin_s32 tcg_gen_smin_i32 #define gen_helper_neon_pmin_u32 tcg_gen_umin_i32 -#define gen_helper_neon_padd_u32 tcg_gen_add_i32 DO_3SAME_PAIR(VPMAX_S, pmax_s) DO_3SAME_PAIR(VPMIN_S, pmin_s) DO_3SAME_PAIR(VPMAX_U, pmax_u) DO_3SAME_PAIR(VPMIN_U, pmin_u) -DO_3SAME_PAIR(VPADD, padd_u) #define DO_3SAME_VQDMULH(INSN, FUNC) \ WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \ From 28b5451bec8d68af3f93e7f5e18c86ad1b47930f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:47 -0700 Subject: [PATCH 1001/2884] target/arm: Convert SMAXP, SMINP, UMAXP, UMINP to decodetree These are the last instructions within handle_simd_3same_pair so remove it. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 16 +++++ target/arm/tcg/a64.decode | 4 ++ target/arm/tcg/gengvec.c | 48 +++++++++++++ target/arm/tcg/translate-a64.c | 119 +++++---------------------------- target/arm/tcg/translate.h | 8 +++ target/arm/tcg/vec_helper.c | 16 +++++ 6 files changed, 109 insertions(+), 102 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 51ed49aa50..f830531dd3 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1064,6 +1064,22 @@ DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_addp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_addp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" #include "tcg/helper-sve.h" diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 84f5bcc0e0..22dfe8568d 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -837,6 +837,10 @@ FMINNMP_v 0.10 1110 110 ..... 00000 1 ..... ..... @qrrr_h FMINNMP_v 0.10 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd ADDP_v 0.00 1110 ..1 ..... 10111 1 ..... ..... @qrrr_e +SMAXP_v 0.00 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e +SMINP_v 0.00 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e +UMAXP_v 0.10 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e +UMINP_v 0.10 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index f010dd5a0e..22c9d17dce 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1622,3 +1622,51 @@ void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, }; tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); } + +void gen_gvec_smaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_smaxp_b, + gen_helper_gvec_smaxp_h, + gen_helper_gvec_smaxp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_sminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_sminp_b, + gen_helper_gvec_sminp_h, + gen_helper_gvec_sminp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_umaxp_b, + gen_helper_gvec_umaxp_h, + gen_helper_gvec_umaxp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_uminp_b, + gen_helper_gvec_uminp_h, + gen_helper_gvec_uminp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b8add91112..9fe70a939b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1352,6 +1352,17 @@ static bool do_gvec_fn3(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) return true; } +static bool do_gvec_fn3_no64(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) +{ + if (a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn3(s, a->q, a->rd, a->rn, a->rm, fn, a->esz); + } + return true; +} + static bool do_gvec_fn4(DisasContext *s, arg_qrrrr_e *a, GVecGen4Fn *fn) { if (!a->q && a->esz == MO_64) { @@ -5246,6 +5257,10 @@ static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = { TRANS(FMINNMP_v, do_fp3_vector, a, f_vector_fminnmp) TRANS(ADDP_v, do_gvec_fn3, a, gen_gvec_addp) +TRANS(SMAXP_v, do_gvec_fn3_no64, a, gen_gvec_smaxp) +TRANS(SMINP_v, do_gvec_fn3_no64, a, gen_gvec_sminp) +TRANS(UMAXP_v, do_gvec_fn3_no64, a, gen_gvec_umaxp) +TRANS(UMINP_v, do_gvec_fn3_no64, a, gen_gvec_uminp) /* * Advanced SIMD scalar/vector x indexed element @@ -10896,84 +10911,6 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) } } -/* Pairwise op subgroup of C3.6.16. - * - * This is called directly for float pairwise - * operations where the opcode and size are calculated differently. - */ -static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, - int size, int rn, int rm, int rd) -{ - int pass; - - if (!fp_access_check(s)) { - return; - } - - /* These operations work on the concatenated rm:rn, with each pair of - * adjacent elements being operated on to produce an element in the result. - */ - if (size == 3) { - g_assert_not_reached(); - } else { - int maxpass = is_q ? 4 : 2; - TCGv_i32 tcg_res[4]; - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - NeonGenTwoOpFn *genfn = NULL; - int passreg = pass < (maxpass / 2) ? rn : rm; - int passelt = (is_q && (pass & 1)) ? 2 : 0; - - read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_32); - read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_32); - tcg_res[pass] = tcg_temp_new_i32(); - - switch (opcode) { - case 0x14: /* SMAXP, UMAXP */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_pmax_s8, gen_helper_neon_pmax_u8 }, - { gen_helper_neon_pmax_s16, gen_helper_neon_pmax_u16 }, - { tcg_gen_smax_i32, tcg_gen_umax_i32 }, - }; - genfn = fns[size][u]; - break; - } - case 0x15: /* SMINP, UMINP */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_pmin_s8, gen_helper_neon_pmin_u8 }, - { gen_helper_neon_pmin_s16, gen_helper_neon_pmin_u16 }, - { tcg_gen_smin_i32, tcg_gen_umin_i32 }, - }; - genfn = fns[size][u]; - break; - } - default: - case 0x17: /* ADDP */ - case 0x58: /* FMAXNMP */ - case 0x5a: /* FADDP */ - case 0x5e: /* FMAXP */ - case 0x78: /* FMINNMP */ - case 0x7e: /* FMINP */ - g_assert_not_reached(); - } - - /* FP ops called directly, otherwise call now */ - if (genfn) { - genfn(tcg_res[pass], tcg_op1, tcg_op2); - } - } - - for (pass = 0; pass < maxpass; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); - } - clear_vec_high(s, is_q, rd); - } -} - /* Floating point op subgroup of C3.6.16. */ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) { @@ -11314,30 +11251,6 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) case 0x3: /* logic ops */ disas_simd_3same_logic(s, insn); break; - case 0x14: /* SMAXP, UMAXP */ - case 0x15: /* SMINP, UMINP */ - { - /* Pairwise operations */ - int is_q = extract32(insn, 30, 1); - int u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - if (opcode == 0x17) { - if (u || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - } else { - if (size == 3) { - unallocated_encoding(s); - return; - } - } - handle_simd_3same_pair(s, is_q, u, opcode, size, rn, rm, rd); - break; - } case 0x18 ... 0x31: /* floating point ops, sz[1] and U are part of opcode */ disas_simd_3same_float(s, insn); @@ -11345,6 +11258,8 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) default: disas_simd_3same_int(s, insn); break; + case 0x14: /* SMAXP, UMAXP */ + case 0x15: /* SMINP, UMINP */ case 0x17: /* ADDP */ unallocated_encoding(s); break; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 04771f483b..3abdbedfe5 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -516,6 +516,14 @@ void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_smaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); /* * Forward to the isar_feature_* tests given a DisasContext pointer. diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 5069899415..56fea14edb 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2259,6 +2259,22 @@ DO_3OP_PAIR(gvec_addp_s, ADD, uint32_t, H4) DO_3OP_PAIR(gvec_addp_d, ADD, uint64_t, ) #undef ADD +DO_3OP_PAIR(gvec_smaxp_b, MAX, int8_t, H1) +DO_3OP_PAIR(gvec_smaxp_h, MAX, int16_t, H2) +DO_3OP_PAIR(gvec_smaxp_s, MAX, int32_t, H4) + +DO_3OP_PAIR(gvec_umaxp_b, MAX, uint8_t, H1) +DO_3OP_PAIR(gvec_umaxp_h, MAX, uint16_t, H2) +DO_3OP_PAIR(gvec_umaxp_s, MAX, uint32_t, H4) + +DO_3OP_PAIR(gvec_sminp_b, MIN, int8_t, H1) +DO_3OP_PAIR(gvec_sminp_h, MIN, int16_t, H2) +DO_3OP_PAIR(gvec_sminp_s, MIN, int32_t, H4) + +DO_3OP_PAIR(gvec_uminp_b, MIN, uint8_t, H1) +DO_3OP_PAIR(gvec_uminp_h, MIN, uint16_t, H2) +DO_3OP_PAIR(gvec_uminp_s, MIN, uint32_t, H4) + #undef DO_3OP_PAIR #define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ From a9240f482ce77fc322411c1ce647d77dfacbdf4d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:48 -0700 Subject: [PATCH 1002/2884] target/arm: Use gvec for neon pmax, pmin Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-35-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-neon.c | 78 ++------------------------------- 1 file changed, 4 insertions(+), 74 deletions(-) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 6c5a7a98e1..18b048611b 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -831,6 +831,10 @@ DO_3SAME_NO_SZ_3(VABA_S, gen_gvec_saba) DO_3SAME_NO_SZ_3(VABD_U, gen_gvec_uabd) DO_3SAME_NO_SZ_3(VABA_U, gen_gvec_uaba) DO_3SAME_NO_SZ_3(VPADD, gen_gvec_addp) +DO_3SAME_NO_SZ_3(VPMAX_S, gen_gvec_smaxp) +DO_3SAME_NO_SZ_3(VPMIN_S, gen_gvec_sminp) +DO_3SAME_NO_SZ_3(VPMAX_U, gen_gvec_umaxp) +DO_3SAME_NO_SZ_3(VPMIN_U, gen_gvec_uminp) #define DO_3SAME_CMP(INSN, COND) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ @@ -1003,80 +1007,6 @@ DO_3SAME_32_ENV(VQSHL_U, qshl_u) DO_3SAME_32_ENV(VQRSHL_S, qrshl_s) DO_3SAME_32_ENV(VQRSHL_U, qrshl_u) -static bool do_3same_pair(DisasContext *s, arg_3same *a, NeonGenTwoOpFn *fn) -{ - /* Operations handled pairwise 32 bits at a time */ - TCGv_i32 tmp, tmp2, tmp3; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { - return false; - } - - if (a->size == 3) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - assert(a->q == 0); /* enforced by decode patterns */ - - /* - * Note that we have to be careful not to clobber the source operands - * in the "vm == vd" case by storing the result of the first pass too - * early. Since Q is 0 there are always just two passes, so instead - * of a complicated loop over each pass we just unroll. - */ - tmp = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i32(); - tmp3 = tcg_temp_new_i32(); - - read_neon_element32(tmp, a->vn, 0, MO_32); - read_neon_element32(tmp2, a->vn, 1, MO_32); - fn(tmp, tmp, tmp2); - - read_neon_element32(tmp3, a->vm, 0, MO_32); - read_neon_element32(tmp2, a->vm, 1, MO_32); - fn(tmp3, tmp3, tmp2); - - write_neon_element32(tmp, a->vd, 0, MO_32); - write_neon_element32(tmp3, a->vd, 1, MO_32); - - return true; -} - -#define DO_3SAME_PAIR(INSN, func) \ - static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - static NeonGenTwoOpFn * const fns[] = { \ - gen_helper_neon_##func##8, \ - gen_helper_neon_##func##16, \ - gen_helper_neon_##func##32, \ - }; \ - if (a->size > 2) { \ - return false; \ - } \ - return do_3same_pair(s, a, fns[a->size]); \ - } - -/* 32-bit pairwise ops end up the same as the elementwise versions. */ -#define gen_helper_neon_pmax_s32 tcg_gen_smax_i32 -#define gen_helper_neon_pmax_u32 tcg_gen_umax_i32 -#define gen_helper_neon_pmin_s32 tcg_gen_smin_i32 -#define gen_helper_neon_pmin_u32 tcg_gen_umin_i32 - -DO_3SAME_PAIR(VPMAX_S, pmax_s) -DO_3SAME_PAIR(VPMIN_S, pmin_s) -DO_3SAME_PAIR(VPMAX_U, pmax_u) -DO_3SAME_PAIR(VPMIN_U, pmin_u) - #define DO_3SAME_VQDMULH(INSN, FUNC) \ WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \ WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##_s32); \ From 641d8231426f02ab814c9bf97fcacea076cb3ef1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:49 -0700 Subject: [PATCH 1003/2884] target/arm: Convert FMLAL, FMLSL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-36-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 +++ target/arm/tcg/translate-a64.c | 144 ++++++++++----------------------- 2 files changed, 51 insertions(+), 103 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 22dfe8568d..7e993ed345 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -797,6 +797,11 @@ FMLA_v 0.00 1110 0.1 ..... 11001 1 ..... ..... @qrrr_sd FMLS_v 0.00 1110 110 ..... 00001 1 ..... ..... @qrrr_h FMLS_v 0.00 1110 1.1 ..... 11001 1 ..... ..... @qrrr_sd +FMLAL_v 0.00 1110 001 ..... 11101 1 ..... ..... @qrrr_h +FMLSL_v 0.00 1110 101 ..... 11101 1 ..... ..... @qrrr_h +FMLAL2_v 0.10 1110 001 ..... 11001 1 ..... ..... @qrrr_h +FMLSL2_v 0.10 1110 101 ..... 11001 1 ..... ..... @qrrr_h + FCMEQ_v 0.00 1110 010 ..... 00100 1 ..... ..... @qrrr_h FCMEQ_v 0.00 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd @@ -877,3 +882,8 @@ FMLS_vi 0.00 1111 11 0 ..... 0101 . 0 ..... ..... @qrrx_d FMULX_vi 0.10 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h FMULX_vi 0.10 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s FMULX_vi 0.10 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d + +FMLAL_vi 0.00 1111 10 .. .... 0000 . 0 ..... ..... @qrrx_h +FMLSL_vi 0.00 1111 10 .. .... 0100 . 0 ..... ..... @qrrx_h +FMLAL2_vi 0.10 1111 10 .. .... 1000 . 0 ..... ..... @qrrx_h +FMLSL2_vi 0.10 1111 10 .. .... 1100 . 0 ..... ..... @qrrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9fe70a939b..a4ff1fd202 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5256,6 +5256,24 @@ static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = { }; TRANS(FMINNMP_v, do_fp3_vector, a, f_vector_fminnmp) +static bool do_fmlal(DisasContext *s, arg_qrrr_e *a, bool is_s, bool is_2) +{ + if (fp_access_check(s)) { + int data = (is_2 << 1) | is_s; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), tcg_env, + a->q ? 16 : 8, vec_full_reg_size(s), + data, gen_helper_gvec_fmlal_a64); + } + return true; +} + +TRANS_FEAT(FMLAL_v, aa64_fhm, do_fmlal, a, false, false) +TRANS_FEAT(FMLSL_v, aa64_fhm, do_fmlal, a, true, false) +TRANS_FEAT(FMLAL2_v, aa64_fhm, do_fmlal, a, false, true) +TRANS_FEAT(FMLSL2_v, aa64_fhm, do_fmlal, a, true, true) + TRANS(ADDP_v, do_gvec_fn3, a, gen_gvec_addp) TRANS(SMAXP_v, do_gvec_fn3_no64, a, gen_gvec_smaxp) TRANS(SMINP_v, do_gvec_fn3_no64, a, gen_gvec_sminp) @@ -5447,6 +5465,24 @@ static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) TRANS(FMLA_vi, do_fmla_vector_idx, a, false) TRANS(FMLS_vi, do_fmla_vector_idx, a, true) +static bool do_fmlal_idx(DisasContext *s, arg_qrrx_e *a, bool is_s, bool is_2) +{ + if (fp_access_check(s)) { + int data = (a->idx << 2) | (is_2 << 1) | is_s; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), tcg_env, + a->q ? 16 : 8, vec_full_reg_size(s), + data, gen_helper_gvec_fmlal_idx_a64); + } + return true; +} + +TRANS_FEAT(FMLAL_vi, aa64_fhm, do_fmlal_idx, a, false, false) +TRANS_FEAT(FMLSL_vi, aa64_fhm, do_fmlal_idx, a, true, false) +TRANS_FEAT(FMLAL2_vi, aa64_fhm, do_fmlal_idx, a, false, true) +TRANS_FEAT(FMLSL2_vi, aa64_fhm, do_fmlal_idx, a, true, true) + /* * Advanced SIMD scalar pairwise */ @@ -10911,78 +10947,6 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) } } -/* Floating point op subgroup of C3.6.16. */ -static void disas_simd_3same_float(DisasContext *s, uint32_t insn) -{ - /* For floating point ops, the U, size[1] and opcode bits - * together indicate the operation. size[0] indicates single - * or double. - */ - int fpopcode = extract32(insn, 11, 5) - | (extract32(insn, 23, 1) << 5) - | (extract32(insn, 29, 1) << 6); - int is_q = extract32(insn, 30, 1); - int size = extract32(insn, 22, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (size == 1 && !is_q) { - unallocated_encoding(s); - return; - } - - switch (fpopcode) { - case 0x1d: /* FMLAL */ - case 0x3d: /* FMLSL */ - case 0x59: /* FMLAL2 */ - case 0x79: /* FMLSL2 */ - if (size & 1 || !dc_isar_feature(aa64_fhm, s)) { - unallocated_encoding(s); - return; - } - if (fp_access_check(s)) { - int is_s = extract32(insn, 23, 1); - int is_2 = extract32(insn, 29, 1); - int data = (is_2 << 1) | is_s; - tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), tcg_env, - is_q ? 16 : 8, vec_full_reg_size(s), - data, gen_helper_gvec_fmlal_a64); - } - return; - - default: - case 0x18: /* FMAXNM */ - case 0x19: /* FMLA */ - case 0x1a: /* FADD */ - case 0x1b: /* FMULX */ - case 0x1c: /* FCMEQ */ - case 0x1e: /* FMAX */ - case 0x1f: /* FRECPS */ - case 0x38: /* FMINNM */ - case 0x39: /* FMLS */ - case 0x3a: /* FSUB */ - case 0x3e: /* FMIN */ - case 0x3f: /* FRSQRTS */ - case 0x58: /* FMAXNMP */ - case 0x5a: /* FADDP */ - case 0x5b: /* FMUL */ - case 0x5c: /* FCMGE */ - case 0x5d: /* FACGE */ - case 0x5e: /* FMAXP */ - case 0x5f: /* FDIV */ - case 0x78: /* FMINNMP */ - case 0x7a: /* FABD */ - case 0x7d: /* FACGT */ - case 0x7c: /* FCMGT */ - case 0x7e: /* FMINP */ - unallocated_encoding(s); - return; - } -} - /* Integer op subgroup of C3.6.16. */ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) { @@ -11251,16 +11215,13 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) case 0x3: /* logic ops */ disas_simd_3same_logic(s, insn); break; - case 0x18 ... 0x31: - /* floating point ops, sz[1] and U are part of opcode */ - disas_simd_3same_float(s, insn); - break; default: disas_simd_3same_int(s, insn); break; case 0x14: /* SMAXP, UMAXP */ case 0x15: /* SMINP, UMINP */ case 0x17: /* ADDP */ + case 0x18 ... 0x31: /* floating point ops */ unallocated_encoding(s); break; } @@ -12526,22 +12487,15 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } is_fp = 2; break; - case 0x00: /* FMLAL */ - case 0x04: /* FMLSL */ - case 0x18: /* FMLAL2 */ - case 0x1c: /* FMLSL2 */ - if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_fhm, s)) { - unallocated_encoding(s); - return; - } - size = MO_16; - /* is_fp, but we pass tcg_env not fp_status. */ - break; default: + case 0x00: /* FMLAL */ case 0x01: /* FMLA */ + case 0x04: /* FMLSL */ case 0x05: /* FMLS */ case 0x09: /* FMUL */ + case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ + case 0x1c: /* FMLSL2 */ unallocated_encoding(s); return; } @@ -12660,22 +12614,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } return; - case 0x00: /* FMLAL */ - case 0x04: /* FMLSL */ - case 0x18: /* FMLAL2 */ - case 0x1c: /* FMLSL2 */ - { - int is_s = extract32(opcode, 2, 1); - int is_2 = u; - int data = (index << 2) | (is_2 << 1) | is_s; - tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), tcg_env, - is_q ? 16 : 8, vec_full_reg_size(s), - data, gen_helper_gvec_fmlal_idx_a64); - } - return; - case 0x08: /* MUL */ if (!is_long && !is_scalar) { static gen_helper_gvec_3 * const fns[3] = { From f240df3c31b40e4cf1af1f156a88efc1a1df406c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 16:20:50 -0700 Subject: [PATCH 1004/2884] target/arm: Convert disas_simd_3same_logic to decodetree This includes AND, ORR, EOR, BIC, ORN, BSF, BIT, BIF. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240524232121.284515-37-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 +++++ target/arm/tcg/translate-a64.c | 68 ++++++++++------------------------ 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7e993ed345..f48adef5bb 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -55,6 +55,7 @@ @rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3 @rrrr_q1e3 ........ ... rm:5 . ra:5 rn:5 rd:5 &qrrrr_e q=1 esz=3 +@qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0 @qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1 @qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd @qrrr_e . q:1 ...... esz:2 . rm:5 ...... rn:5 rd:5 &qrrr_e @@ -847,6 +848,15 @@ SMINP_v 0.00 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e UMAXP_v 0.10 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e UMINP_v 0.10 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e +AND_v 0.00 1110 001 ..... 00011 1 ..... ..... @qrrr_b +BIC_v 0.00 1110 011 ..... 00011 1 ..... ..... @qrrr_b +ORR_v 0.00 1110 101 ..... 00011 1 ..... ..... @qrrr_b +ORN_v 0.00 1110 111 ..... 00011 1 ..... ..... @qrrr_b +EOR_v 0.10 1110 001 ..... 00011 1 ..... ..... @qrrr_b +BSL_v 0.10 1110 011 ..... 00011 1 ..... ..... @qrrr_b +BIT_v 0.10 1110 101 ..... 00011 1 ..... ..... @qrrr_b +BIF_v 0.10 1110 111 ..... 00011 1 ..... ..... @qrrr_b + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a4ff1fd202..9167e4d0bd 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5280,6 +5280,24 @@ TRANS(SMINP_v, do_gvec_fn3_no64, a, gen_gvec_sminp) TRANS(UMAXP_v, do_gvec_fn3_no64, a, gen_gvec_umaxp) TRANS(UMINP_v, do_gvec_fn3_no64, a, gen_gvec_uminp) +TRANS(AND_v, do_gvec_fn3, a, tcg_gen_gvec_and) +TRANS(BIC_v, do_gvec_fn3, a, tcg_gen_gvec_andc) +TRANS(ORR_v, do_gvec_fn3, a, tcg_gen_gvec_or) +TRANS(ORN_v, do_gvec_fn3, a, tcg_gen_gvec_orc) +TRANS(EOR_v, do_gvec_fn3, a, tcg_gen_gvec_xor) + +static bool do_bitsel(DisasContext *s, bool is_q, int d, int a, int b, int c) +{ + if (fp_access_check(s)) { + gen_gvec_fn4(s, is_q, d, a, b, c, tcg_gen_gvec_bitsel, 0); + } + return true; +} + +TRANS(BSL_v, do_bitsel, a->q, a->rd, a->rd, a->rn, a->rm) +TRANS(BIT_v, do_bitsel, a->q, a->rd, a->rm, a->rn, a->rd) +TRANS(BIF_v, do_bitsel, a->q, a->rd, a->rm, a->rd, a->rn) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10901,52 +10919,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) } } -/* Logic op (opcode == 3) subgroup of C3.6.16. */ -static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool is_u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - - if (!fp_access_check(s)) { - return; - } - - switch (size + 4 * is_u) { - case 0: /* AND */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_and, 0); - return; - case 1: /* BIC */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_andc, 0); - return; - case 2: /* ORR */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_or, 0); - return; - case 3: /* ORN */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_orc, 0); - return; - case 4: /* EOR */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_xor, 0); - return; - - case 5: /* BSL bitwise select */ - gen_gvec_fn4(s, is_q, rd, rd, rn, rm, tcg_gen_gvec_bitsel, 0); - return; - case 6: /* BIT, bitwise insert if true */ - gen_gvec_fn4(s, is_q, rd, rm, rn, rd, tcg_gen_gvec_bitsel, 0); - return; - case 7: /* BIF, bitwise insert if false */ - gen_gvec_fn4(s, is_q, rd, rm, rd, rn, tcg_gen_gvec_bitsel, 0); - return; - - default: - g_assert_not_reached(); - } -} - /* Integer op subgroup of C3.6.16. */ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) { @@ -11212,12 +11184,10 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) int opcode = extract32(insn, 11, 5); switch (opcode) { - case 0x3: /* logic ops */ - disas_simd_3same_logic(s, insn); - break; default: disas_simd_3same_int(s, insn); break; + case 0x3: /* logic ops */ case 0x14: /* SMAXP, UMAXP */ case 0x15: /* SMINP, UMINP */ case 0x17: /* ADDP */ From a6a33760a33e5f6b73b139e1e6c87fb6663c76ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:04 -0700 Subject: [PATCH 1005/2884] target/s390x: Do not use unwind for per_check_exception Using exception unwind via tcg_s390_program_interrupt, we discard the current value of psw.addr, which discards the result of a branch. Pass in the address of the next instruction, which may not be sequential. Pass in ilen, which we would have gotten from unwind and is passed to the exception handler. Sync cc_op before the call, which we would have gotten from unwind. Signed-off-by: Richard Henderson Reviewed-by: Ilya Leoshkevich Message-ID: <20240502054417.234340-2-richard.henderson@linaro.org> [thuth: Silence checkpatch.pl errors] Signed-off-by: Thomas Huth --- target/s390x/helper.h | 2 +- target/s390x/tcg/excp_helper.c | 2 +- target/s390x/tcg/misc_helper.c | 23 ++++++++++++++++++++--- target/s390x/tcg/translate.c | 13 +++++++------ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index cc1c20e9e3..96ab71e877 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -359,7 +359,7 @@ DEF_HELPER_FLAGS_4(ipte, TCG_CALL_NO_RWG, void, env, i64, i64, i32) DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env) DEF_HELPER_3(lra, i64, env, i64, i64) -DEF_HELPER_1(per_check_exception, void, env) +DEF_HELPER_FLAGS_3(per_check_exception, TCG_CALL_NO_WG, void, env, i64, i32) DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64) DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index f1c33f7967..4c0b692c9e 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -209,7 +209,7 @@ static void do_program_interrupt(CPUS390XState *env) switch (env->int_pgm_code) { case PGM_PER: - advance = !(env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION); + /* advance already handled */ break; case PGM_ASCE_TYPE: case PGM_REG_FIRST_TRANS: diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 8764846ce8..7c94468392 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/log.h" #include "cpu.h" #include "s390x-internal.h" #include "qemu/host-utils.h" @@ -590,10 +591,26 @@ void HELPER(chsc)(CPUS390XState *env, uint64_t inst) #endif #ifndef CONFIG_USER_ONLY -void HELPER(per_check_exception)(CPUS390XState *env) +static G_NORETURN void per_raise_exception(CPUS390XState *env) { - if (env->per_perc_atmid) { - tcg_s390_program_interrupt(env, PGM_PER, GETPC()); + trigger_pgm_exception(env, PGM_PER); + cpu_loop_exit(env_cpu(env)); +} + +static G_NORETURN void per_raise_exception_log(CPUS390XState *env) +{ + qemu_log_mask(CPU_LOG_INT, "PER interrupt after 0x%" PRIx64 "\n", + env->per_address); + per_raise_exception(env); +} + +void HELPER(per_check_exception)(CPUS390XState *env, uint64_t next_pc, + uint32_t ilen) +{ + if (unlikely(env->per_perc_atmid)) { + env->psw.addr = next_pc; + env->int_pgm_ilen = ilen; + per_raise_exception_log(env); } } diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index ebd96abe6c..4c3ff1931b 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6424,13 +6424,14 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER) { - /* An exception might be triggered, save PSW if not already done. */ - if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - } + TCGv_i64 next_pc = psw_addr; - /* Call the helper to check for a possible PER exception. */ - gen_helper_per_check_exception(tcg_env); + if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { + next_pc = tcg_constant_i64(s->pc_tmp); + } + update_cc_op(s); + gen_helper_per_check_exception(tcg_env, next_pc, + tcg_constant_i32(s->ilen)); } #endif From 36db37af3499589dace9edbf29e9000182efe808 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:05 -0700 Subject: [PATCH 1006/2884] target/s390x: Move cpu_get_tb_cpu_state out of line Signed-off-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Ilya Leoshkevich Message-ID: <20240502054417.234340-3-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu.c | 22 ++++++++++++++++++++++ target/s390x/cpu.h | 23 ++--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index f7194534ae..a8428b5a1e 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -324,6 +324,28 @@ static void s390_cpu_reset_full(DeviceState *dev) #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" +void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + if (env->psw.addr & 1) { + /* + * Instructions must be at even addresses. + * This needs to be checked before address translation. + */ + env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); + } + *pc = env->psw.addr; + *cs_base = env->ex_value; + *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; + if (env->cregs[0] & CR0_AFP) { + *flags |= FLAG_MASK_AFP; + } + if (env->cregs[0] & CR0_VECTOR) { + *flags |= FLAG_MASK_VECTOR; + } +} + static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, .restore_state_to_opc = s390x_restore_state_to_opc, diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 414680eed1..950f84f316 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -413,27 +413,8 @@ static inline int s390x_env_mmu_index(CPUS390XState *env, bool ifetch) #include "tcg/tcg_s390x.h" -static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - if (env->psw.addr & 1) { - /* - * Instructions must be at even addresses. - * This needs to be checked before address translation. - */ - env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); - } - *pc = env->psw.addr; - *cs_base = env->ex_value; - *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; - if (env->cregs[0] & CR0_AFP) { - *flags |= FLAG_MASK_AFP; - } - if (env->cregs[0] & CR0_VECTOR) { - *flags |= FLAG_MASK_VECTOR; - } -} +void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags); #endif /* CONFIG_TCG */ From 51a1718b14a57c619d9897c25a59fab75cc980cc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:06 -0700 Subject: [PATCH 1007/2884] target/s390x: Update CR9 bits Update from the PoO 14th edition. Signed-off-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Ilya Leoshkevich Message-ID: <20240502054417.234340-4-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu.h | 18 +++++++++++------- target/s390x/tcg/misc_helper.c | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 950f84f316..1bb723a9d3 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -419,13 +419,17 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, #endif /* CONFIG_TCG */ /* PER bits from control register 9 */ -#define PER_CR9_EVENT_BRANCH 0x80000000 -#define PER_CR9_EVENT_IFETCH 0x40000000 -#define PER_CR9_EVENT_STORE 0x20000000 -#define PER_CR9_EVENT_STORE_REAL 0x08000000 -#define PER_CR9_EVENT_NULLIFICATION 0x01000000 -#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000 -#define PER_CR9_CONTROL_ALTERATION 0x00200000 +#define PER_CR9_EVENT_BRANCH 0x80000000 +#define PER_CR9_EVENT_IFETCH 0x40000000 +#define PER_CR9_EVENT_STORE 0x20000000 +#define PER_CR9_EVENT_STORAGE_KEY_ALTERATION 0x10000000 +#define PER_CR9_EVENT_STORE_REAL 0x08000000 +#define PER_CR9_EVENT_ZERO_ADDRESS_DETECTION 0x04000000 +#define PER_CR9_EVENT_TRANSACTION_END 0x02000000 +#define PER_CR9_EVENT_IFETCH_NULLIFICATION 0x01000000 +#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000 +#define PER_CR9_CONTROL_TRANSACTION_SUPRESS 0x00400000 +#define PER_CR9_CONTROL_STORAGE_ALTERATION 0x00200000 /* PER bits from the PER CODE/ATMID/AI in lowcore */ #define PER_CODE_EVENT_BRANCH 0x8000 diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 7c94468392..0482442458 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -644,7 +644,7 @@ void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) /* If the instruction has to be nullified, trigger the exception immediately. */ - if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) { + if (env->cregs[9] & PER_CR9_EVENT_IFETCH_NULLIFICATION) { CPUState *cs = env_cpu(env); env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION; From 62613ca07382cb7a2d5f442d8a21e340c384a392 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:07 -0700 Subject: [PATCH 1008/2884] target/s390x: Record separate PER bits in TB flags Record successful-branching, instruction-fetching, and store-using-real-address. The other PER bits are not used during translation. Having checked these at translation time, we can remove runtime tests from the helpers. Signed-off-by: Richard Henderson Reviewed-by: Ilya Leoshkevich Message-ID: <20240502054417.234340-5-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu.c | 22 ++++++++++++++---- target/s390x/cpu.h | 42 ++++++++++++++++++++++++---------- target/s390x/tcg/misc_helper.c | 21 +++++++---------- target/s390x/tcg/translate.c | 10 ++++---- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index a8428b5a1e..2bbeaca36e 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -325,8 +325,10 @@ static void s390_cpu_reset_full(DeviceState *dev) #include "hw/core/tcg-cpu-ops.h" void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) + uint64_t *cs_base, uint32_t *pflags) { + uint32_t flags; + if (env->psw.addr & 1) { /* * Instructions must be at even addresses. @@ -335,15 +337,27 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); } + *pc = env->psw.addr; *cs_base = env->ex_value; - *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; + + flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; + if (env->psw.mask & PSW_MASK_PER) { + flags |= env->cregs[9] & (FLAG_MASK_PER_BRANCH | + FLAG_MASK_PER_IFETCH | + FLAG_MASK_PER_IFETCH_NULLIFY); + if ((env->cregs[9] & PER_CR9_EVENT_STORE) && + (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { + flags |= FLAG_MASK_PER_STORE_REAL; + } + } if (env->cregs[0] & CR0_AFP) { - *flags |= FLAG_MASK_AFP; + flags |= FLAG_MASK_AFP; } if (env->cregs[0] & CR0_VECTOR) { - *flags |= FLAG_MASK_VECTOR; + flags |= FLAG_MASK_VECTOR; } + *pflags = flags; } static const TCGCPUOps s390_tcg_ops = { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 1bb723a9d3..d6b75ad0e0 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -342,19 +342,32 @@ extern const VMStateDescription vmstate_s390_cpu; /* tb flags */ -#define FLAG_MASK_PSW_SHIFT 31 -#define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \ - | FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) +#define FLAG_MASK_PSW_SHIFT 31 +#define FLAG_MASK_32 0x00000001u +#define FLAG_MASK_64 0x00000002u +#define FLAG_MASK_AFP 0x00000004u +#define FLAG_MASK_VECTOR 0x00000008u +#define FLAG_MASK_ASC 0x00018000u +#define FLAG_MASK_PSTATE 0x00020000u +#define FLAG_MASK_PER_IFETCH_NULLIFY 0x01000000u +#define FLAG_MASK_DAT 0x08000000u +#define FLAG_MASK_PER_STORE_REAL 0x20000000u +#define FLAG_MASK_PER_IFETCH 0x40000000u +#define FLAG_MASK_PER_BRANCH 0x80000000u -/* we'll use some unused PSW positions to store CR flags in tb flags */ -#define FLAG_MASK_AFP (PSW_MASK_UNUSED_2 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_VECTOR (PSW_MASK_UNUSED_3 >> FLAG_MASK_PSW_SHIFT) +QEMU_BUILD_BUG_ON(FLAG_MASK_32 != PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_64 != PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_ASC != PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_PSTATE != PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_DAT != PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT); + +#define FLAG_MASK_PSW (FLAG_MASK_DAT | FLAG_MASK_PSTATE | \ + FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) +#define FLAG_MASK_CR9 (FLAG_MASK_PER_BRANCH | FLAG_MASK_PER_IFETCH) +#define FLAG_MASK_PER (FLAG_MASK_PER_BRANCH | \ + FLAG_MASK_PER_IFETCH | \ + FLAG_MASK_PER_IFETCH_NULLIFY | \ + FLAG_MASK_PER_STORE_REAL) /* Control register 0 bits */ #define CR0_LOWPROT 0x0000000010000000ULL @@ -431,6 +444,11 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, #define PER_CR9_CONTROL_TRANSACTION_SUPRESS 0x00400000 #define PER_CR9_CONTROL_STORAGE_ALTERATION 0x00200000 +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_BRANCH != PER_CR9_EVENT_BRANCH); +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_IFETCH != PER_CR9_EVENT_IFETCH); +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_IFETCH_NULLIFY != + PER_CR9_EVENT_IFETCH_NULLIFICATION); + /* PER bits from the PER CODE/ATMID/AI in lowcore */ #define PER_CODE_EVENT_BRANCH 0x8000 #define PER_CODE_EVENT_IFETCH 0x4000 diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 0482442458..3f94f71437 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -627,18 +627,16 @@ static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr) void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to) { - if ((env->cregs[9] & PER_CR9_EVENT_BRANCH)) { - if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) - || get_per_in_range(env, to)) { - env->per_address = from; - env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); - } + if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) + || get_per_in_range(env, to)) { + env->per_address = from; + env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); } } void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) { - if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) { + if (get_per_in_range(env, addr)) { env->per_address = addr; env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env); @@ -659,12 +657,9 @@ void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) void HELPER(per_store_real)(CPUS390XState *env) { - if ((env->cregs[9] & PER_CR9_EVENT_STORE) && - (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { - /* PSW is saved just before calling the helper. */ - env->per_address = env->psw.addr; - env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); - } + /* PSW is saved just before calling the helper. */ + env->per_address = env->psw.addr; + env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); } #endif diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 4c3ff1931b..de98115c4d 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -346,7 +346,7 @@ static void per_branch(DisasContext *s, bool to_next) #ifndef CONFIG_USER_ONLY tcg_gen_movi_i64(gbea, s->base.pc_next); - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { TCGv_i64 next_pc = to_next ? tcg_constant_i64(s->pc_tmp) : psw_addr; gen_helper_per_branch(tcg_env, gbea, next_pc); } @@ -357,7 +357,7 @@ static void per_branch_cond(DisasContext *s, TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2) { #ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { TCGLabel *lab = gen_new_label(); tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab); @@ -656,7 +656,7 @@ static void gen_op_calc_cc(DisasContext *s) static bool use_goto_tb(DisasContext *s, uint64_t dest) { - if (unlikely(s->base.tb->flags & FLAG_MASK_PER)) { + if (unlikely(s->base.tb->flags & FLAG_MASK_PER_BRANCH)) { return false; } return translator_use_goto_tb(&s->base, dest); @@ -4409,7 +4409,7 @@ static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st_tl(o->in1, o->in2, MMU_REAL_IDX, s->insn->data); - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER_STORE_REAL) { update_psw_addr(s); gen_helper_per_store_real(tcg_env); } @@ -6323,7 +6323,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { TCGv_i64 addr = tcg_constant_i64(s->base.pc_next); gen_helper_per_ifetch(tcg_env, addr); } From a90e319569547ac7477606301b93491f3b886569 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:08 -0700 Subject: [PATCH 1009/2884] target/s390x: Disable conditional branch-to-next for PER For PER, we require a conditional call to helper_per_branch for the conditional branch. Fold the remaining optimization into a call to helper_goto_direct, which will take care of the remaining gbea adjustment. Reviewed-by: Ilya Leoshkevich Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-6-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/translate.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index de98115c4d..07620948ae 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -1131,13 +1131,13 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, goto egress; } if (is_imm) { - if (dest == s->pc_tmp) { - /* Branch to next. */ - per_branch(s, true); - ret = DISAS_NEXT; - goto egress; - } - if (c->cond == TCG_COND_ALWAYS) { + /* + * Do not optimize a conditional branch if PER enabled, because we + * still need a conditional call to helper_per_branch. + */ + if (c->cond == TCG_COND_ALWAYS + || (dest == s->pc_tmp && + !(s->base.tb->flags & FLAG_MASK_PER_BRANCH))) { ret = help_goto_direct(s, dest); goto egress; } From 9bbbcf5ddb5ae6e006ca394a9ec01492ac6d8122 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:09 -0700 Subject: [PATCH 1010/2884] target/s390x: Introduce help_goto_indirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a small helper to handle unconditional indirect jumps. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Ilya Leoshkevich Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-7-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/translate.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 07620948ae..7ae928c3b0 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -1118,6 +1118,13 @@ static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) } } +static DisasJumpType help_goto_indirect(DisasContext *s, TCGv_i64 dest) +{ + tcg_gen_mov_i64(psw_addr, dest); + per_branch(s, false); + return DISAS_PC_UPDATED; +} + static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { @@ -1148,9 +1155,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, goto egress; } if (c->cond == TCG_COND_ALWAYS) { - tcg_gen_mov_i64(psw_addr, cdest); - per_branch(s, false); - ret = DISAS_PC_UPDATED; + ret = help_goto_indirect(s, cdest); goto egress; } } @@ -1463,9 +1468,7 @@ static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { pc_to_link_info(o->out, s, s->pc_tmp); if (o->in2) { - tcg_gen_mov_i64(psw_addr, o->in2); - per_branch(s, false); - return DISAS_PC_UPDATED; + return help_goto_indirect(s, o->in2); } else { return DISAS_NEXT; } @@ -1495,9 +1498,7 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o) { save_link_info(s, o); if (o->in2) { - tcg_gen_mov_i64(psw_addr, o->in2); - per_branch(s, false); - return DISAS_PC_UPDATED; + return help_goto_indirect(s, o->in2); } else { return DISAS_NEXT; } From e64054552385aced493969af6f8341d944e150a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:10 -0700 Subject: [PATCH 1011/2884] target/s390x: Simplify help_branch Always use a tcg branch, instead of movcond. The movcond was not a bad idea before PER was added, but since then we have either 2 or 3 actions to perform on each leg of the branch, and multiple movcond is inefficient. Reorder the taken branch to be fallthrough of the tcg branch. Reviewed-by: Ilya Leoshkevich Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-8-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/translate.c | 164 ++++++++++++----------------------- 1 file changed, 56 insertions(+), 108 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 7ae928c3b0..78066aaf84 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -353,25 +353,6 @@ static void per_branch(DisasContext *s, bool to_next) #endif } -static void per_branch_cond(DisasContext *s, TCGCond cond, - TCGv_i64 arg1, TCGv_i64 arg2) -{ -#ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { - TCGLabel *lab = gen_new_label(); - tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab); - - tcg_gen_movi_i64(gbea, s->base.pc_next); - gen_helper_per_branch(tcg_env, gbea, psw_addr); - - gen_set_label(lab); - } else { - TCGv_i64 pc = tcg_constant_i64(s->base.pc_next); - tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); - } -#endif -} - static void per_breaking_event(DisasContext *s) { tcg_gen_movi_i64(gbea, s->base.pc_next); @@ -1128,14 +1109,12 @@ static DisasJumpType help_goto_indirect(DisasContext *s, TCGv_i64 dest) static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { - DisasJumpType ret; uint64_t dest = s->base.pc_next + (int64_t)imm * 2; - TCGLabel *lab; + TCGLabel *lab, *over; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { - ret = DISAS_NEXT; - goto egress; + return DISAS_NEXT; } if (is_imm) { /* @@ -1145,104 +1124,73 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, if (c->cond == TCG_COND_ALWAYS || (dest == s->pc_tmp && !(s->base.tb->flags & FLAG_MASK_PER_BRANCH))) { - ret = help_goto_direct(s, dest); - goto egress; + return help_goto_direct(s, dest); } } else { if (!cdest) { /* E.g. bcr %r0 -> no branch. */ - ret = DISAS_NEXT; - goto egress; + return DISAS_NEXT; } if (c->cond == TCG_COND_ALWAYS) { - ret = help_goto_indirect(s, cdest); - goto egress; + return help_goto_indirect(s, cdest); } } - if (use_goto_tb(s, s->pc_tmp)) { - if (is_imm && use_goto_tb(s, dest)) { - /* Both exits can use goto_tb. */ - update_cc_op(s); + update_cc_op(s); - lab = gen_new_label(); - if (c->is_64) { - tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); - } else { - tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); - } - - /* Branch not taken. */ - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb(s->base.tb, 0); - - /* Branch taken. */ - gen_set_label(lab); - per_breaking_event(s); - tcg_gen_goto_tb(1); - tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb(s->base.tb, 1); - - ret = DISAS_NORETURN; - } else { - /* Fallthru can use goto_tb, but taken branch cannot. */ - /* Store taken branch destination before the brcond. This - avoids having to allocate a new local temp to hold it. - We'll overwrite this in the not taken case anyway. */ - if (!is_imm) { - tcg_gen_mov_i64(psw_addr, cdest); - } - - lab = gen_new_label(); - if (c->is_64) { - tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); - } else { - tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); - } - - /* Branch not taken. */ - update_cc_op(s); - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb(s->base.tb, 0); - - gen_set_label(lab); - if (is_imm) { - tcg_gen_movi_i64(psw_addr, dest); - } - per_breaking_event(s); - ret = DISAS_PC_UPDATED; - } + /* + * Ensure the taken branch is fall-through of the tcg branch. + * This keeps @cdest usage within the extended basic block, + * which avoids an otherwise unnecessary spill to the stack. + */ + lab = gen_new_label(); + if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { + over = gen_new_label(); } else { - /* Fallthru cannot use goto_tb. This by itself is vanishingly rare. - Most commonly we're single-stepping or some other condition that - disables all use of goto_tb. Just update the PC and exit. */ - - TCGv_i64 next = tcg_constant_i64(s->pc_tmp); - if (is_imm) { - cdest = tcg_constant_i64(dest); - } - - if (c->is_64) { - tcg_gen_movcond_i64(c->cond, psw_addr, c->u.s64.a, c->u.s64.b, - cdest, next); - per_branch_cond(s, c->cond, c->u.s64.a, c->u.s64.b); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 z = tcg_constant_i64(0); - tcg_gen_setcond_i32(c->cond, t0, c->u.s32.a, c->u.s32.b); - tcg_gen_extu_i32_i64(t1, t0); - tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next); - per_branch_cond(s, TCG_COND_NE, t1, z); - } - - ret = DISAS_PC_UPDATED; + over = NULL; } - egress: - return ret; + if (c->is_64) { + tcg_gen_brcond_i64(tcg_invert_cond(c->cond), + c->u.s64.a, c->u.s64.b, lab); + } else { + tcg_gen_brcond_i32(tcg_invert_cond(c->cond), + c->u.s32.a, c->u.s32.b, lab); + } + + /* Branch taken. */ + if (is_imm) { + tcg_gen_movi_i64(psw_addr, dest); + } else { + tcg_gen_mov_i64(psw_addr, cdest); + } + per_branch(s, false); + + if (is_imm && use_goto_tb(s, dest)) { + tcg_gen_goto_tb(0); + tcg_gen_exit_tb(s->base.tb, 0); + } else if (over) { + tcg_gen_br(over); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + + gen_set_label(lab); + + /* Branch not taken. */ + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + if (use_goto_tb(s, s->pc_tmp)) { + tcg_gen_goto_tb(1); + tcg_gen_exit_tb(s->base.tb, 1); + } + + if (over) { + gen_set_label(over); + return DISAS_PC_UPDATED; + } + + tcg_gen_lookup_and_goto_ptr(); + return DISAS_NORETURN; } /* ====================================================================== */ From 619f6891ffb252ae1a4ca0e13a630bd3c13d198c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:11 -0700 Subject: [PATCH 1012/2884] target/s390x: Split per_breaking_event from per_branch_* The breaking-event-address register is updated regardless of PER being enabled. Reviewed-by: Ilya Leoshkevich Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-9-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 78066aaf84..10d5e87bb4 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -344,8 +344,6 @@ static void update_psw_addr(DisasContext *s) static void per_branch(DisasContext *s, bool to_next) { #ifndef CONFIG_USER_ONLY - tcg_gen_movi_i64(gbea, s->base.pc_next); - if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { TCGv_i64 next_pc = to_next ? tcg_constant_i64(s->pc_tmp) : psw_addr; gen_helper_per_branch(tcg_env, gbea, next_pc); @@ -1081,13 +1079,13 @@ struct DisasInsn { static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { + per_breaking_event(s); if (dest == s->pc_tmp) { per_branch(s, true); return DISAS_NEXT; } if (use_goto_tb(s, dest)) { update_cc_op(s); - per_breaking_event(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 0); @@ -1101,6 +1099,7 @@ static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) static DisasJumpType help_goto_indirect(DisasContext *s, TCGv_i64 dest) { + per_breaking_event(s); tcg_gen_mov_i64(psw_addr, dest); per_branch(s, false); return DISAS_PC_UPDATED; @@ -1159,6 +1158,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, } /* Branch taken. */ + per_breaking_event(s); if (is_imm) { tcg_gen_movi_i64(psw_addr, dest); } else { From 5331339651dbf081cb78c219f000dab243022de9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:12 -0700 Subject: [PATCH 1013/2884] target/s390x: Raise exception from helper_per_branch Drop from argument, since gbea has always been updated with this address. Add ilen argument for setting int_pgm_ilen. Use update_cc_op before calling per_branch. By raising the exception here, we need not call per_check_exception later, which means we can clean up the normal non-exception branch path. Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-10-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.h | 2 +- target/s390x/tcg/misc_helper.c | 15 +++++++---- target/s390x/tcg/translate.c | 48 ++++++++++++---------------------- 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 96ab71e877..061b379065 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -360,7 +360,7 @@ DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env) DEF_HELPER_3(lra, i64, env, i64, i64) DEF_HELPER_FLAGS_3(per_check_exception, TCG_CALL_NO_WG, void, env, i64, i32) -DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64) +DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32) DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 3f94f71437..974d14703c 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -625,13 +625,18 @@ static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr) } } -void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to) +void HELPER(per_branch)(CPUS390XState *env, uint64_t dest, uint32_t ilen) { - if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) - || get_per_in_range(env, to)) { - env->per_address = from; - env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); + if ((env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) + && !get_per_in_range(env, dest)) { + return; } + + env->psw.addr = dest; + env->int_pgm_ilen = ilen; + env->per_address = env->gbea; + env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); + per_raise_exception_log(env); } void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 10d5e87bb4..de029185e0 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -341,12 +341,11 @@ static void update_psw_addr(DisasContext *s) tcg_gen_movi_i64(psw_addr, s->base.pc_next); } -static void per_branch(DisasContext *s, bool to_next) +static void per_branch(DisasContext *s, TCGv_i64 dest) { #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { - TCGv_i64 next_pc = to_next ? tcg_constant_i64(s->pc_tmp) : psw_addr; - gen_helper_per_branch(tcg_env, gbea, next_pc); + gen_helper_per_branch(tcg_env, dest, tcg_constant_i32(s->ilen)); } #endif } @@ -635,9 +634,6 @@ static void gen_op_calc_cc(DisasContext *s) static bool use_goto_tb(DisasContext *s, uint64_t dest) { - if (unlikely(s->base.tb->flags & FLAG_MASK_PER_BRANCH)) { - return false; - } return translator_use_goto_tb(&s->base, dest); } @@ -1079,37 +1075,38 @@ struct DisasInsn { static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { + update_cc_op(s); per_breaking_event(s); + per_branch(s, tcg_constant_i64(dest)); + if (dest == s->pc_tmp) { - per_branch(s, true); return DISAS_NEXT; } if (use_goto_tb(s, dest)) { - update_cc_op(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(psw_addr, dest); - per_branch(s, false); - return DISAS_PC_UPDATED; + return DISAS_PC_CC_UPDATED; } } static DisasJumpType help_goto_indirect(DisasContext *s, TCGv_i64 dest) { + update_cc_op(s); per_breaking_event(s); tcg_gen_mov_i64(psw_addr, dest); - per_branch(s, false); - return DISAS_PC_UPDATED; + per_branch(s, psw_addr); + return DISAS_PC_CC_UPDATED; } static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { uint64_t dest = s->base.pc_next + (int64_t)imm * 2; - TCGLabel *lab, *over; + TCGLabel *lab; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { @@ -1143,12 +1140,6 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, * which avoids an otherwise unnecessary spill to the stack. */ lab = gen_new_label(); - if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { - over = gen_new_label(); - } else { - over = NULL; - } - if (c->is_64) { tcg_gen_brcond_i64(tcg_invert_cond(c->cond), c->u.s64.a, c->u.s64.b, lab); @@ -1164,13 +1155,11 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, } else { tcg_gen_mov_i64(psw_addr, cdest); } - per_branch(s, false); + per_branch(s, psw_addr); if (is_imm && use_goto_tb(s, dest)) { tcg_gen_goto_tb(0); tcg_gen_exit_tb(s->base.tb, 0); - } else if (over) { - tcg_gen_br(over); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -1182,15 +1171,9 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, if (use_goto_tb(s, s->pc_tmp)) { tcg_gen_goto_tb(1); tcg_gen_exit_tb(s->base.tb, 1); + return DISAS_NORETURN; } - - if (over) { - gen_set_label(over); - return DISAS_PC_UPDATED; - } - - tcg_gen_lookup_and_goto_ptr(); - return DISAS_NORETURN; + return DISAS_PC_CC_UPDATED; } /* ====================================================================== */ @@ -6372,7 +6355,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & (FLAG_MASK_PER_STORE_REAL | + FLAG_MASK_PER_IFETCH)) { TCGv_i64 next_pc = psw_addr; if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { @@ -6402,7 +6386,7 @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->cc_op = CC_OP_DYNAMIC; dc->ex_value = dc->base.tb->cs_base; - dc->exit_to_mainloop = (dc->base.tb->flags & FLAG_MASK_PER) || dc->ex_value; + dc->exit_to_mainloop = dc->ex_value; } static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) From 31b2d4a1b3136f727866326b2cee5e993ea36e8a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:13 -0700 Subject: [PATCH 1014/2884] target/s390x: Raise exception from per_store_real At this point the instruction is complete and there's nothing left to do but raise the exception. With this change we need not make two helper calls for this event. Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-11-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.h | 2 +- target/s390x/tcg/misc_helper.c | 4 +++- target/s390x/tcg/translate.c | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 061b379065..5611155ba1 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -362,7 +362,7 @@ DEF_HELPER_3(lra, i64, env, i64, i64) DEF_HELPER_FLAGS_3(per_check_exception, TCG_CALL_NO_WG, void, env, i64, i32) DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32) DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(per_store_real, TCG_CALL_NO_WG, noreturn, env, i32) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) DEF_HELPER_2(xsch, void, env, i64) diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 974d14703c..43cacc448f 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -660,11 +660,13 @@ void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) } } -void HELPER(per_store_real)(CPUS390XState *env) +void HELPER(per_store_real)(CPUS390XState *env, uint32_t ilen) { /* PSW is saved just before calling the helper. */ env->per_address = env->psw.addr; + env->int_pgm_ilen = ilen; env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); + per_raise_exception_log(env); } #endif diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index de029185e0..5bb15a46e0 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -4342,8 +4342,10 @@ static DisasJumpType op_stura(DisasContext *s, DisasOps *o) tcg_gen_qemu_st_tl(o->in1, o->in2, MMU_REAL_IDX, s->insn->data); if (s->base.tb->flags & FLAG_MASK_PER_STORE_REAL) { + update_cc_op(s); update_psw_addr(s); - gen_helper_per_store_real(tcg_env); + gen_helper_per_store_real(tcg_env, tcg_constant_i32(s->ilen)); + return DISAS_NORETURN; } return DISAS_NEXT; } @@ -6355,8 +6357,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & (FLAG_MASK_PER_STORE_REAL | - FLAG_MASK_PER_IFETCH)) { + if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { TCGv_i64 next_pc = psw_addr; if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { From 67b765d3f3f1430abfc97915063740d12f2f7dba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:14 -0700 Subject: [PATCH 1015/2884] target/s390x: Fix helper_per_ifetch flags CPU state is read on the exception path. Fixes: 83bb161299c ("target-s390x: PER instruction-fetch nullification event support") Signed-off-by: Richard Henderson Reviewed-by: David Hildenbrand Message-ID: <20240502054417.234340-12-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 5611155ba1..31bd193322 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -361,7 +361,7 @@ DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env) DEF_HELPER_3(lra, i64, env, i64, i64) DEF_HELPER_FLAGS_3(per_check_exception, TCG_CALL_NO_WG, void, env, i64, i32) DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32) -DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(per_store_real, TCG_CALL_NO_WG, noreturn, env, i32) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) From a47d08ee0d45f98284806b2ddeda83bb33a2b351 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:15 -0700 Subject: [PATCH 1016/2884] target/s390x: Simplify per_ifetch, per_check_exception Set per_address and ilen in per_ifetch; this is valid for all PER exceptions and will last until the end of the instruction. Therefore we don't need to give the same data to per_check_exception. Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-13-richard.henderson@linaro.org> [thuth: Silence checkpatch.pl errors] Signed-off-by: Thomas Huth --- target/s390x/helper.h | 4 ++-- target/s390x/tcg/misc_helper.c | 23 +++++++++-------------- target/s390x/tcg/translate.c | 20 ++++++++++++-------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 31bd193322..1a8a76abb9 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -359,9 +359,9 @@ DEF_HELPER_FLAGS_4(ipte, TCG_CALL_NO_RWG, void, env, i64, i64, i32) DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env) DEF_HELPER_3(lra, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(per_check_exception, TCG_CALL_NO_WG, void, env, i64, i32) +DEF_HELPER_FLAGS_1(per_check_exception, TCG_CALL_NO_WG, void, env) DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32) -DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_WG, void, env, i64) +DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_WG, void, env, i32) DEF_HELPER_FLAGS_2(per_store_real, TCG_CALL_NO_WG, noreturn, env, i32) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 43cacc448f..303f86d363 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -604,12 +604,10 @@ static G_NORETURN void per_raise_exception_log(CPUS390XState *env) per_raise_exception(env); } -void HELPER(per_check_exception)(CPUS390XState *env, uint64_t next_pc, - uint32_t ilen) +void HELPER(per_check_exception)(CPUS390XState *env) { + /* psw_addr, per_address and int_pgm_ilen are already set. */ if (unlikely(env->per_perc_atmid)) { - env->psw.addr = next_pc; - env->int_pgm_ilen = ilen; per_raise_exception_log(env); } } @@ -639,23 +637,20 @@ void HELPER(per_branch)(CPUS390XState *env, uint64_t dest, uint32_t ilen) per_raise_exception_log(env); } -void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) +void HELPER(per_ifetch)(CPUS390XState *env, uint32_t ilen) { - if (get_per_in_range(env, addr)) { - env->per_address = addr; + if (get_per_in_range(env, env->psw.addr)) { + env->per_address = env->psw.addr; + env->int_pgm_ilen = ilen; env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env); /* If the instruction has to be nullified, trigger the exception immediately. */ if (env->cregs[9] & PER_CR9_EVENT_IFETCH_NULLIFICATION) { - CPUState *cs = env_cpu(env); - env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION; - env->int_pgm_code = PGM_PER; - env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr)); - - cs->exception_index = EXCP_PGM; - cpu_loop_exit(cs); + qemu_log_mask(CPU_LOG_INT, "PER interrupt before 0x%" PRIx64 "\n", + env->per_address); + per_raise_exception(env); } } } diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 5bb15a46e0..c9a5a1687e 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6258,8 +6258,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { - TCGv_i64 addr = tcg_constant_i64(s->base.pc_next); - gen_helper_per_ifetch(tcg_env, addr); + /* With ifetch set, psw_addr and cc_op are always up-to-date. */ + gen_helper_per_ifetch(tcg_env, tcg_constant_i32(s->ilen)); } #endif @@ -6358,14 +6358,18 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { - TCGv_i64 next_pc = psw_addr; - - if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { - next_pc = tcg_constant_i64(s->pc_tmp); + switch (ret) { + case DISAS_TOO_MANY: + s->base.is_jmp = DISAS_PC_CC_UPDATED; + /* fall through */ + case DISAS_NEXT: + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + break; + default: + break; } update_cc_op(s); - gen_helper_per_check_exception(tcg_env, next_pc, - tcg_constant_i32(s->ilen)); + gen_helper_per_check_exception(tcg_env); } #endif From be0fcbc462bb97605cc5d37b5f13a3a28c1417ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:16 -0700 Subject: [PATCH 1017/2884] target/s390x: Adjust check of noreturn in translate_one If help_op is not set, ret == DISAS_NEXT. Shift the test up from surrounding help_wout, help_cout to skipping to out, as we do elsewhere in the function. Signed-off-by: Richard Henderson Message-ID: <20240502054417.234340-14-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/translate.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index c9a5a1687e..c81e035dea 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6341,14 +6341,15 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } if (insn->help_op) { ret = insn->help_op(s, &o); + if (ret == DISAS_NORETURN) { + goto out; + } } - if (ret != DISAS_NORETURN) { - if (insn->help_wout) { - insn->help_wout(s, &o); - } - if (insn->help_cout) { - insn->help_cout(s, &o); - } + if (insn->help_wout) { + insn->help_wout(s, &o); + } + if (insn->help_cout) { + insn->help_cout(s, &o); } /* io should be the last instruction in tb when icount is enabled */ From fa8718c76681105b55895aa189e3f6f6e0358b2a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 May 2024 22:44:17 -0700 Subject: [PATCH 1018/2884] tests/tcg/s390x: Add per.S Add a small test to avoid regressions. Signed-off-by: Richard Henderson Acked-by: Ilya Leoshkevich Tested-by: Ilya Leoshkevich Message-ID: <20240502054417.234340-15-richard.henderson@linaro.org> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.softmmu-target | 1 + tests/tcg/s390x/per.S | 82 +++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 tests/tcg/s390x/per.S diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 1a1f088b28..80159cccf5 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -25,6 +25,7 @@ ASM_TESTS = \ lpswe-early \ lra \ mc \ + per \ precise-smc-softmmu \ ssm-early \ stosm-early \ diff --git a/tests/tcg/s390x/per.S b/tests/tcg/s390x/per.S new file mode 100644 index 0000000000..79e704a6ff --- /dev/null +++ b/tests/tcg/s390x/per.S @@ -0,0 +1,82 @@ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x96 +per_code: + .org 0x98 +per_address: + .org 0x150 +program_old_psw: + .org 0x1d0 +program_new_psw: + .quad 0, pgm_handler + + .org 0x200 /* exit lowcore */ + +per_on_psw: + .quad 0x4000000000000000, start_per +per_on_regs: + .quad 0x80000000, 0, -1 /* successful-branching everywhere */ +per_off_regs: + .quad 0, 0 ,0 +success_psw: + .quad 0x2000000000000, 0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000, 0 /* disabled wait */ + + .org 0x2000 /* exit lowcore pages */ + + .globl _start +_start: + lpswe per_on_psw +start_per: + lctlg %c9, %c11, per_on_regs + +/* Test unconditional relative branch. */ + larl %r0, j1 + larl %r1, d1 + lhi %r2, 0 +j1: j d1 + lpswe failure_psw +d1: + +/* Test unconditional indirect branch. */ + larl %r0, j2 + larl %r1, d2 +j2: br %r1 + lpswe failure_psw +d2: + +/* Test conditional relative branch. */ + larl %r0, j3 + larl %r1, d3 + clr %r1, %r2 /* d3 != 0 */ +j3: jne d3 + lpswe failure_psw +d3: + +/* Test conditional register branch. */ + larl %r0, j4 + larl %r1, d4 + clr %r1, %r2 /* d4 != 0 */ +j4: bner %r1 + lpswe failure_psw +d4: + +/* Success! */ + nop + lpswe success_psw + +pgm_handler: + chhsi program_interruption_code, 0x80 /* PER event? */ + jne fail + cli per_code, 0x80 /* successful-branching event? */ + jne fail + clg %r0, per_address /* per_address == jump insn? */ + jne fail + clg %r1, program_old_psw+8 /* psw.addr updated to dest? */ + jne fail + lpswe program_old_psw +fail: + lpswe failure_psw From e7fca81e170530104c36bd8f3e1d7e7c11011481 Mon Sep 17 00:00:00 2001 From: Alexander Bulekov Date: Mon, 27 May 2024 00:07:05 -0400 Subject: [PATCH 1019/2884] fuzz: specify audiodev for usb-audio Fixes test-failure on Fedora 40 CI. Reported-by: Thomas Huth Signed-off-by: Alexander Bulekov Reviewed-by: Thomas Huth Message-ID: <20240527040711.311865-1-alxndr@bu.edu> Signed-off-by: Thomas Huth --- tests/qtest/fuzz/generic_fuzz_configs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index 4d7c8ca4ec..ef0ad95712 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -150,7 +150,8 @@ const generic_fuzz_config predefined_configs[] = { "-chardev null,id=cd0 -chardev null,id=cd1 " "-device usb-braille,chardev=cd0 -device usb-ccid -device usb-ccid " "-device usb-kbd -device usb-mouse -device usb-serial,chardev=cd1 " - "-device usb-tablet -device usb-wacom-tablet -device usb-audio", + "-device usb-tablet -device usb-wacom-tablet " + "-device usb-audio,audiodev=snd0 -audiodev none,id=snd0", .objects = "*usb* *uhci* *xhci*", },{ .name = "pc-i440fx", From 3e964275d65b92075249201c49b39dfb06d08ad4 Mon Sep 17 00:00:00 2001 From: Alexander Bulekov Date: Mon, 27 May 2024 10:59:58 -0400 Subject: [PATCH 1020/2884] fuzz: disable leak-detection for oss-fuzz builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are building for OSS-Fuzz, we want to ensure that the fuzzer targets are actually created, regardless of leaks. Leaks will be detected by the subsequent tests of the individual fuzz-targets. Signed-off-by: Alexander Bulekov Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240527150001.325565-1-alxndr@bu.edu> Signed-off-by: Thomas Huth --- scripts/oss-fuzz/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 5238f83343..7398298173 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -92,6 +92,7 @@ make install DESTDIR=$DEST_DIR/qemu-bundle rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/bin rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/libexec +export ASAN_OPTIONS=detect_leaks=0 targets=$(./qemu-fuzz-i386 | grep generic-fuzz | awk '$1 ~ /\*/ {print $2}') base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)" From 84aacddd808b87824bca68006da0f73a1d0905f1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 27 May 2024 14:13:51 +0200 Subject: [PATCH 1021/2884] hw/s390x: Remove unused macro VMSTATE_ADAPTER_ROUTES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not used anywhere, so let's simply remove it. Message-ID: <20240527121351.211266-1-thuth@redhat.com> Reviewed-by: Cédric Le Goater Reviewed-by: Eric Farman Signed-off-by: Thomas Huth --- include/hw/s390x/s390_flic.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index bcb081def5..382d9833f1 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -35,9 +35,6 @@ typedef struct AdapterRoutes { extern const VMStateDescription vmstate_adapter_routes; -#define VMSTATE_ADAPTER_ROUTES(_f, _s) \ - VMSTATE_STRUCT(_f, _s, 1, vmstate_adapter_routes, AdapterRoutes) - #define TYPE_S390_FLIC_COMMON "s390-flic" OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass, S390_FLIC_COMMON) From 3efc75ad9d9317e5709861bbebb2c29390f8e7a2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 27 May 2024 08:02:43 +0200 Subject: [PATCH 1022/2884] scripts/update-linux-headers.sh: Remove temporary directory inbetween We are reusing the same temporary directory for installing the headers of all targets, so there could be stale files here when switching from one target to another. Make sure to delete the folder before installing a new set of target headers into it. Message-ID: <20240527060243.12647-1-thuth@redhat.com> Reviewed-by: Michael S. Tsirkin Acked-by: Cornelia Huck Signed-off-by: Thomas Huth --- scripts/update-linux-headers.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 8963c39189..fbf7e119bc 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -112,6 +112,7 @@ for arch in $ARCHLIST; do arch_var=ARCH fi + rm -rf "$hdrdir" make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install rm -rf "$output/linux-headers/asm-$arch" From bde26d90ae9f7551cac90e117fc7216c807a3bfe Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 27 May 2024 08:01:26 +0200 Subject: [PATCH 1023/2884] scripts/update-linux-headers.sh: Fix the path of setup_data.h When running the update-linx-headers.sh script, it currently fails with: scripts/update-linux-headers.sh: line 73: .../qemu/standard-headers/asm-x86/setup_data.h: No such file or directory The "include" folder is obviously missing here - no clue how this could have worked before? Fixes: 66210a1a30 ("scripts/update-linux-headers: Add setup_data.h to import list") Message-ID: <20240527060126.12578-1-thuth@redhat.com> Reviewed-by: Cornelia Huck Signed-off-by: Thomas Huth --- scripts/update-linux-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index fbf7e119bc..23afe8c08a 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -159,7 +159,7 @@ for arch in $ARCHLIST; do cp_portable "$hdrdir/bootparam.h" \ "$output/include/standard-headers/asm-$arch" cp_portable "$hdrdir/include/asm/setup_data.h" \ - "$output/standard-headers/asm-x86" + "$output/include/standard-headers/asm-x86" fi if [ $arch = riscv ]; then cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" From 2523baf7fb4ddca900647be7ac39bce31eae2d42 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 24 May 2024 14:35:47 +0900 Subject: [PATCH 1024/2884] qemu-keymap: Make references to allocations static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LeakSanitizer complains about allocations whose references are held only by automatic variables. It is possible to free them to suppress the complaints, but it is a chore to make sure they are freed in all exit paths so make them static instead. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240524-xkb-v4-1-2de564e5c859@daynix.com> Signed-off-by: Thomas Huth --- qemu-keymap.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qemu-keymap.c b/qemu-keymap.c index 8c80f7a4ed..701e4332af 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -154,9 +154,9 @@ static xkb_mod_mask_t get_mod(struct xkb_keymap *map, const char *name) int main(int argc, char *argv[]) { - struct xkb_context *ctx; - struct xkb_keymap *map; - struct xkb_state *state; + static struct xkb_context *ctx; + static struct xkb_keymap *map; + static struct xkb_state *state; xkb_mod_index_t mod, mods; int rc; @@ -234,8 +234,6 @@ int main(int argc, char *argv[]) state = xkb_state_new(map); xkb_keymap_key_for_each(map, walk_map, state); - xkb_state_unref(state); - state = NULL; /* add quirks */ fprintf(outfile, From a3b3ad72e81072321a7ea996d722c1eabaca7031 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 24 May 2024 14:35:48 +0900 Subject: [PATCH 1025/2884] lockable: Do not cast function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -fsanitize=undefined complains if function pointers are casted. It also prevents enabling the strict mode of CFI which is currently disabled with -fsanitize-cfi-icall-generalize-pointers. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2345 Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240524-xkb-v4-2-2de564e5c859@daynix.com> Signed-off-by: Thomas Huth --- include/qemu/lockable.h | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 62110d2eb7..66713bd429 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -43,15 +43,30 @@ qemu_null_lockable(void *x) return NULL; } +#define QML_FUNC_(name) \ + static inline void qemu_lockable_ ## name ## _lock(void *x) \ + { \ + qemu_ ## name ## _lock(x); \ + } \ + static inline void qemu_lockable_ ## name ## _unlock(void *x) \ + { \ + qemu_ ## name ## _unlock(x); \ + } + +QML_FUNC_(mutex) +QML_FUNC_(rec_mutex) +QML_FUNC_(co_mutex) +QML_FUNC_(spin) + /* * In C, compound literals have the lifetime of an automatic variable. * In C++ it would be different, but then C++ wouldn't need QemuLockable * either... */ -#define QML_OBJ_(x, name) (&(QemuLockable) { \ - .object = (x), \ - .lock = (QemuLockUnlockFunc *) qemu_ ## name ## _lock, \ - .unlock = (QemuLockUnlockFunc *) qemu_ ## name ## _unlock \ +#define QML_OBJ_(x, name) (&(QemuLockable) { \ + .object = (x), \ + .lock = qemu_lockable_ ## name ## _lock, \ + .unlock = qemu_lockable_ ## name ## _unlock \ }) /** From b04091393e6a71065aee6c91b2566f2dec95a4c9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 24 May 2024 14:35:49 +0900 Subject: [PATCH 1026/2884] qapi: Do not cast function pointers Using -fsanitize=undefined with Clang v18 causes an error if function pointers are casted: qapi/qapi-clone-visitor.c:188:5: runtime error: call to function visit_type_SocketAddress through pointer to incorrect function type 'bool (*)(struct Visitor *, const char *, void **, struct Error **)' /tmp/qemu-ubsan/qapi/qapi-visit-sockets.c:487: note: visit_type_SocketAddress defined here #0 0x5642aa2f7f3b in qapi_clone qapi/qapi-clone-visitor.c:188:5 #1 0x5642aa2c8ce5 in qio_channel_socket_listen_async io/channel-socket.c:285:18 #2 0x5642aa2b8903 in test_io_channel_setup_async tests/unit/test-io-channel-socket.c:116:5 #3 0x5642aa2b8204 in test_io_channel tests/unit/test-io-channel-socket.c:179:9 #4 0x5642aa2b8129 in test_io_channel_ipv4 tests/unit/test-io-channel-socket.c:323:5 ... It also prevents enabling the strict mode of CFI which is currently disabled with -fsanitize-cfi-icall-generalize-pointers. The problematic casts are necessary to pass visit_type_T() and visit_type_T_members() as callbacks to qapi_clone() and qapi_clone_members(), respectively. Open-code these two functions to avoid the callbacks, and thus the type casts. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2346 Signed-off-by: Akihiko Odaki Reviewed-by: Markus Armbruster Message-ID: <20240524-xkb-v4-3-2de564e5c859@daynix.com> [thuth: Improve commit message according to Markus' suggestions] Signed-off-by: Thomas Huth --- include/qapi/clone-visitor.h | 37 +++++++++++++++++++++++------------- qapi/qapi-clone-visitor.c | 30 ++++------------------------- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h index adf9a788e2..ebc182b034 100644 --- a/include/qapi/clone-visitor.h +++ b/include/qapi/clone-visitor.h @@ -11,6 +11,7 @@ #ifndef QAPI_CLONE_VISITOR_H #define QAPI_CLONE_VISITOR_H +#include "qapi/error.h" #include "qapi/visitor.h" /* @@ -20,11 +21,8 @@ */ typedef struct QapiCloneVisitor QapiCloneVisitor; -void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *, - void **, Error **)); -void qapi_clone_members(void *dst, const void *src, size_t sz, - bool (*visit_type_members)(Visitor *, void *, - Error **)); +Visitor *qapi_clone_visitor_new(void); +Visitor *qapi_clone_members_visitor_new(void); /* * Deep-clone QAPI object @src of the given @type, and return the result. @@ -32,10 +30,18 @@ void qapi_clone_members(void *dst, const void *src, size_t sz, * Not usable on QAPI scalars (integers, strings, enums), nor on a * QAPI object that references the 'any' type. Safe when @src is NULL. */ -#define QAPI_CLONE(type, src) \ - ((type *)qapi_clone(src, \ - (bool (*)(Visitor *, const char *, void **, \ - Error **))visit_type_ ## type)) +#define QAPI_CLONE(type, src) \ + ({ \ + Visitor *v_; \ + type *dst_ = (type *) (src); /* Cast away const */ \ + \ + if (dst_) { \ + v_ = qapi_clone_visitor_new(); \ + visit_type_ ## type(v_, NULL, &dst_, &error_abort); \ + visit_free(v_); \ + } \ + dst_; \ + }) /* * Copy deep clones of @type members from @src to @dst. @@ -43,9 +49,14 @@ void qapi_clone_members(void *dst, const void *src, size_t sz, * Not usable on QAPI scalars (integers, strings, enums), nor on a * QAPI object that references the 'any' type. */ -#define QAPI_CLONE_MEMBERS(type, dst, src) \ - qapi_clone_members(dst, src, sizeof(type), \ - (bool (*)(Visitor *, void *, \ - Error **))visit_type_ ## type ## _members) +#define QAPI_CLONE_MEMBERS(type, dst, src) \ + ({ \ + Visitor *v_; \ + \ + v_ = qapi_clone_members_visitor_new(); \ + *(type *)(dst) = *(src); \ + visit_type_ ## type ## _members(v_, (type *)(dst), &error_abort); \ + visit_free(v_); \ + }) #endif diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c index c45c5caa3b..bbf953698f 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -149,7 +149,7 @@ static void qapi_clone_free(Visitor *v) g_free(v); } -static Visitor *qapi_clone_visitor_new(void) +Visitor *qapi_clone_visitor_new(void) { QapiCloneVisitor *v; @@ -174,31 +174,9 @@ static Visitor *qapi_clone_visitor_new(void) return &v->visitor; } -void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *, - void **, Error **)) +Visitor *qapi_clone_members_visitor_new(void) { - Visitor *v; - void *dst = (void *) src; /* Cast away const */ - - if (!src) { - return NULL; - } - - v = qapi_clone_visitor_new(); - visit_type(v, NULL, &dst, &error_abort); - visit_free(v); - return dst; -} - -void qapi_clone_members(void *dst, const void *src, size_t sz, - bool (*visit_type_members)(Visitor *, void *, - Error **)) -{ - Visitor *v; - - v = qapi_clone_visitor_new(); - memcpy(dst, src, sz); + Visitor *v = qapi_clone_visitor_new(); to_qcv(v)->depth++; - visit_type_members(v, dst, &error_abort); - visit_free(v); + return v; } From 199e84de1c903ba5aa1f7256310bbc4a20dd930b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 17 May 2024 21:50:14 -0500 Subject: [PATCH 1027/2884] qio: Inherit follow_coroutine_ctx across TLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since qemu 8.2, the combination of NBD + TLS + iothread crashes on an assertion failure: qemu-kvm: ../io/channel.c:534: void qio_channel_restart_read(void *): Assertion `qemu_get_current_aio_context() == qemu_coroutine_get_aio_context(co)' failed. It turns out that when we removed AioContext locking, we did so by having NBD tell its qio channels that it wanted to opt in to qio_channel_set_follow_coroutine_ctx(); but while we opted in on the main channel, we did not opt in on the TLS wrapper channel. qemu-iotests has coverage of NBD+iothread and NBD+TLS, but apparently no coverage of NBD+TLS+iothread, or we would have noticed this regression sooner. (I'll add that in the next patch) But while we could manually opt in to the TLS channel in nbd/server.c (a one-line change), it is more generic if all qio channels that wrap other channels inherit the follow status, in the same way that they inherit feature bits. CC: Stefan Hajnoczi CC: Daniel P. Berrangé CC: qemu-stable@nongnu.org Fixes: https://issues.redhat.com/browse/RHEL-34786 Fixes: 06e0f098 ("io: follow coroutine AioContext in qio_channel_yield()", v8.2.0) Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Daniel P. Berrangé Message-ID: <20240518025246.791593-5-eblake@redhat.com> --- io/channel-tls.c | 26 +++++++++++++++----------- io/channel-websock.c | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/io/channel-tls.c b/io/channel-tls.c index 1d9c9c72bf..67b9700006 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -69,37 +69,40 @@ qio_channel_tls_new_server(QIOChannel *master, const char *aclname, Error **errp) { - QIOChannelTLS *ioc; + QIOChannelTLS *tioc; + QIOChannel *ioc; - ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); + tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); + ioc = QIO_CHANNEL(tioc); - ioc->master = master; + tioc->master = master; + ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { - qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SHUTDOWN); + qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } object_ref(OBJECT(master)); - ioc->session = qcrypto_tls_session_new( + tioc->session = qcrypto_tls_session_new( creds, NULL, aclname, QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, errp); - if (!ioc->session) { + if (!tioc->session) { goto error; } qcrypto_tls_session_set_callbacks( - ioc->session, + tioc->session, qio_channel_tls_write_handler, qio_channel_tls_read_handler, - ioc); + tioc); - trace_qio_channel_tls_new_server(ioc, master, creds, aclname); - return ioc; + trace_qio_channel_tls_new_server(tioc, master, creds, aclname); + return tioc; error: - object_unref(OBJECT(ioc)); + object_unref(OBJECT(tioc)); return NULL; } @@ -116,6 +119,7 @@ qio_channel_tls_new_client(QIOChannel *master, ioc = QIO_CHANNEL(tioc); tioc->master = master; + ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } diff --git a/io/channel-websock.c b/io/channel-websock.c index a12acc27cf..de39f0d182 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -883,6 +883,7 @@ qio_channel_websock_new_server(QIOChannel *master) ioc = QIO_CHANNEL(wioc); wioc->master = master; + ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } From f5e328fef057a79ee40a93cdb27bf0de7991973e Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 24 May 2024 13:32:55 +0200 Subject: [PATCH 1028/2884] hw/intc/arm_gic: Fix set pending of PPIs According to the GICv2 specification section 4.3.7, "Interrupt Set-Pending Registers, GICD_ISPENDRn": "In a multiprocessor implementation, GICD_ISPENDR0 is banked for each connected processor. This register holds the Set-pending bits for interrupts 0-31." Signed-off-by: Sebastian Huber Message-id: 20240524113256.8102-2-sebastian.huber@embedded-brains.de Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index e4b8437f8b..04e5a11660 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -1308,12 +1308,15 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for (i = 0; i < 8; i++) { if (value & (1 << i)) { + int mask = (irq < GIC_INTERNAL) ? (1 << cpu) + : GIC_DIST_TARGET(irq + i); + if (s->security_extn && !attrs.secure && !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - GIC_DIST_SET_PENDING(irq + i, GIC_DIST_TARGET(irq + i)); + GIC_DIST_SET_PENDING(irq + i, mask); } } } else if (offset < 0x300) { From d9aff83ad569714ec1b05176942a80fd80e062b7 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 24 May 2024 13:32:56 +0200 Subject: [PATCH 1029/2884] hw/intc/arm_gic: Fix writes to GICD_ITARGETSRn According to the GICv2 specification section 4.3.12, "Interrupt Processor Targets Registers, GICD_ITARGETSRn": "Any change to a CPU targets field value: [...] * Has an effect on any pending interrupts. This means: - adding a CPU interface to the target list of a pending interrupt makes that interrupt pending on that CPU interface - removing a CPU interface from the target list of a pending interrupt removes the pending state of that interrupt on that CPU interface." Signed-off-by: Sebastian Huber Message-id: 20240524113256.8102-3-sebastian.huber@embedded-brains.de Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 04e5a11660..806832439b 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -1410,6 +1410,13 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, value = ALL_CPU_MASK; } s->irq_target[irq] = value & ALL_CPU_MASK; + if (irq >= GIC_INTERNAL && s->irq_state[irq].pending) { + /* + * Changing the target of an interrupt that is currently + * pending updates the set of CPUs it is pending on. + */ + s->irq_state[irq].pending = value & ALL_CPU_MASK; + } } } else if (offset < 0xf00) { /* Interrupt Configuration. */ From f271877307f1bb43ac4031bf6d962bdd86caa498 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 24 May 2024 14:08:36 +0200 Subject: [PATCH 1030/2884] hw/arm/xilinx_zynq: Add cache controller The Zynq 7000 SoCs contain a CoreLink L2C-310 cache controller. Add the corresponding Qemu device to the xilinx-zynq-a9 machine. Signed-off-by: Sebastian Huber Message-id: 20240524120837.10057-2-sebastian.huber@embedded-brains.de Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/xilinx_zynq.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 8b97683a45..1ad60da7aa 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -370,6 +370,7 @@ config ZYNQ select A9MPCORE select CADENCE # UART select PFLASH_CFI02 + select PL310 # cache controller select PL330 select SDHCI select SSI_M25P80 diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index fc3abcbe88..0abb62f131 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -241,6 +241,7 @@ static void zynq_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); + sysbus_create_varargs("l2x0", MPCORE_PERIPHBASE + 0x2000, NULL); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); sysbus_connect_irq(busdev, 1, From ddcf58e044ce02864170b41785344407cb821e12 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 24 May 2024 14:08:37 +0200 Subject: [PATCH 1031/2884] hw/arm/xilinx_zynq: Support up to two CPU cores The Zynq 7000 SoCs contain two Arm Cortex-A9 MPCore (the Zynq 7000S have only one core). Add support for up to two simulated cores. Signed-off-by: Sebastian Huber Message-id: 20240524120837.10057-3-sebastian.huber@embedded-brains.de Reviewed-by: Peter Maydell [PMM: removed unnecessary double-cast] Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 54 +++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 0abb62f131..7f7a3d23fb 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -84,9 +84,12 @@ static const int dma_irqs[8] = { 0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \ 0xe5801000 + (addr) +#define ZYNQ_MAX_CPUS 2 + struct ZynqMachineState { MachineState parent; Clock *ps_clk; + ARMCPU *cpu[ZYNQ_MAX_CPUS]; }; static void zynq_write_board_setup(ARMCPU *cpu, @@ -176,13 +179,13 @@ static inline int zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, static void zynq_init(MachineState *machine) { ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine); - ARMCPU *cpu; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); DeviceState *dev, *slcr; SysBusDevice *busdev; qemu_irq pic[64]; int n; + unsigned int smp_cpus = machine->smp.cpus; /* max 2GB ram */ if (machine->ram_size > 2 * GiB) { @@ -190,22 +193,27 @@ static void zynq_init(MachineState *machine) exit(EXIT_FAILURE); } - cpu = ARM_CPU(object_new(machine->cpu_type)); + for (n = 0; n < smp_cpus; n++) { + Object *cpuobj = object_new(machine->cpu_type); - /* By default A9 CPUs have EL3 enabled. This board does not - * currently support EL3 so the CPU EL3 property is disabled before - * realization. - */ - if (object_property_find(OBJECT(cpu), "has_el3")) { - object_property_set_bool(OBJECT(cpu), "has_el3", false, &error_fatal); + /* + * By default A9 CPUs have EL3 enabled. This board does not currently + * support EL3 so the CPU EL3 property is disabled before realization. + */ + if (object_property_find(cpuobj, "has_el3")) { + object_property_set_bool(cpuobj, "has_el3", false, &error_fatal); + } + + object_property_set_int(cpuobj, "midr", ZYNQ_BOARD_MIDR, + &error_fatal); + object_property_set_int(cpuobj, "reset-cbar", MPCORE_PERIPHBASE, + &error_fatal); + + qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); + + zynq_machine->cpu[n] = ARM_CPU(cpuobj); } - object_property_set_int(OBJECT(cpu), "midr", ZYNQ_BOARD_MIDR, - &error_fatal); - object_property_set_int(OBJECT(cpu), "reset-cbar", MPCORE_PERIPHBASE, - &error_fatal); - qdev_realize(DEVICE(cpu), NULL, &error_fatal); - /* DDR remapped to address zero. */ memory_region_add_subregion(address_space_mem, 0, machine->ram); @@ -237,15 +245,19 @@ static void zynq_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(slcr), 0, 0xF8000000); dev = qdev_new(TYPE_A9MPCORE_PRIV); - qdev_prop_set_uint32(dev, "num-cpu", 1); + qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); + zynq_binfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; sysbus_create_varargs("l2x0", MPCORE_PERIPHBASE + 0x2000, NULL); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); - sysbus_connect_irq(busdev, 1, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ)); + for (n = 0; n < smp_cpus; n++) { + DeviceState *cpudev = DEVICE(zynq_machine->cpu[n]); + sysbus_connect_irq(busdev, (2 * n) + 0, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(busdev, (2 * n) + 1, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } for (n = 0; n < 64; n++) { pic[n] = qdev_get_gpio_in(dev, n); @@ -350,7 +362,7 @@ static void zynq_init(MachineState *machine) zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; zynq_binfo.write_board_setup = zynq_write_board_setup; - arm_load_kernel(cpu, machine, &zynq_binfo); + arm_load_kernel(zynq_machine->cpu[0], machine, &zynq_binfo); } static void zynq_machine_class_init(ObjectClass *oc, void *data) @@ -362,7 +374,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; mc->init = zynq_init; - mc->max_cpus = 1; + mc->max_cpus = ZYNQ_MAX_CPUS; mc->no_sdcard = 1; mc->ignore_memory_transaction_failures = true; mc->valid_cpu_types = valid_cpu_types; From c19779ce18229a7a01e276ef68c9ce98998fc113 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Tue, 28 May 2024 20:29:17 +0200 Subject: [PATCH 1032/2884] tests/avocado: update sbsa-ref firmware Partial support for NUMA setup: - cpu nodes - memory nodes Used versions: - Trusted Firmware v2.11.0 - Tianocore EDK2 stable202405 - Tianocore EDK2 Platforms code commit 4bbd0ed Firmware is built using Debian 'bookworm' cross toolchain (gcc 12.2.0). Signed-off-by: Peter Maydell --- tests/avocado/machine_aarch64_sbsaref.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/avocado/machine_aarch64_sbsaref.py b/tests/avocado/machine_aarch64_sbsaref.py index 98c76c1ff7..6bb82f2a03 100644 --- a/tests/avocado/machine_aarch64_sbsaref.py +++ b/tests/avocado/machine_aarch64_sbsaref.py @@ -37,18 +37,18 @@ class Aarch64SbsarefMachine(QemuSystemTest): Used components: - - Trusted Firmware 2.10.2 - - Tianocore EDK2 stable202402 - - Tianocore EDK2-platforms commit 085c2fb + - Trusted Firmware 2.11.0 + - Tianocore EDK2 stable202405 + - Tianocore EDK2-platforms commit 4bbd0ed """ # Secure BootRom (TF-A code) fs0_xz_url = ( "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" - "20240313-116475/edk2/SBSA_FLASH0.fd.xz" + "20240528-140808/edk2/SBSA_FLASH0.fd.xz" ) - fs0_xz_hash = "637593749cc307dea7dc13265c32e5d020267552f22b18a31850b8429fc5e159" + fs0_xz_hash = "fa6004900b67172914c908b78557fec4d36a5f784f4c3dd08f49adb75e1892a9" tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash, algorithm='sha256') archive.extract(tar_xz_path, self.workdir) @@ -57,9 +57,9 @@ class Aarch64SbsarefMachine(QemuSystemTest): # Non-secure rom (UEFI and EFI variables) fs1_xz_url = ( "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" - "20240313-116475/edk2/SBSA_FLASH1.fd.xz" + "20240528-140808/edk2/SBSA_FLASH1.fd.xz" ) - fs1_xz_hash = "cb0a5e8cf5e303c5d3dc106cfd5943ffe9714b86afddee7164c69ee1dd41991c" + fs1_xz_hash = "5f3747d4000bc416d9641e33ff4ac60c3cc8cb74ca51b6e932e58531c62eb6f7" tar_xz_path = self.fetch_asset(fs1_xz_url, asset_hash=fs1_xz_hash, algorithm='sha256') archive.extract(tar_xz_path, self.workdir) @@ -98,15 +98,15 @@ class Aarch64SbsarefMachine(QemuSystemTest): # AP Trusted ROM wait_for_console_pattern(self, "Booting Trusted Firmware") - wait_for_console_pattern(self, "BL1: v2.10.2(release):") + wait_for_console_pattern(self, "BL1: v2.11.0(release):") wait_for_console_pattern(self, "BL1: Booting BL2") # Trusted Boot Firmware - wait_for_console_pattern(self, "BL2: v2.10.2(release)") + wait_for_console_pattern(self, "BL2: v2.11.0(release)") wait_for_console_pattern(self, "Booting BL31") # EL3 Runtime Software - wait_for_console_pattern(self, "BL31: v2.10.2(release)") + wait_for_console_pattern(self, "BL31: v2.11.0(release)") # Non-trusted Firmware wait_for_console_pattern(self, "UEFI firmware (version 1.0") From b1d592e7b0a7301eae8e3baa99744ac35db3cd2a Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Thu, 23 May 2024 18:53:53 +0200 Subject: [PATCH 1033/2884] arm/sbsa-ref: move to Neoverse-N2 as default Moving to Neoverse-N2 gives us several cpu features to use for expanding our platform: - branch target identification - pointer authentication - RME for confidential computing - RNG for EFI_PROTOCOL_RNG - SVE being enabled by default We do not go for "max" as default to have stable set of features enabled by default. It is still supported and can be selected with "--cpu" argument. Signed-off-by: Marcin Juszkiewicz Reviewed-by: Leif Lindholm Message-id: 20240523165353.6547-1-marcin.juszkiewicz@linaro.org Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 57c337fd92..e884692f07 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -891,7 +891,7 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) mc->init = sbsa_ref_init; mc->desc = "QEMU 'SBSA Reference' ARM Virtual Machine"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("neoverse-n1"); + mc->default_cpu_type = ARM_CPU_TYPE_NAME("neoverse-n2"); mc->valid_cpu_types = valid_cpu_types; mc->max_cpus = 512; mc->pci_allow_0_address = true; From 76f4a8aecae7f287d6e7f3b763ae88382c3a5457 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:13 -0700 Subject: [PATCH 1034/2884] target/arm: Improve vector UQADD, UQSUB, SQADD, SQSUB No need for a full comparison; xor produces non-zero bits for QC just fine. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 22c9d17dce..bfe6885a01 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1217,21 +1217,21 @@ void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } -static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, +static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { TCGv_vec x = tcg_temp_new_vec_matching(t); tcg_gen_add_vec(vece, x, a, b); tcg_gen_usadd_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); } void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) { static const TCGOpcode vecop_list[] = { - INDEX_op_usadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 + INDEX_op_usadd_vec, INDEX_op_add_vec, 0 }; static const GVecGen4 ops[4] = { { .fniv = gen_uqadd_vec, @@ -1259,21 +1259,21 @@ void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } -static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, +static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { TCGv_vec x = tcg_temp_new_vec_matching(t); tcg_gen_add_vec(vece, x, a, b); tcg_gen_ssadd_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); } void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) { static const TCGOpcode vecop_list[] = { - INDEX_op_ssadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 + INDEX_op_ssadd_vec, INDEX_op_add_vec, 0 }; static const GVecGen4 ops[4] = { { .fniv = gen_sqadd_vec, @@ -1301,21 +1301,21 @@ void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } -static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, +static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { TCGv_vec x = tcg_temp_new_vec_matching(t); tcg_gen_sub_vec(vece, x, a, b); tcg_gen_ussub_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); } void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) { static const TCGOpcode vecop_list[] = { - INDEX_op_ussub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 + INDEX_op_ussub_vec, INDEX_op_sub_vec, 0 }; static const GVecGen4 ops[4] = { { .fniv = gen_uqsub_vec, @@ -1343,21 +1343,21 @@ void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } -static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, +static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { TCGv_vec x = tcg_temp_new_vec_matching(t); tcg_gen_sub_vec(vece, x, a, b); tcg_gen_sssub_vec(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); } void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) { static const TCGOpcode vecop_list[] = { - INDEX_op_sssub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 + INDEX_op_sssub_vec, INDEX_op_sub_vec, 0 }; static const GVecGen4 ops[4] = { { .fniv = gen_sqsub_vec, From 01d5665bc33c3eeadd3d11d8f2446b40601eae17 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:14 -0700 Subject: [PATCH 1035/2884] target/arm: Assert oprsz in range when using vfp.qc Suggested-by: Peter Maydell Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240528203044.612851-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index bfe6885a01..3e2d3c21a1 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -29,6 +29,7 @@ static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, { TCGv_ptr qc_ptr = tcg_temp_new_ptr(); + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc)); tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, qc_ptr, opr_sz, max_sz, 0, fn); @@ -1255,6 +1256,8 @@ void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .opt_opc = vecop_list, .vece = MO_64 }, }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } @@ -1297,6 +1300,8 @@ void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .write_aofs = true, .vece = MO_64 }, }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } @@ -1339,6 +1344,8 @@ void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .write_aofs = true, .vece = MO_64 }, }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } @@ -1381,6 +1388,8 @@ void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .write_aofs = true, .vece = MO_64 }, }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } From 8f6343ae18d745653a4668cc0924016444d76460 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:15 -0700 Subject: [PATCH 1036/2884] target/arm: Convert SUQADD and USQADD to gvec Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 16 +++++ target/arm/tcg/gengvec64.c | 110 ++++++++++++++++++++++++++++++++ target/arm/tcg/translate-a64.c | 113 ++++++++++++++------------------- target/arm/tcg/translate-a64.h | 6 ++ target/arm/tcg/vec_helper.c | 64 +++++++++++++++++++ 5 files changed, 245 insertions(+), 64 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index f830531dd3..de2c5c9aef 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -836,6 +836,22 @@ DEF_HELPER_FLAGS_5(gvec_sqsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_sqsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c index 093b498b13..b3afabd38b 100644 --- a/target/arm/tcg/gengvec64.c +++ b/target/arm/tcg/gengvec64.c @@ -188,3 +188,113 @@ void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); } +static void gen_suqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec max = + tcg_constant_vec_matching(t, vece, (1ull << ((8 << vece) - 1)) - 1); + TCGv_vec u = tcg_temp_new_vec_matching(t); + + /* Maximum value that can be added to @a without overflow. */ + tcg_gen_sub_vec(vece, u, max, a); + + /* Constrain addend so that the next addition never overflows. */ + tcg_gen_umin_vec(vece, u, u, b); + tcg_gen_add_vec(vece, t, u, a); + + /* Compute QC by comparing the adjusted @b. */ + tcg_gen_xor_vec(vece, u, u, b); + tcg_gen_or_vec(vece, qc, qc, u); +} + +void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_add_vec, INDEX_op_sub_vec, INDEX_op_umin_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_usqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec u = tcg_temp_new_vec_matching(t); + TCGv_vec z = tcg_constant_vec_matching(t, vece, 0); + + /* Compute unsigned saturation of add for +b and sub for -b. */ + tcg_gen_neg_vec(vece, t, b); + tcg_gen_usadd_vec(vece, u, a, b); + tcg_gen_ussub_vec(vece, t, a, t); + + /* Select the correct result depending on the sign of b. */ + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, t, b, z, t, u); + + /* Compute QC by comparing against the non-saturated result. */ + tcg_gen_add_vec(vece, u, a, b); + tcg_gen_xor_vec(vece, u, u, t); + tcg_gen_or_vec(vece, qc, qc, u); +} + +void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_add_vec, + INDEX_op_usadd_vec, INDEX_op_ussub_vec, + INDEX_op_cmpsel_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9167e4d0bd..9f948e033e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9983,83 +9983,68 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, /* Remaining saturating accumulating ops */ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, - bool is_q, int size, int rn, int rd) + bool is_q, unsigned size, int rn, int rd) { - bool is_double = (size == 3); + if (!is_scalar) { + gen_gvec_fn3(s, is_q, rd, rd, rn, + is_u ? gen_gvec_usqadd_qc : gen_gvec_suqadd_qc, size); + return; + } - if (is_double) { + if (size == 3) { TCGv_i64 tcg_rn = tcg_temp_new_i64(); TCGv_i64 tcg_rd = tcg_temp_new_i64(); - int pass; - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - read_vec_element(s, tcg_rn, rn, pass, MO_64); - read_vec_element(s, tcg_rd, rd, pass, MO_64); + read_vec_element(s, tcg_rn, rn, 0, MO_64); + read_vec_element(s, tcg_rd, rd, 0, MO_64); - if (is_u) { /* USQADD */ - gen_helper_neon_uqadd_s64(tcg_rd, tcg_env, tcg_rn, tcg_rd); - } else { /* SUQADD */ - gen_helper_neon_sqadd_u64(tcg_rd, tcg_env, tcg_rn, tcg_rd); - } - write_vec_element(s, tcg_rd, rd, pass, MO_64); + if (is_u) { /* USQADD */ + gen_helper_neon_uqadd_s64(tcg_rd, tcg_env, tcg_rn, tcg_rd); + } else { /* SUQADD */ + gen_helper_neon_sqadd_u64(tcg_rd, tcg_env, tcg_rn, tcg_rd); } - clear_vec_high(s, !is_scalar, rd); + write_vec_element(s, tcg_rd, rd, 0, MO_64); + clear_vec_high(s, false, rd); } else { TCGv_i32 tcg_rn = tcg_temp_new_i32(); TCGv_i32 tcg_rd = tcg_temp_new_i32(); - int pass, maxpasses; - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; + read_vec_element_i32(s, tcg_rn, rn, 0, size); + read_vec_element_i32(s, tcg_rd, rd, 0, size); + + if (is_u) { /* USQADD */ + switch (size) { + case 0: + gen_helper_neon_uqadd_s8(tcg_rd, tcg_env, tcg_rn, tcg_rd); + break; + case 1: + gen_helper_neon_uqadd_s16(tcg_rd, tcg_env, tcg_rn, tcg_rd); + break; + case 2: + gen_helper_neon_uqadd_s32(tcg_rd, tcg_env, tcg_rn, tcg_rd); + break; + default: + g_assert_not_reached(); + } + } else { /* SUQADD */ + switch (size) { + case 0: + gen_helper_neon_sqadd_u8(tcg_rd, tcg_env, tcg_rn, tcg_rd); + break; + case 1: + gen_helper_neon_sqadd_u16(tcg_rd, tcg_env, tcg_rn, tcg_rd); + break; + case 2: + gen_helper_neon_sqadd_u32(tcg_rd, tcg_env, tcg_rn, tcg_rd); + break; + default: + g_assert_not_reached(); + } } - for (pass = 0; pass < maxpasses; pass++) { - if (is_scalar) { - read_vec_element_i32(s, tcg_rn, rn, pass, size); - read_vec_element_i32(s, tcg_rd, rd, pass, size); - } else { - read_vec_element_i32(s, tcg_rn, rn, pass, MO_32); - read_vec_element_i32(s, tcg_rd, rd, pass, MO_32); - } - - if (is_u) { /* USQADD */ - switch (size) { - case 0: - gen_helper_neon_uqadd_s8(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 1: - gen_helper_neon_uqadd_s16(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 2: - gen_helper_neon_uqadd_s32(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - default: - g_assert_not_reached(); - } - } else { /* SUQADD */ - switch (size) { - case 0: - gen_helper_neon_sqadd_u8(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 1: - gen_helper_neon_sqadd_u16(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 2: - gen_helper_neon_sqadd_u32(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - default: - g_assert_not_reached(); - } - } - - if (is_scalar) { - write_vec_element(s, tcg_constant_i64(0), rd, 0, MO_64); - } - write_vec_element_i32(s, tcg_rd, rd, pass, MO_32); - } - clear_vec_high(s, is_q, rd); + write_vec_element(s, tcg_constant_i64(0), rd, 0, MO_64); + write_vec_element_i32(s, tcg_rd, rd, 0, MO_32); + clear_vec_high(s, false, rd); } } diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 91750f0ca9..b5cb26f8a2 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -197,6 +197,12 @@ void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, uint32_t a, uint32_t oprsz, uint32_t maxsz); void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, uint32_t a, uint32_t oprsz, uint32_t maxsz); +void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 56fea14edb..d8e96386be 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1555,6 +1555,14 @@ DO_SAT(gvec_sqsub_b, int, int8_t, int8_t, -, INT8_MIN, INT8_MAX) DO_SAT(gvec_sqsub_h, int, int16_t, int16_t, -, INT16_MIN, INT16_MAX) DO_SAT(gvec_sqsub_s, int64_t, int32_t, int32_t, -, INT32_MIN, INT32_MAX) +DO_SAT(gvec_usqadd_b, int, uint8_t, int8_t, +, 0, UINT8_MAX) +DO_SAT(gvec_usqadd_h, int, uint16_t, int16_t, +, 0, UINT16_MAX) +DO_SAT(gvec_usqadd_s, int64_t, uint32_t, int32_t, +, 0, UINT32_MAX) + +DO_SAT(gvec_suqadd_b, int, int8_t, uint8_t, +, INT8_MIN, INT8_MAX) +DO_SAT(gvec_suqadd_h, int, int16_t, uint16_t, +, INT16_MIN, INT16_MAX) +DO_SAT(gvec_suqadd_s, int64_t, int32_t, uint32_t, +, INT32_MIN, INT32_MAX) + #undef DO_SAT void HELPER(gvec_uqadd_d)(void *vd, void *vq, void *vn, @@ -1645,6 +1653,62 @@ void HELPER(gvec_sqsub_d)(void *vd, void *vq, void *vn, clear_tail(d, oprsz, simd_maxsz(desc)); } +void HELPER(gvec_usqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + uint64_t nn = n[i]; + int64_t mm = m[i]; + uint64_t dd = nn + mm; + + if (mm < 0) { + if (nn < (uint64_t)-mm) { + dd = 0; + q = true; + } + } else { + if (dd < nn) { + dd = UINT64_MAX; + q = true; + } + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_suqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + int64_t nn = n[i]; + uint64_t mm = m[i]; + int64_t dd = nn + mm; + + if (mm > (uint64_t)(INT64_MAX - nn)) { + dd = INT64_MAX; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} #define DO_SRA(NAME, TYPE) \ void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ From 1217edace8a5f1f8c4eb7f0648ff47eccd08bc8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:16 -0700 Subject: [PATCH 1037/2884] target/arm: Inline scalar SUQADD and USQADD This eliminates the last uses of these neon helpers. Incorporate the MO_64 expanders as an option to the vector expander. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 8 -- target/arm/tcg/gengvec64.c | 71 ++++++++++++++ target/arm/tcg/neon_helper.c | 165 --------------------------------- target/arm/tcg/translate-a64.c | 73 +++++---------- target/arm/tcg/translate-a64.h | 8 ++ 5 files changed, 103 insertions(+), 222 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index de2c5c9aef..c76158d6d3 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -274,14 +274,6 @@ DEF_HELPER_FLAGS_3(neon_qadd_u16, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(neon_qadd_s16, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(neon_qadd_u32, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(neon_qadd_s32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s64, TCG_CALL_NO_RWG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(neon_sqadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_sqadd_u16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_sqadd_u32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_sqadd_u64, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32) DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32) DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32) diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c index b3afabd38b..2617cde0a5 100644 --- a/target/arm/tcg/gengvec64.c +++ b/target/arm/tcg/gengvec64.c @@ -188,6 +188,38 @@ void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); } +/* + * Set @res to the correctly saturated result. + * Set @qc non-zero if saturation occured. + */ +void gen_suqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + TCGv_i64 max = tcg_constant_i64((1ull << ((8 << esz) - 1)) - 1); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_add_i64(t, a, b); + tcg_gen_smin_i64(res, t, max); + tcg_gen_xor_i64(t, t, res); + tcg_gen_or_i64(qc, qc, t); +} + +void gen_suqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 max = tcg_constant_i64(INT64_MAX); + TCGv_i64 t = tcg_temp_new_i64(); + + /* Maximum value that can be added to @a without overflow. */ + tcg_gen_sub_i64(t, max, a); + + /* Constrain addend so that the next addition never overflows. */ + tcg_gen_umin_i64(t, t, b); + tcg_gen_add_i64(res, a, t); + + tcg_gen_xor_i64(t, t, b); + tcg_gen_or_i64(qc, qc, t); +} + static void gen_suqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { @@ -231,6 +263,7 @@ void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, .write_aofs = true, .vece = MO_32 }, { .fniv = gen_suqadd_vec, + .fni8 = gen_suqadd_d, .fno = gen_helper_gvec_suqadd_d, .opt_opc = vecop_list, .write_aofs = true, @@ -242,6 +275,43 @@ void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +void gen_usqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + TCGv_i64 max = tcg_constant_i64(MAKE_64BIT_MASK(0, 8 << esz)); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_add_i64(tmp, a, b); + tcg_gen_smin_i64(res, tmp, max); + tcg_gen_smax_i64(res, res, zero); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_usqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 tneg = tcg_temp_new_i64(); + TCGv_i64 tpos = tcg_temp_new_i64(); + TCGv_i64 max = tcg_constant_i64(UINT64_MAX); + TCGv_i64 zero = tcg_constant_i64(0); + + tcg_gen_add_i64(tmp, a, b); + + /* If @b is positive, saturate if (a + b) < a, aka unsigned overflow. */ + tcg_gen_movcond_i64(TCG_COND_LTU, tpos, tmp, a, max, tmp); + + /* If @b is negative, saturate if a < -b, ie subtraction is negative. */ + tcg_gen_neg_i64(tneg, b); + tcg_gen_movcond_i64(TCG_COND_LTU, tneg, a, tneg, zero, tmp); + + /* Select correct result from sign of @b. */ + tcg_gen_movcond_i64(TCG_COND_LT, res, b, zero, tneg, tpos); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + static void gen_usqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { @@ -288,6 +358,7 @@ void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, .write_aofs = true, .vece = MO_32 }, { .fniv = gen_usqadd_vec, + .fni8 = gen_usqadd_d, .fno = gen_helper_gvec_usqadd_d, .opt_opc = vecop_list, .write_aofs = true, diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index a0b51c8809..9505a5fd18 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -236,171 +236,6 @@ uint64_t HELPER(neon_qadd_s64)(CPUARMState *env, uint64_t src1, uint64_t src2) return res; } -/* Unsigned saturating accumulate of signed value - * - * Op1/Rn is treated as signed - * Op2/Rd is treated as unsigned - * - * Explicit casting is used to ensure the correct sign extension of - * inputs. The result is treated as a unsigned value and saturated as such. - * - * We use a macro for the 8/16 bit cases which expects signed integers of va, - * vb, and vr for interim calculation and an unsigned 32 bit result value r. - */ - -#define USATACC(bits, shift) \ - do { \ - va = sextract32(a, shift, bits); \ - vb = extract32(b, shift, bits); \ - vr = va + vb; \ - if (vr > UINT##bits##_MAX) { \ - SET_QC(); \ - vr = UINT##bits##_MAX; \ - } else if (vr < 0) { \ - SET_QC(); \ - vr = 0; \ - } \ - r = deposit32(r, shift, bits, vr); \ - } while (0) - -uint32_t HELPER(neon_uqadd_s8)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int16_t va, vb, vr; - uint32_t r = 0; - - USATACC(8, 0); - USATACC(8, 8); - USATACC(8, 16); - USATACC(8, 24); - return r; -} - -uint32_t HELPER(neon_uqadd_s16)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int32_t va, vb, vr; - uint64_t r = 0; - - USATACC(16, 0); - USATACC(16, 16); - return r; -} - -#undef USATACC - -uint32_t HELPER(neon_uqadd_s32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int64_t va = (int32_t)a; - int64_t vb = (uint32_t)b; - int64_t vr = va + vb; - if (vr > UINT32_MAX) { - SET_QC(); - vr = UINT32_MAX; - } else if (vr < 0) { - SET_QC(); - vr = 0; - } - return vr; -} - -uint64_t HELPER(neon_uqadd_s64)(CPUARMState *env, uint64_t a, uint64_t b) -{ - uint64_t res; - res = a + b; - /* We only need to look at the pattern of SIGN bits to detect - * +ve/-ve saturation - */ - if (~a & b & ~res & SIGNBIT64) { - SET_QC(); - res = UINT64_MAX; - } else if (a & ~b & res & SIGNBIT64) { - SET_QC(); - res = 0; - } - return res; -} - -/* Signed saturating accumulate of unsigned value - * - * Op1/Rn is treated as unsigned - * Op2/Rd is treated as signed - * - * The result is treated as a signed value and saturated as such - * - * We use a macro for the 8/16 bit cases which expects signed integers of va, - * vb, and vr for interim calculation and an unsigned 32 bit result value r. - */ - -#define SSATACC(bits, shift) \ - do { \ - va = extract32(a, shift, bits); \ - vb = sextract32(b, shift, bits); \ - vr = va + vb; \ - if (vr > INT##bits##_MAX) { \ - SET_QC(); \ - vr = INT##bits##_MAX; \ - } else if (vr < INT##bits##_MIN) { \ - SET_QC(); \ - vr = INT##bits##_MIN; \ - } \ - r = deposit32(r, shift, bits, vr); \ - } while (0) - -uint32_t HELPER(neon_sqadd_u8)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int16_t va, vb, vr; - uint32_t r = 0; - - SSATACC(8, 0); - SSATACC(8, 8); - SSATACC(8, 16); - SSATACC(8, 24); - return r; -} - -uint32_t HELPER(neon_sqadd_u16)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int32_t va, vb, vr; - uint32_t r = 0; - - SSATACC(16, 0); - SSATACC(16, 16); - - return r; -} - -#undef SSATACC - -uint32_t HELPER(neon_sqadd_u32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int64_t res; - int64_t op1 = (uint32_t)a; - int64_t op2 = (int32_t)b; - res = op1 + op2; - if (res > INT32_MAX) { - SET_QC(); - res = INT32_MAX; - } else if (res < INT32_MIN) { - SET_QC(); - res = INT32_MIN; - } - return res; -} - -uint64_t HELPER(neon_sqadd_u64)(CPUARMState *env, uint64_t a, uint64_t b) -{ - uint64_t res; - res = a + b; - /* We only need to look at the pattern of SIGN bits to detect an overflow */ - if (((a & res) - | (~b & res) - | (a & ~b)) & SIGNBIT64) { - SET_QC(); - res = INT64_MAX; - } - return res; -} - - #define NEON_USAT(dest, src1, src2, type) do { \ uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ if (tmp != (type)tmp) { \ diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9f948e033e..781b224972 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9985,67 +9985,42 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, bool is_q, unsigned size, int rn, int rd) { + TCGv_i64 res, qc, a, b; + if (!is_scalar) { gen_gvec_fn3(s, is_q, rd, rd, rn, is_u ? gen_gvec_usqadd_qc : gen_gvec_suqadd_qc, size); return; } - if (size == 3) { - TCGv_i64 tcg_rn = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = tcg_temp_new_i64(); + res = tcg_temp_new_i64(); + qc = tcg_temp_new_i64(); + a = tcg_temp_new_i64(); + b = tcg_temp_new_i64(); - read_vec_element(s, tcg_rn, rn, 0, MO_64); - read_vec_element(s, tcg_rd, rd, 0, MO_64); + /* Read and extend scalar inputs to 64-bits. */ + read_vec_element(s, a, rd, 0, size | (is_u ? 0 : MO_SIGN)); + read_vec_element(s, b, rn, 0, size | (is_u ? MO_SIGN : 0)); + tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - if (is_u) { /* USQADD */ - gen_helper_neon_uqadd_s64(tcg_rd, tcg_env, tcg_rn, tcg_rd); - } else { /* SUQADD */ - gen_helper_neon_sqadd_u64(tcg_rd, tcg_env, tcg_rn, tcg_rd); + if (size == MO_64) { + if (is_u) { + gen_usqadd_d(res, qc, a, b); + } else { + gen_suqadd_d(res, qc, a, b); } - write_vec_element(s, tcg_rd, rd, 0, MO_64); - clear_vec_high(s, false, rd); } else { - TCGv_i32 tcg_rn = tcg_temp_new_i32(); - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_rn, rn, 0, size); - read_vec_element_i32(s, tcg_rd, rd, 0, size); - - if (is_u) { /* USQADD */ - switch (size) { - case 0: - gen_helper_neon_uqadd_s8(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 1: - gen_helper_neon_uqadd_s16(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 2: - gen_helper_neon_uqadd_s32(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - default: - g_assert_not_reached(); - } - } else { /* SUQADD */ - switch (size) { - case 0: - gen_helper_neon_sqadd_u8(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 1: - gen_helper_neon_sqadd_u16(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - case 2: - gen_helper_neon_sqadd_u32(tcg_rd, tcg_env, tcg_rn, tcg_rd); - break; - default: - g_assert_not_reached(); - } + if (is_u) { + gen_usqadd_bhs(res, qc, a, b, size); + } else { + gen_suqadd_bhs(res, qc, a, b, size); + /* Truncate signed 64-bit result for writeback. */ + tcg_gen_ext_i64(res, res, size); } - - write_vec_element(s, tcg_constant_i64(0), rd, 0, MO_64); - write_vec_element_i32(s, tcg_rd, rd, 0, MO_32); - clear_vec_high(s, false, rd); } + + write_fp_dreg(s, rd, res); + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); } /* AdvSIMD scalar two reg misc diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index b5cb26f8a2..0fcf7cb63a 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -197,9 +197,17 @@ void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, uint32_t a, uint32_t oprsz, uint32_t maxsz); void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, uint32_t a, uint32_t oprsz, uint32_t maxsz); + +void gen_suqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_suqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b); void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_usqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_usqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b); void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); From f4fa83d6148f9d9bbd543c776e6cdc919c43c8e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:17 -0700 Subject: [PATCH 1038/2884] target/arm: Inline scalar SQADD, UQADD, SQSUB, UQSUB This eliminates the last uses of these neon helpers. Incorporate the MO_64 expanders as an option to the vector expander. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 17 ---- target/arm/tcg/gengvec.c | 116 +++++++++++++++++++++++ target/arm/tcg/neon_helper.c | 162 --------------------------------- target/arm/tcg/translate-a64.c | 67 ++++++++------ target/arm/tcg/translate.h | 15 +++ 5 files changed, 169 insertions(+), 208 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index c76158d6d3..a14c040451 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -268,23 +268,6 @@ DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) /* neon_helper.c */ -DEF_HELPER_FLAGS_3(neon_qadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_u16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_u32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64) -DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64) - DEF_HELPER_2(neon_hadd_s8, i32, i32, i32) DEF_HELPER_2(neon_hadd_u8, i32, i32, i32) DEF_HELPER_2(neon_hadd_s16, i32, i32, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 3e2d3c21a1..740f3f864e 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1218,6 +1218,28 @@ void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + uint64_t max = MAKE_64BIT_MASK(0, 8 << esz); + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_add_i64(tmp, a, b); + tcg_gen_umin_i64(res, tmp, tcg_constant_i64(max)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_uqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_add_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, res, t, a, + tcg_constant_i64(UINT64_MAX), t); + tcg_gen_xor_i64(t, t, res); + tcg_gen_or_i64(qc, qc, t); +} + static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { @@ -1251,6 +1273,7 @@ void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .opt_opc = vecop_list, .vece = MO_32 }, { .fniv = gen_uqadd_vec, + .fni8 = gen_uqadd_d, .fno = gen_helper_gvec_uqadd_d, .write_aofs = true, .opt_opc = vecop_list, @@ -1262,6 +1285,41 @@ void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +void gen_sqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + int64_t max = MAKE_64BIT_MASK(0, (8 << esz) - 1); + int64_t min = -1ll - max; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_add_i64(tmp, a, b); + tcg_gen_smin_i64(res, tmp, tcg_constant_i64(max)); + tcg_gen_smax_i64(res, res, tcg_constant_i64(min)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_sqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_add_i64(t0, a, b); + + /* Compute signed overflow indication into T1 */ + tcg_gen_xor_i64(t1, a, b); + tcg_gen_xor_i64(t2, t0, a); + tcg_gen_andc_i64(t1, t2, t1); + + /* Compute saturated value into T2 */ + tcg_gen_sari_i64(t2, a, 63); + tcg_gen_xori_i64(t2, t2, INT64_MAX); + + tcg_gen_movcond_i64(TCG_COND_LT, res, t1, tcg_constant_i64(0), t2, t0); + tcg_gen_xor_i64(t0, t0, res); + tcg_gen_or_i64(qc, qc, t0); +} + static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { @@ -1295,6 +1353,7 @@ void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .write_aofs = true, .vece = MO_32 }, { .fniv = gen_sqadd_vec, + .fni8 = gen_sqadd_d, .fno = gen_helper_gvec_sqadd_d, .opt_opc = vecop_list, .write_aofs = true, @@ -1306,6 +1365,26 @@ void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +void gen_uqsub_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_sub_i64(tmp, a, b); + tcg_gen_smax_i64(res, tmp, tcg_constant_i64(0)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_uqsub_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, res, a, b, tcg_constant_i64(0), t); + tcg_gen_xor_i64(t, t, res); + tcg_gen_or_i64(qc, qc, t); +} + static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { @@ -1339,6 +1418,7 @@ void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .write_aofs = true, .vece = MO_32 }, { .fniv = gen_uqsub_vec, + .fni8 = gen_uqsub_d, .fno = gen_helper_gvec_uqsub_d, .opt_opc = vecop_list, .write_aofs = true, @@ -1350,6 +1430,41 @@ void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +void gen_sqsub_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + int64_t max = MAKE_64BIT_MASK(0, (8 << esz) - 1); + int64_t min = -1ll - max; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_sub_i64(tmp, a, b); + tcg_gen_smin_i64(res, tmp, tcg_constant_i64(max)); + tcg_gen_smax_i64(res, res, tcg_constant_i64(min)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_sqsub_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t0, a, b); + + /* Compute signed overflow indication into T1 */ + tcg_gen_xor_i64(t1, a, b); + tcg_gen_xor_i64(t2, t0, a); + tcg_gen_and_i64(t1, t1, t2); + + /* Compute saturated value into T2 */ + tcg_gen_sari_i64(t2, a, 63); + tcg_gen_xori_i64(t2, t2, INT64_MAX); + + tcg_gen_movcond_i64(TCG_COND_LT, res, t1, tcg_constant_i64(0), t2, t0); + tcg_gen_xor_i64(t0, t0, res); + tcg_gen_or_i64(qc, qc, t0); +} + static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b) { @@ -1383,6 +1498,7 @@ void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, .write_aofs = true, .vece = MO_32 }, { .fniv = gen_sqsub_vec, + .fni8 = gen_sqsub_d, .fno = gen_helper_gvec_sqsub_d, .opt_opc = vecop_list, .write_aofs = true, diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 9505a5fd18..0af15e9f6e 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -155,168 +155,6 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ return arg; \ } - -#define NEON_USAT(dest, src1, src2, type) do { \ - uint32_t tmp = (uint32_t)src1 + (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - dest = ~0; \ - } else { \ - dest = tmp; \ - }} while(0) -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t) -NEON_VOP_ENV(qadd_u8, neon_u8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t) -NEON_VOP_ENV(qadd_u16, neon_u16, 2) -#undef NEON_FN -#undef NEON_USAT - -uint32_t HELPER(neon_qadd_u32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (res < a) { - SET_QC(); - res = ~0; - } - return res; -} - -uint64_t HELPER(neon_qadd_u64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 + src2; - if (res < src1) { - SET_QC(); - res = ~(uint64_t)0; - } - return res; -} - -#define NEON_SSAT(dest, src1, src2, type) do { \ - int32_t tmp = (uint32_t)src1 + (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - if (src2 > 0) { \ - tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \ - } else { \ - tmp = 1 << (sizeof(type) * 8 - 1); \ - } \ - } \ - dest = tmp; \ - } while(0) -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t) -NEON_VOP_ENV(qadd_s8, neon_s8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t) -NEON_VOP_ENV(qadd_s16, neon_s16, 2) -#undef NEON_FN -#undef NEON_SSAT - -uint32_t HELPER(neon_qadd_s32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { - SET_QC(); - res = ~(((int32_t)a >> 31) ^ SIGNBIT); - } - return res; -} - -uint64_t HELPER(neon_qadd_s64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 + src2; - if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) { - SET_QC(); - res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; - } - return res; -} - -#define NEON_USAT(dest, src1, src2, type) do { \ - uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - dest = 0; \ - } else { \ - dest = tmp; \ - }} while(0) -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t) -NEON_VOP_ENV(qsub_u8, neon_u8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t) -NEON_VOP_ENV(qsub_u16, neon_u16, 2) -#undef NEON_FN -#undef NEON_USAT - -uint32_t HELPER(neon_qsub_u32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a - b; - if (res > a) { - SET_QC(); - res = 0; - } - return res; -} - -uint64_t HELPER(neon_qsub_u64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - if (src1 < src2) { - SET_QC(); - res = 0; - } else { - res = src1 - src2; - } - return res; -} - -#define NEON_SSAT(dest, src1, src2, type) do { \ - int32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - if (src2 < 0) { \ - tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \ - } else { \ - tmp = 1 << (sizeof(type) * 8 - 1); \ - } \ - } \ - dest = tmp; \ - } while(0) -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t) -NEON_VOP_ENV(qsub_s8, neon_s8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t) -NEON_VOP_ENV(qsub_s16, neon_s16, 2) -#undef NEON_FN -#undef NEON_SSAT - -uint32_t HELPER(neon_qsub_s32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a - b; - if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { - SET_QC(); - res = ~(((int32_t)a >> 31) ^ SIGNBIT); - } - return res; -} - -uint64_t HELPER(neon_qsub_s64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 - src2; - if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) { - SET_QC(); - res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; - } - return res; -} - #define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1 NEON_VOP(hadd_s8, neon_s8, 4) NEON_VOP(hadd_u8, neon_u8, 4) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 781b224972..ca7ba6b1e8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -9291,21 +9291,28 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, * or scalar-three-reg-same groups. */ TCGCond cond; + TCGv_i64 qc; switch (opcode) { case 0x1: /* SQADD */ + qc = tcg_temp_new_i64(); + tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); if (u) { - gen_helper_neon_qadd_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); + gen_uqadd_d(tcg_rd, qc, tcg_rn, tcg_rm); } else { - gen_helper_neon_qadd_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); + gen_sqadd_d(tcg_rd, qc, tcg_rn, tcg_rm); } + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); break; case 0x5: /* SQSUB */ + qc = tcg_temp_new_i64(); + tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); if (u) { - gen_helper_neon_qsub_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); + gen_uqsub_d(tcg_rd, qc, tcg_rn, tcg_rm); } else { - gen_helper_neon_qsub_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); + gen_sqsub_d(tcg_rd, qc, tcg_rn, tcg_rm); } + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); break; case 0x6: /* CMGT, CMHI */ cond = u ? TCG_COND_GTU : TCG_COND_GT; @@ -9425,35 +9432,16 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) * OPTME: special-purpose helpers would avoid doing some * unnecessary work in the helper for the 8 and 16 bit cases. */ - NeonGenTwoOpEnvFn *genenvfn; - TCGv_i32 tcg_rn = tcg_temp_new_i32(); - TCGv_i32 tcg_rm = tcg_temp_new_i32(); - TCGv_i32 tcg_rd32 = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_rn, rn, 0, size); - read_vec_element_i32(s, tcg_rm, rm, 0, size); + NeonGenTwoOpEnvFn *genenvfn = NULL; + void (*genfn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp) = NULL; switch (opcode) { case 0x1: /* SQADD, UQADD */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qadd_s8, gen_helper_neon_qadd_u8 }, - { gen_helper_neon_qadd_s16, gen_helper_neon_qadd_u16 }, - { gen_helper_neon_qadd_s32, gen_helper_neon_qadd_u32 }, - }; - genenvfn = fns[size][u]; + genfn = u ? gen_uqadd_bhs : gen_sqadd_bhs; break; - } case 0x5: /* SQSUB, UQSUB */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qsub_s8, gen_helper_neon_qsub_u8 }, - { gen_helper_neon_qsub_s16, gen_helper_neon_qsub_u16 }, - { gen_helper_neon_qsub_s32, gen_helper_neon_qsub_u32 }, - }; - genenvfn = fns[size][u]; + genfn = u ? gen_uqsub_bhs : gen_sqsub_bhs; break; - } case 0x9: /* SQSHL, UQSHL */ { static NeonGenTwoOpEnvFn * const fns[3][2] = { @@ -9488,8 +9476,29 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) g_assert_not_reached(); } - genenvfn(tcg_rd32, tcg_env, tcg_rn, tcg_rm); - tcg_gen_extu_i32_i64(tcg_rd, tcg_rd32); + if (genenvfn) { + TCGv_i32 tcg_rn = tcg_temp_new_i32(); + TCGv_i32 tcg_rm = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_rn, rn, 0, size); + read_vec_element_i32(s, tcg_rm, rm, 0, size); + genenvfn(tcg_rn, tcg_env, tcg_rn, tcg_rm); + tcg_gen_extu_i32_i64(tcg_rd, tcg_rn); + } else { + TCGv_i64 tcg_rn = tcg_temp_new_i64(); + TCGv_i64 tcg_rm = tcg_temp_new_i64(); + TCGv_i64 qc = tcg_temp_new_i64(); + + read_vec_element(s, tcg_rn, rn, 0, size | (u ? 0 : MO_SIGN)); + read_vec_element(s, tcg_rm, rm, 0, size | (u ? 0 : MO_SIGN)); + tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + genfn(tcg_rd, qc, tcg_rn, tcg_rm, size); + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + if (!u) { + /* Truncate signed 64-bit result for writeback. */ + tcg_gen_ext_i64(tcg_rd, tcg_rd, size); + } + } } write_fp_dreg(s, rd, tcg_rd); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 3abdbedfe5..87439dcc61 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -466,12 +466,27 @@ void gen_sshl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); void gen_ushl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_sshl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_uqadd_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_sqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_sqadd_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_uqsub_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_uqsub_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_sqsub_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_sqsub_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); From aaf03a399af539e40c7f9d5d5c3a79e981af77b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:18 -0700 Subject: [PATCH 1039/2884] target/arm: Convert SQADD, SQSUB, UQADD, UQSUB to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 11 ++++ target/arm/tcg/translate-a64.c | 96 +++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f48adef5bb..19010af03b 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -44,6 +44,7 @@ @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 @rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd @rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd +@rrr_e ........ esz:2 . rm:5 ...... rn:5 rd:5 &rrr_e @rrx_h ........ .. .. rm:4 .... . . rn:5 rd:5 &rrx_e esz=1 idx=%hlm @rrx_s ........ .. . rm:5 .... . . rn:5 rd:5 &rrx_e esz=2 idx=%hl @@ -744,6 +745,11 @@ FRECPS_s 0101 1110 0.1 ..... 11111 1 ..... ..... @rrr_sd FRSQRTS_s 0101 1110 110 ..... 00111 1 ..... ..... @rrr_h FRSQRTS_s 0101 1110 1.1 ..... 11111 1 ..... ..... @rrr_sd +SQADD_s 0101 1110 ..1 ..... 00001 1 ..... ..... @rrr_e +UQADD_s 0111 1110 ..1 ..... 00001 1 ..... ..... @rrr_e +SQSUB_s 0101 1110 ..1 ..... 00101 1 ..... ..... @rrr_e +UQSUB_s 0111 1110 ..1 ..... 00101 1 ..... ..... @rrr_e + ### Advanced SIMD scalar pairwise FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h @@ -857,6 +863,11 @@ BSL_v 0.10 1110 011 ..... 00011 1 ..... ..... @qrrr_b BIT_v 0.10 1110 101 ..... 00011 1 ..... ..... @qrrr_b BIF_v 0.10 1110 111 ..... 00011 1 ..... ..... @qrrr_b +SQADD_v 0.00 1110 ..1 ..... 00001 1 ..... ..... @qrrr_e +UQADD_v 0.10 1110 ..1 ..... 00001 1 ..... ..... @qrrr_e +SQSUB_v 0.00 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e +UQSUB_v 0.10 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ca7ba6b1e8..3956c41543 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5060,6 +5060,43 @@ static const FPScalar f_scalar_frsqrts = { }; TRANS(FRSQRTS_s, do_fp3_scalar, a, &f_scalar_frsqrts) +static bool do_satacc_s(DisasContext *s, arg_rrr_e *a, + MemOp sgn_n, MemOp sgn_m, + void (*gen_bhs)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp), + void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t0, t1, t2, qc; + MemOp esz = a->esz; + + if (!fp_access_check(s)) { + return true; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + qc = tcg_temp_new_i64(); + read_vec_element(s, t1, a->rn, 0, esz | sgn_n); + read_vec_element(s, t2, a->rm, 0, esz | sgn_m); + tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + + if (esz == MO_64) { + gen_d(t0, qc, t1, t2); + } else { + gen_bhs(t0, qc, t1, t2, esz); + tcg_gen_ext_i64(t0, t0, esz); + } + + write_fp_dreg(s, a->rd, t0); + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + return true; +} + +TRANS(SQADD_s, do_satacc_s, a, MO_SIGN, MO_SIGN, gen_sqadd_bhs, gen_sqadd_d) +TRANS(SQSUB_s, do_satacc_s, a, MO_SIGN, MO_SIGN, gen_sqsub_bhs, gen_sqsub_d) +TRANS(UQADD_s, do_satacc_s, a, 0, 0, gen_uqadd_bhs, gen_uqadd_d) +TRANS(UQSUB_s, do_satacc_s, a, 0, 0, gen_uqsub_bhs, gen_uqsub_d) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5298,6 +5335,11 @@ TRANS(BSL_v, do_bitsel, a->q, a->rd, a->rd, a->rn, a->rm) TRANS(BIT_v, do_bitsel, a->q, a->rd, a->rm, a->rn, a->rd) TRANS(BIF_v, do_bitsel, a->q, a->rd, a->rm, a->rd, a->rn) +TRANS(SQADD_v, do_gvec_fn3, a, gen_gvec_sqadd_qc) +TRANS(UQADD_v, do_gvec_fn3, a, gen_gvec_uqadd_qc) +TRANS(SQSUB_v, do_gvec_fn3, a, gen_gvec_sqsub_qc) +TRANS(UQSUB_v, do_gvec_fn3, a, gen_gvec_uqsub_qc) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -9291,29 +9333,8 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, * or scalar-three-reg-same groups. */ TCGCond cond; - TCGv_i64 qc; switch (opcode) { - case 0x1: /* SQADD */ - qc = tcg_temp_new_i64(); - tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - if (u) { - gen_uqadd_d(tcg_rd, qc, tcg_rn, tcg_rm); - } else { - gen_sqadd_d(tcg_rd, qc, tcg_rn, tcg_rm); - } - tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - break; - case 0x5: /* SQSUB */ - qc = tcg_temp_new_i64(); - tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - if (u) { - gen_uqsub_d(tcg_rd, qc, tcg_rn, tcg_rm); - } else { - gen_sqsub_d(tcg_rd, qc, tcg_rn, tcg_rm); - } - tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - break; case 0x6: /* CMGT, CMHI */ cond = u ? TCG_COND_GTU : TCG_COND_GT; do_cmop: @@ -9366,6 +9387,8 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, } break; default: + case 0x1: /* SQADD / UQADD */ + case 0x5: /* SQSUB / UQSUB */ g_assert_not_reached(); } } @@ -9387,8 +9410,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) TCGv_i64 tcg_rd; switch (opcode) { - case 0x1: /* SQADD, UQADD */ - case 0x5: /* SQSUB, UQSUB */ case 0x9: /* SQSHL, UQSHL */ case 0xb: /* SQRSHL, UQRSHL */ break; @@ -9410,6 +9431,8 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) } break; default: + case 0x1: /* SQADD, UQADD */ + case 0x5: /* SQSUB, UQSUB */ unallocated_encoding(s); return; } @@ -9436,12 +9459,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) void (*genfn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp) = NULL; switch (opcode) { - case 0x1: /* SQADD, UQADD */ - genfn = u ? gen_uqadd_bhs : gen_sqadd_bhs; - break; - case 0x5: /* SQSUB, UQSUB */ - genfn = u ? gen_uqsub_bhs : gen_sqsub_bhs; - break; case 0x9: /* SQSHL, UQSHL */ { static NeonGenTwoOpEnvFn * const fns[3][2] = { @@ -9473,6 +9490,8 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) break; } default: + case 0x1: /* SQADD, UQADD */ + case 0x5: /* SQSUB, UQSUB */ g_assert_not_reached(); } @@ -10933,6 +10952,11 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) return; } break; + + case 0x01: /* SQADD, UQADD */ + case 0x05: /* SQSUB, UQSUB */ + unallocated_encoding(s); + return; } if (!fp_access_check(s)) { @@ -10940,20 +10964,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x01: /* SQADD, UQADD */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uqadd_qc, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqadd_qc, size); - } - return; - case 0x05: /* SQSUB, UQSUB */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uqsub_qc, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqsub_qc, size); - } - return; case 0x08: /* SSHL, USHL */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_ushl, size); From 1f4dd04c2312a6673895575535f872cdb6708817 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:19 -0700 Subject: [PATCH 1040/2884] target/arm: Convert SUQADD, USQADD to decodetree These are faux 2-operand instructions, reading from rd. Sort them next to the other three-operand same insns for clarity. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 8 +++++ target/arm/tcg/translate-a64.c | 64 ++++------------------------------ 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 19010af03b..7c350ba833 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -45,6 +45,7 @@ @rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd @rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd @rrr_e ........ esz:2 . rm:5 ...... rn:5 rd:5 &rrr_e +@r2r_e ........ esz:2 . ..... ...... rm:5 rd:5 &rrr_e rn=%rd @rrx_h ........ .. .. rm:4 .... . . rn:5 rd:5 &rrx_e esz=1 idx=%hlm @rrx_s ........ .. . rm:5 .... . . rn:5 rd:5 &rrx_e esz=2 idx=%hl @@ -60,6 +61,7 @@ @qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1 @qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd @qrrr_e . q:1 ...... esz:2 . rm:5 ...... rn:5 rd:5 &qrrr_e +@qr2r_e . q:1 ...... esz:2 . ..... ...... rm:5 rd:5 &qrrr_e rn=%rd @qrrx_h . q:1 .. .... .. .. rm:4 .... . . rn:5 rd:5 \ &qrrx_e esz=1 idx=%hlm @@ -750,6 +752,9 @@ UQADD_s 0111 1110 ..1 ..... 00001 1 ..... ..... @rrr_e SQSUB_s 0101 1110 ..1 ..... 00101 1 ..... ..... @rrr_e UQSUB_s 0111 1110 ..1 ..... 00101 1 ..... ..... @rrr_e +SUQADD_s 0101 1110 ..1 00000 00111 0 ..... ..... @r2r_e +USQADD_s 0111 1110 ..1 00000 00111 0 ..... ..... @r2r_e + ### Advanced SIMD scalar pairwise FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h @@ -868,6 +873,9 @@ UQADD_v 0.10 1110 ..1 ..... 00001 1 ..... ..... @qrrr_e SQSUB_v 0.00 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e UQSUB_v 0.10 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e +SUQADD_v 0.00 1110 ..1 00000 00111 0 ..... ..... @qr2r_e +USQADD_v 0.10 1110 ..1 00000 00111 0 ..... ..... @qr2r_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3956c41543..c0637bda0f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5096,6 +5096,8 @@ TRANS(SQADD_s, do_satacc_s, a, MO_SIGN, MO_SIGN, gen_sqadd_bhs, gen_sqadd_d) TRANS(SQSUB_s, do_satacc_s, a, MO_SIGN, MO_SIGN, gen_sqsub_bhs, gen_sqsub_d) TRANS(UQADD_s, do_satacc_s, a, 0, 0, gen_uqadd_bhs, gen_uqadd_d) TRANS(UQSUB_s, do_satacc_s, a, 0, 0, gen_uqsub_bhs, gen_uqsub_d) +TRANS(SUQADD_s, do_satacc_s, a, MO_SIGN, 0, gen_suqadd_bhs, gen_suqadd_d) +TRANS(USQADD_s, do_satacc_s, a, 0, MO_SIGN, gen_usqadd_bhs, gen_usqadd_d) static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) @@ -5339,6 +5341,8 @@ TRANS(SQADD_v, do_gvec_fn3, a, gen_gvec_sqadd_qc) TRANS(UQADD_v, do_gvec_fn3, a, gen_gvec_uqadd_qc) TRANS(SQSUB_v, do_gvec_fn3, a, gen_gvec_sqsub_qc) TRANS(UQSUB_v, do_gvec_fn3, a, gen_gvec_uqsub_qc) +TRANS(SUQADD_v, do_gvec_fn3, a, gen_gvec_suqadd_qc) +TRANS(USQADD_v, do_gvec_fn3, a, gen_gvec_usqadd_qc) /* * Advanced SIMD scalar/vector x indexed element @@ -10009,48 +10013,6 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, clear_vec_high(s, is_q, rd); } -/* Remaining saturating accumulating ops */ -static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, - bool is_q, unsigned size, int rn, int rd) -{ - TCGv_i64 res, qc, a, b; - - if (!is_scalar) { - gen_gvec_fn3(s, is_q, rd, rd, rn, - is_u ? gen_gvec_usqadd_qc : gen_gvec_suqadd_qc, size); - return; - } - - res = tcg_temp_new_i64(); - qc = tcg_temp_new_i64(); - a = tcg_temp_new_i64(); - b = tcg_temp_new_i64(); - - /* Read and extend scalar inputs to 64-bits. */ - read_vec_element(s, a, rd, 0, size | (is_u ? 0 : MO_SIGN)); - read_vec_element(s, b, rn, 0, size | (is_u ? MO_SIGN : 0)); - tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - - if (size == MO_64) { - if (is_u) { - gen_usqadd_d(res, qc, a, b); - } else { - gen_suqadd_d(res, qc, a, b); - } - } else { - if (is_u) { - gen_usqadd_bhs(res, qc, a, b, size); - } else { - gen_suqadd_bhs(res, qc, a, b, size); - /* Truncate signed 64-bit result for writeback. */ - tcg_gen_ext_i64(res, res, size); - } - } - - write_fp_dreg(s, rd, res); - tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); -} - /* AdvSIMD scalar two reg misc * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 * +-----+---+-----------+------+-----------+--------+-----+------+------+ @@ -10070,12 +10032,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) TCGv_ptr tcg_fpstatus; switch (opcode) { - case 0x3: /* USQADD / SUQADD*/ - if (!fp_access_check(s)) { - return; - } - handle_2misc_satacc(s, true, u, false, size, rn, rd); - return; case 0x7: /* SQABS / SQNEG */ break; case 0xa: /* CMLT */ @@ -10175,6 +10131,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) } break; default: + case 0x3: /* USQADD / SUQADD */ unallocated_encoding(s); return; } @@ -11662,16 +11619,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) return; } break; - case 0x3: /* SUQADD, USQADD */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_satacc(s, false, u, is_q, size, rn, rd); - return; case 0x7: /* SQABS, SQNEG */ if (size == 3 && !is_q) { unallocated_encoding(s); @@ -11846,6 +11793,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) break; } default: + case 0x3: /* SUQADD, USQADD */ unallocated_encoding(s); return; } From beaa7c41b082d2bd31f3f97d80a66b055c42440f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:20 -0700 Subject: [PATCH 1041/2884] target/arm: Convert SSHL, USHL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 ++++++ target/arm/tcg/translate-a64.c | 40 +++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7c350ba833..ea897d6732 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -42,6 +42,7 @@ @rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd @rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 +@rrr_d ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=3 @rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd @rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd @rrr_e ........ esz:2 . rm:5 ...... rn:5 rd:5 &rrr_e @@ -755,6 +756,9 @@ UQSUB_s 0111 1110 ..1 ..... 00101 1 ..... ..... @rrr_e SUQADD_s 0101 1110 ..1 00000 00111 0 ..... ..... @r2r_e USQADD_s 0111 1110 ..1 00000 00111 0 ..... ..... @r2r_e +SSHL_s 0101 1110 111 ..... 01000 1 ..... ..... @rrr_d +USHL_s 0111 1110 111 ..... 01000 1 ..... ..... @rrr_d + ### Advanced SIMD scalar pairwise FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h @@ -876,6 +880,9 @@ UQSUB_v 0.10 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e SUQADD_v 0.00 1110 ..1 00000 00111 0 ..... ..... @qr2r_e USQADD_v 0.10 1110 ..1 00000 00111 0 ..... ..... @qr2r_e +SSHL_v 0.00 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e +USHL_v 0.10 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c0637bda0f..7c7a22985b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5099,6 +5099,24 @@ TRANS(UQSUB_s, do_satacc_s, a, 0, 0, gen_uqsub_bhs, gen_uqsub_d) TRANS(SUQADD_s, do_satacc_s, a, MO_SIGN, 0, gen_suqadd_bhs, gen_suqadd_d) TRANS(USQADD_s, do_satacc_s, a, 0, MO_SIGN, gen_usqadd_bhs, gen_usqadd_d) +static bool do_int3_scalar_d(DisasContext *s, arg_rrr_e *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rm, 0, MO_64); + fn(t0, t0, t1); + write_fp_dreg(s, a->rd, t0); + } + return true; +} + +TRANS(SSHL_s, do_int3_scalar_d, a, gen_sshl_i64) +TRANS(USHL_s, do_int3_scalar_d, a, gen_ushl_i64) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5344,6 +5362,10 @@ TRANS(UQSUB_v, do_gvec_fn3, a, gen_gvec_uqsub_qc) TRANS(SUQADD_v, do_gvec_fn3, a, gen_gvec_suqadd_qc) TRANS(USQADD_v, do_gvec_fn3, a, gen_gvec_usqadd_qc) +TRANS(SSHL_v, do_gvec_fn3, a, gen_gvec_sshl) +TRANS(USHL_v, do_gvec_fn3, a, gen_gvec_ushl) + + /* * Advanced SIMD scalar/vector x indexed element */ @@ -9355,13 +9377,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, } gen_cmtst_i64(tcg_rd, tcg_rn, tcg_rm); break; - case 0x8: /* SSHL, USHL */ - if (u) { - gen_ushl_i64(tcg_rd, tcg_rn, tcg_rm); - } else { - gen_sshl_i64(tcg_rd, tcg_rn, tcg_rm); - } - break; case 0x9: /* SQSHL, UQSHL */ if (u) { gen_helper_neon_qshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); @@ -9393,6 +9408,7 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, default: case 0x1: /* SQADD / UQADD */ case 0x5: /* SQSUB / UQSUB */ + case 0x8: /* SSHL, USHL */ g_assert_not_reached(); } } @@ -9417,7 +9433,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x9: /* SQSHL, UQSHL */ case 0xb: /* SQRSHL, UQRSHL */ break; - case 0x8: /* SSHL, USHL */ case 0xa: /* SRSHL, URSHL */ case 0x6: /* CMGT, CMHI */ case 0x7: /* CMGE, CMHS */ @@ -9437,6 +9452,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) default: case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ + case 0x8: /* SSHL, USHL */ unallocated_encoding(s); return; } @@ -10912,6 +10928,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x01: /* SQADD, UQADD */ case 0x05: /* SQSUB, UQSUB */ + case 0x08: /* SSHL, USHL */ unallocated_encoding(s); return; } @@ -10921,13 +10938,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x08: /* SSHL, USHL */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_ushl, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sshl, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From 940392c834e9e1a707bae584ac28f63d832b033f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:21 -0700 Subject: [PATCH 1042/2884] target/arm: Convert SRSHL and URSHL (register) to gvec Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240528203044.612851-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 10 +++++++++ target/arm/tcg/gengvec.c | 22 +++++++++++++++++++ target/arm/tcg/neon-dp.decode | 10 ++------- target/arm/tcg/neon_helper.c | 38 ++++++++++++++++++++++++++++++++- target/arm/tcg/translate-a64.c | 17 ++++++--------- target/arm/tcg/translate-neon.c | 6 ++---- target/arm/tcg/translate.h | 4 ++++ 7 files changed, 84 insertions(+), 23 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index a14c040451..25eb7bf5df 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -327,6 +327,16 @@ DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) +DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_urshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_2(neon_add_u8, i32, i32, i32) DEF_HELPER_2(neon_add_u16, i32, i32, i32) DEF_HELPER_2(neon_sub_u8, i32, i32, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 740f3f864e..216a9f81e3 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1218,6 +1218,28 @@ void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +void gen_gvec_srshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[] = { + gen_helper_gvec_srshl_b, gen_helper_gvec_srshl_h, + gen_helper_gvec_srshl_s, gen_helper_gvec_srshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[] = { + gen_helper_gvec_urshl_b, gen_helper_gvec_urshl_h, + gen_helper_gvec_urshl_s, gen_helper_gvec_urshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) { uint64_t max = MAKE_64BIT_MASK(0, 8 << esz); diff --git a/target/arm/tcg/neon-dp.decode b/target/arm/tcg/neon-dp.decode index fd3a01bfa0..8525c65c0d 100644 --- a/target/arm/tcg/neon-dp.decode +++ b/target/arm/tcg/neon-dp.decode @@ -117,14 +117,8 @@ VSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 0 .... @3same_rev VQSHL_U64_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_64_rev VQSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_rev } -{ - VRSHL_S64_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_64_rev - VRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_rev -} -{ - VRSHL_U64_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_64_rev - VRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_rev -} +VRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_rev +VRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_rev { VQRSHL_S64_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_64_rev VQRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_rev diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 0af15e9f6e..516ecc1dcb 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -6,10 +6,11 @@ * * This code is licensed under the GNU GPL v2. */ -#include "qemu/osdep.h" +#include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" #include "vec_internal.h" @@ -117,6 +118,17 @@ NEON_VOP_BODY(vtype, n) uint32_t HELPER(glue(neon_,name))(CPUARMState *env, uint32_t arg1, uint32_t arg2) \ NEON_VOP_BODY(vtype, n) +#define NEON_GVEC_VOP2(name, vtype) \ +void HELPER(name)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + vtype *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ + NEON_FN(d[i], n[i], m[i]); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + /* Pairwise operations. */ /* For 32-bit elements each segment only contains a single element, so the elementwise and pairwise operations are the same. */ @@ -263,11 +275,23 @@ NEON_VOP(shl_s16, neon_s16, 2) #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, true, NULL)) NEON_VOP(rshl_s8, neon_s8, 4) +NEON_GVEC_VOP2(gvec_srshl_b, int8_t) #undef NEON_FN #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, true, NULL)) NEON_VOP(rshl_s16, neon_s16, 2) +NEON_GVEC_VOP2(gvec_srshl_h, int16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, true, NULL)) +NEON_GVEC_VOP2(gvec_srshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, (int8_t)src2, true, NULL)) +NEON_GVEC_VOP2(gvec_srshl_d, int64_t) #undef NEON_FN uint32_t HELPER(neon_rshl_s32)(uint32_t val, uint32_t shift) @@ -283,11 +307,23 @@ uint64_t HELPER(neon_rshl_s64)(uint64_t val, uint64_t shift) #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, true, NULL)) NEON_VOP(rshl_u8, neon_u8, 4) +NEON_GVEC_VOP2(gvec_urshl_b, uint8_t) #undef NEON_FN #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, true, NULL)) NEON_VOP(rshl_u16, neon_u16, 2) +NEON_GVEC_VOP2(gvec_urshl_h, uint16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, true, NULL)) +NEON_GVEC_VOP2(gvec_urshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, (int8_t)src2, true, NULL)) +NEON_GVEC_VOP2(gvec_urshl_d, int64_t) #undef NEON_FN uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shift) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 7c7a22985b..7e981f8d01 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10938,6 +10938,13 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x0a: /* SRSHL, URSHL */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_urshl, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_srshl, size); + } + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11083,16 +11090,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genenvfn = fns[size][u]; break; } - case 0xa: /* SRSHL, URSHL */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_rshl_s8, gen_helper_neon_rshl_u8 }, - { gen_helper_neon_rshl_s16, gen_helper_neon_rshl_u16 }, - { gen_helper_neon_rshl_s32, gen_helper_neon_rshl_u32 }, - }; - genfn = fns[size][u]; - break; - } case 0xb: /* SQRSHL, UQRSHL */ { static NeonGenTwoOpEnvFn * const fns[3][2] = { diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 18b048611b..337488bbf1 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -794,6 +794,8 @@ DO_3SAME(VQADD_S, gen_gvec_sqadd_qc) DO_3SAME(VQADD_U, gen_gvec_uqadd_qc) DO_3SAME(VQSUB_S, gen_gvec_sqsub_qc) DO_3SAME(VQSUB_U, gen_gvec_uqsub_qc) +DO_3SAME(VRSHL_S, gen_gvec_srshl) +DO_3SAME(VRSHL_U, gen_gvec_urshl) /* These insns are all gvec_bitsel but with the inputs in various orders. */ #define DO_3SAME_BITSEL(INSN, O1, O2, O3) \ @@ -929,8 +931,6 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) } \ DO_3SAME_64(INSN, gen_##INSN##_elt) -DO_3SAME_64(VRSHL_S64, gen_helper_neon_rshl_s64) -DO_3SAME_64(VRSHL_U64, gen_helper_neon_rshl_u64) DO_3SAME_64_ENV(VQSHL_S64, gen_helper_neon_qshl_s64) DO_3SAME_64_ENV(VQSHL_U64, gen_helper_neon_qshl_u64) DO_3SAME_64_ENV(VQRSHL_S64, gen_helper_neon_qrshl_s64) @@ -999,8 +999,6 @@ DO_3SAME_32(VHSUB_S, hsub_s) DO_3SAME_32(VHSUB_U, hsub_u) DO_3SAME_32(VRHADD_S, rhadd_s) DO_3SAME_32(VRHADD_U, rhadd_u) -DO_3SAME_32(VRSHL_S, rshl_s) -DO_3SAME_32(VRSHL_U, rshl_u) DO_3SAME_32_ENV(VQSHL_S, qshl_s) DO_3SAME_32_ENV(VQSHL_U, qshl_u) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 87439dcc61..ea63ffc47b 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -459,6 +459,10 @@ void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_srshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); From 2214c9d721b6020f63bbda06d497147fcb19ae93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:22 -0700 Subject: [PATCH 1043/2884] target/arm: Convert SRSHL, URSHL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 22 +++++++--------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index ea897d6732..9e02776036 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -758,6 +758,8 @@ USQADD_s 0111 1110 ..1 00000 00111 0 ..... ..... @r2r_e SSHL_s 0101 1110 111 ..... 01000 1 ..... ..... @rrr_d USHL_s 0111 1110 111 ..... 01000 1 ..... ..... @rrr_d +SRSHL_s 0101 1110 111 ..... 01010 1 ..... ..... @rrr_d +URSHL_s 0111 1110 111 ..... 01010 1 ..... ..... @rrr_d ### Advanced SIMD scalar pairwise @@ -882,6 +884,8 @@ USQADD_v 0.10 1110 ..1 00000 00111 0 ..... ..... @qr2r_e SSHL_v 0.00 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e USHL_v 0.10 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e +SRSHL_v 0.00 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e +URSHL_v 0.10 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 7e981f8d01..c751da78ef 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5116,6 +5116,8 @@ static bool do_int3_scalar_d(DisasContext *s, arg_rrr_e *a, TRANS(SSHL_s, do_int3_scalar_d, a, gen_sshl_i64) TRANS(USHL_s, do_int3_scalar_d, a, gen_ushl_i64) +TRANS(SRSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_s64) +TRANS(URSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_u64) static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) @@ -5364,6 +5366,8 @@ TRANS(USQADD_v, do_gvec_fn3, a, gen_gvec_usqadd_qc) TRANS(SSHL_v, do_gvec_fn3, a, gen_gvec_sshl) TRANS(USHL_v, do_gvec_fn3, a, gen_gvec_ushl) +TRANS(SRSHL_v, do_gvec_fn3, a, gen_gvec_srshl) +TRANS(URSHL_v, do_gvec_fn3, a, gen_gvec_urshl) /* @@ -9384,13 +9388,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, gen_helper_neon_qshl_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } break; - case 0xa: /* SRSHL, URSHL */ - if (u) { - gen_helper_neon_rshl_u64(tcg_rd, tcg_rn, tcg_rm); - } else { - gen_helper_neon_rshl_s64(tcg_rd, tcg_rn, tcg_rm); - } - break; case 0xb: /* SQRSHL, UQRSHL */ if (u) { gen_helper_neon_qrshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); @@ -9409,6 +9406,7 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, case 0x1: /* SQADD / UQADD */ case 0x5: /* SQSUB / UQSUB */ case 0x8: /* SSHL, USHL */ + case 0xa: /* SRSHL, URSHL */ g_assert_not_reached(); } } @@ -9433,7 +9431,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x9: /* SQSHL, UQSHL */ case 0xb: /* SQRSHL, UQRSHL */ break; - case 0xa: /* SRSHL, URSHL */ case 0x6: /* CMGT, CMHI */ case 0x7: /* CMGE, CMHS */ case 0x11: /* CMTST, CMEQ */ @@ -9453,6 +9450,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ case 0x8: /* SSHL, USHL */ + case 0xa: /* SRSHL, URSHL */ unallocated_encoding(s); return; } @@ -10929,6 +10927,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x01: /* SQADD, UQADD */ case 0x05: /* SQSUB, UQSUB */ case 0x08: /* SSHL, USHL */ + case 0x0a: /* SRSHL, URSHL */ unallocated_encoding(s); return; } @@ -10938,13 +10937,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x0a: /* SRSHL, URSHL */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_urshl, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_srshl, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From e72a68781572f31cbd6824681720ff936fba4707 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:23 -0700 Subject: [PATCH 1044/2884] target/arm: Convert SQSHL and UQSHL (register) to gvec Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240528203044.612851-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 8 ++++++++ target/arm/tcg/gengvec.c | 24 ++++++++++++++++++++++ target/arm/tcg/neon-dp.decode | 10 ++------- target/arm/tcg/neon_helper.c | 36 +++++++++++++++++++++++++++++++++ target/arm/tcg/translate-a64.c | 17 +++++++--------- target/arm/tcg/translate-neon.c | 6 ++---- target/arm/tcg/translate.h | 4 ++++ 7 files changed, 83 insertions(+), 22 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 25eb7bf5df..f345087ddb 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -326,6 +326,14 @@ DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(neon_sqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 216a9f81e3..63c3ec2e73 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1240,6 +1240,30 @@ void gen_gvec_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); } +void gen_neon_sqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_sqshl_b, gen_helper_neon_sqshl_h, + gen_helper_neon_sqshl_s, gen_helper_neon_sqshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_uqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_uqshl_b, gen_helper_neon_uqshl_h, + gen_helper_neon_uqshl_s, gen_helper_neon_uqshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) { uint64_t max = MAKE_64BIT_MASK(0, 8 << esz); diff --git a/target/arm/tcg/neon-dp.decode b/target/arm/tcg/neon-dp.decode index 8525c65c0d..6d4996b8d8 100644 --- a/target/arm/tcg/neon-dp.decode +++ b/target/arm/tcg/neon-dp.decode @@ -109,14 +109,8 @@ VSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 0 .... @3same_rev @3same_64_rev .... ... . . . 11 .... .... .... . q:1 . . .... \ &3same vm=%vn_dp vn=%vm_dp vd=%vd_dp size=3 -{ - VQSHL_S64_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_64_rev - VQSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_rev -} -{ - VQSHL_U64_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_64_rev - VQSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_rev -} +VQSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_rev +VQSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_rev VRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_rev VRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_rev { diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 516ecc1dcb..88301f0dcb 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -129,6 +129,18 @@ void HELPER(name)(void *vd, void *vn, void *vm, uint32_t desc) \ clear_tail(d, opr_sz, simd_maxsz(desc)); \ } +#define NEON_GVEC_VOP2_ENV(name, vtype) \ +void HELPER(name)(void *vd, void *vn, void *vm, void *venv, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + vtype *d = vd, *n = vn, *m = vm; \ + CPUARMState *env = venv; \ + for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ + NEON_FN(d[i], n[i], m[i]); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + /* Pairwise operations. */ /* For 32-bit elements each segment only contains a single element, so the elementwise and pairwise operations are the same. */ @@ -339,11 +351,23 @@ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shift) #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) NEON_VOP_ENV(qshl_u8, neon_u8, 4) +NEON_GVEC_VOP2_ENV(neon_uqshl_b, uint8_t) #undef NEON_FN #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) NEON_VOP_ENV(qshl_u16, neon_u16, 2) +NEON_GVEC_VOP2_ENV(neon_uqshl_h, uint16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqshl_s, uint32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, (int8_t)src2, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqshl_d, uint64_t) #undef NEON_FN uint32_t HELPER(neon_qshl_u32)(CPUARMState *env, uint32_t val, uint32_t shift) @@ -359,11 +383,23 @@ uint64_t HELPER(neon_qshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) NEON_VOP_ENV(qshl_s8, neon_s8, 4) +NEON_GVEC_VOP2_ENV(neon_sqshl_b, int8_t) #undef NEON_FN #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) NEON_VOP_ENV(qshl_s16, neon_s16, 2) +NEON_GVEC_VOP2_ENV(neon_sqshl_h, int16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, (int8_t)src2, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqshl_d, int64_t) #undef NEON_FN uint32_t HELPER(neon_qshl_s32)(CPUARMState *env, uint32_t val, uint32_t shift) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c751da78ef..c88702dad6 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10937,6 +10937,13 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x09: /* SQSHL, UQSHL */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_uqshl, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_sqshl, size); + } + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11072,16 +11079,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0x9: /* SQSHL, UQSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qshl_s8, gen_helper_neon_qshl_u8 }, - { gen_helper_neon_qshl_s16, gen_helper_neon_qshl_u16 }, - { gen_helper_neon_qshl_s32, gen_helper_neon_qshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } case 0xb: /* SQRSHL, UQRSHL */ { static NeonGenTwoOpEnvFn * const fns[3][2] = { diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 337488bbf1..a3eec47092 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -796,6 +796,8 @@ DO_3SAME(VQSUB_S, gen_gvec_sqsub_qc) DO_3SAME(VQSUB_U, gen_gvec_uqsub_qc) DO_3SAME(VRSHL_S, gen_gvec_srshl) DO_3SAME(VRSHL_U, gen_gvec_urshl) +DO_3SAME(VQSHL_S, gen_neon_sqshl) +DO_3SAME(VQSHL_U, gen_neon_uqshl) /* These insns are all gvec_bitsel but with the inputs in various orders. */ #define DO_3SAME_BITSEL(INSN, O1, O2, O3) \ @@ -931,8 +933,6 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) } \ DO_3SAME_64(INSN, gen_##INSN##_elt) -DO_3SAME_64_ENV(VQSHL_S64, gen_helper_neon_qshl_s64) -DO_3SAME_64_ENV(VQSHL_U64, gen_helper_neon_qshl_u64) DO_3SAME_64_ENV(VQRSHL_S64, gen_helper_neon_qrshl_s64) DO_3SAME_64_ENV(VQRSHL_U64, gen_helper_neon_qrshl_u64) @@ -1000,8 +1000,6 @@ DO_3SAME_32(VHSUB_U, hsub_u) DO_3SAME_32(VRHADD_S, rhadd_s) DO_3SAME_32(VRHADD_U, rhadd_u) -DO_3SAME_32_ENV(VQSHL_S, qshl_s) -DO_3SAME_32_ENV(VQSHL_U, qshl_u) DO_3SAME_32_ENV(VQRSHL_S, qrshl_s) DO_3SAME_32_ENV(VQRSHL_U, qrshl_u) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index ea63ffc47b..6c6d4d49e7 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -463,6 +463,10 @@ void gen_gvec_srshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_sqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_uqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); From 19b58f3048bad5761d8620360f499daf2b86ee0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:24 -0700 Subject: [PATCH 1045/2884] target/arm: Convert SQSHL, UQSHL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++ target/arm/tcg/translate-a64.c | 74 ++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 9e02776036..85caf37948 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -760,6 +760,8 @@ SSHL_s 0101 1110 111 ..... 01000 1 ..... ..... @rrr_d USHL_s 0111 1110 111 ..... 01000 1 ..... ..... @rrr_d SRSHL_s 0101 1110 111 ..... 01010 1 ..... ..... @rrr_d URSHL_s 0111 1110 111 ..... 01010 1 ..... ..... @rrr_d +SQSHL_s 0101 1110 ..1 ..... 01001 1 ..... ..... @rrr_e +UQSHL_s 0111 1110 ..1 ..... 01001 1 ..... ..... @rrr_e ### Advanced SIMD scalar pairwise @@ -886,6 +888,8 @@ SSHL_v 0.00 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e USHL_v 0.10 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e SRSHL_v 0.00 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e URSHL_v 0.10 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e +SQSHL_v 0.00 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e +UQSHL_v 0.10 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c88702dad6..97bd69eb3f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5119,6 +5119,49 @@ TRANS(USHL_s, do_int3_scalar_d, a, gen_ushl_i64) TRANS(SRSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_s64) TRANS(URSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_u64) +typedef struct ENVScalar2 { + NeonGenTwoOpEnvFn *gen_bhs[3]; + NeonGenTwo64OpEnvFn *gen_d; +} ENVScalar2; + +static bool do_env_scalar2(DisasContext *s, arg_rrr_e *a, const ENVScalar2 *f) +{ + if (!fp_access_check(s)) { + return true; + } + if (a->esz == MO_64) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = read_fp_dreg(s, a->rm); + f->gen_d(t0, tcg_env, t0, t1); + write_fp_dreg(s, a->rd, t0); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, 0, a->esz); + f->gen_bhs[a->esz](t0, tcg_env, t0, t1); + write_fp_sreg(s, a->rd, t0); + } + return true; +} + +static const ENVScalar2 f_scalar_sqshl = { + { gen_helper_neon_qshl_s8, + gen_helper_neon_qshl_s16, + gen_helper_neon_qshl_s32 }, + gen_helper_neon_qshl_s64, +}; +TRANS(SQSHL_s, do_env_scalar2, a, &f_scalar_sqshl) + +static const ENVScalar2 f_scalar_uqshl = { + { gen_helper_neon_qshl_u8, + gen_helper_neon_qshl_u16, + gen_helper_neon_qshl_u32 }, + gen_helper_neon_qshl_u64, +}; +TRANS(UQSHL_s, do_env_scalar2, a, &f_scalar_uqshl) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5368,6 +5411,8 @@ TRANS(SSHL_v, do_gvec_fn3, a, gen_gvec_sshl) TRANS(USHL_v, do_gvec_fn3, a, gen_gvec_ushl) TRANS(SRSHL_v, do_gvec_fn3, a, gen_gvec_srshl) TRANS(URSHL_v, do_gvec_fn3, a, gen_gvec_urshl) +TRANS(SQSHL_v, do_gvec_fn3, a, gen_neon_sqshl) +TRANS(UQSHL_v, do_gvec_fn3, a, gen_neon_uqshl) /* @@ -9381,13 +9426,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, } gen_cmtst_i64(tcg_rd, tcg_rn, tcg_rm); break; - case 0x9: /* SQSHL, UQSHL */ - if (u) { - gen_helper_neon_qshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); - } else { - gen_helper_neon_qshl_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); - } - break; case 0xb: /* SQRSHL, UQRSHL */ if (u) { gen_helper_neon_qrshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); @@ -9406,6 +9444,7 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, case 0x1: /* SQADD / UQADD */ case 0x5: /* SQSUB / UQSUB */ case 0x8: /* SSHL, USHL */ + case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ g_assert_not_reached(); } @@ -9428,7 +9467,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) TCGv_i64 tcg_rd; switch (opcode) { - case 0x9: /* SQSHL, UQSHL */ case 0xb: /* SQRSHL, UQRSHL */ break; case 0x6: /* CMGT, CMHI */ @@ -9450,6 +9488,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ case 0x8: /* SSHL, USHL */ + case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ unallocated_encoding(s); return; @@ -9477,16 +9516,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) void (*genfn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp) = NULL; switch (opcode) { - case 0x9: /* SQSHL, UQSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qshl_s8, gen_helper_neon_qshl_u8 }, - { gen_helper_neon_qshl_s16, gen_helper_neon_qshl_u16 }, - { gen_helper_neon_qshl_s32, gen_helper_neon_qshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } case 0xb: /* SQRSHL, UQRSHL */ { static NeonGenTwoOpEnvFn * const fns[3][2] = { @@ -9510,6 +9539,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) default: case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ + case 0x9: /* SQSHL, UQSHL */ g_assert_not_reached(); } @@ -10927,6 +10957,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x01: /* SQADD, UQADD */ case 0x05: /* SQSUB, UQSUB */ case 0x08: /* SSHL, USHL */ + case 0x09: /* SQSHL, UQSHL */ case 0x0a: /* SRSHL, URSHL */ unallocated_encoding(s); return; @@ -10937,13 +10968,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x09: /* SQSHL, UQSHL */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_uqshl, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_sqshl, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From cef9d54f6b6f2a2ecbfd358b7124c921bae4fb32 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:25 -0700 Subject: [PATCH 1046/2884] target/arm: Convert SQRSHL and UQRSHL (register) to gvec Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 8 ++++++ target/arm/tcg/gengvec.c | 24 ++++++++++++++++ target/arm/tcg/neon-dp.decode | 17 ++---------- target/arm/tcg/neon_helper.c | 24 ++++++++++++++++ target/arm/tcg/translate-a64.c | 17 +++++------- target/arm/tcg/translate-neon.c | 49 ++------------------------------- target/arm/tcg/translate.h | 4 +++ 7 files changed, 71 insertions(+), 72 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index f345087ddb..9a89c9cea7 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -334,6 +334,14 @@ DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 63c3ec2e73..6dc96269d5 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1264,6 +1264,30 @@ void gen_neon_uqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, opr_sz, max_sz, 0, fns[vece]); } +void gen_neon_sqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_sqrshl_b, gen_helper_neon_sqrshl_h, + gen_helper_neon_sqrshl_s, gen_helper_neon_sqrshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_uqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_uqrshl_b, gen_helper_neon_uqrshl_h, + gen_helper_neon_uqrshl_s, gen_helper_neon_uqrshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) { uint64_t max = MAKE_64BIT_MASK(0, 8 << esz); diff --git a/target/arm/tcg/neon-dp.decode b/target/arm/tcg/neon-dp.decode index 6d4996b8d8..788578c8fa 100644 --- a/target/arm/tcg/neon-dp.decode +++ b/target/arm/tcg/neon-dp.decode @@ -102,25 +102,12 @@ VCGE_U_3s 1111 001 1 0 . .. .... .... 0011 . . . 1 .... @3same VSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 0 .... @3same_rev VSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 0 .... @3same_rev - -# Insns operating on 64-bit elements (size!=0b11 handled elsewhere) -# The _rev suffix indicates that Vn and Vm are reversed (as explained -# by the comment for the @3same_rev format). -@3same_64_rev .... ... . . . 11 .... .... .... . q:1 . . .... \ - &3same vm=%vn_dp vn=%vm_dp vd=%vd_dp size=3 - VQSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_rev VQSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_rev VRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_rev VRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_rev -{ - VQRSHL_S64_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_64_rev - VQRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_rev -} -{ - VQRSHL_U64_3s 1111 001 1 0 . .. .... .... 0101 . . . 1 .... @3same_64_rev - VQRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 1 .... @3same_rev -} +VQRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_rev +VQRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 1 .... @3same_rev VMAX_S_3s 1111 001 0 0 . .. .... .... 0110 . . . 0 .... @3same VMAX_U_3s 1111 001 1 0 . .. .... .... 0110 . . . 0 .... @3same diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 88301f0dcb..b29a7c725f 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -435,11 +435,23 @@ uint64_t HELPER(neon_qshlu_s64)(CPUARMState *env, uint64_t val, uint64_t shift) #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, true, env->vfp.qc)) NEON_VOP_ENV(qrshl_u8, neon_u8, 4) +NEON_GVEC_VOP2_ENV(neon_uqrshl_b, uint8_t) #undef NEON_FN #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, true, env->vfp.qc)) NEON_VOP_ENV(qrshl_u16, neon_u16, 2) +NEON_GVEC_VOP2_ENV(neon_uqrshl_h, uint16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqrshl_s, uint32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, (int8_t)src2, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqrshl_d, uint64_t) #undef NEON_FN uint32_t HELPER(neon_qrshl_u32)(CPUARMState *env, uint32_t val, uint32_t shift) @@ -455,11 +467,23 @@ uint64_t HELPER(neon_qrshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, true, env->vfp.qc)) NEON_VOP_ENV(qrshl_s8, neon_s8, 4) +NEON_GVEC_VOP2_ENV(neon_sqrshl_b, int8_t) #undef NEON_FN #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, true, env->vfp.qc)) NEON_VOP_ENV(qrshl_s16, neon_s16, 2) +NEON_GVEC_VOP2_ENV(neon_sqrshl_h, int16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqrshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, (int8_t)src2, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqrshl_d, int64_t) #undef NEON_FN uint32_t HELPER(neon_qrshl_s32)(CPUARMState *env, uint32_t val, uint32_t shift) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 97bd69eb3f..b9d577f620 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10968,6 +10968,13 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x0b: /* SQRSHL, UQRSHL */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_uqrshl, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_sqrshl, size); + } + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11103,16 +11110,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0xb: /* SQRSHL, UQRSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qrshl_s8, gen_helper_neon_qrshl_u8 }, - { gen_helper_neon_qrshl_s16, gen_helper_neon_qrshl_u16 }, - { gen_helper_neon_qrshl_s32, gen_helper_neon_qrshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } default: g_assert_not_reached(); } diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index a3eec47092..5f1576393e 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -798,6 +798,8 @@ DO_3SAME(VRSHL_S, gen_gvec_srshl) DO_3SAME(VRSHL_U, gen_gvec_urshl) DO_3SAME(VQSHL_S, gen_neon_sqshl) DO_3SAME(VQSHL_U, gen_neon_uqshl) +DO_3SAME(VQRSHL_S, gen_neon_sqrshl) +DO_3SAME(VQRSHL_U, gen_neon_uqrshl) /* These insns are all gvec_bitsel but with the inputs in various orders. */ #define DO_3SAME_BITSEL(INSN, O1, O2, O3) \ @@ -916,26 +918,6 @@ DO_SHA2(SHA256H, gen_helper_crypto_sha256h) DO_SHA2(SHA256H2, gen_helper_crypto_sha256h2) DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) -#define DO_3SAME_64(INSN, FUNC) \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 op = { .fni8 = FUNC }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &op); \ - } \ - DO_3SAME(INSN, gen_##INSN##_3s) - -#define DO_3SAME_64_ENV(INSN, FUNC) \ - static void gen_##INSN##_elt(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) \ - { \ - FUNC(d, tcg_env, n, m); \ - } \ - DO_3SAME_64(INSN, gen_##INSN##_elt) - -DO_3SAME_64_ENV(VQRSHL_S64, gen_helper_neon_qrshl_s64) -DO_3SAME_64_ENV(VQRSHL_U64, gen_helper_neon_qrshl_u64) - #define DO_3SAME_32(INSN, FUNC) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ uint32_t rn_ofs, uint32_t rm_ofs, \ @@ -969,30 +951,6 @@ DO_3SAME_64_ENV(VQRSHL_U64, gen_helper_neon_qrshl_u64) FUNC(d, tcg_env, n, m); \ } -#define DO_3SAME_32_ENV(INSN, FUNC) \ - WRAP_ENV_FN(gen_##INSN##_tramp8, gen_helper_neon_##FUNC##8); \ - WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##16); \ - WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##32); \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 ops[4] = { \ - { .fni4 = gen_##INSN##_tramp8 }, \ - { .fni4 = gen_##INSN##_tramp16 }, \ - { .fni4 = gen_##INSN##_tramp32 }, \ - { 0 }, \ - }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &ops[vece]); \ - } \ - static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size > 2) { \ - return false; \ - } \ - return do_3same(s, a, gen_##INSN##_3s); \ - } - DO_3SAME_32(VHADD_S, hadd_s) DO_3SAME_32(VHADD_U, hadd_u) DO_3SAME_32(VHSUB_S, hsub_s) @@ -1000,9 +958,6 @@ DO_3SAME_32(VHSUB_U, hsub_u) DO_3SAME_32(VRHADD_S, rhadd_s) DO_3SAME_32(VRHADD_U, rhadd_u) -DO_3SAME_32_ENV(VQRSHL_S, qrshl_s) -DO_3SAME_32_ENV(VQRSHL_U, qrshl_u) - #define DO_3SAME_VQDMULH(INSN, FUNC) \ WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \ WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##_s32); \ diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 6c6d4d49e7..048cb45ebe 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -467,6 +467,10 @@ void gen_neon_sqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_neon_uqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_sqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_uqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); From 4f92fd736d4c4899e7a27b983d54b62f894eca07 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:26 -0700 Subject: [PATCH 1047/2884] target/arm: Convert SQRSHL, UQRSHL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 +++ target/arm/tcg/translate-a64.c | 48 ++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 85caf37948..96ce35ad40 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -762,6 +762,8 @@ SRSHL_s 0101 1110 111 ..... 01010 1 ..... ..... @rrr_d URSHL_s 0111 1110 111 ..... 01010 1 ..... ..... @rrr_d SQSHL_s 0101 1110 ..1 ..... 01001 1 ..... ..... @rrr_e UQSHL_s 0111 1110 ..1 ..... 01001 1 ..... ..... @rrr_e +SQRSHL_s 0101 1110 ..1 ..... 01011 1 ..... ..... @rrr_e +UQRSHL_s 0111 1110 ..1 ..... 01011 1 ..... ..... @rrr_e ### Advanced SIMD scalar pairwise @@ -890,6 +892,8 @@ SRSHL_v 0.00 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e URSHL_v 0.10 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e SQSHL_v 0.00 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e UQSHL_v 0.10 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e +SQRSHL_v 0.00 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e +UQRSHL_v 0.10 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b9d577f620..2424c6d314 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5162,6 +5162,22 @@ static const ENVScalar2 f_scalar_uqshl = { }; TRANS(UQSHL_s, do_env_scalar2, a, &f_scalar_uqshl) +static const ENVScalar2 f_scalar_sqrshl = { + { gen_helper_neon_qrshl_s8, + gen_helper_neon_qrshl_s16, + gen_helper_neon_qrshl_s32 }, + gen_helper_neon_qrshl_s64, +}; +TRANS(SQRSHL_s, do_env_scalar2, a, &f_scalar_sqrshl) + +static const ENVScalar2 f_scalar_uqrshl = { + { gen_helper_neon_qrshl_u8, + gen_helper_neon_qrshl_u16, + gen_helper_neon_qrshl_u32 }, + gen_helper_neon_qrshl_u64, +}; +TRANS(UQRSHL_s, do_env_scalar2, a, &f_scalar_uqrshl) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5413,6 +5429,8 @@ TRANS(SRSHL_v, do_gvec_fn3, a, gen_gvec_srshl) TRANS(URSHL_v, do_gvec_fn3, a, gen_gvec_urshl) TRANS(SQSHL_v, do_gvec_fn3, a, gen_neon_sqshl) TRANS(UQSHL_v, do_gvec_fn3, a, gen_neon_uqshl) +TRANS(SQRSHL_v, do_gvec_fn3, a, gen_neon_sqrshl) +TRANS(UQRSHL_v, do_gvec_fn3, a, gen_neon_uqrshl) /* @@ -9426,13 +9444,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, } gen_cmtst_i64(tcg_rd, tcg_rn, tcg_rm); break; - case 0xb: /* SQRSHL, UQRSHL */ - if (u) { - gen_helper_neon_qrshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); - } else { - gen_helper_neon_qrshl_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); - } - break; case 0x10: /* ADD, SUB */ if (u) { tcg_gen_sub_i64(tcg_rd, tcg_rn, tcg_rm); @@ -9446,6 +9457,7 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, case 0x8: /* SSHL, USHL */ case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ + case 0xb: /* SQRSHL, UQRSHL */ g_assert_not_reached(); } } @@ -9467,8 +9479,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) TCGv_i64 tcg_rd; switch (opcode) { - case 0xb: /* SQRSHL, UQRSHL */ - break; case 0x6: /* CMGT, CMHI */ case 0x7: /* CMGE, CMHS */ case 0x11: /* CMTST, CMEQ */ @@ -9490,6 +9500,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x8: /* SSHL, USHL */ case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ + case 0xb: /* SQRSHL, UQRSHL */ unallocated_encoding(s); return; } @@ -9516,16 +9527,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) void (*genfn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp) = NULL; switch (opcode) { - case 0xb: /* SQRSHL, UQRSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qrshl_s8, gen_helper_neon_qrshl_u8 }, - { gen_helper_neon_qrshl_s16, gen_helper_neon_qrshl_u16 }, - { gen_helper_neon_qrshl_s32, gen_helper_neon_qrshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } case 0x16: /* SQDMULH, SQRDMULH */ { static NeonGenTwoOpEnvFn * const fns[2][2] = { @@ -9540,6 +9541,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ case 0x9: /* SQSHL, UQSHL */ + case 0xb: /* SQRSHL, UQRSHL */ g_assert_not_reached(); } @@ -10959,6 +10961,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x08: /* SSHL, USHL */ case 0x09: /* SQSHL, UQSHL */ case 0x0a: /* SRSHL, URSHL */ + case 0x0b: /* SQRSHL, UQRSHL */ unallocated_encoding(s); return; } @@ -10968,13 +10971,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x0b: /* SQRSHL, UQRSHL */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_uqrshl, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_neon_sqrshl, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From 6c9bccf52fc55b7d47b4a0904ee804bc2a6994fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:27 -0700 Subject: [PATCH 1048/2884] target/arm: Convert ADD, SUB (vector) to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 6 ++++++ target/arm/tcg/translate-a64.c | 22 +++++++--------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 96ce35ad40..44383b4fc7 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -765,6 +765,9 @@ UQSHL_s 0111 1110 ..1 ..... 01001 1 ..... ..... @rrr_e SQRSHL_s 0101 1110 ..1 ..... 01011 1 ..... ..... @rrr_e UQRSHL_s 0111 1110 ..1 ..... 01011 1 ..... ..... @rrr_e +ADD_s 0101 1110 111 ..... 10000 1 ..... ..... @rrr_d +SUB_s 0111 1110 111 ..... 10000 1 ..... ..... @rrr_d + ### Advanced SIMD scalar pairwise FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h @@ -895,6 +898,9 @@ UQSHL_v 0.10 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e SQRSHL_v 0.00 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e UQRSHL_v 0.10 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e +ADD_v 0.00 1110 ..1 ..... 10000 1 ..... ..... @qrrr_e +SUB_v 0.10 1110 ..1 ..... 10000 1 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2424c6d314..77a64923e7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5118,6 +5118,8 @@ TRANS(SSHL_s, do_int3_scalar_d, a, gen_sshl_i64) TRANS(USHL_s, do_int3_scalar_d, a, gen_ushl_i64) TRANS(SRSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_s64) TRANS(URSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_u64) +TRANS(ADD_s, do_int3_scalar_d, a, tcg_gen_add_i64) +TRANS(SUB_s, do_int3_scalar_d, a, tcg_gen_sub_i64) typedef struct ENVScalar2 { NeonGenTwoOpEnvFn *gen_bhs[3]; @@ -5432,6 +5434,8 @@ TRANS(UQSHL_v, do_gvec_fn3, a, gen_neon_uqshl) TRANS(SQRSHL_v, do_gvec_fn3, a, gen_neon_sqrshl) TRANS(UQRSHL_v, do_gvec_fn3, a, gen_neon_uqrshl) +TRANS(ADD_v, do_gvec_fn3, a, tcg_gen_gvec_add) +TRANS(SUB_v, do_gvec_fn3, a, tcg_gen_gvec_sub) /* * Advanced SIMD scalar/vector x indexed element @@ -9444,13 +9448,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, } gen_cmtst_i64(tcg_rd, tcg_rn, tcg_rm); break; - case 0x10: /* ADD, SUB */ - if (u) { - tcg_gen_sub_i64(tcg_rd, tcg_rn, tcg_rm); - } else { - tcg_gen_add_i64(tcg_rd, tcg_rn, tcg_rm); - } - break; default: case 0x1: /* SQADD / UQADD */ case 0x5: /* SQSUB / UQSUB */ @@ -9458,6 +9455,7 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ case 0xb: /* SQRSHL, UQRSHL */ + case 0x10: /* ADD, SUB */ g_assert_not_reached(); } } @@ -9482,7 +9480,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x6: /* CMGT, CMHI */ case 0x7: /* CMGE, CMHS */ case 0x11: /* CMTST, CMEQ */ - case 0x10: /* ADD, SUB (vector) */ if (size != 3) { unallocated_encoding(s); return; @@ -9501,6 +9498,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ case 0xb: /* SQRSHL, UQRSHL */ + case 0x10: /* ADD, SUB (vector) */ unallocated_encoding(s); return; } @@ -10962,6 +10960,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x09: /* SQSHL, UQSHL */ case 0x0a: /* SRSHL, URSHL */ case 0x0b: /* SQRSHL, UQRSHL */ + case 0x10: /* ADD, SUB */ unallocated_encoding(s); return; } @@ -10999,13 +10998,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_saba, size); } return; - case 0x10: /* ADD, SUB */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_sub, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_add, size); - } - return; case 0x13: /* MUL, PMUL */ if (!u) { /* MUL */ gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_mul, size); From 649005fd3a1805035b1a2343bae9505720275a97 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:28 -0700 Subject: [PATCH 1049/2884] target/arm: Convert CMGT, CMHI, CMGE, CMHS, CMTST, CMEQ to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 12 +++ target/arm/tcg/translate-a64.c | 132 ++++++++++++--------------------- 2 files changed, 60 insertions(+), 84 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 44383b4fc7..3061e26242 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -767,6 +767,12 @@ UQRSHL_s 0111 1110 ..1 ..... 01011 1 ..... ..... @rrr_e ADD_s 0101 1110 111 ..... 10000 1 ..... ..... @rrr_d SUB_s 0111 1110 111 ..... 10000 1 ..... ..... @rrr_d +CMGT_s 0101 1110 111 ..... 00110 1 ..... ..... @rrr_d +CMHI_s 0111 1110 111 ..... 00110 1 ..... ..... @rrr_d +CMGE_s 0101 1110 111 ..... 00111 1 ..... ..... @rrr_d +CMHS_s 0111 1110 111 ..... 00111 1 ..... ..... @rrr_d +CMTST_s 0101 1110 111 ..... 10001 1 ..... ..... @rrr_d +CMEQ_s 0111 1110 111 ..... 10001 1 ..... ..... @rrr_d ### Advanced SIMD scalar pairwise @@ -900,6 +906,12 @@ UQRSHL_v 0.10 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e ADD_v 0.00 1110 ..1 ..... 10000 1 ..... ..... @qrrr_e SUB_v 0.10 1110 ..1 ..... 10000 1 ..... ..... @qrrr_e +CMGT_v 0.00 1110 ..1 ..... 00110 1 ..... ..... @qrrr_e +CMHI_v 0.10 1110 ..1 ..... 00110 1 ..... ..... @qrrr_e +CMGE_v 0.00 1110 ..1 ..... 00111 1 ..... ..... @qrrr_e +CMHS_v 0.10 1110 ..1 ..... 00111 1 ..... ..... @qrrr_e +CMTST_v 0.00 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e +CMEQ_v 0.10 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 77a64923e7..3c6cfc2952 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5180,6 +5180,24 @@ static const ENVScalar2 f_scalar_uqrshl = { }; TRANS(UQRSHL_s, do_env_scalar2, a, &f_scalar_uqrshl) +static bool do_cmop_d(DisasContext *s, arg_rrr_e *a, TCGCond cond) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = read_fp_dreg(s, a->rm); + tcg_gen_negsetcond_i64(cond, t0, t0, t1); + write_fp_dreg(s, a->rd, t0); + } + return true; +} + +TRANS(CMGT_s, do_cmop_d, a, TCG_COND_GT) +TRANS(CMHI_s, do_cmop_d, a, TCG_COND_GTU) +TRANS(CMGE_s, do_cmop_d, a, TCG_COND_GE) +TRANS(CMHS_s, do_cmop_d, a, TCG_COND_GEU) +TRANS(CMEQ_s, do_cmop_d, a, TCG_COND_EQ) +TRANS(CMTST_s, do_cmop_d, a, TCG_COND_TSTNE) + static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5437,6 +5455,28 @@ TRANS(UQRSHL_v, do_gvec_fn3, a, gen_neon_uqrshl) TRANS(ADD_v, do_gvec_fn3, a, tcg_gen_gvec_add) TRANS(SUB_v, do_gvec_fn3, a, tcg_gen_gvec_sub) +static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) +{ + if (a->esz == MO_64 && !a->q) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_cmp(cond, a->esz, + vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + a->q ? 16 : 8, vec_full_reg_size(s)); + } + return true; +} + +TRANS(CMGT_v, do_cmop_v, a, TCG_COND_GT) +TRANS(CMHI_v, do_cmop_v, a, TCG_COND_GTU) +TRANS(CMGE_v, do_cmop_v, a, TCG_COND_GE) +TRANS(CMHS_v, do_cmop_v, a, TCG_COND_GEU) +TRANS(CMEQ_v, do_cmop_v, a, TCG_COND_EQ) +TRANS(CMTST_v, do_gvec_fn3, a, gen_gvec_cmtst) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -9421,45 +9461,6 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) } } -static void handle_3same_64(DisasContext *s, int opcode, bool u, - TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 tcg_rm) -{ - /* Handle 64x64->64 opcodes which are shared between the scalar - * and vector 3-same groups. We cover every opcode where size == 3 - * is valid in either the three-reg-same (integer, not pairwise) - * or scalar-three-reg-same groups. - */ - TCGCond cond; - - switch (opcode) { - case 0x6: /* CMGT, CMHI */ - cond = u ? TCG_COND_GTU : TCG_COND_GT; - do_cmop: - /* 64 bit integer comparison, result = test ? -1 : 0. */ - tcg_gen_negsetcond_i64(cond, tcg_rd, tcg_rn, tcg_rm); - break; - case 0x7: /* CMGE, CMHS */ - cond = u ? TCG_COND_GEU : TCG_COND_GE; - goto do_cmop; - case 0x11: /* CMTST, CMEQ */ - if (u) { - cond = TCG_COND_EQ; - goto do_cmop; - } - gen_cmtst_i64(tcg_rd, tcg_rn, tcg_rm); - break; - default: - case 0x1: /* SQADD / UQADD */ - case 0x5: /* SQSUB / UQSUB */ - case 0x8: /* SSHL, USHL */ - case 0x9: /* SQSHL, UQSHL */ - case 0xa: /* SRSHL, URSHL */ - case 0xb: /* SQRSHL, UQRSHL */ - case 0x10: /* ADD, SUB */ - g_assert_not_reached(); - } -} - /* AdvSIMD scalar three same * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0 * +-----+---+-----------+------+---+------+--------+---+------+------+ @@ -9477,14 +9478,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) TCGv_i64 tcg_rd; switch (opcode) { - case 0x6: /* CMGT, CMHI */ - case 0x7: /* CMGE, CMHS */ - case 0x11: /* CMTST, CMEQ */ - if (size != 3) { - unallocated_encoding(s); - return; - } - break; case 0x16: /* SQDMULH, SQRDMULH (vector) */ if (size != 1 && size != 2) { unallocated_encoding(s); @@ -9494,11 +9487,14 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) default: case 0x1: /* SQADD, UQADD */ case 0x5: /* SQSUB, UQSUB */ + case 0x6: /* CMGT, CMHI */ + case 0x7: /* CMGE, CMHS */ case 0x8: /* SSHL, USHL */ case 0x9: /* SQSHL, UQSHL */ case 0xa: /* SRSHL, URSHL */ case 0xb: /* SQRSHL, UQRSHL */ case 0x10: /* ADD, SUB (vector) */ + case 0x11: /* CMTST, CMEQ */ unallocated_encoding(s); return; } @@ -9510,10 +9506,7 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) tcg_rd = tcg_temp_new_i64(); if (size == 3) { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - TCGv_i64 tcg_rm = read_fp_dreg(s, rm); - - handle_3same_64(s, opcode, u, tcg_rd, tcg_rn, tcg_rm); + g_assert_not_reached(); } else { /* Do a single operation on the lowest element in the vector. * We use the standard Neon helpers and rely on 0 OP 0 == 0 with @@ -10919,7 +10912,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); int pass; - TCGCond cond; switch (opcode) { case 0x13: /* MUL, PMUL */ @@ -10956,11 +10948,14 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x01: /* SQADD, UQADD */ case 0x05: /* SQSUB, UQSUB */ + case 0x06: /* CMGT, CMHI */ + case 0x07: /* CMGE, CMHS */ case 0x08: /* SSHL, USHL */ case 0x09: /* SQSHL, UQSHL */ case 0x0a: /* SRSHL, URSHL */ case 0x0b: /* SQRSHL, UQRSHL */ case 0x10: /* ADD, SUB */ + case 0x11: /* CMTST, CMEQ */ unallocated_encoding(s); return; } @@ -11021,41 +11016,10 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) gen_gvec_op3_qc(s, is_q, rd, rn, rm, fns[size - 1][u]); } return; - case 0x11: - if (!u) { /* CMTST */ - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_cmtst, size); - return; - } - /* else CMEQ */ - cond = TCG_COND_EQ; - goto do_gvec_cmp; - case 0x06: /* CMGT, CMHI */ - cond = u ? TCG_COND_GTU : TCG_COND_GT; - goto do_gvec_cmp; - case 0x07: /* CMGE, CMHS */ - cond = u ? TCG_COND_GEU : TCG_COND_GE; - do_gvec_cmp: - tcg_gen_gvec_cmp(cond, size, vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - is_q ? 16 : 8, vec_full_reg_size(s)); - return; } if (size == 3) { - assert(is_q); - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - - handle_3same_64(s, opcode, u, tcg_res, tcg_op1, tcg_op2); - - write_vec_element(s, tcg_res, rd, pass, MO_64); - } + g_assert_not_reached(); } else { for (pass = 0; pass < (is_q ? 4 : 2); pass++) { TCGv_i32 tcg_op1 = tcg_temp_new_i32(); From 013506e03f27ec00a967c593fc34eda0b42bc100 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:29 -0700 Subject: [PATCH 1050/2884] target/arm: Use TCG_COND_TSTNE in gen_cmtst_{i32, i64} Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 6dc96269d5..e64ca02e0c 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -934,14 +934,12 @@ void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, /* CMTST : test is "if (X & Y != 0)". */ static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_and_i32(d, a, b); - tcg_gen_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0)); + tcg_gen_negsetcond_i32(TCG_COND_TSTNE, d, a, b); } void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_and_i64(d, a, b); - tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0)); + tcg_gen_negsetcond_i64(TCG_COND_TSTNE, d, a, b); } static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) From 2310eb0acac213f878be28adfd1c30a51da111f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:30 -0700 Subject: [PATCH 1051/2884] target/arm: Use TCG_COND_TSTNE in gen_cmtst_vec Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index e64ca02e0c..2451d23823 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -944,9 +944,7 @@ void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) { - tcg_gen_and_vec(vece, d, a, b); - tcg_gen_dupi_vec(vece, a, 0); - tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a); + tcg_gen_cmp_vec(TCG_COND_TSTNE, vece, d, a, b); } void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, From 203aca91252c4d74742f89b761bea801b89ca803 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:31 -0700 Subject: [PATCH 1052/2884] target/arm: Convert SHADD, UHADD to gvec Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 6 -- target/arm/tcg/gengvec.c | 144 ++++++++++++++++++++++++++++++++ target/arm/tcg/neon_helper.c | 27 ------ target/arm/tcg/translate-a64.c | 17 ++-- target/arm/tcg/translate-neon.c | 4 +- target/arm/tcg/translate.h | 5 ++ 6 files changed, 158 insertions(+), 45 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 9a89c9cea7..b26bfcb079 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -268,12 +268,6 @@ DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) /* neon_helper.c */ -DEF_HELPER_2(neon_hadd_s8, i32, i32, i32) -DEF_HELPER_2(neon_hadd_u8, i32, i32, i32) -DEF_HELPER_2(neon_hadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_hadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_hadd_s32, s32, s32, s32) -DEF_HELPER_2(neon_hadd_u32, i32, i32, i32) DEF_HELPER_2(neon_rhadd_s8, i32, i32, i32) DEF_HELPER_2(neon_rhadd_u8, i32, i32, i32) DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 2451d23823..c0627a787b 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -1861,3 +1861,147 @@ void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_debug_assert(vece <= MO_32); tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); } + +static void gen_shadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_sar8i_i64(a, a, 1); + tcg_gen_vec_sar8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_shadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_sar16i_i64(a, a, 1); + tcg_gen_vec_sar16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_shadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_and_i32(t, a, b); + tcg_gen_sari_i32(a, a, 1); + tcg_gen_sari_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_shadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_and_vec(vece, t, a, b); + tcg_gen_sari_vec(vece, a, a, 1); + tcg_gen_sari_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_shadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_shadd8_i64, + .fniv = gen_shadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shadd16_i64, + .fniv = gen_shadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shadd_i32, + .fniv = gen_shadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uhadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_shr8i_i64(a, a, 1); + tcg_gen_vec_shr8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_uhadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_shr16i_i64(a, a, 1); + tcg_gen_vec_shr16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_uhadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_and_i32(t, a, b); + tcg_gen_shri_i32(a, a, 1); + tcg_gen_shri_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_uhadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_and_vec(vece, t, a, b); + tcg_gen_shri_vec(vece, a, a, 1); + tcg_gen_shri_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_uhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_uhadd8_i64, + .fniv = gen_uhadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_uhadd16_i64, + .fniv = gen_uhadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_uhadd_i32, + .fniv = gen_uhadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index b29a7c725f..defd28a6f7 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -179,33 +179,6 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ return arg; \ } -#define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1 -NEON_VOP(hadd_s8, neon_s8, 4) -NEON_VOP(hadd_u8, neon_u8, 4) -NEON_VOP(hadd_s16, neon_s16, 2) -NEON_VOP(hadd_u16, neon_u16, 2) -#undef NEON_FN - -int32_t HELPER(neon_hadd_s32)(int32_t src1, int32_t src2) -{ - int32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if (src1 & src2 & 1) - dest++; - return dest; -} - -uint32_t HELPER(neon_hadd_u32)(uint32_t src1, uint32_t src2) -{ - uint32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if (src1 & src2 & 1) - dest++; - return dest; -} - #define NEON_FN(dest, src1, src2) dest = (src1 + src2 + 1) >> 1 NEON_VOP(rhadd_s8, neon_s8, 4) NEON_VOP(rhadd_u8, neon_u8, 4) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3c6cfc2952..5f3423513d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10965,6 +10965,13 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x00: /* SHADD, UHADD */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uhadd, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_shadd, size); + } + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11032,16 +11039,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); switch (opcode) { - case 0x0: /* SHADD, UHADD */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_hadd_s8, gen_helper_neon_hadd_u8 }, - { gen_helper_neon_hadd_s16, gen_helper_neon_hadd_u16 }, - { gen_helper_neon_hadd_s32, gen_helper_neon_hadd_u32 }, - }; - genfn = fns[size][u]; - break; - } case 0x2: /* SRHADD, URHADD */ { static NeonGenTwoOpFn * const fns[3][2] = { diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 5f1576393e..29e5c4a0a3 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -841,6 +841,8 @@ DO_3SAME_NO_SZ_3(VPMAX_S, gen_gvec_smaxp) DO_3SAME_NO_SZ_3(VPMIN_S, gen_gvec_sminp) DO_3SAME_NO_SZ_3(VPMAX_U, gen_gvec_umaxp) DO_3SAME_NO_SZ_3(VPMIN_U, gen_gvec_uminp) +DO_3SAME_NO_SZ_3(VHADD_S, gen_gvec_shadd) +DO_3SAME_NO_SZ_3(VHADD_U, gen_gvec_uhadd) #define DO_3SAME_CMP(INSN, COND) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ @@ -951,8 +953,6 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) FUNC(d, tcg_env, n, m); \ } -DO_3SAME_32(VHADD_S, hadd_s) -DO_3SAME_32(VHADD_U, hadd_u) DO_3SAME_32(VHSUB_S, hsub_s) DO_3SAME_32(VHSUB_U, hsub_u) DO_3SAME_32(VRHADD_S, rhadd_s) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 048cb45ebe..dd99d76bf2 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -472,6 +472,11 @@ void gen_neon_sqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, void gen_neon_uqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_shadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); void gen_sshl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); From 6ef548ed4b07b8f1e22d049ec3da50df7e6e1ccc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:32 -0700 Subject: [PATCH 1053/2884] target/arm: Convert SHADD, UHADD to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 11 +++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 3061e26242..e33d91fd0a 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -912,6 +912,8 @@ CMGE_v 0.00 1110 ..1 ..... 00111 1 ..... ..... @qrrr_e CMHS_v 0.10 1110 ..1 ..... 00111 1 ..... ..... @qrrr_e CMTST_v 0.00 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e CMEQ_v 0.10 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e +SHADD_v 0.00 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e +UHADD_v 0.10 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5f3423513d..00c04425c1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5454,6 +5454,8 @@ TRANS(UQRSHL_v, do_gvec_fn3, a, gen_neon_uqrshl) TRANS(ADD_v, do_gvec_fn3, a, tcg_gen_gvec_add) TRANS(SUB_v, do_gvec_fn3, a, tcg_gen_gvec_sub) +TRANS(SHADD_v, do_gvec_fn3_no64, a, gen_gvec_shadd) +TRANS(UHADD_v, do_gvec_fn3_no64, a, gen_gvec_uhadd) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -10920,7 +10922,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) return; } /* fall through */ - case 0x0: /* SHADD, UHADD */ case 0x2: /* SRHADD, URHADD */ case 0x4: /* SHSUB, UHSUB */ case 0xc: /* SMAX, UMAX */ @@ -10946,6 +10947,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } break; + case 0x0: /* SHADD, UHADD */ case 0x01: /* SQADD, UQADD */ case 0x05: /* SQSUB, UQSUB */ case 0x06: /* CMGT, CMHI */ @@ -10965,13 +10967,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x00: /* SHADD, UHADD */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uhadd, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_shadd, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From 34c0d865a3a29a160f3e572bd49f606cddc56c85 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:33 -0700 Subject: [PATCH 1054/2884] target/arm: Convert SHSUB, UHSUB to gvec Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 6 -- target/arm/tcg/gengvec.c | 144 ++++++++++++++++++++++++++++++++ target/arm/tcg/neon_helper.c | 27 ------ target/arm/tcg/translate-a64.c | 17 ++-- target/arm/tcg/translate-neon.c | 4 +- target/arm/tcg/translate.h | 4 + 6 files changed, 157 insertions(+), 45 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index b26bfcb079..b95f24ed0a 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -274,12 +274,6 @@ DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32) DEF_HELPER_2(neon_rhadd_u16, i32, i32, i32) DEF_HELPER_2(neon_rhadd_s32, s32, s32, s32) DEF_HELPER_2(neon_rhadd_u32, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s8, i32, i32, i32) -DEF_HELPER_2(neon_hsub_u8, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s16, i32, i32, i32) -DEF_HELPER_2(neon_hsub_u16, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s32, s32, s32, s32) -DEF_HELPER_2(neon_hsub_u32, i32, i32, i32) DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index c0627a787b..c46365c3a6 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2005,3 +2005,147 @@ void gen_gvec_uhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_debug_assert(vece <= MO_32); tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); } + +static void gen_shsub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_sar8i_i64(a, a, 1); + tcg_gen_vec_sar8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_sub8_i64(d, a, b); + tcg_gen_vec_sub8_i64(d, d, t); +} + +static void gen_shsub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_sar16i_i64(a, a, 1); + tcg_gen_vec_sar16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_sub16_i64(d, a, b); + tcg_gen_vec_sub16_i64(d, d, t); +} + +static void gen_shsub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_andc_i32(t, b, a); + tcg_gen_sari_i32(a, a, 1); + tcg_gen_sari_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_sub_i32(d, a, b); + tcg_gen_sub_i32(d, d, t); +} + +static void gen_shsub_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_andc_vec(vece, t, b, a); + tcg_gen_sari_vec(vece, a, a, 1); + tcg_gen_sari_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_sub_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_shsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 g[4] = { + { .fni8 = gen_shsub8_i64, + .fniv = gen_shsub_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shsub16_i64, + .fniv = gen_shsub_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shsub_i32, + .fniv = gen_shsub_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uhsub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_shr8i_i64(a, a, 1); + tcg_gen_vec_shr8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_sub8_i64(d, a, b); + tcg_gen_vec_sub8_i64(d, d, t); +} + +static void gen_uhsub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_shr16i_i64(a, a, 1); + tcg_gen_vec_shr16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_sub16_i64(d, a, b); + tcg_gen_vec_sub16_i64(d, d, t); +} + +static void gen_uhsub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_andc_i32(t, b, a); + tcg_gen_shri_i32(a, a, 1); + tcg_gen_shri_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_sub_i32(d, a, b); + tcg_gen_sub_i32(d, d, t); +} + +static void gen_uhsub_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_andc_vec(vece, t, b, a); + tcg_gen_shri_vec(vece, a, a, 1); + tcg_gen_shri_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_sub_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_uhsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 g[4] = { + { .fni8 = gen_uhsub8_i64, + .fniv = gen_uhsub_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_uhsub16_i64, + .fniv = gen_uhsub_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_uhsub_i32, + .fniv = gen_uhsub_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index defd28a6f7..d1641a5252 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -206,33 +206,6 @@ uint32_t HELPER(neon_rhadd_u32)(uint32_t src1, uint32_t src2) return dest; } -#define NEON_FN(dest, src1, src2) dest = (src1 - src2) >> 1 -NEON_VOP(hsub_s8, neon_s8, 4) -NEON_VOP(hsub_u8, neon_u8, 4) -NEON_VOP(hsub_s16, neon_s16, 2) -NEON_VOP(hsub_u16, neon_u16, 2) -#undef NEON_FN - -int32_t HELPER(neon_hsub_s32)(int32_t src1, int32_t src2) -{ - int32_t dest; - - dest = (src1 >> 1) - (src2 >> 1); - if ((~src1) & src2 & 1) - dest--; - return dest; -} - -uint32_t HELPER(neon_hsub_u32)(uint32_t src1, uint32_t src2) -{ - uint32_t dest; - - dest = (src1 >> 1) - (src2 >> 1); - if ((~src1) & src2 & 1) - dest--; - return dest; -} - #define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2 NEON_POP(pmin_s8, neon_s8, 4) NEON_POP(pmin_u8, neon_u8, 4) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 00c04425c1..63f7a59f94 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10967,6 +10967,13 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x04: /* SHSUB, UHSUB */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uhsub, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_shsub, size); + } + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11044,16 +11051,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0x4: /* SHSUB, UHSUB */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_hsub_s8, gen_helper_neon_hsub_u8 }, - { gen_helper_neon_hsub_s16, gen_helper_neon_hsub_u16 }, - { gen_helper_neon_hsub_s32, gen_helper_neon_hsub_u32 }, - }; - genfn = fns[size][u]; - break; - } default: g_assert_not_reached(); } diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 29e5c4a0a3..d59d5804c5 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -843,6 +843,8 @@ DO_3SAME_NO_SZ_3(VPMAX_U, gen_gvec_umaxp) DO_3SAME_NO_SZ_3(VPMIN_U, gen_gvec_uminp) DO_3SAME_NO_SZ_3(VHADD_S, gen_gvec_shadd) DO_3SAME_NO_SZ_3(VHADD_U, gen_gvec_uhadd) +DO_3SAME_NO_SZ_3(VHSUB_S, gen_gvec_shsub) +DO_3SAME_NO_SZ_3(VHSUB_U, gen_gvec_uhsub) #define DO_3SAME_CMP(INSN, COND) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ @@ -953,8 +955,6 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) FUNC(d, tcg_env, n, m); \ } -DO_3SAME_32(VHSUB_S, hsub_s) -DO_3SAME_32(VHSUB_U, hsub_u) DO_3SAME_32(VRHADD_S, rhadd_s) DO_3SAME_32(VRHADD_U, rhadd_u) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index dd99d76bf2..315e0afd04 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -476,6 +476,10 @@ void gen_gvec_shadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_uhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_shsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uhsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); From fdaf45d852147f0e565c75b18e1dcedc8af6e767 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:34 -0700 Subject: [PATCH 1055/2884] target/arm: Convert SHSUB, UHSUB to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 11 +++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index e33d91fd0a..b1bbcb144e 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -914,6 +914,8 @@ CMTST_v 0.00 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e CMEQ_v 0.10 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e SHADD_v 0.00 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e UHADD_v 0.10 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e +SHSUB_v 0.00 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e +UHSUB_v 0.10 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 63f7a59f94..6571b999f4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5456,6 +5456,8 @@ TRANS(ADD_v, do_gvec_fn3, a, tcg_gen_gvec_add) TRANS(SUB_v, do_gvec_fn3, a, tcg_gen_gvec_sub) TRANS(SHADD_v, do_gvec_fn3_no64, a, gen_gvec_shadd) TRANS(UHADD_v, do_gvec_fn3_no64, a, gen_gvec_uhadd) +TRANS(SHSUB_v, do_gvec_fn3_no64, a, gen_gvec_shsub) +TRANS(UHSUB_v, do_gvec_fn3_no64, a, gen_gvec_uhsub) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -10923,7 +10925,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } /* fall through */ case 0x2: /* SRHADD, URHADD */ - case 0x4: /* SHSUB, UHSUB */ case 0xc: /* SMAX, UMAX */ case 0xd: /* SMIN, UMIN */ case 0xe: /* SABD, UABD */ @@ -10949,6 +10950,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x0: /* SHADD, UHADD */ case 0x01: /* SQADD, UQADD */ + case 0x04: /* SHSUB, UHSUB */ case 0x05: /* SQSUB, UQSUB */ case 0x06: /* CMGT, CMHI */ case 0x07: /* CMGE, CMHS */ @@ -10967,13 +10969,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x04: /* SHSUB, UHSUB */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uhsub, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_shsub, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From 8989b95e71dea9292bab77477949cc1a385c9543 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:35 -0700 Subject: [PATCH 1056/2884] target/arm: Convert SRHADD, URHADD to gvec Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 7 -- target/arm/tcg/gengvec.c | 144 ++++++++++++++++++++++++++++++++ target/arm/tcg/neon_helper.c | 27 ------ target/arm/tcg/translate-a64.c | 48 ++--------- target/arm/tcg/translate-neon.c | 26 +----- target/arm/tcg/translate.h | 4 + 6 files changed, 158 insertions(+), 98 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index b95f24ed0a..85f9302563 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -268,13 +268,6 @@ DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) /* neon_helper.c */ -DEF_HELPER_2(neon_rhadd_s8, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_u8, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s32, s32, s32, s32) -DEF_HELPER_2(neon_rhadd_u32, i32, i32, i32) - DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index c46365c3a6..119826bf28 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -2149,3 +2149,147 @@ void gen_gvec_uhsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, assert(vece <= MO_32); tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); } + +static void gen_srhadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_sar8i_i64(a, a, 1); + tcg_gen_vec_sar8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_srhadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_sar16i_i64(a, a, 1); + tcg_gen_vec_sar16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_srhadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_or_i32(t, a, b); + tcg_gen_sari_i32(a, a, 1); + tcg_gen_sari_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_srhadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_or_vec(vece, t, a, b); + tcg_gen_sari_vec(vece, a, a, 1); + tcg_gen_sari_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_srhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_srhadd8_i64, + .fniv = gen_srhadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_srhadd16_i64, + .fniv = gen_srhadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_srhadd_i32, + .fniv = gen_srhadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_urhadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_shr8i_i64(a, a, 1); + tcg_gen_vec_shr8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_urhadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_shr16i_i64(a, a, 1); + tcg_gen_vec_shr16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_urhadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_or_i32(t, a, b); + tcg_gen_shri_i32(a, a, 1); + tcg_gen_shri_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_urhadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_or_vec(vece, t, a, b); + tcg_gen_shri_vec(vece, a, a, 1); + tcg_gen_shri_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_urhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_urhadd8_i64, + .fniv = gen_urhadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_urhadd16_i64, + .fniv = gen_urhadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_urhadd_i32, + .fniv = gen_urhadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index d1641a5252..082bfd88ad 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -179,33 +179,6 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ return arg; \ } -#define NEON_FN(dest, src1, src2) dest = (src1 + src2 + 1) >> 1 -NEON_VOP(rhadd_s8, neon_s8, 4) -NEON_VOP(rhadd_u8, neon_u8, 4) -NEON_VOP(rhadd_s16, neon_s16, 2) -NEON_VOP(rhadd_u16, neon_u16, 2) -#undef NEON_FN - -int32_t HELPER(neon_rhadd_s32)(int32_t src1, int32_t src2) -{ - int32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if ((src1 | src2) & 1) - dest++; - return dest; -} - -uint32_t HELPER(neon_rhadd_u32)(uint32_t src1, uint32_t src2) -{ - uint32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if ((src1 | src2) & 1) - dest++; - return dest; -} - #define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2 NEON_POP(pmin_s8, neon_s8, 4) NEON_POP(pmin_u8, neon_u8, 4) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 6571b999f4..40aa7a9d57 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10915,7 +10915,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) int rm = extract32(insn, 16, 5); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - int pass; switch (opcode) { case 0x13: /* MUL, PMUL */ @@ -10969,6 +10968,13 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x02: /* SRHADD, URHADD */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_urhadd, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_srhadd, size); + } + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11021,45 +11027,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } return; } - - if (size == 3) { - g_assert_not_reached(); - } else { - for (pass = 0; pass < (is_q ? 4 : 2); pass++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - NeonGenTwoOpFn *genfn = NULL; - NeonGenTwoOpEnvFn *genenvfn = NULL; - - read_vec_element_i32(s, tcg_op1, rn, pass, MO_32); - read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); - - switch (opcode) { - case 0x2: /* SRHADD, URHADD */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_rhadd_s8, gen_helper_neon_rhadd_u8 }, - { gen_helper_neon_rhadd_s16, gen_helper_neon_rhadd_u16 }, - { gen_helper_neon_rhadd_s32, gen_helper_neon_rhadd_u32 }, - }; - genfn = fns[size][u]; - break; - } - default: - g_assert_not_reached(); - } - - if (genenvfn) { - genenvfn(tcg_res, tcg_env, tcg_op1, tcg_op2); - } else { - genfn(tcg_res, tcg_op1, tcg_op2); - } - - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } - clear_vec_high(s, is_q, rd); + g_assert_not_reached(); } /* AdvSIMD three same diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index d59d5804c5..f9a8753906 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -845,6 +845,8 @@ DO_3SAME_NO_SZ_3(VHADD_S, gen_gvec_shadd) DO_3SAME_NO_SZ_3(VHADD_U, gen_gvec_uhadd) DO_3SAME_NO_SZ_3(VHSUB_S, gen_gvec_shsub) DO_3SAME_NO_SZ_3(VHSUB_U, gen_gvec_uhsub) +DO_3SAME_NO_SZ_3(VRHADD_S, gen_gvec_srhadd) +DO_3SAME_NO_SZ_3(VRHADD_U, gen_gvec_urhadd) #define DO_3SAME_CMP(INSN, COND) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ @@ -922,27 +924,6 @@ DO_SHA2(SHA256H, gen_helper_crypto_sha256h) DO_SHA2(SHA256H2, gen_helper_crypto_sha256h2) DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) -#define DO_3SAME_32(INSN, FUNC) \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 ops[4] = { \ - { .fni4 = gen_helper_neon_##FUNC##8 }, \ - { .fni4 = gen_helper_neon_##FUNC##16 }, \ - { .fni4 = gen_helper_neon_##FUNC##32 }, \ - { 0 }, \ - }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &ops[vece]); \ - } \ - static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size > 2) { \ - return false; \ - } \ - return do_3same(s, a, gen_##INSN##_3s); \ - } - /* * Some helper functions need to be passed the tcg_env. In order * to use those with the gvec APIs like tcg_gen_gvec_3() we need @@ -955,9 +936,6 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) FUNC(d, tcg_env, n, m); \ } -DO_3SAME_32(VRHADD_S, rhadd_s) -DO_3SAME_32(VRHADD_U, rhadd_u) - #define DO_3SAME_VQDMULH(INSN, FUNC) \ WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \ WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##_s32); \ diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 315e0afd04..3b1e68b779 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -480,6 +480,10 @@ void gen_gvec_shsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_uhsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_srhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_urhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); From 93b7b9057d825e984463a6ecbce9e7bce12a9ffe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:36 -0700 Subject: [PATCH 1057/2884] target/arm: Convert SRHADD, URHADD to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 11 +++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index b1bbcb144e..1c448b4f7c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -916,6 +916,8 @@ SHADD_v 0.00 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e UHADD_v 0.10 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e SHSUB_v 0.00 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e UHSUB_v 0.10 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e +SRHADD_v 0.00 1110 ..1 ..... 00010 1 ..... ..... @qrrr_e +URHADD_v 0.10 1110 ..1 ..... 00010 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 40aa7a9d57..9ef5de6755 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5458,6 +5458,8 @@ TRANS(SHADD_v, do_gvec_fn3_no64, a, gen_gvec_shadd) TRANS(UHADD_v, do_gvec_fn3_no64, a, gen_gvec_uhadd) TRANS(SHSUB_v, do_gvec_fn3_no64, a, gen_gvec_shsub) TRANS(UHSUB_v, do_gvec_fn3_no64, a, gen_gvec_uhsub) +TRANS(SRHADD_v, do_gvec_fn3_no64, a, gen_gvec_srhadd) +TRANS(URHADD_v, do_gvec_fn3_no64, a, gen_gvec_urhadd) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -10923,7 +10925,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) return; } /* fall through */ - case 0x2: /* SRHADD, URHADD */ case 0xc: /* SMAX, UMAX */ case 0xd: /* SMIN, UMIN */ case 0xe: /* SABD, UABD */ @@ -10949,6 +10950,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x0: /* SHADD, UHADD */ case 0x01: /* SQADD, UQADD */ + case 0x02: /* SRHADD, URHADD */ case 0x04: /* SHSUB, UHSUB */ case 0x05: /* SQSUB, UQSUB */ case 0x06: /* CMGT, CMHI */ @@ -10968,13 +10970,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x02: /* SRHADD, URHADD */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_urhadd, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_srhadd, size); - } - return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); From 41c34bccc2fa834f662f7ac503bfb327ce9ef1cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:37 -0700 Subject: [PATCH 1058/2884] target/arm: Convert SMAX, SMIN, UMAX, UMIN to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-27-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 22 ++++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 1c448b4f7c..bc98963bc5 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -918,6 +918,10 @@ SHSUB_v 0.00 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e UHSUB_v 0.10 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e SRHADD_v 0.00 1110 ..1 ..... 00010 1 ..... ..... @qrrr_e URHADD_v 0.10 1110 ..1 ..... 00010 1 ..... ..... @qrrr_e +SMAX_v 0.00 1110 ..1 ..... 01100 1 ..... ..... @qrrr_e +UMAX_v 0.10 1110 ..1 ..... 01100 1 ..... ..... @qrrr_e +SMIN_v 0.00 1110 ..1 ..... 01101 1 ..... ..... @qrrr_e +UMIN_v 0.10 1110 ..1 ..... 01101 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9ef5de6755..db6f59df17 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5460,6 +5460,10 @@ TRANS(SHSUB_v, do_gvec_fn3_no64, a, gen_gvec_shsub) TRANS(UHSUB_v, do_gvec_fn3_no64, a, gen_gvec_uhsub) TRANS(SRHADD_v, do_gvec_fn3_no64, a, gen_gvec_srhadd) TRANS(URHADD_v, do_gvec_fn3_no64, a, gen_gvec_urhadd) +TRANS(SMAX_v, do_gvec_fn3_no64, a, tcg_gen_gvec_smax) +TRANS(UMAX_v, do_gvec_fn3_no64, a, tcg_gen_gvec_umax) +TRANS(SMIN_v, do_gvec_fn3_no64, a, tcg_gen_gvec_smin) +TRANS(UMIN_v, do_gvec_fn3_no64, a, tcg_gen_gvec_umin) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -10925,8 +10929,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) return; } /* fall through */ - case 0xc: /* SMAX, UMAX */ - case 0xd: /* SMIN, UMIN */ case 0xe: /* SABD, UABD */ case 0xf: /* SABA, UABA */ case 0x12: /* MLA, MLS */ @@ -10959,6 +10961,8 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x09: /* SQSHL, UQSHL */ case 0x0a: /* SRSHL, URSHL */ case 0x0b: /* SQRSHL, UQRSHL */ + case 0x0c: /* SMAX, UMAX */ + case 0x0d: /* SMIN, UMIN */ case 0x10: /* ADD, SUB */ case 0x11: /* CMTST, CMEQ */ unallocated_encoding(s); @@ -10970,20 +10974,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x0c: /* SMAX, UMAX */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_smax, size); - } - return; - case 0x0d: /* SMIN, UMIN */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umin, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_smin, size); - } - return; case 0xe: /* SABD, UABD */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uabd, size); From 5ea1b93ef7476acf3781d717792aad02468d6d41 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:38 -0700 Subject: [PATCH 1059/2884] target/arm: Convert SABA, SABD, UABA, UABD to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-28-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 22 ++++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index bc98963bc5..07b604ec30 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -922,6 +922,10 @@ SMAX_v 0.00 1110 ..1 ..... 01100 1 ..... ..... @qrrr_e UMAX_v 0.10 1110 ..1 ..... 01100 1 ..... ..... @qrrr_e SMIN_v 0.00 1110 ..1 ..... 01101 1 ..... ..... @qrrr_e UMIN_v 0.10 1110 ..1 ..... 01101 1 ..... ..... @qrrr_e +SABD_v 0.00 1110 ..1 ..... 01110 1 ..... ..... @qrrr_e +UABD_v 0.10 1110 ..1 ..... 01110 1 ..... ..... @qrrr_e +SABA_v 0.00 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e +UABA_v 0.10 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index db6f59df17..61afbc434f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5464,6 +5464,10 @@ TRANS(SMAX_v, do_gvec_fn3_no64, a, tcg_gen_gvec_smax) TRANS(UMAX_v, do_gvec_fn3_no64, a, tcg_gen_gvec_umax) TRANS(SMIN_v, do_gvec_fn3_no64, a, tcg_gen_gvec_smin) TRANS(UMIN_v, do_gvec_fn3_no64, a, tcg_gen_gvec_umin) +TRANS(SABA_v, do_gvec_fn3_no64, a, gen_gvec_saba) +TRANS(UABA_v, do_gvec_fn3_no64, a, gen_gvec_uaba) +TRANS(SABD_v, do_gvec_fn3_no64, a, gen_gvec_sabd) +TRANS(UABD_v, do_gvec_fn3_no64, a, gen_gvec_uabd) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -10929,8 +10933,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) return; } /* fall through */ - case 0xe: /* SABD, UABD */ - case 0xf: /* SABA, UABA */ case 0x12: /* MLA, MLS */ if (size == 3) { unallocated_encoding(s); @@ -10963,6 +10965,8 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x0b: /* SQRSHL, UQRSHL */ case 0x0c: /* SMAX, UMAX */ case 0x0d: /* SMIN, UMIN */ + case 0x0e: /* SABD, UABD */ + case 0x0f: /* SABA, UABA */ case 0x10: /* ADD, SUB */ case 0x11: /* CMTST, CMEQ */ unallocated_encoding(s); @@ -10974,20 +10978,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0xe: /* SABD, UABD */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uabd, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sabd, size); - } - return; - case 0xf: /* SABA, UABA */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uaba, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_saba, size); - } - return; case 0x13: /* MUL, PMUL */ if (!u) { /* MUL */ gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_mul, size); From b73c7b174028369ac8b226b55df0df19e414b67e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:39 -0700 Subject: [PATCH 1060/2884] target/arm: Convert MUL, PMUL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-29-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 ++++ target/arm/tcg/translate-a64.c | 51 +++++++++++++--------------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 07b604ec30..3ea0643370 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -926,6 +926,8 @@ SABD_v 0.00 1110 ..1 ..... 01110 1 ..... ..... @qrrr_e UABD_v 0.10 1110 ..1 ..... 01110 1 ..... ..... @qrrr_e SABA_v 0.00 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e UABA_v 0.10 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e +MUL_v 0.00 1110 ..1 ..... 10011 1 ..... ..... @qrrr_e +PMUL_v 0.10 1110 001 ..... 10011 1 ..... ..... @qrrr_b ### Advanced SIMD scalar x indexed element @@ -967,3 +969,6 @@ FMLAL_vi 0.00 1111 10 .. .... 0000 . 0 ..... ..... @qrrx_h FMLSL_vi 0.00 1111 10 .. .... 0100 . 0 ..... ..... @qrrx_h FMLAL2_vi 0.10 1111 10 .. .... 1000 . 0 ..... ..... @qrrx_h FMLSL2_vi 0.10 1111 10 .. .... 1100 . 0 ..... ..... @qrrx_h + +MUL_vi 0.00 1111 01 .. .... 1000 . 0 ..... ..... @qrrx_h +MUL_vi 0.00 1111 10 . ..... 1000 . 0 ..... ..... @qrrx_s diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 61afbc434f..1909d1426c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5468,6 +5468,8 @@ TRANS(SABA_v, do_gvec_fn3_no64, a, gen_gvec_saba) TRANS(UABA_v, do_gvec_fn3_no64, a, gen_gvec_uaba) TRANS(SABD_v, do_gvec_fn3_no64, a, gen_gvec_sabd) TRANS(UABD_v, do_gvec_fn3_no64, a, gen_gvec_uabd) +TRANS(MUL_v, do_gvec_fn3_no64, a, tcg_gen_gvec_mul) +TRANS(PMUL_v, do_gvec_op3_ool, a, 0, gen_helper_gvec_pmul_b) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -5694,6 +5696,22 @@ TRANS_FEAT(FMLSL_vi, aa64_fhm, do_fmlal_idx, a, true, false) TRANS_FEAT(FMLAL2_vi, aa64_fhm, do_fmlal_idx, a, false, true) TRANS_FEAT(FMLSL2_vi, aa64_fhm, do_fmlal_idx, a, true, true) +static bool do_int3_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_3 * const fns[2]) +{ + assert(a->esz == MO_16 || a->esz == MO_32); + if (fp_access_check(s)) { + gen_gvec_op3_ool(s, a->q, a->rd, a->rn, a->rm, a->idx, fns[a->esz - 1]); + } + return true; +} + +static gen_helper_gvec_3 * const f_vector_idx_mul[2] = { + gen_helper_gvec_mul_idx_h, + gen_helper_gvec_mul_idx_s, +}; +TRANS(MUL_vi, do_int3_vector_idx, a, f_vector_idx_mul) + /* * Advanced SIMD scalar pairwise */ @@ -10927,12 +10945,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) int rd = extract32(insn, 0, 5); switch (opcode) { - case 0x13: /* MUL, PMUL */ - if (u && size != 0) { - unallocated_encoding(s); - return; - } - /* fall through */ case 0x12: /* MLA, MLS */ if (size == 3) { unallocated_encoding(s); @@ -10969,6 +10981,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x0f: /* SABA, UABA */ case 0x10: /* ADD, SUB */ case 0x11: /* CMTST, CMEQ */ + case 0x13: /* MUL, PMUL */ unallocated_encoding(s); return; } @@ -10978,13 +10991,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x13: /* MUL, PMUL */ - if (!u) { /* MUL */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_mul, size); - } else { /* PMUL */ - gen_gvec_op3_ool(s, is_q, rd, rn, rm, 0, gen_helper_gvec_pmul_b); - } - return; case 0x12: /* MLA, MLS */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_mls, size); @@ -12198,7 +12204,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) TCGv_ptr fpst; switch (16 * u + opcode) { - case 0x08: /* MUL */ case 0x10: /* MLA */ case 0x14: /* MLS */ if (is_scalar) { @@ -12285,6 +12290,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x01: /* FMLA */ case 0x04: /* FMLSL */ case 0x05: /* FMLS */ + case 0x08: /* MUL */ case 0x09: /* FMUL */ case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ @@ -12407,22 +12413,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } return; - case 0x08: /* MUL */ - if (!is_long && !is_scalar) { - static gen_helper_gvec_3 * const fns[3] = { - gen_helper_gvec_mul_idx_h, - gen_helper_gvec_mul_idx_s, - gen_helper_gvec_mul_idx_d, - }; - tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - is_q ? 16 : 8, vec_full_reg_size(s), - index, fns[size - 1]); - return; - } - break; - case 0x10: /* MLA */ if (!is_long && !is_scalar) { static gen_helper_gvec_4 * const fns[3] = { @@ -12491,7 +12481,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op, rn, pass, is_scalar ? size : MO_32); switch (16 * u + opcode) { - case 0x08: /* MUL */ case 0x10: /* MLA */ case 0x14: /* MLS */ { From 8db93dcd3def0ca3226c924f549988b33a19371a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:40 -0700 Subject: [PATCH 1061/2884] target/arm: Convert MLA, MLS to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 8 ++++ target/arm/tcg/translate-a64.c | 77 ++++++++++------------------------ 2 files changed, 31 insertions(+), 54 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 3ea0643370..2dea68a0a9 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -928,6 +928,8 @@ SABA_v 0.00 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e UABA_v 0.10 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e MUL_v 0.00 1110 ..1 ..... 10011 1 ..... ..... @qrrr_e PMUL_v 0.10 1110 001 ..... 10011 1 ..... ..... @qrrr_b +MLA_v 0.00 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e +MLS_v 0.10 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element @@ -972,3 +974,9 @@ FMLSL2_vi 0.10 1111 10 .. .... 1100 . 0 ..... ..... @qrrx_h MUL_vi 0.00 1111 01 .. .... 1000 . 0 ..... ..... @qrrx_h MUL_vi 0.00 1111 10 . ..... 1000 . 0 ..... ..... @qrrx_s + +MLA_vi 0.10 1111 01 .. .... 0000 . 0 ..... ..... @qrrx_h +MLA_vi 0.10 1111 10 . ..... 0000 . 0 ..... ..... @qrrx_s + +MLS_vi 0.10 1111 01 .. .... 0100 . 0 ..... ..... @qrrx_h +MLS_vi 0.10 1111 10 . ..... 0100 . 0 ..... ..... @qrrx_s diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1909d1426c..c4601cde2f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5470,6 +5470,8 @@ TRANS(SABD_v, do_gvec_fn3_no64, a, gen_gvec_sabd) TRANS(UABD_v, do_gvec_fn3_no64, a, gen_gvec_uabd) TRANS(MUL_v, do_gvec_fn3_no64, a, tcg_gen_gvec_mul) TRANS(PMUL_v, do_gvec_op3_ool, a, 0, gen_helper_gvec_pmul_b) +TRANS(MLA_v, do_gvec_fn3_no64, a, gen_gvec_mla) +TRANS(MLS_v, do_gvec_fn3_no64, a, gen_gvec_mls) static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) { @@ -5712,6 +5714,24 @@ static gen_helper_gvec_3 * const f_vector_idx_mul[2] = { }; TRANS(MUL_vi, do_int3_vector_idx, a, f_vector_idx_mul) +static bool do_mla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool sub) +{ + static gen_helper_gvec_4 * const fns[2][2] = { + { gen_helper_gvec_mla_idx_h, gen_helper_gvec_mls_idx_h }, + { gen_helper_gvec_mla_idx_s, gen_helper_gvec_mls_idx_s }, + }; + + assert(a->esz == MO_16 || a->esz == MO_32); + if (fp_access_check(s)) { + gen_gvec_op4_ool(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->idx, fns[a->esz - 1][sub]); + } + return true; +} + +TRANS(MLA_vi, do_mla_vector_idx, a, false) +TRANS(MLS_vi, do_mla_vector_idx, a, true) + /* * Advanced SIMD scalar pairwise */ @@ -10945,12 +10965,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) int rd = extract32(insn, 0, 5); switch (opcode) { - case 0x12: /* MLA, MLS */ - if (size == 3) { - unallocated_encoding(s); - return; - } - break; case 0x16: /* SQDMULH, SQRDMULH */ if (size == 0 || size == 3) { unallocated_encoding(s); @@ -10981,6 +10995,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x0f: /* SABA, UABA */ case 0x10: /* ADD, SUB */ case 0x11: /* CMTST, CMEQ */ + case 0x12: /* MLA, MLS */ case 0x13: /* MUL, PMUL */ unallocated_encoding(s); return; @@ -10991,13 +11006,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x12: /* MLA, MLS */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_mls, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_mla, size); - } - return; case 0x16: /* SQDMULH, SQRDMULH */ { static gen_helper_gvec_3_ptr * const fns[2][2] = { @@ -12204,13 +12212,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) TCGv_ptr fpst; switch (16 * u + opcode) { - case 0x10: /* MLA */ - case 0x14: /* MLS */ - if (is_scalar) { - unallocated_encoding(s); - return; - } - break; case 0x02: /* SMLAL, SMLAL2 */ case 0x12: /* UMLAL, UMLAL2 */ case 0x06: /* SMLSL, SMLSL2 */ @@ -12292,6 +12293,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x05: /* FMLS */ case 0x08: /* MUL */ case 0x09: /* FMUL */ + case 0x10: /* MLA */ + case 0x14: /* MLS */ case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ case 0x1c: /* FMLSL2 */ @@ -12412,40 +12415,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) : gen_helper_gvec_fcmlah_idx); } return; - - case 0x10: /* MLA */ - if (!is_long && !is_scalar) { - static gen_helper_gvec_4 * const fns[3] = { - gen_helper_gvec_mla_idx_h, - gen_helper_gvec_mla_idx_s, - gen_helper_gvec_mla_idx_d, - }; - tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, rd), - is_q ? 16 : 8, vec_full_reg_size(s), - index, fns[size - 1]); - return; - } - break; - - case 0x14: /* MLS */ - if (!is_long && !is_scalar) { - static gen_helper_gvec_4 * const fns[3] = { - gen_helper_gvec_mls_idx_h, - gen_helper_gvec_mls_idx_s, - gen_helper_gvec_mls_idx_d, - }; - tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, rd), - is_q ? 16 : 8, vec_full_reg_size(s), - index, fns[size - 1]); - return; - } - break; } if (size == 3) { From 8f81dced5d9ce4bb537be4cf17f615e6ddd6b625 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:41 -0700 Subject: [PATCH 1062/2884] target/arm: Tidy SQDMULH, SQRDMULH (vector) We already have a gvec helper for the operations, but we aren't using it on the aa32 neon side. Create a unified expander for use by both aa32 and aa64 translators. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-31-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec.c | 20 ++++++++++++++++++++ target/arm/tcg/translate-a64.c | 23 ++++------------------- target/arm/tcg/translate-neon.c | 23 +++-------------------- target/arm/tcg/translate.h | 4 ++++ 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c index 119826bf28..56a1dc1f75 100644 --- a/target/arm/tcg/gengvec.c +++ b/target/arm/tcg/gengvec.c @@ -35,6 +35,26 @@ static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, opr_sz, max_sz, 0, fn); } +void gen_gvec_sqdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_neon_sqdmulh_h, gen_helper_neon_sqdmulh_s + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +void gen_gvec_sqrdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_neon_sqrdmulh_h, gen_helper_neon_sqrdmulh_s + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) { diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c4601cde2f..c673b95ec7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -724,19 +724,6 @@ static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, is_q ? 16 : 8, vec_full_reg_size(s), data, fn); } -/* Expand a 3-operand + qc + operation using an out-of-line helper. */ -static void gen_gvec_op3_qc(DisasContext *s, bool is_q, int rd, int rn, - int rm, gen_helper_gvec_3_ptr *fn) -{ - TCGv_ptr qc_ptr = tcg_temp_new_ptr(); - - tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc)); - tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), qc_ptr, - is_q ? 16 : 8, vec_full_reg_size(s), 0, fn); -} - /* Expand a 4-operand operation using an out-of-line helper. */ static void gen_gvec_op4_ool(DisasContext *s, bool is_q, int rd, int rn, int rm, int ra, int data, gen_helper_gvec_4 *fn) @@ -11007,12 +10994,10 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) switch (opcode) { case 0x16: /* SQDMULH, SQRDMULH */ - { - static gen_helper_gvec_3_ptr * const fns[2][2] = { - { gen_helper_neon_sqdmulh_h, gen_helper_neon_sqrdmulh_h }, - { gen_helper_neon_sqdmulh_s, gen_helper_neon_sqrdmulh_s }, - }; - gen_gvec_op3_qc(s, is_q, rd, rn, rm, fns[size - 1][u]); + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqrdmulh_qc, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqdmulh_qc, size); } return; } diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index f9a8753906..915c9e56db 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -937,28 +937,11 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) } #define DO_3SAME_VQDMULH(INSN, FUNC) \ - WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \ - WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##_s32); \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 ops[2] = { \ - { .fni4 = gen_##INSN##_tramp16 }, \ - { .fni4 = gen_##INSN##_tramp32 }, \ - }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &ops[vece - 1]); \ - } \ static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size != 1 && a->size != 2) { \ - return false; \ - } \ - return do_3same(s, a, gen_##INSN##_3s); \ - } + { return a->size >= 1 && a->size <= 2 && do_3same(s, a, FUNC); } -DO_3SAME_VQDMULH(VQDMULH, qdmulh) -DO_3SAME_VQDMULH(VQRDMULH, qrdmulh) +DO_3SAME_VQDMULH(VQDMULH, gen_gvec_sqdmulh_qc) +DO_3SAME_VQDMULH(VQRDMULH, gen_gvec_sqrdmulh_qc) #define WRAP_FP_GVEC(WRAPNAME, FPST, FUNC) \ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 3b1e68b779..aba21f730f 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -539,6 +539,10 @@ void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sqdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sqrdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, From f80701cb44d334766a7c0e3b560044c1e5c52cc7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:42 -0700 Subject: [PATCH 1063/2884] target/arm: Convert SQDMULH, SQRDMULH to decodetree These are the last instructions within disas_simd_three_reg_same and disas_simd_scalar_three_reg_same, so remove them. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240528203044.612851-32-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 10 ++ target/arm/tcg/a64.decode | 18 +++ target/arm/tcg/translate-a64.c | 276 ++++++++++----------------------- target/arm/tcg/vec_helper.c | 64 ++++++++ 4 files changed, 172 insertions(+), 196 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 85f9302563..24feecee9b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -968,6 +968,16 @@ DEF_HELPER_FLAGS_5(neon_sqrdmulh_h, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(neon_sqrdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve2_sqdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 2dea68a0a9..f7f897f9fc 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -774,6 +774,9 @@ CMHS_s 0111 1110 111 ..... 00111 1 ..... ..... @rrr_d CMTST_s 0101 1110 111 ..... 10001 1 ..... ..... @rrr_d CMEQ_s 0111 1110 111 ..... 10001 1 ..... ..... @rrr_d +SQDMULH_s 0101 1110 ..1 ..... 10110 1 ..... ..... @rrr_e +SQRDMULH_s 0111 1110 ..1 ..... 10110 1 ..... ..... @rrr_e + ### Advanced SIMD scalar pairwise FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h @@ -931,6 +934,9 @@ PMUL_v 0.10 1110 001 ..... 10011 1 ..... ..... @qrrr_b MLA_v 0.00 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e MLS_v 0.10 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e +SQDMULH_v 0.00 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e +SQRDMULH_v 0.10 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h @@ -949,6 +955,12 @@ FMULX_si 0111 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h FMULX_si 0111 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s FMULX_si 0111 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d +SQDMULH_si 0101 1111 01 .. .... 1100 . 0 ..... ..... @rrx_h +SQDMULH_si 0101 1111 10 .. .... 1100 . 0 ..... ..... @rrx_s + +SQRDMULH_si 0101 1111 01 .. .... 1101 . 0 ..... ..... @rrx_h +SQRDMULH_si 0101 1111 10 . ..... 1101 . 0 ..... ..... @rrx_s + ### Advanced SIMD vector x indexed element FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h @@ -980,3 +992,9 @@ MLA_vi 0.10 1111 10 . ..... 0000 . 0 ..... ..... @qrrx_s MLS_vi 0.10 1111 01 .. .... 0100 . 0 ..... ..... @qrrx_h MLS_vi 0.10 1111 10 . ..... 0100 . 0 ..... ..... @qrrx_s + +SQDMULH_vi 0.00 1111 01 .. .... 1100 . 0 ..... ..... @qrrx_h +SQDMULH_vi 0.00 1111 10 . ..... 1100 . 0 ..... ..... @qrrx_s + +SQRDMULH_vi 0.00 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h +SQRDMULH_vi 0.00 1111 10 . ..... 1101 . 0 ..... ..... @qrrx_s diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c673b95ec7..14226c56cf 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1350,6 +1350,14 @@ static bool do_gvec_fn3_no64(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) return true; } +static bool do_gvec_fn3_no8_no64(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) +{ + if (a->esz == MO_8) { + return false; + } + return do_gvec_fn3_no64(s, a, fn); +} + static bool do_gvec_fn4(DisasContext *s, arg_qrrrr_e *a, GVecGen4Fn *fn) { if (!a->q && a->esz == MO_64) { @@ -5167,6 +5175,25 @@ static const ENVScalar2 f_scalar_uqrshl = { }; TRANS(UQRSHL_s, do_env_scalar2, a, &f_scalar_uqrshl) +static bool do_env_scalar2_hs(DisasContext *s, arg_rrr_e *a, + const ENVScalar2 *f) +{ + if (a->esz == MO_16 || a->esz == MO_32) { + return do_env_scalar2(s, a, f); + } + return false; +} + +static const ENVScalar2 f_scalar_sqdmulh = { + { NULL, gen_helper_neon_qdmulh_s16, gen_helper_neon_qdmulh_s32 } +}; +TRANS(SQDMULH_s, do_env_scalar2_hs, a, &f_scalar_sqdmulh) + +static const ENVScalar2 f_scalar_sqrdmulh = { + { NULL, gen_helper_neon_qrdmulh_s16, gen_helper_neon_qrdmulh_s32 } +}; +TRANS(SQRDMULH_s, do_env_scalar2_hs, a, &f_scalar_sqrdmulh) + static bool do_cmop_d(DisasContext *s, arg_rrr_e *a, TCGCond cond) { if (fp_access_check(s)) { @@ -5482,6 +5509,9 @@ TRANS(CMHS_v, do_cmop_v, a, TCG_COND_GEU) TRANS(CMEQ_v, do_cmop_v, a, TCG_COND_EQ) TRANS(CMTST_v, do_gvec_fn3, a, gen_gvec_cmtst) +TRANS(SQDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqdmulh_qc) +TRANS(SQRDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmulh_qc) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5589,6 +5619,27 @@ static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) TRANS(FMLA_si, do_fmla_scalar_idx, a, false) TRANS(FMLS_si, do_fmla_scalar_idx, a, true) +static bool do_env_scalar2_idx_hs(DisasContext *s, arg_rrx_e *a, + const ENVScalar2 *f) +{ + if (a->esz < MO_16 || a->esz > MO_32) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, a->idx, a->esz); + f->gen_bhs[a->esz](t0, tcg_env, t0, t1); + write_fp_sreg(s, a->rd, t0); + } + return true; +} + +TRANS(SQDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqdmulh) +TRANS(SQRDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqrdmulh) + static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5719,6 +5770,33 @@ static bool do_mla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool sub) TRANS(MLA_vi, do_mla_vector_idx, a, false) TRANS(MLS_vi, do_mla_vector_idx, a, true) +static bool do_int3_qc_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4 * const fns[2]) +{ + assert(a->esz == MO_16 || a->esz == MO_32); + if (fp_access_check(s)) { + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + offsetof(CPUARMState, vfp.qc), + a->q ? 16 : 8, vec_full_reg_size(s), + a->idx, fns[a->esz - 1]); + } + return true; +} + +static gen_helper_gvec_4 * const f_vector_idx_sqdmulh[2] = { + gen_helper_neon_sqdmulh_idx_h, + gen_helper_neon_sqdmulh_idx_s, +}; +TRANS(SQDMULH_vi, do_int3_qc_vector_idx, a, f_vector_idx_sqdmulh) + +static gen_helper_gvec_4 * const f_vector_idx_sqrdmulh[2] = { + gen_helper_neon_sqrdmulh_idx_h, + gen_helper_neon_sqrdmulh_idx_s, +}; +TRANS(SQRDMULH_vi, do_int3_qc_vector_idx, a, f_vector_idx_sqrdmulh) + /* * Advanced SIMD scalar pairwise */ @@ -9500,109 +9578,6 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) } } -/* AdvSIMD scalar three same - * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0 - * +-----+---+-----------+------+---+------+--------+---+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd | - * +-----+---+-----------+------+---+------+--------+---+------+------+ - */ -static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - TCGv_i64 tcg_rd; - - switch (opcode) { - case 0x16: /* SQDMULH, SQRDMULH (vector) */ - if (size != 1 && size != 2) { - unallocated_encoding(s); - return; - } - break; - default: - case 0x1: /* SQADD, UQADD */ - case 0x5: /* SQSUB, UQSUB */ - case 0x6: /* CMGT, CMHI */ - case 0x7: /* CMGE, CMHS */ - case 0x8: /* SSHL, USHL */ - case 0x9: /* SQSHL, UQSHL */ - case 0xa: /* SRSHL, URSHL */ - case 0xb: /* SQRSHL, UQRSHL */ - case 0x10: /* ADD, SUB (vector) */ - case 0x11: /* CMTST, CMEQ */ - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_rd = tcg_temp_new_i64(); - - if (size == 3) { - g_assert_not_reached(); - } else { - /* Do a single operation on the lowest element in the vector. - * We use the standard Neon helpers and rely on 0 OP 0 == 0 with - * no side effects for all these operations. - * OPTME: special-purpose helpers would avoid doing some - * unnecessary work in the helper for the 8 and 16 bit cases. - */ - NeonGenTwoOpEnvFn *genenvfn = NULL; - void (*genfn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp) = NULL; - - switch (opcode) { - case 0x16: /* SQDMULH, SQRDMULH */ - { - static NeonGenTwoOpEnvFn * const fns[2][2] = { - { gen_helper_neon_qdmulh_s16, gen_helper_neon_qrdmulh_s16 }, - { gen_helper_neon_qdmulh_s32, gen_helper_neon_qrdmulh_s32 }, - }; - assert(size == 1 || size == 2); - genenvfn = fns[size - 1][u]; - break; - } - default: - case 0x1: /* SQADD, UQADD */ - case 0x5: /* SQSUB, UQSUB */ - case 0x9: /* SQSHL, UQSHL */ - case 0xb: /* SQRSHL, UQRSHL */ - g_assert_not_reached(); - } - - if (genenvfn) { - TCGv_i32 tcg_rn = tcg_temp_new_i32(); - TCGv_i32 tcg_rm = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_rn, rn, 0, size); - read_vec_element_i32(s, tcg_rm, rm, 0, size); - genenvfn(tcg_rn, tcg_env, tcg_rn, tcg_rm); - tcg_gen_extu_i32_i64(tcg_rd, tcg_rn); - } else { - TCGv_i64 tcg_rn = tcg_temp_new_i64(); - TCGv_i64 tcg_rm = tcg_temp_new_i64(); - TCGv_i64 qc = tcg_temp_new_i64(); - - read_vec_element(s, tcg_rn, rn, 0, size | (u ? 0 : MO_SIGN)); - read_vec_element(s, tcg_rm, rm, 0, size | (u ? 0 : MO_SIGN)); - tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - genfn(tcg_rd, qc, tcg_rn, tcg_rm, size); - tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); - if (!u) { - /* Truncate signed 64-bit result for writeback. */ - tcg_gen_ext_i64(tcg_rd, tcg_rd, size); - } - } - } - - write_fp_dreg(s, rd, tcg_rd); -} - /* AdvSIMD scalar three same extra * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 * +-----+---+-----------+------+---+------+---+--------+---+----+----+ @@ -10940,94 +10915,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) } } -/* Integer op subgroup of C3.6.16. */ -static void disas_simd_3same_int(DisasContext *s, uint32_t insn) -{ - int is_q = extract32(insn, 30, 1); - int u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 11, 5); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - switch (opcode) { - case 0x16: /* SQDMULH, SQRDMULH */ - if (size == 0 || size == 3) { - unallocated_encoding(s); - return; - } - break; - default: - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - - case 0x0: /* SHADD, UHADD */ - case 0x01: /* SQADD, UQADD */ - case 0x02: /* SRHADD, URHADD */ - case 0x04: /* SHSUB, UHSUB */ - case 0x05: /* SQSUB, UQSUB */ - case 0x06: /* CMGT, CMHI */ - case 0x07: /* CMGE, CMHS */ - case 0x08: /* SSHL, USHL */ - case 0x09: /* SQSHL, UQSHL */ - case 0x0a: /* SRSHL, URSHL */ - case 0x0b: /* SQRSHL, UQRSHL */ - case 0x0c: /* SMAX, UMAX */ - case 0x0d: /* SMIN, UMIN */ - case 0x0e: /* SABD, UABD */ - case 0x0f: /* SABA, UABA */ - case 0x10: /* ADD, SUB */ - case 0x11: /* CMTST, CMEQ */ - case 0x12: /* MLA, MLS */ - case 0x13: /* MUL, PMUL */ - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0x16: /* SQDMULH, SQRDMULH */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqrdmulh_qc, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqdmulh_qc, size); - } - return; - } - g_assert_not_reached(); -} - -/* AdvSIMD three same - * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+--------+---+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd | - * +---+---+---+-----------+------+---+------+--------+---+------+------+ - */ -static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 11, 5); - - switch (opcode) { - default: - disas_simd_3same_int(s, insn); - break; - case 0x3: /* logic ops */ - case 0x14: /* SMAXP, UMAXP */ - case 0x15: /* SMINP, UMINP */ - case 0x17: /* ADDP */ - case 0x18 ... 0x31: /* floating point ops */ - unallocated_encoding(s); - break; - } -} - /* AdvSIMD three same extra * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ @@ -12214,9 +12101,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0b: /* SQDMULL, SQDMULL2 */ is_long = true; break; - case 0x0c: /* SQDMULH */ - case 0x0d: /* SQRDMULH */ - break; case 0x1d: /* SQRDMLAH */ case 0x1f: /* SQRDMLSH */ if (!dc_isar_feature(aa64_rdm, s)) { @@ -12278,6 +12162,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x05: /* FMLS */ case 0x08: /* MUL */ case 0x09: /* FMUL */ + case 0x0c: /* SQDMULH */ + case 0x0d: /* SQRDMULH */ case 0x10: /* MLA */ case 0x14: /* MLS */ case 0x18: /* FMLAL2 */ @@ -12683,7 +12569,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) */ static const AArch64DecodeTable data_proc_simd[] = { /* pattern , mask , fn */ - { 0x0e200400, 0x9f200400, disas_simd_three_reg_same }, { 0x0e008400, 0x9f208400, disas_simd_three_reg_same_extra }, { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, @@ -12695,7 +12580,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x0e000000, 0xbf208c00, disas_simd_tb }, { 0x0e000800, 0xbf208c00, disas_simd_zip_trn }, { 0x2e000000, 0xbf208400, disas_simd_ext }, - { 0x5e200400, 0xdf200400, disas_simd_scalar_three_reg_same }, { 0x5e008400, 0xdf208400, disas_simd_scalar_three_reg_same_extra }, { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index d8e96386be..b05922b425 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -311,6 +311,38 @@ void HELPER(neon_sqrdmulh_h)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(neon_sqdmulh_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + + for (i = 0; i < opr_sz / 2; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < 16 / 2; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, false, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmulh_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + + for (i = 0; i < opr_sz / 2; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < 16 / 2; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(sve2_sqrdmlah_h)(void *vd, void *vn, void *vm, void *va, uint32_t desc) { @@ -474,6 +506,38 @@ void HELPER(neon_sqrdmulh_s)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(neon_sqdmulh_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + + for (i = 0; i < opr_sz / 4; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < 16 / 4; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, false, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmulh_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + + for (i = 0; i < opr_sz / 4; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < 16 / 4; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(sve2_sqrdmlah_s)(void *vd, void *vn, void *vm, void *va, uint32_t desc) { From 44463b96d277e71ba85120f151e9d70886a9b458 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:43 -0700 Subject: [PATCH 1064/2884] target/arm: Convert FMADD, FMSUB, FNMADD, FNMSUB to decodetree These are the only instructions in the 3 source scalar class. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240528203044.612851-33-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 ++ target/arm/tcg/translate-a64.c | 231 ++++++++++++--------------------- 2 files changed, 93 insertions(+), 148 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f7f897f9fc..6f6cd805b7 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -32,6 +32,7 @@ &rr_e rd rn esz &rrr_e rd rn rm esz &rrx_e rd rn rm idx esz +&rrrr_e rd rn rm ra esz &qrr_e q rd rn esz &qrrr_e q rd rn rm esz &qrrx_e q rd rn rm idx esz @@ -998,3 +999,12 @@ SQDMULH_vi 0.00 1111 10 . ..... 1100 . 0 ..... ..... @qrrx_s SQRDMULH_vi 0.00 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h SQRDMULH_vi 0.00 1111 10 . ..... 1101 . 0 ..... ..... @qrrx_s + +# Floating-point data-processing (3 source) + +@rrrr_hsd .... .... .. . rm:5 . ra:5 rn:5 rd:5 &rrrr_e esz=%esz_hsd + +FMADD 0001 1111 .. 0 ..... 0 ..... ..... ..... @rrrr_hsd +FMSUB 0001 1111 .. 0 ..... 1 ..... ..... ..... @rrrr_hsd +FNMADD 0001 1111 .. 1 ..... 0 ..... ..... ..... @rrrr_hsd +FNMSUB 0001 1111 .. 1 ..... 1 ..... ..... ..... @rrrr_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 14226c56cf..78a2e6d692 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5866,6 +5866,88 @@ static bool trans_ADDP_s(DisasContext *s, arg_rr_e *a) return true; } +/* + * Floating-point data-processing (3 source) + */ + +static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) +{ + TCGv_ptr fpst; + + /* + * These are fused multiply-add. Note that doing the negations here + * as separate steps is correct: an input NaN should come out with + * its sign bit flipped if it is a negated-input. + */ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 tn = read_fp_dreg(s, a->rn); + TCGv_i64 tm = read_fp_dreg(s, a->rm); + TCGv_i64 ta = read_fp_dreg(s, a->ra); + + if (neg_a) { + gen_vfp_negd(ta, ta); + } + if (neg_n) { + gen_vfp_negd(tn, tn); + } + fpst = fpstatus_ptr(FPST_FPCR); + gen_helper_vfp_muladdd(ta, tn, tm, ta, fpst); + write_fp_dreg(s, a->rd, ta); + } + break; + + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 tn = read_fp_sreg(s, a->rn); + TCGv_i32 tm = read_fp_sreg(s, a->rm); + TCGv_i32 ta = read_fp_sreg(s, a->ra); + + if (neg_a) { + gen_vfp_negs(ta, ta); + } + if (neg_n) { + gen_vfp_negs(tn, tn); + } + fpst = fpstatus_ptr(FPST_FPCR); + gen_helper_vfp_muladds(ta, tn, tm, ta, fpst); + write_fp_sreg(s, a->rd, ta); + } + break; + + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 tn = read_fp_hreg(s, a->rn); + TCGv_i32 tm = read_fp_hreg(s, a->rm); + TCGv_i32 ta = read_fp_hreg(s, a->ra); + + if (neg_a) { + gen_vfp_negh(ta, ta); + } + if (neg_n) { + gen_vfp_negh(tn, tn); + } + fpst = fpstatus_ptr(FPST_FPCR_F16); + gen_helper_advsimd_muladdh(ta, tn, tm, ta, fpst); + write_fp_sreg(s, a->rd, ta); + } + break; + + default: + return false; + } + return true; +} + +TRANS(FMADD, do_fmadd, a, false, false) +TRANS(FNMADD, do_fmadd, a, true, true) +TRANS(FMSUB, do_fmadd, a, false, true) +TRANS(FNMSUB, do_fmadd, a, true, false) + /* Shift a TCGv src by TCGv shift_amount, put result in dst. * Note that it is the caller's responsibility to ensure that the * shift amount is in range (ie 0..31 or 0..63) and provide the ARM @@ -7665,152 +7747,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) } } -/* Floating-point data-processing (3 source) - single precision */ -static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1, - int rd, int rn, int rm, int ra) -{ - TCGv_i32 tcg_op1, tcg_op2, tcg_op3; - TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - tcg_op1 = read_fp_sreg(s, rn); - tcg_op2 = read_fp_sreg(s, rm); - tcg_op3 = read_fp_sreg(s, ra); - - /* These are fused multiply-add, and must be done as one - * floating point operation with no rounding between the - * multiplication and addition steps. - * NB that doing the negations here as separate steps is - * correct : an input NaN should come out with its sign bit - * flipped if it is a negated-input. - */ - if (o1 == true) { - gen_vfp_negs(tcg_op3, tcg_op3); - } - - if (o0 != o1) { - gen_vfp_negs(tcg_op1, tcg_op1); - } - - gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); - - write_fp_sreg(s, rd, tcg_res); -} - -/* Floating-point data-processing (3 source) - double precision */ -static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, - int rd, int rn, int rm, int ra) -{ - TCGv_i64 tcg_op1, tcg_op2, tcg_op3; - TCGv_i64 tcg_res = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - tcg_op1 = read_fp_dreg(s, rn); - tcg_op2 = read_fp_dreg(s, rm); - tcg_op3 = read_fp_dreg(s, ra); - - /* These are fused multiply-add, and must be done as one - * floating point operation with no rounding between the - * multiplication and addition steps. - * NB that doing the negations here as separate steps is - * correct : an input NaN should come out with its sign bit - * flipped if it is a negated-input. - */ - if (o1 == true) { - gen_vfp_negd(tcg_op3, tcg_op3); - } - - if (o0 != o1) { - gen_vfp_negd(tcg_op1, tcg_op1); - } - - gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); - - write_fp_dreg(s, rd, tcg_res); -} - -/* Floating-point data-processing (3 source) - half precision */ -static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1, - int rd, int rn, int rm, int ra) -{ - TCGv_i32 tcg_op1, tcg_op2, tcg_op3; - TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR_F16); - - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - tcg_op3 = read_fp_hreg(s, ra); - - /* These are fused multiply-add, and must be done as one - * floating point operation with no rounding between the - * multiplication and addition steps. - * NB that doing the negations here as separate steps is - * correct : an input NaN should come out with its sign bit - * flipped if it is a negated-input. - */ - if (o1 == true) { - tcg_gen_xori_i32(tcg_op3, tcg_op3, 0x8000); - } - - if (o0 != o1) { - tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); - } - - gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); - - write_fp_sreg(s, rd, tcg_res); -} - -/* Floating point data-processing (3 source) - * 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0 - * +---+---+---+-----------+------+----+------+----+------+------+------+ - * | M | 0 | S | 1 1 1 1 1 | type | o1 | Rm | o0 | Ra | Rn | Rd | - * +---+---+---+-----------+------+----+------+----+------+------+------+ - */ -static void disas_fp_3src(DisasContext *s, uint32_t insn) -{ - int mos = extract32(insn, 29, 3); - int type = extract32(insn, 22, 2); - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int ra = extract32(insn, 10, 5); - int rm = extract32(insn, 16, 5); - bool o0 = extract32(insn, 15, 1); - bool o1 = extract32(insn, 21, 1); - - if (mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - if (!fp_access_check(s)) { - return; - } - handle_fp_3src_single(s, o0, o1, rd, rn, rm, ra); - break; - case 1: - if (!fp_access_check(s)) { - return; - } - handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); - break; - case 3: - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_fp_3src_half(s, o0, o1, rd, rn, rm, ra); - break; - default: - unallocated_encoding(s); - } -} - /* Floating point immediate * 31 30 29 28 24 23 22 21 20 13 12 10 9 5 4 0 * +---+---+---+-----------+------+---+------------+-------+------+------+ @@ -8255,8 +8191,7 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) static void disas_data_proc_fp(DisasContext *s, uint32_t insn) { if (extract32(insn, 24, 1)) { - /* Floating point data-processing (3 source) */ - disas_fp_3src(s, insn); + unallocated_encoding(s); /* in decodetree */ } else if (extract32(insn, 21, 1) == 0) { /* Floating point to fixed point conversions */ disas_fp_fixed_conv(s, insn); From fa31b7e1681cfe20917f172b537a3a0988efc583 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 28 May 2024 13:30:44 -0700 Subject: [PATCH 1065/2884] target/arm: Convert FCSEL to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240528203044.612851-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++ target/arm/tcg/translate-a64.c | 108 ++++++++++++++------------------- 2 files changed, 49 insertions(+), 63 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 6f6cd805b7..5dadbc74d7 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1000,6 +1000,10 @@ SQDMULH_vi 0.00 1111 10 . ..... 1100 . 0 ..... ..... @qrrx_s SQRDMULH_vi 0.00 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h SQRDMULH_vi 0.00 1111 10 . ..... 1101 . 0 ..... ..... @qrrx_s +# Floating-point conditional select + +FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd + # Floating-point data-processing (3 source) @rrrr_hsd .... .... .. . rm:5 . ra:5 rn:5 rd:5 &rrrr_e esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 78a2e6d692..f1dea5834c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5866,6 +5866,50 @@ static bool trans_ADDP_s(DisasContext *s, arg_rr_e *a) return true; } +/* + * Floating-point conditional select + */ + +static bool trans_FCSEL(DisasContext *s, arg_FCSEL *a) +{ + TCGv_i64 t_true, t_false; + DisasCompare64 c; + + switch (a->esz) { + case MO_32: + case MO_64: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + return false; + } + + if (!fp_access_check(s)) { + return true; + } + + /* Zero extend sreg & hreg inputs to 64 bits now. */ + t_true = tcg_temp_new_i64(); + t_false = tcg_temp_new_i64(); + read_vec_element(s, t_true, a->rn, 0, a->esz); + read_vec_element(s, t_false, a->rm, 0, a->esz); + + a64_test_cc(&c, a->cond); + tcg_gen_movcond_i64(c.cond, t_true, c.value, tcg_constant_i64(0), + t_true, t_false); + + /* + * Note that sregs & hregs write back zeros to the high bits, + * and we've already done the zero-extension. + */ + write_fp_dreg(s, a->rd, t_true); + return true; +} + /* * Floating-point data-processing (3 source) */ @@ -7332,68 +7376,6 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) } } -/* Floating point conditional select - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+------+-----+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 1 1 | Rn | Rd | - * +---+---+---+-----------+------+---+------+------+-----+------+------+ - */ -static void disas_fp_csel(DisasContext *s, uint32_t insn) -{ - unsigned int mos, type, rm, cond, rn, rd; - TCGv_i64 t_true, t_false; - DisasCompare64 c; - MemOp sz; - - mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); - rm = extract32(insn, 16, 5); - cond = extract32(insn, 12, 4); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - sz = MO_32; - break; - case 1: - sz = MO_64; - break; - case 3: - sz = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - /* Zero extend sreg & hreg inputs to 64 bits now. */ - t_true = tcg_temp_new_i64(); - t_false = tcg_temp_new_i64(); - read_vec_element(s, t_true, rn, 0, sz); - read_vec_element(s, t_false, rm, 0, sz); - - a64_test_cc(&c, cond); - tcg_gen_movcond_i64(c.cond, t_true, c.value, tcg_constant_i64(0), - t_true, t_false); - - /* Note that sregs & hregs write back zeros to the high bits, - and we've already done the zero-extension. */ - write_fp_dreg(s, rd, t_true); -} - /* Floating-point data-processing (1 source) - half precision */ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) { @@ -8207,7 +8189,7 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn) break; case 3: /* Floating point conditional select */ - disas_fp_csel(s, insn); + unallocated_encoding(s); /* in decodetree */ break; case 0: switch (ctz32(extract32(insn, 12, 4))) { From daf9748ac002ec35258e5986b6257961fd04b565 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Sun, 26 May 2024 13:45:51 -0700 Subject: [PATCH 1066/2884] target/arm: Disable SVE extensions when SVE is disabled Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2304 Reported-by: Marcin Juszkiewicz Signed-off-by: Richard Henderson Signed-off-by: Marcin Juszkiewicz Message-id: 20240526204551.553282-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu64.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index c15d086049..862d2b92fa 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -109,7 +109,11 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * No explicit bits enabled, and no implicit bits from sve-max-vq. */ if (!cpu_isar_feature(aa64_sve, cpu)) { - /* SVE is disabled and so are all vector lengths. Good. */ + /* + * SVE is disabled and so are all vector lengths. Good. + * Disable all SVE extensions as well. + */ + cpu->isar.id_aa64zfr0 = 0; return; } From af3f5c4f8728c6d2ae0081fc38d66e9484cc391e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 May 2024 15:14:21 +0100 Subject: [PATCH 1067/2884] docs/system/target-arm: Re-alphabetize board list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The board list in target-arm.rst is supposed to be in alphabetical order by the title text of each file (which is not the same as alphabetical order by filename). A few items had got out of order; correct them. The entry for "Facebook Yosemite v3.5 Platform and CraterLake Server (fby35)" remains out-of-order, because this is not its own file but is currently part of the aspeed.rst file. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240520141421.1895138-1-peter.maydell@linaro.org --- docs/system/target-arm.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index c9d7c0dda7..870d30e350 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -86,16 +86,16 @@ undocumented; you can get a complete list by running arm/bananapi_m2u.rst arm/b-l475e-iot01a.rst arm/sabrelite + arm/highbank arm/digic arm/cubieboard arm/emcraft-sf2 - arm/highbank arm/musicpal arm/gumstix arm/mainstone arm/kzm - arm/nrf arm/nseries + arm/nrf arm/nuvoton arm/imx25-pdk arm/orangepi @@ -107,8 +107,8 @@ undocumented; you can get a complete list by running arm/stellaris arm/stm32 arm/virt - arm/xlnx-versal-virt arm/xenpvh + arm/xlnx-versal-virt Emulated CPU architecture support ================================= From 408b2b3d9df631102919ada62add6d785f737f74 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 30 Apr 2024 15:00:34 +0100 Subject: [PATCH 1068/2884] accel/tcg: Make TCGCPUOps::cpu_exec_halt return bool for whether to halt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TCGCPUOps::cpu_exec_halt method is called from cpu_handle_halt() when the CPU is halted, so that a target CPU emulation can do anything target-specific it needs to do. (At the moment we only use this on i386.) The current specification of the method doesn't allow the target specific code to do something different if the CPU is about to come out of the halt state, because cpu_handle_halt() only determines this after the method has returned. (If the method called cpu_has_work() itself this would introduce a potential race if an interrupt arrived between the target's method implementation checking and cpu_handle_halt() repeating the check.) Change the definition of the method so that it returns a bool to tell cpu_handle_halt() whether to stay in halt or not. We will want this for the Arm target, where FEAT_WFxT wants to do some work only for the case where the CPU is in halt but about to leave it. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20240430140035.3889879-2-peter.maydell@linaro.org --- accel/tcg/cpu-exec.c | 7 +++++-- include/hw/core/tcg-cpu-ops.h | 15 +++++++++++++-- target/i386/tcg/helper-tcg.h | 2 +- target/i386/tcg/sysemu/seg_helper.c | 3 ++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 2972f75b96..6711b58e0b 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -682,11 +682,14 @@ static inline bool cpu_handle_halt(CPUState *cpu) #ifndef CONFIG_USER_ONLY if (cpu->halted) { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + bool leave_halt; if (tcg_ops->cpu_exec_halt) { - tcg_ops->cpu_exec_halt(cpu); + leave_halt = tcg_ops->cpu_exec_halt(cpu); + } else { + leave_halt = cpu_has_work(cpu); } - if (!cpu_has_work(cpu)) { + if (!leave_halt) { return true; } diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 9387d38748..099de3375e 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -115,8 +115,19 @@ struct TCGCPUOps { void (*do_interrupt)(CPUState *cpu); /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); - /** @cpu_exec_halt: Callback for handling halt in cpu_exec */ - void (*cpu_exec_halt)(CPUState *cpu); + /** + * @cpu_exec_halt: Callback for handling halt in cpu_exec. + * + * The target CPU should do any special processing here that it needs + * to do when the CPU is in the halted state. + * + * Return true to indicate that the CPU should now leave halt, false + * if it should remain in the halted state. + * + * If this method is not provided, the default is to do nothing, and + * to leave halt if cpu_has_work() returns true. + */ + bool (*cpu_exec_halt)(CPUState *cpu); /** * @tlb_fill: Handle a softmmu tlb miss * diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index effc2c1c98..85957943bf 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -39,7 +39,7 @@ QEMU_BUILD_BUG_ON(TCG_PHYS_ADDR_BITS > TARGET_PHYS_ADDR_SPACE_BITS); */ void x86_cpu_do_interrupt(CPUState *cpu); #ifndef CONFIG_USER_ONLY -void x86_cpu_exec_halt(CPUState *cpu); +bool x86_cpu_exec_halt(CPUState *cpu); bool x86_need_replay_interrupt(int interrupt_request); bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req); #endif diff --git a/target/i386/tcg/sysemu/seg_helper.c b/target/i386/tcg/sysemu/seg_helper.c index 2db8083748..9ba94deb3a 100644 --- a/target/i386/tcg/sysemu/seg_helper.c +++ b/target/i386/tcg/sysemu/seg_helper.c @@ -128,7 +128,7 @@ void x86_cpu_do_interrupt(CPUState *cs) } } -void x86_cpu_exec_halt(CPUState *cpu) +bool x86_cpu_exec_halt(CPUState *cpu) { if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -138,6 +138,7 @@ void x86_cpu_exec_halt(CPUState *cpu) cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); bql_unlock(); } + return cpu_has_work(cpu); } bool x86_need_replay_interrupt(int interrupt_request) From a96edb687e76a44b554b7975d9deda522c2c4302 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 30 Apr 2024 15:00:35 +0100 Subject: [PATCH 1069/2884] target/arm: Implement FEAT WFxT and enable for '-cpu max' FEAT_WFxT introduces new instructions WFIT and WFET, which are like the existing WFI and WFE but allow the guest to pass a timeout value in a register. The instructions will wait for an interrupt/event as usual, but will also stop waiting when the value of CNTVCT_EL0 is greater than or equal to the specified timeout value. We implement WFIT by setting up a timer to expire at the right point; when the timer expires it sets the EXITTB interrupt, which will cause the CPU to leave the halted state. If we come out of halt for some other reason, we unset the pending timer. We implement WFET as a nop, which is architecturally permitted and matches the way we currently make WFE a nop. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20240430140035.3889879-3-peter.maydell@linaro.org --- docs/system/arm/emulation.rst | 1 + target/arm/cpu-features.h | 5 ++++ target/arm/cpu.c | 40 +++++++++++++++++++++++++ target/arm/cpu.h | 3 ++ target/arm/helper.c | 4 +-- target/arm/helper.h | 1 + target/arm/internals.h | 8 +++++ target/arm/machine.c | 20 +++++++++++++ target/arm/tcg/a64.decode | 4 +++ target/arm/tcg/cpu64.c | 1 + target/arm/tcg/op_helper.c | 54 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-a64.c | 41 ++++++++++++++++++++++++++ 12 files changed, 180 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 7fcea54d8d..1a06a5feb6 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -146,6 +146,7 @@ the following architecture extensions: - FEAT_UAO (Unprivileged Access Override control) - FEAT_VHE (Virtualization Host Extensions) - FEAT_VMID16 (16-bit VMID) +- FEAT_WFxT (WFE and WFI instructions with timeout) - FEAT_XNX (Translation table stage 2 Unprivileged Execute-never) For information on the specifics of these extensions, please refer diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index b300d0446d..c59ca104fe 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -571,6 +571,11 @@ static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; } +static inline bool isar_feature_aa64_wfxt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, WFXT) >= 2; +} + static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 77f8c9c748..35fa281f1b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1132,6 +1132,35 @@ static bool arm_cpu_virtio_is_big_endian(CPUState *cs) return arm_cpu_data_is_big_endian(env); } +#ifdef CONFIG_TCG +static bool arm_cpu_exec_halt(CPUState *cs) +{ + bool leave_halt = cpu_has_work(cs); + + if (leave_halt) { + /* We're about to come out of WFI/WFE: disable the WFxT timer */ + ARMCPU *cpu = ARM_CPU(cs); + if (cpu->wfxt_timer) { + timer_del(cpu->wfxt_timer); + } + } + return leave_halt; +} +#endif + +static void arm_wfxt_timer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + /* + * We expect the CPU to be halted; this will cause arm_cpu_is_work() + * to return true (so we will come out of halt even with no other + * pending interrupt), and the TCG accelerator's cpu_exec_interrupt() + * function auto-clears the CPU_INTERRUPT_EXITTB flag for us. + */ + cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); +} #endif static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) @@ -1877,6 +1906,9 @@ static void arm_cpu_finalizefn(Object *obj) if (cpu->pmu_timer) { timer_free(cpu->pmu_timer); } + if (cpu->wfxt_timer) { + timer_free(cpu->wfxt_timer); + } #endif } @@ -2369,6 +2401,13 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) #endif } +#ifndef CONFIG_USER_ONLY + if (tcg_enabled() && cpu_isar_feature(aa64_wfxt, cpu)) { + cpu->wfxt_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + arm_wfxt_timer_cb, cpu); + } +#endif + if (tcg_enabled()) { /* * Don't report some architectural features in the ID registers @@ -2625,6 +2664,7 @@ static const TCGCPUOps arm_tcg_ops = { #else .tlb_fill = arm_cpu_tlb_fill, .cpu_exec_interrupt = arm_cpu_exec_interrupt, + .cpu_exec_halt = arm_cpu_exec_halt, .do_interrupt = arm_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, .do_unaligned_access = arm_cpu_do_unaligned_access, diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c17264c239..3841359d0f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -866,6 +866,9 @@ struct ArchCPU { * pmu_op_finish() - it does not need other handling during migration */ QEMUTimer *pmu_timer; + /* Timer used for WFxT timeouts */ + QEMUTimer *wfxt_timer; + /* GPIO outputs for generic timer */ qemu_irq gt_timer_outputs[NUM_GTIMERS]; /* GPIO output for GICv3 maintenance interrupt signal */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 7587635960..ce31957235 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2665,7 +2665,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, } } -static uint64_t gt_get_countervalue(CPUARMState *env) +uint64_t gt_get_countervalue(CPUARMState *env) { ARMCPU *cpu = env_archcpu(env); @@ -2800,7 +2800,7 @@ static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) return gt_get_countervalue(env) - gt_phys_cnt_offset(env); } -static uint64_t gt_virt_cnt_offset(CPUARMState *env) +uint64_t gt_virt_cnt_offset(CPUARMState *env) { uint64_t hcr; diff --git a/target/arm/helper.h b/target/arm/helper.h index 24feecee9b..eca2043fc2 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -53,6 +53,7 @@ DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl) DEF_HELPER_1(setend, void, env) DEF_HELPER_2(wfi, void, env, i32) DEF_HELPER_1(wfe, void, env) +DEF_HELPER_2(wfit, void, env, i64) DEF_HELPER_1(yield, void, env) DEF_HELPER_1(pre_hvc, void, env) DEF_HELPER_2(pre_smc, void, env, i32) diff --git a/target/arm/internals.h b/target/arm/internals.h index ee3ebd383e..11b5da2562 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1770,4 +1770,12 @@ bool check_watchpoint_in_range(int i, target_ulong addr); CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr); int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type); int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); + +/* Return the current value of the system counter in ticks */ +uint64_t gt_get_countervalue(CPUARMState *env); +/* + * Return the currently applicable offset between the system counter + * and CNTVCT_EL0 (this will be either 0 or the value of CNTVOFF_EL2). + */ +uint64_t gt_virt_cnt_offset(CPUARMState *env); #endif diff --git a/target/arm/machine.c b/target/arm/machine.c index b2b39b2475..0a722ca7e7 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -242,6 +242,25 @@ static const VMStateDescription vmstate_irq_line_state = { } }; +static bool wfxt_timer_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + + /* We'll only have the timer object if FEAT_WFxT is implemented */ + return cpu->wfxt_timer; +} + +static const VMStateDescription vmstate_wfxt_timer = { + .name = "cpu/wfxt-timer", + .version_id = 1, + .minimum_version_id = 1, + .needed = wfxt_timer_needed, + .fields = (const VMStateField[]) { + VMSTATE_TIMER_PTR(wfxt_timer, ARMCPU), + VMSTATE_END_OF_LIST() + } +}; + static bool m_needed(void *opaque) { ARMCPU *cpu = opaque; @@ -957,6 +976,7 @@ const VMStateDescription vmstate_arm_cpu = { #endif &vmstate_serror, &vmstate_irq_line_state, + &vmstate_wfxt_timer, NULL } }; diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 5dadbc74d7..2b7a3254a0 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -230,6 +230,10 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB NOP 1101 0101 0000 0011 0010 ---- --- 11111 } +# System instructions with register argument +WFET 1101 0101 0000 0011 0001 0000 000 rd:5 +WFIT 1101 0101 0000 0011 0001 0000 001 rd:5 + # Barriers CLREX 1101 0101 0000 0011 0011 ---- 010 11111 diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index da41a44f75..0899251eef 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1168,6 +1168,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = cpu->isar.id_aa64isar2; t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ + t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ cpu->isar.id_aa64isar2 = t; t = cpu->isar.id_aa64pfr0; diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index c199b69fbf..c083e5cfb8 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -409,6 +409,60 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) #endif } +void HELPER(wfit)(CPUARMState *env, uint64_t timeout) +{ +#ifdef CONFIG_USER_ONLY + /* + * WFI in the user-mode emulator is technically permitted but not + * something any real-world code would do. AArch64 Linux kernels + * trap it via SCTRL_EL1.nTWI and make it an (expensive) NOP; + * AArch32 kernels don't trap it so it will delay a bit. + * For QEMU, make it NOP here, because trying to raise EXCP_HLT + * would trigger an abort. + */ + return; +#else + ARMCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + int target_el = check_wfx_trap(env, false); + /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ + uint64_t cntval = gt_get_countervalue(env); + uint64_t offset = gt_virt_cnt_offset(env); + uint64_t cntvct = cntval - offset; + uint64_t nexttick; + + if (cpu_has_work(cs) || cntvct >= timeout) { + /* + * Don't bother to go into our "low power state" if + * we would just wake up immediately. + */ + return; + } + + if (target_el) { + env->pc -= 4; + raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, false), + target_el); + } + + if (uadd64_overflow(timeout, offset, &nexttick)) { + nexttick = UINT64_MAX; + } + if (nexttick > INT64_MAX / gt_cntfrq_period_ns(cpu)) { + /* + * If the timeout is too long for the signed 64-bit range + * of a QEMUTimer, let it expire early. + */ + timer_mod_ns(cpu->wfxt_timer, INT64_MAX); + } else { + timer_mod(cpu->wfxt_timer, nexttick); + } + cs->exception_index = EXCP_HLT; + cs->halted = 1; + cpu_loop_exit(cs); +#endif +} + void HELPER(wfe)(CPUARMState *env) { /* This is a hint instruction that is semantically different diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f1dea5834c..93543da39c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1745,6 +1745,47 @@ static bool trans_WFE(DisasContext *s, arg_WFI *a) return true; } +static bool trans_WFIT(DisasContext *s, arg_WFIT *a) +{ + if (!dc_isar_feature(aa64_wfxt, s)) { + return false; + } + + /* + * Because we need to pass the register value to the helper, + * it's easier to emit the code now, unlike trans_WFI which + * defers it to aarch64_tr_tb_stop(). That means we need to + * check ss_active so that single-stepping a WFIT doesn't halt. + */ + if (s->ss_active) { + /* Act like a NOP under architectural singlestep */ + return true; + } + + gen_a64_update_pc(s, 4); + gen_helper_wfit(tcg_env, cpu_reg(s, a->rd)); + /* Go back to the main loop to check for interrupts */ + s->base.is_jmp = DISAS_EXIT; + return true; +} + +static bool trans_WFET(DisasContext *s, arg_WFET *a) +{ + if (!dc_isar_feature(aa64_wfxt, s)) { + return false; + } + + /* + * We rely here on our WFE implementation being a NOP, so we + * don't need to do anything different to handle the WFET timeout + * from what trans_WFE does. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + s->base.is_jmp = DISAS_WFE; + } + return true; +} + static bool trans_XPACLRI(DisasContext *s, arg_XPACLRI *a) { if (s->pauth_active) { From 3c3c233677d4f2fe5f35c5d6d6e9b53df48054f4 Mon Sep 17 00:00:00 2001 From: David Hubbard Date: Wed, 8 May 2024 19:29:16 -0500 Subject: [PATCH 1070/2884] hw/usb/hcd-ohci: Fix #1510, #303: pid not IN or OUT This changes the ohci validation to not assert if invalid data is fed to the ohci controller. The poc in https://bugs.launchpad.net/qemu/+bug/1907042 and migrated to bug #303 does the following to feed it a SETUP pid (valid) at an EndPt of 1 (invalid - all SETUP pids must be addressed to EndPt 0): uint32_t MaxPacket = 64; uint32_t TDFormat = 0; uint32_t Skip = 0; uint32_t Speed = 0; uint32_t Direction = 0; /* #define OHCI_TD_DIR_SETUP 0 */ uint32_t EndPt = 1; uint32_t FuncAddress = 0; ed->attr = (MaxPacket << 16) | (TDFormat << 15) | (Skip << 14) | (Speed << 13) | (Direction << 11) | (EndPt << 7) | FuncAddress; ed->tailp = /*TDQTailPntr= */ 0; ed->headp = ((/*TDQHeadPntr= */ &td[0]) & 0xfffffff0) | (/* ToggleCarry= */ 0 << 1); ed->next_ed = (/* NextED= */ 0 & 0xfffffff0) qemu-fuzz also caught the same issue in #1510. They are both fixed by this patch. With a tiny OS[1] that boots and executes the poc the repro shows the issue: * OS that sends USB requests to a USB mass storage device but sends a SETUP with EndPt = 1 * qemu 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.19) * qemu HEAD (4e66a0854) * Actual OHCI controller (hardware) Command line: qemu-system-x86_64 -m 20 \ -device pci-ohci,id=ohci \ -drive if=none,format=raw,id=d,file=testmbr.raw \ -device usb-storage,bus=ohci.0,drive=d \ --trace "usb_*" --trace "ohci_*" -D qemu.log Results are: qemu 6.2.0 | qemu HEAD | actual HW ------------+-----------+---------------- assertion | assertion | sets stall bit The assertion message is: > qemu-system-x86_64: ../../hw/usb/core.c:744: usb_ep_get: Assertion `pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT' failed. > Aborted (core dumped) Tip: if the flags "-serial pty -serial stdio" are added to the command line the poc outputs its USB requests like this: > Free mem 2M ohci port0 conn FS > setup { 80 6 0 1 0 0 8 0 } > ED info=80000 { mps=8 en=0 d=0 } tail=c20920 > td0 c20880 nxt=c20960 f2000000 setup cbp=c20900 be=c20907 cbp=0 be=c20907 > td1 c20960 nxt=c20980 f3140000 in cbp=c20908 be=c2090f cbp=0 be=c2090f > td2 c20980 nxt=c20920 f3080000 out cbp=0 be=0 cbp=0 be=0 > rx { 12 1 0 2 0 0 0 8 } > setup { 0 5 1 0 0 0 0 0 } tx {} > ED info=80000 { mps=8 en=0 d=0 } tail=c20880 > td0 c20920 nxt=c20960 f2000000 setup cbp=c20900 be=c20907 cbp=0 be=c20907 > td1 c20960 nxt=c20880 f3100000 in cbp=0 be=0 cbp=0 be=0 > setup { 80 6 0 1 0 0 12 0 } > ED info=80081 { mps=8 en=0 d=1 } tail=c20960 > td0 c20880 nxt=c209c0 f2000000 setup cbp=c20920 be=c20927 > td1 c209c0 nxt=c209e0 f3140000 in cbp=c20928 be=c20939 > td2 c209e0 nxt=c20960 f3080000 out cbp=0 be=0qemu-system-x86_64: ../../hw/usb/core.c:744: usb_ep_get: Assertion `pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT' failed. > Aborted (core dumped) [1] The OS disk image has been emailed to philmd@linaro.org, mjt@tls.msk.ru, and kraxel@redhat.com: * testBadSetup.img.xz * sha256: 045b43f4396de02b149518358bf8025d5ba11091e86458875339fc649e6e5ac6 Signed-off-by: David Hubbard Reviewed-by: Peter Maydell [PMM: authorship and signed-off-by tag names fixed up as per on-list agreement] Signed-off-by: Peter Maydell --- hw/usb/hcd-ohci.c | 5 +++++ hw/usb/trace-events | 1 + 2 files changed, 6 insertions(+) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index fc8fc91a1d..acd6016980 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -927,6 +927,11 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) case OHCI_TD_DIR_SETUP: str = "setup"; pid = USB_TOKEN_SETUP; + if (OHCI_BM(ed->flags, ED_EN) > 0) { /* setup only allowed to ep 0 */ + trace_usb_ohci_td_bad_pid(str, ed->flags, td.flags); + ohci_die(ohci); + return 1; + } break; default: trace_usb_ohci_td_bad_direction(dir); diff --git a/hw/usb/trace-events b/hw/usb/trace-events index ed7dc210d3..fd7b90d70c 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -28,6 +28,7 @@ usb_ohci_iso_td_data_overrun(int ret, ssize_t len) "DataOverrun %d > %zu" usb_ohci_iso_td_data_underrun(int ret) "DataUnderrun %d" usb_ohci_iso_td_nak(int ret) "got NAK/STALL %d" usb_ohci_iso_td_bad_response(int ret) "Bad device response %d" +usb_ohci_td_bad_pid(const char *s, uint32_t edf, uint32_t tdf) "Bad pid %s: ed.flags 0x%x td.flags 0x%x" usb_ohci_port_attach(int index) "port #%d" usb_ohci_port_detach(int index) "port #%d" usb_ohci_port_wakeup(int index) "port #%d" From c76b121840c6ca79dc6305a5f4bcf17c72217d9c Mon Sep 17 00:00:00 2001 From: "yang.zhang" Date: Tue, 9 Apr 2024 09:44:45 +0800 Subject: [PATCH 1071/2884] hw/intc/riscv_aplic: APLICs should add child earlier than realize Since only root APLICs can have hw IRQ lines, aplic->parent should be initialized first. Fixes: e8f79343cf ("hw/intc: Add RISC-V AIA APLIC device emulation") Reviewed-by: Daniel Henrique Barboza Signed-off-by: yang.zhang Cc: qemu-stable Message-ID: <20240409014445.278-1-gaoshanliukou@163.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index fc5df0d598..32edd6d07b 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -1000,16 +1000,16 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, qdev_prop_set_bit(dev, "msimode", msimode); qdev_prop_set_bit(dev, "mmode", mmode); + if (parent) { + riscv_aplic_add_child(parent, dev); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); if (!is_kvm_aia(msimode)) { sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); } - if (parent) { - riscv_aplic_add_child(parent, dev); - } - if (!msimode) { for (i = 0; i < num_harts; i++) { CPUState *cpu = cpu_by_arch_id(hartid_base + i); From 86997772fa807f3961e5aeed97af7738adec1b43 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 22 Apr 2024 15:46:06 +0200 Subject: [PATCH 1072/2884] target/riscv/kvm: Fix exposure of Zkr The Zkr extension may only be exposed to KVM guests if the VMM implements the SEED CSR. Use the same implementation as TCG. Without this patch, running with a KVM which does not forward the SEED CSR access to QEMU will result in an ILL exception being injected into the guest (this results in Linux guests crashing on boot). And, when running with a KVM which does forward the access, QEMU will crash, since QEMU doesn't know what to do with the exit. Fixes: 3108e2f1c69d ("target/riscv/kvm: update KVM exts to Linux 6.8") Signed-off-by: Andrew Jones Reviewed-by: Daniel Henrique Barboza Cc: qemu-stable Message-ID: <20240422134605.534207-2-ajones@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 3 +++ target/riscv/csr.c | 18 ++++++++++++++---- target/riscv/kvm/kvm-cpu.c | 25 +++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 2d0c02c35b..746efd099a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -819,6 +819,9 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops); void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); +target_ulong riscv_new_csr_seed(target_ulong new_value, + target_ulong write_mask); + uint8_t satp_mode_max_from_map(uint32_t map); const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 726096444f..829d8346ed 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -4267,10 +4267,8 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno, #endif /* Crypto Extension */ -static RISCVException rmw_seed(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, - target_ulong write_mask) +target_ulong riscv_new_csr_seed(target_ulong new_value, + target_ulong write_mask) { uint16_t random_v; Error *random_e = NULL; @@ -4294,6 +4292,18 @@ static RISCVException rmw_seed(CPURISCVState *env, int csrno, rval = random_v | SEED_OPST_ES16; } + return rval; +} + +static RISCVException rmw_seed(CPURISCVState *env, int csrno, + target_ulong *ret_value, + target_ulong new_value, + target_ulong write_mask) +{ + target_ulong rval; + + rval = riscv_new_csr_seed(new_value, write_mask); + if (ret_value) { *ret_value = rval; } diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index eaa36121c7..b8136c7ef8 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1418,6 +1418,28 @@ static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run) return ret; } +static int kvm_riscv_handle_csr(CPUState *cs, struct kvm_run *run) +{ + target_ulong csr_num = run->riscv_csr.csr_num; + target_ulong new_value = run->riscv_csr.new_value; + target_ulong write_mask = run->riscv_csr.write_mask; + int ret = 0; + + switch (csr_num) { + case CSR_SEED: + run->riscv_csr.ret_value = riscv_new_csr_seed(new_value, write_mask); + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: un-handled CSR EXIT for CSR %lx\n", + __func__, csr_num); + ret = -1; + break; + } + + return ret; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { int ret = 0; @@ -1425,6 +1447,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_RISCV_SBI: ret = kvm_riscv_handle_sbi(cs, run); break; + case KVM_EXIT_RISCV_CSR: + ret = kvm_riscv_handle_csr(cs, run); + break; default: qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", __func__, run->exit_reason); From b62e0ce76098d53c875f0aff70776f08418ccb58 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 24 Apr 2024 16:28:09 +0200 Subject: [PATCH 1073/2884] target/riscv: Raise exceptions on wrs.nto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementing wrs.nto to always just return is consistent with the specification, as the instruction is permitted to terminate the stall for any reason, but it's not useful for virtualization, where we'd like the guest to trap to the hypervisor in order to allow scheduling of the lock holding VCPU. Change to always immediately raise exceptions when the appropriate conditions are present, otherwise continue to just return. Note, immediately raising exceptions is also consistent with the specification since the time limit that should expire prior to the exception is implementation-specific. Signed-off-by: Andrew Jones Reviewed-by: Christoph Müllner Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240424142808.62936-2-ajones@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/helper.h | 1 + target/riscv/insn_trans/trans_rvzawrs.c.inc | 29 ++++++++++++++------- target/riscv/op_helper.c | 11 ++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 8a63523851..451261ce5a 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -132,6 +132,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl) DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) DEF_HELPER_1(wfi, void, env) +DEF_HELPER_1(wrs_nto, void, env) DEF_HELPER_1(tlb_flush, void, env) DEF_HELPER_1(tlb_flush_all, void, env) /* Native Debug */ diff --git a/target/riscv/insn_trans/trans_rvzawrs.c.inc b/target/riscv/insn_trans/trans_rvzawrs.c.inc index 32efbff4d5..0eef033838 100644 --- a/target/riscv/insn_trans/trans_rvzawrs.c.inc +++ b/target/riscv/insn_trans/trans_rvzawrs.c.inc @@ -16,7 +16,7 @@ * this program. If not, see . */ -static bool trans_wrs(DisasContext *ctx) +static bool trans_wrs_sto(DisasContext *ctx, arg_wrs_sto *a) { if (!ctx->cfg_ptr->ext_zawrs) { return false; @@ -40,12 +40,23 @@ static bool trans_wrs(DisasContext *ctx) return true; } -#define GEN_TRANS_WRS(insn) \ -static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn *a) \ -{ \ - (void)a; \ - return trans_wrs(ctx); \ -} +static bool trans_wrs_nto(DisasContext *ctx, arg_wrs_nto *a) +{ + if (!ctx->cfg_ptr->ext_zawrs) { + return false; + } -GEN_TRANS_WRS(wrs_nto) -GEN_TRANS_WRS(wrs_sto) + /* + * Depending on the mode of execution, mstatus.TW and hstatus.VTW, wrs.nto + * should raise an exception when the implementation-specific bounded time + * limit has expired. Our time limit is zero, so we either return + * immediately, as does our implementation of wrs.sto, or raise an + * exception, as handled by the wrs.nto helper. + */ +#ifndef CONFIG_USER_ONLY + gen_helper_wrs_nto(tcg_env); +#endif + + /* We only get here when helper_wrs_nto() doesn't raise an exception. */ + return trans_wrs_sto(ctx, NULL); +} diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index f414aaebdb..2baf5bc3ca 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -380,6 +380,17 @@ void helper_wfi(CPURISCVState *env) } } +void helper_wrs_nto(CPURISCVState *env) +{ + if (env->virt_enabled && (env->priv == PRV_S || env->priv == PRV_U) && + get_field(env->hstatus, HSTATUS_VTW) && + !get_field(env->mstatus, MSTATUS_TW)) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); + } else if (env->priv != PRV_M && get_field(env->mstatus, MSTATUS_TW)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } +} + void helper_tlb_flush(CPURISCVState *env) { CPUState *cs = env_cpu(env); From a6b53378f537a51355a49826b7d119698c74ffba Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 25 Apr 2024 12:50:12 -0300 Subject: [PATCH 1074/2884] target/riscv/kvm: implement SBI debug console (DBCN) calls SBI defines a Debug Console extension "DBCN" that will, in time, replace the legacy console putchar and getchar SBI extensions. The appeal of the DBCN extension is that it allows multiple bytes to be read/written in the SBI console in a single SBI call. As far as KVM goes, the DBCN calls are forwarded by an in-kernel KVM module to userspace. But this will only happens if the KVM module actually supports this SBI extension and we activate it. We'll check for DBCN support during init time, checking if get-reg-list is advertising KVM_RISCV_SBI_EXT_DBCN. In that case, we'll enable it via kvm_set_one_reg() during kvm_arch_init_vcpu(). Finally, change kvm_riscv_handle_sbi() to handle the incoming calls for SBI_EXT_DBCN, reading and writing as required. A simple KVM guest with 'earlycon=sbi', running in an emulated RISC-V host, takes around 20 seconds to boot without using DBCN. With this patch we're taking around 14 seconds to boot due to the speed-up in the terminal output. There's no change in boot time if the guest isn't using earlycon. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Message-ID: <20240425155012.581366-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 111 +++++++++++++++++++++++++++++ target/riscv/sbi_ecall_interface.h | 17 +++++ 2 files changed, 128 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index b8136c7ef8..d2491d84e2 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -409,6 +409,12 @@ static KVMCPUConfig kvm_v_vlenb = { KVM_REG_RISCV_VECTOR_CSR_REG(vlenb) }; +static KVMCPUConfig kvm_sbi_dbcn = { + .name = "sbi_dbcn", + .kvm_reg_id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | + KVM_REG_RISCV_SBI_EXT | KVM_RISCV_SBI_EXT_DBCN +}; + static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) { CPURISCVState *env = &cpu->env; @@ -1037,6 +1043,20 @@ static int uint64_cmp(const void *a, const void *b) return 0; } +static void kvm_riscv_check_sbi_dbcn_support(RISCVCPU *cpu, + KVMScratchCPU *kvmcpu, + struct kvm_reg_list *reglist) +{ + struct kvm_reg_list *reg_search; + + reg_search = bsearch(&kvm_sbi_dbcn.kvm_reg_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + + if (reg_search) { + kvm_sbi_dbcn.supported = true; + } +} + static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, struct kvm_reg_list *reglist) { @@ -1142,6 +1162,8 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) if (riscv_has_ext(&cpu->env, RVV)) { kvm_riscv_read_vlenb(cpu, kvmcpu, reglist); } + + kvm_riscv_check_sbi_dbcn_support(cpu, kvmcpu, reglist); } static void riscv_init_kvm_registers(Object *cpu_obj) @@ -1316,6 +1338,17 @@ static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) return ret; } +static int kvm_vcpu_enable_sbi_dbcn(RISCVCPU *cpu, CPUState *cs) +{ + target_ulong reg = 1; + + if (!kvm_sbi_dbcn.supported) { + return 0; + } + + return kvm_set_one_reg(cs, kvm_sbi_dbcn.kvm_reg_id, ®); +} + int kvm_arch_init_vcpu(CPUState *cs) { int ret = 0; @@ -1333,6 +1366,8 @@ int kvm_arch_init_vcpu(CPUState *cs) kvm_riscv_update_cpu_misa_ext(cpu, cs); kvm_riscv_update_cpu_cfg_isa_ext(cpu, cs); + ret = kvm_vcpu_enable_sbi_dbcn(cpu, cs); + return ret; } @@ -1390,6 +1425,79 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cs) return true; } +static void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) +{ + g_autofree uint8_t *buf = NULL; + RISCVCPU *cpu = RISCV_CPU(cs); + target_ulong num_bytes; + uint64_t addr; + unsigned char ch; + int ret; + + switch (run->riscv_sbi.function_id) { + case SBI_EXT_DBCN_CONSOLE_READ: + case SBI_EXT_DBCN_CONSOLE_WRITE: + num_bytes = run->riscv_sbi.args[0]; + + if (num_bytes == 0) { + run->riscv_sbi.ret[0] = SBI_SUCCESS; + run->riscv_sbi.ret[1] = 0; + break; + } + + addr = run->riscv_sbi.args[1]; + + /* + * Handle the case where a 32 bit CPU is running in a + * 64 bit addressing env. + */ + if (riscv_cpu_mxl(&cpu->env) == MXL_RV32) { + addr |= (uint64_t)run->riscv_sbi.args[2] << 32; + } + + buf = g_malloc0(num_bytes); + + if (run->riscv_sbi.function_id == SBI_EXT_DBCN_CONSOLE_READ) { + ret = qemu_chr_fe_read_all(serial_hd(0)->be, buf, num_bytes); + if (ret < 0) { + error_report("SBI_EXT_DBCN_CONSOLE_READ: error when " + "reading chardev"); + exit(1); + } + + cpu_physical_memory_write(addr, buf, ret); + } else { + cpu_physical_memory_read(addr, buf, num_bytes); + + ret = qemu_chr_fe_write_all(serial_hd(0)->be, buf, num_bytes); + if (ret < 0) { + error_report("SBI_EXT_DBCN_CONSOLE_WRITE: error when " + "writing chardev"); + exit(1); + } + } + + run->riscv_sbi.ret[0] = SBI_SUCCESS; + run->riscv_sbi.ret[1] = ret; + break; + case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: + ch = run->riscv_sbi.args[0]; + ret = qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); + + if (ret < 0) { + error_report("SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: error when " + "writing chardev"); + exit(1); + } + + run->riscv_sbi.ret[0] = SBI_SUCCESS; + run->riscv_sbi.ret[1] = 0; + break; + default: + run->riscv_sbi.ret[0] = SBI_ERR_NOT_SUPPORTED; + } +} + static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run) { int ret = 0; @@ -1408,6 +1516,9 @@ static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run) } ret = 0; break; + case SBI_EXT_DBCN: + kvm_riscv_handle_sbi_dbcn(cs, run); + break; default: qemu_log_mask(LOG_UNIMP, "%s: un-handled SBI EXIT, specific reasons is %lu\n", diff --git a/target/riscv/sbi_ecall_interface.h b/target/riscv/sbi_ecall_interface.h index 43899d08f6..7dfe5f72c6 100644 --- a/target/riscv/sbi_ecall_interface.h +++ b/target/riscv/sbi_ecall_interface.h @@ -12,6 +12,17 @@ /* clang-format off */ +#define SBI_SUCCESS 0 +#define SBI_ERR_FAILED -1 +#define SBI_ERR_NOT_SUPPORTED -2 +#define SBI_ERR_INVALID_PARAM -3 +#define SBI_ERR_DENIED -4 +#define SBI_ERR_INVALID_ADDRESS -5 +#define SBI_ERR_ALREADY_AVAILABLE -6 +#define SBI_ERR_ALREADY_STARTED -7 +#define SBI_ERR_ALREADY_STOPPED -8 +#define SBI_ERR_NO_SHMEM -9 + /* SBI Extension IDs */ #define SBI_EXT_0_1_SET_TIMER 0x0 #define SBI_EXT_0_1_CONSOLE_PUTCHAR 0x1 @@ -27,6 +38,7 @@ #define SBI_EXT_IPI 0x735049 #define SBI_EXT_RFENCE 0x52464E43 #define SBI_EXT_HSM 0x48534D +#define SBI_EXT_DBCN 0x4442434E /* SBI function IDs for BASE extension */ #define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 @@ -57,6 +69,11 @@ #define SBI_EXT_HSM_HART_STOP 0x1 #define SBI_EXT_HSM_HART_GET_STATUS 0x2 +/* SBI function IDs for DBCN extension */ +#define SBI_EXT_DBCN_CONSOLE_WRITE 0x0 +#define SBI_EXT_DBCN_CONSOLE_READ 0x1 +#define SBI_EXT_DBCN_CONSOLE_WRITE_BYTE 0x2 + #define SBI_HSM_HART_STATUS_STARTED 0x0 #define SBI_HSM_HART_STATUS_STOPPED 0x1 #define SBI_HSM_HART_STATUS_START_PENDING 0x2 From 039003995047b2f7911142c7c5cfb845fda044fd Mon Sep 17 00:00:00 2001 From: Cheng Yang Date: Mon, 1 Apr 2024 15:51:22 +0800 Subject: [PATCH 1075/2884] hw/riscv/boot.c: Support 64-bit address for initrd Use qemu_fdt_setprop_u64() instead of qemu_fdt_setprop_cell() to set the address of initrd in FDT to support 64-bit address. Signed-off-by: Cheng Yang Reviewed-by: Alistair Francis Message-ID: Signed-off-by: Alistair Francis --- hw/riscv/boot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 09878e722c..47281ca853 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -209,8 +209,8 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ if (fdt) { end = start + size; - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", start); - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", end); + qemu_fdt_setprop_u64(fdt, "/chosen", "linux,initrd-start", start); + qemu_fdt_setprop_u64(fdt, "/chosen", "linux,initrd-end", end); } } From ba7a1c52975a4068573deea4471535567393c366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Mon, 22 Apr 2024 15:58:36 +0200 Subject: [PATCH 1076/2884] target/riscv: change RISCV_EXCP_SEMIHOST exception number to 63 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current semihost exception number (16) is a reserved number (range [16-17]). The upcoming double trap specification uses that number for the double trap exception. Since the privileged spec (Table 22) defines ranges for custom uses change the semihosting exception number to 63 which belongs to the range [48-63] in order to avoid any future collisions with reserved exception. Signed-off-by: Clément Léger Reviewed-by: Alistair Francis Message-ID: <20240422135840.1959967-1-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index fc2068ee4d..74318a925c 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -670,11 +670,11 @@ typedef enum RISCVException { RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ - RISCV_EXCP_SEMIHOST = 0x10, RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15, RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16, RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT = 0x17, + RISCV_EXCP_SEMIHOST = 0x3f, } RISCVException; #define RISCV_EXCP_INT_FLAG 0x80000000 From 1215d45b2aa97512a2867e401aa59f3d0c23cb23 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 22 Apr 2024 14:14:25 -0300 Subject: [PATCH 1077/2884] target/riscv/kvm: tolerate KVM disable ext errors Running a KVM guest using a 6.9-rc3 kernel, in a 6.8 host that has zkr enabled, will fail with a kernel oops SIGILL right at the start. The reason is that we can't expose zkr without implementing the SEED CSR. Disabling zkr in the guest would be a workaround, but if the KVM doesn't allow it we'll error out and never boot. In hindsight this is too strict. If we keep proceeding, despite not disabling the extension in the KVM vcpu, we'll not add the extension in the riscv,isa. The guest kernel will be unaware of the extension, i.e. it doesn't matter if the KVM vcpu has it enabled underneath or not. So it's ok to keep booting in this case. Change our current logic to not error out if we fail to disable an extension in kvm_set_one_reg(), but show a warning and keep booting. It is important to throw a warning because we must make the user aware that the extension is still available in the vcpu, meaning that an ill-behaved guest can ignore the riscv,isa settings and use the extension. The case we're handling happens with an EINVAL error code. If we fail to disable the extension in KVM for any other reason, error out. We'll also keep erroring out when we fail to enable an extension in KVM, since adding the extension in riscv,isa at this point will cause a guest malfunction because the extension isn't enabled in the vcpu. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Cc: qemu-stable Message-ID: <20240422171425.333037-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index d2491d84e2..473416649f 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -433,10 +433,14 @@ static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg); ret = kvm_set_one_reg(cs, id, ®); if (ret != 0) { - error_report("Unable to %s extension %s in KVM, error %d", - reg ? "enable" : "disable", - multi_ext_cfg->name, ret); - exit(EXIT_FAILURE); + if (!reg && ret == -EINVAL) { + warn_report("KVM cannot disable extension %s", + multi_ext_cfg->name); + } else { + error_report("Unable to enable extension %s in KVM, error %d", + multi_ext_cfg->name, ret); + exit(EXIT_FAILURE); + } } } } From 0099f6053410f5611796213b723e908cfc8055eb Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 16 Apr 2024 20:04:36 -0300 Subject: [PATCH 1078/2884] target/riscv/debug: set tval=pc in breakpoint exceptions We're not setting (s/m)tval when triggering breakpoints of type 2 (mcontrol) and 6 (mcontrol6). According to the debug spec section 5.7.12, "Match Control Type 6": "The Privileged Spec says that breakpoint exceptions that occur on instruction fetches, loads, or stores update the tval CSR with either zero or the faulting virtual address. The faulting virtual address for an mcontrol6 trigger with action = 0 is the address being accessed and which caused that trigger to fire." A similar text is also found in the Debug spec section 5.7.11 w.r.t. mcontrol. Note that what we're doing ATM is not violating the spec, but it's simple enough to set mtval/stval and it makes life easier for any software that relies on this info. Given that we always use action = 0, save the faulting address for the mcontrol and mcontrol6 trigger breakpoints into env->badaddr, which is used as as scratch area for traps with address information. 'tval' is then set during riscv_cpu_do_interrupt(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-ID: <20240416230437.1869024-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 1 + target/riscv/debug.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8ad546a45a..179cf3d1a1 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1718,6 +1718,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) tval = env->bins; break; case RISCV_EXCP_BREAKPOINT: + tval = env->badaddr; if (cs->watchpoint_hit) { tval = cs->watchpoint_hit->hitaddr; cs->watchpoint_hit = NULL; diff --git a/target/riscv/debug.c b/target/riscv/debug.c index e30d99cc2f..b110370ea6 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -798,6 +798,7 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { /* check U/S/M bit against current privilege level */ if ((ctrl >> 3) & BIT(env->priv)) { + env->badaddr = pc; return true; } } @@ -810,11 +811,13 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) if (env->virt_enabled) { /* check VU/VS bit against current privilege level */ if ((ctrl >> 23) & BIT(env->priv)) { + env->badaddr = pc; return true; } } else { /* check U/S/M bit against current privilege level */ if ((ctrl >> 3) & BIT(env->priv)) { + env->badaddr = pc; return true; } } From f15af01740efb95d1dccdac763011dcba144c1fe Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 16 Apr 2024 20:04:37 -0300 Subject: [PATCH 1079/2884] trans_privileged.c.inc: set (m|s)tval on ebreak breakpoint Privileged spec section 4.1.9 mentions: "When a trap is taken into S-mode, stval is written with exception-specific information to assist software in handling the trap. (...) If stval is written with a nonzero value when a breakpoint, address-misaligned, access-fault, or page-fault exception occurs on an instruction fetch, load, or store, then stval will contain the faulting virtual address." A similar text is found for mtval in section 3.1.16. Setting mtval/stval in this scenario is optional, but some softwares read these regs when handling ebreaks. Write 'badaddr' in all ebreak breakpoints to write the appropriate 'tval' during riscv_do_cpu_interrrupt(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Reviewed-by: Richard Henderson Message-ID: <20240416230437.1869024-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_privileged.c.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 620ab54eb0..bc5263a4e0 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -62,6 +62,8 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) { generate_exception(ctx, RISCV_EXCP_SEMIHOST); } else { + tcg_gen_st_tl(tcg_constant_tl(ebreak_addr), tcg_env, + offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_BREAKPOINT); } return true; From 9fb41a4418efb6008bce218d9510db830fd744ab Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Thu, 28 Mar 2024 10:23:10 +0800 Subject: [PATCH 1080/2884] target/riscv: Add support for Zve32x extension Add support for Zve32x extension and replace some checks for Zve32f with Zve32x, since Zve32f depends on Zve32x. Signed-off-by: Jason Chien Reviewed-by: Frank Chang Reviewed-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20240328022343.6871-2-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/cpu_helper.c | 2 +- target/riscv/csr.c | 2 +- target/riscv/insn_trans/trans_rvv.c.inc | 4 ++-- target/riscv/tcg/tcg-cpu.c | 16 ++++++++-------- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index eb1a2e7d6d..d744594cc4 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -153,6 +153,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvbb, PRIV_VERSION_1_12_0, ext_zvbb), ISA_EXT_DATA_ENTRY(zvbc, PRIV_VERSION_1_12_0, ext_zvbc), ISA_EXT_DATA_ENTRY(zve32f, PRIV_VERSION_1_10_0, ext_zve32f), + ISA_EXT_DATA_ENTRY(zve32x, PRIV_VERSION_1_10_0, ext_zve32x), ISA_EXT_DATA_ENTRY(zve64f, PRIV_VERSION_1_10_0, ext_zve64f), ISA_EXT_DATA_ENTRY(zve64d, PRIV_VERSION_1_10_0, ext_zve64d), ISA_EXT_DATA_ENTRY(zvfbfmin, PRIV_VERSION_1_12_0, ext_zvfbfmin), @@ -1472,6 +1473,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zfh", ext_zfh, false), MULTI_EXT_CFG_BOOL("zfhmin", ext_zfhmin, false), MULTI_EXT_CFG_BOOL("zve32f", ext_zve32f, false), + MULTI_EXT_CFG_BOOL("zve32x", ext_zve32x, false), MULTI_EXT_CFG_BOOL("zve64f", ext_zve64f, false), MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false), MULTI_EXT_CFG_BOOL("zvfbfmin", ext_zvfbfmin, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index cb750154bd..dce49050c0 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -91,6 +91,7 @@ struct RISCVCPUConfig { bool ext_zhinx; bool ext_zhinxmin; bool ext_zve32f; + bool ext_zve32x; bool ext_zve64f; bool ext_zve64d; bool ext_zvbb; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 179cf3d1a1..d71245a8cb 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -73,7 +73,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; *cs_base = 0; - if (cpu->cfg.ext_zve32f) { + if (cpu->cfg.ext_zve32x) { /* * If env->vl equals to VLMAX, we can use generic vector operation * expanders (GVEC) to accerlate the vector operations. diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 829d8346ed..58ef7079dc 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -93,7 +93,7 @@ static RISCVException fs(CPURISCVState *env, int csrno) static RISCVException vs(CPURISCVState *env, int csrno) { - if (riscv_cpu_cfg(env)->ext_zve32f) { + if (riscv_cpu_cfg(env)->ext_zve32x) { #if !defined(CONFIG_USER_ONLY) if (!env->debugger && !riscv_cpu_vector_enabled(env)) { return RISCV_EXCP_ILLEGAL_INST; diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 7d84e7d812..eec2939e23 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -149,7 +149,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) { TCGv s1, dst; - if (!require_rvv(s) || !s->cfg_ptr->ext_zve32f) { + if (!require_rvv(s) || !s->cfg_ptr->ext_zve32x) { return false; } @@ -179,7 +179,7 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) { TCGv dst; - if (!require_rvv(s) || !s->cfg_ptr->ext_zve32f) { + if (!require_rvv(s) || !s->cfg_ptr->ext_zve32x) { return false; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 40054a391a..e2cf5f429d 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -511,9 +511,13 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (cpu->cfg.ext_zve32f && !riscv_has_ext(env, RVF)) { - error_setg(errp, "Zve32f/Zve64f extensions require F extension"); - return; + /* The Zve32f extension depends on the Zve32x extension */ + if (cpu->cfg.ext_zve32f) { + if (!riscv_has_ext(env, RVF)) { + error_setg(errp, "Zve32f/Zve64f extensions require F extension"); + return; + } + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32x), true); } if (cpu->cfg.ext_zvfh) { @@ -658,13 +662,9 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); } - /* - * In principle Zve*x would also suffice here, were they supported - * in qemu - */ if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned || cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || - cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32f) { + cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32x) { error_setg(errp, "Vector crypto extensions require V or Zve* extensions"); return; From e7dc5e160f69678432c24827b522baf82b73688a Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Thu, 28 Mar 2024 10:23:11 +0800 Subject: [PATCH 1081/2884] target/riscv: Add support for Zve64x extension Add support for Zve64x extension. Enabling Zve64f enables Zve64x and enabling Zve64x enables Zve32x according to their dependency. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2107 Signed-off-by: Jason Chien Reviewed-by: Frank Chang Reviewed-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20240328022343.6871-3-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 17 +++++++++++------ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d744594cc4..a74f0eb29c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -156,6 +156,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zve32x, PRIV_VERSION_1_10_0, ext_zve32x), ISA_EXT_DATA_ENTRY(zve64f, PRIV_VERSION_1_10_0, ext_zve64f), ISA_EXT_DATA_ENTRY(zve64d, PRIV_VERSION_1_10_0, ext_zve64d), + ISA_EXT_DATA_ENTRY(zve64x, PRIV_VERSION_1_10_0, ext_zve64x), ISA_EXT_DATA_ENTRY(zvfbfmin, PRIV_VERSION_1_12_0, ext_zvfbfmin), ISA_EXT_DATA_ENTRY(zvfbfwma, PRIV_VERSION_1_12_0, ext_zvfbfwma), ISA_EXT_DATA_ENTRY(zvfh, PRIV_VERSION_1_12_0, ext_zvfh), @@ -1476,6 +1477,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zve32x", ext_zve32x, false), MULTI_EXT_CFG_BOOL("zve64f", ext_zve64f, false), MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false), + MULTI_EXT_CFG_BOOL("zve64x", ext_zve64x, false), MULTI_EXT_CFG_BOOL("zvfbfmin", ext_zvfbfmin, false), MULTI_EXT_CFG_BOOL("zvfbfwma", ext_zvfbfwma, false), MULTI_EXT_CFG_BOOL("zvfh", ext_zvfh, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index dce49050c0..e1e4f32698 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -94,6 +94,7 @@ struct RISCVCPUConfig { bool ext_zve32x; bool ext_zve64f; bool ext_zve64d; + bool ext_zve64x; bool ext_zvbb; bool ext_zvbc; bool ext_zvkb; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index e2cf5f429d..fedc035313 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -498,17 +498,22 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) /* The Zve64d extension depends on the Zve64f extension */ if (cpu->cfg.ext_zve64d) { + if (!riscv_has_ext(env, RVD)) { + error_setg(errp, "Zve64d/V extensions require D extension"); + return; + } cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64f), true); } - /* The Zve64f extension depends on the Zve32f extension */ + /* The Zve64f extension depends on the Zve64x and Zve32f extensions */ if (cpu->cfg.ext_zve64f) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64x), true); cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32f), true); } - if (cpu->cfg.ext_zve64d && !riscv_has_ext(env, RVD)) { - error_setg(errp, "Zve64d/V extensions require D extension"); - return; + /* The Zve64x extension depends on the Zve32x extension */ + if (cpu->cfg.ext_zve64x) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32x), true); } /* The Zve32f extension depends on the Zve32x extension */ @@ -670,10 +675,10 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64f) { + if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64x) { error_setg( errp, - "Zvbc and Zvknhb extensions require V or Zve64{f,d} extensions"); + "Zvbc and Zvknhb extensions require V or Zve64x extensions"); return; } From 4a90991234f003d8fe55919e84bf3ec7d542830e Mon Sep 17 00:00:00 2001 From: Jason Chien Date: Thu, 28 Mar 2024 10:23:12 +0800 Subject: [PATCH 1082/2884] target/riscv: Relax vector register check in RISCV gdbstub In current implementation, the gdbstub allows reading vector registers only if V extension is supported. However, all vector extensions and vector crypto extensions have the vector registers and they all depend on Zve32x. The gdbstub should check for Zve32x instead. Signed-off-by: Jason Chien Reviewed-by: Frank Chang Reviewed-by: Max Chou Message-ID: <20240328022343.6871-4-jason.chien@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/gdbstub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index be7a02cd90..d0cc5762c2 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -338,7 +338,7 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) gdb_find_static_feature("riscv-32bit-fpu.xml"), 0); } - if (env->misa_ext & RVV) { + if (cpu->cfg.ext_zve32x) { gdb_register_coprocessor(cs, riscv_gdb_get_vector, riscv_gdb_set_vector, ricsv_gen_dynamic_vector_feature(cs, cs->gdb_num_regs), From 75115d880c6d396f8a2d56aab8c12236d85a90e0 Mon Sep 17 00:00:00 2001 From: Huang Tao Date: Mon, 25 Mar 2024 10:16:54 +0800 Subject: [PATCH 1083/2884] target/riscv: Fix the element agnostic function problem In RVV and vcrypto instructions, the masked and tail elements are set to 1s using vext_set_elems_1s function if the vma/vta bit is set. It is the element agnostic policy. However, this function can't deal the big endian situation. This patch fixes the problem by adding handling of such case. Signed-off-by: Huang Tao Suggested-by: Richard Henderson Reviewed-by: LIU Zhiwei Cc: qemu-stable Message-ID: <20240325021654.6594-1-eric.huang@linux.alibaba.com> Signed-off-by: Alistair Francis --- target/riscv/vector_internals.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/target/riscv/vector_internals.c b/target/riscv/vector_internals.c index 996c21eb31..05b2d01e58 100644 --- a/target/riscv/vector_internals.c +++ b/target/riscv/vector_internals.c @@ -30,6 +30,28 @@ void vext_set_elems_1s(void *base, uint32_t is_agnostic, uint32_t cnt, if (tot - cnt == 0) { return ; } + + if (HOST_BIG_ENDIAN) { + /* + * Deal the situation when the elements are insdie + * only one uint64 block including setting the + * masked-off element. + */ + if (((tot - 1) ^ cnt) < 8) { + memset(base + H1(tot - 1), -1, tot - cnt); + return; + } + /* + * Otherwise, at least cross two uint64_t blocks. + * Set first unaligned block. + */ + if (cnt % 8 != 0) { + uint32_t j = ROUND_UP(cnt, 8); + memset(base + H1(j - 1), -1, j - cnt); + cnt = j; + } + /* Set other 64bit aligend blocks */ + } memset(base + cnt, -1, tot - cnt); } From ff33b7a9699e977a050a1014c617a89da1bf8295 Mon Sep 17 00:00:00 2001 From: Yangyu Chen Date: Sat, 11 May 2024 19:26:48 +0800 Subject: [PATCH 1084/2884] target/riscv/cpu.c: fix Zvkb extension config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code has a typo that writes zvkb to zvkg, causing users can't enable zvkb through the config. This patch gets this fixed. Signed-off-by: Yangyu Chen Fixes: ea61ef7097d0 ("target/riscv: Move vector crypto extensions to riscv_cpu_extensions") Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Reviewed-by: Max Chou Reviewed-by:  Weiwei Li Message-ID: Cc: qemu-stable Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a74f0eb29c..0d6fb9b4ba 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1539,7 +1539,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { /* Vector cryptography extensions */ MULTI_EXT_CFG_BOOL("zvbb", ext_zvbb, false), MULTI_EXT_CFG_BOOL("zvbc", ext_zvbc, false), - MULTI_EXT_CFG_BOOL("zvkb", ext_zvkg, false), + MULTI_EXT_CFG_BOOL("zvkb", ext_zvkb, false), MULTI_EXT_CFG_BOOL("zvkg", ext_zvkg, false), MULTI_EXT_CFG_BOOL("zvkned", ext_zvkned, false), MULTI_EXT_CFG_BOOL("zvknha", ext_zvknha, false), From 8c8a7cd647c53cd620e702243914820b6eae70f1 Mon Sep 17 00:00:00 2001 From: Huang Tao Date: Mon, 6 May 2024 10:36:07 +0800 Subject: [PATCH 1085/2884] target/riscv: Implement dynamic establishment of custom decoder In this patch, we modify the decoder to be a freely composable data structure instead of a hardcoded one. It can be dynamically builded up according to the extensions. This approach has several benefits: 1. Provides support for heterogeneous cpu architectures. As we add decoder in RISCVCPU, each cpu can have their own decoder, and the decoders can be different due to cpu's features. 2. Improve the decoding efficiency. We run the guard_func to see if the decoder can be added to the dynamic_decoder when building up the decoder. Therefore, there is no need to run the guard_func when decoding each instruction. It can improve the decoding efficiency 3. For vendor or dynamic cpus, it allows them to customize their own decoder functions to improve decoding efficiency, especially when vendor-defined instruction sets increase. Because of dynamic building up, it can skip the other decoder guard functions when decoding. 4. Pre patch for allowing adding a vendor decoder before decode_insn32() with minimal overhead for users that don't need this particular vendor decoder. Signed-off-by: Huang Tao Suggested-by: Christoph Muellner Co-authored-by: LIU Zhiwei Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-ID: <20240506023607.29544-1-eric.huang@linux.alibaba.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 1 + target/riscv/tcg/tcg-cpu.c | 15 +++++++++++++++ target/riscv/tcg/tcg-cpu.h | 15 +++++++++++++++ target/riscv/translate.c | 31 +++++++++++++++---------------- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0d6fb9b4ba..abeb50369c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1134,6 +1134,7 @@ void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) error_propagate(errp, local_err); return; } + riscv_tcg_cpu_finalize_dynamic_decoder(cpu); } else if (kvm_enabled()) { riscv_kvm_cpu_finalize_features(cpu, &local_err); if (local_err != NULL) { diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 746efd099a..04ab0f153a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -455,6 +455,7 @@ struct ArchCPU { uint32_t pmu_avail_ctrs; /* Mapping of events to counters */ GHashTable *pmu_event_ctr_map; + const GPtrArray *decoders; }; /** diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index fedc035313..f59b5d7f2d 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -863,6 +863,21 @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) } } +void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu) +{ + GPtrArray *dynamic_decoders; + dynamic_decoders = g_ptr_array_sized_new(decoder_table_size); + for (size_t i = 0; i < decoder_table_size; ++i) { + if (decoder_table[i].guard_func && + decoder_table[i].guard_func(&cpu->cfg)) { + g_ptr_array_add(dynamic_decoders, + (gpointer)decoder_table[i].riscv_cpu_decode_fn); + } + } + + cpu->decoders = dynamic_decoders; +} + bool riscv_cpu_tcg_compatible(RISCVCPU *cpu) { return object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST) == NULL; diff --git a/target/riscv/tcg/tcg-cpu.h b/target/riscv/tcg/tcg-cpu.h index f7b32417f8..ce94253fe4 100644 --- a/target/riscv/tcg/tcg-cpu.h +++ b/target/riscv/tcg/tcg-cpu.h @@ -26,4 +26,19 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp); bool riscv_cpu_tcg_compatible(RISCVCPU *cpu); +struct DisasContext; +struct RISCVCPUConfig; +typedef struct RISCVDecoder { + bool (*guard_func)(const struct RISCVCPUConfig *); + bool (*riscv_cpu_decode_fn)(struct DisasContext *, uint32_t); +} RISCVDecoder; + +typedef bool (*riscv_cpu_decode_fn)(struct DisasContext *, uint32_t); + +extern const size_t decoder_table_size; + +extern const RISCVDecoder decoder_table[]; + +void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu); + #endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 2c27fd4ce1..4cd6480558 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -35,6 +35,8 @@ #include "exec/helper-info.c.inc" #undef HELPER_H +#include "tcg/tcg-cpu.h" + /* global register indices */ static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ @@ -114,6 +116,7 @@ typedef struct DisasContext { /* FRM is known to contain a valid value. */ bool frm_valid; bool insn_start_updated; + const GPtrArray *decoders; } DisasContext; static inline bool has_ext(DisasContext *ctx, uint32_t ext) @@ -1123,21 +1126,16 @@ static inline int insn_len(uint16_t first_word) return (first_word & 3) == 3 ? 4 : 2; } +const RISCVDecoder decoder_table[] = { + { always_true_p, decode_insn32 }, + { has_xthead_p, decode_xthead}, + { has_XVentanaCondOps_p, decode_XVentanaCodeOps}, +}; + +const size_t decoder_table_size = ARRAY_SIZE(decoder_table); + static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) { - /* - * A table with predicate (i.e., guard) functions and decoder functions - * that are tested in-order until a decoder matches onto the opcode. - */ - static const struct { - bool (*guard_func)(const RISCVCPUConfig *); - bool (*decode_func)(DisasContext *, uint32_t); - } decoders[] = { - { always_true_p, decode_insn32 }, - { has_xthead_p, decode_xthead }, - { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, - }; - ctx->virt_inst_excp = false; ctx->cur_insn_len = insn_len(opcode); /* Check for compressed insn */ @@ -1158,9 +1156,9 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) ctx->base.pc_next + 2)); ctx->opcode = opcode32; - for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { - if (decoders[i].guard_func(ctx->cfg_ptr) && - decoders[i].decode_func(ctx, opcode32)) { + for (guint i = 0; i < ctx->decoders->len; ++i) { + riscv_cpu_decode_fn func = g_ptr_array_index(ctx->decoders, i); + if (func(ctx, opcode32)) { return; } } @@ -1205,6 +1203,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); ctx->zero = tcg_constant_tl(0); ctx->virt_inst_excp = false; + ctx->decoders = cpu->decoders; } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) From fd53ee268d43a0c16814a2c0d4c7ebcf688cfe09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20M=C3=BCllner?= Date: Mon, 29 Apr 2024 09:36:56 +0200 Subject: [PATCH 1086/2884] riscv: thead: Add th.sxstatus CSR emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The th.sxstatus CSR can be used to identify available custom extension on T-Head CPUs. The CSR is documented here: https://github.com/T-head-Semi/thead-extension-spec/blob/master/xtheadsxstatus.adoc An important property of this patch is, that the th.sxstatus MAEE field is not set (indicating that XTheadMae is not available). XTheadMae is a memory attribute extension (similar to Svpbmt) which is implemented in many T-Head CPUs (C906, C910, etc.) and utilizes bits in PTEs that are marked as reserved. QEMU maintainers prefer to not implement XTheadMae, so we need give kernels a mechanism to identify if XTheadMae is available in a system or not. And this patch introduces this mechanism in QEMU in a way that's compatible with real HW (i.e., probing the th.sxstatus.MAEE bit). Further context can be found on the list: https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg00775.html Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Signed-off-by: Christoph Müllner Message-ID: <20240429073656.2486732-1-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + target/riscv/cpu.c | 1 + target/riscv/cpu.h | 3 ++ target/riscv/meson.build | 1 + target/riscv/th_csr.c | 79 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 target/riscv/th_csr.c diff --git a/MAINTAINERS b/MAINTAINERS index 448dc951c5..e9d861e8ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -343,6 +343,7 @@ L: qemu-riscv@nongnu.org S: Supported F: target/riscv/insn_trans/trans_xthead.c.inc F: target/riscv/xthead*.decode +F: target/riscv/th_* F: disas/riscv-xthead* RISC-V XVentanaCondOps extension diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index abeb50369c..2946ac298a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -547,6 +547,7 @@ static void rv64_thead_c906_cpu_init(Object *obj) cpu->cfg.mvendorid = THEAD_VENDOR_ID; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_SV39); + th_register_custom_csrs(cpu); #endif /* inherited from parent obj via riscv_cpu_init() */ diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 04ab0f153a..12d8b5344a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -826,4 +826,7 @@ target_ulong riscv_new_csr_seed(target_ulong new_value, uint8_t satp_mode_max_from_map(uint32_t map); const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); +/* Implemented in th_csr.c */ +void th_register_custom_csrs(RISCVCPU *cpu); + #endif /* RISCV_CPU_H */ diff --git a/target/riscv/meson.build b/target/riscv/meson.build index a5e0734e7f..a4bd61e52a 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -33,6 +33,7 @@ riscv_system_ss.add(files( 'monitor.c', 'machine.c', 'pmu.c', + 'th_csr.c', 'time_helper.c', 'riscv-qmp-cmds.c', )) diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c new file mode 100644 index 0000000000..6c970d4e81 --- /dev/null +++ b/target/riscv/th_csr.c @@ -0,0 +1,79 @@ +/* + * T-Head-specific CSRs. + * + * Copyright (c) 2024 VRULL GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_vendorid.h" + +#define CSR_TH_SXSTATUS 0x5c0 + +/* TH_SXSTATUS bits */ +#define TH_SXSTATUS_UCME BIT(16) +#define TH_SXSTATUS_MAEE BIT(21) +#define TH_SXSTATUS_THEADISAEE BIT(22) + +typedef struct { + int csrno; + int (*insertion_test)(RISCVCPU *cpu); + riscv_csr_operations csr_ops; +} riscv_csr; + +static RISCVException smode(CPURISCVState *env, int csrno) +{ + if (riscv_has_ext(env, RVS)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} + +static int test_thead_mvendorid(RISCVCPU *cpu) +{ + if (cpu->cfg.mvendorid != THEAD_VENDOR_ID) { + return -1; + } + + return 0; +} + +static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* We don't set MAEE here, because QEMU does not implement MAEE. */ + *val = TH_SXSTATUS_UCME | TH_SXSTATUS_THEADISAEE; + return RISCV_EXCP_NONE; +} + +static riscv_csr th_csr_list[] = { + { + .csrno = CSR_TH_SXSTATUS, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.sxstatus", smode, read_th_sxstatus } + } +}; + +void th_register_custom_csrs(RISCVCPU *cpu) +{ + for (size_t i = 0; i < ARRAY_SIZE(th_csr_list); i++) { + int csrno = th_csr_list[i].csrno; + riscv_csr_operations *csr_ops = &th_csr_list[i].csr_ops; + if (!th_csr_list[i].insertion_test(cpu)) { + riscv_set_csr_ops(csrno, csr_ops); + } + } +} From 17b713c0806e72cd8edc6c2ddd8acc5be0475df6 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 22 Mar 2024 17:25:55 +0800 Subject: [PATCH 1087/2884] target/riscv: rvv: Fix Zvfhmin checking for vfwcvt.f.f.v and vfncvt.f.f.w instructions According v spec 18.4, only the vfwcvt.f.f.v and vfncvt.f.f.w instructions will be affected by Zvfhmin extension. And the vfwcvt.f.f.v and vfncvt.f.f.w instructions only support the conversions of * From 1*SEW(16/32) to 2*SEW(32/64) * From 2*SEW(32/64) to 1*SEW(16/32) Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Cc: qemu-stable Message-ID: <20240322092600.1198921-2-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index eec2939e23..678b34b759 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -50,6 +50,22 @@ static bool require_rvf(DisasContext *s) } } +static bool require_rvfmin(DisasContext *s) +{ + if (s->mstatus_fs == EXT_STATUS_DISABLED) { + return false; + } + + switch (s->sew) { + case MO_16: + return s->cfg_ptr->ext_zvfhmin; + case MO_32: + return s->cfg_ptr->ext_zve32f; + default: + return false; + } +} + static bool require_scale_rvf(DisasContext *s) { if (s->mstatus_fs == EXT_STATUS_DISABLED) { @@ -75,8 +91,6 @@ static bool require_scale_rvfmin(DisasContext *s) } switch (s->sew) { - case MO_8: - return s->cfg_ptr->ext_zvfhmin; case MO_16: return s->cfg_ptr->ext_zve32f; case MO_32: @@ -2685,6 +2699,7 @@ static bool opxfv_widen_check(DisasContext *s, arg_rmr *a) static bool opffv_widen_check(DisasContext *s, arg_rmr *a) { return opfv_widen_check(s, a) && + require_rvfmin(s) && require_scale_rvfmin(s) && (s->sew != MO_8); } @@ -2790,6 +2805,7 @@ static bool opfxv_narrow_check(DisasContext *s, arg_rmr *a) static bool opffv_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && + require_rvfmin(s) && require_scale_rvfmin(s) && (s->sew != MO_8); } From 7a999d4dd704aa71fe6416871ada69438b56b1e5 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 22 Mar 2024 17:25:56 +0800 Subject: [PATCH 1088/2884] target/riscv: rvv: Check single width operator for vector fp widen instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The require_scale_rvf function only checks the double width operator for the vector floating point widen instructions, so most of the widen checking functions need to add require_rvf for single width operator. The vfwcvt.f.x.v and vfwcvt.f.xu.v instructions convert single width integer to double width float, so the opfxv_widen_check function doesn’t need require_rvf for the single width operator(integer). Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Cc: qemu-stable Message-ID: <20240322092600.1198921-3-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 678b34b759..a7217aed4e 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -2331,6 +2331,7 @@ GEN_OPFVF_TRANS(vfrsub_vf, opfvf_check) static bool opfvv_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && @@ -2370,6 +2371,7 @@ GEN_OPFVV_WIDEN_TRANS(vfwsub_vv, opfvv_widen_check) static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && @@ -2402,6 +2404,7 @@ GEN_OPFVF_WIDEN_TRANS(vfwsub_vf) static bool opfwv_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && @@ -2441,6 +2444,7 @@ GEN_OPFWV_WIDEN_TRANS(vfwsub_wv) static bool opfwf_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && @@ -2941,6 +2945,7 @@ GEN_OPFVV_TRANS(vfredmin_vs, freduction_check) static bool freduction_widen_check(DisasContext *s, arg_rmrr *a) { return reduction_widen_check(s, a) && + require_rvf(s) && require_scale_rvf(s) && (s->sew != MO_8); } From 692f33a3abcaae789b08623e7cbdffcd2c738c89 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 22 Mar 2024 17:25:57 +0800 Subject: [PATCH 1089/2884] target/riscv: rvv: Check single width operator for vfncvt.rod.f.f.w The opfv_narrow_check needs to check the single width float operator by require_rvf. Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Cc: qemu-stable Message-ID: <20240322092600.1198921-4-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index a7217aed4e..c3af38af80 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -2817,6 +2817,7 @@ static bool opffv_narrow_check(DisasContext *s, arg_rmr *a) static bool opffv_rod_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && + require_rvf(s) && require_scale_rvf(s) && (s->sew != MO_8); } From 93cb52b7a3ccc64e8d28813324818edae07e21d5 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 22 Mar 2024 17:25:58 +0800 Subject: [PATCH 1090/2884] target/riscv: rvv: Remove redudant SEW checking for vector fp narrow/widen instructions If the checking functions check both the single and double width operators at the same time, then the single width operator checking functions (require_rvf[min]) will check whether the SEW is 8. Signed-off-by: Max Chou Reviewed-by: Daniel Henrique Barboza Cc: qemu-stable Message-ID: <20240322092600.1198921-5-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index c3af38af80..3a3896ba06 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -2333,7 +2333,6 @@ static bool opfvv_widen_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && require_scale_rvf(s) && - (s->sew != MO_8) && vext_check_isa_ill(s) && vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm); } @@ -2373,7 +2372,6 @@ static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && require_scale_rvf(s) && - (s->sew != MO_8) && vext_check_isa_ill(s) && vext_check_ds(s, a->rd, a->rs2, a->vm); } @@ -2406,7 +2404,6 @@ static bool opfwv_widen_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && require_scale_rvf(s) && - (s->sew != MO_8) && vext_check_isa_ill(s) && vext_check_dds(s, a->rd, a->rs1, a->rs2, a->vm); } @@ -2446,7 +2443,6 @@ static bool opfwf_widen_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && require_scale_rvf(s) && - (s->sew != MO_8) && vext_check_isa_ill(s) && vext_check_dd(s, a->rd, a->rs2, a->vm); } @@ -2704,8 +2700,7 @@ static bool opffv_widen_check(DisasContext *s, arg_rmr *a) { return opfv_widen_check(s, a) && require_rvfmin(s) && - require_scale_rvfmin(s) && - (s->sew != MO_8); + require_scale_rvfmin(s); } #define GEN_OPFV_WIDEN_TRANS(NAME, CHECK, HELPER, FRM) \ @@ -2810,16 +2805,14 @@ static bool opffv_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && require_rvfmin(s) && - require_scale_rvfmin(s) && - (s->sew != MO_8); + require_scale_rvfmin(s); } static bool opffv_rod_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && require_rvf(s) && - require_scale_rvf(s) && - (s->sew != MO_8); + require_scale_rvf(s); } #define GEN_OPFV_NARROW_TRANS(NAME, CHECK, HELPER, FRM) \ @@ -2947,8 +2940,7 @@ static bool freduction_widen_check(DisasContext *s, arg_rmrr *a) { return reduction_widen_check(s, a) && require_rvf(s) && - require_scale_rvf(s) && - (s->sew != MO_8); + require_scale_rvf(s); } GEN_OPFVV_WIDEN_TRANS(vfwredusum_vs, freduction_widen_check) From 68e7c86927afa240fa450578cb3a4f18926153e4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Sat, 13 Apr 2024 13:59:28 +0300 Subject: [PATCH 1091/2884] target/riscv: prioritize pmp errors in raise_mmu_exception() raise_mmu_exception(), as is today, is prioritizing guest page faults by checking first if virt_enabled && !first_stage, and then considering the regular inst/load/store faults. There's no mention in the spec about guest page fault being a higher priority that PMP faults. In fact, privileged spec section 3.7.1 says: "Attempting to fetch an instruction from a PMP region that does not have execute permissions raises an instruction access-fault exception. Attempting to execute a load or load-reserved instruction which accesses a physical address within a PMP region without read permissions raises a load access-fault exception. Attempting to execute a store, store-conditional, or AMO instruction which accesses a physical address within a PMP region without write permissions raises a store access-fault exception." So, in fact, we're doing it wrong - PMP faults should always be thrown, regardless of also being a first or second stage fault. The way riscv_cpu_tlb_fill() and get_physical_address() work is adequate: a TRANSLATE_PMP_FAIL error is immediately reported and reflected in the 'pmp_violation' flag. What we need is to change raise_mmu_exception() to prioritize it. Reported-by: Joseph Chan Fixes: 82d53adfbb ("target/riscv/cpu_helper.c: Invalid exception on MMU translation stage") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240413105929.7030-1-alexei.filippov@syntacore.com> Cc: qemu-stable Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index d71245a8cb..574886a694 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1177,28 +1177,30 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, switch (access_type) { case MMU_INST_FETCH: - if (env->virt_enabled && !first_stage) { + if (pmp_violation) { + cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT; + } else if (env->virt_enabled && !first_stage) { cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT; } else { - cs->exception_index = pmp_violation ? - RISCV_EXCP_INST_ACCESS_FAULT : RISCV_EXCP_INST_PAGE_FAULT; + cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT; } break; case MMU_DATA_LOAD: - if (two_stage && !first_stage) { + if (pmp_violation) { + cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; + } else if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; } else { - cs->exception_index = pmp_violation ? - RISCV_EXCP_LOAD_ACCESS_FAULT : RISCV_EXCP_LOAD_PAGE_FAULT; + cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT; } break; case MMU_DATA_STORE: - if (two_stage && !first_stage) { + if (pmp_violation) { + cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } else if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; } else { - cs->exception_index = pmp_violation ? - RISCV_EXCP_STORE_AMO_ACCESS_FAULT : - RISCV_EXCP_STORE_PAGE_FAULT; + cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT; } break; default: From 6c9a344247132ac6c3d0eb9670db45149a29c88f Mon Sep 17 00:00:00 2001 From: Alexei Filippov Date: Fri, 3 May 2024 13:30:52 +0300 Subject: [PATCH 1092/2884] target/riscv: do not set mtval2 for non guest-page faults Previous patch fixed the PMP priority in raise_mmu_exception() but we're still setting mtval2 incorrectly. In riscv_cpu_tlb_fill(), after pmp check in 2 stage translation part, mtval2 will be set in case of successes 2 stage translation but failed pmp check. In this case we gonna set mtval2 via env->guest_phys_fault_addr in context of riscv_cpu_tlb_fill(), as this was a guest-page-fault, but it didn't and mtval2 should be zero, according to RISCV privileged spec sect. 9.4.4: When a guest page-fault is taken into M-mode, mtval2 is written with either zero or guest physical address that faulted, shifted by 2 bits. *For other traps, mtval2 is set to zero...* Signed-off-by: Alexei Filippov Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240503103052.6819-1-alexei.filippov@syntacore.com> Cc: qemu-stable Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 574886a694..a02497d778 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1376,17 +1376,17 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, __func__, pa, ret, prot_pmp, tlb_size); prot &= prot_pmp; - } - - if (ret != TRANSLATE_SUCCESS) { + } else { /* * Guest physical address translation failed, this is a HS * level exception */ first_stage_error = false; - env->guest_phys_fault_addr = (im_address | - (address & - (TARGET_PAGE_SIZE - 1))) >> 2; + if (ret != TRANSLATE_PMP_FAIL) { + env->guest_phys_fault_addr = (im_address | + (address & + (TARGET_PAGE_SIZE - 1))) >> 2; + } } } } else { From 73ef14b1277d4c9d79bfe7cb080c09ddba18044f Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 14 May 2024 12:02:17 +0100 Subject: [PATCH 1093/2884] target/riscv: Remove experimental prefix from "B" extension This extension has now been ratified: https://jira.riscv.org/browse/RVS-2006 so the "x-" prefix can be removed. Since this is now a ratified extension add it to the list of extensions included in the "max" CPU variant. Signed-off-by: Rob Bradford Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Message-ID: <20240514110217.22516-1-rbradford@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- target/riscv/tcg/tcg-cpu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2946ac298a..cee6fc4a9a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1400,7 +1400,7 @@ static const MISAExtInfo misa_ext_info_arr[] = { MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"), MISA_EXT_INFO(RVV, "v", "Vector operations"), MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"), - MISA_EXT_INFO(RVB, "x-b", "Bit manipulation (Zba_Zbb_Zbs)") + MISA_EXT_INFO(RVB, "b", "Bit manipulation (Zba_Zbb_Zbs)") }; static void riscv_cpu_validate_misa_mxl(RISCVCPUClass *mcc) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index f59b5d7f2d..683f604d9f 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1301,7 +1301,7 @@ static void riscv_init_max_cpu_extensions(Object *obj) const RISCVCPUMultiExtConfig *prop; /* Enable RVG, RVJ and RVV that are disabled by default */ - riscv_cpu_set_misa_ext(env, env->misa_ext | RVG | RVJ | RVV); + riscv_cpu_set_misa_ext(env, env->misa_ext | RVB | RVG | RVJ | RVV); for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { isa_ext_update_enabled(cpu, prop->offset, true); From c5eb8d6336741dbcb98efcc347f8265bf60bc9d1 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 14 May 2024 12:39:10 +1000 Subject: [PATCH 1094/2884] target/riscv: rvzicbo: Fixup CBO extension register calculation When running the instruction ``` cbo.flush 0(x0) ``` QEMU would segfault. The issue was in cpu_gpr[a->rs1] as QEMU does not have cpu_gpr[0] allocated. In order to fix this let's use the existing get_address() helper. This also has the benefit of performing pointer mask calculations on the address specified in rs1. The pointer masking specificiation specifically states: """ Cache Management Operations: All instructions in Zicbom, Zicbop and Zicboz """ So this is the correct behaviour and we previously have been incorrectly not masking the address. Signed-off-by: Alistair Francis Reported-by: Fabian Thomas Fixes: e05da09b7cfd ("target/riscv: implement Zicbom extension") Reviewed-by: Richard Henderson Cc: qemu-stable Message-ID: <20240514023910.301766-1-alistair.francis@wdc.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvzicbo.c.inc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvzicbo.c.inc b/target/riscv/insn_trans/trans_rvzicbo.c.inc index d5d7095903..15711c3140 100644 --- a/target/riscv/insn_trans/trans_rvzicbo.c.inc +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -31,27 +31,35 @@ static bool trans_cbo_clean(DisasContext *ctx, arg_cbo_clean *a) { REQUIRE_ZICBOM(ctx); - gen_helper_cbo_clean_flush(tcg_env, cpu_gpr[a->rs1]); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_clean_flush(tcg_env, src); return true; } static bool trans_cbo_flush(DisasContext *ctx, arg_cbo_flush *a) { REQUIRE_ZICBOM(ctx); - gen_helper_cbo_clean_flush(tcg_env, cpu_gpr[a->rs1]); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_clean_flush(tcg_env, src); return true; } static bool trans_cbo_inval(DisasContext *ctx, arg_cbo_inval *a) { REQUIRE_ZICBOM(ctx); - gen_helper_cbo_inval(tcg_env, cpu_gpr[a->rs1]); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_inval(tcg_env, src); return true; } static bool trans_cbo_zero(DisasContext *ctx, arg_cbo_zero *a) { REQUIRE_ZICBOZ(ctx); - gen_helper_cbo_zero(tcg_env, cpu_gpr[a->rs1]); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_zero(tcg_env, src); return true; } From 190b867f28cb5781f3cd01a3deb371e4211595b1 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Wed, 15 May 2024 17:11:28 +0800 Subject: [PATCH 1095/2884] target/riscv/kvm.c: Fix the hart bit setting of AIA In AIA spec, each hart (or each hart within a group) has a unique hart number to locate the memory pages of interrupt files in the address space. The number of bits required to represent any hart number is equal to ceil(log2(hmax + 1)), where hmax is the largest hart number among groups. However, if the largest hart number among groups is a power of 2, QEMU will pass an inaccurate hart-index-bit setting to Linux. For example, when the guest OS has 4 harts, only ceil(log2(3 + 1)) = 2 bits are sufficient to represent 4 harts, but we passes 3 to Linux. The code needs to be updated to ensure accurate hart-index-bit settings. Additionally, a Linux patch[1] is necessary to correctly recover the hart index when the guest OS has only 1 hart, where the hart-index-bit is 0. [1] https://lore.kernel.org/lkml/20240415064905.25184-1-yongxuan.wang@sifive.com/t/ Signed-off-by: Yong-Xuan Wang Reviewed-by: Andrew Jones Cc: qemu-stable Message-ID: <20240515091129.28116-1-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 473416649f..235e2cdaca 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1777,7 +1777,14 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, } } - hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1; + + if (max_hart_per_socket > 1) { + max_hart_per_socket--; + hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1; + } else { + hart_bits = 0; + } + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, KVM_DEV_RISCV_AIA_CONFIG_HART_BITS, &hart_bits, true, NULL); From 583edc4efb7f4075212bdee281f336edfa532e3f Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 17 May 2024 17:30:54 -0300 Subject: [PATCH 1096/2884] riscv, gdbstub.c: fix reg_width in ricsv_gen_dynamic_vector_feature() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 33a24910ae changed 'reg_width' to use 'vlenb', i.e. vector length in bytes, when in this context we want 'reg_width' as the length in bits. Fix 'reg_width' back to the value in bits like 7cb59921c05a ("target/riscv/gdbstub.c: use 'vlenb' instead of shifting 'vlen'") set beforehand. While we're at it, rename 'reg_width' to 'bitsize' to provide a bit more clarity about what the variable represents. 'bitsize' is also used in riscv_gen_dynamic_csr_feature() with the same purpose, i.e. as an input to gdb_feature_builder_append_reg(). Cc: Akihiko Odaki Cc: Alex Bennée Reported-by: Robin Dapp Fixes: 33a24910ae ("target/riscv: Use GDBFeature for dynamic XML") Signed-off-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Acked-by: Alex Bennée Reviewed-by: Akihiko Odaki Reviewed-by: Alistair Francis Cc: qemu-stable Message-ID: <20240517203054.880861-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/gdbstub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index d0cc5762c2..c07df972f1 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -288,7 +288,7 @@ static GDBFeature *riscv_gen_dynamic_csr_feature(CPUState *cs, int base_reg) static GDBFeature *ricsv_gen_dynamic_vector_feature(CPUState *cs, int base_reg) { RISCVCPU *cpu = RISCV_CPU(cs); - int reg_width = cpu->cfg.vlenb; + int bitsize = cpu->cfg.vlenb << 3; GDBFeatureBuilder builder; int i; @@ -298,7 +298,7 @@ static GDBFeature *ricsv_gen_dynamic_vector_feature(CPUState *cs, int base_reg) /* First define types and totals in a whole VL */ for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { - int count = reg_width / vec_lanes[i].size; + int count = bitsize / vec_lanes[i].size; gdb_feature_builder_append_tag( &builder, "", vec_lanes[i].id, vec_lanes[i].gdb_type, count); @@ -316,7 +316,7 @@ static GDBFeature *ricsv_gen_dynamic_vector_feature(CPUState *cs, int base_reg) /* Define vector registers */ for (i = 0; i < 32; i++) { gdb_feature_builder_append_reg(&builder, g_strdup_printf("v%d", i), - reg_width, i, "riscv_vector", "vector"); + bitsize, i, "riscv_vector", "vector"); } gdb_feature_builder_end(&builder); From 915758c537b5fe09575291f4acd87e2d377a93de Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 14 May 2024 15:16:15 +1000 Subject: [PATCH 1097/2884] disas/riscv: Decode all of the pmpcfg and pmpaddr CSRs Previously we only listed a single pmpcfg CSR and the first 16 pmpaddr CSRs. This patch fixes this to list all 16 pmpcfg and all 64 pmpaddr CSRs are part of the disassembly. Reported-by: Eric DeVolder Signed-off-by: Alistair Francis Fixes: ea10325917 ("RISC-V Disassembler") Reviewed-by: Daniel Henrique Barboza Cc: qemu-stable Message-ID: <20240514051615.330979-1-alistair.francis@wdc.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index e236c8b5b7..297cfa2f63 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2190,7 +2190,22 @@ static const char *csr_name(int csrno) case 0x0383: return "mibound"; case 0x0384: return "mdbase"; case 0x0385: return "mdbound"; - case 0x03a0: return "pmpcfg3"; + case 0x03a0: return "pmpcfg0"; + case 0x03a1: return "pmpcfg1"; + case 0x03a2: return "pmpcfg2"; + case 0x03a3: return "pmpcfg3"; + case 0x03a4: return "pmpcfg4"; + case 0x03a5: return "pmpcfg5"; + case 0x03a6: return "pmpcfg6"; + case 0x03a7: return "pmpcfg7"; + case 0x03a8: return "pmpcfg8"; + case 0x03a9: return "pmpcfg9"; + case 0x03aa: return "pmpcfg10"; + case 0x03ab: return "pmpcfg11"; + case 0x03ac: return "pmpcfg12"; + case 0x03ad: return "pmpcfg13"; + case 0x03ae: return "pmpcfg14"; + case 0x03af: return "pmpcfg15"; case 0x03b0: return "pmpaddr0"; case 0x03b1: return "pmpaddr1"; case 0x03b2: return "pmpaddr2"; @@ -2207,6 +2222,54 @@ static const char *csr_name(int csrno) case 0x03bd: return "pmpaddr13"; case 0x03be: return "pmpaddr14"; case 0x03bf: return "pmpaddr15"; + case 0x03c0: return "pmpaddr16"; + case 0x03c1: return "pmpaddr17"; + case 0x03c2: return "pmpaddr18"; + case 0x03c3: return "pmpaddr19"; + case 0x03c4: return "pmpaddr20"; + case 0x03c5: return "pmpaddr21"; + case 0x03c6: return "pmpaddr22"; + case 0x03c7: return "pmpaddr23"; + case 0x03c8: return "pmpaddr24"; + case 0x03c9: return "pmpaddr25"; + case 0x03ca: return "pmpaddr26"; + case 0x03cb: return "pmpaddr27"; + case 0x03cc: return "pmpaddr28"; + case 0x03cd: return "pmpaddr29"; + case 0x03ce: return "pmpaddr30"; + case 0x03cf: return "pmpaddr31"; + case 0x03d0: return "pmpaddr32"; + case 0x03d1: return "pmpaddr33"; + case 0x03d2: return "pmpaddr34"; + case 0x03d3: return "pmpaddr35"; + case 0x03d4: return "pmpaddr36"; + case 0x03d5: return "pmpaddr37"; + case 0x03d6: return "pmpaddr38"; + case 0x03d7: return "pmpaddr39"; + case 0x03d8: return "pmpaddr40"; + case 0x03d9: return "pmpaddr41"; + case 0x03da: return "pmpaddr42"; + case 0x03db: return "pmpaddr43"; + case 0x03dc: return "pmpaddr44"; + case 0x03dd: return "pmpaddr45"; + case 0x03de: return "pmpaddr46"; + case 0x03df: return "pmpaddr47"; + case 0x03e0: return "pmpaddr48"; + case 0x03e1: return "pmpaddr49"; + case 0x03e2: return "pmpaddr50"; + case 0x03e3: return "pmpaddr51"; + case 0x03e4: return "pmpaddr52"; + case 0x03e5: return "pmpaddr53"; + case 0x03e6: return "pmpaddr54"; + case 0x03e7: return "pmpaddr55"; + case 0x03e8: return "pmpaddr56"; + case 0x03e9: return "pmpaddr57"; + case 0x03ea: return "pmpaddr58"; + case 0x03eb: return "pmpaddr59"; + case 0x03ec: return "pmpaddr60"; + case 0x03ed: return "pmpaddr61"; + case 0x03ee: return "pmpaddr62"; + case 0x03ef: return "pmpaddr63"; case 0x0780: return "mtohost"; case 0x0781: return "mfromhost"; case 0x0782: return "mreset"; From cdba3b901ac0b480eb9d940219113f0be80cc4e1 Mon Sep 17 00:00:00 2001 From: Minwoo Im Date: Tue, 28 May 2024 11:31:05 +0900 Subject: [PATCH 1098/2884] hw/ufs: Update MCQ-related fields to block/ufs.h This patch is a prep patch for the following MCQ support patch for hw/ufs. This patch updated minimal mandatory fields to support MCQ based on UFSHCI 4.0. Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-2-minwoo.im@samsung.com> Signed-off-by: Jeuk Kim --- include/block/ufs.h | 108 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/include/block/ufs.h b/include/block/ufs.h index d61598b8f3..3513b6e772 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -7,7 +7,7 @@ typedef struct QEMU_PACKED UfsReg { uint32_t cap; - uint32_t rsvd0; + uint32_t mcqcap; uint32_t ver; uint32_t rsvd1; uint32_t hcpid; @@ -46,6 +46,13 @@ typedef struct QEMU_PACKED UfsReg { uint32_t rsvd7[4]; uint32_t rsvd8[16]; uint32_t ccap; + uint32_t rsvd9[127]; + uint32_t config; + uint32_t rsvd10[3]; + uint32_t rsvd11[28]; + uint32_t mcqconfig; + uint32_t esilba; + uint32_t esiuba; } UfsReg; REG32(CAP, offsetof(UfsReg, cap)) @@ -57,6 +64,15 @@ REG32(CAP, offsetof(UfsReg, cap)) FIELD(CAP, OODDS, 25, 1) FIELD(CAP, UICDMETMS, 26, 1) FIELD(CAP, CS, 28, 1) + FIELD(CAP, LSDBS, 29, 1) + FIELD(CAP, MCQS, 30, 1) +REG32(MCQCAP, offsetof(UfsReg, mcqcap)) + FIELD(MCQCAP, MAXQ, 0, 8) + FIELD(MCQCAP, SP, 8, 1) + FIELD(MCQCAP, RRP, 9, 1) + FIELD(MCQCAP, EIS, 10, 1) + FIELD(MCQCAP, QCFGPTR, 16, 8) + FIELD(MCQCAP, MIAG, 24, 8) REG32(VER, offsetof(UfsReg, ver)) REG32(HCPID, offsetof(UfsReg, hcpid)) REG32(HCMID, offsetof(UfsReg, hcmid)) @@ -78,6 +94,7 @@ REG32(IS, offsetof(UfsReg, is)) FIELD(IS, HCFES, 16, 1) FIELD(IS, SBFES, 17, 1) FIELD(IS, CEFES, 18, 1) + FIELD(IS, CQES, 20, 1) REG32(IE, offsetof(UfsReg, ie)) FIELD(IE, UTRCE, 0, 1) FIELD(IE, UDEPRIE, 1, 1) @@ -95,6 +112,7 @@ REG32(IE, offsetof(UfsReg, ie)) FIELD(IE, HCFEE, 16, 1) FIELD(IE, SBFEE, 17, 1) FIELD(IE, CEFEE, 18, 1) + FIELD(IE, CQEE, 20, 1) REG32(HCS, offsetof(UfsReg, hcs)) FIELD(HCS, DP, 0, 1) FIELD(HCS, UTRLRDY, 1, 1) @@ -128,6 +146,10 @@ REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1)) REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2)) REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3)) REG32(CCAP, offsetof(UfsReg, ccap)) +REG32(CONFIG, offsetof(UfsReg, config)) + FIELD(CONFIG, QT, 0, 1) +REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig)) + FIELD(MCQCONFIG, MAC, 8, 8) #define UFS_INTR_MASK \ ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ @@ -157,6 +179,69 @@ REG32(CCAP, offsetof(UfsReg, ccap)) ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \ UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK) +typedef struct QEMU_PACKED UfsMcqReg { + uint32_t sqattr; + uint32_t sqlba; + uint32_t squba; + uint32_t sqdao; + uint32_t sqisao; + uint32_t sqcfg; + uint32_t rsvd0[2]; + uint32_t cqattr; + uint32_t cqlba; + uint32_t cquba; + uint32_t cqdao; + uint32_t cqisao; + uint32_t cqcfg; + uint32_t rsvd1[2]; +} UfsMcqReg; + +REG32(SQATTR, offsetof(UfsMcqReg, sqattr)) + FIELD(SQATTR, SIZE, 0, 16) + FIELD(SQATTR, CQID, 16, 8) + FIELD(SQATTR, SQPL, 28, 3) + FIELD(SQATTR, SQEN, 31, 1) +REG32(SQLBA, offsetof(UfsMcqReg, sqlba)) +REG32(SQUBA, offsetof(UfsMcqReg, squba)) +REG32(SQDAO, offsetof(UfsMcqReg, sqdao)) +REG32(SQISAO, offsetof(UfsMcqReg, sqisao)) +REG32(SQCFG, offsetof(UfsMcqReg, sqcfg)) +REG32(CQATTR, offsetof(UfsMcqReg, cqattr)) + FIELD(CQATTR, SIZE, 0, 16) + FIELD(CQATTR, CQEN, 31, 1) +REG32(CQLBA, offsetof(UfsMcqReg, cqlba)) +REG32(CQUBA, offsetof(UfsMcqReg, cquba)) +REG32(CQDAO, offsetof(UfsMcqReg, cqdao)) +REG32(CQISAO, offsetof(UfsMcqReg, cqisao)) +REG32(CQCFG, offsetof(UfsMcqReg, cqcfg)) + +typedef struct QEMU_PACKED UfsMcqSqReg { + uint32_t hp; + uint32_t tp; + uint32_t rtc; + uint32_t cti; + uint32_t rts; +} UfsMcqSqReg; + +typedef struct QEMU_PACKED UfsMcqCqReg { + uint32_t hp; + uint32_t tp; +} UfsMcqCqReg; + +typedef struct QEMU_PACKED UfsMcqSqIntReg { + uint32_t is; + uint32_t ie; +} UfsMcqSqIntReg; + +typedef struct QEMU_PACKED UfsMcqCqIntReg { + uint32_t is; + uint32_t ie; + uint32_t iacr; +} UfsMcqCqIntReg; + +REG32(CQIS, offsetof(UfsMcqCqIntReg, is)) + FIELD(CQIS, TEPS, 0, 1) + typedef struct QEMU_PACKED DeviceDescriptor { uint8_t length; uint8_t descriptor_idn; @@ -1064,9 +1149,26 @@ typedef struct QEMU_PACKED UtpUpiuRsp { }; } UtpUpiuRsp; +/* + * MCQ Completion Queue Entry + */ +typedef UtpTransferReqDesc UfsSqEntry; +typedef struct QEMU_PACKED UfsCqEntry { + uint64_t utp_addr; + uint16_t resp_len; + uint16_t resp_off; + uint16_t prdt_len; + uint16_t prdt_off; + uint8_t status; + uint8_t error; + uint16_t rsvd1; + uint32_t rsvd2[3]; +} UfsCqEntry; + static inline void _ufs_check_size(void) { - QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104); + QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x38C); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqReg) != 64); QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89); QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87); QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45); @@ -1086,5 +1188,7 @@ static inline void _ufs_check_size(void) QEMU_BUILD_BUG_ON(sizeof(UtpTaskReqDesc) != 80); QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40); QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288); + QEMU_BUILD_BUG_ON(sizeof(UfsSqEntry) != 32); + QEMU_BUILD_BUG_ON(sizeof(UfsCqEntry) != 32); } #endif From 5c079578d2e46df626d13eeb629c7d761a5c4e44 Mon Sep 17 00:00:00 2001 From: Minwoo Im Date: Tue, 28 May 2024 11:31:06 +0900 Subject: [PATCH 1099/2884] hw/ufs: Add support MCQ of UFSHCI 4.0 This patch adds support for MCQ defined in UFSHCI 4.0. This patch utilized the legacy I/O codes as much as possible to support MCQ. MCQ operation & runtime register is placed at 0x1000 offset of UFSHCI register statically with no spare space among four registers (48B): UfsMcqSqReg, UfsMcqSqIntReg, UfsMcqCqReg, UfsMcqCqIntReg The maxinum number of queue is 32 as per spec, and the default MAC(Multiple Active Commands) are 32 in the device. Example: -device ufs,serial=foo,id=ufs0,mcq=true,mcq-maxq=8 Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-3-minwoo.im@samsung.com> Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c | 475 ++++++++++++++++++++++++++++++++++++++++++-- hw/ufs/ufs.h | 98 ++++++++- include/block/ufs.h | 23 ++- 4 files changed, 593 insertions(+), 20 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 665e1a942b..531dcfc686 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -11,13 +11,18 @@ ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" +ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8"" +ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" +ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" # error condition ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_read_sq(uint8_t sqid, uint64_t addr) "failed to read sq entry. sqid %"PRIu8", hwaddr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_dma_write_cq(uint8_t cqid, uint64_t addr) "failed to write cq entry. cqid %"PRIu8", hwaddr %"PRIu64"" ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" @@ -31,3 +36,15 @@ ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8"" ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8"" ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8"" +ufs_err_mcq_db_wr_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_db_wr_invalid_db(uint8_t qid, uint32_t db) "invalid mcq doorbell sqid %"PRIu8", db %"PRIu32"" +ufs_err_mcq_create_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_create_sq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_sq_already_exists(uint8_t qid) "mcq sqid %"PRIu8 "already exists" +ufs_err_mcq_delete_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_delete_sq_not_exists(uint8_t qid) "mcq sqid %"PRIu8 "not exists" +ufs_err_mcq_create_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exists" +ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists" +ufs_err_mcq_delete_cq_sq_not_deleted(uint8_t sqid, uint8_t cqid) "mcq sq %"PRIu8" still has cq %"PRIu8"" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index bac78a32bb..71a88d221c 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -9,7 +9,7 @@ */ /** - * Reference Specs: https://www.jedec.org/, 3.1 + * Reference Specs: https://www.jedec.org/, 4.0 * * Usage * ----- @@ -28,10 +28,45 @@ #include "trace.h" #include "ufs.h" -/* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +/* The QEMU-UFS device follows spec version 4.0 */ +#define UFS_SPEC_VER 0x0400 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +#define UFS_MCQ_QCFGPTR 2 + +static void ufs_exec_req(UfsRequest *req); +static void ufs_clear_req(UfsRequest *req); + +static inline uint64_t ufs_mcq_reg_addr(UfsHc *u, int qid) +{ + /* Submission Queue MCQ Registers offset (400h) */ + return (UFS_MCQ_QCFGPTR * 0x200) + qid * 0x40; +} + +static inline uint64_t ufs_mcq_op_reg_addr(UfsHc *u, int qid) +{ + /* MCQ Operation & Runtime Registers offset (1000h) */ + return UFS_MCQ_OPR_START + qid * 48; +} + +static inline uint64_t ufs_reg_size(UfsHc *u) +{ + /* Total UFS HCI Register size in bytes */ + return ufs_mcq_op_reg_addr(u, 0) + sizeof(u->mcq_op_reg); +} + +static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr) +{ + uint64_t mcq_reg_addr = ufs_mcq_reg_addr(u, 0); + return addr >= mcq_reg_addr && addr < mcq_reg_addr + sizeof(u->mcq_reg); +} + +static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr) +{ + uint64_t mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0); + return (addr >= mcq_op_reg_addr && + addr < mcq_op_reg_addr + sizeof(u->mcq_op_reg)); +} static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) { @@ -181,9 +216,14 @@ static MemTxResult ufs_dma_read_upiu(UfsRequest *req) { MemTxResult ret; - ret = ufs_dma_read_utrd(req); - if (ret) { - return ret; + /* + * In case of MCQ, UTRD has already been read from a SQ, so skip it. + */ + if (!ufs_mcq_req(req)) { + ret = ufs_dma_read_utrd(req); + if (ret) { + return ret; + } } ret = ufs_dma_read_req_upiu(req); @@ -335,6 +375,219 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val) ufs_irq_check(u); } +static void ufs_mcq_init_req(UfsHc *u, UfsRequest *req, UfsSq *sq) +{ + memset(req, 0, sizeof(*req)); + + req->hc = u; + req->state = UFS_REQUEST_IDLE; + req->slot = UFS_INVALID_SLOT; + req->sq = sq; +} + +static void ufs_mcq_process_sq(void *opaque) +{ + UfsSq *sq = opaque; + UfsHc *u = sq->u; + UfsSqEntry sqe; + UfsRequest *req; + hwaddr addr; + uint16_t head = ufs_mcq_sq_head(u, sq->sqid); + int err; + + while (!(ufs_mcq_sq_empty(u, sq->sqid) || QTAILQ_EMPTY(&sq->req_list))) { + addr = sq->addr + head; + err = ufs_addr_read(sq->u, addr, (void *)&sqe, sizeof(sqe)); + if (err) { + trace_ufs_err_dma_read_sq(sq->sqid, addr); + return; + } + + head = (head + sizeof(sqe)) % (sq->size * sizeof(sqe)); + ufs_mcq_update_sq_head(u, sq->sqid, head); + + req = QTAILQ_FIRST(&sq->req_list); + QTAILQ_REMOVE(&sq->req_list, req, entry); + + ufs_mcq_init_req(sq->u, req, sq); + memcpy(&req->utrd, &sqe, sizeof(req->utrd)); + + req->state = UFS_REQUEST_RUNNING; + ufs_exec_req(req); + } +} + +static void ufs_mcq_process_cq(void *opaque) +{ + UfsCq *cq = opaque; + UfsHc *u = cq->u; + UfsRequest *req, *next; + MemTxResult ret; + uint32_t tail = ufs_mcq_cq_tail(u, cq->cqid); + + QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) + { + ufs_dma_write_rsp_upiu(req); + + req->cqe.utp_addr = + ((uint64_t)req->utrd.command_desc_base_addr_hi << 32ULL) | + req->utrd.command_desc_base_addr_lo; + req->cqe.utp_addr |= req->sq->sqid; + req->cqe.resp_len = req->utrd.response_upiu_length; + req->cqe.resp_off = req->utrd.response_upiu_offset; + req->cqe.prdt_len = req->utrd.prd_table_length; + req->cqe.prdt_off = req->utrd.prd_table_offset; + req->cqe.status = req->utrd.header.dword_2 & 0xf; + req->cqe.error = 0; + + ret = ufs_addr_write(u, cq->addr + tail, &req->cqe, sizeof(req->cqe)); + if (ret) { + trace_ufs_err_dma_write_cq(cq->cqid, cq->addr + tail); + } + QTAILQ_REMOVE(&cq->req_list, req, entry); + + tail = (tail + sizeof(req->cqe)) % (cq->size * sizeof(req->cqe)); + ufs_mcq_update_cq_tail(u, cq->cqid, tail); + + ufs_clear_req(req); + QTAILQ_INSERT_TAIL(&req->sq->req_list, req, entry); + } + + if (!ufs_mcq_cq_empty(u, cq->cqid)) { + u->mcq_op_reg[cq->cqid].cq_int.is = + FIELD_DP32(u->mcq_op_reg[cq->cqid].cq_int.is, CQIS, TEPS, 1); + + u->reg.is = FIELD_DP32(u->reg.is, IS, CQES, 1); + ufs_irq_check(u); + } +} + +static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr) +{ + UfsMcqReg *reg = &u->mcq_reg[qid]; + UfsSq *sq; + uint8_t cqid = FIELD_EX32(attr, SQATTR, CQID); + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_create_sq_invalid_sqid(qid); + return false; + } + + if (u->sq[qid]) { + trace_ufs_err_mcq_create_sq_already_exists(qid); + return false; + } + + if (!u->cq[cqid]) { + trace_ufs_err_mcq_create_sq_invalid_cqid(qid); + return false; + } + + sq = g_malloc0(sizeof(*sq)); + sq->u = u; + sq->sqid = qid; + sq->cq = u->cq[cqid]; + sq->addr = ((uint64_t)reg->squba << 32) | reg->sqlba; + sq->size = ((FIELD_EX32(attr, SQATTR, SIZE) + 1) << 2) / sizeof(UfsSqEntry); + + sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq, + &DEVICE(u)->mem_reentrancy_guard); + sq->req = g_new0(UfsRequest, sq->size); + QTAILQ_INIT(&sq->req_list); + for (int i = 0; i < sq->size; i++) { + ufs_mcq_init_req(u, &sq->req[i], sq); + QTAILQ_INSERT_TAIL(&sq->req_list, &sq->req[i], entry); + } + + u->sq[qid] = sq; + + trace_ufs_mcq_create_sq(sq->sqid, sq->cq->cqid, sq->addr, sq->size); + return true; +} + +static bool ufs_mcq_delete_sq(UfsHc *u, uint8_t qid) +{ + UfsSq *sq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_delete_sq_invalid_sqid(qid); + return false; + } + + if (!u->sq[qid]) { + trace_ufs_err_mcq_delete_sq_not_exists(qid); + return false; + } + + sq = u->sq[qid]; + + qemu_bh_delete(sq->bh); + g_free(sq->req); + g_free(sq); + u->sq[qid] = NULL; + return true; +} + +static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr) +{ + UfsMcqReg *reg = &u->mcq_reg[qid]; + UfsCq *cq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_create_cq_invalid_cqid(qid); + return false; + } + + if (u->cq[qid]) { + trace_ufs_err_mcq_create_cq_already_exists(qid); + return false; + } + + cq = g_malloc0(sizeof(*cq)); + cq->u = u; + cq->cqid = qid; + cq->addr = ((uint64_t)reg->cquba << 32) | reg->cqlba; + cq->size = ((FIELD_EX32(attr, CQATTR, SIZE) + 1) << 2) / sizeof(UfsCqEntry); + + cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq, + &DEVICE(u)->mem_reentrancy_guard); + QTAILQ_INIT(&cq->req_list); + + u->cq[qid] = cq; + + trace_ufs_mcq_create_cq(cq->cqid, cq->addr, cq->size); + return true; +} + +static bool ufs_mcq_delete_cq(UfsHc *u, uint8_t qid) +{ + UfsCq *cq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_delete_cq_invalid_cqid(qid); + return false; + } + + if (!u->cq[qid]) { + trace_ufs_err_mcq_delete_cq_not_exists(qid); + return false; + } + + for (int i = 0; i < ARRAY_SIZE(u->sq); i++) { + if (u->sq[i] && u->sq[i]->cq->cqid == qid) { + trace_ufs_err_mcq_delete_cq_sq_not_deleted(i, qid); + return false; + } + } + + cq = u->cq[qid]; + + qemu_bh_delete(cq->bh); + g_free(cq); + u->cq[qid] = NULL; + return true; +} + static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) { switch (offset) { @@ -390,6 +643,12 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) case A_UCMDARG3: u->reg.ucmdarg3 = data; break; + case A_CONFIG: + u->reg.config = data; + break; + case A_MCQCONFIG: + u->reg.mcqconfig = data; + break; case A_UTRLCLR: case A_UTMRLDBR: case A_UTMRLCLR: @@ -402,18 +661,138 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) } } +static void ufs_write_mcq_reg(UfsHc *u, hwaddr offset, uint32_t data, + unsigned size) +{ + int qid = offset / sizeof(UfsMcqReg); + UfsMcqReg *reg = &u->mcq_reg[qid]; + + switch (offset % sizeof(UfsMcqReg)) { + case A_SQATTR: + if (!FIELD_EX32(reg->sqattr, SQATTR, SQEN) && + FIELD_EX32(data, SQATTR, SQEN)) { + if (!ufs_mcq_create_sq(u, qid, data)) { + break; + } + } else if (FIELD_EX32(reg->sqattr, SQATTR, SQEN) && + !FIELD_EX32(data, SQATTR, SQEN)) { + if (!ufs_mcq_delete_sq(u, qid)) { + break; + } + } + reg->sqattr = data; + break; + case A_SQLBA: + reg->sqlba = data; + break; + case A_SQUBA: + reg->squba = data; + break; + case A_SQCFG: + reg->sqcfg = data; + break; + case A_CQATTR: + if (!FIELD_EX32(reg->cqattr, CQATTR, CQEN) && + FIELD_EX32(data, CQATTR, CQEN)) { + if (!ufs_mcq_create_cq(u, qid, data)) { + break; + } + } else if (FIELD_EX32(reg->cqattr, CQATTR, CQEN) && + !FIELD_EX32(data, CQATTR, CQEN)) { + if (!ufs_mcq_delete_cq(u, qid)) { + break; + } + } + reg->cqattr = data; + break; + case A_CQLBA: + reg->cqlba = data; + break; + case A_CQUBA: + reg->cquba = data; + break; + case A_CQCFG: + reg->cqcfg = data; + break; + case A_SQDAO: + case A_SQISAO: + case A_CQDAO: + case A_CQISAO: + trace_ufs_err_unsupport_register_offset(offset); + break; + default: + trace_ufs_err_invalid_register_offset(offset); + break; + } +} + +static void ufs_mcq_process_db(UfsHc *u, uint8_t qid, uint32_t db) +{ + UfsSq *sq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_db_wr_invalid_sqid(qid); + return; + } + + sq = u->sq[qid]; + if (sq->size * sizeof(UfsSqEntry) <= db) { + trace_ufs_err_mcq_db_wr_invalid_db(qid, db); + return; + } + + ufs_mcq_update_sq_tail(u, sq->sqid, db); + qemu_bh_schedule(sq->bh); +} + +static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, + unsigned size) +{ + int qid = offset / sizeof(UfsMcqOpReg); + UfsMcqOpReg *opr = &u->mcq_op_reg[qid]; + + switch (offset % sizeof(UfsMcqOpReg)) { + case offsetof(UfsMcqOpReg, sq.tp): + if (opr->sq.tp != data) { + ufs_mcq_process_db(u, qid, data); + } + opr->sq.tp = data; + break; + case offsetof(UfsMcqOpReg, cq.hp): + opr->cq.hp = data; + ufs_mcq_update_cq_head(u, qid, data); + break; + case offsetof(UfsMcqOpReg, cq_int.is): + opr->cq_int.is &= ~data; + break; + default: + trace_ufs_err_invalid_register_offset(offset); + break; + } +} + static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size) { UfsHc *u = (UfsHc *)opaque; - uint8_t *ptr = (uint8_t *)&u->reg; + uint8_t *ptr; uint64_t value; + uint64_t offset; - if (addr > sizeof(u->reg) - size) { + if (addr < sizeof(u->reg)) { + offset = addr; + ptr = (uint8_t *)&u->reg; + } else if (ufs_is_mcq_reg(u, addr)) { + offset = addr - ufs_mcq_reg_addr(u, 0); + ptr = (uint8_t *)&u->mcq_reg; + } else if (ufs_is_mcq_op_reg(u, addr)) { + offset = addr - ufs_mcq_op_reg_addr(u, 0); + ptr = (uint8_t *)&u->mcq_op_reg; + } else { trace_ufs_err_invalid_register_offset(addr); return 0; } - value = *(uint32_t *)(ptr + addr); + value = *(uint32_t *)(ptr + offset); trace_ufs_mmio_read(addr, value, size); return value; } @@ -423,13 +802,17 @@ static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data, { UfsHc *u = (UfsHc *)opaque; - if (addr > sizeof(u->reg) - size) { - trace_ufs_err_invalid_register_offset(addr); - return; - } - trace_ufs_mmio_write(addr, data, size); - ufs_write_reg(u, addr, data, size); + + if (addr < sizeof(u->reg)) { + ufs_write_reg(u, addr, data, size); + } else if (ufs_is_mcq_reg(u, addr)) { + ufs_write_mcq_reg(u, addr - ufs_mcq_reg_addr(u, 0), data, size); + } else if (ufs_is_mcq_op_reg(u, addr)) { + ufs_write_mcq_op_reg(u, addr - ufs_mcq_op_reg_addr(u, 0), data, size); + } else { + trace_ufs_err_invalid_register_offset(addr); + } } static const MemoryRegionOps ufs_mmio_ops = { @@ -1086,9 +1469,16 @@ void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_CMD_TABLE_ATTR); } - trace_ufs_complete_req(req->slot); req->state = UFS_REQUEST_COMPLETE; - qemu_bh_schedule(u->complete_bh); + + if (ufs_mcq_req(req)) { + trace_ufs_mcq_complete_req(req->sq->sqid); + QTAILQ_INSERT_TAIL(&req->sq->cq->req_list, req, entry); + qemu_bh_schedule(req->sq->cq->bh); + } else { + trace_ufs_complete_req(req->slot); + qemu_bh_schedule(u->complete_bh); + } } static void ufs_clear_req(UfsRequest *req) @@ -1158,6 +1548,11 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp) return false; } + if (u->params.mcq_maxq >= UFS_MAX_MCQ_QNUM) { + error_setg(errp, "mcq-maxq must be less than %d", UFS_MAX_MCQ_QNUM); + return false; + } + return true; } @@ -1189,15 +1584,24 @@ static void ufs_init_state(UfsHc *u) &DEVICE(u)->mem_reentrancy_guard); u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u, &DEVICE(u)->mem_reentrancy_guard); + + if (u->params.mcq) { + memset(u->sq, 0, sizeof(u->sq)); + memset(u->cq, 0, sizeof(u->cq)); + } } static void ufs_init_hc(UfsHc *u) { uint32_t cap = 0; + uint32_t mcqconfig = 0; + uint32_t mcqcap = 0; - u->reg_size = pow2ceil(sizeof(UfsReg)); + u->reg_size = pow2ceil(ufs_reg_size(u)); memset(&u->reg, 0, sizeof(u->reg)); + memset(&u->mcq_reg, 0, sizeof(u->mcq_reg)); + memset(&u->mcq_op_reg, 0, sizeof(u->mcq_op_reg)); cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1)); cap = FIELD_DP32(cap, CAP, RTT, 2); cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1)); @@ -1206,7 +1610,29 @@ static void ufs_init_hc(UfsHc *u) cap = FIELD_DP32(cap, CAP, OODDS, 0); cap = FIELD_DP32(cap, CAP, UICDMETMS, 0); cap = FIELD_DP32(cap, CAP, CS, 0); + cap = FIELD_DP32(cap, CAP, LSDBS, 1); + cap = FIELD_DP32(cap, CAP, MCQS, u->params.mcq); u->reg.cap = cap; + + if (u->params.mcq) { + mcqconfig = FIELD_DP32(mcqconfig, MCQCONFIG, MAC, 0x1f); + u->reg.mcqconfig = mcqconfig; + + mcqcap = FIELD_DP32(mcqcap, MCQCAP, MAXQ, u->params.mcq_maxq - 1); + mcqcap = FIELD_DP32(mcqcap, MCQCAP, RRP, 1); + mcqcap = FIELD_DP32(mcqcap, MCQCAP, QCFGPTR, UFS_MCQ_QCFGPTR); + u->reg.mcqcap = mcqcap; + + for (int i = 0; i < ARRAY_SIZE(u->mcq_reg); i++) { + uint64_t addr = ufs_mcq_op_reg_addr(u, i); + u->mcq_reg[i].sqdao = addr; + u->mcq_reg[i].sqisao = addr + sizeof(UfsMcqSqReg); + addr += sizeof(UfsMcqSqReg); + u->mcq_reg[i].cqdao = addr + sizeof(UfsMcqSqIntReg); + addr += sizeof(UfsMcqSqIntReg); + u->mcq_reg[i].cqisao = addr + sizeof(UfsMcqCqReg); + } + } u->reg.ver = UFS_SPEC_VER; memset(&u->device_desc, 0, sizeof(DeviceDescriptor)); @@ -1288,12 +1714,25 @@ static void ufs_exit(PCIDevice *pci_dev) ufs_clear_req(&u->req_list[i]); } g_free(u->req_list); + + for (int i = 0; i < ARRAY_SIZE(u->sq); i++) { + if (u->sq[i]) { + ufs_mcq_delete_sq(u, i); + } + } + for (int i = 0; i < ARRAY_SIZE(u->cq); i++) { + if (u->cq[i]) { + ufs_mcq_delete_cq(u, i); + } + } } static Property ufs_props[] = { DEFINE_PROP_STRING("serial", UfsHc, params.serial), DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), + DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index 8fda94f4ef..6c9382cbc4 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -16,6 +16,7 @@ #include "block/ufs.h" #define UFS_MAX_LUS 32 +#define UFS_MAX_MCQ_QNUM 32 #define UFS_BLOCK_SIZE_SHIFT 12 #define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT) @@ -45,10 +46,11 @@ typedef enum UfsReqResult { UFS_REQUEST_NO_COMPLETE = 2, } UfsReqResult; +#define UFS_INVALID_SLOT (-1) typedef struct UfsRequest { struct UfsHc *hc; UfsRequestState state; - int slot; + int slot; /* -1 when it's a MCQ request */ UtpTransferReqDesc utrd; UtpUpiuReq req_upiu; @@ -57,8 +59,18 @@ typedef struct UfsRequest { /* for scsi command */ QEMUSGList *sg; uint32_t data_len; + + /* for MCQ */ + struct UfsSq *sq; + struct UfsCqEntry cqe; + QTAILQ_ENTRY(UfsRequest) entry; } UfsRequest; +static inline bool ufs_mcq_req(UfsRequest *req) +{ + return req->sq != NULL; +} + struct UfsLu; typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *); @@ -76,13 +88,43 @@ typedef struct UfsParams { char *serial; uint8_t nutrs; /* Number of UTP Transfer Request Slots */ uint8_t nutmrs; /* Number of UTP Task Management Request Slots */ + bool mcq; /* Multiple Command Queue support */ + uint8_t mcq_qcfgptr; /* MCQ Queue Configuration Pointer in MCQCAP */ + uint8_t mcq_maxq; /* MCQ Maximum number of Queues */ } UfsParams; +/* + * MCQ Properties + */ +typedef struct UfsSq { + struct UfsHc *u; + uint8_t sqid; + struct UfsCq *cq; + uint64_t addr; + uint16_t size; /* A number of entries (qdepth) */ + + QEMUBH *bh; /* Bottom half to process requests in async */ + UfsRequest *req; + QTAILQ_HEAD(, UfsRequest) req_list; /* Free request list */ +} UfsSq; + +typedef struct UfsCq { + struct UfsHc *u; + uint8_t cqid; + uint64_t addr; + uint16_t size; /* A number of entries (qdepth) */ + + QEMUBH *bh; + QTAILQ_HEAD(, UfsRequest) req_list; +} UfsCq; + typedef struct UfsHc { PCIDevice parent_obj; UfsBus bus; MemoryRegion iomem; UfsReg reg; + UfsMcqReg mcq_reg[UFS_MAX_MCQ_QNUM]; + UfsMcqOpReg mcq_op_reg[UFS_MAX_MCQ_QNUM]; UfsParams params; uint32_t reg_size; UfsRequest *req_list; @@ -100,8 +142,62 @@ typedef struct UfsHc { qemu_irq irq; QEMUBH *doorbell_bh; QEMUBH *complete_bh; + + /* MCQ properties */ + UfsSq *sq[UFS_MAX_MCQ_QNUM]; + UfsCq *cq[UFS_MAX_MCQ_QNUM]; } UfsHc; +static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].sq.tp; +} + +static inline void ufs_mcq_update_sq_tail(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].sq.tp = db; +} + +static inline uint32_t ufs_mcq_sq_head(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].sq.hp; +} + +static inline void ufs_mcq_update_sq_head(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].sq.hp = db; +} + +static inline bool ufs_mcq_sq_empty(UfsHc *u, uint32_t qid) +{ + return ufs_mcq_sq_tail(u, qid) == ufs_mcq_sq_head(u, qid); +} + +static inline uint32_t ufs_mcq_cq_tail(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].cq.tp; +} + +static inline void ufs_mcq_update_cq_tail(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].cq.tp = db; +} + +static inline uint32_t ufs_mcq_cq_head(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].cq.hp; +} + +static inline void ufs_mcq_update_cq_head(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].cq.hp = db; +} + +static inline bool ufs_mcq_cq_empty(UfsHc *u, uint32_t qid) +{ + return ufs_mcq_cq_tail(u, qid) == ufs_mcq_cq_head(u, qid); +} + #define TYPE_UFS "ufs" #define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS) diff --git a/include/block/ufs.h b/include/block/ufs.h index 3513b6e772..92da7a89b9 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -152,7 +152,8 @@ REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig)) FIELD(MCQCONFIG, MAC, 8, 8) #define UFS_INTR_MASK \ - ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ + ((1 << R_IS_CQES_SHIFT) | \ + (1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ (1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \ (1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) | \ (1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \ @@ -242,6 +243,21 @@ typedef struct QEMU_PACKED UfsMcqCqIntReg { REG32(CQIS, offsetof(UfsMcqCqIntReg, is)) FIELD(CQIS, TEPS, 0, 1) +/* + * Provide MCQ Operation & Runtime Registers as a contiguous addressed + * registers for the simplicity. + * DAO(Doorbell Address Offset) and ISAO(Interrupt Status Register Address + * Offset) registers should be properly configured with the following + * structure. + */ +#define UFS_MCQ_OPR_START 0x1000 +typedef struct QEMU_PACKED UfsMcqOpReg { + UfsMcqSqReg sq; + UfsMcqSqIntReg sq_int; + UfsMcqCqReg cq; + UfsMcqCqIntReg cq_int; +} UfsMcqOpReg; + typedef struct QEMU_PACKED DeviceDescriptor { uint8_t length; uint8_t descriptor_idn; @@ -1169,6 +1185,11 @@ static inline void _ufs_check_size(void) { QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x38C); QEMU_BUILD_BUG_ON(sizeof(UfsMcqReg) != 64); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqSqReg) != 20); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqCqReg) != 8); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqSqIntReg) != 8); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqCqIntReg) != 12); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqOpReg) != 48); QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89); QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87); QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45); From a73c99378022ebb785481e84cfe1e81097546268 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 17 May 2024 21:50:15 -0500 Subject: [PATCH 1100/2884] iotests: test NBD+TLS+iothread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent regressions when using NBD with TLS in the presence of iothreads, adding coverage the fix to qio channels made in the previous patch. The shell function pick_unused_port() was copied from nbdkit.git/tests/functions.sh.in, where it had all authors from Red Hat, agreeing to the resulting relicensing from 2-clause BSD to GPLv2. CC: qemu-stable@nongnu.org CC: "Richard W.M. Jones" Signed-off-by: Eric Blake Message-ID: <20240531180639.1392905-6-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé --- tests/qemu-iotests/tests/nbd-tls-iothread | 168 ++++++++++++++++++ tests/qemu-iotests/tests/nbd-tls-iothread.out | 54 ++++++ 2 files changed, 222 insertions(+) create mode 100755 tests/qemu-iotests/tests/nbd-tls-iothread create mode 100644 tests/qemu-iotests/tests/nbd-tls-iothread.out diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread b/tests/qemu-iotests/tests/nbd-tls-iothread new file mode 100755 index 0000000000..a2fb07206e --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-tls-iothread @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test of NBD+TLS+iothread +# +# Copyright (C) 2024 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=eblake@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$dst_image" + tls_x509_cleanup +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu +. ./common.tls +. ./common.nbd + +_supported_fmt qcow2 # Hardcoded to qcow2 command line and QMP below +_supported_proto file + +# pick_unused_port +# +# Picks and returns an "unused" port, setting the global variable +# $port. +# +# This is inherently racy, but we need it because qemu does not currently +# permit NBD+TLS over a Unix domain socket +pick_unused_port () +{ + if ! (ss --version) >/dev/null 2>&1; then + _notrun "ss utility required, skipped this test" + fi + + # Start at a random port to make it less likely that two parallel + # tests will conflict. + port=$(( 50000 + (RANDOM%15000) )) + while ss -ltn | grep -sqE ":$port\b"; do + ((port++)) + if [ $port -eq 65000 ]; then port=50000; fi + done + echo picked unused port +} + +tls_x509_init + +size=1G +DST_IMG="$TEST_DIR/dst.qcow2" + +echo +echo "== preparing TLS creds and spare port ==" + +pick_unused_port +tls_x509_create_root_ca "ca1" +tls_x509_create_server "ca1" "server1" +tls_x509_create_client "ca1" "client1" +tls_obj_base=tls-creds-x509,id=tls0,verify-peer=true,dir="${tls_dir}" + +echo +echo "== preparing image ==" + +_make_test_img $size +$QEMU_IMG create -f qcow2 "$DST_IMG" $size | _filter_img_create + +echo +echo === Starting Src QEMU === +echo + +_launch_qemu -machine q35 \ + -object iothread,id=iothread0 \ + -object "${tls_obj_base}"/client1,endpoint=client \ + -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true, + "bus":"pcie.0"}' \ + -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0", + "bus":"root0", "iothread":"iothread0"}' \ + -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1", + "bus":"virtio_scsi_pci0.0"}' \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$TEST_IMG"'", "node-name":"drive_sys1"}' \ + -blockdev '{"driver":"qcow2", "node-name":"drive_image1", + "file":"drive_sys1"}' +h1=$QEMU_HANDLE +_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return' + +echo +echo === Starting Dst VM2 === +echo + +_launch_qemu -machine q35 \ + -object iothread,id=iothread0 \ + -object "${tls_obj_base}"/server1,endpoint=server \ + -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true, + "bus":"pcie.0"}' \ + -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0", + "bus":"root0", "iothread":"iothread0"}' \ + -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1", + "bus":"virtio_scsi_pci0.0"}' \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$DST_IMG"'", "node-name":"drive_sys1"}' \ + -blockdev '{"driver":"qcow2", "node-name":"drive_image1", + "file":"drive_sys1"}' \ + -incoming defer +h2=$QEMU_HANDLE +_send_qemu_cmd $h2 '{"execute": "qmp_capabilities"}' 'return' + +echo +echo === Dst VM: Enable NBD server for incoming storage migration === +echo + +_send_qemu_cmd $h2 '{"execute": "nbd-server-start", "arguments": + {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": "'$port'"}}, + "tls-creds": "tls0"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g" +_send_qemu_cmd $h2 '{"execute": "block-export-add", "arguments": + {"node-name": "drive_image1", "type": "nbd", "writable": true, + "id": "drive_image1"}}' '{"return": {}}' + +echo +echo === Src VM: Mirror to dst NBD for outgoing storage migration === +echo + +_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments": + {"node-name": "mirror", "driver": "nbd", + "server": {"type": "inet", "host": "127.0.0.1", "port": "'$port'"}, + "export": "drive_image1", "tls-creds": "tls0", + "tls-hostname": "127.0.0.1"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g" +_send_qemu_cmd $h1 '{"execute": "blockdev-mirror", "arguments": + {"sync": "full", "device": "drive_image1", "target": "mirror", + "job-id": "drive_image1_53"}}' '{"return": {}}' +_timed_wait_for $h1 '"ready"' + +echo +echo === Cleaning up === +echo + +_send_qemu_cmd $h1 '{"execute":"quit"}' '' +_send_qemu_cmd $h2 '{"execute":"quit"}' '' + +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread.out b/tests/qemu-iotests/tests/nbd-tls-iothread.out new file mode 100644 index 0000000000..1d83d4f903 --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-tls-iothread.out @@ -0,0 +1,54 @@ +QA output created by nbd-tls-iothread + +== preparing TLS creds and spare port == +picked unused port +Generating a self signed certificate... +Generating a signed certificate... +Generating a signed certificate... + +== preparing image == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/dst.IMGFMT', fmt=IMGFMT size=1073741824 + +=== Starting Src QEMU === + +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Starting Dst VM2 === + +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Dst VM: Enable NBD server for incoming storage migration === + +{"execute": "nbd-server-start", "arguments": + {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": PORT}}, + "tls-creds": "tls0"}} +{"return": {}} +{"execute": "block-export-add", "arguments": + {"node-name": "drive_image1", "type": "nbd", "writable": true, + "id": "drive_image1"}} +{"return": {}} + +=== Src VM: Mirror to dst NBD for outgoing storage migration === + +{"execute": "blockdev-add", "arguments": + {"node-name": "mirror", "driver": "nbd", + "server": {"type": "inet", "host": "127.0.0.1", "port": PORT}, + "export": "drive_image1", "tls-creds": "tls0", + "tls-hostname": "127.0.0.1"}} +{"return": {}} +{"execute": "blockdev-mirror", "arguments": + {"sync": "full", "device": "drive_image1", "target": "mirror", + "job-id": "drive_image1_53"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "drive_image1_53"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "drive_image1_53"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "drive_image1_53"}} + +=== Cleaning up === + +{"execute":"quit"} +{"execute":"quit"} +*** done From 52a7ff526964e7810ec1ccc71efbdd60952dd20b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:44 +0900 Subject: [PATCH 1101/2884] tap: Remove tap_probe_vnet_hdr_len() It was necessary since an Linux older than 2.6.35 may implement the virtio-net header but may not allow to change its length. Remove it since such an old Linux is no longer supported. Signed-off-by: Akihiko Odaki Acked-by: Michael S. Tsirkin Signed-off-by: Jason Wang --- net/tap-bsd.c | 5 ----- net/tap-linux.c | 20 -------------------- net/tap-solaris.c | 5 ----- net/tap-stub.c | 5 ----- net/tap.c | 8 ++------ net/tap_int.h | 1 - 6 files changed, 2 insertions(+), 42 deletions(-) diff --git a/net/tap-bsd.c b/net/tap-bsd.c index 274ea7bd2c..b4c84441ba 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -217,11 +217,6 @@ int tap_probe_has_uso(int fd) return 0; } -int tap_probe_vnet_hdr_len(int fd, int len) -{ - return 0; -} - void tap_fd_set_vnet_hdr_len(int fd, int len) { } diff --git a/net/tap-linux.c b/net/tap-linux.c index c7e514ecb0..1226d5fda2 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -185,26 +185,6 @@ int tap_probe_has_uso(int fd) return 1; } -/* Verify that we can assign given length */ -int tap_probe_vnet_hdr_len(int fd, int len) -{ - int orig; - if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) { - return 0; - } - if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { - return 0; - } - /* Restore original length: we can't handle failure. */ - if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) { - fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n", - strerror(errno)); - abort(); - return -errno; - } - return 1; -} - void tap_fd_set_vnet_hdr_len(int fd, int len) { if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 08b13af512..51b7830bef 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -221,11 +221,6 @@ int tap_probe_has_uso(int fd) return 0; } -int tap_probe_vnet_hdr_len(int fd, int len) -{ - return 0; -} - void tap_fd_set_vnet_hdr_len(int fd, int len) { } diff --git a/net/tap-stub.c b/net/tap-stub.c index 4b24f61e3a..38673434cb 100644 --- a/net/tap-stub.c +++ b/net/tap-stub.c @@ -52,11 +52,6 @@ int tap_probe_has_uso(int fd) return 0; } -int tap_probe_vnet_hdr_len(int fd, int len) -{ - return 0; -} - void tap_fd_set_vnet_hdr_len(int fd, int len) { } diff --git a/net/tap.c b/net/tap.c index baaa2f7a9a..72ae95894f 100644 --- a/net/tap.c +++ b/net/tap.c @@ -259,11 +259,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc) static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - - return !!tap_probe_vnet_hdr_len(s->fd, len); + return tap_has_vnet_hdr(nc); } static int tap_get_vnet_hdr_len(NetClientState *nc) @@ -432,7 +428,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, * Make sure host header length is set correctly in tap: * it might have been modified by another instance of qemu. */ - if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) { + if (vnet_hdr) { tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len); } tap_read_poll(s, true); diff --git a/net/tap_int.h b/net/tap_int.h index 9a2175655b..8857ff299d 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -35,7 +35,6 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen); void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp); int tap_probe_vnet_hdr(int fd, Error **errp); -int tap_probe_vnet_hdr_len(int fd, int len); int tap_probe_has_ufo(int fd); int tap_probe_has_uso(int fd); void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo, From 4b52d63249a508dd927222ffac1a868d38681fc5 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:45 +0900 Subject: [PATCH 1102/2884] tap: Remove qemu_using_vnet_hdr() Since qemu_set_vnet_hdr_len() is always called when qemu_using_vnet_hdr() is called, we can merge them and save some code. For consistency, express that the virtio-net header is not in use by returning 0 with qemu_get_vnet_hdr_len() instead of having a dedicated function, qemu_get_using_vnet_hdr(). Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/e1000e.c | 1 - hw/net/igb.c | 1 - hw/net/net_tx_pkt.c | 4 ++-- hw/net/virtio-net.c | 3 --- hw/net/vmxnet3.c | 2 -- include/net/net.h | 7 ------- net/dump.c | 4 +--- net/net.c | 24 +----------------------- net/netmap.c | 5 ----- net/tap.c | 28 +--------------------------- 10 files changed, 5 insertions(+), 74 deletions(-) diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index edc101eaf6..843892ce09 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -352,7 +352,6 @@ e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr) for (i = 0; i < s->conf.peers.queues; i++) { nc = qemu_get_subqueue(s->nic, i); qemu_set_vnet_hdr_len(nc->peer, sizeof(struct virtio_net_hdr)); - qemu_using_vnet_hdr(nc->peer, true); } } diff --git a/hw/net/igb.c b/hw/net/igb.c index 1ef6170465..b92bba402e 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -349,7 +349,6 @@ igb_init_net_peer(IGBState *s, PCIDevice *pci_dev, uint8_t *macaddr) for (i = 0; i < s->conf.peers.queues; i++) { nc = qemu_get_subqueue(s->nic, i); qemu_set_vnet_hdr_len(nc->peer, sizeof(struct virtio_net_hdr)); - qemu_using_vnet_hdr(nc->peer, true); } } diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index b7b1de816d..1f79b82b77 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -582,7 +582,7 @@ static void net_tx_pkt_sendv( { NetClientState *nc = opaque; - if (qemu_get_using_vnet_hdr(nc->peer)) { + if (qemu_get_vnet_hdr_len(nc->peer)) { qemu_sendv_packet(nc, virt_iov, virt_iov_cnt); } else { qemu_sendv_packet(nc, iov, iov_cnt); @@ -812,7 +812,7 @@ static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) { - bool offload = qemu_get_using_vnet_hdr(nc->peer); + bool offload = qemu_get_vnet_hdr_len(nc->peer); return net_tx_pkt_send_custom(pkt, offload, net_tx_pkt_sendv, nc); } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 24e5e7d347..ff600b3002 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3778,9 +3778,6 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { - for (i = 0; i < n->max_queue_pairs; i++) { - qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); - } n->host_hdr_len = sizeof(struct virtio_net_hdr); } else { n->host_hdr_len = 0; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 707487c636..63a9187773 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2091,8 +2091,6 @@ static void vmxnet3_net_init(VMXNET3State *s) if (s->peer_has_vhdr) { qemu_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer, sizeof(struct virtio_net_hdr)); - - qemu_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1); } qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); diff --git a/include/net/net.h b/include/net/net.h index b1f9b35fcc..6fe5a0aee8 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -57,8 +57,6 @@ typedef bool (HasUfo)(NetClientState *); typedef bool (HasUso)(NetClientState *); typedef bool (HasVnetHdr)(NetClientState *); typedef bool (HasVnetHdrLen)(NetClientState *, int); -typedef bool (GetUsingVnetHdr)(NetClientState *); -typedef void (UsingVnetHdr)(NetClientState *, bool); typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int); typedef int (GetVnetHdrLen)(NetClientState *); typedef void (SetVnetHdrLen)(NetClientState *, int); @@ -88,10 +86,7 @@ typedef struct NetClientInfo { HasUso *has_uso; HasVnetHdr *has_vnet_hdr; HasVnetHdrLen *has_vnet_hdr_len; - GetUsingVnetHdr *get_using_vnet_hdr; - UsingVnetHdr *using_vnet_hdr; SetOffload *set_offload; - GetVnetHdrLen *get_vnet_hdr_len; SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; SetVnetBE *set_vnet_be; @@ -194,8 +189,6 @@ bool qemu_has_ufo(NetClientState *nc); bool qemu_has_uso(NetClientState *nc); bool qemu_has_vnet_hdr(NetClientState *nc); bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); -bool qemu_get_using_vnet_hdr(NetClientState *nc); -void qemu_using_vnet_hdr(NetClientState *nc, bool enable); void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo, int uso4, int uso6); int qemu_get_vnet_hdr_len(NetClientState *nc); diff --git a/net/dump.c b/net/dump.c index 16073f2458..956e34a123 100644 --- a/net/dump.c +++ b/net/dump.c @@ -154,10 +154,8 @@ static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, int iovcnt, NetPacketSent *sent_cb) { NetFilterDumpState *nfds = FILTER_DUMP(nf); - int offset = qemu_get_using_vnet_hdr(nf->netdev) ? - qemu_get_vnet_hdr_len(nf->netdev) : 0; - dump_receive_iov(&nfds->ds, iov, iovcnt, offset); + dump_receive_iov(&nfds->ds, iov, iovcnt, qemu_get_vnet_hdr_len(nf->netdev)); return 0; } diff --git a/net/net.c b/net/net.c index a2f0c828bb..bd51037ebf 100644 --- a/net/net.c +++ b/net/net.c @@ -529,24 +529,6 @@ bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) return nc->info->has_vnet_hdr_len(nc, len); } -bool qemu_get_using_vnet_hdr(NetClientState *nc) -{ - if (!nc || !nc->info->get_using_vnet_hdr) { - return false; - } - - return nc->info->get_using_vnet_hdr(nc); -} - -void qemu_using_vnet_hdr(NetClientState *nc, bool enable) -{ - if (!nc || !nc->info->using_vnet_hdr) { - return; - } - - nc->info->using_vnet_hdr(nc, enable); -} - void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo, int uso4, int uso6) { @@ -559,11 +541,7 @@ void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int qemu_get_vnet_hdr_len(NetClientState *nc) { - if (!nc || !nc->info->get_vnet_hdr_len) { - return 0; - } - - return nc->info->get_vnet_hdr_len(nc); + return nc->vnet_hdr_len; } void qemu_set_vnet_hdr_len(NetClientState *nc, int len) diff --git a/net/netmap.c b/net/netmap.c index 241b27c8e9..297510e190 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -351,10 +351,6 @@ static bool netmap_has_vnet_hdr(NetClientState *nc) return netmap_has_vnet_hdr_len(nc, sizeof(struct virtio_net_hdr)); } -static void netmap_using_vnet_hdr(NetClientState *nc, bool enable) -{ -} - static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); @@ -393,7 +389,6 @@ static NetClientInfo net_netmap_info = { .has_ufo = netmap_has_vnet_hdr, .has_vnet_hdr = netmap_has_vnet_hdr, .has_vnet_hdr_len = netmap_has_vnet_hdr_len, - .using_vnet_hdr = netmap_using_vnet_hdr, .set_offload = netmap_set_offload, .set_vnet_hdr_len = netmap_set_vnet_hdr_len, }; diff --git a/net/tap.c b/net/tap.c index 72ae95894f..c848844955 100644 --- a/net/tap.c +++ b/net/tap.c @@ -262,13 +262,6 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) return tap_has_vnet_hdr(nc); } -static int tap_get_vnet_hdr_len(NetClientState *nc) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - return s->host_vnet_hdr_len; -} - static void tap_set_vnet_hdr_len(NetClientState *nc, int len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -280,23 +273,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len) tap_fd_set_vnet_hdr_len(s->fd, len); s->host_vnet_hdr_len = len; -} - -static bool tap_get_using_vnet_hdr(NetClientState *nc) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - return s->using_vnet_hdr; -} - -static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - assert(!!s->host_vnet_hdr_len == using_vnet_hdr); - - s->using_vnet_hdr = using_vnet_hdr; + s->using_vnet_hdr = true; } static int tap_set_vnet_le(NetClientState *nc, bool is_le) @@ -394,10 +371,7 @@ static NetClientInfo net_tap_info = { .has_uso = tap_has_uso, .has_vnet_hdr = tap_has_vnet_hdr, .has_vnet_hdr_len = tap_has_vnet_hdr_len, - .get_using_vnet_hdr = tap_get_using_vnet_hdr, - .using_vnet_hdr = tap_using_vnet_hdr, .set_offload = tap_set_offload, - .get_vnet_hdr_len = tap_get_vnet_hdr_len, .set_vnet_hdr_len = tap_set_vnet_hdr_len, .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, From a67753710d5fe6c0eef95229ff5fd9cafb78d862 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:46 +0900 Subject: [PATCH 1103/2884] net: Move virtio-net header length assertion The virtio-net header length assertion should happen for any clients. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- net/net.c | 5 +++++ net/tap.c | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/net/net.c b/net/net.c index bd51037ebf..db096765f4 100644 --- a/net/net.c +++ b/net/net.c @@ -56,6 +56,7 @@ #include "net/filter.h" #include "qapi/string-output-visitor.h" #include "qapi/qobject-input-visitor.h" +#include "standard-headers/linux/virtio_net.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -550,6 +551,10 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) return; } + assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || + len == sizeof(struct virtio_net_hdr) || + len == sizeof(struct virtio_net_hdr_v1_hash)); + nc->vnet_hdr_len = len; nc->info->set_vnet_hdr_len(nc, len); } diff --git a/net/tap.c b/net/tap.c index c848844955..49edf6c2b6 100644 --- a/net/tap.c +++ b/net/tap.c @@ -267,9 +267,6 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len) TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || - len == sizeof(struct virtio_net_hdr) || - len == sizeof(struct virtio_net_hdr_v1_hash)); tap_fd_set_vnet_hdr_len(s->fd, len); s->host_vnet_hdr_len = len; From b9ad513e187669db8f5ac238028f6bf25a17c641 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:47 +0900 Subject: [PATCH 1104/2884] net: Remove receive_raw() While netmap implements virtio-net header, it does not implement receive_raw(). Instead of implementing receive_raw for netmap, add virtio-net headers in the common code and use receive_iov()/receive() instead. This also fixes the buffer size for the virtio-net header. Fixes: fbbdbddec0 ("tap: allow extended virtio header with hash info") Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- include/net/net.h | 1 - net/net.c | 18 ++++++++++++------ net/tap.c | 1 - 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/net/net.h b/include/net/net.h index 6fe5a0aee8..c8f679761b 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -72,7 +72,6 @@ typedef struct NetClientInfo { NetClientDriver type; size_t size; NetReceive *receive; - NetReceive *receive_raw; NetReceiveIOV *receive_iov; NetCanReceive *can_receive; NetStart *start; diff --git a/net/net.c b/net/net.c index db096765f4..6938da05e0 100644 --- a/net/net.c +++ b/net/net.c @@ -787,11 +787,7 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, offset = iov_to_buf(iov, iovcnt, 0, buf, offset); } - if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) { - ret = nc->info->receive_raw(nc, buffer, offset); - } else { - ret = nc->info->receive(nc, buffer, offset); - } + ret = nc->info->receive(nc, buffer, offset); g_free(buf); return ret; @@ -806,6 +802,8 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, MemReentrancyGuard *owned_reentrancy_guard; NetClientState *nc = opaque; int ret; + struct virtio_net_hdr_v1_hash vnet_hdr = { }; + g_autofree struct iovec *iov_copy = NULL; if (nc->link_down) { @@ -824,7 +822,15 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, owned_reentrancy_guard->engaged_in_io = true; } - if (nc->info->receive_iov && !(flags & QEMU_NET_PACKET_FLAG_RAW)) { + if ((flags & QEMU_NET_PACKET_FLAG_RAW) && nc->vnet_hdr_len) { + iov_copy = g_new(struct iovec, iovcnt + 1); + iov_copy[0].iov_base = &vnet_hdr; + iov_copy[0].iov_len = nc->vnet_hdr_len; + memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov)); + iov = iov_copy; + } + + if (nc->info->receive_iov) { ret = nc->info->receive_iov(nc, iov, iovcnt); } else { ret = nc_sendv_compat(nc, iov, iovcnt, flags); diff --git a/net/tap.c b/net/tap.c index 49edf6c2b6..99c59ee468 100644 --- a/net/tap.c +++ b/net/tap.c @@ -360,7 +360,6 @@ static NetClientInfo net_tap_info = { .type = NET_CLIENT_DRIVER_TAP, .size = sizeof(TAPState), .receive = tap_receive, - .receive_raw = tap_receive_raw, .receive_iov = tap_receive_iov, .poll = tap_poll, .cleanup = tap_cleanup, From 336a058b26bb5229070f1e10ba0f63ae35420adc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:48 +0900 Subject: [PATCH 1105/2884] tap: Call tap_receive_iov() from tap_receive() This will save duplicate logic found in both of tap_receive_iov() and tap_receive(). Suggested-by: "Zhang, Chen" Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- net/tap.c | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/net/tap.c b/net/tap.c index 99c59ee468..9825518ff1 100644 --- a/net/tap.c +++ b/net/tap.c @@ -133,39 +133,14 @@ static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov, return tap_write_packet(s, iovp, iovcnt); } -static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - struct iovec iov[2]; - int iovcnt = 0; - struct virtio_net_hdr_mrg_rxbuf hdr = { }; - - if (s->host_vnet_hdr_len) { - iov[iovcnt].iov_base = &hdr; - iov[iovcnt].iov_len = s->host_vnet_hdr_len; - iovcnt++; - } - - iov[iovcnt].iov_base = (char *)buf; - iov[iovcnt].iov_len = size; - iovcnt++; - - return tap_write_packet(s, iov, iovcnt); -} - static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); - struct iovec iov[1]; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; - if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { - return tap_receive_raw(nc, buf, size); - } - - iov[0].iov_base = (char *)buf; - iov[0].iov_len = size; - - return tap_write_packet(s, iov, 1); + return tap_receive_iov(nc, &iov, 1); } #ifndef __sun__ From 77db537995be8bcab215a99bc9c1b51066b56557 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:49 +0900 Subject: [PATCH 1106/2884] tap: Shrink zeroed virtio-net header tap prepends a zeroed virtio-net header when writing a packet to a tap with virtio-net header enabled but not in use. This only happens when s->host_vnet_hdr_len == sizeof(struct virtio_net_hdr). Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- net/tap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tap.c b/net/tap.c index 9825518ff1..51f7aec39d 100644 --- a/net/tap.c +++ b/net/tap.c @@ -119,7 +119,7 @@ static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov, TAPState *s = DO_UPCAST(TAPState, nc, nc); const struct iovec *iovp = iov; g_autofree struct iovec *iov_copy = NULL; - struct virtio_net_hdr_mrg_rxbuf hdr = { }; + struct virtio_net_hdr hdr = { }; if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { iov_copy = g_new(struct iovec, iovcnt + 1); From 283be5966eb7ec18fda3e95c979be620dfb8c72a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:50 +0900 Subject: [PATCH 1107/2884] virtio-net: Do not propagate ebpf-rss-fds errors Propagating ebpf-rss-fds errors has several problems. First, it makes device realization fail and disables the fallback to the conventional eBPF loading. Second, it leaks memory by making device realization fail without freeing memory already allocated. Third, the convention is to set an error when a function returns false, but virtio_net_load_ebpf_fds() and virtio_net_load_ebpf() returns false without setting an error, which is confusing. Remove the propagation to fix these problems. Fixes: 0524ea0510a3 ("ebpf: Added eBPF initialization by fds.") Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index ff600b3002..3cee2ef3ac 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1329,24 +1329,22 @@ static void virtio_net_detach_epbf_rss(VirtIONet *n) virtio_net_attach_ebpf_to_backend(n->nic, -1); } -static bool virtio_net_load_ebpf_fds(VirtIONet *n, Error **errp) +static bool virtio_net_load_ebpf_fds(VirtIONet *n) { int fds[EBPF_RSS_MAX_FDS] = { [0 ... EBPF_RSS_MAX_FDS - 1] = -1}; int ret = true; int i = 0; - ERRP_GUARD(); - if (n->nr_ebpf_rss_fds != EBPF_RSS_MAX_FDS) { - error_setg(errp, - "Expected %d file descriptors but got %d", - EBPF_RSS_MAX_FDS, n->nr_ebpf_rss_fds); + warn_report("Expected %d file descriptors but got %d", + EBPF_RSS_MAX_FDS, n->nr_ebpf_rss_fds); return false; } for (i = 0; i < n->nr_ebpf_rss_fds; i++) { - fds[i] = monitor_fd_param(monitor_cur(), n->ebpf_rss_fds[i], errp); - if (*errp) { + fds[i] = monitor_fd_param(monitor_cur(), n->ebpf_rss_fds[i], + &error_warn); + if (fds[i] < 0) { ret = false; goto exit; } @@ -1355,7 +1353,7 @@ static bool virtio_net_load_ebpf_fds(VirtIONet *n, Error **errp) ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3]); exit: - if (!ret || *errp) { + if (!ret) { for (i = 0; i < n->nr_ebpf_rss_fds && fds[i] != -1; i++) { close(fds[i]); } @@ -1364,13 +1362,12 @@ exit: return ret; } -static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) +static bool virtio_net_load_ebpf(VirtIONet *n) { bool ret = false; if (virtio_net_attach_ebpf_to_backend(n->nic, -1)) { - if (!(n->ebpf_rss_fds - && virtio_net_load_ebpf_fds(n, errp))) { + if (!(n->ebpf_rss_fds && virtio_net_load_ebpf_fds(n))) { ret = ebpf_rss_load(&n->ebpf_rss); } } @@ -3809,7 +3806,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) net_rx_pkt_init(&n->rx_pkt); if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { - virtio_net_load_ebpf(n, errp); + virtio_net_load_ebpf(n); } } From 8c49756825dab430b17648637735c2736d23f778 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:51 +0900 Subject: [PATCH 1108/2884] virtio-net: Add only one queue pair when realizing Multiqueue usage is not negotiated yet when realizing. If more than one queue is added and the guest never requests to enable multiqueue, the extra queues will not be deleted when unrealizing and leak. Fixes: f9d6dbf0bf6e ("virtio-net: remove virtio queues if the guest doesn't support multiqueue") Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 3cee2ef3ac..a8db8bfd9c 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3743,9 +3743,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), n->net_conf.tx_queue_size); - for (i = 0; i < n->max_queue_pairs; i++) { - virtio_net_add_queue(n, i); - } + virtio_net_add_queue(n, 0); n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); From ad57f700f469ba1b621274053973f76f8f97cf83 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:52 +0900 Subject: [PATCH 1109/2884] virtio-net: Copy header only when necessary The copied header is only used for byte swapping. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index a8db8bfd9c..13a17a1e5a 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -360,7 +360,8 @@ static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) * can't do it, we fallback onto fixing the headers in the core * virtio-net code. */ - n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, + n->needs_vnet_hdr_swap = n->has_vnet_hdr && + virtio_net_set_vnet_endian(vdev, n->nic->ncs, queue_pairs, true); } else if (virtio_net_started(n, vdev->status)) { /* After using the device, we need to reset the network backend to @@ -2751,7 +2752,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) return -EINVAL; } - if (n->has_vnet_hdr) { + if (n->needs_vnet_hdr_swap) { if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) < n->guest_hdr_len) { virtio_error(vdev, "virtio-net header incorrect"); @@ -2759,19 +2760,16 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) g_free(elem); return -EINVAL; } - if (n->needs_vnet_hdr_swap) { - virtio_net_hdr_swap(vdev, (void *) &vhdr); - sg2[0].iov_base = &vhdr; - sg2[0].iov_len = n->guest_hdr_len; - out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, - out_sg, out_num, - n->guest_hdr_len, -1); - if (out_num == VIRTQUEUE_MAX_SIZE) { - goto drop; - } - out_num += 1; - out_sg = sg2; + virtio_net_hdr_swap(vdev, (void *) &vhdr); + sg2[0].iov_base = &vhdr; + sg2[0].iov_len = n->guest_hdr_len; + out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, out_sg, out_num, + n->guest_hdr_len, -1); + if (out_num == VIRTQUEUE_MAX_SIZE) { + goto drop; } + out_num += 1; + out_sg = sg2; } /* * If host wants to see the guest header as is, we can From 942f420e5cef8cba5daffd6827e6e55e2d17de76 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:53 +0900 Subject: [PATCH 1110/2884] virtio-net: Shrink header byte swapping buffer Byte swapping is only performed for the part of header shared with the legacy standard and the buffer only needs to cover it. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 13a17a1e5a..dabfa6e7d7 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -676,11 +676,6 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, n->mergeable_rx_bufs = mergeable_rx_bufs; - /* - * Note: when extending the vnet header, please make sure to - * change the vnet header copying logic in virtio_net_flush_tx() - * as well. - */ if (version_1) { n->guest_hdr_len = hash_report ? sizeof(struct virtio_net_hdr_v1_hash) : @@ -2736,7 +2731,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) ssize_t ret; unsigned int out_num; struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; - struct virtio_net_hdr_v1_hash vhdr; + struct virtio_net_hdr vhdr; elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); if (!elem) { @@ -2753,18 +2748,18 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) } if (n->needs_vnet_hdr_swap) { - if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) < - n->guest_hdr_len) { + if (iov_to_buf(out_sg, out_num, 0, &vhdr, sizeof(vhdr)) < + sizeof(vhdr)) { virtio_error(vdev, "virtio-net header incorrect"); virtqueue_detach_element(q->tx_vq, elem, 0); g_free(elem); return -EINVAL; } - virtio_net_hdr_swap(vdev, (void *) &vhdr); + virtio_net_hdr_swap(vdev, &vhdr); sg2[0].iov_base = &vhdr; - sg2[0].iov_len = n->guest_hdr_len; + sg2[0].iov_len = sizeof(vhdr); out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, out_sg, out_num, - n->guest_hdr_len, -1); + sizeof(vhdr), -1); if (out_num == VIRTQUEUE_MAX_SIZE) { goto drop; } From cef776c03a24576c5913f7e4427b133766531744 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:54 +0900 Subject: [PATCH 1111/2884] virtio-net: Disable RSS on reset RSS is disabled by default. Fixes: 590790297c ("virtio-net: implement RSS configuration command") Signed-off-by: Akihiko Odaki Reviewed-by: Michael Tokarev Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 70 +++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index dabfa6e7d7..345ef9a8fd 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -600,40 +600,6 @@ static void virtio_net_queue_enable(VirtIODevice *vdev, uint32_t queue_index) } } -static void virtio_net_reset(VirtIODevice *vdev) -{ - VirtIONet *n = VIRTIO_NET(vdev); - int i; - - /* Reset back to compatibility mode */ - n->promisc = 1; - n->allmulti = 0; - n->alluni = 0; - n->nomulti = 0; - n->nouni = 0; - n->nobcast = 0; - /* multiqueue is disabled by default */ - n->curr_queue_pairs = 1; - timer_del(n->announce_timer.tm); - n->announce_timer.round = 0; - n->status &= ~VIRTIO_NET_S_ANNOUNCE; - - /* Flush any MAC and VLAN filter table state */ - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.multi_overflow = 0; - n->mac_table.uni_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); - qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); - memset(n->vlans, 0, MAX_VLAN >> 3); - - /* Flush any async TX */ - for (i = 0; i < n->max_queue_pairs; i++) { - flush_or_purge_queued_packets(qemu_get_subqueue(n->nic, i)); - } -} - static void peer_test_vnet_hdr(VirtIONet *n) { NetClientState *nc = qemu_get_queue(n->nic); @@ -3845,6 +3811,42 @@ static void virtio_net_device_unrealize(DeviceState *dev) virtio_cleanup(vdev); } +static void virtio_net_reset(VirtIODevice *vdev) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i; + + /* Reset back to compatibility mode */ + n->promisc = 1; + n->allmulti = 0; + n->alluni = 0; + n->nomulti = 0; + n->nouni = 0; + n->nobcast = 0; + /* multiqueue is disabled by default */ + n->curr_queue_pairs = 1; + timer_del(n->announce_timer.tm); + n->announce_timer.round = 0; + n->status &= ~VIRTIO_NET_S_ANNOUNCE; + + /* Flush any MAC and VLAN filter table state */ + n->mac_table.in_use = 0; + n->mac_table.first_multi = 0; + n->mac_table.multi_overflow = 0; + n->mac_table.uni_overflow = 0; + memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); + memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); + memset(n->vlans, 0, MAX_VLAN >> 3); + + /* Flush any async TX */ + for (i = 0; i < n->max_queue_pairs; i++) { + flush_or_purge_queued_packets(qemu_get_subqueue(n->nic, i)); + } + + virtio_net_disable_rss(n); +} + static void virtio_net_instance_init(Object *obj) { VirtIONet *n = VIRTIO_NET(obj); From 0e07198ea3d899b0e7946b1a3d2a439d53415289 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:55 +0900 Subject: [PATCH 1112/2884] virtio-net: Unify the logic to update NIC state for RSS The code to attach or detach the eBPF program to RSS were duplicated so unify them into one function to save some code. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 90 ++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 54 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 345ef9a8fd..8464eb4082 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1232,18 +1232,6 @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, } } -static void virtio_net_detach_epbf_rss(VirtIONet *n); - -static void virtio_net_disable_rss(VirtIONet *n) -{ - if (n->rss_data.enabled) { - trace_virtio_net_rss_disable(); - } - n->rss_data.enabled = false; - - virtio_net_detach_epbf_rss(n); -} - static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd) { NetClientState *nc = qemu_get_peer(qemu_get_queue(nic), 0); @@ -1291,6 +1279,40 @@ static void virtio_net_detach_epbf_rss(VirtIONet *n) virtio_net_attach_ebpf_to_backend(n->nic, -1); } +static void virtio_net_commit_rss_config(VirtIONet *n) +{ + if (n->rss_data.enabled) { + n->rss_data.enabled_software_rss = n->rss_data.populate_hash; + if (n->rss_data.populate_hash) { + virtio_net_detach_epbf_rss(n); + } else if (!virtio_net_attach_epbf_rss(n)) { + if (get_vhost_net(qemu_get_queue(n->nic)->peer)) { + warn_report("Can't load eBPF RSS for vhost"); + } else { + warn_report("Can't load eBPF RSS - fallback to software RSS"); + n->rss_data.enabled_software_rss = true; + } + } + + trace_virtio_net_rss_enable(n->rss_data.hash_types, + n->rss_data.indirections_len, + sizeof(n->rss_data.key)); + } else { + virtio_net_detach_epbf_rss(n); + trace_virtio_net_rss_disable(); + } +} + +static void virtio_net_disable_rss(VirtIONet *n) +{ + if (!n->rss_data.enabled) { + return; + } + + n->rss_data.enabled = false; + virtio_net_commit_rss_config(n); +} + static bool virtio_net_load_ebpf_fds(VirtIONet *n) { int fds[EBPF_RSS_MAX_FDS] = { [0 ... EBPF_RSS_MAX_FDS - 1] = -1}; @@ -1455,28 +1477,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, goto error; } n->rss_data.enabled = true; - - if (!n->rss_data.populate_hash) { - if (!virtio_net_attach_epbf_rss(n)) { - /* EBPF must be loaded for vhost */ - if (get_vhost_net(qemu_get_queue(n->nic)->peer)) { - warn_report("Can't load eBPF RSS for vhost"); - goto error; - } - /* fallback to software RSS */ - warn_report("Can't load eBPF RSS - fallback to software RSS"); - n->rss_data.enabled_software_rss = true; - } - } else { - /* use software RSS for hash populating */ - /* and detach eBPF if was loaded before */ - virtio_net_detach_epbf_rss(n); - n->rss_data.enabled_software_rss = true; - } - - trace_virtio_net_rss_enable(n->rss_data.hash_types, - n->rss_data.indirections_len, - temp.b); + virtio_net_commit_rss_config(n); return queue_pairs; error: trace_virtio_net_rss_error(err_msg, err_value); @@ -3076,26 +3077,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) } } - if (n->rss_data.enabled) { - n->rss_data.enabled_software_rss = n->rss_data.populate_hash; - if (!n->rss_data.populate_hash) { - if (!virtio_net_attach_epbf_rss(n)) { - if (get_vhost_net(qemu_get_queue(n->nic)->peer)) { - warn_report("Can't post-load eBPF RSS for vhost"); - } else { - warn_report("Can't post-load eBPF RSS - " - "fallback to software RSS"); - n->rss_data.enabled_software_rss = true; - } - } - } - - trace_virtio_net_rss_enable(n->rss_data.hash_types, - n->rss_data.indirections_len, - sizeof(n->rss_data.key)); - } else { - trace_virtio_net_rss_disable(); - } + virtio_net_commit_rss_config(n); return 0; } From 13d40aa88bdc3eed480a4a4156dc0b84e06d3da7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:56 +0900 Subject: [PATCH 1113/2884] virtio-net: Always set populate_hash The member is not cleared during reset so may have a stale value. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 8464eb4082..b423169a48 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -651,6 +651,7 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, n->guest_hdr_len = n->mergeable_rx_bufs ? sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); + n->rss_data.populate_hash = false; } for (i = 0; i < n->max_queue_pairs; i++) { From a4c960eedcd2c68ff784fb4d4cb8ddd5bff8814f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:57 +0900 Subject: [PATCH 1114/2884] virtio-net: Do not write hashes to peer buffer The peer buffer is qualified with const and not meant to be modified. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index b423169a48..666a4e2a03 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1830,16 +1830,9 @@ static uint8_t virtio_net_get_hash_type(bool hasip4, return 0xff; } -static void virtio_set_packet_hash(const uint8_t *buf, uint8_t report, - uint32_t hash) -{ - struct virtio_net_hdr_v1_hash *hdr = (void *)buf; - hdr->hash_value = hash; - hdr->hash_report = report; -} - static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, - size_t size) + size_t size, + struct virtio_net_hdr_v1_hash *hdr) { VirtIONet *n = qemu_get_nic_opaque(nc); unsigned int index = nc->queue_index, new_index = index; @@ -1870,7 +1863,8 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, n->rss_data.hash_types); if (net_hash_type > NetPktRssIpV6UdpEx) { if (n->rss_data.populate_hash) { - virtio_set_packet_hash(buf, VIRTIO_NET_HASH_REPORT_NONE, 0); + hdr->hash_value = VIRTIO_NET_HASH_REPORT_NONE; + hdr->hash_report = 0; } return n->rss_data.redirect ? n->rss_data.default_queue : -1; } @@ -1878,7 +1872,8 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, hash = net_rx_pkt_calc_rss_hash(pkt, net_hash_type, n->rss_data.key); if (n->rss_data.populate_hash) { - virtio_set_packet_hash(buf, reports[net_hash_type], hash); + hdr->hash_value = hash; + hdr->hash_report = reports[net_hash_type]; } if (n->rss_data.redirect) { @@ -1898,7 +1893,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; size_t lens[VIRTQUEUE_MAX_SIZE]; struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; - struct virtio_net_hdr_mrg_rxbuf mhdr; + struct virtio_net_hdr_v1_hash extra_hdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset, j; ssize_t err; @@ -1908,7 +1903,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, } if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) { - int index = virtio_net_process_rss(nc, buf, size); + int index = virtio_net_process_rss(nc, buf, size, &extra_hdr); if (index >= 0) { NetClientState *nc2 = qemu_get_subqueue(n->nic, index); return virtio_net_receive_rcu(nc2, buf, size, true); @@ -1968,15 +1963,17 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, if (n->mergeable_rx_bufs) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), sg, elem->in_num, - offsetof(typeof(mhdr), num_buffers), - sizeof(mhdr.num_buffers)); + offsetof(typeof(extra_hdr), hdr.num_buffers), + sizeof(extra_hdr.hdr.num_buffers)); } receive_header(n, sg, elem->in_num, buf, size); if (n->rss_data.populate_hash) { - offset = sizeof(mhdr); + offset = offsetof(typeof(extra_hdr), hash_value); iov_from_buf(sg, elem->in_num, offset, - buf + offset, n->host_hdr_len - sizeof(mhdr)); + (char *)&extra_hdr + offset, + sizeof(extra_hdr.hash_value) + + sizeof(extra_hdr.hash_report)); } offset = n->host_hdr_len; total += n->guest_hdr_len; @@ -2006,10 +2003,11 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, } if (mhdr_cnt) { - virtio_stw_p(vdev, &mhdr.num_buffers, i); + virtio_stw_p(vdev, &extra_hdr.hdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, - &mhdr.num_buffers, sizeof mhdr.num_buffers); + &extra_hdr.hdr.num_buffers, + sizeof extra_hdr.hdr.num_buffers); } for (j = 0; j < i; j++) { From 72fa42cfca7060fab00c534e71fc850b194a4c6d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:58 +0900 Subject: [PATCH 1115/2884] ebpf: Fix RSS error handling calculate_rss_hash() was using hash value 0 to tell if it calculated a hash, but the hash value may be 0 on a rare occasion. Have a distinct bool value for correctness. Fixes: f3fa412de2 ("ebpf: Added eBPF RSS program.") Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- ebpf/rss.bpf.skeleton.h | 1210 +++++++++++++++++++-------------------- tools/ebpf/rss.bpf.c | 20 +- 2 files changed, 610 insertions(+), 620 deletions(-) diff --git a/ebpf/rss.bpf.skeleton.h b/ebpf/rss.bpf.skeleton.h index aed4ef9a03..e41ed88901 100644 --- a/ebpf/rss.bpf.skeleton.h +++ b/ebpf/rss.bpf.skeleton.h @@ -165,7 +165,7 @@ rss_bpf__create_skeleton(struct rss_bpf *obj) s->progs[0].prog = &obj->progs.tun_rss_steering_prog; s->progs[0].link = &obj->links.tun_rss_steering_prog; - s->data = (void *)rss_bpf__elf_bytes(&s->data_sz); + s->data = rss_bpf__elf_bytes(&s->data_sz); obj->skeleton = s; return 0; @@ -176,194 +176,188 @@ err: static inline const void *rss_bpf__elf_bytes(size_t *sz) { - *sz = 20600; - return (const void *)"\ + static const char data[] __attribute__((__aligned__(8))) = "\ \x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x38\x4d\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ -\x01\0\xbf\x19\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\x4c\xff\0\0\0\0\xbf\xa7\ -\0\0\0\0\0\0\x07\x07\0\0\x4c\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xb8\x4b\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ +\x01\0\xbf\x19\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\x54\xff\0\0\0\0\xbf\xa7\ +\0\0\0\0\0\0\x07\x07\0\0\x54\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x06\0\0\0\0\0\0\x18\x01\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\ -\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x61\x02\0\0\0\0\xbf\x78\0\0\ -\0\0\0\0\x15\x08\x5f\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\ -\0\x58\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xc0\xff\0\0\0\0\x7b\x1a\xb8\xff\ -\0\0\0\0\x7b\x1a\xb0\xff\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\0\x7b\x1a\xa0\xff\0\0\0\ -\0\x63\x1a\x98\xff\0\0\0\0\x7b\x1a\x90\xff\0\0\0\0\x7b\x1a\x88\xff\0\0\0\0\x7b\ -\x1a\x80\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x7b\x1a\x70\xff\0\0\0\0\x7b\x1a\ -\x68\xff\0\0\0\0\x7b\x1a\x60\xff\0\0\0\0\x7b\x1a\x58\xff\0\0\0\0\x7b\x1a\x50\ -\xff\0\0\0\0\x15\x09\x47\x02\0\0\0\0\x6b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ -\0\x07\x03\0\0\xc8\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\ +\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x4f\x02\0\0\0\0\xbf\x78\0\0\ +\0\0\0\0\x15\x08\x4d\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\ +\0\x46\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xc8\xff\0\0\0\0\x7b\x1a\xc0\xff\ +\0\0\0\0\x7b\x1a\xb8\xff\0\0\0\0\x7b\x1a\xb0\xff\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\ +\0\x63\x1a\xa0\xff\0\0\0\0\x7b\x1a\x98\xff\0\0\0\0\x7b\x1a\x90\xff\0\0\0\0\x7b\ +\x1a\x88\xff\0\0\0\0\x7b\x1a\x80\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x7b\x1a\ +\x70\xff\0\0\0\0\x7b\x1a\x68\xff\0\0\0\0\x7b\x1a\x60\xff\0\0\0\0\x7b\x1a\x58\ +\xff\0\0\0\0\x15\x09\x35\x02\0\0\0\0\x6b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ +\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\ \x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\ -\x77\0\0\0\x20\0\0\0\x55\0\x3c\x02\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xc8\ +\x77\0\0\0\x20\0\0\0\x55\0\x2a\x02\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xd0\ \xff\0\0\0\0\xbf\x13\0\0\0\0\0\0\xdc\x03\0\0\x10\0\0\0\x15\x03\x02\0\0\x81\0\0\ \x55\x03\x0b\0\xa8\x88\0\0\xb7\x02\0\0\x14\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\ -\0\xc8\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\ -\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x2c\x02\0\ -\0\0\0\x69\xa1\xc8\xff\0\0\0\0\x15\x01\x2a\x02\0\0\0\0\x7b\x9a\x38\xff\0\0\0\0\ -\x15\x01\x56\0\x86\xdd\0\0\x55\x01\x3b\0\x08\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\ -\x1a\x50\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\ -\xff\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xc8\xff\ -\xff\xff\x79\xa1\x38\xff\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\ +\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\ +\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x1a\x02\0\ +\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x15\x01\x18\x02\0\0\0\0\x15\x01\x21\0\x86\xdd\0\ +\0\x7b\x9a\x48\xff\0\0\0\0\x55\x01\xf6\0\x08\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\ +\x1a\x58\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\ +\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\ +\xff\xff\x79\xa1\x48\xff\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\ \x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\ -\x55\0\x17\x02\0\0\0\0\x69\xa1\xce\xff\0\0\0\0\x57\x01\0\0\x3f\xff\0\0\xb7\x02\ -\0\0\x01\0\0\0\x55\x01\x01\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x61\xa1\xd4\xff\0\0\0\ -\0\x63\x1a\x5c\xff\0\0\0\0\x61\xa1\xd8\xff\0\0\0\0\x63\x1a\x60\xff\0\0\0\0\x71\ -\xa9\xd1\xff\0\0\0\0\x71\xa1\xc8\xff\0\0\0\0\x67\x01\0\0\x02\0\0\0\x57\x01\0\0\ -\x3c\0\0\0\x7b\x1a\x40\xff\0\0\0\0\x73\x2a\x56\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\ -\x57\x01\0\0\xff\0\0\0\x15\x01\x19\0\0\0\0\0\x57\x02\0\0\xff\0\0\0\x55\x02\x17\ -\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\x57\x01\x11\0\0\0\x55\x09\x14\0\x06\0\ -\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x53\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\ -\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\0\0\ -\0\0\0\x07\x03\0\0\xc8\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\x79\xa2\x40\xff\0\0\ -\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\ -\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xf0\x01\0\0\0\0\x69\xa1\xc8\xff\0\0\0\0\ -\x6b\x1a\x58\xff\0\0\0\0\x69\xa1\xca\xff\0\0\0\0\x6b\x1a\x5a\xff\0\0\0\0\x71\ -\xa1\x50\xff\0\0\0\0\x15\x01\xd8\0\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\ -\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\ -\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\ -\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\xff\0\0\0\0\x15\x02\x09\x01\0\0\0\0\xbf\ -\x12\0\0\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x06\x01\0\0\0\0\x61\xa1\x5c\xff\ -\0\0\0\0\x63\x1a\xa0\xff\0\0\0\0\x61\xa1\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\ -\0\x69\xa1\x58\xff\0\0\0\0\x6b\x1a\xa8\xff\0\0\0\0\x69\xa1\x5a\xff\0\0\0\0\x6b\ -\x1a\xaa\xff\0\0\0\0\x05\0\x68\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x51\ -\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\ -\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\ -\xa3\0\0\0\0\0\0\x07\x03\0\0\xc8\xff\xff\xff\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x40\ -\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\ -\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\ -\x55\0\xfc\0\0\0\0\0\x79\xa1\xd8\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\ -\0\x20\0\0\0\x63\x1a\x68\xff\0\0\0\0\x79\xa1\xd0\xff\0\0\0\0\x63\x1a\x5c\xff\0\ -\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x60\xff\0\0\0\0\x79\xa1\xe0\xff\0\0\0\0\ -\x63\x1a\x6c\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\ -\xe8\xff\0\0\0\0\x63\x1a\x74\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x78\xff\ -\0\0\0\0\x71\xa9\xce\xff\0\0\0\0\x25\x09\x11\x01\x3c\0\0\0\xb7\x01\0\0\x01\0\0\ -\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\ -\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x0a\x01\0\0\0\0\xb7\x01\0\0\0\0\0\0\x6b\x1a\ -\xf8\xff\0\0\0\0\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x40\xff\0\0\0\0\xbf\xa1\0\0\0\0\ -\0\0\x07\x01\0\0\x8c\xff\xff\xff\x7b\x1a\x18\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\ -\x07\x01\0\0\x7c\xff\xff\xff\x7b\x1a\x10\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\ -\x1a\x30\xff\0\0\0\0\x7b\x7a\x28\xff\0\0\0\0\x7b\x8a\x20\xff\0\0\0\0\xbf\xa3\0\ -\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\x79\xa2\x40\xff\ -\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\ -\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\xc7\0\0\0\ -\0\0\xbf\x91\0\0\0\0\0\0\x15\x01\x24\0\x3c\0\0\0\x15\x01\x5c\0\x2c\0\0\0\x55\ -\x01\x5d\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf0\xff\0\0\0\0\xbf\xa3\0\0\0\ -\0\0\0\x07\x03\0\0\xf0\xff\xff\xff\x79\xa9\x38\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\ -\x79\xa2\x40\xff\0\0\0\0\xb7\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\ -\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\ -\x01\x06\x01\0\0\0\0\x71\xa1\xf2\xff\0\0\0\0\x55\x01\x4d\0\x02\0\0\0\x71\xa1\ -\xf1\xff\0\0\0\0\x55\x01\x4b\0\x02\0\0\0\x71\xa1\xf3\xff\0\0\0\0\x55\x01\x49\0\ -\x01\0\0\0\x79\xa2\x40\xff\0\0\0\0\x07\x02\0\0\x08\0\0\0\xbf\x91\0\0\0\0\0\0\ -\x79\xa3\x18\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\ -\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\ -\x01\xf5\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x55\xff\0\0\0\0\x05\0\x3b\0\0\ -\0\0\0\xb7\x08\0\0\x02\0\0\0\xb7\x07\0\0\0\0\0\0\x6b\x7a\xf0\xff\0\0\0\0\x05\0\ +\x55\0\x05\x02\0\0\0\0\x69\xa1\xd6\xff\0\0\0\0\x57\x01\0\0\x3f\xff\0\0\xb7\x04\ +\0\0\x01\0\0\0\x55\x01\x01\0\0\0\0\0\xb7\x04\0\0\0\0\0\0\x61\xa1\xdc\xff\0\0\0\ +\0\x63\x1a\x64\xff\0\0\0\0\x61\xa1\xe0\xff\0\0\0\0\x63\x1a\x68\xff\0\0\0\0\x71\ +\xa9\xd9\xff\0\0\0\0\x71\xa2\xd0\xff\0\0\0\0\x67\x02\0\0\x02\0\0\0\x57\x02\0\0\ +\x3c\0\0\0\x73\x4a\x5e\xff\0\0\0\0\x05\0\xbc\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ +\x73\x1a\x59\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xf0\xff\0\0\0\0\x7b\x1a\ +\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\ +\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\ +\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\ +\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xe4\x01\0\0\0\0\xb7\ +\x03\0\0\x28\0\0\0\x7b\x9a\x48\xff\0\0\0\0\x79\xa1\xe0\xff\0\0\0\0\x63\x1a\x6c\ +\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\xd8\xff\0\0\ +\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x68\xff\0\0\0\0\x79\ +\xa1\xe8\xff\0\0\0\0\x63\x1a\x74\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x78\ +\xff\0\0\0\0\x79\xa1\xf0\xff\0\0\0\0\x63\x1a\x7c\xff\0\0\0\0\x77\x01\0\0\x20\0\ +\0\0\x63\x1a\x80\xff\0\0\0\0\x71\xa9\xd6\xff\0\0\0\0\x25\x09\x93\0\x3c\0\0\0\ +\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\ +\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x8c\0\0\0\0\0\xb7\x01\0\ +\0\0\0\0\0\x6b\x1a\xfe\xff\0\0\0\0\xb7\x02\0\0\x28\0\0\0\xbf\xa1\0\0\0\0\0\0\ +\x07\x01\0\0\x94\xff\xff\xff\x7b\x1a\x20\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\ +\x01\0\0\x84\xff\xff\xff\x7b\x1a\x18\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\ +\x38\xff\0\0\0\0\x7b\x7a\x30\xff\0\0\0\0\x7b\x8a\x28\xff\0\0\0\0\xbf\xa3\0\0\0\ +\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\x7b\x2a\x40\xff\0\0\ +\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\ +\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xb2\x01\0\0\0\0\xbf\x91\0\0\0\0\0\0\x15\ +\x01\x22\0\x3c\0\0\0\x15\x01\x58\0\x2c\0\0\0\x79\xa2\x40\xff\0\0\0\0\x55\x01\ +\x59\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf8\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ +\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa9\x48\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\xb7\ +\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\ +\0\x77\0\0\0\x20\0\0\0\x55\0\xa1\x01\0\0\0\0\x71\xa1\xfa\xff\0\0\0\0\x55\x01\ +\x4a\0\x02\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x55\x01\x48\0\x02\0\0\0\x71\xa1\xfb\ +\xff\0\0\0\0\x55\x01\x46\0\x01\0\0\0\x79\xa2\x40\xff\0\0\0\0\x07\x02\0\0\x08\0\ +\0\0\xbf\x91\0\0\0\0\0\0\x79\xa3\x20\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\ +\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\ +\0\x91\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5d\xff\0\0\0\0\x05\0\x39\0\0\ +\0\0\0\xb7\x08\0\0\x02\0\0\0\xb7\x07\0\0\0\0\0\0\x6b\x7a\xf8\xff\0\0\0\0\x05\0\ \x12\0\0\0\0\0\x0f\x81\0\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\ -\x71\xa3\xf9\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x3d\x32\x09\0\0\0\0\0\xbf\x72\0\ +\x71\xa3\xff\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x3d\x32\x09\0\0\0\0\0\xbf\x72\0\ \0\0\0\0\0\x07\x02\0\0\x01\0\0\0\x67\x07\0\0\x20\0\0\0\xbf\x73\0\0\0\0\0\0\x77\ \x03\0\0\x20\0\0\0\xbf\x27\0\0\0\0\0\0\xbf\x18\0\0\0\0\0\0\xb7\x01\0\0\x1d\0\0\ -\0\x2d\x31\x03\0\0\0\0\0\x79\xa7\x28\xff\0\0\0\0\x79\xa8\x20\xff\0\0\0\0\x05\0\ -\x25\0\0\0\0\0\xbf\x89\0\0\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\x19\0\0\0\0\0\0\ -\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf0\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\xbf\ +\0\x2d\x31\x03\0\0\0\0\0\x79\xa7\x30\xff\0\0\0\0\x79\xa8\x28\xff\0\0\0\0\x05\0\ +\x23\0\0\0\0\0\xbf\x89\0\0\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\x19\0\0\0\0\0\0\ +\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\xbf\ \x92\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ -\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x7a\ -\0\0\0\0\0\x71\xa2\xf0\xff\0\0\0\0\x55\x02\x0e\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\ -\0\x79\xa1\x38\xff\0\0\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x10\xff\0\0\0\0\xb7\x04\ -\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\0\0\0\0\0\ -\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x6d\0\0\0\0\0\xb7\x01\0\0\ -\x01\0\0\0\x73\x1a\x54\xff\0\0\0\0\x05\0\xdf\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ -\x15\x02\xce\xff\0\0\0\0\x71\xa1\xf1\xff\0\0\0\0\x07\x01\0\0\x02\0\0\0\x05\0\ -\xcb\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x56\xff\0\0\0\0\x71\xa1\xf9\xff\ -\0\0\0\0\x67\x01\0\0\x03\0\0\0\x79\xa2\x40\xff\0\0\0\0\x0f\x12\0\0\0\0\0\0\x07\ -\x02\0\0\x08\0\0\0\x7b\x2a\x40\xff\0\0\0\0\x71\xa9\xf8\xff\0\0\0\0\x25\x09\x0f\ -\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\ -\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x08\0\0\0\0\ -\0\x79\xa1\x30\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x1a\x30\xff\0\0\0\0\x67\ -\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x7f\xff\x0b\0\0\0\x71\xa2\x56\ -\xff\0\0\0\0\x05\0\x0c\xff\0\0\0\0\x15\x09\xf7\xff\x87\0\0\0\x05\0\xfc\xff\0\0\ -\0\0\x71\xa1\x51\xff\0\0\0\0\x15\x01\x10\x01\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\ -\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\ -\0\0\x67\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\ -\0\0\0\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\xff\0\0\0\0\x15\x02\x43\0\0\0\0\0\ -\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\x15\x02\x40\0\0\0\0\0\x57\x01\0\0\ -\x80\0\0\0\xb7\x02\0\0\x10\0\0\0\xb7\x03\0\0\x10\0\0\0\x15\x01\x01\0\0\0\0\0\ -\xb7\x03\0\0\x30\0\0\0\x71\xa4\x55\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\ -\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x5c\xff\xff\xff\xbf\x34\0\0\0\0\0\0\ -\x15\x01\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x7c\xff\xff\xff\x71\xa5\ -\x54\xff\0\0\0\0\xbf\x31\0\0\0\0\0\0\x15\x05\x01\0\0\0\0\0\xbf\x41\0\0\0\0\0\0\ -\x61\x14\x04\0\0\0\0\0\x67\x04\0\0\x20\0\0\0\x61\x15\0\0\0\0\0\0\x4f\x54\0\0\0\ -\0\0\0\x7b\x4a\xa0\xff\0\0\0\0\x61\x14\x08\0\0\0\0\0\x61\x11\x0c\0\0\0\0\0\x67\ -\x01\0\0\x20\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\0\x0f\x23\0\0\0\0\ -\0\0\x61\x31\0\0\0\0\0\0\x61\x32\x04\0\0\0\0\0\x61\x34\x08\0\0\0\0\0\x61\x33\ -\x0c\0\0\0\0\0\x69\xa5\x5a\xff\0\0\0\0\x6b\x5a\xc2\xff\0\0\0\0\x69\xa5\x58\xff\ -\0\0\0\0\x6b\x5a\xc0\xff\0\0\0\0\x67\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\ -\x3a\xb8\xff\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb0\xff\ -\0\0\0\0\x05\0\x6b\0\0\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x04\0\0\0\0\0\xbf\ -\x12\0\0\0\0\0\0\x57\x02\0\0\x04\0\0\0\x15\x02\x01\0\0\0\0\0\x05\0\xf4\xfe\0\0\ -\0\0\x57\x01\0\0\x01\0\0\0\x15\x01\xcd\0\0\0\0\0\x61\xa1\x5c\xff\0\0\0\0\x63\ -\x1a\xa0\xff\0\0\0\0\x61\xa1\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\0\x05\0\x5e\ -\0\0\0\0\0\xb7\x09\0\0\x3c\0\0\0\x79\xa7\x28\xff\0\0\0\0\x79\xa8\x20\xff\0\0\0\ -\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x15\0\xac\xff\0\0\0\0\x05\0\xc1\0\0\ -\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x26\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\ -\x02\0\0\x20\0\0\0\x15\x02\x23\0\0\0\0\0\x57\x01\0\0\0\x01\0\0\xb7\x02\0\0\x10\ -\0\0\0\xb7\x03\0\0\x10\0\0\0\x15\x01\x01\0\0\0\0\0\xb7\x03\0\0\x30\0\0\0\x71\ -\xa4\x55\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\ -\0\0\x07\x03\0\0\x5c\xff\xff\xff\xbf\x34\0\0\0\0\0\0\x15\x01\x02\0\0\0\0\0\xbf\ -\xa4\0\0\0\0\0\0\x07\x04\0\0\x7c\xff\xff\xff\x71\xa5\x54\xff\0\0\0\0\xbf\x31\0\ -\0\0\0\0\0\x15\x05\xbc\xff\0\0\0\0\x05\0\xba\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ -\x73\x1a\x52\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\ -\0\0\0\0\0\x07\x03\0\0\xc8\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\x79\xa2\x40\xff\ -\0\0\0\0\xb7\x04\0\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\ -\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x9c\0\0\0\0\0\x05\0\xab\xfe\0\0\0\0\ -\x15\x09\xf5\xfe\x87\0\0\0\x05\0\x83\xff\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\ -\0\x08\0\0\0\x15\x02\x96\0\0\0\0\0\x57\x01\0\0\x40\0\0\0\xb7\x02\0\0\x0c\0\0\0\ -\xb7\x03\0\0\x0c\0\0\0\x15\x01\x01\0\0\0\0\0\xb7\x03\0\0\x2c\0\0\0\x71\xa4\x54\ -\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\ -\x03\0\0\x50\xff\xff\xff\x0f\x23\0\0\0\0\0\0\x61\x32\x04\0\0\0\0\0\x67\x02\0\0\ -\x20\0\0\0\x61\x34\0\0\0\0\0\0\x4f\x42\0\0\0\0\0\0\x7b\x2a\xa0\xff\0\0\0\0\x61\ -\x32\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x67\x03\0\0\x20\0\0\0\x4f\x23\0\0\0\0\ -\0\0\x7b\x3a\xa8\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xb0\xff\xff\xff\ -\x71\xa3\x55\xff\0\0\0\0\x15\x03\x0b\0\0\0\0\0\x15\x01\x0a\0\0\0\0\0\x61\xa1\ -\x98\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x94\xff\0\0\0\0\x63\x12\x08\0\0\ -\0\0\0\x61\xa1\x90\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x8c\xff\0\0\0\0\ -\x05\0\x09\0\0\0\0\0\xb7\x09\0\0\x2b\0\0\0\x05\0\xad\xff\0\0\0\0\x61\xa1\x78\ -\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x74\xff\0\0\0\0\x63\x12\x08\0\0\0\0\ -\0\x61\xa1\x70\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x6c\xff\0\0\0\0\x63\ -\x12\0\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x07\x07\0\0\x04\0\0\0\x61\x83\0\0\0\0\0\0\ -\xb7\x05\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xa0\xff\xff\xff\x0f\x21\0\ -\0\0\0\0\0\x71\x14\0\0\0\0\0\0\xbf\x41\0\0\0\0\0\0\x67\x01\0\0\x38\0\0\0\xc7\ -\x01\0\0\x3f\0\0\0\x5f\x31\0\0\0\0\0\0\xaf\x51\0\0\0\0\0\0\xbf\x75\0\0\0\0\0\0\ -\x0f\x25\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\ -\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x39\ -\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\ -\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\ -\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\ -\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\ -\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\ -\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\ -\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x04\0\0\0\x57\0\0\0\ -\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3c\0\0\0\xc7\0\0\ -\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\ -\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\ -\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\ -\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x02\0\0\0\x57\0\0\0\x01\0\ -\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\ -\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\ -\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\ -\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\0\0\0\0\0\x5f\x34\0\0\0\0\0\0\ -\xaf\x41\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x53\0\0\0\ -\0\0\0\x07\x02\0\0\x01\0\0\0\xbf\x15\0\0\0\0\0\0\x15\x02\x01\0\x24\0\0\0\x05\0\ -\xa9\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\x0c\0\0\0\ -\0\0\x71\x62\x06\0\0\0\0\0\x71\x63\x07\0\0\0\0\0\x67\x03\0\0\x08\0\0\0\x4f\x23\ -\0\0\0\0\0\0\x9f\x31\0\0\0\0\0\0\x63\x1a\x50\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\ -\x07\x02\0\0\x50\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\ +\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x6b\x01\0\0\0\0\x71\xa2\xf8\ +\xff\0\0\0\0\x55\x02\x0d\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\0\x79\xa1\x48\xff\0\0\ +\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x18\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\ +\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\ +\0\x5f\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5c\xff\0\0\0\0\x05\0\xe1\xff\ +\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x15\x02\xd0\xff\0\0\0\0\x71\xa1\xf9\xff\0\0\0\0\ +\x07\x01\0\0\x02\0\0\0\x05\0\xcd\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5e\ +\xff\0\0\0\0\x79\xa2\x40\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\x67\x01\0\0\x03\0\ +\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\x08\0\0\0\x71\xa9\xfe\xff\0\0\0\0\x25\x09\ +\x0e\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x03\0\0\x01\0\0\ +\0\0\0\0\0\0\x18\0\x1c\x5f\x31\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x07\0\0\ +\0\0\0\x79\xa1\x38\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x1a\x38\xff\0\0\0\0\ +\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\x02\0\x0b\0\0\0\x05\0\x84\ +\xff\0\0\0\0\x15\x09\xf8\xff\x87\0\0\0\xbf\x23\0\0\0\0\0\0\x05\0\x01\0\0\0\0\0\ +\x15\x09\x73\xff\x87\0\0\0\x71\xa4\x5e\xff\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\x91\ +\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\0\x15\x01\x18\0\0\0\0\0\x57\x04\0\0\xff\0\0\0\ +\x55\x04\x16\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\xb4\0\x11\0\0\0\x55\x09\ +\x13\0\x06\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5b\xff\0\0\0\0\xb7\x01\0\0\0\0\ +\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\ +\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\xb7\ +\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\ +\0\x77\0\0\0\x20\0\0\0\x55\0\x23\x01\0\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x6b\x1a\ +\x60\xff\0\0\0\0\x69\xa1\xd2\xff\0\0\0\0\x6b\x1a\x62\xff\0\0\0\0\x71\xa1\x58\ +\xff\0\0\0\0\x15\x01\x18\0\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\ +\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\x03\0\0\ +\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\0\0\x4f\ +\x21\0\0\0\0\0\0\x71\xa2\x5b\xff\0\0\0\0\x15\x02\x49\0\0\0\0\0\xbf\x12\0\0\0\0\ +\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x46\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\x63\ +\x1a\xa8\xff\0\0\0\0\x61\xa1\x68\xff\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x69\xa1\ +\x60\xff\0\0\0\0\x6b\x1a\xb0\xff\0\0\0\0\x69\xa1\x62\xff\0\0\0\0\x6b\x1a\xb2\ +\xff\0\0\0\0\x05\0\x9c\0\0\0\0\0\x71\xa1\x59\xff\0\0\0\0\x15\x01\x03\x01\0\0\0\ +\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\ +\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\ +\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x5b\xff\ +\0\0\0\0\x15\x02\x3c\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\x15\ +\x02\x39\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\x5c\ +\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\ +\x03\0\0\x84\xff\xff\xff\x57\x01\0\0\x80\0\0\0\x15\x01\x01\0\0\0\0\0\xbf\x32\0\ +\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x74\xff\xff\xff\x71\xa5\x5d\xff\0\0\ +\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\ +\x94\xff\xff\xff\x15\x01\x01\0\0\0\0\0\xbf\x43\0\0\0\0\0\0\x61\x21\x04\0\0\0\0\ +\0\x67\x01\0\0\x20\0\0\0\x61\x24\0\0\0\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\x1a\xa8\ +\xff\0\0\0\0\x61\x21\x08\0\0\0\0\0\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\ +\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb0\xff\0\0\0\0\x61\x31\0\0\0\0\0\0\x61\x32\x04\0\ +\0\0\0\0\x61\x34\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x69\xa5\x62\xff\0\0\0\0\ +\x6b\x5a\xca\xff\0\0\0\0\x69\xa5\x60\xff\0\0\0\0\x6b\x5a\xc8\xff\0\0\0\0\x67\ +\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xc0\xff\0\0\0\0\x67\x02\0\0\x20\ +\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb8\xff\0\0\0\0\x05\0\x5f\0\0\0\0\0\x71\xa2\ +\x5a\xff\0\0\0\0\x15\x02\x04\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x04\0\0\ +\0\x15\x02\x01\0\0\0\0\0\x05\0\xb4\xff\0\0\0\0\x57\x01\0\0\x01\0\0\0\x15\x01\ +\xc0\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\x63\x1a\xa8\xff\0\0\0\0\x61\xa1\x68\xff\ +\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x05\0\x52\0\0\0\0\0\x71\xa2\x5a\xff\0\0\0\0\ +\x15\x02\x16\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\x13\0\ +\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\x5c\xff\0\0\0\ +\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\ +\x84\xff\xff\xff\x57\x01\0\0\0\x01\0\0\x15\x01\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\ +\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x74\xff\xff\xff\x71\xa5\x5d\xff\0\0\0\0\xbf\ +\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x94\xff\ +\xff\xff\x15\x01\xc3\xff\0\0\0\0\x05\0\xc1\xff\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\ +\x02\0\0\x08\0\0\0\x15\x02\xa0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\ +\xff\xff\xff\x71\xa4\x5c\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\ +\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x84\xff\xff\xff\x57\x01\0\0\x40\0\0\0\x15\x01\ +\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\x61\x23\x04\0\0\0\0\0\x67\x03\0\0\x20\0\0\0\ +\x61\x24\0\0\0\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xa8\xff\0\0\0\0\x61\x23\x08\0\ +\0\0\0\0\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x32\0\0\0\0\0\0\x7b\ +\x2a\xb0\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xb8\xff\xff\xff\x15\x01\ +\x18\0\0\0\0\0\x71\xa1\x5d\xff\0\0\0\0\x15\x01\x16\0\0\0\0\0\x61\xa1\xa0\xff\0\ +\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x9c\xff\0\0\0\0\x63\x12\x08\0\0\0\0\0\x61\ +\xa1\x98\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x94\xff\0\0\0\0\x05\0\x15\0\ +\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5a\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\ +\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\x79\xa1\ +\x48\xff\0\0\0\0\xb7\x04\0\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ +\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x72\0\0\0\0\0\x05\0\x4e\xff\0\ +\0\0\0\x61\xa1\x80\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x7c\xff\0\0\0\0\ +\x63\x12\x08\0\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\ +\x74\xff\0\0\0\0\x63\x12\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x07\x07\0\0\x04\0\0\0\ +\x61\x83\0\0\0\0\0\0\xb7\x05\0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xa8\ +\xff\xff\xff\x0f\x12\0\0\0\0\0\0\x71\x24\0\0\0\0\0\0\xbf\x42\0\0\0\0\0\0\x67\ +\x02\0\0\x38\0\0\0\xc7\x02\0\0\x3f\0\0\0\x5f\x32\0\0\0\0\0\0\xaf\x52\0\0\0\0\0\ +\0\xbf\x75\0\0\0\0\0\0\x0f\x15\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\x01\ +\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\ +\0\0\0\0\x67\0\0\0\x39\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\ +\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\ +\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\0\0\xc7\ +\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\ +\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\ +\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\ +\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\ +\x04\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\ +\0\x3c\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\ +\x50\0\0\0\0\0\0\x77\0\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\ +\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\0\x3f\0\0\ +\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x02\0\ +\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\ +\0\0\0\0\x67\0\0\0\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\ +\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\ +\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\0\0\0\0\0\ +\x5f\x34\0\0\0\0\0\0\xaf\x42\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\x03\0\0\x01\ +\0\0\0\x4f\x53\0\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\xbf\x25\0\0\0\0\0\0\x15\x01\ +\x01\0\x24\0\0\0\x05\0\xa9\xff\0\0\0\0\x71\x61\x06\0\0\0\0\0\x71\x63\x07\0\0\0\ +\0\0\x67\x03\0\0\x08\0\0\0\x4f\x13\0\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x77\x02\0\ +\0\x20\0\0\0\x9f\x32\0\0\0\0\0\0\x63\x2a\x58\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\ +\x07\x02\0\0\x58\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\ \0\0\0\x55\0\x05\0\0\0\0\0\x71\x61\x08\0\0\0\0\0\x71\x60\x09\0\0\0\0\0\x67\0\0\ \0\x08\0\0\0\x4f\x10\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x69\0\0\0\0\0\0\0\x05\0\xfd\ \xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\x47\x50\x4c\x20\x76\x32\0\0\x9f\xeb\x01\0\x18\0\0\0\0\0\0\0\ -\x58\x05\0\0\x58\x05\0\0\x85\x11\0\0\0\0\0\0\0\0\0\x02\x03\0\0\0\x01\0\0\0\0\0\ +\x58\x05\0\0\x58\x05\0\0\x71\x11\0\0\0\0\0\0\0\0\0\x02\x03\0\0\0\x01\0\0\0\0\0\ \0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x02\ \0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x06\0\0\0\0\0\ \0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\ @@ -403,11 +397,11 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \0\0\0\0\x01\0\0\x05\x08\0\0\0\0\x02\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\ \x2d\0\0\0\x03\x02\0\0\0\0\0\x08\x24\0\0\0\x08\x02\0\0\0\0\0\x01\x01\0\0\0\x08\ \0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x16\x02\0\0\x17\0\0\0\x1a\x02\0\0\x01\0\0\ -\x0c\x25\0\0\0\x52\x11\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\ -\0\0\0\x27\0\0\0\x04\0\0\0\x07\0\0\0\x57\x11\0\0\0\0\0\x0e\x28\0\0\0\x01\0\0\0\ -\x60\x11\0\0\x03\0\0\x0f\0\0\0\0\x0e\0\0\0\0\0\0\0\x28\0\0\0\x12\0\0\0\0\0\0\0\ -\x28\0\0\0\x16\0\0\0\0\0\0\0\x28\0\0\0\x66\x11\0\0\x01\0\0\x0f\0\0\0\0\x29\0\0\ -\0\0\0\0\0\x07\0\0\0\x6e\x11\0\0\0\0\0\x07\0\0\0\0\x7c\x11\0\0\0\0\0\x07\0\0\0\ +\x0c\x25\0\0\0\x3e\x11\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\ +\0\0\0\x27\0\0\0\x04\0\0\0\x07\0\0\0\x43\x11\0\0\0\0\0\x0e\x28\0\0\0\x01\0\0\0\ +\x4c\x11\0\0\x03\0\0\x0f\0\0\0\0\x0e\0\0\0\0\0\0\0\x28\0\0\0\x12\0\0\0\0\0\0\0\ +\x28\0\0\0\x16\0\0\0\0\0\0\0\x28\0\0\0\x52\x11\0\0\x01\0\0\x0f\0\0\0\0\x29\0\0\ +\0\0\0\0\0\x07\0\0\0\x5a\x11\0\0\0\0\0\x07\0\0\0\0\x68\x11\0\0\0\0\0\x07\0\0\0\ \0\0\x69\x6e\x74\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\ \x50\x45\x5f\x5f\0\x74\x79\x70\x65\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\x76\x61\ \x6c\x75\x65\x5f\x73\x69\x7a\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\ @@ -436,7 +430,7 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \x67\x20\x6c\x6f\x6e\x67\0\x73\x6b\0\x5f\x5f\x75\x38\0\x75\x6e\x73\x69\x67\x6e\ \x65\x64\x20\x63\x68\x61\x72\0\x73\x6b\x62\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\ \x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x73\x6f\x63\x6b\x65\x74\ -\0\x2f\x68\x6f\x6d\x65\x2f\x61\x6e\x64\x2f\x53\x52\x43\x53\x2f\x71\x65\x6d\x75\ +\0\x2f\x68\x6f\x6d\x65\x2f\x6d\x65\x2f\x71\x2f\x76\x61\x72\x2f\x71\x65\x6d\x75\ \x2f\x74\x6f\x6f\x6c\x73\x2f\x65\x62\x70\x66\x2f\x72\x73\x73\x2e\x62\x70\x66\ \x2e\x63\0\x69\x6e\x74\x20\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\ \x69\x6e\x67\x5f\x70\x72\x6f\x67\x28\x73\x74\x72\x75\x63\x74\x20\x5f\x5f\x73\ @@ -489,125 +483,125 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \x20\x20\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x2e\ \x70\x72\x6f\x74\x6f\x63\x6f\x6c\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\ \x5f\x6f\x66\x66\x73\x65\x74\x20\x3d\x20\x69\x70\x2e\x69\x68\x6c\x20\x2a\x20\ -\x34\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\ -\x6f\x6c\x20\x21\x3d\x20\x30\x20\x26\x26\x20\x21\x69\x6e\x66\x6f\x2d\x3e\x69\ -\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x29\x20\x7b\0\x20\x20\x20\x20\ -\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\ -\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x54\x43\x50\x29\x20\x7b\0\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\ -\x5f\x74\x63\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x73\x74\x72\x75\x63\x74\x20\x74\x63\x70\x68\x64\x72\x20\x74\x63\x70\ -\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\ -\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\ -\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\ -\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x74\x63\x70\x2c\x20\x73\x69\ -\x7a\x65\x6f\x66\x28\x74\x63\x70\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x69\x66\ -\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\ -\x76\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\ -\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x74\x63\x70\x20\x26\x26\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\ -\x76\x36\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\ -\x63\x74\x20\x69\x70\x76\x36\x68\x64\x72\x20\x69\x70\x36\x20\x3d\x20\x7b\x7d\ -\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\ -\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\ -\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x36\x2c\x20\ -\x73\x69\x7a\x65\x6f\x66\x28\x69\x70\x36\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\ -\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x73\x72\x63\x20\x3d\x20\x69\x70\ -\x36\x2e\x73\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\ -\x6f\x2d\x3e\x69\x6e\x36\x5f\x64\x73\x74\x20\x3d\x20\x69\x70\x36\x2e\x64\x61\ -\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x70\x72\x6f\x74\ -\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x36\x2e\x6e\x65\x78\x74\x68\x64\x72\x3b\0\ -\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\x68\x64\x72\x5f\x74\x79\x70\ -\x65\x29\x20\x7b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x69\x70\x76\x36\ -\x5f\x6f\x70\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\x68\x64\x72\x20\x3d\x20\ +\x34\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\ +\x69\x70\x76\x36\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\ +\x72\x75\x63\x74\x20\x69\x70\x76\x36\x68\x64\x72\x20\x69\x70\x36\x20\x3d\x20\ \x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\ \x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\ -\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\ -\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\x68\x64\x72\x2c\0\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x66\x20\x28\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\ -\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x52\x4f\x55\x54\x49\x4e\x47\ -\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\ -\x63\x74\x20\x69\x70\x76\x36\x5f\x72\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\ -\x72\x74\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\ -\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\ -\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\ -\x72\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\ -\x28\x65\x78\x74\x5f\x72\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\ -\x36\x5f\x53\x52\x43\x52\x54\x5f\x54\x59\x50\x45\x5f\x32\x29\x20\x26\x26\0\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\x66\x66\x73\x65\x74\ -\x6f\x66\x28\x73\x74\x72\x75\x63\x74\x20\x72\x74\x32\x5f\x68\x64\x72\x2c\x20\ -\x61\x64\x64\x72\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x36\ +\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x69\x70\x36\x29\x2c\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x73\x72\x63\x20\x3d\x20\ +\x69\x70\x36\x2e\x73\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\ +\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x64\x73\x74\x20\x3d\x20\x69\x70\x36\x2e\ +\x64\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x70\x72\ +\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x36\x2e\x6e\x65\x78\x74\x68\x64\ +\x72\x3b\0\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\x68\x64\x72\x5f\x74\ +\x79\x70\x65\x29\x20\x7b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x69\x70\ +\x76\x36\x5f\x6f\x70\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\x68\x64\x72\x20\ +\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\ +\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\ +\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\ +\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\x68\x64\x72\x2c\0\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x66\x20\x28\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\ +\x6f\x6c\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x52\x4f\x55\x54\x49\ +\x4e\x47\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\ +\x72\x75\x63\x74\x20\x69\x70\x76\x36\x5f\x72\x74\x5f\x68\x64\x72\x20\x65\x78\ +\x74\x5f\x72\x74\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ \x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\ \x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\ -\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\ -\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x5f\x5f\x61\x74\x74\x72\x69\x62\x75\x74\ -\x65\x5f\x5f\x28\x28\x70\x61\x63\x6b\x65\x64\x29\x29\x20\x6f\x70\x74\x20\x3d\ -\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x6f\x70\x74\ -\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\x50\ -\x41\x44\x31\x29\x20\x3f\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\ -\x20\x31\x20\x3e\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\ -\x6e\x20\x2a\x20\x38\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\ -\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\ -\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\ -\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x2e\x74\x79\x70\ -\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\x48\x41\x4f\x29\x20\ -\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\ -\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\ -\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\ -\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\ -\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\ -\x78\x74\x5f\x73\x72\x63\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\ -\x6e\x74\x65\x64\x20\x3d\x20\x74\x72\x75\x65\x3b\0\x20\x20\x20\x20\x20\x20\x20\ -\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x65\x78\x74\ -\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\x6e\x20\x2b\x20\x31\x29\x20\x2a\x20\ -\x38\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\ -\x63\x6f\x6c\x20\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x6e\x65\x78\x74\x68\ -\x64\x72\x3b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x75\x6e\x73\x69\x67\x6e\x65\ -\x64\x20\x69\x6e\x74\x20\x69\x20\x3d\x20\x30\x3b\x20\x69\x20\x3c\x20\x49\x50\ -\x36\x5f\x45\x58\x54\x45\x4e\x53\x49\x4f\x4e\x53\x5f\x43\x4f\x55\x4e\x54\x3b\ -\x20\x2b\x2b\x69\x29\x20\x7b\0\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\ -\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\ -\x70\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\ -\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\ -\x70\x76\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x26\x26\0\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\ -\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\ -\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\ -\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x75\ -\x64\x70\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\ -\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\ -\x79\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\ -\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x34\x29\x20\ -\x7b\0\x20\x20\x20\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\x6d\x63\ -\x70\x79\x28\x26\x72\x73\x73\x5f\x69\x6e\x70\x75\x74\x5b\x2a\x62\x79\x74\x65\ -\x73\x5f\x77\x72\x69\x74\x74\x65\x6e\x5d\x2c\x20\x70\x74\x72\x2c\x20\x73\x69\ -\x7a\x65\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\ -\x6f\x2d\x3e\x69\x73\x5f\x75\x64\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x75\x64\x70\x68\x64\ -\x72\x20\x75\x64\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\ -\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\ -\x73\x6b\x62\x2c\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x75\x64\ -\x70\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x75\x64\x70\x29\x2c\0\x20\x20\x20\x20\ -\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\ -\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\x70\x65\x73\x20\x26\x20\x56\x49\ -\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\ -\x59\x50\x45\x5f\x49\x50\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x66\x6f\x72\x20\ +\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\ +\x74\x5f\x72\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\ +\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x69\x66\x20\x28\x28\x65\x78\x74\x5f\x72\x74\x2e\x74\x79\x70\x65\x20\x3d\ +\x3d\x20\x49\x50\x56\x36\x5f\x53\x52\x43\x52\x54\x5f\x54\x59\x50\x45\x5f\x32\ +\x29\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\ +\x66\x66\x73\x65\x74\x6f\x66\x28\x73\x74\x72\x75\x63\x74\x20\x72\x74\x32\x5f\ +\x68\x64\x72\x2c\x20\x61\x64\x64\x72\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\ +\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\ +\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\ +\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x3d\x20\x31\x3b\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x5f\x5f\x61\x74\x74\ +\x72\x69\x62\x75\x74\x65\x5f\x5f\x28\x28\x70\x61\x63\x6b\x65\x64\x29\x29\x20\ +\x6f\x70\x74\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\ +\x20\x28\x6f\x70\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\ +\x54\x4c\x56\x5f\x50\x41\x44\x31\x29\x20\x3f\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x5f\x6f\x66\x66\ +\x73\x65\x74\x20\x2b\x20\x31\x20\x3e\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\ +\x68\x64\x72\x6c\x65\x6e\x20\x2a\x20\x38\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\ +\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\ +\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\ +\x65\x74\x20\x2b\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x2c\0\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\ +\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\ +\x48\x41\x4f\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\ +\x65\x74\x20\x2b\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\ +\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\ +\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\ +\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\ +\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\x20\x3d\x20\x31\x3b\0\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\ +\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x20\x3d\x20\x74\x72\x75\x65\x3b\0\x20\x20\ +\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\ +\x20\x28\x65\x78\x74\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\x6e\x20\x2b\x20\ +\x31\x29\x20\x2a\x20\x38\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\ +\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\ +\x6e\x65\x78\x74\x68\x64\x72\x3b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x75\x6e\ +\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\x20\x69\x20\x3d\x20\x30\x3b\x20\x69\ +\x20\x3c\x20\x49\x50\x36\x5f\x45\x58\x54\x45\x4e\x53\x49\x4f\x4e\x53\x5f\x43\ +\x4f\x55\x4e\x54\x3b\x20\x2b\x2b\x69\x29\x20\x7b\0\x20\x20\x20\x20\x69\x66\x20\ +\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x21\x3d\x20\x30\x20\x26\ +\x26\x20\x21\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\ +\x74\x65\x64\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6c\ +\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\ +\x54\x4f\x5f\x54\x43\x50\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x74\x63\x70\x20\x3d\x20\x31\x3b\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\ +\x74\x63\x70\x68\x64\x72\x20\x74\x63\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\ +\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\ +\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\ +\x2c\x20\x26\x74\x63\x70\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\x63\x70\x29\ +\x2c\0\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\ +\x6f\x2e\x69\x73\x5f\x69\x70\x76\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\ +\x5f\x74\x63\x70\x20\x26\x26\0\x20\x20\x20\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\ +\x6e\x5f\x6d\x65\x6d\x63\x70\x79\x28\x26\x72\x73\x73\x5f\x69\x6e\x70\x75\x74\ +\x5b\x2a\x62\x79\x74\x65\x73\x5f\x77\x72\x69\x74\x74\x65\x6e\x5d\x2c\x20\x70\ +\x74\x72\x2c\x20\x73\x69\x7a\x65\x29\x3b\0\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\ +\x65\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\ +\x73\x5f\x69\x70\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\ +\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\x20\x26\x26\0\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\ +\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\ +\x64\x73\x74\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\ +\x65\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\ +\x73\x5f\x75\x64\x70\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\ +\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\ +\x68\x5f\x74\x79\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\ +\x54\x5f\x52\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\ +\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\ +\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\ +\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\ +\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x36\x29\x20\x7b\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\ +\x73\x5f\x75\x64\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x75\x64\x70\x68\x64\x72\x20\x75\x64\ +\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\ +\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\ +\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x75\x64\x70\x2c\x20\x73\ +\x69\x7a\x65\x6f\x66\x28\x75\x64\x70\x29\x2c\0\x20\x20\x20\x20\x66\x6f\x72\x20\ \x28\x62\x79\x74\x65\x20\x3d\x20\x30\x3b\x20\x62\x79\x74\x65\x20\x3c\x20\x48\ \x41\x53\x48\x5f\x43\x41\x4c\x43\x55\x4c\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\ \x46\x45\x52\x5f\x53\x49\x5a\x45\x3b\x20\x62\x79\x74\x65\x2b\x2b\x29\x20\x7b\0\ @@ -624,8 +618,7 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \x20\x20\x20\x20\x28\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\ \x74\x73\x20\x3c\x3c\x20\x31\x29\x20\x7c\x20\x28\x28\x6b\x65\x79\x5f\x62\x79\ \x74\x65\x20\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x3e\x3e\x20\x37\ -\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x68\x61\x73\x68\x29\ -\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\ +\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\ \x20\x74\x61\x62\x6c\x65\x5f\x69\x64\x78\x20\x3d\x20\x68\x61\x73\x68\x20\x25\ \x20\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\ \x6e\x73\x5f\x6c\x65\x6e\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ @@ -638,15 +631,15 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \x3b\0\x63\x68\x61\x72\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x6d\x61\x70\x73\ \0\x6c\x69\x63\x65\x6e\x73\x65\0\x62\x70\x66\x5f\x66\x6c\x6f\x77\x5f\x6b\x65\ \x79\x73\0\x62\x70\x66\x5f\x73\x6f\x63\x6b\0\0\0\0\x9f\xeb\x01\0\x20\0\0\0\0\0\ -\0\0\x14\0\0\0\x14\0\0\0\xbc\x0c\0\0\xd0\x0c\0\0\0\0\0\0\x08\0\0\0\x30\x02\0\0\ -\x01\0\0\0\0\0\0\0\x26\0\0\0\x10\0\0\0\x30\x02\0\0\xcb\0\0\0\0\0\0\0\x37\x02\0\ -\0\x60\x02\0\0\0\x5c\x08\0\x10\0\0\0\x37\x02\0\0\x91\x02\0\0\x0b\x74\x08\0\x20\ -\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x28\0\0\0\x37\x02\0\0\xa4\x02\0\0\x0e\x80\ -\x08\0\x50\0\0\0\x37\x02\0\0\xe9\x02\0\0\x0b\x84\x08\0\x88\0\0\0\x37\x02\0\0\ -\x29\x03\0\0\x10\x8c\x08\0\x90\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x98\0\0\0\x37\ -\x02\0\0\x29\x03\0\0\x10\x8c\x08\0\xa0\0\0\0\x37\x02\0\0\x42\x03\0\0\x16\x90\ -\x08\0\xa8\0\0\0\x37\x02\0\0\x42\x03\0\0\x0d\x90\x08\0\xc0\0\0\0\x37\x02\0\0\ -\x63\x03\0\0\x0a\x08\x06\0\xe8\0\0\0\x37\x02\0\0\x9a\x03\0\0\x1f\x18\x06\0\x38\ +\0\0\x14\0\0\0\x14\0\0\0\x6c\x0c\0\0\x80\x0c\0\0\0\0\0\0\x08\0\0\0\x30\x02\0\0\ +\x01\0\0\0\0\0\0\0\x26\0\0\0\x10\0\0\0\x30\x02\0\0\xc6\0\0\0\0\0\0\0\x37\x02\0\ +\0\x60\x02\0\0\0\x68\x08\0\x10\0\0\0\x37\x02\0\0\x91\x02\0\0\x0b\x80\x08\0\x20\ +\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x28\0\0\0\x37\x02\0\0\xa4\x02\0\0\x0e\x8c\ +\x08\0\x50\0\0\0\x37\x02\0\0\xe9\x02\0\0\x0b\x90\x08\0\x88\0\0\0\x37\x02\0\0\ +\x29\x03\0\0\x10\x98\x08\0\x90\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x98\0\0\0\x37\ +\x02\0\0\x29\x03\0\0\x10\x98\x08\0\xa0\0\0\0\x37\x02\0\0\x42\x03\0\0\x16\x9c\ +\x08\0\xa8\0\0\0\x37\x02\0\0\x42\x03\0\0\x0d\x9c\x08\0\xc0\0\0\0\x37\x02\0\0\ +\x63\x03\0\0\x0a\x10\x06\0\xe8\0\0\0\x37\x02\0\0\x9a\x03\0\0\x1f\x1c\x06\0\x38\ \x01\0\0\x37\x02\0\0\xca\x03\0\0\x0f\xac\x04\0\x40\x01\0\0\x37\x02\0\0\xe3\x03\ \0\0\x0c\x2c\x04\0\x50\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x58\x01\0\0\x37\x02\ \0\0\xf7\x03\0\0\x0b\x38\x04\0\x80\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\ @@ -666,313 +659,308 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \x37\x02\0\0\xde\x05\0\0\x1b\x08\x05\0\0\x03\0\0\x37\x02\0\0\xde\x05\0\0\x16\ \x08\x05\0\x08\x03\0\0\x37\x02\0\0\xff\x05\0\0\x1a\x14\x05\0\x10\x03\0\0\x37\ \x02\0\0\x22\x06\0\0\x18\x18\x05\0\x18\x03\0\0\x37\x02\0\0\x22\x06\0\0\x1c\x18\ -\x05\0\x30\x03\0\0\x37\x02\0\0\x6f\x05\0\0\x1d\x0c\x05\0\x38\x03\0\0\x37\x02\0\ -\0\x42\x06\0\0\x15\x74\x05\0\x48\x03\0\0\x37\x02\0\0\x42\x06\0\0\x1a\x74\x05\0\ -\x60\x03\0\0\x37\x02\0\0\x76\x06\0\0\x0d\x78\x05\0\x80\x03\0\0\x37\x02\0\0\xa0\ -\x06\0\0\x1a\x7c\x05\0\x90\x03\0\0\x37\x02\0\0\xbe\x06\0\0\x1b\x84\x05\0\xb0\ -\x03\0\0\x37\x02\0\0\xa0\x06\0\0\x1a\x7c\x05\0\xb8\x03\0\0\x37\x02\0\0\xe2\x06\ -\0\0\x13\x88\x05\0\xe0\x03\0\0\x37\x02\0\0\x33\x07\0\0\x11\x90\x05\0\xf0\x03\0\ -\0\x37\x02\0\0\x33\x07\0\0\x11\x90\x05\0\xf8\x03\0\0\x37\x02\0\0\0\0\0\0\0\0\0\ -\0\x18\x04\0\0\x37\x02\0\0\x4a\x07\0\0\x15\x34\x06\0\x20\x04\0\0\x37\x02\0\0\ -\x4a\x07\0\0\x09\x34\x06\0\x28\x04\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x78\x04\0\0\ -\x37\x02\0\0\x69\x07\0\0\x19\x38\x06\0\x80\x04\0\0\x37\x02\0\0\x69\x07\0\0\x20\ -\x38\x06\0\xa0\x04\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xf0\x04\0\0\x37\x02\0\0\x8b\ -\x07\0\0\x17\x20\x05\0\0\x05\0\0\x37\x02\0\0\xa6\x07\0\0\x18\x28\x05\0\x30\x05\ -\0\0\x37\x02\0\0\x8b\x07\0\0\x17\x20\x05\0\x48\x05\0\0\x37\x02\0\0\xc7\x07\0\0\ -\x0f\x2c\x05\0\x70\x05\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\x80\x05\0\0\ -\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\x88\x05\0\0\x37\x02\0\0\x0c\x08\0\0\x1d\ -\x44\x05\0\xc8\x05\0\0\x37\x02\0\0\x2f\x08\0\0\x1d\x48\x05\0\x08\x06\0\0\x37\ -\x02\0\0\x52\x08\0\0\x1b\x50\x05\0\x10\x06\0\0\x37\x02\0\0\x75\x08\0\0\x05\x3c\ -\x02\0\x58\x06\0\0\x37\x02\0\0\x8d\x08\0\0\x19\xc4\x02\0\xc8\x06\0\0\x37\x02\0\ -\0\0\0\0\0\0\0\0\0\xd0\x06\0\0\x37\x02\0\0\xb3\x08\0\0\x0f\xd4\x02\0\xf8\x06\0\ -\0\x37\x02\0\0\x5c\x05\0\0\x0d\xdc\x02\0\x10\x07\0\0\x37\x02\0\0\x5c\x05\0\0\ -\x0d\xdc\x02\0\x18\x07\0\0\x37\x02\0\0\xf8\x08\0\0\x0d\xec\x02\0\x38\x07\0\0\ -\x37\x02\0\0\x27\x09\0\0\x20\xf0\x02\0\x60\x07\0\0\x37\x02\0\0\x53\x09\0\0\x13\ -\xf8\x02\0\x88\x07\0\0\x37\x02\0\0\x33\x07\0\0\x11\0\x03\0\xa0\x07\0\0\x37\x02\ -\0\0\x33\x07\0\0\x11\0\x03\0\xa8\x07\0\0\x37\x02\0\0\x9b\x09\0\0\x19\x10\x03\0\ -\xb0\x07\0\0\x37\x02\0\0\x9b\x09\0\0\x34\x10\x03\0\xd8\x07\0\0\x37\x02\0\0\xd1\ -\x09\0\0\x15\x24\x03\0\xe8\x07\0\0\x37\x02\0\0\x12\x0a\0\0\x17\x20\x03\0\x10\ -\x08\0\0\x37\x02\0\0\x49\x0a\0\0\x15\x30\x03\0\x28\x08\0\0\x37\x02\0\0\x49\x0a\ -\0\0\x15\x30\x03\0\x30\x08\0\0\x37\x02\0\0\x64\x0a\0\0\x27\x40\x03\0\x58\x08\0\ -\0\x37\x02\0\0\x8f\x0a\0\0\x27\x5c\x03\0\x68\x08\0\0\x37\x02\0\0\xbf\x0a\0\0\ -\x1c\xc0\x03\0\x70\x08\0\0\x37\x02\0\0\xfb\x0a\0\0\x20\xcc\x03\0\x80\x08\0\0\ -\x37\x02\0\0\xfb\x0a\0\0\x2f\xcc\x03\0\x88\x08\0\0\x37\x02\0\0\xfb\x0a\0\0\x36\ -\xcc\x03\0\x90\x08\0\0\x37\x02\0\0\xfb\x0a\0\0\x15\xcc\x03\0\xf8\x08\0\0\x37\ -\x02\0\0\x37\x0b\0\0\x43\x70\x03\0\x18\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x20\ -\x09\0\0\x37\x02\0\0\x37\x0b\0\0\x17\x70\x03\0\x48\x09\0\0\x37\x02\0\0\x49\x0a\ -\0\0\x15\x78\x03\0\x60\x09\0\0\x37\x02\0\0\x49\x0a\0\0\x15\x78\x03\0\x68\x09\0\ -\0\x37\x02\0\0\x87\x0b\0\0\x19\x88\x03\0\x70\x09\0\0\x37\x02\0\0\x87\x0b\0\0\ -\x15\x88\x03\0\x78\x09\0\0\x37\x02\0\0\xb7\x0b\0\0\x19\x90\x03\0\x80\x09\0\0\ -\x37\x02\0\0\xe7\x0b\0\0\x1b\x8c\x03\0\xb0\x09\0\0\x37\x02\0\0\x22\x0c\0\0\x19\ -\xa0\x03\0\xc8\x09\0\0\x37\x02\0\0\x22\x0c\0\0\x19\xa0\x03\0\xd0\x09\0\0\x37\ -\x02\0\0\x41\x0c\0\0\x2b\xb0\x03\0\xf0\x09\0\0\x37\x02\0\0\xbf\x0a\0\0\x1f\xc0\ -\x03\0\x10\x0a\0\0\x37\x02\0\0\x70\x0c\0\0\x21\xe0\x03\0\x20\x0a\0\0\x37\x02\0\ -\0\x98\x0c\0\0\x20\xf0\x03\0\x28\x0a\0\0\x37\x02\0\0\x98\x0c\0\0\x2c\xf0\x03\0\ -\x40\x0a\0\0\x37\x02\0\0\x98\x0c\0\0\x14\xf0\x03\0\x50\x0a\0\0\x37\x02\0\0\xc8\ -\x0c\0\0\x20\xec\x03\0\x58\x0a\0\0\x37\x02\0\0\x75\x08\0\0\x05\x3c\x02\0\xa0\ -\x0a\0\0\x37\x02\0\0\xf0\x0c\0\0\x38\xcc\x02\0\xc0\x0a\0\0\x37\x02\0\0\xf0\x0c\ -\0\0\x05\xcc\x02\0\xd8\x0a\0\0\x37\x02\0\0\x75\x08\0\0\x05\x3c\x02\0\xe8\x0a\0\ -\0\x37\x02\0\0\x2e\x0d\0\0\x1c\xd0\x06\0\xf0\x0a\0\0\x37\x02\0\0\x2e\x0d\0\0\ -\x10\xd0\x06\0\xf8\x0a\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\x37\x02\0\0\ -\x69\x07\0\0\x19\xd4\x06\0\x50\x0b\0\0\x37\x02\0\0\x69\x07\0\0\x20\xd4\x06\0\ -\x88\x0b\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x0c\x07\0\x98\x0b\0\0\x37\x02\0\0\x54\ -\x0d\0\0\x1d\x0c\x07\0\xa0\x0b\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x0c\x07\0\xb0\ -\x0b\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\xe0\x06\0\xe0\x0b\0\0\x37\x02\0\0\x83\x0d\ -\0\0\x1d\xe0\x06\0\xf0\x0b\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\xe0\x06\0\0\x0c\0\0\ -\x37\x02\0\0\0\0\0\0\0\0\0\0\xd0\x0c\0\0\x37\x02\0\0\xb2\x0d\0\0\x20\x74\x06\0\ -\xd8\x0c\0\0\x37\x02\0\0\xb2\x0d\0\0\x27\x74\x06\0\0\x0d\0\0\x37\x02\0\0\xdb\ -\x0d\0\0\x27\xb0\x06\0\x08\x0d\0\0\x37\x02\0\0\xdb\x0d\0\0\x14\xb0\x06\0\x10\ -\x0d\0\0\x37\x02\0\0\x24\x0e\0\0\x05\xa4\x01\0\x20\x0d\0\0\x37\x02\0\0\x24\x0e\ -\0\0\x05\xa4\x01\0\x50\x0d\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x60\x05\0\x60\x0d\0\ -\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x0d\0\0\x37\x02\0\0\xb2\x0d\0\0\x20\x50\x07\ -\0\x78\x0d\0\0\x37\x02\0\0\xb2\x0d\0\0\x27\x50\x07\0\xb0\x0d\0\0\x37\x02\0\0\ -\x54\x0d\0\0\x2d\x88\x07\0\xc0\x0d\0\0\x37\x02\0\0\x54\x0d\0\0\x1d\x88\x07\0\ -\xc8\x0d\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x88\x07\0\xd8\x0d\0\0\x37\x02\0\0\x83\ -\x0d\0\0\x2d\x5c\x07\0\x08\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x1d\x5c\x07\0\x18\ -\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\x5c\x07\0\x30\x0e\0\0\x37\x02\0\0\x61\x0e\ -\0\0\x1a\xac\x05\0\x40\x0e\0\0\x37\x02\0\0\x7f\x0e\0\0\x1b\xb4\x05\0\x50\x0e\0\ -\0\x37\x02\0\0\x61\x0e\0\0\x1a\xac\x05\0\x58\x0e\0\0\x37\x02\0\0\xa3\x0e\0\0\ -\x13\xb8\x05\0\x80\x0e\0\0\x37\x02\0\0\x33\x07\0\0\x11\xc0\x05\0\x90\x0e\0\0\ -\x37\x02\0\0\x33\x07\0\0\x11\xc0\x05\0\xa0\x0e\0\0\x37\x02\0\0\x75\x08\0\0\x05\ -\x3c\x02\0\xb0\x0e\0\0\x37\x02\0\0\xf4\x0e\0\0\x27\xd4\x07\0\xc0\x0e\0\0\x37\ -\x02\0\0\xf4\x0e\0\0\x14\xd4\x07\0\xe0\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\xd8\ -\x07\0\xf0\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x1d\xd8\x07\0\xf8\x0e\0\0\x37\x02\0\ -\0\x83\x0d\0\0\x2d\xd8\x07\0\x20\x0f\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x0f\0\ -\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x80\x0f\0\0\x37\x02\0\0\x54\x0d\0\0\x1d\x04\x08\ -\0\x88\x0f\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x04\x08\0\x98\x0f\0\0\x37\x02\0\0\ -\x24\x0e\0\0\x05\xa4\x01\0\xe8\x0f\0\0\x37\x02\0\0\x24\x0e\0\0\x05\xa4\x01\0\ -\x20\x10\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x30\x10\0\0\x37\x02\0\0\x3d\x0f\0\0\ -\x05\xdc\x01\0\x38\x10\0\0\x37\x02\0\0\x7f\x0f\0\0\x23\xd0\x01\0\x50\x10\0\0\ -\x37\x02\0\0\0\0\0\0\0\0\0\0\x58\x10\0\0\x37\x02\0\0\xb3\x0f\0\0\x1b\xe0\x01\0\ -\x78\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x90\x10\0\0\x37\x02\0\0\x03\ -\x10\0\0\x19\xe4\x01\0\xa8\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xc0\ -\x10\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\xc8\x10\0\0\x37\x02\0\0\xda\x0f\ -\0\0\x11\xf4\x01\0\x08\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x10\x11\0\ -\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x18\x11\0\0\x37\x02\0\0\xda\x0f\0\0\ -\x11\xf4\x01\0\x40\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x60\x11\0\0\ -\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x68\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\ -\xf4\x01\0\x90\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xb0\x11\0\0\x37\ -\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\xb8\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\ -\x01\0\xf8\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\0\x12\0\0\x37\x02\0\0\ -\x31\x10\0\0\x2d\x08\x02\0\x08\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\ -\x48\x12\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x50\x12\0\0\x37\x02\0\0\x31\ -\x10\0\0\x2d\x08\x02\0\x58\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x98\ -\x12\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xa0\x12\0\0\x37\x02\0\0\x31\x10\ -\0\0\x2d\x08\x02\0\xa8\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xd0\x12\0\ -\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xd8\x12\0\0\x37\x02\0\0\x31\x10\0\0\ -\x2d\x08\x02\0\xe0\x12\0\0\x37\x02\0\0\x3d\x0f\0\0\x3d\xdc\x01\0\xf0\x12\0\0\ -\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\x01\0\0\x13\0\0\x37\x02\0\0\x7d\x10\0\0\x0d\ -\xa4\x08\0\x10\x13\0\0\x37\x02\0\0\x7d\x10\0\0\x0d\xa4\x08\0\x18\x13\0\0\x37\ -\x02\0\0\x91\x10\0\0\x2e\xa8\x08\0\x38\x13\0\0\x37\x02\0\0\x91\x10\0\0\x24\xa8\ -\x08\0\x40\x13\0\0\x37\x02\0\0\x91\x10\0\0\x13\xa8\x08\0\x50\x13\0\0\x37\x02\0\ -\0\x91\x10\0\0\x2e\xa8\x08\0\x58\x13\0\0\x37\x02\0\0\xd0\x10\0\0\x15\xb4\x08\0\ -\x70\x13\0\0\x37\x02\0\0\x18\x11\0\0\x11\xc0\x08\0\x78\x13\0\0\x37\x02\0\0\0\0\ -\0\0\0\0\0\0\x98\x13\0\0\x37\x02\0\0\x31\x11\0\0\x01\xe4\x08\0\xa0\x13\0\0\x37\ -\x02\0\0\x33\x11\0\0\x18\xc4\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2d\x01\0\0\0\0\x03\0\ -\x98\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6f\x01\0\0\0\0\x03\0\xb8\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x46\x01\0\0\0\0\x03\0\x78\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\ -\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\0\0\0\0\x03\0\x20\ -\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\x01\0\0\0\0\x03\0\xe8\x04\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\xcc\0\0\0\0\0\x03\0\x18\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x25\x01\0\ -\0\0\0\x03\0\xe8\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x32\x02\0\0\0\0\x03\0\x38\x03\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2a\x02\0\0\0\0\x03\0\x28\x0e\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\xec\0\0\0\0\0\x03\0\xf8\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x22\x02\0\0\0\ -\0\x03\0\xe8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5f\x01\0\0\0\0\x03\0\xd0\x0c\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\x76\x01\0\0\0\0\x03\0\xa0\x04\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\xe8\x01\0\0\0\0\x03\0\x28\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x97\x01\0\0\0\0\ -\x03\0\x68\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x42\x02\0\0\0\0\x03\0\xa0\x0e\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xe0\x01\0\0\0\0\x03\0\x50\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\xaf\x01\0\0\0\0\x03\0\xc0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd0\x01\0\0\0\0\ -\x03\0\x50\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa7\x01\0\0\0\0\x03\0\x48\x08\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x7e\x01\0\0\0\0\x03\0\x10\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x67\x01\0\0\0\0\x03\0\x20\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf9\x01\0\0\0\0\ -\x03\0\xd8\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x86\x01\0\0\0\0\x03\0\xf8\x08\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x3a\x02\0\0\0\0\x03\0\x68\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\xd8\x01\0\0\0\0\x03\0\xe0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\x01\0\0\0\0\ -\x03\0\x38\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfc\0\0\0\0\0\x03\0\xe8\x09\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\x3e\x01\0\0\0\0\x03\0\xd8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\xf4\0\0\0\0\0\x03\0\x98\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x03\0\ -\xc8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc8\x01\0\0\0\0\x03\0\x70\x0d\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x57\x01\0\0\0\0\x03\0\x98\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1d\ -\x01\0\0\0\0\x03\0\xb0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\0\0\0\0\0\x03\0\xe0\ -\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf1\x01\0\0\0\0\x03\0\0\x0c\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\x1a\x02\0\0\0\0\x03\0\xf8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe4\0\0\0\ -\0\0\x03\0\0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x01\0\0\0\0\x03\0\xb0\x0e\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\x36\x01\0\0\0\0\x03\0\xc0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\xdc\0\0\0\0\0\x03\0\xd8\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x12\x02\0\0\0\0\ -\x03\0\x08\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4f\x01\0\0\0\0\x03\0\xf0\x0e\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x15\x01\0\0\0\0\x03\0\x08\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x4a\x02\0\0\0\0\x03\0\xe8\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\x02\0\0\0\0\ -\x03\0\x20\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x01\0\0\0\0\x03\0\x48\x10\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x8e\x01\0\0\0\0\x03\0\0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x0c\x01\0\0\0\0\x03\0\xa0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x59\0\0\0\x12\0\x03\ -\0\0\0\0\0\0\0\0\0\xb0\x13\0\0\0\0\0\0\x3e\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\ -\x28\0\0\0\0\0\0\0\x01\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\ -\x86\0\0\0\x11\0\x05\0\x50\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x7d\0\0\0\x11\0\x06\ -\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x35\0\0\0\x50\ -\0\0\0\0\0\0\0\x01\0\0\0\x36\0\0\0\x58\x13\0\0\0\0\0\0\x01\0\0\0\x37\0\0\0\x20\ -\x05\0\0\0\0\0\0\x04\0\0\0\x35\0\0\0\x2c\x05\0\0\0\0\0\0\x04\0\0\0\x36\0\0\0\ -\x38\x05\0\0\0\0\0\0\x04\0\0\0\x37\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\0\x38\0\0\ -\0\x2c\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x50\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x70\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x90\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xb0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xd0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xf0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x10\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x30\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x50\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x01\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x70\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x01\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x90\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x01\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\xb0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x01\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\xd0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x01\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\xf0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x02\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x10\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x02\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x30\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x02\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\x50\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x02\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\x70\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x02\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x90\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x02\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x02\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x02\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x03\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\ -\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x40\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x60\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x80\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x03\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\xa0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x03\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xc0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x03\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\xe0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x03\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x04\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x20\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x04\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x40\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x04\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x60\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x04\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\x80\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x04\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\xa0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x04\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x04\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x04\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x05\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x05\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\ -\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x70\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x90\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\xb0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x05\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\xd0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x05\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xf0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x06\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x10\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x06\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x30\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x06\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x50\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x06\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x70\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x06\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x90\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x06\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\xb0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x06\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\xd0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x06\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x07\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x10\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x07\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x07\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x07\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\ -\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xa0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\xc0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\xe0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x07\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x08\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x20\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x08\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x40\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x08\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x60\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x08\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x80\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x08\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\xa0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x08\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\xc0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x08\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\xe0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x08\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x09\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\x20\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x09\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x40\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x09\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x09\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x09\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\ -\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xd0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\xf0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x10\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x30\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0a\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x50\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0a\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x70\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0a\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x90\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0a\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\xb0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0a\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\xd0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x0a\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\xf0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0b\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x10\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0b\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\x30\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0b\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\x50\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0b\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0b\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0b\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0b\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\ -\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\ -\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x20\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x40\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x60\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0c\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x80\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0c\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xa0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0c\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\xc0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0c\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\xe0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x3e\x3f\x40\x41\x42\0\ -\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\ -\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\ -\x2e\x65\x78\x74\0\x2e\x72\x65\x6c\x73\x6f\x63\x6b\x65\x74\0\x2e\x6d\x61\x70\ -\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\x6e\x66\x69\x67\ -\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\ -\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\ -\x72\x73\x69\x67\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x74\x61\x70\x5f\x72\x73\ -\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\ -\x61\x62\x6c\x65\0\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\ -\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\x42\x42\x30\x5f\x39\0\x4c\x42\x42\x30\ -\x5f\x37\x39\0\x4c\x42\x42\x30\x5f\x35\x39\0\x4c\x42\x42\x30\x5f\x34\x39\0\x4c\ -\x42\x42\x30\x5f\x38\x38\0\x4c\x42\x42\x30\x5f\x36\x38\0\x4c\x42\x42\x30\x5f\ -\x35\x38\0\x4c\x42\x42\x30\x5f\x34\x38\0\x4c\x42\x42\x30\x5f\x33\x38\0\x4c\x42\ -\x42\x30\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x31\x30\x38\0\x4c\x42\x42\x30\x5f\ -\x39\x37\0\x4c\x42\x42\x30\x5f\x37\x37\0\x4c\x42\x42\x30\x5f\x31\x37\0\x4c\x42\ -\x42\x30\x5f\x31\x30\x37\0\x4c\x42\x42\x30\x5f\x38\x36\0\x4c\x42\x42\x30\x5f\ -\x34\x36\0\x4c\x42\x42\x30\x5f\x31\x30\x36\0\x4c\x42\x42\x30\x5f\x39\x35\0\x4c\ -\x42\x42\x30\x5f\x37\x35\0\x4c\x42\x42\x30\x5f\x36\x35\0\x4c\x42\x42\x30\x5f\ -\x34\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\x42\x30\x5f\x36\x34\0\x4c\x42\x42\ -\x30\x5f\x34\x34\0\x4c\x42\x42\x30\x5f\x33\x34\0\x4c\x42\x42\x30\x5f\x31\x30\ -\x34\0\x4c\x42\x42\x30\x5f\x35\x33\0\x4c\x42\x42\x30\x5f\x34\x33\0\x4c\x42\x42\ -\x30\x5f\x33\x33\0\x4c\x42\x42\x30\x5f\x32\x33\0\x4c\x42\x42\x30\x5f\x31\x30\ -\x33\0\x4c\x42\x42\x30\x5f\x39\x32\0\x4c\x42\x42\x30\x5f\x38\x32\0\x4c\x42\x42\ -\x30\x5f\x35\x32\0\x4c\x42\x42\x30\x5f\x34\x32\0\x4c\x42\x42\x30\x5f\x32\x32\0\ -\x4c\x42\x42\x30\x5f\x31\x30\x32\0\x4c\x42\x42\x30\x5f\x38\x31\0\x4c\x42\x42\ -\x30\x5f\x35\x31\0\x4c\x42\x42\x30\x5f\x31\x31\0\x4c\x42\x42\x30\x5f\x31\x30\ -\x31\0\x4c\x42\x42\x30\x5f\x39\x30\0\x4c\x42\x42\x30\x5f\x38\x30\0\x4c\x42\x42\ -\x30\x5f\x37\x30\0\x4c\x42\x42\x30\x5f\x36\x30\0\x4c\x42\x42\x30\x5f\x35\x30\0\ -\x4c\x42\x42\x30\x5f\x34\x30\0\x4c\x42\x42\x30\x5f\x32\x30\0\x4c\x42\x42\x30\ -\x5f\x31\x30\x30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\ -\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe5\x4a\0\0\0\0\0\0\x53\x02\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\x01\0\0\0\x06\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x31\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\xb0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x2d\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\xb0\x3d\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\ -\x10\0\0\0\0\0\0\0\x38\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\ -\x13\0\0\0\0\0\0\x78\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\x7e\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x14\0\0\0\0\0\ -\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\0\0\ -\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\x14\0\0\0\0\0\0\xf5\x16\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb4\0\0\0\x09\0\0\0\ -\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe0\x3d\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x0c\0\ -\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x24\0\0\0\x01\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\x68\x2b\0\0\0\0\0\0\xf0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\x20\x3e\0\0\0\0\0\0\xc0\x0c\0\0\0\0\0\0\x0c\0\0\0\x09\0\0\0\x08\0\ -\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x6f\0\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\ -\0\0\0\0\0\0\xe0\x4a\0\0\0\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x58\ -\x38\0\0\0\0\0\0\x58\x05\0\0\0\0\0\0\x01\0\0\0\x34\0\0\0\x08\0\0\0\0\0\0\0\x18\ -\0\0\0\0\0\0\0"; +\x05\0\x28\x03\0\0\x37\x02\0\0\x6f\x05\0\0\x1d\x0c\x05\0\x40\x03\0\0\x37\x02\0\ +\0\x42\x06\0\0\x17\x20\x05\0\x50\x03\0\0\x37\x02\0\0\x5d\x06\0\0\x18\x28\x05\0\ +\x80\x03\0\0\x37\x02\0\0\x42\x06\0\0\x17\x20\x05\0\x88\x03\0\0\x37\x02\0\0\x7e\ +\x06\0\0\x0f\x2c\x05\0\xb0\x03\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\xc0\ +\x03\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\xd8\x03\0\0\x37\x02\0\0\xc3\x06\ +\0\0\x1d\x44\x05\0\x18\x04\0\0\x37\x02\0\0\xe6\x06\0\0\x1d\x48\x05\0\x58\x04\0\ +\0\x37\x02\0\0\x09\x07\0\0\x1b\x50\x05\0\x60\x04\0\0\x37\x02\0\0\x2c\x07\0\0\ +\x05\x3c\x02\0\xa8\x04\0\0\x37\x02\0\0\x44\x07\0\0\x19\xc4\x02\0\x10\x05\0\0\ +\x37\x02\0\0\0\0\0\0\0\0\0\0\x18\x05\0\0\x37\x02\0\0\x6a\x07\0\0\x0f\xd4\x02\0\ +\x40\x05\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xdc\x02\0\x50\x05\0\0\x37\x02\0\0\x5c\ +\x05\0\0\x0d\xdc\x02\0\x58\x05\0\0\x37\x02\0\0\xaf\x07\0\0\x0d\xec\x02\0\x80\ +\x05\0\0\x37\x02\0\0\xde\x07\0\0\x20\xf0\x02\0\xa8\x05\0\0\x37\x02\0\0\x0a\x08\ +\0\0\x13\xf8\x02\0\xc8\x05\0\0\x37\x02\0\0\x52\x08\0\0\x11\0\x03\0\xd8\x05\0\0\ +\x37\x02\0\0\x52\x08\0\0\x11\0\x03\0\xe0\x05\0\0\x37\x02\0\0\x69\x08\0\0\x19\ +\x10\x03\0\xe8\x05\0\0\x37\x02\0\0\x69\x08\0\0\x34\x10\x03\0\x10\x06\0\0\x37\ +\x02\0\0\x9f\x08\0\0\x15\x24\x03\0\x20\x06\0\0\x37\x02\0\0\xe0\x08\0\0\x17\x20\ +\x03\0\x48\x06\0\0\x37\x02\0\0\x17\x09\0\0\x15\x30\x03\0\x58\x06\0\0\x37\x02\0\ +\0\x17\x09\0\0\x15\x30\x03\0\x60\x06\0\0\x37\x02\0\0\x32\x09\0\0\x27\x40\x03\0\ +\x88\x06\0\0\x37\x02\0\0\x5d\x09\0\0\x27\x5c\x03\0\x98\x06\0\0\x37\x02\0\0\x8d\ +\x09\0\0\x1c\xc0\x03\0\xa0\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x20\xcc\x03\0\xb0\ +\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x2f\xcc\x03\0\xb8\x06\0\0\x37\x02\0\0\xc9\x09\ +\0\0\x36\xcc\x03\0\xc0\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x15\xcc\x03\0\x28\x07\0\ +\0\x37\x02\0\0\x05\x0a\0\0\x43\x70\x03\0\x48\x07\0\0\x37\x02\0\0\0\0\0\0\0\0\0\ +\0\x50\x07\0\0\x37\x02\0\0\x05\x0a\0\0\x17\x70\x03\0\x78\x07\0\0\x37\x02\0\0\ +\x17\x09\0\0\x15\x78\x03\0\x88\x07\0\0\x37\x02\0\0\x17\x09\0\0\x15\x78\x03\0\ +\x90\x07\0\0\x37\x02\0\0\x55\x0a\0\0\x19\x88\x03\0\x98\x07\0\0\x37\x02\0\0\x55\ +\x0a\0\0\x15\x88\x03\0\xa0\x07\0\0\x37\x02\0\0\x85\x0a\0\0\x19\x90\x03\0\xa8\ +\x07\0\0\x37\x02\0\0\xb5\x0a\0\0\x1b\x8c\x03\0\xd8\x07\0\0\x37\x02\0\0\xf0\x0a\ +\0\0\x19\xa0\x03\0\xe8\x07\0\0\x37\x02\0\0\xf0\x0a\0\0\x19\xa0\x03\0\xf0\x07\0\ +\0\x37\x02\0\0\x0f\x0b\0\0\x2b\xb0\x03\0\x10\x08\0\0\x37\x02\0\0\x8d\x09\0\0\ +\x1f\xc0\x03\0\x30\x08\0\0\x37\x02\0\0\x3e\x0b\0\0\x21\xe0\x03\0\x48\x08\0\0\ +\x37\x02\0\0\x66\x0b\0\0\x20\xf0\x03\0\x50\x08\0\0\x37\x02\0\0\x66\x0b\0\0\x2c\ +\xf0\x03\0\x60\x08\0\0\x37\x02\0\0\x66\x0b\0\0\x14\xf0\x03\0\x68\x08\0\0\x37\ +\x02\0\0\x96\x0b\0\0\x20\xec\x03\0\x70\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\ +\x02\0\xb8\x08\0\0\x37\x02\0\0\xbe\x0b\0\0\x38\xcc\x02\0\xd8\x08\0\0\x37\x02\0\ +\0\xbe\x0b\0\0\x05\xcc\x02\0\xe8\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\ +\0\x09\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\x18\x09\0\0\x37\x02\0\0\xfc\ +\x0b\0\0\x15\x74\x05\0\x28\x09\0\0\x37\x02\0\0\xfc\x0b\0\0\x1a\x74\x05\0\x40\ +\x09\0\0\x37\x02\0\0\x30\x0c\0\0\x0d\x78\x05\0\x60\x09\0\0\x37\x02\0\0\x5a\x0c\ +\0\0\x1a\x7c\x05\0\x70\x09\0\0\x37\x02\0\0\x78\x0c\0\0\x1b\x84\x05\0\x90\x09\0\ +\0\x37\x02\0\0\x5a\x0c\0\0\x1a\x7c\x05\0\x98\x09\0\0\x37\x02\0\0\x9c\x0c\0\0\ +\x13\x88\x05\0\xb8\x09\0\0\x37\x02\0\0\x52\x08\0\0\x11\x90\x05\0\xc8\x09\0\0\ +\x37\x02\0\0\x52\x08\0\0\x11\x90\x05\0\xd0\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\ +\xf0\x09\0\0\x37\x02\0\0\xed\x0c\0\0\x15\x38\x06\0\xf8\x09\0\0\x37\x02\0\0\xed\ +\x0c\0\0\x09\x38\x06\0\0\x0a\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x50\x0a\0\0\x37\ +\x02\0\0\x0c\x0d\0\0\x19\x3c\x06\0\x58\x0a\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\x3c\ +\x06\0\x78\x0a\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xc0\x0a\0\0\x37\x02\0\ +\0\x6b\x0d\0\0\x1c\xd4\x06\0\xc8\x0a\0\0\x37\x02\0\0\x6b\x0d\0\0\x10\xd4\x06\0\ +\xd0\x0a\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x20\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\ +\x19\xd8\x06\0\x28\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\xd8\x06\0\x48\x0b\0\0\ +\x37\x02\0\0\x91\x0d\0\0\x2d\xe4\x06\0\x58\x0b\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\ +\xe4\x06\0\x68\x0b\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\xe4\x06\0\x98\x0b\0\0\x37\ +\x02\0\0\xc0\x0d\0\0\x2d\x10\x07\0\xa8\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x10\ +\x07\0\xb8\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\x10\x07\0\xe0\x0b\0\0\x37\x02\0\ +\0\x2e\x0d\0\0\x05\xa4\x01\0\xa8\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x78\x06\0\ +\xb0\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x27\x78\x06\0\xd8\x0c\0\0\x37\x02\0\0\x18\ +\x0e\0\0\x27\xb4\x06\0\xe0\x0c\0\0\x37\x02\0\0\x18\x0e\0\0\x14\xb4\x06\0\xe8\ +\x0c\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xf8\x0c\0\0\x37\x02\0\0\x2e\x0d\ +\0\0\x05\xa4\x01\0\x10\x0d\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x54\x07\0\x18\x0d\0\ +\0\x37\x02\0\0\xef\x0d\0\0\x27\x54\x07\0\x38\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\ +\x2d\x60\x07\0\x48\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\x60\x07\0\x58\x0d\0\0\ +\x37\x02\0\0\x91\x0d\0\0\x2d\x60\x07\0\x88\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\ +\x8c\x07\0\x98\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x8c\x07\0\xa8\x0d\0\0\x37\ +\x02\0\0\xc0\x0d\0\0\x2d\x8c\x07\0\xd0\x0d\0\0\x37\x02\0\0\x61\x0e\0\0\x27\xd8\ +\x07\0\xe0\x0d\0\0\x37\x02\0\0\x61\x0e\0\0\x14\xd8\x07\0\xe8\x0d\0\0\x37\x02\0\ +\0\x91\x0d\0\0\x2d\xdc\x07\0\xf8\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\xdc\x07\0\ +\x08\x0e\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\xdc\x07\0\x38\x0e\0\0\x37\x02\0\0\x2e\ +\x0d\0\0\x05\xa4\x01\0\x88\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x17\xa4\x01\0\x98\ +\x0e\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\x08\x08\0\xb0\x0e\0\0\x37\x02\0\0\x2e\x0d\ +\0\0\x05\xa4\x01\0\xf8\x0e\0\0\x37\x02\0\0\xaa\x0e\0\0\x1a\xac\x05\0\x08\x0f\0\ +\0\x37\x02\0\0\xc8\x0e\0\0\x1b\xb4\x05\0\x18\x0f\0\0\x37\x02\0\0\xaa\x0e\0\0\ +\x1a\xac\x05\0\x20\x0f\0\0\x37\x02\0\0\xec\x0e\0\0\x13\xb8\x05\0\x40\x0f\0\0\ +\x37\x02\0\0\x52\x08\0\0\x11\xc0\x05\0\x50\x0f\0\0\x37\x02\0\0\x52\x08\0\0\x11\ +\xc0\x05\0\x60\x0f\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\x98\x0f\0\0\x37\ +\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xa8\x0f\0\0\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\ +\x01\0\xb0\x0f\0\0\x37\x02\0\0\x7f\x0f\0\0\x23\xd0\x01\0\xc8\x0f\0\0\x37\x02\0\ +\0\0\0\0\0\0\0\0\0\xd0\x0f\0\0\x37\x02\0\0\xb3\x0f\0\0\x1b\xe0\x01\0\xf0\x0f\0\ +\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x08\x10\0\0\x37\x02\0\0\x03\x10\0\0\ +\x19\xe4\x01\0\x20\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x38\x10\0\0\ +\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x40\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\ +\xf4\x01\0\x80\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x88\x10\0\0\x37\ +\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x90\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\ +\x01\0\xb8\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xd8\x10\0\0\x37\x02\0\ +\0\x31\x10\0\0\x2d\x08\x02\0\xe0\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\ +\x08\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x28\x11\0\0\x37\x02\0\0\x31\ +\x10\0\0\x2d\x08\x02\0\x30\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x70\ +\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x78\x11\0\0\x37\x02\0\0\x31\x10\ +\0\0\x2d\x08\x02\0\x80\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xc0\x11\0\ +\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xc8\x11\0\0\x37\x02\0\0\x31\x10\0\0\ +\x2d\x08\x02\0\xd0\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x10\x12\0\0\ +\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x18\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\ +\x08\x02\0\x20\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x48\x12\0\0\x37\ +\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x50\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\ +\x02\0\x58\x12\0\0\x37\x02\0\0\x3d\x0f\0\0\x3d\xdc\x01\0\x68\x12\0\0\x37\x02\0\ +\0\x3d\x0f\0\0\x05\xdc\x01\0\x78\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x2e\xb0\x08\0\ +\x98\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x24\xb0\x08\0\xb0\x12\0\0\x37\x02\0\0\x7d\ +\x10\0\0\x13\xb0\x08\0\xc0\x12\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xc8\x12\0\0\x37\ +\x02\0\0\xbc\x10\0\0\x15\xbc\x08\0\xe0\x12\0\0\x37\x02\0\0\x04\x11\0\0\x11\xc8\ +\x08\0\xe8\x12\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x08\x13\0\0\x37\x02\0\0\x1d\x11\ +\0\0\x01\xec\x08\0\x10\x13\0\0\x37\x02\0\0\x1f\x11\0\0\x18\xcc\x08\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x94\x01\0\0\0\0\x03\0\x08\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x75\x01\0\0\0\0\x03\0\xb8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcd\x01\0\0\0\0\x03\0\ +\xe8\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\xff\x01\0\0\0\0\x03\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\ +\x01\0\0\0\0\x03\0\x38\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x24\x01\0\0\0\0\x03\0\ +\xf0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2c\x01\0\0\0\0\x03\0\xe8\x02\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\xf7\x01\0\0\0\0\x03\0\x18\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x30\ +\x02\0\0\0\0\x03\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe6\x01\0\0\0\0\x03\0\xa0\ +\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc5\x01\0\0\0\0\x03\0\x08\x05\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\xbd\x01\0\0\0\0\x03\0\x78\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb5\x01\ +\0\0\0\0\x03\0\x30\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x01\0\0\0\0\x03\0\x48\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x84\x01\0\0\0\0\x03\0\x40\x08\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x8c\x01\0\0\0\0\x03\0\x28\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x28\x02\ +\0\0\0\0\x03\0\x98\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xde\x01\0\0\0\0\x03\0\x10\ +\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0c\x01\0\0\0\0\x03\0\x08\x08\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x4c\x01\0\0\0\0\x03\0\xe8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\x01\ +\0\0\0\0\x03\0\xb0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x03\0\xf0\x08\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x02\0\0\0\0\x03\0\x08\x09\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\xfc\0\0\0\0\0\x03\0\xf0\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x44\x01\0\0\0\ +\0\x03\0\xd0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf4\0\0\0\0\0\x03\0\xc0\x0a\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xad\x01\0\0\0\0\x03\0\xa8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\xd6\x01\0\0\0\0\x03\0\x78\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x02\0\0\0\0\ +\x03\0\xa0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x02\0\0\0\0\x03\0\x10\x0d\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xa5\x01\0\0\0\0\x03\0\x80\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x5c\x01\0\0\0\0\x03\0\x98\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1c\x01\0\0\0\0\ +\x03\0\xd0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcc\0\0\0\0\0\x03\0\xe0\x0b\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\xec\0\0\0\0\0\x03\0\xd8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x3c\x01\0\0\0\0\x03\0\xd8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x02\0\0\0\0\x03\ +\0\xd0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7c\x01\0\0\0\0\x03\0\x70\x0d\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\x34\x01\0\0\0\0\x03\0\x88\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xe4\0\0\0\0\0\x03\0\xc0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9d\x01\0\0\0\0\x03\0\ +\x20\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x54\x01\0\0\0\0\x03\0\x38\x0e\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\xdc\0\0\0\0\0\x03\0\x60\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\0\ +\0\0\0\0\x03\0\x98\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x07\x02\0\0\0\0\x03\0\xc0\ +\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xee\x01\0\0\0\0\x03\0\x78\x12\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x6c\x01\0\0\0\0\x03\0\x10\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x59\0\0\ +\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x20\x13\0\0\0\0\0\0\x3e\0\0\0\x11\0\x05\0\0\0\0\ +\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x28\0\0\0\ +\0\0\0\0\x86\0\0\0\x11\0\x05\0\x50\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x7d\0\0\0\ +\x11\0\x06\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x33\ +\0\0\0\x50\0\0\0\0\0\0\0\x01\0\0\0\x34\0\0\0\xc8\x12\0\0\0\0\0\0\x01\0\0\0\x35\ +\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x33\0\0\0\x2c\x05\0\0\0\0\0\0\x04\0\0\0\ +\x34\0\0\0\x38\x05\0\0\0\0\0\0\x04\0\0\0\x35\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\ +\0\x36\0\0\0\x2c\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\0\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x50\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\0\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x70\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x90\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\0\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xb0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\0\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xd0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\0\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xf0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x01\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x10\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x30\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x01\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x50\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x01\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x70\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x01\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x90\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x01\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\xb0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x01\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\xd0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x01\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x02\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x10\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x02\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x02\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x02\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\ +\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xa0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xc0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\xe0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x02\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x03\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x20\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x03\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x40\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x03\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x60\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x03\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x80\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x03\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xa0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x03\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xc0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x03\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\xe0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x03\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x04\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x20\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x04\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x40\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x04\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x04\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x04\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\ +\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xd0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xf0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x10\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x30\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x05\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x05\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x70\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x05\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x90\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x05\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xb0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x05\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xd0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x05\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xf0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x06\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x10\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x06\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x30\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x06\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x50\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x06\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x70\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x06\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x06\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x06\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\ +\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\ +\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x20\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x40\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x60\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x07\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x80\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x07\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xa0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x07\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xc0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x07\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xe0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x07\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x08\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x20\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x08\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x40\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x08\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x60\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x08\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x80\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x08\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x08\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x08\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x08\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x09\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\ +\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x50\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x70\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x90\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x09\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\xb0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x09\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xd0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x09\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xf0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0a\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x10\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0a\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x30\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0a\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x50\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0a\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x70\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0a\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x90\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0a\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\xb0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0a\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x0a\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0b\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0b\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0b\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\ +\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x80\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xa0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\xc0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\xe0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x0b\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0c\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x20\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0c\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x40\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0c\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x60\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0c\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x80\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0c\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x39\x3a\x3b\x3c\x3d\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\ +\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\ +\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\x2e\x65\x78\x74\0\x2e\x72\x65\x6c\x73\ +\x6f\x63\x6b\x65\x74\0\x2e\x6d\x61\x70\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\ +\x6d\x61\x70\x5f\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\ +\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\ +\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\x72\x73\x69\x67\0\x5f\x6c\x69\x63\ +\x65\x6e\x73\x65\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\ +\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x2e\x73\x74\x72\x74\ +\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\ +\x42\x42\x30\x5f\x39\0\x4c\x42\x42\x30\x5f\x39\x39\0\x4c\x42\x42\x30\x5f\x37\ +\x39\0\x4c\x42\x42\x30\x5f\x34\x39\0\x4c\x42\x42\x30\x5f\x39\x38\0\x4c\x42\x42\ +\x30\x5f\x38\x38\0\x4c\x42\x42\x30\x5f\x37\x38\0\x4c\x42\x42\x30\x5f\x36\x38\0\ +\x4c\x42\x42\x30\x5f\x35\x38\0\x4c\x42\x42\x30\x5f\x34\x38\0\x4c\x42\x42\x30\ +\x5f\x33\x38\0\x4c\x42\x42\x30\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x37\x37\0\x4c\ +\x42\x42\x30\x5f\x35\x37\0\x4c\x42\x42\x30\x5f\x31\x37\0\x4c\x42\x42\x30\x5f\ +\x38\x36\0\x4c\x42\x42\x30\x5f\x36\x36\0\x4c\x42\x42\x30\x5f\x35\x36\0\x4c\x42\ +\x42\x30\x5f\x34\x36\0\x4c\x42\x42\x30\x5f\x39\x35\0\x4c\x42\x42\x30\x5f\x37\ +\x35\0\x4c\x42\x42\x30\x5f\x34\x35\0\x4c\x42\x42\x30\x5f\x31\x30\x35\0\x4c\x42\ +\x42\x30\x5f\x34\0\x4c\x42\x42\x30\x5f\x38\x34\0\x4c\x42\x42\x30\x5f\x34\x34\0\ +\x4c\x42\x42\x30\x5f\x33\x34\0\x4c\x42\x42\x30\x5f\x31\x30\x34\0\x4c\x42\x42\ +\x30\x5f\x39\x33\0\x4c\x42\x42\x30\x5f\x37\x33\0\x4c\x42\x42\x30\x5f\x36\x33\0\ +\x4c\x42\x42\x30\x5f\x34\x33\0\x4c\x42\x42\x30\x5f\x33\x33\0\x4c\x42\x42\x30\ +\x5f\x32\x33\0\x4c\x42\x42\x30\x5f\x31\x30\x33\0\x4c\x42\x42\x30\x5f\x36\x32\0\ +\x4c\x42\x42\x30\x5f\x34\x32\0\x4c\x42\x42\x30\x5f\x32\x32\0\x4c\x42\x42\x30\ +\x5f\x31\x30\x32\0\x4c\x42\x42\x30\x5f\x35\x31\0\x4c\x42\x42\x30\x5f\x31\x31\0\ +\x4c\x42\x42\x30\x5f\x31\x30\x31\0\x4c\x42\x42\x30\x5f\x39\x30\0\x4c\x42\x42\ +\x30\x5f\x38\x30\0\x4c\x42\x42\x30\x5f\x35\x30\0\x4c\x42\x42\x30\x5f\x34\x30\0\ +\x4c\x42\x42\x30\x5f\x32\x30\0\x4c\x42\x42\x30\x5f\x31\x30\x30\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\x75\x49\0\0\0\0\0\0\x41\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\x31\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\ +\0\x20\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2d\0\ +\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x3c\0\0\0\0\0\0\x30\0\0\0\ +\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x38\0\0\0\x01\ +\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x60\x13\0\0\0\0\0\0\x78\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7e\0\0\0\x01\0\0\0\x03\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\xe0\x13\0\0\0\0\0\0\xe1\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xb4\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xc0\x3c\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x0c\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\ +\x10\0\0\0\0\0\0\0\x24\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\x2a\ +\0\0\0\0\0\0\xa0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\x20\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3d\0\0\0\0\0\0\ +\x70\x0c\0\0\0\0\0\0\x0c\0\0\0\x09\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\ +\x6f\0\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\x70\x49\0\0\0\0\0\ +\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\ +\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x37\0\0\0\0\0\0\x28\x05\0\0\0\ +\0\0\0\x01\0\0\0\x32\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0"; + + *sz = sizeof(data) - 1; + return (const void *)data; } #ifdef __cplusplus diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c index 9715d1170e..fa55e1fcb5 100644 --- a/tools/ebpf/rss.bpf.c +++ b/tools/ebpf/rss.bpf.c @@ -380,18 +380,19 @@ error: return err; } -static inline __u32 calculate_rss_hash(struct __sk_buff *skb, - struct rss_config_t *config, struct toeplitz_key_data_t *toe) +static inline bool calculate_rss_hash(struct __sk_buff *skb, + struct rss_config_t *config, + struct toeplitz_key_data_t *toe, + __u32 *result) { __u8 rss_input[HASH_CALCULATION_BUFFER_SIZE] = {}; size_t bytes_written = 0; - __u32 result = 0; int err = 0; struct packet_hash_info_t packet_info = {}; err = parse_packet(skb, &packet_info); if (err) { - return 0; + return false; } if (packet_info.is_ipv4) { @@ -524,11 +525,13 @@ static inline __u32 calculate_rss_hash(struct __sk_buff *skb, } } - if (bytes_written) { - net_toeplitz_add(&result, rss_input, bytes_written, toe); + if (!bytes_written) { + return false; } - return result; + net_toeplitz_add(result, rss_input, bytes_written, toe); + + return true; } SEC("socket") @@ -549,8 +552,7 @@ int tun_rss_steering_prog(struct __sk_buff *skb) return config->default_queue; } - hash = calculate_rss_hash(skb, config, toe); - if (hash) { + if (calculate_rss_hash(skb, config, toe, &hash)) { __u32 table_idx = hash % config->indirections_len; __u16 *queue = 0; From 8dc8220e23a6d16415b6a0a46785224e8bc7edd1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:00:59 +0900 Subject: [PATCH 1116/2884] ebpf: Return 0 when configuration fails The kernel interprets the returned value as an unsigned 32-bit so -1 will mean queue 4294967295, which is awkward. Return 0 instead. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- ebpf/rss.bpf.skeleton.h | 1546 +++++++++++++++++++-------------------- tools/ebpf/rss.bpf.c | 2 +- 2 files changed, 774 insertions(+), 774 deletions(-) diff --git a/ebpf/rss.bpf.skeleton.h b/ebpf/rss.bpf.skeleton.h index e41ed88901..647212e5dd 100644 --- a/ebpf/rss.bpf.skeleton.h +++ b/ebpf/rss.bpf.skeleton.h @@ -178,786 +178,786 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) { static const char data[] __attribute__((__aligned__(8))) = "\ \x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xb8\x4b\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ -\x01\0\xbf\x19\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\x54\xff\0\0\0\0\xbf\xa7\ -\0\0\0\0\0\0\x07\x07\0\0\x54\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x06\0\0\0\0\0\0\x18\x01\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\ -\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x4f\x02\0\0\0\0\xbf\x78\0\0\ -\0\0\0\0\x15\x08\x4d\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\ -\0\x46\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xc8\xff\0\0\0\0\x7b\x1a\xc0\xff\ -\0\0\0\0\x7b\x1a\xb8\xff\0\0\0\0\x7b\x1a\xb0\xff\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\ -\0\x63\x1a\xa0\xff\0\0\0\0\x7b\x1a\x98\xff\0\0\0\0\x7b\x1a\x90\xff\0\0\0\0\x7b\ -\x1a\x88\xff\0\0\0\0\x7b\x1a\x80\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x7b\x1a\ -\x70\xff\0\0\0\0\x7b\x1a\x68\xff\0\0\0\0\x7b\x1a\x60\xff\0\0\0\0\x7b\x1a\x58\ -\xff\0\0\0\0\x15\x09\x35\x02\0\0\0\0\x6b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ -\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\ -\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\ -\x77\0\0\0\x20\0\0\0\x55\0\x2a\x02\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xd0\ -\xff\0\0\0\0\xbf\x13\0\0\0\0\0\0\xdc\x03\0\0\x10\0\0\0\x15\x03\x02\0\0\x81\0\0\ -\x55\x03\x0b\0\xa8\x88\0\0\xb7\x02\0\0\x14\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\ -\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\ -\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x1a\x02\0\ -\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x15\x01\x18\x02\0\0\0\0\x15\x01\x21\0\x86\xdd\0\ -\0\x7b\x9a\x48\xff\0\0\0\0\x55\x01\xf6\0\x08\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\ -\x1a\x58\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\ -\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\ -\xff\xff\x79\xa1\x48\xff\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\ -\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\ -\x55\0\x05\x02\0\0\0\0\x69\xa1\xd6\xff\0\0\0\0\x57\x01\0\0\x3f\xff\0\0\xb7\x04\ -\0\0\x01\0\0\0\x55\x01\x01\0\0\0\0\0\xb7\x04\0\0\0\0\0\0\x61\xa1\xdc\xff\0\0\0\ -\0\x63\x1a\x64\xff\0\0\0\0\x61\xa1\xe0\xff\0\0\0\0\x63\x1a\x68\xff\0\0\0\0\x71\ -\xa9\xd9\xff\0\0\0\0\x71\xa2\xd0\xff\0\0\0\0\x67\x02\0\0\x02\0\0\0\x57\x02\0\0\ -\x3c\0\0\0\x73\x4a\x5e\xff\0\0\0\0\x05\0\xbc\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ -\x73\x1a\x59\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xf0\xff\0\0\0\0\x7b\x1a\ -\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\ -\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\ -\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\ -\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xe4\x01\0\0\0\0\xb7\ -\x03\0\0\x28\0\0\0\x7b\x9a\x48\xff\0\0\0\0\x79\xa1\xe0\xff\0\0\0\0\x63\x1a\x6c\ -\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\xd8\xff\0\0\ -\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x68\xff\0\0\0\0\x79\ -\xa1\xe8\xff\0\0\0\0\x63\x1a\x74\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x78\ -\xff\0\0\0\0\x79\xa1\xf0\xff\0\0\0\0\x63\x1a\x7c\xff\0\0\0\0\x77\x01\0\0\x20\0\ -\0\0\x63\x1a\x80\xff\0\0\0\0\x71\xa9\xd6\xff\0\0\0\0\x25\x09\x93\0\x3c\0\0\0\ -\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\ -\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x8c\0\0\0\0\0\xb7\x01\0\ -\0\0\0\0\0\x6b\x1a\xfe\xff\0\0\0\0\xb7\x02\0\0\x28\0\0\0\xbf\xa1\0\0\0\0\0\0\ -\x07\x01\0\0\x94\xff\xff\xff\x7b\x1a\x20\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\ -\x01\0\0\x84\xff\xff\xff\x7b\x1a\x18\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\ -\x38\xff\0\0\0\0\x7b\x7a\x30\xff\0\0\0\0\x7b\x8a\x28\xff\0\0\0\0\xbf\xa3\0\0\0\ -\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\x7b\x2a\x40\xff\0\0\ -\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\ -\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xb2\x01\0\0\0\0\xbf\x91\0\0\0\0\0\0\x15\ -\x01\x22\0\x3c\0\0\0\x15\x01\x58\0\x2c\0\0\0\x79\xa2\x40\xff\0\0\0\0\x55\x01\ -\x59\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf8\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ -\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa9\x48\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\xb7\ -\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\ -\0\x77\0\0\0\x20\0\0\0\x55\0\xa1\x01\0\0\0\0\x71\xa1\xfa\xff\0\0\0\0\x55\x01\ -\x4a\0\x02\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x55\x01\x48\0\x02\0\0\0\x71\xa1\xfb\ -\xff\0\0\0\0\x55\x01\x46\0\x01\0\0\0\x79\xa2\x40\xff\0\0\0\0\x07\x02\0\0\x08\0\ -\0\0\xbf\x91\0\0\0\0\0\0\x79\xa3\x20\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\ -\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\ -\0\x91\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5d\xff\0\0\0\0\x05\0\x39\0\0\ -\0\0\0\xb7\x08\0\0\x02\0\0\0\xb7\x07\0\0\0\0\0\0\x6b\x7a\xf8\xff\0\0\0\0\x05\0\ -\x12\0\0\0\0\0\x0f\x81\0\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\ -\x71\xa3\xff\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x3d\x32\x09\0\0\0\0\0\xbf\x72\0\ -\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\x67\x07\0\0\x20\0\0\0\xbf\x73\0\0\0\0\0\0\x77\ -\x03\0\0\x20\0\0\0\xbf\x27\0\0\0\0\0\0\xbf\x18\0\0\0\0\0\0\xb7\x01\0\0\x1d\0\0\ -\0\x2d\x31\x03\0\0\0\0\0\x79\xa7\x30\xff\0\0\0\0\x79\xa8\x28\xff\0\0\0\0\x05\0\ -\x23\0\0\0\0\0\xbf\x89\0\0\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\x19\0\0\0\0\0\0\ -\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\xbf\ -\x92\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ -\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x6b\x01\0\0\0\0\x71\xa2\xf8\ -\xff\0\0\0\0\x55\x02\x0d\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\0\x79\xa1\x48\xff\0\0\ -\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x18\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\ -\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\ -\0\x5f\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5c\xff\0\0\0\0\x05\0\xe1\xff\ -\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x15\x02\xd0\xff\0\0\0\0\x71\xa1\xf9\xff\0\0\0\0\ -\x07\x01\0\0\x02\0\0\0\x05\0\xcd\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5e\ -\xff\0\0\0\0\x79\xa2\x40\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\x67\x01\0\0\x03\0\ -\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\x08\0\0\0\x71\xa9\xfe\xff\0\0\0\0\x25\x09\ -\x0e\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x03\0\0\x01\0\0\ -\0\0\0\0\0\0\x18\0\x1c\x5f\x31\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x07\0\0\ -\0\0\0\x79\xa1\x38\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x1a\x38\xff\0\0\0\0\ -\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\x02\0\x0b\0\0\0\x05\0\x84\ -\xff\0\0\0\0\x15\x09\xf8\xff\x87\0\0\0\xbf\x23\0\0\0\0\0\0\x05\0\x01\0\0\0\0\0\ -\x15\x09\x73\xff\x87\0\0\0\x71\xa4\x5e\xff\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\x91\ -\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\0\x15\x01\x18\0\0\0\0\0\x57\x04\0\0\xff\0\0\0\ -\x55\x04\x16\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\xb4\0\x11\0\0\0\x55\x09\ -\x13\0\x06\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5b\xff\0\0\0\0\xb7\x01\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xb0\x4b\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ +\x01\0\x7b\x1a\x48\xff\0\0\0\0\xb7\x09\0\0\0\0\0\0\x63\x9a\x54\xff\0\0\0\0\xbf\ +\xa7\0\0\0\0\0\0\x07\x07\0\0\x54\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x06\0\0\0\0\0\0\x18\x01\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\ +\0\x15\x06\x4e\x02\0\0\0\0\xbf\x78\0\0\0\0\0\0\x15\x08\x4c\x02\0\0\0\0\x71\x61\ +\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x45\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\ +\x63\x1a\xc8\xff\0\0\0\0\x7b\x1a\xc0\xff\0\0\0\0\x7b\x1a\xb8\xff\0\0\0\0\x7b\ +\x1a\xb0\xff\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\0\x63\x1a\xa0\xff\0\0\0\0\x7b\x1a\ +\x98\xff\0\0\0\0\x7b\x1a\x90\xff\0\0\0\0\x7b\x1a\x88\xff\0\0\0\0\x7b\x1a\x80\ +\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x7b\x1a\x70\xff\0\0\0\0\x7b\x1a\x68\xff\0\ +\0\0\0\x7b\x1a\x60\xff\0\0\0\0\x7b\x1a\x58\xff\0\0\0\0\x79\xa9\x48\xff\0\0\0\0\ +\x15\x09\x33\x02\0\0\0\0\x6b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\ +\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\x04\0\0\x02\0\ +\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\ +\0\0\0\x55\0\x28\x02\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xd0\xff\0\0\0\0\xbf\ +\x13\0\0\0\0\0\0\xdc\x03\0\0\x10\0\0\0\x15\x03\x02\0\0\x81\0\0\x55\x03\x0b\0\ +\xa8\x88\0\0\xb7\x02\0\0\x14\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\ +\xff\xbf\x91\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\ +\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x18\x02\0\0\0\0\x69\ +\xa1\xd0\xff\0\0\0\0\x15\x01\x16\x02\0\0\0\0\x15\x01\x20\0\x86\xdd\0\0\x55\x01\ +\xf5\0\x08\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x58\xff\0\0\0\0\xb7\x01\0\0\0\0\ \0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\ \xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\xb7\ -\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\ -\0\x77\0\0\0\x20\0\0\0\x55\0\x23\x01\0\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x6b\x1a\ -\x60\xff\0\0\0\0\x69\xa1\xd2\xff\0\0\0\0\x6b\x1a\x62\xff\0\0\0\0\x71\xa1\x58\ -\xff\0\0\0\0\x15\x01\x18\0\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\ -\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\x03\0\0\ -\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\0\0\x4f\ -\x21\0\0\0\0\0\0\x71\xa2\x5b\xff\0\0\0\0\x15\x02\x49\0\0\0\0\0\xbf\x12\0\0\0\0\ -\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x46\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\x63\ -\x1a\xa8\xff\0\0\0\0\x61\xa1\x68\xff\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x69\xa1\ -\x60\xff\0\0\0\0\x6b\x1a\xb0\xff\0\0\0\0\x69\xa1\x62\xff\0\0\0\0\x6b\x1a\xb2\ -\xff\0\0\0\0\x05\0\x9c\0\0\0\0\0\x71\xa1\x59\xff\0\0\0\0\x15\x01\x03\x01\0\0\0\ -\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\ -\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\ -\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x5b\xff\ -\0\0\0\0\x15\x02\x3c\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\x15\ -\x02\x39\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\x5c\ -\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\ -\x03\0\0\x84\xff\xff\xff\x57\x01\0\0\x80\0\0\0\x15\x01\x01\0\0\0\0\0\xbf\x32\0\ -\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x74\xff\xff\xff\x71\xa5\x5d\xff\0\0\ -\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\ -\x94\xff\xff\xff\x15\x01\x01\0\0\0\0\0\xbf\x43\0\0\0\0\0\0\x61\x21\x04\0\0\0\0\ -\0\x67\x01\0\0\x20\0\0\0\x61\x24\0\0\0\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\x1a\xa8\ -\xff\0\0\0\0\x61\x21\x08\0\0\0\0\0\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\ -\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb0\xff\0\0\0\0\x61\x31\0\0\0\0\0\0\x61\x32\x04\0\ -\0\0\0\0\x61\x34\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x69\xa5\x62\xff\0\0\0\0\ -\x6b\x5a\xca\xff\0\0\0\0\x69\xa5\x60\xff\0\0\0\0\x6b\x5a\xc8\xff\0\0\0\0\x67\ -\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xc0\xff\0\0\0\0\x67\x02\0\0\x20\ -\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb8\xff\0\0\0\0\x05\0\x5f\0\0\0\0\0\x71\xa2\ -\x5a\xff\0\0\0\0\x15\x02\x04\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x04\0\0\ -\0\x15\x02\x01\0\0\0\0\0\x05\0\xb4\xff\0\0\0\0\x57\x01\0\0\x01\0\0\0\x15\x01\ -\xc0\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\x63\x1a\xa8\xff\0\0\0\0\x61\xa1\x68\xff\ -\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x05\0\x52\0\0\0\0\0\x71\xa2\x5a\xff\0\0\0\0\ -\x15\x02\x16\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\x13\0\ -\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\x5c\xff\0\0\0\ -\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\ -\x84\xff\xff\xff\x57\x01\0\0\0\x01\0\0\x15\x01\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\ -\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x74\xff\xff\xff\x71\xa5\x5d\xff\0\0\0\0\xbf\ -\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x94\xff\ -\xff\xff\x15\x01\xc3\xff\0\0\0\0\x05\0\xc1\xff\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\ -\x02\0\0\x08\0\0\0\x15\x02\xa0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\ -\xff\xff\xff\x71\xa4\x5c\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\ -\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x84\xff\xff\xff\x57\x01\0\0\x40\0\0\0\x15\x01\ -\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\x61\x23\x04\0\0\0\0\0\x67\x03\0\0\x20\0\0\0\ -\x61\x24\0\0\0\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xa8\xff\0\0\0\0\x61\x23\x08\0\ -\0\0\0\0\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x32\0\0\0\0\0\0\x7b\ -\x2a\xb0\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xb8\xff\xff\xff\x15\x01\ -\x18\0\0\0\0\0\x71\xa1\x5d\xff\0\0\0\0\x15\x01\x16\0\0\0\0\0\x61\xa1\xa0\xff\0\ -\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x9c\xff\0\0\0\0\x63\x12\x08\0\0\0\0\0\x61\ -\xa1\x98\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x94\xff\0\0\0\0\x05\0\x15\0\ -\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5a\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\ -\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\x79\xa1\ -\x48\xff\0\0\0\0\xb7\x04\0\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ -\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x72\0\0\0\0\0\x05\0\x4e\xff\0\ -\0\0\0\x61\xa1\x80\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x7c\xff\0\0\0\0\ -\x63\x12\x08\0\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\ -\x74\xff\0\0\0\0\x63\x12\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x07\x07\0\0\x04\0\0\0\ -\x61\x83\0\0\0\0\0\0\xb7\x05\0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xa8\ -\xff\xff\xff\x0f\x12\0\0\0\0\0\0\x71\x24\0\0\0\0\0\0\xbf\x42\0\0\0\0\0\0\x67\ -\x02\0\0\x38\0\0\0\xc7\x02\0\0\x3f\0\0\0\x5f\x32\0\0\0\0\0\0\xaf\x52\0\0\0\0\0\ -\0\xbf\x75\0\0\0\0\0\0\x0f\x15\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\x01\ -\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\ -\0\0\0\0\x67\0\0\0\x39\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\ -\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\ -\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\0\0\xc7\ -\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\ -\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\ -\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\ -\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\ -\x04\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\ -\0\x3c\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\ -\x50\0\0\0\0\0\0\x77\0\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\ -\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\0\x3f\0\0\ -\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x02\0\ -\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\ -\0\0\0\0\x67\0\0\0\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\ -\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\ -\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\0\0\0\0\0\ -\x5f\x34\0\0\0\0\0\0\xaf\x42\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\x03\0\0\x01\ -\0\0\0\x4f\x53\0\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\xbf\x25\0\0\0\0\0\0\x15\x01\ -\x01\0\x24\0\0\0\x05\0\xa9\xff\0\0\0\0\x71\x61\x06\0\0\0\0\0\x71\x63\x07\0\0\0\ -\0\0\x67\x03\0\0\x08\0\0\0\x4f\x13\0\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x77\x02\0\ -\0\x20\0\0\0\x9f\x32\0\0\0\0\0\0\x63\x2a\x58\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\ -\x07\x02\0\0\x58\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\ -\0\0\0\x55\0\x05\0\0\0\0\0\x71\x61\x08\0\0\0\0\0\x71\x60\x09\0\0\0\0\0\x67\0\0\ -\0\x08\0\0\0\x4f\x10\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x69\0\0\0\0\0\0\0\x05\0\xfd\ -\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x02\0\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ +\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x04\x02\0\0\0\0\x69\xa1\xd6\ +\xff\0\0\0\0\x57\x01\0\0\x3f\xff\0\0\xb7\x04\0\0\x01\0\0\0\x55\x01\x01\0\0\0\0\ +\0\xb7\x04\0\0\0\0\0\0\x61\xa1\xdc\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x61\xa1\ +\xe0\xff\0\0\0\0\x63\x1a\x68\xff\0\0\0\0\x71\xa9\xd9\xff\0\0\0\0\x71\xa2\xd0\ +\xff\0\0\0\0\x67\x02\0\0\x02\0\0\0\x57\x02\0\0\x3c\0\0\0\x73\x4a\x5e\xff\0\0\0\ +\0\x05\0\xbb\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x59\xff\0\0\0\0\xb7\x01\0\ +\0\0\0\0\0\x7b\x1a\xf0\xff\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\ +\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\ +\x03\0\0\xd0\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\ +\x28\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\ +\0\0\x20\0\0\0\x55\0\xe3\x01\0\0\0\0\xb7\x03\0\0\x28\0\0\0\x79\xa1\xe0\xff\0\0\ +\0\0\x63\x1a\x6c\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\ +\xa1\xd8\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x68\ +\xff\0\0\0\0\x79\xa1\xe8\xff\0\0\0\0\x63\x1a\x74\xff\0\0\0\0\x77\x01\0\0\x20\0\ +\0\0\x63\x1a\x78\xff\0\0\0\0\x79\xa1\xf0\xff\0\0\0\0\x63\x1a\x7c\xff\0\0\0\0\ +\x77\x01\0\0\x20\0\0\0\x63\x1a\x80\xff\0\0\0\0\x71\xa9\xd6\xff\0\0\0\0\x25\x09\ +\x93\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\ +\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x8c\0\0\ +\0\0\0\xb7\x01\0\0\0\0\0\0\x6b\x1a\xfe\xff\0\0\0\0\xb7\x02\0\0\x28\0\0\0\xbf\ +\xa1\0\0\0\0\0\0\x07\x01\0\0\x94\xff\xff\xff\x7b\x1a\x20\xff\0\0\0\0\xbf\xa1\0\ +\0\0\0\0\0\x07\x01\0\0\x84\xff\xff\xff\x7b\x1a\x18\xff\0\0\0\0\xb7\x01\0\0\0\0\ +\0\0\x7b\x1a\x38\xff\0\0\0\0\x7b\x7a\x30\xff\0\0\0\0\x7b\x8a\x28\xff\0\0\0\0\ +\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\x7b\ +\x2a\x40\xff\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\ +\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xb2\x01\0\0\0\0\xbf\x91\0\ +\0\0\0\0\0\x15\x01\x22\0\x3c\0\0\0\x15\x01\x58\0\x2c\0\0\0\x79\xa2\x40\xff\0\0\ +\0\0\x55\x01\x59\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf8\xff\0\0\0\0\xbf\ +\xa3\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa9\x48\xff\0\0\0\0\xbf\x91\0\ +\0\0\0\0\0\xb7\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\ +\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xa1\x01\0\0\0\0\x71\xa1\xfa\xff\0\0\ +\0\0\x55\x01\x4a\0\x02\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x55\x01\x48\0\x02\0\0\0\ +\x71\xa1\xfb\xff\0\0\0\0\x55\x01\x46\0\x01\0\0\0\x79\xa2\x40\xff\0\0\0\0\x07\ +\x02\0\0\x08\0\0\0\xbf\x91\0\0\0\0\0\0\x79\xa3\x20\xff\0\0\0\0\xb7\x04\0\0\x10\ +\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\ +\x20\0\0\0\x55\0\x91\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5d\xff\0\0\0\0\ +\x05\0\x39\0\0\0\0\0\xb7\x08\0\0\x02\0\0\0\xb7\x07\0\0\0\0\0\0\x6b\x7a\xf8\xff\ +\0\0\0\0\x05\0\x12\0\0\0\0\0\x0f\x81\0\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x07\x02\0\ +\0\x01\0\0\0\x71\xa3\xff\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x3d\x32\x09\0\0\0\0\ +\0\xbf\x72\0\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\x67\x07\0\0\x20\0\0\0\xbf\x73\0\0\ +\0\0\0\0\x77\x03\0\0\x20\0\0\0\xbf\x27\0\0\0\0\0\0\xbf\x18\0\0\0\0\0\0\xb7\x01\ +\0\0\x1d\0\0\0\x2d\x31\x03\0\0\0\0\0\x79\xa7\x30\xff\0\0\0\0\x79\xa8\x28\xff\0\ +\0\0\0\x05\0\x23\0\0\0\0\0\xbf\x89\0\0\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\x19\ +\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa1\x48\xff\0\ +\0\0\0\xbf\x92\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\ +\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x6b\x01\0\0\0\0\x71\ +\xa2\xf8\xff\0\0\0\0\x55\x02\x0d\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\0\x79\xa1\x48\ +\xff\0\0\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x18\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\ +\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\ +\0\0\x55\0\x5f\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5c\xff\0\0\0\0\x05\0\ +\xe1\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x15\x02\xd0\xff\0\0\0\0\x71\xa1\xf9\xff\ +\0\0\0\0\x07\x01\0\0\x02\0\0\0\x05\0\xcd\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\ +\x1a\x5e\xff\0\0\0\0\x79\xa2\x40\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\x67\x01\0\ +\0\x03\0\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\x08\0\0\0\x71\xa9\xfe\xff\0\0\0\0\ +\x25\x09\x0e\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x03\0\0\ +\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x31\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\ +\x07\0\0\0\0\0\x79\xa1\x38\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x1a\x38\xff\0\ +\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\x02\0\x0b\0\0\0\x05\ +\0\x84\xff\0\0\0\0\x15\x09\xf8\xff\x87\0\0\0\xbf\x23\0\0\0\0\0\0\x05\0\x01\0\0\ +\0\0\0\x15\x09\x73\xff\x87\0\0\0\x71\xa4\x5e\xff\0\0\0\0\xbf\x32\0\0\0\0\0\0\ +\xbf\x91\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\0\x15\x01\x18\0\0\0\0\0\x57\x04\0\0\ +\xff\0\0\0\x55\x04\x16\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\xb4\0\x11\0\0\0\ +\x55\x09\x13\0\x06\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5b\xff\0\0\0\0\xb7\x01\ +\0\0\0\0\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\ +\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\ +\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\ +\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x23\x01\0\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x6b\ +\x1a\x60\xff\0\0\0\0\x69\xa1\xd2\xff\0\0\0\0\x6b\x1a\x62\xff\0\0\0\0\x71\xa1\ +\x58\xff\0\0\0\0\x15\x01\x18\0\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\ +\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\x03\0\ +\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\0\0\ +\x4f\x21\0\0\0\0\0\0\x71\xa2\x5b\xff\0\0\0\0\x15\x02\x49\0\0\0\0\0\xbf\x12\0\0\ +\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x46\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\ +\x63\x1a\xa8\xff\0\0\0\0\x61\xa1\x68\xff\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x69\ +\xa1\x60\xff\0\0\0\0\x6b\x1a\xb0\xff\0\0\0\0\x69\xa1\x62\xff\0\0\0\0\x6b\x1a\ +\xb2\xff\0\0\0\0\x05\0\x9c\0\0\0\0\0\x71\xa1\x59\xff\0\0\0\0\x15\x01\x03\x01\0\ +\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\ +\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\ +\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x5b\ +\xff\0\0\0\0\x15\x02\x3c\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\ +\x15\x02\x39\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\ +\x5c\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\ +\x07\x03\0\0\x84\xff\xff\xff\x57\x01\0\0\x80\0\0\0\x15\x01\x01\0\0\0\0\0\xbf\ +\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x74\xff\xff\xff\x71\xa5\x5d\ +\xff\0\0\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\ +\x04\0\0\x94\xff\xff\xff\x15\x01\x01\0\0\0\0\0\xbf\x43\0\0\0\0\0\0\x61\x21\x04\ +\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\x24\0\0\0\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\ +\x1a\xa8\xff\0\0\0\0\x61\x21\x08\0\0\0\0\0\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\ +\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb0\xff\0\0\0\0\x61\x31\0\0\0\0\0\0\x61\ +\x32\x04\0\0\0\0\0\x61\x34\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x69\xa5\x62\xff\ +\0\0\0\0\x6b\x5a\xca\xff\0\0\0\0\x69\xa5\x60\xff\0\0\0\0\x6b\x5a\xc8\xff\0\0\0\ +\0\x67\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xc0\xff\0\0\0\0\x67\x02\0\ +\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb8\xff\0\0\0\0\x05\0\x5f\0\0\0\0\0\ +\x71\xa2\x5a\xff\0\0\0\0\x15\x02\x04\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\ +\x04\0\0\0\x15\x02\x01\0\0\0\0\0\x05\0\xb4\xff\0\0\0\0\x57\x01\0\0\x01\0\0\0\ +\x15\x01\xc0\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\x63\x1a\xa8\xff\0\0\0\0\x61\xa1\ +\x68\xff\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x05\0\x52\0\0\0\0\0\x71\xa2\x5a\xff\0\ +\0\0\0\x15\x02\x16\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\ +\x13\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\x5c\xff\ +\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\ +\0\0\x84\xff\xff\xff\x57\x01\0\0\0\x01\0\0\x15\x01\x01\0\0\0\0\0\xbf\x32\0\0\0\ +\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x74\xff\xff\xff\x71\xa5\x5d\xff\0\0\0\0\ +\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x94\ +\xff\xff\xff\x15\x01\xc3\xff\0\0\0\0\x05\0\xc1\xff\0\0\0\0\xbf\x12\0\0\0\0\0\0\ +\x57\x02\0\0\x08\0\0\0\x15\x02\xa0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\ +\x64\xff\xff\xff\x71\xa4\x5c\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\ +\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x84\xff\xff\xff\x57\x01\0\0\x40\0\0\0\x15\ +\x01\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\x61\x23\x04\0\0\0\0\0\x67\x03\0\0\x20\0\ +\0\0\x61\x24\0\0\0\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xa8\xff\0\0\0\0\x61\x23\ +\x08\0\0\0\0\0\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x32\0\0\0\0\0\0\ +\x7b\x2a\xb0\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xb8\xff\xff\xff\x15\ +\x01\x18\0\0\0\0\0\x71\xa1\x5d\xff\0\0\0\0\x15\x01\x16\0\0\0\0\0\x61\xa1\xa0\ +\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x9c\xff\0\0\0\0\x63\x12\x08\0\0\0\0\ +\0\x61\xa1\x98\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x94\xff\0\0\0\0\x05\0\ +\x15\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5a\xff\0\0\0\0\xb7\x01\0\0\0\0\0\ +\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\x79\ +\xa1\x48\xff\0\0\0\0\xb7\x04\0\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\ +\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x72\0\0\0\0\0\x05\0\x4e\ +\xff\0\0\0\0\x61\xa1\x80\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x7c\xff\0\0\ +\0\0\x63\x12\x08\0\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\ +\xa1\x74\xff\0\0\0\0\x63\x12\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x07\x07\0\0\x04\0\ +\0\0\x61\x83\0\0\0\0\0\0\xb7\x05\0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\ +\xa8\xff\xff\xff\x0f\x12\0\0\0\0\0\0\x71\x24\0\0\0\0\0\0\xbf\x42\0\0\0\0\0\0\ +\x67\x02\0\0\x38\0\0\0\xc7\x02\0\0\x3f\0\0\0\x5f\x32\0\0\0\0\0\0\xaf\x52\0\0\0\ +\0\0\0\xbf\x75\0\0\0\0\0\0\x0f\x15\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\ +\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\ +\0\0\0\0\0\0\x67\0\0\0\x39\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\ +\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\ +\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\ +\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\ +\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\ +\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\ +\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\ +\x77\0\0\0\x04\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\ +\0\x67\0\0\0\x3c\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\ +\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\ +\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\ +\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\ +\0\0\x02\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\ +\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\ +\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\ +\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\ +\0\0\0\0\0\x5f\x34\0\0\0\0\0\0\xaf\x42\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\ +\x03\0\0\x01\0\0\0\x4f\x53\0\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\xbf\x25\0\0\0\0\0\ +\0\x15\x01\x01\0\x24\0\0\0\x05\0\xa9\xff\0\0\0\0\x71\x61\x06\0\0\0\0\0\x71\x63\ +\x07\0\0\0\0\0\x67\x03\0\0\x08\0\0\0\x4f\x13\0\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\ +\x77\x02\0\0\x20\0\0\0\x9f\x32\0\0\0\0\0\0\x63\x2a\x58\xff\0\0\0\0\xbf\xa2\0\0\ +\0\0\0\0\x07\x02\0\0\x58\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\ +\0\0\x01\0\0\0\x55\0\x07\0\0\0\0\0\x71\x61\x08\0\0\0\0\0\x71\x69\x09\0\0\0\0\0\ +\x67\x09\0\0\x08\0\0\0\x4f\x19\0\0\0\0\0\0\x57\x09\0\0\xff\xff\0\0\xbf\x90\0\0\ +\0\0\0\0\x95\0\0\0\0\0\0\0\x69\x09\0\0\0\0\0\0\x05\0\xfb\xff\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x47\x50\x4c\x20\x76\x32\0\0\x9f\xeb\x01\0\x18\0\0\0\0\0\0\0\ -\x58\x05\0\0\x58\x05\0\0\x71\x11\0\0\0\0\0\0\0\0\0\x02\x03\0\0\0\x01\0\0\0\0\0\ -\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x02\ -\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x06\0\0\0\0\0\ -\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\ -\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\x02\ -\x0a\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x01\0\0\0\0\0\0\0\0\0\ -\0\x02\x0c\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\ -\0\x05\0\0\x04\x28\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\ -\0\0\x27\0\0\0\x07\0\0\0\x80\0\0\0\x32\0\0\0\x09\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\ -\0\0\0\0\x01\0\0\x48\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\ -\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x28\0\0\0\0\0\0\0\x05\0\0\ -\x04\x28\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\ -\0\0\x0f\0\0\0\x80\0\0\0\x32\0\0\0\x09\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\ -\x01\0\0\x63\0\0\0\0\0\0\x0e\x11\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x14\0\0\0\0\ -\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x05\0\0\x04\x28\ -\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\0\0\x01\ -\0\0\0\x80\0\0\0\x32\0\0\0\x13\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\x01\0\0\ -\x7c\0\0\0\0\0\0\x0e\x15\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x18\0\0\0\x9a\0\0\0\ -\x22\0\0\x04\xc0\0\0\0\xa4\0\0\0\x19\0\0\0\0\0\0\0\xa8\0\0\0\x19\0\0\0\x20\0\0\ -\0\xb1\0\0\0\x19\0\0\0\x40\0\0\0\xb6\0\0\0\x19\0\0\0\x60\0\0\0\xc4\0\0\0\x19\0\ -\0\0\x80\0\0\0\xcd\0\0\0\x19\0\0\0\xa0\0\0\0\xda\0\0\0\x19\0\0\0\xc0\0\0\0\xe3\ -\0\0\0\x19\0\0\0\xe0\0\0\0\xee\0\0\0\x19\0\0\0\0\x01\0\0\xf7\0\0\0\x19\0\0\0\ -\x20\x01\0\0\x07\x01\0\0\x19\0\0\0\x40\x01\0\0\x0f\x01\0\0\x19\0\0\0\x60\x01\0\ -\0\x18\x01\0\0\x1b\0\0\0\x80\x01\0\0\x1b\x01\0\0\x19\0\0\0\x20\x02\0\0\x20\x01\ -\0\0\x19\0\0\0\x40\x02\0\0\x2b\x01\0\0\x19\0\0\0\x60\x02\0\0\x30\x01\0\0\x19\0\ -\0\0\x80\x02\0\0\x39\x01\0\0\x19\0\0\0\xa0\x02\0\0\x41\x01\0\0\x19\0\0\0\xc0\ -\x02\0\0\x48\x01\0\0\x19\0\0\0\xe0\x02\0\0\x53\x01\0\0\x19\0\0\0\0\x03\0\0\x5d\ -\x01\0\0\x1c\0\0\0\x20\x03\0\0\x68\x01\0\0\x1c\0\0\0\xa0\x03\0\0\x72\x01\0\0\ -\x19\0\0\0\x20\x04\0\0\x7e\x01\0\0\x19\0\0\0\x40\x04\0\0\x89\x01\0\0\x19\0\0\0\ -\x60\x04\0\0\0\0\0\0\x1d\0\0\0\x80\x04\0\0\x93\x01\0\0\x1f\0\0\0\xc0\x04\0\0\ -\x9a\x01\0\0\x19\0\0\0\0\x05\0\0\xa3\x01\0\0\x19\0\0\0\x20\x05\0\0\0\0\0\0\x21\ -\0\0\0\x40\x05\0\0\xac\x01\0\0\x19\0\0\0\x80\x05\0\0\xb5\x01\0\0\x23\0\0\0\xa0\ -\x05\0\0\xc1\x01\0\0\x1f\0\0\0\xc0\x05\0\0\xca\x01\0\0\0\0\0\x08\x1a\0\0\0\xd0\ -\x01\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x19\0\0\0\x04\ -\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x19\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\ -\0\x01\0\0\x05\x08\0\0\0\xdd\x01\0\0\x1e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2c\0\ -\0\0\xe7\x01\0\0\0\0\0\x08\x20\0\0\0\xed\x01\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\ -\0\0\0\0\x01\0\0\x05\x08\0\0\0\0\x02\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\ -\x2d\0\0\0\x03\x02\0\0\0\0\0\x08\x24\0\0\0\x08\x02\0\0\0\0\0\x01\x01\0\0\0\x08\ -\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x16\x02\0\0\x17\0\0\0\x1a\x02\0\0\x01\0\0\ -\x0c\x25\0\0\0\x3e\x11\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\ -\0\0\0\x27\0\0\0\x04\0\0\0\x07\0\0\0\x43\x11\0\0\0\0\0\x0e\x28\0\0\0\x01\0\0\0\ -\x4c\x11\0\0\x03\0\0\x0f\0\0\0\0\x0e\0\0\0\0\0\0\0\x28\0\0\0\x12\0\0\0\0\0\0\0\ -\x28\0\0\0\x16\0\0\0\0\0\0\0\x28\0\0\0\x52\x11\0\0\x01\0\0\x0f\0\0\0\0\x29\0\0\ -\0\0\0\0\0\x07\0\0\0\x5a\x11\0\0\0\0\0\x07\0\0\0\0\x68\x11\0\0\0\0\0\x07\0\0\0\ -\0\0\x69\x6e\x74\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\ -\x50\x45\x5f\x5f\0\x74\x79\x70\x65\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\x76\x61\ -\x6c\x75\x65\x5f\x73\x69\x7a\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\ -\0\x6d\x61\x70\x5f\x66\x6c\x61\x67\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\ -\x61\x70\x5f\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x61\ -\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\ -\x6b\x65\x79\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\ -\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x5f\x5f\x73\x6b\x5f\x62\ -\x75\x66\x66\0\x6c\x65\x6e\0\x70\x6b\x74\x5f\x74\x79\x70\x65\0\x6d\x61\x72\x6b\ -\0\x71\x75\x65\x75\x65\x5f\x6d\x61\x70\x70\x69\x6e\x67\0\x70\x72\x6f\x74\x6f\ -\x63\x6f\x6c\0\x76\x6c\x61\x6e\x5f\x70\x72\x65\x73\x65\x6e\x74\0\x76\x6c\x61\ -\x6e\x5f\x74\x63\x69\0\x76\x6c\x61\x6e\x5f\x70\x72\x6f\x74\x6f\0\x70\x72\x69\ -\x6f\x72\x69\x74\x79\0\x69\x6e\x67\x72\x65\x73\x73\x5f\x69\x66\x69\x6e\x64\x65\ -\x78\0\x69\x66\x69\x6e\x64\x65\x78\0\x74\x63\x5f\x69\x6e\x64\x65\x78\0\x63\x62\ -\0\x68\x61\x73\x68\0\x74\x63\x5f\x63\x6c\x61\x73\x73\x69\x64\0\x64\x61\x74\x61\ -\0\x64\x61\x74\x61\x5f\x65\x6e\x64\0\x6e\x61\x70\x69\x5f\x69\x64\0\x66\x61\x6d\ -\x69\x6c\x79\0\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x34\0\x6c\x6f\x63\x61\x6c\ -\x5f\x69\x70\x34\0\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x36\0\x6c\x6f\x63\x61\ -\x6c\x5f\x69\x70\x36\0\x72\x65\x6d\x6f\x74\x65\x5f\x70\x6f\x72\x74\0\x6c\x6f\ -\x63\x61\x6c\x5f\x70\x6f\x72\x74\0\x64\x61\x74\x61\x5f\x6d\x65\x74\x61\0\x74\ -\x73\x74\x61\x6d\x70\0\x77\x69\x72\x65\x5f\x6c\x65\x6e\0\x67\x73\x6f\x5f\x73\ -\x65\x67\x73\0\x67\x73\x6f\x5f\x73\x69\x7a\x65\0\x74\x73\x74\x61\x6d\x70\x5f\ -\x74\x79\x70\x65\0\x68\x77\x74\x73\x74\x61\x6d\x70\0\x5f\x5f\x75\x33\x32\0\x75\ -\x6e\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x66\x6c\x6f\x77\x5f\x6b\x65\x79\ -\x73\0\x5f\x5f\x75\x36\x34\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x6c\x6f\x6e\ -\x67\x20\x6c\x6f\x6e\x67\0\x73\x6b\0\x5f\x5f\x75\x38\0\x75\x6e\x73\x69\x67\x6e\ -\x65\x64\x20\x63\x68\x61\x72\0\x73\x6b\x62\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\ -\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x73\x6f\x63\x6b\x65\x74\ -\0\x2f\x68\x6f\x6d\x65\x2f\x6d\x65\x2f\x71\x2f\x76\x61\x72\x2f\x71\x65\x6d\x75\ -\x2f\x74\x6f\x6f\x6c\x73\x2f\x65\x62\x70\x66\x2f\x72\x73\x73\x2e\x62\x70\x66\ -\x2e\x63\0\x69\x6e\x74\x20\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\ -\x69\x6e\x67\x5f\x70\x72\x6f\x67\x28\x73\x74\x72\x75\x63\x74\x20\x5f\x5f\x73\ -\x6b\x5f\x62\x75\x66\x66\x20\x2a\x73\x6b\x62\x29\0\x20\x20\x20\x20\x5f\x5f\x75\ -\x33\x32\x20\x6b\x65\x79\x20\x3d\x20\x30\x3b\0\x20\x20\x20\x20\x63\x6f\x6e\x66\ -\x69\x67\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\ -\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\ -\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\x2c\x20\x26\x6b\x65\ -\x79\x29\x3b\0\x20\x20\x20\x20\x74\x6f\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\ -\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\ -\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\ -\x79\x2c\x20\x26\x6b\x65\x79\x29\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x63\x6f\ -\x6e\x66\x69\x67\x20\x26\x26\x20\x74\x6f\x65\x29\x20\x7b\0\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x66\x20\x28\x21\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x72\x65\x64\ -\x69\x72\x65\x63\x74\x29\x20\x7b\0\x20\x20\x20\x20\x5f\x5f\x75\x38\x20\x72\x73\ -\x73\x5f\x69\x6e\x70\x75\x74\x5b\x48\x41\x53\x48\x5f\x43\x41\x4c\x43\x55\x4c\ -\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\x46\x45\x52\x5f\x53\x49\x5a\x45\x5d\x20\ -\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x70\x61\x63\ -\x6b\x65\x74\x5f\x68\x61\x73\x68\x5f\x69\x6e\x66\x6f\x5f\x74\x20\x70\x61\x63\ -\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x69\ -\x66\x20\x28\x21\x69\x6e\x66\x6f\x20\x7c\x7c\x20\x21\x73\x6b\x62\x29\x20\x7b\0\ -\x20\x20\x20\x20\x5f\x5f\x62\x65\x31\x36\x20\x72\x65\x74\x20\x3d\x20\x30\x3b\0\ -\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\ -\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\ -\x73\x6b\x62\x2c\x20\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x72\x65\x74\x2c\x20\ -\x73\x69\x7a\x65\x6f\x66\x28\x72\x65\x74\x29\x2c\0\x20\x20\x20\x20\x69\x66\x20\ -\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\ -\x62\x70\x66\x5f\x6e\x74\x6f\x68\x73\x28\x72\x65\x74\x29\x29\x20\x7b\0\x20\x20\ -\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\ -\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\ -\x65\x28\x73\x6b\x62\x2c\x20\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x72\x65\x74\ -\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x72\x65\x74\x29\x2c\0\x20\x20\x20\x20\x72\ -\x65\x74\x75\x72\x6e\x20\x72\x65\x74\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x6c\ -\x33\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x3d\x20\x30\x29\x20\x7b\0\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\ -\x34\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\ -\x74\x20\x69\x70\x68\x64\x72\x20\x69\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\ -\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\ -\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x2c\x20\x73\x69\x7a\x65\x6f\ -\x66\x28\x69\x70\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\ -\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\ -\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x20\x3d\x20\x21\x21\x28\ -\x62\x70\x66\x5f\x6e\x74\x6f\x68\x73\x28\x69\x70\x2e\x66\x72\x61\x67\x5f\x6f\ -\x66\x66\x29\x20\x26\x20\x28\x30\x78\x32\x30\x30\x30\x20\x7c\x20\x30\x78\x31\ -\x66\x66\x66\x29\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\ -\x3e\x69\x6e\x5f\x73\x72\x63\x20\x3d\x20\x69\x70\x2e\x73\x61\x64\x64\x72\x3b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x5f\x64\x73\ -\x74\x20\x3d\x20\x69\x70\x2e\x64\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x2e\ -\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\ -\x5f\x6f\x66\x66\x73\x65\x74\x20\x3d\x20\x69\x70\x2e\x69\x68\x6c\x20\x2a\x20\ -\x34\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\ -\x69\x70\x76\x36\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\ -\x72\x75\x63\x74\x20\x69\x70\x76\x36\x68\x64\x72\x20\x69\x70\x36\x20\x3d\x20\ -\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\ -\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\ -\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x36\ -\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x69\x70\x36\x29\x2c\0\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x73\x72\x63\x20\x3d\x20\ -\x69\x70\x36\x2e\x73\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\ -\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x64\x73\x74\x20\x3d\x20\x69\x70\x36\x2e\ -\x64\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x70\x72\ -\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x36\x2e\x6e\x65\x78\x74\x68\x64\ -\x72\x3b\0\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\x68\x64\x72\x5f\x74\ -\x79\x70\x65\x29\x20\x7b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x69\x70\ -\x76\x36\x5f\x6f\x70\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\x68\x64\x72\x20\ -\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\ -\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\ -\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\ -\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\x68\x64\x72\x2c\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x69\x66\x20\x28\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\ -\x6f\x6c\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x52\x4f\x55\x54\x49\ -\x4e\x47\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\ -\x72\x75\x63\x74\x20\x69\x70\x76\x36\x5f\x72\x74\x5f\x68\x64\x72\x20\x65\x78\ -\x74\x5f\x72\x74\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\ -\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\ -\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\ -\x74\x5f\x72\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\ -\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x69\x66\x20\x28\x28\x65\x78\x74\x5f\x72\x74\x2e\x74\x79\x70\x65\x20\x3d\ -\x3d\x20\x49\x50\x56\x36\x5f\x53\x52\x43\x52\x54\x5f\x54\x59\x50\x45\x5f\x32\ -\x29\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\ -\x66\x66\x73\x65\x74\x6f\x66\x28\x73\x74\x72\x75\x63\x74\x20\x72\x74\x32\x5f\ -\x68\x64\x72\x2c\x20\x61\x64\x64\x72\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\ -\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\ -\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\ -\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x3d\x20\x31\x3b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x5f\x5f\x61\x74\x74\ -\x72\x69\x62\x75\x74\x65\x5f\x5f\x28\x28\x70\x61\x63\x6b\x65\x64\x29\x29\x20\ -\x6f\x70\x74\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\ -\x20\x28\x6f\x70\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\ -\x54\x4c\x56\x5f\x50\x41\x44\x31\x29\x20\x3f\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x5f\x6f\x66\x66\ -\x73\x65\x74\x20\x2b\x20\x31\x20\x3e\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\ -\x68\x64\x72\x6c\x65\x6e\x20\x2a\x20\x38\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\ -\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\ -\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\ -\x65\x74\x20\x2b\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x2c\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\ -\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\ -\x48\x41\x4f\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\ -\x65\x74\x20\x2b\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\0\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\ -\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\ -\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\ -\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\ -\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\x20\x3d\x20\x31\x3b\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\ -\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x20\x3d\x20\x74\x72\x75\x65\x3b\0\x20\x20\ -\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\ -\x20\x28\x65\x78\x74\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\x6e\x20\x2b\x20\ -\x31\x29\x20\x2a\x20\x38\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\ -\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\ -\x6e\x65\x78\x74\x68\x64\x72\x3b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x75\x6e\ -\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\x20\x69\x20\x3d\x20\x30\x3b\x20\x69\ -\x20\x3c\x20\x49\x50\x36\x5f\x45\x58\x54\x45\x4e\x53\x49\x4f\x4e\x53\x5f\x43\ -\x4f\x55\x4e\x54\x3b\x20\x2b\x2b\x69\x29\x20\x7b\0\x20\x20\x20\x20\x69\x66\x20\ -\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x21\x3d\x20\x30\x20\x26\ -\x26\x20\x21\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\ -\x74\x65\x64\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6c\ -\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\ -\x54\x4f\x5f\x54\x43\x50\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x74\x63\x70\x20\x3d\x20\x31\x3b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\ -\x74\x63\x70\x68\x64\x72\x20\x74\x63\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\ -\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\ -\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\ -\x2c\x20\x26\x74\x63\x70\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\x63\x70\x29\ -\x2c\0\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\ -\x6f\x2e\x69\x73\x5f\x69\x70\x76\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\ -\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\ -\x5f\x74\x63\x70\x20\x26\x26\0\x20\x20\x20\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\ -\x6e\x5f\x6d\x65\x6d\x63\x70\x79\x28\x26\x72\x73\x73\x5f\x69\x6e\x70\x75\x74\ -\x5b\x2a\x62\x79\x74\x65\x73\x5f\x77\x72\x69\x74\x74\x65\x6e\x5d\x2c\x20\x70\ -\x74\x72\x2c\x20\x73\x69\x7a\x65\x29\x3b\0\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\ -\x65\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\ -\x73\x5f\x69\x70\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\ -\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\x20\x26\x26\0\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\ -\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\ -\x64\x73\x74\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\ -\x65\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\ -\x73\x5f\x75\x64\x70\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\ -\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\ -\x68\x5f\x74\x79\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\ -\x54\x5f\x52\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\ -\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\ -\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\ -\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\ -\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x36\x29\x20\x7b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\ -\x73\x5f\x75\x64\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x75\x64\x70\x68\x64\x72\x20\x75\x64\ -\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\ +\x50\x4c\x20\x76\x32\0\0\x9f\xeb\x01\0\x18\0\0\0\0\0\0\0\x58\x05\0\0\x58\x05\0\ +\0\x71\x11\0\0\0\0\0\0\0\0\0\x02\x03\0\0\0\x01\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\ +\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x02\0\0\0\x05\0\0\0\0\0\0\ +\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\ +\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\0\0\0\0\0\0\0\0\x03\0\ +\0\0\0\x02\0\0\0\x04\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\x02\x0a\0\0\0\0\0\0\0\0\0\0\ +\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x0c\0\0\0\0\0\0\0\ +\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\0\x05\0\0\x04\x28\0\0\0\ +\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\0\0\x07\0\0\0\ +\x80\0\0\0\x32\0\0\0\x09\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\x01\0\0\x48\0\0\ +\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\0\0\0\0\0\0\0\0\0\0\x03\ +\0\0\0\0\x02\0\0\0\x04\0\0\0\x28\0\0\0\0\0\0\0\x05\0\0\x04\x28\0\0\0\x19\0\0\0\ +\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\0\0\x0f\0\0\0\x80\0\0\0\ +\x32\0\0\0\x09\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\x01\0\0\x63\0\0\0\0\0\0\ +\x0e\x11\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x14\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\ +\x02\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x05\0\0\x04\x28\0\0\0\x19\0\0\0\x01\0\0\ +\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\0\0\x01\0\0\0\x80\0\0\0\x32\0\0\ +\0\x13\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\x01\0\0\x7c\0\0\0\0\0\0\x0e\x15\0\ +\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x18\0\0\0\x9a\0\0\0\x22\0\0\x04\xc0\0\0\0\xa4\ +\0\0\0\x19\0\0\0\0\0\0\0\xa8\0\0\0\x19\0\0\0\x20\0\0\0\xb1\0\0\0\x19\0\0\0\x40\ +\0\0\0\xb6\0\0\0\x19\0\0\0\x60\0\0\0\xc4\0\0\0\x19\0\0\0\x80\0\0\0\xcd\0\0\0\ +\x19\0\0\0\xa0\0\0\0\xda\0\0\0\x19\0\0\0\xc0\0\0\0\xe3\0\0\0\x19\0\0\0\xe0\0\0\ +\0\xee\0\0\0\x19\0\0\0\0\x01\0\0\xf7\0\0\0\x19\0\0\0\x20\x01\0\0\x07\x01\0\0\ +\x19\0\0\0\x40\x01\0\0\x0f\x01\0\0\x19\0\0\0\x60\x01\0\0\x18\x01\0\0\x1b\0\0\0\ +\x80\x01\0\0\x1b\x01\0\0\x19\0\0\0\x20\x02\0\0\x20\x01\0\0\x19\0\0\0\x40\x02\0\ +\0\x2b\x01\0\0\x19\0\0\0\x60\x02\0\0\x30\x01\0\0\x19\0\0\0\x80\x02\0\0\x39\x01\ +\0\0\x19\0\0\0\xa0\x02\0\0\x41\x01\0\0\x19\0\0\0\xc0\x02\0\0\x48\x01\0\0\x19\0\ +\0\0\xe0\x02\0\0\x53\x01\0\0\x19\0\0\0\0\x03\0\0\x5d\x01\0\0\x1c\0\0\0\x20\x03\ +\0\0\x68\x01\0\0\x1c\0\0\0\xa0\x03\0\0\x72\x01\0\0\x19\0\0\0\x20\x04\0\0\x7e\ +\x01\0\0\x19\0\0\0\x40\x04\0\0\x89\x01\0\0\x19\0\0\0\x60\x04\0\0\0\0\0\0\x1d\0\ +\0\0\x80\x04\0\0\x93\x01\0\0\x1f\0\0\0\xc0\x04\0\0\x9a\x01\0\0\x19\0\0\0\0\x05\ +\0\0\xa3\x01\0\0\x19\0\0\0\x20\x05\0\0\0\0\0\0\x21\0\0\0\x40\x05\0\0\xac\x01\0\ +\0\x19\0\0\0\x80\x05\0\0\xb5\x01\0\0\x23\0\0\0\xa0\x05\0\0\xc1\x01\0\0\x1f\0\0\ +\0\xc0\x05\0\0\xca\x01\0\0\0\0\0\x08\x1a\0\0\0\xd0\x01\0\0\0\0\0\x01\x04\0\0\0\ +\x20\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x19\0\0\0\x04\0\0\0\x05\0\0\0\0\0\0\0\0\0\ +\0\x03\0\0\0\0\x19\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\x01\0\0\x05\x08\0\0\0\xdd\ +\x01\0\0\x1e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2c\0\0\0\xe7\x01\0\0\0\0\0\x08\ +\x20\0\0\0\xed\x01\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x05\x08\0\ +\0\0\0\x02\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2d\0\0\0\x03\x02\0\0\0\0\0\ +\x08\x24\0\0\0\x08\x02\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\0\0\0\0\0\x01\0\0\x0d\ +\x02\0\0\0\x16\x02\0\0\x17\0\0\0\x1a\x02\0\0\x01\0\0\x0c\x25\0\0\0\x3e\x11\0\0\ +\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x27\0\0\0\x04\0\0\0\ +\x07\0\0\0\x43\x11\0\0\0\0\0\x0e\x28\0\0\0\x01\0\0\0\x4c\x11\0\0\x03\0\0\x0f\0\ +\0\0\0\x0e\0\0\0\0\0\0\0\x28\0\0\0\x12\0\0\0\0\0\0\0\x28\0\0\0\x16\0\0\0\0\0\0\ +\0\x28\0\0\0\x52\x11\0\0\x01\0\0\x0f\0\0\0\0\x29\0\0\0\0\0\0\0\x07\0\0\0\x5a\ +\x11\0\0\0\0\0\x07\0\0\0\0\x68\x11\0\0\0\0\0\x07\0\0\0\0\0\x69\x6e\x74\0\x5f\ +\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x74\ +\x79\x70\x65\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\x76\x61\x6c\x75\x65\x5f\x73\ +\x69\x7a\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x6d\x61\x70\x5f\ +\x66\x6c\x61\x67\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\ +\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x61\x70\x5f\x72\x73\x73\ +\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\x79\0\x74\x61\ +\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\ +\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x5f\x5f\x73\x6b\x5f\x62\x75\x66\x66\0\x6c\ +\x65\x6e\0\x70\x6b\x74\x5f\x74\x79\x70\x65\0\x6d\x61\x72\x6b\0\x71\x75\x65\x75\ +\x65\x5f\x6d\x61\x70\x70\x69\x6e\x67\0\x70\x72\x6f\x74\x6f\x63\x6f\x6c\0\x76\ +\x6c\x61\x6e\x5f\x70\x72\x65\x73\x65\x6e\x74\0\x76\x6c\x61\x6e\x5f\x74\x63\x69\ +\0\x76\x6c\x61\x6e\x5f\x70\x72\x6f\x74\x6f\0\x70\x72\x69\x6f\x72\x69\x74\x79\0\ +\x69\x6e\x67\x72\x65\x73\x73\x5f\x69\x66\x69\x6e\x64\x65\x78\0\x69\x66\x69\x6e\ +\x64\x65\x78\0\x74\x63\x5f\x69\x6e\x64\x65\x78\0\x63\x62\0\x68\x61\x73\x68\0\ +\x74\x63\x5f\x63\x6c\x61\x73\x73\x69\x64\0\x64\x61\x74\x61\0\x64\x61\x74\x61\ +\x5f\x65\x6e\x64\0\x6e\x61\x70\x69\x5f\x69\x64\0\x66\x61\x6d\x69\x6c\x79\0\x72\ +\x65\x6d\x6f\x74\x65\x5f\x69\x70\x34\0\x6c\x6f\x63\x61\x6c\x5f\x69\x70\x34\0\ +\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x36\0\x6c\x6f\x63\x61\x6c\x5f\x69\x70\x36\ +\0\x72\x65\x6d\x6f\x74\x65\x5f\x70\x6f\x72\x74\0\x6c\x6f\x63\x61\x6c\x5f\x70\ +\x6f\x72\x74\0\x64\x61\x74\x61\x5f\x6d\x65\x74\x61\0\x74\x73\x74\x61\x6d\x70\0\ +\x77\x69\x72\x65\x5f\x6c\x65\x6e\0\x67\x73\x6f\x5f\x73\x65\x67\x73\0\x67\x73\ +\x6f\x5f\x73\x69\x7a\x65\0\x74\x73\x74\x61\x6d\x70\x5f\x74\x79\x70\x65\0\x68\ +\x77\x74\x73\x74\x61\x6d\x70\0\x5f\x5f\x75\x33\x32\0\x75\x6e\x73\x69\x67\x6e\ +\x65\x64\x20\x69\x6e\x74\0\x66\x6c\x6f\x77\x5f\x6b\x65\x79\x73\0\x5f\x5f\x75\ +\x36\x34\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\ +\x67\0\x73\x6b\0\x5f\x5f\x75\x38\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x63\x68\ +\x61\x72\0\x73\x6b\x62\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\ +\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x73\x6f\x63\x6b\x65\x74\0\x2f\x68\x6f\x6d\ +\x65\x2f\x6d\x65\x2f\x71\x2f\x76\x61\x72\x2f\x71\x65\x6d\x75\x2f\x74\x6f\x6f\ +\x6c\x73\x2f\x65\x62\x70\x66\x2f\x72\x73\x73\x2e\x62\x70\x66\x2e\x63\0\x69\x6e\ +\x74\x20\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\ +\x70\x72\x6f\x67\x28\x73\x74\x72\x75\x63\x74\x20\x5f\x5f\x73\x6b\x5f\x62\x75\ +\x66\x66\x20\x2a\x73\x6b\x62\x29\0\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\x20\x6b\ +\x65\x79\x20\x3d\x20\x30\x3b\0\x20\x20\x20\x20\x63\x6f\x6e\x66\x69\x67\x20\x3d\ +\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\ +\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\x6e\x66\ +\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\x2c\x20\x26\x6b\x65\x79\x29\x3b\0\x20\ +\x20\x20\x20\x74\x6f\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\ +\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\ +\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\x79\x2c\x20\x26\ +\x6b\x65\x79\x29\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\ +\x20\x26\x26\x20\x74\x6f\x65\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\ +\x66\x20\x28\x21\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x72\x65\x64\x69\x72\x65\x63\ +\x74\x29\x20\x7b\0\x20\x20\x20\x20\x5f\x5f\x75\x38\x20\x72\x73\x73\x5f\x69\x6e\ +\x70\x75\x74\x5b\x48\x41\x53\x48\x5f\x43\x41\x4c\x43\x55\x4c\x41\x54\x49\x4f\ +\x4e\x5f\x42\x55\x46\x46\x45\x52\x5f\x53\x49\x5a\x45\x5d\x20\x3d\x20\x7b\x7d\ +\x3b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x70\x61\x63\x6b\x65\x74\x5f\ +\x68\x61\x73\x68\x5f\x69\x6e\x66\x6f\x5f\x74\x20\x70\x61\x63\x6b\x65\x74\x5f\ +\x69\x6e\x66\x6f\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x21\ +\x69\x6e\x66\x6f\x20\x7c\x7c\x20\x21\x73\x6b\x62\x29\x20\x7b\0\x20\x20\x20\x20\ +\x5f\x5f\x62\x65\x31\x36\x20\x72\x65\x74\x20\x3d\x20\x30\x3b\0\x20\x20\x20\x20\ \x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\ \x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\ -\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x75\x64\x70\x2c\x20\x73\ -\x69\x7a\x65\x6f\x66\x28\x75\x64\x70\x29\x2c\0\x20\x20\x20\x20\x66\x6f\x72\x20\ -\x28\x62\x79\x74\x65\x20\x3d\x20\x30\x3b\x20\x62\x79\x74\x65\x20\x3c\x20\x48\ -\x41\x53\x48\x5f\x43\x41\x4c\x43\x55\x4c\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\ -\x46\x45\x52\x5f\x53\x49\x5a\x45\x3b\x20\x62\x79\x74\x65\x2b\x2b\x29\x20\x7b\0\ -\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\x20\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\ -\x33\x32\x5f\x62\x69\x74\x73\x20\x3d\x20\x6b\x65\x79\x2d\x3e\x6c\x65\x66\x74\ -\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\x74\x73\x3b\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x5f\x5f\x75\x38\x20\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\x65\x20\x3d\ -\x20\x69\x6e\x70\x75\x74\x5b\x62\x79\x74\x65\x5d\x3b\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\ -\x65\x20\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x7b\0\x20\x20\x20\x20\ -\x20\x20\x20\x20\x5f\x5f\x75\x38\x20\x6b\x65\x79\x5f\x62\x79\x74\x65\x20\x3d\ -\x20\x6b\x65\x79\x2d\x3e\x6e\x65\x78\x74\x5f\x62\x79\x74\x65\x5b\x62\x79\x74\ -\x65\x5d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x28\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\ -\x74\x73\x20\x3c\x3c\x20\x31\x29\x20\x7c\x20\x28\x28\x6b\x65\x79\x5f\x62\x79\ -\x74\x65\x20\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x3e\x3e\x20\x37\ -\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\ -\x20\x74\x61\x62\x6c\x65\x5f\x69\x64\x78\x20\x3d\x20\x68\x61\x73\x68\x20\x25\ -\x20\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\ -\x6e\x73\x5f\x6c\x65\x6e\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x71\x75\x65\x75\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\ -\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\ -\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\ -\x65\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x71\ -\x75\x65\x75\x65\x29\x20\x7b\0\x7d\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x20\x2a\x71\x75\x65\x75\x65\ -\x3b\0\x63\x68\x61\x72\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x6d\x61\x70\x73\ -\0\x6c\x69\x63\x65\x6e\x73\x65\0\x62\x70\x66\x5f\x66\x6c\x6f\x77\x5f\x6b\x65\ -\x79\x73\0\x62\x70\x66\x5f\x73\x6f\x63\x6b\0\0\0\0\x9f\xeb\x01\0\x20\0\0\0\0\0\ -\0\0\x14\0\0\0\x14\0\0\0\x6c\x0c\0\0\x80\x0c\0\0\0\0\0\0\x08\0\0\0\x30\x02\0\0\ -\x01\0\0\0\0\0\0\0\x26\0\0\0\x10\0\0\0\x30\x02\0\0\xc6\0\0\0\0\0\0\0\x37\x02\0\ -\0\x60\x02\0\0\0\x68\x08\0\x10\0\0\0\x37\x02\0\0\x91\x02\0\0\x0b\x80\x08\0\x20\ -\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x28\0\0\0\x37\x02\0\0\xa4\x02\0\0\x0e\x8c\ -\x08\0\x50\0\0\0\x37\x02\0\0\xe9\x02\0\0\x0b\x90\x08\0\x88\0\0\0\x37\x02\0\0\ -\x29\x03\0\0\x10\x98\x08\0\x90\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x98\0\0\0\x37\ -\x02\0\0\x29\x03\0\0\x10\x98\x08\0\xa0\0\0\0\x37\x02\0\0\x42\x03\0\0\x16\x9c\ -\x08\0\xa8\0\0\0\x37\x02\0\0\x42\x03\0\0\x0d\x9c\x08\0\xc0\0\0\0\x37\x02\0\0\ -\x63\x03\0\0\x0a\x10\x06\0\xe8\0\0\0\x37\x02\0\0\x9a\x03\0\0\x1f\x1c\x06\0\x38\ -\x01\0\0\x37\x02\0\0\xca\x03\0\0\x0f\xac\x04\0\x40\x01\0\0\x37\x02\0\0\xe3\x03\ -\0\0\x0c\x2c\x04\0\x50\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x58\x01\0\0\x37\x02\ -\0\0\xf7\x03\0\0\x0b\x38\x04\0\x80\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\ -\0\x90\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\0\xa0\x01\0\0\x37\x02\0\0\ -\x4c\x04\0\0\x0d\x50\x04\0\xb8\x01\0\0\x37\x02\0\0\x4c\x04\0\0\x05\x50\x04\0\ -\xd8\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xe0\x01\0\0\x37\x02\0\0\x6a\x04\0\0\ -\x0f\x64\x04\0\0\x02\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x7c\x04\0\x10\x02\0\0\x37\ -\x02\0\0\x3d\x04\0\0\x09\x7c\x04\0\x18\x02\0\0\x37\x02\0\0\xb4\x04\0\0\x0c\x8c\ -\x04\0\x20\x02\0\0\x37\x02\0\0\xc4\x04\0\0\x09\xc8\x04\0\x48\x02\0\0\x37\x02\0\ -\0\xe0\x04\0\0\x17\xe0\x04\0\x58\x02\0\0\x37\x02\0\0\xfb\x04\0\0\x16\xe8\x04\0\ -\x78\x02\0\0\x37\x02\0\0\xe0\x04\0\0\x17\xe0\x04\0\x80\x02\0\0\x37\x02\0\0\x19\ -\x05\0\0\x0f\xec\x04\0\xa8\x02\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xb8\ -\x02\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xc0\x02\0\0\x37\x02\0\0\x6f\x05\ -\0\0\x22\x0c\x05\0\xc8\x02\0\0\x37\x02\0\0\x6f\x05\0\0\x39\x0c\x05\0\xd8\x02\0\ -\0\x37\x02\0\0\x6f\x05\0\0\x20\x0c\x05\0\xe8\x02\0\0\x37\x02\0\0\xbd\x05\0\0\ -\x1b\x04\x05\0\xf0\x02\0\0\x37\x02\0\0\xbd\x05\0\0\x16\x04\x05\0\xf8\x02\0\0\ -\x37\x02\0\0\xde\x05\0\0\x1b\x08\x05\0\0\x03\0\0\x37\x02\0\0\xde\x05\0\0\x16\ -\x08\x05\0\x08\x03\0\0\x37\x02\0\0\xff\x05\0\0\x1a\x14\x05\0\x10\x03\0\0\x37\ -\x02\0\0\x22\x06\0\0\x18\x18\x05\0\x18\x03\0\0\x37\x02\0\0\x22\x06\0\0\x1c\x18\ -\x05\0\x28\x03\0\0\x37\x02\0\0\x6f\x05\0\0\x1d\x0c\x05\0\x40\x03\0\0\x37\x02\0\ -\0\x42\x06\0\0\x17\x20\x05\0\x50\x03\0\0\x37\x02\0\0\x5d\x06\0\0\x18\x28\x05\0\ -\x80\x03\0\0\x37\x02\0\0\x42\x06\0\0\x17\x20\x05\0\x88\x03\0\0\x37\x02\0\0\x7e\ -\x06\0\0\x0f\x2c\x05\0\xb0\x03\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\xc0\ -\x03\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\xd8\x03\0\0\x37\x02\0\0\xc3\x06\ -\0\0\x1d\x44\x05\0\x18\x04\0\0\x37\x02\0\0\xe6\x06\0\0\x1d\x48\x05\0\x58\x04\0\ -\0\x37\x02\0\0\x09\x07\0\0\x1b\x50\x05\0\x60\x04\0\0\x37\x02\0\0\x2c\x07\0\0\ -\x05\x3c\x02\0\xa8\x04\0\0\x37\x02\0\0\x44\x07\0\0\x19\xc4\x02\0\x10\x05\0\0\ -\x37\x02\0\0\0\0\0\0\0\0\0\0\x18\x05\0\0\x37\x02\0\0\x6a\x07\0\0\x0f\xd4\x02\0\ -\x40\x05\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xdc\x02\0\x50\x05\0\0\x37\x02\0\0\x5c\ -\x05\0\0\x0d\xdc\x02\0\x58\x05\0\0\x37\x02\0\0\xaf\x07\0\0\x0d\xec\x02\0\x80\ -\x05\0\0\x37\x02\0\0\xde\x07\0\0\x20\xf0\x02\0\xa8\x05\0\0\x37\x02\0\0\x0a\x08\ -\0\0\x13\xf8\x02\0\xc8\x05\0\0\x37\x02\0\0\x52\x08\0\0\x11\0\x03\0\xd8\x05\0\0\ -\x37\x02\0\0\x52\x08\0\0\x11\0\x03\0\xe0\x05\0\0\x37\x02\0\0\x69\x08\0\0\x19\ -\x10\x03\0\xe8\x05\0\0\x37\x02\0\0\x69\x08\0\0\x34\x10\x03\0\x10\x06\0\0\x37\ -\x02\0\0\x9f\x08\0\0\x15\x24\x03\0\x20\x06\0\0\x37\x02\0\0\xe0\x08\0\0\x17\x20\ -\x03\0\x48\x06\0\0\x37\x02\0\0\x17\x09\0\0\x15\x30\x03\0\x58\x06\0\0\x37\x02\0\ -\0\x17\x09\0\0\x15\x30\x03\0\x60\x06\0\0\x37\x02\0\0\x32\x09\0\0\x27\x40\x03\0\ -\x88\x06\0\0\x37\x02\0\0\x5d\x09\0\0\x27\x5c\x03\0\x98\x06\0\0\x37\x02\0\0\x8d\ -\x09\0\0\x1c\xc0\x03\0\xa0\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x20\xcc\x03\0\xb0\ -\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x2f\xcc\x03\0\xb8\x06\0\0\x37\x02\0\0\xc9\x09\ -\0\0\x36\xcc\x03\0\xc0\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x15\xcc\x03\0\x28\x07\0\ -\0\x37\x02\0\0\x05\x0a\0\0\x43\x70\x03\0\x48\x07\0\0\x37\x02\0\0\0\0\0\0\0\0\0\ -\0\x50\x07\0\0\x37\x02\0\0\x05\x0a\0\0\x17\x70\x03\0\x78\x07\0\0\x37\x02\0\0\ -\x17\x09\0\0\x15\x78\x03\0\x88\x07\0\0\x37\x02\0\0\x17\x09\0\0\x15\x78\x03\0\ -\x90\x07\0\0\x37\x02\0\0\x55\x0a\0\0\x19\x88\x03\0\x98\x07\0\0\x37\x02\0\0\x55\ -\x0a\0\0\x15\x88\x03\0\xa0\x07\0\0\x37\x02\0\0\x85\x0a\0\0\x19\x90\x03\0\xa8\ -\x07\0\0\x37\x02\0\0\xb5\x0a\0\0\x1b\x8c\x03\0\xd8\x07\0\0\x37\x02\0\0\xf0\x0a\ -\0\0\x19\xa0\x03\0\xe8\x07\0\0\x37\x02\0\0\xf0\x0a\0\0\x19\xa0\x03\0\xf0\x07\0\ -\0\x37\x02\0\0\x0f\x0b\0\0\x2b\xb0\x03\0\x10\x08\0\0\x37\x02\0\0\x8d\x09\0\0\ -\x1f\xc0\x03\0\x30\x08\0\0\x37\x02\0\0\x3e\x0b\0\0\x21\xe0\x03\0\x48\x08\0\0\ -\x37\x02\0\0\x66\x0b\0\0\x20\xf0\x03\0\x50\x08\0\0\x37\x02\0\0\x66\x0b\0\0\x2c\ -\xf0\x03\0\x60\x08\0\0\x37\x02\0\0\x66\x0b\0\0\x14\xf0\x03\0\x68\x08\0\0\x37\ -\x02\0\0\x96\x0b\0\0\x20\xec\x03\0\x70\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\ -\x02\0\xb8\x08\0\0\x37\x02\0\0\xbe\x0b\0\0\x38\xcc\x02\0\xd8\x08\0\0\x37\x02\0\ -\0\xbe\x0b\0\0\x05\xcc\x02\0\xe8\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\ -\0\x09\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\x18\x09\0\0\x37\x02\0\0\xfc\ -\x0b\0\0\x15\x74\x05\0\x28\x09\0\0\x37\x02\0\0\xfc\x0b\0\0\x1a\x74\x05\0\x40\ -\x09\0\0\x37\x02\0\0\x30\x0c\0\0\x0d\x78\x05\0\x60\x09\0\0\x37\x02\0\0\x5a\x0c\ -\0\0\x1a\x7c\x05\0\x70\x09\0\0\x37\x02\0\0\x78\x0c\0\0\x1b\x84\x05\0\x90\x09\0\ -\0\x37\x02\0\0\x5a\x0c\0\0\x1a\x7c\x05\0\x98\x09\0\0\x37\x02\0\0\x9c\x0c\0\0\ -\x13\x88\x05\0\xb8\x09\0\0\x37\x02\0\0\x52\x08\0\0\x11\x90\x05\0\xc8\x09\0\0\ -\x37\x02\0\0\x52\x08\0\0\x11\x90\x05\0\xd0\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\ -\xf0\x09\0\0\x37\x02\0\0\xed\x0c\0\0\x15\x38\x06\0\xf8\x09\0\0\x37\x02\0\0\xed\ -\x0c\0\0\x09\x38\x06\0\0\x0a\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x50\x0a\0\0\x37\ -\x02\0\0\x0c\x0d\0\0\x19\x3c\x06\0\x58\x0a\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\x3c\ -\x06\0\x78\x0a\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xc0\x0a\0\0\x37\x02\0\ -\0\x6b\x0d\0\0\x1c\xd4\x06\0\xc8\x0a\0\0\x37\x02\0\0\x6b\x0d\0\0\x10\xd4\x06\0\ -\xd0\x0a\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x20\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\ -\x19\xd8\x06\0\x28\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\xd8\x06\0\x48\x0b\0\0\ -\x37\x02\0\0\x91\x0d\0\0\x2d\xe4\x06\0\x58\x0b\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\ -\xe4\x06\0\x68\x0b\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\xe4\x06\0\x98\x0b\0\0\x37\ -\x02\0\0\xc0\x0d\0\0\x2d\x10\x07\0\xa8\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x10\ -\x07\0\xb8\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\x10\x07\0\xe0\x0b\0\0\x37\x02\0\ -\0\x2e\x0d\0\0\x05\xa4\x01\0\xa8\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x78\x06\0\ -\xb0\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x27\x78\x06\0\xd8\x0c\0\0\x37\x02\0\0\x18\ -\x0e\0\0\x27\xb4\x06\0\xe0\x0c\0\0\x37\x02\0\0\x18\x0e\0\0\x14\xb4\x06\0\xe8\ -\x0c\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xf8\x0c\0\0\x37\x02\0\0\x2e\x0d\ -\0\0\x05\xa4\x01\0\x10\x0d\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x54\x07\0\x18\x0d\0\ -\0\x37\x02\0\0\xef\x0d\0\0\x27\x54\x07\0\x38\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\ -\x2d\x60\x07\0\x48\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\x60\x07\0\x58\x0d\0\0\ -\x37\x02\0\0\x91\x0d\0\0\x2d\x60\x07\0\x88\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\ -\x8c\x07\0\x98\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x8c\x07\0\xa8\x0d\0\0\x37\ -\x02\0\0\xc0\x0d\0\0\x2d\x8c\x07\0\xd0\x0d\0\0\x37\x02\0\0\x61\x0e\0\0\x27\xd8\ -\x07\0\xe0\x0d\0\0\x37\x02\0\0\x61\x0e\0\0\x14\xd8\x07\0\xe8\x0d\0\0\x37\x02\0\ -\0\x91\x0d\0\0\x2d\xdc\x07\0\xf8\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\xdc\x07\0\ -\x08\x0e\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\xdc\x07\0\x38\x0e\0\0\x37\x02\0\0\x2e\ -\x0d\0\0\x05\xa4\x01\0\x88\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x17\xa4\x01\0\x98\ -\x0e\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\x08\x08\0\xb0\x0e\0\0\x37\x02\0\0\x2e\x0d\ -\0\0\x05\xa4\x01\0\xf8\x0e\0\0\x37\x02\0\0\xaa\x0e\0\0\x1a\xac\x05\0\x08\x0f\0\ -\0\x37\x02\0\0\xc8\x0e\0\0\x1b\xb4\x05\0\x18\x0f\0\0\x37\x02\0\0\xaa\x0e\0\0\ -\x1a\xac\x05\0\x20\x0f\0\0\x37\x02\0\0\xec\x0e\0\0\x13\xb8\x05\0\x40\x0f\0\0\ -\x37\x02\0\0\x52\x08\0\0\x11\xc0\x05\0\x50\x0f\0\0\x37\x02\0\0\x52\x08\0\0\x11\ -\xc0\x05\0\x60\x0f\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\x98\x0f\0\0\x37\ -\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xa8\x0f\0\0\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\ -\x01\0\xb0\x0f\0\0\x37\x02\0\0\x7f\x0f\0\0\x23\xd0\x01\0\xc8\x0f\0\0\x37\x02\0\ -\0\0\0\0\0\0\0\0\0\xd0\x0f\0\0\x37\x02\0\0\xb3\x0f\0\0\x1b\xe0\x01\0\xf0\x0f\0\ -\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x08\x10\0\0\x37\x02\0\0\x03\x10\0\0\ -\x19\xe4\x01\0\x20\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x38\x10\0\0\ -\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x40\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\ -\xf4\x01\0\x80\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x88\x10\0\0\x37\ -\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x90\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\ -\x01\0\xb8\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xd8\x10\0\0\x37\x02\0\ -\0\x31\x10\0\0\x2d\x08\x02\0\xe0\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\ -\x08\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x28\x11\0\0\x37\x02\0\0\x31\ -\x10\0\0\x2d\x08\x02\0\x30\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x70\ -\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x78\x11\0\0\x37\x02\0\0\x31\x10\ -\0\0\x2d\x08\x02\0\x80\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xc0\x11\0\ -\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xc8\x11\0\0\x37\x02\0\0\x31\x10\0\0\ -\x2d\x08\x02\0\xd0\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x10\x12\0\0\ -\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x18\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\ -\x08\x02\0\x20\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x48\x12\0\0\x37\ -\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x50\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\ -\x02\0\x58\x12\0\0\x37\x02\0\0\x3d\x0f\0\0\x3d\xdc\x01\0\x68\x12\0\0\x37\x02\0\ -\0\x3d\x0f\0\0\x05\xdc\x01\0\x78\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x2e\xb0\x08\0\ -\x98\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x24\xb0\x08\0\xb0\x12\0\0\x37\x02\0\0\x7d\ -\x10\0\0\x13\xb0\x08\0\xc0\x12\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xc8\x12\0\0\x37\ -\x02\0\0\xbc\x10\0\0\x15\xbc\x08\0\xe0\x12\0\0\x37\x02\0\0\x04\x11\0\0\x11\xc8\ -\x08\0\xe8\x12\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x08\x13\0\0\x37\x02\0\0\x1d\x11\ -\0\0\x01\xec\x08\0\x10\x13\0\0\x37\x02\0\0\x1f\x11\0\0\x18\xcc\x08\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\x94\x01\0\0\0\0\x03\0\x08\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x75\x01\0\0\0\0\x03\0\xb8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcd\x01\0\0\0\0\x03\0\ -\xe8\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\xff\x01\0\0\0\0\x03\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\ -\x01\0\0\0\0\x03\0\x38\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x24\x01\0\0\0\0\x03\0\ -\xf0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2c\x01\0\0\0\0\x03\0\xe8\x02\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\xf7\x01\0\0\0\0\x03\0\x18\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x30\ -\x02\0\0\0\0\x03\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe6\x01\0\0\0\0\x03\0\xa0\ -\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc5\x01\0\0\0\0\x03\0\x08\x05\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\xbd\x01\0\0\0\0\x03\0\x78\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb5\x01\ -\0\0\0\0\x03\0\x30\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x01\0\0\0\0\x03\0\x48\ -\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x84\x01\0\0\0\0\x03\0\x40\x08\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\x8c\x01\0\0\0\0\x03\0\x28\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x28\x02\ -\0\0\0\0\x03\0\x98\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xde\x01\0\0\0\0\x03\0\x10\ -\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0c\x01\0\0\0\0\x03\0\x08\x08\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\x4c\x01\0\0\0\0\x03\0\xe8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\x01\ -\0\0\0\0\x03\0\xb0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x03\0\xf0\x08\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x02\0\0\0\0\x03\0\x08\x09\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\xfc\0\0\0\0\0\x03\0\xf0\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x44\x01\0\0\0\ -\0\x03\0\xd0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf4\0\0\0\0\0\x03\0\xc0\x0a\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xad\x01\0\0\0\0\x03\0\xa8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\xd6\x01\0\0\0\0\x03\0\x78\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x02\0\0\0\0\ -\x03\0\xa0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x02\0\0\0\0\x03\0\x10\x0d\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xa5\x01\0\0\0\0\x03\0\x80\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x5c\x01\0\0\0\0\x03\0\x98\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1c\x01\0\0\0\0\ -\x03\0\xd0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcc\0\0\0\0\0\x03\0\xe0\x0b\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\xec\0\0\0\0\0\x03\0\xd8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x3c\x01\0\0\0\0\x03\0\xd8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x02\0\0\0\0\x03\ -\0\xd0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7c\x01\0\0\0\0\x03\0\x70\x0d\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x34\x01\0\0\0\0\x03\0\x88\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\xe4\0\0\0\0\0\x03\0\xc0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9d\x01\0\0\0\0\x03\0\ -\x20\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x54\x01\0\0\0\0\x03\0\x38\x0e\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\xdc\0\0\0\0\0\x03\0\x60\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\0\ -\0\0\0\0\x03\0\x98\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x07\x02\0\0\0\0\x03\0\xc0\ -\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xee\x01\0\0\0\0\x03\0\x78\x12\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\x6c\x01\0\0\0\0\x03\0\x10\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x59\0\0\ -\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x20\x13\0\0\0\0\0\0\x3e\0\0\0\x11\0\x05\0\0\0\0\ -\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x28\0\0\0\ -\0\0\0\0\x86\0\0\0\x11\0\x05\0\x50\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x7d\0\0\0\ -\x11\0\x06\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x33\ -\0\0\0\x50\0\0\0\0\0\0\0\x01\0\0\0\x34\0\0\0\xc8\x12\0\0\0\0\0\0\x01\0\0\0\x35\ -\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x33\0\0\0\x2c\x05\0\0\0\0\0\0\x04\0\0\0\ -\x34\0\0\0\x38\x05\0\0\0\0\0\0\x04\0\0\0\x35\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\ -\0\x36\0\0\0\x2c\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\0\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x50\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\0\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x70\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x90\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\0\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xb0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\0\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xd0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\0\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xf0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x01\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x10\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x30\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x01\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x50\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x01\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x70\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x01\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x90\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x01\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\xb0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x01\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\xd0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x01\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x02\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x10\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x02\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x02\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x02\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\ -\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xa0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\xc0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\xe0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x02\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x03\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x20\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x03\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x40\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x03\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x60\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x03\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x80\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x03\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\xa0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x03\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\xc0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x03\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\xe0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x03\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x04\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\x20\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x04\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x40\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x04\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x04\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x04\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\ -\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\xd0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\xf0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x10\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x30\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x05\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x05\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x70\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x05\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x90\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x05\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\xb0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x05\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\xd0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x05\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\xf0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x06\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x10\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x06\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\x30\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x06\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\x50\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x06\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\x70\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x06\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x06\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x06\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\ -\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\ -\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x20\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x40\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x60\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x07\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\x80\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x07\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xa0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x07\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\xc0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x07\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\xe0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x07\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x08\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x20\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x08\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x40\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x08\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\x60\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x08\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\x80\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x08\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x08\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x08\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x08\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x09\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\ -\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x50\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\x70\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\x90\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x09\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\xb0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x09\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\xd0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x09\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\xf0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0a\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x10\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0a\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x30\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0a\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x50\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0a\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x70\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0a\0\0\0\0\0\ -\0\x04\0\0\0\x01\0\0\0\x90\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0a\0\0\0\0\ -\0\0\x04\0\0\0\x01\0\0\0\xb0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0a\0\0\0\ -\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x0a\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0b\0\0\ -\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0b\0\ -\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0b\ -\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\ -\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ -\x80\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ -\0\xa0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\ -\0\0\xc0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\ -\0\0\0\xe0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x0b\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0c\0\0\0\0\0\0\x04\0\0\0\ -\x01\0\0\0\x20\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0c\0\0\0\0\0\0\x04\0\0\ -\0\x01\0\0\0\x40\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0c\0\0\0\0\0\0\x04\0\ -\0\0\x01\0\0\0\x60\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0c\0\0\0\0\0\0\x04\ -\0\0\0\x01\0\0\0\x80\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0c\0\0\0\0\0\0\ -\x04\0\0\0\x01\0\0\0\x39\x3a\x3b\x3c\x3d\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\ -\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\ -\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\x2e\x65\x78\x74\0\x2e\x72\x65\x6c\x73\ -\x6f\x63\x6b\x65\x74\0\x2e\x6d\x61\x70\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\ -\x6d\x61\x70\x5f\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\ -\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\ -\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\x72\x73\x69\x67\0\x5f\x6c\x69\x63\ -\x65\x6e\x73\x65\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\ -\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x2e\x73\x74\x72\x74\ -\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\ -\x42\x42\x30\x5f\x39\0\x4c\x42\x42\x30\x5f\x39\x39\0\x4c\x42\x42\x30\x5f\x37\ -\x39\0\x4c\x42\x42\x30\x5f\x34\x39\0\x4c\x42\x42\x30\x5f\x39\x38\0\x4c\x42\x42\ -\x30\x5f\x38\x38\0\x4c\x42\x42\x30\x5f\x37\x38\0\x4c\x42\x42\x30\x5f\x36\x38\0\ -\x4c\x42\x42\x30\x5f\x35\x38\0\x4c\x42\x42\x30\x5f\x34\x38\0\x4c\x42\x42\x30\ -\x5f\x33\x38\0\x4c\x42\x42\x30\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x37\x37\0\x4c\ -\x42\x42\x30\x5f\x35\x37\0\x4c\x42\x42\x30\x5f\x31\x37\0\x4c\x42\x42\x30\x5f\ -\x38\x36\0\x4c\x42\x42\x30\x5f\x36\x36\0\x4c\x42\x42\x30\x5f\x35\x36\0\x4c\x42\ -\x42\x30\x5f\x34\x36\0\x4c\x42\x42\x30\x5f\x39\x35\0\x4c\x42\x42\x30\x5f\x37\ -\x35\0\x4c\x42\x42\x30\x5f\x34\x35\0\x4c\x42\x42\x30\x5f\x31\x30\x35\0\x4c\x42\ -\x42\x30\x5f\x34\0\x4c\x42\x42\x30\x5f\x38\x34\0\x4c\x42\x42\x30\x5f\x34\x34\0\ -\x4c\x42\x42\x30\x5f\x33\x34\0\x4c\x42\x42\x30\x5f\x31\x30\x34\0\x4c\x42\x42\ -\x30\x5f\x39\x33\0\x4c\x42\x42\x30\x5f\x37\x33\0\x4c\x42\x42\x30\x5f\x36\x33\0\ -\x4c\x42\x42\x30\x5f\x34\x33\0\x4c\x42\x42\x30\x5f\x33\x33\0\x4c\x42\x42\x30\ -\x5f\x32\x33\0\x4c\x42\x42\x30\x5f\x31\x30\x33\0\x4c\x42\x42\x30\x5f\x36\x32\0\ -\x4c\x42\x42\x30\x5f\x34\x32\0\x4c\x42\x42\x30\x5f\x32\x32\0\x4c\x42\x42\x30\ -\x5f\x31\x30\x32\0\x4c\x42\x42\x30\x5f\x35\x31\0\x4c\x42\x42\x30\x5f\x31\x31\0\ -\x4c\x42\x42\x30\x5f\x31\x30\x31\0\x4c\x42\x42\x30\x5f\x39\x30\0\x4c\x42\x42\ -\x30\x5f\x38\x30\0\x4c\x42\x42\x30\x5f\x35\x30\0\x4c\x42\x42\x30\x5f\x34\x30\0\ -\x4c\x42\x42\x30\x5f\x32\x30\0\x4c\x42\x42\x30\x5f\x31\x30\x30\0\0\0\0\0\0\0\0\ +\x20\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x72\x65\x74\x2c\x20\x73\x69\x7a\x65\ +\x6f\x66\x28\x72\x65\x74\x29\x2c\0\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\ +\x29\x20\x7b\0\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\x62\x70\x66\x5f\ +\x6e\x74\x6f\x68\x73\x28\x72\x65\x74\x29\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\ +\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\ +\x62\x2c\x20\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x72\x65\x74\x2c\x20\x73\x69\ +\x7a\x65\x6f\x66\x28\x72\x65\x74\x29\x2c\0\x20\x20\x20\x20\x72\x65\x74\x75\x72\ +\x6e\x20\x72\x65\x74\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x33\x5f\x70\x72\ +\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x3d\x20\x30\x29\x20\x7b\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\x34\x20\x3d\x20\ +\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x69\x70\ +\x68\x64\x72\x20\x69\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\ +\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\ +\x2c\x20\x30\x2c\x20\x26\x69\x70\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x69\x70\ +\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\ +\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\ +\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x20\x3d\x20\x21\x21\x28\x62\x70\x66\x5f\ +\x6e\x74\x6f\x68\x73\x28\x69\x70\x2e\x66\x72\x61\x67\x5f\x6f\x66\x66\x29\x20\ +\x26\x20\x28\x30\x78\x32\x30\x30\x30\x20\x7c\x20\x30\x78\x31\x66\x66\x66\x29\ +\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x5f\ +\x73\x72\x63\x20\x3d\x20\x69\x70\x2e\x73\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x5f\x64\x73\x74\x20\x3d\x20\ +\x69\x70\x2e\x64\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\ +\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x2e\x70\x72\x6f\x74\ +\x6f\x63\x6f\x6c\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x6f\x66\x66\ +\x73\x65\x74\x20\x3d\x20\x69\x70\x2e\x69\x68\x6c\x20\x2a\x20\x34\x3b\0\x20\x20\ +\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\x36\ +\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\ +\x20\x69\x70\x76\x36\x68\x64\x72\x20\x69\x70\x36\x20\x3d\x20\x7b\x7d\x3b\0\x20\ +\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\ +\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\ +\x76\x65\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x36\x2c\x20\x73\x69\ +\x7a\x65\x6f\x66\x28\x69\x70\x36\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\ +\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x73\x72\x63\x20\x3d\x20\x69\x70\x36\x2e\ +\x73\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\ +\x3e\x69\x6e\x36\x5f\x64\x73\x74\x20\x3d\x20\x69\x70\x36\x2e\x64\x61\x64\x64\ +\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\ +\x6f\x6c\x20\x3d\x20\x69\x70\x36\x2e\x6e\x65\x78\x74\x68\x64\x72\x3b\0\x20\x20\ +\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\x68\x64\x72\x5f\x74\x79\x70\x65\x29\ +\x20\x7b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x69\x70\x76\x36\x5f\x6f\ +\x70\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\x68\x64\x72\x20\x3d\x20\x7b\x7d\ +\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\ +\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\ +\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\ +\x74\x2c\x20\x26\x65\x78\x74\x5f\x68\x64\x72\x2c\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x69\x66\x20\x28\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\ +\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x52\x4f\x55\x54\x49\x4e\x47\x29\x20\ +\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\ +\x20\x69\x70\x76\x36\x5f\x72\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\x72\x74\ +\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\ +\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\ +\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\ +\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\x72\x74\ +\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\ +\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\ +\x28\x28\x65\x78\x74\x5f\x72\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\ +\x56\x36\x5f\x53\x52\x43\x52\x54\x5f\x54\x59\x50\x45\x5f\x32\x29\x20\x26\x26\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\x66\x66\x73\x65\ +\x74\x6f\x66\x28\x73\x74\x72\x75\x63\x74\x20\x72\x74\x32\x5f\x68\x64\x72\x2c\ +\x20\x61\x64\x64\x72\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\ +\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\ +\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\ +\x76\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x5f\x5f\x61\x74\x74\x72\x69\x62\x75\ +\x74\x65\x5f\x5f\x28\x28\x70\x61\x63\x6b\x65\x64\x29\x29\x20\x6f\x70\x74\x20\ +\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x6f\x70\ +\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\ +\x50\x41\x44\x31\x29\x20\x3f\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\ +\x2b\x20\x31\x20\x3e\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\ +\x65\x6e\x20\x2a\x20\x38\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\ +\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\ +\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\ +\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x2e\x74\x79\ +\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\x48\x41\x4f\x29\ +\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\ +\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\ +\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\ +\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\ +\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\x36\x5f\ +\x65\x78\x74\x5f\x73\x72\x63\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\ +\x65\x6e\x74\x65\x64\x20\x3d\x20\x74\x72\x75\x65\x3b\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x65\x78\ +\x74\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\x6e\x20\x2b\x20\x31\x29\x20\x2a\ +\x20\x38\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x70\x72\x6f\x74\ +\x6f\x63\x6f\x6c\x20\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x6e\x65\x78\x74\ +\x68\x64\x72\x3b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x75\x6e\x73\x69\x67\x6e\ +\x65\x64\x20\x69\x6e\x74\x20\x69\x20\x3d\x20\x30\x3b\x20\x69\x20\x3c\x20\x49\ +\x50\x36\x5f\x45\x58\x54\x45\x4e\x53\x49\x4f\x4e\x53\x5f\x43\x4f\x55\x4e\x54\ +\x3b\x20\x2b\x2b\x69\x29\x20\x7b\0\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\ +\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x21\x3d\x20\x30\x20\x26\x26\x20\x21\x69\ +\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x29\ +\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\ +\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x54\ +\x43\x50\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\ +\x66\x6f\x2d\x3e\x69\x73\x5f\x74\x63\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x74\x63\x70\x68\ +\x64\x72\x20\x74\x63\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\ +\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\ +\x28\x73\x6b\x62\x2c\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x74\ +\x63\x70\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\x63\x70\x29\x2c\0\x20\x20\x20\ +\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\ +\x5f\x69\x70\x76\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\ +\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x74\x63\x70\ +\x20\x26\x26\0\x20\x20\x20\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\ +\x6d\x63\x70\x79\x28\x26\x72\x73\x73\x5f\x69\x6e\x70\x75\x74\x5b\x2a\x62\x79\ +\x74\x65\x73\x5f\x77\x72\x69\x74\x74\x65\x6e\x5d\x2c\x20\x70\x74\x72\x2c\x20\ +\x73\x69\x7a\x65\x29\x3b\0\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\ +\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\ +\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\ +\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\ +\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\ +\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\ +\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\ +\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x75\x64\ +\x70\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\ +\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\ +\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\ +\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x34\x29\x20\x7b\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\ +\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\x70\x65\x73\x20\ +\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\x53\x5f\x48\x41\ +\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x75\x64\ +\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ +\x74\x72\x75\x63\x74\x20\x75\x64\x70\x68\x64\x72\x20\x75\x64\x70\x20\x3d\x20\ +\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\ +\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\ +\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x6c\x34\x5f\ +\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x75\x64\x70\x2c\x20\x73\x69\x7a\x65\x6f\ +\x66\x28\x75\x64\x70\x29\x2c\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x62\x79\x74\ +\x65\x20\x3d\x20\x30\x3b\x20\x62\x79\x74\x65\x20\x3c\x20\x48\x41\x53\x48\x5f\ +\x43\x41\x4c\x43\x55\x4c\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\x46\x45\x52\x5f\ +\x53\x49\x5a\x45\x3b\x20\x62\x79\x74\x65\x2b\x2b\x29\x20\x7b\0\x20\x20\x20\x20\ +\x5f\x5f\x75\x33\x32\x20\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\ +\x69\x74\x73\x20\x3d\x20\x6b\x65\x79\x2d\x3e\x6c\x65\x66\x74\x6d\x6f\x73\x74\ +\x5f\x33\x32\x5f\x62\x69\x74\x73\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\ +\x75\x38\x20\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\x65\x20\x3d\x20\x69\x6e\x70\ +\x75\x74\x5b\x62\x79\x74\x65\x5d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x69\x66\x20\x28\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\x65\x20\x26\x20\ +\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x5f\x5f\x75\x38\x20\x6b\x65\x79\x5f\x62\x79\x74\x65\x20\x3d\x20\x6b\x65\x79\ +\x2d\x3e\x6e\x65\x78\x74\x5f\x62\x79\x74\x65\x5b\x62\x79\x74\x65\x5d\x3b\0\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x28\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\x74\x73\x20\x3c\ +\x3c\x20\x31\x29\x20\x7c\x20\x28\x28\x6b\x65\x79\x5f\x62\x79\x74\x65\x20\x26\ +\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x3e\x3e\x20\x37\x29\x3b\0\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\x20\x74\x61\x62\ +\x6c\x65\x5f\x69\x64\x78\x20\x3d\x20\x68\x61\x73\x68\x20\x25\x20\x63\x6f\x6e\ +\x66\x69\x67\x2d\x3e\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x73\x5f\x6c\ +\x65\x6e\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x71\x75\x65\x75\ +\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\ +\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\ +\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\x65\x2c\0\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x71\x75\x65\x75\x65\ +\x29\x20\x7b\0\x7d\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x72\x65\x74\x75\x72\x6e\x20\x2a\x71\x75\x65\x75\x65\x3b\0\x63\x68\x61\ +\x72\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x6d\x61\x70\x73\0\x6c\x69\x63\x65\ +\x6e\x73\x65\0\x62\x70\x66\x5f\x66\x6c\x6f\x77\x5f\x6b\x65\x79\x73\0\x62\x70\ +\x66\x5f\x73\x6f\x63\x6b\0\0\0\0\x9f\xeb\x01\0\x20\0\0\0\0\0\0\0\x14\0\0\0\x14\ +\0\0\0\x6c\x0c\0\0\x80\x0c\0\0\0\0\0\0\x08\0\0\0\x30\x02\0\0\x01\0\0\0\0\0\0\0\ +\x26\0\0\0\x10\0\0\0\x30\x02\0\0\xc6\0\0\0\0\0\0\0\x37\x02\0\0\x60\x02\0\0\0\ +\x68\x08\0\x10\0\0\0\x37\x02\0\0\x91\x02\0\0\x0b\x80\x08\0\x20\0\0\0\x37\x02\0\ +\0\0\0\0\0\0\0\0\0\x28\0\0\0\x37\x02\0\0\xa4\x02\0\0\x0e\x8c\x08\0\x50\0\0\0\ +\x37\x02\0\0\xe9\x02\0\0\x0b\x90\x08\0\x78\0\0\0\x37\x02\0\0\x29\x03\0\0\x10\ +\x98\x08\0\x80\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x88\0\0\0\x37\x02\0\0\x29\x03\ +\0\0\x10\x98\x08\0\x90\0\0\0\x37\x02\0\0\x42\x03\0\0\x16\x9c\x08\0\x98\0\0\0\ +\x37\x02\0\0\x42\x03\0\0\x0d\x9c\x08\0\xb0\0\0\0\x37\x02\0\0\x63\x03\0\0\x0a\ +\x10\x06\0\xd8\0\0\0\x37\x02\0\0\x9a\x03\0\0\x1f\x1c\x06\0\x30\x01\0\0\x37\x02\ +\0\0\xca\x03\0\0\x0f\xac\x04\0\x38\x01\0\0\x37\x02\0\0\xe3\x03\0\0\x0c\x2c\x04\ +\0\x48\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x50\x01\0\0\x37\x02\0\0\xf7\x03\0\0\ +\x0b\x38\x04\0\x78\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\0\x88\x01\0\0\ +\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\0\x98\x01\0\0\x37\x02\0\0\x4c\x04\0\0\x0d\ +\x50\x04\0\xb0\x01\0\0\x37\x02\0\0\x4c\x04\0\0\x05\x50\x04\0\xd0\x01\0\0\x37\ +\x02\0\0\0\0\0\0\0\0\0\0\xd8\x01\0\0\x37\x02\0\0\x6a\x04\0\0\x0f\x64\x04\0\xf8\ +\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x7c\x04\0\x08\x02\0\0\x37\x02\0\0\x3d\x04\ +\0\0\x09\x7c\x04\0\x10\x02\0\0\x37\x02\0\0\xb4\x04\0\0\x0c\x8c\x04\0\x18\x02\0\ +\0\x37\x02\0\0\xc4\x04\0\0\x09\xc8\x04\0\x38\x02\0\0\x37\x02\0\0\xe0\x04\0\0\ +\x17\xe0\x04\0\x48\x02\0\0\x37\x02\0\0\xfb\x04\0\0\x16\xe8\x04\0\x68\x02\0\0\ +\x37\x02\0\0\xe0\x04\0\0\x17\xe0\x04\0\x70\x02\0\0\x37\x02\0\0\x19\x05\0\0\x0f\ +\xec\x04\0\x98\x02\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xa8\x02\0\0\x37\ +\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xb0\x02\0\0\x37\x02\0\0\x6f\x05\0\0\x22\x0c\ +\x05\0\xb8\x02\0\0\x37\x02\0\0\x6f\x05\0\0\x39\x0c\x05\0\xc8\x02\0\0\x37\x02\0\ +\0\x6f\x05\0\0\x20\x0c\x05\0\xd8\x02\0\0\x37\x02\0\0\xbd\x05\0\0\x1b\x04\x05\0\ +\xe0\x02\0\0\x37\x02\0\0\xbd\x05\0\0\x16\x04\x05\0\xe8\x02\0\0\x37\x02\0\0\xde\ +\x05\0\0\x1b\x08\x05\0\xf0\x02\0\0\x37\x02\0\0\xde\x05\0\0\x16\x08\x05\0\xf8\ +\x02\0\0\x37\x02\0\0\xff\x05\0\0\x1a\x14\x05\0\0\x03\0\0\x37\x02\0\0\x22\x06\0\ +\0\x18\x18\x05\0\x08\x03\0\0\x37\x02\0\0\x22\x06\0\0\x1c\x18\x05\0\x18\x03\0\0\ +\x37\x02\0\0\x6f\x05\0\0\x1d\x0c\x05\0\x30\x03\0\0\x37\x02\0\0\x42\x06\0\0\x17\ +\x20\x05\0\x40\x03\0\0\x37\x02\0\0\x5d\x06\0\0\x18\x28\x05\0\x70\x03\0\0\x37\ +\x02\0\0\x42\x06\0\0\x17\x20\x05\0\x78\x03\0\0\x37\x02\0\0\x7e\x06\0\0\x0f\x2c\ +\x05\0\xa0\x03\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\xb0\x03\0\0\x37\x02\0\ +\0\x5c\x05\0\0\x0d\x34\x05\0\xc0\x03\0\0\x37\x02\0\0\xc3\x06\0\0\x1d\x44\x05\0\ +\0\x04\0\0\x37\x02\0\0\xe6\x06\0\0\x1d\x48\x05\0\x40\x04\0\0\x37\x02\0\0\x09\ +\x07\0\0\x1b\x50\x05\0\x48\x04\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\x90\ +\x04\0\0\x37\x02\0\0\x44\x07\0\0\x19\xc4\x02\0\xf8\x04\0\0\x37\x02\0\0\0\0\0\0\ +\0\0\0\0\0\x05\0\0\x37\x02\0\0\x6a\x07\0\0\x0f\xd4\x02\0\x28\x05\0\0\x37\x02\0\ +\0\x5c\x05\0\0\x0d\xdc\x02\0\x38\x05\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xdc\x02\0\ +\x40\x05\0\0\x37\x02\0\0\xaf\x07\0\0\x0d\xec\x02\0\x68\x05\0\0\x37\x02\0\0\xde\ +\x07\0\0\x20\xf0\x02\0\x90\x05\0\0\x37\x02\0\0\x0a\x08\0\0\x13\xf8\x02\0\xb0\ +\x05\0\0\x37\x02\0\0\x52\x08\0\0\x11\0\x03\0\xc0\x05\0\0\x37\x02\0\0\x52\x08\0\ +\0\x11\0\x03\0\xc8\x05\0\0\x37\x02\0\0\x69\x08\0\0\x19\x10\x03\0\xd0\x05\0\0\ +\x37\x02\0\0\x69\x08\0\0\x34\x10\x03\0\xf8\x05\0\0\x37\x02\0\0\x9f\x08\0\0\x15\ +\x24\x03\0\x08\x06\0\0\x37\x02\0\0\xe0\x08\0\0\x17\x20\x03\0\x30\x06\0\0\x37\ +\x02\0\0\x17\x09\0\0\x15\x30\x03\0\x40\x06\0\0\x37\x02\0\0\x17\x09\0\0\x15\x30\ +\x03\0\x48\x06\0\0\x37\x02\0\0\x32\x09\0\0\x27\x40\x03\0\x70\x06\0\0\x37\x02\0\ +\0\x5d\x09\0\0\x27\x5c\x03\0\x80\x06\0\0\x37\x02\0\0\x8d\x09\0\0\x1c\xc0\x03\0\ +\x88\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x20\xcc\x03\0\x98\x06\0\0\x37\x02\0\0\xc9\ +\x09\0\0\x2f\xcc\x03\0\xa0\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x36\xcc\x03\0\xa8\ +\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x15\xcc\x03\0\x10\x07\0\0\x37\x02\0\0\x05\x0a\ +\0\0\x43\x70\x03\0\x30\x07\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x38\x07\0\0\x37\x02\ +\0\0\x05\x0a\0\0\x17\x70\x03\0\x60\x07\0\0\x37\x02\0\0\x17\x09\0\0\x15\x78\x03\ +\0\x70\x07\0\0\x37\x02\0\0\x17\x09\0\0\x15\x78\x03\0\x78\x07\0\0\x37\x02\0\0\ +\x55\x0a\0\0\x19\x88\x03\0\x80\x07\0\0\x37\x02\0\0\x55\x0a\0\0\x15\x88\x03\0\ +\x88\x07\0\0\x37\x02\0\0\x85\x0a\0\0\x19\x90\x03\0\x90\x07\0\0\x37\x02\0\0\xb5\ +\x0a\0\0\x1b\x8c\x03\0\xc0\x07\0\0\x37\x02\0\0\xf0\x0a\0\0\x19\xa0\x03\0\xd0\ +\x07\0\0\x37\x02\0\0\xf0\x0a\0\0\x19\xa0\x03\0\xd8\x07\0\0\x37\x02\0\0\x0f\x0b\ +\0\0\x2b\xb0\x03\0\xf8\x07\0\0\x37\x02\0\0\x8d\x09\0\0\x1f\xc0\x03\0\x18\x08\0\ +\0\x37\x02\0\0\x3e\x0b\0\0\x21\xe0\x03\0\x30\x08\0\0\x37\x02\0\0\x66\x0b\0\0\ +\x20\xf0\x03\0\x38\x08\0\0\x37\x02\0\0\x66\x0b\0\0\x2c\xf0\x03\0\x48\x08\0\0\ +\x37\x02\0\0\x66\x0b\0\0\x14\xf0\x03\0\x50\x08\0\0\x37\x02\0\0\x96\x0b\0\0\x20\ +\xec\x03\0\x58\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\xa0\x08\0\0\x37\ +\x02\0\0\xbe\x0b\0\0\x38\xcc\x02\0\xc0\x08\0\0\x37\x02\0\0\xbe\x0b\0\0\x05\xcc\ +\x02\0\xd0\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\xe8\x08\0\0\x37\x02\0\ +\0\x2c\x07\0\0\x05\x3c\x02\0\0\x09\0\0\x37\x02\0\0\xfc\x0b\0\0\x15\x74\x05\0\ +\x10\x09\0\0\x37\x02\0\0\xfc\x0b\0\0\x1a\x74\x05\0\x28\x09\0\0\x37\x02\0\0\x30\ +\x0c\0\0\x0d\x78\x05\0\x48\x09\0\0\x37\x02\0\0\x5a\x0c\0\0\x1a\x7c\x05\0\x58\ +\x09\0\0\x37\x02\0\0\x78\x0c\0\0\x1b\x84\x05\0\x78\x09\0\0\x37\x02\0\0\x5a\x0c\ +\0\0\x1a\x7c\x05\0\x80\x09\0\0\x37\x02\0\0\x9c\x0c\0\0\x13\x88\x05\0\xa0\x09\0\ +\0\x37\x02\0\0\x52\x08\0\0\x11\x90\x05\0\xb0\x09\0\0\x37\x02\0\0\x52\x08\0\0\ +\x11\x90\x05\0\xb8\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xd8\x09\0\0\x37\x02\0\0\ +\xed\x0c\0\0\x15\x38\x06\0\xe0\x09\0\0\x37\x02\0\0\xed\x0c\0\0\x09\x38\x06\0\ +\xe8\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x38\x0a\0\0\x37\x02\0\0\x0c\x0d\0\0\ +\x19\x3c\x06\0\x40\x0a\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\x3c\x06\0\x60\x0a\0\0\ +\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xa8\x0a\0\0\x37\x02\0\0\x6b\x0d\0\0\x1c\ +\xd4\x06\0\xb0\x0a\0\0\x37\x02\0\0\x6b\x0d\0\0\x10\xd4\x06\0\xb8\x0a\0\0\x37\ +\x02\0\0\0\0\0\0\0\0\0\0\x08\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\x19\xd8\x06\0\x10\ +\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\xd8\x06\0\x30\x0b\0\0\x37\x02\0\0\x91\x0d\ +\0\0\x2d\xe4\x06\0\x40\x0b\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\xe4\x06\0\x50\x0b\0\ +\0\x37\x02\0\0\x91\x0d\0\0\x2d\xe4\x06\0\x80\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\ +\x2d\x10\x07\0\x90\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x10\x07\0\xa0\x0b\0\0\ +\x37\x02\0\0\xc0\x0d\0\0\x2d\x10\x07\0\xc8\x0b\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\ +\xa4\x01\0\x90\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x78\x06\0\x98\x0c\0\0\x37\ +\x02\0\0\xef\x0d\0\0\x27\x78\x06\0\xc0\x0c\0\0\x37\x02\0\0\x18\x0e\0\0\x27\xb4\ +\x06\0\xc8\x0c\0\0\x37\x02\0\0\x18\x0e\0\0\x14\xb4\x06\0\xd0\x0c\0\0\x37\x02\0\ +\0\x2e\x0d\0\0\x05\xa4\x01\0\xe0\x0c\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\ +\xf8\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x54\x07\0\0\x0d\0\0\x37\x02\0\0\xef\ +\x0d\0\0\x27\x54\x07\0\x20\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\x60\x07\0\x30\ +\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\x60\x07\0\x40\x0d\0\0\x37\x02\0\0\x91\x0d\ +\0\0\x2d\x60\x07\0\x70\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\x8c\x07\0\x80\x0d\0\ +\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x8c\x07\0\x90\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\ +\x2d\x8c\x07\0\xb8\x0d\0\0\x37\x02\0\0\x61\x0e\0\0\x27\xd8\x07\0\xc8\x0d\0\0\ +\x37\x02\0\0\x61\x0e\0\0\x14\xd8\x07\0\xd0\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\ +\xdc\x07\0\xe0\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\xdc\x07\0\xf0\x0d\0\0\x37\ +\x02\0\0\x91\x0d\0\0\x2d\xdc\x07\0\x20\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\ +\x01\0\x70\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x17\xa4\x01\0\x80\x0e\0\0\x37\x02\0\ +\0\xc0\x0d\0\0\x2d\x08\x08\0\x98\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\ +\xe0\x0e\0\0\x37\x02\0\0\xaa\x0e\0\0\x1a\xac\x05\0\xf0\x0e\0\0\x37\x02\0\0\xc8\ +\x0e\0\0\x1b\xb4\x05\0\0\x0f\0\0\x37\x02\0\0\xaa\x0e\0\0\x1a\xac\x05\0\x08\x0f\ +\0\0\x37\x02\0\0\xec\x0e\0\0\x13\xb8\x05\0\x28\x0f\0\0\x37\x02\0\0\x52\x08\0\0\ +\x11\xc0\x05\0\x38\x0f\0\0\x37\x02\0\0\x52\x08\0\0\x11\xc0\x05\0\x48\x0f\0\0\ +\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\x80\x0f\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\ +\xa4\x01\0\x90\x0f\0\0\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\x01\0\x98\x0f\0\0\x37\ +\x02\0\0\x7f\x0f\0\0\x23\xd0\x01\0\xb0\x0f\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xb8\ +\x0f\0\0\x37\x02\0\0\xb3\x0f\0\0\x1b\xe0\x01\0\xd8\x0f\0\0\x37\x02\0\0\xda\x0f\ +\0\0\x11\xf4\x01\0\xf0\x0f\0\0\x37\x02\0\0\x03\x10\0\0\x19\xe4\x01\0\x08\x10\0\ +\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x20\x10\0\0\x37\x02\0\0\x31\x10\0\0\ +\x2d\x08\x02\0\x28\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x68\x10\0\0\ +\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x70\x10\0\0\x37\x02\0\0\x31\x10\0\0\x2d\ +\x08\x02\0\x78\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xa0\x10\0\0\x37\ +\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xc0\x10\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\ +\x02\0\xc8\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xf0\x10\0\0\x37\x02\0\ +\0\x31\x10\0\0\x27\x08\x02\0\x10\x11\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\ +\x18\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x58\x11\0\0\x37\x02\0\0\x31\ +\x10\0\0\x27\x08\x02\0\x60\x11\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x68\ +\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xa8\x11\0\0\x37\x02\0\0\x31\x10\ +\0\0\x27\x08\x02\0\xb0\x11\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\xb8\x11\0\ +\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xf8\x11\0\0\x37\x02\0\0\x31\x10\0\0\ +\x27\x08\x02\0\0\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x08\x12\0\0\x37\ +\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x30\x12\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\ +\x02\0\x38\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x40\x12\0\0\x37\x02\0\ +\0\x3d\x0f\0\0\x3d\xdc\x01\0\x50\x12\0\0\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\x01\0\ +\x60\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x2e\xb0\x08\0\x80\x12\0\0\x37\x02\0\0\x7d\ +\x10\0\0\x24\xb0\x08\0\x98\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x13\xb0\x08\0\xa8\ +\x12\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xb0\x12\0\0\x37\x02\0\0\xbc\x10\0\0\x15\ +\xbc\x08\0\xc8\x12\0\0\x37\x02\0\0\x04\x11\0\0\x11\xc8\x08\0\xd0\x12\0\0\x37\ +\x02\0\0\0\0\0\0\0\0\0\0\xf0\x12\0\0\x37\x02\0\0\x1d\x11\0\0\x01\xec\x08\0\x08\ +\x13\0\0\x37\x02\0\0\x1f\x11\0\0\x18\xcc\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x94\x01\0\0\0\0\x03\0\xf0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x75\x01\0\0\0\0\x03\ +\0\xa8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcd\x01\0\0\0\0\x03\0\xd0\x12\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\xbd\0\0\0\0\0\x03\0\xc8\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\ +\x01\0\0\0\0\x03\0\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\x01\0\0\0\0\x03\0\ +\x28\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x24\x01\0\0\0\0\x03\0\xd8\x09\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x2c\x01\0\0\0\0\x03\0\xd8\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf7\ +\x01\0\0\0\0\x03\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x30\x02\0\0\0\0\x03\0\xe8\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe6\x01\0\0\0\0\x03\0\x88\x04\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\xc5\x01\0\0\0\0\x03\0\xf0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\x01\ +\0\0\0\0\x03\0\x60\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb5\x01\0\0\0\0\x03\0\x18\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x01\0\0\0\0\x03\0\x30\x08\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x84\x01\0\0\0\0\x03\0\x28\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x8c\x01\ +\0\0\0\0\x03\0\x10\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x28\x02\0\0\0\0\x03\0\x80\ +\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xde\x01\0\0\0\0\x03\0\xf8\x06\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x0c\x01\0\0\0\0\x03\0\xf0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4c\x01\ +\0\0\0\0\x03\0\xd0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\x01\0\0\0\0\x03\0\x98\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x03\0\xd8\x08\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\x20\x02\0\0\0\0\x03\0\xf0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfc\0\0\0\ +\0\0\x03\0\xd8\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x44\x01\0\0\0\0\x03\0\xb8\x09\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\xf4\0\0\0\0\0\x03\0\xa8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\xad\x01\0\0\0\0\x03\0\x90\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd6\x01\0\0\0\0\ +\x03\0\x60\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x02\0\0\0\0\x03\0\x88\x0f\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x18\x02\0\0\0\0\x03\0\xf8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\xa5\x01\0\0\0\0\x03\0\x68\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5c\x01\0\0\0\0\ +\x03\0\x80\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1c\x01\0\0\0\0\x03\0\xb8\x0b\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xcc\0\0\0\0\0\x03\0\xc8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xec\0\0\0\0\0\x03\0\xc0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3c\x01\0\0\0\0\x03\0\ +\xc0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x02\0\0\0\0\x03\0\xb8\x0d\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x7c\x01\0\0\0\0\x03\0\x58\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x34\ +\x01\0\0\0\0\x03\0\x70\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe4\0\0\0\0\0\x03\0\xa8\ +\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9d\x01\0\0\0\0\x03\0\x08\x0e\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x54\x01\0\0\0\0\x03\0\x20\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdc\0\0\ +\0\0\0\x03\0\x48\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\0\0\0\0\0\x03\0\x80\x0f\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\x07\x02\0\0\0\0\x03\0\xa8\x0f\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\xee\x01\0\0\0\0\x03\0\x60\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6c\x01\0\0\0\ +\0\x03\0\x08\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x59\0\0\0\x12\0\x03\0\0\0\0\0\0\0\ +\0\0\x18\x13\0\0\0\0\0\0\x3e\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\ +\0\x01\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x86\0\0\0\x11\0\ +\x05\0\x50\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x7d\0\0\0\x11\0\x06\0\0\0\0\0\0\0\0\ +\0\x07\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x33\0\0\0\x50\0\0\0\0\0\0\0\ +\x01\0\0\0\x34\0\0\0\xb0\x12\0\0\0\0\0\0\x01\0\0\0\x35\0\0\0\x20\x05\0\0\0\0\0\ +\0\x04\0\0\0\x33\0\0\0\x2c\x05\0\0\0\0\0\0\x04\0\0\0\x34\0\0\0\x38\x05\0\0\0\0\ +\0\0\x04\0\0\0\x35\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\0\x36\0\0\0\x2c\0\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x40\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\0\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x60\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\0\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\0\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xa0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\0\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xc0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\0\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xe0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\0\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x01\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x01\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x40\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x01\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x60\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x01\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x80\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x01\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x01\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x01\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\ +\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\ +\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x30\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x50\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x70\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x02\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x90\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x02\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xb0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x02\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xd0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x02\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xf0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x03\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x10\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x03\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x30\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x03\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x50\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x03\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x70\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x03\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x90\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x03\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x03\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x03\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x04\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x04\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\ +\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x60\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x80\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\xa0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x04\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\xc0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x04\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xe0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x04\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x05\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x05\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x40\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x05\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x60\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x05\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x80\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x05\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\xa0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x05\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\xc0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x05\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x05\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x06\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x06\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x06\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\ +\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x90\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xb0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\xd0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x06\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\xf0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x07\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x10\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x07\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x30\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x07\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x50\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x07\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x70\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x07\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x90\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x07\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xb0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x07\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\xd0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x07\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\xf0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x08\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x10\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x08\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x30\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x08\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x08\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x08\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\ +\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xc0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xe0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x20\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x09\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x40\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x09\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x60\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x09\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x80\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x09\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xa0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x09\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xc0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x09\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xe0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x09\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0a\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x20\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0a\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x40\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0a\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0a\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0a\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0a\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\ +\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xf0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x10\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x30\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x50\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0b\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x70\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0b\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x90\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0b\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xb0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0b\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xd0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x0b\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xf0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0c\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x10\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0c\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x30\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0c\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x50\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0c\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x70\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0c\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x39\x3a\x3b\ +\x3c\x3d\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\ +\x69\x74\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\x74\0\x2e\x72\x65\x6c\x2e\x42\ +\x54\x46\x2e\x65\x78\x74\0\x2e\x72\x65\x6c\x73\x6f\x63\x6b\x65\x74\0\x2e\x6d\ +\x61\x70\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\x6e\x66\ +\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\ +\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\ +\x64\x64\x72\x73\x69\x67\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x74\x61\x70\x5f\ +\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\ +\x5f\x74\x61\x62\x6c\x65\0\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\ +\x61\x62\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\x42\x42\x30\x5f\x39\0\x4c\x42\ +\x42\x30\x5f\x39\x39\0\x4c\x42\x42\x30\x5f\x37\x39\0\x4c\x42\x42\x30\x5f\x34\ +\x39\0\x4c\x42\x42\x30\x5f\x39\x38\0\x4c\x42\x42\x30\x5f\x38\x38\0\x4c\x42\x42\ +\x30\x5f\x37\x38\0\x4c\x42\x42\x30\x5f\x36\x38\0\x4c\x42\x42\x30\x5f\x35\x38\0\ +\x4c\x42\x42\x30\x5f\x34\x38\0\x4c\x42\x42\x30\x5f\x33\x38\0\x4c\x42\x42\x30\ +\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x37\x37\0\x4c\x42\x42\x30\x5f\x35\x37\0\x4c\ +\x42\x42\x30\x5f\x31\x37\0\x4c\x42\x42\x30\x5f\x38\x36\0\x4c\x42\x42\x30\x5f\ +\x36\x36\0\x4c\x42\x42\x30\x5f\x35\x36\0\x4c\x42\x42\x30\x5f\x34\x36\0\x4c\x42\ +\x42\x30\x5f\x39\x35\0\x4c\x42\x42\x30\x5f\x37\x35\0\x4c\x42\x42\x30\x5f\x34\ +\x35\0\x4c\x42\x42\x30\x5f\x31\x30\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\x42\ +\x30\x5f\x38\x34\0\x4c\x42\x42\x30\x5f\x34\x34\0\x4c\x42\x42\x30\x5f\x33\x34\0\ +\x4c\x42\x42\x30\x5f\x31\x30\x34\0\x4c\x42\x42\x30\x5f\x39\x33\0\x4c\x42\x42\ +\x30\x5f\x37\x33\0\x4c\x42\x42\x30\x5f\x36\x33\0\x4c\x42\x42\x30\x5f\x34\x33\0\ +\x4c\x42\x42\x30\x5f\x33\x33\0\x4c\x42\x42\x30\x5f\x32\x33\0\x4c\x42\x42\x30\ +\x5f\x31\x30\x33\0\x4c\x42\x42\x30\x5f\x36\x32\0\x4c\x42\x42\x30\x5f\x34\x32\0\ +\x4c\x42\x42\x30\x5f\x32\x32\0\x4c\x42\x42\x30\x5f\x31\x30\x32\0\x4c\x42\x42\ +\x30\x5f\x35\x31\0\x4c\x42\x42\x30\x5f\x31\x31\0\x4c\x42\x42\x30\x5f\x31\x30\ +\x31\0\x4c\x42\x42\x30\x5f\x39\x30\0\x4c\x42\x42\x30\x5f\x38\x30\0\x4c\x42\x42\ +\x30\x5f\x35\x30\0\x4c\x42\x42\x30\x5f\x34\x30\0\x4c\x42\x42\x30\x5f\x32\x30\0\ +\x4c\x42\x42\x30\x5f\x31\x30\x30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x75\x49\0\0\0\0\0\0\x41\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\x31\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\ -\0\x20\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2d\0\ -\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x3c\0\0\0\0\0\0\x30\0\0\0\ -\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x38\0\0\0\x01\ -\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x60\x13\0\0\0\0\0\0\x78\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7e\0\0\0\x01\0\0\0\x03\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\xe0\x13\0\0\0\0\0\0\xe1\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xb4\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\xc0\x3c\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x0c\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\ -\x10\0\0\0\0\0\0\0\x24\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\x2a\ -\0\0\0\0\0\0\xa0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\x20\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3d\0\0\0\0\0\0\ -\x70\x0c\0\0\0\0\0\0\x0c\0\0\0\x09\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\ -\x6f\0\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\x70\x49\0\0\0\0\0\ -\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\ -\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x37\0\0\0\0\0\0\x28\x05\0\0\0\ -\0\0\0\x01\0\0\0\x32\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0"; +\0\0\0\0\0\xa4\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6d\x49\0\0\0\0\ +\0\0\x41\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\ +\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x31\0\0\0\x01\0\0\0\ +\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x18\x13\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2d\0\0\0\x09\0\0\0\x40\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x88\x3c\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x0c\0\0\0\x03\0\0\0\ +\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x38\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x58\x13\0\0\0\0\0\0\x78\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x7e\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xd0\x13\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\xb8\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\ +\0\0\xe1\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb4\ +\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\x3c\0\0\0\0\0\0\x40\0\0\ +\0\0\0\0\0\x0c\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x24\0\0\0\ +\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbc\x2a\0\0\0\0\0\0\xa0\x0c\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\x09\0\0\0\x40\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf8\x3c\0\0\0\0\0\0\x70\x0c\0\0\0\0\0\0\x0c\0\0\ +\0\x09\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x6f\0\0\0\x03\x4c\xff\x6f\0\0\ +\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\x68\x49\0\0\0\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\x60\x37\0\0\0\0\0\0\x28\x05\0\0\0\0\0\0\x01\0\0\0\x32\0\0\0\x08\ +\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0"; *sz = sizeof(data) - 1; return (const void *)data; diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c index fa55e1fcb5..77434435ac 100644 --- a/tools/ebpf/rss.bpf.c +++ b/tools/ebpf/rss.bpf.c @@ -567,7 +567,7 @@ int tun_rss_steering_prog(struct __sk_buff *skb) return config->default_queue; } - return -1; + return 0; } char _license[] SEC("license") = "GPL v2"; From f5c69e7ab2507553d1a2780e41d924c32dec3c44 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:01:00 +0900 Subject: [PATCH 1117/2884] ebpf: Refactor tun_rss_steering_prog() This saves branches and makes later BPF program changes easier. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- tools/ebpf/rss.bpf.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c index 77434435ac..c989cb3cd8 100644 --- a/tools/ebpf/rss.bpf.c +++ b/tools/ebpf/rss.bpf.c @@ -547,27 +547,23 @@ int tun_rss_steering_prog(struct __sk_buff *skb) config = bpf_map_lookup_elem(&tap_rss_map_configurations, &key); toe = bpf_map_lookup_elem(&tap_rss_map_toeplitz_key, &key); - if (config && toe) { - if (!config->redirect) { - return config->default_queue; - } - - if (calculate_rss_hash(skb, config, toe, &hash)) { - __u32 table_idx = hash % config->indirections_len; - __u16 *queue = 0; - - queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table, - &table_idx); - - if (queue) { - return *queue; - } - } - - return config->default_queue; + if (!config || !toe) { + return 0; } - return 0; + if (config->redirect && calculate_rss_hash(skb, config, toe, &hash)) { + __u32 table_idx = hash % config->indirections_len; + __u16 *queue = 0; + + queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table, + &table_idx); + + if (queue) { + return *queue; + } + } + + return config->default_queue; } char _license[] SEC("license") = "GPL v2"; From 6832aa80a193d04aaf8e89a64e1825a158290057 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Apr 2024 16:01:01 +0900 Subject: [PATCH 1118/2884] ebpf: Add a separate target for skeleton This generalizes the rule to generate the skeleton and allows to add another. Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- tools/ebpf/Makefile.ebpf | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tools/ebpf/Makefile.ebpf b/tools/ebpf/Makefile.ebpf index 3391e7ce08..572ca5987a 100755 --- a/tools/ebpf/Makefile.ebpf +++ b/tools/ebpf/Makefile.ebpf @@ -1,23 +1,24 @@ -OBJS = rss.bpf.o +SKELETONS = rss.bpf.skeleton.h LLVM_STRIP ?= llvm-strip CLANG ?= clang INC_FLAGS = `$(CLANG) -print-file-name=include` EXTRA_CFLAGS ?= -O2 -g -target bpf -all: $(OBJS) +all: $(SKELETONS) .PHONY: clean clean: - rm -f $(OBJS) - rm -f rss.bpf.skeleton.h + rm -f $(SKELETONS) $(SKELETONS:%.skeleton.h=%.o) -$(OBJS): %.o:%.c +%.o: %.c $(CLANG) $(INC_FLAGS) \ -D__KERNEL__ -D__ASM_SYSREG_H \ -I../include $(LINUXINCLUDE) \ $(EXTRA_CFLAGS) -c $< -o $@ $(LLVM_STRIP) -g $@ - bpftool gen skeleton rss.bpf.o > rss.bpf.skeleton.h - cp rss.bpf.skeleton.h ../../ebpf/ + +%.skeleton.h: %.o + bpftool gen skeleton $< > $@ + cp $@ ../../ebpf/ From 2c3e4e2de699cd4d9f6c71f30a22d8f125cd6164 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 30 Apr 2024 13:53:33 +0300 Subject: [PATCH 1119/2884] virtio-net: drop too short packets early Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451 creates small packet (1 segment, len = 10 == n->guest_hdr_len), then destroys queue. "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates zero length/zero segment packet as there is nothing after guest header. qemu_sendv_packet_async() tries to send it. slirp discards it because it is smaller than Ethernet header, but returns 0 because tx hooks are supposed to return total length of data. 0 is propagated upwards and is interpreted as "packet has been sent" which is terrible because queue is being destroyed, nobody is waiting for TX to complete and assert it triggered. Fix is discard such empty packets instead of sending them. Length 1 packets will go via different codepath: virtqueue_push(q->tx_vq, elem, 0); virtio_notify(vdev, q->tx_vq); g_free(elem); and aren't problematic. Signed-off-by: Alexey Dobriyan Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 666a4e2a03..9c7e85caea 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2708,18 +2708,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) out_sg = elem->out_sg; if (out_num < 1) { virtio_error(vdev, "virtio-net header not in first element"); - virtqueue_detach_element(q->tx_vq, elem, 0); - g_free(elem); - return -EINVAL; + goto detach; } if (n->needs_vnet_hdr_swap) { if (iov_to_buf(out_sg, out_num, 0, &vhdr, sizeof(vhdr)) < sizeof(vhdr)) { virtio_error(vdev, "virtio-net header incorrect"); - virtqueue_detach_element(q->tx_vq, elem, 0); - g_free(elem); - return -EINVAL; + goto detach; } virtio_net_hdr_swap(vdev, &vhdr); sg2[0].iov_base = &vhdr; @@ -2747,6 +2743,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) n->guest_hdr_len, -1); out_num = sg_num; out_sg = sg; + + if (out_num < 1) { + virtio_error(vdev, "virtio-net nothing to send"); + goto detach; + } } ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), @@ -2767,6 +2768,11 @@ drop: } } return num_packets; + +detach: + virtqueue_detach_element(q->tx_vq, elem, 0); + g_free(elem); + return -EINVAL; } static void virtio_net_tx_timer(void *opaque); From dcab53611191f50cf4feabc1d8794d04afe53407 Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Mon, 22 Apr 2024 16:51:16 +0300 Subject: [PATCH 1120/2884] ebpf: Added traces back. Changed source set for eBPF to 'system'. There was an issue with Qemu build with "--disable-system". The traces could be generated and the build fails. The traces were 'cut out' for previous patches, and overall, the 'system' source set should be used like in pre-'eBPF blob' patches. Signed-off-by: Andrew Melnychenko Signed-off-by: Jason Wang --- ebpf/ebpf_rss.c | 7 +++++++ ebpf/trace.h | 1 + 2 files changed, 8 insertions(+) create mode 100644 ebpf/trace.h diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c index d102f3dd09..87f0714910 100644 --- a/ebpf/ebpf_rss.c +++ b/ebpf/ebpf_rss.c @@ -25,6 +25,8 @@ #include "ebpf/rss.bpf.skeleton.h" #include "ebpf/ebpf.h" +#include "trace.h" + void ebpf_rss_init(struct EBPFRSSContext *ctx) { if (ctx != NULL) { @@ -55,18 +57,21 @@ static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx) PROT_READ | PROT_WRITE, MAP_SHARED, ctx->map_configuration, 0); if (ctx->mmap_configuration == MAP_FAILED) { + trace_ebpf_error("eBPF RSS", "can not mmap eBPF configuration array"); return false; } ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(), PROT_READ | PROT_WRITE, MAP_SHARED, ctx->map_toeplitz_key, 0); if (ctx->mmap_toeplitz_key == MAP_FAILED) { + trace_ebpf_error("eBPF RSS", "can not mmap eBPF toeplitz key"); goto toeplitz_fail; } ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(), PROT_READ | PROT_WRITE, MAP_SHARED, ctx->map_indirections_table, 0); if (ctx->mmap_indirections_table == MAP_FAILED) { + trace_ebpf_error("eBPF RSS", "can not mmap eBPF indirection table"); goto indirection_fail; } @@ -108,12 +113,14 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx) rss_bpf_ctx = rss_bpf__open(); if (rss_bpf_ctx == NULL) { + trace_ebpf_error("eBPF RSS", "can not open eBPF RSS object"); goto error; } bpf_program__set_type(rss_bpf_ctx->progs.tun_rss_steering_prog, BPF_PROG_TYPE_SOCKET_FILTER); if (rss_bpf__load(rss_bpf_ctx)) { + trace_ebpf_error("eBPF RSS", "can not load RSS program"); goto error; } diff --git a/ebpf/trace.h b/ebpf/trace.h new file mode 100644 index 0000000000..abefc46ab1 --- /dev/null +++ b/ebpf/trace.h @@ -0,0 +1 @@ +#include "trace/trace-ebpf.h" From 7106121d26229f36ac0fb33e35bcb50513447582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 26 Jun 2023 23:12:01 +0200 Subject: [PATCH 1121/2884] target/riscv: Remove unused 'instmap.h' header in translate.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-Id: <20230626232007.8933-2-philmd@linaro.org> --- target/riscv/translate.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 2c27fd4ce1..32a453f686 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -28,7 +28,6 @@ #include "exec/log.h" #include "semihosting/semihost.h" -#include "instmap.h" #include "internals.h" #define HELPER_H "helper.h" From 14482b1360c34e1ccd3b9a6135aeff17ee3c8ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 26 Jun 2023 22:36:10 +0200 Subject: [PATCH 1122/2884] target/riscv: Restrict 'rv128' machine to TCG accelerator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only build for 32/64-bit hosts, so TCG is required for 128-bit targets. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-Id: <20230626232007.8933-5-philmd@linaro.org> --- target/riscv/cpu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index eb1a2e7d6d..0dad66ec96 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -591,6 +591,7 @@ static void rv64_veyron_v1_cpu_init(Object *obj) #endif } +#ifdef CONFIG_TCG static void rv128_base_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); @@ -612,6 +613,7 @@ static void rv128_base_cpu_init(Object *obj) set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); #endif } +#endif /* CONFIG_TCG */ static void rv64i_bare_cpu_init(Object *obj) { @@ -624,7 +626,9 @@ static void rv64e_bare_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; riscv_cpu_set_misa_ext(env, RVE); } -#else + +#else /* !TARGET_RISCV64 */ + static void rv32_base_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); @@ -2550,12 +2554,14 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), +#ifdef CONFIG_TCG DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), +#endif /* CONFIG_TCG */ DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64I, MXL_RV64, rv64i_bare_cpu_init), DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64E, MXL_RV64, rv64e_bare_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, MXL_RV64, rva22u64_profile_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, MXL_RV64, rva22s64_profile_cpu_init), -#endif +#endif /* TARGET_RISCV64 */ }; DEFINE_TYPES(riscv_cpu_type_infos) From 2dd31749900fa26d0457a2f95a9b760b6fcff17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Jun 2023 00:19:30 +0200 Subject: [PATCH 1123/2884] target/riscv: Restrict riscv_cpu_do_interrupt() to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit riscv_cpu_do_interrupt() is not reachable on user emulation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-Id: <20230626232007.8933-7-philmd@linaro.org> --- target/riscv/cpu.h | 5 +++-- target/riscv/cpu_helper.c | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 2d0c02c35b..648e640f22 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -484,7 +484,6 @@ extern const char * const riscv_int_regnamesh[]; extern const char * const riscv_fpr_regnames[]; const char *riscv_cpu_get_trap_name(target_ulong cause, bool async); -void riscv_cpu_do_interrupt(CPUState *cpu); int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, DumpState *s); int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, @@ -514,6 +513,7 @@ int riscv_cpu_max_xlen(RISCVCPUClass *mcc); bool riscv_cpu_option_set(const char *optname); #ifndef CONFIG_USER_ONLY +void riscv_cpu_do_interrupt(CPUState *cpu); void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename); void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, @@ -539,7 +539,8 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, void *rmw_fn_arg); RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); -#endif +#endif /* !CONFIG_USER_ONLY */ + void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); void riscv_translate_init(void); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8ad546a45a..0868357f1c 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1635,7 +1635,6 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env, return xinsn; } -#endif /* !CONFIG_USER_ONLY */ /* * Handle Traps @@ -1645,8 +1644,6 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env, */ void riscv_cpu_do_interrupt(CPUState *cs) { -#if !defined(CONFIG_USER_ONLY) - RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; bool write_gva = false; @@ -1842,6 +1839,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->two_stage_lookup = false; env->two_stage_indirect_lookup = false; -#endif - cs->exception_index = RISCV_EXCP_NONE; /* mark handled to qemu */ } + +#endif /* !CONFIG_USER_ONLY */ From c76b288d780dd46716cf64743b88e885c6456679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 29 May 2024 17:13:30 +0200 Subject: [PATCH 1124/2884] target/mips: Remove unused 'hw/misc/mips_itu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit e1152f8166 ("target/mips: Remove helpers accessing SAAR registers") this header is not needed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Jiaxun Yang Message-Id: <20240529155216.5574-1-philmd@linaro.org> --- target/mips/tcg/sysemu/cp0_helper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/mips/tcg/sysemu/cp0_helper.c b/target/mips/tcg/sysemu/cp0_helper.c index ded6c78e9a..79a5c833ce 100644 --- a/target/mips/tcg/sysemu/cp0_helper.c +++ b/target/mips/tcg/sysemu/cp0_helper.c @@ -28,7 +28,6 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "hw/misc/mips_itu.h" /* SMP helpers. */ From 2ad9d04492434b9143f52535cfd12aaae1cfd667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Apr 2024 12:31:44 +0200 Subject: [PATCH 1125/2884] target/arm: Replace sprintf() by snprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1, resulting in painful developper experience. Use snprintf() instead. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-Id: <20240411104340.6617-9-philmd@linaro.org> Signed-off-by: Richard Henderson --- target/arm/cpu64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 862d2b92fa..262a1d6c0b 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -437,7 +437,7 @@ void aarch64_add_sve_properties(Object *obj) for (vq = 1; vq <= ARM_MAX_VQ; ++vq) { char name[8]; - sprintf(name, "sve%d", vq * 128); + snprintf(name, sizeof(name), "sve%d", vq * 128); object_property_add(obj, name, "bool", cpu_arm_get_vq, cpu_arm_set_vq, NULL, &cpu->sve_vq); } @@ -462,7 +462,7 @@ void aarch64_add_sme_properties(Object *obj) for (vq = 1; vq <= ARM_MAX_VQ; vq <<= 1) { char name[8]; - sprintf(name, "sme%d", vq * 128); + snprintf(name, sizeof(name), "sme%d", vq * 128); object_property_add(obj, name, "bool", cpu_arm_get_vq, cpu_arm_set_vq, NULL, &cpu->sme_vq); } From a93b4061b0e2ff914be9476d74ca6a4f9a8926a1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:46 -0700 Subject: [PATCH 1126/2884] target/i386/kvm: Improve KVM_EXIT_NOTIFY warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-ID: <20240412073346.458116-28-richard.henderson@linaro.org> [PMD: Fixed typo reported by Peter Maydell] Signed-off-by: Philippe Mathieu-Daudé --- target/i386/kvm/kvm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6c864e4611..82ebddada1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5329,7 +5329,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) uint64_t code; int ret; bool ctx_invalid; - char str[256]; KVMState *state; switch (run->exit_reason) { @@ -5389,15 +5388,15 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_NOTIFY: ctx_invalid = !!(run->notify.flags & KVM_NOTIFY_CONTEXT_INVALID); state = KVM_STATE(current_accel()); - sprintf(str, "Encounter a notify exit with %svalid context in" - " guest. There can be possible misbehaves in guest." - " Please have a look.", ctx_invalid ? "in" : ""); if (ctx_invalid || state->notify_vmexit == NOTIFY_VMEXIT_OPTION_INTERNAL_ERROR) { - warn_report("KVM internal error: %s", str); + warn_report("KVM internal error: Encountered a notify exit " + "with invalid context in guest."); ret = -1; } else { - warn_report_once("KVM: %s", str); + warn_report_once("KVM: Encountered a notify exit with valid " + "context in guest. " + "The guest could be misbehaving."); ret = 0; } break; From 28d5bfc098e607d58fae0ba2fab47ccb26244dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Apr 2024 12:40:07 +0200 Subject: [PATCH 1127/2884] disas/m68k: Replace sprintf() by snprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1, resulting in painful developper experience. Use snprintf() instead. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-Id: <20240411104340.6617-2-philmd@linaro.org> Signed-off-by: Richard Henderson --- disas/m68k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disas/m68k.c b/disas/m68k.c index 1f16e295ab..800b4145ac 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -1000,7 +1000,7 @@ print_indexed (int basereg, /* Generate the text for the index register. Where this will be output is not yet determined. */ - sprintf (buf, "%s:%c%s", + snprintf(buf, sizeof(buf), "%s:%c%s", reg_names[(word >> 12) & 0xf], (word & 0x800) ? 'l' : 'w', scales[(word >> 9) & 3]); From c54c6a10884545cdc910a328fd298b316f97ff55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Apr 2024 12:32:14 +0200 Subject: [PATCH 1128/2884] disas/microblaze: Replace sprintf() by snprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1, resulting in painful developper experience. Use snprintf() instead. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Message-Id: <20240411104340.6617-3-philmd@linaro.org> Signed-off-by: Richard Henderson --- disas/microblaze.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index 0b89b9c4fa..49a4c0fd40 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -600,7 +600,8 @@ static char * get_field (long instr, long mask, unsigned short low) { char tmpstr[25]; - sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low)); + snprintf(tmpstr, sizeof(tmpstr), "%s%d", register_prefix, + (int)((instr & mask) >> low)); return(strdup(tmpstr)); } @@ -608,7 +609,8 @@ static char * get_field_imm (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW)); + snprintf(tmpstr, sizeof(tmpstr), "%d", + (short)((instr & IMM_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } @@ -616,7 +618,8 @@ static char * get_field_imm5 (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW)); + snprintf(tmpstr, sizeof(tmpstr), "%d", + (short)((instr & IMM5_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } @@ -624,7 +627,8 @@ static char * get_field_rfsl (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & RFSL_MASK) >> IMM_LOW)); + snprintf(tmpstr, sizeof(tmpstr), "%s%d", fsl_register_prefix, + (short)((instr & RFSL_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } @@ -632,7 +636,8 @@ static char * get_field_imm15 (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW)); + snprintf(tmpstr, sizeof(tmpstr), "%d", + (short)((instr & IMM15_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } @@ -641,7 +646,8 @@ static char * get_field_unsigned_imm (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW)); + snprintf(tmpstr, sizeof(tmpstr), "%d", + (int)((instr & IMM_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } #endif @@ -653,7 +659,8 @@ get_field_unsigned_imm (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%s%s", register_prefix, (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr"); + snprintf(tmpstr, sizeof(tmpstr), "%s%s", register_prefix, + (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr"); return(strdup(tmpstr)); } @@ -709,7 +716,7 @@ get_field_special(long instr, const struct op_code_struct *op) default : { if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { - sprintf(tmpstr, "%s%u", pvr_register_prefix, + snprintf(tmpstr, sizeof(tmpstr), "%s%u", pvr_register_prefix, (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ^ REG_PVR_MASK); return(strdup(tmpstr)); @@ -720,7 +727,7 @@ get_field_special(long instr, const struct op_code_struct *op) break; } - sprintf(tmpstr, "%s%s", register_prefix, spr); + snprintf(tmpstr, sizeof(tmpstr), "%s%s", register_prefix, spr); return(strdup(tmpstr)); } From 5837a76cd2e6fe6345a4c7dcecec58f23f42a3e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:20 -0700 Subject: [PATCH 1129/2884] util/hexdump: Remove b parameter from qemu_hexdump_line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require that the caller output the offset and increment bufptr. Use QEMU_HEXDUMP_LINE_BYTES in vhost_vdpa_dump_config instead of raw integer. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240412073346.458116-2-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/virtio/trace-events | 2 +- hw/virtio/vhost-vdpa.c | 4 ++-- include/qemu/cutils.h | 2 +- util/hexdump.c | 13 ++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 96632fd026..3cf84e04a7 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -50,7 +50,7 @@ vhost_vdpa_get_device_id(void *dev, uint32_t device_id) "dev: %p device_id %"PRI vhost_vdpa_reset_device(void *dev) "dev: %p" vhost_vdpa_get_vq_index(void *dev, int idx, int vq_idx) "dev: %p idx: %d vq idx: %d" vhost_vdpa_set_vring_enable_one(void *dev, unsigned i, int enable, int r) "dev: %p, idx: %u, enable: %u, r: %d" -vhost_vdpa_dump_config(void *dev, const char *line) "dev: %p %s" +vhost_vdpa_dump_config(void *dev, unsigned ofs, const char *line) "dev: %p 0x%04x: %s" vhost_vdpa_set_config(void *dev, uint32_t offset, uint32_t size, uint32_t flags) "dev: %p offset: %"PRIu32" size: %"PRIu32" flags: 0x%"PRIx32 vhost_vdpa_get_config(void *dev, void *config, uint32_t config_len) "dev: %p config: %p config_len: %"PRIu32 vhost_vdpa_suspend(void *dev) "dev: %p" diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index ed99ab8745..f3a86c1a8c 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -949,8 +949,8 @@ static void vhost_vdpa_dump_config(struct vhost_dev *dev, const uint8_t *config, for (b = 0; b < config_len; b += 16) { len = config_len - b; - qemu_hexdump_line(line, b, config, len, false); - trace_vhost_vdpa_dump_config(dev, line); + qemu_hexdump_line(line, config + b, len, false); + trace_vhost_vdpa_dump_config(dev, b, line); } } diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 741dade7cf..d7715f7a33 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -287,7 +287,7 @@ int parse_debug_env(const char *name, int max, int initial); */ #define QEMU_HEXDUMP_LINE_BYTES 16 /* Number of bytes to dump */ #define QEMU_HEXDUMP_LINE_LEN 75 /* Number of characters in line */ -void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, +void qemu_hexdump_line(char *line, const void *bufptr, unsigned int len, bool ascii); /* diff --git a/util/hexdump.c b/util/hexdump.c index 9921114b3c..7324e7b126 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, +void qemu_hexdump_line(char *line, const void *bufptr, unsigned int len, bool ascii) { const char *buf = bufptr; @@ -26,13 +26,12 @@ void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, len = QEMU_HEXDUMP_LINE_BYTES; } - line += snprintf(line, 6, "%04x:", b); for (i = 0; i < QEMU_HEXDUMP_LINE_BYTES; i++) { - if ((i % 4) == 0) { + if (i != 0 && (i % 4) == 0) { *line++ = ' '; } if (i < len) { - line += sprintf(line, " %02x", (unsigned char)buf[b + i]); + line += sprintf(line, " %02x", (unsigned char)buf[i]); } else { line += sprintf(line, " "); } @@ -40,7 +39,7 @@ void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, if (ascii) { *line++ = ' '; for (i = 0; i < len; i++) { - c = buf[b + i]; + c = buf[i]; if (c < ' ' || c > '~') { c = '.'; } @@ -58,8 +57,8 @@ void qemu_hexdump(FILE *fp, const char *prefix, for (b = 0; b < size; b += QEMU_HEXDUMP_LINE_BYTES) { len = size - b; - qemu_hexdump_line(line, b, bufptr, len, true); - fprintf(fp, "%s: %s\n", prefix, line); + qemu_hexdump_line(line, bufptr + b, len, true); + fprintf(fp, "%s: %04x: %s\n", prefix, b, line); } } From 13dfa93300285b88df96ca4366f499c5a137d5b2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:21 -0700 Subject: [PATCH 1130/2884] util/hexdump: Remove ascii parameter from qemu_hexdump_line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out asciidump_line as a separate function, local to hexdump.c, for use by qemu_hexdump. Use "%-*s" to generate the alignment between the hex and the ascii, rather than explicit spaces. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240412073346.458116-3-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/virtio/vhost-vdpa.c | 2 +- include/qemu/cutils.h | 3 +-- util/hexdump.c | 56 ++++++++++++++++++++++++------------------ 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index f3a86c1a8c..7368b71902 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -949,7 +949,7 @@ static void vhost_vdpa_dump_config(struct vhost_dev *dev, const uint8_t *config, for (b = 0; b < config_len; b += 16) { len = config_len - b; - qemu_hexdump_line(line, config + b, len, false); + qemu_hexdump_line(line, config + b, len); trace_vhost_vdpa_dump_config(dev, b, line); } } diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index d7715f7a33..c5dea63742 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -287,8 +287,7 @@ int parse_debug_env(const char *name, int max, int initial); */ #define QEMU_HEXDUMP_LINE_BYTES 16 /* Number of bytes to dump */ #define QEMU_HEXDUMP_LINE_LEN 75 /* Number of characters in line */ -void qemu_hexdump_line(char *line, const void *bufptr, - unsigned int len, bool ascii); +void qemu_hexdump_line(char *line, const void *bufptr, size_t len); /* * Hexdump a buffer to a file. An optional string prefix is added to every line diff --git a/util/hexdump.c b/util/hexdump.c index 7324e7b126..0f943e31e5 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -16,49 +16,57 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -void qemu_hexdump_line(char *line, const void *bufptr, - unsigned int len, bool ascii) +void qemu_hexdump_line(char *line, const void *bufptr, size_t len) { const char *buf = bufptr; - int i, c; + int i; if (len > QEMU_HEXDUMP_LINE_BYTES) { len = QEMU_HEXDUMP_LINE_BYTES; } - for (i = 0; i < QEMU_HEXDUMP_LINE_BYTES; i++) { + for (i = 0; i < len; i++) { if (i != 0 && (i % 4) == 0) { *line++ = ' '; } - if (i < len) { - line += sprintf(line, " %02x", (unsigned char)buf[i]); - } else { - line += sprintf(line, " "); - } - } - if (ascii) { - *line++ = ' '; - for (i = 0; i < len; i++) { - c = buf[i]; - if (c < ' ' || c > '~') { - c = '.'; - } - *line++ = c; - } + line += sprintf(line, " %02x", (unsigned char)buf[i]); } *line = '\0'; } +static void asciidump_line(char *line, const void *bufptr, size_t len) +{ + const char *buf = bufptr; + + for (size_t i = 0; i < len; i++) { + char c = buf[i]; + + if (c < ' ' || c > '~') { + c = '.'; + } + *line++ = c; + } + *line = '\0'; +} + +#define QEMU_HEXDUMP_LINE_WIDTH \ + (QEMU_HEXDUMP_LINE_BYTES * 2 + QEMU_HEXDUMP_LINE_BYTES / 4) + void qemu_hexdump(FILE *fp, const char *prefix, const void *bufptr, size_t size) { - unsigned int b, len; char line[QEMU_HEXDUMP_LINE_LEN]; + char ascii[QEMU_HEXDUMP_LINE_BYTES + 1]; + size_t b, len; - for (b = 0; b < size; b += QEMU_HEXDUMP_LINE_BYTES) { - len = size - b; - qemu_hexdump_line(line, bufptr + b, len, true); - fprintf(fp, "%s: %04x: %s\n", prefix, b, line); + for (b = 0; b < size; b += len) { + len = MIN(size - b, QEMU_HEXDUMP_LINE_BYTES); + + qemu_hexdump_line(line, bufptr + b, len); + asciidump_line(ascii, bufptr + b, len); + + fprintf(fp, "%s: %04zx: %-*s %s\n", + prefix, b, QEMU_HEXDUMP_LINE_WIDTH, line, ascii); } } From 21d61b39436237cac7a9694864dfd92caec05fa4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 May 2024 14:03:39 +0200 Subject: [PATCH 1131/2884] MAINTAINERS: drop usb maintainership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove myself from usb entries. Flip status to "Orphan" for entries which have nobody else listed. Signed-off-by: Gerd Hoffmann Reviewed-by: Manos Pitsidianakis Message-ID: <20240528083858.836262-3-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 448dc951c5..5bc478f7ff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2141,8 +2141,7 @@ F: tests/qtest/fuzz-sdcard-test.c F: tests/qtest/sdhci-test.c USB -M: Gerd Hoffmann -S: Odd Fixes +S: Orphan F: hw/usb/* F: stubs/usb-dev-stub.c F: tests/qtest/usb-*-test.c @@ -2151,7 +2150,6 @@ F: include/hw/usb.h F: include/hw/usb/ USB (serial adapter) -R: Gerd Hoffmann M: Samuel Thibault S: Maintained F: hw/usb/dev-serial.c From 096d9104f9b5d19f7ceff3332a40d9fe188c27cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 28 May 2024 14:14:04 +0200 Subject: [PATCH 1132/2884] system/runstate: Remove unused 'qemu/plugin.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit system/runstate.c never required "qemu/plugin.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240528145953.65398-2-philmd@linaro.org> --- system/runstate.c | 1 - 1 file changed, 1 deletion(-) diff --git a/system/runstate.c b/system/runstate.c index cb4905a40f..ec32e270cb 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -45,7 +45,6 @@ #include "qemu/job.h" #include "qemu/log.h" #include "qemu/module.h" -#include "qemu/plugin.h" #include "qemu/sockets.h" #include "qemu/timer.h" #include "qemu/thread.h" From eeb6198ee89692b65fc3bea45fb04d7a32cbe4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 28 May 2024 14:26:44 +0200 Subject: [PATCH 1133/2884] accel/tcg: Move common declarations to 'internal-common.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'internal-target.h' is meant for target-specific declarations, while 'internal-common.h' for common ones. Move common declarations to it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240528145953.65398-3-philmd@linaro.org> --- accel/tcg/internal-common.h | 15 +++++++++++++++ accel/tcg/internal-target.h | 14 -------------- accel/tcg/tcg-all.c | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index cff43d221b..a8fc3db774 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -15,6 +15,8 @@ extern int64_t max_delay; extern int64_t max_advance; +extern bool one_insn_per_tb; + /* * Return true if CS is not running in parallel with other cpus, either * because there are no other cpus or we are within an exclusive context. @@ -41,4 +43,17 @@ static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) #endif } +TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc, + uint64_t cs_base, uint32_t flags, + int cflags); +void page_init(void); +void tb_htable_init(void); +void tb_reset_jump(TranslationBlock *tb, int n); +TranslationBlock *tb_link_page(TranslationBlock *tb); +void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, + uintptr_t host_pc); + +bool tcg_exec_realizefn(CPUState *cpu, Error **errp); +void tcg_exec_unrealizefn(CPUState *cpu); + #endif diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 4e36cf858e..fe109724c6 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -69,19 +69,7 @@ void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); #endif /* CONFIG_SOFTMMU */ -TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - int cflags); -void page_init(void); -void tb_htable_init(void); -void tb_reset_jump(TranslationBlock *tb, int n); -TranslationBlock *tb_link_page(TranslationBlock *tb); bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); -void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, - uintptr_t host_pc); - -bool tcg_exec_realizefn(CPUState *cpu, Error **errp); -void tcg_exec_unrealizefn(CPUState *cpu); /* Return the current PC from CPU, which may be cached in TB. */ static inline vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) @@ -93,8 +81,6 @@ static inline vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) } } -extern bool one_insn_per_tb; - /** * tcg_req_mo: * @type: TCGBar diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index c6619f5b98..2090907dba 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -38,7 +38,7 @@ #if !defined(CONFIG_USER_ONLY) #include "hw/boards.h" #endif -#include "internal-target.h" +#include "internal-common.h" struct TCGState { AccelState parent_obj; From a128c309c914ccc2e742fb015320a554c9faf8b9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 May 2024 18:09:52 +0100 Subject: [PATCH 1134/2884] accel/kvm: Fix two lines with hard-coded tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In kvm-all.c, two lines have been accidentally indented with hard-coded tabs rather than spaces. Normalise to match the rest of the file. Signed-off-by: Peter Maydell Reviewed-by: Zhao Liu Message-ID: <20240531170952.505323-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- accel/kvm/kvm-all.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index c0be9f5eed..009b49de44 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2893,7 +2893,7 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) !memory_region_is_ram_device(mr) && !memory_region_is_rom(mr) && !memory_region_is_romd(mr)) { - ret = 0; + ret = 0; } else { error_report("Convert non guest_memfd backed memory region " "(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s", @@ -2964,7 +2964,7 @@ int kvm_cpu_exec(CPUState *cpu) kvm_arch_pre_run(cpu, run); if (qatomic_read(&cpu->exit_request)) { - trace_kvm_interrupt_exit_request(); + trace_kvm_interrupt_exit_request(); /* * KVM requires us to reenter the kernel after IO exits to complete * instruction emulation. This self-signal will ensure that we From b8a208ccf51013a88eb410a7820a4951834a913f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 30 May 2024 20:42:46 +0100 Subject: [PATCH 1135/2884] hw/core: expand on the alignment of CPUState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the relationship between CPUState, ArchCPU and cpu_env a bit clearer in the kdoc comments. Signed-off-by: Alex Bennée Reviewed-by: Pierrick Bouvier Message-ID: <20240530194250.1801701-2-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/cpu.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index bb398e8237..be44746d24 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -391,7 +391,8 @@ struct qemu_work_item; #define CPU_UNSET_NUMA_NODE_ID -1 /** - * CPUState: + * struct CPUState - common state of one CPU core or thread. + * * @cpu_index: CPU index (informative). * @cluster_index: Identifies which cluster this CPU is in. * For boards which don't define clusters or for "loose" CPUs not assigned @@ -439,10 +440,15 @@ struct qemu_work_item; * @kvm_fetch_index: Keeps the index that we last fetched from the per-vCPU * dirty ring structure. * - * State of one CPU core or thread. + * @neg_align: The CPUState is the common part of a concrete ArchCPU + * which is allocated when an individual CPU instance is created. As + * such care is taken is ensure there is no gap between between + * CPUState and CPUArchState within ArchCPU. * - * Align, in order to match possible alignment required by CPUArchState, - * and eliminate a hole between CPUState and CPUArchState within ArchCPU. + * @neg: The architectural register state ("cpu_env") immediately follows + * CPUState in ArchCPU and is passed to TCG code. The @neg structure holds + * some common TCG CPU variables which are accessed with a negative offset + * from cpu_env. */ struct CPUState { /*< private >*/ From a4c2735f35b8b2bca5784ff9bf754a99b654c9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 30 May 2024 20:42:47 +0100 Subject: [PATCH 1136/2884] cpu: move Qemu[Thread|Cond] setup into common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aside from the round robin threads this is all common code. By moving the halt_cond setup we also no longer need hacks to work around the race between QOM object creation and thread creation. It is a little ugly to free stuff up for the round robin thread but better it deal with its own specialises than making the other accelerators jump through hoops. Signed-off-by: Alex Bennée Reviewed-by: Pierrick Bouvier Message-ID: <20240530194250.1801701-3-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- accel/dummy-cpus.c | 3 --- accel/hvf/hvf-accel-ops.c | 4 ---- accel/kvm/kvm-accel-ops.c | 3 --- accel/tcg/tcg-accel-ops-mttcg.c | 4 ---- accel/tcg/tcg-accel-ops-rr.c | 14 +++++++------- hw/core/cpu-common.c | 5 +++++ include/hw/core/cpu.h | 4 ++++ target/i386/nvmm/nvmm-accel-ops.c | 3 --- target/i386/whpx/whpx-accel-ops.c | 3 --- 9 files changed, 16 insertions(+), 27 deletions(-) diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 20519f1ea4..f32d8c8dc3 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -68,9 +68,6 @@ void dummy_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, dummy_cpu_thread_fn, cpu, diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 40d4187d9d..6f1e27ef46 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -463,10 +463,6 @@ static void hvf_start_vcpu_thread(CPUState *cpu) */ assert(hvf_enabled()); - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, hvf_cpu_thread_fn, diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 94c828ac8d..c239dfc87a 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -66,9 +66,6 @@ static void kvm_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn, diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index c552b45b8e..49814ec4af 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -137,10 +137,6 @@ void mttcg_start_vcpu_thread(CPUState *cpu) g_assert(tcg_enabled()); tcg_cpu_init_cflags(cpu, current_machine->smp.max_cpus > 1); - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - /* create a thread per vCPU with TCG (MTTCG) */ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", cpu->cpu_index); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 894e73e52c..84c36c1450 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -317,22 +317,22 @@ void rr_start_vcpu_thread(CPUState *cpu) tcg_cpu_init_cflags(cpu, false); if (!single_tcg_cpu_thread) { - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_new0(QemuCond, 1); - qemu_cond_init(cpu->halt_cond); + single_tcg_halt_cond = cpu->halt_cond; + single_tcg_cpu_thread = cpu->thread; /* share a single thread for all cpus with TCG */ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG"); qemu_thread_create(cpu->thread, thread_name, rr_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); - - single_tcg_halt_cond = cpu->halt_cond; - single_tcg_cpu_thread = cpu->thread; } else { - /* we share the thread */ + /* we share the thread, dump spare data */ + g_free(cpu->thread); + qemu_cond_destroy(cpu->halt_cond); cpu->thread = single_tcg_cpu_thread; cpu->halt_cond = single_tcg_halt_cond; + + /* copy the stuff done at start of rr_cpu_thread_fn */ cpu->thread_id = first_cpu->thread_id; cpu->neg.can_do_io = 1; cpu->created = true; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 0f0a247f56..6cfc01593a 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -261,6 +261,11 @@ static void cpu_common_initfn(Object *obj) cpu->nr_threads = 1; cpu->cflags_next_tb = -1; + /* allocate storage for thread info, initialise condition variables */ + cpu->thread = g_new0(QemuThread, 1); + cpu->halt_cond = g_new0(QemuCond, 1); + qemu_cond_init(cpu->halt_cond); + qemu_mutex_init(&cpu->work_mutex); qemu_lockcnt_init(&cpu->in_ioctl_lock); QSIMPLEQ_INIT(&cpu->work_list); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index be44746d24..a2c8536943 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -404,10 +404,14 @@ struct qemu_work_item; * @tcg_cflags: Pre-computed cflags for this cpu. * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU core. + * @thread: Host thread details, only live once @created is #true + * @sem: WIN32 only semaphore used only for qtest + * @thread_id: native thread id of vCPU, only live once @created is #true * @running: #true if CPU is currently running (lockless). * @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end; * valid under cpu_list_lock. * @created: Indicates whether the CPU thread has been successfully created. + * @halt_cond: condition variable sleeping threads can wait on. * @interrupt_request: Indicates a pending interrupt request. * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 6b2bfd9b9c..0ba31201e2 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -64,9 +64,6 @@ static void nvmm_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_new0(QemuCond, 1); - qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/NVMM", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, qemu_nvmm_cpu_thread_fn, diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 189ae0f140..1a2b4e1c43 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -64,9 +64,6 @@ static void whpx_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_new0(QemuCond, 1); - qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/WHPX", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, whpx_cpu_thread_fn, From 39e4bc4cdf83175e4a2d73e32c8d6785b80ebeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 30 May 2024 20:42:48 +0100 Subject: [PATCH 1137/2884] cpu-target: don't set cpu->thread_id to bogus value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The thread_id isn't valid until the threads are created. There is no point setting it here. The only thing that cares about the thread_id is qmp_query_cpus_fast. Signed-off-by: Alex Bennée Reviewed-by: Pierrick Bouvier Message-ID: <20240530194250.1801701-4-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- cpu-target.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cpu-target.c b/cpu-target.c index 5af120e8aa..499facf774 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -241,7 +241,6 @@ void cpu_exec_initfn(CPUState *cpu) cpu->num_ases = 0; #ifndef CONFIG_USER_ONLY - cpu->thread_id = qemu_get_thread_id(); cpu->memory = get_system_memory(); object_ref(OBJECT(cpu->memory)); #endif From 2fa16246dd89b1a4500a89c105795814e7cbe7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 30 May 2024 20:42:49 +0100 Subject: [PATCH 1138/2884] plugins: remove special casing for cpu->realized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now the condition variable is initialised early on we don't need to go through hoops to avoid calling async_run_on_cpu. Signed-off-by: Alex Bennée Reviewed-by: Pierrick Bouvier Message-ID: <20240530194250.1801701-5-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- plugins/core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/core.c b/plugins/core.c index 0726bc7f25..badede28cf 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -65,11 +65,7 @@ static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata) CPUState *cpu = container_of(k, CPUState, cpu_index); run_on_cpu_data mask = RUN_ON_CPU_HOST_ULONG(*plugin.mask); - if (DEVICE(cpu)->realized) { - async_run_on_cpu(cpu, plugin_cpu_update__async, mask); - } else { - plugin_cpu_update__async(cpu, mask); - } + async_run_on_cpu(cpu, plugin_cpu_update__async, mask); } void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx, From 638181a180bd4815eb4db64cfc50092cc3e5035e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 30 May 2024 20:42:50 +0100 Subject: [PATCH 1139/2884] core/cpu-common: initialise plugin state before thread creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally I tried to move where vCPU thread initialisation to later in realize. However pulling that thread (sic) got gnarly really quickly. It turns out some steps of CPU realization need values that can only be determined from the running vCPU thread. However having moved enough out of the thread creation we can now queue work before the thread starts (at least for TCG guests) and avoid the race between vcpu_init and other vcpu states a plugin might subscribe to. Signed-off-by: Alex Bennée Reviewed-by: Pierrick Bouvier Message-ID: <20240530194250.1801701-6-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/cpu-common.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 6cfc01593a..bf1a7b8892 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -222,14 +222,6 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) cpu_resume(cpu); } - /* Plugin initialization must wait until the cpu start executing code */ -#ifdef CONFIG_PLUGIN - if (tcg_enabled()) { - cpu->plugin_state = qemu_plugin_create_vcpu_state(); - async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL); - } -#endif - /* NOTE: latest generic point where the cpu is fully realized */ } @@ -273,6 +265,18 @@ static void cpu_common_initfn(Object *obj) QTAILQ_INIT(&cpu->watchpoints); cpu_exec_initfn(cpu); + + /* + * Plugin initialization must wait until the cpu start executing + * code, but we must queue this work before the threads are + * created to ensure we don't race. + */ +#ifdef CONFIG_PLUGIN + if (tcg_enabled()) { + cpu->plugin_state = qemu_plugin_create_vcpu_state(); + async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL); + } +#endif } static void cpu_common_finalize(Object *obj) From 1be974bc2658b2f1d9a3aaacda2657dc85c208d2 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 18:49:36 +0200 Subject: [PATCH 1140/2884] xen: Add xen_mr_is_memory() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add xen_mr_is_memory() to abstract away tests for the xen_memory MR. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Acked-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240529140739.1387692-4-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen-hvm-common.c | 10 ++++++++-- include/sysemu/xen.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index 2d1b032121..a0a0252da0 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -12,6 +12,12 @@ MemoryRegion xen_memory; +/* Check for xen memory. */ +bool xen_mr_is_memory(MemoryRegion *mr) +{ + return mr == &xen_memory; +} + void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, Error **errp) { @@ -28,7 +34,7 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, return; } - if (mr == &xen_memory) { + if (xen_mr_is_memory(mr)) { return; } @@ -55,7 +61,7 @@ static void xen_set_memory(struct MemoryListener *listener, { XenIOState *state = container_of(listener, XenIOState, memory_listener); - if (section->mr == &xen_memory) { + if (xen_mr_is_memory(section->mr)) { return; } else { if (add) { diff --git a/include/sysemu/xen.h b/include/sysemu/xen.h index 754ec2e6cb..3445888e39 100644 --- a/include/sysemu/xen.h +++ b/include/sysemu/xen.h @@ -49,4 +49,5 @@ static inline void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, #endif /* CONFIG_XEN_IS_POSSIBLE */ +bool xen_mr_is_memory(MemoryRegion *mr); #endif From 5d1c26029e54bbdcef081cf3a016abf890f2da54 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 3 May 2024 03:44:45 +0200 Subject: [PATCH 1141/2884] physmem: Always pass offset + addr to xen_map_cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always pass address with offset to xen_map_cache(). This is in preparation for support for grant mappings. Since this is within a block that checks for offset == 0, this has no functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240529140739.1387692-5-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index 342b7a8fd4..5e6257ef65 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2230,7 +2230,8 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, * In that case just map the requested area. */ if (block->offset == 0) { - return xen_map_cache(block->mr, addr, len, lock, lock, + return xen_map_cache(block->mr, block->offset + addr, + len, lock, lock, is_write); } From a5bdc451c7d10056acd9b49f6028895451b37df5 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 3 May 2024 03:44:46 +0200 Subject: [PATCH 1142/2884] physmem: Replace check for RAMBlock offset 0 with xen_mr_is_memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For xen, when checking for the first RAM (xen_memory), use xen_mr_is_memory() rather than checking for a RAMBlock with offset 0. All Xen machines create xen_memory first so this has no functional change for existing machines. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: David Hildenbrand Message-ID: <20240529140739.1387692-6-edgar.iglesias@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index 5e6257ef65..b7847db1a2 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2229,7 +2229,7 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, * because we don't want to map the entire memory in QEMU. * In that case just map the requested area. */ - if (block->offset == 0) { + if (xen_mr_is_memory(block->mr)) { return xen_map_cache(block->mr, block->offset + addr, len, lock, lock, is_write); From 61d993d4babf3b8bce3b01f69be10f470a006d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 May 2024 10:22:42 +0100 Subject: [PATCH 1143/2884] hw/xen: Constify XenLegacyDevice::XenDevOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XenDevOps @ops is not updated, mark it const. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Paul Durrant Message-Id: <20240510104908.76908-3-philmd@linaro.org> --- hw/xen/xen-legacy-backend.c | 2 +- include/hw/xen/xen_pvdev.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 6f0b300a42..33620fe42e 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -170,7 +170,7 @@ int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, */ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, int dev, - struct XenDevOps *ops) + const struct XenDevOps *ops) { struct XenLegacyDevice *xendev; diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index fdf84f47af..0c98444047 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -52,7 +52,7 @@ struct XenLegacyDevice { xenevtchn_handle *evtchndev; xengnttab_handle *gnttabdev; - struct XenDevOps *ops; + const struct XenDevOps *ops; QTAILQ_ENTRY(XenLegacyDevice) next; }; From 19c2d53c029e57bb096377837fe250e367afece4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 May 2024 10:25:08 +0100 Subject: [PATCH 1144/2884] hw/xen: Constify xenstore_be::XenDevOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XenDevOps @ops is not updated, mark it const. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Paul Durrant Message-Id: <20240510104908.76908-4-philmd@linaro.org> --- hw/xen/xen-legacy-backend.c | 6 +++--- include/hw/xen/xen-legacy-backend.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 33620fe42e..5514184f9c 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -520,7 +520,7 @@ void xen_be_check_state(struct XenLegacyDevice *xendev) struct xenstore_be { const char *type; int dom; - struct XenDevOps *ops; + const struct XenDevOps *ops; }; static void xenstore_update_be(void *opaque, const char *watch) @@ -557,7 +557,7 @@ static void xenstore_update_be(void *opaque, const char *watch) } } -static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) +static int xenstore_scan(const char *type, int dom, const struct XenDevOps *ops) { struct XenLegacyDevice *xendev; char path[XEN_BUFSIZE]; @@ -624,7 +624,7 @@ void xen_be_init(void) xen_set_dynamic_sysbus(); } -int xen_be_register(const char *type, struct XenDevOps *ops) +int xen_be_register(const char *type, const struct XenDevOps *ops) { char path[50]; diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 979c4ea04c..62623ecb30 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -40,7 +40,7 @@ void xen_be_check_state(struct XenLegacyDevice *xendev); /* xen backend driver bits */ void xen_be_init(void); -int xen_be_register(const char *type, struct XenDevOps *ops); +int xen_be_register(const char *type, const struct XenDevOps *ops); int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenLegacyDevice *xendev); void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, From d76795ea3dd412e7f4e293672170e3292ef0f7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 May 2024 11:36:51 +0200 Subject: [PATCH 1145/2884] hw/xen: Make XenDevOps structures const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep XenDevOps structures in .rodata. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Paul Durrant Message-Id: <20240510104908.76908-5-philmd@linaro.org> --- hw/9pfs/xen-9p-backend.c | 2 +- hw/display/xenfb.c | 4 ++-- hw/usb/xen-usb.c | 2 +- include/hw/xen/xen-legacy-backend.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index a3ac53f989..79359d911a 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -513,7 +513,7 @@ static void xen_9pfs_alloc(struct XenLegacyDevice *xendev) xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); } -static struct XenDevOps xen_9pfs_ops = { +static const struct XenDevOps xen_9pfs_ops = { .size = sizeof(Xen9pfsDev), .flags = DEVOPS_FLAG_NEED_GNTDEV, .alloc = xen_9pfs_alloc, diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 27536bfce0..b6d370bdf6 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -972,7 +972,7 @@ static void fb_event(struct XenLegacyDevice *xendev) /* -------------------------------------------------------------------- */ -static struct XenDevOps xen_kbdmouse_ops = { +static const struct XenDevOps xen_kbdmouse_ops = { .size = sizeof(struct XenInput), .init = input_init, .initialise = input_initialise, @@ -981,7 +981,7 @@ static struct XenDevOps xen_kbdmouse_ops = { .event = input_event, }; -struct XenDevOps xen_framebuffer_ops = { +const struct XenDevOps xen_framebuffer_ops = { .size = sizeof(struct XenFB), .init = fb_init, .initialise = fb_initialise, diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 416623f956..13901625c0 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -1083,7 +1083,7 @@ static void usbback_event(struct XenLegacyDevice *xendev) qemu_bh_schedule(usbif->bh); } -static struct XenDevOps xen_usb_ops = { +static const struct XenDevOps xen_usb_ops = { .size = sizeof(struct usbback_info), .flags = DEVOPS_FLAG_NEED_GNTDEV, .init = usbback_init, diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 62623ecb30..e55a14057f 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -67,7 +67,7 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, } /* backend drivers not included in all machines */ -extern struct XenDevOps xen_framebuffer_ops; /* xenfb.c */ +extern const struct XenDevOps xen_framebuffer_ops; /* xenfb.c */ /* configuration (aka xenbus setup) */ void xen_config_cleanup(void); From 6ece1df96629de8e2381f06b876ceeb5fade758a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 May 2024 12:18:00 +0200 Subject: [PATCH 1146/2884] hw/xen: Register framebuffer backend via xen_backend_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align the framebuffer backend with the other legacy ones, register it via xen_backend_init() when '-vga xenfb' is used. It is safe because MODULE_INIT_XEN_BACKEND is called in xen_bus_realize(), long after CLI processing initialized the vga_interface_type variable. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Paul Durrant Message-Id: <20240510104908.76908-8-philmd@linaro.org> --- hw/display/xenfb.c | 9 +++++++-- hw/xenpv/xen_machine_pv.c | 2 -- include/hw/xen/xen-legacy-backend.h | 3 --- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index b6d370bdf6..ff442ced1a 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -29,6 +29,7 @@ #include "ui/input.h" #include "ui/console.h" +#include "sysemu/sysemu.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/interface/io/fbif.h" @@ -996,8 +997,12 @@ static const GraphicHwOps xenfb_ops = { .ui_info = xenfb_ui_info, }; -static void xen_vkbd_register_backend(void) +static void xen_ui_register_backend(void) { xen_be_register("vkbd", &xen_kbdmouse_ops); + + if (vga_interface_type == VGA_XENFB) { + xen_be_register("vfb", &xen_framebuffer_ops); + } } -xen_backend_init(xen_vkbd_register_backend); +xen_backend_init(xen_ui_register_backend); diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index b500ce0989..24395f42cb 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -50,8 +50,6 @@ static void xen_init_pv(MachineState *machine) break; } - xen_be_register("vfb", &xen_framebuffer_ops); - /* configure framebuffer */ if (vga_interface_type == VGA_XENFB) { xen_config_dev_vfb(0, "vnc"); diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index e55a14057f..943732b8d1 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -66,9 +66,6 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, return xen_be_unmap_grant_refs(xendev, ptr, &ref, 1); } -/* backend drivers not included in all machines */ -extern const struct XenDevOps xen_framebuffer_ops; /* xenfb.c */ - /* configuration (aka xenbus setup) */ void xen_config_cleanup(void); int xen_config_dev_vfb(int vdev, const char *type); From af1cf62401f01b1ecce1e2cd94fbd8410064418a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 23 May 2024 09:09:17 +0200 Subject: [PATCH 1147/2884] hw/misc/debugexit: use runstate API instead of plain exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Directly calling exit() prevents any kind of management or handling. Instead use the corresponding runstate API. The default behavior of the runstate API is the same as exit(). Signed-off-by: Thomas Weißschuh Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240523-debugexit-v1-1-d52fcaf7bf8b@t-8ch.de> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/debugexit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index ab6de69ce7..c5c562fd93 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -12,6 +12,7 @@ #include "hw/qdev-properties.h" #include "qemu/module.h" #include "qom/object.h" +#include "sysemu/runstate.h" #define TYPE_ISA_DEBUG_EXIT_DEVICE "isa-debug-exit" OBJECT_DECLARE_SIMPLE_TYPE(ISADebugExitState, ISA_DEBUG_EXIT_DEVICE) @@ -32,7 +33,8 @@ static uint64_t debug_exit_read(void *opaque, hwaddr addr, unsigned size) static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { - exit((val << 1) | 1); + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, + (val << 1) | 1); } static const MemoryRegionOps debug_exit_ops = { From a7d8244be9f9a0fde9f694a46bdd04aabbbb5b4a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 May 2024 13:46:28 +0100 Subject: [PATCH 1148/2884] hw/dma/xlnx_dpdma: Read descriptor into buffer, not into pointer-to-buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In fdf029762f501 we factored out the handling of reading and writing DMA descriptors from guest memory. Unfortunately we accidentally made the descriptor-read read the descriptor into the address of the buffer rather than into the buffer, because we didn't notice we needed to update the arguments to the dma_memory_read() call. Before the refactoring, "&desc" is the address of a local struct DPDMADescriptor variable in xlnx_dpdma_start_operation(), which is the correct target for the guest-memory-read. But after the refactoring 'desc' is the "DPDMADescriptor *desc" argument to the new function, and so it is already an address. This bug is an overrun of a stack variable, since a pointer is at most 8 bytes long and we try to read 64 bytes, as well as being incorrect behaviour. Pass 'desc' rather than '&desc' as the dma_memory_read() argument to fix this. (The same bug is not present in xlnx_dpdma_write_descriptor(), because there we are writing the descriptor from a local struct variable "DPDMADescriptor tmp_desc" and so passing &tmp_desc to dma_memory_write() is correct.) Spotted by Coverity: CID 1546649 Fixes: fdf029762f50101 ("xlnx_dpdma: fix descriptor endianness bug") Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240531124628.476938-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/dma/xlnx_dpdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index dde4aeca40..a685bd28bb 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -619,7 +619,7 @@ static MemTxResult xlnx_dpdma_read_descriptor(XlnxDPDMAState *s, DPDMADescriptor *desc) { MemTxResult res = dma_memory_read(&address_space_memory, desc_addr, - &desc, sizeof(DPDMADescriptor), + desc, sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); if (res) { return res; From 0f910b8724ab7e29a7a605509edb710787ff13ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 May 2024 08:53:47 +0200 Subject: [PATCH 1149/2884] hw/acpi: Remove the deprecated QAPI MEM_UNPLUG_ERROR event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MEM_UNPLUG_ERROR event is deprecated since commit d43f1670c7 ("qapi/qdev.json: add DEVICE_UNPLUG_GUEST_ERROR QAPI event"), time to remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Harsh Prateek Bora Reviewed-by: Michael S. Tsirkin Reviewed-by: Markus Armbruster Message-Id: <20240530071548.20074-2-philmd@linaro.org> --- docs/about/deprecated.rst | 5 ----- docs/about/removed-features.rst | 9 +++++++++ hw/acpi/memory_hotplug.c | 8 -------- hw/ppc/spapr.c | 11 +---------- qapi/machine.json | 28 ---------------------------- 5 files changed, 10 insertions(+), 51 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 40585ca7d5..4a61894db6 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -151,11 +151,6 @@ property types. QEMU Machine Protocol (QMP) events ---------------------------------- -``MEM_UNPLUG_ERROR`` (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. - ``vcpu`` trace events (since 8.1) ''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index fba0cfb0b0..f1e70263e2 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -671,6 +671,15 @@ Use ``multifd-channels`` instead. Use ``multifd-compression`` instead. +QEMU Machine Protocol (QMP) events +---------------------------------- + +``MEM_UNPLUG_ERROR`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''' + +MEM_UNPLUG_ERROR has been replaced by the more generic ``DEVICE_UNPLUG_GUEST_ERROR`` event. + + Human Monitor Protocol (HMP) commands ------------------------------------- diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index de6f974ebb..9b974b7274 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -178,14 +178,6 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); if (local_err) { trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); - - /* - * Send both MEM_UNPLUG_ERROR and DEVICE_UNPLUG_GUEST_ERROR - * while the deprecation of MEM_UNPLUG_ERROR is - * pending. - */ - qapi_event_send_mem_unplug_error(dev->id ? : "", - error_get_pretty(local_err)); qapi_event_send_device_unplug_guest_error(dev->id, dev->canonical_path); error_free(local_err); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 4345764bce..81a187f126 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3786,7 +3786,6 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) SpaprDrc *drc; uint32_t nr_lmbs; uint64_t size, addr_start, addr; - g_autofree char *qapi_error = NULL; int i; if (!dev) { @@ -3823,16 +3822,8 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) /* * Tell QAPI that something happened and the memory - * hotunplug wasn't successful. Keep sending - * MEM_UNPLUG_ERROR even while sending - * DEVICE_UNPLUG_GUEST_ERROR until the deprecation of - * MEM_UNPLUG_ERROR is due. + * hotunplug wasn't successful. */ - qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest " - "for device %s", dev->id); - - qapi_event_send_mem_unplug_error(dev->id ? : "", qapi_error); - qapi_event_send_device_unplug_guest_error(dev->id, dev->canonical_path); } diff --git a/qapi/machine.json b/qapi/machine.json index bce6e1bbc4..453feb9347 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1607,34 +1607,6 @@ { 'event': 'MEMORY_DEVICE_SIZE_CHANGE', 'data': { '*id': 'str', 'size': 'size', 'qom-path' : 'str'} } -## -# @MEM_UNPLUG_ERROR: -# -# Emitted when memory hot unplug error occurs. -# -# @device: device name -# -# @msg: Informative message -# -# Features: -# -# @deprecated: This event is deprecated. Use -# @DEVICE_UNPLUG_GUEST_ERROR instead. -# -# Since: 2.4 -# -# Example: -# -# <- { "event": "MEM_UNPLUG_ERROR", -# "data": { "device": "dimm1", -# "msg": "acpi: device unplug for unsupported device" -# }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -## -{ 'event': 'MEM_UNPLUG_ERROR', - 'data': { 'device': 'str', 'msg': 'str' }, - 'features': ['deprecated'] } - ## # @BootConfiguration: # From 7ffc4894a63b6c42837d4a1066e536073bccd39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 May 2024 08:59:30 +0200 Subject: [PATCH 1150/2884] trace: Remove deprecated 'vcpu' field from QMP trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'vcpu' fields are deprecated since commit 5485e52a33 ("qapi: make the vcpu parameters deprecated for 8.1"), time to remove them. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Markus Armbruster Message-Id: <20240530071548.20074-3-philmd@linaro.org> --- docs/about/deprecated.rst | 11 ----------- docs/about/removed-features.rst | 6 ++++++ qapi/trace.json | 27 +++------------------------ trace/qmp.c | 2 -- trace/trace-hmp-cmds.c | 4 ++-- 5 files changed, 11 insertions(+), 39 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 4a61894db6..187c8a3f97 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -148,17 +148,6 @@ accepted incorrect commands will return an error. Users should make sure that all arguments passed to ``device_add`` are consistent with the documented property types. -QEMU Machine Protocol (QMP) events ----------------------------------- - -``vcpu`` trace events (since 8.1) -''''''''''''''''''''''''''''''''' - -The ability to instrument QEMU helper functions with vCPU-aware trace -points was removed in 7.0. However QMP still exposed the vcpu -parameter. This argument has now been deprecated and the remaining -remaining trace points that used it are selected just by name. - Host Architectures ------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index f1e70263e2..1e64c27cd8 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -679,6 +679,12 @@ QEMU Machine Protocol (QMP) events MEM_UNPLUG_ERROR has been replaced by the more generic ``DEVICE_UNPLUG_GUEST_ERROR`` event. +``vcpu`` trace events (removed in 9.1) +'''''''''''''''''''''''''''''''''''''' + +The ability to instrument QEMU helper functions with vCPU-aware trace +points was removed in 7.0. + Human Monitor Protocol (HMP) commands ------------------------------------- diff --git a/qapi/trace.json b/qapi/trace.json index 043d12f83e..9ebb6d9eaf 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -35,17 +35,10 @@ # # @state: Tracing state. # -# @vcpu: Whether this is a per-vCPU event (since 2.7). -# -# Features: -# -# @deprecated: Member @vcpu is deprecated, and always ignored. -# # Since: 2.2 ## { 'struct': 'TraceEventInfo', - 'data': {'name': 'str', 'state': 'TraceEventState', - 'vcpu': { 'type': 'bool', 'features': ['deprecated'] } } } + 'data': {'name': 'str', 'state': 'TraceEventState' } } ## # @trace-event-get-state: @@ -54,12 +47,6 @@ # # @name: Event name pattern (case-sensitive glob). # -# @vcpu: The vCPU to query (since 2.7). -# -# Features: -# -# @deprecated: Member @vcpu is deprecated, and always ignored. -# # Returns: a list of @TraceEventInfo for the matching events # # Since: 2.2 @@ -71,8 +58,7 @@ # <- { "return": [ { "name": "qemu_memalign", "state": "disabled", "vcpu": false } ] } ## { 'command': 'trace-event-get-state', - 'data': {'name': 'str', - '*vcpu': {'type': 'int', 'features': ['deprecated'] } }, + 'data': {'name': 'str' }, 'returns': ['TraceEventInfo'] } ## @@ -86,12 +72,6 @@ # # @ignore-unavailable: Do not match unavailable events with @name. # -# @vcpu: The vCPU to act upon (all by default; since 2.7). -# -# Features: -# -# @deprecated: Member @vcpu is deprecated, and always ignored. -# # Since: 2.2 # # Example: @@ -101,5 +81,4 @@ # <- { "return": {} } ## { 'command': 'trace-event-set-state', - 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool', - '*vcpu': {'type': 'int', 'features': ['deprecated'] } } } + 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool' } } diff --git a/trace/qmp.c b/trace/qmp.c index 3e3971c6a8..074a27b204 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -48,7 +48,6 @@ static bool check_events(bool ignore_unavailable, bool is_pattern, } TraceEventInfoList *qmp_trace_event_get_state(const char *name, - bool has_vcpu, int64_t vcpu, Error **errp) { TraceEventInfoList *events = NULL; @@ -86,7 +85,6 @@ TraceEventInfoList *qmp_trace_event_get_state(const char *name, void qmp_trace_event_set_state(const char *name, bool enable, bool has_ignore_unavailable, bool ignore_unavailable, - bool has_vcpu, int64_t vcpu, Error **errp) { TraceEventIter iter; diff --git a/trace/trace-hmp-cmds.c b/trace/trace-hmp-cmds.c index 86211fce27..d38dd600de 100644 --- a/trace/trace-hmp-cmds.c +++ b/trace/trace-hmp-cmds.c @@ -40,7 +40,7 @@ void hmp_trace_event(Monitor *mon, const QDict *qdict) Error *local_err = NULL; qmp_trace_event_set_state(tp_name, new_state, - true, true, false, 0, &local_err); + true, true, &local_err); if (local_err) { error_report_err(local_err); } @@ -82,7 +82,7 @@ void hmp_info_trace_events(Monitor *mon, const QDict *qdict) name = "*"; } - events = qmp_trace_event_get_state(name, false, 0, &local_err); + events = qmp_trace_event_get_state(name, &local_err); if (local_err) { error_report_err(local_err); return; From a7a2d636ae4549ef0551134d4bf8e084a14431c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 May 2024 08:36:43 +0200 Subject: [PATCH 1151/2884] qga: Remove deprecated 'blacklist' argument / config key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'blacklist' argument / config key are deprecated since commit 582a098e6c ("qga: Replace 'blacklist' command line and config file options by 'block-rpcs'"), time to remove them. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Konstantin Kostiuk Message-Id: <20240530070413.19181-1-philmd@linaro.org> --- docs/about/deprecated.rst | 18 ------------------ docs/about/removed-features.rst | 18 ++++++++++++++++++ qga/main.c | 6 ------ 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 187c8a3f97..a6f4655a3a 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -462,24 +462,6 @@ versions, aliases will point to newer CPU model versions depending on the machine type, so management software must resolve CPU model aliases before starting a virtual machine. -QEMU guest agent ----------------- - -``--blacklist`` command line option (since 7.2) -''''''''''''''''''''''''''''''''''''''''''''''' - -``--blacklist`` has been replaced by ``--block-rpcs`` (which is a better -wording for what this option does). The short form ``-b`` still stays -the same and thus is the preferred way for scripts that should run with -both, older and future versions of QEMU. - -``blacklist`` config file option (since 7.2) -'''''''''''''''''''''''''''''''''''''''''''' - -The ``blacklist`` config file option has been renamed to ``block-rpcs`` -(to be in sync with the renaming of the corresponding command line -option). - Migration --------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 1e64c27cd8..210a90bee8 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1144,4 +1144,22 @@ stable for some time and is now widely used. The command line and feature set is very close to the removed C implementation. +QEMU guest agent +---------------- + +``--blacklist`` command line option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''' + +``--blacklist`` has been replaced by ``--block-rpcs`` (which is a better +wording for what this option does). The short form ``-b`` still stays +the same and thus is the preferred way for scripts that should run with +both, older and future versions of QEMU. + +``blacklist`` config file option (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``blacklist`` config file option has been renamed to ``block-rpcs`` +(to be in sync with the renaming of the corresponding command line +option). + .. _Intel discontinuance notification: https://www.intel.com/content/www/us/en/content-details/781327/intel-is-discontinuing-ip-ordering-codes-listed-in-pdn2312-for-nios-ii-ip.html diff --git a/qga/main.c b/qga/main.c index bdf5344584..f4d5f15bb3 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1071,11 +1071,6 @@ static void config_load(GAConfig *config) g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr); } - if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) { - g_warning("config using deprecated 'blacklist' key, should be replaced" - " with the 'block-rpcs' key."); - blockrpcs_key = "blacklist"; - } if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL)) { config->bliststr = g_key_file_get_string(keyfile, "general", blockrpcs_key, &gerr); @@ -1190,7 +1185,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, { "block-rpcs", 1, NULL, 'b' }, - { "blacklist", 1, NULL, 'b' }, /* deprecated alias for 'block-rpcs' */ { "allow-rpcs", 1, NULL, 'a' }, #ifdef _WIN32 { "service", 1, NULL, 's' }, From 7c2397643c1e025c157bab95088b3b480f0d98ae Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 May 2024 13:27:15 +0200 Subject: [PATCH 1152/2884] usb: add config options for the hub and hid devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240530112718.1752905-3-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/Kconfig | 10 ++++++++++ hw/usb/meson.build | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index f569ed7eea..84bc7fbe36 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -65,6 +65,16 @@ config TUSB6010 bool select USB_MUSB +config USB_HUB + bool + default y + depends on USB + +config USB_HID + bool + default y + depends on USB + config USB_TABLET_WACOM bool default y diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 23f7f7acb5..d7de1003e3 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -35,8 +35,8 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl- system_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) # emulated usb devices -system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) -system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) +system_ss.add(when: 'CONFIG_USB_HUB', if_true: files('dev-hub.c')) +system_ss.add(when: 'CONFIG_USB_HID', if_true: files('dev-hid.c')) system_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c')) system_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c')) system_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c')) From 993e38020fddc65e7536c5e1469c22588e5a9f16 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 4 Jun 2024 16:40:57 +0200 Subject: [PATCH 1153/2884] docs, tests: do not specify scsi=off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has been the default forever. Acked-by: Alex Bennée Signed-off-by: Paolo Bonzini --- docs/pci_expander_bridge.txt | 2 +- docs/specs/tpm.rst | 2 +- tests/avocado/intel_iommu.py | 2 +- tests/avocado/smmu.py | 2 +- tests/avocado/tuxrun_baselines.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/pci_expander_bridge.txt b/docs/pci_expander_bridge.txt index 36750273bb..540191f5e0 100644 --- a/docs/pci_expander_bridge.txt +++ b/docs/pci_expander_bridge.txt @@ -25,7 +25,7 @@ A detailed command line would be: -object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1 -device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd -device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8 -device e1000,bus=bridge2,addr=0x3 --device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 +-device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,bus=bridge3,addr=1 Here you have: - 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes) diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst index 68cb8cf7e6..1ad36ad709 100644 --- a/docs/specs/tpm.rst +++ b/docs/specs/tpm.rst @@ -336,7 +336,7 @@ In case a pSeries machine is emulated, use the following command line: -tpmdev emulator,id=tpm0,chardev=chrtpm \ -device tpm-spapr,tpmdev=tpm0 \ -device spapr-vscsi,id=scsi0,reg=0x00002000 \ - -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ + -device virtio-blk-pci,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0 In case an Arm virt machine is emulated, use the following command line: diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py index f04ee1cf9d..09e694bd40 100644 --- a/tests/avocado/intel_iommu.py +++ b/tests/avocado/intel_iommu.py @@ -32,7 +32,7 @@ class IntelIOMMU(LinuxTest): def set_up_boot(self): path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' + + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + 'drive=drv0,id=virtio-disk0,bootindex=1,' 'werror=stop,rerror=stop' + self.IOMMU_ADDON) self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON) diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py index 21ff030ca7..4ebfa7128c 100644 --- a/tests/avocado/smmu.py +++ b/tests/avocado/smmu.py @@ -32,7 +32,7 @@ class SMMU(LinuxTest): def set_up_boot(self): path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' + + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + 'drive=drv0,id=virtio-disk0,bootindex=1,' 'werror=stop,rerror=stop' + self.IOMMU_ADDON) self.vm.add_args('-drive', diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py index a936a3b780..736e4aa289 100644 --- a/tests/avocado/tuxrun_baselines.py +++ b/tests/avocado/tuxrun_baselines.py @@ -235,7 +235,7 @@ class TuxRunBaselineTest(QemuSystemTest): self.vm.add_args('-drive', 'file=' + qcow2.name + ',format=qcow2,if=none,id=' 'drive-virtio-disk1', - '-device', 'virtio-blk-pci,scsi=off,bus=pci.0,' + '-device', 'virtio-blk-pci,bus=pci.0,' 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1' ',bootindex=2') self.common_tuxrun(csums=sums, drive="scsi-hd") From a271b8d7b2f39275a05e49deb7c8edc20b7a8279 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 May 2024 15:51:57 +0200 Subject: [PATCH 1154/2884] virtio-blk: remove SCSI passthrough functionality The legacy SCSI passthrough functionality has never been enabled for VIRTIO 1.0 and was deprecated more than four years ago. Get rid of it---almost, because QEMU is advertising it unconditionally for legacy virtio-blk devices. Just parse the header and return a nonzero status. Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 10 -- docs/about/removed-features.rst | 8 ++ hw/block/virtio-blk.c | 166 +++----------------------------- hw/core/machine.c | 2 - 4 files changed, 19 insertions(+), 167 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 40585ca7d5..4980d721cf 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -296,16 +296,6 @@ Device options Emulated device options ''''''''''''''''''''''' -``-device virtio-blk,scsi=on|off`` (since 5.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 -and later do not support it because the virtio-scsi device was introduced for -full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. - -Note this also applies to ``-device virtio-blk-pci,scsi=on|off``, which is an -alias. - ``-device nvme-ns,eui64-default=on|off`` (since 7.1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index fba0cfb0b0..ae6269eb56 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -510,6 +510,14 @@ than zero. Removed along with the ``compression`` migration capability. +``-device virtio-blk,scsi=on|off`` (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''' + +The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 +and later do not support it because the virtio-scsi device was introduced for +full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. + + User-mode emulator command line arguments ----------------------------------------- diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index bb86e65f65..73bdfd6122 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -172,57 +172,6 @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) virtio_blk_free_request(req); } -#ifdef __linux__ - -typedef struct { - VirtIOBlockReq *req; - struct sg_io_hdr hdr; -} VirtIOBlockIoctlReq; - -static void virtio_blk_ioctl_complete(void *opaque, int status) -{ - VirtIOBlockIoctlReq *ioctl_req = opaque; - VirtIOBlockReq *req = ioctl_req->req; - VirtIOBlock *s = req->dev; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - struct virtio_scsi_inhdr *scsi; - struct sg_io_hdr *hdr; - - scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - - if (status) { - status = VIRTIO_BLK_S_UNSUPP; - virtio_stl_p(vdev, &scsi->errors, 255); - goto out; - } - - hdr = &ioctl_req->hdr; - /* - * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) - * clear the masked_status field [hence status gets cleared too, see - * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED - * status has occurred. However they do set DRIVER_SENSE in driver_status - * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. - */ - if (hdr->status == 0 && hdr->sb_len_wr > 0) { - hdr->status = CHECK_CONDITION; - } - - virtio_stl_p(vdev, &scsi->errors, - hdr->status | (hdr->msg_status << 8) | - (hdr->host_status << 16) | (hdr->driver_status << 24)); - virtio_stl_p(vdev, &scsi->residual, hdr->resid); - virtio_stl_p(vdev, &scsi->sense_len, hdr->sb_len_wr); - virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len); - -out: - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - g_free(ioctl_req); -} - -#endif - static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) { VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq)); @@ -233,20 +182,14 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) return req; } -static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) +static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { - int status = VIRTIO_BLK_S_OK; - struct virtio_scsi_inhdr *scsi = NULL; + int status; + struct virtio_scsi_inhdr *scsi; VirtIOBlock *blk = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(blk); VirtQueueElement *elem = &req->elem; -#ifdef __linux__ - int i; - VirtIOBlockIoctlReq *ioctl_req; - BlockAIOCB *acb; -#endif - /* * We require at least one output segment each for the virtio_blk_outhdr * and the SCSI command block. @@ -262,95 +205,16 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. + * + * Just put anything nonzero so that the ioctl fails in the guest. */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; - - if (!virtio_has_feature(blk->host_features, VIRTIO_BLK_F_SCSI)) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - - /* - * No support for bidirection commands yet. - */ - if (elem->out_num > 2 && elem->in_num > 3) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - -#ifdef __linux__ - ioctl_req = g_new0(VirtIOBlockIoctlReq, 1); - ioctl_req->req = req; - ioctl_req->hdr.interface_id = 'S'; - ioctl_req->hdr.cmd_len = elem->out_sg[1].iov_len; - ioctl_req->hdr.cmdp = elem->out_sg[1].iov_base; - ioctl_req->hdr.dxfer_len = 0; - - if (elem->out_num > 2) { - /* - * If there are more than the minimally required 2 output segments - * there is write payload starting from the third iovec. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_TO_DEV; - ioctl_req->hdr.iovec_count = elem->out_num - 2; - - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->out_sg[i + 2].iov_len; - } - - ioctl_req->hdr.dxferp = elem->out_sg + 2; - - } else if (elem->in_num > 3) { - /* - * If we have more than 3 input segments the guest wants to actually - * read data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_FROM_DEV; - ioctl_req->hdr.iovec_count = elem->in_num - 3; - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->in_sg[i].iov_len; - } - - ioctl_req->hdr.dxferp = elem->in_sg; - } else { - /* - * Some SCSI commands don't actually transfer any data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_NONE; - } - - ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; - ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; - - acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, - virtio_blk_ioctl_complete, ioctl_req); - if (!acb) { - g_free(ioctl_req); - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - return -EINPROGRESS; -#else - abort(); -#endif + virtio_stl_p(vdev, &scsi->errors, 255); + status = VIRTIO_BLK_S_UNSUPP; fail: - /* Just put anything nonzero so that the ioctl fails in the guest. */ - if (scsi) { - virtio_stl_p(vdev, &scsi->errors, 255); - } - return status; -} - -static void virtio_blk_handle_scsi(VirtIOBlockReq *req) -{ - int status; - - status = virtio_blk_handle_scsi_req(req); - if (status != -EINPROGRESS) { - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - } + virtio_blk_req_complete(req, status); + virtio_blk_free_request(req); } static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb, @@ -1379,13 +1243,9 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); - if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) { - if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_SCSI)) { - error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); - return 0; - } - } else { + if (!virtio_has_feature(features, VIRTIO_F_VERSION_1)) { virtio_clear_feature(&features, VIRTIO_F_ANY_LAYOUT); + /* Added for historical reasons, removing it could break migration. */ virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); } @@ -2132,10 +1992,6 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), DEFINE_PROP_BIT64("config-wce", VirtIOBlock, host_features, VIRTIO_BLK_F_CONFIG_WCE, true), -#ifdef __linux__ - DEFINE_PROP_BIT64("scsi", VirtIOBlock, host_features, - VIRTIO_BLK_F_SCSI, false), -#endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, diff --git a/hw/core/machine.c b/hw/core/machine.c index 8087026b45..17292b13e6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -265,8 +265,6 @@ GlobalProperty hw_compat_2_5[] = { const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); GlobalProperty hw_compat_2_4[] = { - /* Optional because the 'scsi' property is Linux-only */ - { "virtio-blk-device", "scsi", "true", .optional = true }, { "e1000", "extra_mac_registers", "off" }, { "virtio-pci", "x-disable-pcie", "on" }, { "virtio-pci", "migrate-extra", "off" }, From 72baef13b9dce71f20ae840d9951e559e14abf6d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 11:02:04 +0200 Subject: [PATCH 1155/2884] host/i386: nothing looks at CPUINFO_SSE4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only user was the SSE4.1 variant of buffer_is_zero, which has been removed; code to compute CPUINFO_SSE4 is dead. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- host/include/i386/host/cpuinfo.h | 1 - util/cpuinfo-i386.c | 1 - 2 files changed, 2 deletions(-) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index b89e6d2e55..9386c74988 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -16,7 +16,6 @@ #define CPUINFO_BMI1 (1u << 5) #define CPUINFO_BMI2 (1u << 6) #define CPUINFO_SSE2 (1u << 7) -#define CPUINFO_SSE4 (1u << 8) #define CPUINFO_AVX1 (1u << 9) #define CPUINFO_AVX2 (1u << 10) #define CPUINFO_AVX512F (1u << 11) diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index 9fddb18303..18ab747a6d 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -36,7 +36,6 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= (d & bit_CMOV ? CPUINFO_CMOV : 0); info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0); - info |= (c & bit_SSE4_1 ? CPUINFO_SSE4 : 0); info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); From 294ac64e459aca023f43441651d860980c9784f1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 10:37:06 +0200 Subject: [PATCH 1156/2884] meson: assume x86-64-v2 baseline ISA x86-64-v2 processors were released in 2008, assume that we have one. Unfortunately there is no GCC flag to enable all the features without disabling what came after; so enable them one by one. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- meson.build | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 6386607144..d80203f1cd 100644 --- a/meson.build +++ b/meson.build @@ -336,9 +336,13 @@ if host_arch == 'i386' and not cc.links(''' qemu_common_flags = ['-march=i486'] + qemu_common_flags endif -# ??? Only extremely old AMD cpus do not have cmpxchg16b. -# If we truly care, we should simply detect this case at -# runtime and generate the fallback to serial emulation. +# Assume x86-64-v2 (minus CMPXCHG16B for 32-bit code) +if host_arch == 'i386' + qemu_common_flags = ['-mfpmath=sse'] + qemu_common_flags +endif +if host_arch in ['i386', 'x86_64'] + qemu_common_flags = ['-mpopcnt', '-msse4.2'] + qemu_common_flags +endif if host_arch == 'x86_64' qemu_common_flags = ['-mcx16'] + qemu_common_flags endif From e68e97ce55b3d17af22dd62c3b3dc72f761b0862 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 10:14:48 +0200 Subject: [PATCH 1157/2884] host/i386: assume presence of CMOV QEMU now requires an x86-64-v2 host, which always has CMOV. Use it freely in TCG generated code. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- host/include/i386/host/cpuinfo.h | 1 - tcg/i386/tcg-target.c.inc | 15 +-------------- util/cpuinfo-i386.c | 1 - 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index 9386c74988..81771733ea 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -9,7 +9,6 @@ /* Digested version of */ #define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ -#define CPUINFO_CMOV (1u << 1) #define CPUINFO_MOVBE (1u << 2) #define CPUINFO_LZCNT (1u << 3) #define CPUINFO_POPCNT (1u << 4) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 59235b4f38..9a54ef7f8d 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -157,12 +157,6 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define SOFTMMU_RESERVE_REGS \ (tcg_use_softmmu ? (1 << TCG_REG_L0) | (1 << TCG_REG_L1) : 0) -/* For 64-bit, we always know that CMOV is available. */ -#if TCG_TARGET_REG_BITS == 64 -# define have_cmov true -#else -# define have_cmov (cpuinfo & CPUINFO_CMOV) -#endif #define have_bmi2 (cpuinfo & CPUINFO_BMI2) #define have_lzcnt (cpuinfo & CPUINFO_LZCNT) @@ -1815,14 +1809,7 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, static void tcg_out_cmov(TCGContext *s, int jcc, int rexw, TCGReg dest, TCGReg v1) { - if (have_cmov) { - tcg_out_modrm(s, OPC_CMOVCC | jcc | rexw, dest, v1); - } else { - TCGLabel *over = gen_new_label(); - tcg_out_jxx(s, jcc ^ 1, over, 1); - tcg_out_mov(s, TCG_TYPE_I32, dest, v1); - tcg_out_label(s, over); - } + tcg_out_modrm(s, OPC_CMOVCC | jcc | rexw, dest, v1); } static void tcg_out_movcond(TCGContext *s, int rexw, TCGCond cond, diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index 18ab747a6d..90f92a42dc 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -34,7 +34,6 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) if (max >= 1) { __cpuid(1, a, b, c, d); - info |= (d & bit_CMOV ? CPUINFO_CMOV : 0); info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0); info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); From b18236897ca15c3db1506d8edb9a191dfe51429c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 11:02:46 +0200 Subject: [PATCH 1158/2884] host/i386: assume presence of SSE2 QEMU now requires an x86-64-v2 host, which has SSE2. Use it freely in buffer_is_zero. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- host/include/i386/host/cpuinfo.h | 1 - util/bufferiszero.c | 4 ++-- util/cpuinfo-i386.c | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index 81771733ea..72f6fad61e 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -14,7 +14,6 @@ #define CPUINFO_POPCNT (1u << 4) #define CPUINFO_BMI1 (1u << 5) #define CPUINFO_BMI2 (1u << 6) -#define CPUINFO_SSE2 (1u << 7) #define CPUINFO_AVX1 (1u << 9) #define CPUINFO_AVX2 (1u << 10) #define CPUINFO_AVX512F (1u << 11) diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 74864f7b78..11c080e02c 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -188,14 +188,14 @@ static biz_accel_fn const accel_table[] = { static unsigned best_accel(void) { +#ifdef CONFIG_AVX2_OPT unsigned info = cpuinfo_init(); -#ifdef CONFIG_AVX2_OPT if (info & CPUINFO_AVX2) { return 2; } #endif - return info & CPUINFO_SSE2 ? 1 : 0; + return 1; } #elif defined(__aarch64__) && defined(__ARM_NEON) diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index 90f92a42dc..ca74ef04f5 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -34,7 +34,6 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) if (max >= 1) { __cpuid(1, a, b, c, d); - info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0); info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); From 433cd6d94a8256af70a5200f236dc8047c3c1468 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 10:22:33 +0200 Subject: [PATCH 1159/2884] host/i386: assume presence of SSSE3 QEMU now requires an x86-64-v2 host, which has SSSE3 instructions (notably, PSHUFB which is used by QEMU's AES implementation). Do not bother checking it. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- util/cpuinfo-i386.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index ca74ef04f5..6d474a6259 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -38,8 +38,8 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); - /* Our AES support requires PSHUFB as well. */ - info |= ((c & bit_AES) && (c & bit_SSSE3) ? CPUINFO_AES : 0); + /* NOTE: our AES support requires SSSE3 (PSHUFB) as well. */ + info |= (c & bit_AES) ? CPUINFO_AES : 0; /* For AVX features, we must check available and usable. */ if ((c & bit_AVX) && (c & bit_OSXSAVE)) { From 45ccdbcb24baf99667997fac5cf60318e5e7db51 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 10:29:32 +0200 Subject: [PATCH 1160/2884] host/i386: assume presence of POPCNT QEMU now requires an x86-64-v2 host, which has the POPCNT instruction. Use it freely in TCG-generated code. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- host/include/i386/host/cpuinfo.h | 1 - tcg/i386/tcg-target.h | 5 ++--- util/cpuinfo-i386.c | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index 72f6fad61e..c1e94d75ce 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -11,7 +11,6 @@ #define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ #define CPUINFO_MOVBE (1u << 2) #define CPUINFO_LZCNT (1u << 3) -#define CPUINFO_POPCNT (1u << 4) #define CPUINFO_BMI1 (1u << 5) #define CPUINFO_BMI2 (1u << 6) #define CPUINFO_AVX1 (1u << 9) diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 2f67a97e05..ecc6982728 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -111,7 +111,6 @@ typedef enum { #endif #define have_bmi1 (cpuinfo & CPUINFO_BMI1) -#define have_popcnt (cpuinfo & CPUINFO_POPCNT) #define have_avx1 (cpuinfo & CPUINFO_AVX1) #define have_avx2 (cpuinfo & CPUINFO_AVX2) #define have_movbe (cpuinfo & CPUINFO_MOVBE) @@ -143,7 +142,7 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 have_popcnt +#define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 1 @@ -178,7 +177,7 @@ typedef enum { #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 have_popcnt +#define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index 6d474a6259..8f2694d88f 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -35,7 +35,6 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) __cpuid(1, a, b, c, d); info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); - info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); /* NOTE: our AES support requires SSSE3 (PSHUFB) as well. */ From da7c95920d027dbb00c6879c1da0216b19509191 Mon Sep 17 00:00:00 2001 From: Xinyu Li Date: Sun, 2 Jun 2024 18:09:04 +0800 Subject: [PATCH 1161/2884] target/i386: fix SSE and SSE2 feature check Features check of CPUID_SSE and CPUID_SSE2 should use cpuid_features, rather than cpuid_ext_features. Signed-off-by: Xinyu Li Reviewed-by: Zhao Liu Message-ID: <20240602100904.2137939-1-lixinyu20s@ict.ac.cn> Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 27dc1bb146..0ec849b003 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2041,9 +2041,9 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) case X86_FEAT_PCLMULQDQ: return (s->cpuid_ext_features & CPUID_EXT_PCLMULQDQ); case X86_FEAT_SSE: - return (s->cpuid_ext_features & CPUID_SSE); + return (s->cpuid_features & CPUID_SSE); case X86_FEAT_SSE2: - return (s->cpuid_ext_features & CPUID_SSE2); + return (s->cpuid_features & CPUID_SSE2); case X86_FEAT_SSE3: return (s->cpuid_ext_features & CPUID_EXT_SSE3); case X86_FEAT_SSSE3: From 0683fff1cf7d495e50955a946bf59f8b9c20c3f1 Mon Sep 17 00:00:00 2001 From: Xinyu Li Date: Sun, 2 Jun 2024 18:05:28 +0800 Subject: [PATCH 1162/2884] target/i386: fix memory opsize for Mov to/from Seg This commit fixes an issue with MOV instructions (0x8C and 0x8E) involving segment registers; MOV to segment register's source is 16-bit, while MOV from segment register has to explicitly set the memory operand size to 16 bits. Introduce a new flag X86_SPECIAL_Op0_Mw to handle this specification correctly. Signed-off-by: Xinyu Li Message-ID: <20240602100528.2135717-1-lixinyu20s@ict.ac.cn> Fixes: 5e9e21bcc4d ("target/i386: move 60-BF opcodes to new decoder", 2024-05-07) Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 13 +++++++++++-- target/i386/tcg/decode-new.h | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 0ec849b003..0ff0866e8f 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -202,6 +202,7 @@ #define avx_movx .special = X86_SPECIAL_AVXExtMov, #define sextT0 .special = X86_SPECIAL_SExtT0, #define zextT0 .special = X86_SPECIAL_ZExtT0, +#define op0_Mw .special = X86_SPECIAL_Op0_Mw, #define vex1 .vex_class = 1, #define vex1_rep3 .vex_class = 1, .vex_special = X86_VEX_REPScalar, @@ -1576,9 +1577,10 @@ static const X86OpEntry opcodes_root[256] = { [0x89] = X86_OP_ENTRY3(MOV, E,v, G,v, None, None), [0x8A] = X86_OP_ENTRY3(MOV, G,b, E,b, None, None), [0x8B] = X86_OP_ENTRY3(MOV, G,v, E,v, None, None), - [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None), + /* Missing in Table A-2: memory destination is always 16-bit. */ + [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None, op0_Mw), [0x8D] = X86_OP_ENTRY3(LEA, G,v, M,v, None, None, noseg), - [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,v, None, None), + [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,w, None, None), [0x8F] = X86_OP_GROUPw(group1A, E,v), [0x98] = X86_OP_ENTRY1(CBW, 0,v), /* rAX */ @@ -2514,6 +2516,13 @@ static void disas_insn(DisasContext *s, CPUState *cpu) s->override = -1; break; + case X86_SPECIAL_Op0_Mw: + assert(decode.op[0].unit == X86_OP_INT); + if (decode.op[0].has_ea) { + decode.op[0].ot = MO_16; + } + break; + default: break; } diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 51ef0e621b..1f90cf9640 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -203,6 +203,9 @@ typedef enum X86InsnSpecial { /* When loaded into s->T0, register operand 1 is zero/sign extended. */ X86_SPECIAL_SExtT0, X86_SPECIAL_ZExtT0, + + /* Memory operand size of MOV from segment register is MO_16 */ + X86_SPECIAL_Op0_Mw, } X86InsnSpecial; /* From f2c04bede384608a1ab4f66865dd0e2a433ed94c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 3 Jun 2024 16:07:23 +0800 Subject: [PATCH 1163/2884] target/i386/tcg: Fix RDPID feature check DisasContext.cpuid_ext_features indicates CPUID.01H.ECX. Use DisasContext.cpuid_7_0_ecx_features field to check RDPID feature bit (CPUID_7_0_ECX_RDPID). Fixes: 6750485bf42a ("target/i386: implement RDPID in TCG") Inspired-by: Xinyu Li Signed-off-by: Zhao Liu Message-ID: <20240603080723.1256662-1-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 6dedfe94c0..0486ab6911 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3199,7 +3199,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) goto illegal_op; } if (s->prefix & PREFIX_REPZ) { - if (!(s->cpuid_ext_features & CPUID_7_0_ECX_RDPID)) { + if (!(s->cpuid_7_0_ecx_features & CPUID_7_0_ECX_RDPID)) { goto illegal_op; } gen_helper_rdpid(s->T0, tcg_env); From 7604bbc2d87d153e65e38cf2d671a5a9a35917b1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Jun 2024 12:01:12 +0200 Subject: [PATCH 1164/2884] target/i386: fix xsave.flat from kvm-unit-tests xsave.flat checks that "executing the XSETBV instruction causes a general- protection fault (#GP) if ECX = 0 and EAX[2:1] has the value 10b". QEMU allows that option, so the test fails. Add the condition. Cc: qemu-stable@nongnu.org Fixes: 892544317fe ("target/i386: implement XSAVE and XRSTOR of AVX registers", 2022-10-18) Reported-by: Thomas Huth Signed-off-by: Paolo Bonzini --- target/i386/tcg/fpu_helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index e322293371..e1b850f3fc 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -3142,6 +3142,11 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) goto do_gpf; } + /* SSE can be disabled, but only if AVX is disabled too. */ + if ((mask & (XSTATE_SSE_MASK | XSTATE_YMM_MASK)) == XSTATE_YMM_MASK) { + goto do_gpf; + } + /* Disallow enabling unimplemented features. */ cpu_x86_cpuid(env, 0x0d, 0, &ena_lo, &dummy, &dummy, &ena_hi); ena = ((uint64_t)ena_hi << 32) | ena_lo; From ef7c70f020ca1fe9e7c98ea2cd9d6ba3c5714716 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Jun 2024 13:49:49 +0200 Subject: [PATCH 1165/2884] update-linux-headers: fix forwarding to asm-generic headers Afer commit 3efc75ad9d9 ("scripts/update-linux-headers.sh: Remove temporary directory inbetween", 2024-05-29), updating linux-headers/ results in errors such as cp: cannot stat '/tmp/tmp.1A1Eejh1UE/headers/include/asm/bitsperlong.h': No such file or directory because Loongarch does not have an asm/bitsperlong.h file and uses the generic version. Before commit 3efc75ad9d9, the missing file would incorrectly cause stale files to be included in linux-headers/. The files were never committed to qemu.git, but were wrong nevertheless. The build would just use the system version of the files, which is opposite to the idea of importing Linux header files into QEMU's tree. Create forwarding headers, resembling the ones that are generated during a kernel build by scripts/Makefile.asm-generic, if a file is only installed under include/asm-generic/. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- scripts/update-linux-headers.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 23afe8c08a..57a48837aa 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -118,7 +118,14 @@ for arch in $ARCHLIST; do rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" for header in kvm.h unistd.h bitsperlong.h mman.h; do - cp "$hdrdir/include/asm/$header" "$output/linux-headers/asm-$arch" + if test -f "$hdrdir/include/asm/$header"; then + cp "$hdrdir/include/asm/$header" "$output/linux-headers/asm-$arch" + elif test -f "$hdrdir/include/asm-generic/$header"; then + # not installed as , but used as such in kernel sources + cat <$output/linux-headers/asm-$arch/$header +#include +EOF + fi done if [ $arch = mips ]; then From b8116f4cbaa0f64bb07564f20b3b5219e23c8bff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Jun 2024 14:16:55 +0200 Subject: [PATCH 1166/2884] update-linux-headers: move pvpanic.h to correct directory Linux has , not . Use the same directory for QEMU's include/standard-headers/ copy. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- hw/misc/pvpanic-isa.c | 2 +- hw/misc/pvpanic-pci.c | 2 +- hw/misc/pvpanic.c | 2 +- include/standard-headers/{linux => misc}/pvpanic.h | 0 scripts/update-linux-headers.sh | 6 ++++-- 5 files changed, 7 insertions(+), 5 deletions(-) rename include/standard-headers/{linux => misc}/pvpanic.h (100%) diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index ccec50f61b..b4f84c4110 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -21,7 +21,7 @@ #include "hw/misc/pvpanic.h" #include "qom/object.h" #include "hw/isa/isa.h" -#include "standard-headers/linux/pvpanic.h" +#include "standard-headers/misc/pvpanic.h" #include "hw/acpi/acpi_aml_interface.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE) diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 83be95d0d2..4d44a881da 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -21,7 +21,7 @@ #include "hw/misc/pvpanic.h" #include "qom/object.h" #include "hw/pci/pci_device.h" -#include "standard-headers/linux/pvpanic.h" +#include "standard-headers/misc/pvpanic.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicPCIState, PVPANIC_PCI_DEVICE) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 1540e9091a..80289ecf5f 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -21,7 +21,7 @@ #include "hw/qdev-properties.h" #include "hw/misc/pvpanic.h" #include "qom/object.h" -#include "standard-headers/linux/pvpanic.h" +#include "standard-headers/misc/pvpanic.h" static void handle_event(int event) { diff --git a/include/standard-headers/linux/pvpanic.h b/include/standard-headers/misc/pvpanic.h similarity index 100% rename from include/standard-headers/linux/pvpanic.h rename to include/standard-headers/misc/pvpanic.h diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 57a48837aa..7e93acb3b5 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -231,10 +231,12 @@ for i in "$hdrdir"/include/linux/*virtio*.h \ "$hdrdir/include/linux/const.h" \ "$hdrdir/include/linux/kernel.h" \ "$hdrdir/include/linux/vhost_types.h" \ - "$hdrdir/include/linux/sysinfo.h" \ - "$hdrdir/include/misc/pvpanic.h"; do + "$hdrdir/include/linux/sysinfo.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done +mkdir -p "$output/include/standard-headers/misc" +cp_portable "$hdrdir/include/misc/pvpanic.h" \ + "$output/include/standard-headers/misc" mkdir -p "$output/include/standard-headers/drm" cp_portable "$hdrdir/include/drm/drm_fourcc.h" \ "$output/include/standard-headers/drm" From 5f69e42da5b40a2213f4db70ca461f554abca686 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:14 -0500 Subject: [PATCH 1167/2884] linux-headers: Update to current kvm/next This updates kernel headers to commit 6f627b425378 ("KVM: SVM: Add module parameter to enable SEV-SNP", 2024-05-12). The SNP host patches will be included in Linux 6.11, to be released next July. Also brings in an linux-headers/linux/vhost.h fix from v6.9-rc4. Co-developed-by: Michael Roth Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-3-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- linux-headers/asm-loongarch/kvm.h | 4 +++ linux-headers/asm-riscv/kvm.h | 1 + linux-headers/asm-x86/kvm.h | 52 ++++++++++++++++++++++++++++++- linux-headers/linux/vhost.h | 15 ++++----- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h index 109785922c..f9abef3823 100644 --- a/linux-headers/asm-loongarch/kvm.h +++ b/linux-headers/asm-loongarch/kvm.h @@ -17,6 +17,8 @@ #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 #define KVM_DIRTY_LOG_PAGE_OFFSET 64 +#define KVM_GUESTDBG_USE_SW_BP 0x00010000 + /* * for KVM_GET_REGS and KVM_SET_REGS */ @@ -72,6 +74,8 @@ struct kvm_fpu { #define KVM_REG_LOONGARCH_COUNTER (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 1) #define KVM_REG_LOONGARCH_VCPU_RESET (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 2) +/* Debugging: Special instruction for software breakpoint */ +#define KVM_REG_LOONGARCH_DEBUG_INST (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3) #define LOONGARCH_REG_SHIFT 3 #define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT)) diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index b1c503c295..e878e7cc39 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -167,6 +167,7 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_ZFA, KVM_RISCV_ISA_EXT_ZTSO, KVM_RISCV_ISA_EXT_ZACAS, + KVM_RISCV_ISA_EXT_SSCOFPMF, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 31c95c2dfe..1c8f918234 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -695,6 +695,11 @@ enum sev_cmd_id { /* Second time is the charm; improved versions of the above ioctls. */ KVM_SEV_INIT2, + /* SNP-specific commands */ + KVM_SEV_SNP_LAUNCH_START = 100, + KVM_SEV_SNP_LAUNCH_UPDATE, + KVM_SEV_SNP_LAUNCH_FINISH, + KVM_SEV_NR_MAX, }; @@ -709,7 +714,9 @@ struct kvm_sev_cmd { struct kvm_sev_init { __u64 vmsa_features; __u32 flags; - __u32 pad[9]; + __u16 ghcb_version; + __u16 pad1; + __u32 pad2[8]; }; struct kvm_sev_launch_start { @@ -820,6 +827,48 @@ struct kvm_sev_receive_update_data { __u32 pad2; }; +struct kvm_sev_snp_launch_start { + __u64 policy; + __u8 gosvw[16]; + __u16 flags; + __u8 pad0[6]; + __u64 pad1[4]; +}; + +/* Kept in sync with firmware values for simplicity. */ +#define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 +#define KVM_SEV_SNP_PAGE_TYPE_ZERO 0x3 +#define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED 0x4 +#define KVM_SEV_SNP_PAGE_TYPE_SECRETS 0x5 +#define KVM_SEV_SNP_PAGE_TYPE_CPUID 0x6 + +struct kvm_sev_snp_launch_update { + __u64 gfn_start; + __u64 uaddr; + __u64 len; + __u8 type; + __u8 pad0; + __u16 flags; + __u32 pad1; + __u64 pad2[4]; +}; + +#define KVM_SEV_SNP_ID_BLOCK_SIZE 96 +#define KVM_SEV_SNP_ID_AUTH_SIZE 4096 +#define KVM_SEV_SNP_FINISH_DATA_SIZE 32 + +struct kvm_sev_snp_launch_finish { + __u64 id_block_uaddr; + __u64 id_auth_uaddr; + __u8 id_block_en; + __u8 auth_key_en; + __u8 vcek_disabled; + __u8 host_data[KVM_SEV_SNP_FINISH_DATA_SIZE]; + __u8 pad0[3]; + __u16 flags; + __u64 pad1[4]; +}; + #define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0) #define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1) @@ -870,5 +919,6 @@ struct kvm_hyperv_eventfd { #define KVM_X86_SW_PROTECTED_VM 1 #define KVM_X86_SEV_VM 2 #define KVM_X86_SEV_ES_VM 3 +#define KVM_X86_SNP_VM 4 #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index bea6973906..b95dd84eef 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -179,12 +179,6 @@ /* Get the config size */ #define VHOST_VDPA_GET_CONFIG_SIZE _IOR(VHOST_VIRTIO, 0x79, __u32) -/* Get the count of all virtqueues */ -#define VHOST_VDPA_GET_VQS_COUNT _IOR(VHOST_VIRTIO, 0x80, __u32) - -/* Get the number of virtqueue groups. */ -#define VHOST_VDPA_GET_GROUP_NUM _IOR(VHOST_VIRTIO, 0x81, __u32) - /* Get the number of address spaces. */ #define VHOST_VDPA_GET_AS_NUM _IOR(VHOST_VIRTIO, 0x7A, unsigned int) @@ -228,10 +222,17 @@ #define VHOST_VDPA_GET_VRING_DESC_GROUP _IOWR(VHOST_VIRTIO, 0x7F, \ struct vhost_vring_state) + +/* Get the count of all virtqueues */ +#define VHOST_VDPA_GET_VQS_COUNT _IOR(VHOST_VIRTIO, 0x80, __u32) + +/* Get the number of virtqueue groups. */ +#define VHOST_VDPA_GET_GROUP_NUM _IOR(VHOST_VIRTIO, 0x81, __u32) + /* Get the queue size of a specific virtqueue. * userspace set the vring index in vhost_vring_state.index * kernel set the queue size in vhost_vring_state.num */ -#define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x80, \ +#define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ struct vhost_vring_state) #endif From aa274c33c39e7de981dc195abe60e1a246c9d248 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Jun 2024 14:25:06 +0200 Subject: [PATCH 1168/2884] update-linux-headers: import linux/kvm_para.h header Right now QEMU is importing arch/x86/include/uapi/asm/kvm_para.h because it includes definitions for kvmclock and for KVM CPUID bits. However, other definitions for KVM hypercall values and return codes are included in include/uapi/linux/kvm_para.h and they will be used by SEV-SNP. To ensure that it is possible to include both and "standard-headers/asm-x86/kvm_para.h" without conflicts, provide linux/kvm_para.h as a portable header too, and forward linux-headers/ files to those in include/standard-headers. Note that will include architecture-specific definitions as well, but "standard-headers/linux/kvm_para.h" will not because it can be used in architecture-independent files. This could easily be extended to other architectures, but right now they do not need any symbol in their specific kvm_para.h files. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- include/standard-headers/linux/kvm_para.h | 38 +++++++++++++++++++++++ linux-headers/asm-x86/kvm_para.h | 1 + linux-headers/linux/kvm_para.h | 2 ++ scripts/update-linux-headers.sh | 22 ++++++++++++- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 include/standard-headers/linux/kvm_para.h create mode 100644 linux-headers/asm-x86/kvm_para.h create mode 100644 linux-headers/linux/kvm_para.h diff --git a/include/standard-headers/linux/kvm_para.h b/include/standard-headers/linux/kvm_para.h new file mode 100644 index 0000000000..015c166302 --- /dev/null +++ b/include/standard-headers/linux/kvm_para.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_KVM_PARA_H +#define __LINUX_KVM_PARA_H + +/* + * This header file provides a method for making a hypercall to the host + * Architectures should define: + * - kvm_hypercall0, kvm_hypercall1... + * - kvm_arch_para_features + * - kvm_para_available + */ + +/* Return values for hypercalls */ +#define KVM_ENOSYS 1000 +#define KVM_EFAULT EFAULT +#define KVM_EINVAL EINVAL +#define KVM_E2BIG E2BIG +#define KVM_EPERM EPERM +#define KVM_EOPNOTSUPP 95 + +#define KVM_HC_VAPIC_POLL_IRQ 1 +#define KVM_HC_MMU_OP 2 +#define KVM_HC_FEATURES 3 +#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 +#define KVM_HC_KICK_CPU 5 +#define KVM_HC_MIPS_GET_CLOCK_FREQ 6 +#define KVM_HC_MIPS_EXIT_VM 7 +#define KVM_HC_MIPS_CONSOLE_OUTPUT 8 +#define KVM_HC_CLOCK_PAIRING 9 +#define KVM_HC_SEND_IPI 10 +#define KVM_HC_SCHED_YIELD 11 +#define KVM_HC_MAP_GPA_RANGE 12 + +/* + * hypercalls use architecture specific + */ + +#endif /* __LINUX_KVM_PARA_H */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h new file mode 100644 index 0000000000..1d3e0e0b07 --- /dev/null +++ b/linux-headers/asm-x86/kvm_para.h @@ -0,0 +1 @@ +#include "standard-headers/asm-x86/kvm_para.h" diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h new file mode 100644 index 0000000000..6a1e672259 --- /dev/null +++ b/linux-headers/linux/kvm_para.h @@ -0,0 +1,2 @@ +#include "standard-headers/linux/kvm_para.h" +#include diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 7e93acb3b5..c34ac6454e 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -63,6 +63,7 @@ cp_portable() { -e 'linux/kernel' \ -e 'linux/sysinfo' \ -e 'asm/setup_data.h' \ + -e 'asm/kvm_para.h' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -70,6 +71,15 @@ cp_portable() { fi header=$(basename "$f"); + + if test -z "$arch"; then + # Let users of include/standard-headers/linux/ headers pick the + # asm-* header that they care about + arch_cmd='/]*\)>/d' + else + arch_cmd='s/]*\)>/"standard-headers\/asm-'$arch'\/\1"/' + fi + sed -e 's/__aligned_u64/__u64 __attribute__((aligned(8)))/g' \ -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/u\([0-9][0-9]*\)/uint\1_t/g' \ @@ -78,7 +88,7 @@ cp_portable() { -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/"\(input-event-codes\.h\)"/"standard-headers\/linux\/\1"/' \ -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ - -e 's/]*\)>/"standard-headers\/asm-'$arch'\/\1"/' \ + -e "$arch_cmd" \ -e 's/__bitwise//' \ -e 's/__attribute__((packed))/QEMU_PACKED/' \ -e 's/__inline__/inline/' \ @@ -158,7 +168,12 @@ EOF cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" cp "$hdrdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + cp_portable "$hdrdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" + cat <$output/linux-headers/asm-$arch/kvm_para.h +#include "standard-headers/asm-$arch/kvm_para.h" +EOF + # Remove everything except the macros from bootparam.h avoiding the # unnecessary import of several video/ist/etc headers sed -e '/__ASSEMBLY__/,/__ASSEMBLY__/d' \ @@ -208,6 +223,10 @@ if [ -d "$linux/LICENSES" ]; then done fi +cat <$output/linux-headers/linux/kvm_para.h +#include "standard-headers/linux/kvm_para.h" +#include +EOF cat <$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF @@ -230,6 +249,7 @@ for i in "$hdrdir"/include/linux/*virtio*.h \ "$hdrdir/include/linux/ethtool.h" \ "$hdrdir/include/linux/const.h" \ "$hdrdir/include/linux/kernel.h" \ + "$hdrdir/include/linux/kvm_para.h" \ "$hdrdir/include/linux/vhost_types.h" \ "$hdrdir/include/linux/sysinfo.h"; do cp_portable "$i" "$output/include/standard-headers/linux" From dc0d28ca46c0e7ee3c055ad4da24022995bd3765 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 13:29:53 +0200 Subject: [PATCH 1169/2884] machine: allow early use of machine_require_guest_memfd Ask the ConfidentialGuestSupport object whether to use guest_memfd for KVM-backend private memory. This bool can be set in instance_init (or user_complete) so that it is available when the machine is created. Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 2 +- include/exec/confidential-guest-support.h | 5 +++++ include/hw/boards.h | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 17292b13e6..77a356f232 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1216,7 +1216,7 @@ bool machine_mem_merge(MachineState *machine) bool machine_require_guest_memfd(MachineState *machine) { - return machine->require_guest_memfd; + return machine->cgs && machine->cgs->require_guest_memfd; } static char *cpu_slot_to_string(const CPUArchId *cpu) diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h index e5b188cffb..02dc4e518f 100644 --- a/include/exec/confidential-guest-support.h +++ b/include/exec/confidential-guest-support.h @@ -31,6 +31,11 @@ OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, struct ConfidentialGuestSupport { Object parent; + /* + * True if the machine should use guest_memfd for RAM. + */ + bool require_guest_memfd; + /* * ready: flag set by CGS initialization code once it's ready to * start executing instructions in a potentially-secure diff --git a/include/hw/boards.h b/include/hw/boards.h index 2fa800f11a..73ad319d7d 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -375,7 +375,6 @@ struct MachineState { char *dt_compatible; bool dump_guest_core; bool mem_merge; - bool require_guest_memfd; bool usb; bool usb_disabled; char *firmware; From 18c453409a3a84cf7b2c764c5a03fb429a73bbeb Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:13 -0500 Subject: [PATCH 1170/2884] i386/sev: Replace error_report with error_setg Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-2-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index d30b68c11e..67ed32e5ea 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -952,13 +952,13 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) if (sev_es_enabled()) { if (!kvm_kernel_irqchip_allowed()) { - error_report("%s: SEV-ES guests require in-kernel irqchip support", - __func__); + error_setg(errp, "%s: SEV-ES guests require in-kernel irqchip" + "support", __func__); goto err; } if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { - error_report("%s: guest policy requires SEV-ES, but " + error_setg(errp, "%s: guest policy requires SEV-ES, but " "host SEV-ES support unavailable", __func__); goto err; From 16dcf200dc951c1cde3e5b442457db5f690b8cf0 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:16 -0500 Subject: [PATCH 1171/2884] i386/sev: Introduce "sev-common" type to encapsulate common SEV state Currently all SEV/SEV-ES functionality is managed through a single 'sev-guest' QOM type. With upcoming support for SEV-SNP, taking this same approach won't work well since some of the properties/state managed by 'sev-guest' is not applicable to SEV-SNP, which will instead rely on a new QOM type with its own set of properties/state. To prepare for this, this patch moves common state into an abstract 'sev-common' parent type to encapsulate properties/state that are common to both SEV/SEV-ES and SEV-SNP, leaving only SEV/SEV-ES-specific properties/state in the current 'sev-guest' type. This should not affect current behavior or command-line options. As part of this patch, some related changes are also made: - a static 'sev_guest' variable is currently used to keep track of the 'sev-guest' instance. SEV-SNP would similarly introduce an 'sev_snp_guest' static variable. But these instances are now available via qdev_get_machine()->cgs, so switch to using that instead and drop the static variable. - 'sev_guest' is currently used as the name for the static variable holding a pointer to the 'sev-guest' instance. Re-purpose the name as a local variable referring the 'sev-guest' instance, and use that consistently throughout the code so it can be easily distinguished from sev-common/sev-snp-guest instances. - 'sev' is generally used as the name for local variables holding a pointer to the 'sev-guest' instance. In cases where that now points to common state, use the name 'sev_common'; in cases where that now points to state specific to 'sev-guest' instance, use the name 'sev_guest' In order to enable kernel-hashes for SNP, pull it from SevGuestProperties to its parent SevCommonProperties so it will be available for both SEV and SNP. Signed-off-by: Michael Roth Co-developed-by: Dov Murik Signed-off-by: Dov Murik Acked-by: Markus Armbruster (QAPI schema) Co-developed-by: Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-5-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- qapi/qom.json | 40 ++-- target/i386/sev.c | 493 ++++++++++++++++++++++++++-------------------- target/i386/sev.h | 3 + 3 files changed, 303 insertions(+), 233 deletions(-) diff --git a/qapi/qom.json b/qapi/qom.json index 38dde6d785..056b38f491 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -875,20 +875,12 @@ 'data': { '*filename': 'str' } } ## -# @SevGuestProperties: +# @SevCommonProperties: # -# Properties for sev-guest objects. +# Properties common to objects that are derivatives of sev-common. # # @sev-device: SEV device to use (default: "/dev/sev") # -# @dh-cert-file: guest owners DH certificate (encoded with base64) -# -# @session-file: guest owners session parameters (encoded with base64) -# -# @policy: SEV policy value (default: 0x1) -# -# @handle: SEV firmware handle (default: 0) -# # @cbitpos: C-bit location in page table entry (default: 0) # # @reduced-phys-bits: number of bits in physical addresses that become @@ -898,6 +890,27 @@ # designated guest firmware page for measured boot with -kernel # (default: false) (since 6.2) # +# Since: 9.1 +## +{ 'struct': 'SevCommonProperties', + 'data': { '*sev-device': 'str', + '*cbitpos': 'uint32', + 'reduced-phys-bits': 'uint32', + '*kernel-hashes': 'bool' } } + +## +# @SevGuestProperties: +# +# Properties for sev-guest objects. +# +# @dh-cert-file: guest owners DH certificate (encoded with base64) +# +# @session-file: guest owners session parameters (encoded with base64) +# +# @policy: SEV policy value (default: 0x1) +# +# @handle: SEV firmware handle (default: 0) +# # @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating the VM. # The newer KVM_SEV_INIT2 interface syncs additional vCPU # state when initializing the VMSA structures, which will @@ -909,14 +922,11 @@ # Since: 2.12 ## { 'struct': 'SevGuestProperties', - 'data': { '*sev-device': 'str', - '*dh-cert-file': 'str', + 'base': 'SevCommonProperties', + 'data': { '*dh-cert-file': 'str', '*session-file': 'str', '*policy': 'uint32', '*handle': 'uint32', - '*cbitpos': 'uint32', - 'reduced-phys-bits': 'uint32', - '*kernel-hashes': 'bool', '*legacy-vm-type': 'bool' } } ## diff --git a/target/i386/sev.c b/target/i386/sev.c index 67ed32e5ea..33e606eea0 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -40,9 +40,36 @@ #include "hw/i386/pc.h" #include "exec/address-spaces.h" -#define TYPE_SEV_GUEST "sev-guest" -OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) +OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) +OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) +struct SevCommonState { + X86ConfidentialGuest parent_obj; + + int kvm_type; + + /* configuration parameters */ + char *sev_device; + uint32_t cbitpos; + uint32_t reduced_phys_bits; + bool kernel_hashes; + + /* runtime state */ + uint8_t api_major; + uint8_t api_minor; + uint8_t build_id; + int sev_fd; + SevState state; + + uint32_t reset_cs; + uint32_t reset_ip; + bool reset_data_valid; +}; + +struct SevCommonStateClass { + X86ConfidentialGuestClass parent_class; + +}; /** * SevGuestState: @@ -55,32 +82,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) * -machine ...,memory-encryption=sev0 */ struct SevGuestState { - X86ConfidentialGuest parent_obj; - - int kvm_type; + SevCommonState parent_obj; + gchar *measurement; /* configuration parameters */ - char *sev_device; + uint32_t handle; uint32_t policy; char *dh_cert_file; char *session_file; - uint32_t cbitpos; - uint32_t reduced_phys_bits; - bool kernel_hashes; bool legacy_vm_type; - - /* runtime state */ - uint32_t handle; - uint8_t api_major; - uint8_t api_minor; - uint8_t build_id; - int sev_fd; - SevState state; - gchar *measurement; - - uint32_t reset_cs; - uint32_t reset_ip; - bool reset_data_valid; }; #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ @@ -128,7 +138,6 @@ typedef struct QEMU_PACKED PaddedSevHashTable { QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); -static SevGuestState *sev_guest; static Error *sev_mig_blocker; static const char *const sev_fw_errlist[] = { @@ -209,21 +218,21 @@ fw_error_to_str(int code) } static bool -sev_check_state(const SevGuestState *sev, SevState state) +sev_check_state(const SevCommonState *sev_common, SevState state) { - assert(sev); - return sev->state == state ? true : false; + assert(sev_common); + return sev_common->state == state ? true : false; } static void -sev_set_guest_state(SevGuestState *sev, SevState new_state) +sev_set_guest_state(SevCommonState *sev_common, SevState new_state) { assert(new_state < SEV_STATE__MAX); - assert(sev); + assert(sev_common); - trace_kvm_sev_change_state(SevState_str(sev->state), + trace_kvm_sev_change_state(SevState_str(sev_common->state), SevState_str(new_state)); - sev->state = new_state; + sev_common->state = new_state; } static void @@ -290,121 +299,61 @@ static struct RAMBlockNotifier sev_ram_notifier = { .ram_block_removed = sev_ram_block_removed, }; -static void -sev_guest_finalize(Object *obj) -{ -} - -static char * -sev_guest_get_session_file(Object *obj, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - return s->session_file ? g_strdup(s->session_file) : NULL; -} - -static void -sev_guest_set_session_file(Object *obj, const char *value, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - s->session_file = g_strdup(value); -} - -static char * -sev_guest_get_dh_cert_file(Object *obj, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - return g_strdup(s->dh_cert_file); -} - -static void -sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - s->dh_cert_file = g_strdup(value); -} - -static char * -sev_guest_get_sev_device(Object *obj, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - return g_strdup(sev->sev_device); -} - -static void -sev_guest_set_sev_device(Object *obj, const char *value, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - sev->sev_device = g_strdup(value); -} - -static bool sev_guest_get_kernel_hashes(Object *obj, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - return sev->kernel_hashes; -} - -static void sev_guest_set_kernel_hashes(Object *obj, bool value, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - sev->kernel_hashes = value; -} - -static bool sev_guest_get_legacy_vm_type(Object *obj, Error **errp) -{ - return SEV_GUEST(obj)->legacy_vm_type; -} - -static void sev_guest_set_legacy_vm_type(Object *obj, bool value, Error **errp) -{ - SEV_GUEST(obj)->legacy_vm_type = value; -} - bool sev_enabled(void) { - return !!sev_guest; + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + + return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON); } bool sev_es_enabled(void) { - return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES); + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + + return sev_enabled() && (SEV_GUEST(cgs)->policy & SEV_POLICY_ES); } uint32_t sev_get_cbit_position(void) { - return sev_guest ? sev_guest->cbitpos : 0; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + + return sev_common ? sev_common->cbitpos : 0; } uint32_t sev_get_reduced_phys_bits(void) { - return sev_guest ? sev_guest->reduced_phys_bits : 0; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + + return sev_common ? sev_common->reduced_phys_bits : 0; } static SevInfo *sev_get_info(void) { SevInfo *info; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevGuestState *sev_guest = + (SevGuestState *)object_dynamic_cast(OBJECT(sev_common), + TYPE_SEV_GUEST); info = g_new0(SevInfo, 1); info->enabled = sev_enabled(); if (info->enabled) { - info->api_major = sev_guest->api_major; - info->api_minor = sev_guest->api_minor; - info->build_id = sev_guest->build_id; - info->policy = sev_guest->policy; - info->state = sev_guest->state; - info->handle = sev_guest->handle; + if (sev_guest) { + info->handle = sev_guest->handle; + } + info->api_major = sev_common->api_major; + info->api_minor = sev_common->api_minor; + info->build_id = sev_common->build_id; + info->state = sev_common->state; + /* we only report the lower 32-bits of policy for SNP, ok for now... */ + info->policy = + (uint32_t)object_property_get_uint(OBJECT(sev_common), + "policy", NULL); } return info; @@ -530,6 +479,8 @@ static SevCapability *sev_get_capabilities(Error **errp) size_t pdh_len = 0, cert_chain_len = 0, cpu0_id_len = 0; uint32_t ebx; int fd; + SevCommonState *sev_common; + char *sev_device; if (!kvm_enabled()) { error_setg(errp, "KVM not enabled"); @@ -540,12 +491,21 @@ static SevCapability *sev_get_capabilities(Error **errp) return NULL; } - fd = open(DEFAULT_SEV_DEVICE, O_RDWR); + sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + if (!sev_common) { + error_setg(errp, "SEV is not configured"); + } + + sev_device = object_property_get_str(OBJECT(sev_common), "sev-device", + &error_abort); + fd = open(sev_device, O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "SEV: Failed to open %s", DEFAULT_SEV_DEVICE); + g_free(sev_device); return NULL; } + g_free(sev_device); if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, &cert_chain_data, &cert_chain_len, errp)) { @@ -588,7 +548,7 @@ static SevAttestationReport *sev_get_attestation_report(const char *mnonce, { struct kvm_sev_attestation_report input = {}; SevAttestationReport *report = NULL; - SevGuestState *sev = sev_guest; + SevCommonState *sev_common; g_autofree guchar *data = NULL; g_autofree guchar *buf = NULL; gsize len; @@ -613,8 +573,10 @@ static SevAttestationReport *sev_get_attestation_report(const char *mnonce, return NULL; } + sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + /* Query the report length */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, &input, &err); if (ret < 0) { if (err != SEV_RET_INVALID_LEN) { @@ -630,7 +592,7 @@ static SevAttestationReport *sev_get_attestation_report(const char *mnonce, memcpy(input.mnonce, buf, sizeof(input.mnonce)); /* Query the report */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, &input, &err); if (ret) { error_setg_errno(errp, errno, "SEV: Failed to get attestation report" @@ -670,26 +632,27 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len) } static int -sev_launch_start(SevGuestState *sev) +sev_launch_start(SevGuestState *sev_guest) { gsize sz; int ret = 1; int fw_error, rc; struct kvm_sev_launch_start start = { - .handle = sev->handle, .policy = sev->policy + .handle = sev_guest->handle, .policy = sev_guest->policy }; guchar *session = NULL, *dh_cert = NULL; + SevCommonState *sev_common = SEV_COMMON(sev_guest); - if (sev->session_file) { - if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { + if (sev_guest->session_file) { + if (sev_read_file_base64(sev_guest->session_file, &session, &sz) < 0) { goto out; } start.session_uaddr = (unsigned long)session; start.session_len = sz; } - if (sev->dh_cert_file) { - if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { + if (sev_guest->dh_cert_file) { + if (sev_read_file_base64(sev_guest->dh_cert_file, &dh_cert, &sz) < 0) { goto out; } start.dh_uaddr = (unsigned long)dh_cert; @@ -697,15 +660,15 @@ sev_launch_start(SevGuestState *sev) } trace_kvm_sev_launch_start(start.policy, session, dh_cert); - rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); if (rc < 0) { error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); goto out; } - sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE); - sev->handle = start.handle; + sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE); + sev_guest->handle = start.handle; ret = 0; out: @@ -715,7 +678,7 @@ out: } static int -sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) +sev_launch_update_data(SevGuestState *sev_guest, uint8_t *addr, uint64_t len) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -727,7 +690,7 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) update.uaddr = (uintptr_t)addr; update.len = len; trace_kvm_sev_launch_update_data(addr, len); - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, + ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", @@ -738,11 +701,12 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) } static int -sev_launch_update_vmsa(SevGuestState *sev) +sev_launch_update_vmsa(SevGuestState *sev_guest) { int ret, fw_error; - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); + ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, + NULL, &fw_error); if (ret) { error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); @@ -754,18 +718,19 @@ sev_launch_update_vmsa(SevGuestState *sev) static void sev_launch_get_measure(Notifier *notifier, void *unused) { - SevGuestState *sev = sev_guest; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevGuestState *sev_guest = SEV_GUEST(sev_common); int ret, error; g_autofree guchar *data = NULL; struct kvm_sev_launch_measure measurement = {}; - if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) { + if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { return; } if (sev_es_enabled()) { /* measure all the VM save areas before getting launch_measure */ - ret = sev_launch_update_vmsa(sev); + ret = sev_launch_update_vmsa(sev_guest); if (ret) { exit(1); } @@ -773,7 +738,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) } /* query the measurement blob length */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (!measurement.len) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", @@ -785,7 +750,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) measurement.uaddr = (unsigned long)data; /* get the measurement blob */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (ret) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", @@ -793,17 +758,19 @@ sev_launch_get_measure(Notifier *notifier, void *unused) return; } - sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET); + sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_SECRET); /* encode the measurement value and emit the event */ - sev->measurement = g_base64_encode(data, measurement.len); - trace_kvm_sev_launch_measurement(sev->measurement); + sev_guest->measurement = g_base64_encode(data, measurement.len); + trace_kvm_sev_launch_measurement(sev_guest->measurement); } static char *sev_get_launch_measurement(void) { + SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); + if (sev_guest && - sev_guest->state >= SEV_STATE_LAUNCH_SECRET) { + SEV_COMMON(sev_guest)->state >= SEV_STATE_LAUNCH_SECRET) { return g_strdup(sev_guest->measurement); } @@ -832,19 +799,20 @@ static Notifier sev_machine_done_notify = { }; static void -sev_launch_finish(SevGuestState *sev) +sev_launch_finish(SevGuestState *sev_guest) { int ret, error; trace_kvm_sev_launch_finish(); - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); + ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, + &error); if (ret) { error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(error)); exit(1); } - sev_set_guest_state(sev, SEV_STATE_RUNNING); + sev_set_guest_state(SEV_COMMON(sev_guest), SEV_STATE_RUNNING); /* add migration blocker */ error_setg(&sev_mig_blocker, @@ -855,38 +823,40 @@ sev_launch_finish(SevGuestState *sev) static void sev_vm_state_change(void *opaque, bool running, RunState state) { - SevGuestState *sev = opaque; + SevCommonState *sev_common = opaque; if (running) { - if (!sev_check_state(sev, SEV_STATE_RUNNING)) { - sev_launch_finish(sev); + if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) { + sev_launch_finish(SEV_GUEST(sev_common)); } } } static int sev_kvm_type(X86ConfidentialGuest *cg) { - SevGuestState *sev = SEV_GUEST(cg); + SevCommonState *sev_common = SEV_COMMON(cg); + SevGuestState *sev_guest = SEV_GUEST(sev_common); int kvm_type; - if (sev->kvm_type != -1) { + if (sev_common->kvm_type != -1) { goto out; } - kvm_type = (sev->policy & SEV_POLICY_ES) ? KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; - if (kvm_is_vm_type_supported(kvm_type) && !sev->legacy_vm_type) { - sev->kvm_type = kvm_type; + kvm_type = (sev_guest->policy & SEV_POLICY_ES) ? + KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; + if (kvm_is_vm_type_supported(kvm_type) && !sev_guest->legacy_vm_type) { + sev_common->kvm_type = kvm_type; } else { - sev->kvm_type = KVM_X86_DEFAULT_VM; + sev_common->kvm_type = KVM_X86_DEFAULT_VM; } out: - return sev->kvm_type; + return sev_common->kvm_type; } static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - SevGuestState *sev = SEV_GUEST(cgs); + SevCommonState *sev_common = SEV_COMMON(cgs); char *devname; int ret, fw_error, cmd; uint32_t ebx; @@ -899,8 +869,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return -1; } - sev_guest = sev; - sev->state = SEV_STATE_UNINIT; + sev_common->state = SEV_STATE_UNINIT; host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); host_cbitpos = ebx & 0x3f; @@ -910,9 +879,9 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * register of CPUID 0x8000001F. No need to verify the range as the * comparison against the host value accomplishes that. */ - if (host_cbitpos != sev->cbitpos) { + if (host_cbitpos != sev_common->cbitpos) { error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", - __func__, host_cbitpos, sev->cbitpos); + __func__, host_cbitpos, sev_common->cbitpos); goto err; } @@ -921,16 +890,17 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * the EBX register of CPUID 0x8000001F, so verify the supplied value * is in the range of 1 to 63. */ - if (sev->reduced_phys_bits < 1 || sev->reduced_phys_bits > 63) { + if (sev_common->reduced_phys_bits < 1 || + sev_common->reduced_phys_bits > 63) { error_setg(errp, "%s: reduced_phys_bits check failed," " it should be in the range of 1 to 63, requested '%d'", - __func__, sev->reduced_phys_bits); + __func__, sev_common->reduced_phys_bits); goto err; } - devname = object_property_get_str(OBJECT(sev), "sev-device", NULL); - sev->sev_fd = open(devname, O_RDWR); - if (sev->sev_fd < 0) { + devname = object_property_get_str(OBJECT(sev_common), "sev-device", NULL); + sev_common->sev_fd = open(devname, O_RDWR); + if (sev_common->sev_fd < 0) { error_setg(errp, "%s: Failed to open %s '%s'", __func__, devname, strerror(errno)); g_free(devname); @@ -938,7 +908,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } g_free(devname); - ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status, + ret = sev_platform_ioctl(sev_common->sev_fd, SEV_PLATFORM_STATUS, &status, &fw_error); if (ret) { error_setg(errp, "%s: failed to get platform status ret=%d " @@ -946,9 +916,9 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) fw_error_to_str(fw_error)); goto err; } - sev->build_id = status.build; - sev->api_major = status.api_major; - sev->api_minor = status.api_minor; + sev_common->build_id = status.build; + sev_common->api_major = status.api_major; + sev_common->api_minor = status.api_minor; if (sev_es_enabled()) { if (!kvm_kernel_irqchip_allowed()) { @@ -966,14 +936,14 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } trace_kvm_sev_init(); - if (sev_kvm_type(X86_CONFIDENTIAL_GUEST(sev)) == KVM_X86_DEFAULT_VM) { + if (sev_kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) { cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT; - ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); + ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error); } else { struct kvm_sev_init args = { 0 }; - ret = sev_ioctl(sev->sev_fd, KVM_SEV_INIT2, &args, &fw_error); + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); } if (ret) { @@ -982,7 +952,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) goto err; } - ret = sev_launch_start(sev); + sev_launch_start(SEV_GUEST(sev_common)); if (ret) { error_setg(errp, "%s: failed to create encryption context", __func__); goto err; @@ -990,13 +960,12 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) ram_block_notifier_add(&sev_ram_notifier); qemu_add_machine_init_done_notifier(&sev_machine_done_notify); - qemu_add_vm_change_state_handler(sev_vm_state_change, sev); + qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common); cgs->ready = true; return 0; err: - sev_guest = NULL; ram_block_discard_disable(false); return -1; } @@ -1004,13 +973,15 @@ err: int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) { - if (!sev_guest) { + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + + if (!sev_common) { return 0; } /* if SEV is in update state then encrypt the data else do nothing */ - if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) { - int ret = sev_launch_update_data(sev_guest, ptr, len); + if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { + int ret = sev_launch_update_data(SEV_GUEST(sev_common), ptr, len); if (ret < 0) { error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; @@ -1030,16 +1001,17 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, void *hva; gsize hdr_sz = 0, data_sz = 0; MemoryRegion *mr = NULL; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - if (!sev_guest) { + if (!sev_common) { error_setg(errp, "SEV not enabled for guest"); return 1; } /* secret can be injected only in this state */ - if (!sev_check_state(sev_guest, SEV_STATE_LAUNCH_SECRET)) { + if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_SECRET)) { error_setg(errp, "SEV: Not in correct state. (LSECRET) %x", - sev_guest->state); + sev_common->state); return 1; } @@ -1073,7 +1045,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, trace_kvm_sev_launch_secret(gpa, input.guest_uaddr, input.trans_uaddr, input.trans_len); - ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_LAUNCH_SECRET, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_SECRET, &input, &error); if (ret) { error_setg(errp, "SEV: failed to inject secret ret=%d fw_error=%d '%s'", @@ -1180,9 +1152,10 @@ void sev_es_set_reset_vector(CPUState *cpu) { X86CPU *x86; CPUX86State *env; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); /* Only update if we have valid reset information */ - if (!sev_guest || !sev_guest->reset_data_valid) { + if (!sev_common || !sev_common->reset_data_valid) { return; } @@ -1194,11 +1167,11 @@ void sev_es_set_reset_vector(CPUState *cpu) x86 = X86_CPU(cpu); env = &x86->env; - cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_guest->reset_cs, 0xffff, + cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_common->reset_cs, 0xffff, DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - env->eip = sev_guest->reset_ip; + env->eip = sev_common->reset_ip; } int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) @@ -1206,6 +1179,7 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) CPUState *cpu; uint32_t addr; int ret; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_es_enabled()) { return 0; @@ -1219,9 +1193,9 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) } if (addr) { - sev_guest->reset_cs = addr & 0xffff0000; - sev_guest->reset_ip = addr & 0x0000ffff; - sev_guest->reset_data_valid = true; + sev_common->reset_cs = addr & 0xffff0000; + sev_common->reset_ip = addr & 0x0000ffff; + sev_common->reset_data_valid = true; CPU_FOREACH(cpu) { sev_es_set_reset_vector(cpu); @@ -1267,12 +1241,13 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) hwaddr mapped_len = sizeof(*padded_ht); MemTxAttrs attrs = { 0 }; bool ret = true; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); /* * Only add the kernel hashes if the sev-guest configuration explicitly * stated kernel-hashes=on. */ - if (!sev_guest->kernel_hashes) { + if (!sev_common->kernel_hashes) { return false; } @@ -1363,8 +1338,30 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) return ret; } +static char * +sev_common_get_sev_device(Object *obj, Error **errp) +{ + return g_strdup(SEV_COMMON(obj)->sev_device); +} + static void -sev_guest_class_init(ObjectClass *oc, void *data) +sev_common_set_sev_device(Object *obj, const char *value, Error **errp) +{ + SEV_COMMON(obj)->sev_device = g_strdup(value); +} + +static bool sev_common_get_kernel_hashes(Object *obj, Error **errp) +{ + return SEV_COMMON(obj)->kernel_hashes; +} + +static void sev_common_set_kernel_hashes(Object *obj, bool value, Error **errp) +{ + SEV_COMMON(obj)->kernel_hashes = value; +} + +static void +sev_common_class_init(ObjectClass *oc, void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); @@ -1373,10 +1370,87 @@ sev_guest_class_init(ObjectClass *oc, void *data) x86_klass->kvm_type = sev_kvm_type; object_class_property_add_str(oc, "sev-device", - sev_guest_get_sev_device, - sev_guest_set_sev_device); + sev_common_get_sev_device, + sev_common_set_sev_device); object_class_property_set_description(oc, "sev-device", "SEV device to use"); + object_class_property_add_bool(oc, "kernel-hashes", + sev_common_get_kernel_hashes, + sev_common_set_kernel_hashes); + object_class_property_set_description(oc, "kernel-hashes", + "add kernel hashes to guest firmware for measured Linux boot"); +} + +static void +sev_common_instance_init(Object *obj) +{ + SevCommonState *sev_common = SEV_COMMON(obj); + + sev_common->kvm_type = -1; + + sev_common->sev_device = g_strdup(DEFAULT_SEV_DEVICE); + + object_property_add_uint32_ptr(obj, "cbitpos", &sev_common->cbitpos, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint32_ptr(obj, "reduced-phys-bits", + &sev_common->reduced_phys_bits, + OBJ_PROP_FLAG_READWRITE); +} + +/* sev guest info common to sev/sev-es/sev-snp */ +static const TypeInfo sev_common_info = { + .parent = TYPE_X86_CONFIDENTIAL_GUEST, + .name = TYPE_SEV_COMMON, + .instance_size = sizeof(SevCommonState), + .instance_init = sev_common_instance_init, + .class_size = sizeof(SevCommonStateClass), + .class_init = sev_common_class_init, + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static char * +sev_guest_get_dh_cert_file(Object *obj, Error **errp) +{ + return g_strdup(SEV_GUEST(obj)->dh_cert_file); +} + +static void +sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) +{ + SEV_GUEST(obj)->dh_cert_file = g_strdup(value); +} + +static char * +sev_guest_get_session_file(Object *obj, Error **errp) +{ + SevGuestState *sev_guest = SEV_GUEST(obj); + + return sev_guest->session_file ? g_strdup(sev_guest->session_file) : NULL; +} + +static void +sev_guest_set_session_file(Object *obj, const char *value, Error **errp) +{ + SEV_GUEST(obj)->session_file = g_strdup(value); +} + +static bool sev_guest_get_legacy_vm_type(Object *obj, Error **errp) +{ + return SEV_GUEST(obj)->legacy_vm_type; +} + +static void sev_guest_set_legacy_vm_type(Object *obj, bool value, Error **errp) +{ + SEV_GUEST(obj)->legacy_vm_type = value; +} + +static void +sev_guest_class_init(ObjectClass *oc, void *data) +{ object_class_property_add_str(oc, "dh-cert-file", sev_guest_get_dh_cert_file, sev_guest_set_dh_cert_file); @@ -1387,11 +1461,6 @@ sev_guest_class_init(ObjectClass *oc, void *data) sev_guest_set_session_file); object_class_property_set_description(oc, "session-file", "guest owners session parameters (encoded with base64)"); - object_class_property_add_bool(oc, "kernel-hashes", - sev_guest_get_kernel_hashes, - sev_guest_set_kernel_hashes); - object_class_property_set_description(oc, "kernel-hashes", - "add kernel hashes to guest firmware for measured Linux boot"); object_class_property_add_bool(oc, "legacy-vm-type", sev_guest_get_legacy_vm_type, sev_guest_set_legacy_vm_type); @@ -1402,41 +1471,29 @@ sev_guest_class_init(ObjectClass *oc, void *data) static void sev_guest_instance_init(Object *obj) { - SevGuestState *sev = SEV_GUEST(obj); + SevGuestState *sev_guest = SEV_GUEST(obj); - sev->kvm_type = -1; - - sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); - sev->policy = DEFAULT_GUEST_POLICY; - object_property_add_uint32_ptr(obj, "policy", &sev->policy, + sev_guest->policy = DEFAULT_GUEST_POLICY; + object_property_add_uint32_ptr(obj, "handle", &sev_guest->handle, OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "handle", &sev->handle, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "reduced-phys-bits", - &sev->reduced_phys_bits, + object_property_add_uint32_ptr(obj, "policy", &sev_guest->policy, OBJ_PROP_FLAG_READWRITE); object_apply_compat_props(obj); } -/* sev guest info */ +/* guest info specific sev/sev-es */ static const TypeInfo sev_guest_info = { - .parent = TYPE_X86_CONFIDENTIAL_GUEST, + .parent = TYPE_SEV_COMMON, .name = TYPE_SEV_GUEST, .instance_size = sizeof(SevGuestState), - .instance_finalize = sev_guest_finalize, - .class_init = sev_guest_class_init, .instance_init = sev_guest_instance_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } - } + .class_init = sev_guest_class_init, }; static void sev_register_types(void) { + type_register_static(&sev_common_info); type_register_static(&sev_guest_info); } diff --git a/target/i386/sev.h b/target/i386/sev.h index 9e10d09539..668374eef3 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -20,6 +20,9 @@ #include "exec/confidential-guest-support.h" +#define TYPE_SEV_COMMON "sev-common" +#define TYPE_SEV_GUEST "sev-guest" + #define SEV_POLICY_NODBG 0x1 #define SEV_POLICY_NOKS 0x2 #define SEV_POLICY_ES 0x4 From 6600f1ac0c81cbe67faf048ea07f78542dea925f Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:17 -0500 Subject: [PATCH 1172/2884] i386/sev: Move sev_launch_update to separate class method When sev-snp-guest objects are introduced there will be a number of differences in how the launch data is handled compared to the existing sev-guest object. Move sev_launch_start() to a class method to make it easier to implement SNP-specific launch update functionality later. Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-6-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 33e606eea0..b2aa0d6f99 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -69,6 +69,8 @@ struct SevCommonState { struct SevCommonStateClass { X86ConfidentialGuestClass parent_class; + /* public */ + int (*launch_start)(SevCommonState *sev_common); }; /** @@ -632,16 +634,16 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len) } static int -sev_launch_start(SevGuestState *sev_guest) +sev_launch_start(SevCommonState *sev_common) { gsize sz; int ret = 1; int fw_error, rc; + SevGuestState *sev_guest = SEV_GUEST(sev_common); struct kvm_sev_launch_start start = { .handle = sev_guest->handle, .policy = sev_guest->policy }; guchar *session = NULL, *dh_cert = NULL; - SevCommonState *sev_common = SEV_COMMON(sev_guest); if (sev_guest->session_file) { if (sev_read_file_base64(sev_guest->session_file, &session, &sz) < 0) { @@ -862,6 +864,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) uint32_t ebx; uint32_t host_cbitpos; struct sev_user_data_status status = {}; + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(cgs); ret = ram_block_discard_disable(true); if (ret) { @@ -952,7 +955,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) goto err; } - sev_launch_start(SEV_GUEST(sev_common)); + ret = klass->launch_start(sev_common); if (ret) { error_setg(errp, "%s: failed to create encryption context", __func__); goto err; @@ -1451,6 +1454,10 @@ static void sev_guest_set_legacy_vm_type(Object *obj, bool value, Error **errp) static void sev_guest_class_init(ObjectClass *oc, void *data) { + SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); + + klass->launch_start = sev_launch_start; + object_class_property_add_str(oc, "dh-cert-file", sev_guest_get_dh_cert_file, sev_guest_set_dh_cert_file); From bce615a14aec07cab0488e5a242f6a91e641efcb Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:18 -0500 Subject: [PATCH 1173/2884] i386/sev: Move sev_launch_finish to separate class method When sev-snp-guest objects are introduced there will be a number of differences in how the launch finish is handled compared to the existing sev-guest object. Move sev_launch_finish() to a class method to make it easier to implement SNP-specific launch update functionality later. Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-7-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index b2aa0d6f99..28a018ed83 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -71,6 +71,7 @@ struct SevCommonStateClass { /* public */ int (*launch_start)(SevCommonState *sev_common); + void (*launch_finish)(SevCommonState *sev_common); }; /** @@ -801,12 +802,12 @@ static Notifier sev_machine_done_notify = { }; static void -sev_launch_finish(SevGuestState *sev_guest) +sev_launch_finish(SevCommonState *sev_common) { int ret, error; trace_kvm_sev_launch_finish(); - ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); if (ret) { error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", @@ -814,7 +815,7 @@ sev_launch_finish(SevGuestState *sev_guest) exit(1); } - sev_set_guest_state(SEV_COMMON(sev_guest), SEV_STATE_RUNNING); + sev_set_guest_state(sev_common, SEV_STATE_RUNNING); /* add migration blocker */ error_setg(&sev_mig_blocker, @@ -826,10 +827,11 @@ static void sev_vm_state_change(void *opaque, bool running, RunState state) { SevCommonState *sev_common = opaque; + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(opaque); if (running) { if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) { - sev_launch_finish(SEV_GUEST(sev_common)); + klass->launch_finish(sev_common); } } } @@ -1457,6 +1459,7 @@ sev_guest_class_init(ObjectClass *oc, void *data) SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); klass->launch_start = sev_launch_start; + klass->launch_finish = sev_launch_finish; object_class_property_add_str(oc, "dh-cert-file", sev_guest_get_dh_cert_file, From 7b34df44260b391e33bc3acf1ced30019d9aadf1 Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Thu, 30 May 2024 06:16:19 -0500 Subject: [PATCH 1174/2884] i386/sev: Introduce 'sev-snp-guest' object SEV-SNP support relies on a different set of properties/state than the existing 'sev-guest' object. This patch introduces the 'sev-snp-guest' object, which can be used to configure an SEV-SNP guest. For example, a default-configured SEV-SNP guest with no additional information passed in for use with attestation: -object sev-snp-guest,id=sev0 or a fully-specified SEV-SNP guest where all spec-defined binary blobs are passed in as base64-encoded strings: -object sev-snp-guest,id=sev0, \ policy=0x30000, \ init-flags=0, \ id-block=YWFhYWFhYWFhYWFhYWFhCg==, \ id-auth=CxHK/OKLkXGn/KpAC7Wl1FSiisWDbGTEKz..., \ author-key-enabled=on, \ host-data=LNkCWBRC5CcdGXirbNUV1OrsR28s..., \ guest-visible-workarounds=AA==, \ See the QAPI schema updates included in this patch for more usage details. In some cases these blobs may be up to 4096 characters, but this is generally well below the default limit for linux hosts where command-line sizes are defined by the sysconf-configurable ARG_MAX value, which defaults to 2097152 characters for Ubuntu hosts, for example. Signed-off-by: Brijesh Singh Co-developed-by: Michael Roth Acked-by: Markus Armbruster (for QAPI schema) Signed-off-by: Michael Roth Co-developed-by: Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-8-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- docs/system/i386/amd-memory-encryption.rst | 70 +++++- qapi/qom.json | 58 +++++ target/i386/sev.c | 253 +++++++++++++++++++++ target/i386/sev.h | 1 + 4 files changed, 380 insertions(+), 2 deletions(-) diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst index e9bc142bc1..748f5094ba 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -25,8 +25,8 @@ support for notifying a guest's operating system when certain types of VMEXITs are about to occur. This allows the guest to selectively share information with the hypervisor to satisfy the requested function. -Launching ---------- +Launching (SEV and SEV-ES) +-------------------------- Boot images (such as bios) must be encrypted before a guest can be booted. The ``MEMORY_ENCRYPT_OP`` ioctl provides commands to encrypt the images: ``LAUNCH_START``, @@ -161,6 +161,72 @@ The value of GCTX.LD is If kernel hashes are not used, or SEV-ES is disabled, use empty blobs for ``kernel_hashes_blob`` and ``vmsas_blob`` as needed. +Launching (SEV-SNP) +------------------- +Boot images (such as bios) must be encrypted before a guest can be booted. The +``MEMORY_ENCRYPT_OP`` ioctl provides commands to encrypt the images: +``SNP_LAUNCH_START``, ``SNP_LAUNCH_UPDATE``, and ``SNP_LAUNCH_FINISH``. These +three commands communicate with SEV-SNP firmware to generate a fresh memory +encryption key for the VM, encrypt the boot images for a successful launch. For +more details on the SEV-SNP firmware interfaces used by these commands please +see the SEV-SNP Firmware ABI. + +``SNP_LAUNCH_START`` is called first to create a cryptographic launch context +within the firmware. To create this context, the guest owner must provide a +guest policy and other parameters as described in the SEV-SNP firmware +specification. The launch parameters should be specified as described in the +QAPI schema for the sev-snp-guest object. + +The ``SNP_LAUNCH_START`` uses the following parameters, which can be configured +by the corresponding parameters documented in the QAPI schema for the +'sev-snp-guest' object. + ++--------+-------+----------+-------------------------------------------------+ +| key | type | default | meaning | ++---------------------------+-------------------------------------------------+ +| policy | hex | 0x30000 | a 64-bit guest policy | ++---------------------------+-------------------------------------------------+ +| guest-visible-workarounds | string| 0 | 16-byte base64 encoded string| +| | | | for guest OS visible | +| | | | workarounds. | ++---------------------------+-------------------------------------------------+ + +``SNP_LAUNCH_UPDATE`` encrypts the memory region using the cryptographic context +created via the ``SNP_LAUNCH_START`` command. If required, this command can be +called multiple times to encrypt different memory regions. The command also +calculates the measurement of the memory contents as it encrypts. + +``SNP_LAUNCH_FINISH`` finalizes the guest launch flow. Optionally, while +finalizing the launch the firmware can perform checks on the launch digest +computing through the ``SNP_LAUNCH_UPDATE``. To perform the check the user must +supply the id block, authentication blob and host data that should be included +in the attestation report. See the SEV-SNP spec for further details. + +The ``SNP_LAUNCH_FINISH`` uses the following parameters, which can be configured +by the corresponding parameters documented in the QAPI schema for the +'sev-snp-guest' object. + ++--------------------+-------+----------+-------------------------------------+ +| key | type | default | meaning | ++--------------------+-------+----------+-------------------------------------+ +| id-block | string| none | base64 encoded ID block | ++--------------------+-------+----------+-------------------------------------+ +| id-auth | string| none | base64 encoded authentication | +| | | | information | ++--------------------+-------+----------+-------------------------------------+ +| author-key-enabled | bool | 0 | auth block contains author key | ++--------------------+-------+----------+-------------------------------------+ +| host_data | string| none | host provided data | ++--------------------+-------+----------+-------------------------------------+ + +To launch a SEV-SNP guest (additional parameters are documented in the QAPI +schema for the 'sev-snp-guest' object):: + + # ${QEMU} \ + -machine ...,confidential-guest-support=sev0 \ + -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1 + + Debugging --------- diff --git a/qapi/qom.json b/qapi/qom.json index 056b38f491..8bd299265e 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -929,6 +929,62 @@ '*handle': 'uint32', '*legacy-vm-type': 'bool' } } +## +# @SevSnpGuestProperties: +# +# Properties for sev-snp-guest objects. Most of these are direct +# arguments for the KVM_SNP_* interfaces documented in the Linux +# kernel source under +# Documentation/arch/x86/amd-memory-encryption.rst, which are in turn +# closely coupled with the SNP_INIT/SNP_LAUNCH_* firmware commands +# documented in the SEV-SNP Firmware ABI Specification (Rev 0.9). +# +# More usage information is also available in the QEMU source tree +# under docs/amd-memory-encryption. +# +# @policy: the 'POLICY' parameter to the SNP_LAUNCH_START command, as +# defined in the SEV-SNP firmware ABI (default: 0x30000) +# +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report +# hypervisor-defined workarounds, corresponding to the 'GOSVW' +# parameter of the SNP_LAUNCH_START command defined in the SEV-SNP +# firmware ABI (default: all-zero) +# +# @id-block: 96-byte, base64-encoded blob to provide the 'ID Block' +# structure for the SNP_LAUNCH_FINISH command defined in the +# SEV-SNP firmware ABI (default: all-zero) +# +# @id-auth: 4096-byte, base64-encoded blob to provide the 'ID +# Authentication Information Structure' for the SNP_LAUNCH_FINISH +# command defined in the SEV-SNP firmware ABI (default: all-zero) +# +# @author-key-enabled: true if 'id-auth' blob contains the 'AUTHOR_KEY' +# field defined SEV-SNP firmware ABI (default: false) +# +# @host-data: 32-byte, base64-encoded, user-defined blob to provide to +# the guest, as documented for the 'HOST_DATA' parameter of the +# SNP_LAUNCH_FINISH command in the SEV-SNP firmware ABI (default: +# all-zero) +# +# @vcek-disabled: Guests are by default allowed to choose between VLEK +# (Versioned Loaded Endorsement Key) or VCEK (Versioned Chip +# Endorsement Key) when requesting attestation reports from +# firmware. Set this to true to disable the use of VCEK. +# (default: false) (since: 9.1) +# +# Since: 9.1 +## +{ 'struct': 'SevSnpGuestProperties', + 'base': 'SevCommonProperties', + 'data': { + '*policy': 'uint64', + '*guest-visible-workarounds': 'str', + '*id-block': 'str', + '*id-auth': 'str', + '*author-key-enabled': 'bool', + '*host-data': 'str', + '*vcek-disabled': 'bool' } } + ## # @ThreadContextProperties: # @@ -1007,6 +1063,7 @@ { 'name': 'secret_keyring', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest', + 'sev-snp-guest', 'thread-context', 's390-pv-guest', 'throttle-group', @@ -1077,6 +1134,7 @@ 'secret_keyring': { 'type': 'SecretKeyringProperties', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest': 'SevGuestProperties', + 'sev-snp-guest': 'SevSnpGuestProperties', 'thread-context': 'ThreadContextProperties', 'throttle-group': 'ThrottleGroupProperties', 'tls-creds-anon': 'TlsCredsAnonProperties', diff --git a/target/i386/sev.c b/target/i386/sev.c index 28a018ed83..a81b3228d4 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -42,6 +42,7 @@ OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) +OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST) struct SevCommonState { X86ConfidentialGuest parent_obj; @@ -96,8 +97,22 @@ struct SevGuestState { bool legacy_vm_type; }; +struct SevSnpGuestState { + SevCommonState parent_obj; + + /* configuration parameters */ + char *guest_visible_workarounds; + char *id_block; + char *id_auth; + char *host_data; + + struct kvm_sev_snp_launch_start kvm_start_conf; + struct kvm_sev_snp_launch_finish kvm_finish_conf; +}; + #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ #define DEFAULT_SEV_DEVICE "/dev/sev" +#define DEFAULT_SEV_SNP_POLICY 0x30000 #define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" typedef struct __attribute__((__packed__)) SevInfoBlock { @@ -1500,11 +1515,249 @@ static const TypeInfo sev_guest_info = { .class_init = sev_guest_class_init, }; +static void +sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint64(v, name, + (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy, + errp); +} + +static void +sev_snp_guest_set_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint64(v, name, + (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy, + errp); +} + +static char * +sev_snp_guest_get_guest_visible_workarounds(Object *obj, Error **errp) +{ + return g_strdup(SEV_SNP_GUEST(obj)->guest_visible_workarounds); +} + +static void +sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value, + Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf; + g_autofree guchar *blob; + gsize len; + + g_free(sev_snp_guest->guest_visible_workarounds); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->guest_visible_workarounds = g_strdup(value); + + blob = qbase64_decode(sev_snp_guest->guest_visible_workarounds, + -1, &len, errp); + if (!blob) { + return; + } + + if (len != sizeof(start->gosvw)) { + error_setg(errp, "parameter length of %lu exceeds max of %lu", + len, sizeof(start->gosvw)); + return; + } + + memcpy(start->gosvw, blob, len); +} + +static char * +sev_snp_guest_get_id_block(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return g_strdup(sev_snp_guest->id_block); +} + +static void +sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + gsize len; + + g_free(sev_snp_guest->id_block); + g_free((guchar *)finish->id_block_uaddr); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->id_block = g_strdup(value); + + finish->id_block_uaddr = + (uint64_t)qbase64_decode(sev_snp_guest->id_block, -1, &len, errp); + + if (!finish->id_block_uaddr) { + return; + } + + if (len != KVM_SEV_SNP_ID_BLOCK_SIZE) { + error_setg(errp, "parameter length of %lu not equal to %u", + len, KVM_SEV_SNP_ID_BLOCK_SIZE); + return; + } + + finish->id_block_en = (len) ? 1 : 0; +} + +static char * +sev_snp_guest_get_id_auth(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return g_strdup(sev_snp_guest->id_auth); +} + +static void +sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + gsize len; + + g_free(sev_snp_guest->id_auth); + g_free((guchar *)finish->id_auth_uaddr); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->id_auth = g_strdup(value); + + finish->id_auth_uaddr = + (uint64_t)qbase64_decode(sev_snp_guest->id_auth, -1, &len, errp); + + if (!finish->id_auth_uaddr) { + return; + } + + if (len > KVM_SEV_SNP_ID_AUTH_SIZE) { + error_setg(errp, "parameter length:ID_AUTH %lu exceeds max of %u", + len, KVM_SEV_SNP_ID_AUTH_SIZE); + return; + } +} + +static bool +sev_snp_guest_get_author_key_enabled(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return !!sev_snp_guest->kvm_finish_conf.auth_key_en; +} + +static void +sev_snp_guest_set_author_key_enabled(Object *obj, bool value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + sev_snp_guest->kvm_finish_conf.auth_key_en = value; +} + +static bool +sev_snp_guest_get_vcek_disabled(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return !!sev_snp_guest->kvm_finish_conf.vcek_disabled; +} + +static void +sev_snp_guest_set_vcek_disabled(Object *obj, bool value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + sev_snp_guest->kvm_finish_conf.vcek_disabled = value; +} + +static char * +sev_snp_guest_get_host_data(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return g_strdup(sev_snp_guest->host_data); +} + +static void +sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + g_autofree guchar *blob; + gsize len; + + g_free(sev_snp_guest->host_data); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->host_data = g_strdup(value); + + blob = qbase64_decode(sev_snp_guest->host_data, -1, &len, errp); + + if (!blob) { + return; + } + + if (len != sizeof(finish->host_data)) { + error_setg(errp, "parameter length of %lu not equal to %lu", + len, sizeof(finish->host_data)); + return; + } + + memcpy(finish->host_data, blob, len); +} + +static void +sev_snp_guest_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add(oc, "policy", "uint64", + sev_snp_guest_get_policy, + sev_snp_guest_set_policy, NULL, NULL); + object_class_property_add_str(oc, "guest-visible-workarounds", + sev_snp_guest_get_guest_visible_workarounds, + sev_snp_guest_set_guest_visible_workarounds); + object_class_property_add_str(oc, "id-block", + sev_snp_guest_get_id_block, + sev_snp_guest_set_id_block); + object_class_property_add_str(oc, "id-auth", + sev_snp_guest_get_id_auth, + sev_snp_guest_set_id_auth); + object_class_property_add_bool(oc, "author-key-enabled", + sev_snp_guest_get_author_key_enabled, + sev_snp_guest_set_author_key_enabled); + object_class_property_add_bool(oc, "vcek-required", + sev_snp_guest_get_vcek_disabled, + sev_snp_guest_set_vcek_disabled); + object_class_property_add_str(oc, "host-data", + sev_snp_guest_get_host_data, + sev_snp_guest_set_host_data); +} + +static void +sev_snp_guest_instance_init(Object *obj) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + /* default init/start/finish params for kvm */ + sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY; +} + +/* guest info specific to sev-snp */ +static const TypeInfo sev_snp_guest_info = { + .parent = TYPE_SEV_COMMON, + .name = TYPE_SEV_SNP_GUEST, + .instance_size = sizeof(SevSnpGuestState), + .class_init = sev_snp_guest_class_init, + .instance_init = sev_snp_guest_instance_init, +}; + static void sev_register_types(void) { type_register_static(&sev_common_info); type_register_static(&sev_guest_info); + type_register_static(&sev_snp_guest_info); } type_init(sev_register_types); diff --git a/target/i386/sev.h b/target/i386/sev.h index 668374eef3..bedc667eeb 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -22,6 +22,7 @@ #define TYPE_SEV_COMMON "sev-common" #define TYPE_SEV_GUEST "sev-guest" +#define TYPE_SEV_SNP_GUEST "sev-snp-guest" #define SEV_POLICY_NODBG 0x1 #define SEV_POLICY_NOKS 0x2 From 99190f805dca9475fe244fbd8041961842657dc2 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:20 -0500 Subject: [PATCH 1175/2884] i386/sev: Add a sev_snp_enabled() helper Add a simple helper to check if the current guest type is SNP. Also have SNP-enabled imply that SEV-ES is enabled as well, and fix up any places where the sev_es_enabled() check is expecting a pure/non-SNP guest. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-9-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 13 ++++++++++++- target/i386/sev.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index a81b3228d4..4edfedc139 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -325,12 +325,21 @@ sev_enabled(void) return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON); } +bool +sev_snp_enabled(void) +{ + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + + return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_SNP_GUEST); +} + bool sev_es_enabled(void) { ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; - return sev_enabled() && (SEV_GUEST(cgs)->policy & SEV_POLICY_ES); + return sev_snp_enabled() || + (sev_enabled() && SEV_GUEST(cgs)->policy & SEV_POLICY_ES); } uint32_t @@ -946,7 +955,9 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) "support", __func__); goto err; } + } + if (sev_es_enabled() && !sev_snp_enabled()) { if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { error_setg(errp, "%s: guest policy requires SEV-ES, but " "host SEV-ES support unavailable", diff --git a/target/i386/sev.h b/target/i386/sev.h index bedc667eeb..94295ee74f 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -45,9 +45,11 @@ typedef struct SevKernelLoaderContext { #ifdef CONFIG_SEV bool sev_enabled(void); bool sev_es_enabled(void); +bool sev_snp_enabled(void); #else #define sev_enabled() 0 #define sev_es_enabled() 0 +#define sev_snp_enabled() 0 #endif uint32_t sev_get_cbit_position(void); From 990da8d243a8c59dafcbed78b56a0e4ffb1605d9 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:21 -0500 Subject: [PATCH 1176/2884] i386/sev: Add sev_kvm_init() override for SEV class Some aspects of the init routine SEV are specific to SEV and not applicable for SNP guests, so move the SEV-specific bits into separate class method and retain only the common functionality. Co-developed-by: Michael Roth Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-10-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 72 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 4edfedc139..5519de1c6b 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -73,6 +73,7 @@ struct SevCommonStateClass { /* public */ int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); + int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); }; /** @@ -882,7 +883,7 @@ out: return sev_common->kvm_type; } -static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { SevCommonState *sev_common = SEV_COMMON(cgs); char *devname; @@ -892,12 +893,6 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) struct sev_user_data_status status = {}; SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(cgs); - ret = ram_block_discard_disable(true); - if (ret) { - error_report("%s: cannot disable RAM discard", __func__); - return -1; - } - sev_common->state = SEV_STATE_UNINIT; host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); @@ -911,7 +906,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) if (host_cbitpos != sev_common->cbitpos) { error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", __func__, host_cbitpos, sev_common->cbitpos); - goto err; + return -1; } /* @@ -924,7 +919,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) error_setg(errp, "%s: reduced_phys_bits check failed," " it should be in the range of 1 to 63, requested '%d'", __func__, sev_common->reduced_phys_bits); - goto err; + return -1; } devname = object_property_get_str(OBJECT(sev_common), "sev-device", NULL); @@ -933,7 +928,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) error_setg(errp, "%s: Failed to open %s '%s'", __func__, devname, strerror(errno)); g_free(devname); - goto err; + return -1; } g_free(devname); @@ -943,7 +938,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) error_setg(errp, "%s: failed to get platform status ret=%d " "fw_error='%d: %s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); - goto err; + return -1; } sev_common->build_id = status.build; sev_common->api_major = status.api_major; @@ -953,7 +948,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) if (!kvm_kernel_irqchip_allowed()) { error_setg(errp, "%s: SEV-ES guests require in-kernel irqchip" "support", __func__); - goto err; + return -1; } } @@ -962,7 +957,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) error_setg(errp, "%s: guest policy requires SEV-ES, but " "host SEV-ES support unavailable", __func__); - goto err; + return -1; } } @@ -980,25 +975,59 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) if (ret) { error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); - goto err; + return -1; } ret = klass->launch_start(sev_common); if (ret) { error_setg(errp, "%s: failed to create encryption context", __func__); - goto err; + return -1; + } + + if (klass->kvm_init && klass->kvm_init(cgs, errp)) { + return -1; } - ram_block_notifier_add(&sev_ram_notifier); - qemu_add_machine_init_done_notifier(&sev_machine_done_notify); qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common); cgs->ready = true; return 0; -err: - ram_block_discard_disable(false); - return -1; +} + +static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + int ret; + + /* + * SEV/SEV-ES rely on pinned memory to back guest RAM so discarding + * isn't actually possible. With SNP, only guest_memfd pages are used + * for private guest memory, so discarding of shared memory is still + * possible.. + */ + ret = ram_block_discard_disable(true); + if (ret) { + error_setg(errp, "%s: cannot disable RAM discard", __func__); + return -1; + } + + /* + * SEV uses these notifiers to register/pin pages prior to guest use, + * but SNP relies on guest_memfd for private pages, which has its + * own internal mechanisms for registering/pinning private memory. + */ + ram_block_notifier_add(&sev_ram_notifier); + + /* + * The machine done notify event is used for SEV guests to get the + * measurement of the encrypted images. When SEV-SNP is enabled, the + * measurement is part of the guest attestation process where it can + * be collected without any reliance on the VMM. So skip registering + * the notifier for SNP in favor of using guest attestation instead. + */ + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + + return 0; } int @@ -1397,7 +1426,7 @@ sev_common_class_init(ObjectClass *oc, void *data) ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); - klass->kvm_init = sev_kvm_init; + klass->kvm_init = sev_common_kvm_init; x86_klass->kvm_type = sev_kvm_type; object_class_property_add_str(oc, "sev-device", @@ -1486,6 +1515,7 @@ sev_guest_class_init(ObjectClass *oc, void *data) klass->launch_start = sev_launch_start; klass->launch_finish = sev_launch_finish; + klass->kvm_init = sev_kvm_init; object_class_property_add_str(oc, "dh-cert-file", sev_guest_get_dh_cert_file, From 125b95a6d465a03ff30816eff0b1889aec01f0c3 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:22 -0500 Subject: [PATCH 1177/2884] i386/sev: Add snp_kvm_init() override for SNP class SNP does not support SMM and requires guest_memfd for private guest memory, so add SNP specific kvm_init() functionality in snp_kvm_init() class method. Signed-off-by: Michael Roth Co-developed-by: Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-11-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 5519de1c6b..6525b3c1a0 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -885,12 +885,12 @@ out: static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - SevCommonState *sev_common = SEV_COMMON(cgs); char *devname; int ret, fw_error, cmd; uint32_t ebx; uint32_t host_cbitpos; struct sev_user_data_status status = {}; + SevCommonState *sev_common = SEV_COMMON(cgs); SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(cgs); sev_common->state = SEV_STATE_UNINIT; @@ -1030,6 +1030,21 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return 0; } +static int sev_snp_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + X86MachineState *x86ms = X86_MACHINE(ms); + + if (x86ms->smm == ON_OFF_AUTO_AUTO) { + x86ms->smm = ON_OFF_AUTO_OFF; + } else if (x86ms->smm == ON_OFF_AUTO_ON) { + error_setg(errp, "SEV-SNP does not support SMM."); + return -1; + } + + return 0; +} + int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) { @@ -1752,6 +1767,10 @@ sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) static void sev_snp_guest_class_init(ObjectClass *oc, void *data) { + SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); + + klass->kvm_init = sev_snp_kvm_init; + object_class_property_add(oc, "policy", "uint64", sev_snp_guest_get_policy, sev_snp_guest_set_policy, NULL, NULL); @@ -1778,8 +1797,11 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) static void sev_snp_guest_instance_init(Object *obj) { + ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj); SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + cgs->require_guest_memfd = true; + /* default init/start/finish params for kvm */ sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY; } From 7831221941cccbde922412c1550ed8b4bce7c361 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:23 -0500 Subject: [PATCH 1178/2884] i386/cpu: Set SEV-SNP CPUID bit when SNP enabled SNP guests will rely on this bit to determine certain feature support. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-12-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index bc2dceb647..914bef442c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6979,6 +6979,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (sev_enabled()) { *eax = 0x2; *eax |= sev_es_enabled() ? 0x8 : 0; + *eax |= sev_snp_enabled() ? 0x10 : 0; *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ } From 73ae63b162fc1fed520f53ad200712964d7d0264 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:24 -0500 Subject: [PATCH 1179/2884] i386/sev: Don't return launch measurements for SEV-SNP guests For SEV-SNP guests, launch measurement is queried from within the guest during attestation, so don't attempt to return it as part of query-sev-launch-measure. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-13-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 6525b3c1a0..c3daaf1ad5 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -795,7 +795,9 @@ sev_launch_get_measure(Notifier *notifier, void *unused) static char *sev_get_launch_measurement(void) { - SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + SevGuestState *sev_guest = + (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); if (sev_guest && SEV_COMMON(sev_guest)->state >= SEV_STATE_LAUNCH_SECRET) { From a808132f6d8e855bd83a400570ec91d2e00bebe3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 12:44:44 +0200 Subject: [PATCH 1180/2884] i386/sev: Add a class method to determine KVM VM type for SNP guests SEV guests can use either KVM_X86_DEFAULT_VM, KVM_X86_SEV_VM, or KVM_X86_SEV_ES_VM depending on the configuration and what the host kernel supports. SNP guests on the other hand can only ever use KVM_X86_SNP_VM, so split determination of VM type out into a separate class method that can be set accordingly for sev-guest vs. sev-snp-guest objects and add handling for SNP. Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-14-pankaj.gupta@amd.com> [Remove unnecessary function pointer declaration. - Paolo] Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 1 + target/i386/sev.c | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6c864e4611..23a003aaa7 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -167,6 +167,7 @@ static const char *vm_type_name[] = { [KVM_X86_DEFAULT_VM] = "default", [KVM_X86_SEV_VM] = "SEV", [KVM_X86_SEV_ES_VM] = "SEV-ES", + [KVM_X86_SNP_VM] = "SEV-SNP", }; bool kvm_is_vm_type_supported(int type) diff --git a/target/i386/sev.c b/target/i386/sev.c index c3daaf1ad5..072cc4f853 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -885,6 +885,11 @@ out: return sev_common->kvm_type; } +static int sev_snp_kvm_type(X86ConfidentialGuest *cg) +{ + return KVM_X86_SNP_VM; +} + static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { char *devname; @@ -894,6 +899,8 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) struct sev_user_data_status status = {}; SevCommonState *sev_common = SEV_COMMON(cgs); SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(cgs); + X86ConfidentialGuestClass *x86_klass = + X86_CONFIDENTIAL_GUEST_GET_CLASS(cgs); sev_common->state = SEV_STATE_UNINIT; @@ -964,7 +971,7 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } trace_kvm_sev_init(); - if (sev_kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) { + if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) { cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT; ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error); @@ -1441,10 +1448,8 @@ static void sev_common_class_init(ObjectClass *oc, void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); - X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); klass->kvm_init = sev_common_kvm_init; - x86_klass->kvm_type = sev_kvm_type; object_class_property_add_str(oc, "sev-device", sev_common_get_sev_device, @@ -1529,10 +1534,12 @@ static void sev_guest_class_init(ObjectClass *oc, void *data) { SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); klass->launch_start = sev_launch_start; klass->launch_finish = sev_launch_finish; klass->kvm_init = sev_kvm_init; + x86_klass->kvm_type = sev_kvm_type; object_class_property_add_str(oc, "dh-cert-file", sev_guest_get_dh_cert_file, @@ -1770,8 +1777,10 @@ static void sev_snp_guest_class_init(ObjectClass *oc, void *data) { SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); klass->kvm_init = sev_snp_kvm_init; + x86_klass->kvm_type = sev_snp_kvm_type; object_class_property_add(oc, "policy", "uint64", sev_snp_guest_get_policy, From 59d3740cb4ac0f010ce35877572904f6297284b4 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:26 -0500 Subject: [PATCH 1181/2884] i386/sev: Update query-sev QAPI format to handle SEV-SNP Most of the current 'query-sev' command is relevant to both legacy SEV/SEV-ES guests and SEV-SNP guests, with 2 exceptions: - 'policy' is a 64-bit field for SEV-SNP, not 32-bit, and the meaning of the bit positions has changed - 'handle' is not relevant to SEV-SNP To address this, this patch adds a new 'sev-type' field that can be used as a discriminator to select between SEV and SEV-SNP-specific fields/formats without breaking compatibility for existing management tools (so long as management tools that add support for launching SEV-SNP guest update their handling of query-sev appropriately). The corresponding HMP command has also been fixed up similarly. Signed-off-by: Michael Roth Co-developed-by:Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-15-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- qapi/misc-target.json | 72 ++++++++++++++++++++++++++++++++++--------- target/i386/sev.c | 57 +++++++++++++++++++++------------- target/i386/sev.h | 3 ++ 3 files changed, 97 insertions(+), 35 deletions(-) diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 4e0a6492a9..2d7d4d89bd 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -47,6 +47,50 @@ 'send-update', 'receive-update' ], 'if': 'TARGET_I386' } +## +# @SevGuestType: +# +# An enumeration indicating the type of SEV guest being run. +# +# @sev: The guest is a legacy SEV or SEV-ES guest. +# +# @sev-snp: The guest is an SEV-SNP guest. +# +# Since: 6.2 +## +{ 'enum': 'SevGuestType', + 'data': [ 'sev', 'sev-snp' ], + 'if': 'TARGET_I386' } + +## +# @SevGuestInfo: +# +# Information specific to legacy SEV/SEV-ES guests. +# +# @policy: SEV policy value +# +# @handle: SEV firmware handle +# +# Since: 2.12 +## +{ 'struct': 'SevGuestInfo', + 'data': { 'policy': 'uint32', + 'handle': 'uint32' }, + 'if': 'TARGET_I386' } + +## +# @SevSnpGuestInfo: +# +# Information specific to SEV-SNP guests. +# +# @snp-policy: SEV-SNP policy value +# +# Since: 9.1 +## +{ 'struct': 'SevSnpGuestInfo', + 'data': { 'snp-policy': 'uint64' }, + 'if': 'TARGET_I386' } + ## # @SevInfo: # @@ -60,25 +104,25 @@ # # @build-id: SEV FW build id # -# @policy: SEV policy value -# # @state: SEV guest state # -# @handle: SEV firmware handle +# @sev-type: Type of SEV guest being run # # Since: 2.12 ## -{ 'struct': 'SevInfo', - 'data': { 'enabled': 'bool', - 'api-major': 'uint8', - 'api-minor' : 'uint8', - 'build-id' : 'uint8', - 'policy' : 'uint32', - 'state' : 'SevState', - 'handle' : 'uint32' - }, - 'if': 'TARGET_I386' -} +{ 'union': 'SevInfo', + 'base': { 'enabled': 'bool', + 'api-major': 'uint8', + 'api-minor' : 'uint8', + 'build-id' : 'uint8', + 'state' : 'SevState', + 'sev-type' : 'SevGuestType' }, + 'discriminator': 'sev-type', + 'data': { + 'sev': 'SevGuestInfo', + 'sev-snp': 'SevSnpGuestInfo' }, + 'if': 'TARGET_I386' } + ## # @query-sev: diff --git a/target/i386/sev.c b/target/i386/sev.c index 072cc4f853..43d1c48bd9 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -363,25 +363,27 @@ static SevInfo *sev_get_info(void) { SevInfo *info; SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - SevGuestState *sev_guest = - (SevGuestState *)object_dynamic_cast(OBJECT(sev_common), - TYPE_SEV_GUEST); info = g_new0(SevInfo, 1); info->enabled = sev_enabled(); if (info->enabled) { - if (sev_guest) { - info->handle = sev_guest->handle; - } info->api_major = sev_common->api_major; info->api_minor = sev_common->api_minor; info->build_id = sev_common->build_id; info->state = sev_common->state; - /* we only report the lower 32-bits of policy for SNP, ok for now... */ - info->policy = - (uint32_t)object_property_get_uint(OBJECT(sev_common), - "policy", NULL); + + if (sev_snp_enabled()) { + info->sev_type = SEV_GUEST_TYPE_SEV_SNP; + info->u.sev_snp.snp_policy = + object_property_get_uint(OBJECT(sev_common), "policy", NULL); + } else { + info->sev_type = SEV_GUEST_TYPE_SEV; + info->u.sev.handle = SEV_GUEST(sev_common)->handle; + info->u.sev.policy = + (uint32_t)object_property_get_uint(OBJECT(sev_common), + "policy", NULL); + } } return info; @@ -404,20 +406,33 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict) { SevInfo *info = sev_get_info(); - if (info && info->enabled) { - monitor_printf(mon, "handle: %d\n", info->handle); - monitor_printf(mon, "state: %s\n", SevState_str(info->state)); - monitor_printf(mon, "build: %d\n", info->build_id); - monitor_printf(mon, "api version: %d.%d\n", - info->api_major, info->api_minor); - monitor_printf(mon, "debug: %s\n", - info->policy & SEV_POLICY_NODBG ? "off" : "on"); - monitor_printf(mon, "key-sharing: %s\n", - info->policy & SEV_POLICY_NOKS ? "off" : "on"); - } else { + if (!info || !info->enabled) { monitor_printf(mon, "SEV is not enabled\n"); + goto out; } + monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type)); + monitor_printf(mon, "state: %s\n", SevState_str(info->state)); + monitor_printf(mon, "build: %d\n", info->build_id); + monitor_printf(mon, "api version: %d.%d\n", info->api_major, + info->api_minor); + + if (sev_snp_enabled()) { + monitor_printf(mon, "debug: %s\n", + info->u.sev_snp.snp_policy & SEV_SNP_POLICY_DBG ? "on" + : "off"); + monitor_printf(mon, "SMT allowed: %s\n", + info->u.sev_snp.snp_policy & SEV_SNP_POLICY_SMT ? "on" + : "off"); + } else { + monitor_printf(mon, "handle: %d\n", info->u.sev.handle); + monitor_printf(mon, "debug: %s\n", + info->u.sev.policy & SEV_POLICY_NODBG ? "off" : "on"); + monitor_printf(mon, "key-sharing: %s\n", + info->u.sev.policy & SEV_POLICY_NOKS ? "off" : "on"); + } + +out: qapi_free_SevInfo(info); } diff --git a/target/i386/sev.h b/target/i386/sev.h index 94295ee74f..5dc4767b1e 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -31,6 +31,9 @@ #define SEV_POLICY_DOMAIN 0x10 #define SEV_POLICY_SEV 0x20 +#define SEV_SNP_POLICY_SMT 0x10000 +#define SEV_SNP_POLICY_DBG 0x80000 + typedef struct SevKernelLoaderContext { char *setup_data; size_t setup_size; From d3107f882ec22cfb211eab7efa0c4e95f5ce11bb Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Thu, 30 May 2024 06:16:27 -0500 Subject: [PATCH 1182/2884] i386/sev: Add the SNP launch start context The SNP_LAUNCH_START is called first to create a cryptographic launch context within the firmware. Signed-off-by: Brijesh Singh Signed-off-by: Michael Roth Co-developed-by: Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-16-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 39 +++++++++++++++++++++++++++++++++++++++ target/i386/trace-events | 1 + 2 files changed, 40 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index 43d1c48bd9..e89b87d2f5 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -39,6 +39,7 @@ #include "confidential-guest.h" #include "hw/i386/pc.h" #include "exec/address-spaces.h" +#include "qemu/queue.h" OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) @@ -115,6 +116,16 @@ struct SevSnpGuestState { #define DEFAULT_SEV_DEVICE "/dev/sev" #define DEFAULT_SEV_SNP_POLICY 0x30000 +typedef struct SevLaunchUpdateData { + QTAILQ_ENTRY(SevLaunchUpdateData) next; + hwaddr gpa; + void *hva; + uint64_t len; + int type; +} SevLaunchUpdateData; + +static QTAILQ_HEAD(, SevLaunchUpdateData) launch_update; + #define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" typedef struct __attribute__((__packed__)) SevInfoBlock { /* SEV-ES Reset Vector Address */ @@ -674,6 +685,31 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len) return 0; } +static int +sev_snp_launch_start(SevCommonState *sev_common) +{ + int fw_error, rc; + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common); + struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf; + + trace_kvm_sev_snp_launch_start(start->policy, + sev_snp_guest->guest_visible_workarounds); + + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START, + start, &fw_error); + if (rc < 0) { + error_report("%s: SNP_LAUNCH_START ret=%d fw_error=%d '%s'", + __func__, rc, fw_error, fw_error_to_str(fw_error)); + return 1; + } + + QTAILQ_INIT(&launch_update); + + sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE); + + return 0; +} + static int sev_launch_start(SevCommonState *sev_common) { @@ -1003,6 +1039,7 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } ret = klass->launch_start(sev_common); + if (ret) { error_setg(errp, "%s: failed to create encryption context", __func__); return -1; @@ -1794,9 +1831,11 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + klass->launch_start = sev_snp_launch_start; klass->kvm_init = sev_snp_kvm_init; x86_klass->kvm_type = sev_snp_kvm_type; + object_class_property_add(oc, "policy", "uint64", sev_snp_guest_get_policy, sev_snp_guest_set_policy, NULL, NULL); diff --git a/target/i386/trace-events b/target/i386/trace-events index 2cd8726eeb..cb26d8a925 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -11,3 +11,4 @@ kvm_sev_launch_measurement(const char *value) "data %s" kvm_sev_launch_finish(void) "" kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d" kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s" +kvm_sev_snp_launch_start(uint64_t policy, char *gosvw) "policy 0x%" PRIx64 " gosvw %s" From 9f3a6999f9730a694d7db448a99f9c9cb6515992 Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Thu, 30 May 2024 06:16:28 -0500 Subject: [PATCH 1183/2884] i386/sev: Add handling to encrypt/finalize guest launch data Process any queued up launch data and encrypt/measure it into the SNP guest instance prior to initial guest launch. This also updates the KVM_SEV_SNP_LAUNCH_UPDATE call to handle partial update responses. Signed-off-by: Brijesh Singh Co-developed-by: Michael Roth Signed-off-by: Michael Roth Co-developed-by: Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-17-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 112 ++++++++++++++++++++++++++++++++++++++- target/i386/trace-events | 2 + 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index e89b87d2f5..ef2e592ca7 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -756,6 +756,76 @@ out: return ret; } +static const char * +snp_page_type_to_str(int type) +{ + switch (type) { + case KVM_SEV_SNP_PAGE_TYPE_NORMAL: return "Normal"; + case KVM_SEV_SNP_PAGE_TYPE_ZERO: return "Zero"; + case KVM_SEV_SNP_PAGE_TYPE_UNMEASURED: return "Unmeasured"; + case KVM_SEV_SNP_PAGE_TYPE_SECRETS: return "Secrets"; + case KVM_SEV_SNP_PAGE_TYPE_CPUID: return "Cpuid"; + default: return "unknown"; + } +} + +static int +sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, + SevLaunchUpdateData *data) +{ + int ret, fw_error; + struct kvm_sev_snp_launch_update update = {0}; + + if (!data->hva || !data->len) { + error_report("SNP_LAUNCH_UPDATE called with invalid address" + "/ length: %p / %lx", + data->hva, data->len); + return 1; + } + + update.uaddr = (__u64)(unsigned long)data->hva; + update.gfn_start = data->gpa >> TARGET_PAGE_BITS; + update.len = data->len; + update.type = data->type; + + /* + * KVM_SEV_SNP_LAUNCH_UPDATE requires that GPA ranges have the private + * memory attribute set in advance. + */ + ret = kvm_set_memory_attributes_private(data->gpa, data->len); + if (ret) { + error_report("SEV-SNP: failed to configure initial" + "private guest memory"); + goto out; + } + + while (update.len || ret == -EAGAIN) { + trace_kvm_sev_snp_launch_update(update.uaddr, update.gfn_start << + TARGET_PAGE_BITS, update.len, + snp_page_type_to_str(update.type)); + + ret = sev_ioctl(SEV_COMMON(sev_snp_guest)->sev_fd, + KVM_SEV_SNP_LAUNCH_UPDATE, + &update, &fw_error); + if (ret && ret != -EAGAIN) { + error_report("SNP_LAUNCH_UPDATE ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); + break; + } + } + +out: + if (!ret && update.gfn_start << TARGET_PAGE_BITS != data->gpa + data->len) { + error_report("SEV-SNP: expected update of GPA range %lx-%lx," + "got GPA range %lx-%llx", + data->gpa, data->gpa + data->len, data->gpa, + update.gfn_start << TARGET_PAGE_BITS); + ret = -EIO; + } + + return ret; +} + static int sev_launch_update_data(SevGuestState *sev_guest, uint8_t *addr, uint64_t len) { @@ -901,6 +971,46 @@ sev_launch_finish(SevCommonState *sev_common) migrate_add_blocker(&sev_mig_blocker, &error_fatal); } +static void +sev_snp_launch_finish(SevCommonState *sev_common) +{ + int ret, error; + Error *local_err = NULL; + SevLaunchUpdateData *data; + SevSnpGuestState *sev_snp = SEV_SNP_GUEST(sev_common); + struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf; + + QTAILQ_FOREACH(data, &launch_update, next) { + ret = sev_snp_launch_update(sev_snp, data); + if (ret) { + exit(1); + } + } + + trace_kvm_sev_snp_launch_finish(sev_snp->id_block, sev_snp->id_auth, + sev_snp->host_data); + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, + finish, &error); + if (ret) { + error_report("SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(error)); + exit(1); + } + + sev_set_guest_state(sev_common, SEV_STATE_RUNNING); + + /* add migration blocker */ + error_setg(&sev_mig_blocker, + "SEV-SNP: Migration is not implemented"); + ret = migrate_add_blocker(&sev_mig_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(sev_mig_blocker); + exit(1); + } +} + + static void sev_vm_state_change(void *opaque, bool running, RunState state) { @@ -1832,10 +1942,10 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); klass->launch_start = sev_snp_launch_start; + klass->launch_finish = sev_snp_launch_finish; klass->kvm_init = sev_snp_kvm_init; x86_klass->kvm_type = sev_snp_kvm_type; - object_class_property_add(oc, "policy", "uint64", sev_snp_guest_get_policy, sev_snp_guest_set_policy, NULL, NULL); diff --git a/target/i386/trace-events b/target/i386/trace-events index cb26d8a925..06b44ead2e 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -12,3 +12,5 @@ kvm_sev_launch_finish(void) "" kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d" kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s" kvm_sev_snp_launch_start(uint64_t policy, char *gosvw) "policy 0x%" PRIx64 " gosvw %s" +kvm_sev_snp_launch_update(uint64_t src, uint64_t gpa, uint64_t len, const char *type) "src 0x%" PRIx64 " gpa 0x%" PRIx64 " len 0x%" PRIx64 " (%s page)" +kvm_sev_snp_launch_finish(char *id_block, char *id_auth, char *host_data) "id_block %s id_auth %s host_data %s" From 3d44fdff60ea66fbd7a33f5d32b50843cd80f48a Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:29 -0500 Subject: [PATCH 1184/2884] i386/sev: Set CPU state to protected once SNP guest payload is finalized Once KVM_SNP_LAUNCH_FINISH is called the vCPU state is copied into the vCPU's VMSA page and measured/encrypted. Any attempt to read/write CPU state afterward will only be acting on the initial data and so are effectively no-ops. Set the vCPU state to protected at this point so that QEMU don't continue trying to re-sync vCPU data during guest runtime. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-18-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index ef2e592ca7..e84e4395a5 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -997,6 +997,7 @@ sev_snp_launch_finish(SevCommonState *sev_common) exit(1); } + kvm_mark_guest_state_protected(); sev_set_guest_state(sev_common, SEV_STATE_RUNNING); /* add migration blocker */ From f3c30c575d34122573b7370a7da5ca3a27dde481 Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Thu, 30 May 2024 06:16:30 -0500 Subject: [PATCH 1185/2884] hw/i386/sev: Add function to get SEV metadata from OVMF header A recent version of OVMF expanded the reset vector GUID list to add SEV-specific metadata GUID. The SEV metadata describes the reserved memory regions such as the secrets and CPUID page used during the SEV-SNP guest launch. The pc_system_get_ovmf_sev_metadata_ptr() is used to retieve the SEV metadata pointer from the OVMF GUID list. Signed-off-by: Brijesh Singh Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-19-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_sysfw.c | 4 ++++ include/hw/i386/pc.h | 26 ++++++++++++++++++++++++++ target/i386/sev-sysemu-stub.c | 4 ++++ target/i386/sev.c | 32 ++++++++++++++++++++++++++++++++ target/i386/sev.h | 2 ++ 5 files changed, 68 insertions(+) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index ac88ad4eb9..9b8671c441 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -260,6 +260,10 @@ void x86_firmware_configure(void *ptr, int size) pc_system_parse_ovmf_flash(ptr, size); if (sev_enabled()) { + + /* Copy the SEV metadata table (if it exists) */ + pc_system_parse_sev_metadata(ptr, size); + ret = sev_es_save_reset_vector(ptr, size); if (ret) { error_report("failed to locate and/or save reset vector"); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index ad9c3d9ba8..c653b8eeb2 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -164,6 +164,32 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level); #define PCI_HOST_ABOVE_4G_MEM_SIZE "above-4g-mem-size" #define PCI_HOST_PROP_SMM_RANGES "smm-ranges" +typedef enum { + SEV_DESC_TYPE_UNDEF, + /* The section contains the region that must be validated by the VMM. */ + SEV_DESC_TYPE_SNP_SEC_MEM, + /* The section contains the SNP secrets page */ + SEV_DESC_TYPE_SNP_SECRETS, + /* The section contains address that can be used as a CPUID page */ + SEV_DESC_TYPE_CPUID, + +} ovmf_sev_metadata_desc_type; + +typedef struct __attribute__((__packed__)) OvmfSevMetadataDesc { + uint32_t base; + uint32_t len; + ovmf_sev_metadata_desc_type type; +} OvmfSevMetadataDesc; + +typedef struct __attribute__((__packed__)) OvmfSevMetadata { + uint8_t signature[4]; + uint32_t len; + uint32_t version; + uint32_t num_desc; + OvmfSevMetadataDesc descs[]; +} OvmfSevMetadata; + +OvmfSevMetadata *pc_system_get_ovmf_sev_metadata_ptr(void); void pc_pci_as_mapping_init(MemoryRegion *system_memory, MemoryRegion *pci_address_space); diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 96e1c15cc3..fc1c57c411 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -67,3 +67,7 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "SEV is not available in this QEMU\n"); } + +void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size) +{ +} diff --git a/target/i386/sev.c b/target/i386/sev.c index e84e4395a5..17281bb2c7 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -597,6 +597,38 @@ SevCapability *qmp_query_sev_capabilities(Error **errp) return sev_get_capabilities(errp); } +static OvmfSevMetadata *ovmf_sev_metadata_table; + +#define OVMF_SEV_META_DATA_GUID "dc886566-984a-4798-A75e-5585a7bf67cc" +typedef struct __attribute__((__packed__)) OvmfSevMetadataOffset { + uint32_t offset; +} OvmfSevMetadataOffset; + +OvmfSevMetadata *pc_system_get_ovmf_sev_metadata_ptr(void) +{ + return ovmf_sev_metadata_table; +} + +void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size) +{ + OvmfSevMetadata *metadata; + OvmfSevMetadataOffset *data; + + if (!pc_system_ovmf_table_find(OVMF_SEV_META_DATA_GUID, (uint8_t **)&data, + NULL)) { + return; + } + + metadata = (OvmfSevMetadata *)(flash_ptr + flash_size - data->offset); + if (memcmp(metadata->signature, "ASEV", 4) != 0 || + metadata->len < sizeof(OvmfSevMetadata) || + metadata->len > flash_size - data->offset) { + return; + } + + ovmf_sev_metadata_table = g_memdup2(metadata, metadata->len); +} + static SevAttestationReport *sev_get_attestation_report(const char *mnonce, Error **errp) { diff --git a/target/i386/sev.h b/target/i386/sev.h index 5dc4767b1e..cc12824dd6 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -66,4 +66,6 @@ int sev_inject_launch_secret(const char *hdr, const char *secret, int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size); void sev_es_set_reset_vector(CPUState *cpu); +void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size); + #endif From 3d8c2a7f4806ff39423312e503737fd76c34dcae Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Thu, 30 May 2024 06:16:31 -0500 Subject: [PATCH 1186/2884] i386/sev: Add support for populating OVMF metadata pages OVMF reserves various pages so they can be pre-initialized/validated prior to launching the guest. Add support for populating these pages with the expected content. Signed-off-by: Brijesh Singh Signed-off-by: Michael Roth Co-developed-by: Pankaj Gupta Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-20-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index 17281bb2c7..c57534fca2 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1003,15 +1003,89 @@ sev_launch_finish(SevCommonState *sev_common) migrate_add_blocker(&sev_mig_blocker, &error_fatal); } +static int +snp_launch_update_data(uint64_t gpa, void *hva, uint32_t len, int type) +{ + SevLaunchUpdateData *data; + + data = g_new0(SevLaunchUpdateData, 1); + data->gpa = gpa; + data->hva = hva; + data->len = len; + data->type = type; + + QTAILQ_INSERT_TAIL(&launch_update, data, next); + + return 0; +} + +static int +snp_metadata_desc_to_page_type(int desc_type) +{ + switch (desc_type) { + /* Add the umeasured prevalidated pages as a zero page */ + case SEV_DESC_TYPE_SNP_SEC_MEM: return KVM_SEV_SNP_PAGE_TYPE_ZERO; + case SEV_DESC_TYPE_SNP_SECRETS: return KVM_SEV_SNP_PAGE_TYPE_SECRETS; + case SEV_DESC_TYPE_CPUID: return KVM_SEV_SNP_PAGE_TYPE_CPUID; + default: + return KVM_SEV_SNP_PAGE_TYPE_ZERO; + } +} + +static void +snp_populate_metadata_pages(SevSnpGuestState *sev_snp, + OvmfSevMetadata *metadata) +{ + OvmfSevMetadataDesc *desc; + int type, ret, i; + void *hva; + MemoryRegion *mr = NULL; + + for (i = 0; i < metadata->num_desc; i++) { + desc = &metadata->descs[i]; + + type = snp_metadata_desc_to_page_type(desc->type); + + hva = gpa2hva(&mr, desc->base, desc->len, NULL); + if (!hva) { + error_report("%s: Failed to get HVA for GPA 0x%x sz 0x%x", + __func__, desc->base, desc->len); + exit(1); + } + + ret = snp_launch_update_data(desc->base, hva, desc->len, type); + if (ret) { + error_report("%s: Failed to add metadata page gpa 0x%x+%x type %d", + __func__, desc->base, desc->len, desc->type); + exit(1); + } + } +} + static void sev_snp_launch_finish(SevCommonState *sev_common) { int ret, error; Error *local_err = NULL; + OvmfSevMetadata *metadata; SevLaunchUpdateData *data; SevSnpGuestState *sev_snp = SEV_SNP_GUEST(sev_common); struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf; + /* + * To boot the SNP guest, the hypervisor is required to populate the CPUID + * and Secrets page before finalizing the launch flow. The location of + * the secrets and CPUID page is available through the OVMF metadata GUID. + */ + metadata = pc_system_get_ovmf_sev_metadata_ptr(); + if (metadata == NULL) { + error_report("%s: Failed to locate SEV metadata header", __func__); + exit(1); + } + + /* Populate all the metadata pages */ + snp_populate_metadata_pages(sev_snp, metadata); + QTAILQ_FOREACH(data, &launch_update, next) { ret = sev_snp_launch_update(sev_snp, data); if (ret) { From 70943ad8e4dfbe5f77006b880290219be9d03553 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:32 -0500 Subject: [PATCH 1187/2884] i386/sev: Add support for SNP CPUID validation SEV-SNP firmware allows a special guest page to be populated with a table of guest CPUID values so that they can be validated through firmware before being loaded into encrypted guest memory where they can be used in place of hypervisor-provided values[1]. As part of SEV-SNP guest initialization, use this interface to validate the CPUID entries reported by KVM_GET_CPUID2 prior to initial guest start and populate the CPUID page reserved by OVMF with the resulting encrypted data. [1] SEV SNP Firmware ABI Specification, Rev. 0.8, 8.13.2.6 Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-21-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 164 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index c57534fca2..06401f0526 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -200,6 +200,36 @@ static const char *const sev_fw_errlist[] = { #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) +/* doesn't expose this, so re-use the max from kvm.c */ +#define KVM_MAX_CPUID_ENTRIES 100 + +typedef struct KvmCpuidInfo { + struct kvm_cpuid2 cpuid; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; +} KvmCpuidInfo; + +#define SNP_CPUID_FUNCTION_MAXCOUNT 64 +#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF + +typedef struct { + uint32_t eax_in; + uint32_t ecx_in; + uint64_t xcr0_in; + uint64_t xss_in; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint64_t reserved; +} __attribute__((packed)) SnpCpuidFunc; + +typedef struct { + uint32_t count; + uint32_t reserved1; + uint64_t reserved2; + SnpCpuidFunc entries[SNP_CPUID_FUNCTION_MAXCOUNT]; +} __attribute__((packed)) SnpCpuidInfo; + static int sev_ioctl(int fd, int cmd, void *data, int *error) { @@ -788,6 +818,35 @@ out: return ret; } +static void +sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old, + SnpCpuidInfo *new) +{ + size_t i; + + if (old->count != new->count) { + error_report("SEV-SNP: CPUID validation failed due to count mismatch," + "provided: %d, expected: %d", old->count, new->count); + return; + } + + for (i = 0; i < old->count; i++) { + SnpCpuidFunc *old_func, *new_func; + + old_func = &old->entries[i]; + new_func = &new->entries[i]; + + if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) { + error_report("SEV-SNP: CPUID validation failed for function 0x%x, index: 0x%x" + "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x" + "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x", + old_func->eax_in, old_func->ecx_in, + old_func->eax, old_func->ebx, old_func->ecx, old_func->edx, + new_func->eax, new_func->ebx, new_func->ecx, new_func->edx); + } + } +} + static const char * snp_page_type_to_str(int type) { @@ -806,6 +865,7 @@ sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, SevLaunchUpdateData *data) { int ret, fw_error; + SnpCpuidInfo snp_cpuid_info; struct kvm_sev_snp_launch_update update = {0}; if (!data->hva || !data->len) { @@ -815,6 +875,11 @@ sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, return 1; } + if (data->type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { + /* Save a copy for comparison in case the LAUNCH_UPDATE fails */ + memcpy(&snp_cpuid_info, data->hva, sizeof(snp_cpuid_info)); + } + update.uaddr = (__u64)(unsigned long)data->hva; update.gfn_start = data->gpa >> TARGET_PAGE_BITS; update.len = data->len; @@ -842,6 +907,11 @@ sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, if (ret && ret != -EAGAIN) { error_report("SNP_LAUNCH_UPDATE ret=%d fw_error=%d '%s'", ret, fw_error, fw_error_to_str(fw_error)); + + if (data->type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { + sev_snp_cpuid_report_mismatches(&snp_cpuid_info, data->hva); + error_report("SEV-SNP: failed update CPUID page"); + } break; } } @@ -1004,7 +1074,8 @@ sev_launch_finish(SevCommonState *sev_common) } static int -snp_launch_update_data(uint64_t gpa, void *hva, uint32_t len, int type) +snp_launch_update_data(uint64_t gpa, void *hva, + uint32_t len, int type) { SevLaunchUpdateData *data; @@ -1019,6 +1090,90 @@ snp_launch_update_data(uint64_t gpa, void *hva, uint32_t len, int type) return 0; } +static int +sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, + const KvmCpuidInfo *kvm_cpuid_info) +{ + size_t i; + + if (kvm_cpuid_info->cpuid.nent > SNP_CPUID_FUNCTION_MAXCOUNT) { + error_report("SEV-SNP: CPUID entry count (%d) exceeds max (%d)", + kvm_cpuid_info->cpuid.nent, SNP_CPUID_FUNCTION_MAXCOUNT); + return -1; + } + + memset(snp_cpuid_info, 0, sizeof(*snp_cpuid_info)); + + for (i = 0; i < kvm_cpuid_info->cpuid.nent; i++) { + const struct kvm_cpuid_entry2 *kvm_cpuid_entry; + SnpCpuidFunc *snp_cpuid_entry; + + kvm_cpuid_entry = &kvm_cpuid_info->entries[i]; + snp_cpuid_entry = &snp_cpuid_info->entries[i]; + + snp_cpuid_entry->eax_in = kvm_cpuid_entry->function; + if (kvm_cpuid_entry->flags == KVM_CPUID_FLAG_SIGNIFCANT_INDEX) { + snp_cpuid_entry->ecx_in = kvm_cpuid_entry->index; + } + snp_cpuid_entry->eax = kvm_cpuid_entry->eax; + snp_cpuid_entry->ebx = kvm_cpuid_entry->ebx; + snp_cpuid_entry->ecx = kvm_cpuid_entry->ecx; + snp_cpuid_entry->edx = kvm_cpuid_entry->edx; + + /* + * Guest kernels will calculate EBX themselves using the 0xD + * subfunctions corresponding to the individual XSAVE areas, so only + * encode the base XSAVE size in the initial leaves, corresponding + * to the initial XCR0=1 state. + */ + if (snp_cpuid_entry->eax_in == 0xD && + (snp_cpuid_entry->ecx_in == 0x0 || snp_cpuid_entry->ecx_in == 0x1)) { + snp_cpuid_entry->ebx = 0x240; + snp_cpuid_entry->xcr0_in = 1; + snp_cpuid_entry->xss_in = 0; + } + } + + snp_cpuid_info->count = i; + + return 0; +} + +static int +snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len) +{ + KvmCpuidInfo kvm_cpuid_info = {0}; + SnpCpuidInfo snp_cpuid_info; + CPUState *cs = first_cpu; + int ret; + uint32_t i = 0; + + assert(sizeof(snp_cpuid_info) <= cpuid_len); + + /* get the cpuid list from KVM */ + do { + kvm_cpuid_info.cpuid.nent = ++i; + ret = kvm_vcpu_ioctl(cs, KVM_GET_CPUID2, &kvm_cpuid_info); + } while (ret == -E2BIG); + + if (ret) { + error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'", + strerror(-ret)); + return 1; + } + + ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info); + if (ret) { + error_report("SEV-SNP: failed to generate CPUID table information"); + return 1; + } + + memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info)); + + return snp_launch_update_data(cpuid_addr, hva, cpuid_len, + KVM_SEV_SNP_PAGE_TYPE_CPUID); +} + static int snp_metadata_desc_to_page_type(int desc_type) { @@ -1053,7 +1208,12 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, exit(1); } - ret = snp_launch_update_data(desc->base, hva, desc->len, type); + if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { + ret = snp_launch_update_cpuid(desc->base, hva, desc->len); + } else { + ret = snp_launch_update_data(desc->base, hva, desc->len, type); + } + if (ret) { error_report("%s: Failed to add metadata page gpa 0x%x+%x type %d", __func__, desc->base, desc->len, desc->type); From 77d1abd91e5352ad30ae2f83790f95fa6a3c0b6b Mon Sep 17 00:00:00 2001 From: Brijesh Singh Date: Thu, 30 May 2024 06:16:36 -0500 Subject: [PATCH 1188/2884] hw/i386/sev: Add support to encrypt BIOS when SEV-SNP is enabled As with SEV, an SNP guest requires that the BIOS be part of the initial encrypted/measured guest payload. Extend sev_encrypt_flash() to handle the SNP case and plumb through the GPA of the BIOS location since this is needed for SNP. Signed-off-by: Brijesh Singh Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-25-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_sysfw.c | 12 +++++++----- hw/i386/x86-common.c | 2 +- include/hw/i386/x86.h | 2 +- target/i386/sev-sysemu-stub.c | 2 +- target/i386/sev.c | 5 +++-- target/i386/sev.h | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 9b8671c441..7cdbafc8d2 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -148,6 +148,8 @@ static void pc_system_flash_map(PCMachineState *pcms, assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + hwaddr gpa; + system_flash = pcms->flash[i]; blk = pflash_cfi01_get_blk(system_flash); if (!blk) { @@ -177,11 +179,11 @@ static void pc_system_flash_map(PCMachineState *pcms, } total_size += size; + gpa = 0x100000000ULL - total_size; /* where the flash is mapped */ qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks", size / FLASH_SECTOR_SIZE); sysbus_realize_and_unref(SYS_BUS_DEVICE(system_flash), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, - 0x100000000ULL - total_size); + sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, gpa); if (i == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); @@ -196,7 +198,7 @@ static void pc_system_flash_map(PCMachineState *pcms, if (sev_enabled()) { flash_ptr = memory_region_get_ram_ptr(flash_mem); flash_size = memory_region_size(flash_mem); - x86_firmware_configure(flash_ptr, flash_size); + x86_firmware_configure(gpa, flash_ptr, flash_size); } } } @@ -249,7 +251,7 @@ void pc_system_firmware_init(PCMachineState *pcms, pc_system_flash_cleanup_unused(pcms); } -void x86_firmware_configure(void *ptr, int size) +void x86_firmware_configure(hwaddr gpa, void *ptr, int size) { int ret; @@ -270,6 +272,6 @@ void x86_firmware_configure(void *ptr, int size) exit(1); } - sev_encrypt_flash(ptr, size, &error_fatal); + sev_encrypt_flash(gpa, ptr, size, &error_fatal); } } diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index ee9046d9a8..f41cb0a6a8 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -1013,7 +1013,7 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, */ void *ptr = memory_region_get_ram_ptr(&x86ms->bios); load_image_size(filename, ptr, bios_size); - x86_firmware_configure(ptr, bios_size); + x86_firmware_configure(0x100000000ULL - bios_size, ptr, bios_size); } else { memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index b006f16b8d..d43cb3908e 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -154,6 +154,6 @@ void ioapic_init_gsi(GSIState *gsi_state, Object *parent); DeviceState *ioapic_init_secondary(GSIState *gsi_state); /* pc_sysfw.c */ -void x86_firmware_configure(void *ptr, int size); +void x86_firmware_configure(hwaddr gpa, void *ptr, int size); #endif diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index fc1c57c411..d5bf886e79 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -42,7 +42,7 @@ void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, error_setg(errp, "SEV is not available in this QEMU"); } -int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { g_assert_not_reached(); } diff --git a/target/i386/sev.c b/target/i386/sev.c index 06401f0526..7b5c4b4874 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1484,7 +1484,7 @@ static int sev_snp_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } int -sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); @@ -1841,7 +1841,8 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) /* zero the excess data so the measurement can be reliably calculated */ memset(padded_ht->padding, 0, sizeof(padded_ht->padding)); - if (sev_encrypt_flash((uint8_t *)padded_ht, sizeof(*padded_ht), errp) < 0) { + if (sev_encrypt_flash(area->base, (uint8_t *)padded_ht, + sizeof(*padded_ht), errp) < 0) { ret = false; } diff --git a/target/i386/sev.h b/target/i386/sev.h index cc12824dd6..858005a119 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -59,7 +59,7 @@ uint32_t sev_get_cbit_position(void); uint32_t sev_get_reduced_phys_bits(void); bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); -int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); +int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp); int sev_inject_launch_secret(const char *hdr, const char *secret, uint64_t gpa, Error **errp); From 9861405a8f845133b7984322c2df0c43a45553c3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 31 May 2024 12:51:44 +0200 Subject: [PATCH 1189/2884] i386/sev: Invoke launch_updata_data() for SEV class Add launch_update_data() in SevCommonStateClass and invoke as sev_launch_update_data() for SEV object. Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-26-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 7b5c4b4874..8834cf9441 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -74,6 +74,7 @@ struct SevCommonStateClass { /* public */ int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); + int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, uint64_t len); int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); }; @@ -929,7 +930,7 @@ out: } static int -sev_launch_update_data(SevGuestState *sev_guest, uint8_t *addr, uint64_t len) +sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, uint64_t len) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -941,7 +942,7 @@ sev_launch_update_data(SevGuestState *sev_guest, uint8_t *addr, uint64_t len) update.uaddr = (uintptr_t)addr; update.len = len; trace_kvm_sev_launch_update_data(addr, len); - ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", @@ -1487,6 +1488,7 @@ int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); if (!sev_common) { return 0; @@ -1494,7 +1496,9 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) /* if SEV is in update state then encrypt the data else do nothing */ if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { - int ret = sev_launch_update_data(SEV_GUEST(sev_common), ptr, len); + int ret; + + ret = klass->launch_update_data(sev_common, gpa, ptr, len); if (ret < 0) { error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; @@ -1968,6 +1972,7 @@ sev_guest_class_init(ObjectClass *oc, void *data) klass->launch_start = sev_launch_start; klass->launch_finish = sev_launch_finish; + klass->launch_update_data = sev_launch_update_data; klass->kvm_init = sev_kvm_init; x86_klass->kvm_type = sev_kvm_type; From 0765d136eba400ad1cb7cae18438bb10eace64dc Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 30 May 2024 06:16:38 -0500 Subject: [PATCH 1190/2884] i386/sev: Invoke launch_updata_data() for SNP class Invoke as sev_snp_launch_update_data() for SNP object. Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-27-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index 8834cf9441..eaf5fc6c6b 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1091,6 +1091,15 @@ snp_launch_update_data(uint64_t gpa, void *hva, return 0; } +static int +sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, uint64_t len) +{ + int ret = snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_NORMAL); + return ret; +} + static int sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, const KvmCpuidInfo *kvm_cpuid_info) @@ -2216,6 +2225,7 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) klass->launch_start = sev_snp_launch_start; klass->launch_finish = sev_snp_launch_finish; + klass->launch_update_data = sev_snp_launch_update_data; klass->kvm_init = sev_snp_kvm_init; x86_klass->kvm_type = sev_snp_kvm_type; From 47e76d03b155e43beca550251a6eb7ea926c059f Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:42 -0500 Subject: [PATCH 1191/2884] i386/kvm: Add KVM_EXIT_HYPERCALL handling for KVM_HC_MAP_GPA_RANGE KVM_HC_MAP_GPA_RANGE will be used to send requests to userspace for private/shared memory attribute updates requested by the guest. Implement handling for that use-case along with some basic infrastructure for enabling specific hypercall events. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-31-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 55 ++++++++++++++++++++++++++++++++++++ target/i386/kvm/kvm_i386.h | 1 + target/i386/kvm/trace-events | 1 + 3 files changed, 57 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 23a003aaa7..ede3ef1225 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -21,6 +21,7 @@ #include #include +#include #include "standard-headers/asm-x86/kvm_para.h" #include "hw/xen/interface/arch-x86/cpuid.h" @@ -209,6 +210,13 @@ int kvm_get_vm_type(MachineState *ms) return kvm_type; } +bool kvm_enable_hypercall(uint64_t enable_mask) +{ + KVMState *s = KVM_STATE(current_accel()); + + return !kvm_vm_enable_cap(s, KVM_CAP_EXIT_HYPERCALL, 0, enable_mask); +} + bool kvm_has_smm(void) { return kvm_vm_check_extension(kvm_state, KVM_CAP_X86_SMM); @@ -5322,6 +5330,50 @@ static bool host_supports_vmx(void) return ecx & CPUID_EXT_VMX; } +/* + * Currently the handling here only supports use of KVM_HC_MAP_GPA_RANGE + * to service guest-initiated memory attribute update requests so that + * KVM_SET_MEMORY_ATTRIBUTES can update whether or not a page should be + * backed by the private memory pool provided by guest_memfd, and as such + * is only applicable to guest_memfd-backed guests (e.g. SNP/TDX). + * + * Other other use-cases for KVM_HC_MAP_GPA_RANGE, such as for SEV live + * migration, are not implemented here currently. + * + * For the guest_memfd use-case, these exits will generally be synthesized + * by KVM based on platform-specific hypercalls, like GHCB requests in the + * case of SEV-SNP, and not issued directly within the guest though the + * KVM_HC_MAP_GPA_RANGE hypercall. So in this case, KVM_HC_MAP_GPA_RANGE is + * not actually advertised to guests via the KVM CPUID feature bit, as + * opposed to SEV live migration where it would be. Since it is unlikely the + * SEV live migration use-case would be useful for guest-memfd backed guests, + * because private/shared page tracking is already provided through other + * means, these 2 use-cases should be treated as being mutually-exclusive. + */ +static int kvm_handle_hc_map_gpa_range(struct kvm_run *run) +{ + uint64_t gpa, size, attributes; + + if (!machine_require_guest_memfd(current_machine)) + return -EINVAL; + + gpa = run->hypercall.args[0]; + size = run->hypercall.args[1] * TARGET_PAGE_SIZE; + attributes = run->hypercall.args[2]; + + trace_kvm_hc_map_gpa_range(gpa, size, attributes, run->hypercall.flags); + + return kvm_convert_memory(gpa, size, attributes & KVM_MAP_GPA_RANGE_ENCRYPTED); +} + +static int kvm_handle_hypercall(struct kvm_run *run) +{ + if (run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) + return kvm_handle_hc_map_gpa_range(run); + + return -EINVAL; +} + #define VMX_INVALID_GUEST_STATE 0x80000021 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) @@ -5417,6 +5469,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = kvm_xen_handle_exit(cpu, &run->xen); break; #endif + case KVM_EXIT_HYPERCALL: + ret = kvm_handle_hypercall(run); + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 6b44844d95..34fc60774b 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -33,6 +33,7 @@ bool kvm_has_smm(void); bool kvm_enable_x2apic(void); bool kvm_hv_vpindex_settable(void); +bool kvm_enable_hypercall(uint64_t enable_mask); bool kvm_enable_sgx_provisioning(KVMState *s); bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index b365a8e8e2..74a6234ff7 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -5,6 +5,7 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %" kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d" kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d" kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" +kvm_hc_map_gpa_range(uint64_t gpa, uint64_t size, uint64_t attributes, uint64_t flags) "gpa 0x%" PRIx64 " size 0x%" PRIx64 " attributes 0x%" PRIx64 " flags 0x%" PRIx64 # xen-emu.c kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t ret) "xen_hypercall: cpu %d cpl %d input %" PRIu64 " a0 0x%" PRIx64 " a1 0x%" PRIx64 " a2 0x%" PRIx64" ret 0x%" PRIx64 From e3cddff93c1f88fea3b26841e792dc0be6b6fae8 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:43 -0500 Subject: [PATCH 1192/2884] i386/sev: Enable KVM_HC_MAP_GPA_RANGE hcall for SNP guests KVM will forward GHCB page-state change requests to userspace in the form of KVM_HC_MAP_GPA_RANGE, so make sure the hypercall handling is enabled for SNP guests. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-32-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index eaf5fc6c6b..abb63062ac 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include +#include #include #include @@ -758,6 +759,10 @@ sev_snp_launch_start(SevCommonState *sev_common) trace_kvm_sev_snp_launch_start(start->policy, sev_snp_guest->guest_visible_workarounds); + if (!kvm_enable_hypercall(BIT_ULL(KVM_HC_MAP_GPA_RANGE))) { + return 1; + } + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START, start, &fw_error); if (rc < 0) { From 06cbd66cecaa3230cccb330facac241a677b29d5 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Thu, 30 May 2024 06:16:33 -0500 Subject: [PATCH 1193/2884] i386/sev: Extract build_kernel_loader_hashes Extract the building of the kernel hashes table out from sev_add_kernel_loader_hashes() to allow building it in other memory areas (for SNP support). No functional change intended. Signed-off-by: Dov Murik Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-22-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 102 ++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index abb63062ac..73f9406715 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1754,45 +1754,16 @@ static const QemuUUID sev_cmdline_entry_guid = { 0x4d, 0x36, 0xab, 0x2a) }; -/* - * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page - * which is included in SEV's initial memory measurement. - */ -bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +static bool build_kernel_loader_hashes(PaddedSevHashTable *padded_ht, + SevKernelLoaderContext *ctx, + Error **errp) { - uint8_t *data; - SevHashTableDescriptor *area; SevHashTable *ht; - PaddedSevHashTable *padded_ht; uint8_t cmdline_hash[HASH_SIZE]; uint8_t initrd_hash[HASH_SIZE]; uint8_t kernel_hash[HASH_SIZE]; uint8_t *hashp; size_t hash_len = HASH_SIZE; - hwaddr mapped_len = sizeof(*padded_ht); - MemTxAttrs attrs = { 0 }; - bool ret = true; - SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - - /* - * Only add the kernel hashes if the sev-guest configuration explicitly - * stated kernel-hashes=on. - */ - if (!sev_common->kernel_hashes) { - return false; - } - - if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { - error_setg(errp, "SEV: kernel specified but guest firmware " - "has no hashes table GUID"); - return false; - } - area = (SevHashTableDescriptor *)data; - if (!area->base || area->size < sizeof(PaddedSevHashTable)) { - error_setg(errp, "SEV: guest firmware hashes table area is invalid " - "(base=0x%x size=0x%x)", area->base, area->size); - return false; - } /* * Calculate hash of kernel command-line with the terminating null byte. If @@ -1829,16 +1800,6 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) } assert(hash_len == HASH_SIZE); - /* - * Populate the hashes table in the guest's memory at the OVMF-designated - * area for the SEV hashes table - */ - padded_ht = address_space_map(&address_space_memory, area->base, - &mapped_len, true, attrs); - if (!padded_ht || mapped_len != sizeof(*padded_ht)) { - error_setg(errp, "SEV: cannot map hashes table guest memory area"); - return false; - } ht = &padded_ht->ht; ht->guid = sev_hash_table_header_guid; @@ -1859,8 +1820,61 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) /* zero the excess data so the measurement can be reliably calculated */ memset(padded_ht->padding, 0, sizeof(padded_ht->padding)); - if (sev_encrypt_flash(area->base, (uint8_t *)padded_ht, - sizeof(*padded_ht), errp) < 0) { + return true; +} + +/* + * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page + * which is included in SEV's initial memory measurement. + */ +bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +{ + uint8_t *data; + SevHashTableDescriptor *area; + PaddedSevHashTable *padded_ht; + hwaddr mapped_len = sizeof(*padded_ht); + MemTxAttrs attrs = { 0 }; + bool ret = true; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + + /* + * Only add the kernel hashes if the sev-guest configuration explicitly + * stated kernel-hashes=on. + */ + if (!sev_common->kernel_hashes) { + return false; + } + + if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { + error_setg(errp, "SEV: kernel specified but guest firmware " + "has no hashes table GUID"); + return false; + } + + area = (SevHashTableDescriptor *)data; + if (!area->base || area->size < sizeof(PaddedSevHashTable)) { + error_setg(errp, "SEV: guest firmware hashes table area is invalid " + "(base=0x%x size=0x%x)", area->base, area->size); + return false; + } + + /* + * Populate the hashes table in the guest's memory at the OVMF-designated + * area for the SEV hashes table + */ + padded_ht = address_space_map(&address_space_memory, area->base, + &mapped_len, true, attrs); + if (!padded_ht || mapped_len != sizeof(*padded_ht)) { + error_setg(errp, "SEV: cannot map hashes table guest memory area"); + return false; + } + + if (build_kernel_loader_hashes(padded_ht, ctx, errp)) { + if (sev_encrypt_flash(area->base, (uint8_t *)padded_ht, + sizeof(*padded_ht), errp) < 0) { + ret = false; + } + } else { ret = false; } From cc483bf911931f405dea682c74a3d8b9b6c54369 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Thu, 30 May 2024 06:16:34 -0500 Subject: [PATCH 1194/2884] i386/sev: Reorder struct declarations Move the declaration of PaddedSevHashTable before SevSnpGuest so we can add a new such field to the latter. No functional change intended. Signed-off-by: Dov Murik Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-23-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 84 +++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 73f9406715..3fce4c08eb 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -46,6 +46,48 @@ OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST) +/* hard code sha256 digest size */ +#define HASH_SIZE 32 + +typedef struct QEMU_PACKED SevHashTableEntry { + QemuUUID guid; + uint16_t len; + uint8_t hash[HASH_SIZE]; +} SevHashTableEntry; + +typedef struct QEMU_PACKED SevHashTable { + QemuUUID guid; + uint16_t len; + SevHashTableEntry cmdline; + SevHashTableEntry initrd; + SevHashTableEntry kernel; +} SevHashTable; + +/* + * Data encrypted by sev_encrypt_flash() must be padded to a multiple of + * 16 bytes. + */ +typedef struct QEMU_PACKED PaddedSevHashTable { + SevHashTable ht; + uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)]; +} PaddedSevHashTable; + +QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); + +#define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" +typedef struct __attribute__((__packed__)) SevInfoBlock { + /* SEV-ES Reset Vector Address */ + uint32_t reset_addr; +} SevInfoBlock; + +#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454" +typedef struct QEMU_PACKED SevHashTableDescriptor { + /* SEV hash table area guest address */ + uint32_t base; + /* SEV hash table area size (in bytes) */ + uint32_t size; +} SevHashTableDescriptor; + struct SevCommonState { X86ConfidentialGuest parent_obj; @@ -128,48 +170,6 @@ typedef struct SevLaunchUpdateData { static QTAILQ_HEAD(, SevLaunchUpdateData) launch_update; -#define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" -typedef struct __attribute__((__packed__)) SevInfoBlock { - /* SEV-ES Reset Vector Address */ - uint32_t reset_addr; -} SevInfoBlock; - -#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454" -typedef struct QEMU_PACKED SevHashTableDescriptor { - /* SEV hash table area guest address */ - uint32_t base; - /* SEV hash table area size (in bytes) */ - uint32_t size; -} SevHashTableDescriptor; - -/* hard code sha256 digest size */ -#define HASH_SIZE 32 - -typedef struct QEMU_PACKED SevHashTableEntry { - QemuUUID guid; - uint16_t len; - uint8_t hash[HASH_SIZE]; -} SevHashTableEntry; - -typedef struct QEMU_PACKED SevHashTable { - QemuUUID guid; - uint16_t len; - SevHashTableEntry cmdline; - SevHashTableEntry initrd; - SevHashTableEntry kernel; -} SevHashTable; - -/* - * Data encrypted by sev_encrypt_flash() must be padded to a multiple of - * 16 bytes. - */ -typedef struct QEMU_PACKED PaddedSevHashTable { - SevHashTable ht; - uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)]; -} PaddedSevHashTable; - -QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); - static Error *sev_mig_blocker; static const char *const sev_fw_errlist[] = { From c1996992cc882b00139f78067d6a64e2ec9cb0d8 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Thu, 30 May 2024 06:16:35 -0500 Subject: [PATCH 1195/2884] i386/sev: Allow measured direct kernel boot on SNP In SNP, the hashes page designated with a specific metadata entry published in AmdSev OVMF. Therefore, if the user enabled kernel hashes (for measured direct boot), QEMU should prepare the content of hashes table, and during the processing of the metadata entry it copy the content into the designated page and encrypt it. Note that in SNP (unlike SEV and SEV-ES) the measurements is done in whole 4KB pages. Therefore QEMU zeros the whole page that includes the hashes table, and fills in the kernel hashes area in that page, and then encrypts the whole page. The rest of the page is reserved for SEV launch secrets which are not usable anyway on SNP. If the user disabled kernel hashes, QEMU pre-validates the kernel hashes page as a zero page. Signed-off-by: Dov Murik Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-24-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- include/hw/i386/pc.h | 2 + target/i386/sev.c | 113 ++++++++++++++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index c653b8eeb2..ca7904ac2c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -172,6 +172,8 @@ typedef enum { SEV_DESC_TYPE_SNP_SECRETS, /* The section contains address that can be used as a CPUID page */ SEV_DESC_TYPE_CPUID, + /* The section contains the region for kernel hashes for measured direct boot */ + SEV_DESC_TYPE_SNP_KERNEL_HASHES = 0x10, } ovmf_sev_metadata_desc_type; diff --git a/target/i386/sev.c b/target/i386/sev.c index 3fce4c08eb..004c667ac1 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -115,6 +115,10 @@ struct SevCommonStateClass { X86ConfidentialGuestClass parent_class; /* public */ + bool (*build_kernel_loader_hashes)(SevCommonState *sev_common, + SevHashTableDescriptor *area, + SevKernelLoaderContext *ctx, + Error **errp); int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, uint64_t len); @@ -154,6 +158,9 @@ struct SevSnpGuestState { struct kvm_sev_snp_launch_start kvm_start_conf; struct kvm_sev_snp_launch_finish kvm_finish_conf; + + uint32_t kernel_hashes_offset; + PaddedSevHashTable *kernel_hashes_data; }; #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ @@ -1189,6 +1196,23 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len) KVM_SEV_SNP_PAGE_TYPE_CPUID); } +static int +snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, + void *hva, uint32_t len) +{ + int type = KVM_SEV_SNP_PAGE_TYPE_ZERO; + if (sev_snp->parent_obj.kernel_hashes) { + assert(sev_snp->kernel_hashes_data); + assert((sev_snp->kernel_hashes_offset + + sizeof(*sev_snp->kernel_hashes_data)) <= len); + memset(hva, 0, len); + memcpy(hva + sev_snp->kernel_hashes_offset, sev_snp->kernel_hashes_data, + sizeof(*sev_snp->kernel_hashes_data)); + type = KVM_SEV_SNP_PAGE_TYPE_NORMAL; + } + return snp_launch_update_data(addr, hva, len, type); +} + static int snp_metadata_desc_to_page_type(int desc_type) { @@ -1225,6 +1249,9 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { ret = snp_launch_update_cpuid(desc->base, hva, desc->len); + } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) { + ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva, + desc->len); } else { ret = snp_launch_update_data(desc->base, hva, desc->len, type); } @@ -1823,40 +1850,31 @@ static bool build_kernel_loader_hashes(PaddedSevHashTable *padded_ht, return true; } -/* - * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page - * which is included in SEV's initial memory measurement. - */ -bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +static bool sev_snp_build_kernel_loader_hashes(SevCommonState *sev_common, + SevHashTableDescriptor *area, + SevKernelLoaderContext *ctx, + Error **errp) +{ + /* + * SNP: Populate the hashes table in an area that later in + * snp_launch_update_kernel_hashes() will be copied to the guest memory + * and encrypted. + */ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common); + sev_snp_guest->kernel_hashes_offset = area->base & ~TARGET_PAGE_MASK; + sev_snp_guest->kernel_hashes_data = g_new0(PaddedSevHashTable, 1); + return build_kernel_loader_hashes(sev_snp_guest->kernel_hashes_data, ctx, errp); +} + +static bool sev_build_kernel_loader_hashes(SevCommonState *sev_common, + SevHashTableDescriptor *area, + SevKernelLoaderContext *ctx, + Error **errp) { - uint8_t *data; - SevHashTableDescriptor *area; PaddedSevHashTable *padded_ht; hwaddr mapped_len = sizeof(*padded_ht); MemTxAttrs attrs = { 0 }; bool ret = true; - SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - - /* - * Only add the kernel hashes if the sev-guest configuration explicitly - * stated kernel-hashes=on. - */ - if (!sev_common->kernel_hashes) { - return false; - } - - if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { - error_setg(errp, "SEV: kernel specified but guest firmware " - "has no hashes table GUID"); - return false; - } - - area = (SevHashTableDescriptor *)data; - if (!area->base || area->size < sizeof(PaddedSevHashTable)) { - error_setg(errp, "SEV: guest firmware hashes table area is invalid " - "(base=0x%x size=0x%x)", area->base, area->size); - return false; - } /* * Populate the hashes table in the guest's memory at the OVMF-designated @@ -1884,6 +1902,41 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) return ret; } +/* + * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page + * which is included in SEV's initial memory measurement. + */ +bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +{ + uint8_t *data; + SevHashTableDescriptor *area; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); + + /* + * Only add the kernel hashes if the sev-guest configuration explicitly + * stated kernel-hashes=on. + */ + if (!sev_common->kernel_hashes) { + return false; + } + + if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { + error_setg(errp, "SEV: kernel specified but guest firmware " + "has no hashes table GUID"); + return false; + } + + area = (SevHashTableDescriptor *)data; + if (!area->base || area->size < sizeof(PaddedSevHashTable)) { + error_setg(errp, "SEV: guest firmware hashes table area is invalid " + "(base=0x%x size=0x%x)", area->base, area->size); + return false; + } + + return klass->build_kernel_loader_hashes(sev_common, area, ctx, errp); +} + static char * sev_common_get_sev_device(Object *obj, Error **errp) { @@ -1998,6 +2051,7 @@ sev_guest_class_init(ObjectClass *oc, void *data) SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + klass->build_kernel_loader_hashes = sev_build_kernel_loader_hashes; klass->launch_start = sev_launch_start; klass->launch_finish = sev_launch_finish; klass->launch_update_data = sev_launch_update_data; @@ -2242,6 +2296,7 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + klass->build_kernel_loader_hashes = sev_snp_build_kernel_loader_hashes; klass->launch_start = sev_snp_launch_start; klass->launch_finish = sev_snp_launch_finish; klass->launch_update_data = sev_snp_launch_update_data; From a0aa6db7ce72a08703774107185e639e73e7754c Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 30 May 2024 06:16:15 -0500 Subject: [PATCH 1196/2884] memory: Introduce memory_region_init_ram_guest_memfd() Introduce memory_region_init_ram_guest_memfd() to allocate private guset memfd on the MemoryRegion initialization. It's for the use case of TDVF, which must be private on TDX case. Signed-off-by: Xiaoyao Li Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-4-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- include/exec/memory.h | 6 ++++++ system/memory.c | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index 9cdd64e9c6..1be58f694c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1638,6 +1638,12 @@ bool memory_region_init_ram(MemoryRegion *mr, uint64_t size, Error **errp); +bool memory_region_init_ram_guest_memfd(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + Error **errp); + /** * memory_region_init_rom: Initialize a ROM memory region. * diff --git a/system/memory.c b/system/memory.c index 9540caa8a1..74cd73ebc7 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3649,6 +3649,30 @@ bool memory_region_init_ram(MemoryRegion *mr, return true; } +bool memory_region_init_ram_guest_memfd(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + Error **errp) +{ + DeviceState *owner_dev; + + if (!memory_region_init_ram_flags_nomigrate(mr, owner, name, size, + RAM_GUEST_MEMFD, errp)) { + return false; + } + /* This will assert if owner is neither NULL nor a DeviceState. + * We only want the owner here for the purposes of defining a + * unique name for migration. TODO: Ideally we should implement + * a naming scheme for Objects which are not DeviceStates, in + * which case we can relax this restriction. + */ + owner_dev = DEVICE(owner); + vmstate_register_ram(mr, owner_dev); + + return true; +} + bool memory_region_init_rom(MemoryRegion *mr, Object *owner, const char *name, From 413a67450750e0459efeffc3db3ba9759c3e381c Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:39 -0500 Subject: [PATCH 1197/2884] hw/i386/sev: Use guest_memfd for legacy ROMs Current SNP guest kernels will attempt to access these regions with with C-bit set, so guest_memfd is needed to handle that. Otherwise, kvm_convert_memory() will fail when the guest kernel tries to access it and QEMU attempts to call KVM_SET_MEMORY_ATTRIBUTES to set these ranges to private. Whether guests should actually try to access ROM regions in this way (or need to deal with legacy ROM regions at all), is a separate issue to be addressed on kernel side, but current SNP guest kernels will exhibit this behavior and so this handling is needed to allow QEMU to continue running existing SNP guest kernels. Signed-off-by: Michael Roth [pankaj: Added sev_snp_enabled() check] Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-28-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 14 ++++++++++---- hw/i386/pc_sysfw.c | 19 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7b638da7aa..0469af00a7 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -62,6 +62,7 @@ #include "hw/mem/memory-device.h" #include "e820_memory_layout.h" #include "trace.h" +#include "sev.h" #include CONFIG_DEVICES #ifdef CONFIG_XEN_EMU @@ -1022,10 +1023,15 @@ void pc_memory_init(PCMachineState *pcms, pc_system_firmware_init(pcms, rom_memory); option_rom_mr = g_malloc(sizeof(*option_rom_mr)); - memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, - &error_fatal); - if (pcmc->pci_enabled) { - memory_region_set_readonly(option_rom_mr, true); + if (machine_require_guest_memfd(machine)) { + memory_region_init_ram_guest_memfd(option_rom_mr, NULL, "pc.rom", + PC_ROM_SIZE, &error_fatal); + } else { + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, + &error_fatal); + if (pcmc->pci_enabled) { + memory_region_set_readonly(option_rom_mr, true); + } } memory_region_add_subregion_overlap(rom_memory, PC_ROM_MIN_VGA, diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 7cdbafc8d2..ef80281d28 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -40,8 +40,8 @@ #define FLASH_SECTOR_SIZE 4096 -static void pc_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *rom_memory, - MemoryRegion *flash_mem) +static void pc_isa_bios_init(PCMachineState *pcms, MemoryRegion *isa_bios, + MemoryRegion *rom_memory, MemoryRegion *flash_mem) { int isa_bios_size; uint64_t flash_size; @@ -51,8 +51,13 @@ static void pc_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *rom_memory, /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = MIN(flash_size, 128 * KiB); - memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, - &error_fatal); + if (machine_require_guest_memfd(MACHINE(pcms))) { + memory_region_init_ram_guest_memfd(isa_bios, NULL, "isa-bios", + isa_bios_size, &error_fatal); + } else { + memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, + &error_fatal); + } memory_region_add_subregion_overlap(rom_memory, 0x100000 - isa_bios_size, isa_bios, @@ -65,7 +70,9 @@ static void pc_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *rom_memory, ((uint8_t*)flash_ptr) + (flash_size - isa_bios_size), isa_bios_size); - memory_region_set_readonly(isa_bios, true); + if (!machine_require_guest_memfd(current_machine)) { + memory_region_set_readonly(isa_bios, true); + } } static PFlashCFI01 *pc_pflash_create(PCMachineState *pcms, @@ -191,7 +198,7 @@ static void pc_system_flash_map(PCMachineState *pcms, x86_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem, true); } else { - pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem); + pc_isa_bios_init(pcms, &x86ms->isa_bios, rom_memory, flash_mem); } /* Encrypt the pflash boot ROM */ From fc7a69e177e4ba26d11fcf47b853f85115b35a11 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Thu, 30 May 2024 06:16:40 -0500 Subject: [PATCH 1198/2884] hw/i386: Add support for loading BIOS using guest_memfd When guest_memfd is enabled, the BIOS is generally part of the initial encrypted guest image and will be accessed as private guest memory. Add the necessary changes to set up the associated RAM region with a guest_memfd backend to allow for this. Current support centers around using -bios to load the BIOS data. Support for loading the BIOS via pflash requires additional enablement since those interfaces rely on the use of ROM memory regions which make use of the KVM_MEM_READONLY memslot flag, which is not supported for guest_memfd-backed memslots. Signed-off-by: Michael Roth Signed-off-by: Pankaj Gupta Message-ID: <20240530111643.1091816-29-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index f41cb0a6a8..c0c66a0eb5 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -1001,8 +1001,13 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, (bios_size % 65536) != 0) { goto bios_error; } - memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, - &error_fatal); + if (machine_require_guest_memfd(MACHINE(x86ms))) { + memory_region_init_ram_guest_memfd(&x86ms->bios, NULL, "pc.bios", + bios_size, &error_fatal); + } else { + memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", + bios_size, &error_fatal); + } if (sev_enabled()) { /* * The concept of a "reset" simply doesn't exist for @@ -1023,9 +1028,11 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, } g_free(filename); - /* map the last 128KB of the BIOS in ISA space */ - x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, - !isapc_ram_fw); + if (!machine_require_guest_memfd(MACHINE(x86ms))) { + /* map the last 128KB of the BIOS in ISA space */ + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, + !isapc_ram_fw); + } /* map all the bios at the top of memory */ memory_region_add_subregion(rom_memory, From e6e903db6a5e960e595f9f1fd034adb942dd9508 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 23 Apr 2024 17:24:39 +0200 Subject: [PATCH 1199/2884] linux-user: Add ioctl for BLKBSZSET Tiny patch to add the ioctl wrapper definition for BLKBSZSET. Signed-off-by: Michael Vogt Message-Id: <20240423152438.19841-2-mvogt@redhat.com> --- linux-user/ioctls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index d508d0c04a..3b41128fd7 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -102,6 +102,7 @@ IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG)) IOCTL(BLKSSZGET, IOC_R, MK_PTR(TYPE_INT)) IOCTL(BLKBSZGET, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(BLKBSZSET, IOC_W, MK_PTR(TYPE_INT)) IOCTL_SPECIAL(BLKPG, IOC_W, do_ioctl_blkpg, MK_PTR(MK_STRUCT(STRUCT_blkpg_ioctl_arg))) From fa9079a86d94c202c316c97ca2eb61ca3e763907 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 19:23:11 -0700 Subject: [PATCH 1200/2884] target/sparc: Fix ARRAY8 Follow the Oracle Sparc 2015 implementation note and bound the input value of N to 5 from the lower 3 bits of rs2. Spell out all of the intermediate values, matching the diagram in the manual. Fix extraction of upper_x and upper_y for N=0. Signed-off-by: Richard Henderson --- target/sparc/vis_helper.c | 53 ++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index e15c6bb34e..f46fcf1f6a 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -21,25 +21,42 @@ #include "cpu.h" #include "exec/helper-proto.h" -/* This function uses non-native bit order */ -#define GET_FIELD(X, FROM, TO) \ - ((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1)) - -/* This function uses the order in the manuals, i.e. bit 0 is 2^0 */ -#define GET_FIELD_SP(X, FROM, TO) \ - GET_FIELD(X, 63 - (TO), 63 - (FROM)) - -target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) +target_ulong helper_array8(target_ulong rs1, target_ulong rs2) { - return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) | - (GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) | - (GET_FIELD_SP(pixel_addr, 17 + cubesize - 1, 17) << 17) | - (GET_FIELD_SP(pixel_addr, 56, 59) << 13) | - (GET_FIELD_SP(pixel_addr, 35, 38) << 9) | - (GET_FIELD_SP(pixel_addr, 13, 16) << 5) | - (((pixel_addr >> 55) & 1) << 4) | - (GET_FIELD_SP(pixel_addr, 33, 34) << 2) | - GET_FIELD_SP(pixel_addr, 11, 12); + /* + * From Oracle SPARC Architecture 2015: + * Architecturally, an illegal R[rs2] value (>5) causes the array + * instructions to produce undefined results. For historic reference, + * past implementations of these instructions have ignored R[rs2]{63:3} + * and have treated R[rs2] values of 6 and 7 as if they were 5. + */ + target_ulong n = MIN(rs2 & 7, 5); + + target_ulong x_int = (rs1 >> 11) & 0x7ff; + target_ulong y_int = (rs1 >> 33) & 0x7ff; + target_ulong z_int = rs1 >> 55; + + target_ulong lower_x = x_int & 3; + target_ulong lower_y = y_int & 3; + target_ulong lower_z = z_int & 1; + + target_ulong middle_x = (x_int >> 2) & 15; + target_ulong middle_y = (y_int >> 2) & 15; + target_ulong middle_z = (z_int >> 1) & 15; + + target_ulong upper_x = (x_int >> 6) & ((1 << n) - 1); + target_ulong upper_y = (y_int >> 6) & ((1 << n) - 1); + target_ulong upper_z = z_int >> 5; + + return (upper_z << (17 + 2 * n)) + | (upper_y << (17 + n)) + | (upper_x << 17) + | (middle_z << 13) + | (middle_y << 9) + | (middle_x << 5) + | (lower_z << 4) + | (lower_y << 2) + | lower_x; } #if HOST_BIG_ENDIAN From 43db5838022a32752a27e64de6599b8dc427ba9f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 21:26:24 -0700 Subject: [PATCH 1201/2884] target/sparc: Rewrite gen_edge Drop the tables and compute the left and right edges directly. Signed-off-by: Richard Henderson --- target/sparc/translate.c | 98 +++++++++++++++------------------------- 1 file changed, 37 insertions(+), 61 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index dca072888a..00c2a11353 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3519,11 +3519,10 @@ static bool trans_SDIVX(DisasContext *dc, arg_r_r_ri *a) } static bool gen_edge(DisasContext *dc, arg_r_r_r *a, - int width, bool cc, bool left) + int width, bool cc, bool little_endian) { - TCGv dst, s1, s2, lo1, lo2; - uint64_t amask, tabl, tabr; - int shift, imask, omask; + TCGv dst, s1, s2, l, r, t, m; + uint64_t amask = address_mask_i(dc, -8); dst = gen_dest_gpr(dc, a->rd); s1 = gen_load_gpr(dc, a->rs1); @@ -3533,75 +3532,52 @@ static bool gen_edge(DisasContext *dc, arg_r_r_r *a, gen_op_subcc(cpu_cc_N, s1, s2); } - /* - * Theory of operation: there are two tables, left and right (not to - * be confused with the left and right versions of the opcode). These - * are indexed by the low 3 bits of the inputs. To make things "easy", - * these tables are loaded into two constants, TABL and TABR below. - * The operation index = (input & imask) << shift calculates the index - * into the constant, while val = (table >> index) & omask calculates - * the value we're looking for. - */ + l = tcg_temp_new(); + r = tcg_temp_new(); + t = tcg_temp_new(); + switch (width) { case 8: - imask = 0x7; - shift = 3; - omask = 0xff; - if (left) { - tabl = 0x80c0e0f0f8fcfeffULL; - tabr = 0xff7f3f1f0f070301ULL; - } else { - tabl = 0x0103070f1f3f7fffULL; - tabr = 0xfffefcf8f0e0c080ULL; - } + tcg_gen_andi_tl(l, s1, 7); + tcg_gen_andi_tl(r, s2, 7); + tcg_gen_xori_tl(r, r, 7); + m = tcg_constant_tl(0xff); break; case 16: - imask = 0x6; - shift = 1; - omask = 0xf; - if (left) { - tabl = 0x8cef; - tabr = 0xf731; - } else { - tabl = 0x137f; - tabr = 0xfec8; - } + tcg_gen_extract_tl(l, s1, 1, 2); + tcg_gen_extract_tl(r, s2, 1, 2); + tcg_gen_xori_tl(r, r, 3); + m = tcg_constant_tl(0xf); break; case 32: - imask = 0x4; - shift = 0; - omask = 0x3; - if (left) { - tabl = (2 << 2) | 3; - tabr = (3 << 2) | 1; - } else { - tabl = (1 << 2) | 3; - tabr = (3 << 2) | 2; - } + tcg_gen_extract_tl(l, s1, 2, 1); + tcg_gen_extract_tl(r, s2, 2, 1); + tcg_gen_xori_tl(r, r, 1); + m = tcg_constant_tl(0x3); break; default: abort(); } - lo1 = tcg_temp_new(); - lo2 = tcg_temp_new(); - tcg_gen_andi_tl(lo1, s1, imask); - tcg_gen_andi_tl(lo2, s2, imask); - tcg_gen_shli_tl(lo1, lo1, shift); - tcg_gen_shli_tl(lo2, lo2, shift); + /* Compute Left Edge */ + if (little_endian) { + tcg_gen_shl_tl(l, m, l); + tcg_gen_and_tl(l, l, m); + } else { + tcg_gen_shr_tl(l, m, l); + } + /* Compute Right Edge */ + if (little_endian) { + tcg_gen_shr_tl(r, m, r); + } else { + tcg_gen_shl_tl(r, m, r); + tcg_gen_and_tl(r, r, m); + } - tcg_gen_shr_tl(lo1, tcg_constant_tl(tabl), lo1); - tcg_gen_shr_tl(lo2, tcg_constant_tl(tabr), lo2); - tcg_gen_andi_tl(lo1, lo1, omask); - tcg_gen_andi_tl(lo2, lo2, omask); - - amask = address_mask_i(dc, -8); - tcg_gen_andi_tl(s1, s1, amask); - tcg_gen_andi_tl(s2, s2, amask); - - /* Compute dst = (s1 == s2 ? lo1 : lo1 & lo2). */ - tcg_gen_and_tl(lo2, lo2, lo1); - tcg_gen_movcond_tl(TCG_COND_EQ, dst, s1, s2, lo1, lo2); + /* Compute dst = (s1 == s2 under amask ? l : l & r) */ + tcg_gen_xor_tl(t, s1, s2); + tcg_gen_and_tl(r, r, l); + tcg_gen_movcond_tl(TCG_COND_TSTEQ, dst, t, tcg_constant_tl(amask), r, l); gen_store_gpr(dc, a->rd, dst); return advance_pc(dc); From 04d5bf30419412cfbf984e90938b411e22fd8916 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 21:33:01 -0700 Subject: [PATCH 1202/2884] target/sparc: Fix do_dc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply DFPREG to compute the register number. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 00c2a11353..1eb1a6decf 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4253,6 +4253,7 @@ static bool do_dc(DisasContext *dc, int rd, int64_t c) return true; } + rd = DFPREG(rd); tcg_gen_movi_i64(cpu_fpr[rd / 2], c); gen_update_fprs_dirty(dc, rd); return advance_pc(dc); From b5c960470d0d7a976aa83e6e3a9b0fdc1d83c7cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 May 2024 22:04:03 -0700 Subject: [PATCH 1203/2884] target/sparc: Fix helper_fmul8ulx16 This operation returns the high 16 bits of a 24-bit multiply that has been sign-extended to 32 bits. Signed-off-by: Richard Henderson --- target/sparc/vis_helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index f46fcf1f6a..41312deda4 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -174,10 +174,10 @@ uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2) s.ll = src1; d.ll = src2; - d.VIS_W64(0) = do_ms16b(s.VIS_B64(0), d.VIS_SW64(0)); - d.VIS_W64(1) = do_ms16b(s.VIS_B64(2), d.VIS_SW64(1)); - d.VIS_W64(2) = do_ms16b(s.VIS_B64(4), d.VIS_SW64(2)); - d.VIS_W64(3) = do_ms16b(s.VIS_B64(6), d.VIS_SW64(3)); + d.VIS_W64(0) = (s.VIS_B64(0) * d.VIS_SW64(0) + 0x8000) >> 16; + d.VIS_W64(1) = (s.VIS_B64(2) * d.VIS_SW64(1) + 0x8000) >> 16; + d.VIS_W64(2) = (s.VIS_B64(4) * d.VIS_SW64(2) + 0x8000) >> 16; + d.VIS_W64(3) = (s.VIS_B64(6) * d.VIS_SW64(3) + 0x8000) >> 16; return d.ll; } From 0bba7572d40d5d3f79dd2392051fe3970a41e9db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Nov 2023 13:13:05 -0700 Subject: [PATCH 1204/2884] target/sparc: Perform DFPREG/QFPREG in decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Form the proper register decoding from the start. Because we're removing the translation from the inner-most gen_load_fpr_* and gen_store_fpr_* routines, this must be done for all insns at once. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 220 +++++++++++++++++++++++--------------- target/sparc/translate.c | 39 +++---- 2 files changed, 148 insertions(+), 111 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index e2d8a07dc4..02fa505b49 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -26,6 +26,14 @@ CALL 01 i:s30 ## Major Opcode 10 -- integer, floating-point, vis, and system insns. ## +%dfp_rd 25:5 !function=extract_dfpreg +%dfp_rs1 14:5 !function=extract_dfpreg +%dfp_rs2 0:5 !function=extract_dfpreg + +%qfp_rd 25:5 !function=extract_qfpreg +%qfp_rs1 14:5 !function=extract_qfpreg +%qfp_rs2 0:5 !function=extract_qfpreg + &r_r_ri rd rs1 rs2_or_imm imm:bool @n_r_ri .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri rd=0 @r_r_ri .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri @@ -37,11 +45,40 @@ CALL 01 i:s30 &r_r_r rd rs1 rs2 @r_r_r .. rd:5 ...... rs1:5 . ........ rs2:5 &r_r_r +@d_r_r .. ..... ...... rs1:5 . ........ rs2:5 \ + &r_r_r rd=%dfp_rd +@r_d_d .. rd:5 ...... ..... . ........ ..... \ + &r_r_r rs1=%dfp_rs1 rs2=%dfp_rs2 +@d_r_d .. ..... ...... rs1:5 . ........ ..... \ + &r_r_r rd=%dfp_rd rs2=%dfp_rs2 +@d_d_d .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%dfp_rd rs1=%dfp_rs1 rs2=%dfp_rs2 +@q_q_q .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%qfp_rd rs1=%qfp_rs1 rs2=%qfp_rs2 +@q_d_d .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%qfp_rd rs1=%dfp_rs1 rs2=%dfp_rs2 + @r_r_r_swap .. rd:5 ...... rs2:5 . ........ rs1:5 &r_r_r +@d_d_d_swap .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%dfp_rd rs1=%dfp_rs2 rs2=%dfp_rs1 &r_r rd rs @r_r1 .. rd:5 ...... rs:5 . ........ ..... &r_r @r_r2 .. rd:5 ...... ..... . ........ rs:5 &r_r +@r_d2 .. rd:5 ...... ..... . ........ ..... &r_r rs=%dfp_rs2 +@r_q2 .. rd:5 ...... ..... . ........ ..... &r_r rs=%qfp_rs2 +@d_r2 .. ..... ...... ..... . ........ rs:5 &r_r rd=%dfp_rd +@q_r2 .. ..... ...... ..... . ........ rs:5 &r_r rd=%qfp_rd +@d_d1 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%dfp_rd rs=%dfp_rs1 +@d_d2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%dfp_rd rs=%dfp_rs2 +@d_q2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%dfp_rd rs=%qfp_rs2 +@q_q2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%qfp_rd rs=%qfp_rs2 +@q_d2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%qfp_rd rs=%dfp_rs2 { [ @@ -241,68 +278,78 @@ DONE 10 00000 111110 00000 0 0000000000000 RETRY 10 00001 111110 00000 0 0000000000000 FMOVs 10 ..... 110100 00000 0 0000 0001 ..... @r_r2 -FMOVd 10 ..... 110100 00000 0 0000 0010 ..... @r_r2 -FMOVq 10 ..... 110100 00000 0 0000 0011 ..... @r_r2 +FMOVd 10 ..... 110100 00000 0 0000 0010 ..... @d_d2 +FMOVq 10 ..... 110100 00000 0 0000 0011 ..... @q_q2 FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 -FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @r_r2 -FNEGq 10 ..... 110100 00000 0 0000 0111 ..... @r_r2 +FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @d_d2 +FNEGq 10 ..... 110100 00000 0 0000 0111 ..... @q_q2 FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 -FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 -FABSq 10 ..... 110100 00000 0 0000 1011 ..... @r_r2 +FABSd 10 ..... 110100 00000 0 0000 1010 ..... @d_d2 +FABSq 10 ..... 110100 00000 0 0000 1011 ..... @q_q2 FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 -FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 -FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 +FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @d_d2 +FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @q_q2 FADDs 10 ..... 110100 ..... 0 0100 0001 ..... @r_r_r -FADDd 10 ..... 110100 ..... 0 0100 0010 ..... @r_r_r -FADDq 10 ..... 110100 ..... 0 0100 0011 ..... @r_r_r +FADDd 10 ..... 110100 ..... 0 0100 0010 ..... @d_d_d +FADDq 10 ..... 110100 ..... 0 0100 0011 ..... @q_q_q FSUBs 10 ..... 110100 ..... 0 0100 0101 ..... @r_r_r -FSUBd 10 ..... 110100 ..... 0 0100 0110 ..... @r_r_r -FSUBq 10 ..... 110100 ..... 0 0100 0111 ..... @r_r_r +FSUBd 10 ..... 110100 ..... 0 0100 0110 ..... @d_d_d +FSUBq 10 ..... 110100 ..... 0 0100 0111 ..... @q_q_q FMULs 10 ..... 110100 ..... 0 0100 1001 ..... @r_r_r -FMULd 10 ..... 110100 ..... 0 0100 1010 ..... @r_r_r -FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @r_r_r +FMULd 10 ..... 110100 ..... 0 0100 1010 ..... @d_d_d +FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @q_q_q FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r -FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r -FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r -FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r -FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @r_r_r +FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @d_d_d +FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @q_q_q +FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @d_r_r +FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @q_d_d FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 -FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 -FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_r2 +FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_d2 +FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_q2 FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 -FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 -FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @r_r2 +FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_r2 +FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_r2 FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 -FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 -FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_r2 -FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @r_r2 -FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 -FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @r_r2 -FiTOq 10 ..... 110100 00000 0 1100 1100 ..... @r_r2 -FsTOq 10 ..... 110100 00000 0 1100 1101 ..... @r_r2 -FdTOq 10 ..... 110100 00000 0 1100 1110 ..... @r_r2 +FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_d2 +FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_q2 +FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @d_r2 +FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @d_r2 +FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @d_q2 +FiTOq 10 ..... 110100 00000 0 1100 1100 ..... @q_r2 +FsTOq 10 ..... 110100 00000 0 1100 1101 ..... @q_r2 +FdTOq 10 ..... 110100 00000 0 1100 1110 ..... @q_d2 FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 -FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 -FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 +FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_d2 +FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_q2 FMOVscc 10 rd:5 110101 0 cond:4 1 cc:1 0 000001 rs2:5 -FMOVdcc 10 rd:5 110101 0 cond:4 1 cc:1 0 000010 rs2:5 -FMOVqcc 10 rd:5 110101 0 cond:4 1 cc:1 0 000011 rs2:5 +FMOVdcc 10 ..... 110101 0 cond:4 1 cc:1 0 000010 ..... \ + rd=%dfp_rd rs2=%dfp_rs2 +FMOVqcc 10 ..... 110101 0 cond:4 1 cc:1 0 000011 ..... \ + rd=%qfp_rd rs2=%qfp_rs2 FMOVsfcc 10 rd:5 110101 0 cond:4 0 cc:2 000001 rs2:5 -FMOVdfcc 10 rd:5 110101 0 cond:4 0 cc:2 000010 rs2:5 -FMOVqfcc 10 rd:5 110101 0 cond:4 0 cc:2 000011 rs2:5 +FMOVdfcc 10 ..... 110101 0 cond:4 0 cc:2 000010 ..... \ + rd=%dfp_rd rs2=%dfp_rs2 +FMOVqfcc 10 ..... 110101 0 cond:4 0 cc:2 000011 ..... \ + rd=%qfp_rd rs2=%qfp_rs2 FMOVRs 10 rd:5 110101 rs1:5 0 cond:3 00101 rs2:5 -FMOVRd 10 rd:5 110101 rs1:5 0 cond:3 00110 rs2:5 -FMOVRq 10 rd:5 110101 rs1:5 0 cond:3 00111 rs2:5 +FMOVRd 10 ..... 110101 rs1:5 0 cond:3 00110 ..... \ + rd=%dfp_rd rs2=%dfp_rs2 +FMOVRq 10 ..... 110101 rs1:5 0 cond:3 00111 ..... \ + rd=%qfp_rd rs2=%qfp_rs2 FCMPs 10 000 cc:2 110101 rs1:5 0 0101 0001 rs2:5 -FCMPd 10 000 cc:2 110101 rs1:5 0 0101 0010 rs2:5 -FCMPq 10 000 cc:2 110101 rs1:5 0 0101 0011 rs2:5 +FCMPd 10 000 cc:2 110101 ..... 0 0101 0010 ..... \ + rs1=%dfp_rs1 rs2=%dfp_rs2 +FCMPq 10 000 cc:2 110101 ..... 0 0101 0011 ..... \ + rs1=%qfp_rs1 rs2=%qfp_rs2 FCMPEs 10 000 cc:2 110101 rs1:5 0 0101 0101 rs2:5 -FCMPEd 10 000 cc:2 110101 rs1:5 0 0101 0110 rs2:5 -FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 +FCMPEd 10 000 cc:2 110101 ..... 0 0101 0110 ..... \ + rs1=%dfp_rs1 rs2=%dfp_rs2 +FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ + rs1=%qfp_rs1 rs2=%qfp_rs2 { [ @@ -328,74 +375,74 @@ FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r - FPCMPLE16 10 ..... 110110 ..... 0 0010 0000 ..... @r_r_r - FPCMPNE16 10 ..... 110110 ..... 0 0010 0010 ..... @r_r_r - FPCMPGT16 10 ..... 110110 ..... 0 0010 1000 ..... @r_r_r - FPCMPEQ16 10 ..... 110110 ..... 0 0010 1010 ..... @r_r_r - FPCMPLE32 10 ..... 110110 ..... 0 0010 0100 ..... @r_r_r - FPCMPNE32 10 ..... 110110 ..... 0 0010 0110 ..... @r_r_r - FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_r_r - FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_r_r + FPCMPLE16 10 ..... 110110 ..... 0 0010 0000 ..... @r_d_d + FPCMPNE16 10 ..... 110110 ..... 0 0010 0010 ..... @r_d_d + FPCMPGT16 10 ..... 110110 ..... 0 0010 1000 ..... @r_d_d + FPCMPEQ16 10 ..... 110110 ..... 0 0010 1010 ..... @r_d_d + FPCMPLE32 10 ..... 110110 ..... 0 0010 0100 ..... @r_d_d + FPCMPNE32 10 ..... 110110 ..... 0 0010 0110 ..... @r_d_d + FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_d_d + FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_d_d - FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @r_r_r - FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @r_r_r - FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @r_r_r - FMUL8SUx16 10 ..... 110110 ..... 0 0011 0110 ..... @r_r_r - FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @r_r_r - FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @r_r_r - FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @r_r_r - FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @r_r_r - FPACK16 10 ..... 110110 00000 0 0011 1011 ..... @r_r2 - FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_r2 - PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @r_r_r + FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @d_r_d + FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @d_r_r + FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @d_r_r + FMUL8SUx16 10 ..... 110110 ..... 0 0011 0110 ..... @d_d_d + FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @d_d_d + FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @d_r_r + FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @d_r_r + FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @d_d_d + FPACK16 10 ..... 110110 00000 0 0011 1011 ..... @r_d2 + FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_d2 + PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @d_d_d - FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @r_r_r - FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r - BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @r_r_r - FEXPAND 10 ..... 110110 00000 0 0100 1101 ..... @r_r2 + FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @d_d_d + FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r + BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @d_d_d + FEXPAND 10 ..... 110110 00000 0 0100 1101 ..... @d_r2 - FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d + FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @d_d1 # FSRC1d FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s - FSRCd 10 ..... 110110 00000 0 0111 1000 ..... @r_r2 # FSRC2d + FSRCd 10 ..... 110110 00000 0 0111 1000 ..... @d_d2 # FSRC2d FSRCs 10 ..... 110110 00000 0 0111 1001 ..... @r_r2 # FSRC2s - FNOTd 10 ..... 110110 ..... 0 0110 1010 00000 @r_r1 # FNOT1d + FNOTd 10 ..... 110110 ..... 0 0110 1010 00000 @d_d1 # FNOT1d FNOTs 10 ..... 110110 ..... 0 0110 1011 00000 @r_r1 # FNOT1s - FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @r_r2 # FNOT2d + FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @d_d2 # FNOT2d FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s - FPADD16 10 ..... 110110 ..... 0 0101 0000 ..... @r_r_r + FPADD16 10 ..... 110110 ..... 0 0101 0000 ..... @d_d_d FPADD16s 10 ..... 110110 ..... 0 0101 0001 ..... @r_r_r - FPADD32 10 ..... 110110 ..... 0 0101 0010 ..... @r_r_r + FPADD32 10 ..... 110110 ..... 0 0101 0010 ..... @d_d_d FPADD32s 10 ..... 110110 ..... 0 0101 0011 ..... @r_r_r - FPSUB16 10 ..... 110110 ..... 0 0101 0100 ..... @r_r_r + FPSUB16 10 ..... 110110 ..... 0 0101 0100 ..... @d_d_d FPSUB16s 10 ..... 110110 ..... 0 0101 0101 ..... @r_r_r - FPSUB32 10 ..... 110110 ..... 0 0101 0110 ..... @r_r_r + FPSUB32 10 ..... 110110 ..... 0 0101 0110 ..... @d_d_d FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r - FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @r_r_r + FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @d_d_d FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r - FANDNOTd 10 ..... 110110 ..... 0 0110 0100 ..... @r_r_r # FANDNOT2d + FANDNOTd 10 ..... 110110 ..... 0 0110 0100 ..... @d_d_d # FANDNOT2d FANDNOTs 10 ..... 110110 ..... 0 0110 0101 ..... @r_r_r # FANDNOT2s - FANDNOTd 10 ..... 110110 ..... 0 0110 1000 ..... @r_r_r_swap # ... 1d + FANDNOTd 10 ..... 110110 ..... 0 0110 1000 ..... @d_d_d_swap # ... 1d FANDNOTs 10 ..... 110110 ..... 0 0110 1001 ..... @r_r_r_swap # ... 1s - FXORd 10 ..... 110110 ..... 0 0110 1100 ..... @r_r_r + FXORd 10 ..... 110110 ..... 0 0110 1100 ..... @d_d_d FXORs 10 ..... 110110 ..... 0 0110 1101 ..... @r_r_r - FNANDd 10 ..... 110110 ..... 0 0110 1110 ..... @r_r_r + FNANDd 10 ..... 110110 ..... 0 0110 1110 ..... @d_d_d FNANDs 10 ..... 110110 ..... 0 0110 1111 ..... @r_r_r - FANDd 10 ..... 110110 ..... 0 0111 0000 ..... @r_r_r + FANDd 10 ..... 110110 ..... 0 0111 0000 ..... @d_d_d FANDs 10 ..... 110110 ..... 0 0111 0001 ..... @r_r_r - FXNORd 10 ..... 110110 ..... 0 0111 0010 ..... @r_r_r + FXNORd 10 ..... 110110 ..... 0 0111 0010 ..... @d_d_d FXNORs 10 ..... 110110 ..... 0 0111 0011 ..... @r_r_r - FORNOTd 10 ..... 110110 ..... 0 0111 0110 ..... @r_r_r # FORNOT2d + FORNOTd 10 ..... 110110 ..... 0 0111 0110 ..... @d_d_d # FORNOT2d FORNOTs 10 ..... 110110 ..... 0 0111 0111 ..... @r_r_r # FORNOT2s - FORNOTd 10 ..... 110110 ..... 0 0111 1010 ..... @r_r_r_swap # ... 1d + FORNOTd 10 ..... 110110 ..... 0 0111 1010 ..... @d_d_d_swap # ... 1d FORNOTs 10 ..... 110110 ..... 0 0111 1011 ..... @r_r_r_swap # ... 1s - FORd 10 ..... 110110 ..... 0 0111 1100 ..... @r_r_r + FORd 10 ..... 110110 ..... 0 0111 1100 ..... @d_d_d FORs 10 ..... 110110 ..... 0 0111 1101 ..... @r_r_r - FZEROd 10 rd:5 110110 00000 0 0110 0000 00000 + FZEROd 10 ..... 110110 00000 0 0110 0000 00000 rd=%dfp_rd FZEROs 10 rd:5 110110 00000 0 0110 0001 00000 - FONEd 10 rd:5 110110 00000 0 0111 1110 00000 + FONEd 10 ..... 110110 00000 0 0111 1110 00000 rd=%dfp_rd FONEs 10 rd:5 110110 00000 0 0111 1111 00000 ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 @@ -407,9 +454,6 @@ NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 ## Major Opcode 11 -- load and store instructions ## -%dfp_rd 25:5 !function=extract_dfpreg -%qfp_rd 25:5 !function=extract_qfpreg - &r_r_ri_asi rd rs1 rs2_or_imm asi imm:bool @r_r_ri_na .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_asi asi=-1 @d_r_ri_na .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 \ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 1eb1a6decf..f3c52c7c48 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -190,14 +190,6 @@ typedef struct DisasContext { #define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1) #define GET_FIELD_SPs(x,a,b) sign_extend (GET_FIELD_SP(x,a,b), ((b) - (a) + 1)) -#ifdef TARGET_SPARC64 -#define DFPREG(r) (((r & 1) << 5) | (r & 0x1e)) -#define QFPREG(r) (((r & 1) << 5) | (r & 0x1c)) -#else -#define DFPREG(r) (r & 0x1e) -#define QFPREG(r) (r & 0x1c) -#endif - #define UA2005_HTRAP_MASK 0xff #define V8_TRAP_MASK 0x7f @@ -240,34 +232,30 @@ static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v) static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) { - src = DFPREG(src); return cpu_fpr[src / 2]; } static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) { - dst = DFPREG(dst); tcg_gen_mov_i64(cpu_fpr[dst / 2], v); gen_update_fprs_dirty(dc, dst); } static TCGv_i64 gen_dest_fpr_D(DisasContext *dc, unsigned int dst) { - return cpu_fpr[DFPREG(dst) / 2]; + return cpu_fpr[dst / 2]; } static TCGv_i128 gen_load_fpr_Q(DisasContext *dc, unsigned int src) { TCGv_i128 ret = tcg_temp_new_i128(); - src = QFPREG(src); tcg_gen_concat_i64_i128(ret, cpu_fpr[src / 2 + 1], cpu_fpr[src / 2]); return ret; } static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, TCGv_i128 v) { - dst = DFPREG(dst); tcg_gen_extr_i128_i64(cpu_fpr[dst / 2 + 1], cpu_fpr[dst / 2], v); gen_update_fprs_dirty(dc, dst); } @@ -2045,16 +2033,14 @@ static void gen_fmovd(DisasContext *dc, DisasCompare *cmp, int rd, int rs) static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { #ifdef TARGET_SPARC64 - int qd = QFPREG(rd); - int qs = QFPREG(rs); TCGv c2 = tcg_constant_tl(cmp->c2); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, c2, - cpu_fpr[qs / 2], cpu_fpr[qd / 2]); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, c2, - cpu_fpr[qs / 2 + 1], cpu_fpr[qd / 2 + 1]); + tcg_gen_movcond_i64(cmp->cond, cpu_fpr[rd / 2], cmp->c1, c2, + cpu_fpr[rs / 2], cpu_fpr[rd / 2]); + tcg_gen_movcond_i64(cmp->cond, cpu_fpr[rd / 2 + 1], cmp->c1, c2, + cpu_fpr[rs / 2 + 1], cpu_fpr[rd / 2 + 1]); - gen_update_fprs_dirty(dc, qd); + gen_update_fprs_dirty(dc, rd); #else qemu_build_not_reached(); #endif @@ -2086,12 +2072,20 @@ static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) static int extract_dfpreg(DisasContext *dc, int x) { - return DFPREG(x); + int r = x & 0x1e; +#ifdef TARGET_SPARC64 + r |= (x & 1) << 5; +#endif + return r; } static int extract_qfpreg(DisasContext *dc, int x) { - return QFPREG(x); + int r = x & 0x1c; +#ifdef TARGET_SPARC64 + r |= (x & 1) << 5; +#endif + return r; } /* Include the auto-generated decoder. */ @@ -4253,7 +4247,6 @@ static bool do_dc(DisasContext *dc, int rd, int64_t c) return true; } - rd = DFPREG(rd); tcg_gen_movi_i64(cpu_fpr[rd / 2], c); gen_update_fprs_dirty(dc, rd); return advance_pc(dc); From 52f46d4627a3f5ce52636ff6b01895e4fcd5e924 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Nov 2023 13:21:36 -0700 Subject: [PATCH 1205/2884] target/sparc: Remove gen_dest_fpr_D MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace with tcg_temp_new_i64. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/translate.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index f3c52c7c48..750a3e6554 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -241,11 +241,6 @@ static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) gen_update_fprs_dirty(dc, dst); } -static TCGv_i64 gen_dest_fpr_D(DisasContext *dc, unsigned int dst) -{ - return cpu_fpr[dst / 2]; -} - static TCGv_i128 gen_load_fpr_Q(DisasContext *dc, unsigned int src) { TCGv_i128 ret = tcg_temp_new_i128(); @@ -2020,7 +2015,7 @@ static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) static void gen_fmovd(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { #ifdef TARGET_SPARC64 - TCGv_i64 dst = gen_dest_fpr_D(dc, rd); + TCGv_i64 dst = tcg_temp_new_i64(); tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, tcg_constant_tl(cmp->c2), gen_load_fpr_D(dc, rs), gen_load_fpr_D(dc, rd)); @@ -4345,7 +4340,7 @@ static bool do_dd(DisasContext *dc, arg_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src = gen_load_fpr_D(dc, a->rs); func(dst, src); gen_store_fpr_D(dc, a->rd, dst); @@ -4367,7 +4362,7 @@ static bool do_env_dd(DisasContext *dc, arg_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src = gen_load_fpr_D(dc, a->rs); func(dst, tcg_env, src); gen_store_fpr_D(dc, a->rd, dst); @@ -4407,7 +4402,7 @@ static bool do_env_df(DisasContext *dc, arg_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src = gen_load_fpr_F(dc, a->rs); func(dst, tcg_env, src); gen_store_fpr_D(dc, a->rd, dst); @@ -4498,7 +4493,7 @@ static bool do_env_dq(DisasContext *dc, arg_r_r *a, } src = gen_load_fpr_Q(dc, a->rs); - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); func(dst, tcg_env, src); gen_store_fpr_D(dc, a->rd, dst); return advance_pc(dc); @@ -4613,7 +4608,7 @@ static bool do_dff(DisasContext *dc, arg_r_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src1 = gen_load_fpr_F(dc, a->rs1); src2 = gen_load_fpr_F(dc, a->rs2); func(dst, src1, src2); @@ -4637,7 +4632,7 @@ static bool do_dfd(DisasContext *dc, arg_r_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src1 = gen_load_fpr_F(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); func(dst, src1, src2); @@ -4656,7 +4651,7 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src1 = gen_load_fpr_D(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); func(dst, src1, src2); @@ -4721,7 +4716,7 @@ static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src1 = gen_load_fpr_D(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); func(dst, tcg_env, src1, src2); @@ -4746,7 +4741,7 @@ static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) return raise_unimpfpop(dc); } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src1 = gen_load_fpr_F(dc, a->rs1); src2 = gen_load_fpr_F(dc, a->rs2); gen_helper_fsmuld(dst, tcg_env, src1, src2); @@ -4763,7 +4758,7 @@ static bool do_dddd(DisasContext *dc, arg_r_r_r *a, return true; } - dst = gen_dest_fpr_D(dc, a->rd); + dst = tcg_temp_new_i64(); src0 = gen_load_fpr_D(dc, a->rd); src1 = gen_load_fpr_D(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); From 1210a0367d5e9eca7679d401e422c48b61b421b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Nov 2023 14:38:55 -0700 Subject: [PATCH 1206/2884] target/sparc: Remove cpu_fpr[] Use explicit loads and stores to env instead. Signed-off-by: Richard Henderson --- target/sparc/translate.c | 158 +++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 74 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 750a3e6554..362e88de18 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -123,8 +123,7 @@ static TCGv cpu_gsr; #define cpu_xcc_C ({ qemu_build_not_reached(); NULL; }) #endif -/* Floating point registers */ -static TCGv_i64 cpu_fpr[TARGET_DPREGS]; +/* Floating point comparison registers */ static TCGv_i32 cpu_fcc[TARGET_FCCREGS]; #define env_field_offsetof(X) offsetof(CPUSPARCState, X) @@ -209,50 +208,72 @@ static void gen_update_fprs_dirty(DisasContext *dc, int rd) } /* floating point registers moves */ + +static int gen_offset_fpr_F(unsigned int reg) +{ + int ret; + + tcg_debug_assert(reg < 32); + ret= offsetof(CPUSPARCState, fpr[reg / 2]); + if (reg & 1) { + ret += offsetof(CPU_DoubleU, l.lower); + } else { + ret += offsetof(CPU_DoubleU, l.upper); + } + return ret; +} + static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src) { TCGv_i32 ret = tcg_temp_new_i32(); - if (src & 1) { - tcg_gen_extrl_i64_i32(ret, cpu_fpr[src / 2]); - } else { - tcg_gen_extrh_i64_i32(ret, cpu_fpr[src / 2]); - } + tcg_gen_ld_i32(ret, tcg_env, gen_offset_fpr_F(src)); return ret; } static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v) { - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_extu_i32_i64(t, v); - tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t, - (dst & 1 ? 0 : 32), 32); + tcg_gen_st_i32(v, tcg_env, gen_offset_fpr_F(dst)); gen_update_fprs_dirty(dc, dst); } +static int gen_offset_fpr_D(unsigned int reg) +{ + tcg_debug_assert(reg < 64); + tcg_debug_assert(reg % 2 == 0); + return offsetof(CPUSPARCState, fpr[reg / 2]); +} + static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) { - return cpu_fpr[src / 2]; + TCGv_i64 ret = tcg_temp_new_i64(); + tcg_gen_ld_i64(ret, tcg_env, gen_offset_fpr_D(src)); + return ret; } static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) { - tcg_gen_mov_i64(cpu_fpr[dst / 2], v); + tcg_gen_st_i64(v, tcg_env, gen_offset_fpr_D(dst)); gen_update_fprs_dirty(dc, dst); } static TCGv_i128 gen_load_fpr_Q(DisasContext *dc, unsigned int src) { TCGv_i128 ret = tcg_temp_new_i128(); + TCGv_i64 h = gen_load_fpr_D(dc, src); + TCGv_i64 l = gen_load_fpr_D(dc, src + 2); - tcg_gen_concat_i64_i128(ret, cpu_fpr[src / 2 + 1], cpu_fpr[src / 2]); + tcg_gen_concat_i64_i128(ret, l, h); return ret; } static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, TCGv_i128 v) { - tcg_gen_extr_i128_i64(cpu_fpr[dst / 2 + 1], cpu_fpr[dst / 2], v); - gen_update_fprs_dirty(dc, dst); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 l = tcg_temp_new_i64(); + + tcg_gen_extr_i128_i64(l, h, v); + gen_store_fpr_D(dc, dst, h); + gen_store_fpr_D(dc, dst + 2, l); } /* moves */ @@ -1610,7 +1631,7 @@ static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, MemOp memop = da->memop; MemOp size = memop & MO_SIZE; TCGv_i32 d32; - TCGv_i64 d64; + TCGv_i64 d64, l64; TCGv addr_tmp; /* TODO: Use 128-bit load/store below. */ @@ -1632,16 +1653,20 @@ static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, break; case MO_64: - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, memop); + d64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); + gen_store_fpr_D(dc, rd, d64); break; case MO_128: d64 = tcg_temp_new_i64(); + l64 = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); addr_tmp = tcg_temp_new(); tcg_gen_addi_tl(addr_tmp, addr, 8); - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + 1], addr_tmp, da->mem_idx, memop); - tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); + tcg_gen_qemu_ld_i64(l64, addr_tmp, da->mem_idx, memop); + gen_store_fpr_D(dc, rd, d64); + gen_store_fpr_D(dc, rd + 2, l64); break; default: g_assert_not_reached(); @@ -1653,9 +1678,11 @@ static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, if (orig_size == MO_64 && (rd & 7) == 0) { /* The first operation checks required alignment. */ addr_tmp = tcg_temp_new(); + d64 = tcg_temp_new_i64(); for (int i = 0; ; ++i) { - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop | (i == 0 ? MO_ALIGN_64 : 0)); + gen_store_fpr_D(dc, rd + 2 * i, d64); if (i == 7) { break; } @@ -1670,8 +1697,9 @@ static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, case GET_ASI_SHORT: /* Valid for lddfa only. */ if (orig_size == MO_64) { - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, - memop | MO_ALIGN); + d64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop | MO_ALIGN); + gen_store_fpr_D(dc, rd, d64); } else { gen_exception(dc, TT_ILL_INSN); } @@ -1696,17 +1724,19 @@ static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, gen_store_fpr_F(dc, rd, d32); break; case MO_64: - gen_helper_ld_asi(cpu_fpr[rd / 2], tcg_env, addr, - r_asi, r_mop); + d64 = tcg_temp_new_i64(); + gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); + gen_store_fpr_D(dc, rd, d64); break; case MO_128: d64 = tcg_temp_new_i64(); + l64 = tcg_temp_new_i64(); gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); addr_tmp = tcg_temp_new(); tcg_gen_addi_tl(addr_tmp, addr, 8); - gen_helper_ld_asi(cpu_fpr[rd / 2 + 1], tcg_env, addr_tmp, - r_asi, r_mop); - tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); + gen_helper_ld_asi(l64, tcg_env, addr_tmp, r_asi, r_mop); + gen_store_fpr_D(dc, rd, d64); + gen_store_fpr_D(dc, rd + 2, l64); break; default: g_assert_not_reached(); @@ -1722,6 +1752,7 @@ static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, MemOp memop = da->memop; MemOp size = memop & MO_SIZE; TCGv_i32 d32; + TCGv_i64 d64; TCGv addr_tmp; /* TODO: Use 128-bit load/store below. */ @@ -1741,8 +1772,8 @@ static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, tcg_gen_qemu_st_i32(d32, addr, da->mem_idx, memop | MO_ALIGN); break; case MO_64: - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, - memop | MO_ALIGN_4); + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | MO_ALIGN_4); break; case MO_128: /* Only 4-byte alignment required. However, it is legal for the @@ -1750,11 +1781,12 @@ static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, required to fix it up. Requiring 16-byte alignment here avoids having to probe the second page before performing the first write. */ - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, - memop | MO_ALIGN_16); + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | MO_ALIGN_16); addr_tmp = tcg_temp_new(); tcg_gen_addi_tl(addr_tmp, addr, 8); - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + 1], addr_tmp, da->mem_idx, memop); + d64 = gen_load_fpr_D(dc, rd + 2); + tcg_gen_qemu_st_i64(d64, addr_tmp, da->mem_idx, memop); break; default: g_assert_not_reached(); @@ -1767,7 +1799,8 @@ static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, /* The first operation checks required alignment. */ addr_tmp = tcg_temp_new(); for (int i = 0; ; ++i) { - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, + d64 = gen_load_fpr_D(dc, rd + 2 * i); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; @@ -1783,8 +1816,8 @@ static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, case GET_ASI_SHORT: /* Valid for stdfa only. */ if (orig_size == MO_64) { - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, - memop | MO_ALIGN); + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | MO_ALIGN); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2029,13 +2062,17 @@ static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { #ifdef TARGET_SPARC64 TCGv c2 = tcg_constant_tl(cmp->c2); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 l = tcg_temp_new_i64(); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[rd / 2], cmp->c1, c2, - cpu_fpr[rs / 2], cpu_fpr[rd / 2]); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[rd / 2 + 1], cmp->c1, c2, - cpu_fpr[rs / 2 + 1], cpu_fpr[rd / 2 + 1]); - - gen_update_fprs_dirty(dc, rd); + tcg_gen_movcond_i64(cmp->cond, h, cmp->c1, c2, + gen_load_fpr_D(dc, rs), + gen_load_fpr_D(dc, rd)); + tcg_gen_movcond_i64(cmp->cond, l, cmp->c1, c2, + gen_load_fpr_D(dc, rs + 2), + gen_load_fpr_D(dc, rd + 2)); + gen_store_fpr_D(dc, rd, h); + gen_store_fpr_D(dc, rd + 2, l); #else qemu_build_not_reached(); #endif @@ -4211,39 +4248,24 @@ static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) TRANS(STFSR, ALL, do_stfsr, a, MO_TEUL) TRANS(STXFSR, 64, do_stfsr, a, MO_TEUQ) -static bool do_fc(DisasContext *dc, int rd, bool c) +static bool do_fc(DisasContext *dc, int rd, int32_t c) { - uint64_t mask; - if (gen_trap_ifnofpu(dc)) { return true; } - - if (rd & 1) { - mask = MAKE_64BIT_MASK(0, 32); - } else { - mask = MAKE_64BIT_MASK(32, 32); - } - if (c) { - tcg_gen_ori_i64(cpu_fpr[rd / 2], cpu_fpr[rd / 2], mask); - } else { - tcg_gen_andi_i64(cpu_fpr[rd / 2], cpu_fpr[rd / 2], ~mask); - } - gen_update_fprs_dirty(dc, rd); + gen_store_fpr_F(dc, rd, tcg_constant_i32(c)); return advance_pc(dc); } TRANS(FZEROs, VIS1, do_fc, a->rd, 0) -TRANS(FONEs, VIS1, do_fc, a->rd, 1) +TRANS(FONEs, VIS1, do_fc, a->rd, -1) static bool do_dc(DisasContext *dc, int rd, int64_t c) { if (gen_trap_ifnofpu(dc)) { return true; } - - tcg_gen_movi_i64(cpu_fpr[rd / 2], c); - gen_update_fprs_dirty(dc, rd); + gen_store_fpr_D(dc, rd, tcg_constant_i64(c)); return advance_pc(dc); } @@ -5137,12 +5159,6 @@ void sparc_tcg_init(void) "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7", }; - static const char fregnames[32][4] = { - "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14", - "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30", - "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46", - "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62", - }; static const struct { TCGv_i32 *ptr; int off; const char *name; } r32[] = { #ifdef TARGET_SPARC64 @@ -5199,12 +5215,6 @@ void sparc_tcg_init(void) (i - 8) * sizeof(target_ulong), gregnames[i]); } - - for (i = 0; i < TARGET_DPREGS; i++) { - cpu_fpr[i] = tcg_global_mem_new_i64(tcg_env, - offsetof(CPUSPARCState, fpr[i]), - fregnames[i]); - } } void sparc_restore_state_to_opc(CPUState *cs, From 28c131a34d402811bdb51a0f289bd975074884bc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Nov 2023 14:52:45 -0700 Subject: [PATCH 1207/2884] target/sparc: Use gvec for VIS1 parallel add/sub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/translate.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 362e88de18..7c290293ea 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4664,6 +4664,24 @@ static bool do_dfd(DisasContext *dc, arg_r_r_r *a, TRANS(FMUL8x16, VIS1, do_dfd, a, gen_helper_fmul8x16) +static bool do_gvec_ddd(DisasContext *dc, arg_r_r_r *a, MemOp vece, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t)) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + + func(vece, gen_offset_fpr_D(a->rd), gen_offset_fpr_D(a->rs1), + gen_offset_fpr_D(a->rs2), 8, 8); + return advance_pc(dc); +} + +TRANS(FPADD16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_add) +TRANS(FPADD32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_add) +TRANS(FPSUB16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sub) +TRANS(FPSUB32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sub) + static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -4684,10 +4702,6 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a, TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) -TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64) -TRANS(FPADD32, VIS1, do_ddd, a, tcg_gen_vec_add32_i64) -TRANS(FPSUB16, VIS1, do_ddd, a, tcg_gen_vec_sub16_i64) -TRANS(FPSUB32, VIS1, do_ddd, a, tcg_gen_vec_sub32_i64) TRANS(FNORd, VIS1, do_ddd, a, tcg_gen_nor_i64) TRANS(FANDNOTd, VIS1, do_ddd, a, tcg_gen_andc_i64) TRANS(FXORd, VIS1, do_ddd, a, tcg_gen_xor_i64) From 4fd71d19acd6e05b74927a0b5c4a5b0650e3d6f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 12:13:00 -0700 Subject: [PATCH 1208/2884] target/sparc: Implement FMAf extension Rearrange PDIST so that do_dddd is general purpose and may be re-used for FMADDd etc. Add pickNaN and pickNaNMulAdd. Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.c.inc | 31 +++++++++++++ linux-user/elfload.c | 1 + target/sparc/cpu-feature.h.inc | 1 + target/sparc/cpu.c | 3 ++ target/sparc/fop_helper.c | 16 +++++++ target/sparc/helper.h | 2 + target/sparc/insns.decode | 23 +++++++++- target/sparc/translate.c | 84 ++++++++++++++++++++++++++++++++-- 8 files changed, 155 insertions(+), 6 deletions(-) diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index f573014532..8f3b97d9bf 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -447,6 +447,17 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls, } else { return 1; } +#elif defined(TARGET_SPARC) + /* Prefer SNaN over QNaN, order B then A. */ + if (is_snan(b_cls)) { + return 1; + } else if (is_snan(a_cls)) { + return 0; + } else if (is_qnan(b_cls)) { + return 1; + } else { + return 0; + } #elif defined(TARGET_XTENSA) /* * Xtensa has two NaN propagation modes. @@ -624,6 +635,26 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, float_raise(float_flag_invalid | float_flag_invalid_imz, status); } return 3; /* default NaN */ +#elif defined(TARGET_SPARC) + /* For (inf,0,nan) return c. */ + if (infzero) { + float_raise(float_flag_invalid | float_flag_invalid_imz, status); + return 2; + } + /* Prefer SNaN over QNaN, order C, B, A. */ + if (is_snan(c_cls)) { + return 2; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_snan(a_cls)) { + return 0; + } else if (is_qnan(c_cls)) { + return 2; + } else if (is_qnan(b_cls)) { + return 1; + } else { + return 0; + } #elif defined(TARGET_XTENSA) /* * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c1e1511ff2..6a1457346a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1003,6 +1003,7 @@ static uint32_t get_elf_hwcap(void) r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; + r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; #endif return r; diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc index d800f18c4e..a30b9255b2 100644 --- a/target/sparc/cpu-feature.h.inc +++ b/target/sparc/cpu-feature.h.inc @@ -12,3 +12,4 @@ FEATURE(ASR17) FEATURE(CACHE_CTRL) FEATURE(POWERDOWN) FEATURE(CASA) +FEATURE(FMAF) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 5be1592e66..ed9238a69d 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -549,6 +549,7 @@ static const char * const feature_name[] = { [CPU_FEATURE_BIT_HYPV] = "hypv", [CPU_FEATURE_BIT_VIS1] = "vis1", [CPU_FEATURE_BIT_VIS2] = "vis2", + [CPU_FEATURE_BIT_FMAF] = "fmaf", #else [CPU_FEATURE_BIT_MUL] = "mul", [CPU_FEATURE_BIT_DIV] = "div", @@ -877,6 +878,8 @@ static Property sparc_cpu_properties[] = { CPU_FEATURE_BIT_VIS1, false), DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, CPU_FEATURE_BIT_VIS2, false), + DEFINE_PROP_BIT("fmaf", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FMAF, false), #else DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, CPU_FEATURE_BIT_MUL, false), diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 1205a599ef..1de44d79c1 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -343,6 +343,22 @@ Int128 helper_fsqrtq(CPUSPARCState *env, Int128 src) return f128_ret(ret); } +float32 helper_fmadds(CPUSPARCState *env, float32 s1, + float32 s2, float32 s3, uint32_t op) +{ + float32 ret = float32_muladd(s1, s2, s3, op, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fmaddd(CPUSPARCState *env, float64 s1, + float64 s2, float64 s3, uint32_t op) +{ + float64 ret = float64_muladd(s1, s2, s3, op, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + static uint32_t finish_fcmp(CPUSPARCState *env, FloatRelation r, uintptr_t ra) { check_ieee_exceptions(env, ra); diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 97fbf6f66c..f4d3311ac4 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -56,6 +56,7 @@ DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_5(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, i32) DEF_HELPER_FLAGS_3(faddq, TCG_CALL_NO_WG, i128, env, i128, i128) DEF_HELPER_FLAGS_3(fsubq, TCG_CALL_NO_WG, i128, env, i128, i128) @@ -66,6 +67,7 @@ DEF_HELPER_FLAGS_3(fadds, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_5(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, i32) DEF_HELPER_FLAGS_3(fsmuld, TCG_CALL_NO_WG, f64, env, f32, f32) DEF_HELPER_FLAGS_3(fdmulq, TCG_CALL_NO_WG, i128, env, f64, f64) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 02fa505b49..056fba98f9 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -29,6 +29,7 @@ CALL 01 i:s30 %dfp_rd 25:5 !function=extract_dfpreg %dfp_rs1 14:5 !function=extract_dfpreg %dfp_rs2 0:5 !function=extract_dfpreg +%dfp_rs3 9:5 !function=extract_dfpreg %qfp_rd 25:5 !function=extract_qfpreg %qfp_rs1 14:5 !function=extract_qfpreg @@ -80,6 +81,11 @@ CALL 01 i:s30 @q_d2 .. ..... ...... ..... . ........ ..... \ &r_r rd=%qfp_rd rs=%dfp_rs2 +&r_r_r_r rd rs1 rs2 rs3 +@r_r_r_r .. rd:5 ...... rs1:5 rs3:5 .... rs2:5 &r_r_r_r +@d_d_d_d .. ..... ...... ..... ..... .... ..... \ + &r_r_r_r rd=%dfp_rd rs1=%dfp_rs1 rs2=%dfp_rs2 rs3=%dfp_rs3 + { [ STBAR 10 00000 101000 01111 0 0000000000000 @@ -394,7 +400,8 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @d_d_d FPACK16 10 ..... 110110 00000 0 0011 1011 ..... @r_d2 FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_d2 - PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @d_d_d + PDIST 10 ..... 110110 ..... 0 0011 1110 ..... \ + &r_r_r_r rd=%dfp_rd rs1=%dfp_rd rs2=%dfp_rs1 rs3=%dfp_rs2 FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @d_d_d FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r @@ -448,7 +455,19 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } -NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 +{ + [ + FMADDs 10 ..... 110111 ..... ..... 0001 ..... @r_r_r_r + FMADDd 10 ..... 110111 ..... ..... 0010 ..... @d_d_d_d + FMSUBs 10 ..... 110111 ..... ..... 0101 ..... @r_r_r_r + FMSUBd 10 ..... 110111 ..... ..... 0110 ..... @d_d_d_d + FNMSUBs 10 ..... 110111 ..... ..... 1001 ..... @r_r_r_r + FNMSUBd 10 ..... 110111 ..... ..... 1010 ..... @d_d_d_d + FNMADDs 10 ..... 110111 ..... ..... 1101 ..... @r_r_r_r + FNMADDd 10 ..... 110111 ..... ..... 1110 ..... @d_d_d_d + ] + NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 +} ## ## Major Opcode 11 -- load and store instructions diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7c290293ea..5efd09f4f4 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -28,6 +28,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" #include "exec/log.h" +#include "fpu/softfloat.h" #include "asi.h" #define HELPER_H "helper.h" @@ -1142,6 +1143,52 @@ static void gen_op_fabsq(TCGv_i128 dst, TCGv_i128 src) tcg_gen_concat_i64_i128(dst, l, h); } +static void gen_op_fmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) +{ + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(0)); +} + +static void gen_op_fmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) +{ + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(0)); +} + +static void gen_op_fmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) +{ + int op = float_muladd_negate_c; + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + +static void gen_op_fmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) +{ + int op = float_muladd_negate_c; + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + +static void gen_op_fnmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) +{ + int op = float_muladd_negate_c | float_muladd_negate_result; + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + +static void gen_op_fnmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) +{ + int op = float_muladd_negate_c | float_muladd_negate_result; + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + +static void gen_op_fnmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) +{ + int op = float_muladd_negate_result; + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + +static void gen_op_fnmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) +{ + int op = float_muladd_negate_result; + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + static void gen_op_fpexception_im(DisasContext *dc, int ftt) { /* @@ -2136,6 +2183,7 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_MUL(C) true # define avail_POWERDOWN(C) false # define avail_64(C) true +# define avail_FMAF(C) ((C)->def->features & CPU_FEATURE_FMAF) # define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) # define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) # define avail_VIS1(C) ((C)->def->features & CPU_FEATURE_VIS1) @@ -2148,6 +2196,7 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_MUL(C) ((C)->def->features & CPU_FEATURE_MUL) # define avail_POWERDOWN(C) ((C)->def->features & CPU_FEATURE_POWERDOWN) # define avail_64(C) false +# define avail_FMAF(C) false # define avail_GL(C) false # define avail_HYPV(C) false # define avail_VIS1(C) false @@ -4785,25 +4834,52 @@ static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) return advance_pc(dc); } -static bool do_dddd(DisasContext *dc, arg_r_r_r *a, +static bool do_ffff(DisasContext *dc, arg_r_r_r_r *a, + void (*func)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 dst, src1, src2, src3; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + src3 = gen_load_fpr_F(dc, a->rs3); + dst = tcg_temp_new_i32(); + func(dst, src1, src2, src3); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMADDs, FMAF, do_ffff, a, gen_op_fmadds) +TRANS(FMSUBs, FMAF, do_ffff, a, gen_op_fmsubs) +TRANS(FNMSUBs, FMAF, do_ffff, a, gen_op_fnmsubs) +TRANS(FNMADDs, FMAF, do_ffff, a, gen_op_fnmadds) + +static bool do_dddd(DisasContext *dc, arg_r_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { - TCGv_i64 dst, src0, src1, src2; + TCGv_i64 dst, src1, src2, src3; if (gen_trap_ifnofpu(dc)) { return true; } dst = tcg_temp_new_i64(); - src0 = gen_load_fpr_D(dc, a->rd); src1 = gen_load_fpr_D(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); - func(dst, src0, src1, src2); + src3 = gen_load_fpr_D(dc, a->rs3); + func(dst, src1, src2, src3); gen_store_fpr_D(dc, a->rd, dst); return advance_pc(dc); } TRANS(PDIST, VIS1, do_dddd, a, gen_helper_pdist) +TRANS(FMADDd, FMAF, do_dddd, a, gen_op_fmaddd) +TRANS(FMSUBd, FMAF, do_dddd, a, gen_op_fmsubd) +TRANS(FNMSUBd, FMAF, do_dddd, a, gen_op_fnmsubd) +TRANS(FNMADDd, FMAF, do_dddd, a, gen_op_fnmaddd) static bool do_env_qqq(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i128, TCGv_env, TCGv_i128, TCGv_i128)) From 3335a04806d337c69f44a707cdc27515d6c91d84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 12:21:37 -0700 Subject: [PATCH 1209/2884] target/sparc: Add feature bits for VIS 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The manual separates VIS 3 and VIS 3B, even though they are both present in all extant cpus. For clarity, let the translator match the manual but otherwise leave them on the same feature bit. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu-feature.h.inc | 1 + target/sparc/translate.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc index a30b9255b2..3913fb4a54 100644 --- a/target/sparc/cpu-feature.h.inc +++ b/target/sparc/cpu-feature.h.inc @@ -13,3 +13,4 @@ FEATURE(CACHE_CTRL) FEATURE(POWERDOWN) FEATURE(CASA) FEATURE(FMAF) +FEATURE(VIS3) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 5efd09f4f4..59b922c903 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2188,6 +2188,8 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) # define avail_VIS1(C) ((C)->def->features & CPU_FEATURE_VIS1) # define avail_VIS2(C) ((C)->def->features & CPU_FEATURE_VIS2) +# define avail_VIS3(C) ((C)->def->features & CPU_FEATURE_VIS3) +# define avail_VIS3B(C) avail_VIS3(C) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) @@ -2201,6 +2203,8 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_HYPV(C) false # define avail_VIS1(C) false # define avail_VIS2(C) false +# define avail_VIS3(C) false +# define avail_VIS3B(C) false #endif /* Default case for non jump instructions. */ From 015fc6fcdb90034a7551091f8cd9a47ca1bd26b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 12:33:08 -0700 Subject: [PATCH 1210/2884] target/sparc: Implement ADDXC, ADDXCcc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 056fba98f9..5d1c55aa78 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -376,6 +376,9 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ ARRAY16 10 ..... 110110 ..... 0 0001 0010 ..... @r_r_r ARRAY32 10 ..... 110110 ..... 0 0001 0100 ..... @r_r_r + ADDXC 10 ..... 110110 ..... 0 0001 0001 ..... @r_r_r + ADDXCcc 10 ..... 110110 ..... 0 0001 0011 ..... @r_r_r + ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 59b922c903..ad12486758 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -433,6 +433,17 @@ static void gen_op_addccc(TCGv dst, TCGv src1, TCGv src2) gen_op_addcc_int(dst, src1, src2, gen_carry32()); } +static void gen_op_addxc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_add_tl(dst, src1, src2); + tcg_gen_add_tl(dst, dst, cpu_cc_C); +} + +static void gen_op_addxccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addcc_int(dst, src1, src2, cpu_cc_C); +} + static void gen_op_subcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) { TCGv z = tcg_constant_tl(0); @@ -3692,6 +3703,9 @@ TRANS(ARRAY8, VIS1, do_rrr, a, gen_helper_array8) TRANS(ARRAY16, VIS1, do_rrr, a, gen_op_array16) TRANS(ARRAY32, VIS1, do_rrr, a, gen_op_array32) +TRANS(ADDXC, VIS3, do_rrr, a, gen_op_addxc) +TRANS(ADDXCcc, VIS3, do_rrr, a, gen_op_addxccc) + static void gen_op_alignaddr(TCGv dst, TCGv s1, TCGv s2) { #ifdef TARGET_SPARC64 From c973b4e8df6e4a7b0080e3a93742a4d56baeb84b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 12:55:49 -0700 Subject: [PATCH 1211/2884] target/sparc: Implement CMASK instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 3 +++ target/sparc/insns.decode | 4 ++++ target/sparc/translate.c | 13 +++++++++++++ target/sparc/vis_helper.c | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index f4d3311ac4..84435b0932 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -107,6 +107,9 @@ DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmask8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmask16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmask32, TCG_CALL_NO_RWG_SE, i64, i64, i64) #define VIS_CMPHELPER(name) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) \ diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 5d1c55aa78..8be808d065 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -384,6 +384,10 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r + CMASK8 10 00000 110110 00000 0 0001 1011 rs2:5 + CMASK16 10 00000 110110 00000 0 0001 1101 rs2:5 + CMASK32 10 00000 110110 00000 0 0001 1111 rs2:5 + FPCMPLE16 10 ..... 110110 ..... 0 0010 0000 ..... @r_d_d FPCMPNE16 10 ..... 110110 ..... 0 0010 0010 ..... @r_d_d FPCMPGT16 10 ..... 110110 ..... 0 0010 1000 ..... @r_d_d diff --git a/target/sparc/translate.c b/target/sparc/translate.c index ad12486758..2b0a1f5a9a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -61,6 +61,9 @@ # define gen_helper_write_softint(E, S) qemu_build_not_reached() # define gen_helper_wrpil(E, S) qemu_build_not_reached() # define gen_helper_wrpstate(E, S) qemu_build_not_reached() +# define gen_helper_cmask8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_cmask16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_cmask32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpeq16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpeq32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpgt16 ({ qemu_build_not_reached(); NULL; }) @@ -3748,6 +3751,16 @@ static void gen_op_bmask(TCGv dst, TCGv s1, TCGv s2) TRANS(BMASK, VIS2, do_rrr, a, gen_op_bmask) +static bool do_cmask(DisasContext *dc, int rs2, void (*func)(TCGv, TCGv, TCGv)) +{ + func(cpu_gsr, cpu_gsr, gen_load_gpr(dc, rs2)); + return true; +} + +TRANS(CMASK8, VIS3, do_cmask, a->rs2, gen_helper_cmask8) +TRANS(CMASK16, VIS3, do_cmask, a->rs2, gen_helper_cmask16) +TRANS(CMASK32, VIS3, do_cmask, a->rs2, gen_helper_cmask32) + static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) { TCGv dst, src1, src2; diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 41312deda4..20baa4ff71 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -351,3 +351,41 @@ uint64_t helper_bshuffle(uint64_t gsr, uint64_t src1, uint64_t src2) return r.ll; } + +uint64_t helper_cmask8(uint64_t gsr, uint64_t src) +{ + uint32_t mask = 0; + + mask |= (src & 0x01 ? 0x00000007 : 0x0000000f); + mask |= (src & 0x02 ? 0x00000060 : 0x000000e0); + mask |= (src & 0x04 ? 0x00000500 : 0x00000d00); + mask |= (src & 0x08 ? 0x00004000 : 0x0000c000); + mask |= (src & 0x10 ? 0x00030000 : 0x000b0000); + mask |= (src & 0x20 ? 0x00200000 : 0x00a00000); + mask |= (src & 0x40 ? 0x01000000 : 0x09000000); + mask |= (src & 0x80 ? 0x00000000 : 0x80000000); + + return deposit64(gsr, 32, 32, mask); +} + +uint64_t helper_cmask16(uint64_t gsr, uint64_t src) +{ + uint32_t mask = 0; + + mask |= (src & 0x1 ? 0x00000067 : 0x000000ef); + mask |= (src & 0x2 ? 0x00004500 : 0x0000cd00); + mask |= (src & 0x4 ? 0x00230000 : 0x00ab0000); + mask |= (src & 0x8 ? 0x01000000 : 0x89000000); + + return deposit64(gsr, 32, 32, mask); +} + +uint64_t helper_cmask32(uint64_t gsr, uint64_t src) +{ + uint32_t mask = 0; + + mask |= (src & 0x1 ? 0x00004567 : 0x0000cdef); + mask |= (src & 0x2 ? 0x01230000 : 0x89ab0000); + + return deposit64(gsr, 32, 32, mask); +} From 7837185e40dc8b5e97e33e3fbdf94be0b55ef585 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 14:57:27 -0700 Subject: [PATCH 1212/2884] target/sparc: Implement FCHKSM16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 1 + target/sparc/insns.decode | 1 + target/sparc/translate.c | 32 ++++++++++++++++++++++++++++++++ target/sparc/vis_helper.c | 23 +++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 84435b0932..e59307efc2 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -110,6 +110,7 @@ DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) DEF_HELPER_FLAGS_2(cmask8, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmask16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmask32, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fchksm16, TCG_CALL_NO_RWG_SE, i64, i64, i64) #define VIS_CMPHELPER(name) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) \ diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 8be808d065..18d068d2af 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -410,6 +410,7 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ PDIST 10 ..... 110110 ..... 0 0011 1110 ..... \ &r_r_r_r rd=%dfp_rd rs1=%dfp_rd rs2=%dfp_rs1 rs3=%dfp_rs2 + FCHKSM16 10 ..... 110110 ..... 0 0100 0100 ..... @d_d_d FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @d_d_d FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @d_d_d diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2b0a1f5a9a..b8ba8eea74 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -788,6 +788,37 @@ static void gen_op_fmuld8sux16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) tcg_gen_concat_i32_i64(dst, t0, t1); } +#ifdef TARGET_SPARC64 +static void gen_vec_fchksm16(unsigned vece, TCGv_vec dst, + TCGv_vec src1, TCGv_vec src2) +{ + TCGv_vec a = tcg_temp_new_vec_matching(dst); + TCGv_vec c = tcg_temp_new_vec_matching(dst); + + tcg_gen_add_vec(vece, a, src1, src2); + tcg_gen_cmp_vec(TCG_COND_LTU, vece, c, a, src1); + /* Vector cmp produces -1 for true, so subtract to add carry. */ + tcg_gen_sub_vec(vece, dst, a, c); +} + +static void gen_op_fchksm16(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, INDEX_op_add_vec, INDEX_op_sub_vec, + }; + static const GVecGen3 op = { + .fni8 = gen_helper_fchksm16, + .fniv = gen_vec_fchksm16, + .opt_opc = vecop_list, + .vece = MO_16, + }; + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &op); +} +#else +#define gen_op_fchksm16 ({ qemu_build_not_reached(); NULL; }) +#endif + static void finishing_insn(DisasContext *dc) { /* @@ -4761,6 +4792,7 @@ TRANS(FPADD16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_add) TRANS(FPADD32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_add) TRANS(FPSUB16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sub) TRANS(FPSUB32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sub) +TRANS(FCHKSM16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fchksm16) static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 20baa4ff71..fa607375d2 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -389,3 +389,26 @@ uint64_t helper_cmask32(uint64_t gsr, uint64_t src) return deposit64(gsr, 32, 32, mask); } + +static inline uint16_t do_fchksm16(uint16_t src1, uint16_t src2) +{ + uint16_t a = src1 + src2; + uint16_t c = a < src1; + return a + c; +} + +uint64_t helper_fchksm16(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + r.VIS_W64(0) = do_fchksm16(s1.VIS_W64(0), s2.VIS_W64(0)); + r.VIS_W64(1) = do_fchksm16(s1.VIS_W64(1), s2.VIS_W64(1)); + r.VIS_W64(2) = do_fchksm16(s1.VIS_W64(2), s2.VIS_W64(2)); + r.VIS_W64(3) = do_fchksm16(s1.VIS_W64(3), s2.VIS_W64(3)); + + return r.ll; +} From 3d50b7287e3c11b769945fd7352788d69b1a5a5e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 14:59:24 -0700 Subject: [PATCH 1213/2884] target/sparc: Implement FHADD, FHSUB, FNHADD, FNADD, FNMUL Signed-off-by: Richard Henderson --- target/sparc/fop_helper.c | 68 +++++++++++++++++++++++++++++++++++ target/sparc/helper.h | 5 +++ target/sparc/insns.decode | 11 ++++++ target/sparc/translate.c | 76 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 1de44d79c1..08b5f96f95 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -359,6 +359,74 @@ float64 helper_fmaddd(CPUSPARCState *env, float64 s1, return ret; } +float32 helper_fnadds(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_add(src1, src2, &env->fp_status); + + /* + * NaN inputs or result do not get a sign change. + * Nor, apparently, does zero: on hardware, -(x + -x) yields +0. + */ + if (!float32_is_any_nan(ret) && !float32_is_zero(ret)) { + ret = float32_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float32 helper_fnmuls(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_mul(src1, src2, &env->fp_status); + + /* NaN inputs or result do not get a sign change. */ + if (!float32_is_any_nan(ret)) { + ret = float32_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fnaddd(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_add(src1, src2, &env->fp_status); + + /* + * NaN inputs or result do not get a sign change. + * Nor, apparently, does zero: on hardware, -(x + -x) yields +0. + */ + if (!float64_is_any_nan(ret) && !float64_is_zero(ret)) { + ret = float64_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fnmuld(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_mul(src1, src2, &env->fp_status); + + /* NaN inputs or result do not get a sign change. */ + if (!float64_is_any_nan(ret)) { + ret = float64_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fnsmuld(CPUSPARCState *env, float32 src1, float32 src2) +{ + float64 ret = float64_mul(float32_to_float64(src1, &env->fp_status), + float32_to_float64(src2, &env->fp_status), + &env->fp_status); + + /* NaN inputs or result do not get a sign change. */ + if (!float64_is_any_nan(ret)) { + ret = float64_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + static uint32_t finish_fcmp(CPUSPARCState *env, FloatRelation r, uintptr_t ra) { check_ieee_exceptions(env, ra); diff --git a/target/sparc/helper.h b/target/sparc/helper.h index e59307efc2..15f0907a1b 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -57,6 +57,8 @@ DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_5(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, i32) +DEF_HELPER_FLAGS_3(fnaddd, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fnmuld, TCG_CALL_NO_WG, f64, env, f64, f64) DEF_HELPER_FLAGS_3(faddq, TCG_CALL_NO_WG, i128, env, i128, i128) DEF_HELPER_FLAGS_3(fsubq, TCG_CALL_NO_WG, i128, env, i128, i128) @@ -68,8 +70,11 @@ DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_5(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, i32) +DEF_HELPER_FLAGS_3(fnadds, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fnmuls, TCG_CALL_NO_WG, f32, env, f32, f32) DEF_HELPER_FLAGS_3(fsmuld, TCG_CALL_NO_WG, f64, env, f32, f32) +DEF_HELPER_FLAGS_3(fnsmuld, TCG_CALL_NO_WG, f64, env, f32, f32) DEF_HELPER_FLAGS_3(fdmulq, TCG_CALL_NO_WG, i128, env, f64, f64) DEF_HELPER_FLAGS_2(fitod, TCG_CALL_NO_WG, f64, env, s32) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 18d068d2af..6ec3838865 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -307,8 +307,19 @@ FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @q_q_q FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @d_d_d FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @q_q_q +FNADDs 10 ..... 110100 ..... 0 0101 0001 ..... @r_r_r +FNADDd 10 ..... 110100 ..... 0 0101 0010 ..... @d_d_d +FNMULs 10 ..... 110100 ..... 0 0101 1001 ..... @r_r_r +FNMULd 10 ..... 110100 ..... 0 0101 1010 ..... @d_d_d +FHADDs 10 ..... 110100 ..... 0 0110 0001 ..... @r_r_r +FHADDd 10 ..... 110100 ..... 0 0110 0010 ..... @d_d_d +FHSUBs 10 ..... 110100 ..... 0 0110 0101 ..... @r_r_r +FHSUBd 10 ..... 110100 ..... 0 0110 0110 ..... @d_d_d FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @d_r_r FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @q_d_d +FNHADDs 10 ..... 110100 ..... 0 0111 0001 ..... @r_r_r +FNHADDd 10 ..... 110100 ..... 0 0111 0010 ..... @d_d_d +FNsMULd 10 ..... 110100 ..... 0 0111 1001 ..... @d_r_r FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_d2 FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_q2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b8ba8eea74..d81b9ce5a8 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1234,6 +1234,51 @@ static void gen_op_fnmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); } +/* Use muladd to compute (1 * src1) + src2 / 2 with one rounding. */ +static void gen_op_fhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) +{ + TCGv_i32 one = tcg_constant_i32(float32_one); + int op = float_muladd_halve_result; + gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 one = tcg_constant_i64(float64_one); + int op = float_muladd_halve_result; + gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +/* Use muladd to compute (1 * src1) - src2 / 2 with one rounding. */ +static void gen_op_fhsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) +{ + TCGv_i32 one = tcg_constant_i32(float32_one); + int op = float_muladd_negate_c | float_muladd_halve_result; + gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fhsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 one = tcg_constant_i64(float64_one); + int op = float_muladd_negate_c | float_muladd_halve_result; + gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +/* Use muladd to compute -((1 * src1) + src2 / 2) with one rounding. */ +static void gen_op_fnhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) +{ + TCGv_i32 one = tcg_constant_i32(float32_one); + int op = float_muladd_negate_result | float_muladd_halve_result; + gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fnhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 one = tcg_constant_i64(float64_one); + int op = float_muladd_negate_result | float_muladd_halve_result; + gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + static void gen_op_fpexception_im(DisasContext *dc, int ftt) { /* @@ -4710,6 +4755,10 @@ TRANS(FXNORs, VIS1, do_fff, a, tcg_gen_eqv_i32) TRANS(FORNOTs, VIS1, do_fff, a, tcg_gen_orc_i32) TRANS(FORs, VIS1, do_fff, a, tcg_gen_or_i32) +TRANS(FHADDs, VIS3, do_fff, a, gen_op_fhadds) +TRANS(FHSUBs, VIS3, do_fff, a, gen_op_fhsubs) +TRANS(FNHADDs, VIS3, do_fff, a, gen_op_fnhadds) + static bool do_env_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) { @@ -4730,6 +4779,8 @@ TRANS(FADDs, ALL, do_env_fff, a, gen_helper_fadds) TRANS(FSUBs, ALL, do_env_fff, a, gen_helper_fsubs) TRANS(FMULs, ALL, do_env_fff, a, gen_helper_fmuls) TRANS(FDIVs, ALL, do_env_fff, a, gen_helper_fdivs) +TRANS(FNADDs, VIS3, do_env_fff, a, gen_helper_fnadds) +TRANS(FNMULs, VIS3, do_env_fff, a, gen_helper_fnmuls) static bool do_dff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i32, TCGv_i32)) @@ -4827,6 +4878,10 @@ TRANS(FPACK32, VIS1, do_ddd, a, gen_op_fpack32) TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata) TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) +TRANS(FHADDd, VIS3, do_ddd, a, gen_op_fhaddd) +TRANS(FHSUBd, VIS3, do_ddd, a, gen_op_fhsubd) +TRANS(FNHADDd, VIS3, do_ddd, a, gen_op_fnhaddd) + static bool do_rdd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv, TCGv_i64, TCGv_i64)) { @@ -4876,6 +4931,8 @@ TRANS(FADDd, ALL, do_env_ddd, a, gen_helper_faddd) TRANS(FSUBd, ALL, do_env_ddd, a, gen_helper_fsubd) TRANS(FMULd, ALL, do_env_ddd, a, gen_helper_fmuld) TRANS(FDIVd, ALL, do_env_ddd, a, gen_helper_fdivd) +TRANS(FNADDd, VIS3, do_env_ddd, a, gen_helper_fnaddd) +TRANS(FNMULd, VIS3, do_env_ddd, a, gen_helper_fnmuld) static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) { @@ -4897,6 +4954,25 @@ static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) return advance_pc(dc); } +static bool trans_FNsMULd(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (!avail_VIS3(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + gen_helper_fnsmuld(dst, tcg_env, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + static bool do_ffff(DisasContext *dc, arg_r_r_r_r *a, void (*func)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32)) { From 1d3ed3d728f81dee4ae87028a8a3e9beb4fa4a17 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 15:23:35 -0700 Subject: [PATCH 1214/2884] target/sparc: Implement FLCMP Signed-off-by: Richard Henderson --- target/sparc/fop_helper.c | 46 +++++++++++++++++++++++++++++++++++++++ target/sparc/helper.h | 2 ++ target/sparc/insns.decode | 4 ++++ target/sparc/translate.c | 34 +++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 08b5f96f95..1b524c6d3c 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -490,6 +490,52 @@ uint32_t helper_fcmpeq(CPUSPARCState *env, Int128 src1, Int128 src2) return finish_fcmp(env, r, GETPC()); } +uint32_t helper_flcmps(float32 src1, float32 src2) +{ + /* + * FLCMP never raises an exception nor modifies any FSR fields. + * Perform the comparison with a dummy fp environment. + */ + float_status discard = { }; + FloatRelation r = float32_compare_quiet(src1, src2, &discard); + + switch (r) { + case float_relation_equal: + if (src2 == float32_zero && src1 != float32_zero) { + return 1; /* -0.0 < +0.0 */ + } + return 0; + case float_relation_less: + return 1; + case float_relation_greater: + return 0; + case float_relation_unordered: + return float32_is_any_nan(src2) ? 3 : 2; + } + g_assert_not_reached(); +} + +uint32_t helper_flcmpd(float64 src1, float64 src2) +{ + float_status discard = { }; + FloatRelation r = float64_compare_quiet(src1, src2, &discard); + + switch (r) { + case float_relation_equal: + if (src2 == float64_zero && src1 != float64_zero) { + return 1; /* -0.0 < +0.0 */ + } + return 0; + case float_relation_less: + return 1; + case float_relation_greater: + return 0; + case float_relation_unordered: + return float64_is_any_nan(src2) ? 3 : 2; + } + g_assert_not_reached(); +} + target_ulong cpu_get_fsr(CPUSPARCState *env) { target_ulong fsr = env->fsr | env->fsr_cexc_ftt; diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 15f0907a1b..ab79954bb5 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -50,6 +50,8 @@ DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, i32, env, f64, f64) DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, i32, env, f64, f64) DEF_HELPER_FLAGS_3(fcmpq, TCG_CALL_NO_WG, i32, env, i128, i128) DEF_HELPER_FLAGS_3(fcmpeq, TCG_CALL_NO_WG, i32, env, i128, i128) +DEF_HELPER_FLAGS_2(flcmps, TCG_CALL_NO_RWG_SE, i32, f32, f32) +DEF_HELPER_FLAGS_2(flcmpd, TCG_CALL_NO_RWG_SE, i32, f64, f64) DEF_HELPER_2(raise_exception, noreturn, env, int) DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 6ec3838865..de29996304 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -470,6 +470,10 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FZEROs 10 rd:5 110110 00000 0 0110 0001 00000 FONEd 10 ..... 110110 00000 0 0111 1110 00000 rd=%dfp_rd FONEs 10 rd:5 110110 00000 0 0111 1111 00000 + + FLCMPs 10 000 cc:2 110110 rs1:5 1 0101 0001 rs2:5 + FLCMPd 10 000 cc:2 110110 ..... 1 0101 0010 ..... \ + rs1=%dfp_rs1 rs2=%dfp_rs2 ] NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d81b9ce5a8..db3a153c6e 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5207,6 +5207,40 @@ static bool do_fcmpq(DisasContext *dc, arg_FCMPq *a, bool e) TRANS(FCMPq, ALL, do_fcmpq, a, false) TRANS(FCMPEq, ALL, do_fcmpq, a, true) +static bool trans_FLCMPs(DisasContext *dc, arg_FLCMPs *a) +{ + TCGv_i32 src1, src2; + + if (!avail_VIS3(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + gen_helper_flcmps(cpu_fcc[a->cc], src1, src2); + return advance_pc(dc); +} + +static bool trans_FLCMPd(DisasContext *dc, arg_FLCMPd *a) +{ + TCGv_i64 src1, src2; + + if (!avail_VIS3(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + gen_helper_flcmpd(cpu_fcc[a->cc], src1, src2); + return advance_pc(dc); +} + static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); From d6ff1ccb45f9b228e20f74f6e6c801c88a2885b2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 15:40:39 -0700 Subject: [PATCH 1215/2884] target/sparc: Implement FMEAN16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 1 + target/sparc/insns.decode | 1 + target/sparc/translate.c | 30 ++++++++++++++++++++++++++++++ target/sparc/vis_helper.c | 21 +++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index ab79954bb5..f1b84dc9b3 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -118,6 +118,7 @@ DEF_HELPER_FLAGS_2(cmask8, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmask16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmask32, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fchksm16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmean16, TCG_CALL_NO_RWG_SE, i64, i64, i64) #define VIS_CMPHELPER(name) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) \ diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index de29996304..febd1a4a13 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -421,6 +421,7 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ PDIST 10 ..... 110110 ..... 0 0011 1110 ..... \ &r_r_r_r rd=%dfp_rd rs1=%dfp_rd rs2=%dfp_rs1 rs3=%dfp_rs2 + FMEAN16 10 ..... 110110 ..... 0 0100 0000 ..... @d_d_d FCHKSM16 10 ..... 110110 ..... 0 0100 0100 ..... @d_d_d FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @d_d_d FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index db3a153c6e..c3956f489b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -815,8 +815,37 @@ static void gen_op_fchksm16(unsigned vece, uint32_t dofs, uint32_t aofs, }; tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &op); } + +static void gen_vec_fmean16(unsigned vece, TCGv_vec dst, + TCGv_vec src1, TCGv_vec src2) +{ + TCGv_vec t = tcg_temp_new_vec_matching(dst); + + tcg_gen_or_vec(vece, t, src1, src2); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(dst, vece, 1)); + tcg_gen_sari_vec(vece, src1, src1, 1); + tcg_gen_sari_vec(vece, src2, src2, 1); + tcg_gen_add_vec(vece, dst, src1, src2); + tcg_gen_add_vec(vece, dst, dst, t); +} + +static void gen_op_fmean16(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_add_vec, INDEX_op_sari_vec, + }; + static const GVecGen3 op = { + .fni8 = gen_helper_fmean16, + .fniv = gen_vec_fmean16, + .opt_opc = vecop_list, + .vece = MO_16, + }; + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &op); +} #else #define gen_op_fchksm16 ({ qemu_build_not_reached(); NULL; }) +#define gen_op_fmean16 ({ qemu_build_not_reached(); NULL; }) #endif static void finishing_insn(DisasContext *dc) @@ -4844,6 +4873,7 @@ TRANS(FPADD32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_add) TRANS(FPSUB16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sub) TRANS(FPSUB32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sub) TRANS(FCHKSM16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fchksm16) +TRANS(FMEAN16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fmean16) static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index fa607375d2..6ef36755c3 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -412,3 +412,24 @@ uint64_t helper_fchksm16(uint64_t src1, uint64_t src2) return r.ll; } + +static inline int16_t do_fmean16(int16_t src1, int16_t src2) +{ + return (src1 + src2 + 1) / 2; +} + +uint64_t helper_fmean16(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + r.VIS_SW64(0) = do_fmean16(s1.VIS_SW64(0), s2.VIS_SW64(0)); + r.VIS_SW64(1) = do_fmean16(s1.VIS_SW64(1), s2.VIS_SW64(1)); + r.VIS_SW64(2) = do_fmean16(s1.VIS_SW64(2), s2.VIS_SW64(2)); + r.VIS_SW64(3) = do_fmean16(s1.VIS_SW64(3), s2.VIS_SW64(3)); + + return r.ll; +} From bc3f14a9ed777ddab8c8915a9804f9b3ca90839d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 16:39:12 -0700 Subject: [PATCH 1216/2884] target/sparc: Implement FPADD64, FPSUB64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index febd1a4a13..70ca41a69a 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -441,10 +441,12 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPADD16s 10 ..... 110110 ..... 0 0101 0001 ..... @r_r_r FPADD32 10 ..... 110110 ..... 0 0101 0010 ..... @d_d_d FPADD32s 10 ..... 110110 ..... 0 0101 0011 ..... @r_r_r + FPADD64 10 ..... 110110 ..... 0 0100 0010 ..... @d_d_d FPSUB16 10 ..... 110110 ..... 0 0101 0100 ..... @d_d_d FPSUB16s 10 ..... 110110 ..... 0 0101 0101 ..... @r_r_r FPSUB32 10 ..... 110110 ..... 0 0101 0110 ..... @d_d_d FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r + FPSUB64 10 ..... 110110 ..... 0 0100 0110 ..... @d_d_d FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @d_d_d FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index c3956f489b..48cab59c07 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4912,6 +4912,9 @@ TRANS(FHADDd, VIS3, do_ddd, a, gen_op_fhaddd) TRANS(FHSUBd, VIS3, do_ddd, a, gen_op_fhsubd) TRANS(FNHADDd, VIS3, do_ddd, a, gen_op_fnhaddd) +TRANS(FPADD64, VIS3B, do_ddd, a, tcg_gen_add_i64) +TRANS(FPSUB64, VIS3B, do_ddd, a, tcg_gen_sub_i64) + static bool do_rdd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv, TCGv_i64, TCGv_i64)) { From 0d1d3aaf6405f9ecf67af886c06f1f710b046563 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 20:54:48 -0700 Subject: [PATCH 1217/2884] target/sparc: Implement FPADDS, FPSUBS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 9 +++++ target/sparc/translate.c | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 70ca41a69a..b6553362eb 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -448,6 +448,15 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r FPSUB64 10 ..... 110110 ..... 0 0100 0110 ..... @d_d_d + FPADDS16 10 ..... 110110 ..... 0 0101 1000 ..... @d_d_d + FPADDS16s 10 ..... 110110 ..... 0 0101 1001 ..... @r_r_r + FPADDS32 10 ..... 110110 ..... 0 0101 1010 ..... @d_d_d + FPADDS32s 10 ..... 110110 ..... 0 0101 1011 ..... @r_r_r + FPSUBS16 10 ..... 110110 ..... 0 0101 1100 ..... @d_d_d + FPSUBS16s 10 ..... 110110 ..... 0 0101 1101 ..... @r_r_r + FPSUBS32 10 ..... 110110 ..... 0 0101 1110 ..... @d_d_d + FPSUBS32s 10 ..... 110110 ..... 0 0101 1111 ..... @r_r_r + FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @d_d_d FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r FANDNOTd 10 ..... 110110 ..... 0 0110 0100 ..... @d_d_d # FANDNOT2d diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 48cab59c07..7a5e8e0a9a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -698,6 +698,78 @@ static void gen_op_fpack32(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) #endif } +static void gen_op_fpadds16s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) +{ + TCGv_i32 t[2]; + + for (int i = 0; i < 2; i++) { + TCGv_i32 u = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_sextract_i32(u, src1, i * 16, 16); + tcg_gen_sextract_i32(v, src2, i * 16, 16); + tcg_gen_add_i32(u, u, v); + tcg_gen_smax_i32(u, u, tcg_constant_i32(INT16_MIN)); + tcg_gen_smin_i32(u, u, tcg_constant_i32(INT16_MAX)); + t[i] = u; + } + tcg_gen_deposit_i32(d, t[0], t[1], 16, 16); +} + +static void gen_op_fpsubs16s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) +{ + TCGv_i32 t[2]; + + for (int i = 0; i < 2; i++) { + TCGv_i32 u = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_sextract_i32(u, src1, i * 16, 16); + tcg_gen_sextract_i32(v, src2, i * 16, 16); + tcg_gen_sub_i32(u, u, v); + tcg_gen_smax_i32(u, u, tcg_constant_i32(INT16_MIN)); + tcg_gen_smin_i32(u, u, tcg_constant_i32(INT16_MAX)); + t[i] = u; + } + tcg_gen_deposit_i32(d, t[0], t[1], 16, 16); +} + +static void gen_op_fpadds32s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) +{ + TCGv_i32 r = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_add_i32(r, src1, src2); + tcg_gen_xor_i32(t, src1, src2); + tcg_gen_xor_i32(v, r, src2); + tcg_gen_andc_i32(v, v, t); + + tcg_gen_setcond_i32(TCG_COND_GE, t, r, z); + tcg_gen_addi_i32(t, t, INT32_MAX); + + tcg_gen_movcond_i32(TCG_COND_LT, d, v, z, t, r); +} + +static void gen_op_fpsubs32s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) +{ + TCGv_i32 r = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_sub_i32(r, src1, src2); + tcg_gen_xor_i32(t, src1, src2); + tcg_gen_xor_i32(v, r, src1); + tcg_gen_and_i32(v, v, t); + + tcg_gen_setcond_i32(TCG_COND_GE, t, r, z); + tcg_gen_addi_i32(t, t, INT32_MAX); + + tcg_gen_movcond_i32(TCG_COND_LT, d, v, z, t, r); +} + static void gen_op_faligndata(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) { #ifdef TARGET_SPARC64 @@ -4788,6 +4860,11 @@ TRANS(FHADDs, VIS3, do_fff, a, gen_op_fhadds) TRANS(FHSUBs, VIS3, do_fff, a, gen_op_fhsubs) TRANS(FNHADDs, VIS3, do_fff, a, gen_op_fnhadds) +TRANS(FPADDS16s, VIS3, do_fff, a, gen_op_fpadds16s) +TRANS(FPSUBS16s, VIS3, do_fff, a, gen_op_fpsubs16s) +TRANS(FPADDS32s, VIS3, do_fff, a, gen_op_fpadds32s) +TRANS(FPSUBS32s, VIS3, do_fff, a, gen_op_fpsubs32s) + static bool do_env_fff(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) { @@ -4875,6 +4952,11 @@ TRANS(FPSUB32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sub) TRANS(FCHKSM16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fchksm16) TRANS(FMEAN16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fmean16) +TRANS(FPADDS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_ssadd) +TRANS(FPADDS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_ssadd) +TRANS(FPSUBS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sssub) +TRANS(FPSUBS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sssub) + static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) { From 669e077437d5782682e2ac4f1082fcbf102b5680 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 16:53:28 -0700 Subject: [PATCH 1218/2884] target/sparc: Implement FPCMPEQ8, FPCMPNE8, FPCMPULE8, FPCMPUGT8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 4 ++++ target/sparc/insns.decode | 5 +++++ target/sparc/translate.c | 9 +++++++++ target/sparc/vis_helper.c | 40 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index f1b84dc9b3..ed295c01e0 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -128,6 +128,10 @@ VIS_CMPHELPER(cmpgt) VIS_CMPHELPER(cmpeq) VIS_CMPHELPER(cmple) VIS_CMPHELPER(cmpne) +DEF_HELPER_FLAGS_2(fcmpeq8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fcmpne8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fcmpule8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fcmpugt8, TCG_CALL_NO_RWG_SE, i64, i64, i64) #endif #undef VIS_HELPER #undef VIS_CMPHELPER diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index b6553362eb..295fc36128 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -408,6 +408,11 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_d_d FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_d_d + FPCMPULE8 10 ..... 110110 ..... 1 0010 0000 ..... @r_d_d + FPCMPUGT8 10 ..... 110110 ..... 1 0010 1000 ..... @r_d_d + FPCMPNE8 10 ..... 110110 ..... 1 0010 0010 ..... @r_d_d + FPCMPEQ8 10 ..... 110110 ..... 1 0010 1010 ..... @r_d_d + FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @d_r_d FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @d_r_r FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @d_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 7a5e8e0a9a..1da40f6db2 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -64,14 +64,18 @@ # define gen_helper_cmask8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_cmask16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_cmask32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpeq16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpeq32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpgt16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpgt32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmple16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmple32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpne8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpne16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpne32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpule8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpugt8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fdtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) @@ -5025,6 +5029,11 @@ TRANS(FPCMPNE32, VIS1, do_rdd, a, gen_helper_fcmpne32) TRANS(FPCMPGT32, VIS1, do_rdd, a, gen_helper_fcmpgt32) TRANS(FPCMPEQ32, VIS1, do_rdd, a, gen_helper_fcmpeq32) +TRANS(FPCMPEQ8, VIS3B, do_rdd, a, gen_helper_fcmpeq8) +TRANS(FPCMPNE8, VIS3B, do_rdd, a, gen_helper_fcmpne8) +TRANS(FPCMPULE8, VIS3B, do_rdd, a, gen_helper_fcmpule8) +TRANS(FPCMPUGT8, VIS3B, do_rdd, a, gen_helper_fcmpugt8) + static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) { diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 6ef36755c3..5a5da17132 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -238,6 +238,46 @@ VIS_CMPHELPER(helper_fcmpeq, FCMPEQ) VIS_CMPHELPER(helper_fcmple, FCMPLE) VIS_CMPHELPER(helper_fcmpne, FCMPNE) +uint64_t helper_fcmpeq8(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + uint64_t m = 0x7f7f7f7f7f7f7f7fULL; + uint64_t c = ~(((a & m) + m) | a | m); + + /* a.......b.......c.......d.......e.......f.......g.......h....... */ + c |= c << 7; + /* ab......bc......cd......de......ef......fg......gh......h....... */ + c |= c << 14; + /* abcd....bcde....cdef....defg....efgh....fgh.....gh......h....... */ + c |= c << 28; + /* abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... */ + return c >> 56; +} + +uint64_t helper_fcmpne8(uint64_t src1, uint64_t src2) +{ + return helper_fcmpeq8(src1, src2) ^ 0xff; +} + +uint64_t helper_fcmpule8(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 8; ++i) { + r |= (s1.VIS_B64(i) <= s2.VIS_B64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpugt8(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule8(src1, src2) ^ 0xff; +} + uint64_t helper_pdist(uint64_t sum, uint64_t src1, uint64_t src2) { int i; From fbc5c8d4e8f10fdb780c450aa49b503e6d592cc6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 17:24:01 -0700 Subject: [PATCH 1219/2884] target/sparc: Implement FSLL, FSRL, FSRA, FSLAS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 2 ++ target/sparc/insns.decode | 9 +++++++++ target/sparc/translate.c | 11 +++++++++++ target/sparc/vis_helper.c | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index ed295c01e0..219f0e04c7 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -119,6 +119,8 @@ DEF_HELPER_FLAGS_2(cmask16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(cmask32, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fchksm16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmean16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fslas16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fslas32, TCG_CALL_NO_RWG_SE, i64, i64, i64) #define VIS_CMPHELPER(name) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) \ diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 295fc36128..a5eefebfbc 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -408,6 +408,15 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_d_d FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_d_d + FSLL16 10 ..... 110110 ..... 0 0010 0001 ..... @d_d_d + FSRL16 10 ..... 110110 ..... 0 0010 0011 ..... @d_d_d + FSLAS16 10 ..... 110110 ..... 0 0010 1001 ..... @d_d_d + FSRA16 10 ..... 110110 ..... 0 0010 1011 ..... @d_d_d + FSLL32 10 ..... 110110 ..... 0 0010 0101 ..... @d_d_d + FSRL32 10 ..... 110110 ..... 0 0010 0111 ..... @d_d_d + FSLAS32 10 ..... 110110 ..... 0 0010 1101 ..... @d_d_d + FSRA32 10 ..... 110110 ..... 0 0010 1111 ..... @d_d_d + FPCMPULE8 10 ..... 110110 ..... 1 0010 0000 ..... @r_d_d FPCMPUGT8 10 ..... 110110 ..... 1 0010 1000 ..... @r_d_d FPCMPNE8 10 ..... 110110 ..... 1 0010 0010 ..... @r_d_d diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 1da40f6db2..e144217347 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -83,6 +83,8 @@ # define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fqtox ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fslas16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fslas32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtoq ({ qemu_build_not_reached(); NULL; }) @@ -4961,6 +4963,13 @@ TRANS(FPADDS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_ssadd) TRANS(FPSUBS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sssub) TRANS(FPSUBS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sssub) +TRANS(FSLL16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_shlv) +TRANS(FSLL32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_shlv) +TRANS(FSRL16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_shrv) +TRANS(FSRL32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_shrv) +TRANS(FSRA16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sarv) +TRANS(FSRA32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sarv) + static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -5000,6 +5009,8 @@ TRANS(FNHADDd, VIS3, do_ddd, a, gen_op_fnhaddd) TRANS(FPADD64, VIS3B, do_ddd, a, tcg_gen_add_i64) TRANS(FPSUB64, VIS3B, do_ddd, a, tcg_gen_sub_i64) +TRANS(FSLAS16, VIS3, do_ddd, a, gen_helper_fslas16) +TRANS(FSLAS32, VIS3, do_ddd, a, gen_helper_fslas32) static bool do_rdd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv, TCGv_i64, TCGv_i64)) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 5a5da17132..c21522c533 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -473,3 +473,39 @@ uint64_t helper_fmean16(uint64_t src1, uint64_t src2) return r.ll; } + +uint64_t helper_fslas16(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + for (int i = 0; i < 4; ++i) { + int t = s1.VIS_SW64(i) << (s2.VIS_W64(i) % 16); + t = MIN(t, INT16_MAX); + t = MAX(t, INT16_MIN); + r.VIS_SW64(i) = t; + } + + return r.ll; +} + +uint64_t helper_fslas32(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + for (int i = 0; i < 2; ++i) { + int64_t t = (int64_t)(int32_t)s1.VIS_L64(i) << (s2.VIS_L64(i) % 32); + t = MIN(t, INT32_MAX); + t = MAX(t, INT32_MIN); + r.VIS_L64(i) = t; + } + + return r.ll; +} From 298c52f784ac0f6c09a4621609ac9008b7fa15f9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 17:38:34 -0700 Subject: [PATCH 1220/2884] target/sparc: Implement LDXEFSR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/fop_helper.c | 6 ++++++ target/sparc/helper.h | 1 + target/sparc/insns.decode | 1 + target/sparc/translate.c | 11 +++++++++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 1b524c6d3c..0b30665b51 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -602,3 +602,9 @@ void helper_set_fsr_nofcc_noftt(CPUSPARCState *env, uint32_t fsr) env->fsr_cexc_ftt |= fsr & FSR_CEXC_MASK; set_fsr_nonsplit(env, fsr); } + +void helper_set_fsr_nofcc(CPUSPARCState *env, uint32_t fsr) +{ + env->fsr_cexc_ftt = fsr & (FSR_CEXC_MASK | FSR_FTT_MASK); + set_fsr_nonsplit(env, fsr); +} diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 219f0e04c7..4ae97866af 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -40,6 +40,7 @@ DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) #endif DEF_HELPER_FLAGS_1(get_fsr, TCG_CALL_NO_WG_SE, tl, env) +DEF_HELPER_FLAGS_2(set_fsr_nofcc, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_2(set_fsr_nofcc_noftt, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_2(fsqrts, TCG_CALL_NO_WG, f32, env, f32) DEF_HELPER_FLAGS_2(fsqrtd, TCG_CALL_NO_WG, f64, env, f64) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a5eefebfbc..fec055910e 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -589,6 +589,7 @@ STX 11 ..... 011110 ..... . ............. @r_r_i_asi # STXA LDF 11 ..... 100000 ..... . ............. @r_r_ri_na LDFSR 11 00000 100001 ..... . ............. @n_r_ri LDXFSR 11 00001 100001 ..... . ............. @n_r_ri +LDXEFSR 11 00011 100001 ..... . ............. @n_r_ri LDQF 11 ..... 100010 ..... . ............. @q_r_ri_na LDDF 11 ..... 100011 ..... . ............. @d_r_ri_na diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e144217347..c40992df96 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4458,7 +4458,7 @@ static bool trans_LDFSR(DisasContext *dc, arg_r_r_ri *a) return advance_pc(dc); } -static bool trans_LDXFSR(DisasContext *dc, arg_r_r_ri *a) +static bool do_ldxfsr(DisasContext *dc, arg_r_r_ri *a, bool entire) { #ifdef TARGET_SPARC64 TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); @@ -4483,13 +4483,20 @@ static bool trans_LDXFSR(DisasContext *dc, arg_r_r_ri *a) tcg_gen_extract_i32(cpu_fcc[2], hi, FSR_FCC2_SHIFT - 32, 2); tcg_gen_extract_i32(cpu_fcc[3], hi, FSR_FCC3_SHIFT - 32, 2); - gen_helper_set_fsr_nofcc_noftt(tcg_env, lo); + if (entire) { + gen_helper_set_fsr_nofcc(tcg_env, lo); + } else { + gen_helper_set_fsr_nofcc_noftt(tcg_env, lo); + } return advance_pc(dc); #else return false; #endif } +TRANS(LDXFSR, 64, do_ldxfsr, a, false) +TRANS(LDXEFSR, VIS3B, do_ldxfsr, a, true) + static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) { TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); From 875ce3929aef9d0dcee67b506991ea84e505729b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 17:48:25 -0700 Subject: [PATCH 1221/2884] target/sparc: Implement LZCNT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index fec055910e..4766964893 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -389,6 +389,7 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ ADDXC 10 ..... 110110 ..... 0 0001 0001 ..... @r_r_r ADDXCcc 10 ..... 110110 ..... 0 0001 0011 ..... @r_r_r + LZCNT 10 ..... 110110 00000 0 0001 0111 ..... @r_r2 ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index c40992df96..a12cc9f796 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -658,6 +658,11 @@ static void gen_op_popc(TCGv dst, TCGv src1, TCGv src2) tcg_gen_ctpop_tl(dst, src2); } +static void gen_op_lzcnt(TCGv dst, TCGv src) +{ + tcg_gen_clzi_tl(dst, src, TARGET_LONG_BITS); +} + #ifndef TARGET_SPARC64 static void gen_helper_array8(TCGv dst, TCGv src1, TCGv src2) { @@ -3873,6 +3878,19 @@ TRANS(EDGE16LN, VIS2, gen_edge, a, 16, 0, 1) TRANS(EDGE32N, VIS2, gen_edge, a, 32, 0, 0) TRANS(EDGE32LN, VIS2, gen_edge, a, 32, 0, 1) +static bool do_rr(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv, TCGv)) +{ + TCGv dst = gen_dest_gpr(dc, a->rd); + TCGv src = gen_load_gpr(dc, a->rs); + + func(dst, src); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(LZCNT, VIS3, do_rr, a, gen_op_lzcnt) + static bool do_rrr(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv, TCGv, TCGv)) { From 09b157e6283d02e02ec9f47d8d4a2fd0cd8612ce Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 18:07:35 -0700 Subject: [PATCH 1222/2884] target/sparc: Implement MOVsTOw, MOVdTOx, MOVwTOs, MOVxTOd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 6 ++++++ target/sparc/translate.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 4766964893..e0e9248982 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -498,6 +498,12 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FONEd 10 ..... 110110 00000 0 0111 1110 00000 rd=%dfp_rd FONEs 10 rd:5 110110 00000 0 0111 1111 00000 + MOVsTOuw 10 ..... 110110 00000 1 0001 0001 ..... @r_r2 + MOVsTOsw 10 ..... 110110 00000 1 0001 0011 ..... @r_r2 + MOVwTOs 10 ..... 110110 00000 1 0001 1001 ..... @r_r2 + MOVdTOx 10 ..... 110110 00000 1 0001 0000 ..... @r_d2 + MOVxTOd 10 ..... 110110 00000 1 0001 1000 ..... @d_r2 + FLCMPs 10 000 cc:2 110110 rs1:5 1 0101 0001 rs2:5 FLCMPd 10 000 cc:2 110110 ..... 1 0101 0010 ..... \ rs1=%dfp_rs1 rs2=%dfp_rs2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index a12cc9f796..496d490cdd 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5401,6 +5401,42 @@ static bool trans_FLCMPd(DisasContext *dc, arg_FLCMPd *a) return advance_pc(dc); } +static bool do_movf2r(DisasContext *dc, arg_r_r *a, + int (*offset)(unsigned int), + void (*load)(TCGv, TCGv_ptr, tcg_target_long)) +{ + TCGv dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + dst = gen_dest_gpr(dc, a->rd); + load(dst, tcg_env, offset(a->rs)); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(MOVsTOsw, VIS3B, do_movf2r, a, gen_offset_fpr_F, tcg_gen_ld32s_tl) +TRANS(MOVsTOuw, VIS3B, do_movf2r, a, gen_offset_fpr_F, tcg_gen_ld32u_tl) +TRANS(MOVdTOx, VIS3B, do_movf2r, a, gen_offset_fpr_D, tcg_gen_ld_tl) + +static bool do_movr2f(DisasContext *dc, arg_r_r *a, + int (*offset)(unsigned int), + void (*store)(TCGv, TCGv_ptr, tcg_target_long)) +{ + TCGv src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + src = gen_load_gpr(dc, a->rs); + store(src, tcg_env, offset(a->rd)); + return advance_pc(dc); +} + +TRANS(MOVwTOs, VIS3B, do_movr2f, a, gen_offset_fpr_F, tcg_gen_st32_tl) +TRANS(MOVxTOd, VIS3B, do_movr2f, a, gen_offset_fpr_D, tcg_gen_st_tl) + static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); From 7d5ebd8ffe241c57d6857ae75b67de6c6af3429c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 18:16:44 -0700 Subject: [PATCH 1223/2884] target/sparc: Implement PDISTN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index e0e9248982..09c8adca37 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -435,6 +435,7 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_d2 PDIST 10 ..... 110110 ..... 0 0011 1110 ..... \ &r_r_r_r rd=%dfp_rd rs1=%dfp_rd rs2=%dfp_rs1 rs3=%dfp_rs2 + PDISTN 10 ..... 110110 ..... 0 0011 1111 ..... @r_d_d FMEAN16 10 ..... 110110 ..... 0 0100 0000 ..... @d_d_d FCHKSM16 10 ..... 110110 ..... 0 0100 0100 ..... @d_d_d diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 496d490cdd..2480eed1e7 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -817,6 +817,15 @@ static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) #endif } +static void gen_op_pdistn(TCGv dst, TCGv_i64 src1, TCGv_i64 src2) +{ +#ifdef TARGET_SPARC64 + gen_helper_pdist(dst, tcg_constant_i64(0), src1, src2); +#else + g_assert_not_reached(); +#endif +} + static void gen_op_fmul8x16al(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { tcg_gen_ext16s_i32(src2, src2); @@ -5070,6 +5079,8 @@ TRANS(FPCMPNE8, VIS3B, do_rdd, a, gen_helper_fcmpne8) TRANS(FPCMPULE8, VIS3B, do_rdd, a, gen_helper_fcmpule8) TRANS(FPCMPUGT8, VIS3B, do_rdd, a, gen_helper_fcmpugt8) +TRANS(PDISTN, VIS3, do_rdd, a, gen_op_pdistn) + static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) { From 680af1b4a523cd7f15690cad7afb069e6efbbec0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 18:22:42 -0700 Subject: [PATCH 1224/2884] target/sparc: Implement UMULXHI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 09c8adca37..508175eccd 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -389,6 +389,7 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ ADDXC 10 ..... 110110 ..... 0 0001 0001 ..... @r_r_r ADDXCcc 10 ..... 110110 ..... 0 0001 0011 ..... @r_r_r + UMULXHI 10 ..... 110110 ..... 0 0001 0110 ..... @r_r_r LZCNT 10 ..... 110110 00000 0 0001 0111 ..... @r_r2 ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2480eed1e7..2a38152f58 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -581,6 +581,12 @@ static void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) gen_op_multiply(dst, src1, src2, 1); } +static void gen_op_umulxhi(TCGv dst, TCGv src1, TCGv src2) +{ + TCGv discard = tcg_temp_new(); + tcg_gen_mulu2_tl(discard, dst, src1, src2); +} + static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) { #ifdef TARGET_SPARC64 @@ -3919,6 +3925,8 @@ TRANS(ARRAY32, VIS1, do_rrr, a, gen_op_array32) TRANS(ADDXC, VIS3, do_rrr, a, gen_op_addxc) TRANS(ADDXCcc, VIS3, do_rrr, a, gen_op_addxccc) +TRANS(UMULXHI, VIS3, do_rrr, a, gen_op_umulxhi) + static void gen_op_alignaddr(TCGv dst, TCGv s1, TCGv s2) { #ifdef TARGET_SPARC64 From 029b0283dfe64c38e48f9dd9aad0dc6d254c4ea4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 18:31:59 -0700 Subject: [PATCH 1225/2884] target/sparc: Implement XMULX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 2 ++ target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 4 ++++ target/sparc/vis_helper.c | 11 +++++++++++ 4 files changed, 19 insertions(+) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 4ae97866af..fe0d8bc593 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -135,6 +135,8 @@ DEF_HELPER_FLAGS_2(fcmpeq8, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fcmpne8, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fcmpule8, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fcmpugt8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(xmulx, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(xmulxhi, TCG_CALL_NO_RWG_SE, i64, i64, i64) #endif #undef VIS_HELPER #undef VIS_CMPHELPER diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 508175eccd..1d54de5367 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -391,6 +391,8 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ ADDXCcc 10 ..... 110110 ..... 0 0001 0011 ..... @r_r_r UMULXHI 10 ..... 110110 ..... 0 0001 0110 ..... @r_r_r LZCNT 10 ..... 110110 00000 0 0001 0111 ..... @r_r2 + XMULX 10 ..... 110110 ..... 1 0001 0101 ..... @r_r_r + XMULXHI 10 ..... 110110 ..... 1 0001 0110 ..... @r_r_r ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2a38152f58..971e7dae80 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -90,6 +90,8 @@ # define gen_helper_fxtoq ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) # define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_xmulx ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_xmulxhi ({ qemu_build_not_reached(); NULL; }) # define MAXTL_MASK 0 #endif @@ -5088,6 +5090,8 @@ TRANS(FPCMPULE8, VIS3B, do_rdd, a, gen_helper_fcmpule8) TRANS(FPCMPUGT8, VIS3B, do_rdd, a, gen_helper_fcmpugt8) TRANS(PDISTN, VIS3, do_rdd, a, gen_op_pdistn) +TRANS(XMULX, VIS3, do_rrr, a, gen_helper_xmulx) +TRANS(XMULXHI, VIS3, do_rrr, a, gen_helper_xmulxhi) static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index c21522c533..c927a054b8 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "crypto/clmul.h" target_ulong helper_array8(target_ulong rs1, target_ulong rs2) { @@ -509,3 +510,13 @@ uint64_t helper_fslas32(uint64_t src1, uint64_t src2) return r.ll; } + +uint64_t helper_xmulx(uint64_t src1, uint64_t src2) +{ + return int128_getlo(clmul_64(src1, src2)); +} + +uint64_t helper_xmulxhi(uint64_t src1, uint64_t src2) +{ + return int128_gethi(clmul_64(src1, src2)); +} From deadbb14ba7a9cbdabbd102f7bf470c0baf9f25a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 18:49:08 -0700 Subject: [PATCH 1226/2884] target/sparc: Enable VIS3 feature bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 1 + target/sparc/cpu.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6a1457346a..cb79580431 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1004,6 +1004,7 @@ static uint32_t get_elf_hwcap(void) r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; + r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; #endif return r; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index ed9238a69d..8ea977b49f 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -550,6 +550,7 @@ static const char * const feature_name[] = { [CPU_FEATURE_BIT_VIS1] = "vis1", [CPU_FEATURE_BIT_VIS2] = "vis2", [CPU_FEATURE_BIT_FMAF] = "fmaf", + [CPU_FEATURE_BIT_VIS3] = "vis3", #else [CPU_FEATURE_BIT_MUL] = "mul", [CPU_FEATURE_BIT_DIV] = "div", @@ -880,6 +881,8 @@ static Property sparc_cpu_properties[] = { CPU_FEATURE_BIT_VIS2, false), DEFINE_PROP_BIT("fmaf", SPARCCPU, env.def.features, CPU_FEATURE_BIT_FMAF, false), + DEFINE_PROP_BIT("vis3", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS3, false), #else DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, CPU_FEATURE_BIT_MUL, false), From 68a414e99d438ff5e3e598d140c8f81638a8ea9e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 19:40:36 -0700 Subject: [PATCH 1227/2884] target/sparc: Implement IMA extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/elfload.c | 1 + target/sparc/cpu-feature.h.inc | 1 + target/sparc/cpu.c | 3 +++ target/sparc/insns.decode | 3 +++ target/sparc/translate.c | 24 ++++++++++++++++++++++++ 5 files changed, 32 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index cb79580431..0d4dc1f6d1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1005,6 +1005,7 @@ static uint32_t get_elf_hwcap(void) r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; + r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; #endif return r; diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc index 3913fb4a54..e2e6de9144 100644 --- a/target/sparc/cpu-feature.h.inc +++ b/target/sparc/cpu-feature.h.inc @@ -14,3 +14,4 @@ FEATURE(POWERDOWN) FEATURE(CASA) FEATURE(FMAF) FEATURE(VIS3) +FEATURE(IMA) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 8ea977b49f..88da5254e8 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -551,6 +551,7 @@ static const char * const feature_name[] = { [CPU_FEATURE_BIT_VIS2] = "vis2", [CPU_FEATURE_BIT_FMAF] = "fmaf", [CPU_FEATURE_BIT_VIS3] = "vis3", + [CPU_FEATURE_BIT_IMA] = "ima", #else [CPU_FEATURE_BIT_MUL] = "mul", [CPU_FEATURE_BIT_DIV] = "div", @@ -883,6 +884,8 @@ static Property sparc_cpu_properties[] = { CPU_FEATURE_BIT_FMAF, false), DEFINE_PROP_BIT("vis3", SPARCCPU, env.def.features, CPU_FEATURE_BIT_VIS3, false), + DEFINE_PROP_BIT("ima", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_IMA, false), #else DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, CPU_FEATURE_BIT_MUL, false), diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 1d54de5367..5d85e124ed 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -525,6 +525,9 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FNMSUBd 10 ..... 110111 ..... ..... 1010 ..... @d_d_d_d FNMADDs 10 ..... 110111 ..... ..... 1101 ..... @r_r_r_r FNMADDd 10 ..... 110111 ..... ..... 1110 ..... @d_d_d_d + + FPMADDX 10 ..... 110111 ..... ..... 0000 ..... @d_d_d_d + FPMADDXHI 10 ..... 110111 ..... ..... 0100 ..... @d_d_d_d ] NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 971e7dae80..640406570d 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -589,6 +589,26 @@ static void gen_op_umulxhi(TCGv dst, TCGv src1, TCGv src2) tcg_gen_mulu2_tl(discard, dst, src1, src2); } +static void gen_op_fpmaddx(TCGv_i64 dst, TCGv_i64 src1, + TCGv_i64 src2, TCGv_i64 src3) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, src1, src2); + tcg_gen_add_i64(dst, src3, t); +} + +static void gen_op_fpmaddxhi(TCGv_i64 dst, TCGv_i64 src1, + TCGv_i64 src2, TCGv_i64 src3) +{ + TCGv_i64 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + + tcg_gen_mulu2_i64(l, h, src1, src2); + tcg_gen_add2_i64(l, dst, l, h, src3, z); +} + static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) { #ifdef TARGET_SPARC64 @@ -2405,6 +2425,7 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_FMAF(C) ((C)->def->features & CPU_FEATURE_FMAF) # define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) # define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) +# define avail_IMA(C) ((C)->def->features & CPU_FEATURE_IMA) # define avail_VIS1(C) ((C)->def->features & CPU_FEATURE_VIS1) # define avail_VIS2(C) ((C)->def->features & CPU_FEATURE_VIS2) # define avail_VIS3(C) ((C)->def->features & CPU_FEATURE_VIS3) @@ -2420,6 +2441,7 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_FMAF(C) false # define avail_GL(C) false # define avail_HYPV(C) false +# define avail_IMA(C) false # define avail_VIS1(C) false # define avail_VIS2(C) false # define avail_VIS3(C) false @@ -5202,6 +5224,8 @@ TRANS(FMADDd, FMAF, do_dddd, a, gen_op_fmaddd) TRANS(FMSUBd, FMAF, do_dddd, a, gen_op_fmsubd) TRANS(FNMSUBd, FMAF, do_dddd, a, gen_op_fnmsubd) TRANS(FNMADDd, FMAF, do_dddd, a, gen_op_fnmaddd) +TRANS(FPMADDX, IMA, do_dddd, a, gen_op_fpmaddx) +TRANS(FPMADDXHI, IMA, do_dddd, a, gen_op_fpmaddxhi) static bool do_env_qqq(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i128, TCGv_env, TCGv_i128, TCGv_i128)) From 90b1433da8d51ecf0ab36d4c61eec949ee2fffbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 19:44:28 -0700 Subject: [PATCH 1228/2884] target/sparc: Add feature bit for VIS4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu-feature.h.inc | 1 + target/sparc/translate.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc index e2e6de9144..be81005237 100644 --- a/target/sparc/cpu-feature.h.inc +++ b/target/sparc/cpu-feature.h.inc @@ -15,3 +15,4 @@ FEATURE(CASA) FEATURE(FMAF) FEATURE(VIS3) FEATURE(IMA) +FEATURE(VIS4) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 640406570d..6fa0bb6ff5 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -2430,6 +2430,7 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_VIS2(C) ((C)->def->features & CPU_FEATURE_VIS2) # define avail_VIS3(C) ((C)->def->features & CPU_FEATURE_VIS3) # define avail_VIS3B(C) avail_VIS3(C) +# define avail_VIS4(C) ((C)->def->features & CPU_FEATURE_VIS4) #else # define avail_32(C) true # define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) @@ -2446,6 +2447,7 @@ static int extract_qfpreg(DisasContext *dc, int x) # define avail_VIS2(C) false # define avail_VIS3(C) false # define avail_VIS3B(C) false +# define avail_VIS4(C) false #endif /* Default case for non jump instructions. */ From b2b48493362b9f77ca66fffb1464f7fc5a32c6e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 20:00:10 -0700 Subject: [PATCH 1229/2884] target/sparc: Implement FALIGNDATAi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 5d85e124ed..0913fe7a86 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -446,6 +446,7 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @d_d_d FEXPAND 10 ..... 110110 00000 0 0100 1101 ..... @d_r2 + FALIGNDATAi 10 ..... 110110 ..... 0 0100 1001 ..... @d_r_d FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @d_d1 # FSRC1d FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6fa0bb6ff5..f51ce2ff2c 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -809,7 +809,8 @@ static void gen_op_fpsubs32s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) tcg_gen_movcond_i32(TCG_COND_LT, d, v, z, t, r); } -static void gen_op_faligndata(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) +static void gen_op_faligndata_i(TCGv_i64 dst, TCGv_i64 s1, + TCGv_i64 s2, TCGv gsr) { #ifdef TARGET_SPARC64 TCGv t1, t2, shift; @@ -818,7 +819,7 @@ static void gen_op_faligndata(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) t2 = tcg_temp_new(); shift = tcg_temp_new(); - tcg_gen_andi_tl(shift, cpu_gsr, 7); + tcg_gen_andi_tl(shift, gsr, 7); tcg_gen_shli_tl(shift, shift, 3); tcg_gen_shl_tl(t1, s1, shift); @@ -836,6 +837,11 @@ static void gen_op_faligndata(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) #endif } +static void gen_op_faligndata_g(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) +{ + gen_op_faligndata_i(dst, s1, s2, cpu_gsr); +} + static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) { #ifdef TARGET_SPARC64 @@ -5068,7 +5074,7 @@ TRANS(FORNOTd, VIS1, do_ddd, a, tcg_gen_orc_i64) TRANS(FORd, VIS1, do_ddd, a, tcg_gen_or_i64) TRANS(FPACK32, VIS1, do_ddd, a, gen_op_fpack32) -TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata) +TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata_g) TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) TRANS(FHADDd, VIS3, do_ddd, a, gen_op_fhaddd) @@ -5229,6 +5235,27 @@ TRANS(FNMADDd, FMAF, do_dddd, a, gen_op_fnmaddd) TRANS(FPMADDX, IMA, do_dddd, a, gen_op_fpmaddx) TRANS(FPMADDXHI, IMA, do_dddd, a, gen_op_fpmaddxhi) +static bool trans_FALIGNDATAi(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst, src1, src2; + TCGv src3; + + if (!avail_VIS4(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_D(dc, a->rd); + src2 = gen_load_fpr_D(dc, a->rs2); + src3 = gen_load_gpr(dc, a->rs1); + gen_op_faligndata_i(dst, src1, src2, src3); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + static bool do_env_qqq(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i128, TCGv_env, TCGv_i128, TCGv_i128)) { From b99c1bbddd86d170029c3e7ad45bec55d603b927 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 21:04:27 -0700 Subject: [PATCH 1230/2884] target/sparc: Implement 8-bit FPADD, FPADDS, and FPADDUS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 9 +++++++++ target/sparc/translate.c | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 0913fe7a86..80579642d1 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -509,6 +509,15 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ MOVdTOx 10 ..... 110110 00000 1 0001 0000 ..... @r_d2 MOVxTOd 10 ..... 110110 00000 1 0001 1000 ..... @d_r2 + FPADD8 10 ..... 110110 ..... 1 0010 0100 ..... @d_d_d + FPADDS8 10 ..... 110110 ..... 1 0010 0110 ..... @d_d_d + FPADDUS8 10 ..... 110110 ..... 1 0010 0111 ..... @d_d_d + FPADDUS16 10 ..... 110110 ..... 1 0010 0011 ..... @d_d_d + FPSUB8 10 ..... 110110 ..... 1 0101 0100 ..... @d_d_d + FPSUBS8 10 ..... 110110 ..... 1 0101 0110 ..... @d_d_d + FPSUBUS8 10 ..... 110110 ..... 1 0101 0111 ..... @d_d_d + FPSUBUS16 10 ..... 110110 ..... 1 0101 0011 ..... @d_d_d + FLCMPs 10 000 cc:2 110110 rs1:5 1 0101 0001 rs2:5 FLCMPd 10 000 cc:2 110110 ..... 1 0101 0010 ..... \ rs1=%dfp_rs1 rs2=%dfp_rs2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index f51ce2ff2c..973c0cec07 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5025,17 +5025,28 @@ static bool do_gvec_ddd(DisasContext *dc, arg_r_r_r *a, MemOp vece, return advance_pc(dc); } +TRANS(FPADD8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_add) TRANS(FPADD16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_add) TRANS(FPADD32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_add) + +TRANS(FPSUB8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_sub) TRANS(FPSUB16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sub) TRANS(FPSUB32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sub) + TRANS(FCHKSM16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fchksm16) TRANS(FMEAN16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fmean16) +TRANS(FPADDS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_ssadd) TRANS(FPADDS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_ssadd) TRANS(FPADDS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_ssadd) +TRANS(FPADDUS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_usadd) +TRANS(FPADDUS16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_usadd) + +TRANS(FPSUBS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_sssub) TRANS(FPSUBS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sssub) TRANS(FPSUBS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sssub) +TRANS(FPSUBUS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_ussub) +TRANS(FPSUBUS16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_ussub) TRANS(FSLL16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_shlv) TRANS(FSLL32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_shlv) From b3c934dd3457810b5c0810b0d85cf58dda53d8cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 21:31:54 -0700 Subject: [PATCH 1231/2884] target/sparc: Implement VIS4 comparisons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VIS4 completes the set, adding missing signed 8-bit ops and missing unsigned 16 and 32-bit ops. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/helper.h | 12 +-- target/sparc/insns.decode | 6 ++ target/sparc/translate.c | 12 +++ target/sparc/vis_helper.c | 170 +++++++++++++++++++++++++++++--------- 4 files changed, 153 insertions(+), 47 deletions(-) diff --git a/target/sparc/helper.h b/target/sparc/helper.h index fe0d8bc593..134e519a37 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -122,19 +122,19 @@ DEF_HELPER_FLAGS_2(fchksm16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmean16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fslas16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fslas32, TCG_CALL_NO_RWG_SE, i64, i64, i64) -#define VIS_CMPHELPER(name) \ +#define VIS_CMPHELPER(name) \ + DEF_HELPER_FLAGS_2(f##name##8, TCG_CALL_NO_RWG_SE, \ + i64, i64, i64) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ - i64, i64, i64) \ + i64, i64, i64) \ DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) VIS_CMPHELPER(cmpgt) VIS_CMPHELPER(cmpeq) VIS_CMPHELPER(cmple) VIS_CMPHELPER(cmpne) -DEF_HELPER_FLAGS_2(fcmpeq8, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fcmpne8, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fcmpule8, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fcmpugt8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +VIS_CMPHELPER(cmpugt) +VIS_CMPHELPER(cmpule) DEF_HELPER_FLAGS_2(xmulx, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(xmulxhi, TCG_CALL_NO_RWG_SE, i64, i64, i64) #endif diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 80579642d1..be591171ad 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -425,6 +425,12 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPCMPUGT8 10 ..... 110110 ..... 1 0010 1000 ..... @r_d_d FPCMPNE8 10 ..... 110110 ..... 1 0010 0010 ..... @r_d_d FPCMPEQ8 10 ..... 110110 ..... 1 0010 1010 ..... @r_d_d + FPCMPLE8 10 ..... 110110 ..... 0 0011 0100 ..... @r_d_d + FPCMPGT8 10 ..... 110110 ..... 0 0011 1100 ..... @r_d_d + FPCMPULE16 10 ..... 110110 ..... 1 0010 1110 ..... @r_d_d + FPCMPUGT16 10 ..... 110110 ..... 1 0010 1011 ..... @r_d_d + FPCMPULE32 10 ..... 110110 ..... 1 0010 1111 ..... @r_d_d + FPCMPUGT32 10 ..... 110110 ..... 1 0010 1100 ..... @r_d_d FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @d_r_d FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @d_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 973c0cec07..e856c811af 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -67,15 +67,21 @@ # define gen_helper_fcmpeq8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpeq16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpeq32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpgt8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpgt16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpgt32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmple8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmple16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmple32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpne8 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpne16 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpne32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpule8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpule16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpule32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fcmpugt8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpugt16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpugt32 ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fdtox ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) # define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) @@ -5119,16 +5125,22 @@ TRANS(FPCMPLE16, VIS1, do_rdd, a, gen_helper_fcmple16) TRANS(FPCMPNE16, VIS1, do_rdd, a, gen_helper_fcmpne16) TRANS(FPCMPGT16, VIS1, do_rdd, a, gen_helper_fcmpgt16) TRANS(FPCMPEQ16, VIS1, do_rdd, a, gen_helper_fcmpeq16) +TRANS(FPCMPULE16, VIS4, do_rdd, a, gen_helper_fcmpule16) +TRANS(FPCMPUGT16, VIS4, do_rdd, a, gen_helper_fcmpugt16) TRANS(FPCMPLE32, VIS1, do_rdd, a, gen_helper_fcmple32) TRANS(FPCMPNE32, VIS1, do_rdd, a, gen_helper_fcmpne32) TRANS(FPCMPGT32, VIS1, do_rdd, a, gen_helper_fcmpgt32) TRANS(FPCMPEQ32, VIS1, do_rdd, a, gen_helper_fcmpeq32) +TRANS(FPCMPULE32, VIS4, do_rdd, a, gen_helper_fcmpule32) +TRANS(FPCMPUGT32, VIS4, do_rdd, a, gen_helper_fcmpugt32) TRANS(FPCMPEQ8, VIS3B, do_rdd, a, gen_helper_fcmpeq8) TRANS(FPCMPNE8, VIS3B, do_rdd, a, gen_helper_fcmpne8) TRANS(FPCMPULE8, VIS3B, do_rdd, a, gen_helper_fcmpule8) TRANS(FPCMPUGT8, VIS3B, do_rdd, a, gen_helper_fcmpugt8) +TRANS(FPCMPLE8, VIS4, do_rdd, a, gen_helper_fcmple8) +TRANS(FPCMPGT8, VIS4, do_rdd, a, gen_helper_fcmpgt8) TRANS(PDISTN, VIS3, do_rdd, a, gen_op_pdistn) TRANS(XMULX, VIS3, do_rrr, a, gen_helper_xmulx) diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index c927a054b8..371f5445a1 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -66,6 +66,7 @@ target_ulong helper_array8(target_ulong rs1, target_ulong rs2) #define VIS_W64(n) w[3 - (n)] #define VIS_SW64(n) sw[3 - (n)] #define VIS_L64(n) l[1 - (n)] +#define VIS_SL64(n) sl[1 - (n)] #define VIS_B32(n) b[3 - (n)] #define VIS_W32(n) w[1 - (n)] #else @@ -74,6 +75,7 @@ target_ulong helper_array8(target_ulong rs1, target_ulong rs2) #define VIS_W64(n) w[n] #define VIS_SW64(n) sw[n] #define VIS_L64(n) l[n] +#define VIS_SL64(n) sl[n] #define VIS_B32(n) b[n] #define VIS_W32(n) w[n] #endif @@ -84,6 +86,7 @@ typedef union { uint16_t w[4]; int16_t sw[4]; uint32_t l[2]; + int32_t sl[2]; uint64_t ll; float64 d; } VIS64; @@ -198,47 +201,6 @@ uint64_t helper_fexpand(uint32_t src2) return d.ll; } -#define VIS_CMPHELPER(name, F) \ - uint64_t name##16(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \ - d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \ - d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \ - d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \ - d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \ - \ - return d.ll; \ - } \ - \ - uint64_t name##32(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \ - d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \ - d.VIS_L64(1) = 0; \ - \ - return d.ll; \ - } - -#define FCMPGT(a, b) ((a) > (b)) -#define FCMPEQ(a, b) ((a) == (b)) -#define FCMPLE(a, b) ((a) <= (b)) -#define FCMPNE(a, b) ((a) != (b)) - -VIS_CMPHELPER(helper_fcmpgt, FCMPGT) -VIS_CMPHELPER(helper_fcmpeq, FCMPEQ) -VIS_CMPHELPER(helper_fcmple, FCMPLE) -VIS_CMPHELPER(helper_fcmpne, FCMPNE) - uint64_t helper_fcmpeq8(uint64_t src1, uint64_t src2) { uint64_t a = src1 ^ src2; @@ -260,6 +222,25 @@ uint64_t helper_fcmpne8(uint64_t src1, uint64_t src2) return helper_fcmpeq8(src1, src2) ^ 0xff; } +uint64_t helper_fcmple8(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 8; ++i) { + r |= (s1.VIS_SB64(i) <= s2.VIS_SB64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpgt8(uint64_t src1, uint64_t src2) +{ + return helper_fcmple8(src1, src2) ^ 0xff; +} + uint64_t helper_fcmpule8(uint64_t src1, uint64_t src2) { VIS64 s1, s2; @@ -279,6 +260,113 @@ uint64_t helper_fcmpugt8(uint64_t src1, uint64_t src2) return helper_fcmpule8(src1, src2) ^ 0xff; } +uint64_t helper_fcmpeq16(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + uint64_t m = 0x7fff7fff7fff7fffULL; + uint64_t c = ~(((a & m) + m) | a | m); + + /* a...............b...............c...............d............... */ + c |= c << 15; + /* ab..............bc..............cd..............d............... */ + c |= c << 30; + /* abcd............bcd.............cd..............d............... */ + return c >> 60; +} + +uint64_t helper_fcmpne16(uint64_t src1, uint64_t src2) +{ + return helper_fcmpeq16(src1, src2) ^ 0xf; +} + +uint64_t helper_fcmple16(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 4; ++i) { + r |= (s1.VIS_SW64(i) <= s2.VIS_SW64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpgt16(uint64_t src1, uint64_t src2) +{ + return helper_fcmple16(src1, src2) ^ 0xf; +} + +uint64_t helper_fcmpule16(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 4; ++i) { + r |= (s1.VIS_W64(i) <= s2.VIS_W64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpugt16(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule16(src1, src2) ^ 0xf; +} + +uint64_t helper_fcmpeq32(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + return ((uint32_t)a == 0) | (a >> 32 ? 0 : 2); +} + +uint64_t helper_fcmpne32(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + return ((uint32_t)a != 0) | (a >> 32 ? 2 : 0); +} + +uint64_t helper_fcmple32(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 2; ++i) { + r |= (s1.VIS_SL64(i) <= s2.VIS_SL64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpgt32(uint64_t src1, uint64_t src2) +{ + return helper_fcmple32(src1, src2) ^ 3; +} + +uint64_t helper_fcmpule32(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 2; ++i) { + r |= (s1.VIS_L64(i) <= s2.VIS_L64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpugt32(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule32(src1, src2) ^ 3; +} + uint64_t helper_pdist(uint64_t sum, uint64_t src1, uint64_t src2) { int i; From db11dfea83e35c534fef8a86603b72354be9d71b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 21:39:59 -0700 Subject: [PATCH 1232/2884] target/sparc: Implement FPMIN, FPMAX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 14 ++++++++++++++ target/sparc/translate.c | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index be591171ad..2ebee5a1ca 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -524,6 +524,20 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ FPSUBUS8 10 ..... 110110 ..... 1 0101 0111 ..... @d_d_d FPSUBUS16 10 ..... 110110 ..... 1 0101 0011 ..... @d_d_d + FPMIN8 10 ..... 110110 ..... 1 0001 1010 ..... @d_d_d + FPMIN16 10 ..... 110110 ..... 1 0001 1011 ..... @d_d_d + FPMIN32 10 ..... 110110 ..... 1 0001 1100 ..... @d_d_d + FPMINU8 10 ..... 110110 ..... 1 0101 1010 ..... @d_d_d + FPMINU16 10 ..... 110110 ..... 1 0101 1011 ..... @d_d_d + FPMINU32 10 ..... 110110 ..... 1 0101 1100 ..... @d_d_d + + FPMAX8 10 ..... 110110 ..... 1 0001 1101 ..... @d_d_d + FPMAX16 10 ..... 110110 ..... 1 0001 1110 ..... @d_d_d + FPMAX32 10 ..... 110110 ..... 1 0001 1111 ..... @d_d_d + FPMAXU8 10 ..... 110110 ..... 1 0101 1101 ..... @d_d_d + FPMAXU16 10 ..... 110110 ..... 1 0101 1110 ..... @d_d_d + FPMAXU32 10 ..... 110110 ..... 1 0101 1111 ..... @d_d_d + FLCMPs 10 000 cc:2 110110 rs1:5 1 0101 0001 rs2:5 FLCMPd 10 000 cc:2 110110 ..... 1 0101 0010 ..... \ rs1=%dfp_rs1 rs2=%dfp_rs2 diff --git a/target/sparc/translate.c b/target/sparc/translate.c index e856c811af..5bed23a00b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5061,6 +5061,20 @@ TRANS(FSRL32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_shrv) TRANS(FSRA16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sarv) TRANS(FSRA32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sarv) +TRANS(FPMIN8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_smin) +TRANS(FPMIN16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_smin) +TRANS(FPMIN32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_smin) +TRANS(FPMINU8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_umin) +TRANS(FPMINU16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_umin) +TRANS(FPMINU32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_umin) + +TRANS(FPMAX8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_smax) +TRANS(FPMAX16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_smax) +TRANS(FPMAX32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_smax) +TRANS(FPMAXU8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_umax) +TRANS(FPMAXU16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_umax) +TRANS(FPMAXU32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_umax) + static bool do_ddd(DisasContext *dc, arg_r_r_r *a, void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) { From 56f2ef9c7958320d574448f555cc3a82e500c485 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 21:44:01 -0700 Subject: [PATCH 1233/2884] target/sparc: Implement SUBXC, SUBXCcc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 2 ++ target/sparc/translate.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index 2ebee5a1ca..a7720560f8 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -447,6 +447,8 @@ FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ PDISTN 10 ..... 110110 ..... 0 0011 1111 ..... @r_d_d FMEAN16 10 ..... 110110 ..... 0 0100 0000 ..... @d_d_d + SUBXC 10 ..... 110110 ..... 0 0100 0001 ..... @r_r_r + SUBXCcc 10 ..... 110110 ..... 0 0100 0011 ..... @r_r_r FCHKSM16 10 ..... 110110 ..... 0 0100 0100 ..... @d_d_d FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @d_d_d FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 5bed23a00b..b10936a61a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -515,6 +515,17 @@ static void gen_op_subccc(TCGv dst, TCGv src1, TCGv src2) gen_op_subcc_int(dst, src1, src2, gen_carry32()); } +static void gen_op_subxc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_sub_tl(dst, src1, src2); + tcg_gen_sub_tl(dst, dst, cpu_cc_C); +} + +static void gen_op_subxccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subcc_int(dst, src1, src2, cpu_cc_C); +} + static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) { TCGv zero = tcg_constant_tl(0); @@ -3963,6 +3974,9 @@ TRANS(ARRAY32, VIS1, do_rrr, a, gen_op_array32) TRANS(ADDXC, VIS3, do_rrr, a, gen_op_addxc) TRANS(ADDXCcc, VIS3, do_rrr, a, gen_op_addxccc) +TRANS(SUBXC, VIS4, do_rrr, a, gen_op_subxc) +TRANS(SUBXCcc, VIS4, do_rrr, a, gen_op_subxccc) + TRANS(UMULXHI, VIS3, do_rrr, a, gen_op_umulxhi) static void gen_op_alignaddr(TCGv dst, TCGv s1, TCGv s2) From 6fbc032cbc83ba80009c4a2a18e4d5578bc9ba35 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 22:12:24 -0700 Subject: [PATCH 1234/2884] target/sparc: Implement MWAIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/insns.decode | 1 + target/sparc/translate.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index a7720560f8..fbcb4f7aef 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -124,6 +124,7 @@ CALL 01 i:s30 WRTICK_CMPR 10 10111 110000 ..... . ............. @n_r_ri WRSTICK 10 11000 110000 ..... . ............. @n_r_ri WRSTICK_CMPR 10 11001 110000 ..... . ............. @n_r_ri + WRMWAIT 10 11100 110000 ..... . ............. @n_r_ri ] # Before v8, rs1==0 was WRY, and the rest executed as nop. [ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index b10936a61a..6d0ad38fd5 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3344,6 +3344,17 @@ static void do_wrpowerdown(DisasContext *dc, TCGv src) TRANS(WRPOWERDOWN, POWERDOWN, do_wr_special, a, supervisor(dc), do_wrpowerdown) +static void do_wrmwait(DisasContext *dc, TCGv src) +{ + /* + * TODO: This is a stub version of mwait, which merely recognizes + * interrupts immediately and does not wait. + */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRMWAIT, VIS4, do_wr_special, a, true, do_wrmwait) + static void do_wrpsr(DisasContext *dc, TCGv src) { gen_helper_wrpsr(tcg_env, src); From eeb3f592cb364f9d5c70c5525fd90e43b216012d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 22:13:06 -0700 Subject: [PATCH 1235/2884] target/sparc: Implement monitor ASIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ignore the "monitor" portion and treat them the same as their base ASIs. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/asi.h | 4 ++++ target/sparc/ldst_helper.c | 4 ++++ target/sparc/translate.c | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/target/sparc/asi.h b/target/sparc/asi.h index a66829674b..14ffaa3842 100644 --- a/target/sparc/asi.h +++ b/target/sparc/asi.h @@ -144,6 +144,8 @@ * ASIs, "(4V)" designates SUN4V specific ASIs. "(NG4)" designates SPARC-T4 * and later ASIs. */ +#define ASI_MON_AIUP 0x12 /* (VIS4) Primary, user, monitor */ +#define ASI_MON_AIUS 0x13 /* (VIS4) Secondary, user, monitor */ #define ASI_REAL 0x14 /* Real address, cacheable */ #define ASI_PHYS_USE_EC 0x14 /* PADDR, E-cacheable */ #define ASI_REAL_IO 0x15 /* Real address, non-cacheable */ @@ -257,6 +259,8 @@ #define ASI_UDBL_CONTROL_R 0x7f /* External UDB control regs rd low*/ #define ASI_INTR_R 0x7f /* IRQ vector dispatch read */ #define ASI_INTR_DATAN_R 0x7f /* (III) In irq vector data reg N */ +#define ASI_MON_P 0x84 /* (VIS4) Primary, monitor */ +#define ASI_MON_S 0x85 /* (VIS4) Secondary, monitor */ #define ASI_PIC 0xb0 /* (NG4) PIC registers */ #define ASI_PST8_P 0xc0 /* Primary, 8 8-bit, partial */ #define ASI_PST8_S 0xc1 /* Secondary, 8 8-bit, partial */ diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 7bdf99e0c0..2d48e98bf4 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -1395,6 +1395,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_TWINX_PL: /* Primary, twinx, LE */ case ASI_TWINX_S: /* Secondary, twinx */ case ASI_TWINX_SL: /* Secondary, twinx, LE */ + case ASI_MON_P: + case ASI_MON_S: + case ASI_MON_AIUP: + case ASI_MON_AIUS: /* These are always handled inline. */ g_assert_not_reached(); diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 6d0ad38fd5..113639083b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -1607,6 +1607,7 @@ static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) case ASI_BLK_AIUP_L_4V: case ASI_BLK_AIUP: case ASI_BLK_AIUPL: + case ASI_MON_AIUP: mem_idx = MMU_USER_IDX; break; case ASI_AIUS: /* As if user secondary */ @@ -1617,6 +1618,7 @@ static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) case ASI_BLK_AIUS_L_4V: case ASI_BLK_AIUS: case ASI_BLK_AIUSL: + case ASI_MON_AIUS: mem_idx = MMU_USER_SECONDARY_IDX; break; case ASI_S: /* Secondary */ @@ -1630,6 +1632,7 @@ static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) case ASI_FL8_SL: case ASI_FL16_S: case ASI_FL16_SL: + case ASI_MON_S: if (mem_idx == MMU_USER_IDX) { mem_idx = MMU_USER_SECONDARY_IDX; } else if (mem_idx == MMU_KERNEL_IDX) { @@ -1647,6 +1650,7 @@ static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) case ASI_FL8_PL: case ASI_FL16_P: case ASI_FL16_PL: + case ASI_MON_P: break; } switch (asi) { @@ -1664,6 +1668,10 @@ static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) case ASI_SL: case ASI_P: case ASI_PL: + case ASI_MON_P: + case ASI_MON_S: + case ASI_MON_AIUP: + case ASI_MON_AIUS: type = GET_ASI_DIRECT; break; case ASI_TWINX_REAL: From b12b72274320ce3ee516d963efd48766163cb240 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 4 Nov 2023 22:19:26 -0700 Subject: [PATCH 1236/2884] target/sparc: Enable VIS4 feature bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 88da5254e8..9bacfb68cb 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -552,6 +552,7 @@ static const char * const feature_name[] = { [CPU_FEATURE_BIT_FMAF] = "fmaf", [CPU_FEATURE_BIT_VIS3] = "vis3", [CPU_FEATURE_BIT_IMA] = "ima", + [CPU_FEATURE_BIT_VIS4] = "vis4", #else [CPU_FEATURE_BIT_MUL] = "mul", [CPU_FEATURE_BIT_DIV] = "div", @@ -886,6 +887,8 @@ static Property sparc_cpu_properties[] = { CPU_FEATURE_BIT_VIS3, false), DEFINE_PROP_BIT("ima", SPARCCPU, env.def.features, CPU_FEATURE_BIT_IMA, false), + DEFINE_PROP_BIT("vis4", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS4, false), #else DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, CPU_FEATURE_BIT_MUL, false), From 53ee5f551e5743516c90a662425276cae4cf0aeb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:22 -0700 Subject: [PATCH 1237/2884] util/hexdump: Use a GString for qemu_hexdump_line Allocate a new, or append to an existing GString instead of using a fixed sized buffer. Require the caller to determine the length of the line -- do not bound len here. Signed-off-by: Richard Henderson Message-Id: <20240412073346.458116-4-richard.henderson@linaro.org> --- hw/virtio/vhost-vdpa.c | 14 ++++++++------ include/qemu/cutils.h | 15 ++++++++++----- util/hexdump.c | 27 ++++++++++++++++----------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 7368b71902..419463c154 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -944,13 +944,15 @@ static int vhost_vdpa_set_config_call(struct vhost_dev *dev, static void vhost_vdpa_dump_config(struct vhost_dev *dev, const uint8_t *config, uint32_t config_len) { - int b, len; - char line[QEMU_HEXDUMP_LINE_LEN]; + g_autoptr(GString) str = g_string_sized_new(4 * 16); + size_t b, len; - for (b = 0; b < config_len; b += 16) { - len = config_len - b; - qemu_hexdump_line(line, config + b, len); - trace_vhost_vdpa_dump_config(dev, b, line); + for (b = 0; b < config_len; b += len) { + len = MIN(config_len - b, 16); + + g_string_truncate(str, 0); + qemu_hexdump_line(str, config + b, len); + trace_vhost_vdpa_dump_config(dev, b, str->str); } } diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index c5dea63742..14a3285343 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -282,12 +282,17 @@ static inline const char *yes_no(bool b) */ int parse_debug_env(const char *name, int max, int initial); -/* - * Hexdump a line of a byte buffer into a hexadecimal/ASCII buffer +/** + * qemu_hexdump_line: + * @str: GString into which to append + * @buf: buffer to dump + * @len: number of bytes to dump + * + * Append @len bytes of @buf as hexadecimal into @str. + * If @str is NULL, allocate a new string and return it; + * otherwise return @str. */ -#define QEMU_HEXDUMP_LINE_BYTES 16 /* Number of bytes to dump */ -#define QEMU_HEXDUMP_LINE_LEN 75 /* Number of characters in line */ -void qemu_hexdump_line(char *line, const void *bufptr, size_t len); +GString *qemu_hexdump_line(GString *str, const void *buf, size_t len); /* * Hexdump a buffer to a file. An optional string prefix is added to every line diff --git a/util/hexdump.c b/util/hexdump.c index 0f943e31e5..521e346bc6 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -16,22 +16,25 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -void qemu_hexdump_line(char *line, const void *bufptr, size_t len) +GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len) { - const char *buf = bufptr; - int i; + const uint8_t *buf = vbuf; + size_t i; - if (len > QEMU_HEXDUMP_LINE_BYTES) { - len = QEMU_HEXDUMP_LINE_BYTES; + if (str == NULL) { + /* Estimate the length of the output to avoid reallocs. */ + i = len * 3 + len / 4; + str = g_string_sized_new(i + 1); } for (i = 0; i < len; i++) { if (i != 0 && (i % 4) == 0) { - *line++ = ' '; + g_string_append_c(str, ' '); } - line += sprintf(line, " %02x", (unsigned char)buf[i]); + g_string_append_printf(str, " %02x", buf[i]); } - *line = '\0'; + + return str; } static void asciidump_line(char *line, const void *bufptr, size_t len) @@ -49,24 +52,26 @@ static void asciidump_line(char *line, const void *bufptr, size_t len) *line = '\0'; } +#define QEMU_HEXDUMP_LINE_BYTES 16 #define QEMU_HEXDUMP_LINE_WIDTH \ (QEMU_HEXDUMP_LINE_BYTES * 2 + QEMU_HEXDUMP_LINE_BYTES / 4) void qemu_hexdump(FILE *fp, const char *prefix, const void *bufptr, size_t size) { - char line[QEMU_HEXDUMP_LINE_LEN]; + g_autoptr(GString) str = g_string_sized_new(QEMU_HEXDUMP_LINE_WIDTH + 1); char ascii[QEMU_HEXDUMP_LINE_BYTES + 1]; size_t b, len; for (b = 0; b < size; b += len) { len = MIN(size - b, QEMU_HEXDUMP_LINE_BYTES); - qemu_hexdump_line(line, bufptr + b, len); + g_string_truncate(str, 0); + qemu_hexdump_line(str, bufptr + b, len); asciidump_line(ascii, bufptr + b, len); fprintf(fp, "%s: %04zx: %-*s %s\n", - prefix, b, QEMU_HEXDUMP_LINE_WIDTH, line, ascii); + prefix, b, QEMU_HEXDUMP_LINE_WIDTH, str->str, ascii); } } From c49d1c37d89a2ea994861600859b7dcd3ffa4ede Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:23 -0700 Subject: [PATCH 1238/2884] util/hexdump: Add unit_len and block_len to qemu_hexdump_line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generalize the current 1 byte unit and 4 byte blocking within the output. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240412073346.458116-5-richard.henderson@linaro.org> --- hw/virtio/vhost-vdpa.c | 2 +- include/qemu/cutils.h | 6 +++++- util/hexdump.c | 30 +++++++++++++++++++++--------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 419463c154..3cdaa12ed5 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -951,7 +951,7 @@ static void vhost_vdpa_dump_config(struct vhost_dev *dev, const uint8_t *config, len = MIN(config_len - b, 16); g_string_truncate(str, 0); - qemu_hexdump_line(str, config + b, len); + qemu_hexdump_line(str, config + b, len, 1, 4); trace_vhost_vdpa_dump_config(dev, b, str->str); } } diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 14a3285343..da15547bfb 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -287,12 +287,16 @@ int parse_debug_env(const char *name, int max, int initial); * @str: GString into which to append * @buf: buffer to dump * @len: number of bytes to dump + * @unit_len: add a space between every @unit_len bytes + * @block_len: add an extra space between every @block_len bytes * * Append @len bytes of @buf as hexadecimal into @str. + * Add spaces between every @unit_len and @block_len bytes. * If @str is NULL, allocate a new string and return it; * otherwise return @str. */ -GString *qemu_hexdump_line(GString *str, const void *buf, size_t len); +GString *qemu_hexdump_line(GString *str, const void *buf, size_t len, + size_t unit_len, size_t block_len); /* * Hexdump a buffer to a file. An optional string prefix is added to every line diff --git a/util/hexdump.c b/util/hexdump.c index 521e346bc6..b29326b7f2 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -1,5 +1,5 @@ /* - * Helper to hexdump a buffer +* Helper to hexdump a buffer * * Copyright (c) 2013 Red Hat, Inc. * Copyright (c) 2013 Gerd Hoffmann @@ -16,22 +16,34 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len) +GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len, + size_t unit_len, size_t block_len) { const uint8_t *buf = vbuf; - size_t i; + size_t u, b; if (str == NULL) { /* Estimate the length of the output to avoid reallocs. */ - i = len * 3 + len / 4; - str = g_string_sized_new(i + 1); + size_t est = len * 2; + if (unit_len) { + est += len / unit_len; + } + if (block_len) { + est += len / block_len; + } + str = g_string_sized_new(est + 1); } - for (i = 0; i < len; i++) { - if (i != 0 && (i % 4) == 0) { + for (u = 0, b = 0; len; u++, b++, len--, buf++) { + if (unit_len && u == unit_len) { g_string_append_c(str, ' '); + u = 0; } - g_string_append_printf(str, " %02x", buf[i]); + if (block_len && b == block_len) { + g_string_append_c(str, ' '); + b = 0; + } + g_string_append_printf(str, "%02x", *buf); } return str; @@ -67,7 +79,7 @@ void qemu_hexdump(FILE *fp, const char *prefix, len = MIN(size - b, QEMU_HEXDUMP_LINE_BYTES); g_string_truncate(str, 0); - qemu_hexdump_line(str, bufptr + b, len); + qemu_hexdump_line(str, bufptr + b, len, 1, 4); asciidump_line(ascii, bufptr + b, len); fprintf(fp, "%s: %04zx: %-*s %s\n", From 10e4927bc4c5ad673e12c0731e6150050cf327de Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:24 -0700 Subject: [PATCH 1239/2884] util/hexdump: Inline g_string_append_printf "%02x" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial arithmetic can be used for emitting the nibbles, rather than full-blown printf formatting. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240412073346.458116-6-richard.henderson@linaro.org> --- util/hexdump.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/util/hexdump.c b/util/hexdump.c index b29326b7f2..ae0d4992dc 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -16,6 +16,11 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +static inline char hexdump_nibble(unsigned x) +{ + return (x < 10 ? '0' : 'a' - 10) + x; +} + GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len, size_t unit_len, size_t block_len) { @@ -35,6 +40,8 @@ GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len, } for (u = 0, b = 0; len; u++, b++, len--, buf++) { + uint8_t c; + if (unit_len && u == unit_len) { g_string_append_c(str, ' '); u = 0; @@ -43,7 +50,10 @@ GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len, g_string_append_c(str, ' '); b = 0; } - g_string_append_printf(str, "%02x", *buf); + + c = *buf; + g_string_append_c(str, hexdump_nibble(c / 16)); + g_string_append_c(str, hexdump_nibble(c % 16)); } return str; From 3a8ff3667187459427eef5c6b1cdb950c563e094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 12 Apr 2024 00:33:25 -0700 Subject: [PATCH 1240/2884] hw/mips/malta: Add re-usable rng_seed_hex_new() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Extract common code from reinitialize_rng_seed and load_kernel to rng_seed_hex_new. Using qemu_hexdump_line both fixes the deprecation warning and simplifies the code base. Signed-off-by: Philippe Mathieu-Daudé [rth: Use qemu_hexdump_line.] Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-7-richard.henderson@linaro.org> --- hw/mips/malta.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index af74008c82..664a2ae0a9 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -26,6 +26,7 @@ #include "qemu/units.h" #include "qemu/bitops.h" #include "qemu/datadir.h" +#include "qemu/cutils.h" #include "qemu/guest-random.h" #include "hw/clock.h" #include "hw/southbridge/piix.h" @@ -850,15 +851,18 @@ static void G_GNUC_PRINTF(3, 4) prom_set(uint32_t *prom_buf, int index, va_end(ap); } -static void reinitialize_rng_seed(void *opaque) +static GString *rng_seed_hex_new(void) { - char *rng_seed_hex = opaque; uint8_t rng_seed[32]; qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); - for (size_t i = 0; i < sizeof(rng_seed); ++i) { - sprintf(rng_seed_hex + i * 2, "%02x", rng_seed[i]); - } + return qemu_hexdump_line(NULL, rng_seed, sizeof(rng_seed), 0, 0); +} + +static void reinitialize_rng_seed(void *opaque) +{ + g_autoptr(GString) hex = rng_seed_hex_new(); + memcpy(opaque, hex->str, hex->len); } /* Kernel */ @@ -870,8 +874,6 @@ static uint64_t load_kernel(void) uint32_t *prom_buf; long prom_size; int prom_index = 0; - uint8_t rng_seed[32]; - char rng_seed_hex[sizeof(rng_seed) * 2 + 1]; size_t rng_seed_prom_offset; kernel_size = load_elf(loaderparams.kernel_filename, NULL, @@ -946,14 +948,13 @@ static uint64_t load_kernel(void) prom_set(prom_buf, prom_index++, "modetty0"); prom_set(prom_buf, prom_index++, "38400n8r"); - qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); - for (size_t i = 0; i < sizeof(rng_seed); ++i) { - sprintf(rng_seed_hex + i * 2, "%02x", rng_seed[i]); - } prom_set(prom_buf, prom_index++, "rngseed"); rng_seed_prom_offset = prom_index * ENVP_ENTRY_SIZE + sizeof(uint32_t) * ENVP_NB_ENTRIES; - prom_set(prom_buf, prom_index++, "%s", rng_seed_hex); + { + g_autoptr(GString) hex = rng_seed_hex_new(); + prom_set(prom_buf, prom_index++, "%s", hex->str); + } prom_set(prom_buf, prom_index++, NULL); From 4b69210978bdf92d50b84d8662b3c38c78d79803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 12 Apr 2024 00:33:26 -0700 Subject: [PATCH 1241/2884] system/qtest: Replace sprintf by qemu_hexdump_line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Using qemu_hexdump_line both fixes the deprecation warning and simplifies the code base. Signed-off-by: Philippe Mathieu-Daudé ` [rth: Use qemu_hexdump_line] Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240412073346.458116-8-richard.henderson@linaro.org> --- system/qtest.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/system/qtest.c b/system/qtest.c index 6da58b3874..507a358f3b 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -601,9 +601,9 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_send_prefix(chr); qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value); } else if (strcmp(words[0], "read") == 0) { - uint64_t addr, len, i; + g_autoptr(GString) enc = NULL; + uint64_t addr, len; uint8_t *data; - char *enc; int ret; g_assert(words[1] && words[2]); @@ -618,16 +618,12 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, len); - enc = g_malloc(2 * len + 1); - for (i = 0; i < len; i++) { - sprintf(&enc[i * 2], "%02x", data[i]); - } + enc = qemu_hexdump_line(NULL, data, len, 0, 0); qtest_send_prefix(chr); - qtest_sendf(chr, "OK 0x%s\n", enc); + qtest_sendf(chr, "OK 0x%s\n", enc->str); g_free(data); - g_free(enc); } else if (strcmp(words[0], "b64read") == 0) { uint64_t addr, len; uint8_t *data; From 00a17d803d0931b00bffdb3b3e8a3e81251de9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 12 Apr 2024 00:33:27 -0700 Subject: [PATCH 1242/2884] hw/scsi/scsi-disk: Use qemu_hexdump_line to avoid sprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Using qemu_hexdump_line both fixes the deprecation warning and simplifies the code base. Note that this drops the "0x" prefix to every byte, which should be of no consequence to tracing. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240412073346.458116-9-richard.henderson@linaro.org> --- hw/scsi/scsi-disk.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 4bd7af9d0c..f386a2f01c 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2648,19 +2648,12 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { static void scsi_disk_new_request_dump(uint32_t lun, uint32_t tag, uint8_t *buf) { - int i; int len = scsi_cdb_length(buf); - char *line_buffer, *p; + g_autoptr(GString) str = NULL; assert(len > 0 && len <= 16); - line_buffer = g_malloc(len * 5 + 1); - - for (i = 0, p = line_buffer; i < len; i++) { - p += sprintf(p, " 0x%02x", buf[i]); - } - trace_scsi_disk_new_request(lun, tag, line_buffer); - - g_free(line_buffer); + str = qemu_hexdump_line(NULL, buf, len, 1, 0); + trace_scsi_disk_new_request(lun, tag, str->str); } static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, From 80e945894acf6ca837f03292a22cbf44550d22df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 12 Apr 2024 00:33:28 -0700 Subject: [PATCH 1243/2884] hw/ide/atapi: Use qemu_hexdump_line to avoid sprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Using qemu_hexdump_line both fixes the deprecation warning and simplifies the code base. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-10-richard.henderson@linaro.org> --- hw/ide/atapi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 73ec373184..fcb6cca157 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" #include "scsi/constants.h" @@ -1309,14 +1310,9 @@ void ide_atapi_cmd(IDEState *s) trace_ide_atapi_cmd(s, s->io_buffer[0]); if (trace_event_get_state_backends(TRACE_IDE_ATAPI_CMD_PACKET)) { - /* Each pretty-printed byte needs two bytes and a space; */ - char *ppacket = g_malloc(ATAPI_PACKET_SIZE * 3 + 1); - int i; - for (i = 0; i < ATAPI_PACKET_SIZE; i++) { - sprintf(ppacket + (i * 3), "%02x ", buf[i]); - } - trace_ide_atapi_cmd_packet(s, s->lcyl | (s->hcyl << 8), ppacket); - g_free(ppacket); + g_autoptr(GString) str = + qemu_hexdump_line(NULL, buf, ATAPI_PACKET_SIZE, 1, 0); + trace_ide_atapi_cmd_packet(s, s->lcyl | (s->hcyl << 8), str->str); } /* From 7210ddb45fd6ee32140ac9d9731b88c0f61c3f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 12 Apr 2024 00:33:29 -0700 Subject: [PATCH 1244/2884] hw/dma/pl330: Use qemu_hexdump_line to avoid sprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sprintf() is deprecated on Darwin since macOS 13.0 / XCode 14.1. Using qemu_hexdump_line both fixes the deprecation warning and simplifies the code base. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-11-richard.henderson@linaro.org> --- hw/dma/pl330.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 70a502d245..5f89295af3 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -15,6 +15,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" @@ -317,22 +318,14 @@ typedef struct PL330InsnDesc { static void pl330_hexdump(uint8_t *buf, size_t size) { - unsigned int b, i, len; - char tmpbuf[80]; + g_autoptr(GString) str = g_string_sized_new(64); + size_t b, len; - for (b = 0; b < size; b += 16) { - len = size - b; - if (len > 16) { - len = 16; - } - tmpbuf[0] = '\0'; - for (i = 0; i < len; i++) { - if ((i % 4) == 0) { - strcat(tmpbuf, " "); - } - sprintf(tmpbuf + strlen(tmpbuf), " %02x", buf[b + i]); - } - trace_pl330_hexdump(b, tmpbuf); + for (b = 0; b < size; b += len) { + len = MIN(16, size - b); + g_string_truncate(str, 0); + qemu_hexdump_line(str, buf + b, len, 1, 4); + trace_pl330_hexdump(b, str->str); } } From 58e5632c70e818a544c0e6cff28c7ef182ecd031 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:33 -0700 Subject: [PATCH 1245/2884] disas/microblaze: Split out print_immval_addr Unify the code blocks that try to print a symbolic address. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-15-richard.henderson@linaro.org> --- disas/microblaze.c | 89 +++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 60 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index 49a4c0fd40..3473c94164 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -767,6 +767,24 @@ read_insn_microblaze (bfd_vma memaddr, return inst; } +static void print_immval_addr(struct disassemble_info *info, bool immfound, + int immval, unsigned inst, int addend) +{ + if (info->print_address_func && info->symbol_at_address_func) { + if (immfound) { + immval |= get_int_field_imm(inst) & 0x0000ffff; + } else { + immval = (int16_t)get_int_field_imm(inst); + } + immval += addend; + if (immval != 0 && info->symbol_at_address_func(immval, info)) { + info->fprintf_func(info->stream, "\t// "); + info->print_address_func (immval, info); + } else if (addend) { + info->fprintf_func(info->stream, "\t// %x", immval); + } + } +} int print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) @@ -821,18 +839,8 @@ print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) break; case INST_TYPE_RD_R1_IMM: fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); - if (info->print_address_func && get_int_field_r1(inst) == 0 && info->symbol_at_address_func) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } + if (get_int_field_r1(inst) == 0) { + print_immval_addr(info, immfound, immval, inst, 0); } break; case INST_TYPE_RD_R1_IMM5: @@ -860,61 +868,22 @@ print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); /* The non-pc relative instructions are returns, which shouldn't have a label printed */ - if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET && info->symbol_at_address_func) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - immval += memaddr; - if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } else { - fprintf_func (stream, "\t\t// "); - fprintf_func (stream, "%x", immval); - } + if (op->inst_offset_type == INST_PC_OFFSET) { + print_immval_addr(info, immfound, immval, inst, memaddr); } break; case INST_TYPE_RD_IMM: fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); - if (info->print_address_func && info->symbol_at_address_func) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - if (op->inst_offset_type == INST_PC_OFFSET) - immval += (int) memaddr; - if (info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } - } + print_immval_addr(info, immfound, immval, inst, + op->inst_offset_type == INST_PC_OFFSET + ? memaddr : 0); break; case INST_TYPE_IMM: fprintf_func(stream, "\t%s", get_field_imm(inst)); - if (info->print_address_func && info->symbol_at_address_func && op->instr != imm) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - if (op->inst_offset_type == INST_PC_OFFSET) - immval += (int) memaddr; - if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } else if (op->inst_offset_type == INST_PC_OFFSET) { - fprintf_func (stream, "\t\t// "); - fprintf_func (stream, "%x", immval); - } + if (op->instr != imm) { + print_immval_addr(info, immfound, immval, inst, + op->inst_offset_type == INST_PC_OFFSET + ? memaddr : 0); } break; case INST_TYPE_RD_R2: From c3c6fed6460051e265f59d4cd451865faa3e8d71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:34 -0700 Subject: [PATCH 1246/2884] disas/microblaze: Re-indent print_insn_microblaze Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-16-richard.henderson@linaro.org> --- disas/microblaze.c | 263 ++++++++++++++++++++++++--------------------- 1 file changed, 141 insertions(+), 122 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index 3473c94164..c729c76585 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -787,134 +787,153 @@ static void print_immval_addr(struct disassemble_info *info, bool immfound, } int -print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) +print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) { - fprintf_function fprintf_func = info->fprintf_func; - void * stream = info->stream; - unsigned long inst, prev_inst; - const struct op_code_struct *op, *pop; - int immval = 0; - bfd_boolean immfound = FALSE; - static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ - static int prev_insn_vma = -1; /*init the prev insn vma */ - int curr_insn_vma = info->buffer_vma; + fprintf_function fprintf_func = info->fprintf_func; + void *stream = info->stream; + unsigned long inst, prev_inst; + const struct op_code_struct *op, *pop; + int immval = 0; + bool immfound = false; + static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ + static int prev_insn_vma = -1; /*init the prev insn vma */ + int curr_insn_vma = info->buffer_vma; - info->bytes_per_chunk = 4; + info->bytes_per_chunk = 4; - inst = read_insn_microblaze (memaddr, info, &op); - if (inst == 0) { - return -1; - } + inst = read_insn_microblaze (memaddr, info, &op); + if (inst == 0) { + return -1; + } - if (prev_insn_vma == curr_insn_vma) { - if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) { - prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); - if (prev_inst == 0) - return -1; - if (pop->instr == imm) { - immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; - immfound = TRUE; + if (prev_insn_vma == curr_insn_vma) { + if (memaddr - info->bytes_per_chunk == prev_insn_addr) { + prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); + if (prev_inst == 0) + return -1; + if (pop->instr == imm) { + immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; + immfound = TRUE; + } + else { + immval = 0; + immfound = FALSE; + } + } } - else { - immval = 0; - immfound = FALSE; - } - } - } - /* make curr insn as prev insn */ - prev_insn_addr = memaddr; - prev_insn_vma = curr_insn_vma; + /* make curr insn as prev insn */ + prev_insn_addr = memaddr; + prev_insn_vma = curr_insn_vma; - if (op->name == 0) { - fprintf_func (stream, ".short 0x%04lx", inst); - } - else - { - fprintf_func (stream, "%s", op->name); + if (op->name == 0) { + fprintf_func (stream, ".short 0x%04lx", inst); + return 4; + } + + fprintf_func (stream, "%s", op->name); - switch (op->inst_type) - { - case INST_TYPE_RD_R1_R2: - fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); - break; - case INST_TYPE_RD_R1_IMM: - fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); - if (get_int_field_r1(inst) == 0) { - print_immval_addr(info, immfound, immval, inst, 0); - } - break; - case INST_TYPE_RD_R1_IMM5: - fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); - break; - case INST_TYPE_RD_RFSL: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_rfsl(inst)); - break; - case INST_TYPE_R1_RFSL: - fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_rfsl(inst)); - break; - case INST_TYPE_RD_SPECIAL: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op)); - break; - case INST_TYPE_SPECIAL_R1: - fprintf_func(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst)); - break; - case INST_TYPE_RD_R1: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst)); - break; - case INST_TYPE_R1_R2: - fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst)); - break; - case INST_TYPE_R1_IMM: - fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); - /* The non-pc relative instructions are returns, which shouldn't - have a label printed */ - if (op->inst_offset_type == INST_PC_OFFSET) { - print_immval_addr(info, immfound, immval, inst, memaddr); - } - break; - case INST_TYPE_RD_IMM: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); - print_immval_addr(info, immfound, immval, inst, - op->inst_offset_type == INST_PC_OFFSET - ? memaddr : 0); - break; - case INST_TYPE_IMM: - fprintf_func(stream, "\t%s", get_field_imm(inst)); - if (op->instr != imm) { - print_immval_addr(info, immfound, immval, inst, - op->inst_offset_type == INST_PC_OFFSET - ? memaddr : 0); - } - break; - case INST_TYPE_RD_R2: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); - break; - case INST_TYPE_R2: - fprintf_func(stream, "\t%s", get_field_r2(inst)); - break; - case INST_TYPE_R1: - fprintf_func(stream, "\t%s", get_field_r1(inst)); - break; - case INST_TYPE_RD_R1_SPECIAL: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); - break; - case INST_TYPE_RD_IMM15: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm15(inst)); - break; - /* For tuqula instruction */ - case INST_TYPE_RD: - fprintf_func(stream, "\t%s", get_field_rd(inst)); - break; - case INST_TYPE_RFSL: - fprintf_func(stream, "\t%s", get_field_rfsl(inst)); - break; - default: - /* if the disassembler lags the instruction set */ - fprintf_func (stream, "\tundecoded operands, inst is 0x%04lx", inst); - break; - } + switch (op->inst_type) { + case INST_TYPE_RD_R1_R2: + fprintf_func(stream, "\t%s, %s, %s", + get_field_rd(inst), get_field_r1(inst), + get_field_r2(inst)); + break; + case INST_TYPE_RD_R1_IMM: + fprintf_func(stream, "\t%s, %s, %s", + get_field_rd(inst), get_field_r1(inst), + get_field_imm(inst)); + if (get_int_field_r1(inst) == 0) { + print_immval_addr(info, immfound, immval, inst, 0); + } + break; + case INST_TYPE_RD_R1_IMM5: + fprintf_func(stream, "\t%s, %s, %s", + get_field_rd(inst), get_field_r1(inst), + get_field_imm5(inst)); + break; + case INST_TYPE_RD_RFSL: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_rfsl(inst)); + break; + case INST_TYPE_R1_RFSL: + fprintf_func(stream, "\t%s, %s", + get_field_r1(inst), get_field_rfsl(inst)); + break; + case INST_TYPE_RD_SPECIAL: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_special(inst, op)); + break; + case INST_TYPE_SPECIAL_R1: + fprintf_func(stream, "\t%s, %s", + get_field_special(inst, op), get_field_r1(inst)); + break; + case INST_TYPE_RD_R1: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_r1(inst)); + break; + case INST_TYPE_R1_R2: + fprintf_func(stream, "\t%s, %s", + get_field_r1(inst), get_field_r2(inst)); + break; + case INST_TYPE_R1_IMM: + fprintf_func(stream, "\t%s, %s", + get_field_r1(inst), get_field_imm(inst)); + /* + * The non-pc relative instructions are returns, + * which shouldn't have a label printed. + */ + if (op->inst_offset_type == INST_PC_OFFSET) { + print_immval_addr(info, immfound, immval, inst, memaddr); + } + break; + case INST_TYPE_RD_IMM: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_imm(inst)); + print_immval_addr(info, immfound, immval, inst, + op->inst_offset_type == INST_PC_OFFSET + ? memaddr : 0); + break; + case INST_TYPE_IMM: + fprintf_func(stream, "\t%s", get_field_imm(inst)); + if (op->instr != imm) { + print_immval_addr(info, immfound, immval, inst, + op->inst_offset_type == INST_PC_OFFSET + ? memaddr : 0); + } + break; + case INST_TYPE_RD_R2: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_R2: + fprintf_func(stream, "\t%s", + get_field_r2(inst)); + break; + case INST_TYPE_R1: + fprintf_func(stream, "\t%s", + get_field_r1(inst)); + break; + case INST_TYPE_RD_R1_SPECIAL: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_RD_IMM15: + fprintf_func(stream, "\t%s, %s", + get_field_rd(inst), get_field_imm15(inst)); + break; + /* For tuqula instruction */ + case INST_TYPE_RD: + fprintf_func(stream, "\t%s", + get_field_rd(inst)); + break; + case INST_TYPE_RFSL: + fprintf_func(stream, "\t%s", + get_field_rfsl(inst)); + break; + default: + /* if the disassembler lags the instruction set */ + fprintf_func(stream, "\tundecoded operands, inst is 0x%04lx", inst); + break; } - - /* Say how many bytes we consumed? */ - return 4; + return 4; } From de66f9f7ab215e09b0514dbdcd8450f42efdaa9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:35 -0700 Subject: [PATCH 1247/2884] disas/microblaze: Merge op->name output into each fprintf In the common case, issue one single fprintf. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-17-richard.henderson@linaro.org> --- disas/microblaze.c | 80 +++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index c729c76585..a537ac65dd 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -830,54 +830,52 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) return 4; } - fprintf_func (stream, "%s", op->name); - switch (op->inst_type) { case INST_TYPE_RD_R1_R2: - fprintf_func(stream, "\t%s, %s, %s", - get_field_rd(inst), get_field_r1(inst), + fprintf_func(stream, "%s\t%s, %s, %s", + op->name, get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_RD_R1_IMM: - fprintf_func(stream, "\t%s, %s, %s", - get_field_rd(inst), get_field_r1(inst), + fprintf_func(stream, "%s\t%s, %s, %s", + op->name, get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); if (get_int_field_r1(inst) == 0) { print_immval_addr(info, immfound, immval, inst, 0); } break; case INST_TYPE_RD_R1_IMM5: - fprintf_func(stream, "\t%s, %s, %s", - get_field_rd(inst), get_field_r1(inst), + fprintf_func(stream, "%s\t%s, %s, %s", + op->name, get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); break; case INST_TYPE_RD_RFSL: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_rfsl(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_rfsl(inst)); break; case INST_TYPE_R1_RFSL: - fprintf_func(stream, "\t%s, %s", - get_field_r1(inst), get_field_rfsl(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_r1(inst), get_field_rfsl(inst)); break; case INST_TYPE_RD_SPECIAL: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_special(inst, op)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_special(inst, op)); break; case INST_TYPE_SPECIAL_R1: - fprintf_func(stream, "\t%s, %s", - get_field_special(inst, op), get_field_r1(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_special(inst, op), get_field_r1(inst)); break; case INST_TYPE_RD_R1: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_r1(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_r1(inst)); break; case INST_TYPE_R1_R2: - fprintf_func(stream, "\t%s, %s", - get_field_r1(inst), get_field_r2(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_R1_IMM: - fprintf_func(stream, "\t%s, %s", - get_field_r1(inst), get_field_imm(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_r1(inst), get_field_imm(inst)); /* * The non-pc relative instructions are returns, * which shouldn't have a label printed. @@ -887,14 +885,15 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_IMM: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_imm(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_imm(inst)); print_immval_addr(info, immfound, immval, inst, op->inst_offset_type == INST_PC_OFFSET ? memaddr : 0); break; case INST_TYPE_IMM: - fprintf_func(stream, "\t%s", get_field_imm(inst)); + fprintf_func(stream, "%s\t%s", + op->name, get_field_imm(inst)); if (op->instr != imm) { print_immval_addr(info, immfound, immval, inst, op->inst_offset_type == INST_PC_OFFSET @@ -902,37 +901,38 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_R2: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_r2(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_r2(inst)); break; case INST_TYPE_R2: - fprintf_func(stream, "\t%s", - get_field_r2(inst)); + fprintf_func(stream, "%s\t%s", + op->name, get_field_r2(inst)); break; case INST_TYPE_R1: - fprintf_func(stream, "\t%s", - get_field_r1(inst)); + fprintf_func(stream, "%s\t%s", + op->name, get_field_r1(inst)); break; case INST_TYPE_RD_R1_SPECIAL: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_r2(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_r2(inst)); break; case INST_TYPE_RD_IMM15: - fprintf_func(stream, "\t%s, %s", - get_field_rd(inst), get_field_imm15(inst)); + fprintf_func(stream, "%s\t%s, %s", + op->name, get_field_rd(inst), get_field_imm15(inst)); break; /* For tuqula instruction */ case INST_TYPE_RD: - fprintf_func(stream, "\t%s", - get_field_rd(inst)); + fprintf_func(stream, "%s\t%s", + op->name, get_field_rd(inst)); break; case INST_TYPE_RFSL: - fprintf_func(stream, "\t%s", - get_field_rfsl(inst)); + fprintf_func(stream, "%s\t%s", + op->name, get_field_rfsl(inst)); break; default: /* if the disassembler lags the instruction set */ - fprintf_func(stream, "\tundecoded operands, inst is 0x%04lx", inst); + fprintf_func(stream, "%s\tundecoded operands, inst is 0x%04lx", + op->name, inst); break; } return 4; From b0063d831b279335cecc22b981a71eace01e1d77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:36 -0700 Subject: [PATCH 1248/2884] disas/microblaze: Print registers directly with PRIreg Use a printf format instead of sprintf into a buffer. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-18-richard.henderson@linaro.org> --- disas/microblaze.c | 54 ++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index a537ac65dd..c12968f3b9 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -563,7 +563,7 @@ static const struct op_code_struct { }; /* prefix for register names */ -static const char register_prefix[] = "r"; +#define register_prefix "r" static const char fsl_register_prefix[] = "rfsl"; static const char pvr_register_prefix[] = "rpvr"; @@ -579,15 +579,16 @@ static const char pvr_register_prefix[] = "rpvr"; #include "disas/dis-asm.h" -#define get_field_rd(instr) get_field(instr, RD_MASK, RD_LOW) -#define get_field_r1(instr) get_field(instr, RA_MASK, RA_LOW) -#define get_field_r2(instr) get_field(instr, RB_MASK, RB_LOW) +#define PRIreg register_prefix "%ld" + +#define get_field_rd(instr) ((instr & RD_MASK) >> RD_LOW) +#define get_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) +#define get_field_r2(instr) ((instr & RB_MASK) >> RB_LOW) #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) /* Local function prototypes. */ -static char * get_field (long instr, long mask, unsigned short low); static char * get_field_imm (long instr); static char * get_field_imm5 (long instr); static char * get_field_rfsl (long instr); @@ -596,15 +597,6 @@ static char * get_field_imm15 (long instr); static char * get_field_unsigned_imm (long instr); #endif -static char * -get_field (long instr, long mask, unsigned short low) -{ - char tmpstr[25]; - snprintf(tmpstr, sizeof(tmpstr), "%s%d", register_prefix, - (int)((instr & mask) >> low)); - return(strdup(tmpstr)); -} - static char * get_field_imm (long instr) { @@ -832,12 +824,12 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) switch (op->inst_type) { case INST_TYPE_RD_R1_R2: - fprintf_func(stream, "%s\t%s, %s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", " PRIreg, op->name, get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_RD_R1_IMM: - fprintf_func(stream, "%s\t%s, %s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", %s", op->name, get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); if (get_int_field_r1(inst) == 0) { @@ -845,36 +837,36 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_R1_IMM5: - fprintf_func(stream, "%s\t%s, %s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", %s", op->name, get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); break; case INST_TYPE_RD_RFSL: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", %s", op->name, get_field_rd(inst), get_field_rfsl(inst)); break; case INST_TYPE_R1_RFSL: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", %s", op->name, get_field_r1(inst), get_field_rfsl(inst)); break; case INST_TYPE_RD_SPECIAL: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", %s", op->name, get_field_rd(inst), get_field_special(inst, op)); break; case INST_TYPE_SPECIAL_R1: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t%s, " PRIreg, op->name, get_field_special(inst, op), get_field_r1(inst)); break; case INST_TYPE_RD_R1: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, op->name, get_field_rd(inst), get_field_r1(inst)); break; case INST_TYPE_R1_R2: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, op->name, get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_R1_IMM: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", %s", op->name, get_field_r1(inst), get_field_imm(inst)); /* * The non-pc relative instructions are returns, @@ -885,7 +877,7 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_IMM: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", %s", op->name, get_field_rd(inst), get_field_imm(inst)); print_immval_addr(info, immfound, immval, inst, op->inst_offset_type == INST_PC_OFFSET @@ -901,28 +893,28 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_R2: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, op->name, get_field_rd(inst), get_field_r2(inst)); break; case INST_TYPE_R2: - fprintf_func(stream, "%s\t%s", + fprintf_func(stream, "%s\t" PRIreg, op->name, get_field_r2(inst)); break; case INST_TYPE_R1: - fprintf_func(stream, "%s\t%s", + fprintf_func(stream, "%s\t" PRIreg, op->name, get_field_r1(inst)); break; case INST_TYPE_RD_R1_SPECIAL: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, op->name, get_field_rd(inst), get_field_r2(inst)); break; case INST_TYPE_RD_IMM15: - fprintf_func(stream, "%s\t%s, %s", + fprintf_func(stream, "%s\t" PRIreg ", %s", op->name, get_field_rd(inst), get_field_imm15(inst)); break; /* For tuqula instruction */ case INST_TYPE_RD: - fprintf_func(stream, "%s\t%s", + fprintf_func(stream, "%s\t" PRIreg, op->name, get_field_rd(inst)); break; case INST_TYPE_RFSL: From c521e0a3e1153039a31eb4a2071c41931834af8c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:37 -0700 Subject: [PATCH 1249/2884] disas/microblaze: Print immediates directly with PRIimm Use a printf format instead of sprintf into a buffer. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-19-richard.henderson@linaro.org> --- disas/microblaze.c | 61 +++++++++------------------------------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index c12968f3b9..390f98c0a3 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -580,40 +580,21 @@ static const char pvr_register_prefix[] = "rpvr"; #include "disas/dis-asm.h" #define PRIreg register_prefix "%ld" +#define PRIimm "%d" #define get_field_rd(instr) ((instr & RD_MASK) >> RD_LOW) #define get_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) #define get_field_r2(instr) ((instr & RB_MASK) >> RB_LOW) +#define get_field_imm(instr) ((int16_t)instr) +#define get_field_imm5(instr) ((int)instr & IMM5_MASK) +#define get_field_imm15(instr) ((int)instr & IMM15_MASK) + #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) /* Local function prototypes. */ -static char * get_field_imm (long instr); -static char * get_field_imm5 (long instr); static char * get_field_rfsl (long instr); -static char * get_field_imm15 (long instr); -#if 0 -static char * get_field_unsigned_imm (long instr); -#endif - -static char * -get_field_imm (long instr) -{ - char tmpstr[25]; - snprintf(tmpstr, sizeof(tmpstr), "%d", - (short)((instr & IMM_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - -static char * -get_field_imm5 (long instr) -{ - char tmpstr[25]; - snprintf(tmpstr, sizeof(tmpstr), "%d", - (short)((instr & IMM5_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} static char * get_field_rfsl (long instr) @@ -624,26 +605,6 @@ get_field_rfsl (long instr) return(strdup(tmpstr)); } -static char * -get_field_imm15 (long instr) -{ - char tmpstr[25]; - snprintf(tmpstr, sizeof(tmpstr), "%d", - (short)((instr & IMM15_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - -#if 0 -static char * -get_field_unsigned_imm (long instr) -{ - char tmpstr[25]; - snprintf(tmpstr, sizeof(tmpstr), "%d", - (int)((instr & IMM_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} -#endif - /* char * get_field_special (instr) @@ -829,7 +790,7 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) get_field_r2(inst)); break; case INST_TYPE_RD_R1_IMM: - fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", " PRIimm, op->name, get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); if (get_int_field_r1(inst) == 0) { @@ -837,7 +798,7 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_R1_IMM5: - fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", " PRIimm, op->name, get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); break; @@ -866,7 +827,7 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) op->name, get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_R1_IMM: - fprintf_func(stream, "%s\t" PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIimm, op->name, get_field_r1(inst), get_field_imm(inst)); /* * The non-pc relative instructions are returns, @@ -877,14 +838,14 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) } break; case INST_TYPE_RD_IMM: - fprintf_func(stream, "%s\t" PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIimm, op->name, get_field_rd(inst), get_field_imm(inst)); print_immval_addr(info, immfound, immval, inst, op->inst_offset_type == INST_PC_OFFSET ? memaddr : 0); break; case INST_TYPE_IMM: - fprintf_func(stream, "%s\t%s", + fprintf_func(stream, "%s\t" PRIimm, op->name, get_field_imm(inst)); if (op->instr != imm) { print_immval_addr(info, immfound, immval, inst, @@ -909,7 +870,7 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) op->name, get_field_rd(inst), get_field_r2(inst)); break; case INST_TYPE_RD_IMM15: - fprintf_func(stream, "%s\t" PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIimm, op->name, get_field_rd(inst), get_field_imm15(inst)); break; /* For tuqula instruction */ From b35ab133ebcc4cd2ff04882033d2c51957a4d6e1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:38 -0700 Subject: [PATCH 1250/2884] disas/microblaze: Print registers directly with PRIrfsl Use a printf format instead of sprintf into a buffer. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-20-richard.henderson@linaro.org> --- disas/microblaze.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index 390f98c0a3..24febfdea9 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -564,7 +564,6 @@ static const struct op_code_struct { /* prefix for register names */ #define register_prefix "r" -static const char fsl_register_prefix[] = "rfsl"; static const char pvr_register_prefix[] = "rpvr"; @@ -580,11 +579,13 @@ static const char pvr_register_prefix[] = "rpvr"; #include "disas/dis-asm.h" #define PRIreg register_prefix "%ld" +#define PRIrfsl register_prefix "fsl%ld" #define PRIimm "%d" #define get_field_rd(instr) ((instr & RD_MASK) >> RD_LOW) #define get_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) #define get_field_r2(instr) ((instr & RB_MASK) >> RB_LOW) +#define get_field_rfsl(instr) (instr & RFSL_MASK) #define get_field_imm(instr) ((int16_t)instr) #define get_field_imm5(instr) ((int)instr & IMM5_MASK) #define get_field_imm15(instr) ((int)instr & IMM15_MASK) @@ -592,19 +593,6 @@ static const char pvr_register_prefix[] = "rpvr"; #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) -/* Local function prototypes. */ - -static char * get_field_rfsl (long instr); - -static char * -get_field_rfsl (long instr) -{ - char tmpstr[25]; - snprintf(tmpstr, sizeof(tmpstr), "%s%d", fsl_register_prefix, - (short)((instr & RFSL_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - /* char * get_field_special (instr) @@ -803,11 +791,11 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) get_field_imm5(inst)); break; case INST_TYPE_RD_RFSL: - fprintf_func(stream, "%s\t" PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIrfsl, op->name, get_field_rd(inst), get_field_rfsl(inst)); break; case INST_TYPE_R1_RFSL: - fprintf_func(stream, "%s\t" PRIreg ", %s", + fprintf_func(stream, "%s\t" PRIreg ", " PRIrfsl, op->name, get_field_r1(inst), get_field_rfsl(inst)); break; case INST_TYPE_RD_SPECIAL: @@ -879,7 +867,7 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) op->name, get_field_rd(inst)); break; case INST_TYPE_RFSL: - fprintf_func(stream, "%s\t%s", + fprintf_func(stream, "%s\t" PRIrfsl, op->name, get_field_rfsl(inst)); break; default: From a45e0b5290d5c1bfb1c45b6b199d5185f25ace04 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:39 -0700 Subject: [PATCH 1251/2884] disas/microblaze: Split get_field_special Extract the raw special index and a function to lookup a name. Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-21-richard.henderson@linaro.org> --- disas/microblaze.c | 142 +++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 81 deletions(-) diff --git a/disas/microblaze.c b/disas/microblaze.c index 24febfdea9..197327fae4 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -564,8 +564,6 @@ static const struct op_code_struct { /* prefix for register names */ #define register_prefix "r" -static const char pvr_register_prefix[] = "rpvr"; - /* #defines for valid immediate range */ #define MIN_IMM ((int) 0x80000000) @@ -580,6 +578,7 @@ static const char pvr_register_prefix[] = "rpvr"; #define PRIreg register_prefix "%ld" #define PRIrfsl register_prefix "fsl%ld" +#define PRIpvr register_prefix "pvr%d" #define PRIimm "%d" #define get_field_rd(instr) ((instr & RD_MASK) >> RD_LOW) @@ -593,83 +592,48 @@ static const char pvr_register_prefix[] = "rpvr"; #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) -/* - char * - get_field_special (instr) - long instr; - { - char tmpstr[25]; - - snprintf(tmpstr, sizeof(tmpstr), "%s%s", register_prefix, - (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr"); - - return(strdup(tmpstr)); - } -*/ - -static char * -get_field_special(long instr, const struct op_code_struct *op) +static int get_field_special(long instr, const struct op_code_struct *op) { - char tmpstr[25]; - char spr[6]; + return ((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask; +} - switch ( (((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ) { - - case REG_MSR_MASK : - strcpy(spr, "msr"); - break; - case REG_PC_MASK : - strcpy(spr, "pc"); - break; - case REG_EAR_MASK : - strcpy(spr, "ear"); - break; - case REG_ESR_MASK : - strcpy(spr, "esr"); - break; - case REG_FSR_MASK : - strcpy(spr, "fsr"); - break; - case REG_BTR_MASK : - strcpy(spr, "btr"); - break; - case REG_EDR_MASK : - strcpy(spr, "edr"); - break; - case REG_PID_MASK : - strcpy(spr, "pid"); - break; - case REG_ZPR_MASK : - strcpy(spr, "zpr"); - break; - case REG_TLBX_MASK : - strcpy(spr, "tlbx"); - break; - case REG_TLBLO_MASK : - strcpy(spr, "tlblo"); - break; - case REG_TLBHI_MASK : - strcpy(spr, "tlbhi"); - break; - case REG_TLBSX_MASK : - strcpy(spr, "tlbsx"); - break; - default : - { - if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { - snprintf(tmpstr, sizeof(tmpstr), "%s%u", pvr_register_prefix, - (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ - op->immval_mask) ^ REG_PVR_MASK); - return(strdup(tmpstr)); - } else { - strcpy(spr, "pc"); - } - } - break; - } - - snprintf(tmpstr, sizeof(tmpstr), "%s%s", register_prefix, spr); - return(strdup(tmpstr)); +/* Returns NULL for PVR registers, which should be rendered differently. */ +static const char *get_special_name(int special) +{ + switch (special) { + case REG_MSR_MASK: + return register_prefix "msr"; + case REG_PC_MASK: + return register_prefix "pc"; + case REG_EAR_MASK: + return register_prefix "ear"; + case REG_ESR_MASK: + return register_prefix "esr"; + case REG_FSR_MASK: + return register_prefix "fsr"; + case REG_BTR_MASK: + return register_prefix "btr"; + case REG_EDR_MASK: + return register_prefix "edr"; + case REG_PID_MASK: + return register_prefix "pid"; + case REG_ZPR_MASK: + return register_prefix "zpr"; + case REG_TLBX_MASK: + return register_prefix "tlbx"; + case REG_TLBLO_MASK: + return register_prefix "tlblo"; + case REG_TLBHI_MASK: + return register_prefix "tlbhi"; + case REG_TLBSX_MASK: + return register_prefix "tlbsx"; + default: + if ((special & 0xE000) == REG_PVR_MASK) { + /* pvr register */ + return NULL; + } + return register_prefix "pc"; + } } static unsigned long @@ -739,6 +703,8 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ static int prev_insn_vma = -1; /*init the prev insn vma */ int curr_insn_vma = info->buffer_vma; + int special; + const char *special_name; info->bytes_per_chunk = 4; @@ -799,12 +765,26 @@ print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) op->name, get_field_r1(inst), get_field_rfsl(inst)); break; case INST_TYPE_RD_SPECIAL: - fprintf_func(stream, "%s\t" PRIreg ", %s", - op->name, get_field_rd(inst), get_field_special(inst, op)); + special = get_field_special(inst, op); + special_name = get_special_name(special); + if (special_name) { + fprintf_func(stream, "%s\t" PRIreg ", %s", + op->name, get_field_rd(inst), special_name); + } else { + fprintf_func(stream, "%s\t" PRIreg ", " PRIpvr, + op->name, get_field_rd(inst), special ^ REG_PVR_MASK); + } break; case INST_TYPE_SPECIAL_R1: - fprintf_func(stream, "%s\t%s, " PRIreg, - op->name, get_field_special(inst, op), get_field_r1(inst)); + special = get_field_special(inst, op); + special_name = get_special_name(special); + if (special_name) { + fprintf_func(stream, "%s\t%s, " PRIreg, + op->name, special_name, get_field_r1(inst)); + } else { + fprintf_func(stream, "%s\t" PRIpvr ", " PRIreg, + op->name, special ^ REG_PVR_MASK, get_field_r1(inst)); + } break; case INST_TYPE_RD_R1: fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, From b89fb575fd467ed5dfde4608d51c47c2aa427f30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 12 Apr 2024 00:33:40 -0700 Subject: [PATCH 1252/2884] disas/riscv: Use GString in format_inst Allocate and fill a GString instead of snprintf and appending to a fixed sized buffer. Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Pierrick Bouvier Message-Id: <20240412073346.458116-22-richard.henderson@linaro.org> --- disas/riscv.c | 209 ++++++++++++++++++++++---------------------------- 1 file changed, 92 insertions(+), 117 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 297cfa2f63..90d6b26de9 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4820,272 +4820,249 @@ static size_t inst_length(rv_inst inst) /* format instruction */ -static void append(char *s1, const char *s2, size_t n) -{ - size_t l1 = strlen(s1); - if (n - l1 - 1 > 0) { - strncat(s1, s2, n - l1); - } -} - -static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) +static GString *format_inst(size_t tab, rv_decode *dec) { const rv_opcode_data *opcode_data = dec->opcode_data; - char tmp[64]; + GString *buf = g_string_sized_new(64); const char *fmt; fmt = opcode_data[dec->op].format; while (*fmt) { switch (*fmt) { case 'O': - append(buf, opcode_data[dec->op].name, buflen); + g_string_append(buf, opcode_data[dec->op].name); break; case '(': - append(buf, "(", buflen); - break; case ',': - append(buf, ",", buflen); - break; case ')': - append(buf, ")", buflen); - break; case '-': - append(buf, "-", buflen); + g_string_append_c(buf, *fmt); break; case 'b': - snprintf(tmp, sizeof(tmp), "%d", dec->bs); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->bs); break; case 'n': - snprintf(tmp, sizeof(tmp), "%d", dec->rnum); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->rnum); break; case '0': - append(buf, rv_ireg_name_sym[dec->rd], buflen); + g_string_append(buf, rv_ireg_name_sym[dec->rd]); break; case '1': - append(buf, rv_ireg_name_sym[dec->rs1], buflen); + g_string_append(buf, rv_ireg_name_sym[dec->rs1]); break; case '2': - append(buf, rv_ireg_name_sym[dec->rs2], buflen); + g_string_append(buf, rv_ireg_name_sym[dec->rs2]); break; case '3': - append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rd] : - rv_freg_name_sym[dec->rd], - buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rd]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rd]); + } break; case '4': - append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs1] : - rv_freg_name_sym[dec->rs1], - buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rs1]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rs1]); + } break; case '5': - append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs2] : - rv_freg_name_sym[dec->rs2], - buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rs2]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rs2]); + } break; case '6': - append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs3] : - rv_freg_name_sym[dec->rs3], - buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rs3]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rs3]); + } break; case '7': - snprintf(tmp, sizeof(tmp), "%d", dec->rs1); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->rs1); break; case 'i': - snprintf(tmp, sizeof(tmp), "%d", dec->imm); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->imm); break; case 'u': - snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b111111)); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%u", ((uint32_t)dec->imm & 0b111111)); break; case 'j': - snprintf(tmp, sizeof(tmp), "%d", dec->imm1); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->imm1); break; case 'o': - snprintf(tmp, sizeof(tmp), "%d", dec->imm); - append(buf, tmp, buflen); - while (strlen(buf) < tab * 2) { - append(buf, " ", buflen); + g_string_append_printf(buf, "%d", dec->imm); + while (buf->len < tab * 2) { + g_string_append_c(buf, ' '); } - snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64, - dec->pc + dec->imm); - append(buf, tmp, buflen); + g_string_append_printf(buf, "# 0x%" PRIx64, dec->pc + dec->imm); break; case 'U': fmt++; - snprintf(tmp, sizeof(tmp), "%d", dec->imm >> 12); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->imm >> 12); if (*fmt == 'o') { - while (strlen(buf) < tab * 2) { - append(buf, " ", buflen); + while (buf->len < tab * 2) { + g_string_append_c(buf, ' '); } - snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64, - dec->pc + dec->imm); - append(buf, tmp, buflen); + g_string_append_printf(buf, "# 0x%" PRIx64, dec->pc + dec->imm); } break; case 'c': { const char *name = csr_name(dec->imm & 0xfff); if (name) { - append(buf, name, buflen); + g_string_append(buf, name); } else { - snprintf(tmp, sizeof(tmp), "0x%03x", dec->imm & 0xfff); - append(buf, tmp, buflen); + g_string_append_printf(buf, "0x%03x", dec->imm & 0xfff); } break; } case 'r': switch (dec->rm) { case rv_rm_rne: - append(buf, "rne", buflen); + g_string_append(buf, "rne"); break; case rv_rm_rtz: - append(buf, "rtz", buflen); + g_string_append(buf, "rtz"); break; case rv_rm_rdn: - append(buf, "rdn", buflen); + g_string_append(buf, "rdn"); break; case rv_rm_rup: - append(buf, "rup", buflen); + g_string_append(buf, "rup"); break; case rv_rm_rmm: - append(buf, "rmm", buflen); + g_string_append(buf, "rmm"); break; case rv_rm_dyn: - append(buf, "dyn", buflen); + g_string_append(buf, "dyn"); break; default: - append(buf, "inv", buflen); + g_string_append(buf, "inv"); break; } break; case 'p': if (dec->pred & rv_fence_i) { - append(buf, "i", buflen); + g_string_append_c(buf, 'i'); } if (dec->pred & rv_fence_o) { - append(buf, "o", buflen); + g_string_append_c(buf, 'o'); } if (dec->pred & rv_fence_r) { - append(buf, "r", buflen); + g_string_append_c(buf, 'r'); } if (dec->pred & rv_fence_w) { - append(buf, "w", buflen); + g_string_append_c(buf, 'w'); } break; case 's': if (dec->succ & rv_fence_i) { - append(buf, "i", buflen); + g_string_append_c(buf, 'i'); } if (dec->succ & rv_fence_o) { - append(buf, "o", buflen); + g_string_append_c(buf, 'o'); } if (dec->succ & rv_fence_r) { - append(buf, "r", buflen); + g_string_append_c(buf, 'r'); } if (dec->succ & rv_fence_w) { - append(buf, "w", buflen); + g_string_append_c(buf, 'w'); } break; case '\t': - while (strlen(buf) < tab) { - append(buf, " ", buflen); + while (buf->len < tab) { + g_string_append_c(buf, ' '); } break; case 'A': if (dec->aq) { - append(buf, ".aq", buflen); + g_string_append(buf, ".aq"); } break; case 'R': if (dec->rl) { - append(buf, ".rl", buflen); + g_string_append(buf, ".rl"); } break; case 'l': - append(buf, ",v0", buflen); + g_string_append(buf, ",v0"); break; case 'm': if (dec->vm == 0) { - append(buf, ",v0.t", buflen); + g_string_append(buf, ",v0.t"); } break; case 'D': - append(buf, rv_vreg_name_sym[dec->rd], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rd]); break; case 'E': - append(buf, rv_vreg_name_sym[dec->rs1], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rs1]); break; case 'F': - append(buf, rv_vreg_name_sym[dec->rs2], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rs2]); break; case 'G': - append(buf, rv_vreg_name_sym[dec->rs3], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rs3]); break; case 'v': { - char nbuf[32] = {0}; const int sew = 1 << (((dec->vzimm >> 3) & 0b111) + 3); - sprintf(nbuf, "%d", sew); const int lmul = dec->vzimm & 0b11; const int flmul = (dec->vzimm >> 2) & 1; const char *vta = (dec->vzimm >> 6) & 1 ? "ta" : "tu"; const char *vma = (dec->vzimm >> 7) & 1 ? "ma" : "mu"; - append(buf, "e", buflen); - append(buf, nbuf, buflen); - append(buf, ",m", buflen); + + g_string_append_printf(buf, "e%d,m", sew); if (flmul) { switch (lmul) { case 3: - sprintf(nbuf, "f2"); + g_string_append(buf, "f2"); break; case 2: - sprintf(nbuf, "f4"); + g_string_append(buf, "f4"); break; case 1: - sprintf(nbuf, "f8"); - break; + g_string_append(buf, "f8"); + break; } - append(buf, nbuf, buflen); } else { - sprintf(nbuf, "%d", 1 << lmul); - append(buf, nbuf, buflen); + g_string_append_printf(buf, "%d", 1 << lmul); } - append(buf, ",", buflen); - append(buf, vta, buflen); - append(buf, ",", buflen); - append(buf, vma, buflen); + g_string_append_c(buf, ','); + g_string_append(buf, vta); + g_string_append_c(buf, ','); + g_string_append(buf, vma); break; } case 'x': { switch (dec->rlist) { case 4: - snprintf(tmp, sizeof(tmp), "{ra}"); + g_string_append(buf, "{ra}"); break; case 5: - snprintf(tmp, sizeof(tmp), "{ra, s0}"); + g_string_append(buf, "{ra, s0}"); break; case 15: - snprintf(tmp, sizeof(tmp), "{ra, s0-s11}"); + g_string_append(buf, "{ra, s0-s11}"); break; default: - snprintf(tmp, sizeof(tmp), "{ra, s0-s%d}", dec->rlist - 5); + g_string_append_printf(buf, "{ra, s0-s%d}", dec->rlist - 5); break; } - append(buf, tmp, buflen); break; } case 'h': - append(buf, rv_fli_name_const[dec->imm], buflen); + g_string_append(buf, rv_fli_name_const[dec->imm]); break; default: break; } fmt++; } + + return buf; } /* lift instruction to pseudo-instruction */ @@ -5171,9 +5148,8 @@ static void decode_inst_decompress(rv_decode *dec, rv_isa isa) /* disassemble instruction */ -static void -disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, - RISCVCPUConfig *cfg) +static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, + RISCVCPUConfig *cfg) { rv_decode dec = { 0 }; dec.pc = pc; @@ -5220,7 +5196,7 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, decode_inst_operands(&dec, isa); decode_inst_decompress(&dec, isa); decode_inst_lift_pseudo(&dec); - format_inst(buf, buflen, 24, &dec); + return format_inst(24, &dec); } #define INST_FMT_2 "%04" PRIx64 " " @@ -5231,7 +5207,6 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, static int print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) { - char buf[128] = { 0 }; bfd_byte packet[2]; rv_inst inst = 0; size_t len = 2; @@ -5272,9 +5247,9 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) } } - disasm_inst(buf, sizeof(buf), isa, memaddr, inst, - (RISCVCPUConfig *)info->target_info); - (*info->fprintf_func)(info->stream, "%s", buf); + g_autoptr(GString) str = + disasm_inst(isa, memaddr, inst, (RISCVCPUConfig *)info->target_info); + (*info->fprintf_func)(info->stream, "%s", str->str); return len; } From fe43cc5bde32d8bba930242f34ad55002ea4819e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 28 May 2024 16:20:53 +0800 Subject: [PATCH 1253/2884] tests/libqos: Add loongarch virt machine node Add loongarch virt machine to the graph. It is a modified copy of the existing riscv virtmachine in riscv-virt-machine.c It contains a generic-pcihost controller, and an extra function loongarch_config_qpci_bus() to configure GPEX pci host controller information, such as ecam and pio_base addresses. Also hotplug handle checking about TYPE_VIRTIO_IOMMU_PCI device is added on loongarch virt machine, since virtio_mmu_pci device requires it. Signed-off-by: Bibo Mao Acked-by: Thomas Huth Message-Id: <20240528082053.938564-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 2 + tests/qtest/libqos/loongarch-virt-machine.c | 114 ++++++++++++++++++++ tests/qtest/libqos/meson.build | 1 + 3 files changed, 117 insertions(+) create mode 100644 tests/qtest/libqos/loongarch-virt-machine.c diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 3e6e93edf3..2d7f718570 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -45,6 +45,7 @@ #include "sysemu/tpm.h" #include "sysemu/block-backend.h" #include "hw/block/flash.h" +#include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms, @@ -1213,6 +1214,7 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || memhp_type_supported(dev)) { return HOTPLUG_HANDLER(machine); } diff --git a/tests/qtest/libqos/loongarch-virt-machine.c b/tests/qtest/libqos/loongarch-virt-machine.c new file mode 100644 index 0000000000..c12089c015 --- /dev/null +++ b/tests/qtest/libqos/loongarch-virt-machine.c @@ -0,0 +1,114 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * 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 + */ + +#include "qemu/osdep.h" +#include "../libqtest.h" +#include "qemu/module.h" +#include "libqos-malloc.h" +#include "qgraph.h" +#include "virtio-mmio.h" +#include "generic-pcihost.h" +#include "hw/pci/pci_regs.h" + +#define LOONGARCH_PAGE_SIZE 0x1000 +#define LOONGARCH_VIRT_RAM_ADDR 0x100000 +#define LOONGARCH_VIRT_RAM_SIZE 0xFF00000 + +#define LOONGARCH_VIRT_PIO_BASE 0x18000000 +#define LOONGARCH_VIRT_PCIE_PIO_OFFSET 0x4000 +#define LOONGARCH_VIRT_PCIE_PIO_LIMIT 0x10000 +#define LOONGARCH_VIRT_PCIE_ECAM_BASE 0x20000000 +#define LOONGARCH_VIRT_PCIE_MMIO32_BASE 0x40000000 +#define LOONGARCH_VIRT_PCIE_MMIO32_LIMIT 0x80000000 + +typedef struct QVirtMachine QVirtMachine; + +struct QVirtMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QVirtioMMIODevice virtio_mmio; + QGenericPCIHost bridge; +}; + +static void virt_destructor(QOSGraphObject *obj) +{ + QVirtMachine *machine = (QVirtMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *virt_get_driver(void *object, const char *interface) +{ + QVirtMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in loongarch/virtio\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *virt_get_device(void *obj, const char *device) +{ + QVirtMachine *machine = obj; + if (!g_strcmp0(device, "generic-pcihost")) { + return &machine->bridge.obj; + } else if (!g_strcmp0(device, "virtio-mmio")) { + return &machine->virtio_mmio.obj; + } + + fprintf(stderr, "%s not present in loongarch/virt\n", device); + g_assert_not_reached(); +} + +static void loongarch_config_qpci_bus(QGenericPCIBus *qpci) +{ + qpci->gpex_pio_base = LOONGARCH_VIRT_PIO_BASE; + qpci->bus.pio_alloc_ptr = LOONGARCH_VIRT_PCIE_PIO_OFFSET; + qpci->bus.pio_limit = LOONGARCH_VIRT_PCIE_PIO_LIMIT; + qpci->bus.mmio_alloc_ptr = LOONGARCH_VIRT_PCIE_MMIO32_BASE; + qpci->bus.mmio_limit = LOONGARCH_VIRT_PCIE_MMIO32_LIMIT; + qpci->ecam_alloc_ptr = LOONGARCH_VIRT_PCIE_ECAM_BASE; +} + +static void *qos_create_machine_loongarch_virt(QTestState *qts) +{ + QVirtMachine *machine = g_new0(QVirtMachine, 1); + + alloc_init(&machine->alloc, 0, + LOONGARCH_VIRT_RAM_ADDR, + LOONGARCH_VIRT_RAM_ADDR + LOONGARCH_VIRT_RAM_SIZE, + LOONGARCH_PAGE_SIZE); + + qos_create_generic_pcihost(&machine->bridge, qts, &machine->alloc); + loongarch_config_qpci_bus(&machine->bridge.pci); + + machine->obj.get_device = virt_get_device; + machine->obj.get_driver = virt_get_driver; + machine->obj.destructor = virt_destructor; + return machine; +} + +static void virt_machine_register_nodes(void) +{ + qos_node_create_machine_args("loongarch64/virt", + qos_create_machine_loongarch_virt, + " -cpu la464"); + qos_node_contains("loongarch64/virt", "generic-pcihost", NULL); +} + +libqos_init(virt_machine_register_nodes); diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 3aed6efcb8..558eb4c24b 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -61,6 +61,7 @@ libqos_srcs = files( 'ppc64_pseries-machine.c', 'x86_64_pc-machine.c', 'riscv-virt-machine.c', + 'loongarch-virt-machine.c', ) if have_virtfs From a73f7a00eea15c75fe9cfbeeaff5228f5ee24b61 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 28 May 2024 16:21:55 +0800 Subject: [PATCH 1254/2884] tests/qtest: Add numa test for loongarch system Add numa test case for loongarch system, it passes to run with command "make check-qtest". Signed-off-by: Bibo Mao Acked-by: Thomas Huth Tested-by: Song Gao Message-Id: <20240528082155.938586-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- tests/qtest/meson.build | 2 +- tests/qtest/numa-test.c | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index b98fae6a6d..12792948ff 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -140,7 +140,7 @@ qtests_hppa = ['boot-serial-test'] + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) qtests_loongarch64 = qtests_filter + \ - ['boot-serial-test'] + ['boot-serial-test', 'numa-test'] qtests_m68k = ['boot-serial-test'] + \ qtests_filter diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 7aa262dbb9..5518f6596b 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -265,6 +265,54 @@ static void aarch64_numa_cpu(const void *data) qtest_quit(qts); } +static void loongarch64_numa_cpu(const void *data) +{ + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-machine " + "smp.cpus=2,smp.sockets=2,smp.cores=1,smp.threads=1 " + "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " + "-numa cpu,node-id=0,socket-id=1,core-id=0,thread-id=0 " + "-numa cpu,node-id=1,socket-id=0,core-id=0,thread-id=0"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t socket, core, thread, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); + g_assert(qdict_haskey(props, "thread-id")); + thread = qdict_get_int(props, "thread-id"); + + if (socket == 0 && core == 0 && thread == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1 && core == 0 && thread == 0) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); +} + static void pc_dynamic_cpu_cfg(const void *data) { QObject *e; @@ -593,6 +641,11 @@ int main(int argc, char **argv) aarch64_numa_cpu); } + if (!strcmp(arch, "loongarch64")) { + qtest_add_data_func("/numa/loongarch64/cpu/explicit", args, + loongarch64_numa_cpu); + } + out: return g_test_run(); } From dc6f37eb957b2888fea78bda9da0cf0840dd795f Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 28 May 2024 16:38:53 +0800 Subject: [PATCH 1255/2884] hw/intc/loongarch_extioi: Add extioi virt extension definition On LoongArch, IRQs can be routed to four vcpus with hardware extended IRQ model. This patch adds the virt extension definition so that the IRQ can route to 256 vcpus. 1.Extended IRQ model: | +-----------+ +-------------|--------+ +-----------+ | IPI/Timer | --> | CPUINTC(0-3)|(4-255) | <-- | IPI/Timer | +-----------+ +-------------|--------+ +-----------+ ^ | | +---------+ | EIOINTC | +---------+ ^ ^ | | +---------+ +---------+ | PCH-PIC | | PCH-MSI | +---------+ +---------+ ^ ^ ^ | | | +--------+ +---------+ +---------+ | UARTs | | Devices | | Devices | +--------+ +---------+ +---------+ 2.Virt extended IRQ model: +-----+ +---------------+ +-------+ | IPI |--> | CPUINTC(0-255)| <-- | Timer | +-----+ +---------------+ +-------+ ^ | +-----------+ | V-EIOINTC | +-----------+ ^ ^ | | +---------+ +---------+ | PCH-PIC | | PCH-MSI | +---------+ +---------+ ^ ^ ^ | | | +--------+ +---------+ +---------+ | UARTs | | Devices | | Devices | +--------+ +---------+ +---------+ Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240528083855.1912757-2-gaosong@loongson.cn> --- hw/intc/loongarch_extioi.c | 88 ++++++++++++++++++++++++++++-- hw/loongarch/virt.c | 60 +++++++++++++------- include/hw/intc/loongarch_extioi.h | 21 +++++++ 3 files changed, 146 insertions(+), 23 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 0b358548eb..1e8e0114dc 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -143,10 +143,13 @@ static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, for (i = 0; i < 4; i++) { cpu = val & 0xff; - cpu = ctz32(cpu); - cpu = (cpu >= 4) ? 0 : cpu; val = val >> 8; + if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { + cpu = ctz32(cpu); + cpu = (cpu >= 4) ? 0 : cpu; + } + if (s->sw_coremap[irq + i] == cpu) { continue; } @@ -265,6 +268,61 @@ static const MemoryRegionOps extioi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + + switch (addr) { + case EXTIOI_VIRT_FEATURES: + *data = s->features; + break; + case EXTIOI_VIRT_CONFIG: + *data = s->status; + break; + default: + g_assert_not_reached(); + } + + return MEMTX_OK; +} + +static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + + switch (addr) { + case EXTIOI_VIRT_FEATURES: + return MEMTX_ACCESS_ERROR; + + case EXTIOI_VIRT_CONFIG: + /* + * extioi features can only be set at disabled status + */ + if ((s->status & BIT(EXTIOI_ENABLE)) && val) { + return MEMTX_ACCESS_ERROR; + } + + s->status = val & s->features; + break; + default: + g_assert_not_reached(); + } + return MEMTX_OK; +} + +static const MemoryRegionOps extioi_virt_ops = { + .read_with_attrs = extioi_virt_readw, + .write_with_attrs = extioi_virt_writew, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void loongarch_extioi_realize(DeviceState *dev, Error **errp) { LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev); @@ -284,6 +342,16 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, s, "extioi_system_mem", 0x900); sysbus_init_mmio(sbd, &s->extioi_system_mem); + + if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { + memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops, + s, "extioi_virt", EXTIOI_VIRT_SIZE); + sysbus_init_mmio(sbd, &s->virt_extend); + s->features |= EXTIOI_VIRT_HAS_FEATURES; + } else { + s->status |= BIT(EXTIOI_ENABLE); + } + s->cpu = g_new0(ExtIOICore, s->num_cpu); if (s->cpu == NULL) { error_setg(errp, "Memory allocation for ExtIOICore faile"); @@ -304,6 +372,13 @@ static void loongarch_extioi_finalize(Object *obj) g_free(s->cpu); } +static void loongarch_extioi_reset(DeviceState *d) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(d); + + s->status = 0; +} + static int vmstate_extioi_post_load(void *opaque, int version_id) { LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); @@ -333,8 +408,8 @@ static const VMStateDescription vmstate_extioi_core = { static const VMStateDescription vmstate_loongarch_extioi = { .name = TYPE_LOONGARCH_EXTIOI, - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .post_load = vmstate_extioi_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), @@ -347,12 +422,16 @@ static const VMStateDescription vmstate_loongarch_extioi = { VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu, vmstate_extioi_core, ExtIOICore), + VMSTATE_UINT32(features, LoongArchExtIOI), + VMSTATE_UINT32(status, LoongArchExtIOI), VMSTATE_END_OF_LIST() } }; static Property extioi_properties[] = { DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), + DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features, + EXTIOI_HAS_VIRT_EXTENSION, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -361,6 +440,7 @@ static void loongarch_extioi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = loongarch_extioi_realize; + dc->reset = loongarch_extioi_reset; device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_loongarch_extioi; } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 2d7f718570..2b5ae45939 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -718,25 +718,47 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; /* - * The connection of interrupts: - * +-----+ +---------+ +-------+ - * | IPI |--> | CPUINTC | <-- | Timer | - * +-----+ +---------+ +-------+ - * ^ - * | - * +---------+ - * | EIOINTC | - * +---------+ - * ^ ^ - * | | - * +---------+ +---------+ - * | PCH-PIC | | PCH-MSI | - * +---------+ +---------+ - * ^ ^ ^ - * | | | - * +--------+ +---------+ +---------+ - * | UARTs | | Devices | | Devices | - * +--------+ +---------+ +---------+ + * Extended IRQ model. + * | + * +-----------+ +-------------|--------+ +-----------+ + * | IPI/Timer | --> | CPUINTC(0-3)|(4-255) | <-- | IPI/Timer | + * +-----------+ +-------------|--------+ +-----------+ + * ^ | + * | + * +---------+ + * | EIOINTC | + * +---------+ + * ^ ^ + * | | + * +---------+ +---------+ + * | PCH-PIC | | PCH-MSI | + * +---------+ +---------+ + * ^ ^ ^ + * | | | + * +--------+ +---------+ +---------+ + * | UARTs | | Devices | | Devices | + * +--------+ +---------+ +---------+ + * + * Virt extended IRQ model. + * + * +-----+ +---------------+ +-------+ + * | IPI |--> | CPUINTC(0-255)| <-- | Timer | + * +-----+ +---------------+ +-------+ + * ^ + * | + * +-----------+ + * | V-EIOINTC | + * +-----------+ + * ^ ^ + * | | + * +---------+ +---------+ + * | PCH-PIC | | PCH-MSI | + * +---------+ +---------+ + * ^ ^ ^ + * | | | + * +--------+ +---------+ +---------+ + * | UARTs | | Devices | | Devices | + * +--------+ +---------+ +---------+ */ /* Create IPI device */ diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 410c6e1121..eccc2e0d18 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -41,6 +41,24 @@ #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) #define EXTIOI_SIZE 0x800 +#define EXTIOI_VIRT_BASE (0x40000000) +#define EXTIOI_VIRT_SIZE (0x1000) +#define EXTIOI_VIRT_FEATURES (0x0) +#define EXTIOI_HAS_VIRT_EXTENSION (0) +#define EXTIOI_HAS_ENABLE_OPTION (1) +#define EXTIOI_HAS_INT_ENCODE (2) +#define EXTIOI_HAS_CPU_ENCODE (3) +#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ + | BIT(EXTIOI_HAS_ENABLE_OPTION) \ + | BIT(EXTIOI_HAS_INT_ENCODE) \ + | BIT(EXTIOI_HAS_CPU_ENCODE)) +#define EXTIOI_VIRT_CONFIG (0x4) +#define EXTIOI_ENABLE (1) +#define EXTIOI_ENABLE_INT_ENCODE (2) +#define EXTIOI_ENABLE_CPU_ENCODE (3) +#define EXTIOI_VIRT_COREMAP_START (0x40) +#define EXTIOI_VIRT_COREMAP_END (0x240) + typedef struct ExtIOICore { uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); @@ -52,6 +70,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) struct LoongArchExtIOI { SysBusDevice parent_obj; uint32_t num_cpu; + uint32_t features; + uint32_t status; /* hardware state */ uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; @@ -65,5 +85,6 @@ struct LoongArchExtIOI { qemu_irq irq[EXTIOI_IRQS]; ExtIOICore *cpu; MemoryRegion extioi_system_mem; + MemoryRegion virt_extend; }; #endif /* LOONGARCH_EXTIOI_H */ From f2e61edb2946b0473ad634ed8ec401954ace5c5a Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 28 May 2024 16:38:54 +0800 Subject: [PATCH 1256/2884] hw/loongarch/virt: Use MemTxAttrs interface for misc ops Use MemTxAttrs interface read_with_attrs/write_with_attrs for virt_iocsr_misc_ops. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240528083855.1912757-3-gaosong@loongson.cn> --- hw/loongarch/virt.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 2b5ae45939..0cef33a904 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -900,37 +900,49 @@ static void virt_firmware_init(LoongArchVirtMachineState *lvms) } -static void virt_iocsr_misc_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { + return MEMTX_OK; } -static uint64_t virt_iocsr_misc_read(void *opaque, hwaddr addr, unsigned size) +static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) { - uint64_t ret; + uint64_t ret = 0; switch (addr) { case VERSION_REG: - return 0x11ULL; + ret = 0x11ULL; + break; case FEATURE_REG: ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI); if (kvm_enabled()) { ret |= BIT(IOCSRF_VM); } - return ret; + break; case VENDOR_REG: - return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ + ret = 0x6e6f73676e6f6f4cULL; /* "Loongson" */ + break; case CPUNAME_REG: - return 0x303030354133ULL; /* "3A5000" */ + ret = 0x303030354133ULL; /* "3A5000" */ + break; case MISC_FUNC_REG: - return BIT_ULL(IOCSRM_EXTIOI_EN); + ret = BIT_ULL(IOCSRM_EXTIOI_EN); + break; + default: + g_assert_not_reached(); } - return 0ULL; + + *data = ret; + return MEMTX_OK; } static const MemoryRegionOps virt_iocsr_misc_ops = { - .read = virt_iocsr_misc_read, - .write = virt_iocsr_misc_write, + .read_with_attrs = virt_iocsr_misc_read, + .write_with_attrs = virt_iocsr_misc_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, From 2b284fa9ea17171b11e9112daf19801b90b690a6 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 28 May 2024 16:38:55 +0800 Subject: [PATCH 1257/2884] hw/loongarch/virt: Enable extioi virt extension This patch adds a new board attribute 'v-eiointc'. A value of true enables the virt extended I/O interrupt controller. VMs working in kvm mode have 'v-eiointc' enabled by default. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-Id: <20240528083855.1912757-4-gaosong@loongson.cn> --- hw/loongarch/virt.c | 88 +++++++++++++++++++++++++++++++++++-- include/hw/loongarch/virt.h | 1 + target/loongarch/cpu.h | 1 + 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 0cef33a904..66cef201ab 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -11,6 +11,7 @@ #include "hw/boards.h" #include "hw/char/serial.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" @@ -48,6 +49,31 @@ #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" +static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->veiointc == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto veiointc = lvms->veiointc; + + visit_type_OnOffAuto(v, name, &veiointc, errp); +} + +static void virt_set_veiointc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lvms->veiointc, errp); +} + static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms, const char *name, const char *alias_prop_name) @@ -790,9 +816,16 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Create EXTIOI device */ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); + if (virt_is_veiointc_enabled(lvms)) { + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); + } sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); + if (virt_is_veiointc_enabled(lvms)) { + memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); + } /* * connect ext irq to the cpu irq @@ -899,11 +932,37 @@ static void virt_firmware_init(LoongArchVirtMachineState *lvms) } } - static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size, MemTxAttrs attrs) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); + uint64_t features; + + switch (addr) { + case MISC_FUNC_REG: + if (!virt_is_veiointc_enabled(lvms)) { + return MEMTX_OK; + } + + features = address_space_ldl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + attrs, NULL); + if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) { + features |= BIT(EXTIOI_ENABLE); + } + if (val & BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE)) { + features |= BIT(EXTIOI_ENABLE_INT_ENCODE); + } + + address_space_stl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + features, attrs, NULL); + break; + default: + g_assert_not_reached(); + } + return MEMTX_OK; } @@ -911,7 +970,9 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); uint64_t ret = 0; + int features; switch (addr) { case VERSION_REG: @@ -930,7 +991,20 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, ret = 0x303030354133ULL; /* "3A5000" */ break; case MISC_FUNC_REG: - ret = BIT_ULL(IOCSRM_EXTIOI_EN); + if (!virt_is_veiointc_enabled(lvms)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + break; + } + + features = address_space_ldl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + attrs, NULL); + if (features & BIT(EXTIOI_ENABLE)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + } + if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE); + } break; default: g_assert_not_reached(); @@ -1152,6 +1226,9 @@ static void virt_initfn(Object *obj) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + if (tcg_enabled()) { + lvms->veiointc = ON_OFF_AUTO_OFF; + } lvms->acpi = ON_OFF_AUTO_AUTO; lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); @@ -1338,6 +1415,11 @@ static void virt_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, "acpi", "Enable ACPI"); + object_class_property_add(oc, "v-eiointc", "OnOffAuto", + virt_get_veiointc, virt_set_veiointc, + NULL, NULL); + object_class_property_set_description(oc, "v-eiointc", + "Enable Virt Extend I/O Interrupt Controller."); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 2c4f5cf9c8..8fdfacf268 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -50,6 +50,7 @@ struct LoongArchVirtMachineState { Notifier machine_done; Notifier powerdown_notifier; OnOffAuto acpi; + OnOffAuto veiointc; char *oem_id; char *oem_table_id; DeviceState *acpi_ged; diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 41b8e6d96d..6c41fafb70 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -36,6 +36,7 @@ #define CPUNAME_REG 0x20 #define MISC_FUNC_REG 0x420 #define IOCSRM_EXTIOI_EN 48 +#define IOCSRM_EXTIOI_INT_ENCODE 49 #define IOCSR_MEM_SIZE 0x428 From 78f932ea1f7b3b9b0ac628dc2a91281318fe51fa Mon Sep 17 00:00:00 2001 From: lanyanzhi Date: Tue, 4 Jun 2024 15:38:31 +0800 Subject: [PATCH 1258/2884] target/loongarch: fix a wrong print in cpu dump description: loongarch_cpu_dump_state() want to dump all loongarch cpu state registers, but there is a tiny typographical error when printing "PRCFG2". Cc: qemu-stable@nongnu.org Signed-off-by: lanyanzhi Reviewed-by: Richard Henderson Reviewed-by: Song Gao Message-Id: <20240604073831.666690-1-lanyanzhi22b@ict.ac.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index b5c1ec94af..270f711f11 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -707,7 +707,7 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY); qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 "," " PRCFG3=%016" PRIx64 "\n", - env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3); + env->CSR_PRCFG1, env->CSR_PRCFG2, env->CSR_PRCFG3); qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY); qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV); qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA); From 421a22ef8ec19bc6c1859a905142faeeaa7c35b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:17 +0100 Subject: [PATCH 1259/2884] ci: remove centos-steam-8 customer runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This broke since eef0bae3a7 (migration: Remove block migration) but even after that was addressed it still fails to complete. As it will shortly be EOL lets to remove the runner definition and the related ansible setup bits. We still have centos9 docker images build and test. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-2-alex.bennee@linaro.org> --- .gitlab-ci.d/custom-runners.yml | 1 - .../custom-runners/centos-stream-8-x86_64.yml | 24 --- docs/devel/ci-jobs.rst.inc | 7 - .../org.centos/stream/8/build-environment.yml | 82 -------- .../ci/org.centos/stream/8/x86_64/configure | 198 ------------------ .../org.centos/stream/8/x86_64/test-avocado | 65 ------ scripts/ci/org.centos/stream/README | 17 -- 7 files changed, 394 deletions(-) delete mode 100644 .gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml delete mode 100644 scripts/ci/org.centos/stream/8/build-environment.yml delete mode 100755 scripts/ci/org.centos/stream/8/x86_64/configure delete mode 100755 scripts/ci/org.centos/stream/8/x86_64/test-avocado delete mode 100644 scripts/ci/org.centos/stream/README diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 29e52df283..1aa3c60efe 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -32,4 +32,3 @@ include: - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml' - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml' - - local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml' diff --git a/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml b/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml deleted file mode 100644 index 367424db78..0000000000 --- a/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml +++ /dev/null @@ -1,24 +0,0 @@ -# All centos-stream-8 jobs should run successfully in an environment -# setup by the scripts/ci/setup/stream/8/build-environment.yml task -# "Installation of extra packages to build QEMU" - -centos-stream-8-x86_64: - extends: .custom_runner_template - allow_failure: true - needs: [] - stage: build - tags: - - centos_stream_8 - - x86_64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$CENTOS_STREAM_8_x86_64_RUNNER_AVAILABLE" - before_script: - - JOBS=$(expr $(nproc) + 1) - script: - - mkdir build - - cd build - - ../scripts/ci/org.centos/stream/8/x86_64/configure - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make -j"$JOBS" - - make NINJA=":" check check-avocado diff --git a/docs/devel/ci-jobs.rst.inc b/docs/devel/ci-jobs.rst.inc index be06322279..3756bbe355 100644 --- a/docs/devel/ci-jobs.rst.inc +++ b/docs/devel/ci-jobs.rst.inc @@ -182,13 +182,6 @@ If you've got access to an IBM Z host that can be used as a gitlab-CI runner, you can set this variable to enable the tests that require this kind of host. The runner should be tagged with "s390x". -CENTOS_STREAM_8_x86_64_RUNNER_AVAILABLE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you've got access to a CentOS Stream 8 x86_64 host that can be -used as a gitlab-CI runner, you can set this variable to enable the -tests that require this kind of host. The runner should be tagged with -both "centos_stream_8" and "x86_64". - CCACHE_DISABLE ~~~~~~~~~~~~~~ The jobs are configured to use "ccache" by default since this typically diff --git a/scripts/ci/org.centos/stream/8/build-environment.yml b/scripts/ci/org.centos/stream/8/build-environment.yml deleted file mode 100644 index 1ead77e2cb..0000000000 --- a/scripts/ci/org.centos/stream/8/build-environment.yml +++ /dev/null @@ -1,82 +0,0 @@ ---- -- name: Installation of extra packages to build QEMU - hosts: all - tasks: - - name: Extra check for CentOS Stream 8 - lineinfile: - path: /etc/redhat-release - line: CentOS Stream release 8 - state: present - check_mode: yes - register: centos_stream_8 - - - name: Enable EPEL repo on CentOS Stream 8 - dnf: - name: - - epel-release - state: present - when: - - centos_stream_8 - - - name: Enable PowerTools repo on CentOS Stream 8 - ini_file: - path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo - section: powertools - option: enabled - value: "1" - when: - - centos_stream_8 - - - name: Install basic packages to build QEMU on CentOS Stream 8 - dnf: - name: - - bzip2 - - bzip2-devel - - capstone-devel - - dbus-daemon - - device-mapper-multipath-devel - - diffutils - - gcc - - gcc-c++ - - genisoimage - - gettext - - git - - glib2-devel - - glusterfs-api-devel - - gnutls-devel - - libaio-devel - - libcap-ng-devel - - libcurl-devel - - libepoxy-devel - - libfdt-devel - - libgcrypt-devel - - libiscsi-devel - - libpmem-devel - - librados-devel - - librbd-devel - - libseccomp-devel - - libslirp-devel - - libssh-devel - - libxkbcommon-devel - - lzo-devel - - make - - mesa-libEGL-devel - - nettle-devel - - ninja-build - - nmap-ncat - - numactl-devel - - pixman-devel - - python38 - - python3-sphinx - - rdma-core-devel - - redhat-rpm-config - - snappy-devel - - spice-glib-devel - - spice-server-devel - - systemd-devel - - systemtap-sdt-devel - - tar - - zlib-devel - state: present - when: - - centos_stream_8 diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure deleted file mode 100755 index 868db665f6..0000000000 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ /dev/null @@ -1,198 +0,0 @@ -#!/bin/sh -e -# -# Configuration for QEMU based on CentOS Stream 8 x86_64 builds -# -# The "configure" command line is based on: -# -# https://git.centos.org/rpms/qemu-kvm/blob/c8s-stream-rhel/f/SPECS/qemu-kvm.spec -# -# But, because the SPEC file contains a number of conditionals and -# variable and expansions only available at RPM build time, this version -# was initially generated from an actual RPM build on an x86_64 platform. -# -# From that initial version, options that are required or are a -# consequence of non-upstream patches have been adapted. One example -# is "--without-default-devices" which is *not* present here, given -# that patches adding downstream specific devices are not available. -# -../configure \ ---python=/usr/bin/python3.8 \ ---prefix="/usr" \ ---libdir="/usr/lib64" \ ---datadir="/usr/share" \ ---sysconfdir="/etc" \ ---interp-prefix=/usr/qemu-%M \ ---localstatedir="/var" \ ---docdir="/usr/share/doc" \ ---libexecdir="/usr/libexec" \ ---extra-ldflags="-Wl,--build-id -Wl,-z,relro -Wl,-z,now" \ ---extra-cflags="-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection" \ ---with-suffix="qemu-kvm" \ ---firmwarepath=/usr/share/qemu-firmware \ ---target-list="x86_64-softmmu" \ ---block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \ ---audio-drv-list="" \ ---block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \ ---with-coroutine=ucontext \ ---tls-priority=@QEMU,SYSTEM \ ---disable-af-xdp \ ---disable-attr \ ---disable-auth-pam \ ---disable-avx2 \ ---disable-avx512f \ ---disable-bochs \ ---disable-bpf \ ---disable-brlapi \ ---disable-bsd-user \ ---disable-bzip2 \ ---disable-cap-ng \ ---disable-capstone \ ---disable-cfi \ ---disable-cfi-debug \ ---disable-cloop \ ---disable-cocoa \ ---disable-coroutine-pool \ ---disable-crypto-afalg \ ---disable-curl \ ---disable-curses \ ---disable-debug-info \ ---disable-debug-mutex \ ---disable-debug-tcg \ ---disable-dmg \ ---disable-docs \ ---disable-fuse \ ---disable-fuse-lseek \ ---disable-gcrypt \ ---disable-gio \ ---disable-glusterfs \ ---disable-gnutls \ ---disable-gtk \ ---disable-guest-agent \ ---disable-guest-agent-msi \ ---disable-hvf \ ---disable-iconv \ ---disable-kvm \ ---disable-libdaxctl \ ---disable-libiscsi \ ---disable-libnfs \ ---disable-libpmem \ ---disable-libssh \ ---disable-libudev \ ---disable-libusb \ ---disable-linux-aio \ ---disable-linux-io-uring \ ---disable-linux-user \ ---disable-live-block-migration \ ---disable-lto \ ---disable-lzfse \ ---disable-lzo \ ---disable-malloc-trim \ ---disable-membarrier \ ---disable-modules \ ---disable-module-upgrades \ ---disable-mpath \ ---disable-multiprocess \ ---disable-netmap \ ---disable-nettle \ ---disable-numa \ ---disable-nvmm \ ---disable-opengl \ ---disable-parallels \ ---disable-pie \ ---disable-qcow1 \ ---disable-qed \ ---disable-qom-cast-debug \ ---disable-rbd \ ---disable-rdma \ ---disable-replication \ ---disable-rng-none \ ---disable-safe-stack \ ---disable-sanitizers \ ---disable-sdl \ ---disable-sdl-image \ ---disable-seccomp \ ---disable-slirp-smbd \ ---disable-smartcard \ ---disable-snappy \ ---disable-sparse \ ---disable-spice \ ---disable-strip \ ---disable-system \ ---disable-tcg \ ---disable-tools \ ---disable-tpm \ ---disable-u2f \ ---disable-usb-redir \ ---disable-user \ ---disable-vde \ ---disable-vdi \ ---disable-vhost-crypto \ ---disable-vhost-kernel \ ---disable-vhost-net \ ---disable-vhost-user \ ---disable-vhost-user-blk-server \ ---disable-vhost-vdpa \ ---disable-virglrenderer \ ---disable-virtfs \ ---disable-vnc \ ---disable-vnc-jpeg \ ---disable-png \ ---disable-vnc-sasl \ ---disable-vte \ ---disable-vvfat \ ---disable-werror \ ---disable-whpx \ ---disable-xen \ ---disable-xen-pci-passthrough \ ---disable-xkbcommon \ ---disable-zstd \ ---enable-attr \ ---enable-avx2 \ ---enable-cap-ng \ ---enable-capstone \ ---enable-coroutine-pool \ ---enable-curl \ ---enable-debug-info \ ---enable-docs \ ---enable-fdt \ ---enable-gcrypt \ ---enable-glusterfs \ ---enable-gnutls \ ---enable-guest-agent \ ---enable-iconv \ ---enable-kvm \ ---enable-libiscsi \ ---enable-libpmem \ ---enable-libssh \ ---enable-libusb \ ---enable-libudev \ ---enable-linux-aio \ ---enable-lzo \ ---enable-malloc-trim \ ---enable-modules \ ---enable-mpath \ ---enable-numa \ ---enable-opengl \ ---enable-pie \ ---enable-rbd \ ---enable-rdma \ ---enable-seccomp \ ---enable-snappy \ ---enable-smartcard \ ---enable-spice \ ---enable-system \ ---enable-tcg \ ---enable-tools \ ---enable-tpm \ ---enable-trace-backends=dtrace \ ---enable-usb-redir \ ---enable-vhost-kernel \ ---enable-vhost-net \ ---enable-vhost-user \ ---enable-vhost-user-blk-server \ ---enable-vhost-vdpa \ ---enable-vnc \ ---enable-png \ ---enable-vnc-sasl \ ---enable-werror \ ---enable-xkbcommon diff --git a/scripts/ci/org.centos/stream/8/x86_64/test-avocado b/scripts/ci/org.centos/stream/8/x86_64/test-avocado deleted file mode 100755 index 73e7a1a312..0000000000 --- a/scripts/ci/org.centos/stream/8/x86_64/test-avocado +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -e -# -# Runs a previously vetted list of tests, either marked explicitly for -# KVM and x86_64, or tests that are generic enough to be valid for all -# targets. Such a test list can be generated with: -# -# ./pyvenv/bin/avocado list --filter-by-tags-include-empty \ -# --filter-by-tags-include-empty-key -t accel:kvm,arch:x86_64 \ -# tests/avocado/ -# -# This is almost the complete list of avocado based tests available at -# the time this was compile, with the following exceptions: -# -# * Require machine type "x-remote": -# - tests/avocado/multiprocess.py:Multiprocess.test_multiprocess_x86_64 -# -# * Requires display type "egl-headless": -# - tests/avocado/virtio-gpu.py:VirtioGPUx86.test_virtio_vga_virgl -# - tests/avocado/virtio-gpu.py:VirtioGPUx86.test_vhost_user_vga_virgl -# -# * Test is marked (unconditionally) to be skipped: -# - tests/avocado/virtio_check_params.py:VirtioMaxSegSettingsCheck.test_machine_types -# -make get-vm-images -./pyvenv/bin/avocado run \ - --job-results-dir=tests/results/ \ - tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_i440fx_kvm \ - tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_q35_kvm \ - tests/avocado/boot_linux_console.py:BootLinuxConsole.test_x86_64_pc \ - tests/avocado/cpu_queries.py:QueryCPUModelExpansion.test \ - tests/avocado/empty_cpu_model.py:EmptyCPUModel.test \ - tests/avocado/hotplug_cpu.py:HotPlugCPU.test \ - tests/avocado/netdev-ethtool.py:NetDevEthtool.test_igb \ - tests/avocado/netdev-ethtool.py:NetDevEthtool.test_igb_nomsi \ - tests/avocado/info_usernet.py:InfoUsernet.test_hostfwd \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_pt \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_strict \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_strict_cm \ - tests/avocado/linux_initrd.py:LinuxInitrd.test_with_2gib_file_should_exit_error_msg_with_linux_v3_6 \ - tests/avocado/linux_initrd.py:LinuxInitrd.test_with_2gib_file_should_work_with_linux_v4_16 \ - tests/avocado/migration.py:Migration.test_migration_with_exec \ - tests/avocado/migration.py:Migration.test_migration_with_tcp_localhost \ - tests/avocado/migration.py:Migration.test_migration_with_unix \ - tests/avocado/pc_cpu_hotplug_props.py:OmittedCPUProps.test_no_die_id \ - tests/avocado/replay_kernel.py:ReplayKernelNormal.test_x86_64_pc \ - tests/avocado/reverse_debugging.py:ReverseDebugging_X86_64.test_x86_64_pc \ - tests/avocado/version.py:Version.test_qmp_human_info_version \ - tests/avocado/virtio_version.py:VirtioVersionCheck.test_conventional_devs \ - tests/avocado/virtio_version.py:VirtioVersionCheck.test_modern_only_devs \ - tests/avocado/vnc.py:Vnc.test_change_password \ - tests/avocado/vnc.py:Vnc.test_change_password_requires_a_password \ - tests/avocado/vnc.py:Vnc.test_no_vnc \ - tests/avocado/vnc.py:Vnc.test_no_vnc_change_password \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_4_1 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_set_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_unset_4_1 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v1_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v1_set_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v2_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v2_unset_4_1 \ - tests/avocado/x86_cpu_model_versions.py:X86CPUModelAliases.test_4_0_alias_compatibility \ - tests/avocado/x86_cpu_model_versions.py:X86CPUModelAliases.test_4_1_alias \ - tests/avocado/x86_cpu_model_versions.py:X86CPUModelAliases.test_none_alias diff --git a/scripts/ci/org.centos/stream/README b/scripts/ci/org.centos/stream/README deleted file mode 100644 index e3eadfe3ea..0000000000 --- a/scripts/ci/org.centos/stream/README +++ /dev/null @@ -1,17 +0,0 @@ -This directory contains scripts for generating a build of QEMU that -closely matches the CentOS Stream[1] builds of the qemu-kvm package. - -To have the environment ready to configure, build QEMU and run tests, -please start with a CentOS Stream machine and: - - * apply the generic "build-environment.yml" playbook located at - scripts/ci/setup - - * apply the "build-environment.yml" in the directory following the - CentOS Stream version (such as "8"). - -This currently only covers CentOS Stream 8 environments and -packages[2]. - -[1] https://www.centos.org/centos-stream/ -[2] https://git.centos.org/rpms/qemu-kvm/commits/c8s-stream-rhel From cc1d2e04d516da0e1c2e4e99aedf86c5688bd845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:18 +0100 Subject: [PATCH 1260/2884] docs/devel: update references to centos to non-versioned container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >From the website: "After May 31, 2024, CentOS Stream 8 will be archived and no further updates will be provided." We have updated a few bits but there are still references that need fixing. Rather than bump I've replaced them with references to the Debian image so we don't have to bump at the next update. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-3-alex.bennee@linaro.org> --- docs/devel/testing.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index fa28e3ecb2..23d3f44f52 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -387,9 +387,9 @@ make target): .. code:: - make docker-test-build@centos8 + make docker-test-build@debian -This will create a container instance using the ``centos8`` image (the image +This will create a container instance using the ``debian`` image (the image is downloaded and initialized automatically), in which the ``test-build`` job is executed. @@ -410,8 +410,8 @@ locally by using the ``NOCACHE`` build option: Images ~~~~~~ -Along with many other images, the ``centos8`` image is defined in a Dockerfile -in ``tests/docker/dockerfiles/``, called ``centos8.docker``. ``make docker-help`` +Along with many other images, the ``debian`` image is defined in a Dockerfile +in ``tests/docker/dockerfiles/``, called ``debian.docker``. ``make docker-help`` command will list all the available images. A ``.pre`` script can be added beside the ``.docker`` file, which will be From 5ed4e5a15ccf13c633c8b664097bc6f2d61d1109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:19 +0100 Subject: [PATCH 1261/2884] tests/vm: update centos.aarch64 image to 9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Centos Stream 8 goes out of support we need to update. To do this powertools is replaced by crb and we don't over specify the python3 we want. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-4-alex.bennee@linaro.org> --- tests/vm/centos.aarch64 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/vm/centos.aarch64 b/tests/vm/centos.aarch64 index 3f58de1e64..fcf9e08c87 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -25,9 +25,9 @@ DEFAULT_CONFIG = { 'cpu' : "max", 'machine' : "virt,gic-version=max", 'install_cmds' : ( - "dnf config-manager --set-enabled powertools, " + "dnf config-manager --enable crb, " "dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo, " - "dnf install -y make ninja-build git python38 gcc gcc-c++ flex bison "\ + "dnf install -y make ninja-build git python3 gcc gcc-c++ flex bison "\ "glib2-devel pixman-devel zlib-devel docker-ce.aarch64, " "systemctl enable docker, " ), @@ -38,10 +38,10 @@ DEFAULT_CONFIG = { class CentosAarch64VM(basevm.BaseVM): - name = "centos8.aarch64" + name = "centos9.aarch64" arch = "aarch64" - image_name = "CentOS-Stream-GenericCloud-8-20220125.1.aarch64.qcow2" - image_link = "https://cloud.centos.org/centos/8-stream/aarch64/images/" + image_name = "CentOS-Stream-GenericCloud-9-20230501.0.aarch64.qcow2" + image_link = "https://cloud.centos.org/centos/9-stream/aarch64/images/" image_link += image_name BUILD_SCRIPT = """ set -e; From 0f73539676719605e618a0d23326fdc85230963f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:20 +0100 Subject: [PATCH 1262/2884] tests/vm: remove plain centos image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't really used and we have lighter weight docker containers for testing this stuff directly. Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-5-alex.bennee@linaro.org> --- tests/vm/Makefile.include | 1 - tests/vm/centos | 51 --------------------------------------- 2 files changed, 52 deletions(-) delete mode 100755 tests/vm/centos diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index ac56824a87..13ed80f72d 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -45,7 +45,6 @@ vm-help vm-test: @echo " vm-build-netbsd - Build QEMU in NetBSD VM" @echo " vm-build-openbsd - Build QEMU in OpenBSD VM" ifneq ($(GENISOIMAGE),) - @echo " vm-build-centos - Build QEMU in CentOS VM, with Docker" ifneq ($(EFI_AARCH64),) @echo " vm-build-ubuntu.aarch64 - Build QEMU in ubuntu aarch64 VM" @echo " vm-build-centos.aarch64 - Build QEMU in CentOS aarch64 VM" diff --git a/tests/vm/centos b/tests/vm/centos deleted file mode 100755 index d25c8f8b5b..0000000000 --- a/tests/vm/centos +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -# -# CentOS 8 Stream image -# -# Copyright 2018, 2022 Red Hat Inc. -# -# Authors: -# Fam Zheng -# -# This code is licensed under the GPL version 2 or later. See -# the COPYING file in the top-level directory. -# - -import os -import sys -import subprocess -import basevm -import time - -class CentosVM(basevm.BaseVM): - name = "centos" - arch = "x86_64" - BUILD_SCRIPT = """ - set -e; - cd $(mktemp -d); - export SRC_ARCHIVE=/dev/vdb; - sudo chmod a+r $SRC_ARCHIVE; - tar -xf $SRC_ARCHIVE; - make docker-test-block@centos9 {verbose} J={jobs} NETWORK=1; - make docker-test-quick@centos9 {verbose} J={jobs} NETWORK=1; - """ - - def build_image(self, img): - cimg = self._download_with_cache("https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20220125.1.x86_64.qcow2") - img_tmp = img + ".tmp" - subprocess.check_call(['cp', '-f', cimg, img_tmp]) - self.exec_qemu_img("resize", img_tmp, "50G") - self.boot(img_tmp, extra_args = ["-cdrom", self.gen_cloud_init_iso()]) - self.wait_ssh() - self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") - self.ssh_root_check("dnf update -y") - self.ssh_root_check("dnf install -y dnf-plugins-core") - self.ssh_root_check("dnf config-manager --set-enabled powertools") - self.ssh_root_check("dnf install -y podman make ninja-build git python3") - self.ssh_root("poweroff") - self.wait() - os.rename(img_tmp, img) - return 0 - -if __name__ == "__main__": - sys.exit(basevm.main(CentosVM)) From 053d5042ad8df5c4670bee61e175f0dc8046ee6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:21 +0100 Subject: [PATCH 1263/2884] scripts/ci: remove CentOS bits from common build-environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although I've just removed the CentOS specific build-environment its probably a bit too confusing to have multiple distros mixed up in one place. Prior to moving clean-up what will be just for ubuntu. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-6-alex.bennee@linaro.org> --- scripts/ci/setup/build-environment.yml | 98 -------------------------- 1 file changed, 98 deletions(-) diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml index de0d866a1e..e82097b465 100644 --- a/scripts/ci/setup/build-environment.yml +++ b/scripts/ci/setup/build-environment.yml @@ -184,101 +184,3 @@ - ansible_facts['distribution_version'] == '22.04' - ansible_facts['architecture'] == 'aarch64' - - name: Enable EPEL repo on EL8 - dnf: - name: - - epel-release - state: present - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_major_version'] == '8' - - - name: Enable PowerTools repo on CentOS 8 - ini_file: - path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo - section: powertools - option: enabled - value: "1" - when: - - ansible_facts['distribution_file_variety'] == 'CentOS' - - ansible_facts['distribution_major_version'] == '8' - - - name: Install basic packages to build QEMU on EL8 - dnf: - # This list of packages start with tests/docker/dockerfiles/centos8.docker - # but only include files that are common to all distro variants and present - # in the standard repos (no add-ons) - name: - - bzip2 - - bzip2-devel - - capstone-devel - - dbus-daemon - - device-mapper-multipath-devel - - diffutils - - gcc - - gcc-c++ - - genisoimage - - gettext - - git - - glib2-devel - - glusterfs-api-devel - - gnutls-devel - - libaio-devel - - libcap-ng-devel - - libcurl-devel - - libepoxy-devel - - libfdt-devel - - libgcrypt-devel - - libiscsi-devel - - libpmem-devel - - librados-devel - - librbd-devel - - libseccomp-devel - - libssh-devel - - libxkbcommon-devel - - lzo-devel - - make - - mesa-libEGL-devel - - nettle-devel - - ninja-build - - nmap-ncat - - numactl-devel - - pixman-devel - - python38 - - python3-sphinx - - rdma-core-devel - - redhat-rpm-config - - snappy-devel - - spice-glib-devel - - systemd-devel - - systemtap-sdt-devel - - tar - - zlib-devel - state: present - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_version'] == '8' - - - name: Install packages only available on x86 and aarch64 - dnf: - # Spice server not available in ppc64le - name: - - spice-server - - spice-server-devel - state: present - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_version'] == '8' - - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' - - - name: Check whether the Python runtime version is managed by alternatives - stat: - path: /etc/alternatives/python3 - register: python3 - - - name: Set default Python runtime to 3.8 on EL8 - command: alternatives --set python3 /usr/bin/python3.8 - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_version'] == '8' - - python3.stat.islnk and python3.stat.lnk_target != '/usr/bin/python3.8' From 0eb7fadcfdaca701105480f2215bd3e38e40b3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:22 +0100 Subject: [PATCH 1264/2884] docs/ci: clean-up references for consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document we have split up build-environment by distro and update the references that exist in the code base to be correct. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-7-alex.bennee@linaro.org> --- .../custom-runners/ubuntu-22.04-aarch32.yml | 2 +- .../custom-runners/ubuntu-22.04-aarch64.yml | 2 +- .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml | 2 +- docs/devel/ci-runners.rst.inc | 13 ++++++------- scripts/ci/setup/{ => ubuntu}/build-environment.yml | 0 5 files changed, 9 insertions(+), 10 deletions(-) rename scripts/ci/setup/{ => ubuntu}/build-environment.yml (100%) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml index b8a0d75162..8727687e2b 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml @@ -1,5 +1,5 @@ # All ubuntu-22.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/qemu/build-environment.yml task +# setup by the scripts/ci/setup/ubuntu/build-environment.yml task # "Install basic packages to build QEMU on Ubuntu 22.04" ubuntu-22.04-aarch32-all: diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml index 374b0956c3..263a3c2140 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml @@ -1,5 +1,5 @@ # All ubuntu-22.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/qemu/build-environment.yml task +# setup by the scripts/ci/setup/ubuntu/build-environment.yml task # "Install basic packages to build QEMU on Ubuntu 22.04" ubuntu-22.04-aarch64-all-linux-static: diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index 25935048e2..69ddd3e7d5 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -1,5 +1,5 @@ # All ubuntu-22.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/build-environment.yml task +# setup by the scripts/ci/setup/ubuntu/build-environment.yml task # "Install basic packages to build QEMU on Ubuntu 22.04" ubuntu-22.04-s390x-all-linux: diff --git a/docs/devel/ci-runners.rst.inc b/docs/devel/ci-runners.rst.inc index 7817001fb2..67b23d3719 100644 --- a/docs/devel/ci-runners.rst.inc +++ b/docs/devel/ci-runners.rst.inc @@ -41,19 +41,18 @@ those hosts. This would look like:: Build environment ~~~~~~~~~~~~~~~~~ -The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will -set up machines with the environment needed to perform builds and run -QEMU tests. This playbook consists on the installation of various -required packages (and a general package update while at it). It -currently covers a number of different Linux distributions, but it can -be expanded to cover other systems. +The ``scripts/ci/setup/$DISTRO/build-environment.yml`` Ansible +playbook will set up machines with the environment needed to perform +builds and run QEMU tests. This playbook consists on the installation +of various required packages (and a general package update while at +it). The minimum required version of Ansible successfully tested in this playbook is 2.8.0 (a version check is embedded within the playbook itself). To run the playbook, execute:: cd scripts/ci/setup - ansible-playbook -i inventory build-environment.yml + ansible-playbook -i inventory $DISTRO/build-environment.yml Please note that most of the tasks in the playbook require superuser privileges, such as those from the ``root`` account or those obtained diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/ubuntu/build-environment.yml similarity index 100% rename from scripts/ci/setup/build-environment.yml rename to scripts/ci/setup/ubuntu/build-environment.yml From 8e3034914a51444a4e5db9b82a8cc711cc1f76ed Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 3 Jun 2024 18:53:23 +0100 Subject: [PATCH 1265/2884] tests/lcitool: Delete obsolete centos-stream-8.yml file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've missed to delete this file when removing support for CentOS 8. Since the current upstream version of the lcitool removed support for CentOS 8 now, too, we have to remove the file before updating. Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Message-Id: <20240601070543.37786-2-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-8-alex.bennee@linaro.org> --- tests/lcitool/targets/centos-stream-8.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 tests/lcitool/targets/centos-stream-8.yml diff --git a/tests/lcitool/targets/centos-stream-8.yml b/tests/lcitool/targets/centos-stream-8.yml deleted file mode 100644 index 6b11160fd1..0000000000 --- a/tests/lcitool/targets/centos-stream-8.yml +++ /dev/null @@ -1,3 +0,0 @@ -paths: - pip3: /usr/bin/pip3.8 - python: /usr/bin/python3.8 From 23ef50ae2d0c557483727db652a18e02b11d272f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 3 Jun 2024 18:53:24 +0100 Subject: [PATCH 1266/2884] .gitlab-ci.d/buildtest.yml: Use -fno-sanitize=function in the clang-system job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The latest version of Clang (version 18 from Fedora 40) now reports bad function pointer casts as undefined behavior. Unfortunately, we are still doing this in quite a lot of places in the QEMU code and some of them are not easy to fix. So for the time being, temporarily switch this off in the failing clang-system job until all spots in the QEMU sources have been tackled. Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Message-Id: <20240601070543.37786-4-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-9-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 91c57efded..0eec570310 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -432,6 +432,7 @@ clang-system: IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined + --extra-cflags=-fno-sanitize=function TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu s390x-softmmu MAKE_CHECK_ARGS: check-qtest check-tcg From 06f3330bb0b04607c5b16cdcb516a76e5f3f7dc6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 3 Jun 2024 18:53:25 +0100 Subject: [PATCH 1267/2884] tests/lcitool: Bump to latest libvirt-ci and update Fedora and Alpine version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update to the latest version of lcitool. It dropped support for Fedora 38 and Alpine 3.18, so we have to update these to newer versions here, too. Python 3.12 dropped the "imp" module which we still need for running Avocado. Fortunately Fedora 40 still ships with a work-around package that we can use until somebody updates our Avocado to a newer version. Signed-off-by: Thomas Huth Message-Id: <20240601070543.37786-3-thuth@redhat.com> [AJB: regen on rebase] Reviewed-by: Daniel P. Berrangé Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-10-alex.bennee@linaro.org> --- tests/docker/dockerfiles/alpine.docker | 4 ++-- tests/docker/dockerfiles/fedora-win64-cross.docker | 5 +++-- tests/docker/dockerfiles/fedora.docker | 5 +++-- tests/lcitool/libvirt-ci | 2 +- tests/lcitool/projects/qemu.yml | 1 + tests/lcitool/refresh | 6 +++--- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 554464f31e..b079a83fe2 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all alpine-318 qemu +# $ lcitool dockerfile --layers all alpine-319 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/alpine:3.18 +FROM docker.io/library/alpine:3.19 RUN apk update && \ apk upgrade && \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 0f78711876..fef846d5a6 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 qemu,qemu-win-installer +# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-40 qemu,qemu-win-installer # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:38 +FROM registry.fedoraproject.org/fedora:40 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -51,6 +51,7 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-pip \ python3-sphinx \ python3-sphinx_rtd_theme \ + python3-zombie-imp \ sed \ socat \ sparse \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 098c894d10..44f239c088 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-38 qemu +# $ lcitool dockerfile --layers all fedora-40 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:38 +FROM registry.fedoraproject.org/fedora:40 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -110,6 +110,7 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-pip \ python3-sphinx \ python3-sphinx_rtd_theme \ + python3-zombie-imp \ rdma-core-devel \ sed \ snappy-devel \ diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index cec6703971..0e9490cebc 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit cec67039719becbfbab866f9c23574f389cf9559 +Subproject commit 0e9490cebc726ef772b6c9e27dac32e7ae99f9b2 diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 7511ec7ccb..070d7f4706 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -89,6 +89,7 @@ packages: - pkg-config - pulseaudio - python3 + - python3-imp - python3-numpy - python3-opencv - python3-pillow diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 789acefb75..9d8e9c6a4a 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -124,11 +124,11 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-318") + generate_dockerfile("alpine", "alpine-319") generate_dockerfile("centos9", "centos-stream-9") generate_dockerfile("debian", "debian-12", trailer="".join(debian12_extras)) - generate_dockerfile("fedora", "fedora-38") + generate_dockerfile("fedora", "fedora-40") generate_dockerfile("opensuse-leap", "opensuse-leap-15") generate_dockerfile("ubuntu2204", "ubuntu-2204") @@ -191,7 +191,7 @@ try: trailer=cross_build("s390x-linux-gnu-", "s390x-softmmu,s390x-linux-user")) - generate_dockerfile("fedora-win64-cross", "fedora-38", + generate_dockerfile("fedora-win64-cross", "fedora-40", project='qemu,qemu-win-installer', cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", From 61d1e3cbde0e6e84de4edc1b944a61f352f95d57 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 3 Jun 2024 18:53:26 +0100 Subject: [PATCH 1268/2884] tests/lcitool: Install mingw-w64-tools for the Windows cross-builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beside g++ we also need the mingw-w64-tools for properly building the code in qga/vss-win32/ , so let's install that package now, too. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20240601070543.37786-5-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-11-alex.bennee@linaro.org> --- tests/docker/dockerfiles/fedora-win64-cross.docker | 1 + tests/lcitool/projects/qemu-win-installer.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index fef846d5a6..007e1574bd 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -75,6 +75,7 @@ ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" RUN nosync dnf install -y \ + mingw-w64-tools \ mingw32-nsis \ mingw64-SDL2 \ mingw64-SDL2_image \ diff --git a/tests/lcitool/projects/qemu-win-installer.yml b/tests/lcitool/projects/qemu-win-installer.yml index 86aa22297c..f3663ba030 100644 --- a/tests/lcitool/projects/qemu-win-installer.yml +++ b/tests/lcitool/projects/qemu-win-installer.yml @@ -2,3 +2,4 @@ --- packages: - g++ + - mingw-w64-tools From 1417704564a50457fcef9f4ed11afbd9e9050bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:27 +0100 Subject: [PATCH 1269/2884] tests/lcitool: generate package lists for ansible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the new ability to output YAML we can build the package list for our ansible setup scripts. We will integrate them in the next commit. Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-12-alex.bennee@linaro.org> --- .../ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 127 ++++++++++++++++++ .../setup/ubuntu/ubuntu-2204-armhf-cross.yml | 127 ++++++++++++++++++ .../ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 125 +++++++++++++++++ tests/lcitool/refresh | 16 ++- 4 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml create mode 100644 scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml create mode 100644 scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml new file mode 100644 index 0000000000..8d7d8725fb --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -0,0 +1,127 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables --host-arch aarch64 ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +packages: + - bash + - bc + - bison + - bsdextrautils + - bzip2 + - ca-certificates + - ccache + - clang + - dbus + - debianutils + - diffutils + - exuberant-ctags + - findutils + - flex + - gcc + - gcovr + - gettext + - git + - hostname + - libaio-dev + - libasan6 + - libasound2-dev + - libattr1-dev + - libbpf-dev + - libbrlapi-dev + - libbz2-dev + - libc6-dev + - libcacard-dev + - libcap-ng-dev + - libcapstone-dev + - libcmocka-dev + - libcurl4-gnutls-dev + - libdaxctl-dev + - libdrm-dev + - libepoxy-dev + - libfdt-dev + - libffi-dev + - libfuse3-dev + - libgbm-dev + - libgcrypt20-dev + - libglib2.0-dev + - libglusterfs-dev + - libgnutls28-dev + - libgtk-3-dev + - libibumad-dev + - libibverbs-dev + - libiscsi-dev + - libjemalloc-dev + - libjpeg-turbo8-dev + - libjson-c-dev + - liblttng-ust-dev + - liblzo2-dev + - libncursesw5-dev + - libnfs-dev + - libnuma-dev + - libpam0g-dev + - libpcre2-dev + - libpipewire-0.3-dev + - libpixman-1-dev + - libpng-dev + - libpulse-dev + - librbd-dev + - librdmacm-dev + - libsasl2-dev + - libsdl2-dev + - libsdl2-image-dev + - libseccomp-dev + - libselinux1-dev + - libslirp-dev + - libsnappy-dev + - libsndio-dev + - libspice-protocol-dev + - libspice-server-dev + - libssh-dev + - libsystemd-dev + - libtasn1-6-dev + - libubsan1 + - libudev-dev + - liburing-dev + - libusb-1.0-0-dev + - libusbredirhost-dev + - libvdeplug-dev + - libvirglrenderer-dev + - libvte-2.91-dev + - libxen-dev + - libzstd-dev + - llvm + - locales + - make + - meson + - mtools + - multipath-tools + - ncat + - nettle-dev + - ninja-build + - openssh-client + - pkgconf + - python3 + - python3-numpy + - python3-opencv + - python3-pillow + - python3-pip + - python3-sphinx + - python3-sphinx-rtd-theme + - python3-tomli + - python3-venv + - python3-yaml + - rpm2cpio + - sed + - socat + - sparse + - swtpm + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - xorriso + - zlib1g-dev + - zstd + diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml b/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml new file mode 100644 index 0000000000..0cc34cd10b --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml @@ -0,0 +1,127 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables --cross-arch armv7l ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +packages: + - bash + - bc + - bison + - bsdextrautils + - bzip2 + - ca-certificates + - ccache + - dbus + - debianutils + - diffutils + - exuberant-ctags + - findutils + - flex + - gcc + - gcovr + - gettext + - git + - hostname + - libglib2.0-dev + - libpcre2-dev + - libsndio-dev + - libspice-protocol-dev + - llvm + - locales + - make + - meson + - mtools + - ncat + - ninja-build + - openssh-client + - pkgconf + - python3 + - python3-numpy + - python3-opencv + - python3-pillow + - python3-pip + - python3-sphinx + - python3-sphinx-rtd-theme + - python3-tomli + - python3-venv + - python3-yaml + - rpm2cpio + - sed + - socat + - sparse + - swtpm + - tar + - tesseract-ocr + - tesseract-ocr-eng + - xorriso + - zstd + - gcc-arm-linux-gnueabihf + - libaio-dev:armhf + - libasan6:armhf + - libasound2-dev:armhf + - libattr1-dev:armhf + - libbpf-dev:armhf + - libbrlapi-dev:armhf + - libbz2-dev:armhf + - libc6-dev:armhf + - libcacard-dev:armhf + - libcap-ng-dev:armhf + - libcapstone-dev:armhf + - libcmocka-dev:armhf + - libcurl4-gnutls-dev:armhf + - libdaxctl-dev:armhf + - libdrm-dev:armhf + - libepoxy-dev:armhf + - libfdt-dev:armhf + - libffi-dev:armhf + - libfuse3-dev:armhf + - libgbm-dev:armhf + - libgcrypt20-dev:armhf + - libglib2.0-dev:armhf + - libglusterfs-dev:armhf + - libgnutls28-dev:armhf + - libgtk-3-dev:armhf + - libibumad-dev:armhf + - libibverbs-dev:armhf + - libiscsi-dev:armhf + - libjemalloc-dev:armhf + - libjpeg-turbo8-dev:armhf + - libjson-c-dev:armhf + - liblttng-ust-dev:armhf + - liblzo2-dev:armhf + - libncursesw5-dev:armhf + - libnfs-dev:armhf + - libnuma-dev:armhf + - libpam0g-dev:armhf + - libpipewire-0.3-dev:armhf + - libpixman-1-dev:armhf + - libpng-dev:armhf + - libpulse-dev:armhf + - librbd-dev:armhf + - librdmacm-dev:armhf + - libsasl2-dev:armhf + - libsdl2-dev:armhf + - libsdl2-image-dev:armhf + - libseccomp-dev:armhf + - libselinux1-dev:armhf + - libslirp-dev:armhf + - libsnappy-dev:armhf + - libspice-server-dev:armhf + - libssh-dev:armhf + - libsystemd-dev:armhf + - libtasn1-6-dev:armhf + - libubsan1:armhf + - libudev-dev:armhf + - liburing-dev:armhf + - libusb-1.0-0-dev:armhf + - libusbredirhost-dev:armhf + - libvdeplug-dev:armhf + - libvirglrenderer-dev:armhf + - libvte-2.91-dev:armhf + - libxen-dev:armhf + - libzstd-dev:armhf + - nettle-dev:armhf + - systemtap-sdt-dev:armhf + - zlib1g-dev:armhf + diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml new file mode 100644 index 0000000000..16050a5058 --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -0,0 +1,125 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables --host-arch s390x ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +packages: + - bash + - bc + - bison + - bsdextrautils + - bzip2 + - ca-certificates + - ccache + - clang + - dbus + - debianutils + - diffutils + - exuberant-ctags + - findutils + - flex + - gcc + - gcovr + - gettext + - git + - hostname + - libaio-dev + - libasan6 + - libasound2-dev + - libattr1-dev + - libbpf-dev + - libbrlapi-dev + - libbz2-dev + - libc6-dev + - libcacard-dev + - libcap-ng-dev + - libcapstone-dev + - libcmocka-dev + - libcurl4-gnutls-dev + - libdaxctl-dev + - libdrm-dev + - libepoxy-dev + - libfdt-dev + - libffi-dev + - libfuse3-dev + - libgbm-dev + - libgcrypt20-dev + - libglib2.0-dev + - libglusterfs-dev + - libgnutls28-dev + - libgtk-3-dev + - libibumad-dev + - libibverbs-dev + - libiscsi-dev + - libjemalloc-dev + - libjpeg-turbo8-dev + - libjson-c-dev + - liblttng-ust-dev + - liblzo2-dev + - libncursesw5-dev + - libnfs-dev + - libnuma-dev + - libpam0g-dev + - libpcre2-dev + - libpipewire-0.3-dev + - libpixman-1-dev + - libpng-dev + - libpulse-dev + - librbd-dev + - librdmacm-dev + - libsasl2-dev + - libsdl2-dev + - libsdl2-image-dev + - libseccomp-dev + - libselinux1-dev + - libslirp-dev + - libsnappy-dev + - libsndio-dev + - libspice-protocol-dev + - libssh-dev + - libsystemd-dev + - libtasn1-6-dev + - libubsan1 + - libudev-dev + - liburing-dev + - libusb-1.0-0-dev + - libusbredirhost-dev + - libvdeplug-dev + - libvirglrenderer-dev + - libvte-2.91-dev + - libzstd-dev + - llvm + - locales + - make + - meson + - mtools + - multipath-tools + - ncat + - nettle-dev + - ninja-build + - openssh-client + - pkgconf + - python3 + - python3-numpy + - python3-opencv + - python3-pillow + - python3-pip + - python3-sphinx + - python3-sphinx-rtd-theme + - python3-tomli + - python3-venv + - python3-yaml + - rpm2cpio + - sed + - socat + - sparse + - swtpm + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - xorriso + - zlib1g-dev + - zstd + diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 9d8e9c6a4a..b25e3ac4dd 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -80,7 +80,7 @@ def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None): def generate_cirrus(target, trailer=None): filename = Path(src_dir, ".gitlab-ci.d", "cirrus", target + ".vars") - cmd = lcitool_cmd + ["variables", target, "qemu"] + cmd = lcitool_cmd + ["variables", "--format", "shell", target, "qemu"] generate(filename, cmd, trailer) @@ -90,6 +90,13 @@ def generate_pkglist(vm, target): generate(filename, cmd, None) +def generate_yaml(os, target, arch, trailer=None): + filename = Path(src_dir, "scripts", "ci", "setup", os, f"{target}-{arch}.yaml") + cmd = lcitool_cmd + ["variables", "--format", "yaml", "-a", + arch, target, "qemu"] + generate(filename, cmd, trailer) + + # Netmap still needs to be manually built as it is yet to be packaged # into a distro. We also add cscope and gtags which are used in the CI # test @@ -209,6 +216,13 @@ try: # generate_pkglist("freebsd", "freebsd-13") + # + # Ansible package lists + # + generate_yaml("ubuntu", "ubuntu-2204", "aarch64") + generate_yaml("ubuntu", "ubuntu-2204", "s390x") + + sys.exit(0) except Exception as ex: print(str(ex), file=sys.stderr) From c99064d03fc574254ab098562798c937a4761161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 3 Jun 2024 18:53:28 +0100 Subject: [PATCH 1270/2884] scripts/ci: drive ubuntu/build-environment.yml from lcitool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now lcitool can write the package list for us we no longer need to duplicate the information directly in build-environment. Signed-off-by: Alex Bennée Message-Id: <20240603175328.3823123-13-alex.bennee@linaro.org> --- scripts/ci/setup/ubuntu/build-environment.yml | 131 +----------------- 1 file changed, 7 insertions(+), 124 deletions(-) diff --git a/scripts/ci/setup/ubuntu/build-environment.yml b/scripts/ci/setup/ubuntu/build-environment.yml index e82097b465..edf1900b3e 100644 --- a/scripts/ci/setup/ubuntu/build-environment.yml +++ b/scripts/ci/setup/ubuntu/build-environment.yml @@ -32,135 +32,18 @@ when: - ansible_facts['distribution'] == 'Ubuntu' - # lcitool variables -f json ubuntu-2204 qemu | jq -r '.pkgs[]' | xargs -n 1 echo "-" - - name: Install basic packages to build QEMU on Ubuntu 22.04 - package: - name: - - bash - - bc - - bison - - bsdextrautils - - bzip2 - - ca-certificates - - ccache - - clang - - dbus - - debianutils - - diffutils - - exuberant-ctags - - findutils - - flex - - g++ - - gcc - - gcovr - - genisoimage - - gettext - - git - - hostname - - libaio-dev - - libasan5 - - libasound2-dev - - libattr1-dev - - libbpf-dev - - libbrlapi-dev - - libbz2-dev - - libc6-dev - - libcacard-dev - - libcap-ng-dev - - libcapstone-dev - - libcmocka-dev - - libcurl4-gnutls-dev - - libdaxctl-dev - - libdrm-dev - - libepoxy-dev - - libfdt-dev - - libffi-dev - - libgbm-dev - - libgcrypt20-dev - - libglib2.0-dev - - libglusterfs-dev - - libgnutls28-dev - - libgtk-3-dev - - libibumad-dev - - libibverbs-dev - - libiscsi-dev - - libjemalloc-dev - - libjpeg-turbo8-dev - - libjson-c-dev - - liblttng-ust-dev - - liblzo2-dev - - libncursesw5-dev - - libnfs-dev - - libnuma-dev - - libpam0g-dev - - libpcre2-dev - - libpixman-1-dev - - libpng-dev - - libpulse-dev - - librbd-dev - - librdmacm-dev - - libsasl2-dev - - libsdl2-dev - - libsdl2-image-dev - - libseccomp-dev - - libslirp-dev - - libsnappy-dev - - libspice-protocol-dev - - libssh-dev - - libsystemd-dev - - libtasn1-6-dev - - libubsan1 - - libudev-dev - - liburing-dev - - libusb-1.0-0-dev - - libusbredirhost-dev - - libvdeplug-dev - - libvirglrenderer-dev - - libvte-2.91-dev - - libxml2-dev - - libzstd-dev - - llvm - - locales - - make - - meson - - multipath-tools - - ncat - - nettle-dev - - ninja-build - - openssh-client - - pkgconf - - python3 - - python3-numpy - - python3-opencv - - python3-pillow - - python3-pip - - python3-sphinx - - python3-sphinx-rtd-theme - - python3-venv - - python3-yaml - - rpm2cpio - - sed - - sparse - - systemtap-sdt-dev - - tar - - tesseract-ocr - - tesseract-ocr-eng - - texinfo - - xfslibs-dev - - zlib1g-dev - state: present + # the package lists are updated by "make lcitool-refresh" + - name: Include package lists based on OS and architecture + include_vars: + file: "ubuntu-2204-{{ ansible_facts['architecture'] }}.yaml" when: - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' - # not all packages are available for all architectures - - name: Install additional packages to build QEMU on Ubuntu 22.04 + - name: Install packages for QEMU on Ubuntu 22.04 package: - name: - - libpmem-dev - - libspice-server-dev - - libxen-dev - state: present + name: "{{ packages }}" when: - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['distribution_version'] == '22.04' From 69cb498c56263a5ae484fd4fef920d3d3eea04c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 17:46:17 +0200 Subject: [PATCH 1271/2884] target/i386: fix pushed value of EFLAGS.RF When preparing an exception stack frame for a fault exception, the value pushed for RF is 1. Take that into account. The same should be true of interrupts for repeated string instructions, but the situation there is complicated. Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 49 ++++++++++++++++++++++++++++++++---- target/i386/tcg/translate.c | 8 ++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 0301459004..715db1f232 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -526,6 +526,24 @@ static inline unsigned int get_sp_mask(unsigned int e2) } } +static int exception_is_fault(int intno) +{ + switch (intno) { + /* + * #DB can be both fault- and trap-like, but it never sets RF=1 + * in the RFLAGS value pushed on the stack. + */ + case EXCP01_DB: + case EXCP03_INT3: + case EXCP04_INTO: + case EXCP08_DBLE: + case EXCP12_MCHK: + return 0; + } + /* Everything else including reserved exception is a fault. */ + return 1; +} + int exception_has_error_code(int intno) { switch (intno) { @@ -605,8 +623,9 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, int type, dpl, selector, ss_dpl, cpl; int has_error_code, new_stack, shift; uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; - uint32_t old_eip, sp_mask; + uint32_t old_eip, sp_mask, eflags; int vm86 = env->eflags & VM_MASK; + bool set_rf; has_error_code = 0; if (!is_int && !is_hw) { @@ -614,8 +633,10 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } if (is_int) { old_eip = next_eip; + set_rf = false; } else { old_eip = env->eip; + set_rf = exception_is_fault(intno); } dt = &env->idt; @@ -748,6 +769,15 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } push_size <<= shift; #endif + eflags = cpu_compute_eflags(env); + /* + * AMD states that code breakpoint #DBs clear RF=0, Intel leaves it + * as is. AMD behavior could be implemented in check_hw_breakpoints(). + */ + if (set_rf) { + eflags |= RF_MASK; + } + if (shift == 1) { if (new_stack) { if (vm86) { @@ -759,7 +789,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]); } - PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env)); + PUSHL(ssp, esp, sp_mask, eflags); PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); PUSHL(ssp, esp, sp_mask, old_eip); if (has_error_code) { @@ -776,7 +806,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]); } - PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env)); + PUSHW(ssp, esp, sp_mask, eflags); PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); PUSHW(ssp, esp, sp_mask, old_eip); if (has_error_code) { @@ -868,8 +898,9 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, target_ulong ptr; int type, dpl, selector, cpl, ist; int has_error_code, new_stack; - uint32_t e1, e2, e3, ss; + uint32_t e1, e2, e3, ss, eflags; target_ulong old_eip, esp, offset; + bool set_rf; has_error_code = 0; if (!is_int && !is_hw) { @@ -877,8 +908,10 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, } if (is_int) { old_eip = next_eip; + set_rf = false; } else { old_eip = env->eip; + set_rf = exception_is_fault(intno); } dt = &env->idt; @@ -950,9 +983,15 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, } esp &= ~0xfLL; /* align stack */ + /* See do_interrupt_protected. */ + eflags = cpu_compute_eflags(env); + if (set_rf) { + eflags |= RF_MASK; + } + PUSHQ(esp, env->segs[R_SS].selector); PUSHQ(esp, env->regs[R_ESP]); - PUSHQ(esp, cpu_compute_eflags(env)); + PUSHQ(esp, eflags); PUSHQ(esp, env->segs[R_CS].selector); PUSHQ(esp, old_eip); if (has_error_code) { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 0486ab6911..d438f8f76f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4630,6 +4630,14 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) * If jmp_opt, we want to handle each string instruction individually. * For icount also disable repz optimization so that each iteration * is accounted separately. + * + * FIXME: this is messy; it makes REP string instructions a lot less + * efficient than they should be and it gets in the way of correct + * handling of RF (interrupts or traps arriving after any iteration + * of a repeated string instruction but the last should set RF to 1). + * Perhaps it would be more efficient if REP string instructions were + * always at the beginning of the TB, or even their own TB? That + * would even allow accounting up to 64k iterations at once for icount. */ dc->repz_opt = !dc->jmp_opt && !(cflags & CF_USE_ICOUNT); From 73fb7b3c4983e48f3081fca00013a996abf659c0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 13:17:27 +0200 Subject: [PATCH 1272/2884] target/i386: fix implementation of ICEBP ICEBP generates a trap-like exception, while gen_exception() produces a fault. Resurrect gen_update_eip_next() to implement the desired semantics. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/helper.h | 1 + target/i386/tcg/emit.c.inc | 5 ++++- target/i386/tcg/excp_helper.c | 20 ++++++++++++++++++++ target/i386/tcg/helper-tcg.h | 12 +++++++++++- target/i386/tcg/translate.c | 13 +++++++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/target/i386/helper.h b/target/i386/helper.h index a52a1bf0f2..8f291a5f66 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -56,6 +56,7 @@ DEF_HELPER_2(sysret, void, env, int) DEF_HELPER_FLAGS_2(pause, TCG_CALL_NO_WG, noreturn, env, int) DEF_HELPER_FLAGS_3(raise_interrupt, TCG_CALL_NO_WG, noreturn, env, int, int) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, int) +DEF_HELPER_FLAGS_1(icebp, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_3(boundw, void, env, tl, int) DEF_HELPER_3(boundl, void, env, tl, int) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index e990141454..36127d9994 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1858,7 +1858,10 @@ static void gen_INT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) static void gen_INT1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { - gen_exception(s, EXCP01_DB); + gen_update_cc_op(s); + gen_update_eip_next(s); + gen_helper_icebp(tcg_env); + s->base.is_jmp = DISAS_NORETURN; } static void gen_INT3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c index 65e37ae2a0..72387aac24 100644 --- a/target/i386/tcg/excp_helper.c +++ b/target/i386/tcg/excp_helper.c @@ -140,6 +140,26 @@ G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index, raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); } +G_NORETURN void helper_icebp(CPUX86State *env) +{ + CPUState *cs = env_cpu(env); + + do_end_instruction(env); + + /* + * INT1 aka ICEBP generates a trap-like #DB, but it is pretty special. + * + * "Although the ICEBP instruction dispatches through IDT vector 1, + * that event is not interceptable by means of the #DB exception + * intercept". Instead there is a separate fault-like ICEBP intercept. + */ + cs->exception_index = EXCP01_DB; + env->error_code = 0; + env->exception_is_int = 0; + env->exception_next_eip = env->eip; + cpu_loop_exit(cs); +} + G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr, MMUAccessType access_type, uintptr_t retaddr) diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 85957943bf..eb6a4926a4 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -111,7 +111,17 @@ int exception_has_error_code(int intno); /* smm_helper.c */ void do_smm_enter(X86CPU *cpu); -/* bpt_helper.c */ +/* sysemu/bpt_helper.c */ bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update); +/* + * Do the tasks usually performed by gen_eob(). Callers of this function + * should also handle TF as appropriate. + */ +static inline void do_end_instruction(CPUX86State *env) +{ + /* needed if sti is just before */ + env->hflags &= ~HF_INHIBIT_IRQ_MASK; + env->eflags &= ~HF_RF_MASK; +} #endif /* I386_HELPER_TCG_H */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index d438f8f76f..77ed9c1db4 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -549,6 +549,19 @@ static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) } } +static void gen_update_eip_next(DisasContext *s) +{ + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); + } else if (CODE64(s)) { + tcg_gen_movi_tl(cpu_eip, s->pc); + } else { + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base)); + } + s->pc_save = s->pc; +} + static void gen_update_eip_cur(DisasContext *s) { assert(s->pc_save != -1); From 536032566b1fc1f4b66450770dbb30b49e736b52 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 15:12:22 +0200 Subject: [PATCH 1273/2884] target/i386: cleanup HLT helpers Use decode.c's support for intercepts, doing the check in TCG-generated code rather than the helper. This is cleaner because it allows removing the eip_addend argument to helper_hlt(). Signed-off-by: Paolo Bonzini --- target/i386/helper.h | 2 +- target/i386/tcg/decode-new.c.inc | 4 ++-- target/i386/tcg/emit.c.inc | 4 ++-- target/i386/tcg/sysemu/misc_helper.c | 13 ++----------- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/target/i386/helper.h b/target/i386/helper.h index 8f291a5f66..c244dbb481 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -90,7 +90,7 @@ DEF_HELPER_2(vmsave, void, env, int) DEF_HELPER_1(stgi, void, env) DEF_HELPER_1(clgi, void, env) DEF_HELPER_FLAGS_2(flush_page, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_2(hlt, TCG_CALL_NO_WG, noreturn, env, int) +DEF_HELPER_FLAGS_1(hlt, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_FLAGS_2(monitor, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(mwait, TCG_CALL_NO_WG, noreturn, env, int) DEF_HELPER_1(rdmsr, void, env) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 0ff0866e8f..376d2bdabe 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1496,7 +1496,7 @@ static const X86OpEntry opcodes_root[256] = { [0xE7] = X86_OP_ENTRYrr(OUT, 0,v, I_unsigned,b), /* AX/EAX */ [0xF1] = X86_OP_ENTRY0(INT1, svm(ICEBP)), - [0xF4] = X86_OP_ENTRY0(HLT, chk(cpl0)), + [0xF4] = X86_OP_ENTRY0(HLT, chk(cpl0) svm(HLT)), [0xF5] = X86_OP_ENTRY0(CMC), [0xF6] = X86_OP_GROUP1(group3, E,b), [0xF7] = X86_OP_GROUP1(group3, E,v), @@ -2539,7 +2539,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) /* * Checks that result in #GP or VMEXIT come second. Intercepts are - * generally checked after non-memory exceptions (i.e. before all + * generally checked after non-memory exceptions (i.e. after all * exceptions if there is no memory operand). Exceptions are * vm86 checks (INTn, IRET, PUSHF/POPF), RSM and XSETBV (!). * diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 36127d9994..2e94e8ec56 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1638,8 +1638,8 @@ static void gen_HLT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { #ifdef CONFIG_SYSTEM_ONLY gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_hlt(tcg_env, cur_insn_len_i32(s)); + gen_update_eip_next(s); + gen_helper_hlt(tcg_env); s->base.is_jmp = DISAS_NORETURN; #endif } diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index edb7c3d894..e41c88346c 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -516,8 +516,7 @@ void helper_flush_page(CPUX86State *env, target_ulong addr) tlb_flush_page(env_cpu(env), addr); } -static G_NORETURN -void do_hlt(CPUX86State *env) +G_NORETURN void helper_hlt(CPUX86State *env) { CPUState *cs = env_cpu(env); @@ -527,14 +526,6 @@ void do_hlt(CPUX86State *env) cpu_loop_exit(cs); } -G_NORETURN void helper_hlt(CPUX86State *env, int next_eip_addend) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0, GETPC()); - env->eip += next_eip_addend; - - do_hlt(env); -} - void helper_monitor(CPUX86State *env, target_ulong ptr) { if ((uint32_t)env->regs[R_ECX] != 0) { @@ -558,6 +549,6 @@ G_NORETURN void helper_mwait(CPUX86State *env, int next_eip_addend) if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) { do_pause(env); } else { - do_hlt(env); + helper_hlt(env); } } From 330e6adc1acd2a235a96b502b3dd15ba6e77c228 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 15:12:22 +0200 Subject: [PATCH 1274/2884] target/i386: cleanup PAUSE helpers Use decode.c's support for intercepts, doing the check in TCG-generated code rather than the helper. This is cleaner because it allows removing the eip_addend argument to helper_pause(), even though it adds a bit of bloat for opcode 0x90's new decoding function. Signed-off-by: Paolo Bonzini --- target/i386/helper.h | 2 +- target/i386/tcg/decode-new.c.inc | 15 ++++++++++++++- target/i386/tcg/emit.c.inc | 20 ++++++++------------ target/i386/tcg/helper-tcg.h | 1 - target/i386/tcg/misc_helper.c | 10 +--------- target/i386/tcg/sysemu/misc_helper.c | 2 +- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/target/i386/helper.h b/target/i386/helper.h index c244dbb481..2f46cffabd 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -53,7 +53,7 @@ DEF_HELPER_1(sysenter, void, env) DEF_HELPER_2(sysexit, void, env, int) DEF_HELPER_2(syscall, void, env, int) DEF_HELPER_2(sysret, void, env, int) -DEF_HELPER_FLAGS_2(pause, TCG_CALL_NO_WG, noreturn, env, int) +DEF_HELPER_FLAGS_1(pause, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_FLAGS_3(raise_interrupt, TCG_CALL_NO_WG, noreturn, env, int, int) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, int) DEF_HELPER_FLAGS_1(icebp, TCG_CALL_NO_WG, noreturn, env) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 376d2bdabe..c2d8da8d14 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1359,6 +1359,19 @@ static void decode_group11(DisasContext *s, CPUX86State *env, X86OpEntry *entry, } } +static void decode_90(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static X86OpEntry pause = X86_OP_ENTRY0(PAUSE, svm(PAUSE)); + static X86OpEntry nop = X86_OP_ENTRY0(NOP); + static X86OpEntry xchg_ax = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v); + + if (REX_B(s)) { + *entry = xchg_ax; + } else { + *entry = (s->prefix & PREFIX_REPZ) ? pause : nop; + } +} + static const X86OpEntry opcodes_root[256] = { [0x00] = X86_OP_ENTRY2(ADD, E,b, G,b, lock), [0x01] = X86_OP_ENTRY2(ADD, E,v, G,v, lock), @@ -1441,7 +1454,7 @@ static const X86OpEntry opcodes_root[256] = { [0x86] = X86_OP_ENTRY2(XCHG, E,b, G,b, xchg), [0x87] = X86_OP_ENTRY2(XCHG, E,v, G,v, xchg), - [0x90] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x90] = X86_OP_GROUP0(90), [0x91] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), [0x92] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), [0x93] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2e94e8ec56..f90f3d3c58 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2350,6 +2350,14 @@ static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) decode->op[1].offset, vec_len, vec_len); } +static void gen_PAUSE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_next(s); + gen_helper_pause(tcg_env); + s->base.is_jmp = DISAS_NORETURN; +} + static void gen_PCMPESTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -4014,18 +4022,6 @@ static void gen_WAIT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) static void gen_XCHG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { - if (decode->b == 0x90 && !REX_B(s)) { - if (s->prefix & PREFIX_REPZ) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_pause(tcg_env, cur_insn_len_i32(s)); - s->base.is_jmp = DISAS_NORETURN; - } - /* No writeback. */ - decode->op[0].unit = X86_OP_SKIP; - return; - } - if (s->prefix & PREFIX_LOCK) { tcg_gen_atomic_xchg_tl(s->T0, s->A0, s->T1, s->mem_index, decode->op[0].ot | MO_LE); diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index eb6a4926a4..15d6c6f8b4 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -91,7 +91,6 @@ extern const uint8_t parity_table[256]; /* misc_helper.c */ void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask); -G_NORETURN void do_pause(CPUX86State *env); /* sysemu/svm_helper.c */ #ifndef CONFIG_USER_ONLY diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index b0f0f7b893..8316d42ffc 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -88,7 +88,7 @@ G_NORETURN void helper_rdpmc(CPUX86State *env) raise_exception_err(env, EXCP06_ILLOP, 0); } -G_NORETURN void do_pause(CPUX86State *env) +G_NORETURN void helper_pause(CPUX86State *env) { CPUState *cs = env_cpu(env); @@ -97,14 +97,6 @@ G_NORETURN void do_pause(CPUX86State *env) cpu_loop_exit(cs); } -G_NORETURN void helper_pause(CPUX86State *env, int next_eip_addend) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0, GETPC()); - env->eip += next_eip_addend; - - do_pause(env); -} - uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx) { if ((env->cr[4] & CR4_PKE_MASK) == 0) { diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index e41c88346c..093cc2d0f9 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -547,7 +547,7 @@ G_NORETURN void helper_mwait(CPUX86State *env, int next_eip_addend) /* XXX: not complete but not completely erroneous */ if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) { - do_pause(env); + helper_pause(env); } else { helper_hlt(env); } From 57f8dbdbe94a502301f51809e8b282b02df43370 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 13:18:56 +0200 Subject: [PATCH 1275/2884] target/i386: implement DR7.GD DR7.GD triggers a #DB exception on any access to debug registers. The GD bit is cleared so that the #DB handler itself can access the debug registers. Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/bpt_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/sysemu/bpt_helper.c index 4d96a48a3c..c1d5fce250 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/sysemu/bpt_helper.c @@ -238,6 +238,12 @@ target_ulong helper_get_dr(CPUX86State *env, int reg) } } + if (env->dr[7] & DR7_GD) { + env->dr[7] &= ~DR7_GD; + env->dr[6] |= DR6_BD; + raise_exception_ra(env, EXCP01_DB, GETPC()); + } + return env->dr[reg]; } @@ -251,6 +257,12 @@ void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) } } + if (env->dr[7] & DR7_GD) { + env->dr[7] &= ~DR7_GD; + env->dr[6] |= DR6_BD; + raise_exception_ra(env, EXCP01_DB, GETPC()); + } + if (reg < 4) { if (hw_breakpoint_enabled(env->dr[7], reg) && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { From 8aa76496dfaac0d7b0dd34793359680c90d9aea0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 15:41:56 +0200 Subject: [PATCH 1276/2884] target/i386: disable/enable breakpoints on vmentry/vmexit If the required DR7 (either from the VMCB or from the host save area) disables a breakpoint that was enabled prior to vmentry or vmexit, it is left enabled and will trigger EXCP_DEBUG. This causes a spurious #DB on the next crossing of the breakpoint. To disable it, vmentry/vmexit must use cpu_x86_update_dr7 to load DR7. Because cpu_x86_update_dr7 takes a 32-bit argument, check reserved bits prior to calling cpu_x86_update_dr7, and do the same for DR6 as well for consistency. This scenario is tested by the "host_rflags" test in kvm-unit-tests. Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/svm_helper.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 5d6de2294f..922d8964f8 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -163,6 +163,8 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) uint64_t new_cr0; uint64_t new_cr3; uint64_t new_cr4; + uint64_t new_dr6; + uint64_t new_dr7; if (aflag == 2) { addr = env->regs[R_EAX]; @@ -361,20 +363,22 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->vm_vmcb + offsetof(struct vmcb, save.rsp)); env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rax)); - env->dr[7] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr7)); - env->dr[6] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr6)); + + new_dr7 = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr7)); + new_dr6 = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr6)); #ifdef TARGET_X86_64 - if (env->dr[6] & DR_RESERVED_MASK) { + if (new_dr7 & DR_RESERVED_MASK) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } - if (env->dr[7] & DR_RESERVED_MASK) { + if (new_dr6 & DR_RESERVED_MASK) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } #endif + cpu_x86_update_dr7(env, new_dr7); + env->dr[6] = new_dr6; + if (is_efer_invalid_state(env)) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } @@ -864,8 +868,11 @@ void do_vmexit(CPUX86State *env) env->dr[6] = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.dr6)); - env->dr[7] = x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.dr7)); + + /* Disables all breakpoints in the host DR7 register. */ + cpu_x86_update_dr7(env, + x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.dr7)) & ~0xff); /* other setups */ x86_stl_phys(cs, @@ -891,8 +898,6 @@ void do_vmexit(CPUX86State *env) from the page table indicated the host's CR3. If the PDPEs contain illegal state, the processor causes a shutdown. */ - /* Disables all breakpoints in the host DR7 register. */ - /* Checks the reloaded host state for consistency. */ /* If the host's rIP reloaded by #VMEXIT is outside the limit of the From 1a150d331d9bbd882c8b93146ff7fbc6259f0961 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 May 2024 10:30:50 +0200 Subject: [PATCH 1277/2884] target/i386: fix INHIBIT_IRQ/TF/RF handling for VMRUN From vm entry to exit, VMRUN is handled as a single instruction. It uses DISAS_NORETURN in order to avoid processing TF or RF before the first instruction executes in the guest. However, the corresponding handling is missing in vmexit. Add it, and at the same time reorganize the comments with quotes from the manual about the tasks performed by a #VMEXIT. Another gen_eob() task that is missing in VMRUN is preparing the HF_INHIBIT_IRQ flag for the next instruction, in this case by loading it from the VMCB control state. Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/svm_helper.c | 46 +++++++++++++++++++++-------- target/i386/tcg/translate.c | 5 ++++ 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 922d8964f8..9db8ad62a0 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -254,6 +254,13 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) control.intercept_exceptions )); + env->hflags &= ~HF_INHIBIT_IRQ_MASK; + if (x86_ldl_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.int_state)) & + SVM_INTERRUPT_SHADOW_MASK) { + env->hflags |= HF_INHIBIT_IRQ_MASK; + } + nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.nested_ctl)); asid = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, @@ -815,8 +822,12 @@ void do_vmexit(CPUX86State *env) env->hflags &= ~HF_GUEST_MASK; env->intercept = 0; env->intercept_exceptions = 0; + + /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; env->int_ctl = 0; + + /* Clears the TSC_OFFSET inside the processor. */ env->tsc_offset = 0; env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, @@ -836,6 +847,15 @@ void do_vmexit(CPUX86State *env) cpu_x86_update_cr4(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr4))); + + /* + * Resets the current ASID register to zero (host ASID; TLB flush). + * + * If the host is in PAE mode, the processor reloads the host's PDPEs + * from the page table indicated the host's CR3. FIXME: If the PDPEs + * contain illegal state, the processor causes a shutdown (QEMU does + * not implement PDPTRs). + */ cpu_x86_update_cr3(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr3))); @@ -843,12 +863,14 @@ void do_vmexit(CPUX86State *env) set properly */ cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.efer))); + + /* Completion of the VMRUN instruction clears the host EFLAGS.RF bit. */ env->eflags = 0; cpu_load_eflags(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rflags)), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | - VM_MASK)); + RF_MASK | VM_MASK)); svm_load_seg_cache(env, MMU_PHYS_IDX, env->vm_hsave + offsetof(struct vmcb, save.es), R_ES); @@ -888,19 +910,17 @@ void do_vmexit(CPUX86State *env) env->hflags2 &= ~HF2_GIF_MASK; env->hflags2 &= ~HF2_VGIF_MASK; - /* FIXME: Resets the current ASID register to zero (host ASID). */ - /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ - /* Clears the TSC_OFFSET inside the processor. */ + /* FIXME: Checks the reloaded host state for consistency. */ - /* If the host is in PAE mode, the processor reloads the host's PDPEs - from the page table indicated the host's CR3. If the PDPEs contain - illegal state, the processor causes a shutdown. */ - - /* Checks the reloaded host state for consistency. */ - - /* If the host's rIP reloaded by #VMEXIT is outside the limit of the - host's code segment or non-canonical (in the case of long mode), a - #GP fault is delivered inside the host. */ + /* + * EFLAGS.TF causes a #DB trap after the VMRUN completes on the host + * side (i.e., after the #VMEXIT from the guest). Since we're running + * in the main loop, call do_interrupt_all directly. + */ + if ((env->eflags & TF_MASK) != 0) { + env->dr[6] |= DR6_BS; + do_interrupt_all(X86_CPU(cs), EXCP01_DB, 0, 0, env->eip, 0); + } } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 77ed9c1db4..a9c6424c7d 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3745,6 +3745,11 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_update_cc_op(s); gen_update_eip_cur(s); + /* + * Reloads INHIBIT_IRQ mask as well as TF and RF with guest state. + * The usual gen_eob() handling is performed on vmexit after + * host state is reloaded. + */ gen_helper_vmrun(tcg_env, tcg_constant_i32(s->aflag - 1), cur_insn_len_i32(s)); tcg_gen_exit_tb(NULL, 0); From 3718523d011e898d414f09a4ed43cf13d76de0b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 May 2024 10:47:31 +0200 Subject: [PATCH 1278/2884] target/i386: fix INHIBIT_IRQ/TF/RF handling for PAUSE PAUSE uses DISAS_NORETURN because the corresponding helper calls cpu_loop_exit(). However, while HLT clear HF_INHIBIT_IRQ_MASK to correctly handle "STI; HLT", the same is missing from PAUSE. And also gen_eob() clears HF_RF_MASK and synthesizes a #DB exception if single-step is active; none of this is done by HLT and PAUSE. Start fixing PAUSE, HLT will follow. Signed-off-by: Paolo Bonzini --- target/i386/tcg/misc_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index 8316d42ffc..ed4cda8001 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -92,6 +92,10 @@ G_NORETURN void helper_pause(CPUX86State *env) { CPUState *cs = env_cpu(env); + /* Do gen_eob() tasks before going back to the main loop. */ + do_end_instruction(env); + helper_rechecking_single_step(env); + /* Just let another CPU run. */ cs->exception_index = EXCP_INTERRUPT; cpu_loop_exit(cs); From 6dd7d8c6490b73dcc33dfb1fe76c081e7e2eb820 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 May 2024 10:47:31 +0200 Subject: [PATCH 1279/2884] target/i386: fix TF/RF handling for HLT HLT uses DISAS_NORETURN because the corresponding helper calls cpu_loop_exit(). However, while gen_eob() clears HF_RF_MASK and synthesizes a #DB exception if single-step is active, none of this is done by HLT. Note that the single-step trap is generated after the halt is finished. Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/misc_helper.c | 2 +- target/i386/tcg/sysemu/seg_helper.c | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index 093cc2d0f9..7fa0c5a06d 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -520,7 +520,7 @@ G_NORETURN void helper_hlt(CPUX86State *env) { CPUState *cs = env_cpu(env); - env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ + do_end_instruction(env); cs->halted = 1; cs->exception_index = EXCP_HLT; cpu_loop_exit(cs); diff --git a/target/i386/tcg/sysemu/seg_helper.c b/target/i386/tcg/sysemu/seg_helper.c index 9ba94deb3a..05174a79e7 100644 --- a/target/i386/tcg/sysemu/seg_helper.c +++ b/target/i386/tcg/sysemu/seg_helper.c @@ -130,15 +130,26 @@ void x86_cpu_do_interrupt(CPUState *cs) bool x86_cpu_exec_halt(CPUState *cpu) { - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { - X86CPU *x86_cpu = X86_CPU(cpu); + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { bql_lock(); apic_poll_irq(x86_cpu->apic_state); cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); bql_unlock(); } - return cpu_has_work(cpu); + + if (!cpu_has_work(cpu)) { + return false; + } + + /* Complete HLT instruction. */ + if (env->eflags & TF_MASK) { + env->dr[6] |= DR6_BS; + do_interrupt_all(x86_cpu, EXCP01_DB, 0, 0, env->eip, 0); + } + return true; } bool x86_need_replay_interrupt(int interrupt_request) From cdc829b37d4dff686f083c577490da1d75bc159f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 13:31:39 +0200 Subject: [PATCH 1280/2884] target/i386: document incorrect semantics of watchpoint following MOV/POP SS Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/bpt_helper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/sysemu/bpt_helper.c index c1d5fce250..b29acf41c3 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/sysemu/bpt_helper.c @@ -215,6 +215,12 @@ void breakpoint_handler(CPUState *cs) if (cs->watchpoint_hit->flags & BP_CPU) { cs->watchpoint_hit = NULL; if (check_hw_breakpoints(env, false)) { + /* + * FIXME: #DB should be delayed by one instruction if + * INHIBIT_IRQ is set (STI cannot trigger a watchpoint). + * The delayed #DB should also fuse with one generated + * by ICEBP (aka INT1). + */ raise_exception(env, EXCP01_DB); } else { cpu_loop_exit_noexc(cs); From b37c0dc85214e9d5e4a9b6f6a496ce4de3b8a4d6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 May 2024 11:16:14 +0200 Subject: [PATCH 1281/2884] target/i386: document use of DISAS_NORETURN DISAS_NORETURN suppresses the work normally done by gen_eob(), and therefore must be used in special cases only. Document them. Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index a9c6424c7d..2b6f67be40 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4761,6 +4761,17 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (dc->base.is_jmp) { case DISAS_NORETURN: + /* + * Most instructions should not use DISAS_NORETURN, as that suppresses + * the handling of hflags normally done by gen_eob(). We can + * get here: + * - for exception and interrupts + * - for jump optimization (which is disabled by INHIBIT_IRQ/RF/TF) + * - for VMRUN because RF/TF handling for the host is done after vmexit, + * and INHIBIT_IRQ is loaded from the VMCB + * - for HLT/PAUSE/MWAIT to exit the main loop with specific EXCP_* values; + * the helpers handle themselves the tasks normally done by gen_eob(). + */ break; case DISAS_TOO_MANY: gen_update_cc_op(dc); From f41990f552839ad016467586a9540b9455cc52fd Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 6 Jun 2024 10:53:16 +0100 Subject: [PATCH 1282/2884] target/i386: use local X86DecodedOp in gen_POP() This will make subsequent changes a little easier to read. Signed-off-by: Mark Cave-Ayland Message-ID: <20240606095319.229650-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index f90f3d3c58..ca78504b6e 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2575,11 +2575,13 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) { + X86DecodedOp *op = &decode->op[0]; MemOp ot = gen_pop_T0(s); - if (decode->op[0].has_ea) { + + if (op->has_ea) { /* NOTE: order is important for MMU exceptions */ gen_op_st_v(s, ot, s->T0, s->A0); - decode->op[0].unit = X86_OP_SKIP; + op->unit = X86_OP_SKIP; } /* NOTE: writing back registers after update is important for pop %sp */ gen_pop_update(s, ot); From aea49fbb01a4a1191165af0b08b5e27994f22c35 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 6 Jun 2024 10:53:17 +0100 Subject: [PATCH 1283/2884] target/i386: use gen_writeback() within gen_POP() Instead of directly implementing the writeback using gen_op_st_v(), use the existing gen_writeback() function. Suggested-by: Paolo Bonzini Signed-off-by: Mark Cave-Ayland Message-ID: <20240606095319.229650-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index ca78504b6e..6123235c00 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2580,9 +2580,9 @@ static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) if (op->has_ea) { /* NOTE: order is important for MMU exceptions */ - gen_op_st_v(s, ot, s->T0, s->A0); - op->unit = X86_OP_SKIP; + gen_writeback(s, decode, 0, s->T0); } + /* NOTE: writing back registers after update is important for pop %sp */ gen_pop_update(s, ot); } From f1b8613da380af8931e34dfb4dd0a6cca392d43b Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 6 Jun 2024 10:53:18 +0100 Subject: [PATCH 1284/2884] target/i386: fix SP when taking a memory fault during POP When OS/2 Warp configures its segment descriptors, many of them are configured with the P flag clear to allow for a fault-on-demand implementation. In the case where the stack value is POPped into the segment registers, the SP is incremented before calling gen_helper_load_seg() to validate the segment descriptor: IN: 0xffef2c0c: 66 07 popl %es OP: ld_i32 loc9,env,$0xfffffffffffffff8 sub_i32 loc9,loc9,$0x1 brcond_i32 loc9,$0x0,lt,$L0 st16_i32 loc9,env,$0xfffffffffffffff8 st8_i32 $0x1,env,$0xfffffffffffffffc ---- 0000000000000c0c 0000000000000000 ext16u_i64 loc0,rsp add_i64 loc0,loc0,ss_base ext32u_i64 loc0,loc0 qemu_ld_a64_i64 loc0,loc0,noat+un+leul,5 add_i64 loc3,rsp,$0x4 deposit_i64 rsp,rsp,loc3,$0x0,$0x10 extrl_i64_i32 loc5,loc0 call load_seg,$0x0,$0,env,$0x0,loc5 add_i64 rip,rip,$0x2 ext16u_i64 rip,rip exit_tb $0x0 set_label $L0 exit_tb $0x7fff58000043 If helper_load_seg() generates a fault when validating the segment descriptor then as the SP has already been incremented, the topmost word of the stack is overwritten by the arguments pushed onto the stack by the CPU before taking the fault handler. As a consequence things rapidly go wrong upon return from the fault handler due to the corrupted stack. Update the logic for the existing writeback condition so that a POP into the segment registers also calls helper_load_seg() first before incrementing the SP, so that if a fault occurs the SP remains unaltered. Signed-off-by: Mark Cave-Ayland Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2198 Message-ID: <20240606095319.229650-4-mark.cave-ayland@ilande.co.uk> Fixes: cc1d28bdbe0 ("target/i386: move 00-5F opcodes to new decoder", 2024-05-07) Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 6123235c00..4be3d9a6fb 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2578,7 +2578,7 @@ static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) X86DecodedOp *op = &decode->op[0]; MemOp ot = gen_pop_T0(s); - if (op->has_ea) { + if (op->has_ea || op->unit == X86_OP_SEG) { /* NOTE: order is important for MMU exceptions */ gen_writeback(s, decode, 0, s->T0); } From 3973615e7fbaeef1deeaa067577e373781ced70a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 6 Jun 2024 10:53:19 +0100 Subject: [PATCH 1285/2884] target/i386: fix size of EBP writeback in gen_enter() The calculation of FrameTemp is done using the size indicated by mo_pushpop() before being written back to EBP, but the final writeback to EBP is done using the size indicated by mo_stacksize(). In the case where mo_pushpop() is MO_32 and mo_stacksize() is MO_16 then the final writeback to EBP is done using MO_16 which can leave junk in the top 16-bits of EBP after executing ENTER. Change the writeback of EBP to use the same size indicated by mo_pushpop() to ensure that the full value is written back. Signed-off-by: Mark Cave-Ayland Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2198 Message-ID: <20240606095319.229650-5-mark.cave-ayland@ilande.co.uk> Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 2b6f67be40..fcba9c155f 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2138,7 +2138,7 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) } /* Copy the FrameTemp value to EBP. */ - gen_op_mov_reg_v(s, a_ot, R_EBP, s->T1); + gen_op_mov_reg_v(s, d_ot, R_EBP, s->T1); /* Compute the final value of ESP. */ tcg_gen_subi_tl(s->T1, s->T1, esp_addend + size * level); From 75dbebddb6a0f15bdfdae66d63de4905c793ccdb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Jun 2024 11:31:12 +0200 Subject: [PATCH 1286/2884] machine: default -M mem-merge to off is QEMU_MADV_MERGEABLE is not available Otherwise, starting any guest on a non-Linux guests results in qemu-system-arm: Couldn't set property 'merge' on 'memory-backend-ram': Invalid argument Cc: Michal Privoznik Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 77a356f232..a0ee43ca5c 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -17,6 +17,7 @@ #include "hw/loader.h" #include "qapi/error.h" #include "qapi/qapi-visit-machine.h" +#include "qemu/madvise.h" #include "qom/object_interfaces.h" #include "sysemu/cpus.h" #include "sysemu/sysemu.h" @@ -1129,7 +1130,7 @@ static void machine_initfn(Object *obj) container_get(obj, "/peripheral-anon"); ms->dump_guest_core = true; - ms->mem_merge = true; + ms->mem_merge = (QEMU_MADV_MERGEABLE != QEMU_MADV_INVALID); ms->enable_graphics = true; ms->kernel_cmdline = g_strdup(""); ms->ram_size = mc->default_ram_size; From 12d7d0c2496efe6325e73b04578213b8ad0af093 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Wed, 5 Jun 2024 12:44:54 +0200 Subject: [PATCH 1287/2884] meson: Don't even detect posix_madvise() on Darwin On Darwin, posix_madvise() has the same return semantics as plain madvise() [1]. That's not really what our usage expects. Fortunately, madvise() is available and preferred anyways so we may stop detecting posix_madvise() on Darwin. 1: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/man/man2/madvise.2.auto.html Signed-off-by: Michal Privoznik Message-ID: <00f71753bdeb8c0f049fda05fb63b84bb5502fb3.1717584048.git.mprivozn@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index d80203f1cd..ec59effca2 100644 --- a/meson.build +++ b/meson.build @@ -2556,10 +2556,16 @@ config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(gnu_source_prefix + ''' #else int main(void) { struct file_handle fh; return open_by_handle_at(0, &fh, 0); } #endif''')) -config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' - #include - #include - int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) + +# On Darwin posix_madvise() has the same return semantics as plain madvise(), +# i.e. errno is set and -1 is returned. That's not really how POSIX defines the +# function. On the flip side, it has madvise() which is preferred anyways. +if host_os != 'darwin' + config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' + #include + #include + int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) +endif config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(gnu_source_prefix + ''' #include From bfb8c79f89773fb7ef6a373efebf3ce691716cb0 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Wed, 5 Jun 2024 12:44:55 +0200 Subject: [PATCH 1288/2884] osdep: Make qemu_madvise() to set errno in all cases The unspoken premise of qemu_madvise() is that errno is set on error. And it is mostly the case except for posix_madvise() which is documented to return either zero (on success) or a positive error number. This means, we must set errno ourselves. And while at it, make the function return a negative value on error, just like other error paths do. Signed-off-by: Michal Privoznik Reviewed-by: David Hildenbrand Message-ID: Signed-off-by: Paolo Bonzini --- util/osdep.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/osdep.c b/util/osdep.c index e996c4744a..e42f4e8121 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -57,7 +57,12 @@ int qemu_madvise(void *addr, size_t len, int advice) #if defined(CONFIG_MADVISE) return madvise(addr, len, advice); #elif defined(CONFIG_POSIX_MADVISE) - return posix_madvise(addr, len, advice); + int rc = posix_madvise(addr, len, advice); + if (rc) { + errno = rc; + return -1; + } + return 0; #else errno = EINVAL; return -1; From 210b7b2b3cdef01a15ebabf02dd1a9d8a51743ba Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Wed, 5 Jun 2024 12:44:56 +0200 Subject: [PATCH 1289/2884] osdep: Make qemu_madvise() return ENOSYS on unsupported OSes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not every OS is capable of madvise() or posix_madvise() even. In that case, errno should be set to ENOSYS as it reflects the cause better. Signed-off-by: Michal Privoznik Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Message-ID: Signed-off-by: Paolo Bonzini --- util/osdep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/osdep.c b/util/osdep.c index e42f4e8121..5d23bbfbec 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -64,7 +64,7 @@ int qemu_madvise(void *addr, size_t len, int advice) } return 0; #else - errno = EINVAL; + errno = ENOSYS; return -1; #endif } From 5d9a9a617053a97a76ef332896c386d8fc895631 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Wed, 5 Jun 2024 12:44:58 +0200 Subject: [PATCH 1290/2884] backends/hostmem: Report error when memory size is unaligned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If memory-backend-{file,ram} has a size that's not aligned to underlying page size it is not only wasteful, but also may lead to hard to debug behaviour. For instance, in case memory-backend-file and hugepages, madvise() and mbind() fail. Rightfully so, page is the smallest unit they can work with. And even though an error is reported, the root cause it not very clear: qemu-system-x86_64: Couldn't set property 'dump' on 'memory-backend-file': Invalid argument After this commit: qemu-system-x86_64: backend 'memory-backend-file' memory size must be multiple of 2 MiB Signed-off-by: Michal Privoznik Reviewed-by: Philippe Mathieu-Daudé Tested-by: Mario Casquero Message-ID: Signed-off-by: Paolo Bonzini --- backends/hostmem-epc.c | 1 + backends/hostmem-file.c | 1 + backends/hostmem-memfd.c | 1 + backends/hostmem.c | 10 ++++++++++ include/sysemu/hostmem.h | 2 +- 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index 735e2e1cf8..f58fcf00a1 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -36,6 +36,7 @@ sgx_epc_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return false; } + backend->aligned = true; name = object_get_canonical_path(OBJECT(backend)); ram_flags = (backend->share ? RAM_SHARED : 0) | RAM_PROTECTED; return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 3c69db7946..7e5072e33e 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -80,6 +80,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) g_assert_not_reached(); } + backend->aligned = true; name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= fb->readonly ? RAM_READONLY_FD : 0; diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 745ead0034..6a3c89a12b 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -52,6 +52,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return false; } + backend->aligned = true; name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; diff --git a/backends/hostmem.c b/backends/hostmem.c index eb9682b4a8..1edc0ede2a 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -20,6 +20,7 @@ #include "qom/object_interfaces.h" #include "qemu/mmap-alloc.h" #include "qemu/madvise.h" +#include "qemu/cutils.h" #include "hw/qdev-core.h" #ifdef CONFIG_NUMA @@ -325,6 +326,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); void *ptr; uint64_t sz; + size_t pagesize; bool async = !phase_check(PHASE_LATE_BACKENDS_CREATED); if (!bc->alloc) { @@ -336,6 +338,14 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) ptr = memory_region_get_ram_ptr(&backend->mr); sz = memory_region_size(&backend->mr); + pagesize = qemu_ram_pagesize(backend->mr.ram_block); + + if (backend->aligned && !QEMU_IS_ALIGNED(sz, pagesize)) { + g_autofree char *pagesize_str = size_to_str(pagesize); + error_setg(errp, "backend '%s' memory size must be multiple of %s", + object_get_typename(OBJECT(uc)), pagesize_str); + return; + } if (backend->merge) { qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 04b884bf42..de47ae59e4 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -74,7 +74,7 @@ struct HostMemoryBackend { uint64_t size; bool merge, dump, use_canonical_path; bool prealloc, is_mapped, share, reserve; - bool guest_memfd; + bool guest_memfd, aligned; uint32_t prealloc_threads; ThreadContext *prealloc_context; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); From a2b6a96505097c54f5db4c77f66e9c47af4dad22 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Jun 2024 11:33:06 +0200 Subject: [PATCH 1291/2884] machine, hostmem: improve error messages for unsupported features Detect early unsupported MADV_MERGEABLE and MADV_DONTDUMP, and print a clearer error message that points to the deficiency of the host. Cc: Michal Privoznik Signed-off-by: Paolo Bonzini --- backends/hostmem.c | 16 ++++++++++++++++ hw/core/machine.c | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/backends/hostmem.c b/backends/hostmem.c index 1edc0ede2a..6da3d7383e 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -170,6 +170,14 @@ static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); + if (QEMU_MADV_MERGEABLE == QEMU_MADV_INVALID) { + if (value) { + error_setg(errp, "Memory merging is not supported on this host"); + } + assert(!backend->merge); + return; + } + if (!host_memory_backend_mr_inited(backend)) { backend->merge = value; return; @@ -196,6 +204,14 @@ static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); + if (QEMU_MADV_DONTDUMP == QEMU_MADV_INVALID) { + if (!value) { + error_setg(errp, "Dumping guest memory cannot be disabled on this host"); + } + assert(backend->dump); + return; + } + if (!host_memory_backend_mr_inited(backend)) { backend->dump = value; return; diff --git a/hw/core/machine.c b/hw/core/machine.c index a0ee43ca5c..c93d249244 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -428,6 +428,10 @@ static void machine_set_dump_guest_core(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); + if (!value && QEMU_MADV_DONTDUMP == QEMU_MADV_INVALID) { + error_setg(errp, "Dumping guest memory cannot be disabled on this host"); + return; + } ms->dump_guest_core = value; } @@ -442,6 +446,10 @@ static void machine_set_mem_merge(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); + if (value && QEMU_MADV_MERGEABLE == QEMU_MADV_INVALID) { + error_setg(errp, "Memory merging is not supported on this host"); + return; + } ms->mem_merge = value; } From 5becdc0ab083e8bf346270cd34cb11b568e7538c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Jun 2024 13:06:56 +0200 Subject: [PATCH 1292/2884] hostmem: simplify the code for merge and dump properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No semantic change, just simpler control flow. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- backends/hostmem.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/backends/hostmem.c b/backends/hostmem.c index 6da3d7383e..4e5576a4ad 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -178,19 +178,16 @@ static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) return; } - if (!host_memory_backend_mr_inited(backend)) { - backend->merge = value; - return; - } - - if (value != backend->merge) { + if (!host_memory_backend_mr_inited(backend) && + value != backend->merge) { void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); qemu_madvise(ptr, sz, value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE); - backend->merge = value; } + + backend->merge = value; } static bool host_memory_backend_get_dump(Object *obj, Error **errp) @@ -212,19 +209,16 @@ static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) return; } - if (!host_memory_backend_mr_inited(backend)) { - backend->dump = value; - return; - } - - if (value != backend->dump) { + if (host_memory_backend_mr_inited(backend) && + value != backend->dump) { void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); qemu_madvise(ptr, sz, value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP); - backend->dump = value; } + + backend->dump = value; } static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) From 75997e182b695f2e3f0a2d649734952af5caf3ee Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 4 Jun 2024 18:17:55 +0200 Subject: [PATCH 1293/2884] scsi-disk: Don't silently truncate serial number Before this commit, scsi-disk accepts a string of arbitrary length for its "serial" property. However, the value visible on the guest is actually truncated to 36 characters. This limitation doesn't come from the SCSI specification, it is an arbitrary limit that was initially picked as 20 and later bumped to 36 by commit 48b62063. Similarly, device_id was introduced as a copy of the serial number, limited to 20 characters, but commit 48b62063 forgot to actually bump it. As long as we silently truncate the given string, extending the limit is actually not a harmless change, but break the guest ABI. This is the most important reason why commit 48b62063 was really wrong (and it's also why we can't change device_id to be in sync with the serial number again and use 36 characters now, it would be another guest ABI breakage). In order to avoid future breakage, don't silently truncate the serial number string any more, but just error out if it would be truncated. Buglink: https://issues.redhat.com/browse/RHEL-3542 Suggested-by: Peter Krempa Signed-off-by: Kevin Wolf Message-ID: <20240604161755.63448-1-kwolf@redhat.com> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 4bd7af9d0c..5f55ae54e4 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -58,6 +58,9 @@ #define TYPE_SCSI_DISK_BASE "scsi-disk-base" +#define MAX_SERIAL_LEN 36 +#define MAX_SERIAL_LEN_FOR_DEVID 20 + OBJECT_DECLARE_TYPE(SCSIDiskState, SCSIDiskClass, SCSI_DISK_BASE) struct SCSIDiskClass { @@ -648,8 +651,8 @@ static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) } l = strlen(s->serial); - if (l > 36) { - l = 36; + if (l > MAX_SERIAL_LEN) { + l = MAX_SERIAL_LEN; } trace_scsi_disk_emulate_vpd_page_80(req->cmd.xfer); @@ -2501,9 +2504,20 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) if (!s->vendor) { s->vendor = g_strdup("QEMU"); } + if (s->serial && strlen(s->serial) > MAX_SERIAL_LEN) { + error_setg(errp, "The serial number can't be longer than %d characters", + MAX_SERIAL_LEN); + return; + } if (!s->device_id) { if (s->serial) { - s->device_id = g_strdup_printf("%.20s", s->serial); + if (strlen(s->serial) > MAX_SERIAL_LEN_FOR_DEVID) { + error_setg(errp, "The serial number can't be longer than %d " + "characters when it is also used as the default for " + "device_id", MAX_SERIAL_LEN_FOR_DEVID); + return; + } + s->device_id = g_strdup(s->serial); } else { const char *str = blk_name(s->qdev.conf.blk); if (str && *str) { From fcce5287c009770d74f289185eadd6163a1b6a4b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 5 Jun 2024 23:25:49 +0800 Subject: [PATCH 1294/2884] stubs/meson: Fix qemuutil build when --disable-system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling without system, user, tools or guest-agent fails with the following error message: ./configure --disable-system --disable-user --disable-tools \ --disable-guest-agent error message: /usr/bin/ld: libqemuutil.a.p/util_error-report.c.o: in function `error_printf': /media/liuzhao/data/qemu-cook/build/../util/error-report.c:38: undefined reference to `error_vprintf' /usr/bin/ld: libqemuutil.a.p/util_error-report.c.o: in function `vreport': /media/liuzhao/data/qemu-cook/build/../util/error-report.c:215: undefined reference to `error_vprintf' collect2: error: ld returned 1 exit status This is because tests/bench and tests/unit both need qemuutil, which requires error_vprintf stub when system is disabled. Add error_vprintf stub into stub_ss for all cases other than disabling system. Fixes: 3a15604900c4 ("stubs: include stubs only if needed") Reported-by: Daniel P. Berrangé Signed-off-by: Zhao Liu Message-ID: <20240605152549.1795762-1-zhao1.liu@intel.com> [Include error-printf.c unconditionally. - Paolo] Signed-off-by: Paolo Bonzini --- stubs/meson.build | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/stubs/meson.build b/stubs/meson.build index 3b9d42023c..f15b48d01f 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -3,6 +3,7 @@ # below, so that it is clear who needs the stubbed functionality. stub_ss.add(files('cpu-get-clock.c')) +stub_ss.add(files('error-printf.c')) stub_ss.add(files('fdset.c')) stub_ss.add(files('iothread-lock.c')) stub_ss.add(files('is-daemonized.c')) @@ -45,17 +46,10 @@ if have_block or have_ga stub_ss.add(files('qmp-quit.c')) endif -if have_ga - stub_ss.add(files('error-printf.c')) -endif - if have_block or have_user stub_ss.add(files('qtest.c')) stub_ss.add(files('vm-stop.c')) stub_ss.add(files('vmstate.c')) - - # more symbols provided by the monitor - stub_ss.add(files('error-printf.c')) endif if have_user From 9c267239c7a307645849139389b42641655b7ece Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:50 +0200 Subject: [PATCH 1295/2884] i386/hvf: Adds support for INVTSC cpuid bit This patch adds the INVTSC bit to the Hypervisor.framework accelerator's CPUID bit passthrough allow-list. Previously, specifying +invtsc in the CPU configuration would fail with the following warning despite the host CPU advertising the feature: qemu-system-x86_64: warning: host doesn't support requested feature: CPUID.80000007H:EDX.invtsc [bit 8] x86 macOS itself relies on a fixed rate TSC for its own Mach absolute time timestamp mechanism, so there's no reason we can't enable this bit for guests. When the feature is enabled, a migration blocker is installed. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Message-ID: <20240605112556.43193-2-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 18 ++++++++++++++++++ target/i386/hvf/x86_cpuid.c | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index e493452acb..e6e916225b 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -49,6 +49,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/memalign.h" +#include "qapi/error.h" +#include "migration/blocker.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" @@ -74,6 +76,8 @@ #include "qemu/accel.h" #include "target/i386/cpu.h" +static Error *invtsc_mig_blocker; + void vmx_update_tpr(CPUState *cpu) { /* TODO: need integrate APIC handling */ @@ -221,6 +225,8 @@ int hvf_arch_init_vcpu(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); CPUX86State *env = &x86cpu->env; + Error *local_err = NULL; + int r; uint64_t reqCap; init_emu(); @@ -238,6 +244,18 @@ int hvf_arch_init_vcpu(CPUState *cpu) } } + if ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC) && + invtsc_mig_blocker == NULL) { + error_setg(&invtsc_mig_blocker, + "State blocked by non-migratable CPU device (invtsc flag)"); + r = migrate_add_blocker(&invtsc_mig_blocker, &local_err); + if (r < 0) { + error_report_err(local_err); + return r; + } + } + + if (hv_vmx_read_capability(HV_VMX_CAP_PINBASED, &hvf_state->hvf_caps->vmx_cap_pinbased)) { abort(); diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index 9380b90496..e56cd8411b 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -146,6 +146,10 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_OSVW | CPUID_EXT3_XOP | CPUID_EXT3_FMA4 | CPUID_EXT3_TBM; break; + case 0x80000007: + edx &= CPUID_APM_INVTSC; + eax = ebx = ecx = 0; + break; default: return 0; } From 0e4e622e328e203610ac300a140e43d5e2929eda Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:51 +0200 Subject: [PATCH 1296/2884] i386/hvf: Fixes some compilation warnings A bunch of function definitions used empty parentheses instead of (void) syntax, yielding the following warning when building with clang on macOS: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes] In addition to fixing these function headers, it also fixes what appears to be a typo causing a variable to be unused after initialisation. warning: variable 'entry_ctls' set but not used [-Wunused-but-set-variable] Signed-off-by: Phil Dennis-Jordan Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Message-ID: <20240605112556.43193-3-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- target/i386/hvf/vmx.h | 3 +-- target/i386/hvf/x86_decode.c | 2 +- target/i386/hvf/x86_emu.c | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 0fffcfa46c..3954ef883d 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -95,8 +95,7 @@ static void enter_long_mode(hv_vcpuid_t vcpu, uint64_t cr0, uint64_t efer) efer |= MSR_EFER_LMA; wvmcs(vcpu, VMCS_GUEST_IA32_EFER, efer); entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS); - wvmcs(vcpu, VMCS_ENTRY_CTLS, rvmcs(vcpu, VMCS_ENTRY_CTLS) | - VM_ENTRY_GUEST_LMA); + wvmcs(vcpu, VMCS_ENTRY_CTLS, entry_ctls | VM_ENTRY_GUEST_LMA); uint64_t guest_tr_ar = rvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS); if ((efer & MSR_EFER_LME) && diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index 3728d7705e..a4a28f113f 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -2111,7 +2111,7 @@ uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) return decode->len; } -void init_decoder() +void init_decoder(void) { int i; diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 3a3f0a50d0..38c782b8e3 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -1409,7 +1409,7 @@ static struct cmd_handler { static struct cmd_handler _cmd_handler[X86_DECODE_CMD_LAST]; -static void init_cmd_handler() +static void init_cmd_handler(void) { int i; for (i = 0; i < ARRAY_SIZE(handlers); i++) { @@ -1481,7 +1481,7 @@ bool exec_instruction(CPUX86State *env, struct x86_decode *ins) return true; } -void init_emu() +void init_emu(void) { init_cmd_handler(); } From f21f0cbc2c37c35680317d9dccbcbaf4aedd3a25 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:52 +0200 Subject: [PATCH 1297/2884] hvf: Consistent types for vCPU handles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS Hypervisor.framework uses different types for identifying vCPUs, hv_vcpu_t or hv_vcpuid_t, depending on host architecture. They are not just differently named typedefs for the same primitive type, but reference different-width integers. Instead of using an integer type and casting where necessary, this change introduces a typedef which resolves the active architecture’s hvf typedef. It also removes a now-unnecessary cast. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Message-ID: <20240605112556.43193-4-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- accel/hvf/hvf-accel-ops.c | 2 +- include/sysemu/hvf_int.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 6f1e27ef46..b2a37a2229 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -400,7 +400,7 @@ static int hvf_init_vcpu(CPUState *cpu) r = hv_vcpu_create(&cpu->accel->fd, (hv_vcpu_exit_t **)&cpu->accel->exit, NULL); #else - r = hv_vcpu_create((hv_vcpuid_t *)&cpu->accel->fd, HV_VCPU_DEFAULT); + r = hv_vcpu_create(&cpu->accel->fd, HV_VCPU_DEFAULT); #endif cpu->accel->dirty = true; assert_hvf_ok(r); diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 4a327fd526..30e739a2b5 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -13,8 +13,10 @@ #ifdef __aarch64__ #include +typedef hv_vcpu_t hvf_vcpuid; #else #include +typedef hv_vcpuid_t hvf_vcpuid; #endif /* hvf_slot flags */ @@ -50,7 +52,7 @@ struct HVFState { extern HVFState *hvf_state; struct AccelCPUState { - uint64_t fd; + hvf_vcpuid fd; void *exit; bool vtimer_masked; sigset_t unblock_ipi_mask; From 3e2c6727cb6b6311ee129c24b561bb87495f1a25 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:53 +0200 Subject: [PATCH 1298/2884] i386/hvf: Fixes dirty memory tracking by page granularity RX->RWX change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using x86 macOS Hypervisor.framework as accelerator, detection of dirty memory regions is implemented by marking logged memory region slots as read-only in the EPT, then setting the dirty flag when a guest write causes a fault. The area marked dirty should then be marked writable in order for subsequent writes to succeed without a VM exit. However, dirty bits are tracked on a per-page basis, whereas the fault handler was marking the whole logged memory region as writable. This change fixes the fault handler so only the protection of the single faulting page is marked as dirty. (Note: the dirty page tracking appeared to work despite this error because HVF’s hv_vcpu_run() function generated unnecessary EPT fault exits, which ended up causing the dirty marking handler to run even when the memory region had been marked RW. When using hv_vcpu_run_until(), a change planned for a subsequent commit, these spurious exits no longer occur, so dirty memory tracking malfunctions.) Additionally, the dirty page is set to permit code execution, the same as all other guest memory; changing memory protection from RX to RW not RWX appears to have been an oversight. Signed-off-by: Phil Dennis-Jordan Reviewed-by: Roman Bolshakov Tested-by: Roman Bolshakov Message-ID: <20240605112556.43193-5-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index e6e916225b..268c5734d5 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -135,9 +135,10 @@ static bool ept_emulation_fault(hvf_slot *slot, uint64_t gpa, uint64_t ept_qual) if (write && slot) { if (slot->flags & HVF_SLOT_LOG) { + uint64_t dirty_page_start = gpa & ~(TARGET_PAGE_SIZE - 1u); memory_region_set_dirty(slot->region, gpa - slot->start, 1); - hv_vm_protect((hv_gpaddr_t)slot->start, (size_t)slot->size, - HV_MEMORY_READ | HV_MEMORY_WRITE); + hv_vm_protect(dirty_page_start, TARGET_PAGE_SIZE, + HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC); } } From bf9bf2306cc8ea31b2b9bf28002aacb188ec2568 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:54 +0200 Subject: [PATCH 1299/2884] i386/hvf: In kick_vcpu use hv_vcpu_interrupt to force exit When interrupting a vCPU thread, this patch actually tells the hypervisor to stop running guest code on that vCPU. Calling hv_vcpu_interrupt actually forces a vCPU exit, analogously to hv_vcpus_exit on aarch64. Alternatively, if the vCPU thread is not running the VM, it will immediately cause an exit when it attempts to do so. Previously, hvf_kick_vcpu_thread relied upon hv_vcpu_run returning very frequently, including many spurious exits, which made it less of a problem that nothing was actively done to stop the vCPU thread running guest code. The newer, more efficient hv_vcpu_run_until exits much more rarely, so a true "kick" is needed before switching to that. Signed-off-by: Phil Dennis-Jordan Message-ID: <20240605112556.43193-6-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 268c5734d5..106ac5cbf6 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -215,6 +215,7 @@ static inline bool apic_bus_freq_is_known(CPUX86State *env) void hvf_kick_vcpu_thread(CPUState *cpu) { cpus_kick_thread(cpu); + hv_vcpu_interrupt(&cpu->accel->fd, 1); } int hvf_arch_init(void) From a59f5b2f83d4de113556e5b68cbd68c03f712679 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:55 +0200 Subject: [PATCH 1300/2884] i386/hvf: Updates API usage to use modern vCPU run function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS 10.15 introduced the more efficient hv_vcpu_run_until() function to supersede hv_vcpu_run(). According to the documentation, there is no longer any reason to use the latter on modern host OS versions, especially after 11.0 added support for an indefinite deadline. Observed behaviour of the newer function is that as documented, it exits much less frequently - and most of the original function’s exits seem to have been effectively pointless. Another reason to use the new function is that it is a prerequisite for using newer features such as in-kernel APIC support. (Not covered by this patch.) This change implements the upgrade by selecting one of three code paths at compile time: two static code paths for the new and old functions respectively, when building for targets where the new function is either not available, or where the built executable won’t run on older platforms lacking the new function anyway. The third code path selects dynamically based on runtime detected availability of the weakly-linked symbol. Signed-off-by: Phil Dennis-Jordan Message-ID: <20240605112556.43193-7-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 106ac5cbf6..2d0eef6cd9 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -427,6 +427,27 @@ static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } +static hv_return_t hvf_vcpu_run(hv_vcpuid_t vcpu_id) +{ + /* + * hv_vcpu_run_until is available and recommended from macOS 10.15+, + * HV_DEADLINE_FOREVER from 11.0. Test for availability at runtime and fall + * back to hv_vcpu_run() only where necessary. + */ +#ifndef MAC_OS_VERSION_11_0 + return hv_vcpu_run(vcpu_id); +#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 + return hv_vcpu_run_until(vcpu_id, HV_DEADLINE_FOREVER); +#else /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 */ + /* 11.0 SDK or newer, but could be < 11 at runtime */ + if (__builtin_available(macOS 11.0, *)) { + return hv_vcpu_run_until(vcpu_id, HV_DEADLINE_FOREVER); + } else { + return hv_vcpu_run(vcpu_id); + } +#endif +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -455,7 +476,7 @@ int hvf_vcpu_exec(CPUState *cpu) return EXCP_HLT; } - hv_return_t r = hv_vcpu_run(cpu->accel->fd); + hv_return_t r = hvf_vcpu_run(cpu->accel->fd); assert_hvf_ok(r); /* handle VMEXIT */ From a3c67dfc140018626f21ee507a726151cdb95ce3 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Wed, 5 Jun 2024 13:25:56 +0200 Subject: [PATCH 1301/2884] hvf: Makes assert_hvf_ok report failed expression When a macOS Hypervisor.framework call fails which is checked by assert_hvf_ok(), Qemu exits printing the error value, but not the location in the code, as regular assert() macro expansions would. This change turns assert_hvf_ok() into a macro similar to other assertions, which expands to a call to the corresponding _impl() function together with information about the expression that failed the assertion and its location in the code. Additionally, stringifying the numeric hv_return_t code is factored into a helper function that can be reused for diagnostics and debugging outside of assertions. Signed-off-by: Phil Dennis-Jordan Message-ID: <20240605112556.43193-8-phil@philjordan.eu> Signed-off-by: Paolo Bonzini --- accel/hvf/hvf-all.c | 51 +++++++++++++++++----------------------- include/sysemu/hvf_int.h | 5 +++- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index db05b81be5..c008dc2f1e 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -13,40 +13,33 @@ #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" -void assert_hvf_ok(hv_return_t ret) +const char *hvf_return_string(hv_return_t ret) +{ + switch (ret) { + case HV_SUCCESS: return "HV_SUCCESS"; + case HV_ERROR: return "HV_ERROR"; + case HV_BUSY: return "HV_BUSY"; + case HV_BAD_ARGUMENT: return "HV_BAD_ARGUMENT"; + case HV_NO_RESOURCES: return "HV_NO_RESOURCES"; + case HV_NO_DEVICE: return "HV_NO_DEVICE"; + case HV_UNSUPPORTED: return "HV_UNSUPPORTED"; +#if defined(MAC_OS_VERSION_11_0) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 + case HV_DENIED: return "HV_DENIED"; +#endif + default: return "[unknown hv_return value]"; + } +} + +void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, + const char *exp) { if (ret == HV_SUCCESS) { return; } - switch (ret) { - case HV_ERROR: - error_report("Error: HV_ERROR"); - break; - case HV_BUSY: - error_report("Error: HV_BUSY"); - break; - case HV_BAD_ARGUMENT: - error_report("Error: HV_BAD_ARGUMENT"); - break; - case HV_NO_RESOURCES: - error_report("Error: HV_NO_RESOURCES"); - break; - case HV_NO_DEVICE: - error_report("Error: HV_NO_DEVICE"); - break; - case HV_UNSUPPORTED: - error_report("Error: HV_UNSUPPORTED"); - break; -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 - case HV_DENIED: - error_report("Error: HV_DENIED"); - break; -#endif - default: - error_report("Unknown Error"); - } + error_report("Error: %s = %s (0x%x, at %s:%u)", + exp, hvf_return_string(ret), ret, file, line); abort(); } diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 30e739a2b5..5b28d17ba1 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -60,7 +60,10 @@ struct AccelCPUState { bool dirty; }; -void assert_hvf_ok(hv_return_t ret); +void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, + const char *exp); +#define assert_hvf_ok(EX) assert_hvf_ok_impl((EX), __FILE__, __LINE__, #EX) +const char *hvf_return_string(hv_return_t ret); int hvf_arch_init(void); int hvf_arch_init_vcpu(CPUState *cpu); void hvf_arch_vcpu_destroy(CPUState *cpu); From c1acad9f72d14daf918563eb77d2b31c39fbd06a Mon Sep 17 00:00:00 2001 From: Xin Li Date: Wed, 8 Nov 2023 23:20:07 -0800 Subject: [PATCH 1302/2884] target/i386: add support for FRED in CPUID enumeration FRED, i.e., the Intel flexible return and event delivery architecture, defines simple new transitions that change privilege level (ring transitions). The new transitions defined by the FRED architecture are FRED event delivery and, for returning from events, two FRED return instructions. FRED event delivery can effect a transition from ring 3 to ring 0, but it is used also to deliver events incident to ring 0. One FRED instruction (ERETU) effects a return from ring 0 to ring 3, while the other (ERETS) returns while remaining in ring 0. Collectively, FRED event delivery and the FRED return instructions are FRED transitions. In addition to these transitions, the FRED architecture defines a new instruction (LKGS) for managing the state of the GS segment register. The LKGS instruction can be used by 64-bit operating systems that do not use the new FRED transitions. WRMSRNS is an instruction that behaves exactly like WRMSR, with the only difference being that it is not a serializing instruction by default. Under certain conditions, WRMSRNS may replace WRMSR to improve performance. FRED uses it to switch RSP0 in a faster manner. Search for the latest FRED spec in most search engines with this search pattern: site:intel.com FRED (flexible return and event delivery) specification The CPUID feature flag CPUID.(EAX=7,ECX=1):EAX[17] enumerates FRED, and the CPUID feature flag CPUID.(EAX=7,ECX=1):EAX[18] enumerates LKGS, and the CPUID feature flag CPUID.(EAX=7,ECX=1):EAX[19] enumerates WRMSRNS. Add CPUID definitions for FRED/LKGS/WRMSRNS, and expose them to KVM guests. Because FRED relies on LKGS and WRMSRNS, add that to feature dependency map. Tested-by: Shan Kang Signed-off-by: Xin Li Message-ID: <20231109072012.8078-2-xin3.li@intel.com> [Fix order of dependencies, add dependencies from LM to FRED. - Paolo] Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 14 +++++++++++++- target/i386/cpu.h | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 914bef442c..bfb5a25e59 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1114,7 +1114,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "avx-vnni", "avx512-bf16", NULL, "cmpccxadd", NULL, NULL, "fzrm", "fsrs", "fsrc", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "fred", "lkgs", "wrmsrns", NULL, "amx-fp16", NULL, "avx-ifma", NULL, NULL, "lam", NULL, NULL, NULL, NULL, NULL, @@ -1701,6 +1701,18 @@ static FeatureDep feature_dependencies[] = { .from = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, .to = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, }, + { + .from = { FEAT_8000_0001_EDX, CPUID_EXT2_LM }, + .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, + }, + { + .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_LKGS }, + .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, + }, + { + .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_WRMSRNS }, + .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, + }, }; typedef struct X86RegisterInfo32 { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c64ef0c1a2..ad3577056d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -941,6 +941,12 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_1_EDX_AMX_COMPLEX (1U << 8) /* PREFETCHIT0/1 Instructions */ #define CPUID_7_1_EDX_PREFETCHITI (1U << 14) +/* Flexible return and event delivery (FRED) */ +#define CPUID_7_1_EAX_FRED (1U << 17) +/* Load into IA32_KERNEL_GS_BASE (LKGS) */ +#define CPUID_7_1_EAX_LKGS (1U << 18) +/* Non-Serializing Write to Model Specific Register (WRMSRNS) */ +#define CPUID_7_1_EAX_WRMSRNS (1U << 19) /* Do not exhibit MXCSR Configuration Dependent Timing (MCDT) behavior */ #define CPUID_7_2_EDX_MCDT_NO (1U << 5) From f88ddc40c6d8b591a357108feec52cea13796d2d Mon Sep 17 00:00:00 2001 From: Xin Li Date: Wed, 8 Nov 2023 23:20:08 -0800 Subject: [PATCH 1303/2884] target/i386: mark CR4.FRED not reserved The CR4.FRED bit, i.e., CR4[32], is no longer a reserved bit when FRED is exposed to guests, otherwise it is still a reserved bit. Tested-by: Shan Kang Signed-off-by: Xin Li Reviewed-by: Zhao Liu Message-ID: <20231109072012.8078-3-xin3.li@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ad3577056d..9a582218f4 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -261,6 +261,18 @@ typedef enum X86Seg { #define CR4_PKS_MASK (1U << 24) #define CR4_LAM_SUP_MASK (1U << 28) +#ifdef TARGET_X86_64 +#define CR4_FRED_MASK (1ULL << 32) +#else +#define CR4_FRED_MASK 0 +#endif + +#ifdef TARGET_X86_64 +#define CR4_FRED_MASK (1ULL << 32) +#else +#define CR4_FRED_MASK 0 +#endif + #define CR4_RESERVED_MASK \ (~(target_ulong)(CR4_VME_MASK | CR4_PVI_MASK | CR4_TSD_MASK \ | CR4_DE_MASK | CR4_PSE_MASK | CR4_PAE_MASK \ @@ -269,7 +281,7 @@ typedef enum X86Seg { | CR4_LA57_MASK \ | CR4_FSGSBASE_MASK | CR4_PCIDE_MASK | CR4_OSXSAVE_MASK \ | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK \ - | CR4_LAM_SUP_MASK)) + | CR4_LAM_SUP_MASK | CR4_FRED_MASK)) #define DR6_BD (1 << 13) #define DR6_BS (1 << 14) @@ -2613,6 +2625,9 @@ static inline uint64_t cr4_reserved_bits(CPUX86State *env) if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { reserved_bits |= CR4_LAM_SUP_MASK; } + if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED)) { + reserved_bits |= CR4_FRED_MASK; + } return reserved_bits; } From 2e641870170e28df28c5d9914e76ea7cab141516 Mon Sep 17 00:00:00 2001 From: Xin Li Date: Wed, 8 Nov 2023 23:20:10 -0800 Subject: [PATCH 1304/2884] vmxcap: add support for VMX FRED controls Report secondary vm-exit controls and the VMX controls used to save/load FRED MSRs. Tested-by: Shan Kang Signed-off-by: Xin Li Message-ID: <20231109072012.8078-5-xin3.li@intel.com> Signed-off-by: Paolo Bonzini --- scripts/kvm/vmxcap | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index 3fb4d5b342..44898d73c2 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -24,6 +24,7 @@ MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490 MSR_IA32_VMX_VMFUNC = 0x491 MSR_IA32_VMX_PROCBASED_CTLS3 = 0x492 +MSR_IA32_VMX_EXIT_CTLS2 = 0x493 class msr(object): def __init__(self): @@ -219,11 +220,21 @@ controls = [ 23: 'Clear IA32_BNDCFGS', 24: 'Conceal VM exits from PT', 25: 'Clear IA32_RTIT_CTL', + 31: 'Activate secondary VM-exit controls', }, cap_msr = MSR_IA32_VMX_EXIT_CTLS, true_cap_msr = MSR_IA32_VMX_TRUE_EXIT_CTLS, ), + Allowed1Control( + name = 'secondary VM-Exit controls', + bits = { + 0: 'Save IA32 FRED MSRs', + 1: 'Load IA32 FRED MSRs', + }, + cap_msr = MSR_IA32_VMX_EXIT_CTLS2, + ), + Control( name = 'VM-Entry controls', bits = { @@ -237,6 +248,7 @@ controls = [ 16: 'Load IA32_BNDCFGS', 17: 'Conceal VM entries from PT', 18: 'Load IA32_RTIT_CTL', + 23: 'Load IA32 FRED MSRs', }, cap_msr = MSR_IA32_VMX_ENTRY_CTLS, true_cap_msr = MSR_IA32_VMX_TRUE_ENTRY_CTLS, From ef202d64c3020f3df03c39d3ad688732d81aaae8 Mon Sep 17 00:00:00 2001 From: Xin Li Date: Wed, 8 Nov 2023 23:20:11 -0800 Subject: [PATCH 1305/2884] target/i386: enumerate VMX nested-exception support Allow VMX nested-exception support to be exposed in KVM guests, thus nested KVM guests can enumerate it. Tested-by: Shan Kang Signed-off-by: Xin Li Message-ID: <20231109072012.8078-6-xin3.li@intel.com> Signed-off-by: Paolo Bonzini --- scripts/kvm/vmxcap | 1 + target/i386/cpu.c | 1 + target/i386/cpu.h | 1 + 3 files changed, 3 insertions(+) diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index 44898d73c2..508be19c75 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -117,6 +117,7 @@ controls = [ 54: 'INS/OUTS instruction information', 55: 'IA32_VMX_TRUE_*_CTLS support', 56: 'Skip checks on event error code', + 58: 'VMX nested exception support', }, msr = MSR_IA32_VMX_BASIC, ), diff --git a/target/i386/cpu.c b/target/i386/cpu.c index bfb5a25e59..383230fa47 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1492,6 +1492,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [54] = "vmx-ins-outs", [55] = "vmx-true-ctls", [56] = "vmx-any-errcode", + [58] = "vmx-nested-exception", }, .msr = { .index = MSR_IA32_VMX_BASIC, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9a582218f4..8ff27e933d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1071,6 +1071,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define MSR_VMX_BASIC_INS_OUTS (1ULL << 54) #define MSR_VMX_BASIC_TRUE_CTLS (1ULL << 55) #define MSR_VMX_BASIC_ANY_ERRCODE (1ULL << 56) +#define MSR_VMX_BASIC_NESTED_EXCEPTION (1ULL << 58) #define MSR_VMX_MISC_PREEMPTION_TIMER_SHIFT_MASK 0x1Full #define MSR_VMX_MISC_STORE_LMA (1ULL << 5) From 4ebd98eb3ade5957a842da1420bda012eeeaab9c Mon Sep 17 00:00:00 2001 From: Xin Li Date: Wed, 8 Nov 2023 23:20:12 -0800 Subject: [PATCH 1306/2884] target/i386: Add get/set/migrate support for FRED MSRs FRED CPU states are managed in 9 new FRED MSRs, in addtion to a few existing CPU registers and MSRs, e.g., CR4.FRED and MSR_IA32_PL0_SSP. Save/restore/migrate FRED MSRs if FRED is exposed to the guest. Tested-by: Shan Kang Signed-off-by: Xin Li Message-ID: <20231109072012.8078-7-xin3.li@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 22 +++++++++++++++++++ target/i386/kvm/kvm.c | 49 +++++++++++++++++++++++++++++++++++++++++++ target/i386/machine.c | 28 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8ff27e933d..29d799adfd 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -538,6 +538,17 @@ typedef enum X86Seg { #define MSR_IA32_XFD 0x000001c4 #define MSR_IA32_XFD_ERR 0x000001c5 +/* FRED MSRs */ +#define MSR_IA32_FRED_RSP0 0x000001cc /* Stack level 0 regular stack pointer */ +#define MSR_IA32_FRED_RSP1 0x000001cd /* Stack level 1 regular stack pointer */ +#define MSR_IA32_FRED_RSP2 0x000001ce /* Stack level 2 regular stack pointer */ +#define MSR_IA32_FRED_RSP3 0x000001cf /* Stack level 3 regular stack pointer */ +#define MSR_IA32_FRED_STKLVLS 0x000001d0 /* FRED exception stack levels */ +#define MSR_IA32_FRED_SSP1 0x000001d1 /* Stack level 1 shadow stack pointer in ring 0 */ +#define MSR_IA32_FRED_SSP2 0x000001d2 /* Stack level 2 shadow stack pointer in ring 0 */ +#define MSR_IA32_FRED_SSP3 0x000001d3 /* Stack level 3 shadow stack pointer in ring 0 */ +#define MSR_IA32_FRED_CONFIG 0x000001d4 /* FRED Entrypoint and interrupt stack level */ + #define MSR_IA32_BNDCFGS 0x00000d90 #define MSR_IA32_XSS 0x00000da0 #define MSR_IA32_UMWAIT_CONTROL 0xe1 @@ -1723,6 +1734,17 @@ typedef struct CPUArchState { target_ulong cstar; target_ulong fmask; target_ulong kernelgsbase; + + /* FRED MSRs */ + uint64_t fred_rsp0; + uint64_t fred_rsp1; + uint64_t fred_rsp2; + uint64_t fred_rsp3; + uint64_t fred_stklvls; + uint64_t fred_ssp1; + uint64_t fred_ssp2; + uint64_t fred_ssp3; + uint64_t fred_config; #endif uint64_t tsc_adjust; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 0852ed077f..b563520981 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3376,6 +3376,17 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, MSR_KERNELGSBASE, env->kernelgsbase); kvm_msr_entry_add(cpu, MSR_FMASK, env->fmask); kvm_msr_entry_add(cpu, MSR_LSTAR, env->lstar); + if (env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED) { + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP0, env->fred_rsp0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP1, env->fred_rsp1); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP2, env->fred_rsp2); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP3, env->fred_rsp3); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_STKLVLS, env->fred_stklvls); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP1, env->fred_ssp1); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP2, env->fred_ssp2); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP3, env->fred_ssp3); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_CONFIG, env->fred_config); + } } #endif @@ -3848,6 +3859,17 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, MSR_KERNELGSBASE, 0); kvm_msr_entry_add(cpu, MSR_FMASK, 0); kvm_msr_entry_add(cpu, MSR_LSTAR, 0); + if (env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED) { + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP0, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP1, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP2, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP3, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_STKLVLS, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP1, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP2, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP3, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_CONFIG, 0); + } } #endif kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); @@ -4069,6 +4091,33 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_LSTAR: env->lstar = msrs[i].data; break; + case MSR_IA32_FRED_RSP0: + env->fred_rsp0 = msrs[i].data; + break; + case MSR_IA32_FRED_RSP1: + env->fred_rsp1 = msrs[i].data; + break; + case MSR_IA32_FRED_RSP2: + env->fred_rsp2 = msrs[i].data; + break; + case MSR_IA32_FRED_RSP3: + env->fred_rsp3 = msrs[i].data; + break; + case MSR_IA32_FRED_STKLVLS: + env->fred_stklvls = msrs[i].data; + break; + case MSR_IA32_FRED_SSP1: + env->fred_ssp1 = msrs[i].data; + break; + case MSR_IA32_FRED_SSP2: + env->fred_ssp2 = msrs[i].data; + break; + case MSR_IA32_FRED_SSP3: + env->fred_ssp3 = msrs[i].data; + break; + case MSR_IA32_FRED_CONFIG: + env->fred_config = msrs[i].data; + break; #endif case MSR_IA32_TSC: env->tsc = msrs[i].data; diff --git a/target/i386/machine.c b/target/i386/machine.c index c3ae320814..39f8294f27 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1544,6 +1544,33 @@ static const VMStateDescription vmstate_msr_xfd = { }; #ifdef TARGET_X86_64 +static bool intel_fred_msrs_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED); +} + +static const VMStateDescription vmstate_msr_fred = { + .name = "cpu/fred", + .version_id = 1, + .minimum_version_id = 1, + .needed = intel_fred_msrs_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.fred_rsp0, X86CPU), + VMSTATE_UINT64(env.fred_rsp1, X86CPU), + VMSTATE_UINT64(env.fred_rsp2, X86CPU), + VMSTATE_UINT64(env.fred_rsp3, X86CPU), + VMSTATE_UINT64(env.fred_stklvls, X86CPU), + VMSTATE_UINT64(env.fred_ssp1, X86CPU), + VMSTATE_UINT64(env.fred_ssp2, X86CPU), + VMSTATE_UINT64(env.fred_ssp3, X86CPU), + VMSTATE_UINT64(env.fred_config, X86CPU), + VMSTATE_END_OF_LIST() + } + }; + static bool amx_xtile_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1747,6 +1774,7 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_pdptrs, &vmstate_msr_xfd, #ifdef TARGET_X86_64 + &vmstate_msr_fred, &vmstate_amx_xtile, #endif &vmstate_arch_lbr, From 888788dd7620f8e3fa14108eab2d708c16460266 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 6 Jun 2024 16:54:36 +0800 Subject: [PATCH 1307/2884] docs: i386: pc: Avoid mentioning limit of maximum vCPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Different versions of PC machine support different maximum vCPUs, and even different features have limits on the maximum number of vCPUs ( For example, if x2apic is not enabled in the TCG case, the maximum of 255 vCPUs are supported). It is difficult to list the maximum vCPUs under all restrictions. Thus, to avoid confusion, avoid mentioning specific maximum vCPU number limitations here. Suggested-by: Daniel P. Berrangé Signed-off-by: Zhao Liu Reviewed-by: Daniel P. Berrangé Message-ID: <20240606085436.2028900-1-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini --- docs/system/target-i386-desc.rst.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/system/target-i386-desc.rst.inc b/docs/system/target-i386-desc.rst.inc index 319e540573..ae312b1c1e 100644 --- a/docs/system/target-i386-desc.rst.inc +++ b/docs/system/target-i386-desc.rst.inc @@ -36,7 +36,8 @@ The QEMU PC System emulator simulates the following peripherals: - PCI UHCI, OHCI, EHCI or XHCI USB controller and a virtual USB-1.1 hub. -SMP is supported with up to 255 CPUs (and 4096 CPUs for PC Q35 machine). +SMP is supported with a large number of virtual CPUs (upper limit is +configuration dependent). QEMU uses the PC BIOS from the Seabios project and the Plex86/Bochs LGPL VGA BIOS. From 4b77512b2782a6b48691d4341991491de26415de Mon Sep 17 00:00:00 2001 From: John Allen Date: Mon, 3 Jun 2024 19:36:20 +0000 Subject: [PATCH 1308/2884] i386: Fix MCE support for AMD hosts For the most part, AMD hosts can use the same MCE injection code as Intel, but there are instances where the qemu implementation is Intel specific. First, MCE delivery works differently on AMD and does not support broadcast. Second, kvm_mce_inject generates MCEs that include a number of Intel specific status bits. Modify kvm_mce_inject to properly generate MCEs on AMD platforms. Reported-by: William Roche Signed-off-by: John Allen Message-ID: <20240603193622.47156-2-john.allen@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 ++ target/i386/helper.c | 4 ++++ target/i386/kvm/kvm.c | 39 +++++++++++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 29d799adfd..e6d5d1b483 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -377,6 +377,8 @@ typedef enum X86Seg { #define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */ #define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */ #define MCI_STATUS_AR (1ULL<<55) /* Action required */ +#define MCI_STATUS_DEFERRED (1ULL<<44) /* Deferred error */ +#define MCI_STATUS_POISON (1ULL<<43) /* Poisoned data consumed */ /* MISC register defines */ #define MCM_ADDR_SEGOFF 0 /* segment offset */ diff --git a/target/i386/helper.c b/target/i386/helper.c index f9d1381f90..01a268a30b 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -91,6 +91,10 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env) int family = 0; int model = 0; + if (IS_AMD_CPU(env)) { + return 0; + } + cpu_x86_version(env, &family, &model); if ((family == 6 && model >= 14) || family > 6) { return 1; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index b563520981..55a9e8a70c 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -638,17 +638,40 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) { CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | - MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S; - uint64_t mcg_status = MCG_STATUS_MCIP; + uint64_t status = MCI_STATUS_VAL | MCI_STATUS_EN | MCI_STATUS_MISCV | + MCI_STATUS_ADDRV; + uint64_t mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV; int flags = 0; - if (code == BUS_MCEERR_AR) { - status |= MCI_STATUS_AR | 0x134; - mcg_status |= MCG_STATUS_RIPV | MCG_STATUS_EIPV; + if (!IS_AMD_CPU(env)) { + status |= MCI_STATUS_S | MCI_STATUS_UC; + if (code == BUS_MCEERR_AR) { + status |= MCI_STATUS_AR | 0x134; + mcg_status |= MCG_STATUS_EIPV; + } else { + status |= 0xc0; + } } else { - status |= 0xc0; - mcg_status |= MCG_STATUS_RIPV; + if (code == BUS_MCEERR_AR) { + status |= MCI_STATUS_UC | MCI_STATUS_POISON; + mcg_status |= MCG_STATUS_EIPV; + } else { + /* Setting the POISON bit for deferred errors indicates to the + * guest kernel that the address provided by the MCE is valid + * and usable which will ensure that the guest kernel will send + * a SIGBUS_AO signal to the guest process. This allows for + * more desirable behavior in the case that the guest process + * with poisoned memory has set the MCE_KILL_EARLY prctl flag + * which indicates that the process would prefer to handle or + * shutdown due to the poisoned memory condition before the + * memory has been accessed. + * + * While the POISON bit would not be set in a deferred error + * sent from hardware, the bit is not meaningful for deferred + * errors and can be reused in this scenario. + */ + status |= MCI_STATUS_DEFERRED | MCI_STATUS_POISON; + } } flags = cpu_x86_support_mca_broadcast(env) ? MCE_INJECT_BROADCAST : 0; From 2ba8b7ee63589d4063c3b8dff3b70dbf9e224fc6 Mon Sep 17 00:00:00 2001 From: John Allen Date: Mon, 3 Jun 2024 19:36:21 +0000 Subject: [PATCH 1309/2884] i386: Add support for SUCCOR feature Add cpuid bit definition for the SUCCOR feature. This cpuid bit is required to be exposed to guests to allow them to handle machine check exceptions on AMD hosts. ---- v2: - Add "succor" feature word. - Add case to kvm_arch_get_supported_cpuid for the SUCCOR feature. Reported-by: William Roche Reviewed-by: Joao Martins Signed-off-by: John Allen Message-ID: <20240603193622.47156-3-john.allen@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 18 +++++++++++++++++- target/i386/cpu.h | 4 ++++ target/i386/kvm/kvm.c | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 383230fa47..c5a532a254 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1180,6 +1180,22 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .tcg_features = TCG_APM_FEATURES, .unmigratable_flags = CPUID_APM_INVTSC, }, + [FEAT_8000_0007_EBX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, "succor", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { .eax = 0x80000007, .reg = R_EBX, }, + .tcg_features = 0, + .unmigratable_flags = 0, + }, [FEAT_8000_0008_EBX] = { .type = CPUID_FEATURE_WORD, .feat_names = { @@ -6887,7 +6903,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x80000007: *eax = 0; - *ebx = 0; + *ebx = env->features[FEAT_8000_0007_EBX]; *ecx = 0; *edx = env->features[FEAT_8000_0007_EDX]; break; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e6d5d1b483..6786055ec6 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -630,6 +630,7 @@ typedef enum FeatureWord { FEAT_7_1_EAX, /* CPUID[EAX=7,ECX=1].EAX */ FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */ FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ + FEAT_8000_0007_EBX, /* CPUID[8000_0007].EBX */ FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */ FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */ @@ -982,6 +983,9 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, /* Packets which contain IP payload have LIP values */ #define CPUID_14_0_ECX_LIP (1U << 31) +/* RAS Features */ +#define CPUID_8000_0007_EBX_SUCCOR (1U << 1) + /* CLZERO instruction */ #define CPUID_8000_0008_EBX_CLZERO (1U << 0) /* Always save/restore FP error pointers */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 55a9e8a70c..56d8e2a89e 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -532,6 +532,8 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, */ cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); ret |= cpuid_1_edx & CPUID_EXT2_AMD_ALIASES; + } else if (function == 0x80000007 && reg == R_EBX) { + ret |= CPUID_8000_0007_EBX_SUCCOR; } else if (function == KVM_CPUID_FEATURES && reg == R_EAX) { /* kvm_pv_unhalt is reported by GET_SUPPORTED_CPUID, but it can't * be enabled without the in-kernel irqchip From 1ea1432199cdddbb4e7f98cee71cabf50a9516f2 Mon Sep 17 00:00:00 2001 From: John Allen Date: Mon, 3 Jun 2024 19:36:22 +0000 Subject: [PATCH 1310/2884] i386: Add support for overflow recovery Add cpuid bit definition for overflow recovery. This is needed in the case where a deferred error has been sent to the guest, a guest process accesses the poisoned memory, but the machine_check_poll function has not yet handled the original deferred error. If overflow recovery is not set in this case, when we handle the uncorrected error from the poisoned memory access, the overflow bit will be set and will result in the guest being shut down. By the time the MCE reaches the guest, the overflow has been handled by the host and has not caused a shutdown, so include the bit unconditionally. Signed-off-by: John Allen Message-ID: <20240603193622.47156-4-john.allen@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 1 + target/i386/kvm/kvm.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c5a532a254..7466217d5e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1183,7 +1183,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_8000_0007_EBX] = { .type = CPUID_FEATURE_WORD, .feat_names = { - NULL, "succor", NULL, NULL, + "overflow-recov", "succor", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 6786055ec6..8fe28b67e0 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -984,6 +984,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_14_0_ECX_LIP (1U << 31) /* RAS Features */ +#define CPUID_8000_0007_EBX_OVERFLOW_RECOV (1U << 0) #define CPUID_8000_0007_EBX_SUCCOR (1U << 1) /* CLZERO instruction */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 56d8e2a89e..912f5d5a6b 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -533,7 +533,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); ret |= cpuid_1_edx & CPUID_EXT2_AMD_ALIASES; } else if (function == 0x80000007 && reg == R_EBX) { - ret |= CPUID_8000_0007_EBX_SUCCOR; + ret |= CPUID_8000_0007_EBX_OVERFLOW_RECOV | CPUID_8000_0007_EBX_SUCCOR; } else if (function == KVM_CPUID_FEATURES && reg == R_EAX) { /* kvm_pv_unhalt is reported by GET_SUPPORTED_CPUID, but it can't * be enabled without the in-kernel irqchip From 1f97715c8390e582f154d8b579c70779bd8c9bdf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 9 Aug 2023 01:10:34 +0200 Subject: [PATCH 1311/2884] Revert "python: use vendored tomli" Now that Ubuntu 20.04 is not included anymore, there is no need to ship it as part of QEMU; Ubuntu 22.04 includes it and Leap users anyway need to install all the required dependencies from PyPI. This mostly reverts commit ec77ee7634de123b7c899739711000fd21dab68b, with just some changes to the wording. Signed-off-by: Paolo Bonzini --- configure | 4 ---- docs/devel/build-system.rst | 13 ++++++------- python/scripts/vendor.py | 3 --- python/wheels/tomli-2.0.1-py3-none-any.whl | Bin 12757 -> 0 bytes 4 files changed, 6 insertions(+), 14 deletions(-) delete mode 100644 python/wheels/tomli-2.0.1-py3-none-any.whl diff --git a/configure b/configure index 4d01a42ba6..5ad1674ca5 100755 --- a/configure +++ b/configure @@ -955,10 +955,6 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py" # Finish preparing the virtual environment using vendored .whl files -if $python -c 'import sys; sys.exit(sys.version_info >= (3,11))'; then - $mkvenv ensure --dir "${source_path}/python/wheels" \ - 'tomli>=1.2.0' || exit 1 -fi $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ ${source_path}/pythondeps.toml meson || exit 1 diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 09caf2f8e1..f4fd76117d 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -185,14 +185,13 @@ Bundled Python packages Python packages that are **mandatory** dependencies to build QEMU, but are not available in all supported distros, are bundled with the -QEMU sources. Currently this includes Meson (outdated in CentOS 8 -and derivatives, Ubuntu 20.04 and 22.04, and openSUSE Leap) and tomli -(absent in Ubuntu 20.04). +QEMU sources. The only one is currently Meson (outdated in Ubuntu +22.04 and openSUSE Leap). -If you need to update these, please do so by modifying and rerunning -``python/scripts/vendor.py``. This script embeds the sha256 hash of -package sources and checks it. The pypi.org web site provides an easy -way to retrieve the sha256 hash of the sources. +In order to include a new or updated wheel, modify and rerun the +``python/scripts/vendor.py`` script. The script embeds the +sha256 hash of package sources and checks it. The pypi.org web site +provides an easy way to retrieve the sha256 hash of the sources. Stage 2: Meson diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index 1038b14ae0..07aff97cca 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -43,9 +43,6 @@ def main() -> int: packages = { "meson==1.2.3": "4533a43c34548edd1f63a276a42690fce15bde9409bcf20c4b8fa3d7e4d7cac1", - - "tomli==2.0.1": - "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", } vendor_dir = Path(__file__, "..", "..", "wheels").resolve() diff --git a/python/wheels/tomli-2.0.1-py3-none-any.whl b/python/wheels/tomli-2.0.1-py3-none-any.whl deleted file mode 100644 index 29670b98d16e2bc770d4fea718582e1dc0dd8aca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12757 zcmZ{K19T?cvi6(2v8{=1TN67I+qN;W?TKyMwlT47dy-6?{B!QT=Y02k_dnfhuU_4& zpQrb(uBz4bbjeEt!O#Ez02H7}RYJMAoa`MF1OSNoGm!sWb+)sywqVfHv#_;r*3+Z6 zch`}hi0BtU>O7{2a9ah3PcNtq)hCA71L7L+!5ht;=`*8pC+Iw0f6veEbjEa6aZ!t{ z-7eZr5K1xTPCr1$E}2eui*cb3n04x0V575s9eHtd3AnP2al@G=Ow7|XTHLl>i@Uu~ zQ2;lqTW6Tfh`@q@wZc7Dx~3&*V^rA36cr>aDz~D#4UVmCEh*IfIByKl8D&D${Pjy<~={mz=uH0 z;OKxG#BXMAx=t8hwKuh0k+D=iu>QdmGLYQ_>$QTHK(gPU>jK%Hve4f5qUJ)GmUw5^ zilT(pi7l$g-h5`|Q5ZK@Iy|sLApdVx1nrKoh{6B>EYtx2q`$0aZ{X-;;`qmiL%gew z>k{$TZxNPH{>B-G()C>|lj^cyTE!*UBx4BSTcyxf1Z1UB>@D~ux!JK>x35$}xoWyMldsqH<45+0P?zA!=C zrmO~iH3qtRxp@te>kbb1y{B()f+NS~wt=ylY5e5Z@z!@|J%+wpvA{KvWSnT=G#FS9 zx`qzx-jZob`TJmBX$^<>dw!!_f*H6^ke z+osYFw-rsfaF|qDyxGvI*_|vQ^>NGWh1~?hFJ~V(sT=WuCT4KQ^}VQrwHrE4@PeNa zJ@3x2Uu>Q?ylKO2yADm5HTL2^D}yv6kP9_iN{F03?unuwnodf2mgb1uY)iSNRll9v zXZ+n&d*q&2W6FEl)Eim{s+XBAt(HCe9O@hsU^(M;|h~dZgYrt!2 zm7oomE|;Ut2;g?~{N3WDXo5e3_L<}k1wsCeS5kjtK)m@r-yOb8gRns_Fw#8rG zE=5nBoH$F!&665^%LM5GYn_l5||zSlb;T zPfBsV-1?-Wb_u-H@@SvC@GGa&q2iPKgvZp;{OlgV^!`OwS1b&0`>$x3p7%)<*zJbL zyFg7sq;WO;yuv~w%=ZDnrcdhcRqW|CsuNskql<)quW*4wBz1ujWy0xCtXouY zkK9448_4P&GU{B`4KWU*h^L;JB-;dc)>&p*u-T?wANcCm&Yl;-C!57t)h7*LhkX*z zoeYgBmWEI$_|=^}SH)Wa9m97qe53&sL>yT1`skfRvP6JgCO8P=41{4CBmzI6jst^K z8J8wzB2qYS^xfcbK>RBi^(N_+FWM0`hgMTxE zL|v$y=eVB49Qew9_W|LUFanUp!|!%7OaPh1&wro~!kV1zhD)C}-H0T!6uu^I?f^p|7ZDsKSgrTwNWJir;O)LV=?7bwZ~3-lAZrNHYo2k`4GU27 z`X04*5_Afhs%sN%;1Mmi0JX!e(AWEMbULIE@+6>(esH%$x}Jb(~vJWJ7@Qf{$44 z7e~`MjMB^Qul>&``bN2BBtbO&yYN&p;~W+Q)6}oI*a(bF=-i0mR1#y5_sE>#Sikl~ zgsA#kaDi~Ytf$zBvbkx!qnNJ+kr-bt=U1Q5>Qf?T7lSO?4Bigg1<{PF*CpLsb7x@a zf|0%Z_q(M-4E05Ho@`*^va_Y>ir^La2nE}xYHb~4UHl2hi&x<9ifsjhIC8A^1NA*P zae)d_0s_E`6^YE?dc<1pES|*%EB&&SHi+apoIDWuUbTU=qSOU|``o5SZlVu@|F)XX zFOYuW0pw-mOX2&3RFpBMCTF5f>al_lZRZ$LMLv0Uict{f9(-I}S(|zTk8K4*Fghfu zMW_@-9w6RFTX%*o4>EYs)IxqqPs}o9lvVF4i3j`^rG+hy$vB|#0>T<&#Gua^`S?$Q z-M%0Oy?aos(hIu zNyBgOO4B$lGAspX1-ABRdI1n)kREccqQQ%*&@pl4jt`vs3Pfr{utt-{gI7;tWIh8RC+#@megPSS`mU#fH7)V&_Opu`m;3Y82H>SSO@ zXwpwC!!D5^Bz`DbM1(GADdp+uudt_;GAk#5N0@?!sSyrGdTsU5^GW%1JUpST1Qx(( z=QqK@q*Z~C<*OjoQ-*Ufp0x%D4g2-iV(UQ&Xf_9#5#p3+EwO>w1<@vU&DYLpLNnIp zR8%VpfUUZFuqV_z3Yp&lWOTmr$vt$c9Wr%EkV}pj>+7q7dr$(Eq`-{f=f0tv z5TPhWfmG`}P=W_sTY4i>yd^?lZo!DC{wA~3p}x(pBLYmC#K3c{;ykf9Wx7}+XNhYd z!8GJk*-1VHc!UF-81|bQ8I+m7@0-ihnjwAvt(7tYYvArt1@9%Sdpy3ue)|f-JdYe^ zVrx~anRs*l63W}}f0EvAi`<|TProaOq;QZe7m!O3Z#Gh?)e<$9i}Voaj9=I@;y*g= zzzB(Q(74XIHs@v8$By_yK1a4nV2%P_|I<(N;5n7>E0yzLXc>OIfOJhHj<93{N0f;Z zm1VoE(ZmCJ1CHsE8tIS;n<@brB(3*@GKj9!L!m2Pz1it#IG()=hYT@Q-U9gYfg=9( z*0KhumaYq#jrr*mrP)u&8WZf0j6q(m7ilOV{eGC0nm^7sOxLlQO-Lom7y=uM4FCaaPhQVi1j8TY21d4uA-&zcAjg)WTa8$ zCbQG1WXx1bYxTQ{W@V{wy-ih>sw&T-;~v(O=RF!)JqqA;plP>jili1riMuqviFPv& zbXddoJQZxbkT z`^BTGlo-#6cq3B!Xa(4ic(TU#Ke|lib)r5#zXt0vhL$H##s?qMHJq$uiWUtq@_1C{ zNbouNw>boNqv4ff&!M6)Q$!5LPUJ&WBt5irMSS0)b%{ink)g-$V(%?|2tXozK)*GU zE)Ucf&-{(oF#SN7n%q+C&((i|)+ABgX#@lhSX%^g&>)mZUu}n{amF^tRPv7o@bT^% zwn+6G)S6p>sDTT7FB_KQfrBrxTgMEp-XgoIYc0BrF7B^ofthG9qG#CemO|7d_t?)w z0T))-K@nTua0;0BPlxs-T#cP{`6a~dh^NC2`s4wv=c2~LOvjR&0_HsF+Yp5Fv$;z1 zRsCiy)m7Zd*2tT^d86^Hz6Q<3N9kUF;PpdQK4Oj*n;4H_gh&UmI$vYLMp+Bxcdhyu z4KBnBDVdpQros#14&PFryt=ADdmxUD(4%oWv=*KWWbl|jA;xoa#C1?5KSdc^e6*NH z0addwfm6y_@<~y??luw8py(m0nJTJzEVhY~*P4rCi8#*9P^4YK+JrsKaFG+9FAd%BU-wlFb>s)bF!qtt<@iZ#a}4moI%LMZK`m z^|)dI{p7m70> z<0vjX*2>j$i-j`yf_TvIq|iYFn#!of$=$yPR<|ispw1X6@m2pQFg(J}c!r3uw_+Nh zD9T}9>tQZw|B^nrrTb->U)%?4--ANX$$BcnMrE7PgC|y>#jdzKVrQx-?2-&ex+BSw zRh8C9=@xY|zphKO(6v$HLgccs`s|KeT!|#)xGKULNx5>3{p>!{(dzA@i%ZN_az*#& zT6xebM&peVw1n}TkyKT;WqBKGO8&%`1{|lnOBTyxFA11BE{Zil`)jAQvS=Ze1OjJX zIT-so=E8%8IG>Axxu?~|I6gZj|1oES;L%Yk;{G|2;F_(%2#9BhyvigEV9_CcOL{UE$XCE&$PK|#9a9zpTt8lQ2SXi zBkJ|&33G7X=O|;X0kUHX__knsWVhxf`)j+~Y8x&d&-3+ecyndp`ve>^XzD(6SMzAb zkV5fXca+`C-b?TVj&7wvE|8GJT`9BY05ND<;IVlf?C=g>{zFq~htXow&FfCnsCll+ zo5nmtY(QIbMITq2kK<8_?5qLO#QrQE%HTjoQ6^7S2DKZ`UHM|Yk_@P2fTGnTQr|NL zRkuU0^v$oRr0RXrFV3?TGl{j)%o^=+-aIo7eC1^ zDsnz-957J?jq$ma#%@VxyYxA*wR6K`Xjj7!C!zc~x+7jVMpwu>bPzgH%gKDzJ^FQ@ zSO0n20f80WN?yt@SLmhJWufJ6HXhMvK{_kJ{>Da@lKUb~kD5|*TWqBVv#x8dbA^4^ ziu^y45qWwH2c9!$+wwy7lWNpm2?B4YQKo0o?kTuWv-gn7Ox6-Vf833#L7EvHb&JkF ze6xlvB;kVtDqYRSUtPN~zshd2l8}KDEv2(a4jHm-dLwhju{2rJi)+|`tN$k9u?@4& zAXlI&x5*YWwbw+dR<buC^u01$(^=7v-jq$HUTL>~hB&@A zV?jjCP=}o)YRcZ^=dvc5z-LurkCjuye5qEc%nPv=pttZ?cgS?rAuA6!n!GZno@Ea2 zIPI;6Ql)9D6L6^q(rb*RNz=`qkt~)K1$G^%60<4e=c{feFb>fM4{? z`%%qB@pWgP963UwH#DdZFLmAocN)D`Z$^7mGKJbxooC{ADx|8VJhc14&e%+WHs88; zw5X#!`3~!?e%0rKh$LD{XCh>WSHl2xLOj14dFMmEkaKg|3EYiMO>oa8x_FDsPUK`S zCMV%~Ue9@%ky1bkQSxR*rZxxJg2#Jlb$zAsCS|{h;8!+(`sG&+eYb0a)Ls z0-q_pjtWG>bSr9=+;n-QtZNQDzA{IYSS4=~mZ$}`yV#1kjvm~2sYQ{y4>pus)<3R)x1msCP z-Sp(Zx-3{OqEO$RFS6OwW-jfa2r=3XPcjKbH$u#$^!!rUZ&{H_tmkgT=Q0$QCY8$_}tD3an(x!31U=y}OT#05b zqkC>8%PX<;dl4%1B>N)9LA-T^C%3pX63S3zZjz^$=Z^{#7cC`G8YOMbu|!ZywIo4v z_wDM!F!TK`TG)f#>PfJ7_r*R@iAX8my|M^TaZc%dVgf@zvzy14b{J4}Pyx63F+y(J zzZ`vt>t@)7jJB~IA*|MvJwUa&ps;{7Iu%Kx=aVU(MyrPlQ#2B`IY_1{ETP7~g?UsV zF_@*A(w^WaHV^F22x~CK*sFJ?#T9xF`uzHcmPm2?xLS9GOtsC?VxO%al%oL||4E_M z4Z7;2b}pqfgqSmv{MtT$fns+Y_fCPooJe;&((!%dYfnJ|ZPs^R_q*RVMSZigvszCj zGj?7374=fOZwu+%A~e_(__}d0k>H%&6k!V5|8U zYq~?1$HOCePPgQL3=F%6S#ekwq3dRi6$`Hr$5Hl3bmK}uWCW4w=gSg>YkZP{bt#(J& zY!93i+r*@RkCBsRMx8it^aR^KJvrCFsiTtB+I79asvw zYmK6IJ?{h){Osi7PtN#}6nB4yQB&!=!-&ZnRM^>30&sJTl-AK%yNAbX_i$->wHU=g zsP?WN5tlT>2t#Kv`b>5)l;BmEo<_9tTX+nfu2(K)l5s$d`_S?7cxj_puukOG{KG zh}Dgcc!!(ktFBhn9g-+dTk=gSj?Vf$P!rZ~3yQ5uznyq>!s2vY=Ich}zLeI+OIoc1 z!ti!UY4%LmAYi(`UGsZ&=WyFY9ccS@bM_n^JU*W;1@n_(dOX9Fh73_UJm!x2> zH+lK5@dI|vXRnU?B+wVbb7cf5*C2$`@^>yac{l(G^Tm`$EFl<%4;#^^qClfb-)*{! zuA#oV&mwb3`S2|=4OIhx<*#W8Y_SNI#u$zI2&{1D1T7&HM+w`faj~mcSNe+~#Uqd{ zgPpftulU?hgj~cqnw)QlYjg9FkYEF43_gCyLwH+5?%;f%Z1k4AMQHcG;Ofv-uM|N; z8SCVQD2LF|g0cLVUXU@}sX-Iy1T)B?9?C!2(2m2{Zm^C3%8V$ZB8=r z(kKW{wPph7TUgPW%{?mm1TWE{=D=t23IyDP`31E?WLeP$&J|%!s&;keGS|6bSVH#; zg=Y9&pk(=_a*>k0k*1H=2QSflRMMDsxx}4jG*1fKAR^OKM|oup08ZJ#n_(AuAcChF z0<8uclEkc+Ri~{AJB5aCvWIoaLG3)2mIOJVT2*=~bSF`Ybavw&Jp@tAonz`Jl3q-C zO^pOX%+H93riid(KxMuj8a3|=?=2r)hK$`PAM)@XwX7I=H13!a3Xv+>8OR4fSa+kd zh?Y>Y93RIC^ghqFoAZhteNn1EhS(fzbdZ+1Q35<(ss{Lulnm z%??QF!ONlRv*~h%6^)Iu9qV2j_-uLNInnqmx^p&$bWeGBL7q35L+~x9!3~cMsl9t3 zpYf~dly5Pl5TCH_W|Dc;uXM)M)FD~FA}5bO$>ttyiS7aXd!A~zMll=ulb=5R48*_k zl(W0NiPN7{#TVUYIUoQha{Y=Ppp7?wgmpsHni|BeABGsbGf=Fm{iCVT++_5_o5SYR zW$R}KdS?4_>;sQCF@-jPm~jD3p6#yN$>tem3tH`u)TCmD^IKc36<(X{yvHbi{hkfm z!O@}*3YX!DqS2%af)R(>#FXm!D! zLBHzKS6pP?hExueZ`F6{weWlJx*w3oPRw}ap(gfrsjx3hyn8Tb?fBXhWrc%B-5~$I zVj3#I6Yigt;`|v1|5~xVJN-Y_Y^ekfOwUNqL~m^2UiwyV^9%D464n`F%z?2PlBn;Y_7>!U19+@k9ZT$CkXcs83E0A@Vb}VQ z`@8)25LajG!tjDrxgoN#FfT6$R5unlIAq#0-AWbZ$Esai^f90;)Q_a^_w_jWPKHy`fbZY`=C245zZT1-2b? zb?#bec_sK91(epPVO5;yLHHPp@x`pl%0GRu{To6;jA?y@)FzD&$2K8ns5rwGX>NEWe7;0pK5xq(ijkxGm+QxpfAo?U9z0Gk= zk=~?As<vB758PiEPB$nrF0kx4%1uhsNzslm)c=W% zFlBpy&v#J;F#v%7AEzQKsw^lXs4Uo};r)HS8Rv65M+i1W7}%812wc+!J*WJ_ zM>8Ycad~y`7-mEhuDZ?~zkxJ-W!BRp9&gk#sS2}xxhpU2v31}1^$gZ_kColw2~|yV z)Nr&pU+{)`>NyTO&5YO_@7}>#`_R2IS-5$G`NyUKMU5_QJ+o?h#SUb*wnUO;D9(k7 zDZR?*ow~8GD+(qr>V>
;mAt+>gV7*{n<(t#6)oNmB+~P1uzZMb~^P zMELjFliy`hQT#D)(L-cl!pjrrctw20N|@nrOv>8LM*r|w#r}DoNPc0ml5k{>D2T;< zlZ1HO`S!g7WyIM#_zek}8TFKt5NA}R2Kw}cY^LUk)w*uwXfnr8Ah%saos&`2<`*Nl z7Y-4w(TS+zo1oN<0N3pH0fu6yM&9<)MO2N5pl>k-yP}nx>&M5u31oR(z;DWxV9RuK z491!!Z~2(LW|DDv?|T-2Q=h|dXkRaHsN3T>L}jxKYg?JJ&@Nq696n5LzAw35lH?RO z9-Q%KeBRxUzh2EkYr129NrUA~WTv!UH{A#9+*i!o(fla7z_Kj8&xk%J6`0A9WpQ)A z4zeonOyJDAmB^XS7C;qcCg3`4Usj4ho$K)VRlR4)Rerj)wbc-G&E=@h9q^d@)UU6h z>aj)EhMM+EBwJjFy)(UQ%i2fMC0VzM_eqS)F6i0^26_UW-?4W}C)+wsgg^3L`-XNg zNWFif%PDqIVAhp%u~LoOYSB1Q>xR`TX5?Ja#{Th2l6QL_wAb?mm;!Dj(vBT}%I zwZSbXl#r{3JQldo2FiWMxvK!xgmutrpsB^Q?}EJ&uJV1y7sik-l6JPpESN?GC|m(` zogfDZYS_&y?x(WRClm9vyV~h};$UJ?2YfT)CQ85T_05@UO;SWemq4{7EcOt!Fy5(r z??=zdRpqe_r%7XLB;J;I9RB;Aj}!#xl}IE1%ZHxzSIoHLTb}8wiQvQtZRj0(W`55 zTurCYE!;Cz-OENYtUKlVZZ@RygV(u@9(FA7gX*U1F2p|JiC0)s7c`iCg>=AQ07eIJx znykjtY7moB_Uqtk;bkJu1U}T{KDy01i4z_SS{s&=*)(Xt^5Mr))MY~!5jL$kZU5dK zjIW<<%{MTr7wZn*XY+3Z)kV#w>z<x7U62Dacr{uVh2&PYRr>4-Trj&jy!9jbtu^9w4cF(L z;6f`+>vN7o;{}KR@!?rjFD;cP)1XV{?3g7~d22Ej3BQy6tVObof$^owi;p*ZEs>^=qE6>hy8ZG zj`35dSI-RuQjW6#B`rQSBOOHK<@TV%ex%J_1*-gq!!S(uC?n838Aeu=b`y>~{))>a zvQ6SBs-60<{S!@1fR!?MEv&T`rZFNqvwGS1I;na2;Km9MsbDgPQo+u7tJC2$^`j}Z zet!NN$IdmXu7@GA&2@s}6H8H{qqB67|LVzG|Aef$H8l*Wz(z$}h=!rUNXBUqD{iLY z0H;AirWFjoO_DI1XrZ=7Lz{MRX^16Rh+mjYr?Ce;4XcBQyHN9t%2i`Hqn;)VxmZ=0 zh5BsuYH0@6Ws+xsVvFKy31(--QmqId?_KdN?Q&6s3xp81G}tC6dT(ycJm5sUgGWp6 z+LlPO60)o5O+5S8V(Hf|siM0OMgNhNlS;}zjacX^b)@v8V63~tlACEmjBu7j2tN|2 zrnQVu5ZD<#lK3RPl~|?-j`=xsyl7a}7*MlndbbJi6tnv)P@h%xMUgp$J1nb`{oLBA z*;R(s8pq=RbBkB@E5Qyiexx@xKGKwS8xc&t#SUvLm!an(?}~P)N{nX7gI@!A8O941 znqaDnQ>Yyo{yL;_C}eoS40TG5k)G*3K1>IUCSoTq0izYk$g6NXNmS(z^(oUyrst5Q zX_c;0ibiENxH4Zi-S4QhC#W&BszB71bR+v^93cj72@*Hy$jV6=-i(ZE;QEr&k>UXx z@!9wO$gTj4^~s4J$bxp7{9~^Ql{|r0gkEbHBIG>K-^X zc7!p5bN||sM(=KzktjnL4(DJcYis-piLq zNPLO#cMA^sy|pl^Lc?RroLN^6y4C26>oo(t@llmu4m6K!lk8VU)GG2gf>VMfkFyp| zaKXVr$r)4*0S?Z-b<1k&3lfGNOI5Pa!Tt6GRO4%RF=%#n4m%DR=BI(2b zRB)`YJhIB*)tYmFSS^h|w$qvpzFet%(yhB9$8?tR3Bib}smzgDNx2}2AgxfGhHc7^ zq98^!ZIfK3N~z6f`_o9GZH8H*O0P!rb|7qrRaQNK=Z&nwvc2!Uhqc1$laoAWdFf*< zu{_^D!fvv3api;>Dbq0h@(lHDw}`;iH{`8g|SX^m5Fj8@8p+Q%J;9nOeJsxJuOKsVw=|#RK5wM5d~)4GeDZ772{j*a*!11h1GFFAIe*h(s(MVIPSF zgYmd31WP{u{K=f@JC#?ITW5TM`?jBwyyo)Vl9O$%qu1h1&0^ZYak;I3Z1b35udzhg zi^%os%h5Z^Ns5zW%_1h(JKyR_Zth4h#z0RvYBXV0%HUCS$*NWtydoltpiR!a9))LV zHHvUuJ_ROE5$h%eyk99}wUZT89eTZu-Q{_1`L^cNEftN9TIEs>bmJF}PziT1Z5bNc)s-I_RRZJ%W%AUXqR$uPXwEgfWQ(3>y-p4Knn8+>J(jUEqYLU`kXy zX@ize+q&cL1cD!ZOU#J$^yg-hmi<(Y59X{Z(ai-Prt#qi)~p^_@cC7;i7qp=(wSo3 zmD{t9tY=yhDh&9B3ciR?KVgJ8fxZD^Ga$A)$v|#c3!L0<-lH)-T<#xdhj(53$0O6( zm$6sYe62lBpXZPr@0&cnL7a_z%-s0iYreoorz|K~RM(Ln>jkC{xlbu)q7D(U;#!woH7`Wb2Z3uCn{=B+5eyRG` zcb}XeCmvW{y0H8B6C1SA@`u+m<@X=q1K}{V?>yb zo|ADht7g~oP!TfE&;6_k`H(}^3GQ}w2lV>^S}HRS6nJGT%!*bdd1Nb=cSzCwR%THm)7cA)Z+%Yv2^Y{gW`K0j zjvn=hL^7&3($U39I_6t|O+Sg$Pj1AnoyKyms7GQMq38md(?B#$oi^4Q=HE~r}`mV}@`6f`+ zqJ%ra(VV6}CT*n_Tj^B;jOz7GwcUIV-YXHZ8agY;n?K9i{TU~8ZjtSgj~Um`{d5S8 zj9%6`GFBA>_^lME-%*3t9(jjM?4Hn^6Y&$;Ovx=BHKT2RRkwJ)SUuC4)GL2kh(m{Q zR+%$3j=W3sedUA&VTJ+5v!FemkW#K;Mww)%g}I-PH`ZC*j#ZQG*h;?M=%vJ0y{wz< zsA7-KPi#A^T)&Rjs!q;%{)v)nam^789rMMCOX68LPkY4ayrA{jybcFL!J&#Am|h&l zQAuQv83?(v&mk`j0*VIw&u#X9Ui5!$h5r9I{GX=#zr+9DU;f`<0HDxc?2qgG5By)< z=6^^3UE}`?4Z!*r`o9$aza#&yy8VSL{o||u^W1-GaDNB?UBLPaoc>3L`fu<*<*dJB z|1Mhnh28rn_W#7l|43N>o9h3kCj5&EB$4ocrtyEM{!3f Date: Tue, 8 Aug 2023 10:07:55 +0200 Subject: [PATCH 1312/2884] python: mkvenv: remove ensure command This was used to bootstrap the venv with a TOML parser, after which ensuregroup is used. Now that we expect it to be present as a system package (either tomli or, for Python 3.11, tomllib), it is not needed anymore. Note that this means that, when implemented, the hypothetical "isolated" mode that does not use any system packages will only work with Python 3.11+. Signed-off-by: Paolo Bonzini --- python/scripts/mkvenv.py | 105 --------------------------------------- 1 file changed, 105 deletions(-) diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index d0b9c215ca..f2526af0a0 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -13,7 +13,6 @@ Commands: create create a venv post_init post-venv initialization - ensure Ensure that the specified package is installed. ensuregroup Ensure that the specified package group is installed. @@ -36,18 +35,6 @@ options: -------------------------------------------------- -usage: mkvenv ensure [-h] [--online] [--dir DIR] dep_spec... - -positional arguments: - dep_spec PEP 508 Dependency specification, e.g. 'meson>=0.61.5' - -options: - -h, --help show this help message and exit - --online Install packages from PyPI, if necessary. - --dir DIR Path to vendored packages where we may install from. - --------------------------------------------------- - usage: mkvenv ensuregroup [-h] [--online] [--dir DIR] file group... positional arguments: @@ -726,57 +713,6 @@ def _do_ensure( return None -def ensure( - dep_specs: Sequence[str], - online: bool = False, - wheels_dir: Optional[Union[str, Path]] = None, - prog: Optional[str] = None, -) -> None: - """ - Use pip to ensure we have the package specified by @dep_specs. - - If the package is already installed, do nothing. If online and - wheels_dir are both provided, prefer packages found in wheels_dir - first before connecting to PyPI. - - :param dep_specs: - PEP 508 dependency specifications. e.g. ['meson>=0.61.5']. - :param online: If True, fall back to PyPI. - :param wheels_dir: If specified, search this path for packages. - :param prog: - If specified, use this program name for error diagnostics that will - be presented to the user. e.g., 'sphinx-build' can be used as a - bellwether for the presence of 'sphinx'. - """ - - if not HAVE_DISTLIB: - raise Ouch("a usable distlib could not be found, please install it") - - # Convert the depspecs to a dictionary, as if they came - # from a section in a pythondeps.toml file - group: Dict[str, Dict[str, str]] = {} - for spec in dep_specs: - name = distlib.version.LegacyMatcher(spec).name - group[name] = {} - - spec = spec.strip() - pos = len(name) - ver = spec[pos:].strip() - if ver: - group[name]["accepted"] = ver - - if prog: - group[name]["canary"] = prog - prog = None - - result = _do_ensure(group, online, wheels_dir) - if result: - # Well, that's not good. - if result[1]: - raise Ouch(result[0]) - raise SystemExit(f"\n{result[0]}\n\n") - - def _parse_groups(file: str) -> Dict[str, Dict[str, Any]]: if not HAVE_TOMLLIB: if sys.version_info < (3, 11): @@ -888,39 +824,6 @@ def _add_ensuregroup_subcommand(subparsers: Any) -> None: ) -def _add_ensure_subcommand(subparsers: Any) -> None: - subparser = subparsers.add_parser( - "ensure", help="Ensure that the specified package is installed." - ) - subparser.add_argument( - "--online", - action="store_true", - help="Install packages from PyPI, if necessary.", - ) - subparser.add_argument( - "--dir", - type=str, - action="store", - help="Path to vendored packages where we may install from.", - ) - subparser.add_argument( - "--diagnose", - type=str, - action="store", - help=( - "Name of a shell utility to use for " - "diagnostics if this command fails." - ), - ) - subparser.add_argument( - "dep_specs", - type=str, - action="store", - help="PEP 508 Dependency specification, e.g. 'meson>=0.61.5'", - nargs="+", - ) - - def main() -> int: """CLI interface to make_qemu_venv. See module docstring.""" if os.environ.get("DEBUG") or os.environ.get("GITLAB_CI"): @@ -944,7 +847,6 @@ def main() -> int: _add_create_subcommand(subparsers) _add_post_init_subcommand(subparsers) - _add_ensure_subcommand(subparsers) _add_ensuregroup_subcommand(subparsers) args = parser.parse_args() @@ -957,13 +859,6 @@ def main() -> int: ) if args.command == "post_init": post_venv_setup() - if args.command == "ensure": - ensure( - dep_specs=args.dep_specs, - online=args.online, - wheels_dir=args.dir, - prog=args.diagnose, - ) if args.command == "ensuregroup": ensure_group( file=args.file, From a1852002c7509569eaaedb783925f34350fe0a84 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Mon, 20 May 2024 12:53:04 -0300 Subject: [PATCH 1313/2884] Hexagon: fix HVX store new At 09a7e7db0f (Hexagon (target/hexagon) Remove uses of op_regs_generated.h.inc, 2024-03-06), we've changed the logic of check_new_value() to use the new pre-calculated packet->insn[...].dest_idx instead of calculating the index on the fly using opcode_reginfo[...]. The dest_idx index is calculated roughly like the following: for reg in iset[tag]["syntax"]: if reg.is_written(): dest_idx = regno break Thus, we take the first register that is writtable. Before that, however, we also used to follow an alphabetical order on the register type: 'd', 'e', 'x', and 'y'. No longer following that makes us select the wrong register index and the HVX store new instruction does not update the memory like expected. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Reviewed-by: Taylor Simpson Message-Id: Signed-off-by: Brian Cain --- target/hexagon/gen_trans_funcs.py | 9 ++++++--- tests/tcg/hexagon/hvx_misc.c | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py index 9f86b4edbd..30f0c73e0c 100755 --- a/target/hexagon/gen_trans_funcs.py +++ b/target/hexagon/gen_trans_funcs.py @@ -89,6 +89,7 @@ def gen_trans_funcs(f): new_read_idx = -1 dest_idx = -1 + dest_idx_reg_id = None has_pred_dest = "false" for regno, (reg_type, reg_id, *_) in enumerate(regs): reg = hex_common.get_register(tag, reg_type, reg_id) @@ -97,9 +98,11 @@ def gen_trans_funcs(f): """)) if reg.is_read() and reg.is_new(): new_read_idx = regno - # dest_idx should be the first destination, so check for -1 - if reg.is_written() and dest_idx == -1: - dest_idx = regno + if reg.is_written(): + # dest_idx should be the first destination alphabetically + if dest_idx_reg_id is None or reg_id < dest_idx_reg_id: + dest_idx = regno + dest_idx_reg_id = reg_id if reg_type == "P" and reg.is_written() and not reg.is_read(): has_pred_dest = "true" diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index 1fe14b5158..90c3733da0 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -474,6 +474,27 @@ static void test_vcombine(void) check_output_w(__LINE__, BUFSIZE); } +void test_store_new() +{ + asm volatile( + "r0 = #0x12345678\n" + "v0 = vsplat(r0)\n" + "r0 = #0xff00ff00\n" + "v1 = vsplat(r0)\n" + "{\n" + " vdeal(v1,v0,r0)\n" + " vmem(%0) = v0.new\n" + "}\n" + : + : "r"(&output[0]) + : "r0", "v0", "v1", "memory" + ); + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) { + expect[0].w[i] = 0x12345678; + } + check_output_w(__LINE__, 1); +} + int main() { init_buffers(); @@ -515,6 +536,8 @@ int main() test_vcombine(); + test_store_new(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } From e1b526f1d86b7c51b97227989b9ba2925cc53069 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Fri, 3 May 2024 13:53:15 -0300 Subject: [PATCH 1314/2884] Hexagon: add PC alignment check and exception The Hexagon Programmer's Reference Manual says that the exception 0x1e should be raised upon an unaligned program counter. Let's implement that and also add some tests. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Richard Henderson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <277b7aeda2c717a96d4dde936b3ac77707cb6517.1714755107.git.quic_mathbern@quicinc.com> Signed-off-by: Brian Cain --- linux-user/hexagon/cpu_loop.c | 4 ++ target/hexagon/cpu.h | 7 ++ target/hexagon/cpu_bits.h | 4 ++ target/hexagon/macros.h | 3 - target/hexagon/op_helper.c | 9 ++- tests/tcg/hexagon/Makefile.target | 2 + tests/tcg/hexagon/unaligned_pc.c | 107 ++++++++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 tests/tcg/hexagon/unaligned_pc.c diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 7f1499ed28..d41159e52a 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -60,6 +60,10 @@ void cpu_loop(CPUHexagonState *env) env->gpr[0] = ret; } break; + case HEX_EXCP_PC_NOT_ALIGNED: + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, + env->gpr[HEX_REG_R31]); + break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 3eef58fe8f..764f3c38cc 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -134,6 +134,10 @@ struct ArchCPU { FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc); + static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { @@ -144,6 +148,9 @@ static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); } *flags = hex_flags; + if (*pc & PCALIGN_MASK) { + hexagon_raise_exception_err(env, HEX_EXCP_PC_NOT_ALIGNED, 0); + } } typedef HexagonCPU ArchCPU; diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 96fef71729..4279281a71 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -20,9 +20,13 @@ #include "qemu/bitops.h" +#define PCALIGN 4 +#define PCALIGN_MASK (PCALIGN - 1) + #define HEX_EXCP_FETCH_NO_UPAGE 0x012 #define HEX_EXCP_INVALID_PACKET 0x015 #define HEX_EXCP_INVALID_OPCODE 0x015 +#define HEX_EXCP_PC_NOT_ALIGNED 0x01e #define HEX_EXCP_PRIV_NO_UREAD 0x024 #define HEX_EXCP_PRIV_NO_UWRITE 0x025 diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index feb798c6c0..ee3d4c88e7 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -22,9 +22,6 @@ #include "hex_regs.h" #include "reg_fields.h" -#define PCALIGN 4 -#define PCALIGN_MASK (PCALIGN - 1) - #define GET_FIELD(FIELD, REGIN) \ fEXTRACTU_BITS(REGIN, reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index da10ac5847..ae5a605513 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -36,10 +36,9 @@ #define SF_MANTBITS 23 /* Exceptions processing helpers */ -static G_NORETURN -void do_raise_exception_err(CPUHexagonState *env, - uint32_t exception, - uintptr_t pc) +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc) { CPUState *cs = env_cpu(env); qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); @@ -49,7 +48,7 @@ void do_raise_exception_err(CPUHexagonState *env, G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) { - do_raise_exception_err(env, excp, 0); + hexagon_raise_exception_err(env, excp, 0); } void log_store32(CPUHexagonState *env, target_ulong addr, diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index f839b2c0d5..e5182c01d8 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -51,6 +51,7 @@ HEX_TESTS += scatter_gather HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram HEX_TESTS += invalid-slots +HEX_TESTS += unaligned_pc run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \ test $$? -eq 1 && grep -q "exception $(strip $1)" $2.stderr) @@ -107,6 +108,7 @@ overflow: overflow.c hex_test.h preg_alias: preg_alias.c hex_test.h read_write_overlap: read_write_overlap.c hex_test.h reg_mut: reg_mut.c hex_test.h +unaligned_pc: unaligned_pc.c # This test has to be compiled for the -mv67t target usr: usr.c hex_test.h diff --git a/tests/tcg/hexagon/unaligned_pc.c b/tests/tcg/hexagon/unaligned_pc.c new file mode 100644 index 0000000000..e9dc7cb8b5 --- /dev/null +++ b/tests/tcg/hexagon/unaligned_pc.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +/* will be changed in signal handler */ +volatile sig_atomic_t completed_tests; +static jmp_buf after_test; +static int nr_tests; + +void __attribute__((naked)) test_return(void) +{ + asm volatile( + "allocframe(#0x8)\n" + "r0 = #0xffffffff\n" + "framekey = r0\n" + "dealloc_return\n" + : + : + : "r0", "r29", "r30", "r31", "framekey"); +} + +void test_endloop(void) +{ + asm volatile( + "loop0(1f, #2)\n" + "1: r0 = #0x3\n" + "sa0 = r0\n" + "{ nop }:endloop0\n" + : + : + : "r0", "sa0", "lc0", "usr"); +} + +asm( + ".pushsection .text.unaligned\n" + ".org 0x3\n" + ".global test_multi_cof_unaligned\n" + "test_multi_cof_unaligned:\n" + " jumpr r31\n" + ".popsection\n" +); + +#define SYS_EXIT 94 + +void test_multi_cof(void) +{ + asm volatile( + "p0 = cmp.eq(r0, r0)\n" + "{\n" + " if (p0) jump test_multi_cof_unaligned\n" + " if (!p0) jump 1f\n" + "}\n" + "1:" + " r0 = #1\n" + " r6 = #%0\n" + " trap0(#1)\n" + : + : "i"(SYS_EXIT) + : "p0", "r0", "r6"); +} + +void sigbus_handler(int signum) +{ + /* retore framekey after test_return */ + asm volatile( + "r0 = #0\n" + "framekey = r0\n" + : + : + : "r0", "framekey"); + printf("Test %d complete\n", completed_tests); + completed_tests++; + siglongjmp(after_test, 1); +} + +void test_done(void) +{ + int err = (completed_tests != nr_tests); + puts(err ? "FAIL" : "PASS"); + exit(err); +} + +typedef void (*test_fn)(void); + +int main() +{ + test_fn tests[] = { test_return, test_endloop, test_multi_cof, test_done }; + nr_tests = (sizeof(tests) / sizeof(tests[0])) - 1; + + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO + }; + + if (sigaction(SIGBUS, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + sigsetjmp(after_test, 1); + tests[completed_tests](); + + /* should never get here */ + puts("FAIL"); + return 1; +} From 49c1f7a472ebff23b4f374a1d5201250f3fdbd76 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:58:58 +0200 Subject: [PATCH 1315/2884] target/hexagon: idef-parser remove unused defines Before switching to GArray/g_string_printf we used fixed size arrays for output buffers and instructions arguments among other things. Macros defining the sizes of these buffers were left behind, remove them. Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-2-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/idef-parser.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/target/hexagon/idef-parser/idef-parser.h b/target/hexagon/idef-parser/idef-parser.h index 3faa1deecd..8594cbe3a2 100644 --- a/target/hexagon/idef-parser/idef-parser.h +++ b/target/hexagon/idef-parser/idef-parser.h @@ -23,16 +23,6 @@ #include #include -#define TCGV_NAME_SIZE 7 -#define MAX_WRITTEN_REGS 32 -#define OFFSET_STR_LEN 32 -#define ALLOC_LIST_LEN 32 -#define ALLOC_NAME_SIZE 32 -#define INIT_LIST_LEN 32 -#define OUT_BUF_LEN (1024 * 1024) -#define SIGNATURE_BUF_LEN (128 * 1024) -#define HEADER_BUF_LEN (128 * 1024) - /* Variadic macros to wrap the buffer printing functions */ #define EMIT(c, ...) \ do { \ From 348fec2afe9f03b1761caff44ea6290357d87c01 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:58:59 +0200 Subject: [PATCH 1316/2884] target/hexagon: idef-parser remove undefined functions Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-3-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/parser-helpers.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/target/hexagon/idef-parser/parser-helpers.h b/target/hexagon/idef-parser/parser-helpers.h index 7c58087169..2087d534a9 100644 --- a/target/hexagon/idef-parser/parser-helpers.h +++ b/target/hexagon/idef-parser/parser-helpers.h @@ -143,8 +143,6 @@ void commit(Context *c); #define OUT(c, locp, ...) FOR_EACH((c), (locp), OUT_IMPL, __VA_ARGS__) -const char *cmp_swap(Context *c, YYLTYPE *locp, const char *type); - /** * Temporary values creation */ @@ -236,8 +234,6 @@ HexValue gen_extract_op(Context *c, HexValue *index, HexExtract *extract); -HexValue gen_read_reg(Context *c, YYLTYPE *locp, HexValue *reg); - void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value); void gen_assign(Context *c, @@ -274,13 +270,6 @@ HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src); HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *n); -HexValue gen_deinterleave(Context *c, YYLTYPE *locp, HexValue *mixed); - -HexValue gen_interleave(Context *c, - YYLTYPE *locp, - HexValue *odd, - HexValue *even); - HexValue gen_carry_from_add(Context *c, YYLTYPE *locp, HexValue *op1, @@ -349,8 +338,6 @@ HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond, const char *cond_to_str(TCGCond cond); -void emit_header(Context *c); - void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg); void emit_footer(Context *c); From 95408ad8e24c4364086f185285039e89927dad6c Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:59:00 +0200 Subject: [PATCH 1317/2884] target/hexagon: idef-parser fix leak of init_list gen_inst_init_args() is called for instructions using a predicate as an rvalue. Upon first call, the list of arguments which might need initialization init_list is freed to indicate that they have been processed. For instructions without an rvalue predicate, gen_inst_init_args() isn't called and init_list will never be freed. Free init_list from free_instruction() if it hasn't already been freed. A comment in free_instruction is also updated. Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-4-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/parser-helpers.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index 95f2b43076..c150c308be 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -2121,9 +2121,16 @@ void free_instruction(Context *c) g_string_free(g_array_index(c->inst.strings, GString*, i), TRUE); } g_array_free(c->inst.strings, TRUE); + /* + * Free list of arguments that might need initialization, if they haven't + * already been freed. + */ + if (c->inst.init_list) { + g_array_free(c->inst.init_list, TRUE); + } /* Free INAME token value */ g_string_free(c->inst.name, TRUE); - /* Free variables and registers */ + /* Free declared TCGv variables */ g_array_free(c->inst.allocated, TRUE); /* Initialize instruction-specific portion of the context */ memset(&(c->inst), 0, sizeof(Inst)); From 1967a1ea985299c090dfd3efc1e5323ce60d75df Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:59:01 +0200 Subject: [PATCH 1318/2884] target/hexagon: idef-parser simplify predicate init Only predicate instruction arguments need to be initialized by idef-parser. This commit removes registers from the init_list and simplifies gen_inst_init_args() slightly. Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-5-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/idef-parser.y | 2 -- target/hexagon/idef-parser/parser-helpers.c | 26 +++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index cd2612eb8c..9ffb9f9699 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y @@ -233,8 +233,6 @@ code : '{' statements '}' argument_decl : REG { emit_arg(c, &@1, &$1); - /* Enqueue register into initialization list */ - g_array_append_val(c->inst.init_list, $1); } | PRED { diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index c150c308be..a7dcd85fe4 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1652,26 +1652,28 @@ void gen_inst(Context *c, GString *iname) /* - * Initialize declared but uninitialized registers, but only for - * non-conditional instructions + * Initialize declared but uninitialized instruction arguments. Only needed for + * predicate arguments, initialization of registers is handled by the Hexagon + * frontend. */ void gen_inst_init_args(Context *c, YYLTYPE *locp) { + HexValue *val = NULL; + char suffix; + + /* If init_list is NULL arguments have already been initialized */ if (!c->inst.init_list) { return; } for (unsigned i = 0; i < c->inst.init_list->len; i++) { - HexValue *val = &g_array_index(c->inst.init_list, HexValue, i); - if (val->type == REGISTER_ARG) { - /* Nothing to do here */ - } else if (val->type == PREDICATE) { - char suffix = val->is_dotnew ? 'N' : 'V'; - EMIT_HEAD(c, "tcg_gen_movi_i%u(P%c%c, 0);\n", val->bit_width, - val->pred.id, suffix); - } else { - yyassert(c, locp, false, "Invalid arg type!"); - } + val = &g_array_index(c->inst.init_list, HexValue, i); + suffix = val->is_dotnew ? 'N' : 'V'; + yyassert(c, locp, val->type == PREDICATE, + "Only predicates need to be initialized!"); + yyassert(c, locp, val->bit_width == 32, + "Predicates should always be 32 bits"); + EMIT_HEAD(c, "tcg_gen_movi_i32(P%c%c, 0);\n", val->pred.id, suffix); } /* Free argument init list once we have initialized everything */ From 1b6f1b2e820302f08f262a09551767f14c0f98a6 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 6 Jun 2024 21:57:42 -0600 Subject: [PATCH 1319/2884] linux-user: Adjust comment to reflect the code. If the user didn't specify reserved_va, there's an else for 64-bit host 32-bit (or fewer) target to reserve 32-bits of address space. Update the comments to reflect this, and rejustify comment to 80 columns. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- linux-user/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 94e4c47f05..94c99a1366 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -814,10 +814,10 @@ int main(int argc, char **argv, char **envp) thread_cpu = cpu; /* - * Reserving too much vm space via mmap can run into problems - * with rlimits, oom due to page table creation, etc. We will - * still try it, if directed by the command-line option, but - * not by default. + * Reserving too much vm space via mmap can run into problems with rlimits, + * oom due to page table creation, etc. We will still try it, if directed + * by the command-line option, but not by default. Unless we're running a + * target address space of 32 or fewer bits on a host with 64 bits. */ max_reserved_va = MAX_RESERVED_VA(cpu); if (reserved_va != 0) { From ba379542bf026313d3c6aa1b46da3f2520927a4f Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 6 Jun 2024 22:06:45 -0600 Subject: [PATCH 1320/2884] bsd-user: port linux-user:ff8a8bbc2ad1 for variable page sizes Bring in Richard Henderson's ff8a8bbc2ad1 to finalize the page size to allow TARGET_PAGE_BITS_VARY. bsd-user's "blitz" fork has aarch64 support, which is now variable page size. Add support for it here, even though it's effectively a nop in upstream qemu. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bsd-user/main.c b/bsd-user/main.c index 29a629d877..d685734d08 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -46,6 +46,7 @@ #include "crypto/init.h" #include "qemu/guest-random.h" #include "gdbstub/user.h" +#include "exec/page-vary.h" #include "host-os.h" #include "target_arch_cpu.h" @@ -291,6 +292,7 @@ int main(int argc, char **argv) char **target_environ, **wrk; envlist_t *envlist = NULL; char *argv0 = NULL; + int host_page_size; adjust_ssize(); @@ -476,6 +478,16 @@ int main(int argc, char **argv) opt_one_insn_per_tb, &error_abort); ac->init_machine(NULL); } + + /* + * Finalize page size before creating CPUs. + * This will do nothing if !TARGET_PAGE_BITS_VARY. + * The most efficient setting is to match the host. + */ + host_page_size = qemu_real_host_page_size(); + set_preferred_target_page_bits(ctz32(host_page_size)); + finalize_target_page_bits(); + cpu = cpu_create(cpu_type); env = cpu_env(cpu); cpu_reset(cpu); From cb4c259052cbc5dd04c17d963c789360cb8fe340 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 6 Jun 2024 22:01:41 -0600 Subject: [PATCH 1321/2884] bsd-user: Catch up to run-time reserved_va math Catch up to linux-user's 8f67b9c694d0, 13c13397556a, 2f7828b57293, and 95059f9c313a by Richard Henderson which made reserved_va a run-time calculation, defaulting to nothing except in the case of 64-bit host 32-bit target. Also include the adjustment of the comment heading that work submitted in the same patch stream. Since this is a direct copy, squash it into one patch rather than follow the Linux evolution since breaking this down further at this point doesn't make sense for this "new code". Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/main.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index d685734d08..dcad266c2c 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -77,25 +77,16 @@ bool have_guest_base; # if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS # if TARGET_VIRT_ADDR_SPACE_BITS == 32 && \ (TARGET_LONG_BITS == 32 || defined(TARGET_ABI32)) -# define MAX_RESERVED_VA 0xfffffffful +# define MAX_RESERVED_VA(CPU) 0xfffffffful # else -# define MAX_RESERVED_VA ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) +# define MAX_RESERVED_VA(CPU) ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) # endif # else -# define MAX_RESERVED_VA 0 +# define MAX_RESERVED_VA(CPU) 0 # endif #endif -/* - * That said, reserving *too* much vm space via mmap can run into problems - * with rlimits, oom due to page table creation, etc. We will still try it, - * if directed by the command-line option, but not by default. - */ -#if HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32 -unsigned long reserved_va = MAX_RESERVED_VA; -#else unsigned long reserved_va; -#endif const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; @@ -293,6 +284,7 @@ int main(int argc, char **argv) envlist_t *envlist = NULL; char *argv0 = NULL; int host_page_size; + unsigned long max_reserved_va; adjust_ssize(); @@ -493,6 +485,29 @@ int main(int argc, char **argv) cpu_reset(cpu); thread_cpu = cpu; + /* + * Reserving too much vm space via mmap can run into problems with rlimits, + * oom due to page table creation, etc. We will still try it, if directed + * by the command-line option, but not by default. Unless we're running a + * target address space of 32 or fewer bits on a host with 64 bits. + */ + max_reserved_va = MAX_RESERVED_VA(cpu); + if (reserved_va != 0) { + if ((reserved_va + 1) % host_page_size) { + char *s = size_to_str(host_page_size); + fprintf(stderr, "Reserved virtual address not aligned mod %s\n", s); + g_free(s); + exit(EXIT_FAILURE); + } + if (max_reserved_va && reserved_va > max_reserved_va) { + fprintf(stderr, "Reserved virtual address too big\n"); + exit(EXIT_FAILURE); + } + } else if (HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32) { + /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */ + reserved_va = max_reserved_va; + } + if (getenv("QEMU_STRACE")) { do_strace = 1; } From b771b026d8495feea8c3b7aee43ee2084102e3b4 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 29 Apr 2024 16:24:51 +0200 Subject: [PATCH 1322/2884] xen: mapcache: Make MCACHE_BUCKET_SHIFT runtime configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make MCACHE_BUCKET_SHIFT runtime configurable per cache instance. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 54 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index fa6813b1ad..bc860f4373 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -23,13 +23,10 @@ #if HOST_LONG_BITS == 32 -# define MCACHE_BUCKET_SHIFT 16 # define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */ #else -# define MCACHE_BUCKET_SHIFT 20 # define MCACHE_MAX_SIZE (1UL<<35) /* 32GB Cap */ #endif -#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) /* This is the size of the virtual address space reserve to QEMU that will not * be use by MapCache. @@ -65,7 +62,8 @@ typedef struct MapCache { /* For most cases (>99.9%), the page address is the same. */ MapCacheEntry *last_entry; unsigned long max_mcache_size; - unsigned int mcache_bucket_shift; + unsigned int bucket_shift; + unsigned long bucket_size; phys_offset_to_gaddr_t phys_offset_to_gaddr; QemuMutex lock; @@ -95,11 +93,14 @@ static inline int test_bits(int nr, int size, const unsigned long *addr) static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f, void *opaque, + unsigned int bucket_shift, unsigned long max_size) { unsigned long size; MapCache *mc; + assert(bucket_shift >= XC_PAGE_SHIFT); + mc = g_new0(MapCache, 1); mc->phys_offset_to_gaddr = f; @@ -108,12 +109,14 @@ static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f, QTAILQ_INIT(&mc->locked_entries); + mc->bucket_shift = bucket_shift; + mc->bucket_size = 1UL << bucket_shift; mc->max_mcache_size = max_size; mc->nr_buckets = (((mc->max_mcache_size >> XC_PAGE_SHIFT) + - (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> - (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); + (1UL << (bucket_shift - XC_PAGE_SHIFT)) - 1) >> + (bucket_shift - XC_PAGE_SHIFT)); size = mc->nr_buckets * sizeof(MapCacheEntry); size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); @@ -126,6 +129,13 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) { struct rlimit rlimit_as; unsigned long max_mcache_size; + unsigned int bucket_shift; + + if (HOST_LONG_BITS == 32) { + bucket_shift = 16; + } else { + bucket_shift = 20; + } if (geteuid() == 0) { rlimit_as.rlim_cur = RLIM_INFINITY; @@ -146,7 +156,9 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) } } - mapcache = xen_map_cache_init_single(f, opaque, max_mcache_size); + mapcache = xen_map_cache_init_single(f, opaque, + bucket_shift, + max_mcache_size); setrlimit(RLIMIT_AS, &rlimit_as); } @@ -195,7 +207,7 @@ static void xen_remap_bucket(MapCache *mc, entry->valid_mapping = NULL; for (i = 0; i < nb_pfn; i++) { - pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; + pfns[i] = (address_index << (mc->bucket_shift - XC_PAGE_SHIFT)) + i; } /* @@ -266,8 +278,8 @@ static uint8_t *xen_map_cache_unlocked(MapCache *mc, bool dummy = false; tryagain: - address_index = phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1); + address_index = phys_addr >> mc->bucket_shift; + address_offset = phys_addr & (mc->bucket_size - 1); trace_xen_map_cache(phys_addr); @@ -294,14 +306,14 @@ tryagain: return mc->last_entry->vaddr_base + address_offset; } - /* size is always a multiple of MCACHE_BUCKET_SIZE */ + /* size is always a multiple of mc->bucket_size */ if (size) { cache_size = size + address_offset; - if (cache_size % MCACHE_BUCKET_SIZE) { - cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); + if (cache_size % mc->bucket_size) { + cache_size += mc->bucket_size - (cache_size % mc->bucket_size); } } else { - cache_size = MCACHE_BUCKET_SIZE; + cache_size = mc->bucket_size; } entry = &mc->entry[address_index % mc->nr_buckets]; @@ -422,7 +434,7 @@ static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr) trace_xen_ram_addr_from_mapcache_not_in_cache(ptr); raddr = RAM_ADDR_INVALID; } else { - raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + + raddr = (reventry->paddr_index << mc->bucket_shift) + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); } mapcache_unlock(mc); @@ -585,8 +597,8 @@ static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, hwaddr address_index, address_offset; hwaddr test_bit_size, cache_size = size; - address_index = old_phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = old_phys_addr & (MCACHE_BUCKET_SIZE - 1); + address_index = old_phys_addr >> mc->bucket_shift; + address_offset = old_phys_addr & (mc->bucket_size - 1); assert(size); /* test_bit_size is always a multiple of XC_PAGE_SIZE */ @@ -595,8 +607,8 @@ static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); } cache_size = size + address_offset; - if (cache_size % MCACHE_BUCKET_SIZE) { - cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); + if (cache_size % mc->bucket_size) { + cache_size += mc->bucket_size - (cache_size % mc->bucket_size); } entry = &mc->entry[address_index % mc->nr_buckets]; @@ -609,8 +621,8 @@ static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, return NULL; } - address_index = new_phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = new_phys_addr & (MCACHE_BUCKET_SIZE - 1); + address_index = new_phys_addr >> mc->bucket_shift; + address_offset = new_phys_addr & (mc->bucket_size - 1); trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); From 123acd816dccbd358570ec7ab79c5cb2c863780b Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 29 Apr 2024 19:12:42 +0200 Subject: [PATCH 1323/2884] xen: mapcache: Unmap first entries in buckets When invalidating memory ranges, if we happen to hit the first entry in a bucket we were never unmapping it. This was harmless for foreign mappings but now that we're looking to reuse the mapcache for transient grant mappings, we must unmap entries when invalidated. Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini --- hw/xen/xen-mapcache.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index bc860f4373..ec95445696 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -491,18 +491,23 @@ static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, return; } entry->lock--; - if (entry->lock > 0 || pentry == NULL) { + if (entry->lock > 0) { return; } - pentry->next = entry->next; ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); if (munmap(entry->vaddr_base, entry->size) != 0) { perror("unmap fails"); exit(-1); } + g_free(entry->valid_mapping); - g_free(entry); + if (pentry) { + pentry->next = entry->next; + g_free(entry); + } else { + memset(entry, 0, sizeof *entry); + } } typedef struct XenMapCacheData { From 49a7202979e49c7c9c02461fce497a868ef6b143 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 10:14:01 +0200 Subject: [PATCH 1324/2884] xen: mapcache: Pass the ram_addr offset to xen_map_cache() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the ram_addr offset to xen_map_cache. This is in preparation for adding grant mappings that need to compute the address within the RAMBlock. No functional changes. Signed-off-by: Edgar E. Iglesias Reviewed-by: David Hildenbrand Reviewed-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé --- hw/xen/xen-mapcache.c | 16 +++++++++++----- include/sysemu/xen-mapcache.h | 2 ++ system/physmem.c | 9 +++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index ec95445696..a07c47b0b1 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -167,7 +167,8 @@ static void xen_remap_bucket(MapCache *mc, void *vaddr, hwaddr size, hwaddr address_index, - bool dummy) + bool dummy, + ram_addr_t ram_offset) { uint8_t *vaddr_base; xen_pfn_t *pfns; @@ -266,6 +267,7 @@ static void xen_remap_bucket(MapCache *mc, static uint8_t *xen_map_cache_unlocked(MapCache *mc, hwaddr phys_addr, hwaddr size, + ram_addr_t ram_offset, uint8_t lock, bool dma, bool is_write) { MapCacheEntry *entry, *pentry = NULL, @@ -337,14 +339,16 @@ tryagain: if (!entry) { entry = g_new0(MapCacheEntry, 1); pentry->next = entry; - xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy); + xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy, + ram_offset); } else if (!entry->lock) { if (!entry->vaddr_base || entry->paddr_index != address_index || entry->size != cache_size || !test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy); + xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy, + ram_offset); } } @@ -391,13 +395,15 @@ tryagain: uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, + ram_addr_t ram_addr_offset, uint8_t lock, bool dma, bool is_write) { uint8_t *p; mapcache_lock(mapcache); - p = xen_map_cache_unlocked(mapcache, phys_addr, size, lock, dma, is_write); + p = xen_map_cache_unlocked(mapcache, phys_addr, size, ram_addr_offset, + lock, dma, is_write); mapcache_unlock(mapcache); return p; } @@ -632,7 +638,7 @@ static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); xen_remap_bucket(mc, entry, entry->vaddr_base, - cache_size, address_index, false); + cache_size, address_index, false, old_phys_addr); if (!test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index 1ec9e66752..b5e3ea1bc0 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -19,6 +19,7 @@ typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque); uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, + ram_addr_t ram_addr_offset, uint8_t lock, bool dma, bool is_write); ram_addr_t xen_ram_addr_from_mapcache(void *ptr); @@ -37,6 +38,7 @@ static inline void xen_map_cache_init(phys_offset_to_gaddr_t f, static inline uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, + ram_addr_t ram_addr_offset, uint8_t lock, bool dma, bool is_write) diff --git a/system/physmem.c b/system/physmem.c index b7847db1a2..33d09f7571 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2231,13 +2231,14 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, */ if (xen_mr_is_memory(block->mr)) { return xen_map_cache(block->mr, block->offset + addr, - len, lock, lock, - is_write); + len, block->offset, + lock, lock, is_write); } block->host = xen_map_cache(block->mr, block->offset, - block->max_length, 1, - lock, is_write); + block->max_length, + block->offset, + 1, lock, is_write); } return ramblock_ptr(block, addr); From 9ecdd4bf08dfe4a37e16b8a8b228575aff641468 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 30 Apr 2024 10:26:45 +0200 Subject: [PATCH 1325/2884] xen: mapcache: Add support for grant mappings Add a second mapcache for grant mappings. The mapcache for grants needs to work with XC_PAGE_SIZE granularity since we can't map larger ranges than what has been granted to us. Like with foreign mappings (xen_memory), machines using grants are expected to initialize the xen_grants MR and map it into their address-map accordingly. CC: Manos Pitsidianakis Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini --- hw/xen/xen-hvm-common.c | 12 ++- hw/xen/xen-mapcache.c | 165 +++++++++++++++++++++++++------- include/hw/xen/xen-hvm-common.h | 3 + include/sysemu/xen.h | 1 + 4 files changed, 144 insertions(+), 37 deletions(-) diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index a0a0252da0..b8ace1c368 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -10,12 +10,18 @@ #include "hw/boards.h" #include "hw/xen/arch_hvm.h" -MemoryRegion xen_memory; +MemoryRegion xen_memory, xen_grants; -/* Check for xen memory. */ +/* Check for any kind of xen memory, foreign mappings or grants. */ bool xen_mr_is_memory(MemoryRegion *mr) { - return mr == &xen_memory; + return mr == &xen_memory || mr == &xen_grants; +} + +/* Check specifically for grants. */ +bool xen_mr_is_grants(MemoryRegion *mr) +{ + return mr == &xen_grants; } void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index a07c47b0b1..5f23b0adbe 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -14,6 +14,7 @@ #include +#include "hw/xen/xen-hvm-common.h" #include "hw/xen/xen_native.h" #include "qemu/bitmap.h" @@ -21,6 +22,8 @@ #include "sysemu/xen-mapcache.h" #include "trace.h" +#include +#include #if HOST_LONG_BITS == 32 # define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */ @@ -41,6 +44,7 @@ typedef struct MapCacheEntry { unsigned long *valid_mapping; uint32_t lock; #define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0) +#define XEN_MAPCACHE_ENTRY_GRANT (1 << 1) uint8_t flags; hwaddr size; struct MapCacheEntry *next; @@ -71,6 +75,8 @@ typedef struct MapCache { } MapCache; static MapCache *mapcache; +static MapCache *mapcache_grants; +static xengnttab_handle *xen_region_gnttabdev; static inline void mapcache_lock(MapCache *mc) { @@ -131,6 +137,12 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) unsigned long max_mcache_size; unsigned int bucket_shift; + xen_region_gnttabdev = xengnttab_open(NULL, 0); + if (xen_region_gnttabdev == NULL) { + error_report("mapcache: Failed to open gnttab device"); + exit(EXIT_FAILURE); + } + if (HOST_LONG_BITS == 32) { bucket_shift = 16; } else { @@ -159,6 +171,15 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) mapcache = xen_map_cache_init_single(f, opaque, bucket_shift, max_mcache_size); + + /* + * Grant mappings must use XC_PAGE_SIZE granularity since we can't + * map anything beyond the number of pages granted to us. + */ + mapcache_grants = xen_map_cache_init_single(f, opaque, + XC_PAGE_SHIFT, + max_mcache_size); + setrlimit(RLIMIT_AS, &rlimit_as); } @@ -168,17 +189,24 @@ static void xen_remap_bucket(MapCache *mc, hwaddr size, hwaddr address_index, bool dummy, + bool grant, + bool is_write, ram_addr_t ram_offset) { uint8_t *vaddr_base; - xen_pfn_t *pfns; - int *err; + g_autofree uint32_t *refs = NULL; + g_autofree xen_pfn_t *pfns = NULL; + g_autofree int *err; unsigned int i; hwaddr nb_pfn = size >> XC_PAGE_SHIFT; trace_xen_remap_bucket(address_index); - pfns = g_new0(xen_pfn_t, nb_pfn); + if (grant) { + refs = g_new0(uint32_t, nb_pfn); + } else { + pfns = g_new0(xen_pfn_t, nb_pfn); + } err = g_new0(int, nb_pfn); if (entry->vaddr_base != NULL) { @@ -207,21 +235,51 @@ static void xen_remap_bucket(MapCache *mc, g_free(entry->valid_mapping); entry->valid_mapping = NULL; - for (i = 0; i < nb_pfn; i++) { - pfns[i] = (address_index << (mc->bucket_shift - XC_PAGE_SHIFT)) + i; + if (grant) { + hwaddr grant_base = address_index - (ram_offset >> XC_PAGE_SHIFT); + + for (i = 0; i < nb_pfn; i++) { + refs[i] = grant_base + i; + } + } else { + for (i = 0; i < nb_pfn; i++) { + pfns[i] = (address_index << (mc->bucket_shift - XC_PAGE_SHIFT)) + i; + } } - /* - * If the caller has requested the mapping at a specific address use - * MAP_FIXED to make sure it's honored. - */ + entry->flags &= ~XEN_MAPCACHE_ENTRY_GRANT; + if (!dummy) { - vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, - PROT_READ | PROT_WRITE, - vaddr ? MAP_FIXED : 0, - nb_pfn, pfns, err); + if (grant) { + int prot = PROT_READ; + + if (is_write) { + prot |= PROT_WRITE; + } + + entry->flags |= XEN_MAPCACHE_ENTRY_GRANT; + assert(vaddr == NULL); + vaddr_base = xengnttab_map_domain_grant_refs(xen_region_gnttabdev, + nb_pfn, + xen_domid, refs, + prot); + } else { + /* + * If the caller has requested the mapping at a specific address use + * MAP_FIXED to make sure it's honored. + * + * We don't yet support upgrading mappings from RO to RW, to handle + * models using ordinary address_space_rw(), foreign mappings ignore + * is_write and are always mapped RW. + */ + vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, + PROT_READ | PROT_WRITE, + vaddr ? MAP_FIXED : 0, + nb_pfn, pfns, err); + } if (vaddr_base == NULL) { - perror("xenforeignmemory_map2"); + perror(grant ? "xengnttab_map_domain_grant_refs" + : "xenforeignmemory_map2"); exit(-1); } } else { @@ -260,15 +318,13 @@ static void xen_remap_bucket(MapCache *mc, bitmap_set(entry->valid_mapping, i, 1); } } - - g_free(pfns); - g_free(err); } static uint8_t *xen_map_cache_unlocked(MapCache *mc, hwaddr phys_addr, hwaddr size, ram_addr_t ram_offset, - uint8_t lock, bool dma, bool is_write) + uint8_t lock, bool dma, + bool grant, bool is_write) { MapCacheEntry *entry, *pentry = NULL, *free_entry = NULL, *free_pentry = NULL; @@ -340,7 +396,7 @@ tryagain: entry = g_new0(MapCacheEntry, 1); pentry->next = entry; xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy, - ram_offset); + grant, is_write, ram_offset); } else if (!entry->lock) { if (!entry->vaddr_base || entry->paddr_index != address_index || entry->size != cache_size || @@ -348,7 +404,7 @@ tryagain: test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy, - ram_offset); + grant, is_write, ram_offset); } } @@ -399,12 +455,26 @@ uint8_t *xen_map_cache(MemoryRegion *mr, uint8_t lock, bool dma, bool is_write) { + bool grant = xen_mr_is_grants(mr); + MapCache *mc = grant ? mapcache_grants : mapcache; uint8_t *p; - mapcache_lock(mapcache); - p = xen_map_cache_unlocked(mapcache, phys_addr, size, ram_addr_offset, - lock, dma, is_write); - mapcache_unlock(mapcache); + if (grant && !lock) { + /* + * Grants are only supported via address_space_map(). Anything + * else is considered a user/guest error. + * + * QEMU generally doesn't expect these mappings to ever fail, so + * if this happens we report an error message and abort(). + */ + error_report("Tried to access a grant reference without mapping it."); + abort(); + } + + mapcache_lock(mc); + p = xen_map_cache_unlocked(mc, phys_addr, size, ram_addr_offset, + lock, dma, grant, is_write); + mapcache_unlock(mc); return p; } @@ -449,7 +519,14 @@ static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr) ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { - return xen_ram_addr_from_mapcache_single(mapcache, ptr); + ram_addr_t addr; + + addr = xen_ram_addr_from_mapcache_single(mapcache, ptr); + if (addr == RAM_ADDR_INVALID) { + addr = xen_ram_addr_from_mapcache_single(mapcache_grants, ptr); + } + + return addr; } static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, @@ -460,6 +537,7 @@ static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, hwaddr paddr_index; hwaddr size; int found = 0; + int rc; QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (reventry->vaddr_req == buffer) { @@ -502,7 +580,14 @@ static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, } ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); - if (munmap(entry->vaddr_base, entry->size) != 0) { + if (entry->flags & XEN_MAPCACHE_ENTRY_GRANT) { + rc = xengnttab_unmap(xen_region_gnttabdev, entry->vaddr_base, + entry->size >> mc->bucket_shift); + } else { + rc = munmap(entry->vaddr_base, entry->size); + } + + if (rc) { perror("unmap fails"); exit(-1); } @@ -521,14 +606,24 @@ typedef struct XenMapCacheData { uint8_t *buffer; } XenMapCacheData; +static void xen_invalidate_map_cache_entry_single(MapCache *mc, uint8_t *buffer) +{ + mapcache_lock(mc); + xen_invalidate_map_cache_entry_unlocked(mc, buffer); + mapcache_unlock(mc); +} + +static void xen_invalidate_map_cache_entry_all(uint8_t *buffer) +{ + xen_invalidate_map_cache_entry_single(mapcache, buffer); + xen_invalidate_map_cache_entry_single(mapcache_grants, buffer); +} + static void xen_invalidate_map_cache_entry_bh(void *opaque) { XenMapCacheData *data = opaque; - mapcache_lock(mapcache); - xen_invalidate_map_cache_entry_unlocked(mapcache, data->buffer); - mapcache_unlock(mapcache); - + xen_invalidate_map_cache_entry_all(data->buffer); aio_co_wake(data->co); } @@ -543,9 +638,7 @@ void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) xen_invalidate_map_cache_entry_bh, &data); qemu_coroutine_yield(); } else { - mapcache_lock(mapcache); - xen_invalidate_map_cache_entry_unlocked(mapcache, buffer); - mapcache_unlock(mapcache); + xen_invalidate_map_cache_entry_all(buffer); } } @@ -597,6 +690,7 @@ void xen_invalidate_map_cache(void) bdrv_drain_all(); xen_invalidate_map_cache_single(mapcache); + xen_invalidate_map_cache_single(mapcache_grants); } static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, @@ -632,13 +726,16 @@ static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, return NULL; } + assert((entry->flags & XEN_MAPCACHE_ENTRY_GRANT) == 0); + address_index = new_phys_addr >> mc->bucket_shift; address_offset = new_phys_addr & (mc->bucket_size - 1); trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); xen_remap_bucket(mc, entry, entry->vaddr_base, - cache_size, address_index, false, old_phys_addr); + cache_size, address_index, false, + false, false, old_phys_addr); if (!test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h index 65a51aac2e..3d796235dc 100644 --- a/include/hw/xen/xen-hvm-common.h +++ b/include/hw/xen/xen-hvm-common.h @@ -16,6 +16,7 @@ #include extern MemoryRegion xen_memory; +extern MemoryRegion xen_grants; extern MemoryListener xen_io_listener; extern DeviceListener xen_device_listener; @@ -29,6 +30,8 @@ extern DeviceListener xen_device_listener; do { } while (0) #endif +#define XEN_GRANT_ADDR_OFF (1ULL << 63) + static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) { return shared_page->vcpu_ioreq[i].vp_eport; diff --git a/include/sysemu/xen.h b/include/sysemu/xen.h index 3445888e39..d70eacfbe2 100644 --- a/include/sysemu/xen.h +++ b/include/sysemu/xen.h @@ -50,4 +50,5 @@ static inline void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, #endif /* CONFIG_XEN_IS_POSSIBLE */ bool xen_mr_is_memory(MemoryRegion *mr); +bool xen_mr_is_grants(MemoryRegion *mr); #endif From 6d87a2a311fe4a8363143e6914d000697ea0cd83 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Thu, 18 Apr 2024 16:40:58 +0200 Subject: [PATCH 1326/2884] hw/arm: xen: Enable use of grant mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Edgar E. Iglesias Reviewed-by: Stefano Stabellini Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé --- hw/arm/xen_arm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/arm/xen_arm.c b/hw/arm/xen_arm.c index 15fa7dfa84..6fad829ede 100644 --- a/hw/arm/xen_arm.c +++ b/hw/arm/xen_arm.c @@ -125,6 +125,11 @@ static void xen_init_ram(MachineState *machine) GUEST_RAM1_BASE, ram_size[1]); memory_region_add_subregion(sysmem, GUEST_RAM1_BASE, &ram_hi); } + + /* Setup support for grants. */ + memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, + &error_fatal); + memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); } void arch_handle_ioreq(XenIOState *state, ioreq_t *req) From b67e353863deec6b7fbbc728773180a8533e9e48 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev via" Date: Tue, 30 Apr 2024 19:02:13 +0200 Subject: [PATCH 1327/2884] block: drop force_dup parameter of raw_reconfigure_getfd() Since commit 72373e40fbc, this parameter is always passed as 'false' from the caller. Signed-off-by: Denis V. Lunev CC: Andrey Zhadchenko CC: Kevin Wolf CC: Hanna Reitz Message-ID: <20240430170213.148558-1-den@openvz.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/file-posix.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 35684f7e21..5c46938936 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1039,8 +1039,7 @@ static int fcntl_setfl(int fd, int flag) } static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, - int *open_flags, uint64_t perm, bool force_dup, - Error **errp) + int *open_flags, uint64_t perm, Error **errp) { BDRVRawState *s = bs->opaque; int fd = -1; @@ -1068,7 +1067,7 @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, assert((s->open_flags & O_ASYNC) == 0); #endif - if (!force_dup && *open_flags == s->open_flags) { + if (*open_flags == s->open_flags) { /* We're lucky, the existing fd is fine */ return s->fd; } @@ -3748,8 +3747,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, int ret; /* We may need a new fd if auto-read-only switches the mode */ - ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, - false, errp); + ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, errp); if (ret < 0) { return ret; } else if (ret != s->fd) { From 719c6819ed9a9838520fa732f9861918dc693bda Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 6 May 2024 15:06:21 -0400 Subject: [PATCH 1328/2884] Revert "monitor: use aio_co_reschedule_self()" Commit 1f25c172f837 ("monitor: use aio_co_reschedule_self()") was a code cleanup that uses aio_co_reschedule_self() instead of open coding coroutine rescheduling. Bug RHEL-34618 was reported and Kevin Wolf identified the root cause. I missed that aio_co_reschedule_self() -> qemu_get_current_aio_context() only knows about qemu_aio_context/IOThread AioContexts and not about iohandler_ctx. It does not function correctly when going back from the iohandler_ctx to qemu_aio_context. Go back to open coding the AioContext transitions to avoid this bug. This reverts commit 1f25c172f83704e350c0829438d832384084a74d. Cc: qemu-stable@nongnu.org Buglink: https://issues.redhat.com/browse/RHEL-34618 Signed-off-by: Stefan Hajnoczi Message-ID: <20240506190622.56095-2-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qapi/qmp-dispatch.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index f3488afeef..176b549473 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -212,7 +212,8 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ * executing the command handler so that it can make progress if it * involves an AIO_WAIT_WHILE(). */ - aio_co_reschedule_self(qemu_get_aio_context()); + aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self()); + qemu_coroutine_yield(); } monitor_set_cur(qemu_coroutine_self(), cur_mon); @@ -226,7 +227,9 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ * Move back to iohandler_ctx so that nested event loops for * qemu_aio_context don't start new monitor commands. */ - aio_co_reschedule_self(iohandler_get_aio_context()); + aio_co_schedule(iohandler_get_aio_context(), + qemu_coroutine_self()); + qemu_coroutine_yield(); } } else { /* From e669e800fc9ef8806af5c5578249ab758a4f8a5a Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 6 May 2024 15:06:22 -0400 Subject: [PATCH 1329/2884] aio: warn about iohandler_ctx special casing The main loop has two AioContexts: qemu_aio_context and iohandler_ctx. The main loop runs them both, but nested aio_poll() calls on qemu_aio_context exclude iohandler_ctx. Which one should qemu_get_current_aio_context() return when called from the main loop? Document that it's always qemu_aio_context. This has subtle effects on functions that use qemu_get_current_aio_context(). For example, aio_co_reschedule_self() does not work when moving from iohandler_ctx to qemu_aio_context because qemu_get_current_aio_context() does not differentiate these two AioContexts. Document this in order to reduce the chance of future bugs. Signed-off-by: Stefan Hajnoczi Message-ID: <20240506190622.56095-3-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/aio.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/block/aio.h b/include/block/aio.h index 8378553eb9..4ee81936ed 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -629,6 +629,9 @@ void aio_co_schedule(AioContext *ctx, Coroutine *co); * * Move the currently running coroutine to new_ctx. If the coroutine is already * running in new_ctx, do nothing. + * + * Note that this function cannot reschedule from iohandler_ctx to + * qemu_aio_context. */ void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx); @@ -661,6 +664,9 @@ void aio_co_enter(AioContext *ctx, Coroutine *co); * If called from an IOThread this will be the IOThread's AioContext. If * called from the main thread or with the "big QEMU lock" taken it * will be the main loop AioContext. + * + * Note that the return value is never the main loop's iohandler_ctx and the + * return value is the main loop AioContext instead. */ AioContext *qemu_get_current_aio_context(void); From 365911b182017f05122b88a574477ff945ae91ab Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 7 May 2024 14:05:58 -0400 Subject: [PATCH 1330/2884] qemu-io: add cvtnum() error handling for zone commands cvtnum() parses positive int64_t values and returns a negative errno on failure. Print errors and return early when cvtnum() fails. While we're at it, also reject nr_zones values greater or equal to 2^32 since they cannot be represented. Reported-by: Peter Maydell Cc: Sam Li Signed-off-by: Stefan Hajnoczi Message-ID: <20240507180558.377233-1-stefanha@redhat.com> Reviewed-by: Sam Li Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-io-cmds.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index f5d7202a13..e2fab57183 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1739,12 +1739,26 @@ static int zone_report_f(BlockBackend *blk, int argc, char **argv) { int ret; int64_t offset; + int64_t val; unsigned int nr_zones; ++optind; offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } ++optind; - nr_zones = cvtnum(argv[optind]); + val = cvtnum(argv[optind]); + if (val < 0) { + print_cvtnum_err(val, argv[optind]); + return val; + } + if (val > UINT_MAX) { + printf("Number of zones must be less than 2^32\n"); + return -ERANGE; + } + nr_zones = val; g_autofree BlockZoneDescriptor *zones = NULL; zones = g_new(BlockZoneDescriptor, nr_zones); @@ -1780,8 +1794,16 @@ static int zone_open_f(BlockBackend *blk, int argc, char **argv) int64_t offset, len; ++optind; offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } ++optind; len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } ret = blk_zone_mgmt(blk, BLK_ZO_OPEN, offset, len); if (ret < 0) { printf("zone open failed: %s\n", strerror(-ret)); @@ -1805,8 +1827,16 @@ static int zone_close_f(BlockBackend *blk, int argc, char **argv) int64_t offset, len; ++optind; offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } ++optind; len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } ret = blk_zone_mgmt(blk, BLK_ZO_CLOSE, offset, len); if (ret < 0) { printf("zone close failed: %s\n", strerror(-ret)); @@ -1830,8 +1860,16 @@ static int zone_finish_f(BlockBackend *blk, int argc, char **argv) int64_t offset, len; ++optind; offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } ++optind; len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } ret = blk_zone_mgmt(blk, BLK_ZO_FINISH, offset, len); if (ret < 0) { printf("zone finish failed: %s\n", strerror(-ret)); @@ -1855,8 +1893,16 @@ static int zone_reset_f(BlockBackend *blk, int argc, char **argv) int64_t offset, len; ++optind; offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } ++optind; len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } ret = blk_zone_mgmt(blk, BLK_ZO_RESET, offset, len); if (ret < 0) { printf("zone reset failed: %s\n", strerror(-ret)); From 10b1e09ed3c40baf9fe074e0c70a7a3b783839ff Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Mon, 29 Apr 2024 16:19:34 +0200 Subject: [PATCH 1331/2884] block/copy-before-write: use uint64_t for timeout in nanoseconds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rather than the uint32_t for which the maximum is slightly more than 4 seconds and larger values would overflow. The QAPI interface allows specifying the number of seconds, so only values 0 to 4 are safe right now, other values lead to a much lower timeout than a user expects. The block_copy() call where this is used already takes a uint64_t for the timeout, so no change required there. Fixes: 6db7fd1ca9 ("block/copy-before-write: implement cbw-timeout option") Reported-by: Friedrich Weber Signed-off-by: Fiona Ebner Message-ID: <20240429141934.442154-1-f.ebner@proxmox.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/copy-before-write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/copy-before-write.c b/block/copy-before-write.c index cd65524e26..853e01a1eb 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -43,7 +43,7 @@ typedef struct BDRVCopyBeforeWriteState { BlockCopyState *bcs; BdrvChild *target; OnCbwError on_cbw_error; - uint32_t cbw_timeout_ns; + uint64_t cbw_timeout_ns; bool discard_source; /* From 24687abf237e3c15816d689a8e4b08d7c3190dcb Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Thu, 25 Apr 2024 12:34:12 +0530 Subject: [PATCH 1332/2884] linux-aio: add IO_CMD_FDSYNC command support Libaio defines IO_CMD_FDSYNC command to sync all outstanding asynchronous I/O operations, by flushing out file data to the disk storage. Enable linux-aio to submit such aio request. When using aio=native without fdsync() support, QEMU creates pthreads, and destroying these pthreads results in TLB flushes. In a real-time guest environment, TLB flushes cause a latency spike. This patch helps to avoid such spikes. Reviewed-by: Stefan Hajnoczi Signed-off-by: Prasad Pandit Message-ID: <20240425070412.37248-1-ppandit@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/file-posix.c | 9 +++++++++ block/linux-aio.c | 21 ++++++++++++++++++++- include/block/raw-aio.h | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/block/file-posix.c b/block/file-posix.c index 5c46938936..be25e35ff6 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -159,6 +159,7 @@ typedef struct BDRVRawState { bool has_discard:1; bool has_write_zeroes:1; bool use_linux_aio:1; + bool has_laio_fdsync:1; bool use_linux_io_uring:1; int page_cache_inconsistent; /* errno from fdatasync failure */ bool has_fallocate; @@ -718,6 +719,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, ret = -EINVAL; goto fail; } + if (s->use_linux_aio) { + s->has_laio_fdsync = laio_has_fdsync(s->fd); + } #else if (s->use_linux_aio) { error_setg(errp, "aio=native was specified, but is not supported " @@ -2598,6 +2602,11 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) if (raw_check_linux_io_uring(s)) { return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); } +#endif +#ifdef CONFIG_LINUX_AIO + if (s->has_laio_fdsync && raw_check_linux_aio(s)) { + return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); + } #endif return raw_thread_pool_submit(handle_aiocb_flush, &acb); } diff --git a/block/linux-aio.c b/block/linux-aio.c index ec05d946f3..e3b5ec9aba 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -384,6 +384,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, case QEMU_AIO_READ: io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); break; + case QEMU_AIO_FLUSH: + io_prep_fdsync(iocbs, fd); + break; /* Currently Linux kernel does not support other operations */ default: fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", @@ -412,7 +415,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, AioContext *ctx = qemu_get_current_aio_context(); struct qemu_laiocb laiocb = { .co = qemu_coroutine_self(), - .nbytes = qiov->size, + .nbytes = qiov ? qiov->size : 0, .ctx = aio_get_linux_aio(ctx), .ret = -EINPROGRESS, .is_read = (type == QEMU_AIO_READ), @@ -486,3 +489,19 @@ void laio_cleanup(LinuxAioState *s) } g_free(s); } + +bool laio_has_fdsync(int fd) +{ + struct iocb cb; + struct iocb *cbs[] = {&cb, NULL}; + + io_context_t ctx = 0; + io_setup(1, &ctx); + + /* check if host kernel supports IO_CMD_FDSYNC */ + io_prep_fdsync(&cb, fd); + int ret = io_submit(ctx, 1, cbs); + + io_destroy(ctx); + return (ret == -EINVAL) ? false : true; +} diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 20e000b8ef..626706827f 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -60,6 +60,7 @@ void laio_cleanup(LinuxAioState *s); int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, int type, uint64_t dev_max_batch); +bool laio_has_fdsync(int); void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); #endif From af206c284e4c1b17cdfb0f17e898b288c0fc1751 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 27 May 2024 11:58:50 -0400 Subject: [PATCH 1333/2884] block/crypto: create ciphers on demand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ciphers are pre-allocated by qcrypto_block_init_cipher() depending on the given number of threads. The -device virtio-blk-pci,iothread-vq-mapping= feature allows users to assign multiple IOThreads to a virtio-blk device, but the association between the virtio-blk device and the block driver happens after the block driver is already open. When the number of threads given to qcrypto_block_init_cipher() is smaller than the actual number of threads at runtime, the block->n_free_ciphers > 0 assertion in qcrypto_block_pop_cipher() can fail. Get rid of qcrypto_block_init_cipher() n_thread's argument and allocate ciphers on demand. Reported-by: Qing Wang Buglink: https://issues.redhat.com/browse/RHEL-36159 Signed-off-by: Stefan Hajnoczi Message-ID: <20240527155851.892885-2-stefanha@redhat.com> Reviewed-by: Kevin Wolf Acked-by: Daniel P. Berrangé Signed-off-by: Kevin Wolf --- crypto/block-luks.c | 3 +- crypto/block-qcow.c | 2 +- crypto/block.c | 111 ++++++++++++++++++++++++++------------------ crypto/blockpriv.h | 12 +++-- 4 files changed, 78 insertions(+), 50 deletions(-) diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 3ee928fb5a..3357852c0a 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -1262,7 +1262,6 @@ qcrypto_block_luks_open(QCryptoBlock *block, luks->cipher_mode, masterkey, luks->header.master_key_len, - n_threads, errp) < 0) { goto fail; } @@ -1456,7 +1455,7 @@ qcrypto_block_luks_create(QCryptoBlock *block, /* Setup the block device payload encryption objects */ if (qcrypto_block_init_cipher(block, luks_opts.cipher_alg, luks_opts.cipher_mode, masterkey, - luks->header.master_key_len, 1, errp) < 0) { + luks->header.master_key_len, errp) < 0) { goto error; } diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c index 4d7cf36a8f..02305058e3 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -75,7 +75,7 @@ qcrypto_block_qcow_init(QCryptoBlock *block, ret = qcrypto_block_init_cipher(block, QCRYPTO_CIPHER_ALG_AES_128, QCRYPTO_CIPHER_MODE_CBC, keybuf, G_N_ELEMENTS(keybuf), - n_threads, errp); + errp); if (ret < 0) { ret = -ENOTSUP; goto fail; diff --git a/crypto/block.c b/crypto/block.c index 506ea1d1a3..ba6d1cebc7 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "blockpriv.h" #include "block-qcow.h" #include "block-luks.h" @@ -57,6 +58,8 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, { QCryptoBlock *block = g_new0(QCryptoBlock, 1); + qemu_mutex_init(&block->mutex); + block->format = options->format; if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) || @@ -76,8 +79,6 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, return NULL; } - qemu_mutex_init(&block->mutex); - return block; } @@ -92,6 +93,8 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, { QCryptoBlock *block = g_new0(QCryptoBlock, 1); + qemu_mutex_init(&block->mutex); + block->format = options->format; if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) || @@ -111,8 +114,6 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, return NULL; } - qemu_mutex_init(&block->mutex); - return block; } @@ -227,37 +228,42 @@ QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block) * This function is used only in test with one thread (it's safe to skip * pop/push interface), so it's enough to assert it here: */ - assert(block->n_ciphers <= 1); - return block->ciphers ? block->ciphers[0] : NULL; + assert(block->max_free_ciphers <= 1); + return block->free_ciphers ? block->free_ciphers[0] : NULL; } -static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block) +static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block, + Error **errp) { - QCryptoCipher *cipher; + /* Usually there is a free cipher available */ + WITH_QEMU_LOCK_GUARD(&block->mutex) { + if (block->n_free_ciphers > 0) { + block->n_free_ciphers--; + return block->free_ciphers[block->n_free_ciphers]; + } + } - qemu_mutex_lock(&block->mutex); - - assert(block->n_free_ciphers > 0); - block->n_free_ciphers--; - cipher = block->ciphers[block->n_free_ciphers]; - - qemu_mutex_unlock(&block->mutex); - - return cipher; + /* Otherwise allocate a new cipher */ + return qcrypto_cipher_new(block->alg, block->mode, block->key, + block->nkey, errp); } static void qcrypto_block_push_cipher(QCryptoBlock *block, QCryptoCipher *cipher) { - qemu_mutex_lock(&block->mutex); + QEMU_LOCK_GUARD(&block->mutex); - assert(block->n_free_ciphers < block->n_ciphers); - block->ciphers[block->n_free_ciphers] = cipher; + if (block->n_free_ciphers == block->max_free_ciphers) { + block->max_free_ciphers++; + block->free_ciphers = g_renew(QCryptoCipher *, + block->free_ciphers, + block->max_free_ciphers); + } + + block->free_ciphers[block->n_free_ciphers] = cipher; block->n_free_ciphers++; - - qemu_mutex_unlock(&block->mutex); } @@ -265,24 +271,31 @@ int qcrypto_block_init_cipher(QCryptoBlock *block, QCryptoCipherAlgorithm alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, - size_t n_threads, Error **errp) + Error **errp) { - size_t i; + QCryptoCipher *cipher; - assert(!block->ciphers && !block->n_ciphers && !block->n_free_ciphers); + assert(!block->free_ciphers && !block->max_free_ciphers && + !block->n_free_ciphers); - block->ciphers = g_new0(QCryptoCipher *, n_threads); + /* Stash away cipher parameters for qcrypto_block_pop_cipher() */ + block->alg = alg; + block->mode = mode; + block->key = g_memdup2(key, nkey); + block->nkey = nkey; - for (i = 0; i < n_threads; i++) { - block->ciphers[i] = qcrypto_cipher_new(alg, mode, key, nkey, errp); - if (!block->ciphers[i]) { - qcrypto_block_free_cipher(block); - return -1; - } - block->n_ciphers++; - block->n_free_ciphers++; + /* + * Create a new cipher to validate the parameters now. This reduces the + * chance of cipher creation failing at I/O time. + */ + cipher = qcrypto_block_pop_cipher(block, errp); + if (!cipher) { + g_free(block->key); + block->key = NULL; + return -1; } + qcrypto_block_push_cipher(block, cipher); return 0; } @@ -291,19 +304,23 @@ void qcrypto_block_free_cipher(QCryptoBlock *block) { size_t i; - if (!block->ciphers) { + g_free(block->key); + block->key = NULL; + + if (!block->free_ciphers) { return; } - assert(block->n_ciphers == block->n_free_ciphers); + /* All popped ciphers were eventually pushed back */ + assert(block->n_free_ciphers == block->max_free_ciphers); - for (i = 0; i < block->n_ciphers; i++) { - qcrypto_cipher_free(block->ciphers[i]); + for (i = 0; i < block->max_free_ciphers; i++) { + qcrypto_cipher_free(block->free_ciphers[i]); } - g_free(block->ciphers); - block->ciphers = NULL; - block->n_ciphers = block->n_free_ciphers = 0; + g_free(block->free_ciphers); + block->free_ciphers = NULL; + block->max_free_ciphers = block->n_free_ciphers = 0; } QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) @@ -311,7 +328,7 @@ QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) /* ivgen should be accessed under mutex. However, this function is used only * in test with one thread, so it's enough to assert it here: */ - assert(block->n_ciphers <= 1); + assert(block->max_free_ciphers <= 1); return block->ivgen; } @@ -446,7 +463,10 @@ int qcrypto_block_decrypt_helper(QCryptoBlock *block, Error **errp) { int ret; - QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block, errp); + if (!cipher) { + return -1; + } ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, &block->mutex, sectorsize, offset, buf, @@ -465,7 +485,10 @@ int qcrypto_block_encrypt_helper(QCryptoBlock *block, Error **errp) { int ret; - QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block, errp); + if (!cipher) { + return -1; + } ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, &block->mutex, sectorsize, offset, buf, diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h index 836f3b4726..4bf6043d5d 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -32,8 +32,14 @@ struct QCryptoBlock { const QCryptoBlockDriver *driver; void *opaque; - QCryptoCipher **ciphers; - size_t n_ciphers; + /* Cipher parameters */ + QCryptoCipherAlgorithm alg; + QCryptoCipherMode mode; + uint8_t *key; + size_t nkey; + + QCryptoCipher **free_ciphers; + size_t max_free_ciphers; size_t n_free_ciphers; QCryptoIVGen *ivgen; QemuMutex mutex; @@ -130,7 +136,7 @@ int qcrypto_block_init_cipher(QCryptoBlock *block, QCryptoCipherAlgorithm alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, - size_t n_threads, Error **errp); + Error **errp); void qcrypto_block_free_cipher(QCryptoBlock *block); From 3ab0f063e58ed9224237d69c4211ca83335164c4 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 27 May 2024 11:58:51 -0400 Subject: [PATCH 1334/2884] crypto/block: drop qcrypto_block_open() n_threads argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The n_threads argument is no longer used since the previous commit. Remove it. Signed-off-by: Stefan Hajnoczi Message-ID: <20240527155851.892885-3-stefanha@redhat.com> Reviewed-by: Kevin Wolf Acked-by: Daniel P. Berrangé Signed-off-by: Kevin Wolf --- block/crypto.c | 1 - block/qcow.c | 2 +- block/qcow2.c | 5 ++--- crypto/block-luks.c | 1 - crypto/block-qcow.c | 6 ++---- crypto/block.c | 3 +-- crypto/blockpriv.h | 1 - include/crypto/block.h | 2 -- tests/unit/test-crypto-block.c | 4 ---- 9 files changed, 6 insertions(+), 19 deletions(-) diff --git a/block/crypto.c b/block/crypto.c index 21eed909c1..4eed3ffa6a 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -363,7 +363,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, block_crypto_read_func, bs, cflags, - 1, errp); if (!crypto->block) { diff --git a/block/qcow.c b/block/qcow.c index ca8e1d5ec8..c2f89db055 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -211,7 +211,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } s->crypto = qcrypto_block_open(crypto_opts, "encrypt.", - NULL, NULL, cflags, 1, errp); + NULL, NULL, cflags, errp); if (!s->crypto) { ret = -EINVAL; goto fail; diff --git a/block/qcow2.c b/block/qcow2.c index 956128b409..10883a2494 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -321,7 +321,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, } s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", qcow2_crypto_hdr_read_func, - bs, cflags, QCOW2_MAX_THREADS, errp); + bs, cflags, errp); if (!s->crypto) { return -EINVAL; } @@ -1701,8 +1701,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", - NULL, NULL, cflags, - QCOW2_MAX_THREADS, errp); + NULL, NULL, cflags, errp); if (!s->crypto) { ret = -EINVAL; goto fail; diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 3357852c0a..5b777c15d3 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -1189,7 +1189,6 @@ qcrypto_block_luks_open(QCryptoBlock *block, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp) { QCryptoBlockLUKS *luks = NULL; diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c index 02305058e3..42e9556e42 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -44,7 +44,6 @@ qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED, static int qcrypto_block_qcow_init(QCryptoBlock *block, const char *keysecret, - size_t n_threads, Error **errp) { char *password; @@ -100,7 +99,6 @@ qcrypto_block_qcow_open(QCryptoBlock *block, QCryptoBlockReadFunc readfunc G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED, unsigned int flags, - size_t n_threads, Error **errp) { if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) { @@ -115,7 +113,7 @@ qcrypto_block_qcow_open(QCryptoBlock *block, return -1; } return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, - n_threads, errp); + errp); } } @@ -135,7 +133,7 @@ qcrypto_block_qcow_create(QCryptoBlock *block, return -1; } /* QCow2 has no special header, since everything is hardwired */ - return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, 1, errp); + return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp); } diff --git a/crypto/block.c b/crypto/block.c index ba6d1cebc7..3bcc4270c3 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -53,7 +53,6 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp) { QCryptoBlock *block = g_new0(QCryptoBlock, 1); @@ -73,7 +72,7 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, block->driver = qcrypto_block_drivers[options->format]; if (block->driver->open(block, options, optprefix, - readfunc, opaque, flags, n_threads, errp) < 0) + readfunc, opaque, flags, errp) < 0) { g_free(block); return NULL; diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h index 4bf6043d5d..b8f77cb5eb 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -59,7 +59,6 @@ struct QCryptoBlockDriver { QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp); int (*create)(QCryptoBlock *block, diff --git a/include/crypto/block.h b/include/crypto/block.h index 92e823c9f2..5b5d039800 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -76,7 +76,6 @@ typedef enum { * @readfunc: callback for reading data from the volume * @opaque: data to pass to @readfunc * @flags: bitmask of QCryptoBlockOpenFlags values - * @n_threads: allow concurrent I/O from up to @n_threads threads * @errp: pointer to a NULL-initialized error object * * Create a new block encryption object for an existing @@ -113,7 +112,6 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp); typedef enum { diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 6cfc817a92..42cfab6067 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -303,7 +303,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, - 1, NULL); g_assert(blk == NULL); @@ -312,7 +311,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, QCRYPTO_BLOCK_OPEN_NO_IO, - 1, &error_abort); g_assert(qcrypto_block_get_cipher(blk) == NULL); @@ -327,7 +325,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, - 1, &error_abort); g_assert(blk); @@ -384,7 +381,6 @@ test_luks_bad_header(gconstpointer data) test_block_read_func, &buf, 0, - 1, &err); g_assert(!blk); g_assert(err); From 0e2b9edfb6126fd9ce235a1b34ba20bbeb2547ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jun 2024 12:26:31 +0200 Subject: [PATCH 1335/2884] tracetool: Remove unused vcpu.py script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vcpu.py is pointless since commit 89aafcf2a7 ("trace: remove code that depends on setting vcpu"), remote it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Message-id: 20240606102631.78152-1-philmd@linaro.org Signed-off-by: Stefan Hajnoczi --- meson.build | 1 - scripts/tracetool/__init__.py | 8 +---- scripts/tracetool/vcpu.py | 59 ----------------------------------- 3 files changed, 1 insertion(+), 67 deletions(-) delete mode 100644 scripts/tracetool/vcpu.py diff --git a/meson.build b/meson.build index ec59effca2..91278667ea 100644 --- a/meson.build +++ b/meson.build @@ -3232,7 +3232,6 @@ tracetool_depends = files( 'scripts/tracetool/format/log_stap.py', 'scripts/tracetool/format/stap.py', 'scripts/tracetool/__init__.py', - 'scripts/tracetool/vcpu.py' ) qemu_version_cmd = [find_program('scripts/qemu-version.sh'), diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index b887540a55..7237abe0e8 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -306,13 +306,7 @@ class Event(object): fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) - event = Event(name, props, fmt, args, lineno, filename) - - # add implicit arguments when using the 'vcpu' property - import tracetool.vcpu - event = tracetool.vcpu.transform_event(event) - - return event + return Event(name, props, fmt, args, lineno, filename) def __repr__(self): """Evaluable string representation for this object.""" diff --git a/scripts/tracetool/vcpu.py b/scripts/tracetool/vcpu.py deleted file mode 100644 index d232cb1d06..0000000000 --- a/scripts/tracetool/vcpu.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Generic management for the 'vcpu' property. - -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2016, Lluís Vilanova " -__license__ = "GPL version 2 or (at your option) any later version" - -__maintainer__ = "Stefan Hajnoczi" -__email__ = "stefanha@redhat.com" - - -from tracetool import Arguments, try_import - - -def transform_event(event): - """Transform event to comply with the 'vcpu' property (if present).""" - if "vcpu" in event.properties: - event.args = Arguments([("void *", "__cpu"), event.args]) - fmt = "\"cpu=%p \"" - event.fmt = fmt + event.fmt - return event - - -def transform_args(format, event, *args, **kwargs): - """Transforms the arguments to suit the specified format. - - The format module must implement function 'vcpu_args', which receives the - implicit arguments added by the 'vcpu' property, and must return suitable - arguments for the given format. - - The function is only called for events with the 'vcpu' property. - - Parameters - ========== - format : str - Format module name. - event : Event - args, kwargs - Passed to 'vcpu_transform_args'. - - Returns - ======= - Arguments - The transformed arguments, including the non-implicit ones. - - """ - if "vcpu" in event.properties: - ok, func = try_import("tracetool.format." + format, - "vcpu_transform_args") - assert ok - assert func - return Arguments([func(event.args[:1], *args, **kwargs), - event.args[1:]]) - else: - return event.args From b4912afa5f9fc87afd82950965941e31f3b2b06c Mon Sep 17 00:00:00 2001 From: Hyman Huang Date: Fri, 24 May 2024 14:29:16 +0800 Subject: [PATCH 1336/2884] scsi-disk: Fix crash for VM configured with USB CDROM after live migration For VMs configured with the USB CDROM device: -drive file=/path/to/local/file,id=drive-usb-disk0,media=cdrom,readonly=on... -device usb-storage,drive=drive-usb-disk0,id=usb-disk0... QEMU process may crash after live migration, to reproduce the issue, configure VM (Guest OS ubuntu 20.04 or 21.10) with the following XML:
Do the live migration repeatedly, crash may happen after live migratoin, trace log at the source before live migration is as follows: 324808@1711972823.521945:usb_uhci_frame_start nr 319 324808@1711972823.521978:usb_uhci_qh_load qh 0x35cb5400 324808@1711972823.521989:usb_uhci_qh_load qh 0x35cb5480 324808@1711972823.521997:usb_uhci_td_load qh 0x35cb5480, td 0x35cbe000, ctrl 0x0, token 0xffe07f69 324808@1711972823.522010:usb_uhci_td_nextqh qh 0x35cb5480, td 0x35cbe000 324808@1711972823.522022:usb_uhci_qh_load qh 0x35cb5680 324808@1711972823.522030:usb_uhci_td_load qh 0x35cb5680, td 0x75ac5180, ctrl 0x19800000, token 0x3c903e1 324808@1711972823.522045:usb_uhci_packet_add token 0x103e1, td 0x75ac5180 324808@1711972823.522056:usb_packet_state_change bus 0, port 2, ep 2, packet 0x559f9ba14b00, state undef -> setup 324808@1711972823.522079:usb_msd_cmd_submit lun 0, tag 0x472, flags 0x00000080, len 10, data-len 8 324808@1711972823.522107:scsi_req_parsed target 0 lun 0 tag 1138 command 74 dir 1 length 8 324808@1711972823.522124:scsi_req_parsed_lba target 0 lun 0 tag 1138 command 74 lba 4096 324808@1711972823.522139:scsi_req_alloc target 0 lun 0 tag 1138 324808@1711972823.522169:scsi_req_continue target 0 lun 0 tag 1138 324808@1711972823.522181:scsi_req_data target 0 lun 0 tag 1138 len 8 324808@1711972823.522194:usb_packet_state_change bus 0, port 2, ep 2, packet 0x559f9ba14b00, state setup -> complete 324808@1711972823.522209:usb_uhci_packet_complete_success token 0x103e1, td 0x75ac5180 324808@1711972823.522219:usb_uhci_packet_del token 0x103e1, td 0x75ac5180 324808@1711972823.522232:usb_uhci_td_complete qh 0x35cb5680, td 0x75ac5180 trace log at the destination after live migration is as follows: 3286206@1711972823.951646:usb_uhci_frame_start nr 320 3286206@1711972823.951663:usb_uhci_qh_load qh 0x35cb5100 3286206@1711972823.951671:usb_uhci_qh_load qh 0x35cb5480 3286206@1711972823.951680:usb_uhci_td_load qh 0x35cb5480, td 0x35cbe000, ctrl 0x1000000, token 0xffe07f69 3286206@1711972823.951693:usb_uhci_td_nextqh qh 0x35cb5480, td 0x35cbe000 3286206@1711972823.951702:usb_uhci_qh_load qh 0x35cb5700 3286206@1711972823.951709:usb_uhci_td_load qh 0x35cb5700, td 0x75ac5240, ctrl 0x39800000, token 0xe08369 3286206@1711972823.951727:usb_uhci_queue_add token 0x8369 3286206@1711972823.951735:usb_uhci_packet_add token 0x8369, td 0x75ac5240 3286206@1711972823.951746:usb_packet_state_change bus 0, port 2, ep 1, packet 0x56066b2fb5a0, state undef -> setup 3286206@1711972823.951766:usb_msd_data_in 8/8 (scsi 8) 2024-04-01 12:00:24.665+0000: shutting down, reason=crashed The backtrace reveals the following: Program terminated with signal SIGSEGV, Segmentation fault. 0 __memmove_sse2_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:312 312 movq -8(%rsi,%rdx), %rcx [Current thread is 1 (Thread 0x7f0a9025fc00 (LWP 3286206))] (gdb) bt 0 __memmove_sse2_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:312 1 memcpy (__len=8, __src=, __dest=) at /usr/include/bits/string_fortified.h:34 2 iov_from_buf_full (iov=, iov_cnt=, offset=, buf=0x0, bytes=bytes@entry=8) at ../util/iov.c:33 3 iov_from_buf (bytes=8, buf=, offset=, iov_cnt=, iov=) at /usr/src/debug/qemu-6-6.2.0-75.7.oe1.smartx.git.40.x86_64/include/qemu/iov.h:49 4 usb_packet_copy (p=p@entry=0x56066b2fb5a0, ptr=, bytes=bytes@entry=8) at ../hw/usb/core.c:636 5 usb_msd_copy_data (s=s@entry=0x56066c62c770, p=p@entry=0x56066b2fb5a0) at ../hw/usb/dev-storage.c:186 6 usb_msd_handle_data (dev=0x56066c62c770, p=0x56066b2fb5a0) at ../hw/usb/dev-storage.c:496 7 usb_handle_packet (dev=0x56066c62c770, p=p@entry=0x56066b2fb5a0) at ../hw/usb/core.c:455 8 uhci_handle_td (s=s@entry=0x56066bd5f210, q=0x56066bb7fbd0, q@entry=0x0, qh_addr=qh_addr@entry=902518530, td=td@entry=0x7fffe6e788f0, td_addr=, int_mask=int_mask@entry=0x7fffe6e788e4) at ../hw/usb/hcd-uhci.c:885 9 uhci_process_frame (s=s@entry=0x56066bd5f210) at ../hw/usb/hcd-uhci.c:1061 10 uhci_frame_timer (opaque=opaque@entry=0x56066bd5f210) at ../hw/usb/hcd-uhci.c:1159 11 timerlist_run_timers (timer_list=0x56066af26bd0) at ../util/qemu-timer.c:642 12 qemu_clock_run_timers (type=QEMU_CLOCK_VIRTUAL) at ../util/qemu-timer.c:656 13 qemu_clock_run_all_timers () at ../util/qemu-timer.c:738 14 main_loop_wait (nonblocking=nonblocking@entry=0) at ../util/main-loop.c:542 15 qemu_main_loop () at ../softmmu/runstate.c:739 16 main (argc=, argv=, envp=) at ../softmmu/main.c:52 (gdb) frame 5 (gdb) p ((SCSIDiskReq *)s->req)->iov $1 = {iov_base = 0x0, iov_len = 0} (gdb) p/x s->req->tag $2 = 0x472 When designing the USB mass storage device model, QEMU places SCSI disk device as the backend of USB mass storage device. In addition, USB mass device driver in Guest OS conforms to the "Universal Serial Bus Mass Storage Class Bulk-Only Transport" specification in order to simulate the transform behavior between a USB controller and a USB mass device. The following shows the protocol hierarchy: +----------------+ CDROM driver | scsi command | CDROM +----------------+ +-----------------------+ USB mass | USB Mass Storage Class| USB mass storage driver | Bulk-Only Transport | storage device +-----------------------+ +----------------+ USB Controller | USB Protocol | USB device +----------------+ In the USB protocol layer, between the USB controller and USB device, at least two USB packets will be transformed when guest OS send a read operation to USB mass storage device: 1. The CBW packet, which will be delivered to the USB device's Bulk-Out endpoint. In order to simulate a read operation, the USB mass storage device parses the CBW and converts it to a SCSI command, which would be executed by CDROM(represented as SCSI disk in QEMU internally), and store the result data of the SCSI command in a buffer. 2. The DATA-IN packet, which will be delivered from the USB device's Bulk-In endpoint(fetched directly from the preceding buffer) to the USB controller. We consider UHCI to be the controller. The two packets mentioned above may have been processed by UHCI in two separate frame entries of the Frame List , and also described by two different TDs. Unlike the physical environment, a virtualized environment requires the QEMU to make sure that the result data of CBW is not lost and is delivered to the UHCI controller. Currently, these types of SCSI requests are not migrated, so QEMU cannot ensure the result data of the IO operation is not lost if there are inflight emulated SCSI requests during the live migration. Assume for the moment that the USB mass storage device is processing the CBW and storing the result data of the read operation to a buffre, live migration happens and moves the VM to the destination while not migrating the result data of the read operation. After migration, when UHCI at the destination issues a DATA-IN request to the USB mass storage device, a crash happens because USB mass storage device fetches the result data and get nothing. The scenario this patch addresses is this one. Theoretically, any device that uses the SCSI disk as a back-end would be affected by this issue. In this case, it is the USB CDROM. To fix it, inflight emulated SCSI request be migrated during live migration, similar to the DMA SCSI request. Signed-off-by: Hyman Huang Message-ID: <878c8f093f3fc2f584b5c31cb2490d9f6a12131a.1716531409.git.yong.huang@smartx.com> [Do not bump migration version, introduce compat property instead. - Paolo] Signed-off-by: Paolo Bonzini --- hw/core/machine.c | 1 + hw/scsi/scsi-disk.c | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index c93d249244..655d75c21f 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -36,6 +36,7 @@ GlobalProperty hw_compat_9_0[] = { {"arm-cpu", "backcompat-cntfrq", "true" }, + {"scsi-disk-base", "migrate-emulated-scsi-request", "false" }, {"vfio-pci", "skip-vsc-check", "false" }, }; const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 0812d39c02..a67092db6a 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -114,6 +114,7 @@ struct SCSIDiskState { * 0xffff - reserved */ uint16_t rotation_rate; + bool migrate_emulated_scsi_request; }; static void scsi_free_request(SCSIRequest *req) @@ -162,6 +163,15 @@ static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) } } +static void scsi_disk_emulate_save_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + if (s->migrate_emulated_scsi_request) { + scsi_disk_save_request(f, req); + } +} + static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); @@ -185,6 +195,15 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } +static void scsi_disk_emulate_load_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + if (s->migrate_emulated_scsi_request) { + scsi_disk_load_request(f, req); + } +} + /* * scsi_handle_rw_error has two return values. False means that the error * must be ignored, true means that the error has been processed and the @@ -2606,6 +2625,8 @@ static const SCSIReqOps scsi_disk_emulate_reqops = { .read_data = scsi_disk_emulate_read_data, .write_data = scsi_disk_emulate_write_data, .get_buf = scsi_get_buf, + .load_request = scsi_disk_emulate_load_request, + .save_request = scsi_disk_emulate_save_request, }; static const SCSIReqOps scsi_disk_dma_reqops = { @@ -3114,7 +3135,8 @@ static const TypeInfo scsi_disk_base_info = { DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ DEFINE_PROP_STRING("product", SCSIDiskState, product), \ - DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id) + DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id), \ + DEFINE_PROP_BOOL("migrate-emulated-scsi-request", SCSIDiskState, migrate_emulated_scsi_request, true) static Property scsi_hd_properties[] = { From 7682ecd48d6c177667e02a64b4287d7f31c27bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jun 2024 12:39:39 +0200 Subject: [PATCH 1337/2884] backends/tpm: Remove newline character in trace event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the 'tpm_util_show_buffer' event in two to avoid using a newline character. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Mads Ynddal Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefan Berger Message-id: 20240606103943.79116-2-philmd@linaro.org Signed-off-by: Stefan Hajnoczi --- backends/tpm/tpm_util.c | 5 +++-- backends/tpm/trace-events | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index 1856589c3b..cf138551df 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -339,10 +339,11 @@ void tpm_util_show_buffer(const unsigned char *buffer, size_t len, i; char *line_buffer, *p; - if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { + if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER_CONTENT)) { return; } len = MIN(tpm_cmd_get_size(buffer), buffer_size); + trace_tpm_util_show_buffer_header(string, len); /* * allocate enough room for 3 chars per buffer entry plus a @@ -356,7 +357,7 @@ void tpm_util_show_buffer(const unsigned char *buffer, } p += sprintf(p, "%.2X ", buffer[i]); } - trace_tpm_util_show_buffer(string, len, line_buffer); + trace_tpm_util_show_buffer_content(line_buffer); g_free(line_buffer); } diff --git a/backends/tpm/trace-events b/backends/tpm/trace-events index 1ecef42a07..cb5cfa6510 100644 --- a/backends/tpm/trace-events +++ b/backends/tpm/trace-events @@ -10,7 +10,8 @@ tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu" tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu" tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu" -tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s" +tpm_util_show_buffer_header(const char *direction, size_t len) "direction: %s len: %zu" +tpm_util_show_buffer_content(const char *buf) "%s" # tpm_emulator.c tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" From 769244f9fcd12d91c56db1ad9f318f5bb28e4907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jun 2024 12:39:40 +0200 Subject: [PATCH 1338/2884] hw/sh4: Remove newline character in trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trace events aren't designed to be multi-lines. Remove the newline character which doesn't bring much value. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Mads Ynddal Reviewed-by: Daniel P. Berrangé Message-id: 20240606103943.79116-3-philmd@linaro.org Signed-off-by: Stefan Hajnoczi --- hw/sh4/trace-events | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sh4/trace-events b/hw/sh4/trace-events index 4b61cd56c8..6bfd7eebc4 100644 --- a/hw/sh4/trace-events +++ b/hw/sh4/trace-events @@ -1,3 +1,3 @@ # sh7750.c -sh7750_porta(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "porta changed from 0x%04x to 0x%04x\npdtra=0x%04x, pctra=0x%08x" -sh7750_portb(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "portb changed from 0x%04x to 0x%04x\npdtrb=0x%04x, pctrb=0x%08x" +sh7750_porta(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "porta changed from 0x%04x to 0x%04x (pdtra=0x%04x, pctra=0x%08x)" +sh7750_portb(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "portb changed from 0x%04x to 0x%04x (pdtrb=0x%04x, pctrb=0x%08x)" From ce3d01da898ad73509c0d5a851d775670fb7ba1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jun 2024 12:39:41 +0200 Subject: [PATCH 1339/2884] hw/usb: Remove newline character in trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trace events aren't designed to be multi-lines. Remove the newline characters. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Mads Ynddal Reviewed-by: Daniel P. Berrangé Message-id: 20240606103943.79116-4-philmd@linaro.org Signed-off-by: Stefan Hajnoczi --- hw/usb/trace-events | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/usb/trace-events b/hw/usb/trace-events index fd7b90d70c..46732717a9 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -15,7 +15,7 @@ usb_ohci_exit(const char *s) "%s" # hcd-ohci.c usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at 0x%x" -usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count 0x%.8x relative %d" +usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x, flags 0x%.8x bp 0x%.8x next 0x%.8x be 0x%.8x, frame_number 0x%.8x starting_frame 0x%.8x, frame_count 0x%.8x relative %d" usb_ohci_iso_td_head_offset(uint32_t o0, uint32_t o1, uint32_t o2, uint32_t o3, uint32_t o4, uint32_t o5, uint32_t o6, uint32_t o7) "0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x" usb_ohci_iso_td_relative_frame_number_neg(int rel) "ISO_TD R=%d < 0" usb_ohci_iso_td_relative_frame_number_big(int rel, int count) "ISO_TD R=%d > FC=%d" @@ -23,7 +23,7 @@ usb_ohci_iso_td_bad_direction(int dir) "Bad direction %d" usb_ohci_iso_td_bad_bp_be(uint32_t bp, uint32_t be) "ISO_TD bp 0x%.8x be 0x%.8x" usb_ohci_iso_td_bad_cc_not_accessed(uint32_t start, uint32_t next) "ISO_TD cc != not accessed 0x%.8x 0x%.8x" usb_ohci_iso_td_bad_cc_overrun(uint32_t start, uint32_t next) "ISO_TD start_offset=0x%.8x > next_offset=0x%.8x" -usb_ohci_iso_td_so(uint32_t so, uint32_t eo, uint32_t s, uint32_t e, const char *str, ssize_t len, int ret) "0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d" +usb_ohci_iso_td_so(uint32_t so, uint32_t eo, uint32_t s, uint32_t e, const char *str, ssize_t len, int ret) "0x%.8x eo 0x%.8x sa 0x%.8x ea 0x%.8x dir %s len %zu ret %d" usb_ohci_iso_td_data_overrun(int ret, ssize_t len) "DataOverrun %d > %zu" usb_ohci_iso_td_data_underrun(int ret) "DataUnderrun %d" usb_ohci_iso_td_nak(int ret) "got NAK/STALL %d" @@ -55,7 +55,7 @@ usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s" usb_ohci_td_too_many_pending(int ep) "ep=%d" usb_ohci_td_packet_status(int status) "status=%d" usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x" -usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x" +usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u head=0x%.8x tailp=0x%.8x next=0x%.8x" usb_ohci_ed_pkt_flags(uint32_t fa, uint32_t en, uint32_t d, int s, int k, int f, uint32_t mps) "fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u" usb_ohci_hcca_read_error(uint32_t addr) "HCCA read error at 0x%x" usb_ohci_mem_read(uint32_t size, const char *name, uint32_t addr, uint32_t offs, uint32_t val) "%d %s 0x%x %d -> 0x%x" From 956f63f87826fcd96d256bdbca17d31b060940e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jun 2024 12:39:42 +0200 Subject: [PATCH 1340/2884] hw/vfio: Remove newline character in trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trace events aren't designed to be multi-lines. Remove the newline characters. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Mads Ynddal Reviewed-by: Daniel P. Berrangé Message-id: 20240606103943.79116-5-philmd@linaro.org Signed-off-by: Stefan Hajnoczi --- hw/vfio/trace-events | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 64161bf6f4..e16179b507 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -19,7 +19,7 @@ vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) vfio_msix_relo(const char *name, int bar, uint64_t offset) " (%s) BAR %d offset 0x%"PRIx64"" vfio_msi_enable(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" vfio_msi_disable(const char *name) " (%s)" -vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s ROM:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' ROM: size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_rom_read(const char *name, uint64_t addr, int size, uint64_t data) " (%s, 0x%"PRIx64", 0x%x) = 0x%"PRIx64 vfio_pci_size_rom(const char *name, int size) "%s ROM size 0x%x" vfio_vga_write(uint64_t addr, uint64_t data, int size) " (0x%"PRIx64", 0x%"PRIx64", %d)" @@ -35,7 +35,7 @@ vfio_pci_hot_reset(const char *name, const char *type) " (%s) %s" vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent devices:" vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" -vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' config: size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" vfio_attach_device(const char *name, int group_id) " (%s) group %d" vfio_detach_device(const char *name, int group_id) " (%s) group %d" From 4c2b6f328742084a5bd770af7c3a2ef07828c41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 6 Jun 2024 12:39:43 +0200 Subject: [PATCH 1341/2884] tracetool: Forbid newline character in event format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Events aren't designed to be multi-lines. Multiple events can be used instead. Prevent that format using multi-lines by forbidding the newline character. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Mads Ynddal Reviewed-by: Daniel P. Berrangé Message-id: 20240606103943.79116-6-philmd@linaro.org Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 7237abe0e8..bc03238c0f 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -301,6 +301,8 @@ class Event(object): if fmt.endswith(r'\n"'): raise ValueError("Event format must not end with a newline " "character") + if '\\n' in fmt: + raise ValueError("Event format must not use new line character") if len(fmt_trans) > 0: fmt = [fmt_trans, fmt] From 903916f0a017fe4b7789f1c6c6982333a5a71876 Mon Sep 17 00:00:00 2001 From: Chuang Xu Date: Tue, 11 Jun 2024 11:23:14 +0800 Subject: [PATCH 1342/2884] i386/cpu: fixup number of addressable IDs for processor cores in the physical package When QEMU is started with: -cpu host,host-cache-info=on,l3-cache=off \ -smp 2,sockets=1,dies=1,cores=1,threads=2 Guest can't acquire maximum number of addressable IDs for processor cores in the physical package from CPUID[04H]. When creating a CPU topology of 1 core per package, host-cache-info only uses the Host's addressable core IDs field (CPUID.04H.EAX[bits 31-26]), resulting in a conflict (on the multicore Host) between the Guest core topology information in this field and the Guest's actual cores number. Fix it by removing the unnecessary condition to cover 1 core per package case. This is safe because cores_per_pkg will not be 0 and will be at least 1. Fixes: d7caf13b5fcf ("x86: cpu: fixup number of addressable IDs for logical processors sharing cache") Signed-off-by: Guixiong Wei Signed-off-by: Yipeng Yin Signed-off-by: Chuang Xu Reviewed-by: Zhao Liu Message-ID: <20240611032314.64076-1-xuchuangxclwt@bytedance.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 7466217d5e..365852cb99 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6455,10 +6455,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (*eax & 31) { int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); - if (cores_per_pkg > 1) { - *eax &= ~0xFC000000; - *eax |= max_core_ids_in_package(&topo_info) << 26; - } + *eax &= ~0xFC000000; + *eax |= max_core_ids_in_package(&topo_info) << 26; if (host_vcpus_per_cache > threads_per_pkg) { *eax &= ~0x3FFC000; From c94eb5db8e409c932da9eb187e68d4cdc14acc5b Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Fri, 7 Jun 2024 13:36:09 -0500 Subject: [PATCH 1343/2884] i386/sev: fix unreachable code coverity issue Set 'finish->id_block_en' early, so that it is properly reset. Fixes coverity CID 1546887. Fixes: 7b34df4426 ("i386/sev: Introduce 'sev-snp-guest' object") Signed-off-by: Pankaj Gupta Message-ID: <20240607183611.1111100-2-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 004c667ac1..7c9df621de 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2165,6 +2165,7 @@ sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; gsize len; + finish->id_block_en = 0; g_free(sev_snp_guest->id_block); g_free((guchar *)finish->id_block_uaddr); @@ -2184,7 +2185,7 @@ sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) return; } - finish->id_block_en = (len) ? 1 : 0; + finish->id_block_en = 1; } static char * From 48779faef3c8e2fe70bd8285bffa731bd76dc844 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Fri, 7 Jun 2024 13:36:10 -0500 Subject: [PATCH 1344/2884] i386/sev: Move SEV_COMMON null check before dereferencing Fixes Coverity CID 1546886. Fixes: 9861405a8f ("i386/sev: Invoke launch_updata_data() for SEV class") Signed-off-by: Pankaj Gupta Message-ID: <20240607183611.1111100-3-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 7c9df621de..f18432f58e 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1529,11 +1529,12 @@ int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); + SevCommonStateClass *klass; if (!sev_common) { return 0; } + klass = SEV_COMMON_GET_CLASS(sev_common); /* if SEV is in update state then encrypt the data else do nothing */ if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { From cd7093a7a168a823d07671348996f049d45e8f67 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Fri, 7 Jun 2024 13:36:11 -0500 Subject: [PATCH 1345/2884] i386/sev: Return when sev_common is null Fixes Coverity CID 1546885. Fixes: 16dcf200dc ("i386/sev: Introduce "sev-common" type to encapsulate common SEV state") Signed-off-by: Pankaj Gupta Message-ID: <20240607183611.1111100-4-pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index f18432f58e..c40562dce3 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -587,6 +587,7 @@ static SevCapability *sev_get_capabilities(Error **errp) sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_common) { error_setg(errp, "SEV is not configured"); + return NULL; } sev_device = object_property_get_str(OBJECT(sev_common), "sev-device", From 4228eb8cc6ba44d35cd52b05508a47e780668051 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 16:04:46 +0200 Subject: [PATCH 1346/2884] target/i386: remove CPUX86State argument from generator functions CPUX86State argument would only be used to fetch bytes, but that has to be done before the generator function is called. So remove it, and all temptation together with it. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 +- target/i386/tcg/decode-new.h | 2 +- target/i386/tcg/emit.c.inc | 572 +++++++++++++++---------------- 3 files changed, 289 insertions(+), 289 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index c2d8da8d14..e7d8802048 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2590,7 +2590,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) } if (s->prefix & PREFIX_LOCK) { gen_load(s, &decode, 2, s->T1); - decode.e.gen(s, env, &decode); + decode.e.gen(s, &decode); } else { if (decode.op[0].unit == X86_OP_MMX) { compute_mmx_offset(&decode.op[0]); @@ -2599,7 +2599,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) } gen_load(s, &decode, 1, s->T0); gen_load(s, &decode, 2, s->T1); - decode.e.gen(s, env, &decode); + decode.e.gen(s, &decode); gen_writeback(s, &decode, 0, s->T0); } diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 1f90cf9640..f704698575 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -245,7 +245,7 @@ typedef struct X86DecodedInsn X86DecodedInsn; typedef void (*X86DecodeFunc)(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b); /* Code generation function. */ -typedef void (*X86GenFunc)(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); +typedef void (*X86GenFunc)(DisasContext *s, X86DecodedInsn *decode); struct X86OpEntry { /* Based on the is_decode flags. */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 4be3d9a6fb..df7597c7e2 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -60,8 +60,8 @@ typedef void (*SSEFunc_0_eppppii)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 even, TCGv_i32 odd); -static void gen_JMP_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); -static void gen_JMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); +static void gen_JMP_m(DisasContext *s, X86DecodedInsn *decode); +static void gen_JMP(DisasContext *s, X86DecodedInsn *decode); static inline TCGv_i32 tcg_constant8u_i32(uint8_t val) { @@ -446,7 +446,7 @@ static const SSEFunc_0_epp fns_3dnow[] = { [0xbf] = gen_helper_pavgusb, }; -static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_3dnow(DisasContext *s, X86DecodedInsn *decode) { uint8_t b = decode->immediate; SSEFunc_0_epp fn = b < ARRAY_SIZE(fns_3dnow) ? fns_3dnow[b] : NULL; @@ -479,7 +479,7 @@ static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) * f3 = v*ss Vss, Hss, Wps * f2 = v*sd Vsd, Hsd, Wps */ -static inline void gen_unary_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_unary_fp_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epp pd_xmm, SSEFunc_0_epp ps_xmm, SSEFunc_0_epp pd_ymm, SSEFunc_0_epp ps_ymm, SSEFunc_0_eppp sd, SSEFunc_0_eppp ss) @@ -504,9 +504,9 @@ static inline void gen_unary_fp_sse(DisasContext *s, CPUX86State *env, X86Decode } } #define UNARY_FP_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_fp_sse(s, env, decode, \ + gen_unary_fp_sse(s, decode, \ gen_helper_##lname##pd_xmm, \ gen_helper_##lname##ps_xmm, \ gen_helper_##lname##pd_ymm, \ @@ -522,7 +522,7 @@ UNARY_FP_SSE(VSQRT, sqrt) * f3 = v*ss Vss, Hss, Wps * f2 = v*sd Vsd, Hsd, Wps */ -static inline void gen_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_fp_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppp pd_xmm, SSEFunc_0_eppp ps_xmm, SSEFunc_0_eppp pd_ymm, SSEFunc_0_eppp ps_ymm, SSEFunc_0_eppp sd, SSEFunc_0_eppp ss) @@ -543,9 +543,9 @@ static inline void gen_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn } #define FP_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_fp_sse(s, env, decode, \ + gen_fp_sse(s, decode, \ gen_helper_##lname##pd_xmm, \ gen_helper_##lname##ps_xmm, \ gen_helper_##lname##pd_ymm, \ @@ -561,7 +561,7 @@ FP_SSE(VDIV, div) FP_SSE(VMAX, max) #define FMA_SSE_PACKED(uname, ptr0, ptr1, ptr2, even, odd) \ -static void gen_##uname##Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname##Px(DisasContext *s, X86DecodedInsn *decode) \ { \ SSEFunc_0_eppppii xmm = s->vex_w ? gen_helper_fma4pd_xmm : gen_helper_fma4ps_xmm; \ SSEFunc_0_eppppii ymm = s->vex_w ? gen_helper_fma4pd_ymm : gen_helper_fma4ps_ymm; \ @@ -574,7 +574,7 @@ static void gen_##uname##Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *d #define FMA_SSE(uname, ptr0, ptr1, ptr2, flags) \ FMA_SSE_PACKED(uname, ptr0, ptr1, ptr2, flags, flags) \ -static void gen_##uname##Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname##Sx(DisasContext *s, X86DecodedInsn *decode) \ { \ SSEFunc_0_eppppi fn = s->vex_w ? gen_helper_fma4sd : gen_helper_fma4ss; \ \ @@ -607,10 +607,10 @@ FMA_SSE_PACKED(VFMSUBADD213, OP_PTR1, OP_PTR0, OP_PTR2, 0, float_muladd_negate_c FMA_SSE_PACKED(VFMSUBADD132, OP_PTR0, OP_PTR2, OP_PTR1, 0, float_muladd_negate_c) #define FP_UNPACK_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ /* PS maps to the DQ integer instruction, PD maps to QDQ. */ \ - gen_fp_sse(s, env, decode, \ + gen_fp_sse(s, decode, \ gen_helper_##lname##qdq_xmm, \ gen_helper_##lname##dq_xmm, \ gen_helper_##lname##qdq_ymm, \ @@ -624,7 +624,7 @@ FP_UNPACK_SSE(VUNPCKHPx, punpckh) * 00 = v*ps Vps, Wpd * f3 = v*ss Vss, Wps */ -static inline void gen_unary_fp32_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_unary_fp32_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epp ps_xmm, SSEFunc_0_epp ps_ymm, SSEFunc_0_eppp ss) @@ -649,9 +649,9 @@ illegal_op: gen_illegal_opcode(s); } #define UNARY_FP32_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_fp32_sse(s, env, decode, \ + gen_unary_fp32_sse(s, decode, \ gen_helper_##lname##ps_xmm, \ gen_helper_##lname##ps_ymm, \ gen_helper_##lname##ss); \ @@ -663,7 +663,7 @@ UNARY_FP32_SSE(VRCP, rcp) * 66 = v*pd Vpd, Hpd, Wpd * f2 = v*ps Vps, Hps, Wps */ -static inline void gen_horizontal_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_horizontal_fp_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppp pd_xmm, SSEFunc_0_eppp ps_xmm, SSEFunc_0_eppp pd_ymm, SSEFunc_0_eppp ps_ymm) { @@ -674,9 +674,9 @@ static inline void gen_horizontal_fp_sse(DisasContext *s, CPUX86State *env, X86D fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } #define HORIZONTAL_FP_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_horizontal_fp_sse(s, env, decode, \ + gen_horizontal_fp_sse(s, decode, \ gen_helper_##lname##pd_xmm, gen_helper_##lname##ps_xmm, \ gen_helper_##lname##pd_ymm, gen_helper_##lname##ps_ymm); \ } @@ -684,7 +684,7 @@ HORIZONTAL_FP_SSE(VHADD, hadd) HORIZONTAL_FP_SSE(VHSUB, hsub) HORIZONTAL_FP_SSE(VADDSUB, addsub) -static inline void gen_ternary_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_ternary_sse(DisasContext *s, X86DecodedInsn *decode, int op3, SSEFunc_0_epppp xmm, SSEFunc_0_epppp ymm) { SSEFunc_0_epppp fn = s->vex_l ? ymm : xmm; @@ -695,21 +695,21 @@ static inline void gen_ternary_sse(DisasContext *s, CPUX86State *env, X86Decoded fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, ptr3); } #define TERNARY_SSE(uname, uvname, lname) \ -static void gen_##uvname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uvname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_ternary_sse(s, env, decode, (uint8_t)decode->immediate >> 4, \ + gen_ternary_sse(s, decode, (uint8_t)decode->immediate >> 4, \ gen_helper_##lname##_xmm, gen_helper_##lname##_ymm); \ } \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_ternary_sse(s, env, decode, 0, \ + gen_ternary_sse(s, decode, 0, \ gen_helper_##lname##_xmm, gen_helper_##lname##_ymm); \ } TERNARY_SSE(BLENDVPS, VBLENDVPS, blendvps) TERNARY_SSE(BLENDVPD, VBLENDVPD, blendvpd) TERNARY_SSE(PBLENDVB, VPBLENDVB, pblendvb) -static inline void gen_binary_imm_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_binary_imm_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epppi xmm, SSEFunc_0_epppi ymm) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -721,9 +721,9 @@ static inline void gen_binary_imm_sse(DisasContext *s, CPUX86State *env, X86Deco } #define BINARY_IMM_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_binary_imm_sse(s, env, decode, \ + gen_binary_imm_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -739,7 +739,7 @@ BINARY_IMM_SSE(PCLMULQDQ, pclmulqdq) #define UNARY_INT_GVEC(uname, func, ...) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ int vec_len = vector_len(s, decode); \ \ @@ -757,7 +757,7 @@ UNARY_INT_GVEC(VPBROADCASTQ, tcg_gen_gvec_dup_mem, MO_64) #define BINARY_INT_GVEC(uname, func, ...) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ int vec_len = vector_len(s, decode); \ \ @@ -816,7 +816,7 @@ BINARY_INT_GVEC(PXOR, tcg_gen_gvec_xor, MO_64) * These are really the same encoding, because 1) V is the same as P when VEX.V * is not present 2) P and Q are the same as H and W apart from MM/XMM */ -static inline void gen_binary_int_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_binary_int_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppp mmx, SSEFunc_0_eppp xmm, SSEFunc_0_eppp ymm) { assert(!!mmx == !!(decode->e.special == X86_SPECIAL_MMX)); @@ -837,9 +837,9 @@ static inline void gen_binary_int_sse(DisasContext *s, CPUX86State *env, X86Deco #define BINARY_INT_MMX(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_binary_int_sse(s, env, decode, \ + gen_binary_int_sse(s, decode, \ gen_helper_##lname##_mmx, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ @@ -886,9 +886,9 @@ BINARY_INT_MMX(PMULHRSW, pmulhrsw) /* Instructions with no MMX equivalent. */ #define BINARY_INT_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_binary_int_sse(s, env, decode, \ + gen_binary_int_sse(s, decode, \ NULL, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ @@ -911,7 +911,7 @@ BINARY_INT_SSE(VAESENC, aesenc) BINARY_INT_SSE(VAESENCLAST, aesenclast) #define UNARY_CMP_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ if (!s->vex_l) { \ gen_helper_##lname##_xmm(tcg_env, OP_PTR1, OP_PTR2); \ @@ -924,7 +924,7 @@ UNARY_CMP_SSE(VPTEST, ptest) UNARY_CMP_SSE(VTESTPS, vtestps) UNARY_CMP_SSE(VTESTPD, vtestpd) -static inline void gen_unary_int_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_unary_int_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epp xmm, SSEFunc_0_epp ymm) { if (!s->vex_l) { @@ -935,9 +935,9 @@ static inline void gen_unary_int_sse(DisasContext *s, CPUX86State *env, X86Decod } #define UNARY_INT_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_int_sse(s, env, decode, \ + gen_unary_int_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -969,7 +969,7 @@ UNARY_INT_SSE(VCVTTPS2DQ, cvttps2dq) UNARY_INT_SSE(VCVTPH2PS, cvtph2ps) -static inline void gen_unary_imm_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_unary_imm_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_ppi xmm, SSEFunc_0_ppi ymm) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -981,9 +981,9 @@ static inline void gen_unary_imm_sse(DisasContext *s, CPUX86State *env, X86Decod } #define UNARY_IMM_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_imm_sse(s, env, decode, \ + gen_unary_imm_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -996,7 +996,7 @@ UNARY_IMM_SSE(VPERMQ, vpermq) UNARY_IMM_SSE(VPERMILPS_i, vpermilps_imm) UNARY_IMM_SSE(VPERMILPD_i, vpermilpd_imm) -static inline void gen_unary_imm_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_unary_imm_fp_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppi xmm, SSEFunc_0_eppi ymm) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -1008,9 +1008,9 @@ static inline void gen_unary_imm_fp_sse(DisasContext *s, CPUX86State *env, X86De } #define UNARY_IMM_FP_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_imm_fp_sse(s, env, decode, \ + gen_unary_imm_fp_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -1018,7 +1018,7 @@ static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod UNARY_IMM_FP_SSE(VROUNDPS, roundps) UNARY_IMM_FP_SSE(VROUNDPD, roundpd) -static inline void gen_vexw_avx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_vexw_avx(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppp d_xmm, SSEFunc_0_eppp q_xmm, SSEFunc_0_eppp d_ymm, SSEFunc_0_eppp q_ymm) { @@ -1030,9 +1030,9 @@ static inline void gen_vexw_avx(DisasContext *s, CPUX86State *env, X86DecodedIns /* VEX.W affects whether to operate on 32- or 64-bit elements. */ #define VEXW_AVX(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_vexw_avx(s, env, decode, \ + gen_vexw_avx(s, decode, \ gen_helper_##lname##d_xmm, gen_helper_##lname##q_xmm, \ gen_helper_##lname##d_ymm, gen_helper_##lname##q_ymm); \ } @@ -1042,7 +1042,7 @@ VEXW_AVX(VPSRAV, vpsrav) VEXW_AVX(VPMASKMOV, vpmaskmov) /* Same as above, but with extra arguments to the helper. */ -static inline void gen_vsib_avx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_vsib_avx(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epppti d_xmm, SSEFunc_0_epppti q_xmm, SSEFunc_0_epppti d_ymm, SSEFunc_0_epppti q_ymm) { @@ -1066,29 +1066,29 @@ static inline void gen_vsib_avx(DisasContext *s, CPUX86State *env, X86DecodedIns } } #define VSIB_AVX(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_vsib_avx(s, env, decode, \ + gen_vsib_avx(s, decode, \ gen_helper_##lname##d_xmm, gen_helper_##lname##q_xmm, \ gen_helper_##lname##d_ymm, gen_helper_##lname##q_ymm); \ } VSIB_AVX(VPGATHERD, vpgatherd) VSIB_AVX(VPGATHERQ, vpgatherq) -static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_AAA(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_aaa(tcg_env); assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_AAD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_AAD(DisasContext *s, X86DecodedInsn *decode) { gen_helper_aad(s->T0, s->T0, s->T1); prepare_update1_cc(decode, s, CC_OP_LOGICB); } -static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_AAM(DisasContext *s, X86DecodedInsn *decode) { if (decode->immediate == 0) { gen_exception(s, EXCP00_DIVZ); @@ -1098,14 +1098,14 @@ static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_AAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_AAS(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_aas(tcg_env); assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_ADC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ADC(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; TCGv c_in = tcg_temp_new(); @@ -1123,7 +1123,7 @@ static void gen_ADC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } /* ADCX/ADOX do not have memory operands and can use set_cc_op. */ -static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) +static void gen_ADCOX(DisasContext *s, MemOp ot, int cc_op) { int opposite_cc_op; TCGv carry_in = NULL; @@ -1170,12 +1170,12 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) } } -static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ADCX(DisasContext *s, X86DecodedInsn *decode) { - gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADCX); + gen_ADCOX(s, decode->op[0].ot, CC_OP_ADCX); } -static void gen_ADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ADD(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1188,12 +1188,12 @@ static void gen_ADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_ADDB + ot); } -static void gen_ADOX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ADOX(DisasContext *s, X86DecodedInsn *decode) { - gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADOX); + gen_ADCOX(s, decode->op[0].ot, CC_OP_ADOX); } -static void gen_AND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_AND(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1206,7 +1206,7 @@ static void gen_AND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ANDN(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1214,7 +1214,7 @@ static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -static void gen_ARPL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ARPL(DisasContext *s, X86DecodedInsn *decode) { TCGv zf = tcg_temp_new(); TCGv flags = tcg_temp_new(); @@ -1235,7 +1235,7 @@ static void gen_ARPL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) decode->cc_op = CC_OP_EFLAGS; } -static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BEXTR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); @@ -1264,7 +1264,7 @@ static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } /* BLSI do not have memory operands and can use set_cc_op. */ -static void gen_BLSI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BLSI(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1276,7 +1276,7 @@ static void gen_BLSI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } /* BLSMSK do not have memory operands and can use set_cc_op. */ -static void gen_BLSMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BLSMSK(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1288,7 +1288,7 @@ static void gen_BLSMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode } /* BLSR do not have memory operands and can use set_cc_op. */ -static void gen_BLSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BLSR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1299,7 +1299,7 @@ static void gen_BLSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) set_cc_op(s, CC_OP_BMILGB + ot); } -static void gen_BOUND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BOUND(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 op = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(op, s->T0); @@ -1310,7 +1310,7 @@ static void gen_BOUND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_BSWAP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BSWAP(DisasContext *s, X86DecodedInsn *decode) { #ifdef TARGET_X86_64 if (s->dflag == MO_64) { @@ -1321,7 +1321,7 @@ static void gen_BSWAP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_bswap32_tl(s->T0, s->T0, TCG_BSWAP_OZ); } -static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); @@ -1341,24 +1341,24 @@ static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -static void gen_CALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALL(DisasContext *s, X86DecodedInsn *decode) { gen_push_v(s, eip_next_tl(s)); - gen_JMP(s, env, decode); + gen_JMP(s, decode); } -static void gen_CALL_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALL_m(DisasContext *s, X86DecodedInsn *decode) { gen_push_v(s, eip_next_tl(s)); - gen_JMP_m(s, env, decode); + gen_JMP_m(s, decode); } -static void gen_CALLF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALLF(DisasContext *s, X86DecodedInsn *decode) { gen_far_call(s); } -static void gen_CALLF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALLF_m(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1368,41 +1368,41 @@ static void gen_CALLF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_far_call(s); } -static void gen_CBW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CBW(DisasContext *s, X86DecodedInsn *decode) { MemOp src_ot = decode->op[0].ot - 1; tcg_gen_ext_tl(s->T0, s->T0, src_ot | MO_SIGN); } -static void gen_CLC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CLC(DisasContext *s, X86DecodedInsn *decode) { gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); } -static void gen_CLD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CLD(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, offsetof(CPUX86State, df)); } -static void gen_CLI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CLI(DisasContext *s, X86DecodedInsn *decode) { gen_reset_eflags(s, IF_MASK); } -static void gen_CMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMC(DisasContext *s, X86DecodedInsn *decode) { gen_compute_eflags(s); tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); } -static void gen_CMOVcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMOVcc(DisasContext *s, X86DecodedInsn *decode) { gen_cmovcc1(s, decode->b & 0xf, s->T0, s->T1); } -static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMPccXADD(DisasContext *s, X86DecodedInsn *decode) { TCGLabel *label_top = gen_new_label(); TCGLabel *label_bottom = gen_new_label(); @@ -1505,7 +1505,7 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec decode->cc_op = CC_OP_SUBB + ot; } -static void gen_CMPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMPS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -1515,7 +1515,7 @@ static void gen_CMPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CRC32(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1523,7 +1523,7 @@ static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_helper_crc32(s->T0, s->tmp2_i32, s->T1, tcg_constant_i32(8 << ot)); } -static void gen_CVTPI2Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CVTPI2Px(DisasContext *s, X86DecodedInsn *decode) { gen_helper_enter_mmx(tcg_env); if (s->prefix & PREFIX_DATA) { @@ -1533,7 +1533,7 @@ static void gen_CVTPI2Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } -static void gen_CVTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CVTPx2PI(DisasContext *s, X86DecodedInsn *decode) { gen_helper_enter_mmx(tcg_env); if (s->prefix & PREFIX_DATA) { @@ -1543,7 +1543,7 @@ static void gen_CVTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } -static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CVTTPx2PI(DisasContext *s, X86DecodedInsn *decode) { gen_helper_enter_mmx(tcg_env); if (s->prefix & PREFIX_DATA) { @@ -1553,28 +1553,28 @@ static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_CWD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CWD(DisasContext *s, X86DecodedInsn *decode) { int shift = 8 << decode->op[0].ot; tcg_gen_sextract_tl(s->T0, s->T0, shift - 1, 1); } -static void gen_DAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_DAA(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_daa(tcg_env); assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_DAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_DAS(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_das(tcg_env); assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_DEC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_DEC(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1588,7 +1588,7 @@ static void gen_DEC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update_cc_incdec(decode, s, CC_OP_DECB + ot); } -static void gen_DIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_DIV(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1611,17 +1611,17 @@ static void gen_DIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_EMMS(DisasContext *s, X86DecodedInsn *decode) { gen_helper_emms(tcg_env); } -static void gen_ENTER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ENTER(DisasContext *s, X86DecodedInsn *decode) { gen_enter(s, decode->op[1].imm, decode->op[2].imm); } -static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_EXTRQ_i(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); @@ -1629,12 +1629,12 @@ static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_helper_extrq_i(tcg_env, OP_PTR0, index, length); } -static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_EXTRQ_r(DisasContext *s, X86DecodedInsn *decode) { gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); } -static void gen_HLT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_HLT(DisasContext *s, X86DecodedInsn *decode) { #ifdef CONFIG_SYSTEM_ONLY gen_update_cc_op(s); @@ -1644,7 +1644,7 @@ static void gen_HLT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) #endif } -static void gen_IDIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IDIV(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1667,7 +1667,7 @@ static void gen_IDIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_IMUL3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IMUL3(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv cc_src_rhs; @@ -1730,7 +1730,7 @@ static void gen_IMUL3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_MULB + ot); } -static void gen_IMUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IMUL(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; TCGv cc_src_rhs; @@ -1788,7 +1788,7 @@ static void gen_IMUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_MULB + ot); } -static void gen_IN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IN(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv_i32 port = tcg_temp_new_i32(); @@ -1804,7 +1804,7 @@ static void gen_IN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_bpt_io(s, port, ot); } -static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INC(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -1818,7 +1818,7 @@ static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update_cc_incdec(decode, s, CC_OP_INCB + ot); } -static void gen_INS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; TCGv_i32 port = tcg_temp_new_i32(); @@ -1838,7 +1838,7 @@ static void gen_INS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INSERTQ_i(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); @@ -1846,17 +1846,17 @@ static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec gen_helper_insertq_i(tcg_env, OP_PTR0, OP_PTR1, index, length); } -static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INSERTQ_r(DisasContext *s, X86DecodedInsn *decode) { gen_helper_insertq_r(tcg_env, OP_PTR0, OP_PTR2); } -static void gen_INT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INT(DisasContext *s, X86DecodedInsn *decode) { gen_interrupt(s, decode->immediate); } -static void gen_INT1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INT1(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_update_eip_next(s); @@ -1864,19 +1864,19 @@ static void gen_INT1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) s->base.is_jmp = DISAS_NORETURN; } -static void gen_INT3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INT3(DisasContext *s, X86DecodedInsn *decode) { gen_interrupt(s, EXCP03_INT3); } -static void gen_INTO(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INTO(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_update_eip_cur(s); gen_helper_into(tcg_env, cur_insn_len_i32(s)); } -static void gen_IRET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IRET(DisasContext *s, X86DecodedInsn *decode) { if (!PE(s) || VM86(s)) { gen_helper_iret_real(tcg_env, tcg_constant_i32(s->dflag - 1)); @@ -1888,13 +1888,13 @@ static void gen_IRET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) s->base.is_jmp = DISAS_EOB_ONLY; } -static void gen_Jcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_Jcc(DisasContext *s, X86DecodedInsn *decode) { gen_bnd_jmp(s); gen_jcc(s, decode->b & 0xf, decode->immediate); } -static void gen_JCXZ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_JCXZ(DisasContext *s, X86DecodedInsn *decode) { TCGLabel *taken = gen_new_label(); @@ -1903,25 +1903,25 @@ static void gen_JCXZ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_conditional_jump_labels(s, decode->immediate, NULL, taken); } -static void gen_JMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_JMP(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_jmp_rel(s, s->dflag, decode->immediate, 0); } -static void gen_JMP_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_JMP_m(DisasContext *s, X86DecodedInsn *decode) { gen_op_jmp_v(s, s->T0); gen_bnd_jmp(s); s->base.is_jmp = DISAS_JUMP; } -static void gen_JMPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_JMPF(DisasContext *s, X86DecodedInsn *decode) { gen_far_jmp(s); } -static void gen_JMPF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_JMPF_m(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1931,7 +1931,7 @@ static void gen_JMPF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode gen_far_jmp(s); } -static void gen_LAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LAHF(DisasContext *s, X86DecodedInsn *decode) { if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { return gen_illegal_opcode(s); @@ -1942,13 +1942,13 @@ static void gen_LAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); } -static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LDMXCSR(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); } -static void gen_lxx_seg(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, int seg) +static void gen_lxx_seg(DisasContext *s, X86DecodedInsn *decode, int seg) { MemOp ot = decode->op[0].ot; @@ -1960,37 +1960,37 @@ static void gen_lxx_seg(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod gen_movl_seg(s, seg, s->T1); } -static void gen_LDS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LDS(DisasContext *s, X86DecodedInsn *decode) { - gen_lxx_seg(s, env, decode, R_DS); + gen_lxx_seg(s, decode, R_DS); } -static void gen_LEA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LEA(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_mov_tl(s->T0, s->A0); } -static void gen_LEAVE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LEAVE(DisasContext *s, X86DecodedInsn *decode) { gen_leave(s); } -static void gen_LES(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LES(DisasContext *s, X86DecodedInsn *decode) { - gen_lxx_seg(s, env, decode, R_ES); + gen_lxx_seg(s, decode, R_ES); } -static void gen_LFS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LFS(DisasContext *s, X86DecodedInsn *decode) { - gen_lxx_seg(s, env, decode, R_FS); + gen_lxx_seg(s, decode, R_FS); } -static void gen_LGS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LGS(DisasContext *s, X86DecodedInsn *decode) { - gen_lxx_seg(s, env, decode, R_GS); + gen_lxx_seg(s, decode, R_GS); } -static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LODS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -2000,7 +2000,7 @@ static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_LOOP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LOOP(DisasContext *s, X86DecodedInsn *decode) { TCGLabel *taken = gen_new_label(); @@ -2010,7 +2010,7 @@ static void gen_LOOP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_conditional_jump_labels(s, decode->immediate, NULL, taken); } -static void gen_LOOPE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LOOPE(DisasContext *s, X86DecodedInsn *decode) { TCGLabel *taken = gen_new_label(); TCGLabel *not_taken = gen_new_label(); @@ -2022,7 +2022,7 @@ static void gen_LOOPE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); } -static void gen_LOOPNE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LOOPNE(DisasContext *s, X86DecodedInsn *decode) { TCGLabel *taken = gen_new_label(); TCGLabel *not_taken = gen_new_label(); @@ -2034,18 +2034,18 @@ static void gen_LOOPNE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); } -static void gen_LSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_LSS(DisasContext *s, X86DecodedInsn *decode) { - gen_lxx_seg(s, env, decode, R_SS); + gen_lxx_seg(s, decode, R_SS); } -static void gen_MOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOV(DisasContext *s, X86DecodedInsn *decode) { /* nothing to do! */ } #define gen_NOP gen_MOV -static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MASKMOV(DisasContext *s, X86DecodedInsn *decode) { gen_lea_v_seg(s, cpu_regs[R_EDI], R_DS, s->override); @@ -2056,7 +2056,7 @@ static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_MOVBE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVBE(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2068,7 +2068,7 @@ static void gen_MOVBE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_MOVD_from(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVD_from(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -2086,7 +2086,7 @@ static void gen_MOVD_from(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_MOVD_to(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVD_to(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; int vec_len = vector_len(s, decode); @@ -2108,12 +2108,12 @@ static void gen_MOVD_to(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_MOVDQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVDQ(DisasContext *s, X86DecodedInsn *decode) { gen_store_sse(s, decode, decode->op[2].offset); } -static void gen_MOVMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVMSK(DisasContext *s, X86DecodedInsn *decode) { typeof(gen_helper_movmskps_ymm) *ps, *pd, *fn; ps = s->vex_l ? gen_helper_movmskps_ymm : gen_helper_movmskps_xmm; @@ -2123,7 +2123,7 @@ static void gen_MOVMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); } -static void gen_MOVQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVQ(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); int lo_ofs = vector_elem_offset(&decode->op[0], MO_64, 0); @@ -2145,14 +2145,14 @@ static void gen_MOVQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_MOVq_dq(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVq_dq(DisasContext *s, X86DecodedInsn *decode) { gen_helper_enter_mmx(tcg_env); /* Otherwise the same as any other movq. */ - return gen_MOVQ(s, env, decode); + return gen_MOVQ(s, decode); } -static void gen_MOVS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -2162,7 +2162,7 @@ static void gen_MOVS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_MUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MUL(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -2213,7 +2213,7 @@ static void gen_MUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) decode->cc_op = CC_OP_MULB + ot; } -static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MULX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2239,7 +2239,7 @@ static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_NEG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_NEG(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv oldv = tcg_temp_new(); @@ -2266,7 +2266,7 @@ static void gen_NEG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) decode->cc_op = CC_OP_SUBB + ot; } -static void gen_NOT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_NOT(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -2279,7 +2279,7 @@ static void gen_NOT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_OR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -2292,7 +2292,7 @@ static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -static void gen_OUT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_OUT(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; TCGv_i32 port = tcg_temp_new_i32(); @@ -2309,7 +2309,7 @@ static void gen_OUT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_bpt_io(s, port, ot); } -static void gen_OUTS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_OUTS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; TCGv_i32 port = tcg_temp_new_i32(); @@ -2328,7 +2328,7 @@ static void gen_OUTS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PALIGNR(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); if (!(s->prefix & PREFIX_DATA)) { @@ -2340,7 +2340,7 @@ static void gen_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PANDN(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2350,7 +2350,7 @@ static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) decode->op[1].offset, vec_len, vec_len); } -static void gen_PAUSE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PAUSE(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_update_eip_next(s); @@ -2358,14 +2358,14 @@ static void gen_PAUSE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) s->base.is_jmp = DISAS_NORETURN; } -static void gen_PCMPESTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PCMPESTRI(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpestri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_PCMPESTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PCMPESTRM(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpestrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); @@ -2376,14 +2376,14 @@ static void gen_PCMPESTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_PCMPISTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PCMPISTRI(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpistri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_PCMPISTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PCMPISTRM(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pcmpistrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); @@ -2394,17 +2394,17 @@ static void gen_PCMPISTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_PDEP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PDEP(DisasContext *s, X86DecodedInsn *decode) { gen_helper_pdep(s->T0, s->T0, s->T1); } -static void gen_PEXT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXT(DisasContext *s, X86DecodedInsn *decode) { gen_helper_pext(s->T0, s->T0, s->T1); } -static inline void gen_pextr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +static inline void gen_pextr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) { int vec_len = vector_len(s, decode); int mask = (vec_len >> ot) - 1; @@ -2430,23 +2430,23 @@ static inline void gen_pextr(DisasContext *s, CPUX86State *env, X86DecodedInsn * } } -static void gen_PEXTRB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXTRB(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_8); + gen_pextr(s, decode, MO_8); } -static void gen_PEXTRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXTRW(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_16); + gen_pextr(s, decode, MO_16); } -static void gen_PEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXTR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; - gen_pextr(s, env, decode, ot); + gen_pextr(s, decode, ot); } -static inline void gen_pinsr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +static inline void gen_pinsr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) { int vec_len = vector_len(s, decode); int mask = (vec_len >> ot) - 1; @@ -2477,19 +2477,19 @@ static inline void gen_pinsr(DisasContext *s, CPUX86State *env, X86DecodedInsn * } } -static void gen_PINSRB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PINSRB(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, MO_8); + gen_pinsr(s, decode, MO_8); } -static void gen_PINSRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PINSRW(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, MO_16); + gen_pinsr(s, decode, MO_16); } -static void gen_PINSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PINSR(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, decode->op[2].ot); + gen_pinsr(s, decode, decode->op[2].ot); } static void gen_pmovmskb_i64(TCGv_i64 d, TCGv_i64 s) @@ -2529,7 +2529,7 @@ static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s) tcg_gen_or_vec(vece, d, d, t); } -static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PMOVMSKB(DisasContext *s, X86DecodedInsn *decode) { static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; static const GVecGen2 g = { @@ -2573,7 +2573,7 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } -static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_POP(DisasContext *s, X86DecodedInsn *decode) { X86DecodedOp *op = &decode->op[0]; MemOp ot = gen_pop_T0(s); @@ -2587,12 +2587,12 @@ static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_pop_update(s, ot); } -static void gen_POPA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_POPA(DisasContext *s, X86DecodedInsn *decode) { gen_popa(s); } -static void gen_POPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_POPF(DisasContext *s, X86DecodedInsn *decode) { MemOp ot; int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK; @@ -2614,13 +2614,13 @@ static void gen_POPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) s->base.is_jmp = DISAS_EOB_NEXT; } -static void gen_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSHUFW(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); gen_helper_pshufw_mmx(OP_PTR0, OP_PTR1, imm); } -static void gen_PSRLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRLW_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2633,7 +2633,7 @@ static void gen_PSRLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSLLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLW_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2646,7 +2646,7 @@ static void gen_PSLLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSRAW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRAW_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2658,7 +2658,7 @@ static void gen_PSRAW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod decode->immediate, vec_len, vec_len); } -static void gen_PSRLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRLD_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2671,7 +2671,7 @@ static void gen_PSRLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSLLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLD_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2684,7 +2684,7 @@ static void gen_PSLLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSRAD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRAD_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2696,7 +2696,7 @@ static void gen_PSRAD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod decode->immediate, vec_len, vec_len); } -static void gen_PSRLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRLQ_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2709,7 +2709,7 @@ static void gen_PSRLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSLLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLQ_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2736,7 +2736,7 @@ static TCGv_ptr make_imm8u_xmm_vec(uint8_t imm, int vec_len) return ptr; } -static void gen_PSRLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRLDQ_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); @@ -2748,7 +2748,7 @@ static void gen_PSRLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } -static void gen_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLDQ_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); @@ -2760,17 +2760,17 @@ static void gen_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } -static void gen_PUSH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PUSH(DisasContext *s, X86DecodedInsn *decode) { gen_push_v(s, s->T1); } -static void gen_PUSHA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PUSHA(DisasContext *s, X86DecodedInsn *decode) { gen_pusha(s); } -static void gen_PUSHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PUSHF(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); gen_helper_read_eflags(s->T0, tcg_env); @@ -2967,7 +2967,7 @@ static void gen_rotc_mod(MemOp ot, TCGv count) * length - count, because (length-1) - (count-1) can be computed with * a XOR, and that is commutative unlike subtraction. */ -static void gen_RCL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_RCL(DisasContext *s, X86DecodedInsn *decode) { bool have_1bit_cin, can_be_zero; TCGv count; @@ -3019,7 +3019,7 @@ static void gen_RCL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_RCR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) { bool have_1bit_cin, can_be_zero; TCGv count; @@ -3072,7 +3072,7 @@ static void gen_RCR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_RET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_RET(DisasContext *s, X86DecodedInsn *decode) { int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; @@ -3083,7 +3083,7 @@ static void gen_RET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) s->base.is_jmp = DISAS_JUMP; } -static void gen_RETF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_RETF(DisasContext *s, X86DecodedInsn *decode) { int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; @@ -3154,7 +3154,7 @@ static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, } } -static void gen_ROL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ROL(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; @@ -3182,7 +3182,7 @@ static void gen_ROL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_rot_overflow(decode, s->T0, old, can_be_zero, count); } -static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ROR(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; @@ -3211,7 +3211,7 @@ static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_rot_overflow(decode, s->T0, old, can_be_zero, count); } -static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_RORX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; int mask = ot == MO_64 ? 63 : 31; @@ -3235,7 +3235,7 @@ static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_SAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SAHF(DisasContext *s, X86DecodedInsn *decode) { if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { return gen_illegal_opcode(s); @@ -3247,7 +3247,7 @@ static void gen_SAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); } -static void gen_SALC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SALC(DisasContext *s, X86DecodedInsn *decode) { gen_compute_eflags_c(s, s->T0); tcg_gen_neg_tl(s->T0, s->T0); @@ -3283,7 +3283,7 @@ static void gen_shift_dynamic_flags(DisasContext *s, X86DecodedInsn *decode, TCG old_cc_op, tcg_constant_i32(cc_op)); } -static void gen_SAR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SAR(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; @@ -3305,7 +3305,7 @@ static void gen_SAR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SARX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; int mask; @@ -3315,7 +3315,7 @@ static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_sar_tl(s->T0, s->T0, s->T1); } -static void gen_SBB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SBB(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv c_in = tcg_temp_new(); @@ -3337,7 +3337,7 @@ static void gen_SBB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update3_cc(decode, s, CC_OP_SBBB + ot, c_in); } -static void gen_SCAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SCAS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -3347,27 +3347,27 @@ static void gen_SCAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_SETcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SETcc(DisasContext *s, X86DecodedInsn *decode) { gen_setcc1(s, decode->b & 0xf, s->T0); } -static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA1NEXTE(DisasContext *s, X86DecodedInsn *decode) { gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_SHA1MSG1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA1MSG1(DisasContext *s, X86DecodedInsn *decode) { gen_helper_sha1msg1(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_SHA1MSG2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA1MSG2(DisasContext *s, X86DecodedInsn *decode) { gen_helper_sha1msg2(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_SHA1RNDS4(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA1RNDS4(DisasContext *s, X86DecodedInsn *decode) { switch(decode->immediate & 3) { case 0: @@ -3385,17 +3385,17 @@ static void gen_SHA1RNDS4(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_SHA256MSG1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA256MSG1(DisasContext *s, X86DecodedInsn *decode) { gen_helper_sha256msg1(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_SHA256MSG2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA256MSG2(DisasContext *s, X86DecodedInsn *decode) { gen_helper_sha256msg2(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_SHA256RNDS2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA256RNDS2(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 wk0 = tcg_temp_new_i32(); TCGv_i32 wk1 = tcg_temp_new_i32(); @@ -3406,7 +3406,7 @@ static void gen_SHA256RNDS2(DisasContext *s, CPUX86State *env, X86DecodedInsn *d gen_helper_sha256rnds2(OP_PTR0, OP_PTR1, OP_PTR2, wk0, wk1); } -static void gen_SHL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHL(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; @@ -3428,7 +3428,7 @@ static void gen_SHL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHLX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; int mask; @@ -3438,7 +3438,7 @@ static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_shl_tl(s->T0, s->T0, s->T1); } -static void gen_SHR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHR(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; @@ -3460,7 +3460,7 @@ static void gen_SHR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHRX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; int mask; @@ -3470,37 +3470,37 @@ static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_shr_tl(s->T0, s->T0, s->T1); } -static void gen_STC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STC(DisasContext *s, X86DecodedInsn *decode) { gen_compute_eflags(s); tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); } -static void gen_STD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STD(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_st_i32(tcg_constant_i32(-1), tcg_env, offsetof(CPUX86State, df)); } -static void gen_STI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STI(DisasContext *s, X86DecodedInsn *decode) { gen_set_eflags(s, IF_MASK); s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } -static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VAESKEYGEN(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(!s->vex_l); gen_helper_aeskeygenassist_xmm(tcg_env, OP_PTR0, OP_PTR1, imm); } -static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STMXCSR(DisasContext *s, X86DecodedInsn *decode) { gen_helper_update_mxcsr(tcg_env); tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); } -static void gen_STOS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STOS(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { @@ -3510,7 +3510,7 @@ static void gen_STOS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SUB(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; @@ -3526,12 +3526,12 @@ static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_SUBB + ot); } -static void gen_UD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_UD(DisasContext *s, X86DecodedInsn *decode) { gen_illegal_opcode(s); } -static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VAESIMC(DisasContext *s, X86DecodedInsn *decode) { assert(!s->vex_l); gen_helper_aesimc_xmm(tcg_env, OP_PTR0, OP_PTR2); @@ -3586,7 +3586,7 @@ static const SSEFunc_0_eppp gen_helper_cmp_funcs[32][6] = { }; #undef SSE_CMP -static void gen_VCMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCMP(DisasContext *s, X86DecodedInsn *decode) { int index = decode->immediate & (s->prefix & PREFIX_VEX ? 31 : 7); int b = @@ -3597,7 +3597,7 @@ static void gen_VCMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_helper_cmp_funcs[index][b](tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCOMI(DisasContext *s, X86DecodedInsn *decode) { SSEFunc_0_epp fn; fn = s->prefix & PREFIX_DATA ? gen_helper_comisd : gen_helper_comiss; @@ -3605,7 +3605,7 @@ static void gen_VCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_VCVTPD2PS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPD2PS(DisasContext *s, X86DecodedInsn *decode) { if (s->vex_l) { gen_helper_cvtpd2ps_ymm(tcg_env, OP_PTR0, OP_PTR2); @@ -3614,7 +3614,7 @@ static void gen_VCVTPD2PS(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_VCVTPS2PD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPS2PD(DisasContext *s, X86DecodedInsn *decode) { if (s->vex_l) { gen_helper_cvtps2pd_ymm(tcg_env, OP_PTR0, OP_PTR2); @@ -3623,9 +3623,9 @@ static void gen_VCVTPS2PD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPS2PH(DisasContext *s, X86DecodedInsn *decode) { - gen_unary_imm_fp_sse(s, env, decode, + gen_unary_imm_fp_sse(s, decode, gen_helper_cvtps2ph_xmm, gen_helper_cvtps2ph_ymm); /* @@ -3637,17 +3637,17 @@ static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_VCVTSD2SS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSD2SS(DisasContext *s, X86DecodedInsn *decode) { gen_helper_cvtsd2ss(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VCVTSS2SD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSS2SD(DisasContext *s, X86DecodedInsn *decode) { gen_helper_cvtss2sd(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSI2Sx(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); TCGv_i32 in; @@ -3677,7 +3677,7 @@ static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_VCVTtSx2SI(DisasContext *s, X86DecodedInsn *decode, SSEFunc_i_ep ss2si, SSEFunc_l_ep ss2sq, SSEFunc_i_ep sd2si, SSEFunc_l_ep sd2sq) { @@ -3715,21 +3715,21 @@ static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedI #define gen_helper_cvttsd2sq NULL #endif -static void gen_VCVTSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSx2SI(DisasContext *s, X86DecodedInsn *decode) { - gen_VCVTtSx2SI(s, env, decode, + gen_VCVTtSx2SI(s, decode, gen_helper_cvtss2si, gen_helper_cvtss2sq, gen_helper_cvtsd2si, gen_helper_cvtsd2sq); } -static void gen_VCVTTSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTTSx2SI(DisasContext *s, X86DecodedInsn *decode) { - gen_VCVTtSx2SI(s, env, decode, + gen_VCVTtSx2SI(s, decode, gen_helper_cvttss2si, gen_helper_cvttss2sq, gen_helper_cvttsd2si, gen_helper_cvttsd2sq); } -static void gen_VEXTRACTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VEXTRACTx128(DisasContext *s, X86DecodedInsn *decode) { int mask = decode->immediate & 1; int src_ofs = vector_elem_offset(&decode->op[1], MO_128, mask); @@ -3741,12 +3741,12 @@ static void gen_VEXTRACTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn * } } -static void gen_VEXTRACTPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VEXTRACTPS(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_32); + gen_pextr(s, decode, MO_32); } -static void gen_vinsertps(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_vinsertps(DisasContext *s, X86DecodedInsn *decode) { int val = decode->immediate; int dest_word = (val >> 4) & 3; @@ -3779,21 +3779,21 @@ static void gen_vinsertps(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_VINSERTPS_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VINSERTPS_r(DisasContext *s, X86DecodedInsn *decode) { int val = decode->immediate; tcg_gen_ld_i32(s->tmp2_i32, tcg_env, vector_elem_offset(&decode->op[2], MO_32, (val >> 6) & 3)); - gen_vinsertps(s, env, decode); + gen_vinsertps(s, decode); } -static void gen_VINSERTPS_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VINSERTPS_m(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_vinsertps(s, env, decode); + gen_vinsertps(s, decode); } -static void gen_VINSERTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VINSERTx128(DisasContext *s, X86DecodedInsn *decode) { int mask = decode->immediate & 1; tcg_gen_gvec_mov(MO_64, @@ -3804,7 +3804,7 @@ static void gen_VINSERTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *d decode->op[1].offset + offsetof(YMMReg, YMM_X(!mask)), 16, 16); } -static inline void gen_maskmov(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_maskmov(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppt xmm, SSEFunc_0_eppt ymm) { if (!s->vex_l) { @@ -3814,17 +3814,17 @@ static inline void gen_maskmov(DisasContext *s, CPUX86State *env, X86DecodedInsn } } -static void gen_VMASKMOVPD_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMASKMOVPD_st(DisasContext *s, X86DecodedInsn *decode) { - gen_maskmov(s, env, decode, gen_helper_vpmaskmovq_st_xmm, gen_helper_vpmaskmovq_st_ymm); + gen_maskmov(s, decode, gen_helper_vpmaskmovq_st_xmm, gen_helper_vpmaskmovq_st_ymm); } -static void gen_VMASKMOVPS_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMASKMOVPS_st(DisasContext *s, X86DecodedInsn *decode) { - gen_maskmov(s, env, decode, gen_helper_vpmaskmovd_st_xmm, gen_helper_vpmaskmovd_st_ymm); + gen_maskmov(s, decode, gen_helper_vpmaskmovd_st_xmm, gen_helper_vpmaskmovd_st_ymm); } -static void gen_VMOVHPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVHPx_ld(DisasContext *s, X86DecodedInsn *decode) { gen_ldq_env_A0(s, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); if (decode->op[0].offset != decode->op[1].offset) { @@ -3833,12 +3833,12 @@ static void gen_VMOVHPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *de } } -static void gen_VMOVHPx_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVHPx_st(DisasContext *s, X86DecodedInsn *decode) { gen_stq_env_A0(s, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); } -static void gen_VMOVHPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVHPx(DisasContext *s, X86DecodedInsn *decode) { if (decode->op[0].offset != decode->op[2].offset) { tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); @@ -3850,7 +3850,7 @@ static void gen_VMOVHPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_VMOVHLPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVHLPS(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); @@ -3860,7 +3860,7 @@ static void gen_VMOVHLPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco } } -static void gen_VMOVLHPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVLHPS(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset); tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); @@ -3875,7 +3875,7 @@ static void gen_VMOVLHPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco * Use a gvec move to move everything above the bottom 64 bits. */ -static void gen_VMOVLPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVLPx(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -3884,7 +3884,7 @@ static void gen_VMOVLPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); } -static void gen_VMOVLPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVLPx_ld(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -3893,13 +3893,13 @@ static void gen_VMOVLPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *de tcg_gen_st_i64(s->tmp1_i64, OP_PTR0, offsetof(ZMMReg, ZMM_Q(0))); } -static void gen_VMOVLPx_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVLPx_st(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_ld_i64(s->tmp1_i64, OP_PTR2, offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); } -static void gen_VMOVSD_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVSD_ld(DisasContext *s, X86DecodedInsn *decode) { TCGv_i64 zero = tcg_constant_i64(0); @@ -3908,7 +3908,7 @@ static void gen_VMOVSD_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec tcg_gen_st_i64(s->tmp1_i64, OP_PTR0, offsetof(ZMMReg, ZMM_Q(0))); } -static void gen_VMOVSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVSS(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -3917,7 +3917,7 @@ static void gen_VMOVSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); } -static void gen_VMOVSS_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVSS_ld(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -3926,55 +3926,55 @@ static void gen_VMOVSS_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); } -static void gen_VMOVSS_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVSS_st(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_ld_i32(s->tmp2_i32, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); } -static void gen_VPMASKMOV_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VPMASKMOV_st(DisasContext *s, X86DecodedInsn *decode) { if (s->vex_w) { - gen_VMASKMOVPD_st(s, env, decode); + gen_VMASKMOVPD_st(s, decode); } else { - gen_VMASKMOVPS_st(s, env, decode); + gen_VMASKMOVPS_st(s, decode); } } -static void gen_VPERMD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VPERMD(DisasContext *s, X86DecodedInsn *decode) { assert(s->vex_l); gen_helper_vpermd_ymm(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VPERM2x128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VPERM2x128(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(s->vex_l); gen_helper_vpermdq_ymm(OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VPHMINPOSUW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VPHMINPOSUW(DisasContext *s, X86DecodedInsn *decode) { assert(!s->vex_l); gen_helper_phminposuw_xmm(tcg_env, OP_PTR0, OP_PTR2); } -static void gen_VROUNDSD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VROUNDSD(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(!s->vex_l); gen_helper_roundsd_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VROUNDSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VROUNDSS(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(!s->vex_l); gen_helper_roundss_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VSHUF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VSHUF(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant_i32(decode->immediate); SSEFunc_0_pppi ps, pd, fn; @@ -3984,7 +3984,7 @@ static void gen_VSHUF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) fn(OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VUCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VUCOMI(DisasContext *s, X86DecodedInsn *decode) { SSEFunc_0_epp fn; fn = s->prefix & PREFIX_DATA ? gen_helper_ucomisd : gen_helper_ucomiss; @@ -3992,7 +3992,7 @@ static void gen_VUCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_VZEROALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VZEROALL(DisasContext *s, X86DecodedInsn *decode) { TCGv_ptr ptr = tcg_temp_new_ptr(); @@ -4001,7 +4001,7 @@ static void gen_VZEROALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco tcg_constant_ptr(CPU_NB_REGS * sizeof(ZMMReg))); } -static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VZEROUPPER(DisasContext *s, X86DecodedInsn *decode) { int i; @@ -4011,7 +4011,7 @@ static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *de } } -static void gen_WAIT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_WAIT(DisasContext *s, X86DecodedInsn *decode) { if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == (HF_MP_MASK | HF_TS_MASK)) { gen_NM_exception(s); @@ -4022,7 +4022,7 @@ static void gen_WAIT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_XCHG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_XCHG(DisasContext *s, X86DecodedInsn *decode) { if (s->prefix & PREFIX_LOCK) { tcg_gen_atomic_xchg_tl(s->T0, s->A0, s->T1, @@ -4036,7 +4036,7 @@ static void gen_XCHG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_XLAT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_XLAT(DisasContext *s, X86DecodedInsn *decode) { /* AL is already zero-extended into s->T0. */ tcg_gen_add_tl(s->A0, cpu_regs[R_EBX], s->T0); @@ -4044,7 +4044,7 @@ static void gen_XLAT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_op_ld_v(s, MO_8, s->T0, s->A0); } -static void gen_XOR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_XOR(DisasContext *s, X86DecodedInsn *decode) { /* special case XOR reg, reg */ if (decode->op[1].unit == X86_OP_INT && From cc155f19717ced44d70df3cd5f149a5b9f9a13f1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 May 2024 09:39:08 +0200 Subject: [PATCH 1347/2884] target/i386: rewrite flags writeback for ADCX/ADOX Avoid using set_cc_op() in preparation for implementing APX; treat CC_OP_EFLAGS similar to the case where we have the "opposite" cc_op (CC_OP_ADOX for ADCX and CC_OP_ADCX for ADOX), except the resulting cc_op is not CC_OP_ADCOX. This is written easily as two "if"s, whose conditions are both false for CC_OP_EFLAGS, both true for CC_OP_ADCOX, and one each true for CC_OP_ADCX/ADOX. The new logic also makes it easy to drop usage of tmp0. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 9 +++--- target/i386/tcg/emit.c.inc | 61 ++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8fe28b67e0..7e2a9b56ae 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1260,6 +1260,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, /* Use a clearer name for this. */ #define CPU_INTERRUPT_INIT CPU_INTERRUPT_RESET +#define CC_OP_HAS_EFLAGS(op) ((op) >= CC_OP_EFLAGS && (op) <= CC_OP_ADCOX) + /* Instead of computing the condition codes after each x86 instruction, * QEMU just stores one operand (called CC_SRC), the result * (called CC_DST) and the type of operation (called CC_OP). When the @@ -1270,6 +1272,9 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, typedef enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_EFLAGS, /* all cc are explicitly computed, CC_SRC = flags */ + CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest. */ + CC_OP_ADOX, /* CC_SRC2 = O, CC_SRC = rest. */ + CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest. */ CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */ CC_OP_MULW, @@ -1326,10 +1331,6 @@ typedef enum { CC_OP_BMILGL, CC_OP_BMILGQ, - CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest. */ - CC_OP_ADOX, /* CC_DST = O, CC_SRC = rest. */ - CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest. */ - CC_OP_CLR, /* Z set, all other flags clear. */ CC_OP_POPCNT, /* Z via CC_SRC, all other flags clear. */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index df7597c7e2..2041ea9d04 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1122,24 +1122,41 @@ static void gen_ADC(DisasContext *s, X86DecodedInsn *decode) prepare_update3_cc(decode, s, CC_OP_ADCB + ot, c_in); } -/* ADCX/ADOX do not have memory operands and can use set_cc_op. */ -static void gen_ADCOX(DisasContext *s, MemOp ot, int cc_op) +static void gen_ADCOX(DisasContext *s, X86DecodedInsn *decode, int cc_op) { - int opposite_cc_op; + MemOp ot = decode->op[0].ot; TCGv carry_in = NULL; - TCGv carry_out = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2); + TCGv *carry_out = (cc_op == CC_OP_ADCX ? &decode->cc_dst : &decode->cc_src2); TCGv zero; - if (cc_op == s->cc_op || s->cc_op == CC_OP_ADCOX) { - /* Re-use the carry-out from a previous round. */ - carry_in = carry_out; - } else { - /* We don't have a carry-in, get it out of EFLAGS. */ - if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { - gen_compute_eflags(s); + decode->cc_op = cc_op; + *carry_out = tcg_temp_new(); + if (CC_OP_HAS_EFLAGS(s->cc_op)) { + decode->cc_src = cpu_cc_src; + + /* Re-use the carry-out from a previous round? */ + if (s->cc_op == cc_op || s->cc_op == CC_OP_ADCOX) { + carry_in = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2); } - carry_in = s->tmp0; - tcg_gen_extract_tl(carry_in, cpu_cc_src, + + /* Preserve the opposite carry from previous rounds? */ + if (s->cc_op != cc_op && s->cc_op != CC_OP_EFLAGS) { + decode->cc_op = CC_OP_ADCOX; + if (carry_out == &decode->cc_dst) { + decode->cc_src2 = cpu_cc_src2; + } else { + decode->cc_dst = cpu_cc_dst; + } + } + } else { + decode->cc_src = tcg_temp_new(); + gen_mov_eflags(s, decode->cc_src); + } + + if (!carry_in) { + /* Get carry_in out of EFLAGS. */ + carry_in = tcg_temp_new(); + tcg_gen_extract_tl(carry_in, decode->cc_src, ctz32(cc_op == CC_OP_ADCX ? CC_C : CC_O), 1); } @@ -1151,28 +1168,20 @@ static void gen_ADCOX(DisasContext *s, MemOp ot, int cc_op) tcg_gen_ext32u_tl(s->T1, s->T1); tcg_gen_add_i64(s->T0, s->T0, s->T1); tcg_gen_add_i64(s->T0, s->T0, carry_in); - tcg_gen_shri_i64(carry_out, s->T0, 32); + tcg_gen_shri_i64(*carry_out, s->T0, 32); break; #endif default: zero = tcg_constant_tl(0); - tcg_gen_add2_tl(s->T0, carry_out, s->T0, zero, carry_in, zero); - tcg_gen_add2_tl(s->T0, carry_out, s->T0, carry_out, s->T1, zero); + tcg_gen_add2_tl(s->T0, *carry_out, s->T0, zero, carry_in, zero); + tcg_gen_add2_tl(s->T0, *carry_out, s->T0, *carry_out, s->T1, zero); break; } - - opposite_cc_op = cc_op == CC_OP_ADCX ? CC_OP_ADOX : CC_OP_ADCX; - if (s->cc_op == CC_OP_ADCOX || s->cc_op == opposite_cc_op) { - /* Merge with the carry-out from the opposite instruction. */ - set_cc_op(s, CC_OP_ADCOX); - } else { - set_cc_op(s, cc_op); - } } static void gen_ADCX(DisasContext *s, X86DecodedInsn *decode) { - gen_ADCOX(s, decode->op[0].ot, CC_OP_ADCX); + gen_ADCOX(s, decode, CC_OP_ADCX); } static void gen_ADD(DisasContext *s, X86DecodedInsn *decode) @@ -1190,7 +1199,7 @@ static void gen_ADD(DisasContext *s, X86DecodedInsn *decode) static void gen_ADOX(DisasContext *s, X86DecodedInsn *decode) { - gen_ADCOX(s, decode->op[0].ot, CC_OP_ADOX); + gen_ADCOX(s, decode, CC_OP_ADOX); } static void gen_AND(DisasContext *s, X86DecodedInsn *decode) From e628387cf9a27a4895b00821313635fad4cfab43 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 May 2024 09:33:22 +0200 Subject: [PATCH 1348/2884] target/i386: put BLS* input in T1, use generic flag writeback This makes for easier cpu_cc_* setup, and not using set_cc_op() should come in handy if QEMU ever implements APX. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 ++-- target/i386/tcg/emit.c.inc | 24 +++++++++--------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index e7d8802048..380fb79353 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -633,7 +633,7 @@ static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { {}, }, [3] = { - X86_OP_GROUP3(group17, B,y, E,y, None,None, vex13 cpuid(BMI1)), + X86_OP_GROUP3(group17, B,y, None,None, E,y, vex13 cpuid(BMI1)), {}, {}, {}, @@ -2604,7 +2604,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) } /* - * Write back flags after last memory access. Some newer ALU instructions, as + * Write back flags after last memory access. Some older ALU instructions, as * well as SSE instructions, write flags in the gen_* function, but that can * cause incorrect tracking of CC_OP for instructions that write to both memory * and flags. diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2041ea9d04..a25b3dfc6b 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1272,40 +1272,34 @@ static void gen_BEXTR(DisasContext *s, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -/* BLSI do not have memory operands and can use set_cc_op. */ static void gen_BLSI(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; - tcg_gen_mov_tl(cpu_cc_src, s->T0); - tcg_gen_neg_tl(s->T1, s->T0); + /* input in T1, which is ready for prepare_update2_cc */ + tcg_gen_neg_tl(s->T0, s->T1); tcg_gen_and_tl(s->T0, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, CC_OP_BMILGB + ot); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -/* BLSMSK do not have memory operands and can use set_cc_op. */ static void gen_BLSMSK(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; - tcg_gen_mov_tl(cpu_cc_src, s->T0); - tcg_gen_subi_tl(s->T1, s->T0, 1); + /* input in T1, which is ready for prepare_update2_cc */ + tcg_gen_subi_tl(s->T0, s->T1, 1); tcg_gen_xor_tl(s->T0, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, CC_OP_BMILGB + ot); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -/* BLSR do not have memory operands and can use set_cc_op. */ static void gen_BLSR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; - tcg_gen_mov_tl(cpu_cc_src, s->T0); - tcg_gen_subi_tl(s->T1, s->T0, 1); + /* input in T1, which is ready for prepare_update2_cc */ + tcg_gen_subi_tl(s->T0, s->T1, 1); tcg_gen_and_tl(s->T0, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, CC_OP_BMILGB + ot); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } static void gen_BOUND(DisasContext *s, X86DecodedInsn *decode) From c2b6b6a65a227d2bb45e1b2694cf064b881543e4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 11:06:50 +0200 Subject: [PATCH 1349/2884] target/i386: change X86_ENTRYr to use T0 I am not sure why I made it use T1. It is a bit more symmetric with respect to X86_ENTRYwr (which uses T0 for the "w"ritten operand and T1 for the "r"ead operand), but it is also less flexible because it does not let you apply zextT0/sextT0. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 6 +++--- target/i386/tcg/emit.c.inc | 34 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 380fb79353..f9d3e2577b 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -186,7 +186,7 @@ #define X86_OP_ENTRYw(op, op0, s0, ...) \ X86_OP_ENTRY3(op, op0, s0, None, None, None, None, ## __VA_ARGS__) #define X86_OP_ENTRYr(op, op0, s0, ...) \ - X86_OP_ENTRY3(op, None, None, None, None, op0, s0, ## __VA_ARGS__) + X86_OP_ENTRY3(op, None, None, op0, s0, None, None, ## __VA_ARGS__) #define X86_OP_ENTRY1(op, op0, s0, ...) \ X86_OP_ENTRY3(op, op0, s0, 2op, s0, None, None, ## __VA_ARGS__) #define X86_OP_ENTRY0(op, ...) \ @@ -1335,9 +1335,9 @@ static void decode_group4_5(DisasContext *s, CPUX86State *env, X86OpEntry *entry /* 0xff */ [0x08] = X86_OP_ENTRY1(INC, E,v, lock), [0x09] = X86_OP_ENTRY1(DEC, E,v, lock), - [0x0a] = X86_OP_ENTRY3(CALL_m, None, None, E,f64, None, None, zextT0), + [0x0a] = X86_OP_ENTRYr(CALL_m, E,f64, zextT0), [0x0b] = X86_OP_ENTRYr(CALLF_m, M,p), - [0x0c] = X86_OP_ENTRY3(JMP_m, None, None, E,f64, None, None, zextT0), + [0x0c] = X86_OP_ENTRYr(JMP_m, E,f64, zextT0), [0x0d] = X86_OP_ENTRYr(JMPF_m, M,p), [0x0e] = X86_OP_ENTRYr(PUSH, E,f64), }; diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index a25b3dfc6b..797e6e8140 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1363,7 +1363,7 @@ static void gen_CALLF(DisasContext *s, X86DecodedInsn *decode) static void gen_CALLF_m(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; + MemOp ot = decode->op[1].ot; gen_op_ld_v(s, ot, s->T0, s->A0); gen_add_A0_im(s, 1 << ot); @@ -1593,22 +1593,22 @@ static void gen_DEC(DisasContext *s, X86DecodedInsn *decode) static void gen_DIV(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; + MemOp ot = decode->op[1].ot; switch(ot) { case MO_8: - gen_helper_divb_AL(tcg_env, s->T1); + gen_helper_divb_AL(tcg_env, s->T0); break; case MO_16: - gen_helper_divw_AX(tcg_env, s->T1); + gen_helper_divw_AX(tcg_env, s->T0); break; default: case MO_32: - gen_helper_divl_EAX(tcg_env, s->T1); + gen_helper_divl_EAX(tcg_env, s->T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_divq_EAX(tcg_env, s->T1); + gen_helper_divq_EAX(tcg_env, s->T0); break; #endif } @@ -1649,22 +1649,22 @@ static void gen_HLT(DisasContext *s, X86DecodedInsn *decode) static void gen_IDIV(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; + MemOp ot = decode->op[1].ot; switch(ot) { case MO_8: - gen_helper_idivb_AL(tcg_env, s->T1); + gen_helper_idivb_AL(tcg_env, s->T0); break; case MO_16: - gen_helper_idivw_AX(tcg_env, s->T1); + gen_helper_idivw_AX(tcg_env, s->T0); break; default: case MO_32: - gen_helper_idivl_EAX(tcg_env, s->T1); + gen_helper_idivl_EAX(tcg_env, s->T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_idivq_EAX(tcg_env, s->T1); + gen_helper_idivq_EAX(tcg_env, s->T0); break; #endif } @@ -1926,7 +1926,7 @@ static void gen_JMPF(DisasContext *s, X86DecodedInsn *decode) static void gen_JMPF_m(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; + MemOp ot = decode->op[1].ot; gen_op_ld_v(s, ot, s->T0, s->A0); gen_add_A0_im(s, 1 << ot); @@ -1947,7 +1947,7 @@ static void gen_LAHF(DisasContext *s, X86DecodedInsn *decode) static void gen_LDMXCSR(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); } @@ -1995,7 +1995,7 @@ static void gen_LGS(DisasContext *s, X86DecodedInsn *decode) static void gen_LODS(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; + MemOp ot = decode->op[1].ot; if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { gen_repz(s, ot, gen_lods); } else { @@ -2765,7 +2765,7 @@ static void gen_PSLLDQ_i(DisasContext *s, X86DecodedInsn *decode) static void gen_PUSH(DisasContext *s, X86DecodedInsn *decode) { - gen_push_v(s, s->T1); + gen_push_v(s, s->T0); } static void gen_PUSHA(DisasContext *s, X86DecodedInsn *decode) @@ -3077,7 +3077,7 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) static void gen_RET(DisasContext *s, X86DecodedInsn *decode) { - int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; + int16_t adjust = decode->e.op1 == X86_TYPE_I ? decode->immediate : 0; MemOp ot = gen_pop_T0(s); gen_stack_update(s, adjust + (1 << ot)); @@ -3088,7 +3088,7 @@ static void gen_RET(DisasContext *s, X86DecodedInsn *decode) static void gen_RETF(DisasContext *s, X86DecodedInsn *decode) { - int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0; + int16_t adjust = decode->e.op1 == X86_TYPE_I ? decode->immediate : 0; if (!PE(s) || VM86(s)) { gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], 0); From 4e2dc59cf99b5d352b426ee30b8fbb9804e237d1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 11:06:50 +0200 Subject: [PATCH 1350/2884] target/i386: change X86_ENTRYwr to use T0, use it for moves Just like X86_ENTRYr, X86_ENTRYwr is easily changed to use only T0. In this case, the motivation is to use it for the MOV instruction family. The case when you need to preserve the input value is the odd one, as it is used basically only for BLS* instructions. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 48 ++++++++++++++++---------------- target/i386/tcg/emit.c.inc | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index f9d3e2577b..d41002e2f5 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -180,7 +180,7 @@ #define X86_OP_ENTRYrr(op, op0, s0, op1, s1, ...) \ X86_OP_ENTRY3(op, None, None, op0, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_ENTRYwr(op, op0, s0, op1, s1, ...) \ - X86_OP_ENTRY3(op, op0, s0, None, None, op1, s1, ## __VA_ARGS__) + X86_OP_ENTRY3(op, op0, s0, op1, s1, None, None, ## __VA_ARGS__) #define X86_OP_ENTRY2(op, op0, s0, op1, s1, ...) \ X86_OP_ENTRY3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_ENTRYw(op, op0, s0, ...) \ @@ -612,15 +612,15 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { /* five rows for no prefix, 66, F3, F2, 66+F2 */ static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { [0] = { - X86_OP_ENTRY3(MOVBE, G,y, M,y, None,None, cpuid(MOVBE)), - X86_OP_ENTRY3(MOVBE, G,w, M,w, None,None, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, G,y, M,y, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, G,w, M,w, cpuid(MOVBE)), {}, X86_OP_ENTRY2(CRC32, G,d, E,b, cpuid(SSE42)), X86_OP_ENTRY2(CRC32, G,d, E,b, cpuid(SSE42)), }, [1] = { - X86_OP_ENTRY3(MOVBE, M,y, G,y, None,None, cpuid(MOVBE)), - X86_OP_ENTRY3(MOVBE, M,w, G,w, None,None, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, M,y, G,y, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, M,w, G,w, cpuid(MOVBE)), {}, X86_OP_ENTRY2(CRC32, G,d, E,y, cpuid(SSE42)), X86_OP_ENTRY2(CRC32, G,d, E,w, cpuid(SSE42)), @@ -1586,18 +1586,18 @@ static const X86OpEntry opcodes_root[256] = { [0x7E] = X86_OP_ENTRYr(Jcc, J,b), [0x7F] = X86_OP_ENTRYr(Jcc, J,b), - [0x88] = X86_OP_ENTRY3(MOV, E,b, G,b, None, None), - [0x89] = X86_OP_ENTRY3(MOV, E,v, G,v, None, None), - [0x8A] = X86_OP_ENTRY3(MOV, G,b, E,b, None, None), - [0x8B] = X86_OP_ENTRY3(MOV, G,v, E,v, None, None), - /* Missing in Table A-2: memory destination is always 16-bit. */ - [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None, op0_Mw), - [0x8D] = X86_OP_ENTRY3(LEA, G,v, M,v, None, None, noseg), - [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,w, None, None), + [0x88] = X86_OP_ENTRYwr(MOV, E,b, G,b), + [0x89] = X86_OP_ENTRYwr(MOV, E,v, G,v), + [0x8A] = X86_OP_ENTRYwr(MOV, G,b, E,b), + [0x8B] = X86_OP_ENTRYwr(MOV, G,v, E,v), + /* Missing in Table A-2: memory destination is always 16-bit. */ + [0x8C] = X86_OP_ENTRYwr(MOV, E,v, S,w, op0_Mw), + [0x8D] = X86_OP_ENTRYwr(LEA, G,v, M,v, noseg), + [0x8E] = X86_OP_ENTRYwr(MOV, S,w, E,w), [0x8F] = X86_OP_GROUPw(group1A, E,v), [0x98] = X86_OP_ENTRY1(CBW, 0,v), /* rAX */ - [0x99] = X86_OP_ENTRY3(CWD, 2,v, 0,v, None, None), /* rDX, rAX */ + [0x99] = X86_OP_ENTRYwr(CWD, 2,v, 0,v), /* rDX, rAX */ [0x9A] = X86_OP_ENTRYrr(CALLF, I_unsigned,p, I_unsigned,w, chk(i64)), [0x9B] = X86_OP_ENTRY0(WAIT), [0x9C] = X86_OP_ENTRY0(PUSHF, chk(vm86_iopl) svm(PUSHF)), @@ -1607,22 +1607,22 @@ static const X86OpEntry opcodes_root[256] = { [0xA8] = X86_OP_ENTRYrr(AND, 0,b, I,b), /* AL, Ib */ [0xA9] = X86_OP_ENTRYrr(AND, 0,v, I,z), /* rAX, Iz */ - [0xAA] = X86_OP_ENTRY3(STOS, Y,b, 0,b, None, None), - [0xAB] = X86_OP_ENTRY3(STOS, Y,v, 0,v, None, None), + [0xAA] = X86_OP_ENTRYwr(STOS, Y,b, 0,b), + [0xAB] = X86_OP_ENTRYwr(STOS, Y,v, 0,v), /* Manual writeback because REP LODS (!) has to write EAX/RAX after every LODS. */ [0xAC] = X86_OP_ENTRYr(LODS, X,b), [0xAD] = X86_OP_ENTRYr(LODS, X,v), [0xAE] = X86_OP_ENTRYrr(SCAS, 0,b, Y,b), [0xAF] = X86_OP_ENTRYrr(SCAS, 0,v, Y,v), - [0xB8] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xB9] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xBA] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xBB] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xBC] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xBD] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xBE] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), - [0xBF] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None), + [0xB8] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xB9] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBA] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBB] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBC] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBD] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBE] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBF] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), [0xC8] = X86_OP_ENTRYrr(ENTER, I,w, I,b), [0xC9] = X86_OP_ENTRY1(LEAVE, A,d64), diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 797e6e8140..78d89db57c 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1796,7 +1796,7 @@ static void gen_IN(DisasContext *s, X86DecodedInsn *decode) MemOp ot = decode->op[0].ot; TCGv_i32 port = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_trunc_tl_i32(port, s->T0); tcg_gen_ext16u_i32(port, port); if (!gen_check_io(s, ot, port, SVM_IOIO_TYPE_MASK)) { return; From c0df9563a3d1b95b91d3ad7e2519fed0c2952772 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 16:56:26 +0200 Subject: [PATCH 1351/2884] target/i386: replace NoSeg special with NoLoadEA This is a bit more generic, as it can be applied to MPX as well. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 12 ++++-------- target/i386/tcg/decode-new.h | 5 +++-- target/i386/tcg/emit.c.inc | 3 ++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index d41002e2f5..4f5fcdb88d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -193,7 +193,7 @@ X86_OP_ENTRY3(op, None, None, None, None, None, None, ## __VA_ARGS__) #define cpuid(feat) .cpuid = X86_FEAT_##feat, -#define noseg .special = X86_SPECIAL_NoSeg, +#define nolea .special = X86_SPECIAL_NoLoadEA, #define xchg .special = X86_SPECIAL_Locked, #define lock .special = X86_SPECIAL_HasLock, #define mmx .special = X86_SPECIAL_MMX, @@ -1592,7 +1592,7 @@ static const X86OpEntry opcodes_root[256] = { [0x8B] = X86_OP_ENTRYwr(MOV, G,v, E,v), /* Missing in Table A-2: memory destination is always 16-bit. */ [0x8C] = X86_OP_ENTRYwr(MOV, E,v, S,w, op0_Mw), - [0x8D] = X86_OP_ENTRYwr(LEA, G,v, M,v, noseg), + [0x8D] = X86_OP_ENTRYwr(LEA, G,v, M,v, nolea), [0x8E] = X86_OP_ENTRYwr(MOV, S,w, E,w), [0x8F] = X86_OP_GROUPw(group1A, E,v), @@ -2524,11 +2524,6 @@ static void disas_insn(DisasContext *s, CPUState *cpu) assert(decode.op[1].unit == X86_OP_INT); break; - case X86_SPECIAL_NoSeg: - decode.mem.def_seg = -1; - s->override = -1; - break; - case X86_SPECIAL_Op0_Mw: assert(decode.op[0].unit == X86_OP_INT); if (decode.op[0].has_ea) { @@ -2585,7 +2580,8 @@ static void disas_insn(DisasContext *s, CPUState *cpu) gen_helper_enter_mmx(tcg_env); } - if (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea) { + if (decode.e.special != X86_SPECIAL_NoLoadEA && + (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea)) { gen_load_ea(s, &decode.mem, decode.e.vex_class == 12); } if (s->prefix & PREFIX_LOCK) { diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index f704698575..46a96b220d 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -170,8 +170,9 @@ typedef enum X86InsnSpecial { /* Always locked if it has a memory operand (XCHG) */ X86_SPECIAL_Locked, - /* Do not apply segment base to effective address */ - X86_SPECIAL_NoSeg, + /* Do not load effective address in s->A0 */ + X86_SPECIAL_NoLoadEA, + /* * Rd/Mb or Rd/Mw in the manual: register operand 0 is treated as 32 bits * (and writeback zero-extends it to 64 bits if applicable). PREFIX_DATA diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 78d89db57c..e6521632ed 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1970,7 +1970,8 @@ static void gen_LDS(DisasContext *s, X86DecodedInsn *decode) static void gen_LEA(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_mov_tl(s->T0, s->A0); + TCGv ea = gen_lea_modrm_1(s, decode->mem, false); + gen_lea_v_seg_dest(s, s->aflag, s->T0, ea, -1, -1); } static void gen_LEAVE(DisasContext *s, X86DecodedInsn *decode) From 024538287e4b4838a21cacec3709ed55093807b9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 14:34:24 +0200 Subject: [PATCH 1352/2884] target/i386: fix processing of intercept 0 (read CR0) Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 ++-- target/i386/tcg/decode-new.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 4f5fcdb88d..cd925fe358 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -221,7 +221,7 @@ #define vex13 .vex_class = 13, #define chk(a) .check = X86_CHECK_##a, -#define svm(a) .intercept = SVM_EXIT_##a, +#define svm(a) .intercept = SVM_EXIT_##a, .has_intercept = true, #define avx2_256 .vex_special = X86_VEX_AVX2_256, @@ -2559,7 +2559,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) goto gp_fault; } } - if (decode.e.intercept && unlikely(GUEST(s))) { + if (decode.e.has_intercept && unlikely(GUEST(s))) { gen_helper_svm_check_intercept(tcg_env, tcg_constant_i32(decode.e.intercept)); } diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 46a96b220d..8465717ea2 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -272,6 +272,7 @@ struct X86OpEntry { unsigned valid_prefix:16; unsigned check:16; unsigned intercept:8; + bool has_intercept:1; bool is_decode:1; }; From 23530e42d27cac186eadf54b323c15dd39da5541 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 4 Jun 2024 11:00:57 +0800 Subject: [PATCH 1353/2884] tests/avocado: Update LoongArch bios file The VM uses old bios to boot up only 1 cpu, causing the test case to fail. Update the bios to solve this problem. Reported-by: Thomas Huth Signed-off-by: Song Gao Message-ID: <20240604030058.2327145-1-gaosong@loongson.cn> Signed-off-by: Thomas Huth --- tests/avocado/machine_loongarch.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/avocado/machine_loongarch.py b/tests/avocado/machine_loongarch.py index 7d8a3c1fa5..8de308f2d6 100644 --- a/tests/avocado/machine_loongarch.py +++ b/tests/avocado/machine_loongarch.py @@ -27,18 +27,18 @@ class LoongArchMachine(QemuSystemTest): """ kernel_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/binary-files/vmlinuz.efi') + 'releases/download/2024-05-30/vmlinuz.efi') kernel_hash = '951b485b16e3788b6db03a3e1793c067009e31a2' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) initrd_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/binary-files/ramdisk') + 'releases/download/2024-05-30/ramdisk') initrd_hash = 'c67658d9b2a447ce7db2f73ba3d373c9b2b90ab2' initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) bios_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/binary-files/QEMU_EFI.fd') - bios_hash = ('dfc1bfba4853cd763b9d392d0031827e8addbca8') + 'releases/download/2024-05-30/QEMU_EFI.fd') + bios_hash = ('f4d0966b5117d4cd82327c050dd668741046be69') bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) self.vm.set_console() From 07c8d9ac0fa30712fdf78046a7998ee8d2231d6f Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 10 Jun 2024 21:22:58 +0530 Subject: [PATCH 1354/2884] qtest/x86/numa-test: do not use the obsolete 'pentium' cpu 'pentium' cpu is old and obsolete and should be avoided for running tests if its not strictly needed. Use 'max' cpu instead for generic non-cpu specific numa test. Reviewed-by: Thomas Huth Reviewed-by: Igor Mammedov Tested-by: Mario Casquero Signed-off-by: Ani Sinha Message-ID: <20240610155303.7933-2-anisinha@redhat.com> Signed-off-by: Thomas Huth --- tests/qtest/numa-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index 5518f6596b..ede418963c 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -125,7 +125,8 @@ static void pc_numa_cpu(const void *data) QTestState *qts; g_autofree char *cli = NULL; - cli = make_cli(data, "-cpu pentium -machine smp.cpus=8,smp.sockets=2,smp.cores=2,smp.threads=2 " + cli = make_cli(data, + "-cpu max -machine smp.cpus=8,smp.sockets=2,smp.cores=2,smp.threads=2 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " "-numa cpu,node-id=1,socket-id=0 " "-numa cpu,node-id=0,socket-id=1,core-id=0 " From f43f8abe457a4aa32441bd190638e1118d291c42 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 10 Jun 2024 21:22:59 +0530 Subject: [PATCH 1355/2884] tests/qtest/libqtest: add qtest_has_cpu_model() api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a new test api qtest_has_cpu_model() in order to check availability of some cpu models in the current QEMU binary. The specific architecture of the QEMU binary is selected using the QTEST_QEMU_BINARY environment variable. This api would be useful to run tests against some older cpu models after checking if QEMU actually supported these models. Signed-off-by: Ani Sinha Reviewed-by: Reviewed-by: Daniel P. Berrangé Message-ID: <20240610155303.7933-3-anisinha@redhat.com> Signed-off-by: Thomas Huth --- tests/qtest/libqtest.c | 83 ++++++++++++++++++++++++++++++++++++++++++ tests/qtest/libqtest.h | 8 ++++ 2 files changed, 91 insertions(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index d8f80d335e..18e2f7f282 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -37,6 +37,7 @@ #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" +#include "qapi/qmp/qbool.h" #define MAX_IRQ 256 @@ -1471,6 +1472,12 @@ struct MachInfo { char *alias; }; +struct CpuModel { + char *name; + char *alias_of; + bool deprecated; +}; + static void qtest_free_machine_list(struct MachInfo *machines) { if (machines) { @@ -1550,6 +1557,82 @@ static struct MachInfo *qtest_get_machines(const char *var) return machines; } +static struct CpuModel *qtest_get_cpu_models(void) +{ + static struct CpuModel *cpus; + QDict *response, *minfo; + QList *list; + const QListEntry *p; + QObject *qobj; + QString *qstr; + QBool *qbool; + QTestState *qts; + int idx; + + if (cpus) { + return cpus; + } + + silence_spawn_log = !g_test_verbose(); + + qts = qtest_init_with_env(NULL, "-machine none"); + response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }"); + g_assert(response); + list = qdict_get_qlist(response, "return"); + g_assert(list); + + cpus = g_new0(struct CpuModel, qlist_size(list) + 1); + + for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + g_assert(minfo); + + qobj = qdict_get(minfo, "name"); + g_assert(qobj); + qstr = qobject_to(QString, qobj); + g_assert(qstr); + cpus[idx].name = g_strdup(qstring_get_str(qstr)); + + qobj = qdict_get(minfo, "alias_of"); + if (qobj) { /* old machines do not report aliases */ + qstr = qobject_to(QString, qobj); + g_assert(qstr); + cpus[idx].alias_of = g_strdup(qstring_get_str(qstr)); + } else { + cpus[idx].alias_of = NULL; + } + + qobj = qdict_get(minfo, "deprecated"); + qbool = qobject_to(QBool, qobj); + g_assert(qbool); + cpus[idx].deprecated = qbool_get_bool(qbool); + } + + qtest_quit(qts); + qobject_unref(response); + + silence_spawn_log = false; + + return cpus; +} + +bool qtest_has_cpu_model(const char *cpu) +{ + struct CpuModel *cpus; + int i; + + cpus = qtest_get_cpu_models(); + + for (i = 0; cpus[i].name != NULL; i++) { + if (g_str_equal(cpu, cpus[i].name) || + (cpus[i].alias_of && g_str_equal(cpu, cpus[i].alias_of))) { + return true; + } + } + + return false; +} + void qtest_cb_for_every_machine(void (*cb)(const char *machine), bool skip_old_versioned) { diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 6e3d3525bf..beb96b18eb 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -949,6 +949,14 @@ bool qtest_has_machine(const char *machine); */ bool qtest_has_machine_with_env(const char *var, const char *machine); +/** + * qtest_has_cpu_model: + * @cpu: The cpu to look for + * + * Returns: true if the cpu is available in the target binary. + */ +bool qtest_has_cpu_model(const char *cpu); + /** * qtest_has_device: * @device: The device to look for From e08f6e0b9fcf708f641bbb8839b7e30d857989d9 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 10 Jun 2024 21:23:00 +0530 Subject: [PATCH 1356/2884] tests/qtest/x86: check for availability of older cpu models before running tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is better to check if some older cpu models like 486, athlon, pentium, penryn, phenom, core2duo etc are available before running their corresponding tests. Some downstream distributions may no longer support these older cpu models. Signature of add_feature_test() has been modified to return void as FeatureTestArgs* was not used by the caller. One minor correction. Replaced 'phenom' with '486' in the test 'x86/cpuid/auto-level/phenom/arat' matching the cpu used. Signed-off-by: Ani Sinha Reviewed-by: Daniel P. Berrangé Message-ID: <20240610155303.7933-4-anisinha@redhat.com> Signed-off-by: Thomas Huth --- tests/qtest/test-x86-cpuid-compat.c | 170 ++++++++++++++++++---------- 1 file changed, 108 insertions(+), 62 deletions(-) diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index 6a39454fce..b9e7e5ef7b 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -67,10 +67,29 @@ static void test_cpuid_prop(const void *data) g_free(path); } -static void add_cpuid_test(const char *name, const char *cmdline, +static void add_cpuid_test(const char *name, const char *cpu, + const char *cpufeat, const char *machine, const char *property, int64_t expected_value) { CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); + char *cmdline; + char *save; + + if (!qtest_has_cpu_model(cpu)) { + return; + } + cmdline = g_strdup_printf("-cpu %s", cpu); + + if (cpufeat) { + save = cmdline; + cmdline = g_strdup_printf("%s,%s", cmdline, cpufeat); + g_free(save); + } + if (machine) { + save = cmdline; + cmdline = g_strdup_printf("-machine %s %s", machine, cmdline); + g_free(save); + } args->cmdline = cmdline; args->property = property; args->expected_value = expected_value; @@ -149,12 +168,24 @@ static void test_feature_flag(const void *data) * either "feature-words" or "filtered-features", when running QEMU * using cmdline */ -static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, - uint32_t eax, uint32_t ecx, - const char *reg, int bitnr, - bool expected_value) +static void add_feature_test(const char *name, const char *cpu, + const char *cpufeat, uint32_t eax, + uint32_t ecx, const char *reg, + int bitnr, bool expected_value) { FeatureTestArgs *args = g_new0(FeatureTestArgs, 1); + char *cmdline; + + if (!qtest_has_cpu_model(cpu)) { + return; + } + + if (cpufeat) { + cmdline = g_strdup_printf("-cpu %s,%s", cpu, cpufeat); + } else { + cmdline = g_strdup_printf("-cpu %s", cpu); + } + args->cmdline = cmdline; args->in_eax = eax; args->in_ecx = ecx; @@ -162,13 +193,17 @@ static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, args->bitnr = bitnr; args->expected_value = expected_value; qtest_add_data_func(name, args, test_feature_flag); - return args; + return; } static void test_plus_minus_subprocess(void) { char *path; + if (!qtest_has_cpu_model("pentium")) { + return; + } + /* Rules: * 1)"-foo" overrides "+foo" * 2) "[+-]foo" overrides "foo=..." @@ -198,6 +233,10 @@ static void test_plus_minus_subprocess(void) static void test_plus_minus(void) { + if (!qtest_has_cpu_model("pentium")) { + return; + } + g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); g_test_trap_assert_passed(); g_test_trap_assert_stderr("*Ambiguous CPU model string. " @@ -217,99 +256,105 @@ int main(int argc, char **argv) /* Original level values for CPU models: */ add_cpuid_test("x86/cpuid/phenom/level", - "-cpu phenom", "level", 5); + "phenom", NULL, NULL, "level", 5); add_cpuid_test("x86/cpuid/Conroe/level", - "-cpu Conroe", "level", 10); + "Conroe", NULL, NULL, "level", 10); add_cpuid_test("x86/cpuid/SandyBridge/level", - "-cpu SandyBridge", "level", 0xd); + "SandyBridge", NULL, NULL, "level", 0xd); add_cpuid_test("x86/cpuid/486/xlevel", - "-cpu 486", "xlevel", 0); + "486", NULL, NULL, "xlevel", 0); add_cpuid_test("x86/cpuid/core2duo/xlevel", - "-cpu core2duo", "xlevel", 0x80000008); + "core2duo", NULL, NULL, "xlevel", 0x80000008); add_cpuid_test("x86/cpuid/phenom/xlevel", - "-cpu phenom", "xlevel", 0x8000001A); + "phenom", NULL, NULL, "xlevel", 0x8000001A); add_cpuid_test("x86/cpuid/athlon/xlevel", - "-cpu athlon", "xlevel", 0x80000008); + "athlon", NULL, NULL, "xlevel", 0x80000008); /* If level is not large enough, it should increase automatically: */ /* CPUID[6].EAX: */ - add_cpuid_test("x86/cpuid/auto-level/phenom/arat", - "-cpu 486,arat=on", "level", 6); + add_cpuid_test("x86/cpuid/auto-level/486/arat", + "486", "arat=on", NULL, "level", 6); /* CPUID[EAX=7,ECX=0].EBX: */ add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", - "-cpu phenom,fsgsbase=on", "level", 7); + "phenom", "fsgsbase=on", NULL, "level", 7); /* CPUID[EAX=7,ECX=0].ECX: */ add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", - "-cpu phenom,avx512vbmi=on", "level", 7); + "phenom", "avx512vbmi=on", NULL, "level", 7); /* CPUID[EAX=0xd,ECX=1].EAX: */ add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", - "-cpu phenom,xsaveopt=on", "level", 0xd); + "phenom", "xsaveopt=on", NULL, "level", 0xd); /* CPUID[8000_0001].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", - "-cpu 486,3dnow=on", "xlevel", 0x80000001); + "486", "3dnow=on", NULL, "xlevel", 0x80000001); /* CPUID[8000_0001].ECX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", - "-cpu 486,sse4a=on", "xlevel", 0x80000001); + "486", "sse4a=on", NULL, "xlevel", 0x80000001); /* CPUID[8000_0007].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", - "-cpu 486,invtsc=on", "xlevel", 0x80000007); + "486", "invtsc=on", NULL, "xlevel", 0x80000007); /* CPUID[8000_000A].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", - "-cpu 486,svm=on,npt=on", "xlevel", 0x8000000A); + "486", "svm=on,npt=on", NULL, "xlevel", 0x8000000A); /* CPUID[C000_0001].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", - "-cpu phenom,xstore=on", "xlevel2", 0xC0000001); + "phenom", "xstore=on", NULL, "xlevel2", 0xC0000001); /* SVM needs CPUID[0x8000000A] */ add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", - "-cpu athlon,svm=on", "xlevel", 0x8000000A); + "athlon", "svm=on", NULL, "xlevel", 0x8000000A); /* If level is already large enough, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", - "-cpu SandyBridge,arat=on,fsgsbase=on,avx512vbmi=on", - "level", 0xd); + "SandyBridge", "arat=on,fsgsbase=on,avx512vbmi=on", + NULL, "level", 0xd); /* If level is explicitly set, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", - "-cpu 486,level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", - "level", 0xF); + "486", + "level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", + NULL, "level", 0xF); add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", - "-cpu 486,level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", - "level", 2); + "486", + "level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", + NULL, "level", 2); add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", - "-cpu 486,level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", - "level", 0); + "486", + "level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", + NULL, "level", 0); /* if xlevel is already large enough, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", - "-cpu phenom,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0x8000001A); + "phenom", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0x8000001A); /* If xlevel is explicitly set, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", - "-cpu 486,xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0x80000002); + "486", + "xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0x80000002); add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", - "-cpu 486,xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0x8000001A); + "486", + "xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0x8000001A); add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", - "-cpu 486,xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0); + "486", + "xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0); /* if xlevel2 is already large enough, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", - "-cpu 486,xlevel2=0xC0000002,xstore=on", - "xlevel2", 0xC0000002); + "486", "xlevel2=0xC0000002,xstore=on", + NULL, "xlevel2", 0xC0000002); /* Check compatibility of old machine-types that didn't * auto-increase level/xlevel/xlevel2: */ if (qtest_has_machine("pc-i440fx-2.7")) { add_cpuid_test("x86/cpuid/auto-level/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,arat=on,avx512vbmi=on,xsaveopt=on", - "level", 1); + "486", "arat=on,avx512vbmi=on,xsaveopt=on", + "pc-i440fx-2.7", "level", 1); add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0); + "486", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + "pc-i440fx-2.7", "xlevel", 0); add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,xstore=on", + "486", "xstore=on", "pc-i440fx-2.7", "xlevel2", 0); } /* @@ -319,18 +364,18 @@ int main(int argc, char **argv) */ if (qtest_has_machine("pc-i440fx-2.3")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", - "-machine pc-i440fx-2.3 -cpu Penryn", + "Penryn", NULL, "pc-i440fx-2.3", "level", 4); add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", - "-machine pc-i440fx-2.3 -cpu Penryn,erms=on", + "Penryn", "erms=on", "pc-i440fx-2.3", "level", 7); } if (qtest_has_machine("pc-i440fx-2.9")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", - "-machine pc-i440fx-2.9 -cpu Conroe", + "Conroe", NULL, "pc-i440fx-2.9", "level", 10); add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on", - "-machine pc-i440fx-2.9 -cpu Conroe,erms=on", + "Conroe", "erms=on", "pc-i440fx-2.9", "level", 10); } @@ -341,42 +386,43 @@ int main(int argc, char **argv) */ if (qtest_has_machine("pc-i440fx-2.3")) { add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", - "-machine pc-i440fx-2.3 -cpu SandyBridge", + "SandyBridge", NULL, "pc-i440fx-2.3", "xlevel", 0x8000000a); } if (qtest_has_machine("pc-i440fx-2.4")) { add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", - "-machine pc-i440fx-2.4 -cpu SandyBridge,", + "SandyBridge", NULL, "pc-i440fx-2.4", "xlevel", 0x80000008); add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", - "-machine pc-i440fx-2.4 -cpu SandyBridge,svm=on,npt=on", + "SandyBridge", "svm=on,npt=on", "pc-i440fx-2.4", "xlevel", 0x80000008); } /* Test feature parsing */ add_feature_test("x86/cpuid/features/plus", - "-cpu 486,+arat", + "486", "+arat", 6, 0, "EAX", 2, true); add_feature_test("x86/cpuid/features/minus", - "-cpu pentium,-mmx", + "pentium", "-mmx", 1, 0, "EDX", 23, false); add_feature_test("x86/cpuid/features/on", - "-cpu 486,arat=on", + "486", "arat=on", 6, 0, "EAX", 2, true); add_feature_test("x86/cpuid/features/off", - "-cpu pentium,mmx=off", + "pentium", "mmx=off", 1, 0, "EDX", 23, false); + add_feature_test("x86/cpuid/features/max-plus-invtsc", - "-cpu max,+invtsc", + "max" , "+invtsc", 0x80000007, 0, "EDX", 8, true); add_feature_test("x86/cpuid/features/max-invtsc-on", - "-cpu max,invtsc=on", + "max", "invtsc=on", 0x80000007, 0, "EDX", 8, true); add_feature_test("x86/cpuid/features/max-minus-mmx", - "-cpu max,-mmx", + "max", "-mmx", 1, 0, "EDX", 23, false); add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off", - "-cpu max,mmx=off", + "max", "mmx=off", 1, 0, "EDX", 23, false); return g_test_run(); From 829858f4f2015d34272d016d519e557de572e7ad Mon Sep 17 00:00:00 2001 From: zhenwei pi Date: Tue, 11 Jun 2024 18:54:26 +0800 Subject: [PATCH 1357/2884] meson: Remove libibumad dependence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RDMA based migration has no dependence on libumad. libibverbs and librdmacm are enough. libumad was used by rdmacm-mux which has been already removed. It's remained mistakenly. Fixes: 1dfd42c4264b ("hw/rdma: Remove deprecated pvrdma device and rdmacm-mux helper") Cc: Philippe Mathieu-Daudé Signed-off-by: zhenwei pi Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240611105427.61395-2-pizhenwei@bytedance.com> Signed-off-by: Thomas Huth --- meson.build | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/meson.build b/meson.build index ec59effca2..226b97ea26 100644 --- a/meson.build +++ b/meson.build @@ -1885,11 +1885,9 @@ endif rdma = not_found if not get_option('rdma').auto() or have_system - libumad = cc.find_library('ibumad', required: get_option('rdma')) rdma_libs = [cc.find_library('rdmacm', has_headers: ['rdma/rdma_cma.h'], required: get_option('rdma')), - cc.find_library('ibverbs', required: get_option('rdma')), - libumad] + cc.find_library('ibverbs', required: get_option('rdma'))] rdma = declare_dependency(dependencies: rdma_libs) foreach lib: rdma_libs if not lib.found() From c0cb5ccc35cd24858bee16942ac04d60b4ae37da Mon Sep 17 00:00:00 2001 From: zhenwei pi Date: Tue, 11 Jun 2024 18:54:27 +0800 Subject: [PATCH 1358/2884] test: Remove libibumad dependence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove libibumad dependence from the test environment. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: zhenwei pi Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240611105427.61395-3-pizhenwei@bytedance.com> Signed-off-by: Thomas Huth --- scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 1 - scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 1 - tests/docker/dockerfiles/debian-amd64-cross.docker | 1 - tests/docker/dockerfiles/debian-arm64-cross.docker | 1 - tests/docker/dockerfiles/debian-armel-cross.docker | 1 - tests/docker/dockerfiles/debian-armhf-cross.docker | 1 - tests/docker/dockerfiles/debian-i686-cross.docker | 1 - tests/docker/dockerfiles/debian-mips64el-cross.docker | 1 - tests/docker/dockerfiles/debian-mipsel-cross.docker | 1 - tests/docker/dockerfiles/debian-ppc64el-cross.docker | 1 - tests/docker/dockerfiles/debian-s390x-cross.docker | 1 - tests/docker/dockerfiles/debian.docker | 1 - tests/docker/dockerfiles/ubuntu2204.docker | 1 - tests/lcitool/projects/qemu.yml | 1 - 14 files changed, 14 deletions(-) diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml index 8d7d8725fb..fd5489cd82 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -49,7 +49,6 @@ packages: - libglusterfs-dev - libgnutls28-dev - libgtk-3-dev - - libibumad-dev - libibverbs-dev - libiscsi-dev - libjemalloc-dev diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml index 16050a5058..afa04502cf 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -49,7 +49,6 @@ packages: - libglusterfs-dev - libgnutls28-dev - libgtk-3-dev - - libibumad-dev - libibverbs-dev - libiscsi-dev - libjemalloc-dev diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index f8c61d1191..8058695979 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -105,7 +105,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:amd64 \ libgnutls28-dev:amd64 \ libgtk-3-dev:amd64 \ - libibumad-dev:amd64 \ libibverbs-dev:amd64 \ libiscsi-dev:amd64 \ libjemalloc-dev:amd64 \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 6510872279..15457d7657 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -105,7 +105,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:arm64 \ libgnutls28-dev:arm64 \ libgtk-3-dev:arm64 \ - libibumad-dev:arm64 \ libibverbs-dev:arm64 \ libiscsi-dev:arm64 \ libjemalloc-dev:arm64 \ diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index f227d42987..c26ffc2e9e 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -108,7 +108,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:armel \ libgnutls28-dev:armel \ libgtk-3-dev:armel \ - libibumad-dev:armel \ libibverbs-dev:armel \ libiscsi-dev:armel \ libjemalloc-dev:armel \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 58bdf07223..8f87656d89 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -105,7 +105,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:armhf \ libgnutls28-dev:armhf \ libgtk-3-dev:armhf \ - libibumad-dev:armhf \ libibverbs-dev:armhf \ libiscsi-dev:armhf \ libjemalloc-dev:armhf \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 9f4102be8f..f1e5b0b877 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -108,7 +108,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:i386 \ libgnutls28-dev:i386 \ libgtk-3-dev:i386 \ - libibumad-dev:i386 \ libibverbs-dev:i386 \ libiscsi-dev:i386 \ libjemalloc-dev:i386 \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index c861c3bd5b..59c4c68dce 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -107,7 +107,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:mips64el \ libgnutls28-dev:mips64el \ libgtk-3-dev:mips64el \ - libibumad-dev:mips64el \ libibverbs-dev:mips64el \ libiscsi-dev:mips64el \ libjemalloc-dev:mips64el \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index fe9415395e..880c774f1c 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -107,7 +107,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:mipsel \ libgnutls28-dev:mipsel \ libgtk-3-dev:mipsel \ - libibumad-dev:mipsel \ libibverbs-dev:mipsel \ libiscsi-dev:mipsel \ libjemalloc-dev:mipsel \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 35c8ff0864..1d55b9514c 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -105,7 +105,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:ppc64el \ libgnutls28-dev:ppc64el \ libgtk-3-dev:ppc64el \ - libibumad-dev:ppc64el \ libibverbs-dev:ppc64el \ libiscsi-dev:ppc64el \ libjemalloc-dev:ppc64el \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index bef9dff17a..62ccda6ab1 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -105,7 +105,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:s390x \ libgnutls28-dev:s390x \ libgtk-3-dev:s390x \ - libibumad-dev:s390x \ libibverbs-dev:s390x \ libiscsi-dev:s390x \ libjemalloc-dev:s390x \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 63d7aac616..0d1d401eb8 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -55,7 +55,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev \ libgnutls28-dev \ libgtk-3-dev \ - libibumad-dev \ libibverbs-dev \ libiscsi-dev \ libjemalloc-dev \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index febd25b320..beeb44fc28 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -55,7 +55,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev \ libgnutls28-dev \ libgtk-3-dev \ - libibumad-dev \ libibverbs-dev \ libiscsi-dev \ libjemalloc-dev \ diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 070d7f4706..0c85784259 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -47,7 +47,6 @@ packages: - libfdt - libffi - libgcrypt - - libibumad - libibverbs - libiscsi - libjemalloc From 94aae6ed218e962095bfe37efe5ae5ca6f30d9a2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:18 +0800 Subject: [PATCH 1359/2884] tests/unit/test-smp-parse: Fix comments of drawers and books case Fix the comments to match the actual configurations. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-2-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 9fdba24fce..fa8e7d83a7 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -474,8 +474,8 @@ static const struct SMPTestData data_with_drawers_invalid[] = { static const struct SMPTestData data_with_drawers_books_invalid[] = { { /* - * config: -smp 200,drawers=2,books=2,sockets=2,cores=4,\ - * threads=2,maxcpus=200 + * config: -smp 200,drawers=3,books=5,sockets=2,cores=4,\ + * threads=2,maxcpus=200 */ .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 200, T, 3, T, 5, T, 2, T, 4, T, 2, T, 200), @@ -485,8 +485,8 @@ static const struct SMPTestData data_with_drawers_books_invalid[] = { "cores (4) * threads (2) != maxcpus (200)", }, { /* - * config: -smp 242,drawers=2,books=2,sockets=2,cores=4,\ - * threads=2,maxcpus=240 + * config: -smp 242,drawers=3,books=5,sockets=2,cores=4,\ + * threads=2,maxcpus=240 */ .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 242, T, 3, T, 5, T, 2, T, 4, T, 2, T, 240), From 7c56fb74b31fa697c7cdf634a141756b98771f08 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:19 +0800 Subject: [PATCH 1360/2884] tests/unit/test-smp-parse: Fix comment of parameters=1 case SMP_CONFIG_WITH_FULL_TOPO hasn't support module level, so the parameter should indicate the "clusters". Additionally, reorder the parameters of -smp to match the topology hierarchy order. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-3-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index fa8e7d83a7..c9cbc89c21 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -333,7 +333,9 @@ static const struct SMPTestData data_generic_valid[] = { }, { /* * Unsupported parameters are always allowed to be set to '1' - * config: -smp 8,books=1,drawers=1,sockets=2,modules=1,dies=1,cores=2,threads=2,maxcpus=8 + * config: + * -smp 8,drawers=1,books=1,sockets=2,dies=1,clusters=1,cores=2,\ + * threads=2,maxcpus=8 * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */ .config = SMP_CONFIG_WITH_FULL_TOPO(8, 1, 1, 2, 1, 1, 2, 2, 8), .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), From 74c3d84d48084be058e5a9f9aa21f9edf174540e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:20 +0800 Subject: [PATCH 1361/2884] tests/unit/test-smp-parse: Fix an invalid topology case Adjust the "cpus" parameter to match the comment configuration. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-4-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index c9cbc89c21..5d99e0d923 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -528,7 +528,7 @@ static const struct SMPTestData data_full_topo_invalid[] = { * config: -smp 1,drawers=3,books=5,sockets=2,dies=4,\ * clusters=2,cores=7,threads=3,maxcpus=5040 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(3361, 3, 5, 2, 4, 2, 7, 3, 5040), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 3, 5, 2, 4, 2, 7, 3, 5040), .expect_error = "Invalid SMP CPUs 5040. The max CPUs supported " "by machine '" SMP_MACHINE_NAME "' is 4096", }, From aedfeffe19e65a657a4d989593fcf233809127d8 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:21 +0800 Subject: [PATCH 1362/2884] tests/unit/test-smp-parse: Use default parameters=0 when not set in -smp Since -smp allows parameters=1 whether the level is supported by machine, to avoid the test scenarios where the parameter defaults to 1 cause some errors to be masked, explicitly set undesired parameters to 0. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-5-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 5d99e0d923..e3a0a9d12d 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -436,7 +436,7 @@ static const struct SMPTestData data_with_clusters_invalid[] = { static const struct SMPTestData data_with_books_invalid[] = { { /* config: -smp 16,books=2,sockets=2,cores=4,threads=2,maxcpus=16 */ - .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, F, 1, T, 2, T, + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, F, 0, T, 2, T, 2, T, 4, T, 2, T, 16), .expect_error = "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " @@ -444,7 +444,7 @@ static const struct SMPTestData data_with_books_invalid[] = { "!= maxcpus (16)", }, { /* config: -smp 34,books=2,sockets=2,cores=4,threads=2,maxcpus=32 */ - .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, F, 1, T, 2, T, + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, F, 0, T, 2, T, 2, T, 4, T, 2, T, 32), .expect_error = "Invalid CPU topology: " "maxcpus must be equal to or greater than smp: " @@ -456,7 +456,7 @@ static const struct SMPTestData data_with_books_invalid[] = { static const struct SMPTestData data_with_drawers_invalid[] = { { /* config: -smp 16,drawers=2,sockets=2,cores=4,threads=2,maxcpus=16 */ - .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, T, 2, F, 1, T, + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, T, 2, F, 0, T, 2, T, 4, T, 2, T, 16), .expect_error = "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " @@ -464,7 +464,7 @@ static const struct SMPTestData data_with_drawers_invalid[] = { "!= maxcpus (16)", }, { /* config: -smp 34,drawers=2,sockets=2,cores=4,threads=2,maxcpus=32 */ - .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, T, 2, F, 1, T, + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, T, 2, F, 0, T, 2, T, 4, T, 2, T, 32), .expect_error = "Invalid CPU topology: " "maxcpus must be equal to or greater than smp: " From b985f4be53fe40e10583c3d35ff05255d712686a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:22 +0800 Subject: [PATCH 1363/2884] tests/unit/test-smp-parse: Make test cases aware of module level Currently, -smp supports module level. It is necessary to consider the effects of module in the test cases to ensure that the calculations are correct. This is also the preparation to add module test cases. Signed-off-by: Zhao Liu Reviewed-by: Thomas Huth Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-6-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index e3a0a9d12d..2214e47ba9 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -629,6 +629,7 @@ static char *smp_config_to_string(const SMPConfiguration *config) " .has_sockets = %5s, sockets = %" PRId64 ",\n" " .has_dies = %5s, dies = %" PRId64 ",\n" " .has_clusters = %5s, clusters = %" PRId64 ",\n" + " .has_modules = %5s, modules = %" PRId64 ",\n" " .has_cores = %5s, cores = %" PRId64 ",\n" " .has_threads = %5s, threads = %" PRId64 ",\n" " .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n" @@ -639,6 +640,7 @@ static char *smp_config_to_string(const SMPConfiguration *config) config->has_sockets ? "true" : "false", config->sockets, config->has_dies ? "true" : "false", config->dies, config->has_clusters ? "true" : "false", config->clusters, + config->has_modules ? "true" : "false", config->modules, config->has_cores ? "true" : "false", config->cores, config->has_threads ? "true" : "false", config->threads, config->has_maxcpus ? "true" : "false", config->maxcpus); @@ -679,6 +681,7 @@ static char *cpu_topology_to_string(const CpuTopology *topo, " .sockets = %u,\n" " .dies = %u,\n" " .clusters = %u,\n" + " .modules = %u,\n" " .cores = %u,\n" " .threads = %u,\n" " .max_cpus = %u,\n" @@ -688,8 +691,8 @@ static char *cpu_topology_to_string(const CpuTopology *topo, "}", topo->cpus, topo->drawers, topo->books, topo->sockets, topo->dies, topo->clusters, - topo->cores, topo->threads, topo->max_cpus, - threads_per_socket, cores_per_socket, + topo->modules, topo->cores, topo->threads, + topo->max_cpus, threads_per_socket, cores_per_socket, has_clusters ? "true" : "false"); } @@ -732,6 +735,7 @@ static void check_parse(MachineState *ms, const SMPConfiguration *config, (ms->smp.sockets == expect_topo->sockets) && (ms->smp.dies == expect_topo->dies) && (ms->smp.clusters == expect_topo->clusters) && + (ms->smp.modules == expect_topo->modules) && (ms->smp.cores == expect_topo->cores) && (ms->smp.threads == expect_topo->threads) && (ms->smp.max_cpus == expect_topo->max_cpus) && @@ -812,6 +816,11 @@ static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid) /* The parsed results of the unsupported parameters should be 1 */ static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) { + if (!mc->smp_props.modules_supported) { + data->expect_prefer_sockets.modules = 1; + data->expect_prefer_cores.modules = 1; + } + if (!mc->smp_props.dies_supported) { data->expect_prefer_sockets.dies = 1; data->expect_prefer_cores.dies = 1; From a05ed358542b90a3d53d96108fe5b83c545dfb84 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:23 +0800 Subject: [PATCH 1364/2884] tests/unit/test-smp-parse: Test "modules" parameter in -smp Cover the module cases in test-smp-parse. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-7-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 112 +++++++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 9 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 2214e47ba9..01832e5eda 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -48,17 +48,19 @@ } /* - * Currently a 4-level topology hierarchy is supported on PC machines - * -sockets/dies/cores/threads + * Currently a 5-level topology hierarchy is supported on PC machines + * -sockets/dies/modules/cores/threads */ -#define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \ +#define SMP_CONFIG_WITH_MODS_DIES(ha, a, hb, b, hc, c, hd, d, \ + he, e, hf, f, hg, g) \ { \ .has_cpus = ha, .cpus = a, \ .has_sockets = hb, .sockets = b, \ .has_dies = hc, .dies = c, \ - .has_cores = hd, .cores = d, \ - .has_threads = he, .threads = e, \ - .has_maxcpus = hf, .maxcpus = f, \ + .has_modules = hd, .modules = d, \ + .has_cores = he, .cores = e, \ + .has_threads = hf, .threads = f, \ + .has_maxcpus = hg, .maxcpus = g, \ } /* @@ -345,8 +347,14 @@ static const struct SMPTestData data_generic_valid[] = { static const struct SMPTestData data_generic_invalid[] = { { + /* config: -smp 2,modules=2 */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 2, F, 0, F, 0, T, 2, + F, 0, F, 0, F, 0), + .expect_error = "modules > 1 not supported by this machine's CPU topology", + }, { /* config: -smp 2,dies=2 */ - .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), + .config = SMP_CONFIG_WITH_MODS_DIES(T, 2, F, 0, T, 2, F, 0, + F, 0, F, 0, F, 0), .expect_error = "dies > 1 not supported by this machine's CPU topology", }, { /* config: -smp 2,clusters=2 */ @@ -397,17 +405,39 @@ static const struct SMPTestData data_generic_invalid[] = { }, }; +static const struct SMPTestData data_with_modules_invalid[] = { + { + /* config: -smp 16,sockets=2,modules=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 16, T, 2, F, 0, T, 2, + T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (2) * modules (2) * cores (4) * threads (2) " + "!= maxcpus (16)", + }, { + /* config: -smp 34,sockets=2,modules=2,cores=4,threads=2,maxcpus=32 */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 34, T, 2, F, 0, T, 2, + T, 4, T, 2, T, 32), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "sockets (2) * modules (2) * cores (4) * threads (2) " + "== maxcpus (32) < smp_cpus (34)", + }, +}; + static const struct SMPTestData data_with_dies_invalid[] = { { /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */ - .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16), + .config = SMP_CONFIG_WITH_MODS_DIES(T, 16, T, 2, T, 2, F, 0, + T, 4, T, 2, T, 16), .expect_error = "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " "sockets (2) * dies (2) * cores (4) * threads (2) " "!= maxcpus (16)", }, { /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */ - .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32), + .config = SMP_CONFIG_WITH_MODS_DIES(T, 34, T, 2, T, 2, F, 0, + T, 4, T, 2, T, 32), .expect_error = "Invalid CPU topology: " "maxcpus must be equal to or greater than smp: " "sockets (2) * dies (2) * cores (4) * threads (2) " @@ -861,6 +891,13 @@ static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS - 1; } +static void machine_with_modules_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.modules_supported = true; +} + static void machine_with_dies_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -945,6 +982,56 @@ static void test_generic_invalid(const void *opaque) object_unref(obj); } +static void test_with_modules(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int num_modules = 2; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* when modules parameter is omitted, it will be set as 1 */ + data.expect_prefer_sockets.modules = 1; + data.expect_prefer_cores.modules = 1; + + smp_parse_test(ms, &data, true); + + /* when modules parameter is specified */ + data.config.has_modules = true; + data.config.modules = num_modules; + if (data.config.has_cpus) { + data.config.cpus *= num_modules; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_modules; + } + + data.expect_prefer_sockets.modules = num_modules; + data.expect_prefer_sockets.cpus *= num_modules; + data.expect_prefer_sockets.max_cpus *= num_modules; + data.expect_prefer_cores.modules = num_modules; + data.expect_prefer_cores.cpus *= num_modules; + data.expect_prefer_cores.max_cpus *= num_modules; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_modules_invalid); i++) { + data = data_with_modules_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + static void test_with_dies(const void *opaque) { const char *machine_type = opaque; @@ -1303,6 +1390,10 @@ static const TypeInfo smp_machine_types[] = { .name = MACHINE_TYPE_NAME("smp-generic-invalid"), .parent = TYPE_MACHINE, .class_init = machine_generic_invalid_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-modules"), + .parent = TYPE_MACHINE, + .class_init = machine_with_modules_class_init, }, { .name = MACHINE_TYPE_NAME("smp-with-dies"), .parent = TYPE_MACHINE, @@ -1344,6 +1435,9 @@ int main(int argc, char *argv[]) g_test_add_data_func("/test-smp-parse/generic/invalid", MACHINE_TYPE_NAME("smp-generic-invalid"), test_generic_invalid); + g_test_add_data_func("/test-smp-parse/with_modules", + MACHINE_TYPE_NAME("smp-with-modules"), + test_with_modules); g_test_add_data_func("/test-smp-parse/with_dies", MACHINE_TYPE_NAME("smp-with-dies"), test_with_dies); From fe4b99525ac669ce9c57af19a7b9a87ebeb68866 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:24 +0800 Subject: [PATCH 1365/2884] tests/unit/test-smp-parse: Test "modules" and "dies" combination case Since i386 PC machine supports both "modules" and "dies" in -smp, add the "modules" and "dies" combination test case to match the actual topology usage scenario. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-8-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 01832e5eda..2ca8530e93 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -445,6 +445,33 @@ static const struct SMPTestData data_with_dies_invalid[] = { }, }; +static const struct SMPTestData data_with_modules_dies_invalid[] = { + { + /* + * config: -smp 200,sockets=3,dies=5,modules=2,cores=4,\ + * threads=2,maxcpus=200 + */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 200, T, 3, T, 5, T, + 2, T, 4, T, 2, T, 200), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (3) * dies (5) * modules (2) * " + "cores (4) * threads (2) != maxcpus (200)", + }, { + /* + * config: -smp 242,sockets=3,dies=5,modules=2,cores=4,\ + * threads=2,maxcpus=240 + */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 242, T, 3, T, 5, T, + 2, T, 4, T, 2, T, 240), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "sockets (3) * dies (5) * modules (2) * " + "cores (4) * threads (2) " + "== maxcpus (240) < smp_cpus (242)", + }, +}; + static const struct SMPTestData data_with_clusters_invalid[] = { { /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */ @@ -905,6 +932,14 @@ static void machine_with_dies_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; } +static void machine_with_modules_dies_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.modules_supported = true; + mc->smp_props.dies_supported = true; +} + static void machine_with_clusters_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1082,6 +1117,67 @@ static void test_with_dies(const void *opaque) object_unref(obj); } +static void test_with_modules_dies(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int num_modules = 5, num_dies = 3; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* + * when modules and dies parameters are omitted, they will + * be both set as 1. + */ + data.expect_prefer_sockets.modules = 1; + data.expect_prefer_sockets.dies = 1; + data.expect_prefer_cores.modules = 1; + data.expect_prefer_cores.dies = 1; + + smp_parse_test(ms, &data, true); + + /* when modules and dies parameters are both specified */ + data.config.has_modules = true; + data.config.modules = num_modules; + data.config.has_dies = true; + data.config.dies = num_dies; + + if (data.config.has_cpus) { + data.config.cpus *= num_modules * num_dies; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_modules * num_dies; + } + + data.expect_prefer_sockets.modules = num_modules; + data.expect_prefer_sockets.dies = num_dies; + data.expect_prefer_sockets.cpus *= num_modules * num_dies; + data.expect_prefer_sockets.max_cpus *= num_modules * num_dies; + + data.expect_prefer_cores.modules = num_modules; + data.expect_prefer_cores.dies = num_dies; + data.expect_prefer_cores.cpus *= num_modules * num_dies; + data.expect_prefer_cores.max_cpus *= num_modules * num_dies; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_modules_dies_invalid); i++) { + data = data_with_modules_dies_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + static void test_with_clusters(const void *opaque) { const char *machine_type = opaque; @@ -1398,6 +1494,10 @@ static const TypeInfo smp_machine_types[] = { .name = MACHINE_TYPE_NAME("smp-with-dies"), .parent = TYPE_MACHINE, .class_init = machine_with_dies_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-modules-dies"), + .parent = TYPE_MACHINE, + .class_init = machine_with_modules_dies_class_init, }, { .name = MACHINE_TYPE_NAME("smp-with-clusters"), .parent = TYPE_MACHINE, @@ -1441,6 +1541,9 @@ int main(int argc, char *argv[]) g_test_add_data_func("/test-smp-parse/with_dies", MACHINE_TYPE_NAME("smp-with-dies"), test_with_dies); + g_test_add_data_func("/test-smp-parse/with_modules_dies", + MACHINE_TYPE_NAME("smp-with-modules-dies"), + test_with_modules_dies); g_test_add_data_func("/test-smp-parse/with_clusters", MACHINE_TYPE_NAME("smp-with-clusters"), test_with_clusters); From 6a235525d93ae5a35c5006226ffdd7c7de47ba87 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 29 May 2024 14:19:25 +0800 Subject: [PATCH 1366/2884] tests/unit/test-smp-parse: Test the full 8-levels topology hierarchy With module level, QEMU now support 8-levels topology hierarchy. Cover "modules" in SMP_CONFIG_WITH_FULL_TOPO related cases. Signed-off-by: Zhao Liu Tested-by: Yongwei Ma Message-ID: <20240529061925.350323-9-zhao1.liu@intel.com> Signed-off-by: Thomas Huth --- tests/unit/test-smp-parse.c | 129 ++++++++++++++++++++++++------------ 1 file changed, 85 insertions(+), 44 deletions(-) diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index 2ca8530e93..f9bccb56ab 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -94,11 +94,11 @@ } /* - * Currently QEMU supports up to a 7-level topology hierarchy, which is the + * Currently QEMU supports up to a 8-level topology hierarchy, which is the * QEMU's unified abstract representation of CPU topology. - * -drawers/books/sockets/dies/clusters/cores/threads + * -drawers/books/sockets/dies/clusters/modules/cores/threads */ -#define SMP_CONFIG_WITH_FULL_TOPO(a, b, c, d, e, f, g, h, i) \ +#define SMP_CONFIG_WITH_FULL_TOPO(a, b, c, d, e, f, g, h, i, j) \ { \ .has_cpus = true, .cpus = a, \ .has_drawers = true, .drawers = b, \ @@ -106,9 +106,10 @@ .has_sockets = true, .sockets = d, \ .has_dies = true, .dies = e, \ .has_clusters = true, .clusters = f, \ - .has_cores = true, .cores = g, \ - .has_threads = true, .threads = h, \ - .has_maxcpus = true, .maxcpus = i, \ + .has_modules = true, .modules = g, \ + .has_cores = true, .cores = h, \ + .has_threads = true, .threads = i, \ + .has_maxcpus = true, .maxcpus = j, \ } /** @@ -336,10 +337,10 @@ static const struct SMPTestData data_generic_valid[] = { /* * Unsupported parameters are always allowed to be set to '1' * config: - * -smp 8,drawers=1,books=1,sockets=2,dies=1,clusters=1,cores=2,\ - * threads=2,maxcpus=8 + * -smp 8,drawers=1,books=1,sockets=2,dies=1,clusters=1,modules=1,\ + * cores=2,threads=2,maxcpus=8 * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(8, 1, 1, 2, 1, 1, 2, 2, 8), + .config = SMP_CONFIG_WITH_FULL_TOPO(8, 1, 1, 2, 1, 1, 1, 2, 2, 8), .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), }, @@ -561,32 +562,37 @@ static const struct SMPTestData data_full_topo_invalid[] = { { /* * config: -smp 200,drawers=3,books=5,sockets=2,dies=4,\ - * clusters=2,cores=7,threads=2,maxcpus=200 + * clusters=2,modules=3,cores=7,threads=2,\ + * maxcpus=200 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(200, 3, 5, 2, 4, 2, 7, 2, 200), + .config = SMP_CONFIG_WITH_FULL_TOPO(200, 3, 5, 2, 4, 2, 3, 7, 2, 200), .expect_error = "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " "drawers (3) * books (5) * sockets (2) * dies (4) * " - "clusters (2) * cores (7) * threads (2) " + "clusters (2) * modules (3) * cores (7) * threads (2) " "!= maxcpus (200)", }, { /* - * config: -smp 3361,drawers=3,books=5,sockets=2,dies=4,\ - * clusters=2,cores=7,threads=2,maxcpus=3360 + * config: -smp 2881,drawers=3,books=5,sockets=2,dies=4,\ + * clusters=2,modules=3,cores=2,threads=2, + * maxcpus=2880 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(3361, 3, 5, 2, 4, 2, 7, 2, 3360), + .config = SMP_CONFIG_WITH_FULL_TOPO(2881, 3, 5, 2, 4, + 2, 3, 2, 2, 2880), .expect_error = "Invalid CPU topology: " "maxcpus must be equal to or greater than smp: " - "drawers (3) * books (5) * sockets (2) * dies (4) * " - "clusters (2) * cores (7) * threads (2) " - "== maxcpus (3360) < smp_cpus (3361)", + "drawers (3) * books (5) * sockets (2) * " + "dies (4) * clusters (2) * modules (3) * " + "cores (2) * threads (2) == maxcpus (2880) " + "< smp_cpus (2881)", }, { /* * config: -smp 1,drawers=3,books=5,sockets=2,dies=4,\ - * clusters=2,cores=7,threads=3,maxcpus=5040 + * clusters=2,modules=3,cores=3,threads=3,\ + * maxcpus=6480 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 3, 5, 2, 4, 2, 7, 3, 5040), - .expect_error = "Invalid SMP CPUs 5040. The max CPUs supported " + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 3, 5, 2, 4, 2, 3, 3, 3, 6480), + .expect_error = "Invalid SMP CPUs 6480. The max CPUs supported " "by machine '" SMP_MACHINE_NAME "' is 4096", }, }; @@ -596,81 +602,100 @@ static const struct SMPTestData data_zero_topo_invalid[] = { /* * Test "cpus=0". * config: -smp 0,drawers=1,books=1,sockets=1,dies=1,\ - * clusters=1,cores=1,threads=1,maxcpus=1 + * clusters=1,modules=1,cores=1,threads=1,\ + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(0, 1, 1, 1, 1, 1, 1, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(0, 1, 1, 1, 1, 1, 1, 1, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "drawers=0". * config: -smp 1,drawers=0,books=1,sockets=1,dies=1,\ - * clusters=1,cores=1,threads=1,maxcpus=1 + * clusters=1,modules=1,cores=1,threads=1,\ + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 0, 1, 1, 1, 1, 1, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 0, 1, 1, 1, 1, 1, 1, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "books=0". * config: -smp 1,drawers=1,books=0,sockets=1,dies=1,\ - * clusters=1,cores=1,threads=1,maxcpus=1 + * clusters=1,modules=1,cores=1,threads=1,\ + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 0, 1, 1, 1, 1, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 0, 1, 1, 1, 1, 1, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "sockets=0". * config: -smp 1,drawers=1,books=1,sockets=0,dies=1,\ - * clusters=1,cores=1,threads=1,maxcpus=1 + * clusters=1,modules=1,cores=1,threads=1, + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 0, 1, 1, 1, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 0, 1, 1, 1, 1, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "dies=0". * config: -smp 1,drawers=1,books=1,sockets=1,dies=0,\ - * clusters=1,cores=1,threads=1,maxcpus=1 + * clusters=1,modules=1,cores=1,threads=1,\ + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 0, 1, 1, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 0, 1, 1, 1, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "clusters=0". * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ - * clusters=0,cores=1,threads=1,maxcpus=1 + * clusters=0,modules=1,cores=1,threads=1,\ + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 0, 1, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 0, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "modules=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=1,modules=0,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 0, 1, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "cores=0". * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ - * clusters=1,cores=0,threads=1,maxcpus=1 + * clusters=1,modules=1,cores=0,threads=1, + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 0, 1, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 0, 1, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "threads=0". * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ - * clusters=1,cores=1,threads=0,maxcpus=1 + * clusters=1,modules=1,cores=1,threads=0,\ + * maxcpus=1 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 0, 1), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 1, 0, 1), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, { /* * Test "maxcpus=0". * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ - * clusters=1,cores=1,threads=1,maxcpus=0 + * clusters=1,modules=1,cores=1,threads=1,\ + * maxcpus=0 */ - .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 1, 0), + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 1, 1, 0), .expect_error = "Invalid CPU topology: CPU topology parameters must " "be greater than zero", }, @@ -977,6 +1002,7 @@ static void machine_full_topo_class_init(ObjectClass *oc, void *data) mc->smp_props.books_supported = true; mc->smp_props.dies_supported = true; mc->smp_props.clusters_supported = true; + mc->smp_props.modules_supported = true; } static void test_generic_valid(const void *opaque) @@ -1396,30 +1422,41 @@ static void test_full_topo(const void *opaque) MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); SMPTestData data = {}; - unsigned int drawers = 5, books = 3, dies = 2, clusters = 7, multiplier; + unsigned int drawers, books, dies, clusters, modules, multiplier; int i; - multiplier = drawers * books * dies * clusters; + drawers = 5; + books = 3; + dies = 2; + clusters = 3; + modules = 2; + + multiplier = drawers * books * dies * clusters * modules; for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { data = data_generic_valid[i]; unsupported_params_init(mc, &data); /* - * when drawers, books, dies and clusters parameters are omitted, - * they will be set as 1. + * when drawers, books, dies, clusters and modules parameters are + * omitted, they will be set as 1. */ data.expect_prefer_sockets.drawers = 1; data.expect_prefer_sockets.books = 1; data.expect_prefer_sockets.dies = 1; data.expect_prefer_sockets.clusters = 1; + data.expect_prefer_sockets.modules = 1; data.expect_prefer_cores.drawers = 1; data.expect_prefer_cores.books = 1; data.expect_prefer_cores.dies = 1; data.expect_prefer_cores.clusters = 1; + data.expect_prefer_cores.modules = 1; smp_parse_test(ms, &data, true); - /* when drawers, books, dies and clusters parameters are specified. */ + /* + * when drawers, books, dies, clusters and modules parameters + * are specified. + */ data.config.has_drawers = true; data.config.drawers = drawers; data.config.has_books = true; @@ -1428,6 +1465,8 @@ static void test_full_topo(const void *opaque) data.config.dies = dies; data.config.has_clusters = true; data.config.clusters = clusters; + data.config.has_modules = true; + data.config.modules = modules; if (data.config.has_cpus) { data.config.cpus *= multiplier; @@ -1440,6 +1479,7 @@ static void test_full_topo(const void *opaque) data.expect_prefer_sockets.books = books; data.expect_prefer_sockets.dies = dies; data.expect_prefer_sockets.clusters = clusters; + data.expect_prefer_sockets.modules = modules; data.expect_prefer_sockets.cpus *= multiplier; data.expect_prefer_sockets.max_cpus *= multiplier; @@ -1447,6 +1487,7 @@ static void test_full_topo(const void *opaque) data.expect_prefer_cores.books = books; data.expect_prefer_cores.dies = dies; data.expect_prefer_cores.clusters = clusters; + data.expect_prefer_cores.modules = modules; data.expect_prefer_cores.cpus *= multiplier; data.expect_prefer_cores.max_cpus *= multiplier; From 26a09ead7351f117ae780781b347f014da03c20b Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 22 May 2024 20:38:48 +0200 Subject: [PATCH 1367/2884] tests/tcg/s390x: Allow specifying extra QEMU options on the command line The use case for this is `make check-tcg EXTFLAGS="-accel kvm"`, which allows validating the system TCG testcases on real hardware. EXTFLAGS name is borrowed from tests/tcg/xtensa/Makefile.softmmu-target. While at it, use += instead of = in order to be consistent with the other architectures. Signed-off-by: Ilya Leoshkevich Reviewed-by: Thomas Huth Message-ID: <20240522184116.35975-1-iii@linux.ibm.com> Signed-off-by: Thomas Huth --- tests/tcg/s390x/Makefile.softmmu-target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 80159cccf5..4c8e15e625 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -1,6 +1,6 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) -QEMU_OPTS=-action panic=exit-failure -nographic -kernel +QEMU_OPTS+=-action panic=exit-failure -nographic $(EXTFLAGS) -kernel LINK_SCRIPT=$(S390X_SRC)/softmmu.ld CFLAGS+=-ggdb -O0 LDFLAGS=-nostdlib -static From 3e40bdb15e2ba6e55ee92d148b0cefb7050fad20 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 30 May 2024 17:44:49 +1000 Subject: [PATCH 1368/2884] tests/qtest: Move common define from libqos-spapr.h to new ppc-util.h The spapr QEMU machine defaults is useful outside libqos, so create a new header for ppc specific qtests and move it there. Reviewed-by: Thomas Huth Signed-off-by: Nicholas Piggin Signed-off-by: Fabiano Rosas --- tests/qtest/boot-serial-test.c | 2 +- tests/qtest/libqos/libqos-spapr.h | 7 ------- tests/qtest/ppc-util.h | 19 +++++++++++++++++++ tests/qtest/prom-env-test.c | 2 +- tests/qtest/pxe-test.c | 2 +- 5 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 tests/qtest/ppc-util.h diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index df389adeeb..3b92fa5d50 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/libqos-spapr.h" +#include "ppc-util.h" static const uint8_t bios_avr[] = { 0x88, 0xe0, /* ldi r24, 0x08 */ diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h index e4483c14f8..a446276416 100644 --- a/tests/qtest/libqos/libqos-spapr.h +++ b/tests/qtest/libqos/libqos-spapr.h @@ -9,11 +9,4 @@ QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...) G_GNUC_PRINTF(1, 2); void qtest_spapr_shutdown(QOSState *qs); -/* List of capabilities needed to silence warnings with TCG */ -#define PSERIES_DEFAULT_CAPABILITIES \ - "cap-cfpc=broken," \ - "cap-sbbc=broken," \ - "cap-ibs=broken," \ - "cap-ccf-assist=off," - #endif diff --git a/tests/qtest/ppc-util.h b/tests/qtest/ppc-util.h new file mode 100644 index 0000000000..f68ee93520 --- /dev/null +++ b/tests/qtest/ppc-util.h @@ -0,0 +1,19 @@ +/* + * PowerPC misc useful things + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_UTIL_H +#define PPC_UTIL_H + +/* List of capabilities needed to silence warnings with TCG */ +#define PSERIES_DEFAULT_CAPABILITIES \ + "cap-cfpc=broken," \ + "cap-sbbc=broken," \ + "cap-ibs=broken," \ + "cap-ccf-assist=off," + +#endif /* PPC_UTIL_H */ diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c index 39ccb59797..14705105ad 100644 --- a/tests/qtest/prom-env-test.c +++ b/tests/qtest/prom-env-test.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/libqos-spapr.h" +#include "ppc-util.h" #define MAGIC 0xcafec0de #define ADDRESS 0x4000 diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c index e4b48225a5..a3f900fbea 100644 --- a/tests/qtest/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -16,7 +16,7 @@ #include #include "libqtest.h" #include "boot-sector.h" -#include "libqos/libqos-spapr.h" +#include "ppc-util.h" #define NETNAME "net0" From ea6ce9109eab5b9bca47d7145f1291ee4ddfe5de Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 30 May 2024 17:44:50 +1000 Subject: [PATCH 1369/2884] tests/qtest/migration-test: Quieten ppc64 QEMU warnings Reviewed-by: Thomas Huth Signed-off-by: Nicholas Piggin Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index b7e3406471..48f59822f4 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -21,6 +21,7 @@ #include "chardev/char.h" #include "crypto/tlscredspsk.h" #include "qapi/qmp/qlist.h" +#include "ppc-util.h" #include "migration-helpers.h" #include "tests/migration/migration-test.h" @@ -742,7 +743,8 @@ static int test_migrate_start(QTestState **from, QTestState **to, "until'", end_address, start_address); machine_alias = "pseries"; machine_opts = "vsmt=8"; - arch_opts = g_strdup("-nodefaults"); + arch_opts = g_strdup("-nodefaults " + "-machine " PSERIES_DEFAULT_CAPABILITIES); } else if (strcmp(arch, "aarch64") == 0) { memory_size = "150M"; machine_alias = "virt"; From bd1dcd86a02a0f4bf7a15ce45729912f54663e35 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 30 May 2024 17:44:51 +1000 Subject: [PATCH 1370/2884] tests/qtest/migration-test: Enable on ppc64 TCG ppc64 with TCG seems to no longer be failing this test, perhaps since commit 03bfc2188f061 ("physmem: Fix migration dirty bitmap coherency with TCG memory access") which is not ppc specific but was seen to hit ppc64 quite easily. Let's enable it again. The s390x problem has been identified so mention it while we are adjusting the comment. Reviewed-by: Thomas Huth Signed-off-by: Nicholas Piggin Reviewed-by: Prasad Pandit Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 48f59822f4..ef9ddef1c8 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -3454,19 +3454,9 @@ int main(int argc, char **argv) #endif /* - * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG - * is touchy due to race conditions on dirty bits (especially on PPC for - * some reason) - */ - if (g_str_equal(arch, "ppc64") && - (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { - g_test_message("Skipping tests: kvm_hv not available"); - goto test_add_done; - } - - /* - * Similar to ppc64, s390x seems to be touchy with TCG, so disable it - * there until the problems are resolved + * On s390x with TCG, migration is observed to hang due to the 'pending' + * state of the flic interrupt controller not being migrated or + * reconstructed post-migration. Disable it until the problem is resolved. */ if (g_str_equal(arch, "s390x") && !has_kvm) { g_test_message("Skipping tests: s390x host with KVM is required"); From 34cc54fb356145073051444be9d982c1974ba24e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 30 May 2024 17:44:52 +1000 Subject: [PATCH 1371/2884] tests/qtest/migration-test: Use custom asm bios for ppc64 Similar to other archs, build a custom bios memory updater. Running the test with OF code is a cool trick, but SLOF takes a long time to boot. This reduces test time by around 3x (150s to 50s). Reviewed-by: Fabiano Rosas Signed-off-by: Nicholas Piggin Signed-off-by: Fabiano Rosas --- tests/migration/Makefile | 2 +- tests/migration/migration-test.h | 1 + tests/migration/ppc64/Makefile | 15 +++++++ tests/migration/ppc64/a-b-kernel.S | 66 ++++++++++++++++++++++++++++++ tests/migration/ppc64/a-b-kernel.h | 42 +++++++++++++++++++ tests/qtest/migration-test.c | 37 +++-------------- 6 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 tests/migration/ppc64/Makefile create mode 100644 tests/migration/ppc64/a-b-kernel.S create mode 100644 tests/migration/ppc64/a-b-kernel.h diff --git a/tests/migration/Makefile b/tests/migration/Makefile index 13e99b1692..2c5ee287ec 100644 --- a/tests/migration/Makefile +++ b/tests/migration/Makefile @@ -5,7 +5,7 @@ # See the COPYING file in the top-level directory. # -TARGET_LIST = i386 aarch64 s390x +TARGET_LIST = i386 aarch64 s390x ppc64 SRC_PATH = ../.. diff --git a/tests/migration/migration-test.h b/tests/migration/migration-test.h index 68512c0b1b..194df7df6f 100644 --- a/tests/migration/migration-test.h +++ b/tests/migration/migration-test.h @@ -22,6 +22,7 @@ /* PPC */ #define PPC_TEST_MEM_START (1 * 1024 * 1024) #define PPC_TEST_MEM_END (100 * 1024 * 1024) +#define PPC_H_PUT_TERM_CHAR 0x58 /* ARM */ #define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) diff --git a/tests/migration/ppc64/Makefile b/tests/migration/ppc64/Makefile new file mode 100644 index 0000000000..a3a2d98ac8 --- /dev/null +++ b/tests/migration/ppc64/Makefile @@ -0,0 +1,15 @@ +.PHONY: all clean +all: a-b-kernel.h + +a-b-kernel.h: ppc64.kernel + echo "$$__note" > $@ + xxd -i $< | sed -e 's/.*int.*//' >> $@ + +ppc64.kernel: ppc64.elf + $(CROSS_PREFIX)objcopy -O binary -S $< $@ + +ppc64.elf: a-b-kernel.S + $(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $< + +clean: + $(RM) *.kernel *.elf diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/migration/ppc64/a-b-kernel.S new file mode 100644 index 0000000000..0613a8d18e --- /dev/null +++ b/tests/migration/ppc64/a-b-kernel.S @@ -0,0 +1,66 @@ +# +# Copyright (c) 2024 IBM, Inc +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +#include "../migration-test.h" + +.section .text + +.macro print ch + li %r3,PPC_H_PUT_TERM_CHAR + li %r4,0 + li %r5,1 + li %r6,\ch + sldi %r6,%r6,56 + sc 1 +.endm + + .globl _start +_start: +. = 0x100 + /* + * Enter 64-bit mode. Not necessary because the test uses 32-bit + * addresses, but those constants could easily be changed and break + * in 32-bit mode. + */ + mfmsr %r9 + li %r10,-1 + rldimi %r9,%r10,63,0 + mtmsrd %r9 + + /* + * Set up test memory region. Non-volatiles are used because the + * hcall can clobber regs. + * r20 - start address + * r21 - number of pages + */ + lis %r20,PPC_TEST_MEM_START@h + ori %r20,%r20,PPC_TEST_MEM_START@l + lis %r9,PPC_TEST_MEM_END@h + ori %r9,%r9,PPC_TEST_MEM_END@l + subf %r21,%r20,%r9 + li %r10,TEST_MEM_PAGE_SIZE + divd %r21,%r21,%r10 + + print 'A' + + li %r3,0 + mr %r9,%r20 + mtctr %r21 +1: stb %r3,0(%r9) + addi %r9,%r9,TEST_MEM_PAGE_SIZE + bdnz 1b + +loop: + mr %r9,%r20 + mtctr %r21 +1: lbz %r3,0(%r9) + addi %r3,%r3,1 + stb %r3,0(%r9) + addi %r9,%r9,TEST_MEM_PAGE_SIZE + bdnz 1b + + print 'B' + b loop diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/migration/ppc64/a-b-kernel.h new file mode 100644 index 0000000000..673317efdb --- /dev/null +++ b/tests/migration/ppc64/a-b-kernel.h @@ -0,0 +1,42 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/ppc64. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char ppc64_kernel[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x20, 0x00, 0xa6, 0x39, 0x40, 0xff, 0xff, + 0x79, 0x49, 0xf8, 0x0e, 0x7d, 0x20, 0x01, 0x64, 0x3e, 0x80, 0x00, 0x10, + 0x62, 0x94, 0x00, 0x00, 0x3d, 0x20, 0x06, 0x40, 0x61, 0x29, 0x00, 0x00, + 0x7e, 0xb4, 0x48, 0x50, 0x39, 0x40, 0x10, 0x00, 0x7e, 0xb5, 0x53, 0xd2, + 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, 0x38, 0xa0, 0x00, 0x01, + 0x38, 0xc0, 0x00, 0x41, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22, + 0x38, 0x60, 0x00, 0x00, 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, + 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, 0x42, 0x00, 0xff, 0xf8, + 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, 0x88, 0x69, 0x00, 0x00, + 0x38, 0x63, 0x00, 0x01, 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, + 0x42, 0x00, 0xff, 0xf0, 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, + 0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6, + 0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc +}; + diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index ef9ddef1c8..d6f5ceed80 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -128,6 +128,7 @@ static char *bootpath; */ #include "tests/migration/i386/a-b-bootblock.h" #include "tests/migration/aarch64/a-b-kernel.h" +#include "tests/migration/ppc64/a-b-kernel.h" #include "tests/migration/s390x/a-b-bios.h" static void bootfile_create(char *dir, bool suspend_me) @@ -147,10 +148,8 @@ static void bootfile_create(char *dir, bool suspend_me) content = s390x_elf; len = sizeof(s390x_elf); } else if (strcmp(arch, "ppc64") == 0) { - /* - * sane architectures can be programmed at the boot prompt - */ - return; + content = ppc64_kernel; + len = sizeof(ppc64_kernel); } else if (strcmp(arch, "aarch64") == 0) { content = aarch64_kernel; len = sizeof(aarch64_kernel); @@ -181,29 +180,10 @@ static void wait_for_serial(const char *side) { g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); FILE *serialfile = fopen(serialpath, "r"); - const char *arch = qtest_get_arch(); - int started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; do { int readvalue = fgetc(serialfile); - if (!started) { - /* SLOF prints its banner before starting test, - * to ignore it, mark the start of the test with '_', - * ignore all characters until this marker - */ - switch (readvalue) { - case '_': - started = 1; - break; - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - } - continue; - } switch (readvalue) { case 'A': /* Fine */ @@ -215,8 +195,6 @@ static void wait_for_serial(const char *side) return; case EOF: - started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; fseek(serialfile, 0, SEEK_SET); usleep(1000); break; @@ -737,14 +715,11 @@ static int test_migrate_start(QTestState **from, QTestState **to, memory_size = "256M"; start_address = PPC_TEST_MEM_START; end_address = PPC_TEST_MEM_END; - arch_source = g_strdup_printf("-prom-env 'use-nvramrc?=true' -prom-env " - "'nvramrc=hex .\" _\" begin %x %x " - "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " - "until'", end_address, start_address); machine_alias = "pseries"; machine_opts = "vsmt=8"; - arch_opts = g_strdup("-nodefaults " - "-machine " PSERIES_DEFAULT_CAPABILITIES); + arch_opts = g_strdup_printf( + "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " + "-bios %s", bootpath); } else if (strcmp(arch, "aarch64") == 0) { memory_size = "150M"; machine_alias = "virt"; From 0d40b3d76ced77c1c82c77a636af703fabdb407c Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:04 +0800 Subject: [PATCH 1372/2884] docs/migration: add qpl compression feature add Intel Query Processing Library (QPL) compression method introduction Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Fabiano Rosas Acked-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/devel/migration/features.rst | 1 + docs/devel/migration/qpl-compression.rst | 260 +++++++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 docs/devel/migration/qpl-compression.rst diff --git a/docs/devel/migration/features.rst b/docs/devel/migration/features.rst index d5ca7b86d5..bc98b65075 100644 --- a/docs/devel/migration/features.rst +++ b/docs/devel/migration/features.rst @@ -12,3 +12,4 @@ Migration has plenty of features to support different use cases. virtio mapped-ram CPR + qpl-compression diff --git a/docs/devel/migration/qpl-compression.rst b/docs/devel/migration/qpl-compression.rst new file mode 100644 index 0000000000..990992d786 --- /dev/null +++ b/docs/devel/migration/qpl-compression.rst @@ -0,0 +1,260 @@ +=============== +QPL Compression +=============== +The Intel Query Processing Library (Intel ``QPL``) is an open-source library to +provide compression and decompression features and it is based on deflate +compression algorithm (RFC 1951). + +The ``QPL`` compression relies on Intel In-Memory Analytics Accelerator(``IAA``) +and Shared Virtual Memory(``SVM``) technology, they are new features supported +from Intel 4th Gen Intel Xeon Scalable processors, codenamed Sapphire Rapids +processor(``SPR``). + +For more ``QPL`` introduction, please refer to `QPL Introduction +`_ + +QPL Compression Framework +========================= + +:: + + +----------------+ +------------------+ + | MultiFD Thread | |accel-config tool | + +-------+--------+ +--------+---------+ + | | + | | + |compress/decompress | + +-------+--------+ | Setup IAA + | QPL library | | Resources + +-------+---+----+ | + | | | + | +-------------+-------+ + | Open IAA | + | Devices +-----+-----+ + | |idxd driver| + | +-----+-----+ + | | + | | + | +-----+-----+ + +-----------+IAA Devices| + Submit jobs +-----------+ + via enqcmd + + +QPL Build And Installation +-------------------------- + +.. code-block:: shell + + $git clone --recursive https://github.com/intel/qpl.git qpl + $mkdir qpl/build + $cd qpl/build + $cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DQPL_LIBRARY_TYPE=SHARED .. + $sudo cmake --build . --target install + +For more details about ``QPL`` installation, please refer to `QPL Installation +`_ + +IAA Device Management +--------------------- + +The number of ``IAA`` devices will vary depending on the Xeon product model. +On a ``SPR`` server, there can be a maximum of 8 ``IAA`` devices, with up to +4 devices per socket. + +By default, all ``IAA`` devices are disabled and need to be configured and +enabled by users manually. + +Check the number of devices through the following command + +.. code-block:: shell + + #lspci -d 8086:0cfe + 6a:02.0 System peripheral: Intel Corporation Device 0cfe + 6f:02.0 System peripheral: Intel Corporation Device 0cfe + 74:02.0 System peripheral: Intel Corporation Device 0cfe + 79:02.0 System peripheral: Intel Corporation Device 0cfe + e7:02.0 System peripheral: Intel Corporation Device 0cfe + ec:02.0 System peripheral: Intel Corporation Device 0cfe + f1:02.0 System peripheral: Intel Corporation Device 0cfe + f6:02.0 System peripheral: Intel Corporation Device 0cfe + +IAA Device Configuration And Enabling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``accel-config`` tool is used to enable ``IAA`` devices and configure +``IAA`` hardware resources(work queues and engines). One ``IAA`` device +has 8 work queues and 8 processing engines, multiple engines can be assigned +to a work queue via ``group`` attribute. + +For ``accel-config`` installation, please refer to `accel-config installation +`_ + +One example of configuring and enabling an ``IAA`` device. + +.. code-block:: shell + + #accel-config config-engine iax1/engine1.0 -g 0 + #accel-config config-engine iax1/engine1.1 -g 0 + #accel-config config-engine iax1/engine1.2 -g 0 + #accel-config config-engine iax1/engine1.3 -g 0 + #accel-config config-engine iax1/engine1.4 -g 0 + #accel-config config-engine iax1/engine1.5 -g 0 + #accel-config config-engine iax1/engine1.6 -g 0 + #accel-config config-engine iax1/engine1.7 -g 0 + #accel-config config-wq iax1/wq1.0 -g 0 -s 128 -p 10 -b 1 -t 128 -m shared -y user -n app1 -d user + #accel-config enable-device iax1 + #accel-config enable-wq iax1/wq1.0 + +.. note:: + IAX is an early name for IAA + +- The ``IAA`` device index is 1, use ``ls -lh /sys/bus/dsa/devices/iax*`` + command to query the ``IAA`` device index. + +- 8 engines and 1 work queue are configured in group 0, so all compression jobs + submitted to this work queue can be processed by all engines at the same time. + +- Set work queue attributes including the work mode, work queue size and so on. + +- Enable the ``IAA1`` device and work queue 1.0 + +.. note:: + + Set work queue mode to shared mode, since ``QPL`` library only supports + shared mode + +For more detailed configuration, please refer to `IAA Configuration Samples +`_ + +IAA Unit Test +^^^^^^^^^^^^^ + +- Enabling ``IAA`` devices for Xeon platform, please refer to `IAA User Guide + `_ + +- ``IAA`` device driver is Intel Data Accelerator Driver (idxd), it is + recommended that the minimum version of Linux kernel is 5.18. + +- Add ``"intel_iommu=on,sm_on"`` parameter to kernel command line + for ``SVM`` feature enabling. + +Here is an easy way to verify ``IAA`` device driver and ``SVM`` with `iaa_test +`_ + +.. code-block:: shell + + #./test/iaa_test + [ info] alloc wq 0 shared size 128 addr 0x7f26cebe5000 batch sz 0xfffffffe xfer sz 0x80000000 + [ info] test noop: tflags 0x1 num_desc 1 + [ info] preparing descriptor for noop + [ info] Submitted all noop jobs + [ info] verifying task result for 0x16f7e20 + [ info] test with op 0 passed + + +IAA Resources Allocation For Migration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There is no ``IAA`` resource configuration parameters for migration and +``accel-config`` tool configuration cannot directly specify the ``IAA`` +resources used for migration. + +The multifd migration with ``QPL`` compression method will use all work +queues that are enabled and shared mode. + +.. note:: + + Accessing IAA resources requires ``sudo`` command or ``root`` privileges + by default. Administrators can modify the IAA device node ownership + so that QEMU can use IAA with specified user permissions. + + For example + + #chown -R qemu /dev/iax + +Shared Virtual Memory(SVM) Introduction +======================================= + +An ability for an accelerator I/O device to operate in the same virtual +memory space of applications on host processors. It also implies the +ability to operate from pageable memory, avoiding functional requirements +to pin memory for DMA operations. + +When using ``SVM`` technology, users do not need to reserve memory for the +``IAA`` device and perform pin memory operation. The ``IAA`` device can +directly access data using the virtual address of the process. + +For more ``SVM`` technology, please refer to +`Shared Virtual Addressing (SVA) with ENQCMD +`_ + + +How To Use QPL Compression In Migration +======================================= + +1 - Installation of ``QPL`` library and ``accel-config`` library if using IAA + +2 - Configure and enable ``IAA`` devices and work queues via ``accel-config`` + +3 - Build ``QEMU`` with ``--enable-qpl`` parameter + + E.g. configure --target-list=x86_64-softmmu --enable-kvm ``--enable-qpl`` + +4 - Enable ``QPL`` compression during migration + + Set ``migrate_set_parameter multifd-compression qpl`` when migrating, the + ``QPL`` compression does not support configuring the compression level, it + only supports one compression level. + +The Difference Between QPL And ZLIB +=================================== + +Although both ``QPL`` and ``ZLIB`` are based on the deflate compression +algorithm, and ``QPL`` can support the header and tail of ``ZLIB``, ``QPL`` +is still not fully compatible with the ``ZLIB`` compression in the migration. + +``QPL`` only supports 4K history buffer, and ``ZLIB`` is 32K by default. +``ZLIB`` compresses data that ``QPL`` may not decompress correctly and +vice versa. + +``QPL`` does not support the ``Z_SYNC_FLUSH`` operation in ``ZLIB`` streaming +compression, current ``ZLIB`` implementation uses ``Z_SYNC_FLUSH``, so each +``multifd`` thread has a ``ZLIB`` streaming context, and all page compression +and decompression are based on this stream. ``QPL`` cannot decompress such data +and vice versa. + +The introduction for ``Z_SYNC_FLUSH``, please refer to `Zlib Manual +`_ + +The Best Practices +================== +When user enables the IAA device for ``QPL`` compression, it is recommended +to add ``-mem-prealloc`` parameter to the destination boot parameters. This +parameter can avoid the occurrence of I/O page fault and reduce the overhead +of IAA compression and decompression. + +The example of booting with ``-mem-prealloc`` parameter + +.. code-block:: shell + + $qemu-system-x86_64 --enable-kvm -cpu host --mem-prealloc ... + + +An example about I/O page fault measurement of destination without +``-mem-prealloc``, the ``svm_prq`` indicates the number of I/O page fault +occurrences and processing time. + +.. code-block:: shell + + #echo 1 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #echo 2 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #echo 3 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #echo 4 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #cat /sys/kernel/debug/iommu/intel/dmar_perf_latency + IOMMU: dmar18 Register Base Address: c87fc000 + <0.1us 0.1us-1us 1us-10us 10us-100us 100us-1ms 1ms-10ms >=10ms min(us) max(us) average(us) + inv_iotlb 0 286 123 0 0 0 0 0 1 0 + inv_devtlb 0 276 133 0 0 0 0 0 2 0 + inv_iec 0 0 0 0 0 0 0 0 0 0 + svm_prq 0 0 25206 364 395 0 0 1 556 9 From d9d3e4f243214f742425d9d8360f0794bb05c999 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:05 +0800 Subject: [PATCH 1373/2884] migration/multifd: put IOV initialization into compression method Different compression methods may require different numbers of IOVs. Based on streaming compression of zlib and zstd, all pages will be compressed to a data block, so two IOVs are needed for packet header and compressed data block. Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/multifd-zlib.c | 7 +++++++ migration/multifd-zstd.c | 8 +++++++- migration/multifd.c | 22 ++++++++++++---------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 737a9645d2..2ced69487e 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -70,6 +70,10 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) goto err_free_zbuff; } p->compress_data = z; + + /* Needs 2 IOVs, one for packet header and one for compressed data */ + p->iov = g_new0(struct iovec, 2); + return 0; err_free_zbuff: @@ -101,6 +105,9 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) z->buf = NULL; g_free(p->compress_data); p->compress_data = NULL; + + g_free(p->iov); + p->iov = NULL; } /** diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 256858df0a..ca17b7e310 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -52,7 +52,6 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) struct zstd_data *z = g_new0(struct zstd_data, 1); int res; - p->compress_data = z; z->zcs = ZSTD_createCStream(); if (!z->zcs) { g_free(z); @@ -77,6 +76,10 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) error_setg(errp, "multifd %u: out of memory for zbuff", p->id); return -1; } + p->compress_data = z; + + /* Needs 2 IOVs, one for packet header and one for compressed data */ + p->iov = g_new0(struct iovec, 2); return 0; } @@ -98,6 +101,9 @@ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) z->zbuff = NULL; g_free(p->compress_data); p->compress_data = NULL; + + g_free(p->iov); + p->iov = NULL; } /** diff --git a/migration/multifd.c b/migration/multifd.c index f317bff077..d82885fdbb 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -137,6 +137,13 @@ static int nocomp_send_setup(MultiFDSendParams *p, Error **errp) p->write_flags |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; } + if (multifd_use_packets()) { + /* We need one extra place for the packet header */ + p->iov = g_new0(struct iovec, p->page_count + 1); + } else { + p->iov = g_new0(struct iovec, p->page_count); + } + return 0; } @@ -150,6 +157,8 @@ static int nocomp_send_setup(MultiFDSendParams *p, Error **errp) */ static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) { + g_free(p->iov); + p->iov = NULL; return; } @@ -228,6 +237,7 @@ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) */ static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) { + p->iov = g_new0(struct iovec, p->page_count); return 0; } @@ -240,6 +250,8 @@ static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) */ static void nocomp_recv_cleanup(MultiFDRecvParams *p) { + g_free(p->iov); + p->iov = NULL; } /** @@ -783,8 +795,6 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp) p->packet_len = 0; g_free(p->packet); p->packet = NULL; - g_free(p->iov); - p->iov = NULL; multifd_send_state->ops->send_cleanup(p, errp); return *errp == NULL; @@ -1179,11 +1189,6 @@ bool multifd_send_setup(void) p->packet = g_malloc0(p->packet_len); p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); p->packet->version = cpu_to_be32(MULTIFD_VERSION); - - /* We need one extra place for the packet header */ - p->iov = g_new0(struct iovec, page_count + 1); - } else { - p->iov = g_new0(struct iovec, page_count); } p->name = g_strdup_printf("multifdsend_%d", i); p->page_size = qemu_target_page_size(); @@ -1353,8 +1358,6 @@ static void multifd_recv_cleanup_channel(MultiFDRecvParams *p) p->packet_len = 0; g_free(p->packet); p->packet = NULL; - g_free(p->iov); - p->iov = NULL; g_free(p->normal); p->normal = NULL; g_free(p->zero); @@ -1602,7 +1605,6 @@ int multifd_recv_setup(Error **errp) p->packet = g_malloc0(p->packet_len); } p->name = g_strdup_printf("multifdrecv_%d", i); - p->iov = g_new0(struct iovec, page_count); p->normal = g_new0(ram_addr_t, page_count); p->zero = g_new0(ram_addr_t, page_count); p->page_count = page_count; From b844a2c7cc7f7c7756a27d372e64f6688d67c4eb Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:06 +0800 Subject: [PATCH 1374/2884] configure: add --enable-qpl build option add --enable-qpl and --disable-qpl options to enable and disable the QPL compression method for multifd migration. The Query Processing Library (QPL) is an open-source library that supports data compression and decompression features. It is based on the deflate compression algorithm and use Intel In-Memory Analytics Accelerator(IAA) hardware for compression and decompression acceleration. For more live migration with IAA, please refer to the document docs/devel/migration/qpl-compression.rst Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- meson.build | 8 ++++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 3 files changed, 13 insertions(+) diff --git a/meson.build b/meson.build index 3a2f126490..c16574710c 100644 --- a/meson.build +++ b/meson.build @@ -1201,6 +1201,12 @@ if not get_option('zstd').auto() or have_block required: get_option('zstd'), method: 'pkg-config') endif +qpl = not_found +if not get_option('qpl').auto() or have_system + qpl = dependency('qpl', version: '>=1.5.0', + required: get_option('qpl'), + method: 'pkg-config') +endif virgl = not_found have_vhost_user_gpu = have_tools and host_os == 'linux' and pixman.found() @@ -2333,6 +2339,7 @@ config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim) config_host_data.set('CONFIG_STATX', has_statx) config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id) config_host_data.set('CONFIG_ZSTD', zstd.found()) +config_host_data.set('CONFIG_QPL', qpl.found()) config_host_data.set('CONFIG_FUSE', fuse.found()) config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found()) config_host_data.set('CONFIG_SPICE_PROTOCOL', spice_protocol.found()) @@ -4446,6 +4453,7 @@ summary_info += {'snappy support': snappy} summary_info += {'bzip2 support': libbzip2} summary_info += {'lzfse support': liblzfse} summary_info += {'zstd support': zstd} +summary_info += {'Query Processing Library support': qpl} summary_info += {'NUMA host support': numa} summary_info += {'capstone': capstone} summary_info += {'libpmem support': libpmem} diff --git a/meson_options.txt b/meson_options.txt index 4c1583eb40..dd680a5faf 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -259,6 +259,8 @@ option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') option('zstd', type : 'feature', value : 'auto', description: 'zstd compression support') +option('qpl', type : 'feature', value : 'auto', + description: 'Query Processing Library support') option('fuse', type: 'feature', value: 'auto', description: 'FUSE block device export') option('fuse_lseek', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 6ce5a8b72a..73ae8cedfc 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -220,6 +220,7 @@ meson_options_help() { printf "%s\n" ' Xen PCI passthrough support' printf "%s\n" ' xkbcommon xkbcommon support' printf "%s\n" ' zstd zstd compression support' + printf "%s\n" ' qpl Query Processing Library support' } _meson_option_parse() { case $1 in @@ -558,6 +559,8 @@ _meson_option_parse() { --disable-xkbcommon) printf "%s" -Dxkbcommon=disabled ;; --enable-zstd) printf "%s" -Dzstd=enabled ;; --disable-zstd) printf "%s" -Dzstd=disabled ;; + --enable-qpl) printf "%s" -Dqpl=enabled ;; + --disable-qpl) printf "%s" -Dqpl=disabled ;; *) return 1 ;; esac } From 354cac2859e48ec5f7ee72a2a071da6c60a462d0 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:07 +0800 Subject: [PATCH 1375/2884] migration/multifd: add qpl compression method add the Query Processing Library (QPL) compression method Introduce the qpl as a new multifd migration compression method, it can use In-Memory Analytics Accelerator(IAA) to accelerate compression and decompression, which can not only reduce network bandwidth requirement but also reduce host compression and decompression CPU overhead. How to enable qpl compression during migration: migrate_set_parameter multifd-compression qpl There is no qpl compression level parameter added since it only supports level one, users do not need to specify the qpl compression level. Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas [fixed docs spacing in migration.json] Signed-off-by: Fabiano Rosas --- hw/core/qdev-properties-system.c | 2 +- migration/meson.build | 1 + migration/multifd-qpl.c | 20 ++++++++++++++++++++ migration/multifd.h | 1 + qapi/migration.json | 8 +++++++- 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 migration/multifd-qpl.c diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index d79d6f4b53..6ccd7224f6 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -659,7 +659,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = { const PropertyInfo qdev_prop_multifd_compression = { .name = "MultiFDCompression", .description = "multifd_compression values, " - "none/zlib/zstd", + "none/zlib/zstd/qpl", .enum_table = &MultiFDCompression_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, diff --git a/migration/meson.build b/migration/meson.build index bdc3244bce..5f146fe8a9 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -39,6 +39,7 @@ endif system_ss.add(when: rdma, if_true: files('rdma.c')) system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) +system_ss.add(when: qpl, if_true: files('multifd-qpl.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('ram.c', diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c new file mode 100644 index 0000000000..056a68a060 --- /dev/null +++ b/migration/multifd-qpl.c @@ -0,0 +1,20 @@ +/* + * Multifd qpl compression accelerator implementation + * + * Copyright (c) 2023 Intel Corporation + * + * Authors: + * Yuan Liu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/module.h" + +static void multifd_qpl_register(void) +{ + /* noop */ +} + +migration_init(multifd_qpl_register); diff --git a/migration/multifd.h b/migration/multifd.h index c9d9b09239..5b7d9b15f8 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -40,6 +40,7 @@ MultiFDRecvData *multifd_get_recv_data(void); #define MULTIFD_FLAG_NOCOMP (0 << 1) #define MULTIFD_FLAG_ZLIB (1 << 1) #define MULTIFD_FLAG_ZSTD (2 << 1) +#define MULTIFD_FLAG_QPL (4 << 1) /* This value needs to be a multiple of qemu_target_page_size() */ #define MULTIFD_PACKET_SIZE (512 * 1024) diff --git a/qapi/migration.json b/qapi/migration.json index a351fd3714..a35d0b5b83 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -554,11 +554,17 @@ # # @zstd: use zstd compression method. # +# @qpl: use qpl compression method. Query Processing Library(qpl) is +# based on the deflate compression algorithm and use the Intel +# In-Memory Analytics Accelerator(IAA) accelerated compression +# and decompression. (Since 9.1) +# # Since: 5.0 ## { 'enum': 'MultiFDCompression', 'data': [ 'none', 'zlib', - { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } + { 'name': 'zstd', 'if': 'CONFIG_ZSTD' }, + { 'name': 'qpl', 'if': 'CONFIG_QPL' } ] } ## # @MigMode: From 34e104b897da6e144a5f34e7c5eebf8a4c4d9d59 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:08 +0800 Subject: [PATCH 1376/2884] migration/multifd: implement initialization of qpl compression during initialization, a software job is allocated to each channel for software path fallabck when the IAA hardware is unavailable or the hardware job submission fails. If the IAA hardware is available, multiple hardware jobs are allocated for batch processing. Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- migration/multifd-qpl.c | 328 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 327 insertions(+), 1 deletion(-) diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index 056a68a060..6791a204d5 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -9,12 +9,338 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ + #include "qemu/osdep.h" #include "qemu/module.h" +#include "qapi/error.h" +#include "multifd.h" +#include "qpl/qpl.h" + +typedef struct { + /* the QPL hardware path job */ + qpl_job *job; + /* indicates if fallback to software path is required */ + bool fallback_sw_path; + /* output data from the software path */ + uint8_t *sw_output; + /* output data length from the software path */ + uint32_t sw_output_len; +} QplHwJob; + +typedef struct { + /* array of hardware jobs, the number of jobs equals the number pages */ + QplHwJob *hw_jobs; + /* the QPL software job for the slow path and software fallback */ + qpl_job *sw_job; + /* the number of pages that the QPL needs to process at one time */ + uint32_t page_num; + /* array of compressed page buffers */ + uint8_t *zbuf; + /* array of compressed page lengths */ + uint32_t *zlen; + /* the status of the hardware device */ + bool hw_avail; +} QplData; + +/** + * check_hw_avail: check if IAA hardware is available + * + * If the IAA hardware does not exist or is unavailable, + * the QPL hardware job initialization will fail. + * + * Returns true if IAA hardware is available, otherwise false. + * + * @job_size: indicates the hardware job size if hardware is available + */ +static bool check_hw_avail(uint32_t *job_size) +{ + qpl_path_t path = qpl_path_hardware; + uint32_t size = 0; + qpl_job *job; + + if (qpl_get_job_size(path, &size) != QPL_STS_OK) { + return false; + } + assert(size > 0); + job = g_malloc0(size); + if (qpl_init_job(path, job) != QPL_STS_OK) { + g_free(job); + return false; + } + g_free(job); + *job_size = size; + return true; +} + +/** + * multifd_qpl_free_sw_job: clean up software job + * + * Free the software job resources. + * + * @qpl: pointer to the QplData structure + */ +static void multifd_qpl_free_sw_job(QplData *qpl) +{ + assert(qpl); + if (qpl->sw_job) { + qpl_fini_job(qpl->sw_job); + g_free(qpl->sw_job); + qpl->sw_job = NULL; + } +} + +/** + * multifd_qpl_free_jobs: clean up hardware jobs + * + * Free all hardware job resources. + * + * @qpl: pointer to the QplData structure + */ +static void multifd_qpl_free_hw_job(QplData *qpl) +{ + assert(qpl); + if (qpl->hw_jobs) { + for (int i = 0; i < qpl->page_num; i++) { + qpl_fini_job(qpl->hw_jobs[i].job); + g_free(qpl->hw_jobs[i].job); + qpl->hw_jobs[i].job = NULL; + } + g_free(qpl->hw_jobs); + qpl->hw_jobs = NULL; + } +} + +/** + * multifd_qpl_init_sw_job: initialize a software job + * + * Use the QPL software path to initialize a job + * + * @qpl: pointer to the QplData structure + * @errp: pointer to an error + */ +static int multifd_qpl_init_sw_job(QplData *qpl, Error **errp) +{ + qpl_path_t path = qpl_path_software; + uint32_t size = 0; + qpl_job *job = NULL; + qpl_status status; + + status = qpl_get_job_size(path, &size); + if (status != QPL_STS_OK) { + error_setg(errp, "qpl_get_job_size failed with error %d", status); + return -1; + } + job = g_malloc0(size); + status = qpl_init_job(path, job); + if (status != QPL_STS_OK) { + error_setg(errp, "qpl_init_job failed with error %d", status); + g_free(job); + return -1; + } + qpl->sw_job = job; + return 0; +} + +/** + * multifd_qpl_init_jobs: initialize hardware jobs + * + * Use the QPL hardware path to initialize jobs + * + * @qpl: pointer to the QplData structure + * @size: the size of QPL hardware path job + * @errp: pointer to an error + */ +static void multifd_qpl_init_hw_job(QplData *qpl, uint32_t size, Error **errp) +{ + qpl_path_t path = qpl_path_hardware; + qpl_job *job = NULL; + qpl_status status; + + qpl->hw_jobs = g_new0(QplHwJob, qpl->page_num); + for (int i = 0; i < qpl->page_num; i++) { + job = g_malloc0(size); + status = qpl_init_job(path, job); + /* the job initialization should succeed after check_hw_avail */ + assert(status == QPL_STS_OK); + qpl->hw_jobs[i].job = job; + } +} + +/** + * multifd_qpl_init: initialize QplData structure + * + * Allocate and initialize a QplData structure + * + * Returns a QplData pointer on success or NULL on error + * + * @num: the number of pages + * @size: the page size + * @errp: pointer to an error + */ +static QplData *multifd_qpl_init(uint32_t num, uint32_t size, Error **errp) +{ + uint32_t job_size = 0; + QplData *qpl; + + qpl = g_new0(QplData, 1); + qpl->page_num = num; + if (multifd_qpl_init_sw_job(qpl, errp) != 0) { + g_free(qpl); + return NULL; + } + qpl->hw_avail = check_hw_avail(&job_size); + if (qpl->hw_avail) { + multifd_qpl_init_hw_job(qpl, job_size, errp); + } + qpl->zbuf = g_malloc0(size * num); + qpl->zlen = g_new0(uint32_t, num); + return qpl; +} + +/** + * multifd_qpl_deinit: clean up QplData structure + * + * Free jobs, buffers and the QplData structure + * + * @qpl: pointer to the QplData structure + */ +static void multifd_qpl_deinit(QplData *qpl) +{ + if (qpl) { + multifd_qpl_free_sw_job(qpl); + multifd_qpl_free_hw_job(qpl); + g_free(qpl->zbuf); + g_free(qpl->zlen); + g_free(qpl); + } +} + +/** + * multifd_qpl_send_setup: set up send side + * + * Set up the channel with QPL compression. + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_send_setup(MultiFDSendParams *p, Error **errp) +{ + QplData *qpl; + + qpl = multifd_qpl_init(p->page_count, p->page_size, errp); + if (!qpl) { + return -1; + } + p->compress_data = qpl; + + /* + * the page will be compressed independently and sent using an IOV. The + * additional two IOVs are used to store packet header and compressed data + * length + */ + p->iov = g_new0(struct iovec, p->page_count + 2); + return 0; +} + +/** + * multifd_qpl_send_cleanup: clean up send side + * + * Close the channel and free memory. + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static void multifd_qpl_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + multifd_qpl_deinit(p->compress_data); + p->compress_data = NULL; + g_free(p->iov); + p->iov = NULL; +} + +/** + * multifd_qpl_send_prepare: prepare data to be able to send + * + * Create a compressed buffer with all the pages that we are going to + * send. + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_send_prepare(MultiFDSendParams *p, Error **errp) +{ + /* Implement in next patch */ + return -1; +} + +/** + * multifd_qpl_recv_setup: set up receive side + * + * Create the compressed channel and buffer. + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + QplData *qpl; + + qpl = multifd_qpl_init(p->page_count, p->page_size, errp); + if (!qpl) { + return -1; + } + p->compress_data = qpl; + return 0; +} + +/** + * multifd_qpl_recv_cleanup: set up receive side + * + * Close the channel and free memory. + * + * @p: Params for the channel being used + */ +static void multifd_qpl_recv_cleanup(MultiFDRecvParams *p) +{ + multifd_qpl_deinit(p->compress_data); + p->compress_data = NULL; +} + +/** + * multifd_qpl_recv: read the data from the channel into actual pages + * + * Read the compressed buffer, and uncompress it into the actual + * pages. + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) +{ + /* Implement in next patch */ + return -1; +} + +static MultiFDMethods multifd_qpl_ops = { + .send_setup = multifd_qpl_send_setup, + .send_cleanup = multifd_qpl_send_cleanup, + .send_prepare = multifd_qpl_send_prepare, + .recv_setup = multifd_qpl_recv_setup, + .recv_cleanup = multifd_qpl_recv_cleanup, + .recv = multifd_qpl_recv, +}; static void multifd_qpl_register(void) { - /* noop */ + multifd_register_ops(MULTIFD_COMPRESSION_QPL, &multifd_qpl_ops); } migration_init(multifd_qpl_register); From f6fe9fea995249ecc2cd72975d803fbf4d512c02 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:09 +0800 Subject: [PATCH 1377/2884] migration/multifd: implement qpl compression and decompression QPL compression and decompression will use IAA hardware path if the IAA hardware is available. Otherwise the QPL library software path is used. The hardware path will automatically fall back to QPL software path if the IAA queues are busy. In some scenarios, this may happen frequently, such as configuring 4 channels but only one IAA device is available. In the case of insufficient IAA hardware resources, retry and fallback can help optimize performance: 1. Retry + SW fallback: total time: 14649 ms downtime: 25 ms throughput: 17666.57 mbps pages-per-second: 1509647 2. No fallback, always wait for work queues to become available total time: 18381 ms downtime: 25 ms throughput: 13698.65 mbps pages-per-second: 859607 If both the hardware and software paths fail, the uncompressed page is sent directly. Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- migration/multifd-qpl.c | 424 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 420 insertions(+), 4 deletions(-) diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index 6791a204d5..9265098ee7 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -13,9 +13,14 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qapi/error.h" +#include "qapi/qapi-types-migration.h" +#include "exec/ramblock.h" #include "multifd.h" #include "qpl/qpl.h" +/* Maximum number of retries to resubmit a job if IAA work queues are full */ +#define MAX_SUBMIT_RETRY_NUM (3) + typedef struct { /* the QPL hardware path job */ qpl_job *job; @@ -260,6 +265,225 @@ static void multifd_qpl_send_cleanup(MultiFDSendParams *p, Error **errp) p->iov = NULL; } +/** + * multifd_qpl_prepare_job: prepare the job + * + * Set the QPL job parameters and properties. + * + * @job: pointer to the qpl_job structure + * @is_compression: indicates compression and decompression + * @input: pointer to the input data buffer + * @input_len: the length of the input data + * @output: pointer to the output data buffer + * @output_len: the length of the output data + */ +static void multifd_qpl_prepare_job(qpl_job *job, bool is_compression, + uint8_t *input, uint32_t input_len, + uint8_t *output, uint32_t output_len) +{ + job->op = is_compression ? qpl_op_compress : qpl_op_decompress; + job->next_in_ptr = input; + job->next_out_ptr = output; + job->available_in = input_len; + job->available_out = output_len; + job->flags = QPL_FLAG_FIRST | QPL_FLAG_LAST | QPL_FLAG_OMIT_VERIFY; + /* only supports compression level 1 */ + job->level = 1; +} + +/** + * multifd_qpl_prepare_comp_job: prepare the compression job + * + * Set the compression job parameters and properties. + * + * @job: pointer to the qpl_job structure + * @input: pointer to the input data buffer + * @output: pointer to the output data buffer + * @size: the page size + */ +static void multifd_qpl_prepare_comp_job(qpl_job *job, uint8_t *input, + uint8_t *output, uint32_t size) +{ + /* + * Set output length to less than the page size to force the job to + * fail in case it compresses to a larger size. We'll send that page + * without compression and skip the decompression operation on the + * destination. + */ + multifd_qpl_prepare_job(job, true, input, size, output, size - 1); +} + +/** + * multifd_qpl_prepare_decomp_job: prepare the decompression job + * + * Set the decompression job parameters and properties. + * + * @job: pointer to the qpl_job structure + * @input: pointer to the input data buffer + * @len: the length of the input data + * @output: pointer to the output data buffer + * @size: the page size + */ +static void multifd_qpl_prepare_decomp_job(qpl_job *job, uint8_t *input, + uint32_t len, uint8_t *output, + uint32_t size) +{ + multifd_qpl_prepare_job(job, false, input, len, output, size); +} + +/** + * multifd_qpl_fill_iov: fill in the IOV + * + * Fill in the QPL packet IOV + * + * @p: Params for the channel being used + * @data: pointer to the IOV data + * @len: The length of the IOV data + */ +static void multifd_qpl_fill_iov(MultiFDSendParams *p, uint8_t *data, + uint32_t len) +{ + p->iov[p->iovs_num].iov_base = data; + p->iov[p->iovs_num].iov_len = len; + p->iovs_num++; + p->next_packet_size += len; +} + +/** + * multifd_qpl_fill_packet: fill the compressed page into the QPL packet + * + * Fill the compressed page length and IOV into the QPL packet + * + * @idx: The index of the compressed length array + * @p: Params for the channel being used + * @data: pointer to the compressed page buffer + * @len: The length of the compressed page + */ +static void multifd_qpl_fill_packet(uint32_t idx, MultiFDSendParams *p, + uint8_t *data, uint32_t len) +{ + QplData *qpl = p->compress_data; + + qpl->zlen[idx] = cpu_to_be32(len); + multifd_qpl_fill_iov(p, data, len); +} + +/** + * multifd_qpl_submit_job: submit a job to the hardware + * + * Submit a QPL hardware job to the IAA device + * + * Returns true if the job is submitted successfully, otherwise false. + * + * @job: pointer to the qpl_job structure + */ +static bool multifd_qpl_submit_job(qpl_job *job) +{ + qpl_status status; + uint32_t num = 0; + +retry: + status = qpl_submit_job(job); + if (status == QPL_STS_QUEUES_ARE_BUSY_ERR) { + if (num < MAX_SUBMIT_RETRY_NUM) { + num++; + goto retry; + } + } + return (status == QPL_STS_OK); +} + +/** + * multifd_qpl_compress_pages_slow_path: compress pages using slow path + * + * Compress the pages using software. If compression fails, the uncompressed + * page will be sent. + * + * @p: Params for the channel being used + */ +static void multifd_qpl_compress_pages_slow_path(MultiFDSendParams *p) +{ + QplData *qpl = p->compress_data; + uint32_t size = p->page_size; + qpl_job *job = qpl->sw_job; + uint8_t *zbuf = qpl->zbuf; + uint8_t *buf; + + for (int i = 0; i < p->pages->normal_num; i++) { + buf = p->pages->block->host + p->pages->offset[i]; + multifd_qpl_prepare_comp_job(job, buf, zbuf, size); + if (qpl_execute_job(job) == QPL_STS_OK) { + multifd_qpl_fill_packet(i, p, zbuf, job->total_out); + } else { + /* send the uncompressed page */ + multifd_qpl_fill_packet(i, p, buf, size); + } + zbuf += size; + } +} + +/** + * multifd_qpl_compress_pages: compress pages + * + * Submit the pages to the IAA hardware for compression. If hardware + * compression fails, it falls back to software compression. If software + * compression also fails, the uncompressed page is sent. + * + * @p: Params for the channel being used + */ +static void multifd_qpl_compress_pages(MultiFDSendParams *p) +{ + QplData *qpl = p->compress_data; + MultiFDPages_t *pages = p->pages; + uint32_t size = p->page_size; + QplHwJob *hw_job; + uint8_t *buf; + uint8_t *zbuf; + + for (int i = 0; i < pages->normal_num; i++) { + buf = pages->block->host + pages->offset[i]; + zbuf = qpl->zbuf + (size * i); + hw_job = &qpl->hw_jobs[i]; + multifd_qpl_prepare_comp_job(hw_job->job, buf, zbuf, size); + if (multifd_qpl_submit_job(hw_job->job)) { + hw_job->fallback_sw_path = false; + } else { + /* + * The IAA work queue is full, any immediate subsequent job + * submission is likely to fail, sending the page via the QPL + * software path at this point gives us a better chance of + * finding the queue open for the next pages. + */ + hw_job->fallback_sw_path = true; + multifd_qpl_prepare_comp_job(qpl->sw_job, buf, zbuf, size); + if (qpl_execute_job(qpl->sw_job) == QPL_STS_OK) { + hw_job->sw_output = zbuf; + hw_job->sw_output_len = qpl->sw_job->total_out; + } else { + hw_job->sw_output = buf; + hw_job->sw_output_len = size; + } + } + } + + for (int i = 0; i < pages->normal_num; i++) { + buf = pages->block->host + pages->offset[i]; + zbuf = qpl->zbuf + (size * i); + hw_job = &qpl->hw_jobs[i]; + if (hw_job->fallback_sw_path) { + multifd_qpl_fill_packet(i, p, hw_job->sw_output, + hw_job->sw_output_len); + continue; + } + if (qpl_wait_job(hw_job->job) == QPL_STS_OK) { + multifd_qpl_fill_packet(i, p, zbuf, hw_job->job->total_out); + } else { + /* send the uncompressed page */ + multifd_qpl_fill_packet(i, p, buf, size); + } + } +} + /** * multifd_qpl_send_prepare: prepare data to be able to send * @@ -273,8 +497,26 @@ static void multifd_qpl_send_cleanup(MultiFDSendParams *p, Error **errp) */ static int multifd_qpl_send_prepare(MultiFDSendParams *p, Error **errp) { - /* Implement in next patch */ - return -1; + QplData *qpl = p->compress_data; + uint32_t len = 0; + + if (!multifd_send_prepare_common(p)) { + goto out; + } + + /* The first IOV is used to store the compressed page lengths */ + len = p->pages->normal_num * sizeof(uint32_t); + multifd_qpl_fill_iov(p, (uint8_t *) qpl->zlen, len); + if (qpl->hw_avail) { + multifd_qpl_compress_pages(p); + } else { + multifd_qpl_compress_pages_slow_path(p); + } + +out: + p->flags |= MULTIFD_FLAG_QPL; + multifd_send_fill_packet(p); + return 0; } /** @@ -312,6 +554,140 @@ static void multifd_qpl_recv_cleanup(MultiFDRecvParams *p) p->compress_data = NULL; } +/** + * multifd_qpl_process_and_check_job: process and check a QPL job + * + * Process the job and check whether the job output length is the + * same as the specified length + * + * Returns true if the job execution succeeded and the output length + * is equal to the specified length, otherwise false. + * + * @job: pointer to the qpl_job structure + * @is_hardware: indicates whether the job is a hardware job + * @len: Specified output length + * @errp: pointer to an error + */ +static bool multifd_qpl_process_and_check_job(qpl_job *job, bool is_hardware, + uint32_t len, Error **errp) +{ + qpl_status status; + + status = (is_hardware ? qpl_wait_job(job) : qpl_execute_job(job)); + if (status != QPL_STS_OK) { + error_setg(errp, "qpl job failed with error %d", status); + return false; + } + if (job->total_out != len) { + error_setg(errp, "qpl decompressed len %u, expected len %u", + job->total_out, len); + return false; + } + return true; +} + +/** + * multifd_qpl_decompress_pages_slow_path: decompress pages using slow path + * + * Decompress the pages using software + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_decompress_pages_slow_path(MultiFDRecvParams *p, + Error **errp) +{ + QplData *qpl = p->compress_data; + uint32_t size = p->page_size; + qpl_job *job = qpl->sw_job; + uint8_t *zbuf = qpl->zbuf; + uint8_t *addr; + uint32_t len; + + for (int i = 0; i < p->normal_num; i++) { + len = qpl->zlen[i]; + addr = p->host + p->normal[i]; + /* the page is uncompressed, load it */ + if (len == size) { + memcpy(addr, zbuf, size); + zbuf += size; + continue; + } + multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size); + if (!multifd_qpl_process_and_check_job(job, false, size, errp)) { + return -1; + } + zbuf += len; + } + return 0; +} + +/** + * multifd_qpl_decompress_pages: decompress pages + * + * Decompress the pages using the IAA hardware. If hardware + * decompression fails, it falls back to software decompression. + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_decompress_pages(MultiFDRecvParams *p, Error **errp) +{ + QplData *qpl = p->compress_data; + uint32_t size = p->page_size; + uint8_t *zbuf = qpl->zbuf; + uint8_t *addr; + uint32_t len; + qpl_job *job; + + for (int i = 0; i < p->normal_num; i++) { + addr = p->host + p->normal[i]; + len = qpl->zlen[i]; + /* the page is uncompressed if received length equals the page size */ + if (len == size) { + memcpy(addr, zbuf, size); + zbuf += size; + continue; + } + + job = qpl->hw_jobs[i].job; + multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size); + if (multifd_qpl_submit_job(job)) { + qpl->hw_jobs[i].fallback_sw_path = false; + } else { + /* + * The IAA work queue is full, any immediate subsequent job + * submission is likely to fail, sending the page via the QPL + * software path at this point gives us a better chance of + * finding the queue open for the next pages. + */ + qpl->hw_jobs[i].fallback_sw_path = true; + job = qpl->sw_job; + multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size); + if (!multifd_qpl_process_and_check_job(job, false, size, errp)) { + return -1; + } + } + zbuf += len; + } + + for (int i = 0; i < p->normal_num; i++) { + /* ignore pages that have already been processed */ + if (qpl->zlen[i] == size || qpl->hw_jobs[i].fallback_sw_path) { + continue; + } + + job = qpl->hw_jobs[i].job; + if (!multifd_qpl_process_and_check_job(job, true, size, errp)) { + return -1; + } + } + return 0; +} /** * multifd_qpl_recv: read the data from the channel into actual pages * @@ -325,8 +701,48 @@ static void multifd_qpl_recv_cleanup(MultiFDRecvParams *p) */ static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) { - /* Implement in next patch */ - return -1; + QplData *qpl = p->compress_data; + uint32_t in_size = p->next_packet_size; + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + uint32_t len = 0; + uint32_t zbuf_len = 0; + int ret; + + if (flags != MULTIFD_FLAG_QPL) { + error_setg(errp, "multifd %u: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_QPL); + return -1; + } + multifd_recv_zero_page_process(p); + if (!p->normal_num) { + assert(in_size == 0); + return 0; + } + + /* read compressed page lengths */ + len = p->normal_num * sizeof(uint32_t); + assert(len < in_size); + ret = qio_channel_read_all(p->c, (void *) qpl->zlen, len, errp); + if (ret != 0) { + return ret; + } + for (int i = 0; i < p->normal_num; i++) { + qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]); + assert(qpl->zlen[i] <= p->page_size); + zbuf_len += qpl->zlen[i]; + } + + /* read compressed pages */ + assert(in_size == len + zbuf_len); + ret = qio_channel_read_all(p->c, (void *) qpl->zbuf, zbuf_len, errp); + if (ret != 0) { + return ret; + } + + if (qpl->hw_avail) { + return multifd_qpl_decompress_pages(p, errp); + } + return multifd_qpl_decompress_pages_slow_path(p, errp); } static MultiFDMethods multifd_qpl_ops = { From 08b82d207d138173ddd334c91b387213508a6e13 Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Mon, 10 Jun 2024 18:21:10 +0800 Subject: [PATCH 1378/2884] tests/migration-test: add qpl compression test add qpl to compression method test for multifd migration the qpl compression supports software path and hardware path(IAA device), and the hardware path is used first by default. If the hardware path is unavailable, it will automatically fallback to the software path for testing. Signed-off-by: Yuan Liu Reviewed-by: Nanhai Zou Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index d6f5ceed80..887cd9fe42 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -2638,6 +2638,15 @@ test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, } #endif /* CONFIG_ZSTD */ +#ifdef CONFIG_QPL +static void * +test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from, + QTestState *to) +{ + return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl"); +} +#endif /* CONFIG_QPL */ + static void test_multifd_tcp_uri_none(void) { MigrateCommon args = { @@ -2718,6 +2727,17 @@ static void test_multifd_tcp_zstd(void) } #endif +#ifdef CONFIG_QPL +static void test_multifd_tcp_qpl(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_qpl_start, + }; + test_precopy_common(&args); +} +#endif + #ifdef CONFIG_GNUTLS static void * test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, @@ -3593,6 +3613,10 @@ int main(int argc, char **argv) migration_test_add("/migration/multifd/tcp/plain/zstd", test_multifd_tcp_zstd); #endif +#ifdef CONFIG_QPL + migration_test_add("/migration/multifd/tcp/plain/qpl", + test_multifd_tcp_qpl); +#endif #ifdef CONFIG_GNUTLS migration_test_add("/migration/multifd/tcp/tls/psk/match", test_multifd_tcp_tls_psk_match); From 3ae9bd97829213808298ae6d35ea26f8def15dc1 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:04 +0100 Subject: [PATCH 1379/2884] docs/migration: add uadk compression feature Document UADK(User Space Accelerator Development Kit) library details and how to use that for migration. Signed-off-by: Shameer Kolothum Reviewed-by: Zhangfei Gao [s/Qemu/QEMU in docs] Signed-off-by: Fabiano Rosas --- docs/devel/migration/features.rst | 1 + docs/devel/migration/uadk-compression.rst | 144 ++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 docs/devel/migration/uadk-compression.rst diff --git a/docs/devel/migration/features.rst b/docs/devel/migration/features.rst index bc98b65075..58f8fd9e16 100644 --- a/docs/devel/migration/features.rst +++ b/docs/devel/migration/features.rst @@ -13,3 +13,4 @@ Migration has plenty of features to support different use cases. mapped-ram CPR qpl-compression + uadk-compression diff --git a/docs/devel/migration/uadk-compression.rst b/docs/devel/migration/uadk-compression.rst new file mode 100644 index 0000000000..3f73345dd5 --- /dev/null +++ b/docs/devel/migration/uadk-compression.rst @@ -0,0 +1,144 @@ +========================================================= +User Space Accelerator Development Kit (UADK) Compression +========================================================= +UADK is a general-purpose user space accelerator framework that uses shared +virtual addressing (SVA) to provide a unified programming interface for +hardware acceleration of cryptographic and compression algorithms. + +UADK includes Unified/User-space-access-intended Accelerator Framework (UACCE), +which enables hardware accelerators from different vendors that support SVA to +adapt to UADK. + +Currently, HiSilicon Kunpeng hardware accelerators have been registered with +UACCE. Through the UADK framework, users can run cryptographic and compression +algorithms using hardware accelerators instead of CPUs, freeing up CPU +computing power and improving computing performance. + +https://github.com/Linaro/uadk/tree/master/docs + +UADK Framework +============== +UADK consists of UACCE, vendors' drivers, and an algorithm layer. UADK requires +the hardware accelerator to support SVA, and the operating system to support +IOMMU and SVA. Hardware accelerators from different vendors are registered as +different character devices with UACCE by using kernel-mode drivers of the +vendors. A user can access the hardware accelerators by performing user-mode +operations on the character devices. + +:: + + +----------------------------------+ + | apps | + +----+------------------------+----+ + | | + | | + +-------+--------+ +-------+-------+ + | scheduler | | alg libraries | + +-------+--------+ +-------+-------+ + | | + | | + | | + | +--------+------+ + | | vendor drivers| + | +-+-------------+ + | | + | | + +--+------------------+--+ + | libwd | + User +----+-------------+-----+ + -------------------------------------------------- + Kernel +--+-----+ +------+ + | uacce | | smmu | + +---+----+ +------+ + | + +---+------------------+ + | vendor kernel driver | + +----------------------+ + -------------------------------------------------- + +----------------------+ + | HW Accelerators | + +----------------------+ + +UADK Installation +----------------- +Build UADK +^^^^^^^^^^ + +.. code-block:: shell + + git clone https://github.com/Linaro/uadk.git + cd uadk + mkdir build + ./autogen.sh + ./configure --prefix=$PWD/build + make + make install + +Without --prefix, UADK will be installed to /usr/local/lib by default. +If get error:"cannot find -lnuma", please install the libnuma-dev + +Run pkg-config libwd to ensure env is setup correctly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* export PKG_CONFIG_PATH=$PWD/build/lib/pkgconfig +* pkg-config libwd --cflags --libs + -I/usr/local/include -L/usr/local/lib -lwd + +* export PKG_CONFIG_PATH is required on demand. + Not required if UADK is installed to /usr/local/lib + +UADK Host Kernel Requirements +----------------------------- +User needs to make sure that ``UACCE`` is already supported in Linux kernel. +The kernel version should be at least v5.9 with SVA (Shared Virtual +Addressing) enabled. + +Kernel Configuration +^^^^^^^^^^^^^^^^^^^^ + +``UACCE`` could be built as module or built-in. + +Here's an example to enable UACCE with hardware accelerator in HiSilicon +Kunpeng platform. + +* CONFIG_IOMMU_SVA_LIB=y +* CONFIG_ARM_SMMU=y +* CONFIG_ARM_SMMU_V3=y +* CONFIG_ARM_SMMU_V3_SVA=y +* CONFIG_PCI_PASID=y +* CONFIG_UACCE=y +* CONFIG_CRYPTO_DEV_HISI_QM=y +* CONFIG_CRYPTO_DEV_HISI_ZIP=y + +Make sure all these above kernel configurations are selected. + +Accelerator dev node permissions +-------------------------------- +Harware accelerators(eg: HiSilicon Kunpeng Zip accelerator) gets registered to +UADK and char devices are created in dev directory. In order to access resources +on hardware accelerator devices, write permission should be provided to user. + +.. code-block:: shell + + $ sudo chmod 777 /dev/hisi_zip-* + +How To Use UADK Compression In QEMU Migration +--------------------------------------------- +* Make sure UADK is installed as above +* Build ``QEMU`` with ``--enable-uadk`` parameter + + E.g. configure --target-list=aarch64-softmmu --enable-kvm ``--enable-uadk`` + +* Enable ``UADK`` compression during migration + + Set ``migrate_set_parameter multifd-compression uadk`` + +Since UADK uses Shared Virtual Addressing(SVA) and device access virtual memory +directly it is possible that SMMUv3 may enounter page faults while walking the +IO page tables. This may impact the performance. In order to mitigate this, +please make sure to specify ``-mem-prealloc`` parameter to the destination VM +boot parameters. + +Though both UADK and ZLIB are based on the deflate compression algorithm, UADK +is not fully compatible with ZLIB. Hence, please make sure to use ``uadk`` on +both source and destination during migration. From cfc589a89b31930d9d658f4b0b6c4e6f33280e10 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:05 +0100 Subject: [PATCH 1380/2884] configure: Add uadk option Add --enable-uadk and --disable-uadk options to enable and disable UADK compression accelerator. This is for using UADK based hardware accelerators for live migration. Reviewed-by: Fabiano Rosas Signed-off-by: Shameer Kolothum Reviewed-by: Zhangfei Gao Signed-off-by: Fabiano Rosas --- meson.build | 14 ++++++++++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 3 files changed, 19 insertions(+) diff --git a/meson.build b/meson.build index c16574710c..97e00d6f59 100644 --- a/meson.build +++ b/meson.build @@ -1207,6 +1207,18 @@ if not get_option('qpl').auto() or have_system required: get_option('qpl'), method: 'pkg-config') endif +uadk = not_found +if not get_option('uadk').auto() or have_system + libwd = dependency('libwd', version: '>=2.6', + required: get_option('uadk'), + method: 'pkg-config') + libwd_comp = dependency('libwd_comp', version: '>=2.6', + required: get_option('uadk'), + method: 'pkg-config') + if libwd.found() and libwd_comp.found() + uadk = declare_dependency(dependencies: [libwd, libwd_comp]) + endif +endif virgl = not_found have_vhost_user_gpu = have_tools and host_os == 'linux' and pixman.found() @@ -2340,6 +2352,7 @@ config_host_data.set('CONFIG_STATX', has_statx) config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id) config_host_data.set('CONFIG_ZSTD', zstd.found()) config_host_data.set('CONFIG_QPL', qpl.found()) +config_host_data.set('CONFIG_UADK', uadk.found()) config_host_data.set('CONFIG_FUSE', fuse.found()) config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found()) config_host_data.set('CONFIG_SPICE_PROTOCOL', spice_protocol.found()) @@ -4454,6 +4467,7 @@ summary_info += {'bzip2 support': libbzip2} summary_info += {'lzfse support': liblzfse} summary_info += {'zstd support': zstd} summary_info += {'Query Processing Library support': qpl} +summary_info += {'UADK Library support': uadk} summary_info += {'NUMA host support': numa} summary_info += {'capstone': capstone} summary_info += {'libpmem support': libpmem} diff --git a/meson_options.txt b/meson_options.txt index dd680a5faf..7a79dd8970 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -261,6 +261,8 @@ option('zstd', type : 'feature', value : 'auto', description: 'zstd compression support') option('qpl', type : 'feature', value : 'auto', description: 'Query Processing Library support') +option('uadk', type : 'feature', value : 'auto', + description: 'UADK Library support') option('fuse', type: 'feature', value: 'auto', description: 'FUSE block device export') option('fuse_lseek', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 73ae8cedfc..58d49a447d 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -221,6 +221,7 @@ meson_options_help() { printf "%s\n" ' xkbcommon xkbcommon support' printf "%s\n" ' zstd zstd compression support' printf "%s\n" ' qpl Query Processing Library support' + printf "%s\n" ' uadk UADK Library support' } _meson_option_parse() { case $1 in @@ -561,6 +562,8 @@ _meson_option_parse() { --disable-zstd) printf "%s" -Dzstd=disabled ;; --enable-qpl) printf "%s" -Dqpl=enabled ;; --disable-qpl) printf "%s" -Dqpl=disabled ;; + --enable-uadk) printf "%s" -Duadk=enabled ;; + --disable-uadk) printf "%s" -Duadk=disabled ;; *) return 1 ;; esac } From f3d8bb759d13a2e33389f00fa338d0761309029a Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:06 +0100 Subject: [PATCH 1381/2884] migration/multifd: add uadk compression framework Adds the skeleton to support uadk compression method. Complete functionality will be added in subsequent patches. Acked-by: Markus Armbruster Reviewed-by: Fabiano Rosas Signed-off-by: Shameer Kolothum Reviewed-by: Zhangfei Gao Signed-off-by: Fabiano Rosas --- hw/core/qdev-properties-system.c | 2 +- migration/meson.build | 1 + migration/multifd-uadk.c | 20 ++++++++++++++++++++ migration/multifd.h | 5 +++-- qapi/migration.json | 5 ++++- 5 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 migration/multifd-uadk.c diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 6ccd7224f6..f13350b4fb 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -659,7 +659,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = { const PropertyInfo qdev_prop_multifd_compression = { .name = "MultiFDCompression", .description = "multifd_compression values, " - "none/zlib/zstd/qpl", + "none/zlib/zstd/qpl/uadk", .enum_table = &MultiFDCompression_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, diff --git a/migration/meson.build b/migration/meson.build index 5f146fe8a9..5ce2acb41e 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -40,6 +40,7 @@ endif system_ss.add(when: rdma, if_true: files('rdma.c')) system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) system_ss.add(when: qpl, if_true: files('multifd-qpl.c')) +system_ss.add(when: uadk, if_true: files('multifd-uadk.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('ram.c', diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c new file mode 100644 index 0000000000..c2bb07535b --- /dev/null +++ b/migration/multifd-uadk.c @@ -0,0 +1,20 @@ +/* + * Multifd UADK compression accelerator implementation + * + * Copyright (c) 2024 Huawei Technologies R & D (UK) Ltd + * + * Authors: + * Shameer Kolothum + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" + +static void multifd_uadk_register(void) +{ + /* noop for now */ +} +migration_init(multifd_uadk_register); diff --git a/migration/multifd.h b/migration/multifd.h index 5b7d9b15f8..0ecd6f47d7 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -34,13 +34,14 @@ MultiFDRecvData *multifd_get_recv_data(void); /* Multifd Compression flags */ #define MULTIFD_FLAG_SYNC (1 << 0) -/* We reserve 3 bits for compression methods */ -#define MULTIFD_FLAG_COMPRESSION_MASK (7 << 1) +/* We reserve 4 bits for compression methods */ +#define MULTIFD_FLAG_COMPRESSION_MASK (0xf << 1) /* we need to be compatible. Before compression value was 0 */ #define MULTIFD_FLAG_NOCOMP (0 << 1) #define MULTIFD_FLAG_ZLIB (1 << 1) #define MULTIFD_FLAG_ZSTD (2 << 1) #define MULTIFD_FLAG_QPL (4 << 1) +#define MULTIFD_FLAG_UADK (8 << 1) /* This value needs to be a multiple of qemu_target_page_size() */ #define MULTIFD_PACKET_SIZE (512 * 1024) diff --git a/qapi/migration.json b/qapi/migration.json index a35d0b5b83..470f746cc5 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -559,12 +559,15 @@ # In-Memory Analytics Accelerator(IAA) accelerated compression # and decompression. (Since 9.1) # +# @uadk: use UADK library compression method. (Since 9.1) +# # Since: 5.0 ## { 'enum': 'MultiFDCompression', 'data': [ 'none', 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' }, - { 'name': 'qpl', 'if': 'CONFIG_QPL' } ] } + { 'name': 'qpl', 'if': 'CONFIG_QPL' }, + { 'name': 'uadk', 'if': 'CONFIG_UADK' } ] } ## # @MigMode: From 819dd20636d51d5dc9d42aa28edb3dd9c1b8b863 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:07 +0100 Subject: [PATCH 1382/2884] migration/multifd: Add UADK initialization Initialize UADK session and allocate buffers required. The actual compression/decompression will only be done in a subsequent patch. Signed-off-by: Shameer Kolothum Reviewed-by: Fabiano Rosas Reviewed-by: Zhangfei Gao Signed-off-by: Fabiano Rosas --- migration/multifd-uadk.c | 209 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 208 insertions(+), 1 deletion(-) diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index c2bb07535b..535411a405 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -12,9 +12,216 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "qapi/error.h" +#include "migration.h" +#include "multifd.h" +#include "options.h" +#include "uadk/wd_comp.h" +#include "uadk/wd_sched.h" + +struct wd_data { + handle_t handle; + uint8_t *buf; + uint32_t *buf_hdr; +}; + +static bool uadk_hw_init(void) +{ + char alg[] = "zlib"; + int ret; + + ret = wd_comp_init2(alg, SCHED_POLICY_RR, TASK_HW); + if (ret && ret != -WD_EEXIST) { + return false; + } else { + return true; + } +} + +static struct wd_data *multifd_uadk_init_sess(uint32_t count, + uint32_t page_size, + bool compress, Error **errp) +{ + struct wd_comp_sess_setup ss = {0}; + struct sched_params param = {0}; + uint32_t size = count * page_size; + struct wd_data *wd; + + if (!uadk_hw_init()) { + error_setg(errp, "multifd: UADK hardware not available"); + return NULL; + } + + wd = g_new0(struct wd_data, 1); + ss.alg_type = WD_ZLIB; + if (compress) { + ss.op_type = WD_DIR_COMPRESS; + /* Add an additional page for handling output > input */ + size += page_size; + } else { + ss.op_type = WD_DIR_DECOMPRESS; + } + + /* We use default level 1 compression and 4K window size */ + param.type = ss.op_type; + ss.sched_param = ¶m; + + wd->handle = wd_comp_alloc_sess(&ss); + if (!wd->handle) { + error_setg(errp, "multifd: failed wd_comp_alloc_sess"); + goto out; + } + + wd->buf = g_try_malloc(size); + if (!wd->buf) { + error_setg(errp, "multifd: out of mem for uadk buf"); + goto out_free_sess; + } + wd->buf_hdr = g_new0(uint32_t, count); + return wd; + +out_free_sess: + wd_comp_free_sess(wd->handle); +out: + wd_comp_uninit2(); + g_free(wd); + return NULL; +} + +static void multifd_uadk_uninit_sess(struct wd_data *wd) +{ + wd_comp_free_sess(wd->handle); + wd_comp_uninit2(); + g_free(wd->buf); + g_free(wd->buf_hdr); + g_free(wd); +} + +/** + * multifd_uadk_send_setup: setup send side + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp) +{ + struct wd_data *wd; + + wd = multifd_uadk_init_sess(p->page_count, p->page_size, true, errp); + if (!wd) { + return -1; + } + + p->compress_data = wd; + assert(p->iov == NULL); + /* + * Each page will be compressed independently and sent using an IOV. The + * additional two IOVs are used to store packet header and compressed data + * length + */ + + p->iov = g_new0(struct iovec, p->page_count + 2); + return 0; +} + +/** + * multifd_uadk_send_cleanup: cleanup send side + * + * Close the channel and return memory. + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + struct wd_data *wd = p->compress_data; + + multifd_uadk_uninit_sess(wd); + p->compress_data = NULL; +} + +/** + * multifd_uadk_send_prepare: prepare data to be able to send + * + * Create a compressed buffer with all the pages that we are going to + * send. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) +{ + return -1; +} + +/** + * multifd_uadk_recv_setup: setup receive side + * + * Create the compressed channel and buffer. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + struct wd_data *wd; + + wd = multifd_uadk_init_sess(p->page_count, p->page_size, false, errp); + if (!wd) { + return -1; + } + p->compress_data = wd; + return 0; +} + +/** + * multifd_uadk_recv_cleanup: cleanup receive side + * + * Close the channel and return memory. + * + * @p: Params for the channel that we are using + */ +static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p) +{ + struct wd_data *wd = p->compress_data; + + multifd_uadk_uninit_sess(wd); + p->compress_data = NULL; +} + +/** + * multifd_uadk_recv: read the data from the channel into actual pages + * + * Read the compressed buffer, and uncompress it into the actual + * pages. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) +{ + return -1; +} + +static MultiFDMethods multifd_uadk_ops = { + .send_setup = multifd_uadk_send_setup, + .send_cleanup = multifd_uadk_send_cleanup, + .send_prepare = multifd_uadk_send_prepare, + .recv_setup = multifd_uadk_recv_setup, + .recv_cleanup = multifd_uadk_recv_cleanup, + .recv = multifd_uadk_recv, +}; static void multifd_uadk_register(void) { - /* noop for now */ + multifd_register_ops(MULTIFD_COMPRESSION_UADK, &multifd_uadk_ops); } migration_init(multifd_uadk_register); From 3c49191a0d011d941b347fda8fdadd88c988e753 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:08 +0100 Subject: [PATCH 1383/2884] migration/multifd: Add UADK based compression and decompression Uses UADK wd_do_comp_sync() API to (de)compress a normal page using hardware accelerator. Reviewed-by: Fabiano Rosas Signed-off-by: Shameer Kolothum Reviewed-by: Zhangfei Gao Signed-off-by: Fabiano Rosas --- migration/multifd-uadk.c | 132 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index 535411a405..70bba92eaa 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qapi/error.h" +#include "exec/ramblock.h" #include "migration.h" #include "multifd.h" #include "options.h" @@ -142,6 +143,15 @@ static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) p->compress_data = NULL; } +static inline void prepare_next_iov(MultiFDSendParams *p, void *base, + uint32_t len) +{ + p->iov[p->iovs_num].iov_base = (uint8_t *)base; + p->iov[p->iovs_num].iov_len = len; + p->next_packet_size += len; + p->iovs_num++; +} + /** * multifd_uadk_send_prepare: prepare data to be able to send * @@ -155,7 +165,56 @@ static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) */ static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) { - return -1; + struct wd_data *uadk_data = p->compress_data; + uint32_t hdr_size; + uint8_t *buf = uadk_data->buf; + int ret = 0; + + if (!multifd_send_prepare_common(p)) { + goto out; + } + + hdr_size = p->pages->normal_num * sizeof(uint32_t); + /* prepare the header that stores the lengths of all compressed data */ + prepare_next_iov(p, uadk_data->buf_hdr, hdr_size); + + for (int i = 0; i < p->pages->normal_num; i++) { + struct wd_comp_req creq = { + .op_type = WD_DIR_COMPRESS, + .src = p->pages->block->host + p->pages->offset[i], + .src_len = p->page_size, + .dst = buf, + /* Set dst_len to double the src in case compressed out >= page_size */ + .dst_len = p->page_size * 2, + }; + + ret = wd_do_comp_sync(uadk_data->handle, &creq); + if (ret || creq.status) { + error_setg(errp, "multifd %u: failed compression, ret %d status %d", + p->id, ret, creq.status); + return -1; + } + if (creq.dst_len < p->page_size) { + uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len); + prepare_next_iov(p, buf, creq.dst_len); + buf += creq.dst_len; + } else { + /* + * Send raw data if compressed out >= page_size. We might be better + * off sending raw data if output is slightly less than page_size + * as well because at the receive end we can skip the decompression. + * But it is tricky to find the right number here. + */ + uadk_data->buf_hdr[i] = cpu_to_be32(p->page_size); + prepare_next_iov(p, p->pages->block->host + p->pages->offset[i], + p->page_size); + buf += p->page_size; + } + } +out: + p->flags |= MULTIFD_FLAG_UADK; + multifd_send_fill_packet(p); + return 0; } /** @@ -208,7 +267,76 @@ static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p) */ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) { - return -1; + struct wd_data *uadk_data = p->compress_data; + uint32_t in_size = p->next_packet_size; + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + uint32_t hdr_len = p->normal_num * sizeof(uint32_t); + uint32_t data_len = 0; + uint8_t *buf = uadk_data->buf; + int ret = 0; + + if (flags != MULTIFD_FLAG_UADK) { + error_setg(errp, "multifd %u: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_ZLIB); + return -1; + } + + multifd_recv_zero_page_process(p); + if (!p->normal_num) { + assert(in_size == 0); + return 0; + } + + /* read compressed data lengths */ + assert(hdr_len < in_size); + ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr, + hdr_len, errp); + if (ret != 0) { + return ret; + } + + for (int i = 0; i < p->normal_num; i++) { + uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); + data_len += uadk_data->buf_hdr[i]; + assert(uadk_data->buf_hdr[i] <= p->page_size); + } + + /* read compressed data */ + assert(in_size == hdr_len + data_len); + ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp); + if (ret != 0) { + return ret; + } + + for (int i = 0; i < p->normal_num; i++) { + struct wd_comp_req creq = { + .op_type = WD_DIR_DECOMPRESS, + .src = buf, + .src_len = uadk_data->buf_hdr[i], + .dst = p->host + p->normal[i], + .dst_len = p->page_size, + }; + + if (uadk_data->buf_hdr[i] == p->page_size) { + memcpy(p->host + p->normal[i], buf, p->page_size); + buf += p->page_size; + continue; + } + + ret = wd_do_comp_sync(uadk_data->handle, &creq); + if (ret || creq.status) { + error_setg(errp, "multifd %u: failed decompression, ret %d status %d", + p->id, ret, creq.status); + return -1; + } + if (creq.dst_len != p->page_size) { + error_setg(errp, "multifd %u: decompressed length error", p->id); + return -1; + } + buf += uadk_data->buf_hdr[i]; + } + + return 0; } static MultiFDMethods multifd_uadk_ops = { From c1dfd12168e1be0a940e97f85044098e18d18178 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:09 +0100 Subject: [PATCH 1384/2884] migration/multifd: Switch to no compression when no hardware support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Send raw packets over if UADK hardware support is not available. This is to satisfy  Qemu qtest CI which may run on platforms that don't have UADK hardware support. Subsequent patch will add support for uadk migration qtest. Reviewed-by: Fabiano Rosas Signed-off-by: Shameer Kolothum Reviewed-by: Zhangfei Gao Signed-off-by: Fabiano Rosas --- migration/multifd-uadk.c | 92 +++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index 70bba92eaa..d12353fb21 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -17,6 +17,7 @@ #include "migration.h" #include "multifd.h" #include "options.h" +#include "qemu/error-report.h" #include "uadk/wd_comp.h" #include "uadk/wd_sched.h" @@ -48,29 +49,29 @@ static struct wd_data *multifd_uadk_init_sess(uint32_t count, uint32_t size = count * page_size; struct wd_data *wd; - if (!uadk_hw_init()) { - error_setg(errp, "multifd: UADK hardware not available"); - return NULL; - } - wd = g_new0(struct wd_data, 1); - ss.alg_type = WD_ZLIB; - if (compress) { - ss.op_type = WD_DIR_COMPRESS; - /* Add an additional page for handling output > input */ - size += page_size; + + if (uadk_hw_init()) { + ss.alg_type = WD_ZLIB; + if (compress) { + ss.op_type = WD_DIR_COMPRESS; + /* Add an additional page for handling output > input */ + size += page_size; + } else { + ss.op_type = WD_DIR_DECOMPRESS; + } + /* We use default level 1 compression and 4K window size */ + param.type = ss.op_type; + ss.sched_param = ¶m; + + wd->handle = wd_comp_alloc_sess(&ss); + if (!wd->handle) { + error_setg(errp, "multifd: failed wd_comp_alloc_sess"); + goto out; + } } else { - ss.op_type = WD_DIR_DECOMPRESS; - } - - /* We use default level 1 compression and 4K window size */ - param.type = ss.op_type; - ss.sched_param = ¶m; - - wd->handle = wd_comp_alloc_sess(&ss); - if (!wd->handle) { - error_setg(errp, "multifd: failed wd_comp_alloc_sess"); - goto out; + /* For CI test use */ + warn_report_once("UADK hardware not available. Switch to no compression mode"); } wd->buf = g_try_malloc(size); @@ -82,7 +83,9 @@ static struct wd_data *multifd_uadk_init_sess(uint32_t count, return wd; out_free_sess: - wd_comp_free_sess(wd->handle); + if (wd->handle) { + wd_comp_free_sess(wd->handle); + } out: wd_comp_uninit2(); g_free(wd); @@ -91,7 +94,9 @@ out: static void multifd_uadk_uninit_sess(struct wd_data *wd) { - wd_comp_free_sess(wd->handle); + if (wd->handle) { + wd_comp_free_sess(wd->handle); + } wd_comp_uninit2(); g_free(wd->buf); g_free(wd->buf_hdr); @@ -188,23 +193,26 @@ static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) .dst_len = p->page_size * 2, }; - ret = wd_do_comp_sync(uadk_data->handle, &creq); - if (ret || creq.status) { - error_setg(errp, "multifd %u: failed compression, ret %d status %d", - p->id, ret, creq.status); - return -1; + if (uadk_data->handle) { + ret = wd_do_comp_sync(uadk_data->handle, &creq); + if (ret || creq.status) { + error_setg(errp, "multifd %u: failed compression, ret %d status %d", + p->id, ret, creq.status); + return -1; + } + if (creq.dst_len < p->page_size) { + uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len); + prepare_next_iov(p, buf, creq.dst_len); + buf += creq.dst_len; + } } - if (creq.dst_len < p->page_size) { - uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len); - prepare_next_iov(p, buf, creq.dst_len); - buf += creq.dst_len; - } else { - /* - * Send raw data if compressed out >= page_size. We might be better - * off sending raw data if output is slightly less than page_size - * as well because at the receive end we can skip the decompression. - * But it is tricky to find the right number here. - */ + /* + * Send raw data if no UADK hardware or if compressed out >= page_size. + * We might be better off sending raw data if output is slightly less + * than page_size as well because at the receive end we can skip the + * decompression. But it is tricky to find the right number here. + */ + if (!uadk_data->handle || creq.dst_len >= p->page_size) { uadk_data->buf_hdr[i] = cpu_to_be32(p->page_size); prepare_next_iov(p, p->pages->block->host + p->pages->offset[i], p->page_size); @@ -323,6 +331,12 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) continue; } + if (unlikely(!uadk_data->handle)) { + error_setg(errp, "multifd %u: UADK HW not available for decompression", + p->id); + return -1; + } + ret = wd_do_comp_sync(uadk_data->handle, &creq); if (ret || creq.status) { error_setg(errp, "multifd %u: failed decompression, ret %d status %d", From c519caa825f5eba6e204bed5a464df167a5421d0 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 7 Jun 2024 14:53:10 +0100 Subject: [PATCH 1385/2884] tests/migration-test: add uadk compression test Reviewed-by: Fabiano Rosas Signed-off-by: Shameer Kolothum Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 887cd9fe42..0dccb4beff 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -2646,6 +2646,14 @@ test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from, return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl"); } #endif /* CONFIG_QPL */ +#ifdef CONFIG_UADK +static void * +test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from, + QTestState *to) +{ + return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk"); +} +#endif /* CONFIG_UADK */ static void test_multifd_tcp_uri_none(void) { @@ -2738,6 +2746,17 @@ static void test_multifd_tcp_qpl(void) } #endif +#ifdef CONFIG_UADK +static void test_multifd_tcp_uadk(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_uadk_start, + }; + test_precopy_common(&args); +} +#endif + #ifdef CONFIG_GNUTLS static void * test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, @@ -3617,6 +3636,10 @@ int main(int argc, char **argv) migration_test_add("/migration/multifd/tcp/plain/qpl", test_multifd_tcp_qpl); #endif +#ifdef CONFIG_UADK + migration_test_add("/migration/multifd/tcp/plain/uadk", + test_multifd_tcp_uadk); +#endif #ifdef CONFIG_GNUTLS migration_test_add("/migration/multifd/tcp/tls/psk/match", test_multifd_tcp_tls_psk_match); From ee48fef06c034ff245db9e553dcf0f1262f97bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 May 2024 16:12:12 +0200 Subject: [PATCH 1386/2884] aspeed/smc: Reintroduce "dram-base" property for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Aspeed SMC device model use to have a 'sdram_base' property. It was removed by commit d177892d4a48 ("aspeed/smc: Remove unused "sdram-base" property") because previous changes simplified the DMA transaction model to use an offset in RAM and not the physical address. The AST2700 SoC has larger address space (64-bit) and a new register DMA DRAM Side Address High Part (0x7C) is introduced to deal with the high bits of the DMA address. To be able to compute the offset of the DMA transaction, as done on the other SoCs, we will need to know where the DRAM is mapped in the address space. Re-introduce a "dram-base" property to hold this value. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Jamin Lin Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 1 + include/hw/ssi/aspeed_smc.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 6e1a84c197..7075bc9d61 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1220,6 +1220,7 @@ static const VMStateDescription vmstate_aspeed_smc = { static Property aspeed_smc_properties[] = { DEFINE_PROP_BOOL("inject-failure", AspeedSMCState, inject_failure, false), + DEFINE_PROP_UINT64("dram-base", AspeedSMCState, dram_base, 0), DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 8e1dda556b..8791cc0ecb 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -76,6 +76,7 @@ struct AspeedSMCState { AddressSpace flash_as; MemoryRegion *dram_mr; AddressSpace dram_as; + uint64_t dram_base; AspeedSMCFlash flashes[ASPEED_SMC_CS_MAX]; From 8db36a4f74ea97c2d7b5cd21515cd61c3749f39d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:22 +0800 Subject: [PATCH 1387/2884] aspeed/wdt: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 wdt controller is similiar to AST2600's wdt, but the AST2700 has 8 watchdogs, and they each have 0x80 of registers. Introduce ast2700 object class and increase the number of regs(offset) of ast2700 model. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/watchdog/wdt_aspeed.c | 24 ++++++++++++++++++++++++ include/hw/watchdog/wdt_aspeed.h | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index d70b656f8e..75685c5647 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -422,12 +422,36 @@ static const TypeInfo aspeed_1030_wdt_info = { .class_init = aspeed_1030_wdt_class_init, }; +static void aspeed_2700_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); + + dc->desc = "ASPEED 2700 Watchdog Controller"; + awc->iosize = 0x80; + awc->ext_pulse_width_mask = 0xfffff; /* TODO */ + awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; + awc->reset_pulse = aspeed_2500_wdt_reset_pulse; + awc->wdt_reload = aspeed_wdt_reload_1mhz; + awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; + awc->default_status = 0x014FB180; + awc->default_reload_value = 0x014FB180; +} + +static const TypeInfo aspeed_2700_wdt_info = { + .name = TYPE_ASPEED_2700_WDT, + .parent = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_2700_wdt_class_init, +}; + static void wdt_aspeed_register_types(void) { type_register_static(&aspeed_wdt_info); type_register_static(&aspeed_2400_wdt_info); type_register_static(&aspeed_2500_wdt_info); type_register_static(&aspeed_2600_wdt_info); + type_register_static(&aspeed_2700_wdt_info); type_register_static(&aspeed_1030_wdt_info); } diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h index e90ef86651..830b0a7936 100644 --- a/include/hw/watchdog/wdt_aspeed.h +++ b/include/hw/watchdog/wdt_aspeed.h @@ -19,9 +19,10 @@ OBJECT_DECLARE_TYPE(AspeedWDTState, AspeedWDTClass, ASPEED_WDT) #define TYPE_ASPEED_2400_WDT TYPE_ASPEED_WDT "-ast2400" #define TYPE_ASPEED_2500_WDT TYPE_ASPEED_WDT "-ast2500" #define TYPE_ASPEED_2600_WDT TYPE_ASPEED_WDT "-ast2600" +#define TYPE_ASPEED_2700_WDT TYPE_ASPEED_WDT "-ast2700" #define TYPE_ASPEED_1030_WDT TYPE_ASPEED_WDT "-ast1030" -#define ASPEED_WDT_REGS_MAX (0x30 / 4) +#define ASPEED_WDT_REGS_MAX (0x80 / 4) struct AspeedWDTState { /*< private >*/ From f944890dfd4222f091cea8680281e0bf7114f721 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:23 +0800 Subject: [PATCH 1388/2884] aspeed/sli: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 SLI engine is designed to accelerate the throughput between cross-die connections. It have CPU_SLI at CPU die and IO_SLI at IO die. Introduce dummy AST2700 SLI and SLIIO models. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/misc/aspeed_sli.c | 177 +++++++++++++++++++++++++++++++++++ hw/misc/meson.build | 3 +- hw/misc/trace-events | 7 ++ include/hw/misc/aspeed_sli.h | 27 ++++++ 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 hw/misc/aspeed_sli.c create mode 100644 include/hw/misc/aspeed_sli.h diff --git a/hw/misc/aspeed_sli.c b/hw/misc/aspeed_sli.c new file mode 100644 index 0000000000..fe720ead50 --- /dev/null +++ b/hw/misc/aspeed_sli.c @@ -0,0 +1,177 @@ +/* + * ASPEED SLI Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/qdev-properties.h" +#include "hw/misc/aspeed_sli.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define SLI_REGION_SIZE 0x500 +#define TO_REG(addr) ((addr) >> 2) + +static uint64_t aspeed_sli_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + trace_aspeed_sli_read(addr, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_sli_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + trace_aspeed_sli_write(addr, size, data); + s->regs[reg] = data; +} + +static uint64_t aspeed_sliio_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + trace_aspeed_sliio_read(addr, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_sliio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + trace_aspeed_sliio_write(addr, size, data); + s->regs[reg] = data; +} + +static const MemoryRegionOps aspeed_sli_ops = { + .read = aspeed_sli_read, + .write = aspeed_sli_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps aspeed_sliio_ops = { + .read = aspeed_sliio_read, + .write = aspeed_sliio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_sli_realize(DeviceState *dev, Error **errp) +{ + AspeedSLIState *s = ASPEED_SLI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sli_ops, s, + TYPE_ASPEED_SLI, SLI_REGION_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_sliio_realize(DeviceState *dev, Error **errp) +{ + AspeedSLIState *s = ASPEED_SLI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sliio_ops, s, + TYPE_ASPEED_SLI, SLI_REGION_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_sli_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Aspeed SLI Controller"; + dc->realize = aspeed_sli_realize; +} + +static const TypeInfo aspeed_sli_info = { + .name = TYPE_ASPEED_SLI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSLIState), + .class_init = aspeed_sli_class_init, + .abstract = true, +}; + +static void aspeed_2700_sli_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AST2700 SLI Controller"; +} + +static void aspeed_2700_sliio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AST2700 I/O SLI Controller"; + dc->realize = aspeed_sliio_realize; +} + +static const TypeInfo aspeed_2700_sli_info = { + .name = TYPE_ASPEED_2700_SLI, + .parent = TYPE_ASPEED_SLI, + .class_init = aspeed_2700_sli_class_init, +}; + +static const TypeInfo aspeed_2700_sliio_info = { + .name = TYPE_ASPEED_2700_SLIIO, + .parent = TYPE_ASPEED_SLI, + .class_init = aspeed_2700_sliio_class_init, +}; + +static void aspeed_sli_register_types(void) +{ + type_register_static(&aspeed_sli_info); + type_register_static(&aspeed_2700_sli_info); + type_register_static(&aspeed_2700_sliio_info); +} + +type_init(aspeed_sli_register_types); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 86596a3888..2ca8717be2 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -136,7 +136,8 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_sbc.c', 'aspeed_sdmc.c', 'aspeed_xdma.c', - 'aspeed_peci.c')) + 'aspeed_peci.c', + 'aspeed_sli.c')) system_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 5d241cb40a..e13b648221 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -351,3 +351,10 @@ djmemc_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRI # iosb.c iosb_read(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u" iosb_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u" + +# aspeed_sli.c +aspeed_sli_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_sli_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_sliio_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_sliio_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 + diff --git a/include/hw/misc/aspeed_sli.h b/include/hw/misc/aspeed_sli.h new file mode 100644 index 0000000000..23f346ab93 --- /dev/null +++ b/include/hw/misc/aspeed_sli.h @@ -0,0 +1,27 @@ +/* + * ASPEED SLI Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ASPEED_SLI_H +#define ASPEED_SLI_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_SLI "aspeed.sli" +#define TYPE_ASPEED_2700_SLI TYPE_ASPEED_SLI "-ast2700" +#define TYPE_ASPEED_2700_SLIIO TYPE_ASPEED_SLI "io" "-ast2700" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedSLIState, ASPEED_SLI) + +#define ASPEED_SLI_NR_REGS (0x500 >> 2) + +struct AspeedSLIState { + SysBusDevice parent; + MemoryRegion iomem; + + uint32_t regs[ASPEED_SLI_NR_REGS]; +}; + +#endif /* ASPEED_SLI_H */ From c7f3b1a042e7bdb105972185d1baf27901bd498e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:24 +0800 Subject: [PATCH 1389/2884] aspeed/sdmc: remove redundant macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These macros are no longer used for ASPEED SOCs, so removes them. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/misc/aspeed_sdmc.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 64cd1a81dc..74279bbe8e 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -76,10 +76,6 @@ #define ASPEED_SDMC_VGA_32MB 0x2 #define ASPEED_SDMC_VGA_64MB 0x3 #define ASPEED_SDMC_DRAM_SIZE(x) (x & 0x3) -#define ASPEED_SDMC_DRAM_64MB 0x0 -#define ASPEED_SDMC_DRAM_128MB 0x1 -#define ASPEED_SDMC_DRAM_256MB 0x2 -#define ASPEED_SDMC_DRAM_512MB 0x3 #define ASPEED_SDMC_READONLY_MASK \ (ASPEED_SDMC_RESERVED | ASPEED_SDMC_VGA_COMPAT | \ @@ -100,17 +96,6 @@ #define ASPEED_SDMC_CACHE_ENABLE (1 << 10) /* differs from AST2400 */ #define ASPEED_SDMC_DRAM_TYPE (1 << 4) /* differs from AST2400 */ -/* DRAM size definitions differs */ -#define ASPEED_SDMC_AST2500_128MB 0x0 -#define ASPEED_SDMC_AST2500_256MB 0x1 -#define ASPEED_SDMC_AST2500_512MB 0x2 -#define ASPEED_SDMC_AST2500_1024MB 0x3 - -#define ASPEED_SDMC_AST2600_256MB 0x0 -#define ASPEED_SDMC_AST2600_512MB 0x1 -#define ASPEED_SDMC_AST2600_1024MB 0x2 -#define ASPEED_SDMC_AST2600_2048MB 0x3 - #define ASPEED_SDMC_AST2500_READONLY_MASK \ (ASPEED_SDMC_HW_VERSION(0xf) | ASPEED_SDMC_CACHE_INITIAL_DONE | \ ASPEED_SDMC_AST2500_RESERVED | ASPEED_SDMC_VGA_COMPAT | \ From 39e6dc52a851e750e059965504eac71b727ab1ca Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:25 +0800 Subject: [PATCH 1390/2884] aspeed/sdmc: fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl Test command: scripts/checkpatch.pl --no-tree -f hw/misc/aspeed_sdmc.c Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/misc/aspeed_sdmc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 74279bbe8e..873d67c592 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -296,7 +296,8 @@ static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg, uint32_t data) { if (reg == R_PROT) { - s->regs[reg] = (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; + s->regs[reg] = + (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; return; } @@ -354,7 +355,8 @@ static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg, uint32_t data) { if (reg == R_PROT) { - s->regs[reg] = (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; + s->regs[reg] = + (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; return; } @@ -434,8 +436,9 @@ static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg, } if (s->regs[R_PROT] == PROT_HARDLOCKED) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: SDMC is locked until system reset!\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SDMC is locked until system reset!\n", + __func__); return; } From 3347b9a1f7f74513dad91a7c4bee74903e787bf9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:26 +0800 Subject: [PATCH 1391/2884] aspeed/sdmc: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDRAM memory controller(DRAMC) controls the access to external DDR4 and DDR5 SDRAM and power up to DDR4 and DDR5 PHY. The DRAM memory controller of AST2700 is not backward compatible to previous chips such AST2600, AST2500 and AST2400. Max memory is now 8GiB on the AST2700. Introduce new aspeed_2700_sdmc and class with read/write operation and reset handlers. Define DRAMC necessary protected registers and unprotected registers for AST2700 and increase the register set to 0x1000. Add unlocked property to change controller protected status. Incrementing the version of vmstate to 2. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/misc/aspeed_sdmc.c | 194 +++++++++++++++++++++++++++++++++- include/hw/misc/aspeed_sdmc.h | 5 +- 2 files changed, 195 insertions(+), 4 deletions(-) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 873d67c592..93e2e29ead 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -27,6 +27,7 @@ #define PROT_SOFTLOCKED 0x00 #define PROT_KEY_UNLOCK 0xFC600309 +#define PROT_2700_KEY_UNLOCK 0x1688A8A8 #define PROT_KEY_HARDLOCK 0xDEADDEAD /* AST2600 */ /* Configuration Register */ @@ -54,6 +55,46 @@ #define R_DRAM_TIME (0x8c / 4) #define R_ECC_ERR_INJECT (0xb4 / 4) +/* AST2700 Register */ +#define R_2700_PROT (0x00 / 4) +#define R_INT_STATUS (0x04 / 4) +#define R_INT_CLEAR (0x08 / 4) +#define R_INT_MASK (0x0c / 4) +#define R_MAIN_CONF (0x10 / 4) +#define R_MAIN_CONTROL (0x14 / 4) +#define R_MAIN_STATUS (0x18 / 4) +#define R_ERR_STATUS (0x1c / 4) +#define R_ECC_FAIL_STATUS (0x78 / 4) +#define R_ECC_FAIL_ADDR (0x7c / 4) +#define R_ECC_TESTING_CONTROL (0x80 / 4) +#define R_PROT_REGION_LOCK_STATUS (0x94 / 4) +#define R_TEST_FAIL_ADDR (0xd4 / 4) +#define R_TEST_FAIL_D0 (0xd8 / 4) +#define R_TEST_FAIL_D1 (0xdc / 4) +#define R_TEST_FAIL_D2 (0xe0 / 4) +#define R_TEST_FAIL_D3 (0xe4 / 4) +#define R_DBG_STATUS (0xf4 / 4) +#define R_PHY_INTERFACE_STATUS (0xf8 / 4) +#define R_GRAPHIC_MEM_BASE_ADDR (0x10c / 4) +#define R_PORT0_INTERFACE_MONITOR0 (0x240 / 4) +#define R_PORT0_INTERFACE_MONITOR1 (0x244 / 4) +#define R_PORT0_INTERFACE_MONITOR2 (0x248 / 4) +#define R_PORT1_INTERFACE_MONITOR0 (0x2c0 / 4) +#define R_PORT1_INTERFACE_MONITOR1 (0x2c4 / 4) +#define R_PORT1_INTERFACE_MONITOR2 (0x2c8 / 4) +#define R_PORT2_INTERFACE_MONITOR0 (0x340 / 4) +#define R_PORT2_INTERFACE_MONITOR1 (0x344 / 4) +#define R_PORT2_INTERFACE_MONITOR2 (0x348 / 4) +#define R_PORT3_INTERFACE_MONITOR0 (0x3c0 / 4) +#define R_PORT3_INTERFACE_MONITOR1 (0x3c4 / 4) +#define R_PORT3_INTERFACE_MONITOR2 (0x3c8 / 4) +#define R_PORT4_INTERFACE_MONITOR0 (0x440 / 4) +#define R_PORT4_INTERFACE_MONITOR1 (0x444 / 4) +#define R_PORT4_INTERFACE_MONITOR2 (0x448 / 4) +#define R_PORT5_INTERFACE_MONITOR0 (0x4c0 / 4) +#define R_PORT5_INTERFACE_MONITOR1 (0x4c4 / 4) +#define R_PORT5_INTERFACE_MONITOR2 (0x4c8 / 4) + /* * Configuration register Ox4 (for Aspeed AST2400 SOC) * @@ -101,6 +142,19 @@ ASPEED_SDMC_AST2500_RESERVED | ASPEED_SDMC_VGA_COMPAT | \ ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB)) +/* + * Main Configuration register Ox10 (for Aspeed AST2700 SOC and higher) + * + */ +#define ASPEED_SDMC_AST2700_RESERVED 0xFFFF2082 /* 31:16, 13, 7, 1 */ +#define ASPEED_SDMC_AST2700_DATA_SCRAMBLE (1 << 8) +#define ASPEED_SDMC_AST2700_ECC_ENABLE (1 << 6) +#define ASPEED_SDMC_AST2700_PAGE_MATCHING_ENABLE (1 << 5) +#define ASPEED_SDMC_AST2700_DRAM_SIZE(x) ((x & 0x7) << 2) + +#define ASPEED_SDMC_AST2700_READONLY_MASK \ + (ASPEED_SDMC_AST2700_RESERVED) + static uint64_t aspeed_sdmc_read(void *opaque, hwaddr addr, unsigned size) { AspeedSDMCState *s = ASPEED_SDMC(opaque); @@ -216,7 +270,7 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) AspeedSDMCState *s = ASPEED_SDMC(dev); AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); - assert(asc->max_ram_size < 4 * GiB); /* 32-bit address bus */ + assert(asc->max_ram_size < 4 * GiB || asc->is_bus64bit); s->max_ram_size = asc->max_ram_size; memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s, @@ -226,8 +280,8 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_sdmc = { .name = "aspeed.sdmc", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSDMCState, ASPEED_SDMC_NR_REGS), VMSTATE_END_OF_LIST() @@ -236,6 +290,7 @@ static const VMStateDescription vmstate_aspeed_sdmc = { static Property aspeed_sdmc_properties[] = { DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), + DEFINE_PROP_BOOL("unlocked", AspeedSDMCState, unlocked, false), DEFINE_PROP_END_OF_LIST(), }; @@ -500,12 +555,145 @@ static const TypeInfo aspeed_2600_sdmc_info = { .class_init = aspeed_2600_sdmc_class_init, }; +static void aspeed_2700_sdmc_reset(DeviceState *dev) +{ + AspeedSDMCState *s = ASPEED_SDMC(dev); + AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set ram size bit and defaults values */ + s->regs[R_MAIN_CONF] = asc->compute_conf(s, 0); + + if (s->unlocked) { + s->regs[R_2700_PROT] = PROT_UNLOCKED; + } +} + +static uint32_t aspeed_2700_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data) +{ + uint32_t fixed_conf = ASPEED_SDMC_AST2700_PAGE_MATCHING_ENABLE | + ASPEED_SDMC_AST2700_DRAM_SIZE(aspeed_sdmc_get_ram_bits(s)); + + /* Make sure readonly bits are kept */ + data &= ~ASPEED_SDMC_AST2700_READONLY_MASK; + + return data | fixed_conf; +} + +static void aspeed_2700_sdmc_write(AspeedSDMCState *s, uint32_t reg, + uint32_t data) +{ + /* Unprotected registers */ + switch (reg) { + case R_INT_STATUS: + case R_INT_CLEAR: + case R_INT_MASK: + case R_MAIN_STATUS: + case R_ERR_STATUS: + case R_ECC_FAIL_STATUS: + case R_ECC_FAIL_ADDR: + case R_PROT_REGION_LOCK_STATUS: + case R_TEST_FAIL_ADDR: + case R_TEST_FAIL_D0: + case R_TEST_FAIL_D1: + case R_TEST_FAIL_D2: + case R_TEST_FAIL_D3: + case R_DBG_STATUS: + case R_PHY_INTERFACE_STATUS: + case R_GRAPHIC_MEM_BASE_ADDR: + case R_PORT0_INTERFACE_MONITOR0: + case R_PORT0_INTERFACE_MONITOR1: + case R_PORT0_INTERFACE_MONITOR2: + case R_PORT1_INTERFACE_MONITOR0: + case R_PORT1_INTERFACE_MONITOR1: + case R_PORT1_INTERFACE_MONITOR2: + case R_PORT2_INTERFACE_MONITOR0: + case R_PORT2_INTERFACE_MONITOR1: + case R_PORT2_INTERFACE_MONITOR2: + case R_PORT3_INTERFACE_MONITOR0: + case R_PORT3_INTERFACE_MONITOR1: + case R_PORT3_INTERFACE_MONITOR2: + case R_PORT4_INTERFACE_MONITOR0: + case R_PORT4_INTERFACE_MONITOR1: + case R_PORT4_INTERFACE_MONITOR2: + case R_PORT5_INTERFACE_MONITOR0: + case R_PORT5_INTERFACE_MONITOR1: + case R_PORT5_INTERFACE_MONITOR2: + s->regs[reg] = data; + return; + } + + if (s->regs[R_2700_PROT] == PROT_HARDLOCKED) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SDMC is locked until system reset!\n", + __func__); + return; + } + + if (reg != R_2700_PROT && s->regs[R_2700_PROT] == PROT_SOFTLOCKED) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SDMC is locked! (write to MCR%02x blocked)\n", + __func__, reg * 4); + return; + } + + switch (reg) { + case R_2700_PROT: + if (data == PROT_2700_KEY_UNLOCK) { + data = PROT_UNLOCKED; + } else if (data == PROT_KEY_HARDLOCK) { + data = PROT_HARDLOCKED; + } else { + data = PROT_SOFTLOCKED; + } + break; + case R_MAIN_CONF: + data = aspeed_2700_sdmc_compute_conf(s, data); + break; + case R_MAIN_STATUS: + /* Will never return 'busy'. */ + data &= ~PHY_BUSY_STATE; + break; + default: + break; + } + + s->regs[reg] = data; +} + +static const uint64_t + aspeed_2700_ram_sizes[] = { 256 * MiB, 512 * MiB, 1024 * MiB, + 2048 * MiB, 4096 * MiB, 8192 * MiB, 0}; + +static void aspeed_2700_sdmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); + + dc->desc = "ASPEED 2700 SDRAM Memory Controller"; + dc->reset = aspeed_2700_sdmc_reset; + + asc->is_bus64bit = true; + asc->max_ram_size = 8 * GiB; + asc->compute_conf = aspeed_2700_sdmc_compute_conf; + asc->write = aspeed_2700_sdmc_write; + asc->valid_ram_sizes = aspeed_2700_ram_sizes; +} + +static const TypeInfo aspeed_2700_sdmc_info = { + .name = TYPE_ASPEED_2700_SDMC, + .parent = TYPE_ASPEED_SDMC, + .class_init = aspeed_2700_sdmc_class_init, +}; + static void aspeed_sdmc_register_types(void) { type_register_static(&aspeed_sdmc_info); type_register_static(&aspeed_2400_sdmc_info); type_register_static(&aspeed_2500_sdmc_info); type_register_static(&aspeed_2600_sdmc_info); + type_register_static(&aspeed_2700_sdmc_info); } type_init(aspeed_sdmc_register_types); diff --git a/include/hw/misc/aspeed_sdmc.h b/include/hw/misc/aspeed_sdmc.h index ec2d59a14f..61c979583a 100644 --- a/include/hw/misc/aspeed_sdmc.h +++ b/include/hw/misc/aspeed_sdmc.h @@ -17,6 +17,7 @@ OBJECT_DECLARE_TYPE(AspeedSDMCState, AspeedSDMCClass, ASPEED_SDMC) #define TYPE_ASPEED_2400_SDMC TYPE_ASPEED_SDMC "-ast2400" #define TYPE_ASPEED_2500_SDMC TYPE_ASPEED_SDMC "-ast2500" #define TYPE_ASPEED_2600_SDMC TYPE_ASPEED_SDMC "-ast2600" +#define TYPE_ASPEED_2700_SDMC TYPE_ASPEED_SDMC "-ast2700" /* * SDMC has 174 documented registers. In addition the u-boot device tree @@ -29,7 +30,7 @@ OBJECT_DECLARE_TYPE(AspeedSDMCState, AspeedSDMCClass, ASPEED_SDMC) * time, and the other is in the DDR-PHY IP which is used during DDR-PHY * training. */ -#define ASPEED_SDMC_NR_REGS (0x500 >> 2) +#define ASPEED_SDMC_NR_REGS (0x1000 >> 2) struct AspeedSDMCState { /*< private >*/ @@ -41,6 +42,7 @@ struct AspeedSDMCState { uint32_t regs[ASPEED_SDMC_NR_REGS]; uint64_t ram_size; uint64_t max_ram_size; + bool unlocked; }; @@ -51,6 +53,7 @@ struct AspeedSDMCClass { const uint64_t *valid_ram_sizes; uint32_t (*compute_conf)(AspeedSDMCState *s, uint32_t data); void (*write)(AspeedSDMCState *s, uint32_t reg, uint32_t data); + bool is_bus64bit; }; #endif /* ASPEED_SDMC_H */ From d108dfea1967d57d462a0d4af344524fcf9cd23e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:27 +0800 Subject: [PATCH 1392/2884] aspeed/smc: correct device description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 7075bc9d61..fe1cd96b80 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1449,7 +1449,7 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); - dc->desc = "Aspeed 2600 FMC Controller"; + dc->desc = "Aspeed 2500 FMC Controller"; asc->r_conf = R_CONF; asc->r_ce_ctrl = R_CE_CTRL; asc->r_ctrl0 = R_CTRL0; @@ -1487,7 +1487,7 @@ static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); - dc->desc = "Aspeed 2600 SPI1 Controller"; + dc->desc = "Aspeed 2500 SPI1 Controller"; asc->r_conf = R_CONF; asc->r_ce_ctrl = R_CE_CTRL; asc->r_ctrl0 = R_CTRL0; @@ -1522,7 +1522,7 @@ static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); - dc->desc = "Aspeed 2600 SPI2 Controller"; + dc->desc = "Aspeed 2500 SPI2 Controller"; asc->r_conf = R_CONF; asc->r_ce_ctrl = R_CE_CTRL; asc->r_ctrl0 = R_CTRL0; From 3a6c0f0e9d71e9a5f5bf4d5d31693a2f0cdd71c1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:28 +0800 Subject: [PATCH 1393/2884] aspeed/smc: support dma start length and 1 byte length unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DMA length is from 1 byte to 32MB for AST2600 and AST10x0 and DMA length is from 4 bytes to 32MB for AST2500. In other words, if "R_DMA_LEN" is 0, it should move at least 1 byte data for AST2600 and AST10x0 and 4 bytes data for AST2500. To support all ASPEED SOCs, adds dma_start_length parameter to store the start length, add helper routines function to compute the dma length and update DMA_LENGTH mask to "1FFFFFF" to support dma 1 byte length unit for AST2600 and AST1030. Currently, only supports dma length 4 bytes aligned. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 43 ++++++++++++++++++++++++++++++------- include/hw/ssi/aspeed_smc.h | 1 + 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index fe1cd96b80..0b8488a113 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -178,13 +178,17 @@ * DMA flash addresses should be 4 bytes aligned and the valid address * range is 0x20000000 - 0x2FFFFFFF. * - * DMA length is from 4 bytes to 32MB + * DMA length is from 4 bytes to 32MB (AST2500) * 0: 4 bytes - * 0x7FFFFF: 32M bytes + * 0x1FFFFFC: 32M bytes + * + * DMA length is from 1 byte to 32MB (AST2600, AST10x0) + * 0: 1 byte + * 0x1FFFFFF: 32M bytes */ #define DMA_DRAM_ADDR(asc, val) ((val) & (asc)->dma_dram_mask) #define DMA_FLASH_ADDR(asc, val) ((val) & (asc)->dma_flash_mask) -#define DMA_LENGTH(val) ((val) & 0x01FFFFFC) +#define DMA_LENGTH(val) ((val) & 0x01FFFFFF) /* Flash opcodes. */ #define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */ @@ -843,6 +847,13 @@ static bool aspeed_smc_inject_read_failure(AspeedSMCState *s) } } +static uint32_t aspeed_smc_dma_len(AspeedSMCState *s) +{ + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + return QEMU_ALIGN_UP(s->regs[R_DMA_LEN] + asc->dma_start_length, 4); +} + /* * Accumulate the result of the reads to provide a checksum that will * be used to validate the read timing settings. @@ -850,6 +861,7 @@ static bool aspeed_smc_inject_read_failure(AspeedSMCState *s) static void aspeed_smc_dma_checksum(AspeedSMCState *s) { MemTxResult result; + uint32_t dma_len; uint32_t data; if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { @@ -861,7 +873,9 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) aspeed_smc_dma_calibration(s); } - while (s->regs[R_DMA_LEN]) { + dma_len = aspeed_smc_dma_len(s); + + while (dma_len) { data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR], MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { @@ -877,7 +891,8 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) */ s->regs[R_DMA_CHECKSUM] += data; s->regs[R_DMA_FLASH_ADDR] += 4; - s->regs[R_DMA_LEN] -= 4; + dma_len -= 4; + s->regs[R_DMA_LEN] = dma_len; } if (s->inject_failure && aspeed_smc_inject_read_failure(s)) { @@ -889,14 +904,17 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) static void aspeed_smc_dma_rw(AspeedSMCState *s) { MemTxResult result; + uint32_t dma_len; uint32_t data; + dma_len = aspeed_smc_dma_len(s); + trace_aspeed_smc_dma_rw(s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE ? "write" : "read", s->regs[R_DMA_FLASH_ADDR], s->regs[R_DMA_DRAM_ADDR], - s->regs[R_DMA_LEN]); - while (s->regs[R_DMA_LEN]) { + dma_len); + while (dma_len) { if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { data = address_space_ldl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], MEMTXATTRS_UNSPECIFIED, &result); @@ -937,7 +955,8 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) */ s->regs[R_DMA_FLASH_ADDR] += 4; s->regs[R_DMA_DRAM_ADDR] += 4; - s->regs[R_DMA_LEN] -= 4; + dma_len -= 4; + s->regs[R_DMA_LEN] = dma_len; s->regs[R_DMA_CHECKSUM] += data; } } @@ -1382,6 +1401,7 @@ static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x1FFFFFFC; + asc->dma_start_length = 4; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; @@ -1465,6 +1485,7 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 4; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; @@ -1621,6 +1642,7 @@ static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) ASPEED_SMC_FEATURE_WDT_CONTROL; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; @@ -1659,6 +1681,7 @@ static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) ASPEED_SMC_FEATURE_DMA_GRANT; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; @@ -1698,6 +1721,7 @@ static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) ASPEED_SMC_FEATURE_DMA_GRANT; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; @@ -1779,6 +1803,7 @@ static void aspeed_1030_fmc_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_1030_smc_segment_to_reg; asc->reg_to_segment = aspeed_1030_smc_reg_to_segment; @@ -1816,6 +1841,7 @@ static void aspeed_1030_spi1_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; @@ -1852,6 +1878,7 @@ static void aspeed_1030_spi2_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 8791cc0ecb..d305ce2e2f 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -107,6 +107,7 @@ struct AspeedSMCClass { uint32_t features; hwaddr dma_flash_mask; hwaddr dma_dram_mask; + uint32_t dma_start_length; uint32_t nregs; uint32_t (*segment_to_reg)(const AspeedSMCState *s, const AspeedSegments *seg); From 6330be8da44cf11e429197187e814299eff881cd Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:29 +0800 Subject: [PATCH 1394/2884] aspeed/smc: support 64 bits dma dram address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 support the maximum dram size is 8GiB and has a "DMA DRAM Side Address High Part(0x7C)" register to support 64 bits dma dram address. Add helper routines functions to compute the dma dram address, new features and update trace-event to support 64 bits dram address. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 51 ++++++++++++++++++++++++++++++++++++++------- hw/ssi/trace-events | 2 +- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 0b8488a113..df0c63469c 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -132,6 +132,9 @@ #define FMC_WDT2_CTRL_BOOT_SOURCE BIT(4) /* O: primary 1: alternate */ #define FMC_WDT2_CTRL_EN BIT(0) +/* DMA DRAM Side Address High Part (AST2700) */ +#define R_DMA_DRAM_ADDR_HIGH (0x7c / 4) + /* DMA Control/Status Register */ #define R_DMA_CTRL (0x80 / 4) #define DMA_CTRL_REQUEST (1 << 31) @@ -187,6 +190,7 @@ * 0x1FFFFFF: 32M bytes */ #define DMA_DRAM_ADDR(asc, val) ((val) & (asc)->dma_dram_mask) +#define DMA_DRAM_ADDR_HIGH(val) ((val) & 0xf) #define DMA_FLASH_ADDR(asc, val) ((val) & (asc)->dma_flash_mask) #define DMA_LENGTH(val) ((val) & 0x01FFFFFF) @@ -207,6 +211,7 @@ static const AspeedSegments aspeed_2500_spi2_segments[]; #define ASPEED_SMC_FEATURE_DMA 0x1 #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 #define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4 +#define ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH 0x08 static inline bool aspeed_smc_has_dma(const AspeedSMCClass *asc) { @@ -218,6 +223,11 @@ static inline bool aspeed_smc_has_wdt_control(const AspeedSMCClass *asc) return !!(asc->features & ASPEED_SMC_FEATURE_WDT_CONTROL); } +static inline bool aspeed_smc_has_dma64(const AspeedSMCClass *asc) +{ + return !!(asc->features & ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH); +} + #define aspeed_smc_error(fmt, ...) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__) @@ -747,6 +757,8 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) (aspeed_smc_has_dma(asc) && addr == R_DMA_CTRL) || (aspeed_smc_has_dma(asc) && addr == R_DMA_FLASH_ADDR) || (aspeed_smc_has_dma(asc) && addr == R_DMA_DRAM_ADDR) || + (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) && + addr == R_DMA_DRAM_ADDR_HIGH) || (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN) || (aspeed_smc_has_dma(asc) && addr == R_DMA_CHECKSUM) || (addr >= R_SEG_ADDR0 && @@ -847,6 +859,12 @@ static bool aspeed_smc_inject_read_failure(AspeedSMCState *s) } } +static uint64_t aspeed_smc_dma_dram_addr(AspeedSMCState *s) +{ + return s->regs[R_DMA_DRAM_ADDR] | + ((uint64_t) s->regs[R_DMA_DRAM_ADDR_HIGH] << 32); +} + static uint32_t aspeed_smc_dma_len(AspeedSMCState *s) { AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); @@ -903,24 +921,34 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) static void aspeed_smc_dma_rw(AspeedSMCState *s) { + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + uint64_t dma_dram_offset; + uint64_t dma_dram_addr; MemTxResult result; uint32_t dma_len; uint32_t data; dma_len = aspeed_smc_dma_len(s); + dma_dram_addr = aspeed_smc_dma_dram_addr(s); + + if (aspeed_smc_has_dma64(asc)) { + dma_dram_offset = dma_dram_addr - s->dram_base; + } else { + dma_dram_offset = dma_dram_addr; + } trace_aspeed_smc_dma_rw(s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE ? "write" : "read", s->regs[R_DMA_FLASH_ADDR], - s->regs[R_DMA_DRAM_ADDR], + dma_dram_offset, dma_len); while (dma_len) { if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { - data = address_space_ldl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], + data = address_space_ldl_le(&s->dram_as, dma_dram_offset, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - aspeed_smc_error("DRAM read failed @%08x", - s->regs[R_DMA_DRAM_ADDR]); + aspeed_smc_error("DRAM read failed @%" PRIx64, + dma_dram_offset); return; } @@ -940,11 +968,11 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) return; } - address_space_stl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], + address_space_stl_le(&s->dram_as, dma_dram_offset, data, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - aspeed_smc_error("DRAM write failed @%08x", - s->regs[R_DMA_DRAM_ADDR]); + aspeed_smc_error("DRAM write failed @%" PRIx64, + dma_dram_offset); return; } } @@ -953,8 +981,12 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) * When the DMA is on-going, the DMA registers are updated * with the current working addresses and length. */ + dma_dram_offset += 4; + dma_dram_addr += 4; + + s->regs[R_DMA_DRAM_ADDR_HIGH] = dma_dram_addr >> 32; + s->regs[R_DMA_DRAM_ADDR] = dma_dram_addr & 0xffffffff; s->regs[R_DMA_FLASH_ADDR] += 4; - s->regs[R_DMA_DRAM_ADDR] += 4; dma_len -= 4; s->regs[R_DMA_LEN] = dma_len; s->regs[R_DMA_CHECKSUM] += data; @@ -1107,6 +1139,9 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, } else if (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN && aspeed_smc_dma_granted(s)) { s->regs[addr] = DMA_LENGTH(value); + } else if (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) && + addr == R_DMA_DRAM_ADDR_HIGH) { + s->regs[addr] = DMA_DRAM_ADDR_HIGH(value); } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index 2d5bd2b83d..7b5ad6a939 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -6,7 +6,7 @@ aspeed_smc_do_snoop(int cs, int index, int dummies, int data) "CS%d index:0x%x d aspeed_smc_flash_write(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" aspeed_smc_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x" -aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x" +aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint64_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%" PRIx64 " size:0x%08x" aspeed_smc_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect" From 0559e60669bae9c047cd5cf6f9be38ea7ced39b2 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:30 +0800 Subject: [PATCH 1395/2884] aspeed/smc: support different memory region ops for SMC flash region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It set "aspeed_smc_flash_ops" struct which containing read and write callbacks to be used when I/O is performed on the SMC flash region. And it set the valid max_access_size 4 by default for all ASPEED SMC models. However, the valid max_access_size 4 only support 32 bits CPUs. To support all ASPEED SMC model, introduce a new "const MemoryRegionOps *" attribute in AspeedSMCClass and use it in aspeed_smc_flash_realize function. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 14 +++++++++++++- include/hw/ssi/aspeed_smc.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index df0c63469c..129d06690d 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1316,7 +1316,7 @@ static void aspeed_smc_flash_realize(DeviceState *dev, Error **errp) * Use the default segment value to size the memory region. This * can be changed by FW at runtime. */ - memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_flash_ops, + memory_region_init_io(&s->mmio, OBJECT(s), s->asc->reg_ops, s, name, s->asc->segments[s->cs].size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); } @@ -1391,6 +1391,7 @@ static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2400_smc_info = { @@ -1441,6 +1442,7 @@ static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2400_fmc_info = { @@ -1480,6 +1482,7 @@ static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; asc->addr_width = aspeed_2400_spi1_addr_width; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2400_spi1_info = { @@ -1525,6 +1528,7 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2500_fmc_info = { @@ -1560,6 +1564,7 @@ static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2500_spi1_info = { @@ -1595,6 +1600,7 @@ static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2500_spi2_info = { @@ -1682,6 +1688,7 @@ static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2600_fmc_info = { @@ -1721,6 +1728,7 @@ static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2600_spi1_info = { @@ -1761,6 +1769,7 @@ static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2600_spi2_info = { @@ -1843,6 +1852,7 @@ static void aspeed_1030_fmc_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_1030_smc_segment_to_reg; asc->reg_to_segment = aspeed_1030_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_1030_fmc_info = { @@ -1881,6 +1891,7 @@ static void aspeed_1030_spi1_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_1030_spi1_info = { @@ -1918,6 +1929,7 @@ static void aspeed_1030_spi2_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_1030_spi2_info = { diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index d305ce2e2f..234dca32b0 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -115,6 +115,7 @@ struct AspeedSMCClass { AspeedSegments *seg); void (*dma_ctrl)(AspeedSMCState *s, uint32_t value); int (*addr_width)(const AspeedSMCState *s); + const MemoryRegionOps *reg_ops; }; #endif /* ASPEED_SMC_H */ From bdb3748dba309cba0bf71ca6ea4521911953b825 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:31 +0800 Subject: [PATCH 1396/2884] aspeed/smc: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 fmc/spi controller's address decoding unit is 64KB and only bits [31:16] are used for decoding. Introduce seg_to_reg and reg_to_seg handlers for ast2700 fmc/spi controller. In addition, adds ast2700 fmc, spi0, spi1, and spi2 class init handler. AST2700 is a 64 bits quad core CPUs(Cortex-a35). Introduce a new "aspeed_2700_smc_flash_ops" and set its valid "max_access_size" 8 for 64 bits data format access. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 234 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 233 insertions(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 129d06690d..49205ab76d 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -185,7 +185,7 @@ * 0: 4 bytes * 0x1FFFFFC: 32M bytes * - * DMA length is from 1 byte to 32MB (AST2600, AST10x0) + * DMA length is from 1 byte to 32MB (AST2600, AST10x0 and AST2700) * 0: 1 byte * 0x1FFFFFF: 32M bytes */ @@ -1938,6 +1938,234 @@ static const TypeInfo aspeed_1030_spi2_info = { .class_init = aspeed_1030_spi2_class_init, }; +/* + * The FMC Segment Registers of the AST2700 have a 64KB unit. + * Only bits [31:16] are used for decoding. + */ +#define AST2700_SEG_ADDR_MASK 0xffff0000 + +static uint32_t aspeed_2700_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg) +{ + uint32_t reg = 0; + + /* Disabled segments have a nil register */ + if (!seg->size) { + return 0; + } + + reg |= (seg->addr & AST2700_SEG_ADDR_MASK) >> 16; /* start offset */ + reg |= (seg->addr + seg->size - 1) & AST2700_SEG_ADDR_MASK; /* end offset */ + return reg; +} + +static void aspeed_2700_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg) +{ + uint32_t start_offset = (reg << 16) & AST2700_SEG_ADDR_MASK; + uint32_t end_offset = reg & AST2700_SEG_ADDR_MASK; + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + if (reg) { + seg->addr = asc->flash_window_base + start_offset; + seg->size = end_offset + (64 * KiB) - start_offset; + } else { + seg->addr = asc->flash_window_base; + seg->size = 0; + } +} + +static const uint32_t aspeed_2700_fmc_resets[ASPEED_SMC_R_MAX] = { + [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | + CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1), + [R_CE_CTRL] = 0x0000aa00, + [R_CTRL0] = 0x406b0641, + [R_CTRL1] = 0x00000400, + [R_CTRL2] = 0x00000400, + [R_CTRL3] = 0x00000400, + [R_SEG_ADDR0] = 0x08000000, + [R_SEG_ADDR1] = 0x10000800, + [R_SEG_ADDR2] = 0x00000000, + [R_SEG_ADDR3] = 0x00000000, + [R_DUMMY_DATA] = 0x00010000, + [R_DMA_DRAM_ADDR_HIGH] = 0x00000000, + [R_TIMINGS] = 0x007b0000, +}; + +static const MemoryRegionOps aspeed_2700_smc_flash_ops = { + .read = aspeed_smc_flash_read, + .write = aspeed_smc_flash_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static const AspeedSegments aspeed_2700_fmc_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 128 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ + { 256 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 FMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 3; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 3; + asc->segments = aspeed_2700_fmc_segments; + asc->segment_addr_mask = 0xffffffff; + asc->resets = aspeed_2700_fmc_resets; + asc->flash_window_base = 0x100000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x2FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_fmc_info = { + .name = "aspeed.fmc-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_fmc_class_init, +}; + +static const AspeedSegments aspeed_2700_spi0_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 128 * MiB, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_spi0_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 SPI0 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_2700_spi0_segments; + asc->segment_addr_mask = 0xffffffff; + asc->flash_window_base = 0x180000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x2FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_spi0_info = { + .name = "aspeed.spi0-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_spi0_class_init, +}; + +static const AspeedSegments aspeed_2700_spi1_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_spi1_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 SPI1 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_2700_spi1_segments; + asc->segment_addr_mask = 0xffffffff; + asc->flash_window_base = 0x200000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x2FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_spi1_info = { + .name = "aspeed.spi1-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_spi1_class_init, +}; + +static const AspeedSegments aspeed_2700_spi2_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_spi2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 SPI2 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_2700_spi2_segments; + asc->segment_addr_mask = 0xffffffff; + asc->flash_window_base = 0x280000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_spi2_info = { + .name = "aspeed.spi2-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_spi2_class_init, +}; + static void aspeed_smc_register_types(void) { type_register_static(&aspeed_smc_flash_info); @@ -1954,6 +2182,10 @@ static void aspeed_smc_register_types(void) type_register_static(&aspeed_1030_fmc_info); type_register_static(&aspeed_1030_spi1_info); type_register_static(&aspeed_1030_spi2_info); + type_register_static(&aspeed_2700_fmc_info); + type_register_static(&aspeed_2700_spi0_info); + type_register_static(&aspeed_2700_spi1_info); + type_register_static(&aspeed_2700_spi2_info); } type_init(aspeed_smc_register_types) From e7c8106d48b8f3719415c9fedbcf2d2b1897c608 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:32 +0800 Subject: [PATCH 1397/2884] aspeed/scu: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 have two SCU controllers which are SCU and SCUIO. Both SCU and SCUIO registers are not compatible previous SOCs , introduces new registers and adds ast2700 scu, sucio class init handler. The pclk divider selection of SCUIO is defined in SCUIO280[20:18] and the pclk divider selection of SCU is defined in SCU280[25:23]. Both of them are not compatible AST2600 SOCs, adds a get_apb_freq function and trace-event for AST2700 SCU and SCUIO. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater [clg: Fixed spelling : Unhandeled -> Unhandled ] --- hw/misc/aspeed_scu.c | 306 ++++++++++++++++++++++++++++++++++- hw/misc/trace-events | 4 + include/hw/misc/aspeed_scu.h | 47 +++++- 3 files changed, 351 insertions(+), 6 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 1ac04b6cb0..451e837272 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -134,6 +134,48 @@ #define AST2600_CLK TO_REG(0x40) +#define AST2700_SILICON_REV TO_REG(0x00) +#define AST2700_HW_STRAP1 TO_REG(0x10) +#define AST2700_HW_STRAP1_CLR TO_REG(0x14) +#define AST2700_HW_STRAP1_LOCK TO_REG(0x20) +#define AST2700_HW_STRAP1_SEC1 TO_REG(0x24) +#define AST2700_HW_STRAP1_SEC2 TO_REG(0x28) +#define AST2700_HW_STRAP1_SEC3 TO_REG(0x2C) + +#define AST2700_SCU_CLK_SEL_1 TO_REG(0x280) +#define AST2700_SCU_HPLL_PARAM TO_REG(0x300) +#define AST2700_SCU_HPLL_EXT_PARAM TO_REG(0x304) +#define AST2700_SCU_DPLL_PARAM TO_REG(0x308) +#define AST2700_SCU_DPLL_EXT_PARAM TO_REG(0x30c) +#define AST2700_SCU_MPLL_PARAM TO_REG(0x310) +#define AST2700_SCU_MPLL_EXT_PARAM TO_REG(0x314) +#define AST2700_SCU_D1CLK_PARAM TO_REG(0x320) +#define AST2700_SCU_D2CLK_PARAM TO_REG(0x330) +#define AST2700_SCU_CRT1CLK_PARAM TO_REG(0x340) +#define AST2700_SCU_CRT2CLK_PARAM TO_REG(0x350) +#define AST2700_SCU_MPHYCLK_PARAM TO_REG(0x360) +#define AST2700_SCU_FREQ_CNTR TO_REG(0x3b0) +#define AST2700_SCU_CPU_SCRATCH_0 TO_REG(0x780) +#define AST2700_SCU_CPU_SCRATCH_1 TO_REG(0x784) + +#define AST2700_SCUIO_CLK_STOP_CTL_1 TO_REG(0x240) +#define AST2700_SCUIO_CLK_STOP_CLR_1 TO_REG(0x244) +#define AST2700_SCUIO_CLK_STOP_CTL_2 TO_REG(0x260) +#define AST2700_SCUIO_CLK_STOP_CLR_2 TO_REG(0x264) +#define AST2700_SCUIO_CLK_SEL_1 TO_REG(0x280) +#define AST2700_SCUIO_CLK_SEL_2 TO_REG(0x284) +#define AST2700_SCUIO_HPLL_PARAM TO_REG(0x300) +#define AST2700_SCUIO_HPLL_EXT_PARAM TO_REG(0x304) +#define AST2700_SCUIO_APLL_PARAM TO_REG(0x310) +#define AST2700_SCUIO_APLL_EXT_PARAM TO_REG(0x314) +#define AST2700_SCUIO_DPLL_PARAM TO_REG(0x320) +#define AST2700_SCUIO_DPLL_EXT_PARAM TO_REG(0x324) +#define AST2700_SCUIO_DPLL_PARAM_READ TO_REG(0x328) +#define AST2700_SCUIO_DPLL_EXT_PARAM_READ TO_REG(0x32c) +#define AST2700_SCUIO_UARTCLK_GEN TO_REG(0x330) +#define AST2700_SCUIO_HUARTCLK_GEN TO_REG(0x334) +#define AST2700_SCUIO_CLK_DUTY_MEAS_RST TO_REG(0x388) + #define SCU_IO_REGION_SIZE 0x1000 static const uint32_t ast2400_a0_resets[ASPEED_SCU_NR_REGS] = { @@ -244,6 +286,25 @@ static uint32_t aspeed_1030_scu_get_apb_freq(AspeedSCUState *s) / asc->apb_divider; } +static uint32_t aspeed_2700_scu_get_apb_freq(AspeedSCUState *s) +{ + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); + uint32_t hpll = asc->calc_hpll(s, s->regs[AST2700_SCU_HPLL_PARAM]); + + return hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[AST2700_SCU_CLK_SEL_1]) + 1) + / asc->apb_divider; +} + +static uint32_t aspeed_2700_scuio_get_apb_freq(AspeedSCUState *s) +{ + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); + uint32_t hpll = asc->calc_hpll(s, s->regs[AST2700_SCUIO_HPLL_PARAM]); + + return hpll / + (SCUIO_AST2700_CLK_GET_PCLK_DIV(s->regs[AST2700_SCUIO_CLK_SEL_1]) + 1) + / asc->apb_divider; +} + static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); @@ -258,7 +319,8 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) switch (reg) { case RNG_DATA: - /* On hardware, RNG_DATA works regardless of + /* + * On hardware, RNG_DATA works regardless of * the state of the enable bit in RNG_CTRL */ s->regs[RNG_DATA] = aspeed_scu_get_random(); @@ -494,6 +556,9 @@ static uint32_t aspeed_silicon_revs[] = { AST2600_A3_SILICON_REV, AST1030_A0_SILICON_REV, AST1030_A1_SILICON_REV, + AST2700_A0_SILICON_REV, + AST2720_A0_SILICON_REV, + AST2750_A0_SILICON_REV, }; bool is_supported_silicon_rev(uint32_t silicon_rev) @@ -783,6 +848,243 @@ static const TypeInfo aspeed_2600_scu_info = { .class_init = aspeed_2600_scu_class_init, }; +static uint64_t aspeed_ast2700_scu_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_aspeed_ast2700_scu_read(offset, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_ast2700_scu_write(void *opaque, hwaddr offset, + uint64_t data64, unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + /* Truncate here so bitwise operations below behave as expected */ + uint32_t data = data64; + + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_aspeed_ast2700_scu_write(offset, size, data); + + switch (reg) { + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + s->regs[reg] = data; +} + +static const MemoryRegionOps aspeed_ast2700_scu_ops = { + .read = aspeed_ast2700_scu_read, + .write = aspeed_ast2700_scu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .valid.unaligned = false, +}; + +static const uint32_t ast2700_a0_resets[ASPEED_AST2700_SCU_NR_REGS] = { + [AST2700_SILICON_REV] = AST2700_A0_SILICON_REV, + [AST2700_HW_STRAP1] = 0x00000800, + [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, + [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, + [AST2700_HW_STRAP1_SEC1] = 0x000000FF, + [AST2700_HW_STRAP1_SEC2] = 0x00000000, + [AST2700_HW_STRAP1_SEC3] = 0x1000408F, + [AST2700_SCU_HPLL_PARAM] = 0x0000009f, + [AST2700_SCU_HPLL_EXT_PARAM] = 0x8000004f, + [AST2700_SCU_DPLL_PARAM] = 0x0080009f, + [AST2700_SCU_DPLL_EXT_PARAM] = 0x8000004f, + [AST2700_SCU_MPLL_PARAM] = 0x00000040, + [AST2700_SCU_MPLL_EXT_PARAM] = 0x80000000, + [AST2700_SCU_D1CLK_PARAM] = 0x00050002, + [AST2700_SCU_D2CLK_PARAM] = 0x00050002, + [AST2700_SCU_CRT1CLK_PARAM] = 0x00050002, + [AST2700_SCU_CRT2CLK_PARAM] = 0x00050002, + [AST2700_SCU_MPHYCLK_PARAM] = 0x0000004c, + [AST2700_SCU_FREQ_CNTR] = 0x000375eb, + [AST2700_SCU_CPU_SCRATCH_0] = 0x00000000, + [AST2700_SCU_CPU_SCRATCH_1] = 0x00000004, +}; + +static void aspeed_ast2700_scu_reset(DeviceState *dev) +{ + AspeedSCUState *s = ASPEED_SCU(dev); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); + + memcpy(s->regs, asc->resets, asc->nr_regs * 4); +} + +static void aspeed_2700_scu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); + + dc->desc = "ASPEED 2700 System Control Unit"; + dc->reset = aspeed_ast2700_scu_reset; + asc->resets = ast2700_a0_resets; + asc->calc_hpll = aspeed_2600_scu_calc_hpll; + asc->get_apb = aspeed_2700_scu_get_apb_freq; + asc->apb_divider = 4; + asc->nr_regs = ASPEED_AST2700_SCU_NR_REGS; + asc->clkin_25Mhz = true; + asc->ops = &aspeed_ast2700_scu_ops; +} + +static uint64_t aspeed_ast2700_scuio_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_aspeed_ast2700_scuio_read(offset, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_ast2700_scuio_write(void *opaque, hwaddr offset, + uint64_t data64, unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + /* Truncate here so bitwise operations below behave as expected */ + uint32_t data = data64; + bool updated = false; + + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_aspeed_ast2700_scuio_write(offset, size, data); + + switch (reg) { + case AST2700_SCUIO_CLK_STOP_CTL_1: + case AST2700_SCUIO_CLK_STOP_CTL_2: + s->regs[reg] |= data; + updated = true; + break; + case AST2700_SCUIO_CLK_STOP_CLR_1: + case AST2700_SCUIO_CLK_STOP_CLR_2: + s->regs[reg - 1] ^= data; + updated = true; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + if (!updated) { + s->regs[reg] = data; + } +} + +static const MemoryRegionOps aspeed_ast2700_scuio_ops = { + .read = aspeed_ast2700_scuio_read, + .write = aspeed_ast2700_scuio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .valid.unaligned = false, +}; + +static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { + [AST2700_SILICON_REV] = 0x06000003, + [AST2700_HW_STRAP1] = 0x00000504, + [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, + [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, + [AST2700_HW_STRAP1_SEC1] = 0x000000FF, + [AST2700_HW_STRAP1_SEC2] = 0x00000000, + [AST2700_HW_STRAP1_SEC3] = 0x1000408F, + [AST2700_SCUIO_CLK_STOP_CTL_1] = 0xffff8400, + [AST2700_SCUIO_CLK_STOP_CTL_2] = 0x00005f30, + [AST2700_SCUIO_CLK_SEL_1] = 0x86900000, + [AST2700_SCUIO_CLK_SEL_2] = 0x00400000, + [AST2700_SCUIO_HPLL_PARAM] = 0x10000027, + [AST2700_SCUIO_HPLL_EXT_PARAM] = 0x80000014, + [AST2700_SCUIO_APLL_PARAM] = 0x1000001f, + [AST2700_SCUIO_APLL_EXT_PARAM] = 0x8000000f, + [AST2700_SCUIO_DPLL_PARAM] = 0x106e42ce, + [AST2700_SCUIO_DPLL_EXT_PARAM] = 0x80000167, + [AST2700_SCUIO_DPLL_PARAM_READ] = 0x106e42ce, + [AST2700_SCUIO_DPLL_EXT_PARAM_READ] = 0x80000167, + [AST2700_SCUIO_UARTCLK_GEN] = 0x00014506, + [AST2700_SCUIO_HUARTCLK_GEN] = 0x000145c0, + [AST2700_SCUIO_CLK_DUTY_MEAS_RST] = 0x0c9100d2, +}; + +static void aspeed_2700_scuio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); + + dc->desc = "ASPEED 2700 System Control Unit I/O"; + dc->reset = aspeed_ast2700_scu_reset; + asc->resets = ast2700_a0_resets_io; + asc->calc_hpll = aspeed_2600_scu_calc_hpll; + asc->get_apb = aspeed_2700_scuio_get_apb_freq; + asc->apb_divider = 2; + asc->nr_regs = ASPEED_AST2700_SCU_NR_REGS; + asc->clkin_25Mhz = true; + asc->ops = &aspeed_ast2700_scuio_ops; +} + +static const TypeInfo aspeed_2700_scu_info = { + .name = TYPE_ASPEED_2700_SCU, + .parent = TYPE_ASPEED_SCU, + .instance_size = sizeof(AspeedSCUState), + .class_init = aspeed_2700_scu_class_init, +}; + +static const TypeInfo aspeed_2700_scuio_info = { + .name = TYPE_ASPEED_2700_SCUIO, + .parent = TYPE_ASPEED_SCU, + .instance_size = sizeof(AspeedSCUState), + .class_init = aspeed_2700_scuio_class_init, +}; + static const uint32_t ast1030_a1_resets[ASPEED_AST2600_SCU_NR_REGS] = { [AST2600_SYS_RST_CTRL] = 0xFFC3FED8, [AST2600_SYS_RST_CTRL2] = 0x09FFFFFC, @@ -841,6 +1143,8 @@ static void aspeed_scu_register_types(void) type_register_static(&aspeed_2500_scu_info); type_register_static(&aspeed_2600_scu_info); type_register_static(&aspeed_1030_scu_info); + type_register_static(&aspeed_2700_scu_info); + type_register_static(&aspeed_2700_scuio_info); } type_init(aspeed_scu_register_types); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index e13b648221..1be0717c0c 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -93,6 +93,10 @@ slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x" # aspeed_scu.c aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scuio_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scuio_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 # mps2-scc.c mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 7cb6018dbc..58db28db45 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -19,10 +19,13 @@ OBJECT_DECLARE_TYPE(AspeedSCUState, AspeedSCUClass, ASPEED_SCU) #define TYPE_ASPEED_2400_SCU TYPE_ASPEED_SCU "-ast2400" #define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500" #define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600" +#define TYPE_ASPEED_2700_SCU TYPE_ASPEED_SCU "-ast2700" +#define TYPE_ASPEED_2700_SCUIO TYPE_ASPEED_SCU "io" "-ast2700" #define TYPE_ASPEED_1030_SCU TYPE_ASPEED_SCU "-ast1030" #define ASPEED_SCU_NR_REGS (0x1A8 >> 2) #define ASPEED_AST2600_SCU_NR_REGS (0xE20 >> 2) +#define ASPEED_AST2700_SCU_NR_REGS (0xE20 >> 2) struct AspeedSCUState { /*< private >*/ @@ -31,7 +34,7 @@ struct AspeedSCUState { /*< public >*/ MemoryRegion iomem; - uint32_t regs[ASPEED_AST2600_SCU_NR_REGS]; + uint32_t regs[ASPEED_AST2700_SCU_NR_REGS]; uint32_t silicon_rev; uint32_t hw_strap1; uint32_t hw_strap2; @@ -48,6 +51,9 @@ struct AspeedSCUState { #define AST2600_A3_SILICON_REV 0x05030303U #define AST1030_A0_SILICON_REV 0x80000000U #define AST1030_A1_SILICON_REV 0x80010000U +#define AST2700_A0_SILICON_REV 0x06000103U +#define AST2720_A0_SILICON_REV 0x06000203U +#define AST2750_A0_SILICON_REV 0x06000003U #define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) @@ -87,7 +93,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); * 1. 2012/12/29 Ryan Chen Create */ -/* SCU08 Clock Selection Register +/* + * SCU08 Clock Selection Register * * 31 Enable Video Engine clock dynamic slow down * 30:28 Video Engine clock slow down setting @@ -109,7 +116,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); */ #define SCU_CLK_GET_PCLK_DIV(x) (((x) >> 23) & 0x7) -/* SCU24 H-PLL Parameter Register (for Aspeed AST2400 SOC) +/* + * SCU24 H-PLL Parameter Register (for Aspeed AST2400 SOC) * * 18 H-PLL parameter selection * 0: Select H-PLL by strapping resistors @@ -127,7 +135,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); #define SCU_AST2400_H_PLL_BYPASS_EN (0x1 << 17) #define SCU_AST2400_H_PLL_OFF (0x1 << 16) -/* SCU24 H-PLL Parameter Register (for Aspeed AST2500 SOC) +/* + * SCU24 H-PLL Parameter Register (for Aspeed AST2500 SOC) * * 21 Enable H-PLL reset * 20 Enable H-PLL bypass mode @@ -144,7 +153,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); #define SCU_H_PLL_BYPASS_EN (0x1 << 20) #define SCU_H_PLL_OFF (0x1 << 19) -/* SCU70 Hardware Strapping Register definition (for Aspeed AST2400 SOC) +/* + * SCU70 Hardware Strapping Register definition (for Aspeed AST2400 SOC) * * 31:29 Software defined strapping registers * 28:27 DRAM size setting (for VGA driver use) @@ -361,4 +371,31 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); */ #define SCU_AST1030_CLK_GET_PCLK_DIV(x) (((x) >> 8) & 0xf) +/* + * SCU280 Clock Selection 1 Register (for Aspeed AST2700 SCUIO) + * + * 31:29 MHCLK_DIV + * 28 Reserved + * 27:25 RGMIICLK_DIV + * 24 Reserved + * 23:21 RMIICLK_DIV + * 20:18 PCLK_DIV + * 17:14 SDCLK_DIV + * 13 SDCLK_SEL + * 12 UART13CLK_SEL + * 11 UART12CLK_SEL + * 10 UART11CLK_SEL + * 9 UART10CLK_SEL + * 8 UART9CLK_SEL + * 7 UART8CLK_SEL + * 6 UART7CLK_SEL + * 5 UART6CLK_SEL + * 4 UARTDBCLK_SEL + * 3 UART4CLK_SEL + * 2 UART3CLK_SEL + * 1 UART2CLK_SEL + * 0 UART1CLK_SEL + */ +#define SCUIO_AST2700_CLK_GET_PCLK_DIV(x) (((x) >> 18) & 0x7) + #endif /* ASPEED_SCU_H */ From d831c5fd868225882c5297b34a241929c267be13 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:33 +0800 Subject: [PATCH 1398/2884] aspeed/intc: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 interrupt controller(INTC) provides hardware interrupt interfaces to interrupt of processors PSP, SSP and TSP. In INTC, each interrupt of INT 128 to INT136 combines 32 interrupts. Introduce a new aspeed_intc class with instance_init and realize handlers. So far, this model only supports GICINT128 to GICINT136. It creates 9 GICINT or-gates to connect 32 interrupts sources from GICINT128 to GICINT136 as IRQ GPIO-OUTPUT pins. Then, this model registers IRQ handler with its IRQ GPIO-INPUT pins which connect to GICINT or-gates. And creates 9 GICINT IRQ GPIO-OUTPUT pins which connect to GIC device with GIC IRQ 128 to 136. If one interrupt source from GICINT128 to GICINT136 set irq, the OR-GATE irq callback function is called and set irq to INTC by OR-GATE GPIO-OUTPUT pins. Then, the INTC irq callback function is called and set irq to GIC by its GICINT IRQ GPIO-OUTPUT pins. Finally, the GIC irq callback function is called and set irq to CPUs and CPUs execute Interrupt Service Routine (ISR). Block diagram of GICINT132: GICINT132 ETH1 +-----------+ +-------->+0 3| ETH2 | 4| +-------->+1 5| ETH3 | 6| +-------->+2 19| INTC GIC UART0 | 20| +--------------------------+ +-------->+7 21| | | +--------------+ UART1 | 22| |orgate0 +----> output_pin0+----------->+GIC128 | +-------->+8 23| | | | | UART2 | 24| |orgate1 +----> output_pin1+----------->+GIC129 | +-------->+9 25| | | | | UART3 | 26| |orgate2 +----> output_pin2+----------->+GIC130 | +--------->10 27| | | | | UART5 | 28| |orgate3 +----> output_pin3+----------->+GIC131 | +-------->+11 29| | | | | UART6 | +----------->+orgate4 +----> output_pin4+----------->+GIC132 | +-------->+12 30| | | | | UART7 | 31| |orgate5 +----> output_pin5+----------->+GIC133 | +-------->+13 | | | | | UART8 | OR[0:31] | |orgate6 +----> output_pin6+----------->+GIC134 | ---------->14 | | | | | UART9 | | |orgate7 +----> output_pin7+----------->+GIC135 | --------->+15 | | | | | UART10 | | |orgate8 +----> output_pin8+----------->+GIC136 | --------->+16 | | | +--------------+ UART11 | | +--------------------------+ +-------->+17 | UART12 | | +--------->18 | | | | | | | +-----------+ Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater [clg: Fixed class_size in TYPE_ASPEED_INTC definition ] --- hw/intc/aspeed_intc.c | 361 ++++++++++++++++++++++++++++++++++ hw/intc/meson.build | 1 + hw/intc/trace-events | 13 ++ include/hw/intc/aspeed_intc.h | 44 +++++ 4 files changed, 419 insertions(+) create mode 100644 hw/intc/aspeed_intc.c create mode 100644 include/hw/intc/aspeed_intc.h diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c new file mode 100644 index 0000000000..7515558bab --- /dev/null +++ b/hw/intc/aspeed_intc.c @@ -0,0 +1,361 @@ +/* + * ASPEED INTC Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/intc/aspeed_intc.h" +#include "hw/irq.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/registerfields.h" +#include "qapi/error.h" + +/* INTC Registers */ +REG32(GICINT128_EN, 0x1000) +REG32(GICINT128_STATUS, 0x1004) +REG32(GICINT129_EN, 0x1100) +REG32(GICINT129_STATUS, 0x1104) +REG32(GICINT130_EN, 0x1200) +REG32(GICINT130_STATUS, 0x1204) +REG32(GICINT131_EN, 0x1300) +REG32(GICINT131_STATUS, 0x1304) +REG32(GICINT132_EN, 0x1400) +REG32(GICINT132_STATUS, 0x1404) +REG32(GICINT133_EN, 0x1500) +REG32(GICINT133_STATUS, 0x1504) +REG32(GICINT134_EN, 0x1600) +REG32(GICINT134_STATUS, 0x1604) +REG32(GICINT135_EN, 0x1700) +REG32(GICINT135_STATUS, 0x1704) +REG32(GICINT136_EN, 0x1800) +REG32(GICINT136_STATUS, 0x1804) + +#define GICINT_STATUS_BASE R_GICINT128_STATUS + +static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + trace_aspeed_intc_update_irq(irq, level); + qemu_set_irq(s->output_pins[irq], level); +} + +/* + * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. + * Utilize "address & 0x0f00" to get the irq and irq output pin index + * The value of irq should be 0 to num_ints. + * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. + */ +static void aspeed_intc_set_irq(void *opaque, int irq, int level) +{ + AspeedINTCState *s = (AspeedINTCState *)opaque; + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t status_addr = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); + uint32_t select = 0; + uint32_t enable; + int i; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + trace_aspeed_intc_set_irq(irq, level); + enable = s->enable[irq]; + + if (!level) { + return; + } + + for (i = 0; i < aic->num_lines; i++) { + if (s->orgates[irq].levels[i]) { + if (enable & BIT(i)) { + select |= BIT(i); + } + } + } + + if (!select) { + return; + } + + trace_aspeed_intc_select(select); + + if (s->mask[irq] || s->regs[status_addr]) { + /* + * a. mask is not 0 means in ISR mode + * sources interrupt routine are executing. + * b. status register value is not 0 means previous + * source interrupt does not be executed, yet. + * + * save source interrupt to pending variable. + */ + s->pending[irq] |= select; + trace_aspeed_intc_pending_irq(irq, s->pending[irq]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status register + */ + s->regs[status_addr] = select; + trace_aspeed_intc_trigger_irq(irq, s->regs[status_addr]); + aspeed_intc_update(s, irq, 1); + } +} + +static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + uint32_t addr = offset >> 2; + uint32_t value = 0; + + if (addr >= ASPEED_INTC_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + value = s->regs[addr]; + trace_aspeed_intc_read(offset, size, value); + + return value; +} + +static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t addr = offset >> 2; + uint32_t old_enable; + uint32_t change; + uint32_t irq; + + if (addr >= ASPEED_INTC_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_aspeed_intc_write(offset, size, data); + + switch (addr) { + case R_GICINT128_EN: + case R_GICINT129_EN: + case R_GICINT130_EN: + case R_GICINT131_EN: + case R_GICINT132_EN: + case R_GICINT133_EN: + case R_GICINT134_EN: + case R_GICINT135_EN: + case R_GICINT136_EN: + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* + * These registers are used for enable sources interrupt and + * mask and unmask source interrupt while executing source ISR. + */ + + /* disable all source interrupt */ + if (!data && !s->enable[irq]) { + s->regs[addr] = data; + return; + } + + old_enable = s->enable[irq]; + s->enable[irq] |= data; + + /* enable new source interrupt */ + if (old_enable != s->enable[irq]) { + trace_aspeed_intc_enable(s->enable[irq]); + s->regs[addr] = data; + return; + } + + /* mask and unmask source interrupt */ + change = s->regs[addr] ^ data; + if (change & data) { + s->mask[irq] &= ~change; + trace_aspeed_intc_unmask(change, s->mask[irq]); + } else { + s->mask[irq] |= change; + trace_aspeed_intc_mask(change, s->mask[irq]); + } + s->regs[addr] = data; + break; + case R_GICINT128_STATUS: + case R_GICINT129_STATUS: + case R_GICINT130_STATUS: + case R_GICINT131_STATUS: + case R_GICINT132_STATUS: + case R_GICINT133_STATUS: + case R_GICINT134_STATUS: + case R_GICINT135_STATUS: + case R_GICINT136_STATUS: + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* clear status */ + s->regs[addr] &= ~data; + + /* + * These status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + if (data == 0xffffffff) { + return; + } + + /* All source ISR execution are done */ + if (!s->regs[addr]) { + trace_aspeed_intc_all_isr_done(irq); + if (s->pending[irq]) { + /* + * handle pending source interrupt + * notify firmware which source interrupt are pending + * by setting status register + */ + s->regs[addr] = s->pending[irq]; + s->pending[irq] = 0; + trace_aspeed_intc_trigger_irq(irq, s->regs[addr]); + aspeed_intc_update(s, irq, 1); + } else { + /* clear irq */ + trace_aspeed_intc_clear_irq(irq, 0); + aspeed_intc_update(s, irq, 0); + } + } + break; + default: + s->regs[addr] = data; + break; + } + + return; +} + +static const MemoryRegionOps aspeed_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void aspeed_intc_instance_init(Object *obj) +{ + AspeedINTCState *s = ASPEED_INTC(obj); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + int i; + + assert(aic->num_ints <= ASPEED_INTC_NR_INTS); + for (i = 0; i < aic->num_ints; i++) { + object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i], + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->orgates[i]), "num-lines", + aic->num_lines, &error_abort); + } +} + +static void aspeed_intc_reset(DeviceState *dev) +{ + AspeedINTCState *s = ASPEED_INTC(dev); + + memset(s->regs, 0, sizeof(s->regs)); + memset(s->enable, 0, sizeof(s->enable)); + memset(s->mask, 0, sizeof(s->mask)); + memset(s->pending, 0, sizeof(s->pending)); +} + +static void aspeed_intc_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedINTCState *s = ASPEED_INTC(dev); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + int i; + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, + TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); + + sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); + + for (i = 0; i < aic->num_ints; i++) { + if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { + return; + } + sysbus_init_irq(sbd, &s->output_pins[i]); + } +} + +static void aspeed_intc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "ASPEED INTC Controller"; + dc->realize = aspeed_intc_realize; + dc->reset = aspeed_intc_reset; + dc->vmsd = NULL; +} + +static const TypeInfo aspeed_intc_info = { + .name = TYPE_ASPEED_INTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = aspeed_intc_instance_init, + .instance_size = sizeof(AspeedINTCState), + .class_init = aspeed_intc_class_init, + .class_size = sizeof(AspeedINTCClass), + .abstract = true, +}; + +static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 INTC Controller"; + aic->num_lines = 32; + aic->num_ints = 9; +} + +static const TypeInfo aspeed_2700_intc_info = { + .name = TYPE_ASPEED_2700_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700_intc_class_init, +}; + +static void aspeed_intc_register_types(void) +{ + type_register_static(&aspeed_intc_info); + type_register_static(&aspeed_2700_intc_info); +} + +type_init(aspeed_intc_register_types); diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 0d1b7d0a43..afd1aa51ee 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -14,6 +14,7 @@ system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( )) system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_intc.c')) system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_gic.c', 'exynos4210_combiner.c')) system_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index b815cea129..3dcf147198 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -79,6 +79,19 @@ aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" aspeed_vic_update_irq(int flags) "Raising IRQ: %d" aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +# aspeed_intc.c +aspeed_intc_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_set_irq(int irq, int level) "Set IRQ %d: %d" +aspeed_intc_clear_irq(int irq, int level) "Clear IRQ %d: %d" +aspeed_intc_update_irq(int irq, int level) "Update IRQ: %d: %d" +aspeed_intc_pending_irq(int irq, uint32_t value) "Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(int irq, uint32_t value) "Trigger IRQ: %d: 0x%x" +aspeed_intc_all_isr_done(int irq) "All source ISR execution are done: %d" +aspeed_intc_enable(uint32_t value) "Enable: 0x%x" +aspeed_intc_select(uint32_t value) "Select: 0x%x" +aspeed_intc_mask(uint32_t change, uint32_t value) "Mask: 0x%x: 0x%x" +aspeed_intc_unmask(uint32_t change, uint32_t value) "UnMask: 0x%x: 0x%x" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h new file mode 100644 index 0000000000..18cb43476c --- /dev/null +++ b/include/hw/intc/aspeed_intc.h @@ -0,0 +1,44 @@ +/* + * ASPEED INTC Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ASPEED_INTC_H +#define ASPEED_INTC_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "hw/or-irq.h" + +#define TYPE_ASPEED_INTC "aspeed.intc" +#define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" +OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) + +#define ASPEED_INTC_NR_REGS (0x2000 >> 2) +#define ASPEED_INTC_NR_INTS 9 + +struct AspeedINTCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + uint32_t regs[ASPEED_INTC_NR_REGS]; + OrIRQState orgates[ASPEED_INTC_NR_INTS]; + qemu_irq output_pins[ASPEED_INTC_NR_INTS]; + + uint32_t enable[ASPEED_INTC_NR_INTS]; + uint32_t mask[ASPEED_INTC_NR_INTS]; + uint32_t pending[ASPEED_INTC_NR_INTS]; +}; + +struct AspeedINTCClass { + SysBusDeviceClass parent_class; + + uint32_t num_lines; + uint32_t num_ints; +}; + +#endif /* ASPEED_INTC_H */ From 5dd883ab0635c9f715c77cc32622e458a0724581 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:34 +0800 Subject: [PATCH 1399/2884] aspeed/soc: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial definitions for a simple machine using an AST2700 SOC (Cortex-a35 CPU). AST2700 SOC and its interrupt controller are too complex to handle in the common Aspeed SoC framework. We introduce a new ast2700 class with instance_init and realize handlers. AST2700 is a 64 bits quad core cpus and support 8 watchdog. Update maximum ASPEED_CPUS_NUM to 4 and ASPEED_WDTS_NUM to 8. In addition, update AspeedSocState to support scuio, sli, sliio and intc. Add TYPE_ASPEED27X0_SOC machine type. The SDMC controller is unlocked at SPL stage. At present, only supports to emulate booting start from u-boot stage. Set SDMC controller unlocked by default. In INTC, each interrupt of INT 128 to INT 136 combines 32 interrupts. It connect GICINT IRQ GPIO-OUTPUT pins to GIC device with irq 128 to 136. And, if a device irq is 128 to 136, its irq GPIO-OUTPUT pin is connected to GICINT or-gates instead of GIC device. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 563 ++++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 1 + include/hw/arm/aspeed_soc.h | 28 +- 3 files changed, 590 insertions(+), 2 deletions(-) create mode 100644 hw/arm/aspeed_ast27x0.c diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c new file mode 100644 index 0000000000..29e75072c4 --- /dev/null +++ b/hw/arm/aspeed_ast27x0.c @@ -0,0 +1,563 @@ +/* + * ASPEED SoC 27x0 family + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * Implementation extracted from the AST2600 and adapted for AST27x0. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "hw/i2c/aspeed_i2c.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "hw/intc/arm_gicv3.h" +#include "qapi/qmp/qlist.h" + +static const hwaddr aspeed_soc_ast2700_memmap[] = { + [ASPEED_DEV_SPI_BOOT] = 0x400000000, + [ASPEED_DEV_SRAM] = 0x10000000, + [ASPEED_DEV_SDMC] = 0x12C00000, + [ASPEED_DEV_SCU] = 0x12C02000, + [ASPEED_DEV_SCUIO] = 0x14C02000, + [ASPEED_DEV_UART0] = 0X14C33000, + [ASPEED_DEV_UART1] = 0X14C33100, + [ASPEED_DEV_UART2] = 0X14C33200, + [ASPEED_DEV_UART3] = 0X14C33300, + [ASPEED_DEV_UART4] = 0X12C1A000, + [ASPEED_DEV_UART5] = 0X14C33400, + [ASPEED_DEV_UART6] = 0X14C33500, + [ASPEED_DEV_UART7] = 0X14C33600, + [ASPEED_DEV_UART8] = 0X14C33700, + [ASPEED_DEV_UART9] = 0X14C33800, + [ASPEED_DEV_UART10] = 0X14C33900, + [ASPEED_DEV_UART11] = 0X14C33A00, + [ASPEED_DEV_UART12] = 0X14C33B00, + [ASPEED_DEV_WDT] = 0x14C37000, + [ASPEED_DEV_VUART] = 0X14C30000, + [ASPEED_DEV_FMC] = 0x14000000, + [ASPEED_DEV_SPI0] = 0x14010000, + [ASPEED_DEV_SPI1] = 0x14020000, + [ASPEED_DEV_SPI2] = 0x14030000, + [ASPEED_DEV_SDRAM] = 0x400000000, + [ASPEED_DEV_MII1] = 0x14040000, + [ASPEED_DEV_MII2] = 0x14040008, + [ASPEED_DEV_MII3] = 0x14040010, + [ASPEED_DEV_ETH1] = 0x14050000, + [ASPEED_DEV_ETH2] = 0x14060000, + [ASPEED_DEV_ETH3] = 0x14070000, + [ASPEED_DEV_EMMC] = 0x12090000, + [ASPEED_DEV_INTC] = 0x12100000, + [ASPEED_DEV_SLI] = 0x12C17000, + [ASPEED_DEV_SLIIO] = 0x14C1E000, + [ASPEED_GIC_DIST] = 0x12200000, + [ASPEED_GIC_REDIST] = 0x12280000, +}; + +#define AST2700_MAX_IRQ 288 + +/* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ +static const int aspeed_soc_ast2700_irqmap[] = { + [ASPEED_DEV_UART0] = 132, + [ASPEED_DEV_UART1] = 132, + [ASPEED_DEV_UART2] = 132, + [ASPEED_DEV_UART3] = 132, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_UART5] = 132, + [ASPEED_DEV_UART6] = 132, + [ASPEED_DEV_UART7] = 132, + [ASPEED_DEV_UART8] = 132, + [ASPEED_DEV_UART9] = 132, + [ASPEED_DEV_UART10] = 132, + [ASPEED_DEV_UART11] = 132, + [ASPEED_DEV_UART12] = 132, + [ASPEED_DEV_FMC] = 131, + [ASPEED_DEV_SDMC] = 0, + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_ADC] = 130, + [ASPEED_DEV_XDMA] = 5, + [ASPEED_DEV_EMMC] = 15, + [ASPEED_DEV_GPIO] = 11, + [ASPEED_DEV_GPIO_1_8V] = 130, + [ASPEED_DEV_RTC] = 13, + [ASPEED_DEV_TIMER1] = 16, + [ASPEED_DEV_TIMER2] = 17, + [ASPEED_DEV_TIMER3] = 18, + [ASPEED_DEV_TIMER4] = 19, + [ASPEED_DEV_TIMER5] = 20, + [ASPEED_DEV_TIMER6] = 21, + [ASPEED_DEV_TIMER7] = 22, + [ASPEED_DEV_TIMER8] = 23, + [ASPEED_DEV_WDT] = 131, + [ASPEED_DEV_PWM] = 131, + [ASPEED_DEV_LPC] = 128, + [ASPEED_DEV_IBT] = 128, + [ASPEED_DEV_I2C] = 130, + [ASPEED_DEV_PECI] = 133, + [ASPEED_DEV_ETH1] = 132, + [ASPEED_DEV_ETH2] = 132, + [ASPEED_DEV_ETH3] = 132, + [ASPEED_DEV_HACE] = 4, + [ASPEED_DEV_KCS] = 128, + [ASPEED_DEV_DP] = 28, + [ASPEED_DEV_I3C] = 131, +}; + +/* GICINT 128 */ +static const int aspeed_soc_ast2700_gic128_intcmap[] = { + [ASPEED_DEV_LPC] = 0, + [ASPEED_DEV_IBT] = 2, + [ASPEED_DEV_KCS] = 4, +}; + +/* GICINT 130 */ +static const int aspeed_soc_ast2700_gic130_intcmap[] = { + [ASPEED_DEV_I2C] = 0, + [ASPEED_DEV_ADC] = 16, + [ASPEED_DEV_GPIO_1_8V] = 18, +}; + +/* GICINT 131 */ +static const int aspeed_soc_ast2700_gic131_intcmap[] = { + [ASPEED_DEV_I3C] = 0, + [ASPEED_DEV_WDT] = 16, + [ASPEED_DEV_FMC] = 25, + [ASPEED_DEV_PWM] = 29, +}; + +/* GICINT 132 */ +static const int aspeed_soc_ast2700_gic132_intcmap[] = { + [ASPEED_DEV_ETH1] = 0, + [ASPEED_DEV_ETH2] = 1, + [ASPEED_DEV_ETH3] = 2, + [ASPEED_DEV_UART0] = 7, + [ASPEED_DEV_UART1] = 8, + [ASPEED_DEV_UART2] = 9, + [ASPEED_DEV_UART3] = 10, + [ASPEED_DEV_UART5] = 11, + [ASPEED_DEV_UART6] = 12, + [ASPEED_DEV_UART7] = 13, + [ASPEED_DEV_UART8] = 14, + [ASPEED_DEV_UART9] = 15, + [ASPEED_DEV_UART10] = 16, + [ASPEED_DEV_UART11] = 17, + [ASPEED_DEV_UART12] = 18, +}; + +/* GICINT 133 */ +static const int aspeed_soc_ast2700_gic133_intcmap[] = { + [ASPEED_DEV_PECI] = 4, +}; + +/* GICINT 128 ~ 136 */ +struct gic_intc_irq_info { + int irq; + const int *ptr; +}; + +static const struct gic_intc_irq_info aspeed_soc_ast2700_gic_intcmap[] = { + {128, aspeed_soc_ast2700_gic128_intcmap}, + {129, NULL}, + {130, aspeed_soc_ast2700_gic130_intcmap}, + {131, aspeed_soc_ast2700_gic131_intcmap}, + {132, aspeed_soc_ast2700_gic132_intcmap}, + {133, aspeed_soc_ast2700_gic133_intcmap}, + {134, NULL}, + {135, NULL}, + {136, NULL}, +}; + +static qemu_irq aspeed_soc_ast2700_get_irq(AspeedSoCState *s, int dev) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + for (i = 0; i < ARRAY_SIZE(aspeed_soc_ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == aspeed_soc_ast2700_gic_intcmap[i].irq) { + assert(aspeed_soc_ast2700_gic_intcmap[i].ptr); + return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), + aspeed_soc_ast2700_gic_intcmap[i].ptr[dev]); + } + } + + return qdev_get_gpio_in(DEVICE(&a->gic), sc->irqmap[dev]); +} + +static void aspeed_soc_ast2700_init(Object *obj) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(obj); + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + char socname[8]; + char typename[64]; + + if (sscanf(sc->name, "%7s", socname) != 1) { + g_assert_not_reached(); + } + + for (i = 0; i < sc->num_cpus; i++) { + object_initialize_child(obj, "cpu[*]", &a->cpu[i], + aspeed_soc_cpu_type(sc)); + } + + object_initialize_child(obj, "gic", &a->gic, gicv3_class_name()); + + object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", + sc->silicon_rev); + object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), + "hw-strap1"); + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), + "hw-strap2"); + object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), + "hw-prot-key"); + + object_initialize_child(obj, "scuio", &s->scuio, TYPE_ASPEED_2700_SCUIO); + qdev_prop_set_uint32(DEVICE(&s->scuio), "silicon-rev", + sc->silicon_rev); + + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); + object_initialize_child(obj, "fmc", &s->fmc, typename); + + for (i = 0; i < sc->spis_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i, socname); + object_initialize_child(obj, "spi[*]", &s->spi[i], typename); + } + + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); + object_initialize_child(obj, "sdmc", &s->sdmc, typename); + object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), + "ram-size"); + + for (i = 0; i < sc->wdts_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); + object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); + } + + for (i = 0; i < sc->macs_num; i++) { + object_initialize_child(obj, "ftgmac100[*]", &s->ftgmac100[i], + TYPE_FTGMAC100); + + object_initialize_child(obj, "mii[*]", &s->mii[i], TYPE_ASPEED_MII); + } + + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + + object_initialize_child(obj, "sli", &s->sli, TYPE_ASPEED_2700_SLI); + object_initialize_child(obj, "sliio", &s->sliio, TYPE_ASPEED_2700_SLIIO); + object_initialize_child(obj, "intc", &a->intc, TYPE_ASPEED_2700_INTC); +} + +/* + * ASPEED ast2700 has 0x0 as cluster ID + * + * https://developer.arm.com/documentation/100236/0100/register-descriptions/aarch64-system-registers/multiprocessor-affinity-register--el1 + */ +static uint64_t aspeed_calc_affinity(int cpu) +{ + return (0x0 << ARM_AFF1_SHIFT) | cpu; +} + +static bool aspeed_soc_ast2700_gic_realize(DeviceState *dev, Error **errp) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + SysBusDevice *gicbusdev; + DeviceState *gicdev; + QList *redist_region_count; + int i; + + gicbusdev = SYS_BUS_DEVICE(&a->gic); + gicdev = DEVICE(&a->gic); + qdev_prop_set_uint32(gicdev, "revision", 3); + qdev_prop_set_uint32(gicdev, "num-cpu", sc->num_cpus); + qdev_prop_set_uint32(gicdev, "num-irq", AST2700_MAX_IRQ); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, sc->num_cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + + if (!sysbus_realize(gicbusdev, errp)) { + return false; + } + sysbus_mmio_map(gicbusdev, 0, sc->memmap[ASPEED_GIC_DIST]); + sysbus_mmio_map(gicbusdev, 1, sc->memmap[ASPEED_GIC_REDIST]); + + for (i = 0; i < sc->num_cpus; i++) { + DeviceState *cpudev = DEVICE(&a->cpu[i]); + int NUM_IRQS = 256, ARCH_GIC_MAINT_IRQ = 9, VIRTUAL_PMU_IRQ = 7; + int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + + const int timer_irq[] = { + [GTIMER_PHYS] = 14, + [GTIMER_VIRT] = 11, + [GTIMER_HYP] = 10, + [GTIMER_SEC] = 13, + }; + int j; + + for (j = 0; j < ARRAY_SIZE(timer_irq); j++) { + qdev_connect_gpio_out(cpudev, j, + qdev_get_gpio_in(gicdev, ppibase + timer_irq[j])); + } + + qemu_irq irq = qdev_get_gpio_in(gicdev, + ppibase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(gicdev, ppibase + VIRTUAL_PMU_IRQ)); + + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + } + + return true; +} + +static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) +{ + int i; + Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc); + g_autofree char *sram_name = NULL; + + /* Default boot region (SPI memory or ROMs) */ + memory_region_init(&s->spi_boot_container, OBJECT(s), + "aspeed.spi_boot_container", 0x400000000); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SPI_BOOT], + &s->spi_boot_container); + + /* CPU */ + for (i = 0; i < sc->num_cpus; i++) { + object_property_set_int(OBJECT(&a->cpu[i]), "mp-affinity", + aspeed_calc_affinity(i), &error_abort); + + object_property_set_int(OBJECT(&a->cpu[i]), "cntfrq", 1125000000, + &error_abort); + object_property_set_link(OBJECT(&a->cpu[i]), "memory", + OBJECT(s->memory), &error_abort); + + if (!qdev_realize(DEVICE(&a->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + if (!aspeed_soc_ast2700_gic_realize(dev, errp)) { + return; + } + + /* INTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* GICINT orgates -> INTC -> GIC */ + for (i = 0; i < ic->num_ints; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc.orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc), i)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc), i, + qdev_get_gpio_in(DEVICE(&a->gic), + aspeed_soc_ast2700_gic_intcmap[i].irq)); + } + + /* SRAM */ + sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, + errp)) { + return; + } + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SRAM], &s->sram); + + /* SCU */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* SCU1 */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scuio), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scuio), 0, + sc->memmap[ASPEED_DEV_SCUIO]); + + /* UART */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + /* FMC, The number of CS is set at the board level */ + object_property_set_int(OBJECT(&s->fmc), "dram-base", + sc->memmap[ASPEED_DEV_SDRAM], + &error_abort); + object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, + ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + + /* Set up an alias on the FMC CE0 region (boot default) */ + MemoryRegion *fmc0_mmio = &s->fmc.flashes[0].mmio; + memory_region_init_alias(&s->spi_boot, OBJECT(s), "aspeed.spi_boot", + fmc0_mmio, 0, memory_region_size(fmc0_mmio)); + memory_region_add_subregion(&s->spi_boot_container, 0x0, &s->spi_boot); + + /* SPI */ + for (i = 0; i < sc->spis_num; i++) { + object_property_set_link(OBJECT(&s->spi[i]), "dram", + OBJECT(s->dram_mr), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + sc->memmap[ASPEED_DEV_SPI0 + i]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); + } + + /* + * SDMC - SDRAM Memory Controller + * The SDMC controller is unlocked at SPL stage. + * At present, only supports to emulate booting + * start from u-boot stage. Set SDMC controller + * unlocked by default. It is a temporarily solution. + */ + object_property_set_bool(OBJECT(&s->sdmc), "unlocked", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + sc->memmap[ASPEED_DEV_SDMC]); + + /* RAM */ + if (!aspeed_soc_dram_init(s, errp)) { + return; + } + + for (i = 0; i < sc->macs_num; i++) { + object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + sc->memmap[ASPEED_DEV_ETH1 + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); + + object_property_set_link(OBJECT(&s->mii[i]), "nic", + OBJECT(&s->ftgmac100[i]), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->mii[i]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->mii[i]), 0, + sc->memmap[ASPEED_DEV_MII1 + i]); + } + + /* Watch dog */ + for (i = 0; i < sc->wdts_num; i++) { + AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + hwaddr wdt_offset = sc->memmap[ASPEED_DEV_WDT] + i * awc->iosize; + + object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + } + + /* SLI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sli), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sli), 0, sc->memmap[ASPEED_DEV_SLI]); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sliio), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sliio), 0, + sc->memmap[ASPEED_DEV_SLIIO]); + + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); + create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); + create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); + create_unimplemented_device("ast2700.ltpi", 0x30000000, 0x1000000); + create_unimplemented_device("ast2700.io", 0x0, 0x4000000); +} + +static void aspeed_soc_ast2700_class_init(ObjectClass *oc, void *data) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a35"), + NULL + }; + DeviceClass *dc = DEVICE_CLASS(oc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; + dc->realize = aspeed_soc_ast2700_realize; + + sc->name = "ast2700-a0"; + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A0_SILICON_REV; + sc->sram_size = 0x20000; + sc->spis_num = 3; + sc->wdts_num = 8; + sc->macs_num = 1; + sc->uarts_num = 13; + sc->num_cpus = 4; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast2700_irqmap; + sc->memmap = aspeed_soc_ast2700_memmap; + sc->get_irq = aspeed_soc_ast2700_get_irq; +} + +static const TypeInfo aspeed_soc_ast27x0_types[] = { + { + .name = TYPE_ASPEED27X0_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed27x0SoCState), + .abstract = true, + }, { + .name = "ast2700-a0", + .parent = TYPE_ASPEED27X0_SOC, + .instance_init = aspeed_soc_ast2700_init, + .class_init = aspeed_soc_ast2700_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index aefde0c69a..0c07ab522f 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -49,6 +49,7 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) +arm_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files('aspeed_ast27x0.c')) arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index c60fac900a..caef0d100b 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -15,6 +15,7 @@ #include "hw/cpu/a15mpcore.h" #include "hw/arm/armv7m.h" #include "hw/intc/aspeed_vic.h" +#include "hw/intc/aspeed_intc.h" #include "hw/misc/aspeed_scu.h" #include "hw/adc/aspeed_adc.h" #include "hw/misc/aspeed_sdmc.h" @@ -26,6 +27,7 @@ #include "hw/ssi/aspeed_smc.h" #include "hw/misc/aspeed_hace.h" #include "hw/misc/aspeed_sbc.h" +#include "hw/misc/aspeed_sli.h" #include "hw/watchdog/wdt_aspeed.h" #include "hw/net/ftgmac100.h" #include "target/arm/cpu.h" @@ -38,11 +40,12 @@ #include "hw/misc/aspeed_peci.h" #include "hw/fsi/aspeed_apb2opb.h" #include "hw/char/serial.h" +#include "hw/intc/arm_gicv3.h" #define ASPEED_SPIS_NUM 2 #define ASPEED_EHCIS_NUM 2 -#define ASPEED_WDTS_NUM 4 -#define ASPEED_CPUS_NUM 2 +#define ASPEED_WDTS_NUM 8 +#define ASPEED_CPUS_NUM 4 #define ASPEED_MACS_NUM 4 #define ASPEED_UARTS_NUM 13 #define ASPEED_JTAG_NUM 2 @@ -61,6 +64,7 @@ struct AspeedSoCState { AspeedI2CState i2c; AspeedI3CState i3c; AspeedSCUState scu; + AspeedSCUState scuio; AspeedHACEState hace; AspeedXDMAState xdma; AspeedADCState adc; @@ -68,6 +72,8 @@ struct AspeedSoCState { AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; AspeedSBCState sbc; + AspeedSLIState sli; + AspeedSLIState sliio; MemoryRegion secsram; UnimplementedDeviceState sbc_unimplemented; AspeedSDMCState sdmc; @@ -117,6 +123,17 @@ struct Aspeed2600SoCState { #define TYPE_ASPEED2600_SOC "aspeed2600-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2600SoCState, ASPEED2600_SOC) +struct Aspeed27x0SoCState { + AspeedSoCState parent; + + ARMCPU cpu[ASPEED_CPUS_NUM]; + AspeedINTCState intc; + GICv3State gic; +}; + +#define TYPE_ASPEED27X0_SOC "aspeed27x0-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SoCState, ASPEED27X0_SOC) + struct Aspeed10x0SoCState { AspeedSoCState parent; @@ -168,11 +185,13 @@ enum { ASPEED_DEV_UART13, ASPEED_DEV_VUART, ASPEED_DEV_FMC, + ASPEED_DEV_SPI0, ASPEED_DEV_SPI1, ASPEED_DEV_SPI2, ASPEED_DEV_EHCI1, ASPEED_DEV_EHCI2, ASPEED_DEV_VIC, + ASPEED_DEV_INTC, ASPEED_DEV_SDMC, ASPEED_DEV_SCU, ASPEED_DEV_ADC, @@ -222,6 +241,11 @@ enum { ASPEED_DEV_JTAG1, ASPEED_DEV_FSI1, ASPEED_DEV_FSI2, + ASPEED_DEV_SCUIO, + ASPEED_DEV_SLI, + ASPEED_DEV_SLIIO, + ASPEED_GIC_DIST, + ASPEED_GIC_REDIST, }; qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); From 92707992103effc3e4f6f8a03da59e627acc1e34 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:35 +0800 Subject: [PATCH 1400/2884] aspeed: Add an AST2700 eval board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 CPU is ARM Cortex-A35 which is 64 bits. Add TARGET_AARCH64 to build this machine. According to the design of ast2700, it has a bootmcu(riscv-32) which is used for executing SPL. Then, CPUs(cortex-a35) execute u-boot, kernel and rofs. Currently, qemu not support emulate two CPU architectures at the same machine. Therefore, qemu will only support to emulate CPU(cortex-a35) side for ast2700 Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/arm/aspeed.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 93ca87fda2..40dc0e4c76 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -178,6 +178,12 @@ struct AspeedMachineState { #define AST2600_EVB_HW_STRAP1 0x000000C0 #define AST2600_EVB_HW_STRAP2 0x00000003 +#ifdef TARGET_AARCH64 +/* AST2700 evb hardware value */ +#define AST2700_EVB_HW_STRAP1 0x000000C0 +#define AST2700_EVB_HW_STRAP2 0x00000003 +#endif + /* Tacoma hardware value */ #define TACOMA_BMC_HW_STRAP1 0x00000000 #define TACOMA_BMC_HW_STRAP2 0x00000040 @@ -1588,6 +1594,26 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, aspeed_machine_class_init_cpus_defaults(mc); } +#ifdef TARGET_AARCH64 +static void aspeed_machine_ast2700_evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2700 EVB (Cortex-A35)"; + amc->soc_name = "ast2700-a0"; + amc->hw_strap1 = AST2700_EVB_HW_STRAP1; + amc->hw_strap2 = AST2700_EVB_HW_STRAP2; + amc->fmc_model = "w25q01jvq"; + amc->spi_model = "w25q512jv"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; + amc->uart_default = ASPEED_DEV_UART12; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); +} +#endif + static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, void *data) { @@ -1711,6 +1737,12 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("ast1030-evb"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_minibmc_machine_ast1030_evb_class_init, +#ifdef TARGET_AARCH64 + }, { + .name = MACHINE_TYPE_NAME("ast2700-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2700_evb_class_init, +#endif }, { .name = TYPE_ASPEED_MACHINE, .parent = TYPE_MACHINE, From 7436db1063bbfecc2e498a7d795613b33312d665 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:36 +0800 Subject: [PATCH 1401/2884] aspeed/soc: fix incorrect dram size for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 dram size calculation is not back compatible AST2600. According to the DDR capacity hardware behavior, if users write the data to the address which is beyond the ram size, it would write the data to the "address % ram_size". For example: a. sdram base address "0x4 00000000" b. sdram size 1 GiB The available address range is from "0x4 00000000" to "0x4 3FFFFFFF". If users write 0x12345678 to address "0x5 00000000", the value of DRAM address 0 (base address 0x4 00000000) will be 0x12345678. Add aspeed_soc_ast2700_dram_init to calculate the dram size and add memory I/O whose address range is from "max_ram_size - ram_size" to max_ram_size and its read/write handler to emulate DDR capacity hardware behavior. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 87 ++++++++++++++++++++++++++++++++++++- include/hw/arm/aspeed_soc.h | 2 + 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 29e75072c4..b6876b4862 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -20,6 +20,7 @@ #include "sysemu/sysemu.h" #include "hw/intc/arm_gicv3.h" #include "qapi/qmp/qlist.h" +#include "qemu/log.h" static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_SPI_BOOT] = 0x400000000, @@ -191,6 +192,90 @@ static qemu_irq aspeed_soc_ast2700_get_irq(AspeedSoCState *s, int dev) return qdev_get_gpio_in(DEVICE(&a->gic), sc->irqmap[dev]); } +static uint64_t aspeed_ram_capacity_read(void *opaque, hwaddr addr, + unsigned int size) +{ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DRAM read out of ram size, addr:0x%" PRIx64 "\n", + __func__, addr); + return 0; +} + +static void aspeed_ram_capacity_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSoCState *s = ASPEED_SOC(opaque); + ram_addr_t ram_size; + MemTxResult result; + + ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", + &error_abort); + + /* + * Emulate ddr capacity hardware behavior. + * If writes the data to the address which is beyond the ram size, + * it would write the data to the "address % ram_size". + */ + result = address_space_write(&s->dram_as, addr % ram_size, + MEMTXATTRS_UNSPECIFIED, &data, 4); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DRAM write failed, addr:0x%" HWADDR_PRIx + ", data :0x%" PRIx64 "\n", + __func__, addr % ram_size, data); + } +} + +static const MemoryRegionOps aspeed_ram_capacity_ops = { + .read = aspeed_ram_capacity_read, + .write = aspeed_ram_capacity_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +/* + * SDMC should be realized first to get correct RAM size and max size + * values + */ +static bool aspeed_soc_ast2700_dram_init(DeviceState *dev, Error **errp) +{ + ram_addr_t ram_size, max_ram_size; + Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", + &error_abort); + max_ram_size = object_property_get_uint(OBJECT(&s->sdmc), "max-ram-size", + &error_abort); + + memory_region_init(&s->dram_container, OBJECT(s), "ram-container", + ram_size); + memory_region_add_subregion(&s->dram_container, 0, s->dram_mr); + address_space_init(&s->dram_as, s->dram_mr, "dram"); + + /* + * Add a memory region beyond the RAM region to emulate + * ddr capacity hardware behavior. + */ + if (ram_size < max_ram_size) { + memory_region_init_io(&a->dram_empty, OBJECT(s), + &aspeed_ram_capacity_ops, s, + "ram-empty", max_ram_size - ram_size); + + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SDRAM] + ram_size, + &a->dram_empty); + } + + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SDRAM], &s->dram_container); + return true; +} + static void aspeed_soc_ast2700_init(Object *obj) { Aspeed27x0SoCState *a = ASPEED27X0_SOC(obj); @@ -461,7 +546,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_SDMC]); /* RAM */ - if (!aspeed_soc_dram_init(s, errp)) { + if (!aspeed_soc_ast2700_dram_init(dev, errp)) { return; } diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index caef0d100b..849ba37f95 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -59,6 +59,7 @@ struct AspeedSoCState { MemoryRegion sram; MemoryRegion spi_boot_container; MemoryRegion spi_boot; + AddressSpace dram_as; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; @@ -129,6 +130,7 @@ struct Aspeed27x0SoCState { ARMCPU cpu[ASPEED_CPUS_NUM]; AspeedINTCState intc; GICv3State gic; + MemoryRegion dram_empty; }; #define TYPE_ASPEED27X0_SOC "aspeed27x0-soc" From 1478055d3a3940e4d708919b6d83b03ef6474af4 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:37 +0800 Subject: [PATCH 1402/2884] test/avocado/machine_aspeed.py: Add AST2700 test case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test case to test Aspeed OpenBMC SDK v09.01 on AST2700 board. It loads u-boot-nodtb.bin, u-boot.dtb, tfa and optee-os images to dram first which base address is 0x400000000. Then, boot and launch 4 cpu cores. ``` qemu-system-aarch64 -machine ast2700-evb -device loader,force-raw=on,addr=0x400000000,file=workdir/u-boot-nodtb.bin \ -device loader,force-raw=on,addr=uboot_dtb_load_addr,file=workdir/u-boot.dtb\ -device loader,force-raw=on,addr=0x430000000,file=workdir/bl31.bin\ -device loader,force-raw=on,addr=0x430080000,file=workdir/optee/tee-raw.bin\ -device loader,cpu-num=0,addr=0x430000000 \ -device loader,cpu-num=1,addr=0x430000000 \ -device loader,cpu-num=2,addr=0x430000000 \ -device loader,cpu-num=3,addr=0x430000000 \ -smp 4 \ -drive file=workdir/image-bmc,format=raw,if=mtd ``` A test image is downloaded from the ASPEED Forked OpenBMC GitHub release repository : https://github.com/AspeedTech-BMC/openbmc/releases/ Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- tests/avocado/machine_aspeed.py | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index cec0181424..3a20644fb2 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -311,6 +311,17 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): self, 'boot', '## Loading kernel from FIT Image') self.wait_for_console_pattern('Starting kernel ...') + def do_test_aarch64_aspeed_sdk_start(self, image): + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw') + + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2023.10') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern("systemd[1]: Hostname set to") + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') def test_arm_ast2500_evb_sdk(self): @@ -375,3 +386,54 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); year = time.strftime("%Y") self.ssh_command_output_contains('/sbin/hwclock -f /dev/rtc1', year); + + def test_aarch64_ast2700_evb_sdk_v09_01(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:ast2700-evb + """ + + image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' + 'download/v09.01/ast2700-default-obmc.tar.gz') + image_hash = 'b1cc0fd73c7650d34c9c8459a243f52a91e9e27144b8608b2645ab19461d1e07' + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + archive.extract(image_path, self.workdir) + + num_cpu = 4 + image_dir = self.workdir + '/ast2700-default/' + uboot_size = os.path.getsize(image_dir + 'u-boot-nodtb.bin') + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': image_dir + 'u-boot-nodtb.bin' + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': image_dir + 'u-boot.dtb' + }, + { + 'addr': '0x430000000', + 'file': image_dir + 'bl31.bin' + }, + { + 'addr': '0x430080000', + 'file': image_dir + 'optee/tee-raw.bin' + } + ] + + for load_image in load_images_list: + addr = load_image['addr'] + file = load_image['file'] + self.vm.add_args('-device', + f'loader,force-raw=on,addr={addr},file={file}') + + for i in range(num_cpu): + self.vm.add_args('-device', + f'loader,addr=0x430000000,cpu-num={i}') + + self.vm.add_args('-smp', str(num_cpu)) + self.do_test_aarch64_aspeed_sdk_start(image_dir + 'image-bmc') + From b3e8223e48f4a8981232a17ebf48c198829ce191 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Jun 2024 13:44:38 +0800 Subject: [PATCH 1403/2884] docs:aspeed: Add AST2700 Evaluation board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AST2700 Evaluation board and its boot command. Signed-off-by: Troy Lee Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 39 ++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index b2dea54eed..cd9559e3e2 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,11 +1,12 @@ -Aspeed family boards (``*-bmc``, ``ast2500-evb``, ``ast2600-evb``) -================================================================== +Aspeed family boards (``*-bmc``, ``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``) +=================================================================================== The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the Aspeed SoC : the AST2400 integrating an ARM926EJ-S CPU (400MHz), the -AST2500 with an ARM1176JZS CPU (800MHz) and more recently the AST2600 -with dual cores ARM Cortex-A7 CPUs (1.2GHz). +AST2500 with an ARM1176JZS CPU (800MHz), the AST2600 +with dual cores ARM Cortex-A7 CPUs (1.2GHz) and more recently the AST2700 +with quad cores ARM Cortex-A35 64 bits CPUs (1.6GHz) The SoC comes with RAM, Gigabit ethernet, USB, SD/MMC, USB, SPI, I2C, etc. @@ -38,6 +39,10 @@ AST2600 SoC based machines : - ``qcom-dc-scm-v1-bmc`` Qualcomm DC-SCM V1 BMC - ``qcom-firework-bmc`` Qualcomm Firework BMC +AST2700 SoC based machines : + +- ``ast2700-evb`` Aspeed AST2700 Evaluation board (Cortex-A35) + Supported devices ----------------- @@ -66,6 +71,7 @@ Supported devices * eMMC Boot Controller (dummy) * PECI Controller (minimal) * I3C Controller + * Internal Bridge Controller (SLI dummy) Missing devices @@ -95,6 +101,10 @@ or directly from the OpenBMC GitHub release repository : https://github.com/openbmc/openbmc/releases +or directly from the ASPEED Forked OpenBMC GitHub release repository : + + https://github.com/AspeedTech-BMC/openbmc/releases + To boot a kernel directly from a Linux build tree: .. code-block:: bash @@ -164,6 +174,27 @@ under Linux), use : -M ast2500-evb,bmc-console=uart3 + +Boot the AST2700 machine from the flash image, use an MTD drive : + +.. code-block:: bash + + IMGDIR=ast2700-default + UBOOT_SIZE=$(stat --format=%s -L ${IMGDIR}/u-boot-nodtb.bin) + + $ qemu-system-aarch64 -M ast2700-evb \ + -device loader,force-raw=on,addr=0x400000000,file=${IMGDIR}/u-boot-nodtb.bin \ + -device loader,force-raw=on,addr=$((0x400000000 + ${UBOOT_SIZE})),file=${IMGDIR}/u-boot.dtb \ + -device loader,force-raw=on,addr=0x430000000,file=${IMGDIR}/bl31.bin \ + -device loader,force-raw=on,addr=0x430080000,file=${IMGDIR}/optee/tee-raw.bin \ + -device loader,cpu-num=0,addr=0x430000000 \ + -device loader,cpu-num=1,addr=0x430000000 \ + -device loader,cpu-num=2,addr=0x430000000 \ + -device loader,cpu-num=3,addr=0x430000000 \ + -smp 4 \ + -drive file=${IMGDIR}/image-bmc,format=raw,if=mtd \ + -nographic + Aspeed minibmc family boards (``ast1030-evb``) ================================================================== From 5f44521242d2fdfa190206a6be40577a58a71ef9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 5 Jun 2024 14:03:10 +0800 Subject: [PATCH 1404/2884] MAINTAINERS: Add reviewers for ASPEED BMCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ASPEED members "Steven Lee", "Troy Lee" and "Jamin Lin" to be reviewers of ASPEED BMCs. Signed-off-by: Jamin Lin Signed-off-by: Troy Lee Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jeffery --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 951556224a..0f63bcdc7d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1158,6 +1158,9 @@ F: docs/system/arm/emcraft-sf2.rst ASPEED BMCs M: Cédric Le Goater M: Peter Maydell +R: Steven Lee +R: Troy Lee +R: Jamin Lin R: Andrew Jeffery R: Joel Stanley L: qemu-arm@nongnu.org From a1af7fba5a003dd4b12b4b7dfdd869fd1aab80ef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 May 2024 15:55:22 +0200 Subject: [PATCH 1405/2884] target/i386: convert MOV from/to CR and DR to new decoder Complete implementation of C and D operand types, then the operations are just MOVs. Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 61 ++++++++++++++++++++++-- target/i386/tcg/decode-new.h | 1 + target/i386/tcg/emit.c.inc | 24 +++++++++- target/i386/tcg/translate.c | 79 -------------------------------- 4 files changed, 81 insertions(+), 84 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index cd925fe358..0f31e1f455 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -151,6 +151,8 @@ X86_OP_GROUP3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) #define X86_OP_GROUPw(op, op0, s0, ...) \ X86_OP_GROUP3(op, op0, s0, None, None, None, None, ## __VA_ARGS__) +#define X86_OP_GROUPwr(op, op0, s0, op1, s1, ...) \ + X86_OP_GROUP3(op, op0, s0, op1, s1, None, None, ## __VA_ARGS__) #define X86_OP_GROUP0(op, ...) \ X86_OP_GROUP3(op, None, None, None, None, None, None, ## __VA_ARGS__) @@ -985,6 +987,24 @@ static void decode_0FE6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui *entry = *decode_by_prefix(s, opcodes_0FE6); } +/* + * These ignore the mod bits (assume (modrm&0xc0)==0xc0), so group the + * pre-decode tweak here for all MOVs from/to CR and DR. + * + * AMD documentation (24594.pdf) and testing of Intel 386 and 486 + * processors all show that the mod bits are assumed to be 1's, + * regardless of actual values. + */ +static void decode_MOV_CR_DR(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* + */ + get_modrm(s, env); + s->modrm |= 0xC0; + + entry->gen = gen_MOV; +} + static const X86OpEntry opcodes_0F[256] = { [0x0E] = X86_OP_ENTRY0(EMMS, cpuid(3DNOW)), /* femms */ /* @@ -1004,6 +1024,15 @@ static const X86OpEntry opcodes_0F[256] = { /* Incorrectly listed as Mq,Vq in the manual */ [0x17] = X86_OP_ENTRY3(VMOVHPx_st, M,q, None,None, V,dq, vex5 p_00_66), + /* + * Incorrectly listed as using "d" operand type in the manual. In reality + * there's no 16-bit version (like y) and it does not use REX.W (like d64). + */ + [0x20] = X86_OP_GROUPwr(MOV_CR_DR, R,y_d64, C,y_d64, chk(cpl0) svm(READ_CR0)), + [0x21] = X86_OP_GROUPwr(MOV_CR_DR, R,y_d64, D,y_d64, chk(cpl0) svm(READ_DR0)), + [0x22] = X86_OP_GROUPwr(MOV_CR_DR, C,y_d64, R,y_d64, zextT0 chk(cpl0) svm(WRITE_CR0)), + [0x23] = X86_OP_GROUPwr(MOV_CR_DR, D,y_d64, R,y_d64, zextT0 chk(cpl0) svm(WRITE_DR0)), + [0x40] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), [0x41] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), [0x42] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), @@ -1725,6 +1754,10 @@ static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp *ot = s->dflag == MO_16 ? MO_32 : s->dflag; return true; + case X86_SIZE_y_d64: /* Full (not 16-bit) register access */ + *ot = CODE64(s) ? MO_64 : MO_32; + return true; + case X86_SIZE_z: /* 16-bit for 16-bit operand size, else 32-bit */ *ot = s->dflag == MO_16 ? MO_16 : MO_32; return true; @@ -1802,11 +1835,34 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, case X86_TYPE_C: /* REG in the modrm byte selects a control register */ op->unit = X86_OP_CR; - goto get_reg; + op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + if (op->n == 0 && (s->prefix & PREFIX_LOCK) && + (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { + op->n = 8; + s->prefix &= ~PREFIX_LOCK; + } + if (op->n != 0 && op->n != 2 && op->n != 3 && op->n != 4 && op->n != 8) { + return false; + } + if (decode->e.intercept) { + decode->e.intercept += op->n; + } + break; case X86_TYPE_D: /* REG in the modrm byte selects a debug register */ op->unit = X86_OP_DR; - goto get_reg; + op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + if (op->n >= 8) { + /* + * illegal opcode. The DR4 and DR5 case is checked in the generated + * code instead, to save on hflags bits. + */ + return false; + } + if (decode->e.intercept) { + decode->e.intercept += op->n; + } + break; case X86_TYPE_G: /* REG in the modrm byte selects a GPR */ op->unit = X86_OP_INT; @@ -2431,7 +2487,6 @@ static void disas_insn(DisasContext *s, CPUState *cpu) case 0x00 ... 0x03: /* mostly privileged instructions */ case 0x05 ... 0x09: case 0x1a ... 0x1b: /* MPX */ - case 0x20 ... 0x23: /* mov from/to CR and DR */ case 0x30 ... 0x35: /* more privileged instructions */ case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */ case 0xaa ... 0xae: /* RSM, SHRD, grp15 */ diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 8465717ea2..60a191ee76 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -90,6 +90,7 @@ typedef enum X86OpSize { X86_SIZE_w, /* 16-bit */ X86_SIZE_x, /* 128/256-bit, based on operand size */ X86_SIZE_y, /* 32/64-bit, based on operand size */ + X86_SIZE_y_d64, /* 32/64-bit, based on 64-bit mode */ X86_SIZE_z, /* 16-bit for 16-bit operand size, else 32-bit */ X86_SIZE_z_f64, /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index e6521632ed..db56bf7aee 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -242,12 +242,20 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) tcg_gen_ld32u_tl(v, tcg_env, offsetof(CPUX86State,segs[op->n].selector)); break; +#ifndef CONFIG_USER_ONLY case X86_OP_CR: - tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, cr[op->n])); + if (op->n == 8) { + translator_io_start(&s->base); + gen_helper_read_crN(v, tcg_env, tcg_constant_i32(op->n)); + } else { + tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, cr[op->n])); + } break; case X86_OP_DR: - tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, dr[op->n])); + /* CR4.DE tested in the helper. */ + gen_helper_get_dr(v, tcg_env, tcg_constant_i32(op->n)); break; +#endif case X86_OP_INT: if (op->has_ea) { if (v == s->T0 && decode->e.special == X86_SPECIAL_SExtT0) { @@ -343,8 +351,20 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv 16, 16, 0); } break; +#ifndef CONFIG_USER_ONLY case X86_OP_CR: + if (op->n == 8) { + translator_io_start(&s->base); + } + gen_helper_write_crN(tcg_env, tcg_constant_i32(op->n), v); + s->base.is_jmp = DISAS_EOB_NEXT; + break; case X86_OP_DR: + /* CR4.DE tested in the helper. */ + gen_helper_set_dr(tcg_env, tcg_constant_i32(op->n), v); + s->base.is_jmp = DISAS_EOB_NEXT; + break; +#endif default: g_assert_not_reached(); } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index fcba9c155f..4958f4c45d 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -247,9 +247,6 @@ STUB_HELPER(outb, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outw, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outl, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(rdmsr, TCGv_env env) -STUB_HELPER(read_crN, TCGv ret, TCGv_env env, TCGv_i32 reg) -STUB_HELPER(get_dr, TCGv ret, TCGv_env env, TCGv_i32 reg) -STUB_HELPER(set_dr, TCGv_env env, TCGv_i32 reg, TCGv val) STUB_HELPER(stgi, TCGv_env env) STUB_HELPER(svm_check_intercept, TCGv_env env, TCGv_i32 type) STUB_HELPER(vmload, TCGv_env env, TCGv_i32 aflag) @@ -4192,82 +4189,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) gen_nop_modrm(env, s, modrm); break; - case 0x120: /* mov reg, crN */ - case 0x122: /* mov crN, reg */ - if (!check_cpl0(s)) { - break; - } - modrm = x86_ldub_code(env, s); - /* - * Ignore the mod bits (assume (modrm&0xc0)==0xc0). - * AMD documentation (24594.pdf) and testing of Intel 386 and 486 - * processors all show that the mod bits are assumed to be 1's, - * regardless of actual values. - */ - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | REX_R(s); - switch (reg) { - case 0: - if ((prefixes & PREFIX_LOCK) && - (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { - reg = 8; - } - break; - case 2: - case 3: - case 4: - case 8: - break; - default: - goto unknown_op; - } - ot = (CODE64(s) ? MO_64 : MO_32); - - translator_io_start(&s->base); - if (b & 2) { - gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0 + reg); - gen_op_mov_v_reg(s, ot, s->T0, rm); - gen_helper_write_crN(tcg_env, tcg_constant_i32(reg), s->T0); - s->base.is_jmp = DISAS_EOB_NEXT; - } else { - gen_svm_check_intercept(s, SVM_EXIT_READ_CR0 + reg); - gen_helper_read_crN(s->T0, tcg_env, tcg_constant_i32(reg)); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - break; - - case 0x121: /* mov reg, drN */ - case 0x123: /* mov drN, reg */ - if (check_cpl0(s)) { - modrm = x86_ldub_code(env, s); - /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). - * AMD documentation (24594.pdf) and testing of - * intel 386 and 486 processors all show that the mod bits - * are assumed to be 1's, regardless of actual values. - */ - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | REX_R(s); - if (CODE64(s)) - ot = MO_64; - else - ot = MO_32; - if (reg >= 8) { - goto illegal_op; - } - if (b & 2) { - gen_svm_check_intercept(s, SVM_EXIT_WRITE_DR0 + reg); - gen_op_mov_v_reg(s, ot, s->T0, rm); - tcg_gen_movi_i32(s->tmp2_i32, reg); - gen_helper_set_dr(tcg_env, s->tmp2_i32, s->T0); - s->base.is_jmp = DISAS_EOB_NEXT; - } else { - gen_svm_check_intercept(s, SVM_EXIT_READ_DR0 + reg); - tcg_gen_movi_i32(s->tmp2_i32, reg); - gen_helper_get_dr(s->T0, tcg_env, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - break; case 0x106: /* clts */ if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); From e0448caebfdb6849860100cbd1591bc63874e369 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Jun 2024 19:43:30 +0200 Subject: [PATCH 1406/2884] target/i386: replace read_crN helper with read_cr8 All other control registers are stored plainly in CPUX86State. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/helper.h | 2 +- target/i386/tcg/emit.c.inc | 2 +- target/i386/tcg/sysemu/misc_helper.c | 20 +++++--------------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/target/i386/helper.h b/target/i386/helper.h index 2f46cffabd..eeb8df56ea 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -95,7 +95,7 @@ DEF_HELPER_FLAGS_2(monitor, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(mwait, TCG_CALL_NO_WG, noreturn, env, int) DEF_HELPER_1(rdmsr, void, env) DEF_HELPER_1(wrmsr, void, env) -DEF_HELPER_FLAGS_2(read_crN, TCG_CALL_NO_RWG, tl, env, int) +DEF_HELPER_FLAGS_1(read_cr8, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_3(write_crN, TCG_CALL_NO_RWG, void, env, int, tl) #endif /* !CONFIG_USER_ONLY */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index db56bf7aee..2d0c212e5c 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -246,7 +246,7 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) case X86_OP_CR: if (op->n == 8) { translator_io_start(&s->base); - gen_helper_read_crN(v, tcg_env, tcg_constant_i32(op->n)); + gen_helper_read_cr8(v, tcg_env); } else { tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, cr[op->n])); } diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index 7fa0c5a06d..094aa56a20 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -63,23 +63,13 @@ target_ulong helper_inl(CPUX86State *env, uint32_t port) cpu_get_mem_attrs(env), NULL); } -target_ulong helper_read_crN(CPUX86State *env, int reg) +target_ulong helper_read_cr8(CPUX86State *env) { - target_ulong val; - - switch (reg) { - default: - val = env->cr[reg]; - break; - case 8: - if (!(env->hflags2 & HF2_VINTR_MASK)) { - val = cpu_get_apic_tpr(env_archcpu(env)->apic_state); - } else { - val = env->int_ctl & V_TPR_MASK; - } - break; + if (!(env->hflags2 & HF2_VINTR_MASK)) { + return cpu_get_apic_tpr(env_archcpu(env)->apic_state); + } else { + return env->int_ctl & V_TPR_MASK; } - return val; } void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) From 10340080cd501b1aba23c3e502e2e0aa7c825fbf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 14:38:27 +0200 Subject: [PATCH 1407/2884] target/i386: fix bad sorting of entries in the 0F table Aesthetic change only. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 93 ++++++++++++++++---------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 0f31e1f455..19289f8180 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1006,14 +1006,6 @@ static void decode_MOV_CR_DR(DisasContext *s, CPUX86State *env, X86OpEntry *entr } static const X86OpEntry opcodes_0F[256] = { - [0x0E] = X86_OP_ENTRY0(EMMS, cpuid(3DNOW)), /* femms */ - /* - * 3DNow!'s opcode byte comes *after* modrm and displacements, making it - * more like an Ib operand. Dispatch to the right helper in a single gen_* - * function. - */ - [0x0F] = X86_OP_ENTRY3(3dnow, P,q, Q,q, I,b, cpuid(3DNOW)), - [0x10] = X86_OP_GROUP0(0F10), [0x11] = X86_OP_GROUP0(0F11), [0x12] = X86_OP_GROUP0(0F12), @@ -1090,8 +1082,54 @@ static const X86OpEntry opcodes_0F[256] = { [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), [0xa1] = X86_OP_ENTRYw(POP, FS, w), + [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), + [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), + [0xb5] = X86_OP_ENTRY3(LGS, G,v, EM,p, None, None), + [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ + [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ + + [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0xc3] = X86_OP_ENTRY3(MOV, EM,y,G,y, None,None, cpuid(SSE2)), /* MOVNTI */ + [0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66), + [0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66), + [0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66), + + [0xd0] = X86_OP_ENTRY3(VADDSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), + [0xd1] = X86_OP_ENTRY3(PSRLW_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd3] = X86_OP_ENTRY3(PSRLQ_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd4] = X86_OP_ENTRY3(PADDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd5] = X86_OP_ENTRY3(PMULLW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd6] = X86_OP_GROUP0(0FD6), + [0xd7] = X86_OP_ENTRY3(PMOVMSKB, G,d, None,None, U,x, vex7 mmx avx2_256 p_00_66), + + [0xe0] = X86_OP_ENTRY3(PAVGB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe1] = X86_OP_ENTRY3(PSRAW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xe2] = X86_OP_ENTRY3(PSRAD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xe3] = X86_OP_ENTRY3(PAVGW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe4] = X86_OP_ENTRY3(PMULHUW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe5] = X86_OP_ENTRY3(PMULHW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe6] = X86_OP_GROUP0(0FE6), + [0xe7] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 mmx p_00_66), /* MOVNTQ/MOVNTDQ */ + + [0xf0] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex4_unal cpuid(SSE3) p_f2), /* LDDQU */ + [0xf1] = X86_OP_ENTRY3(PSLLW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf2] = X86_OP_ENTRY3(PSLLD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf3] = X86_OP_ENTRY3(PSLLQ_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf4] = X86_OP_ENTRY3(PMULUDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf5] = X86_OP_ENTRY3(PMADDWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf6] = X86_OP_ENTRY3(PSADBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf7] = X86_OP_ENTRY3(MASKMOV, None,None, V,dq, U,dq, vex4_unal avx2_256 mmx p_00_66), + [0x0b] = X86_OP_ENTRY0(UD), /* UD2 */ [0x0d] = X86_OP_ENTRY1(NOP, M,v), /* 3DNow! prefetch */ + [0x0e] = X86_OP_ENTRY0(EMMS, cpuid(3DNOW)), /* femms */ + /* + * 3DNow!'s opcode byte comes *after* modrm and displacements, making it + * more like an Ib operand. Dispatch to the right helper in a single gen_* + * function. + */ + [0x0f] = X86_OP_ENTRY3(3dnow, P,q, Q,q, I,b, cpuid(3DNOW)), [0x18] = X86_OP_ENTRY1(NOP, nop,v), /* prefetch/reserved NOP */ [0x19] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ @@ -1173,23 +1211,11 @@ static const X86OpEntry opcodes_0F[256] = { */ [0xaf] = X86_OP_ENTRY3(IMUL3, G,v, E,v, 2op,v, sextT0), - [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), - [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), - [0xb5] = X86_OP_ENTRY3(LGS, G,v, EM,p, None, None), - [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ - [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ - /* decoded as modrm, which is visible as a difference between page fault and #UD */ [0xb9] = X86_OP_ENTRYr(UD, nop,v), /* UD1 */ [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */ [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */ - [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), - [0xc3] = X86_OP_ENTRY3(MOV, EM,y,G,y, None,None, cpuid(SSE2)), /* MOVNTI */ - [0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66), - [0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66), - [0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66), - [0xc8] = X86_OP_ENTRY1(BSWAP, LoBits,y), [0xc9] = X86_OP_ENTRY1(BSWAP, LoBits,y), [0xca] = X86_OP_ENTRY1(BSWAP, LoBits,y), @@ -1199,33 +1225,6 @@ static const X86OpEntry opcodes_0F[256] = { [0xce] = X86_OP_ENTRY1(BSWAP, LoBits,y), [0xcf] = X86_OP_ENTRY1(BSWAP, LoBits,y), - [0xd0] = X86_OP_ENTRY3(VADDSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), - [0xd1] = X86_OP_ENTRY3(PSRLW_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd3] = X86_OP_ENTRY3(PSRLQ_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd4] = X86_OP_ENTRY3(PADDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd5] = X86_OP_ENTRY3(PMULLW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd6] = X86_OP_GROUP0(0FD6), - [0xd7] = X86_OP_ENTRY3(PMOVMSKB, G,d, None,None, U,x, vex7 mmx avx2_256 p_00_66), - - [0xe0] = X86_OP_ENTRY3(PAVGB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe1] = X86_OP_ENTRY3(PSRAW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xe2] = X86_OP_ENTRY3(PSRAD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xe3] = X86_OP_ENTRY3(PAVGW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe4] = X86_OP_ENTRY3(PMULHUW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe5] = X86_OP_ENTRY3(PMULHW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe6] = X86_OP_GROUP0(0FE6), - [0xe7] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 mmx p_00_66), /* MOVNTQ/MOVNTDQ */ - - [0xf0] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex4_unal cpuid(SSE3) p_f2), /* LDDQU */ - [0xf1] = X86_OP_ENTRY3(PSLLW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xf2] = X86_OP_ENTRY3(PSLLD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xf3] = X86_OP_ENTRY3(PSLLQ_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xf4] = X86_OP_ENTRY3(PMULUDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xf5] = X86_OP_ENTRY3(PMADDWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xf6] = X86_OP_ENTRY3(PSADBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xf7] = X86_OP_ENTRY3(MASKMOV, None,None, V,dq, U,dq, vex4_unal avx2_256 mmx p_00_66), - /* Incorrectly missing from 2-17 */ [0xd8] = X86_OP_ENTRY3(PSUBUSB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xd9] = X86_OP_ENTRY3(PSUBUSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), From ea89aa895e98fd8a1b9ebf7e3dc8bfcd863b9466 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 May 2024 17:45:53 +0200 Subject: [PATCH 1408/2884] target/i386: finish converting 0F AE to the new decoder This is already partly implemented due to VLDMXCSR and VSTMXCSR; finish the job. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 48 +++++++- target/i386/tcg/decode-new.h | 7 ++ target/i386/tcg/emit.c.inc | 80 +++++++++++++ target/i386/tcg/translate.c | 188 ------------------------------- 4 files changed, 129 insertions(+), 194 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 19289f8180..b845a1b7b4 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -269,20 +269,41 @@ static inline const X86OpEntry *decode_by_prefix(DisasContext *s, const X86OpEnt static void decode_group15(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { - /* only includes ldmxcsr and stmxcsr, because they have AVX variants. */ static const X86OpEntry group15_reg[8] = { + [0] = X86_OP_ENTRYw(RDxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3), + [1] = X86_OP_ENTRYw(RDxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3), + [2] = X86_OP_ENTRYr(WRxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0), + [3] = X86_OP_ENTRYr(WRxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0), + [5] = X86_OP_ENTRY0(LFENCE, cpuid(SSE2) p_00), + [6] = X86_OP_ENTRY0(MFENCE, cpuid(SSE2) p_00), + [7] = X86_OP_ENTRY0(SFENCE, cpuid(SSE2) p_00), }; static const X86OpEntry group15_mem[8] = { - [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5 chk(VEX128)), - [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5 chk(VEX128)), + [0] = X86_OP_ENTRYw(FXSAVE, M,y, cpuid(FXSR) p_00), + [1] = X86_OP_ENTRYr(FXRSTOR, M,y, cpuid(FXSR) p_00), + [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5 chk(VEX128) p_00), + [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5 chk(VEX128) p_00), + [4] = X86_OP_ENTRYw(XSAVE, M,y, cpuid(XSAVE) p_00), + [5] = X86_OP_ENTRYr(XRSTOR, M,y, cpuid(XSAVE) p_00), + [6] = X86_OP_ENTRYw(XSAVEOPT, M,b, cpuid(XSAVEOPT) p_00), + [7] = X86_OP_ENTRYw(NOP, M,b, cpuid(CLFLUSH) p_00), + }; + + static const X86OpEntry group15_mem_66[8] = { + [6] = X86_OP_ENTRYw(NOP, M,b, cpuid(CLWB)), + [7] = X86_OP_ENTRYw(NOP, M,b, cpuid(CLFLUSHOPT)), }; uint8_t modrm = get_modrm(s, env); + int op = (modrm >> 3) & 7; + if ((modrm >> 6) == 3) { - *entry = group15_reg[(modrm >> 3) & 7]; + *entry = group15_reg[op]; + } else if (s->prefix & PREFIX_DATA) { + *entry = group15_mem_66[op]; } else { - *entry = group15_mem[(modrm >> 3) & 7]; + *entry = group15_mem[op]; } } @@ -2102,6 +2123,10 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) return true; case X86_FEAT_CMOV: return (s->cpuid_features & CPUID_CMOV); + case X86_FEAT_CLFLUSH: + return (s->cpuid_features & CPUID_CLFLUSH); + case X86_FEAT_FXSR: + return (s->cpuid_features & CPUID_FXSR); case X86_FEAT_F16C: return (s->cpuid_ext_features & CPUID_EXT_F16C); case X86_FEAT_FMA: @@ -2135,6 +2160,8 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) case X86_FEAT_AVX: return (s->cpuid_ext_features & CPUID_EXT_AVX); + case X86_FEAT_XSAVE: + return (s->cpuid_ext_features & CPUID_EXT_XSAVE); case X86_FEAT_3DNOW: return (s->cpuid_ext2_features & CPUID_EXT2_3DNOW); @@ -2149,11 +2176,20 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2); case X86_FEAT_AVX2: return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_AVX2); + case X86_FEAT_CLFLUSHOPT: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT); + case X86_FEAT_CLWB: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB); + case X86_FEAT_FSGSBASE: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE); case X86_FEAT_SHA_NI: return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SHA_NI); case X86_FEAT_CMPCCXADD: return (s->cpuid_7_1_eax_features & CPUID_7_1_EAX_CMPCCXADD); + + case X86_FEAT_XSAVEOPT: + return (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT); } g_assert_not_reached(); } @@ -2488,7 +2524,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) case 0x1a ... 0x1b: /* MPX */ case 0x30 ... 0x35: /* more privileged instructions */ case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */ - case 0xaa ... 0xae: /* RSM, SHRD, grp15 */ + case 0xaa ... 0xad: /* RSM, SHRD */ case 0xb0 ... 0xb1: /* cmpxchg */ case 0xb3: /* btr */ case 0xb8: /* integer ops */ diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 60a191ee76..bcac844ec4 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -109,10 +109,15 @@ typedef enum X86CPUIDFeature { X86_FEAT_AVX2, X86_FEAT_BMI1, X86_FEAT_BMI2, + X86_FEAT_CLFLUSH, + X86_FEAT_CLFLUSHOPT, + X86_FEAT_CLWB, X86_FEAT_CMOV, X86_FEAT_CMPCCXADD, X86_FEAT_F16C, X86_FEAT_FMA, + X86_FEAT_FSGSBASE, + X86_FEAT_FXSR, X86_FEAT_MOVBE, X86_FEAT_PCLMULQDQ, X86_FEAT_SHA_NI, @@ -123,6 +128,8 @@ typedef enum X86CPUIDFeature { X86_FEAT_SSE41, X86_FEAT_SSE42, X86_FEAT_SSE4A, + X86_FEAT_XSAVE, + X86_FEAT_XSAVEOPT, } X86CPUIDFeature; /* Execution flags */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2d0c212e5c..9649a6d84e 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1657,6 +1657,22 @@ static void gen_EXTRQ_r(DisasContext *s, X86DecodedInsn *decode) gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); } +static void gen_FXRSTOR(DisasContext *s, X86DecodedInsn *decode) +{ + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_NM_exception(s); + } + gen_helper_fxrstor(tcg_env, s->A0); +} + +static void gen_FXSAVE(DisasContext *s, X86DecodedInsn *decode) +{ + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_NM_exception(s); + } + gen_helper_fxsave(tcg_env, s->A0); +} + static void gen_HLT(DisasContext *s, X86DecodedInsn *decode) { #ifdef CONFIG_SYSTEM_ONLY @@ -2004,6 +2020,11 @@ static void gen_LES(DisasContext *s, X86DecodedInsn *decode) gen_lxx_seg(s, decode, R_ES); } +static void gen_LFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_LD_LD | TCG_BAR_SC); +} + static void gen_LFS(DisasContext *s, X86DecodedInsn *decode) { gen_lxx_seg(s, decode, R_FS); @@ -2063,6 +2084,11 @@ static void gen_LSS(DisasContext *s, X86DecodedInsn *decode) gen_lxx_seg(s, decode, R_SS); } +static void gen_MFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); +} + static void gen_MOV(DisasContext *s, X86DecodedInsn *decode) { /* nothing to do! */ @@ -3096,6 +3122,15 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) } } +static void gen_RDxxBASE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv base = cpu_seg_base[s->modrm & 8 ? R_GS : R_FS]; + + /* Preserve hflags bits by testing CR4 at runtime. */ + gen_helper_cr4_testbit(tcg_env, tcg_constant_i32(CR4_FSGSBASE_MASK)); + tcg_gen_mov_tl(s->T0, base); +} + static void gen_RET(DisasContext *s, X86DecodedInsn *decode) { int16_t adjust = decode->e.op1 == X86_TYPE_I ? decode->immediate : 0; @@ -3376,6 +3411,11 @@ static void gen_SETcc(DisasContext *s, X86DecodedInsn *decode) gen_setcc1(s, decode->b & 0xf, s->T0); } +static void gen_SFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); +} + static void gen_SHA1NEXTE(DisasContext *s, X86DecodedInsn *decode) { gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); @@ -4046,6 +4086,15 @@ static void gen_WAIT(DisasContext *s, X86DecodedInsn *decode) } } +static void gen_WRxxBASE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv base = cpu_seg_base[s->modrm & 8 ? R_GS : R_FS]; + + /* Preserve hflags bits by testing CR4 at runtime. */ + gen_helper_cr4_testbit(tcg_env, tcg_constant_i32(CR4_FSGSBASE_MASK)); + tcg_gen_mov_tl(base, s->T0); +} + static void gen_XCHG(DisasContext *s, X86DecodedInsn *decode) { if (s->prefix & PREFIX_LOCK) { @@ -4088,3 +4137,34 @@ static void gen_XOR(DisasContext *s, X86DecodedInsn *decode) prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } } + +static void gen_XRSTOR(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 features = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(features, cpu_regs[R_EAX], cpu_regs[R_EDX]); + gen_helper_xrstor(tcg_env, s->A0, features); + if (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_MPX) { + /* + * XRSTOR is how MPX is enabled, which changes how + * we translate. Thus we need to end the TB. + */ + s->base.is_jmp = DISAS_EOB_NEXT; + } +} + +static void gen_XSAVE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 features = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(features, cpu_regs[R_EAX], cpu_regs[R_EDX]); + gen_helper_xsave(tcg_env, s->A0, features); +} + +static void gen_XSAVEOPT(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 features = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(features, cpu_regs[R_EAX], cpu_regs[R_EDX]); + gen_helper_xsave(tcg_env, s->A0, features); +} diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4958f4c45d..ebae745ecb 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -4197,194 +4197,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) s->base.is_jmp = DISAS_EOB_NEXT; } break; - /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ - case 0x1ae: - modrm = x86_ldub_code(env, s); - switch (modrm) { - CASE_MODRM_MEM_OP(0): /* fxsave */ - if (!(s->cpuid_features & CPUID_FXSR) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_lea_modrm(env, s, modrm); - gen_helper_fxsave(tcg_env, s->A0); - break; - - CASE_MODRM_MEM_OP(1): /* fxrstor */ - if (!(s->cpuid_features & CPUID_FXSR) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_lea_modrm(env, s, modrm); - gen_helper_fxrstor(tcg_env, s->A0); - break; - - CASE_MODRM_MEM_OP(2): /* ldmxcsr */ - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { - goto illegal_op; - } - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); - break; - - CASE_MODRM_MEM_OP(3): /* stmxcsr */ - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { - goto illegal_op; - } - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_helper_update_mxcsr(tcg_env); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); - gen_op_st_v(s, MO_32, s->T0, s->A0); - break; - - CASE_MODRM_MEM_OP(4): /* xsave */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (prefixes & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - gen_helper_xsave(tcg_env, s->A0, s->tmp1_i64); - break; - - CASE_MODRM_MEM_OP(5): /* xrstor */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (prefixes & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - gen_helper_xrstor(tcg_env, s->A0, s->tmp1_i64); - /* XRSTOR is how MPX is enabled, which changes how - we translate. Thus we need to end the TB. */ - s->base.is_jmp = DISAS_EOB_NEXT; - break; - - CASE_MODRM_MEM_OP(6): /* xsaveopt / clwb */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - if (prefixes & PREFIX_DATA) { - /* clwb */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) { - goto illegal_op; - } - gen_nop_modrm(env, s, modrm); - } else { - /* xsaveopt */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 - || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - gen_helper_xsaveopt(tcg_env, s->A0, s->tmp1_i64); - } - break; - - CASE_MODRM_MEM_OP(7): /* clflush / clflushopt */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - if (prefixes & PREFIX_DATA) { - /* clflushopt */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) { - goto illegal_op; - } - } else { - /* clflush */ - if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) - || !(s->cpuid_features & CPUID_CLFLUSH)) { - goto illegal_op; - } - } - gen_nop_modrm(env, s, modrm); - break; - - case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */ - case 0xc8 ... 0xcf: /* rdgsbase (f3 0f ae /1) */ - case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */ - case 0xd8 ... 0xdf: /* wrgsbase (f3 0f ae /3) */ - if (CODE64(s) - && (prefixes & PREFIX_REPZ) - && !(prefixes & PREFIX_LOCK) - && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) { - TCGv base, treg, src, dst; - - /* Preserve hflags bits by testing CR4 at runtime. */ - tcg_gen_movi_i32(s->tmp2_i32, CR4_FSGSBASE_MASK); - gen_helper_cr4_testbit(tcg_env, s->tmp2_i32); - - base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; - treg = cpu_regs[(modrm & 7) | REX_B(s)]; - - if (modrm & 0x10) { - /* wr*base */ - dst = base, src = treg; - } else { - /* rd*base */ - dst = treg, src = base; - } - - if (s->dflag == MO_32) { - tcg_gen_ext32u_tl(dst, src); - } else { - tcg_gen_mov_tl(dst, src); - } - break; - } - goto unknown_op; - - case 0xf8 ... 0xff: /* sfence */ - if (!(s->cpuid_features & CPUID_SSE) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); - break; - case 0xe8 ... 0xef: /* lfence */ - if (!(s->cpuid_features & CPUID_SSE) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_LD_LD | TCG_BAR_SC); - break; - case 0xf0 ... 0xf7: /* mfence */ - if (!(s->cpuid_features & CPUID_SSE2) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - break; - - default: - goto unknown_op; - } - break; - case 0x1aa: /* rsm */ gen_svm_check_intercept(s, SVM_EXIT_RSM); if (!(s->flags & HF_SMM_MASK)) From 556c4c5cc44c3454f78d796b6050c6d574a35dd2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 09:52:30 +0200 Subject: [PATCH 1409/2884] target/i386: split X86_CHECK_prot into PE and VM86 checks SYSENTER is allowed in VM86 mode, but not in real mode. Split the check so that PE and !VM86 are covered by separate bits. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 9 +++++++-- target/i386/tcg/decode-new.h | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index b845a1b7b4..d0384a623b 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2566,8 +2566,13 @@ static void disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } } - if (decode.e.check & X86_CHECK_prot) { - if (!PE(s) || VM86(s)) { + if (decode.e.check & X86_CHECK_prot_or_vm86) { + if (!PE(s)) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_no_vm86) { + if (VM86(s)) { goto illegal_op; } } diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index bcac844ec4..1af28efaf5 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -150,8 +150,8 @@ typedef enum X86InsnCheck { X86_CHECK_i64 = 1, X86_CHECK_o64 = 2, - /* Fault outside protected mode */ - X86_CHECK_prot = 4, + /* Fault in vm86 mode */ + X86_CHECK_no_vm86 = 4, /* Privileged instruction checks */ X86_CHECK_cpl0 = 8, @@ -167,6 +167,10 @@ typedef enum X86InsnCheck { /* Fault if VEX.W=0 */ X86_CHECK_W1 = 256, + + /* Fault outside protected mode, possibly including vm86 mode */ + X86_CHECK_prot_or_vm86 = 512, + X86_CHECK_prot = X86_CHECK_prot_or_vm86 | X86_CHECK_no_vm86, } X86InsnCheck; typedef enum X86InsnSpecial { From ae541c0eb47f2fbcfd975c8e2fcb0e3a2613dc1c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 May 2024 10:49:26 +0200 Subject: [PATCH 1410/2884] target/i386: convert non-grouped, helper-based 2-byte opcodes These have very simple generators and no need for complex group decoding. Apart from LAR/LSL which are simplified to use gen_op_deposit_reg_v and movcond, the code is generally lifted from translate.c into the generators. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 48 +++++++--- target/i386/tcg/decode-new.h | 7 ++ target/i386/tcg/emit.c.inc | 157 ++++++++++++++++++++++++++++++- target/i386/tcg/seg_helper.c | 16 ++-- target/i386/tcg/translate.c | 148 ----------------------------- 5 files changed, 206 insertions(+), 170 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index d0384a623b..85e5422068 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -223,6 +223,8 @@ #define vex13 .vex_class = 13, #define chk(a) .check = X86_CHECK_##a, +#define chk2(a, b) .check = X86_CHECK_##a | X86_CHECK_##b, +#define chk3(a, b, c) .check = X86_CHECK_##a | X86_CHECK_##b | X86_CHECK_##c, #define svm(a) .intercept = SVM_EXIT_##a, .has_intercept = true, #define avx2_256 .vex_special = X86_VEX_AVX2_256, @@ -1027,6 +1029,12 @@ static void decode_MOV_CR_DR(DisasContext *s, CPUX86State *env, X86OpEntry *entr } static const X86OpEntry opcodes_0F[256] = { + [0x02] = X86_OP_ENTRYwr(LAR, G,v, E,w, chk(prot)), + [0x03] = X86_OP_ENTRYwr(LSL, G,v, E,w, chk(prot)), + [0x05] = X86_OP_ENTRY0(SYSCALL, chk(o64_intel)), + [0x06] = X86_OP_ENTRY0(CLTS, chk(cpl0) svm(WRITE_CR0)), + [0x07] = X86_OP_ENTRY0(SYSRET, chk3(o64_intel, prot, cpl0)), + [0x10] = X86_OP_GROUP0(0F10), [0x11] = X86_OP_GROUP0(0F11), [0x12] = X86_OP_GROUP0(0F12), @@ -1046,6 +1054,13 @@ static const X86OpEntry opcodes_0F[256] = { [0x22] = X86_OP_GROUPwr(MOV_CR_DR, C,y_d64, R,y_d64, zextT0 chk(cpl0) svm(WRITE_CR0)), [0x23] = X86_OP_GROUPwr(MOV_CR_DR, D,y_d64, R,y_d64, zextT0 chk(cpl0) svm(WRITE_DR0)), + [0x30] = X86_OP_ENTRY0(WRMSR, chk(cpl0)), + [0x31] = X86_OP_ENTRY0(RDTSC), + [0x32] = X86_OP_ENTRY0(RDMSR, chk(cpl0)), + [0x33] = X86_OP_ENTRY0(RDPMC), + [0x34] = X86_OP_ENTRY0(SYSENTER, chk2(i64_amd, prot_or_vm86)), + [0x35] = X86_OP_ENTRY0(SYSEXIT, chk3(i64_amd, prot, cpl0)), + [0x40] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), [0x41] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), [0x42] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), @@ -1102,6 +1117,7 @@ static const X86OpEntry opcodes_0F[256] = { [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), [0xa1] = X86_OP_ENTRYw(POP, FS, w), + [0xa2] = X86_OP_ENTRY0(CPUID), [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), @@ -1142,6 +1158,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xf6] = X86_OP_ENTRY3(PSADBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0xf7] = X86_OP_ENTRY3(MASKMOV, None,None, V,dq, U,dq, vex4_unal avx2_256 mmx p_00_66), + [0x08] = X86_OP_ENTRY0(NOP, svm(INVD)), + [0x09] = X86_OP_ENTRY0(NOP, svm(WBINVD)), [0x0b] = X86_OP_ENTRY0(UD), /* UD2 */ [0x0d] = X86_OP_ENTRY1(NOP, M,v), /* 3DNow! prefetch */ [0x0e] = X86_OP_ENTRY0(EMMS, cpuid(3DNOW)), /* femms */ @@ -1225,6 +1243,7 @@ static const X86OpEntry opcodes_0F[256] = { [0xa8] = X86_OP_ENTRYr(PUSH, GS, w), [0xa9] = X86_OP_ENTRYw(POP, GS, w), + [0xaa] = X86_OP_ENTRY0(RSM, chk(smm) svm(RSM)), [0xae] = X86_OP_GROUP0(group15), /* * It's slightly more efficient to put Ev operand in T0 and allow gen_IMUL3 @@ -2519,12 +2538,10 @@ static void disas_insn(DisasContext *s, CPUState *cpu) if (b == 0x0f) { b = x86_ldub_code(env, s); switch (b) { - case 0x00 ... 0x03: /* mostly privileged instructions */ - case 0x05 ... 0x09: + case 0x00 ... 0x01: /* mostly privileged instructions */ case 0x1a ... 0x1b: /* MPX */ - case 0x30 ... 0x35: /* more privileged instructions */ - case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */ - case 0xaa ... 0xad: /* RSM, SHRD */ + case 0xa3 ... 0xa5: /* BT, SHLD */ + case 0xab ... 0xad: /* BTS, SHRD */ case 0xb0 ... 0xb1: /* cmpxchg */ case 0xb3: /* btr */ case 0xb8: /* integer ops */ @@ -2556,13 +2573,18 @@ static void disas_insn(DisasContext *s, CPUState *cpu) /* Checks that result in #UD come first. */ if (decode.e.check) { - if (decode.e.check & X86_CHECK_i64) { - if (CODE64(s)) { + if (CODE64(s)) { + if (decode.e.check & X86_CHECK_i64) { goto illegal_op; } - } - if (decode.e.check & X86_CHECK_o64) { - if (!CODE64(s)) { + if ((decode.e.check & X86_CHECK_i64_amd) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { + goto illegal_op; + } + } else { + if (decode.e.check & X86_CHECK_o64) { + goto illegal_op; + } + if ((decode.e.check & X86_CHECK_o64_intel) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { goto illegal_op; } } @@ -2646,8 +2668,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) * exceptions if there is no memory operand). Exceptions are * vm86 checks (INTn, IRET, PUSHF/POPF), RSM and XSETBV (!). * - * RSM and XSETBV will be handled in the gen_* functions - * instead of using chk(). + * XSETBV will check for CPL0 in the gen_* function instead of using chk(). */ if (decode.e.check & X86_CHECK_cpl0) { if (CPL(s) != 0) { @@ -2659,6 +2680,9 @@ static void disas_insn(DisasContext *s, CPUState *cpu) tcg_constant_i32(decode.e.intercept)); } if (decode.e.check) { + if ((decode.e.check & X86_CHECK_smm) && !(s->flags & HF_SMM_MASK)) { + goto illegal_op; + } if ((decode.e.check & X86_CHECK_vm86_iopl) && VM86(s)) { if (IOPL(s) < 3) { goto gp_fault; diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index 1af28efaf5..e8d7d69954 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -171,6 +171,13 @@ typedef enum X86InsnCheck { /* Fault outside protected mode, possibly including vm86 mode */ X86_CHECK_prot_or_vm86 = 512, X86_CHECK_prot = X86_CHECK_prot_or_vm86 | X86_CHECK_no_vm86, + + /* Fault outside SMM */ + X86_CHECK_smm = 1024, + + /* Vendor-specific checks for Intel/AMD differences */ + X86_CHECK_i64_amd = 2048, + X86_CHECK_o64_intel = 4096, } X86InsnCheck; typedef enum X86InsnSpecial { diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 9649a6d84e..2cfa453711 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1414,6 +1414,13 @@ static void gen_CLI(DisasContext *s, X86DecodedInsn *decode) gen_reset_eflags(s, IF_MASK); } +static void gen_CLTS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_clts(tcg_env); + /* abort block because static cpu state changed */ + s->base.is_jmp = DISAS_EOB_NEXT; +} + static void gen_CMC(DisasContext *s, X86DecodedInsn *decode) { gen_compute_eflags(s); @@ -1538,6 +1545,13 @@ static void gen_CMPS(DisasContext *s, X86DecodedInsn *decode) } } +static void gen_CPUID(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_cpuid(tcg_env); +} + static void gen_CRC32(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1661,16 +1675,18 @@ static void gen_FXRSTOR(DisasContext *s, X86DecodedInsn *decode) { if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { gen_NM_exception(s); + } else { + gen_helper_fxrstor(tcg_env, s->A0); } - gen_helper_fxrstor(tcg_env, s->A0); } static void gen_FXSAVE(DisasContext *s, X86DecodedInsn *decode) { if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { gen_NM_exception(s); + } else { + gen_helper_fxsave(tcg_env, s->A0); } - gen_helper_fxsave(tcg_env, s->A0); } static void gen_HLT(DisasContext *s, X86DecodedInsn *decode) @@ -1981,6 +1997,23 @@ static void gen_LAHF(DisasContext *s, X86DecodedInsn *decode) tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); } +static void gen_LAR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv result = tcg_temp_new(); + TCGv dest; + + gen_compute_eflags(s); + gen_update_cc_op(s); + gen_helper_lar(result, tcg_env, s->T0); + + /* Perform writeback here to skip it if ZF=0. */ + decode->op[0].unit = X86_OP_SKIP; + dest = gen_op_deposit_reg_v(s, ot, decode->op[0].n, result, result); + tcg_gen_movcond_tl(TCG_COND_TSTNE, dest, cpu_cc_src, tcg_constant_tl(CC_Z), + result, dest); +} + static void gen_LDMXCSR(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); @@ -2079,6 +2112,23 @@ static void gen_LOOPNE(DisasContext *s, X86DecodedInsn *decode) gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); } +static void gen_LSL(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv result = tcg_temp_new(); + TCGv dest; + + gen_compute_eflags(s); + gen_update_cc_op(s); + gen_helper_lsl(result, tcg_env, s->T0); + + /* Perform writeback here to skip it if ZF=0. */ + decode->op[0].unit = X86_OP_SKIP; + dest = gen_op_deposit_reg_v(s, ot, decode->op[0].n, result, result); + tcg_gen_movcond_tl(TCG_COND_TSTNE, dest, cpu_cc_src, tcg_constant_tl(CC_Z), + result, dest); +} + static void gen_LSS(DisasContext *s, X86DecodedInsn *decode) { gen_lxx_seg(s, decode, R_SS); @@ -3122,6 +3172,41 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) } } +#ifdef CONFIG_USER_ONLY +static void gen_unreachable(DisasContext *s, X86DecodedInsn *decode) +{ + g_assert_not_reached(); +} +#endif + +#ifndef CONFIG_USER_ONLY +static void gen_RDMSR(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_rdmsr(tcg_env); +} +#else +#define gen_RDMSR gen_unreachable +#endif + +static void gen_RDPMC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + translator_io_start(&s->base); + gen_helper_rdpmc(tcg_env); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_RDTSC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + translator_io_start(&s->base); + gen_helper_rdtsc(tcg_env); +} + static void gen_RDxxBASE(DisasContext *s, X86DecodedInsn *decode) { TCGv base = cpu_seg_base[s->modrm & 8 ? R_GS : R_FS]; @@ -3294,6 +3379,17 @@ static void gen_RORX(DisasContext *s, X86DecodedInsn *decode) } } +#ifndef CONFIG_USER_ONLY +static void gen_RSM(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_rsm(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); + s->base.is_jmp = DISAS_EOB_ONLY; +} +#else +#define gen_RSM gen_UD +#endif + static void gen_SAHF(DisasContext *s, X86DecodedInsn *decode) { if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { @@ -3590,6 +3686,51 @@ static void gen_SUB(DisasContext *s, X86DecodedInsn *decode) prepare_update2_cc(decode, s, CC_OP_SUBB + ot); } +static void gen_SYSCALL(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_syscall(tcg_env, cur_insn_len_i32(s)); + if (LMA(s)) { + assume_cc_op(s, CC_OP_EFLAGS); + } + + /* + * TF handling for the syscall insn is different. The TF bit is checked + * after the syscall insn completes. This allows #DB to not be + * generated after one has entered CPL0 if TF is set in FMASK. + */ + s->base.is_jmp = DISAS_EOB_RECHECK_TF; +} + +static void gen_SYSENTER(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sysenter(tcg_env); + s->base.is_jmp = DISAS_EOB_ONLY; +} + +static void gen_SYSEXIT(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sysexit(tcg_env, tcg_constant_i32(s->dflag - 1)); + s->base.is_jmp = DISAS_EOB_ONLY; +} + +static void gen_SYSRET(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sysret(tcg_env, tcg_constant_i32(s->dflag - 1)); + if (LMA(s)) { + assume_cc_op(s, CC_OP_EFLAGS); + } + + /* + * TF handling for the sysret insn is different. The TF bit is checked + * after the sysret insn completes. This allows #DB to be + * generated "as if" the syscall insn in userspace has just + * completed. + */ + s->base.is_jmp = DISAS_EOB_RECHECK_TF; +} + static void gen_UD(DisasContext *s, X86DecodedInsn *decode) { gen_illegal_opcode(s); @@ -4086,6 +4227,18 @@ static void gen_WAIT(DisasContext *s, X86DecodedInsn *decode) } } +#ifndef CONFIG_USER_ONLY +static void gen_WRMSR(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_wrmsr(tcg_env); + s->base.is_jmp = DISAS_EOB_NEXT; +} +#else +#define gen_WRMSR gen_unreachable +#endif + static void gen_WRxxBASE(DisasContext *s, X86DecodedInsn *decode) { TCGv base = cpu_seg_base[s->modrm & 8 ? R_GS : R_FS]; diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 715db1f232..aee3d19f29 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -2265,11 +2265,11 @@ void helper_sysexit(CPUX86State *env, int dflag) target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) { unsigned int limit; - uint32_t e1, e2, eflags, selector; + uint32_t e1, e2, selector; int rpl, dpl, cpl, type; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env); + assert(CC_OP == CC_OP_EFLAGS); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2301,22 +2301,22 @@ target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) } if (dpl < cpl || dpl < rpl) { fail: - CC_SRC = eflags & ~CC_Z; + CC_SRC &= ~CC_Z; return 0; } } limit = get_seg_limit(e1, e2); - CC_SRC = eflags | CC_Z; + CC_SRC |= CC_Z; return limit; } target_ulong helper_lar(CPUX86State *env, target_ulong selector1) { - uint32_t e1, e2, eflags, selector; + uint32_t e1, e2, selector; int rpl, dpl, cpl, type; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env); + assert(CC_OP == CC_OP_EFLAGS); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2351,11 +2351,11 @@ target_ulong helper_lar(CPUX86State *env, target_ulong selector1) } if (dpl < cpl || dpl < rpl) { fail: - CC_SRC = eflags & ~CC_Z; + CC_SRC &= ~CC_Z; return 0; } } - CC_SRC = eflags | CC_Z; + CC_SRC |= CC_Z; return e2 & 0x00f0ff00; } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ebae745ecb..4b2f748802 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -246,7 +246,6 @@ STUB_HELPER(mwait, TCGv_env env, TCGv_i32 pc_ofs) STUB_HELPER(outb, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outw, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outl, TCGv_env env, TCGv_i32 port, TCGv_i32 val) -STUB_HELPER(rdmsr, TCGv_env env) STUB_HELPER(stgi, TCGv_env env) STUB_HELPER(svm_check_intercept, TCGv_env env, TCGv_i32 type) STUB_HELPER(vmload, TCGv_env env, TCGv_i32 aflag) @@ -254,7 +253,6 @@ STUB_HELPER(vmmcall, TCGv_env env) STUB_HELPER(vmrun, TCGv_env env, TCGv_i32 aflag, TCGv_i32 pc_ofs) STUB_HELPER(vmsave, TCGv_env env, TCGv_i32 aflag) STUB_HELPER(write_crN, TCGv_env env, TCGv_i32 reg, TCGv val) -STUB_HELPER(wrmsr, TCGv_env env) #endif static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num); @@ -3470,97 +3468,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_op_mov_reg_v(s, ot, reg, s->T0); break; - case 0x130: /* wrmsr */ - case 0x132: /* rdmsr */ - if (check_cpl0(s)) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - if (b & 2) { - gen_helper_rdmsr(tcg_env); - } else { - gen_helper_wrmsr(tcg_env); - s->base.is_jmp = DISAS_EOB_NEXT; - } - } - break; - case 0x131: /* rdtsc */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - translator_io_start(&s->base); - gen_helper_rdtsc(tcg_env); - break; - case 0x133: /* rdpmc */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_rdpmc(tcg_env); - s->base.is_jmp = DISAS_NORETURN; - break; - case 0x134: /* sysenter */ - /* For AMD SYSENTER is not valid in long mode */ - if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - if (!PE(s)) { - gen_exception_gpf(s); - } else { - gen_helper_sysenter(tcg_env); - s->base.is_jmp = DISAS_EOB_ONLY; - } - break; - case 0x135: /* sysexit */ - /* For AMD SYSEXIT is not valid in long mode */ - if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - if (!PE(s) || CPL(s) != 0) { - gen_exception_gpf(s); - } else { - gen_helper_sysexit(tcg_env, tcg_constant_i32(dflag - 1)); - s->base.is_jmp = DISAS_EOB_ONLY; - } - break; - case 0x105: /* syscall */ - /* For Intel SYSCALL is only valid in long mode */ - if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_syscall(tcg_env, cur_insn_len_i32(s)); - /* condition codes are modified only in long mode */ - if (LMA(s)) { - assume_cc_op(s, CC_OP_EFLAGS); - } - /* TF handling for the syscall insn is different. The TF bit is checked - after the syscall insn completes. This allows #DB to not be - generated after one has entered CPL0 if TF is set in FMASK. */ - s->base.is_jmp = DISAS_EOB_RECHECK_TF; - break; - case 0x107: /* sysret */ - /* For Intel SYSRET is only valid in long mode */ - if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - if (!PE(s) || CPL(s) != 0) { - gen_exception_gpf(s); - } else { - gen_helper_sysret(tcg_env, tcg_constant_i32(dflag - 1)); - /* condition codes are modified only in long mode */ - if (LMA(s)) { - assume_cc_op(s, CC_OP_EFLAGS); - } - /* TF handling for the sysret insn is different. The TF bit is - checked after the sysret insn completes. This allows #DB to be - generated "as if" the syscall insn in userspace has just - completed. */ - s->base.is_jmp = DISAS_EOB_RECHECK_TF; - } - break; - case 0x1a2: /* cpuid */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_cpuid(tcg_env); - break; case 0x100: modrm = x86_ldub_code(env, s); mod = (modrm >> 6) & 3; @@ -3964,39 +3871,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } break; - case 0x108: /* invd */ - case 0x109: /* wbinvd; wbnoinvd with REPZ prefix */ - if (check_cpl0(s)) { - gen_svm_check_intercept(s, (b & 1) ? SVM_EXIT_WBINVD : SVM_EXIT_INVD); - /* nothing to do */ - } - break; - case 0x102: /* lar */ - case 0x103: /* lsl */ - { - TCGLabel *label1; - TCGv t0; - if (!PE(s) || VM86(s)) - goto illegal_op; - ot = dflag != MO_16 ? MO_32 : MO_16; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ld_modrm(env, s, modrm, MO_16); - t0 = tcg_temp_new(); - gen_update_cc_op(s); - if (b == 0x102) { - gen_helper_lar(t0, tcg_env, s->T0); - } else { - gen_helper_lsl(t0, tcg_env, s->T0); - } - tcg_gen_andi_tl(s->tmp0, cpu_cc_src, CC_Z); - label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); - gen_op_mov_reg_v(s, ot, reg, t0); - gen_set_label(label1); - set_cc_op(s, CC_OP_EFLAGS); - } - break; case 0x11a: modrm = x86_ldub_code(env, s); if (s->flags & HF_MPX_EN_MASK) { @@ -4188,28 +4062,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_nop_modrm(env, s, modrm); break; - - case 0x106: /* clts */ - if (check_cpl0(s)) { - gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); - gen_helper_clts(tcg_env); - /* abort block because static cpu state changed */ - s->base.is_jmp = DISAS_EOB_NEXT; - } - break; - case 0x1aa: /* rsm */ - gen_svm_check_intercept(s, SVM_EXIT_RSM); - if (!(s->flags & HF_SMM_MASK)) - goto illegal_op; -#ifdef CONFIG_USER_ONLY - /* we should not be in SMM mode */ - g_assert_not_reached(); -#else - gen_helper_rsm(tcg_env); - assume_cc_op(s, CC_OP_EFLAGS); -#endif /* CONFIG_USER_ONLY */ - s->base.is_jmp = DISAS_EOB_ONLY; - break; case 0x1b8: /* SSE4.2 popcnt */ if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != PREFIX_REPZ) From 87b2037b65d07d43edff1c4e177e9136dff32896 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 May 2024 10:38:29 +0200 Subject: [PATCH 1411/2884] target/i386: pull load/writeback out of gen_shiftd_rm_T1 Use gen_ld_modrm/gen_st_modrm, moving them and gen_shift_flags to the caller. This way, gen_shiftd_rm_T1 becomes something that the new decoder can call. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 55 ++++++++++--------------------------- 1 file changed, 14 insertions(+), 41 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4b2f748802..5200b578a0 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -535,15 +535,6 @@ static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) tcg_gen_qemu_st_tl(t0, a0, s->mem_index, idx | MO_LE); } -static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) -{ - if (d == OR_TMP0) { - gen_op_st_v(s, idx, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, idx, d, s->T0); - } -} - static void gen_update_eip_next(DisasContext *s) { assert(s->pc_save != -1); @@ -1486,19 +1477,12 @@ static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, } /* XXX: add faster immediate case */ -static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, +static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, bool is_right, TCGv count_in) { target_ulong mask = (ot == MO_64 ? 63 : 31); TCGv count; - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - count = tcg_temp_new(); tcg_gen_andi_tl(count, count_in, mask); @@ -1563,10 +1547,7 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, break; } - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - gen_shift_flags(s, ot, s->T0, s->tmp0, count, is_right); + return count; } #define X86_MAX_INSN_LENGTH 15 @@ -3076,9 +3057,9 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) CPUX86State *env = cpu_env(cpu); int prefixes = s->prefix; MemOp dflag = s->dflag; - int shift; + TCGv shift; MemOp ot; - int modrm, reg, rm, mod, op, opreg, val; + int modrm, reg, rm, mod, op, val; /* now check op code */ switch (b) { @@ -3244,39 +3225,31 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) /* shifts */ case 0x1a4: /* shld imm */ op = 0; - shift = 1; + shift = NULL; goto do_shiftd; case 0x1a5: /* shld cl */ op = 0; - shift = 0; + shift = cpu_regs[R_ECX]; goto do_shiftd; case 0x1ac: /* shrd imm */ op = 1; - shift = 1; + shift = NULL; goto do_shiftd; case 0x1ad: /* shrd cl */ op = 1; - shift = 0; + shift = cpu_regs[R_ECX]; do_shiftd: ot = dflag; modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); reg = ((modrm >> 3) & 7) | REX_R(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = rm; + gen_ld_modrm(env, s, modrm, ot); + if (!shift) { + shift = tcg_constant_tl(x86_ldub_code(env, s)); } gen_op_mov_v_reg(s, ot, s->T1, reg); - - if (shift) { - TCGv imm = tcg_constant_tl(x86_ldub_code(env, s)); - gen_shiftd_rm_T1(s, ot, opreg, op, imm); - } else { - gen_shiftd_rm_T1(s, ot, opreg, op, cpu_regs[R_ECX]); - } + shift = gen_shiftd_rm_T1(s, ot, op, shift); + gen_st_modrm(env, s, modrm, ot); + gen_shift_flags(s, ot, s->T0, s->tmp0, shift, op); break; /************************/ From e4e5981daf37146473b30b9219f78796d15320c5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 10:15:05 +0200 Subject: [PATCH 1412/2884] target/i386: adapt gen_shift_count for SHLD/SHRD SHLD/SHRD can have 3 register operands - s->T0, s->T1 and either 1 or CL - and therefore decode->op[2] is taken by the low part of the register being shifted. Pass X86_OP_* to gen_shift_count from its current callers and hardcode cpu_regs[R_ECX] as the shift count. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2cfa453711..0b794c88ce 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2878,16 +2878,16 @@ static void gen_PUSHF(DisasContext *s, X86DecodedInsn *decode) } static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, - bool *can_be_zero, TCGv *count) + bool *can_be_zero, TCGv *count, int unit) { MemOp ot = decode->op[0].ot; int mask = (ot <= MO_32 ? 0x1f : 0x3f); *can_be_zero = false; - switch (decode->op[2].unit) { + switch (unit) { case X86_OP_INT: *count = tcg_temp_new(); - tcg_gen_andi_tl(*count, s->T1, mask); + tcg_gen_andi_tl(*count, cpu_regs[R_ECX], mask); *can_be_zero = true; break; @@ -3072,7 +3072,7 @@ static void gen_RCL(DisasContext *s, X86DecodedInsn *decode) bool have_1bit_cin, can_be_zero; TCGv count; TCGLabel *zero_label = NULL; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); TCGv low, high, low_count; if (!count) { @@ -3124,7 +3124,7 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) bool have_1bit_cin, can_be_zero; TCGv count; TCGLabel *zero_label = NULL; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); TCGv low, high, high_count; if (!count) { @@ -3302,7 +3302,7 @@ static void gen_ROL(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); TCGv_i32 temp32, count32; TCGv old = tcg_temp_new(); @@ -3330,7 +3330,7 @@ static void gen_ROR(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); TCGv_i32 temp32, count32; TCGv old = tcg_temp_new(); @@ -3442,7 +3442,7 @@ static void gen_SAR(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); if (!count) { return; @@ -3570,7 +3570,7 @@ static void gen_SHL(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); if (!count) { return; @@ -3602,7 +3602,7 @@ static void gen_SHR(DisasContext *s, X86DecodedInsn *decode) { bool can_be_zero; TCGv count; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count); + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); if (!count) { return; From 647690274053a35dbaa2617f01d432d6ba4e76a8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 11:46:59 +0200 Subject: [PATCH 1413/2884] target/i386: convert SHLD/SHRD to new decoder Use the same flag generation code as SHL and SHR, but use the existing gen_shiftd_rm_T1 function to compute the result as well as CC_SRC. Decoding-wise, SHLD/SHRD by immediate count as a 4 operand instruction because s->T0 and s->T1 actually occupy three op slots. The infrastructure used by opcodes in the 0F 3A table works fine. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 8 ++- target/i386/tcg/emit.c.inc | 42 ++++++++++++++++ target/i386/tcg/translate.c | 84 +------------------------------- 3 files changed, 50 insertions(+), 84 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 85e5422068..598112d93d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1118,6 +1118,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), [0xa1] = X86_OP_ENTRYw(POP, FS, w), [0xa2] = X86_OP_ENTRY0(CPUID), + [0xa4] = X86_OP_ENTRY4(SHLD, E,v, 2op,v, G,v), + [0xa5] = X86_OP_ENTRY3(SHLD, E,v, 2op,v, G,v), [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), @@ -1244,6 +1246,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xa8] = X86_OP_ENTRYr(PUSH, GS, w), [0xa9] = X86_OP_ENTRYw(POP, GS, w), [0xaa] = X86_OP_ENTRY0(RSM, chk(smm) svm(RSM)), + [0xac] = X86_OP_ENTRY4(SHRD, E,v, 2op,v, G,v), + [0xad] = X86_OP_ENTRY3(SHRD, E,v, 2op,v, G,v), [0xae] = X86_OP_GROUP0(group15), /* * It's slightly more efficient to put Ev operand in T0 and allow gen_IMUL3 @@ -2540,8 +2544,8 @@ static void disas_insn(DisasContext *s, CPUState *cpu) switch (b) { case 0x00 ... 0x01: /* mostly privileged instructions */ case 0x1a ... 0x1b: /* MPX */ - case 0xa3 ... 0xa5: /* BT, SHLD */ - case 0xab ... 0xad: /* BTS, SHRD */ + case 0xa3: /* bt */ + case 0xab: /* bts */ case 0xb0 ... 0xb1: /* cmpxchg */ case 0xb3: /* btr */ case 0xb8: /* integer ops */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 0b794c88ce..2b1f0f1b9b 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3588,6 +3588,27 @@ static void gen_SHL(DisasContext *s, X86DecodedInsn *decode) } } +static void gen_SHLD(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + int unit = decode->e.op3 == X86_TYPE_I ? X86_OP_IMM : X86_OP_INT; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = s->tmp0; + gen_shiftd_rm_T1(s, ot, false, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot); + } else { + decode->cc_op = CC_OP_SHLB + ot; + } +} + static void gen_SHLX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -3620,6 +3641,27 @@ static void gen_SHR(DisasContext *s, X86DecodedInsn *decode) } } +static void gen_SHRD(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + int unit = decode->e.op3 == X86_TYPE_I ? X86_OP_IMM : X86_OP_INT; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = s->tmp0; + gen_shiftd_rm_T1(s, ot, true, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); + } else { + decode->cc_op = CC_OP_SARB + ot; + } +} + static void gen_SHRX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5200b578a0..33058db4e3 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1434,57 +1434,11 @@ static bool check_cpl0(DisasContext *s) return false; } -static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, - TCGv shm1, TCGv count, bool is_right) -{ - TCGv_i32 z32, s32, oldop; - TCGv z_tl; - - /* Store the results into the CC variables. If we know that the - variable must be dead, store unconditionally. Otherwise we'll - need to not disrupt the current contents. */ - z_tl = tcg_constant_tl(0); - if (cc_op_live[s->cc_op] & USES_CC_DST) { - tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, - result, cpu_cc_dst); - } else { - tcg_gen_mov_tl(cpu_cc_dst, result); - } - if (cc_op_live[s->cc_op] & USES_CC_SRC) { - tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_src, count, z_tl, - shm1, cpu_cc_src); - } else { - tcg_gen_mov_tl(cpu_cc_src, shm1); - } - - /* Get the two potential CC_OP values into temporaries. */ - tcg_gen_movi_i32(s->tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); - if (s->cc_op == CC_OP_DYNAMIC) { - oldop = cpu_cc_op; - } else { - tcg_gen_movi_i32(s->tmp3_i32, s->cc_op); - oldop = s->tmp3_i32; - } - - /* Conditionally store the CC_OP value. */ - z32 = tcg_constant_i32(0); - s32 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(s32, count); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, s->tmp2_i32, oldop); - - /* The CC_OP value is no longer predictable. */ - set_cc_op(s, CC_OP_DYNAMIC); -} - /* XXX: add faster immediate case */ -static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, - bool is_right, TCGv count_in) +static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, + bool is_right, TCGv count) { target_ulong mask = (ot == MO_64 ? 63 : 31); - TCGv count; - - count = tcg_temp_new(); - tcg_gen_andi_tl(count, count_in, mask); switch (ot) { case MO_16: @@ -1546,8 +1500,6 @@ static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, tcg_gen_or_tl(s->T0, s->T0, s->T1); break; } - - return count; } #define X86_MAX_INSN_LENGTH 15 @@ -3057,7 +3009,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) CPUX86State *env = cpu_env(cpu); int prefixes = s->prefix; MemOp dflag = s->dflag; - TCGv shift; MemOp ot; int modrm, reg, rm, mod, op, val; @@ -3221,37 +3172,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } break; - /**************************/ - /* shifts */ - case 0x1a4: /* shld imm */ - op = 0; - shift = NULL; - goto do_shiftd; - case 0x1a5: /* shld cl */ - op = 0; - shift = cpu_regs[R_ECX]; - goto do_shiftd; - case 0x1ac: /* shrd imm */ - op = 1; - shift = NULL; - goto do_shiftd; - case 0x1ad: /* shrd cl */ - op = 1; - shift = cpu_regs[R_ECX]; - do_shiftd: - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ld_modrm(env, s, modrm, ot); - if (!shift) { - shift = tcg_constant_tl(x86_ldub_code(env, s)); - } - gen_op_mov_v_reg(s, ot, s->T1, reg); - shift = gen_shiftd_rm_T1(s, ot, op, shift); - gen_st_modrm(env, s, modrm, ot); - gen_shift_flags(s, ot, s->T0, s->tmp0, shift, op); - break; - /************************/ /* bit operations */ case 0x1ba: /* bt/bts/btr/btc Gv, im */ From 11ffaf8c73aae1a70f4640ada14a437a78d06efb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 15:11:41 +0200 Subject: [PATCH 1414/2884] target/i386: convert LZCNT/TZCNT/BSF/BSR/POPCNT to new decoder Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 52 +++++++++++++++++++- target/i386/tcg/decode-new.h | 1 + target/i386/tcg/emit.c.inc | 82 ++++++++++++++++++++++++++++++++ target/i386/tcg/translate.c | 74 ---------------------------- 4 files changed, 133 insertions(+), 76 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 598112d93d..e159280b51 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -450,6 +450,50 @@ static void decode_0F7F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui *entry = *decode_by_prefix(s, opcodes_0F7F); } +static void decode_0FB8(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry popcnt = + X86_OP_ENTRYwr(POPCNT, G,v, E,v, cpuid(POPCNT) zextT0); + + if (s->prefix & PREFIX_REPZ) { + *entry = popcnt; + } else { + memset(entry, 0, sizeof(*entry)); + } +} + +static void decode_0FBC(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* For BSF, pass 2op as the third operand so that we can use zextT0 */ + static const X86OpEntry opcodes_0FBC[4] = { + X86_OP_ENTRY3(BSF, G,v, E,v, 2op,v, zextT0), + X86_OP_ENTRY3(BSF, G,v, E,v, 2op,v, zextT0), /* 0x66 */ + X86_OP_ENTRYwr(TZCNT, G,v, E,v, zextT0), /* 0xf3 */ + X86_OP_ENTRY3(BSF, G,v, E,v, 2op,v, zextT0), /* 0xf2 */ + }; + if (!(s->cpuid_ext3_features & CPUID_EXT3_ABM)) { + *entry = opcodes_0FBC[0]; + } else { + *entry = *decode_by_prefix(s, opcodes_0FBC); + } +} + +static void decode_0FBD(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* For BSR, pass 2op as the third operand so that we can use zextT0 */ + static const X86OpEntry opcodes_0FBD[4] = { + X86_OP_ENTRY3(BSR, G,v, E,v, 2op,v, zextT0), + X86_OP_ENTRY3(BSR, G,v, E,v, 2op,v, zextT0), /* 0x66 */ + X86_OP_ENTRYwr(LZCNT, G,v, E,v, zextT0), /* 0xf3 */ + X86_OP_ENTRY3(BSR, G,v, E,v, 2op,v, zextT0), /* 0xf2 */ + }; + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { + *entry = opcodes_0FBD[0]; + } else { + *entry = *decode_by_prefix(s, opcodes_0FBD); + } +} + static void decode_0FD6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { static const X86OpEntry movq[4] = { @@ -1255,8 +1299,11 @@ static const X86OpEntry opcodes_0F[256] = { */ [0xaf] = X86_OP_ENTRY3(IMUL3, G,v, E,v, 2op,v, sextT0), + [0xb8] = X86_OP_GROUP0(0FB8), /* decoded as modrm, which is visible as a difference between page fault and #UD */ [0xb9] = X86_OP_ENTRYr(UD, nop,v), /* UD1 */ + [0xbc] = X86_OP_GROUP0(0FBC), + [0xbd] = X86_OP_GROUP0(0FBD), [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */ [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */ @@ -2158,6 +2205,8 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) return (s->cpuid_ext_features & CPUID_EXT_MOVBE); case X86_FEAT_PCLMULQDQ: return (s->cpuid_ext_features & CPUID_EXT_PCLMULQDQ); + case X86_FEAT_POPCNT: + return (s->cpuid_ext_features & CPUID_EXT_POPCNT); case X86_FEAT_SSE: return (s->cpuid_features & CPUID_SSE); case X86_FEAT_SSE2: @@ -2548,8 +2597,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu) case 0xab: /* bts */ case 0xb0 ... 0xb1: /* cmpxchg */ case 0xb3: /* btr */ - case 0xb8: /* integer ops */ - case 0xba ... 0xbd: /* integer ops */ + case 0xba ... 0xbb: /* grp8, btc */ case 0xc0 ... 0xc1: /* xadd */ case 0xc7: /* grp9 */ disas_insn_old(s, cpu, b + 0x100); diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index e8d7d69954..f9bf9a6041 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -120,6 +120,7 @@ typedef enum X86CPUIDFeature { X86_FEAT_FXSR, X86_FEAT_MOVBE, X86_FEAT_PCLMULQDQ, + X86_FEAT_POPCNT, X86_FEAT_SHA_NI, X86_FEAT_SSE, X86_FEAT_SSE2, diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 2b1f0f1b9b..99b08eb719 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1333,6 +1333,47 @@ static void gen_BOUND(DisasContext *s, X86DecodedInsn *decode) } } +/* Non-standard convention - on entry T0 is zero-extended input, T1 is the output. */ +static void gen_BSF(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* Only the Z bit is defined and it is related to the input. */ + decode->cc_dst = tcg_temp_new(); + decode->cc_op = CC_OP_LOGICB + ot; + tcg_gen_mov_tl(decode->cc_dst, s->T0); + + /* + * The manual says that the output is undefined when the + * input is zero, but real hardware leaves it unchanged, and + * real programs appear to depend on that. Accomplish this + * by passing the output as the value to return upon zero. + */ + tcg_gen_ctz_tl(s->T0, s->T0, s->T1); +} + +/* Non-standard convention - on entry T0 is zero-extended input, T1 is the output. */ +static void gen_BSR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* Only the Z bit is defined and it is related to the input. */ + decode->cc_dst = tcg_temp_new(); + decode->cc_op = CC_OP_LOGICB + ot; + tcg_gen_mov_tl(decode->cc_dst, s->T0); + + /* + * The manual says that the output is undefined when the + * input is zero, but real hardware leaves it unchanged, and + * real programs appear to depend on that. Accomplish this + * by passing the output as the value to return upon zero. + * Plus, return the bit index of the first 1 bit. + */ + tcg_gen_xori_tl(s->T1, s->T1, TARGET_LONG_BITS - 1); + tcg_gen_clz_tl(s->T0, s->T0, s->T1); + tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); +} + static void gen_BSWAP(DisasContext *s, X86DecodedInsn *decode) { #ifdef TARGET_X86_64 @@ -2134,6 +2175,24 @@ static void gen_LSS(DisasContext *s, X86DecodedInsn *decode) gen_lxx_seg(s, decode, R_SS); } +static void gen_LZCNT(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* C bit (cc_src) is defined related to the input. */ + decode->cc_src = tcg_temp_new(); + decode->cc_dst = s->T0; + decode->cc_op = CC_OP_BMILGB + ot; + tcg_gen_mov_tl(decode->cc_src, s->T0); + + /* + * Reduce the target_ulong result by the number of zeros that + * we expect to find at the top. + */ + tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); + tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - (8 << ot)); +} + static void gen_MFENCE(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); @@ -2692,6 +2751,15 @@ static void gen_POPA(DisasContext *s, X86DecodedInsn *decode) gen_popa(s); } +static void gen_POPCNT(DisasContext *s, X86DecodedInsn *decode) +{ + decode->cc_src = tcg_temp_new(); + decode->cc_op = CC_OP_POPCNT; + + tcg_gen_mov_tl(decode->cc_src, s->T0); + tcg_gen_ctpop_tl(s->T0, s->T0); +} + static void gen_POPF(DisasContext *s, X86DecodedInsn *decode) { MemOp ot; @@ -3773,6 +3841,20 @@ static void gen_SYSRET(DisasContext *s, X86DecodedInsn *decode) s->base.is_jmp = DISAS_EOB_RECHECK_TF; } +static void gen_TZCNT(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* C bit (cc_src) is defined related to the input. */ + decode->cc_src = tcg_temp_new(); + decode->cc_dst = s->T0; + decode->cc_op = CC_OP_BMILGB + ot; + tcg_gen_mov_tl(decode->cc_src, s->T0); + + /* A zero input returns the operand size. */ + tcg_gen_ctzi_tl(s->T0, s->T0, 8 << ot); +} + static void gen_UD(DisasContext *s, X86DecodedInsn *decode) { gen_illegal_opcode(s); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 33058db4e3..68a11f8178 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -823,11 +823,6 @@ static void gen_movs(DisasContext *s, MemOp ot) gen_op_add_reg(s, s->aflag, R_EDI, dshift); } -static void gen_op_update1_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - static void gen_op_update2_cc(DisasContext *s) { tcg_gen_mov_tl(cpu_cc_src, s->T1); @@ -3311,56 +3306,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) break; } break; - case 0x1bc: /* bsf / tzcnt */ - case 0x1bd: /* bsr / lzcnt */ - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ld_modrm(env, s, modrm, ot); - gen_extu(ot, s->T0); - - /* Note that lzcnt and tzcnt are in different extensions. */ - if ((prefixes & PREFIX_REPZ) - && (b & 1 - ? s->cpuid_ext3_features & CPUID_EXT3_ABM - : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { - int size = 8 << ot; - /* For lzcnt/tzcnt, C bit is defined related to the input. */ - tcg_gen_mov_tl(cpu_cc_src, s->T0); - if (b & 1) { - /* For lzcnt, reduce the target_ulong result by the - number of zeros that we expect to find at the top. */ - tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); - tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - size); - } else { - /* For tzcnt, a zero input must return the operand size. */ - tcg_gen_ctzi_tl(s->T0, s->T0, size); - } - /* For lzcnt/tzcnt, Z bit is defined related to the result. */ - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_BMILGB + ot); - } else { - /* For bsr/bsf, only the Z bit is defined and it is related - to the input and not the result. */ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, CC_OP_LOGICB + ot); - - /* ??? The manual says that the output is undefined when the - input is zero, but real hardware leaves it unchanged, and - real programs appear to depend on that. Accomplish this - by passing the output as the value to return upon zero. */ - if (b & 1) { - /* For bsr, return the bit index of the first 1 bit, - not the count of leading zeros. */ - tcg_gen_xori_tl(s->T1, cpu_regs[reg], TARGET_LONG_BITS - 1); - tcg_gen_clz_tl(s->T0, s->T0, s->T1); - tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); - } else { - tcg_gen_ctz_tl(s->T0, s->T0, cpu_regs[reg]); - } - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; case 0x100: modrm = x86_ldub_code(env, s); mod = (modrm >> 6) & 3; @@ -3955,25 +3900,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } gen_nop_modrm(env, s, modrm); break; - case 0x1b8: /* SSE4.2 popcnt */ - if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != - PREFIX_REPZ) - goto illegal_op; - if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT)) - goto illegal_op; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - ot = dflag; - gen_ld_modrm(env, s, modrm, ot); - gen_extu(ot, s->T0); - tcg_gen_mov_tl(cpu_cc_src, s->T0); - tcg_gen_ctpop_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - - set_cc_op(s, CC_OP_POPCNT); - break; default: g_assert_not_reached(); } From 7b1f25ac3a6dd482d2637a3b4fbbfc326dac0bc8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 14 May 2024 16:40:32 +0200 Subject: [PATCH 1415/2884] target/i386: convert XADD to new decoder Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 3 ++- target/i386/tcg/emit.c.inc | 24 ++++++++++++++++++++++ target/i386/tcg/translate.c | 35 -------------------------------- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index e159280b51..e8b3a12d32 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1171,6 +1171,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ + [0xc0] = X86_OP_ENTRY2(XADD, E,b, G,b, lock), + [0xc1] = X86_OP_ENTRY2(XADD, E,v, G,v, lock), [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), [0xc3] = X86_OP_ENTRY3(MOV, EM,y,G,y, None,None, cpuid(SSE2)), /* MOVNTI */ [0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66), @@ -2598,7 +2600,6 @@ static void disas_insn(DisasContext *s, CPUState *cpu) case 0xb0 ... 0xb1: /* cmpxchg */ case 0xb3: /* btr */ case 0xba ... 0xbb: /* grp8, btc */ - case 0xc0 ... 0xc1: /* xadd */ case 0xc7: /* grp9 */ disas_insn_old(s, cpu, b + 0x100); return; diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 99b08eb719..3b92d04c0f 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -4372,6 +4372,30 @@ static void gen_WRxxBASE(DisasContext *s, X86DecodedInsn *decode) tcg_gen_mov_tl(base, s->T0); } +static void gen_XADD(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + decode->cc_dst = tcg_temp_new(); + decode->cc_src = s->T1; + decode->cc_op = CC_OP_ADDB + ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_add_tl(s->T0, s->A0, s->T1, s->mem_index, ot | MO_LE); + tcg_gen_add_tl(decode->cc_dst, s->T0, s->T1); + } else { + tcg_gen_add_tl(decode->cc_dst, s->T0, s->T1); + /* + * NOTE: writing memory first is important for MMU exceptions, + * but "new result" wins for XADD AX, AX. + */ + gen_writeback(s, decode, 0, decode->cc_dst); + } + if (decode->op[0].has_ea || decode->op[2].n != decode->op[0].n) { + gen_writeback(s, decode, 2, s->T0); + } +} + static void gen_XCHG(DisasContext *s, X86DecodedInsn *decode) { if (s->prefix & PREFIX_LOCK) { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 68a11f8178..5d9312bb48 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -823,12 +823,6 @@ static void gen_movs(DisasContext *s, MemOp ot) gen_op_add_reg(s, s->aflag, R_EDI, dshift); } -static void gen_op_update2_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_src, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - /* compute all eflags to reg */ static void gen_mov_eflags(DisasContext *s, TCGv reg) { @@ -3011,35 +3005,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) switch (b) { /**************************/ /* arith & logic */ - case 0x1c0: - case 0x1c1: /* xadd Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - gen_op_mov_v_reg(s, ot, s->T0, reg); - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, ot, s->T1, rm); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, ot, reg, s->T1); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - if (s->prefix & PREFIX_LOCK) { - tcg_gen_atomic_fetch_add_tl(s->T1, s->A0, s->T0, - s->mem_index, ot | MO_LE); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - } else { - gen_op_ld_v(s, ot, s->T1, s->A0); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - gen_op_st_v(s, ot, s->T0, s->A0); - } - gen_op_mov_reg_v(s, ot, reg, s->T1); - } - gen_op_update2_cc(s); - set_cc_op(s, CC_OP_ADDB + ot); - break; case 0x1b0: case 0x1b1: /* cmpxchg Ev, Gv */ { From 0c4da54883336e43af13ad3b3c33dac646cfa9fb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 May 2024 14:41:19 +0200 Subject: [PATCH 1416/2884] target/i386: convert CMPXCHG to new decoder Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 3 +- target/i386/tcg/emit.c.inc | 51 +++++++++++++++++++++ target/i386/tcg/translate.c | 79 -------------------------------- 3 files changed, 53 insertions(+), 80 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index e8b3a12d32..0d846c32c2 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1165,6 +1165,8 @@ static const X86OpEntry opcodes_0F[256] = { [0xa4] = X86_OP_ENTRY4(SHLD, E,v, 2op,v, G,v), [0xa5] = X86_OP_ENTRY3(SHLD, E,v, 2op,v, G,v), + [0xb0] = X86_OP_ENTRY2(CMPXCHG,E,b, G,b, lock), + [0xb1] = X86_OP_ENTRY2(CMPXCHG,E,v, G,v, lock), [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), [0xb5] = X86_OP_ENTRY3(LGS, G,v, EM,p, None, None), @@ -2597,7 +2599,6 @@ static void disas_insn(DisasContext *s, CPUState *cpu) case 0x1a ... 0x1b: /* MPX */ case 0xa3: /* bt */ case 0xab: /* bts */ - case 0xb0 ... 0xb1: /* cmpxchg */ case 0xb3: /* btr */ case 0xba ... 0xbb: /* grp8, btc */ case 0xc7: /* grp9 */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 3b92d04c0f..11faa70b5e 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1586,6 +1586,57 @@ static void gen_CMPS(DisasContext *s, X86DecodedInsn *decode) } } +static void gen_CMPXCHG(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + TCGv cmpv = tcg_temp_new(); + TCGv oldv = tcg_temp_new(); + TCGv newv = tcg_temp_new(); + TCGv dest; + + tcg_gen_ext_tl(cmpv, cpu_regs[R_EAX], ot); + tcg_gen_ext_tl(newv, s->T1, ot); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_ext_tl(oldv, s->T0, ot); + if (decode->op[0].has_ea) { + /* + * Perform an unconditional store cycle like physical cpu; + * must be before changing accumulator to ensure + * idempotency if the store faults and the instruction + * is restarted + */ + tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); + gen_op_st_v(s, ot, newv, s->A0); + } else { + /* + * Unlike the memory case, where "the destination operand receives + * a write cycle without regard to the result of the comparison", + * rm must not be touched altogether if the write fails, including + * not zero-extending it on 64-bit processors. So, precompute + * the result of a successful writeback and perform the movcond + * directly on cpu_regs. In case rm is part of RAX, note that this + * movcond and the one below are mutually exclusive is executed. + */ + dest = gen_op_deposit_reg_v(s, ot, decode->op[0].n, newv, newv); + tcg_gen_movcond_tl(TCG_COND_EQ, dest, oldv, cmpv, newv, dest); + } + decode->op[0].unit = X86_OP_SKIP; + } + + /* Write RAX only if the cmpxchg fails. */ + dest = gen_op_deposit_reg_v(s, ot, R_EAX, s->T0, oldv); + tcg_gen_movcond_tl(TCG_COND_NE, dest, oldv, cmpv, s->T0, dest); + + tcg_gen_mov_tl(s->cc_srcT, cmpv); + tcg_gen_sub_tl(cmpv, cmpv, oldv); + decode->cc_dst = cmpv; + decode->cc_src = oldv; + decode->cc_op = CC_OP_SUBB + ot; +} + static void gen_CPUID(DisasContext *s, X86DecodedInsn *decode) { gen_update_cc_op(s); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5d9312bb48..ad1819815a 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -434,13 +434,6 @@ static inline MemOp mo_stacksize(DisasContext *s) return CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16; } -/* Select size 8 if lsb of B is clear, else OT. Used for decoding - byte vs word opcodes. */ -static inline MemOp mo_b_d(int b, MemOp ot) -{ - return b & 1 ? ot : MO_8; -} - /* Compute the result of writing t0 to the OT-sized register REG. * * If DEST is NULL, store the result into the register and return the @@ -715,11 +708,6 @@ static TCGv gen_ext_tl(TCGv dst, TCGv src, MemOp size, bool sign) return dst; } -static void gen_extu(MemOp ot, TCGv reg) -{ - gen_ext_tl(reg, reg, ot, false); -} - static void gen_exts(MemOp ot, TCGv reg) { gen_ext_tl(reg, reg, ot, true); @@ -3003,73 +2991,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) /* now check op code */ switch (b) { - /**************************/ - /* arith & logic */ - case 0x1b0: - case 0x1b1: /* cmpxchg Ev, Gv */ - { - TCGv oldv, newv, cmpv, dest; - - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - oldv = tcg_temp_new(); - newv = tcg_temp_new(); - cmpv = tcg_temp_new(); - gen_op_mov_v_reg(s, ot, newv, reg); - tcg_gen_mov_tl(cmpv, cpu_regs[R_EAX]); - gen_extu(ot, cmpv); - if (s->prefix & PREFIX_LOCK) { - if (mod == 3) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, - s->mem_index, ot | MO_LE); - } else { - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, ot, oldv, rm); - gen_extu(ot, oldv); - - /* - * Unlike the memory case, where "the destination operand receives - * a write cycle without regard to the result of the comparison", - * rm must not be touched altogether if the write fails, including - * not zero-extending it on 64-bit processors. So, precompute - * the result of a successful writeback and perform the movcond - * directly on cpu_regs. Also need to write accumulator first, in - * case rm is part of RAX too. - */ - dest = gen_op_deposit_reg_v(s, ot, rm, newv, newv); - tcg_gen_movcond_tl(TCG_COND_EQ, dest, oldv, cmpv, newv, dest); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, oldv, s->A0); - - /* - * Perform an unconditional store cycle like physical cpu; - * must be before changing accumulator to ensure - * idempotency if the store faults and the instruction - * is restarted - */ - tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); - gen_op_st_v(s, ot, newv, s->A0); - } - } - /* - * Write EAX only if the cmpxchg fails; reuse newv as the destination, - * since it's dead here. - */ - dest = gen_op_deposit_reg_v(s, ot, R_EAX, newv, oldv); - tcg_gen_movcond_tl(TCG_COND_EQ, dest, oldv, cmpv, dest, newv); - tcg_gen_mov_tl(cpu_cc_src, oldv); - tcg_gen_mov_tl(s->cc_srcT, cmpv); - tcg_gen_sub_tl(cpu_cc_dst, cmpv, oldv); - set_cc_op(s, CC_OP_SUBB + ot); - } - break; case 0x1c7: /* cmpxchg8b */ modrm = x86_ldub_code(env, s); mod = (modrm >> 6) & 3; From 109238a8d97cd8e85ca614109724a0b1222b21f5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 5 Jun 2024 20:09:44 +0200 Subject: [PATCH 1417/2884] target/i386: SEV: do not assume machine->cgs is SEV There can be other confidential computing classes that are not derived from sev-common. Avoid aborting when encountering them. Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index c40562dce3..30b83f1d77 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1712,7 +1712,9 @@ void sev_es_set_reset_vector(CPUState *cpu) { X86CPU *x86; CPUX86State *env; - SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + SevCommonState *sev_common = SEV_COMMON( + object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON)); /* Only update if we have valid reset information */ if (!sev_common || !sev_common->reset_data_valid) { From e65152d5483b2c847ec7a947ed52650152cfdcc0 Mon Sep 17 00:00:00 2001 From: Masato Imai Date: Mon, 17 Jun 2024 22:46:04 +0800 Subject: [PATCH 1418/2884] migration/dirtyrate: Fix segmentation fault Since the kvm_dirty_ring_enabled function accesses a null kvm_state pointer when the KVM acceleration parameter is not specified, running calc_dirty_rate with the -r or -b option causes a segmentation fault. Signed-off-by: Masato Imai Message-ID: <20240507025010.1968881-1-mii@sfc.wide.ad.jp> [Assert kvm_state when kvm_dirty_ring_enabled was called to fix it. - Hyman] Signed-off-by: Hyman Huang --- accel/kvm/kvm-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 009b49de44..854cb86b22 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2329,7 +2329,7 @@ bool kvm_vcpu_id_is_valid(int vcpu_id) bool kvm_dirty_ring_enabled(void) { - return kvm_state->kvm_dirty_ring_size ? true : false; + return kvm_state && kvm_state->kvm_dirty_ring_size; } static void query_stats_cb(StatsResultList **result, StatsTarget target, From e6b45b6bb9ce721119857507a66768b06e8836a2 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 13 Jun 2024 09:34:59 +0800 Subject: [PATCH 1419/2884] hw/dma: Enhance error handling in loading description Loading a description from memory may cause a bus-error. In this case, the DMA should stop working, set the error flag, and return the failure value. When calling the loading a description function, it should be noticed that the function may return a failure value. Breaking the loop in this case is one of the possible ways to handle it. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Edgar E. Iglesias Signed-off-by: Edgar E. Iglesias --- hw/dma/xilinx_axidma.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 0ae056ed06..ad307994c2 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -71,8 +71,11 @@ enum { enum { DMASR_HALTED = 1, DMASR_IDLE = 2, + DMASR_SLVERR = 1 << 5, + DMASR_DECERR = 1 << 6, DMASR_IOC_IRQ = 1 << 12, DMASR_DLY_IRQ = 1 << 13, + DMASR_ERR_IRQ = 1 << 14, DMASR_IRQ_MASK = 7 << 12 }; @@ -190,17 +193,32 @@ static inline int streamid_from_addr(hwaddr addr) return sid; } -static void stream_desc_load(struct Stream *s, hwaddr addr) +static MemTxResult stream_desc_load(struct Stream *s, hwaddr addr) { struct SDesc *d = &s->desc; - address_space_read(&s->dma->as, addr, MEMTXATTRS_UNSPECIFIED, d, sizeof *d); + MemTxResult result = address_space_read(&s->dma->as, + addr, MEMTXATTRS_UNSPECIFIED, + d, sizeof *d); + if (result != MEMTX_OK) { + if (result == MEMTX_DECODE_ERROR) { + s->regs[R_DMASR] |= DMASR_DECERR; + } else { + s->regs[R_DMASR] |= DMASR_SLVERR; + } + + s->regs[R_DMACR] &= ~DMACR_RUNSTOP; + s->regs[R_DMASR] |= DMASR_HALTED; + s->regs[R_DMASR] |= DMASR_ERR_IRQ; + return result; + } /* Convert from LE into host endianness. */ d->buffer_address = le64_to_cpu(d->buffer_address); d->nxtdesc = le64_to_cpu(d->nxtdesc); d->control = le32_to_cpu(d->control); d->status = le32_to_cpu(d->status); + return result; } static void stream_desc_store(struct Stream *s, hwaddr addr) @@ -279,7 +297,9 @@ static void stream_process_mem2s(struct Stream *s, StreamSink *tx_data_dev, } while (1) { - stream_desc_load(s, s->regs[R_CURDESC]); + if (MEMTX_OK != stream_desc_load(s, s->regs[R_CURDESC])) { + break; + } if (s->desc.status & SDESC_STATUS_COMPLETE) { s->regs[R_DMASR] |= DMASR_HALTED; @@ -336,7 +356,9 @@ static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, } while (len) { - stream_desc_load(s, s->regs[R_CURDESC]); + if (MEMTX_OK != stream_desc_load(s, s->regs[R_CURDESC])) { + break; + } if (s->desc.status & SDESC_STATUS_COMPLETE) { s->regs[R_DMASR] |= DMASR_HALTED; From 8e25dddaf93bd9b2fa79e4b5229e7b3a97a86b7f Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 13 Jun 2024 09:35:00 +0800 Subject: [PATCH 1420/2884] hw/dma: Add a trace log for a description loading failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to a description loading failure, adding a trace log makes observing the DMA behavior easy. Signed-off-by: Fea.Wang Reviewed-by: Edgar E. Iglesias Reviewed-by: Frank Chang Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias --- hw/dma/trace-events | 3 +++ hw/dma/xilinx_axidma.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 3c47df54e4..4c09790f9a 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -44,3 +44,6 @@ pl330_debug_exec_stall(void) "stall of debug instruction not implemented" pl330_iomem_write(uint32_t offset, uint32_t value) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 pl330_iomem_write_clr(int i) "event interrupt lowered %d" pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 + +# xilinx_axidma.c +xilinx_axidma_loading_desc_fail(uint32_t res) "error:%u" diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index ad307994c2..c9cfc3169b 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -36,6 +36,7 @@ #include "sysemu/dma.h" #include "hw/stream.h" #include "qom/object.h" +#include "trace.h" #define D(x) @@ -201,6 +202,8 @@ static MemTxResult stream_desc_load(struct Stream *s, hwaddr addr) addr, MEMTXATTRS_UNSPECIFIED, d, sizeof *d); if (result != MEMTX_OK) { + trace_xilinx_axidma_loading_desc_fail(result); + if (result == MEMTX_DECODE_ERROR) { s->regs[R_DMASR] |= DMASR_DECERR; } else { From 3a6d374b754b4b345195ff6846eeaffedc96a7c5 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 13 Jun 2024 09:35:01 +0800 Subject: [PATCH 1421/2884] hw/net: Fix the transmission return size Fix the transmission return size because not all bytes could be transmitted successfully. So, return a successful length instead of a constant value. Signed-off-by: Fea.Wang Reviewed-by: Edgar E. Iglesias Reviewed-by: Frank Chang Signed-off-by: Edgar E. Iglesias --- hw/net/xilinx_axienet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 7d1fd37b4a..05d41bd548 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -847,7 +847,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) axienet_eth_rx_notify(s); enet_update_irq(s); - return size; + return s->rxpos; } static size_t From 792b4fdd4eb8197bd6eb9e80a1dfaf0cb3b54aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:34:35 +0100 Subject: [PATCH 1422/2884] hw/i386/pc: Deprecate 2.4 to 2.12 pc-i440fx machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to the commit c7437f0ddb "docs/about: Mark the old pc-i440fx-2.0 - 2.3 machine types as deprecated", deprecate the 2.4 to 2.12 machines. Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-2-philmd@linaro.org> --- docs/about/deprecated.rst | 4 ++-- hw/i386/pc_piix.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index b62877e51c..66a5f43626 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -212,8 +212,8 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-2.0`` up to ``pc-i440fx-2.3`` (since 8.2) -''''''''''''''''''''''''''''''''''''''''''''''''''''' +``pc-i440fx-2.0`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These old machine types are quite neglected nowadays and thus might have various pitfalls with regards to live migration. Use a newer machine type diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index ebb51de380..02878060d0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -742,6 +742,7 @@ DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, static void pc_i440fx_2_12_machine_options(MachineClass *m) { pc_i440fx_3_0_machine_options(m); + m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } @@ -847,7 +848,6 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m) { pc_i440fx_2_4_machine_options(m); m->hw_version = "2.3.0"; - m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, hw_compat_2_3, hw_compat_2_3_len); compat_props_add(m->compat_props, pc_compat_2_3, pc_compat_2_3_len); } From dbe442ad4849aa9bb11535f2a68c0bfa7c51ce86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:26:33 +0100 Subject: [PATCH 1423/2884] hw/i386/pc: Remove deprecated pc-i440fx-2.0 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc-i440fx-2.0 machine was deprecated for the 8.2 release (see commit c7437f0ddb "docs/about: Mark the old pc-i440fx-2.0 - 2.3 machine types as deprecated"), time to remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-3-philmd@linaro.org> --- docs/about/deprecated.rst | 2 +- docs/about/removed-features.rst | 2 +- hw/i386/pc.c | 15 ------------- hw/i386/pc_piix.c | 37 --------------------------------- include/hw/i386/pc.h | 3 --- 5 files changed, 2 insertions(+), 57 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 66a5f43626..e12eab1f72 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -212,7 +212,7 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-2.0`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) +``pc-i440fx-2.1`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These old machine types are quite neglected nowadays and thus might have diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index eaae82b8a5..f84bfaf2b6 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -948,7 +948,7 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-i440fx-1.7`` (removed in 4.0 up to 8.2) +``pc-0.10`` up to ``pc-i440fx-2.0`` (removed in 4.0 up to 9.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These machine types were very old and likely could not be used for live diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 0469af00a7..d0f2628d46 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -320,21 +320,6 @@ GlobalProperty pc_compat_2_1[] = { }; const size_t pc_compat_2_1_len = G_N_ELEMENTS(pc_compat_2_1); -GlobalProperty pc_compat_2_0[] = { - PC_CPU_MODEL_IDS("2.0.0") - { "virtio-scsi-pci", "any_layout", "off" }, - { "PIIX4_PM", "memory-hotplug-support", "off" }, - { "apic", "version", "0x11" }, - { "nec-usb-xhci", "superspeed-ports-first", "off" }, - { "nec-usb-xhci", "force-pcie-endcap", "on" }, - { "pci-serial", "prog_if", "0" }, - { "pci-serial-2x", "prog_if", "0" }, - { "pci-serial-4x", "prog_if", "0" }, - { "virtio-net-pci", "guest_announce", "off" }, - { "ICH9-LPC", "memory-hotplug-support", "off" }, -}; -const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 02878060d0..a750a0e6ab 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -441,11 +441,6 @@ static void pc_compat_2_1_fn(MachineState *machine) x86_cpu_change_kvm_default("svm", NULL); } -static void pc_compat_2_0_fn(MachineState *machine) -{ - pc_compat_2_1_fn(machine); -} - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { @@ -887,38 +882,6 @@ static void pc_i440fx_2_1_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1_fn, pc_i440fx_2_1_machine_options); -static void pc_i440fx_2_0_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_1_machine_options(m); - m->hw_version = "2.0.0"; - compat_props_add(m->compat_props, pc_compat_2_0, pc_compat_2_0_len); - pcmc->smbios_legacy_mode = true; - pcmc->has_reserved_memory = false; - /* This value depends on the actual DSDT and SSDT compiled into - * the source QEMU; unfortunately it depends on the binary and - * not on the machine type, so we cannot make pc-i440fx-1.7 work on - * both QEMU 1.7 and QEMU 2.0. - * - * Large variations cause migration to fail for more than one - * consecutive value of the "-smp" maxcpus option. - * - * For small variations of the kind caused by different iasl versions, - * the 4k rounding usually leaves slack. However, there could be still - * one or two values that break. For QEMU 1.7 and QEMU 2.0 the - * slack is only ~10 bytes before one "-smp maxcpus" value breaks! - * - * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on - * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. - */ - pcmc->legacy_acpi_table_size = 6652; - pcmc->acpi_data_size = 0x10000; -} - -DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0_fn, - pc_i440fx_2_0_machine_options); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index ca7904ac2c..3e606949fb 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -318,9 +318,6 @@ extern const size_t pc_compat_2_2_len; extern GlobalProperty pc_compat_2_1[]; extern const size_t pc_compat_2_1_len; -extern GlobalProperty pc_compat_2_0[]; -extern const size_t pc_compat_2_0_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ From 931863ac6f2d0d573936cdcc2bd6bfa15ffa187c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:15:33 +0100 Subject: [PATCH 1424/2884] hw/usb/hcd-xhci: Remove XHCI_FLAG_FORCE_PCIE_ENDCAP flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XHCI_FLAG_FORCE_PCIE_ENDCAP was only used by the pc-i440fx-2.0 machine, which got removed. Remove it and simplify usb_xhci_pci_realize(). Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-4-philmd@linaro.org> --- hw/usb/hcd-xhci-nec.c | 2 -- hw/usb/hcd-xhci-pci.c | 3 +-- hw/usb/hcd-xhci.h | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 328e5bfe7c..5d5b069cf9 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -43,8 +43,6 @@ static Property nec_xhci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("superspeed-ports-first", XHCINecState, flags, XHCI_FLAG_SS_FIRST, true), - DEFINE_PROP_BIT("force-pcie-endcap", XHCINecState, flags, - XHCI_FLAG_FORCE_PCIE_ENDCAP, false), DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index 4423983308..cbad96f393 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -148,8 +148,7 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp) PCI_BASE_ADDRESS_MEM_TYPE_64, &s->xhci.mem); - if (pci_bus_is_express(pci_get_bus(dev)) || - xhci_get_flag(&s->xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { + if (pci_bus_is_express(pci_get_bus(dev))) { ret = pcie_endpoint_cap_init(dev, 0xa0); assert(ret > 0); } diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index 98f598382a..1efa4858fb 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -37,7 +37,6 @@ typedef struct XHCIEPContext XHCIEPContext; enum xhci_flags { XHCI_FLAG_SS_FIRST = 1, - XHCI_FLAG_FORCE_PCIE_ENDCAP, XHCI_FLAG_ENABLE_STREAMS, }; From b9599519a011d83896b906e43458a5e76dad6237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:18:40 +0100 Subject: [PATCH 1425/2884] hw/usb/hcd-xhci: Remove XHCI_FLAG_SS_FIRST flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XHCI_FLAG_SS_FIRST was only used by the pc-i440fx-2.0 machine, which got removed. Remove it and simplify various functions in hcd-xhci.c. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-5-philmd@linaro.org> --- hw/usb/hcd-xhci-nec.c | 2 -- hw/usb/hcd-xhci-pci.c | 1 - hw/usb/hcd-xhci.c | 42 ++++++++---------------------------------- hw/usb/hcd-xhci.h | 3 +-- 4 files changed, 9 insertions(+), 39 deletions(-) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 5d5b069cf9..0c063b3697 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -41,8 +41,6 @@ struct XHCINecState { static Property nec_xhci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), - DEFINE_PROP_BIT("superspeed-ports-first", XHCINecState, flags, - XHCI_FLAG_SS_FIRST, true), DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index cbad96f393..264d7ebb77 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -242,7 +242,6 @@ static void qemu_xhci_instance_init(Object *obj) s->msix = ON_OFF_AUTO_AUTO; xhci->numintrs = XHCI_MAXINTRS; xhci->numslots = XHCI_MAXSLOTS; - xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST); } static const TypeInfo qemu_xhci_info = { diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index ad40232eb6..b6411f0bda 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -541,18 +541,10 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index + xhci->numports_3; - } else { - index = uport->index; - } + index = uport->index + xhci->numports_3; break; case USB_SPEED_SUPER: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index; - } else { - index = uport->index + xhci->numports_2; - } + index = uport->index; break; default: return NULL; @@ -2779,11 +2771,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x20425355; /* "USB " */ break; case 0x28: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_2<<8) | (xhci->numports_3+1); - } else { - ret = (xhci->numports_2<<8) | 1; - } + ret = (xhci->numports_2 << 8) | (xhci->numports_3 + 1); break; case 0x2c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -2795,11 +2783,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x20425355; /* "USB " */ break; case 0x38: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_3<<8) | 1; - } else { - ret = (xhci->numports_3<<8) | (xhci->numports_2+1); - } + ret = (xhci->numports_3 << 8) | 1; break; case 0x3c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -3349,13 +3333,8 @@ static void usb_xhci_init(XHCIState *xhci) for (i = 0; i < usbports; i++) { speedmask = 0; if (i < xhci->numports_2) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i + xhci->numports_3]; - port->portnr = i + 1 + xhci->numports_3; - } else { - port = &xhci->ports[i]; - port->portnr = i + 1; - } + port = &xhci->ports[i + xhci->numports_3]; + port->portnr = i + 1 + xhci->numports_3; port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_LOW | @@ -3366,13 +3345,8 @@ static void usb_xhci_init(XHCIState *xhci) speedmask |= port->speedmask; } if (i < xhci->numports_3) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i]; - port->portnr = i + 1; - } else { - port = &xhci->ports[i + xhci->numports_2]; - port->portnr = i + 1 + xhci->numports_2; - } + port = &xhci->ports[i]; + port->portnr = i + 1; port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_SUPER; assert(i < XHCI_MAXPORTS); diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index 1efa4858fb..fe16d7ad05 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -36,8 +36,7 @@ typedef struct XHCIStreamContext XHCIStreamContext; typedef struct XHCIEPContext XHCIEPContext; enum xhci_flags { - XHCI_FLAG_SS_FIRST = 1, - XHCI_FLAG_ENABLE_STREAMS, + XHCI_FLAG_ENABLE_STREAMS = 1, }; typedef enum TRBType { From ea7a74ab2e38e7f2ac19d79974d43471cfc8cdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:30:01 +0100 Subject: [PATCH 1426/2884] hw/i386/acpi: Remove PCMachineClass::legacy_acpi_table_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCMachineClass::legacy_acpi_table_size was only used by the pc-i440fx-2.0 machine, which got removed. Remove it and simplify acpi_build(). Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240617071118.60464-6-philmd@linaro.org> --- hw/i386/acpi-build.c | 63 +++++++++----------------------------------- include/hw/i386/pc.h | 1 - 2 files changed, 12 insertions(+), 52 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 53f804ac16..f5d74e2b4b 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -85,7 +85,6 @@ * a little bit, there should be plenty of free space since the DSDT * shrunk by ~1.5k between QEMU 2.0 and QEMU 2.1. */ -#define ACPI_BUILD_LEGACY_CPU_AML_SIZE 97 #define ACPI_BUILD_ALIGN_SIZE 0x1000 #define ACPI_BUILD_TABLE_SIZE 0x20000 @@ -2499,13 +2498,12 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) X86MachineState *x86ms = X86_MACHINE(machine); DeviceState *iommu = pcms->iommu; GArray *table_offsets; - unsigned facs, dsdt, rsdt, fadt; + unsigned facs, dsdt, rsdt; AcpiPmInfo pm; AcpiMiscInfo misc; AcpiMcfgInfo mcfg; Range pci_hole = {}, pci_hole64 = {}; uint8_t *u; - size_t aml_len = 0; GArray *tables_blob = tables->table_data; AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL }; Object *vmgenid_dev; @@ -2551,19 +2549,12 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci_hole, &pci_hole64, machine); - /* Count the size of the DSDT and SSDT, we will need it for legacy - * sizing of ACPI tables. - */ - aml_len += tables_blob->len - dsdt; - /* ACPI tables pointed to by RSDT */ - fadt = tables_blob->len; acpi_add_table(table_offsets, tables_blob); pm.fadt.facs_tbl_offset = &facs; pm.fadt.dsdt_tbl_offset = &dsdt; pm.fadt.xdsdt_tbl_offset = &dsdt; build_fadt(tables_blob, tables->linker, &pm.fadt, oem_id, oem_table_id); - aml_len += tables_blob->len - fadt; acpi_add_table(table_offsets, tables_blob); acpi_build_madt(tables_blob, tables->linker, x86ms, @@ -2694,49 +2685,19 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) * too simple to be enough. 4k turned out to be too small an * alignment very soon, and in fact it is almost impossible to * keep the table size stable for all (max_cpus, max_memory_slots) - * combinations. So the table size is always 64k for pc-i440fx-2.1 - * and we give an error if the table grows beyond that limit. - * - * We still have the problem of migrating from "-M pc-i440fx-2.0". For - * that, we exploit the fact that QEMU 2.1 generates _smaller_ tables - * than 2.0 and we can always pad the smaller tables with zeros. We can - * then use the exact size of the 2.0 tables. - * - * All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration. + * combinations. */ - if (pcmc->legacy_acpi_table_size) { - /* Subtracting aml_len gives the size of fixed tables. Then add the - * size of the PIIX4 DSDT/SSDT in QEMU 2.0. - */ - int legacy_aml_len = - pcmc->legacy_acpi_table_size + - ACPI_BUILD_LEGACY_CPU_AML_SIZE * x86ms->apic_id_limit; - int legacy_table_size = - ROUND_UP(tables_blob->len - aml_len + legacy_aml_len, - ACPI_BUILD_ALIGN_SIZE); - if ((tables_blob->len > legacy_table_size) && - !pcmc->resizable_acpi_blob) { - /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ - warn_report("ACPI table size %u exceeds %d bytes," - " migration may not work", - tables_blob->len, legacy_table_size); - error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges.\n"); - } - g_array_set_size(tables_blob, legacy_table_size); - } else { - /* Make sure we have a buffer in case we need to resize the tables. */ - if ((tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) && - !pcmc->resizable_acpi_blob) { - /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ - warn_report("ACPI table size %u exceeds %d bytes," - " migration may not work", - tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); - error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges.\n"); - } - acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); + /* Make sure we have a buffer in case we need to resize the tables. */ + if ((tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) && + !pcmc->resizable_acpi_blob) { + /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ + warn_report("ACPI table size %u exceeds %d bytes," + " migration may not work", + tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); + error_printf("Try removing CPUs, NUMA nodes, memory slots" + " or PCI bridges.\n"); } + acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 3e606949fb..434e531160 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -103,7 +103,6 @@ struct PCMachineClass { /* ACPI compat: */ bool has_acpi_build; bool rsdp_in_ram; - int legacy_acpi_table_size; unsigned acpi_data_size; int pci_root_uid; From d0226c582307c8a0403fb34f4b3e9b305d4a4563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Mar 2024 09:57:14 +0100 Subject: [PATCH 1427/2884] hw/acpi/ich9: Remove 'memory-hotplug-support' property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No external code sets the 'memory-hotplug-support' property, remove it. Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-7-philmd@linaro.org> --- hw/acpi/ich9.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 573d032e8e..9b605af21a 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -351,21 +351,6 @@ static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } -static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - - return s->pm.acpi_memory_hotplug.is_enabled; -} - -static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, - Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - - s->pm.acpi_memory_hotplug.is_enabled = value; -} - static bool ich9_pm_get_cpu_hotplug_legacy(Object *obj, Error **errp) { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); @@ -445,9 +430,6 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) NULL, NULL, pm); object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, &gpe0_len, OBJ_PROP_FLAG_READ); - object_property_add_bool(obj, "memory-hotplug-support", - ich9_pm_get_memory_hotplug_support, - ich9_pm_set_memory_hotplug_support); object_property_add_bool(obj, "cpu-hotplug-legacy", ich9_pm_get_cpu_hotplug_legacy, ich9_pm_set_cpu_hotplug_legacy); From 2529ea2d561ea9fe359fb19ebdcfeb8b6cddd219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Mar 2024 10:03:38 +0100 Subject: [PATCH 1428/2884] hw/acpi/ich9: Remove dead code related to 'acpi_memory_hotplug' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit acpi_memory_hotplug::is_enabled is set to %true once via ich9_lpc_initfn() -> ich9_pm_add_properties(). No need to check it, so remove now dead code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-8-philmd@linaro.org> --- hw/acpi/ich9.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 9b605af21a..02d8546bd3 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -153,17 +153,10 @@ static int ich9_pm_post_load(void *opaque, int version_id) .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ } -static bool vmstate_test_use_memhp(void *opaque) -{ - ICH9LPCPMRegs *s = opaque; - return s->acpi_memory_hotplug.is_enabled; -} - static const VMStateDescription vmstate_memhp_state = { .name = "ich9_pm/memhp", .version_id = 1, .minimum_version_id = 1, - .needed = vmstate_test_use_memhp, .fields = (const VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() @@ -335,11 +328,9 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) legacy_acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); - if (pm->acpi_memory_hotplug.is_enabled) { - acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->acpi_memory_hotplug, - ACPI_MEMORY_HOTPLUG_BASE); - } + acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), + &pm->acpi_memory_hotplug, + ACPI_MEMORY_HOTPLUG_BASE); } static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name, @@ -460,12 +451,7 @@ void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && - !lpc->pm.acpi_memory_hotplug.is_enabled) { - error_setg(errp, - "memory hotplug is not enabled: %s.memory-hotplug-support " - "is not set", object_get_typename(OBJECT(lpc))); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { uint64_t negotiated = lpc->smi_negotiated_features; if (negotiated & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT) && @@ -509,8 +495,7 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - if (lpc->pm.acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_request_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug, dev, errp); @@ -545,8 +530,7 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - if (lpc->pm.acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&lpc->pm.acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && !lpc->pm.cpu_hotplug_legacy) { From 80685972e3d8628192631055e8808b69f3df3cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:28:31 +0100 Subject: [PATCH 1429/2884] hw/i386/pc: Remove deprecated pc-i440fx-2.1 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc-i440fx-2.1 machine was deprecated for the 8.2 release (see commit c7437f0ddb "docs/about: Mark the old pc-i440fx-2.0 - 2.3 machine types as deprecated"), time to remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-9-philmd@linaro.org> --- docs/about/deprecated.rst | 2 +- docs/about/removed-features.rst | 2 +- hw/i386/pc.c | 7 ------- hw/i386/pc_piix.c | 23 ----------------------- include/hw/i386/pc.h | 3 --- 5 files changed, 2 insertions(+), 35 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index e12eab1f72..e332eb46e9 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -212,7 +212,7 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-2.1`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) +``pc-i440fx-2.2`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These old machine types are quite neglected nowadays and thus might have diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index f84bfaf2b6..347e04e50e 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -948,7 +948,7 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-i440fx-2.0`` (removed in 4.0 up to 9.0) +``pc-0.10`` up to ``pc-i440fx-2.1`` (removed in 4.0 up to 9.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These machine types were very old and likely could not be used for live diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d0f2628d46..b0b8ecd64d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -313,13 +313,6 @@ GlobalProperty pc_compat_2_2[] = { }; const size_t pc_compat_2_2_len = G_N_ELEMENTS(pc_compat_2_2); -GlobalProperty pc_compat_2_1[] = { - PC_CPU_MODEL_IDS("2.1.0") - { "coreduo" "-" TYPE_X86_CPU, "vmx", "on" }, - { "core2duo" "-" TYPE_X86_CPU, "vmx", "on" }, -}; -const size_t pc_compat_2_1_len = G_N_ELEMENTS(pc_compat_2_1); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index a750a0e6ab..e0b421dd51 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -66,7 +66,6 @@ #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" #include "hw/i386/acpi-build.h" -#include "kvm/kvm-cpu.h" #include "target/i386/cpu.h" #define XEN_IOAPIC_NUM_PIRQS 128ULL @@ -435,12 +434,6 @@ static void pc_compat_2_2_fn(MachineState *machine) pc_compat_2_3_fn(machine); } -static void pc_compat_2_1_fn(MachineState *machine) -{ - pc_compat_2_2_fn(machine); - x86_cpu_change_kvm_default("svm", NULL); -} - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { @@ -866,22 +859,6 @@ static void pc_i440fx_2_2_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2_fn, pc_i440fx_2_2_machine_options); -static void pc_i440fx_2_1_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_2_machine_options(m); - m->hw_version = "2.1.0"; - m->default_display = NULL; - compat_props_add(m->compat_props, hw_compat_2_1, hw_compat_2_1_len); - compat_props_add(m->compat_props, pc_compat_2_1, pc_compat_2_1_len); - pcmc->smbios_uuid_encoded = false; - pcmc->enforce_aligned_dimm = false; -} - -DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1_fn, - pc_i440fx_2_1_machine_options); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 434e531160..0d730318fe 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -314,9 +314,6 @@ extern const size_t pc_compat_2_3_len; extern GlobalProperty pc_compat_2_2[]; extern const size_t pc_compat_2_2_len; -extern GlobalProperty pc_compat_2_1[]; -extern const size_t pc_compat_2_1_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ From 63f16d97c6a15532bf4fe17ac9874b94baf58ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:10:39 +0100 Subject: [PATCH 1430/2884] target/i386/kvm: Remove x86_cpu_change_kvm_default() and 'kvm-cpu.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit x86_cpu_change_kvm_default() was only used out of kvm-cpu.c by the pc-i440fx-2.1 machine, which got removed. Make it static, and remove its declaration. "kvm-cpu.h" is now empty, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-10-philmd@linaro.org> --- target/i386/kvm/kvm-cpu.c | 3 +-- target/i386/kvm/kvm-cpu.h | 41 --------------------------------------- 2 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 target/i386/kvm/kvm-cpu.h diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index f76972e47e..f9b99b5f50 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -10,7 +10,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "host-cpu.h" -#include "kvm-cpu.h" #include "qapi/error.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -178,7 +177,7 @@ static PropValue kvm_default_props[] = { /* * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -void x86_cpu_change_kvm_default(const char *prop, const char *value) +static void x86_cpu_change_kvm_default(const char *prop, const char *value) { PropValue *pv; for (pv = kvm_default_props; pv->prop; pv++) { diff --git a/target/i386/kvm/kvm-cpu.h b/target/i386/kvm/kvm-cpu.h deleted file mode 100644 index e858ca21e5..0000000000 --- a/target/i386/kvm/kvm-cpu.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * i386 KVM CPU type and functions - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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 . - */ - -#ifndef KVM_CPU_H -#define KVM_CPU_H - -#ifdef CONFIG_KVM -/* - * Change the value of a KVM-specific default - * - * If value is NULL, no default will be set and the original - * value from the CPU model table will be kept. - * - * It is valid to call this function only for properties that - * are already present in the kvm_default_props table. - */ -void x86_cpu_change_kvm_default(const char *prop, const char *value); - -#else /* !CONFIG_KVM */ - -#define x86_cpu_change_kvm_default(a, b) - -#endif /* CONFIG_KVM */ - -#endif /* KVM_CPU_H */ From 05814d9663a0bf995286c822696b406330f8f602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:44:02 +0100 Subject: [PATCH 1431/2884] hw/i386/pc: Remove PCMachineClass::smbios_uuid_encoded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCMachineClass::smbios_uuid_encoded was only used by the pc-i440fx-2.1 machine, which got removed. It is now always true, remove it. Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-11-philmd@linaro.org> --- hw/i386/fw_cfg.c | 3 +-- hw/i386/pc.c | 1 - include/hw/i386/pc.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 6e0d9945d0..f9e8af3bf5 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -63,8 +63,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, if (pcmc->smbios_defaults) { /* These values are guest ABI, do not change */ - smbios_set_defaults("QEMU", mc->desc, mc->name, - pcmc->smbios_uuid_encoded); + smbios_set_defaults("QEMU", mc->desc, mc->name, true); } /* tell smbios about cpuid version and features */ diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b0b8ecd64d..215462e861 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1796,7 +1796,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->has_acpi_build = true; pcmc->rsdp_in_ram = true; pcmc->smbios_defaults = true; - pcmc->smbios_uuid_encoded = true; pcmc->gigabyte_align = true; pcmc->has_reserved_memory = true; pcmc->enforce_aligned_dimm = true; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 0d730318fe..5667b87ed0 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -109,7 +109,6 @@ struct PCMachineClass { /* SMBIOS compat: */ bool smbios_defaults; bool smbios_legacy_mode; - bool smbios_uuid_encoded; SmbiosEntryPointType default_smbios_ep_type; /* RAM / address space compat: */ From c338128e80237ef4c8ca238940d9ffaed6211fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Mar 2024 10:08:05 +0100 Subject: [PATCH 1432/2884] hw/smbios: Remove 'uuid_encoded' argument from smbios_set_defaults() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'uuid_encoded' is always true, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-12-philmd@linaro.org> --- hw/arm/virt.c | 3 +-- hw/i386/fw_cfg.c | 2 +- hw/loongarch/virt.c | 2 +- hw/riscv/virt.c | 2 +- hw/smbios/smbios.c | 6 ++---- include/hw/firmware/smbios.h | 3 +-- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3c93c0c0a6..268b25e332 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1677,8 +1677,7 @@ static void virt_build_smbios(VirtMachineState *vms) } smbios_set_defaults("QEMU", product, - vmc->smbios_old_sys_ver ? "1.0" : mc->name, - true); + vmc->smbios_old_sys_ver ? "1.0" : mc->name); /* build the array of physical mem area from base_memmap */ mem_array.address = vms->memmap[VIRT_MEM].base; diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index f9e8af3bf5..7c43c325ef 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -63,7 +63,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, if (pcmc->smbios_defaults) { /* These values are guest ABI, do not change */ - smbios_set_defaults("QEMU", mc->desc, mc->name, true); + smbios_set_defaults("QEMU", mc->desc, mc->name); } /* tell smbios about cpuid version and features */ diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 66cef201ab..b7ea17d5f3 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -556,7 +556,7 @@ static void virt_build_smbios(LoongArchVirtMachineState *lvms) return; } - smbios_set_defaults("QEMU", product, mc->name, true); + smbios_set_defaults("QEMU", product, mc->name); smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, NULL, 0, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4fdb660525..5676d66d12 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1277,7 +1277,7 @@ static void virt_build_smbios(RISCVVirtState *s) product = "KVM Virtual Machine"; } - smbios_set_defaults("QEMU", product, mc->name, true); + smbios_set_defaults("QEMU", product, mc->name); if (riscv_is_32bit(&s->soc[0])) { smbios_set_default_processor_family(0x200); diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index eed5787b15..8261eb716f 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -30,7 +30,7 @@ #include "hw/pci/pci_device.h" #include "smbios_build.h" -static bool smbios_uuid_encoded = true; +static const bool smbios_uuid_encoded = true; /* * SMBIOS tables provided by user with '-smbios file=' option */ @@ -1017,11 +1017,9 @@ void smbios_set_default_processor_family(uint16_t processor_family) } void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, - bool uuid_encoded) + const char *version) { smbios_have_defaults = true; - smbios_uuid_encoded = uuid_encoded; SMBIOS_SET_DEFAULT(smbios_type1.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(smbios_type1.product, product); diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 8d3fb2fb3b..f066ab7262 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -331,8 +331,7 @@ void smbios_add_usr_blob_size(size_t size); void smbios_entry_add(QemuOpts *opts, Error **errp); void smbios_set_cpuid(uint32_t version, uint32_t features); void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, - bool uuid_encoded); + const char *version); void smbios_set_default_processor_family(uint16_t processor_family); uint8_t *smbios_get_table_legacy(size_t *length, Error **errp); void smbios_get_tables(MachineState *ms, From 9adf35f04b5d4c4c3074e1b0018eabff5c0cc741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 27 Mar 2024 10:09:11 +0100 Subject: [PATCH 1433/2884] hw/smbios: Remove 'smbios_uuid_encoded', simplify smbios_encode_uuid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'smbios_encode_uuid' is always true, remove it, simplifying smbios_encode_uuid(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-13-philmd@linaro.org> --- hw/smbios/smbios.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 8261eb716f..3b7703489d 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -30,7 +30,6 @@ #include "hw/pci/pci_device.h" #include "smbios_build.h" -static const bool smbios_uuid_encoded = true; /* * SMBIOS tables provided by user with '-smbios file=' option */ @@ -600,11 +599,9 @@ static void smbios_build_type_0_table(void) static void smbios_encode_uuid(struct smbios_uuid *uuid, QemuUUID *in) { memcpy(uuid, in, 16); - if (smbios_uuid_encoded) { - uuid->time_low = bswap32(uuid->time_low); - uuid->time_mid = bswap16(uuid->time_mid); - uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); - } + uuid->time_low = bswap32(uuid->time_low); + uuid->time_mid = bswap16(uuid->time_mid); + uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); } static void smbios_build_type_1_table(void) From 516871f0023858c73772ac9705a42d1b2906c8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:45:47 +0100 Subject: [PATCH 1434/2884] hw/i386/pc: Remove PCMachineClass::enforce_aligned_dimm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCMachineClass::enforce_aligned_dimm was only used by the pc-i440fx-2.1 machine, which got removed. It is now always true. Remove it, simplifying pc_get_device_memory_range(). Update the comment in Avocado test_phybits_low_pse36(). Reviewed-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-14-philmd@linaro.org> --- hw/i386/pc.c | 14 +++----------- include/hw/i386/pc.h | 3 --- tests/avocado/mem-addr-space-check.py | 9 ++++----- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 215462e861..ff5683a8ee 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -728,7 +728,6 @@ static void pc_get_device_memory_range(PCMachineState *pcms, hwaddr *base, ram_addr_t *device_mem_size) { - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); MachineState *machine = MACHINE(pcms); ram_addr_t size; hwaddr addr; @@ -736,10 +735,8 @@ static void pc_get_device_memory_range(PCMachineState *pcms, size = machine->maxram_size - machine->ram_size; addr = ROUND_UP(pc_above_4g_end(pcms), 1 * GiB); - if (pcmc->enforce_aligned_dimm) { - /* size device region assuming 1G page max alignment per slot */ - size += (1 * GiB) * machine->ram_slots; - } + /* size device region assuming 1G page max alignment per slot */ + size += (1 * GiB) * machine->ram_slots; *base = addr; *device_mem_size = size; @@ -1303,12 +1300,9 @@ void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - const PCMachineState *pcms = PC_MACHINE(hotplug_dev); const X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - const uint64_t legacy_align = TARGET_PAGE_SIZE; Error *local_err = NULL; /* @@ -1333,8 +1327,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), - pcmc->enforce_aligned_dimm ? NULL : &legacy_align, errp); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); } static void pc_memory_plug(HotplugHandler *hotplug_dev, @@ -1798,7 +1791,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->smbios_defaults = true; pcmc->gigabyte_align = true; pcmc->has_reserved_memory = true; - pcmc->enforce_aligned_dimm = true; pcmc->enforce_amd_1tb_hole = true; pcmc->isa_bios_alias = true; /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 5667b87ed0..ca72f8cab8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -74,8 +74,6 @@ typedef struct PCMachineState { * * Compat fields: * - * @enforce_aligned_dimm: check that DIMM's address/size is aligned by - * backend's alignment value if provided * @acpi_data_size: Size of the chunk of memory at the top of RAM * for the BIOS ACPI tables and other BIOS * datastructures. @@ -114,7 +112,6 @@ struct PCMachineClass { /* RAM / address space compat: */ bool gigabyte_align; bool has_reserved_memory; - bool enforce_aligned_dimm; bool broken_reserved_end; bool enforce_amd_1tb_hole; bool isa_bios_alias; diff --git a/tests/avocado/mem-addr-space-check.py b/tests/avocado/mem-addr-space-check.py index af019969c0..85541ea051 100644 --- a/tests/avocado/mem-addr-space-check.py +++ b/tests/avocado/mem-addr-space-check.py @@ -31,11 +31,10 @@ class MemAddrCheck(QemuSystemTest): at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when we have 0.5 GiB of VM memory, see pc_q35_init()). This means total hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory - for dimm alignment for all newer machines (see enforce_aligned_dimm - property for pc machines and pc_get_device_memory_range()). That leaves - total hotpluggable actual memory size of 59 GiB. If the VM is started - with 0.5 GiB of memory, maxmem should be set to a maximum value of - 59.5 GiB to ensure that the processor can address all memory directly. + for dimm alignment for all machines. That leaves total hotpluggable + actual memory size of 59 GiB. If the VM is started with 0.5 GiB of + memory, maxmem should be set to a maximum value of 59.5 GiB to ensure + that the processor can address all memory directly. Note that 64-bit pci hole size is 0 in this case. If maxmem is set to 59.6G, QEMU should fail to start with a message "phy-bits are too low". If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU From d4fdb05b0e640c8ce47ec2dd224e8838ca3f7958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:26:33 +0100 Subject: [PATCH 1435/2884] hw/mem/pc-dimm: Remove legacy_align argument from pc_dimm_pre_plug() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'legacy_align' is always NULL, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-15-philmd@linaro.org> --- hw/arm/virt.c | 2 +- hw/i386/pc.c | 2 +- hw/loongarch/virt.c | 2 +- hw/mem/pc-dimm.c | 6 ++---- hw/ppc/spapr.c | 2 +- include/hw/mem/pc-dimm.h | 3 +-- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 268b25e332..c7a1f754e7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2763,7 +2763,7 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } static void virt_memory_plug(HotplugHandler *hotplug_dev, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index ff5683a8ee..2d0a517c4c 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1327,7 +1327,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } static void pc_memory_plug(HotplugHandler *hotplug_dev, diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index b7ea17d5f3..8be2d2ff6a 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1245,7 +1245,7 @@ static bool memhp_type_supported(DeviceState *dev) static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } static void virt_device_pre_plug(HotplugHandler *hotplug_dev, diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 37f1f4ccfd..836384a90f 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -44,8 +44,7 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) return host_memory_backend_get_memory(dimm->hostmem); } -void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, - const uint64_t *legacy_align, Error **errp) +void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp) { Error *local_err = NULL; int slot; @@ -70,8 +69,7 @@ void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, &error_abort); trace_mhp_pc_dimm_assigned_slot(slot); - memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, legacy_align, - errp); + memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, NULL, errp); } void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 81a187f126..d7d4b188ee 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3700,7 +3700,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(dimm, MACHINE(hotplug_dev), NULL, errp); + pc_dimm_pre_plug(dimm, MACHINE(hotplug_dev), errp); } struct SpaprDimmState { diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 322bebe555..fe0f3ea963 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -66,8 +66,7 @@ struct PCDIMMDeviceClass { void (*unrealize)(PCDIMMDevice *dimm); }; -void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, - const uint64_t *legacy_align, Error **errp); +void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp); void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine); void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine); #endif From 0e0bf77d28c573d4b8c8166829ba55664347a499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:26:53 +0100 Subject: [PATCH 1436/2884] hw/mem/memory-device: Remove legacy_align from memory_device_pre_plug() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'legacy_align' is always NULL, remove it, simplifying memory_device_pre_plug(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-16-philmd@linaro.org> --- hw/i386/pc.c | 3 +-- hw/mem/memory-device.c | 12 ++++-------- hw/mem/pc-dimm.c | 2 +- hw/virtio/virtio-md-pci.c | 2 +- include/hw/mem/memory-device.h | 2 +- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2d0a517c4c..8a9c025754 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1395,8 +1395,7 @@ static void pc_hv_balloon_pre_plug(HotplugHandler *hotplug_dev, { /* The vmbus handler has no hotplug handler; we should never end up here. */ g_assert(!dev->hotplugged); - memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL, - errp); + memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), errp); } static void pc_hv_balloon_plug(HotplugHandler *hotplug_dev, diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index e098585cda..a5f279adcc 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -345,7 +345,7 @@ uint64_t get_plugged_memory_size(void) } void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, - const uint64_t *legacy_align, Error **errp) + Error **errp) { const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); Error *local_err = NULL; @@ -388,14 +388,10 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, return; } - if (legacy_align) { - align = *legacy_align; - } else { - if (mdc->get_min_alignment) { - align = mdc->get_min_alignment(md); - } - align = MAX(align, memory_region_get_alignment(mr)); + if (mdc->get_min_alignment) { + align = mdc->get_min_alignment(md); } + align = MAX(align, memory_region_get_alignment(mr)); addr = mdc->get_addr(md); addr = memory_device_get_free_addr(ms, !addr ? NULL : &addr, align, memory_region_size(mr), &local_err); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 836384a90f..27919ca45d 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -69,7 +69,7 @@ void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp) &error_abort); trace_mhp_pc_dimm_assigned_slot(slot); - memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, NULL, errp); + memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, errp); } void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) diff --git a/hw/virtio/virtio-md-pci.c b/hw/virtio/virtio-md-pci.c index 62bfb7920b..9ec5067662 100644 --- a/hw/virtio/virtio-md-pci.c +++ b/hw/virtio/virtio-md-pci.c @@ -37,7 +37,7 @@ void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) * First, see if we can plug this memory device at all. If that * succeeds, branch of to the actual hotplug handler. */ - memory_device_pre_plug(md, ms, NULL, &local_err); + memory_device_pre_plug(md, ms, &local_err); if (!local_err && bus_handler) { hotplug_handler_pre_plug(bus_handler, dev, &local_err); } diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index e0571c8a31..c0a58087cc 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -169,7 +169,7 @@ uint64_t get_plugged_memory_size(void); unsigned int memory_devices_get_reserved_memslots(void); bool memory_devices_memslot_auto_decision_active(void); void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, - const uint64_t *legacy_align, Error **errp); + Error **errp); void memory_device_plug(MemoryDeviceState *md, MachineState *ms); void memory_device_unplug(MemoryDeviceState *md, MachineState *ms); uint64_t memory_device_get_region_size(const MemoryDeviceState *md, From d6b832fca8909bfd629e89a5064addc8a7a48c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:28:43 +0100 Subject: [PATCH 1437/2884] hw/i386/pc: Remove deprecated pc-i440fx-2.2 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc-i440fx-2.2 machine was deprecated for the 8.2 release (see commit c7437f0ddb "docs/about: Mark the old pc-i440fx-2.0 - 2.3 machine types as deprecated"), time to remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-17-philmd@linaro.org> --- docs/about/deprecated.rst | 2 +- docs/about/removed-features.rst | 2 +- hw/i386/pc.c | 23 ----------------------- hw/i386/pc_piix.c | 21 --------------------- include/hw/i386/pc.h | 3 --- 5 files changed, 2 insertions(+), 49 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index e332eb46e9..d7775fbb84 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -212,7 +212,7 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-2.2`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) +``pc-i440fx-2.3`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These old machine types are quite neglected nowadays and thus might have diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 347e04e50e..58821a8695 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -948,7 +948,7 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-i440fx-2.1`` (removed in 4.0 up to 9.0) +``pc-0.10`` up to ``pc-i440fx-2.2`` (removed in 4.0 up to 9.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These machine types were very old and likely could not be used for live diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 8a9c025754..21a9b7a5ae 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -290,29 +290,6 @@ GlobalProperty pc_compat_2_3[] = { }; const size_t pc_compat_2_3_len = G_N_ELEMENTS(pc_compat_2_3); -GlobalProperty pc_compat_2_2[] = { - PC_CPU_MODEL_IDS("2.2.0") - { "kvm64" "-" TYPE_X86_CPU, "vme", "off" }, - { "kvm32" "-" TYPE_X86_CPU, "vme", "off" }, - { "Conroe" "-" TYPE_X86_CPU, "vme", "off" }, - { "Penryn" "-" TYPE_X86_CPU, "vme", "off" }, - { "Nehalem" "-" TYPE_X86_CPU, "vme", "off" }, - { "Westmere" "-" TYPE_X86_CPU, "vme", "off" }, - { "SandyBridge" "-" TYPE_X86_CPU, "vme", "off" }, - { "Haswell" "-" TYPE_X86_CPU, "vme", "off" }, - { "Broadwell" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G1" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G2" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G3" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "vme", "off" }, - { "Haswell" "-" TYPE_X86_CPU, "f16c", "off" }, - { "Haswell" "-" TYPE_X86_CPU, "rdrand", "off" }, - { "Broadwell" "-" TYPE_X86_CPU, "f16c", "off" }, - { "Broadwell" "-" TYPE_X86_CPU, "rdrand", "off" }, -}; -const size_t pc_compat_2_2_len = G_N_ELEMENTS(pc_compat_2_2); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e0b421dd51..1343fd93e7 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -429,11 +429,6 @@ static void pc_compat_2_3_fn(MachineState *machine) } } -static void pc_compat_2_2_fn(MachineState *machine) -{ - pc_compat_2_3_fn(machine); -} - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { @@ -843,22 +838,6 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3_fn, pc_i440fx_2_3_machine_options); -static void pc_i440fx_2_2_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_3_machine_options(m); - m->hw_version = "2.2.0"; - m->default_machine_opts = "firmware=bios-256k.bin,suppress-vmdesc=on"; - compat_props_add(m->compat_props, hw_compat_2_2, hw_compat_2_2_len); - compat_props_add(m->compat_props, pc_compat_2_2, pc_compat_2_2_len); - pcmc->rsdp_in_ram = false; - pcmc->resizable_acpi_blob = false; -} - -DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2_fn, - pc_i440fx_2_2_machine_options); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index ca72f8cab8..155a02b1b5 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -307,9 +307,6 @@ extern const size_t pc_compat_2_4_len; extern GlobalProperty pc_compat_2_3[]; extern const size_t pc_compat_2_3_len; -extern GlobalProperty pc_compat_2_2[]; -extern const size_t pc_compat_2_2_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ From 91616f812a1d0bbca9efb3f87ca128759f01f5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:52:30 +0100 Subject: [PATCH 1438/2884] hw/i386/pc: Remove PCMachineClass::resizable_acpi_blob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCMachineClass::resizable_acpi_blob was only used by the pc-i440fx-2.2 machine, which got removed. It is now always true. Remove it, simplifying acpi_build(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-18-philmd@linaro.org> --- hw/i386/acpi-build.c | 10 ---------- hw/i386/pc.c | 1 - include/hw/i386/pc.h | 3 --- 3 files changed, 14 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index f5d74e2b4b..eafc3761c8 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2687,16 +2687,6 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) * keep the table size stable for all (max_cpus, max_memory_slots) * combinations. */ - /* Make sure we have a buffer in case we need to resize the tables. */ - if ((tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) && - !pcmc->resizable_acpi_blob) { - /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ - warn_report("ACPI table size %u exceeds %d bytes," - " migration may not work", - tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); - error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges.\n"); - } acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 21a9b7a5ae..a6d50df500 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1774,7 +1774,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->acpi_data_size = 0x20000 + 0x8000; pcmc->pvh_enabled = true; pcmc->kvmclock_create_always = true; - pcmc->resizable_acpi_blob = true; x86mc->apic_xrupt_override = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 155a02b1b5..d01ddc4618 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -125,9 +125,6 @@ struct PCMachineClass { /* create kvmclock device even when KVM PV features are not exposed */ bool kvmclock_create_always; - /* resizable acpi blob compat */ - bool resizable_acpi_blob; - /* * whether the machine type implements broken 32-bit address space bound * check for memory. From af8348f65828f9e72530e26eaed27c46a500be72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:54:16 +0100 Subject: [PATCH 1439/2884] hw/i386/pc: Remove PCMachineClass::rsdp_in_ram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCMachineClass::rsdp_in_ram was only used by the pc-i440fx-2.2 machine, which got removed. It is now always true. Remove it, simplifying acpi_setup(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-19-philmd@linaro.org> --- hw/i386/acpi-build.c | 35 ++++------------------------------- hw/i386/pc.c | 1 - include/hw/i386/pc.h | 1 - 3 files changed, 4 insertions(+), 33 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index eafc3761c8..158e74f64a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2494,7 +2494,6 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); DeviceState *iommu = pcms->iommu; GArray *table_offsets; @@ -2666,16 +2665,6 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) .rsdt_tbl_offset = &rsdt, }; build_rsdp(tables->rsdp, tables->linker, &rsdp_data); - if (!pcmc->rsdp_in_ram) { - /* We used to allocate some extra space for RSDP revision 2 but - * only used the RSDP revision 0 space. The extra bytes were - * zeroed out and not used. - * Here we continue wasting those extra 16 bytes to make sure we - * don't break migration for machine types 2.2 and older due to - * RSDP blob size mismatch. - */ - build_append_int_noprefix(tables->rsdp, 0, 16); - } } /* We'll expose it all to Guest so we want to reduce @@ -2754,7 +2743,6 @@ static const VMStateDescription vmstate_acpi_build = { void acpi_setup(void) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(pcms); AcpiBuildTables tables; AcpiBuildState *build_state; @@ -2816,25 +2804,10 @@ void acpi_setup(void) tables.vmgenid); } - if (!pcmc->rsdp_in_ram) { - /* - * Keep for compatibility with old machine types. - * Though RSDP is small, its contents isn't immutable, so - * we'll update it along with the rest of tables on guest access. - */ - uint32_t rsdp_size = acpi_data_len(tables.rsdp); - - build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); - fw_cfg_add_file_callback(x86ms->fw_cfg, ACPI_BUILD_RSDP_FILE, - acpi_build_update, NULL, build_state, - build_state->rsdp, rsdp_size, true); - build_state->rsdp_mr = NULL; - } else { - build_state->rsdp = NULL; - build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, - build_state, tables.rsdp, - ACPI_BUILD_RSDP_FILE); - } + build_state->rsdp = NULL; + build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, + build_state, tables.rsdp, + ACPI_BUILD_RSDP_FILE); qemu_register_reset(acpi_build_reset, build_state); acpi_build_reset(build_state); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a6d50df500..b0fc8686d8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1763,7 +1763,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->pci_enabled = true; pcmc->has_acpi_build = true; - pcmc->rsdp_in_ram = true; pcmc->smbios_defaults = true; pcmc->gigabyte_align = true; pcmc->has_reserved_memory = true; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d01ddc4618..0b7a18a882 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -100,7 +100,6 @@ struct PCMachineClass { /* ACPI compat: */ bool has_acpi_build; - bool rsdp_in_ram; unsigned acpi_data_size; int pci_root_uid; From e00cb9a7c2cf7e3941198f133c5d35b0f255e9ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:55:42 +0100 Subject: [PATCH 1440/2884] hw/i386/acpi: Remove AcpiBuildState::rsdp field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AcpiBuildState::rsdp is always NULL, remove it, simplifying acpi_build_update(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-20-philmd@linaro.org> --- hw/i386/acpi-build.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 158e74f64a..f4e366f64f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2458,7 +2458,6 @@ struct AcpiBuildState { MemoryRegion *table_mr; /* Is table patched? */ uint8_t patched; - void *rsdp; MemoryRegion *rsdp_mr; MemoryRegion *linker_mr; } AcpiBuildState; @@ -2714,11 +2713,7 @@ static void acpi_build_update(void *build_opaque) acpi_ram_update(build_state->table_mr, tables.table_data); - if (build_state->rsdp) { - memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp)); - } else { - acpi_ram_update(build_state->rsdp_mr, tables.rsdp); - } + acpi_ram_update(build_state->rsdp_mr, tables.rsdp); acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); acpi_build_tables_cleanup(&tables, true); @@ -2804,7 +2799,6 @@ void acpi_setup(void) tables.vmgenid); } - build_state->rsdp = NULL; build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, build_state, tables.rsdp, ACPI_BUILD_RSDP_FILE); From 46a2bd525712660356ac27faaa44e6f23327f558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:34:35 +0100 Subject: [PATCH 1441/2884] hw/i386/pc: Remove deprecated pc-i440fx-2.3 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc-i440fx-2.3 machine was deprecated for the 8.2 release (see commit c7437f0ddb "docs/about: Mark the old pc-i440fx-2.0 - 2.3 machine types as deprecated"), time to remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-21-philmd@linaro.org> --- docs/about/deprecated.rst | 4 ++-- docs/about/removed-features.rst | 2 +- hw/i386/pc.c | 25 ------------------------- hw/i386/pc_piix.c | 19 ------------------- 4 files changed, 3 insertions(+), 47 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d7775fbb84..ff3da68208 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -212,8 +212,8 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-2.3`` up to ``pc-i440fx-2.3`` (since 8.2) and ``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' These old machine types are quite neglected nowadays and thus might have various pitfalls with regards to live migration. Use a newer machine type diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 58821a8695..fc7b28e637 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -948,7 +948,7 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-i440fx-2.2`` (removed in 4.0 up to 9.0) +``pc-0.10`` up to ``pc-i440fx-2.3`` (removed in 4.0 up to 9.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These machine types were very old and likely could not be used for live diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b0fc8686d8..819a164373 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -265,31 +265,6 @@ GlobalProperty pc_compat_2_4[] = { }; const size_t pc_compat_2_4_len = G_N_ELEMENTS(pc_compat_2_4); -GlobalProperty pc_compat_2_3[] = { - PC_CPU_MODEL_IDS("2.3.0") - { TYPE_X86_CPU, "arat", "off" }, - { "qemu64" "-" TYPE_X86_CPU, "min-level", "4" }, - { "kvm64" "-" TYPE_X86_CPU, "min-level", "5" }, - { "pentium3" "-" TYPE_X86_CPU, "min-level", "2" }, - { "n270" "-" TYPE_X86_CPU, "min-level", "5" }, - { "Conroe" "-" TYPE_X86_CPU, "min-level", "4" }, - { "Penryn" "-" TYPE_X86_CPU, "min-level", "4" }, - { "Nehalem" "-" TYPE_X86_CPU, "min-level", "4" }, - { "n270" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Penryn" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Conroe" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Nehalem" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Westmere" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "SandyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "IvyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Haswell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Haswell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Broadwell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Broadwell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { TYPE_X86_CPU, "kvm-no-smi-migration", "on" }, -}; -const size_t pc_compat_2_3_len = G_N_ELEMENTS(pc_compat_2_3); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 1343fd93e7..217c749705 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -421,14 +421,6 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) * hw_compat_*, pc_compat_*, or * pc_*_machine_options(). */ -static void pc_compat_2_3_fn(MachineState *machine) -{ - X86MachineState *x86ms = X86_MACHINE(machine); - if (kvm_enabled()) { - x86ms->smm = ON_OFF_AUTO_OFF; - } -} - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { @@ -827,17 +819,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL, pc_i440fx_2_4_machine_options) -static void pc_i440fx_2_3_machine_options(MachineClass *m) -{ - pc_i440fx_2_4_machine_options(m); - m->hw_version = "2.3.0"; - compat_props_add(m->compat_props, hw_compat_2_3, hw_compat_2_3_len); - compat_props_add(m->compat_props, pc_compat_2_3, pc_compat_2_3_len); -} - -DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3_fn, - pc_i440fx_2_3_machine_options); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { From fff35c5da5f7ad7b61a9bd79ce0a14bb8dcfa49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 29 May 2024 07:06:55 +0200 Subject: [PATCH 1442/2884] hw/i386/pc: Simplify DEFINE_I440FX_MACHINE() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last commit removed the last non-NULL use of DEFINE_I440FX_MACHINE 3rd parameter. 'compatfn' is now obsolete, remove it. Suggested-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Daniel P. Berrangé Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-22-philmd@linaro.org> --- hw/i386/pc_piix.c | 69 ++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 217c749705..e4930b7f48 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -414,13 +414,6 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) pcms->south_bridge = PCSouthBridgeOption_lookup.array[value]; } -/* Looking for a pc_compat_2_4() function? It doesn't exist. - * pc_compat_*() functions that run on machine-init time and - * change global QEMU state are deprecated. Please don't create - * one, and implement any pc-*-2.4 (and newer) compat code in - * hw_compat_*, pc_compat_*, or * pc_*_machine_options(). - */ - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { @@ -452,13 +445,9 @@ static void pc_xen_hvm_init(MachineState *machine) } #endif -#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \ +#define DEFINE_I440FX_MACHINE(suffix, name, optionfn) \ static void pc_init_##suffix(MachineState *machine) \ { \ - void (*compat)(MachineState *m) = (compatfn); \ - if (compat) { \ - compat(machine); \ - } \ pc_init1(machine, TYPE_I440FX_PCI_DEVICE); \ } \ DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) @@ -496,7 +485,7 @@ static void pc_i440fx_9_1_machine_options(MachineClass *m) m->is_default = true; } -DEFINE_I440FX_MACHINE(v9_1, "pc-i440fx-9.1", NULL, +DEFINE_I440FX_MACHINE(v9_1, "pc-i440fx-9.1", pc_i440fx_9_1_machine_options); static void pc_i440fx_9_0_machine_options(MachineClass *m) @@ -512,7 +501,7 @@ static void pc_i440fx_9_0_machine_options(MachineClass *m) pcmc->isa_bios_alias = false; } -DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL, +DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", pc_i440fx_9_0_machine_options); static void pc_i440fx_8_2_machine_options(MachineClass *m) @@ -527,7 +516,7 @@ static void pc_i440fx_8_2_machine_options(MachineClass *m) pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; } -DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", NULL, +DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", pc_i440fx_8_2_machine_options); static void pc_i440fx_8_1_machine_options(MachineClass *m) @@ -541,7 +530,7 @@ static void pc_i440fx_8_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_8_1, pc_compat_8_1_len); } -DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL, +DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", pc_i440fx_8_1_machine_options); static void pc_i440fx_8_0_machine_options(MachineClass *m) @@ -556,7 +545,7 @@ static void pc_i440fx_8_0_machine_options(MachineClass *m) pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; } -DEFINE_I440FX_MACHINE(v8_0, "pc-i440fx-8.0", NULL, +DEFINE_I440FX_MACHINE(v8_0, "pc-i440fx-8.0", pc_i440fx_8_0_machine_options); static void pc_i440fx_7_2_machine_options(MachineClass *m) @@ -566,7 +555,7 @@ static void pc_i440fx_7_2_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_7_2, pc_compat_7_2_len); } -DEFINE_I440FX_MACHINE(v7_2, "pc-i440fx-7.2", NULL, +DEFINE_I440FX_MACHINE(v7_2, "pc-i440fx-7.2", pc_i440fx_7_2_machine_options); static void pc_i440fx_7_1_machine_options(MachineClass *m) @@ -576,7 +565,7 @@ static void pc_i440fx_7_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_7_1, pc_compat_7_1_len); } -DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL, +DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", pc_i440fx_7_1_machine_options); static void pc_i440fx_7_0_machine_options(MachineClass *m) @@ -588,7 +577,7 @@ static void pc_i440fx_7_0_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); } -DEFINE_I440FX_MACHINE(v7_0, "pc-i440fx-7.0", NULL, +DEFINE_I440FX_MACHINE(v7_0, "pc-i440fx-7.0", pc_i440fx_7_0_machine_options); static void pc_i440fx_6_2_machine_options(MachineClass *m) @@ -598,7 +587,7 @@ static void pc_i440fx_6_2_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_6_2, pc_compat_6_2_len); } -DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL, +DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", pc_i440fx_6_2_machine_options); static void pc_i440fx_6_1_machine_options(MachineClass *m) @@ -609,7 +598,7 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m) m->smp_props.prefer_sockets = true; } -DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL, +DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", pc_i440fx_6_1_machine_options); static void pc_i440fx_6_0_machine_options(MachineClass *m) @@ -619,7 +608,7 @@ static void pc_i440fx_6_0_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len); } -DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", NULL, +DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", pc_i440fx_6_0_machine_options); static void pc_i440fx_5_2_machine_options(MachineClass *m) @@ -629,7 +618,7 @@ static void pc_i440fx_5_2_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); } -DEFINE_I440FX_MACHINE(v5_2, "pc-i440fx-5.2", NULL, +DEFINE_I440FX_MACHINE(v5_2, "pc-i440fx-5.2", pc_i440fx_5_2_machine_options); static void pc_i440fx_5_1_machine_options(MachineClass *m) @@ -643,7 +632,7 @@ static void pc_i440fx_5_1_machine_options(MachineClass *m) pcmc->pci_root_uid = 1; } -DEFINE_I440FX_MACHINE(v5_1, "pc-i440fx-5.1", NULL, +DEFINE_I440FX_MACHINE(v5_1, "pc-i440fx-5.1", pc_i440fx_5_1_machine_options); static void pc_i440fx_5_0_machine_options(MachineClass *m) @@ -655,7 +644,7 @@ static void pc_i440fx_5_0_machine_options(MachineClass *m) m->auto_enable_numa_with_memdev = false; } -DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", NULL, +DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", pc_i440fx_5_0_machine_options); static void pc_i440fx_4_2_machine_options(MachineClass *m) @@ -665,7 +654,7 @@ static void pc_i440fx_4_2_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); } -DEFINE_I440FX_MACHINE(v4_2, "pc-i440fx-4.2", NULL, +DEFINE_I440FX_MACHINE(v4_2, "pc-i440fx-4.2", pc_i440fx_4_2_machine_options); static void pc_i440fx_4_1_machine_options(MachineClass *m) @@ -675,7 +664,7 @@ static void pc_i440fx_4_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_4_1, pc_compat_4_1_len); } -DEFINE_I440FX_MACHINE(v4_1, "pc-i440fx-4.1", NULL, +DEFINE_I440FX_MACHINE(v4_1, "pc-i440fx-4.1", pc_i440fx_4_1_machine_options); static void pc_i440fx_4_0_machine_options(MachineClass *m) @@ -687,7 +676,7 @@ static void pc_i440fx_4_0_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); } -DEFINE_I440FX_MACHINE(v4_0, "pc-i440fx-4.0", NULL, +DEFINE_I440FX_MACHINE(v4_0, "pc-i440fx-4.0", pc_i440fx_4_0_machine_options); static void pc_i440fx_3_1_machine_options(MachineClass *m) @@ -701,7 +690,7 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); } -DEFINE_I440FX_MACHINE(v3_1, "pc-i440fx-3.1", NULL, +DEFINE_I440FX_MACHINE(v3_1, "pc-i440fx-3.1", pc_i440fx_3_1_machine_options); static void pc_i440fx_3_0_machine_options(MachineClass *m) @@ -711,7 +700,7 @@ static void pc_i440fx_3_0_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); } -DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, +DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", pc_i440fx_3_0_machine_options); static void pc_i440fx_2_12_machine_options(MachineClass *m) @@ -722,7 +711,7 @@ static void pc_i440fx_2_12_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } -DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, +DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", pc_i440fx_2_12_machine_options); static void pc_i440fx_2_11_machine_options(MachineClass *m) @@ -732,7 +721,7 @@ static void pc_i440fx_2_11_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_11, pc_compat_2_11_len); } -DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", NULL, +DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", pc_i440fx_2_11_machine_options); static void pc_i440fx_2_10_machine_options(MachineClass *m) @@ -743,7 +732,7 @@ static void pc_i440fx_2_10_machine_options(MachineClass *m) m->auto_enable_numa_with_memhp = false; } -DEFINE_I440FX_MACHINE(v2_10, "pc-i440fx-2.10", NULL, +DEFINE_I440FX_MACHINE(v2_10, "pc-i440fx-2.10", pc_i440fx_2_10_machine_options); static void pc_i440fx_2_9_machine_options(MachineClass *m) @@ -753,7 +742,7 @@ static void pc_i440fx_2_9_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); } -DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", NULL, +DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", pc_i440fx_2_9_machine_options); static void pc_i440fx_2_8_machine_options(MachineClass *m) @@ -763,7 +752,7 @@ static void pc_i440fx_2_8_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } -DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", NULL, +DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", pc_i440fx_2_8_machine_options); static void pc_i440fx_2_7_machine_options(MachineClass *m) @@ -773,7 +762,7 @@ static void pc_i440fx_2_7_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } -DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, +DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", pc_i440fx_2_7_machine_options); static void pc_i440fx_2_6_machine_options(MachineClass *m) @@ -788,7 +777,7 @@ static void pc_i440fx_2_6_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } -DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL, +DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", pc_i440fx_2_6_machine_options); static void pc_i440fx_2_5_machine_options(MachineClass *m) @@ -802,7 +791,7 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } -DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL, +DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", pc_i440fx_2_5_machine_options); static void pc_i440fx_2_4_machine_options(MachineClass *m) @@ -816,7 +805,7 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } -DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL, +DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", pc_i440fx_2_4_machine_options) #ifdef CONFIG_ISAPC From 82912391139f9f5eb6164ffb18f37fda441c8fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 10:36:37 +0100 Subject: [PATCH 1443/2884] target/i386: Remove X86CPU::kvm_no_smi_migration field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X86CPU::kvm_no_smi_migration was only used by the pc-i440fx-2.3 machine, which got removed. Remove it and simplify kvm_put_vcpu_events(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Thomas Huth Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-23-philmd@linaro.org> --- target/i386/cpu.c | 2 -- target/i386/cpu.h | 3 --- target/i386/kvm/kvm.c | 7 +------ 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 365852cb99..4c2e6f3a71 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8281,8 +8281,6 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("x-vendor-cpuid-only", X86CPU, vendor_cpuid_only, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), - DEFINE_PROP_BOOL("kvm-no-smi-migration", X86CPU, kvm_no_smi_migration, - false), DEFINE_PROP_BOOL("kvm-pv-enforce-cpuid", X86CPU, kvm_pv_enforce_cpuid, false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7e2a9b56ae..52571ababe 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2108,9 +2108,6 @@ struct ArchCPU { /* if set, limit maximum value for phys_bits when host_phys_bits is true */ uint8_t host_phys_bits_limit; - /* Stop SMI delivery for migration compatibility with old machines */ - bool kvm_no_smi_migration; - /* Forcefully disable KVM PV features not exposed in guest CPUIDs */ bool kvm_pv_enforce_cpuid; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 912f5d5a6b..7ad8072748 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -4474,6 +4474,7 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) events.sipi_vector = env->sipi_vector; if (has_msr_smbase) { + events.flags |= KVM_VCPUEVENT_VALID_SMM; events.smi.smm = !!(env->hflags & HF_SMM_MASK); events.smi.smm_inside_nmi = !!(env->hflags2 & HF2_SMM_INSIDE_NMI_MASK); if (kvm_irqchip_in_kernel()) { @@ -4488,12 +4489,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) events.smi.pending = 0; events.smi.latched_init = 0; } - /* Stop SMI delivery on old machine types to avoid a reboot - * on an inward migration of an old VM. - */ - if (!cpu->kvm_no_smi_migration) { - events.flags |= KVM_VCPUEVENT_VALID_SMM; - } } if (level >= KVM_PUT_RESET_STATE) { From e6115657a4437ae13985352ab3e77d78f8cf1ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 28 Feb 2024 09:39:50 +0100 Subject: [PATCH 1444/2884] hw/i386/pc: Replace PCMachineClass::acpi_data_size by PC_ACPI_DATA_SIZE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCMachineClass::acpi_data_size was only used by the pc-i440fx-2.0 machine, which got removed. Since it is constant, replace the class field by a definition (local to hw/i386/pc.c, since not used elsewhere). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Message-Id: <20240617071118.60464-24-philmd@linaro.org> --- hw/i386/pc.c | 19 ++++++++++++------- include/hw/i386/pc.h | 4 ---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 819a164373..77415064c6 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -265,6 +265,16 @@ GlobalProperty pc_compat_2_4[] = { }; const size_t pc_compat_2_4_len = G_N_ELEMENTS(pc_compat_2_4); +/* + * @PC_FW_DATA: + * Size of the chunk of memory at the top of RAM for the BIOS ACPI tables + * and other BIOS datastructures. + * + * BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K + * reported to be used at the moment, 32K should be enough for a while. + */ +#define PC_FW_DATA (0x20000 + 0x8000) + GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; @@ -646,8 +656,7 @@ void xen_load_linux(PCMachineState *pcms) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); rom_set_fw(fw_cfg); - x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, - pcmc->pvh_enabled); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, pcmc->pvh_enabled); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || @@ -986,8 +995,7 @@ void pc_memory_init(PCMachineState *pcms, } if (linux_boot) { - x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, - pcmc->pvh_enabled); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, pcmc->pvh_enabled); } for (i = 0; i < nb_option_roms; i++) { @@ -1743,9 +1751,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->has_reserved_memory = true; pcmc->enforce_amd_1tb_hole = true; pcmc->isa_bios_alias = true; - /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported - * to be used at the moment, 32K should be enough for a while. */ - pcmc->acpi_data_size = 0x20000 + 0x8000; pcmc->pvh_enabled = true; pcmc->kvmclock_create_always = true; x86mc->apic_xrupt_override = true; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 0b7a18a882..46bc411063 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -74,9 +74,6 @@ typedef struct PCMachineState { * * Compat fields: * - * @acpi_data_size: Size of the chunk of memory at the top of RAM - * for the BIOS ACPI tables and other BIOS - * datastructures. * @gigabyte_align: Make sure that guest addresses aligned at * 1Gbyte boundaries get mapped to host * addresses aligned at 1Gbyte boundaries. This @@ -100,7 +97,6 @@ struct PCMachineClass { /* ACPI compat: */ bool has_acpi_build; - unsigned acpi_data_size; int pci_root_uid; /* SMBIOS compat: */ From a276ec8e2632c9015d0f9b4e47194e4e91dfa8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 22 Apr 2024 14:47:23 +0200 Subject: [PATCH 1445/2884] hw/audio/virtio-snd: Always use little endian audio format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VIRTIO Sound Device conforms with the Virtio spec v1.2, thus only use little endianness. Remove the suspicious target_words_bigendian() noticed during code review. Cc: qemu-stable@nongnu.org Fixes: eb9ad377bb ("virtio-sound: handle control messages and streams") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Message-Id: <20240422211830.25606-1-philmd@linaro.org> --- hw/audio/virtio-snd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 7d09800d1f..5993f4f040 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -401,7 +401,7 @@ static void virtio_snd_get_qemu_audsettings(audsettings *as, as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); as->fmt = virtio_snd_get_qemu_format(params->format); as->freq = virtio_snd_get_qemu_freq(params->rate); - as->endianness = target_words_bigendian() ? 1 : 0; + as->endianness = 0; /* Conforming to VIRTIO 1.0: always little endian. */ } /* From dafec001d6ce8ca7f8a1061acd1e4995881d8efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 13:21:53 +0200 Subject: [PATCH 1446/2884] hw/ppc: Avoid using Monitor in pnv_phb3_msi_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-2-philmd@linaro.org> --- hw/pci-host/pnv_phb3_msi.c | 21 ++++++++++----------- hw/ppc/pnv.c | 8 +++++++- include/hw/pci-host/pnv_phb3.h | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index a6d827f903..77d673da54 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -13,7 +13,6 @@ #include "hw/pci-host/pnv_phb3.h" #include "hw/ppc/pnv.h" #include "hw/pci/msi.h" -#include "monitor/monitor.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" @@ -316,13 +315,13 @@ static void pnv_phb3_msi_register_types(void) type_init(pnv_phb3_msi_register_types); -void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon) +void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, GString *buf) { ICSState *ics = ICS(msi); int i; - monitor_printf(mon, "ICS %4x..%4x %p\n", - ics->offset, ics->offset + ics->nr_irqs - 1, ics); + g_string_append_printf(buf, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); for (i = 0; i < ics->nr_irqs; i++) { uint64_t ive; @@ -335,12 +334,12 @@ void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon) continue; } - monitor_printf(mon, " %4x %c%c server=%04x prio=%02x gen=%d\n", - ics->offset + i, - GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-', - GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-', - (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2, - (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive), - (uint32_t) GETFIELD(IODA2_IVT_GEN, ive)); + g_string_append_printf(buf, " %4x %c%c server=%04x prio=%02x gen=%d\n", + ics->offset + i, + GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-', + GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-', + (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2, + (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive), + (uint32_t) GETFIELD(IODA2_IVT_GEN, ive)); } } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 6e3a5ccdec..5356a4e295 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -38,6 +38,7 @@ #include "hw/loader.h" #include "hw/nmi.h" #include "qapi/visitor.h" +#include "qapi/type-helpers.h" #include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/ipmi/ipmi.h" @@ -774,8 +775,13 @@ static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) for (i = 0; i < chip8->num_phbs; i++) { PnvPHB *phb = chip8->phbs[i]; PnvPHB3 *phb3 = PNV_PHB3(phb->backend); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + pnv_phb3_msi_pic_print_info(&phb3->msis, buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); - pnv_phb3_msi_pic_print_info(&phb3->msis, mon); ics_pic_print_info(&phb3->lsis, mon); } } diff --git a/include/hw/pci-host/pnv_phb3.h b/include/hw/pci-host/pnv_phb3.h index d62b3091ac..24ca3dddaa 100644 --- a/include/hw/pci-host/pnv_phb3.h +++ b/include/hw/pci-host/pnv_phb3.h @@ -40,7 +40,7 @@ void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, int32_t dev_pe); void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val); -void pnv_phb3_msi_pic_print_info(Phb3MsiState *msis, Monitor *mon); +void pnv_phb3_msi_pic_print_info(Phb3MsiState *msis, GString *buf); /* From 5242494c056f19be05335e8a190e7a40f72e6a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 13:14:20 +0200 Subject: [PATCH 1447/2884] hw/ppc: Avoid using Monitor in icp_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-3-philmd@linaro.org> --- hw/intc/xics.c | 8 ++++---- hw/intc/xics_spapr.c | 8 +++++++- hw/ppc/pnv.c | 8 +++++++- include/hw/ppc/xics.h | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 9b3b7abaea..039e10a0e4 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -42,7 +42,7 @@ #include "sysemu/reset.h" #include "target/ppc/cpu.h" -void icp_pic_print_info(ICPState *icp, Monitor *mon) +void icp_pic_print_info(ICPState *icp, GString *buf) { int cpu_index; @@ -63,9 +63,9 @@ void icp_pic_print_info(ICPState *icp, Monitor *mon) icp_synchronize_state(icp); } - monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", - cpu_index, icp->xirr, icp->xirr_owner, - icp->pending_priority, icp->mfrr); + g_string_append_printf(buf, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", + cpu_index, icp->xirr, icp->xirr_owner, + icp->pending_priority, icp->mfrr); } void ics_pic_print_info(ICSState *ics, Monitor *mon) diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 37b2d99977..bab9d88218 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -34,6 +34,8 @@ #include "hw/ppc/xics_spapr.h" #include "hw/ppc/fdt.h" #include "qapi/visitor.h" +#include "qapi/type-helpers.h" +#include "monitor/monitor.h" /* * Guest interfaces @@ -399,12 +401,16 @@ static void xics_spapr_print_info(SpaprInterruptController *intc, Monitor *mon) { ICSState *ics = ICS_SPAPR(intc); CPUState *cs; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - icp_pic_print_info(spapr_cpu_state(cpu)->icp, mon); + icp_pic_print_info(spapr_cpu_state(cpu)->icp, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); ics_pic_print_info(ics, mon); } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 5356a4e295..fa23b27a2b 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1130,7 +1130,13 @@ static void pnv_chip_power8_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon) { - icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } /* diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 95ead0dd7c..1116aa6953 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -171,7 +171,7 @@ static inline bool ics_irq_free(ICSState *ics, uint32_t srcno) } void ics_set_irq_type(ICSState *ics, int srcno, bool lsi); -void icp_pic_print_info(ICPState *icp, Monitor *mon); +void icp_pic_print_info(ICPState *icp, GString *buf); void ics_pic_print_info(ICSState *ics, Monitor *mon); void ics_resend(ICSState *ics); From f163e2707e9dc7229f790bc0450270408b442f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 13:11:08 +0200 Subject: [PATCH 1448/2884] hw/ppc: Avoid using Monitor in xive_tctx_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-4-philmd@linaro.org> --- hw/intc/spapr_xive.c | 7 ++++++- hw/intc/xive.c | 11 ++++++----- hw/ppc/pnv.c | 16 ++++++++++++++-- include/hw/ppc/xive.h | 2 +- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index d7e56bfb20..b7c12aa432 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -11,6 +11,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "qemu/error-report.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" @@ -703,12 +704,16 @@ static void spapr_xive_print_info(SpaprInterruptController *intc, Monitor *mon) { SpaprXive *xive = SPAPR_XIVE(intc); CPUState *cs; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, mon); + xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); spapr_xive_pic_print_info(xive, mon); } diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 057b308ae9..a0d7e7ca67 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -669,7 +669,7 @@ static const char * const xive_tctx_ring_names[] = { xpc->in_kernel ? xpc->in_kernel(xptr) : false; \ })) -void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon) +void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf) { int cpu_index; int i; @@ -693,13 +693,14 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon) } } - monitor_printf(mon, "CPU[%04x]: QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR" - " W2\n", cpu_index); + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR W2\n", + cpu_index); for (i = 0; i < XIVE_TM_RING_COUNT; i++) { char *s = xive_tctx_ring_print(&tctx->regs[i * XIVE_TM_RING_SIZE]); - monitor_printf(mon, "CPU[%04x]: %4s %s\n", cpu_index, - xive_tctx_ring_names[i], s); + g_string_append_printf(buf, "CPU[%04x]: %4s %s\n", + cpu_index, xive_tctx_ring_names[i], s); g_free(s); } } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index fa23b27a2b..5854358f65 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1223,7 +1223,13 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon) { - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1267,7 +1273,13 @@ static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon) { - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } /* diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index f120874e0f..bc1cbad8a8 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -528,7 +528,7 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); -void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon); +void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf); Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp); void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); From dd77c49e742a9c3d872fdc742f34c354173dd8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 13:23:47 +0200 Subject: [PATCH 1449/2884] hw/ppc: Avoid using Monitor in ics_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-5-philmd@linaro.org> --- hw/intc/xics.c | 17 ++++++++--------- hw/intc/xics_spapr.c | 4 ++-- hw/ppc/pnv.c | 15 ++++++++------- include/hw/ppc/xics.h | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 039e10a0e4..6f4d5271ea 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -35,7 +35,6 @@ #include "qemu/module.h" #include "qapi/visitor.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/irq.h" #include "sysemu/kvm.h" @@ -68,12 +67,12 @@ void icp_pic_print_info(ICPState *icp, GString *buf) icp->pending_priority, icp->mfrr); } -void ics_pic_print_info(ICSState *ics, Monitor *mon) +void ics_pic_print_info(ICSState *ics, GString *buf) { uint32_t i; - monitor_printf(mon, "ICS %4x..%4x %p\n", - ics->offset, ics->offset + ics->nr_irqs - 1, ics); + g_string_append_printf(buf, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); if (!ics->irqs) { return; @@ -89,11 +88,11 @@ void ics_pic_print_info(ICSState *ics, Monitor *mon) if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { continue; } - monitor_printf(mon, " %4x %s %02x %02x\n", - ics->offset + i, - (irq->flags & XICS_FLAGS_IRQ_LSI) ? - "LSI" : "MSI", - irq->priority, irq->status); + g_string_append_printf(buf, " %4x %s %02x %02x\n", + ics->offset + i, + (irq->flags & XICS_FLAGS_IRQ_LSI) ? + "LSI" : "MSI", + irq->priority, irq->status); } } diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index bab9d88218..1926373ebd 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -409,10 +409,10 @@ static void xics_spapr_print_info(SpaprInterruptController *intc, Monitor *mon) icp_pic_print_info(spapr_cpu_state(cpu)->icp, buf); } + ics_pic_print_info(ics, buf); + info = human_readable_text_from_str(buf); monitor_puts(mon, info->human_readable_text); - - ics_pic_print_info(ics, mon); } static int xics_spapr_post_load(SpaprInterruptController *intc, int version_id) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 5854358f65..3eaf674efa 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -770,20 +770,21 @@ static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) Pnv8Chip *chip8 = PNV8_CHIP(chip); int i; - ics_pic_print_info(&chip8->psi.ics, mon); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + ics_pic_print_info(&chip8->psi.ics, buf); for (i = 0; i < chip8->num_phbs; i++) { PnvPHB *phb = chip8->phbs[i]; PnvPHB3 *phb3 = PNV_PHB3(phb->backend); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; pnv_phb3_msi_pic_print_info(&phb3->msis, buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - - ics_pic_print_info(&phb3->lsis, mon); + ics_pic_print_info(&phb3->lsis, buf); } + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 1116aa6953..e94d53405f 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -172,7 +172,7 @@ static inline bool ics_irq_free(ICSState *ics, uint32_t srcno) void ics_set_irq_type(ICSState *ics, int srcno, bool lsi); void icp_pic_print_info(ICPState *icp, GString *buf); -void ics_pic_print_info(ICSState *ics, Monitor *mon); +void ics_pic_print_info(ICSState *ics, GString *buf); void ics_resend(ICSState *ics); void icp_resend(ICPState *ss); From ae08259b84c92e27fa7fe2cc986cdbfce4145b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 13:25:38 +0200 Subject: [PATCH 1450/2884] hw/ppc: Avoid using Monitor in PnvChipClass::intc_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-6-philmd@linaro.org> --- hw/ppc/pnv.c | 30 ++++++++---------------------- include/hw/ppc/pnv_chip.h | 2 +- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 3eaf674efa..aed6767c8d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1129,15 +1129,9 @@ static void pnv_chip_power8_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) } static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, - Monitor *mon) + GString *buf) { - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } /* @@ -1222,15 +1216,9 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) } static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, - Monitor *mon) + GString *buf) { - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1272,15 +1260,9 @@ static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) } static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, - Monitor *mon) + GString *buf) { - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } /* @@ -2363,14 +2345,18 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, PnvMachineState *pnv = PNV_MACHINE(obj); int i; CPUState *cs; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); /* XXX: loop on each chip/core/thread instead of CPU_FOREACH() */ PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu, - mon); + buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); for (i = 0; i < pnv->num_chips; i++) { PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], mon); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 8589f3291e..a5e428be7c 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -151,7 +151,7 @@ struct PnvChipClass { void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp); void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu); void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu); - void (*intc_print_info)(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon); + void (*intc_print_info)(PnvChip *chip, PowerPCCPU *cpu, GString *buf); ISABus *(*isa_create)(PnvChip *chip, Error **errp); void (*dt_populate)(PnvChip *chip, void *fdt); void (*pic_print_info)(PnvChip *chip, Monitor *mon); From ace6fcde9b398113482b0c5955c237d64413f2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 14:43:47 +0200 Subject: [PATCH 1451/2884] hw/ppc: Avoid using Monitor in xive_end_queue_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-7-philmd@linaro.org> --- hw/intc/spapr_xive.c | 7 ++++++- hw/intc/xive.c | 17 +++++++++++------ include/hw/ppc/xive_regs.h | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index b7c12aa432..3357f6325f 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -142,12 +142,17 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, uint32_t qentries = 1 << (qsize + 10); uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6); uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; monitor_printf(mon, "%3d/%d % 6d/%5d @%"PRIx64" ^%d", spapr_xive_nvt_to_target(0, nvt), priority, qindex, qentries, qaddr_base, qgen); - xive_end_queue_pic_print_info(end, 6, mon); + xive_end_queue_pic_print_info(end, 6, buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } /* diff --git a/hw/intc/xive.c b/hw/intc/xive.c index a0d7e7ca67..260a94e2ca 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -11,6 +11,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" @@ -1323,7 +1324,7 @@ static const TypeInfo xive_source_info = { * XiveEND helpers */ -void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) +void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf) { uint64_t qaddr_base = xive_end_qaddr(end); uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0); @@ -1334,7 +1335,7 @@ void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) /* * print out the [ (qindex - (width - 1)) .. (qindex + 1)] window */ - monitor_printf(mon, " [ "); + g_string_append_printf(buf, " [ "); qindex = (qindex - (width - 1)) & (qentries - 1); for (i = 0; i < width; i++) { uint64_t qaddr = qaddr_base + (qindex << 2); @@ -1346,11 +1347,11 @@ void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) HWADDR_PRIx "\n", qaddr); return; } - monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "", - be32_to_cpu(qdata)); + g_string_append_printf(buf, "%s%08x ", i == width - 1 ? "^" : "", + be32_to_cpu(qdata)); qindex = (qindex + 1) & (qentries - 1); } - monitor_printf(mon, "]"); + g_string_append_c(buf, ']'); } void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) @@ -1365,6 +1366,8 @@ void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) uint32_t nvt_idx = xive_get_field32(END_W6_NVT_INDEX, end->w6); uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); uint8_t pq; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; if (!xive_end_is_valid(end)) { return; @@ -1389,8 +1392,10 @@ void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) if (qaddr_base) { monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", qaddr_base, qindex, qentries, qgen); - xive_end_queue_pic_print_info(end, 6, mon); + xive_end_queue_pic_print_info(end, 6, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "\n"); } diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index 4a3c9badd3..51e9a2152e 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -262,7 +262,7 @@ static inline uint64_t xive_end_qaddr(XiveEND *end) } void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); -void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon); +void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf); void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); /* Notification Virtual Target (NVT) */ From 950f1273abed699227d0fde2c7436a6fbc16fd53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 14:46:52 +0200 Subject: [PATCH 1452/2884] hw/ppc: Avoid using Monitor in spapr_xive_end_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-8-philmd@linaro.org> --- hw/intc/spapr_xive.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 3357f6325f..d571645e9e 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -133,7 +133,7 @@ static int spapr_xive_target_to_end(uint32_t target, uint8_t prio, * structure dumping only the information related to the OS EQ. */ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, - Monitor *mon) + GString *buf) { uint64_t qaddr_base = xive_end_qaddr(end); uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); @@ -142,17 +142,12 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, uint32_t qentries = 1 << (qsize + 10); uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6); uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - monitor_printf(mon, "%3d/%d % 6d/%5d @%"PRIx64" ^%d", - spapr_xive_nvt_to_target(0, nvt), - priority, qindex, qentries, qaddr_base, qgen); + g_string_append_printf(buf, "%3d/%d % 6d/%5d @%"PRIx64" ^%d", + spapr_xive_nvt_to_target(0, nvt), + priority, qindex, qentries, qaddr_base, qgen); xive_end_queue_pic_print_info(end, 6, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } /* @@ -198,13 +193,18 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) if (!xive_eas_is_masked(eas)) { uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); XiveEND *end; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; assert(end_idx < xive->nr_ends); end = &xive->endt[end_idx]; if (xive_end_is_valid(end)) { - spapr_xive_end_pic_print_info(xive, end, mon); + spapr_xive_end_pic_print_info(xive, end, buf); } + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } monitor_printf(mon, "\n"); } From 4d62448c1c74629ec7a1a19920770cbfb13861de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 14:48:53 +0200 Subject: [PATCH 1453/2884] hw/ppc: Avoid using Monitor in spapr_xive_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-9-philmd@linaro.org> --- hw/intc/spapr_xive.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index d571645e9e..9d0d5948ff 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -157,7 +157,7 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, #define spapr_xive_in_kernel(xive) \ (kvm_irqchip_in_kernel() && (xive)->fd != -1) -static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) +static void spapr_xive_pic_print_info(SpaprXive *xive, GString *buf) { XiveSource *xsrc = &xive->source; int i; @@ -172,7 +172,7 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) } } - monitor_printf(mon, " LISN PQ EISN CPU/PRIO EQ\n"); + g_string_append_printf(buf, " LISN PQ EISN CPU/PRIO EQ\n"); for (i = 0; i < xive->nr_irqs; i++) { uint8_t pq = xive_source_esb_get(xsrc, i); @@ -182,19 +182,17 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) continue; } - monitor_printf(mon, " %08x %s %c%c%c %s %08x ", i, - xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_source_is_asserted(xsrc, i) ? 'A' : ' ', - xive_eas_is_masked(eas) ? "M" : " ", - (int) xive_get_field64(EAS_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %s %c%c%c %s %08x ", i, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_source_is_asserted(xsrc, i) ? 'A' : ' ', + xive_eas_is_masked(eas) ? "M" : " ", + (int) xive_get_field64(EAS_END_DATA, eas->w)); if (!xive_eas_is_masked(eas)) { uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); XiveEND *end; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; assert(end_idx < xive->nr_ends); end = &xive->endt[end_idx]; @@ -203,10 +201,8 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) spapr_xive_end_pic_print_info(xive, end, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } } @@ -717,10 +713,10 @@ static void spapr_xive_print_info(SpaprInterruptController *intc, Monitor *mon) xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, buf); } + spapr_xive_pic_print_info(xive, buf); + info = human_readable_text_from_str(buf); monitor_puts(mon, info->human_readable_text); - - spapr_xive_pic_print_info(xive, mon); } static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, From b71a3f67bcf1d24b6829d22f726dd9b7d31dfb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:01:53 +0200 Subject: [PATCH 1454/2884] hw/ppc: Avoid using Monitor in xive_source_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-10-philmd@linaro.org> --- hw/intc/pnv_xive.c | 11 ++++++++--- hw/intc/pnv_xive2.c | 8 +++++++- hw/intc/xive.c | 16 +++++++--------- hw/pci-host/pnv_phb4.c | 20 ++++++++++++++------ hw/ppc/pnv_psi.c | 12 +++++++++--- include/hw/ppc/xive.h | 2 +- 6 files changed, 46 insertions(+), 23 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index da10deceb8..520d7e0acd 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -11,6 +11,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" @@ -1857,10 +1858,14 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) XiveNVT nvt; int i; uint64_t xive_nvt_per_subpage; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; - monitor_printf(mon, "XIVE[%x] #%d Source %08x .. %08x\n", chip_id, blk, - srcno0, srcno0 + nr_ipis - 1); - xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); + g_string_append_printf(buf, "XIVE[%x] #%d Source %08x .. %08x\n", + chip_id, blk, srcno0, srcno0 + nr_ipis - 1); + xive_source_pic_print_info(&xive->ipi_source, srcno0, buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] #%d EAT %08x .. %08x\n", chip_id, blk, srcno0, srcno0 + nr_ipis - 1); diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 4b8d0a5d81..83e0f6b09f 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" @@ -2116,10 +2117,15 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) Xive2Nvp nvp; int i; uint64_t xive_nvp_per_subpage; + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, srcno0 + nr_esbs - 1); - xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); + xive_source_pic_print_info(&xive->ipi_source, srcno0, buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0, srcno0 + nr_esbs - 1); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 260a94e2ca..1adb0439c5 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1209,22 +1209,20 @@ void xive_source_set_irq(void *opaque, int srcno, int val) } } -void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon) +void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, GString *buf) { - int i; - - for (i = 0; i < xsrc->nr_irqs; i++) { + for (unsigned i = 0; i < xsrc->nr_irqs; i++) { uint8_t pq = xive_source_esb_get(xsrc, i); if (pq == XIVE_ESB_OFF) { continue; } - monitor_printf(mon, " %08x %s %c%c%c\n", i + offset, - xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_source_is_asserted(xsrc, i) ? 'A' : ' '); + g_string_append_printf(buf, " %08x %s %c%c%c\n", i + offset, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_source_is_asserted(xsrc, i) ? 'A' : ' '); } } diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 075499d36d..4b453997e7 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -10,6 +10,7 @@ #include "qemu/log.h" #include "qapi/visitor.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "monitor/monitor.h" #include "target/ppc/cpu.h" #include "hw/pci-host/pnv_phb4_regs.h" @@ -1807,11 +1808,18 @@ void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon) phb->regs[PHB_INT_NOTIFY_ADDR >> 3] & ~PHB_INT_NOTIFY_ADDR_64K; uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; bool abt = !!(phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_ABT_MODE); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; - monitor_printf(mon, "PHB4[%x:%x] Source %08x .. %08x %s @%"HWADDR_PRIx"\n", - phb->chip_id, phb->phb_id, - offset, offset + phb->xsrc.nr_irqs - 1, - abt ? "ABT" : "", - notif_port); - xive_source_pic_print_info(&phb->xsrc, 0, mon); + g_string_append_printf(buf, + "PHB4[%x:%x] Source %08x .. %08x " + "%s @%"HWADDR_PRIx"\n", + phb->chip_id, phb->phb_id, + offset, offset + phb->xsrc.nr_irqs - 1, + abt ? "ABT" : "", + notif_port); + xive_source_pic_print_info(&phb->xsrc, 0, buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 26460d210d..922ac07b2f 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -25,6 +25,7 @@ #include "qemu/module.h" #include "sysemu/reset.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "monitor/monitor.h" @@ -980,11 +981,16 @@ type_init(pnv_psi_register_types); void pnv_psi_pic_print_info(Pnv9Psi *psi9, Monitor *mon) { PnvPsi *psi = PNV_PSI(psi9); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; uint32_t offset = (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); - monitor_printf(mon, "PSIHB Source %08x .. %08x\n", - offset, offset + psi9->source.nr_irqs - 1); - xive_source_pic_print_info(&psi9->source, offset, mon); + g_string_append_printf(buf, "PSIHB Source %08x .. %08x\n", + offset, offset + psi9->source.nr_irqs - 1); + xive_source_pic_print_info(&psi9->source, offset, buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index bc1cbad8a8..28c181faa2 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -314,7 +314,7 @@ static inline bool xive_source_is_asserted(XiveSource *xsrc, uint32_t srcno) } void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, - Monitor *mon); + GString *buf); static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno) { From dbcbb8c00f7eba6207cfe7b91203025f8c6f092a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:15:33 +0200 Subject: [PATCH 1455/2884] hw/ppc: Avoid using Monitor in pnv_phb4_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-11-philmd@linaro.org> --- hw/pci-host/pnv_phb4.c | 9 +-------- hw/ppc/pnv.c | 6 +++++- include/hw/pci-host/pnv_phb4.h | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 4b453997e7..99991008c1 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -10,8 +10,6 @@ #include "qemu/log.h" #include "qapi/visitor.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" -#include "monitor/monitor.h" #include "target/ppc/cpu.h" #include "hw/pci-host/pnv_phb4_regs.h" #include "hw/pci-host/pnv_phb4.h" @@ -1802,14 +1800,12 @@ static void pnv_phb4_register_types(void) type_init(pnv_phb4_register_types); -void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon) +void pnv_phb4_pic_print_info(PnvPHB4 *phb, GString *buf) { uint64_t notif_port = phb->regs[PHB_INT_NOTIFY_ADDR >> 3] & ~PHB_INT_NOTIFY_ADDR_64K; uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; bool abt = !!(phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_ABT_MODE); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; g_string_append_printf(buf, "PHB4[%x:%x] Source %08x .. %08x " @@ -1819,7 +1815,4 @@ void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon) abt ? "ABT" : "", notif_port); xive_source_pic_print_info(&phb->xsrc, 0, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index aed6767c8d..df187c5180 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -791,12 +791,16 @@ static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) { Monitor *mon = opaque; PnvPHB *phb = (PnvPHB *) object_dynamic_cast(child, TYPE_PNV_PHB); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; if (!phb) { return 0; } - pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), mon); + pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); return 0; } diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 3212e68160..8abee78e4d 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -155,7 +155,7 @@ struct PnvPHB4 { QLIST_HEAD(, PnvPhb4DMASpace) dma_spaces; }; -void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon); +void pnv_phb4_pic_print_info(PnvPHB4 *phb, GString *buf); int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index); PnvPhb4PecState *pnv_pec_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp); void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb); From bc8c553b893c39a7f68518e181c675ec20c74594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:18:30 +0200 Subject: [PATCH 1456/2884] hw/ppc: Avoid using Monitor in xive_eas_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-12-philmd@linaro.org> --- hw/intc/pnv_xive.c | 10 +++++----- hw/intc/xive.c | 12 ++++++------ include/hw/ppc/xive_regs.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 520d7e0acd..c377823522 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -1864,19 +1864,19 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) g_string_append_printf(buf, "XIVE[%x] #%d Source %08x .. %08x\n", chip_id, blk, srcno0, srcno0 + nr_ipis - 1); xive_source_pic_print_info(&xive->ipi_source, srcno0, buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "XIVE[%x] #%d EAT %08x .. %08x\n", chip_id, blk, - srcno0, srcno0 + nr_ipis - 1); + g_string_append_printf(buf, "XIVE[%x] #%d EAT %08x .. %08x\n", + chip_id, blk, srcno0, srcno0 + nr_ipis - 1); for (i = 0; i < nr_ipis; i++) { if (xive_router_get_eas(xrtr, blk, i, &eas)) { break; } if (!xive_eas_is_masked(&eas)) { - xive_eas_pic_print_info(&eas, i, mon); + xive_eas_pic_print_info(&eas, i, buf); } } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); i = 0; diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 1adb0439c5..b2203b721b 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1921,17 +1921,17 @@ static const TypeInfo xive_router_info = { } }; -void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon) +void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, GString *buf) { if (!xive_eas_is_valid(eas)) { return; } - monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n", - lisn, xive_eas_is_masked(eas) ? "M" : " ", - (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %s end:%02x/%04x data:%08x\n", + lisn, xive_eas_is_masked(eas) ? "M" : " ", + (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); } /* diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index 51e9a2152e..dd4a76bcf0 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -167,7 +167,7 @@ typedef struct XiveEAS { #define xive_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS_VALID) #define xive_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS_MASKED) -void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon); +void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, GString *buf); static inline uint64_t xive_get_field64(uint64_t mask, uint64_t word) { From f1bca2ca84c371b43561c5605affcf0788bfd915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:21:26 +0200 Subject: [PATCH 1457/2884] hw/ppc: Avoid using Monitor in xive_end_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-13-philmd@linaro.org> --- hw/intc/pnv_xive.c | 8 ++++---- hw/intc/xive.c | 40 +++++++++++++++++--------------------- include/hw/ppc/xive_regs.h | 2 +- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index c377823522..1dddbf7827 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -1875,14 +1875,14 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) xive_eas_pic_print_info(&eas, i, buf); } } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d ENDT\n", chip_id, blk); i = 0; while (!xive_router_get_end(xrtr, blk, i, &end)) { - xive_end_pic_print_info(&end, i++, mon); + xive_end_pic_print_info(&end, i++, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); i = 0; diff --git a/hw/intc/xive.c b/hw/intc/xive.c index b2203b721b..f631d7cd6e 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -11,7 +11,6 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" @@ -1352,7 +1351,7 @@ void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf) g_string_append_c(buf, ']'); } -void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) +void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf) { uint64_t qaddr_base = xive_end_qaddr(end); uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); @@ -1364,8 +1363,6 @@ void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) uint32_t nvt_idx = xive_get_field32(END_W6_NVT_INDEX, end->w6); uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); uint8_t pq; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; if (!xive_end_is_valid(end)) { return; @@ -1373,28 +1370,27 @@ void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) pq = xive_get_field32(END_W1_ESn, end->w1); - monitor_printf(mon, " %08x %c%c %c%c%c%c%c%c%c%c prio:%d nvt:%02x/%04x", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_end_is_valid(end) ? 'v' : '-', - xive_end_is_enqueue(end) ? 'q' : '-', - xive_end_is_notify(end) ? 'n' : '-', - xive_end_is_backlog(end) ? 'b' : '-', - xive_end_is_escalate(end) ? 'e' : '-', - xive_end_is_uncond_escalation(end) ? 'u' : '-', - xive_end_is_silent_escalation(end) ? 's' : '-', - xive_end_is_firmware(end) ? 'f' : '-', - priority, nvt_blk, nvt_idx); + g_string_append_printf(buf, + " %08x %c%c %c%c%c%c%c%c%c%c prio:%d nvt:%02x/%04x", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_end_is_valid(end) ? 'v' : '-', + xive_end_is_enqueue(end) ? 'q' : '-', + xive_end_is_notify(end) ? 'n' : '-', + xive_end_is_backlog(end) ? 'b' : '-', + xive_end_is_escalate(end) ? 'e' : '-', + xive_end_is_uncond_escalation(end) ? 'u' : '-', + xive_end_is_silent_escalation(end) ? 's' : '-', + xive_end_is_firmware(end) ? 'f' : '-', + priority, nvt_blk, nvt_idx); if (qaddr_base) { - monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", - qaddr_base, qindex, qentries, qgen); + g_string_append_printf(buf, " eq:@%08"PRIx64"% 6d/%5d ^%d", + qaddr_base, qindex, qentries, qgen); xive_end_queue_pic_print_info(end, 6, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } static void xive_end_enqueue(XiveEND *end, uint32_t data) diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index dd4a76bcf0..5e6f9d1be4 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -261,7 +261,7 @@ static inline uint64_t xive_end_qaddr(XiveEND *end) be32_to_cpu(end->w3); } -void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); +void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf); void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf); void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); From 3d1e062c803c9126bcd13f15318f39798d32978f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:22:54 +0200 Subject: [PATCH 1458/2884] hw/ppc: Avoid using Monitor in xive_end_eas_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-14-philmd@linaro.org> --- hw/intc/pnv_xive.c | 9 +++++---- hw/intc/xive.c | 22 ++++++++++------------ include/hw/ppc/xive_regs.h | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 1dddbf7827..a0c6dee5db 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -1881,14 +1881,15 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) while (!xive_router_get_end(xrtr, blk, i, &end)) { xive_end_pic_print_info(&end, i++, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d END Escalation EAT\n", + chip_id, blk); i = 0; while (!xive_router_get_end(xrtr, blk, i, &end)) { - xive_end_eas_pic_print_info(&end, i++, mon); + xive_end_eas_pic_print_info(&end, i++, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] #%d NVTT %08x .. %08x\n", chip_id, blk, 0, XIVE_NVT_COUNT - 1); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index f631d7cd6e..70f11f993b 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -17,7 +17,6 @@ #include "sysemu/reset.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/irq.h" #include "hw/ppc/xive.h" #include "hw/ppc/xive2.h" @@ -1419,8 +1418,7 @@ static void xive_end_enqueue(XiveEND *end, uint32_t data) end->w1 = xive_set_field32(END_W1_PAGE_OFF, end->w1, qindex); } -void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, - Monitor *mon) +void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf) { XiveEAS *eas = (XiveEAS *) &end->w4; uint8_t pq; @@ -1431,15 +1429,15 @@ void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, pq = xive_get_field32(END_W1_ESe, end->w1); - monitor_printf(mon, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_eas_is_valid(eas) ? 'V' : ' ', - xive_eas_is_masked(eas) ? 'M' : ' ', - (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_eas_is_valid(eas) ? 'V' : ' ', + xive_eas_is_masked(eas) ? 'M' : ' ', + (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); } /* diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index 5e6f9d1be4..b9db7abc2e 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -263,7 +263,7 @@ static inline uint64_t xive_end_qaddr(XiveEND *end) void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf); void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf); -void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); +void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf); /* Notification Virtual Target (NVT) */ typedef struct XiveNVT { From 1a40b0ca9e95f7ab31463701b696fde27811ad3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:24:07 +0200 Subject: [PATCH 1459/2884] hw/ppc: Avoid using Monitor in xive_nvt_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-15-philmd@linaro.org> --- hw/intc/pnv_xive.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index a0c6dee5db..3ad4ac8e7d 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -1832,7 +1832,7 @@ static const MemoryRegionOps pnv_xive_pc_ops = { }; static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx, - Monitor *mon) + GString *buf) { uint8_t eq_blk = xive_get_field32(NVT_W1_EQ_BLOCK, nvt->w1); uint32_t eq_idx = xive_get_field32(NVT_W1_EQ_INDEX, nvt->w1); @@ -1841,9 +1841,9 @@ static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx, return; } - monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x\n", nvt_idx, - eq_blk, eq_idx, - xive_get_field32(NVT_W4_IPB, nvt->w4)); + g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x\n", + nvt_idx, eq_blk, eq_idx, + xive_get_field32(NVT_W4_IPB, nvt->w4)); } void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) @@ -1888,17 +1888,18 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) while (!xive_router_get_end(xrtr, blk, i, &end)) { xive_end_eas_pic_print_info(&end, i++, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "XIVE[%x] #%d NVTT %08x .. %08x\n", chip_id, blk, - 0, XIVE_NVT_COUNT - 1); + g_string_append_printf(buf, "XIVE[%x] #%d NVTT %08x .. %08x\n", + chip_id, blk, 0, XIVE_NVT_COUNT - 1); xive_nvt_per_subpage = pnv_xive_vst_per_subpage(xive, VST_TSEL_VPDT); for (i = 0; i < XIVE_NVT_COUNT; i += xive_nvt_per_subpage) { while (!xive_router_get_nvt(xrtr, blk, i, &nvt)) { - xive_nvt_pic_print_info(&nvt, i++, mon); + xive_nvt_pic_print_info(&nvt, i++, buf); } } + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } static void pnv_xive_reset(void *dev) From 0527563a47fbccdf0b35c4c021cbf58d2c421777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:28:54 +0200 Subject: [PATCH 1460/2884] hw/ppc: Avoid using Monitor in pnv_xive_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-16-philmd@linaro.org> --- hw/intc/pnv_xive.c | 9 +-------- hw/ppc/pnv.c | 8 +++++++- include/hw/ppc/pnv_xive.h | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 3ad4ac8e7d..5bacbce6a4 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -11,12 +11,10 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" #include "sysemu/reset.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" @@ -1846,7 +1844,7 @@ static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx, xive_get_field32(NVT_W4_IPB, nvt->w4)); } -void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) +void pnv_xive_pic_print_info(PnvXive *xive, GString *buf) { XiveRouter *xrtr = XIVE_ROUTER(xive); uint8_t blk = pnv_xive_block_id(xive); @@ -1858,8 +1856,6 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) XiveNVT nvt; int i; uint64_t xive_nvt_per_subpage; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; g_string_append_printf(buf, "XIVE[%x] #%d Source %08x .. %08x\n", chip_id, blk, srcno0, srcno0 + nr_ipis - 1); @@ -1897,9 +1893,6 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) xive_nvt_pic_print_info(&nvt, i++, buf); } } - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static void pnv_xive_reset(void *dev) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index df187c5180..7e6f923c7e 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -808,8 +808,14 @@ static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) { Pnv9Chip *chip9 = PNV9_CHIP(chip); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + pnv_xive_pic_print_info(&chip9->xive, buf); + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); - pnv_xive_pic_print_info(&chip9->xive, mon); pnv_psi_pic_print_info(&chip9->psi, mon); object_child_foreach_recursive(OBJECT(chip), diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index 9c48430ee4..7d634e469c 100644 --- a/include/hw/ppc/pnv_xive.h +++ b/include/hw/ppc/pnv_xive.h @@ -93,7 +93,7 @@ struct PnvXiveClass { DeviceRealize parent_realize; }; -void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon); +void pnv_xive_pic_print_info(PnvXive *xive, GString *buf); /* * XIVE2 interrupt controller (POWER10) From d88f39dba47cbd4a5d989c7732e59d0fded980a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:56:36 +0200 Subject: [PATCH 1461/2884] hw/ppc: Avoid using Monitor in pnv_psi_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-17-philmd@linaro.org> --- hw/ppc/pnv.c | 10 +++++++--- hw/ppc/pnv_psi.c | 9 +-------- include/hw/ppc/pnv_psi.h | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7e6f923c7e..4a1a302a25 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -812,12 +812,11 @@ static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) g_autoptr(HumanReadableText) info = NULL; pnv_xive_pic_print_info(&chip9->xive, buf); + pnv_psi_pic_print_info(&chip9->psi, buf); info = human_readable_text_from_str(buf); monitor_puts(mon, info->human_readable_text); - pnv_psi_pic_print_info(&chip9->psi, mon); - object_child_foreach_recursive(OBJECT(chip), pnv_chip_power9_pic_print_info_child, mon); } @@ -862,9 +861,14 @@ static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq) static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) { Pnv10Chip *chip10 = PNV10_CHIP(chip); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; pnv_xive2_pic_print_info(&chip10->xive, mon); - pnv_psi_pic_print_info(&chip10->psi, mon); + + pnv_psi_pic_print_info(&chip10->psi, buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); object_child_foreach_recursive(OBJECT(chip), pnv_chip_power9_pic_print_info_child, mon); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 922ac07b2f..18cc76a7e4 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -25,8 +25,6 @@ #include "qemu/module.h" #include "sysemu/reset.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" @@ -978,11 +976,9 @@ static void pnv_psi_register_types(void) type_init(pnv_psi_register_types); -void pnv_psi_pic_print_info(Pnv9Psi *psi9, Monitor *mon) +void pnv_psi_pic_print_info(Pnv9Psi *psi9, GString *buf) { PnvPsi *psi = PNV_PSI(psi9); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; uint32_t offset = (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); @@ -990,7 +986,4 @@ void pnv_psi_pic_print_info(Pnv9Psi *psi9, Monitor *mon) g_string_append_printf(buf, "PSIHB Source %08x .. %08x\n", offset, offset + psi9->source.nr_irqs - 1); xive_source_pic_print_info(&psi9->source, offset, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h index 2a6f715350..cf7f95a6b1 100644 --- a/include/hw/ppc/pnv_psi.h +++ b/include/hw/ppc/pnv_psi.h @@ -110,6 +110,6 @@ typedef enum PnvPsiIrq { #define PSIHB9_IRQ_PSU 13 #define PSIHB9_NUM_IRQS 14 -void pnv_psi_pic_print_info(Pnv9Psi *psi, Monitor *mon); +void pnv_psi_pic_print_info(Pnv9Psi *psi, GString *buf); #endif /* PPC_PNV_PSI_H */ From 0018666462233d21b69153d5c92fc5581f15e094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 15:59:41 +0200 Subject: [PATCH 1462/2884] hw/ppc: Avoid using Monitor in xive2_eas_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-18-philmd@linaro.org> --- hw/intc/pnv_xive2.c | 16 ++++++++-------- hw/intc/xive2.c | 12 ++++++------ include/hw/ppc/xive2_regs.h | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 83e0f6b09f..649e5001fd 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2120,24 +2120,24 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) g_autoptr(GString) buf = g_string_new(""); g_autoptr(HumanReadableText) info = NULL; - monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, - srcno0 + nr_esbs - 1); + g_string_append_printf(buf, "XIVE[%x] Source %08x .. %08x\n", + blk, srcno0, srcno0 + nr_esbs - 1); xive_source_pic_print_info(&xive->ipi_source, srcno0, buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - - monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0, - srcno0 + nr_esbs - 1); + g_string_append_printf(buf, "XIVE[%x] EAT %08x .. %08x\n", + blk, srcno0, srcno0 + nr_esbs - 1); for (i = 0; i < nr_esbs; i++) { if (xive2_router_get_eas(xrtr, blk, i, &eas)) { break; } if (!xive2_eas_is_masked(&eas)) { - xive2_eas_pic_print_info(&eas, i, mon); + xive2_eas_pic_print_info(&eas, i, buf); } } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); + monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); i = 0; while (!xive2_router_get_end(xrtr, blk, i, &end)) { diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 98c0d8ba44..0154ebb59c 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -27,17 +27,17 @@ uint32_t xive2_router_get_config(Xive2Router *xrtr) return xrc->get_config(xrtr); } -void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, Monitor *mon) +void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) { if (!xive2_eas_is_valid(eas)) { return; } - monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n", - lisn, xive2_eas_is_masked(eas) ? "M" : " ", - (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %s end:%02x/%04x data:%08x\n", + lisn, xive2_eas_is_masked(eas) ? "M" : " ", + (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 816f5d0e84..f662f0e325 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -48,7 +48,7 @@ typedef struct Xive2Eas { #define xive2_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS2_VALID) #define xive2_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS2_MASKED) -void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, Monitor *mon); +void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf); /* * Event Notifification Descriptor (END) From 9d5c1da9c04d59871c69cf18e39051e6d63da78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:01:20 +0200 Subject: [PATCH 1463/2884] hw/ppc: Avoid using Monitor in xive2_end_eas_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-19-philmd@linaro.org> --- hw/intc/pnv_xive2.c | 10 +++++----- hw/intc/xive2.c | 20 ++++++++++---------- include/hw/ppc/xive2_regs.h | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 649e5001fd..9535f60828 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2135,14 +2135,14 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) } } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - - monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d END Escalation EAT\n", + chip_id, blk); i = 0; while (!xive2_router_get_end(xrtr, blk, i, &end)) { - xive2_end_eas_pic_print_info(&end, i++, mon); + xive2_end_eas_pic_print_info(&end, i++, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); i = 0; diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 0154ebb59c..23356acff7 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -116,7 +116,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) } void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, - Monitor *mon) + GString *buf) { Xive2Eas *eas = (Xive2Eas *) &end->w4; uint8_t pq; @@ -127,15 +127,15 @@ void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, pq = xive_get_field32(END2_W1_ESe, end->w1); - monitor_printf(mon, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive2_eas_is_valid(eas) ? 'v' : ' ', - xive2_eas_is_masked(eas) ? 'M' : ' ', - (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive2_eas_is_valid(eas) ? 'v' : ' ', + xive2_eas_is_masked(eas) ? 'M' : ' ', + (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } static void xive2_end_enqueue(Xive2End *end, uint32_t data) diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index f662f0e325..37f572ed6d 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -134,7 +134,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon); void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, Monitor *mon); void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, - Monitor *mon); + GString *buf); /* * Notification Virtual Processor (NVP) From fd32d8233989b59098ac0c4bdb934f4c4888883b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:04:56 +0200 Subject: [PATCH 1464/2884] hw/ppc: Avoid using Monitor in xive2_end_queue_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-20-philmd@linaro.org> --- hw/intc/xive2.c | 19 ++++++++++++------- include/hw/ppc/xive2_regs.h | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 23356acff7..6a15b4d1e4 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -11,6 +11,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" @@ -40,8 +41,7 @@ void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } -void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, - Monitor *mon) +void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf) { uint64_t qaddr_base = xive2_end_qaddr(end); uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); @@ -52,7 +52,7 @@ void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, /* * print out the [ (qindex - (width - 1)) .. (qindex + 1)] window */ - monitor_printf(mon, " [ "); + g_string_append_printf(buf, " [ "); qindex = (qindex - (width - 1)) & (qentries - 1); for (i = 0; i < width; i++) { uint64_t qaddr = qaddr_base + (qindex << 2); @@ -64,11 +64,11 @@ void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, HWADDR_PRIx "\n", qaddr); return; } - monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "", - be32_to_cpu(qdata)); + g_string_append_printf(buf, "%s%08x ", i == width - 1 ? "^" : "", + be32_to_cpu(qdata)); qindex = (qindex + 1) & (qentries - 1); } - monitor_printf(mon, "]"); + g_string_append_printf(buf, "]"); } void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) @@ -108,9 +108,14 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) priority, nvp_blk, nvp_idx); if (qaddr_base) { + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", qaddr_base, qindex, qentries, qgen); - xive2_end_queue_pic_print_info(end, 6, mon); + xive2_end_queue_pic_print_info(end, 6, buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } monitor_printf(mon, "\n"); } diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 37f572ed6d..4d32703c26 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -132,7 +132,7 @@ static inline uint64_t xive2_end_qaddr(Xive2End *end) void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon); void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, - Monitor *mon); + GString *buf); void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf); From 33e3642684a9f7a62c6275573fb8588819825347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:03:14 +0200 Subject: [PATCH 1465/2884] hw/ppc: Avoid using Monitor in xive2_end_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-21-philmd@linaro.org> --- hw/intc/pnv_xive2.c | 8 +++---- hw/intc/xive2.c | 48 ++++++++++++++++--------------------- include/hw/ppc/xive2_regs.h | 2 +- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 9535f60828..52505fd1a4 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2141,14 +2141,14 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) while (!xive2_router_get_end(xrtr, blk, i, &end)) { xive2_end_eas_pic_print_info(&end, i++, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d ENDT\n", chip_id, blk); i = 0; while (!xive2_router_get_end(xrtr, blk, i, &end)) { - xive2_end_pic_print_info(&end, i++, mon); + xive2_end_pic_print_info(&end, i++, buf); } + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); monitor_printf(mon, "XIVE[%x] #%d NVPT %08x .. %08x\n", chip_id, blk, 0, XIVE2_NVP_COUNT - 1); diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 6a15b4d1e4..3e7238c663 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -11,12 +11,10 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" #include "hw/qdev-properties.h" -#include "monitor/monitor.h" #include "hw/ppc/xive.h" #include "hw/ppc/xive2.h" #include "hw/ppc/xive2_regs.h" @@ -71,7 +69,7 @@ void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf) g_string_append_printf(buf, "]"); } -void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) +void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) { uint64_t qaddr_base = xive2_end_qaddr(end); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); @@ -90,34 +88,30 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) pq = xive_get_field32(END2_W1_ESn, end->w1); - monitor_printf(mon, - " %08x %c%c %c%c%c%c%c%c%c%c%c%c prio:%d nvp:%02x/%04x", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive2_end_is_valid(end) ? 'v' : '-', - xive2_end_is_enqueue(end) ? 'q' : '-', - xive2_end_is_notify(end) ? 'n' : '-', - xive2_end_is_backlog(end) ? 'b' : '-', - xive2_end_is_escalate(end) ? 'e' : '-', - xive2_end_is_escalate_end(end) ? 'N' : '-', - xive2_end_is_uncond_escalation(end) ? 'u' : '-', - xive2_end_is_silent_escalation(end) ? 's' : '-', - xive2_end_is_firmware1(end) ? 'f' : '-', - xive2_end_is_firmware2(end) ? 'F' : '-', - priority, nvp_blk, nvp_idx); + g_string_append_printf(buf, + " %08x %c%c %c%c%c%c%c%c%c%c%c%c " + "prio:%d nvp:%02x/%04x", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive2_end_is_valid(end) ? 'v' : '-', + xive2_end_is_enqueue(end) ? 'q' : '-', + xive2_end_is_notify(end) ? 'n' : '-', + xive2_end_is_backlog(end) ? 'b' : '-', + xive2_end_is_escalate(end) ? 'e' : '-', + xive2_end_is_escalate_end(end) ? 'N' : '-', + xive2_end_is_uncond_escalation(end) ? 'u' : '-', + xive2_end_is_silent_escalation(end) ? 's' : '-', + xive2_end_is_firmware1(end) ? 'f' : '-', + xive2_end_is_firmware2(end) ? 'F' : '-', + priority, nvp_blk, nvp_idx); if (qaddr_base) { - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - - monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", - qaddr_base, qindex, qentries, qgen); + g_string_append_printf(buf, " eq:@%08"PRIx64"% 6d/%5d ^%d", + qaddr_base, qindex, qentries, qgen); xive2_end_queue_pic_print_info(end, 6, buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 4d32703c26..4e5e17cd89 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -130,7 +130,7 @@ static inline uint64_t xive2_end_qaddr(Xive2End *end) (be32_to_cpu(end->w3) & END2_W3_EQ_ADDR_LO); } -void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon); +void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf); void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf); void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, From e6024fd8328f66f26a181764b989885c1daad2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:11:09 +0200 Subject: [PATCH 1466/2884] hw/ppc: Avoid using Monitor in xive2_nvp_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-22-philmd@linaro.org> --- hw/intc/pnv_xive2.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 52505fd1a4..10914b04df 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2028,7 +2028,7 @@ static void pnv_xive2_register_types(void) type_init(pnv_xive2_register_types) static void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, - Monitor *mon) + GString *buf) { uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); @@ -2037,21 +2037,21 @@ static void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, return; } - monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x", - nvp_idx, eq_blk, eq_idx, - xive_get_field32(NVP2_W2_IPB, nvp->w2)); + g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x", + nvp_idx, eq_blk, eq_idx, + xive_get_field32(NVP2_W2_IPB, nvp->w2)); /* * When the NVP is HW controlled, more fields are updated */ if (xive2_nvp_is_hw(nvp)) { - monitor_printf(mon, " CPPR:%02x", - xive_get_field32(NVP2_W2_CPPR, nvp->w2)); + g_string_append_printf(buf, " CPPR:%02x", + xive_get_field32(NVP2_W2_CPPR, nvp->w2)); if (xive2_nvp_is_co(nvp)) { - monitor_printf(mon, " CO:%04x", - xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); + g_string_append_printf(buf, " CO:%04x", + xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); } } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } /* @@ -2147,15 +2147,16 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) while (!xive2_router_get_end(xrtr, blk, i, &end)) { xive2_end_pic_print_info(&end, i++, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - monitor_printf(mon, "XIVE[%x] #%d NVPT %08x .. %08x\n", chip_id, blk, - 0, XIVE2_NVP_COUNT - 1); + g_string_append_printf(buf, "XIVE[%x] #%d NVPT %08x .. %08x\n", + chip_id, blk, 0, XIVE2_NVP_COUNT - 1); xive_nvp_per_subpage = pnv_xive2_vst_per_subpage(xive, VST_NVP); for (i = 0; i < XIVE2_NVP_COUNT; i += xive_nvp_per_subpage) { while (!xive2_router_get_nvp(xrtr, blk, i, &nvp)) { - xive2_nvp_pic_print_info(&nvp, i++, mon); + xive2_nvp_pic_print_info(&nvp, i++, buf); } } + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } From 70fb275d076d35e1cb01dbc8b0193144b1fbaba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:08:56 +0200 Subject: [PATCH 1467/2884] hw/ppc: Avoid using Monitor in pnv_xive2_pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-23-philmd@linaro.org> --- hw/intc/pnv_xive2.c | 9 +-------- hw/ppc/pnv.c | 2 +- include/hw/ppc/pnv_xive.h | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 10914b04df..2fb4fa29d4 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -10,11 +10,9 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" @@ -2105,7 +2103,7 @@ static uint64_t pnv_xive2_vst_per_subpage(PnvXive2 *xive, uint32_t type) return (1ull << page_shift) / info->size; } -void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) +void pnv_xive2_pic_print_info(PnvXive2 *xive, GString *buf) { Xive2Router *xrtr = XIVE2_ROUTER(xive); uint8_t blk = pnv_xive2_block_id(xive); @@ -2117,8 +2115,6 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) Xive2Nvp nvp; int i; uint64_t xive_nvp_per_subpage; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; g_string_append_printf(buf, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, srcno0 + nr_esbs - 1); @@ -2156,7 +2152,4 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) xive2_nvp_pic_print_info(&nvp, i++, buf); } } - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4a1a302a25..5b9dbff754 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -864,7 +864,7 @@ static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) g_autoptr(GString) buf = g_string_new(""); g_autoptr(HumanReadableText) info = NULL; - pnv_xive2_pic_print_info(&chip10->xive, mon); + pnv_xive2_pic_print_info(&chip10->xive, buf); pnv_psi_pic_print_info(&chip10->psi, buf); info = human_readable_text_from_str(buf); diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index 7d634e469c..5b4cb4167b 100644 --- a/include/hw/ppc/pnv_xive.h +++ b/include/hw/ppc/pnv_xive.h @@ -163,6 +163,6 @@ typedef struct PnvXive2Class { DeviceRealize parent_realize; } PnvXive2Class; -void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon); +void pnv_xive2_pic_print_info(PnvXive2 *xive, GString *buf); #endif /* PPC_PNV_XIVE_H */ From 4abeadf65161edf45989b5ada81be5e002106342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:21:05 +0200 Subject: [PATCH 1468/2884] hw/ppc: Avoid using Monitor in SpaprInterruptControllerClass::print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-24-philmd@linaro.org> --- hw/intc/spapr_xive.c | 9 +-------- hw/intc/xics_spapr.c | 9 +-------- hw/ppc/spapr_irq.c | 8 +++++++- include/hw/ppc/spapr_irq.h | 2 +- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 9d0d5948ff..283a6b8fd2 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -11,13 +11,11 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" #include "qemu/error-report.h" #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/reset.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" @@ -701,12 +699,10 @@ static void spapr_xive_set_irq(SpaprInterruptController *intc, int irq, int val) } } -static void spapr_xive_print_info(SpaprInterruptController *intc, Monitor *mon) +static void spapr_xive_print_info(SpaprInterruptController *intc, GString *buf) { SpaprXive *xive = SPAPR_XIVE(intc); CPUState *cs; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -714,9 +710,6 @@ static void spapr_xive_print_info(SpaprInterruptController *intc, Monitor *mon) xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, buf); } spapr_xive_pic_print_info(xive, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 1926373ebd..a0d97bdefe 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -34,8 +34,6 @@ #include "hw/ppc/xics_spapr.h" #include "hw/ppc/fdt.h" #include "qapi/visitor.h" -#include "qapi/type-helpers.h" -#include "monitor/monitor.h" /* * Guest interfaces @@ -397,12 +395,10 @@ static void xics_spapr_set_irq(SpaprInterruptController *intc, int irq, int val) ics_set_irq(ics, srcno, val); } -static void xics_spapr_print_info(SpaprInterruptController *intc, Monitor *mon) +static void xics_spapr_print_info(SpaprInterruptController *intc, GString *buf) { ICSState *ics = ICS_SPAPR(intc); CPUState *cs; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -410,9 +406,6 @@ static void xics_spapr_print_info(SpaprInterruptController *intc, Monitor *mon) icp_pic_print_info(spapr_cpu_state(cpu)->icp, buf); } ics_pic_print_info(ics, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static int xics_spapr_post_load(SpaprInterruptController *intc, int version_id) diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index 97b2fc42ab..b43917e7fe 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -11,6 +11,7 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/type-helpers.h" #include "hw/irq.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" @@ -18,6 +19,7 @@ #include "hw/ppc/xics.h" #include "hw/ppc/xics_spapr.h" #include "hw/qdev-properties.h" +#include "monitor/monitor.h" #include "cpu-models.h" #include "sysemu/kvm.h" @@ -269,8 +271,12 @@ void spapr_irq_print_info(SpaprMachineState *spapr, Monitor *mon) { SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; - sicc->print_info(spapr->active_intc, mon); + sicc->print_info(spapr->active_intc, buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } void spapr_irq_dt(SpaprMachineState *spapr, uint32_t nr_servers, diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h index 4fd2d5853d..6e50470cff 100644 --- a/include/hw/ppc/spapr_irq.h +++ b/include/hw/ppc/spapr_irq.h @@ -73,7 +73,7 @@ struct SpaprInterruptControllerClass { /* These methods should only be called on the active intc */ void (*set_irq)(SpaprInterruptController *intc, int irq, int val); - void (*print_info)(SpaprInterruptController *intc, Monitor *mon); + void (*print_info)(SpaprInterruptController *intc, GString *buf); void (*dt)(SpaprInterruptController *intc, uint32_t nr_servers, void *fdt, uint32_t phandle); int (*post_load)(SpaprInterruptController *intc, int version_id); From f50bb2a26aeedc0310e8da567190790bb1d2cd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:24:04 +0200 Subject: [PATCH 1469/2884] hw/ppc: Avoid using Monitor in spapr_irq_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-25-philmd@linaro.org> --- hw/ppc/spapr.c | 11 ++++++++--- hw/ppc/spapr_irq.c | 8 +------- include/hw/ppc/spapr_irq.h | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d7d4b188ee..a22decb643 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -31,6 +31,7 @@ #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" +#include "qapi/type-helpers.h" #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/hostmem.h" @@ -4530,10 +4531,14 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; - spapr_irq_print_info(spapr, mon); - monitor_printf(mon, "irqchip: %s\n", - kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); + spapr_irq_print_info(spapr, buf); + g_string_append_printf(buf, "irqchip: %s\n", + kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } /* diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index b43917e7fe..aebd7eaabb 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -11,7 +11,6 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qapi/type-helpers.h" #include "hw/irq.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" @@ -19,7 +18,6 @@ #include "hw/ppc/xics.h" #include "hw/ppc/xics_spapr.h" #include "hw/qdev-properties.h" -#include "monitor/monitor.h" #include "cpu-models.h" #include "sysemu/kvm.h" @@ -267,16 +265,12 @@ static void spapr_set_irq(void *opaque, int irq, int level) sicc->set_irq(spapr->active_intc, irq, level); } -void spapr_irq_print_info(SpaprMachineState *spapr, Monitor *mon) +void spapr_irq_print_info(SpaprMachineState *spapr, GString *buf) { SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; sicc->print_info(spapr->active_intc, buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } void spapr_irq_dt(SpaprMachineState *spapr, uint32_t nr_servers, diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h index 6e50470cff..cb9a85f657 100644 --- a/include/hw/ppc/spapr_irq.h +++ b/include/hw/ppc/spapr_irq.h @@ -85,7 +85,7 @@ int spapr_irq_cpu_intc_create(struct SpaprMachineState *spapr, PowerPCCPU *cpu, Error **errp); void spapr_irq_cpu_intc_reset(struct SpaprMachineState *spapr, PowerPCCPU *cpu); void spapr_irq_cpu_intc_destroy(struct SpaprMachineState *spapr, PowerPCCPU *cpu); -void spapr_irq_print_info(struct SpaprMachineState *spapr, Monitor *mon); +void spapr_irq_print_info(struct SpaprMachineState *spapr, GString *buf); void spapr_irq_dt(struct SpaprMachineState *spapr, uint32_t nr_servers, void *fdt, uint32_t phandle); From d312b62a2e5f1e6c44fe0ba50c784d4b89413cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:27:33 +0200 Subject: [PATCH 1470/2884] hw/ppc: Avoid using Monitor in pnv_chip_power9_pic_print_info_child() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-26-philmd@linaro.org> --- hw/ppc/pnv.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 5b9dbff754..9039c1777a 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -789,18 +789,14 @@ static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) { - Monitor *mon = opaque; + GString *buf = opaque; PnvPHB *phb = (PnvPHB *) object_dynamic_cast(child, TYPE_PNV_PHB); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; if (!phb) { return 0; } pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); return 0; } @@ -813,12 +809,11 @@ static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) pnv_xive_pic_print_info(&chip9->xive, buf); pnv_psi_pic_print_info(&chip9->psi, buf); + object_child_foreach_recursive(OBJECT(chip), + pnv_chip_power9_pic_print_info_child, buf); info = human_readable_text_from_str(buf); monitor_puts(mon, info->human_readable_text); - - object_child_foreach_recursive(OBJECT(chip), - pnv_chip_power9_pic_print_info_child, mon); } static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip, @@ -865,13 +860,12 @@ static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) g_autoptr(HumanReadableText) info = NULL; pnv_xive2_pic_print_info(&chip10->xive, buf); - pnv_psi_pic_print_info(&chip10->psi, buf); + object_child_foreach_recursive(OBJECT(chip), + pnv_chip_power9_pic_print_info_child, buf); + info = human_readable_text_from_str(buf); monitor_puts(mon, info->human_readable_text); - - object_child_foreach_recursive(OBJECT(chip), - pnv_chip_power9_pic_print_info_child, mon); } /* Always give the first 1GB to chip 0 else we won't boot */ From a58e653aa29cd39a0740af0a9eb3012c27326c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:25:41 +0200 Subject: [PATCH 1471/2884] hw/ppc: Avoid using Monitor in pic_print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Message-Id: <20240610062105.49848-27-philmd@linaro.org> --- hw/ppc/pnv.c | 29 +++++++---------------------- include/hw/ppc/pnv_chip.h | 2 +- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 9039c1777a..df74f032d7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -765,14 +765,11 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } -static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) +static void pnv_chip_power8_pic_print_info(PnvChip *chip, GString *buf) { Pnv8Chip *chip8 = PNV8_CHIP(chip); int i; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - ics_pic_print_info(&chip8->psi.ics, buf); for (i = 0; i < chip8->num_phbs; i++) { @@ -782,9 +779,6 @@ static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) pnv_phb3_msi_pic_print_info(&phb3->msis, buf); ics_pic_print_info(&phb3->lsis, buf); } - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) @@ -801,19 +795,14 @@ static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) return 0; } -static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) +static void pnv_chip_power9_pic_print_info(PnvChip *chip, GString *buf) { Pnv9Chip *chip9 = PNV9_CHIP(chip); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; pnv_xive_pic_print_info(&chip9->xive, buf); pnv_psi_pic_print_info(&chip9->psi, buf); object_child_foreach_recursive(OBJECT(chip), pnv_chip_power9_pic_print_info_child, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip, @@ -853,19 +842,14 @@ static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq) isa_realize_and_unref(dev, bus, &error_fatal); } -static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) +static void pnv_chip_power10_pic_print_info(PnvChip *chip, GString *buf) { Pnv10Chip *chip10 = PNV10_CHIP(chip); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; pnv_xive2_pic_print_info(&chip10->xive, buf); pnv_psi_pic_print_info(&chip10->psi, buf); object_child_foreach_recursive(OBJECT(chip), pnv_chip_power9_pic_print_info_child, buf); - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } /* Always give the first 1GB to chip 0 else we won't boot */ @@ -2363,12 +2347,13 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu, buf); } - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); for (i = 0; i < pnv->num_chips; i++) { - PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], mon); + PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], buf); } + + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index a5e428be7c..a4ed17ac59 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -154,7 +154,7 @@ struct PnvChipClass { void (*intc_print_info)(PnvChip *chip, PowerPCCPU *cpu, GString *buf); ISABus *(*isa_create)(PnvChip *chip, Error **errp); void (*dt_populate)(PnvChip *chip, void *fdt); - void (*pic_print_info)(PnvChip *chip, Monitor *mon); + void (*pic_print_info)(PnvChip *chip, GString *buf); uint64_t (*xscom_core_base)(PnvChip *chip, uint32_t core_id); uint32_t (*xscom_pcba)(PnvChip *chip, uint64_t addr); }; From b2580720d026fa1591218468891a47f42be6a335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 12:47:04 +0200 Subject: [PATCH 1472/2884] hw/intc: Avoid using Monitor in INTERRUPT_STATS_PROVIDER::print_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Monitor API by HumanReadableText one (see commit f2de406f29 "docs/devel: document expectations for QAPI data modelling for QMP" for rationale). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20240610063518.50680-2-philmd@linaro.org> --- hw/intc/goldfish_pic.c | 8 +++--- hw/intc/i8259_common.c | 14 +++++----- hw/intc/ioapic_common.c | 57 +++++++++++++++++++++-------------------- hw/intc/m68k_irqc.c | 5 ++-- hw/intc/slavio_intctl.c | 11 ++++---- hw/ppc/pnv.c | 10 +------- hw/ppc/spapr.c | 10 +------- include/hw/intc/intc.h | 2 +- monitor/hmp-cmds.c | 8 +++++- 9 files changed, 57 insertions(+), 68 deletions(-) diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index d662dfeb99..6cc1c69d26 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -12,7 +12,6 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "qemu/log.h" #include "trace.h" #include "hw/intc/intc.h" @@ -39,11 +38,12 @@ static bool goldfish_pic_get_statistics(InterruptStatsProvider *obj, return true; } -static void goldfish_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void goldfish_pic_print_info(InterruptStatsProvider *obj, GString *buf) { GoldfishPICState *s = GOLDFISH_PIC(obj); - monitor_printf(mon, "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n", - s->idx, s->pending, s->enabled); + g_string_append_printf(buf, + "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n", + s->idx, s->pending, s->enabled); } static void goldfish_pic_update(GoldfishPICState *s) diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index ee0041115c..d9558e3940 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -28,7 +28,6 @@ #include "hw/isa/i8259_internal.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "qapi/error.h" static int irq_level[16]; @@ -132,16 +131,17 @@ static bool pic_get_statistics(InterruptStatsProvider *obj, return true; } -static void pic_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void pic_print_info(InterruptStatsProvider *obj, GString *buf) { PICCommonState *s = PIC_COMMON(obj); pic_dispatch_pre_save(s); - monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " - "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", - s->master ? 0 : 1, s->irr, s->imr, s->isr, s->priority_add, - s->irq_base, s->read_reg_select, s->elcr, - s->special_fully_nested_mode); + g_string_append_printf(buf, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " + "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", + s->master ? 0 : 1, s->irr, s->imr, s->isr, + s->priority_add, + s->irq_base, s->read_reg_select, s->elcr, + s->special_fully_nested_mode); } static bool ltim_state_needed(void *opaque) diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index efbe6958c8..769896353a 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -23,7 +23,6 @@ #include "qapi/error.h" #include "qemu/module.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/intc/ioapic.h" #include "hw/intc/ioapic_internal.h" @@ -59,59 +58,62 @@ static bool ioapic_get_statistics(InterruptStatsProvider *obj, return true; } -static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap) +static void ioapic_irr_dump(GString *buf, const char *name, uint32_t bitmap) { int i; - monitor_printf(mon, "%-10s ", name); + g_string_append_printf(buf, "%-10s ", name); if (bitmap == 0) { - monitor_printf(mon, "(none)\n"); + g_string_append_printf(buf, "(none)\n"); return; } for (i = 0; i < IOAPIC_NUM_PINS; i++) { if (bitmap & (1 << i)) { - monitor_printf(mon, "%-2u ", i); + g_string_append_printf(buf, "%-2u ", i); } } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } -static void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) +static void ioapic_print_redtbl(GString *buf, IOAPICCommonState *s) { static const char *delm_str[] = { "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"}; uint32_t remote_irr = 0; int i; - monitor_printf(mon, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x", - s->version, s->id, s->ioregsel); + g_string_append_printf(buf, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x", + s->version, s->id, s->ioregsel); if (s->ioregsel) { - monitor_printf(mon, " (redir[%u])\n", - (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1); + g_string_append_printf(buf, " (redir[%u])\n", + (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1); } else { - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } for (i = 0; i < IOAPIC_NUM_PINS; i++) { uint64_t entry = s->ioredtbl[i]; uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >> IOAPIC_LVT_DELIV_MODE_SHIFT); - monitor_printf(mon, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64 - " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", - i, entry, - (entry >> IOAPIC_LVT_DEST_SHIFT) & - (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf), - entry & IOAPIC_VECTOR_MASK, - entry & IOAPIC_LVT_POLARITY ? "active-lo" : "active-hi", - entry & IOAPIC_LVT_TRIGGER_MODE ? "level" : "edge", - entry & IOAPIC_LVT_MASKED ? "masked" : "", - delm_str[delm], - entry & IOAPIC_LVT_DEST_MODE ? "logical" : "physical"); + g_string_append_printf(buf, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64 + " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", + i, entry, + (entry >> IOAPIC_LVT_DEST_SHIFT) & + (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf), + entry & IOAPIC_VECTOR_MASK, + entry & IOAPIC_LVT_POLARITY + ? "active-lo" : "active-hi", + entry & IOAPIC_LVT_TRIGGER_MODE + ? "level" : "edge", + entry & IOAPIC_LVT_MASKED ? "masked" : "", + delm_str[delm], + entry & IOAPIC_LVT_DEST_MODE + ? "logical" : "physical"); remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ? (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0; } - ioapic_irr_dump(mon, " IRR", s->irr); - ioapic_irr_dump(mon, " Remote IRR", remote_irr); + ioapic_irr_dump(buf, " IRR", s->irr); + ioapic_irr_dump(buf, " Remote IRR", remote_irr); } void ioapic_reset_common(DeviceState *dev) @@ -171,13 +173,12 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp) ioapic_no++; } -static void ioapic_print_info(InterruptStatsProvider *obj, - Monitor *mon) +static void ioapic_print_info(InterruptStatsProvider *obj, GString *buf) { IOAPICCommonState *s = IOAPIC_COMMON(obj); ioapic_dispatch_pre_save(s); - ioapic_print_redtbl(mon, s); + ioapic_print_redtbl(buf, s); } static const VMStateDescription vmstate_ioapic_common = { diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 4b11fb9f72..cf3beefcfe 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -10,7 +10,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/qdev-properties.h" #include "hw/nmi.h" #include "hw/intc/intc.h" @@ -27,10 +26,10 @@ static bool m68k_irqc_get_statistics(InterruptStatsProvider *obj, return true; } -static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void m68k_irqc_print_info(InterruptStatsProvider *obj, GString *buf) { M68KIRQCState *s = M68K_IRQC(obj); - monitor_printf(mon, "m68k-irqc: ipr=0x%x\n", s->ipr); + g_string_append_printf(buf, "m68k-irqc: ipr=0x%x\n", s->ipr); } static void m68k_set_irq(void *opaque, int irq, int level) diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index 36b4a12f60..d6e49d29aa 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "hw/intc/intc.h" @@ -401,17 +400,17 @@ static bool slavio_intctl_get_statistics(InterruptStatsProvider *obj, } #endif -static void slavio_intctl_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void slavio_intctl_print_info(InterruptStatsProvider *obj, GString *buf) { SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj); int i; for (i = 0; i < MAX_CPUS; i++) { - monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i, - s->slaves[i].intreg_pending); + g_string_append_printf(buf, "per-cpu %d: pending 0x%08x\n", i, + s->slaves[i].intreg_pending); } - monitor_printf(mon, "master: pending 0x%08x, disabled 0x%08x\n", - s->intregm_pending, s->intregm_disabled); + g_string_append_printf(buf, "master: pending 0x%08x, disabled 0x%08x\n", + s->intregm_pending, s->intregm_disabled); } static void slavio_intctl_init(Object *obj) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index df74f032d7..03c595788f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -38,8 +38,6 @@ #include "hw/loader.h" #include "hw/nmi.h" #include "qapi/visitor.h" -#include "qapi/type-helpers.h" -#include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/ipmi/ipmi.h" #include "target/ppc/mmu-hash64.h" @@ -2331,14 +2329,11 @@ static ICPState *pnv_icp_get(XICSFabric *xi, int pir) return cpu ? ICP(pnv_cpu_state(cpu)->intc) : NULL; } -static void pnv_pic_print_info(InterruptStatsProvider *obj, - Monitor *mon) +static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) { PnvMachineState *pnv = PNV_MACHINE(obj); int i; CPUState *cs; - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -2351,9 +2346,6 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, for (i = 0; i < pnv->num_chips; i++) { PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], buf); } - - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a22decb643..a9908545e6 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -31,7 +31,6 @@ #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" -#include "qapi/type-helpers.h" #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/hostmem.h" @@ -90,8 +89,6 @@ #include "hw/ppc/spapr_nvdimm.h" #include "hw/ppc/spapr_numa.h" -#include "monitor/monitor.h" - #include /* SLOF memory layout: @@ -4527,18 +4524,13 @@ static ICPState *spapr_icp_get(XICSFabric *xi, int vcpu_id) return cpu ? spapr_cpu_state(cpu)->icp : NULL; } -static void spapr_pic_print_info(InterruptStatsProvider *obj, - Monitor *mon) +static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; spapr_irq_print_info(spapr, buf); g_string_append_printf(buf, "irqchip: %s\n", kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); } /* diff --git a/include/hw/intc/intc.h b/include/hw/intc/intc.h index 7018f608ca..e40194b8e3 100644 --- a/include/hw/intc/intc.h +++ b/include/hw/intc/intc.h @@ -22,7 +22,7 @@ struct InterruptStatsProviderClass { */ bool (*get_statistics)(InterruptStatsProvider *obj, uint64_t **irq_counts, unsigned int *nb_irqs); - void (*print_info)(InterruptStatsProvider *obj, Monitor *mon); + void (*print_info)(InterruptStatsProvider *obj, GString *buf); }; #endif diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index ea79148ee8..fbff7fdb57 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -25,6 +25,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" +#include "qapi/type-helpers.h" #include "qemu/cutils.h" #include "hw/intc/intc.h" #include "qemu/log.h" @@ -92,7 +93,12 @@ static int hmp_info_pic_foreach(Object *obj, void *opaque) intc = INTERRUPT_STATS_PROVIDER(obj); k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); if (k->print_info) { - k->print_info(intc, mon); + g_autoptr(GString) buf = g_string_new(""); + g_autoptr(HumanReadableText) info = NULL; + + k->print_info(intc, buf); + info = human_readable_text_from_str(buf); + monitor_puts(mon, info->human_readable_text); } else { monitor_printf(mon, "Interrupt controller information not available for %s.\n", object_get_typename(obj)); From 795eaa62fa68c452b926fbfc4ccdcb4cd1552531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 7 Jun 2024 16:37:24 +0200 Subject: [PATCH 1473/2884] hw/intc: Introduce x-query-interrupt-controllers QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a counterpart to the HMP "info pic" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20240610063518.50680-3-philmd@linaro.org> --- hmp-commands-info.hx | 2 +- hw/core/machine-qmp-cmds.c | 29 +++++++++++++++++++++++++++++ monitor/hmp-cmds.c | 33 --------------------------------- qapi/machine.json | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 20a9835ea8..cfd4ad5651 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -174,7 +174,7 @@ ERST .args_type = "", .params = "", .help = "show PIC state", - .cmd = hmp_info_pic, + .cmd_info_hrt = qmp_x_query_interrupt_controllers, }, SRST diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 5972100b1f..130217da8f 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -361,6 +361,35 @@ HumanReadableText *qmp_x_query_irq(Error **errp) return human_readable_text_from_str(buf); } +static int qmp_x_query_intc_foreach(Object *obj, void *opaque) +{ + InterruptStatsProvider *intc; + InterruptStatsProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { + intc = INTERRUPT_STATS_PROVIDER(obj); + k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); + if (k->print_info) { + k->print_info(intc, buf); + } else { + g_string_append_printf(buf, + "Interrupt controller information not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_interrupt_controllers(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + object_child_foreach_recursive(object_get_root(), + qmp_x_query_intc_foreach, buf); + return human_readable_text_from_str(buf); +} + GuidInfo *qmp_query_vm_generation_id(Error **errp) { GuidInfo *info; diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index fbff7fdb57..45ee3a9e1f 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -25,9 +25,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" -#include "qapi/type-helpers.h" #include "qemu/cutils.h" -#include "hw/intc/intc.h" #include "qemu/log.h" #include "sysemu/sysemu.h" @@ -83,37 +81,6 @@ void hmp_info_version(Monitor *mon, const QDict *qdict) qapi_free_VersionInfo(info); } -static int hmp_info_pic_foreach(Object *obj, void *opaque) -{ - InterruptStatsProvider *intc; - InterruptStatsProviderClass *k; - Monitor *mon = opaque; - - if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { - intc = INTERRUPT_STATS_PROVIDER(obj); - k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); - if (k->print_info) { - g_autoptr(GString) buf = g_string_new(""); - g_autoptr(HumanReadableText) info = NULL; - - k->print_info(intc, buf); - info = human_readable_text_from_str(buf); - monitor_puts(mon, info->human_readable_text); - } else { - monitor_printf(mon, "Interrupt controller information not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -void hmp_info_pic(Monitor *mon, const QDict *qdict) -{ - object_child_foreach_recursive(object_get_root(), - hmp_info_pic_foreach, mon); -} - void hmp_quit(Monitor *mon, const QDict *qdict) { monitor_suspend(mon); diff --git a/qapi/machine.json b/qapi/machine.json index 453feb9347..2fd3e9c3d5 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1864,3 +1864,20 @@ { 'command': 'dumpdtb', 'data': { 'filename': 'str' }, 'if': 'CONFIG_FDT' } + +## +# @x-query-interrupt-controllers: +# +# Query information on interrupt controller devices +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: Interrupt controller devices information +# +# Since: 9.1 +## +{ 'command': 'x-query-interrupt-controllers', + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ]} From 80ce0d5874d60dac171ec9395a2aaba91a25c1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 24 Apr 2024 11:30:48 +0200 Subject: [PATCH 1474/2884] ppc/pnv: Introduce pnv_chip_foreach_cpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper routine uses the machine definition, sockets, cores and threads, to loop on all CPUs of the machine. Replace CPU_FOREACH() with it. Signed-off-by: Cédric Le Goater Reviewed-by: Nicholas Piggin Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240424093048.180966-1-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/pnv.c | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 03c595788f..6b41d1d2dd 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2261,6 +2261,21 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) return NULL; } +static void pnv_chip_foreach_cpu(PnvChip *chip, + void (*fn)(PnvChip *chip, PowerPCCPU *cpu, void *opaque), + void *opaque) +{ + int i, j; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + + for (j = 0; j < CPU_CORE(pc)->nr_threads; j++) { + fn(chip, pc->threads[j], opaque); + } + } +} + static ICSState *pnv_ics_get(XICSFabric *xi, int irq) { PnvMachineState *pnv = PNV_MACHINE(xi); @@ -2329,22 +2344,25 @@ static ICPState *pnv_icp_get(XICSFabric *xi, int pir) return cpu ? ICP(pnv_cpu_state(cpu)->intc) : NULL; } +static void pnv_pic_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + void *opaque) +{ + PNV_CHIP_GET_CLASS(chip)->intc_print_info(chip, cpu, opaque); +} + static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) { PnvMachineState *pnv = PNV_MACHINE(obj); int i; - CPUState *cs; - - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - - /* XXX: loop on each chip/core/thread instead of CPU_FOREACH() */ - PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu, - buf); - } for (i = 0; i < pnv->num_chips; i++) { - PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], buf); + PnvChip *chip = pnv->chips[i]; + + /* First CPU presenters */ + pnv_chip_foreach_cpu(chip, pnv_pic_intc_print_info, buf); + + /* Then other devices, PHB, PSI, XIVE */ + PNV_CHIP_GET_CLASS(chip)->pic_print_info(chip, buf); } } @@ -2545,12 +2563,18 @@ static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) } } +static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) +{ + async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); +} + static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) { - CPUState *cs; + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; - CPU_FOREACH(cs) { - async_run_on_cpu(cs, pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); + for (i = 0; i < pnv->num_chips; i++) { + pnv_chip_foreach_cpu(pnv->chips[i], pnv_cpu_do_nmi, NULL); } } From ec40be993b7b12b3f338ccf79f7eaaef6d1870f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 14:05:33 +0200 Subject: [PATCH 1475/2884] memory: Constify IOMMUTLBEvent in memory_region_notify_iommu_one() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @event access is read-only. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Message-Id: <20240612132532.85928-2-philmd@linaro.org> --- include/exec/memory.h | 2 +- system/memory.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 1be58f694c..2bf5e23b6a 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1852,7 +1852,7 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, * range. */ void memory_region_notify_iommu_one(IOMMUNotifier *notifier, - IOMMUTLBEvent *event); + const IOMMUTLBEvent *event); /** * memory_region_unmap_iommu_notifier_range: notify a unmap for an IOMMU diff --git a/system/memory.c b/system/memory.c index 74cd73ebc7..f3a37c97c1 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2006,9 +2006,9 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr, } void memory_region_notify_iommu_one(IOMMUNotifier *notifier, - IOMMUTLBEvent *event) + const IOMMUTLBEvent *event) { - IOMMUTLBEntry *entry = &event->entry; + const IOMMUTLBEntry *entry = &event->entry; hwaddr entry_end = entry->iova + entry->addr_mask; IOMMUTLBEntry tmp = *entry; From eb5b2896f608d9c0dcd53375987db2377337f752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 14:06:20 +0200 Subject: [PATCH 1476/2884] memory: Constify IOMMUTLBEvent in memory_region_notify_iommu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @event access is read-only. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Message-Id: <20240612132532.85928-3-philmd@linaro.org> --- include/exec/memory.h | 2 +- system/memory.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 2bf5e23b6a..2d7c278b9f 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1837,7 +1837,7 @@ uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr); */ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, int iommu_idx, - IOMMUTLBEvent event); + const IOMMUTLBEvent event); /** * memory_region_notify_iommu_one: notify a change in an IOMMU translation diff --git a/system/memory.c b/system/memory.c index f3a37c97c1..47c600df63 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2052,7 +2052,7 @@ void memory_region_unmap_iommu_notifier_range(IOMMUNotifier *notifier) void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, int iommu_idx, - IOMMUTLBEvent event) + const IOMMUTLBEvent event) { IOMMUNotifier *iommu_notifier; From fc9ad5cf9c9c0e5420b41d422a0c2d29c197a9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 14:08:43 +0200 Subject: [PATCH 1477/2884] hw/i386/iommu: Constify IOMMUTLBEvent in vtd_page_walk_hook prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @event access is read-only. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Message-Id: <20240612132532.85928-4-philmd@linaro.org> --- hw/i386/intel_iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index cc8e59674e..c4350e0ff0 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1170,7 +1170,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, } } -typedef int (*vtd_page_walk_hook)(IOMMUTLBEvent *event, void *private); +typedef int (*vtd_page_walk_hook)(const IOMMUTLBEvent *event, void *private); /** * Constant information used during page walking @@ -1533,7 +1533,7 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } -static int vtd_sync_shadow_page_hook(IOMMUTLBEvent *event, +static int vtd_sync_shadow_page_hook(const IOMMUTLBEvent *event, void *private) { memory_region_notify_iommu(private, 0, *event); @@ -2219,7 +2219,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, * page tables. We just deliver the PSI down to * invalidate caches. */ - IOMMUTLBEvent event = { + const IOMMUTLBEvent event = { .type = IOMMU_NOTIFIER_UNMAP, .entry = { .target_as = &address_space_memory, @@ -3889,7 +3889,7 @@ static void vtd_address_space_refresh_all(IntelIOMMUState *s) vtd_switch_address_space_all(s); } -static int vtd_replay_hook(IOMMUTLBEvent *event, void *private) +static int vtd_replay_hook(const IOMMUTLBEvent *event, void *private) { memory_region_notify_iommu_one(private, event); return 0; From ce5311c47600a246995438e956f0090175e6965a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Jun 2024 10:49:53 +0200 Subject: [PATCH 1478/2884] hw/usb: Remove unused 'host.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 99761176ee ("usb: Remove legacy -usbdevice options (host, serial, disk and net)") hw/usb/host.h is not used, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20240611102305.60735-2-philmd@linaro.org> --- hw/usb/host.h | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 hw/usb/host.h diff --git a/hw/usb/host.h b/hw/usb/host.h deleted file mode 100644 index 048ff3b482..0000000000 --- a/hw/usb/host.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_USB_HOST_H -#define QEMU_USB_HOST_H - -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - -#endif /* QEMU_USB_HOST_H */ From 283720489fdadadbd99774c2cdeff6635e396415 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Tue, 18 Jun 2024 02:36:57 +0200 Subject: [PATCH 1479/2884] hw/usb/dev-mtp: Correctly report free space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to compute the amount of free space (in bytes), the number of available blocks (f_bavail) should be multiplied by the block size (f_frsize) instead of the total number of blocks (f_blocks). Signed-off-by: Fabio D'Urso Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240618003657.3344685-1-fdurso@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/dev-mtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 7e4a0765ae..554b397e88 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -886,7 +886,7 @@ static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) rc = statvfs(s->root, &buf); if (rc == 0) { usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); - usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); + usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_bavail); usb_mtp_add_u32(d, buf.f_ffree); } else { usb_mtp_add_u64(d, 0xffffffff); From 5f82fb2a3a71bb510b3e1b7229929d468c01740a Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 5 Jun 2024 03:15:02 +0100 Subject: [PATCH 1480/2884] hw/intc: Remove loongarch_ipi.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was missed out in previous commit. Fixes: b4a12dfc2132 ("hw/intc/loongarch_ipi: Rename as loongson_ipi") Signed-off-by: Jiaxun Yang Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240605-loongson3-ipi-v3-1-ddd2c0e03fa3@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/loongarch_ipi.c | 347 ---------------------------------------- 1 file changed, 347 deletions(-) delete mode 100644 hw/intc/loongarch_ipi.c diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c deleted file mode 100644 index 44b3b9c138..0000000000 --- a/hw/intc/loongarch_ipi.c +++ /dev/null @@ -1,347 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * LoongArch ipi interrupt support - * - * Copyright (C) 2021 Loongson Technology Corporation Limited - */ - -#include "qemu/osdep.h" -#include "hw/boards.h" -#include "hw/sysbus.h" -#include "hw/intc/loongarch_ipi.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "exec/address-spaces.h" -#include "migration/vmstate.h" -#include "target/loongarch/cpu.h" -#include "trace.h" - -static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, - uint64_t *data, - unsigned size, MemTxAttrs attrs) -{ - IPICore *s; - LoongArchIPI *ipi = opaque; - uint64_t ret = 0; - int index = 0; - - s = &ipi->cpu[attrs.requester_id]; - addr &= 0xff; - switch (addr) { - case CORE_STATUS_OFF: - ret = s->status; - break; - case CORE_EN_OFF: - ret = s->en; - break; - case CORE_SET_OFF: - ret = 0; - break; - case CORE_CLEAR_OFF: - ret = 0; - break; - case CORE_BUF_20 ... CORE_BUF_38 + 4: - index = (addr - CORE_BUF_20) >> 2; - ret = s->buf[index]; - break; - default: - qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); - break; - } - - trace_loongarch_ipi_read(size, (uint64_t)addr, ret); - *data = ret; - return MEMTX_OK; -} - -static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, - MemTxAttrs attrs) -{ - int i, mask = 0, data = 0; - - /* - * bit 27-30 is mask for byte writing, - * if the mask is 0, we need not to do anything. - */ - if ((val >> 27) & 0xf) { - data = address_space_ldl(env->address_space_iocsr, addr, - attrs, NULL); - for (i = 0; i < 4; i++) { - /* get mask for byte writing */ - if (val & (0x1 << (27 + i))) { - mask |= 0xff << (i * 8); - } - } - } - - data &= mask; - data |= (val >> 32) & ~mask; - address_space_stl(env->address_space_iocsr, addr, - data, attrs, NULL); -} - -static int archid_cmp(const void *a, const void *b) -{ - CPUArchId *archid_a = (CPUArchId *)a; - CPUArchId *archid_b = (CPUArchId *)b; - - return archid_a->arch_id - archid_b->arch_id; -} - -static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) -{ - CPUArchId apic_id, *found_cpu; - - apic_id.arch_id = id; - found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, - ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), - archid_cmp); - - return found_cpu; -} - -static CPUState *ipi_getcpu(int arch_id) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - CPUArchId *archid; - - archid = find_cpu_by_archid(machine, arch_id); - if (archid) { - return CPU(archid->cpu); - } - - return NULL; -} - -static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) -{ - uint32_t cpuid; - hwaddr addr; - CPUState *cs; - - cpuid = extract32(val, 16, 10); - cs = ipi_getcpu(cpuid); - if (cs == NULL) { - return MEMTX_DECODE_ERROR; - } - - /* override requester_id */ - addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); - attrs.requester_id = cs->cpu_index; - send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); - return MEMTX_OK; -} - -static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) -{ - uint32_t cpuid; - hwaddr addr; - CPUState *cs; - - cpuid = extract32(val, 16, 10); - cs = ipi_getcpu(cpuid); - if (cs == NULL) { - return MEMTX_DECODE_ERROR; - } - - /* override requester_id */ - addr = val & 0xffff; - attrs.requester_id = cs->cpu_index; - send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); - return MEMTX_OK; -} - -static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size, MemTxAttrs attrs) -{ - LoongArchIPI *ipi = opaque; - IPICore *s; - int index = 0; - uint32_t cpuid; - uint8_t vector; - CPUState *cs; - - s = &ipi->cpu[attrs.requester_id]; - addr &= 0xff; - trace_loongarch_ipi_write(size, (uint64_t)addr, val); - switch (addr) { - case CORE_STATUS_OFF: - qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); - break; - case CORE_EN_OFF: - s->en = val; - break; - case CORE_SET_OFF: - s->status |= val; - if (s->status != 0 && (s->status & s->en) != 0) { - qemu_irq_raise(s->irq); - } - break; - case CORE_CLEAR_OFF: - s->status &= ~val; - if (s->status == 0 && s->en != 0) { - qemu_irq_lower(s->irq); - } - break; - case CORE_BUF_20 ... CORE_BUF_38 + 4: - index = (addr - CORE_BUF_20) >> 2; - s->buf[index] = val; - break; - case IOCSR_IPI_SEND: - cpuid = extract32(val, 16, 10); - /* IPI status vector */ - vector = extract8(val, 0, 5); - cs = ipi_getcpu(cpuid); - if (cs == NULL) { - return MEMTX_DECODE_ERROR; - } - - /* override requester_id */ - attrs.requester_id = cs->cpu_index; - loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); - break; - default: - qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); - break; - } - - return MEMTX_OK; -} - -static const MemoryRegionOps loongarch_ipi_ops = { - .read_with_attrs = loongarch_ipi_readl, - .write_with_attrs = loongarch_ipi_writel, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .valid.min_access_size = 4, - .valid.max_access_size = 8, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* mail send and any send only support writeq */ -static MemTxResult loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, - unsigned size, MemTxAttrs attrs) -{ - MemTxResult ret = MEMTX_OK; - - addr &= 0xfff; - switch (addr) { - case MAIL_SEND_OFFSET: - ret = mail_send(val, attrs); - break; - case ANY_SEND_OFFSET: - ret = any_send(val, attrs); - break; - default: - break; - } - - return ret; -} - -static const MemoryRegionOps loongarch_ipi64_ops = { - .write_with_attrs = loongarch_ipi_writeq, - .impl.min_access_size = 8, - .impl.max_access_size = 8, - .valid.min_access_size = 8, - .valid.max_access_size = 8, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void loongarch_ipi_realize(DeviceState *dev, Error **errp) -{ - LoongArchIPI *s = LOONGARCH_IPI(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - if (s->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); - return; - } - - memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongarch_ipi_ops, - s, "loongarch_ipi_iocsr", 0x48); - - /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ - s->ipi_iocsr_mem.disable_reentrancy_guard = true; - - sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); - - memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), - &loongarch_ipi64_ops, - s, "loongarch_ipi64_iocsr", 0x118); - sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); - - s->cpu = g_new0(IPICore, s->num_cpu); - if (s->cpu == NULL) { - error_setg(errp, "Memory allocation for ExtIOICore faile"); - return; - } - - for (i = 0; i < s->num_cpu; i++) { - qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); - } -} - -static const VMStateDescription vmstate_ipi_core = { - .name = "ipi-single", - .version_id = 2, - .minimum_version_id = 2, - .fields = (const VMStateField[]) { - VMSTATE_UINT32(status, IPICore), - VMSTATE_UINT32(en, IPICore), - VMSTATE_UINT32(set, IPICore), - VMSTATE_UINT32(clear, IPICore), - VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_loongarch_ipi = { - .name = TYPE_LOONGARCH_IPI, - .version_id = 2, - .minimum_version_id = 2, - .fields = (const VMStateField[]) { - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchIPI, num_cpu, - vmstate_ipi_core, IPICore), - VMSTATE_END_OF_LIST() - } -}; - -static Property ipi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongArchIPI, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void loongarch_ipi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = loongarch_ipi_realize; - device_class_set_props(dc, ipi_properties); - dc->vmsd = &vmstate_loongarch_ipi; -} - -static void loongarch_ipi_finalize(Object *obj) -{ - LoongArchIPI *s = LOONGARCH_IPI(obj); - - g_free(s->cpu); -} - -static const TypeInfo loongarch_ipi_info = { - .name = TYPE_LOONGARCH_IPI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LoongArchIPI), - .class_init = loongarch_ipi_class_init, - .instance_finalize = loongarch_ipi_finalize, -}; - -static void loongarch_ipi_register_types(void) -{ - type_register_static(&loongarch_ipi_info); -} - -type_init(loongarch_ipi_register_types) From 49eba52a52fec563af83a77d5ec5c59dba412127 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 May 2024 14:06:49 +0100 Subject: [PATCH 1481/2884] hw/intc/loongson_ipi: Provide per core MMIO address spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real IPI hardware have dedicated MMIO registers mapped into memory address space for every core. This is not used by LoongArch guest software but it is essential for CPU without IOCSR such as Loongson-3A1000. Implement it with existing infrastructure. Acked-by: Song Gao Signed-off-by: Jiaxun Yang Message-ID: <20240605-loongson3-ipi-v3-2-ddd2c0e03fa3@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/loongson_ipi.c | 86 +++++++++++++++++++++++++--------- include/hw/intc/loongson_ipi.h | 2 + 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 93cc50a37a..08a74a0b4f 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -23,16 +23,14 @@ #endif #include "trace.h" -static MemTxResult loongson_ipi_readl(void *opaque, hwaddr addr, - uint64_t *data, - unsigned size, MemTxAttrs attrs) +static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) { - IPICore *s; - LoongsonIPI *ipi = opaque; + IPICore *s = opaque; uint64_t ret = 0; int index = 0; - s = &ipi->cpu[attrs.requester_id]; addr &= 0xff; switch (addr) { case CORE_STATUS_OFF: @@ -61,6 +59,21 @@ static MemTxResult loongson_ipi_readl(void *opaque, hwaddr addr, return MEMTX_OK; } +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + LoongsonIPI *ipi = opaque; + IPICore *s; + + if (attrs.requester_id >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + + s = &ipi->cpu[attrs.requester_id]; + return loongson_ipi_core_readl(s, addr, data, size, attrs); +} + static AddressSpace *get_cpu_iocsr_as(CPUState *cpu) { #ifdef TARGET_LOONGARCH64 @@ -174,17 +187,17 @@ static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) return send_ipi_data(cs, val, addr, attrs); } -static MemTxResult loongson_ipi_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size, MemTxAttrs attrs) +static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { - LoongsonIPI *ipi = opaque; - IPICore *s; + IPICore *s = opaque; + LoongsonIPI *ipi = s->ipi; int index = 0; uint32_t cpuid; uint8_t vector; CPUState *cs; - s = &ipi->cpu[attrs.requester_id]; addr &= 0xff; trace_loongson_ipi_write(size, (uint64_t)addr, val); switch (addr) { @@ -215,13 +228,11 @@ static MemTxResult loongson_ipi_writel(void *opaque, hwaddr addr, uint64_t val, /* IPI status vector */ vector = extract8(val, 0, 5); cs = ipi_getcpu(cpuid); - if (cs == NULL) { + if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { return MEMTX_DECODE_ERROR; } - - /* override requester_id */ - attrs.requester_id = cs->cpu_index; - loongson_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); + loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF, + BIT(vector), 4, attrs); break; default: qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); @@ -231,9 +242,34 @@ static MemTxResult loongson_ipi_writel(void *opaque, hwaddr addr, uint64_t val, return MEMTX_OK; } -static const MemoryRegionOps loongson_ipi_ops = { - .read_with_attrs = loongson_ipi_readl, - .write_with_attrs = loongson_ipi_writel, +static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LoongsonIPI *ipi = opaque; + IPICore *s; + + if (attrs.requester_id >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + + s = &ipi->cpu[attrs.requester_id]; + return loongson_ipi_core_writel(s, addr, val, size, attrs); +} + +static const MemoryRegionOps loongson_ipi_core_ops = { + .read_with_attrs = loongson_ipi_core_readl, + .write_with_attrs = loongson_ipi_core_writel, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static const MemoryRegionOps loongson_ipi_iocsr_ops = { + .read_with_attrs = loongson_ipi_iocsr_readl, + .write_with_attrs = loongson_ipi_iocsr_writel, .impl.min_access_size = 4, .impl.max_access_size = 4, .valid.min_access_size = 4, @@ -282,7 +318,8 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) return; } - memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongson_ipi_ops, + memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), + &loongson_ipi_iocsr_ops, s, "loongson_ipi_iocsr", 0x48); /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */ @@ -297,11 +334,18 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) s->cpu = g_new0(IPICore, s->num_cpu); if (s->cpu == NULL) { - error_setg(errp, "Memory allocation for ExtIOICore faile"); + error_setg(errp, "Memory allocation for IPICore faile"); return; } for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].ipi = s; + s->cpu[i].ipi_mmio_mem = g_new0(MemoryRegion, 1); + g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); + memory_region_init_io(s->cpu[i].ipi_mmio_mem, OBJECT(dev), + &loongson_ipi_core_ops, &s->cpu[i], name, 0x48); + sysbus_init_mmio(sbd, s->cpu[i].ipi_mmio_mem); + qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); } } diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h index 2c0e8820f5..3f795edbf3 100644 --- a/include/hw/intc/loongson_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -34,6 +34,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongsonIPI, LOONGSON_IPI) typedef struct IPICore { + LoongsonIPI *ipi; + MemoryRegion *ipi_mmio_mem; uint32_t status; uint32_t en; uint32_t set; From 03ca348b6b9038ce284916b36c19f700ac0ce7a6 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 5 Jun 2024 03:04:27 +0100 Subject: [PATCH 1482/2884] hw/intc/loongson_ipi: Replace ipi_getcpu with cpu_by_arch_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_by_arch_id is doing the same thing as our ipi_getcpu logic. Signed-off-by: Jiaxun Yang Reviewed-by: Song Gao Message-ID: <20240605-loongson3-ipi-v3-4-ddd2c0e03fa3@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/loongson_ipi.c | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 08a74a0b4f..e6a7142480 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -118,39 +118,6 @@ static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr, return MEMTX_OK; } -static int archid_cmp(const void *a, const void *b) -{ - CPUArchId *archid_a = (CPUArchId *)a; - CPUArchId *archid_b = (CPUArchId *)b; - - return archid_a->arch_id - archid_b->arch_id; -} - -static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) -{ - CPUArchId apic_id, *found_cpu; - - apic_id.arch_id = id; - found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, - ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), - archid_cmp); - - return found_cpu; -} - -static CPUState *ipi_getcpu(int arch_id) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - CPUArchId *archid; - - archid = find_cpu_by_archid(machine, arch_id); - if (archid) { - return CPU(archid->cpu); - } - - return NULL; -} - static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) { uint32_t cpuid; @@ -158,7 +125,7 @@ static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) CPUState *cs; cpuid = extract32(val, 16, 10); - cs = ipi_getcpu(cpuid); + cs = cpu_by_arch_id(cpuid); if (cs == NULL) { return MEMTX_DECODE_ERROR; } @@ -176,7 +143,7 @@ static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) CPUState *cs; cpuid = extract32(val, 16, 10); - cs = ipi_getcpu(cpuid); + cs = cpu_by_arch_id(cpuid); if (cs == NULL) { return MEMTX_DECODE_ERROR; } @@ -227,7 +194,7 @@ static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, cpuid = extract32(val, 16, 10); /* IPI status vector */ vector = extract8(val, 0, 5); - cs = ipi_getcpu(cpuid); + cs = cpu_by_arch_id(cpuid); if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { return MEMTX_DECODE_ERROR; } From c3425158d67103b260543e842ddcc6731133ca0f Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 8 May 2024 14:06:50 +0100 Subject: [PATCH 1483/2884] hw/mips/loongson3_virt: Wire up loongson_ipi device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up loongson_ipi device for loongson3_virt machine, so we can have SMP support for TCG backend as well. Signed-off-by: Jiaxun Yang Acked-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240605-loongson3-ipi-v3-3-ddd2c0e03fa3@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/Kconfig | 1 + hw/mips/loongson3_bootp.c | 2 -- hw/mips/loongson3_bootp.h | 3 +++ hw/mips/loongson3_virt.c | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index a7f26edebe..692bede538 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -67,6 +67,7 @@ config LOONGSON3V imply USB_OHCI_PCI select SERIAL select GOLDFISH_RTC + select LOONGSON_IPI select LOONGSON_LIOINTC select PCI_EXPRESS_GENERIC_BRIDGE select MSI_NONBROKEN diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index 03a10b63c1..b97b81903b 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -25,8 +25,6 @@ #include "hw/boards.h" #include "hw/mips/loongson3_bootp.h" -#define LOONGSON3_CORE_PER_NODE 4 - static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq) { struct efi_cpuinfo_loongson *c = g_cpuinfo; diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h index 1b0dd3b591..9091265df7 100644 --- a/hw/mips/loongson3_bootp.h +++ b/hw/mips/loongson3_bootp.h @@ -200,6 +200,8 @@ struct boot_params { struct efi_reset_system_t reset_system; }; +#define LOONGSON3_CORE_PER_NODE 4 + /* Overall MMIO & Memory layout */ enum { VIRT_LOWMEM, @@ -211,6 +213,7 @@ enum { VIRT_BIOS_ROM, VIRT_UART, VIRT_LIOINTC, + VIRT_IPI, VIRT_PCIE_MMIO, VIRT_HIGHMEM }; diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 440268a074..4ad36f0c5b 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -36,6 +36,7 @@ #include "hw/mips/loongson3_bootp.h" #include "hw/misc/unimp.h" #include "hw/intc/i8259.h" +#include "hw/intc/loongson_ipi.h" #include "hw/loader.h" #include "hw/isa/superio.h" #include "hw/pci/msi.h" @@ -74,6 +75,7 @@ const MemMapEntry virt_memmap[] = { [VIRT_PCIE_ECAM] = { 0x1a000000, 0x2000000 }, [VIRT_BIOS_ROM] = { 0x1fc00000, 0x200000 }, [VIRT_UART] = { 0x1fe001e0, 0x8 }, + [VIRT_IPI] = { 0x3ff01000, 0x400 }, [VIRT_LIOINTC] = { 0x3ff01400, 0x64 }, [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 }, [VIRT_HIGHMEM] = { 0x80000000, 0x0 }, /* Variable */ @@ -485,6 +487,7 @@ static void mips_loongson3_virt_init(MachineState *machine) Clock *cpuclk; CPUMIPSState *env; DeviceState *liointc; + DeviceState *ipi = NULL; char *filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *kernel_filename = machine->kernel_filename; @@ -494,6 +497,7 @@ static void mips_loongson3_virt_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *iomem = g_new(MemoryRegion, 1); + MemoryRegion *iocsr = g_new(MemoryRegion, 1); /* TODO: TCG will support all CPU types */ if (!kvm_enabled()) { @@ -527,6 +531,19 @@ static void mips_loongson3_virt_init(MachineState *machine) create_unimplemented_device("mmio fallback 0", 0x10000000, 256 * MiB); create_unimplemented_device("mmio fallback 1", 0x30000000, 256 * MiB); + memory_region_init(iocsr, OBJECT(machine), "loongson3.iocsr", UINT32_MAX); + + /* IPI controller is in kernel for KVM */ + if (!kvm_enabled()) { + ipi = qdev_new(TYPE_LOONGSON_IPI); + qdev_prop_set_uint32(ipi, "num-cpu", machine->smp.cpus); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + memory_region_add_subregion(iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + } + liointc = qdev_new("loongson.liointc"); sysbus_realize_and_unref(SYS_BUS_DEVICE(liointc), &error_fatal); @@ -543,6 +560,8 @@ static void mips_loongson3_virt_init(MachineState *machine) clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ); for (i = 0; i < machine->smp.cpus; i++) { + int node = i / LOONGSON3_CORE_PER_NODE; + int core = i % LOONGSON3_CORE_PER_NODE; int ip; /* init CPUs */ @@ -553,12 +572,28 @@ static void mips_loongson3_virt_init(MachineState *machine) cpu_mips_clock_init(cpu); qemu_register_reset(main_cpu_reset, cpu); - if (i >= 4) { + if (ipi) { + hwaddr base = ((hwaddr)node << 44) + virt_memmap[VIRT_IPI].base; + base += core * 0x100; + qdev_connect_gpio_out(ipi, i, cpu->env.irq[6]); + sysbus_mmio_map(SYS_BUS_DEVICE(ipi), i + 2, base); + } + + if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) { + MemoryRegion *core_iocsr = g_new(MemoryRegion, 1); + g_autofree char *name = g_strdup_printf("core%d_iocsr", i); + memory_region_init_alias(core_iocsr, OBJECT(cpu), name, + iocsr, 0, UINT32_MAX); + memory_region_add_subregion(&MIPS_CPU(cpu)->env.iocsr.mr, + 0, core_iocsr); + } + + if (node > 0) { continue; /* Only node-0 can be connected to LIOINTC */ } for (ip = 0; ip < 4 ; ip++) { - int pin = i * 4 + ip; + int pin = core * LOONGSON3_CORE_PER_NODE + ip; sysbus_connect_irq(SYS_BUS_DEVICE(liointc), pin, cpu->env.irq[ip + 2]); } From b926895357ddf8199585d0f97055935abe90c3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 13 Jun 2024 12:25:28 +0200 Subject: [PATCH 1484/2884] hw/s390x: Introduce s390_skeys_get|set() helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s390_skeys_set() dispatch to S390SKeysClass::set_skeys(), and s390_skeys_get() to S390SKeysClass::get_skeys(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Paolo Bonzini Message-Id: <20240613104415.9643-2-philmd@linaro.org> --- hw/s390x/s390-skeys.c | 27 +++++++++++++++++++++++++++ hw/s390x/trace-events | 4 ++++ include/hw/s390x/storage-keys.h | 10 ++++++++++ 3 files changed, 41 insertions(+) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 5c535d483e..bf22d6863e 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -23,6 +23,7 @@ #include "sysemu/kvm.h" #include "migration/qemu-file-types.h" #include "migration/register.h" +#include "trace.h" #define S390_SKEYS_BUFFER_SIZE (128 * KiB) /* Room for 128k storage keys */ #define S390_SKEYS_SAVE_FLAG_EOS 0x01 @@ -54,6 +55,32 @@ void s390_skeys_init(void) qdev_realize(DEVICE(obj), NULL, &error_fatal); } +int s390_skeys_get(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + S390SKeysClass *kc = S390_SKEYS_GET_CLASS(ks); + int rc; + + rc = kc->get_skeys(ks, start_gfn, count, keys); + if (rc) { + trace_s390_skeys_get_nonzero(rc); + } + return rc; +} + +int s390_skeys_set(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + S390SKeysClass *kc = S390_SKEYS_GET_CLASS(ks); + int rc; + + rc = kc->set_skeys(ks, start_gfn, count, keys); + if (rc) { + trace_s390_skeys_set_nonzero(rc); + } + return rc; +} + static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn, uint64_t count, Error **errp) { diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events index 34da5ea323..4e74bf4484 100644 --- a/hw/s390x/trace-events +++ b/hw/s390x/trace-events @@ -36,3 +36,7 @@ s390_pci_unknown(const char *msg, uint32_t cmd) "%s unknown command 0x%x" s390_pci_bar(uint32_t bar, uint32_t addr, uint64_t size, uint32_t barsize) "bar %d addr 0x%x size 0x%" PRIx64 "barsize 0x%x" s390_pci_nodev(const char *cmd, uint32_t fh) "%s no pci dev fh 0x%x" s390_pci_invalid(const char *cmd, uint32_t fh) "%s invalid space fh 0x%x" + +# s390-skeys.c +s390_skeys_get_nonzero(int rc) "SKEY: Call to get_skeys unexpectedly returned %d" +s390_skeys_set_nonzero(int rc) "SKEY: Call to set_skeys unexpectedly returned %d" diff --git a/include/hw/s390x/storage-keys.h b/include/hw/s390x/storage-keys.h index aa2ec2aae5..976ffb2039 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -111,6 +111,16 @@ struct QEMUS390SKeysState { }; void s390_skeys_init(void); +/** + * @s390_skeys_get: See S390SKeysClass::get_skeys() + */ +int s390_skeys_get(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys); +/** + * @s390_skeys_set: See S390SKeysClass::set_skeys() + */ +int s390_skeys_set(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys); S390SKeysState *s390_get_skeys_device(void); From 4860af2c4fc4632c180ba902758f6a7f66ed9ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 13 Jun 2024 12:30:00 +0200 Subject: [PATCH 1485/2884] target/s390x: Use s390_skeys_get|set() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit c9274b6bf0 ("target/s390x: start moving TCG-only code to tcg/") moved mem_helper.c, but the trace-events file is still in the parent directory, so is the generated trace.h. Call the s390_skeys_get|set() helper, removing the need for the trace event shared with the tcg/ sub-directory, fixing the following build failure: In file included from ../target/s390x/tcg/mem_helper.c:33: ../target/s390x/tcg/trace.h:1:10: fatal error: 'trace/trace-target_s390x_tcg.h' file not found #include "trace/trace-target_s390x_tcg.h" Reported-by: Mark Cave-Ayland Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Paolo Bonzini Message-Id: <20240613104415.9643-3-philmd@linaro.org> --- target/s390x/mmu_helper.c | 11 ++--------- target/s390x/tcg/mem_helper.c | 16 ++++------------ target/s390x/trace-events | 4 ---- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index f3a2f25a5c..6c59d0d216 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -25,7 +25,6 @@ #include "sysemu/tcg.h" #include "exec/exec-all.h" #include "exec/page-protection.h" -#include "trace.h" #include "hw/hw.h" #include "hw/s390x/storage-keys.h" #include "hw/boards.h" @@ -303,7 +302,6 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) static S390SKeysClass *skeyclass; static S390SKeysState *ss; uint8_t key, old_key; - int rc; /* * We expect to be called with an absolute address that has already been @@ -341,9 +339,7 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) * * TODO: we have races between getting and setting the key. */ - rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_get_skeys_nonzero(rc); + if (s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key)) { return; } old_key = key; @@ -371,10 +367,7 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) key |= SK_R; if (key != old_key) { - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_set_skeys_nonzero(rc); - } + s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key); } } diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 6a308c5553..6cdbc34178 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -30,7 +30,6 @@ #include "hw/core/tcg-cpu-ops.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "trace.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/storage-keys.h" @@ -2093,9 +2092,8 @@ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) } } - rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + rc = s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (rc) { - trace_get_skeys_nonzero(rc); return 0; } return key; @@ -2108,7 +2106,6 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) static S390SKeysClass *skeyclass; uint64_t addr = wrap_address(env, r2); uint8_t key; - int rc; addr = mmu_real2abs(env, addr); if (!mmu_absolute_addr_valid(addr, false)) { @@ -2124,10 +2121,7 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) } key = r1 & 0xfe; - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_set_skeys_nonzero(rc); - } + s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key); /* * As we can only flush by virtual address and not all the entries * that point to a physical address we have to flush the whole TLB. @@ -2157,18 +2151,16 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) } } - rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + rc = s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (rc) { - trace_get_skeys_nonzero(rc); return 0; } re = key & (SK_R | SK_C); key &= ~SK_R; - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + rc = s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (rc) { - trace_set_skeys_nonzero(rc); return 0; } /* diff --git a/target/s390x/trace-events b/target/s390x/trace-events index 729cb012b4..d371ef71b9 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -1,9 +1,5 @@ # See docs/devel/tracing.rst for syntax documentation. -# mmu_helper.c -get_skeys_nonzero(int rc) "SKEY: Call to get_skeys unexpectedly returned %d" -set_skeys_nonzero(int rc) "SKEY: Call to set_skeys unexpectedly returned %d" - # ioinst.c ioinst(const char *insn) "IOINST: %s" ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" From 9051350d25e58bb091ea51b5fde861ee7c3aafba Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 13 Jun 2024 11:06:11 +0300 Subject: [PATCH 1486/2884] util/readline: Fix lints for readline_handle_byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While they do not give warnings under our current buildsystem configuration, my clang's language server daemon was complaining about missing default: labels in switch statements. While at it, add /* fallthrough */ annotations where appropriate. This is a purely style and not functional change. Signed-off-by: Manos Pitsidianakis Reviewed-by: Marc-André Lureau Message-ID: <16f745ac7f5fef74498709ffd98857e76edff6aa.1718265822.git.manos.pitsidianakis@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- util/readline.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/util/readline.c b/util/readline.c index 494a3d924e..ded31b04b7 100644 --- a/util/readline.c +++ b/util/readline.c @@ -405,7 +405,7 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 12: readline_clear_screen(rs); break; - case 10: + case 10: /* fallthrough */ case 13: rs->cmd_buf[rs->cmd_buf_size] = '\0'; if (!rs->read_password) { @@ -425,7 +425,7 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 27: rs->esc_state = IS_ESC; break; - case 127: + case 127: /* fallthrough */ case 8: readline_backspace(rs); break; @@ -452,11 +452,11 @@ void readline_handle_byte(ReadLineState *rs, int ch) break; case IS_CSI: switch (ch) { - case 'A': + case 'A': /* fallthrough */ case 'F': readline_up_char(rs); break; - case 'B': + case 'B': /* fallthrough */ case 'E': readline_down_char(rs); break; @@ -480,12 +480,15 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 4: readline_eol(rs); break; + default: + break; } break; default: break; } rs->esc_state = IS_NORM; + /* fallthrough */ the_end: break; case IS_SS3: @@ -496,9 +499,13 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 'H': readline_bol(rs); break; + default: + break; } rs->esc_state = IS_NORM; break; + default: + break; } readline_update(rs); } From 96c99e38312c517115a9c4f9e9d409059818e56d Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 13 Jun 2024 11:06:12 +0300 Subject: [PATCH 1487/2884] util/readline: Add C-n, C-p shortcuts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C-n and C-p are the default bindings for readline's next-history and previous-history respectively. They have the same functionality as the Down and Up arrow keys. Signed-off-by: Manos Pitsidianakis Reviewed-by: Marc-André Lureau Message-ID: <9876594132d1f2e7210ab3f7ca01a82f95206447.1718265822.git.manos.pitsidianakis@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- util/readline.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/readline.c b/util/readline.c index ded31b04b7..0b627d62ad 100644 --- a/util/readline.c +++ b/util/readline.c @@ -418,6 +418,14 @@ void readline_handle_byte(ReadLineState *rs, int ch) rs->last_cmd_buf_size = 0; rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque); break; + case 14: + /* ^N Next line in history */ + readline_down_char(rs); + break; + case 16: + /* ^P Prev line in history */ + readline_up_char(rs); + break; case 23: /* ^W */ readline_backword(rs); From e1e55d346a2f648f05d36bed7edcd3910f516f1d Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 13 Jun 2024 11:06:13 +0300 Subject: [PATCH 1488/2884] util/readline: Add C-u shortcut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the unix-line-discard readline action, which erases from the cursor position up to the beginning of the line. The default binding, C-u, was chosen. This is useful to quickly erase command input while working on the monitor interface. Signed-off-by: Manos Pitsidianakis Reviewed-by: Marc-André Lureau Message-ID: <6772067e1c0d4b1c5310e5446e9e3e1c6b3b5bc0.1718265822.git.manos.pitsidianakis@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- util/readline.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/util/readline.c b/util/readline.c index 0b627d62ad..0f19674f52 100644 --- a/util/readline.c +++ b/util/readline.c @@ -271,6 +271,14 @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) rs->hist_entry = -1; } +static void readline_kill_line(ReadLineState *rs) +{ + while (rs->cmd_buf_index > 0) { + readline_backward_char(rs); + readline_delete_char(rs); + } +} + /* completion support */ void readline_add_completion(ReadLineState *rs, const char *str) @@ -426,6 +434,10 @@ void readline_handle_byte(ReadLineState *rs, int ch) /* ^P Prev line in history */ readline_up_char(rs); break; + case 21: + /* ^U Kill backward from point to the beginning of the line. */ + readline_kill_line(rs); + break; case 23: /* ^W */ readline_backword(rs); From 2f8cd5a9b6ca50612c697ba352c04cf1cb70af15 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 May 2024 10:38:55 +0200 Subject: [PATCH 1489/2884] MAINTAINERS: drop virtio-gpu maintainership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove myself from virtio-gpu entries. Flip status to "Orphan" for entries which have nobody else listed. Signed-off-by: Gerd Hoffmann Reviewed-by: Manos Pitsidianakis Message-ID: <20240528083858.836262-4-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0f63bcdc7d..7cc6421e3b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2579,8 +2579,7 @@ F: hw/display/ramfb*.c F: include/hw/display/ramfb.h virtio-gpu -M: Gerd Hoffmann -S: Odd Fixes +S: Orphan F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h @@ -2602,7 +2601,6 @@ F: include/hw/virtio/virtio-blk-common.h vhost-user-gpu M: Marc-André Lureau -R: Gerd Hoffmann S: Maintained F: docs/interop/vhost-user-gpu.rst F: contrib/vhost-user-gpu From 34761036899619dc281628d26c27b63807f6cf08 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 May 2024 10:38:56 +0200 Subject: [PATCH 1490/2884] MAINTAINERS: drop spice+ui maintainership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove myself from spice and ui entries. Flip status to "Orphan" for entries which have nobody else listed. Signed-off-by: Gerd Hoffmann Reviewed-by: Manos Pitsidianakis Message-ID: <20240528083858.836262-5-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7cc6421e3b..cef54de759 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3058,8 +3058,7 @@ F: stubs/memory_device.c F: docs/nvdimm.txt SPICE -M: Gerd Hoffmann -S: Odd Fixes +S: Orphan F: include/ui/qemu-spice.h F: include/ui/spice-display.h F: ui/spice-*.c @@ -3069,7 +3068,6 @@ F: qapi/ui.json F: docs/spice-port-fqdn.txt Graphics -M: Gerd Hoffmann M: Marc-André Lureau S: Odd Fixes F: ui/ From e7b53d160f61b3de2c1db6007848ac6748d93ab4 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 22 Mar 2024 19:54:25 +0900 Subject: [PATCH 1491/2884] ui/cocoa: Use qemu_add_mouse_change_notifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This eliminates the polling in cocoa_refresh and implements the propagation of the mouse mode change from absolute to relative. Signed-off-by: Akihiko Odaki Reviewed-by: Phil Dennis-Jordan Tested-by: Phil Dennis-Jordan Message-ID: <20240322-mouse-v1-1-0b7d4d9bdfbf@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- ui/cocoa.m | 62 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 981615a8b9..2935247cdd 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -296,6 +296,14 @@ static void handleAnyDeviceErrors(Error * err) { QEMUScreen screen; pixman_image_t *pixman_image; + /* The state surrounding mouse grabbing is potentially confusing. + * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated + * pointing device an absolute-position one?"], but is only updated on + * next refresh. + * isMouseGrabbed tracks whether GUI events are directed to the guest; + * it controls whether special keys like Cmd get sent to the guest, + * and whether we capture the mouse when in non-absolute mode. + */ BOOL isMouseGrabbed; BOOL isAbsoluteEnabled; CFMachPortRef eventsTap; @@ -307,17 +315,8 @@ static void handleAnyDeviceErrors(Error * err) - (void) handleMonitorInput:(NSEvent *)event; - (bool) handleEvent:(NSEvent *)event; - (bool) handleEventLocked:(NSEvent *)event; -- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; -/* The state surrounding mouse grabbing is potentially confusing. - * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated - * pointing device an absolute-position one?"], but is only updated on - * next refresh. - * isMouseGrabbed tracks whether GUI events are directed to the guest; - * it controls whether special keys like Cmd get sent to the guest, - * and whether we capture the mouse when in non-absolute mode. - */ +- (void) notifyMouseModeChange; - (BOOL) isMouseGrabbed; -- (BOOL) isAbsoluteEnabled; - (QEMUScreen) gscreen; - (void) raiseAllKeys; @end @@ -404,6 +403,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven qkbd_state_switch_console(kbd, con); dcl.con = con; register_displaychangelistener(&dcl); + [self notifyMouseModeChange]; [self updateUIInfo]; } @@ -1109,14 +1109,26 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [self raiseAllButtons]; } -- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled { +- (void) notifyMouseModeChange { + bool tIsAbsoluteEnabled = bool_with_bql(^{ + return qemu_input_is_absolute(dcl.con); + }); + + if (tIsAbsoluteEnabled == isAbsoluteEnabled) { + return; + } + isAbsoluteEnabled = tIsAbsoluteEnabled; + if (isMouseGrabbed) { - CGAssociateMouseAndMouseCursorPosition(isAbsoluteEnabled); + if (isAbsoluteEnabled) { + [self ungrabMouse]; + } else { + CGAssociateMouseAndMouseCursorPosition(isAbsoluteEnabled); + } } } - (BOOL) isMouseGrabbed {return isMouseGrabbed;} -- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} - (QEMUScreen) gscreen {return screen;} /* @@ -1791,6 +1803,17 @@ static void addRemovableDevicesMenuItems(void) qapi_free_BlockInfoList(pointerToFree); } +static void cocoa_mouse_mode_change_notify(Notifier *notifier, void *data) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView notifyMouseModeChange]; + }); +} + +static Notifier mouse_mode_change_notifier = { + .notify = cocoa_mouse_mode_change_notify +}; + @interface QemuCocoaPasteboardTypeOwner : NSObject @end @@ -1975,17 +1998,6 @@ static void cocoa_refresh(DisplayChangeListener *dcl) COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); graphic_hw_update(dcl->con); - if (qemu_input_is_absolute(dcl->con)) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (![cocoaView isAbsoluteEnabled]) { - if ([cocoaView isMouseGrabbed]) { - [cocoaView ungrabMouse]; - } - } - [cocoaView setAbsoluteEnabled:YES]; - }); - } - if (cbchangecount != [[NSPasteboard generalPasteboard] changeCount]) { qemu_clipboard_info_unref(cbinfo); cbinfo = qemu_clipboard_info_new(&cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD); @@ -2062,6 +2074,8 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) // register vga output callbacks register_displaychangelistener(&dcl); + qemu_add_mouse_mode_change_notifier(&mouse_mode_change_notifier); + [cocoaView notifyMouseModeChange]; [cocoaView updateUIInfo]; qemu_event_init(&cbevent, false); From b1cf266c82cb1211ee2785f1813a6a3f3e693390 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 5 Jun 2024 15:14:41 +0200 Subject: [PATCH 1492/2884] stdvga: fix screen blanking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case the display surface uses a shared buffer (i.e. uses vga vram directly instead of a shadow) go unshare the buffer before clearing it. This avoids vga memory corruption, which in turn fixes unblanking not working properly with X11. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2067 Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Message-ID: <20240605131444.797896-2-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/vga.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/display/vga.c b/hw/display/vga.c index 30facc6c8e..be2b19b839 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1762,6 +1762,13 @@ static void vga_draw_blank(VGACommonState *s, int full_update) if (s->last_scr_width <= 0 || s->last_scr_height <= 0) return; + if (is_buffer_shared(surface)) { + /* unshare buffer, otherwise the blanking corrupts vga vram */ + surface = qemu_create_displaysurface(s->last_scr_width, + s->last_scr_height); + dpy_gfx_replace_surface(s->con, surface); + } + w = s->last_scr_width * surface_bytes_per_pixel(surface); d = surface_data(surface); for(i = 0; i < s->last_scr_height; i++) { From 9badf12ac20df80542f642d3dab7b2f1a95b20f5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 5 Jun 2024 15:14:42 +0200 Subject: [PATCH 1493/2884] ui+display: rename is_placeholder() -> surface_is_placeholder() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change. Signed-off-by: Gerd Hoffmann Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240605131444.797896-3-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/ui/surface.h | 2 +- ui/console.c | 2 +- ui/sdl2-2d.c | 2 +- ui/sdl2-gl.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ui/surface.h b/include/ui/surface.h index 4244e0ca4a..273bb4769a 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -50,7 +50,7 @@ static inline int is_buffer_shared(DisplaySurface *surface) return !(surface->flags & QEMU_ALLOCATED_FLAG); } -static inline int is_placeholder(DisplaySurface *surface) +static inline int surface_is_placeholder(DisplaySurface *surface) { return surface->flags & QEMU_PLACEHOLDER_FLAG; } diff --git a/ui/console.c b/ui/console.c index 1b2cd0c736..c2173fc0b1 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1510,7 +1510,7 @@ void qemu_console_resize(QemuConsole *s, int width, int height) assert(QEMU_IS_GRAPHIC_CONSOLE(s)); if ((s->scanout.kind != SCANOUT_SURFACE || - (surface && !is_buffer_shared(surface) && !is_placeholder(surface))) && + (surface && !is_buffer_shared(surface) && !surface_is_placeholder(surface))) && qemu_console_get_width(s, -1) == width && qemu_console_get_height(s, -1) == height) { return; diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index 06468cd493..73052383c2 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -72,7 +72,7 @@ void sdl2_2d_switch(DisplayChangeListener *dcl, scon->texture = NULL; } - if (is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { + if (surface_is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { sdl2_window_destroy(scon); return; } diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index 28d796607c..91b7ee2419 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -89,7 +89,7 @@ void sdl2_gl_switch(DisplayChangeListener *dcl, scon->surface = new_surface; - if (is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { + if (surface_is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { qemu_gl_fini_shader(scon->gls); scon->gls = NULL; sdl2_window_destroy(scon); From abd749b517827b3da38230f50a82a94fccfaddac Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 5 Jun 2024 15:14:43 +0200 Subject: [PATCH 1494/2884] ui+display: rename is_buffer_shared() -> surface_is_allocated() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Boolean return value is reversed, to align with QEMU_ALLOCATED_FLAG, so all callers must be adapted. Also rename share_surface variable in vga_draw_graphic() to reduce confusion. No functional change. Suggested-by: Marc-André Lureau Signed-off-by: Gerd Hoffmann Reviewed-by: Paul Durrant Reviewed-by: Marc-André Lureau Message-ID: <20240605131444.797896-4-kraxel@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/qxl-render.c | 2 +- hw/display/vga.c | 20 ++++++++++---------- hw/display/xenfb.c | 5 +++-- include/ui/surface.h | 4 ++-- ui/console.c | 3 ++- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index ec99ec887a..8daae72c8d 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -31,7 +31,7 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) uint8_t *src; int len, i; - if (is_buffer_shared(surface)) { + if (!surface_is_allocated(surface)) { return; } trace_qxl_render_blit(qxl->guest_primary.qxl_stride, diff --git a/hw/display/vga.c b/hw/display/vga.c index be2b19b839..892fedc8dc 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1487,7 +1487,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) uint8_t *d; uint32_t v, addr1, addr; vga_draw_line_func *vga_draw_line = NULL; - bool share_surface, force_shadow = false; + bool allocate_surface, force_shadow = false; pixman_format_code_t format; #if HOST_BIG_ENDIAN bool byteswap = !s->big_endian_fb; @@ -1609,10 +1609,10 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) */ format = qemu_default_pixman_format(depth, !byteswap); if (format) { - share_surface = dpy_gfx_check_format(s->con, format) - && !s->force_shadow && !force_shadow; + allocate_surface = !dpy_gfx_check_format(s->con, format) + || s->force_shadow || force_shadow; } else { - share_surface = false; + allocate_surface = true; } if (s->params.line_offset != s->last_line_offset || @@ -1620,7 +1620,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) height != s->last_height || s->last_depth != depth || s->last_byteswap != byteswap || - share_surface != is_buffer_shared(surface)) { + allocate_surface != surface_is_allocated(surface)) { /* display parameters changed -> need new display surface */ s->last_scr_width = disp_width; s->last_scr_height = height; @@ -1635,14 +1635,14 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) full_update = 1; } if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) - && is_buffer_shared(surface)) { + && !surface_is_allocated(surface)) { /* base address changed (page flip) -> shared display surfaces * must be updated with the new base address */ full_update = 1; } if (full_update) { - if (share_surface) { + if (!allocate_surface) { surface = qemu_create_displaysurface_from(disp_width, height, format, s->params.line_offset, s->vram_ptr + (s->params.start_addr * 4)); @@ -1655,7 +1655,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) vga_draw_line = vga_draw_line_table[v]; - if (!is_buffer_shared(surface) && s->cursor_invalidate) { + if (surface_is_allocated(surface) && s->cursor_invalidate) { s->cursor_invalidate(s); } @@ -1707,7 +1707,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) if (update) { if (y_start < 0) y_start = y; - if (!(is_buffer_shared(surface))) { + if (surface_is_allocated(surface)) { uint8_t *p; p = vga_draw_line(s, d, addr, width, hpel); if (p) { @@ -1762,7 +1762,7 @@ static void vga_draw_blank(VGACommonState *s, int full_update) if (s->last_scr_width <= 0 || s->last_scr_height <= 0) return; - if (is_buffer_shared(surface)) { + if (!surface_is_allocated(surface)) { /* unshare buffer, otherwise the blanking corrupts vga vram */ surface = qemu_create_displaysurface(s->last_scr_width, s->last_scr_height); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index ff442ced1a..314d378a1b 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -638,7 +638,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) int linesize = surface_stride(surface); uint8_t *data = surface_data(surface); - if (!is_buffer_shared(surface)) { + if (surface_is_allocated(surface)) { switch (xenfb->depth) { case 8: if (bpp == 16) { @@ -756,7 +756,8 @@ static void xenfb_update(void *opaque) xen_pv_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", xenfb->width, xenfb->height, xenfb->depth, - is_buffer_shared(surface) ? " (shared)" : ""); + surface_is_allocated(surface) + ? " (allocated)" : " (borrowed)"); xenfb->up_fullscreen = 1; } diff --git a/include/ui/surface.h b/include/ui/surface.h index 273bb4769a..345b19169d 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -45,9 +45,9 @@ void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, DisplaySurface *qemu_create_displaysurface(int width, int height); void qemu_free_displaysurface(DisplaySurface *surface); -static inline int is_buffer_shared(DisplaySurface *surface) +static inline int surface_is_allocated(DisplaySurface *surface) { - return !(surface->flags & QEMU_ALLOCATED_FLAG); + return surface->flags & QEMU_ALLOCATED_FLAG; } static inline int surface_is_placeholder(DisplaySurface *surface) diff --git a/ui/console.c b/ui/console.c index c2173fc0b1..e67c5dae2b 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1510,7 +1510,8 @@ void qemu_console_resize(QemuConsole *s, int width, int height) assert(QEMU_IS_GRAPHIC_CONSOLE(s)); if ((s->scanout.kind != SCANOUT_SURFACE || - (surface && !is_buffer_shared(surface) && !surface_is_placeholder(surface))) && + (surface && surface_is_allocated(surface) && + !surface_is_placeholder(surface))) && qemu_console_get_width(s, -1) == width && qemu_console_get_height(s, -1) == height) { return; From fc0870c180872d0f40e63507cc6bf8565ffd8d98 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Jun 2024 15:45:28 -0700 Subject: [PATCH 1495/2884] exec: Make the MemOp enum cast explicit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the MemOp enum cast explicit to use the QEMU headers with a C++ compiler. Signed-off-by: Roman Kiryanov Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240618224528.878425-1-rkir@google.com> Signed-off-by: Philippe Mathieu-Daudé --- include/exec/memop.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/memop.h b/include/exec/memop.h index 06417ff361..f881fe7af4 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -161,7 +161,7 @@ static inline MemOp size_memop(unsigned size) /* Power of 2 up to 8. */ assert((size & (size - 1)) == 0 && size >= 1 && size <= 8); #endif - return ctz32(size); + return (MemOp)ctz32(size); } /* Big endianness from MemOp. */ From 741b0ee832129a5c7cbae356df19e65f4268434c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 15:54:39 +0000 Subject: [PATCH 1496/2884] tcg/loongarch64: Import LASX, FP insns Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-insn-defs.c.inc | 6181 +++++++++------------------ 1 file changed, 2123 insertions(+), 4058 deletions(-) diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index ee3b483b02..6bb8656fd8 100644 --- a/tcg/loongarch64/tcg-insn-defs.c.inc +++ b/tcg/loongarch64/tcg-insn-defs.c.inc @@ -4,11 +4,13 @@ * * This file is auto-generated by genqemutcgdefs from * https://github.com/loongson-community/loongarch-opcodes, - * from commit 8027da9a8157a8b47fc48ff1def292e09c5668bd. + * from commit 7f353fb69bd99ce6edfad7ad63948c4bb526f0bf. * DO NOT EDIT. */ typedef enum { + OPC_MOVGR2SCR = 0x00000800, + OPC_MOVSCR2GR = 0x00000c00, OPC_CLZ_W = 0x00001400, OPC_CTZ_W = 0x00001c00, OPC_CLZ_D = 0x00002400, @@ -38,6 +40,8 @@ typedef enum { OPC_SLL_D = 0x00188000, OPC_SRL_D = 0x00190000, OPC_SRA_D = 0x00198000, + OPC_ROTR_B = 0x001a0000, + OPC_ROTR_H = 0x001a8000, OPC_ROTR_W = 0x001b0000, OPC_ROTR_D = 0x001b8000, OPC_MUL_W = 0x001c0000, @@ -60,12 +64,17 @@ typedef enum { OPC_SRLI_D = 0x00450000, OPC_SRAI_W = 0x00488000, OPC_SRAI_D = 0x00490000, + OPC_ROTRI_B = 0x004c2000, + OPC_ROTRI_H = 0x004c4000, OPC_ROTRI_W = 0x004c8000, OPC_ROTRI_D = 0x004d0000, OPC_BSTRINS_W = 0x00600000, OPC_BSTRPICK_W = 0x00608000, OPC_BSTRINS_D = 0x00800000, OPC_BSTRPICK_D = 0x00c00000, + OPC_FMOV_D = 0x01149800, + OPC_MOVGR2FR_D = 0x0114a800, + OPC_MOVFR2GR_D = 0x0114b800, OPC_SLTI = 0x02000000, OPC_SLTUI = 0x02400000, OPC_ADDI_W = 0x02800000, @@ -74,60 +83,10 @@ typedef enum { OPC_ANDI = 0x03400000, OPC_ORI = 0x03800000, OPC_XORI = 0x03c00000, - OPC_VFMADD_S = 0x09100000, - OPC_VFMADD_D = 0x09200000, - OPC_VFMSUB_S = 0x09500000, - OPC_VFMSUB_D = 0x09600000, - OPC_VFNMADD_S = 0x09900000, - OPC_VFNMADD_D = 0x09a00000, - OPC_VFNMSUB_S = 0x09d00000, - OPC_VFNMSUB_D = 0x09e00000, - OPC_VFCMP_CAF_S = 0x0c500000, - OPC_VFCMP_SAF_S = 0x0c508000, - OPC_VFCMP_CLT_S = 0x0c510000, - OPC_VFCMP_SLT_S = 0x0c518000, - OPC_VFCMP_CEQ_S = 0x0c520000, - OPC_VFCMP_SEQ_S = 0x0c528000, - OPC_VFCMP_CLE_S = 0x0c530000, - OPC_VFCMP_SLE_S = 0x0c538000, - OPC_VFCMP_CUN_S = 0x0c540000, - OPC_VFCMP_SUN_S = 0x0c548000, - OPC_VFCMP_CULT_S = 0x0c550000, - OPC_VFCMP_SULT_S = 0x0c558000, - OPC_VFCMP_CUEQ_S = 0x0c560000, - OPC_VFCMP_SUEQ_S = 0x0c568000, - OPC_VFCMP_CULE_S = 0x0c570000, - OPC_VFCMP_SULE_S = 0x0c578000, - OPC_VFCMP_CNE_S = 0x0c580000, - OPC_VFCMP_SNE_S = 0x0c588000, - OPC_VFCMP_COR_S = 0x0c5a0000, - OPC_VFCMP_SOR_S = 0x0c5a8000, - OPC_VFCMP_CUNE_S = 0x0c5c0000, - OPC_VFCMP_SUNE_S = 0x0c5c8000, - OPC_VFCMP_CAF_D = 0x0c600000, - OPC_VFCMP_SAF_D = 0x0c608000, - OPC_VFCMP_CLT_D = 0x0c610000, - OPC_VFCMP_SLT_D = 0x0c618000, - OPC_VFCMP_CEQ_D = 0x0c620000, - OPC_VFCMP_SEQ_D = 0x0c628000, - OPC_VFCMP_CLE_D = 0x0c630000, - OPC_VFCMP_SLE_D = 0x0c638000, - OPC_VFCMP_CUN_D = 0x0c640000, - OPC_VFCMP_SUN_D = 0x0c648000, - OPC_VFCMP_CULT_D = 0x0c650000, - OPC_VFCMP_SULT_D = 0x0c658000, - OPC_VFCMP_CUEQ_D = 0x0c660000, - OPC_VFCMP_SUEQ_D = 0x0c668000, - OPC_VFCMP_CULE_D = 0x0c670000, - OPC_VFCMP_SULE_D = 0x0c678000, - OPC_VFCMP_CNE_D = 0x0c680000, - OPC_VFCMP_SNE_D = 0x0c688000, - OPC_VFCMP_COR_D = 0x0c6a0000, - OPC_VFCMP_SOR_D = 0x0c6a8000, - OPC_VFCMP_CUNE_D = 0x0c6c0000, - OPC_VFCMP_SUNE_D = 0x0c6c8000, OPC_VBITSEL_V = 0x0d100000, + OPC_XVBITSEL_V = 0x0d200000, OPC_VSHUF_B = 0x0d500000, + OPC_XVSHUF_B = 0x0d600000, OPC_ADDU16I_D = 0x10000000, OPC_LU12I_W = 0x14000000, OPC_CU32I_D = 0x16000000, @@ -146,8 +105,14 @@ typedef enum { OPC_LD_BU = 0x2a000000, OPC_LD_HU = 0x2a400000, OPC_LD_WU = 0x2a800000, + OPC_FLD_S = 0x2b000000, + OPC_FST_S = 0x2b400000, + OPC_FLD_D = 0x2b800000, + OPC_FST_D = 0x2bc00000, OPC_VLD = 0x2c000000, OPC_VST = 0x2c400000, + OPC_XVLD = 0x2c800000, + OPC_XVST = 0x2cc00000, OPC_VLDREPL_D = 0x30100000, OPC_VLDREPL_W = 0x30200000, OPC_VLDREPL_H = 0x30400000, @@ -156,6 +121,14 @@ typedef enum { OPC_VSTELM_W = 0x31200000, OPC_VSTELM_H = 0x31400000, OPC_VSTELM_B = 0x31800000, + OPC_XVLDREPL_D = 0x32100000, + OPC_XVLDREPL_W = 0x32200000, + OPC_XVLDREPL_H = 0x32400000, + OPC_XVLDREPL_B = 0x32800000, + OPC_XVSTELM_D = 0x33100000, + OPC_XVSTELM_W = 0x33200000, + OPC_XVSTELM_H = 0x33400000, + OPC_XVSTELM_B = 0x33800000, OPC_LDX_B = 0x38000000, OPC_LDX_H = 0x38040000, OPC_LDX_W = 0x38080000, @@ -167,9 +140,17 @@ typedef enum { OPC_LDX_BU = 0x38200000, OPC_LDX_HU = 0x38240000, OPC_LDX_WU = 0x38280000, + OPC_FLDX_S = 0x38300000, + OPC_FLDX_D = 0x38340000, + OPC_FSTX_S = 0x38380000, + OPC_FSTX_D = 0x383c0000, OPC_VLDX = 0x38400000, OPC_VSTX = 0x38440000, + OPC_XVLDX = 0x38480000, + OPC_XVSTX = 0x384c0000, OPC_DBAR = 0x38720000, + OPC_JISCR0 = 0x48000200, + OPC_JISCR1 = 0x48000300, OPC_JIRL = 0x4c000000, OPC_B = 0x50000000, OPC_BL = 0x54000000, @@ -207,46 +188,6 @@ typedef enum { OPC_VSUB_H = 0x700c8000, OPC_VSUB_W = 0x700d0000, OPC_VSUB_D = 0x700d8000, - OPC_VADDWEV_H_B = 0x701e0000, - OPC_VADDWEV_W_H = 0x701e8000, - OPC_VADDWEV_D_W = 0x701f0000, - OPC_VADDWEV_Q_D = 0x701f8000, - OPC_VSUBWEV_H_B = 0x70200000, - OPC_VSUBWEV_W_H = 0x70208000, - OPC_VSUBWEV_D_W = 0x70210000, - OPC_VSUBWEV_Q_D = 0x70218000, - OPC_VADDWOD_H_B = 0x70220000, - OPC_VADDWOD_W_H = 0x70228000, - OPC_VADDWOD_D_W = 0x70230000, - OPC_VADDWOD_Q_D = 0x70238000, - OPC_VSUBWOD_H_B = 0x70240000, - OPC_VSUBWOD_W_H = 0x70248000, - OPC_VSUBWOD_D_W = 0x70250000, - OPC_VSUBWOD_Q_D = 0x70258000, - OPC_VADDWEV_H_BU = 0x702e0000, - OPC_VADDWEV_W_HU = 0x702e8000, - OPC_VADDWEV_D_WU = 0x702f0000, - OPC_VADDWEV_Q_DU = 0x702f8000, - OPC_VSUBWEV_H_BU = 0x70300000, - OPC_VSUBWEV_W_HU = 0x70308000, - OPC_VSUBWEV_D_WU = 0x70310000, - OPC_VSUBWEV_Q_DU = 0x70318000, - OPC_VADDWOD_H_BU = 0x70320000, - OPC_VADDWOD_W_HU = 0x70328000, - OPC_VADDWOD_D_WU = 0x70330000, - OPC_VADDWOD_Q_DU = 0x70338000, - OPC_VSUBWOD_H_BU = 0x70340000, - OPC_VSUBWOD_W_HU = 0x70348000, - OPC_VSUBWOD_D_WU = 0x70350000, - OPC_VSUBWOD_Q_DU = 0x70358000, - OPC_VADDWEV_H_BU_B = 0x703e0000, - OPC_VADDWEV_W_HU_H = 0x703e8000, - OPC_VADDWEV_D_WU_W = 0x703f0000, - OPC_VADDWEV_Q_DU_D = 0x703f8000, - OPC_VADDWOD_H_BU_B = 0x70400000, - OPC_VADDWOD_W_HU_H = 0x70408000, - OPC_VADDWOD_D_WU_W = 0x70410000, - OPC_VADDWOD_Q_DU_D = 0x70418000, OPC_VSADD_B = 0x70460000, OPC_VSADD_H = 0x70468000, OPC_VSADD_W = 0x70470000, @@ -263,50 +204,6 @@ typedef enum { OPC_VSSUB_HU = 0x704c8000, OPC_VSSUB_WU = 0x704d0000, OPC_VSSUB_DU = 0x704d8000, - OPC_VHADDW_H_B = 0x70540000, - OPC_VHADDW_W_H = 0x70548000, - OPC_VHADDW_D_W = 0x70550000, - OPC_VHADDW_Q_D = 0x70558000, - OPC_VHSUBW_H_B = 0x70560000, - OPC_VHSUBW_W_H = 0x70568000, - OPC_VHSUBW_D_W = 0x70570000, - OPC_VHSUBW_Q_D = 0x70578000, - OPC_VHADDW_HU_BU = 0x70580000, - OPC_VHADDW_WU_HU = 0x70588000, - OPC_VHADDW_DU_WU = 0x70590000, - OPC_VHADDW_QU_DU = 0x70598000, - OPC_VHSUBW_HU_BU = 0x705a0000, - OPC_VHSUBW_WU_HU = 0x705a8000, - OPC_VHSUBW_DU_WU = 0x705b0000, - OPC_VHSUBW_QU_DU = 0x705b8000, - OPC_VADDA_B = 0x705c0000, - OPC_VADDA_H = 0x705c8000, - OPC_VADDA_W = 0x705d0000, - OPC_VADDA_D = 0x705d8000, - OPC_VABSD_B = 0x70600000, - OPC_VABSD_H = 0x70608000, - OPC_VABSD_W = 0x70610000, - OPC_VABSD_D = 0x70618000, - OPC_VABSD_BU = 0x70620000, - OPC_VABSD_HU = 0x70628000, - OPC_VABSD_WU = 0x70630000, - OPC_VABSD_DU = 0x70638000, - OPC_VAVG_B = 0x70640000, - OPC_VAVG_H = 0x70648000, - OPC_VAVG_W = 0x70650000, - OPC_VAVG_D = 0x70658000, - OPC_VAVG_BU = 0x70660000, - OPC_VAVG_HU = 0x70668000, - OPC_VAVG_WU = 0x70670000, - OPC_VAVG_DU = 0x70678000, - OPC_VAVGR_B = 0x70680000, - OPC_VAVGR_H = 0x70688000, - OPC_VAVGR_W = 0x70690000, - OPC_VAVGR_D = 0x70698000, - OPC_VAVGR_BU = 0x706a0000, - OPC_VAVGR_HU = 0x706a8000, - OPC_VAVGR_WU = 0x706b0000, - OPC_VAVGR_DU = 0x706b8000, OPC_VMAX_B = 0x70700000, OPC_VMAX_H = 0x70708000, OPC_VMAX_W = 0x70710000, @@ -327,86 +224,6 @@ typedef enum { OPC_VMUL_H = 0x70848000, OPC_VMUL_W = 0x70850000, OPC_VMUL_D = 0x70858000, - OPC_VMUH_B = 0x70860000, - OPC_VMUH_H = 0x70868000, - OPC_VMUH_W = 0x70870000, - OPC_VMUH_D = 0x70878000, - OPC_VMUH_BU = 0x70880000, - OPC_VMUH_HU = 0x70888000, - OPC_VMUH_WU = 0x70890000, - OPC_VMUH_DU = 0x70898000, - OPC_VMULWEV_H_B = 0x70900000, - OPC_VMULWEV_W_H = 0x70908000, - OPC_VMULWEV_D_W = 0x70910000, - OPC_VMULWEV_Q_D = 0x70918000, - OPC_VMULWOD_H_B = 0x70920000, - OPC_VMULWOD_W_H = 0x70928000, - OPC_VMULWOD_D_W = 0x70930000, - OPC_VMULWOD_Q_D = 0x70938000, - OPC_VMULWEV_H_BU = 0x70980000, - OPC_VMULWEV_W_HU = 0x70988000, - OPC_VMULWEV_D_WU = 0x70990000, - OPC_VMULWEV_Q_DU = 0x70998000, - OPC_VMULWOD_H_BU = 0x709a0000, - OPC_VMULWOD_W_HU = 0x709a8000, - OPC_VMULWOD_D_WU = 0x709b0000, - OPC_VMULWOD_Q_DU = 0x709b8000, - OPC_VMULWEV_H_BU_B = 0x70a00000, - OPC_VMULWEV_W_HU_H = 0x70a08000, - OPC_VMULWEV_D_WU_W = 0x70a10000, - OPC_VMULWEV_Q_DU_D = 0x70a18000, - OPC_VMULWOD_H_BU_B = 0x70a20000, - OPC_VMULWOD_W_HU_H = 0x70a28000, - OPC_VMULWOD_D_WU_W = 0x70a30000, - OPC_VMULWOD_Q_DU_D = 0x70a38000, - OPC_VMADD_B = 0x70a80000, - OPC_VMADD_H = 0x70a88000, - OPC_VMADD_W = 0x70a90000, - OPC_VMADD_D = 0x70a98000, - OPC_VMSUB_B = 0x70aa0000, - OPC_VMSUB_H = 0x70aa8000, - OPC_VMSUB_W = 0x70ab0000, - OPC_VMSUB_D = 0x70ab8000, - OPC_VMADDWEV_H_B = 0x70ac0000, - OPC_VMADDWEV_W_H = 0x70ac8000, - OPC_VMADDWEV_D_W = 0x70ad0000, - OPC_VMADDWEV_Q_D = 0x70ad8000, - OPC_VMADDWOD_H_B = 0x70ae0000, - OPC_VMADDWOD_W_H = 0x70ae8000, - OPC_VMADDWOD_D_W = 0x70af0000, - OPC_VMADDWOD_Q_D = 0x70af8000, - OPC_VMADDWEV_H_BU = 0x70b40000, - OPC_VMADDWEV_W_HU = 0x70b48000, - OPC_VMADDWEV_D_WU = 0x70b50000, - OPC_VMADDWEV_Q_DU = 0x70b58000, - OPC_VMADDWOD_H_BU = 0x70b60000, - OPC_VMADDWOD_W_HU = 0x70b68000, - OPC_VMADDWOD_D_WU = 0x70b70000, - OPC_VMADDWOD_Q_DU = 0x70b78000, - OPC_VMADDWEV_H_BU_B = 0x70bc0000, - OPC_VMADDWEV_W_HU_H = 0x70bc8000, - OPC_VMADDWEV_D_WU_W = 0x70bd0000, - OPC_VMADDWEV_Q_DU_D = 0x70bd8000, - OPC_VMADDWOD_H_BU_B = 0x70be0000, - OPC_VMADDWOD_W_HU_H = 0x70be8000, - OPC_VMADDWOD_D_WU_W = 0x70bf0000, - OPC_VMADDWOD_Q_DU_D = 0x70bf8000, - OPC_VDIV_B = 0x70e00000, - OPC_VDIV_H = 0x70e08000, - OPC_VDIV_W = 0x70e10000, - OPC_VDIV_D = 0x70e18000, - OPC_VMOD_B = 0x70e20000, - OPC_VMOD_H = 0x70e28000, - OPC_VMOD_W = 0x70e30000, - OPC_VMOD_D = 0x70e38000, - OPC_VDIV_BU = 0x70e40000, - OPC_VDIV_HU = 0x70e48000, - OPC_VDIV_WU = 0x70e50000, - OPC_VDIV_DU = 0x70e58000, - OPC_VMOD_BU = 0x70e60000, - OPC_VMOD_HU = 0x70e68000, - OPC_VMOD_WU = 0x70e70000, - OPC_VMOD_DU = 0x70e78000, OPC_VSLL_B = 0x70e80000, OPC_VSLL_H = 0x70e88000, OPC_VSLL_W = 0x70e90000, @@ -423,86 +240,6 @@ typedef enum { OPC_VROTR_H = 0x70ee8000, OPC_VROTR_W = 0x70ef0000, OPC_VROTR_D = 0x70ef8000, - OPC_VSRLR_B = 0x70f00000, - OPC_VSRLR_H = 0x70f08000, - OPC_VSRLR_W = 0x70f10000, - OPC_VSRLR_D = 0x70f18000, - OPC_VSRAR_B = 0x70f20000, - OPC_VSRAR_H = 0x70f28000, - OPC_VSRAR_W = 0x70f30000, - OPC_VSRAR_D = 0x70f38000, - OPC_VSRLN_B_H = 0x70f48000, - OPC_VSRLN_H_W = 0x70f50000, - OPC_VSRLN_W_D = 0x70f58000, - OPC_VSRAN_B_H = 0x70f68000, - OPC_VSRAN_H_W = 0x70f70000, - OPC_VSRAN_W_D = 0x70f78000, - OPC_VSRLRN_B_H = 0x70f88000, - OPC_VSRLRN_H_W = 0x70f90000, - OPC_VSRLRN_W_D = 0x70f98000, - OPC_VSRARN_B_H = 0x70fa8000, - OPC_VSRARN_H_W = 0x70fb0000, - OPC_VSRARN_W_D = 0x70fb8000, - OPC_VSSRLN_B_H = 0x70fc8000, - OPC_VSSRLN_H_W = 0x70fd0000, - OPC_VSSRLN_W_D = 0x70fd8000, - OPC_VSSRAN_B_H = 0x70fe8000, - OPC_VSSRAN_H_W = 0x70ff0000, - OPC_VSSRAN_W_D = 0x70ff8000, - OPC_VSSRLRN_B_H = 0x71008000, - OPC_VSSRLRN_H_W = 0x71010000, - OPC_VSSRLRN_W_D = 0x71018000, - OPC_VSSRARN_B_H = 0x71028000, - OPC_VSSRARN_H_W = 0x71030000, - OPC_VSSRARN_W_D = 0x71038000, - OPC_VSSRLN_BU_H = 0x71048000, - OPC_VSSRLN_HU_W = 0x71050000, - OPC_VSSRLN_WU_D = 0x71058000, - OPC_VSSRAN_BU_H = 0x71068000, - OPC_VSSRAN_HU_W = 0x71070000, - OPC_VSSRAN_WU_D = 0x71078000, - OPC_VSSRLRN_BU_H = 0x71088000, - OPC_VSSRLRN_HU_W = 0x71090000, - OPC_VSSRLRN_WU_D = 0x71098000, - OPC_VSSRARN_BU_H = 0x710a8000, - OPC_VSSRARN_HU_W = 0x710b0000, - OPC_VSSRARN_WU_D = 0x710b8000, - OPC_VBITCLR_B = 0x710c0000, - OPC_VBITCLR_H = 0x710c8000, - OPC_VBITCLR_W = 0x710d0000, - OPC_VBITCLR_D = 0x710d8000, - OPC_VBITSET_B = 0x710e0000, - OPC_VBITSET_H = 0x710e8000, - OPC_VBITSET_W = 0x710f0000, - OPC_VBITSET_D = 0x710f8000, - OPC_VBITREV_B = 0x71100000, - OPC_VBITREV_H = 0x71108000, - OPC_VBITREV_W = 0x71110000, - OPC_VBITREV_D = 0x71118000, - OPC_VPACKEV_B = 0x71160000, - OPC_VPACKEV_H = 0x71168000, - OPC_VPACKEV_W = 0x71170000, - OPC_VPACKEV_D = 0x71178000, - OPC_VPACKOD_B = 0x71180000, - OPC_VPACKOD_H = 0x71188000, - OPC_VPACKOD_W = 0x71190000, - OPC_VPACKOD_D = 0x71198000, - OPC_VILVL_B = 0x711a0000, - OPC_VILVL_H = 0x711a8000, - OPC_VILVL_W = 0x711b0000, - OPC_VILVL_D = 0x711b8000, - OPC_VILVH_B = 0x711c0000, - OPC_VILVH_H = 0x711c8000, - OPC_VILVH_W = 0x711d0000, - OPC_VILVH_D = 0x711d8000, - OPC_VPICKEV_B = 0x711e0000, - OPC_VPICKEV_H = 0x711e8000, - OPC_VPICKEV_W = 0x711f0000, - OPC_VPICKEV_D = 0x711f8000, - OPC_VPICKOD_B = 0x71200000, - OPC_VPICKOD_H = 0x71208000, - OPC_VPICKOD_W = 0x71210000, - OPC_VPICKOD_D = 0x71218000, OPC_VREPLVE_B = 0x71220000, OPC_VREPLVE_H = 0x71228000, OPC_VREPLVE_W = 0x71230000, @@ -513,41 +250,6 @@ typedef enum { OPC_VNOR_V = 0x71278000, OPC_VANDN_V = 0x71280000, OPC_VORN_V = 0x71288000, - OPC_VFRSTP_B = 0x712b0000, - OPC_VFRSTP_H = 0x712b8000, - OPC_VADD_Q = 0x712d0000, - OPC_VSUB_Q = 0x712d8000, - OPC_VSIGNCOV_B = 0x712e0000, - OPC_VSIGNCOV_H = 0x712e8000, - OPC_VSIGNCOV_W = 0x712f0000, - OPC_VSIGNCOV_D = 0x712f8000, - OPC_VFADD_S = 0x71308000, - OPC_VFADD_D = 0x71310000, - OPC_VFSUB_S = 0x71328000, - OPC_VFSUB_D = 0x71330000, - OPC_VFMUL_S = 0x71388000, - OPC_VFMUL_D = 0x71390000, - OPC_VFDIV_S = 0x713a8000, - OPC_VFDIV_D = 0x713b0000, - OPC_VFMAX_S = 0x713c8000, - OPC_VFMAX_D = 0x713d0000, - OPC_VFMIN_S = 0x713e8000, - OPC_VFMIN_D = 0x713f0000, - OPC_VFMAXA_S = 0x71408000, - OPC_VFMAXA_D = 0x71410000, - OPC_VFMINA_S = 0x71428000, - OPC_VFMINA_D = 0x71430000, - OPC_VFCVT_H_S = 0x71460000, - OPC_VFCVT_S_D = 0x71468000, - OPC_VFFINT_S_L = 0x71480000, - OPC_VFTINT_W_D = 0x71498000, - OPC_VFTINTRM_W_D = 0x714a0000, - OPC_VFTINTRP_W_D = 0x714a8000, - OPC_VFTINTRZ_W_D = 0x714b0000, - OPC_VFTINTRNE_W_D = 0x714b8000, - OPC_VSHUF_H = 0x717a8000, - OPC_VSHUF_W = 0x717b0000, - OPC_VSHUF_D = 0x717b8000, OPC_VSEQI_B = 0x72800000, OPC_VSEQI_H = 0x72808000, OPC_VSEQI_W = 0x72810000, @@ -576,8 +278,6 @@ typedef enum { OPC_VSUBI_HU = 0x728c8000, OPC_VSUBI_WU = 0x728d0000, OPC_VSUBI_DU = 0x728d8000, - OPC_VBSLL_V = 0x728e0000, - OPC_VBSRL_V = 0x728e8000, OPC_VMAXI_B = 0x72900000, OPC_VMAXI_H = 0x72908000, OPC_VMAXI_W = 0x72910000, @@ -594,102 +294,10 @@ typedef enum { OPC_VMINI_HU = 0x72968000, OPC_VMINI_WU = 0x72970000, OPC_VMINI_DU = 0x72978000, - OPC_VFRSTPI_B = 0x729a0000, - OPC_VFRSTPI_H = 0x729a8000, - OPC_VCLO_B = 0x729c0000, - OPC_VCLO_H = 0x729c0400, - OPC_VCLO_W = 0x729c0800, - OPC_VCLO_D = 0x729c0c00, - OPC_VCLZ_B = 0x729c1000, - OPC_VCLZ_H = 0x729c1400, - OPC_VCLZ_W = 0x729c1800, - OPC_VCLZ_D = 0x729c1c00, - OPC_VPCNT_B = 0x729c2000, - OPC_VPCNT_H = 0x729c2400, - OPC_VPCNT_W = 0x729c2800, - OPC_VPCNT_D = 0x729c2c00, OPC_VNEG_B = 0x729c3000, OPC_VNEG_H = 0x729c3400, OPC_VNEG_W = 0x729c3800, OPC_VNEG_D = 0x729c3c00, - OPC_VMSKLTZ_B = 0x729c4000, - OPC_VMSKLTZ_H = 0x729c4400, - OPC_VMSKLTZ_W = 0x729c4800, - OPC_VMSKLTZ_D = 0x729c4c00, - OPC_VMSKGEZ_B = 0x729c5000, - OPC_VMSKNZ_B = 0x729c6000, - OPC_VSETEQZ_V = 0x729c9800, - OPC_VSETNEZ_V = 0x729c9c00, - OPC_VSETANYEQZ_B = 0x729ca000, - OPC_VSETANYEQZ_H = 0x729ca400, - OPC_VSETANYEQZ_W = 0x729ca800, - OPC_VSETANYEQZ_D = 0x729cac00, - OPC_VSETALLNEZ_B = 0x729cb000, - OPC_VSETALLNEZ_H = 0x729cb400, - OPC_VSETALLNEZ_W = 0x729cb800, - OPC_VSETALLNEZ_D = 0x729cbc00, - OPC_VFLOGB_S = 0x729cc400, - OPC_VFLOGB_D = 0x729cc800, - OPC_VFCLASS_S = 0x729cd400, - OPC_VFCLASS_D = 0x729cd800, - OPC_VFSQRT_S = 0x729ce400, - OPC_VFSQRT_D = 0x729ce800, - OPC_VFRECIP_S = 0x729cf400, - OPC_VFRECIP_D = 0x729cf800, - OPC_VFRSQRT_S = 0x729d0400, - OPC_VFRSQRT_D = 0x729d0800, - OPC_VFRINT_S = 0x729d3400, - OPC_VFRINT_D = 0x729d3800, - OPC_VFRINTRM_S = 0x729d4400, - OPC_VFRINTRM_D = 0x729d4800, - OPC_VFRINTRP_S = 0x729d5400, - OPC_VFRINTRP_D = 0x729d5800, - OPC_VFRINTRZ_S = 0x729d6400, - OPC_VFRINTRZ_D = 0x729d6800, - OPC_VFRINTRNE_S = 0x729d7400, - OPC_VFRINTRNE_D = 0x729d7800, - OPC_VFCVTL_S_H = 0x729de800, - OPC_VFCVTH_S_H = 0x729dec00, - OPC_VFCVTL_D_S = 0x729df000, - OPC_VFCVTH_D_S = 0x729df400, - OPC_VFFINT_S_W = 0x729e0000, - OPC_VFFINT_S_WU = 0x729e0400, - OPC_VFFINT_D_L = 0x729e0800, - OPC_VFFINT_D_LU = 0x729e0c00, - OPC_VFFINTL_D_W = 0x729e1000, - OPC_VFFINTH_D_W = 0x729e1400, - OPC_VFTINT_W_S = 0x729e3000, - OPC_VFTINT_L_D = 0x729e3400, - OPC_VFTINTRM_W_S = 0x729e3800, - OPC_VFTINTRM_L_D = 0x729e3c00, - OPC_VFTINTRP_W_S = 0x729e4000, - OPC_VFTINTRP_L_D = 0x729e4400, - OPC_VFTINTRZ_W_S = 0x729e4800, - OPC_VFTINTRZ_L_D = 0x729e4c00, - OPC_VFTINTRNE_W_S = 0x729e5000, - OPC_VFTINTRNE_L_D = 0x729e5400, - OPC_VFTINT_WU_S = 0x729e5800, - OPC_VFTINT_LU_D = 0x729e5c00, - OPC_VFTINTRZ_WU_S = 0x729e7000, - OPC_VFTINTRZ_LU_D = 0x729e7400, - OPC_VFTINTL_L_S = 0x729e8000, - OPC_VFTINTH_L_S = 0x729e8400, - OPC_VFTINTRML_L_S = 0x729e8800, - OPC_VFTINTRMH_L_S = 0x729e8c00, - OPC_VFTINTRPL_L_S = 0x729e9000, - OPC_VFTINTRPH_L_S = 0x729e9400, - OPC_VFTINTRZL_L_S = 0x729e9800, - OPC_VFTINTRZH_L_S = 0x729e9c00, - OPC_VFTINTRNEL_L_S = 0x729ea000, - OPC_VFTINTRNEH_L_S = 0x729ea400, - OPC_VEXTH_H_B = 0x729ee000, - OPC_VEXTH_W_H = 0x729ee400, - OPC_VEXTH_D_W = 0x729ee800, - OPC_VEXTH_Q_D = 0x729eec00, - OPC_VEXTH_HU_BU = 0x729ef000, - OPC_VEXTH_WU_HU = 0x729ef400, - OPC_VEXTH_DU_WU = 0x729ef800, - OPC_VEXTH_QU_DU = 0x729efc00, OPC_VREPLGR2VR_B = 0x729f0000, OPC_VREPLGR2VR_H = 0x729f0400, OPC_VREPLGR2VR_W = 0x729f0800, @@ -698,14 +306,6 @@ typedef enum { OPC_VROTRI_H = 0x72a04000, OPC_VROTRI_W = 0x72a08000, OPC_VROTRI_D = 0x72a10000, - OPC_VSRLRI_B = 0x72a42000, - OPC_VSRLRI_H = 0x72a44000, - OPC_VSRLRI_W = 0x72a48000, - OPC_VSRLRI_D = 0x72a50000, - OPC_VSRARI_B = 0x72a82000, - OPC_VSRARI_H = 0x72a84000, - OPC_VSRARI_W = 0x72a88000, - OPC_VSRARI_D = 0x72a90000, OPC_VINSGR2VR_B = 0x72eb8000, OPC_VINSGR2VR_H = 0x72ebc000, OPC_VINSGR2VR_W = 0x72ebe000, @@ -722,14 +322,6 @@ typedef enum { OPC_VREPLVEI_H = 0x72f7c000, OPC_VREPLVEI_W = 0x72f7e000, OPC_VREPLVEI_D = 0x72f7f000, - OPC_VSLLWIL_H_B = 0x73082000, - OPC_VSLLWIL_W_H = 0x73084000, - OPC_VSLLWIL_D_W = 0x73088000, - OPC_VEXTL_Q_D = 0x73090000, - OPC_VSLLWIL_HU_BU = 0x730c2000, - OPC_VSLLWIL_WU_HU = 0x730c4000, - OPC_VSLLWIL_DU_WU = 0x730c8000, - OPC_VEXTL_QU_DU = 0x730d0000, OPC_VBITCLRI_B = 0x73102000, OPC_VBITCLRI_H = 0x73104000, OPC_VBITCLRI_W = 0x73108000, @@ -742,14 +334,6 @@ typedef enum { OPC_VBITREVI_H = 0x73184000, OPC_VBITREVI_W = 0x73188000, OPC_VBITREVI_D = 0x73190000, - OPC_VSAT_B = 0x73242000, - OPC_VSAT_H = 0x73244000, - OPC_VSAT_W = 0x73248000, - OPC_VSAT_D = 0x73250000, - OPC_VSAT_BU = 0x73282000, - OPC_VSAT_HU = 0x73284000, - OPC_VSAT_WU = 0x73288000, - OPC_VSAT_DU = 0x73290000, OPC_VSLLI_B = 0x732c2000, OPC_VSLLI_H = 0x732c4000, OPC_VSLLI_W = 0x732c8000, @@ -762,69 +346,203 @@ typedef enum { OPC_VSRAI_H = 0x73344000, OPC_VSRAI_W = 0x73348000, OPC_VSRAI_D = 0x73350000, - OPC_VSRLNI_B_H = 0x73404000, - OPC_VSRLNI_H_W = 0x73408000, - OPC_VSRLNI_W_D = 0x73410000, - OPC_VSRLNI_D_Q = 0x73420000, - OPC_VSRLRNI_B_H = 0x73444000, - OPC_VSRLRNI_H_W = 0x73448000, - OPC_VSRLRNI_W_D = 0x73450000, - OPC_VSRLRNI_D_Q = 0x73460000, - OPC_VSSRLNI_B_H = 0x73484000, - OPC_VSSRLNI_H_W = 0x73488000, - OPC_VSSRLNI_W_D = 0x73490000, - OPC_VSSRLNI_D_Q = 0x734a0000, - OPC_VSSRLNI_BU_H = 0x734c4000, - OPC_VSSRLNI_HU_W = 0x734c8000, - OPC_VSSRLNI_WU_D = 0x734d0000, - OPC_VSSRLNI_DU_Q = 0x734e0000, - OPC_VSSRLRNI_B_H = 0x73504000, - OPC_VSSRLRNI_H_W = 0x73508000, - OPC_VSSRLRNI_W_D = 0x73510000, - OPC_VSSRLRNI_D_Q = 0x73520000, - OPC_VSSRLRNI_BU_H = 0x73544000, - OPC_VSSRLRNI_HU_W = 0x73548000, - OPC_VSSRLRNI_WU_D = 0x73550000, - OPC_VSSRLRNI_DU_Q = 0x73560000, - OPC_VSRANI_B_H = 0x73584000, - OPC_VSRANI_H_W = 0x73588000, - OPC_VSRANI_W_D = 0x73590000, - OPC_VSRANI_D_Q = 0x735a0000, - OPC_VSRARNI_B_H = 0x735c4000, - OPC_VSRARNI_H_W = 0x735c8000, - OPC_VSRARNI_W_D = 0x735d0000, - OPC_VSRARNI_D_Q = 0x735e0000, - OPC_VSSRANI_B_H = 0x73604000, - OPC_VSSRANI_H_W = 0x73608000, - OPC_VSSRANI_W_D = 0x73610000, - OPC_VSSRANI_D_Q = 0x73620000, - OPC_VSSRANI_BU_H = 0x73644000, - OPC_VSSRANI_HU_W = 0x73648000, - OPC_VSSRANI_WU_D = 0x73650000, - OPC_VSSRANI_DU_Q = 0x73660000, - OPC_VSSRARNI_B_H = 0x73684000, - OPC_VSSRARNI_H_W = 0x73688000, - OPC_VSSRARNI_W_D = 0x73690000, - OPC_VSSRARNI_D_Q = 0x736a0000, - OPC_VSSRARNI_BU_H = 0x736c4000, - OPC_VSSRARNI_HU_W = 0x736c8000, - OPC_VSSRARNI_WU_D = 0x736d0000, - OPC_VSSRARNI_DU_Q = 0x736e0000, - OPC_VEXTRINS_D = 0x73800000, - OPC_VEXTRINS_W = 0x73840000, - OPC_VEXTRINS_H = 0x73880000, - OPC_VEXTRINS_B = 0x738c0000, - OPC_VSHUF4I_B = 0x73900000, - OPC_VSHUF4I_H = 0x73940000, - OPC_VSHUF4I_W = 0x73980000, - OPC_VSHUF4I_D = 0x739c0000, OPC_VBITSELI_B = 0x73c40000, OPC_VANDI_B = 0x73d00000, OPC_VORI_B = 0x73d40000, OPC_VXORI_B = 0x73d80000, OPC_VNORI_B = 0x73dc0000, OPC_VLDI = 0x73e00000, - OPC_VPERMI_W = 0x73e40000, + OPC_XVSEQ_B = 0x74000000, + OPC_XVSEQ_H = 0x74008000, + OPC_XVSEQ_W = 0x74010000, + OPC_XVSEQ_D = 0x74018000, + OPC_XVSLE_B = 0x74020000, + OPC_XVSLE_H = 0x74028000, + OPC_XVSLE_W = 0x74030000, + OPC_XVSLE_D = 0x74038000, + OPC_XVSLE_BU = 0x74040000, + OPC_XVSLE_HU = 0x74048000, + OPC_XVSLE_WU = 0x74050000, + OPC_XVSLE_DU = 0x74058000, + OPC_XVSLT_B = 0x74060000, + OPC_XVSLT_H = 0x74068000, + OPC_XVSLT_W = 0x74070000, + OPC_XVSLT_D = 0x74078000, + OPC_XVSLT_BU = 0x74080000, + OPC_XVSLT_HU = 0x74088000, + OPC_XVSLT_WU = 0x74090000, + OPC_XVSLT_DU = 0x74098000, + OPC_XVADD_B = 0x740a0000, + OPC_XVADD_H = 0x740a8000, + OPC_XVADD_W = 0x740b0000, + OPC_XVADD_D = 0x740b8000, + OPC_XVSUB_B = 0x740c0000, + OPC_XVSUB_H = 0x740c8000, + OPC_XVSUB_W = 0x740d0000, + OPC_XVSUB_D = 0x740d8000, + OPC_XVSADD_B = 0x74460000, + OPC_XVSADD_H = 0x74468000, + OPC_XVSADD_W = 0x74470000, + OPC_XVSADD_D = 0x74478000, + OPC_XVSSUB_B = 0x74480000, + OPC_XVSSUB_H = 0x74488000, + OPC_XVSSUB_W = 0x74490000, + OPC_XVSSUB_D = 0x74498000, + OPC_XVSADD_BU = 0x744a0000, + OPC_XVSADD_HU = 0x744a8000, + OPC_XVSADD_WU = 0x744b0000, + OPC_XVSADD_DU = 0x744b8000, + OPC_XVSSUB_BU = 0x744c0000, + OPC_XVSSUB_HU = 0x744c8000, + OPC_XVSSUB_WU = 0x744d0000, + OPC_XVSSUB_DU = 0x744d8000, + OPC_XVMAX_B = 0x74700000, + OPC_XVMAX_H = 0x74708000, + OPC_XVMAX_W = 0x74710000, + OPC_XVMAX_D = 0x74718000, + OPC_XVMIN_B = 0x74720000, + OPC_XVMIN_H = 0x74728000, + OPC_XVMIN_W = 0x74730000, + OPC_XVMIN_D = 0x74738000, + OPC_XVMAX_BU = 0x74740000, + OPC_XVMAX_HU = 0x74748000, + OPC_XVMAX_WU = 0x74750000, + OPC_XVMAX_DU = 0x74758000, + OPC_XVMIN_BU = 0x74760000, + OPC_XVMIN_HU = 0x74768000, + OPC_XVMIN_WU = 0x74770000, + OPC_XVMIN_DU = 0x74778000, + OPC_XVMUL_B = 0x74840000, + OPC_XVMUL_H = 0x74848000, + OPC_XVMUL_W = 0x74850000, + OPC_XVMUL_D = 0x74858000, + OPC_XVSLL_B = 0x74e80000, + OPC_XVSLL_H = 0x74e88000, + OPC_XVSLL_W = 0x74e90000, + OPC_XVSLL_D = 0x74e98000, + OPC_XVSRL_B = 0x74ea0000, + OPC_XVSRL_H = 0x74ea8000, + OPC_XVSRL_W = 0x74eb0000, + OPC_XVSRL_D = 0x74eb8000, + OPC_XVSRA_B = 0x74ec0000, + OPC_XVSRA_H = 0x74ec8000, + OPC_XVSRA_W = 0x74ed0000, + OPC_XVSRA_D = 0x74ed8000, + OPC_XVROTR_B = 0x74ee0000, + OPC_XVROTR_H = 0x74ee8000, + OPC_XVROTR_W = 0x74ef0000, + OPC_XVROTR_D = 0x74ef8000, + OPC_XVREPLVE_B = 0x75220000, + OPC_XVREPLVE_H = 0x75228000, + OPC_XVREPLVE_W = 0x75230000, + OPC_XVREPLVE_D = 0x75238000, + OPC_XVAND_V = 0x75260000, + OPC_XVOR_V = 0x75268000, + OPC_XVXOR_V = 0x75270000, + OPC_XVNOR_V = 0x75278000, + OPC_XVANDN_V = 0x75280000, + OPC_XVORN_V = 0x75288000, + OPC_XVSEQI_B = 0x76800000, + OPC_XVSEQI_H = 0x76808000, + OPC_XVSEQI_W = 0x76810000, + OPC_XVSEQI_D = 0x76818000, + OPC_XVSLEI_B = 0x76820000, + OPC_XVSLEI_H = 0x76828000, + OPC_XVSLEI_W = 0x76830000, + OPC_XVSLEI_D = 0x76838000, + OPC_XVSLEI_BU = 0x76840000, + OPC_XVSLEI_HU = 0x76848000, + OPC_XVSLEI_WU = 0x76850000, + OPC_XVSLEI_DU = 0x76858000, + OPC_XVSLTI_B = 0x76860000, + OPC_XVSLTI_H = 0x76868000, + OPC_XVSLTI_W = 0x76870000, + OPC_XVSLTI_D = 0x76878000, + OPC_XVSLTI_BU = 0x76880000, + OPC_XVSLTI_HU = 0x76888000, + OPC_XVSLTI_WU = 0x76890000, + OPC_XVSLTI_DU = 0x76898000, + OPC_XVADDI_BU = 0x768a0000, + OPC_XVADDI_HU = 0x768a8000, + OPC_XVADDI_WU = 0x768b0000, + OPC_XVADDI_DU = 0x768b8000, + OPC_XVSUBI_BU = 0x768c0000, + OPC_XVSUBI_HU = 0x768c8000, + OPC_XVSUBI_WU = 0x768d0000, + OPC_XVSUBI_DU = 0x768d8000, + OPC_XVMAXI_B = 0x76900000, + OPC_XVMAXI_H = 0x76908000, + OPC_XVMAXI_W = 0x76910000, + OPC_XVMAXI_D = 0x76918000, + OPC_XVMINI_B = 0x76920000, + OPC_XVMINI_H = 0x76928000, + OPC_XVMINI_W = 0x76930000, + OPC_XVMINI_D = 0x76938000, + OPC_XVMAXI_BU = 0x76940000, + OPC_XVMAXI_HU = 0x76948000, + OPC_XVMAXI_WU = 0x76950000, + OPC_XVMAXI_DU = 0x76958000, + OPC_XVMINI_BU = 0x76960000, + OPC_XVMINI_HU = 0x76968000, + OPC_XVMINI_WU = 0x76970000, + OPC_XVMINI_DU = 0x76978000, + OPC_XVNEG_B = 0x769c3000, + OPC_XVNEG_H = 0x769c3400, + OPC_XVNEG_W = 0x769c3800, + OPC_XVNEG_D = 0x769c3c00, + OPC_XVREPLGR2VR_B = 0x769f0000, + OPC_XVREPLGR2VR_H = 0x769f0400, + OPC_XVREPLGR2VR_W = 0x769f0800, + OPC_XVREPLGR2VR_D = 0x769f0c00, + OPC_XVROTRI_B = 0x76a02000, + OPC_XVROTRI_H = 0x76a04000, + OPC_XVROTRI_W = 0x76a08000, + OPC_XVROTRI_D = 0x76a10000, + OPC_XVINSGR2VR_W = 0x76ebc000, + OPC_XVINSGR2VR_D = 0x76ebe000, + OPC_XVPICKVE2GR_W = 0x76efc000, + OPC_XVPICKVE2GR_D = 0x76efe000, + OPC_XVPICKVE2GR_WU = 0x76f3c000, + OPC_XVPICKVE2GR_DU = 0x76f3e000, + OPC_XVREPL128VEI_B = 0x76f78000, + OPC_XVREPL128VEI_H = 0x76f7c000, + OPC_XVREPL128VEI_W = 0x76f7e000, + OPC_XVREPL128VEI_D = 0x76f7f000, + OPC_XVREPLVE0_B = 0x77070000, + OPC_XVREPLVE0_H = 0x77078000, + OPC_XVREPLVE0_W = 0x7707c000, + OPC_XVREPLVE0_D = 0x7707e000, + OPC_XVREPLVE0_Q = 0x7707f000, + OPC_XVBITCLRI_B = 0x77102000, + OPC_XVBITCLRI_H = 0x77104000, + OPC_XVBITCLRI_W = 0x77108000, + OPC_XVBITCLRI_D = 0x77110000, + OPC_XVBITSETI_B = 0x77142000, + OPC_XVBITSETI_H = 0x77144000, + OPC_XVBITSETI_W = 0x77148000, + OPC_XVBITSETI_D = 0x77150000, + OPC_XVBITREVI_B = 0x77182000, + OPC_XVBITREVI_H = 0x77184000, + OPC_XVBITREVI_W = 0x77188000, + OPC_XVBITREVI_D = 0x77190000, + OPC_XVSLLI_B = 0x772c2000, + OPC_XVSLLI_H = 0x772c4000, + OPC_XVSLLI_W = 0x772c8000, + OPC_XVSLLI_D = 0x772d0000, + OPC_XVSRLI_B = 0x77302000, + OPC_XVSRLI_H = 0x77304000, + OPC_XVSRLI_W = 0x77308000, + OPC_XVSRLI_D = 0x77310000, + OPC_XVSRAI_B = 0x77342000, + OPC_XVSRAI_H = 0x77344000, + OPC_XVSRAI_W = 0x77348000, + OPC_XVSRAI_D = 0x77350000, + OPC_XVBITSELI_B = 0x77c40000, + OPC_XVANDI_B = 0x77d00000, + OPC_XVORI_B = 0x77d40000, + OPC_XVXORI_B = 0x77d80000, + OPC_XVNORI_B = 0x77dc0000, + OPC_XVLDI = 0x77e00000, } LoongArchInsn; static int32_t __attribute__((unused)) @@ -873,11 +591,11 @@ encode_dk_slots(LoongArchInsn opc, uint32_t d, uint32_t k) } static int32_t __attribute__((unused)) -encode_cdvj_insn(LoongArchInsn opc, TCGReg cd, TCGReg vj) +encode_dfj_insn(LoongArchInsn opc, TCGReg d, TCGReg fj) { - tcg_debug_assert(cd >= 0 && cd <= 0x7); - tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); - return encode_dj_slots(opc, cd, vj & 0x1f); + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); + return encode_dj_slots(opc, d, fj & 0x1f); } static int32_t __attribute__((unused)) @@ -924,6 +642,24 @@ encode_djuk12_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk12) return encode_djk_slots(opc, d, j, uk12); } +static int32_t __attribute__((unused)) +encode_djuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk3) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, d, j, uk3); +} + +static int32_t __attribute__((unused)) +encode_djuk4_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk4) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, d, j, uk4); +} + static int32_t __attribute__((unused)) encode_djuk5_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk5) { @@ -972,6 +708,14 @@ encode_dsj20_insn(LoongArchInsn opc, TCGReg d, int32_t sj20) return encode_dj_slots(opc, d, sj20 & 0xfffff); } +static int32_t __attribute__((unused)) +encode_dtj_insn(LoongArchInsn opc, TCGReg d, TCGReg tj) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(tj >= 0 && tj <= 0x3); + return encode_dj_slots(opc, d, tj); +} + static int32_t __attribute__((unused)) encode_dvjuk1_insn(LoongArchInsn opc, TCGReg d, TCGReg vj, uint32_t uk1) { @@ -1008,6 +752,58 @@ encode_dvjuk4_insn(LoongArchInsn opc, TCGReg d, TCGReg vj, uint32_t uk4) return encode_djk_slots(opc, d, vj & 0x1f, uk4); } +static int32_t __attribute__((unused)) +encode_dxjuk2_insn(LoongArchInsn opc, TCGReg d, TCGReg xj, uint32_t uk2) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, d, xj & 0x1f, uk2); +} + +static int32_t __attribute__((unused)) +encode_dxjuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg xj, uint32_t uk3) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, d, xj & 0x1f, uk3); +} + +static int32_t __attribute__((unused)) +encode_fdfj_insn(LoongArchInsn opc, TCGReg fd, TCGReg fj) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); + return encode_dj_slots(opc, fd & 0x1f, fj & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_fdj_insn(LoongArchInsn opc, TCGReg fd, TCGReg j) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, fd & 0x1f, j); +} + +static int32_t __attribute__((unused)) +encode_fdjk_insn(LoongArchInsn opc, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, fd & 0x1f, j, k); +} + +static int32_t __attribute__((unused)) +encode_fdjsk12_insn(LoongArchInsn opc, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk12 >= -0x800 && sk12 <= 0x7ff); + return encode_djk_slots(opc, fd & 0x1f, j, sk12 & 0xfff); +} + static int32_t __attribute__((unused)) encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) { @@ -1015,6 +811,21 @@ encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) return encode_dk_slots(opc, (sd10k16 >> 16) & 0x3ff, sd10k16 & 0xffff); } +static int32_t __attribute__((unused)) +encode_sd5k16_insn(LoongArchInsn opc, int32_t sd5k16) +{ + tcg_debug_assert(sd5k16 >= -0x100000 && sd5k16 <= 0xfffff); + return encode_dk_slots(opc, (sd5k16 >> 16) & 0x1f, sd5k16 & 0xffff); +} + +static int32_t __attribute__((unused)) +encode_tdj_insn(LoongArchInsn opc, TCGReg td, TCGReg j) +{ + tcg_debug_assert(td >= 0 && td <= 0x3); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, td, j); +} + static int32_t __attribute__((unused)) encode_ud15_insn(LoongArchInsn opc, uint32_t ud15) { @@ -1243,15 +1054,6 @@ encode_vdvjuk6_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk6) return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk6); } -static int32_t __attribute__((unused)) -encode_vdvjuk7_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); - tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); - tcg_debug_assert(uk7 <= 0x7f); - return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk7); -} - static int32_t __attribute__((unused)) encode_vdvjuk8_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk8) { @@ -1281,6 +1083,252 @@ encode_vdvjvkva_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg vk, return encode_djka_slots(opc, vd & 0x1f, vj & 0x1f, vk & 0x1f, va & 0x1f); } +static int32_t __attribute__((unused)) +encode_xdj_insn(LoongArchInsn opc, TCGReg xd, TCGReg j) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, xd & 0x1f, j); +} + +static int32_t __attribute__((unused)) +encode_xdjk_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, TCGReg k) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, xd & 0x1f, j, k); +} + +static int32_t __attribute__((unused)) +encode_xdjsk10_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk10) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk10 >= -0x200 && sk10 <= 0x1ff); + return encode_djk_slots(opc, xd & 0x1f, j, sk10 & 0x3ff); +} + +static int32_t __attribute__((unused)) +encode_xdjsk11_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk11) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk11 >= -0x400 && sk11 <= 0x3ff); + return encode_djk_slots(opc, xd & 0x1f, j, sk11 & 0x7ff); +} + +static int32_t __attribute__((unused)) +encode_xdjsk12_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk12 >= -0x800 && sk12 <= 0x7ff); + return encode_djk_slots(opc, xd & 0x1f, j, sk12 & 0xfff); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un2_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un2) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un2 <= 0x3); + return encode_djkn_slots(opc, xd & 0x1f, j, sk8 & 0xff, un2); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un3_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un3) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un3 <= 0x7); + return encode_djkn_slots(opc, xd & 0x1f, j, sk8 & 0xff, un3); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un4_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un4) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un4 <= 0xf); + return encode_djkn_slots(opc, xd & 0x1f, j, sk8 & 0xff, un4); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un5_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un5) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un5 <= 0x1f); + return encode_djkn_slots(opc, xd & 0x1f, j, sk8 & 0xff, un5); +} + +static int32_t __attribute__((unused)) +encode_xdjsk9_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk9) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk9 >= -0x100 && sk9 <= 0xff); + return encode_djk_slots(opc, xd & 0x1f, j, sk9 & 0x1ff); +} + +static int32_t __attribute__((unused)) +encode_xdjuk2_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, uint32_t uk2) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, xd & 0x1f, j, uk2); +} + +static int32_t __attribute__((unused)) +encode_xdjuk3_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, uint32_t uk3) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, xd & 0x1f, j, uk3); +} + +static int32_t __attribute__((unused)) +encode_xdsj13_insn(LoongArchInsn opc, TCGReg xd, int32_t sj13) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(sj13 >= -0x1000 && sj13 <= 0xfff); + return encode_dj_slots(opc, xd & 0x1f, sj13 & 0x1fff); +} + +static int32_t __attribute__((unused)) +encode_xdxj_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + return encode_dj_slots(opc, xd & 0x1f, xj & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_xdxjk_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, k); +} + +static int32_t __attribute__((unused)) +encode_xdxjsk5_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(sk5 >= -0x10 && sk5 <= 0xf); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, sk5 & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk1_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk1) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk1 <= 0x1); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk1); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk2_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk2) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk2); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk3_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk3); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk4_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk4); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk5_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk5 <= 0x1f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk5); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk6_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk6 <= 0x3f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk6); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk8_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk8 <= 0xff); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk8); +} + +static int32_t __attribute__((unused)) +encode_xdxjxk_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(xk >= 0x20 && xk <= 0x3f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, xk & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_xdxjxkxa_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg xk, + TCGReg xa) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(xk >= 0x20 && xk <= 0x3f); + tcg_debug_assert(xa >= 0x20 && xa <= 0x3f); + return encode_djka_slots(opc, xd & 0x1f, xj & 0x1f, xk & 0x1f, xa & 0x1f); +} + +/* Emits the `movgr2scr td, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movgr2scr(TCGContext *s, TCGReg td, TCGReg j) +{ + tcg_out32(s, encode_tdj_insn(OPC_MOVGR2SCR, td, j)); +} + +/* Emits the `movscr2gr d, tj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movscr2gr(TCGContext *s, TCGReg d, TCGReg tj) +{ + tcg_out32(s, encode_dtj_insn(OPC_MOVSCR2GR, d, tj)); +} + /* Emits the `clz.w d, j` instruction. */ static void __attribute__((unused)) tcg_out_opc_clz_w(TCGContext *s, TCGReg d, TCGReg j) @@ -1484,6 +1532,20 @@ tcg_out_opc_sra_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) tcg_out32(s, encode_djk_insn(OPC_SRA_D, d, j, k)); } +/* Emits the `rotr.b d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotr_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ROTR_B, d, j, k)); +} + +/* Emits the `rotr.h d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotr_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ROTR_H, d, j, k)); +} + /* Emits the `rotr.w d, j, k` instruction. */ static void __attribute__((unused)) tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) @@ -1638,6 +1700,20 @@ tcg_out_opc_srai_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) tcg_out32(s, encode_djuk6_insn(OPC_SRAI_D, d, j, uk6)); } +/* Emits the `rotri.b d, j, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotri_b(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk3) +{ + tcg_out32(s, encode_djuk3_insn(OPC_ROTRI_B, d, j, uk3)); +} + +/* Emits the `rotri.h d, j, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotri_h(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk4) +{ + tcg_out32(s, encode_djuk4_insn(OPC_ROTRI_H, d, j, uk4)); +} + /* Emits the `rotri.w d, j, uk5` instruction. */ static void __attribute__((unused)) tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) @@ -1684,6 +1760,27 @@ tcg_out_opc_bstrpick_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRPICK_D, d, j, uk6, um6)); } +/* Emits the `fmov.d fd, fj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fmov_d(TCGContext *s, TCGReg fd, TCGReg fj) +{ + tcg_out32(s, encode_fdfj_insn(OPC_FMOV_D, fd, fj)); +} + +/* Emits the `movgr2fr.d fd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movgr2fr_d(TCGContext *s, TCGReg fd, TCGReg j) +{ + tcg_out32(s, encode_fdj_insn(OPC_MOVGR2FR_D, fd, j)); +} + +/* Emits the `movfr2gr.d d, fj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movfr2gr_d(TCGContext *s, TCGReg d, TCGReg fj) +{ + tcg_out32(s, encode_dfj_insn(OPC_MOVFR2GR_D, d, fj)); +} + /* Emits the `slti d, j, sk12` instruction. */ static void __attribute__((unused)) tcg_out_opc_slti(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) @@ -1740,370 +1837,6 @@ tcg_out_opc_xori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) tcg_out32(s, encode_djuk12_insn(OPC_XORI, d, j, uk12)); } -/* Emits the `vfmadd.s vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmadd_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMADD_S, vd, vj, vk, va)); -} - -/* Emits the `vfmadd.d vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMADD_D, vd, vj, vk, va)); -} - -/* Emits the `vfmsub.s vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmsub_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMSUB_S, vd, vj, vk, va)); -} - -/* Emits the `vfmsub.d vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMSUB_D, vd, vj, vk, va)); -} - -/* Emits the `vfnmadd.s vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfnmadd_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMADD_S, vd, vj, vk, va)); -} - -/* Emits the `vfnmadd.d vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfnmadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMADD_D, vd, vj, vk, va)); -} - -/* Emits the `vfnmsub.s vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfnmsub_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMSUB_S, vd, vj, vk, va)); -} - -/* Emits the `vfnmsub.d vd, vj, vk, va` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfnmsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) -{ - tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMSUB_D, vd, vj, vk, va)); -} - -/* Emits the `vfcmp.caf.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_caf_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CAF_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.saf.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_saf_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SAF_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.clt.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_clt_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLT_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.slt.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_slt_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLT_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.ceq.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_ceq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CEQ_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.seq.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_seq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SEQ_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cle.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cle_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sle.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sle_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cun.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cun_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUN_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sun.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sun_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUN_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cult.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cult_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULT_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sult.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sult_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULT_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cueq.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cueq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUEQ_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sueq.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sueq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUEQ_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cule.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cule_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sule.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sule_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cne.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cne_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CNE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sne.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sne_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SNE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cor.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cor_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_COR_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sor.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sor_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SOR_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.cune.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cune_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUNE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.sune.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sune_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUNE_S, vd, vj, vk)); -} - -/* Emits the `vfcmp.caf.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_caf_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CAF_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.saf.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_saf_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SAF_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.clt.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_clt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLT_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.slt.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_slt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLT_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.ceq.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_ceq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CEQ_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.seq.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_seq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SEQ_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cle.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sle.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cun.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cun_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUN_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sun.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sun_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUN_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cult.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cult_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULT_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sult.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sult_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULT_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cueq.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cueq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUEQ_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sueq.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sueq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUEQ_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cule.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cule_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sule.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sule_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cne.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cne_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CNE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sne.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sne_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SNE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cor.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cor_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_COR_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sor.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sor_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SOR_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.cune.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_cune_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUNE_D, vd, vj, vk)); -} - -/* Emits the `vfcmp.sune.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcmp_sune_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUNE_D, vd, vj, vk)); -} - /* Emits the `vbitsel.v vd, vj, vk, va` instruction. */ static void __attribute__((unused)) tcg_out_opc_vbitsel_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) @@ -2111,6 +1844,14 @@ tcg_out_opc_vbitsel_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) tcg_out32(s, encode_vdvjvkva_insn(OPC_VBITSEL_V, vd, vj, vk, va)); } +/* Emits the `xvbitsel.v xd, xj, xk, xa` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitsel_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk, + TCGReg xa) +{ + tcg_out32(s, encode_xdxjxkxa_insn(OPC_XVBITSEL_V, xd, xj, xk, xa)); +} + /* Emits the `vshuf.b vd, vj, vk, va` instruction. */ static void __attribute__((unused)) tcg_out_opc_vshuf_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) @@ -2118,6 +1859,13 @@ tcg_out_opc_vshuf_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) tcg_out32(s, encode_vdvjvkva_insn(OPC_VSHUF_B, vd, vj, vk, va)); } +/* Emits the `xvshuf.b xd, xj, xk, xa` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvshuf_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk, TCGReg xa) +{ + tcg_out32(s, encode_xdxjxkxa_insn(OPC_XVSHUF_B, xd, xj, xk, xa)); +} + /* Emits the `addu16i.d d, j, sk16` instruction. */ static void __attribute__((unused)) tcg_out_opc_addu16i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) @@ -2244,6 +1992,34 @@ tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) tcg_out32(s, encode_djsk12_insn(OPC_LD_WU, d, j, sk12)); } +/* Emits the `fld.s fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fld_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_S, fd, j, sk12)); +} + +/* Emits the `fst.s fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fst_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FST_S, fd, j, sk12)); +} + +/* Emits the `fld.d fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fld_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_D, fd, j, sk12)); +} + +/* Emits the `fst.d fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fst_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FST_D, fd, j, sk12)); +} + /* Emits the `vld vd, j, sk12` instruction. */ static void __attribute__((unused)) tcg_out_opc_vld(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) @@ -2258,6 +2034,20 @@ tcg_out_opc_vst(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) tcg_out32(s, encode_vdjsk12_insn(OPC_VST, vd, j, sk12)); } +/* Emits the `xvld xd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvld(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_xdjsk12_insn(OPC_XVLD, xd, j, sk12)); +} + +/* Emits the `xvst xd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvst(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_xdjsk12_insn(OPC_XVST, xd, j, sk12)); +} + /* Emits the `vldrepl.d vd, j, sk9` instruction. */ static void __attribute__((unused)) tcg_out_opc_vldrepl_d(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk9) @@ -2318,6 +2108,66 @@ tcg_out_opc_vstelm_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, tcg_out32(s, encode_vdjsk8un4_insn(OPC_VSTELM_B, vd, j, sk8, un4)); } +/* Emits the `xvldrepl.d xd, j, sk9` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_d(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk9) +{ + tcg_out32(s, encode_xdjsk9_insn(OPC_XVLDREPL_D, xd, j, sk9)); +} + +/* Emits the `xvldrepl.w xd, j, sk10` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_w(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk10) +{ + tcg_out32(s, encode_xdjsk10_insn(OPC_XVLDREPL_W, xd, j, sk10)); +} + +/* Emits the `xvldrepl.h xd, j, sk11` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_h(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk11) +{ + tcg_out32(s, encode_xdjsk11_insn(OPC_XVLDREPL_H, xd, j, sk11)); +} + +/* Emits the `xvldrepl.b xd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_xdjsk12_insn(OPC_XVLDREPL_B, xd, j, sk12)); +} + +/* Emits the `xvstelm.d xd, j, sk8, un2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_d(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un2) +{ + tcg_out32(s, encode_xdjsk8un2_insn(OPC_XVSTELM_D, xd, j, sk8, un2)); +} + +/* Emits the `xvstelm.w xd, j, sk8, un3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_w(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un3) +{ + tcg_out32(s, encode_xdjsk8un3_insn(OPC_XVSTELM_W, xd, j, sk8, un3)); +} + +/* Emits the `xvstelm.h xd, j, sk8, un4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_h(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un4) +{ + tcg_out32(s, encode_xdjsk8un4_insn(OPC_XVSTELM_H, xd, j, sk8, un4)); +} + +/* Emits the `xvstelm.b xd, j, sk8, un5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un5) +{ + tcg_out32(s, encode_xdjsk8un5_insn(OPC_XVSTELM_B, xd, j, sk8, un5)); +} + /* Emits the `ldx.b d, j, k` instruction. */ static void __attribute__((unused)) tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) @@ -2395,6 +2245,34 @@ tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) tcg_out32(s, encode_djk_insn(OPC_LDX_WU, d, j, k)); } +/* Emits the `fldx.s fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fldx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FLDX_S, fd, j, k)); +} + +/* Emits the `fldx.d fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fldx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FLDX_D, fd, j, k)); +} + +/* Emits the `fstx.s fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fstx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FSTX_S, fd, j, k)); +} + +/* Emits the `fstx.d fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fstx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FSTX_D, fd, j, k)); +} + /* Emits the `vldx vd, j, k` instruction. */ static void __attribute__((unused)) tcg_out_opc_vldx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) @@ -2409,6 +2287,20 @@ tcg_out_opc_vstx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) tcg_out32(s, encode_vdjk_insn(OPC_VSTX, vd, j, k)); } +/* Emits the `xvldx xd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_xdjk_insn(OPC_XVLDX, xd, j, k)); +} + +/* Emits the `xvstx xd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_xdjk_insn(OPC_XVSTX, xd, j, k)); +} + /* Emits the `dbar ud15` instruction. */ static void __attribute__((unused)) tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) @@ -2416,6 +2308,20 @@ tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) tcg_out32(s, encode_ud15_insn(OPC_DBAR, ud15)); } +/* Emits the `jiscr0 sd5k16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_jiscr0(TCGContext *s, int32_t sd5k16) +{ + tcg_out32(s, encode_sd5k16_insn(OPC_JISCR0, sd5k16)); +} + +/* Emits the `jiscr1 sd5k16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_jiscr1(TCGContext *s, int32_t sd5k16) +{ + tcg_out32(s, encode_sd5k16_insn(OPC_JISCR1, sd5k16)); +} + /* Emits the `jirl d, j, sk16` instruction. */ static void __attribute__((unused)) tcg_out_opc_jirl(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) @@ -2675,286 +2581,6 @@ tcg_out_opc_vsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_D, vd, vj, vk)); } -/* Emits the `vaddwev.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_H_B, vd, vj, vk)); -} - -/* Emits the `vaddwev.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_W_H, vd, vj, vk)); -} - -/* Emits the `vaddwev.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_D_W, vd, vj, vk)); -} - -/* Emits the `vaddwev.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_Q_D, vd, vj, vk)); -} - -/* Emits the `vsubwev.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_H_B, vd, vj, vk)); -} - -/* Emits the `vsubwev.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_W_H, vd, vj, vk)); -} - -/* Emits the `vsubwev.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_D_W, vd, vj, vk)); -} - -/* Emits the `vsubwev.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_Q_D, vd, vj, vk)); -} - -/* Emits the `vaddwod.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_H_B, vd, vj, vk)); -} - -/* Emits the `vaddwod.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_W_H, vd, vj, vk)); -} - -/* Emits the `vaddwod.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_D_W, vd, vj, vk)); -} - -/* Emits the `vaddwod.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_Q_D, vd, vj, vk)); -} - -/* Emits the `vsubwod.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_H_B, vd, vj, vk)); -} - -/* Emits the `vsubwod.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_W_H, vd, vj, vk)); -} - -/* Emits the `vsubwod.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_D_W, vd, vj, vk)); -} - -/* Emits the `vsubwod.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_Q_D, vd, vj, vk)); -} - -/* Emits the `vaddwev.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_H_BU, vd, vj, vk)); -} - -/* Emits the `vaddwev.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_W_HU, vd, vj, vk)); -} - -/* Emits the `vaddwev.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_D_WU, vd, vj, vk)); -} - -/* Emits the `vaddwev.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_Q_DU, vd, vj, vk)); -} - -/* Emits the `vsubwev.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_H_BU, vd, vj, vk)); -} - -/* Emits the `vsubwev.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_W_HU, vd, vj, vk)); -} - -/* Emits the `vsubwev.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_D_WU, vd, vj, vk)); -} - -/* Emits the `vsubwev.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_Q_DU, vd, vj, vk)); -} - -/* Emits the `vaddwod.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_H_BU, vd, vj, vk)); -} - -/* Emits the `vaddwod.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_W_HU, vd, vj, vk)); -} - -/* Emits the `vaddwod.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_D_WU, vd, vj, vk)); -} - -/* Emits the `vaddwod.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_Q_DU, vd, vj, vk)); -} - -/* Emits the `vsubwod.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_H_BU, vd, vj, vk)); -} - -/* Emits the `vsubwod.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_W_HU, vd, vj, vk)); -} - -/* Emits the `vsubwod.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_D_WU, vd, vj, vk)); -} - -/* Emits the `vsubwod.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsubwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_Q_DU, vd, vj, vk)); -} - -/* Emits the `vaddwev.h.bu.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_H_BU_B, vd, vj, vk)); -} - -/* Emits the `vaddwev.w.hu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_W_HU_H, vd, vj, vk)); -} - -/* Emits the `vaddwev.d.wu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_D_WU_W, vd, vj, vk)); -} - -/* Emits the `vaddwev.q.du.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwev_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_Q_DU_D, vd, vj, vk)); -} - -/* Emits the `vaddwod.h.bu.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_H_BU_B, vd, vj, vk)); -} - -/* Emits the `vaddwod.w.hu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_W_HU_H, vd, vj, vk)); -} - -/* Emits the `vaddwod.d.wu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_D_WU_W, vd, vj, vk)); -} - -/* Emits the `vaddwod.q.du.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vaddwod_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_Q_DU_D, vd, vj, vk)); -} - /* Emits the `vsadd.b vd, vj, vk` instruction. */ static void __attribute__((unused)) tcg_out_opc_vsadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) @@ -3067,314 +2693,6 @@ tcg_out_opc_vssub_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_DU, vd, vj, vk)); } -/* Emits the `vhaddw.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_H_B, vd, vj, vk)); -} - -/* Emits the `vhaddw.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_W_H, vd, vj, vk)); -} - -/* Emits the `vhaddw.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_D_W, vd, vj, vk)); -} - -/* Emits the `vhaddw.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_Q_D, vd, vj, vk)); -} - -/* Emits the `vhsubw.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_H_B, vd, vj, vk)); -} - -/* Emits the `vhsubw.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_W_H, vd, vj, vk)); -} - -/* Emits the `vhsubw.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_D_W, vd, vj, vk)); -} - -/* Emits the `vhsubw.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_Q_D, vd, vj, vk)); -} - -/* Emits the `vhaddw.hu.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_HU_BU, vd, vj, vk)); -} - -/* Emits the `vhaddw.wu.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_WU_HU, vd, vj, vk)); -} - -/* Emits the `vhaddw.du.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_du_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_DU_WU, vd, vj, vk)); -} - -/* Emits the `vhaddw.qu.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhaddw_qu_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_QU_DU, vd, vj, vk)); -} - -/* Emits the `vhsubw.hu.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_HU_BU, vd, vj, vk)); -} - -/* Emits the `vhsubw.wu.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_WU_HU, vd, vj, vk)); -} - -/* Emits the `vhsubw.du.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_du_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_DU_WU, vd, vj, vk)); -} - -/* Emits the `vhsubw.qu.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vhsubw_qu_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_QU_DU, vd, vj, vk)); -} - -/* Emits the `vadda.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadda_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_B, vd, vj, vk)); -} - -/* Emits the `vadda.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadda_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_H, vd, vj, vk)); -} - -/* Emits the `vadda.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadda_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_W, vd, vj, vk)); -} - -/* Emits the `vadda.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadda_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_D, vd, vj, vk)); -} - -/* Emits the `vabsd.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_B, vd, vj, vk)); -} - -/* Emits the `vabsd.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_H, vd, vj, vk)); -} - -/* Emits the `vabsd.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_W, vd, vj, vk)); -} - -/* Emits the `vabsd.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_D, vd, vj, vk)); -} - -/* Emits the `vabsd.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_BU, vd, vj, vk)); -} - -/* Emits the `vabsd.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_HU, vd, vj, vk)); -} - -/* Emits the `vabsd.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_WU, vd, vj, vk)); -} - -/* Emits the `vabsd.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vabsd_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_DU, vd, vj, vk)); -} - -/* Emits the `vavg.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_B, vd, vj, vk)); -} - -/* Emits the `vavg.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_H, vd, vj, vk)); -} - -/* Emits the `vavg.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_W, vd, vj, vk)); -} - -/* Emits the `vavg.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_D, vd, vj, vk)); -} - -/* Emits the `vavg.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_BU, vd, vj, vk)); -} - -/* Emits the `vavg.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_HU, vd, vj, vk)); -} - -/* Emits the `vavg.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_WU, vd, vj, vk)); -} - -/* Emits the `vavg.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavg_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_DU, vd, vj, vk)); -} - -/* Emits the `vavgr.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_B, vd, vj, vk)); -} - -/* Emits the `vavgr.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_H, vd, vj, vk)); -} - -/* Emits the `vavgr.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_W, vd, vj, vk)); -} - -/* Emits the `vavgr.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_D, vd, vj, vk)); -} - -/* Emits the `vavgr.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_BU, vd, vj, vk)); -} - -/* Emits the `vavgr.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_HU, vd, vj, vk)); -} - -/* Emits the `vavgr.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_WU, vd, vj, vk)); -} - -/* Emits the `vavgr.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vavgr_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_DU, vd, vj, vk)); -} - /* Emits the `vmax.b vd, vj, vk` instruction. */ static void __attribute__((unused)) tcg_out_opc_vmax_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) @@ -3515,566 +2833,6 @@ tcg_out_opc_vmul_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_D, vd, vj, vk)); } -/* Emits the `vmuh.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_B, vd, vj, vk)); -} - -/* Emits the `vmuh.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_H, vd, vj, vk)); -} - -/* Emits the `vmuh.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_W, vd, vj, vk)); -} - -/* Emits the `vmuh.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_D, vd, vj, vk)); -} - -/* Emits the `vmuh.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_BU, vd, vj, vk)); -} - -/* Emits the `vmuh.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_HU, vd, vj, vk)); -} - -/* Emits the `vmuh.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_WU, vd, vj, vk)); -} - -/* Emits the `vmuh.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmuh_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_DU, vd, vj, vk)); -} - -/* Emits the `vmulwev.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_H_B, vd, vj, vk)); -} - -/* Emits the `vmulwev.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_W_H, vd, vj, vk)); -} - -/* Emits the `vmulwev.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_D_W, vd, vj, vk)); -} - -/* Emits the `vmulwev.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_Q_D, vd, vj, vk)); -} - -/* Emits the `vmulwod.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_H_B, vd, vj, vk)); -} - -/* Emits the `vmulwod.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_W_H, vd, vj, vk)); -} - -/* Emits the `vmulwod.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_D_W, vd, vj, vk)); -} - -/* Emits the `vmulwod.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_Q_D, vd, vj, vk)); -} - -/* Emits the `vmulwev.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_H_BU, vd, vj, vk)); -} - -/* Emits the `vmulwev.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_W_HU, vd, vj, vk)); -} - -/* Emits the `vmulwev.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_D_WU, vd, vj, vk)); -} - -/* Emits the `vmulwev.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_Q_DU, vd, vj, vk)); -} - -/* Emits the `vmulwod.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_H_BU, vd, vj, vk)); -} - -/* Emits the `vmulwod.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_W_HU, vd, vj, vk)); -} - -/* Emits the `vmulwod.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_D_WU, vd, vj, vk)); -} - -/* Emits the `vmulwod.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_Q_DU, vd, vj, vk)); -} - -/* Emits the `vmulwev.h.bu.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_H_BU_B, vd, vj, vk)); -} - -/* Emits the `vmulwev.w.hu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_W_HU_H, vd, vj, vk)); -} - -/* Emits the `vmulwev.d.wu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_D_WU_W, vd, vj, vk)); -} - -/* Emits the `vmulwev.q.du.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwev_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_Q_DU_D, vd, vj, vk)); -} - -/* Emits the `vmulwod.h.bu.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_H_BU_B, vd, vj, vk)); -} - -/* Emits the `vmulwod.w.hu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_W_HU_H, vd, vj, vk)); -} - -/* Emits the `vmulwod.d.wu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_D_WU_W, vd, vj, vk)); -} - -/* Emits the `vmulwod.q.du.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmulwod_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_Q_DU_D, vd, vj, vk)); -} - -/* Emits the `vmadd.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_B, vd, vj, vk)); -} - -/* Emits the `vmadd.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmadd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_H, vd, vj, vk)); -} - -/* Emits the `vmadd.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmadd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_W, vd, vj, vk)); -} - -/* Emits the `vmadd.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_D, vd, vj, vk)); -} - -/* Emits the `vmsub.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmsub_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_B, vd, vj, vk)); -} - -/* Emits the `vmsub.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmsub_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_H, vd, vj, vk)); -} - -/* Emits the `vmsub.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmsub_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_W, vd, vj, vk)); -} - -/* Emits the `vmsub.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_D, vd, vj, vk)); -} - -/* Emits the `vmaddwev.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_H_B, vd, vj, vk)); -} - -/* Emits the `vmaddwev.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_W_H, vd, vj, vk)); -} - -/* Emits the `vmaddwev.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_D_W, vd, vj, vk)); -} - -/* Emits the `vmaddwev.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_Q_D, vd, vj, vk)); -} - -/* Emits the `vmaddwod.h.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_H_B, vd, vj, vk)); -} - -/* Emits the `vmaddwod.w.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_W_H, vd, vj, vk)); -} - -/* Emits the `vmaddwod.d.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_D_W, vd, vj, vk)); -} - -/* Emits the `vmaddwod.q.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_Q_D, vd, vj, vk)); -} - -/* Emits the `vmaddwev.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_H_BU, vd, vj, vk)); -} - -/* Emits the `vmaddwev.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_W_HU, vd, vj, vk)); -} - -/* Emits the `vmaddwev.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_D_WU, vd, vj, vk)); -} - -/* Emits the `vmaddwev.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_Q_DU, vd, vj, vk)); -} - -/* Emits the `vmaddwod.h.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_H_BU, vd, vj, vk)); -} - -/* Emits the `vmaddwod.w.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_W_HU, vd, vj, vk)); -} - -/* Emits the `vmaddwod.d.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_D_WU, vd, vj, vk)); -} - -/* Emits the `vmaddwod.q.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_Q_DU, vd, vj, vk)); -} - -/* Emits the `vmaddwev.h.bu.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_H_BU_B, vd, vj, vk)); -} - -/* Emits the `vmaddwev.w.hu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_W_HU_H, vd, vj, vk)); -} - -/* Emits the `vmaddwev.d.wu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_D_WU_W, vd, vj, vk)); -} - -/* Emits the `vmaddwev.q.du.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwev_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_Q_DU_D, vd, vj, vk)); -} - -/* Emits the `vmaddwod.h.bu.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_H_BU_B, vd, vj, vk)); -} - -/* Emits the `vmaddwod.w.hu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_W_HU_H, vd, vj, vk)); -} - -/* Emits the `vmaddwod.d.wu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_D_WU_W, vd, vj, vk)); -} - -/* Emits the `vmaddwod.q.du.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmaddwod_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_Q_DU_D, vd, vj, vk)); -} - -/* Emits the `vdiv.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_B, vd, vj, vk)); -} - -/* Emits the `vdiv.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_H, vd, vj, vk)); -} - -/* Emits the `vdiv.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_W, vd, vj, vk)); -} - -/* Emits the `vdiv.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_D, vd, vj, vk)); -} - -/* Emits the `vmod.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_B, vd, vj, vk)); -} - -/* Emits the `vmod.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_H, vd, vj, vk)); -} - -/* Emits the `vmod.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_W, vd, vj, vk)); -} - -/* Emits the `vmod.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_D, vd, vj, vk)); -} - -/* Emits the `vdiv.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_BU, vd, vj, vk)); -} - -/* Emits the `vdiv.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_HU, vd, vj, vk)); -} - -/* Emits the `vdiv.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_WU, vd, vj, vk)); -} - -/* Emits the `vdiv.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vdiv_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_DU, vd, vj, vk)); -} - -/* Emits the `vmod.bu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_BU, vd, vj, vk)); -} - -/* Emits the `vmod.hu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_HU, vd, vj, vk)); -} - -/* Emits the `vmod.wu vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_WU, vd, vj, vk)); -} - -/* Emits the `vmod.du vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmod_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_DU, vd, vj, vk)); -} - /* Emits the `vsll.b vd, vj, vk` instruction. */ static void __attribute__((unused)) tcg_out_opc_vsll_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) @@ -4187,566 +2945,6 @@ tcg_out_opc_vrotr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) tcg_out32(s, encode_vdvjvk_insn(OPC_VROTR_D, vd, vj, vk)); } -/* Emits the `vsrlr.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_B, vd, vj, vk)); -} - -/* Emits the `vsrlr.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_H, vd, vj, vk)); -} - -/* Emits the `vsrlr.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_W, vd, vj, vk)); -} - -/* Emits the `vsrlr.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_D, vd, vj, vk)); -} - -/* Emits the `vsrar.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrar_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_B, vd, vj, vk)); -} - -/* Emits the `vsrar.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrar_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_H, vd, vj, vk)); -} - -/* Emits the `vsrar.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrar_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_W, vd, vj, vk)); -} - -/* Emits the `vsrar.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrar_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_D, vd, vj, vk)); -} - -/* Emits the `vsrln.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrln_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLN_B_H, vd, vj, vk)); -} - -/* Emits the `vsrln.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrln_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLN_H_W, vd, vj, vk)); -} - -/* Emits the `vsrln.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrln_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLN_W_D, vd, vj, vk)); -} - -/* Emits the `vsran.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsran_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAN_B_H, vd, vj, vk)); -} - -/* Emits the `vsran.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsran_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAN_H_W, vd, vj, vk)); -} - -/* Emits the `vsran.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsran_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAN_W_D, vd, vj, vk)); -} - -/* Emits the `vsrlrn.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLRN_B_H, vd, vj, vk)); -} - -/* Emits the `vsrlrn.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLRN_H_W, vd, vj, vk)); -} - -/* Emits the `vsrlrn.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLRN_W_D, vd, vj, vk)); -} - -/* Emits the `vsrarn.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRARN_B_H, vd, vj, vk)); -} - -/* Emits the `vsrarn.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRARN_H_W, vd, vj, vk)); -} - -/* Emits the `vsrarn.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSRARN_W_D, vd, vj, vk)); -} - -/* Emits the `vssrln.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrln_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_B_H, vd, vj, vk)); -} - -/* Emits the `vssrln.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrln_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_H_W, vd, vj, vk)); -} - -/* Emits the `vssrln.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrln_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_W_D, vd, vj, vk)); -} - -/* Emits the `vssran.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssran_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_B_H, vd, vj, vk)); -} - -/* Emits the `vssran.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssran_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_H_W, vd, vj, vk)); -} - -/* Emits the `vssran.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssran_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_W_D, vd, vj, vk)); -} - -/* Emits the `vssrlrn.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_B_H, vd, vj, vk)); -} - -/* Emits the `vssrlrn.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_H_W, vd, vj, vk)); -} - -/* Emits the `vssrlrn.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_W_D, vd, vj, vk)); -} - -/* Emits the `vssrarn.b.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_B_H, vd, vj, vk)); -} - -/* Emits the `vssrarn.h.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_H_W, vd, vj, vk)); -} - -/* Emits the `vssrarn.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_W_D, vd, vj, vk)); -} - -/* Emits the `vssrln.bu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrln_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_BU_H, vd, vj, vk)); -} - -/* Emits the `vssrln.hu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrln_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_HU_W, vd, vj, vk)); -} - -/* Emits the `vssrln.wu.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrln_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_WU_D, vd, vj, vk)); -} - -/* Emits the `vssran.bu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssran_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_BU_H, vd, vj, vk)); -} - -/* Emits the `vssran.hu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssran_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_HU_W, vd, vj, vk)); -} - -/* Emits the `vssran.wu.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssran_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_WU_D, vd, vj, vk)); -} - -/* Emits the `vssrlrn.bu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrn_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_BU_H, vd, vj, vk)); -} - -/* Emits the `vssrlrn.hu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrn_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_HU_W, vd, vj, vk)); -} - -/* Emits the `vssrlrn.wu.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrn_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_WU_D, vd, vj, vk)); -} - -/* Emits the `vssrarn.bu.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarn_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_BU_H, vd, vj, vk)); -} - -/* Emits the `vssrarn.hu.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarn_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_HU_W, vd, vj, vk)); -} - -/* Emits the `vssrarn.wu.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarn_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_WU_D, vd, vj, vk)); -} - -/* Emits the `vbitclr.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitclr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_B, vd, vj, vk)); -} - -/* Emits the `vbitclr.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitclr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_H, vd, vj, vk)); -} - -/* Emits the `vbitclr.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitclr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_W, vd, vj, vk)); -} - -/* Emits the `vbitclr.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitclr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_D, vd, vj, vk)); -} - -/* Emits the `vbitset.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitset_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_B, vd, vj, vk)); -} - -/* Emits the `vbitset.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitset_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_H, vd, vj, vk)); -} - -/* Emits the `vbitset.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitset_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_W, vd, vj, vk)); -} - -/* Emits the `vbitset.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitset_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_D, vd, vj, vk)); -} - -/* Emits the `vbitrev.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitrev_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_B, vd, vj, vk)); -} - -/* Emits the `vbitrev.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitrev_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_H, vd, vj, vk)); -} - -/* Emits the `vbitrev.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitrev_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_W, vd, vj, vk)); -} - -/* Emits the `vbitrev.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbitrev_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_D, vd, vj, vk)); -} - -/* Emits the `vpackev.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackev_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_B, vd, vj, vk)); -} - -/* Emits the `vpackev.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackev_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_H, vd, vj, vk)); -} - -/* Emits the `vpackev.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackev_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_W, vd, vj, vk)); -} - -/* Emits the `vpackev.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackev_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_D, vd, vj, vk)); -} - -/* Emits the `vpackod.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackod_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_B, vd, vj, vk)); -} - -/* Emits the `vpackod.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackod_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_H, vd, vj, vk)); -} - -/* Emits the `vpackod.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackod_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_W, vd, vj, vk)); -} - -/* Emits the `vpackod.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpackod_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_D, vd, vj, vk)); -} - -/* Emits the `vilvl.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvl_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_B, vd, vj, vk)); -} - -/* Emits the `vilvl.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvl_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_H, vd, vj, vk)); -} - -/* Emits the `vilvl.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvl_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_W, vd, vj, vk)); -} - -/* Emits the `vilvl.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvl_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_D, vd, vj, vk)); -} - -/* Emits the `vilvh.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvh_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_B, vd, vj, vk)); -} - -/* Emits the `vilvh.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvh_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_H, vd, vj, vk)); -} - -/* Emits the `vilvh.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvh_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_W, vd, vj, vk)); -} - -/* Emits the `vilvh.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vilvh_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_D, vd, vj, vk)); -} - -/* Emits the `vpickev.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickev_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_B, vd, vj, vk)); -} - -/* Emits the `vpickev.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickev_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_H, vd, vj, vk)); -} - -/* Emits the `vpickev.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickev_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_W, vd, vj, vk)); -} - -/* Emits the `vpickev.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickev_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_D, vd, vj, vk)); -} - -/* Emits the `vpickod.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickod_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_B, vd, vj, vk)); -} - -/* Emits the `vpickod.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickod_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_H, vd, vj, vk)); -} - -/* Emits the `vpickod.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickod_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_W, vd, vj, vk)); -} - -/* Emits the `vpickod.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpickod_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_D, vd, vj, vk)); -} - /* Emits the `vreplve.b vd, vj, k` instruction. */ static void __attribute__((unused)) tcg_out_opc_vreplve_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) @@ -4817,251 +3015,6 @@ tcg_out_opc_vorn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) tcg_out32(s, encode_vdvjvk_insn(OPC_VORN_V, vd, vj, vk)); } -/* Emits the `vfrstp.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrstp_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFRSTP_B, vd, vj, vk)); -} - -/* Emits the `vfrstp.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrstp_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFRSTP_H, vd, vj, vk)); -} - -/* Emits the `vadd.q vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_q(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_Q, vd, vj, vk)); -} - -/* Emits the `vsub.q vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_q(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_Q, vd, vj, vk)); -} - -/* Emits the `vsigncov.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsigncov_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_B, vd, vj, vk)); -} - -/* Emits the `vsigncov.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsigncov_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_H, vd, vj, vk)); -} - -/* Emits the `vsigncov.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsigncov_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_W, vd, vj, vk)); -} - -/* Emits the `vsigncov.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsigncov_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_D, vd, vj, vk)); -} - -/* Emits the `vfadd.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfadd_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFADD_S, vd, vj, vk)); -} - -/* Emits the `vfadd.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFADD_D, vd, vj, vk)); -} - -/* Emits the `vfsub.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfsub_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFSUB_S, vd, vj, vk)); -} - -/* Emits the `vfsub.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFSUB_D, vd, vj, vk)); -} - -/* Emits the `vfmul.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmul_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMUL_S, vd, vj, vk)); -} - -/* Emits the `vfmul.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmul_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMUL_D, vd, vj, vk)); -} - -/* Emits the `vfdiv.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfdiv_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFDIV_S, vd, vj, vk)); -} - -/* Emits the `vfdiv.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfdiv_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFDIV_D, vd, vj, vk)); -} - -/* Emits the `vfmax.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmax_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAX_S, vd, vj, vk)); -} - -/* Emits the `vfmax.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmax_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAX_D, vd, vj, vk)); -} - -/* Emits the `vfmin.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmin_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMIN_S, vd, vj, vk)); -} - -/* Emits the `vfmin.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmin_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMIN_D, vd, vj, vk)); -} - -/* Emits the `vfmaxa.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmaxa_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAXA_S, vd, vj, vk)); -} - -/* Emits the `vfmaxa.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmaxa_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAXA_D, vd, vj, vk)); -} - -/* Emits the `vfmina.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmina_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMINA_S, vd, vj, vk)); -} - -/* Emits the `vfmina.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfmina_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFMINA_D, vd, vj, vk)); -} - -/* Emits the `vfcvt.h.s vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcvt_h_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCVT_H_S, vd, vj, vk)); -} - -/* Emits the `vfcvt.s.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcvt_s_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFCVT_S_D, vd, vj, vk)); -} - -/* Emits the `vffint.s.l vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffint_s_l(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFFINT_S_L, vd, vj, vk)); -} - -/* Emits the `vftint.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftint_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINT_W_D, vd, vj, vk)); -} - -/* Emits the `vftintrm.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrm_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRM_W_D, vd, vj, vk)); -} - -/* Emits the `vftintrp.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrp_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRP_W_D, vd, vj, vk)); -} - -/* Emits the `vftintrz.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrz_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRZ_W_D, vd, vj, vk)); -} - -/* Emits the `vftintrne.w.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrne_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRNE_W_D, vd, vj, vk)); -} - -/* Emits the `vshuf.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSHUF_H, vd, vj, vk)); -} - -/* Emits the `vshuf.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSHUF_W, vd, vj, vk)); -} - -/* Emits the `vshuf.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) -{ - tcg_out32(s, encode_vdvjvk_insn(OPC_VSHUF_D, vd, vj, vk)); -} - /* Emits the `vseqi.b vd, vj, sk5` instruction. */ static void __attribute__((unused)) tcg_out_opc_vseqi_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) @@ -5258,20 +3211,6 @@ tcg_out_opc_vsubi_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) tcg_out32(s, encode_vdvjuk5_insn(OPC_VSUBI_DU, vd, vj, uk5)); } -/* Emits the `vbsll.v vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbsll_v(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VBSLL_V, vd, vj, uk5)); -} - -/* Emits the `vbsrl.v vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vbsrl_v(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VBSRL_V, vd, vj, uk5)); -} - /* Emits the `vmaxi.b vd, vj, sk5` instruction. */ static void __attribute__((unused)) tcg_out_opc_vmaxi_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) @@ -5384,104 +3323,6 @@ tcg_out_opc_vmini_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) tcg_out32(s, encode_vdvjuk5_insn(OPC_VMINI_DU, vd, vj, uk5)); } -/* Emits the `vfrstpi.b vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrstpi_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VFRSTPI_B, vd, vj, uk5)); -} - -/* Emits the `vfrstpi.h vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrstpi_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VFRSTPI_H, vd, vj, uk5)); -} - -/* Emits the `vclo.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclo_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLO_B, vd, vj)); -} - -/* Emits the `vclo.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclo_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLO_H, vd, vj)); -} - -/* Emits the `vclo.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclo_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLO_W, vd, vj)); -} - -/* Emits the `vclo.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclo_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLO_D, vd, vj)); -} - -/* Emits the `vclz.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclz_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_B, vd, vj)); -} - -/* Emits the `vclz.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclz_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_H, vd, vj)); -} - -/* Emits the `vclz.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclz_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_W, vd, vj)); -} - -/* Emits the `vclz.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vclz_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_D, vd, vj)); -} - -/* Emits the `vpcnt.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpcnt_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_B, vd, vj)); -} - -/* Emits the `vpcnt.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpcnt_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_H, vd, vj)); -} - -/* Emits the `vpcnt.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpcnt_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_W, vd, vj)); -} - -/* Emits the `vpcnt.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vpcnt_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_D, vd, vj)); -} - /* Emits the `vneg.b vd, vj` instruction. */ static void __attribute__((unused)) tcg_out_opc_vneg_b(TCGContext *s, TCGReg vd, TCGReg vj) @@ -5510,552 +3351,6 @@ tcg_out_opc_vneg_d(TCGContext *s, TCGReg vd, TCGReg vj) tcg_out32(s, encode_vdvj_insn(OPC_VNEG_D, vd, vj)); } -/* Emits the `vmskltz.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmskltz_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_B, vd, vj)); -} - -/* Emits the `vmskltz.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmskltz_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_H, vd, vj)); -} - -/* Emits the `vmskltz.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmskltz_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_W, vd, vj)); -} - -/* Emits the `vmskltz.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmskltz_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_D, vd, vj)); -} - -/* Emits the `vmskgez.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmskgez_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VMSKGEZ_B, vd, vj)); -} - -/* Emits the `vmsknz.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmsknz_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VMSKNZ_B, vd, vj)); -} - -/* Emits the `vseteqz.v cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseteqz_v(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETEQZ_V, cd, vj)); -} - -/* Emits the `vsetnez.v cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetnez_v(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETNEZ_V, cd, vj)); -} - -/* Emits the `vsetanyeqz.b cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetanyeqz_b(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_B, cd, vj)); -} - -/* Emits the `vsetanyeqz.h cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetanyeqz_h(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_H, cd, vj)); -} - -/* Emits the `vsetanyeqz.w cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetanyeqz_w(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_W, cd, vj)); -} - -/* Emits the `vsetanyeqz.d cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetanyeqz_d(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_D, cd, vj)); -} - -/* Emits the `vsetallnez.b cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetallnez_b(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_B, cd, vj)); -} - -/* Emits the `vsetallnez.h cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetallnez_h(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_H, cd, vj)); -} - -/* Emits the `vsetallnez.w cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetallnez_w(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_W, cd, vj)); -} - -/* Emits the `vsetallnez.d cd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsetallnez_d(TCGContext *s, TCGReg cd, TCGReg vj) -{ - tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_D, cd, vj)); -} - -/* Emits the `vflogb.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vflogb_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFLOGB_S, vd, vj)); -} - -/* Emits the `vflogb.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vflogb_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFLOGB_D, vd, vj)); -} - -/* Emits the `vfclass.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfclass_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFCLASS_S, vd, vj)); -} - -/* Emits the `vfclass.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfclass_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFCLASS_D, vd, vj)); -} - -/* Emits the `vfsqrt.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfsqrt_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFSQRT_S, vd, vj)); -} - -/* Emits the `vfsqrt.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfsqrt_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFSQRT_D, vd, vj)); -} - -/* Emits the `vfrecip.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrecip_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRECIP_S, vd, vj)); -} - -/* Emits the `vfrecip.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrecip_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRECIP_D, vd, vj)); -} - -/* Emits the `vfrsqrt.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrsqrt_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRSQRT_S, vd, vj)); -} - -/* Emits the `vfrsqrt.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrsqrt_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRSQRT_D, vd, vj)); -} - -/* Emits the `vfrint.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrint_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINT_S, vd, vj)); -} - -/* Emits the `vfrint.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrint_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINT_D, vd, vj)); -} - -/* Emits the `vfrintrm.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrm_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRM_S, vd, vj)); -} - -/* Emits the `vfrintrm.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrm_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRM_D, vd, vj)); -} - -/* Emits the `vfrintrp.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrp_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRP_S, vd, vj)); -} - -/* Emits the `vfrintrp.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrp_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRP_D, vd, vj)); -} - -/* Emits the `vfrintrz.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrz_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRZ_S, vd, vj)); -} - -/* Emits the `vfrintrz.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrz_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRZ_D, vd, vj)); -} - -/* Emits the `vfrintrne.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrne_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRNE_S, vd, vj)); -} - -/* Emits the `vfrintrne.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfrintrne_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRNE_D, vd, vj)); -} - -/* Emits the `vfcvtl.s.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcvtl_s_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFCVTL_S_H, vd, vj)); -} - -/* Emits the `vfcvth.s.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcvth_s_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFCVTH_S_H, vd, vj)); -} - -/* Emits the `vfcvtl.d.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcvtl_d_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFCVTL_D_S, vd, vj)); -} - -/* Emits the `vfcvth.d.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vfcvth_d_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFCVTH_D_S, vd, vj)); -} - -/* Emits the `vffint.s.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffint_s_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_S_W, vd, vj)); -} - -/* Emits the `vffint.s.wu vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffint_s_wu(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_S_WU, vd, vj)); -} - -/* Emits the `vffint.d.l vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffint_d_l(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_D_L, vd, vj)); -} - -/* Emits the `vffint.d.lu vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffint_d_lu(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_D_LU, vd, vj)); -} - -/* Emits the `vffintl.d.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffintl_d_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFFINTL_D_W, vd, vj)); -} - -/* Emits the `vffinth.d.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vffinth_d_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFFINTH_D_W, vd, vj)); -} - -/* Emits the `vftint.w.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftint_w_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_W_S, vd, vj)); -} - -/* Emits the `vftint.l.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftint_l_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_L_D, vd, vj)); -} - -/* Emits the `vftintrm.w.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrm_w_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRM_W_S, vd, vj)); -} - -/* Emits the `vftintrm.l.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrm_l_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRM_L_D, vd, vj)); -} - -/* Emits the `vftintrp.w.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrp_w_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRP_W_S, vd, vj)); -} - -/* Emits the `vftintrp.l.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrp_l_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRP_L_D, vd, vj)); -} - -/* Emits the `vftintrz.w.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrz_w_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_W_S, vd, vj)); -} - -/* Emits the `vftintrz.l.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrz_l_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_L_D, vd, vj)); -} - -/* Emits the `vftintrne.w.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrne_w_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNE_W_S, vd, vj)); -} - -/* Emits the `vftintrne.l.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrne_l_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNE_L_D, vd, vj)); -} - -/* Emits the `vftint.wu.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftint_wu_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_WU_S, vd, vj)); -} - -/* Emits the `vftint.lu.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftint_lu_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_LU_D, vd, vj)); -} - -/* Emits the `vftintrz.wu.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrz_wu_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_WU_S, vd, vj)); -} - -/* Emits the `vftintrz.lu.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrz_lu_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_LU_D, vd, vj)); -} - -/* Emits the `vftintl.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintl_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTL_L_S, vd, vj)); -} - -/* Emits the `vftinth.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftinth_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTH_L_S, vd, vj)); -} - -/* Emits the `vftintrml.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrml_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRML_L_S, vd, vj)); -} - -/* Emits the `vftintrmh.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrmh_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRMH_L_S, vd, vj)); -} - -/* Emits the `vftintrpl.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrpl_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRPL_L_S, vd, vj)); -} - -/* Emits the `vftintrph.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrph_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRPH_L_S, vd, vj)); -} - -/* Emits the `vftintrzl.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrzl_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZL_L_S, vd, vj)); -} - -/* Emits the `vftintrzh.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrzh_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZH_L_S, vd, vj)); -} - -/* Emits the `vftintrnel.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrnel_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNEL_L_S, vd, vj)); -} - -/* Emits the `vftintrneh.l.s vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vftintrneh_l_s(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNEH_L_S, vd, vj)); -} - -/* Emits the `vexth.h.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_h_b(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_H_B, vd, vj)); -} - -/* Emits the `vexth.w.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_w_h(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_W_H, vd, vj)); -} - -/* Emits the `vexth.d.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_d_w(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_D_W, vd, vj)); -} - -/* Emits the `vexth.q.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_q_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_Q_D, vd, vj)); -} - -/* Emits the `vexth.hu.bu vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_HU_BU, vd, vj)); -} - -/* Emits the `vexth.wu.hu vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_WU_HU, vd, vj)); -} - -/* Emits the `vexth.du.wu vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_du_wu(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_DU_WU, vd, vj)); -} - -/* Emits the `vexth.qu.du vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vexth_qu_du(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_QU_DU, vd, vj)); -} - /* Emits the `vreplgr2vr.b vd, j` instruction. */ static void __attribute__((unused)) tcg_out_opc_vreplgr2vr_b(TCGContext *s, TCGReg vd, TCGReg j) @@ -6112,62 +3407,6 @@ tcg_out_opc_vrotri_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) tcg_out32(s, encode_vdvjuk6_insn(OPC_VROTRI_D, vd, vj, uk6)); } -/* Emits the `vsrlri.b vd, vj, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlri_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) -{ - tcg_out32(s, encode_vdvjuk3_insn(OPC_VSRLRI_B, vd, vj, uk3)); -} - -/* Emits the `vsrlri.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlri_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLRI_H, vd, vj, uk4)); -} - -/* Emits the `vsrlri.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlri_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLRI_W, vd, vj, uk5)); -} - -/* Emits the `vsrlri.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlri_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLRI_D, vd, vj, uk6)); -} - -/* Emits the `vsrari.b vd, vj, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrari_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) -{ - tcg_out32(s, encode_vdvjuk3_insn(OPC_VSRARI_B, vd, vj, uk3)); -} - -/* Emits the `vsrari.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrari_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRARI_H, vd, vj, uk4)); -} - -/* Emits the `vsrari.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrari_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRARI_W, vd, vj, uk5)); -} - -/* Emits the `vsrari.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrari_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRARI_D, vd, vj, uk6)); -} - /* Emits the `vinsgr2vr.b vd, j, uk4` instruction. */ static void __attribute__((unused)) tcg_out_opc_vinsgr2vr_b(TCGContext *s, TCGReg vd, TCGReg j, uint32_t uk4) @@ -6280,62 +3519,6 @@ tcg_out_opc_vreplvei_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk1) tcg_out32(s, encode_vdvjuk1_insn(OPC_VREPLVEI_D, vd, vj, uk1)); } -/* Emits the `vsllwil.h.b vd, vj, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsllwil_h_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) -{ - tcg_out32(s, encode_vdvjuk3_insn(OPC_VSLLWIL_H_B, vd, vj, uk3)); -} - -/* Emits the `vsllwil.w.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsllwil_w_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSLLWIL_W_H, vd, vj, uk4)); -} - -/* Emits the `vsllwil.d.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsllwil_d_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLLWIL_D_W, vd, vj, uk5)); -} - -/* Emits the `vextl.q.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vextl_q_d(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTL_Q_D, vd, vj)); -} - -/* Emits the `vsllwil.hu.bu vd, vj, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsllwil_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) -{ - tcg_out32(s, encode_vdvjuk3_insn(OPC_VSLLWIL_HU_BU, vd, vj, uk3)); -} - -/* Emits the `vsllwil.wu.hu vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsllwil_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSLLWIL_WU_HU, vd, vj, uk4)); -} - -/* Emits the `vsllwil.du.wu vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsllwil_du_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLLWIL_DU_WU, vd, vj, uk5)); -} - -/* Emits the `vextl.qu.du vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vextl_qu_du(TCGContext *s, TCGReg vd, TCGReg vj) -{ - tcg_out32(s, encode_vdvj_insn(OPC_VEXTL_QU_DU, vd, vj)); -} - /* Emits the `vbitclri.b vd, vj, uk3` instruction. */ static void __attribute__((unused)) tcg_out_opc_vbitclri_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) @@ -6420,62 +3603,6 @@ tcg_out_opc_vbitrevi_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) tcg_out32(s, encode_vdvjuk6_insn(OPC_VBITREVI_D, vd, vj, uk6)); } -/* Emits the `vsat.b vd, vj, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) -{ - tcg_out32(s, encode_vdvjuk3_insn(OPC_VSAT_B, vd, vj, uk3)); -} - -/* Emits the `vsat.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSAT_H, vd, vj, uk4)); -} - -/* Emits the `vsat.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSAT_W, vd, vj, uk5)); -} - -/* Emits the `vsat.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSAT_D, vd, vj, uk6)); -} - -/* Emits the `vsat.bu vd, vj, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) -{ - tcg_out32(s, encode_vdvjuk3_insn(OPC_VSAT_BU, vd, vj, uk3)); -} - -/* Emits the `vsat.hu vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSAT_HU, vd, vj, uk4)); -} - -/* Emits the `vsat.wu vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSAT_WU, vd, vj, uk5)); -} - -/* Emits the `vsat.du vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsat_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSAT_DU, vd, vj, uk6)); -} - /* Emits the `vslli.b vd, vj, uk3` instruction. */ static void __attribute__((unused)) tcg_out_opc_vslli_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) @@ -6560,398 +3687,6 @@ tcg_out_opc_vsrai_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRAI_D, vd, vj, uk6)); } -/* Emits the `vsrlni.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLNI_B_H, vd, vj, uk4)); -} - -/* Emits the `vsrlni.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLNI_H_W, vd, vj, uk5)); -} - -/* Emits the `vsrlni.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLNI_W_D, vd, vj, uk6)); -} - -/* Emits the `vsrlni.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRLNI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vsrlrni.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLRNI_B_H, vd, vj, uk4)); -} - -/* Emits the `vsrlrni.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLRNI_H_W, vd, vj, uk5)); -} - -/* Emits the `vsrlrni.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLRNI_W_D, vd, vj, uk6)); -} - -/* Emits the `vsrlrni.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrlrni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRLRNI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vssrlni.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLNI_B_H, vd, vj, uk4)); -} - -/* Emits the `vssrlni.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLNI_H_W, vd, vj, uk5)); -} - -/* Emits the `vssrlni.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLNI_W_D, vd, vj, uk6)); -} - -/* Emits the `vssrlni.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLNI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vssrlni.bu.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLNI_BU_H, vd, vj, uk4)); -} - -/* Emits the `vssrlni.hu.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLNI_HU_W, vd, vj, uk5)); -} - -/* Emits the `vssrlni.wu.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLNI_WU_D, vd, vj, uk6)); -} - -/* Emits the `vssrlni.du.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlni_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLNI_DU_Q, vd, vj, uk7)); -} - -/* Emits the `vssrlrni.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLRNI_B_H, vd, vj, uk4)); -} - -/* Emits the `vssrlrni.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLRNI_H_W, vd, vj, uk5)); -} - -/* Emits the `vssrlrni.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLRNI_W_D, vd, vj, uk6)); -} - -/* Emits the `vssrlrni.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLRNI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vssrlrni.bu.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLRNI_BU_H, vd, vj, uk4)); -} - -/* Emits the `vssrlrni.hu.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLRNI_HU_W, vd, vj, uk5)); -} - -/* Emits the `vssrlrni.wu.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLRNI_WU_D, vd, vj, uk6)); -} - -/* Emits the `vssrlrni.du.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrlrni_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLRNI_DU_Q, vd, vj, uk7)); -} - -/* Emits the `vsrani.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrani_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRANI_B_H, vd, vj, uk4)); -} - -/* Emits the `vsrani.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrani_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRANI_H_W, vd, vj, uk5)); -} - -/* Emits the `vsrani.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrani_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRANI_W_D, vd, vj, uk6)); -} - -/* Emits the `vsrani.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrani_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRANI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vsrarni.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRARNI_B_H, vd, vj, uk4)); -} - -/* Emits the `vsrarni.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRARNI_H_W, vd, vj, uk5)); -} - -/* Emits the `vsrarni.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRARNI_W_D, vd, vj, uk6)); -} - -/* Emits the `vsrarni.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrarni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRARNI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vssrani.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRANI_B_H, vd, vj, uk4)); -} - -/* Emits the `vssrani.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRANI_H_W, vd, vj, uk5)); -} - -/* Emits the `vssrani.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRANI_W_D, vd, vj, uk6)); -} - -/* Emits the `vssrani.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRANI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vssrani.bu.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRANI_BU_H, vd, vj, uk4)); -} - -/* Emits the `vssrani.hu.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRANI_HU_W, vd, vj, uk5)); -} - -/* Emits the `vssrani.wu.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRANI_WU_D, vd, vj, uk6)); -} - -/* Emits the `vssrani.du.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrani_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRANI_DU_Q, vd, vj, uk7)); -} - -/* Emits the `vssrarni.b.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRARNI_B_H, vd, vj, uk4)); -} - -/* Emits the `vssrarni.h.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRARNI_H_W, vd, vj, uk5)); -} - -/* Emits the `vssrarni.w.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRARNI_W_D, vd, vj, uk6)); -} - -/* Emits the `vssrarni.d.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRARNI_D_Q, vd, vj, uk7)); -} - -/* Emits the `vssrarni.bu.h vd, vj, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) -{ - tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRARNI_BU_H, vd, vj, uk4)); -} - -/* Emits the `vssrarni.hu.w vd, vj, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) -{ - tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRARNI_HU_W, vd, vj, uk5)); -} - -/* Emits the `vssrarni.wu.d vd, vj, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) -{ - tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRARNI_WU_D, vd, vj, uk6)); -} - -/* Emits the `vssrarni.du.q vd, vj, uk7` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vssrarni_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) -{ - tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRARNI_DU_Q, vd, vj, uk7)); -} - -/* Emits the `vextrins.d vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vextrins_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_D, vd, vj, uk8)); -} - -/* Emits the `vextrins.w vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vextrins_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_W, vd, vj, uk8)); -} - -/* Emits the `vextrins.h vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vextrins_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_H, vd, vj, uk8)); -} - -/* Emits the `vextrins.b vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vextrins_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_B, vd, vj, uk8)); -} - -/* Emits the `vshuf4i.b vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf4i_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_B, vd, vj, uk8)); -} - -/* Emits the `vshuf4i.h vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf4i_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_H, vd, vj, uk8)); -} - -/* Emits the `vshuf4i.w vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf4i_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_W, vd, vj, uk8)); -} - -/* Emits the `vshuf4i.d vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vshuf4i_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) -{ - tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_D, vd, vj, uk8)); -} - /* Emits the `vbitseli.b vd, vj, uk8` instruction. */ static void __attribute__((unused)) tcg_out_opc_vbitseli_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) @@ -6994,11 +3729,1341 @@ tcg_out_opc_vldi(TCGContext *s, TCGReg vd, int32_t sj13) tcg_out32(s, encode_vdsj13_insn(OPC_VLDI, vd, sj13)); } -/* Emits the `vpermi.w vd, vj, uk8` instruction. */ +/* Emits the `xvseq.b xd, xj, xk` instruction. */ static void __attribute__((unused)) -tcg_out_opc_vpermi_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +tcg_out_opc_xvseq_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) { - tcg_out32(s, encode_vdvjuk8_insn(OPC_VPERMI_W, vd, vj, uk8)); + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_B, xd, xj, xk)); +} + +/* Emits the `xvseq.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_H, xd, xj, xk)); +} + +/* Emits the `xvseq.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_W, xd, xj, xk)); +} + +/* Emits the `xvseq.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_D, xd, xj, xk)); +} + +/* Emits the `xvsle.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_B, xd, xj, xk)); +} + +/* Emits the `xvsle.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_H, xd, xj, xk)); +} + +/* Emits the `xvsle.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_W, xd, xj, xk)); +} + +/* Emits the `xvsle.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_D, xd, xj, xk)); +} + +/* Emits the `xvsle.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_BU, xd, xj, xk)); +} + +/* Emits the `xvsle.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_HU, xd, xj, xk)); +} + +/* Emits the `xvsle.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_WU, xd, xj, xk)); +} + +/* Emits the `xvsle.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_DU, xd, xj, xk)); +} + +/* Emits the `xvslt.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_B, xd, xj, xk)); +} + +/* Emits the `xvslt.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_H, xd, xj, xk)); +} + +/* Emits the `xvslt.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_W, xd, xj, xk)); +} + +/* Emits the `xvslt.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_D, xd, xj, xk)); +} + +/* Emits the `xvslt.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_BU, xd, xj, xk)); +} + +/* Emits the `xvslt.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_HU, xd, xj, xk)); +} + +/* Emits the `xvslt.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_WU, xd, xj, xk)); +} + +/* Emits the `xvslt.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_DU, xd, xj, xk)); +} + +/* Emits the `xvadd.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_B, xd, xj, xk)); +} + +/* Emits the `xvadd.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_H, xd, xj, xk)); +} + +/* Emits the `xvadd.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_W, xd, xj, xk)); +} + +/* Emits the `xvadd.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_D, xd, xj, xk)); +} + +/* Emits the `xvsub.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_B, xd, xj, xk)); +} + +/* Emits the `xvsub.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_H, xd, xj, xk)); +} + +/* Emits the `xvsub.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_W, xd, xj, xk)); +} + +/* Emits the `xvsub.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_D, xd, xj, xk)); +} + +/* Emits the `xvsadd.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_B, xd, xj, xk)); +} + +/* Emits the `xvsadd.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_H, xd, xj, xk)); +} + +/* Emits the `xvsadd.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_W, xd, xj, xk)); +} + +/* Emits the `xvsadd.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_D, xd, xj, xk)); +} + +/* Emits the `xvssub.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_B, xd, xj, xk)); +} + +/* Emits the `xvssub.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_H, xd, xj, xk)); +} + +/* Emits the `xvssub.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_W, xd, xj, xk)); +} + +/* Emits the `xvssub.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_D, xd, xj, xk)); +} + +/* Emits the `xvsadd.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_BU, xd, xj, xk)); +} + +/* Emits the `xvsadd.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_HU, xd, xj, xk)); +} + +/* Emits the `xvsadd.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_WU, xd, xj, xk)); +} + +/* Emits the `xvsadd.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_DU, xd, xj, xk)); +} + +/* Emits the `xvssub.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_BU, xd, xj, xk)); +} + +/* Emits the `xvssub.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_HU, xd, xj, xk)); +} + +/* Emits the `xvssub.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_WU, xd, xj, xk)); +} + +/* Emits the `xvssub.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_DU, xd, xj, xk)); +} + +/* Emits the `xvmax.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_B, xd, xj, xk)); +} + +/* Emits the `xvmax.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_H, xd, xj, xk)); +} + +/* Emits the `xvmax.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_W, xd, xj, xk)); +} + +/* Emits the `xvmax.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_D, xd, xj, xk)); +} + +/* Emits the `xvmin.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_B, xd, xj, xk)); +} + +/* Emits the `xvmin.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_H, xd, xj, xk)); +} + +/* Emits the `xvmin.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_W, xd, xj, xk)); +} + +/* Emits the `xvmin.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_D, xd, xj, xk)); +} + +/* Emits the `xvmax.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_BU, xd, xj, xk)); +} + +/* Emits the `xvmax.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_HU, xd, xj, xk)); +} + +/* Emits the `xvmax.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_WU, xd, xj, xk)); +} + +/* Emits the `xvmax.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_DU, xd, xj, xk)); +} + +/* Emits the `xvmin.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_BU, xd, xj, xk)); +} + +/* Emits the `xvmin.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_HU, xd, xj, xk)); +} + +/* Emits the `xvmin.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_WU, xd, xj, xk)); +} + +/* Emits the `xvmin.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_DU, xd, xj, xk)); +} + +/* Emits the `xvmul.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_B, xd, xj, xk)); +} + +/* Emits the `xvmul.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_H, xd, xj, xk)); +} + +/* Emits the `xvmul.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_W, xd, xj, xk)); +} + +/* Emits the `xvmul.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_D, xd, xj, xk)); +} + +/* Emits the `xvsll.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_B, xd, xj, xk)); +} + +/* Emits the `xvsll.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_H, xd, xj, xk)); +} + +/* Emits the `xvsll.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_W, xd, xj, xk)); +} + +/* Emits the `xvsll.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_D, xd, xj, xk)); +} + +/* Emits the `xvsrl.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_B, xd, xj, xk)); +} + +/* Emits the `xvsrl.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_H, xd, xj, xk)); +} + +/* Emits the `xvsrl.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_W, xd, xj, xk)); +} + +/* Emits the `xvsrl.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_D, xd, xj, xk)); +} + +/* Emits the `xvsra.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_B, xd, xj, xk)); +} + +/* Emits the `xvsra.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_H, xd, xj, xk)); +} + +/* Emits the `xvsra.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_W, xd, xj, xk)); +} + +/* Emits the `xvsra.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_D, xd, xj, xk)); +} + +/* Emits the `xvrotr.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_B, xd, xj, xk)); +} + +/* Emits the `xvrotr.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_H, xd, xj, xk)); +} + +/* Emits the `xvrotr.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_W, xd, xj, xk)); +} + +/* Emits the `xvrotr.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_D, xd, xj, xk)); +} + +/* Emits the `xvreplve.b xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_B, xd, xj, k)); +} + +/* Emits the `xvreplve.h xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_H, xd, xj, k)); +} + +/* Emits the `xvreplve.w xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_W, xd, xj, k)); +} + +/* Emits the `xvreplve.d xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_D, xd, xj, k)); +} + +/* Emits the `xvand.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvand_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVAND_V, xd, xj, xk)); +} + +/* Emits the `xvor.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVOR_V, xd, xj, xk)); +} + +/* Emits the `xvxor.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvxor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVXOR_V, xd, xj, xk)); +} + +/* Emits the `xvnor.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvnor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVNOR_V, xd, xj, xk)); +} + +/* Emits the `xvandn.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvandn_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVANDN_V, xd, xj, xk)); +} + +/* Emits the `xvorn.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvorn_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVORN_V, xd, xj, xk)); +} + +/* Emits the `xvseqi.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_B, xd, xj, sk5)); +} + +/* Emits the `xvseqi.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_H, xd, xj, sk5)); +} + +/* Emits the `xvseqi.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_W, xd, xj, sk5)); +} + +/* Emits the `xvseqi.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_D, xd, xj, sk5)); +} + +/* Emits the `xvslei.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_B, xd, xj, sk5)); +} + +/* Emits the `xvslei.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_H, xd, xj, sk5)); +} + +/* Emits the `xvslei.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_W, xd, xj, sk5)); +} + +/* Emits the `xvslei.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_D, xd, xj, sk5)); +} + +/* Emits the `xvslei.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_BU, xd, xj, uk5)); +} + +/* Emits the `xvslei.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_HU, xd, xj, uk5)); +} + +/* Emits the `xvslei.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_WU, xd, xj, uk5)); +} + +/* Emits the `xvslei.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_DU, xd, xj, uk5)); +} + +/* Emits the `xvslti.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_B, xd, xj, sk5)); +} + +/* Emits the `xvslti.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_H, xd, xj, sk5)); +} + +/* Emits the `xvslti.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_W, xd, xj, sk5)); +} + +/* Emits the `xvslti.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_D, xd, xj, sk5)); +} + +/* Emits the `xvslti.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_BU, xd, xj, uk5)); +} + +/* Emits the `xvslti.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_HU, xd, xj, uk5)); +} + +/* Emits the `xvslti.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_WU, xd, xj, uk5)); +} + +/* Emits the `xvslti.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_DU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_BU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_HU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_WU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_DU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_BU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_HU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_WU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_DU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_B, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_H, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_W, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_D, xd, xj, sk5)); +} + +/* Emits the `xvmini.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_B, xd, xj, sk5)); +} + +/* Emits the `xvmini.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_H, xd, xj, sk5)); +} + +/* Emits the `xvmini.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_W, xd, xj, sk5)); +} + +/* Emits the `xvmini.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_D, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_BU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_HU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_WU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_DU, xd, xj, uk5)); +} + +/* Emits the `xvmini.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_BU, xd, xj, uk5)); +} + +/* Emits the `xvmini.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_HU, xd, xj, uk5)); +} + +/* Emits the `xvmini.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_WU, xd, xj, uk5)); +} + +/* Emits the `xvmini.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_DU, xd, xj, uk5)); +} + +/* Emits the `xvneg.b xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_b(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_B, xd, xj)); +} + +/* Emits the `xvneg.h xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_h(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_H, xd, xj)); +} + +/* Emits the `xvneg.w xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_w(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_W, xd, xj)); +} + +/* Emits the `xvneg.d xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_d(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_D, xd, xj)); +} + +/* Emits the `xvreplgr2vr.b xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_b(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_B, xd, j)); +} + +/* Emits the `xvreplgr2vr.h xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_h(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_H, xd, j)); +} + +/* Emits the `xvreplgr2vr.w xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_w(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_W, xd, j)); +} + +/* Emits the `xvreplgr2vr.d xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_d(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_D, xd, j)); +} + +/* Emits the `xvrotri.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVROTRI_B, xd, xj, uk3)); +} + +/* Emits the `xvrotri.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVROTRI_H, xd, xj, uk4)); +} + +/* Emits the `xvrotri.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVROTRI_W, xd, xj, uk5)); +} + +/* Emits the `xvrotri.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVROTRI_D, xd, xj, uk6)); +} + +/* Emits the `xvinsgr2vr.w xd, j, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvinsgr2vr_w(TCGContext *s, TCGReg xd, TCGReg j, uint32_t uk3) +{ + tcg_out32(s, encode_xdjuk3_insn(OPC_XVINSGR2VR_W, xd, j, uk3)); +} + +/* Emits the `xvinsgr2vr.d xd, j, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvinsgr2vr_d(TCGContext *s, TCGReg xd, TCGReg j, uint32_t uk2) +{ + tcg_out32(s, encode_xdjuk2_insn(OPC_XVINSGR2VR_D, xd, j, uk2)); +} + +/* Emits the `xvpickve2gr.w d, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_w(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_dxjuk3_insn(OPC_XVPICKVE2GR_W, d, xj, uk3)); +} + +/* Emits the `xvpickve2gr.d d, xj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_d(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk2) +{ + tcg_out32(s, encode_dxjuk2_insn(OPC_XVPICKVE2GR_D, d, xj, uk2)); +} + +/* Emits the `xvpickve2gr.wu d, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_wu(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_dxjuk3_insn(OPC_XVPICKVE2GR_WU, d, xj, uk3)); +} + +/* Emits the `xvpickve2gr.du d, xj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_du(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk2) +{ + tcg_out32(s, encode_dxjuk2_insn(OPC_XVPICKVE2GR_DU, d, xj, uk2)); +} + +/* Emits the `xvrepl128vei.b xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVREPL128VEI_B, xd, xj, uk4)); +} + +/* Emits the `xvrepl128vei.h xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVREPL128VEI_H, xd, xj, uk3)); +} + +/* Emits the `xvrepl128vei.w xd, xj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk2) +{ + tcg_out32(s, encode_xdxjuk2_insn(OPC_XVREPL128VEI_W, xd, xj, uk2)); +} + +/* Emits the `xvrepl128vei.d xd, xj, uk1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk1) +{ + tcg_out32(s, encode_xdxjuk1_insn(OPC_XVREPL128VEI_D, xd, xj, uk1)); +} + +/* Emits the `xvreplve0.b xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_b(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_B, xd, xj)); +} + +/* Emits the `xvreplve0.h xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_h(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_H, xd, xj)); +} + +/* Emits the `xvreplve0.w xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_w(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_W, xd, xj)); +} + +/* Emits the `xvreplve0.d xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_d(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_D, xd, xj)); +} + +/* Emits the `xvreplve0.q xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_q(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_Q, xd, xj)); +} + +/* Emits the `xvbitclri.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITCLRI_B, xd, xj, uk3)); +} + +/* Emits the `xvbitclri.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITCLRI_H, xd, xj, uk4)); +} + +/* Emits the `xvbitclri.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITCLRI_W, xd, xj, uk5)); +} + +/* Emits the `xvbitclri.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITCLRI_D, xd, xj, uk6)); +} + +/* Emits the `xvbitseti.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITSETI_B, xd, xj, uk3)); +} + +/* Emits the `xvbitseti.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITSETI_H, xd, xj, uk4)); +} + +/* Emits the `xvbitseti.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITSETI_W, xd, xj, uk5)); +} + +/* Emits the `xvbitseti.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITSETI_D, xd, xj, uk6)); +} + +/* Emits the `xvbitrevi.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITREVI_B, xd, xj, uk3)); +} + +/* Emits the `xvbitrevi.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITREVI_H, xd, xj, uk4)); +} + +/* Emits the `xvbitrevi.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITREVI_W, xd, xj, uk5)); +} + +/* Emits the `xvbitrevi.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITREVI_D, xd, xj, uk6)); +} + +/* Emits the `xvslli.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSLLI_B, xd, xj, uk3)); +} + +/* Emits the `xvslli.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSLLI_H, xd, xj, uk4)); +} + +/* Emits the `xvslli.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLLI_W, xd, xj, uk5)); +} + +/* Emits the `xvslli.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSLLI_D, xd, xj, uk6)); +} + +/* Emits the `xvsrli.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSRLI_B, xd, xj, uk3)); +} + +/* Emits the `xvsrli.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSRLI_H, xd, xj, uk4)); +} + +/* Emits the `xvsrli.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSRLI_W, xd, xj, uk5)); +} + +/* Emits the `xvsrli.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSRLI_D, xd, xj, uk6)); +} + +/* Emits the `xvsrai.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSRAI_B, xd, xj, uk3)); +} + +/* Emits the `xvsrai.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSRAI_H, xd, xj, uk4)); +} + +/* Emits the `xvsrai.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSRAI_W, xd, xj, uk5)); +} + +/* Emits the `xvsrai.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSRAI_D, xd, xj, uk6)); +} + +/* Emits the `xvbitseli.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVBITSELI_B, xd, xj, uk8)); +} + +/* Emits the `xvandi.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvandi_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVANDI_B, xd, xj, uk8)); +} + +/* Emits the `xvori.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVORI_B, xd, xj, uk8)); +} + +/* Emits the `xvxori.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvxori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVXORI_B, xd, xj, uk8)); +} + +/* Emits the `xvnori.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvnori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVNORI_B, xd, xj, uk8)); +} + +/* Emits the `xvldi xd, sj13` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, int32_t sj13) +{ + tcg_out32(s, encode_xdsj13_insn(OPC_XVLDI, xd, sj13)); } /* End of generated code. */ From 3e261310410948fc9c44d2df9f759dac293d9fd6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 16:24:59 +0000 Subject: [PATCH 1497/2884] tcg/loongarch64: Use fp load/store for I32 and I64 into vector regs Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 36 +++++++++----------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 06ca1ab11c..b9078ac793 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -803,6 +803,12 @@ static void tcg_out_ldst(TCGContext *s, LoongArchInsn opc, TCGReg data, case OPC_ST_D: tcg_out32(s, encode_djsk12_insn(opc, data, addr, imm12)); break; + case OPC_FLD_S: + case OPC_FLD_D: + case OPC_FST_S: + case OPC_FST_D: + tcg_out32(s, encode_fdjsk12_insn(opc, data, addr, imm12)); + break; default: g_assert_not_reached(); } @@ -816,14 +822,14 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg dest, if (dest < TCG_REG_V0) { tcg_out_ldst(s, OPC_LD_W, dest, base, offset); } else { - tcg_out_dupm_vec(s, TCG_TYPE_I128, MO_32, dest, base, offset); + tcg_out_ldst(s, OPC_FLD_S, dest, base, offset); } break; case TCG_TYPE_I64: if (dest < TCG_REG_V0) { tcg_out_ldst(s, OPC_LD_D, dest, base, offset); } else { - tcg_out_dupm_vec(s, TCG_TYPE_I128, MO_64, dest, base, offset); + tcg_out_ldst(s, OPC_FLD_D, dest, base, offset); } break; case TCG_TYPE_V128: @@ -847,36 +853,14 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg src, if (src < TCG_REG_V0) { tcg_out_ldst(s, OPC_ST_W, src, base, offset); } else { - /* TODO: Could use fst_s, fstx_s */ - if (offset < -0x100 || offset > 0xff || (offset & 3)) { - if (-0x800 <= offset && offset <= 0x7ff) { - tcg_out_opc_addi_d(s, TCG_REG_TMP0, base, offset); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); - tcg_out_opc_add_d(s, TCG_REG_TMP0, TCG_REG_TMP0, base); - } - base = TCG_REG_TMP0; - offset = 0; - } - tcg_out_opc_vstelm_w(s, src, base, offset, 0); + tcg_out_ldst(s, OPC_FST_S, src, base, offset); } break; case TCG_TYPE_I64: if (src < TCG_REG_V0) { tcg_out_ldst(s, OPC_ST_D, src, base, offset); } else { - /* TODO: Could use fst_d, fstx_d */ - if (offset < -0x100 || offset > 0xff || (offset & 7)) { - if (-0x800 <= offset && offset <= 0x7ff) { - tcg_out_opc_addi_d(s, TCG_REG_TMP0, base, offset); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); - tcg_out_opc_add_d(s, TCG_REG_TMP0, TCG_REG_TMP0, base); - } - base = TCG_REG_TMP0; - offset = 0; - } - tcg_out_opc_vstelm_d(s, src, base, offset, 0); + tcg_out_ldst(s, OPC_FST_D, src, base, offset); } break; case TCG_TYPE_V128: From 3a7a53c3526771d20443dbe2cc44c7d768b074bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 16:53:01 +0000 Subject: [PATCH 1498/2884] tcg/loongarch64: Handle i32 and i64 moves between gr and fr Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index b9078ac793..de5369536e 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -303,11 +303,23 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) switch (type) { case TCG_TYPE_I32: case TCG_TYPE_I64: - /* - * Conventional register-register move used in LoongArch is - * `or dst, src, zero`. - */ - tcg_out_opc_or(s, ret, arg, TCG_REG_ZERO); + if (ret < TCG_REG_V0) { + if (arg < TCG_REG_V0) { + /* + * Conventional register-register move used in LoongArch is + * `or dst, src, zero`. + */ + tcg_out_opc_or(s, ret, arg, TCG_REG_ZERO); + } else { + tcg_out_opc_movfr2gr_d(s, ret, arg); + } + } else { + if (arg < TCG_REG_V0) { + tcg_out_opc_movgr2fr_d(s, ret, arg); + } else { + tcg_out_opc_fmov_d(s, ret, arg); + } + } break; case TCG_TYPE_V128: tcg_out_opc_vori_b(s, ret, arg, 0); From 1c05d53baf1366e1f34a055a7f4ab91c11dc211e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 17:08:46 +0000 Subject: [PATCH 1499/2884] tcg/loongarch64: Support TCG_TYPE_V64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can implement this with fld_d, fst_d for load and store, and then use the normal v128 operations in registers. This will improve support for guests which use v64. Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 8 ++++++-- tcg/loongarch64/tcg-target.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index de5369536e..980ea10211 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -321,6 +321,7 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) } } break; + case TCG_TYPE_V64: case TCG_TYPE_V128: tcg_out_opc_vori_b(s, ret, arg, 0); break; @@ -838,6 +839,7 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg dest, } break; case TCG_TYPE_I64: + case TCG_TYPE_V64: if (dest < TCG_REG_V0) { tcg_out_ldst(s, OPC_LD_D, dest, base, offset); } else { @@ -869,6 +871,7 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg src, } break; case TCG_TYPE_I64: + case TCG_TYPE_V64: if (src < TCG_REG_V0) { tcg_out_ldst(s, OPC_ST_D, src, base, offset); } else { @@ -1880,8 +1883,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, a2 = args[2]; a3 = args[3]; - /* Currently only supports V128 */ - tcg_debug_assert(type == TCG_TYPE_V128); + /* Currently only supports V64 & V128 */ + tcg_debug_assert(type == TCG_TYPE_V64 || type == TCG_TYPE_V128); switch (opc) { case INDEX_op_st_vec: @@ -2394,6 +2397,7 @@ static void tcg_target_init(TCGContext *s) tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S9); if (cpuinfo & CPUINFO_LSX) { + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V24); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V25); diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 29e4860d20..990bad1d51 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -171,7 +171,7 @@ typedef enum { #define TCG_TARGET_HAS_tst 0 -#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_LSX) #define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) #define TCG_TARGET_HAS_v256 0 From 9d779187b86eee59899111978c6c1bd56076b1ed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 17:18:20 +0000 Subject: [PATCH 1500/2884] util/loongarch64: Detect LASX vector support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- host/include/loongarch64/host/cpuinfo.h | 1 + util/cpuinfo-loongarch.c | 1 + 2 files changed, 2 insertions(+) diff --git a/host/include/loongarch64/host/cpuinfo.h b/host/include/loongarch64/host/cpuinfo.h index fab664a10b..d7bf27501d 100644 --- a/host/include/loongarch64/host/cpuinfo.h +++ b/host/include/loongarch64/host/cpuinfo.h @@ -8,6 +8,7 @@ #define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ #define CPUINFO_LSX (1u << 1) +#define CPUINFO_LASX (1u << 2) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/util/cpuinfo-loongarch.c b/util/cpuinfo-loongarch.c index 08b6d7460c..bb1f7f698b 100644 --- a/util/cpuinfo-loongarch.c +++ b/util/cpuinfo-loongarch.c @@ -29,6 +29,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info = CPUINFO_ALWAYS; info |= (hwcap & HWCAP_LOONGARCH_LSX ? CPUINFO_LSX : 0); + info |= (hwcap & HWCAP_LOONGARCH_LASX ? CPUINFO_LASX : 0); cpuinfo = info; return info; From 4f222d890936ee2797c9ba16e67348daba9a33cc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 18:45:20 +0000 Subject: [PATCH 1501/2884] tcg/loongarch64: Simplify tcg_out_dup_vec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 980ea10211..b1d652355d 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1674,22 +1674,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg rd, TCGReg rs) { - switch (vece) { - case MO_8: - tcg_out_opc_vreplgr2vr_b(s, rd, rs); - break; - case MO_16: - tcg_out_opc_vreplgr2vr_h(s, rd, rs); - break; - case MO_32: - tcg_out_opc_vreplgr2vr_w(s, rd, rs); - break; - case MO_64: - tcg_out_opc_vreplgr2vr_d(s, rd, rs); - break; - default: - g_assert_not_reached(); - } + static const LoongArchInsn repl_insn[4] = { + OPC_VREPLGR2VR_B, OPC_VREPLGR2VR_H, OPC_VREPLGR2VR_W, OPC_VREPLGR2VR_D + }; + + tcg_debug_assert(vece <= MO_64); + tcg_out32(s, encode_vdj_insn(repl_insn[vece], rd, rs)); return true; } From e78dc00f1dc2caa898262e87324a1c47afb96208 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 18:48:59 +0000 Subject: [PATCH 1502/2884] tcg/loongarch64: Support LASX in tcg_out_dup_vec Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index b1d652355d..cc54bc4a53 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1674,12 +1674,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg rd, TCGReg rs) { - static const LoongArchInsn repl_insn[4] = { - OPC_VREPLGR2VR_B, OPC_VREPLGR2VR_H, OPC_VREPLGR2VR_W, OPC_VREPLGR2VR_D + static const LoongArchInsn repl_insn[2][4] = { + { OPC_VREPLGR2VR_B, OPC_VREPLGR2VR_H, + OPC_VREPLGR2VR_W, OPC_VREPLGR2VR_D }, + { OPC_XVREPLGR2VR_B, OPC_XVREPLGR2VR_H, + OPC_XVREPLGR2VR_W, OPC_XVREPLGR2VR_D }, }; + bool lasx = type == TCG_TYPE_V256; tcg_debug_assert(vece <= MO_64); - tcg_out32(s, encode_vdj_insn(repl_insn[vece], rd, rs)); + tcg_out32(s, encode_vdj_insn(repl_insn[lasx][vece], rd, rs)); return true; } From 15750faa8ecd070cbc01bbd22db8c7a2b7e410e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 19:05:33 +0000 Subject: [PATCH 1503/2884] tcg/loongarch64: Support LASX in tcg_out_dupm_vec Each element size has a different encoding, so code cannot be shared in the same way as with tcg_out_dup_vec. Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index cc54bc4a53..1e721b8b20 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1690,8 +1690,10 @@ static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg r, TCGReg base, intptr_t offset) { - /* Handle imm overflow and division (vldrepl.d imm is divided by 8) */ - if (offset < -0x800 || offset > 0x7ff || \ + bool lasx = type == TCG_TYPE_V256; + + /* Handle imm overflow and division (vldrepl.d imm is divided by 8). */ + if (offset < -0x800 || offset > 0x7ff || (offset & ((1 << vece) - 1)) != 0) { tcg_out_addi(s, TCG_TYPE_I64, TCG_REG_TMP0, base, offset); base = TCG_REG_TMP0; @@ -1701,16 +1703,32 @@ static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, switch (vece) { case MO_8: - tcg_out_opc_vldrepl_b(s, r, base, offset); + if (lasx) { + tcg_out_opc_xvldrepl_b(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_b(s, r, base, offset); + } break; case MO_16: - tcg_out_opc_vldrepl_h(s, r, base, offset); + if (lasx) { + tcg_out_opc_xvldrepl_h(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_h(s, r, base, offset); + } break; case MO_32: - tcg_out_opc_vldrepl_w(s, r, base, offset); + if (lasx) { + tcg_out_opc_xvldrepl_w(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_w(s, r, base, offset); + } break; case MO_64: - tcg_out_opc_vldrepl_d(s, r, base, offset); + if (lasx) { + tcg_out_opc_xvldrepl_d(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_d(s, r, base, offset); + } break; default: g_assert_not_reached(); From 75b5ffdd0d026d77d23e59c18c56803d8e7e0e01 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 19:18:15 +0000 Subject: [PATCH 1504/2884] tcg/loongarch64: Use tcg_out_dup_vec in tcg_out_dupi_vec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 1e721b8b20..9a8f67cf3e 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1749,24 +1749,8 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, /* TODO: vldi patterns when imm 12 is set */ - /* Fallback to vreplgr2vr */ tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, value); - switch (vece) { - case MO_8: - tcg_out_opc_vreplgr2vr_b(s, rd, TCG_REG_TMP0); - break; - case MO_16: - tcg_out_opc_vreplgr2vr_h(s, rd, TCG_REG_TMP0); - break; - case MO_32: - tcg_out_opc_vreplgr2vr_w(s, rd, TCG_REG_TMP0); - break; - case MO_64: - tcg_out_opc_vreplgr2vr_d(s, rd, TCG_REG_TMP0); - break; - default: - g_assert_not_reached(); - } + tcg_out_dup_vec(s, type, vece, rd, TCG_REG_TMP0); } static void tcg_out_addsub_vec(TCGContext *s, unsigned vece, const TCGArg a0, From 825d53f35a1d7fb0c8cab520d0f14688b947f888 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 19:20:44 +0000 Subject: [PATCH 1505/2884] tcg/loongarch64: Support LASX in tcg_out_dupi_vec Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 9a8f67cf3e..c7d0c7839b 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1743,7 +1743,12 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, int64_t value = sextract64(v64, 0, 8 << vece); if (-0x200 <= value && value <= 0x1FF) { uint32_t imm = (vece << 10) | ((uint32_t)v64 & 0x3FF); - tcg_out_opc_vldi(s, rd, imm); + + if (type == TCG_TYPE_V256) { + tcg_out_opc_xvldi(s, rd, imm); + } else { + tcg_out_opc_vldi(s, rd, imm); + } return; } From ce37579571ef0eaa387c306040d02a9994cf5bbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 19:34:29 +0000 Subject: [PATCH 1506/2884] tcg/loongarch64: Simplify tcg_out_addsub_vec Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index c7d0c7839b..47011488dd 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1774,33 +1774,34 @@ static void tcg_out_addsub_vec(TCGContext *s, unsigned vece, const TCGArg a0, static const LoongArchInsn sub_vec_imm_insn[4] = { OPC_VSUBI_BU, OPC_VSUBI_HU, OPC_VSUBI_WU, OPC_VSUBI_DU }; + LoongArchInsn insn; if (a2_is_const) { int64_t value = sextract64(a2, 0, 8 << vece); + if (!is_add) { value = -value; } - - /* Try vaddi/vsubi */ - if (0 <= value && value <= 0x1f) { - tcg_out32(s, encode_vdvjuk5_insn(add_vec_imm_insn[vece], a0, \ - a1, value)); - return; - } else if (-0x1f <= value && value < 0) { - tcg_out32(s, encode_vdvjuk5_insn(sub_vec_imm_insn[vece], a0, \ - a1, -value)); - return; + if (value < 0) { + insn = sub_vec_imm_insn[vece]; + value = -value; + } else { + insn = add_vec_imm_insn[vece]; } - /* constraint TCG_CT_CONST_VADD ensures unreachable */ - g_assert_not_reached(); + /* Constraint TCG_CT_CONST_VADD ensures validity. */ + tcg_debug_assert(0 <= value && value <= 0x1f); + + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); + return; } if (is_add) { - tcg_out32(s, encode_vdvjvk_insn(add_vec_insn[vece], a0, a1, a2)); + insn = add_vec_insn[vece]; } else { - tcg_out32(s, encode_vdvjvk_insn(sub_vec_insn[vece], a0, a1, a2)); + insn = sub_vec_insn[vece]; } + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); } static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, From cbf5a8f150501460d398de491b9697727801eb65 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 19:40:16 +0000 Subject: [PATCH 1507/2884] tcg/loongarch64: Support LASX in tcg_out_addsub_vec Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 36 ++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 47011488dd..652aa261a3 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1758,21 +1758,25 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out_dup_vec(s, type, vece, rd, TCG_REG_TMP0); } -static void tcg_out_addsub_vec(TCGContext *s, unsigned vece, const TCGArg a0, - const TCGArg a1, const TCGArg a2, +static void tcg_out_addsub_vec(TCGContext *s, bool lasx, unsigned vece, + TCGArg a0, TCGArg a1, TCGArg a2, bool a2_is_const, bool is_add) { - static const LoongArchInsn add_vec_insn[4] = { - OPC_VADD_B, OPC_VADD_H, OPC_VADD_W, OPC_VADD_D + static const LoongArchInsn add_vec_insn[2][4] = { + { OPC_VADD_B, OPC_VADD_H, OPC_VADD_W, OPC_VADD_D }, + { OPC_XVADD_B, OPC_XVADD_H, OPC_XVADD_W, OPC_XVADD_D }, }; - static const LoongArchInsn add_vec_imm_insn[4] = { - OPC_VADDI_BU, OPC_VADDI_HU, OPC_VADDI_WU, OPC_VADDI_DU + static const LoongArchInsn add_vec_imm_insn[2][4] = { + { OPC_VADDI_BU, OPC_VADDI_HU, OPC_VADDI_WU, OPC_VADDI_DU }, + { OPC_XVADDI_BU, OPC_XVADDI_HU, OPC_XVADDI_WU, OPC_XVADDI_DU }, }; - static const LoongArchInsn sub_vec_insn[4] = { - OPC_VSUB_B, OPC_VSUB_H, OPC_VSUB_W, OPC_VSUB_D + static const LoongArchInsn sub_vec_insn[2][4] = { + { OPC_VSUB_B, OPC_VSUB_H, OPC_VSUB_W, OPC_VSUB_D }, + { OPC_XVSUB_B, OPC_XVSUB_H, OPC_XVSUB_W, OPC_XVSUB_D }, }; - static const LoongArchInsn sub_vec_imm_insn[4] = { - OPC_VSUBI_BU, OPC_VSUBI_HU, OPC_VSUBI_WU, OPC_VSUBI_DU + static const LoongArchInsn sub_vec_imm_insn[2][4] = { + { OPC_VSUBI_BU, OPC_VSUBI_HU, OPC_VSUBI_WU, OPC_VSUBI_DU }, + { OPC_XVSUBI_BU, OPC_XVSUBI_HU, OPC_XVSUBI_WU, OPC_XVSUBI_DU }, }; LoongArchInsn insn; @@ -1783,10 +1787,10 @@ static void tcg_out_addsub_vec(TCGContext *s, unsigned vece, const TCGArg a0, value = -value; } if (value < 0) { - insn = sub_vec_imm_insn[vece]; + insn = sub_vec_imm_insn[lasx][vece]; value = -value; } else { - insn = add_vec_imm_insn[vece]; + insn = add_vec_imm_insn[lasx][vece]; } /* Constraint TCG_CT_CONST_VADD ensures validity. */ @@ -1797,9 +1801,9 @@ static void tcg_out_addsub_vec(TCGContext *s, unsigned vece, const TCGArg a0, } if (is_add) { - insn = add_vec_insn[vece]; + insn = add_vec_insn[lasx][vece]; } else { - insn = sub_vec_insn[vece]; + insn = sub_vec_insn[lasx][vece]; } tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); } @@ -1963,10 +1967,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } break; case INDEX_op_add_vec: - tcg_out_addsub_vec(s, vece, a0, a1, a2, const_args[2], true); + tcg_out_addsub_vec(s, false, vece, a0, a1, a2, const_args[2], true); break; case INDEX_op_sub_vec: - tcg_out_addsub_vec(s, vece, a0, a1, a2, const_args[2], false); + tcg_out_addsub_vec(s, false, vece, a0, a1, a2, const_args[2], false); break; case INDEX_op_neg_vec: tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], a0, a1)); From 604ba8176c1f7103601c75fc9425475264c2c978 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 19:57:12 +0000 Subject: [PATCH 1508/2884] tcg/loongarch64: Split out vdvjvk in tcg_out_vec_op Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 119 ++++++++++++++++--------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 652aa261a3..8f5f38aa0a 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1900,49 +1900,55 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_ld(s, type, a0, a1, a2); break; case INDEX_op_and_vec: - tcg_out_opc_vand_v(s, a0, a1, a2); - break; + insn = OPC_VAND_V; + goto vdvjvk; case INDEX_op_andc_vec: /* * vandn vd, vj, vk: vd = vk & ~vj * andc_vec vd, vj, vk: vd = vj & ~vk - * vk and vk are swapped + * vj and vk are swapped */ - tcg_out_opc_vandn_v(s, a0, a2, a1); - break; + a1 = a2; + a2 = args[1]; + insn = OPC_VANDN_V; + goto vdvjvk; case INDEX_op_or_vec: - tcg_out_opc_vor_v(s, a0, a1, a2); - break; + insn = OPC_VOR_V; + goto vdvjvk; case INDEX_op_orc_vec: - tcg_out_opc_vorn_v(s, a0, a1, a2); - break; + insn = OPC_VORN_V; + goto vdvjvk; case INDEX_op_xor_vec: - tcg_out_opc_vxor_v(s, a0, a1, a2); - break; - case INDEX_op_nor_vec: - tcg_out_opc_vnor_v(s, a0, a1, a2); - break; + insn = OPC_VXOR_V; + goto vdvjvk; case INDEX_op_not_vec: - tcg_out_opc_vnor_v(s, a0, a1, a1); - break; + a2 = a1; + /* fall through */ + case INDEX_op_nor_vec: + insn = OPC_VNOR_V; + goto vdvjvk; case INDEX_op_cmp_vec: { TCGCond cond = args[3]; + if (const_args[2]) { /* * cmp_vec dest, src, value * Try vseqi/vslei/vslti */ int64_t value = sextract64(a2, 0, 8 << vece); - if ((cond == TCG_COND_EQ || cond == TCG_COND_LE || \ - cond == TCG_COND_LT) && (-0x10 <= value && value <= 0x0f)) { - tcg_out32(s, encode_vdvjsk5_insn(cmp_vec_imm_insn[cond][vece], \ - a0, a1, value)); + if ((cond == TCG_COND_EQ || + cond == TCG_COND_LE || + cond == TCG_COND_LT) && + (-0x10 <= value && value <= 0x0f)) { + insn = cmp_vec_imm_insn[cond][vece]; + tcg_out32(s, encode_vdvjsk5_insn(insn, a0, a1, value)); break; - } else if ((cond == TCG_COND_LEU || cond == TCG_COND_LTU) && - (0x00 <= value && value <= 0x1f)) { - tcg_out32(s, encode_vdvjuk5_insn(cmp_vec_imm_insn[cond][vece], \ - a0, a1, value)); + } else if ((cond == TCG_COND_LEU || + cond == TCG_COND_LTU) && + (0x00 <= value && value <= 0x1f)) { + insn = cmp_vec_imm_insn[cond][vece]; + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); break; } @@ -1963,9 +1969,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, insn = cmp_vec_insn[cond][vece]; tcg_debug_assert(insn != 0); } - tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); } - break; + goto vdvjvk; case INDEX_op_add_vec: tcg_out_addsub_vec(s, false, vece, a0, a1, a2, const_args[2], true); break; @@ -1976,41 +1981,41 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], a0, a1)); break; case INDEX_op_mul_vec: - tcg_out32(s, encode_vdvjvk_insn(mul_vec_insn[vece], a0, a1, a2)); - break; + insn = mul_vec_insn[vece]; + goto vdvjvk; case INDEX_op_smin_vec: - tcg_out32(s, encode_vdvjvk_insn(smin_vec_insn[vece], a0, a1, a2)); - break; + insn = smin_vec_insn[vece]; + goto vdvjvk; case INDEX_op_smax_vec: - tcg_out32(s, encode_vdvjvk_insn(smax_vec_insn[vece], a0, a1, a2)); - break; + insn = smax_vec_insn[vece]; + goto vdvjvk; case INDEX_op_umin_vec: - tcg_out32(s, encode_vdvjvk_insn(umin_vec_insn[vece], a0, a1, a2)); - break; + insn = umin_vec_insn[vece]; + goto vdvjvk; case INDEX_op_umax_vec: - tcg_out32(s, encode_vdvjvk_insn(umax_vec_insn[vece], a0, a1, a2)); - break; + insn = umax_vec_insn[vece]; + goto vdvjvk; case INDEX_op_ssadd_vec: - tcg_out32(s, encode_vdvjvk_insn(ssadd_vec_insn[vece], a0, a1, a2)); - break; + insn = ssadd_vec_insn[vece]; + goto vdvjvk; case INDEX_op_usadd_vec: - tcg_out32(s, encode_vdvjvk_insn(usadd_vec_insn[vece], a0, a1, a2)); - break; + insn = usadd_vec_insn[vece]; + goto vdvjvk; case INDEX_op_sssub_vec: - tcg_out32(s, encode_vdvjvk_insn(sssub_vec_insn[vece], a0, a1, a2)); - break; + insn = sssub_vec_insn[vece]; + goto vdvjvk; case INDEX_op_ussub_vec: - tcg_out32(s, encode_vdvjvk_insn(ussub_vec_insn[vece], a0, a1, a2)); - break; + insn = ussub_vec_insn[vece]; + goto vdvjvk; case INDEX_op_shlv_vec: - tcg_out32(s, encode_vdvjvk_insn(shlv_vec_insn[vece], a0, a1, a2)); - break; + insn = shlv_vec_insn[vece]; + goto vdvjvk; case INDEX_op_shrv_vec: - tcg_out32(s, encode_vdvjvk_insn(shrv_vec_insn[vece], a0, a1, a2)); - break; + insn = shrv_vec_insn[vece]; + goto vdvjvk; case INDEX_op_sarv_vec: - tcg_out32(s, encode_vdvjvk_insn(sarv_vec_insn[vece], a0, a1, a2)); - break; + insn = sarv_vec_insn[vece]; + goto vdvjvk; case INDEX_op_shli_vec: tcg_out32(s, encode_vdvjuk3_insn(shli_vec_insn[vece], a0, a1, a2)); break; @@ -2020,15 +2025,14 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_sari_vec: tcg_out32(s, encode_vdvjuk3_insn(sari_vec_insn[vece], a0, a1, a2)); break; - case INDEX_op_rotrv_vec: - tcg_out32(s, encode_vdvjvk_insn(rotrv_vec_insn[vece], a0, a1, a2)); - break; case INDEX_op_rotlv_vec: /* rotlv_vec a1, a2 = rotrv_vec a1, -a2 */ tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], temp_vec, a2)); - tcg_out32(s, encode_vdvjvk_insn(rotrv_vec_insn[vece], a0, a1, - temp_vec)); - break; + a2 = temp_vec; + /* fall through */ + case INDEX_op_rotrv_vec: + insn = rotrv_vec_insn[vece]; + goto vdvjvk; case INDEX_op_rotli_vec: /* rotli_vec a1, a2 = rotri_vec a1, -a2 */ a2 = extract32(-a2, 0, 3 + vece); @@ -2058,6 +2062,9 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; default: g_assert_not_reached(); + vdvjvk: + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); + break; } } From 571f64f0bf97108f6e52357ad8e15284ead73cbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 20:03:46 +0000 Subject: [PATCH 1509/2884] tcg/loongarch64: Support LASX in tcg_out_{mov,ld,st} Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 8f5f38aa0a..4ead3bedef 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -325,6 +325,9 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) case TCG_TYPE_V128: tcg_out_opc_vori_b(s, ret, arg, 0); break; + case TCG_TYPE_V256: + tcg_out_opc_xvori_b(s, ret, arg, 0); + break; default: g_assert_not_reached(); } @@ -854,6 +857,14 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg dest, tcg_out_opc_vldx(s, dest, base, TCG_REG_TMP0); } break; + case TCG_TYPE_V256: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_xvld(s, dest, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_xvldx(s, dest, base, TCG_REG_TMP0); + } + break; default: g_assert_not_reached(); } @@ -886,6 +897,14 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg src, tcg_out_opc_vstx(s, src, base, TCG_REG_TMP0); } break; + case TCG_TYPE_V256: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_xvst(s, src, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_xvstx(s, src, base, TCG_REG_TMP0); + } + break; default: g_assert_not_reached(); } From 840770183f05b7f04123dba2b32360b5ff04bd21 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 20:09:16 +0000 Subject: [PATCH 1510/2884] tcg/loongarch64: Remove temp_vec from tcg_out_vec_op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use TCG_VEC_TMP0 directly. Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 4ead3bedef..1d9e0bf028 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1834,7 +1834,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, { TCGType type = vecl + TCG_TYPE_V64; TCGArg a0, a1, a2, a3; - TCGReg temp_vec = TCG_VEC_TMP0; static const LoongArchInsn cmp_vec_insn[16][4] = { [TCG_COND_EQ] = {OPC_VSEQ_B, OPC_VSEQ_H, OPC_VSEQ_W, OPC_VSEQ_D}, @@ -1976,8 +1975,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, * dupi_vec temp, a2 * cmp_vec a0, a1, temp, cond */ - tcg_out_dupi_vec(s, type, vece, temp_vec, a2); - a2 = temp_vec; + tcg_out_dupi_vec(s, type, vece, TCG_VEC_TMP0, a2); + a2 = TCG_VEC_TMP0; } insn = cmp_vec_insn[cond][vece]; @@ -2046,8 +2045,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_rotlv_vec: /* rotlv_vec a1, a2 = rotrv_vec a1, -a2 */ - tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], temp_vec, a2)); - a2 = temp_vec; + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], TCG_VEC_TMP0, a2)); + a2 = TCG_VEC_TMP0; /* fall through */ case INDEX_op_rotrv_vec: insn = rotrv_vec_insn[vece]; From 3683354664ad153907d92b5cacdd523d4a34bf61 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 20:39:13 +0000 Subject: [PATCH 1511/2884] tcg/loongarch64: Split out vdvjukN in tcg_out_vec_op Fixes a bug in the immediate shifts, because the exact encoding depends on the element size. Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 58 ++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 1d9e0bf028..ab1b67e028 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1901,6 +1901,9 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, static const LoongArchInsn rotrv_vec_insn[4] = { OPC_VROTR_B, OPC_VROTR_H, OPC_VROTR_W, OPC_VROTR_D }; + static const LoongArchInsn rotri_vec_insn[4] = { + OPC_VROTRI_B, OPC_VROTRI_H, OPC_VROTRI_W, OPC_VROTRI_D + }; a0 = args[0]; a1 = args[1]; @@ -2034,15 +2037,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_sarv_vec: insn = sarv_vec_insn[vece]; goto vdvjvk; - case INDEX_op_shli_vec: - tcg_out32(s, encode_vdvjuk3_insn(shli_vec_insn[vece], a0, a1, a2)); - break; - case INDEX_op_shri_vec: - tcg_out32(s, encode_vdvjuk3_insn(shri_vec_insn[vece], a0, a1, a2)); - break; - case INDEX_op_sari_vec: - tcg_out32(s, encode_vdvjuk3_insn(sari_vec_insn[vece], a0, a1, a2)); - break; case INDEX_op_rotlv_vec: /* rotlv_vec a1, a2 = rotrv_vec a1, -a2 */ tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], TCG_VEC_TMP0, a2)); @@ -2051,26 +2045,20 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_rotrv_vec: insn = rotrv_vec_insn[vece]; goto vdvjvk; + case INDEX_op_shli_vec: + insn = shli_vec_insn[vece]; + goto vdvjukN; + case INDEX_op_shri_vec: + insn = shri_vec_insn[vece]; + goto vdvjukN; + case INDEX_op_sari_vec: + insn = sari_vec_insn[vece]; + goto vdvjukN; case INDEX_op_rotli_vec: /* rotli_vec a1, a2 = rotri_vec a1, -a2 */ a2 = extract32(-a2, 0, 3 + vece); - switch (vece) { - case MO_8: - tcg_out_opc_vrotri_b(s, a0, a1, a2); - break; - case MO_16: - tcg_out_opc_vrotri_h(s, a0, a1, a2); - break; - case MO_32: - tcg_out_opc_vrotri_w(s, a0, a1, a2); - break; - case MO_64: - tcg_out_opc_vrotri_d(s, a0, a1, a2); - break; - default: - g_assert_not_reached(); - } - break; + insn = rotri_vec_insn[vece]; + goto vdvjukN; case INDEX_op_bitsel_vec: /* vbitsel vd, vj, vk, va = bitsel_vec vd, va, vk, vj */ tcg_out_opc_vbitsel_v(s, a0, a3, a2, a1); @@ -2083,6 +2071,24 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, vdvjvk: tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); break; + vdvjukN: + switch (vece) { + case MO_8: + tcg_out32(s, encode_vdvjuk3_insn(insn, a0, a1, a2)); + break; + case MO_16: + tcg_out32(s, encode_vdvjuk4_insn(insn, a0, a1, a2)); + break; + case MO_32: + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, a2)); + break; + case MO_64: + tcg_out32(s, encode_vdvjuk6_insn(insn, a0, a1, a2)); + break; + default: + g_assert_not_reached(); + } + break; } } From 4c2c5744aa7823b6271792e0e12d1ba1de91f89c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 20:44:24 +0000 Subject: [PATCH 1512/2884] tcg/loongarch64: Support LASX in tcg_out_vec_op Reviewed-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 223 +++++++++++++++++++------------ 1 file changed, 137 insertions(+), 86 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index ab1b67e028..dff966c395 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1833,76 +1833,125 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, const int const_args[TCG_MAX_OP_ARGS]) { TCGType type = vecl + TCG_TYPE_V64; + bool lasx = type == TCG_TYPE_V256; TCGArg a0, a1, a2, a3; - - static const LoongArchInsn cmp_vec_insn[16][4] = { - [TCG_COND_EQ] = {OPC_VSEQ_B, OPC_VSEQ_H, OPC_VSEQ_W, OPC_VSEQ_D}, - [TCG_COND_LE] = {OPC_VSLE_B, OPC_VSLE_H, OPC_VSLE_W, OPC_VSLE_D}, - [TCG_COND_LEU] = {OPC_VSLE_BU, OPC_VSLE_HU, OPC_VSLE_WU, OPC_VSLE_DU}, - [TCG_COND_LT] = {OPC_VSLT_B, OPC_VSLT_H, OPC_VSLT_W, OPC_VSLT_D}, - [TCG_COND_LTU] = {OPC_VSLT_BU, OPC_VSLT_HU, OPC_VSLT_WU, OPC_VSLT_DU}, - }; - static const LoongArchInsn cmp_vec_imm_insn[16][4] = { - [TCG_COND_EQ] = {OPC_VSEQI_B, OPC_VSEQI_H, OPC_VSEQI_W, OPC_VSEQI_D}, - [TCG_COND_LE] = {OPC_VSLEI_B, OPC_VSLEI_H, OPC_VSLEI_W, OPC_VSLEI_D}, - [TCG_COND_LEU] = {OPC_VSLEI_BU, OPC_VSLEI_HU, OPC_VSLEI_WU, OPC_VSLEI_DU}, - [TCG_COND_LT] = {OPC_VSLTI_B, OPC_VSLTI_H, OPC_VSLTI_W, OPC_VSLTI_D}, - [TCG_COND_LTU] = {OPC_VSLTI_BU, OPC_VSLTI_HU, OPC_VSLTI_WU, OPC_VSLTI_DU}, - }; LoongArchInsn insn; - static const LoongArchInsn neg_vec_insn[4] = { - OPC_VNEG_B, OPC_VNEG_H, OPC_VNEG_W, OPC_VNEG_D + + static const LoongArchInsn cmp_vec_insn[16][2][4] = { + [TCG_COND_EQ] = { + { OPC_VSEQ_B, OPC_VSEQ_H, OPC_VSEQ_W, OPC_VSEQ_D }, + { OPC_XVSEQ_B, OPC_XVSEQ_H, OPC_XVSEQ_W, OPC_XVSEQ_D }, + }, + [TCG_COND_LE] = { + { OPC_VSLE_B, OPC_VSLE_H, OPC_VSLE_W, OPC_VSLE_D }, + { OPC_XVSLE_B, OPC_XVSLE_H, OPC_XVSLE_W, OPC_XVSLE_D }, + }, + [TCG_COND_LEU] = { + { OPC_VSLE_BU, OPC_VSLE_HU, OPC_VSLE_WU, OPC_VSLE_DU }, + { OPC_XVSLE_BU, OPC_XVSLE_HU, OPC_XVSLE_WU, OPC_XVSLE_DU }, + }, + [TCG_COND_LT] = { + { OPC_VSLT_B, OPC_VSLT_H, OPC_VSLT_W, OPC_VSLT_D }, + { OPC_XVSLT_B, OPC_XVSLT_H, OPC_XVSLT_W, OPC_XVSLT_D }, + }, + [TCG_COND_LTU] = { + { OPC_VSLT_BU, OPC_VSLT_HU, OPC_VSLT_WU, OPC_VSLT_DU }, + { OPC_XVSLT_BU, OPC_XVSLT_HU, OPC_XVSLT_WU, OPC_XVSLT_DU }, + } }; - static const LoongArchInsn mul_vec_insn[4] = { - OPC_VMUL_B, OPC_VMUL_H, OPC_VMUL_W, OPC_VMUL_D + static const LoongArchInsn cmp_vec_imm_insn[16][2][4] = { + [TCG_COND_EQ] = { + { OPC_VSEQI_B, OPC_VSEQI_H, OPC_VSEQI_W, OPC_VSEQI_D }, + { OPC_XVSEQI_B, OPC_XVSEQI_H, OPC_XVSEQI_W, OPC_XVSEQI_D }, + }, + [TCG_COND_LE] = { + { OPC_VSLEI_B, OPC_VSLEI_H, OPC_VSLEI_W, OPC_VSLEI_D }, + { OPC_XVSLEI_B, OPC_XVSLEI_H, OPC_XVSLEI_W, OPC_XVSLEI_D }, + }, + [TCG_COND_LEU] = { + { OPC_VSLEI_BU, OPC_VSLEI_HU, OPC_VSLEI_WU, OPC_VSLEI_DU }, + { OPC_XVSLEI_BU, OPC_XVSLEI_HU, OPC_XVSLEI_WU, OPC_XVSLEI_DU }, + }, + [TCG_COND_LT] = { + { OPC_VSLTI_B, OPC_VSLTI_H, OPC_VSLTI_W, OPC_VSLTI_D }, + { OPC_XVSLTI_B, OPC_XVSLTI_H, OPC_XVSLTI_W, OPC_XVSLTI_D }, + }, + [TCG_COND_LTU] = { + { OPC_VSLTI_BU, OPC_VSLTI_HU, OPC_VSLTI_WU, OPC_VSLTI_DU }, + { OPC_XVSLTI_BU, OPC_XVSLTI_HU, OPC_XVSLTI_WU, OPC_XVSLTI_DU }, + } }; - static const LoongArchInsn smin_vec_insn[4] = { - OPC_VMIN_B, OPC_VMIN_H, OPC_VMIN_W, OPC_VMIN_D + static const LoongArchInsn neg_vec_insn[2][4] = { + { OPC_VNEG_B, OPC_VNEG_H, OPC_VNEG_W, OPC_VNEG_D }, + { OPC_XVNEG_B, OPC_XVNEG_H, OPC_XVNEG_W, OPC_XVNEG_D }, }; - static const LoongArchInsn umin_vec_insn[4] = { - OPC_VMIN_BU, OPC_VMIN_HU, OPC_VMIN_WU, OPC_VMIN_DU + static const LoongArchInsn mul_vec_insn[2][4] = { + { OPC_VMUL_B, OPC_VMUL_H, OPC_VMUL_W, OPC_VMUL_D }, + { OPC_XVMUL_B, OPC_XVMUL_H, OPC_XVMUL_W, OPC_XVMUL_D }, }; - static const LoongArchInsn smax_vec_insn[4] = { - OPC_VMAX_B, OPC_VMAX_H, OPC_VMAX_W, OPC_VMAX_D + static const LoongArchInsn smin_vec_insn[2][4] = { + { OPC_VMIN_B, OPC_VMIN_H, OPC_VMIN_W, OPC_VMIN_D }, + { OPC_XVMIN_B, OPC_XVMIN_H, OPC_XVMIN_W, OPC_XVMIN_D }, }; - static const LoongArchInsn umax_vec_insn[4] = { - OPC_VMAX_BU, OPC_VMAX_HU, OPC_VMAX_WU, OPC_VMAX_DU + static const LoongArchInsn umin_vec_insn[2][4] = { + { OPC_VMIN_BU, OPC_VMIN_HU, OPC_VMIN_WU, OPC_VMIN_DU }, + { OPC_XVMIN_BU, OPC_XVMIN_HU, OPC_XVMIN_WU, OPC_XVMIN_DU }, }; - static const LoongArchInsn ssadd_vec_insn[4] = { - OPC_VSADD_B, OPC_VSADD_H, OPC_VSADD_W, OPC_VSADD_D + static const LoongArchInsn smax_vec_insn[2][4] = { + { OPC_VMAX_B, OPC_VMAX_H, OPC_VMAX_W, OPC_VMAX_D }, + { OPC_XVMAX_B, OPC_XVMAX_H, OPC_XVMAX_W, OPC_XVMAX_D }, }; - static const LoongArchInsn usadd_vec_insn[4] = { - OPC_VSADD_BU, OPC_VSADD_HU, OPC_VSADD_WU, OPC_VSADD_DU + static const LoongArchInsn umax_vec_insn[2][4] = { + { OPC_VMAX_BU, OPC_VMAX_HU, OPC_VMAX_WU, OPC_VMAX_DU }, + { OPC_XVMAX_BU, OPC_XVMAX_HU, OPC_XVMAX_WU, OPC_XVMAX_DU }, }; - static const LoongArchInsn sssub_vec_insn[4] = { - OPC_VSSUB_B, OPC_VSSUB_H, OPC_VSSUB_W, OPC_VSSUB_D + static const LoongArchInsn ssadd_vec_insn[2][4] = { + { OPC_VSADD_B, OPC_VSADD_H, OPC_VSADD_W, OPC_VSADD_D }, + { OPC_XVSADD_B, OPC_XVSADD_H, OPC_XVSADD_W, OPC_XVSADD_D }, }; - static const LoongArchInsn ussub_vec_insn[4] = { - OPC_VSSUB_BU, OPC_VSSUB_HU, OPC_VSSUB_WU, OPC_VSSUB_DU + static const LoongArchInsn usadd_vec_insn[2][4] = { + { OPC_VSADD_BU, OPC_VSADD_HU, OPC_VSADD_WU, OPC_VSADD_DU }, + { OPC_XVSADD_BU, OPC_XVSADD_HU, OPC_XVSADD_WU, OPC_XVSADD_DU }, }; - static const LoongArchInsn shlv_vec_insn[4] = { - OPC_VSLL_B, OPC_VSLL_H, OPC_VSLL_W, OPC_VSLL_D + static const LoongArchInsn sssub_vec_insn[2][4] = { + { OPC_VSSUB_B, OPC_VSSUB_H, OPC_VSSUB_W, OPC_VSSUB_D }, + { OPC_XVSSUB_B, OPC_XVSSUB_H, OPC_XVSSUB_W, OPC_XVSSUB_D }, }; - static const LoongArchInsn shrv_vec_insn[4] = { - OPC_VSRL_B, OPC_VSRL_H, OPC_VSRL_W, OPC_VSRL_D + static const LoongArchInsn ussub_vec_insn[2][4] = { + { OPC_VSSUB_BU, OPC_VSSUB_HU, OPC_VSSUB_WU, OPC_VSSUB_DU }, + { OPC_XVSSUB_BU, OPC_XVSSUB_HU, OPC_XVSSUB_WU, OPC_XVSSUB_DU }, }; - static const LoongArchInsn sarv_vec_insn[4] = { - OPC_VSRA_B, OPC_VSRA_H, OPC_VSRA_W, OPC_VSRA_D + static const LoongArchInsn shlv_vec_insn[2][4] = { + { OPC_VSLL_B, OPC_VSLL_H, OPC_VSLL_W, OPC_VSLL_D }, + { OPC_XVSLL_B, OPC_XVSLL_H, OPC_XVSLL_W, OPC_XVSLL_D }, }; - static const LoongArchInsn shli_vec_insn[4] = { - OPC_VSLLI_B, OPC_VSLLI_H, OPC_VSLLI_W, OPC_VSLLI_D + static const LoongArchInsn shrv_vec_insn[2][4] = { + { OPC_VSRL_B, OPC_VSRL_H, OPC_VSRL_W, OPC_VSRL_D }, + { OPC_XVSRL_B, OPC_XVSRL_H, OPC_XVSRL_W, OPC_XVSRL_D }, }; - static const LoongArchInsn shri_vec_insn[4] = { - OPC_VSRLI_B, OPC_VSRLI_H, OPC_VSRLI_W, OPC_VSRLI_D + static const LoongArchInsn sarv_vec_insn[2][4] = { + { OPC_VSRA_B, OPC_VSRA_H, OPC_VSRA_W, OPC_VSRA_D }, + { OPC_XVSRA_B, OPC_XVSRA_H, OPC_XVSRA_W, OPC_XVSRA_D }, }; - static const LoongArchInsn sari_vec_insn[4] = { - OPC_VSRAI_B, OPC_VSRAI_H, OPC_VSRAI_W, OPC_VSRAI_D + static const LoongArchInsn shli_vec_insn[2][4] = { + { OPC_VSLLI_B, OPC_VSLLI_H, OPC_VSLLI_W, OPC_VSLLI_D }, + { OPC_XVSLLI_B, OPC_XVSLLI_H, OPC_XVSLLI_W, OPC_XVSLLI_D }, }; - static const LoongArchInsn rotrv_vec_insn[4] = { - OPC_VROTR_B, OPC_VROTR_H, OPC_VROTR_W, OPC_VROTR_D + static const LoongArchInsn shri_vec_insn[2][4] = { + { OPC_VSRLI_B, OPC_VSRLI_H, OPC_VSRLI_W, OPC_VSRLI_D }, + { OPC_XVSRLI_B, OPC_XVSRLI_H, OPC_XVSRLI_W, OPC_XVSRLI_D }, }; - static const LoongArchInsn rotri_vec_insn[4] = { - OPC_VROTRI_B, OPC_VROTRI_H, OPC_VROTRI_W, OPC_VROTRI_D + static const LoongArchInsn sari_vec_insn[2][4] = { + { OPC_VSRAI_B, OPC_VSRAI_H, OPC_VSRAI_W, OPC_VSRAI_D }, + { OPC_XVSRAI_B, OPC_XVSRAI_H, OPC_XVSRAI_W, OPC_XVSRAI_D }, + }; + static const LoongArchInsn rotrv_vec_insn[2][4] = { + { OPC_VROTR_B, OPC_VROTR_H, OPC_VROTR_W, OPC_VROTR_D }, + { OPC_XVROTR_B, OPC_XVROTR_H, OPC_XVROTR_W, OPC_XVROTR_D }, + }; + static const LoongArchInsn rotri_vec_insn[2][4] = { + { OPC_VROTRI_B, OPC_VROTRI_H, OPC_VROTRI_W, OPC_VROTRI_D }, + { OPC_XVROTRI_B, OPC_XVROTRI_H, OPC_XVROTRI_W, OPC_XVROTRI_D }, }; a0 = args[0]; @@ -1910,9 +1959,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, a2 = args[2]; a3 = args[3]; - /* Currently only supports V64 & V128 */ - tcg_debug_assert(type == TCG_TYPE_V64 || type == TCG_TYPE_V128); - switch (opc) { case INDEX_op_st_vec: tcg_out_st(s, type, a0, a1, a2); @@ -1921,7 +1967,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_out_ld(s, type, a0, a1, a2); break; case INDEX_op_and_vec: - insn = OPC_VAND_V; + insn = lasx ? OPC_XVAND_V : OPC_VAND_V; goto vdvjvk; case INDEX_op_andc_vec: /* @@ -1931,22 +1977,22 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, */ a1 = a2; a2 = args[1]; - insn = OPC_VANDN_V; + insn = lasx ? OPC_XVANDN_V : OPC_VANDN_V; goto vdvjvk; case INDEX_op_or_vec: - insn = OPC_VOR_V; + insn = lasx ? OPC_XVOR_V : OPC_VOR_V; goto vdvjvk; case INDEX_op_orc_vec: - insn = OPC_VORN_V; + insn = lasx ? OPC_XVORN_V : OPC_VORN_V; goto vdvjvk; case INDEX_op_xor_vec: - insn = OPC_VXOR_V; + insn = lasx ? OPC_XVXOR_V : OPC_VXOR_V; goto vdvjvk; case INDEX_op_not_vec: a2 = a1; /* fall through */ case INDEX_op_nor_vec: - insn = OPC_VNOR_V; + insn = lasx ? OPC_XVNOR_V : OPC_VNOR_V; goto vdvjvk; case INDEX_op_cmp_vec: { @@ -1962,13 +2008,13 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, cond == TCG_COND_LE || cond == TCG_COND_LT) && (-0x10 <= value && value <= 0x0f)) { - insn = cmp_vec_imm_insn[cond][vece]; + insn = cmp_vec_imm_insn[cond][lasx][vece]; tcg_out32(s, encode_vdvjsk5_insn(insn, a0, a1, value)); break; } else if ((cond == TCG_COND_LEU || cond == TCG_COND_LTU) && (0x00 <= value && value <= 0x1f)) { - insn = cmp_vec_imm_insn[cond][vece]; + insn = cmp_vec_imm_insn[cond][lasx][vece]; tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); break; } @@ -1982,86 +2028,91 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, a2 = TCG_VEC_TMP0; } - insn = cmp_vec_insn[cond][vece]; + insn = cmp_vec_insn[cond][lasx][vece]; if (insn == 0) { TCGArg t; t = a1, a1 = a2, a2 = t; cond = tcg_swap_cond(cond); - insn = cmp_vec_insn[cond][vece]; + insn = cmp_vec_insn[cond][lasx][vece]; tcg_debug_assert(insn != 0); } } goto vdvjvk; case INDEX_op_add_vec: - tcg_out_addsub_vec(s, false, vece, a0, a1, a2, const_args[2], true); + tcg_out_addsub_vec(s, lasx, vece, a0, a1, a2, const_args[2], true); break; case INDEX_op_sub_vec: - tcg_out_addsub_vec(s, false, vece, a0, a1, a2, const_args[2], false); + tcg_out_addsub_vec(s, lasx, vece, a0, a1, a2, const_args[2], false); break; case INDEX_op_neg_vec: - tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], a0, a1)); + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[lasx][vece], a0, a1)); break; case INDEX_op_mul_vec: - insn = mul_vec_insn[vece]; + insn = mul_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_smin_vec: - insn = smin_vec_insn[vece]; + insn = smin_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_smax_vec: - insn = smax_vec_insn[vece]; + insn = smax_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_umin_vec: - insn = umin_vec_insn[vece]; + insn = umin_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_umax_vec: - insn = umax_vec_insn[vece]; + insn = umax_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_ssadd_vec: - insn = ssadd_vec_insn[vece]; + insn = ssadd_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_usadd_vec: - insn = usadd_vec_insn[vece]; + insn = usadd_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_sssub_vec: - insn = sssub_vec_insn[vece]; + insn = sssub_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_ussub_vec: - insn = ussub_vec_insn[vece]; + insn = ussub_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_shlv_vec: - insn = shlv_vec_insn[vece]; + insn = shlv_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_shrv_vec: - insn = shrv_vec_insn[vece]; + insn = shrv_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_sarv_vec: - insn = sarv_vec_insn[vece]; + insn = sarv_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_rotlv_vec: /* rotlv_vec a1, a2 = rotrv_vec a1, -a2 */ - tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], TCG_VEC_TMP0, a2)); + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[lasx][vece], + TCG_VEC_TMP0, a2)); a2 = TCG_VEC_TMP0; /* fall through */ case INDEX_op_rotrv_vec: - insn = rotrv_vec_insn[vece]; + insn = rotrv_vec_insn[lasx][vece]; goto vdvjvk; case INDEX_op_shli_vec: - insn = shli_vec_insn[vece]; + insn = shli_vec_insn[lasx][vece]; goto vdvjukN; case INDEX_op_shri_vec: - insn = shri_vec_insn[vece]; + insn = shri_vec_insn[lasx][vece]; goto vdvjukN; case INDEX_op_sari_vec: - insn = sari_vec_insn[vece]; + insn = sari_vec_insn[lasx][vece]; goto vdvjukN; case INDEX_op_rotli_vec: /* rotli_vec a1, a2 = rotri_vec a1, -a2 */ a2 = extract32(-a2, 0, 3 + vece); - insn = rotri_vec_insn[vece]; + insn = rotri_vec_insn[lasx][vece]; goto vdvjukN; case INDEX_op_bitsel_vec: /* vbitsel vd, vj, vk, va = bitsel_vec vd, va, vk, vj */ - tcg_out_opc_vbitsel_v(s, a0, a3, a2, a1); + if (lasx) { + tcg_out_opc_xvbitsel_v(s, a0, a3, a2, a1); + } else { + tcg_out_opc_vbitsel_v(s, a0, a3, a2, a1); + } break; case INDEX_op_dupm_vec: tcg_out_dupm_vec(s, type, vece, a0, a1, a2); From 6b0ca412e16dae52bf1ef2da9436d4d8c81759f2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 27 May 2024 20:53:48 +0000 Subject: [PATCH 1513/2884] tcg/loongarch64: Enable v256 with LASX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Song Gao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 3 +++ tcg/loongarch64/tcg-target.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index dff966c395..1c4dc4decb 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -2487,6 +2487,9 @@ static void tcg_target_init(TCGContext *s) if (cpuinfo & CPUINFO_LSX) { tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + if (cpuinfo & CPUINFO_LASX) { + tcg_target_available_regs[TCG_TYPE_V256] = ALL_VECTOR_REGS; + } tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V24); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V25); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V26); diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 990bad1d51..58bd7d258e 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -173,7 +173,7 @@ typedef enum { #define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_LSX) #define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) -#define TCG_TARGET_HAS_v256 0 +#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_LASX) #define TCG_TARGET_HAS_not_vec 1 #define TCG_TARGET_HAS_neg_vec 1 From 2d32a5d2a06a524c5e7967f918c406ec99418a34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 5 Jun 2024 20:58:37 -0700 Subject: [PATCH 1514/2884] util/bufferiszero: Split out host include files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out host/bufferiszero.h.inc for x86, aarch64 and generic in order to avoid an overlong ifdef ladder. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- host/include/aarch64/host/bufferiszero.c.inc | 76 ++++++++ host/include/generic/host/bufferiszero.c.inc | 10 + host/include/i386/host/bufferiszero.c.inc | 124 ++++++++++++ host/include/x86_64/host/bufferiszero.c.inc | 1 + util/bufferiszero.c | 191 +------------------ 5 files changed, 212 insertions(+), 190 deletions(-) create mode 100644 host/include/aarch64/host/bufferiszero.c.inc create mode 100644 host/include/generic/host/bufferiszero.c.inc create mode 100644 host/include/i386/host/bufferiszero.c.inc create mode 100644 host/include/x86_64/host/bufferiszero.c.inc diff --git a/host/include/aarch64/host/bufferiszero.c.inc b/host/include/aarch64/host/bufferiszero.c.inc new file mode 100644 index 0000000000..947ee7ca1f --- /dev/null +++ b/host/include/aarch64/host/bufferiszero.c.inc @@ -0,0 +1,76 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, aarch64 version. + */ + +#ifdef __ARM_NEON +#include + +/* + * Helper for preventing the compiler from reassociating + * chains of binary vector operations. + */ +#define REASSOC_BARRIER(vec0, vec1) asm("" : "+w"(vec0), "+w"(vec1)) + +static bool buffer_is_zero_simd(const void *buf, size_t len) +{ + uint32x4_t t0, t1, t2, t3; + + /* Align head/tail to 16-byte boundaries. */ + const uint32x4_t *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const uint32x4_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); + + /* Unaligned loads at head/tail. */ + t0 = vld1q_u32(buf) | vld1q_u32(buf + len - 16); + + /* Collect a partial block at tail end. */ + t1 = e[-7] | e[-6]; + t2 = e[-5] | e[-4]; + t3 = e[-3] | e[-2]; + t0 |= e[-1]; + REASSOC_BARRIER(t0, t1); + REASSOC_BARRIER(t2, t3); + t0 |= t1; + t2 |= t3; + REASSOC_BARRIER(t0, t2); + t0 |= t2; + + /* + * Loop over complete 128-byte blocks. + * With the head and tail removed, e - p >= 14, so the loop + * must iterate at least once. + */ + do { + /* + * Reduce via UMAXV. Whatever the actual result, + * it will only be zero if all input bytes are zero. + */ + if (unlikely(vmaxvq_u32(t0) != 0)) { + return false; + } + + t0 = p[0] | p[1]; + t1 = p[2] | p[3]; + t2 = p[4] | p[5]; + t3 = p[6] | p[7]; + REASSOC_BARRIER(t0, t1); + REASSOC_BARRIER(t2, t3); + t0 |= t1; + t2 |= t3; + REASSOC_BARRIER(t0, t2); + t0 |= t2; + p += 8; + } while (p < e - 7); + + return vmaxvq_u32(t0) == 0; +} + +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_is_zero_simd, +}; + +#define best_accel() 1 +#else +# include "host/include/generic/host/bufferiszero.c.inc" +#endif diff --git a/host/include/generic/host/bufferiszero.c.inc b/host/include/generic/host/bufferiszero.c.inc new file mode 100644 index 0000000000..ea0875c24a --- /dev/null +++ b/host/include/generic/host/bufferiszero.c.inc @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, generic version. + */ + +static biz_accel_fn const accel_table[1] = { + buffer_is_zero_int_ge256 +}; + +#define best_accel() 0 diff --git a/host/include/i386/host/bufferiszero.c.inc b/host/include/i386/host/bufferiszero.c.inc new file mode 100644 index 0000000000..3b9605d806 --- /dev/null +++ b/host/include/i386/host/bufferiszero.c.inc @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, x86 version. + */ + +#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) +#include + +/* Helper for preventing the compiler from reassociating + chains of binary vector operations. */ +#define SSE_REASSOC_BARRIER(vec0, vec1) asm("" : "+x"(vec0), "+x"(vec1)) + +/* Note that these vectorized functions may assume len >= 256. */ + +static bool __attribute__((target("sse2"))) +buffer_zero_sse2(const void *buf, size_t len) +{ + /* Unaligned loads at head/tail. */ + __m128i v = *(__m128i_u *)(buf); + __m128i w = *(__m128i_u *)(buf + len - 16); + /* Align head/tail to 16-byte boundaries. */ + const __m128i *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const __m128i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); + __m128i zero = { 0 }; + + /* Collect a partial block at tail end. */ + v |= e[-1]; w |= e[-2]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-3]; w |= e[-4]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-5]; w |= e[-6]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-7]; v |= w; + + /* + * Loop over complete 128-byte blocks. + * With the head and tail removed, e - p >= 14, so the loop + * must iterate at least once. + */ + do { + v = _mm_cmpeq_epi8(v, zero); + if (unlikely(_mm_movemask_epi8(v) != 0xFFFF)) { + return false; + } + v = p[0]; w = p[1]; + SSE_REASSOC_BARRIER(v, w); + v |= p[2]; w |= p[3]; + SSE_REASSOC_BARRIER(v, w); + v |= p[4]; w |= p[5]; + SSE_REASSOC_BARRIER(v, w); + v |= p[6]; w |= p[7]; + SSE_REASSOC_BARRIER(v, w); + v |= w; + p += 8; + } while (p < e - 7); + + return _mm_movemask_epi8(_mm_cmpeq_epi8(v, zero)) == 0xFFFF; +} + +#ifdef CONFIG_AVX2_OPT +static bool __attribute__((target("avx2"))) +buffer_zero_avx2(const void *buf, size_t len) +{ + /* Unaligned loads at head/tail. */ + __m256i v = *(__m256i_u *)(buf); + __m256i w = *(__m256i_u *)(buf + len - 32); + /* Align head/tail to 32-byte boundaries. */ + const __m256i *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32); + const __m256i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32); + __m256i zero = { 0 }; + + /* Collect a partial block at tail end. */ + v |= e[-1]; w |= e[-2]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-3]; w |= e[-4]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-5]; w |= e[-6]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-7]; v |= w; + + /* Loop over complete 256-byte blocks. */ + for (; p < e - 7; p += 8) { + /* PTEST is not profitable here. */ + v = _mm256_cmpeq_epi8(v, zero); + if (unlikely(_mm256_movemask_epi8(v) != 0xFFFFFFFF)) { + return false; + } + v = p[0]; w = p[1]; + SSE_REASSOC_BARRIER(v, w); + v |= p[2]; w |= p[3]; + SSE_REASSOC_BARRIER(v, w); + v |= p[4]; w |= p[5]; + SSE_REASSOC_BARRIER(v, w); + v |= p[6]; w |= p[7]; + SSE_REASSOC_BARRIER(v, w); + v |= w; + } + + return _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, zero)) == 0xFFFFFFFF; +} +#endif /* CONFIG_AVX2_OPT */ + +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_zero_sse2, +#ifdef CONFIG_AVX2_OPT + buffer_zero_avx2, +#endif +}; + +static unsigned best_accel(void) +{ +#ifdef CONFIG_AVX2_OPT + unsigned info = cpuinfo_init(); + if (info & CPUINFO_AVX2) { + return 2; + } +#endif + return 1; +} + +#else +# include "host/include/generic/host/bufferiszero.c.inc" +#endif diff --git a/host/include/x86_64/host/bufferiszero.c.inc b/host/include/x86_64/host/bufferiszero.c.inc new file mode 100644 index 0000000000..1d3f1fd6f5 --- /dev/null +++ b/host/include/x86_64/host/bufferiszero.c.inc @@ -0,0 +1 @@ +#include "host/include/i386/host/bufferiszero.c.inc" diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 11c080e02c..522146dab9 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -81,196 +81,7 @@ static bool buffer_is_zero_int_ge256(const void *buf, size_t len) return t == 0; } -#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) -#include - -/* Helper for preventing the compiler from reassociating - chains of binary vector operations. */ -#define SSE_REASSOC_BARRIER(vec0, vec1) asm("" : "+x"(vec0), "+x"(vec1)) - -/* Note that these vectorized functions may assume len >= 256. */ - -static bool __attribute__((target("sse2"))) -buffer_zero_sse2(const void *buf, size_t len) -{ - /* Unaligned loads at head/tail. */ - __m128i v = *(__m128i_u *)(buf); - __m128i w = *(__m128i_u *)(buf + len - 16); - /* Align head/tail to 16-byte boundaries. */ - const __m128i *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); - const __m128i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); - __m128i zero = { 0 }; - - /* Collect a partial block at tail end. */ - v |= e[-1]; w |= e[-2]; - SSE_REASSOC_BARRIER(v, w); - v |= e[-3]; w |= e[-4]; - SSE_REASSOC_BARRIER(v, w); - v |= e[-5]; w |= e[-6]; - SSE_REASSOC_BARRIER(v, w); - v |= e[-7]; v |= w; - - /* - * Loop over complete 128-byte blocks. - * With the head and tail removed, e - p >= 14, so the loop - * must iterate at least once. - */ - do { - v = _mm_cmpeq_epi8(v, zero); - if (unlikely(_mm_movemask_epi8(v) != 0xFFFF)) { - return false; - } - v = p[0]; w = p[1]; - SSE_REASSOC_BARRIER(v, w); - v |= p[2]; w |= p[3]; - SSE_REASSOC_BARRIER(v, w); - v |= p[4]; w |= p[5]; - SSE_REASSOC_BARRIER(v, w); - v |= p[6]; w |= p[7]; - SSE_REASSOC_BARRIER(v, w); - v |= w; - p += 8; - } while (p < e - 7); - - return _mm_movemask_epi8(_mm_cmpeq_epi8(v, zero)) == 0xFFFF; -} - -#ifdef CONFIG_AVX2_OPT -static bool __attribute__((target("avx2"))) -buffer_zero_avx2(const void *buf, size_t len) -{ - /* Unaligned loads at head/tail. */ - __m256i v = *(__m256i_u *)(buf); - __m256i w = *(__m256i_u *)(buf + len - 32); - /* Align head/tail to 32-byte boundaries. */ - const __m256i *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32); - const __m256i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32); - __m256i zero = { 0 }; - - /* Collect a partial block at tail end. */ - v |= e[-1]; w |= e[-2]; - SSE_REASSOC_BARRIER(v, w); - v |= e[-3]; w |= e[-4]; - SSE_REASSOC_BARRIER(v, w); - v |= e[-5]; w |= e[-6]; - SSE_REASSOC_BARRIER(v, w); - v |= e[-7]; v |= w; - - /* Loop over complete 256-byte blocks. */ - for (; p < e - 7; p += 8) { - /* PTEST is not profitable here. */ - v = _mm256_cmpeq_epi8(v, zero); - if (unlikely(_mm256_movemask_epi8(v) != 0xFFFFFFFF)) { - return false; - } - v = p[0]; w = p[1]; - SSE_REASSOC_BARRIER(v, w); - v |= p[2]; w |= p[3]; - SSE_REASSOC_BARRIER(v, w); - v |= p[4]; w |= p[5]; - SSE_REASSOC_BARRIER(v, w); - v |= p[6]; w |= p[7]; - SSE_REASSOC_BARRIER(v, w); - v |= w; - } - - return _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, zero)) == 0xFFFFFFFF; -} -#endif /* CONFIG_AVX2_OPT */ - -static biz_accel_fn const accel_table[] = { - buffer_is_zero_int_ge256, - buffer_zero_sse2, -#ifdef CONFIG_AVX2_OPT - buffer_zero_avx2, -#endif -}; - -static unsigned best_accel(void) -{ -#ifdef CONFIG_AVX2_OPT - unsigned info = cpuinfo_init(); - - if (info & CPUINFO_AVX2) { - return 2; - } -#endif - return 1; -} - -#elif defined(__aarch64__) && defined(__ARM_NEON) -#include - -/* - * Helper for preventing the compiler from reassociating - * chains of binary vector operations. - */ -#define REASSOC_BARRIER(vec0, vec1) asm("" : "+w"(vec0), "+w"(vec1)) - -static bool buffer_is_zero_simd(const void *buf, size_t len) -{ - uint32x4_t t0, t1, t2, t3; - - /* Align head/tail to 16-byte boundaries. */ - const uint32x4_t *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); - const uint32x4_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); - - /* Unaligned loads at head/tail. */ - t0 = vld1q_u32(buf) | vld1q_u32(buf + len - 16); - - /* Collect a partial block at tail end. */ - t1 = e[-7] | e[-6]; - t2 = e[-5] | e[-4]; - t3 = e[-3] | e[-2]; - t0 |= e[-1]; - REASSOC_BARRIER(t0, t1); - REASSOC_BARRIER(t2, t3); - t0 |= t1; - t2 |= t3; - REASSOC_BARRIER(t0, t2); - t0 |= t2; - - /* - * Loop over complete 128-byte blocks. - * With the head and tail removed, e - p >= 14, so the loop - * must iterate at least once. - */ - do { - /* - * Reduce via UMAXV. Whatever the actual result, - * it will only be zero if all input bytes are zero. - */ - if (unlikely(vmaxvq_u32(t0) != 0)) { - return false; - } - - t0 = p[0] | p[1]; - t1 = p[2] | p[3]; - t2 = p[4] | p[5]; - t3 = p[6] | p[7]; - REASSOC_BARRIER(t0, t1); - REASSOC_BARRIER(t2, t3); - t0 |= t1; - t2 |= t3; - REASSOC_BARRIER(t0, t2); - t0 |= t2; - p += 8; - } while (p < e - 7); - - return vmaxvq_u32(t0) == 0; -} - -#define best_accel() 1 -static biz_accel_fn const accel_table[] = { - buffer_is_zero_int_ge256, - buffer_is_zero_simd, -}; -#else -#define best_accel() 0 -static biz_accel_fn const accel_table[1] = { - buffer_is_zero_int_ge256 -}; -#endif +#include "host/bufferiszero.c.inc" static biz_accel_fn buffer_is_zero_accel; static unsigned accel_index; From a96a4987385c2c0477bebffdc9a2d5ceabd43141 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Jun 2024 23:54:52 +0000 Subject: [PATCH 1515/2884] util/bufferiszero: Add loongarch64 vector acceleration Use inline assembly because no release compiler allows per-function selection of the ISA. Tested-by: Bibo Mao Signed-off-by: Richard Henderson --- .../loongarch64/host/bufferiszero.c.inc | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 host/include/loongarch64/host/bufferiszero.c.inc diff --git a/host/include/loongarch64/host/bufferiszero.c.inc b/host/include/loongarch64/host/bufferiszero.c.inc new file mode 100644 index 0000000000..69891eac80 --- /dev/null +++ b/host/include/loongarch64/host/bufferiszero.c.inc @@ -0,0 +1,143 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, loongarch64 version. + */ + +/* + * Builtins for LSX and LASX are introduced by gcc 14 and llvm 18, + * but as yet neither has support for attribute target, so neither + * is able to enable the optimization without globally enabling + * vector support. Since we want runtime detection, use assembly. + */ + +static bool buffer_is_zero_lsx(const void *buf, size_t len) +{ + const void *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const void *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16) - (7 * 16); + const void *l = buf + len; + bool ret; + + asm("vld $vr0,%2,0\n\t" /* first: buf + 0 */ + "vld $vr1,%4,-16\n\t" /* last: buf + len - 16 */ + "vld $vr2,%3,0\n\t" /* e[0] */ + "vld $vr3,%3,16\n\t" /* e[1] */ + "vld $vr4,%3,32\n\t" /* e[2] */ + "vld $vr5,%3,48\n\t" /* e[3] */ + "vld $vr6,%3,64\n\t" /* e[4] */ + "vld $vr7,%3,80\n\t" /* e[5] */ + "vld $vr8,%3,96\n\t" /* e[6] */ + "vor.v $vr0,$vr0,$vr1\n\t" + "vor.v $vr2,$vr2,$vr3\n\t" + "vor.v $vr4,$vr4,$vr5\n\t" + "vor.v $vr6,$vr6,$vr7\n\t" + "vor.v $vr0,$vr0,$vr2\n\t" + "vor.v $vr4,$vr4,$vr6\n\t" + "vor.v $vr0,$vr0,$vr4\n\t" + "vor.v $vr0,$vr0,$vr8\n\t" + "or %0,$r0,$r0\n" /* prepare return false */ + "1:\n\t" + "vsetnez.v $fcc0,$vr0\n\t" + "bcnez $fcc0,2f\n\t" + "vld $vr0,%1,0\n\t" /* p[0] */ + "vld $vr1,%1,16\n\t" /* p[1] */ + "vld $vr2,%1,32\n\t" /* p[2] */ + "vld $vr3,%1,48\n\t" /* p[3] */ + "vld $vr4,%1,64\n\t" /* p[4] */ + "vld $vr5,%1,80\n\t" /* p[5] */ + "vld $vr6,%1,96\n\t" /* p[6] */ + "vld $vr7,%1,112\n\t" /* p[7] */ + "addi.d %1,%1,128\n\t" + "vor.v $vr0,$vr0,$vr1\n\t" + "vor.v $vr2,$vr2,$vr3\n\t" + "vor.v $vr4,$vr4,$vr5\n\t" + "vor.v $vr6,$vr6,$vr7\n\t" + "vor.v $vr0,$vr0,$vr2\n\t" + "vor.v $vr4,$vr4,$vr6\n\t" + "vor.v $vr0,$vr0,$vr4\n\t" + "bltu %1,%3,1b\n\t" + "vsetnez.v $fcc0,$vr0\n\t" + "bcnez $fcc0,2f\n\t" + "ori %0,$r0,1\n" + "2:" + : "=&r"(ret), "+r"(p) + : "r"(buf), "r"(e), "r"(l) + : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + + return ret; +} + +static bool buffer_is_zero_lasx(const void *buf, size_t len) +{ + const void *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32); + const void *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32) - (7 * 32); + const void *l = buf + len; + bool ret; + + asm("xvld $xr0,%2,0\n\t" /* first: buf + 0 */ + "xvld $xr1,%4,-32\n\t" /* last: buf + len - 32 */ + "xvld $xr2,%3,0\n\t" /* e[0] */ + "xvld $xr3,%3,32\n\t" /* e[1] */ + "xvld $xr4,%3,64\n\t" /* e[2] */ + "xvld $xr5,%3,96\n\t" /* e[3] */ + "xvld $xr6,%3,128\n\t" /* e[4] */ + "xvld $xr7,%3,160\n\t" /* e[5] */ + "xvld $xr8,%3,192\n\t" /* e[6] */ + "xvor.v $xr0,$xr0,$xr1\n\t" + "xvor.v $xr2,$xr2,$xr3\n\t" + "xvor.v $xr4,$xr4,$xr5\n\t" + "xvor.v $xr6,$xr6,$xr7\n\t" + "xvor.v $xr0,$xr0,$xr2\n\t" + "xvor.v $xr4,$xr4,$xr6\n\t" + "xvor.v $xr0,$xr0,$xr4\n\t" + "xvor.v $xr0,$xr0,$xr8\n\t" + "or %0,$r0,$r0\n\t" /* prepare return false */ + "bgeu %1,%3,2f\n" + "1:\n\t" + "xvsetnez.v $fcc0,$xr0\n\t" + "bcnez $fcc0,3f\n\t" + "xvld $xr0,%1,0\n\t" /* p[0] */ + "xvld $xr1,%1,32\n\t" /* p[1] */ + "xvld $xr2,%1,64\n\t" /* p[2] */ + "xvld $xr3,%1,96\n\t" /* p[3] */ + "xvld $xr4,%1,128\n\t" /* p[4] */ + "xvld $xr5,%1,160\n\t" /* p[5] */ + "xvld $xr6,%1,192\n\t" /* p[6] */ + "xvld $xr7,%1,224\n\t" /* p[7] */ + "addi.d %1,%1,256\n\t" + "xvor.v $xr0,$xr0,$xr1\n\t" + "xvor.v $xr2,$xr2,$xr3\n\t" + "xvor.v $xr4,$xr4,$xr5\n\t" + "xvor.v $xr6,$xr6,$xr7\n\t" + "xvor.v $xr0,$xr0,$xr2\n\t" + "xvor.v $xr4,$xr4,$xr6\n\t" + "xvor.v $xr0,$xr0,$xr4\n\t" + "bltu %1,%3,1b\n" + "2:\n\t" + "xvsetnez.v $fcc0,$xr0\n\t" + "bcnez $fcc0,3f\n\t" + "ori %0,$r0,1\n" + "3:" + : "=&r"(ret), "+r"(p) + : "r"(buf), "r"(e), "r"(l) + : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + + return ret; +} + +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_is_zero_lsx, + buffer_is_zero_lasx, +}; + +static unsigned best_accel(void) +{ + unsigned info = cpuinfo_init(); + if (info & CPUINFO_LASX) { + return 2; + } + if (info & CPUINFO_LSX) { + return 1; + } + return 0; +} From 3b279f73fa37bec8d3ba04a15f5153d6491cffaf Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Wed, 12 Jun 2024 15:30:31 +0200 Subject: [PATCH 1516/2884] accel/tcg: Fix typo causing tb->page_addr[1] to not be recorded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For TBs crossing page boundaries, the 2nd page will never be recorded/removed, as the index of the 2nd page is computed from the address of the 1st page. This is due to a typo, fix it. Cc: qemu-stable@nongnu.org Fixes: deba78709a ("accel/tcg: Always lock pages before translation") Signed-off-by: Anton Johansson Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20240612133031.15298-1-anjo@rev.ng> Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 19ae6793f3..cc0f5afd47 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -713,7 +713,7 @@ static void tb_record(TranslationBlock *tb) tb_page_addr_t paddr0 = tb_page_addr0(tb); tb_page_addr_t paddr1 = tb_page_addr1(tb); tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; - tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; assert(paddr0 != -1); if (unlikely(paddr1 != -1) && pindex0 != pindex1) { @@ -745,7 +745,7 @@ static void tb_remove(TranslationBlock *tb) tb_page_addr_t paddr0 = tb_page_addr0(tb); tb_page_addr_t paddr1 = tb_page_addr1(tb); tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; - tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; assert(paddr0 != -1); if (unlikely(paddr1 != -1) && pindex0 != pindex1) { From 54b27921026df384f67df86f04c39539df375c60 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Fri, 14 Jun 2024 17:46:40 +0200 Subject: [PATCH 1517/2884] linux-user: Make TARGET_NR_setgroups affect only the current thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like TARGET_NR_setuid, TARGET_NR_setgroups should affect only the calling thread, and not the entire process. Therefore, implement it using a syscall, and not a libc call. Cc: qemu-stable@nongnu.org Fixes: 19b84f3c35d7 ("added setgroups and getgroups syscalls") Signed-off-by: Ilya Leoshkevich Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240614154710.1078766-1-iii@linux.ibm.com> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- linux-user/syscall.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b9b5a387b3..e2804312fc 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -7209,11 +7209,17 @@ static inline int tswapid(int id) #else #define __NR_sys_setresgid __NR_setresgid #endif +#ifdef __NR_setgroups32 +#define __NR_sys_setgroups __NR_setgroups32 +#else +#define __NR_sys_setgroups __NR_setgroups +#endif _syscall1(int, sys_setuid, uid_t, uid) _syscall1(int, sys_setgid, gid_t, gid) _syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) _syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) +_syscall2(int, sys_setgroups, int, size, gid_t *, grouplist) void syscall_init(void) { @@ -11891,7 +11897,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id)); } - return get_errno(setgroups(gidsetsize, grouplist)); + return get_errno(sys_setgroups(gidsetsize, grouplist)); } case TARGET_NR_fchown: return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); @@ -12227,7 +12233,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } unlock_user(target_grouplist, arg2, 0); } - return get_errno(setgroups(gidsetsize, grouplist)); + return get_errno(sys_setgroups(gidsetsize, grouplist)); } #endif #ifdef TARGET_NR_fchown32 From 6b4965373e561b77f91cfbdf41353635c9661358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Thu, 6 Jun 2024 16:43:31 +0200 Subject: [PATCH 1518/2884] target/sparc: use signed denominator in sdiv helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The result has to be done with the signed denominator (b32) instead of the unsigned value passed in argument (b). Cc: qemu-stable@nongnu.org Fixes: 1326010322d6 ("target/sparc: Remove CC_OP_DIV") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2319 Signed-off-by: Clément Chigot Reviewed-by: Richard Henderson Message-Id: <20240606144331.698361-1-chigot@adacore.com> Signed-off-by: Richard Henderson --- target/sparc/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 2247e243b5..7846ddd6f6 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -121,7 +121,7 @@ uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) return (uint32_t)(b32 < 0 ? INT32_MAX : INT32_MIN) | (-1ull << 32); } - a64 /= b; + a64 /= b32; r = a64; if (unlikely(r != a64)) { return (uint32_t)(a64 < 0 ? INT32_MIN : INT32_MAX) | (-1ull << 32); From 521d7fb3ebdf88112ed13556a93e3037742b9eb8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 19 Jun 2024 05:41:13 +0000 Subject: [PATCH 1519/2884] tcg/loongarch64: Fix tcg_out_movi vs some pcrel pointers Simplify the logic for two-part, 32-bit pc-relative addresses. Rather than assume all such fit in int32_t, do some arithmetic and assert a result, do some arithmetic first and then check to see if the pieces are in range. Cc: qemu-stable@nongnu.org Fixes: dacc51720db ("tcg/loongarch64: Implement tcg_out_mov and tcg_out_movi") Reviewed-by: Song Gao Reported-by: Song Gao Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 1c4dc4decb..5b7ed5c176 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -382,8 +382,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, * back to the slow path. */ - intptr_t pc_offset; - tcg_target_long val_lo, val_hi, pc_hi, offset_hi; + intptr_t src_rx, pc_offset; tcg_target_long hi12, hi32, hi52; /* Value fits in signed i32. */ @@ -393,24 +392,23 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, } /* PC-relative cases. */ - pc_offset = tcg_pcrel_diff(s, (void *)val); - if (pc_offset == sextreg(pc_offset, 0, 22) && (pc_offset & 3) == 0) { - /* Single pcaddu2i. */ - tcg_out_opc_pcaddu2i(s, rd, pc_offset >> 2); - return; + src_rx = (intptr_t)tcg_splitwx_to_rx(s->code_ptr); + if ((val & 3) == 0) { + pc_offset = val - src_rx; + if (pc_offset == sextreg(pc_offset, 0, 22)) { + /* Single pcaddu2i. */ + tcg_out_opc_pcaddu2i(s, rd, pc_offset >> 2); + return; + } } - if (pc_offset == (int32_t)pc_offset) { - /* Offset within 32 bits; load with pcalau12i + ori. */ - val_lo = sextreg(val, 0, 12); - val_hi = val >> 12; - pc_hi = (val - pc_offset) >> 12; - offset_hi = val_hi - pc_hi; - - tcg_debug_assert(offset_hi == sextreg(offset_hi, 0, 20)); - tcg_out_opc_pcalau12i(s, rd, offset_hi); + pc_offset = (val >> 12) - (src_rx >> 12); + if (pc_offset == sextreg(pc_offset, 0, 20)) { + /* Load with pcalau12i + ori. */ + tcg_target_long val_lo = val & 0xfff; + tcg_out_opc_pcalau12i(s, rd, pc_offset); if (val_lo != 0) { - tcg_out_opc_ori(s, rd, rd, val_lo & 0xfff); + tcg_out_opc_ori(s, rd, rd, val_lo); } return; } From a701c03decf140c7666edee1301e151714e50a72 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:16 -0300 Subject: [PATCH 1520/2884] migration: Drop reference to QIOChannel if file seeking fails We forgot to drop the reference to the QIOChannel in the error path of the offset adjustment. Do it now. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/file.c b/migration/file.c index ab18ba505a..2bb8c64092 100644 --- a/migration/file.c +++ b/migration/file.c @@ -94,6 +94,7 @@ void file_start_outgoing_migration(MigrationState *s, ioc = QIO_CHANNEL(fioc); if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { + object_unref(OBJECT(fioc)); return; } qio_channel_set_name(ioc, "migration-file-outgoing"); From 6d3279655ac49b806265f08415165f471d33e032 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:17 -0300 Subject: [PATCH 1521/2884] migration: Fix file migration with fdset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the "file:" migration support was added we missed the special case in the qemu_open_old implementation that allows for a particular file name format to be used to refer to a set of file descriptors that have been previously provided to QEMU via the add-fd QMP command. When using this fdset feature, we should not truncate the migration file because being given an fd means that the management layer is in control of the file and will likely already have some data written to it. This is further indicated by the presence of the 'offset' argument, which indicates the start of the region where QEMU is allowed to write. Fix the issue by replacing the O_TRUNC flag on open by an ftruncate call, which will take the offset into consideration. Fixes: 385f510df5 ("migration: file URI offset") Suggested-by: Daniel P. Berrangé Reviewed-by: Prasad Pandit Reviewed-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- migration/file.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/migration/file.c b/migration/file.c index 2bb8c64092..a903710f06 100644 --- a/migration/file.c +++ b/migration/file.c @@ -84,12 +84,19 @@ void file_start_outgoing_migration(MigrationState *s, trace_migration_file_outgoing(filename); - fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, - 0600, errp); + fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY, 0600, errp); if (!fioc) { return; } + if (ftruncate(fioc->fd, offset)) { + error_setg_errno(errp, errno, + "failed to truncate migration file to offset %" PRIx64, + offset); + object_unref(OBJECT(fioc)); + return; + } + outgoing_args.fname = g_strdup(filename); ioc = QIO_CHANNEL(fioc); From 55fc0c2f68ec81cc51f39964cf6d1bf3f7467a4f Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:18 -0300 Subject: [PATCH 1522/2884] tests/qtest/migration: Fix file migration offset check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When doing file migration, QEMU accepts an offset that should be skipped when writing the migration stream to the file. The purpose of the offset is to allow the management layer to put its own metadata at the start of the file. We have tests for this in migration-test, but only testing that the migration stream starts at the correct offset and not that it actually leaves the data intact. Unsurprisingly, there's been a bug in that area that the tests didn't catch. Fix the tests to write some data to the offset region and check that it's actually there after the migration. While here, switch to using g_get_file_contents() which is more portable than mmap(). Reviewed-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 79 ++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0dccb4beff..0a529a527b 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -68,6 +68,7 @@ static QTestMigrationState dst_state; #define QEMU_VM_FILE_MAGIC 0x5145564d #define FILE_TEST_FILENAME "migfile" #define FILE_TEST_OFFSET 0x1000 +#define FILE_TEST_MARKER 'X' #define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" #define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" @@ -1693,10 +1694,43 @@ finish: test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); } +static void file_dirty_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *data = g_new0(char, size); + + memset(data, FILE_TEST_MARKER, size); + g_assert(g_file_set_contents(path, data, size, NULL)); +} + +static void file_check_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *expected = g_new0(char, size); + g_autofree char *actual = NULL; + uint64_t *stream_start; + + /* + * Ensure the skipped offset region's data has not been touched + * and the migration stream starts at the right place. + */ + + memset(expected, FILE_TEST_MARKER, size); + + g_assert(g_file_get_contents(path, &actual, NULL, NULL)); + g_assert(!memcmp(actual, expected, size)); + + stream_start = (uint64_t *)(actual + size); + g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); +} + static void test_file_common(MigrateCommon *args, bool stop_src) { QTestState *from, *to; void *data_hook = NULL; + bool check_offset = false; if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -1709,6 +1743,16 @@ static void test_file_common(MigrateCommon *args, bool stop_src) */ g_assert_false(args->live); + if (g_strrstr(args->connect_uri, "offset=")) { + check_offset = true; + /* + * This comes before the start_hook because it's equivalent to + * a management application creating the file and writing to + * it so hooks should expect the file to be already present. + */ + file_dirty_offset_region(); + } + if (args->start_hook) { data_hook = args->start_hook(from, to); } @@ -1743,6 +1787,10 @@ static void test_file_common(MigrateCommon *args, bool stop_src) wait_for_serial("dest_serial"); + if (check_offset) { + file_check_offset_region(); + } + finish: if (args->finish_hook) { args->finish_hook(from, to, data_hook); @@ -1942,36 +1990,6 @@ static void test_precopy_file(void) test_file_common(&args, true); } -static void file_offset_finish_hook(QTestState *from, QTestState *to, - void *opaque) -{ -#if defined(__linux__) - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET + sizeof(QEMU_VM_FILE_MAGIC); - uintptr_t *addr, *p; - int fd; - - fd = open(path, O_RDONLY); - g_assert(fd != -1); - addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - g_assert(addr != MAP_FAILED); - - /* - * Ensure the skipped offset contains zeros and the migration - * stream starts at the right place. - */ - p = addr; - while (p < addr + FILE_TEST_OFFSET / sizeof(uintptr_t)) { - g_assert(*p == 0); - p++; - } - g_assert_cmpint(cpu_to_be64(*p) >> 32, ==, QEMU_VM_FILE_MAGIC); - - munmap(addr, size); - close(fd); -#endif -} - static void test_precopy_file_offset(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, @@ -1980,7 +1998,6 @@ static void test_precopy_file_offset(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .finish_hook = file_offset_finish_hook, }; test_file_common(&args, false); From 926554c0bfdfbf7b058ed370c2b484e56b126d34 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:19 -0300 Subject: [PATCH 1523/2884] tests/qtest/migration: Add a precopy file test with fdset Add a test for file migration using fdset. The passing of fds is more complex than using a file path. This is also the scenario where it's most important we ensure that the initial migration stream offset is respected because the fdset interface is the one used by the management layer when providing a non empty migration file. Note that fd passing is not available on Windows, so anything that uses add-fd needs to exclude that platform. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 0a529a527b..22b07bc0ec 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1990,6 +1990,46 @@ static void test_precopy_file(void) test_file_common(&args, true); } +#ifndef _WIN32 +static void fdset_add_fds(QTestState *qts, const char *file, int flags, + int num_fds) +{ + for (int i = 0; i < num_fds; i++) { + int fd; + + fd = open(file, flags, 0660); + assert(fd != -1); + + qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " + "'arguments': {'fdset-id': 1}}"); + close(fd); + } +} + +static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 1); + fdset_add_fds(to, file, O_RDONLY, 1); + + return NULL; +} + +static void test_precopy_file_offset_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = file_offset_fdset_start_hook, + }; + + test_file_common(&args, false); +} +#endif + static void test_precopy_file_offset(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, @@ -3527,6 +3567,10 @@ int main(int argc, char **argv) test_precopy_file); migration_test_add("/migration/precopy/file/offset", test_precopy_file_offset); +#ifndef _WIN32 + migration_test_add("/migration/precopy/file/offset/fdset", + test_precopy_file_offset_fdset); +#endif migration_test_add("/migration/precopy/file/offset/bad", test_precopy_file_offset_bad); From 1cd93fb0bf8b1fddab4c38e17145cc8776eadaa0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 17 Jun 2024 15:57:20 -0300 Subject: [PATCH 1524/2884] monitor: Drop monitor_fdset_dup_fd_find/_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those functions are not needed, one remove function should already work. Clean it up. Here the code doesn't really care about whether we need to keep that dupfd around if close() failed: when that happens something got very wrong, keeping the dup_fd around the fdsets may not help that situation so far. Cc: Dr. David Alan Gilbert Cc: Markus Armbruster Cc: Philippe Mathieu-Daudé Cc: Paolo Bonzini Cc: Daniel P. Berrangé Signed-off-by: Peter Xu Reviewed-by: Daniel P. Berrangé [add missing return statement, removal during traversal is not safe] Signed-off-by: Fabiano Rosas --- include/monitor/monitor.h | 1 - monitor/fds.c | 28 ++++++---------------------- stubs/fdset.c | 5 ----- util/osdep.c | 15 +-------------- 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 965f5d5450..fd9b3f538c 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -53,7 +53,6 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, const char *opaque, Error **errp); int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags); void monitor_fdset_dup_fd_remove(int dup_fd); -int64_t monitor_fdset_dup_fd_find(int dup_fd); void monitor_register_hmp(const char *name, bool info, void (*cmd)(Monitor *mon, const QDict *qdict)); diff --git a/monitor/fds.c b/monitor/fds.c index d86c2c674c..fb9f58c056 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -458,7 +458,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) #endif } -static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) +void monitor_fdset_dup_fd_remove(int dup_fd) { MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd_dup; @@ -467,31 +467,15 @@ static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { if (mon_fdset_fd_dup->fd == dup_fd) { - if (remove) { - QLIST_REMOVE(mon_fdset_fd_dup, next); - g_free(mon_fdset_fd_dup); - if (QLIST_EMPTY(&mon_fdset->dup_fds)) { - monitor_fdset_cleanup(mon_fdset); - } - return -1; - } else { - return mon_fdset->id; + QLIST_REMOVE(mon_fdset_fd_dup, next); + g_free(mon_fdset_fd_dup); + if (QLIST_EMPTY(&mon_fdset->dup_fds)) { + monitor_fdset_cleanup(mon_fdset); } + return; } } } - - return -1; -} - -int64_t monitor_fdset_dup_fd_find(int dup_fd) -{ - return monitor_fdset_dup_fd_find_remove(dup_fd, false); -} - -void monitor_fdset_dup_fd_remove(int dup_fd) -{ - monitor_fdset_dup_fd_find_remove(dup_fd, true); } int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) diff --git a/stubs/fdset.c b/stubs/fdset.c index d7c39a28ac..389e368a29 100644 --- a/stubs/fdset.c +++ b/stubs/fdset.c @@ -9,11 +9,6 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) return -1; } -int64_t monitor_fdset_dup_fd_find(int dup_fd) -{ - return -1; -} - void monitor_fdset_dup_fd_remove(int dupfd) { } diff --git a/util/osdep.c b/util/osdep.c index 5d23bbfbec..756de9a745 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -398,21 +398,8 @@ int qemu_open_old(const char *name, int flags, ...) int qemu_close(int fd) { - int64_t fdset_id; - /* Close fd that was dup'd from an fdset */ - fdset_id = monitor_fdset_dup_fd_find(fd); - if (fdset_id != -1) { - int ret; - - ret = close(fd); - if (ret == 0) { - monitor_fdset_dup_fd_remove(fd); - } - - return ret; - } - + monitor_fdset_dup_fd_remove(fd); return close(fd); } From a93ad56053e54a94875faabb042d7c60fdd2fe20 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:21 -0300 Subject: [PATCH 1525/2884] monitor: Introduce monitor_fdset_*free Introduce new functions to remove and free no longer used fds and fdsets. We need those to decouple the remove/free routines from monitor_fdset_cleanup() which will go away in the next patches. The new functions: - monitor_fdset_free/_if_empty() will be used when a monitor connection closes and when an fd is removed to cleanup any fdset that is now empty. - monitor_fdset_fd_free() will be used to remove one or more fds that have been explicitly targeted by qmp_remove_fd(). Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- monitor/fds.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/monitor/fds.c b/monitor/fds.c index fb9f58c056..bd45a26368 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -167,6 +167,27 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) return -1; } +static void monitor_fdset_free(MonFdset *mon_fdset) +{ + QLIST_REMOVE(mon_fdset, next); + g_free(mon_fdset); +} + +static void monitor_fdset_free_if_empty(MonFdset *mon_fdset) +{ + if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { + monitor_fdset_free(mon_fdset); + } +} + +static void monitor_fdset_fd_free(MonFdsetFd *mon_fdset_fd) +{ + close(mon_fdset_fd->fd); + g_free(mon_fdset_fd->opaque); + QLIST_REMOVE(mon_fdset_fd, next); + g_free(mon_fdset_fd); +} + static void monitor_fdset_cleanup(MonFdset *mon_fdset) { MonFdsetFd *mon_fdset_fd; @@ -176,17 +197,11 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset) if ((mon_fdset_fd->removed || (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && runstate_is_running()) { - close(mon_fdset_fd->fd); - g_free(mon_fdset_fd->opaque); - QLIST_REMOVE(mon_fdset_fd, next); - g_free(mon_fdset_fd); + monitor_fdset_fd_free(mon_fdset_fd); } } - if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { - QLIST_REMOVE(mon_fdset, next); - g_free(mon_fdset); - } + monitor_fdset_free_if_empty(mon_fdset); } void monitor_fdsets_cleanup(void) From 87d67fadb9db5e87072c244df794c0755150fd2a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:22 -0300 Subject: [PATCH 1526/2884] monitor: Stop removing non-duplicated fds monitor_fdsets_cleanup() currently has three responsibilities: 1- Remove the fds that have been marked for removal(->removed=true) by qmp_remove_fd(). This is overly complicated, but ok. 2- Remove any file descriptors that have been passed into QEMU and never duplicated[1,2]. A file descriptor without duplicates indicates that no part of QEMU has made use of it. This is problematic because the current implementation does it only if the guest is not running and the monitor is closed. 3- Remove/free fdsets that have become empty due to the above removals. This is ok. The scenario described in (2) is starting to show some cracks now that we're trying to consume fds from the migration code: - Doing cleanup every time the last monitor connection closes works to reap unused fds, but also has the side effect of forcing the management layer to pass the file descriptors again in case of a disconnect/re-connect, if that happened to be the only monitor connection. Another side effect is that removing an fd with qmp_remove_fd() is effectively delayed until the last monitor connection closes. The usage of mon_refcount is also problematic because it's racy. - Checking runstate_is_running() skips the cleanup unless the VM is running and avoids premature cleanup of the fds, but also has the side effect of blocking the legitimate removal of an fd via qmp_remove_fd() if the VM happens to be in another state. This affects qmp_remove_fd() and qmp_query_fdsets() in particular because requesting a removal at a bad time (guest stopped) might cause an fd to never be removed, or to be removed at a much later point in time, causing the query command to continue showing the supposedly removed fd/fdset. Note that file descriptors that *have* been duplicated are owned by the code that uses them and will be removed after qemu_close() is called. Therefore we've decided that the best course of action to avoid the undesired side-effects is to stop managing non-duplicated file descriptors. 1- efb87c1697 ("monitor: Clean up fd sets on monitor disconnect") 2- ebe52b592d ("monitor: Prevent removing fd from set during init") Reviewed-by: Peter Xu [fix logic mistake: s/fdset_free/fdset_free_if_empty] Signed-off-by: Fabiano Rosas --- monitor/fds.c | 15 ++++--- monitor/hmp.c | 2 - monitor/monitor-internal.h | 1 - monitor/monitor.c | 1 - monitor/qmp.c | 2 - tests/qtest/libqtest.c | 15 ++++--- tests/qtest/libqtest.h | 2 + tests/qtest/migration-test.c | 82 ++++++++++++++++++++++++++++++++++++ 8 files changed, 102 insertions(+), 18 deletions(-) diff --git a/monitor/fds.c b/monitor/fds.c index bd45a26368..76199d4b3b 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -175,6 +175,11 @@ static void monitor_fdset_free(MonFdset *mon_fdset) static void monitor_fdset_free_if_empty(MonFdset *mon_fdset) { + /* + * Only remove an empty fdset. The fds are owned by the user and + * should have been removed with qmp_remove_fd(). The dup_fds are + * owned by QEMU and should have been removed with qemu_close(). + */ if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { monitor_fdset_free(mon_fdset); } @@ -194,9 +199,7 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset) MonFdsetFd *mon_fdset_fd_next; QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { - if ((mon_fdset_fd->removed || - (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && - runstate_is_running()) { + if (mon_fdset_fd->removed) { monitor_fdset_fd_free(mon_fdset_fd); } } @@ -211,7 +214,7 @@ void monitor_fdsets_cleanup(void) QEMU_LOCK_GUARD(&mon_fdsets_lock); QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { - monitor_fdset_cleanup(mon_fdset); + monitor_fdset_free_if_empty(mon_fdset); } } @@ -484,9 +487,7 @@ void monitor_fdset_dup_fd_remove(int dup_fd) if (mon_fdset_fd_dup->fd == dup_fd) { QLIST_REMOVE(mon_fdset_fd_dup, next); g_free(mon_fdset_fd_dup); - if (QLIST_EMPTY(&mon_fdset->dup_fds)) { - monitor_fdset_cleanup(mon_fdset); - } + monitor_fdset_free_if_empty(mon_fdset); return; } } diff --git a/monitor/hmp.c b/monitor/hmp.c index 69c1b7e98a..460e8832f6 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -1437,11 +1437,9 @@ static void monitor_event(void *opaque, QEMUChrEvent event) monitor_resume(mon); } qemu_mutex_unlock(&mon->mon_lock); - mon_refcount++; break; case CHR_EVENT_CLOSED: - mon_refcount--; monitor_fdsets_cleanup(); break; diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 252de85681..cb628f681d 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -168,7 +168,6 @@ extern bool qmp_dispatcher_co_shutdown; extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands; extern QemuMutex monitor_lock; extern MonitorList mon_list; -extern int mon_refcount; extern HMPCommand hmp_cmds[]; diff --git a/monitor/monitor.c b/monitor/monitor.c index 01ede1babd..db52a9c7ef 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -71,7 +71,6 @@ static GHashTable *monitor_qapi_event_state; static GHashTable *coroutine_mon; /* Maps Coroutine* to Monitor* */ MonitorList mon_list; -int mon_refcount; static bool monitor_destroyed; Monitor *monitor_cur(void) diff --git a/monitor/qmp.c b/monitor/qmp.c index a239945e8d..5e538f34c0 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -466,7 +466,6 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event) data = qmp_greeting(mon); qmp_send_response(mon, data); qobject_unref(data); - mon_refcount++; break; case CHR_EVENT_CLOSED: /* @@ -479,7 +478,6 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event) json_message_parser_destroy(&mon->parser); json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL); - mon_refcount--; monitor_fdsets_cleanup(); break; case CHR_EVENT_BREAK: diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 18e2f7f282..c7f6897d78 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -514,11 +514,6 @@ static QTestState *qtest_init_internal(const char *qemu_bin, kill(s->qemu_pid, SIGSTOP); } #endif - - /* ask endianness of the target */ - - s->big_endian = qtest_query_target_endianness(s); - return s; } @@ -527,11 +522,21 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); } +QTestState *qtest_init_with_env_no_handshake(const char *var, + const char *extra_args) +{ + return qtest_init_internal(qtest_qemu_binary(var), extra_args); +} + QTestState *qtest_init_with_env(const char *var, const char *extra_args) { QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); QDict *greeting; + /* ask endianness of the target */ + + s->big_endian = qtest_query_target_endianness(s); + /* Read the QMP greeting and then do the handshake */ greeting = qtest_qmp_receive(s); qobject_unref(greeting); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index beb96b18eb..c261b7e0b3 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -68,6 +68,8 @@ QTestState *qtest_init(const char *extra_args); */ QTestState *qtest_init_with_env(const char *var, const char *extra_args); +QTestState *qtest_init_with_env_no_handshake(const char *var, + const char *extra_args); /** * qtest_init_without_qmp_handshake: * @extra_args: other arguments to pass to QEMU. CAUTION: these diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 22b07bc0ec..eb4d5948e0 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -64,6 +64,7 @@ static QTestMigrationState dst_state; #define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ #define ANALYZE_SCRIPT "scripts/analyze-migration.py" +#define VMSTATE_CHECKER_SCRIPT "scripts/vmstate-static-checker.py" #define QEMU_VM_FILE_MAGIC 0x5145564d #define FILE_TEST_FILENAME "migfile" @@ -1589,6 +1590,85 @@ static void test_analyze_script(void) test_migrate_end(from, to, false); cleanup("migfile"); } + +static void test_vmstate_checker_script(void) +{ + g_autofree gchar *cmd_src = NULL; + g_autofree gchar *cmd_dst = NULL; + g_autofree gchar *vmstate_src = NULL; + g_autofree gchar *vmstate_dst = NULL; + const char *machine_alias, *machine_opts = ""; + g_autofree char *machine = NULL; + const char *arch = qtest_get_arch(); + int pid, wstatus; + const char *python = g_getenv("PYTHON"); + + if (!getenv(QEMU_ENV_SRC) && !getenv(QEMU_ENV_DST)) { + g_test_skip("Test needs two different QEMU versions"); + return; + } + + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (g_str_equal(arch, "i386")) { + machine_alias = "pc"; + } else { + machine_alias = "q35"; + } + } else if (g_str_equal(arch, "s390x")) { + machine_alias = "s390-ccw-virtio"; + } else if (strcmp(arch, "ppc64") == 0) { + machine_alias = "pseries"; + } else if (strcmp(arch, "aarch64") == 0) { + machine_alias = "virt"; + } else { + g_assert_not_reached(); + } + + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return; + } + + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, + QEMU_ENV_DST); + + vmstate_src = g_strdup_printf("%s/vmstate-src", tmpfs); + vmstate_dst = g_strdup_printf("%s/vmstate-dst", tmpfs); + + cmd_dst = g_strdup_printf("-machine %s,%s -dump-vmstate %s", + machine, machine_opts, vmstate_dst); + cmd_src = g_strdup_printf("-machine %s,%s -dump-vmstate %s", + machine, machine_opts, vmstate_src); + + qtest_init_with_env_no_handshake(QEMU_ENV_SRC, cmd_src); + qtest_init_with_env_no_handshake(QEMU_ENV_DST, cmd_dst); + + pid = fork(); + if (!pid) { + close(1); + open("/dev/null", O_WRONLY); + execl(python, python, VMSTATE_CHECKER_SCRIPT, + "-s", vmstate_src, + "-d", vmstate_dst, + NULL); + g_assert_not_reached(); + } + + g_assert(waitpid(pid, &wstatus, 0) == pid); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + g_test_message("Failed to run vmstate-static-checker.py"); + g_test_fail(); + } + + cleanup("vmstate-src"); + cleanup("vmstate-dst"); +} #endif static void test_precopy_common(MigrateCommon *args) @@ -3522,6 +3602,8 @@ int main(int argc, char **argv) migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); + migration_test_add("/migration/vmstate-checker-script", + test_vmstate_checker_script); #endif /* From 881172f3f9dfe5764e7cb8983e5a660b93224d0c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:23 -0300 Subject: [PATCH 1527/2884] monitor: Simplify fdset and fd removal Remove fds right away instead of setting the ->removed flag. We don't need the extra complexity of having a cleanup function reap the removed entries at a later time. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- monitor/fds.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/monitor/fds.c b/monitor/fds.c index 76199d4b3b..e7619a6103 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -43,7 +43,6 @@ struct mon_fd_t { typedef struct MonFdsetFd MonFdsetFd; struct MonFdsetFd { int fd; - bool removed; char *opaque; QLIST_ENTRY(MonFdsetFd) next; }; @@ -193,20 +192,6 @@ static void monitor_fdset_fd_free(MonFdsetFd *mon_fdset_fd) g_free(mon_fdset_fd); } -static void monitor_fdset_cleanup(MonFdset *mon_fdset) -{ - MonFdsetFd *mon_fdset_fd; - MonFdsetFd *mon_fdset_fd_next; - - QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { - if (mon_fdset_fd->removed) { - monitor_fdset_fd_free(mon_fdset_fd); - } - } - - monitor_fdset_free_if_empty(mon_fdset); -} - void monitor_fdsets_cleanup(void) { MonFdset *mon_fdset; @@ -281,7 +266,7 @@ void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp) void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) { MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd, *mon_fdset_fd_next; char fd_str[60]; QEMU_LOCK_GUARD(&mon_fdsets_lock); @@ -289,21 +274,22 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) if (mon_fdset->id != fdset_id) { continue; } - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, + mon_fdset_fd_next) { if (has_fd) { if (mon_fdset_fd->fd != fd) { continue; } - mon_fdset_fd->removed = true; + monitor_fdset_fd_free(mon_fdset_fd); break; } else { - mon_fdset_fd->removed = true; + monitor_fdset_fd_free(mon_fdset_fd); } } if (has_fd && !mon_fdset_fd) { goto error; } - monitor_fdset_cleanup(mon_fdset); + monitor_fdset_free_if_empty(mon_fdset); return; } @@ -413,7 +399,6 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); mon_fdset_fd->fd = fd; - mon_fdset_fd->removed = false; mon_fdset_fd->opaque = g_strdup(opaque); QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); From 960f29b347ad34a53580fa822083d51ba7851b7b Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:24 -0300 Subject: [PATCH 1528/2884] monitor: Report errors from monitor_fdset_dup_fd_add I'm keeping the EACCES because callers expect to be able to look at errno. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- include/monitor/monitor.h | 2 +- monitor/fds.c | 10 +++++++++- stubs/fdset.c | 2 +- util/osdep.c | 10 +--------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index fd9b3f538c..c3740ec616 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -51,7 +51,7 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, const char *opaque, Error **errp); -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags); +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp); void monitor_fdset_dup_fd_remove(int dup_fd); void monitor_register_hmp(const char *name, bool info, diff --git a/monitor/fds.c b/monitor/fds.c index e7619a6103..d8c6b395b0 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -409,9 +409,10 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, return fdinfo; } -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp) { #ifdef _WIN32 + error_setg(errp, "Platform does not support fd passing (fdset)"); return -ENOENT; #else MonFdset *mon_fdset; @@ -431,6 +432,8 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); if (mon_fd_flags == -1) { + error_setg(errp, "Failed to read file status flags for fd=%d", + mon_fdset_fd->fd); return -1; } @@ -442,11 +445,15 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) if (fd == -1) { errno = EACCES; + error_setg(errp, + "Failed to find file descriptor with matching flags=0x%x", + flags); return -1; } dup_fd = qemu_dup_flags(fd, flags); if (dup_fd == -1) { + error_setg(errp, "Failed to dup() given file descriptor fd=%d", fd); return -1; } @@ -456,6 +463,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) return dup_fd; } + error_setg(errp, "Failed to find fdset /dev/fdset/%" PRId64, fdset_id); errno = ENOENT; return -1; #endif diff --git a/stubs/fdset.c b/stubs/fdset.c index 389e368a29..2950fd91fd 100644 --- a/stubs/fdset.c +++ b/stubs/fdset.c @@ -3,7 +3,7 @@ #include "monitor/monitor.h" #include "../monitor/monitor-internal.h" -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp) { errno = ENOSYS; return -1; diff --git a/util/osdep.c b/util/osdep.c index 756de9a745..5bbfdfac7a 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -310,7 +310,6 @@ qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) /* Attempt dup of fd from fd set */ if (strstart(name, "/dev/fdset/", &fdset_id_str)) { int64_t fdset_id; - int dupfd; fdset_id = qemu_parse_fdset(fdset_id_str); if (fdset_id == -1) { @@ -319,14 +318,7 @@ qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) return -1; } - dupfd = monitor_fdset_dup_fd_add(fdset_id, flags); - if (dupfd == -1) { - error_setg_errno(errp, errno, "Could not dup FD for %s flags %x", - name, flags); - return -1; - } - - return dupfd; + return monitor_fdset_dup_fd_add(fdset_id, flags, errp); } #endif From 46cec74c1b71973756d8960a305bae1491ae6259 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:25 -0300 Subject: [PATCH 1529/2884] io: Stop using qemu_open_old in channel-file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to make use of the Error object to report fdset errors from qemu_open_internal() and passing the error pointer to qemu_open_old() would require changing all callers. Move the file channel to the new API instead. Reviewed-by: Daniel P. Berrangé Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- io/channel-file.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/io/channel-file.c b/io/channel-file.c index 6436cfb6ae..2ea8d08360 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -68,11 +68,13 @@ qio_channel_file_new_path(const char *path, ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); - ioc->fd = qemu_open_old(path, flags, mode); + if (flags & O_CREAT) { + ioc->fd = qemu_create(path, flags & ~O_CREAT, mode, errp); + } else { + ioc->fd = qemu_open(path, flags, errp); + } if (ioc->fd < 0) { object_unref(OBJECT(ioc)); - error_setg_errno(errp, errno, - "Unable to open %s", path); return NULL; } From b43b61d5bee522dadf44b472af71aab7235c13d5 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:26 -0300 Subject: [PATCH 1530/2884] migration: Add direct-io parameter Add the direct-io migration parameter that tells the migration code to use O_DIRECT when opening the migration stream file whenever possible. This is currently only used with the mapped-ram migration that has a clear window guaranteed to perform aligned writes. Acked-by: Markus Armbruster Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- include/qemu/osdep.h | 2 ++ migration/migration-hmp-cmds.c | 11 +++++++++++ migration/options.c | 35 ++++++++++++++++++++++++++++++++++ migration/options.h | 1 + qapi/migration.json | 21 +++++++++++++++++--- util/osdep.c | 9 +++++++++ 6 files changed, 76 insertions(+), 3 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index f61edcfdc2..191916f38e 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -612,6 +612,8 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive); bool qemu_has_ofd_lock(void); #endif +bool qemu_has_direct_io(void); + #if defined(__HAIKU__) && defined(__i386__) #define FMT_pid "%ld" #elif defined(WIN64) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 9f0e8029e0..7d608d26e1 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -351,6 +351,13 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %s\n", MigrationParameter_str(MIGRATION_PARAMETER_MODE), qapi_enum_lookup(&MigMode_lookup, params->mode)); + + if (params->has_direct_io) { + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str( + MIGRATION_PARAMETER_DIRECT_IO), + params->direct_io ? "on" : "off"); + } } qapi_free_MigrationParameters(params); @@ -624,6 +631,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_mode = true; visit_type_MigMode(v, param, &p->mode, &err); break; + case MIGRATION_PARAMETER_DIRECT_IO: + p->has_direct_io = true; + visit_type_bool(v, param, &p->direct_io, &err); + break; default: assert(0); } diff --git a/migration/options.c b/migration/options.c index 5ab5b6d85d..645f55003d 100644 --- a/migration/options.c +++ b/migration/options.c @@ -702,6 +702,25 @@ bool migrate_cpu_throttle_tailslow(void) return s->parameters.cpu_throttle_tailslow; } +bool migrate_direct_io(void) +{ + MigrationState *s = migrate_get_current(); + + /* + * O_DIRECT is only supported with mapped-ram and multifd. + * + * mapped-ram is needed because filesystems impose restrictions on + * O_DIRECT IO alignment (see MAPPED_RAM_FILE_OFFSET_ALIGNMENT). + * + * multifd is needed to keep the unaligned portion of the stream + * isolated to the main migration thread while multifd channels + * process the aligned data with O_DIRECT enabled. + */ + return s->parameters.direct_io && + s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM] && + s->capabilities[MIGRATION_CAPABILITY_MULTIFD]; +} + uint64_t migrate_downtime_limit(void) { MigrationState *s = migrate_get_current(); @@ -905,6 +924,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->mode = s->parameters.mode; params->has_zero_page_detection = true; params->zero_page_detection = s->parameters.zero_page_detection; + params->has_direct_io = true; + params->direct_io = s->parameters.direct_io; return params; } @@ -937,6 +958,7 @@ void migrate_params_init(MigrationParameters *params) params->has_vcpu_dirty_limit = true; params->has_mode = true; params->has_zero_page_detection = true; + params->has_direct_io = true; } /* @@ -1110,6 +1132,11 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_direct_io && params->direct_io && !qemu_has_direct_io()) { + error_setg(errp, "No build-time support for direct-io"); + return false; + } + return true; } @@ -1216,6 +1243,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_zero_page_detection) { dest->zero_page_detection = params->zero_page_detection; } + + if (params->has_direct_io) { + dest->direct_io = params->direct_io; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1341,6 +1372,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_zero_page_detection) { s->parameters.zero_page_detection = params->zero_page_detection; } + + if (params->has_direct_io) { + s->parameters.direct_io = params->direct_io; + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) diff --git a/migration/options.h b/migration/options.h index 4b21cc2669..a2397026db 100644 --- a/migration/options.h +++ b/migration/options.h @@ -69,6 +69,7 @@ uint32_t migrate_checkpoint_delay(void); uint8_t migrate_cpu_throttle_increment(void); uint8_t migrate_cpu_throttle_initial(void); bool migrate_cpu_throttle_tailslow(void); +bool migrate_direct_io(void); uint64_t migrate_downtime_limit(void); uint8_t migrate_max_cpu_throttle(void); uint64_t migrate_max_bandwidth(void); diff --git a/qapi/migration.json b/qapi/migration.json index 470f746cc5..de6c8b0444 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -821,6 +821,10 @@ # See description in @ZeroPageDetection. Default is 'multifd'. # (since 9.0) # +# @direct-io: Open migration files with O_DIRECT when possible. This +# only has effect if the @mapped-ram capability is enabled. +# (Since 9.1) +# # Features: # # @unstable: Members @x-checkpoint-delay and @@ -845,7 +849,8 @@ { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, 'vcpu-dirty-limit', 'mode', - 'zero-page-detection'] } + 'zero-page-detection', + 'direct-io'] } ## # @MigrateSetParameters: @@ -991,6 +996,10 @@ # See description in @ZeroPageDetection. Default is 'multifd'. # (since 9.0) # +# @direct-io: Open migration files with O_DIRECT when possible. This +# only has effect if the @mapped-ram capability is enabled. +# (Since 9.1) +# # Features: # # @unstable: Members @x-checkpoint-delay and @@ -1030,7 +1039,8 @@ 'features': [ 'unstable' ] }, '*vcpu-dirty-limit': 'uint64', '*mode': 'MigMode', - '*zero-page-detection': 'ZeroPageDetection'} } + '*zero-page-detection': 'ZeroPageDetection', + '*direct-io': 'bool' } } ## # @migrate-set-parameters: @@ -1190,6 +1200,10 @@ # See description in @ZeroPageDetection. Default is 'multifd'. # (since 9.0) # +# @direct-io: Open migration files with O_DIRECT when possible. This +# only has effect if the @mapped-ram capability is enabled. +# (Since 9.1) +# # Features: # # @unstable: Members @x-checkpoint-delay and @@ -1226,7 +1240,8 @@ 'features': [ 'unstable' ] }, '*vcpu-dirty-limit': 'uint64', '*mode': 'MigMode', - '*zero-page-detection': 'ZeroPageDetection'} } + '*zero-page-detection': 'ZeroPageDetection', + '*direct-io': 'bool' } } ## # @query-migrate-parameters: diff --git a/util/osdep.c b/util/osdep.c index 5bbfdfac7a..770369831b 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -282,6 +282,15 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive) } #endif +bool qemu_has_direct_io(void) +{ +#ifdef O_DIRECT + return true; +#else + return false; +#endif +} + static int qemu_open_cloexec(const char *name, int flags, mode_t mode) { int ret; From 9d70239e56fadaa3571b8a7998a323ced52e8e76 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:27 -0300 Subject: [PATCH 1531/2884] migration/multifd: Add direct-io support When multifd is used along with mapped-ram, we can take benefit of a filesystem that supports the O_DIRECT flag and perform direct I/O in the multifd threads. This brings a significant performance improvement because direct-io writes bypass the page cache which would otherwise be thrashed by the multifd data which is unlikely to be needed again in a short period of time. To be able to use a multifd channel opened with O_DIRECT, we must ensure that a certain aligment is used. Filesystems usually require a block-size alignment for direct I/O. The way to achieve this is by enabling the mapped-ram feature, which already aligns its I/O properly (see MAPPED_RAM_FILE_OFFSET_ALIGNMENT at ram.c). By setting O_DIRECT on the multifd channels, all writes to the same file descriptor need to be aligned as well, even the ones that come from outside multifd, such as the QEMUFile I/O from the main migration code. This makes it impossible to use the same file descriptor for the QEMUFile and for the multifd channels. The various flags and metadata written by the main migration code will always be unaligned by virtue of their small size. To workaround this issue, we'll require a second file descriptor to be used exclusively for direct I/O. The second file descriptor can be obtained by QEMU by re-opening the migration file (already possible), or by being provided by the user or management application (support to be added in future patches). Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/file.c | 33 ++++++++++++++++++++++++++++----- migration/file.h | 1 - migration/migration.c | 23 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/migration/file.c b/migration/file.c index a903710f06..db870f2cf0 100644 --- a/migration/file.c +++ b/migration/file.c @@ -50,12 +50,31 @@ void file_cleanup_outgoing_migration(void) outgoing_args.fname = NULL; } +static void file_enable_direct_io(int *flags) +{ +#ifdef O_DIRECT + *flags |= O_DIRECT; +#else + /* it should have been rejected when setting the parameter */ + g_assert_not_reached(); +#endif +} + bool file_send_channel_create(gpointer opaque, Error **errp) { QIOChannelFile *ioc; int flags = O_WRONLY; bool ret = true; + if (migrate_direct_io()) { + /* + * Enable O_DIRECT for the secondary channels. These are used + * for sending ram pages and writes should be guaranteed to be + * aligned to at least page size. + */ + file_enable_direct_io(&flags); + } + ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); if (!ioc) { ret = false; @@ -117,21 +136,25 @@ static gboolean file_accept_incoming_migration(QIOChannel *ioc, return G_SOURCE_REMOVE; } -void file_create_incoming_channels(QIOChannel *ioc, Error **errp) +static void file_create_incoming_channels(QIOChannel *ioc, char *filename, + Error **errp) { - int i, fd, channels = 1; + int i, channels = 1; g_autofree QIOChannel **iocs = NULL; + int flags = O_RDONLY; if (migrate_multifd()) { channels += migrate_multifd_channels(); + if (migrate_direct_io()) { + file_enable_direct_io(&flags); + } } iocs = g_new0(QIOChannel *, channels); - fd = QIO_CHANNEL_FILE(ioc)->fd; iocs[0] = ioc; for (i = 1; i < channels; i++) { - QIOChannelFile *fioc = qio_channel_file_new_dupfd(fd, errp); + QIOChannelFile *fioc = qio_channel_file_new_path(filename, flags, 0, errp); if (!fioc) { while (i) { @@ -171,7 +194,7 @@ void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) return; } - file_create_incoming_channels(QIO_CHANNEL(fioc), errp); + file_create_incoming_channels(QIO_CHANNEL(fioc), filename, errp); } int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, diff --git a/migration/file.h b/migration/file.h index 7699c04677..9f71e87f74 100644 --- a/migration/file.h +++ b/migration/file.h @@ -20,7 +20,6 @@ void file_start_outgoing_migration(MigrationState *s, int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); void file_cleanup_outgoing_migration(void); bool file_send_channel_create(gpointer opaque, Error **errp); -void file_create_incoming_channels(QIOChannel *ioc, Error **errp); int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, int niov, RAMBlock *block, Error **errp); int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp); diff --git a/migration/migration.c b/migration/migration.c index e1b269624c..e03c80b3aa 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -155,6 +155,16 @@ static bool migration_needs_seekable_channel(void) return migrate_mapped_ram(); } +static bool migration_needs_extra_fds(void) +{ + /* + * When doing direct-io, multifd requires two different, + * non-duplicated file descriptors so we can use one of them for + * unaligned IO. + */ + return migrate_multifd() && migrate_direct_io(); +} + static bool transport_supports_seeking(MigrationAddress *addr) { if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { @@ -164,6 +174,12 @@ static bool transport_supports_seeking(MigrationAddress *addr) return false; } +static bool transport_supports_extra_fds(MigrationAddress *addr) +{ + /* file: works because QEMU can open it multiple times */ + return addr->transport == MIGRATION_ADDRESS_TYPE_FILE; +} + static bool migration_channels_and_transport_compatible(MigrationAddress *addr, Error **errp) @@ -180,6 +196,13 @@ migration_channels_and_transport_compatible(MigrationAddress *addr, return false; } + if (migration_needs_extra_fds() && + !transport_supports_extra_fds(addr)) { + error_setg(errp, + "Migration requires a transport that allows for extra fds (e.g. file)"); + return false; + } + return true; } From 408d295da82103df833f2f0443442d8aed880cb0 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:28 -0300 Subject: [PATCH 1532/2884] tests/qtest/migration: Add tests for file migration with direct-io The tests are only allowed to run in systems that know about the O_DIRECT flag and in filesystems which support it. Note: this also brings back migrate_set_parameter_bool() which went away when we removed the compression tests. I copied it verbatim. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-helpers.c | 44 +++++++++++++++++++++++ tests/qtest/migration-helpers.h | 8 +++++ tests/qtest/migration-test.c | 62 +++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index ce6d6615b5..0ac49ceb54 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -18,6 +18,7 @@ #include "qapi/error.h" #include "qapi/qmp/qlist.h" #include "qemu/cutils.h" +#include "qemu/memalign.h" #include "migration-helpers.h" @@ -473,3 +474,46 @@ void migration_test_add(const char *path, void (*fn)(void)) qtest_add_data_func_full(path, test, migration_test_wrapper, migration_test_destroy); } + +#ifdef O_DIRECT +/* + * Probe for O_DIRECT support on the filesystem. Since this is used + * for tests, be conservative, if anything fails, assume it's + * unsupported. + */ +bool probe_o_direct_support(const char *tmpfs) +{ + g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); + int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; + void *buf; + ssize_t ret, len; + uint64_t offset; + + fd = open(filename, flags, 0660); + if (fd < 0) { + unlink(filename); + return false; + } + + /* + * Using 1MB alignment as conservative choice to satisfy any + * plausible architecture default page size, and/or filesystem + * alignment restrictions. + */ + len = 0x100000; + offset = 0x100000; + + buf = qemu_try_memalign(len, len); + g_assert(buf); + + ret = pwrite(fd, buf, len, offset); + unlink(filename); + g_free(buf); + + if (ret < 0) { + return false; + } + + return true; +} +#endif diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 1339835698..50095fca4a 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -54,5 +54,13 @@ char *find_common_machine_version(const char *mtype, const char *var1, const char *var2); char *resolve_machine_version(const char *alias, const char *var1, const char *var2); +#ifdef O_DIRECT +bool probe_o_direct_support(const char *tmpfs); +#else +static inline bool probe_o_direct_support(const char *tmpfs) +{ + return false; +} +#endif void migration_test_add(const char *path, void (*fn)(void)); #endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index eb4d5948e0..5c41d1b70e 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -408,6 +408,38 @@ static void migrate_set_parameter_str(QTestState *who, const char *parameter, migrate_check_parameter_str(who, parameter, value); } +static long long migrate_get_parameter_bool(QTestState *who, + const char *parameter) +{ + QDict *rsp; + int result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_bool(rsp, parameter); + qobject_unref(rsp); + return !!result; +} + +static void migrate_check_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + int result; + + result = migrate_get_parameter_bool(who, parameter); + g_assert_cmpint(result, ==, value); +} + +static void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %i } }", + parameter, value); + migrate_check_parameter_bool(who, parameter, value); +} + static void migrate_ensure_non_converge(QTestState *who) { /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ @@ -2235,6 +2267,33 @@ static void test_multifd_file_mapped_ram(void) test_file_common(&args, true); } +static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to) +{ + migrate_multifd_mapped_ram_start(from, to); + + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = multifd_mapped_ram_dio_start, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} static void test_precopy_tcp_plain(void) { @@ -3674,6 +3733,9 @@ int main(int argc, char **argv) migration_test_add("/migration/multifd/file/mapped-ram/live", test_multifd_file_mapped_ram_live); + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); + #ifdef CONFIG_GNUTLS migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); From 99c147e2f53726290bbdde795b6efbb4d9138657 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:29 -0300 Subject: [PATCH 1533/2884] monitor: fdset: Match against O_DIRECT We're about to enable the use of O_DIRECT in the migration code and due to the alignment restrictions imposed by filesystems we need to make sure the flag is only used when doing aligned IO. The migration will do parallel IO to different regions of a file, so we need to use more than one file descriptor. Those cannot be obtained by duplicating (dup()) since duplicated file descriptors share the file status flags, including O_DIRECT. If one migration channel does unaligned IO while another sets O_DIRECT to do aligned IO, the filesystem would fail the unaligned operation. The add-fd QMP command along with the fdset code are specifically designed to allow the user to pass a set of file descriptors with different access flags into QEMU to be later fetched by code that needs to alternate between those flags when doing IO. Extend the fdset matching to behave the same with the O_DIRECT flag. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- monitor/fds.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/monitor/fds.c b/monitor/fds.c index d8c6b395b0..b5416b5b5d 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -424,6 +424,11 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp) int fd = -1; int dup_fd; int mon_fd_flags; + int mask = O_ACCMODE; + +#ifdef O_DIRECT + mask |= O_DIRECT; +#endif if (mon_fdset->id != fdset_id) { continue; @@ -437,7 +442,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp) return -1; } - if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { + if ((flags & mask) == (mon_fd_flags & mask)) { fd = mon_fdset_fd->fd; break; } From 8d60280e4f1621e13aea4aa593b5bc3e2af34e9d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:30 -0300 Subject: [PATCH 1534/2884] migration: Add documentation for fdset with multifd + file With the last few changes to the fdset infrastructure, we now allow multifd to use an fdset when migrating to a file. This is useful for the scenario where the management layer wants to have control over the migration file. By receiving the file descriptors directly, QEMU can delegate some high level operating system operations to the management layer (such as mandatory access control). The management layer might also want to add its own headers before the migration stream. Document the "file:/dev/fdset/#" syntax for the multifd migration with mapped-ram. The requirements for the fdset mechanism are: - the fdset must contain two fds that are not duplicates between themselves; - if direct-io is to be used, exactly one of the fds must have the O_DIRECT flag set; - the file must be opened with WRONLY on the migration source side; - the file must be opened with RDONLY on the migration destination side. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/devel/migration/main.rst | 24 +++++++++++++++++++----- docs/devel/migration/mapped-ram.rst | 6 +++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index 495cdcb112..784c899dca 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -47,11 +47,25 @@ over any transport. QEMU interference. Note that QEMU does not flush cached file data/metadata at the end of migration. -In addition, support is included for migration using RDMA, which -transports the page data using ``RDMA``, where the hardware takes care of -transporting the pages, and the load on the CPU is much lower. While the -internals of RDMA migration are a bit different, this isn't really visible -outside the RAM migration code. + The file migration also supports using a file that has already been + opened. A set of file descriptors is passed to QEMU via an "fdset" + (see add-fd QMP command documentation). This method allows a + management application to have control over the migration file + opening operation. There are, however, strict requirements to this + interface if the multifd capability is enabled: + + - the fdset must contain two file descriptors that are not + duplicates between themselves; + - if the direct-io capability is to be used, exactly one of the + file descriptors must have the O_DIRECT flag set; + - the file must be opened with WRONLY on the migration source side + and RDONLY on the migration destination side. + +- rdma migration: support is included for migration using RDMA, which + transports the page data using ``RDMA``, where the hardware takes + care of transporting the pages, and the load on the CPU is much + lower. While the internals of RDMA migration are a bit different, + this isn't really visible outside the RAM migration code. All these migration protocols use the same infrastructure to save/restore state devices. This infrastructure is shared with the diff --git a/docs/devel/migration/mapped-ram.rst b/docs/devel/migration/mapped-ram.rst index fa4cefd9fc..d352b546e9 100644 --- a/docs/devel/migration/mapped-ram.rst +++ b/docs/devel/migration/mapped-ram.rst @@ -16,7 +16,7 @@ location in the file, rather than constantly being added to a sequential stream. Having the pages at fixed offsets also allows the usage of O_DIRECT for save/restore of the migration stream as the pages are ensured to be written respecting O_DIRECT alignment -restrictions (direct-io support not yet implemented). +restrictions. Usage ----- @@ -35,6 +35,10 @@ Use a ``file:`` URL for migration: Mapped-ram migration is best done non-live, i.e. by stopping the VM on the source side before migrating. +For best performance enable the ``direct-io`` parameter as well: + + ``migrate_set_parameter direct-io on`` + Use-cases --------- From 31a5a3032eb3d62e045e18c80658e5e8f5341cda Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 17 Jun 2024 15:57:31 -0300 Subject: [PATCH 1535/2884] tests/qtest/migration: Add a test for mapped-ram with passing of fds Add a multifd test for mapped-ram with passing of fds into QEMU. This is how libvirt will consume the feature. There are a couple of details to the fdset mechanism: - multifd needs two distinct file descriptors (not duplicated with dup()) so it can enable O_DIRECT only on the channels that do aligned IO. The dup() system call creates file descriptors that share status flags, of which O_DIRECT is one. - the open() access mode flags used for the fds passed into QEMU need to match the flags QEMU uses to open the file. Currently O_WRONLY for src and O_RDONLY for dst. Note that fdset code goes under _WIN32 because fd passing is not supported on Windows. Reviewed-by: Peter Xu [brought back the qmp_remove_fd() call at the end of the tests] Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 105 ++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 5c41d1b70e..6207305ff8 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -2104,11 +2104,18 @@ static void test_precopy_file(void) #ifndef _WIN32 static void fdset_add_fds(QTestState *qts, const char *file, int flags, - int num_fds) + int num_fds, bool direct_io) { for (int i = 0; i < num_fds; i++) { int fd; +#ifdef O_DIRECT + /* only secondary channels can use direct-io */ + if (direct_io && i != 0) { + flags |= O_DIRECT; + } +#endif + fd = open(file, flags, 0660); assert(fd != -1); @@ -2122,8 +2129,8 @@ static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to) { g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - fdset_add_fds(from, file, O_WRONLY, 1); - fdset_add_fds(to, file, O_RDONLY, 1); + fdset_add_fds(from, file, O_WRONLY, 1, false); + fdset_add_fds(to, file, O_RDONLY, 1, false); return NULL; } @@ -2295,6 +2302,91 @@ static void test_multifd_file_mapped_ram_dio(void) test_file_common(&args, true); } +#ifndef _WIN32 +static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, + void *opaque) +{ + QDict *resp; + QList *fdsets; + + /* + * Remove the fdsets after migration, otherwise a second migration + * would fail due fdset reuse. + */ + qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " + "'arguments': { 'fdset-id': 1}}"); + + /* + * Make sure no fdsets are left after migration, otherwise a + * second migration would fail due fdset reuse. + */ + resp = qtest_qmp(from, "{'execute': 'query-fdsets', " + "'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + fdsets = qdict_get_qlist(resp, "return"); + g_assert(fdsets && qlist_empty(fdsets)); +} + +static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, true); + fdset_add_fds(to, file, O_RDONLY, 2, true); + + migrate_multifd_mapped_ram_start(from, to); + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, false); + fdset_add_fds(to, file, O_RDONLY, 2, false); + + migrate_multifd_mapped_ram_start(from, to); + + return NULL; +} + +static void test_multifd_file_mapped_ram_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = multifd_mapped_ram_fdset, + .finish_hook = multifd_mapped_ram_fdset_end, + }; + + test_file_common(&args, true); +} + +static void test_multifd_file_mapped_ram_fdset_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = multifd_mapped_ram_fdset_dio, + .finish_hook = multifd_mapped_ram_fdset_end, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} +#endif /* !_WIN32 */ + static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -3736,6 +3828,13 @@ int main(int argc, char **argv) migration_test_add("/migration/multifd/file/mapped-ram/dio", test_multifd_file_mapped_ram_dio); +#ifndef _WIN32 + migration_test_add("/migration/multifd/file/mapped-ram/fdset", + test_multifd_file_mapped_ram_fdset); + migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", + test_multifd_file_mapped_ram_fdset_dio); +#endif + #ifdef CONFIG_GNUTLS migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); From 637280aeb242517ede480aa2d5ba1c29d41eac11 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:36 -0400 Subject: [PATCH 1536/2884] migration/multifd: Avoid the final FLUSH in complete() We always do the flush when finishing one round of scan, and during complete() phase we should scan one more round making sure no dirty page existed. In that case we shouldn't need one explicit FLUSH at the end of complete(), as when reaching there all pages should have been flushed. Reviewed-by: Fabiano Rosas Tested-by: Fabiano Rosas Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/ram.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index ceea586b06..edec1a2d07 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3300,10 +3300,6 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } } - if (migrate_multifd() && !migrate_multifd_flush_after_each_section() && - !migrate_mapped_ram()) { - qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); - } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); return qemu_fflush(f); } From 60ce47675d74ddae3f13a32767d097d9fecbda4b Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:37 -0400 Subject: [PATCH 1537/2884] migration: Rename thread debug names The postcopy thread names on dest QEMU are slightly confusing, partly I'll need to blame myself on 36f62f11e4 ("migration: Postcopy preemption preparation on channel creation"). E.g., "fault-fast" reads like a fast version of "fault-default", but it's actually the fast version of "postcopy/listen". Taking this chance, rename all the migration threads with proper rules. Considering we only have 15 chars usable, prefix all threads with "mig/", meanwhile identify src/dst threads properly this time. So now most thread names will look like "mig/DIR/xxx", where DIR will be "src"/"dst", except the bg-snapshot thread which doesn't have a direction. For multifd threads, making them "mig/{src|dst}/{send|recv}_%d". We used to have "live_migration" thread for a very long time, now it's called "mig/src/main". We may hope to have "mig/dst/main" soon but not yet. Reviewed-by: Fabiano Rosas Reviewed-by: Zhijian Li (Fujitsu) Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/colo.c | 2 +- migration/migration.c | 6 +++--- migration/multifd.c | 6 +++--- migration/postcopy-ram.c | 4 ++-- migration/savevm.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index f96c2ee069..6449490221 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -935,7 +935,7 @@ void coroutine_fn colo_incoming_co(void) assert(bql_locked()); assert(migration_incoming_colo_enabled()); - qemu_thread_create(&th, "COLO incoming", colo_process_incoming_thread, + qemu_thread_create(&th, "mig/dst/colo", colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); mis->colo_incoming_co = qemu_coroutine_self(); diff --git a/migration/migration.c b/migration/migration.c index e03c80b3aa..f9b69af62f 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2431,7 +2431,7 @@ static int open_return_path_on_source(MigrationState *ms) trace_open_return_path_on_source(); - qemu_thread_create(&ms->rp_state.rp_thread, "return path", + qemu_thread_create(&ms->rp_state.rp_thread, "mig/src/rp-thr", source_return_path_thread, ms, QEMU_THREAD_JOINABLE); ms->rp_state.rp_thread_created = true; @@ -3770,10 +3770,10 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) } if (migrate_background_snapshot()) { - qemu_thread_create(&s->thread, "bg_snapshot", + qemu_thread_create(&s->thread, "mig/snapshot", bg_migration_thread, s, QEMU_THREAD_JOINABLE); } else { - qemu_thread_create(&s->thread, "live_migration", + qemu_thread_create(&s->thread, "mig/src/main", migration_thread, s, QEMU_THREAD_JOINABLE); } s->migration_thread_running = true; diff --git a/migration/multifd.c b/migration/multifd.c index d82885fdbb..0b4cbaddfe 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1069,7 +1069,7 @@ static bool multifd_tls_channel_connect(MultiFDSendParams *p, args->p = p; p->tls_thread_created = true; - qemu_thread_create(&p->tls_thread, "multifd-tls-handshake-worker", + qemu_thread_create(&p->tls_thread, "mig/src/tls", multifd_tls_handshake_thread, args, QEMU_THREAD_JOINABLE); return true; @@ -1190,7 +1190,7 @@ bool multifd_send_setup(void) p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); p->packet->version = cpu_to_be32(MULTIFD_VERSION); } - p->name = g_strdup_printf("multifdsend_%d", i); + p->name = g_strdup_printf("mig/src/send_%d", i); p->page_size = qemu_target_page_size(); p->page_count = page_count; p->write_flags = 0; @@ -1604,7 +1604,7 @@ int multifd_recv_setup(Error **errp) + sizeof(uint64_t) * page_count; p->packet = g_malloc0(p->packet_len); } - p->name = g_strdup_printf("multifdrecv_%d", i); + p->name = g_strdup_printf("mig/dst/recv_%d", i); p->normal = g_new0(ram_addr_t, page_count); p->zero = g_new0(ram_addr_t, page_count); p->page_count = page_count; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 3419779548..97701e6bb2 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1238,7 +1238,7 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } - postcopy_thread_create(mis, &mis->fault_thread, "fault-default", + postcopy_thread_create(mis, &mis->fault_thread, "mig/dst/fault", postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE); mis->have_fault_thread = true; @@ -1258,7 +1258,7 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) * This thread needs to be created after the temp pages because * it'll fetch RAM_CHANNEL_POSTCOPY PostcopyTmpPage immediately. */ - postcopy_thread_create(mis, &mis->postcopy_prio_thread, "fault-fast", + postcopy_thread_create(mis, &mis->postcopy_prio_thread, "mig/dst/preempt", postcopy_preempt_thread, QEMU_THREAD_JOINABLE); mis->preempt_thread_status = PREEMPT_THREAD_CREATED; } diff --git a/migration/savevm.c b/migration/savevm.c index c621f2359b..e71410d8c1 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2129,7 +2129,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) } mis->have_listen_thread = true; - postcopy_thread_create(mis, &mis->listen_thread, "postcopy/listen", + postcopy_thread_create(mis, &mis->listen_thread, "mig/dst/listen", postcopy_ram_listen_thread, QEMU_THREAD_DETACHED); trace_loadvm_postcopy_handle_listen("return"); From a5c24e13e9f176901058b460e61425756322f3e8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:38 -0400 Subject: [PATCH 1538/2884] migration: Use MigrationStatus instead of int QEMU uses "int" in most cases even if it stores MigrationStatus. I don't know why, so let's try to do that right and see what blows up.. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 24 +++++++----------------- migration/migration.h | 9 +++++---- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index f9b69af62f..795b30f0d0 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -413,7 +413,7 @@ void migration_incoming_state_destroy(void) yank_unregister_instance(MIGRATION_YANK_INSTANCE); } -static void migrate_generate_event(int new_state) +static void migrate_generate_event(MigrationStatus new_state) { if (migrate_events()) { qapi_event_send_migration(new_state); @@ -1296,8 +1296,6 @@ static void fill_destination_migration_info(MigrationInfo *info) } switch (mis->state) { - case MIGRATION_STATUS_NONE: - return; case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_CANCELLING: case MIGRATION_STATUS_CANCELLED: @@ -1313,6 +1311,8 @@ static void fill_destination_migration_info(MigrationInfo *info) info->has_status = true; fill_destination_postcopy_migration_info(info); break; + default: + return; } info->status = mis->state; @@ -1360,7 +1360,8 @@ void qmp_migrate_start_postcopy(Error **errp) /* shared migration helpers */ -void migrate_set_state(int *state, int old_state, int new_state) +void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, + MigrationStatus new_state) { assert(new_state < MIGRATION_STATUS__MAX); if (qatomic_cmpxchg(state, old_state, new_state) == old_state) { @@ -1567,7 +1568,7 @@ bool migration_in_postcopy(void) } } -bool migration_postcopy_is_alive(int state) +bool migration_postcopy_is_alive(MigrationStatus state) { switch (state) { case MIGRATION_STATUS_POSTCOPY_ACTIVE: @@ -1612,20 +1613,9 @@ bool migration_is_idle(void) case MIGRATION_STATUS_COMPLETED: case MIGRATION_STATUS_FAILED: return true; - case MIGRATION_STATUS_SETUP: - case MIGRATION_STATUS_CANCELLING: - case MIGRATION_STATUS_ACTIVE: - case MIGRATION_STATUS_POSTCOPY_ACTIVE: - case MIGRATION_STATUS_COLO: - case MIGRATION_STATUS_PRE_SWITCHOVER: - case MIGRATION_STATUS_DEVICE: - case MIGRATION_STATUS_WAIT_UNPLUG: + default: return false; - case MIGRATION_STATUS__MAX: - g_assert_not_reached(); } - - return false; } bool migration_is_active(void) diff --git a/migration/migration.h b/migration/migration.h index 6af01362d4..38aa1402d5 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -160,7 +160,7 @@ struct MigrationIncomingState { /* PostCopyFD's for external userfaultfds & handlers of shared memory */ GArray *postcopy_remote_fds; - int state; + MigrationStatus state; /* * The incoming migration coroutine, non-NULL during qemu_loadvm_state(). @@ -301,7 +301,7 @@ struct MigrationState { /* params from 'migrate-set-parameters' */ MigrationParameters parameters; - int state; + MigrationStatus state; /* State related to return path */ struct { @@ -459,7 +459,8 @@ struct MigrationState { bool rdma_migration; }; -void migrate_set_state(int *state, int old_state, int new_state); +void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, + MigrationStatus new_state); void migration_fd_process_incoming(QEMUFile *f); void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp); @@ -479,7 +480,7 @@ int migrate_init(MigrationState *s, Error **errp); bool migration_is_blocked(Error **errp); /* True if outgoing migration has entered postcopy phase */ bool migration_in_postcopy(void); -bool migration_postcopy_is_alive(int state); +bool migration_postcopy_is_alive(MigrationStatus state); MigrationState *migrate_get_current(void); bool migration_has_failed(MigrationState *); bool migrate_mode_is_cpr(MigrationState *); From 4dd5f7b8d568116b3ce594b0055a47c6db50f49c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:39 -0400 Subject: [PATCH 1539/2884] migration: Cleanup incoming migration setup state change Destination QEMU can setup incoming ports for two purposes: either a fresh new incoming migration, in which QEMU will switch to SETUP for channel establishment, or a paused postcopy migration, in which QEMU will stay in POSTCOPY_PAUSED until kicking off the RECOVER phase. Now the state machine worked on dest node for the latter, only because migrate_set_state() implicitly will become a noop if the current state check failed. It wasn't clear at all. Clean it up by providing a helper migration_incoming_state_setup() doing proper checks over current status. Postcopy-paused will be explicitly checked now, and then we can bail out for unknown states. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 795b30f0d0..41a88fc50a 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -618,6 +618,29 @@ bool migrate_uri_parse(const char *uri, MigrationChannel **channel, return true; } +static bool +migration_incoming_state_setup(MigrationIncomingState *mis, Error **errp) +{ + MigrationStatus current = mis->state; + + if (current == MIGRATION_STATUS_POSTCOPY_PAUSED) { + /* + * Incoming postcopy migration will stay in PAUSED state even if + * reconnection happened. + */ + return true; + } + + if (current != MIGRATION_STATUS_NONE) { + error_setg(errp, "Illegal migration incoming state: %s", + MigrationStatus_str(current)); + return false; + } + + migrate_set_state(&mis->state, current, MIGRATION_STATUS_SETUP); + return true; +} + static void qemu_start_incoming_migration(const char *uri, bool has_channels, MigrationChannelList *channels, Error **errp) @@ -656,8 +679,9 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, return; } - migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, - MIGRATION_STATUS_SETUP); + if (!migration_incoming_state_setup(mis, errp)) { + return; + } if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { SocketAddress *saddr = &addr->u.socket; From 4146b77ec7640d3c30d42558e13423594b114385 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:40 -0400 Subject: [PATCH 1540/2884] migration/postcopy: Add postcopy-recover-setup phase This patch adds a migration state on src called "postcopy-recover-setup". The new state will describe the intermediate step starting from when the src QEMU received a postcopy recovery request, until the migration channels are properly established, but before the recovery process take place. The request came from Libvirt where Libvirt currently rely on the migration state events to detect migration state changes. That works for most of the migration process but except postcopy recovery failures at the beginning. Currently postcopy recovery only has two major states: - postcopy-paused: this is the state that both sides of QEMU will be in for a long time as long as the migration channel was interrupted. - postcopy-recover: this is the state where both sides of QEMU handshake with each other, preparing for a continuation of postcopy which used to be interrupted. The issue here is when the recovery port is invalid, the src QEMU will take the URI/channels, noticing the ports are not valid, and it'll silently keep in the postcopy-paused state, with no event sent to Libvirt. In this case, the only thing Libvirt can do is to poll the migration status with a proper interval, however that's less optimal. Considering that this is the only case where Libvirt won't get a notification from QEMU on such events, let's add postcopy-recover-setup state to mimic what we have with the "setup" state of a newly initialized migration, describing the phase of connection establishment. With that, postcopy recovery will have two paths to go now, and either path will guarantee an event generated. Now the events will look like this during a recovery process on src QEMU: - Initially when the recovery is initiated on src, QEMU will go from "postcopy-paused" -> "postcopy-recover-setup". Old QEMUs don't have this event. - Depending on whether the channel re-establishment is succeeded: - In succeeded case, src QEMU will move from "postcopy-recover-setup" to "postcopy-recover". Old QEMUs also have this event. - In failure case, src QEMU will move from "postcopy-recover-setup" to "postcopy-paused" again. Old QEMUs don't have this event. This guarantees that Libvirt will always receive a notification for recovery process properly. One thing to mention is, such new status is only needed on src QEMU not both. On dest QEMU, the state machine doesn't change. Hence the events don't change either. It's done like so because dest QEMU may not have an explicit point of setup start. E.g., it can happen that when dest QEMUs doesn't use migrate-recover command to use a new URI/channel, but the old URI/channels can be reused in recovery, in which case the old ports simply can work again after the network routes are fixed up. Add a new helper postcopy_is_paused() detecting whether postcopy is still paused, taking RECOVER_SETUP into account too. When using it on both src/dst, a slight change is done altogether to always wait for the semaphore before checking the status, because for both sides a sem_post() will be required for a recovery. Cc: Jiri Denemark Cc: Prasad Pandit Reviewed-by: Fabiano Rosas Buglink: https://issues.redhat.com/browse/RHEL-38485 Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 40 ++++++++++++++++++++++++++++++++++------ migration/postcopy-ram.c | 6 ++++++ migration/postcopy-ram.h | 3 +++ migration/savevm.c | 4 ++-- qapi/migration.json | 4 ++++ 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 41a88fc50a..3dea06d577 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1117,6 +1117,7 @@ bool migration_is_setup_or_active(void) case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_PRE_SWITCHOVER: @@ -1139,6 +1140,7 @@ bool migration_is_running(void) case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_PRE_SWITCHOVER: @@ -1276,6 +1278,7 @@ static void fill_source_migration_info(MigrationInfo *info) case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: /* TODO add some postcopy stats */ populate_time_info(info, s); @@ -1482,9 +1485,30 @@ static void migrate_error_free(MigrationState *s) static void migrate_fd_error(MigrationState *s, const Error *error) { + MigrationStatus current = s->state; + MigrationStatus next; + assert(s->to_dst_file == NULL); - migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, - MIGRATION_STATUS_FAILED); + + switch (current) { + case MIGRATION_STATUS_SETUP: + next = MIGRATION_STATUS_FAILED; + break; + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: + /* Never fail a postcopy migration; switch back to PAUSED instead */ + next = MIGRATION_STATUS_POSTCOPY_PAUSED; + break; + default: + /* + * This really shouldn't happen. Just be careful to not crash a VM + * just for this. Instead, dump something. + */ + error_report("%s: Illegal migration status (%s) detected", + __func__, MigrationStatus_str(current)); + return; + } + + migrate_set_state(&s->state, current, next); migrate_set_error(s, error); } @@ -1585,6 +1609,7 @@ bool migration_in_postcopy(void) switch (s->state) { case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: return true; default: @@ -1972,6 +1997,9 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp) return false; } + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP); + /* This is a resume, skip init status */ return true; } @@ -3004,9 +3032,9 @@ static MigThrError postcopy_pause(MigrationState *s) * We wait until things fixed up. Then someone will setup the * status back for us. */ - while (s->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + do { qemu_sem_wait(&s->postcopy_pause_sem); - } + } while (postcopy_is_paused(s->state)); if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { /* Woken up by a recover procedure. Give it a shot */ @@ -3702,7 +3730,7 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) { Error *local_err = NULL; uint64_t rate_limit; - bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; + bool resume = (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP); int ret; /* @@ -3769,7 +3797,7 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) if (resume) { /* Wakeup the main migration thread to do the recovery */ - migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP, MIGRATION_STATUS_POSTCOPY_RECOVER); qemu_sem_post(&s->postcopy_pause_sem); return; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 97701e6bb2..1c374b7ea1 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1770,3 +1770,9 @@ void *postcopy_preempt_thread(void *opaque) return NULL; } + +bool postcopy_is_paused(MigrationStatus status) +{ + return status == MIGRATION_STATUS_POSTCOPY_PAUSED || + status == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP; +} diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index ecae941211..a6df1b2811 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -13,6 +13,8 @@ #ifndef QEMU_POSTCOPY_RAM_H #define QEMU_POSTCOPY_RAM_H +#include "qapi/qapi-types-migration.h" + /* Return true if the host supports everything we need to do postcopy-ram */ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp); @@ -193,5 +195,6 @@ enum PostcopyChannels { void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file); void postcopy_preempt_setup(MigrationState *s); int postcopy_preempt_establish_channel(MigrationState *s); +bool postcopy_is_paused(MigrationStatus status); #endif diff --git a/migration/savevm.c b/migration/savevm.c index e71410d8c1..deb57833f8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2864,9 +2864,9 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) error_report("Detected IO failure for postcopy. " "Migration paused."); - while (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + do { qemu_sem_wait(&mis->postcopy_pause_sem_dst); - } + } while (postcopy_is_paused(mis->state)); trace_postcopy_pause_incoming_continued(); diff --git a/qapi/migration.json b/qapi/migration.json index de6c8b0444..0f24206bce 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -142,6 +142,9 @@ # # @postcopy-paused: during postcopy but paused. (since 3.0) # +# @postcopy-recover-setup: setup phase for a postcopy recovery process, +# preparing for a recovery phase to start. (since 9.1) +# # @postcopy-recover: trying to recover from a paused postcopy. (since # 3.0) # @@ -166,6 +169,7 @@ { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', 'active', 'postcopy-active', 'postcopy-paused', + 'postcopy-recover-setup', 'postcopy-recover', 'completed', 'failed', 'colo', 'pre-switchover', 'device', 'wait-unplug' ] } ## From 21e89f7ad526f0dddfc722e615bfb0fcdb705c87 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:41 -0400 Subject: [PATCH 1541/2884] migration/docs: Update postcopy recover session for SETUP phase Firstly, the "Paused" state was added in the wrong place before. The state machine section was describing PostcopyState, rather than MigrationStatus. Drop the Paused state descriptions. Then in the postcopy recover session, add more information on the state machine for MigrationStatus in the lines. Add the new RECOVER_SETUP phase. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu [fix typo s/reconnects/reconnect] Signed-off-by: Fabiano Rosas --- docs/devel/migration/postcopy.rst | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/devel/migration/postcopy.rst b/docs/devel/migration/postcopy.rst index 6c51e96d79..82e7a848c6 100644 --- a/docs/devel/migration/postcopy.rst +++ b/docs/devel/migration/postcopy.rst @@ -99,17 +99,6 @@ ADVISE->DISCARD->LISTEN->RUNNING->END (although it can't do the cleanup it would do as it finishes a normal migration). - - Paused - - Postcopy can run into a paused state (normally on both sides when - happens), where all threads will be temporarily halted mostly due to - network errors. When reaching paused state, migration will make sure - the qemu binary on both sides maintain the data without corrupting - the VM. To continue the migration, the admin needs to fix the - migration channel using the QMP command 'migrate-recover' on the - destination node, then resume the migration using QMP command 'migrate' - again on source node, with resume=true flag set. - - End The listen thread can now quit, and perform the cleanup of migration @@ -221,7 +210,8 @@ paused postcopy migration. The recovery phase normally contains a few steps: - - When network issue occurs, both QEMU will go into PAUSED state + - When network issue occurs, both QEMU will go into **POSTCOPY_PAUSED** + migration state. - When the network is recovered (or a new network is provided), the admin can setup the new channel for migration using QMP command @@ -229,9 +219,20 @@ The recovery phase normally contains a few steps: - On source host, the admin can continue the interrupted postcopy migration using QMP command 'migrate' with resume=true flag set. + Source QEMU will go into **POSTCOPY_RECOVER_SETUP** state trying to + re-establish the channels. - - After the connection is re-established, QEMU will continue the postcopy - migration on both sides. + - When both sides of QEMU successfully reconnect using a new or fixed up + channel, they will go into **POSTCOPY_RECOVER** state, some handshake + procedure will be needed to properly synchronize the VM states between + the two QEMUs to continue the postcopy migration. For example, there + can be pages sent right during the window when the network is + interrupted, then the handshake will guarantee pages lost in-flight + will be resent again. + + - After a proper handshake synchronization, QEMU will continue the + postcopy migration on both sides and go back to **POSTCOPY_ACTIVE** + state. Postcopy migration will continue. During a paused postcopy migration, the VM can logically still continue running, and it will not be impacted from any page access to pages that From 0fd397359540a6622c5f2164e76fc2cefd811f2a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:42 -0400 Subject: [PATCH 1542/2884] tests/migration-tests: Drop most WIN32 ifdefs for postcopy failure tests Most of them are not needed, we can stick with one ifdef inside postcopy_recover_fail() so as to cover the scm right tricks only. The tests won't run on windows anyway due to has_uffd always false. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 6207305ff8..b7dea1aabb 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1364,9 +1364,9 @@ static void wait_for_postcopy_status(QTestState *one, const char *status) "completed", NULL }); } -#ifndef _WIN32 static void postcopy_recover_fail(QTestState *from, QTestState *to) { +#ifndef _WIN32 int ret, pair1[2], pair2[2]; char c; @@ -1428,8 +1428,8 @@ static void postcopy_recover_fail(QTestState *from, QTestState *to) close(pair1[1]); close(pair2[0]); close(pair2[1]); +#endif } -#endif /* _WIN32 */ static void test_postcopy_recovery_common(MigrateCommon *args) { @@ -1469,7 +1469,6 @@ static void test_postcopy_recovery_common(MigrateCommon *args) wait_for_postcopy_status(to, "postcopy-paused"); wait_for_postcopy_status(from, "postcopy-paused"); -#ifndef _WIN32 if (args->postcopy_recovery_test_fail) { /* * Test when a wrong socket specified for recover, and then the @@ -1478,7 +1477,6 @@ static void test_postcopy_recovery_common(MigrateCommon *args) postcopy_recover_fail(from, to); /* continue with a good recovery */ } -#endif /* _WIN32 */ /* * Create a new socket to emulate a new channel that is different @@ -1507,7 +1505,6 @@ static void test_postcopy_recovery(void) test_postcopy_recovery_common(&args); } -#ifndef _WIN32 static void test_postcopy_recovery_double_fail(void) { MigrateCommon args = { @@ -1516,7 +1513,6 @@ static void test_postcopy_recovery_double_fail(void) test_postcopy_recovery_common(&args); } -#endif /* _WIN32 */ #ifdef CONFIG_GNUTLS static void test_postcopy_recovery_tls_psk(void) @@ -3782,10 +3778,8 @@ int main(int argc, char **argv) test_postcopy_preempt); migration_test_add("/migration/postcopy/preempt/recovery/plain", test_postcopy_preempt_recovery); -#ifndef _WIN32 migration_test_add("/migration/postcopy/recovery/double-failures", test_postcopy_recovery_double_fail); -#endif /* _WIN32 */ if (is_x86) { migration_test_add("/migration/postcopy/suspend", test_postcopy_suspend); From cd313b66f203381f2f2f984d5155d7942d26725d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:43 -0400 Subject: [PATCH 1543/2884] tests/migration-tests: Always enable migration events Libvirt should always enable it, so it'll be nice qtest also cover that for all tests on both sides. migrate_incoming_qmp() used to enable it only on dst, now we enable them on both, as we'll start to sanity check events even on the src QEMU. We'll need to leave the one in migrate_incoming_qmp(), because virtio-net-failover test uses that one only, and it relies on the events to work. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/migration-helpers.c | 1 + tests/qtest/migration-test.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 0ac49ceb54..2ca4425d71 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -258,6 +258,7 @@ void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) g_assert(!qdict_haskey(args, "uri")); qdict_put_str(args, "uri", uri); + /* This function relies on the event to work, make sure it's enabled */ migrate_set_capability(to, "events", true); rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index b7dea1aabb..32e31fff86 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -852,6 +852,13 @@ static int test_migrate_start(QTestState **from, QTestState **to, unlink(shmem_path); } + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(*from, "events", true); + migrate_set_capability(*to, "events", true); + return 0; } From d444e5673c223241bd2edbc207b02cc1b2114b71 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:44 -0400 Subject: [PATCH 1544/2884] tests/migration-tests: migration_event_wait() Introduce a small helper to wait for a migration event, generalized from the incoming migration path. Make the helper easier to use by allowing it to keep waiting until the expected event is received. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/migration-helpers.c | 31 ++++++++++++++++++++++--------- tests/qtest/migration-helpers.h | 2 ++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 2ca4425d71..84f49db85e 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -249,7 +249,7 @@ void migrate_set_capability(QTestState *who, const char *capability, void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) { va_list ap; - QDict *args, *rsp, *data; + QDict *args, *rsp; va_start(ap, fmt); args = qdict_from_vjsonf_nofail(fmt, ap); @@ -272,14 +272,7 @@ void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); - rsp = qtest_qmp_eventwait_ref(to, "MIGRATION"); - g_assert(qdict_haskey(rsp, "data")); - - data = qdict_get_qdict(rsp, "data"); - g_assert(qdict_haskey(data, "status")); - g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup"); - - qobject_unref(rsp); + migration_event_wait(to, "setup"); } /* @@ -518,3 +511,23 @@ bool probe_o_direct_support(const char *tmpfs) return true; } #endif + +/* + * Wait for a "MIGRATION" event. This is what Libvirt uses to track + * migration status changes. + */ +void migration_event_wait(QTestState *s, const char *target) +{ + QDict *response, *data; + const char *status; + bool found; + + do { + response = qtest_qmp_eventwait_ref(s, "MIGRATION"); + data = qdict_get_qdict(response, "data"); + g_assert(data); + status = qdict_get_str(data, "status"); + found = (strcmp(status, target) == 0); + qobject_unref(response); + } while (!found); +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 50095fca4a..72dba369fb 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -63,4 +63,6 @@ static inline bool probe_o_direct_support(const char *tmpfs) } #endif void migration_test_add(const char *path, void (*fn)(void)); +void migration_event_wait(QTestState *s, const char *target); + #endif /* MIGRATION_HELPERS_H */ From 35e71ec53581fc8a90dfff1565e4ba344945cf80 Mon Sep 17 00:00:00 2001 From: Shiva sagar Myana Date: Tue, 18 Jun 2024 16:22:20 +0100 Subject: [PATCH 1545/2884] hw/net/can/xlnx-versal-canfd: Fix sorting of the tx queue Returning an uint32_t casted to a gint from g_cmp_ids causes the tx queue to become wrongly sorted when executing g_slist_sort. Fix this by always returning -1 or 1 from g_cmp_ids based on the ID comparison instead. Also, if two message IDs are the same, sort them by using their index and transmit the message at the lowest index first. Signed-off-by: Shiva sagar Myana Reviewed-by: Francisco Iglesias Message-id: 20240603051732.3334571-1-Shivasagar.Myana@amd.com Signed-off-by: Peter Maydell --- hw/net/can/xlnx-versal-canfd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 47a14cfe63..5f083c21e9 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -1312,7 +1312,10 @@ static gint g_cmp_ids(gconstpointer data1, gconstpointer data2) tx_ready_reg_info *tx_reg_1 = (tx_ready_reg_info *) data1; tx_ready_reg_info *tx_reg_2 = (tx_ready_reg_info *) data2; - return tx_reg_1->can_id - tx_reg_2->can_id; + if (tx_reg_1->can_id == tx_reg_2->can_id) { + return (tx_reg_1->reg_num < tx_reg_2->reg_num) ? -1 : 1; + } + return (tx_reg_1->can_id < tx_reg_2->can_id) ? -1 : 1; } static void free_list(GSList *list) From 7edca16e745ca2105066832c9da34077af41347e Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Tue, 18 Jun 2024 16:22:20 +0100 Subject: [PATCH 1546/2884] hw/arm/sbsa-ref: switch to 1GHz timer frequency Updated firmware for QEMU CI is already in merge queue so we can move platform to be future proof. All supported cpus work fine with 1GHz timer frequency when firmware is fresh enough. Signed-off-by: Marcin Juszkiewicz Reviewed-by: Leif Lindholm Message-id: 20240531093729.220758-2-marcin.juszkiewicz@linaro.org Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index e884692f07..87884400e3 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -62,16 +62,12 @@ /* * Generic timer frequency in Hz (which drives both the CPU generic timers - * and the SBSA watchdog-timer). Older versions of the TF-A firmware - * typically used with sbsa-ref (including the binaries in our Avocado test - * Aarch64SbsarefMachine.test_sbsaref_alpine_linux_max_pauth_impdef - * assume it is this value. + * and the SBSA watchdog-timer). Older (<2.11) versions of the TF-A firmware + * assumed 62.5MHz here. * - * TODO: this value is not architecturally correct for an Armv8.6 or - * better CPU, so we should move to 1GHz once the TF-A fix above has - * made it into a release and into our Avocado test. + * Starting with Armv8.6 CPU 1GHz timer frequency is mandated. */ -#define SBSA_GTIMER_HZ 62500000 +#define SBSA_GTIMER_HZ 1000000000 enum { SBSA_FLASH, From 7175a562f157d39725ab396e39c1e8e410d206b3 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 18 Jun 2024 16:22:20 +0100 Subject: [PATCH 1547/2884] hw/intc/arm_gic: Fix deactivation of SPI lines Julien reported that he has seen strange behaviour when running Xen on QEMU using GICv2. When Xen migrates a guest's vCPU from one pCPU to another while the vCPU is handling an interrupt, the guest is unable to properly deactivate interrupts. Looking at it a little closer, our GICv2 model treats deactivation of SPI lines as if they were PPI's, i.e banked per CPU core. The state for active interrupts should only be banked for PPI lines, not for SPI lines. Make deactivation of SPI lines unbanked, similar to how we handle writes to GICD_ICACTIVER. Reported-by: Julien Grall Signed-off-by: Edgar E. Iglesias Message-id: 20240605143044.2029444-2-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/gic_internal.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 8d29b40ca1..8ddbf554c6 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -280,6 +280,8 @@ static inline void gic_set_active(GICState *s, int irq, int cpu) static inline void gic_clear_active(GICState *s, int irq, int cpu) { + unsigned int cm; + if (gic_is_vcpu(cpu)) { uint32_t *entry = gic_get_lr_entry(s, irq, cpu); GICH_LR_CLEAR_ACTIVE(*entry); @@ -301,11 +303,13 @@ static inline void gic_clear_active(GICState *s, int irq, int cpu) * the GIC is secure. */ if (!s->security_extn || GIC_DIST_TEST_GROUP(phys_irq, 1 << rcpu)) { - GIC_DIST_CLEAR_ACTIVE(phys_irq, 1 << rcpu); + cm = phys_irq < GIC_INTERNAL ? 1 << rcpu : ALL_CPU_MASK; + GIC_DIST_CLEAR_ACTIVE(phys_irq, cm); } } } else { - GIC_DIST_CLEAR_ACTIVE(irq, 1 << cpu); + cm = irq < GIC_INTERNAL ? 1 << cpu : ALL_CPU_MASK; + GIC_DIST_CLEAR_ACTIVE(irq, cm); } } From 9b113a09ff34857d463c130faa873c7b6fc4a004 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 18 Jun 2024 16:22:21 +0100 Subject: [PATCH 1548/2884] hw/arm/xilinx_zynq: Fix IRQ/FIQ routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the system bus interrupt line to CPU core assignment. Fixes: ddcf58e044ce0 ("hw/arm/xilinx_zynq: Support up to two CPU cores") Signed-off-by: Sebastian Huber Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240610052906.4432-1-sebastian.huber@embedded-brains.de Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 7f7a3d23fb..c79661bbc1 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -252,10 +252,11 @@ static void zynq_init(MachineState *machine) zynq_binfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; sysbus_create_varargs("l2x0", MPCORE_PERIPHBASE + 0x2000, NULL); for (n = 0; n < smp_cpus; n++) { + /* See "hw/intc/arm_gic.h" for the IRQ line association */ DeviceState *cpudev = DEVICE(zynq_machine->cpu[n]); - sysbus_connect_irq(busdev, (2 * n) + 0, + sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); - sysbus_connect_irq(busdev, (2 * n) + 1, + sysbus_connect_irq(busdev, smp_cpus + n, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); } From 057f7680f4ed1cc27a0520c0628bfb94f850c56a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 18 Jun 2024 16:22:21 +0100 Subject: [PATCH 1549/2884] scripts/coverity-scan/COMPONENTS.md: Update paths to match gitlab CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 83aa1baa069c we have been running the build for Coverity Scan as a Gitlab CI job, rather than the old setup where it was run on a local developer's machine. This is working well, but the absolute paths of files are different for the Gitlab CI job, which means that the regexes we use to identify Coverity components no longer work. With Gitlab CI builds the file paths are of the form /builds/qemu-project/qemu/accel/kvm/kvm-all.c rather than the old /qemu/accel/kvm/kvm-all.c and our regexes all don't match. Update all the regexes to start with .*/qemu/ . This will hopefully avoid the need to change them again in future if the build path changes again. This change was made with a search-and-replace of (/qemu)? to .*/qemu . Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240604145934.1230583-2-peter.maydell@linaro.org --- scripts/coverity-scan/COMPONENTS.md | 104 ++++++++++++++-------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 1537e49cd5..98d4bcd6a5 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -1,157 +1,157 @@ This is the list of currently configured Coverity components: alpha - ~ (/qemu)?((/include)?/hw/alpha/.*|/target/alpha/.*) + ~ .*/qemu((/include)?/hw/alpha/.*|/target/alpha/.*) arm - ~ (/qemu)?((/include)?/hw/arm/.*|(/include)?/hw/.*/(arm|allwinner-a10|bcm28|digic|exynos|imx|omap|stellaris|pxa2xx|versatile|zynq|cadence).*|/hw/net/xgmac.c|/hw/ssi/xilinx_spips.c|/target/arm/.*) + ~ .*/qemu((/include)?/hw/arm/.*|(/include)?/hw/.*/(arm|allwinner-a10|bcm28|digic|exynos|imx|omap|stellaris|pxa2xx|versatile|zynq|cadence).*|/hw/net/xgmac.c|/hw/ssi/xilinx_spips.c|/target/arm/.*) avr - ~ (/qemu)?((/include)?/hw/avr/.*|/target/avr/.*) + ~ .*/qemu((/include)?/hw/avr/.*|/target/avr/.*) cris - ~ (/qemu)?((/include)?/hw/cris/.*|/target/cris/.*) + ~ .*/qemu((/include)?/hw/cris/.*|/target/cris/.*) hexagon-gen (component should be ignored in analysis) - ~ (/qemu)?(/target/hexagon/.*generated.*) + ~ .*/qemu(/target/hexagon/.*generated.*) hexagon - ~ (/qemu)?(/target/hexagon/.*) + ~ .*/qemu(/target/hexagon/.*) hppa - ~ (/qemu)?((/include)?/hw/hppa/.*|/target/hppa/.*) + ~ .*/qemu((/include)?/hw/hppa/.*|/target/hppa/.*) i386 - ~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) + ~ .*/qemu((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) loongarch - ~ (/qemu)?((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*) + ~ .*/qemu((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*) m68k - ~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*) + ~ .*/qemu((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*) microblaze - ~ (/qemu)?((/include)?/hw/microblaze/.*|/target/microblaze/.*) + ~ .*/qemu((/include)?/hw/microblaze/.*|/target/microblaze/.*) mips - ~ (/qemu)?((/include)?/hw/mips/.*|/target/mips/.*) + ~ .*/qemu((/include)?/hw/mips/.*|/target/mips/.*) openrisc - ~ (/qemu)?((/include)?/hw/openrisc/.*|/target/openrisc/.*) + ~ .*/qemu((/include)?/hw/openrisc/.*|/target/openrisc/.*) ppc - ~ (/qemu)?((/include)?/hw/ppc/.*|/target/ppc/.*|/hw/pci-host/(uninorth.*|dec.*|prep.*|ppc.*)|/hw/misc/macio/.*|(/include)?/hw/.*/(xics|openpic|spapr).*) + ~ .*/qemu((/include)?/hw/ppc/.*|/target/ppc/.*|/hw/pci-host/(uninorth.*|dec.*|prep.*|ppc.*)|/hw/misc/macio/.*|(/include)?/hw/.*/(xics|openpic|spapr).*) riscv - ~ (/qemu)?((/include)?/hw/riscv/.*|/target/riscv/.*|/hw/.*/(riscv_|ibex_|sifive_).*) + ~ .*/qemu((/include)?/hw/riscv/.*|/target/riscv/.*|/hw/.*/(riscv_|ibex_|sifive_).*) rx - ~ (/qemu)?((/include)?/hw/rx/.*|/target/rx/.*) + ~ .*/qemu((/include)?/hw/rx/.*|/target/rx/.*) s390 - ~ (/qemu)?((/include)?/hw/s390x/.*|/target/s390x/.*|/hw/.*/s390_.*) + ~ .*/qemu((/include)?/hw/s390x/.*|/target/s390x/.*|/hw/.*/s390_.*) sh4 - ~ (/qemu)?((/include)?/hw/sh4/.*|/target/sh4/.*) + ~ .*/qemu((/include)?/hw/sh4/.*|/target/sh4/.*) sparc - ~ (/qemu)?((/include)?/hw/sparc(64)?.*|/target/sparc/.*|/hw/.*/grlib.*|/hw/display/cg3.c) + ~ .*/qemu((/include)?/hw/sparc(64)?.*|/target/sparc/.*|/hw/.*/grlib.*|/hw/display/cg3.c) tricore - ~ (/qemu)?((/include)?/hw/tricore/.*|/target/tricore/.*) + ~ .*/qemu((/include)?/hw/tricore/.*|/target/tricore/.*) xtensa - ~ (/qemu)?((/include)?/hw/xtensa/.*|/target/xtensa/.*) + ~ .*/qemu((/include)?/hw/xtensa/.*|/target/xtensa/.*) 9pfs - ~ (/qemu)?(/hw/9pfs/.*|/fsdev/.*) + ~ .*/qemu(/hw/9pfs/.*|/fsdev/.*) audio - ~ (/qemu)?((/include)?/(audio|hw/audio)/.*) + ~ .*/qemu((/include)?/(audio|hw/audio)/.*) block - ~ (/qemu)?(/block.*|(/include?)/(block|storage-daemon)/.*|(/include)?/hw/(block|ide|nvme)/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) + ~ .*/qemu(/block.*|(/include?)/(block|storage-daemon)/.*|(/include)?/hw/(block|ide|nvme)/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) char - ~ (/qemu)?(/qemu-char\.c|/include/sysemu/char\.h|(/include)?/hw/char/.*) + ~ .*/qemu(/qemu-char\.c|/include/sysemu/char\.h|(/include)?/hw/char/.*) crypto - ~ (/qemu)?((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*) + ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*) disas - ~ (/qemu)?((/include)?/disas.*) + ~ .*/qemu((/include)?/disas.*) fpu - ~ (/qemu)?((/include)?(/fpu|/libdecnumber)/.*) + ~ .*/qemu((/include)?(/fpu|/libdecnumber)/.*) io - ~ (/qemu)?((/include)?/io/.*) + ~ .*/qemu((/include)?/io/.*) ipmi - ~ (/qemu)?((/include)?/hw/ipmi/.*) + ~ .*/qemu((/include)?/hw/ipmi/.*) migration - ~ (/qemu)?((/include)?/migration/.*) + ~ .*/qemu((/include)?/migration/.*) monitor - ~ (/qemu)?(/qapi.*|/qobject/.*|/monitor\..*|/[hq]mp\..*) + ~ .*/qemu(/qapi.*|/qobject/.*|/monitor\..*|/[hq]mp\..*) nbd - ~ (/qemu)?(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) + ~ .*/qemu(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) net - ~ (/qemu)?((/include)?(/hw)?/(net|rdma)/.*) + ~ .*/qemu((/include)?(/hw)?/(net|rdma)/.*) pci - ~ (/qemu)?(/include)?/hw/(cxl/|pci).* + ~ .*/qemu(/include)?/hw/(cxl/|pci).* qemu-ga - ~ (/qemu)?(/qga/.*) + ~ .*/qemu(/qga/.*) scsi - ~ (/qemu)?(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) + ~ .*/qemu(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) trace - ~ (/qemu)?(/.*trace.*\.[ch]) + ~ .*/qemu(/.*trace.*\.[ch]) ui - ~ (/qemu)?((/include)?(/ui|/hw/display|/hw/input)/.*) + ~ .*/qemu((/include)?(/ui|/hw/display|/hw/input)/.*) usb - ~ (/qemu)?(/hw/usb/.*|/include/hw/usb/.*) + ~ .*/qemu(/hw/usb/.*|/include/hw/usb/.*) user - ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/user/.*) + ~ .*/qemu(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/user/.*) util - ~ (/qemu)?(/util/.*|/include/qemu/.*) + ~ .*/qemu(/util/.*|/include/qemu/.*) vfio - ~ (/qemu)?(/include)?/hw/vfio/.* + ~ .*/qemu(/include)?/hw/vfio/.* virtio - ~ (/qemu)?(/include)?/hw/virtio/.* + ~ .*/qemu(/include)?/hw/virtio/.* xen - ~ (/qemu)?(.*/xen.*) + ~ .*/qemu(.*/xen.*) hvf - ~ (/qemu)?(.*/hvf.*) + ~ .*/qemu(.*/hvf.*) kvm - ~ (/qemu)?(.*/kvm.*) + ~ .*/qemu(.*/kvm.*) tcg - ~ (/qemu)?(/accel/tcg|/replay|/tcg)/.* + ~ .*/qemu(/accel/tcg|/replay|/tcg)/.* sysemu - ~ (/qemu)?(/system/.*|/accel/.*) + ~ .*/qemu(/system/.*|/accel/.*) (headers) - ~ (/qemu)?(/include/.*) + ~ .*/qemu(/include/.*) testlibs - ~ (/qemu)?(/tests/qtest(/libqos/.*|/libqtest.*)) + ~ .*/qemu(/tests/qtest(/libqos/.*|/libqtest.*)) tests - ~ (/qemu)?(/tests/.*) + ~ .*/qemu(/tests/.*) From 7966cf71d36560024f07863fef26e265b2941f25 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 18 Jun 2024 16:22:21 +0100 Subject: [PATCH 1550/2884] scripts/coverity-scan/COMPONENTS.md: Fix 'char' component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'char' component: * includes the no-longer-present qemu-char.c, which has been long since split into the chardev/ backend code * also includes the hw/char devices Split it into two components: * char is the hw/char devices * chardev is the chardev backends with regexes matching our current sources. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240604145934.1230583-3-peter.maydell@linaro.org --- scripts/coverity-scan/COMPONENTS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 98d4bcd6a5..fb081a5926 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -73,7 +73,10 @@ block ~ .*/qemu(/block.*|(/include?)/(block|storage-daemon)/.*|(/include)?/hw/(block|ide|nvme)/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) char - ~ .*/qemu(/qemu-char\.c|/include/sysemu/char\.h|(/include)?/hw/char/.*) + ~ .*/qemu((/include)?/hw/char/.*) + +chardev + ~ .*/qemu((/include)?/chardev/.*) crypto ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*) From 9c43c934d1a998d46a4a15675950f0c5eccb6b4c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 18 Jun 2024 16:22:22 +0100 Subject: [PATCH 1551/2884] scripts/coverity-scan/COMPONENTS.md: Add crypto headers in host/include to the crypto component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit host/include/*/host/crypto/ are relatively new headers; add them to the crypto component. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240604145934.1230583-4-peter.maydell@linaro.org --- scripts/coverity-scan/COMPONENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index fb081a5926..205ab23b28 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -79,7 +79,7 @@ chardev ~ .*/qemu((/include)?/chardev/.*) crypto - ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*) + ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*|/host/include/.*/host/crypto/.*) disas ~ .*/qemu((/include)?/disas.*) From 8e055eab4fea581644cd6df28984f1a2a5fde08e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 18 Jun 2024 16:22:22 +0100 Subject: [PATCH 1552/2884] scripts/coverity-scan/COMPONENTS.md: Fix monitor component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the 'monitor' component: * qapi/ and monitor/ are now subdirectories * add job-qmp.c Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240604145934.1230583-5-peter.maydell@linaro.org --- scripts/coverity-scan/COMPONENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 205ab23b28..3864f8eda0 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -97,7 +97,7 @@ migration ~ .*/qemu((/include)?/migration/.*) monitor - ~ .*/qemu(/qapi.*|/qobject/.*|/monitor\..*|/[hq]mp\..*) + ~ .*/qemu((/include)?/(qapi|qobject|monitor)/.*|/job-qmp.c) nbd ~ .*/qemu(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) From 5d173f30f6828a1a4e6133eb324cc4ab0277a06d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 18 Jun 2024 16:22:22 +0100 Subject: [PATCH 1553/2884] scripts/coverity-scan/COMPONENTS.md: Include libqmp in testlibs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add libqmp to the testlibs component. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240604145934.1230583-6-peter.maydell@linaro.org --- scripts/coverity-scan/COMPONENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 3864f8eda0..858190be09 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -154,7 +154,7 @@ sysemu ~ .*/qemu(/include/.*) testlibs - ~ .*/qemu(/tests/qtest(/libqos/.*|/libqtest.*)) + ~ .*/qemu(/tests/qtest(/libqos/.*|/libqtest.*|/libqmp.*)) tests ~ .*/qemu(/tests/.*) From ff8aff01fa20c4fd5bbe46e1d25fbefdf996ef73 Mon Sep 17 00:00:00 2001 From: Zheyu Ma Date: Tue, 18 Jun 2024 16:40:09 +0200 Subject: [PATCH 1554/2884] hw/timer/a9gtimer: Handle QTest mode in a9_gtimer_get_current_cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates the a9_gtimer_get_current_cpu() function to handle cases where QTest is enabled. When QTest is used, it returns 0 instead of dereferencing the current_cpu, which can be NULL. This prevents the program from crashing during QTest runs. Reproducer: cat << EOF | qemu-system-aarch64 -display \ none -machine accel=qtest, -m 512M -machine npcm750-evb -qtest stdio writel 0xf03fe20c 0x26d7468c EOF Signed-off-by: Zheyu Ma Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240618144009.3137806-1-zheyuma97@gmail.com Signed-off-by: Peter Maydell --- hw/timer/a9gtimer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index a2ac5bdfb9..64d80cdf6a 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -32,6 +32,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "hw/core/cpu.h" +#include "sysemu/qtest.h" #ifndef A9_GTIMER_ERR_DEBUG #define A9_GTIMER_ERR_DEBUG 0 @@ -48,6 +49,10 @@ static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) { + if (qtest_enabled()) { + return 0; + } + if (current_cpu->cpu_index >= s->num_cpu) { hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n", s->num_cpu, current_cpu->cpu_index); From 813b59e8b8ab5d284672e0bbd79173df2e25e01d Mon Sep 17 00:00:00 2001 From: Zheyu Ma Date: Tue, 18 Jun 2024 15:56:10 +0200 Subject: [PATCH 1555/2884] hw/usb/hcd-dwc2: Handle invalid address access in read and write functions This commit modifies the dwc2_hsotg_read() and dwc2_hsotg_write() functions to handle invalid address access gracefully. Instead of using g_assert_not_reached(), which causes the program to abort, the functions now log an error message and return a default value for reads or do nothing for writes. This change prevents the program from aborting and provides clear log messages indicating when an invalid memory address is accessed. Reproducer: cat << EOF | qemu-system-aarch64 -display none \ -machine accel=qtest, -m 512M -machine raspi2b -m 1G -nodefaults \ -usb -drive file=null-co://,if=none,format=raw,id=disk0 -device \ usb-storage,port=1,drive=disk0 -qtest stdio readl 0x3f980dfb EOF Signed-off-by: Zheyu Ma Reviewed-by: Paul Zimmerman Message-id: 20240618135610.3109175-1-zheyuma97@gmail.com Signed-off-by: Peter Maydell --- hw/usb/hcd-dwc2.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index 8cac9c0a06..b4f0652c7d 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1128,7 +1128,10 @@ static uint64_t dwc2_hsotg_read(void *ptr, hwaddr addr, unsigned size) val = dwc2_pcgreg_read(ptr, addr, (addr - HSOTG_REG(0xe00)) >> 2, size); break; default: - g_assert_not_reached(); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, addr); + val = 0; + break; } return val; @@ -1160,7 +1163,9 @@ static void dwc2_hsotg_write(void *ptr, hwaddr addr, uint64_t val, dwc2_pcgreg_write(ptr, addr, (addr - HSOTG_REG(0xe00)) >> 2, val, size); break; default: - g_assert_not_reached(); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, addr); + break; } } From 9ed2fb65cc827904241dee189c801c10079c2fe3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Jun 2024 17:23:41 +0100 Subject: [PATCH 1556/2884] hw/arm/virt: Add serial aliases in DTB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there is more than one UART in the DTB, then there is no guarantee on which order a guest is supposed to initialise them. The standard solution to this is "serialN" entries in the "/aliases" node of the dtb which give the nodename of the UARTs. At the moment we only have two UARTs in the DTB when one is for the Secure world and one for the Non-Secure world, so this isn't really a problem. However if we want to add a second NS UART we'll need the aliases to ensure guests pick the right one. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240610162343.2131524-2-peter.maydell@linaro.org --- hw/arm/virt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c7a1f754e7..61a9d47c02 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -284,6 +284,8 @@ static void create_fdt(VirtMachineState *vms) } } + qemu_fdt_add_subnode(fdt, "/aliases"); + /* Clock node, for the benefit of the UART. The kernel device tree * binding documentation claims the PL011 node clock properties are * optional but in practice if you omit them the kernel refuses to @@ -939,7 +941,9 @@ static void create_uart(const VirtMachineState *vms, int uart, if (uart == VIRT_UART) { qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename); } else { + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial1", nodename); /* Mark as not usable by the normal world */ qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); From fe22cba940d82e93818135c044afed4099056628 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Jun 2024 17:23:42 +0100 Subject: [PATCH 1557/2884] hw/arm/virt: Rename VIRT_UART and VIRT_SECURE_UART to VIRT_UART[01] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're going to make the second UART not always a secure-only device. Rename the constants VIRT_UART and VIRT_SECURE_UART to VIRT_UART0 and VIRT_UART1 accordingly. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240610162343.2131524-3-peter.maydell@linaro.org --- hw/arm/virt-acpi-build.c | 12 ++++++------ hw/arm/virt.c | 14 +++++++------- include/hw/arm/virt.h | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index c3ccfef026..eb5796e309 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -440,10 +440,10 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) .base_addr.width = 32, .base_addr.offset = 0, .base_addr.size = 3, - .base_addr.addr = vms->memmap[VIRT_UART].base, + .base_addr.addr = vms->memmap[VIRT_UART0].base, .interrupt_type = (1 << 3),/* Bit[3] ARMH GIC interrupt*/ .pc_interrupt = 0, /* IRQ */ - .interrupt = (vms->irqmap[VIRT_UART] + ARM_SPI_BASE), + .interrupt = (vms->irqmap[VIRT_UART0] + ARM_SPI_BASE), .baud_rate = 3, /* 9600 */ .parity = 0, /* No Parity */ .stop_bits = 1, /* 1 Stop bit */ @@ -631,11 +631,11 @@ build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* BaseAddressRegister[] */ build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 32, 0, 3, - vms->memmap[VIRT_UART].base); + vms->memmap[VIRT_UART0].base); /* AddressSize[] */ build_append_int_noprefix(table_data, - vms->memmap[VIRT_UART].size, 4); + vms->memmap[VIRT_UART0].size, 4); /* NamespaceString[] */ g_array_append_vals(table_data, name, namespace_length); @@ -816,8 +816,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) */ scope = aml_scope("\\_SB"); acpi_dsdt_add_cpus(scope, vms); - acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], - (irqmap[VIRT_UART] + ARM_SPI_BASE)); + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], + (irqmap[VIRT_UART0] + ARM_SPI_BASE)); if (vmc->acpi_expose_flash) { acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 61a9d47c02..ffb4983885 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -165,11 +165,11 @@ static const MemMapEntry base_memmap[] = { [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 }, /* This redistributor space allows up to 2*64kB*123 CPUs */ [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, - [VIRT_UART] = { 0x09000000, 0x00001000 }, + [VIRT_UART0] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_GPIO] = { 0x09030000, 0x00001000 }, - [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 }, + [VIRT_UART1] = { 0x09040000, 0x00001000 }, [VIRT_SMMU] = { 0x09050000, 0x00020000 }, [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN }, [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN }, @@ -212,11 +212,11 @@ static MemMapEntry extended_memmap[] = { }; static const int a15irqmap[] = { - [VIRT_UART] = 1, + [VIRT_UART0] = 1, [VIRT_RTC] = 2, [VIRT_PCIE] = 3, /* ... to 6 */ [VIRT_GPIO] = 7, - [VIRT_SECURE_UART] = 8, + [VIRT_UART1] = 8, [VIRT_ACPI_GED] = 9, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ @@ -939,7 +939,7 @@ static void create_uart(const VirtMachineState *vms, int uart, qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); - if (uart == VIRT_UART) { + if (uart == VIRT_UART0) { qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename); } else { @@ -2317,11 +2317,11 @@ static void machvirt_init(MachineState *machine) fdt_add_pmu_nodes(vms); - create_uart(vms, VIRT_UART, sysmem, serial_hd(0)); + create_uart(vms, VIRT_UART0, sysmem, serial_hd(0)); if (vms->secure) { create_secure_ram(vms, secure_sysmem, secure_tag_sysmem); - create_uart(vms, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); + create_uart(vms, VIRT_UART1, secure_sysmem, serial_hd(1)); } if (tag_sysmem) { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index bb486d36b1..1227e7f7f0 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -59,7 +59,7 @@ enum { VIRT_GIC_ITS, VIRT_GIC_REDIST, VIRT_SMMU, - VIRT_UART, + VIRT_UART0, VIRT_MMIO, VIRT_RTC, VIRT_FW_CFG, @@ -69,7 +69,7 @@ enum { VIRT_PCIE_ECAM, VIRT_PLATFORM_BUS, VIRT_GPIO, - VIRT_SECURE_UART, + VIRT_UART1, VIRT_SECURE_MEM, VIRT_SECURE_GPIO, VIRT_PCDIMM_ACPI, From e7100972f2df313d1e47a0714aed968991437e86 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Jun 2024 17:23:43 +0100 Subject: [PATCH 1558/2884] hw/arm/virt: allow creation of a second NonSecure UART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some use-cases, it is helpful to have more than one UART available to the guest. If the second UART slot is not already used for a TrustZone Secure-World-only UART, create it as a NonSecure UART only when the user provides a serial backend (e.g. via a second -serial command line option). This avoids problems where existing guest software only expects a single UART, and gets confused by the second UART in the DTB. The major example of this is older EDK2 firmware, which will send the GRUB bootloader output to UART1 and the guest serial output to UART0. Users who want to use both UARTs with a guest setup including EDK2 are advised to update to EDK2 release edk2-stable202311 or newer. (The prebuilt EDK2 blobs QEMU upstream provides are new enough.) The relevant EDK2 changes are the ones described here: https://bugzilla.tianocore.org/show_bug.cgi?id=4577 Inspired-by: Axel Heider Signed-off-by: Peter Maydell Tested-by: Laszlo Ersek Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240610162343.2131524-4-peter.maydell@linaro.org --- docs/system/arm/virt.rst | 6 +++++- hw/arm/virt-acpi-build.c | 12 ++++++++---- hw/arm/virt.c | 38 +++++++++++++++++++++++++++++++++++--- include/hw/arm/virt.h | 1 + 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 26fcba00b7..e67e7f0f7c 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -26,7 +26,7 @@ The virt board supports: - PCI/PCIe devices - Flash memory -- One PL011 UART +- Either one or two PL011 UARTs for the NonSecure World - An RTC - The fw_cfg device that allows a guest to obtain data from QEMU - A PL061 GPIO controller @@ -48,6 +48,10 @@ The virt board supports: - A secure flash memory - 16MB of secure RAM +The second NonSecure UART only exists if a backend is configured +explicitly (e.g. with a second -serial command line option) and +TrustZone emulation is not enabled. + Supported guest CPU types: - ``cortex-a7`` (32-bit) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index eb5796e309..b2366f24f9 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -79,11 +79,11 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms) } static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, - uint32_t uart_irq) + uint32_t uart_irq, int uartidx) { - Aml *dev = aml_device("COM0"); + Aml *dev = aml_device("COM%d", uartidx); aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(uartidx))); Aml *crs = aml_resource_template(); aml_append(crs, aml_memory32_fixed(uart_memmap->base, @@ -817,7 +817,11 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) scope = aml_scope("\\_SB"); acpi_dsdt_add_cpus(scope, vms); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], - (irqmap[VIRT_UART0] + ARM_SPI_BASE)); + (irqmap[VIRT_UART0] + ARM_SPI_BASE), 0); + if (vms->second_ns_uart_present) { + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1], + (irqmap[VIRT_UART1] + ARM_SPI_BASE), 1); + } if (vmc->acpi_expose_flash) { acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ffb4983885..8555615256 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -906,7 +906,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) } static void create_uart(const VirtMachineState *vms, int uart, - MemoryRegion *mem, Chardev *chr) + MemoryRegion *mem, Chardev *chr, bool secure) { char *nodename; hwaddr base = vms->memmap[uart].base; @@ -944,6 +944,8 @@ static void create_uart(const VirtMachineState *vms, int uart, qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename); } else { qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial1", nodename); + } + if (secure) { /* Mark as not usable by the normal world */ qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); @@ -2317,11 +2319,41 @@ static void machvirt_init(MachineState *machine) fdt_add_pmu_nodes(vms); - create_uart(vms, VIRT_UART0, sysmem, serial_hd(0)); + /* + * The first UART always exists. If the security extensions are + * enabled, the second UART also always exists. Otherwise, it only exists + * if a backend is configured explicitly via '-serial '. + * This avoids potentially breaking existing user setups that expect + * only one NonSecure UART to be present (for instance, older EDK2 + * binaries). + * + * The nodes end up in the DTB in reverse order of creation, so we must + * create UART0 last to ensure it appears as the first node in the DTB, + * for compatibility with guest software that just iterates through the + * DTB to find the first UART, as older versions of EDK2 do. + * DTB readers that follow the spec, as Linux does, should honour the + * aliases node information and /chosen/stdout-path regardless of + * the order that nodes appear in the DTB. + * + * For similar back-compatibility reasons, if UART1 is the secure UART + * we create it second (and so it appears first in the DTB), because + * that's what QEMU has always done. + */ + if (!vms->secure) { + Chardev *serial1 = serial_hd(1); + + if (serial1) { + vms->second_ns_uart_present = true; + create_uart(vms, VIRT_UART1, sysmem, serial1, false); + } + } + create_uart(vms, VIRT_UART0, sysmem, serial_hd(0), false); + if (vms->secure) { + create_uart(vms, VIRT_UART1, secure_sysmem, serial_hd(1), true); + } if (vms->secure) { create_secure_ram(vms, secure_sysmem, secure_tag_sysmem); - create_uart(vms, VIRT_UART1, secure_sysmem, serial_hd(1)); } if (tag_sysmem) { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 1227e7f7f0..ab961bb6a9 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -151,6 +151,7 @@ struct VirtMachineState { bool ras; bool mte; bool dtb_randomness; + bool second_ns_uart_present; OnOffAuto acpi; VirtGICType gic_version; VirtIOMMUType iommu; From dda533087ad5559674ff486e7031c88dc01e0abd Mon Sep 17 00:00:00 2001 From: Zhenyu Zhang Date: Tue, 11 Jun 2024 22:05:06 -0400 Subject: [PATCH 1559/2884] hw/arm/virt: Avoid unexpected warning from Linux guest on host with Fujitsu CPUs Multiple warning messages and corresponding backtraces are observed when Linux guest is booted on the host with Fujitsu CPUs. One of them is shown as below. [ 0.032443] ------------[ cut here ]------------ [ 0.032446] uart-pl011 9000000.pl011: ARCH_DMA_MINALIGN smaller than CTR_EL0.CWG (128 < 256) [ 0.032454] WARNING: CPU: 0 PID: 1 at arch/arm64/mm/dma-mapping.c:54 arch_setup_dma_ops+0xbc/0xcc [ 0.032470] Modules linked in: [ 0.032475] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.0-452.el9.aarch64 [ 0.032481] Hardware name: linux,dummy-virt (DT) [ 0.032484] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.032490] pc : arch_setup_dma_ops+0xbc/0xcc [ 0.032496] lr : arch_setup_dma_ops+0xbc/0xcc [ 0.032501] sp : ffff80008003b860 [ 0.032503] x29: ffff80008003b860 x28: 0000000000000000 x27: ffffaae4b949049c [ 0.032510] x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000 [ 0.032517] x23: 0000000000000100 x22: 0000000000000000 x21: 0000000000000000 [ 0.032523] x20: 0000000100000000 x19: ffff2f06c02ea400 x18: ffffffffffffffff [ 0.032529] x17: 00000000208a5f76 x16: 000000006589dbcb x15: ffffaae4ba071c89 [ 0.032535] x14: 0000000000000000 x13: ffffaae4ba071c84 x12: 455f525443206e61 [ 0.032541] x11: 68742072656c6c61 x10: 0000000000000029 x9 : ffffaae4b7d21da4 [ 0.032547] x8 : 0000000000000029 x7 : 4c414e494d5f414d x6 : 0000000000000029 [ 0.032553] x5 : 000000000000000f x4 : ffffaae4b9617a00 x3 : 0000000000000001 [ 0.032558] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff2f06c029be40 [ 0.032564] Call trace: [ 0.032566] arch_setup_dma_ops+0xbc/0xcc [ 0.032572] of_dma_configure_id+0x138/0x300 [ 0.032591] amba_dma_configure+0x34/0xc0 [ 0.032600] really_probe+0x78/0x3dc [ 0.032614] __driver_probe_device+0x108/0x160 [ 0.032619] driver_probe_device+0x44/0x114 [ 0.032624] __device_attach_driver+0xb8/0x14c [ 0.032629] bus_for_each_drv+0x88/0xe4 [ 0.032634] __device_attach+0xb0/0x1e0 [ 0.032638] device_initial_probe+0x18/0x20 [ 0.032643] bus_probe_device+0xa8/0xb0 [ 0.032648] device_add+0x4b4/0x6c0 [ 0.032652] amba_device_try_add.part.0+0x48/0x360 [ 0.032657] amba_device_add+0x104/0x144 [ 0.032662] of_amba_device_create.isra.0+0x100/0x1c4 [ 0.032666] of_platform_bus_create+0x294/0x35c [ 0.032669] of_platform_populate+0x5c/0x150 [ 0.032672] of_platform_default_populate_init+0xd0/0xec [ 0.032697] do_one_initcall+0x4c/0x2e0 [ 0.032701] do_initcalls+0x100/0x13c [ 0.032707] kernel_init_freeable+0x1c8/0x21c [ 0.032712] kernel_init+0x28/0x140 [ 0.032731] ret_from_fork+0x10/0x20 [ 0.032735] ---[ end trace 0000000000000000 ]--- In Linux, a check is applied to every device which is exposed through device-tree node. The warning message is raised when the device isn't DMA coherent and the cache line size is larger than ARCH_DMA_MINALIGN (128 bytes). The cache line is sorted from CTR_EL0[CWG], which corresponds to 256 bytes on the guest CPUs. The DMA coherent capability is claimed through 'dma-coherent' in their device-tree nodes or parent nodes. This happens even when the device doesn't implement or use DMA at all, for legacy reasons. Fix the issue by adding 'dma-coherent' property to the device-tree root node, meaning all devices are capable of DMA coherent by default. This both suppresses the spurious kernel warnings and also guards against possible future QEMU bugs where we add a DMA-capable device and forget to mark it as dma-coherent. Signed-off-by: Zhenyu Zhang Reviewed-by: Gavin Shan Reviewed-by: Donald Dutile Message-id: 20240612020506.307793-1-zhenyzha@redhat.com [PMM: tweaked commit message] Signed-off-by: Peter Maydell --- hw/arm/virt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 8555615256..0784ee7f46 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -271,6 +271,17 @@ static void create_fdt(VirtMachineState *vms) qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); qemu_fdt_setprop_string(fdt, "/", "model", "linux,dummy-virt"); + /* + * For QEMU, all DMA is coherent. Advertising this in the root node + * has two benefits: + * + * - It avoids potential bugs where we forget to mark a DMA + * capable device as being dma-coherent + * - It avoids spurious warnings from the Linux kernel about + * devices which can't do DMA at all + */ + qemu_fdt_setprop(fdt, "/", "dma-coherent", NULL, 0); + /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); if (vms->dtb_randomness) { From d338b5a80922433a2c4a63986b41f82ae4137dfc Mon Sep 17 00:00:00 2001 From: Zheyu Ma Date: Tue, 18 Jun 2024 18:37:01 +0200 Subject: [PATCH 1560/2884] hw/misc: Set valid access size for Exynos4210 RNG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Exynos4210 RNG module requires 32-bit (4-byte) accesses to its registers. According to the User Manual Section 25.3[1], the registers for RNG operations are 32-bit. This change ensures that the memory region operations for the RNG module enforce the correct access sizes, preventing invalid memory accesses. [1] http://www.mediafire.com/view/8ly2fqls3c9c31c/Exynos_4412_SCP_Users_Manual_Ver.0.10.00_Preliminary0.pdf Reproducer: cat << EOF | qemu-system-aarch64 -display none \ -machine accel=qtest, -m 512M -machine smdkc210 -qtest stdio readb 0x10830454 EOF Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Zheyu Ma Message-id: 20240618163701.3204975-1-zheyuma97@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/exynos4210_rng.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c index 0756bd3205..674d8eece5 100644 --- a/hw/misc/exynos4210_rng.c +++ b/hw/misc/exynos4210_rng.c @@ -217,6 +217,8 @@ static const MemoryRegionOps exynos4210_rng_ops = { .read = exynos4210_rng_read, .write = exynos4210_rng_write, .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, }; static void exynos4210_rng_reset(DeviceState *dev) From 53aaa88105e2f9cbef3a4d17df007dbdf985d6e2 Mon Sep 17 00:00:00 2001 From: David Hubbard Date: Mon, 20 May 2024 18:26:34 -0500 Subject: [PATCH 1561/2884] hw/usb/hcd-ohci: Fix ohci_service_td: accept zero-length TDs where CBP=BE+1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the way the ohci emulation handles a Transfer Descriptor with "Buffer End" set to "Current Buffer Pointer" - 1, specifically in the case of a zero-length packet. The OHCI spec 4.3.1.2 Table 4-2 specifies td.cbp to be zero for a zero-length packet. Peter Maydell tracked down commit 1328fe0c32 (hw: usb: hcd-ohci: check len and frame_number variables) where qemu started checking this according to the spec. What this patch does is loosen the qemu ohci implementation to allow a zero-length packet if td.be (Buffer End) is set to td.cbp - 1, and with a non-zero td.cbp value. The spec is unclear whether this is valid or not -- it is not the clearly documented way to send a zero length TD (which is CBP=BE=0), but it isn't specifically forbidden. Actual hw seems to be ok with it. Does any OS rely on this behavior? There have been no reports to qemu-devel of this problem. This is attempting to have qemu behave like actual hardware, but this is just a minor change. With a tiny OS[1] that boots and executes a test, the issue can be seen: * OS that sends USB requests to a USB mass storage device but sends td.cbp = td.be + 1 * qemu 4.2 * qemu HEAD (4e66a0854) * Actual OHCI controller (hardware) Command line: qemu-system-x86_64 -m 20 \ -device pci-ohci,id=ohci \ -drive if=none,format=raw,id=d,file=testmbr.raw \ -device usb-storage,bus=ohci.0,drive=d \ --trace "usb_*" --trace "ohci_*" -D qemu.log Results are: qemu 4.2 | qemu HEAD | actual HW -----------+------------+----------- works fine | ohci_die() | works fine Tip: if the flags "-serial pty -serial stdio" are added to the command line the test will output USB requests like this: Testing qemu HEAD: > Free mem 2M ohci port2 conn FS > setup { 80 6 0 1 0 0 8 0 } > ED info=80000 { mps=8 en=0 d=0 } tail=c20920 > td0 c20880 nxt=c20960 f2000000 setup cbp=c20900 be=c20907 > td1 c20960 nxt=c20980 f3140000 in cbp=c20908 be=c2090f > td2 c20980 nxt=c20920 f3080000 out cbp=c20910 be=c2090f ohci20 host err > usb stopped And in qemu.log: usb_ohci_iso_td_bad_cc_overrun ISO_TD start_offset=0x00c20910 > next_offset=0x00c2090f Testing qemu 4.2: > Free mem 2M ohci port2 conn FS > setup { 80 6 0 1 0 0 8 0 } > ED info=80000 { mps=8 en=0 d=0 } tail=620920 > td0 620880 nxt=620960 f2000000 setup cbp=620900 be=620907 cbp=0 be=620907 > td1 620960 nxt=620980 f3140000 in cbp=620908 be=62090f cbp=0 be=62090f > td2 620980 nxt=620920 f3080000 out cbp=620910 be=62090f cbp=0 be=62090f > rx { 12 1 0 2 0 0 0 8 } > setup { 0 5 1 0 0 0 0 0 } tx {} > ED info=80000 { mps=8 en=0 d=0 } tail=620880 > td0 620920 nxt=620960 f2000000 setup cbp=620900 be=620907 cbp=0 be=620907 > td1 620960 nxt=620880 f3100000 in cbp=620908 be=620907 cbp=0 be=620907 > setup { 80 6 0 1 0 0 12 0 } > ED info=80001 { mps=8 en=0 d=1 } tail=620960 > td0 620880 nxt=6209c0 f2000000 setup cbp=620920 be=620927 cbp=0 be=620927 > td1 6209c0 nxt=6209e0 f3140000 in cbp=620928 be=620939 cbp=0 be=620939 > td2 6209e0 nxt=620960 f3080000 out cbp=62093a be=620939 cbp=0 be=620939 > rx { 12 1 0 2 0 0 0 8 f4 46 1 0 0 0 1 2 3 1 } > setup { 80 6 0 2 0 0 0 1 } > ED info=80001 { mps=8 en=0 d=1 } tail=620880 > td0 620960 nxt=6209a0 f2000000 setup cbp=620a20 be=620a27 cbp=0 be=620a27 > td1 6209a0 nxt=6209c0 f3140004 in cbp=620a28 be=620b27 cbp=620a48 be=620b27 > td2 6209c0 nxt=620880 f3080000 out cbp=620b28 be=620b27 cbp=0 be=620b27 > rx { 9 2 20 0 1 1 4 c0 0 9 4 0 0 2 8 6 50 0 7 5 81 2 40 0 0 7 5 2 2 40 0 0 } > setup { 0 9 1 0 0 0 0 0 } tx {} > ED info=80001 { mps=8 en=0 d=1 } tail=620900 > td0 620880 nxt=620940 f2000000 setup cbp=620a00 be=620a07 cbp=0 be=620a07 > td1 620940 nxt=620900 f3100000 in cbp=620a08 be=620a07 cbp=0 be=620a07 [1] The OS disk image has been emailed to philmd@linaro.org, mjt@tls.msk.ru, and kraxel@redhat.com: * testCbpOffBy1.img.xz * sha256: f87baddcb86de845de12f002c698670a426affb40946025cc32694f9daa3abed Signed-off-by: David Hubbard Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/usb/hcd-ohci.c | 4 ++-- hw/usb/trace-events | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index acd6016980..71b54914d3 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -941,8 +941,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) { len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff); } else { - if (td.cbp > td.be) { - trace_usb_ohci_iso_td_bad_cc_overrun(td.cbp, td.be); + if (td.cbp - 1 > td.be) { /* rely on td.cbp != 0 */ + trace_usb_ohci_td_bad_buf(td.cbp, td.be); ohci_die(ohci); return 1; } diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 46732717a9..dd04f14add 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -29,6 +29,7 @@ usb_ohci_iso_td_data_underrun(int ret) "DataUnderrun %d" usb_ohci_iso_td_nak(int ret) "got NAK/STALL %d" usb_ohci_iso_td_bad_response(int ret) "Bad device response %d" usb_ohci_td_bad_pid(const char *s, uint32_t edf, uint32_t tdf) "Bad pid %s: ed.flags 0x%x td.flags 0x%x" +usb_ohci_td_bad_buf(uint32_t cbp, uint32_t be) "Bad cbp = 0x%x > be = 0x%x" usb_ohci_port_attach(int index) "port #%d" usb_ohci_port_detach(int index) "port #%d" usb_ohci_port_wakeup(int index) "port #%d" From 3b36cead6ecc0e40edb8b2f3e253baa01ebc1e9a Mon Sep 17 00:00:00 2001 From: Xiong Yining Date: Fri, 7 Jun 2024 10:38:25 +0000 Subject: [PATCH 1562/2884] hw/arm/sbsa-ref: Enable CPU cluster on ARM sbsa machine Enable CPU cluster support on SbsaQemu platform, so that users can specify a 4-level CPU hierarchy sockets/clusters/cores/threads. And this topology can be passed to the firmware through /cpus/topology Device Tree. Signed-off-by: Xiong Yining Reviewed-by: Marcin Juszkiewicz Reviewed-by: Leif Lindholm Message-id: 20240607103825.1295328-2-xiongyining1480@phytium.com.cn Tested-by: Marcin Juszkiewicz Signed-off-by: Peter Maydell --- docs/system/arm/sbsa.rst | 4 ++++ hw/arm/sbsa-ref.c | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/sbsa.rst b/docs/system/arm/sbsa.rst index 2bf22a1d0b..2bf3fc8d59 100644 --- a/docs/system/arm/sbsa.rst +++ b/docs/system/arm/sbsa.rst @@ -62,6 +62,7 @@ The devicetree reports: - platform version - GIC addresses - NUMA node id for CPUs and memory + - CPU topology information Platform version '''''''''''''''' @@ -88,3 +89,6 @@ Platform version changes: 0.3 The USB controller is an XHCI device, not EHCI. + +0.4 + CPU topology information is present in devicetree. diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 87884400e3..ae37a92301 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -219,7 +219,7 @@ static void create_fdt(SBSAMachineState *sms) * fw compatibility. */ qemu_fdt_setprop_cell(fdt, "/", "machine-version-major", 0); - qemu_fdt_setprop_cell(fdt, "/", "machine-version-minor", 3); + qemu_fdt_setprop_cell(fdt, "/", "machine-version-minor", 4); if (ms->numa_state->have_numa_distance) { int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t); @@ -276,6 +276,14 @@ static void create_fdt(SBSAMachineState *sms) g_free(nodename); } + /* Add CPU topology description through fdt node topology. */ + qemu_fdt_add_subnode(sms->fdt, "/cpus/topology"); + + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "sockets", ms->smp.sockets); + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "clusters", ms->smp.clusters); + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "cores", ms->smp.cores); + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "threads", ms->smp.threads); + sbsa_fdt_add_gic_node(sms); } @@ -898,6 +906,7 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1 * GiB; mc->default_ram_id = "sbsa-ref.ram"; mc->default_cpus = 4; + mc->smp_props.clusters_supported = true; mc->possible_cpu_arch_ids = sbsa_ref_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = sbsa_ref_cpu_index_to_props; mc->get_default_cpu_node_id = sbsa_ref_get_default_cpu_node_id; From 8dbd24d3aa6d67b2d3576da016fb631fd1edfc2c Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:45 -0400 Subject: [PATCH 1563/2884] tests/migration-tests: Verify postcopy-recover-setup status Making sure the postcopy-recover-setup status is present in the postcopy failure unit test. Note that it only applies to src QEMU not dest. Signed-off-by: Peter Xu Reviewed-by: Fabiano Rosas Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 32e31fff86..e61096adfe 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1413,6 +1413,12 @@ static void postcopy_recover_fail(QTestState *from, QTestState *to) migrate_recover(to, "fd:fd-mig"); migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); + /* + * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. + * Make sure it appears along the way. + */ + migration_event_wait(from, "postcopy-recover-setup"); + /* * Make sure both QEMU instances will go into RECOVER stage, then test * kicking them out using migrate-pause. From 6cf56a87baf8b99c4296a943d220eb8276ca035a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jun 2024 18:30:46 -0400 Subject: [PATCH 1564/2884] tests/migration-tests: Cover postcopy failure on reconnect Make sure there will be an event for postcopy recovery, irrelevant of whether the reconnect will success, or when the failure happens. The added new case is to fail early in postcopy recovery, in which case it didn't even reach RECOVER stage on src (and in real life it'll be the same to dest, but the test case is just slightly more involved due to the dual socketpair setup). To do that, rename the postcopy_recovery_test_fail to reflect either stage to fail, instead of a boolean. Reviewed-by: Fabiano Rosas Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- tests/qtest/migration-test.c | 95 +++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index e61096adfe..571fc1334c 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -73,6 +73,17 @@ static QTestMigrationState dst_state; #define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" #define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" +typedef enum PostcopyRecoveryFailStage { + /* + * "no failure" must be 0 as it's the default. OTOH, real failure + * cases must be >0 to make sure they trigger by a "if" test. + */ + POSTCOPY_FAIL_NONE = 0, + POSTCOPY_FAIL_CHANNEL_ESTABLISH, + POSTCOPY_FAIL_RECOVERY, + POSTCOPY_FAIL_MAX +} PostcopyRecoveryFailStage; + #if defined(__linux__) #include #include @@ -693,7 +704,7 @@ typedef struct { /* Postcopy specific fields */ void *postcopy_data; bool postcopy_preempt; - bool postcopy_recovery_test_fail; + PostcopyRecoveryFailStage postcopy_recovery_fail_stage; } MigrateCommon; static int test_migrate_start(QTestState **from, QTestState **to, @@ -1371,12 +1382,16 @@ static void wait_for_postcopy_status(QTestState *one, const char *status) "completed", NULL }); } -static void postcopy_recover_fail(QTestState *from, QTestState *to) +static void postcopy_recover_fail(QTestState *from, QTestState *to, + PostcopyRecoveryFailStage stage) { #ifndef _WIN32 + bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); int ret, pair1[2], pair2[2]; char c; + g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); + /* Create two unrelated socketpairs */ ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); g_assert_cmpint(ret, ==, 0); @@ -1410,6 +1425,14 @@ static void postcopy_recover_fail(QTestState *from, QTestState *to) ret = send(pair2[1], &c, 1, 0); g_assert_cmpint(ret, ==, 1); + if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + /* + * This will make src QEMU to fail at an early stage when trying to + * resume later, where it shouldn't reach RECOVER stage at all. + */ + close(pair1[1]); + } + migrate_recover(to, "fd:fd-mig"); migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); @@ -1419,28 +1442,53 @@ static void postcopy_recover_fail(QTestState *from, QTestState *to) */ migration_event_wait(from, "postcopy-recover-setup"); + if (fail_early) { + /* + * When fails at reconnection, src QEMU will automatically goes + * back to PAUSED state. Making sure there is an event in this + * case: Libvirt relies on this to detect early reconnection + * errors. + */ + migration_event_wait(from, "postcopy-paused"); + } else { + /* + * We want to test "fail later" at RECOVER stage here. Make sure + * both QEMU instances will go into RECOVER stage first, then test + * kicking them out using migrate-pause. + * + * Explicitly check the RECOVER event on src, that's what Libvirt + * relies on, rather than polling. + */ + migration_event_wait(from, "postcopy-recover"); + wait_for_postcopy_status(from, "postcopy-recover"); + + /* Need an explicit kick on src QEMU in this case */ + migrate_pause(from); + } + /* - * Make sure both QEMU instances will go into RECOVER stage, then test - * kicking them out using migrate-pause. + * For all failure cases, we'll reach such states on both sides now. + * Check them. */ - wait_for_postcopy_status(from, "postcopy-recover"); + wait_for_postcopy_status(from, "postcopy-paused"); wait_for_postcopy_status(to, "postcopy-recover"); /* - * This would be issued by the admin upon noticing the hang, we should - * make sure we're able to kick this out. + * Kick dest QEMU out too. This is normally not needed in reality + * because when the channel is shutdown it should also happen on src. + * However here we used separate socket pairs so we need to do that + * explicitly. */ - migrate_pause(from); - wait_for_postcopy_status(from, "postcopy-paused"); - - /* Do the same test on dest */ migrate_pause(to); wait_for_postcopy_status(to, "postcopy-paused"); close(pair1[0]); - close(pair1[1]); close(pair2[0]); close(pair2[1]); + + if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + close(pair1[1]); + } #endif } @@ -1482,12 +1530,12 @@ static void test_postcopy_recovery_common(MigrateCommon *args) wait_for_postcopy_status(to, "postcopy-paused"); wait_for_postcopy_status(from, "postcopy-paused"); - if (args->postcopy_recovery_test_fail) { + if (args->postcopy_recovery_fail_stage) { /* * Test when a wrong socket specified for recover, and then the * ability to kick it out, and continue with a correct socket. */ - postcopy_recover_fail(from, to); + postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); /* continue with a good recovery */ } @@ -1518,10 +1566,19 @@ static void test_postcopy_recovery(void) test_postcopy_recovery_common(&args); } -static void test_postcopy_recovery_double_fail(void) +static void test_postcopy_recovery_fail_handshake(void) { MigrateCommon args = { - .postcopy_recovery_test_fail = true, + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_reconnect(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, }; test_postcopy_recovery_common(&args); @@ -3791,8 +3848,10 @@ int main(int argc, char **argv) test_postcopy_preempt); migration_test_add("/migration/postcopy/preempt/recovery/plain", test_postcopy_preempt_recovery); - migration_test_add("/migration/postcopy/recovery/double-failures", - test_postcopy_recovery_double_fail); + migration_test_add("/migration/postcopy/recovery/double-failures/handshake", + test_postcopy_recovery_fail_handshake); + migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", + test_postcopy_recovery_fail_reconnect); if (is_x86) { migration_test_add("/migration/postcopy/suspend", test_postcopy_suspend); From 04b09de16d78cf2d163ca65d7c6d161bf2baceb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Jun 2024 09:03:17 +0200 Subject: [PATCH 1565/2884] migration: Remove unused VMSTATE_ARRAY_TEST() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last use of VMSTATE_ARRAY_TEST() was removed in commit 46baa9007f ("migration/i386: Remove old non-softfloat 64bit FP support"), we can safely get rid of it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Li Zhijian Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas --- include/migration/vmstate.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 294d2d8486..f313f2f408 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -388,16 +388,6 @@ extern const VMStateInfo vmstate_info_qlist; .offset = vmstate_offset_varray(_state, _field, _type), \ } -#define VMSTATE_ARRAY_TEST(_field, _state, _num, _test, _info, _type) {\ - .name = (stringify(_field)), \ - .field_exists = (_test), \ - .num = (_num), \ - .info = &(_info), \ - .size = sizeof(_type), \ - .flags = VMS_ARRAY, \ - .offset = vmstate_offset_array(_state, _field, _type, _num),\ -} - #define VMSTATE_SUB_ARRAY(_field, _state, _start, _num, _version, _info, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ From 9bf21277c4edabaef3346e2cb566040331fbc6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 May 2024 19:01:01 +0200 Subject: [PATCH 1566/2884] hw/s390x/ccw: Make s390_ccw_get_dev_info() return a bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since s390_ccw_get_dev_info() takes an 'Error **' argument, best practices suggest to return a bool. See the qapi/error.h Rules section. While at it, modify the call in s390_ccw_realize(). Signed-off-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-2-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-ccw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 5261e66724..a06e91dfb3 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -71,7 +71,7 @@ IOInstEnding s390_ccw_store(SubchDev *sch) return ret; } -static void s390_ccw_get_dev_info(S390CCWDevice *cdev, +static bool s390_ccw_get_dev_info(S390CCWDevice *cdev, char *sysfsdev, Error **errp) { @@ -84,12 +84,12 @@ static void s390_ccw_get_dev_info(S390CCWDevice *cdev, error_setg(errp, "No host device provided"); error_append_hint(errp, "Use -device vfio-ccw,sysfsdev=PATH_TO_DEVICE\n"); - return; + return false; } if (!realpath(sysfsdev, dev_path)) { error_setg_errno(errp, errno, "Host device '%s' not found", sysfsdev); - return; + return false; } cdev->mdevid = g_path_get_basename(dev_path); @@ -98,13 +98,14 @@ static void s390_ccw_get_dev_info(S390CCWDevice *cdev, tmp = g_path_get_basename(tmp_dir); if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) { error_setg_errno(errp, errno, "Failed to read %s", tmp); - return; + return false; } cdev->hostid.cssid = cssid; cdev->hostid.ssid = ssid; cdev->hostid.devid = devid; cdev->hostid.valid = true; + return true; } static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) @@ -116,8 +117,7 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) int ret; Error *err = NULL; - s390_ccw_get_dev_info(cdev, sysfsdev, &err); - if (err) { + if (!s390_ccw_get_dev_info(cdev, sysfsdev, &err)) { goto out_err_propagate; } From 4a6a90f30fa3b2973bbb6faf985466b4338f903f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 May 2024 19:01:02 +0200 Subject: [PATCH 1567/2884] s390x/css: Make CCWDeviceClass::realize return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the realize() handler of CCWDeviceClass takes an 'Error **' argument, best practices suggest to return a bool. See the api/error.h Rules section. While at it, modify the call in s390_ccw_realize(). Signed-off-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-3-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/ccw-device.c | 3 ++- hw/s390x/ccw-device.h | 2 +- hw/s390x/s390-ccw.c | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index fb8c1acc64..a7d682e5af 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -31,9 +31,10 @@ static void ccw_device_refill_ids(CcwDevice *dev) dev->subch_id.valid = true; } -static void ccw_device_realize(CcwDevice *dev, Error **errp) +static bool ccw_device_realize(CcwDevice *dev, Error **errp) { ccw_device_refill_ids(dev); + return true; } static Property ccw_device_properties[] = { diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 6dff95225d..5feeb0ee7a 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -36,7 +36,7 @@ extern const VMStateDescription vmstate_ccw_dev; struct CCWDeviceClass { DeviceClass parent_class; void (*unplug)(HotplugHandler *, DeviceState *, Error **); - void (*realize)(CcwDevice *, Error **); + bool (*realize)(CcwDevice *, Error **); void (*refill_ids)(CcwDevice *); }; diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index a06e91dfb3..4b8ede701d 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -137,8 +137,7 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) goto out_err; } - ck->realize(ccw_dev, &err); - if (err) { + if (!ck->realize(ccw_dev, &err)) { goto out_err; } From 19a1740fd3eb31ffc5b73ed101f1e3847c546631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 May 2024 19:01:03 +0200 Subject: [PATCH 1568/2884] hw/s390x/ccw: Remove local Error variable from s390_ccw_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the 'Error **errp' argument of s390_ccw_realize() instead and remove the error_propagate() call. Signed-off-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-4-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-ccw.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 4b8ede701d..b3d14c61d7 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -115,13 +115,12 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) DeviceState *parent = DEVICE(ccw_dev); SubchDev *sch; int ret; - Error *err = NULL; - if (!s390_ccw_get_dev_info(cdev, sysfsdev, &err)) { - goto out_err_propagate; + if (!s390_ccw_get_dev_info(cdev, sysfsdev, errp)) { + return; } - sch = css_create_sch(ccw_dev->devno, &err); + sch = css_create_sch(ccw_dev->devno, errp); if (!sch) { goto out_mdevid_free; } @@ -132,12 +131,12 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) ccw_dev->sch = sch; ret = css_sch_build_schib(sch, &cdev->hostid); if (ret) { - error_setg_errno(&err, -ret, "%s: Failed to build initial schib", + error_setg_errno(errp, -ret, "%s: Failed to build initial schib", __func__); goto out_err; } - if (!ck->realize(ccw_dev, &err)) { + if (!ck->realize(ccw_dev, errp)) { goto out_err; } @@ -151,8 +150,6 @@ out_err: g_free(sch); out_mdevid_free: g_free(cdev->mdevid); -out_err_propagate: - error_propagate(errp, err); } static void s390_ccw_unrealize(S390CCWDevice *cdev) From 45f4218784ce20b2c303dd07b1fa3aa6838eca73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 May 2024 19:01:04 +0200 Subject: [PATCH 1569/2884] s390x/css: Make S390CCWDeviceClass::realize return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the realize() handler of S390CCWDeviceClass takes an 'Error **' argument, best practices suggest to return a bool. See the api/error.h Rules section. While at it, modify the call in vfio_ccw_realize(). Signed-off-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-5-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-ccw.c | 7 ++++--- hw/vfio/ccw.c | 3 +-- include/hw/s390x/s390-ccw.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index b3d14c61d7..3c09750550 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -108,7 +108,7 @@ static bool s390_ccw_get_dev_info(S390CCWDevice *cdev, return true; } -static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) +static bool s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) { CcwDevice *ccw_dev = CCW_DEVICE(cdev); CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); @@ -117,7 +117,7 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) int ret; if (!s390_ccw_get_dev_info(cdev, sysfsdev, errp)) { - return; + return false; } sch = css_create_sch(ccw_dev->devno, errp); @@ -142,7 +142,7 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, parent->hotplugged, 1); - return; + return true; out_err: css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); @@ -150,6 +150,7 @@ out_err: g_free(sch); out_mdevid_free: g_free(cdev->mdevid); + return false; } static void s390_ccw_unrealize(S390CCWDevice *cdev) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 2600e62e37..9a8e052711 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -582,8 +582,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) /* Call the class init function for subchannel. */ if (cdc->realize) { - cdc->realize(cdev, vcdev->vdev.sysfsdev, &err); - if (err) { + if (!cdc->realize(cdev, vcdev->vdev.sysfsdev, &err)) { goto out_err_propagate; } } diff --git a/include/hw/s390x/s390-ccw.h b/include/hw/s390x/s390-ccw.h index 2c807ee3a1..2e0a709981 100644 --- a/include/hw/s390x/s390-ccw.h +++ b/include/hw/s390x/s390-ccw.h @@ -31,7 +31,7 @@ struct S390CCWDevice { struct S390CCWDeviceClass { CCWDeviceClass parent_class; - void (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp); + bool (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp); void (*unrealize)(S390CCWDevice *dev); IOInstEnding (*handle_request) (SubchDev *sch); int (*handle_halt) (SubchDev *sch); From 1aeebbd6213af1aef09b4906c487f9079a5d8947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 May 2024 19:01:05 +0200 Subject: [PATCH 1570/2884] vfio/ccw: Use the 'Error **errp' argument of vfio_ccw_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The local error variable is kept for vfio_ccw_register_irq_notifier() because it is not considered as a failing condition. We will change how error reporting is done in following changes. Remove the error_propagate() call. Cc: Zhenzhong Duan Signed-off-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-6-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/vfio/ccw.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 9a8e052711..a468fa2342 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -582,8 +582,8 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) /* Call the class init function for subchannel. */ if (cdc->realize) { - if (!cdc->realize(cdev, vcdev->vdev.sysfsdev, &err)) { - goto out_err_propagate; + if (!cdc->realize(cdev, vcdev->vdev.sysfsdev, errp)) { + return; } } @@ -596,17 +596,17 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_attach_dev_err; } - if (!vfio_ccw_get_region(vcdev, &err)) { + if (!vfio_ccw_get_region(vcdev, errp)) { goto out_region_err; } - if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err)) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, errp)) { goto out_io_notifier_err; } if (vcdev->crw_region) { if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, - &err)) { + errp)) { goto out_irq_notifier_err; } } @@ -634,8 +634,6 @@ out_attach_dev_err: if (cdc->unrealize) { cdc->unrealize(cdev); } -out_err_propagate: - error_propagate(errp, err); } static void vfio_ccw_unrealize(DeviceState *dev) From fa8053841efebab7fcdc76252b953f8dafe7a343 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 22 May 2024 19:01:06 +0200 Subject: [PATCH 1571/2884] vfio/ccw: Fix the missed unrealize() call in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When get name failed, we should call unrealize() so that vfio_ccw_realize() is self contained. Fixes: 909a6254eda ("vfio/ccw: Make vfio cdev pre-openable by passing a file handle") Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-7-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/vfio/ccw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index a468fa2342..36f2677a44 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -588,7 +588,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) } if (!vfio_device_get_name(vbasedev, errp)) { - return; + goto out_unrealize; } if (!vfio_attach_device(cdev->mdevid, vbasedev, @@ -631,6 +631,7 @@ out_region_err: vfio_detach_device(vbasedev); out_attach_dev_err: g_free(vbasedev->name); +out_unrealize: if (cdc->unrealize) { cdc->unrealize(cdev); } From d48a54042f25d6f1fe442e3a524b1f2b7afd6d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 May 2024 19:01:07 +0200 Subject: [PATCH 1572/2884] vfio/{ap, ccw}: Use warn_report_err() for IRQ notifier registration errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_ccw_register_irq_notifier() and vfio_ap_register_irq_notifier() errors are currently reported using error_report_err(). Since they are not considered as failing conditions, using warn_report_err() is more appropriate. Signed-off-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Reviewed-by: Anthony Krowiak Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20240522170107.289532-8-clg@redhat.com> Signed-off-by: Thomas Huth --- hw/vfio/ap.c | 2 +- hw/vfio/ccw.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index c12531a788..0c4354e3e7 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -172,7 +172,7 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) * Report this error, but do not make it a failing condition. * Lack of this IRQ in the host does not prevent normal operation. */ - error_report_err(err); + warn_report_err(err); } return; diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 36f2677a44..1f8e1272c7 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -616,7 +616,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) * Report this error, but do not make it a failing condition. * Lack of this IRQ in the host does not prevent normal operation. */ - error_report_err(err); + warn_report_err(err); } return; From 7c66540db45a726029e5165f6e5c34008f08edec Mon Sep 17 00:00:00 2001 From: Dmitry Frolov Date: Tue, 21 May 2024 13:31:08 +0300 Subject: [PATCH 1573/2884] tests/qtest/fuzz: fix memleak in qos_fuzz.c Found with fuzzing for qemu-8.2, but also relevant for master Signed-off-by: Dmitry Frolov Reviewed-by: Thomas Huth Reviewed-by: Alexander Bulekov Message-ID: <20240521103106.119021-3-frolov@swemel.ru> Signed-off-by: Thomas Huth --- tests/qtest/fuzz/qos_fuzz.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index b71e945c5f..d3839bf999 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -180,6 +180,7 @@ static void walk_path(QOSGraphNode *orig_path, int len) fuzz_path_vec = path_vec; } else { + g_string_free(cmd_line, true); g_free(path_vec); } From 87c9d801a60c0003e1b570d47a91921f49c6006f Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Tue, 18 Jun 2024 22:00:12 -0700 Subject: [PATCH 1574/2884] target/s390x/arch_dump: use correct byte order for pid The pid field of prstatus needs to be big endian like all of the other fields. Fixes: f738f296eaae ("s390x/arch_dump: pass cpuid into notes sections") Signed-off-by: Omar Sandoval Reviewed-by: Thomas Huth Message-ID: <5929f76d536d355afd04af51bf293695a1065118.1718771802.git.osandov@osandov.com> Signed-off-by: Thomas Huth --- target/s390x/arch_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index 7e8a1b4fc0..029d91d93a 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -102,7 +102,7 @@ static void s390x_write_elf64_prstatus(Note *note, S390CPU *cpu, int id) regs->acrs[i] = cpu_to_be32(cpu->env.aregs[i]); regs->gprs[i] = cpu_to_be64(cpu->env.regs[i]); } - note->contents.prstatus.pid = id; + note->contents.prstatus.pid = cpu_to_be32(id); } static void s390x_write_elf64_fpregset(Note *note, S390CPU *cpu, int id) From 3f6be80ca138d0c770b6b0709bc6f5acd6ad7476 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 19 Jun 2024 07:54:47 +0200 Subject: [PATCH 1575/2884] MAINTAINERS: Cover all tests/qtest/migration-* files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beside migration-test.c, there is nowadays migration-helpers.[ch], too, so update the entry in the migration section to also cover these files now. While we're at it, exclude these files in the common qtest section, since the migration test is well covered by the migration maintainers already. Since the test is under very active development, it was causing a lot of distraction to the generic qtest maintainers with regards to the patches that need to be reviewed by the migration maintainers anyway. Message-ID: <20240619055447.129943-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Thomas Huth --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index cef54de759..f144b5af44 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3312,6 +3312,7 @@ F: tests/qtest/ F: docs/devel/qgraph.rst F: docs/devel/qtest.rst X: tests/qtest/bios-tables-test* +X: tests/qtest/migration-* Device Fuzzing M: Alexander Bulekov @@ -3408,7 +3409,7 @@ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ -F: tests/qtest/migration-test.c +F: tests/qtest/migration-* F: docs/devel/migration/ F: qapi/migration.json F: tests/migration/ From d6a7c3f44cf3f60c066dbf087ef79d4b12acc642 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 13 Jun 2024 16:14:22 +0200 Subject: [PATCH 1576/2884] target/s390x: Add a CONFIG switch to disable legacy CPUs The oldest model that IBM still supports is the z13. Considering that each generation can "emulate" the previous two generations in hardware (via the "IBC" feature of the CPUs), this means that everything that is older than z114/196 is not an officially supported CPU model anymore. The Linux kernel still support the z10, so if we also take this into account, everything older than that can definitely be considered as a legacy CPU model. For downstream builds of QEMU, we would like to be able to disable these legacy CPUs in the build. Thus add a CONFIG switch that can be used to disable them (and old machine types that use them by default). Message-Id: <20240614125019.588928-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 5 +++++ target/s390x/Kconfig | 5 +++++ target/s390x/cpu_models.c | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 3d0bc3e7f2..cd063f8b64 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -47,6 +47,7 @@ #include "migration/blocker.h" #include "qapi/visitor.h" #include "hw/s390x/cpu-topology.h" +#include CONFIG_DEVICES static Error *pv_mig_blocker; @@ -1126,6 +1127,8 @@ static void ccw_machine_2_12_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2_12, "2.12", false); +#ifdef CONFIG_S390X_LEGACY_CPUS + static void ccw_machine_2_11_instance_options(MachineState *machine) { static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V2_11 }; @@ -1272,6 +1275,8 @@ static void ccw_machine_2_4_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2_4, "2.4", false); +#endif + static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); diff --git a/target/s390x/Kconfig b/target/s390x/Kconfig index d886be48b4..8a95f2bc3f 100644 --- a/target/s390x/Kconfig +++ b/target/s390x/Kconfig @@ -2,3 +2,8 @@ config S390X bool select PCI select S390_FLIC + +config S390X_LEGACY_CPUS + bool + default y + depends on S390X diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index efb508cd2e..a27f4b6f79 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -25,6 +25,7 @@ #ifndef CONFIG_USER_ONLY #include "sysemu/sysemu.h" #include "target/s390x/kvm/pv.h" +#include CONFIG_DEVICES #endif #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ @@ -47,6 +48,13 @@ * generation 15 one base feature and one optional feature have been deprecated. */ static S390CPUDef s390_cpu_defs[] = { + /* + * Linux requires at least z10 nowadays, and IBM only supports recent CPUs + * (see https://www.ibm.com/support/pages/ibm-mainframe-life-cycle-history), + * so we consider older CPUs as legacy that can optionally be disabled via + * the CONFIG_S390X_LEGACY_CPUS config switch. + */ +#if defined(CONFIG_S390X_LEGACY_CPUS) || defined(CONFIG_USER_ONLY) CPUDEF_INIT(0x2064, 7, 1, 38, 0x00000000U, "z900", "IBM zSeries 900 GA1"), CPUDEF_INIT(0x2064, 7, 2, 38, 0x00000000U, "z900.2", "IBM zSeries 900 GA2"), CPUDEF_INIT(0x2064, 7, 3, 38, 0x00000000U, "z900.3", "IBM zSeries 900 GA3"), @@ -64,6 +72,7 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x2096, 9, 2, 40, 0x00000000U, "z9BC", "IBM System z9 BC GA1"), CPUDEF_INIT(0x2094, 9, 3, 40, 0x00000000U, "z9EC.3", "IBM System z9 EC GA3"), CPUDEF_INIT(0x2096, 9, 3, 40, 0x00000000U, "z9BC.2", "IBM System z9 BC GA2"), +#endif CPUDEF_INIT(0x2097, 10, 1, 43, 0x00000000U, "z10EC", "IBM System z10 EC GA1"), CPUDEF_INIT(0x2097, 10, 2, 43, 0x00000000U, "z10EC.2", "IBM System z10 EC GA2"), CPUDEF_INIT(0x2098, 10, 2, 43, 0x00000000U, "z10BC", "IBM System z10 BC GA1"), From 4fbeddb088d886be3fb69ddb896df3142177d3df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 18:51:01 +0200 Subject: [PATCH 1577/2884] bswap: Add st24_be_p() to store 24 bits in big-endian order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 14180d6221 ("bswap: Add the ability to store to an unaligned 24 bit field") added st24_le_p() for little endianness, add st24_be_p() equivalent for bit one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Reviewed-by: Richard Henderson Message-Id: <20240621075607.17902-1-philmd@linaro.org> --- include/qemu/bswap.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index bd67468e5e..ad22910a5d 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -38,12 +38,14 @@ static inline void bswap64s(uint64_t *s) #if HOST_BIG_ENDIAN #define be_bswap(v, size) (v) #define le_bswap(v, size) glue(__builtin_bswap, size)(v) +#define be_bswap24(v) (v) #define le_bswap24(v) bswap24(v) #define be_bswaps(v, size) #define le_bswaps(p, size) \ do { *p = glue(__builtin_bswap, size)(*p); } while (0) #else #define le_bswap(v, size) (v) +#define be_bswap24(v) bswap24(v) #define le_bswap24(v) (v) #define be_bswap(v, size) glue(__builtin_bswap, size)(v) #define le_bswaps(v, size) @@ -357,6 +359,11 @@ static inline void stw_be_p(void *ptr, uint16_t v) stw_he_p(ptr, be_bswap(v, 16)); } +static inline void st24_be_p(void *ptr, uint32_t v) +{ + st24_he_p(ptr, be_bswap24(v)); +} + static inline void stl_be_p(void *ptr, uint32_t v) { stl_he_p(ptr, be_bswap(v, 32)); From c2e857b4cac0186d4ba1521fcfeb9b5322831676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Apr 2024 15:38:11 +0200 Subject: [PATCH 1578/2884] hw/sd/sdcard: Avoid OOB in sd_read_byte() during unexpected CMD switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For multi-bytes commands, our implementation uses the @data_start and @data_offset fields to track byte access. We initialize the command start/offset in buffer once. Malicious guest might abuse by switching command while staying in the 'transfer' state, switching command buffer size, and our implementation can access out of buffer boundary. For example, CMD17 (READ_SINGLE_BLOCK) allows to read up to 512 bytes, and CMD13 (SEND_STATUS) up to 64 bytes. By switching from CMD17 to CMD13 (see reproducer below), bytes [64-511] are out of the 'status' buffer. Our implementation return R0 status code for unexpected commands. Such in-transaction command switch is unexpected and returns R0. This is a good place to reset the start/offset fields to avoid malicious accesses. Can be reproduced running: $ export UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 $ cat << EOF | qemu-system-i386 \ -display none -nographic \ -machine accel=qtest -m 512M \ -nodefaults \ -device sdhci-pci,sd-spec-version=3 \ -device sd-card,drive=mydrive \ -drive if=none,index=0,file=null-co://,format=raw,id=mydrive \ -qtest stdio -trace sd\* -trace -sdbus_read outl 0xcf8 0x80001010 outl 0xcfc 0xe0000000 outl 0xcf8 0x80001004 outw 0xcfc 0x02 write 0xe000002c 0x1 0x05 write 0xe000000f 0x1 0x37 write 0xe000000a 0x1 0x01 write 0xe000000f 0x1 0x29 write 0xe000000f 0x1 0x02 write 0xe000000f 0x1 0x03 write 0xe000000c 0x1 0x32 write 0xe000000f 0x1 0x06 write 0xe0000005 0x1 0x01 write 0xe0000007 0x1 0x01 write 0xe0000003 0x1 0x00 write 0xe000000f 0x1 0x11 write 0xe000002a 0x1 0x01 write 0xe000002a 0x1 0x02 write 0xe000000f 0x1 0x0d write 0xe000002a 0x1 0x01 write 0xe000002a 0x1 0x02 EOF hw/sd/sd.c:1984:15: runtime error: index 256 out of bounds for type 'uint8_t [64]' #0 sd_read_byte hw/sd/sd.c:1984:15 #1 sdbus_read_data hw/sd/core.c:157:23 #2 sdhci_read_block_from_card hw/sd/sdhci.c:423:9 #3 sdhci_blkgap_write hw/sd/sdhci.c:1074:13 #4 sdhci_write hw/sd/sdhci.c:1195:13 #5 memory_region_write_accessor softmmu/memory.c:492:5 #6 access_with_adjusted_size softmmu/memory.c:554:18 #7 memory_region_dispatch_write softmmu/memory.c #8 flatview_write_continue softmmu/physmem.c:2778:23 #9 flatview_write softmmu/physmem.c:2818:14 #10 address_space_write softmmu/physmem.c:2910:18 SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior hw/sd/sd.c:1984:15 Reported-by: Alexander Bulekov Resolves: https://gitlab.com/qemu-project/qemu/-/issues/487 Buglink: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=36240 Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240408141717.66154-2-philmd@linaro.org> --- hw/sd/sd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 807b5d3de3..6a7a10501b 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1826,6 +1826,13 @@ send_response: break; case sd_r0: + /* + * Invalid state transition, reset implementation + * fields to avoid OOB abuse. + */ + sd->data_start = 0; + sd->data_offset = 0; + /* fall-through */ case sd_illegal: rsplen = 0; break; From 8d380565adf45fa456832d7e2cbbf7be359ebbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Jun 2024 16:19:41 +0200 Subject: [PATCH 1579/2884] hw/sd/sdcard: Correct code indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix mis-alignment from commits 793d04f495 and 6380cd2052 ("Add sd_cmd_SEND_TUNING_BLOCK" and "Add sd_cmd_SET_BLOCK_COUNT"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-2-philmd@linaro.org> --- hw/sd/sd.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 6a7a10501b..ba7f165cb5 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1069,33 +1069,33 @@ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { - if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { - return sd_cmd_illegal(sd, req); - } + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + return sd_cmd_illegal(sd, req); + } - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); - } + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - sd->state = sd_sendingdata_state; - sd->data_offset = 0; + sd->state = sd_sendingdata_state; + sd->data_offset = 0; - return sd_r1; + return sd_r1; } static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) { - if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { - return sd_cmd_illegal(sd, req); - } + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + return sd_cmd_illegal(sd, req); + } - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); - } + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - sd->multi_blk_cnt = req.arg; + sd->multi_blk_cnt = req.arg; - return sd_r1; + return sd_r1; } static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) From d00e614f61e1a90ee905e783d3363752e63df8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 16:58:29 +0200 Subject: [PATCH 1580/2884] hw/sd/sdcard: Rewrite sd_cmd_ALL_SEND_CID using switch case (CMD2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep this handler style in sync with other handlers by using a switch() case, which might become handy to handle other states. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-3-philmd@linaro.org> --- hw/sd/sd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index ba7f165cb5..46388a140a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1044,13 +1044,13 @@ static sd_rsp_type_t sd_cmd_SEND_OP_CMD(SDState *sd, SDRequest req) static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) { - if (sd->state != sd_ready_state) { + switch (sd->state) { + case sd_ready_state: + sd->state = sd_identification_state; + return sd_r2_i; + default: return sd_invalid_state_for_cmd(sd, req); } - - sd->state = sd_identification_state; - - return sd_r2_i; } static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) From 3dc5d349a32d741d3b432cc53f2ed518ff3c5f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 05:11:12 +0200 Subject: [PATCH 1581/2884] hw/sd/sdcard: Fix typo in SEND_OP_COND command name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no SEND_OP_CMD but SEND_OP_COND. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-4-philmd@linaro.org> --- hw/sd/sd.c | 6 +++--- hw/sd/sdmmc-internal.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 46388a140a..72d71259d3 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1035,7 +1035,7 @@ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) return sd_is_spi(sd) ? sd_r1 : sd_r0; } -static sd_rsp_type_t sd_cmd_SEND_OP_CMD(SDState *sd, SDRequest req) +static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req) { sd->state = sd_transfer_state; @@ -2150,7 +2150,7 @@ static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { [0] = sd_cmd_GO_IDLE_STATE, - [1] = sd_cmd_SEND_OP_CMD, + [1] = spi_cmd_SEND_OP_COND, [2 ... 4] = sd_cmd_illegal, [5] = sd_cmd_illegal, [7] = sd_cmd_illegal, @@ -2160,7 +2160,7 @@ static const SDProto sd_proto_spi = { }, .acmd = { [6] = sd_cmd_unimplemented, - [41] = sd_cmd_SEND_OP_CMD, + [41] = spi_cmd_SEND_OP_COND, }, }; diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c index 8648a7808d..c1d5508ae6 100644 --- a/hw/sd/sdmmc-internal.c +++ b/hw/sd/sdmmc-internal.c @@ -14,7 +14,7 @@ const char *sd_cmd_name(uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [0] = "GO_IDLE_STATE", [1] = "SEND_OP_CMD", + [0] = "GO_IDLE_STATE", [1] = "SEND_OP_COND", [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", From f17fb69c55c7020b90cc1a9eaefe716902a5bc0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 25 Oct 2020 19:36:59 +0100 Subject: [PATCH 1582/2884] hw/sd/sdcard: Use HWBLOCK_SHIFT definition instead of magic values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-5-philmd@linaro.org> --- hw/sd/sd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 72d71259d3..a14c5ff147 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -596,7 +596,7 @@ static void sd_reset(DeviceState *dev) } else { sect = 0; } - size = sect << 9; + size = sect << HWBLOCK_SHIFT; sect = sd_addr_to_wpnum(size) + 1; @@ -822,8 +822,8 @@ static void sd_erase(SDState *sd) if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { /* High capacity memory card: erase units are 512 byte blocks */ - erase_start *= 512; - erase_end *= 512; + erase_start <<= HWBLOCK_SHIFT; + erase_end <<= HWBLOCK_SHIFT; sdsc = false; } From 904547845c559d7530a20792d599a77d8e2ae442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 13:03:56 +0200 Subject: [PATCH 1583/2884] hw/sd/sdcard: Use registerfield CSR::CURRENT_STATE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use registerfield-generated definitions to update card_status. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-6-philmd@linaro.org> --- hw/sd/sd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a14c5ff147..64270dec0f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1788,8 +1788,8 @@ int sd_do_command(SDState *sd, SDRequest *req, * (Do this now so they appear in r1 responses.) */ sd->current_cmd = req->cmd; - sd->card_status &= ~CURRENT_STATE; - sd->card_status |= (last_state << 9); + sd->card_status = FIELD_DP32(sd->card_status, CSR, + CURRENT_STATE, last_state); } send_response: From eac7ce3de78ee96c3e2b3eb2678682e44078bcdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 18:47:27 +0200 Subject: [PATCH 1584/2884] hw/sd/sdcard: Use Load/Store API to fill some CID/CSD registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ld/st API helps noticing CID or CSD bytes refer to the same field. Multi-bytes fields are stored MSB first in CID / CSD. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-7-philmd@linaro.org> --- hw/sd/sd.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 64270dec0f..6e346e28ca 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -393,10 +393,7 @@ static void sd_set_cid(SDState *sd) sd->cid[6] = PNM[3]; sd->cid[7] = PNM[4]; sd->cid[8] = PRV; /* Fake product revision (PRV) */ - sd->cid[9] = 0xde; /* Fake serial number (PSN) */ - sd->cid[10] = 0xad; - sd->cid[11] = 0xbe; - sd->cid[12] = 0xef; + stl_be_p(&sd->cid[9], 0xdeadbeef); /* Fake serial number (PSN) */ sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ ((MDT_YR - 2000) / 10); sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON; @@ -462,9 +459,7 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[4] = 0x5b; sd->csd[5] = 0x59; sd->csd[6] = 0x00; - sd->csd[7] = (size >> 16) & 0xff; - sd->csd[8] = (size >> 8) & 0xff; - sd->csd[9] = (size & 0xff); + st24_be_p(&sd->csd[7], size); sd->csd[10] = 0x7f; sd->csd[11] = 0x80; sd->csd[12] = 0x0a; From 3b87fff1cd34a1f7cbf2884e615cfd69c28c8ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 14:43:13 +0200 Subject: [PATCH 1585/2884] hw/sd/sdcard: Remove ACMD6 handler for SPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no ACMD6 command in SPI mode, remove the pointless handler introduced in commit 946897ce18 ("sdcard: handles more commands in SPI mode"). Keep sd_cmd_unimplemented() since we'll reuse it later. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-8-philmd@linaro.org> --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 6e346e28ca..08a6d0aff8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1012,6 +1012,7 @@ static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) } /* Commands that are recognised but not yet implemented. */ +__attribute__((unused)) static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) { qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n", @@ -2154,7 +2155,6 @@ static const SDProto sd_proto_spi = { [52 ... 54] = sd_cmd_illegal, }, .acmd = { - [6] = sd_cmd_unimplemented, [41] = spi_cmd_SEND_OP_COND, }, }; From 913638464c5cc244545af0a7f2a1c32c5780d6a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 05:14:05 +0200 Subject: [PATCH 1586/2884] hw/sd/sdcard: Remove explicit entries for illegal commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NULL handler is already handled as illegal, no need to duplicate (that keeps this array simpler to maintain). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-9-philmd@linaro.org> --- hw/sd/sd.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 08a6d0aff8..4afb6988c7 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2147,12 +2147,6 @@ static const SDProto sd_proto_spi = { .cmd = { [0] = sd_cmd_GO_IDLE_STATE, [1] = spi_cmd_SEND_OP_COND, - [2 ... 4] = sd_cmd_illegal, - [5] = sd_cmd_illegal, - [7] = sd_cmd_illegal, - [15] = sd_cmd_illegal, - [26] = sd_cmd_illegal, - [52 ... 54] = sd_cmd_illegal, }, .acmd = { [41] = spi_cmd_SEND_OP_COND, @@ -2163,15 +2157,10 @@ static const SDProto sd_proto_sd = { .name = "SD", .cmd = { [0] = sd_cmd_GO_IDLE_STATE, - [1] = sd_cmd_illegal, [2] = sd_cmd_ALL_SEND_CID, [3] = sd_cmd_SEND_RELATIVE_ADDR, - [5] = sd_cmd_illegal, [19] = sd_cmd_SEND_TUNING_BLOCK, [23] = sd_cmd_SET_BLOCK_COUNT, - [52 ... 54] = sd_cmd_illegal, - [58] = sd_cmd_illegal, - [59] = sd_cmd_illegal, }, }; From 0e93b3b30b29b3e23eabf5ba759315cece761a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jun 2024 01:20:22 +0200 Subject: [PATCH 1587/2884] hw/sd/sdcard: Trace update of block count (CMD23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-12-philmd@linaro.org> --- hw/sd/sd.c | 1 + hw/sd/trace-events | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 4afb6988c7..44225bae9b 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1090,6 +1090,7 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) } sd->multi_blk_cnt = req.arg; + trace_sdcard_set_block_count(sd->multi_blk_cnt); return sd_r1; } diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 94a00557b2..724365efc3 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -43,7 +43,8 @@ sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" sdcard_reset(void) "" -sdcard_set_blocklen(uint16_t length) "0x%03x" +sdcard_set_blocklen(uint16_t length) "block len 0x%03x" +sdcard_set_block_count(uint32_t cnt) "block cnt 0x%"PRIx32 sdcard_inserted(bool readonly) "read_only: %u" sdcard_ejected(void) "" sdcard_erase(uint32_t first, uint32_t last) "addr first 0x%" PRIx32" last 0x%" PRIx32 From eded0d1a485f3e6279142b88edf444e5e764c9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 05:28:51 +0200 Subject: [PATCH 1588/2884] hw/sd/sdcard: Have cmd_valid_while_locked() return a boolean value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-14-philmd@linaro.org> --- hw/sd/sd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 44225bae9b..04b141784b 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1717,7 +1717,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, return sd_illegal; } -static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd) +static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) { /* Valid commands in locked state: * basic class (0) @@ -1731,7 +1731,7 @@ static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd) return cmd == 41 || cmd == 42; } if (cmd == 16 || cmd == 55) { - return 1; + return true; } return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7; } From 0ab318ca3c0b370ff82e9fabf80633cc93d0574e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Jun 2024 16:38:01 +0200 Subject: [PATCH 1589/2884] hw/sd/sdcard: Factor sd_req_get_rca() method out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract sd_req_get_rca() so we can re-use it in various SDProto handlers. Return a 16-bit value since RCA is 16-bit. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-15-philmd@linaro.org> --- hw/sd/sd.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 04b141784b..b909a85d53 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -474,6 +474,14 @@ static void sd_set_rca(SDState *sd) sd->rca += 0x4567; } +static uint16_t sd_req_get_rca(SDState *s, SDRequest req) +{ + if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) { + return req.arg >> 16; + } + return 0; +} + FIELD(CSR, AKE_SEQ_ERROR, 3, 1) FIELD(CSR, APP_CMD, 5, 1) FIELD(CSR, FX_EVENT, 6, 1) @@ -1097,7 +1105,7 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { - uint32_t rca = 0x0000; + uint16_t rca = sd_req_get_rca(sd, req); uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; /* CMD55 precedes an ACMD, so we are not interested in tracing it. @@ -1112,11 +1120,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Not interpreting this as an app command */ sd->card_status &= ~APP_CMD; - if (sd_cmd_type[req.cmd] == sd_ac - || sd_cmd_type[req.cmd] == sd_adtc) { - rca = req.arg >> 16; - } - /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25 * if not, its effects are cancelled */ if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) { From 4a829730c85a8e44f142aeca6c95b8b07742c96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Jun 2024 22:12:35 +0200 Subject: [PATCH 1590/2884] hw/sd/sdcard: Only call sd_req_get_rca() where RCA is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be useful later to assert only AC commands (Addressed point-to-point Commands, defined as the 'sd_ac' enum) extract the RCA value from the command argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-16-philmd@linaro.org> --- hw/sd/sd.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b909a85d53..912b2f8984 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1105,7 +1105,7 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { - uint16_t rca = sd_req_get_rca(sd, req); + uint16_t rca; uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; /* CMD55 precedes an ACMD, so we are not interested in tracing it. @@ -1162,6 +1162,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 7: /* CMD7: SELECT/DESELECT_CARD */ + rca = sd_req_get_rca(sd, req); switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -1216,6 +1217,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_r7; case 9: /* CMD9: SEND_CSD */ + rca = sd_req_get_rca(sd, req); switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -1239,6 +1241,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 10: /* CMD10: SEND_CID */ + rca = sd_req_get_rca(sd, req); switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -1279,6 +1282,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 13: /* CMD13: SEND_STATUS */ + rca = sd_req_get_rca(sd, req); switch (sd->mode) { case sd_data_transfer_mode: if (!sd_is_spi(sd) && sd->rca != rca) { @@ -1293,6 +1297,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 15: /* CMD15: GO_INACTIVE_STATE */ + rca = sd_req_get_rca(sd, req); switch (sd->mode) { case sd_data_transfer_mode: if (sd->rca != rca) @@ -1525,6 +1530,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Application specific commands (Class 8) */ case 55: /* CMD55: APP_CMD */ + rca = sd_req_get_rca(sd, req); switch (sd->state) { case sd_ready_state: case sd_identification_state: From 8b91a5613ca1520286b7c3ffe19ebecf144c781a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Jun 2024 16:50:30 +0200 Subject: [PATCH 1591/2884] hw/sd/sdcard: Factor sd_req_get_address() method out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract sd_cmd_get_address() so we can re-use it in various SDProto handlers. Use CARD_CAPACITY and HWBLOCK_SHIFT definitions instead of magic values. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-17-philmd@linaro.org> --- hw/sd/sd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 912b2f8984..51ab7cd003 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -582,6 +582,14 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) stl_be_p(response, sd->vhs); } +static uint64_t sd_req_get_address(SDState *sd, SDRequest req) +{ + if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + return (uint64_t) req.arg << HWBLOCK_SHIFT; + } + return req.arg; +} + static inline uint64_t sd_addr_to_wpnum(uint64_t addr) { return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); @@ -1106,7 +1114,7 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; - uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; + uint64_t addr = sd_req_get_address(sd, req); /* CMD55 precedes an ACMD, so we are not interested in tracing it. * However there is no ACMD55, so we want to trace this particular case. From 6a226b2cab40f9d40be5de59230d80d9aa11bc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 14:29:46 +0200 Subject: [PATCH 1592/2884] hw/sd/sdcard: Only call sd_req_get_address() where address is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be useful later to assert only ADTC commands (Addressed point-to-point Data Transfer Commands, defined as the 'sd_adtc' enum) extract the address value from the command argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-18-philmd@linaro.org> --- hw/sd/sd.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 51ab7cd003..3e4eb656e1 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1114,7 +1114,7 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; - uint64_t addr = sd_req_get_address(sd, req); + uint64_t addr; /* CMD55 precedes an ACMD, so we are not interested in tracing it. * However there is no ACMD55, so we want to trace this particular case. @@ -1239,7 +1239,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } sd->state = sd_sendingdata_state; memcpy(sd->data, sd->csd, 16); - sd->data_start = addr; + sd->data_start = sd_req_get_address(sd, req); sd->data_offset = 0; return sd_r1; @@ -1263,7 +1263,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } sd->state = sd_sendingdata_state; memcpy(sd->data, sd->cid, 16); - sd->data_start = addr; + sd->data_start = sd_req_get_address(sd, req); sd->data_offset = 0; return sd_r1; @@ -1339,6 +1339,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 17: /* CMD17: READ_SINGLE_BLOCK */ case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: @@ -1359,6 +1360,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: @@ -1417,7 +1419,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; } - + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) { @@ -1439,7 +1441,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; } - + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) { @@ -1461,7 +1463,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; } - + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: if (!address_in_range(sd, "SEND_WRITE_PROT", From 9ee9292b75fcce4c32ebfe009c950d8437a267f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 13 Jun 2024 22:45:43 +0200 Subject: [PATCH 1593/2884] hw/sd/sdcard: Add sd_invalid_mode_for_cmd to report invalid mode switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having the mode switch displayed help to track incomplete command implementations. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-19-philmd@linaro.org> --- hw/sd/sd.c | 75 +++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 3e4eb656e1..969340e5cb 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -178,6 +178,17 @@ static const char *sd_version_str(enum SDPhySpecificationVersion version) return sdphy_version[version]; } +static const char *sd_mode_name(enum SDCardModes mode) +{ + static const char *mode_name[] = { + [sd_inactive] = "inactive", + [sd_card_identification_mode] = "identification", + [sd_data_transfer_mode] = "transfer", + }; + assert(mode < ARRAY_SIZE(mode_name)); + return mode_name[mode]; +} + static const char *sd_state_name(enum SDCardStates state) { static const char *state_name[] = { @@ -1018,6 +1029,15 @@ static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) return sd_illegal; } +static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong mode: %s (spec %s)\n", + sd_proto(sd)->name, req.cmd, sd_mode_name(sd->mode), + sd_version_str(sd->spec_version)); + + return sd_illegal; +} + static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown CMD%i for spec %s\n", @@ -1156,18 +1176,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 6: /* CMD6: SWITCH_FUNCTION */ - switch (sd->mode) { - case sd_data_transfer_mode: - sd_function_switch(sd, req.arg); - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); } - break; + sd_function_switch(sd, req.arg); + sd->state = sd_sendingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return sd_r1; case 7: /* CMD7: SELECT/DESELECT_CARD */ rca = sd_req_get_rca(sd, req); @@ -1291,33 +1307,24 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 13: /* CMD13: SEND_STATUS */ rca = sd_req_get_rca(sd, req); - switch (sd->mode) { - case sd_data_transfer_mode: - if (!sd_is_spi(sd) && sd->rca != rca) { - return sd_r0; - } - - return sd_r1; - - default: - break; + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); } - break; + if (!sd_is_spi(sd) && sd->rca != rca) { + return sd_r0; + } + + return sd_r1; case 15: /* CMD15: GO_INACTIVE_STATE */ - rca = sd_req_get_rca(sd, req); - switch (sd->mode) { - case sd_data_transfer_mode: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_inactive_state; - return sd_r0; - - default: - break; + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); } - break; + rca = sd_req_get_rca(sd, req); + if (sd->rca == rca) { + sd->state = sd_inactive_state; + } + return sd_r0; /* Block read commands (Class 2) */ case 16: /* CMD16: SET_BLOCKLEN */ From b31bf9f8f72477742615888e78ab07845574e19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:09 +0100 Subject: [PATCH 1594/2884] include/exec: add missing include guard comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20240620152220.2192768-2-alex.bennee@linaro.org> --- include/exec/gdbstub.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index eb14b91139..008a92198a 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -144,4 +144,4 @@ void gdb_set_stop_cpu(CPUState *cpu); /* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ extern const GDBFeature gdb_static_features[]; -#endif +#endif /* GDBSTUB_H */ From 5b7d54d4ed7857b8b27fb040c6027ba59e003889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:10 +0100 Subject: [PATCH 1595/2884] gdbstub: move enums into separate header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an experiment to further reduce the amount we throw into the exec headers. It might not be as useful as I initially thought because just under half of the users also need gdbserver_start(). Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20240620152220.2192768-3-alex.bennee@linaro.org> --- accel/hvf/hvf-accel-ops.c | 2 +- accel/kvm/kvm-all.c | 2 +- accel/tcg/tcg-accel-ops.c | 2 +- gdbstub/user.c | 1 + include/exec/gdbstub.h | 9 --------- include/gdbstub/enums.h | 21 +++++++++++++++++++++ monitor/hmp-cmds.c | 3 ++- system/vl.c | 1 + target/arm/hvf/hvf.c | 2 +- target/arm/hyp_gdbstub.c | 2 +- target/arm/kvm.c | 2 +- target/i386/kvm/kvm.c | 2 +- target/ppc/kvm.c | 2 +- target/s390x/kvm/kvm.c | 2 +- 14 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 include/gdbstub/enums.h diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index b2a37a2229..ac08cfb9f3 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -52,7 +52,7 @@ #include "qemu/main-loop.h" #include "exec/address-spaces.h" #include "exec/exec-all.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "sysemu/cpus.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 854cb86b22..2b4ab89679 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -27,7 +27,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/s390x/adapter.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "sysemu/cpus.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 1433e38f40..3c19e68a79 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -35,7 +35,7 @@ #include "exec/exec-all.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "hw/core/cpu.h" diff --git a/gdbstub/user.c b/gdbstub/user.c index edeb72efeb..e34b58b407 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -18,6 +18,7 @@ #include "exec/gdbstub.h" #include "gdbstub/syscalls.h" #include "gdbstub/user.h" +#include "gdbstub/enums.h" #include "hw/core/cpu.h" #include "trace.h" #include "internals.h" diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 008a92198a..1bd2c4ec2a 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -1,15 +1,6 @@ #ifndef GDBSTUB_H #define GDBSTUB_H -#define DEFAULT_GDBSTUB_PORT "1234" - -/* GDB breakpoint/watchpoint types */ -#define GDB_BREAKPOINT_SW 0 -#define GDB_BREAKPOINT_HW 1 -#define GDB_WATCHPOINT_WRITE 2 -#define GDB_WATCHPOINT_READ 3 -#define GDB_WATCHPOINT_ACCESS 4 - typedef struct GDBFeature { const char *xmlname; const char *xml; diff --git a/include/gdbstub/enums.h b/include/gdbstub/enums.h new file mode 100644 index 0000000000..c4d54a1d08 --- /dev/null +++ b/include/gdbstub/enums.h @@ -0,0 +1,21 @@ +/* + * gdbstub enums + * + * Copyright (c) 2024 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GDBSTUB_ENUMS_H +#define GDBSTUB_ENUMS_H + +#define DEFAULT_GDBSTUB_PORT "1234" + +/* GDB breakpoint/watchpoint types */ +#define GDB_BREAKPOINT_SW 0 +#define GDB_BREAKPOINT_HW 1 +#define GDB_WATCHPOINT_WRITE 2 +#define GDB_WATCHPOINT_READ 3 +#define GDB_WATCHPOINT_ACCESS 4 + +#endif /* GDBSTUB_ENUMS_H */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 45ee3a9e1f..f601d06ab8 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -15,8 +15,9 @@ #include "qemu/osdep.h" #include "exec/address-spaces.h" -#include "exec/gdbstub.h" #include "exec/ioport.h" +#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "monitor/hmp.h" #include "qemu/help_option.h" #include "monitor/monitor-internal.h" diff --git a/system/vl.c b/system/vl.c index a3eede5fa5..cfcb674425 100644 --- a/system/vl.c +++ b/system/vl.c @@ -68,6 +68,7 @@ #include "sysemu/numa.h" #include "sysemu/hostmem.h" #include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "qemu/timer.h" #include "chardev/char.h" #include "qemu/bitmap.h" diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 45e2218be5..ef9bc42738 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -33,7 +33,7 @@ #include "trace/trace-target_arm_hvf.h" #include "migration/vmstate.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #define MDSCR_EL1_SS_SHIFT 0 #define MDSCR_EL1_MDE_SHIFT 15 diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c index ebde2899cd..f120d55caa 100644 --- a/target/arm/hyp_gdbstub.c +++ b/target/arm/hyp_gdbstub.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" /* Maximum and current break/watch point counts */ int max_hw_bps, max_hw_wps; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 7cf5cf31de..70f79eda33 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -31,7 +31,7 @@ #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "hw/boards.h" #include "hw/irq.h" #include "qapi/visitor.h" diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 7ad8072748..dd8b0f3313 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -38,7 +38,7 @@ #include "hyperv.h" #include "hyperv-proto.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "qemu/host-utils.h" #include "qemu/main-loop.h" #include "qemu/ratelimit.h" diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 005f2239f3..2c3932200b 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -39,7 +39,7 @@ #include "migration/qemu-file-types.h" #include "sysemu/watchdog.h" #include "trace.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "exec/memattrs.h" #include "exec/ram_addr.h" #include "sysemu/hostmem.h" diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 1b494ecc20..94181d9281 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -40,7 +40,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "sysemu/device_tree.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "exec/ram_addr.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" From ad59d5caee26178b6719a2f38e931c89840b7350 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 20 Jun 2024 16:22:11 +0100 Subject: [PATCH 1596/2884] plugins: Ensure register handles are not NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure register handles are not NULL so that a plugin can assume NULL is invalid as a register handle. Signed-off-by: Akihiko Odaki Reviewed-by: Pierrick Bouvier Message-Id: <20240229-null-v1-1-e716501d981e@daynix.com> Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20240620152220.2192768-4-alex.bennee@linaro.org> --- plugins/api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/api.c b/plugins/api.c index 5a0a7f8c71..6bdb26bbe3 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -507,7 +507,7 @@ static GArray *create_register_handles(GArray *gdbstub_regs) } /* Create a record for the plugin */ - desc.handle = GINT_TO_POINTER(grd->gdb_reg); + desc.handle = GINT_TO_POINTER(grd->gdb_reg + 1); desc.name = g_intern_string(grd->name); desc.feature = g_intern_string(grd->feature_name); g_array_append_val(find_data, desc); @@ -528,7 +528,7 @@ int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) { g_assert(current_cpu); - return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg)); + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); } struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) From 113ac1d2127e4255129963c5061578b3fd85cc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:12 +0100 Subject: [PATCH 1597/2884] sysemu: add set_virtual_time to accel ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are about to remove direct calls to individual accelerators for this information and will need a central point for plugins to hook into time changes. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20240530220610.1245424-2-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-5-alex.bennee@linaro.org> --- include/sysemu/accel-ops.h | 18 +++++++++++++++++- include/sysemu/cpu-timers.h | 3 ++- ...et-virtual-clock.c => cpus-virtual-clock.c} | 5 +++++ stubs/meson.build | 2 +- system/cpus.c | 11 +++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%) diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h index ef91fc28bb..a088672230 100644 --- a/include/sysemu/accel-ops.h +++ b/include/sysemu/accel-ops.h @@ -20,7 +20,12 @@ typedef struct AccelOpsClass AccelOpsClass; DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS) -/* cpus.c operations interface */ +/** + * struct AccelOpsClass - accelerator interfaces + * + * This structure is used to abstract accelerator differences from the + * core CPU code. Not all have to be implemented. + */ struct AccelOpsClass { /*< private >*/ ObjectClass parent_class; @@ -44,7 +49,18 @@ struct AccelOpsClass { void (*handle_interrupt)(CPUState *cpu, int mask); + /** + * @get_virtual_clock: fetch virtual clock + * @set_virtual_clock: set virtual clock + * + * These allow the timer subsystem to defer to the accelerator to + * fetch time. The set function is needed if the accelerator wants + * to track the changes to time as the timer is warped through + * various timer events. + */ int64_t (*get_virtual_clock)(void); + void (*set_virtual_clock)(int64_t time); + int64_t (*get_elapsed_ticks)(void); /* gdbstub hooks */ diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h index d86738a378..7bfa960fbd 100644 --- a/include/sysemu/cpu-timers.h +++ b/include/sysemu/cpu-timers.h @@ -96,8 +96,9 @@ int64_t cpu_get_clock(void); void qemu_timer_notify_cb(void *opaque, QEMUClockType type); -/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */ +/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */ int64_t cpus_get_virtual_clock(void); +void cpus_set_virtual_clock(int64_t new_time); int64_t cpus_get_elapsed_ticks(void); #endif /* SYSEMU_CPU_TIMERS_H */ diff --git a/stubs/cpus-get-virtual-clock.c b/stubs/cpus-virtual-clock.c similarity index 68% rename from stubs/cpus-get-virtual-clock.c rename to stubs/cpus-virtual-clock.c index fd447d53f3..af7c1a1d40 100644 --- a/stubs/cpus-get-virtual-clock.c +++ b/stubs/cpus-virtual-clock.c @@ -6,3 +6,8 @@ int64_t cpus_get_virtual_clock(void) { return cpu_get_clock(); } + +void cpus_set_virtual_clock(int64_t new_time) +{ + /* do nothing */ +} diff --git a/stubs/meson.build b/stubs/meson.build index f15b48d01f..772a3e817d 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -29,7 +29,7 @@ endif if have_block or have_ga stub_ss.add(files('replay-tools.c')) # stubs for hooks in util/main-loop.c, util/async.c etc. - stub_ss.add(files('cpus-get-virtual-clock.c')) + stub_ss.add(files('cpus-virtual-clock.c')) stub_ss.add(files('icount.c')) stub_ss.add(files('graph-lock.c')) if linux_io_uring.found() diff --git a/system/cpus.c b/system/cpus.c index f8fa78f33d..d3640c9503 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -229,6 +229,17 @@ int64_t cpus_get_virtual_clock(void) return cpu_get_clock(); } +/* + * Signal the new virtual time to the accelerator. This is only needed + * by accelerators that need to track the changes as we warp time. + */ +void cpus_set_virtual_clock(int64_t new_time) +{ + if (cpus_accel && cpus_accel->set_virtual_clock) { + cpus_accel->set_virtual_clock(new_time); + } +} + /* * return the time elapsed in VM between vm_start and vm_stop. Unless * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle From e83e386200deeede6241007db6a27d09350ae060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:13 +0100 Subject: [PATCH 1598/2884] qtest: use cpu interface in qtest_clock_warp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This generalises the qtest_clock_warp code to use the AccelOps handlers for updating its own sense of time. This will make the next patch which moves the warp code closer to pure code motion. From: Alex Bennée Acked-by: Thomas Huth Signed-off-by: Pierrick Bouvier Message-Id: <20240530220610.1245424-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-6-alex.bennee@linaro.org> --- accel/qtest/qtest.c | 1 + include/sysemu/qtest.h | 1 + system/qtest.c | 6 +++--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index f6056ac836..53182e6c2a 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -52,6 +52,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) ops->create_vcpu_thread = dummy_start_vcpu_thread; ops->get_virtual_clock = qtest_get_virtual_clock; + ops->set_virtual_clock = qtest_set_virtual_clock; }; static const TypeInfo qtest_accel_ops_type = { diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index b5d5fd3463..45f3b7e1df 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -36,6 +36,7 @@ void qtest_server_set_send_handler(void (*send)(void *, const char *), void qtest_server_inproc_recv(void *opaque, const char *buf); int64_t qtest_get_virtual_clock(void); +void qtest_set_virtual_clock(int64_t count); #endif #endif diff --git a/system/qtest.c b/system/qtest.c index 507a358f3b..5be66b0140 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -332,14 +332,14 @@ int64_t qtest_get_virtual_clock(void) return qatomic_read_i64(&qtest_clock_counter); } -static void qtest_set_virtual_clock(int64_t count) +void qtest_set_virtual_clock(int64_t count) { qatomic_set_i64(&qtest_clock_counter, count); } static void qtest_clock_warp(int64_t dest) { - int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t clock = cpus_get_virtual_clock(); AioContext *aio_context; assert(qtest_enabled()); aio_context = qemu_get_aio_context(); @@ -348,7 +348,7 @@ static void qtest_clock_warp(int64_t dest) QEMU_TIMER_ATTR_ALL); int64_t warp = qemu_soonest_timeout(dest - clock, deadline); - qtest_set_virtual_clock(qtest_get_virtual_clock() + warp); + cpus_set_virtual_clock(cpus_get_virtual_clock() + warp); qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); From ffbc3949462e67d44ae96ed1d25a5a061557bb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:14 +0100 Subject: [PATCH 1599/2884] sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the key functionality of moving time forward into the clock sub-system itself. This will allow us to plumb in time control into plugins. Signed-off-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-7-alex.bennee@linaro.org> --- include/qemu/timer.h | 15 +++++++++++++++ system/qtest.c | 25 +++---------------------- util/qemu-timer.c | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 9a366e551f..5ce83c7911 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -245,6 +245,21 @@ bool qemu_clock_run_timers(QEMUClockType type); */ bool qemu_clock_run_all_timers(void); +/** + * qemu_clock_advance_virtual_time(): advance the virtual time tick + * @target_ns: target time in nanoseconds + * + * This function is used where the control of the flow of time has + * been delegated to outside the clock subsystem (be it qtest, icount + * or some other external source). You can ask the clock system to + * return @early at the first expired timer. + * + * Time can only move forward, attempts to reverse time would lead to + * an error. + * + * Returns: new virtual time. + */ +int64_t qemu_clock_advance_virtual_time(int64_t target_ns); /* * QEMUTimerList diff --git a/system/qtest.c b/system/qtest.c index 5be66b0140..8cb98966b4 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -337,26 +337,6 @@ void qtest_set_virtual_clock(int64_t count) qatomic_set_i64(&qtest_clock_counter, count); } -static void qtest_clock_warp(int64_t dest) -{ - int64_t clock = cpus_get_virtual_clock(); - AioContext *aio_context; - assert(qtest_enabled()); - aio_context = qemu_get_aio_context(); - while (clock < dest) { - int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - int64_t warp = qemu_soonest_timeout(dest - clock, deadline); - - cpus_set_virtual_clock(cpus_get_virtual_clock() + warp); - - qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); - timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); - clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); -} - static bool (*process_command_cb)(CharBackend *chr, gchar **words); void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)) @@ -751,7 +731,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); } - qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns); + qemu_clock_advance_virtual_time( + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns); qtest_send_prefix(chr); qtest_sendf(chr, "OK %"PRIi64"\n", (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); @@ -777,7 +758,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(words[1]); ret = qemu_strtoi64(words[1], NULL, 0, &ns); g_assert(ret == 0); - qtest_clock_warp(ns); + qemu_clock_advance_virtual_time(ns); qtest_send_prefix(chr); qtest_sendf(chr, "OK %"PRIi64"\n", (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 6a0de33dd2..213114be68 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -645,6 +645,11 @@ int64_t qemu_clock_get_ns(QEMUClockType type) } } +static void qemu_virtual_clock_set_ns(int64_t time) +{ + return cpus_set_virtual_clock(time); +} + void init_clocks(QEMUTimerListNotifyCB *notify_cb) { QEMUClockType type; @@ -675,3 +680,24 @@ bool qemu_clock_run_all_timers(void) return progress; } + +int64_t qemu_clock_advance_virtual_time(int64_t dest) +{ + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + AioContext *aio_context; + aio_context = qemu_get_aio_context(); + while (clock < dest) { + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + int64_t warp = qemu_soonest_timeout(dest - clock, deadline); + + qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); + + qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); + timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); + clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + + return clock; +} From d4d133a34b300b574d446316491828867205d17f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 20 Jun 2024 16:22:15 +0100 Subject: [PATCH 1600/2884] qtest: move qtest_{get, set}_virtual_clock to accel/qtest/qtest.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20240530220610.1245424-5-pierrick.bouvier@linaro.org> Message-Id: <20240620152220.2192768-8-alex.bennee@linaro.org> --- accel/qtest/qtest.c | 12 ++++++++++++ include/sysemu/qtest.h | 3 --- system/qtest.c | 12 ------------ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 53182e6c2a..bf14032d29 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -24,6 +24,18 @@ #include "qemu/main-loop.h" #include "hw/core/cpu.h" +static int64_t qtest_clock_counter; + +static int64_t qtest_get_virtual_clock(void) +{ + return qatomic_read_i64(&qtest_clock_counter); +} + +static void qtest_set_virtual_clock(int64_t count) +{ + qatomic_set_i64(&qtest_clock_counter, count); +} + static int qtest_init_accel(MachineState *ms) { return 0; diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 45f3b7e1df..c161d75165 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -34,9 +34,6 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** void qtest_server_set_send_handler(void (*send)(void *, const char *), void *opaque); void qtest_server_inproc_recv(void *opaque, const char *buf); - -int64_t qtest_get_virtual_clock(void); -void qtest_set_virtual_clock(int64_t count); #endif #endif diff --git a/system/qtest.c b/system/qtest.c index 8cb98966b4..12703a2045 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -325,18 +325,6 @@ static void qtest_irq_handler(void *opaque, int n, int level) } } -static int64_t qtest_clock_counter; - -int64_t qtest_get_virtual_clock(void) -{ - return qatomic_read_i64(&qtest_clock_counter); -} - -void qtest_set_virtual_clock(int64_t count) -{ - qatomic_set_i64(&qtest_clock_counter, count); -} - static bool (*process_command_cb)(CharBackend *chr, gchar **words); void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)) From 847a65dd76bf1868c7966a2b2608dcd00cb68dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:16 +0100 Subject: [PATCH 1601/2884] plugins: add time control API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the ability to control time through the plugin API. Only one plugin can control time so it has to request control when loaded. There are probably more corner cases to catch here. Signed-off-by: Pierrick Bouvier [AJB: tweaked user-mode handling, merged QEMU_PLUGIN_API fix] Message-Id: <20240530220610.1245424-6-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-9-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 27 +++++++++++++++++++++++++++ plugins/api.c | 35 +++++++++++++++++++++++++++++++++++ plugins/qemu-plugins.symbols | 2 ++ 3 files changed, 64 insertions(+) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 95703d8fec..c71c705b69 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -661,6 +661,33 @@ void qemu_plugin_register_vcpu_mem_inline_per_vcpu( qemu_plugin_u64 entry, uint64_t imm); +/** + * qemu_plugin_request_time_control() - request the ability to control time + * + * This grants the plugin the ability to control system time. Only one + * plugin can control time so if multiple plugins request the ability + * all but the first will fail. + * + * Returns an opaque handle or NULL if fails + */ +QEMU_PLUGIN_API +const void *qemu_plugin_request_time_control(void); + +/** + * qemu_plugin_update_ns() - update system emulation time + * @handle: opaque handle returned by qemu_plugin_request_time_control() + * @time: time in nanoseconds + * + * This allows an appropriately authorised plugin (i.e. holding the + * time control handle) to move system time forward to @time. For + * user-mode emulation the time is not changed by this as all reported + * time comes from the host kernel. + * + * Start time is 0. + */ +QEMU_PLUGIN_API +void qemu_plugin_update_ns(const void *handle, int64_t time); + typedef void (*qemu_plugin_vcpu_syscall_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_index, int64_t num, uint64_t a1, uint64_t a2, diff --git a/plugins/api.c b/plugins/api.c index 6bdb26bbe3..4431a0ea7e 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -39,6 +39,7 @@ #include "qemu/main-loop.h" #include "qemu/plugin.h" #include "qemu/log.h" +#include "qemu/timer.h" #include "tcg/tcg.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" @@ -583,3 +584,37 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) } return total; } + +/* + * Time control + */ +static bool has_control; + +const void *qemu_plugin_request_time_control(void) +{ + if (!has_control) { + has_control = true; + return &has_control; + } + return NULL; +} + +#ifdef CONFIG_SOFTMMU +static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data) +{ + int64_t new_time = data.host_ulong; + qemu_clock_advance_virtual_time(new_time); +} +#endif + +void qemu_plugin_update_ns(const void *handle, int64_t new_time) +{ +#ifdef CONFIG_SOFTMMU + if (handle == &has_control) { + /* Need to execute out of cpu_exec, so bql can be locked. */ + async_run_on_cpu(current_cpu, + advance_virtual_time__async, + RUN_ON_CPU_HOST_ULONG(new_time)); + } +#endif +} diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index aa0a77a319..ca773d8d9f 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -38,6 +38,7 @@ qemu_plugin_register_vcpu_tb_exec_cond_cb; qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu; qemu_plugin_register_vcpu_tb_trans_cb; + qemu_plugin_request_time_control; qemu_plugin_reset; qemu_plugin_scoreboard_free; qemu_plugin_scoreboard_find; @@ -51,5 +52,6 @@ qemu_plugin_u64_set; qemu_plugin_u64_sum; qemu_plugin_uninstall; + qemu_plugin_update_ns; qemu_plugin_vcpu_for_each; }; From 508036532c5ee1c72896f6b950e8629cf334e485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 20 Jun 2024 16:22:17 +0100 Subject: [PATCH 1602/2884] plugins: add migration blocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the plugin in controlling time there is some state that might be missing from the plugin tracking it. Migration is unlikely to work in this case so lets put a migration blocker in to let the user know if they try. Suggested-by: Dr. David Alan Gilbert Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-10-alex.bennee@linaro.org> --- plugins/api.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/api.c b/plugins/api.c index 4431a0ea7e..2ff13d09de 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -47,6 +47,8 @@ #include "disas/disas.h" #include "plugin.h" #ifndef CONFIG_USER_ONLY +#include "qapi/error.h" +#include "migration/blocker.h" #include "exec/ram_addr.h" #include "qemu/plugin-memory.h" #include "hw/boards.h" @@ -589,11 +591,19 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) * Time control */ static bool has_control; +#ifdef CONFIG_SOFTMMU +static Error *migration_blocker; +#endif const void *qemu_plugin_request_time_control(void) { if (!has_control) { has_control = true; +#ifdef CONFIG_SOFTMMU + error_setg(&migration_blocker, + "TCG plugin time control does not support migration"); + migrate_add_blocker(&migration_blocker, NULL); +#endif return &has_control; } return NULL; From 72db6d54a4a9203736261fc63f32f6cd3486b7e4 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 20 Jun 2024 16:22:18 +0100 Subject: [PATCH 1603/2884] contrib/plugins: add Instructions Per Second (IPS) example for cost modeling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This plugin uses the new time control interface to make decisions about the state of time during the emulation. The algorithm is currently very simple. The user specifies an ips rate which applies per core. If the core runs ahead of its allocated execution time the plugin sleeps for a bit to let real time catch up. Either way time is updated for the emulation as a function of total executed instructions with some adjustments for cores that idle. Examples -------- Slow down execution of /bin/true: $ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin /bin/true |& grep total | sed -e 's/.*: //') $ time ./build/qemu-x86_64 -plugin ./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true real 4.000s Boot a Linux kernel simulating a 250MHz cpu: $ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append "console=ttyS0" -plugin ./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512 check time until kernel panic on serial0 Tested in system mode by booting a full debian system, and using: $ sysbench cpu run Performance decrease linearly with the given number of ips. Signed-off-by: Pierrick Bouvier Message-Id: <20240530220610.1245424-7-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-11-alex.bennee@linaro.org> --- contrib/plugins/Makefile | 1 + contrib/plugins/ips.c | 164 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 contrib/plugins/ips.c diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 0b64d2c1e3..449ead1130 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -27,6 +27,7 @@ endif NAMES += hwprofile NAMES += cache NAMES += drcov +NAMES += ips ifeq ($(CONFIG_WIN32),y) SO_SUFFIX := .dll diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c new file mode 100644 index 0000000000..29fa556d0f --- /dev/null +++ b/contrib/plugins/ips.c @@ -0,0 +1,164 @@ +/* + * Instructions Per Second (IPS) rate limiting plugin. + * + * This plugin can be used to restrict the execution of a system to a + * particular number of Instructions Per Second (IPS). This controls + * time as seen by the guest so while wall-clock time may be longer + * from the guests point of view time will pass at the normal rate. + * + * This uses the new plugin API which allows the plugin to control + * system time. + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +/* how many times do we update time per sec */ +#define NUM_TIME_UPDATE_PER_SEC 10 +#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000) + +static GMutex global_state_lock; + +static uint64_t max_insn_per_second = 1000 * 1000 * 1000; /* ips per core, per second */ +static uint64_t max_insn_per_quantum; /* trap every N instructions */ +static int64_t virtual_time_ns; /* last set virtual time */ + +static const void *time_handle; + +typedef struct { + uint64_t total_insn; + uint64_t quantum_insn; /* insn in last quantum */ + int64_t last_quantum_time; /* time when last quantum started */ +} vCPUTime; + +struct qemu_plugin_scoreboard *vcpus; + +/* return epoch time in ns */ +static int64_t now_ns(void) +{ + return g_get_real_time() * 1000; +} + +static uint64_t num_insn_during(int64_t elapsed_ns) +{ + double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC; + return num_secs * (double) max_insn_per_second; +} + +static int64_t time_for_insn(uint64_t num_insn) +{ + double num_secs = (double) num_insn / (double) max_insn_per_second; + return num_secs * (double) NSEC_IN_ONE_SEC; +} + +static void update_system_time(vCPUTime *vcpu) +{ + int64_t elapsed_ns = now_ns() - vcpu->last_quantum_time; + uint64_t max_insn = num_insn_during(elapsed_ns); + + if (vcpu->quantum_insn >= max_insn) { + /* this vcpu ran faster than expected, so it has to sleep */ + uint64_t insn_advance = vcpu->quantum_insn - max_insn; + uint64_t time_advance_ns = time_for_insn(insn_advance); + int64_t sleep_us = time_advance_ns / 1000; + g_usleep(sleep_us); + } + + vcpu->total_insn += vcpu->quantum_insn; + vcpu->quantum_insn = 0; + vcpu->last_quantum_time = now_ns(); + + /* based on total number of instructions, what should be the new time? */ + int64_t new_virtual_time = time_for_insn(vcpu->total_insn); + + g_mutex_lock(&global_state_lock); + + /* Time only moves forward. Another vcpu might have updated it already. */ + if (new_virtual_time > virtual_time_ns) { + qemu_plugin_update_ns(time_handle, new_virtual_time); + virtual_time_ns = new_virtual_time; + } + + g_mutex_unlock(&global_state_lock); +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + vcpu->total_insn = 0; + vcpu->quantum_insn = 0; + vcpu->last_quantum_time = now_ns(); +} + +static void vcpu_exit(qemu_plugin_id_t id, unsigned int cpu_index) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + update_system_time(vcpu); +} + +static void every_quantum_insn(unsigned int cpu_index, void *udata) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + g_assert(vcpu->quantum_insn >= max_insn_per_quantum); + update_system_time(vcpu); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n_insns = qemu_plugin_tb_n_insns(tb); + qemu_plugin_u64 quantum_insn = + qemu_plugin_scoreboard_u64_in_struct(vcpus, vCPUTime, quantum_insn); + /* count (and eventually trap) once per tb */ + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, quantum_insn, n_insns); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, every_quantum_insn, + QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_COND_GE, + quantum_insn, max_insn_per_quantum, NULL); +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) +{ + qemu_plugin_scoreboard_free(vcpus); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "ips") == 0) { + max_insn_per_second = g_ascii_strtoull(tokens[1], NULL, 10); + if (!max_insn_per_second && errno) { + fprintf(stderr, "%s: couldn't parse %s (%s)\n", + __func__, tokens[1], g_strerror(errno)); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + vcpus = qemu_plugin_scoreboard_new(sizeof(vCPUTime)); + max_insn_per_quantum = max_insn_per_second / NUM_TIME_UPDATE_PER_SEC; + + time_handle = qemu_plugin_request_time_control(); + g_assert(time_handle); + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_vcpu_exit_cb(id, vcpu_exit); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} From ca7d7f4276d4fd4711b693a78fec79652cf2ffc5 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 20 Jun 2024 16:22:19 +0100 Subject: [PATCH 1604/2884] plugins: fix inject_mem_cb rw masking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are not booleans, but masks. Issue found by Richard Henderson. Fixes: f86fd4d8721 ("plugins: distinct types for callbacks") Signed-off-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-Id: <20240612195147.93121-3-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-12-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 4 ++-- plugins/core.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index cc1634e7a6..b6bae32b99 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -240,13 +240,13 @@ static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb, { switch (cb->type) { case PLUGIN_CB_MEM_REGULAR: - if (rw && cb->regular.rw) { + if (rw & cb->regular.rw) { gen_mem_cb(&cb->regular, meminfo, addr); } break; case PLUGIN_CB_INLINE_ADD_U64: case PLUGIN_CB_INLINE_STORE_U64: - if (rw && cb->inline_insn.rw) { + if (rw & cb->inline_insn.rw) { inject_cb(cb); } break; diff --git a/plugins/core.c b/plugins/core.c index badede28cf..9d737d8278 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -589,7 +589,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, switch (cb->type) { case PLUGIN_CB_MEM_REGULAR: - if (rw && cb->regular.rw) { + if (rw & cb->regular.rw) { cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), vaddr, cb->regular.userp); @@ -597,7 +597,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, break; case PLUGIN_CB_INLINE_ADD_U64: case PLUGIN_CB_INLINE_STORE_U64: - if (rw && cb->inline_insn.rw) { + if (rw & cb->inline_insn.rw) { exec_inline_op(cb->type, &cb->inline_insn, cpu->cpu_index); } break; From fce3d48038e9f38e3e342a59f76c7f9f9b043ed2 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 20 Jun 2024 16:22:20 +0100 Subject: [PATCH 1605/2884] accel/tcg: Avoid unnecessary call overhead from qemu_plugin_vcpu_mem_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are not any QEMU plugin memory callback functions, checking before calling the qemu_plugin_vcpu_mem_cb function can reduce the function call overhead. Signed-off-by: Max Chou Reviewed-by: Richard Henderson Reviewed-by: Frank Chang Message-Id: <20240613175122.1299212-2-max.chou@sifive.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20240620152220.2192768-13-alex.bennee@linaro.org> --- accel/tcg/ldst_common.c.inc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index c82048e377..87ceb95487 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -125,7 +125,9 @@ void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi) static void plugin_load_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi) { - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + if (cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + } } uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) @@ -188,7 +190,9 @@ Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, static void plugin_store_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi) { - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); + if (cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); + } } void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, From cd44ccee18e2a8856f7ddc0b00bbc40dfa9fd5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 8 Apr 2024 15:41:56 +0200 Subject: [PATCH 1606/2884] hw/sd/sdcard: Inline BLK_READ_BLOCK / BLK_WRITE_BLOCK macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These macros only save 3 chars and make the code harder to maintain, simply remove them. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-20-philmd@linaro.org> --- hw/sd/sd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 969340e5cb..d4e3d079a8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -819,8 +819,6 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) } } -#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len) -#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len) #define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len) #define APP_WRITE_BLOCK(a, len) @@ -872,7 +870,7 @@ static void sd_erase(SDState *sd) continue; } } - BLK_WRITE_BLOCK(erase_addr, erase_len); + sd_blk_write(sd, erase_addr, erase_len); } } @@ -1903,7 +1901,7 @@ void sd_write_byte(SDState *sd, uint8_t value) if (sd->data_offset >= sd->blk_len) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd_blk_write(sd, sd->data_start, sd->data_offset); sd->blk_written ++; sd->csd[14] |= 0x40; /* Bzzzzzzztt .... Operation complete. */ @@ -1929,7 +1927,7 @@ void sd_write_byte(SDState *sd, uint8_t value) if (sd->data_offset >= sd->blk_len) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd_blk_write(sd, sd->data_start, sd->data_offset); sd->blk_written++; sd->data_start += sd->blk_len; sd->data_offset = 0; @@ -2077,8 +2075,9 @@ uint8_t sd_read_byte(SDState *sd) break; case 17: /* CMD17: READ_SINGLE_BLOCK */ - if (sd->data_offset == 0) - BLK_READ_BLOCK(sd->data_start, io_len); + if (sd->data_offset == 0) { + sd_blk_read(sd, sd->data_start, io_len); + } ret = sd->data[sd->data_offset ++]; if (sd->data_offset >= io_len) @@ -2091,7 +2090,7 @@ uint8_t sd_read_byte(SDState *sd) sd->data_start, io_len)) { return 0x00; } - BLK_READ_BLOCK(sd->data_start, io_len); + sd_blk_read(sd, sd->data_start, io_len); } ret = sd->data[sd->data_offset ++]; From 76ae9a231487a2b127c90bcb657fd42a1f6c06f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 25 Oct 2020 20:17:47 +0100 Subject: [PATCH 1607/2884] hw/sd/sdcard: Add comments around registers and commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240621080554.18986-21-philmd@linaro.org> --- hw/sd/sd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d4e3d079a8..a48010cfc1 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -315,6 +315,8 @@ static uint8_t sd_crc7(const void *message, size_t width) return shift_reg; } +/* Operation Conditions register */ + #define OCR_POWER_DELAY_NS 500000 /* 0.5ms */ FIELD(OCR, VDD_VOLTAGE_WINDOW, 0, 24) @@ -364,6 +366,8 @@ static void sd_set_ocr(SDState *sd) } } +/* SD Configuration register */ + static void sd_set_scr(SDState *sd) { sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */ @@ -386,6 +390,8 @@ static void sd_set_scr(SDState *sd) sd->scr[7] = 0x00; } +/* Card IDentification register */ + #define MID 0xaa #define OID "XY" #define PNM "QEMU!" @@ -411,6 +417,8 @@ static void sd_set_cid(SDState *sd) sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; } +/* Card-Specific Data register */ + #define HWBLOCK_SHIFT 9 /* 512 bytes */ #define SECTOR_SHIFT 5 /* 16 kilobytes */ #define WPGROUP_SHIFT 7 /* 2 megs */ @@ -480,6 +488,8 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; } +/* Relative Card Address register */ + static void sd_set_rca(SDState *sd) { sd->rca += 0x4567; @@ -493,6 +503,8 @@ static uint16_t sd_req_get_rca(SDState *s, SDRequest req) return 0; } +/* Card Status register */ + FIELD(CSR, AKE_SEQ_ERROR, 3, 1) FIELD(CSR, APP_CMD, 5, 1) FIELD(CSR, FX_EVENT, 6, 1) @@ -623,6 +635,8 @@ static void sd_reset(DeviceState *dev) sect = sd_addr_to_wpnum(size) + 1; sd->state = sd_idle_state; + + /* card registers */ sd->rca = 0x0000; sd->size = size; sd_set_ocr(sd); @@ -1055,6 +1069,7 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) return sd_illegal; } +/* CMD0 */ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) { if (sd->state != sd_inactive_state) { @@ -1065,6 +1080,7 @@ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) return sd_is_spi(sd) ? sd_r1 : sd_r0; } +/* CMD1 */ static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req) { sd->state = sd_transfer_state; @@ -1072,6 +1088,7 @@ static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req) return sd_r1; } +/* CMD2 */ static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) { switch (sd->state) { @@ -1083,6 +1100,7 @@ static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) } } +/* CMD3 */ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) { switch (sd->state) { @@ -1097,6 +1115,7 @@ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) } } +/* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { @@ -1113,6 +1132,7 @@ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) return sd_r1; } +/* CMD23 */ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) { if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { From 1f94b21801b22b01ba131e7d776f07c1062d9433 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:27 +0800 Subject: [PATCH 1608/2884] backends: Introduce HostIOMMUDevice abstract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A HostIOMMUDevice is an abstraction for an assigned device that is protected by a physical IOMMU (aka host IOMMU). The userspace interaction with this physical IOMMU can be done either through the VFIO IOMMU type 1 legacy backend or the new iommufd backend. The assigned device can be a VFIO device or a VDPA device. The HostIOMMUDevice is needed to interact with the host IOMMU that protects the assigned device. It is especially useful when the device is also protected by a virtual IOMMU as this latter use the translation services of the physical IOMMU and is constrained by it. In that context the HostIOMMUDevice can be passed to the virtual IOMMU to collect physical IOMMU capabilities such as the supported address width. In the future, the virtual IOMMU will use the HostIOMMUDevice to program the guest page tables in the first translation stage of the physical IOMMU. Introduce .realize() to initialize HostIOMMUDevice further after instance init. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- MAINTAINERS | 2 ++ backends/host_iommu_device.c | 33 +++++++++++++++++++ backends/meson.build | 1 + include/sysemu/host_iommu_device.h | 53 ++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 backends/host_iommu_device.c create mode 100644 include/sysemu/host_iommu_device.h diff --git a/MAINTAINERS b/MAINTAINERS index f144b5af44..19f67dc5d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2198,6 +2198,8 @@ M: Zhenzhong Duan S: Supported F: backends/iommufd.c F: include/sysemu/iommufd.h +F: backends/host_iommu_device.c +F: include/sysemu/host_iommu_device.h F: include/qemu/chardev_open.h F: util/chardev_open.c F: docs/devel/vfio-iommufd.rst diff --git a/backends/host_iommu_device.c b/backends/host_iommu_device.c new file mode 100644 index 0000000000..8f2dda1beb --- /dev/null +++ b/backends/host_iommu_device.c @@ -0,0 +1,33 @@ +/* + * Host IOMMU device abstract + * + * Copyright (C) 2024 Intel Corporation. + * + * Authors: Zhenzhong Duan + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "sysemu/host_iommu_device.h" + +OBJECT_DEFINE_ABSTRACT_TYPE(HostIOMMUDevice, + host_iommu_device, + HOST_IOMMU_DEVICE, + OBJECT) + +static void host_iommu_device_class_init(ObjectClass *oc, void *data) +{ +} + +static void host_iommu_device_init(Object *obj) +{ +} + +static void host_iommu_device_finalize(Object *obj) +{ + HostIOMMUDevice *hiod = HOST_IOMMU_DEVICE(obj); + + g_free(hiod->name); +} diff --git a/backends/meson.build b/backends/meson.build index 8b2b111497..106312f0c8 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -16,6 +16,7 @@ if host_os != 'windows' endif if host_os == 'linux' system_ss.add(files('hostmem-memfd.c')) + system_ss.add(files('host_iommu_device.c')) endif if keyutils.found() system_ss.add(keyutils, files('cryptodev-lkcf.c')) diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h new file mode 100644 index 0000000000..db47a16189 --- /dev/null +++ b/include/sysemu/host_iommu_device.h @@ -0,0 +1,53 @@ +/* + * Host IOMMU device abstract declaration + * + * Copyright (C) 2024 Intel Corporation. + * + * Authors: Zhenzhong Duan + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef HOST_IOMMU_DEVICE_H +#define HOST_IOMMU_DEVICE_H + +#include "qom/object.h" +#include "qapi/error.h" + +#define TYPE_HOST_IOMMU_DEVICE "host-iommu-device" +OBJECT_DECLARE_TYPE(HostIOMMUDevice, HostIOMMUDeviceClass, HOST_IOMMU_DEVICE) + +struct HostIOMMUDevice { + Object parent_obj; + + char *name; +}; + +/** + * struct HostIOMMUDeviceClass - The base class for all host IOMMU devices. + * + * Different types of host devices (e.g., VFIO or VDPA device) or devices + * with different backend (e.g., VFIO legacy container or IOMMUFD backend) + * will have different implementations of the HostIOMMUDeviceClass. + */ +struct HostIOMMUDeviceClass { + ObjectClass parent_class; + + /** + * @realize: initialize host IOMMU device instance further. + * + * Mandatory callback. + * + * @hiod: pointer to a host IOMMU device instance. + * + * @opaque: pointer to agent device of this host IOMMU device, + * e.g., VFIO base device or VDPA device. + * + * @errp: pass an Error out when realize fails. + * + * Returns: true on success, false on failure. + */ + bool (*realize)(HostIOMMUDevice *hiod, void *opaque, Error **errp); +}; +#endif From 38998c79a1831e23ffed4b8b6f76222c1fedb9d5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:28 +0800 Subject: [PATCH 1609/2884] backends/host_iommu_device: Introduce HostIOMMUDeviceCaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HostIOMMUDeviceCaps's elements map to the host IOMMU's capabilities. Different platform IOMMU can support different elements. Currently only two elements, type and aw_bits, type hints the host platform IOMMU type, i.e., INTEL vtd, ARM smmu, etc; aw_bits hints host IOMMU address width. Introduce .get_cap() handler to check if HOST_IOMMU_DEVICE_CAP_XXX is supported. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- include/sysemu/host_iommu_device.h | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index db47a16189..a57873958b 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -15,6 +15,18 @@ #include "qom/object.h" #include "qapi/error.h" +/** + * struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities. + * + * @type: host platform IOMMU type. + * + * @aw_bits: host IOMMU address width. 0xff if no limitation. + */ +typedef struct HostIOMMUDeviceCaps { + uint32_t type; + uint8_t aw_bits; +} HostIOMMUDeviceCaps; + #define TYPE_HOST_IOMMU_DEVICE "host-iommu-device" OBJECT_DECLARE_TYPE(HostIOMMUDevice, HostIOMMUDeviceClass, HOST_IOMMU_DEVICE) @@ -22,6 +34,7 @@ struct HostIOMMUDevice { Object parent_obj; char *name; + HostIOMMUDeviceCaps caps; }; /** @@ -49,5 +62,30 @@ struct HostIOMMUDeviceClass { * Returns: true on success, false on failure. */ bool (*realize)(HostIOMMUDevice *hiod, void *opaque, Error **errp); + /** + * @get_cap: check if a host IOMMU device capability is supported. + * + * Optional callback, if not implemented, hint not supporting query + * of @cap. + * + * @hiod: pointer to a host IOMMU device instance. + * + * @cap: capability to check. + * + * @errp: pass an Error out when fails to query capability. + * + * Returns: <0 on failure, 0 if a @cap is unsupported, or else + * 1 or some positive value for some special @cap, + * i.e., HOST_IOMMU_DEVICE_CAP_AW_BITS. + */ + int (*get_cap)(HostIOMMUDevice *hiod, int cap, Error **errp); }; + +/* + * Host IOMMU device capability list. + */ +#define HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE 0 +#define HOST_IOMMU_DEVICE_CAP_AW_BITS 1 + +#define HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX 64 #endif From 0533739ecefbd3b820d32b576ad10dc6b0d56c29 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:29 +0800 Subject: [PATCH 1610/2884] vfio/container: Introduce TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO represents a host IOMMU device under VFIO legacy container backend. It will have its own realize implementation. Suggested-by: Eric Auger Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/vfio/container.c | 5 ++++- include/hw/vfio/vfio-common.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 096cc97258..c4fca2dfca 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1141,7 +1141,10 @@ static const TypeInfo types[] = { .name = TYPE_VFIO_IOMMU_LEGACY, .parent = TYPE_VFIO_IOMMU, .class_init = vfio_iommu_legacy_class_init, - }, + }, { + .name = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, + .parent = TYPE_HOST_IOMMU_DEVICE, + } }; DEFINE_TYPES(types) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 4cb1ab8645..75b167979a 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -31,6 +31,7 @@ #endif #include "sysemu/sysemu.h" #include "hw/vfio/vfio-container-base.h" +#include "sysemu/host_iommu_device.h" #define VFIO_MSG_PREFIX "vfio %s: " @@ -171,6 +172,8 @@ typedef struct VFIOGroup { bool ram_block_discard_allowed; } VFIOGroup; +#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" + typedef struct VFIODMABuf { QemuDmaBuf *buf; uint32_t pos_x, pos_y, pos_updates; From 9005f928447841ed253e000d5e8220e381872cb0 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:30 +0800 Subject: [PATCH 1611/2884] backends/iommufd: Introduce TYPE_HOST_IOMMU_DEVICE_IOMMUFD[_VFIO] devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE_HOST_IOMMU_DEVICE_IOMMUFD represents a host IOMMU device under iommufd backend. It is abstract, because it is going to be derived into VFIO or VDPA type'd device. It will have its own .get_cap() implementation. TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO is a sub-class of TYPE_HOST_IOMMU_DEVICE_IOMMUFD, represents a VFIO type'd host IOMMU device under iommufd backend. It will be created during VFIO device attaching and passed to vIOMMU. It will have its own .realize() implementation. Opportunistically, add missed header to include/sysemu/iommufd.h. Suggested-by: Cédric Le Goater Signed-off-by: Yi Liu Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- backends/iommufd.c | 35 ++++++++++++++++++----------------- hw/vfio/iommufd.c | 5 ++++- include/hw/vfio/vfio-common.h | 3 +++ include/sysemu/iommufd.h | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index c506afbdac..012f18d8d8 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -208,23 +208,24 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, return ret; } -static const TypeInfo iommufd_backend_info = { - .name = TYPE_IOMMUFD_BACKEND, - .parent = TYPE_OBJECT, - .instance_size = sizeof(IOMMUFDBackend), - .instance_init = iommufd_backend_init, - .instance_finalize = iommufd_backend_finalize, - .class_size = sizeof(IOMMUFDBackendClass), - .class_init = iommufd_backend_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } +static const TypeInfo types[] = { + { + .name = TYPE_IOMMUFD_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(IOMMUFDBackend), + .instance_init = iommufd_backend_init, + .instance_finalize = iommufd_backend_finalize, + .class_size = sizeof(IOMMUFDBackendClass), + .class_init = iommufd_backend_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } + }, { + .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, + .parent = TYPE_HOST_IOMMU_DEVICE, + .abstract = true, } }; -static void register_types(void) -{ - type_register_static(&iommufd_backend_info); -} - -type_init(register_types); +DEFINE_TYPES(types) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 554f9a6292..e4a507d55c 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -624,7 +624,10 @@ static const TypeInfo types[] = { .name = TYPE_VFIO_IOMMU_IOMMUFD, .parent = TYPE_VFIO_IOMMU, .class_init = vfio_iommu_iommufd_class_init, - }, + }, { + .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, + .parent = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, + } }; DEFINE_TYPES(types) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 75b167979a..56d1717211 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -32,6 +32,7 @@ #include "sysemu/sysemu.h" #include "hw/vfio/vfio-container-base.h" #include "sysemu/host_iommu_device.h" +#include "sysemu/iommufd.h" #define VFIO_MSG_PREFIX "vfio %s: " @@ -173,6 +174,8 @@ typedef struct VFIOGroup { } VFIOGroup; #define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ + TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" typedef struct VFIODMABuf { QemuDmaBuf *buf; diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index 293bfbe967..f6e6d6e1f9 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -1,9 +1,23 @@ +/* + * iommufd container backend declaration + * + * Copyright (C) 2024 Intel Corporation. + * Copyright Red Hat, Inc. 2024 + * + * Authors: Yi Liu + * Eric Auger + * Zhenzhong Duan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + #ifndef SYSEMU_IOMMUFD_H #define SYSEMU_IOMMUFD_H #include "qom/object.h" #include "exec/hwaddr.h" #include "exec/cpu-common.h" +#include "sysemu/host_iommu_device.h" #define TYPE_IOMMUFD_BACKEND "iommufd" OBJECT_DECLARE_TYPE(IOMMUFDBackend, IOMMUFDBackendClass, IOMMUFD_BACKEND) @@ -33,4 +47,6 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size); + +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From 6f274444c579305d14d355bab24af31ea2bef224 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:31 +0800 Subject: [PATCH 1612/2884] range: Introduce range_get_last_bit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper get the highest 1 bit position of the upper bound. If the range is empty or upper bound is zero, -1 is returned. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- include/qemu/range.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/qemu/range.h b/include/qemu/range.h index 205e1da76d..4ce694a398 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -20,6 +20,8 @@ #ifndef QEMU_RANGE_H #define QEMU_RANGE_H +#include "qemu/bitops.h" + /* * Operations on 64 bit address ranges. * Notes: @@ -217,6 +219,15 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1, return !(last2 < first1 || last1 < first2); } +/* Get highest non-zero bit position of a range */ +static inline int range_get_last_bit(Range *range) +{ + if (range_is_empty(range)) { + return -1; + } + return 63 - clz64(range->upb); +} + /* * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. * Both @a and @b must not be empty. From d441e05e26033eb0e49c4185293424a480ef750f Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:32 +0800 Subject: [PATCH 1613/2884] vfio/container: Implement HostIOMMUDeviceClass::realize() handler The realize function populates the capabilities. For now only the aw_bits caps is computed for legacy backend. Introduce a helper function vfio_device_get_aw_bits() which calls range_get_last_bit() to get host aw_bits and package it in HostIOMMUDeviceCaps for query with .get_cap(). This helper will also be used by iommufd backend. Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/vfio/container.c | 19 +++++++++++++++++++ hw/vfio/helpers.c | 17 +++++++++++++++++ include/hw/vfio/vfio-common.h | 1 + 3 files changed, 37 insertions(+) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index c4fca2dfca..2f62c13214 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1136,6 +1136,24 @@ static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data) vioc->pci_hot_reset = vfio_legacy_pci_hot_reset; }; +static bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque, + Error **errp) +{ + VFIODevice *vdev = opaque; + + hiod->name = g_strdup(vdev->name); + hiod->caps.aw_bits = vfio_device_get_aw_bits(vdev); + + return true; +} + +static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) +{ + HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); + + hioc->realize = hiod_legacy_vfio_realize; +}; + static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_LEGACY, @@ -1144,6 +1162,7 @@ static const TypeInfo types[] = { }, { .name = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, .parent = TYPE_HOST_IOMMU_DEVICE, + .class_init = hiod_legacy_vfio_class_init, } }; diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 27ea26aa48..b14edd46ed 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -658,3 +658,20 @@ void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, vbasedev->ram_block_discard_allowed = ram_discard; } + +int vfio_device_get_aw_bits(VFIODevice *vdev) +{ + /* + * iova_ranges is a sorted list. For old kernels that support + * VFIO but not support query of iova ranges, iova_ranges is NULL, + * in this case HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX(64) is returned. + */ + GList *l = g_list_last(vdev->bcontainer->iova_ranges); + + if (l) { + Range *range = l->data; + return range_get_last_bit(range) + 1; + } + + return HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX; +} diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 56d1717211..105b8b7e80 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -289,4 +289,5 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp); void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp); void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, DeviceState *dev, bool ram_discard); +int vfio_device_get_aw_bits(VFIODevice *vdev); #endif /* HW_VFIO_VFIO_COMMON_H */ From 42965386ea21fe375a5a2a6d85261663576d86d9 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:33 +0800 Subject: [PATCH 1614/2884] backends/iommufd: Introduce helper function iommufd_backend_get_device_info() Introduce a helper function iommufd_backend_get_device_info() to get host IOMMU related information through iommufd uAPI. Signed-off-by: Yi Liu Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- backends/iommufd.c | 22 ++++++++++++++++++++++ include/sysemu/iommufd.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index 012f18d8d8..c7e969d6f7 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -208,6 +208,28 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, return ret; } +bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, + uint32_t *type, void *data, uint32_t len, + Error **errp) +{ + struct iommu_hw_info info = { + .size = sizeof(info), + .dev_id = devid, + .data_len = len, + .data_uptr = (uintptr_t)data, + }; + + if (ioctl(be->fd, IOMMU_GET_HW_INFO, &info)) { + error_setg_errno(errp, errno, "Failed to get hardware info"); + return false; + } + + g_assert(type); + *type = info.out_data_type; + + return true; +} + static const TypeInfo types[] = { { .name = TYPE_IOMMUFD_BACKEND, diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index f6e6d6e1f9..9edfec6045 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -47,6 +47,9 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size); +bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, + uint32_t *type, void *data, uint32_t len, + Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From 930589520128a5f25e65f9f923ac8dc6fac32ff8 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:34 +0800 Subject: [PATCH 1615/2884] vfio/iommufd: Implement HostIOMMUDeviceClass::realize() handler It calls iommufd_backend_get_device_info() to get host IOMMU related information and translate it into HostIOMMUDeviceCaps for query with .get_cap(). For aw_bits, use the same way as legacy backend by calling vfio_device_get_aw_bits() which is common for different vendor IOMMU. Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/vfio/iommufd.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index e4a507d55c..1674c61227 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -619,6 +619,35 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) vioc->pci_hot_reset = iommufd_cdev_pci_hot_reset; }; +static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, + Error **errp) +{ + VFIODevice *vdev = opaque; + HostIOMMUDeviceCaps *caps = &hiod->caps; + enum iommu_hw_info_type type; + union { + struct iommu_hw_info_vtd vtd; + } data; + + if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, + &type, &data, sizeof(data), errp)) { + return false; + } + + hiod->name = g_strdup(vdev->name); + caps->type = type; + caps->aw_bits = vfio_device_get_aw_bits(vdev); + + return true; +} + +static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) +{ + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); + + hiodc->realize = hiod_iommufd_vfio_realize; +}; + static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_IOMMUFD, @@ -627,6 +656,7 @@ static const TypeInfo types[] = { }, { .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, .parent = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, + .class_init = hiod_iommufd_vfio_class_init, } }; From ed92ed2d486e5fddacb1e611c57e976eb160d0a5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:35 +0800 Subject: [PATCH 1616/2884] vfio/container: Implement HostIOMMUDeviceClass::get_cap() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/vfio/container.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 2f62c13214..99beeba422 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1147,11 +1147,26 @@ static bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque, return true; } +static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap, + Error **errp) +{ + HostIOMMUDeviceCaps *caps = &hiod->caps; + + switch (cap) { + case HOST_IOMMU_DEVICE_CAP_AW_BITS: + return caps->aw_bits; + default: + error_setg(errp, "%s: unsupported capability %x", hiod->name, cap); + return -EINVAL; + } +} + static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) { HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); hioc->realize = hiod_legacy_vfio_realize; + hioc->get_cap = hiod_legacy_vfio_get_cap; }; static const TypeInfo types[] = { From 63c6e83ec236f5de07444eac1d0bb747c506cc90 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:36 +0800 Subject: [PATCH 1617/2884] backends/iommufd: Implement HostIOMMUDeviceClass::get_cap() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- backends/iommufd.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index c7e969d6f7..84fefbc9ee 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -230,6 +230,28 @@ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, return true; } +static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) +{ + HostIOMMUDeviceCaps *caps = &hiod->caps; + + switch (cap) { + case HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE: + return caps->type; + case HOST_IOMMU_DEVICE_CAP_AW_BITS: + return caps->aw_bits; + default: + error_setg(errp, "%s: unsupported capability %x", hiod->name, cap); + return -EINVAL; + } +} + +static void hiod_iommufd_class_init(ObjectClass *oc, void *data) +{ + HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); + + hioc->get_cap = hiod_iommufd_get_cap; +}; + static const TypeInfo types[] = { { .name = TYPE_IOMMUFD_BACKEND, @@ -246,6 +268,7 @@ static const TypeInfo types[] = { }, { .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, .parent = TYPE_HOST_IOMMU_DEVICE, + .class_init = hiod_iommufd_class_init, .abstract = true, } }; From a7fd91b876fe788c7401120c8eefe5c91b6f17c3 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:37 +0800 Subject: [PATCH 1618/2884] vfio: Create host IOMMU device instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create host IOMMU device instance in vfio_attach_device() and call .realize() to initialize it further. Introuduce attribute VFIOIOMMUClass::hiod_typename and initialize it based on VFIO backend type. It will facilitate HostIOMMUDevice creation in vfio_attach_device(). Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/vfio/common.c | 16 +++++++++++++++- hw/vfio/container.c | 2 ++ hw/vfio/iommufd.c | 2 ++ include/hw/vfio/vfio-common.h | 1 + include/hw/vfio/vfio-container-base.h | 3 +++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f9619a1dfb..f20a7b5bba 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1528,6 +1528,7 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, { const VFIOIOMMUClass *ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); + HostIOMMUDevice *hiod; if (vbasedev->iommufd) { ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); @@ -1535,7 +1536,19 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, assert(ops); - return ops->attach_device(name, vbasedev, as, errp); + if (!ops->attach_device(name, vbasedev, as, errp)) { + return false; + } + + hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); + if (!HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp)) { + object_unref(hiod); + ops->detach_device(vbasedev); + return false; + } + vbasedev->hiod = hiod; + + return true; } void vfio_detach_device(VFIODevice *vbasedev) @@ -1543,5 +1556,6 @@ void vfio_detach_device(VFIODevice *vbasedev) if (!vbasedev->bcontainer) { return; } + object_unref(vbasedev->hiod); vbasedev->bcontainer->ops->detach_device(vbasedev); } diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 99beeba422..26e6f7fb4f 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1126,6 +1126,8 @@ static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + vioc->hiod_typename = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO; + vioc->setup = vfio_legacy_setup; vioc->dma_map = vfio_legacy_dma_map; vioc->dma_unmap = vfio_legacy_dma_unmap; diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 1674c61227..409ed3dcc9 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -612,6 +612,8 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + vioc->hiod_typename = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO; + vioc->dma_map = iommufd_cdev_map; vioc->dma_unmap = iommufd_cdev_unmap; vioc->attach_device = iommufd_cdev_attach; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 105b8b7e80..776de8064f 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -127,6 +127,7 @@ typedef struct VFIODevice { OnOffAuto pre_copy_dirty_page_tracking; bool dirty_pages_supported; bool dirty_tracking; + HostIOMMUDevice *hiod; int devid; IOMMUFDBackend *iommufd; } VFIODevice; diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 2776481fc9..442c0dfc4c 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -109,6 +109,9 @@ DECLARE_CLASS_CHECKERS(VFIOIOMMUClass, VFIO_IOMMU, TYPE_VFIO_IOMMU) struct VFIOIOMMUClass { InterfaceClass parent_class; + /* Properties */ + const char *hiod_typename; + /* basic feature */ bool (*setup)(VFIOContainerBase *bcontainer, Error **errp); int (*dma_map)(const VFIOContainerBase *bcontainer, From 6c8ed5fea1ff60167736d530e39f3903a826df20 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:38 +0800 Subject: [PATCH 1619/2884] hw/pci: Introduce helper function pci_device_get_iommu_bus_devfn() Extract out pci_device_get_iommu_bus_devfn() from pci_device_iommu_address_space() to facilitate implementation of pci_device_[set|unset]_iommu_device() in following patch. No functional change intended. Signed-off-by: Yi Liu Signed-off-by: Yi Sun Signed-off-by: Nicolin Chen Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/pci/pci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 324c1302d2..02a4bb2af6 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2648,11 +2648,27 @@ static void pci_device_class_base_init(ObjectClass *klass, void *data) } } -AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) +/* + * Get IOMMU root bus, aliased bus and devfn of a PCI device + * + * IOMMU root bus is needed by all call sites to call into iommu_ops. + * For call sites which don't need aliased BDF, passing NULL to + * aliased_[bus|devfn] is allowed. + * + * @piommu_bus: return root #PCIBus backed by an IOMMU for the PCI device. + * + * @aliased_bus: return aliased #PCIBus of the PCI device, optional. + * + * @aliased_devfn: return aliased devfn of the PCI device, optional. + */ +static void pci_device_get_iommu_bus_devfn(PCIDevice *dev, + PCIBus **piommu_bus, + PCIBus **aliased_bus, + int *aliased_devfn) { PCIBus *bus = pci_get_bus(dev); PCIBus *iommu_bus = bus; - uint8_t devfn = dev->devfn; + int devfn = dev->devfn; while (iommu_bus && !iommu_bus->iommu_ops && iommu_bus->parent_dev) { PCIBus *parent_bus = pci_get_bus(iommu_bus->parent_dev); @@ -2693,7 +2709,33 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) iommu_bus = parent_bus; } - if (!pci_bus_bypass_iommu(bus) && iommu_bus->iommu_ops) { + + assert(0 <= devfn && devfn < PCI_DEVFN_MAX); + assert(iommu_bus); + + if (pci_bus_bypass_iommu(bus) || !iommu_bus->iommu_ops) { + iommu_bus = NULL; + } + + *piommu_bus = iommu_bus; + + if (aliased_bus) { + *aliased_bus = bus; + } + + if (aliased_devfn) { + *aliased_devfn = devfn; + } +} + +AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); + if (iommu_bus) { return iommu_bus->iommu_ops->get_address_space(bus, iommu_bus->iommu_opaque, devfn); } From b025ea6886fbea80f0c3ab7e54b4cfa7040ab8bf Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Wed, 5 Jun 2024 16:30:39 +0800 Subject: [PATCH 1620/2884] hw/pci: Introduce pci_device_[set|unset]_iommu_device() pci_device_[set|unset]_iommu_device() call pci_device_get_iommu_bus_devfn() to get iommu_bus->iommu_ops and call [set|unset]_iommu_device callback to set/unset HostIOMMUDevice for a given PCI device. Signed-off-by: Yi Liu Signed-off-by: Yi Sun Signed-off-by: Nicolin Chen Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/pci/pci.c | 27 +++++++++++++++++++++++++++ include/hw/pci/pci.h | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 02a4bb2af6..c8a8aab306 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2742,6 +2742,33 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) return &address_space_memory; } +bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, + Error **errp) +{ + PCIBus *iommu_bus; + + /* set_iommu_device requires device's direct BDF instead of aliased BDF */ + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL); + if (iommu_bus && iommu_bus->iommu_ops->set_iommu_device) { + return iommu_bus->iommu_ops->set_iommu_device(pci_get_bus(dev), + iommu_bus->iommu_opaque, + dev->devfn, hiod, errp); + } + return true; +} + +void pci_device_unset_iommu_device(PCIDevice *dev) +{ + PCIBus *iommu_bus; + + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL); + if (iommu_bus && iommu_bus->iommu_ops->unset_iommu_device) { + return iommu_bus->iommu_ops->unset_iommu_device(pci_get_bus(dev), + iommu_bus->iommu_opaque, + dev->devfn); + } +} + void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) { /* diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index eaa3fc99d8..eb26cac810 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -3,6 +3,7 @@ #include "exec/memory.h" #include "sysemu/dma.h" +#include "sysemu/host_iommu_device.h" /* PCI includes legacy ISA access. */ #include "hw/isa/isa.h" @@ -383,10 +384,45 @@ typedef struct PCIIOMMUOps { * * @devfn: device and function number */ - AddressSpace * (*get_address_space)(PCIBus *bus, void *opaque, int devfn); + AddressSpace * (*get_address_space)(PCIBus *bus, void *opaque, int devfn); + /** + * @set_iommu_device: attach a HostIOMMUDevice to a vIOMMU + * + * Optional callback, if not implemented in vIOMMU, then vIOMMU can't + * retrieve host information from the associated HostIOMMUDevice. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @dev: the #HostIOMMUDevice to attach. + * + * @errp: pass an Error out only when return false + * + * Returns: true if HostIOMMUDevice is attached or else false with errp set. + */ + bool (*set_iommu_device)(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *dev, Error **errp); + /** + * @unset_iommu_device: detach a HostIOMMUDevice from a vIOMMU + * + * Optional callback. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + */ + void (*unset_iommu_device)(PCIBus *bus, void *opaque, int devfn); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); +bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, + Error **errp); +void pci_device_unset_iommu_device(PCIDevice *dev); /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus From ee26474daa127d7ebfb5e3dd6633361decda9e08 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:40 +0800 Subject: [PATCH 1621/2884] vfio/pci: Pass HostIOMMUDevice to vIOMMU With HostIOMMUDevice passed, vIOMMU can check compatibility with host IOMMU, call into IOMMUFD specific methods, etc. Originally-by: Yi Liu Signed-off-by: Nicolin Chen Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/vfio/pci.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 74a79bdf61..d8a76c1ee0 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3121,10 +3121,15 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bars_register(vdev); - if (!vfio_add_capabilities(vdev, errp)) { + if (!pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { + error_prepend(errp, "Failed to set iommu_device: "); goto out_teardown; } + if (!vfio_add_capabilities(vdev, errp)) { + goto out_unset_idev; + } + if (vdev->vga) { vfio_vga_quirk_setup(vdev); } @@ -3141,7 +3146,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) error_setg(errp, "cannot support IGD OpRegion feature on hotplugged " "device"); - goto out_teardown; + goto out_unset_idev; } ret = vfio_get_dev_region_info(vbasedev, @@ -3150,11 +3155,11 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (ret) { error_setg_errno(errp, -ret, "does not support requested IGD OpRegion feature"); - goto out_teardown; + goto out_unset_idev; } if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) { - goto out_teardown; + goto out_unset_idev; } } @@ -3238,6 +3243,8 @@ out_deregister: if (vdev->intx.mmap_timer) { timer_free(vdev->intx.mmap_timer); } +out_unset_idev: + pci_device_unset_iommu_device(pdev); out_teardown: vfio_teardown_msi(vdev); vfio_bars_exit(vdev); @@ -3266,6 +3273,7 @@ static void vfio_instance_finalize(Object *obj) static void vfio_exitfn(PCIDevice *pdev) { VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); @@ -3280,7 +3288,8 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_teardown_msi(vdev); vfio_pci_disable_rp_atomics(vdev); vfio_bars_exit(vdev); - vfio_migration_exit(&vdev->vbasedev); + vfio_migration_exit(vbasedev); + pci_device_unset_iommu_device(pdev); } static void vfio_pci_reset(DeviceState *dev) From d5fd978d918516b7bc4224de432c7ef93ec089a3 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:41 +0800 Subject: [PATCH 1622/2884] intel_iommu: Extract out vtd_cap_init() to initialize cap/ecap Extract cap/ecap initialization in vtd_cap_init() to make code cleaner. No functional change intended. Reviewed-by: Eric Auger Signed-off-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 93 ++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index c4350e0ff0..c69c0d285b 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3934,30 +3934,10 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) return; } -/* Do the initialization. It will also be called when reset, so pay - * attention when adding new initialization stuff. - */ -static void vtd_init(IntelIOMMUState *s) +static void vtd_cap_init(IntelIOMMUState *s) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); - memset(s->csr, 0, DMAR_REG_SIZE); - memset(s->wmask, 0, DMAR_REG_SIZE); - memset(s->w1cmask, 0, DMAR_REG_SIZE); - memset(s->womask, 0, DMAR_REG_SIZE); - - s->root = 0; - s->root_scalable = false; - s->dmar_enabled = false; - s->intr_enabled = false; - s->iq_head = 0; - s->iq_tail = 0; - s->iq = 0; - s->iq_size = 0; - s->qi_enabled = false; - s->iq_last_desc_type = VTD_INV_DESC_NONE; - s->iq_dw = false; - s->next_frcd_reg = 0; s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS | VTD_CAP_MGAW(s->aw_bits); @@ -3974,27 +3954,6 @@ static void vtd_init(IntelIOMMUState *s) } s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO; - /* - * Rsvd field masks for spte - */ - vtd_spte_rsvd[0] = ~0ULL; - vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits, - x86_iommu->dt_supported); - vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits); - vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits); - vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits); - - vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits, - x86_iommu->dt_supported); - vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits, - x86_iommu->dt_supported); - - if (s->scalable_mode || s->snoop_control) { - vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP; - vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP; - vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP; - } - if (x86_iommu_ir_supported(x86_iommu)) { s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV; if (s->intr_eim == ON_OFF_AUTO_ON) { @@ -4027,6 +3986,56 @@ static void vtd_init(IntelIOMMUState *s) if (s->pasid) { s->ecap |= VTD_ECAP_PASID; } +} + +/* + * Do the initialization. It will also be called when reset, so pay + * attention when adding new initialization stuff. + */ +static void vtd_init(IntelIOMMUState *s) +{ + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); + + memset(s->csr, 0, DMAR_REG_SIZE); + memset(s->wmask, 0, DMAR_REG_SIZE); + memset(s->w1cmask, 0, DMAR_REG_SIZE); + memset(s->womask, 0, DMAR_REG_SIZE); + + s->root = 0; + s->root_scalable = false; + s->dmar_enabled = false; + s->intr_enabled = false; + s->iq_head = 0; + s->iq_tail = 0; + s->iq = 0; + s->iq_size = 0; + s->qi_enabled = false; + s->iq_last_desc_type = VTD_INV_DESC_NONE; + s->iq_dw = false; + s->next_frcd_reg = 0; + + vtd_cap_init(s); + + /* + * Rsvd field masks for spte + */ + vtd_spte_rsvd[0] = ~0ULL; + vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits, + x86_iommu->dt_supported); + vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits); + vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits); + vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits); + + vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits, + x86_iommu->dt_supported); + vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits, + x86_iommu->dt_supported); + + if (s->scalable_mode || s->snoop_control) { + vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP; + vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP; + vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP; + } vtd_reset_caches(s); From a20910ca3e283b10585c71061746ea6b1fa6ca91 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Wed, 5 Jun 2024 16:30:42 +0800 Subject: [PATCH 1623/2884] intel_iommu: Implement [set|unset]_iommu_device() callbacks Implement [set|unset]_iommu_device() callbacks in Intel vIOMMU. In set call, we take a reference of HostIOMMUDevice and store it in hash table indexed by PCI BDF. Note this BDF index is device's real BDF not the aliased one which is different from the index of VTDAddressSpace. There can be multiple assigned devices under same virtual iommu group and share same VTDAddressSpace, but each has its own HostIOMMUDevice. Signed-off-by: Yi Liu Signed-off-by: Yi Sun Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 81 +++++++++++++++++++++++++++++++++++ include/hw/i386/intel_iommu.h | 2 + 2 files changed, 83 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index c69c0d285b..019d1c9c80 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -61,6 +61,12 @@ struct vtd_as_key { uint32_t pasid; }; +/* bus/devfn is PCI device's real BDF not the aliased one */ +struct vtd_hiod_key { + PCIBus *bus; + uint8_t devfn; +}; + struct vtd_iotlb_key { uint64_t gfn; uint32_t pasid; @@ -250,6 +256,25 @@ static guint vtd_as_hash(gconstpointer v) return (guint)(value << 8 | key->devfn); } +/* Same implementation as vtd_as_hash() */ +static guint vtd_hiod_hash(gconstpointer v) +{ + return vtd_as_hash(v); +} + +static gboolean vtd_hiod_equal(gconstpointer v1, gconstpointer v2) +{ + const struct vtd_hiod_key *key1 = v1; + const struct vtd_hiod_key *key2 = v2; + + return (key1->bus == key2->bus) && (key1->devfn == key2->devfn); +} + +static void vtd_hiod_destroy(gpointer v) +{ + object_unref(v); +} + static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, gpointer user_data) { @@ -3812,6 +3837,58 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, return vtd_dev_as; } +static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *hiod, Error **errp) +{ + IntelIOMMUState *s = opaque; + struct vtd_as_key key = { + .bus = bus, + .devfn = devfn, + }; + struct vtd_as_key *new_key; + + assert(hiod); + + vtd_iommu_lock(s); + + if (g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) { + error_setg(errp, "Host IOMMU device already exist"); + vtd_iommu_unlock(s); + return false; + } + + new_key = g_malloc(sizeof(*new_key)); + new_key->bus = bus; + new_key->devfn = devfn; + + object_ref(hiod); + g_hash_table_insert(s->vtd_host_iommu_dev, new_key, hiod); + + vtd_iommu_unlock(s); + + return true; +} + +static void vtd_dev_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) +{ + IntelIOMMUState *s = opaque; + struct vtd_as_key key = { + .bus = bus, + .devfn = devfn, + }; + + vtd_iommu_lock(s); + + if (!g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) { + vtd_iommu_unlock(s); + return; + } + + g_hash_table_remove(s->vtd_host_iommu_dev, &key); + + vtd_iommu_unlock(s); +} + /* Unmap the whole range in the notifier's scope. */ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) { @@ -4116,6 +4193,8 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) static PCIIOMMUOps vtd_iommu_ops = { .get_address_space = vtd_host_dma_iommu, + .set_iommu_device = vtd_dev_set_iommu_device, + .unset_iommu_device = vtd_dev_unset_iommu_device, }; static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) @@ -4235,6 +4314,8 @@ static void vtd_realize(DeviceState *dev, Error **errp) g_free, g_free); s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal, g_free, g_free); + s->vtd_host_iommu_dev = g_hash_table_new_full(vtd_hiod_hash, vtd_hiod_equal, + g_free, vtd_hiod_destroy); vtd_init(s); pci_setup_iommu(bus, &vtd_iommu_ops, dev); /* Pseudo address space under root PCI bus. */ diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 7fa0a695c8..1eb05c29fc 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -292,6 +292,8 @@ struct IntelIOMMUState { /* list of registered notifiers */ QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; + GHashTable *vtd_host_iommu_dev; /* HostIOMMUDevice */ + /* interrupt remapping */ bool intr_enabled; /* Whether guest enabled IR */ dma_addr_t intr_root; /* Interrupt remapping table pointer */ From 77f6efc0ab93718da2bc3f908b7ff7fffa489ea1 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 5 Jun 2024 16:30:43 +0800 Subject: [PATCH 1624/2884] intel_iommu: Check compatibility with host IOMMU capabilities If check fails, host device (either VFIO or VDPA device) is not compatible with current vIOMMU config and should not be passed to guest. Only aw_bits is checked for now, we don't care about other caps before scalable modern mode is introduced. Signed-off-by: Yi Liu Signed-off-by: Zhenzhong Duan Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 019d1c9c80..37c21a0aec 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3837,6 +3837,30 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, return vtd_dev_as; } +static bool vtd_check_hiod(IntelIOMMUState *s, HostIOMMUDevice *hiod, + Error **errp) +{ + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod); + int ret; + + if (!hiodc->get_cap) { + error_setg(errp, ".get_cap() not implemented"); + return false; + } + + /* Common checks */ + ret = hiodc->get_cap(hiod, HOST_IOMMU_DEVICE_CAP_AW_BITS, errp); + if (ret < 0) { + return false; + } + if (s->aw_bits > ret) { + error_setg(errp, "aw-bits %d > host aw-bits %d", s->aw_bits, ret); + return false; + } + + return true; +} + static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, HostIOMMUDevice *hiod, Error **errp) { @@ -3857,6 +3881,11 @@ static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, return false; } + if (!vtd_check_hiod(s, hiod, errp)) { + vtd_iommu_unlock(s); + return false; + } + new_key = g_malloc(sizeof(*new_key)); new_key->bus = bus; new_key->devfn = devfn; From dc169694cad4b4c4028c755ec44ebb7565bd7461 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:51 +0200 Subject: [PATCH 1625/2884] HostIOMMUDevice: Store the VFIO/VDPA agent Store the agent device (VFIO or VDPA) in the host IOMMU device. This will allow easy access to some of its resources. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/vfio/container.c | 1 + hw/vfio/iommufd.c | 2 ++ include/sysemu/host_iommu_device.h | 1 + 3 files changed, 4 insertions(+) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 26e6f7fb4f..b728b978a2 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1145,6 +1145,7 @@ static bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque, hiod->name = g_strdup(vdev->name); hiod->caps.aw_bits = vfio_device_get_aw_bits(vdev); + hiod->agent = opaque; return true; } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 409ed3dcc9..dbdae1adbb 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -631,6 +631,8 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, struct iommu_hw_info_vtd vtd; } data; + hiod->agent = opaque; + if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, &type, &data, sizeof(data), errp)) { return false; diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index a57873958b..3e5f058e7b 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -34,6 +34,7 @@ struct HostIOMMUDevice { Object parent_obj; char *name; + void *agent; /* pointer to agent device, ie. VFIO or VDPA device */ HostIOMMUDeviceCaps caps; }; From 817ef10da23cd9a6ee61c8f31f55b845c7a74760 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:52 +0200 Subject: [PATCH 1626/2884] virtio-iommu: Implement set|unset]_iommu_device() callbacks Implement PCIIOMMUOPs [set|unset]_iommu_device() callbacks. In set(), the HostIOMMUDevice handle is stored in a hash table indexed by PCI BDF. The object will allow to retrieve information related to the physical IOMMU. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu.c | 82 ++++++++++++++++++++++++++++++++ include/hw/virtio/virtio-iommu.h | 2 + 2 files changed, 84 insertions(+) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 1326c6ec41..16c8ec3ca4 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -69,6 +69,11 @@ typedef struct VirtIOIOMMUMapping { uint32_t flags; } VirtIOIOMMUMapping; +struct hiod_key { + PCIBus *bus; + uint8_t devfn; +}; + static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev) { return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); @@ -462,8 +467,82 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, return &sdev->as; } +static gboolean hiod_equal(gconstpointer v1, gconstpointer v2) +{ + const struct hiod_key *key1 = v1; + const struct hiod_key *key2 = v2; + + return (key1->bus == key2->bus) && (key1->devfn == key2->devfn); +} + +static guint hiod_hash(gconstpointer v) +{ + const struct hiod_key *key = v; + guint value = (guint)(uintptr_t)key->bus; + + return (guint)(value << 8 | key->devfn); +} + +static void hiod_destroy(gpointer v) +{ + object_unref(v); +} + +static HostIOMMUDevice * +get_host_iommu_device(VirtIOIOMMU *viommu, PCIBus *bus, int devfn) { + struct hiod_key key = { + .bus = bus, + .devfn = devfn, + }; + + return g_hash_table_lookup(viommu->host_iommu_devices, &key); +} + +static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *hiod, Error **errp) +{ + VirtIOIOMMU *viommu = opaque; + struct hiod_key *new_key; + + assert(hiod); + + if (get_host_iommu_device(viommu, bus, devfn)) { + error_setg(errp, "Host IOMMU device already exists"); + return false; + } + + new_key = g_malloc(sizeof(*new_key)); + new_key->bus = bus; + new_key->devfn = devfn; + + object_ref(hiod); + g_hash_table_insert(viommu->host_iommu_devices, new_key, hiod); + + return true; +} + +static void +virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) +{ + VirtIOIOMMU *viommu = opaque; + HostIOMMUDevice *hiod; + struct hiod_key key = { + .bus = bus, + .devfn = devfn, + }; + + hiod = g_hash_table_lookup(viommu->host_iommu_devices, &key); + if (!hiod) { + return; + } + + g_hash_table_remove(viommu->host_iommu_devices, &key); +} + static const PCIIOMMUOps virtio_iommu_ops = { .get_address_space = virtio_iommu_find_add_as, + .set_iommu_device = virtio_iommu_set_iommu_device, + .unset_iommu_device = virtio_iommu_unset_iommu_device, }; static int virtio_iommu_attach(VirtIOIOMMU *s, @@ -1357,6 +1436,9 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free); + s->host_iommu_devices = g_hash_table_new_full(hiod_hash, hiod_equal, + g_free, hiod_destroy); + if (s->primary_bus) { pci_setup_iommu(s->primary_bus, &virtio_iommu_ops, s); } else { diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 83a52cc446..bdb3da72d0 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -25,6 +25,7 @@ #include "hw/pci/pci.h" #include "qom/object.h" #include "qapi/qapi-types-virtio.h" +#include "sysemu/host_iommu_device.h" #define TYPE_VIRTIO_IOMMU "virtio-iommu-device" #define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci" @@ -57,6 +58,7 @@ struct VirtIOIOMMU { struct virtio_iommu_config config; uint64_t features; GHashTable *as_by_busptr; + GHashTable *host_iommu_devices; IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX]; PCIBus *primary_bus; ReservedRegion *prop_resv_regions; From 3ad35d9158bde0500aeaea4147345d39ad0ae1c0 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:53 +0200 Subject: [PATCH 1627/2884] HostIOMMUDevice: Introduce get_iova_ranges callback Introduce a new HostIOMMUDevice callback that allows to retrieve the usable IOVA ranges. Implement this callback in the legacy VFIO and IOMMUFD VFIO host iommu devices. This relies on the VFIODevice agent's base container iova_ranges resource. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/vfio/container.c | 16 ++++++++++++++++ hw/vfio/iommufd.c | 16 ++++++++++++++++ include/sysemu/host_iommu_device.h | 8 ++++++++ 3 files changed, 40 insertions(+) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index b728b978a2..c48749c089 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1164,12 +1164,28 @@ static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap, } } +static GList * +hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) +{ + VFIODevice *vdev = hiod->agent; + GList *l = NULL; + + g_assert(vdev); + + if (vdev->bcontainer) { + l = g_list_copy(vdev->bcontainer->iova_ranges); + } + + return l; +} + static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) { HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); hioc->realize = hiod_legacy_vfio_realize; hioc->get_cap = hiod_legacy_vfio_get_cap; + hioc->get_iova_ranges = hiod_legacy_vfio_get_iova_ranges; }; static const TypeInfo types[] = { diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index dbdae1adbb..e502081c2a 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -645,11 +645,27 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, return true; } +static GList * +hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) +{ + VFIODevice *vdev = hiod->agent; + GList *l = NULL; + + g_assert(vdev); + + if (vdev->bcontainer) { + l = g_list_copy(vdev->bcontainer->iova_ranges); + } + + return l; +} + static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) { HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); hiodc->realize = hiod_iommufd_vfio_realize; + hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges; }; static const TypeInfo types[] = { diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index 3e5f058e7b..40e0fa13ef 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -80,6 +80,14 @@ struct HostIOMMUDeviceClass { * i.e., HOST_IOMMU_DEVICE_CAP_AW_BITS. */ int (*get_cap)(HostIOMMUDevice *hiod, int cap, Error **errp); + /** + * @get_iova_ranges: Return the list of usable iova_ranges along with + * @hiod Host IOMMU device + * + * @hiod: handle to the host IOMMU device + * @errp: error handle + */ + GList* (*get_iova_ranges)(HostIOMMUDevice *hiod, Error **errp); }; /* From a95264191f10173e27b175ebc4a487520a523d62 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:54 +0200 Subject: [PATCH 1628/2884] HostIOMMUDevice: Store the aliased bus and devfn Store the aliased bus and devfn in the HostIOMMUDevice. This will be useful to handle info that are iommu group specific and not device specific (such as reserved iova ranges). Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/pci/pci.c | 8 ++++++-- include/sysemu/host_iommu_device.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c8a8aab306..50b86d5790 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2745,11 +2745,15 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, Error **errp) { - PCIBus *iommu_bus; + PCIBus *iommu_bus, *aliased_bus; + int aliased_devfn; /* set_iommu_device requires device's direct BDF instead of aliased BDF */ - pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL); + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, + &aliased_bus, &aliased_devfn); if (iommu_bus && iommu_bus->iommu_ops->set_iommu_device) { + hiod->aliased_bus = aliased_bus; + hiod->aliased_devfn = aliased_devfn; return iommu_bus->iommu_ops->set_iommu_device(pci_get_bus(dev), iommu_bus->iommu_opaque, dev->devfn, hiod, errp); diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index 40e0fa13ef..ee6c813c8b 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -35,6 +35,8 @@ struct HostIOMMUDevice { char *name; void *agent; /* pointer to agent device, ie. VFIO or VDPA device */ + PCIBus *aliased_bus; + int aliased_devfn; HostIOMMUDeviceCaps caps; }; From cf2647a76e7854ff28c21c8fb4bfca1da603e007 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:55 +0200 Subject: [PATCH 1629/2884] virtio-iommu: Compute host reserved regions Compute the host reserved regions in virtio_iommu_set_iommu_device(). The usable IOVA regions are retrieved from the HostIOMMUDevice. The virtio_iommu_set_host_iova_ranges() helper turns usable regions into complementary reserved regions while testing the inclusion into existing ones. virtio_iommu_set_host_iova_ranges() reuse the implementation of virtio_iommu_set_iova_ranges() which will be removed in subsequent patches. rebuild_resv_regions() is just moved. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu.c | 147 ++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 34 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 16c8ec3ca4..a4c0cceb65 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -498,11 +498,108 @@ get_host_iommu_device(VirtIOIOMMU *viommu, PCIBus *bus, int devfn) { return g_hash_table_lookup(viommu->host_iommu_devices, &key); } +/** + * rebuild_resv_regions: rebuild resv regions with both the + * info of host resv ranges and property set resv ranges + */ +static int rebuild_resv_regions(IOMMUDevice *sdev) +{ + GList *l; + int i = 0; + + /* free the existing list and rebuild it from scratch */ + g_list_free_full(sdev->resv_regions, g_free); + sdev->resv_regions = NULL; + + /* First add host reserved regions if any, all tagged as RESERVED */ + for (l = sdev->host_resv_ranges; l; l = l->next) { + ReservedRegion *reg = g_new0(ReservedRegion, 1); + Range *r = (Range *)l->data; + + reg->type = VIRTIO_IOMMU_RESV_MEM_T_RESERVED; + range_set_bounds(®->range, range_lob(r), range_upb(r)); + sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg); + trace_virtio_iommu_host_resv_regions(sdev->iommu_mr.parent_obj.name, i, + range_lob(®->range), + range_upb(®->range)); + i++; + } + /* + * then add higher priority reserved regions set by the machine + * through properties + */ + add_prop_resv_regions(sdev); + return 0; +} + +static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, + int devfn, GList *iova_ranges, + Error **errp) +{ + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + IOMMUDevice *sdev; + GList *current_ranges; + GList *l, *tmp, *new_ranges = NULL; + int ret = -EINVAL; + + if (!sbus) { + error_report("%s no sbus", __func__); + } + + sdev = sbus->pbdev[devfn]; + + current_ranges = sdev->host_resv_ranges; + + g_assert(!sdev->probe_done); + + /* check that each new resv region is included in an existing one */ + if (sdev->host_resv_ranges) { + range_inverse_array(iova_ranges, + &new_ranges, + 0, UINT64_MAX); + + for (tmp = new_ranges; tmp; tmp = tmp->next) { + Range *newr = (Range *)tmp->data; + bool included = false; + + for (l = current_ranges; l; l = l->next) { + Range * r = (Range *)l->data; + + if (range_contains_range(r, newr)) { + included = true; + break; + } + } + if (!included) { + goto error; + } + } + /* all new reserved ranges are included in existing ones */ + ret = 0; + goto out; + } + + range_inverse_array(iova_ranges, + &sdev->host_resv_ranges, + 0, UINT64_MAX); + rebuild_resv_regions(sdev); + + return 0; +error: + error_setg(errp, "%s Conflicting host reserved ranges set!", + __func__); +out: + g_list_free_full(new_ranges, g_free); + return ret; +} + static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, HostIOMMUDevice *hiod, Error **errp) { VirtIOIOMMU *viommu = opaque; + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod); struct hiod_key *new_key; + GList *host_iova_ranges = NULL; assert(hiod); @@ -511,12 +608,28 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, return false; } + if (hiodc->get_iova_ranges) { + int ret; + host_iova_ranges = hiodc->get_iova_ranges(hiod, errp); + if (!host_iova_ranges) { + return true; /* some old kernels may not support that capability */ + } + ret = virtio_iommu_set_host_iova_ranges(viommu, hiod->aliased_bus, + hiod->aliased_devfn, + host_iova_ranges, errp); + if (ret) { + g_list_free_full(host_iova_ranges, g_free); + return false; + } + } + new_key = g_malloc(sizeof(*new_key)); new_key->bus = bus; new_key->devfn = devfn; object_ref(hiod); g_hash_table_insert(viommu->host_iommu_devices, new_key, hiod); + g_list_free_full(host_iova_ranges, g_free); return true; } @@ -1238,40 +1351,6 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, return 0; } -/** - * rebuild_resv_regions: rebuild resv regions with both the - * info of host resv ranges and property set resv ranges - */ -static int rebuild_resv_regions(IOMMUDevice *sdev) -{ - GList *l; - int i = 0; - - /* free the existing list and rebuild it from scratch */ - g_list_free_full(sdev->resv_regions, g_free); - sdev->resv_regions = NULL; - - /* First add host reserved regions if any, all tagged as RESERVED */ - for (l = sdev->host_resv_ranges; l; l = l->next) { - ReservedRegion *reg = g_new0(ReservedRegion, 1); - Range *r = (Range *)l->data; - - reg->type = VIRTIO_IOMMU_RESV_MEM_T_RESERVED; - range_set_bounds(®->range, range_lob(r), range_upb(r)); - sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg); - trace_virtio_iommu_host_resv_regions(sdev->iommu_mr.parent_obj.name, i, - range_lob(®->range), - range_upb(®->range)); - i++; - } - /* - * then add higher priority reserved regions set by the machine - * through properties - */ - add_prop_resv_regions(sdev); - return 0; -} - /** * virtio_iommu_set_iova_ranges: Conveys the usable IOVA ranges * From 3ba100b41946b10efc12c2493997f7074081707f Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:56 +0200 Subject: [PATCH 1630/2884] virtio-iommu: Remove the implementation of iommu_set_iova_range Now that we use PCIIOMMUOps to convey information about usable IOVA ranges we do not to implement the iommu_set_iova_ranges IOMMU MR callback. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu.c | 67 ---------------------------------------- 1 file changed, 67 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index a4c0cceb65..b9a7ddcd14 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1351,72 +1351,6 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, return 0; } -/** - * virtio_iommu_set_iova_ranges: Conveys the usable IOVA ranges - * - * The function turns those into reserved ranges. Once some - * reserved ranges have been set, new reserved regions cannot be - * added outside of the original ones. - * - * @mr: IOMMU MR - * @iova_ranges: list of usable IOVA ranges - * @errp: error handle - */ -static int virtio_iommu_set_iova_ranges(IOMMUMemoryRegion *mr, - GList *iova_ranges, - Error **errp) -{ - IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); - GList *current_ranges = sdev->host_resv_ranges; - GList *l, *tmp, *new_ranges = NULL; - int ret = -EINVAL; - - /* check that each new resv region is included in an existing one */ - if (sdev->host_resv_ranges) { - range_inverse_array(iova_ranges, - &new_ranges, - 0, UINT64_MAX); - - for (tmp = new_ranges; tmp; tmp = tmp->next) { - Range *newr = (Range *)tmp->data; - bool included = false; - - for (l = current_ranges; l; l = l->next) { - Range * r = (Range *)l->data; - - if (range_contains_range(r, newr)) { - included = true; - break; - } - } - if (!included) { - goto error; - } - } - /* all new reserved ranges are included in existing ones */ - ret = 0; - goto out; - } - - if (sdev->probe_done) { - warn_report("%s: Notified about new host reserved regions after probe", - mr->parent_obj.name); - } - - range_inverse_array(iova_ranges, - &sdev->host_resv_ranges, - 0, UINT64_MAX); - rebuild_resv_regions(sdev); - - return 0; -error: - error_setg(errp, "IOMMU mr=%s Conflicting host reserved ranges set!", - mr->parent_obj.name); -out: - g_list_free_full(new_ranges, g_free); - return ret; -} - static void virtio_iommu_system_reset(void *opaque) { VirtIOIOMMU *s = opaque; @@ -1742,7 +1676,6 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, imrc->replay = virtio_iommu_replay; imrc->notify_flag_changed = virtio_iommu_notify_flag_changed; imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask; - imrc->iommu_set_iova_ranges = virtio_iommu_set_iova_ranges; } static const TypeInfo virtio_iommu_info = { From 44079a9839bc0a682db7c0ab6093fce79c73d261 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:57 +0200 Subject: [PATCH 1631/2884] hw/vfio: Remove memory_region_iommu_set_iova_ranges() call As we have just removed the only implementation of iommu_set_iova_ranges IOMMU MR callback in the virtio-iommu, let's remove the call to the memory wrapper. Usable IOVA ranges are now conveyed through the PCIIOMMUOps in VFIO-PCI. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- hw/vfio/common.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f20a7b5bba..9e4c0cc95f 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -630,16 +630,6 @@ static void vfio_listener_region_add(MemoryListener *listener, goto fail; } - if (bcontainer->iova_ranges) { - ret = memory_region_iommu_set_iova_ranges(giommu->iommu_mr, - bcontainer->iova_ranges, - &err); - if (ret) { - g_free(giommu); - goto fail; - } - } - ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, &err); if (ret) { From 71386c6efd7d57cc549b1c3caff889e7506c54a9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 14 Jun 2024 11:52:58 +0200 Subject: [PATCH 1632/2884] memory: Remove IOMMU MR iommu_set_iova_range API Since the host IOVA ranges are now passed through the PCIIOMMUOps set_host_resv_regions and we have removed the only implementation of iommu_set_iova_range() in the virtio-iommu and the only call site in vfio/common, let's retire the IOMMU MR API and its memory wrapper. Signed-off-by: Eric Auger Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin --- include/exec/memory.h | 32 -------------------------------- system/memory.c | 13 ------------- 2 files changed, 45 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 2d7c278b9f..0903513d13 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -530,26 +530,6 @@ struct IOMMUMemoryRegionClass { int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, uint64_t page_size_mask, Error **errp); - /** - * @iommu_set_iova_ranges: - * - * Propagate information about the usable IOVA ranges for a given IOMMU - * memory region. Used for example to propagate host physical device - * reserved memory region constraints to the virtual IOMMU. - * - * Optional method: if this method is not provided, then the default IOVA - * aperture is used. - * - * @iommu: the IOMMUMemoryRegion - * - * @iova_ranges: list of ordered IOVA ranges (at least one range) - * - * Returns 0 on success, or a negative error. In case of failure, the error - * object must be created. - */ - int (*iommu_set_iova_ranges)(IOMMUMemoryRegion *iommu, - GList *iova_ranges, - Error **errp); }; typedef struct RamDiscardListener RamDiscardListener; @@ -1951,18 +1931,6 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, uint64_t page_size_mask, Error **errp); -/** - * memory_region_iommu_set_iova_ranges - Set the usable IOVA ranges - * for a given IOMMU MR region - * - * @iommu: IOMMU memory region - * @iova_ranges: list of ordered IOVA ranges (at least one range) - * @errp: pointer to Error*, to store an error if it happens. - */ -int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu, - GList *iova_ranges, - Error **errp); - /** * memory_region_name: get a memory region's name * diff --git a/system/memory.c b/system/memory.c index 47c600df63..2d69521360 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1914,19 +1914,6 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, return ret; } -int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu_mr, - GList *iova_ranges, - Error **errp) -{ - IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); - int ret = 0; - - if (imrc->iommu_set_iova_ranges) { - ret = imrc->iommu_set_iova_ranges(iommu_mr, iova_ranges, errp); - } - return ret; -} - int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) { From 332b9b0da409d727ac4765fa613158189562dec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:33:53 +0200 Subject: [PATCH 1633/2884] vfio: Make vfio_devices_dma_logging_start() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vfio_devices_dma_logging_start() takes an 'Error **' argument, best practices suggest to return a bool. See the api/error.h Rules section. It will simplify potential changes coming after. vfio_container_set_dirty_page_tracking() could be modified in the same way but the errno value can be saved in the migration stream when called from vfio_listener_log_global_stop(). Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 9e4c0cc95f..d48cd9b936 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1020,7 +1020,7 @@ static void vfio_device_feature_dma_logging_start_destroy( g_free(feature); } -static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer, +static bool vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer, Error **errp) { struct vfio_device_feature *feature; @@ -1033,7 +1033,7 @@ static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer, &ranges); if (!feature) { error_setg_errno(errp, errno, "Failed to prepare DMA logging"); - return -errno; + return false; } QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { @@ -1058,7 +1058,7 @@ out: vfio_device_feature_dma_logging_start_destroy(feature); - return ret; + return ret == 0; } static bool vfio_listener_log_global_start(MemoryListener *listener, @@ -1067,18 +1067,18 @@ static bool vfio_listener_log_global_start(MemoryListener *listener, ERRP_GUARD(); VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener); - int ret; + bool ret; if (vfio_devices_all_device_dirty_tracking(bcontainer)) { ret = vfio_devices_dma_logging_start(bcontainer, errp); } else { - ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp); + ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp) == 0; } - if (ret) { + if (!ret) { error_prepend(errp, "vfio: Could not start dirty page tracking - "); } - return !ret; + return ret; } static void vfio_listener_log_global_stop(MemoryListener *listener) From 889833e5ae1fe83b39e065b7386eb020af7cf304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:33:54 +0200 Subject: [PATCH 1634/2884] vfio: Remove unused declarations from vfio-common.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were forgotten in the recent cleanups. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-common.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 776de8064f..c19572f90b 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -207,10 +207,6 @@ typedef struct VFIODisplay { VFIOAddressSpace *vfio_get_address_space(AddressSpace *as); void vfio_put_address_space(VFIOAddressSpace *space); -/* SPAPR specific */ -int vfio_spapr_container_init(VFIOContainer *container, Error **errp); -void vfio_spapr_container_deinit(VFIOContainer *container); - void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); From 344e70945db3af08862e37a8bb10afaf4c59f88b Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Mon, 17 Jun 2024 08:33:55 +0200 Subject: [PATCH 1635/2884] vfio/common: Move dirty tracking ranges update to helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate the changes that update the ranges from the listener, to make it reusable in preparation to expand its use to vIOMMU support. Signed-off-by: Joao Martins Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger [ clg: - Rebased on upstream - Introduced vfio_dirty_tracking_update_range() - Fixed typ in commit log ] Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d48cd9b936..fe215918bd 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -839,20 +839,11 @@ static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, return false; } -static void vfio_dirty_tracking_update(MemoryListener *listener, - MemoryRegionSection *section) +static void vfio_dirty_tracking_update_range(VFIODirtyRanges *range, + hwaddr iova, hwaddr end, + bool update_pci) { - VFIODirtyRangesListener *dirty = container_of(listener, - VFIODirtyRangesListener, - listener); - VFIODirtyRanges *range = &dirty->ranges; - hwaddr iova, end, *min, *max; - - if (!vfio_listener_valid_section(section, "tracking_update") || - !vfio_get_section_iova_range(dirty->bcontainer, section, - &iova, &end, NULL)) { - return; - } + hwaddr *min, *max; /* * The address space passed to the dirty tracker is reduced to three ranges: @@ -873,8 +864,7 @@ static void vfio_dirty_tracking_update(MemoryListener *listener, * The alternative would be an IOVATree but that has a much bigger runtime * overhead and unnecessary complexity. */ - if (vfio_section_is_vfio_pci(section, dirty->bcontainer) && - iova >= UINT32_MAX) { + if (update_pci && iova >= UINT32_MAX) { min = &range->minpci64; max = &range->maxpci64; } else { @@ -889,7 +879,23 @@ static void vfio_dirty_tracking_update(MemoryListener *listener, } trace_vfio_device_dirty_tracking_update(iova, end, *min, *max); - return; +} + +static void vfio_dirty_tracking_update(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIODirtyRangesListener *dirty = + container_of(listener, VFIODirtyRangesListener, listener); + hwaddr iova, end; + + if (!vfio_listener_valid_section(section, "tracking_update") || + !vfio_get_section_iova_range(dirty->bcontainer, section, + &iova, &end, NULL)) { + return; + } + + vfio_dirty_tracking_update_range(&dirty->ranges, iova, end, + vfio_section_is_vfio_pci(section, dirty->bcontainer)); } static const MemoryListener vfio_dirty_tracking_listener = { From 723f702b89b9c86058a608db9dea3b5618ff284a Mon Sep 17 00:00:00 2001 From: Avihai Horon Date: Mon, 17 Jun 2024 08:33:56 +0200 Subject: [PATCH 1636/2884] vfio/common: Extract vIOMMU code from vfio_sync_dirty_bitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract vIOMMU code from vfio_sync_dirty_bitmap() to a new function and restructure the code. This is done in preparation for optimizing vIOMMU device dirty page tracking. No functional changes intended. Signed-off-by: Avihai Horon Signed-off-by: Joao Martins [ clg: - Rebased on upstream - Fixed typo in commit log ] Reviewed-by: Zhenzhong Duan Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 63 +++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index fe215918bd..f28641bad5 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1302,37 +1302,50 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, &vrdl); } +static int vfio_sync_iommu_dirty_bitmap(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIOGuestIOMMU *giommu; + bool found = false; + Int128 llend; + vfio_giommu_dirty_notifier gdn; + int idx; + + QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { + if (MEMORY_REGION(giommu->iommu_mr) == section->mr && + giommu->n.start == section->offset_within_region) { + found = true; + break; + } + } + + if (!found) { + return 0; + } + + gdn.giommu = giommu; + idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr, + MEMTXATTRS_UNSPECIFIED); + + llend = int128_add(int128_make64(section->offset_within_region), + section->size); + llend = int128_sub(llend, int128_one()); + + iommu_notifier_init(&gdn.n, vfio_iommu_map_dirty_notify, IOMMU_NOTIFIER_MAP, + section->offset_within_region, int128_get64(llend), + idx); + memory_region_iommu_replay(giommu->iommu_mr, &gdn.n); + + return 0; +} + static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, MemoryRegionSection *section, Error **errp) { ram_addr_t ram_addr; if (memory_region_is_iommu(section->mr)) { - VFIOGuestIOMMU *giommu; - - QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { - if (MEMORY_REGION(giommu->iommu_mr) == section->mr && - giommu->n.start == section->offset_within_region) { - Int128 llend; - vfio_giommu_dirty_notifier gdn = { .giommu = giommu }; - int idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr, - MEMTXATTRS_UNSPECIFIED); - - llend = int128_add(int128_make64(section->offset_within_region), - section->size); - llend = int128_sub(llend, int128_one()); - - iommu_notifier_init(&gdn.n, - vfio_iommu_map_dirty_notify, - IOMMU_NOTIFIER_MAP, - section->offset_within_region, - int128_get64(llend), - idx); - memory_region_iommu_replay(giommu->iommu_mr, &gdn.n); - break; - } - } - return 0; + return vfio_sync_iommu_dirty_bitmap(bcontainer, section); } else if (memory_region_has_ram_discard_manager(section->mr)) { int ret; From b7b79588ebb365e157ec2425a4fa472a314c3ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:33:57 +0200 Subject: [PATCH 1637/2884] vfio/container: Introduce vfio_address_space_insert() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It prepares ground for a future change initializing the 'space' pointer of VFIOContainerBase. The goal is to replace vfio_container_init() by an .instance_init() handler when VFIOContainerBase is QOMified. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 6 ++++++ hw/vfio/container.c | 2 +- hw/vfio/iommufd.c | 2 +- include/hw/vfio/vfio-common.h | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f28641bad5..8cdf26c6f5 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1508,6 +1508,12 @@ void vfio_put_address_space(VFIOAddressSpace *space) } } +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer) +{ + QLIST_INSERT_HEAD(&space->containers, bcontainer, next); +} + struct vfio_device_info *vfio_get_device_info(int fd) { struct vfio_device_info *info; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index c48749c089..0237c21698 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -637,7 +637,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, vfio_kvm_device_add_group(group); QLIST_INIT(&container->group_list); - QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + vfio_address_space_insert(space, bcontainer); group->container = container; QLIST_INSERT_HEAD(&container->group_list, group, container_next); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index e502081c2a..9f8f33e383 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -358,7 +358,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, bcontainer = &container->bcontainer; vfio_container_init(bcontainer, space, iommufd_vioc); - QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + vfio_address_space_insert(space, bcontainer); if (!iommufd_cdev_attach_container(vbasedev, container, errp)) { goto err_attach_container; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index c19572f90b..825d80130b 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -206,6 +206,8 @@ typedef struct VFIODisplay { VFIOAddressSpace *vfio_get_address_space(AddressSpace *as); void vfio_put_address_space(VFIOAddressSpace *space); +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer); void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); From 09181a8e9729c77c69f8d3988a1dbed0b91402d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:33:58 +0200 Subject: [PATCH 1638/2884] vfio/container: Simplify vfio_container_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assign the base container VFIOAddressSpace 'space' pointer in vfio_address_space_insert(). The ultimate goal is to remove vfio_container_init() and instead rely on an .instance_init() handler to perfom the initialization of VFIOContainerBase. To be noted that vfio_connect_container() will assign the 'space' pointer later in the execution flow. This should not have any consequence. Reviewed-by: Zhenzhong Duan Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 1 + hw/vfio/container-base.c | 3 +-- hw/vfio/container.c | 6 +++--- hw/vfio/iommufd.c | 2 +- include/hw/vfio/vfio-container-base.h | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 8cdf26c6f5..1686a0bed2 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1512,6 +1512,7 @@ void vfio_address_space_insert(VFIOAddressSpace *space, VFIOContainerBase *bcontainer) { QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + bcontainer->space = space; } struct vfio_device_info *vfio_get_device_info(int fd) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 760d9d0622..280f0dd2db 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -71,11 +71,10 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, errp); } -void vfio_container_init(VFIOContainerBase *bcontainer, VFIOAddressSpace *space, +void vfio_container_init(VFIOContainerBase *bcontainer, const VFIOIOMMUClass *ops) { bcontainer->ops = ops; - bcontainer->space = space; bcontainer->error = NULL; bcontainer->dirty_pages_supported = false; bcontainer->dma_max_mappings = 0; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 0237c21698..dc85a79cb9 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -394,7 +394,7 @@ static const VFIOIOMMUClass *vfio_get_iommu_class(int iommu_type, Error **errp) } static bool vfio_set_iommu(VFIOContainer *container, int group_fd, - VFIOAddressSpace *space, Error **errp) + Error **errp) { int iommu_type; const VFIOIOMMUClass *vioc; @@ -432,7 +432,7 @@ static bool vfio_set_iommu(VFIOContainer *container, int group_fd, return false; } - vfio_container_init(&container->bcontainer, space, vioc); + vfio_container_init(&container->bcontainer, vioc); return true; } @@ -614,7 +614,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->fd = fd; bcontainer = &container->bcontainer; - if (!vfio_set_iommu(container, group->fd, space, errp)) { + if (!vfio_set_iommu(container, group->fd, errp)) { goto free_container_exit; } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 9f8f33e383..e5d9334142 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -357,7 +357,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, container->ioas_id = ioas_id; bcontainer = &container->bcontainer; - vfio_container_init(bcontainer, space, iommufd_vioc); + vfio_container_init(bcontainer, iommufd_vioc); vfio_address_space_insert(space, bcontainer); if (!iommufd_cdev_attach_container(vbasedev, container, errp)) { diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 442c0dfc4c..d505f63607 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -87,7 +87,6 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); void vfio_container_init(VFIOContainerBase *bcontainer, - VFIOAddressSpace *space, const VFIOIOMMUClass *ops); void vfio_container_destroy(VFIOContainerBase *bcontainer); From 55974f35eac3d8166a8dee498b1efee2ef9663c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:33:59 +0200 Subject: [PATCH 1639/2884] vfio/container: Modify vfio_get_iommu_type() to use a container fd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'container' pointer has no other use than its 'fd' attribute. Simplify the prototype to ease future changes. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index dc85a79cb9..589f37bc6d 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -354,7 +354,7 @@ static void vfio_kvm_device_del_group(VFIOGroup *group) /* * vfio_get_iommu_type - selects the richest iommu_type (v2 first) */ -static int vfio_get_iommu_type(VFIOContainer *container, +static int vfio_get_iommu_type(int container_fd, Error **errp) { int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, @@ -362,7 +362,7 @@ static int vfio_get_iommu_type(VFIOContainer *container, int i; for (i = 0; i < ARRAY_SIZE(iommu_types); i++) { - if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { + if (ioctl(container_fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { return iommu_types[i]; } } @@ -399,7 +399,7 @@ static bool vfio_set_iommu(VFIOContainer *container, int group_fd, int iommu_type; const VFIOIOMMUClass *vioc; - iommu_type = vfio_get_iommu_type(container, errp); + iommu_type = vfio_get_iommu_type(container->fd, errp); if (iommu_type < 0) { return false; } From 17401879c416c48c72e5e4d805244b893d44637c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:00 +0200 Subject: [PATCH 1640/2884] vfio/container: Introduce vfio_get_iommu_class_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework vfio_get_iommu_class() to return a literal class name instead of a class object. We will need this name to instantiate the object later on. Since the default case asserts, remove the error report as QEMU will simply abort before. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 589f37bc6d..bb6abe60ee 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -373,24 +373,20 @@ static int vfio_get_iommu_type(int container_fd, /* * vfio_get_iommu_ops - get a VFIOIOMMUClass associated with a type */ -static const VFIOIOMMUClass *vfio_get_iommu_class(int iommu_type, Error **errp) +static const char *vfio_get_iommu_class_name(int iommu_type) { - ObjectClass *klass = NULL; - switch (iommu_type) { case VFIO_TYPE1v2_IOMMU: case VFIO_TYPE1_IOMMU: - klass = object_class_by_name(TYPE_VFIO_IOMMU_LEGACY); + return TYPE_VFIO_IOMMU_LEGACY; break; case VFIO_SPAPR_TCE_v2_IOMMU: case VFIO_SPAPR_TCE_IOMMU: - klass = object_class_by_name(TYPE_VFIO_IOMMU_SPAPR); + return TYPE_VFIO_IOMMU_SPAPR; break; default: g_assert_not_reached(); }; - - return VFIO_IOMMU_CLASS(klass); } static bool vfio_set_iommu(VFIOContainer *container, int group_fd, @@ -398,6 +394,7 @@ static bool vfio_set_iommu(VFIOContainer *container, int group_fd, { int iommu_type; const VFIOIOMMUClass *vioc; + const char *vioc_name; iommu_type = vfio_get_iommu_type(container->fd, errp); if (iommu_type < 0) { @@ -426,11 +423,8 @@ static bool vfio_set_iommu(VFIOContainer *container, int group_fd, container->iommu_type = iommu_type; - vioc = vfio_get_iommu_class(iommu_type, errp); - if (!vioc) { - error_setg(errp, "No available IOMMU models"); - return false; - } + vioc_name = vfio_get_iommu_class_name(iommu_type); + vioc = VFIO_IOMMU_CLASS(object_class_by_name(vioc_name)); vfio_container_init(&container->bcontainer, vioc); return true; From 58f5c13260c2bf8fe158d0e176d1595858157946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:01 +0200 Subject: [PATCH 1641/2884] vfio/container: Introduce vfio_create_container() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This routine allocates the QEMU struct type representing the VFIO container. It is minimal currently and future changes will do more initialization. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index bb6abe60ee..a869194279 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -430,6 +430,16 @@ static bool vfio_set_iommu(VFIOContainer *container, int group_fd, return true; } +static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, + Error **errp) +{ + VFIOContainer *container; + + container = g_malloc0(sizeof(*container)); + container->fd = fd; + return container; +} + static int vfio_get_iommu_info(VFIOContainer *container, struct vfio_iommu_type1_info **info) { @@ -604,13 +614,14 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, goto close_fd_exit; } - container = g_malloc0(sizeof(*container)); - container->fd = fd; - bcontainer = &container->bcontainer; - + container = vfio_create_container(fd, group, errp); + if (!container) { + goto close_fd_exit; + } if (!vfio_set_iommu(container, group->fd, errp)) { goto free_container_exit; } + bcontainer = &container->bcontainer; if (!vfio_cpr_register_container(bcontainer, errp)) { goto free_container_exit; From 9550fdfd29f52d548d99aed2b1a002308ad6175a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:02 +0200 Subject: [PATCH 1642/2884] vfio/container: Discover IOMMU type before creating the container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the QEMU struct type representing the VFIO container is deduced from the IOMMU type exposed by the host, this type should be well defined *before* creating the container struct. This will be necessary to instantiate a QOM object of the correct type in future changes. Rework vfio_set_iommu() to extract the part doing the container initialization and move it under vfio_create_container(). Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 47 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index a869194279..31bdc46a96 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -389,54 +389,56 @@ static const char *vfio_get_iommu_class_name(int iommu_type) }; } -static bool vfio_set_iommu(VFIOContainer *container, int group_fd, - Error **errp) +static bool vfio_set_iommu(int container_fd, int group_fd, + int *iommu_type, Error **errp) { - int iommu_type; - const VFIOIOMMUClass *vioc; - const char *vioc_name; - - iommu_type = vfio_get_iommu_type(container->fd, errp); - if (iommu_type < 0) { - return false; - } - - if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container_fd)) { error_setg_errno(errp, errno, "Failed to set group container"); return false; } - while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) { - if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + while (ioctl(container_fd, VFIO_SET_IOMMU, *iommu_type)) { + if (*iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { /* * On sPAPR, despite the IOMMU subdriver always advertises v1 and * v2, the running platform may not support v2 and there is no * way to guess it until an IOMMU group gets added to the container. * So in case it fails with v2, try v1 as a fallback. */ - iommu_type = VFIO_SPAPR_TCE_IOMMU; + *iommu_type = VFIO_SPAPR_TCE_IOMMU; continue; } error_setg_errno(errp, errno, "Failed to set iommu for container"); return false; } - container->iommu_type = iommu_type; - - vioc_name = vfio_get_iommu_class_name(iommu_type); - vioc = VFIO_IOMMU_CLASS(object_class_by_name(vioc_name)); - - vfio_container_init(&container->bcontainer, vioc); return true; } static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, Error **errp) { + int iommu_type; + const VFIOIOMMUClass *vioc; + const char *vioc_name; VFIOContainer *container; + iommu_type = vfio_get_iommu_type(fd, errp); + if (iommu_type < 0) { + return NULL; + } + + if (!vfio_set_iommu(fd, group->fd, &iommu_type, errp)) { + return NULL; + } + + vioc_name = vfio_get_iommu_class_name(iommu_type); + vioc = VFIO_IOMMU_CLASS(object_class_by_name(vioc_name)); + container = g_malloc0(sizeof(*container)); container->fd = fd; + container->iommu_type = iommu_type; + vfio_container_init(&container->bcontainer, vioc); return container; } @@ -618,9 +620,6 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, if (!container) { goto close_fd_exit; } - if (!vfio_set_iommu(container, group->fd, errp)) { - goto free_container_exit; - } bcontainer = &container->bcontainer; if (!vfio_cpr_register_container(bcontainer, errp)) { From 504d297e10fdfe1b1243274e334abb0074ee69f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:03 +0200 Subject: [PATCH 1643/2884] vfio/container: Change VFIOContainerBase to use QOM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIOContainerBase was made a QOM interface because we believed that a QOM object would expose all the IOMMU backends to the QEMU machine and human interface. This only applies to user creatable devices or objects. Change the VFIOContainerBase nature from interface to object and make the necessary adjustments in the VFIO_IOMMU hierarchy. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 4 +++- hw/vfio/container.c | 1 + hw/vfio/iommufd.c | 1 + hw/vfio/spapr.c | 3 +++ include/hw/vfio/vfio-common.h | 4 ++++ include/hw/vfio/vfio-container-base.h | 12 +++--------- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 280f0dd2db..98c15e174d 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -102,8 +102,10 @@ void vfio_container_destroy(VFIOContainerBase *bcontainer) static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU, - .parent = TYPE_INTERFACE, + .parent = TYPE_OBJECT, + .instance_size = sizeof(VFIOContainerBase), .class_size = sizeof(VFIOIOMMUClass), + .abstract = true, }, }; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 31bdc46a96..3ae52530a9 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1196,6 +1196,7 @@ static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_LEGACY, .parent = TYPE_VFIO_IOMMU, + .instance_size = sizeof(VFIOContainer), .class_init = vfio_iommu_legacy_class_init, }, { .name = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index e5d9334142..3e9d642034 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -672,6 +672,7 @@ static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_IOMMUFD, .parent = TYPE_VFIO_IOMMU, + .instance_size = sizeof(VFIOIOMMUFDContainer), .class_init = vfio_iommu_iommufd_class_init, }, { .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 47b040f1bc..018bd20481 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -30,6 +30,8 @@ typedef struct VFIOSpaprContainer { QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; } VFIOSpaprContainer; +OBJECT_DECLARE_SIMPLE_TYPE(VFIOSpaprContainer, VFIO_IOMMU_SPAPR); + static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section) { if (memory_region_is_iommu(section->mr)) { @@ -548,6 +550,7 @@ static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_SPAPR, .parent = TYPE_VFIO_IOMMU_LEGACY, + .instance_size = sizeof(VFIOSpaprContainer), .class_init = vfio_iommu_spapr_class_init, }, }; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 825d80130b..e8ddf92bb1 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -84,6 +84,8 @@ typedef struct VFIOContainer { QLIST_HEAD(, VFIOGroup) group_list; } VFIOContainer; +OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); + typedef struct VFIOHostDMAWindow { hwaddr min_iova; hwaddr max_iova; @@ -99,6 +101,8 @@ typedef struct VFIOIOMMUFDContainer { uint32_t ioas_id; } VFIOIOMMUFDContainer; +OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); + typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIODevice { diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index d505f63607..b079b76f68 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -34,6 +34,7 @@ typedef struct VFIOAddressSpace { * This is the base object for vfio container backends */ typedef struct VFIOContainerBase { + Object parent; const VFIOIOMMUClass *ops; VFIOAddressSpace *space; MemoryListener listener; @@ -96,17 +97,10 @@ void vfio_container_destroy(VFIOContainerBase *bcontainer); #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" #define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" -/* - * VFIOContainerBase is not an abstract QOM object because it felt - * unnecessary to expose all the IOMMU backends to the QEMU machine - * and human interface. However, we can still abstract the IOMMU - * backend handlers using a QOM interface class. This provides more - * flexibility when referencing the various implementations. - */ -DECLARE_CLASS_CHECKERS(VFIOIOMMUClass, VFIO_IOMMU, TYPE_VFIO_IOMMU) +OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) struct VFIOIOMMUClass { - InterfaceClass parent_class; + ObjectClass parent_class; /* Properties */ const char *hiod_typename; From 938026053f43031319d5e2159dcf4f993519afef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:04 +0200 Subject: [PATCH 1644/2884] vfio/container: Switch to QOM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of allocating the container struct, create a QOM object of the appropriate type. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 6 +++--- hw/vfio/iommufd.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3ae52530a9..ff3a6831da 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -435,7 +435,7 @@ static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, vioc_name = vfio_get_iommu_class_name(iommu_type); vioc = VFIO_IOMMU_CLASS(object_class_by_name(vioc_name)); - container = g_malloc0(sizeof(*container)); + container = VFIO_IOMMU_LEGACY(object_new(vioc_name)); container->fd = fd; container->iommu_type = iommu_type; vfio_container_init(&container->bcontainer, vioc); @@ -674,7 +674,7 @@ unregister_container_exit: vfio_cpr_unregister_container(bcontainer); free_container_exit: - g_free(container); + object_unref(container); close_fd_exit: close(fd); @@ -718,7 +718,7 @@ static void vfio_disconnect_container(VFIOGroup *group) trace_vfio_disconnect_container(container->fd); vfio_cpr_unregister_container(bcontainer); close(container->fd); - g_free(container); + object_unref(container); vfio_put_address_space(space); } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 3e9d642034..d59df85840 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -239,7 +239,7 @@ static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) memory_listener_unregister(&bcontainer->listener); vfio_container_destroy(bcontainer); iommufd_backend_free_id(container->be, container->ioas_id); - g_free(container); + object_unref(container); } static int iommufd_cdev_ram_block_discard_disable(bool state) @@ -352,7 +352,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, trace_iommufd_cdev_alloc_ioas(vbasedev->iommufd->fd, ioas_id); - container = g_malloc0(sizeof(*container)); + container = VFIO_IOMMU_IOMMUFD(object_new(TYPE_VFIO_IOMMU_IOMMUFD)); container->be = vbasedev->iommufd; container->ioas_id = ioas_id; From 2137d2fd1779df61ae011186b0f3a8ecb9ca0a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:05 +0200 Subject: [PATCH 1645/2884] vfio/container: Introduce an instance_init() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to move the initialization code from vfio_container_init(), which we will soon remove. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 98c15e174d..3858f5ab1d 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -75,12 +75,6 @@ void vfio_container_init(VFIOContainerBase *bcontainer, const VFIOIOMMUClass *ops) { bcontainer->ops = ops; - bcontainer->error = NULL; - bcontainer->dirty_pages_supported = false; - bcontainer->dma_max_mappings = 0; - bcontainer->iova_ranges = NULL; - QLIST_INIT(&bcontainer->giommu_list); - QLIST_INIT(&bcontainer->vrdl_list); } void vfio_container_destroy(VFIOContainerBase *bcontainer) @@ -99,10 +93,23 @@ void vfio_container_destroy(VFIOContainerBase *bcontainer) g_list_free_full(bcontainer->iova_ranges, g_free); } +static void vfio_container_instance_init(Object *obj) +{ + VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); + + bcontainer->error = NULL; + bcontainer->dirty_pages_supported = false; + bcontainer->dma_max_mappings = 0; + bcontainer->iova_ranges = NULL; + QLIST_INIT(&bcontainer->giommu_list); + QLIST_INIT(&bcontainer->vrdl_list); +} + static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU, .parent = TYPE_OBJECT, + .instance_init = vfio_container_instance_init, .instance_size = sizeof(VFIOContainerBase), .class_size = sizeof(VFIOIOMMUClass), .abstract = true, From 41d698b8d63b719c5b32bd056109be272f6dd740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:06 +0200 Subject: [PATCH 1646/2884] vfio/container: Remove VFIOContainerBase::ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, use VFIO_IOMMU_GET_CLASS() to get the class pointer. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 2 +- hw/vfio/container-base.c | 37 +++++++++++++++++---------- hw/vfio/container.c | 15 ++++++----- hw/vfio/iommufd.c | 4 +-- hw/vfio/pci.c | 4 +-- include/hw/vfio/vfio-container-base.h | 1 - 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 1686a0bed2..7cdb969fd3 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1573,5 +1573,5 @@ void vfio_detach_device(VFIODevice *vbasedev) return; } object_unref(vbasedev->hiod); - vbasedev->bcontainer->ops->detach_device(vbasedev); + VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 3858f5ab1d..24669d4d74 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -19,62 +19,73 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly) { - g_assert(bcontainer->ops->dma_map); - return bcontainer->ops->dma_map(bcontainer, iova, size, vaddr, readonly); + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->dma_map); + return vioc->dma_map(bcontainer, iova, size, vaddr, readonly); } int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - g_assert(bcontainer->ops->dma_unmap); - return bcontainer->ops->dma_unmap(bcontainer, iova, size, iotlb); + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->dma_unmap); + return vioc->dma_unmap(bcontainer, iova, size, iotlb); } bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section, Error **errp) { - if (!bcontainer->ops->add_window) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + if (!vioc->add_window) { return true; } - return bcontainer->ops->add_window(bcontainer, section, errp); + return vioc->add_window(bcontainer, section, errp); } void vfio_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { - if (!bcontainer->ops->del_window) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + if (!vioc->del_window) { return; } - return bcontainer->ops->del_window(bcontainer, section); + return vioc->del_window(bcontainer, section); } int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + if (!bcontainer->dirty_pages_supported) { return 0; } - g_assert(bcontainer->ops->set_dirty_page_tracking); - return bcontainer->ops->set_dirty_page_tracking(bcontainer, start, errp); + g_assert(vioc->set_dirty_page_tracking); + return vioc->set_dirty_page_tracking(bcontainer, start, errp); } int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { - g_assert(bcontainer->ops->query_dirty_bitmap); - return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size, + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->query_dirty_bitmap); + return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size, errp); } void vfio_container_init(VFIOContainerBase *bcontainer, const VFIOIOMMUClass *ops) { - bcontainer->ops = ops; } void vfio_container_destroy(VFIOContainerBase *bcontainer) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index ff3a6831da..a2f5fbad00 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -548,6 +548,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, VFIOContainerBase *bcontainer; int ret, fd; VFIOAddressSpace *space; + VFIOIOMMUClass *vioc; space = vfio_get_address_space(as); @@ -632,9 +633,10 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, goto unregister_container_exit; } - assert(bcontainer->ops->setup); + vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + assert(vioc->setup); - if (!bcontainer->ops->setup(bcontainer, errp)) { + if (!vioc->setup(bcontainer, errp)) { goto enable_discards_exit; } @@ -663,8 +665,8 @@ listener_release_exit: QLIST_REMOVE(bcontainer, next); vfio_kvm_device_del_group(group); memory_listener_unregister(&bcontainer->listener); - if (bcontainer->ops->release) { - bcontainer->ops->release(bcontainer); + if (vioc->release) { + vioc->release(bcontainer); } enable_discards_exit: @@ -689,6 +691,7 @@ static void vfio_disconnect_container(VFIOGroup *group) { VFIOContainer *container = group->container; VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); QLIST_REMOVE(group, container_next); group->container = NULL; @@ -700,8 +703,8 @@ static void vfio_disconnect_container(VFIOGroup *group) */ if (QLIST_EMPTY(&container->group_list)) { memory_listener_unregister(&bcontainer->listener); - if (bcontainer->ops->release) { - bcontainer->ops->release(bcontainer); + if (vioc->release) { + vioc->release(bcontainer); } } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index d59df85840..7bc76f80b4 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -324,7 +324,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, /* try to attach to an existing container in this space */ QLIST_FOREACH(bcontainer, &space->containers, next) { container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); - if (bcontainer->ops != iommufd_vioc || + if (VFIO_IOMMU_GET_CLASS(bcontainer) != iommufd_vioc || vbasedev->iommufd != container->be) { continue; } @@ -465,7 +465,7 @@ static VFIODevice *iommufd_cdev_pci_find_by_devid(__u32 devid) VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); QLIST_FOREACH(vbasedev_iter, &vfio_device_list, global_next) { - if (vbasedev_iter->bcontainer->ops != iommufd_vioc) { + if (VFIO_IOMMU_GET_CLASS(vbasedev_iter->bcontainer) != iommufd_vioc) { continue; } if (devid == vbasedev_iter->devid) { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d8a76c1ee0..e03d9f3ba5 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2511,9 +2511,9 @@ int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) { VFIODevice *vbasedev = &vdev->vbasedev; - const VFIOIOMMUClass *ops = vbasedev->bcontainer->ops; + const VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer); - return ops->pci_hot_reset(vbasedev, single); + return vioc->pci_hot_reset(vbasedev, single); } /* diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index b079b76f68..6b57cd8e7f 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -35,7 +35,6 @@ typedef struct VFIOAddressSpace { */ typedef struct VFIOContainerBase { Object parent; - const VFIOIOMMUClass *ops; VFIOAddressSpace *space; MemoryListener listener; Error *error; From 2f7243cb8a3184cc26c70805e5aaec07fac943d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:07 +0200 Subject: [PATCH 1647/2884] vfio/container: Remove vfio_container_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's now empty. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 5 ----- hw/vfio/container.c | 3 --- hw/vfio/iommufd.c | 1 - include/hw/vfio/vfio-container-base.h | 2 -- 4 files changed, 11 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 24669d4d74..970ae2356a 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -83,11 +83,6 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, errp); } -void vfio_container_init(VFIOContainerBase *bcontainer, - const VFIOIOMMUClass *ops) -{ -} - void vfio_container_destroy(VFIOContainerBase *bcontainer) { VFIOGuestIOMMU *giommu, *tmp; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index a2f5fbad00..3f2032d5c4 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -419,7 +419,6 @@ static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, Error **errp) { int iommu_type; - const VFIOIOMMUClass *vioc; const char *vioc_name; VFIOContainer *container; @@ -433,12 +432,10 @@ static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, } vioc_name = vfio_get_iommu_class_name(iommu_type); - vioc = VFIO_IOMMU_CLASS(object_class_by_name(vioc_name)); container = VFIO_IOMMU_LEGACY(object_new(vioc_name)); container->fd = fd; container->iommu_type = iommu_type; - vfio_container_init(&container->bcontainer, vioc); return container; } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7bc76f80b4..09b71a6617 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -357,7 +357,6 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, container->ioas_id = ioas_id; bcontainer = &container->bcontainer; - vfio_container_init(bcontainer, iommufd_vioc); vfio_address_space_insert(space, bcontainer); if (!iommufd_cdev_attach_container(vbasedev, container, errp)) { diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 6b57cd8e7f..6242a62771 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -86,8 +86,6 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); -void vfio_container_init(VFIOContainerBase *bcontainer, - const VFIOIOMMUClass *ops); void vfio_container_destroy(VFIOContainerBase *bcontainer); From b052f73cbec3bf593a04b2f5cdaf3569256859a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:08 +0200 Subject: [PATCH 1648/2884] vfio/container: Introduce vfio_iommu_legacy_instance_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just as we did for the VFIOContainerBase object, introduce an instance_init() handler for the legacy VFIOContainer object and do the specific initialization there. Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3f2032d5c4..45123acbdd 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -639,7 +639,6 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, vfio_kvm_device_add_group(group); - QLIST_INIT(&container->group_list); vfio_address_space_insert(space, bcontainer); group->container = container; @@ -1183,6 +1182,13 @@ hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) return l; } +static void vfio_iommu_legacy_instance_init(Object *obj) +{ + VFIOContainer *container = VFIO_IOMMU_LEGACY(obj); + + QLIST_INIT(&container->group_list); +} + static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) { HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); @@ -1196,6 +1202,7 @@ static const TypeInfo types[] = { { .name = TYPE_VFIO_IOMMU_LEGACY, .parent = TYPE_VFIO_IOMMU, + .instance_init = vfio_iommu_legacy_instance_init, .instance_size = sizeof(VFIOContainer), .class_init = vfio_iommu_legacy_class_init, }, { From 96b7af4388b38bc1f66467a9c7c8ee9d3bff500f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 17 Jun 2024 08:34:09 +0200 Subject: [PATCH 1649/2884] vfio/container: Move vfio_container_destroy() to an instance_finalize() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_container_destroy() clears the resources allocated VFIOContainerBase object. Now that VFIOContainerBase is a QOM object, add an instance_finalize() handler to do the cleanup. It will be called through object_unref(). Suggested-by: Zhenzhong Duan Reviewed-by: Zhenzhong Duan Reviewed-by: Eric Auger Tested-by: Eric Auger Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 4 +++- hw/vfio/container.c | 2 -- hw/vfio/iommufd.c | 1 - include/hw/vfio/vfio-container-base.h | 3 --- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 970ae2356a..50b1664f89 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -83,8 +83,9 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, errp); } -void vfio_container_destroy(VFIOContainerBase *bcontainer) +static void vfio_container_instance_finalize(Object *obj) { + VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); VFIOGuestIOMMU *giommu, *tmp; QLIST_REMOVE(bcontainer, next); @@ -116,6 +117,7 @@ static const TypeInfo types[] = { .name = TYPE_VFIO_IOMMU, .parent = TYPE_OBJECT, .instance_init = vfio_container_instance_init, + .instance_finalize = vfio_container_instance_finalize, .instance_size = sizeof(VFIOContainerBase), .class_size = sizeof(VFIOIOMMUClass), .abstract = true, diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 45123acbdd..2e7ecdf10e 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -712,8 +712,6 @@ static void vfio_disconnect_container(VFIOGroup *group) if (QLIST_EMPTY(&container->group_list)) { VFIOAddressSpace *space = bcontainer->space; - vfio_container_destroy(bcontainer); - trace_vfio_disconnect_container(container->fd); vfio_cpr_unregister_container(bcontainer); close(container->fd); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 09b71a6617..c2f158e603 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -237,7 +237,6 @@ static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) return; } memory_listener_unregister(&bcontainer->listener); - vfio_container_destroy(bcontainer); iommufd_backend_free_id(container->be, container->ioas_id); object_unref(container); } diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 6242a62771..419e45ee7a 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -86,9 +86,6 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); -void vfio_container_destroy(VFIOContainerBase *bcontainer); - - #define TYPE_VFIO_IOMMU "vfio-iommu" #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" From 87088fadb352c1ffa8718015f25564fc64079f4e Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 20 May 2024 13:51:56 +0100 Subject: [PATCH 1650/2884] target/riscv: Extend virtual irq csrs masks to be 64 bit wide. AIA extends the width of all IRQ CSRs to 64bit even in 32bit systems by adding missing half CSRs. This seems to be missed while adding support for virtual IRQs. The whole logic seems to be correct except the width of the masks. Fixes: 1697837ed9 ("target/riscv: Add M-mode virtual interrupt and IRQ filtering support.") Fixes: 40336d5b1d ("target/riscv: Add HS-mode virtual interrupt and IRQ filtering support.") Signed-off-by: Rajnesh Kanwal Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20240520125157.311503-2-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 58ef7079dc..dd89edb06a 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1197,18 +1197,18 @@ static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | */ /* Bit STIP can be an alias of mip.STIP that's why it's writable in mvip. */ -static const target_ulong mvip_writable_mask = MIP_SSIP | MIP_STIP | MIP_SEIP | +static const uint64_t mvip_writable_mask = MIP_SSIP | MIP_STIP | MIP_SEIP | LOCAL_INTERRUPTS; -static const target_ulong mvien_writable_mask = MIP_SSIP | MIP_SEIP | +static const uint64_t mvien_writable_mask = MIP_SSIP | MIP_SEIP | LOCAL_INTERRUPTS; -static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; -static const target_ulong hip_writable_mask = MIP_VSSIP; -static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | +static const uint64_t sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; +static const uint64_t hip_writable_mask = MIP_VSSIP; +static const uint64_t hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | LOCAL_INTERRUPTS; -static const target_ulong hvien_writable_mask = LOCAL_INTERRUPTS; +static const uint64_t hvien_writable_mask = LOCAL_INTERRUPTS; -static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; +static const uint64_t vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; const bool valid_vm_1_10_32[16] = { [VM_1_10_MBARE] = true, From 92c82a126e633c51ac01b6fc158123aca96dddf6 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal Date: Mon, 20 May 2024 13:51:57 +0100 Subject: [PATCH 1651/2884] target/riscv: Move Guest irqs out of the core local irqs range. Qemu maps IRQs 0:15 for core interrupts and 16 onward for guest interrupts which are later translated to hgiep in `riscv_cpu_set_irq()` function. With virtual IRQ support added, software now can fully use the whole local interrupt range without any actual hardware attached. This change moves the guest interrupt range after the core local interrupt range to avoid clash. Fixes: 1697837ed9 ("target/riscv: Add M-mode virtual interrupt and IRQ filtering support.") Fixes: 40336d5b1d ("target/riscv: Add HS-mode virtual interrupt and IRQ filtering support.") Signed-off-by: Rajnesh Kanwal Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20240520125157.311503-3-rkanwal@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 3 ++- target/riscv/csr.c | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 74318a925c..a470fda9be 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -695,7 +695,8 @@ typedef enum RISCVException { #define IRQ_M_EXT 11 #define IRQ_S_GEXT 12 #define IRQ_PMU_OVF 13 -#define IRQ_LOCAL_MAX 16 +#define IRQ_LOCAL_MAX 64 +/* -1 is due to bit zero of hgeip and hgeie being ROZ. */ #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) /* mip masks */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c index dd89edb06a..ee33019b03 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1145,7 +1145,14 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno, #define VSTOPI_NUM_SRCS 5 -#define LOCAL_INTERRUPTS (~0x1FFF) +/* + * All core local interrupts except the fixed ones 0:12. This macro is for + * virtual interrupts logic so please don't change this to avoid messing up + * the whole support, For reference see AIA spec: `5.3 Interrupt filtering and + * virtual interrupts for supervisor level` and `6.3.2 Virtual interrupts for + * VS level`. + */ +#define LOCAL_INTERRUPTS (~0x1FFFULL) static const uint64_t delegable_ints = S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP; From 15b8ddb18ae0be3f3921cab7169fa562b77227e0 Mon Sep 17 00:00:00 2001 From: Jerry Zhang Jian Date: Tue, 28 May 2024 21:03:49 +0800 Subject: [PATCH 1652/2884] target/riscv: zvbb implies zvkb According to RISC-V crypto spec, Zvkb extension is a subset of the Zvbb extension [1]. 1: https://github.com/riscv/riscv-crypto/blob/1769c2609bf4535632e0c0fd715778f212bb272e/doc/vector/riscv-crypto-vector-zvkb.adoc?plain=1#L10 Signed-off-by: Jerry Zhang Jian Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Message-ID: <20240528130349.20193-1-jerry.zhangjian@sifive.com> [ Changes by AF: - Tidy up commit message - Rebase ] Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 683f604d9f..fa8a17cc60 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -667,6 +667,10 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); } + if (cpu->cfg.ext_zvbb) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); + } + if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned || cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32x) { From 190e0ae6290d17780c075ca38b9ecb9895dee419 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:52 -0300 Subject: [PATCH 1653/2884] hw/riscv/virt.c: add address-cells in create_fdt_one_aplic() We need #address-cells properties in all interrupt controllers that are referred by an interrupt-map [1]. For the RISC-V machine, both PLIC and APLIC controllers must have this property. PLIC already sets it in create_fdt_socket_plic(). Set the property for APLIC in create_fdt_one_aplic(). [1] https://lore.kernel.org/linux-arm-kernel/CAL_JsqJE15D-xXxmELsmuD+JQHZzxGzdXvikChn6KFWqk6NzPw@mail.gmail.com/ Suggested-by: Anup Patel Fixes: e6faee65855b ("hw/riscv: virt: Add optional AIA APLIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 ++ include/hw/riscv/virt.h | 1 + 2 files changed, 3 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 5676d66d12..e903f05851 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -609,6 +609,8 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); qemu_fdt_add_subnode(ms->fdt, aplic_name); qemu_fdt_setprop_string(ms->fdt, aplic_name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_cell(ms->fdt, aplic_name, "#address-cells", + FDT_APLIC_ADDR_CELLS); qemu_fdt_setprop_cell(ms->fdt, aplic_name, "#interrupt-cells", FDT_APLIC_INT_CELLS); qemu_fdt_setprop(ms->fdt, aplic_name, "interrupt-controller", NULL, 0); diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 3db839160f..c0dc41ff9a 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -118,6 +118,7 @@ enum { #define FDT_PLIC_ADDR_CELLS 0 #define FDT_PLIC_INT_CELLS 1 #define FDT_APLIC_INT_CELLS 2 +#define FDT_APLIC_ADDR_CELLS 0 #define FDT_IMSIC_INT_CELLS 0 #define FDT_MAX_INT_CELLS 2 #define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ From 02dd57b3f9f0951d5de087043d29aac64c92a6b4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:53 -0300 Subject: [PATCH 1654/2884] hw/riscv/virt.c: add aplic nodename helper We'll change the aplic DT nodename in the next patch and the name is hardcoded in 2 different functions. Create a helper to change a single place later. While we're at it, in create_fdt_socket_aplic(), move 'aplic_name' inside the conditional to avoid allocating a string that won't be used when socket == NULL. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e903f05851..569d9def24 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -588,6 +588,12 @@ static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, } +/* Caller must free string after use */ +static char *fdt_get_aplic_nodename(unsigned long aplic_addr) +{ + return g_strdup_printf("/soc/aplic@%lx", aplic_addr); +} + static void create_fdt_one_aplic(RISCVVirtState *s, int socket, unsigned long aplic_addr, uint32_t aplic_size, uint32_t msi_phandle, @@ -597,7 +603,7 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, bool m_mode, int num_harts) { int cpu; - g_autofree char *aplic_name = NULL; + g_autofree char *aplic_name = fdt_get_aplic_nodename(aplic_addr); g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2); MachineState *ms = MACHINE(s); @@ -606,7 +612,6 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, aplic_cells[cpu * 2 + 1] = cpu_to_be32(m_mode ? IRQ_M_EXT : IRQ_S_EXT); } - aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); qemu_fdt_add_subnode(ms->fdt, aplic_name); qemu_fdt_setprop_string(ms->fdt, aplic_name, "compatible", "riscv,aplic"); qemu_fdt_setprop_cell(ms->fdt, aplic_name, "#address-cells", @@ -648,7 +653,6 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, uint32_t *aplic_phandles, int num_harts) { - g_autofree char *aplic_name = NULL; unsigned long aplic_addr; MachineState *ms = MACHINE(s); uint32_t aplic_m_phandle, aplic_s_phandle; @@ -674,9 +678,8 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, aplic_s_phandle, 0, false, num_harts); - aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); - if (!socket) { + g_autofree char *aplic_name = fdt_get_aplic_nodename(aplic_addr); platform_bus_add_all_fdt_nodes(ms->fdt, aplic_name, memmap[VIRT_PLATFORM_BUS].base, memmap[VIRT_PLATFORM_BUS].size, From 29390fdbc1f80f16008391a36024e5d090590bc5 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:54 -0300 Subject: [PATCH 1655/2884] hw/riscv/virt.c: rename aplic nodename to 'interrupt-controller' The correct name of the aplic controller node, as per Linux kernel DT docs [1], is 'interrupt-controller@addr'. [1] Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml Reported-by: Conor Dooley Fixes: e6faee65855b ("hw/riscv: virt: Add optional AIA APLIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 569d9def24..a803c33e21 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -591,7 +591,7 @@ static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, /* Caller must free string after use */ static char *fdt_get_aplic_nodename(unsigned long aplic_addr) { - return g_strdup_printf("/soc/aplic@%lx", aplic_addr); + return g_strdup_printf("/soc/interrupt-controller@%lx", aplic_addr); } static void create_fdt_one_aplic(RISCVVirtState *s, int socket, From 362b31fc35f39d5148744c7394d1e9a7d886effd Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:55 -0300 Subject: [PATCH 1656/2884] hw/riscv/virt.c: aplic DT: add 'qemu, aplic' to 'compatible' The DT docs for riscv,aplic [1] predicts a 'qemu,aplic' enum in the 'compatible' property. [1] Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml Reported-by: Conor Dooley Fixes: e6faee65855b ("hw/riscv: virt: Add optional AIA APLIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index a803c33e21..746df3f294 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -606,6 +606,9 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, g_autofree char *aplic_name = fdt_get_aplic_nodename(aplic_addr); g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2); MachineState *ms = MACHINE(s); + static const char * const aplic_compat[2] = { + "qemu,aplic", "riscv,aplic" + }; for (cpu = 0; cpu < num_harts; cpu++) { aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); @@ -613,7 +616,9 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, } qemu_fdt_add_subnode(ms->fdt, aplic_name); - qemu_fdt_setprop_string(ms->fdt, aplic_name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_string_array(ms->fdt, aplic_name, "compatible", + (char **)&aplic_compat, + ARRAY_SIZE(aplic_compat)); qemu_fdt_setprop_cell(ms->fdt, aplic_name, "#address-cells", FDT_APLIC_ADDR_CELLS); qemu_fdt_setprop_cell(ms->fdt, aplic_name, From b1f1e9dcfab03936b5160d606bcfdbeea1cab782 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:56 -0300 Subject: [PATCH 1657/2884] hw/riscv/virt.c: aplic DT: rename prop to 'riscv, delegation' The DT docs for riscv,aplic [1] predicts a 'riscv,delegation' property. Not 'riscv,delegate'. [1] Documentation/devicetree/bindings/interrupt-controller/riscv,aplic.yaml Reported-by: Conor Dooley Fixes: e6faee65855b ("hw/riscv: virt: Add optional AIA APLIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 746df3f294..9c6b39b7df 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -640,7 +640,7 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, if (aplic_child_phandle) { qemu_fdt_setprop_cell(ms->fdt, aplic_name, "riscv,children", aplic_child_phandle); - qemu_fdt_setprop_cells(ms->fdt, aplic_name, "riscv,delegate", + qemu_fdt_setprop_cells(ms->fdt, aplic_name, "riscv,delegation", aplic_child_phandle, 0x1, VIRT_IRQCHIP_NUM_SOURCES); } From e8ad5817b250720321d44302a811cf7319f4b029 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:57 -0300 Subject: [PATCH 1658/2884] hw/riscv/virt.c: change imsic nodename to 'interrupt-controller' The Linux DT docs for imsic [1] predicts an 'interrupt-controller@addr' node, not 'imsic@addr', given this node inherits the 'interrupt-controller' node. [1] Documentation/devicetree/bindings/interrupt-controller/riscv,imsics.yaml Reported-by: Conor Dooley Fixes: 28d8c281200f ("hw/riscv: virt: Add optional AIA IMSIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 9c6b39b7df..376e362a68 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -538,7 +538,8 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, } } - imsic_name = g_strdup_printf("/soc/imsics@%lx", (unsigned long)base_addr); + imsic_name = g_strdup_printf("/soc/interrupt-controller@%lx", + (unsigned long)base_addr); qemu_fdt_add_subnode(ms->fdt, imsic_name); qemu_fdt_setprop_string(ms->fdt, imsic_name, "compatible", "riscv,imsics"); qemu_fdt_setprop_cell(ms->fdt, imsic_name, "#interrupt-cells", From 8fb0bb5e8a601c66af2f1b5261256451d3d23587 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:58 -0300 Subject: [PATCH 1659/2884] hw/riscv/virt.c: imsics DT: add 'qemu, imsics' to 'compatible' The DT docs for riscv,imsics [1] predicts a 'qemu,imsics' enum in the 'compatible' property. [1] Documentation/devicetree/bindings/interrupt-controller/riscv,imsics.yaml Reported-by: Conor Dooley Fixes: 28d8c281200f ("hw/riscv: virt: Add optional AIA IMSIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240531202759.911601-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 376e362a68..e1ecf79551 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -515,6 +515,9 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, uint32_t imsic_max_hart_per_socket, imsic_addr, imsic_size; g_autofree uint32_t *imsic_cells = NULL; g_autofree uint32_t *imsic_regs = NULL; + static const char * const imsic_compat[2] = { + "qemu,imsics", "riscv,imsics" + }; imsic_cells = g_new0(uint32_t, ms->smp.cpus * 2); imsic_regs = g_new0(uint32_t, socket_count * 4); @@ -541,7 +544,10 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, imsic_name = g_strdup_printf("/soc/interrupt-controller@%lx", (unsigned long)base_addr); qemu_fdt_add_subnode(ms->fdt, imsic_name); - qemu_fdt_setprop_string(ms->fdt, imsic_name, "compatible", "riscv,imsics"); + qemu_fdt_setprop_string_array(ms->fdt, imsic_name, "compatible", + (char **)&imsic_compat, + ARRAY_SIZE(imsic_compat)); + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "#interrupt-cells", FDT_IMSIC_INT_CELLS); qemu_fdt_setprop(ms->fdt, imsic_name, "interrupt-controller", NULL, 0); From f42cdf2ea5b3a1dc369792d7acbf9cd3e5c90815 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 31 May 2024 17:27:59 -0300 Subject: [PATCH 1660/2884] hw/riscv/virt.c: imsics DT: add '#msi-cells' The DT docs for riscv,imsics [1] requires a 'msi-cell' property. Add one and set it zero. [1] Documentation/devicetree/bindings/interrupt-controller/riscv,imsics.yaml Reported-by: Conor Dooley Fixes: 28d8c281200f ("hw/riscv: virt: Add optional AIA IMSIC support to virt machine") Signed-off-by: Daniel Henrique Barboza Message-ID: <20240531202759.911601-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e1ecf79551..9b648540e6 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -552,6 +552,7 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, FDT_IMSIC_INT_CELLS); qemu_fdt_setprop(ms->fdt, imsic_name, "interrupt-controller", NULL, 0); qemu_fdt_setprop(ms->fdt, imsic_name, "msi-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "#msi-cells", 0); qemu_fdt_setprop(ms->fdt, imsic_name, "interrupts-extended", imsic_cells, ms->smp.cpus * sizeof(uint32_t) * 2); qemu_fdt_setprop(ms->fdt, imsic_name, "reg", imsic_regs, From 80b605056d59bb5ee979e2e6ba60528fb3f9eb1b Mon Sep 17 00:00:00 2001 From: Chao Du Date: Thu, 6 Jun 2024 01:44:59 +0000 Subject: [PATCH 1661/2884] target/riscv/kvm: add software breakpoints support This patch implements insert/remove software breakpoint process. For RISC-V, GDB treats single-step similarly to breakpoint: add a breakpoint at the next step address, then continue. So this also works for single-step debugging. Implement kvm_arch_update_guest_debug(): Set the control flag when there are active breakpoints. This will help KVM to know the status in the userspace. Add some stubs which are necessary for building, and will be implemented later. Signed-off-by: Chao Du Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20240606014501.20763-2-duchao@eswincomputing.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 235e2cdaca..748fe5980f 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1969,3 +1969,72 @@ static const TypeInfo riscv_kvm_cpu_type_infos[] = { }; DEFINE_TYPES(riscv_kvm_cpu_type_infos) + +static const uint32_t ebreak_insn = 0x00100073; +static const uint16_t c_ebreak_insn = 0x9002; + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 2, 0)) { + return -EINVAL; + } + + if ((bp->saved_insn & 0x3) == 0x3) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) + || cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&ebreak_insn, 4, 1)) { + return -EINVAL; + } + } else { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&c_ebreak_insn, 2, 1)) { + return -EINVAL; + } + } + + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + uint32_t ebreak; + uint16_t c_ebreak; + + if ((bp->saved_insn & 0x3) == 0x3) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&ebreak, 4, 0) || + ebreak != ebreak_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + return -EINVAL; + } + } else { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&c_ebreak, 2, 0) || + c_ebreak != c_ebreak_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 2, 1)) { + return -EINVAL; + } + } + + return 0; +} + +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + /* TODO; To be implemented later. */ + return -EINVAL; +} + +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + /* TODO; To be implemented later. */ + return -EINVAL; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + /* TODO; To be implemented later. */ +} + +void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) +{ + if (kvm_sw_breakpoints_active(cs)) { + dbg->control |= KVM_GUESTDBG_ENABLE; + } +} From b60520392daf977bca26010960f78c4885db7e9f Mon Sep 17 00:00:00 2001 From: Chao Du Date: Thu, 6 Jun 2024 01:45:00 +0000 Subject: [PATCH 1662/2884] target/riscv/kvm: handle the exit with debug reason If the breakpoint belongs to the userspace then set the ret value. Signed-off-by: Chao Du Reviewed-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20240606014501.20763-3-duchao@eswincomputing.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 748fe5980f..1047961fed 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1555,6 +1555,21 @@ static int kvm_riscv_handle_csr(CPUState *cs, struct kvm_run *run) return ret; } +static bool kvm_riscv_handle_debug(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + /* Ensure PC is synchronised */ + kvm_cpu_synchronize_state(cs); + + if (kvm_find_sw_breakpoint(cs, env->pc)) { + return true; + } + + return false; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { int ret = 0; @@ -1565,6 +1580,11 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_RISCV_CSR: ret = kvm_riscv_handle_csr(cs, run); break; + case KVM_EXIT_DEBUG: + if (kvm_riscv_handle_debug(cs)) { + ret = EXCP_DEBUG; + } + break; default: qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", __func__, run->exit_reason); From 50a92d9b46492a71fc31ee896aba4254b091a1c4 Mon Sep 17 00:00:00 2001 From: Chao Du Date: Thu, 6 Jun 2024 01:45:01 +0000 Subject: [PATCH 1663/2884] target/riscv/kvm: define TARGET_KVM_HAVE_GUEST_DEBUG To enable the KVM GUEST DEBUG for RISC-V at QEMU side. Signed-off-by: Chao Du Reviewed-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20240606014501.20763-4-duchao@eswincomputing.com> Signed-off-by: Alistair Francis --- configs/targets/riscv64-softmmu.mak | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index f688ffa7bc..917980e63e 100644 --- a/configs/targets/riscv64-softmmu.mak +++ b/configs/targets/riscv64-softmmu.mak @@ -1,6 +1,7 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml # needed by boot.c TARGET_NEED_FDT=y From a1a8e7768f321232cff276817adf37e116ba8423 Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Thu, 6 Jun 2024 21:54:49 +0800 Subject: [PATCH 1664/2884] target/riscv: Reuse the conversion function of priv_spec Public the conversion function of priv_spec in cpu.h, so that tcg-cpu.c could also use it. Signed-off-by: Jim Shu Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20240606135454.119186-2-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- target/riscv/cpu.h | 1 + target/riscv/tcg/tcg-cpu.c | 13 ++++--------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 69a08e8c2c..fd0f09c468 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1790,7 +1790,7 @@ static int priv_spec_from_str(const char *priv_spec_str) return priv_version; } -static const char *priv_spec_to_str(int priv_version) +const char *priv_spec_to_str(int priv_version) { switch (priv_version) { case PRIV_VERSION_1_10_0: diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 6fe0d712b4..b4c9e13774 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -830,4 +830,5 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); /* Implemented in th_csr.c */ void th_register_custom_csrs(RISCVCPU *cpu); +const char *priv_spec_to_str(int priv_version); #endif /* RISCV_CPU_H */ diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index fa8a17cc60..4c6141f947 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -76,16 +76,11 @@ static void riscv_cpu_write_misa_bit(RISCVCPU *cpu, uint32_t bit, static const char *cpu_priv_ver_to_str(int priv_ver) { - switch (priv_ver) { - case PRIV_VERSION_1_10_0: - return "v1.10.0"; - case PRIV_VERSION_1_11_0: - return "v1.11.0"; - case PRIV_VERSION_1_12_0: - return "v1.12.0"; - } + const char *priv_spec_str = priv_spec_to_str(priv_ver); - g_assert_not_reached(); + g_assert(priv_spec_str); + + return priv_spec_str; } static void riscv_cpu_synchronize_from_tb(CPUState *cs, From 0c2d5f7396d73a957ba665d7824c9ee7926c0357 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 6 Jun 2024 21:54:50 +0800 Subject: [PATCH 1665/2884] target/riscv: Define macros and variables for ss1p13 Add macros and variables for RISC-V privilege 1.13 support. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20240606135454.119186-3-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 4 +++- target/riscv/cpu_cfg.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index b4c9e13774..90b8f1b08f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -96,12 +96,14 @@ extern RISCVCPUProfile *riscv_profiles[]; #define PRIV_VER_1_10_0_STR "v1.10.0" #define PRIV_VER_1_11_0_STR "v1.11.0" #define PRIV_VER_1_12_0_STR "v1.12.0" +#define PRIV_VER_1_13_0_STR "v1.13.0" enum { PRIV_VERSION_1_10_0 = 0, PRIV_VERSION_1_11_0, PRIV_VERSION_1_12_0, + PRIV_VERSION_1_13_0, - PRIV_VERSION_LATEST = PRIV_VERSION_1_12_0, + PRIV_VERSION_LATEST = PRIV_VERSION_1_13_0, }; #define VEXT_VERSION_1_00_0 0x00010000 diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index e1e4f32698..fb7eebde52 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -136,6 +136,7 @@ struct RISCVCPUConfig { * TCG always implement/can't be user disabled, * based on spec version. */ + bool has_priv_1_13; bool has_priv_1_12; bool has_priv_1_11; From 7750e10656352bc9945843fa6116dc1035e1c9b4 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 6 Jun 2024 21:54:51 +0800 Subject: [PATCH 1666/2884] target/riscv: Add 'P1P13' bit in SMSTATEEN0 Based on privilege 1.13 spec, there should be a bit56 for 'P1P13' in mstateen0 that controls access to the hedeleg. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Reviewed-by: Alistair Francis Message-ID: <20240606135454.119186-4-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 1 + target/riscv/csr.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index a470fda9be..c895aa0334 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -315,6 +315,7 @@ #define SMSTATEEN0_CS (1ULL << 0) #define SMSTATEEN0_FCSR (1ULL << 1) #define SMSTATEEN0_JVT (1ULL << 2) +#define SMSTATEEN0_P1P13 (1ULL << 56) #define SMSTATEEN0_HSCONTXT (1ULL << 57) #define SMSTATEEN0_IMSIC (1ULL << 58) #define SMSTATEEN0_AIA (1ULL << 59) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index ee33019b03..a19e1afa1f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -2252,6 +2252,10 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_FCSR; } + if (env->priv_ver >= PRIV_VERSION_1_13_0) { + wr_mask |= SMSTATEEN0_P1P13; + } + return write_mstateen(env, csrno, wr_mask, new_val); } @@ -2287,6 +2291,10 @@ static RISCVException write_mstateen0h(CPURISCVState *env, int csrno, { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (env->priv_ver >= PRIV_VERSION_1_13_0) { + wr_mask |= SMSTATEEN0_P1P13; + } + return write_mstateenh(env, csrno, wr_mask, new_val); } From 27796989ac55983e95bc0538310fd5ee2eefba59 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 6 Jun 2024 21:54:52 +0800 Subject: [PATCH 1667/2884] target/riscv: Add MEDELEGH, HEDELEGH csrs for RV32 Based on privileged spec 1.13, the RV32 needs to implement MEDELEGH and HEDELEGH for exception codes 32-47 for reserving and exception codes 48-63 for custom use. Add the CSR number though the implementation is just reading zero and writing ignore. Besides, for accessing HEDELEGH, it should be controlled by mstateen0 'P1P13' bit. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20240606135454.119186-5-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 2 ++ target/riscv/csr.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index c895aa0334..096a51b331 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -156,6 +156,8 @@ /* 32-bit only */ #define CSR_MSTATUSH 0x310 +#define CSR_MEDELEGH 0x312 +#define CSR_HEDELEGH 0x612 /* Machine Trap Handling */ #define CSR_MSCRATCH 0x340 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a19e1afa1f..6f15612e76 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3229,6 +3229,33 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_hedelegh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + RISCVException ret; + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_P1P13); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Reserved, now read zero */ + *val = 0; + return RISCV_EXCP_NONE; +} + +static RISCVException write_hedelegh(CPURISCVState *env, int csrno, + target_ulong val) +{ + RISCVException ret; + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_P1P13); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Reserved, now write ignore */ + return RISCV_EXCP_NONE; +} + static RISCVException rmw_hvien64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) @@ -4633,6 +4660,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush }, + [CSR_MEDELEGH] = { "medelegh", any32, read_zero, write_ignore, + .min_priv_ver = PRIV_VERSION_1_13_0 }, + [CSR_HEDELEGH] = { "hedelegh", hmode32, read_hedelegh, write_hedelegh, + .min_priv_ver = PRIV_VERSION_1_13_0 }, /* Machine Trap Handling */ [CSR_MSCRATCH] = { "mscratch", any, read_mscratch, write_mscratch, From 8392a7c148a9d55cae97393f6a5eab3a6edbafd9 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 6 Jun 2024 21:54:53 +0800 Subject: [PATCH 1668/2884] target/riscv: Reserve exception codes for sw-check and hw-err Based on the priv-1.13.0, add the exception codes for Software-check and Hardware-error. Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20240606135454.119186-6-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 096a51b331..c257c5ed7d 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -673,6 +673,8 @@ typedef enum RISCVException { RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ + RISCV_EXCP_SW_CHECK = 0x12, /* since: priv-1.13.0 */ + RISCV_EXCP_HW_ERR = 0x13, /* since: priv-1.13.0 */ RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15, RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16, From 3adf4def19c61ae15611af15a5291d13a1c9c546 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" Date: Thu, 6 Jun 2024 21:54:54 +0800 Subject: [PATCH 1669/2884] target/riscv: Support the version for ss1p13 Add RISC-V privilege 1.13 support. Signed-off-by: Fea.Wang Signed-off-by: Fea.Wang Reviewed-by: Frank Chang Reviewed-by: Weiwei Li Reviewed-by: LIU Zhiwei Message-ID: <20240606135454.119186-7-fea.wang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 6 +++++- target/riscv/tcg/tcg-cpu.c | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fd0f09c468..4760cb2cc1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1779,7 +1779,9 @@ static int priv_spec_from_str(const char *priv_spec_str) { int priv_version = -1; - if (!g_strcmp0(priv_spec_str, PRIV_VER_1_12_0_STR)) { + if (!g_strcmp0(priv_spec_str, PRIV_VER_1_13_0_STR)) { + priv_version = PRIV_VERSION_1_13_0; + } else if (!g_strcmp0(priv_spec_str, PRIV_VER_1_12_0_STR)) { priv_version = PRIV_VERSION_1_12_0; } else if (!g_strcmp0(priv_spec_str, PRIV_VER_1_11_0_STR)) { priv_version = PRIV_VERSION_1_11_0; @@ -1799,6 +1801,8 @@ const char *priv_spec_to_str(int priv_version) return PRIV_VER_1_11_0_STR; case PRIV_VERSION_1_12_0: return PRIV_VER_1_12_0_STR; + case PRIV_VERSION_1_13_0: + return PRIV_VER_1_13_0_STR; default: return NULL; } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 4c6141f947..eb6f7b9d12 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -318,6 +318,10 @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu) cpu->cfg.has_priv_1_12 = true; } + if (cpu->env.priv_ver >= PRIV_VERSION_1_13_0) { + cpu->cfg.has_priv_1_13 = true; + } + /* zic64b is 1.12 or later */ cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && cpu->cfg.cbop_blocksize == 64 && From 4406ba2b5efce6af64905f827ca244f699db8170 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 20 Jun 2024 12:17:18 +0530 Subject: [PATCH 1670/2884] hw/riscv/virt.c: Make block devices default to virtio RISC-V virt is currently missing default type for block devices. Without this being set, proper backend is not created when option like -cdrom is used. So, make the virt board's default block device type be IF_VIRTIO similar to other architectures. We also need to set no_cdrom to avoid getting a default cdrom device. Signed-off-by: Sunil V L Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20240620064718.275427-1-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 9b648540e6..bc0893e087 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1764,6 +1764,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->init = virt_machine_init; mc->max_cpus = VIRT_CPUS_MAX; mc->default_cpu_type = TYPE_RISCV_CPU_BASE; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; mc->pci_allow_0_address = true; mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; From 209b7c293585d0859396071ef00405c02aab5472 Mon Sep 17 00:00:00 2001 From: Branislav Brzak Date: Sat, 8 Jun 2024 23:45:46 +0200 Subject: [PATCH 1671/2884] target/riscv: Fix froundnx.h nanbox check helper_froundnx_h function mistakenly uses single percision nanbox check instead of the half percision one. This patch fixes the issue. Signed-off-by: Branislav Brzak Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Message-ID: <20240608214546.226963-1-brzakbranislav@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/fpu_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 871a70a316..91b1a56d10 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -676,7 +676,7 @@ uint64_t helper_fround_h(CPURISCVState *env, uint64_t rs1) uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_s(env, rs1); + float16 frs1 = check_nanbox_h(env, rs1); frs1 = float16_round_to_int(frs1, &env->fp_status); return nanbox_h(env, frs1); } From c165408779ae3a5aceddc8603471b1c20e58fcae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Tue, 18 Jun 2024 13:26:45 +0200 Subject: [PATCH 1672/2884] target/riscv: fix instructions count handling in icount mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When icount is enabled, rather than returning the virtual CPU time, we should return the instruction count itself. Add an instructions bool parameter to get_ticks() to correctly return icount_get_raw() when icount_enabled() == 1 and instruction count is queried. This will modify the existing behavior which was returning an instructions count close to the number of cycles (CPI ~= 1). Signed-off-by: Clément Léger Reviewed-by: Atish Patra Message-ID: <20240618112649.76683-1-cleger@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 6f15612e76..432c59dc66 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -762,14 +762,18 @@ static RISCVException write_vcsr(CPURISCVState *env, int csrno, } /* User Timers and Counters */ -static target_ulong get_ticks(bool shift) +static target_ulong get_ticks(bool shift, bool instructions) { int64_t val; target_ulong result; #if !defined(CONFIG_USER_ONLY) if (icount_enabled()) { - val = icount_get(); + if (instructions) { + val = icount_get_raw(); + } else { + val = icount_get(); + } } else { val = cpu_get_host_ticks(); } @@ -804,14 +808,14 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, static RISCVException read_hpmcounter(CPURISCVState *env, int csrno, target_ulong *val) { - *val = get_ticks(false); + *val = get_ticks(false, (csrno == CSR_INSTRET)); return RISCV_EXCP_NONE; } static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) { - *val = get_ticks(true); + *val = get_ticks(true, (csrno == CSR_INSTRETH)); return RISCV_EXCP_NONE; } @@ -875,11 +879,11 @@ static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, int ctr_idx = csrno - CSR_MCYCLE; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; uint64_t mhpmctr_val = val; + bool instr = riscv_pmu_ctr_monitor_instructions(env, ctr_idx); counter->mhpmcounter_val = val; - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { - counter->mhpmcounter_prev = get_ticks(false); + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || instr) { + counter->mhpmcounter_prev = get_ticks(false, instr); if (ctr_idx > 2) { if (riscv_cpu_mxl(env) == MXL_RV32) { mhpmctr_val = mhpmctr_val | @@ -902,12 +906,12 @@ static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; uint64_t mhpmctr_val = counter->mhpmcounter_val; uint64_t mhpmctrh_val = val; + bool instr = riscv_pmu_ctr_monitor_instructions(env, ctr_idx); counter->mhpmcounterh_val = val; mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { - counter->mhpmcounterh_prev = get_ticks(true); + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || instr) { + counter->mhpmcounterh_prev = get_ticks(true, instr); if (ctr_idx > 2) { riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); } @@ -926,6 +930,7 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, counter->mhpmcounter_prev; target_ulong ctr_val = upper_half ? counter->mhpmcounterh_val : counter->mhpmcounter_val; + bool instr = riscv_pmu_ctr_monitor_instructions(env, ctr_idx); if (get_field(env->mcountinhibit, BIT(ctr_idx))) { /* @@ -946,9 +951,8 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, * The kernel computes the perf delta by subtracting the current value from * the value it initialized previously (ctr_val). */ - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { - *val = get_ticks(upper_half) - ctr_prev + ctr_val; + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || instr) { + *val = get_ticks(upper_half, instr) - ctr_prev + ctr_val; } else { *val = ctr_val; } From f04f7709203c539bec29258288ba846b993db8e3 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 25 Jun 2024 19:46:24 +0800 Subject: [PATCH 1673/2884] target/riscv: Introduce extension implied rules definition RISCVCPUImpliedExtsRule is created to store the implied rules. 'is_misa' flag is used to distinguish whether the rule is derived from the MISA or other extensions. 'ext' stores the MISA bit if 'is_misa' is true. Otherwise, it stores the offset of the extension defined in RISCVCPUConfig. 'ext' will also serve as the key of the hash tables to look up the rule in the following commit. Signed-off-by: Frank Chang Reviewed-by: Jerry Zhang Jian Tested-by: Max Chou Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20240625114629.27793-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 8 ++++++++ target/riscv/cpu.h | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4760cb2cc1..7b071ade04 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2250,6 +2250,14 @@ RISCVCPUProfile *riscv_profiles[] = { NULL, }; +RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { + NULL +}; + +RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { + NULL +}; + static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 90b8f1b08f..87742047ce 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -124,6 +124,29 @@ typedef enum { EXT_STATUS_DIRTY, } RISCVExtStatus; +typedef struct riscv_cpu_implied_exts_rule { +#ifndef CONFIG_USER_ONLY + /* + * Bitmask indicates the rule enabled status for the harts. + * This enhancement is only available in system-mode QEMU, + * as we don't have a good way (e.g. mhartid) to distinguish + * the SMP cores in user-mode QEMU. + */ + unsigned long *enabled; +#endif + /* True if this is a MISA implied rule. */ + bool is_misa; + /* ext is MISA bit if is_misa flag is true, else multi extension offset. */ + const uint32_t ext; + const uint32_t implied_misa_exts; + const uint32_t implied_multi_exts[]; +} RISCVCPUImpliedExtsRule; + +extern RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[]; +extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; + +#define RISCV_IMPLIED_EXTS_RULE_END -1 + #define MMU_USER_IDX 3 #define MAX_RISCV_PMPS (16) From 047da861f94e1306cc1d76f3f76462e4f7ed2930 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 25 Jun 2024 19:46:25 +0800 Subject: [PATCH 1674/2884] target/riscv: Introduce extension implied rule helpers Introduce helpers to enable the extensions based on the implied rules. The implied extensions are enabled recursively, so we don't have to expand all of them manually. This also eliminates the old-fashioned ordering requirement. For example, Zvksg implies Zvks, Zvks implies Zvksed, etc., removing the need to check the implied rules of Zvksg before Zvks. Signed-off-by: Frank Chang Reviewed-by: Jerry Zhang Jian Tested-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20240625114629.27793-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index eb6f7b9d12..1a3aef5bff 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -31,11 +31,17 @@ #include "hw/core/accel-cpu.h" #include "hw/core/tcg-cpu-ops.h" #include "tcg/tcg.h" +#ifndef CONFIG_USER_ONLY +#include "hw/boards.h" +#endif /* Hash that stores user set extensions */ static GHashTable *multi_ext_user_opts; static GHashTable *misa_ext_user_opts; +static GHashTable *multi_ext_implied_rules; +static GHashTable *misa_ext_implied_rules; + static bool cpu_cfg_ext_is_user_set(uint32_t ext_offset) { return g_hash_table_contains(multi_ext_user_opts, @@ -836,11 +842,117 @@ static void riscv_cpu_validate_profiles(RISCVCPU *cpu) } } +static void riscv_cpu_init_implied_exts_rules(void) +{ + RISCVCPUImpliedExtsRule *rule; +#ifndef CONFIG_USER_ONLY + MachineState *ms = MACHINE(qdev_get_machine()); +#endif + static bool initialized; + int i; + + /* Implied rules only need to be initialized once. */ + if (initialized) { + return; + } + + for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) { +#ifndef CONFIG_USER_ONLY + rule->enabled = bitmap_new(ms->smp.cpus); +#endif + g_hash_table_insert(misa_ext_implied_rules, + GUINT_TO_POINTER(rule->ext), (gpointer)rule); + } + + for (i = 0; (rule = riscv_multi_ext_implied_rules[i]); i++) { +#ifndef CONFIG_USER_ONLY + rule->enabled = bitmap_new(ms->smp.cpus); +#endif + g_hash_table_insert(multi_ext_implied_rules, + GUINT_TO_POINTER(rule->ext), (gpointer)rule); + } + + initialized = true; +} + +static void cpu_enable_implied_rule(RISCVCPU *cpu, + RISCVCPUImpliedExtsRule *rule) +{ + CPURISCVState *env = &cpu->env; + RISCVCPUImpliedExtsRule *ir; + bool enabled = false; + int i; + +#ifndef CONFIG_USER_ONLY + enabled = test_bit(cpu->env.mhartid, rule->enabled); +#endif + + if (!enabled) { + /* Enable the implied MISAs. */ + if (rule->implied_misa_exts) { + riscv_cpu_set_misa_ext(env, + env->misa_ext | rule->implied_misa_exts); + + for (i = 0; misa_bits[i] != 0; i++) { + if (rule->implied_misa_exts & misa_bits[i]) { + ir = g_hash_table_lookup(misa_ext_implied_rules, + GUINT_TO_POINTER(misa_bits[i])); + + if (ir) { + cpu_enable_implied_rule(cpu, ir); + } + } + } + } + + /* Enable the implied extensions. */ + for (i = 0; + rule->implied_multi_exts[i] != RISCV_IMPLIED_EXTS_RULE_END; i++) { + cpu_cfg_ext_auto_update(cpu, rule->implied_multi_exts[i], true); + + ir = g_hash_table_lookup(multi_ext_implied_rules, + GUINT_TO_POINTER( + rule->implied_multi_exts[i])); + + if (ir) { + cpu_enable_implied_rule(cpu, ir); + } + } + +#ifndef CONFIG_USER_ONLY + bitmap_set(rule->enabled, cpu->env.mhartid, 1); +#endif + } +} + +static void riscv_cpu_enable_implied_rules(RISCVCPU *cpu) +{ + RISCVCPUImpliedExtsRule *rule; + int i; + + /* Enable the implied MISAs. */ + for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) { + if (riscv_has_ext(&cpu->env, rule->ext)) { + cpu_enable_implied_rule(cpu, rule); + } + } + + /* Enable the implied extensions. */ + for (i = 0; (rule = riscv_multi_ext_implied_rules[i]); i++) { + if (isa_ext_is_enabled(cpu, rule->ext)) { + cpu_enable_implied_rule(cpu, rule); + } + } +} + void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) { CPURISCVState *env = &cpu->env; Error *local_err = NULL; + riscv_cpu_init_implied_exts_rules(); + riscv_cpu_enable_implied_rules(cpu); + riscv_cpu_validate_misa_priv(env, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1346,6 +1458,15 @@ static void riscv_tcg_cpu_instance_init(CPUState *cs) misa_ext_user_opts = g_hash_table_new(NULL, g_direct_equal); multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal); + + if (!misa_ext_implied_rules) { + misa_ext_implied_rules = g_hash_table_new(NULL, g_direct_equal); + } + + if (!multi_ext_implied_rules) { + multi_ext_implied_rules = g_hash_table_new(NULL, g_direct_equal); + } + riscv_cpu_add_user_properties(obj); if (riscv_cpu_has_max_extensions(obj)) { From 171773391a2b3a9d8aa4d807b945e9fabe080df6 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 25 Jun 2024 19:46:26 +0800 Subject: [PATCH 1675/2884] target/riscv: Add MISA extension implied rules Add MISA extension implied rules to enable the implied extensions of MISA recursively. Signed-off-by: Frank Chang Reviewed-by: Jerry Zhang Jian Tested-by: Max Chou Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20240625114629.27793-4-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 50 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7b071ade04..b463bd8370 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2250,8 +2250,56 @@ RISCVCPUProfile *riscv_profiles[] = { NULL, }; +static RISCVCPUImpliedExtsRule RVA_IMPLIED = { + .is_misa = true, + .ext = RVA, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zalrsc), CPU_CFG_OFFSET(ext_zaamo), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule RVD_IMPLIED = { + .is_misa = true, + .ext = RVD, + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule RVF_IMPLIED = { + .is_misa = true, + .ext = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule RVM_IMPLIED = { + .is_misa = true, + .ext = RVM, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zmmul), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule RVV_IMPLIED = { + .is_misa = true, + .ext = RVV, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64d), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { - NULL + &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, + &RVM_IMPLIED, &RVV_IMPLIED, NULL }; RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { From 340c3ca5f26591578171a7e6f3b6b9512fae2232 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 25 Jun 2024 19:46:27 +0800 Subject: [PATCH 1676/2884] target/riscv: Add multi extension implied rules Add multi extension implied rules to enable the implied extensions of the multi extension recursively. Signed-off-by: Frank Chang Reviewed-by: Jerry Zhang Jian Tested-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20240625114629.27793-5-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 340 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b463bd8370..a2640cf259 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2297,12 +2297,352 @@ static RISCVCPUImpliedExtsRule RVV_IMPLIED = { }, }; +static RISCVCPUImpliedExtsRule ZCB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCD_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcd), + .implied_misa_exts = RVD, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCE_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zce), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zcb), CPU_CFG_OFFSET(ext_zcmp), + CPU_CFG_OFFSET(ext_zcmt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCF_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcf), + .implied_misa_exts = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCMP_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcmp), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCMT_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcmt), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZDINX_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zdinx), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zfinx), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZFA_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfa), + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule ZFBFMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfbfmin), + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule ZFH_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfh), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zfhmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZFHMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfhmin), + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule ZFINX_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfinx), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZHINX_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zhinx), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zhinxmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZHINXMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zhinxmin), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zfinx), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZICNTR_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zicntr), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZIHPM_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zihpm), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZK_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zk), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zkn), CPU_CFG_OFFSET(ext_zkr), + CPU_CFG_OFFSET(ext_zkt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZKN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zkn), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zbkb), CPU_CFG_OFFSET(ext_zbkc), + CPU_CFG_OFFSET(ext_zbkx), CPU_CFG_OFFSET(ext_zkne), + CPU_CFG_OFFSET(ext_zknd), CPU_CFG_OFFSET(ext_zknh), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZKS_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zks), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zbkb), CPU_CFG_OFFSET(ext_zbkc), + CPU_CFG_OFFSET(ext_zbkx), CPU_CFG_OFFSET(ext_zksed), + CPU_CFG_OFFSET(ext_zksh), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVBB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvbb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkb), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE32F_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve32f), + .implied_misa_exts = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE32X_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve32x), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE64D_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve64d), + .implied_misa_exts = RVD, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64f), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE64F_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve64f), + .implied_misa_exts = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32f), CPU_CFG_OFFSET(ext_zve64x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE64X_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve64x), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFBFMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfbfmin), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32f), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFBFWMA_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfbfwma), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvfbfmin), CPU_CFG_OFFSET(ext_zfbfmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFH_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfh), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvfhmin), CPU_CFG_OFFSET(ext_zfhmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFHMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfhmin), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32f), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvkn), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkned), CPU_CFG_OFFSET(ext_zvknhb), + CPU_CFG_OFFSET(ext_zvkb), CPU_CFG_OFFSET(ext_zvkt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKNC_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvknc), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkn), CPU_CFG_OFFSET(ext_zvbc), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKNG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvkng), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkn), CPU_CFG_OFFSET(ext_zvkg), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKNHB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvknhb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKS_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvks), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvksed), CPU_CFG_OFFSET(ext_zvksh), + CPU_CFG_OFFSET(ext_zvkb), CPU_CFG_OFFSET(ext_zvkt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKSC_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvksc), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvks), CPU_CFG_OFFSET(ext_zvbc), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKSG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvksg), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvks), CPU_CFG_OFFSET(ext_zvkg), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, &RVM_IMPLIED, &RVV_IMPLIED, NULL }; RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { + &ZCB_IMPLIED, &ZCD_IMPLIED, &ZCE_IMPLIED, + &ZCF_IMPLIED, &ZCMP_IMPLIED, &ZCMT_IMPLIED, + &ZDINX_IMPLIED, &ZFA_IMPLIED, &ZFBFMIN_IMPLIED, + &ZFH_IMPLIED, &ZFHMIN_IMPLIED, &ZFINX_IMPLIED, + &ZHINX_IMPLIED, &ZHINXMIN_IMPLIED, &ZICNTR_IMPLIED, + &ZIHPM_IMPLIED, &ZK_IMPLIED, &ZKN_IMPLIED, + &ZKS_IMPLIED, &ZVBB_IMPLIED, &ZVE32F_IMPLIED, + &ZVE32X_IMPLIED, &ZVE64D_IMPLIED, &ZVE64F_IMPLIED, + &ZVE64X_IMPLIED, &ZVFBFMIN_IMPLIED, &ZVFBFWMA_IMPLIED, + &ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED, + &ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED, + &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, NULL }; From 3dd2168c33dbfebe40e964dc0d9eb445011797d6 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 25 Jun 2024 19:46:28 +0800 Subject: [PATCH 1677/2884] target/riscv: Add Zc extension implied rule Zc extension has special implied rules that need to be handled separately. Signed-off-by: Frank Chang Reviewed-by: Jerry Zhang Jian Tested-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20240625114629.27793-6-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 1a3aef5bff..ccca9037ed 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -925,11 +925,45 @@ static void cpu_enable_implied_rule(RISCVCPU *cpu, } } +/* Zc extension has special implied rules that need to be handled separately. */ +static void cpu_enable_zc_implied_rules(RISCVCPU *cpu) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + CPURISCVState *env = &cpu->env; + + if (cpu->cfg.ext_zce) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true); + + if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); + } + } + + /* Zca, Zcd and Zcf has a PRIV 1.12.0 restriction */ + if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); + + if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); + } + + if (riscv_has_ext(env, RVD)) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcd), true); + } + } +} + static void riscv_cpu_enable_implied_rules(RISCVCPU *cpu) { RISCVCPUImpliedExtsRule *rule; int i; + /* Enable the implied extensions for Zc. */ + cpu_enable_zc_implied_rules(cpu); + /* Enable the implied MISAs. */ for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) { if (riscv_has_ext(&cpu->env, rule->ext)) { From cb5b50d91d8568afaae09b75a01ec38f062603f7 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Tue, 25 Jun 2024 19:46:29 +0800 Subject: [PATCH 1678/2884] target/riscv: Remove extension auto-update check statements Remove the old-fashioned extension auto-update check statements as they are replaced by the extension implied rules. Signed-off-by: Frank Chang Reviewed-by: Jerry Zhang Jian Tested-by: Max Chou Reviewed-by: Daniel Henrique Barboza Message-ID: <20240625114629.27793-7-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 119 ------------------------------------- 1 file changed, 119 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index ccca9037ed..ae25686824 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -471,10 +471,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (cpu->cfg.ext_zfh) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zfhmin), true); - } - if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) { error_setg(errp, "Zfh/Zfhmin extensions require F extension"); return; @@ -496,9 +492,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) error_propagate(errp, local_err); return; } - - /* The V vector extension depends on the Zve64d extension */ - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64d), true); } /* The Zve64d extension depends on the Zve64f extension */ @@ -507,18 +500,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) error_setg(errp, "Zve64d/V extensions require D extension"); return; } - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64f), true); - } - - /* The Zve64f extension depends on the Zve64x and Zve32f extensions */ - if (cpu->cfg.ext_zve64f) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64x), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32f), true); - } - - /* The Zve64x extension depends on the Zve32x extension */ - if (cpu->cfg.ext_zve64x) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32x), true); } /* The Zve32f extension depends on the Zve32x extension */ @@ -527,11 +508,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) error_setg(errp, "Zve32f/Zve64f extensions require F extension"); return; } - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32x), true); - } - - if (cpu->cfg.ext_zvfh) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvfhmin), true); } if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) { @@ -554,11 +530,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - /* Set the ISA extensions, checks should have happened above */ - if (cpu->cfg.ext_zhinx) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); - } - if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) { error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx"); return; @@ -576,27 +547,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) } } - if (cpu->cfg.ext_zce) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true); - if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); - } - } - - /* zca, zcd and zcf has a PRIV 1.12.0 restriction */ - if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); - if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); - } - if (riscv_has_ext(env, RVD)) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcd), true); - } - } - if (mcc->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) { error_setg(errp, "Zcf extension is only relevant to RV32"); return; @@ -630,52 +580,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - /* - * Shorthand vector crypto extensions - */ - if (cpu->cfg.ext_zvknc) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkn), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); - } - - if (cpu->cfg.ext_zvkng) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkn), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkg), true); - } - - if (cpu->cfg.ext_zvkn) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkned), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvknhb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); - } - - if (cpu->cfg.ext_zvksc) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvks), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); - } - - if (cpu->cfg.ext_zvksg) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvks), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkg), true); - } - - if (cpu->cfg.ext_zvks) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvksed), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvksh), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); - } - - if (cpu->cfg.ext_zvkt) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); - } - - if (cpu->cfg.ext_zvbb) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); - } - if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned || cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32x) { @@ -691,29 +595,6 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (cpu->cfg.ext_zk) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkn), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkr), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkt), true); - } - - if (cpu->cfg.ext_zkn) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkne), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknd), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknh), true); - } - - if (cpu->cfg.ext_zks) { - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksed), true); - cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksh), true); - } - if (cpu->cfg.ext_zicntr && !cpu->cfg.ext_zicsr) { if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicntr))) { error_setg(errp, "zicntr requires zicsr"); From 5e20b88953b564145f586c052cf7a75dc77796b5 Mon Sep 17 00:00:00 2001 From: Alvin Chang Date: Wed, 26 Jun 2024 21:22:45 +0800 Subject: [PATCH 1679/2884] target/riscv: Add functions for common matching conditions of trigger According to RISC-V Debug specification version 0.13 [1] (also applied to version 1.0 [2] but it has not been ratified yet), there are several common matching conditions before firing a trigger, including the enabled privilege levels of the trigger. This commit adds trigger_common_match() to prepare the common matching conditions for the type 2/3/6 triggers. For now, we just implement trigger_priv_match() to check if the enabled privilege levels of the trigger match CPU's current privilege level. Remove the related code in riscv_cpu_debug_check_breakpoint() and invoke trigger_common_match() to check the privilege levels of the type 2 and type 6 triggers for the breakpoints. This commit also changes the behavior of looping the triggers. In previous implementation, if we have a type 2 trigger and env->virt_enabled is true, we directly return false to stop the loop. Now we keep looping all the triggers until we find a matched trigger. Only the execution bit and the executed PC should be futher checked in riscv_cpu_debug_check_breakpoint(). [1]: https://github.com/riscv/riscv-debug-spec/releases/tag/task_group_vote [2]: https://github.com/riscv/riscv-debug-spec/releases/tag/1.0.0-rc1-asciidoc Signed-off-by: Alvin Chang Reviewed-by: Alistair Francis Message-ID: <20240626132247.2761286-2-alvinga@andestech.com> Signed-off-by: Alistair Francis --- target/riscv/debug.c | 101 +++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 23 deletions(-) diff --git a/target/riscv/debug.c b/target/riscv/debug.c index b110370ea6..11125f333b 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -241,6 +241,76 @@ static void do_trigger_action(CPURISCVState *env, target_ulong trigger_index) } } +/* + * Check the privilege level of specific trigger matches CPU's current privilege + * level. + */ +static bool trigger_priv_match(CPURISCVState *env, trigger_type_t type, + int trigger_index) +{ + target_ulong ctrl = env->tdata1[trigger_index]; + + switch (type) { + case TRIGGER_TYPE_AD_MATCH: + /* type 2 trigger cannot be fired in VU/VS mode */ + if (env->virt_enabled) { + return false; + } + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + break; + case TRIGGER_TYPE_AD_MATCH6: + if (env->virt_enabled) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 23) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + break; + case TRIGGER_TYPE_INST_CNT: + if (env->virt_enabled) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 25) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 6) & BIT(env->priv)) { + return true; + } + } + break; + case TRIGGER_TYPE_INT: + case TRIGGER_TYPE_EXCP: + case TRIGGER_TYPE_EXT_SRC: + qemu_log_mask(LOG_UNIMP, "trigger type: %d is not supported\n", type); + break; + case TRIGGER_TYPE_NO_EXIST: + case TRIGGER_TYPE_UNAVAIL: + qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exist\n", + type); + break; + default: + g_assert_not_reached(); + } + + return false; +} + +/* Common matching conditions for all types of the triggers. */ +static bool trigger_common_match(CPURISCVState *env, trigger_type_t type, + int trigger_index) +{ + return trigger_priv_match(env, type, trigger_index); +} + /* type 2 trigger */ static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl) @@ -785,22 +855,18 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) for (i = 0; i < RV_MAX_TRIGGERS; i++) { trigger_type = get_trigger_type(env, i); + if (!trigger_common_match(env, trigger_type, i)) { + continue; + } + switch (trigger_type) { case TRIGGER_TYPE_AD_MATCH: - /* type 2 trigger cannot be fired in VU/VS mode */ - if (env->virt_enabled) { - return false; - } - ctrl = env->tdata1[i]; pc = env->tdata2[i]; if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - env->badaddr = pc; - return true; - } + env->badaddr = pc; + return true; } break; case TRIGGER_TYPE_AD_MATCH6: @@ -808,19 +874,8 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) pc = env->tdata2[i]; if ((ctrl & TYPE6_EXEC) && (bp->pc == pc)) { - if (env->virt_enabled) { - /* check VU/VS bit against current privilege level */ - if ((ctrl >> 23) & BIT(env->priv)) { - env->badaddr = pc; - return true; - } - } else { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - env->badaddr = pc; - return true; - } - } + env->badaddr = pc; + return true; } break; default: From 72dec1666fe92b5a5932d7f5c26bdfc72e238bcb Mon Sep 17 00:00:00 2001 From: Alvin Chang Date: Wed, 26 Jun 2024 21:22:46 +0800 Subject: [PATCH 1680/2884] target/riscv: Apply modularized matching conditions for watchpoint We have implemented trigger_common_match(), which checks if the enabled privilege levels of the trigger match CPU's current privilege level. Remove the related code in riscv_cpu_debug_check_watchpoint() and invoke trigger_common_match() to check the privilege levels of the type 2 and type 6 triggers for the watchpoints. This commit also changes the behavior of looping the triggers. In previous implementation, if we have a type 2 trigger and env->virt_enabled is true, we directly return false to stop the loop. Now we keep looping all the triggers until we find a matched trigger. Only load/store bits and loaded/stored address should be further checked in riscv_cpu_debug_check_watchpoint(). Signed-off-by: Alvin Chang Acked-by: Alistair Francis Message-ID: <20240626132247.2761286-3-alvinga@andestech.com> Signed-off-by: Alistair Francis --- target/riscv/debug.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/target/riscv/debug.c b/target/riscv/debug.c index 11125f333b..c290d6002e 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -901,13 +901,12 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) for (i = 0; i < RV_MAX_TRIGGERS; i++) { trigger_type = get_trigger_type(env, i); + if (!trigger_common_match(env, trigger_type, i)) { + continue; + } + switch (trigger_type) { case TRIGGER_TYPE_AD_MATCH: - /* type 2 trigger cannot be fired in VU/VS mode */ - if (env->virt_enabled) { - return false; - } - ctrl = env->tdata1[i]; addr = env->tdata2[i]; flags = 0; @@ -920,10 +919,7 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) } if ((wp->flags & flags) && (wp->vaddr == addr)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; - } + return true; } break; case TRIGGER_TYPE_AD_MATCH6: @@ -939,17 +935,7 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) } if ((wp->flags & flags) && (wp->vaddr == addr)) { - if (env->virt_enabled) { - /* check VU/VS bit against current privilege level */ - if ((ctrl >> 23) & BIT(env->priv)) { - return true; - } - } else { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; - } - } + return true; } break; default: From 2f5a2315b84a9b1f089ecfc3f31b29813609a7b7 Mon Sep 17 00:00:00 2001 From: Alvin Chang Date: Wed, 26 Jun 2024 21:22:47 +0800 Subject: [PATCH 1681/2884] target/riscv: Apply modularized matching conditions for icount trigger We have implemented trigger_common_match(), which checks if the enabled privilege levels of the trigger match CPU's current privilege level. We can invoke trigger_common_match() to check the privilege levels of the type 3 triggers. Signed-off-by: Alvin Chang Acked-by: Alistair Francis Message-ID: <20240626132247.2761286-4-alvinga@andestech.com> Signed-off-by: Alistair Francis --- target/riscv/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/debug.c b/target/riscv/debug.c index c290d6002e..0b5099ff9a 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -624,7 +624,7 @@ void helper_itrigger_match(CPURISCVState *env) if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { continue; } - if (check_itrigger_priv(env, i)) { + if (!trigger_common_match(env, TRIGGER_TYPE_INST_CNT, i)) { continue; } count = itrigger_get_count(env, i); From b1fbee456c8dcb5d53dd0324bde1e17ffc6bc5de Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 19 Jun 2024 13:45:49 +0200 Subject: [PATCH 1682/2884] configure: detect --cpu=mipsisa64r6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Treat it as a MIPS64 machine. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 5ad1674ca5..8b6a2f16ce 100755 --- a/configure +++ b/configure @@ -450,7 +450,7 @@ case "$cpu" in linux_arch=loongarch ;; - mips64*) + mips64*|mipsisa64*) cpu=mips64 host_arch=mips linux_arch=mips From fe721c1948ef459caab106190276717bec252c88 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Jun 2024 17:34:32 +0200 Subject: [PATCH 1683/2884] Revert "host/i386: assume presence of POPCNT" This reverts commit 45ccdbcb24baf99667997fac5cf60318e5e7db51. The x86-64 instruction set can now be tuned down to x86-64 v1 or i386 Pentium Pro. Signed-off-by: Paolo Bonzini --- host/include/i386/host/cpuinfo.h | 1 + tcg/i386/tcg-target.h | 5 +++-- util/cpuinfo-i386.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index c1e94d75ce..72f6fad61e 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -11,6 +11,7 @@ #define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ #define CPUINFO_MOVBE (1u << 2) #define CPUINFO_LZCNT (1u << 3) +#define CPUINFO_POPCNT (1u << 4) #define CPUINFO_BMI1 (1u << 5) #define CPUINFO_BMI2 (1u << 6) #define CPUINFO_AVX1 (1u << 9) diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index ecc6982728..2f67a97e05 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -111,6 +111,7 @@ typedef enum { #endif #define have_bmi1 (cpuinfo & CPUINFO_BMI1) +#define have_popcnt (cpuinfo & CPUINFO_POPCNT) #define have_avx1 (cpuinfo & CPUINFO_AVX1) #define have_avx2 (cpuinfo & CPUINFO_AVX2) #define have_movbe (cpuinfo & CPUINFO_MOVBE) @@ -142,7 +143,7 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 1 +#define TCG_TARGET_HAS_ctpop_i32 have_popcnt #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 1 @@ -177,7 +178,7 @@ typedef enum { #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 1 +#define TCG_TARGET_HAS_ctpop_i64 have_popcnt #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index 8f2694d88f..6d474a6259 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -35,6 +35,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) __cpuid(1, a, b, c, d); info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); + info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); /* NOTE: our AES support requires SSSE3 (PSHUFB) as well. */ From 39a367a42a3e77f56e9cc01d098298167df3fcc3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Jun 2024 17:34:45 +0200 Subject: [PATCH 1684/2884] Revert "host/i386: assume presence of SSSE3" This reverts commit 433cd6d94a8256af70a5200f236dc8047c3c1468. The x86-64 instruction set can now be tuned down to x86-64 v1 or i386 Pentium Pro. Signed-off-by: Paolo Bonzini --- util/cpuinfo-i386.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index 6d474a6259..ca74ef04f5 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -38,8 +38,8 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); - /* NOTE: our AES support requires SSSE3 (PSHUFB) as well. */ - info |= (c & bit_AES) ? CPUINFO_AES : 0; + /* Our AES support requires PSHUFB as well. */ + info |= ((c & bit_AES) && (c & bit_SSSE3) ? CPUINFO_AES : 0); /* For AVX features, we must check available and usable. */ if ((c & bit_AVX) && (c & bit_OSXSAVE)) { From 87b8bde55dc1700f212b2249b9c150714df67369 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Jun 2024 17:34:48 +0200 Subject: [PATCH 1685/2884] Revert "host/i386: assume presence of SSE2" This reverts commit b18236897ca15c3db1506d8edb9a191dfe51429c. The x86-64 instruction set can now be tuned down to x86-64 v1 or i386 Pentium Pro. Signed-off-by: Paolo Bonzini --- host/include/i386/host/bufferiszero.c.inc | 5 +++-- host/include/i386/host/cpuinfo.h | 1 + util/cpuinfo-i386.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/host/include/i386/host/bufferiszero.c.inc b/host/include/i386/host/bufferiszero.c.inc index 3b9605d806..74ae98580f 100644 --- a/host/include/i386/host/bufferiszero.c.inc +++ b/host/include/i386/host/bufferiszero.c.inc @@ -110,13 +110,14 @@ static biz_accel_fn const accel_table[] = { static unsigned best_accel(void) { -#ifdef CONFIG_AVX2_OPT unsigned info = cpuinfo_init(); + +#ifdef CONFIG_AVX2_OPT if (info & CPUINFO_AVX2) { return 2; } #endif - return 1; + return info & CPUINFO_SSE2 ? 1 : 0; } #else diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index 72f6fad61e..81771733ea 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -14,6 +14,7 @@ #define CPUINFO_POPCNT (1u << 4) #define CPUINFO_BMI1 (1u << 5) #define CPUINFO_BMI2 (1u << 6) +#define CPUINFO_SSE2 (1u << 7) #define CPUINFO_AVX1 (1u << 9) #define CPUINFO_AVX2 (1u << 10) #define CPUINFO_AVX512F (1u << 11) diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index ca74ef04f5..90f92a42dc 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -34,6 +34,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) if (max >= 1) { __cpuid(1, a, b, c, d); + info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0); info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); From ef7d1adfa8589bb7d6cb06463bf554877e086beb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Jun 2024 17:32:52 +0200 Subject: [PATCH 1686/2884] meson: allow configuring the x86-64 baseline Add a Meson option to configure which x86-64 instruction set to use. QEMU will now default to x86-64-v1 + cmpxchg16b for 64-bit builds (that corresponds to a Pentium 4 for 32-bit builds). The baseline can be tuned down to Pentium Pro for 32-bit builds (with -Dx86_version=0), or up as desired. Acked-by: Richard Henderson Signed-off-by: Paolo Bonzini --- meson.build | 41 ++++++++++++++++++++++++++++------- meson_options.txt | 3 +++ scripts/meson-buildoptions.sh | 3 +++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index 97e00d6f59..6e694ecd9f 100644 --- a/meson.build +++ b/meson.build @@ -336,15 +336,40 @@ if host_arch == 'i386' and not cc.links(''' qemu_common_flags = ['-march=i486'] + qemu_common_flags endif -# Assume x86-64-v2 (minus CMPXCHG16B for 32-bit code) -if host_arch == 'i386' - qemu_common_flags = ['-mfpmath=sse'] + qemu_common_flags -endif +# Pick x86-64 baseline version if host_arch in ['i386', 'x86_64'] - qemu_common_flags = ['-mpopcnt', '-msse4.2'] + qemu_common_flags -endif -if host_arch == 'x86_64' - qemu_common_flags = ['-mcx16'] + qemu_common_flags + if get_option('x86_version') == '0' and host_arch == 'x86_64' + error('x86_64-v1 required for x86-64 hosts') + endif + + # add flags for individual instruction set extensions + if get_option('x86_version') >= '1' + if host_arch == 'i386' + qemu_common_flags = ['-mfpmath=sse'] + qemu_common_flags + else + # present on basically all processors but technically not part of + # x86-64-v1, so only include -mneeded for x86-64 version 2 and above + qemu_common_flags = ['-mcx16'] + qemu_common_flags + endif + endif + if get_option('x86_version') >= '2' + qemu_common_flags = ['-mpopcnt'] + qemu_common_flags + qemu_common_flags = cc.get_supported_arguments('-mneeded') + qemu_common_flags + endif + if get_option('x86_version') >= '3' + qemu_common_flags = ['-mmovbe', '-mabm', '-mbmi1', '-mbmi2', '-mfma', '-mf16c'] + qemu_common_flags + endif + + # add required vector instruction set (each level implies those below) + if get_option('x86_version') == '1' + qemu_common_flags = ['-msse2'] + qemu_common_flags + elif get_option('x86_version') == '2' + qemu_common_flags = ['-msse4.2'] + qemu_common_flags + elif get_option('x86_version') == '3' + qemu_common_flags = ['-mavx2'] + qemu_common_flags + elif get_option('x86_version') == '4' + qemu_common_flags = ['-mavx512f', '-mavx512bw', '-mavx512cd', '-mavx512dq', '-mavx512vl'] + qemu_common_flags + endif endif if get_option('prefer_static') diff --git a/meson_options.txt b/meson_options.txt index 7a79dd8970..6065ed2d35 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -370,3 +370,6 @@ option('qemu_ga_version', type: 'string', value: '', option('hexagon_idef_parser', type : 'boolean', value : true, description: 'use idef-parser to automatically generate TCG code for the Hexagon frontend') + +option('x86_version', type : 'combo', choices : ['0', '1', '2', '3', '4'], value: '1', + description: 'tweak required x86_64 architecture version beyond compiler default') diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 58d49a447d..62842d47e8 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -82,6 +82,8 @@ meson_options_help() { printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories' printf "%s\n" ' (can be empty) [qemu]' printf "%s\n" ' --with-trace-file=VALUE Trace file prefix for simple backend [trace]' + printf "%s\n" ' --x86-version=CHOICE tweak required x86_64 architecture version beyond' + printf "%s\n" ' compiler default [1] (choices: 0/1/2/3)' printf "%s\n" '' printf "%s\n" 'Optional features, enabled with --enable-FEATURE and' printf "%s\n" 'disabled with --disable-FEATURE, default is enabled if available' @@ -552,6 +554,7 @@ _meson_option_parse() { --disable-werror) printf "%s" -Dwerror=false ;; --enable-whpx) printf "%s" -Dwhpx=enabled ;; --disable-whpx) printf "%s" -Dwhpx=disabled ;; + --x86-version=*) quote_sh "-Dx86_version=$2" ;; --enable-xen) printf "%s" -Dxen=enabled ;; --disable-xen) printf "%s" -Dxen=disabled ;; --enable-xen-pci-passthrough) printf "%s" -Dxen_pci_passthrough=enabled ;; From b3f1ce8a472e7239b196bcb3aaa38738012f23b5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Jun 2024 14:57:30 +0200 Subject: [PATCH 1687/2884] meson: remove dead optimization option Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- meson.build | 13 ------------- meson_options.txt | 2 -- scripts/meson-buildoptions.sh | 3 --- 3 files changed, 18 deletions(-) diff --git a/meson.build b/meson.build index 6e694ecd9f..54e6b09f4f 100644 --- a/meson.build +++ b/meson.build @@ -2874,18 +2874,6 @@ config_host_data.set('CONFIG_AVX2_OPT', get_option('avx2') \ int main(int argc, char *argv[]) { return bar(argv[argc - 1]); } '''), error_message: 'AVX2 not available').allowed()) -config_host_data.set('CONFIG_AVX512F_OPT', get_option('avx512f') \ - .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX512F') \ - .require(cc.links(''' - #include - #include - static int __attribute__((target("avx512f"))) bar(void *a) { - __m512i x = *(__m512i *)a; - return _mm512_test_epi64_mask(x, x); - } - int main(int argc, char *argv[]) { return bar(argv[argc - 1]); } - '''), error_message: 'AVX512F not available').allowed()) - config_host_data.set('CONFIG_AVX512BW_OPT', get_option('avx512bw') \ .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX512BW') \ .require(cc.links(''' @@ -4283,7 +4271,6 @@ summary_info += {'mutex debugging': get_option('debug_mutex')} summary_info += {'memory allocator': get_option('malloc')} summary_info += {'avx2 optimization': config_host_data.get('CONFIG_AVX2_OPT')} summary_info += {'avx512bw optimization': config_host_data.get('CONFIG_AVX512BW_OPT')} -summary_info += {'avx512f optimization': config_host_data.get('CONFIG_AVX512F_OPT')} summary_info += {'gcov': get_option('b_coverage')} summary_info += {'thread sanitizer': get_option('tsan')} summary_info += {'CFI support': get_option('cfi')} diff --git a/meson_options.txt b/meson_options.txt index 6065ed2d35..0269fa0f16 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -119,8 +119,6 @@ option('membarrier', type: 'feature', value: 'disabled', option('avx2', type: 'feature', value: 'auto', description: 'AVX2 optimizations') -option('avx512f', type: 'feature', value: 'disabled', - description: 'AVX512F optimizations') option('avx512bw', type: 'feature', value: 'auto', description: 'AVX512BW optimizations') option('keyring', type: 'feature', value: 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 62842d47e8..cfadb5ea86 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -95,7 +95,6 @@ meson_options_help() { printf "%s\n" ' auth-pam PAM access control' printf "%s\n" ' avx2 AVX2 optimizations' printf "%s\n" ' avx512bw AVX512BW optimizations' - printf "%s\n" ' avx512f AVX512F optimizations' printf "%s\n" ' blkio libblkio block device driver' printf "%s\n" ' bochs bochs image format support' printf "%s\n" ' bpf eBPF support' @@ -240,8 +239,6 @@ _meson_option_parse() { --disable-avx2) printf "%s" -Davx2=disabled ;; --enable-avx512bw) printf "%s" -Davx512bw=enabled ;; --disable-avx512bw) printf "%s" -Davx512bw=disabled ;; - --enable-avx512f) printf "%s" -Davx512f=enabled ;; - --disable-avx512f) printf "%s" -Davx512f=disabled ;; --enable-gcov) printf "%s" -Db_coverage=true ;; --disable-gcov) printf "%s" -Db_coverage=false ;; --enable-lto) printf "%s" -Db_lto=true ;; From ae8b45d29317be0bcc60d40e5d627f978b27ccd0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 24 Nov 2022 16:29:06 +0100 Subject: [PATCH 1688/2884] block: make assertion more generic .bdrv_needs_filename is only set for drivers that also set bdrv_file_open, i.e. protocol drivers. So we can make the assertion always, it will always pass for those drivers that use bdrv_open. Signed-off-by: Paolo Bonzini --- block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block.c b/block.c index 468cf5e67d..69a2905178 100644 --- a/block.c +++ b/block.c @@ -1655,8 +1655,8 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, bs->drv = drv; bs->opaque = g_malloc0(drv->instance_size); + assert(!drv->bdrv_needs_filename || bs->filename[0]); if (drv->bdrv_file_open) { - assert(!drv->bdrv_needs_filename || bs->filename[0]); ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); } else if (drv->bdrv_open) { ret = drv->bdrv_open(bs, options, open_flags, &local_err); From 41770f6e6ff41ca98e130c79b566f05ec68912fe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 24 Nov 2022 16:21:18 +0100 Subject: [PATCH 1689/2884] block: do not check bdrv_file_open The set of BlockDrivers that have .bdrv_file_open coincides with those that have .protocol_name and guess what---checking drv->bdrv_file_open is done to see if the driver is a protocol. So check drv->protocol_name instead. Signed-off-by: Paolo Bonzini --- block.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index 69a2905178..dd14ba85fc 100644 --- a/block.c +++ b/block.c @@ -926,7 +926,6 @@ BlockDriver *bdrv_find_protocol(const char *filename, int i; GLOBAL_STATE_CODE(); - /* TODO Drivers without bdrv_file_open must be specified explicitly */ /* * XXX(hch): we really should not let host device detection @@ -1983,7 +1982,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, open_flags = bdrv_open_flags(bs, bs->open_flags); node_name = qemu_opt_get(opts, "node-name"); - assert(!drv->bdrv_file_open || file == NULL); + assert(!drv->protocol_name || file == NULL); ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp); if (ret < 0) { goto fail_opts; @@ -2084,7 +2083,7 @@ static int bdrv_fill_options(QDict **options, const char *filename, } /* If the user has explicitly specified the driver, this choice should * override the BDRV_O_PROTOCOL flag */ - protocol = drv->bdrv_file_open; + protocol = drv->protocol_name; } if (protocol) { @@ -4123,7 +4122,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, } /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */ - assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open); + assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->protocol_name); /* file must be NULL if a protocol BDS is about to be created * (the inverse results in an error message from bdrv_open_common()) */ assert(!(flags & BDRV_O_PROTOCOL) || !file); @@ -5971,7 +5970,7 @@ int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs) return drv->bdrv_co_get_allocated_file_size(bs); } - if (drv->bdrv_file_open) { + if (drv->protocol_name) { /* * Protocol drivers default to -ENOTSUP (most of their data is * not stored in any of their children (if they even have any), @@ -8030,7 +8029,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) * Both of these conditions are represented by generate_json_filename. */ if (primary_child_bs->exact_filename[0] && - primary_child_bs->drv->bdrv_file_open && + primary_child_bs->drv->protocol_name && !drv->is_filter && !generate_json_filename) { strcpy(bs->exact_filename, primary_child_bs->exact_filename); From 44b424dc4a3e2d47fa20676f00645fb950d8d76a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 24 Nov 2022 16:22:22 +0100 Subject: [PATCH 1690/2884] block: remove separate bdrv_file_open callback bdrv_file_open and bdrv_open are completely equivalent, they are never checked except to see which one to invoke. So merge them into a single one. Signed-off-by: Paolo Bonzini --- block.c | 4 +--- block/blkdebug.c | 2 +- block/blkio.c | 2 +- block/blkverify.c | 2 +- block/curl.c | 8 ++++---- block/file-posix.c | 8 ++++---- block/file-win32.c | 4 ++-- block/gluster.c | 6 +++--- block/iscsi.c | 4 ++-- block/nbd.c | 6 +++--- block/nfs.c | 2 +- block/null.c | 4 ++-- block/nvme.c | 2 +- block/rbd.c | 3 ++- block/ssh.c | 2 +- block/vvfat.c | 2 +- include/block/block_int-common.h | 3 --- 17 files changed, 30 insertions(+), 34 deletions(-) diff --git a/block.c b/block.c index dd14ba85fc..c1cc313d21 100644 --- a/block.c +++ b/block.c @@ -1655,9 +1655,7 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, bs->opaque = g_malloc0(drv->instance_size); assert(!drv->bdrv_needs_filename || bs->filename[0]); - if (drv->bdrv_file_open) { - ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); - } else if (drv->bdrv_open) { + if (drv->bdrv_open) { ret = drv->bdrv_open(bs, options, open_flags, &local_err); } else { ret = 0; diff --git a/block/blkdebug.c b/block/blkdebug.c index 9da8c9eddc..c95c818c38 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -1073,7 +1073,7 @@ static BlockDriver bdrv_blkdebug = { .is_filter = true, .bdrv_parse_filename = blkdebug_parse_filename, - .bdrv_file_open = blkdebug_open, + .bdrv_open = blkdebug_open, .bdrv_close = blkdebug_close, .bdrv_reopen_prepare = blkdebug_reopen_prepare, .bdrv_child_perm = blkdebug_child_perm, diff --git a/block/blkio.c b/block/blkio.c index 882e1c297b..1a38064ce7 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -1088,7 +1088,7 @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) */ #define BLKIO_DRIVER_COMMON \ .instance_size = sizeof(BDRVBlkioState), \ - .bdrv_file_open = blkio_file_open, \ + .bdrv_open = blkio_file_open, \ .bdrv_close = blkio_close, \ .bdrv_co_getlength = blkio_co_getlength, \ .bdrv_co_truncate = blkio_truncate, \ diff --git a/block/blkverify.c b/block/blkverify.c index ec45d8335e..5a9bf674d9 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -321,7 +321,7 @@ static BlockDriver bdrv_blkverify = { .instance_size = sizeof(BDRVBlkverifyState), .bdrv_parse_filename = blkverify_parse_filename, - .bdrv_file_open = blkverify_open, + .bdrv_open = blkverify_open, .bdrv_close = blkverify_close, .bdrv_child_perm = bdrv_default_perms, .bdrv_co_getlength = blkverify_co_getlength, diff --git a/block/curl.c b/block/curl.c index 419f7c89ef..ef5252d00b 100644 --- a/block/curl.c +++ b/block/curl.c @@ -1034,7 +1034,7 @@ static BlockDriver bdrv_http = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, .bdrv_co_getlength = curl_co_getlength, @@ -1053,7 +1053,7 @@ static BlockDriver bdrv_https = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, .bdrv_co_getlength = curl_co_getlength, @@ -1072,7 +1072,7 @@ static BlockDriver bdrv_ftp = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, .bdrv_co_getlength = curl_co_getlength, @@ -1091,7 +1091,7 @@ static BlockDriver bdrv_ftps = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, .bdrv_co_getlength = curl_co_getlength, diff --git a/block/file-posix.c b/block/file-posix.c index be25e35ff6..f3bd946afa 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3886,7 +3886,7 @@ BlockDriver bdrv_file = { .bdrv_needs_filename = true, .bdrv_probe = NULL, /* no probe for protocols */ .bdrv_parse_filename = raw_parse_filename, - .bdrv_file_open = raw_open, + .bdrv_open = raw_open, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, @@ -4257,7 +4257,7 @@ static BlockDriver bdrv_host_device = { .bdrv_needs_filename = true, .bdrv_probe_device = hdev_probe_device, .bdrv_parse_filename = hdev_parse_filename, - .bdrv_file_open = hdev_open, + .bdrv_open = hdev_open, .bdrv_close = raw_close, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, @@ -4396,7 +4396,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_parse_filename = cdrom_parse_filename, - .bdrv_file_open = cdrom_open, + .bdrv_open = cdrom_open, .bdrv_close = raw_close, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, @@ -4522,7 +4522,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_parse_filename = cdrom_parse_filename, - .bdrv_file_open = cdrom_open, + .bdrv_open = cdrom_open, .bdrv_close = raw_close, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, diff --git a/block/file-win32.c b/block/file-win32.c index 48b790d917..7e1baa1ece 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -746,7 +746,7 @@ BlockDriver bdrv_file = { .instance_size = sizeof(BDRVRawState), .bdrv_needs_filename = true, .bdrv_parse_filename = raw_parse_filename, - .bdrv_file_open = raw_open, + .bdrv_open = raw_open, .bdrv_refresh_limits = raw_probe_alignment, .bdrv_close = raw_close, .bdrv_co_create_opts = raw_co_create_opts, @@ -920,7 +920,7 @@ static BlockDriver bdrv_host_device = { .bdrv_needs_filename = true, .bdrv_parse_filename = hdev_parse_filename, .bdrv_probe_device = hdev_probe_device, - .bdrv_file_open = hdev_open, + .bdrv_open = hdev_open, .bdrv_close = raw_close, .bdrv_refresh_limits = hdev_refresh_limits, diff --git a/block/gluster.c b/block/gluster.c index d0999903df..f8b415f381 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1551,7 +1551,7 @@ static BlockDriver bdrv_gluster = { .format_name = "gluster", .protocol_name = "gluster", .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, + .bdrv_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, @@ -1580,7 +1580,7 @@ static BlockDriver bdrv_gluster_tcp = { .format_name = "gluster", .protocol_name = "gluster+tcp", .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, + .bdrv_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, @@ -1609,7 +1609,7 @@ static BlockDriver bdrv_gluster_unix = { .format_name = "gluster", .protocol_name = "gluster+unix", .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, + .bdrv_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, diff --git a/block/iscsi.c b/block/iscsi.c index 2ff14b7472..979bf90cb7 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2429,7 +2429,7 @@ static BlockDriver bdrv_iscsi = { .instance_size = sizeof(IscsiLun), .bdrv_parse_filename = iscsi_parse_filename, - .bdrv_file_open = iscsi_open, + .bdrv_open = iscsi_open, .bdrv_close = iscsi_close, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, @@ -2468,7 +2468,7 @@ static BlockDriver bdrv_iser = { .instance_size = sizeof(IscsiLun), .bdrv_parse_filename = iscsi_parse_filename, - .bdrv_file_open = iscsi_open, + .bdrv_open = iscsi_open, .bdrv_close = iscsi_close, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, diff --git a/block/nbd.c b/block/nbd.c index 589d28af83..d464315766 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -2146,7 +2146,7 @@ static BlockDriver bdrv_nbd = { .bdrv_parse_filename = nbd_parse_filename, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, - .bdrv_file_open = nbd_open, + .bdrv_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .bdrv_co_pwritev = nbd_client_co_pwritev, @@ -2174,7 +2174,7 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_parse_filename = nbd_parse_filename, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, - .bdrv_file_open = nbd_open, + .bdrv_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .bdrv_co_pwritev = nbd_client_co_pwritev, @@ -2202,7 +2202,7 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_parse_filename = nbd_parse_filename, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, - .bdrv_file_open = nbd_open, + .bdrv_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .bdrv_co_pwritev = nbd_client_co_pwritev, diff --git a/block/nfs.c b/block/nfs.c index 60240a8733..0500f60c08 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -888,7 +888,7 @@ static BlockDriver bdrv_nfs = { #endif .bdrv_co_truncate = nfs_file_co_truncate, - .bdrv_file_open = nfs_file_open, + .bdrv_open = nfs_file_open, .bdrv_close = nfs_file_close, .bdrv_co_create = nfs_file_co_create, .bdrv_co_create_opts = nfs_file_co_create_opts, diff --git a/block/null.c b/block/null.c index 4808704ffd..6fa64d20d8 100644 --- a/block/null.c +++ b/block/null.c @@ -283,7 +283,7 @@ static BlockDriver bdrv_null_co = { .protocol_name = "null-co", .instance_size = sizeof(BDRVNullState), - .bdrv_file_open = null_file_open, + .bdrv_open = null_file_open, .bdrv_parse_filename = null_co_parse_filename, .bdrv_co_getlength = null_co_getlength, .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, @@ -304,7 +304,7 @@ static BlockDriver bdrv_null_aio = { .protocol_name = "null-aio", .instance_size = sizeof(BDRVNullState), - .bdrv_file_open = null_file_open, + .bdrv_open = null_file_open, .bdrv_parse_filename = null_aio_parse_filename, .bdrv_co_getlength = null_co_getlength, .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, diff --git a/block/nvme.c b/block/nvme.c index 3a3c6da73d..c84914af6d 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1630,7 +1630,7 @@ static BlockDriver bdrv_nvme = { .create_opts = &bdrv_create_opts_simple, .bdrv_parse_filename = nvme_parse_filename, - .bdrv_file_open = nvme_file_open, + .bdrv_open = nvme_file_open, .bdrv_close = nvme_close, .bdrv_co_getlength = nvme_co_getlength, .bdrv_probe_blocksizes = nvme_probe_blocksizes, diff --git a/block/rbd.c b/block/rbd.c index 84bb2fa5d7..9c0fd0cb3f 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1815,8 +1815,9 @@ static const char *const qemu_rbd_strong_runtime_opts[] = { static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), + .bdrv_parse_filename = qemu_rbd_parse_filename, - .bdrv_file_open = qemu_rbd_open, + .bdrv_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, .bdrv_co_create = qemu_rbd_co_create, diff --git a/block/ssh.c b/block/ssh.c index a88171d4b5..1344822ed8 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -1362,7 +1362,7 @@ static BlockDriver bdrv_ssh = { .protocol_name = "ssh", .instance_size = sizeof(BDRVSSHState), .bdrv_parse_filename = ssh_parse_filename, - .bdrv_file_open = ssh_file_open, + .bdrv_open = ssh_file_open, .bdrv_co_create = ssh_co_create, .bdrv_co_create_opts = ssh_co_create_opts, .bdrv_close = ssh_close, diff --git a/block/vvfat.c b/block/vvfat.c index 9d050ba3ae..086fedf474 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3258,7 +3258,7 @@ static BlockDriver bdrv_vvfat = { .instance_size = sizeof(BDRVVVFATState), .bdrv_parse_filename = vvfat_parse_filename, - .bdrv_file_open = vvfat_open, + .bdrv_open = vvfat_open, .bdrv_refresh_limits = vvfat_refresh_limits, .bdrv_close = vvfat_close, .bdrv_child_perm = vvfat_child_perm, diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 761276127e..ebb4e56a50 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -248,9 +248,6 @@ struct BlockDriver { int GRAPH_UNLOCKED_PTR (*bdrv_open)( BlockDriverState *bs, QDict *options, int flags, Error **errp); - /* Protocol drivers should implement this instead of bdrv_open */ - int GRAPH_UNLOCKED_PTR (*bdrv_file_open)( - BlockDriverState *bs, QDict *options, int flags, Error **errp); void (*bdrv_close)(BlockDriverState *bs); int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)( From d656aaa1369adcd20baea485d4a96a4bfd6b1c86 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Sep 2023 12:07:19 +0200 Subject: [PATCH 1691/2884] block: rename former bdrv_file_open callbacks Since there is no bdrv_file_open callback anymore, rename the implementations so that they end with "_open" instead of "_file_open". NFS is the exception because all the functions are named nfs_file_*. Suggested-by: Kevin Wolf Signed-off-by: Paolo Bonzini --- block/blkio.c | 8 ++++---- block/null.c | 8 ++++---- block/nvme.c | 8 ++++---- block/ssh.c | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/block/blkio.c b/block/blkio.c index 1a38064ce7..3d9a2e764c 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -713,7 +713,7 @@ static int blkio_virtio_blk_connect(BlockDriverState *bs, QDict *options, * for example will fail. * * In order to open the device read-only, we are using the `read-only` - * property of the libblkio driver in blkio_file_open(). + * property of the libblkio driver in blkio_open(). */ fd = qemu_open(path, O_RDWR, NULL); if (fd < 0) { @@ -791,8 +791,8 @@ static int blkio_virtio_blk_connect(BlockDriverState *bs, QDict *options, return 0; } -static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int blkio_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { const char *blkio_driver = bs->drv->protocol_name; BDRVBlkioState *s = bs->opaque; @@ -1088,7 +1088,7 @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) */ #define BLKIO_DRIVER_COMMON \ .instance_size = sizeof(BDRVBlkioState), \ - .bdrv_open = blkio_file_open, \ + .bdrv_open = blkio_open, \ .bdrv_close = blkio_close, \ .bdrv_co_getlength = blkio_co_getlength, \ .bdrv_co_truncate = blkio_truncate, \ diff --git a/block/null.c b/block/null.c index 6fa64d20d8..4730acc1eb 100644 --- a/block/null.c +++ b/block/null.c @@ -77,8 +77,8 @@ static void null_aio_parse_filename(const char *filename, QDict *options, } } -static int null_file_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int null_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { QemuOpts *opts; BDRVNullState *s = bs->opaque; @@ -283,7 +283,7 @@ static BlockDriver bdrv_null_co = { .protocol_name = "null-co", .instance_size = sizeof(BDRVNullState), - .bdrv_open = null_file_open, + .bdrv_open = null_open, .bdrv_parse_filename = null_co_parse_filename, .bdrv_co_getlength = null_co_getlength, .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, @@ -304,7 +304,7 @@ static BlockDriver bdrv_null_aio = { .protocol_name = "null-aio", .instance_size = sizeof(BDRVNullState), - .bdrv_open = null_file_open, + .bdrv_open = null_open, .bdrv_parse_filename = null_aio_parse_filename, .bdrv_co_getlength = null_co_getlength, .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, diff --git a/block/nvme.c b/block/nvme.c index c84914af6d..3b588b139f 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -889,7 +889,7 @@ out: qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)regs, 0, sizeof(NvmeBar)); } - /* Cleaning up is done in nvme_file_open() upon error. */ + /* Cleaning up is done in nvme_open() upon error. */ return ret; } @@ -967,8 +967,8 @@ static void nvme_close(BlockDriverState *bs) g_free(s->device); } -static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int nvme_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { const char *device; QemuOpts *opts; @@ -1630,7 +1630,7 @@ static BlockDriver bdrv_nvme = { .create_opts = &bdrv_create_opts_simple, .bdrv_parse_filename = nvme_parse_filename, - .bdrv_open = nvme_file_open, + .bdrv_open = nvme_open, .bdrv_close = nvme_close, .bdrv_co_getlength = nvme_co_getlength, .bdrv_probe_blocksizes = nvme_probe_blocksizes, diff --git a/block/ssh.c b/block/ssh.c index 1344822ed8..27d582e0e3 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -837,8 +837,8 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, return ret; } -static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, - Error **errp) +static int ssh_open(BlockDriverState *bs, QDict *options, int bdrv_flags, + Error **errp) { BDRVSSHState *s = bs->opaque; BlockdevOptionsSsh *opts; @@ -1362,7 +1362,7 @@ static BlockDriver bdrv_ssh = { .protocol_name = "ssh", .instance_size = sizeof(BDRVSSHState), .bdrv_parse_filename = ssh_parse_filename, - .bdrv_open = ssh_file_open, + .bdrv_open = ssh_open, .bdrv_co_create = ssh_co_create, .bdrv_co_create_opts = ssh_co_create_opts, .bdrv_close = ssh_close, From 17c7df806b39d512271749ffdfc0376473744d63 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Jun 2024 15:45:53 -0700 Subject: [PATCH 1692/2884] exec: avoid using C++ keywords in function parameters to use the QEMU headers with a C++ compiler. Signed-off-by: Roman Kiryanov Link: https://lore.kernel.org/r/20240618224553.878869-1-rkir@google.com Signed-off-by: Paolo Bonzini --- include/exec/memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 0903513d13..154626f9ad 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -925,7 +925,7 @@ struct MemoryListener { * the current transaction. */ void (*log_start)(MemoryListener *listener, MemoryRegionSection *section, - int old, int new); + int old_val, int new_val); /** * @log_stop: @@ -944,7 +944,7 @@ struct MemoryListener { * the current transaction. */ void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section, - int old, int new); + int old_val, int new_val); /** * @log_sync: From 7246c4cc470409bc77ae607463d1fcd026149d6a Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Thu, 20 Jun 2024 13:16:54 -0700 Subject: [PATCH 1693/2884] exec: don't use void* in pointer arithmetic in headers void* pointer arithmetic is a GCC extentension which could not be available in other build tools (e.g. C++). This changes removes this assumption. Signed-off-by: Roman Kiryanov Suggested-by: Paolo Bonzini Link: https://lore.kernel.org/r/20240620201654.598024-1-rkir@google.com Signed-off-by: Paolo Bonzini --- include/exec/memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 154626f9ad..c26ede33d2 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2764,7 +2764,7 @@ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, #include "exec/memory_ldst_phys.h.inc" struct MemoryRegionCache { - void *ptr; + uint8_t *ptr; hwaddr xlat; hwaddr len; FlatView *fv; From eb350d1d01d9b9df0ce174e2e1681699b071bab3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Jun 2024 13:12:20 +0200 Subject: [PATCH 1694/2884] include: move typeof_strip_qual to compiler.h, use it in QAPI_LIST_LENGTH() The typeof_strip_qual() is most useful for the atomic fetch-and-modify operations in atomic.h, but it can be used elsewhere as well. For example, QAPI_LIST_LENGTH() assumes that the argument is not const, which is not a requirement. Move the macro to compiler.h and, while at it, move it under #ifndef __cplusplus to emphasize that it uses C-only constructs. A C++ version of typeof_strip_qual() using type traits is possible[1], but beyond the scope of this patch because the little C++ code that is in QEMU does not use QAPI. The patch was tested by changing the declaration of strv_from_str_list() in qapi/qapi-type-helpers.c to: char **strv_from_str_list(const strList *const list) This is valid C code, and it fails to compile without this change. [1] https://lore.kernel.org/qemu-devel/20240624205647.112034-1-flwu@google.com/ Reviewed-by: Richard Henderson Reviewed-by: Manos Pitsidianakis Tested-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- include/qapi/util.h | 2 +- include/qemu/atomic.h | 42 ------------------------------------- include/qemu/compiler.h | 46 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/include/qapi/util.h b/include/qapi/util.h index 20dfea8a54..b8254247b8 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -62,7 +62,7 @@ int parse_qapi_name(const char *name, bool complete); #define QAPI_LIST_LENGTH(list) \ ({ \ size_t _len = 0; \ - typeof(list) _tail; \ + typeof_strip_qual(list) _tail; \ for (_tail = list; _tail != NULL; _tail = _tail->next) { \ _len++; \ } \ diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 99110abefb..dc4118ddd9 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -20,48 +20,6 @@ /* Compiler barrier */ #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) -/* The variable that receives the old value of an atomically-accessed - * variable must be non-qualified, because atomic builtins return values - * through a pointer-type argument as in __atomic_load(&var, &old, MODEL). - * - * This macro has to handle types smaller than int manually, because of - * implicit promotion. int and larger types, as well as pointers, can be - * converted to a non-qualified type just by applying a binary operator. - */ -#define typeof_strip_qual(expr) \ - typeof( \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), bool) || \ - __builtin_types_compatible_p(typeof(expr), const bool) || \ - __builtin_types_compatible_p(typeof(expr), volatile bool) || \ - __builtin_types_compatible_p(typeof(expr), const volatile bool), \ - (bool)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), signed char) || \ - __builtin_types_compatible_p(typeof(expr), const signed char) || \ - __builtin_types_compatible_p(typeof(expr), volatile signed char) || \ - __builtin_types_compatible_p(typeof(expr), const volatile signed char), \ - (signed char)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), unsigned char) || \ - __builtin_types_compatible_p(typeof(expr), const unsigned char) || \ - __builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \ - __builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \ - (unsigned char)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), signed short) || \ - __builtin_types_compatible_p(typeof(expr), const signed short) || \ - __builtin_types_compatible_p(typeof(expr), volatile signed short) || \ - __builtin_types_compatible_p(typeof(expr), const volatile signed short), \ - (signed short)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), unsigned short) || \ - __builtin_types_compatible_p(typeof(expr), const unsigned short) || \ - __builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \ - __builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \ - (unsigned short)1, \ - (expr)+0)))))) - #ifndef __ATOMIC_RELAXED #error "Expecting C11 atomic ops" #endif diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index c797f0d457..554c5ce7df 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -227,4 +227,50 @@ #define SECOND_ARG(first, second, ...) second #define IS_EMPTY_(junk_maybecomma) SECOND_ARG(junk_maybecomma 1, 0) +#ifndef __cplusplus +/* + * Useful in macros that need to declare temporary variables. For example, + * the variable that receives the old value of an atomically-accessed + * variable must be non-qualified, because atomic builtins return values + * through a pointer-type argument as in __atomic_load(&var, &old, MODEL). + * + * This macro has to handle types smaller than int manually, because of + * implicit promotion. int and larger types, as well as pointers, can be + * converted to a non-qualified type just by applying a binary operator. + */ +#define typeof_strip_qual(expr) \ + typeof( \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), bool) || \ + __builtin_types_compatible_p(typeof(expr), const bool) || \ + __builtin_types_compatible_p(typeof(expr), volatile bool) || \ + __builtin_types_compatible_p(typeof(expr), const volatile bool), \ + (bool)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), signed char) || \ + __builtin_types_compatible_p(typeof(expr), const signed char) || \ + __builtin_types_compatible_p(typeof(expr), volatile signed char) || \ + __builtin_types_compatible_p(typeof(expr), const volatile signed char), \ + (signed char)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), unsigned char) || \ + __builtin_types_compatible_p(typeof(expr), const unsigned char) || \ + __builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \ + __builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \ + (unsigned char)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), signed short) || \ + __builtin_types_compatible_p(typeof(expr), const signed short) || \ + __builtin_types_compatible_p(typeof(expr), volatile signed short) || \ + __builtin_types_compatible_p(typeof(expr), const volatile signed short), \ + (signed short)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), unsigned short) || \ + __builtin_types_compatible_p(typeof(expr), const unsigned short) || \ + __builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \ + __builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \ + (unsigned short)1, \ + (expr)+0)))))) +#endif + #endif /* COMPILER_H */ From e36b976da4f6f4c0d434e6bb811f60b7b445e8ea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Jun 2024 19:46:07 +0200 Subject: [PATCH 1695/2884] target/i386: fix CC_OP dump POPCNT was missing, and the entries were all out of order after ADCX/ADOX/ADCOX were moved close to EFLAGS. Just use designated initializers. Fixes: 4885c3c4953 ("target-i386: Use ctpop helper", 2017-01-10) Fixes: cc155f19717 ("target/i386: rewrite flags writeback for ADCX/ADOX", 2024-06-11) Signed-off-by: Paolo Bonzini --- target/i386/cpu-dump.c | 101 +++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c index 40697064d9..3bb8e44091 100644 --- a/target/i386/cpu-dump.c +++ b/target/i386/cpu-dump.c @@ -28,69 +28,70 @@ /* x86 debug */ static const char *cc_op_str[CC_OP_NB] = { - "DYNAMIC", - "EFLAGS", + [CC_OP_DYNAMIC] = "DYNAMIC", - "MULB", - "MULW", - "MULL", - "MULQ", + [CC_OP_EFLAGS] = "EFLAGS", + [CC_OP_ADCX] = "ADCX", + [CC_OP_ADOX] = "ADOX", + [CC_OP_ADCOX] = "ADCOX", - "ADDB", - "ADDW", - "ADDL", - "ADDQ", + [CC_OP_MULB] = "MULB", + [CC_OP_MULW] = "MULW", + [CC_OP_MULL] = "MULL", + [CC_OP_MULQ] = "MULQ", - "ADCB", - "ADCW", - "ADCL", - "ADCQ", + [CC_OP_ADDB] = "ADDB", + [CC_OP_ADDW] = "ADDW", + [CC_OP_ADDL] = "ADDL", + [CC_OP_ADDQ] = "ADDQ", - "SUBB", - "SUBW", - "SUBL", - "SUBQ", + [CC_OP_ADCB] = "ADCB", + [CC_OP_ADCW] = "ADCW", + [CC_OP_ADCL] = "ADCL", + [CC_OP_ADCQ] = "ADCQ", - "SBBB", - "SBBW", - "SBBL", - "SBBQ", + [CC_OP_SUBB] = "SUBB", + [CC_OP_SUBW] = "SUBW", + [CC_OP_SUBL] = "SUBL", + [CC_OP_SUBQ] = "SUBQ", - "LOGICB", - "LOGICW", - "LOGICL", - "LOGICQ", + [CC_OP_SBBB] = "SBBB", + [CC_OP_SBBW] = "SBBW", + [CC_OP_SBBL] = "SBBL", + [CC_OP_SBBQ] = "SBBQ", - "INCB", - "INCW", - "INCL", - "INCQ", + [CC_OP_LOGICB] = "LOGICB", + [CC_OP_LOGICW] = "LOGICW", + [CC_OP_LOGICL] = "LOGICL", + [CC_OP_LOGICQ] = "LOGICQ", - "DECB", - "DECW", - "DECL", - "DECQ", + [CC_OP_INCB] = "INCB", + [CC_OP_INCW] = "INCW", + [CC_OP_INCL] = "INCL", + [CC_OP_INCQ] = "INCQ", - "SHLB", - "SHLW", - "SHLL", - "SHLQ", + [CC_OP_DECB] = "DECB", + [CC_OP_DECW] = "DECW", + [CC_OP_DECL] = "DECL", + [CC_OP_DECQ] = "DECQ", - "SARB", - "SARW", - "SARL", - "SARQ", + [CC_OP_SHLB] = "SHLB", + [CC_OP_SHLW] = "SHLW", + [CC_OP_SHLL] = "SHLL", + [CC_OP_SHLQ] = "SHLQ", - "BMILGB", - "BMILGW", - "BMILGL", - "BMILGQ", + [CC_OP_SARB] = "SARB", + [CC_OP_SARW] = "SARW", + [CC_OP_SARL] = "SARL", + [CC_OP_SARQ] = "SARQ", - "ADCX", - "ADOX", - "ADCOX", + [CC_OP_BMILGB] = "BMILGB", + [CC_OP_BMILGW] = "BMILGW", + [CC_OP_BMILGL] = "BMILGL", + [CC_OP_BMILGQ] = "BMILGQ", - "CLR", + [CC_OP_POPCNT] = "POPCNT", + [CC_OP_CLR] = "CLR", }; static void From 944f4001346019a3cd05567695aa48830c904626 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Jun 2024 11:07:39 +0200 Subject: [PATCH 1696/2884] target/i386: use cpu_cc_dst for CC_OP_POPCNT It is the only CCOp, among those that compute ZF from one of the cc_op_* registers, that uses cpu_cc_src. Do not make it the odd one off, instead use cpu_cc_dst like the others. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 +- target/i386/tcg/cc_helper.c | 2 +- target/i386/tcg/emit.c.inc | 4 ++-- target/i386/tcg/translate.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 52571ababe..1b4edbe058 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1332,7 +1332,7 @@ typedef enum { CC_OP_BMILGQ, CC_OP_CLR, /* Z set, all other flags clear. */ - CC_OP_POPCNT, /* Z via CC_SRC, all other flags clear. */ + CC_OP_POPCNT, /* Z via CC_DST, all other flags clear. */ CC_OP_NB, } CCOp; diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c index f76e9cb8cf..301ed95406 100644 --- a/target/i386/tcg/cc_helper.c +++ b/target/i386/tcg/cc_helper.c @@ -107,7 +107,7 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, case CC_OP_CLR: return CC_Z | CC_P; case CC_OP_POPCNT: - return src1 ? 0 : CC_Z; + return dst ? 0 : CC_Z; case CC_OP_MULB: return compute_all_mulb(dst, src1); diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 11faa70b5e..fc7477833b 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2804,10 +2804,10 @@ static void gen_POPA(DisasContext *s, X86DecodedInsn *decode) static void gen_POPCNT(DisasContext *s, X86DecodedInsn *decode) { - decode->cc_src = tcg_temp_new(); + decode->cc_dst = tcg_temp_new(); decode->cc_op = CC_OP_POPCNT; - tcg_gen_mov_tl(decode->cc_src, s->T0); + tcg_gen_mov_tl(decode->cc_dst, s->T0); tcg_gen_ctpop_tl(s->T0, s->T0); } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index ad1819815a..eb353dc3c9 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -324,7 +324,7 @@ static const uint8_t cc_op_live[CC_OP_NB] = { [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, [CC_OP_CLR] = 0, - [CC_OP_POPCNT] = USES_CC_SRC, + [CC_OP_POPCNT] = USES_CC_DST, }; static void set_cc_op_1(DisasContext *s, CCOp op, bool dirty) @@ -1020,7 +1020,7 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) case CC_OP_CLR: return (CCPrepare) { .cond = TCG_COND_ALWAYS }; case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src }; + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst }; default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; From 460231ad369fd9e6608859fd747bbf276850d96b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Jun 2024 10:33:56 +0200 Subject: [PATCH 1697/2884] target/i386: give CC_OP_POPCNT low bits corresponding to MO_TL Handle it like the other arithmetic cc_ops. This simplifies a bit the implementation of bit test instructions. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 13 +++++++++++-- target/i386/tcg/translate.c | 3 +-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1b4edbe058..29daf37048 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1275,6 +1275,7 @@ typedef enum { CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest. */ CC_OP_ADOX, /* CC_SRC2 = O, CC_SRC = rest. */ CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest. */ + CC_OP_CLR, /* Z and P set, all other flags clear. */ CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */ CC_OP_MULW, @@ -1331,8 +1332,16 @@ typedef enum { CC_OP_BMILGL, CC_OP_BMILGQ, - CC_OP_CLR, /* Z set, all other flags clear. */ - CC_OP_POPCNT, /* Z via CC_DST, all other flags clear. */ + /* + * Note that only CC_OP_POPCNT (i.e. the one with MO_TL size) + * is used or implemented, because the translation needs + * to zero-extend CC_DST anyway. + */ + CC_OP_POPCNTB__, /* Z via CC_DST, all other flags clear. */ + CC_OP_POPCNTW__, + CC_OP_POPCNTL__, + CC_OP_POPCNTQ__, + CC_OP_POPCNT = sizeof(target_ulong) == 8 ? CC_OP_POPCNTQ__ : CC_OP_POPCNTL__, CC_OP_NB, } CCOp; diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index eb353dc3c9..934c514e64 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1019,8 +1019,6 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) .imm = CC_Z }; case CC_OP_CLR: return (CCPrepare) { .cond = TCG_COND_ALWAYS }; - case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst }; default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; @@ -3177,6 +3175,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) case CC_OP_SHLB ... CC_OP_SHLQ: case CC_OP_SARB ... CC_OP_SARQ: case CC_OP_BMILGB ... CC_OP_BMILGQ: + case CC_OP_POPCNT: /* Z was going to be computed from the non-zero status of CC_DST. We can get that same Z value (and the new C value) by leaving CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the From 74f73c29187f8a464e0cea88e2ae43755b9961c4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Jun 2024 12:52:25 +0200 Subject: [PATCH 1698/2884] target/i386: remove unused enum Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 934c514e64..95bad55bf4 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -282,22 +282,6 @@ enum { JCC_LE, }; -enum { - /* I386 int registers */ - OR_EAX, /* MUST be even numbered */ - OR_ECX, - OR_EDX, - OR_EBX, - OR_ESP, - OR_EBP, - OR_ESI, - OR_EDI, - - OR_TMP0 = 16, /* temporary operand register */ - OR_TMP1, - OR_A0, /* temporary register used when doing address evaluation */ -}; - enum { USES_CC_DST = 1, USES_CC_SRC = 2, From 68c3aa3e97d843b080d5e445ff3a900f6f703471 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 26 Jun 2024 19:03:38 +0200 Subject: [PATCH 1699/2884] target/i386: SEV: rename sev_snp_guest->id_block Free the "id_block" name for the binary version of the data. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 30b83f1d77..6daa8c264c 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -152,7 +152,7 @@ struct SevSnpGuestState { /* configuration parameters */ char *guest_visible_workarounds; - char *id_block; + char *id_block_base64; char *id_auth; char *host_data; @@ -1296,7 +1296,7 @@ sev_snp_launch_finish(SevCommonState *sev_common) } } - trace_kvm_sev_snp_launch_finish(sev_snp->id_block, sev_snp->id_auth, + trace_kvm_sev_snp_launch_finish(sev_snp->id_block_base64, sev_snp->id_auth, sev_snp->host_data); ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error); @@ -2159,7 +2159,7 @@ sev_snp_guest_get_id_block(Object *obj, Error **errp) { SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); - return g_strdup(sev_snp_guest->id_block); + return g_strdup(sev_snp_guest->id_block_base64); } static void @@ -2170,14 +2170,14 @@ sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) gsize len; finish->id_block_en = 0; - g_free(sev_snp_guest->id_block); + g_free(sev_snp_guest->id_block_base64); g_free((guchar *)finish->id_block_uaddr); /* store the base64 str so we don't need to re-encode in getter */ - sev_snp_guest->id_block = g_strdup(value); + sev_snp_guest->id_block_base64 = g_strdup(value); finish->id_block_uaddr = - (uint64_t)qbase64_decode(sev_snp_guest->id_block, -1, &len, errp); + (uint64_t)qbase64_decode(sev_snp_guest->id_block_base64, -1, &len, errp); if (!finish->id_block_uaddr) { return; From dd1b2fb554fb0ace09319e96b21e3b776eb7f5ba Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 26 Jun 2024 19:05:21 +0200 Subject: [PATCH 1700/2884] target/i386: SEV: store pointer to decoded id_block in SevSnpGuest Do not rely on finish->id_block_uaddr, so that there are no casts from pointer to uint64_t. They break on 32-bit hosts. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 6daa8c264c..2d4cfd41e8 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -153,6 +153,7 @@ struct SevSnpGuestState { /* configuration parameters */ char *guest_visible_workarounds; char *id_block_base64; + uint8_t *id_block; char *id_auth; char *host_data; @@ -2170,16 +2171,15 @@ sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) gsize len; finish->id_block_en = 0; + g_free(sev_snp_guest->id_block); g_free(sev_snp_guest->id_block_base64); - g_free((guchar *)finish->id_block_uaddr); /* store the base64 str so we don't need to re-encode in getter */ sev_snp_guest->id_block_base64 = g_strdup(value); + sev_snp_guest->id_block = + qbase64_decode(sev_snp_guest->id_block_base64, -1, &len, errp); - finish->id_block_uaddr = - (uint64_t)qbase64_decode(sev_snp_guest->id_block_base64, -1, &len, errp); - - if (!finish->id_block_uaddr) { + if (!sev_snp_guest->id_block) { return; } @@ -2190,6 +2190,7 @@ sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) } finish->id_block_en = 1; + finish->id_block_uaddr = (uintptr_t)sev_snp_guest->id_block; } static char * From 803b7718e6de87a3ecd5555da7d9fce6435c7db9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 26 Jun 2024 19:03:38 +0200 Subject: [PATCH 1701/2884] target/i386: SEV: rename sev_snp_guest->id_auth Free the "id_auth" name for the binary version of the data. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 2d4cfd41e8..a6b063b762 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -154,7 +154,7 @@ struct SevSnpGuestState { char *guest_visible_workarounds; char *id_block_base64; uint8_t *id_block; - char *id_auth; + char *id_auth_base64; char *host_data; struct kvm_sev_snp_launch_start kvm_start_conf; @@ -1297,7 +1297,7 @@ sev_snp_launch_finish(SevCommonState *sev_common) } } - trace_kvm_sev_snp_launch_finish(sev_snp->id_block_base64, sev_snp->id_auth, + trace_kvm_sev_snp_launch_finish(sev_snp->id_block_base64, sev_snp->id_auth_base64, sev_snp->host_data); ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, finish, &error); @@ -2198,7 +2198,7 @@ sev_snp_guest_get_id_auth(Object *obj, Error **errp) { SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); - return g_strdup(sev_snp_guest->id_auth); + return g_strdup(sev_snp_guest->id_auth_base64); } static void @@ -2208,14 +2208,14 @@ sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp) struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; gsize len; - g_free(sev_snp_guest->id_auth); + g_free(sev_snp_guest->id_auth_base64); g_free((guchar *)finish->id_auth_uaddr); /* store the base64 str so we don't need to re-encode in getter */ - sev_snp_guest->id_auth = g_strdup(value); + sev_snp_guest->id_auth_base64 = g_strdup(value); finish->id_auth_uaddr = - (uint64_t)qbase64_decode(sev_snp_guest->id_auth, -1, &len, errp); + (uint64_t)qbase64_decode(sev_snp_guest->id_auth_base64, -1, &len, errp); if (!finish->id_auth_uaddr) { return; From 1ab620bf3601a3c9e264031d80c7023690ddc2b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 26 Jun 2024 19:05:21 +0200 Subject: [PATCH 1702/2884] target/i386: SEV: store pointer to decoded id_auth in SevSnpGuest Do not rely on finish->id_auth_uaddr, so that there are no casts from pointer to uint64_t. They break on 32-bit hosts. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index a6b063b762..28d6bd3adf 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -155,6 +155,7 @@ struct SevSnpGuestState { char *id_block_base64; uint8_t *id_block; char *id_auth_base64; + uint8_t *id_auth; char *host_data; struct kvm_sev_snp_launch_start kvm_start_conf; @@ -2208,16 +2209,16 @@ sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp) struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; gsize len; + finish->id_auth_uaddr = 0; + g_free(sev_snp_guest->id_auth); g_free(sev_snp_guest->id_auth_base64); - g_free((guchar *)finish->id_auth_uaddr); /* store the base64 str so we don't need to re-encode in getter */ sev_snp_guest->id_auth_base64 = g_strdup(value); + sev_snp_guest->id_auth = + qbase64_decode(sev_snp_guest->id_auth_base64, -1, &len, errp); - finish->id_auth_uaddr = - (uint64_t)qbase64_decode(sev_snp_guest->id_auth_base64, -1, &len, errp); - - if (!finish->id_auth_uaddr) { + if (!sev_snp_guest->id_auth) { return; } @@ -2226,6 +2227,8 @@ sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp) len, KVM_SEV_SNP_ID_AUTH_SIZE); return; } + + finish->id_auth_uaddr = (uintptr_t)sev_snp_guest->id_auth; } static bool From cb61b174620abc41badf12b4caedb85c1747605c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 26 Jun 2024 12:49:49 -0700 Subject: [PATCH 1703/2884] target/i386/sev: Use size_t for object sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code was using both uint32_t and uint64_t for len. Consistently use size_t instead. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20240626194950.1725800-3-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 16 ++++++++-------- target/i386/trace-events | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 28d6bd3adf..0ffdf8952c 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -121,7 +121,7 @@ struct SevCommonStateClass { Error **errp); int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); - int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, uint64_t len); + int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, size_t len); int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); }; @@ -173,7 +173,7 @@ typedef struct SevLaunchUpdateData { QTAILQ_ENTRY(SevLaunchUpdateData) next; hwaddr gpa; void *hva; - uint64_t len; + size_t len; int type; } SevLaunchUpdateData; @@ -886,7 +886,7 @@ sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, if (!data->hva || !data->len) { error_report("SNP_LAUNCH_UPDATE called with invalid address" - "/ length: %p / %lx", + "/ length: %p / %zx", data->hva, data->len); return 1; } @@ -945,7 +945,8 @@ out: } static int -sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, uint64_t len) +sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *addr, size_t len) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -1090,8 +1091,7 @@ sev_launch_finish(SevCommonState *sev_common) } static int -snp_launch_update_data(uint64_t gpa, void *hva, - uint32_t len, int type) +snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) { SevLaunchUpdateData *data; @@ -1108,7 +1108,7 @@ snp_launch_update_data(uint64_t gpa, void *hva, static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *ptr, uint64_t len) + uint8_t *ptr, size_t len) { int ret = snp_launch_update_data(gpa, ptr, len, KVM_SEV_SNP_PAGE_TYPE_NORMAL); @@ -1165,7 +1165,7 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, } static int -snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, uint32_t cpuid_len) +snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) { KvmCpuidInfo kvm_cpuid_info = {0}; SnpCpuidInfo snp_cpuid_info; diff --git a/target/i386/trace-events b/target/i386/trace-events index 06b44ead2e..51301673f0 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -6,7 +6,7 @@ kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zx" kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zx" kvm_sev_change_state(const char *old, const char *new) "%s -> %s" kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p" -kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIx64 +kvm_sev_launch_update_data(void *addr, size_t len) "addr %p len 0x%zx" kvm_sev_launch_measurement(const char *value) "data %s" kvm_sev_launch_finish(void) "" kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d" From b31d386781cf85c193f3b1355dd0604cd6a59943 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 26 Jun 2024 12:49:50 -0700 Subject: [PATCH 1704/2884] target/i386/sev: Fix printf formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hwaddr uses HWADDR_PRIx, sizeof yields size_t so uses %zu, and gsize uses G_GSIZE_FORMAT. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20240626194950.1725800-4-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 0ffdf8952c..3ab8b3c28b 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -934,8 +934,9 @@ sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, out: if (!ret && update.gfn_start << TARGET_PAGE_BITS != data->gpa + data->len) { - error_report("SEV-SNP: expected update of GPA range %lx-%lx," - "got GPA range %lx-%llx", + error_report("SEV-SNP: expected update of GPA range %" + HWADDR_PRIx "-%" HWADDR_PRIx "," + "got GPA range %" HWADDR_PRIx "-%llx", data->gpa, data->gpa + data->len, data->gpa, update.gfn_start << TARGET_PAGE_BITS); ret = -EIO; @@ -2148,7 +2149,8 @@ sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value, } if (len != sizeof(start->gosvw)) { - error_setg(errp, "parameter length of %lu exceeds max of %lu", + error_setg(errp, "parameter length of %" G_GSIZE_FORMAT + " exceeds max of %zu", len, sizeof(start->gosvw)); return; } @@ -2185,7 +2187,8 @@ sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) } if (len != KVM_SEV_SNP_ID_BLOCK_SIZE) { - error_setg(errp, "parameter length of %lu not equal to %u", + error_setg(errp, "parameter length of %" G_GSIZE_FORMAT + " not equal to %u", len, KVM_SEV_SNP_ID_BLOCK_SIZE); return; } @@ -2223,7 +2226,8 @@ sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp) } if (len > KVM_SEV_SNP_ID_AUTH_SIZE) { - error_setg(errp, "parameter length:ID_AUTH %lu exceeds max of %u", + error_setg(errp, "parameter length:ID_AUTH %" G_GSIZE_FORMAT + " exceeds max of %u", len, KVM_SEV_SNP_ID_AUTH_SIZE); return; } @@ -2291,7 +2295,8 @@ sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) } if (len != sizeof(finish->host_data)) { - error_setg(errp, "parameter length of %lu not equal to %lu", + error_setg(errp, "parameter length of %" G_GSIZE_FORMAT + " not equal to %zu", len, sizeof(finish->host_data)); return; } From e12b11f6f29272ee31ccde6b0db1a10139e87083 Mon Sep 17 00:00:00 2001 From: Minwoo Im Date: Sun, 23 Jun 2024 11:45:55 +0900 Subject: [PATCH 1705/2884] hw/ufs: Fix potential bugs in MMIO read|write This patch fixes two points reported in coverity scan report [1]. Check the MMIO access address with (addr + size), not just with the start offset addr to make sure that the requested memory access not to exceed the actual register region. We also updated (uint8_t *) to (uint32_t *) to represent we are accessing the MMIO registers by dword-sized only. [1] https://lore.kernel.org/qemu-devel/CAFEAcA82L-WZnHMW0X+Dr40bHM-EVq2ZH4DG4pdqop4xxDP2Og@mail.gmail.com/ Cc: Jeuk Kim Reported-by: Peter Maydell Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Reviewed-by: Peter Maydell Message-Id: <20240623024555.78697-1-minwoo.im.dev@gmail.com> Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 71a88d221c..683fff5840 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -55,17 +55,18 @@ static inline uint64_t ufs_reg_size(UfsHc *u) return ufs_mcq_op_reg_addr(u, 0) + sizeof(u->mcq_op_reg); } -static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr) +static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr, unsigned size) { uint64_t mcq_reg_addr = ufs_mcq_reg_addr(u, 0); - return addr >= mcq_reg_addr && addr < mcq_reg_addr + sizeof(u->mcq_reg); + return (addr >= mcq_reg_addr && + addr + size <= mcq_reg_addr + sizeof(u->mcq_reg)); } -static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr) +static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr, unsigned size) { uint64_t mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0); return (addr >= mcq_op_reg_addr && - addr < mcq_op_reg_addr + sizeof(u->mcq_op_reg)); + addr + size <= mcq_op_reg_addr + sizeof(u->mcq_op_reg)); } static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) @@ -774,25 +775,25 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size) { UfsHc *u = (UfsHc *)opaque; - uint8_t *ptr; + uint32_t *ptr; uint64_t value; uint64_t offset; - if (addr < sizeof(u->reg)) { + if (addr + size <= sizeof(u->reg)) { offset = addr; - ptr = (uint8_t *)&u->reg; - } else if (ufs_is_mcq_reg(u, addr)) { + ptr = (uint32_t *)&u->reg; + } else if (ufs_is_mcq_reg(u, addr, size)) { offset = addr - ufs_mcq_reg_addr(u, 0); - ptr = (uint8_t *)&u->mcq_reg; - } else if (ufs_is_mcq_op_reg(u, addr)) { + ptr = (uint32_t *)&u->mcq_reg; + } else if (ufs_is_mcq_op_reg(u, addr, size)) { offset = addr - ufs_mcq_op_reg_addr(u, 0); - ptr = (uint8_t *)&u->mcq_op_reg; + ptr = (uint32_t *)&u->mcq_op_reg; } else { trace_ufs_err_invalid_register_offset(addr); return 0; } - value = *(uint32_t *)(ptr + offset); + value = ptr[offset >> 2]; trace_ufs_mmio_read(addr, value, size); return value; } @@ -804,11 +805,11 @@ static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data, trace_ufs_mmio_write(addr, data, size); - if (addr < sizeof(u->reg)) { + if (addr + size <= sizeof(u->reg)) { ufs_write_reg(u, addr, data, size); - } else if (ufs_is_mcq_reg(u, addr)) { + } else if (ufs_is_mcq_reg(u, addr, size)) { ufs_write_mcq_reg(u, addr - ufs_mcq_reg_addr(u, 0), data, size); - } else if (ufs_is_mcq_op_reg(u, addr)) { + } else if (ufs_is_mcq_op_reg(u, addr, size)) { ufs_write_mcq_op_reg(u, addr - ufs_mcq_op_reg_addr(u, 0), data, size); } else { trace_ufs_err_invalid_register_offset(addr); From b35505523a000ea2080ba57bab7d8b3a02f8e854 Mon Sep 17 00:00:00 2001 From: Martin Joerg Date: Sat, 15 Jun 2024 13:43:23 +0200 Subject: [PATCH 1706/2884] hmp-commands-info.hx: Add missing info command for stats subcommand Signed-off-by: Martin Joerg Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hmp-commands-info.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index cfd4ad5651..c59cd6637b 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -892,7 +892,7 @@ ERST }, SRST - ``stats`` + ``info stats`` Show runtime-collected statistics ERST From 2b5d12b68514e3d81086a65fc8496822d5bd4359 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Wed, 12 Jun 2024 14:04:46 -0300 Subject: [PATCH 1707/2884] cpu: fix memleak of 'halt_cond' and 'thread' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since a4c2735f35 (cpu: move Qemu[Thread|Cond] setup into common code, 2024-05-30) these fields are now allocated at cpu_common_initfn(). So let's make sure we also free them at cpu_common_finalize(). Furthermore, the code also frees these on round robin, but we missed 'halt_cond'. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- accel/tcg/tcg-accel-ops-rr.c | 1 + hw/core/cpu-common.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 84c36c1450..48c38714bd 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -329,6 +329,7 @@ void rr_start_vcpu_thread(CPUState *cpu) /* we share the thread, dump spare data */ g_free(cpu->thread); qemu_cond_destroy(cpu->halt_cond); + g_free(cpu->halt_cond); cpu->thread = single_tcg_cpu_thread; cpu->halt_cond = single_tcg_halt_cond; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index bf1a7b8892..f131cde2c0 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -286,6 +286,9 @@ static void cpu_common_finalize(Object *obj) g_array_free(cpu->gdb_regs, TRUE); qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); + qemu_cond_destroy(cpu->halt_cond); + g_free(cpu->halt_cond); + g_free(cpu->thread); } static int64_t cpu_common_get_arch_id(CPUState *cpu) From 3fd73736c69b71035cf1154ef58e8fa494f8612c Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Mon, 3 Jun 2024 17:02:21 -0700 Subject: [PATCH 1708/2884] vl: Allow multiple -overcommit commands Both cpu-pm and mem-lock are related to system resource overcommit, but they are separate from each other, in terms of how they are realized, and of course, they are applied to different system resources. It's tempting to use separate command lines to specify their behavior. e.g., in the following example, the cpu-pm command is quietly overwritten, and it's not easy to notice it without careful inspection. --overcommit mem-lock=on --overcommit cpu-pm=on Fixes: c8c9dc42b7ca ("Remove the deprecated -realtime option") Suggested-by: Thomas Huth Signed-off-by: Zide Chen Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Igor Mammedov Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- system/vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/vl.c b/system/vl.c index cfcb674425..4dc862652f 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3546,8 +3546,8 @@ void qemu_init(int argc, char **argv) if (!opts) { exit(1); } - enable_mlock = qemu_opt_get_bool(opts, "mem-lock", false); - enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", false); + enable_mlock = qemu_opt_get_bool(opts, "mem-lock", enable_mlock); + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); break; case QEMU_OPTION_compat: { From 05fc711c3aa08ad800bf76eb0b7aeeb7a5cd0ecf Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Mon, 3 Jun 2024 17:02:22 -0700 Subject: [PATCH 1709/2884] target/i386: Advertise MWAIT iff host supports host_cpu_realizefn() sets CPUID_EXT_MONITOR without consulting host/KVM capabilities. This may cause problems: - If MWAIT/MONITOR is not available on the host, advertising this feature to the guest and executing MWAIT/MONITOR from the guest triggers #UD and the guest doesn't boot. This is because typically #UD takes priority over VM-Exit interception checks and KVM doesn't emulate MONITOR/MWAIT on #UD. - If KVM doesn't support KVM_X86_DISABLE_EXITS_MWAIT, MWAIT/MONITOR from the guest are intercepted by KVM, which is not what cpu-pm=on intends to do. In these cases, MWAIT/MONITOR should not be exposed to the guest. The logic in kvm_arch_get_supported_cpuid() to handle CPUID_EXT_MONITOR is correct and sufficient, and we can't set CPUID_EXT_MONITOR after x86_cpu_filter_features(). This was not an issue before commit 662175b91ff ("i386: reorder call to cpu_exec_realizefn") because the feature added in the accel-specific realizefn could be checked against host availability and filtered out. Additionally, it seems not a good idea to handle guest CPUID leaves in host_cpu_realizefn(), and this patch merges host_cpu_enable_cpu_pm() into kvm_cpu_realizefn(). Fixes: f5cc5a5c1686 ("i386: split cpu accelerators from cpu.c, using AccelCPUClass") Fixes: 662175b91ff2 ("i386: reorder call to cpu_exec_realizefn") Signed-off-by: Zide Chen Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Reviewed-by: Igor Mammedov Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- target/i386/host-cpu.c | 12 ------------ target/i386/kvm/kvm-cpu.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 280e427c01..8b8bf5afec 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -42,15 +42,6 @@ static uint32_t host_cpu_phys_bits(void) return host_phys_bits; } -static void host_cpu_enable_cpu_pm(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - - host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, - &cpu->mwait.ecx, &cpu->mwait.edx); - env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; -} - static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu) { uint32_t host_phys_bits = host_cpu_phys_bits(); @@ -83,9 +74,6 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (cpu->max_features && enable_cpu_pm) { - host_cpu_enable_cpu_pm(cpu); - } if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { uint32_t phys_bits = host_cpu_adjust_phys_bits(cpu); diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index f9b99b5f50..d57a68a301 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -64,8 +64,15 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * cpu_common_realizefn() (via xcc->parent_realize) */ if (cpu->max_features) { - if (enable_cpu_pm && kvm_has_waitpkg()) { - env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; + if (enable_cpu_pm) { + if (kvm_has_waitpkg()) { + env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; + } + + if (env->features[FEAT_1_ECX] & CPUID_EXT_MONITOR) { + host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, + &cpu->mwait.ecx, &cpu->mwait.edx); + } } if (cpu->ucode_rev == 0) { cpu->ucode_rev = From 4475a9b0585977dcd46e17da1005e1f4569dbf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Jun 2024 08:39:24 +0200 Subject: [PATCH 1710/2884] monitor: Remove obsolete stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hmp_info_roms() was removed in commit dd98234c05 ("qapi: introduce x-query-roms QMP command"), hmp_info_numa() in commit 1b8ae799d8 ("qapi: introduce x-query-numa QMP command"), hmp_info_ramblock() in commit ca411b7c8a ("qapi: introduce x-query-ramblock QMP command") and hmp_info_irq() in commit 91f2fa7045 ("qapi: introduce x-query-irq QMP command"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- include/hw/loader.h | 1 - include/monitor/hmp.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/include/hw/loader.h b/include/hw/loader.h index 8685e27334..9844c5e3cf 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -338,7 +338,6 @@ void *rom_ptr(hwaddr addr, size_t size); * rom_ptr(). */ void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); -void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ rom_add_file(_f, NULL, _a, _i, false, NULL, NULL) diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 954f3c83ad..ae116d9804 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -35,7 +35,6 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vnc(Monitor *mon, const QDict *qdict); void hmp_info_spice(Monitor *mon, const QDict *qdict); void hmp_info_balloon(Monitor *mon, const QDict *qdict); -void hmp_info_irq(Monitor *mon, const QDict *qdict); void hmp_info_pic(Monitor *mon, const QDict *qdict); void hmp_info_pci(Monitor *mon, const QDict *qdict); void hmp_info_tpm(Monitor *mon, const QDict *qdict); @@ -102,7 +101,6 @@ void hmp_chardev_send_break(Monitor *mon, const QDict *qdict); void hmp_object_add(Monitor *mon, const QDict *qdict); void hmp_object_del(Monitor *mon, const QDict *qdict); void hmp_info_memdev(Monitor *mon, const QDict *qdict); -void hmp_info_numa(Monitor *mon, const QDict *qdict); void hmp_info_memory_devices(Monitor *mon, const QDict *qdict); void hmp_qom_list(Monitor *mon, const QDict *qdict); void hmp_qom_get(Monitor *mon, const QDict *qdict); @@ -141,7 +139,6 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); void hmp_info_dump(Monitor *mon, const QDict *qdict); -void hmp_info_ramblock(Monitor *mon, const QDict *qdict); void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict); void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict); From 2cf382d479a14d5f2e923e8e3db2878865040523 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 5 May 2024 18:14:38 +0100 Subject: [PATCH 1711/2884] linux-user: cris: Remove unused struct 'rt_signal_frame' Since 'setup_rt_frame' has never been implemented, this struct is unused. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Richard Henderson Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- linux-user/cris/signal.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c index 4f532b2903..10948bcf30 100644 --- a/linux-user/cris/signal.c +++ b/linux-user/cris/signal.c @@ -35,14 +35,6 @@ struct target_signal_frame { uint16_t retcode[4]; /* Trampoline code. */ }; -struct rt_signal_frame { - siginfo_t *pinfo; - void *puc; - siginfo_t info; - ucontext_t uc; - uint16_t retcode[4]; /* Trampoline code. */ -}; - static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) { __put_user(env->regs[0], &sc->regs.r0); From 23e6b6ef15a1cc169d3b3476af4755c8f711fe9c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 5 May 2024 18:14:40 +0100 Subject: [PATCH 1712/2884] linux-user: sparc: Remove unused struct 'target_mc_fq' This struct is unused since Peter's Commit b8ae597f0e6d ("linux-user/sparc: Fix errors in target_ucontext structures") However, hmm, I'm a bit confused since that commit modifies the structure and then removes it, was that intentional? Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- linux-user/sparc/signal.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index f164b74032..8181b8b92c 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -546,11 +546,6 @@ void setup_sigtramp(abi_ulong sigtramp_page) typedef abi_ulong target_mc_greg_t; typedef target_mc_greg_t target_mc_gregset_t[SPARC_MC_NGREG]; -struct target_mc_fq { - abi_ulong mcfq_addr; - uint32_t mcfq_insn; -}; - /* * Note the manual 16-alignment; the kernel gets this because it * includes a "long double qregs[16]" in the mcpu_fregs union, From 83c9f9d39f373ba2863614c3077244ee6c07a50c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 5 May 2024 18:14:42 +0100 Subject: [PATCH 1713/2884] hw/arm/bcm2836: Remove unusued struct 'BCM283XClass' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This struct has been unused since Commit f932093ae165 ("hw/arm/bcm2836: Split out common part of BCM283X classes") Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/arm/bcm2836.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index db191661f2..40a379bc36 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -18,18 +18,6 @@ #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" -struct BCM283XClass { - /*< private >*/ - DeviceClass parent_class; - /*< public >*/ - const char *name; - const char *cpu_type; - unsigned core_count; - hwaddr peri_base; /* Peripheral base address seen by the CPU */ - hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */ - int clusterid; -}; - static Property bcm2836_enabled_cores_property = DEFINE_PROP_UINT32("enabled-cpus", BCM283XBaseState, enabled_cpus, 0); From 737308fe2be4cf653c3fe9e1358b6a08f673a5d1 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 5 May 2024 18:14:44 +0100 Subject: [PATCH 1714/2884] net/can: Remove unused struct 'CanBusState' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as I can tell this struct has never been used in this file (it is used in can_core.c). Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- net/can/can_host.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/can/can_host.c b/net/can/can_host.c index a3c84028c6..b2fe553f91 100644 --- a/net/can/can_host.c +++ b/net/can/can_host.c @@ -34,12 +34,6 @@ #include "net/can_emu.h" #include "net/can_host.h" -struct CanBusState { - Object object; - - QTAILQ_HEAD(, CanBusClientState) clients; -}; - static void can_host_disconnect(CanHostState *ch) { CanHostClass *chc = CAN_HOST_GET_CLASS(ch); From de448e0f26e710e9d2b7fc91393c40ac24b75847 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Fri, 14 Jun 2024 17:06:38 -0400 Subject: [PATCH 1715/2884] os-posix: Expand setrlimit() syscall compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Darwin uses a subtly different version of the setrlimit() syscall as described in the COMPATIBILITY section of the macOS man page. The value of the rlim_cur member has been adjusted accordingly for Darwin-based systems. Signed-off-by: Trent Huber Tested-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- os-posix.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/os-posix.c b/os-posix.c index a4284e2c07..43f9a43f3f 100644 --- a/os-posix.c +++ b/os-posix.c @@ -270,7 +270,11 @@ void os_setup_limits(void) return; } +#ifdef CONFIG_DARWIN + nofile.rlim_cur = OPEN_MAX < nofile.rlim_max ? OPEN_MAX : nofile.rlim_max; +#else nofile.rlim_cur = nofile.rlim_max; +#endif if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) { warn_report("unable to set NOFILE limit: %s", strerror(errno)); From ad8a0f48e119a53ce2d6231cfb24b29296e14251 Mon Sep 17 00:00:00 2001 From: Hyeongtak Ji Date: Wed, 26 Jun 2024 13:34:58 +0900 Subject: [PATCH 1716/2884] docs/cxl: fix some typos This patch corrects minor typographical errors to ensure the ASCII art aligns with the explanations provided. Specifically, it fixes an incorrect root port reference and removes redundant words. Signed-off-by: Hyeongtak Ji Signed-off-by: Michael Tokarev --- docs/system/devices/cxl.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index 10a0e9bc9f..882b036f5e 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -218,17 +218,17 @@ Notes: A complex configuration here, might be to use the following HDM decoders in HB0. HDM0 routes CFMW0 requests to RP0 and hence part of CXL Type3 0. HDM1 routes CFMW0 requests from a - different region of the CFMW0 PA range to RP2 and hence part + different region of the CFMW0 PA range to RP1 and hence part of CXL Type 3 1. HDM2 routes yet another PA range from within CFMW0 to be interleaved across RP0 and RP1, providing 2 way interleave of part of the memory provided by CXL Type3 0 and CXL Type 3 1. HDM3 routes those interleaved accesses from CFMW1 that target HB0 to RP 0 and another part of the memory of CXL Type 3 0 (as part of a 2 way interleave at the system level - across for example CXL Type3 0 and CXL Type3 2. + across for example CXL Type3 0 and CXL Type3 2). HDM4 is used to enable system wide 4 way interleave across all the present CXL type3 devices, by interleaving those (interleaved) - requests that HB0 receives from from CFMW1 across RP 0 and + requests that HB0 receives from CFMW1 across RP 0 and RP 1 and hence to yet more regions of the memory of the attached Type3 devices. Note this is a representative subset of the full range of possible HDM decoder configurations in this From 875b2fabc063219303f6a8b5b1faba39dc0da906 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 26 Jun 2024 11:44:06 +0200 Subject: [PATCH 1717/2884] docs/system/devices/usb: Replace the non-existing "qemu" binary We don't ship a binary that is simply called "qemu", so we should avoid this in the documentation. Use the configurable binary name via "|qemu_system|" instead. Signed-off-by: Thomas Huth Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/system/devices/usb.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/devices/usb.rst b/docs/system/devices/usb.rst index a6ca7b0c37..dc694d23c2 100644 --- a/docs/system/devices/usb.rst +++ b/docs/system/devices/usb.rst @@ -18,7 +18,7 @@ emulation uses less resources (especially CPU). So if your guest supports XHCI (which should be the case for any operating system released around 2010 or later) we recommend using it: - qemu -device qemu-xhci + |qemu_system| -device qemu-xhci XHCI supports USB 1.1, USB 2.0 and USB 3.0 devices, so this is the only controller you need. With only a single USB controller (and From e9945a87816905c49172fae1905da3f25788750a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jun 2024 16:43:03 +0300 Subject: [PATCH 1718/2884] vl.c: select_machine(): use ERRP_GUARD instead of error propagation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- system/vl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/system/vl.c b/system/vl.c index 4dc862652f..fda93d150c 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1665,28 +1665,28 @@ static const QEMUOption *lookup_opt(int argc, char **argv, static MachineClass *select_machine(QDict *qdict, Error **errp) { + ERRP_GUARD(); const char *machine_type = qdict_get_try_str(qdict, "type"); GSList *machines = object_class_get_list(TYPE_MACHINE, false); - MachineClass *machine_class; - Error *local_err = NULL; + MachineClass *machine_class = NULL; if (machine_type) { machine_class = find_machine(machine_type, machines); qdict_del(qdict, "type"); if (!machine_class) { - error_setg(&local_err, "unsupported machine type"); + error_setg(errp, "unsupported machine type"); } } else { machine_class = find_default_machine(machines); if (!machine_class) { - error_setg(&local_err, "No machine specified, and there is no default"); + error_setg(errp, "No machine specified, and there is no default"); } } g_slist_free(machines); - if (local_err) { - error_append_hint(&local_err, "Use -machine help to list supported machines\n"); - error_propagate(errp, local_err); + if (!machine_class) { + error_append_hint(errp, + "Use -machine help to list supported machines\n"); } return machine_class; } From 0e460ac329980d90bafd400cc1756df8fb72e41a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jun 2024 16:43:04 +0300 Subject: [PATCH 1719/2884] vl.c: select_machine(): use g_autoptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- system/vl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/vl.c b/system/vl.c index fda93d150c..92fc29c193 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1667,7 +1667,7 @@ static MachineClass *select_machine(QDict *qdict, Error **errp) { ERRP_GUARD(); const char *machine_type = qdict_get_try_str(qdict, "type"); - GSList *machines = object_class_get_list(TYPE_MACHINE, false); + g_autoptr(GSList) machines = object_class_get_list(TYPE_MACHINE, false); MachineClass *machine_class = NULL; if (machine_type) { @@ -1683,7 +1683,6 @@ static MachineClass *select_machine(QDict *qdict, Error **errp) } } - g_slist_free(machines); if (!machine_class) { error_append_hint(errp, "Use -machine help to list supported machines\n"); From 412d294ffdc63b56c0e512351ecc01d3a9b90d68 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jun 2024 16:43:05 +0300 Subject: [PATCH 1720/2884] vl.c: select_machine(): add selected machine type to error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- system/vl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/vl.c b/system/vl.c index 92fc29c193..bdd2f6ecf6 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1674,7 +1674,7 @@ static MachineClass *select_machine(QDict *qdict, Error **errp) machine_class = find_machine(machine_type, machines); qdict_del(qdict, "type"); if (!machine_class) { - error_setg(errp, "unsupported machine type"); + error_setg(errp, "unsupported machine type: \"%s\"", optarg); } } else { machine_class = find_default_machine(machines); From f22855dffdbc2906f744b5bcfea869cbb66b8fb2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 27 Jun 2024 19:25:07 +0300 Subject: [PATCH 1721/2884] hw/core/loader: gunzip(): fix memory leak on error path We should call inflateEnd() like on success path to cleanup state in s variable. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/core/loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/loader.c b/hw/core/loader.c index 2f8105d7de..a3bea1e718 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -610,6 +610,7 @@ ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) r = inflate(&s, Z_FINISH); if (r != Z_OK && r != Z_STREAM_END) { printf ("Error: inflate() returned %d\n", r); + inflateEnd(&s); return -1; } dstbytes = s.next_out - (unsigned char *) dst; From c1bf754041f4a9101059c0151475ed69df1c7e2e Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Sun, 19 May 2024 15:11:04 +0530 Subject: [PATCH 1722/2884] hw/nvram: Add BCM2835 OTP device The OTP device registers are currently stubbed. For now, the device houses the OTP rows which will be accessed directly by other peripherals. Signed-off-by: Rayhan Faizel Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/nvram/bcm2835_otp.c | 187 +++++++++++++++++++++++++++++++++ hw/nvram/meson.build | 1 + include/hw/nvram/bcm2835_otp.h | 68 ++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 hw/nvram/bcm2835_otp.c create mode 100644 include/hw/nvram/bcm2835_otp.h diff --git a/hw/nvram/bcm2835_otp.c b/hw/nvram/bcm2835_otp.c new file mode 100644 index 0000000000..c4aed28472 --- /dev/null +++ b/hw/nvram/bcm2835_otp.c @@ -0,0 +1,187 @@ +/* + * BCM2835 One-Time Programmable (OTP) Memory + * + * The OTP implementation is mostly a stub except for the OTP rows + * which are accessed directly by other peripherals such as the mailbox. + * + * The OTP registers are unimplemented due to lack of documentation. + * + * Copyright (c) 2024 Rayhan Faizel + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/nvram/bcm2835_otp.h" +#include "migration/vmstate.h" + +/* OTP rows are 1-indexed */ +uint32_t bcm2835_otp_get_row(BCM2835OTPState *s, unsigned int row) +{ + assert(row <= BCM2835_OTP_ROW_COUNT && row >= 1); + + return s->otp_rows[row - 1]; +} + +void bcm2835_otp_set_row(BCM2835OTPState *s, unsigned int row, + uint32_t value) +{ + assert(row <= BCM2835_OTP_ROW_COUNT && row >= 1); + + /* Real OTP rows work as e-fuses */ + s->otp_rows[row - 1] |= value; +} + +static uint64_t bcm2835_otp_read(void *opaque, hwaddr addr, unsigned size) +{ + switch (addr) { + case BCM2835_OTP_BOOTMODE_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BOOTMODE_REG\n"); + break; + case BCM2835_OTP_CONFIG_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CONFIG_REG\n"); + break; + case BCM2835_OTP_CTRL_LO_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_LO_REG\n"); + break; + case BCM2835_OTP_CTRL_HI_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_HI_REG\n"); + break; + case BCM2835_OTP_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_STATUS_REG\n"); + break; + case BCM2835_OTP_BITSEL_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BITSEL_REG\n"); + break; + case BCM2835_OTP_DATA_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_DATA_REG\n"); + break; + case BCM2835_OTP_ADDR_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_ADDR_REG\n"); + break; + case BCM2835_OTP_WRITE_DATA_READ_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_WRITE_DATA_READ_REG\n"); + break; + case BCM2835_OTP_INIT_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_INIT_STATUS_REG\n"); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } + + return 0; +} + +static void bcm2835_otp_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + switch (addr) { + case BCM2835_OTP_BOOTMODE_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BOOTMODE_REG\n"); + break; + case BCM2835_OTP_CONFIG_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CONFIG_REG\n"); + break; + case BCM2835_OTP_CTRL_LO_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_LO_REG\n"); + break; + case BCM2835_OTP_CTRL_HI_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_HI_REG\n"); + break; + case BCM2835_OTP_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_STATUS_REG\n"); + break; + case BCM2835_OTP_BITSEL_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BITSEL_REG\n"); + break; + case BCM2835_OTP_DATA_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_DATA_REG\n"); + break; + case BCM2835_OTP_ADDR_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_ADDR_REG\n"); + break; + case BCM2835_OTP_WRITE_DATA_READ_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_WRITE_DATA_READ_REG\n"); + break; + case BCM2835_OTP_INIT_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_INIT_STATUS_REG\n"); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps bcm2835_otp_ops = { + .read = bcm2835_otp_read, + .write = bcm2835_otp_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void bcm2835_otp_realize(DeviceState *dev, Error **errp) +{ + BCM2835OTPState *s = BCM2835_OTP(dev); + memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_otp_ops, s, + TYPE_BCM2835_OTP, 0x80); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + + memset(s->otp_rows, 0x00, sizeof(s->otp_rows)); +} + +static const VMStateDescription vmstate_bcm2835_otp = { + .name = TYPE_BCM2835_OTP, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(otp_rows, BCM2835OTPState, BCM2835_OTP_ROW_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_otp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_otp_realize; + dc->vmsd = &vmstate_bcm2835_otp; +} + +static const TypeInfo bcm2835_otp_info = { + .name = TYPE_BCM2835_OTP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835OTPState), + .class_init = bcm2835_otp_class_init, +}; + +static void bcm2835_otp_register_types(void) +{ + type_register_static(&bcm2835_otp_info); +} + +type_init(bcm2835_otp_register_types) diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index 4996c72456..10f3639db6 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -1,5 +1,6 @@ system_ss.add(files('fw_cfg-interface.c')) system_ss.add(files('fw_cfg.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_otp.c')) system_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c')) system_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c')) system_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c')) diff --git a/include/hw/nvram/bcm2835_otp.h b/include/hw/nvram/bcm2835_otp.h new file mode 100644 index 0000000000..1df33700bd --- /dev/null +++ b/include/hw/nvram/bcm2835_otp.h @@ -0,0 +1,68 @@ +/* + * BCM2835 One-Time Programmable (OTP) Memory + * + * Copyright (c) 2024 Rayhan Faizel + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BCM2835_OTP_H +#define BCM2835_OTP_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_BCM2835_OTP "bcm2835-otp" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2835OTPState, BCM2835_OTP) + +#define BCM2835_OTP_ROW_COUNT 66 + +/* https://elinux.org/BCM2835_registers#OTP */ +#define BCM2835_OTP_BOOTMODE_REG 0x00 +#define BCM2835_OTP_CONFIG_REG 0x04 +#define BCM2835_OTP_CTRL_LO_REG 0x08 +#define BCM2835_OTP_CTRL_HI_REG 0x0c +#define BCM2835_OTP_STATUS_REG 0x10 +#define BCM2835_OTP_BITSEL_REG 0x14 +#define BCM2835_OTP_DATA_REG 0x18 +#define BCM2835_OTP_ADDR_REG 0x1c +#define BCM2835_OTP_WRITE_DATA_READ_REG 0x20 +#define BCM2835_OTP_INIT_STATUS_REG 0x24 + + +/* -- Row 32: Undocumented -- */ + +#define BCM2835_OTP_ROW_32 32 + +/* Lock OTP Programming (Customer OTP and private key) */ +#define BCM2835_OTP_ROW_32_LOCK BIT(6) + +/* -- Row 36-43: Customer OTP -- */ + +#define BCM2835_OTP_CUSTOMER_OTP 36 +#define BCM2835_OTP_CUSTOMER_OTP_LEN 8 + +/* Magic numbers to lock programming of customer OTP and private key */ +#define BCM2835_OTP_LOCK_NUM1 0xffffffff +#define BCM2835_OTP_LOCK_NUM2 0xaffe0000 + +/* -- Row 56-63: Device-specific private key -- */ + +#define BCM2835_OTP_PRIVATE_KEY 56 +#define BCM2835_OTP_PRIVATE_KEY_LEN 8 + + +struct BCM2835OTPState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + uint32_t otp_rows[BCM2835_OTP_ROW_COUNT]; +}; + + +uint32_t bcm2835_otp_get_row(BCM2835OTPState *s, unsigned int row); +void bcm2835_otp_set_row(BCM2835OTPState *s, unsigned int row, uint32_t value); + +#endif From 6bf7993921827817eb313f44509bf4ba7ebf88bb Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Sun, 19 May 2024 15:11:05 +0530 Subject: [PATCH 1723/2884] hw/arm: Connect OTP device to BCM2835 Replace stubbed OTP memory region with the new OTP device. Signed-off-by: Rayhan Faizel Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 13 ++++++++++++- include/hw/arm/bcm2835_peripherals.h | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 1695d8b453..7d735bb56c 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -116,6 +116,10 @@ static void raspi_peripherals_base_init(Object *obj) object_property_add_const_link(OBJECT(&s->fb), "dma-mr", OBJECT(&s->gpu_bus_mr)); + /* OTP */ + object_initialize_child(obj, "bcm2835-otp", &s->otp, + TYPE_BCM2835_OTP); + /* Property channel */ object_initialize_child(obj, "property", &s->property, TYPE_BCM2835_PROPERTY); @@ -374,6 +378,14 @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0, qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB)); + /* OTP */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->otp), errp)) { + return; + } + + memory_region_add_subregion(&s->peri_mr, OTP_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->otp), 0)); + /* Property channel */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->property), errp)) { return; @@ -500,7 +512,6 @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp) create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100); create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100); create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100); - create_unimp(s, &s->otp, "bcm2835-otp", OTP_OFFSET, 0x80); create_unimp(s, &s->dbus, "bcm2835-dbus", DBUS_OFFSET, 0x8000); create_unimp(s, &s->ave0, "bcm2835-ave0", AVE0_OFFSET, 0x8000); create_unimp(s, &s->v3d, "bcm2835-v3d", V3D_OFFSET, 0x1000); diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 636203baa5..1eeaeec9e0 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -33,6 +33,7 @@ #include "hw/usb/hcd-dwc2.h" #include "hw/ssi/bcm2835_spi.h" #include "hw/i2c/bcm2835_i2c.h" +#include "hw/nvram/bcm2835_otp.h" #include "hw/misc/unimp.h" #include "qom/object.h" @@ -71,7 +72,7 @@ struct BCMSocPeripheralBaseState { BCM2835SPIState spi[1]; BCM2835I2CState i2c[3]; OrIRQState orgated_i2c_irq; - UnimplementedDeviceState otp; + BCM2835OTPState otp; UnimplementedDeviceState dbus; UnimplementedDeviceState ave0; UnimplementedDeviceState v3d; From 5d5f1b60916aa6bbebe192d74acb762414377430 Mon Sep 17 00:00:00 2001 From: Rayhan Faizel Date: Sun, 19 May 2024 15:11:06 +0530 Subject: [PATCH 1724/2884] hw/misc: Implement mailbox properties for customer OTP and device specific private keys Four mailbox properties are implemented as follows: 1. Customer OTP: GET_CUSTOMER_OTP and SET_CUSTOMER_OTP 2. Device-specific private key: GET_PRIVATE_KEY and SET_PRIVATE_KEY. The customer OTP is located in the rows 36-43. The device-specific private key is located in the rows 56-63. The customer OTP can be locked with the magic numbers 0xffffffff 0xaffe0000 when running the SET_CUSTOMER_OTP mailbox command. Bit 6 of row 32 indicates this lock, which is undocumented. The lock also applies to the device-specific private key. Signed-off-by: Rayhan Faizel Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 2 + hw/misc/bcm2835_property.c | 87 ++++++++++++++++++++++++++++ include/hw/arm/raspberrypi-fw-defs.h | 2 + include/hw/misc/bcm2835_property.h | 2 + 4 files changed, 93 insertions(+) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 7d735bb56c..ac153a96b9 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -132,6 +132,8 @@ static void raspi_peripherals_base_init(Object *obj) OBJECT(&s->fb)); object_property_add_const_link(OBJECT(&s->property), "dma-mr", OBJECT(&s->gpu_bus_mr)); + object_property_add_const_link(OBJECT(&s->property), "otp", + OBJECT(&s->otp)); /* Extended Mass Media Controller */ object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI); diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index bdd9a6bbce..63de3db621 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -32,6 +32,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tmp; int n; uint32_t offset, length, color; + uint32_t start_num, number, otp_row; /* * Copy the current state of the framebuffer config; we will update @@ -322,6 +323,89 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) 0); resplen = VCHI_BUSADDR_SIZE; break; + + /* Customer OTP */ + + case RPI_FWREQ_GET_CUSTOMER_OTP: + start_num = ldl_le_phys(&s->dma_as, value + 12); + number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 8 + 4 * number; + + for (n = start_num; n < start_num + number && + n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { + otp_row = bcm2835_otp_get_row(s->otp, + BCM2835_OTP_CUSTOMER_OTP + n); + stl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2), otp_row); + } + break; + case RPI_FWREQ_SET_CUSTOMER_OTP: + start_num = ldl_le_phys(&s->dma_as, value + 12); + number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 4; + + /* Magic numbers to permanently lock customer OTP */ + if (start_num == BCM2835_OTP_LOCK_NUM1 && + number == BCM2835_OTP_LOCK_NUM2) { + bcm2835_otp_set_row(s->otp, + BCM2835_OTP_ROW_32, + BCM2835_OTP_ROW_32_LOCK); + break; + } + + /* If row 32 has the lock bit, don't allow further writes */ + if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & + BCM2835_OTP_ROW_32_LOCK) { + break; + } + + for (n = start_num; n < start_num + number && + n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { + otp_row = ldl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2)); + bcm2835_otp_set_row(s->otp, + BCM2835_OTP_CUSTOMER_OTP + n, otp_row); + } + break; + + /* Device-specific private key */ + + case RPI_FWREQ_GET_PRIVATE_KEY: + start_num = ldl_le_phys(&s->dma_as, value + 12); + number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 8 + 4 * number; + + for (n = start_num; n < start_num + number && + n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { + otp_row = bcm2835_otp_get_row(s->otp, + BCM2835_OTP_PRIVATE_KEY + n); + stl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2), otp_row); + } + break; + case RPI_FWREQ_SET_PRIVATE_KEY: + start_num = ldl_le_phys(&s->dma_as, value + 12); + number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 4; + + /* If row 32 has the lock bit, don't allow further writes */ + if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & + BCM2835_OTP_ROW_32_LOCK) { + break; + } + + for (n = start_num; n < start_num + number && + n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { + otp_row = ldl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2)); + bcm2835_otp_set_row(s->otp, + BCM2835_OTP_PRIVATE_KEY + n, otp_row); + } + break; default: qemu_log_mask(LOG_UNIMP, "bcm2835_property: unhandled tag 0x%08x\n", tag); @@ -449,6 +533,9 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) s->dma_mr = MEMORY_REGION(obj); address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory"); + obj = object_property_get_link(OBJECT(dev), "otp", &error_abort); + s->otp = BCM2835_OTP(obj); + /* TODO: connect to MAC address of USB NIC device, once we emulate it */ qemu_macaddr_default_if_unset(&s->macaddr); diff --git a/include/hw/arm/raspberrypi-fw-defs.h b/include/hw/arm/raspberrypi-fw-defs.h index 8b404e0533..60b8e5b451 100644 --- a/include/hw/arm/raspberrypi-fw-defs.h +++ b/include/hw/arm/raspberrypi-fw-defs.h @@ -56,6 +56,7 @@ enum rpi_firmware_property_tag { RPI_FWREQ_GET_THROTTLED = 0x00030046, RPI_FWREQ_GET_CLOCK_MEASURED = 0x00030047, RPI_FWREQ_NOTIFY_REBOOT = 0x00030048, + RPI_FWREQ_GET_PRIVATE_KEY = 0x00030081, RPI_FWREQ_SET_CLOCK_STATE = 0x00038001, RPI_FWREQ_SET_CLOCK_RATE = 0x00038002, RPI_FWREQ_SET_VOLTAGE = 0x00038003, @@ -73,6 +74,7 @@ enum rpi_firmware_property_tag { RPI_FWREQ_SET_PERIPH_REG = 0x00038045, RPI_FWREQ_GET_POE_HAT_VAL = 0x00030049, RPI_FWREQ_SET_POE_HAT_VAL = 0x00038049, + RPI_FWREQ_SET_PRIVATE_KEY = 0x00038081, RPI_FWREQ_SET_POE_HAT_VAL_OLD = 0x00030050, RPI_FWREQ_NOTIFY_XHCI_RESET = 0x00030058, RPI_FWREQ_GET_REBOOT_FLAGS = 0x00030064, diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index ba8896610c..2f93fd0c75 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -11,6 +11,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/display/bcm2835_fb.h" +#include "hw/nvram/bcm2835_otp.h" #include "qom/object.h" #define TYPE_BCM2835_PROPERTY "bcm2835-property" @@ -26,6 +27,7 @@ struct BCM2835PropertyState { MemoryRegion iomem; qemu_irq mbox_irq; BCM2835FBState *fbdev; + BCM2835OTPState *otp; MACAddr macaddr; uint32_t board_rev; From 24a7cd6a7c21f82f1ce9bd5ecf6fb54eb7bf4602 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Thu, 20 Jun 2024 12:19:48 +0200 Subject: [PATCH 1725/2884] tests/avocado: update firmware for sbsa-ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update firmware to have graphics card memory fix from EDK2 commit c1d1910be6e04a8b1a73090cf2881fb698947a6e: OvmfPkg/QemuVideoDxe: add feature PCD to remap framebuffer W/C Some platforms (such as SBSA-QEMU on recent builds of the emulator) only tolerate misaligned accesses to normal memory, and raise alignment faults on such accesses to device memory, which is the default for PCIe MMIO BARs. When emulating a PCIe graphics controller, the framebuffer is typically exposed via a MMIO BAR, while the disposition of the region is closer to memory (no side effects on reads or writes, except for the changing picture on the screen; direct random access to any pixel in the image). In order to permit the use of such controllers on platforms that only tolerate these types of accesses for normal memory, it is necessary to remap the memory. Use the DXE services to set the desired capabilities and attributes. Hide this behavior under a feature PCD so only platforms that really need it can enable it. (OVMF on x86 has no need for this) With this fix enabled we can boot sbsa-ref with more than one cpu core. Signed-off-by: Marcin Juszkiewicz Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20240620-b4-new-firmware-v3-1-29a3a2f1be1e@linaro.org Signed-off-by: Peter Maydell --- tests/avocado/machine_aarch64_sbsaref.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/avocado/machine_aarch64_sbsaref.py b/tests/avocado/machine_aarch64_sbsaref.py index 6bb82f2a03..e854ec6a1a 100644 --- a/tests/avocado/machine_aarch64_sbsaref.py +++ b/tests/avocado/machine_aarch64_sbsaref.py @@ -37,18 +37,18 @@ class Aarch64SbsarefMachine(QemuSystemTest): Used components: - - Trusted Firmware 2.11.0 - - Tianocore EDK2 stable202405 - - Tianocore EDK2-platforms commit 4bbd0ed + - Trusted Firmware v2.11.0 + - Tianocore EDK2 4d4f569924 + - Tianocore EDK2-platforms 3f08401 """ # Secure BootRom (TF-A code) fs0_xz_url = ( "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" - "20240528-140808/edk2/SBSA_FLASH0.fd.xz" + "20240619-148232/edk2/SBSA_FLASH0.fd.xz" ) - fs0_xz_hash = "fa6004900b67172914c908b78557fec4d36a5f784f4c3dd08f49adb75e1892a9" + fs0_xz_hash = "0c954842a590988f526984de22e21ae0ab9cb351a0c99a8a58e928f0c7359cf7" tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash, algorithm='sha256') archive.extract(tar_xz_path, self.workdir) @@ -57,9 +57,9 @@ class Aarch64SbsarefMachine(QemuSystemTest): # Non-secure rom (UEFI and EFI variables) fs1_xz_url = ( "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" - "20240528-140808/edk2/SBSA_FLASH1.fd.xz" + "20240619-148232/edk2/SBSA_FLASH1.fd.xz" ) - fs1_xz_hash = "5f3747d4000bc416d9641e33ff4ac60c3cc8cb74ca51b6e932e58531c62eb6f7" + fs1_xz_hash = "c6ec39374c4d79bb9e9cdeeb6db44732d90bb4a334cec92002b3f4b9cac4b5ee" tar_xz_path = self.fetch_asset(fs1_xz_url, asset_hash=fs1_xz_hash, algorithm='sha256') archive.extract(tar_xz_path, self.workdir) From 6c84daac58f0d75a908626faf89a832fc0532140 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Thu, 20 Jun 2024 12:19:49 +0200 Subject: [PATCH 1726/2884] tests/avocado: use default amount of cores on sbsa-ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version of the sbsa-ref EDK2 firmware we used to use in this test had a bug where it might make an unaligned access to the framebuffer, which causes a guest crash on newer versions of QEMU where we enforce the architectural requirement that unaligned accesses to Device memory should take an exception. We happened to not notice this because our test was booting with "-smp 1" and through luck this didn't write the boot logo to the framebuffer at an unaligned address; but trying to boot the same firmware with two CPUs would result in a guest crash. Now we have updated the firmware we're using for the test, we can make the test use all the cores on the board, so we are testing the SMP boot path. Signed-off-by: Marcin Juszkiewicz Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20240620-b4-new-firmware-v3-2-29a3a2f1be1e@linaro.org Signed-off-by: Peter Maydell --- tests/avocado/machine_aarch64_sbsaref.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/avocado/machine_aarch64_sbsaref.py b/tests/avocado/machine_aarch64_sbsaref.py index e854ec6a1a..e920bbf08c 100644 --- a/tests/avocado/machine_aarch64_sbsaref.py +++ b/tests/avocado/machine_aarch64_sbsaref.py @@ -75,8 +75,6 @@ class Aarch64SbsarefMachine(QemuSystemTest): f"if=pflash,file={fs0_path},format=raw", "-drive", f"if=pflash,file={fs1_path},format=raw", - "-smp", - "1", "-machine", "sbsa-ref", ) From 69970205edb661f79a7f6d6046785c521c6da80b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 18 Jun 2024 17:22:18 -0700 Subject: [PATCH 1727/2884] hw/arm/smmu-common: Replace smmu_iommu_mr with smmu_find_sdev The caller of smmu_iommu_mr wants to get sdev for smmuv3_flush_config(). Do it directly instead of bridging with an iommu mr pointer. Signed-off-by: Nicolin Chen Message-id: 20240619002218.926674-1-nicolinc@nvidia.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 8 ++------ hw/arm/smmuv3.c | 12 ++++-------- include/hw/arm/smmu-common.h | 4 ++-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 1ce706bf94..b6601cc102 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -620,20 +620,16 @@ static const PCIIOMMUOps smmu_ops = { .get_address_space = smmu_find_add_as, }; -IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) +SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid) { uint8_t bus_n, devfn; SMMUPciBus *smmu_bus; - SMMUDevice *smmu; bus_n = PCI_BUS_NUM(sid); smmu_bus = smmu_find_smmu_pcibus(s, bus_n); if (smmu_bus) { devfn = SMMU_PCI_DEVFN(sid); - smmu = smmu_bus->pbdev[devfn]; - if (smmu) { - return &smmu->iommu; - } + return smmu_bus->pbdev[devfn]; } return NULL; } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 2d1e0d55ec..445e04ddf7 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1218,20 +1218,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_CFGI_STE: { uint32_t sid = CMD_SID(&cmd); - IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); - SMMUDevice *sdev; + SMMUDevice *sdev = smmu_find_sdev(bs, sid); if (CMD_SSEC(&cmd)) { cmd_error = SMMU_CERROR_ILL; break; } - if (!mr) { + if (!sdev) { break; } trace_smmuv3_cmdq_cfgi_ste(sid); - sdev = container_of(mr, SMMUDevice, iommu); smmuv3_flush_config(sdev); break; @@ -1260,20 +1258,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_CFGI_CD_ALL: { uint32_t sid = CMD_SID(&cmd); - IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); - SMMUDevice *sdev; + SMMUDevice *sdev = smmu_find_sdev(bs, sid); if (CMD_SSEC(&cmd)) { cmd_error = SMMU_CERROR_ILL; break; } - if (!mr) { + if (!sdev) { break; } trace_smmuv3_cmdq_cfgi_cd(sid); - sdev = container_of(mr, SMMUDevice, iommu); smmuv3_flush_config(sdev); break; } diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 5ec2e6c1a4..687b7ca132 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -182,8 +182,8 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, */ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); -/* Return the iommu mr associated to @sid, or NULL if none */ -IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); +/* Return the SMMUDevice associated to @sid, or NULL if none */ +SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid); #define SMMU_IOTLB_MAX_SIZE 256 From 76bccf3cb9d9383da0128bbc6d1300cddbe3ae8f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:24 -0700 Subject: [PATCH 1728/2884] target/arm: Fix VCMLA Dd, Dn, Dm[idx] The inner loop, bounded by eltspersegment, must not be larger than the outer loop, bounded by elements. Cc: qemu-stable@nongnu.org Fixes: 18fc2405781 ("target/arm: Implement SVE fp complex multiply add (indexed)") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2376 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index b05922b425..7b34cc98af 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -907,7 +907,7 @@ void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, void *va, intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; intptr_t elements = opr_sz / sizeof(float16); - intptr_t eltspersegment = 16 / sizeof(float16); + intptr_t eltspersegment = MIN(16 / sizeof(float16), elements); intptr_t i, j; /* Shift boolean to the sign bit so we can xor to negate. */ @@ -969,7 +969,7 @@ void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, void *va, intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; intptr_t elements = opr_sz / sizeof(float32); - intptr_t eltspersegment = 16 / sizeof(float32); + intptr_t eltspersegment = MIN(16 / sizeof(float32), elements); intptr_t i, j; /* Shift boolean to the sign bit so we can xor to negate. */ From a5b72ccc0f21183e7863745b0d82f144ecfb59f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:25 -0700 Subject: [PATCH 1729/2884] target/arm: Fix SQDMULH (by element) with Q=0 The inner loop, bounded by eltspersegment, must not be larger than the outer loop, bounded by elements. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 7b34cc98af..d477479bb1 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -317,10 +317,12 @@ void HELPER(neon_sqdmulh_idx_h)(void *vd, void *vn, void *vm, intptr_t i, j, opr_sz = simd_oprsz(desc); int idx = simd_data(desc); int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); - for (i = 0; i < opr_sz / 2; i += 16 / 2) { + for (i = 0; i < elements; i += 16 / 2) { int16_t mm = m[i]; - for (j = 0; j < 16 / 2; ++j) { + for (j = 0; j < eltspersegment; ++j) { d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, false, vq); } } @@ -333,10 +335,12 @@ void HELPER(neon_sqrdmulh_idx_h)(void *vd, void *vn, void *vm, intptr_t i, j, opr_sz = simd_oprsz(desc); int idx = simd_data(desc); int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); - for (i = 0; i < opr_sz / 2; i += 16 / 2) { + for (i = 0; i < elements; i += 16 / 2) { int16_t mm = m[i]; - for (j = 0; j < 16 / 2; ++j) { + for (j = 0; j < eltspersegment; ++j) { d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, true, vq); } } @@ -512,10 +516,12 @@ void HELPER(neon_sqdmulh_idx_s)(void *vd, void *vn, void *vm, intptr_t i, j, opr_sz = simd_oprsz(desc); int idx = simd_data(desc); int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); - for (i = 0; i < opr_sz / 4; i += 16 / 4) { + for (i = 0; i < elements; i += 16 / 4) { int32_t mm = m[i]; - for (j = 0; j < 16 / 4; ++j) { + for (j = 0; j < eltspersegment; ++j) { d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, false, vq); } } @@ -528,10 +534,12 @@ void HELPER(neon_sqrdmulh_idx_s)(void *vd, void *vn, void *vm, intptr_t i, j, opr_sz = simd_oprsz(desc); int idx = simd_data(desc); int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); - for (i = 0; i < opr_sz / 4; i += 16 / 4) { + for (i = 0; i < elements; i += 16 / 4) { int32_t mm = m[i]; - for (j = 0; j < 16 / 4; ++j) { + for (j = 0; j < eltspersegment; ++j) { d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, true, vq); } } From 7619129f0d4a14d918227c5c47ad7433662e9ccc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:26 -0700 Subject: [PATCH 1730/2884] target/arm: Fix FJCVTZS vs flush-to-zero Input denormals cause the Javascript inexact bit (output to Z) to be set. Cc: qemu-stable@nongnu.org Fixes: 6c1f6f2733a ("target/arm: Implement ARMv8.3-JSConv") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2375 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-4-richard.henderson@linaro.org [PMM: fixed hardcoded tab in test case] Signed-off-by: Peter Maydell --- target/arm/vfp_helper.c | 18 +++++++++--------- tests/tcg/aarch64/Makefile.target | 3 ++- tests/tcg/aarch64/test-2375.c | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 tests/tcg/aarch64/test-2375.c diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index ce26b8a71a..50d7042fa9 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -1091,8 +1091,8 @@ const FloatRoundMode arm_rmode_to_sf_map[] = { uint64_t HELPER(fjcvtzs)(float64 value, void *vstatus) { float_status *status = vstatus; - uint32_t inexact, frac; - uint32_t e_old, e_new; + uint32_t frac, e_old, e_new; + bool inexact; e_old = get_float_exception_flags(status); set_float_exception_flags(0, status); @@ -1100,13 +1100,13 @@ uint64_t HELPER(fjcvtzs)(float64 value, void *vstatus) e_new = get_float_exception_flags(status); set_float_exception_flags(e_old | e_new, status); - if (value == float64_chs(float64_zero)) { - /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ - inexact = 1; - } else { - /* Normal inexact or overflow or NaN */ - inexact = e_new & (float_flag_inexact | float_flag_invalid); - } + /* Normal inexact, denormal with flush-to-zero, or overflow or NaN */ + inexact = e_new & (float_flag_inexact | + float_flag_input_denormal | + float_flag_invalid); + + /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ + inexact |= value == float64_chs(float64_zero); /* Pack the result and the env->ZF representation of Z together. */ return deposit64(frac, 32, 32, inexact); diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 70d728ae9a..4ecbca6a41 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -41,8 +41,9 @@ endif # Pauth Tests ifneq ($(CROSS_CC_HAS_ARMV8_3),) -AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 +AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 test-2375 pauth-%: CFLAGS += -march=armv8.3-a +test-2375: CFLAGS += -march=armv8.3-a run-pauth-1: QEMU_OPTS += -cpu max run-pauth-2: QEMU_OPTS += -cpu max # Choose a cpu with FEAT_Pauth but without FEAT_FPAC for pauth-[45]. diff --git a/tests/tcg/aarch64/test-2375.c b/tests/tcg/aarch64/test-2375.c new file mode 100644 index 0000000000..84c7e7de71 --- /dev/null +++ b/tests/tcg/aarch64/test-2375.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Linaro Ltd */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2375 */ + +#include + +int main(void) +{ + int r, z; + + asm("msr fpcr, %2\n\t" + "fjcvtzs %w0, %d3\n\t" + "cset %1, eq" + : "=r"(r), "=r"(z) + : "r"(0x01000000L), /* FZ = 1 */ + "w"(0xfcff00L)); /* denormal */ + + assert(r == 0); + assert(z == 0); + return 0; +} From b2bbadc63c5e907bbb7a19244929ef4c028f3d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 27 Mar 2024 04:05:14 +0100 Subject: [PATCH 1731/2884] hw/xen: detect when running inside stubdomain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce global xen_is_stubdomain variable when qemu is running inside a stubdomain instead of dom0. This will be relevant for subsequent patches, as few things like accessing PCI config space need to be done differently. Signed-off-by: Marek Marczykowski-Górecki Reviewed-by: Anthony PERARD Message-Id: Signed-off-by: Anthony PERARD --- hw/i386/xen/xen-hvm.c | 22 ++++++++++++++++++++++ include/hw/xen/xen.h | 1 + system/globals.c | 1 + 3 files changed, 24 insertions(+) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 006d219ad5..4f6446600c 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -584,6 +584,26 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } +static bool xen_check_stubdomain(struct xs_handle *xsh) +{ + char *dm_path = g_strdup_printf( + "/local/domain/%d/image/device-model-domid", xen_domid); + char *val; + int32_t dm_domid; + bool is_stubdom = false; + + val = xs_read(xsh, 0, dm_path, NULL); + if (val) { + if (sscanf(val, "%d", &dm_domid) == 1) { + is_stubdom = dm_domid != 0; + } + free(val); + } + + g_free(dm_path); + return is_stubdom; +} + void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) { MachineState *ms = MACHINE(pcms); @@ -596,6 +616,8 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) xen_register_ioreq(state, max_cpus, &xen_memory_listener); + xen_is_stubdomain = xen_check_stubdomain(state->xenstore); + QLIST_INIT(&xen_physmap); xen_read_physmap(state); diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 37ecc91fc3..ecb89ecfc1 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -36,6 +36,7 @@ enum xen_mode { extern uint32_t xen_domid; extern enum xen_mode xen_mode; extern bool xen_domid_restrict; +extern bool xen_is_stubdomain; int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); int xen_set_pci_link_route(uint8_t link, uint8_t irq); diff --git a/system/globals.c b/system/globals.c index e353584201..d602a04fa2 100644 --- a/system/globals.c +++ b/system/globals.c @@ -60,6 +60,7 @@ bool qemu_uuid_set; uint32_t xen_domid; enum xen_mode xen_mode = XEN_DISABLED; bool xen_domid_restrict; +bool xen_is_stubdomain; struct evtchn_backend_ops *xen_evtchn_ops; struct gnttab_backend_ops *xen_gnttab_ops; struct foreignmem_backend_ops *xen_foreignmem_ops; From 196fb962baeff16342279111cc927a153415f85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 27 Mar 2024 04:05:15 +0100 Subject: [PATCH 1732/2884] xen: fix stubdom PCI addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running in a stubdomain, the config space access via sysfs needs to use BDF as seen inside stubdomain (connected via xen-pcifront), which is different from the real BDF. For other purposes (hypercall parameters etc), the real BDF needs to be used. Get the in-stubdomain BDF by looking up relevant PV PCI xenstore entries. Signed-off-by: Marek Marczykowski-Górecki Reviewed-by: Anthony PERARD Message-Id: <35049e99da634a74578a1ff2cb3ae4cc436ede33.1711506237.git-series.marmarek@invisiblethingslab.com> Signed-off-by: Anthony PERARD --- hw/xen/xen-host-pci-device.c | 76 +++++++++++++++++++++++++++++++++++- hw/xen/xen-host-pci-device.h | 6 +++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c index 8c6e9a1716..eaf32f2710 100644 --- a/hw/xen/xen-host-pci-device.c +++ b/hw/xen/xen-host-pci-device.c @@ -9,6 +9,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/cutils.h" +#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus-helper.h" #include "xen-host-pci-device.h" #define XEN_HOST_PCI_MAX_EXT_CAP \ @@ -33,13 +35,73 @@ #define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ #define IORESOURCE_MEM_64 0x00100000 +/* + * Non-passthrough (dom0) accesses are local PCI devices and use the given BDF + * Passthough (stubdom) accesses are through PV frontend PCI device. Those + * either have a BDF identical to the backend's BDF (xen-backend.passthrough=1) + * or a local virtual BDF (xen-backend.passthrough=0) + * + * We are always given the backend's BDF and need to lookup the appropriate + * local BDF for sysfs access. + */ +static void xen_host_pci_fill_local_addr(XenHostPCIDevice *d, Error **errp) +{ + unsigned int num_devs, len, i; + unsigned int domain, bus, dev, func; + char *be_path = NULL; + char path[16]; + + be_path = qemu_xen_xs_read(xenstore, 0, "device/pci/0/backend", &len); + if (!be_path) { + error_setg(errp, "Failed to read device/pci/0/backend"); + goto out; + } + + if (xs_node_scanf(xenstore, 0, be_path, "num_devs", NULL, + "%d", &num_devs) != 1) { + error_setg(errp, "Failed to read or parse %s/num_devs", be_path); + goto out; + } + + for (i = 0; i < num_devs; i++) { + snprintf(path, sizeof(path), "dev-%d", i); + if (xs_node_scanf(xenstore, 0, be_path, path, NULL, + "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) { + error_setg(errp, "Failed to read or parse %s/%s", be_path, path); + goto out; + } + if (domain != d->domain || + bus != d->bus || + dev != d->dev || + func != d->func) + continue; + snprintf(path, sizeof(path), "vdev-%d", i); + if (xs_node_scanf(xenstore, 0, be_path, path, NULL, + "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) { + error_setg(errp, "Failed to read or parse %s/%s", be_path, path); + goto out; + } + d->local_domain = domain; + d->local_bus = bus; + d->local_dev = dev; + d->local_func = func; + goto out; + } + error_setg(errp, "Failed to find PCI device %x:%x:%x.%x in xenstore", + d->domain, d->bus, d->dev, d->func); + +out: + free(be_path); +} + static void xen_host_pci_sysfs_path(const XenHostPCIDevice *d, const char *name, char *buf, ssize_t size) { int rc; rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", - d->domain, d->bus, d->dev, d->func, name); + d->local_domain, d->local_bus, d->local_dev, d->local_func, + name); assert(rc >= 0 && rc < size); } @@ -342,6 +404,18 @@ void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, d->dev = dev; d->func = func; + if (xen_is_stubdomain) { + xen_host_pci_fill_local_addr(d, errp); + if (*errp) { + goto error; + } + } else { + d->local_domain = d->domain; + d->local_bus = d->bus; + d->local_dev = d->dev; + d->local_func = d->func; + } + xen_host_pci_config_open(d, errp); if (*errp) { goto error; diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h index 4d8d34ecb0..270dcb27f7 100644 --- a/hw/xen/xen-host-pci-device.h +++ b/hw/xen/xen-host-pci-device.h @@ -23,6 +23,12 @@ typedef struct XenHostPCIDevice { uint8_t dev; uint8_t func; + /* different from the above in case of stubdomain */ + uint16_t local_domain; + uint8_t local_bus; + uint8_t local_dev; + uint8_t local_func; + uint16_t vendor_id; uint16_t device_id; uint32_t class_code; From 410b4d560dfa3b38a11ad19cf00180238651d9b7 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Thu, 4 Apr 2024 15:08:33 +0100 Subject: [PATCH 1733/2884] xen-hvm: Avoid livelock while handling buffered ioreqs A malicious or buggy guest may generated buffered ioreqs faster than QEMU can process them in handle_buffered_iopage(). The result is a livelock - QEMU continuously processes ioreqs on the main thread without iterating through the main loop which prevents handling other events, processing timers, etc. Without QEMU handling other events, it often results in the guest becoming unsable and makes it difficult to stop the source of buffered ioreqs. To avoid this, if we process a full page of buffered ioreqs, stop and reschedule an immediate timer to continue processing them. This lets QEMU go back to the main loop and catch up. Signed-off-by: Ross Lagerwall Reviewed-by: Paul Durrant Message-Id: <20240404140833.1557953-1-ross.lagerwall@citrix.com> Signed-off-by: Anthony PERARD --- hw/xen/xen-hvm-common.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index b8ace1c368..3a9d6f981b 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -475,11 +475,11 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) } } -static bool handle_buffered_iopage(XenIOState *state) +static unsigned int handle_buffered_iopage(XenIOState *state) { buffered_iopage_t *buf_page = state->buffered_io_page; buf_ioreq_t *buf_req = NULL; - bool handled_ioreq = false; + unsigned int handled = 0; ioreq_t req; int qw; @@ -492,7 +492,7 @@ static bool handle_buffered_iopage(XenIOState *state) req.count = 1; req.dir = IOREQ_WRITE; - for (;;) { + do { uint32_t rdptr = buf_page->read_pointer, wrptr; xen_rmb(); @@ -533,22 +533,30 @@ static bool handle_buffered_iopage(XenIOState *state) assert(!req.data_is_ptr); qatomic_add(&buf_page->read_pointer, qw + 1); - handled_ioreq = true; - } + handled += qw + 1; + } while (handled < IOREQ_BUFFER_SLOT_NUM); - return handled_ioreq; + return handled; } static void handle_buffered_io(void *opaque) { + unsigned int handled; XenIOState *state = opaque; - if (handle_buffered_iopage(state)) { + handled = handle_buffered_iopage(state); + if (handled >= IOREQ_BUFFER_SLOT_NUM) { + /* We handled a full page of ioreqs. Schedule a timer to continue + * processing while giving other stuff a chance to run. + */ timer_mod(state->buffered_io_timer, - BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - } else { + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } else if (handled == 0) { timer_del(state->buffered_io_timer); qemu_xen_evtchn_unmask(state->xce_handle, state->bufioreq_local_port); + } else { + timer_mod(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); } } From f698e4527064da714859892ba49d25a0a2afd12d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:27 -0700 Subject: [PATCH 1734/2884] target/arm: Convert SQRDMLAH, SQRDMLSH to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 10 ++ target/arm/tcg/a64.decode | 16 +++ target/arm/tcg/translate-a64.c | 206 +++++++++++++-------------------- target/arm/tcg/vec_helper.c | 72 ++++++++++++ 4 files changed, 180 insertions(+), 124 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index eca2043fc2..970d059dec 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -979,6 +979,16 @@ DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_h, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve2_sqdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 2b7a3254a0..613cc9365c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -781,6 +781,8 @@ CMEQ_s 0111 1110 111 ..... 10001 1 ..... ..... @rrr_d SQDMULH_s 0101 1110 ..1 ..... 10110 1 ..... ..... @rrr_e SQRDMULH_s 0111 1110 ..1 ..... 10110 1 ..... ..... @rrr_e +SQRDMLAH_s 0111 1110 ..0 ..... 10000 1 ..... ..... @rrr_e +SQRDMLSH_s 0111 1110 ..0 ..... 10001 1 ..... ..... @rrr_e ### Advanced SIMD scalar pairwise @@ -941,6 +943,8 @@ MLS_v 0.10 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e SQDMULH_v 0.00 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e SQRDMULH_v 0.10 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e +SQRDMLAH_v 0.10 1110 ..0 ..... 10000 1 ..... ..... @qrrr_e +SQRDMLSH_v 0.10 1110 ..0 ..... 10001 1 ..... ..... @qrrr_e ### Advanced SIMD scalar x indexed element @@ -966,6 +970,12 @@ SQDMULH_si 0101 1111 10 .. .... 1100 . 0 ..... ..... @rrx_s SQRDMULH_si 0101 1111 01 .. .... 1101 . 0 ..... ..... @rrx_h SQRDMULH_si 0101 1111 10 . ..... 1101 . 0 ..... ..... @rrx_s +SQRDMLAH_si 0111 1111 01 .. .... 1101 . 0 ..... ..... @rrx_h +SQRDMLAH_si 0111 1111 10 .. .... 1101 . 0 ..... ..... @rrx_s + +SQRDMLSH_si 0111 1111 01 .. .... 1111 . 0 ..... ..... @rrx_h +SQRDMLSH_si 0111 1111 10 .. .... 1111 . 0 ..... ..... @rrx_s + ### Advanced SIMD vector x indexed element FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h @@ -1004,6 +1014,12 @@ SQDMULH_vi 0.00 1111 10 . ..... 1100 . 0 ..... ..... @qrrx_s SQRDMULH_vi 0.00 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h SQRDMULH_vi 0.00 1111 10 . ..... 1101 . 0 ..... ..... @qrrx_s +SQRDMLAH_vi 0.10 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h +SQRDMLAH_vi 0.10 1111 10 .. .... 1101 . 0 ..... ..... @qrrx_s + +SQRDMLSH_vi 0.10 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_h +SQRDMLSH_vi 0.10 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s + # Floating-point conditional select FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 93543da39c..32c24c7422 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5235,6 +5235,43 @@ static const ENVScalar2 f_scalar_sqrdmulh = { }; TRANS(SQRDMULH_s, do_env_scalar2_hs, a, &f_scalar_sqrdmulh) +typedef struct ENVScalar3 { + NeonGenThreeOpEnvFn *gen_hs[2]; +} ENVScalar3; + +static bool do_env_scalar3_hs(DisasContext *s, arg_rrr_e *a, + const ENVScalar3 *f) +{ + TCGv_i32 t0, t1, t2; + + if (a->esz != MO_16 && a->esz != MO_32) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, 0, a->esz); + read_vec_element_i32(s, t2, a->rd, 0, a->esz); + f->gen_hs[a->esz - 1](t0, tcg_env, t0, t1, t2); + write_fp_sreg(s, a->rd, t0); + return true; +} + +static const ENVScalar3 f_scalar_sqrdmlah = { + { gen_helper_neon_qrdmlah_s16, gen_helper_neon_qrdmlah_s32 } +}; +TRANS_FEAT(SQRDMLAH_s, aa64_rdm, do_env_scalar3_hs, a, &f_scalar_sqrdmlah) + +static const ENVScalar3 f_scalar_sqrdmlsh = { + { gen_helper_neon_qrdmlsh_s16, gen_helper_neon_qrdmlsh_s32 } +}; +TRANS_FEAT(SQRDMLSH_s, aa64_rdm, do_env_scalar3_hs, a, &f_scalar_sqrdmlsh) + static bool do_cmop_d(DisasContext *s, arg_rrr_e *a, TCGCond cond) { if (fp_access_check(s)) { @@ -5552,6 +5589,8 @@ TRANS(CMTST_v, do_gvec_fn3, a, gen_gvec_cmtst) TRANS(SQDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqdmulh_qc) TRANS(SQRDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmulh_qc) +TRANS_FEAT(SQRDMLAH_v, aa64_rdm, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmlah_qc) +TRANS_FEAT(SQRDMLSH_v, aa64_rdm, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmlsh_qc) /* * Advanced SIMD scalar/vector x indexed element @@ -5681,6 +5720,29 @@ static bool do_env_scalar2_idx_hs(DisasContext *s, arg_rrx_e *a, TRANS(SQDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqdmulh) TRANS(SQRDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqrdmulh) +static bool do_env_scalar3_idx_hs(DisasContext *s, arg_rrx_e *a, + const ENVScalar3 *f) +{ + if (a->esz < MO_16 || a->esz > MO_32) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, a->idx, a->esz); + read_vec_element_i32(s, t2, a->rd, 0, a->esz); + f->gen_hs[a->esz - 1](t0, tcg_env, t0, t1, t2); + write_fp_sreg(s, a->rd, t0); + } + return true; +} + +TRANS_FEAT(SQRDMLAH_si, aa64_rdm, do_env_scalar3_idx_hs, a, &f_scalar_sqrdmlah) +TRANS_FEAT(SQRDMLSH_si, aa64_rdm, do_env_scalar3_idx_hs, a, &f_scalar_sqrdmlsh) + static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -5838,6 +5900,20 @@ static gen_helper_gvec_4 * const f_vector_idx_sqrdmulh[2] = { }; TRANS(SQRDMULH_vi, do_int3_qc_vector_idx, a, f_vector_idx_sqrdmulh) +static gen_helper_gvec_4 * const f_vector_idx_sqrdmlah[2] = { + gen_helper_neon_sqrdmlah_idx_h, + gen_helper_neon_sqrdmlah_idx_s, +}; +TRANS_FEAT(SQRDMLAH_vi, aa64_rdm, do_int3_qc_vector_idx, a, + f_vector_idx_sqrdmlah) + +static gen_helper_gvec_4 * const f_vector_idx_sqrdmlsh[2] = { + gen_helper_neon_sqrdmlsh_idx_h, + gen_helper_neon_sqrdmlsh_idx_s, +}; +TRANS_FEAT(SQRDMLSH_vi, aa64_rdm, do_int3_qc_vector_idx, a, + f_vector_idx_sqrdmlsh) + /* * Advanced SIMD scalar pairwise */ @@ -9536,84 +9612,6 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) } } -/* AdvSIMD scalar three same extra - * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 - * +-----+---+-----------+------+---+------+---+--------+---+----+----+ - * | 0 1 | U | 1 1 1 1 0 | size | 0 | Rm | 1 | opcode | 1 | Rn | Rd | - * +-----+---+-----------+------+---+------+---+--------+---+----+----+ - */ -static void disas_simd_scalar_three_reg_same_extra(DisasContext *s, - uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 4); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - TCGv_i32 ele1, ele2, ele3; - TCGv_i64 res; - bool feature; - - switch (u * 16 + opcode) { - case 0x10: /* SQRDMLAH (vector) */ - case 0x11: /* SQRDMLSH (vector) */ - if (size != 1 && size != 2) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_rdm, s); - break; - default: - unallocated_encoding(s); - return; - } - if (!feature) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - /* Do a single operation on the lowest element in the vector. - * We use the standard Neon helpers and rely on 0 OP 0 == 0 - * with no side effects for all these operations. - * OPTME: special-purpose helpers would avoid doing some - * unnecessary work in the helper for the 16 bit cases. - */ - ele1 = tcg_temp_new_i32(); - ele2 = tcg_temp_new_i32(); - ele3 = tcg_temp_new_i32(); - - read_vec_element_i32(s, ele1, rn, 0, size); - read_vec_element_i32(s, ele2, rm, 0, size); - read_vec_element_i32(s, ele3, rd, 0, size); - - switch (opcode) { - case 0x0: /* SQRDMLAH */ - if (size == 1) { - gen_helper_neon_qrdmlah_s16(ele3, tcg_env, ele1, ele2, ele3); - } else { - gen_helper_neon_qrdmlah_s32(ele3, tcg_env, ele1, ele2, ele3); - } - break; - case 0x1: /* SQRDMLSH */ - if (size == 1) { - gen_helper_neon_qrdmlsh_s16(ele3, tcg_env, ele1, ele2, ele3); - } else { - gen_helper_neon_qrdmlsh_s32(ele3, tcg_env, ele1, ele2, ele3); - } - break; - default: - g_assert_not_reached(); - } - - res = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(res, ele3); - write_fp_dreg(s, rd, res); -} - static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) @@ -10892,14 +10890,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) int rot; switch (u * 16 + opcode) { - case 0x10: /* SQRDMLAH (vector) */ - case 0x11: /* SQRDMLSH (vector) */ - if (size != 1 && size != 2) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_rdm, s); - break; case 0x02: /* SDOT (vector) */ case 0x12: /* UDOT (vector) */ if (size != MO_32) { @@ -10957,6 +10947,8 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } break; default: + case 0x10: /* SQRDMLAH (vector) */ + case 0x11: /* SQRDMLSH (vector) */ unallocated_encoding(s); return; } @@ -10969,14 +10961,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x0: /* SQRDMLAH (vector) */ - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqrdmlah_qc, size); - return; - - case 0x1: /* SQRDMLSH (vector) */ - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqrdmlsh_qc, size); - return; - case 0x2: /* SDOT / UDOT */ gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b); @@ -12059,13 +12043,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0b: /* SQDMULL, SQDMULL2 */ is_long = true; break; - case 0x1d: /* SQRDMLAH */ - case 0x1f: /* SQRDMLSH */ - if (!dc_isar_feature(aa64_rdm, s)) { - unallocated_encoding(s); - return; - } - break; case 0x0e: /* SDOT */ case 0x1e: /* UDOT */ if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_dp, s)) { @@ -12127,6 +12104,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ case 0x1c: /* FMLSL2 */ + case 0x1d: /* SQRDMLAH */ + case 0x1f: /* SQRDMLSH */ unallocated_encoding(s); return; } @@ -12320,33 +12299,13 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_op, tcg_idx); } break; - case 0x1d: /* SQRDMLAH */ - read_vec_element_i32(s, tcg_res, rd, pass, - is_scalar ? size : MO_32); - if (size == 1) { - gen_helper_neon_qrdmlah_s16(tcg_res, tcg_env, - tcg_op, tcg_idx, tcg_res); - } else { - gen_helper_neon_qrdmlah_s32(tcg_res, tcg_env, - tcg_op, tcg_idx, tcg_res); - } - break; - case 0x1f: /* SQRDMLSH */ - read_vec_element_i32(s, tcg_res, rd, pass, - is_scalar ? size : MO_32); - if (size == 1) { - gen_helper_neon_qrdmlsh_s16(tcg_res, tcg_env, - tcg_op, tcg_idx, tcg_res); - } else { - gen_helper_neon_qrdmlsh_s32(tcg_res, tcg_env, - tcg_op, tcg_idx, tcg_res); - } - break; default: case 0x01: /* FMLA */ case 0x05: /* FMLS */ case 0x09: /* FMUL */ case 0x19: /* FMULX */ + case 0x1d: /* SQRDMLAH */ + case 0x1f: /* SQRDMLSH */ g_assert_not_reached(); } @@ -12538,7 +12497,6 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x0e000000, 0xbf208c00, disas_simd_tb }, { 0x0e000800, 0xbf208c00, disas_simd_zip_trn }, { 0x2e000000, 0xbf208400, disas_simd_ext }, - { 0x5e008400, 0xdf208400, disas_simd_scalar_three_reg_same_extra }, { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index d477479bb1..98604d170f 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -347,6 +347,42 @@ void HELPER(neon_sqrdmulh_idx_h)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(neon_sqrdmlah_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); + + for (i = 0; i < elements; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, d[i + j], false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmlsh_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); + + for (i = 0; i < elements; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, d[i + j], true, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(sve2_sqrdmlah_h)(void *vd, void *vn, void *vm, void *va, uint32_t desc) { @@ -546,6 +582,42 @@ void HELPER(neon_sqrdmulh_idx_s)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(neon_sqrdmlah_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + + for (i = 0; i < elements; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, d[i + j], false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmlsh_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + + for (i = 0; i < elements; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, d[i + j], true, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(sve2_sqrdmlah_s)(void *vd, void *vn, void *vm, void *va, uint32_t desc) { From 65dd60a65b83191c14ad2c144a2c189b6eb00b46 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:28 -0700 Subject: [PATCH 1735/2884] target/arm: Convert SDOT, UDOT to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 7 +++++ target/arm/tcg/translate-a64.c | 54 ++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 613cc9365c..7411d4ba97 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -61,6 +61,7 @@ @qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0 @qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1 +@qrrr_s . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=2 @qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd @qrrr_e . q:1 ...... esz:2 . rm:5 ...... rn:5 rd:5 &qrrr_e @qr2r_e . q:1 ...... esz:2 . ..... ...... rm:5 rd:5 &qrrr_e rn=%rd @@ -946,6 +947,9 @@ SQRDMULH_v 0.10 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e SQRDMLAH_v 0.10 1110 ..0 ..... 10000 1 ..... ..... @qrrr_e SQRDMLSH_v 0.10 1110 ..0 ..... 10001 1 ..... ..... @qrrr_e +SDOT_v 0.00 1110 100 ..... 10010 1 ..... ..... @qrrr_s +UDOT_v 0.10 1110 100 ..... 10010 1 ..... ..... @qrrr_s + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h @@ -1020,6 +1024,9 @@ SQRDMLAH_vi 0.10 1111 10 .. .... 1101 . 0 ..... ..... @qrrx_s SQRDMLSH_vi 0.10 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_h SQRDMLSH_vi 0.10 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s +SDOT_vi 0.00 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s +UDOT_vi 0.10 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s + # Floating-point conditional select FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 32c24c7422..f2e7d8d75c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5592,6 +5592,18 @@ TRANS(SQRDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmulh_qc) TRANS_FEAT(SQRDMLAH_v, aa64_rdm, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmlah_qc) TRANS_FEAT(SQRDMLSH_v, aa64_rdm, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmlsh_qc) +static bool do_dot_vector(DisasContext *s, arg_qrrr_e *a, + gen_helper_gvec_4 *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_ool(s, a->q, a->rd, a->rn, a->rm, a->rd, 0, fn); + } + return true; +} + +TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) +TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5914,6 +5926,18 @@ static gen_helper_gvec_4 * const f_vector_idx_sqrdmlsh[2] = { TRANS_FEAT(SQRDMLSH_vi, aa64_rdm, do_int3_qc_vector_idx, a, f_vector_idx_sqrdmlsh) +static bool do_dot_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4 *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_ool(s, a->q, a->rd, a->rn, a->rm, a->rd, a->idx, fn); + } + return true; +} + +TRANS_FEAT(SDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_sdot_idx_b) +TRANS_FEAT(UDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_udot_idx_b) + /* * Advanced SIMD scalar pairwise */ @@ -10890,14 +10914,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) int rot; switch (u * 16 + opcode) { - case 0x02: /* SDOT (vector) */ - case 0x12: /* UDOT (vector) */ - if (size != MO_32) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_dp, s); - break; case 0x03: /* USDOT */ if (size != MO_32) { unallocated_encoding(s); @@ -10947,8 +10963,10 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } break; default: + case 0x02: /* SDOT (vector) */ case 0x10: /* SQRDMLAH (vector) */ case 0x11: /* SQRDMLSH (vector) */ + case 0x12: /* UDOT (vector) */ unallocated_encoding(s); return; } @@ -10961,11 +10979,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x2: /* SDOT / UDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, - u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b); - return; - case 0x3: /* USDOT */ gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_usdot_b); return; @@ -12043,13 +12056,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0b: /* SQDMULL, SQDMULL2 */ is_long = true; break; - case 0x0e: /* SDOT */ - case 0x1e: /* UDOT */ - if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_dp, s)) { - unallocated_encoding(s); - return; - } - break; case 0x0f: switch (size) { case 0: /* SUDOT */ @@ -12099,12 +12105,14 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x09: /* FMUL */ case 0x0c: /* SQDMULH */ case 0x0d: /* SQRDMULH */ + case 0x0e: /* SDOT */ case 0x10: /* MLA */ case 0x14: /* MLS */ case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ case 0x1c: /* FMLSL2 */ case 0x1d: /* SQRDMLAH */ + case 0x1e: /* UDOT */ case 0x1f: /* SQRDMLSH */ unallocated_encoding(s); return; @@ -12180,12 +12188,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } switch (16 * u + opcode) { - case 0x0e: /* SDOT */ - case 0x1e: /* UDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - u ? gen_helper_gvec_udot_idx_b - : gen_helper_gvec_sdot_idx_b); - return; case 0x0f: switch (extract32(insn, 22, 2)) { case 0: /* SUDOT */ From 849b7c1661d1784dad4bb56998ba8952d2c8a5d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:29 -0700 Subject: [PATCH 1736/2884] target/arm: Convert SUDOT, USDOT to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 +++ target/arm/tcg/translate-a64.c | 35 ++++++++-------------------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7411d4ba97..8a0251f83c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -949,6 +949,7 @@ SQRDMLSH_v 0.10 1110 ..0 ..... 10001 1 ..... ..... @qrrr_e SDOT_v 0.00 1110 100 ..... 10010 1 ..... ..... @qrrr_s UDOT_v 0.10 1110 100 ..... 10010 1 ..... ..... @qrrr_s +USDOT_v 0.00 1110 100 ..... 10011 1 ..... ..... @qrrr_s ### Advanced SIMD scalar x indexed element @@ -1026,6 +1027,8 @@ SQRDMLSH_vi 0.10 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s SDOT_vi 0.00 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s UDOT_vi 0.10 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s +SUDOT_vi 0.00 1111 00 .. .... 1111 . 0 ..... ..... @qrrx_s +USDOT_vi 0.00 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s # Floating-point conditional select diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f2e7d8d75c..9a658ca876 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5603,6 +5603,7 @@ static bool do_dot_vector(DisasContext *s, arg_qrrr_e *a, TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) +TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) /* * Advanced SIMD scalar/vector x indexed element @@ -5937,6 +5938,10 @@ static bool do_dot_vector_idx(DisasContext *s, arg_qrrx_e *a, TRANS_FEAT(SDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_sdot_idx_b) TRANS_FEAT(UDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_udot_idx_b) +TRANS_FEAT(SUDOT_vi, aa64_i8mm, do_dot_vector_idx, a, + gen_helper_gvec_sudot_idx_b) +TRANS_FEAT(USDOT_vi, aa64_i8mm, do_dot_vector_idx, a, + gen_helper_gvec_usdot_idx_b) /* * Advanced SIMD scalar pairwise @@ -10914,13 +10919,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) int rot; switch (u * 16 + opcode) { - case 0x03: /* USDOT */ - if (size != MO_32) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_i8mm, s); - break; case 0x04: /* SMMLA */ case 0x14: /* UMMLA */ case 0x05: /* USMMLA */ @@ -10964,6 +10962,7 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) break; default: case 0x02: /* SDOT (vector) */ + case 0x03: /* USDOT */ case 0x10: /* SQRDMLAH (vector) */ case 0x11: /* SQRDMLSH (vector) */ case 0x12: /* UDOT (vector) */ @@ -10979,10 +10978,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x3: /* USDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_usdot_b); - return; - case 0x04: /* SMMLA, UMMLA */ gen_gvec_op4_ool(s, 1, rd, rn, rm, rd, 0, u ? gen_helper_gvec_ummla_b @@ -12058,14 +12053,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; case 0x0f: switch (size) { - case 0: /* SUDOT */ - case 2: /* USDOT */ - if (is_scalar || !dc_isar_feature(aa64_i8mm, s)) { - unallocated_encoding(s); - return; - } - size = MO_32; - break; case 1: /* BFDOT */ if (is_scalar || !dc_isar_feature(aa64_bf16, s)) { unallocated_encoding(s); @@ -12082,6 +12069,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) size = MO_16; break; default: + case 0: /* SUDOT */ + case 2: /* USDOT */ unallocated_encoding(s); return; } @@ -12190,18 +12179,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) switch (16 * u + opcode) { case 0x0f: switch (extract32(insn, 22, 2)) { - case 0: /* SUDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - gen_helper_gvec_sudot_idx_b); - return; case 1: /* BFDOT */ gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, gen_helper_gvec_bfdot_idx); return; - case 2: /* USDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - gen_helper_gvec_usdot_idx_b); - return; case 3: /* BFMLAL{B,T} */ gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, 0, (index << 1) | is_q, gen_helper_gvec_bfmlal_idx); From 9130827c4c7976a52dcc70db8beb699a200c35a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:30 -0700 Subject: [PATCH 1737/2884] target/arm: Convert BFDOT to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 ++ target/arm/tcg/translate-a64.c | 20 +++++--------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8a0251f83c..6819fd2587 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -950,6 +950,7 @@ SQRDMLSH_v 0.10 1110 ..0 ..... 10001 1 ..... ..... @qrrr_e SDOT_v 0.00 1110 100 ..... 10010 1 ..... ..... @qrrr_s UDOT_v 0.10 1110 100 ..... 10010 1 ..... ..... @qrrr_s USDOT_v 0.00 1110 100 ..... 10011 1 ..... ..... @qrrr_s +BFDOT_v 0.10 1110 010 ..... 11111 1 ..... ..... @qrrr_s ### Advanced SIMD scalar x indexed element @@ -1029,6 +1030,7 @@ SDOT_vi 0.00 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s UDOT_vi 0.10 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s SUDOT_vi 0.00 1111 00 .. .... 1111 . 0 ..... ..... @qrrx_s USDOT_vi 0.00 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s +BFDOT_vi 0.00 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_s # Floating-point conditional select diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 9a658ca876..0f44cd5aee 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5604,6 +5604,7 @@ static bool do_dot_vector(DisasContext *s, arg_qrrr_e *a, TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) +TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector, a, gen_helper_gvec_bfdot) /* * Advanced SIMD scalar/vector x indexed element @@ -5942,6 +5943,8 @@ TRANS_FEAT(SUDOT_vi, aa64_i8mm, do_dot_vector_idx, a, gen_helper_gvec_sudot_idx_b) TRANS_FEAT(USDOT_vi, aa64_i8mm, do_dot_vector_idx, a, gen_helper_gvec_usdot_idx_b) +TRANS_FEAT(BFDOT_vi, aa64_bf16, do_dot_vector_idx, a, + gen_helper_gvec_bfdot_idx) /* * Advanced SIMD scalar pairwise @@ -10951,11 +10954,11 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) break; case 0x1f: switch (size) { - case 1: /* BFDOT */ case 3: /* BFMLAL{B,T} */ feature = dc_isar_feature(aa64_bf16, s); break; default: + case 1: /* BFDOT */ unallocated_encoding(s); return; } @@ -11036,9 +11039,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) return; case 0xf: switch (size) { - case 1: /* BFDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_bfdot); - break; case 3: /* BFMLAL{B,T} */ gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, false, is_q, gen_helper_gvec_bfmlal); @@ -12053,13 +12053,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; case 0x0f: switch (size) { - case 1: /* BFDOT */ - if (is_scalar || !dc_isar_feature(aa64_bf16, s)) { - unallocated_encoding(s); - return; - } - size = MO_32; - break; case 3: /* BFMLAL{B,T} */ if (is_scalar || !dc_isar_feature(aa64_bf16, s)) { unallocated_encoding(s); @@ -12070,6 +12063,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; default: case 0: /* SUDOT */ + case 1: /* BFDOT */ case 2: /* USDOT */ unallocated_encoding(s); return; @@ -12179,10 +12173,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) switch (16 * u + opcode) { case 0x0f: switch (extract32(insn, 22, 2)) { - case 1: /* BFDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - gen_helper_gvec_bfdot_idx); - return; case 3: /* BFMLAL{B,T} */ gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, 0, (index << 1) | is_q, gen_helper_gvec_bfmlal_idx); From 1c6ecab43172b9a5c345df38de5e8b4fb5c285e1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:31 -0700 Subject: [PATCH 1738/2884] target/arm: Convert BFMLALB, BFMLALT to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 2 + target/arm/tcg/translate-a64.c | 77 +++++++++++++--------------------- 2 files changed, 31 insertions(+), 48 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 6819fd2587..15344a73de 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -951,6 +951,7 @@ SDOT_v 0.00 1110 100 ..... 10010 1 ..... ..... @qrrr_s UDOT_v 0.10 1110 100 ..... 10010 1 ..... ..... @qrrr_s USDOT_v 0.00 1110 100 ..... 10011 1 ..... ..... @qrrr_s BFDOT_v 0.10 1110 010 ..... 11111 1 ..... ..... @qrrr_s +BFMLAL_v 0.10 1110 110 ..... 11111 1 ..... ..... @qrrr_h ### Advanced SIMD scalar x indexed element @@ -1031,6 +1032,7 @@ UDOT_vi 0.10 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s SUDOT_vi 0.00 1111 00 .. .... 1111 . 0 ..... ..... @qrrx_s USDOT_vi 0.00 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s BFDOT_vi 0.00 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_s +BFMLAL_vi 0.00 1111 11 .. .... 1111 . 0 ..... ..... @qrrx_h # Floating-point conditional select diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0f44cd5aee..95be862dde 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5606,6 +5606,19 @@ TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector, a, gen_helper_gvec_bfdot) +static bool trans_BFMLAL_v(DisasContext *s, arg_qrrr_e *a) +{ + if (!dc_isar_feature(aa64_bf16, s)) { + return false; + } + if (fp_access_check(s)) { + /* Q bit selects BFMLALB vs BFMLALT. */ + gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, false, a->q, + gen_helper_gvec_bfmlal); + } + return true; +} + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5946,6 +5959,20 @@ TRANS_FEAT(USDOT_vi, aa64_i8mm, do_dot_vector_idx, a, TRANS_FEAT(BFDOT_vi, aa64_bf16, do_dot_vector_idx, a, gen_helper_gvec_bfdot_idx) +static bool trans_BFMLAL_vi(DisasContext *s, arg_qrrx_e *a) +{ + if (!dc_isar_feature(aa64_bf16, s)) { + return false; + } + if (fp_access_check(s)) { + /* Q bit selects BFMLALB vs BFMLALT. */ + gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, 0, + (a->idx << 1) | a->q, + gen_helper_gvec_bfmlal_idx); + } + return true; +} + /* * Advanced SIMD scalar pairwise */ @@ -10952,23 +10979,13 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } feature = dc_isar_feature(aa64_bf16, s); break; - case 0x1f: - switch (size) { - case 3: /* BFMLAL{B,T} */ - feature = dc_isar_feature(aa64_bf16, s); - break; - default: - case 1: /* BFDOT */ - unallocated_encoding(s); - return; - } - break; default: case 0x02: /* SDOT (vector) */ case 0x03: /* USDOT */ case 0x10: /* SQRDMLAH (vector) */ case 0x11: /* SQRDMLSH (vector) */ case 0x12: /* UDOT (vector) */ + case 0x1f: /* BFDOT / BFMLAL */ unallocated_encoding(s); return; } @@ -11037,17 +11054,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) case 0xd: /* BFMMLA */ gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_bfmmla); return; - case 0xf: - switch (size) { - case 3: /* BFMLAL{B,T} */ - gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, false, is_q, - gen_helper_gvec_bfmlal); - break; - default: - g_assert_not_reached(); - } - return; - default: g_assert_not_reached(); } @@ -12051,24 +12057,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0b: /* SQDMULL, SQDMULL2 */ is_long = true; break; - case 0x0f: - switch (size) { - case 3: /* BFMLAL{B,T} */ - if (is_scalar || !dc_isar_feature(aa64_bf16, s)) { - unallocated_encoding(s); - return; - } - /* can't set is_fp without other incorrect size checks */ - size = MO_16; - break; - default: - case 0: /* SUDOT */ - case 1: /* BFDOT */ - case 2: /* USDOT */ - unallocated_encoding(s); - return; - } - break; case 0x11: /* FCMLA #0 */ case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ @@ -12089,6 +12077,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0c: /* SQDMULH */ case 0x0d: /* SQRDMULH */ case 0x0e: /* SDOT */ + case 0x0f: /* SUDOT / BFDOT / USDOT / BFMLAL */ case 0x10: /* MLA */ case 0x14: /* MLS */ case 0x18: /* FMLAL2 */ @@ -12171,14 +12160,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } switch (16 * u + opcode) { - case 0x0f: - switch (extract32(insn, 22, 2)) { - case 3: /* BFMLAL{B,T} */ - gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, 0, (index << 1) | is_q, - gen_helper_gvec_bfmlal_idx); - return; - } - g_assert_not_reached(); case 0x11: /* FCMLA #0 */ case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ From 9676c9d9b5a937e432118de8070261648fe14706 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:32 -0700 Subject: [PATCH 1739/2884] target/arm: Convert BFMMLA, SMMLA, UMMLA, USMMLA to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 36 ++++++++-------------------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 15344a73de..b2c7e36969 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -952,6 +952,10 @@ UDOT_v 0.10 1110 100 ..... 10010 1 ..... ..... @qrrr_s USDOT_v 0.00 1110 100 ..... 10011 1 ..... ..... @qrrr_s BFDOT_v 0.10 1110 010 ..... 11111 1 ..... ..... @qrrr_s BFMLAL_v 0.10 1110 110 ..... 11111 1 ..... ..... @qrrr_h +BFMMLA 0110 1110 010 ..... 11101 1 ..... ..... @rrr_q1e0 +SMMLA 0100 1110 100 ..... 10100 1 ..... ..... @rrr_q1e0 +UMMLA 0110 1110 100 ..... 10100 1 ..... ..... @rrr_q1e0 +USMMLA 0100 1110 100 ..... 10101 1 ..... ..... @rrr_q1e0 ### Advanced SIMD scalar x indexed element diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 95be862dde..2697c4b305 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5605,6 +5605,10 @@ TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector, a, gen_helper_gvec_bfdot) +TRANS_FEAT(BFMMLA, aa64_bf16, do_dot_vector, a, gen_helper_gvec_bfmmla) +TRANS_FEAT(SMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_smmla_b) +TRANS_FEAT(UMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_ummla_b) +TRANS_FEAT(USMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usmmla_b) static bool trans_BFMLAL_v(DisasContext *s, arg_qrrr_e *a) { @@ -10949,15 +10953,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) int rot; switch (u * 16 + opcode) { - case 0x04: /* SMMLA */ - case 0x14: /* UMMLA */ - case 0x05: /* USMMLA */ - if (!is_q || size != MO_32) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_i8mm, s); - break; case 0x18: /* FCMLA, #0 */ case 0x19: /* FCMLA, #90 */ case 0x1a: /* FCMLA, #180 */ @@ -10972,19 +10967,16 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } feature = dc_isar_feature(aa64_fcma, s); break; - case 0x1d: /* BFMMLA */ - if (size != MO_16 || !is_q) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_bf16, s); - break; default: case 0x02: /* SDOT (vector) */ case 0x03: /* USDOT */ + case 0x04: /* SMMLA */ + case 0x05: /* USMMLA */ case 0x10: /* SQRDMLAH (vector) */ case 0x11: /* SQRDMLSH (vector) */ case 0x12: /* UDOT (vector) */ + case 0x14: /* UMMLA */ + case 0x1d: /* BFMMLA */ case 0x1f: /* BFDOT / BFMLAL */ unallocated_encoding(s); return; @@ -10998,15 +10990,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } switch (opcode) { - case 0x04: /* SMMLA, UMMLA */ - gen_gvec_op4_ool(s, 1, rd, rn, rm, rd, 0, - u ? gen_helper_gvec_ummla_b - : gen_helper_gvec_smmla_b); - return; - case 0x05: /* USMMLA */ - gen_gvec_op4_ool(s, 1, rd, rn, rm, rd, 0, gen_helper_gvec_usmmla_b); - return; - case 0x8: /* FCMLA, #0 */ case 0x9: /* FCMLA, #90 */ case 0xa: /* FCMLA, #180 */ @@ -11051,9 +11034,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } return; - case 0xd: /* BFMMLA */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_bfmmla); - return; default: g_assert_not_reached(); } From 6515b13e87c83f8c3a9f6ad5e6a3f45ba31c70ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:33 -0700 Subject: [PATCH 1740/2884] target/arm: Add data argument to do_fp3_vector Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 52 +++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2697c4b305..57cdde008e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5290,7 +5290,7 @@ TRANS(CMHS_s, do_cmop_d, a, TCG_COND_GEU) TRANS(CMEQ_s, do_cmop_d, a, TCG_COND_EQ) TRANS(CMTST_s, do_cmop_d, a, TCG_COND_TSTNE) -static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, +static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, gen_helper_gvec_3_ptr * const fns[3]) { MemOp esz = a->esz; @@ -5313,7 +5313,7 @@ static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, } if (fp_access_check(s)) { gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, - esz == MO_16, 0, fns[esz - 1]); + esz == MO_16, data, fns[esz - 1]); } return true; } @@ -5323,168 +5323,168 @@ static gen_helper_gvec_3_ptr * const f_vector_fadd[3] = { gen_helper_gvec_fadd_s, gen_helper_gvec_fadd_d, }; -TRANS(FADD_v, do_fp3_vector, a, f_vector_fadd) +TRANS(FADD_v, do_fp3_vector, a, 0, f_vector_fadd) static gen_helper_gvec_3_ptr * const f_vector_fsub[3] = { gen_helper_gvec_fsub_h, gen_helper_gvec_fsub_s, gen_helper_gvec_fsub_d, }; -TRANS(FSUB_v, do_fp3_vector, a, f_vector_fsub) +TRANS(FSUB_v, do_fp3_vector, a, 0, f_vector_fsub) static gen_helper_gvec_3_ptr * const f_vector_fdiv[3] = { gen_helper_gvec_fdiv_h, gen_helper_gvec_fdiv_s, gen_helper_gvec_fdiv_d, }; -TRANS(FDIV_v, do_fp3_vector, a, f_vector_fdiv) +TRANS(FDIV_v, do_fp3_vector, a, 0, f_vector_fdiv) static gen_helper_gvec_3_ptr * const f_vector_fmul[3] = { gen_helper_gvec_fmul_h, gen_helper_gvec_fmul_s, gen_helper_gvec_fmul_d, }; -TRANS(FMUL_v, do_fp3_vector, a, f_vector_fmul) +TRANS(FMUL_v, do_fp3_vector, a, 0, f_vector_fmul) static gen_helper_gvec_3_ptr * const f_vector_fmax[3] = { gen_helper_gvec_fmax_h, gen_helper_gvec_fmax_s, gen_helper_gvec_fmax_d, }; -TRANS(FMAX_v, do_fp3_vector, a, f_vector_fmax) +TRANS(FMAX_v, do_fp3_vector, a, 0, f_vector_fmax) static gen_helper_gvec_3_ptr * const f_vector_fmin[3] = { gen_helper_gvec_fmin_h, gen_helper_gvec_fmin_s, gen_helper_gvec_fmin_d, }; -TRANS(FMIN_v, do_fp3_vector, a, f_vector_fmin) +TRANS(FMIN_v, do_fp3_vector, a, 0, f_vector_fmin) static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[3] = { gen_helper_gvec_fmaxnum_h, gen_helper_gvec_fmaxnum_s, gen_helper_gvec_fmaxnum_d, }; -TRANS(FMAXNM_v, do_fp3_vector, a, f_vector_fmaxnm) +TRANS(FMAXNM_v, do_fp3_vector, a, 0, f_vector_fmaxnm) static gen_helper_gvec_3_ptr * const f_vector_fminnm[3] = { gen_helper_gvec_fminnum_h, gen_helper_gvec_fminnum_s, gen_helper_gvec_fminnum_d, }; -TRANS(FMINNM_v, do_fp3_vector, a, f_vector_fminnm) +TRANS(FMINNM_v, do_fp3_vector, a, 0, f_vector_fminnm) static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = { gen_helper_gvec_fmulx_h, gen_helper_gvec_fmulx_s, gen_helper_gvec_fmulx_d, }; -TRANS(FMULX_v, do_fp3_vector, a, f_vector_fmulx) +TRANS(FMULX_v, do_fp3_vector, a, 0, f_vector_fmulx) static gen_helper_gvec_3_ptr * const f_vector_fmla[3] = { gen_helper_gvec_vfma_h, gen_helper_gvec_vfma_s, gen_helper_gvec_vfma_d, }; -TRANS(FMLA_v, do_fp3_vector, a, f_vector_fmla) +TRANS(FMLA_v, do_fp3_vector, a, 0, f_vector_fmla) static gen_helper_gvec_3_ptr * const f_vector_fmls[3] = { gen_helper_gvec_vfms_h, gen_helper_gvec_vfms_s, gen_helper_gvec_vfms_d, }; -TRANS(FMLS_v, do_fp3_vector, a, f_vector_fmls) +TRANS(FMLS_v, do_fp3_vector, a, 0, f_vector_fmls) static gen_helper_gvec_3_ptr * const f_vector_fcmeq[3] = { gen_helper_gvec_fceq_h, gen_helper_gvec_fceq_s, gen_helper_gvec_fceq_d, }; -TRANS(FCMEQ_v, do_fp3_vector, a, f_vector_fcmeq) +TRANS(FCMEQ_v, do_fp3_vector, a, 0, f_vector_fcmeq) static gen_helper_gvec_3_ptr * const f_vector_fcmge[3] = { gen_helper_gvec_fcge_h, gen_helper_gvec_fcge_s, gen_helper_gvec_fcge_d, }; -TRANS(FCMGE_v, do_fp3_vector, a, f_vector_fcmge) +TRANS(FCMGE_v, do_fp3_vector, a, 0, f_vector_fcmge) static gen_helper_gvec_3_ptr * const f_vector_fcmgt[3] = { gen_helper_gvec_fcgt_h, gen_helper_gvec_fcgt_s, gen_helper_gvec_fcgt_d, }; -TRANS(FCMGT_v, do_fp3_vector, a, f_vector_fcmgt) +TRANS(FCMGT_v, do_fp3_vector, a, 0, f_vector_fcmgt) static gen_helper_gvec_3_ptr * const f_vector_facge[3] = { gen_helper_gvec_facge_h, gen_helper_gvec_facge_s, gen_helper_gvec_facge_d, }; -TRANS(FACGE_v, do_fp3_vector, a, f_vector_facge) +TRANS(FACGE_v, do_fp3_vector, a, 0, f_vector_facge) static gen_helper_gvec_3_ptr * const f_vector_facgt[3] = { gen_helper_gvec_facgt_h, gen_helper_gvec_facgt_s, gen_helper_gvec_facgt_d, }; -TRANS(FACGT_v, do_fp3_vector, a, f_vector_facgt) +TRANS(FACGT_v, do_fp3_vector, a, 0, f_vector_facgt) static gen_helper_gvec_3_ptr * const f_vector_fabd[3] = { gen_helper_gvec_fabd_h, gen_helper_gvec_fabd_s, gen_helper_gvec_fabd_d, }; -TRANS(FABD_v, do_fp3_vector, a, f_vector_fabd) +TRANS(FABD_v, do_fp3_vector, a, 0, f_vector_fabd) static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = { gen_helper_gvec_recps_h, gen_helper_gvec_recps_s, gen_helper_gvec_recps_d, }; -TRANS(FRECPS_v, do_fp3_vector, a, f_vector_frecps) +TRANS(FRECPS_v, do_fp3_vector, a, 0, f_vector_frecps) static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = { gen_helper_gvec_rsqrts_h, gen_helper_gvec_rsqrts_s, gen_helper_gvec_rsqrts_d, }; -TRANS(FRSQRTS_v, do_fp3_vector, a, f_vector_frsqrts) +TRANS(FRSQRTS_v, do_fp3_vector, a, 0, f_vector_frsqrts) static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = { gen_helper_gvec_faddp_h, gen_helper_gvec_faddp_s, gen_helper_gvec_faddp_d, }; -TRANS(FADDP_v, do_fp3_vector, a, f_vector_faddp) +TRANS(FADDP_v, do_fp3_vector, a, 0, f_vector_faddp) static gen_helper_gvec_3_ptr * const f_vector_fmaxp[3] = { gen_helper_gvec_fmaxp_h, gen_helper_gvec_fmaxp_s, gen_helper_gvec_fmaxp_d, }; -TRANS(FMAXP_v, do_fp3_vector, a, f_vector_fmaxp) +TRANS(FMAXP_v, do_fp3_vector, a, 0, f_vector_fmaxp) static gen_helper_gvec_3_ptr * const f_vector_fminp[3] = { gen_helper_gvec_fminp_h, gen_helper_gvec_fminp_s, gen_helper_gvec_fminp_d, }; -TRANS(FMINP_v, do_fp3_vector, a, f_vector_fminp) +TRANS(FMINP_v, do_fp3_vector, a, 0, f_vector_fminp) static gen_helper_gvec_3_ptr * const f_vector_fmaxnmp[3] = { gen_helper_gvec_fmaxnump_h, gen_helper_gvec_fmaxnump_s, gen_helper_gvec_fmaxnump_d, }; -TRANS(FMAXNMP_v, do_fp3_vector, a, f_vector_fmaxnmp) +TRANS(FMAXNMP_v, do_fp3_vector, a, 0, f_vector_fmaxnmp) static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = { gen_helper_gvec_fminnump_h, gen_helper_gvec_fminnump_s, gen_helper_gvec_fminnump_d, }; -TRANS(FMINNMP_v, do_fp3_vector, a, f_vector_fminnmp) +TRANS(FMINNMP_v, do_fp3_vector, a, 0, f_vector_fminnmp) static bool do_fmlal(DisasContext *s, arg_qrrr_e *a, bool is_s, bool is_2) { From 0f46ebee6318238c375df35c0c0f56d04f075c50 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:34 -0700 Subject: [PATCH 1741/2884] target/arm: Convert FCADD to decodetree Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 3 +++ target/arm/tcg/translate-a64.c | 33 ++++++++++----------------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index b2c7e36969..f330919851 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -957,6 +957,9 @@ SMMLA 0100 1110 100 ..... 10100 1 ..... ..... @rrr_q1e0 UMMLA 0110 1110 100 ..... 10100 1 ..... ..... @rrr_q1e0 USMMLA 0100 1110 100 ..... 10101 1 ..... ..... @rrr_q1e0 +FCADD_90 0.10 1110 ..0 ..... 11100 1 ..... ..... @qrrr_e +FCADD_270 0.10 1110 ..0 ..... 11110 1 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 57cdde008e..a1b338263f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5623,6 +5623,14 @@ static bool trans_BFMLAL_v(DisasContext *s, arg_qrrr_e *a) return true; } +static gen_helper_gvec_3_ptr * const f_vector_fcadd[3] = { + gen_helper_gvec_fcaddh, + gen_helper_gvec_fcadds, + gen_helper_gvec_fcaddd, +}; +TRANS_FEAT(FCADD_90, aa64_fcma, do_fp3_vector, a, 0, f_vector_fcadd) +TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1, f_vector_fcadd) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10957,8 +10965,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) case 0x19: /* FCMLA, #90 */ case 0x1a: /* FCMLA, #180 */ case 0x1b: /* FCMLA, #270 */ - case 0x1c: /* FCADD, #90 */ - case 0x1e: /* FCADD, #270 */ if (size == 0 || (size == 1 && !dc_isar_feature(aa64_fp16, s)) || (size == 3 && !is_q)) { @@ -10976,7 +10982,9 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) case 0x11: /* SQRDMLSH (vector) */ case 0x12: /* UDOT (vector) */ case 0x14: /* UMMLA */ + case 0x1c: /* FCADD, #90 */ case 0x1d: /* BFMMLA */ + case 0x1e: /* FCADD, #270 */ case 0x1f: /* BFDOT / BFMLAL */ unallocated_encoding(s); return; @@ -11013,27 +11021,6 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } return; - case 0xc: /* FCADD, #90 */ - case 0xe: /* FCADD, #270 */ - rot = extract32(opcode, 1, 1); - switch (size) { - case 1: - gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, - gen_helper_gvec_fcaddh); - break; - case 2: - gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, - gen_helper_gvec_fcadds); - break; - case 3: - gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, - gen_helper_gvec_fcaddd); - break; - default: - g_assert_not_reached(); - } - return; - default: g_assert_not_reached(); } From 80b02a565e518414ba3ced137571ed4597595082 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:35 -0700 Subject: [PATCH 1742/2884] target/arm: Convert FCMLA to decodetree Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell --- target/arm/tcg/a64.decode | 6 + target/arm/tcg/translate-a64.c | 238 ++++++++++----------------------- 2 files changed, 74 insertions(+), 170 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index f330919851..223eac3cac 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -960,6 +960,8 @@ USMMLA 0100 1110 100 ..... 10101 1 ..... ..... @rrr_q1e0 FCADD_90 0.10 1110 ..0 ..... 11100 1 ..... ..... @qrrr_e FCADD_270 0.10 1110 ..0 ..... 11110 1 ..... ..... @qrrr_e +FCMLA_v 0 q:1 10 1110 esz:2 0 rm:5 110 rot:2 1 rn:5 rd:5 + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h @@ -1041,6 +1043,10 @@ USDOT_vi 0.00 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s BFDOT_vi 0.00 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_s BFMLAL_vi 0.00 1111 11 .. .... 1111 . 0 ..... ..... @qrrx_h +FCMLA_vi 0 0 10 1111 01 idx:1 rm:5 0 rot:2 1 0 0 rn:5 rd:5 esz=1 q=0 +FCMLA_vi 0 1 10 1111 01 . rm:5 0 rot:2 1 . 0 rn:5 rd:5 esz=1 idx=%hl q=1 +FCMLA_vi 0 1 10 1111 10 0 rm:5 0 rot:2 1 idx:1 0 rn:5 rd:5 esz=2 q=1 + # Floating-point conditional select FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index a1b338263f..161fa2659c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5631,6 +5631,39 @@ static gen_helper_gvec_3_ptr * const f_vector_fcadd[3] = { TRANS_FEAT(FCADD_90, aa64_fcma, do_fp3_vector, a, 0, f_vector_fcadd) TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1, f_vector_fcadd) +static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) +{ + gen_helper_gvec_4_ptr *fn; + + if (!dc_isar_feature(aa64_fcma, s)) { + return false; + } + switch (a->esz) { + case MO_64: + if (!a->q) { + return false; + } + fn = gen_helper_gvec_fcmlad; + break; + case MO_32: + fn = gen_helper_gvec_fcmlas; + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + fn = gen_helper_gvec_fcmlah; + break; + default: + return false; + } + if (fp_access_check(s)) { + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->esz == MO_16, a->rot, fn); + } + return true; +} + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5985,6 +6018,33 @@ static bool trans_BFMLAL_vi(DisasContext *s, arg_qrrx_e *a) return true; } +static bool trans_FCMLA_vi(DisasContext *s, arg_FCMLA_vi *a) +{ + gen_helper_gvec_4_ptr *fn; + + if (!dc_isar_feature(aa64_fcma, s)) { + return false; + } + switch (a->esz) { + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + fn = gen_helper_gvec_fcmlah_idx; + break; + case MO_32: + fn = gen_helper_gvec_fcmlas_idx; + break; + default: + g_assert_not_reached(); + } + if (fp_access_check(s)) { + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->esz == MO_16, (a->idx << 2) | a->rot, fn); + } + return true; +} + /* * Advanced SIMD scalar pairwise */ @@ -10942,90 +11002,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) } } -/* AdvSIMD three same extra - * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ - * | 0 | Q | U | 0 1 1 1 0 | size | 0 | Rm | 1 | opcode | 1 | Rn | Rd | - * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ - */ -static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 4); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - bool feature; - int rot; - - switch (u * 16 + opcode) { - case 0x18: /* FCMLA, #0 */ - case 0x19: /* FCMLA, #90 */ - case 0x1a: /* FCMLA, #180 */ - case 0x1b: /* FCMLA, #270 */ - if (size == 0 - || (size == 1 && !dc_isar_feature(aa64_fp16, s)) - || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_fcma, s); - break; - default: - case 0x02: /* SDOT (vector) */ - case 0x03: /* USDOT */ - case 0x04: /* SMMLA */ - case 0x05: /* USMMLA */ - case 0x10: /* SQRDMLAH (vector) */ - case 0x11: /* SQRDMLSH (vector) */ - case 0x12: /* UDOT (vector) */ - case 0x14: /* UMMLA */ - case 0x1c: /* FCADD, #90 */ - case 0x1d: /* BFMMLA */ - case 0x1e: /* FCADD, #270 */ - case 0x1f: /* BFDOT / BFMLAL */ - unallocated_encoding(s); - return; - } - if (!feature) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0x8: /* FCMLA, #0 */ - case 0x9: /* FCMLA, #90 */ - case 0xa: /* FCMLA, #180 */ - case 0xb: /* FCMLA, #270 */ - rot = extract32(opcode, 0, 2); - switch (size) { - case 1: - gen_gvec_op4_fpst(s, is_q, rd, rn, rm, rd, true, rot, - gen_helper_gvec_fcmlah); - break; - case 2: - gen_gvec_op4_fpst(s, is_q, rd, rn, rm, rd, false, rot, - gen_helper_gvec_fcmlas); - break; - case 3: - gen_gvec_op4_fpst(s, is_q, rd, rn, rm, rd, false, rot, - gen_helper_gvec_fcmlad); - break; - default: - g_assert_not_reached(); - } - return; - - default: - g_assert_not_reached(); - } -} - static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -12001,10 +11977,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); bool is_long = false; - int is_fp = 0; - bool is_fp16 = false; int index; - TCGv_ptr fpst; switch (16 * u + opcode) { case 0x02: /* SMLAL, SMLAL2 */ @@ -12024,16 +11997,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0b: /* SQDMULL, SQDMULL2 */ is_long = true; break; - case 0x11: /* FCMLA #0 */ - case 0x13: /* FCMLA #90 */ - case 0x15: /* FCMLA #180 */ - case 0x17: /* FCMLA #270 */ - if (is_scalar || !dc_isar_feature(aa64_fcma, s)) { - unallocated_encoding(s); - return; - } - is_fp = 2; - break; default: case 0x00: /* FMLAL */ case 0x01: /* FMLA */ @@ -12046,7 +12009,11 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x0e: /* SDOT */ case 0x0f: /* SUDOT / BFDOT / USDOT / BFMLAL */ case 0x10: /* MLA */ + case 0x11: /* FCMLA #0 */ + case 0x13: /* FCMLA #90 */ case 0x14: /* MLS */ + case 0x15: /* FCMLA #180 */ + case 0x17: /* FCMLA #270 */ case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ case 0x1c: /* FMLSL2 */ @@ -12057,46 +12024,12 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) return; } - switch (is_fp) { - case 1: /* normal fp */ - unallocated_encoding(s); /* in decodetree */ - return; - - case 2: /* complex fp */ - /* Each indexable element is a complex pair. */ - size += 1; - switch (size) { - case MO_32: - if (h && !is_q) { - unallocated_encoding(s); - return; - } - is_fp16 = true; - break; - case MO_64: - break; - default: - unallocated_encoding(s); - return; - } - break; - - default: /* integer */ - switch (size) { - case MO_8: - case MO_64: - unallocated_encoding(s); - return; - } - break; - } - if (is_fp16 && !dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - /* Given MemOp size, adjust register and indexing. */ switch (size) { + case MO_8: + case MO_64: + unallocated_encoding(s); + return; case MO_16: index = h << 2 | l << 1 | m; break; @@ -12104,14 +12037,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) index = h << 1 | l; rm |= m << 4; break; - case MO_64: - if (l || !is_q) { - unallocated_encoding(s); - return; - } - index = h; - rm |= m << 4; - break; default: g_assert_not_reached(); } @@ -12120,32 +12045,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) return; } - if (is_fp) { - fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); - } else { - fpst = NULL; - } - - switch (16 * u + opcode) { - case 0x11: /* FCMLA #0 */ - case 0x13: /* FCMLA #90 */ - case 0x15: /* FCMLA #180 */ - case 0x17: /* FCMLA #270 */ - { - int rot = extract32(insn, 13, 2); - int data = (index << 2) | rot; - tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, rd), fpst, - is_q ? 16 : 8, vec_full_reg_size(s), data, - size == MO_64 - ? gen_helper_gvec_fcmlas_idx - : gen_helper_gvec_fcmlah_idx); - } - return; - } - if (size == 3) { g_assert_not_reached(); } else if (!is_long) { @@ -12407,7 +12306,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) */ static const AArch64DecodeTable data_proc_simd[] = { /* pattern , mask , fn */ - { 0x0e008400, 0x9f208400, disas_simd_three_reg_same_extra }, { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes }, From 2cd5078d5784ef57935d2b157bf6d5566e86eac9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Jun 2024 11:35:36 -0700 Subject: [PATCH 1743/2884] target/arm: Delete dead code from disas_simd_indexed MLA, MLS, SQDMULH, SQRDMULH, were converted with 8db93dcd3def and f80701cb44d, and this code should have been removed then. Signed-off-by: Richard Henderson Message-id: 20240625183536.1672454-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 93 ---------------------------------- 1 file changed, 93 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 161fa2659c..6c07aeaf3b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -11976,7 +11976,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) int h = extract32(insn, 11, 1); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - bool is_long = false; int index; switch (16 * u + opcode) { @@ -11990,12 +11989,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - is_long = true; break; case 0x03: /* SQDMLAL, SQDMLAL2 */ case 0x07: /* SQDMLSL, SQDMLSL2 */ case 0x0b: /* SQDMULL, SQDMULL2 */ - is_long = true; break; default: case 0x00: /* FMLAL */ @@ -12047,96 +12044,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) if (size == 3) { g_assert_not_reached(); - } else if (!is_long) { - /* 32 bit floating point, or 16 or 32 bit integer. - * For the 16 bit scalar case we use the usual Neon helpers and - * rely on the fact that 0 op 0 == 0 with no side effects. - */ - TCGv_i32 tcg_idx = tcg_temp_new_i32(); - int pass, maxpasses; - - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; - } - - read_vec_element_i32(s, tcg_idx, rm, index, size); - - if (size == 1 && !is_scalar) { - /* The simplest way to handle the 16x16 indexed ops is to duplicate - * the index into both halves of the 32 bit tcg_idx and then use - * the usual Neon helpers. - */ - tcg_gen_deposit_i32(tcg_idx, tcg_idx, tcg_idx, 16, 16); - } - - for (pass = 0; pass < maxpasses; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, is_scalar ? size : MO_32); - - switch (16 * u + opcode) { - case 0x10: /* MLA */ - case 0x14: /* MLS */ - { - static NeonGenTwoOpFn * const fns[2][2] = { - { gen_helper_neon_add_u16, gen_helper_neon_sub_u16 }, - { tcg_gen_add_i32, tcg_gen_sub_i32 }, - }; - NeonGenTwoOpFn *genfn; - bool is_sub = opcode == 0x4; - - if (size == 1) { - gen_helper_neon_mul_u16(tcg_res, tcg_op, tcg_idx); - } else { - tcg_gen_mul_i32(tcg_res, tcg_op, tcg_idx); - } - if (opcode == 0x8) { - break; - } - read_vec_element_i32(s, tcg_op, rd, pass, MO_32); - genfn = fns[size - 1][is_sub]; - genfn(tcg_res, tcg_op, tcg_res); - break; - } - case 0x0c: /* SQDMULH */ - if (size == 1) { - gen_helper_neon_qdmulh_s16(tcg_res, tcg_env, - tcg_op, tcg_idx); - } else { - gen_helper_neon_qdmulh_s32(tcg_res, tcg_env, - tcg_op, tcg_idx); - } - break; - case 0x0d: /* SQRDMULH */ - if (size == 1) { - gen_helper_neon_qrdmulh_s16(tcg_res, tcg_env, - tcg_op, tcg_idx); - } else { - gen_helper_neon_qrdmulh_s32(tcg_res, tcg_env, - tcg_op, tcg_idx); - } - break; - default: - case 0x01: /* FMLA */ - case 0x05: /* FMLS */ - case 0x09: /* FMUL */ - case 0x19: /* FMULX */ - case 0x1d: /* SQRDMLAH */ - case 0x1f: /* SQRDMLSH */ - g_assert_not_reached(); - } - - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } else { - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } - - clear_vec_high(s, is_q, rd); } else { /* long ops: 16x16->32 or 32x32->64 */ TCGv_i64 tcg_res[2]; From 4df378ab5164635d0431086b800adb770c037a64 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 24 Jun 2024 18:09:13 +0000 Subject: [PATCH 1744/2884] target/arm: Fix indentation Fix comment indentation adding a missing space. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Message-id: 20240624180915.4528-2-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/cpu64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 0899251eef..71e1bfcd4e 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1167,7 +1167,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = cpu->isar.id_aa64isar2; t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ - t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ + t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ cpu->isar.id_aa64isar2 = t; From c5f9e8bb9300d0cae42d2cb19f6a4d340813f8b5 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 24 Jun 2024 18:09:14 +0000 Subject: [PATCH 1745/2884] target/arm: Move initialization of debug ID registers Move the initialization of the debug ID registers to aa32_max_features, which is used to set the 32-bit ID registers. This ensures that the debug ID registers are consistently set for the max CPU in a single place. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Message-id: 20240624180915.4528-3-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 2 ++ target/arm/tcg/cpu32.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 3841359d0f..d8eb986a04 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2299,6 +2299,8 @@ FIELD(DBGDEVID, DOUBLELOCK, 20, 4) FIELD(DBGDEVID, AUXREGS, 24, 4) FIELD(DBGDEVID, CIDMASK, 28, 4) +FIELD(DBGDEVID1, PCSROFFSET, 0, 4) + FIELD(MVFR0, SIMDREG, 0, 4) FIELD(MVFR0, FPSP, 4, 4) FIELD(MVFR0, FPDP, 8, 4) diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index bdd82d912a..28a5c033bb 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -87,6 +87,34 @@ void aa32_max_features(ARMCPU *cpu) t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ cpu->isar.id_dfr0 = t; + /* Debug ID registers. */ + + /* Bit[15] is RES1, Bit[13] and Bits[11:0] are RES0. */ + t = 0x00008000; + t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, VERSION, 6); /* Armv8 debug */ + t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); + t = FIELD_DP32(t, DBGDIDR, BRPS, 5); + t = FIELD_DP32(t, DBGDIDR, WRPS, 3); + cpu->isar.dbgdidr = t; + + t = 0; + t = FIELD_DP32(t, DBGDEVID, PCSAMPLE, 3); + t = FIELD_DP32(t, DBGDEVID, WPADDRMASK, 1); + t = FIELD_DP32(t, DBGDEVID, BPADDRMASK, 15); + t = FIELD_DP32(t, DBGDEVID, VECTORCATCH, 0); + t = FIELD_DP32(t, DBGDEVID, VIRTEXTNS, 1); + t = FIELD_DP32(t, DBGDEVID, DOUBLELOCK, 1); + t = FIELD_DP32(t, DBGDEVID, AUXREGS, 0); + t = FIELD_DP32(t, DBGDEVID, CIDMASK, 0); + cpu->isar.dbgdevid = t; + + /* Bits[31:4] are RES0. */ + t = 0; + t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); + cpu->isar.dbgdevid1 = t; + t = cpu->isar.id_dfr1; t = FIELD_DP32(t, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ cpu->isar.id_dfr1 = t; @@ -955,9 +983,6 @@ static void arm_max_initfn(Object *obj) cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; - cpu->isar.dbgdidr = 0x3516d000; - cpu->isar.dbgdevid = 0x00110f13; - cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41013000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ From 02ff2add77099f9deb7d6a18c9b4110572771f6a Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 24 Jun 2024 18:09:15 +0000 Subject: [PATCH 1746/2884] target/arm: Enable FEAT_Debugv8p8 for -cpu max Enable FEAT_Debugv8p8 for max CPU. This feature is out of scope for QEMU since it concerns the external debug interface for JTAG, but is mandatory in Armv8.8 implementations, hence it is reported as supported in the ID registers. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Message-id: 20240624180915.4528-4-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu32.c | 6 +++--- target/arm/tcg/cpu64.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 1a06a5feb6..3ab6e72667 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -41,6 +41,7 @@ the following architecture extensions: - FEAT_Debugv8p1 (Debug with VHE) - FEAT_Debugv8p2 (Debug changes for v8.2) - FEAT_Debugv8p4 (Debug changes for v8.4) +- FEAT_Debugv8p8 (Debug changes for v8.8) - FEAT_DotProd (Advanced SIMD dot product instructions) - FEAT_DoubleFault (Double Fault Extension) - FEAT_E0PD (Preventing EL0 access to halves of address maps) diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 28a5c033bb..20c2737f17 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -82,8 +82,8 @@ void aa32_max_features(ARMCPU *cpu) cpu->isar.id_pfr2 = t; t = cpu->isar.id_dfr0; - t = FIELD_DP32(t, ID_DFR0, COPDBG, 9); /* FEAT_Debugv8p4 */ - t = FIELD_DP32(t, ID_DFR0, COPSDBG, 9); /* FEAT_Debugv8p4 */ + t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ cpu->isar.id_dfr0 = t; @@ -93,7 +93,7 @@ void aa32_max_features(ARMCPU *cpu) t = 0x00008000; t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); - t = FIELD_DP32(t, DBGDIDR, VERSION, 6); /* Armv8 debug */ + t = FIELD_DP32(t, DBGDIDR, VERSION, 10); /* FEAT_Debugv8p8 */ t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); t = FIELD_DP32(t, DBGDIDR, BRPS, 5); t = FIELD_DP32(t, DBGDIDR, WRPS, 3); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 71e1bfcd4e..fe232eb306 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1253,7 +1253,7 @@ void aarch64_max_tcg_initfn(Object *obj) cpu->isar.id_aa64zfr0 = t; t = cpu->isar.id_aa64dfr0; - t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 9); /* FEAT_Debugv8p4 */ + t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* FEAT_Debugv8p8 */ t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* FEAT_PMUv3p5 */ t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* FEAT_HPMN0 */ cpu->isar.id_aa64dfr0 = t; From 6529511a8412a54afbcad850a62c0f39b4a9fc6c Mon Sep 17 00:00:00 2001 From: Patrick Leis Date: Wed, 26 Jun 2024 21:16:22 +0000 Subject: [PATCH 1747/2884] MAINTAINERS: Update my family name Signed-off-by: Patrick Leis Message-id: 20240626211623.3510701-1-venture@google.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 19f67dc5d2..13255d4a3b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2496,7 +2496,7 @@ F: hw/net/tulip.c F: hw/net/tulip.h pca954x -M: Patrick Venture +M: Patrick Leis S: Maintained F: hw/i2c/i2c_mux_pca954x.c F: include/hw/i2c/i2c_mux_pca954x.h From 247f24507f78ed80314b6bd474db8ae1ac7c0253 Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Fri, 21 Jun 2024 18:29:04 +0530 Subject: [PATCH 1748/2884] hw/misc/zynq_slcr: Add boot-mode property boot-mode property sets user values into BOOT_MODE register, on hardware these are derived from board switches. Signed-off-by: Sai Pavan Boddu Reviewed-by: Edgar E. Iglesias Reviewed-by: Francisco Iglesias Message-id: 20240621125906.1300995-2-sai.pavan.boddu@amd.com Signed-off-by: Peter Maydell --- hw/misc/zynq_slcr.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index 3412ff099e..ad814c3a79 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -24,6 +24,8 @@ #include "hw/registerfields.h" #include "hw/qdev-clock.h" #include "qom/object.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" #ifndef ZYNQ_SLCR_ERR_DEBUG #define ZYNQ_SLCR_ERR_DEBUG 0 @@ -121,6 +123,7 @@ REG32(RST_REASON, 0x250) REG32(REBOOT_STATUS, 0x258) REG32(BOOT_MODE, 0x25c) + FIELD(BOOT_MODE, BOOT_MODE, 0, 4) REG32(APU_CTRL, 0x300) REG32(WDT_CLK_SEL, 0x304) @@ -195,6 +198,7 @@ struct ZynqSLCRState { Clock *ps_clk; Clock *uart0_ref_clk; Clock *uart1_ref_clk; + uint8_t boot_mode; }; /* @@ -371,7 +375,7 @@ static void zynq_slcr_reset_init(Object *obj, ResetType type) s->regs[R_FPGA_RST_CTRL] = 0x01F33F0F; s->regs[R_RST_REASON] = 0x00000040; - s->regs[R_BOOT_MODE] = 0x00000001; + s->regs[R_BOOT_MODE] = s->boot_mode & R_BOOT_MODE_BOOT_MODE_MASK; /* 0x700 - 0x7D4 */ for (i = 0; i < 54; i++) { @@ -588,6 +592,15 @@ static const ClockPortInitArray zynq_slcr_clocks = { QDEV_CLOCK_END }; +static void zynq_slcr_realize(DeviceState *dev, Error **errp) +{ + ZynqSLCRState *s = ZYNQ_SLCR(dev); + + if (s->boot_mode > 0xF) { + error_setg(errp, "Invalid boot mode %d specified", s->boot_mode); + } +} + static void zynq_slcr_init(Object *obj) { ZynqSLCRState *s = ZYNQ_SLCR(obj); @@ -610,15 +623,22 @@ static const VMStateDescription vmstate_zynq_slcr = { } }; +static Property zynq_slcr_props[] = { + DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static void zynq_slcr_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_zynq_slcr; + dc->realize = zynq_slcr_realize; rc->phases.enter = zynq_slcr_reset_init; rc->phases.hold = zynq_slcr_reset_hold; rc->phases.exit = zynq_slcr_reset_exit; + device_class_set_props(dc, zynq_slcr_props); } static const TypeInfo zynq_slcr_info = { From 7df3747c92d13a56ddfd8b8fdec06c7c6fddfd66 Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Fri, 21 Jun 2024 18:29:05 +0530 Subject: [PATCH 1749/2884] hw/arm/xilinx_zynq: Add boot-mode property Read boot-mode value as machine property and propagate that to SLCR.BOOT_MODE register. Signed-off-by: Sai Pavan Boddu Acked-by: Edgar E. Iglesias Reviewed-by: Francisco Iglesias Message-id: 20240621125906.1300995-3-sai.pavan.boddu@amd.com Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index c79661bbc1..3c56b9abe1 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -38,6 +38,7 @@ #include "qom/object.h" #include "exec/tswap.h" #include "target/arm/cpu-qom.h" +#include "qapi/visitor.h" #define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9") OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) @@ -90,6 +91,7 @@ struct ZynqMachineState { MachineState parent; Clock *ps_clk; ARMCPU *cpu[ZYNQ_MAX_CPUS]; + uint8_t boot_mode; }; static void zynq_write_board_setup(ARMCPU *cpu, @@ -176,6 +178,27 @@ static inline int zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, return unit; } +static void zynq_set_boot_mode(Object *obj, const char *str, + Error **errp) +{ + ZynqMachineState *m = ZYNQ_MACHINE(obj); + uint8_t mode = 0; + + if (!strncasecmp(str, "qspi", 4)) { + mode = 1; + } else if (!strncasecmp(str, "sd", 2)) { + mode = 5; + } else if (!strncasecmp(str, "nor", 3)) { + mode = 2; + } else if (!strncasecmp(str, "jtag", 4)) { + mode = 0; + } else { + error_setg(errp, "%s boot mode not supported", str); + return; + } + m->boot_mode = mode; +} + static void zynq_init(MachineState *machine) { ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine); @@ -241,6 +264,7 @@ static void zynq_init(MachineState *machine) /* Create slcr, keep a pointer to connect clocks */ slcr = qdev_new("xilinx-zynq_slcr"); qdev_connect_clock_in(slcr, "ps_clk", zynq_machine->ps_clk); + qdev_prop_set_uint8(slcr, "boot-mode", zynq_machine->boot_mode); sysbus_realize_and_unref(SYS_BUS_DEVICE(slcr), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(slcr), 0, 0xF8000000); @@ -373,6 +397,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data) NULL }; MachineClass *mc = MACHINE_CLASS(oc); + ObjectProperty *prop; mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; mc->init = zynq_init; mc->max_cpus = ZYNQ_MAX_CPUS; @@ -380,6 +405,12 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "zynq.ext_ram"; + prop = object_class_property_add_str(oc, "boot-mode", NULL, + zynq_set_boot_mode); + object_class_property_set_description(oc, "boot-mode", + "Supported boot modes:" + " jtag qspi sd nor"); + object_property_set_default_str(prop, "qspi"); } static const TypeInfo zynq_machine_type = { From 2d30506d065743008184a3b0269529bd9159560e Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu Date: Fri, 21 Jun 2024 18:29:06 +0530 Subject: [PATCH 1750/2884] docs/system/arm: Add a doc for zynq board Added the supported device list and an example command. Signed-off-by: Sai Pavan Boddu Reviewed-by: Edgar E. Iglesias Reviewed-by: Francisco Iglesias Message-id: 20240621125906.1300995-4-sai.pavan.boddu@amd.com Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + docs/system/arm/xlnx-zynq.rst | 47 +++++++++++++++++++++++++++++++++++ docs/system/target-arm.rst | 1 + 3 files changed, 49 insertions(+) create mode 100644 docs/system/arm/xlnx-zynq.rst diff --git a/MAINTAINERS b/MAINTAINERS index 13255d4a3b..6725913c8b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1033,6 +1033,7 @@ F: hw/adc/zynq-xadc.c F: include/hw/misc/zynq_slcr.h F: include/hw/adc/zynq-xadc.h X: hw/ssi/xilinx_* +F: docs/system/arm/xlnx-zynq.rst Xilinx ZynqMP and Versal M: Alistair Francis diff --git a/docs/system/arm/xlnx-zynq.rst b/docs/system/arm/xlnx-zynq.rst new file mode 100644 index 0000000000..ade18a3fe1 --- /dev/null +++ b/docs/system/arm/xlnx-zynq.rst @@ -0,0 +1,47 @@ +Xilinx Zynq board (``xilinx-zynq-a9``) +====================================== +The Zynq 7000 family is based on the AMD SoC architecture. These products +integrate a feature-rich dual or single-core Arm Cortex-A9 MPCore based +processing system (PS) and AMD programmable logic (PL) in a single device. + +More details here: +https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/Zynq-7000-SoC-Technical-Reference-Manual + +QEMU xilinx-zynq-a9 board supports following devices: + - A9 MPCORE + - cortex-a9 + - GIC v1 + - Generic timer + - wdt + - OCM 256KB + - SMC SRAM@0xe2000000 64MB + - Zynq SLCR + - SPI x2 + - QSPI + - UART + - TTC x2 + - Gigabit Ethernet Controller x2 + - SD Controller x2 + - XADC + - Arm PrimeCell DMA Controller + - DDR Memory + - USB 2.0 x2 + +Running +""""""" +Direct Linux boot of a generic ARM upstream Linux kernel: + +.. code-block:: bash + + $ qemu-system-aarch64 -M xilinx-zynq-a9 \ + -dtb zynq-zc702.dtb -serial null -serial mon:stdio \ + -display none -m 1024 \ + -initrd rootfs.cpio.gz -kernel zImage + +For configuring the boot-mode provide the following on the command line: + +.. code-block:: bash + + -machine boot-mode=qspi + +Supported values are jtag, sd, qspi, nor. diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 870d30e350..7b99272284 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -109,6 +109,7 @@ undocumented; you can get a complete list by running arm/virt arm/xenpvh arm/xlnx-versal-virt + arm/xlnx-zynq Emulated CPU architecture support ================================= From 7d9b3c34f30045eb50d1c890be46bfde4100bf65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Sat, 29 Jun 2024 12:44:49 +0200 Subject: [PATCH 1751/2884] tests/qtest: Fix STM32L4x5 SYSCFG irq line 15 state assumption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QTest `test_irq_pin_multiplexer` makes the assumption that the reset state of irq line 15 is low, which is false since STM32L4x5 GPIO was implemented (the reset state of pin GPIOA15 is high because there's pull-up and it results in the irq line 15 also being high at reset). It wasn't triggering an error because `test_interrupt` was mistakenly "resetting" the line low. This commit corrects these two mistakes by : - not setting the line low in `test_interrupt` - using an irq line in `test_irq_pin_multiplexer` which is low at reset Signed-off-by: Inès Varhol Message-id: 20240629104454.366283-1-ines.varhol@telecom-paris.fr Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/qtest/stm32l4x5_syscfg-test.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index 506ca08bc2..1cdf8f05c8 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -223,7 +223,7 @@ static void test_interrupt(void) /* Clean the test */ syscfg_writel(SYSCFG_EXTICR1, 0x00000000); syscfg_set_irq(0, 0); - syscfg_set_irq(15, 0); + /* irq 15 is high at reset because GPIOA15 is high at reset */ syscfg_set_irq(17, 0); } @@ -237,21 +237,21 @@ static void test_irq_pin_multiplexer(void) syscfg_set_irq(0, 1); - /* Check that irq 0 was set and irq 15 wasn't */ + /* Check that irq 0 was set and irq 2 wasn't */ g_assert_true(get_irq(0)); - g_assert_false(get_irq(15)); + g_assert_false(get_irq(2)); /* Clean the test */ syscfg_set_irq(0, 0); - syscfg_set_irq(15, 1); + syscfg_set_irq(2, 1); - /* Check that irq 15 was set and irq 0 wasn't */ - g_assert_true(get_irq(15)); + /* Check that irq 2 was set and irq 0 wasn't */ + g_assert_true(get_irq(2)); g_assert_false(get_irq(0)); /* Clean the test */ - syscfg_set_irq(15, 0); + syscfg_set_irq(2, 0); } static void test_irq_gpio_multiplexer(void) From 9c4887e3b68a362f2f5a4e86daab98a51b3b769e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Sat, 29 Jun 2024 13:07:08 +0200 Subject: [PATCH 1752/2884] hw/misc: In STM32L4x5 EXTI, correct configurable interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation of configurable interrupts (interrupts supporting edge selection) was incorrectly expecting alternating input levels : this commits adds a new status field `irq_levels` to actually detect edges. Signed-off-by: Inès Varhol Message-id: 20240629110800.539969-2-ines.varhol@telecom-paris.fr Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/stm32l4x5_exti.c | 28 +++++++++++++--------------- include/hw/misc/stm32l4x5_exti.h | 2 ++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index 495a0004ab..6a2ec62d78 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -88,6 +88,7 @@ static void stm32l4x5_exti_reset_hold(Object *obj, ResetType type) s->ftsr[bank] = 0x00000000; s->swier[bank] = 0x00000000; s->pr[bank] = 0x00000000; + s->irq_levels[bank] = 0x00000000; } } @@ -102,27 +103,23 @@ static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level) /* Shift the value to enable access in x2 registers. */ irq %= EXTI_MAX_IRQ_PER_BANK; + if (level == extract32(s->irq_levels[bank], irq, 1)) { + /* No change in IRQ line state: do nothing */ + return; + } + s->irq_levels[bank] = deposit32(s->irq_levels[bank], irq, 1, level); + /* If the interrupt is masked, pr won't be raised */ if (!extract32(s->imr[bank], irq, 1)) { return; } - if (((1 << irq) & s->rtsr[bank]) && level) { - /* Rising Edge */ - s->pr[bank] |= 1 << irq; - qemu_irq_pulse(s->irq[oirq]); - } else if (((1 << irq) & s->ftsr[bank]) && !level) { - /* Falling Edge */ + if ((level && extract32(s->rtsr[bank], irq, 1)) || + (!level && extract32(s->ftsr[bank], irq, 1))) { + s->pr[bank] |= 1 << irq; qemu_irq_pulse(s->irq[oirq]); } - /* - * In the following situations : - * - falling edge but rising trigger selected - * - rising edge but falling trigger selected - * - no trigger selected - * No action is required - */ } static uint64_t stm32l4x5_exti_read(void *opaque, hwaddr addr, @@ -255,8 +252,8 @@ static void stm32l4x5_exti_init(Object *obj) static const VMStateDescription vmstate_stm32l4x5_exti = { .name = TYPE_STM32L4X5_EXTI, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(imr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), VMSTATE_UINT32_ARRAY(emr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), @@ -264,6 +261,7 @@ static const VMStateDescription vmstate_stm32l4x5_exti = { VMSTATE_UINT32_ARRAY(ftsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), VMSTATE_UINT32_ARRAY(swier, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), VMSTATE_UINT32_ARRAY(pr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(irq_levels, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h index be961d2f01..55f763fa37 100644 --- a/include/hw/misc/stm32l4x5_exti.h +++ b/include/hw/misc/stm32l4x5_exti.h @@ -45,6 +45,8 @@ struct Stm32l4x5ExtiState { uint32_t swier[EXTI_NUM_REGISTER]; uint32_t pr[EXTI_NUM_REGISTER]; + /* used for edge detection */ + uint32_t irq_levels[EXTI_NUM_REGISTER]; qemu_irq irq[EXTI_NUM_INTERRUPT_OUT_LINES]; }; From 58c782de557beb496bfb4c5ade721bbbd2480c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Sat, 29 Jun 2024 13:07:09 +0200 Subject: [PATCH 1753/2884] tests/qtest: Ensure STM32L4x5 EXTI state is correct at the end of QTests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EXTI's new field `irq_levels` tracks irq levels between tests when using `global_qtest`. This happens in `stm32l4x5_exti-test.c`, `stm32l4x5_syscfg-test.c` and `stm32l4x5_gpio-test.c` (`dm163.c` doesn't use `global_qtest`). To ensure that `irq_levels` has the same value before and after each QTest, this commit toggles back the irq lines that were changed at the end of each problematic test. Most QTests were already doing this. Signed-off-by: Inès Varhol Message-id: 20240629110800.539969-3-ines.varhol@telecom-paris.fr Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/qtest/stm32l4x5_exti-test.c | 8 ++++++++ tests/qtest/stm32l4x5_syscfg-test.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c index 7092860b9b..7e39c992fd 100644 --- a/tests/qtest/stm32l4x5_exti-test.c +++ b/tests/qtest/stm32l4x5_exti-test.c @@ -448,6 +448,9 @@ static void test_masked_interrupt(void) g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); /* Check that the interrupt isn't pending in NVIC */ g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Clean EXTI */ + exti_set_irq(1, 0); } static void test_interrupt(void) @@ -498,6 +501,9 @@ static void test_interrupt(void) /* Clean NVIC */ unpend_nvic_irq(EXTI1_IRQ); g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Clean EXTI */ + exti_set_irq(1, 0); } static void test_orred_interrupts(void) @@ -531,6 +537,8 @@ static void test_orred_interrupts(void) unpend_nvic_irq(EXTI5_9_IRQ); g_assert_false(check_nvic_pending(EXTI5_9_IRQ)); + + exti_set_irq(i, 0); } } diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index 1cdf8f05c8..258417cd88 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -221,10 +221,10 @@ static void test_interrupt(void) g_assert_true(get_irq(1)); /* Clean the test */ - syscfg_writel(SYSCFG_EXTICR1, 0x00000000); syscfg_set_irq(0, 0); /* irq 15 is high at reset because GPIOA15 is high at reset */ syscfg_set_irq(17, 0); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); } static void test_irq_pin_multiplexer(void) From 51d59a64eed6c2cd2d2f991f44ffbe21eb33c733 Mon Sep 17 00:00:00 2001 From: Si-Wei Liu Date: Thu, 14 Mar 2024 13:27:34 -0700 Subject: [PATCH 1754/2884] vhost: dirty log should be per backend type There could be a mix of both vhost-user and vhost-kernel clients in the same QEMU process, where separate vhost loggers for the specific vhost type have to be used. Make the vhost logger per backend type, and have them properly reference counted. Suggested-by: Michael S. Tsirkin Signed-off-by: Si-Wei Liu Message-Id: <1710448055-11709-1-git-send-email-si-wei.liu@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 4acd77e890..a1e8b79e1a 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -43,8 +43,8 @@ do { } while (0) #endif -static struct vhost_log *vhost_log; -static struct vhost_log *vhost_log_shm; +static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX]; +static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX]; /* Memslots used by backends that support private memslots (without an fd). */ static unsigned int used_memslots; @@ -287,6 +287,10 @@ static int vhost_set_backend_type(struct vhost_dev *dev, r = -1; } + if (r == 0) { + assert(dev->vhost_ops->backend_type == backend_type); + } + return r; } @@ -319,16 +323,22 @@ static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) return log; } -static struct vhost_log *vhost_log_get(uint64_t size, bool share) +static struct vhost_log *vhost_log_get(VhostBackendType backend_type, + uint64_t size, bool share) { - struct vhost_log *log = share ? vhost_log_shm : vhost_log; + struct vhost_log *log; + + assert(backend_type > VHOST_BACKEND_TYPE_NONE); + assert(backend_type < VHOST_BACKEND_TYPE_MAX); + + log = share ? vhost_log_shm[backend_type] : vhost_log[backend_type]; if (!log || log->size != size) { log = vhost_log_alloc(size, share); if (share) { - vhost_log_shm = log; + vhost_log_shm[backend_type] = log; } else { - vhost_log = log; + vhost_log[backend_type] = log; } } else { ++log->refcnt; @@ -340,11 +350,20 @@ static struct vhost_log *vhost_log_get(uint64_t size, bool share) static void vhost_log_put(struct vhost_dev *dev, bool sync) { struct vhost_log *log = dev->log; + VhostBackendType backend_type; if (!log) { return; } + assert(dev->vhost_ops); + backend_type = dev->vhost_ops->backend_type; + + if (backend_type == VHOST_BACKEND_TYPE_NONE || + backend_type >= VHOST_BACKEND_TYPE_MAX) { + return; + } + --log->refcnt; if (log->refcnt == 0) { /* Sync only the range covered by the old log */ @@ -352,13 +371,13 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); } - if (vhost_log == log) { + if (vhost_log[backend_type] == log) { g_free(log->log); - vhost_log = NULL; - } else if (vhost_log_shm == log) { + vhost_log[backend_type] = NULL; + } else if (vhost_log_shm[backend_type] == log) { qemu_memfd_free(log->log, log->size * sizeof(*(log->log)), log->fd); - vhost_log_shm = NULL; + vhost_log_shm[backend_type] = NULL; } g_free(log); @@ -376,7 +395,8 @@ static bool vhost_dev_log_is_shared(struct vhost_dev *dev) static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) { - struct vhost_log *log = vhost_log_get(size, vhost_dev_log_is_shared(dev)); + struct vhost_log *log = vhost_log_get(dev->vhost_ops->backend_type, + size, vhost_dev_log_is_shared(dev)); uint64_t log_base = (uintptr_t)log->log; int r; @@ -2044,7 +2064,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) uint64_t log_base; hdev->log_size = vhost_get_log_size(hdev); - hdev->log = vhost_log_get(hdev->log_size, + hdev->log = vhost_log_get(hdev->vhost_ops->backend_type, + hdev->log_size, vhost_dev_log_is_shared(hdev)); log_base = (uintptr_t)hdev->log->log; r = hdev->vhost_ops->vhost_set_log_base(hdev, From c5cd7e5f230afb56891e3826fbb60f9e2b6c086a Mon Sep 17 00:00:00 2001 From: Si-Wei Liu Date: Thu, 14 Mar 2024 13:27:35 -0700 Subject: [PATCH 1755/2884] vhost: Perform memory section dirty scans once per iteration On setups with one or more virtio-net devices with vhost on, dirty tracking iteration increases cost the bigger the number amount of queues are set up e.g. on idle guests migration the following is observed with virtio-net with vhost=on: 48 queues -> 78.11% [.] vhost_dev_sync_region.isra.13 8 queues -> 40.50% [.] vhost_dev_sync_region.isra.13 1 queue -> 6.89% [.] vhost_dev_sync_region.isra.13 2 devices, 1 queue -> 18.60% [.] vhost_dev_sync_region.isra.14 With high memory rates the symptom is lack of convergence as soon as it has a vhost device with a sufficiently high number of queues, the sufficient number of vhost devices. On every migration iteration (every 100msecs) it will redundantly query the *shared log* the number of queues configured with vhost that exist in the guest. For the virtqueue data, this is necessary, but not for the memory sections which are the same. So essentially we end up scanning the dirty log too often. To fix that, select a vhost device responsible for scanning the log with regards to memory sections dirty tracking. It is selected when we enable the logger (during migration) and cleared when we disable the logger. If the vhost logger device goes away for some reason, the logger will be re-selected from the rest of vhost devices. After making mem-section logger a singleton instance, constant cost of 7%-9% (like the 1 queue report) will be seen, no matter how many queues or how many vhost devices are configured: 48 queues -> 8.71% [.] vhost_dev_sync_region.isra.13 2 devices, 8 queues -> 7.97% [.] vhost_dev_sync_region.isra.14 Co-developed-by: Joao Martins Signed-off-by: Joao Martins Signed-off-by: Si-Wei Liu Message-Id: <1710448055-11709-2-git-send-email-si-wei.liu@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang --- hw/virtio/vhost.c | 67 +++++++++++++++++++++++++++++++++++---- include/hw/virtio/vhost.h | 1 + 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index a1e8b79e1a..06fc71746e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -45,6 +45,7 @@ static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX]; static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX]; +static QLIST_HEAD(, vhost_dev) vhost_log_devs[VHOST_BACKEND_TYPE_MAX]; /* Memslots used by backends that support private memslots (without an fd). */ static unsigned int used_memslots; @@ -149,6 +150,47 @@ bool vhost_dev_has_iommu(struct vhost_dev *dev) } } +static inline bool vhost_dev_should_log(struct vhost_dev *dev) +{ + assert(dev->vhost_ops); + assert(dev->vhost_ops->backend_type > VHOST_BACKEND_TYPE_NONE); + assert(dev->vhost_ops->backend_type < VHOST_BACKEND_TYPE_MAX); + + return dev == QLIST_FIRST(&vhost_log_devs[dev->vhost_ops->backend_type]); +} + +static inline void vhost_dev_elect_mem_logger(struct vhost_dev *hdev, bool add) +{ + VhostBackendType backend_type; + + assert(hdev->vhost_ops); + + backend_type = hdev->vhost_ops->backend_type; + assert(backend_type > VHOST_BACKEND_TYPE_NONE); + assert(backend_type < VHOST_BACKEND_TYPE_MAX); + + if (add && !QLIST_IS_INSERTED(hdev, logdev_entry)) { + if (QLIST_EMPTY(&vhost_log_devs[backend_type])) { + QLIST_INSERT_HEAD(&vhost_log_devs[backend_type], + hdev, logdev_entry); + } else { + /* + * The first vhost_device in the list is selected as the shared + * logger to scan memory sections. Put new entry next to the head + * to avoid inadvertent change to the underlying logger device. + * This is done in order to get better cache locality and to avoid + * performance churn on the hot path for log scanning. Even when + * new devices come and go quickly, it wouldn't end up changing + * the active leading logger device at all. + */ + QLIST_INSERT_AFTER(QLIST_FIRST(&vhost_log_devs[backend_type]), + hdev, logdev_entry); + } + } else if (!add && QLIST_IS_INSERTED(hdev, logdev_entry)) { + QLIST_REMOVE(hdev, logdev_entry); + } +} + static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, MemoryRegionSection *section, hwaddr first, @@ -166,12 +208,14 @@ static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, start_addr = MAX(first, start_addr); end_addr = MIN(last, end_addr); - for (i = 0; i < dev->mem->nregions; ++i) { - struct vhost_memory_region *reg = dev->mem->regions + i; - vhost_dev_sync_region(dev, section, start_addr, end_addr, - reg->guest_phys_addr, - range_get_last(reg->guest_phys_addr, - reg->memory_size)); + if (vhost_dev_should_log(dev)) { + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + vhost_dev_sync_region(dev, section, start_addr, end_addr, + reg->guest_phys_addr, + range_get_last(reg->guest_phys_addr, + reg->memory_size)); + } } for (i = 0; i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; @@ -383,6 +427,7 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) g_free(log); } + vhost_dev_elect_mem_logger(dev, false); dev->log = NULL; dev->log_size = 0; } @@ -998,6 +1043,15 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) goto err_vq; } } + + /* + * At log start we select our vhost_device logger that will scan the + * memory sections and skip for the others. This is possible because + * the log is shared amongst all vhost devices for a given type of + * backend. + */ + vhost_dev_elect_mem_logger(dev, enable_log); + return 0; err_vq: for (; i >= 0; --i) { @@ -2075,6 +2129,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) VHOST_OPS_DEBUG(r, "vhost_set_log_base failed"); goto fail_log; } + vhost_dev_elect_mem_logger(hdev, true); } if (vrings) { r = vhost_dev_set_vring_enable(hdev, true); diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 02477788df..d75faf46e9 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -129,6 +129,7 @@ struct vhost_dev { void *opaque; struct vhost_log *log; QLIST_ENTRY(vhost_dev) entry; + QLIST_ENTRY(vhost_dev) logdev_entry; QLIST_HEAD(, vhost_iommu) iommu_list; IOMMUNotifier n; const VhostDevConfigOps *config_ops; From 9d5a807c4cb56837f11be9a9250f854fab951290 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 22 Mar 2024 10:23:15 +0100 Subject: [PATCH 1756/2884] vhost-vdpa: check vhost_vdpa_set_vring_ready() return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vhost_vdpa_set_vring_ready() could already fail, but if Linux's patch [1] will be merged, it may fail with more chance if userspace does not activate virtqueues before DRIVER_OK when VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK is not negotiated. So better check its return value anyway. [1] https://lore.kernel.org/virtualization/20240206145154.118044-1-sgarzare@redhat.com/T/#u Acked-by: Eugenio Pérez Acked-by: Jason Wang Signed-off-by: Stefano Garzarella Message-Id: <20240322092315.31885-1-sgarzare@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 85e73dd6a7..eda714d1a4 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -399,7 +399,10 @@ static int vhost_vdpa_net_data_load(NetClientState *nc) } for (int i = 0; i < v->dev->nvqs; ++i) { - vhost_vdpa_set_vring_ready(v, i + v->dev->vq_index); + int ret = vhost_vdpa_set_vring_ready(v, i + v->dev->vq_index); + if (ret < 0) { + return ret; + } } return 0; } @@ -1238,7 +1241,10 @@ static int vhost_vdpa_net_cvq_load(NetClientState *nc) assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); - vhost_vdpa_set_vring_ready(v, v->dev->vq_index); + r = vhost_vdpa_set_vring_ready(v, v->dev->vq_index); + if (unlikely(r < 0)) { + return r; + } if (v->shadow_vqs_enabled) { n = VIRTIO_NET(v->dev->vdev); @@ -1277,7 +1283,10 @@ static int vhost_vdpa_net_cvq_load(NetClientState *nc) } for (int i = 0; i < v->dev->vq_index; ++i) { - vhost_vdpa_set_vring_ready(v, i); + r = vhost_vdpa_set_vring_ready(v, i); + if (unlikely(r < 0)) { + return r; + } } return 0; From cf39b82860b63589460d8797dd70ae3c1647ccca Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Fri, 15 Mar 2024 12:55:52 -0400 Subject: [PATCH 1757/2884] virtio/virtio-pci: Handle extra notification data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to virtio-pci devices for handling the extra data sent from the driver to the device when the VIRTIO_F_NOTIFICATION_DATA transport feature has been negotiated. The extra data that's passed to the virtio-pci device when this feature is enabled varies depending on the device's virtqueue layout. In a split virtqueue layout, this data includes: - upper 16 bits: shadow_avail_idx - lower 16 bits: virtqueue index In a packed virtqueue layout, this data includes: - upper 16 bits: 1-bit wrap counter & 15-bit shadow_avail_idx - lower 16 bits: virtqueue index Signed-off-by: Jonah Palmer Message-Id: <20240315165557.26942-2-jonah.palmer@oracle.com> Reviewed-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 12 +++++++++--- hw/virtio/virtio.c | 18 ++++++++++++++++++ include/hw/virtio/virtio.h | 2 ++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index b1d02f4b3d..cffc7efcae 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -384,7 +384,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - uint16_t vector; + uint16_t vector, vq_idx; hwaddr pa; switch (addr) { @@ -408,8 +408,14 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->queue_sel = val; break; case VIRTIO_PCI_QUEUE_NOTIFY: - if (val < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, val); + vq_idx = val; + if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + VirtQueue *vq = virtio_get_queue(vdev, vq_idx); + + virtio_queue_set_shadow_avail_idx(vq, val >> 16); + } + virtio_queue_notify(vdev, vq_idx); } break; case VIRTIO_PCI_STATUS: diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 893a072c9d..f7c99e3a96 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2264,6 +2264,24 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) } } +void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t shadow_avail_idx) +{ + if (!vq->vring.desc) { + return; + } + + /* + * 16-bit data for packed VQs include 1-bit wrap counter and + * 15-bit shadow_avail_idx. + */ + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + vq->shadow_avail_wrap_counter = (shadow_avail_idx >> 15) & 0x1; + vq->shadow_avail_idx = shadow_avail_idx & 0x7FFF; + } else { + vq->shadow_avail_idx = shadow_avail_idx; + } +} + static void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc && vq->handle_output) { diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 7d5ffdc145..1451926a13 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -307,6 +307,8 @@ int virtio_queue_ready(VirtQueue *vq); int virtio_queue_empty(VirtQueue *vq); +void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t idx); + /* Host binding interface. */ uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr); From 78378f450a723eed34156259ca2861a0c5ca77cf Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Fri, 15 Mar 2024 12:55:53 -0400 Subject: [PATCH 1758/2884] virtio: Prevent creation of device using notification-data with ioeventfd Prevent the realization of a virtio device that attempts to use the VIRTIO_F_NOTIFICATION_DATA transport feature without disabling ioeventfd. Due to ioeventfd not being able to carry the extra data associated with this feature, having both enabled is a functional mismatch and therefore Qemu should not continue the device's realization process. Although the device does not yet know if the feature will be successfully negotiated, many devices using this feature wont actually work without this extra data and would fail FEATURES_OK anyway. If ioeventfd is able to work with the extra notification data in the future, this compatibility check can be removed. Signed-off-by: Jonah Palmer Message-Id: <20240315165557.26942-3-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f7c99e3a96..28cd406e16 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2980,6 +2980,20 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } +static void virtio_device_check_notification_compatibility(VirtIODevice *vdev, + Error **errp) +{ + VirtioBusState *bus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); + DeviceState *proxy = DEVICE(BUS(bus)->parent); + + if (virtio_host_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA) && + k->ioeventfd_enabled(proxy)) { + error_setg(errp, + "notification_data=on without ioeventfd=off is not supported"); + } +} + size_t virtio_get_config_size(const VirtIOConfigSizeParams *params, uint64_t host_features) { @@ -3740,6 +3754,14 @@ static void virtio_device_realize(DeviceState *dev, Error **errp) } } + /* Devices should not use both ioeventfd and notification data feature */ + virtio_device_check_notification_compatibility(vdev, &err); + if (err != NULL) { + error_propagate(errp, err); + vdc->unrealize(dev); + return; + } + virtio_bus_device_plugged(vdev, &err); if (err != NULL) { error_propagate(errp, err); From 54869366be60af2eb52cffaedad73ba1f4247e15 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Fri, 15 Mar 2024 12:55:54 -0400 Subject: [PATCH 1759/2884] virtio-mmio: Handle extra notification data Add support to virtio-mmio devices for handling the extra data sent from the driver to the device when the VIRTIO_F_NOTIFICATION_DATA transport feature has been negotiated. The extra data that's passed to the virtio-mmio device when this feature is enabled varies depending on the device's virtqueue layout. The data passed to the virtio-mmio device is in the same format as the data passed to virtio-pci devices. Signed-off-by: Jonah Palmer Message-Id: <20240315165557.26942-4-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-mmio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 22f9fbcf5a..320428ac0d 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -248,6 +248,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint16_t vq_idx; trace_virtio_mmio_write_offset(offset, value); @@ -407,8 +408,14 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, } break; case VIRTIO_MMIO_QUEUE_NOTIFY: - if (value < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, value); + vq_idx = value; + if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + VirtQueue *vq = virtio_get_queue(vdev, vq_idx); + + virtio_queue_set_shadow_avail_idx(vq, (value >> 16) & 0xFFFF); + } + virtio_queue_notify(vdev, vq_idx); } break; case VIRTIO_MMIO_INTERRUPT_ACK: From 594b543a4a75d08f47e5ea92c96a89502a3eab72 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Fri, 15 Mar 2024 12:55:55 -0400 Subject: [PATCH 1760/2884] virtio-ccw: Handle extra notification data Add support to virtio-ccw devices for handling the extra data sent from the driver to the device when the VIRTIO_F_NOTIFICATION_DATA transport feature has been negotiated. The extra data that's passed to the virtio-ccw device when this feature is enabled varies depending on the device's virtqueue layout. That data passed to the virtio-ccw device is in the same format as the data passed to virtio-pci devices. Signed-off-by: Jonah Palmer Message-Id: <20240315165557.26942-5-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/s390x/s390-virtio-ccw.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index cd063f8b64..8cd912f20e 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -126,9 +126,11 @@ static void subsystem_reset(void) static int virtio_ccw_hcall_notify(const uint64_t *args) { uint64_t subch_id = args[0]; - uint64_t queue = args[1]; + uint64_t data = args[1]; SubchDev *sch; + VirtIODevice *vdev; int cssid, ssid, schid, m; + uint16_t vq_idx = data; if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { return -EINVAL; @@ -137,12 +139,19 @@ static int virtio_ccw_hcall_notify(const uint64_t *args) if (!sch || !css_subch_visible(sch)) { return -EINVAL; } - if (queue >= VIRTIO_QUEUE_MAX) { + + vdev = virtio_ccw_get_vdev(sch); + if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { return -EINVAL; } - virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); - return 0; + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx), + (data >> 16) & 0xFFFF); + } + + virtio_queue_notify(vdev, vq_idx); + return 0; } static int virtio_ccw_hcall_early_printk(const uint64_t *args) From b937fa896321fb7b6d7f2205edb5490e0e5d6c69 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Fri, 15 Mar 2024 12:55:56 -0400 Subject: [PATCH 1761/2884] vhost/vhost-user: Add VIRTIO_F_NOTIFICATION_DATA to vhost feature bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the VIRTIO_F_NOTIFICATION_DATA feature across a variety of vhost devices. The inclusion of VIRTIO_F_NOTIFICATION_DATA in the feature bits arrays for these devices ensures that the backend is capable of offering and providing support for this feature, and that it can be disabled if the backend does not support it. Tested-by: Lei Yang Reviewed-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20240315165557.26942-6-jonah.palmer@oracle.com> Acked-by: Srujana Challa Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/vhost-user-blk.c | 1 + hw/net/vhost_net.c | 2 ++ hw/scsi/vhost-scsi.c | 1 + hw/scsi/vhost-user-scsi.c | 1 + hw/virtio/vhost-user-fs.c | 2 +- hw/virtio/vhost-user-vsock.c | 1 + net/vhost-vdpa.c | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 9e6bbc6950..bc2677dbef 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -51,6 +51,7 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_RESET, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index fd1a93701a..18898afe81 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -48,6 +48,7 @@ static const int kernel_feature_bits[] = { VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, + VIRTIO_F_NOTIFICATION_DATA, VIRTIO_NET_F_HASH_REPORT, VHOST_INVALID_FEATURE_BIT }; @@ -55,6 +56,7 @@ static const int kernel_feature_bits[] = { /* Features supported by others. */ static const int user_feature_bits[] = { VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index ae26bc19a4..3d5fe0994d 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -38,6 +38,7 @@ static const int kernel_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_F_RING_RESET, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index a63b1f4948..0b050805a8 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -36,6 +36,7 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_F_RING_RESET, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index cca2cd41be..ae48cc1c96 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -33,7 +33,7 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_RESET, - + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 9431b9792c..802b44a07d 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -21,6 +21,7 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index eda714d1a4..daa38428c5 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -62,6 +62,7 @@ const int vdpa_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, VIRTIO_F_VERSION_1, + VIRTIO_F_NOTIFICATION_DATA, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, VIRTIO_NET_F_CTRL_MAC_ADDR, From 5093bee0fa8a6c9712c96653da3a79bc37a4e45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20P=C3=B6tzsch?= Date: Fri, 26 Apr 2024 10:33:13 +0200 Subject: [PATCH 1762/2884] Fix vhost user assertion when sending more than one fd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the client sends more than one region this assert triggers. The reason is that two fd's are 8 bytes and VHOST_MEMORY_BASELINE_NREGIONS is exactly 8. The assert is wrong because it should not test for the size of the fd array, but for the numbers of regions. Signed-off-by: Christian Pötzsch Message-Id: <20240426083313.3081272-1-christian.poetzsch@kernkonzept.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- subprojects/libvhost-user/libvhost-user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index a879149fef..8adb277d54 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -568,7 +568,7 @@ vu_message_read_default(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { fd_size = cmsg->cmsg_len - CMSG_LEN(0); vmsg->fd_num = fd_size / sizeof(int); - assert(fd_size < VHOST_MEMORY_BASELINE_NREGIONS); + assert(vmsg->fd_num <= VHOST_MEMORY_BASELINE_NREGIONS); memcpy(vmsg->fds, CMSG_DATA(cmsg), fd_size); break; } From a0eebd790ca4f90fc1e3662cb38542ccc21963bf Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Mon, 29 Apr 2024 13:33:34 +0200 Subject: [PATCH 1763/2884] vhost-vsock: add VIRTIO_F_RING_PACKED to feature_bits Not having VIRTIO_F_RING_PACKED in feature_bits[] is a problem when the vhost-vsock device does not offer the feature bit VIRTIO_F_RING_PACKED but the in QEMU device is configured to try to use the packed layout (the virtio property "packed" is on). As of today, the Linux kernel vhost-vsock device does not support the packed queue layout (as vhost does not support packed), and does not offer VIRTIO_F_RING_PACKED. Thus when for example a vhost-vsock-ccw is used with packed=on, VIRTIO_F_RING_PACKED ends up being negotiated, despite the fact that the device does not actually support it, and one gets to keep the pieces. Fixes: 74b3e46630 ("virtio: add property to enable packed virtqueue") Reported-by: Marc Hartmayer Signed-off-by: Halil Pasic Message-Id: <20240429113334.2454197-1-pasic@linux.ibm.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vsock-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 12ea87d7a7..fd88df2560 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -22,6 +22,7 @@ const int feature_bits[] = { VIRTIO_VSOCK_F_SEQPACKET, VIRTIO_F_RING_RESET, + VIRTIO_F_RING_PACKED, VHOST_INVALID_FEATURE_BIT }; From 33abfea239592a706e98269b01c0096249612ea4 Mon Sep 17 00:00:00 2001 From: Wafer Date: Fri, 10 May 2024 15:27:53 +0800 Subject: [PATCH 1764/2884] hw/virtio: Fix obtain the buffer id from the last descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The virtio-1.3 specification writes: 2.8.6 Next Flag: Descriptor Chaining Buffer ID is included in the last descriptor in the list. If the feature (_F_INDIRECT_DESC) has been negotiated, install only one descriptor in the virtqueue. Therefor the buffer id should be obtained from the first descriptor. In descriptor chaining scenarios, the buffer id should be obtained from the last descriptor. Fixes: 86044b24e8 ("virtio: basic packed virtqueue support") Signed-off-by: Wafer Reviewed-by: Jason Wang Reviewed-by: Eugenio Pérez Acked-by: Jason Wang Message-Id: <20240510072753.26158-2-wafer@jaguarmicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 28cd406e16..3678ec2f88 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1745,6 +1745,11 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) &indirect_desc_cache); } while (rc == VIRTQUEUE_READ_DESC_MORE); + if (desc_cache != &indirect_desc_cache) { + /* Buffer ID is included in the last descriptor in the list. */ + id = desc.id; + } + /* Now copy what we have collected and mapped */ elem = virtqueue_alloc_element(sz, out_num, in_num); for (i = 0; i < out_num; i++) { From 84b58169e40f5c7428db6f0b229e01213068aa21 Mon Sep 17 00:00:00 2001 From: Jiqian Chen Date: Wed, 15 May 2024 15:35:25 +0800 Subject: [PATCH 1765/2884] virtio-pci: only reset pm state during resetting Fix bug imported by 27ce0f3afc9dd ("fix Power Management Control Register for PCI Express virtio devices" After this change, observe that QEMU may erroneously clear the power status of the device, or may erroneously clear non writable registers, such as NO_SOFT_RESET, etc. Only state of PM_CTRL is writable. Only when flag VIRTIO_PCI_FLAG_INIT_PM is set, need to reset state. Fixes: 27ce0f3afc9dd ("fix Power Management Control Register for PCI Express virtio devices" Signed-off-by: Jiqian Chen Message-Id: <20240515073526.17297-2-Jiqian.Chen@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index cffc7efcae..7d62e92365 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2306,10 +2306,16 @@ static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) virtio_pci_reset(qdev); if (pci_is_express(dev)) { + VirtIOPCIProxy *proxy = VIRTIO_PCI(dev); + pcie_cap_deverr_reset(dev); pcie_cap_lnkctl_reset(dev); - pci_set_word(dev->config + dev->exp.pm_cap + PCI_PM_CTRL, 0); + if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) { + pci_word_test_and_clear_mask( + dev->config + dev->exp.pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + } } } From 80c8a26de5f1b7d67d4594957c0d82a0c47626be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 15 May 2024 14:52:37 +0400 Subject: [PATCH 1766/2884] vhost-user-gpu: fix import of DMABUF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using vhost-user-gpu with GL, qemu -display gtk doesn't show output and prints: qemu: eglCreateImageKHR failed Since commit 9ac06df8b ("virtio-gpu-udmabuf: correct naming of QemuDmaBuf size properties"), egl_dmabuf_import_texture() uses backing_{width,height} for the texture dimension. Fixes: 9ac06df8b ("virtio-gpu-udmabuf: correct naming of QemuDmaBuf size properties") Signed-off-by: Marc-André Lureau Message-Id: <20240515105237.1074116-1-marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/display/vhost-user-gpu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index e4b398d26c..63c64ddde6 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -281,8 +281,9 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) modifier = m2->modifier; } - dmabuf = qemu_dmabuf_new(m->fd_width, m->fd_height, - m->fd_stride, 0, 0, 0, 0, + dmabuf = qemu_dmabuf_new(m->width, m->height, + m->fd_stride, 0, 0, + m->fd_width, m->fd_height, m->fd_drm_fourcc, modifier, fd, false, m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP); From 9569fe0aacbe6c7752935c9ede427ca1e8aafe51 Mon Sep 17 00:00:00 2001 From: Li Feng Date: Thu, 16 May 2024 10:57:45 +0800 Subject: [PATCH 1767/2884] Revert "vhost-user: fix lost reconnect" This reverts commit f02a4b8e6431598612466f76aac64ab492849abf. Since the current patch cannot completely fix the lost reconnect problem, there is a scenario that is not considered: - When the virtio-blk driver is removed from the guest os, s->connected has no chance to be set to false, resulting in subsequent reconnection not being executed. The next patch will completely fix this issue with a better approach. Signed-off-by: Li Feng Message-Id: <20240516025753.130171-2-fengli@smartx.com> Reviewed-by: Raphael Norwitz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/vhost-user-blk.c | 2 +- hw/scsi/vhost-user-scsi.c | 3 +-- hw/virtio/vhost-user-base.c | 2 +- hw/virtio/vhost-user.c | 10 ++-------- include/hw/virtio/vhost-user.h | 3 +-- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index bc2677dbef..15cc24d017 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -385,7 +385,7 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) case CHR_EVENT_CLOSED: /* defer close until later to avoid circular close */ vhost_user_async_close(dev, &s->chardev, &s->dev, - vhost_user_blk_disconnect, vhost_user_blk_event); + vhost_user_blk_disconnect); break; case CHR_EVENT_BREAK: case CHR_EVENT_MUX_IN: diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 0b050805a8..421cd654f8 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -215,8 +215,7 @@ static void vhost_user_scsi_event(void *opaque, QEMUChrEvent event) case CHR_EVENT_CLOSED: /* defer close until later to avoid circular close */ vhost_user_async_close(dev, &vs->conf.chardev, &vsc->dev, - vhost_user_scsi_disconnect, - vhost_user_scsi_event); + vhost_user_scsi_disconnect); break; case CHR_EVENT_BREAK: case CHR_EVENT_MUX_IN: diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c index a83167191e..4b54255682 100644 --- a/hw/virtio/vhost-user-base.c +++ b/hw/virtio/vhost-user-base.c @@ -254,7 +254,7 @@ static void vub_event(void *opaque, QEMUChrEvent event) case CHR_EVENT_CLOSED: /* defer close until later to avoid circular close */ vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev, - vub_disconnect, vub_event); + vub_disconnect); break; case CHR_EVENT_BREAK: case CHR_EVENT_MUX_IN: diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index cdf9af4a4b..c929097e87 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2776,7 +2776,6 @@ typedef struct { DeviceState *dev; CharBackend *cd; struct vhost_dev *vhost; - IOEventHandler *event_cb; } VhostAsyncCallback; static void vhost_user_async_close_bh(void *opaque) @@ -2791,10 +2790,7 @@ static void vhost_user_async_close_bh(void *opaque) */ if (vhost->vdev) { data->cb(data->dev); - } else if (data->event_cb) { - qemu_chr_fe_set_handlers(data->cd, NULL, NULL, data->event_cb, - NULL, data->dev, NULL, true); - } + } g_free(data); } @@ -2806,8 +2802,7 @@ static void vhost_user_async_close_bh(void *opaque) */ void vhost_user_async_close(DeviceState *d, CharBackend *chardev, struct vhost_dev *vhost, - vu_async_close_fn cb, - IOEventHandler *event_cb) + vu_async_close_fn cb) { if (!runstate_check(RUN_STATE_SHUTDOWN)) { /* @@ -2823,7 +2818,6 @@ void vhost_user_async_close(DeviceState *d, data->dev = d; data->cd = chardev; data->vhost = vhost; - data->event_cb = event_cb; /* Disable any further notifications on the chardev */ qemu_chr_fe_set_handlers(chardev, diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index d7c09ffd34..324cd8663a 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h @@ -108,7 +108,6 @@ typedef void (*vu_async_close_fn)(DeviceState *cb); void vhost_user_async_close(DeviceState *d, CharBackend *chardev, struct vhost_dev *vhost, - vu_async_close_fn cb, - IOEventHandler *event_cb); + vu_async_close_fn cb); #endif From 6eaf0e612b415877d1c411b95bed2ecb53b546bb Mon Sep 17 00:00:00 2001 From: Li Feng Date: Thu, 16 May 2024 10:57:46 +0800 Subject: [PATCH 1768/2884] vhost-user: fix lost reconnect again When the vhost-user is reconnecting to the backend, and if the vhost-user fails at the get_features in vhost_dev_init(), then the reconnect will fail and it will not be retriggered forever. The reason is: When the vhost-user fail at get_features, the vhost_dev_cleanup will be called immediately. vhost_dev_cleanup calls 'memset(hdev, 0, sizeof(struct vhost_dev))'. The reconnect path is: vhost_user_blk_event vhost_user_async_close(.. vhost_user_blk_disconnect ..) qemu_chr_fe_set_handlers <----- clear the notifier callback schedule vhost_user_async_close_bh The vhost->vdev is null, so the vhost_user_blk_disconnect will not be called, then the event fd callback will not be reinstalled. We need to ensure that even if vhost_dev_init initialization fails, the event handler still needs to be reinstalled when s->connected is false. All vhost-user devices have this issue, including vhost-user-blk/scsi. Fixes: 71e076a07d ("hw/virtio: generalise CHR_EVENT_CLOSED handling") Signed-off-by: Li Feng Message-Id: <20240516025753.130171-3-fengli@smartx.com> Reviewed-by: Raphael Norwitz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/vhost-user-blk.c | 3 ++- hw/scsi/vhost-user-scsi.c | 3 ++- hw/virtio/vhost-user-base.c | 3 ++- hw/virtio/vhost-user.c | 10 +--------- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 15cc24d017..fdbc30b9ce 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -354,7 +354,7 @@ static void vhost_user_blk_disconnect(DeviceState *dev) VHostUserBlk *s = VHOST_USER_BLK(vdev); if (!s->connected) { - return; + goto done; } s->connected = false; @@ -362,6 +362,7 @@ static void vhost_user_blk_disconnect(DeviceState *dev) vhost_dev_cleanup(&s->dev); +done: /* Re-instate the event handler for new connections */ qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event, NULL, dev, NULL, true); diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 421cd654f8..cc91ade525 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -182,7 +182,7 @@ static void vhost_user_scsi_disconnect(DeviceState *dev) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); if (!s->connected) { - return; + goto done; } s->connected = false; @@ -190,6 +190,7 @@ static void vhost_user_scsi_disconnect(DeviceState *dev) vhost_dev_cleanup(&vsc->dev); +done: /* Re-instate the event handler for new connections */ qemu_chr_fe_set_handlers(&vs->conf.chardev, NULL, NULL, vhost_user_scsi_event, NULL, dev, NULL, true); diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c index 4b54255682..11e72b1e3b 100644 --- a/hw/virtio/vhost-user-base.c +++ b/hw/virtio/vhost-user-base.c @@ -225,13 +225,14 @@ static void vub_disconnect(DeviceState *dev) VHostUserBase *vub = VHOST_USER_BASE(vdev); if (!vub->connected) { - return; + goto done; } vub->connected = false; vub_stop(vdev); vhost_dev_cleanup(&vub->vhost_dev); +done: /* Re-instate the event handler for new connections */ qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index c929097e87..c407ea8939 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -2781,16 +2781,8 @@ typedef struct { static void vhost_user_async_close_bh(void *opaque) { VhostAsyncCallback *data = opaque; - struct vhost_dev *vhost = data->vhost; - /* - * If the vhost_dev has been cleared in the meantime there is - * nothing left to do as some other path has completed the - * cleanup. - */ - if (vhost->vdev) { - data->cb(data->dev); - } + data->cb(data->dev); g_free(data); } From 05b70ceba033759d44c6d3d9b24118cd9fc9d616 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Thu, 23 May 2024 10:44:41 -0700 Subject: [PATCH 1769/2884] hw/cxl/mailbox: change CCI cmd set structure to be a member, not a reference This allows devices to have fully customized CCIs, along with complex devices where wrapper devices can override or add additional CCI commands without having to replicate full command structures or pollute a base device with every command that might ever be used. Signed-off-by: Gregory Price Signed-off-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-2-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 19 +++++++++++++++---- include/hw/cxl/cxl_device.h | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index e5eb97cb91..2c9f50f0f9 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1447,10 +1447,21 @@ void cxl_init_cci(CXLCCI *cci, size_t payload_max) bg_timercb, cci); } +static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[256]) +{ + for (int set = 0; set < 256; set++) { + for (int cmd = 0; cmd < 256; cmd++) { + if (cxl_cmds[set][cmd].handler) { + cci->cxl_cmd_set[set][cmd] = cxl_cmds[set][cmd]; + } + } + } +} + void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, DeviceState *d, size_t payload_max) { - cci->cxl_cmd_set = cxl_cmd_set_sw; + cxl_copy_cci_commands(cci, cxl_cmd_set_sw); cci->d = d; cci->intf = intf; cxl_init_cci(cci, payload_max); @@ -1458,7 +1469,7 @@ void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) { - cci->cxl_cmd_set = cxl_cmd_set; + cxl_copy_cci_commands(cci, cxl_cmd_set); cci->d = d; /* No separation for PCI MB as protocol handled in PCI device */ @@ -1476,7 +1487,7 @@ static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = { void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf, size_t payload_max) { - cci->cxl_cmd_set = cxl_cmd_set_t3_ld; + cxl_copy_cci_commands(cci, cxl_cmd_set_t3_ld); cci->d = d; cci->intf = intf; cxl_init_cci(cci, payload_max); @@ -1496,7 +1507,7 @@ void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *intf, size_t payload_max) { - cci->cxl_cmd_set = cxl_cmd_set_t3_fm_owned_ld_mctp; + cxl_copy_cci_commands(cci, cxl_cmd_set_t3_fm_owned_ld_mctp); cci->d = d; cci->intf = intf; cxl_init_cci(cci, payload_max); diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 279b276bda..ccc4611875 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -164,7 +164,7 @@ typedef struct CXLEventLog { } CXLEventLog; typedef struct CXLCCI { - const struct cxl_cmd (*cxl_cmd_set)[256]; + struct cxl_cmd cxl_cmd_set[256][256]; struct cel_log { uint16_t opcode; uint16_t effect; From 67adb7979b6c1151a906d99f19fccf1e789316b1 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Thu, 23 May 2024 10:44:42 -0700 Subject: [PATCH 1770/2884] hw/cxl/mailbox: interface to add CCI commands to an existing CCI This enables wrapper devices to customize the base device's CCI (for example, with custom commands outside the specification) without the need to change the base device. The also enabled the base device to dispatch those commands without requiring additional driver support. Heavily edited by Jonathan Cameron to increase code reuse Signed-off-by: Gregory Price Signed-off-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-3-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 19 +++++++++++++++++-- include/hw/cxl/cxl_device.h | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 2c9f50f0f9..2a64c58e2f 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1424,9 +1424,9 @@ static void bg_timercb(void *opaque) } } -void cxl_init_cci(CXLCCI *cci, size_t payload_max) +static void cxl_rebuild_cel(CXLCCI *cci) { - cci->payload_max = payload_max; + cci->cel_size = 0; /* Reset for a fresh build */ for (int set = 0; set < 256; set++) { for (int cmd = 0; cmd < 256; cmd++) { if (cci->cxl_cmd_set[set][cmd].handler) { @@ -1440,6 +1440,13 @@ void cxl_init_cci(CXLCCI *cci, size_t payload_max) } } } +} + +void cxl_init_cci(CXLCCI *cci, size_t payload_max) +{ + cci->payload_max = payload_max; + cxl_rebuild_cel(cci); + cci->bg.complete_pct = 0; cci->bg.starttime = 0; cci->bg.runtime = 0; @@ -1458,6 +1465,14 @@ static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[ } } +void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256], + size_t payload_max) +{ + cci->payload_max = MAX(payload_max, cci->payload_max); + cxl_copy_cci_commands(cci, cxl_cmd_set); + cxl_rebuild_cel(cci); +} + void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, DeviceState *d, size_t payload_max) { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index ccc4611875..a5f8e25020 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -301,6 +301,8 @@ void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max); void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, DeviceState *d, size_t payload_max); void cxl_init_cci(CXLCCI *cci, size_t payload_max); +void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256], + size_t payload_max); int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, size_t len_in, uint8_t *pl_in, size_t *len_out, uint8_t *pl_out, From 7a21e5dedbbcec11ebab7a53186085f09a53f9e7 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:43 -0700 Subject: [PATCH 1771/2884] hw/cxl/cxl-mailbox-utils: Add dc_event_log_size field to output payload of identify memory device command Based on CXL spec r3.1 Table 8-127 (Identify Memory Device Output Payload), dynamic capacity event log size should be part of output of the Identify command. Add dc_event_log_size to the output payload for the host to get the info. Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-4-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 2a64c58e2f..626acc1d0d 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -21,6 +21,7 @@ #include "sysemu/hostmem.h" #define CXL_CAPACITY_MULTIPLIER (256 * MiB) +#define CXL_DC_EVENT_LOG_SIZE 8 /* * How to add a new command, example. The command set FOO, with cmd BAR. @@ -780,8 +781,9 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, uint16_t inject_poison_limit; uint8_t poison_caps; uint8_t qos_telemetry_caps; + uint16_t dc_event_log_size; } QEMU_PACKED *id; - QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43); + QEMU_BUILD_BUG_ON(sizeof(*id) != 0x45); CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; @@ -807,6 +809,7 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, st24_le_p(id->poison_list_max_mer, 256); /* No limit - so limited by main poison record limit */ stw_le_p(&id->inject_poison_limit, 0); + stw_le_p(&id->dc_event_log_size, CXL_DC_EVENT_LOG_SIZE); *len_out = sizeof(*id); return CXL_MBOX_SUCCESS; From 0f0f140b100392fd938eb6933752155ea68b26a8 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:44 -0700 Subject: [PATCH 1772/2884] hw/cxl/cxl-mailbox-utils: Add dynamic capacity region representative and mailbox command support Per cxl spec r3.1, add dynamic capacity (DC) region representative based on Table 8-165 and extend the cxl type3 device definition to include DC region information. Also, based on info in 8.2.9.9.9.1, add 'Get Dynamic Capacity Configuration' mailbox support. Note: we store region decode length as byte-wise length on the device, which should be divided by 256 * MiB before being returned to the host for "Get Dynamic Capacity Configuration" mailbox command per specification. Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-5-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 96 +++++++++++++++++++++++++++++++++++++ include/hw/cxl/cxl_device.h | 16 +++++++ 2 files changed, 112 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 626acc1d0d..bede28e3c8 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -22,6 +22,8 @@ #define CXL_CAPACITY_MULTIPLIER (256 * MiB) #define CXL_DC_EVENT_LOG_SIZE 8 +#define CXL_NUM_EXTENTS_SUPPORTED 512 +#define CXL_NUM_TAGS_SUPPORTED 0 /* * How to add a new command, example. The command set FOO, with cmd BAR. @@ -80,6 +82,8 @@ enum { #define GET_POISON_LIST 0x0 #define INJECT_POISON 0x1 #define CLEAR_POISON 0x2 + DCD_CONFIG = 0x48, + #define GET_DC_CONFIG 0x0 PHYSICAL_SWITCH = 0x51, #define IDENTIFY_SWITCH_DEVICE 0x0 #define GET_PHYSICAL_PORT_STATE 0x1 @@ -1238,6 +1242,88 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* + * CXL r3.1 section 8.2.9.9.9.1: Get Dynamic Capacity Configuration + * (Opcode: 4800h) + */ +static CXLRetCode cmd_dcd_get_dyn_cap_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + struct { + uint8_t region_cnt; + uint8_t start_rid; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint8_t num_regions; + uint8_t regions_returned; + uint8_t rsvd1[6]; + struct { + uint64_t base; + uint64_t decode_len; + uint64_t region_len; + uint64_t block_size; + uint32_t dsmadhandle; + uint8_t flags; + uint8_t rsvd2[3]; + } QEMU_PACKED records[]; + } QEMU_PACKED *out = (void *)payload_out; + struct { + uint32_t num_extents_supported; + uint32_t num_extents_available; + uint32_t num_tags_supported; + uint32_t num_tags_available; + } QEMU_PACKED *extra_out; + uint16_t record_count; + uint16_t i; + uint16_t out_pl_len; + uint8_t start_rid; + + start_rid = in->start_rid; + if (start_rid >= ct3d->dc.num_regions) { + return CXL_MBOX_INVALID_INPUT; + } + + record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt); + + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + extra_out = (void *)(payload_out + out_pl_len); + out_pl_len += sizeof(*extra_out); + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + out->num_regions = ct3d->dc.num_regions; + out->regions_returned = record_count; + for (i = 0; i < record_count; i++) { + stq_le_p(&out->records[i].base, + ct3d->dc.regions[start_rid + i].base); + stq_le_p(&out->records[i].decode_len, + ct3d->dc.regions[start_rid + i].decode_len / + CXL_CAPACITY_MULTIPLIER); + stq_le_p(&out->records[i].region_len, + ct3d->dc.regions[start_rid + i].len); + stq_le_p(&out->records[i].block_size, + ct3d->dc.regions[start_rid + i].block_size); + stl_le_p(&out->records[i].dsmadhandle, + ct3d->dc.regions[start_rid + i].dsmadhandle); + out->records[i].flags = ct3d->dc.regions[start_rid + i].flags; + } + /* + * TODO: Assign values once extents and tags are introduced + * to use. + */ + stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED); + stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED); + stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED); + stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED); + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + #define IMMEDIATE_CONFIG_CHANGE (1 << 1) #define IMMEDIATE_DATA_CHANGE (1 << 2) #define IMMEDIATE_POLICY_CHANGE (1 << 3) @@ -1282,6 +1368,11 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_media_clear_poison, 72, 0 }, }; +static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { + [DCD_CONFIG][GET_DC_CONFIG] = { "DCD_GET_DC_CONFIG", + cmd_dcd_get_dyn_cap_config, 2, 0 }, +}; + static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS", @@ -1487,7 +1578,12 @@ void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) { + CXLType3Dev *ct3d = CXL_TYPE3(d); + cxl_copy_cci_commands(cci, cxl_cmd_set); + if (ct3d->dc.num_regions) { + cxl_copy_cci_commands(cci, cxl_cmd_set_dcd); + } cci->d = d; /* No separation for PCI MB as protocol handled in PCI device */ diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index a5f8e25020..e839370266 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -422,6 +422,17 @@ typedef struct CXLPoison { typedef QLIST_HEAD(, CXLPoison) CXLPoisonList; #define CXL_POISON_LIST_LIMIT 256 +#define DCD_MAX_NUM_REGION 8 + +typedef struct CXLDCRegion { + uint64_t base; /* aligned to 256*MiB */ + uint64_t decode_len; /* aligned to 256*MiB */ + uint64_t len; + uint64_t block_size; + uint32_t dsmadhandle; + uint8_t flags; +} CXLDCRegion; + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; @@ -454,6 +465,11 @@ struct CXLType3Dev { unsigned int poison_list_cnt; bool poison_list_overflowed; uint64_t poison_list_overflow_ts; + + struct dynamic_capacity { + uint8_t num_regions; /* 0-8 regions */ + CXLDCRegion regions[DCD_MAX_NUM_REGION]; + } dc; }; #define TYPE_CXL_TYPE3 "cxl-type3" From 25851080772387ae33d6ee94250b3a31bf719e5c Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:45 -0700 Subject: [PATCH 1773/2884] include/hw/cxl/cxl_device: Rename mem_size as static_mem_size for type3 memory devices Rename mem_size as static_mem_size for type3 memdev to cover static RAM and pmem capacity, preparing for the introduction of dynamic capacity to support dynamic capacity devices. Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-6-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 4 ++-- hw/mem/cxl_type3.c | 8 ++++---- include/hw/cxl/cxl_device.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index bede28e3c8..b592473587 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -803,7 +803,7 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); stq_le_p(&id->total_capacity, - cxl_dstate->mem_size / CXL_CAPACITY_MULTIPLIER); + cxl_dstate->static_mem_size / CXL_CAPACITY_MULTIPLIER); stq_le_p(&id->persistent_capacity, cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); stq_le_p(&id->volatile_capacity, @@ -1179,7 +1179,7 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, struct clear_poison_pl *in = (void *)payload_in; dpa = ldq_le_p(&in->dpa); - if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->mem_size) { + if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->static_mem_size) { return CXL_MBOX_INVALID_PA; } diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 3e42490b6c..7194c8f902 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -608,7 +608,7 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) } address_space_init(&ct3d->hostvmem_as, vmr, v_name); ct3d->cxl_dstate.vmem_size = memory_region_size(vmr); - ct3d->cxl_dstate.mem_size += memory_region_size(vmr); + ct3d->cxl_dstate.static_mem_size += memory_region_size(vmr); g_free(v_name); } @@ -631,7 +631,7 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) } address_space_init(&ct3d->hostpmem_as, pmr, p_name); ct3d->cxl_dstate.pmem_size = memory_region_size(pmr); - ct3d->cxl_dstate.mem_size += memory_region_size(pmr); + ct3d->cxl_dstate.static_mem_size += memory_region_size(pmr); g_free(p_name); } @@ -837,7 +837,7 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, return -EINVAL; } - if (*dpa_offset > ct3d->cxl_dstate.mem_size) { + if (*dpa_offset > ct3d->cxl_dstate.static_mem_size) { return -EINVAL; } @@ -1010,7 +1010,7 @@ static bool set_cacheline(CXLType3Dev *ct3d, uint64_t dpa_offset, uint8_t *data) return false; } - if (dpa_offset + CXL_CACHE_LINE_SIZE > ct3d->cxl_dstate.mem_size) { + if (dpa_offset + CXL_CACHE_LINE_SIZE > ct3d->cxl_dstate.static_mem_size) { return false; } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index e839370266..f7f56b44e3 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -234,7 +234,7 @@ typedef struct cxl_device_state { } timestamp; /* memory region size, HDM */ - uint64_t mem_size; + uint64_t static_mem_size; uint64_t pmem_size; uint64_t vmem_size; From f4fd91af3ae3b80c795ff5f3e0c1001b14ceb761 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:46 -0700 Subject: [PATCH 1774/2884] hw/mem/cxl_type3: Add support to create DC regions to type3 memory devices With the change, when setting up memory for type3 memory device, we can create DC regions. A property 'num-dc-regions' is added to ct3_props to allow users to pass the number of DC regions to create. To make it easier, other region parameters like region base, length, and block size are hard coded. If needed, these parameters can be added easily. With the change, we can create DC regions with proper kernel side support like below: region=$(cat /sys/bus/cxl/devices/decoder0.0/create_dc_region) echo $region > /sys/bus/cxl/devices/decoder0.0/create_dc_region echo 256 > /sys/bus/cxl/devices/$region/interleave_granularity echo 1 > /sys/bus/cxl/devices/$region/interleave_ways echo "dc0" >/sys/bus/cxl/devices/decoder2.0/mode echo 0x40000000 >/sys/bus/cxl/devices/decoder2.0/dpa_size echo 0x40000000 > /sys/bus/cxl/devices/$region/size echo "decoder2.0" > /sys/bus/cxl/devices/$region/target0 echo 1 > /sys/bus/cxl/devices/$region/commit echo $region > /sys/bus/cxl/drivers/cxl_region/bind Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-7-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Li Zhijian --- hw/mem/cxl_type3.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 7194c8f902..06c6f9bb78 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -30,6 +30,7 @@ #include "hw/pci/msix.h" #define DWORD_BYTE 4 +#define CXL_CAPACITY_MULTIPLIER (256 * MiB) /* Default CDAT entries for a memory region */ enum { @@ -567,6 +568,50 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, } } +/* + * TODO: dc region configuration will be updated once host backend and address + * space support is added for DCD. + */ +static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) +{ + int i; + uint64_t region_base = 0; + uint64_t region_len = 2 * GiB; + uint64_t decode_len = 2 * GiB; + uint64_t blk_size = 2 * MiB; + CXLDCRegion *region; + MemoryRegion *mr; + + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + region_base += memory_region_size(mr); + } + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + region_base += memory_region_size(mr); + } + if (region_base % CXL_CAPACITY_MULTIPLIER != 0) { + error_setg(errp, "DC region base not aligned to 0x%lx", + CXL_CAPACITY_MULTIPLIER); + return false; + } + + for (i = 0, region = &ct3d->dc.regions[0]; + i < ct3d->dc.num_regions; + i++, region++, region_base += region_len) { + *region = (CXLDCRegion) { + .base = region_base, + .decode_len = decode_len, + .len = region_len, + .block_size = blk_size, + /* dsmad_handle set when creating CDAT table entries */ + .flags = 0, + }; + } + + return true; +} + static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) { DeviceState *ds = DEVICE(ct3d); @@ -635,6 +680,13 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) g_free(p_name); } + if (ct3d->dc.num_regions > 0) { + if (!cxl_create_dc_regions(ct3d, errp)) { + error_append_hint(errp, "setup DC regions failed"); + return false; + } + } + return true; } @@ -930,6 +982,7 @@ static Property ct3_props[] = { HostMemoryBackend *), DEFINE_PROP_UINT64("sn", CXLType3Dev, sn, UI64_NULL), DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat.filename), + DEFINE_PROP_UINT8("num-dc-regions", CXLType3Dev, dc.num_regions, 0), DEFINE_PROP_END_OF_LIST(), }; From 69e4fb569dc1602bfeef5b8c58de5f40cd5d756e Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:47 -0700 Subject: [PATCH 1775/2884] hw/mem/cxl-type3: Refactor ct3_build_cdat_entries_for_mr to take mr size instead of mr as argument The function ct3_build_cdat_entries_for_mr only uses size of the passed memory region argument, refactor the function definition to make the passed arguments more specific. Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-8-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 06c6f9bb78..51be50ce87 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -44,7 +44,7 @@ enum { }; static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, - int dsmad_handle, MemoryRegion *mr, + int dsmad_handle, uint64_t size, bool is_pmem, uint64_t dpa_base) { CDATDsmas *dsmas; @@ -63,7 +63,7 @@ static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, .DSMADhandle = dsmad_handle, .flags = is_pmem ? CDAT_DSMAS_FLAG_NV : 0, .DPA_base = dpa_base, - .DPA_length = memory_region_size(mr), + .DPA_length = size, }; /* For now, no memory side cache, plausiblish numbers */ @@ -132,7 +132,7 @@ static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, */ .EFI_memory_type_attr = is_pmem ? 2 : 1, .DPA_offset = 0, - .DPA_length = memory_region_size(mr), + .DPA_length = size, }; /* Header always at start of structure */ @@ -149,6 +149,7 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) g_autofree CDATSubHeader **table = NULL; CXLType3Dev *ct3d = priv; MemoryRegion *volatile_mr = NULL, *nonvolatile_mr = NULL; + uint64_t vmr_size = 0, pmr_size = 0; int dsmad_handle = 0; int cur_ent = 0; int len = 0; @@ -163,6 +164,7 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) return -EINVAL; } len += CT3_CDAT_NUM_ENTRIES; + vmr_size = memory_region_size(volatile_mr); } if (ct3d->hostpmem) { @@ -171,21 +173,22 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) return -EINVAL; } len += CT3_CDAT_NUM_ENTRIES; + pmr_size = memory_region_size(nonvolatile_mr); } table = g_malloc0(len * sizeof(*table)); /* Now fill them in */ if (volatile_mr) { - ct3_build_cdat_entries_for_mr(table, dsmad_handle++, volatile_mr, + ct3_build_cdat_entries_for_mr(table, dsmad_handle++, vmr_size, false, 0); cur_ent = CT3_CDAT_NUM_ENTRIES; } if (nonvolatile_mr) { - uint64_t base = volatile_mr ? memory_region_size(volatile_mr) : 0; + uint64_t base = vmr_size; ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, - nonvolatile_mr, true, base); + pmr_size, true, base); cur_ent += CT3_CDAT_NUM_ENTRIES; } assert(len == cur_ent); From 90de94612bb568117e038c6ce9edd35d17d239f9 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:48 -0700 Subject: [PATCH 1776/2884] hw/mem/cxl_type3: Add host backend and address space handling for DC regions Add (file/memory backed) host backend for DCD. All the dynamic capacity regions will share a single, large enough host backend. Set up address space for DC regions to support read/write operations to dynamic capacity for DCD. With the change, the following support is added: 1. Add a new property to type3 device "volatile-dc-memdev" to point to host memory backend for dynamic capacity. Currently, all DC regions share one host backend; 2. Add namespace for dynamic capacity for read/write support; 3. Create cdat entries for each dynamic capacity region. Reviewed-by: Gregory Price Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-9-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 16 +++- hw/mem/cxl_type3.c | 177 +++++++++++++++++++++++++++++------- include/hw/cxl/cxl_device.h | 8 ++ 3 files changed, 164 insertions(+), 37 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index b592473587..6ad227f112 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -622,7 +622,8 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, size_t *len_out, CXLCCI *cci) { - CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; struct { uint8_t slots_supported; uint8_t slot_info; @@ -636,7 +637,8 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50); if ((cxl_dstate->vmem_size < CXL_CAPACITY_MULTIPLIER) || - (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER)) { + (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER) || + (ct3d->dc.total_capacity < CXL_CAPACITY_MULTIPLIER)) { return CXL_MBOX_INTERNAL_ERROR; } @@ -793,7 +795,8 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || - (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { + (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER)) || + (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) { return CXL_MBOX_INTERNAL_ERROR; } @@ -835,9 +838,11 @@ static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, uint64_t next_pmem; } QEMU_PACKED *part_info = (void *)payload_out; QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); + CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || - (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { + (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER)) || + (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) { return CXL_MBOX_INTERNAL_ERROR; } @@ -1179,7 +1184,8 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, struct clear_poison_pl *in = (void *)payload_in; dpa = ldq_le_p(&in->dpa); - if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->static_mem_size) { + if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->static_mem_size + + ct3d->dc.total_capacity) { return CXL_MBOX_INVALID_PA; } diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 51be50ce87..658570aa1a 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -45,7 +45,8 @@ enum { static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, int dsmad_handle, uint64_t size, - bool is_pmem, uint64_t dpa_base) + bool is_pmem, bool is_dynamic, + uint64_t dpa_base) { CDATDsmas *dsmas; CDATDslbis *dslbis0; @@ -61,7 +62,8 @@ static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, .length = sizeof(*dsmas), }, .DSMADhandle = dsmad_handle, - .flags = is_pmem ? CDAT_DSMAS_FLAG_NV : 0, + .flags = (is_pmem ? CDAT_DSMAS_FLAG_NV : 0) | + (is_dynamic ? CDAT_DSMAS_FLAG_DYNAMIC_CAP : 0), .DPA_base = dpa_base, .DPA_length = size, }; @@ -149,12 +151,13 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) g_autofree CDATSubHeader **table = NULL; CXLType3Dev *ct3d = priv; MemoryRegion *volatile_mr = NULL, *nonvolatile_mr = NULL; + MemoryRegion *dc_mr = NULL; uint64_t vmr_size = 0, pmr_size = 0; int dsmad_handle = 0; int cur_ent = 0; int len = 0; - if (!ct3d->hostpmem && !ct3d->hostvmem) { + if (!ct3d->hostpmem && !ct3d->hostvmem && !ct3d->dc.num_regions) { return 0; } @@ -176,21 +179,54 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) pmr_size = memory_region_size(nonvolatile_mr); } + if (ct3d->dc.num_regions) { + if (!ct3d->dc.host_dc) { + return -EINVAL; + } + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + if (!dc_mr) { + return -EINVAL; + } + len += CT3_CDAT_NUM_ENTRIES * ct3d->dc.num_regions; + } + table = g_malloc0(len * sizeof(*table)); /* Now fill them in */ if (volatile_mr) { ct3_build_cdat_entries_for_mr(table, dsmad_handle++, vmr_size, - false, 0); + false, false, 0); cur_ent = CT3_CDAT_NUM_ENTRIES; } if (nonvolatile_mr) { uint64_t base = vmr_size; ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, - pmr_size, true, base); + pmr_size, true, false, base); cur_ent += CT3_CDAT_NUM_ENTRIES; } + + if (dc_mr) { + int i; + uint64_t region_base = vmr_size + pmr_size; + + /* + * We assume the dynamic capacity to be volatile for now. + * Non-volatile dynamic capacity will be added if needed in the + * future. + */ + for (i = 0; i < ct3d->dc.num_regions; i++) { + ct3_build_cdat_entries_for_mr(&(table[cur_ent]), + dsmad_handle++, + ct3d->dc.regions[i].len, + false, true, region_base); + ct3d->dc.regions[i].dsmadhandle = dsmad_handle - 1; + + cur_ent += CT3_CDAT_NUM_ENTRIES; + region_base += ct3d->dc.regions[i].len; + } + } + assert(len == cur_ent); *cdat_table = g_steal_pointer(&table); @@ -301,10 +337,17 @@ static void build_dvsecs(CXLType3Dev *ct3d) range2_size_lo = (2 << 5) | (2 << 2) | 0x3 | (ct3d->hostpmem->size & 0xF0000000); } - } else { + } else if (ct3d->hostpmem) { range1_size_hi = ct3d->hostpmem->size >> 32; range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | (ct3d->hostpmem->size & 0xF0000000); + } else { + /* + * For DCD with no static memory, set memory active, memory class bits. + * No range is set. + */ + range1_size_hi = 0; + range1_size_lo = (2 << 5) | (2 << 2) | 0x3; } dvsec = (uint8_t *)&(CXLDVSECDevice){ @@ -579,11 +622,29 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) { int i; uint64_t region_base = 0; - uint64_t region_len = 2 * GiB; - uint64_t decode_len = 2 * GiB; + uint64_t region_len; + uint64_t decode_len; uint64_t blk_size = 2 * MiB; CXLDCRegion *region; MemoryRegion *mr; + uint64_t dc_size; + + mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(mr); + region_len = DIV_ROUND_UP(dc_size, ct3d->dc.num_regions); + + if (dc_size % (ct3d->dc.num_regions * CXL_CAPACITY_MULTIPLIER) != 0) { + error_setg(errp, + "backend size is not multiple of region len: 0x%" PRIx64, + region_len); + return false; + } + if (region_len % CXL_CAPACITY_MULTIPLIER != 0) { + error_setg(errp, "DC region size is unaligned to 0x%" PRIx64, + CXL_CAPACITY_MULTIPLIER); + return false; + } + decode_len = region_len; if (ct3d->hostvmem) { mr = host_memory_backend_get_memory(ct3d->hostvmem); @@ -594,7 +655,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) region_base += memory_region_size(mr); } if (region_base % CXL_CAPACITY_MULTIPLIER != 0) { - error_setg(errp, "DC region base not aligned to 0x%lx", + error_setg(errp, "DC region base not aligned to 0x%" PRIx64, CXL_CAPACITY_MULTIPLIER); return false; } @@ -610,6 +671,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) /* dsmad_handle set when creating CDAT table entries */ .flags = 0, }; + ct3d->dc.total_capacity += region->len; } return true; @@ -619,7 +681,8 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) { DeviceState *ds = DEVICE(ct3d); - if (!ct3d->hostmem && !ct3d->hostvmem && !ct3d->hostpmem) { + if (!ct3d->hostmem && !ct3d->hostvmem && !ct3d->hostpmem + && !ct3d->dc.num_regions) { error_setg(errp, "at least one memdev property must be set"); return false; } else if (ct3d->hostmem && ct3d->hostpmem) { @@ -683,7 +746,37 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) g_free(p_name); } + ct3d->dc.total_capacity = 0; if (ct3d->dc.num_regions > 0) { + MemoryRegion *dc_mr; + char *dc_name; + + if (!ct3d->dc.host_dc) { + error_setg(errp, "dynamic capacity must have a backing device"); + return false; + } + + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + if (!dc_mr) { + error_setg(errp, "dynamic capacity must have a backing device"); + return false; + } + + /* + * Set DC regions as volatile for now, non-volatile support can + * be added in the future if needed. + */ + memory_region_set_nonvolatile(dc_mr, false); + memory_region_set_enabled(dc_mr, true); + host_memory_backend_set_mapped(ct3d->dc.host_dc, true); + if (ds->id) { + dc_name = g_strdup_printf("cxl-dcd-dpa-dc-space:%s", ds->id); + } else { + dc_name = g_strdup("cxl-dcd-dpa-dc-space"); + } + address_space_init(&ct3d->dc.host_dc_as, dc_mr, dc_name); + g_free(dc_name); + if (!cxl_create_dc_regions(ct3d, errp)) { error_append_hint(errp, "setup DC regions failed"); return false; @@ -779,6 +872,9 @@ err_release_cdat: err_free_special_ops: g_free(regs->special_ops); err_address_space_free: + if (ct3d->dc.host_dc) { + address_space_destroy(&ct3d->dc.host_dc_as); + } if (ct3d->hostpmem) { address_space_destroy(&ct3d->hostpmem_as); } @@ -797,6 +893,9 @@ static void ct3_exit(PCIDevice *pci_dev) pcie_aer_exit(pci_dev); cxl_doe_cdat_release(cxl_cstate); g_free(regs->special_ops); + if (ct3d->dc.host_dc) { + address_space_destroy(&ct3d->dc.host_dc_as); + } if (ct3d->hostpmem) { address_space_destroy(&ct3d->hostpmem_as); } @@ -875,16 +974,23 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, AddressSpace **as, uint64_t *dpa_offset) { - MemoryRegion *vmr = NULL, *pmr = NULL; + MemoryRegion *vmr = NULL, *pmr = NULL, *dc_mr = NULL; + uint64_t vmr_size = 0, pmr_size = 0, dc_size = 0; if (ct3d->hostvmem) { vmr = host_memory_backend_get_memory(ct3d->hostvmem); + vmr_size = memory_region_size(vmr); } if (ct3d->hostpmem) { pmr = host_memory_backend_get_memory(ct3d->hostpmem); + pmr_size = memory_region_size(pmr); + } + if (ct3d->dc.host_dc) { + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(dc_mr); } - if (!vmr && !pmr) { + if (!vmr && !pmr && !dc_mr) { return -ENODEV; } @@ -892,19 +998,18 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, return -EINVAL; } - if (*dpa_offset > ct3d->cxl_dstate.static_mem_size) { + if (*dpa_offset >= vmr_size + pmr_size + dc_size) { return -EINVAL; } - if (vmr) { - if (*dpa_offset < memory_region_size(vmr)) { - *as = &ct3d->hostvmem_as; - } else { - *as = &ct3d->hostpmem_as; - *dpa_offset -= memory_region_size(vmr); - } - } else { + if (*dpa_offset < vmr_size) { + *as = &ct3d->hostvmem_as; + } else if (*dpa_offset < vmr_size + pmr_size) { *as = &ct3d->hostpmem_as; + *dpa_offset -= vmr_size; + } else { + *as = &ct3d->dc.host_dc_as; + *dpa_offset -= (vmr_size + pmr_size); } return 0; @@ -986,6 +1091,8 @@ static Property ct3_props[] = { DEFINE_PROP_UINT64("sn", CXLType3Dev, sn, UI64_NULL), DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat.filename), DEFINE_PROP_UINT8("num-dc-regions", CXLType3Dev, dc.num_regions, 0), + DEFINE_PROP_LINK("volatile-dc-memdev", CXLType3Dev, dc.host_dc, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -1052,33 +1159,39 @@ static void set_lsa(CXLType3Dev *ct3d, const void *buf, uint64_t size, static bool set_cacheline(CXLType3Dev *ct3d, uint64_t dpa_offset, uint8_t *data) { - MemoryRegion *vmr = NULL, *pmr = NULL; + MemoryRegion *vmr = NULL, *pmr = NULL, *dc_mr = NULL; AddressSpace *as; + uint64_t vmr_size = 0, pmr_size = 0, dc_size = 0; if (ct3d->hostvmem) { vmr = host_memory_backend_get_memory(ct3d->hostvmem); + vmr_size = memory_region_size(vmr); } if (ct3d->hostpmem) { pmr = host_memory_backend_get_memory(ct3d->hostpmem); + pmr_size = memory_region_size(pmr); } + if (ct3d->dc.host_dc) { + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(dc_mr); + } - if (!vmr && !pmr) { + if (!vmr && !pmr && !dc_mr) { return false; } - if (dpa_offset + CXL_CACHE_LINE_SIZE > ct3d->cxl_dstate.static_mem_size) { + if (dpa_offset + CXL_CACHE_LINE_SIZE > vmr_size + pmr_size + dc_size) { return false; } - if (vmr) { - if (dpa_offset < memory_region_size(vmr)) { - as = &ct3d->hostvmem_as; - } else { - as = &ct3d->hostpmem_as; - dpa_offset -= memory_region_size(vmr); - } - } else { + if (dpa_offset < vmr_size) { + as = &ct3d->hostvmem_as; + } else if (dpa_offset < vmr_size + pmr_size) { as = &ct3d->hostpmem_as; + dpa_offset -= vmr_size; + } else { + as = &ct3d->dc.host_dc_as; + dpa_offset -= (vmr_size + pmr_size); } address_space_write(as, dpa_offset, MEMTXATTRS_UNSPECIFIED, &data, diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index f7f56b44e3..c2c3df0d2a 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -467,6 +467,14 @@ struct CXLType3Dev { uint64_t poison_list_overflow_ts; struct dynamic_capacity { + HostMemoryBackend *host_dc; + AddressSpace host_dc_as; + /* + * total_capacity is equivalent to the dynamic capability + * memory region size. + */ + uint64_t total_capacity; /* 256M aligned */ + uint8_t num_regions; /* 0-8 regions */ CXLDCRegion regions[DCD_MAX_NUM_REGION]; } dc; From 1c9221f19e62e448a9ca71a2d5c8a369102a0c38 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:49 -0700 Subject: [PATCH 1777/2884] hw/mem/cxl_type3: Add DC extent list representative and get DC extent list mailbox support Add dynamic capacity extent list representative to the definition of CXLType3Dev and implement get DC extent list mailbox command per CXL.spec.3.1:.8.2.9.9.9.2. Tested-by: Svetly Todorov Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-10-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 73 ++++++++++++++++++++++++++++++++++++- hw/mem/cxl_type3.c | 1 + include/hw/cxl/cxl_device.h | 22 +++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 6ad227f112..7872d2f3e6 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -84,6 +84,7 @@ enum { #define CLEAR_POISON 0x2 DCD_CONFIG = 0x48, #define GET_DC_CONFIG 0x0 + #define GET_DYN_CAP_EXT_LIST 0x1 PHYSICAL_SWITCH = 0x51, #define IDENTIFY_SWITCH_DEVICE 0x0 #define GET_PHYSICAL_PORT_STATE 0x1 @@ -1322,7 +1323,8 @@ static CXLRetCode cmd_dcd_get_dyn_cap_config(const struct cxl_cmd *cmd, * to use. */ stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED); - stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED); + stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED - + ct3d->dc.total_extent_count); stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED); stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED); @@ -1330,6 +1332,72 @@ static CXLRetCode cmd_dcd_get_dyn_cap_config(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* + * CXL r3.1 section 8.2.9.9.9.2: + * Get Dynamic Capacity Extent List (Opcode 4801h) + */ +static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + struct { + uint32_t extent_cnt; + uint32_t start_extent_id; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint32_t count; + uint32_t total_extents; + uint32_t generation_num; + uint8_t rsvd[4]; + CXLDCExtentRaw records[]; + } QEMU_PACKED *out = (void *)payload_out; + uint32_t start_extent_id = in->start_extent_id; + CXLDCExtentList *extent_list = &ct3d->dc.extents; + uint16_t record_count = 0, i = 0, record_done = 0; + uint16_t out_pl_len, size; + CXLDCExtent *ent; + + if (start_extent_id > ct3d->dc.total_extent_count) { + return CXL_MBOX_INVALID_INPUT; + } + + record_count = MIN(in->extent_cnt, + ct3d->dc.total_extent_count - start_extent_id); + size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out); + record_count = MIN(record_count, size / sizeof(out->records[0])); + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + + stl_le_p(&out->count, record_count); + stl_le_p(&out->total_extents, ct3d->dc.total_extent_count); + stl_le_p(&out->generation_num, ct3d->dc.ext_list_gen_seq); + + if (record_count > 0) { + CXLDCExtentRaw *out_rec = &out->records[record_done]; + + QTAILQ_FOREACH(ent, extent_list, node) { + if (i++ < start_extent_id) { + continue; + } + stq_le_p(&out_rec->start_dpa, ent->start_dpa); + stq_le_p(&out_rec->len, ent->len); + memcpy(&out_rec->tag, ent->tag, 0x10); + stw_le_p(&out_rec->shared_seq, ent->shared_seq); + + record_done++; + if (record_done == record_count) { + break; + } + } + } + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + #define IMMEDIATE_CONFIG_CHANGE (1 << 1) #define IMMEDIATE_DATA_CHANGE (1 << 2) #define IMMEDIATE_POLICY_CHANGE (1 << 3) @@ -1377,6 +1445,9 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { [DCD_CONFIG][GET_DC_CONFIG] = { "DCD_GET_DC_CONFIG", cmd_dcd_get_dyn_cap_config, 2, 0 }, + [DCD_CONFIG][GET_DYN_CAP_EXT_LIST] = { + "DCD_GET_DYNAMIC_CAPACITY_EXTENT_LIST", cmd_dcd_get_dyn_cap_ext_list, + 8, 0 }, }; static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 658570aa1a..2075846b1b 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -673,6 +673,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) }; ct3d->dc.total_capacity += region->len; } + QTAILQ_INIT(&ct3d->dc.extents); return true; } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index c2c3df0d2a..6aec6ac983 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -424,6 +424,25 @@ typedef QLIST_HEAD(, CXLPoison) CXLPoisonList; #define DCD_MAX_NUM_REGION 8 +typedef struct CXLDCExtentRaw { + uint64_t start_dpa; + uint64_t len; + uint8_t tag[0x10]; + uint16_t shared_seq; + uint8_t rsvd[0x6]; +} QEMU_PACKED CXLDCExtentRaw; + +typedef struct CXLDCExtent { + uint64_t start_dpa; + uint64_t len; + uint8_t tag[0x10]; + uint16_t shared_seq; + uint8_t rsvd[0x6]; + + QTAILQ_ENTRY(CXLDCExtent) node; +} CXLDCExtent; +typedef QTAILQ_HEAD(, CXLDCExtent) CXLDCExtentList; + typedef struct CXLDCRegion { uint64_t base; /* aligned to 256*MiB */ uint64_t decode_len; /* aligned to 256*MiB */ @@ -474,6 +493,9 @@ struct CXLType3Dev { * memory region size. */ uint64_t total_capacity; /* 256M aligned */ + CXLDCExtentList extents; + uint32_t total_extent_count; + uint32_t ext_list_gen_seq; uint8_t num_regions; /* 0-8 regions */ CXLDCRegion regions[DCD_MAX_NUM_REGION]; From 16fd1b1216a2895a7995345ad6630151954c43a3 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:50 -0700 Subject: [PATCH 1778/2884] hw/cxl/cxl-mailbox-utils: Add mailbox commands to support add/release dynamic capacity response Per CXL spec 3.1, two mailbox commands are implemented: Add Dynamic Capacity Response (Opcode 4802h) 8.2.9.9.9.3, and Release Dynamic Capacity (Opcode 4803h) 8.2.9.9.9.4. For the process of the above two commands, we use two-pass approach. Pass 1: Check whether the input payload is valid or not; if not, skip Pass 2 and return mailbox process error. Pass 2: Do the real work--add or release extents, respectively. Tested-by: Svetly Todorov Reviewed-by: Gregory Price Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-11-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 394 ++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 11 + include/hw/cxl/cxl_device.h | 4 + 3 files changed, 409 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 7872d2f3e6..e322407fb3 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -19,6 +19,7 @@ #include "qemu/units.h" #include "qemu/uuid.h" #include "sysemu/hostmem.h" +#include "qemu/range.h" #define CXL_CAPACITY_MULTIPLIER (256 * MiB) #define CXL_DC_EVENT_LOG_SIZE 8 @@ -85,6 +86,8 @@ enum { DCD_CONFIG = 0x48, #define GET_DC_CONFIG 0x0 #define GET_DYN_CAP_EXT_LIST 0x1 + #define ADD_DYN_CAP_RSP 0x2 + #define RELEASE_DYN_CAP 0x3 PHYSICAL_SWITCH = 0x51, #define IDENTIFY_SWITCH_DEVICE 0x0 #define GET_PHYSICAL_PORT_STATE 0x1 @@ -1398,6 +1401,391 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* + * Check whether any bit between addr[nr, nr+size) is set, + * return true if any bit is set, otherwise return false + */ +static bool test_any_bits_set(const unsigned long *addr, unsigned long nr, + unsigned long size) +{ + unsigned long res = find_next_bit(addr, size + nr, nr); + + return res < nr + size; +} + +CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len) +{ + int i; + CXLDCRegion *region = &ct3d->dc.regions[0]; + + if (dpa < region->base || + dpa >= region->base + ct3d->dc.total_capacity) { + return NULL; + } + + /* + * CXL r3.1 section 9.13.3: Dynamic Capacity Device (DCD) + * + * Regions are used in increasing-DPA order, with Region 0 being used for + * the lowest DPA of Dynamic Capacity and Region 7 for the highest DPA. + * So check from the last region to find where the dpa belongs. Extents that + * cross multiple regions are not allowed. + */ + for (i = ct3d->dc.num_regions - 1; i >= 0; i--) { + region = &ct3d->dc.regions[i]; + if (dpa >= region->base) { + if (dpa + len > region->base + region->len) { + return NULL; + } + return region; + } + } + + return NULL; +} + +static void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, + uint64_t dpa, + uint64_t len, + uint8_t *tag, + uint16_t shared_seq) +{ + CXLDCExtent *extent; + + extent = g_new0(CXLDCExtent, 1); + extent->start_dpa = dpa; + extent->len = len; + if (tag) { + memcpy(extent->tag, tag, 0x10); + } + extent->shared_seq = shared_seq; + + QTAILQ_INSERT_TAIL(list, extent, node); +} + +void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, + CXLDCExtent *extent) +{ + QTAILQ_REMOVE(list, extent, node); + g_free(extent); +} + +/* + * CXL r3.1 Table 8-168: Add Dynamic Capacity Response Input Payload + * CXL r3.1 Table 8-170: Release Dynamic Capacity Input Payload + */ +typedef struct CXLUpdateDCExtentListInPl { + uint32_t num_entries_updated; + uint8_t flags; + uint8_t rsvd[3]; + /* CXL r3.1 Table 8-169: Updated Extent */ + struct { + uint64_t start_dpa; + uint64_t len; + uint8_t rsvd[8]; + } QEMU_PACKED updated_entries[]; +} QEMU_PACKED CXLUpdateDCExtentListInPl; + +/* + * For the extents in the extent list to operate, check whether they are valid + * 1. The extent should be in the range of a valid DC region; + * 2. The extent should not cross multiple regions; + * 3. The start DPA and the length of the extent should align with the block + * size of the region; + * 4. The address range of multiple extents in the list should not overlap. + */ +static CXLRetCode cxl_detect_malformed_extent_list(CXLType3Dev *ct3d, + const CXLUpdateDCExtentListInPl *in) +{ + uint64_t min_block_size = UINT64_MAX; + CXLDCRegion *region; + CXLDCRegion *lastregion = &ct3d->dc.regions[ct3d->dc.num_regions - 1]; + g_autofree unsigned long *blk_bitmap = NULL; + uint64_t dpa, len; + uint32_t i; + + for (i = 0; i < ct3d->dc.num_regions; i++) { + region = &ct3d->dc.regions[i]; + min_block_size = MIN(min_block_size, region->block_size); + } + + blk_bitmap = bitmap_new((lastregion->base + lastregion->len - + ct3d->dc.regions[0].base) / min_block_size); + + for (i = 0; i < in->num_entries_updated; i++) { + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return CXL_MBOX_INVALID_PA; + } + + dpa -= ct3d->dc.regions[0].base; + if (dpa % region->block_size || len % region->block_size) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + /* the dpa range already covered by some other extents in the list */ + if (test_any_bits_set(blk_bitmap, dpa / min_block_size, + len / min_block_size)) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + bitmap_set(blk_bitmap, dpa / min_block_size, len / min_block_size); + } + + return CXL_MBOX_SUCCESS; +} + +static CXLRetCode cxl_dcd_add_dyn_cap_rsp_dry_run(CXLType3Dev *ct3d, + const CXLUpdateDCExtentListInPl *in) +{ + uint32_t i; + CXLDCExtent *ent; + uint64_t dpa, len; + Range range1, range2; + + for (i = 0; i < in->num_entries_updated; i++) { + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + range_init_nofail(&range1, dpa, len); + + /* + * TODO: once the pending extent list is added, check against + * the list will be added here. + */ + + /* to-be-added range should not overlap with range already accepted */ + QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) { + range_init_nofail(&range2, ent->start_dpa, ent->len); + if (range_overlaps_range(&range1, &range2)) { + return CXL_MBOX_INVALID_PA; + } + } + } + return CXL_MBOX_SUCCESS; +} + +/* + * CXL r3.1 section 8.2.9.9.9.3: Add Dynamic Capacity Response (Opcode 4802h) + * An extent is added to the extent list and becomes usable only after the + * response is processed successfully. + */ +static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLUpdateDCExtentListInPl *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCExtentList *extent_list = &ct3d->dc.extents; + uint32_t i; + uint64_t dpa, len; + CXLRetCode ret; + + if (in->num_entries_updated == 0) { + /* + * TODO: once the pending list is introduced, extents in the beginning + * will get wiped out. + */ + return CXL_MBOX_SUCCESS; + } + + /* Adding extents causes exceeding device's extent tracking ability. */ + if (in->num_entries_updated + ct3d->dc.total_extent_count > + CXL_NUM_EXTENTS_SUPPORTED) { + return CXL_MBOX_RESOURCES_EXHAUSTED; + } + + ret = cxl_detect_malformed_extent_list(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + ret = cxl_dcd_add_dyn_cap_rsp_dry_run(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + for (i = 0; i < in->num_entries_updated; i++) { + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); + ct3d->dc.total_extent_count += 1; + /* + * TODO: we will add a pending extent list based on event log record + * and process the list accordingly here. + */ + } + + return CXL_MBOX_SUCCESS; +} + +/* + * Copy extent list from src to dst + * Return value: number of extents copied + */ +static uint32_t copy_extent_list(CXLDCExtentList *dst, + const CXLDCExtentList *src) +{ + uint32_t cnt = 0; + CXLDCExtent *ent; + + if (!dst || !src) { + return 0; + } + + QTAILQ_FOREACH(ent, src, node) { + cxl_insert_extent_to_extent_list(dst, ent->start_dpa, ent->len, + ent->tag, ent->shared_seq); + cnt++; + } + return cnt; +} + +static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, + const CXLUpdateDCExtentListInPl *in, CXLDCExtentList *updated_list, + uint32_t *updated_list_size) +{ + CXLDCExtent *ent, *ent_next; + uint64_t dpa, len; + uint32_t i; + int cnt_delta = 0; + CXLRetCode ret = CXL_MBOX_SUCCESS; + + QTAILQ_INIT(updated_list); + copy_extent_list(updated_list, &ct3d->dc.extents); + + for (i = 0; i < in->num_entries_updated; i++) { + Range range; + + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + while (len > 0) { + QTAILQ_FOREACH(ent, updated_list, node) { + range_init_nofail(&range, ent->start_dpa, ent->len); + + if (range_contains(&range, dpa)) { + uint64_t len1, len2 = 0, len_done = 0; + uint64_t ent_start_dpa = ent->start_dpa; + uint64_t ent_len = ent->len; + + len1 = dpa - ent->start_dpa; + /* Found the extent or the subset of an existing extent */ + if (range_contains(&range, dpa + len - 1)) { + len2 = ent_start_dpa + ent_len - dpa - len; + } else { + /* + * TODO: we reject the attempt to remove an extent + * that overlaps with multiple extents in the device + * for now. We will allow it once superset release + * support is added. + */ + ret = CXL_MBOX_INVALID_PA; + goto free_and_exit; + } + len_done = ent_len - len1 - len2; + + cxl_remove_extent_from_extent_list(updated_list, ent); + cnt_delta--; + + if (len1) { + cxl_insert_extent_to_extent_list(updated_list, + ent_start_dpa, + len1, NULL, 0); + cnt_delta++; + } + if (len2) { + cxl_insert_extent_to_extent_list(updated_list, + dpa + len, + len2, NULL, 0); + cnt_delta++; + } + + if (cnt_delta + ct3d->dc.total_extent_count > + CXL_NUM_EXTENTS_SUPPORTED) { + ret = CXL_MBOX_RESOURCES_EXHAUSTED; + goto free_and_exit; + } + + len -= len_done; + /* len == 0 here until superset release is added */ + break; + } + } + if (len) { + ret = CXL_MBOX_INVALID_PA; + goto free_and_exit; + } + } + } +free_and_exit: + if (ret != CXL_MBOX_SUCCESS) { + QTAILQ_FOREACH_SAFE(ent, updated_list, node, ent_next) { + cxl_remove_extent_from_extent_list(updated_list, ent); + } + *updated_list_size = 0; + } else { + *updated_list_size = ct3d->dc.total_extent_count + cnt_delta; + } + + return ret; +} + +/* + * CXL r3.1 section 8.2.9.9.9.4: Release Dynamic Capacity (Opcode 4803h) + */ +static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLUpdateDCExtentListInPl *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCExtentList updated_list; + CXLDCExtent *ent, *ent_next; + uint32_t updated_list_size; + CXLRetCode ret; + + if (in->num_entries_updated == 0) { + return CXL_MBOX_INVALID_INPUT; + } + + ret = cxl_detect_malformed_extent_list(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + ret = cxl_dc_extent_release_dry_run(ct3d, in, &updated_list, + &updated_list_size); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + /* + * If the dry run release passes, the returned updated_list will + * be the updated extent list and we just need to clear the extents + * in the accepted list and copy extents in the updated_list to accepted + * list and update the extent count; + */ + QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { + cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); + } + copy_extent_list(&ct3d->dc.extents, &updated_list); + QTAILQ_FOREACH_SAFE(ent, &updated_list, node, ent_next) { + cxl_remove_extent_from_extent_list(&updated_list, ent); + } + ct3d->dc.total_extent_count = updated_list_size; + + return CXL_MBOX_SUCCESS; +} + #define IMMEDIATE_CONFIG_CHANGE (1 << 1) #define IMMEDIATE_DATA_CHANGE (1 << 2) #define IMMEDIATE_POLICY_CHANGE (1 << 3) @@ -1448,6 +1836,12 @@ static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { [DCD_CONFIG][GET_DYN_CAP_EXT_LIST] = { "DCD_GET_DYNAMIC_CAPACITY_EXTENT_LIST", cmd_dcd_get_dyn_cap_ext_list, 8, 0 }, + [DCD_CONFIG][ADD_DYN_CAP_RSP] = { + "DCD_ADD_DYNAMIC_CAPACITY_RESPONSE", cmd_dcd_add_dyn_cap_rsp, + ~0, IMMEDIATE_DATA_CHANGE }, + [DCD_CONFIG][RELEASE_DYN_CAP] = { + "DCD_RELEASE_DYNAMIC_CAPACITY", cmd_dcd_release_dyn_cap, + ~0, IMMEDIATE_DATA_CHANGE }, }; static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 2075846b1b..db5191b3b7 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -678,6 +678,15 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) return true; } +static void cxl_destroy_dc_regions(CXLType3Dev *ct3d) +{ + CXLDCExtent *ent, *ent_next; + + QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { + cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); + } +} + static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) { DeviceState *ds = DEVICE(ct3d); @@ -874,6 +883,7 @@ err_free_special_ops: g_free(regs->special_ops); err_address_space_free: if (ct3d->dc.host_dc) { + cxl_destroy_dc_regions(ct3d); address_space_destroy(&ct3d->dc.host_dc_as); } if (ct3d->hostpmem) { @@ -895,6 +905,7 @@ static void ct3_exit(PCIDevice *pci_dev) cxl_doe_cdat_release(cxl_cstate); g_free(regs->special_ops); if (ct3d->dc.host_dc) { + cxl_destroy_dc_regions(ct3d); address_space_destroy(&ct3d->dc.host_dc_as); } if (ct3d->hostpmem) { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 6aec6ac983..df3511e91b 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -551,4 +551,8 @@ void cxl_event_irq_assert(CXLType3Dev *ct3d); void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d); +CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); + +void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, + CXLDCExtent *extent); #endif From d0b9b28a5b9f1e3d22b508f4f05d903a4b443e38 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:51 -0700 Subject: [PATCH 1779/2884] hw/cxl/events: Add qmp interfaces to add/release dynamic capacity extents To simulate FM functionalities for initiating Dynamic Capacity Add (Opcode 5604h) and Dynamic Capacity Release (Opcode 5605h) as in CXL spec r3.1 7.6.7.6.5 and 7.6.7.6.6, we implemented two QMP interfaces to issue add/release dynamic capacity extents requests. With the change, we allow to release an extent only when its DPA range is contained by a single accepted extent in the device. That is to say, extent superset release is not supported yet. 1. Add dynamic capacity extents: For example, the command to add two continuous extents (each 128MiB long) to region 0 (starting at DPA offset 0) looks like below: { "execute": "qmp_capabilities" } { "execute": "cxl-add-dynamic-capacity", "arguments": { "path": "/machine/peripheral/cxl-dcd0", "host-id": 0, "selection-policy": "prescriptive", "region": 0, "extents": [ { "offset": 0, "len": 134217728 }, { "offset": 134217728, "len": 134217728 } ] } } 2. Release dynamic capacity extents: For example, the command to release an extent of size 128MiB from region 0 (DPA offset 128MiB) looks like below: { "execute": "cxl-release-dynamic-capacity", "arguments": { "path": "/machine/peripheral/cxl-dcd0", "host-id": 0, "removal-policy":"prescriptive", "region": 0, "extents": [ { "offset": 134217728, "len": 134217728 } ] } } Tested-by: Svetly Todorov Reviewed-by: Gregory Price Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-12-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 62 ++++++-- hw/mem/cxl_type3.c | 306 +++++++++++++++++++++++++++++++++++- hw/mem/cxl_type3_stubs.c | 25 +++ include/hw/cxl/cxl_device.h | 22 +++ include/hw/cxl/cxl_events.h | 18 +++ qapi/cxl.json | 143 +++++++++++++++++ 6 files changed, 563 insertions(+), 13 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index e322407fb3..64387f34ce 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1405,7 +1405,7 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, * Check whether any bit between addr[nr, nr+size) is set, * return true if any bit is set, otherwise return false */ -static bool test_any_bits_set(const unsigned long *addr, unsigned long nr, +bool test_any_bits_set(const unsigned long *addr, unsigned long nr, unsigned long size) { unsigned long res = find_next_bit(addr, size + nr, nr); @@ -1444,7 +1444,7 @@ CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len) return NULL; } -static void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, +void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, uint64_t dpa, uint64_t len, uint8_t *tag, @@ -1470,6 +1470,44 @@ void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, g_free(extent); } +/* + * Add a new extent to the extent "group" if group exists; + * otherwise, create a new group + * Return value: the extent group where the extent is inserted. + */ +CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, + uint64_t dpa, + uint64_t len, + uint8_t *tag, + uint16_t shared_seq) +{ + if (!group) { + group = g_new0(CXLDCExtentGroup, 1); + QTAILQ_INIT(&group->list); + } + cxl_insert_extent_to_extent_list(&group->list, dpa, len, + tag, shared_seq); + return group; +} + +void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, + CXLDCExtentGroup *group) +{ + QTAILQ_INSERT_TAIL(list, group, node); +} + +void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) +{ + CXLDCExtent *ent, *ent_next; + CXLDCExtentGroup *group = QTAILQ_FIRST(list); + + QTAILQ_REMOVE(list, group, node); + QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { + cxl_remove_extent_from_extent_list(&group->list, ent); + } + g_free(group); +} + /* * CXL r3.1 Table 8-168: Add Dynamic Capacity Response Input Payload * CXL r3.1 Table 8-170: Release Dynamic Capacity Input Payload @@ -1541,6 +1579,7 @@ static CXLRetCode cxl_dcd_add_dyn_cap_rsp_dry_run(CXLType3Dev *ct3d, { uint32_t i; CXLDCExtent *ent; + CXLDCExtentGroup *ext_group; uint64_t dpa, len; Range range1, range2; @@ -1551,9 +1590,13 @@ static CXLRetCode cxl_dcd_add_dyn_cap_rsp_dry_run(CXLType3Dev *ct3d, range_init_nofail(&range1, dpa, len); /* - * TODO: once the pending extent list is added, check against - * the list will be added here. + * The host-accepted DPA range must be contained by the first extent + * group in the pending list */ + ext_group = QTAILQ_FIRST(&ct3d->dc.extents_pending); + if (!cxl_extents_contains_dpa_range(&ext_group->list, dpa, len)) { + return CXL_MBOX_INVALID_PA; + } /* to-be-added range should not overlap with range already accepted */ QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) { @@ -1586,10 +1629,7 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, CXLRetCode ret; if (in->num_entries_updated == 0) { - /* - * TODO: once the pending list is introduced, extents in the beginning - * will get wiped out. - */ + cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); return CXL_MBOX_SUCCESS; } @@ -1615,11 +1655,9 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); ct3d->dc.total_extent_count += 1; - /* - * TODO: we will add a pending extent list based on event log record - * and process the list accordingly here. - */ } + /* Remove the first extent group in the pending list */ + cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); return CXL_MBOX_SUCCESS; } diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index db5191b3b7..f53bcca6d3 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -674,6 +674,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) ct3d->dc.total_capacity += region->len; } QTAILQ_INIT(&ct3d->dc.extents); + QTAILQ_INIT(&ct3d->dc.extents_pending); return true; } @@ -681,10 +682,19 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) static void cxl_destroy_dc_regions(CXLType3Dev *ct3d) { CXLDCExtent *ent, *ent_next; + CXLDCExtentGroup *group, *group_next; QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); } + + QTAILQ_FOREACH_SAFE(group, &ct3d->dc.extents_pending, node, group_next) { + QTAILQ_REMOVE(&ct3d->dc.extents_pending, group, node); + QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { + cxl_remove_extent_from_extent_list(&group->list, ent); + } + g_free(group); + } } static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) @@ -1449,7 +1459,6 @@ static int ct3d_qmp_cxl_event_log_enc(CxlEventLog log) return CXL_EVENT_TYPE_FAIL; case CXL_EVENT_LOG_FATAL: return CXL_EVENT_TYPE_FATAL; -/* DCD not yet supported */ default: return -EINVAL; } @@ -1700,6 +1709,301 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, } } +/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */ +static const QemuUUID dynamic_capacity_uuid = { + .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f, + 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a), +}; + +typedef enum CXLDCEventType { + DC_EVENT_ADD_CAPACITY = 0x0, + DC_EVENT_RELEASE_CAPACITY = 0x1, + DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2, + DC_EVENT_REGION_CONFIG_UPDATED = 0x3, + DC_EVENT_ADD_CAPACITY_RSP = 0x4, + DC_EVENT_CAPACITY_RELEASED = 0x5, +} CXLDCEventType; + +/* + * Check whether the range [dpa, dpa + len - 1] has overlaps with extents in + * the list. + * Return value: return true if has overlaps; otherwise, return false + */ +static bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len) +{ + CXLDCExtent *ent; + Range range1, range2; + + if (!list) { + return false; + } + + range_init_nofail(&range1, dpa, len); + QTAILQ_FOREACH(ent, list, node) { + range_init_nofail(&range2, ent->start_dpa, ent->len); + if (range_overlaps_range(&range1, &range2)) { + return true; + } + } + return false; +} + +/* + * Check whether the range [dpa, dpa + len - 1] is contained by extents in + * the list. + * Will check multiple extents containment once superset release is added. + * Return value: return true if range is contained; otherwise, return false + */ +bool cxl_extents_contains_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len) +{ + CXLDCExtent *ent; + Range range1, range2; + + if (!list) { + return false; + } + + range_init_nofail(&range1, dpa, len); + QTAILQ_FOREACH(ent, list, node) { + range_init_nofail(&range2, ent->start_dpa, ent->len); + if (range_contains_range(&range2, &range1)) { + return true; + } + } + return false; +} + +static bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, + uint64_t dpa, uint64_t len) +{ + CXLDCExtentGroup *group; + + if (!list) { + return false; + } + + QTAILQ_FOREACH(group, list, node) { + if (cxl_extents_overlaps_dpa_range(&group->list, dpa, len)) { + return true; + } + } + return false; +} + +/* + * The main function to process dynamic capacity event with extent list. + * Currently DC extents add/release requests are processed. + */ +static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, + uint16_t hid, CXLDCEventType type, uint8_t rid, + CXLDynamicCapacityExtentList *records, Error **errp) +{ + Object *obj; + CXLEventDynamicCapacity dCap = {}; + CXLEventRecordHdr *hdr = &dCap.hdr; + CXLType3Dev *dcd; + uint8_t flags = 1 << CXL_EVENT_TYPE_INFO; + uint32_t num_extents = 0; + CXLDynamicCapacityExtentList *list; + CXLDCExtentGroup *group = NULL; + g_autofree CXLDCExtentRaw *extents = NULL; + uint8_t enc_log = CXL_EVENT_TYPE_DYNAMIC_CAP; + uint64_t dpa, offset, len, block_size; + g_autofree unsigned long *blk_bitmap = NULL; + int i; + + obj = object_resolve_path_type(path, TYPE_CXL_TYPE3, NULL); + if (!obj) { + error_setg(errp, "Unable to resolve CXL type 3 device"); + return; + } + + dcd = CXL_TYPE3(obj); + if (!dcd->dc.num_regions) { + error_setg(errp, "No dynamic capacity support from the device"); + return; + } + + + if (rid >= dcd->dc.num_regions) { + error_setg(errp, "region id is too large"); + return; + } + block_size = dcd->dc.regions[rid].block_size; + blk_bitmap = bitmap_new(dcd->dc.regions[rid].len / block_size); + + /* Sanity check and count the extents */ + list = records; + while (list) { + offset = list->value->offset; + len = list->value->len; + dpa = offset + dcd->dc.regions[rid].base; + + if (len == 0) { + error_setg(errp, "extent with 0 length is not allowed"); + return; + } + + if (offset % block_size || len % block_size) { + error_setg(errp, "dpa or len is not aligned to region block size"); + return; + } + + if (offset + len > dcd->dc.regions[rid].len) { + error_setg(errp, "extent range is beyond the region end"); + return; + } + + /* No duplicate or overlapped extents are allowed */ + if (test_any_bits_set(blk_bitmap, offset / block_size, + len / block_size)) { + error_setg(errp, "duplicate or overlapped extents are detected"); + return; + } + bitmap_set(blk_bitmap, offset / block_size, len / block_size); + + if (type == DC_EVENT_RELEASE_CAPACITY) { + if (cxl_extent_groups_overlaps_dpa_range(&dcd->dc.extents_pending, + dpa, len)) { + error_setg(errp, + "cannot release extent with pending DPA range"); + return; + } + if (!cxl_extents_contains_dpa_range(&dcd->dc.extents, dpa, len)) { + error_setg(errp, + "cannot release extent with non-existing DPA range"); + return; + } + } else if (type == DC_EVENT_ADD_CAPACITY) { + if (cxl_extents_overlaps_dpa_range(&dcd->dc.extents, dpa, len)) { + error_setg(errp, + "cannot add DPA already accessible to the same LD"); + return; + } + if (cxl_extent_groups_overlaps_dpa_range(&dcd->dc.extents_pending, + dpa, len)) { + error_setg(errp, + "cannot add DPA again while still pending"); + return; + } + } + list = list->next; + num_extents++; + } + + /* Create extent list for event being passed to host */ + i = 0; + list = records; + extents = g_new0(CXLDCExtentRaw, num_extents); + while (list) { + offset = list->value->offset; + len = list->value->len; + dpa = dcd->dc.regions[rid].base + offset; + + extents[i].start_dpa = dpa; + extents[i].len = len; + memset(extents[i].tag, 0, 0x10); + extents[i].shared_seq = 0; + if (type == DC_EVENT_ADD_CAPACITY) { + group = cxl_insert_extent_to_extent_group(group, + extents[i].start_dpa, + extents[i].len, + extents[i].tag, + extents[i].shared_seq); + } + + list = list->next; + i++; + } + if (group) { + cxl_extent_group_list_insert_tail(&dcd->dc.extents_pending, group); + } + + /* + * CXL r3.1 section 8.2.9.2.1.6: Dynamic Capacity Event Record + * + * All Dynamic Capacity event records shall set the Event Record Severity + * field in the Common Event Record Format to Informational Event. All + * Dynamic Capacity related events shall be logged in the Dynamic Capacity + * Event Log. + */ + cxl_assign_event_header(hdr, &dynamic_capacity_uuid, flags, sizeof(dCap), + cxl_device_get_timestamp(&dcd->cxl_dstate)); + + dCap.type = type; + /* FIXME: for now, validity flag is cleared */ + dCap.validity_flags = 0; + stw_le_p(&dCap.host_id, hid); + /* only valid for DC_REGION_CONFIG_UPDATED event */ + dCap.updated_region_id = 0; + dCap.flags = 0; + for (i = 0; i < num_extents; i++) { + memcpy(&dCap.dynamic_capacity_extent, &extents[i], + sizeof(CXLDCExtentRaw)); + + if (i < num_extents - 1) { + /* Set "More" flag */ + dCap.flags |= BIT(0); + } + + if (cxl_event_insert(&dcd->cxl_dstate, enc_log, + (CXLEventRecordRaw *)&dCap)) { + cxl_event_irq_assert(dcd); + } + } +} + +void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, + CXLExtSelPolicy sel_policy, uint8_t region, + const char *tag, + CXLDynamicCapacityExtentList *extents, + Error **errp) +{ + switch (sel_policy) { + case CXL_EXT_SEL_POLICY_PRESCRIPTIVE: + qmp_cxl_process_dynamic_capacity_prescriptive(path, host_id, + DC_EVENT_ADD_CAPACITY, + region, extents, errp); + return; + default: + error_setg(errp, "Selection policy not supported"); + return; + } +} + +void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, + CXLExtRemovalPolicy removal_policy, + bool has_forced_removal, + bool forced_removal, + bool has_sanitize_on_release, + bool sanitize_on_release, + uint8_t region, + const char *tag, + CXLDynamicCapacityExtentList *extents, + Error **errp) +{ + CXLDCEventType type = DC_EVENT_RELEASE_CAPACITY; + + if (has_forced_removal && forced_removal) { + /* TODO: enable forced removal in the future */ + type = DC_EVENT_FORCED_RELEASE_CAPACITY; + error_setg(errp, "Forced removal not supported yet"); + return; + } + + switch (removal_policy) { + case CXL_EXT_REMOVAL_POLICY_PRESCRIPTIVE: + qmp_cxl_process_dynamic_capacity_prescriptive(path, host_id, type, + region, extents, errp); + return; + default: + error_setg(errp, "Removal policy not supported"); + return; + } +} + static void ct3_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/mem/cxl_type3_stubs.c b/hw/mem/cxl_type3_stubs.c index 3e1851e32b..45419bbefe 100644 --- a/hw/mem/cxl_type3_stubs.c +++ b/hw/mem/cxl_type3_stubs.c @@ -67,3 +67,28 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, { error_setg(errp, "CXL Type 3 support is not compiled in"); } + +void qmp_cxl_add_dynamic_capacity(const char *path, + uint16_t host_id, + CXLExtSelPolicy sel_policy, + uint8_t region, + const char *tag, + CXLDynamicCapacityExtentList *extents, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} + +void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, + CXLExtRemovalPolicy removal_policy, + bool has_forced_removal, + bool forced_removal, + bool has_sanitize_on_release, + bool sanitize_on_release, + uint8_t region, + const char *tag, + CXLDynamicCapacityExtentList *extents, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index df3511e91b..c69ff6b5de 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -443,6 +443,12 @@ typedef struct CXLDCExtent { } CXLDCExtent; typedef QTAILQ_HEAD(, CXLDCExtent) CXLDCExtentList; +typedef struct CXLDCExtentGroup { + CXLDCExtentList list; + QTAILQ_ENTRY(CXLDCExtentGroup) node; +} CXLDCExtentGroup; +typedef QTAILQ_HEAD(, CXLDCExtentGroup) CXLDCExtentGroupList; + typedef struct CXLDCRegion { uint64_t base; /* aligned to 256*MiB */ uint64_t decode_len; /* aligned to 256*MiB */ @@ -494,6 +500,7 @@ struct CXLType3Dev { */ uint64_t total_capacity; /* 256M aligned */ CXLDCExtentList extents; + CXLDCExtentGroupList extents_pending; uint32_t total_extent_count; uint32_t ext_list_gen_seq; @@ -555,4 +562,19 @@ CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, CXLDCExtent *extent); +void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, uint64_t dpa, + uint64_t len, uint8_t *tag, + uint16_t shared_seq); +bool test_any_bits_set(const unsigned long *addr, unsigned long nr, + unsigned long size); +bool cxl_extents_contains_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len); +CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, + uint64_t dpa, + uint64_t len, + uint8_t *tag, + uint16_t shared_seq); +void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, + CXLDCExtentGroup *group); +void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); #endif diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h index 5170b8dbf8..38cadaa0f3 100644 --- a/include/hw/cxl/cxl_events.h +++ b/include/hw/cxl/cxl_events.h @@ -166,4 +166,22 @@ typedef struct CXLEventMemoryModule { uint8_t reserved[0x3d]; } QEMU_PACKED CXLEventMemoryModule; +/* + * CXL r3.1 section Table 8-50: Dynamic Capacity Event Record + * All fields little endian. + */ +typedef struct CXLEventDynamicCapacity { + CXLEventRecordHdr hdr; + uint8_t type; + uint8_t validity_flags; + uint16_t host_id; + uint8_t updated_region_id; + uint8_t flags; + uint8_t reserved2[2]; + uint8_t dynamic_capacity_extent[0x28]; /* defined in cxl_device.h */ + uint8_t reserved[0x18]; + uint32_t extents_avail; + uint32_t tags_avail; +} QEMU_PACKED CXLEventDynamicCapacity; + #endif /* CXL_EVENTS_H */ diff --git a/qapi/cxl.json b/qapi/cxl.json index 4281726dec..57d9f82014 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -361,3 +361,146 @@ ## {'command': 'cxl-inject-correctable-error', 'data': {'path': 'str', 'type': 'CxlCorErrorType'}} + +## +# @CXLDynamicCapacityExtent: +# +# A single dynamic capacity extent +# +# @offset: The offset (in bytes) to the start of the region +# where the extent belongs to. +# +# @len: The length of the extent in bytes. +# +# Since: 9.1 +## +{ 'struct': 'CXLDynamicCapacityExtent', + 'data': { + 'offset':'uint64', + 'len': 'uint64' + } +} + +## +# @CXLExtSelPolicy: +# +# The policy to use for selecting which extents comprise the added +# capacity, as defined in cxl spec r3.1 Table 7-70. +# +# @free: 0h = Free +# +# @contiguous: 1h = Continuous +# +# @prescriptive: 2h = Prescriptive +# +# @enable-shared-access: 3h = Enable Shared Access +# +# Since: 9.1 +## +{ 'enum': 'CXLExtSelPolicy', + 'data': ['free', + 'contiguous', + 'prescriptive', + 'enable-shared-access'] +} + +## +# @cxl-add-dynamic-capacity: +# +# Command to initiate to add dynamic capacity extents to a host. It +# simulates operations defined in cxl spec r3.1 7.6.7.6.5. +# +# @path: CXL DCD canonical QOM path. +# +# @host-id: The "Host ID" field as defined in cxl spec r3.1 +# Table 7-70. +# +# @selection-policy: The "Selection Policy" bits as defined in +# cxl spec r3.1 Table 7-70. It specifies the policy to use for +# selecting which extents comprise the added capacity. +# +# @region: The "Region Number" field as defined in cxl spec r3.1 +# Table 7-70. The dynamic capacity region where the capacity +# is being added. Valid range is from 0-7. +# +# @tag: The "Tag" field as defined in cxl spec r3.1 Table 7-70. +# +# @extents: The "Extent List" field as defined in cxl spec r3.1 +# Table 7-70. +# +# Since : 9.1 +## +{ 'command': 'cxl-add-dynamic-capacity', + 'data': { 'path': 'str', + 'host-id': 'uint16', + 'selection-policy': 'CXLExtSelPolicy', + 'region': 'uint8', + '*tag': 'str', + 'extents': [ 'CXLDynamicCapacityExtent' ] + } +} + +## +# @CXLExtRemovalPolicy: +# +# The policy to use for selecting which extents comprise the released +# capacity, defined in the "Flags" field in cxl spec r3.1 Table 7-71. +# +# @tag-based: value = 0h. Extents are selected by the device based +# on tag, with no requirement for contiguous extents. +# +# @prescriptive: value = 1h. Extent list of capacity to release is +# included in the request payload. +# +# Since: 9.1 +## +{ 'enum': 'CXLExtRemovalPolicy', + 'data': ['tag-based', + 'prescriptive'] +} + +## +# @cxl-release-dynamic-capacity: +# +# Command to initiate to release dynamic capacity extents from a +# host. It simulates operations defined in cxl spec r3.1 7.6.7.6.6. +# +# @path: CXL DCD canonical QOM path. +# +# @host-id: The "Host ID" field as defined in cxl spec r3.1 +# Table 7-71. +# +# @removal-policy: Bit[3:0] of the "Flags" field as defined in cxl +# spec r3.1 Table 7-71. +# +# @forced-removal: Bit[4] of the "Flags" field in cxl spec r3.1 +# Table 7-71. When set, device does not wait for a Release +# Dynamic Capacity command from the host. Host immediately +# loses access to released capacity. +# +# @sanitize-on-release: Bit[5] of the "Flags" field in cxl spec r3.1 +# Table 7-71. When set, device should sanitize all released +# capacity as a result of this request. +# +# @region: The "Region Number" field as defined in cxl spec r3.1 +# Table 7-71. The dynamic capacity region where the capacity +# is being added. Valid range is from 0-7. +# +# @tag: The "Tag" field as defined in cxl spec r3.1 Table 7-71. +# +# @extents: The "Extent List" field as defined in cxl spec r3.1 +# Table 7-71. +# +# Since : 9.1 +## +{ 'command': 'cxl-release-dynamic-capacity', + 'data': { 'path': 'str', + 'host-id': 'uint16', + 'removal-policy': 'CXLExtRemovalPolicy', + '*forced-removal': 'bool', + '*sanitize-on-release': 'bool', + 'region': 'uint8', + '*tag': 'str', + 'extents': [ 'CXLDynamicCapacityExtent' ] + } +} From e4180db4e63b904183374c6e7ec07f66aa0decde Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:52 -0700 Subject: [PATCH 1780/2884] hw/mem/cxl_type3: Add DPA range validation for accesses to DC regions All DPA ranges in the DC regions are invalid to access until an extent covering the range has been successfully accepted by the host. A bitmap is added to each region to record whether a DC block in the region has been backed by a DC extent. Each bit in the bitmap represents a DC block. When a DC extent is accepted, all the bits representing the blocks in the extent are set, which will be cleared when the extent is released. Tested-by: Svetly Todorov Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-13-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 3 ++ hw/mem/cxl_type3.c | 76 +++++++++++++++++++++++++++++++++++++ include/hw/cxl/cxl_device.h | 7 ++++ 3 files changed, 86 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 64387f34ce..c4852112fe 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1655,6 +1655,7 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); ct3d->dc.total_extent_count += 1; + ct3_set_region_block_backed(ct3d, dpa, len); } /* Remove the first extent group in the pending list */ cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); @@ -1813,10 +1814,12 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, * list and update the extent count; */ QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { + ct3_clear_region_block_backed(ct3d, ent->start_dpa, ent->len); cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); } copy_extent_list(&ct3d->dc.extents, &updated_list); QTAILQ_FOREACH_SAFE(ent, &updated_list, node, ent_next) { + ct3_set_region_block_backed(ct3d, ent->start_dpa, ent->len); cxl_remove_extent_from_extent_list(&updated_list, ent); } ct3d->dc.total_extent_count = updated_list_size; diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index f53bcca6d3..0d18259ec0 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -672,6 +672,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) .flags = 0, }; ct3d->dc.total_capacity += region->len; + region->blk_bitmap = bitmap_new(region->len / region->block_size); } QTAILQ_INIT(&ct3d->dc.extents); QTAILQ_INIT(&ct3d->dc.extents_pending); @@ -683,6 +684,8 @@ static void cxl_destroy_dc_regions(CXLType3Dev *ct3d) { CXLDCExtent *ent, *ent_next; CXLDCExtentGroup *group, *group_next; + int i; + CXLDCRegion *region; QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); @@ -695,6 +698,11 @@ static void cxl_destroy_dc_regions(CXLType3Dev *ct3d) } g_free(group); } + + for (i = 0; i < ct3d->dc.num_regions; i++) { + region = &ct3d->dc.regions[i]; + g_free(region->blk_bitmap); + } } static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) @@ -926,6 +934,70 @@ static void ct3_exit(PCIDevice *pci_dev) } } +/* + * Mark the DPA range [dpa, dap + len - 1] to be backed and accessible. This + * happens when a DC extent is added and accepted by the host. + */ +void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len) +{ + CXLDCRegion *region; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return; + } + + bitmap_set(region->blk_bitmap, (dpa - region->base) / region->block_size, + len / region->block_size); +} + +/* + * Check whether the DPA range [dpa, dpa + len - 1] is backed with DC extents. + * Used when validating read/write to dc regions + */ +bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len) +{ + CXLDCRegion *region; + uint64_t nbits; + long nr; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return false; + } + + nr = (dpa - region->base) / region->block_size; + nbits = DIV_ROUND_UP(len, region->block_size); + /* + * if bits between [dpa, dpa + len) are all 1s, meaning the DPA range is + * backed with DC extents, return true; else return false. + */ + return find_next_zero_bit(region->blk_bitmap, nr + nbits, nr) == nr + nbits; +} + +/* + * Mark the DPA range [dpa, dap + len - 1] to be unbacked and inaccessible. + * This happens when a dc extent is released by the host. + */ +void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len) +{ + CXLDCRegion *region; + uint64_t nbits; + long nr; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return; + } + + nr = (dpa - region->base) / region->block_size; + nbits = len / region->block_size; + bitmap_clear(region->blk_bitmap, nr, nbits); +} + static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa) { int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; @@ -1030,6 +1102,10 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, *as = &ct3d->hostpmem_as; *dpa_offset -= vmr_size; } else { + if (!ct3_test_region_block_backed(ct3d, *dpa_offset, size)) { + return -ENODEV; + } + *as = &ct3d->dc.host_dc_as; *dpa_offset -= (vmr_size + pmr_size); } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index c69ff6b5de..0a4fcb2800 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -456,6 +456,7 @@ typedef struct CXLDCRegion { uint64_t block_size; uint32_t dsmadhandle; uint8_t flags; + unsigned long *blk_bitmap; } CXLDCRegion; struct CXLType3Dev { @@ -577,4 +578,10 @@ CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, CXLDCExtentGroup *group); void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); +void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len); +void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len); +bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len); #endif From 3083f018b59fd35b9ee993715694f967c49afeb1 Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:53 -0700 Subject: [PATCH 1781/2884] hw/cxl/cxl-mailbox-utils: Add superset extent release mailbox support With the change, we extend the extent release mailbox command processing to allow more flexible release. As long as the DPA range of the extent to release is covered by accepted extent(s) in the device, the release can be performed. Tested-by: Svetly Todorov Reviewed-by: Gregory Price Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-14-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index c4852112fe..74eeb6fde7 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1704,6 +1704,13 @@ static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, dpa = in->updated_entries[i].start_dpa; len = in->updated_entries[i].len; + /* Check if the DPA range is not fully backed with valid extents */ + if (!ct3_test_region_block_backed(ct3d, dpa, len)) { + ret = CXL_MBOX_INVALID_PA; + goto free_and_exit; + } + + /* After this point, extent overflow is the only error can happen */ while (len > 0) { QTAILQ_FOREACH(ent, updated_list, node) { range_init_nofail(&range, ent->start_dpa, ent->len); @@ -1718,14 +1725,7 @@ static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, if (range_contains(&range, dpa + len - 1)) { len2 = ent_start_dpa + ent_len - dpa - len; } else { - /* - * TODO: we reject the attempt to remove an extent - * that overlaps with multiple extents in the device - * for now. We will allow it once superset release - * support is added. - */ - ret = CXL_MBOX_INVALID_PA; - goto free_and_exit; + dpa = ent_start_dpa + ent_len; } len_done = ent_len - len1 - len2; @@ -1752,14 +1752,9 @@ static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, } len -= len_done; - /* len == 0 here until superset release is added */ break; } } - if (len) { - ret = CXL_MBOX_INVALID_PA; - goto free_and_exit; - } } } free_and_exit: From c51dca04281f9be6eacdad8fc8f9c7ddc87dcf3c Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Thu, 23 May 2024 10:44:54 -0700 Subject: [PATCH 1782/2884] hw/mem/cxl_type3: Allow to release extent superset in QMP interface Before the change, the QMP interface used for add/release DC extents only allows to release an extent whose DPA range is contained by a single accepted extent in the device. With the change, we relax the constraints. As long as the DPA range of the extent is covered by accepted extents, we allow the release. Tested-by: Svetly Todorov Reviewed-by: Gregory Price Reviewed-by: Jonathan Cameron Signed-off-by: Fan Ni Message-Id: <20240523174651.1089554-15-nifan.cxl@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 0d18259ec0..5d4a1276be 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1947,7 +1947,7 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, "cannot release extent with pending DPA range"); return; } - if (!cxl_extents_contains_dpa_range(&dcd->dc.extents, dpa, len)) { + if (!ct3_test_region_block_backed(dcd, dpa, len)) { error_setg(errp, "cannot release extent with non-existing DPA range"); return; From c5614ee3f2775534871914c02be4b5a61b71ed40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 May 2024 08:27:48 +0200 Subject: [PATCH 1783/2884] linux-headers: update to 6.10-rc1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Weißschuh Message-Id: <20240527-pvpanic-shutdown-v8-2-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/standard-headers/linux/ethtool.h | 55 ++++++++ include/standard-headers/linux/pci_regs.h | 6 + include/standard-headers/linux/virtio_bt.h | 1 - include/standard-headers/linux/virtio_mem.h | 2 + include/standard-headers/linux/virtio_net.h | 143 ++++++++++++++++++++ include/standard-headers/misc/pvpanic.h | 7 +- linux-headers/asm-generic/unistd.h | 5 +- linux-headers/asm-mips/unistd_n32.h | 1 + linux-headers/asm-mips/unistd_n64.h | 1 + linux-headers/asm-mips/unistd_o32.h | 1 + linux-headers/asm-powerpc/unistd_32.h | 1 + linux-headers/asm-powerpc/unistd_64.h | 1 + linux-headers/asm-s390/unistd_32.h | 1 + linux-headers/asm-s390/unistd_64.h | 1 + linux-headers/asm-x86/unistd_32.h | 1 + linux-headers/asm-x86/unistd_64.h | 1 + linux-headers/asm-x86/unistd_x32.h | 2 + linux-headers/linux/kvm.h | 4 +- linux-headers/linux/stddef.h | 8 ++ 19 files changed, 236 insertions(+), 6 deletions(-) diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 01503784d2..b0b4b68410 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -752,6 +752,61 @@ enum ethtool_module_power_mode { ETHTOOL_MODULE_POWER_MODE_HIGH, }; +/** + * enum ethtool_pse_types - Types of PSE controller. + * @ETHTOOL_PSE_UNKNOWN: Type of PSE controller is unknown + * @ETHTOOL_PSE_PODL: PSE controller which support PoDL + * @ETHTOOL_PSE_C33: PSE controller which support Clause 33 (PoE) + */ +enum ethtool_pse_types { + ETHTOOL_PSE_UNKNOWN = 1 << 0, + ETHTOOL_PSE_PODL = 1 << 1, + ETHTOOL_PSE_C33 = 1 << 2, +}; + +/** + * enum ethtool_c33_pse_admin_state - operational state of the PoDL PSE + * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState + * @ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN: state of PSE functions is unknown + * @ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED: PSE functions are disabled + * @ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED: PSE functions are enabled + */ +enum ethtool_c33_pse_admin_state { + ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN = 1, + ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED, + ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED, +}; + +/** + * enum ethtool_c33_pse_pw_d_status - power detection status of the PSE. + * IEEE 802.3-2022 30.9.1.1.3 aPoDLPSEPowerDetectionStatus: + * @ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN: PSE status is unknown + * @ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED: The enumeration "disabled" + * indicates that the PSE State diagram is in the state DISABLED. + * @ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING: The enumeration "searching" + * indicates the PSE State diagram is in a state other than those + * listed. + * @ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING: The enumeration + * "deliveringPower" indicates that the PSE State diagram is in the + * state POWER_ON. + * @ETHTOOL_C33_PSE_PW_D_STATUS_TEST: The enumeration "test" indicates that + * the PSE State diagram is in the state TEST_MODE. + * @ETHTOOL_C33_PSE_PW_D_STATUS_FAULT: The enumeration "fault" indicates that + * the PSE State diagram is in the state TEST_ERROR. + * @ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT: The enumeration "otherFault" + * indicates that the PSE State diagram is in the state IDLE due to + * the variable error_condition = true. + */ +enum ethtool_c33_pse_pw_d_status { + ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN = 1, + ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED, + ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING, + ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING, + ETHTOOL_C33_PSE_PW_D_STATUS_TEST, + ETHTOOL_C33_PSE_PW_D_STATUS_FAULT, + ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT, +}; + /** * enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index a39193213f..94c00996e6 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -1144,8 +1144,14 @@ #define PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH 0x0003ffff #define PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX 0x000000ff +#define PCI_DOE_DATA_OBJECT_DISC_REQ_3_VER 0x0000ff00 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID 0x0000ffff #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000 +/* Compute Express Link (CXL r3.1, sec 8.1.5) */ +#define PCI_DVSEC_CXL_PORT 3 +#define PCI_DVSEC_CXL_PORT_CTL 0x0c +#define PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR 0x00000001 + #endif /* LINUX_PCI_REGS_H */ diff --git a/include/standard-headers/linux/virtio_bt.h b/include/standard-headers/linux/virtio_bt.h index a11ecc3f92..6f0dee7e32 100644 --- a/include/standard-headers/linux/virtio_bt.h +++ b/include/standard-headers/linux/virtio_bt.h @@ -13,7 +13,6 @@ enum virtio_bt_config_type { VIRTIO_BT_CONFIG_TYPE_PRIMARY = 0, - VIRTIO_BT_CONFIG_TYPE_AMP = 1, }; enum virtio_bt_config_vendor { diff --git a/include/standard-headers/linux/virtio_mem.h b/include/standard-headers/linux/virtio_mem.h index 18c74c527c..6bfa41bd8b 100644 --- a/include/standard-headers/linux/virtio_mem.h +++ b/include/standard-headers/linux/virtio_mem.h @@ -90,6 +90,8 @@ #define VIRTIO_MEM_F_ACPI_PXM 0 /* unplugged memory must not be accessed */ #define VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE 1 +/* plugged memory will remain plugged when suspending+resuming */ +#define VIRTIO_MEM_F_PERSISTENT_SUSPEND 2 /* --- virtio-mem: guest -> host requests --- */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index 0f88417742..fc594fe5fc 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -56,6 +56,7 @@ #define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#define VIRTIO_NET_F_DEVICE_STATS 50 /* Device can provide device-level statistics. */ #define VIRTIO_NET_F_VQ_NOTF_COAL 52 /* Device supports virtqueue notification coalescing */ #define VIRTIO_NET_F_NOTF_COAL 53 /* Device supports notifications coalescing */ #define VIRTIO_NET_F_GUEST_USO4 54 /* Guest can handle USOv4 in. */ @@ -406,4 +407,146 @@ struct virtio_net_ctrl_coal_vq { struct virtio_net_ctrl_coal coal; }; +/* + * Device Statistics + */ +#define VIRTIO_NET_CTRL_STATS 8 +#define VIRTIO_NET_CTRL_STATS_QUERY 0 +#define VIRTIO_NET_CTRL_STATS_GET 1 + +struct virtio_net_stats_capabilities { + +#define VIRTIO_NET_STATS_TYPE_CVQ (1ULL << 32) + +#define VIRTIO_NET_STATS_TYPE_RX_BASIC (1ULL << 0) +#define VIRTIO_NET_STATS_TYPE_RX_CSUM (1ULL << 1) +#define VIRTIO_NET_STATS_TYPE_RX_GSO (1ULL << 2) +#define VIRTIO_NET_STATS_TYPE_RX_SPEED (1ULL << 3) + +#define VIRTIO_NET_STATS_TYPE_TX_BASIC (1ULL << 16) +#define VIRTIO_NET_STATS_TYPE_TX_CSUM (1ULL << 17) +#define VIRTIO_NET_STATS_TYPE_TX_GSO (1ULL << 18) +#define VIRTIO_NET_STATS_TYPE_TX_SPEED (1ULL << 19) + + uint64_t supported_stats_types[1]; +}; + +struct virtio_net_ctrl_queue_stats { + struct { + uint16_t vq_index; + uint16_t reserved[3]; + uint64_t types_bitmap[1]; + } stats[1]; +}; + +struct virtio_net_stats_reply_hdr { +#define VIRTIO_NET_STATS_TYPE_REPLY_CVQ 32 + +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_BASIC 0 +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_CSUM 1 +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_GSO 2 +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_SPEED 3 + +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_BASIC 16 +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_CSUM 17 +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_GSO 18 +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_SPEED 19 + uint8_t type; + uint8_t reserved; + uint16_t vq_index; + uint16_t reserved1; + uint16_t size; +}; + +struct virtio_net_stats_cvq { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t command_num; + uint64_t ok_num; +}; + +struct virtio_net_stats_rx_basic { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t rx_notifications; + + uint64_t rx_packets; + uint64_t rx_bytes; + + uint64_t rx_interrupts; + + uint64_t rx_drops; + uint64_t rx_drop_overruns; +}; + +struct virtio_net_stats_tx_basic { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t tx_notifications; + + uint64_t tx_packets; + uint64_t tx_bytes; + + uint64_t tx_interrupts; + + uint64_t tx_drops; + uint64_t tx_drop_malformed; +}; + +struct virtio_net_stats_rx_csum { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t rx_csum_valid; + uint64_t rx_needs_csum; + uint64_t rx_csum_none; + uint64_t rx_csum_bad; +}; + +struct virtio_net_stats_tx_csum { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t tx_csum_none; + uint64_t tx_needs_csum; +}; + +struct virtio_net_stats_rx_gso { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t rx_gso_packets; + uint64_t rx_gso_bytes; + uint64_t rx_gso_packets_coalesced; + uint64_t rx_gso_bytes_coalesced; +}; + +struct virtio_net_stats_tx_gso { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t tx_gso_packets; + uint64_t tx_gso_bytes; + uint64_t tx_gso_segments; + uint64_t tx_gso_segments_bytes; + uint64_t tx_gso_packets_noseg; + uint64_t tx_gso_bytes_noseg; +}; + +struct virtio_net_stats_rx_speed { + struct virtio_net_stats_reply_hdr hdr; + + /* rx_{packets,bytes}_allowance_exceeded are too long. So rename to + * short name. + */ + uint64_t rx_ratelimit_packets; + uint64_t rx_ratelimit_bytes; +}; + +struct virtio_net_stats_tx_speed { + struct virtio_net_stats_reply_hdr hdr; + + /* tx_{packets,bytes}_allowance_exceeded are too long. So rename to + * short name. + */ + uint64_t tx_ratelimit_packets; + uint64_t tx_ratelimit_bytes; +}; + #endif /* _LINUX_VIRTIO_NET_H */ diff --git a/include/standard-headers/misc/pvpanic.h b/include/standard-headers/misc/pvpanic.h index 54b7485390..b115094431 100644 --- a/include/standard-headers/misc/pvpanic.h +++ b/include/standard-headers/misc/pvpanic.h @@ -3,7 +3,10 @@ #ifndef __PVPANIC_H__ #define __PVPANIC_H__ -#define PVPANIC_PANICKED (1 << 0) -#define PVPANIC_CRASH_LOADED (1 << 1) +#include "standard-headers/linux/const.h" + +#define PVPANIC_PANICKED _BITUL(0) +#define PVPANIC_CRASH_LOADED _BITUL(1) +#define PVPANIC_SHUTDOWN _BITUL(2) #endif /* __PVPANIC_H__ */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 75f00965ab..d983c48a3b 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -842,8 +842,11 @@ __SYSCALL(__NR_lsm_set_self_attr, sys_lsm_set_self_attr) #define __NR_lsm_list_modules 461 __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules) +#define __NR_mseal 462 +__SYSCALL(__NR_mseal, sys_mseal) + #undef __NR_syscalls -#define __NR_syscalls 462 +#define __NR_syscalls 463 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index ce2e050a9b..fc93b3be30 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -390,5 +390,6 @@ #define __NR_lsm_get_self_attr (__NR_Linux + 459) #define __NR_lsm_set_self_attr (__NR_Linux + 460) #define __NR_lsm_list_modules (__NR_Linux + 461) +#define __NR_mseal (__NR_Linux + 462) #endif /* _ASM_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index 5bfb3733ff..e72a3eb2c9 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -366,5 +366,6 @@ #define __NR_lsm_get_self_attr (__NR_Linux + 459) #define __NR_lsm_set_self_attr (__NR_Linux + 460) #define __NR_lsm_list_modules (__NR_Linux + 461) +#define __NR_mseal (__NR_Linux + 462) #endif /* _ASM_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 02eaecd020..b86eb0786c 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -436,5 +436,6 @@ #define __NR_lsm_get_self_attr (__NR_Linux + 459) #define __NR_lsm_set_self_attr (__NR_Linux + 460) #define __NR_lsm_list_modules (__NR_Linux + 461) +#define __NR_mseal (__NR_Linux + 462) #endif /* _ASM_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index bbab08d6ec..28627b6546 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -443,6 +443,7 @@ #define __NR_lsm_get_self_attr 459 #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 +#define __NR_mseal 462 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index af34cde70f..1fc42a8300 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -415,6 +415,7 @@ #define __NR_lsm_get_self_attr 459 #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 +#define __NR_mseal 462 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index a3ece69d82..7706c21b87 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -434,5 +434,6 @@ #define __NR_lsm_get_self_attr 459 #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 +#define __NR_mseal 462 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 8c5fd93495..62082d592d 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -382,5 +382,6 @@ #define __NR_lsm_get_self_attr 459 #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 +#define __NR_mseal 462 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 5c9c329e93..fb7b8b169b 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -452,6 +452,7 @@ #define __NR_lsm_get_self_attr 459 #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 +#define __NR_mseal 462 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index d9aab7ae87..da439afee1 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -374,6 +374,7 @@ #define __NR_lsm_get_self_attr 459 #define __NR_lsm_set_self_attr 460 #define __NR_lsm_list_modules 461 +#define __NR_mseal 462 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 63cdd1ee43..4fcb607c72 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -318,6 +318,7 @@ #define __NR_set_mempolicy_home_node (__X32_SYSCALL_BIT + 450) #define __NR_cachestat (__X32_SYSCALL_BIT + 451) #define __NR_fchmodat2 (__X32_SYSCALL_BIT + 452) +#define __NR_map_shadow_stack (__X32_SYSCALL_BIT + 453) #define __NR_futex_wake (__X32_SYSCALL_BIT + 454) #define __NR_futex_wait (__X32_SYSCALL_BIT + 455) #define __NR_futex_requeue (__X32_SYSCALL_BIT + 456) @@ -326,6 +327,7 @@ #define __NR_lsm_get_self_attr (__X32_SYSCALL_BIT + 459) #define __NR_lsm_set_self_attr (__X32_SYSCALL_BIT + 460) #define __NR_lsm_list_modules (__X32_SYSCALL_BIT + 461) +#define __NR_mseal (__X32_SYSCALL_BIT + 462) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 038731cdef..c93876ca0b 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -1217,9 +1217,9 @@ struct kvm_vfio_spapr_tce { /* Available with KVM_CAP_SPAPR_RESIZE_HPT */ #define KVM_PPC_RESIZE_HPT_PREPARE _IOR(KVMIO, 0xad, struct kvm_ppc_resize_hpt) #define KVM_PPC_RESIZE_HPT_COMMIT _IOR(KVMIO, 0xae, struct kvm_ppc_resize_hpt) -/* Available with KVM_CAP_PPC_RADIX_MMU or KVM_CAP_PPC_HASH_MMU_V3 */ +/* Available with KVM_CAP_PPC_MMU_RADIX or KVM_CAP_PPC_MMU_HASH_V3 */ #define KVM_PPC_CONFIGURE_V3_MMU _IOW(KVMIO, 0xaf, struct kvm_ppc_mmuv3_cfg) -/* Available with KVM_CAP_PPC_RADIX_MMU */ +/* Available with KVM_CAP_PPC_MMU_RADIX */ #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) /* Available with KVM_CAP_PPC_GET_CPU_CHAR */ #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) diff --git a/linux-headers/linux/stddef.h b/linux-headers/linux/stddef.h index bf9749dd14..96aa341942 100644 --- a/linux-headers/linux/stddef.h +++ b/linux-headers/linux/stddef.h @@ -55,4 +55,12 @@ #define __counted_by(m) #endif +#ifndef __counted_by_le +#define __counted_by_le(m) +#endif + +#ifndef __counted_by_be +#define __counted_by_be(m) +#endif + #endif /* _LINUX_STDDEF_H */ From 9b13640da3f94c0fbacbae6d23bd91febfa44588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 May 2024 08:27:49 +0200 Subject: [PATCH 1784/2884] hw/misc/pvpanic: centralize definition of supported events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The different components of pvpanic duplicate the list of supported events. Move it to the shared header file to minimize changes when new events are added. MST: tweak: keep header included in pvpanic.c to avoid header dependency, rebase. Reviewed-by: Thomas Huth Reviewed-by: Cornelia Huck Signed-off-by: Thomas Weißschuh Message-Id: <20240527-pvpanic-shutdown-v8-3-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/misc/pvpanic-isa.c | 3 +-- hw/misc/pvpanic-pci.c | 2 +- hw/misc/pvpanic.c | 2 +- include/hw/misc/pvpanic.h | 4 ++++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index b4f84c4110..9a923b7869 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -21,7 +21,6 @@ #include "hw/misc/pvpanic.h" #include "qom/object.h" #include "hw/isa/isa.h" -#include "standard-headers/misc/pvpanic.h" #include "hw/acpi/acpi_aml_interface.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE) @@ -102,7 +101,7 @@ static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope) static Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505), DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, - PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), + PVPANIC_EVENTS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 4d44a881da..106d03ccd6 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -55,7 +55,7 @@ static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp) static Property pvpanic_pci_properties[] = { DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, - PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), + PVPANIC_EVENTS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 80289ecf5f..4b2307d2c2 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -27,7 +27,7 @@ static void handle_event(int event) { static bool logged; - if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASH_LOADED) && !logged) { + if (event & ~PVPANIC_EVENTS && !logged) { qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event); logged = true; } diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index fab94165d0..1e5b20e4ed 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -18,6 +18,10 @@ #include "exec/memory.h" #include "qom/object.h" +#include "standard-headers/misc/pvpanic.h" + +#define PVPANIC_EVENTS (PVPANIC_PANICKED | PVPANIC_CRASH_LOADED) + #define TYPE_PVPANIC_ISA_DEVICE "pvpanic" #define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci" From 462dc749c110fe8e41ae0fb554b9bc2f2671e973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 May 2024 08:27:50 +0200 Subject: [PATCH 1785/2884] tests/qtest/pvpanic: use centralized definition of supported events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the necessity to update all tests when new events are added to the device. Acked-by: Thomas Huth Reviewed-by: Cornelia Huck Signed-off-by: Thomas Weißschuh Message-Id: <20240527-pvpanic-shutdown-v8-4-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/pvpanic-pci-test.c | 5 +++-- tests/qtest/pvpanic-test.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index 2c05b376ba..b372caf41d 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -16,6 +16,7 @@ #include "qapi/qmp/qdict.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" +#include "hw/misc/pvpanic.h" #include "hw/pci/pci_regs.h" static void test_panic_nopause(void) @@ -34,7 +35,7 @@ static void test_panic_nopause(void) bar = qpci_iomap(dev, 0, NULL); qpci_memread(dev, bar, 0, &val, sizeof(val)); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); val = 1; qpci_memwrite(dev, bar, 0, &val, sizeof(val)); @@ -67,7 +68,7 @@ static void test_panic(void) bar = qpci_iomap(dev, 0, NULL); qpci_memread(dev, bar, 0, &val, sizeof(val)); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); val = 1; qpci_memwrite(dev, bar, 0, &val, sizeof(val)); diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index 78f1cf8186..ccc603472f 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qapi/qmp/qdict.h" +#include "hw/misc/pvpanic.h" static void test_panic_nopause(void) { @@ -20,7 +21,7 @@ static void test_panic_nopause(void) qts = qtest_init("-device pvpanic -action panic=none"); val = qtest_inb(qts, 0x505); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); qtest_outb(qts, 0x505, 0x1); @@ -43,7 +44,7 @@ static void test_panic(void) qts = qtest_init("-device pvpanic -action panic=pause"); val = qtest_inb(qts, 0x505); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); qtest_outb(qts, 0x505, 0x1); From 6269086b0179e3d70750672174ed7fbd29ac7eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 May 2024 08:27:51 +0200 Subject: [PATCH 1786/2884] hw/misc/pvpanic: add support for normal shutdowns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shutdown requests are normally hardware dependent. By extending pvpanic to also handle shutdown requests, guests can submit such requests with an easily implementable and cross-platform mechanism. Acked-by: Cornelia Huck Signed-off-by: Thomas Weißschuh Message-Id: <20240527-pvpanic-shutdown-v8-5-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/misc/pvpanic.c | 5 +++++ include/hw/misc/pvpanic.h | 4 +++- include/sysemu/runstate.h | 1 + system/runstate.c | 5 +++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 4b2307d2c2..3b893340c0 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -41,6 +41,11 @@ static void handle_event(int event) qemu_system_guest_crashloaded(NULL); return; } + + if (event & PVPANIC_SHUTDOWN) { + qemu_system_guest_pvshutdown(); + return; + } } /* return supported events on read */ diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index 1e5b20e4ed..9a71a5ad0d 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -20,7 +20,9 @@ #include "standard-headers/misc/pvpanic.h" -#define PVPANIC_EVENTS (PVPANIC_PANICKED | PVPANIC_CRASH_LOADED) +#define PVPANIC_EVENTS (PVPANIC_PANICKED | \ + PVPANIC_CRASH_LOADED | \ + PVPANIC_SHUTDOWN) #define TYPE_PVPANIC_ISA_DEVICE "pvpanic" #define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci" diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h index 0117d243c4..e210a37abf 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -104,6 +104,7 @@ void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(ShutdownCause reason); void qemu_system_guest_panicked(GuestPanicInformation *info); void qemu_system_guest_crashloaded(GuestPanicInformation *info); +void qemu_system_guest_pvshutdown(void); bool qemu_system_dump_in_progress(void); #endif diff --git a/system/runstate.c b/system/runstate.c index ec32e270cb..fc49fd3e61 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -584,6 +584,11 @@ void qemu_system_guest_crashloaded(GuestPanicInformation *info) qapi_free_GuestPanicInformation(info); } +void qemu_system_guest_pvshutdown(void) +{ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); +} + void qemu_system_reset_request(ShutdownCause reason) { if (reboot_action == REBOOT_ACTION_SHUTDOWN && From 8db1f7be788b23f8eca189fe4546298ed387e9cb Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Mon, 27 May 2024 08:27:52 +0200 Subject: [PATCH 1787/2884] pvpanic: Emit GUEST_PVSHUTDOWN QMP event on pvpanic shutdown signal Emit a QMP event on receiving a PVPANIC_SHUTDOWN event. Even though a typical SHUTDOWN event will be sent, it will be indistinguishable from a shutdown originating from other cases (e.g. KVM exit due to KVM_SYSTEM_EVENT_SHUTDOWN) that also issue the guest-shutdown cause. A management layer application can detect the new GUEST_PVSHUTDOWN event to determine if the guest is using the pvpanic interface to request shutdowns. Signed-off-by: Alejandro Jimenez Message-Id: <20240527-pvpanic-shutdown-v8-6-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- qapi/run-state.json | 14 ++++++++++++++ system/runstate.c | 1 + 2 files changed, 15 insertions(+) diff --git a/qapi/run-state.json b/qapi/run-state.json index f8773f23b2..5ac0fec852 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -462,6 +462,20 @@ { 'event': 'GUEST_CRASHLOADED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } +## +# @GUEST_PVSHUTDOWN: +# +# Emitted when guest submits a shutdown request via pvpanic interface +# +# Since: 9.1 +# +# Example: +# +# <- { "event": "GUEST_PVSHUTDOWN", +# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } +## +{ 'event': 'GUEST_PVSHUTDOWN' } + ## # @GuestPanicAction: # diff --git a/system/runstate.c b/system/runstate.c index fc49fd3e61..c833316f6d 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -586,6 +586,7 @@ void qemu_system_guest_crashloaded(GuestPanicInformation *info) void qemu_system_guest_pvshutdown(void) { + qapi_event_send_guest_pvshutdown(); qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } From b279c3c88da3e8a301e4436fcdf233c0838ed4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 May 2024 08:27:53 +0200 Subject: [PATCH 1788/2884] tests/qtest/pvpanic: add tests for pvshutdown event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validate that a shutdown via the pvpanic device emits the correct QMP events. Signed-off-by: Thomas Weißschuh Reviewed-by: Thomas Huth Message-Id: <20240527-pvpanic-shutdown-v8-7-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/pvpanic-pci-test.c | 39 ++++++++++++++++++++++++++++++++++ tests/qtest/pvpanic-test.c | 29 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index b372caf41d..dc021c2fdf 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -85,11 +85,50 @@ static void test_panic(void) qtest_quit(qts); } +static void test_pvshutdown(void) +{ + uint8_t val; + QDict *response, *data; + QTestState *qts; + QPCIBus *pcibus; + QPCIDevice *dev; + QPCIBar bar; + + qts = qtest_init("-device pvpanic-pci,addr=04.0"); + pcibus = qpci_new_pc(qts, NULL); + dev = qpci_device_find(pcibus, QPCI_DEVFN(0x4, 0x0)); + qpci_device_enable(dev); + bar = qpci_iomap(dev, 0, NULL); + + qpci_memread(dev, bar, 0, &val, sizeof(val)); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); + + val = PVPANIC_SHUTDOWN; + qpci_memwrite(dev, bar, 0, &val, sizeof(val)); + + response = qtest_qmp_eventwait_ref(qts, "GUEST_PVSHUTDOWN"); + qobject_unref(response); + + response = qtest_qmp_eventwait_ref(qts, "SHUTDOWN"); + g_assert(qdict_haskey(response, "data")); + data = qdict_get_qdict(response, "data"); + g_assert(qdict_haskey(data, "guest")); + g_assert(qdict_get_bool(data, "guest")); + g_assert(qdict_haskey(data, "reason")); + g_assert_cmpstr(qdict_get_str(data, "reason"), ==, "guest-shutdown"); + qobject_unref(response); + + g_free(dev); + qpci_free_pc(pcibus); + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); qtest_add_func("/pvpanic-pci/panic", test_panic); qtest_add_func("/pvpanic-pci/panic-nopause", test_panic_nopause); + qtest_add_func("/pvpanic-pci/pvshutdown", test_pvshutdown); return g_test_run(); } diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index ccc603472f..d49d2ba931 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -58,11 +58,40 @@ static void test_panic(void) qtest_quit(qts); } +static void test_pvshutdown(void) +{ + uint8_t val; + QDict *response, *data; + QTestState *qts; + + qts = qtest_init("-device pvpanic"); + + val = qtest_inb(qts, 0x505); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); + + qtest_outb(qts, 0x505, PVPANIC_SHUTDOWN); + + response = qtest_qmp_eventwait_ref(qts, "GUEST_PVSHUTDOWN"); + qobject_unref(response); + + response = qtest_qmp_eventwait_ref(qts, "SHUTDOWN"); + g_assert(qdict_haskey(response, "data")); + data = qdict_get_qdict(response, "data"); + g_assert(qdict_haskey(data, "guest")); + g_assert(qdict_get_bool(data, "guest")); + g_assert(qdict_haskey(data, "reason")); + g_assert_cmpstr(qdict_get_str(data, "reason"), ==, "guest-shutdown"); + qobject_unref(response); + + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); qtest_add_func("/pvpanic/panic", test_panic); qtest_add_func("/pvpanic/panic-nopause", test_panic_nopause); + qtest_add_func("/pvpanic/pvshutdown", test_pvshutdown); return g_test_run(); } From 0c0cc13d319cf7b876f327fa1c5cc1866ad868cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 May 2024 08:27:54 +0200 Subject: [PATCH 1789/2884] Revert "docs/specs/pvpanic: mark shutdown event as not implemented" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The missing functionality has been implemented now. This reverts commit e739d1935c461d0668057e9dbba9d06f728d29ec. Signed-off-by: Thomas Weißschuh Message-Id: <20240527-pvpanic-shutdown-v8-8-5a28ec02558b@t-8ch.de> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/specs/pvpanic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specs/pvpanic.rst b/docs/specs/pvpanic.rst index b0f27860ec..61a80480ed 100644 --- a/docs/specs/pvpanic.rst +++ b/docs/specs/pvpanic.rst @@ -29,7 +29,7 @@ bit 1 a guest panic has happened and will be handled by the guest; the host should record it or report it, but should not affect the execution of the guest. -bit 2 (to be implemented) +bit 2 a regular guest shutdown has happened and should be processed by the host PCI Interface From a113d041e8d0b152d72a7c2bf47dd09aabf9ade2 Mon Sep 17 00:00:00 2001 From: Cindy Lu Date: Tue, 28 May 2024 16:48:15 +0800 Subject: [PATCH 1790/2884] virtio-pci: Fix the failure process in kvm_virtio_pci_vector_use_one() In function kvm_virtio_pci_vector_use_one(), the function will only use the irqfd/vector for itself. Therefore, in the undo label, the failing process is incorrect. To fix this, we can just remove this label. Fixes: f9a09ca3ea ("vhost: add support for configure interrupt") Cc: qemu-stable@nongnu.org Signed-off-by: Cindy Lu Message-Id: <20240528084840.194538-1-lulu@redhat.com> Reviewed-by: Peter Maydell Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 7d62e92365..5941f1a94d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -898,7 +898,7 @@ static int kvm_virtio_pci_vector_use_one(VirtIOPCIProxy *proxy, int queue_no) } ret = kvm_virtio_pci_vq_vector_use(proxy, vector); if (ret < 0) { - goto undo; + return ret; } /* * If guest supports masking, set up irqfd now. @@ -908,25 +908,11 @@ static int kvm_virtio_pci_vector_use_one(VirtIOPCIProxy *proxy, int queue_no) ret = kvm_virtio_pci_irqfd_use(proxy, n, vector); if (ret < 0) { kvm_virtio_pci_vq_vector_release(proxy, vector); - goto undo; + return ret; } } return 0; -undo: - - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - return ret; - } - if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { - ret = virtio_pci_get_notifier(proxy, queue_no, &n, &vector); - if (ret < 0) { - return ret; - } - kvm_virtio_pci_irqfd_release(proxy, n, vector); - } - return ret; } static int kvm_virtio_pci_vector_vq_use(VirtIOPCIProxy *proxy, int nvqs) { From e6c9c9e7f46a9ecaf1d90a68595915d65cd9d72d Mon Sep 17 00:00:00 2001 From: Ira Weiny Date: Fri, 31 May 2024 11:22:05 -0500 Subject: [PATCH 1791/2884] hw/cxl: Fix read from bogus memory Peter and coverity report: We've passed '&data' to address_space_write(), which means "read from the address on the stack where the function argument 'data' lives", so instead of writing 64 bytes of data to the guest , we'll write 64 bytes which start with a host pointer value and then continue with whatever happens to be on the host stack after that. Indeed the intention was to write 64 bytes of data at the address given. Fix the parameter to address_space_write(). Reported-by: Peter Maydell Link: https://lore.kernel.org/all/CAFEAcA-u4sytGwTKsb__Y+_+0O2-WwARntm3x8WNhvL1WfHOBg@mail.gmail.com/ Fixes: 6bda41a69bdc ("hw/cxl: Add clear poison mailbox command support.") Cc: Jonathan Cameron Signed-off-by: Ira Weiny Message-Id: <20240531-fix-poison-set-cacheline-v1-1-e3bc7e8f1158@intel.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Jonathan Cameron --- hw/mem/cxl_type3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 5d4a1276be..3274e5dcbb 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1292,7 +1292,7 @@ static bool set_cacheline(CXLType3Dev *ct3d, uint64_t dpa_offset, uint8_t *data) dpa_offset -= (vmr_size + pmr_size); } - address_space_write(as, dpa_offset, MEMTXATTRS_UNSPECIFIED, &data, + address_space_write(as, dpa_offset, MEMTXATTRS_UNSPECIFIED, data, CXL_CACHE_LINE_SIZE); return true; } From 5d98e18823af6d5230fca8098a7ee966aaedeb29 Mon Sep 17 00:00:00 2001 From: Jiqian Chen Date: Thu, 6 Jun 2024 18:22:05 +0800 Subject: [PATCH 1792/2884] virtio-pci: implement No_Soft_Reset bit In current code, when guest does S3, virtio-gpu are reset due to the bit No_Soft_Reset is not set. After resetting, the display resources of virtio-gpu are destroyed, then the display can't come back and only show blank after resuming. Implement No_Soft_Reset bit of PCI_PM_CTRL register, then guest can check this bit, if this bit is set, the devices resetting will not be done, and then the display can work after resuming. No_Soft_Reset bit is implemented for all virtio devices, and was tested only on virtio-gpu device. Set it false by default for safety. Signed-off-by: Jiqian Chen Message-Id: <20240606102205.114671-3-Jiqian.Chen@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/machine.c | 1 + hw/virtio/virtio-pci.c | 29 +++++++++++++++++++++++++++++ include/hw/virtio/virtio-pci.h | 5 +++++ 3 files changed, 35 insertions(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index 655d75c21f..f4cba6496c 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -38,6 +38,7 @@ GlobalProperty hw_compat_9_0[] = { {"arm-cpu", "backcompat-cntfrq", "true" }, {"scsi-disk-base", "migrate-emulated-scsi-request", "false" }, {"vfio-pci", "skip-vsc-check", "false" }, + { "virtio-pci", "x-pcie-pm-no-soft-reset", "off" }, }; const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 5941f1a94d..9534730bba 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2222,6 +2222,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_cap_lnkctl_init(pci_dev); } + if (proxy->flags & VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET) { + pci_set_word(pci_dev->config + pos + PCI_PM_CTRL, + PCI_PM_CTRL_NO_SOFT_RESET); + } + if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) { /* Init Power Management Control Register */ pci_set_word(pci_dev->wmask + pos + PCI_PM_CTRL, @@ -2284,11 +2289,33 @@ static void virtio_pci_reset(DeviceState *qdev) } } +static bool virtio_pci_no_soft_reset(PCIDevice *dev) +{ + uint16_t pmcsr; + + if (!pci_is_express(dev) || !dev->exp.pm_cap) { + return false; + } + + pmcsr = pci_get_word(dev->config + dev->exp.pm_cap + PCI_PM_CTRL); + + /* + * When No_Soft_Reset bit is set and the device + * is in D3hot state, don't reset device + */ + return (pmcsr & PCI_PM_CTRL_NO_SOFT_RESET) && + (pmcsr & PCI_PM_CTRL_STATE_MASK) == 3; +} + static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) { PCIDevice *dev = PCI_DEVICE(obj); DeviceState *qdev = DEVICE(obj); + if (virtio_pci_no_soft_reset(dev)) { + return; + } + virtio_pci_reset(qdev); if (pci_is_express(dev)) { @@ -2328,6 +2355,8 @@ static Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_INIT_LNKCTL_BIT, true), DEFINE_PROP_BIT("x-pcie-pm-init", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_INIT_PM_BIT, true), + DEFINE_PROP_BIT("x-pcie-pm-no-soft-reset", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET_BIT, false), DEFINE_PROP_BIT("x-pcie-flr-init", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_INIT_FLR_BIT, true), DEFINE_PROP_BIT("aer", VirtIOPCIProxy, flags, diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 59d88018c1..9e67ba38c7 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -43,6 +43,7 @@ enum { VIRTIO_PCI_FLAG_INIT_FLR_BIT, VIRTIO_PCI_FLAG_AER_BIT, VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED_BIT, + VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET_BIT, }; /* Need to activate work-arounds for buggy guests at vmstate load. */ @@ -79,6 +80,10 @@ enum { /* Init Power Management */ #define VIRTIO_PCI_FLAG_INIT_PM (1 << VIRTIO_PCI_FLAG_INIT_PM_BIT) +/* Init The No_Soft_Reset bit of Power Management */ +#define VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET \ + (1 << VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET_BIT) + /* Init Function Level Reset capability */ #define VIRTIO_PCI_FLAG_INIT_FLR (1 << VIRTIO_PCI_FLAG_INIT_FLR_BIT) From f72fc16910c8f44edf052f52672e0e63bbbc773c Mon Sep 17 00:00:00 2001 From: Yuxue Liu Date: Thu, 11 Apr 2024 15:35:55 +0800 Subject: [PATCH 1793/2884] vhost-user-test: no set non-blocking for cal fd less than 0. In the scenario where vhost-user sets eventfd to -1, qemu_chr_fe_get_msgfds retrieves fd as -1. When vhost_user_read receives, it does not perform blocking operations on the descriptor with fd=-1, so non-blocking operations should not be performed here either.This is a normal use case. Calling g_unix_set_fd_nonblocking at this point will cause the test to interrupt. When vhost_user_write sets the call fd to -1, it sets the number of fds to 0, so the fds obtained by qemu_chr_fe_get_msgfds will also be 0. Signed-off-by: Yuxue Liu Message-Id: <20240411073555.1357-1-yuxue.liu@jaguarmicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/vhost-user-test.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index d4e437265f..255bde54ab 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -458,7 +458,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_VRING_KICK: case VHOST_USER_SET_VRING_CALL: /* consume the fd */ - qemu_chr_fe_get_msgfds(chr, &fd, 1); + if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) { + qos_printf("call fd: %d, do not set non-blocking\n", fd); + break; + } /* * This is a non-blocking eventfd. * The receive function forces it to be blocking, From e05ee2994a9c188fc49a9ddf70b79ed7f1808e2f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 6 Jun 2024 22:08:58 +0800 Subject: [PATCH 1794/2884] i386/apic: Add hint on boot failure because of disabling x2APIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the Q35 supports up to 4096 vCPUs (since v9.0), but for TCG cases, if x2APIC is not actively enabled to boot more than 255 vCPUs ( e.g., qemu-system-i386 -M pc-q35-9.0 -smp 666), the following error is reported: Unexpected error in apic_common_set_id() at ../hw/intc/apic_common.c:449: qemu-system-i386: APIC ID 255 requires x2APIC feature in CPU Aborted (core dumped) This error can be resolved by setting x2apic=on in -cpu. In order to better help users deal with this scenario, add the error hint to instruct users on how to enable the x2apic feature. Then, the error report becomes the following: Unexpected error in apic_common_set_id() at ../hw/intc/apic_common.c:448: qemu-system-i386: APIC ID 255 requires x2APIC feature in CPU Try x2apic=on in -cpu. Aborted (core dumped) Note since @errp is &error_abort, error_append_hint() can't be applied on @errp. And in order to separate the exact error message from the (perhaps effectively) hint, adding a hint via error_append_hint() is also necessary. Therefore, introduce @local_error in apic_common_set_id() to handle both the error message and the error hint. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: Zhao Liu Message-Id: <20240606140858.2157106-1-zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/intc/apic_common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index d8fc1e2815..c13cdd7994 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -433,6 +433,7 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, APICCommonState *s = APIC_COMMON(obj); DeviceState *dev = DEVICE(obj); uint32_t value; + Error *local_err = NULL; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); @@ -444,7 +445,11 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, } if (value >= 255 && !cpu_has_x2apic_feature(&s->cpu->env)) { - error_setg(errp, "APIC ID %d requires x2APIC feature in CPU", value); + error_setg(&local_err, + "APIC ID %d requires x2APIC feature in CPU", + value); + error_append_hint(&local_err, "Try x2apic=on in -cpu.\n"); + error_propagate(errp, local_err); return; } From 25b8a0f40c7f408442c5fd4da195fce9997cfb78 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 22:37:50 +0900 Subject: [PATCH 1795/2884] hw/virtio: Free vqs after vhost_dev_cleanup() This fixes LeakSanitizer warnings. Signed-off-by: Akihiko Odaki Message-Id: <20240627-san-v2-7-750bb0946dbd@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user-base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c index 11e72b1e3b..2bc3423326 100644 --- a/hw/virtio/vhost-user-base.c +++ b/hw/virtio/vhost-user-base.c @@ -223,6 +223,7 @@ static void vub_disconnect(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBase *vub = VHOST_USER_BASE(vdev); + struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs; if (!vub->connected) { goto done; @@ -231,6 +232,7 @@ static void vub_disconnect(DeviceState *dev) vub_stop(vdev); vhost_dev_cleanup(&vub->vhost_dev); + g_free(vhost_vqs); done: /* Re-instate the event handler for new connections */ From 704391f94a5494f10b886ba79c157363a79b1239 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 13 Jun 2024 08:49:12 +0300 Subject: [PATCH 1796/2884] virtio-iommu: add error check before assert A fuzzer case discovered by Zheyu Ma causes an assert failure. Add a check before the assert, and respond with an error before moving on to the next queue element. To reproduce the failure: cat << EOF | \ qemu-system-x86_64 \ -display none -machine accel=qtest -m 512M -machine q35 -nodefaults \ -device virtio-iommu -qtest stdio outl 0xcf8 0x80000804 outw 0xcfc 0x06 outl 0xcf8 0x80000820 outl 0xcfc 0xe0004000 write 0x10000e 0x1 0x01 write 0xe0004020 0x4 0x00001000 write 0xe0004028 0x4 0x00101000 write 0xe000401c 0x1 0x01 write 0x106000 0x1 0x05 write 0x100001 0x1 0x60 write 0x100002 0x1 0x10 write 0x100009 0x1 0x04 write 0x10000c 0x1 0x01 write 0x100018 0x1 0x04 write 0x10001c 0x1 0x02 write 0x101003 0x1 0x01 write 0xe0007001 0x1 0x00 EOF Reported-by: Zheyu Ma Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2359 Signed-off-by: Manos Pitsidianakis Message-Id: <20240613-fuzz-2359-fix-v2-manos.pitsidianakis@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index b9a7ddcd14..ed7426afc7 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -974,6 +974,9 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) iov = elem->out_sg; sz = iov_to_buf(iov, iov_cnt, 0, &head, sizeof(head)); if (unlikely(sz != sizeof(head))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read %zu bytes from command head" + "but expected %zu\n", __func__, sz, sizeof(head)); tail.status = VIRTIO_IOMMU_S_DEVERR; goto out; } @@ -1010,6 +1013,25 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) out: sz = iov_from_buf(elem->in_sg, elem->in_num, 0, buf ? buf : &tail, output_size); + if (unlikely(sz != output_size)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: wrote %zu bytes to command response" + "but response size is %zu\n", + __func__, sz, output_size); + tail.status = VIRTIO_IOMMU_S_DEVERR; + /* + * We checked that sizeof(tail) can fit to elem->in_sg at the + * beginning of the loop + */ + output_size = sizeof(tail); + g_free(buf); + buf = NULL; + sz = iov_from_buf(elem->in_sg, + elem->in_num, + 0, + &tail, + output_size); + } assert(sz == output_size); virtqueue_push(vq, elem, sz); From 7c211eb078c42146ee9a441cc028fbc4c378ef5a Mon Sep 17 00:00:00 2001 From: BillXiang Date: Thu, 13 Jun 2024 14:51:50 +0800 Subject: [PATCH 1797/2884] vhost-user: Skip unnecessary duplicated VHOST_USER_SET_LOG_BASE requests The VHOST_USER_SET_LOG_BASE requests should be categorized into non-vring specific messages, and should be sent only once. If send more than once, dpdk will munmap old log_addr which may has been used and cause segmentation fault. Signed-off-by: BillXiang Message-Id: <20240613065150.3100-1-xiangwencheng@dayudpu.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index c407ea8939..00561daa06 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -371,6 +371,7 @@ static bool vhost_user_per_device_request(VhostUserRequest request) case VHOST_USER_RESET_DEVICE: case VHOST_USER_ADD_MEM_REG: case VHOST_USER_REM_MEM_REG: + case VHOST_USER_SET_LOG_BASE: return true; default: return false; From d4f471eb7e562c2cc398448a1c1e7ee838ec30bd Mon Sep 17 00:00:00 2001 From: Dmitry Frolov Date: Thu, 13 Jun 2024 17:35:30 +0300 Subject: [PATCH 1798/2884] hw/net/virtio-net.c: fix crash in iov_copy() A crash found while fuzzing device virtio-net-socket-check-used. Assertion "offset == 0" in iov_copy() fails if less than guest_hdr_len bytes were transmited. Signed-off-by: Dmitry Frolov Message-Id: <20240613143529.602591-2-frolov@swemel.ru> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 9c7e85caea..8f30972708 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2735,6 +2735,10 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) */ assert(n->host_hdr_len <= n->guest_hdr_len); if (n->host_hdr_len != n->guest_hdr_len) { + if (iov_size(out_sg, out_num) < n->guest_hdr_len) { + virtio_error(vdev, "virtio-net header is invalid"); + goto detach; + } unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), out_sg, out_num, 0, n->host_hdr_len); From 67f67bd85476b92093167e1831bf2b5bc597082c Mon Sep 17 00:00:00 2001 From: Oleg Sviridov Date: Fri, 31 May 2024 10:36:27 +0300 Subject: [PATCH 1799/2884] hw/net/spapr: prevent potential NULL dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pointer, returned from function 'spapr_vio_find_by_reg', may be NULL and is dereferenced immediately after. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Oleg Sviridov Message-ID: <20240531073636.3779559-1-oleg.sviridov@red-soft.ru> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/spapr_llan.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index ecb30b7c76..8af33d91b6 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -770,6 +770,12 @@ static target_ulong h_change_logical_lan_mac(PowerPCCPU *cpu, SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); int i; + if (!dev) { + hcall_dprintf("H_CHANGE_LOGICAL_LAN_MAC called when " + "no NIC is present\n"); + return H_PARAMETER; + } + for (i = 0; i < ETH_ALEN; i++) { dev->nicconf.macaddr.a[ETH_ALEN - i - 1] = macaddr & 0xff; macaddr >>= 8; From a1c314861d3b95e10ef0fc6a82d991e68105bce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:29 +0100 Subject: [PATCH 1800/2884] include/hw: add helpers for defining versioned machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The various targets which define versioned machine types have a bunch of obfuscated macro code for defining unique function and variable names using string concatenation. This adds a couple of helpers to improve the clarity of such code macro. Reviewed-by: Thomas Huth Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrangé Message-ID: <20240620165742.1711389-2-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/boards.h | 185 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/include/hw/boards.h b/include/hw/boards.h index 73ad319d7d..d5ad712585 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -413,6 +413,191 @@ struct MachineState { struct NumaState *numa_state; }; +/* + * The macros which follow are intended to facilitate the + * definition of versioned machine types, using a somewhat + * similar pattern across targets. + * + * For example, a macro that can be used to define versioned + * 'virt' machine types would look like: + * + * #define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ + * static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ + * ObjectClass *oc, \ + * void *data) \ + * { \ + * MachineClass *mc = MACHINE_CLASS(oc); \ + * MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ + * mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " Virtual Machine"; \ + * if (latest) { \ + * mc->alias = "virt"; \ + * } \ + * } \ + * static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = { \ + * .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ + * .parent = TYPE_VIRT_MACHINE, \ + * .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ + * }; \ + * static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ + * { \ + * type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ + * } \ + * type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); + * + * Following this, one (or more) helpers can be added for + * whichever scenarios need to be catered for with a machine: + * + * // Normal 2 digit, marked as latest e.g. 'virt-9.0' + * #define DEFINE_VIRT_MACHINE_LATEST(major, minor) \ + * DEFINE_VIRT_MACHINE_IMPL(true, major, minor) + * + * // Normal 2 digit e.g. 'virt-9.0' + * #define DEFINE_VIRT_MACHINE(major, minor) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor) + * + * // Bugfix 3 digit e.g. 'virt-9.0.1' + * #define DEFINE_VIRT_MACHINE_BUGFIX(major, minor, micro) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, micro) + * + * // Tagged 2 digit e.g. 'virt-9.0-extra' + * #define DEFINE_VIRT_MACHINE_TAGGED(major, minor, tag) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, _, tag) + * + * // Tagged bugfix 2 digit e.g. 'virt-9.0.1-extra' + * #define DEFINE_VIRT_MACHINE_TAGGED(major, minor, micro, tag) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, micro, _, tag) + */ + +/* + * Helper for dispatching different macros based on how + * many __VA_ARGS__ are passed. Supports 1 to 5 variadic + * arguments, with the called target able to be prefixed + * with 0 or more fixed arguments too. To be called thus: + * + * _MACHINE_VER_PICK(__VA_ARGS, + * MACRO_MATCHING_5_ARGS, + * MACRO_MATCHING_4_ARGS, + * MACRO_MATCHING_3_ARGS, + * MACRO_MATCHING_2_ARGS, + * MACRO_MATCHING_1_ARG) (FIXED-ARG-1, + * ..., + * FIXED-ARG-N, + * __VA_ARGS__) + */ +#define _MACHINE_VER_PICK(x1, x2, x3, x4, x5, x6, ...) x6 + +/* + * Construct a human targeted machine version string. + * + * Can be invoked with various signatures + * + * MACHINE_VER_STR(sym, prefix, major, minor) + * MACHINE_VER_STR(sym, prefix, major, minor, micro) + * MACHINE_VER_STR(sym, prefix, major, minor, _, tag) + * MACHINE_VER_STR(sym, prefix, major, minor, micro, _, tag) + * + * Respectively emitting symbols with the format + * + * "{major}.{minor}" + * "{major}.{minor}-{tag}" + * "{major}.{minor}.{micro}" + * "{major}.{minor}.{micro}-{tag}" + */ +#define _MACHINE_VER_STR2(major, minor) \ + #major "." #minor + +#define _MACHINE_VER_STR3(major, minor, micro) \ + #major "." #minor "." #micro + +#define _MACHINE_VER_STR4(major, minor, _unused_, tag) \ + #major "." #minor "-" #tag + +#define _MACHINE_VER_STR5(major, minor, micro, _unused_, tag) \ + #major "." #minor "." #micro "-" #tag + +#define MACHINE_VER_STR(...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_STR5, \ + _MACHINE_VER_STR4, \ + _MACHINE_VER_STR3, \ + _MACHINE_VER_STR2) (__VA_ARGS__) + + +/* + * Construct a QAPI type name for a versioned machine + * type + * + * Can be invoked with various signatures + * + * MACHINE_VER_TYPE_NAME(prefix, major, minor) + * MACHINE_VER_TYPE_NAME(prefix, major, minor, micro) + * MACHINE_VER_TYPE_NAME(prefix, major, minor, _, tag) + * MACHINE_VER_TYPE_NAME(prefix, major, minor, micro, _, tag) + * + * Respectively emitting symbols with the format + * + * "{prefix}-{major}.{minor}" + * "{prefix}-{major}.{minor}.{micro}" + * "{prefix}-{major}.{minor}-{tag}" + * "{prefix}-{major}.{minor}.{micro}-{tag}" + */ +#define _MACHINE_VER_TYPE_NAME2(prefix, major, minor) \ + prefix "-" #major "." #minor TYPE_MACHINE_SUFFIX + +#define _MACHINE_VER_TYPE_NAME3(prefix, major, minor, micro) \ + prefix "-" #major "." #minor "." #micro TYPE_MACHINE_SUFFIX + +#define _MACHINE_VER_TYPE_NAME4(prefix, major, minor, _unused_, tag) \ + prefix "-" #major "." #minor "-" #tag TYPE_MACHINE_SUFFIX + +#define _MACHINE_VER_TYPE_NAME5(prefix, major, minor, micro, _unused_, tag) \ + prefix "-" #major "." #minor "." #micro "-" #tag TYPE_MACHINE_SUFFIX + +#define MACHINE_VER_TYPE_NAME(prefix, ...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_TYPE_NAME5, \ + _MACHINE_VER_TYPE_NAME4, \ + _MACHINE_VER_TYPE_NAME3, \ + _MACHINE_VER_TYPE_NAME2) (prefix, __VA_ARGS__) + +/* + * Construct a name for a versioned machine type that is + * suitable for use as a C symbol (function/variable/etc). + * + * Can be invoked with various signatures + * + * MACHINE_VER_SYM(sym, prefix, major, minor) + * MACHINE_VER_SYM(sym, prefix, major, minor, micro) + * MACHINE_VER_SYM(sym, prefix, major, minor, _, tag) + * MACHINE_VER_SYM(sym, prefix, major, minor, micro, _, tag) + * + * Respectively emitting symbols with the format + * + * {prefix}_machine_{major}_{minor}_{sym} + * {prefix}_machine_{major}_{minor}_{micro}_{sym} + * {prefix}_machine_{major}_{minor}_{tag}_{sym} + * {prefix}_machine_{major}_{minor}_{micro}_{tag}_{sym} + */ +#define _MACHINE_VER_SYM2(sym, prefix, major, minor) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## sym + +#define _MACHINE_VER_SYM3(sym, prefix, major, minor, micro) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## micro ## _ ## sym + +#define _MACHINE_VER_SYM4(sym, prefix, major, minor, _unused_, tag) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## tag ## _ ## sym + +#define _MACHINE_VER_SYM5(sym, prefix, major, minor, micro, _unused_, tag) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## micro ## _ ## tag ## _ ## sym + +#define MACHINE_VER_SYM(sym, prefix, ...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_SYM5, \ + _MACHINE_VER_SYM4, \ + _MACHINE_VER_SYM3, \ + _MACHINE_VER_SYM2) (sym, prefix, __VA_ARGS__) + + #define DEFINE_MACHINE(namestr, machine_initfn) \ static void machine_initfn##_class_init(ObjectClass *oc, void *data) \ { \ From d1baf8753be6c8b9d5f3ef802e69133c91ce0c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:30 +0100 Subject: [PATCH 1801/2884] hw/arm: convert 'virt' machine definitions to use new macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the DEFINE_VIRT_MACHINE macro to use the common helpers for constructing versioned symbol names and strings, bringing greater consistency across targets. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-3-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/virt.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0784ee7f46..08990b9abe 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -101,33 +101,35 @@ static void arm_virt_compat_set(MachineClass *mc) arm_virt_compat_len); } -#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ - static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ arm_virt_compat_set(mc); \ - virt_machine_##major##_##minor##_options(mc); \ - mc->desc = "QEMU " # major "." # minor " ARM Virtual Machine"; \ + MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ + mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " ARM Virtual Machine"; \ if (latest) { \ mc->alias = "virt"; \ } \ } \ - static const TypeInfo machvirt_##major##_##minor##_info = { \ - .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ - .parent = TYPE_VIRT_MACHINE, \ - .class_init = virt_##major##_##minor##_class_init, \ - }; \ - static void machvirt_machine_##major##_##minor##_init(void) \ + static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ { \ - type_register_static(&machvirt_##major##_##minor##_info); \ + .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ + .parent = TYPE_VIRT_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ + { \ + type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ } \ - type_init(machvirt_machine_##major##_##minor##_init); + type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); #define DEFINE_VIRT_MACHINE_AS_LATEST(major, minor) \ - DEFINE_VIRT_MACHINE_LATEST(major, minor, true) + DEFINE_VIRT_MACHINE_IMPL(true, major, minor) #define DEFINE_VIRT_MACHINE(major, minor) \ - DEFINE_VIRT_MACHINE_LATEST(major, minor, false) + DEFINE_VIRT_MACHINE_IMPL(false, major, minor) /* Number of external interrupt lines to configure the GIC with */ From 7fb1e06a534208910722e29260f792719c2231cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:31 +0100 Subject: [PATCH 1802/2884] hw/s390x: convert 'ccw' machine definitions to use new macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the DEFINE_CCW_MACHINE macro to use the common helpers for constructing versioned symbol names and strings, bringing greater consistency across targets. The added benefit is that it avoids the need to repeat the version number twice in two different formats in the calls to DEFINE_CCW_MACHINE. A DEFINE_CCW_MACHINE_AS_LATEST helper is added so that it is not required to pass 'false' for every single historical machine type. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-4-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/s390x/s390-virtio-ccw.c | 100 ++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index cd063f8b64..380e9e2e5b 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -15,6 +15,7 @@ #include "qapi/error.h" #include "exec/ram_addr.h" #include "exec/confidential-guest-support.h" +#include "hw/boards.h" #include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" @@ -817,35 +818,44 @@ static const TypeInfo ccw_machine_info = { }, }; -#define DEFINE_CCW_MACHINE(suffix, verstr, latest) \ - static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_CCW_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - ccw_machine_##suffix##_class_options(mc); \ - mc->desc = "Virtual s390x machine (version " verstr ")"; \ + MACHINE_VER_SYM(class_options, ccw, __VA_ARGS__)(mc); \ + mc->desc = "Virtual s390x machine (version " MACHINE_VER_STR(__VA_ARGS__) ")"; \ if (latest) { \ mc->alias = "s390-ccw-virtio"; \ mc->is_default = true; \ } \ } \ - static void ccw_machine_##suffix##_instance_init(Object *obj) \ + static void MACHINE_VER_SYM(instance_init, ccw, __VA_ARGS__)(Object *obj) \ { \ MachineState *machine = MACHINE(obj); \ - current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ - ccw_machine_##suffix##_instance_options(machine); \ + current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ + MACHINE_VER_SYM(instance_options, ccw, __VA_ARGS__)(machine); \ } \ - static const TypeInfo ccw_machine_##suffix##_info = { \ - .name = MACHINE_TYPE_NAME("s390-ccw-virtio-" verstr), \ - .parent = TYPE_S390_CCW_MACHINE, \ - .class_init = ccw_machine_##suffix##_class_init, \ - .instance_init = ccw_machine_##suffix##_instance_init, \ - }; \ - static void ccw_machine_register_##suffix(void) \ + static const TypeInfo MACHINE_VER_SYM(info, ccw, __VA_ARGS__) = \ { \ - type_register_static(&ccw_machine_##suffix##_info); \ + .name = MACHINE_VER_TYPE_NAME("s390-ccw-virtio", __VA_ARGS__), \ + .parent = TYPE_S390_CCW_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__), \ + .instance_init = MACHINE_VER_SYM(instance_init, ccw, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, ccw, __VA_ARGS__)(void) \ + { \ + type_register_static(&MACHINE_VER_SYM(info, ccw, __VA_ARGS__)); \ } \ - type_init(ccw_machine_register_##suffix) + type_init(MACHINE_VER_SYM(register, ccw, __VA_ARGS__)) + +#define DEFINE_CCW_MACHINE_AS_LATEST(major, minor) \ + DEFINE_CCW_MACHINE_IMPL(true, major, minor) + +#define DEFINE_CCW_MACHINE(major, minor) \ + DEFINE_CCW_MACHINE_IMPL(false, major, minor) + static void ccw_machine_9_1_instance_options(MachineState *machine) { @@ -854,7 +864,7 @@ static void ccw_machine_9_1_instance_options(MachineState *machine) static void ccw_machine_9_1_class_options(MachineClass *mc) { } -DEFINE_CCW_MACHINE(9_1, "9.1", true); +DEFINE_CCW_MACHINE_AS_LATEST(9, 1); static void ccw_machine_9_0_instance_options(MachineState *machine) { @@ -866,7 +876,7 @@ static void ccw_machine_9_0_class_options(MachineClass *mc) ccw_machine_9_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); } -DEFINE_CCW_MACHINE(9_0, "9.0", false); +DEFINE_CCW_MACHINE(9, 0); static void ccw_machine_8_2_instance_options(MachineState *machine) { @@ -878,7 +888,7 @@ static void ccw_machine_8_2_class_options(MachineClass *mc) ccw_machine_9_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); } -DEFINE_CCW_MACHINE(8_2, "8.2", false); +DEFINE_CCW_MACHINE(8, 2); static void ccw_machine_8_1_instance_options(MachineState *machine) { @@ -892,7 +902,7 @@ static void ccw_machine_8_1_class_options(MachineClass *mc) mc->smp_props.drawers_supported = false; mc->smp_props.books_supported = false; } -DEFINE_CCW_MACHINE(8_1, "8.1", false); +DEFINE_CCW_MACHINE(8, 1); static void ccw_machine_8_0_instance_options(MachineState *machine) { @@ -904,7 +914,7 @@ static void ccw_machine_8_0_class_options(MachineClass *mc) ccw_machine_8_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); } -DEFINE_CCW_MACHINE(8_0, "8.0", false); +DEFINE_CCW_MACHINE(8, 0); static void ccw_machine_7_2_instance_options(MachineState *machine) { @@ -916,7 +926,7 @@ static void ccw_machine_7_2_class_options(MachineClass *mc) ccw_machine_8_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); } -DEFINE_CCW_MACHINE(7_2, "7.2", false); +DEFINE_CCW_MACHINE(7, 2); static void ccw_machine_7_1_instance_options(MachineState *machine) { @@ -940,7 +950,7 @@ static void ccw_machine_7_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); s390mc->max_threads = S390_MAX_CPUS; } -DEFINE_CCW_MACHINE(7_1, "7.1", false); +DEFINE_CCW_MACHINE(7, 1); static void ccw_machine_7_0_instance_options(MachineState *machine) { @@ -955,7 +965,7 @@ static void ccw_machine_7_0_class_options(MachineClass *mc) ccw_machine_7_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_CCW_MACHINE(7_0, "7.0", false); +DEFINE_CCW_MACHINE(7, 0); static void ccw_machine_6_2_instance_options(MachineState *machine) { @@ -970,7 +980,7 @@ static void ccw_machine_6_2_class_options(MachineClass *mc) ccw_machine_7_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); } -DEFINE_CCW_MACHINE(6_2, "6.2", false); +DEFINE_CCW_MACHINE(6, 2); static void ccw_machine_6_1_instance_options(MachineState *machine) { @@ -988,7 +998,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); mc->smp_props.prefer_sockets = true; } -DEFINE_CCW_MACHINE(6_1, "6.1", false); +DEFINE_CCW_MACHINE(6, 1); static void ccw_machine_6_0_instance_options(MachineState *machine) { @@ -1003,7 +1013,7 @@ static void ccw_machine_6_0_class_options(MachineClass *mc) ccw_machine_6_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); } -DEFINE_CCW_MACHINE(6_0, "6.0", false); +DEFINE_CCW_MACHINE(6, 0); static void ccw_machine_5_2_instance_options(MachineState *machine) { @@ -1015,7 +1025,7 @@ static void ccw_machine_5_2_class_options(MachineClass *mc) ccw_machine_6_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_CCW_MACHINE(5_2, "5.2", false); +DEFINE_CCW_MACHINE(5, 2); static void ccw_machine_5_1_instance_options(MachineState *machine) { @@ -1027,7 +1037,7 @@ static void ccw_machine_5_1_class_options(MachineClass *mc) ccw_machine_5_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_1, hw_compat_5_1_len); } -DEFINE_CCW_MACHINE(5_1, "5.1", false); +DEFINE_CCW_MACHINE(5, 1); static void ccw_machine_5_0_instance_options(MachineState *machine) { @@ -1039,7 +1049,7 @@ static void ccw_machine_5_0_class_options(MachineClass *mc) ccw_machine_5_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_0, hw_compat_5_0_len); } -DEFINE_CCW_MACHINE(5_0, "5.0", false); +DEFINE_CCW_MACHINE(5, 0); static void ccw_machine_4_2_instance_options(MachineState *machine) { @@ -1052,7 +1062,7 @@ static void ccw_machine_4_2_class_options(MachineClass *mc) mc->fixup_ram_size = s390_fixup_ram_size; compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); } -DEFINE_CCW_MACHINE(4_2, "4.2", false); +DEFINE_CCW_MACHINE(4, 2); static void ccw_machine_4_1_instance_options(MachineState *machine) { @@ -1066,7 +1076,7 @@ static void ccw_machine_4_1_class_options(MachineClass *mc) ccw_machine_4_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len); } -DEFINE_CCW_MACHINE(4_1, "4.1", false); +DEFINE_CCW_MACHINE(4, 1); static void ccw_machine_4_0_instance_options(MachineState *machine) { @@ -1080,7 +1090,7 @@ static void ccw_machine_4_0_class_options(MachineClass *mc) ccw_machine_4_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len); } -DEFINE_CCW_MACHINE(4_0, "4.0", false); +DEFINE_CCW_MACHINE(4, 0); static void ccw_machine_3_1_instance_options(MachineState *machine) { @@ -1096,7 +1106,7 @@ static void ccw_machine_3_1_class_options(MachineClass *mc) ccw_machine_4_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); } -DEFINE_CCW_MACHINE(3_1, "3.1", false); +DEFINE_CCW_MACHINE(3, 1); static void ccw_machine_3_0_instance_options(MachineState *machine) { @@ -1111,7 +1121,7 @@ static void ccw_machine_3_0_class_options(MachineClass *mc) ccw_machine_3_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); } -DEFINE_CCW_MACHINE(3_0, "3.0", false); +DEFINE_CCW_MACHINE(3, 0); static void ccw_machine_2_12_instance_options(MachineState *machine) { @@ -1125,7 +1135,7 @@ static void ccw_machine_2_12_class_options(MachineClass *mc) ccw_machine_3_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); } -DEFINE_CCW_MACHINE(2_12, "2.12", false); +DEFINE_CCW_MACHINE(2, 12); #ifdef CONFIG_S390X_LEGACY_CPUS @@ -1148,7 +1158,7 @@ static void ccw_machine_2_11_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_11, "2.11", false); +DEFINE_CCW_MACHINE(2, 11); static void ccw_machine_2_10_instance_options(MachineState *machine) { @@ -1160,7 +1170,7 @@ static void ccw_machine_2_10_class_options(MachineClass *mc) ccw_machine_2_11_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); } -DEFINE_CCW_MACHINE(2_10, "2.10", false); +DEFINE_CCW_MACHINE(2, 10); static void ccw_machine_2_9_instance_options(MachineState *machine) { @@ -1184,7 +1194,7 @@ static void ccw_machine_2_9_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); css_migration_enabled = false; } -DEFINE_CCW_MACHINE(2_9, "2.9", false); +DEFINE_CCW_MACHINE(2, 9); static void ccw_machine_2_8_instance_options(MachineState *machine) { @@ -1201,7 +1211,7 @@ static void ccw_machine_2_8_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_8, "2.8", false); +DEFINE_CCW_MACHINE(2, 8); static void ccw_machine_2_7_instance_options(MachineState *machine) { @@ -1216,7 +1226,7 @@ static void ccw_machine_2_7_class_options(MachineClass *mc) ccw_machine_2_8_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); } -DEFINE_CCW_MACHINE(2_7, "2.7", false); +DEFINE_CCW_MACHINE(2, 7); static void ccw_machine_2_6_instance_options(MachineState *machine) { @@ -1236,7 +1246,7 @@ static void ccw_machine_2_6_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_6, "2.6", false); +DEFINE_CCW_MACHINE(2, 6); static void ccw_machine_2_5_instance_options(MachineState *machine) { @@ -1248,7 +1258,7 @@ static void ccw_machine_2_5_class_options(MachineClass *mc) ccw_machine_2_6_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_5, hw_compat_2_5_len); } -DEFINE_CCW_MACHINE(2_5, "2.5", false); +DEFINE_CCW_MACHINE(2, 5); static void ccw_machine_2_4_instance_options(MachineState *machine) { @@ -1273,7 +1283,7 @@ static void ccw_machine_2_4_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_4, "2.4", false); +DEFINE_CCW_MACHINE(2, 4); #endif From 8d40cc1483f94a9584f77452879f8815beb4ef14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:32 +0100 Subject: [PATCH 1803/2884] hw/ppc: convert 'spapr' machine definitions to use new macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the DEFINE_SPAPR_MACHINE macro to use the common helpers for constructing versioned symbol names and strings, bringing greater consistency across targets. The added benefit is that it avoids the need to repeat the version number twice in two different formats in the calls to DEFINE_SPAPR_MACHINE. A DEFINE_SPAPR_MACHINE_AS_LATEST helper is added so that it is not required to pass 'false' for every single historical machine type. Due to the odd-ball '2.12-sxxm' machine type version, this commit introduces a DEFINE_SPAPR_MACHINE_TAGGED helper to allow defining of "tagged" machine types which have a string suffix. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Message-ID: <20240620165742.1711389-5-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/spapr.c | 97 +++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a9908545e6..2785b6b303 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4804,26 +4804,35 @@ static void spapr_machine_latest_class_options(MachineClass *mc) mc->is_default = true; } -#define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \ - static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_SPAPR_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - spapr_machine_##suffix##_class_options(mc); \ + MACHINE_VER_SYM(class_options, spapr, __VA_ARGS__)(mc); \ if (latest) { \ spapr_machine_latest_class_options(mc); \ } \ } \ - static const TypeInfo spapr_machine_##suffix##_info = { \ - .name = MACHINE_TYPE_NAME("pseries-" verstr), \ - .parent = TYPE_SPAPR_MACHINE, \ - .class_init = spapr_machine_##suffix##_class_init, \ - }; \ - static void spapr_machine_register_##suffix(void) \ + static const TypeInfo MACHINE_VER_SYM(info, spapr, __VA_ARGS__) = \ { \ - type_register(&spapr_machine_##suffix##_info); \ + .name = MACHINE_VER_TYPE_NAME("pseries", __VA_ARGS__), \ + .parent = TYPE_SPAPR_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \ + { \ + type_register(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \ } \ - type_init(spapr_machine_register_##suffix) + type_init(MACHINE_VER_SYM(register, spapr, __VA_ARGS__)) + +#define DEFINE_SPAPR_MACHINE_AS_LATEST(major, minor) \ + DEFINE_SPAPR_MACHINE_IMPL(true, major, minor) +#define DEFINE_SPAPR_MACHINE(major, minor) \ + DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) +#define DEFINE_SPAPR_MACHINE_TAGGED(major, minor, tag) \ + DEFINE_SPAPR_MACHINE_IMPL(false, major, minor, _, tag) /* * pseries-9.1 @@ -4833,7 +4842,7 @@ static void spapr_machine_9_1_class_options(MachineClass *mc) /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(9_1, "9.1", true); +DEFINE_SPAPR_MACHINE_AS_LATEST(9, 1); /* * pseries-9.0 @@ -4844,7 +4853,7 @@ static void spapr_machine_9_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); } -DEFINE_SPAPR_MACHINE(9_0, "9.0", false); +DEFINE_SPAPR_MACHINE(9, 0); /* * pseries-8.2 @@ -4855,7 +4864,7 @@ static void spapr_machine_8_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); } -DEFINE_SPAPR_MACHINE(8_2, "8.2", false); +DEFINE_SPAPR_MACHINE(8, 2); /* * pseries-8.1 @@ -4866,7 +4875,7 @@ static void spapr_machine_8_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); } -DEFINE_SPAPR_MACHINE(8_1, "8.1", false); +DEFINE_SPAPR_MACHINE(8, 1); /* * pseries-8.0 @@ -4877,7 +4886,7 @@ static void spapr_machine_8_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); } -DEFINE_SPAPR_MACHINE(8_0, "8.0", false); +DEFINE_SPAPR_MACHINE(8, 0); /* * pseries-7.2 @@ -4888,7 +4897,7 @@ static void spapr_machine_7_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); } -DEFINE_SPAPR_MACHINE(7_2, "7.2", false); +DEFINE_SPAPR_MACHINE(7, 2); /* * pseries-7.1 @@ -4899,7 +4908,7 @@ static void spapr_machine_7_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); } -DEFINE_SPAPR_MACHINE(7_1, "7.1", false); +DEFINE_SPAPR_MACHINE(7, 1); /* * pseries-7.0 @@ -4910,7 +4919,7 @@ static void spapr_machine_7_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_SPAPR_MACHINE(7_0, "7.0", false); +DEFINE_SPAPR_MACHINE(7, 0); /* * pseries-6.2 @@ -4921,7 +4930,7 @@ static void spapr_machine_6_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); } -DEFINE_SPAPR_MACHINE(6_2, "6.2", false); +DEFINE_SPAPR_MACHINE(6, 2); /* * pseries-6.1 @@ -4936,7 +4945,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc) mc->smp_props.prefer_sockets = true; } -DEFINE_SPAPR_MACHINE(6_1, "6.1", false); +DEFINE_SPAPR_MACHINE(6, 1); /* * pseries-6.0 @@ -4947,7 +4956,7 @@ static void spapr_machine_6_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); } -DEFINE_SPAPR_MACHINE(6_0, "6.0", false); +DEFINE_SPAPR_MACHINE(6, 0); /* * pseries-5.2 @@ -4958,7 +4967,7 @@ static void spapr_machine_5_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_SPAPR_MACHINE(5_2, "5.2", false); +DEFINE_SPAPR_MACHINE(5, 2); /* * pseries-5.1 @@ -4972,7 +4981,7 @@ static void spapr_machine_5_1_class_options(MachineClass *mc) smc->pre_5_2_numa_associativity = true; } -DEFINE_SPAPR_MACHINE(5_1, "5.1", false); +DEFINE_SPAPR_MACHINE(5, 1); /* * pseries-5.0 @@ -4991,7 +5000,7 @@ static void spapr_machine_5_0_class_options(MachineClass *mc) smc->pre_5_1_assoc_refpoints = true; } -DEFINE_SPAPR_MACHINE(5_0, "5.0", false); +DEFINE_SPAPR_MACHINE(5, 0); /* * pseries-4.2 @@ -5008,7 +5017,7 @@ static void spapr_machine_4_2_class_options(MachineClass *mc) mc->nvdimm_supported = false; } -DEFINE_SPAPR_MACHINE(4_2, "4.2", false); +DEFINE_SPAPR_MACHINE(4, 2); /* * pseries-4.1 @@ -5028,7 +5037,7 @@ static void spapr_machine_4_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_SPAPR_MACHINE(4_1, "4.1", false); +DEFINE_SPAPR_MACHINE(4, 1); /* * pseries-4.0 @@ -5055,7 +5064,7 @@ static void spapr_machine_4_0_class_options(MachineClass *mc) smc->pre_4_1_migration = true; } -DEFINE_SPAPR_MACHINE(4_0, "4.0", false); +DEFINE_SPAPR_MACHINE(4, 0); /* * pseries-3.1 @@ -5077,7 +5086,7 @@ static void spapr_machine_3_1_class_options(MachineClass *mc) smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; } -DEFINE_SPAPR_MACHINE(3_1, "3.1", false); +DEFINE_SPAPR_MACHINE(3, 1); /* * pseries-3.0 @@ -5095,7 +5104,7 @@ static void spapr_machine_3_0_class_options(MachineClass *mc) smc->irq = &spapr_irq_xics_legacy; } -DEFINE_SPAPR_MACHINE(3_0, "3.0", false); +DEFINE_SPAPR_MACHINE(3, 0); /* * pseries-2.12 @@ -5120,7 +5129,7 @@ static void spapr_machine_2_12_class_options(MachineClass *mc) smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 0; } -DEFINE_SPAPR_MACHINE(2_12, "2.12", false); +DEFINE_SPAPR_MACHINE(2, 12); static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc) { @@ -5132,7 +5141,7 @@ static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc) smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; } -DEFINE_SPAPR_MACHINE(2_12_sxxm, "2.12-sxxm", false); +DEFINE_SPAPR_MACHINE_TAGGED(2, 12, sxxm); /* * pseries-2.11 @@ -5148,7 +5157,7 @@ static void spapr_machine_2_11_class_options(MachineClass *mc) mc->deprecation_reason = "old and not maintained - use a 2.12+ version"; } -DEFINE_SPAPR_MACHINE(2_11, "2.11", false); +DEFINE_SPAPR_MACHINE(2, 11); /* * pseries-2.10 @@ -5160,7 +5169,7 @@ static void spapr_machine_2_10_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); } -DEFINE_SPAPR_MACHINE(2_10, "2.10", false); +DEFINE_SPAPR_MACHINE(2, 10); /* * pseries-2.9 @@ -5180,7 +5189,7 @@ static void spapr_machine_2_9_class_options(MachineClass *mc) smc->resize_hpt_default = SPAPR_RESIZE_HPT_DISABLED; } -DEFINE_SPAPR_MACHINE(2_9, "2.9", false); +DEFINE_SPAPR_MACHINE(2, 9); /* * pseries-2.8 @@ -5198,7 +5207,7 @@ static void spapr_machine_2_8_class_options(MachineClass *mc) mc->numa_mem_align_shift = 23; } -DEFINE_SPAPR_MACHINE(2_8, "2.8", false); +DEFINE_SPAPR_MACHINE(2, 8); /* * pseries-2.7 @@ -5273,7 +5282,7 @@ static void spapr_machine_2_7_class_options(MachineClass *mc) smc->phb_placement = phb_placement_2_7; } -DEFINE_SPAPR_MACHINE(2_7, "2.7", false); +DEFINE_SPAPR_MACHINE(2, 7); /* * pseries-2.6 @@ -5291,7 +5300,7 @@ static void spapr_machine_2_6_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_SPAPR_MACHINE(2_6, "2.6", false); +DEFINE_SPAPR_MACHINE(2, 6); /* * pseries-2.5 @@ -5310,7 +5319,7 @@ static void spapr_machine_2_5_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_SPAPR_MACHINE(2_5, "2.5", false); +DEFINE_SPAPR_MACHINE(2, 5); /* * pseries-2.4 @@ -5325,7 +5334,7 @@ static void spapr_machine_2_4_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); } -DEFINE_SPAPR_MACHINE(2_4, "2.4", false); +DEFINE_SPAPR_MACHINE(2, 4); /* * pseries-2.3 @@ -5340,7 +5349,7 @@ static void spapr_machine_2_3_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_3, hw_compat_2_3_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_SPAPR_MACHINE(2_3, "2.3", false); +DEFINE_SPAPR_MACHINE(2, 3); /* * pseries-2.2 @@ -5357,7 +5366,7 @@ static void spapr_machine_2_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); mc->default_machine_opts = "modern-hotplug-events=off,suppress-vmdesc=on"; } -DEFINE_SPAPR_MACHINE(2_2, "2.2", false); +DEFINE_SPAPR_MACHINE(2, 2); /* * pseries-2.1 @@ -5368,7 +5377,7 @@ static void spapr_machine_2_1_class_options(MachineClass *mc) spapr_machine_2_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_1, hw_compat_2_1_len); } -DEFINE_SPAPR_MACHINE(2_1, "2.1", false); +DEFINE_SPAPR_MACHINE(2, 1); static void spapr_machine_register_types(void) { From 1d32d1d136c8bbc5a3e1760220a8585eeaea0287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:33 +0100 Subject: [PATCH 1804/2884] hw/m68k: convert 'virt' machine definitions to use new macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the DEFINE_VIRT_MACHINE macro to use the common helpers for constructing versioned symbol names and strings, bringing greater consistency across targets. A DEFINE_VIRT_MACHINE_AS_LATEST helper is added so that it is not required to pass 'false' for every single historical machine type. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-6-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/m68k/virt.c | 55 ++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 09bc9bdfef..cd6ee692f7 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -335,99 +335,106 @@ static void virt_machine_register_types(void) type_init(virt_machine_register_types) -#define DEFINE_VIRT_MACHINE(major, minor, latest) \ - static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - virt_machine_##major##_##minor##_options(mc); \ - mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \ + MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ + mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " M68K Virtual Machine"; \ if (latest) { \ mc->alias = "virt"; \ } \ } \ - static const TypeInfo machvirt_##major##_##minor##_info = { \ - .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ - .parent = MACHINE_TYPE_NAME("virt"), \ - .class_init = virt_##major##_##minor##_class_init, \ - }; \ - static void machvirt_machine_##major##_##minor##_init(void) \ + static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ { \ - type_register_static(&machvirt_##major##_##minor##_info); \ + .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ + .parent = MACHINE_TYPE_NAME("virt"), \ + .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ + { \ + type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ } \ - type_init(machvirt_machine_##major##_##minor##_init); + type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); + +#define DEFINE_VIRT_MACHINE_AS_LATEST(major, minor) \ + DEFINE_VIRT_MACHINE_IMPL(true, major, minor) +#define DEFINE_VIRT_MACHINE(major, minor) \ + DEFINE_VIRT_MACHINE_IMPL(false, major, minor) static void virt_machine_9_1_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE(9, 1, true) +DEFINE_VIRT_MACHINE_AS_LATEST(9, 1) static void virt_machine_9_0_options(MachineClass *mc) { virt_machine_9_1_options(mc); compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); } -DEFINE_VIRT_MACHINE(9, 0, false) +DEFINE_VIRT_MACHINE(9, 0) static void virt_machine_8_2_options(MachineClass *mc) { virt_machine_9_0_options(mc); compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); } -DEFINE_VIRT_MACHINE(8, 2, false) +DEFINE_VIRT_MACHINE(8, 2) static void virt_machine_8_1_options(MachineClass *mc) { virt_machine_8_2_options(mc); compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); } -DEFINE_VIRT_MACHINE(8, 1, false) +DEFINE_VIRT_MACHINE(8, 1) static void virt_machine_8_0_options(MachineClass *mc) { virt_machine_8_1_options(mc); compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); } -DEFINE_VIRT_MACHINE(8, 0, false) +DEFINE_VIRT_MACHINE(8, 0) static void virt_machine_7_2_options(MachineClass *mc) { virt_machine_8_0_options(mc); compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); } -DEFINE_VIRT_MACHINE(7, 2, false) +DEFINE_VIRT_MACHINE(7, 2) static void virt_machine_7_1_options(MachineClass *mc) { virt_machine_7_2_options(mc); compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); } -DEFINE_VIRT_MACHINE(7, 1, false) +DEFINE_VIRT_MACHINE(7, 1) static void virt_machine_7_0_options(MachineClass *mc) { virt_machine_7_1_options(mc); compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_VIRT_MACHINE(7, 0, false) +DEFINE_VIRT_MACHINE(7, 0) static void virt_machine_6_2_options(MachineClass *mc) { virt_machine_7_0_options(mc); compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); } -DEFINE_VIRT_MACHINE(6, 2, false) +DEFINE_VIRT_MACHINE(6, 2) static void virt_machine_6_1_options(MachineClass *mc) { virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); } -DEFINE_VIRT_MACHINE(6, 1, false) +DEFINE_VIRT_MACHINE(6, 1) static void virt_machine_6_0_options(MachineClass *mc) { virt_machine_6_1_options(mc); compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); } -DEFINE_VIRT_MACHINE(6, 0, false) +DEFINE_VIRT_MACHINE(6, 0) From a0220c65c4037349031ca8ff3f50e312b9bb9286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:34 +0100 Subject: [PATCH 1805/2884] hw/i386: convert 'i440fx' machine definitions to use new macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the DEFINE_I440FX_MACHINE macro to use the common helpers for constructing versioned symbol names and strings, bringing greater consistency across targets. The added benefit is that it avoids the need to repeat the version number thrice in three different formats in the calls to DEFINE_I440FX_MACHINE. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-7-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_piix.c | 219 +++++++++++++++++++------------------------ include/hw/i386/pc.h | 26 +++++ 2 files changed, 122 insertions(+), 123 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e4930b7f48..5705d6e155 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,12 +445,13 @@ static void pc_xen_hvm_init(MachineState *machine) } #endif -#define DEFINE_I440FX_MACHINE(suffix, name, optionfn) \ - static void pc_init_##suffix(MachineState *machine) \ - { \ - pc_init1(machine, TYPE_I440FX_PCI_DEVICE); \ - } \ - DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) +static void pc_i440fx_init(MachineState *machine) +{ + pc_init1(machine, TYPE_I440FX_PCI_DEVICE); +} + +#define DEFINE_I440FX_MACHINE(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_i440fx, "pc-i440fx", pc_i440fx_init, major, minor); static void pc_i440fx_machine_options(MachineClass *m) { @@ -478,21 +479,20 @@ static void pc_i440fx_machine_options(MachineClass *m) "Use a different south bridge than PIIX3"); } -static void pc_i440fx_9_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_9_1_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = true; } -DEFINE_I440FX_MACHINE(v9_1, "pc-i440fx-9.1", - pc_i440fx_9_1_machine_options); +DEFINE_I440FX_MACHINE(9, 1); -static void pc_i440fx_9_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_9_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_9_1_machine_options(m); + pc_i440fx_machine_9_1_options(m); m->alias = NULL; m->is_default = false; @@ -501,14 +501,13 @@ static void pc_i440fx_9_0_machine_options(MachineClass *m) pcmc->isa_bios_alias = false; } -DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", - pc_i440fx_9_0_machine_options); +DEFINE_I440FX_MACHINE(9, 0); -static void pc_i440fx_8_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_8_2_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_9_0_machine_options(m); + pc_i440fx_machine_9_0_options(m); compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); @@ -516,28 +515,26 @@ static void pc_i440fx_8_2_machine_options(MachineClass *m) pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; } -DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", - pc_i440fx_8_2_machine_options); +DEFINE_I440FX_MACHINE(8, 2); -static void pc_i440fx_8_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_8_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_8_2_machine_options(m); + pc_i440fx_machine_8_2_options(m); pcmc->broken_32bit_mem_addr_check = true; compat_props_add(m->compat_props, hw_compat_8_1, hw_compat_8_1_len); compat_props_add(m->compat_props, pc_compat_8_1, pc_compat_8_1_len); } -DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", - pc_i440fx_8_1_machine_options); +DEFINE_I440FX_MACHINE(8, 1); -static void pc_i440fx_8_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_8_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_8_1_machine_options(m); + pc_i440fx_machine_8_1_options(m); compat_props_add(m->compat_props, hw_compat_8_0, hw_compat_8_0_len); compat_props_add(m->compat_props, pc_compat_8_0, pc_compat_8_0_len); @@ -545,268 +542,244 @@ static void pc_i440fx_8_0_machine_options(MachineClass *m) pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; } -DEFINE_I440FX_MACHINE(v8_0, "pc-i440fx-8.0", - pc_i440fx_8_0_machine_options); +DEFINE_I440FX_MACHINE(8, 0); -static void pc_i440fx_7_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_7_2_options(MachineClass *m) { - pc_i440fx_8_0_machine_options(m); + pc_i440fx_machine_8_0_options(m); compat_props_add(m->compat_props, hw_compat_7_2, hw_compat_7_2_len); compat_props_add(m->compat_props, pc_compat_7_2, pc_compat_7_2_len); } -DEFINE_I440FX_MACHINE(v7_2, "pc-i440fx-7.2", - pc_i440fx_7_2_machine_options); +DEFINE_I440FX_MACHINE(7, 2) -static void pc_i440fx_7_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_7_1_options(MachineClass *m) { - pc_i440fx_7_2_machine_options(m); + pc_i440fx_machine_7_2_options(m); compat_props_add(m->compat_props, hw_compat_7_1, hw_compat_7_1_len); compat_props_add(m->compat_props, pc_compat_7_1, pc_compat_7_1_len); } -DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", - pc_i440fx_7_1_machine_options); +DEFINE_I440FX_MACHINE(7, 1); -static void pc_i440fx_7_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_7_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_7_1_machine_options(m); + pc_i440fx_machine_7_1_options(m); pcmc->enforce_amd_1tb_hole = false; compat_props_add(m->compat_props, hw_compat_7_0, hw_compat_7_0_len); compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); } -DEFINE_I440FX_MACHINE(v7_0, "pc-i440fx-7.0", - pc_i440fx_7_0_machine_options); +DEFINE_I440FX_MACHINE(7, 0); -static void pc_i440fx_6_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_6_2_options(MachineClass *m) { - pc_i440fx_7_0_machine_options(m); + pc_i440fx_machine_7_0_options(m); compat_props_add(m->compat_props, hw_compat_6_2, hw_compat_6_2_len); compat_props_add(m->compat_props, pc_compat_6_2, pc_compat_6_2_len); } -DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", - pc_i440fx_6_2_machine_options); +DEFINE_I440FX_MACHINE(6, 2); -static void pc_i440fx_6_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_6_1_options(MachineClass *m) { - pc_i440fx_6_2_machine_options(m); + pc_i440fx_machine_6_2_options(m); compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); m->smp_props.prefer_sockets = true; } -DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", - pc_i440fx_6_1_machine_options); +DEFINE_I440FX_MACHINE(6, 1); -static void pc_i440fx_6_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_6_0_options(MachineClass *m) { - pc_i440fx_6_1_machine_options(m); + pc_i440fx_machine_6_1_options(m); compat_props_add(m->compat_props, hw_compat_6_0, hw_compat_6_0_len); compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len); } -DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", - pc_i440fx_6_0_machine_options); +DEFINE_I440FX_MACHINE(6, 0); -static void pc_i440fx_5_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_5_2_options(MachineClass *m) { - pc_i440fx_6_0_machine_options(m); + pc_i440fx_machine_6_0_options(m); compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len); compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); } -DEFINE_I440FX_MACHINE(v5_2, "pc-i440fx-5.2", - pc_i440fx_5_2_machine_options); +DEFINE_I440FX_MACHINE(5, 2); -static void pc_i440fx_5_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_5_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_5_2_machine_options(m); + pc_i440fx_machine_5_2_options(m); compat_props_add(m->compat_props, hw_compat_5_1, hw_compat_5_1_len); compat_props_add(m->compat_props, pc_compat_5_1, pc_compat_5_1_len); pcmc->kvmclock_create_always = false; pcmc->pci_root_uid = 1; } -DEFINE_I440FX_MACHINE(v5_1, "pc-i440fx-5.1", - pc_i440fx_5_1_machine_options); +DEFINE_I440FX_MACHINE(5, 1); -static void pc_i440fx_5_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_5_0_options(MachineClass *m) { - pc_i440fx_5_1_machine_options(m); + pc_i440fx_machine_5_1_options(m); m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); m->auto_enable_numa_with_memdev = false; } -DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", - pc_i440fx_5_0_machine_options); +DEFINE_I440FX_MACHINE(5, 0); -static void pc_i440fx_4_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_4_2_options(MachineClass *m) { - pc_i440fx_5_0_machine_options(m); + pc_i440fx_machine_5_0_options(m); compat_props_add(m->compat_props, hw_compat_4_2, hw_compat_4_2_len); compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); } -DEFINE_I440FX_MACHINE(v4_2, "pc-i440fx-4.2", - pc_i440fx_4_2_machine_options); +DEFINE_I440FX_MACHINE(4, 2); -static void pc_i440fx_4_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_4_1_options(MachineClass *m) { - pc_i440fx_4_2_machine_options(m); + pc_i440fx_machine_4_2_options(m); compat_props_add(m->compat_props, hw_compat_4_1, hw_compat_4_1_len); compat_props_add(m->compat_props, pc_compat_4_1, pc_compat_4_1_len); } -DEFINE_I440FX_MACHINE(v4_1, "pc-i440fx-4.1", - pc_i440fx_4_1_machine_options); +DEFINE_I440FX_MACHINE(4, 1); -static void pc_i440fx_4_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_4_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_4_1_machine_options(m); + pc_i440fx_machine_4_1_options(m); pcmc->default_cpu_version = CPU_VERSION_LEGACY; compat_props_add(m->compat_props, hw_compat_4_0, hw_compat_4_0_len); compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); } -DEFINE_I440FX_MACHINE(v4_0, "pc-i440fx-4.0", - pc_i440fx_4_0_machine_options); +DEFINE_I440FX_MACHINE(4, 0); -static void pc_i440fx_3_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_3_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_4_0_machine_options(m); + pc_i440fx_machine_4_0_options(m); m->smbus_no_migration_support = true; pcmc->pvh_enabled = false; compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); } -DEFINE_I440FX_MACHINE(v3_1, "pc-i440fx-3.1", - pc_i440fx_3_1_machine_options); +DEFINE_I440FX_MACHINE(3, 1); -static void pc_i440fx_3_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_3_0_options(MachineClass *m) { - pc_i440fx_3_1_machine_options(m); + pc_i440fx_machine_3_1_options(m); compat_props_add(m->compat_props, hw_compat_3_0, hw_compat_3_0_len); compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); } -DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", - pc_i440fx_3_0_machine_options); +DEFINE_I440FX_MACHINE(3, 0); -static void pc_i440fx_2_12_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_12_options(MachineClass *m) { - pc_i440fx_3_0_machine_options(m); + pc_i440fx_machine_3_0_options(m); m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } -DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", - pc_i440fx_2_12_machine_options); +DEFINE_I440FX_MACHINE(2, 12); -static void pc_i440fx_2_11_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_11_options(MachineClass *m) { - pc_i440fx_2_12_machine_options(m); + pc_i440fx_machine_2_12_options(m); compat_props_add(m->compat_props, hw_compat_2_11, hw_compat_2_11_len); compat_props_add(m->compat_props, pc_compat_2_11, pc_compat_2_11_len); } -DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", - pc_i440fx_2_11_machine_options); +DEFINE_I440FX_MACHINE(2, 11); -static void pc_i440fx_2_10_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_10_options(MachineClass *m) { - pc_i440fx_2_11_machine_options(m); + pc_i440fx_machine_2_11_options(m); compat_props_add(m->compat_props, hw_compat_2_10, hw_compat_2_10_len); compat_props_add(m->compat_props, pc_compat_2_10, pc_compat_2_10_len); m->auto_enable_numa_with_memhp = false; } -DEFINE_I440FX_MACHINE(v2_10, "pc-i440fx-2.10", - pc_i440fx_2_10_machine_options); +DEFINE_I440FX_MACHINE(2, 10); -static void pc_i440fx_2_9_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_9_options(MachineClass *m) { - pc_i440fx_2_10_machine_options(m); + pc_i440fx_machine_2_10_options(m); compat_props_add(m->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); } -DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", - pc_i440fx_2_9_machine_options); +DEFINE_I440FX_MACHINE(2, 9); -static void pc_i440fx_2_8_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_8_options(MachineClass *m) { - pc_i440fx_2_9_machine_options(m); + pc_i440fx_machine_2_9_options(m); compat_props_add(m->compat_props, hw_compat_2_8, hw_compat_2_8_len); compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } -DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", - pc_i440fx_2_8_machine_options); +DEFINE_I440FX_MACHINE(2, 8); -static void pc_i440fx_2_7_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_7_options(MachineClass *m) { - pc_i440fx_2_8_machine_options(m); + pc_i440fx_machine_2_8_options(m); compat_props_add(m->compat_props, hw_compat_2_7, hw_compat_2_7_len); compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } -DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", - pc_i440fx_2_7_machine_options); +DEFINE_I440FX_MACHINE(2, 7); -static void pc_i440fx_2_6_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_6_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_2_7_machine_options(m); + pc_i440fx_machine_2_7_options(m); pcmc->legacy_cpu_hotplug = true; x86mc->fwcfg_dma_enabled = false; compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } -DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", - pc_i440fx_2_6_machine_options); +DEFINE_I440FX_MACHINE(2, 6); -static void pc_i440fx_2_5_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_5_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - pc_i440fx_2_6_machine_options(m); + pc_i440fx_machine_2_6_options(m); x86mc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } -DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", - pc_i440fx_2_5_machine_options); +DEFINE_I440FX_MACHINE(2, 5); -static void pc_i440fx_2_4_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_4_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_2_5_machine_options(m); + pc_i440fx_machine_2_5_options(m); m->hw_version = "2.4.0"; pcmc->broken_reserved_end = true; compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } -DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", - pc_i440fx_2_4_machine_options) +DEFINE_I440FX_MACHINE(2, 4); #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) @@ -833,20 +806,20 @@ DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, #endif #ifdef CONFIG_XEN -static void xenfv_4_2_machine_options(MachineClass *m) +static void xenfv_machine_4_2_options(MachineClass *m) { - pc_i440fx_4_2_machine_options(m); + pc_i440fx_machine_4_2_options(m); m->desc = "Xen Fully-virtualized PC"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; } DEFINE_PC_MACHINE(xenfv_4_2, "xenfv-4.2", pc_xen_hvm_init, - xenfv_4_2_machine_options); + xenfv_machine_4_2_options); -static void xenfv_3_1_machine_options(MachineClass *m) +static void xenfv_machine_3_1_options(MachineClass *m) { - pc_i440fx_3_1_machine_options(m); + pc_i440fx_machine_3_1_options(m); m->desc = "Xen Fully-virtualized PC"; m->alias = "xenfv"; m->max_cpus = HVM_MAX_VCPUS; @@ -854,5 +827,5 @@ static void xenfv_3_1_machine_options(MachineClass *m) } DEFINE_PC_MACHINE(xenfv, "xenfv-3.1", pc_xen_hvm_init, - xenfv_3_1_machine_options); + xenfv_machine_3_1_options); #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 46bc411063..027c6f29f7 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -317,4 +317,30 @@ extern const size_t pc_compat_2_3_len; } \ type_init(pc_machine_init_##suffix) +#define DEFINE_PC_VER_MACHINE(namesym, namestr, initfn, ...) \ + static void MACHINE_VER_SYM(init, namesym, __VA_ARGS__)( \ + MachineState *machine) \ + { \ + initfn(machine); \ + } \ + static void MACHINE_VER_SYM(class_init, namesym, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + MACHINE_VER_SYM(options, namesym, __VA_ARGS__)(mc); \ + mc->init = MACHINE_VER_SYM(init, namesym, __VA_ARGS__); \ + } \ + static const TypeInfo MACHINE_VER_SYM(info, namesym, __VA_ARGS__) = \ + { \ + .name = MACHINE_VER_TYPE_NAME(namestr, __VA_ARGS__), \ + .parent = TYPE_PC_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, namesym, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, namesym, __VA_ARGS__)(void) \ + { \ + type_register(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \ + } \ + type_init(MACHINE_VER_SYM(register, namesym, __VA_ARGS__)); + #endif From 3e3ead554d99ca02d74be84f3427044cb138c9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:35 +0100 Subject: [PATCH 1806/2884] hw/i386: convert 'q35' machine definitions to use new macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the DEFINE_Q35_MACHINE macro to use the common helpers for constructing versioned symbol names and strings, bringing greater consistency across targets. The added benefit is that it avoids the need to repeat the version number thrice in three different formats in the calls to DEFINE_Q35_MACHINE. Due to the odd-ball '4.0.1' machine type version, this commit introduces a DEFINE_Q35_BUGFIX helper, to allow defining of "bugfix" machine types which have a three digit version. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-8-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_q35.c | 215 ++++++++++++++++++++--------------------------- 1 file changed, 90 insertions(+), 125 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index bd7db4abac..71d3c6d122 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -331,17 +331,11 @@ static void pc_q35_init(MachineState *machine) } } -#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \ - static void pc_init_##suffix(MachineState *machine) \ - { \ - void (*compat)(MachineState *m) = (compatfn); \ - if (compat) { \ - compat(machine); \ - } \ - pc_q35_init(machine); \ - } \ - DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) +#define DEFINE_Q35_MACHINE(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, major, minor); +#define DEFINE_Q35_MACHINE_BUGFIX(major, minor, micro) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, major, minor, micro); static void pc_q35_machine_options(MachineClass *m) { @@ -367,32 +361,30 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_9_1_machine_options(MachineClass *m) +static void pc_q35_machine_9_1_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } -DEFINE_Q35_MACHINE(v9_1, "pc-q35-9.1", NULL, - pc_q35_9_1_machine_options); +DEFINE_Q35_MACHINE(9, 1); -static void pc_q35_9_0_machine_options(MachineClass *m) +static void pc_q35_machine_9_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_9_1_machine_options(m); + pc_q35_machine_9_1_options(m); m->alias = NULL; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); pcmc->isa_bios_alias = false; } -DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL, - pc_q35_9_0_machine_options); +DEFINE_Q35_MACHINE(9, 0); -static void pc_q35_8_2_machine_options(MachineClass *m) +static void pc_q35_machine_8_2_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_9_0_machine_options(m); + pc_q35_machine_9_0_options(m); m->max_cpus = 1024; compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); @@ -400,26 +392,24 @@ static void pc_q35_8_2_machine_options(MachineClass *m) pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; } -DEFINE_Q35_MACHINE(v8_2, "pc-q35-8.2", NULL, - pc_q35_8_2_machine_options); +DEFINE_Q35_MACHINE(8, 2); -static void pc_q35_8_1_machine_options(MachineClass *m) +static void pc_q35_machine_8_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_8_2_machine_options(m); + pc_q35_machine_8_2_options(m); pcmc->broken_32bit_mem_addr_check = true; compat_props_add(m->compat_props, hw_compat_8_1, hw_compat_8_1_len); compat_props_add(m->compat_props, pc_compat_8_1, pc_compat_8_1_len); } -DEFINE_Q35_MACHINE(v8_1, "pc-q35-8.1", NULL, - pc_q35_8_1_machine_options); +DEFINE_Q35_MACHINE(8, 1); -static void pc_q35_8_0_machine_options(MachineClass *m) +static void pc_q35_machine_8_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_8_1_machine_options(m); + pc_q35_machine_8_1_options(m); compat_props_add(m->compat_props, hw_compat_8_0, hw_compat_8_0_len); compat_props_add(m->compat_props, pc_compat_8_0, pc_compat_8_0_len); @@ -428,132 +418,120 @@ static void pc_q35_8_0_machine_options(MachineClass *m) m->max_cpus = 288; } -DEFINE_Q35_MACHINE(v8_0, "pc-q35-8.0", NULL, - pc_q35_8_0_machine_options); +DEFINE_Q35_MACHINE(8, 0); -static void pc_q35_7_2_machine_options(MachineClass *m) +static void pc_q35_machine_7_2_options(MachineClass *m) { - pc_q35_8_0_machine_options(m); + pc_q35_machine_8_0_options(m); compat_props_add(m->compat_props, hw_compat_7_2, hw_compat_7_2_len); compat_props_add(m->compat_props, pc_compat_7_2, pc_compat_7_2_len); } -DEFINE_Q35_MACHINE(v7_2, "pc-q35-7.2", NULL, - pc_q35_7_2_machine_options); +DEFINE_Q35_MACHINE(7, 2); -static void pc_q35_7_1_machine_options(MachineClass *m) +static void pc_q35_machine_7_1_options(MachineClass *m) { - pc_q35_7_2_machine_options(m); + pc_q35_machine_7_2_options(m); compat_props_add(m->compat_props, hw_compat_7_1, hw_compat_7_1_len); compat_props_add(m->compat_props, pc_compat_7_1, pc_compat_7_1_len); } -DEFINE_Q35_MACHINE(v7_1, "pc-q35-7.1", NULL, - pc_q35_7_1_machine_options); +DEFINE_Q35_MACHINE(7, 1); -static void pc_q35_7_0_machine_options(MachineClass *m) +static void pc_q35_machine_7_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_7_1_machine_options(m); + pc_q35_machine_7_1_options(m); pcmc->enforce_amd_1tb_hole = false; compat_props_add(m->compat_props, hw_compat_7_0, hw_compat_7_0_len); compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); } -DEFINE_Q35_MACHINE(v7_0, "pc-q35-7.0", NULL, - pc_q35_7_0_machine_options); +DEFINE_Q35_MACHINE(7, 0); -static void pc_q35_6_2_machine_options(MachineClass *m) +static void pc_q35_machine_6_2_options(MachineClass *m) { - pc_q35_7_0_machine_options(m); + pc_q35_machine_7_0_options(m); compat_props_add(m->compat_props, hw_compat_6_2, hw_compat_6_2_len); compat_props_add(m->compat_props, pc_compat_6_2, pc_compat_6_2_len); } -DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL, - pc_q35_6_2_machine_options); +DEFINE_Q35_MACHINE(6, 2); -static void pc_q35_6_1_machine_options(MachineClass *m) +static void pc_q35_machine_6_1_options(MachineClass *m) { - pc_q35_6_2_machine_options(m); + pc_q35_machine_6_2_options(m); compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); m->smp_props.prefer_sockets = true; } -DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL, - pc_q35_6_1_machine_options); +DEFINE_Q35_MACHINE(6, 1); -static void pc_q35_6_0_machine_options(MachineClass *m) +static void pc_q35_machine_6_0_options(MachineClass *m) { - pc_q35_6_1_machine_options(m); + pc_q35_machine_6_1_options(m); compat_props_add(m->compat_props, hw_compat_6_0, hw_compat_6_0_len); compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len); } -DEFINE_Q35_MACHINE(v6_0, "pc-q35-6.0", NULL, - pc_q35_6_0_machine_options); +DEFINE_Q35_MACHINE(6, 0); -static void pc_q35_5_2_machine_options(MachineClass *m) +static void pc_q35_machine_5_2_options(MachineClass *m) { - pc_q35_6_0_machine_options(m); + pc_q35_machine_6_0_options(m); compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len); compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); } -DEFINE_Q35_MACHINE(v5_2, "pc-q35-5.2", NULL, - pc_q35_5_2_machine_options); +DEFINE_Q35_MACHINE(5, 2); -static void pc_q35_5_1_machine_options(MachineClass *m) +static void pc_q35_machine_5_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_5_2_machine_options(m); + pc_q35_machine_5_2_options(m); compat_props_add(m->compat_props, hw_compat_5_1, hw_compat_5_1_len); compat_props_add(m->compat_props, pc_compat_5_1, pc_compat_5_1_len); pcmc->kvmclock_create_always = false; pcmc->pci_root_uid = 1; } -DEFINE_Q35_MACHINE(v5_1, "pc-q35-5.1", NULL, - pc_q35_5_1_machine_options); +DEFINE_Q35_MACHINE(5, 1); -static void pc_q35_5_0_machine_options(MachineClass *m) +static void pc_q35_machine_5_0_options(MachineClass *m) { - pc_q35_5_1_machine_options(m); + pc_q35_machine_5_1_options(m); m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); m->auto_enable_numa_with_memdev = false; } -DEFINE_Q35_MACHINE(v5_0, "pc-q35-5.0", NULL, - pc_q35_5_0_machine_options); +DEFINE_Q35_MACHINE(5, 0); -static void pc_q35_4_2_machine_options(MachineClass *m) +static void pc_q35_machine_4_2_options(MachineClass *m) { - pc_q35_5_0_machine_options(m); + pc_q35_machine_5_0_options(m); compat_props_add(m->compat_props, hw_compat_4_2, hw_compat_4_2_len); compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); } -DEFINE_Q35_MACHINE(v4_2, "pc-q35-4.2", NULL, - pc_q35_4_2_machine_options); +DEFINE_Q35_MACHINE(4, 2); -static void pc_q35_4_1_machine_options(MachineClass *m) +static void pc_q35_machine_4_1_options(MachineClass *m) { - pc_q35_4_2_machine_options(m); + pc_q35_machine_4_2_options(m); compat_props_add(m->compat_props, hw_compat_4_1, hw_compat_4_1_len); compat_props_add(m->compat_props, pc_compat_4_1, pc_compat_4_1_len); } -DEFINE_Q35_MACHINE(v4_1, "pc-q35-4.1", NULL, - pc_q35_4_1_machine_options); +DEFINE_Q35_MACHINE(4, 1); -static void pc_q35_4_0_1_machine_options(MachineClass *m) +static void pc_q35_machine_4_0_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_4_1_machine_options(m); + pc_q35_machine_4_1_options(m); pcmc->default_cpu_version = CPU_VERSION_LEGACY; /* * This is the default machine for the 4.0-stable branch. It is basically @@ -564,24 +542,22 @@ static void pc_q35_4_0_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); } -DEFINE_Q35_MACHINE(v4_0_1, "pc-q35-4.0.1", NULL, - pc_q35_4_0_1_machine_options); +DEFINE_Q35_MACHINE_BUGFIX(4, 0, 1); -static void pc_q35_4_0_machine_options(MachineClass *m) +static void pc_q35_machine_4_0_options(MachineClass *m) { - pc_q35_4_0_1_machine_options(m); + pc_q35_machine_4_0_1_options(m); m->default_kernel_irqchip_split = true; /* Compat props are applied by the 4.0.1 machine */ } -DEFINE_Q35_MACHINE(v4_0, "pc-q35-4.0", NULL, - pc_q35_4_0_machine_options); +DEFINE_Q35_MACHINE(4, 0); -static void pc_q35_3_1_machine_options(MachineClass *m) +static void pc_q35_machine_3_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_4_0_machine_options(m); + pc_q35_machine_4_0_options(m); m->default_kernel_irqchip_split = false; m->smbus_no_migration_support = true; pcmc->pvh_enabled = false; @@ -589,121 +565,110 @@ static void pc_q35_3_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); } -DEFINE_Q35_MACHINE(v3_1, "pc-q35-3.1", NULL, - pc_q35_3_1_machine_options); +DEFINE_Q35_MACHINE(3, 1); -static void pc_q35_3_0_machine_options(MachineClass *m) +static void pc_q35_machine_3_0_options(MachineClass *m) { - pc_q35_3_1_machine_options(m); + pc_q35_machine_3_1_options(m); compat_props_add(m->compat_props, hw_compat_3_0, hw_compat_3_0_len); compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); } -DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, - pc_q35_3_0_machine_options); +DEFINE_Q35_MACHINE(3, 0); -static void pc_q35_2_12_machine_options(MachineClass *m) +static void pc_q35_machine_2_12_options(MachineClass *m) { - pc_q35_3_0_machine_options(m); + pc_q35_machine_3_0_options(m); compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } -DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, - pc_q35_2_12_machine_options); +DEFINE_Q35_MACHINE(2, 12); -static void pc_q35_2_11_machine_options(MachineClass *m) +static void pc_q35_machine_2_11_options(MachineClass *m) { - pc_q35_2_12_machine_options(m); + pc_q35_machine_2_12_options(m); m->default_nic = "e1000"; compat_props_add(m->compat_props, hw_compat_2_11, hw_compat_2_11_len); compat_props_add(m->compat_props, pc_compat_2_11, pc_compat_2_11_len); } -DEFINE_Q35_MACHINE(v2_11, "pc-q35-2.11", NULL, - pc_q35_2_11_machine_options); +DEFINE_Q35_MACHINE(2, 11); -static void pc_q35_2_10_machine_options(MachineClass *m) +static void pc_q35_machine_2_10_options(MachineClass *m) { - pc_q35_2_11_machine_options(m); + pc_q35_machine_2_11_options(m); compat_props_add(m->compat_props, hw_compat_2_10, hw_compat_2_10_len); compat_props_add(m->compat_props, pc_compat_2_10, pc_compat_2_10_len); m->auto_enable_numa_with_memhp = false; } -DEFINE_Q35_MACHINE(v2_10, "pc-q35-2.10", NULL, - pc_q35_2_10_machine_options); +DEFINE_Q35_MACHINE(2, 10); -static void pc_q35_2_9_machine_options(MachineClass *m) +static void pc_q35_machine_2_9_options(MachineClass *m) { - pc_q35_2_10_machine_options(m); + pc_q35_machine_2_10_options(m); compat_props_add(m->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); } -DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL, - pc_q35_2_9_machine_options); +DEFINE_Q35_MACHINE(2, 9); -static void pc_q35_2_8_machine_options(MachineClass *m) +static void pc_q35_machine_2_8_options(MachineClass *m) { - pc_q35_2_9_machine_options(m); + pc_q35_machine_2_9_options(m); compat_props_add(m->compat_props, hw_compat_2_8, hw_compat_2_8_len); compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } -DEFINE_Q35_MACHINE(v2_8, "pc-q35-2.8", NULL, - pc_q35_2_8_machine_options); +DEFINE_Q35_MACHINE(2, 8); -static void pc_q35_2_7_machine_options(MachineClass *m) +static void pc_q35_machine_2_7_options(MachineClass *m) { - pc_q35_2_8_machine_options(m); + pc_q35_machine_2_8_options(m); m->max_cpus = 255; compat_props_add(m->compat_props, hw_compat_2_7, hw_compat_2_7_len); compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } -DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, - pc_q35_2_7_machine_options); +DEFINE_Q35_MACHINE(2, 7); -static void pc_q35_2_6_machine_options(MachineClass *m) +static void pc_q35_machine_2_6_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_2_7_machine_options(m); + pc_q35_machine_2_7_options(m); pcmc->legacy_cpu_hotplug = true; x86mc->fwcfg_dma_enabled = false; compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } -DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, - pc_q35_2_6_machine_options); +DEFINE_Q35_MACHINE(2, 6); -static void pc_q35_2_5_machine_options(MachineClass *m) +static void pc_q35_machine_2_5_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - pc_q35_2_6_machine_options(m); + pc_q35_machine_2_6_options(m); x86mc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } -DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL, - pc_q35_2_5_machine_options); +DEFINE_Q35_MACHINE(2, 5); -static void pc_q35_2_4_machine_options(MachineClass *m) +static void pc_q35_machine_2_4_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_2_5_machine_options(m); + pc_q35_machine_2_5_options(m); m->hw_version = "2.4.0"; pcmc->broken_reserved_end = true; compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } -DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL, - pc_q35_2_4_machine_options); +DEFINE_Q35_MACHINE(2, 4); From a35f8577a0789fe883047afb13a45ecfae4f0107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:36 +0100 Subject: [PATCH 1807/2884] include/hw: add macros for deprecation & removal of versioned machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Versioned machines live for a long time to provide back compat for incoming migration and restore of saved images. To guide users away from usage of old machines, however, we want to deprecate any older than 3 years (equiv of 9 releases), and delete any older than 6 years (equiva of 18 releases). To get a standardized deprecation message and avoid having to remember to manually add it after three years, this introduces two macros to be used by targets when defining versioned machines. * MACHINE_VER_DEPRECATION(major, minor) Automates the task of setting the 'deprecation_reason' field on the machine, if-and-only-if the major/minor version is older than 3 years. * MACHINE_VER_DELETION(major, minor) Simulates the deletion of by skipping registration of the QOM type for a versioned machine, if-and-only-if the major/minor version is older than 6 years. By using these two macros there is no longer any manual work required per-release to deprecate old machines. By preventing the use of machines that have reached their deletion date, it is also not necessary to manually delete machines per-release. Deletion can be batched up once a year or whenever makes most sense. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-9-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/boards.h | 96 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/include/hw/boards.h b/include/hw/boards.h index d5ad712585..187ed76646 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -429,6 +429,7 @@ struct MachineState { * MachineClass *mc = MACHINE_CLASS(oc); \ * MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ * mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " Virtual Machine"; \ + * MACHINE_VER_DEPRECATION(__VA_ARGS__); \ * if (latest) { \ * mc->alias = "virt"; \ * } \ @@ -440,6 +441,7 @@ struct MachineState { * }; \ * static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ * { \ + * MACHINE_VER_DELETION(__VA_ARGS__); \ * type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ * } \ * type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); @@ -598,6 +600,100 @@ struct MachineState { _MACHINE_VER_SYM2) (sym, prefix, __VA_ARGS__) +/* + * How many years/major releases for each phase + * of the life cycle. Assumes use of versioning + * scheme where major is bumped each year + */ +#define MACHINE_VER_DELETION_MAJOR 6 +#define MACHINE_VER_DEPRECATION_MAJOR 3 + +/* + * Expands to a static string containing a deprecation + * message for a versioned machine type + */ +#define MACHINE_VER_DEPRECATION_MSG \ + "machines more than " stringify(MACHINE_VER_DEPRECATION_MAJOR) \ + " years old are subject to deletion after " \ + stringify(MACHINE_VER_DELETION_MAJOR) " years" + +#define _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) \ + (((QEMU_VERSION_MAJOR - major) > cutoff) || \ + (((QEMU_VERSION_MAJOR - major) == cutoff) && \ + (QEMU_VERSION_MINOR - minor) >= 0)) + +#define _MACHINE_VER_IS_EXPIRED2(cutoff, major, minor) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) +#define _MACHINE_VER_IS_EXPIRED3(cutoff, major, minor, micro) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) +#define _MACHINE_VER_IS_EXPIRED4(cutoff, major, minor, _unused, tag) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) +#define _MACHINE_VER_IS_EXPIRED5(cutoff, major, minor, micro, _unused, tag) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) + +#define _MACHINE_IS_EXPIRED(cutoff, ...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_IS_EXPIRED5, \ + _MACHINE_VER_IS_EXPIRED4, \ + _MACHINE_VER_IS_EXPIRED3, \ + _MACHINE_VER_IS_EXPIRED2) (cutoff, __VA_ARGS__) + +/* + * Evaluates true when a machine type with (major, minor) + * or (major, minor, micro) version should be considered + * deprecated based on the current versioned machine type + * lifecycle rules + */ +#define MACHINE_VER_IS_DEPRECATED(...) \ + _MACHINE_IS_EXPIRED(MACHINE_VER_DEPRECATION_MAJOR, __VA_ARGS__) + +/* + * Evaluates true when a machine type with (major, minor) + * or (major, minor, micro) version should be considered + * for deletion based on the current versioned machine type + * lifecycle rules + */ +#define MACHINE_VER_SHOULD_DELETE(...) \ + _MACHINE_IS_EXPIRED(MACHINE_VER_DELETION_MAJOR, __VA_ARGS__) + +/* + * Sets the deprecation reason for a versioned machine based + * on its age + * + * This must be unconditionally used in the _class_init + * function for all machine types which support versioning. + * + * Initially it will effectively be a no-op, but after a + * suitable period of time has passed, it will set the + * 'deprecation_reason' field on the machine, to warn users + * about forthcoming removal. + */ +#define MACHINE_VER_DEPRECATION(...) \ + do { \ + if (MACHINE_VER_IS_DEPRECATED(__VA_ARGS__)) { \ + mc->deprecation_reason = MACHINE_VER_DEPRECATION_MSG; \ + } \ + } while (0) + +/* + * Prevents registration of a versioned machined based on + * its age + * + * This must be unconditionally used in the register + * method for all machine types which support versioning. + * + * Inijtially it will effectively be a no-op, but after a + * suitable period of time has passed, it will cause + * execution of the method to return, avoiding registration + * of the machine + */ +#define MACHINE_VER_DELETION(...) \ + do { \ + if (MACHINE_VER_SHOULD_DELETE(__VA_ARGS__)) { \ + return; \ + } \ + } while (0) + #define DEFINE_MACHINE(namestr, machine_initfn) \ static void machine_initfn##_class_init(ObjectClass *oc, void *data) \ { \ From c9fd2d9a48ee3c195cf83cc611b87b09f02f0013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:37 +0100 Subject: [PATCH 1808/2884] include/hw: temporarily disable deletion of versioned machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new deprecation and deletion policy for versioned machine types is being introduced in QEMU 9.1.0. Under the new policy a number of old machine types (any prior to 2.12) would be liable for immediate deletion which would be a violation of our historical deprecation and removal policy Thus automatic deletions (by skipping QOM registration) are temporarily gated on existance of the env variable "QEMU_DELETE_MACHINES" / QEMU version number >= 10.1.0. This allows opt-in testing of the automatic deletion logic, while activating it fully in QEMU >= 10.1.0. This whole commit should be reverted in the 10.1.0 dev cycle or shortly thereafter. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20240620165742.1711389-10-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/boards.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/include/hw/boards.h b/include/hw/boards.h index 187ed76646..ef6f18f2c1 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -686,11 +686,28 @@ struct MachineState { * suitable period of time has passed, it will cause * execution of the method to return, avoiding registration * of the machine + * + * The new deprecation and deletion policy for versioned + * machine types was introduced in QEMU 9.1.0. + * + * Under the new policy a number of old machine types (any + * prior to 2.12) would be liable for immediate deletion + * which would be a violation of our historical deprecation + * and removal policy + * + * Thus deletions are temporarily gated on existance of + * the env variable "QEMU_DELETE_MACHINES" / QEMU version + * number >= 10.1.0. This gate can be deleted in the 10.1.0 + * dev cycle */ #define MACHINE_VER_DELETION(...) \ do { \ if (MACHINE_VER_SHOULD_DELETE(__VA_ARGS__)) { \ - return; \ + if (getenv("QEMU_DELETE_MACHINES") || \ + QEMU_VERSION_MAJOR > 10 || (QEMU_VERSION_MAJOR == 10 && \ + QEMU_VERSION_MINOR >= 1)) { \ + return; \ + } \ } \ } while (0) From 8d3122a80647673eee7a5166041a687dc9879a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:38 +0100 Subject: [PATCH 1809/2884] hw: set deprecation info for all versioned machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This calls the MACHINE_VER_DEPRECATION() macro in the definition of all machine type classes which support versioning. This ensures that they will automatically get deprecation info set when they reach the appropriate point in their lifecycle. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-11-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/virt.c | 1 + hw/m68k/virt.c | 1 + hw/ppc/spapr.c | 1 + hw/s390x/s390-virtio-ccw.c | 1 + include/hw/i386/pc.h | 1 + 5 files changed, 5 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 08990b9abe..02e13b4a3d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -110,6 +110,7 @@ static void arm_virt_compat_set(MachineClass *mc) arm_virt_compat_set(mc); \ MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " ARM Virtual Machine"; \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ } \ diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index cd6ee692f7..37bb36b385 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -343,6 +343,7 @@ type_init(virt_machine_register_types) MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " M68K Virtual Machine"; \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ } \ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2785b6b303..55268489d3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4811,6 +4811,7 @@ static void spapr_machine_latest_class_options(MachineClass *mc) { \ MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(class_options, spapr, __VA_ARGS__)(mc); \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ spapr_machine_latest_class_options(mc); \ } \ diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 380e9e2e5b..c25dc3e13f 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -826,6 +826,7 @@ static const TypeInfo ccw_machine_info = { MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(class_options, ccw, __VA_ARGS__)(mc); \ mc->desc = "Virtual s390x machine (version " MACHINE_VER_STR(__VA_ARGS__) ")"; \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "s390-ccw-virtio"; \ mc->is_default = true; \ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 027c6f29f7..83d2e66498 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -330,6 +330,7 @@ extern const size_t pc_compat_2_3_len; MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(options, namesym, __VA_ARGS__)(mc); \ mc->init = MACHINE_VER_SYM(init, namesym, __VA_ARGS__); \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ } \ static const TypeInfo MACHINE_VER_SYM(info, namesym, __VA_ARGS__) = \ { \ From a391eeb129de8b0a9cb9fac4d3fbe5bd3a3cc6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:39 +0100 Subject: [PATCH 1810/2884] hw: skip registration of outdated versioned machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This calls the MACHINE_VER_DELETION() macro in the machine type registration method, so that when a versioned machine type reaches the end of its life, it is no longer registered with QOM and thus cannot be used. The actual definition of the machine type should be deleted at this point, but experience shows that can easily be forgotten. By skipping registration the manual code deletion task can be done at any later date. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-12-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/virt.c | 1 + hw/m68k/virt.c | 1 + hw/ppc/spapr.c | 1 + hw/s390x/s390-virtio-ccw.c | 1 + include/hw/i386/pc.h | 1 + 5 files changed, 5 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 02e13b4a3d..b0c68d66a3 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -123,6 +123,7 @@ static void arm_virt_compat_set(MachineClass *mc) }; \ static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 37bb36b385..cda199af8f 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -356,6 +356,7 @@ type_init(virt_machine_register_types) }; \ static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 55268489d3..044e6a8d9d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4824,6 +4824,7 @@ static void spapr_machine_latest_class_options(MachineClass *mc) }; \ static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \ { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ type_register(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, spapr, __VA_ARGS__)) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index c25dc3e13f..336cb8c6d4 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -847,6 +847,7 @@ static const TypeInfo ccw_machine_info = { }; \ static void MACHINE_VER_SYM(register, ccw, __VA_ARGS__)(void) \ { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ type_register_static(&MACHINE_VER_SYM(info, ccw, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, ccw, __VA_ARGS__)) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 83d2e66498..4e55d7ef6e 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -340,6 +340,7 @@ extern const size_t pc_compat_2_3_len; }; \ static void MACHINE_VER_SYM(register, namesym, __VA_ARGS__)(void) \ { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ type_register(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \ } \ type_init(MACHINE_VER_SYM(register, namesym, __VA_ARGS__)); From f8df57728685495c7cf004e625c085f744d2de23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:40 +0100 Subject: [PATCH 1811/2884] hw/ppc: remove obsolete manual deprecation reason string of spapr machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The automatic deprecation mechanism introduced in the preceeding patches will mark every spapr machine upto and including 2.12 as deprecated. As such we can revert the manually added deprecation which was a subset: commit 1392617d35765d5d912625fbb5cab1ffbed8e140 Author: Cédric Le Goater Date: Tue Jan 23 16:37:02 2024 +1000 spapr: Tag pseries-2.1 - 2.11 machines as deprecated Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-13-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/spapr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 044e6a8d9d..98fa3aa6a8 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -5156,7 +5156,6 @@ static void spapr_machine_2_11_class_options(MachineClass *mc) spapr_machine_2_12_class_options(mc); smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_ON; compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); - mc->deprecation_reason = "old and not maintained - use a 2.12+ version"; } DEFINE_SPAPR_MACHINE(2, 11); From 37193b7b43b6a973e56fa115098c5895ebdc7145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:41 +0100 Subject: [PATCH 1812/2884] hw/i386: remove obsolete manual deprecation reason string of i440fx machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The automatic deprecation mechanism introduced in the preceeding patches will mark every i440fx machine upto and including 2.12 as deprecated. As such we can revert the manually added deprecation introduced in: commit 792b4fdd4eb8197bd6eb9e80a1dfaf0cb3b54aeb Author: Philippe Mathieu-Daudé Date: Wed Feb 28 10:34:35 2024 +0100 hw/i386/pc: Deprecate 2.4 to 2.12 pc-i440fx machines Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-14-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 5705d6e155..9445b07b4f 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -688,7 +688,6 @@ DEFINE_I440FX_MACHINE(3, 0); static void pc_i440fx_machine_2_12_options(MachineClass *m) { pc_i440fx_machine_3_0_options(m); - m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } From ce80c4fa6ff0f5c379bba7db74d04593e9fb12f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 20 Jun 2024 17:57:42 +0100 Subject: [PATCH 1813/2884] docs: document special exception for machine type deprecation & removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extends the deprecation policy to indicate that versioned machine types will be marked deprecated after 3 years, and then subject to removal after a further 3 years has passed. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240620165742.1711389-15-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- docs/about/deprecated.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index ff3da68208..bba12d1641 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -11,6 +11,19 @@ releases, the feature is liable to be removed. Deprecated features may also generate warnings on the console when QEMU starts up, or if activated via a monitor command, however, this is not a mandatory requirement. +As a special exception to this general timeframe, rather than have an +indefinite lifetime, versioned machine types are only intended to be +supported for a period of 6 years, equivalent to 18 QEMU releases. All +versioned machine types will be automatically marked deprecated after an +initial 3 years (9 QEMU releases) has passed, and will then be deleted after +a further 3 year period has passed. It is recommended that a deprecated +machine type is only used for incoming migrations and restore of saved state, +for pre-existing VM deployments. They should be scheduled for updating to a +newer machine type during an appropriate service window. Newly deployed VMs +should exclusively use a non-deprecated machine type, with use of the most +recent version highly recommended. Non-versioned machine types follow the +general feature deprecation policy. + Prior to the 2.10.0 release there was no official policy on how long features would be deprecated prior to their removal, nor any documented list of which features were deprecated. Thus From 0ff3243a4686b020c554c62efbb25df1d4712b97 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 28 Jun 2024 13:05:52 +0900 Subject: [PATCH 1814/2884] system/physmem: Fix reference to dump-guest-core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dump_guest_core is exposed as dump-guest-core with QOM. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Message-ID: <20240628-dump-v1-1-c581d10f3646@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index 33d09f7571..261196cde0 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1521,7 +1521,7 @@ static void qemu_ram_setup_dump(void *addr, ram_addr_t size) if (ret) { perror("qemu_madvise"); fprintf(stderr, "madvise doesn't support MADV_DONTDUMP, " - "but dump_guest_core=off specified\n"); + "but dump-guest-core=off specified\n"); } } } From efb359346c7af62dfa86f7ae7d3717a098b239f5 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 28 Jun 2024 17:03:34 +0100 Subject: [PATCH 1815/2884] hw/ide/macio: switch from using qemu_allocate_irq() to qdev input GPIOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prevents the IRQs from being leaked when the macio IDE device is used. Signed-off-by: Mark Cave-Ayland Reviewed-by: Peter Maydell Reviewed-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240628160334.653168-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/macio.c | 10 ++++++---- include/hw/misc/macio/macio.h | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index aca90d04f0..e84bf2c9f6 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -420,7 +420,8 @@ static void macio_ide_realizefn(DeviceState *dev, Error **errp) { MACIOIDEState *s = MACIO_IDE(dev); - ide_bus_init_output_irq(&s->bus, s->ide_irq); + ide_bus_init_output_irq(&s->bus, + qdev_get_gpio_in(dev, MACIO_IDE_PMAC_IDE_IRQ)); /* Register DMA callbacks */ s->dma.ops = &dbdma_ops; @@ -456,8 +457,8 @@ static void macio_ide_initfn(Object *obj) sysbus_init_mmio(d, &s->mem); sysbus_init_irq(d, &s->real_ide_irq); sysbus_init_irq(d, &s->real_dma_irq); - s->dma_irq = qemu_allocate_irq(pmac_ide_irq, s, 0); - s->ide_irq = qemu_allocate_irq(pmac_ide_irq, s, 1); + + qdev_init_gpio_in(DEVICE(obj), pmac_ide_irq, MACIO_IDE_PMAC_NIRQS); object_property_add_link(obj, "dbdma", TYPE_MAC_DBDMA, (Object **) &s->dbdma, @@ -508,7 +509,8 @@ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) void macio_ide_register_dma(MACIOIDEState *s) { - DBDMA_register_channel(s->dbdma, s->channel, s->dma_irq, + DBDMA_register_channel(s->dbdma, s->channel, + qdev_get_gpio_in(DEVICE(s), MACIO_IDE_PMAC_DMA_IRQ), pmac_ide_transfer, pmac_ide_flush, s); } diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 2b54da6b31..16aa95b876 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -80,8 +80,6 @@ struct MACIOIDEState { uint32_t channel; qemu_irq real_ide_irq; qemu_irq real_dma_irq; - qemu_irq ide_irq; - qemu_irq dma_irq; MemoryRegion mem; IDEBus bus; @@ -92,6 +90,11 @@ struct MACIOIDEState { uint32_t irq_reg; }; +#define MACIO_IDE_PMAC_NIRQS 2 + +#define MACIO_IDE_PMAC_DMA_IRQ 0 +#define MACIO_IDE_PMAC_IDE_IRQ 1 + void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); void macio_ide_register_dma(MACIOIDEState *ide); From f64933c8aee2fd0c46049096f2003f3d156a45ba Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 29 Jun 2024 15:24:44 +0900 Subject: [PATCH 1816/2884] hvf: Drop ifdef for macOS versions older than 12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS versions older than 12.0 are no longer supported. docs/about/build-platforms.rst says: > Support for the previous major version will be dropped 2 years after > the new major version is released or when the vendor itself drops > support, whichever comes first. macOS 12.0 was released 2021: https://www.apple.com/newsroom/2021/10/macos-monterey-is-now-available/ Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240629-macos-v1-1-6e70a6b700a0@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- accel/hvf/hvf-all.c | 3 --- target/i386/hvf/hvf.c | 23 +---------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index c008dc2f1e..6ca0850b20 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -23,10 +23,7 @@ const char *hvf_return_string(hv_return_t ret) case HV_NO_RESOURCES: return "HV_NO_RESOURCES"; case HV_NO_DEVICE: return "HV_NO_DEVICE"; case HV_UNSUPPORTED: return "HV_UNSUPPORTED"; -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 case HV_DENIED: return "HV_DENIED"; -#endif default: return "[unknown hv_return value]"; } } diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 2d0eef6cd9..c9c64e2978 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -427,27 +427,6 @@ static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } -static hv_return_t hvf_vcpu_run(hv_vcpuid_t vcpu_id) -{ - /* - * hv_vcpu_run_until is available and recommended from macOS 10.15+, - * HV_DEADLINE_FOREVER from 11.0. Test for availability at runtime and fall - * back to hv_vcpu_run() only where necessary. - */ -#ifndef MAC_OS_VERSION_11_0 - return hv_vcpu_run(vcpu_id); -#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 - return hv_vcpu_run_until(vcpu_id, HV_DEADLINE_FOREVER); -#else /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 */ - /* 11.0 SDK or newer, but could be < 11 at runtime */ - if (__builtin_available(macOS 11.0, *)) { - return hv_vcpu_run_until(vcpu_id, HV_DEADLINE_FOREVER); - } else { - return hv_vcpu_run(vcpu_id); - } -#endif -} - int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -476,7 +455,7 @@ int hvf_vcpu_exec(CPUState *cpu) return EXCP_HLT; } - hv_return_t r = hvf_vcpu_run(cpu->accel->fd); + hv_return_t r = hv_vcpu_run_until(cpu->accel->fd, HV_DEADLINE_FOREVER); assert_hvf_ok(r); /* handle VMEXIT */ From 0e376d45411f026053c21f850797aecd7e48888f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 29 Jun 2024 15:24:45 +0900 Subject: [PATCH 1817/2884] audio: Drop ifdef for macOS versions older than 12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS versions older than 12.0 are no longer supported. docs/about/build-platforms.rst says: > Support for the previous major version will be dropped 2 years after > the new major version is released or when the vendor itself drops > support, whichever comes first. macOS 12.0 was released 2021: https://www.apple.com/newsroom/2021/10/macos-monterey-is-now-available/ Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240629-macos-v1-2-6e70a6b700a0@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- audio/coreaudio.m | 5 ----- 1 file changed, 5 deletions(-) diff --git a/audio/coreaudio.m b/audio/coreaudio.m index ab632b9bbb..cadd729d50 100644 --- a/audio/coreaudio.m +++ b/audio/coreaudio.m @@ -44,11 +44,6 @@ typedef struct coreaudioVoiceOut { bool enabled; } coreaudioVoiceOut; -#if !defined(MAC_OS_VERSION_12_0) \ - || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) -#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster -#endif - static const AudioObjectPropertyAddress voice_addr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, From e9c9d8dc3ba993384bc9553b617120ad4717345c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 29 Jun 2024 15:24:46 +0900 Subject: [PATCH 1818/2884] block/file-posix: Drop ifdef for macOS versions older than 12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS versions older than 12.0 are no longer supported. docs/about/build-platforms.rst says: > Support for the previous major version will be dropped 2 years after > the new major version is released or when the vendor itself drops > support, whichever comes first. macOS 12.0 was released 2021: https://www.apple.com/newsroom/2021/10/macos-monterey-is-now-available/ Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240629-macos-v1-3-6e70a6b700a0@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- block/file-posix.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index f3bd946afa..ff928b5e85 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3929,11 +3929,6 @@ BlockDriver bdrv_file = { static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize, int flags); -#if !defined(MAC_OS_VERSION_12_0) \ - || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) -#define IOMainPort IOMasterPort -#endif - static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) { kern_return_t kernResult = KERN_FAILURE; From 3bf445fbb199676a516202677acda38015fc51a1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 29 Jun 2024 15:24:47 +0900 Subject: [PATCH 1819/2884] net/vmnet: Drop ifdef for macOS versions older than 12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS versions older than 12.0 are no longer supported. docs/about/build-platforms.rst says: > Support for the previous major version will be dropped 2 years after > the new major version is released or when the vendor itself drops > support, whichever comes first. macOS 12.0 was released 2021: https://www.apple.com/newsroom/2021/10/macos-monterey-is-now-available/ Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20240629-macos-v1-4-6e70a6b700a0@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- net/vmnet-bridged.m | 13 +------------ net/vmnet-common.m | 3 --- net/vmnet-host.c | 24 +----------------------- net/vmnet-shared.c | 13 ------------- 4 files changed, 2 insertions(+), 51 deletions(-) diff --git a/net/vmnet-bridged.m b/net/vmnet-bridged.m index 76a28abe79..a04a14fa11 100644 --- a/net/vmnet-bridged.m +++ b/net/vmnet-bridged.m @@ -88,15 +88,6 @@ static bool validate_options(const Netdev *netdev, Error **errp) return false; } -#if !defined(MAC_OS_VERSION_11_0) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 - if (options->has_isolated) { - error_setg(errp, - "vmnet-bridged.isolated feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#endif return true; } @@ -115,12 +106,10 @@ static xpc_object_t build_if_desc(const Netdev *netdev) vmnet_shared_interface_name_key, options->ifname); -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 xpc_dictionary_set_bool(if_desc, vmnet_enable_isolation_key, options->isolated); -#endif + return if_desc; } diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 2958283485..30c4e53c13 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -47,11 +47,8 @@ const char *vmnet_status_map_str(vmnet_return_t status) return "buffers exhausted in kernel"; case VMNET_TOO_MANY_PACKETS: return "packet count exceeds limit"; -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 case VMNET_SHARING_SERVICE_BUSY: return "conflict, sharing service is in use"; -#endif default: return "unknown vmnet error"; } diff --git a/net/vmnet-host.c b/net/vmnet-host.c index 1f95f7343a..49fb25c224 100644 --- a/net/vmnet-host.c +++ b/net/vmnet-host.c @@ -21,31 +21,13 @@ static bool validate_options(const Netdev *netdev, Error **errp) { const NetdevVmnetHostOptions *options = &(netdev->u.vmnet_host); - -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 - QemuUUID net_uuid; + if (options->net_uuid && qemu_uuid_parse(options->net_uuid, &net_uuid) < 0) { error_setg(errp, "Invalid UUID provided in 'net-uuid'"); return false; } -#else - if (options->has_isolated) { - error_setg(errp, - "vmnet-host.isolated feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } - - if (options->net_uuid) { - error_setg(errp, - "vmnet-host.net-uuid feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#endif if ((options->start_address || options->end_address || @@ -71,9 +53,6 @@ static xpc_object_t build_if_desc(const Netdev *netdev) vmnet_operation_mode_key, VMNET_HOST_MODE); -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 - xpc_dictionary_set_bool(if_desc, vmnet_enable_isolation_key, options->isolated); @@ -85,7 +64,6 @@ static xpc_object_t build_if_desc(const Netdev *netdev) vmnet_network_identifier_key, net_uuid.data); } -#endif if (options->start_address) { xpc_dictionary_set_string(if_desc, diff --git a/net/vmnet-shared.c b/net/vmnet-shared.c index 40c7306a75..4726b07253 100644 --- a/net/vmnet-shared.c +++ b/net/vmnet-shared.c @@ -21,16 +21,6 @@ static bool validate_options(const Netdev *netdev, Error **errp) { const NetdevVmnetSharedOptions *options = &(netdev->u.vmnet_shared); -#if !defined(MAC_OS_VERSION_11_0) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 - if (options->has_isolated) { - error_setg(errp, - "vmnet-shared.isolated feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#endif - if ((options->start_address || options->end_address || options->subnet_mask) && @@ -76,14 +66,11 @@ static xpc_object_t build_if_desc(const Netdev *netdev) options->subnet_mask); } -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 xpc_dictionary_set_bool( if_desc, vmnet_enable_isolation_key, options->isolated ); -#endif return if_desc; } From f0936cbc1d42410ccd58c042bc26fa33a23a77d6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 1 Jul 2024 15:26:49 +0200 Subject: [PATCH 1820/2884] Remove inclusion of hw/hw.h from files that don't need it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw/hw.h only contains the prototype of hw_error() nowadays, so files that don't use this function don't need to include this header. Signed-off-by: Thomas Huth Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Message-ID: <20240701132649.58345-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/edu.c | 1 - hw/vfio/container.c | 1 - include/hw/misc/xlnx-cfi-if.h | 1 - 3 files changed, 3 deletions(-) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index fa052c44db..504178b4a2 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -26,7 +26,6 @@ #include "qemu/log.h" #include "qemu/units.h" #include "hw/pci/pci.h" -#include "hw/hw.h" #include "hw/pci/msi.h" #include "qemu/timer.h" #include "qom/object.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 2e7ecdf10e..88ede913d6 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -26,7 +26,6 @@ #include "exec/address-spaces.h" #include "exec/memory.h" #include "exec/ram_addr.h" -#include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/range.h" #include "sysemu/reset.h" diff --git a/include/hw/misc/xlnx-cfi-if.h b/include/hw/misc/xlnx-cfi-if.h index f9bd12292d..5010401517 100644 --- a/include/hw/misc/xlnx-cfi-if.h +++ b/include/hw/misc/xlnx-cfi-if.h @@ -11,7 +11,6 @@ #define XLNX_CFI_IF_H 1 #include "qemu/help-texts.h" -#include "hw/hw.h" #include "qom/object.h" #define TYPE_XLNX_CFI_IF "xlnx-cfi-if" From 87511bb878b2fc6618bee71add5c28c4e3d36d60 Mon Sep 17 00:00:00 2001 From: Zheyu Ma Date: Thu, 20 Jun 2024 16:02:39 +0200 Subject: [PATCH 1821/2884] hw/gpio/aspeed: Add reg_table_count to AspeedGPIOClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASan detected a global-buffer-overflow error in the aspeed_gpio_read() function. This issue occurred when reading beyond the bounds of the reg_table. To enhance the safety and maintainability of the Aspeed GPIO code, this commit introduces a reg_table_count member to the AspeedGPIOClass structure. This change ensures that the size of the GPIO register table is explicitly tracked and initialized, reducing the risk of errors if new register tables are introduced in the future. Reproducer: cat << EOF | qemu-system-aarch64 -display none \ -machine accel=qtest, -m 512M -machine ast1030-evb -qtest stdio readq 0x7e780272 EOF ASAN log indicating the issue: ==2602930==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55a5da29e128 at pc 0x55a5d700dc62 bp 0x7fff096c4e90 sp 0x7fff096c4e88 READ of size 2 at 0x55a5da29e128 thread T0 #0 0x55a5d700dc61 in aspeed_gpio_read hw/gpio/aspeed_gpio.c:564:14 #1 0x55a5d933f3ab in memory_region_read_accessor system/memory.c:445:11 #2 0x55a5d92fba40 in access_with_adjusted_size system/memory.c:573:18 #3 0x55a5d92f842c in memory_region_dispatch_read1 system/memory.c:1426:16 #4 0x55a5d92f7b68 in memory_region_dispatch_read system/memory.c:1459:9 #5 0x55a5d9376ad1 in flatview_read_continue_step system/physmem.c:2836:18 #6 0x55a5d9376399 in flatview_read_continue system/physmem.c:2877:19 #7 0x55a5d93775b8 in flatview_read system/physmem.c:2907:12 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2355 Signed-off-by: Zheyu Ma Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Andrew Jeffery --- hw/gpio/aspeed_gpio.c | 17 +++++++++++++++++ include/hw/gpio/aspeed_gpio.h | 1 + 2 files changed, 18 insertions(+) diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index c1781e2ba3..6474bb8de5 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -559,6 +559,12 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size) return debounce_value; } + if (idx >= agc->reg_table_count) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: idx 0x%" PRIx64 " out of bounds\n", + __func__, idx); + return 0; + } + reg = &agc->reg_table[idx]; if (reg->set_idx >= agc->nr_gpio_sets) { qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" @@ -785,6 +791,12 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, return; } + if (idx >= agc->reg_table_count) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: idx 0x%" PRIx64 " out of bounds\n", + __func__, idx); + return; + } + reg = &agc->reg_table[idx]; if (reg->set_idx >= agc->nr_gpio_sets) { qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" @@ -1117,6 +1129,7 @@ static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 216; agc->nr_gpio_sets = 7; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; } static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) @@ -1127,6 +1140,7 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 228; agc->nr_gpio_sets = 8; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; } static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data) @@ -1137,6 +1151,7 @@ static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 208; agc->nr_gpio_sets = 7; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; } static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) @@ -1147,6 +1162,7 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 36; agc->nr_gpio_sets = 2; agc->reg_table = aspeed_1_8v_gpios; + agc->reg_table_count = GPIO_1_8V_REG_ARRAY_SIZE; } static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) @@ -1157,6 +1173,7 @@ static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 151; agc->nr_gpio_sets = 6; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; } static const TypeInfo aspeed_gpio_info = { diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h index 904eecf62c..90a12ae318 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -75,6 +75,7 @@ struct AspeedGPIOClass { uint32_t nr_gpio_pins; uint32_t nr_gpio_sets; const AspeedGPIOReg *reg_table; + unsigned reg_table_count; }; struct AspeedGPIOState { From 56a37eda93edafabcc4de0184b88d082ede6dec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 24 Jun 2024 19:29:18 +0200 Subject: [PATCH 1822/2884] aspeed: Deprecate the tacoma-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tacoma-bmc machine was a board including an AST2600 SoC based BMC and a witherspoon like OpenPOWER system. It was used for bring up of the AST2600 SoC in labs. It can be easily replaced by the rainier-bmc machine which is part of a real product offering. Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé --- docs/about/deprecated.rst | 8 ++++++++ hw/arm/aspeed.c | 2 ++ 2 files changed, 10 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index ff3da68208..5d9e4d8de7 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -256,6 +256,14 @@ images are not available, OpenWRT dropped support in 2019, U-Boot in 2017, Linux also is dropping support in 2024. It is time to let go of this ancient hardware and focus on newer CPUs and platforms. +Arm ``tacoma-bmc`` machine (since 9.1) +'''''''''''''''''''''''''''''''''''''''' + +The ``tacoma-bmc`` machine was a board including an AST2600 SoC based +BMC and a witherspoon like OpenPOWER system. It was used for bring up +of the AST2600 SoC in labs. It can be easily replaced by the +``rainier-bmc`` machine which is a real product. + Backend options --------------- diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 40dc0e4c76..53a4f665d0 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1379,6 +1379,8 @@ static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) amc->i2c_init = witherspoon_bmc_i2c_init; /* Same board layout */ mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); + + mc->deprecation_reason = "Please use the similar 'rainier-bmc' machine"; }; static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) From 61578d1e806d7271813c870e31160a7b21eab508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 25 Jun 2024 08:37:43 +0200 Subject: [PATCH 1823/2884] aspeed/sdmc: Check RAM size value at realize time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RAM size of the SDMC device is validated for the SoC and set when the Aspeed machines are initialized and then later used by several SoC implementations. However, the SDMC model never checks that the RAM size has been actually set before being used. Do that at realize. Signed-off-by: Cédric Le Goater Reviewed-by: Jamin_lin < jamin_lin@aspeedtech.com> --- hw/misc/aspeed_sdmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 93e2e29ead..44da085e10 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -271,6 +271,12 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); assert(asc->max_ram_size < 4 * GiB || asc->is_bus64bit); + + if (!s->ram_size) { + error_setg(errp, "RAM size is not set"); + return; + } + s->max_ram_size = asc->max_ram_size; memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s, From 5c065dfc71b84179a9aec2d283bb57b5a5675b0b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 25 Jun 2024 15:07:39 +0800 Subject: [PATCH 1824/2884] aspeed/soc: Fix possible divide by zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reports a possible DIVIDE_BY_ZERO issue regarding the "ram_size" object property. This can not happen because RAM has predefined valid sizes per SoC. Nevertheless, add a test to close the issue. Fixes: Coverity CID 1547113 Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater [ clg: Rewrote commit log ] Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index b6876b4862..18e6a8b10c 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -211,6 +211,8 @@ static void aspeed_ram_capacity_write(void *opaque, hwaddr addr, uint64_t data, ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", &error_abort); + assert(ram_size > 0); + /* * Emulate ddr capacity hardware behavior. * If writes the data to the address which is beyond the ram size, From 50c527b9ef58086dc377a0fa5e3053d16fc5728e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 25 Jun 2024 15:07:40 +0800 Subject: [PATCH 1825/2884] aspeed/sdmc: Remove extra R_MAIN_STATUS case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reports that the newly added 'case R_MAIN_STATUS' is DEADCODE because it can not be reached. This is because R_MAIN_STATUS is handled before in the "Unprotected registers" switch statement. Remove it. Fixes: Coverity CID 1547112 Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater [ clg: Rewrote commit log ] Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_sdmc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 44da085e10..ebf139cb5c 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -595,7 +595,6 @@ static void aspeed_2700_sdmc_write(AspeedSDMCState *s, uint32_t reg, case R_INT_STATUS: case R_INT_CLEAR: case R_INT_MASK: - case R_MAIN_STATUS: case R_ERR_STATUS: case R_ECC_FAIL_STATUS: case R_ECC_FAIL_ADDR: From 5b0961f7ad6790f473623703834351b6e43fbaa6 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 19 Jun 2024 18:01:01 +0800 Subject: [PATCH 1826/2884] hw/net:ftgmac100: fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl Test command: ./scripts/checkpatch.pl --no-tree -f hw/net/ftgmac100.c Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater --- hw/net/ftgmac100.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 74b6c3d9a7..25e4c0cd5b 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -238,7 +238,8 @@ typedef struct { */ #define FTGMAC100_MAX_FRAME_SIZE 9220 -/* Limits depending on the type of the frame +/* + * Limits depending on the type of the frame * * 9216 for Jumbo frames (+ 4 for VLAN) * 1518 for other frames (+ 4 for VLAN) @@ -533,8 +534,10 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, break; } - /* record transmit flags as they are valid only on the first - * segment */ + /* + * record transmit flags as they are valid only on the first + * segment + */ if (bd.des0 & FTGMAC100_TXDES0_FTS) { flags = bd.des1; } @@ -639,7 +642,8 @@ static bool ftgmac100_can_receive(NetClientState *nc) */ static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) { - /* Polling times : + /* + * Polling times : * * Speed TIME_SEL=0 TIME_SEL=1 * From c1991c0984243850faa267735db6f624f2aea06a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 19 Jun 2024 16:44:21 +0200 Subject: [PATCH 1827/2884] hw/intc/s390_flic: Fix interrupt controller migration on s390x with TCG Migration of a s390x guest with TCG was long known to be very unstable, so the tests in tests/qtest/migration-test.c are disabled if running with TCG instead of KVM. Nicholas Piggin did a great analysis of the problem: "The flic pending state is not migrated, so if the machine is migrated while an interrupt is pending, it can be lost. This shows up in qtest migration test, an extint is pending (due to console writes?) and the CPU waits via s390_cpu_set_psw and expects the interrupt to wake it. However when the flic pending state is lost, s390_cpu_has_int returns false, so s390_cpu_exec_interrupt falls through to halting again." Thus let's finally migrate the pending state, and to be on the safe side, also the other state variables of the QEMUS390FLICState structure. Message-ID: <20240619144421.261342-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/intc/s390_flic.c | 75 ++++++++++++++++++++++++++++++++++-- hw/s390x/s390-virtio-ccw.c | 5 +++ include/hw/s390x/s390_flic.h | 1 + 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 6771645699..a91a4a47e8 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -361,15 +361,77 @@ bool ais_needed(void *opaque) return s->ais_supported; } +static bool ais_needed_v(void *opaque, int version_id) +{ + return ais_needed(opaque); +} + +static bool qemu_s390_flic_full_state_needed(void *opaque) +{ + QEMUS390FLICState *s = opaque; + + return s->migrate_all_state; +} + +static bool qemu_s390_flic_state_needed(void *opaque) +{ + return ais_needed(opaque) || qemu_s390_flic_full_state_needed(opaque); +} + +static const VMStateDescription vmstate_qemu_s390_flic_io = { + .name = "qemu-s390-flic-io", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16(id, QEMUS390FlicIO), + VMSTATE_UINT16(nr, QEMUS390FlicIO), + VMSTATE_UINT32(parm, QEMUS390FlicIO), + VMSTATE_UINT32(word, QEMUS390FlicIO), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_qemu_s390_flic_full = { + .name = "qemu-s390-flic-full", + .version_id = 1, + .minimum_version_id = 1, + .needed = qemu_s390_flic_full_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(pending, QEMUS390FLICState), + VMSTATE_UINT32(service_param, QEMUS390FLICState), + VMSTATE_QLIST_V(io[0], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[1], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[2], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[3], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[4], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[5], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[6], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[7], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription qemu_s390_flic_vmstate = { .name = "qemu-s390-flic", .version_id = 1, .minimum_version_id = 1, - .needed = ais_needed, + .needed = qemu_s390_flic_state_needed, .fields = (const VMStateField[]) { - VMSTATE_UINT8(simm, QEMUS390FLICState), - VMSTATE_UINT8(nimm, QEMUS390FLICState), + VMSTATE_UINT8_TEST(simm, QEMUS390FLICState, ais_needed_v), + VMSTATE_UINT8_TEST(nimm, QEMUS390FLICState, ais_needed_v), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_qemu_s390_flic_full, + NULL } }; @@ -383,11 +445,18 @@ static void qemu_s390_flic_instance_init(Object *obj) } } +static Property qemu_s390_flic_properties[] = { + DEFINE_PROP_BOOL("migrate-all-state", QEMUS390FLICState, + migrate_all_state, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); + device_class_set_props(dc, qemu_s390_flic_properties); dc->reset = qemu_s390_flic_reset; dc->vmsd = &qemu_s390_flic_vmstate; fsc->register_io_adapter = qemu_s390_register_io_adapter; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index cd063f8b64..f87ca36264 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -863,8 +863,13 @@ static void ccw_machine_9_0_instance_options(MachineState *machine) static void ccw_machine_9_0_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_QEMU_S390_FLIC, "migrate-all-state", "off", }, + }; + ccw_machine_9_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_CCW_MACHINE(9_0, "9.0", false); diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 382d9833f1..4d66c5e42e 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -116,6 +116,7 @@ struct QEMUS390FLICState { uint8_t simm; uint8_t nimm; QLIST_HEAD(, QEMUS390FlicIO) io[8]; + bool migrate_all_state; }; uint32_t qemu_s390_flic_dequeue_service(QEMUS390FLICState *flic); From ada9311de31720c1b347e42582c0540914f2ffa6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 25 May 2024 23:12:39 +1000 Subject: [PATCH 1828/2884] tests/qtest/migration-test: enable on s390x with TCG s390x with TCG is more stable now. Enable it. Signed-off-by: Nicholas Piggin Message-Id: <20240525131241.378473-3-npiggin@gmail.com> Reviewed-by: Prasad Pandit [thuth: Added "with TCG" to the commit message] Signed-off-by: Thomas Huth --- tests/qtest/migration-test.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 571fc1334c..70b606b888 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -3823,16 +3823,6 @@ int main(int argc, char **argv) test_vmstate_checker_script); #endif - /* - * On s390x with TCG, migration is observed to hang due to the 'pending' - * state of the flic interrupt controller not being migrated or - * reconstructed post-migration. Disable it until the problem is resolved. - */ - if (g_str_equal(arch, "s390x") && !has_kvm) { - g_test_message("Skipping tests: s390x host with KVM is required"); - goto test_add_done; - } - if (is_x86) { migration_test_add("/migration/precopy/unix/suspend/live", test_precopy_unix_suspend_live); @@ -4036,8 +4026,6 @@ int main(int argc, char **argv) test_vcpu_dirty_limit); } -test_add_done: - ret = g_test_run(); g_assert_cmpint(ret, ==, 0); From 6a414c01100359b799498a2a9f4f3883834df86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 08:57:38 +0200 Subject: [PATCH 1829/2884] hw/sd/sdcard: Deprecate support for spec v1.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use the v2.00 spec by default since commit 2f0939c234 ("sdcard: Add a 'spec_version' property, default to Spec v2.00"). Time to deprecate the v1.10 which doesn't bring much, and is not tested. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240627071040.36190-2-philmd@linaro.org> --- docs/about/deprecated.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index ff3da68208..02cdef14aa 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -362,6 +362,12 @@ recommending to switch to their stable counterparts: - "Zve64f" should be replaced with "zve64f" - "Zve64d" should be replaced with "zve64d" +``-device sd-card,spec_version=1`` (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SD physical layer specification v2.00 supersedes the v1.10 one. +v2.00 is the default since QEMU 3.0.0. + Block device options '''''''''''''''''''' From 702ff29c8ff397e6f3aa7e46f30f9ccdd74fb492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jun 2024 01:44:28 +0200 Subject: [PATCH 1830/2884] hw/sd/sdcard: Track last command used to help logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The command is selected on the I/O lines, and further processing might be done on the DAT lines via the sd_read_byte() and sd_write_byte() handlers. Since these methods can't distinct between normal and APP commands, keep the name of the current command in the SDState and use it in the DAT handlers. This fixes a bug that all normal commands were displayed as APP commands. Fixes: 2ed61fb57b ("sdcard: Display command name when tracing CMD/ACMD") Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-4-philmd@linaro.org> --- hw/sd/sd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a48010cfc1..aa011fc892 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -133,6 +133,7 @@ struct SDState { uint32_t pwd_len; uint8_t function_group[6]; uint8_t current_cmd; + const char *last_cmd_name; /* True if we will handle the next command as an ACMD. Note that this does * *not* track the APP_CMD status bit! */ @@ -1154,12 +1155,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) uint16_t rca; uint64_t addr; + sd->last_cmd_name = sd_cmd_name(req.cmd); /* CMD55 precedes an ACMD, so we are not interested in tracing it. * However there is no ACMD55, so we want to trace this particular case. */ if (req.cmd != 55 || sd->expecting_acmd) { trace_sdcard_normal_command(sd_proto(sd)->name, - sd_cmd_name(req.cmd), req.cmd, + sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); } @@ -1620,7 +1622,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { - trace_sdcard_app_command(sd_proto(sd)->name, sd_acmd_name(req.cmd), + sd->last_cmd_name = sd_acmd_name(req.cmd); + trace_sdcard_app_command(sd_proto(sd)->name, sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; @@ -1913,7 +1916,7 @@ void sd_write_byte(SDState *sd, uint8_t value) return; trace_sdcard_write_data(sd_proto(sd)->name, - sd_acmd_name(sd->current_cmd), + sd->last_cmd_name, sd->current_cmd, value); switch (sd->current_cmd) { case 24: /* CMD24: WRITE_SINGLE_BLOCK */ @@ -2069,7 +2072,7 @@ uint8_t sd_read_byte(SDState *sd) io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; trace_sdcard_read_data(sd_proto(sd)->name, - sd_acmd_name(sd->current_cmd), + sd->last_cmd_name, sd->current_cmd, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ @@ -2214,6 +2217,7 @@ static void sd_instance_init(Object *obj) { SDState *sd = SD_CARD(obj); + sd->last_cmd_name = "UNSET"; sd->enable = true; sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd); } From c159de66f29f7f0e6f7739d8722b54e0cb3cb971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Jun 2024 20:26:47 +0200 Subject: [PATCH 1831/2884] hw/sd/sdcard: Trace block offset in READ/WRITE data accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Useful to detect out of bound accesses. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-5-philmd@linaro.org> --- hw/sd/sd.c | 4 ++-- hw/sd/trace-events | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index aa011fc892..bed5966ea7 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1917,7 +1917,7 @@ void sd_write_byte(SDState *sd, uint8_t value) trace_sdcard_write_data(sd_proto(sd)->name, sd->last_cmd_name, - sd->current_cmd, value); + sd->current_cmd, sd->data_offset, value); switch (sd->current_cmd) { case 24: /* CMD24: WRITE_SINGLE_BLOCK */ sd->data[sd->data_offset ++] = value; @@ -2073,7 +2073,7 @@ uint8_t sd_read_byte(SDState *sd) trace_sdcard_read_data(sd_proto(sd)->name, sd->last_cmd_name, - sd->current_cmd, io_len); + sd->current_cmd, sd->data_offset, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ ret = sd->data[sd->data_offset ++]; diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 724365efc3..0eee98a646 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -52,8 +52,8 @@ sdcard_lock(void) "" sdcard_unlock(void) "" sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" -sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x" -sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t length) "%s %20s/ CMD%02d len %" PRIu32 +sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint8_t value) "%s %20s/ CMD%02d ofs %"PRIu32" value 0x%02x" +sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint32_t length) "%s %20s/ CMD%02d ofs %"PRIu32" len %" PRIu32 sdcard_set_voltage(uint16_t millivolts) "%u mV" # pxa2xx_mmci.c From 2ec83d679e1900aee7ac38f2a9acd6debae05557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 09:43:26 +0200 Subject: [PATCH 1832/2884] hw/sd/sdcard: Trace requested address computed by sd_req_get_address() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Luc Michel Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-6-philmd@linaro.org> --- hw/sd/sd.c | 9 +++++++-- hw/sd/trace-events | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index bed5966ea7..396185f240 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -608,10 +608,15 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) static uint64_t sd_req_get_address(SDState *sd, SDRequest req) { + uint64_t addr; + if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { - return (uint64_t) req.arg << HWBLOCK_SHIFT; + addr = (uint64_t) req.arg << HWBLOCK_SHIFT; + } else { + addr = req.arg; } - return req.arg; + trace_sdcard_req_addr(req.arg, addr); + return addr; } static inline uint64_t sd_addr_to_wpnum(uint64_t addr) diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 0eee98a646..43eaeba149 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -50,6 +50,7 @@ sdcard_ejected(void) "" sdcard_erase(uint32_t first, uint32_t last) "addr first 0x%" PRIx32" last 0x%" PRIx32 sdcard_lock(void) "" sdcard_unlock(void) "" +sdcard_req_addr(uint32_t req_arg, uint64_t addr) "req 0x%" PRIx32 " addr 0x%" PRIx64 sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint8_t value) "%s %20s/ CMD%02d ofs %"PRIu32" value 0x%02x" From eefd26b8766d1db527946070510970af07ca033f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 22:37:53 +0900 Subject: [PATCH 1833/2884] tests/qtest: Use qtest_add_data_func_full() A test function may not be executed depending on the test command line so it is wrong to free data with a test function. Use qtest_add_data_func_full() to register a function to free data. Signed-off-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Message-ID: <20240627-san-v2-10-750bb0946dbd@daynix.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/qtest/device-introspect-test.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index 5b0ffe43f5..587da59623 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -266,7 +266,6 @@ static void test_device_intro_concrete(const void *args) qobject_unref(types); qtest_quit(qts); - g_free((void *)args); } static void test_abstract_interfaces(void) @@ -310,12 +309,12 @@ static void add_machine_test_case(const char *mname) path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); args = g_strdup_printf("-M %s", mname); - qtest_add_data_func(path, args, test_device_intro_concrete); + qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free); g_free(path); path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); args = g_strdup_printf("-nodefaults -M %s", mname); - qtest_add_data_func(path, args, test_device_intro_concrete); + qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free); g_free(path); } @@ -330,7 +329,7 @@ int main(int argc, char **argv) qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); if (g_test_quick()) { qtest_add_data_func("device/introspect/concrete/defaults/none", - g_strdup(common_args), test_device_intro_concrete); + common_args, test_device_intro_concrete); } else { qtest_cb_for_every_machine(add_machine_test_case, true); } From f48b7a4b69b55ed0c9e74013822ee3b16d8d7055 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 22:37:54 +0900 Subject: [PATCH 1834/2884] tests/qtest: Free unused QMP response This fixes LeakSanitizer warnings. Signed-off-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Message-ID: <20240627-san-v2-11-750bb0946dbd@daynix.com> Signed-off-by: Thomas Huth --- tests/qtest/libqtest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index c7f6897d78..ca46b1f8d0 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -749,6 +749,8 @@ QDict *qtest_qmp_receive(QTestState *s) response, s->eventData)) { /* Stash the event for a later consumption */ s->pending_events = g_list_append(s->pending_events, response); + } else { + qobject_unref(response); } } } From 0d626d12eb3e550a1ac0dd2e505042ed1fb4d50b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 22:37:55 +0900 Subject: [PATCH 1835/2884] tests/qtest: Free old machine variable name This fixes LeakSanitizer warnings. Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Reviewed-by: Michael S. Tsirkin Message-ID: <20240627-san-v2-12-750bb0946dbd@daynix.com> Signed-off-by: Thomas Huth --- tests/qtest/libqtest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index ca46b1f8d0..1326e34291 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1514,6 +1514,7 @@ static struct MachInfo *qtest_get_machines(const char *var) int idx; if (g_strcmp0(qemu_var, var)) { + g_free(qemu_var); qemu_var = g_strdup(var); /* new qemu, clear the cache */ From dcc3e1218d463484e703a87cd754f4930402d30d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 22:37:57 +0900 Subject: [PATCH 1836/2884] tests/qtest: Free paths This fixes LeakSanitizer warnings. Signed-off-by: Akihiko Odaki Reviewed-by: Michael S. Tsirkin Message-ID: <20240627-san-v2-14-750bb0946dbd@daynix.com> Signed-off-by: Thomas Huth --- tests/qtest/qos-test.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index 5da4091ec3..114f6bef27 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -33,7 +33,6 @@ static char *old_path; - /** * qos_set_machines_devices_available(): sets availability of qgraph * machines and devices. @@ -191,6 +190,12 @@ static void subprocess_run_one_test(const void *arg) g_test_trap_assert_passed(); } +static void destroy_pathv(void *arg) +{ + g_free(((char **)arg)[0]); + g_free(arg); +} + /* * in this function, 2 path will be built: * path_str, a one-string path (ex "pc/i440FX-pcihost/...") @@ -295,10 +300,13 @@ static void walk_path(QOSGraphNode *orig_path, int len) if (path->u.test.subprocess) { gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", qtest_get_arch(), path_str); - qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test); - g_test_add_data_func(subprocess_path, path_vec, run_one_test); + qtest_add_data_func_full(path_str, subprocess_path, + subprocess_run_one_test, g_free); + g_test_add_data_func_full(subprocess_path, path_vec, + run_one_test, destroy_pathv); } else { - qtest_add_data_func(path_str, path_vec, run_one_test); + qtest_add_data_func_full(path_str, path_vec, + run_one_test, destroy_pathv); } g_free(path_str); From 4ab2546265c2133395c163bbdf2bea2ea30e3989 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 22:37:58 +0900 Subject: [PATCH 1837/2884] tests/qtest: Free GThread These GThreads are never referenced. Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Reviewed-by: Michael S. Tsirkin Message-ID: <20240627-san-v2-15-750bb0946dbd@daynix.com> Signed-off-by: Thomas Huth --- tests/qtest/vhost-user-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index d4e437265f..929af5c183 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -928,7 +928,7 @@ static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) { TestServer *s = test_server_new("reconnect", arg); - g_thread_new("connect", connect_thread, s); + g_thread_unref(g_thread_new("connect", connect_thread, s)); append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); s->vu_ops->append_opts(s, cmd_line, ",server=on"); @@ -965,7 +965,7 @@ static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) s->test_fail = true; - g_thread_new("connect", connect_thread, s); + g_thread_unref(g_thread_new("connect", connect_thread, s)); append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); s->vu_ops->append_opts(s, cmd_line, ",server=on"); @@ -980,7 +980,7 @@ static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) s->test_flags = TEST_FLAGS_DISCONNECT; - g_thread_new("connect", connect_thread, s); + g_thread_unref(g_thread_new("connect", connect_thread, s)); append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); s->vu_ops->append_opts(s, cmd_line, ",server=on"); From 5f79abcf74d32d7b9cef3481df2c354fc0cad540 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 20 Jun 2024 15:57:32 +0200 Subject: [PATCH 1838/2884] docs: add precision about capstone for execlog plugin Some people are wondering why they get an empty string as disassembly. Most of the time, they configured QEMU without Capstone support. Let's document this behaviour to help users. Signed-off-by: Alexandre Iooss Reviewed-by: Pierrick Bouvier Message-ID: <20240620135731.977377-1-erdnaxe@crans.org> Signed-off-by: Thomas Huth --- docs/devel/tcg-plugins.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 9cc09d8c3d..f7d7b9e3a4 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -539,7 +539,9 @@ which will output an execution trace following this structure:: 0, 0xd34, 0xf9c8f000, "bl #0x10c8" 0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM -the output can be filtered to only track certain instructions or +Please note that you need to configure QEMU with Capstone support to get disassembly. + +The output can be filtered to only track certain instructions or addresses using the ``ifilter`` or ``afilter`` options. You can stack the arguments if required:: From 999c870e9ebc9a0b742d58b884a3bab756ffd777 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 1 Jul 2024 22:01:08 +0200 Subject: [PATCH 1839/2884] hw/s390x: Attach default virtio-net devices to the /machine/virtual-css-bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial virtio-net-ccw devices currently do not have a proper parent in the QOM tree, so they show up under /machine/unattached - which is somewhat ugly. Let's attach them to /machine/virtual-css-bridge/virtual-css instead. Message-ID: <20240701200108.154271-1-thuth@redhat.com> Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index f87ca36264..c1edbd9131 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -216,8 +216,11 @@ static void s390_init_ipl_dev(const char *kernel_filename, static void s390_create_virtio_net(BusState *bus, const char *name) { DeviceState *dev; + int cnt = 0; while ((dev = qemu_create_nic_device(name, true, "virtio"))) { + g_autofree char *childname = g_strdup_printf("%s[%d]", name, cnt++); + object_property_add_child(OBJECT(bus), childname, OBJECT(dev)); qdev_realize_and_unref(dev, bus, &error_fatal); } } From 99a28bd50fff03ef0bc2570ed3082c1e7e1be0bf Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 9 Apr 2024 09:58:54 +0300 Subject: [PATCH 1840/2884] tests/avocado: add hotplug_blk test Introduce a test, that checks that plug/unplug of virtio-blk device works. (the test is developed by copying hotplug_cpu.py, so keep original copyright) Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Thomas Huth Message-ID: <20240409065854.366856-1-vsementsov@yandex-team.ru> Signed-off-by: Thomas Huth --- tests/avocado/hotplug_blk.py | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/avocado/hotplug_blk.py diff --git a/tests/avocado/hotplug_blk.py b/tests/avocado/hotplug_blk.py new file mode 100644 index 0000000000..5dc30f6616 --- /dev/null +++ b/tests/avocado/hotplug_blk.py @@ -0,0 +1,69 @@ +# Functional test that hotplugs a virtio blk disk and checks it on a Linux +# guest +# +# Copyright (c) 2021 Red Hat, Inc. +# Copyright (c) Yandex +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import time + +from avocado_qemu import LinuxTest + + +class HotPlug(LinuxTest): + def blockdev_add(self) -> None: + self.vm.cmd('blockdev-add', **{ + 'driver': 'null-co', + 'size': 1073741824, + 'node-name': 'disk' + }) + + def assert_vda(self) -> None: + self.ssh_command('test -e /sys/block/vda') + + def assert_no_vda(self) -> None: + with self.assertRaises(AssertionError): + self.assert_vda() + + def plug(self) -> None: + args = { + 'driver': 'virtio-blk-pci', + 'drive': 'disk', + 'id': 'virtio-disk0', + 'bus': 'pci.1', + 'addr': 1 + } + + self.assert_no_vda() + self.vm.cmd('device_add', args) + try: + self.assert_vda() + except AssertionError: + time.sleep(1) + self.assert_vda() + + def unplug(self) -> None: + self.vm.cmd('device_del', id='virtio-disk0') + + self.vm.event_wait('DEVICE_DELETED', 1.0, + match={'data': {'device': 'virtio-disk0'}}) + + self.assert_no_vda() + + def test(self) -> None: + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + :avocado: tags=accel:kvm + """ + self.require_accelerator('kvm') + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') + + self.launch_and_wait() + self.blockdev_add() + + self.plug() + self.unplug() From 5e8881c2d27db8a1e3924d604f0507b1186fb3a8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 24 Jun 2024 11:48:07 +0200 Subject: [PATCH 1841/2884] .travis.yml: Install python3-tomli in all build jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 1f97715c83 ('Revert "python: use vendored tomli"') this package is a hard requirement for compiling QEMU, so install it now in all Travis jobs, too. Message-ID: <20240624094807.182313-1-thuth@redhat.com> Acked-by: Alex Bennée Signed-off-by: Thomas Huth --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index cef0308952..8fc1ae0cf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,6 +106,7 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: @@ -141,6 +142,7 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: @@ -175,6 +177,7 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: @@ -215,6 +218,7 @@ jobs: - libzstd-dev - nettle-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: @@ -231,6 +235,7 @@ jobs: - ninja-build - flex - bison + - python3-tomli env: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --disable-system" @@ -263,6 +268,7 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli env: - TEST_CMD="make check-unit" - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools From e3e2708fee10e6df413c36a71b100c59710e727e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 21 Jun 2024 10:24:16 +0200 Subject: [PATCH 1842/2884] pc-bios/s390-ccw: Remove duplicated LDFLAGS The -Wl,-pie and -nostdlib flags are added to LDFLAGS twice. Merge the two lines to get rid of the duplicates. Message-ID: <20240621082422.136217-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index acfcd1e71a..6207911b53 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -40,7 +40,7 @@ EXTRA_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables EXTRA_CFLAGS += -msoft-float EXTRA_CFLAGS += -std=gnu99 -LDFLAGS += -Wl,-pie -nostdlib +LDFLAGS += -Wl,-pie -nostdlib -z noexecstack cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null cc-option = if $(call cc-test, $1); then \ @@ -55,8 +55,6 @@ config-cc.mak: Makefile $(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak -include config-cc.mak -LDFLAGS += -Wl,-pie -nostdlib -z noexecstack - build-all: s390-ccw.img s390-netboot.img s390-ccw.elf: $(OBJECTS) From c239084f5b2b13e74716071a5e029329a52e643a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Jun 2024 04:48:12 +0200 Subject: [PATCH 1843/2884] hw/sd/sdcard: Restrict SWITCH_FUNCTION to sd_transfer_state (CMD6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SWITCH_FUNCTION is only allowed in TRANSFER state (See 4.8 "Card State Transition Table). Fixes: a1bb27b1e9 ("Initial SD card emulation") Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-13-philmd@linaro.org> --- hw/sd/sd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 396185f240..b5d002e6d7 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1204,6 +1204,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (sd->mode != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd_function_switch(sd, req.arg); sd->state = sd_sendingdata_state; sd->data_start = 0; From e55cbe727b07f1c39f57c1af842ff2fca9bc2d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jun 2024 15:17:55 +0200 Subject: [PATCH 1844/2884] hw/sd/sdcard: Send WRITE_PROT bits MSB first (CMD30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per sections 3.6.1 (SD Bus Protocol) and 7.3.2 (Responses): In the CMD line the Most Significant Bit is transmitted first. Use the stl_be_p() helper to store the value in big-endian. Fixes: a1bb27b1e9 ("Initial SD card emulation") Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Peter Maydell Message-Id: <20240628070216.92609-8-philmd@linaro.org> --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b5d002e6d7..1e9530f9ae 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1508,7 +1508,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } sd->state = sd_sendingdata_state; - *(uint32_t *) sd->data = sd_wpbits(sd, req.arg); + stl_be_p(sd->data, sd_wpbits(sd, req.arg)); sd->data_start = addr; sd->data_offset = 0; return sd_r1; From 7028187bd003f62e11f41cc8c6a26c1c12755cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jun 2024 15:28:10 +0200 Subject: [PATCH 1845/2884] hw/sd/sdcard: Send NUM_WR_BLOCKS bits MSB first (ACMD22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per sections 3.6.1 (SD Bus Protocol), 4.3.4 "Data Write" and 7.3.2 (Responses): In the CMD line the Most Significant Bit is transmitted first. Use the stl_be_p() helper to store the value in big-endian. Fixes: a1bb27b1e9 ("Initial SD card emulation") Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Peter Maydell Message-Id: <20240628070216.92609-9-philmd@linaro.org> --- hw/sd/sd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1e9530f9ae..54bb0ff1c9 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1669,8 +1669,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ switch (sd->state) { case sd_transfer_state: - *(uint32_t *) sd->data = sd->blk_written; - + stl_be_p(sd->data, sd->blk_written); sd->state = sd_sendingdata_state; sd->data_start = 0; sd->data_offset = 0; From 854f5bc655aaca38d3e05b795c500c4b27cfa070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Jun 2024 16:41:10 +0200 Subject: [PATCH 1846/2884] hw/sd/sdcard: Use READY_FOR_DATA definition instead of magic value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-10-philmd@linaro.org> --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 54bb0ff1c9..04ca895645 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -559,7 +559,7 @@ FIELD(CSR, OUT_OF_RANGE, 31, 1) static void sd_set_cardstatus(SDState *sd) { - sd->card_status = 0x00000100; + sd->card_status = READY_FOR_DATA; } static void sd_set_sdstatus(SDState *sd) From 26be1ceee5c5d0b01ebadc271807e6fb870670e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Jun 2024 20:18:46 +0200 Subject: [PATCH 1847/2884] hw/sd/sdcard: Assign SDCardStates enum values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDCardStates enum values are specified, so assign them correspondingly. It will be useful later when we add states from later specs, which might not be continuous. See CURRENT_STATE bits in section 4.10.1 "Card Status". Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-11-philmd@linaro.org> --- hw/sd/sd.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 04ca895645..824cb47856 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -75,16 +75,16 @@ enum SDCardModes { }; enum SDCardStates { - sd_inactive_state = -1, - sd_idle_state = 0, - sd_ready_state, - sd_identification_state, - sd_standby_state, - sd_transfer_state, - sd_sendingdata_state, - sd_receivingdata_state, - sd_programming_state, - sd_disconnect_state, + sd_inactive_state = -1, + sd_idle_state = 0, + sd_ready_state = 1, + sd_identification_state = 2, + sd_standby_state = 3, + sd_transfer_state = 4, + sd_sendingdata_state = 5, + sd_receivingdata_state = 6, + sd_programming_state = 7, + sd_disconnect_state = 8, }; typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); From 257e36c9e602b5d10927292d38a9c85c5738683a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Jun 2024 05:50:16 +0200 Subject: [PATCH 1848/2884] hw/sd/sdcard: Simplify sd_inactive_state handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Card entering sd_inactive_state powers off, and won't respond anymore. Handle that once when entering sd_do_command(). Remove condition always true in sd_cmd_GO_IDLE_STATE(). Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-12-philmd@linaro.org> --- hw/sd/sd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 824cb47856..30c1d299d4 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1078,10 +1078,8 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) /* CMD0 */ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) { - if (sd->state != sd_inactive_state) { - sd->state = sd_idle_state; - sd_reset(DEVICE(sd)); - } + sd->state = sd_idle_state; + sd_reset(DEVICE(sd)); return sd_is_spi(sd) ? sd_r1 : sd_r0; } @@ -1580,7 +1578,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (sd->state) { case sd_ready_state: case sd_identification_state: - case sd_inactive_state: return sd_illegal; case sd_idle_state: if (rca) { @@ -1801,6 +1798,11 @@ int sd_do_command(SDState *sd, SDRequest *req, return 0; } + if (sd->state == sd_inactive_state) { + rtype = sd_illegal; + goto send_response; + } + if (sd_req_crc_validate(req)) { sd->card_status |= COM_CRC_ERROR; rtype = sd_illegal; From 12160bebd4ed928fb4decdde4c4adc6af044839f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Jun 2024 20:19:10 +0200 Subject: [PATCH 1849/2884] hw/sd/sdcard: Add direct reference to SDProto in SDState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep direct reference to SDProto in SDState, remove then unnecessary sd_proto(). Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-14-philmd@linaro.org> --- hw/sd/sd.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 30c1d299d4..d06e670024 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -116,6 +116,8 @@ struct SDState { uint8_t spec_version; BlockBackend *blk; + const SDProto *proto; + /* Runtime changeables */ uint32_t mode; /* current card mode, one of SDCardModes */ @@ -152,18 +154,11 @@ struct SDState { static void sd_realize(DeviceState *dev, Error **errp); -static const struct SDProto *sd_proto(SDState *sd) -{ - SDCardClass *sc = SD_CARD_GET_CLASS(sd); - - return sc->proto; -} - static const SDProto sd_proto_spi; static bool sd_is_spi(SDState *sd) { - return sd_proto(sd) == &sd_proto_spi; + return sd->proto == &sd_proto_spi; } static const char *sd_version_str(enum SDPhySpecificationVersion version) @@ -1041,7 +1036,7 @@ static bool address_in_range(SDState *sd, const char *desc, static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong state: %s (spec %s)\n", - sd_proto(sd)->name, req.cmd, sd_state_name(sd->state), + sd->proto->name, req.cmd, sd_state_name(sd->state), sd_version_str(sd->spec_version)); return sd_illegal; @@ -1050,7 +1045,7 @@ static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong mode: %s (spec %s)\n", - sd_proto(sd)->name, req.cmd, sd_mode_name(sd->mode), + sd->proto->name, req.cmd, sd_mode_name(sd->mode), sd_version_str(sd->spec_version)); return sd_illegal; @@ -1059,7 +1054,7 @@ static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req) static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown CMD%i for spec %s\n", - sd_proto(sd)->name, req.cmd, + sd->proto->name, req.cmd, sd_version_str(sd->spec_version)); return sd_illegal; @@ -1070,7 +1065,7 @@ __attribute__((unused)) static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) { qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n", - sd_proto(sd)->name, req.cmd); + sd->proto->name, req.cmd); return sd_illegal; } @@ -1163,7 +1158,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) * However there is no ACMD55, so we want to trace this particular case. */ if (req.cmd != 55 || sd->expecting_acmd) { - trace_sdcard_normal_command(sd_proto(sd)->name, + trace_sdcard_normal_command(sd->proto->name, sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); } @@ -1182,8 +1177,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_illegal; } - if (sd_proto(sd)->cmd[req.cmd]) { - return sd_proto(sd)->cmd[req.cmd](sd, req); + if (sd->proto->cmd[req.cmd]) { + return sd->proto->cmd[req.cmd](sd, req); } switch (req.cmd) { @@ -1629,12 +1624,12 @@ static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { sd->last_cmd_name = sd_acmd_name(req.cmd); - trace_sdcard_app_command(sd_proto(sd)->name, sd->last_cmd_name, + trace_sdcard_app_command(sd->proto->name, sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; - if (sd_proto(sd)->acmd[req.cmd]) { - return sd_proto(sd)->acmd[req.cmd](sd, req); + if (sd->proto->acmd[req.cmd]) { + return sd->proto->acmd[req.cmd](sd, req); } switch (req.cmd) { @@ -1925,7 +1920,7 @@ void sd_write_byte(SDState *sd, uint8_t value) if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) return; - trace_sdcard_write_data(sd_proto(sd)->name, + trace_sdcard_write_data(sd->proto->name, sd->last_cmd_name, sd->current_cmd, sd->data_offset, value); switch (sd->current_cmd) { @@ -2081,7 +2076,7 @@ uint8_t sd_read_byte(SDState *sd) io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; - trace_sdcard_read_data(sd_proto(sd)->name, + trace_sdcard_read_data(sd->proto->name, sd->last_cmd_name, sd->current_cmd, sd->data_offset, io_len); switch (sd->current_cmd) { @@ -2226,7 +2221,9 @@ static const SDProto sd_proto_sd = { static void sd_instance_init(Object *obj) { SDState *sd = SD_CARD(obj); + SDCardClass *sc = SD_CARD_GET_CLASS(sd); + sd->proto = sc->proto; sd->last_cmd_name = "UNSET"; sd->enable = true; sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd); From cb49e2ca22b830f0ca2752118b0bd01981e02bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 26 Oct 2020 01:32:52 +0100 Subject: [PATCH 1850/2884] hw/sd/sdcard: Extract sd_blk_len() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract sd_blk_len() helper, use definitions instead of magic values. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-15-philmd@linaro.org> --- hw/sd/sd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d06e670024..18bb2f9fd8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -601,6 +601,14 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) stl_be_p(response, sd->vhs); } +static uint32_t sd_blk_len(SDState *sd) +{ + if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + return 1 << HWBLOCK_SHIFT; + } + return sd->blk_len; +} + static uint64_t sd_req_get_address(SDState *sd, SDRequest req) { uint64_t addr; @@ -2074,7 +2082,7 @@ uint8_t sd_read_byte(SDState *sd) if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) return 0x00; - io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; + io_len = sd_blk_len(sd); trace_sdcard_read_data(sd->proto->name, sd->last_cmd_name, From d45c3d7e1a3a530d39f7000e9c9259217b9a47ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 25 May 2022 09:21:22 +0200 Subject: [PATCH 1851/2884] hw/sd/sdcard: Introduce definitions for EXT_CSD register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Le Goater Signed-off-by: Cédric Le Goater Co-Developed-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20240628070216.92609-18-philmd@linaro.org> --- hw/sd/sdmmc-internal.h | 108 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index d8bf17d204..936c75cace 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -11,6 +11,114 @@ #ifndef SDMMC_INTERNAL_H #define SDMMC_INTERNAL_H +/* + * EXT_CSD Modes segment + * + * Define the configuration the Device is working in. + * These modes can be changed by the host by means of the SWITCH command. + */ +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ +#define EXT_CSD_FLUSH_CACHE 32 /* W */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W */ +#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ +#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */ +#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */ +#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */ +#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ +#define EXT_CSD_CLASS_6_CTRL 59 +#define EXT_CSD_INI_TIMEOUT_EMU 60 +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_USE_NATIVE_SECTOR 62 +#define EXT_CSD_NATIVE_SECTOR_SIZE 63 +#define EXT_CSD_VENDOR_SPECIFIC_FIELD 64 /* 64 bytes */ +#define EXT_CSD_PROGRAM_CID_CSD_DDR_SUPPORT 130 +#define EXT_CSD_PERIODIC_WAKEUP 131 +#define EXT_CSD_TCASE_SUPPORT 132 +#define EXT_CSD_SEC_BAD_BLK_MGMNT 134 +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ +#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ +#define EXT_CSD_MAX_ENH_SIZE_MULT 157 /* RO, 3 bytes */ +#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_HPI_MGMT 161 /* R/W */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ +#define EXT_CSD_BKOPS_EN 163 /* R/W */ +#define EXT_CSD_BKOPS_START 164 /* W */ +#define EXT_CSD_SANITIZE_START 165 /* W */ +#define EXT_CSD_WR_REL_PARAM 166 /* RO */ +#define EXT_CSD_WR_REL_SET 167 +#define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_USER_WP 171 +#define EXT_CSD_BOOT_WP 173 /* R/W */ +#define EXT_CSD_BOOT_WP_STATUS 174 +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_BOOT_BUS_CONDITIONS 177 +#define EXT_CSD_BOOT_CONFIG_PROT 178 +#define EXT_CSD_PART_CONFIG 179 /* R/W */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_CMD_SET_REV 189 +#define EXT_CSD_CMD_SET 191 +/* + * EXT_CSD Properties segment + * + * Define the Device capabilities, cannot be modified by the host. + */ +#define EXT_CSD_REV 192 +#define EXT_CSD_STRUCTURE 194 +#define EXT_CSD_CARD_TYPE 196 +#define EXT_CSD_DRIVER_STRENGTH 197 +#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 +#define EXT_CSD_PART_SWITCH_TIME 199 +#define EXT_CSD_PWR_CL_52_195 200 +#define EXT_CSD_PWR_CL_26_195 201 +#define EXT_CSD_PWR_CL_52_360 202 +#define EXT_CSD_PWR_CL_26_360 203 +#define EXT_CSD_SEC_CNT 212 /* 4 bytes */ +#define EXT_CSD_S_A_TIMEOUT 217 +#define EXT_CSD_S_C_VCCQ 219 +#define EXT_CSD_S_C_VCC 220 +#define EXT_CSD_REL_WR_SEC_C 222 +#define EXT_CSD_HC_WP_GRP_SIZE 221 +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 +#define EXT_CSD_ACC_SIZE 225 +#define EXT_CSD_BOOT_MULT 226 +#define EXT_CSD_BOOT_INFO 228 +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 +#define EXT_CSD_TRIM_MULT 232 +#define EXT_CSD_INI_TIMEOUT_PA 241 +#define EXT_CSD_BKOPS_STATUS 246 +#define EXT_CSD_POWER_OFF_LONG_TIME 247 +#define EXT_CSD_GENERIC_CMD6_TIME 248 +#define EXT_CSD_CACHE_SIZE 249 /* 4 bytes */ +#define EXT_CSD_EXT_SUPPORT 494 +#define EXT_CSD_LARGE_UNIT_SIZE_M1 495 +#define EXT_CSD_CONTEXT_CAPABILITIES 496 +#define EXT_CSD_TAG_RES_SIZE 497 +#define EXT_CSD_TAG_UNIT_SIZE 498 +#define EXT_CSD_DATA_TAG_SUPPORT 499 +#define EXT_CSD_MAX_PACKED_WRITES 500 +#define EXT_CSD_MAX_PACKED_READS 501 +#define EXT_CSD_BKOPS_SUPPORT 502 +#define EXT_CSD_HPI_FEATURES 503 +#define EXT_CSD_S_CMD_SET 504 + +#define EXT_CSD_WR_REL_PARAM_EN (1 << 2) +#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR (1 << 4) + +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_DEFAULT (0x0) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) + +#define EXT_CSD_PART_CONFIG_EN_MASK (0x7 << 3) +#define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3) +#define EXT_CSD_PART_CONFIG_EN_USER (0x7 << 3) + #define SDMMC_CMD_MAX 64 /** From f486bf7d1092bb9ec45dab123a3cdf6aaa18ea30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 13 Jun 2024 16:21:12 +0200 Subject: [PATCH 1852/2884] hw/sd/sdcard: Introduce sd_cmd_to_sendingdata and sd_generic_read_byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All commands switching from TRANSFER state to (sending)DATA do the same: send stream of data on the DAT lines. Instead of duplicating the same code many times, introduce 2 helpers: - sd_cmd_to_sendingdata() on the I/O line setup the data to be transferred, - sd_generic_read_byte() on the DAT lines to fetch the data. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <4c9f7f51-83ee-421a-8690-9af2e80b134b@linaro.org> --- hw/sd/sd.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 18bb2f9fd8..60235d3898 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -141,8 +141,10 @@ struct SDState { */ bool expecting_acmd; uint32_t blk_written; + uint64_t data_start; uint32_t data_offset; + size_t data_size; uint8_t data[512]; qemu_irq readonly_cb; qemu_irq inserted_cb; @@ -1078,6 +1080,29 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) return sd_illegal; } +/* Configure fields for following sd_generic_read_byte() calls */ +__attribute__((unused)) +static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, + uint64_t start, + const void *data, size_t size) +{ + if (sd->state != sd_transfer_state) { + sd_invalid_state_for_cmd(sd, req); + } + + sd->state = sd_sendingdata_state; + sd->data_start = start; + sd->data_offset = 0; + if (data) { + assert(size > 0 && size <= sizeof(sd->data)); + memcpy(sd->data, data, size); + } + if (size) { + sd->data_size = size; + } + return sd_r1; +} + /* CMD0 */ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) { @@ -1912,6 +1937,20 @@ send_response: return rsplen; } +/* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdata() */ +__attribute__((unused)) +static bool sd_generic_read_byte(SDState *sd, uint8_t *value) +{ + *value = sd->data[sd->data_offset]; + + if (++sd->data_offset >= sd->data_size) { + sd->state = sd_transfer_state; + return true; + } + + return false; +} + void sd_write_byte(SDState *sd, uint8_t value) { int i; From 1544e9f41dbfe1246611d86629d1c7ed1e3d0817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:09:08 +0200 Subject: [PATCH 1853/2884] hw/sd/sdcard: Convert SWITCH_FUNCTION to generic_read_byte (CMD6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-20-philmd@linaro.org> --- hw/sd/sd.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 60235d3898..b073700180 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1081,7 +1081,6 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) } /* Configure fields for following sd_generic_read_byte() calls */ -__attribute__((unused)) static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, uint64_t start, const void *data, size_t size) @@ -1235,10 +1234,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } sd_function_switch(sd, req.arg); - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); case 7: /* CMD7: SELECT/DESELECT_CARD */ rca = sd_req_get_rca(sd, req); @@ -1938,7 +1934,6 @@ send_response: } /* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdata() */ -__attribute__((unused)) static bool sd_generic_read_byte(SDState *sd, uint8_t *value) { *value = sd->data[sd->data_offset]; @@ -2128,10 +2123,7 @@ uint8_t sd_read_byte(SDState *sd) sd->current_cmd, sd->data_offset, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 64) - sd->state = sd_transfer_state; + sd_generic_read_byte(sd, &ret); break; case 9: /* CMD9: SEND_CSD */ From 374c93ecea604dd3fb675d6306644875922d56ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:13:05 +0200 Subject: [PATCH 1854/2884] hw/sd/sdcard: Convert SEND_CSD/SEND_CID to generic_read_byte (CMD9 & 10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-21-philmd@linaro.org> --- hw/sd/sd.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b073700180..6b02e0a178 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1304,11 +1304,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (!sd_is_spi(sd)) { break; } - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->csd, 16); - sd->data_start = sd_req_get_address(sd, req); - sd->data_offset = 0; - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->csd, 16); default: break; @@ -1328,11 +1325,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (!sd_is_spi(sd)) { break; } - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->cid, 16); - sd->data_start = sd_req_get_address(sd, req); - sd->data_offset = 0; - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->cid, 16); default: break; @@ -2123,15 +2117,9 @@ uint8_t sd_read_byte(SDState *sd) sd->current_cmd, sd->data_offset, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ - sd_generic_read_byte(sd, &ret); - break; - case 9: /* CMD9: SEND_CSD */ - case 10: /* CMD10: SEND_CID */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 16) - sd->state = sd_transfer_state; + case 10: /* CMD10: SEND_CID */ + sd_generic_read_byte(sd, &ret); break; case 13: /* ACMD13: SD_STATUS */ From eef0d42995c0638c336ae0ceb4e487b409094250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Jun 2024 05:36:27 +0200 Subject: [PATCH 1855/2884] hw/sd/sdcard: Duplicate READ_SINGLE_BLOCK / READ_MULTIPLE_BLOCK cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to modify the READ_SINGLE_BLOCK case in the next commit, duplicate it first. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-22-philmd@linaro.org> --- hw/sd/sd.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 6b02e0a178..43e0a2d925 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1390,6 +1390,24 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 17: /* CMD17: READ_SINGLE_BLOCK */ + addr = sd_req_get_address(sd, req); + switch (sd->state) { + case sd_transfer_state: + + if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) { + return sd_r1; + } + + sd->state = sd_sendingdata_state; + sd->data_start = addr; + sd->data_offset = 0; + return sd_r1; + + default: + break; + } + break; + case 18: /* CMD18: READ_MULTIPLE_BLOCK */ addr = sd_req_get_address(sd, req); switch (sd->state) { From e7a73713f821c017f71554f1d42c51698be18310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:27:51 +0200 Subject: [PATCH 1856/2884] hw/sd/sdcard: Convert READ_SINGLE_BLOCK to generic_read_byte (CMD17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-23-philmd@linaro.org> --- hw/sd/sd.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 43e0a2d925..8415c23e20 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1397,11 +1397,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) { return sd_r1; } - - sd->state = sd_sendingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; + sd_blk_read(sd, addr, sd->blk_len); + return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len); default: break; @@ -2137,6 +2134,7 @@ uint8_t sd_read_byte(SDState *sd) case 6: /* CMD6: SWITCH_FUNCTION */ case 9: /* CMD9: SEND_CSD */ case 10: /* CMD10: SEND_CID */ + case 17: /* CMD17: READ_SINGLE_BLOCK */ sd_generic_read_byte(sd, &ret); break; @@ -2147,16 +2145,6 @@ uint8_t sd_read_byte(SDState *sd) sd->state = sd_transfer_state; break; - case 17: /* CMD17: READ_SINGLE_BLOCK */ - if (sd->data_offset == 0) { - sd_blk_read(sd, sd->data_start, io_len); - } - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= io_len) - sd->state = sd_transfer_state; - break; - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ if (sd->data_offset == 0) { if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", From 060f0dac8668937a303120d0be0936ac6b29ce13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:15:10 +0200 Subject: [PATCH 1857/2884] hw/sd/sdcard: Convert SEND_TUNING_BLOCK to generic_read_byte (CMD19) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-24-philmd@linaro.org> --- hw/sd/sd.c | 48 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8415c23e20..940cbb0d3c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -564,6 +564,21 @@ static void sd_set_sdstatus(SDState *sd) memset(sd->sd_status, 0, 64); } +static const uint8_t sd_tuning_block_pattern4[64] = { + /* + * See: Physical Layer Simplified Specification Version 3.01, + * Table 4-2. + */ + 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde +}; + static int sd_req_crc_validate(SDRequest *req) { uint8_t buffer[5]; @@ -1153,14 +1168,9 @@ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) return sd_cmd_illegal(sd, req); } - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); - } - - sd->state = sd_sendingdata_state; - sd->data_offset = 0; - - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, 0, + sd_tuning_block_pattern4, + sizeof(sd_tuning_block_pattern4)); } /* CMD23 */ @@ -2093,20 +2103,6 @@ void sd_write_byte(SDState *sd, uint8_t value) } } -#define SD_TUNING_BLOCK_SIZE 64 - -static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = { - /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */ - 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc, - 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, - 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, - 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, - 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, - 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, - 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, - 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, -}; - uint8_t sd_read_byte(SDState *sd) { /* TODO: Append CRCs */ @@ -2135,6 +2131,7 @@ uint8_t sd_read_byte(SDState *sd) case 9: /* CMD9: SEND_CSD */ case 10: /* CMD10: SEND_CID */ case 17: /* CMD17: READ_SINGLE_BLOCK */ + case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ sd_generic_read_byte(sd, &ret); break; @@ -2169,13 +2166,6 @@ uint8_t sd_read_byte(SDState *sd) } break; - case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ - if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) { - sd->state = sd_transfer_state; - } - ret = sd_tuning_block_pattern[sd->data_offset++]; - break; - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ ret = sd->data[sd->data_offset ++]; From 2c67f8e707c27b13f1db4cc16ea6bddcccfa1ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:28:24 +0200 Subject: [PATCH 1858/2884] hw/sd/sdcard: Convert SEND_WRITE_PROT to generic_read_byte (CMD30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-25-philmd@linaro.org> --- hw/sd/sd.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 940cbb0d3c..1d6bacf885 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1194,6 +1194,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; uint64_t addr; + uint32_t data; sd->last_cmd_name = sd_cmd_name(req.cmd); /* CMD55 precedes an ACMD, so we are not interested in tracing it. @@ -1547,12 +1548,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) req.arg, sd->blk_len)) { return sd_r1; } - - sd->state = sd_sendingdata_state; - stl_be_p(sd->data, sd_wpbits(sd, req.arg)); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; + data = sd_wpbits(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data)); default: break; @@ -2132,6 +2129,7 @@ uint8_t sd_read_byte(SDState *sd) case 10: /* CMD10: SEND_CID */ case 17: /* CMD17: READ_SINGLE_BLOCK */ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + case 30: /* CMD30: SEND_WRITE_PROT */ sd_generic_read_byte(sd, &ret); break; @@ -2173,13 +2171,6 @@ uint8_t sd_read_byte(SDState *sd) sd->state = sd_transfer_state; break; - case 30: /* CMD30: SEND_WRITE_PROT */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 4) - sd->state = sd_transfer_state; - break; - case 51: /* ACMD51: SEND_SCR */ ret = sd->scr[sd->data_offset ++]; From ca24559d2c0df2d354e1ae329582de94817cd992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:25:08 +0200 Subject: [PATCH 1859/2884] hw/sd/sdcard: Convert SD_STATUS to generic_read_byte (ACMD13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-27-philmd@linaro.org> --- hw/sd/sd.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1d6bacf885..0a65bd5a76 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1693,10 +1693,9 @@ static sd_rsp_type_t sd_app_command(SDState *sd, case 13: /* ACMD13: SD_STATUS */ switch (sd->state) { case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, 0, + sd->sd_status, + sizeof(sd->sd_status)); default: break; @@ -2127,19 +2126,13 @@ uint8_t sd_read_byte(SDState *sd) case 6: /* CMD6: SWITCH_FUNCTION */ case 9: /* CMD9: SEND_CSD */ case 10: /* CMD10: SEND_CID */ + case 13: /* ACMD13: SD_STATUS */ case 17: /* CMD17: READ_SINGLE_BLOCK */ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ case 30: /* CMD30: SEND_WRITE_PROT */ sd_generic_read_byte(sd, &ret); break; - case 13: /* ACMD13: SD_STATUS */ - ret = sd->sd_status[sd->data_offset ++]; - - if (sd->data_offset >= sizeof(sd->sd_status)) - sd->state = sd_transfer_state; - break; - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ if (sd->data_offset == 0) { if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", From 4d842275e28d47788e7c0c34ee52ee33708b5f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:44:53 +0200 Subject: [PATCH 1860/2884] hw/sd/sdcard: Convert SEND_NUM_WR_BLOCKS to generic_read_byte (ACMD22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-28-philmd@linaro.org> --- hw/sd/sd.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0a65bd5a76..5d5e55243e 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1705,11 +1705,9 @@ static sd_rsp_type_t sd_app_command(SDState *sd, case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ switch (sd->state) { case sd_transfer_state: - stl_be_p(sd->data, sd->blk_written); - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, 0, + &sd->blk_written, + sizeof(sd->blk_written)); default: break; @@ -2129,6 +2127,7 @@ uint8_t sd_read_byte(SDState *sd) case 13: /* ACMD13: SD_STATUS */ case 17: /* CMD17: READ_SINGLE_BLOCK */ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ case 30: /* CMD30: SEND_WRITE_PROT */ sd_generic_read_byte(sd, &ret); break; @@ -2157,13 +2156,6 @@ uint8_t sd_read_byte(SDState *sd) } break; - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 4) - sd->state = sd_transfer_state; - break; - case 51: /* ACMD51: SEND_SCR */ ret = sd->scr[sd->data_offset ++]; From a320f9c06748f19012ee23e4f5a471f1e04554fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:24:07 +0200 Subject: [PATCH 1861/2884] hw/sd/sdcard: Convert SEND_SCR to generic_read_byte (ACMD51) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-29-philmd@linaro.org> --- hw/sd/sd.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5d5e55243e..78aabb65ac 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1775,10 +1775,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, case 51: /* ACMD51: SEND_SCR */ switch (sd->state) { case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; + return sd_cmd_to_sendingdata(sd, req, 0, sd->scr, sizeof(sd->scr)); default: break; @@ -2129,6 +2126,7 @@ uint8_t sd_read_byte(SDState *sd) case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ case 30: /* CMD30: SEND_WRITE_PROT */ + case 51: /* ACMD51: SEND_SCR */ sd_generic_read_byte(sd, &ret); break; @@ -2156,13 +2154,6 @@ uint8_t sd_read_byte(SDState *sd) } break; - case 51: /* ACMD51: SEND_SCR */ - ret = sd->scr[sd->data_offset ++]; - - if (sd->data_offset >= sizeof(sd->scr)) - sd->state = sd_transfer_state; - break; - case 56: /* CMD56: GEN_CMD */ if (sd->data_offset == 0) APP_READ_BLOCK(sd->data_start, sd->blk_len); From 740d51d1a3132c31594f3ff5fb63d6fcc22607ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 13 Jun 2024 16:21:50 +0200 Subject: [PATCH 1862/2884] hw/sd/sdcard: Introduce sd_cmd_to_receivingdata / sd_generic_write_byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All commands switching from TRANSFER state to (receiving)DATA do the same: receive stream of data from the DAT lines. Instead of duplicating the same code many times, introduce 2 helpers: - sd_cmd_to_receivingdata() on the I/O line setup the data to be received on the data[] buffer, - sd_generic_write_byte() on the DAT lines to push the data. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-30-philmd@linaro.org> --- hw/sd/sd.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 78aabb65ac..990f038b79 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1095,6 +1095,22 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) return sd_illegal; } +/* Configure fields for following sd_generic_write_byte() calls */ +__attribute__((unused)) +static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req, + uint64_t start, size_t size) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->state = sd_receivingdata_state; + sd->data_start = start; + sd->data_offset = 0; + /* sd->data[] used as receive buffer */ + sd->data_size = size ?: sizeof(sd->data); + return sd_r1; +} + /* Configure fields for following sd_generic_read_byte() calls */ static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, uint64_t start, @@ -1943,6 +1959,19 @@ send_response: return rsplen; } +/* Return true if buffer is consumed. Configured by sd_cmd_to_receivingdata() */ +__attribute__((unused)) +static bool sd_generic_write_byte(SDState *sd, uint8_t value) +{ + sd->data[sd->data_offset] = value; + + if (++sd->data_offset >= sd->data_size) { + sd->state = sd_transfer_state; + return true; + } + return false; +} + /* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdata() */ static bool sd_generic_read_byte(SDState *sd, uint8_t *value) { From dba4b37b38317ed5f91e04ca95f3731c946dba5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Jun 2024 09:14:09 +0200 Subject: [PATCH 1863/2884] hw/sd/sdcard: Duplicate WRITE_SINGLE_BLOCK / WRITE_MULTIPLE_BLOCK cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to modify the WRITE_SINGLE_BLOCK case in the next commit, duplicate it first. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-31-philmd@linaro.org> --- hw/sd/sd.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 990f038b79..b1acddca45 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1453,6 +1453,35 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + addr = sd_req_get_address(sd, req); + switch (sd->state) { + case sd_transfer_state: + + if (!address_in_range(sd, "WRITE_SINGLE_BLOCK", addr, + sd->blk_len)) { + return sd_r1; + } + + sd->state = sd_receivingdata_state; + sd->data_start = addr; + sd->data_offset = 0; + + if (sd->size <= SDSC_MAX_CAPACITY) { + if (sd_wp_addr(sd, sd->data_start)) { + sd->card_status |= WP_VIOLATION; + } + } + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + } + sd->blk_written = 0; + return sd_r1; + + default: + break; + } + break; + case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ addr = sd_req_get_address(sd, req); switch (sd->state) { From 540c1832dde061f15697112f49d766f9ba589794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:51:01 +0200 Subject: [PATCH 1864/2884] hw/sd/sdcard: Convert WRITE_SINGLE_BLOCK to generic_write_byte (CMD24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-32-philmd@linaro.org> --- hw/sd/sd.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b1acddca45..349549f801 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1096,7 +1096,6 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) } /* Configure fields for following sd_generic_write_byte() calls */ -__attribute__((unused)) static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req, uint64_t start, size_t size) { @@ -1462,10 +1461,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_r1; } - sd->state = sd_receivingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - if (sd->size <= SDSC_MAX_CAPACITY) { if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; @@ -1475,7 +1470,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->card_status |= WP_VIOLATION; } sd->blk_written = 0; - return sd_r1; + return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); default: break; @@ -1989,7 +1984,6 @@ send_response: } /* Return true if buffer is consumed. Configured by sd_cmd_to_receivingdata() */ -__attribute__((unused)) static bool sd_generic_write_byte(SDState *sd, uint8_t value) { sd->data[sd->data_offset] = value; @@ -2035,8 +2029,7 @@ void sd_write_byte(SDState *sd, uint8_t value) sd->current_cmd, sd->data_offset, value); switch (sd->current_cmd) { case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; sd_blk_write(sd, sd->data_start, sd->data_offset); From dbccae20f76ddfcade41bb5a746ec88dc90b27f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:55:03 +0200 Subject: [PATCH 1865/2884] hw/sd/sdcard: Convert PROGRAM_CID to generic_write_byte (CMD26) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-33-philmd@linaro.org> --- hw/sd/sd.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 349549f801..0aead3c866 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1507,17 +1507,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 26: /* CMD26: PROGRAM_CID */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; + return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); case 27: /* CMD27: PROGRAM_CSD */ switch (sd->state) { @@ -2078,8 +2068,7 @@ void sd_write_byte(SDState *sd, uint8_t value) break; case 26: /* CMD26: PROGRAM_CID */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sizeof(sd->cid)) { + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; for (i = 0; i < sizeof(sd->cid); i ++) From a182208e5db658dfa71506e3e0ff9a698d08a972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:23:54 +0200 Subject: [PATCH 1866/2884] hw/sd/sdcard: Convert PROGRAM_CSD to generic_write_byte (CMD27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-34-philmd@linaro.org> --- hw/sd/sd.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0aead3c866..95ba4d0755 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1510,17 +1510,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); case 27: /* CMD27: PROGRAM_CSD */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; + return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->csd)); /* Write protection (Class 6) */ case 28: /* CMD28: SET_WRITE_PROT */ @@ -2086,8 +2076,7 @@ void sd_write_byte(SDState *sd, uint8_t value) break; case 27: /* CMD27: PROGRAM_CSD */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sizeof(sd->csd)) { + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; for (i = 0; i < sizeof(sd->csd); i ++) From 77a2f97d1d1390d493a881983123bff5d04ead9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:32:18 +0200 Subject: [PATCH 1867/2884] hw/sd/sdcard: Convert LOCK_UNLOCK to generic_write_byte (CMD42) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-35-philmd@linaro.org> --- hw/sd/sd.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 95ba4d0755..822debb28b 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1620,17 +1620,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; + return sd_cmd_to_receivingdata(sd, req, 0, 0); /* Application specific commands (Class 8) */ case 55: /* CMD55: APP_CMD */ @@ -2099,8 +2089,7 @@ void sd_write_byte(SDState *sd, uint8_t value) break; case 42: /* CMD42: LOCK_UNLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; sd_lock_command(sd); From d4613f9f40a355e5f99268ac9daf7ad14764e99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 14:51:03 +0200 Subject: [PATCH 1868/2884] hw/sd/sdcard: Move sd_[a]cmd_name() methods to sd.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge sdmmc-internal.c into sd.c by moving sd_cmd_name() and sd_acmd_name() and updating meson.build. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-37-philmd@linaro.org> --- hw/sd/meson.build | 2 +- hw/sd/sd.c | 62 ++++++++++++++++++++++++++++++++++++ hw/sd/sdmmc-internal.c | 72 ------------------------------------------ hw/sd/sdmmc-internal.h | 26 --------------- 4 files changed, 63 insertions(+), 99 deletions(-) delete mode 100644 hw/sd/sdmmc-internal.c diff --git a/hw/sd/meson.build b/hw/sd/meson.build index abfac9e461..bbb75af0c9 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -1,5 +1,5 @@ system_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c')) -system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c')) +system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c')) system_ss.add(when: 'CONFIG_SDHCI', if_true: files('sdhci.c')) system_ss.add(when: 'CONFIG_SDHCI_PCI', if_true: files('sdhci-pci.c')) system_ss.add(when: 'CONFIG_SSI_SD', if_true: files('ssi-sd.c')) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 822debb28b..90bb47ad26 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -87,6 +87,8 @@ enum SDCardStates { sd_disconnect_state = 8, }; +#define SDMMC_CMD_MAX 64 + typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); typedef struct SDProto { @@ -228,6 +230,66 @@ static const char *sd_response_name(sd_rsp_type_t rsp) return response_name[rsp]; } +static const char *sd_cmd_name(uint8_t cmd) +{ + static const char *cmd_abbrev[SDMMC_CMD_MAX] = { + [0] = "GO_IDLE_STATE", [1] = "SEND_OP_COND", + [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", + [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", + [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", + [8] = "SEND_IF_COND", [9] = "SEND_CSD", + [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH", + [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", + [15] = "GO_INACTIVE_STATE", + [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", + [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK", + [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec", + [23] = "SET_BLOCK_COUNT", + [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", + [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", + [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", + [30] = "SEND_WRITE_PROT", + [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", + [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD", + [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD", + [38] = "ERASE", + [40] = "DPS_spec", + [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT", + [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B", + [46] = "Q_RD_TASK", [47] = "Q_WR_TASK", + [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE", + [50] = "SW_FUNC_RSVD", + [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED", + [54] = "SDIO_RSVD", [55] = "APP_CMD", + [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", + [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI", + [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", + [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", + }; + return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; +} + +static const char *sd_acmd_name(uint8_t cmd) +{ + static const char *acmd_abbrev[SDMMC_CMD_MAX] = { + [6] = "SET_BUS_WIDTH", + [13] = "SD_STATUS", + [14] = "DPS_spec", [15] = "DPS_spec", + [16] = "DPS_spec", + [18] = "SECU_spec", + [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT", + [41] = "SD_SEND_OP_COND", + [42] = "SET_CLR_CARD_DETECT", + [51] = "SEND_SCR", + [52] = "SECU_spec", [53] = "SECU_spec", + [54] = "SECU_spec", + [56] = "SECU_spec", [57] = "SECU_spec", + [58] = "SECU_spec", [59] = "SECU_spec", + }; + + return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD"; +} + static uint8_t sd_get_dat_lines(SDState *sd) { return sd->enable ? sd->dat_lines : 0; diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c deleted file mode 100644 index c1d5508ae6..0000000000 --- a/hw/sd/sdmmc-internal.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SD/MMC cards common helpers - * - * Copyright (c) 2018 Philippe Mathieu-Daudé - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "sdmmc-internal.h" - -const char *sd_cmd_name(uint8_t cmd) -{ - static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [0] = "GO_IDLE_STATE", [1] = "SEND_OP_COND", - [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", - [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", - [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", - [8] = "SEND_IF_COND", [9] = "SEND_CSD", - [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH", - [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", - [15] = "GO_INACTIVE_STATE", - [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", - [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK", - [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec", - [23] = "SET_BLOCK_COUNT", - [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", - [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", - [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", - [30] = "SEND_WRITE_PROT", - [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", - [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD", - [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD", - [38] = "ERASE", - [40] = "DPS_spec", - [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT", - [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B", - [46] = "Q_RD_TASK", [47] = "Q_WR_TASK", - [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE", - [50] = "SW_FUNC_RSVD", - [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED", - [54] = "SDIO_RSVD", [55] = "APP_CMD", - [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", - [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI", - [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", - [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", - }; - return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; -} - -const char *sd_acmd_name(uint8_t cmd) -{ - static const char *acmd_abbrev[SDMMC_CMD_MAX] = { - [6] = "SET_BUS_WIDTH", - [13] = "SD_STATUS", - [14] = "DPS_spec", [15] = "DPS_spec", - [16] = "DPS_spec", - [18] = "SECU_spec", - [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT", - [41] = "SD_SEND_OP_COND", - [42] = "SET_CLR_CARD_DETECT", - [51] = "SEND_SCR", - [52] = "SECU_spec", [53] = "SECU_spec", - [54] = "SECU_spec", - [56] = "SECU_spec", [57] = "SECU_spec", - [58] = "SECU_spec", [59] = "SECU_spec", - }; - - return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD"; -} diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index 936c75cace..cc0b69e834 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -119,30 +119,4 @@ #define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3) #define EXT_CSD_PART_CONFIG_EN_USER (0x7 << 3) -#define SDMMC_CMD_MAX 64 - -/** - * sd_cmd_name: - * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX. - * - * Returns a human-readable name describing the command. - * The return value is always a static string which does not need - * to be freed after use. - * - * Returns: The command name of @cmd or "UNKNOWN_CMD". - */ -const char *sd_cmd_name(uint8_t cmd); - -/** - * sd_acmd_name: - * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX. - * - * Returns a human-readable name describing the application command. - * The return value is always a static string which does not need - * to be freed after use. - * - * Returns: The application command name of @cmd or "UNKNOWN_ACMD". - */ -const char *sd_acmd_name(uint8_t cmd); - #endif From fb72e681c9caccc5f9effce219447cae1ed05c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 14:56:03 +0200 Subject: [PATCH 1869/2884] hw/sd/sdcard: Pass SDState as argument to sd_[a]cmd_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to access SDState::SDProto from sd_[a]cmd_name(), pass SDState as argument. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-38-philmd@linaro.org> --- hw/sd/sd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 90bb47ad26..5b356f0be8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -230,7 +230,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) return response_name[rsp]; } -static const char *sd_cmd_name(uint8_t cmd) +static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { [0] = "GO_IDLE_STATE", [1] = "SEND_OP_COND", @@ -269,7 +269,7 @@ static const char *sd_cmd_name(uint8_t cmd) return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; } -static const char *sd_acmd_name(uint8_t cmd) +static const char *sd_acmd_name(SDState *sd, uint8_t cmd) { static const char *acmd_abbrev[SDMMC_CMD_MAX] = { [6] = "SET_BUS_WIDTH", @@ -1273,7 +1273,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) uint64_t addr; uint32_t data; - sd->last_cmd_name = sd_cmd_name(req.cmd); + sd->last_cmd_name = sd_cmd_name(sd, req.cmd); /* CMD55 precedes an ACMD, so we are not interested in tracing it. * However there is no ACMD55, so we want to trace this particular case. */ @@ -1740,7 +1740,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { - sd->last_cmd_name = sd_acmd_name(req.cmd); + sd->last_cmd_name = sd_acmd_name(sd, req.cmd); trace_sdcard_app_command(sd->proto->name, sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; From 4518a49a019648110a84d7decebd46a12c229a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 05:15:46 +0200 Subject: [PATCH 1870/2884] hw/sd/sdcard: Prepare SDProto to contain more fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert array of command handlers to array of structures. The structure contains the command handler. No logical change intended. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-39-philmd@linaro.org> --- hw/sd/sd.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5b356f0be8..fb82bc9aa3 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -93,8 +93,9 @@ typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); typedef struct SDProto { const char *name; - sd_cmd_handler cmd[SDMMC_CMD_MAX]; - sd_cmd_handler acmd[SDMMC_CMD_MAX]; + struct { + sd_cmd_handler handler; + } cmd[SDMMC_CMD_MAX], acmd[SDMMC_CMD_MAX]; } SDProto; struct SDState { @@ -1297,8 +1298,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_illegal; } - if (sd->proto->cmd[req.cmd]) { - return sd->proto->cmd[req.cmd](sd, req); + if (sd->proto->cmd[req.cmd].handler) { + return sd->proto->cmd[req.cmd].handler(sd, req); } switch (req.cmd) { @@ -1745,8 +1746,8 @@ static sd_rsp_type_t sd_app_command(SDState *sd, req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; - if (sd->proto->acmd[req.cmd]) { - return sd->proto->acmd[req.cmd](sd, req); + if (sd->proto->acmd[req.cmd].handler) { + return sd->proto->acmd[req.cmd].handler(sd, req); } switch (req.cmd) { @@ -2269,22 +2270,22 @@ void sd_enable(SDState *sd, bool enable) static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { - [0] = sd_cmd_GO_IDLE_STATE, - [1] = spi_cmd_SEND_OP_COND, + [0] = {sd_cmd_GO_IDLE_STATE}, + [1] = {spi_cmd_SEND_OP_COND}, }, .acmd = { - [41] = spi_cmd_SEND_OP_COND, + [41] = {spi_cmd_SEND_OP_COND}, }, }; static const SDProto sd_proto_sd = { .name = "SD", .cmd = { - [0] = sd_cmd_GO_IDLE_STATE, - [2] = sd_cmd_ALL_SEND_CID, - [3] = sd_cmd_SEND_RELATIVE_ADDR, - [19] = sd_cmd_SEND_TUNING_BLOCK, - [23] = sd_cmd_SET_BLOCK_COUNT, + [0] = {sd_cmd_GO_IDLE_STATE}, + [2] = {sd_cmd_ALL_SEND_CID}, + [3] = {sd_cmd_SEND_RELATIVE_ADDR}, + [19] = {sd_cmd_SEND_TUNING_BLOCK}, + [23] = {sd_cmd_SET_BLOCK_COUNT}, }, }; From 572cdb1d9092e165d34f4ff8ab5483a97c6a99a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 14:43:28 +0200 Subject: [PATCH 1871/2884] hw/sd/sdcard: Store command name in SDProto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have a const array where command handlers are listed. Store the command name there too. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-40-philmd@linaro.org> --- hw/sd/sd.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index fb82bc9aa3..2cfba6ff60 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -94,6 +94,7 @@ typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); typedef struct SDProto { const char *name; struct { + const char *name; sd_cmd_handler handler; } cmd[SDMMC_CMD_MAX], acmd[SDMMC_CMD_MAX]; } SDProto; @@ -234,8 +235,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [0] = "GO_IDLE_STATE", [1] = "SEND_OP_COND", - [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", [8] = "SEND_IF_COND", [9] = "SEND_CSD", @@ -243,9 +242,8 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", [15] = "GO_INACTIVE_STATE", [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", - [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK", + [18] = "READ_MULTIPLE_BLOCK", [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec", - [23] = "SET_BLOCK_COUNT", [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", @@ -267,6 +265,12 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", }; + const SDProto *sdp = sd->proto; + + if (sdp->cmd[cmd].handler) { + assert(!cmd_abbrev[cmd]); + return sdp->cmd[cmd].name; + } return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; } @@ -279,7 +283,6 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) [16] = "DPS_spec", [18] = "SECU_spec", [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT", - [41] = "SD_SEND_OP_COND", [42] = "SET_CLR_CARD_DETECT", [51] = "SEND_SCR", [52] = "SECU_spec", [53] = "SECU_spec", @@ -287,6 +290,12 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) [56] = "SECU_spec", [57] = "SECU_spec", [58] = "SECU_spec", [59] = "SECU_spec", }; + const SDProto *sdp = sd->proto; + + if (sdp->acmd[cmd].handler) { + assert(!acmd_abbrev[cmd]); + return sdp->acmd[cmd].name; + } return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD"; } @@ -2270,22 +2279,22 @@ void sd_enable(SDState *sd, bool enable) static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { - [0] = {sd_cmd_GO_IDLE_STATE}, - [1] = {spi_cmd_SEND_OP_COND}, + [0] = { "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [1] = { "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, .acmd = { - [41] = {spi_cmd_SEND_OP_COND}, + [41] = { "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, }; static const SDProto sd_proto_sd = { .name = "SD", .cmd = { - [0] = {sd_cmd_GO_IDLE_STATE}, - [2] = {sd_cmd_ALL_SEND_CID}, - [3] = {sd_cmd_SEND_RELATIVE_ADDR}, - [19] = {sd_cmd_SEND_TUNING_BLOCK}, - [23] = {sd_cmd_SET_BLOCK_COUNT}, + [0] = { "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [2] = { "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [3] = { "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, + [19] = { "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, + [23] = { "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, }, }; From 1ab08790bb75e40cf35002edc26672d6b0e8004e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 05:20:57 +0200 Subject: [PATCH 1872/2884] hw/sd/sdcard: Store command type in SDProto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store the command type altogether with the command handler and name. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-41-philmd@linaro.org> --- hw/sd/sd.c | 44 ++++++++++++++++++++------------------------ include/hw/sd/sd.h | 5 +++-- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 2cfba6ff60..9f257906b5 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -94,6 +94,7 @@ typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); typedef struct SDProto { const char *name; struct { + const sd_cmd_type_t type; const char *name; sd_cmd_handler handler; } cmd[SDMMC_CMD_MAX], acmd[SDMMC_CMD_MAX]; @@ -348,20 +349,6 @@ static void sd_set_mode(SDState *sd) } } -static const sd_cmd_type_t sd_cmd_type[SDMMC_CMD_MAX] = { - sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac, - sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac, - /* 16 */ - sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, - sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none, - /* 32 */ - sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, - sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none, - /* 48 */ - sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, - sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, -}; - static const int sd_cmd_class[SDMMC_CMD_MAX] = { 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, @@ -567,10 +554,19 @@ static void sd_set_rca(SDState *sd) static uint16_t sd_req_get_rca(SDState *s, SDRequest req) { - if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) { + switch (s->proto->cmd[req.cmd].type) { + case sd_none: + /* Called from legacy code not ported to SDProto array */ + assert(!s->proto->cmd[req.cmd].handler); + /* fall-through */ + case sd_ac: + case sd_adtc: return req.arg >> 16; + case sd_spi: + g_assert_not_reached(); + default: + return 0; } - return 0; } /* Card Status register */ @@ -2279,22 +2275,22 @@ void sd_enable(SDState *sd, bool enable) static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { - [0] = { "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, - [1] = { "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [0] = { sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [1] = { sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, .acmd = { - [41] = { "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [41] = { sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, }; static const SDProto sd_proto_sd = { .name = "SD", .cmd = { - [0] = { "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, - [2] = { "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, - [3] = { "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, - [19] = { "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, - [23] = { "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [0] = { sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [2] = { sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [3] = { sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, + [19] = { sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, + [23] = { sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, }, }; diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 2c8748fb9b..29c76935a0 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -76,8 +76,9 @@ typedef enum { } sd_uhs_mode_t; typedef enum { - sd_none = -1, - sd_bc = 0, /* broadcast -- no response */ + sd_none = 0, + sd_spi, + sd_bc, /* broadcast -- no response */ sd_bcr, /* broadcast with response */ sd_ac, /* addressed -- no data transfer */ sd_adtc, /* addressed with data transfer */ From de32f0aa9a53db5d4cac8e9a5944c688b0ab24a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 05:28:09 +0200 Subject: [PATCH 1873/2884] hw/sd/sdcard: Store command class in SDProto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store the command class altogether with the other command fields (handler, name and type). Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-42-philmd@linaro.org> --- hw/sd/sd.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 9f257906b5..68e6944263 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -94,6 +94,7 @@ typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); typedef struct SDProto { const char *name; struct { + const unsigned class; const sd_cmd_type_t type; const char *name; sd_cmd_handler handler; @@ -349,13 +350,6 @@ static void sd_set_mode(SDState *sd) } } -static const int sd_cmd_class[SDMMC_CMD_MAX] = { - 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, - 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7, - 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8, -}; - static uint8_t sd_crc7(const void *message, size_t width) { int i, bit; @@ -1298,7 +1292,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->multi_blk_cnt = 0; } - if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + if (sd->proto->cmd[req.cmd].class == 6 && FIELD_EX32(sd->ocr, OCR, + CARD_CAPACITY)) { /* Only Standard Capacity cards support class 6 commands */ return sd_illegal; } @@ -1883,6 +1878,8 @@ static sd_rsp_type_t sd_app_command(SDState *sd, static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) { + unsigned cmd_class; + /* Valid commands in locked state: * basic class (0) * lock card class (7) @@ -1897,7 +1894,12 @@ static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) if (cmd == 16 || cmd == 55) { return true; } - return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7; + if (!sd->proto->cmd[cmd].handler) { + return false; + } + cmd_class = sd->proto->cmd[cmd].class; + + return cmd_class == 0 || cmd_class == 7; } int sd_do_command(SDState *sd, SDRequest *req, @@ -2275,22 +2277,22 @@ void sd_enable(SDState *sd, bool enable) static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { - [0] = { sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, - [1] = { sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, .acmd = { - [41] = { sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, }; static const SDProto sd_proto_sd = { .name = "SD", .cmd = { - [0] = { sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, - [2] = { sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, - [3] = { sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, - [19] = { sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, - [23] = { sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, + [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, + [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, }, }; From 617c5a734571145f65423336376001bac8fc371a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 13 Jun 2024 22:38:17 +0200 Subject: [PATCH 1874/2884] hw/sd/sdcard: Remove SEND_DSR dead case (CMD4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CSD::CSR_IMP bit defines whether the Driver Stage Register (DSR) is implemented or not. We do not set this bit in CSD: static void sd_set_csd(SDState *sd, uint64_t size) { ... if (size <= SDSC_MAX_CAPACITY) { /* Standard Capacity SD */ ... sd->csd[6] = 0xe0 | /* Partial block for read allowed */ ((csize >> 10) & 0x03); ... } else { /* SDHC */ ... sd->csd[6] = 0x00; ... } ... } The sd_normal_command() switch case for the SEND_DSR command do nothing and fallback to "illegal command". Since the command is mandatory (although the register isn't...) call the sd_cmd_unimplemented() handler. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-43-philmd@linaro.org> --- hw/sd/sd.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 68e6944263..c25e376b35 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", + [5] = "IO_SEND_OP_COND", [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", [8] = "SEND_IF_COND", [9] = "SEND_CSD", [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH", @@ -1148,7 +1148,6 @@ static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) } /* Commands that are recognised but not yet implemented. */ -__attribute__((unused)) static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) { qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n", @@ -1304,16 +1303,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 4: /* CMD4: SEND_DSR */ - switch (sd->state) { - case sd_standby_state: - break; - - default: - break; - } - break; - case 6: /* CMD6: SWITCH_FUNCTION */ if (sd->mode != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); @@ -2291,6 +2280,7 @@ static const SDProto sd_proto_sd = { [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, + [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, }, From 720c0f3e6cf856fa62c06a8f0005d814684c30d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 11:46:45 +0200 Subject: [PATCH 1875/2884] hw/sd/sdcard: Register generic optional handlers (CMD11 and CMD20) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-44-philmd@linaro.org> --- hw/sd/sd.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index c25e376b35..b4fd863189 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -240,12 +240,12 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [5] = "IO_SEND_OP_COND", [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", [8] = "SEND_IF_COND", [9] = "SEND_CSD", - [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH", + [10] = "SEND_CID", [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", [15] = "GO_INACTIVE_STATE", [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", [18] = "READ_MULTIPLE_BLOCK", - [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec", + [21] = "DPS_spec", [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", @@ -1156,6 +1156,14 @@ static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) return sd_illegal; } +static sd_rsp_type_t sd_cmd_optional(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_UNIMP, "%s: Optional CMD%i not implemented\n", + sd->proto->name, req.cmd); + + return sd_illegal; +} + /* Configure fields for following sd_generic_write_byte() calls */ static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req, uint64_t start, size_t size) @@ -2281,7 +2289,9 @@ static const SDProto sd_proto_sd = { [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, + [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, + [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, }, }; From a3e8ca8381aff3b115103c29f0b8f2265705e90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 11:48:44 +0200 Subject: [PATCH 1876/2884] hw/sd/sdcard: Register optional handlers from spec v6.00 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-45-philmd@linaro.org> --- hw/sd/sd.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b4fd863189..f8672b6603 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -255,15 +255,11 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD", [38] = "ERASE", [40] = "DPS_spec", - [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT", - [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B", - [46] = "Q_RD_TASK", [47] = "Q_WR_TASK", - [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE", + [42] = "LOCK_UNLOCK", [50] = "SW_FUNC_RSVD", [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED", [54] = "SDIO_RSVD", [55] = "APP_CMD", [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", - [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI", [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", }; @@ -2293,6 +2289,15 @@ static const SDProto sd_proto_sd = { [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [43] = {1, sd_ac, "Q_MANAGEMENT", sd_cmd_optional}, + [44] = {1, sd_ac, "Q_TASK_INFO_A", sd_cmd_optional}, + [45] = {1, sd_ac, "Q_TASK_INFO_B", sd_cmd_optional}, + [46] = {1, sd_adtc, "Q_RD_TASK", sd_cmd_optional}, + [47] = {1, sd_adtc, "Q_WR_TASK", sd_cmd_optional}, + [48] = {1, sd_adtc, "READ_EXTR_SINGLE", sd_cmd_optional}, + [49] = {1, sd_adtc, "WRITE_EXTR_SINGLE", sd_cmd_optional}, + [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, + [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, }, }; From 9b2e17756f08849a20b4989aec1e7a29520f5ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 11:49:38 +0200 Subject: [PATCH 1877/2884] hw/sd/sdcard: Register SDIO optional handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See "SD Input/Output Card Specification" v1.00. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-46-philmd@linaro.org> --- hw/sd/sd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index f8672b6603..e050f3d5ef 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [5] = "IO_SEND_OP_COND", [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", [8] = "SEND_IF_COND", [9] = "SEND_CSD", [10] = "SEND_CID", @@ -257,7 +256,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [40] = "DPS_spec", [42] = "LOCK_UNLOCK", [50] = "SW_FUNC_RSVD", - [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED", [54] = "SDIO_RSVD", [55] = "APP_CMD", [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", @@ -2272,6 +2270,9 @@ static const SDProto sd_proto_spi = { .cmd = { [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, + [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, + [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, }, .acmd = { [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, @@ -2285,6 +2286,7 @@ static const SDProto sd_proto_sd = { [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, + [5] = {9, sd_bc, "IO_SEND_OP_COND", sd_cmd_optional}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, @@ -2296,6 +2298,8 @@ static const SDProto sd_proto_sd = { [47] = {1, sd_adtc, "Q_WR_TASK", sd_cmd_optional}, [48] = {1, sd_adtc, "READ_EXTR_SINGLE", sd_cmd_optional}, [49] = {1, sd_adtc, "WRITE_EXTR_SINGLE", sd_cmd_optional}, + [52] = {9, sd_bc, "IO_RW_DIRECT", sd_cmd_optional}, + [53] = {9, sd_bc, "IO_RW_EXTENDED", sd_cmd_optional}, [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, }, From f274e6f59042f92746474c658d3a413342694bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 11:50:21 +0200 Subject: [PATCH 1878/2884] hw/sd/sdcard: Register Security Extension optional handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See "Advanced Security SD Extension Specification" v2.00. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-47-philmd@linaro.org> --- hw/sd/sd.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index e050f3d5ef..54b9ec72e4 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -250,14 +250,11 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", [30] = "SEND_WRITE_PROT", [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", - [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD", - [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD", [38] = "ERASE", [40] = "DPS_spec", [42] = "LOCK_UNLOCK", - [50] = "SW_FUNC_RSVD", [54] = "SDIO_RSVD", [55] = "APP_CMD", - [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", + [56] = "GEN_CMD", [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", }; @@ -2271,8 +2268,14 @@ static const SDProto sd_proto_spi = { [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, + [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, + [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, + [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, + [37] = {10, sd_spi, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, + [50] = {10, sd_spi, "DIRECT_SECURE_READ", sd_cmd_optional}, [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, + [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional}, }, .acmd = { [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, @@ -2291,6 +2294,10 @@ static const SDProto sd_proto_sd = { [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, + [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, + [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, + [37] = {10, sd_ac, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, [43] = {1, sd_ac, "Q_MANAGEMENT", sd_cmd_optional}, [44] = {1, sd_ac, "Q_TASK_INFO_A", sd_cmd_optional}, [45] = {1, sd_ac, "Q_TASK_INFO_B", sd_cmd_optional}, @@ -2298,8 +2305,10 @@ static const SDProto sd_proto_sd = { [47] = {1, sd_adtc, "Q_WR_TASK", sd_cmd_optional}, [48] = {1, sd_adtc, "READ_EXTR_SINGLE", sd_cmd_optional}, [49] = {1, sd_adtc, "WRITE_EXTR_SINGLE", sd_cmd_optional}, + [50] = {10, sd_adtc, "DIRECT_SECURE_READ", sd_cmd_optional}, [52] = {9, sd_bc, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_bc, "IO_RW_EXTENDED", sd_cmd_optional}, + [57] = {10, sd_adtc, "DIRECT_SECURE_WRITE", sd_cmd_optional}, [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, }, From 4064824fb7781fce85ed50e56611241eb16ce83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Jun 2022 07:10:42 +0200 Subject: [PATCH 1879/2884] hw/sd/sdcard: Add sd_cmd_SWITCH_FUNCTION handler (CMD6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-48-philmd@linaro.org> --- hw/sd/sd.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 54b9ec72e4..5aa63f732f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", + [7] = "SELECT/DESELECT_CARD", [8] = "SEND_IF_COND", [9] = "SEND_CSD", [10] = "SEND_CID", [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", @@ -1236,6 +1236,20 @@ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) } } +/* CMD6 */ +static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) +{ + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); + } + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd_function_switch(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1302,17 +1316,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 6: /* CMD6: SWITCH_FUNCTION */ - if (sd->mode != sd_data_transfer_mode) { - return sd_invalid_mode_for_cmd(sd, req); - } - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); - } - - sd_function_switch(sd, req.arg); - return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); - case 7: /* CMD7: SELECT/DESELECT_CARD */ rca = sd_req_get_rca(sd, req); switch (sd->state) { @@ -2268,6 +2271,7 @@ static const SDProto sd_proto_spi = { [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, + [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2290,6 +2294,7 @@ static const SDProto sd_proto_sd = { [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [5] = {9, sd_bc, "IO_SEND_OP_COND", sd_cmd_optional}, + [6] = {10, sd_adtc, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, From 9193deb4066ad3734f27e7fbe3717ad80db0f16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:11:20 +0200 Subject: [PATCH 1880/2884] hw/sd/sdcard: Add sd_cmd_DE/SELECT_CARD handler (CMD7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-49-philmd@linaro.org> --- hw/sd/sd.c | 85 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5aa63f732f..f83ae4ed18 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [7] = "SELECT/DESELECT_CARD", [8] = "SEND_IF_COND", [9] = "SEND_CSD", [10] = "SEND_CID", [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", @@ -554,6 +553,11 @@ static uint16_t sd_req_get_rca(SDState *s, SDRequest req) } } +static bool sd_req_rca_same(SDState *s, SDRequest req) +{ + return sd_req_get_rca(s, req) == s->rca; +} + /* Card Status register */ FIELD(CSR, AKE_SEQ_ERROR, 3, 1) @@ -1250,6 +1254,47 @@ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); } +/* CMD7 */ +static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req) +{ + bool same_rca = sd_req_rca_same(sd, req); + + switch (sd->state) { + case sd_standby_state: + if (!same_rca) { + return sd_r0; + } + sd->state = sd_transfer_state; + return sd_r1b; + + case sd_transfer_state: + case sd_sendingdata_state: + if (same_rca) { + break; + } + sd->state = sd_standby_state; + return sd_r1b; + + case sd_disconnect_state: + if (!same_rca) { + return sd_r0; + } + sd->state = sd_programming_state; + return sd_r1b; + + case sd_programming_state: + if (same_rca) { + break; + } + sd->state = sd_disconnect_state; + return sd_r1b; + + default: + break; + } + return sd_invalid_state_for_cmd(sd, req); +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1316,43 +1361,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 7: /* CMD7: SELECT/DESELECT_CARD */ - rca = sd_req_get_rca(sd, req); - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_transfer_state; - return sd_r1b; - - case sd_transfer_state: - case sd_sendingdata_state: - if (sd->rca == rca) - break; - - sd->state = sd_standby_state; - return sd_r1b; - - case sd_disconnect_state: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_programming_state; - return sd_r1b; - - case sd_programming_state: - if (sd->rca == rca) - break; - - sd->state = sd_disconnect_state; - return sd_r1b; - - default: - break; - } - break; - case 8: /* CMD8: SEND_IF_COND */ if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { break; @@ -2295,6 +2303,7 @@ static const SDProto sd_proto_sd = { [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [5] = {9, sd_bc, "IO_SEND_OP_COND", sd_cmd_optional}, [6] = {10, sd_adtc, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, + [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, From 31798907b71f9fd15aa2ccb20b2f18e782d8a3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:12:23 +0200 Subject: [PATCH 1881/2884] hw/sd/sdcard: Add sd_cmd_SEND_IF_COND handler (CMD8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-50-philmd@linaro.org> --- hw/sd/sd.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index f83ae4ed18..1bde4c9f7f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [8] = "SEND_IF_COND", [9] = "SEND_CSD", + [9] = "SEND_CSD", [10] = "SEND_CID", [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", [15] = "GO_INACTIVE_STATE", @@ -1295,6 +1295,27 @@ static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req) return sd_invalid_state_for_cmd(sd, req); } +/* CMD8 */ +static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) +{ + if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { + return sd_cmd_illegal(sd, req); + } + if (sd->state != sd_idle_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->vhs = 0; + + /* No response if not exactly one VHS bit is set. */ + if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { + return sd_is_spi(sd) ? sd_r7 : sd_r0; + } + + /* Accept. */ + sd->vhs = req.arg; + return sd_r7; +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1361,24 +1382,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 8: /* CMD8: SEND_IF_COND */ - if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { - break; - } - if (sd->state != sd_idle_state) { - break; - } - sd->vhs = 0; - - /* No response if not exactly one VHS bit is set. */ - if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { - return sd_is_spi(sd) ? sd_r7 : sd_r0; - } - - /* Accept. */ - sd->vhs = req.arg; - return sd_r7; - case 9: /* CMD9: SEND_CSD */ rca = sd_req_get_rca(sd, req); switch (sd->state) { @@ -2280,6 +2283,7 @@ static const SDProto sd_proto_spi = { [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, + [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2304,6 +2308,7 @@ static const SDProto sd_proto_sd = { [5] = {9, sd_bc, "IO_SEND_OP_COND", sd_cmd_optional}, [6] = {10, sd_adtc, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, + [8] = {0, sd_bcr, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, From 1ec3cb893fa8883f5baf69850a4d0a97502bbad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:16:40 +0200 Subject: [PATCH 1882/2884] hw/sd/sdcard: Add sd_cmd_SEND_CSD/CID handlers (CMD9 & CMD10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-51-philmd@linaro.org> --- hw/sd/sd.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1bde4c9f7f..e372f88073 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,8 +237,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [9] = "SEND_CSD", - [10] = "SEND_CID", [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", [15] = "GO_INACTIVE_STATE", [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", @@ -1316,6 +1314,26 @@ static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) return sd_r7; } +/* CMD9 */ +static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + return sd_req_rca_same(sd, req) ? sd_r2_s : sd_r0; +} + +/* CMD10 */ +static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + return sd_req_rca_same(sd, req) ? sd_r2_i : sd_r0; +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1385,12 +1403,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 9: /* CMD9: SEND_CSD */ rca = sd_req_get_rca(sd, req); switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - return sd_r2_s; - case sd_transfer_state: if (!sd_is_spi(sd)) { break; @@ -1406,12 +1418,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 10: /* CMD10: SEND_CID */ rca = sd_req_get_rca(sd, req); switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - return sd_r2_i; - case sd_transfer_state: if (!sd_is_spi(sd)) { break; @@ -2309,6 +2315,8 @@ static const SDProto sd_proto_sd = { [6] = {10, sd_adtc, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, [8] = {0, sd_bcr, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, + [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, + [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, From da954d0e32444f122a41c24948d4d1c718bf66d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:16:40 +0200 Subject: [PATCH 1883/2884] hw/sd/sdcard: Add spi_cmd_SEND_CSD/CID handlers (CMD9 & CMD10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-52-philmd@linaro.org> --- hw/sd/sd.c | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index e372f88073..49fc79cf8a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1315,6 +1315,15 @@ static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) } /* CMD9 */ +static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->csd, 16); +} + static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) { if (sd->state != sd_standby_state) { @@ -1325,6 +1334,15 @@ static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) } /* CMD10 */ +static sd_rsp_type_t spi_cmd_SEND_CID(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->cid, 16); +} + static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req) { if (sd->state != sd_standby_state) { @@ -1400,36 +1418,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 9: /* CMD9: SEND_CSD */ - rca = sd_req_get_rca(sd, req); - switch (sd->state) { - case sd_transfer_state: - if (!sd_is_spi(sd)) { - break; - } - return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), - sd->csd, 16); - - default: - break; - } - break; - - case 10: /* CMD10: SEND_CID */ - rca = sd_req_get_rca(sd, req); - switch (sd->state) { - case sd_transfer_state: - if (!sd_is_spi(sd)) { - break; - } - return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), - sd->cid, 16); - - default: - break; - } - break; - case 12: /* CMD12: STOP_TRANSMISSION */ switch (sd->state) { case sd_sendingdata_state: @@ -2290,6 +2278,8 @@ static const SDProto sd_proto_spi = { [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, + [9] = {0, sd_spi, "SEND_CSD", spi_cmd_SEND_CSD}, + [10] = {0, sd_spi, "SEND_CID", spi_cmd_SEND_CID}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, From 030897e89d3dff8ef7efd5cc570612da4476734f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:17:49 +0200 Subject: [PATCH 1884/2884] hw/sd/sdcard: Add sd_cmd_STOP_TRANSMISSION handler (CMD12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-53-philmd@linaro.org> --- hw/sd/sd.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 49fc79cf8a..0a554d30a2 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", + [13] = "SEND_STATUS", [15] = "GO_INACTIVE_STATE", [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", [18] = "READ_MULTIPLE_BLOCK", @@ -1352,6 +1352,23 @@ static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req) return sd_req_rca_same(sd, req) ? sd_r2_i : sd_r0; } +/* CMD12 */ +static sd_rsp_type_t sd_cmd_STOP_TRANSMISSION(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_sendingdata_state: + sd->state = sd_transfer_state; + return sd_r1b; + case sd_receivingdata_state: + sd->state = sd_programming_state; + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return sd_r1; + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1418,23 +1435,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 12: /* CMD12: STOP_TRANSMISSION */ - switch (sd->state) { - case sd_sendingdata_state: - sd->state = sd_transfer_state; - return sd_r1b; - - case sd_receivingdata_state: - sd->state = sd_programming_state; - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - case 13: /* CMD13: SEND_STATUS */ rca = sd_req_get_rca(sd, req); if (sd->mode != sd_data_transfer_mode) { @@ -2280,6 +2280,7 @@ static const SDProto sd_proto_spi = { [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, [9] = {0, sd_spi, "SEND_CSD", spi_cmd_SEND_CSD}, [10] = {0, sd_spi, "SEND_CID", spi_cmd_SEND_CID}, + [12] = {0, sd_spi, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2308,6 +2309,7 @@ static const SDProto sd_proto_sd = { [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, + [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, From 807f6adac3773c18772bfc9dbee7a3b4a4f9dfe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:18:39 +0200 Subject: [PATCH 1885/2884] hw/sd/sdcard: Add sd_cmd_SEND_STATUS handler (CMD13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-54-philmd@linaro.org> --- hw/sd/sd.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0a554d30a2..426144c659 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [13] = "SEND_STATUS", [15] = "GO_INACTIVE_STATE", [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", [18] = "READ_MULTIPLE_BLOCK", @@ -1369,6 +1368,32 @@ static sd_rsp_type_t sd_cmd_STOP_TRANSMISSION(SDState *sd, SDRequest req) } } +/* CMD13 */ +static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) +{ + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); + } + + switch (sd->state) { + case sd_standby_state: + case sd_transfer_state: + case sd_sendingdata_state: + case sd_receivingdata_state: + case sd_programming_state: + case sd_disconnect_state: + break; + default: + return sd_invalid_state_for_cmd(sd, req); + } + + if (sd_is_spi(sd)) { + return sd_r2_s; + } + + return sd_req_rca_same(sd, req) ? sd_r1 : sd_r0; +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1435,17 +1460,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 13: /* CMD13: SEND_STATUS */ - rca = sd_req_get_rca(sd, req); - if (sd->mode != sd_data_transfer_mode) { - return sd_invalid_mode_for_cmd(sd, req); - } - if (!sd_is_spi(sd) && sd->rca != rca) { - return sd_r0; - } - - return sd_r1; - case 15: /* CMD15: GO_INACTIVE_STATE */ if (sd->mode != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); @@ -2281,6 +2295,7 @@ static const SDProto sd_proto_spi = { [9] = {0, sd_spi, "SEND_CSD", spi_cmd_SEND_CSD}, [10] = {0, sd_spi, "SEND_CID", spi_cmd_SEND_CID}, [12] = {0, sd_spi, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, + [13] = {0, sd_spi, "SEND_STATUS", sd_cmd_SEND_STATUS}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2310,6 +2325,7 @@ static const SDProto sd_proto_sd = { [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, + [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, From 9318be060506be33a0fbf01198b0024fdeb28f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:19:33 +0200 Subject: [PATCH 1886/2884] hw/sd/sdcard: Add sd_cmd_GO_INACTIVE_STATE handler (CMD15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-55-philmd@linaro.org> --- hw/sd/sd.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 426144c659..56b4b274a1 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [15] = "GO_INACTIVE_STATE", [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", [18] = "READ_MULTIPLE_BLOCK", [21] = "DPS_spec", @@ -1394,6 +1393,30 @@ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) return sd_req_rca_same(sd, req) ? sd_r1 : sd_r0; } +/* CMD15 */ +static sd_rsp_type_t sd_cmd_GO_INACTIVE_STATE(SDState *sd, SDRequest req) +{ + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); + } + switch (sd->state) { + case sd_standby_state: + case sd_transfer_state: + case sd_sendingdata_state: + case sd_receivingdata_state: + case sd_programming_state: + case sd_disconnect_state: + break; + default: + return sd_invalid_state_for_cmd(sd, req); + } + if (sd_req_rca_same(sd, req)) { + sd->state = sd_inactive_state; + } + + return sd_r0; +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1459,17 +1482,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } switch (req.cmd) { - /* Basic commands (Class 0 and Class 1) */ - case 15: /* CMD15: GO_INACTIVE_STATE */ - if (sd->mode != sd_data_transfer_mode) { - return sd_invalid_mode_for_cmd(sd, req); - } - rca = sd_req_get_rca(sd, req); - if (sd->rca == rca) { - sd->state = sd_inactive_state; - } - return sd_r0; - /* Block read commands (Class 2) */ case 16: /* CMD16: SET_BLOCKLEN */ switch (sd->state) { @@ -2326,6 +2338,7 @@ static const SDProto sd_proto_sd = { [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, From 6d4e6424a6f7dbc11d06b231d9993958fcc1f929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:20:36 +0200 Subject: [PATCH 1887/2884] hw/sd/sdcard: Add sd_cmd_SET_BLOCKLEN handler (CMD16) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-56-philmd@linaro.org> --- hw/sd/sd.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 56b4b274a1..335b3e03db 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", + [17] = "READ_SINGLE_BLOCK", [18] = "READ_MULTIPLE_BLOCK", [21] = "DPS_spec", [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", @@ -1417,6 +1417,22 @@ static sd_rsp_type_t sd_cmd_GO_INACTIVE_STATE(SDState *sd, SDRequest req) return sd_r0; } +/* CMD16 */ +static sd_rsp_type_t sd_cmd_SET_BLOCKLEN(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + if (req.arg > (1 << HWBLOCK_SHIFT)) { + sd->card_status |= BLOCK_LEN_ERROR; + } else { + trace_sdcard_set_blocklen(req.arg); + sd->blk_len = req.arg; + } + + return sd_r1; +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1483,23 +1499,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Block read commands (Class 2) */ - case 16: /* CMD16: SET_BLOCKLEN */ - switch (sd->state) { - case sd_transfer_state: - if (req.arg > (1 << HWBLOCK_SHIFT)) { - sd->card_status |= BLOCK_LEN_ERROR; - } else { - trace_sdcard_set_blocklen(req.arg); - sd->blk_len = req.arg; - } - - return sd_r1; - - default: - break; - } - break; - case 17: /* CMD17: READ_SINGLE_BLOCK */ addr = sd_req_get_address(sd, req); switch (sd->state) { @@ -2308,6 +2307,7 @@ static const SDProto sd_proto_spi = { [10] = {0, sd_spi, "SEND_CID", spi_cmd_SEND_CID}, [12] = {0, sd_spi, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [13] = {0, sd_spi, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [16] = {2, sd_spi, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2339,6 +2339,7 @@ static const SDProto sd_proto_sd = { [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, + [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, From 3d3caf2021a355e4f5ffa635d4942beddf6ea1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jun 2024 00:09:02 +0200 Subject: [PATCH 1888/2884] hw/sd/sdcard: Add sd_cmd_READ_SINGLE_BLOCK handler (CMD17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-57-philmd@linaro.org> --- hw/sd/sd.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 335b3e03db..3f5cc0c55c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -237,7 +237,6 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [17] = "READ_SINGLE_BLOCK", [18] = "READ_MULTIPLE_BLOCK", [21] = "DPS_spec", [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", @@ -1433,6 +1432,24 @@ static sd_rsp_type_t sd_cmd_SET_BLOCKLEN(SDState *sd, SDRequest req) return sd_r1; } +/* CMD17 */ +static sd_rsp_type_t sd_cmd_READ_SINGLE_BLOCK(SDState *sd, SDRequest req) +{ + uint64_t addr; + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) { + return sd_r1; + } + + sd_blk_read(sd, addr, sd->blk_len); + return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len); +} + /* CMD19 */ static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) { @@ -1499,22 +1516,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (req.cmd) { /* Block read commands (Class 2) */ - case 17: /* CMD17: READ_SINGLE_BLOCK */ - addr = sd_req_get_address(sd, req); - switch (sd->state) { - case sd_transfer_state: - - if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) { - return sd_r1; - } - sd_blk_read(sd, addr, sd->blk_len); - return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len); - - default: - break; - } - break; - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ addr = sd_req_get_address(sd, req); switch (sd->state) { @@ -2308,6 +2309,7 @@ static const SDProto sd_proto_spi = { [12] = {0, sd_spi, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [13] = {0, sd_spi, "SEND_STATUS", sd_cmd_SEND_STATUS}, [16] = {2, sd_spi, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, + [17] = {2, sd_spi, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2340,6 +2342,7 @@ static const SDProto sd_proto_sd = { [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, + [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, From 4f862e6409ea3756a59dd1bde6b7e8303916ff23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:22:52 +0200 Subject: [PATCH 1889/2884] hw/sd/sdcard: Add sd_cmd_WRITE_SINGLE_BLOCK handler (CMD24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-58-philmd@linaro.org> --- hw/sd/sd.c | 57 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 3f5cc0c55c..02a1203691 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -239,7 +239,7 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) static const char *cmd_abbrev[SDMMC_CMD_MAX] = { [18] = "READ_MULTIPLE_BLOCK", [21] = "DPS_spec", - [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", + [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", [30] = "SEND_WRITE_PROT", @@ -1479,6 +1479,33 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) return sd_r1; } +/* CMD24 */ +static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req) +{ + uint64_t addr; + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "WRITE_SINGLE_BLOCK", addr, sd->blk_len)) { + return sd_r1; + } + + if (sd->size <= SDSC_MAX_CAPACITY) { + if (sd_wp_addr(sd, addr)) { + sd->card_status |= WP_VIOLATION; + } + } + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + } + + sd->blk_written = 0; + return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; @@ -1536,32 +1563,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; /* Block write commands (Class 4) */ - case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - addr = sd_req_get_address(sd, req); - switch (sd->state) { - case sd_transfer_state: - - if (!address_in_range(sd, "WRITE_SINGLE_BLOCK", addr, - sd->blk_len)) { - return sd_r1; - } - - if (sd->size <= SDSC_MAX_CAPACITY) { - if (sd_wp_addr(sd, sd->data_start)) { - sd->card_status |= WP_VIOLATION; - } - } - if (sd->csd[14] & 0x30) { - sd->card_status |= WP_VIOLATION; - } - sd->blk_written = 0; - return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); - - default: - break; - } - break; - case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ addr = sd_req_get_address(sd, req); switch (sd->state) { @@ -2310,6 +2311,7 @@ static const SDProto sd_proto_spi = { [13] = {0, sd_spi, "SEND_STATUS", sd_cmd_SEND_STATUS}, [16] = {2, sd_spi, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, [17] = {2, sd_spi, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, + [24] = {4, sd_spi, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2346,6 +2348,7 @@ static const SDProto sd_proto_sd = { [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, From 96f3d00ac1680f978984314d24ae6f2f6f281fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 25 Jun 2024 13:59:28 +0200 Subject: [PATCH 1890/2884] hw/sd/sdcard: Add sd_cmd_PROGRAM_CSD handler (CMD27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-59-philmd@linaro.org> --- hw/sd/sd.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 02a1203691..e1c799c117 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -240,7 +240,7 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [18] = "READ_MULTIPLE_BLOCK", [21] = "DPS_spec", [25] = "WRITE_MULTIPLE_BLOCK", - [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", + [26] = "MANUF_RSVD", [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", [30] = "SEND_WRITE_PROT", [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", @@ -1506,6 +1506,12 @@ static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); } +/* CMD27 */ +static sd_rsp_type_t sd_cmd_PROGRAM_CSD(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->csd)); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; @@ -1595,9 +1601,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 26: /* CMD26: PROGRAM_CID */ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); - case 27: /* CMD27: PROGRAM_CSD */ - return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->csd)); - /* Write protection (Class 6) */ case 28: /* CMD28: SET_WRITE_PROT */ if (sd->size > SDSC_MAX_CAPACITY) { @@ -2312,6 +2315,7 @@ static const SDProto sd_proto_spi = { [16] = {2, sd_spi, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, [17] = {2, sd_spi, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, [24] = {4, sd_spi, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [27] = {4, sd_spi, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2349,6 +2353,7 @@ static const SDProto sd_proto_sd = { [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, From 4194922b6fc9af3e3b4987dbf9c90db3aa10d3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:27:30 +0200 Subject: [PATCH 1891/2884] hw/sd/sdcard: Add sd_cmd_SET/CLR_WRITE_PROT handler (CMD28 & CMD29) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-60-philmd@linaro.org> --- hw/sd/sd.c | 91 +++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index e1c799c117..384ce77b36 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -241,7 +241,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [21] = "DPS_spec", [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", - [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", [30] = "SEND_WRITE_PROT", [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", [38] = "ERASE", @@ -1512,6 +1511,48 @@ static sd_rsp_type_t sd_cmd_PROGRAM_CSD(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->csd)); } +static sd_rsp_type_t sd_cmd_SET_CLR_WRITE_PROT(SDState *sd, SDRequest req, + bool is_write) +{ + uint64_t addr; + + if (sd->size > SDSC_MAX_CAPACITY) { + return sd_illegal; + } + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, is_write ? "SET_WRITE_PROT" : "CLR_WRITE_PROT", + addr, 1)) { + return sd_r1b; + } + + sd->state = sd_programming_state; + if (is_write) { + set_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); + } else { + clear_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); + } + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return sd_r1; +} + +/* CMD28 */ +static sd_rsp_type_t sd_cmd_SET_WRITE_PROT(SDState *sd, SDRequest req) +{ + return sd_cmd_SET_CLR_WRITE_PROT(sd, req, true); +} + +/* CMD29 */ +static sd_rsp_type_t sd_cmd_CLR_WRITE_PROT(SDState *sd, SDRequest req) +{ + return sd_cmd_SET_CLR_WRITE_PROT(sd, req, false); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; @@ -1602,50 +1643,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); /* Write protection (Class 6) */ - case 28: /* CMD28: SET_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } - addr = sd_req_get_address(sd, req); - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) { - return sd_r1b; - } - - sd->state = sd_programming_state; - set_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 29: /* CMD29: CLR_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } - addr = sd_req_get_address(sd, req); - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) { - return sd_r1b; - } - - sd->state = sd_programming_state; - clear_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - case 30: /* CMD30: SEND_WRITE_PROT */ if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; @@ -2316,6 +2313,8 @@ static const SDProto sd_proto_spi = { [17] = {2, sd_spi, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, [24] = {4, sd_spi, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, [27] = {4, sd_spi, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, + [28] = {6, sd_spi, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, + [29] = {6, sd_spi, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2354,6 +2353,8 @@ static const SDProto sd_proto_sd = { [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, + [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, + [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, From ff47c3593de92f9c583f346586691f86545b605a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:28:24 +0200 Subject: [PATCH 1892/2884] hw/sd/sdcard: Add sd_cmd_SEND_WRITE_PROT handler (CMD30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-61-philmd@linaro.org> --- hw/sd/sd.c | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 384ce77b36..b205cc4692 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -241,7 +241,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [21] = "DPS_spec", [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", - [30] = "SEND_WRITE_PROT", [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", [38] = "ERASE", [40] = "DPS_spec", @@ -1553,11 +1552,33 @@ static sd_rsp_type_t sd_cmd_CLR_WRITE_PROT(SDState *sd, SDRequest req) return sd_cmd_SET_CLR_WRITE_PROT(sd, req, false); } +/* CMD30 */ +static sd_rsp_type_t sd_cmd_SEND_WRITE_PROT(SDState *sd, SDRequest req) +{ + uint64_t addr; + uint32_t data; + + if (sd->size > SDSC_MAX_CAPACITY) { + return sd_illegal; + } + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "SEND_WRITE_PROT", addr, sd->blk_len)) { + return sd_r1; + } + + data = sd_wpbits(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data)); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; uint64_t addr; - uint32_t data; sd->last_cmd_name = sd_cmd_name(sd, req.cmd); /* CMD55 precedes an ACMD, so we are not interested in tracing it. @@ -1642,26 +1663,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 26: /* CMD26: PROGRAM_CID */ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); - /* Write protection (Class 6) */ - case 30: /* CMD30: SEND_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } - addr = sd_req_get_address(sd, req); - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "SEND_WRITE_PROT", - req.arg, sd->blk_len)) { - return sd_r1; - } - data = sd_wpbits(sd, req.arg); - return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data)); - - default: - break; - } - break; - /* Erase commands (Class 5) */ case 32: /* CMD32: ERASE_WR_BLK_START */ switch (sd->state) { @@ -2315,6 +2316,7 @@ static const SDProto sd_proto_spi = { [27] = {4, sd_spi, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, [28] = {6, sd_spi, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, [29] = {6, sd_spi, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, + [30] = {6, sd_spi, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2355,6 +2357,7 @@ static const SDProto sd_proto_sd = { [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, + [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, From b633bf2b45ff6aa396be8bf693c4b8ae2ce83485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:29:52 +0200 Subject: [PATCH 1893/2884] hw/sd/sdcard: Add sd_cmd_ERASE_WR_BLK_START/END handlers (CMD32 & CMD33) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-62-philmd@linaro.org> --- hw/sd/sd.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b205cc4692..d517a00ee1 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -241,7 +241,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [21] = "DPS_spec", [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", - [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", [38] = "ERASE", [40] = "DPS_spec", [42] = "LOCK_UNLOCK", @@ -1575,6 +1574,26 @@ static sd_rsp_type_t sd_cmd_SEND_WRITE_PROT(SDState *sd, SDRequest req) return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data)); } +/* CMD32 */ +static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_START(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->erase_start = req.arg; + return sd_r1; +} + +/* CMD33 */ +static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_END(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->erase_end = req.arg; + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; @@ -1664,28 +1683,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); /* Erase commands (Class 5) */ - case 32: /* CMD32: ERASE_WR_BLK_START */ - switch (sd->state) { - case sd_transfer_state: - sd->erase_start = req.arg; - return sd_r1; - - default: - break; - } - break; - - case 33: /* CMD33: ERASE_WR_BLK_END */ - switch (sd->state) { - case sd_transfer_state: - sd->erase_end = req.arg; - return sd_r1; - - default: - break; - } - break; - case 38: /* CMD38: ERASE */ switch (sd->state) { case sd_transfer_state: @@ -2317,6 +2314,8 @@ static const SDProto sd_proto_spi = { [28] = {6, sd_spi, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, [29] = {6, sd_spi, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, [30] = {6, sd_spi, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [32] = {5, sd_spi, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, + [33] = {5, sd_spi, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, @@ -2358,6 +2357,8 @@ static const SDProto sd_proto_sd = { [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [32] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, + [33] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, From 1dfa77d4e63b7794d7bb9a81f2b6b26a1af082d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:30:28 +0200 Subject: [PATCH 1894/2884] hw/sd/sdcard: Add sd_cmd_ERASE handler (CMD38) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-63-philmd@linaro.org> --- hw/sd/sd.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d517a00ee1..be9437141a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -241,7 +241,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [21] = "DPS_spec", [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", - [38] = "ERASE", [40] = "DPS_spec", [42] = "LOCK_UNLOCK", [54] = "SDIO_RSVD", [55] = "APP_CMD", @@ -1594,6 +1593,24 @@ static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_END(SDState *sd, SDRequest req) return sd_r1; } +/* CMD38 */ +static sd_rsp_type_t sd_cmd_ERASE(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + return sd_r1b; + } + + sd->state = sd_programming_state; + sd_erase(sd); + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return sd_r1b; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; @@ -1682,26 +1699,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 26: /* CMD26: PROGRAM_CID */ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); - /* Erase commands (Class 5) */ - case 38: /* CMD38: ERASE */ - switch (sd->state) { - case sd_transfer_state: - if (sd->csd[14] & 0x30) { - sd->card_status |= WP_VIOLATION; - return sd_r1b; - } - - sd->state = sd_programming_state; - sd_erase(sd); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ return sd_cmd_to_receivingdata(sd, req, 0, 0); @@ -2320,6 +2317,7 @@ static const SDProto sd_proto_spi = { [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, [37] = {10, sd_spi, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, + [38] = {5, sd_spi, "ERASE", sd_cmd_ERASE}, [50] = {10, sd_spi, "DIRECT_SECURE_READ", sd_cmd_optional}, [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, @@ -2363,6 +2361,7 @@ static const SDProto sd_proto_sd = { [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, [37] = {10, sd_ac, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, + [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE}, [43] = {1, sd_ac, "Q_MANAGEMENT", sd_cmd_optional}, [44] = {1, sd_ac, "Q_TASK_INFO_A", sd_cmd_optional}, [45] = {1, sd_ac, "Q_TASK_INFO_B", sd_cmd_optional}, From 29d247ad63a7cfc0c3b2ec2662c71853e35d6a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:32:18 +0200 Subject: [PATCH 1895/2884] hw/sd/sdcard: Add sd_cmd_LOCK_UNLOCK handler (CMD42) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-64-philmd@linaro.org> --- hw/sd/sd.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index be9437141a..da344589f2 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -242,7 +242,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", [40] = "DPS_spec", - [42] = "LOCK_UNLOCK", [54] = "SDIO_RSVD", [55] = "APP_CMD", [56] = "GEN_CMD", [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", @@ -1611,6 +1610,12 @@ static sd_rsp_type_t sd_cmd_ERASE(SDState *sd, SDRequest req) return sd_r1b; } +/* CMD42 */ +static sd_rsp_type_t sd_cmd_LOCK_UNLOCK(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, 0); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint16_t rca; @@ -1699,10 +1704,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 26: /* CMD26: PROGRAM_CID */ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); - /* Lock card commands (Class 7) */ - case 42: /* CMD42: LOCK_UNLOCK */ - return sd_cmd_to_receivingdata(sd, req, 0, 0); - /* Application specific commands (Class 8) */ case 55: /* CMD55: APP_CMD */ rca = sd_req_get_rca(sd, req); @@ -2318,6 +2319,7 @@ static const SDProto sd_proto_spi = { [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, [37] = {10, sd_spi, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, [38] = {5, sd_spi, "ERASE", sd_cmd_ERASE}, + [42] = {7, sd_spi, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, [50] = {10, sd_spi, "DIRECT_SECURE_READ", sd_cmd_optional}, [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, @@ -2362,6 +2364,7 @@ static const SDProto sd_proto_sd = { [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, [37] = {10, sd_ac, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE}, + [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, [43] = {1, sd_ac, "Q_MANAGEMENT", sd_cmd_optional}, [44] = {1, sd_ac, "Q_TASK_INFO_A", sd_cmd_optional}, [45] = {1, sd_ac, "Q_TASK_INFO_B", sd_cmd_optional}, From a171b753365d308b98d5e4db4dafa55369eea917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:35:19 +0200 Subject: [PATCH 1896/2884] hw/sd/sdcard: Add sd_cmd_APP_CMD handler (CMD55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-65-philmd@linaro.org> --- hw/sd/sd.c | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index da344589f2..f5548d5667 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -242,7 +242,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", [40] = "DPS_spec", - [54] = "SDIO_RSVD", [55] = "APP_CMD", [56] = "GEN_CMD", [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", @@ -1616,9 +1615,34 @@ static sd_rsp_type_t sd_cmd_LOCK_UNLOCK(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, 0, 0); } +/* CMD55 */ +static sd_rsp_type_t sd_cmd_APP_CMD(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_ready_state: + case sd_identification_state: + case sd_inactive_state: + return sd_invalid_state_for_cmd(sd, req); + case sd_idle_state: + if (!sd_is_spi(sd) && sd_req_get_rca(sd, req) != 0x0000) { + qemu_log_mask(LOG_GUEST_ERROR, + "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd); + } + /* fall-through */ + default: + break; + } + if (!sd_is_spi(sd) && !sd_req_rca_same(sd, req)) { + return sd_r0; + } + sd->expecting_acmd = true; + sd->card_status |= APP_CMD; + + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { - uint16_t rca; uint64_t addr; sd->last_cmd_name = sd_cmd_name(sd, req.cmd); @@ -1705,29 +1729,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); /* Application specific commands (Class 8) */ - case 55: /* CMD55: APP_CMD */ - rca = sd_req_get_rca(sd, req); - switch (sd->state) { - case sd_ready_state: - case sd_identification_state: - return sd_illegal; - case sd_idle_state: - if (rca) { - qemu_log_mask(LOG_GUEST_ERROR, - "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd); - } - default: - break; - } - if (!sd_is_spi(sd)) { - if (sd->rca != rca) { - return sd_r0; - } - } - sd->expecting_acmd = true; - sd->card_status |= APP_CMD; - return sd_r1; - case 56: /* CMD56: GEN_CMD */ switch (sd->state) { case sd_transfer_state: @@ -2323,6 +2324,7 @@ static const SDProto sd_proto_spi = { [50] = {10, sd_spi, "DIRECT_SECURE_READ", sd_cmd_optional}, [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, + [55] = {8, sd_spi, "APP_CMD", sd_cmd_APP_CMD}, [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional}, }, .acmd = { @@ -2375,6 +2377,7 @@ static const SDProto sd_proto_sd = { [50] = {10, sd_adtc, "DIRECT_SECURE_READ", sd_cmd_optional}, [52] = {9, sd_bc, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_bc, "IO_RW_EXTENDED", sd_cmd_optional}, + [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD}, [57] = {10, sd_adtc, "DIRECT_SECURE_WRITE", sd_cmd_optional}, [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, From 8aaae7fd191cdb7f72d2d8956d92d3acaf7cd7a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:38:31 +0200 Subject: [PATCH 1897/2884] hw/sd/sdcard: Add spi_cmd_READ_OCR handler (CMD58) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-67-philmd@linaro.org> --- hw/sd/sd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index f5548d5667..08fb3cf9d3 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1641,6 +1641,12 @@ static sd_rsp_type_t sd_cmd_APP_CMD(SDState *sd, SDRequest req) return sd_r1; } +/* CMD58 */ +static sd_rsp_type_t spi_cmd_READ_OCR(SDState *sd, SDRequest req) +{ + return sd_r3; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1744,9 +1750,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 58: /* CMD58: READ_OCR (SPI) */ - return sd_r3; - case 59: /* CMD59: CRC_ON_OFF (SPI) */ return sd_r1; @@ -2326,6 +2329,7 @@ static const SDProto sd_proto_spi = { [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, [55] = {8, sd_spi, "APP_CMD", sd_cmd_APP_CMD}, [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional}, + [58] = {0, sd_spi, "READ_OCR", spi_cmd_READ_OCR}, }, .acmd = { [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, From 95e9305bf9422781ba10fbb327e4b5e13aa1e450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:39:08 +0200 Subject: [PATCH 1898/2884] hw/sd/sdcard: Add spi_cmd_CRC_ON_OFF handler (CMD59) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-68-philmd@linaro.org> --- hw/sd/sd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 08fb3cf9d3..31cebe609c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1647,6 +1647,12 @@ static sd_rsp_type_t spi_cmd_READ_OCR(SDState *sd, SDRequest req) return sd_r3; } +/* CMD59 */ +static sd_rsp_type_t spi_cmd_CRC_ON_OFF(SDState *sd, SDRequest req) +{ + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1750,9 +1756,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 59: /* CMD59: CRC_ON_OFF (SPI) */ - return sd_r1; - default: qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; @@ -2330,6 +2333,7 @@ static const SDProto sd_proto_spi = { [55] = {8, sd_spi, "APP_CMD", sd_cmd_APP_CMD}, [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional}, [58] = {0, sd_spi, "READ_OCR", spi_cmd_READ_OCR}, + [59] = {0, sd_spi, "CRC_ON_OFF", spi_cmd_CRC_ON_OFF}, }, .acmd = { [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, From 55f2645eab67e712da2a776e5798412e37ae488c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:42:52 +0200 Subject: [PATCH 1899/2884] hw/sd/sdcard: Add sd_acmd_SET_BUS_WIDTH handler (ACMD6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-69-philmd@linaro.org> --- hw/sd/sd.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 31cebe609c..fed95563b8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -258,7 +258,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) static const char *sd_acmd_name(SDState *sd, uint8_t cmd) { static const char *acmd_abbrev[SDMMC_CMD_MAX] = { - [6] = "SET_BUS_WIDTH", [13] = "SD_STATUS", [14] = "DPS_spec", [15] = "DPS_spec", [16] = "DPS_spec", @@ -1653,6 +1652,18 @@ static sd_rsp_type_t spi_cmd_CRC_ON_OFF(SDState *sd, SDRequest req) return sd_r1; } +/* ACMD6 */ +static sd_rsp_type_t sd_acmd_SET_BUS_WIDTH(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd->sd_status[0] &= 0x3f; + sd->sd_status[0] |= (req.arg & 0x03) << 6; + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1777,18 +1788,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 6: /* ACMD6: SET_BUS_WIDTH */ - switch (sd->state) { - case sd_transfer_state: - sd->sd_status[0] &= 0x3f; - sd->sd_status[0] |= (req.arg & 0x03) << 6; - return sd_r1; - - default: - break; - } - break; - case 13: /* ACMD13: SD_STATUS */ switch (sd->state) { case sd_transfer_state: @@ -2390,6 +2389,9 @@ static const SDProto sd_proto_sd = { [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, }, + .acmd = { + [6] = {8, sd_ac, "SET_BUS_WIDTH", sd_acmd_SET_BUS_WIDTH}, + }, }; static void sd_instance_init(Object *obj) From cc7c6bda3f16fc8c91daa38b1c96506e49fcc50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:43:53 +0200 Subject: [PATCH 1900/2884] hw/sd/sdcard: Add sd_acmd_SD_STATUS handler (ACMD13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-70-philmd@linaro.org> --- hw/sd/sd.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index fed95563b8..8b31e0b41c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -258,7 +258,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) static const char *sd_acmd_name(SDState *sd, uint8_t cmd) { static const char *acmd_abbrev[SDMMC_CMD_MAX] = { - [13] = "SD_STATUS", [14] = "DPS_spec", [15] = "DPS_spec", [16] = "DPS_spec", [18] = "SECU_spec", @@ -1664,6 +1663,13 @@ static sd_rsp_type_t sd_acmd_SET_BUS_WIDTH(SDState *sd, SDRequest req) return sd_r1; } +/* ACMD13 */ +static sd_rsp_type_t sd_acmd_SD_STATUS(SDState *sd, SDRequest req) +{ + return sd_cmd_to_sendingdata(sd, req, 0, + sd->sd_status, sizeof(sd->sd_status)); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1788,18 +1794,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 13: /* ACMD13: SD_STATUS */ - switch (sd->state) { - case sd_transfer_state: - return sd_cmd_to_sendingdata(sd, req, 0, - sd->sd_status, - sizeof(sd->sd_status)); - - default: - break; - } - break; - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ switch (sd->state) { case sd_transfer_state: @@ -2335,6 +2329,7 @@ static const SDProto sd_proto_spi = { [59] = {0, sd_spi, "CRC_ON_OFF", spi_cmd_CRC_ON_OFF}, }, .acmd = { + [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS}, [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, }; @@ -2391,6 +2386,7 @@ static const SDProto sd_proto_sd = { }, .acmd = { [6] = {8, sd_ac, "SET_BUS_WIDTH", sd_acmd_SET_BUS_WIDTH}, + [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS}, }, }; From 225e70fb218b274f1175af515230619dda4c7163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:44:53 +0200 Subject: [PATCH 1901/2884] hw/sd/sdcard: Add sd_acmd_SEND_NUM_WR_BLOCKS handler (ACMD22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-71-philmd@linaro.org> --- hw/sd/sd.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8b31e0b41c..6e9ab92a1a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -261,7 +261,7 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) [14] = "DPS_spec", [15] = "DPS_spec", [16] = "DPS_spec", [18] = "SECU_spec", - [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT", + [23] = "SET_WR_BLK_ERASE_COUNT", [42] = "SET_CLR_CARD_DETECT", [51] = "SEND_SCR", [52] = "SECU_spec", [53] = "SECU_spec", @@ -1670,6 +1670,13 @@ static sd_rsp_type_t sd_acmd_SD_STATUS(SDState *sd, SDRequest req) sd->sd_status, sizeof(sd->sd_status)); } +/* ACMD22 */ +static sd_rsp_type_t sd_acmd_SEND_NUM_WR_BLOCKS(SDState *sd, SDRequest req) +{ + return sd_cmd_to_sendingdata(sd, req, 0, + &sd->blk_written, sizeof(sd->blk_written)); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1794,18 +1801,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ - switch (sd->state) { - case sd_transfer_state: - return sd_cmd_to_sendingdata(sd, req, 0, - &sd->blk_written, - sizeof(sd->blk_written)); - - default: - break; - } - break; - case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ switch (sd->state) { case sd_transfer_state: @@ -2330,6 +2325,7 @@ static const SDProto sd_proto_spi = { }, .acmd = { [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS}, + [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, }; @@ -2387,6 +2383,7 @@ static const SDProto sd_proto_sd = { .acmd = { [6] = {8, sd_ac, "SET_BUS_WIDTH", sd_acmd_SET_BUS_WIDTH}, [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS}, + [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, }, }; From f443003ff43a6473da0499393dfb11a4b9b4bd07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:45:33 +0200 Subject: [PATCH 1902/2884] hw/sd/sdcard: Add sd_acmd_SET_WR_BLK_ERASE_COUNT handler (ACMD23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-72-philmd@linaro.org> --- hw/sd/sd.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 6e9ab92a1a..c56790f091 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -261,7 +261,6 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) [14] = "DPS_spec", [15] = "DPS_spec", [16] = "DPS_spec", [18] = "SECU_spec", - [23] = "SET_WR_BLK_ERASE_COUNT", [42] = "SET_CLR_CARD_DETECT", [51] = "SEND_SCR", [52] = "SECU_spec", [53] = "SECU_spec", @@ -1677,6 +1676,15 @@ static sd_rsp_type_t sd_acmd_SEND_NUM_WR_BLOCKS(SDState *sd, SDRequest req) &sd->blk_written, sizeof(sd->blk_written)); } +/* ACMD23 */ +static sd_rsp_type_t sd_acmd_SET_WR_BLK_ERASE_COUNT(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1801,16 +1809,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ - switch (sd->state) { - case sd_transfer_state: - return sd_r1; - - default: - break; - } - break; - case 41: /* ACMD41: SD_APP_OP_COND */ if (sd->state != sd_idle_state) { break; @@ -2326,6 +2324,7 @@ static const SDProto sd_proto_spi = { .acmd = { [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS}, [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, + [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, }, }; @@ -2384,6 +2383,7 @@ static const SDProto sd_proto_sd = { [6] = {8, sd_ac, "SET_BUS_WIDTH", sd_acmd_SET_BUS_WIDTH}, [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS}, [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, + [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, }, }; From c79cbe44ec5a6c2c50ae1fde73e029d4c0386758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:46:39 +0200 Subject: [PATCH 1903/2884] hw/sd/sdcard: Add sd_acmd_SD_APP_OP_COND handler (ACMD41) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-73-philmd@linaro.org> --- hw/sd/sd.c | 82 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index c56790f091..207deb07e6 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1685,6 +1685,50 @@ static sd_rsp_type_t sd_acmd_SET_WR_BLK_ERASE_COUNT(SDState *sd, SDRequest req) return sd_r1; } +/* ACMD41 */ +static sd_rsp_type_t sd_acmd_SD_APP_OP_COND(SDState *sd, SDRequest req) +{ + if (sd->state != sd_idle_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + /* + * If it's the first ACMD41 since reset, we need to decide + * whether to power up. If this is not an enquiry ACMD41, + * we immediately report power on and proceed below to the + * ready state, but if it is, we set a timer to model a + * delay for power up. This works around a bug in EDK2 + * UEFI, which sends an initial enquiry ACMD41, but + * assumes that the card is in ready state as soon as it + * sees the power up bit set. + */ + if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) { + if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { + timer_del(sd->ocr_power_timer); + sd_ocr_powerup(sd); + } else { + trace_sdcard_inquiry_cmd41(); + if (!timer_pending(sd->ocr_power_timer)) { + timer_mod_ns(sd->ocr_power_timer, + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + OCR_POWER_DELAY_NS)); + } + } + } + + if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) { + /* + * We accept any voltage. 10000 V is nothing. + * + * Once we're powered up, we advance straight to ready state + * unless it's an enquiry ACMD41 (bits 23:0 == 0). + */ + sd->state = sd_ready_state; + } + + return sd_r3; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1809,43 +1853,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 41: /* ACMD41: SD_APP_OP_COND */ - if (sd->state != sd_idle_state) { - break; - } - /* If it's the first ACMD41 since reset, we need to decide - * whether to power up. If this is not an enquiry ACMD41, - * we immediately report power on and proceed below to the - * ready state, but if it is, we set a timer to model a - * delay for power up. This works around a bug in EDK2 - * UEFI, which sends an initial enquiry ACMD41, but - * assumes that the card is in ready state as soon as it - * sees the power up bit set. */ - if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) { - if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { - timer_del(sd->ocr_power_timer); - sd_ocr_powerup(sd); - } else { - trace_sdcard_inquiry_cmd41(); - if (!timer_pending(sd->ocr_power_timer)) { - timer_mod_ns(sd->ocr_power_timer, - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + OCR_POWER_DELAY_NS)); - } - } - } - - if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) { - /* We accept any voltage. 10000 V is nothing. - * - * Once we're powered up, we advance straight to ready state - * unless it's an enquiry ACMD41 (bits 23:0 == 0). - */ - sd->state = sd_ready_state; - } - - return sd_r3; - case 42: /* ACMD42: SET_CLR_CARD_DETECT */ switch (sd->state) { case sd_transfer_state: @@ -2384,6 +2391,7 @@ static const SDProto sd_proto_sd = { [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS}, [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, + [41] = {8, sd_bcr, "SD_APP_OP_COND", sd_acmd_SD_APP_OP_COND}, }, }; From 7614306f2a67bf395472921ed7f16c7fcded0db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:47:35 +0200 Subject: [PATCH 1904/2884] hw/sd/sdcard: Add sd_acmd_SET_CLR_CARD_DETECT handler (ACMD42) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-74-philmd@linaro.org> --- hw/sd/sd.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 207deb07e6..698d64d2cb 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -261,7 +261,6 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) [14] = "DPS_spec", [15] = "DPS_spec", [16] = "DPS_spec", [18] = "SECU_spec", - [42] = "SET_CLR_CARD_DETECT", [51] = "SEND_SCR", [52] = "SECU_spec", [53] = "SECU_spec", [54] = "SECU_spec", @@ -1729,6 +1728,17 @@ static sd_rsp_type_t sd_acmd_SD_APP_OP_COND(SDState *sd, SDRequest req) return sd_r3; } +/* ACMD42 */ +static sd_rsp_type_t sd_acmd_SET_CLR_CARD_DETECT(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + /* Bringing in the 50KOhm pull-up resistor... Done. */ + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1853,17 +1863,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 42: /* ACMD42: SET_CLR_CARD_DETECT */ - switch (sd->state) { - case sd_transfer_state: - /* Bringing in the 50KOhm pull-up resistor... Done. */ - return sd_r1; - - default: - break; - } - break; - case 51: /* ACMD51: SEND_SCR */ switch (sd->state) { case sd_transfer_state: @@ -2333,6 +2332,7 @@ static const SDProto sd_proto_spi = { [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [42] = {8, sd_spi, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, }, }; @@ -2392,6 +2392,7 @@ static const SDProto sd_proto_sd = { [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, [41] = {8, sd_bcr, "SD_APP_OP_COND", sd_acmd_SD_APP_OP_COND}, + [42] = {8, sd_ac, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, }, }; From 8442e1625ba6723bee2c6d0fdb7207a3e27a2b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Jun 2024 23:48:03 +0200 Subject: [PATCH 1905/2884] hw/sd/sdcard: Add sd_acmd_SEND_SCR handler (ACMD51) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240628070216.92609-75-philmd@linaro.org> --- hw/sd/sd.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 698d64d2cb..552957b2e5 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -261,7 +261,6 @@ static const char *sd_acmd_name(SDState *sd, uint8_t cmd) [14] = "DPS_spec", [15] = "DPS_spec", [16] = "DPS_spec", [18] = "SECU_spec", - [51] = "SEND_SCR", [52] = "SECU_spec", [53] = "SECU_spec", [54] = "SECU_spec", [56] = "SECU_spec", [57] = "SECU_spec", @@ -1739,6 +1738,12 @@ static sd_rsp_type_t sd_acmd_SET_CLR_CARD_DETECT(SDState *sd, SDRequest req) return sd_r1; } +/* ACMD51 */ +static sd_rsp_type_t sd_acmd_SEND_SCR(SDState *sd, SDRequest req) +{ + return sd_cmd_to_sendingdata(sd, req, 0, sd->scr, sizeof(sd->scr)); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint64_t addr; @@ -1863,16 +1868,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (req.cmd) { - case 51: /* ACMD51: SEND_SCR */ - switch (sd->state) { - case sd_transfer_state: - return sd_cmd_to_sendingdata(sd, req, 0, sd->scr, sizeof(sd->scr)); - - default: - break; - } - break; - case 18: /* Reserved for SD security applications */ case 25: case 26: @@ -2333,6 +2328,7 @@ static const SDProto sd_proto_spi = { [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, [42] = {8, sd_spi, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, + [51] = {8, sd_spi, "SEND_SCR", sd_acmd_SEND_SCR}, }, }; @@ -2393,6 +2389,7 @@ static const SDProto sd_proto_sd = { [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, [41] = {8, sd_bcr, "SD_APP_OP_COND", sd_acmd_SD_APP_OP_COND}, [42] = {8, sd_ac, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, + [51] = {8, sd_adtc, "SEND_SCR", sd_acmd_SEND_SCR}, }, }; From 0aa7f10c7af222a32e49e38df8383e394a0aa5c3 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:00:31 +0200 Subject: [PATCH 1906/2884] qapi: clarify that the default is backend dependent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default value of the @share option of the @MemoryBackendProperties really depends on the backend type, so let's document the default values in the same place where we define the option to avoid dispersing the information. Cc: David Hildenbrand Suggested-by: Markus Armbruster Reviewed-by: Markus Armbruster Signed-off-by: Stefano Garzarella Message-Id: <20240618100043.144657-2-sgarzare@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- qapi/qom.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qapi/qom.json b/qapi/qom.json index 8bd299265e..9b8f6a7ab5 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -600,7 +600,9 @@ # preallocation threads (default: none) (since 7.2) # # @share: if false, the memory is private to QEMU; if true, it is -# shared (default: false) +# shared (default false for backends memory-backend-file and +# memory-backend-ram, true for backends memory-backend-epc and +# memory-backend-memfd) # # @reserve: if true, reserve swap space (or huge pages) if applicable # (default: true) (since 6.1) @@ -700,8 +702,6 @@ # # Properties for memory-backend-memfd objects. # -# The @share boolean option is true by default with memfd. -# # @hugetlb: if true, the file to be created resides in the hugetlbfs # filesystem (default: false) # @@ -726,8 +726,6 @@ # # Properties for memory-backend-epc objects. # -# The @share boolean option is true by default with epc -# # The @merge boolean option is false by default with epc # # The @dump boolean option is false by default with epc From 516dfbb783484959cf33f051864f2e44cbed45ca Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:00:32 +0200 Subject: [PATCH 1907/2884] libvhost-user: set msg.msg_control to NULL when it is empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some OS (e.g. macOS) sendmsg() returns -1 (errno EINVAL) if the `struct msghdr` has the field `msg_controllen` set to 0, but `msg_control` is not NULL. Reviewed-by: Eric Blake Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Message-Id: <20240618100043.144657-3-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- subprojects/libvhost-user/libvhost-user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 8adb277d54..53bf1adda6 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -632,6 +632,7 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) memcpy(CMSG_DATA(cmsg), vmsg->fds, fdsize); } else { msg.msg_controllen = 0; + msg.msg_control = NULL; } do { From 92b58bc7e9086e489295040d408118a81c47b31d Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:00:33 +0200 Subject: [PATCH 1908/2884] libvhost-user: fail vu_message_write() if sendmsg() is failing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In vu_message_write() we use sendmsg() to send the message header, then a write() to send the payload. If sendmsg() fails we should avoid sending the payload, since we were unable to send the header. Discovered before fixing the issue with the previous patch, where sendmsg() failed on macOS due to wrong parameters, but the frontend still sent the payload which the backend incorrectly interpreted as a wrong header. Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Stefan Hajnoczi Reviewed-by: David Hildenbrand Signed-off-by: Stefano Garzarella Message-Id: <20240618100043.144657-4-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- subprojects/libvhost-user/libvhost-user.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 53bf1adda6..ea27683dac 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -639,6 +639,11 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) rc = sendmsg(conn_fd, &msg, 0); } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc <= 0) { + vu_panic(dev, "Error while writing: %s", strerror(errno)); + return false; + } + if (vmsg->size) { do { if (vmsg->data) { From ebdede644bbf5744f91dbe0d39742f17b03c4e10 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:00:34 +0200 Subject: [PATCH 1909/2884] libvhost-user: mask F_INFLIGHT_SHMFD if memfd is not supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libvhost-user will panic when receiving VHOST_USER_GET_INFLIGHT_FD message if MFD_ALLOW_SEALING is not defined, since it's not able to create a memfd. VHOST_USER_GET_INFLIGHT_FD is used only if VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD is negotiated. So, let's mask that feature if the backend is not able to properly handle these messages. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Stefan Hajnoczi Reviewed-by: David Hildenbrand Signed-off-by: Stefano Garzarella Message-Id: <20240618100043.144657-5-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- subprojects/libvhost-user/libvhost-user.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index ea27683dac..9c630c2170 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -1674,6 +1674,17 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) features |= dev->iface->get_protocol_features(dev); } +#ifndef MFD_ALLOW_SEALING + /* + * If MFD_ALLOW_SEALING is not defined, we are not able to handle + * VHOST_USER_GET_INFLIGHT_FD messages, since we can't create a memfd. + * Those messages are used only if VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD + * is negotiated. A device implementation can enable it, so let's mask + * it to avoid a runtime panic. + */ + features &= ~(1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD); +#endif + vmsg_set_reply_u64(vmsg, features); return true; } From 4c58843e5d3192c67394b28a3330144ea56eefac Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:00:35 +0200 Subject: [PATCH 1910/2884] vhost-user-server: do not set memory fd non-blocking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In vhost-user-server we set all fd received from the other peer in non-blocking mode. For some of them (e.g. memfd, shm_open, etc.) it's not really needed, because we don't use these fd with blocking operations, but only to map memory. In addition, in some systems this operation can fail (e.g. in macOS setting an fd returned by shm_open() non-blocking fails with errno = ENOTTY). So, let's avoid setting fd non-blocking for those messages that we know carry memory fd (e.g. VHOST_USER_ADD_MEM_REG, VHOST_USER_SET_MEM_TABLE). Reviewed-by: Daniel P. Berrangé Acked-by: Stefan Hajnoczi Reviewed-by: David Hildenbrand Signed-off-by: Stefano Garzarella Message-Id: <20240618100043.144657-6-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- util/vhost-user-server.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index 3bfb1ad3ec..b19229074a 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -65,6 +65,18 @@ static void vmsg_close_fds(VhostUserMsg *vmsg) static void vmsg_unblock_fds(VhostUserMsg *vmsg) { int i; + + /* + * These messages carry fd used to map memory, not to send/receive messages, + * so this operation is useless. In addition, in some systems this + * operation can fail (e.g. in macOS setting an fd returned by shm_open() + * non-blocking fails with errno = ENOTTY) + */ + if (vmsg->request == VHOST_USER_ADD_MEM_REG || + vmsg->request == VHOST_USER_SET_MEM_TABLE) { + return; + } + for (i = 0; i < vmsg->fd_num; i++) { qemu_socket_set_nonblock(vmsg->fds[i]); } From 03582094da1ea7ce978cec58008c81f7458ee8dd Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:04:39 +0200 Subject: [PATCH 1911/2884] contrib/vhost-user-blk: fix bind() using the right size of the address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS passing `-s /tmp/vhost.socket` parameter to the vhost-user-blk application, the bind was done on `/tmp/vhost.socke` pathname, missing the last character. This sounds like one of the portability problems described in the unix(7) manpage: Pathname sockets When binding a socket to a pathname, a few rules should be observed for maximum portability and ease of coding: • The pathname in sun_path should be null-terminated. • The length of the pathname, including the terminating null byte, should not exceed the size of sun_path. • The addrlen argument that describes the enclosing sockaddr_un structure should have a value of at least: offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)+1 or, more simply, addrlen can be specified as sizeof(struct sockaddr_un). So let's follow the last advice and simplify the code as well. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Stefan Hajnoczi Reviewed-by: David Hildenbrand Signed-off-by: Stefano Garzarella Message-Id: <20240618100440.145664-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- contrib/vhost-user-blk/vhost-user-blk.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 89e5f11a64..a8ab9269a2 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -469,7 +469,6 @@ static int unix_sock_new(char *unix_fn) { int sock; struct sockaddr_un un; - size_t len; assert(unix_fn); @@ -481,10 +480,9 @@ static int unix_sock_new(char *unix_fn) un.sun_family = AF_UNIX; (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn); - len = sizeof(un.sun_family) + strlen(un.sun_path); (void)unlink(unix_fn); - if (bind(sock, (struct sockaddr *)&un, len) < 0) { + if (bind(sock, (struct sockaddr *)&un, sizeof(un)) < 0) { perror("bind"); goto fail; } From 5ab04420c3de11ae4a573b08b53584a2a0c5dd00 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:04:47 +0200 Subject: [PATCH 1912/2884] contrib/vhost-user-*: use QEMU bswap helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's replace the calls to le*toh() and htole*() with qemu/bswap.h helpers to make the code more portable. Suggested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Acked-by: Stefan Hajnoczi Reviewed-by: David Hildenbrand Signed-off-by: Stefano Garzarella Message-Id: <20240618100447.145697-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- contrib/vhost-user-blk/vhost-user-blk.c | 9 +++++---- contrib/vhost-user-input/main.c | 16 ++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index a8ab9269a2..9492146855 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "standard-headers/linux/virtio_blk.h" #include "libvhost-user-glib.h" @@ -194,8 +195,8 @@ vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt, #if defined(__linux__) && defined(BLKDISCARD) && defined(BLKZEROOUT) VubDev *vdev_blk = req->vdev_blk; desc = buf; - uint64_t range[2] = { le64toh(desc->sector) << 9, - le32toh(desc->num_sectors) << 9 }; + uint64_t range[2] = { le64_to_cpu(desc->sector) << 9, + le32_to_cpu(desc->num_sectors) << 9 }; if (type == VIRTIO_BLK_T_DISCARD) { if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) { g_free(buf); @@ -267,13 +268,13 @@ static int vub_virtio_process_req(VubDev *vdev_blk, req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base; in_num--; - type = le32toh(req->out->type); + type = le32_to_cpu(req->out->type); switch (type & ~VIRTIO_BLK_T_BARRIER) { case VIRTIO_BLK_T_IN: case VIRTIO_BLK_T_OUT: { ssize_t ret = 0; bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = le64toh(req->out->sector); + req->sector_num = le64_to_cpu(req->out->sector); if (is_write) { ret = vub_writev(req, &elem->out_sg[1], out_num); } else { diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c index 081230da54..f3362d41ac 100644 --- a/contrib/vhost-user-input/main.c +++ b/contrib/vhost-user-input/main.c @@ -51,8 +51,8 @@ static void vi_input_send(VuInput *vi, struct virtio_input_event *event) vi->queue[vi->qindex++].event = *event; /* ... until we see a report sync ... */ - if (event->type != htole16(EV_SYN) || - event->code != htole16(SYN_REPORT)) { + if (event->type != cpu_to_le16(EV_SYN) || + event->code != cpu_to_le16(SYN_REPORT)) { return; } @@ -103,9 +103,9 @@ vi_evdev_watch(VuDev *dev, int condition, void *data) g_debug("input %d %d %d", evdev.type, evdev.code, evdev.value); - virtio.type = htole16(evdev.type); - virtio.code = htole16(evdev.code); - virtio.value = htole32(evdev.value); + virtio.type = cpu_to_le16(evdev.type); + virtio.code = cpu_to_le16(evdev.code); + virtio.value = cpu_to_le32(evdev.value); vi_input_send(vi, &virtio); } } @@ -124,9 +124,9 @@ static void vi_handle_status(VuInput *vi, virtio_input_event *event) evdev.input_event_sec = tval.tv_sec; evdev.input_event_usec = tval.tv_usec; - evdev.type = le16toh(event->type); - evdev.code = le16toh(event->code); - evdev.value = le32toh(event->value); + evdev.type = le16_to_cpu(event->type); + evdev.code = le16_to_cpu(event->code); + evdev.value = le32_to_cpu(event->value); rc = write(vi->evdevfd, &evdev, sizeof(evdev)); if (rc == -1) { From bd385a5298d7062668e804d73944d52aec9549f1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 11 Apr 2024 15:06:01 +0200 Subject: [PATCH 1913/2884] qcow2: Don't open data_file with BDRV_O_NO_IO One use case for 'qemu-img info' is verifying that untrusted images don't reference an unwanted external file, be it as a backing file or an external data file. To make sure that calling 'qemu-img info' can't already have undesired side effects with a malicious image, just don't open the data file at all with BDRV_O_NO_IO. If nothing ever tries to do I/O, we don't need to have it open. This changes the output of iotests case 061, which used 'qemu-img info' to show that opening an image with an invalid data file fails. After this patch, it succeeds. Replace this part of the test with a qemu-io call, but keep the final 'qemu-img info' to show that the invalid data file is correctly displayed in the output. Fixes: CVE-2024-4467 Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek --- block/qcow2.c | 17 ++++++++++++++++- tests/qemu-iotests/061 | 6 ++++-- tests/qemu-iotests/061.out | 8 ++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 10883a2494..70b19730a3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1636,7 +1636,22 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - if (open_data_file) { + if (open_data_file && (flags & BDRV_O_NO_IO)) { + /* + * Don't open the data file for 'qemu-img info' so that it can be used + * to verify that an untrusted qcow2 image doesn't refer to external + * files. + * + * Note: This still makes has_data_file() return true. + */ + if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { + s->data_file = NULL; + } else { + s->data_file = bs->file; + } + qdict_extract_subqdict(options, NULL, "data-file."); + qdict_del(options, "data-file"); + } else if (open_data_file) { /* Open external data file */ bdrv_graph_co_rdunlock(); s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs, diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 53c7d428e3..b71ac097d1 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -326,12 +326,14 @@ $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" echo _make_test_img -o "compat=1.1,data_file=$TEST_IMG.data" 64M $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" -_img_info --format-specific +$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts echo $QEMU_IMG amend -o "data_file=" --image-opts "data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -_img_info --format-specific +$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts echo diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 139fc68177..24c33add7c 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -545,7 +545,9 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: data-file can only be set for images that use an external data file Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'foo': No such file or directory +qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'foo': No such file or directory +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) @@ -560,7 +562,9 @@ Format specific information: corrupt: false extended l2: false -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'data-file' is required for this image +qemu-io: can't open device TEST_DIR/t.IMGFMT: 'data-file' is required for this image +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) From 2eb42a728d27a43fdcad5f37d3f65706ce6deba5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 25 Apr 2024 14:49:40 +0200 Subject: [PATCH 1914/2884] iotests/244: Don't store data-file with protocol in image We want to disable filename parsing for data files because it's too easy to abuse in malicious image files. Make the test ready for the change by passing the data file explicitly in command line options. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek --- tests/qemu-iotests/244 | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244 index 3e61fa25bb..bb9cc6512f 100755 --- a/tests/qemu-iotests/244 +++ b/tests/qemu-iotests/244 @@ -215,9 +215,22 @@ $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG" $QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG" # blkdebug doesn't support copy offloading, so this tests the error path -$QEMU_IMG amend -f $IMGFMT -o "data_file=blkdebug::$TEST_IMG.data" "$TEST_IMG" -$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG" -$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG" +test_img_with_blkdebug="json:{ + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }, + 'data-file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': '$TEST_IMG.data' + } + } +}" +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$test_img_with_blkdebug" +$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$test_img_with_blkdebug" echo echo "=== Flushing should flush the data file ===" From 7e1110664ecbc4826f3c978ccb06b6c1bce823e6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 25 Apr 2024 14:49:40 +0200 Subject: [PATCH 1915/2884] iotests/270: Don't store data-file with json: prefix in image We want to disable filename parsing for data files because it's too easy to abuse in malicious image files. Make the test ready for the change by passing the data file explicitly in command line options. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek --- tests/qemu-iotests/270 | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/270 b/tests/qemu-iotests/270 index 74352342db..c37b674aa2 100755 --- a/tests/qemu-iotests/270 +++ b/tests/qemu-iotests/270 @@ -60,8 +60,16 @@ _make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \ # "write" 2G of data without using any space. # (qemu-img create does not like it, though, because null-co does not # support image creation.) -$QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \ - "$TEST_IMG" +test_img_with_null_data="json:{ + 'driver': '$IMGFMT', + 'file': { + 'filename': '$TEST_IMG' + }, + 'data-file': { + 'driver': 'null-co', + 'size':'4294967296' + } +}" # This gives us a range of: # 2^31 - 512 + 768 - 1 = 2^31 + 255 > 2^31 @@ -74,7 +82,7 @@ $QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \ # on L2 boundaries, we need large L2 tables; hence the cluster size of # 2 MB. (Anything from 256 kB should work, though, because then one L2 # table covers 8 GB.) -$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$test_img_with_null_data" | _filter_qemu_io _check_test_img From 7ead946998610657d38d1a505d5f25300d4ca613 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 25 Apr 2024 14:56:02 +0200 Subject: [PATCH 1916/2884] block: Parse filenames only when explicitly requested When handling image filenames from legacy options such as -drive or from tools, these filenames are parsed for protocol prefixes, including for the json:{} pseudo-protocol. This behaviour is intended for filenames that come directly from the command line and for backing files, which may come from the image file itself. Higher level management tools generally take care to verify that untrusted images don't contain a bad (or any) backing file reference; 'qemu-img info' is a suitable tool for this. However, for other files that can be referenced in images, such as qcow2 data files or VMDK extents, the string from the image file is usually not verified by management tools - and 'qemu-img info' wouldn't be suitable because in contrast to backing files, it already opens these other referenced files. So here the string should be interpreted as a literal local filename. More complex configurations need to be specified explicitly on the command line or in QMP. This patch changes bdrv_open_inherit() so that it only parses filenames if a new parameter parse_filename is true. It is set for the top level in bdrv_open(), for the file child and for the backing file child. All other callers pass false and disable filename parsing this way. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek --- block.c | 90 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/block.c b/block.c index c1cc313d21..c317de9eaa 100644 --- a/block.c +++ b/block.c @@ -86,6 +86,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, + bool parse_filename, Error **errp); static bool bdrv_recurse_has_child(BlockDriverState *bs, @@ -2055,7 +2056,8 @@ static void parse_json_protocol(QDict *options, const char **pfilename, * block driver has been specified explicitly. */ static int bdrv_fill_options(QDict **options, const char *filename, - int *flags, Error **errp) + int *flags, bool allow_parse_filename, + Error **errp) { const char *drvname; bool protocol = *flags & BDRV_O_PROTOCOL; @@ -2097,7 +2099,7 @@ static int bdrv_fill_options(QDict **options, const char *filename, if (protocol && filename) { if (!qdict_haskey(*options, "filename")) { qdict_put_str(*options, "filename", filename); - parse_filename = true; + parse_filename = allow_parse_filename; } else { error_setg(errp, "Can't specify 'file' and 'filename' options at " "the same time"); @@ -3660,7 +3662,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, } backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs, - &child_of_bds, bdrv_backing_role(bs), errp); + &child_of_bds, bdrv_backing_role(bs), true, + errp); if (!backing_hd) { bs->open_flags |= BDRV_O_NO_BACKING; error_prepend(errp, "Could not open backing file: "); @@ -3694,7 +3697,8 @@ free_exit: static BlockDriverState * bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, const BdrvChildClass *child_class, - BdrvChildRole child_role, bool allow_none, Error **errp) + BdrvChildRole child_role, bool allow_none, + bool parse_filename, Error **errp) { BlockDriverState *bs = NULL; QDict *image_options; @@ -3725,7 +3729,8 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, } bs = bdrv_open_inherit(filename, reference, image_options, 0, - parent, child_class, child_role, errp); + parent, child_class, child_role, parse_filename, + errp); if (!bs) { goto done; } @@ -3735,6 +3740,33 @@ done: return bs; } +static BdrvChild *bdrv_open_child_common(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + bool allow_none, bool parse_filename, + Error **errp) +{ + BlockDriverState *bs; + BdrvChild *child; + + GLOBAL_STATE_CODE(); + + bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, + child_role, allow_none, parse_filename, errp); + if (bs == NULL) { + return NULL; + } + + bdrv_graph_wrlock(); + child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, + errp); + bdrv_graph_wrunlock(); + + return child; +} + /* * Opens a disk image whose options are given as BlockdevRef in another block * device's options. @@ -3758,27 +3790,15 @@ BdrvChild *bdrv_open_child(const char *filename, BdrvChildRole child_role, bool allow_none, Error **errp) { - BlockDriverState *bs; - BdrvChild *child; - - GLOBAL_STATE_CODE(); - - bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, - child_role, allow_none, errp); - if (bs == NULL) { - return NULL; - } - - bdrv_graph_wrlock(); - child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, - errp); - bdrv_graph_wrunlock(); - - return child; + return bdrv_open_child_common(filename, options, bdref_key, parent, + child_class, child_role, allow_none, false, + errp); } /* - * Wrapper on bdrv_open_child() for most popular case: open primary child of bs. + * This does mostly the same as bdrv_open_child(), but for opening the primary + * child of a node. A notable difference from bdrv_open_child() is that it + * enables filename parsing for protocol names (including json:). * * @parent can move to a different AioContext in this function. */ @@ -3793,8 +3813,8 @@ int bdrv_open_file_child(const char *filename, role = parent->drv->is_filter ? (BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE; - if (!bdrv_open_child(filename, options, bdref_key, parent, - &child_of_bds, role, false, errp)) + if (!bdrv_open_child_common(filename, options, bdref_key, parent, + &child_of_bds, role, false, true, errp)) { return -EINVAL; } @@ -3839,7 +3859,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) } - bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, errp); + bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, false, + errp); obj = NULL; qobject_unref(obj); visit_free(v); @@ -3929,7 +3950,7 @@ static BlockDriverState * no_coroutine_fn bdrv_open_inherit(const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, - Error **errp) + bool parse_filename, Error **errp) { int ret; BlockBackend *file = NULL; @@ -3977,9 +3998,11 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, } /* json: syntax counts as explicit options, as if in the QDict */ - parse_json_protocol(options, &filename, &local_err); - if (local_err) { - goto fail; + if (parse_filename) { + parse_json_protocol(options, &filename, &local_err); + if (local_err) { + goto fail; + } } bs->explicit_options = qdict_clone_shallow(options); @@ -4004,7 +4027,8 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, parent->open_flags, parent->options); } - ret = bdrv_fill_options(&options, filename, &flags, &local_err); + ret = bdrv_fill_options(&options, filename, &flags, parse_filename, + &local_err); if (ret < 0) { goto fail; } @@ -4073,7 +4097,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, file_bs = bdrv_open_child_bs(filename, options, "file", bs, &child_of_bds, BDRV_CHILD_IMAGE, - true, &local_err); + true, true, &local_err); if (local_err) { goto fail; } @@ -4222,7 +4246,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, GLOBAL_STATE_CODE(); return bdrv_open_inherit(filename, reference, options, flags, NULL, - NULL, 0, errp); + NULL, 0, true, errp); } /* Return true if the NULL-terminated @list contains @str */ From 272d3decc19aebe87955f4ec6d0c6cc8790471f1 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Wed, 26 Jun 2024 22:22:13 -0400 Subject: [PATCH 1917/2884] util/cpuinfo-ppc: Fix building on OpenBSD OpenBSD does not support AT_HWCAP. Signed-off-by: Brad Smith Message-Id: Signed-off-by: Richard Henderson --- util/cpuinfo-ppc.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/util/cpuinfo-ppc.c b/util/cpuinfo-ppc.c index b2d8893a06..47af55aa0c 100644 --- a/util/cpuinfo-ppc.c +++ b/util/cpuinfo-ppc.c @@ -6,11 +6,13 @@ #include "qemu/osdep.h" #include "host/cpuinfo.h" -#include -#ifdef CONFIG_GETAUXVAL -# include -#else -# include "elf.h" +#ifdef CONFIG_LINUX +# include +# ifdef CONFIG_GETAUXVAL +# include +# else +# include "elf.h" +# endif #endif unsigned cpuinfo; @@ -19,16 +21,17 @@ unsigned cpuinfo; unsigned __attribute__((constructor)) cpuinfo_init(void) { unsigned info = cpuinfo; - unsigned long hwcap, hwcap2; if (info) { return info; } - hwcap = qemu_getauxval(AT_HWCAP); - hwcap2 = qemu_getauxval(AT_HWCAP2); info = CPUINFO_ALWAYS; +#ifdef CONFIG_LINUX + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); + /* Version numbers are monotonic, and so imply all lower versions. */ if (hwcap2 & PPC_FEATURE2_ARCH_3_1) { info |= CPUINFO_V3_1 | CPUINFO_V3_0 | CPUINFO_V2_07 | CPUINFO_V2_06; @@ -58,6 +61,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) } } } +#endif cpuinfo = info; return info; From fae7a427c787359fc4a3bc244541c5bd9e8e14bc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 10:27:21 +0200 Subject: [PATCH 1918/2884] meson: move shared_module() calls where modules are already walked Signed-off-by: Paolo Bonzini --- meson.build | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/meson.build b/meson.build index 54e6b09f4f..8909f8c87d 100644 --- a/meson.build +++ b/meson.build @@ -3602,6 +3602,7 @@ modinfo_files = [] block_mods = [] system_mods = [] +emulator_modules = [] foreach d, list : modules if not (d == 'block' ? have_block : have_system) continue @@ -3609,14 +3610,20 @@ foreach d, list : modules foreach m, module_ss : list if enable_modules + module_ss.add(modulecommon) module_ss = module_ss.apply(config_all_devices, strict: false) sl = static_library(d + '-' + m, [genh, module_ss.sources()], - dependencies: [modulecommon, module_ss.dependencies()], pic: true) + dependencies: module_ss.dependencies(), pic: true) if d == 'block' block_mods += sl else system_mods += sl endif + emulator_modules += shared_module(sl.name(), + name_prefix: '', + link_whole: sl, + install: true, + install_dir: qemu_moddir) if module_ss.sources() != [] # FIXME: Should use sl.extract_all_objects(recursive: true) as # input. Sources can be used multiple times but objects are @@ -3642,6 +3649,7 @@ endforeach foreach d, list : target_modules foreach m, module_ss : list if enable_modules + module_ss.add(modulecommon) foreach target : target_dirs if target.endswith('-softmmu') config_target = config_target_mak[target] @@ -3654,11 +3662,16 @@ foreach d, list : target_modules module_name = d + '-' + m + '-' + config_target['TARGET_NAME'] sl = static_library(module_name, [genh, target_module_ss.sources()], - dependencies: [modulecommon, target_module_ss.dependencies()], + dependencies: target_module_ss.dependencies(), include_directories: target_inc, c_args: c_args, pic: true) system_mods += sl + emulator_modules += shared_module(sl.name(), + name_prefix: '', + link_whole: sl, + install: true, + install_dir: qemu_moddir) # FIXME: Should use sl.extract_all_objects(recursive: true) too. modinfo_files += custom_target(module_name + '.modinfo', output: module_name + '.modinfo', @@ -3692,6 +3705,10 @@ if enable_modules hw_arch[arch].add(modinfo_dep) endif endforeach + + if emulator_modules.length() > 0 + alias_target('modules', emulator_modules) + endif endif nm = find_program('nm') @@ -3785,19 +3802,6 @@ common_ss.add(hwcore) # Targets # ########### -emulator_modules = [] -foreach m : block_mods + system_mods - emulator_modules += shared_module(m.name(), - build_by_default: true, - name_prefix: '', - link_whole: m, - install: true, - install_dir: qemu_moddir) -endforeach -if emulator_modules.length() > 0 - alias_target('modules', emulator_modules) -endif - system_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) From e8f62689acd5930a712655d0c6838ec5eccc6b1c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 13:17:08 +0200 Subject: [PATCH 1919/2884] meson: move block.syms dependency out of libblock In order to define libqemuutil symbols that are requested by block modules, QEMU currently uses a combination of the "link_depends" argument of libraries (which is propagated into dependencies, but not available in dependencies) and the "link_args" argument of declare_dependency() (which _is_ available in static_library, but probably not used for historical reasons only). Unfortunately the link_depends will not be propagated into the "block" dependency if it is defined using declare_dependency(objects: ...); and it is not possible to add it directly to the dependency because the keyword argument simply is not available. The only solution, in order to switch to defining the dependency without using "link_whole" (which has problems of its own, see https://github.com/mesonbuild/meson/pull/8151#issuecomment-754796420), is unfortunately to add the link_args and link_depends to the executables directly; fortunately there is just four of them. It is possible (and I will look into it) to add "link_depends" to declare_dependency(), but it probably will be a while before QEMU can use it. Signed-off-by: Paolo Bonzini --- meson.build | 5 +++-- storage-daemon/meson.build | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 8909f8c87d..df9a64302f 100644 --- a/meson.build +++ b/meson.build @@ -3759,12 +3759,10 @@ system_ss.add(migration) block_ss = block_ss.apply({}) libblock = static_library('block', block_ss.sources() + genh, dependencies: block_ss.dependencies(), - link_depends: block_syms, name_suffix: 'fa', build_by_default: false) block = declare_dependency(link_whole: [libblock], - link_args: '@block.syms', dependencies: [crypto, io]) blockdev_ss = blockdev_ss.apply({}) @@ -4033,10 +4031,13 @@ endif if have_tools qemu_img = executable('qemu-img', [files('qemu-img.c'), hxdep], + link_args: '@block.syms', link_depends: block_syms, dependencies: [authz, block, crypto, io, qom, qemuutil], install: true) qemu_io = executable('qemu-io', files('qemu-io.c'), + link_args: '@block.syms', link_depends: block_syms, dependencies: [block, qemuutil], install: true) qemu_nbd = executable('qemu-nbd', files('qemu-nbd.c'), + link_args: '@block.syms', link_depends: block_syms, dependencies: [blockdev, qemuutil, gnutls, selinux], install: true) diff --git a/storage-daemon/meson.build b/storage-daemon/meson.build index 46267b63e7..fd5e32f4b2 100644 --- a/storage-daemon/meson.build +++ b/storage-daemon/meson.build @@ -8,6 +8,7 @@ if have_tools qsd_ss = qsd_ss.apply({}) qsd = executable('qemu-storage-daemon', qsd_ss.sources(), + link_args: '@block.syms', link_depends: block_syms, dependencies: qsd_ss.dependencies(), install: true) endif From 0082475e26430297ef65e598db5b67c8ac182620 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Jun 2024 15:07:23 +0200 Subject: [PATCH 1920/2884] meson: merge plugin_ldflags into emulator_link_args These serve the same purpose, except plugin_ldflags ends up in the linker command line in a more roundabout way (through specific_ss). Simplify. Signed-off-by: Paolo Bonzini --- plugins/meson.build | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/meson.build b/plugins/meson.build index 51b4350c2a..18a0303bff 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,4 +1,3 @@ -plugin_ldflags = [] # Modules need more symbols than just those in plugins/qemu-plugins.symbols if not enable_modules if host_os == 'darwin' @@ -7,9 +6,9 @@ if not enable_modules output: 'qemu-plugins-ld64.symbols', capture: true, command: ['sed', '-ne', 's/^[[:space:]]*\\(qemu_.*\\);/_\\1/p', '@INPUT@']) - plugin_ldflags = ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols'] + emulator_link_args += ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols'] else - plugin_ldflags = ['-Xlinker', '--dynamic-list=' + (meson.project_source_root() / 'plugins/qemu-plugins.symbols')] + emulator_link_args += ['-Xlinker', '--dynamic-list=' + (meson.project_source_root() / 'plugins/qemu-plugins.symbols')] endif endif @@ -37,5 +36,5 @@ if get_option('plugins') 'loader.c', 'core.c', 'api.c', - ), declare_dependency(link_args: plugin_ldflags)) + )) endif From 414b180d42315dc77c02170f1eee8db58c9fe052 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 24 May 2024 17:00:22 +0900 Subject: [PATCH 1921/2884] meson: Pass objects and dependencies to declare_dependency() We used to request declare_dependency() to link_whole static libraries. If a static library is a thin archive, GNU ld keeps all object files referenced by the archive open, and sometimes exceeds the open file limit. Another problem with link_whole is that suboptimal handling of nested dependencies. link_whole by itself does not propagate dependencies. In particular, gnutls, a dependency of crypto, is not propagated to its users, and we currently workaround the issue by declaring gnutls as a dependency for each crypto user. On the other hand, if you write something like libfoo = static_library('foo', 'foo.c', dependencies: gnutls) foo = declare_dependency(link_whole: libfoo) libbar = static_library('bar', 'bar.c', dependencies: foo) bar = declare_dependency(link_whole: libbar, dependencies: foo) executable('prog', sources: files('prog.c'), dependencies: [foo, bar]) hoping to propagate the gnutls dependency into bar.c, you'll see a linking failure for "prog", because the foo.c.o object file is included in libbar.a and therefore it is linked twice into "prog": once from libfoo.a and once from libbar.a. Here Meson does not see the duplication, it just asks the linker to link all of libfoo.a and libbar.a into "prog". Instead of using link_whole, extract objects included in static libraries and pass them to declare_dependency(); and then the dependencies can be added as well so that they are propagated, because object files on the linker command line are always deduplicated. This requires Meson 1.1.0 or later. Signed-off-by: Akihiko Odaki Message-ID: <20240524-objects-v1-1-07cbbe96166b@daynix.com> Signed-off-by: Paolo Bonzini --- docs/devel/build-system.rst | 3 ++- gdbstub/meson.build | 4 ++-- meson.build | 44 +++++++++++++++++++--------------- pythondeps.toml | 2 +- tcg/meson.build | 6 +++-- tests/qtest/libqos/meson.build | 2 +- 6 files changed, 35 insertions(+), 26 deletions(-) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index f4fd76117d..39a1934c63 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -238,7 +238,8 @@ Subsystem sourcesets: name_suffix: 'fa', build_by_default: false) - chardev = declare_dependency(link_whole: libchardev) + chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false), + dependencies: chardev_ss.dependencies()) As of Meson 0.55.1, the special ``.fa`` suffix should be used for everything that is used with ``link_whole``, to ensure that the link flags are placed diff --git a/gdbstub/meson.build b/gdbstub/meson.build index da5721d845..c56b54eae7 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -28,9 +28,9 @@ libgdb_system = static_library('gdb_system', name_suffix: 'fa', build_by_default: false) -gdb_user = declare_dependency(link_whole: libgdb_user) +gdb_user = declare_dependency(objects: libgdb_user.extract_all_objects(recursive: false)) user_ss.add(gdb_user) -gdb_system = declare_dependency(link_whole: libgdb_system) +gdb_system = declare_dependency(objects: libgdb_system.extract_all_objects(recursive: false)) system_ss.add(gdb_system) common_ss.add(files('syscalls.c')) diff --git a/meson.build b/meson.build index df9a64302f..0c314ae570 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('qemu', ['c'], meson_version: '>=0.63.0', +project('qemu', ['c'], meson_version: '>=1.1.0', default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], version: files('VERSION')) @@ -3461,7 +3461,7 @@ endif if enable_modules libmodulecommon = static_library('module-common', files('module-common.c') + genh, pic: true, c_args: '-DBUILD_DSO') - modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO') + modulecommon = declare_dependency(objects: libmodulecommon.extract_all_objects(recursive: false), compile_args: '-DBUILD_DSO') endif qom_ss = qom_ss.apply({}) @@ -3469,14 +3469,15 @@ libqom = static_library('qom', qom_ss.sources() + genh, dependencies: [qom_ss.dependencies()], name_suffix: 'fa', build_by_default: false) -qom = declare_dependency(link_whole: libqom) +qom = declare_dependency(objects: libqom.extract_all_objects(recursive: false), + dependencies: qom_ss.dependencies()) event_loop_base = files('event-loop-base.c') event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh, name_suffix: 'fa', build_by_default: false) -event_loop_base = declare_dependency(link_whole: event_loop_base, +event_loop_base = declare_dependency(objects: event_loop_base.extract_all_objects(recursive: false), dependencies: [qom]) stub_ss = stub_ss.apply({}) @@ -3621,7 +3622,8 @@ foreach d, list : modules endif emulator_modules += shared_module(sl.name(), name_prefix: '', - link_whole: sl, + objects: sl.extract_all_objects(recursive: false), + dependencies: module_ss.dependencies(), install: true, install_dir: qemu_moddir) if module_ss.sources() != [] @@ -3669,7 +3671,8 @@ foreach d, list : target_modules system_mods += sl emulator_modules += shared_module(sl.name(), name_prefix: '', - link_whole: sl, + objects: sl.extract_all_objects(recursive: false), + dependencies: target_module_ss.dependencies(), install: true, install_dir: qemu_moddir) # FIXME: Should use sl.extract_all_objects(recursive: true) too. @@ -3728,8 +3731,8 @@ libauthz = static_library('authz', authz_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -authz = declare_dependency(link_whole: libauthz, - dependencies: qom) +authz = declare_dependency(objects: libauthz.extract_all_objects(recursive: false), + dependencies: [authz_ss.dependencies(), qom]) crypto_ss = crypto_ss.apply({}) libcrypto = static_library('crypto', crypto_ss.sources() + genh, @@ -3737,8 +3740,8 @@ libcrypto = static_library('crypto', crypto_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -crypto = declare_dependency(link_whole: libcrypto, - dependencies: [authz, qom]) +crypto = declare_dependency(objects: libcrypto.extract_all_objects(recursive: false), + dependencies: [crypto_ss.dependencies(), authz, qom]) io_ss = io_ss.apply({}) libio = static_library('io', io_ss.sources() + genh, @@ -3747,12 +3750,13 @@ libio = static_library('io', io_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -io = declare_dependency(link_whole: libio, dependencies: [crypto, qom]) +io = declare_dependency(objects: libio.extract_all_objects(recursive: false), + dependencies: [io_ss.dependencies(), crypto, qom]) libmigration = static_library('migration', sources: migration_files + genh, name_suffix: 'fa', build_by_default: false) -migration = declare_dependency(link_with: libmigration, +migration = declare_dependency(objects: libmigration.extract_all_objects(recursive: false), dependencies: [qom, io]) system_ss.add(migration) @@ -3762,8 +3766,8 @@ libblock = static_library('block', block_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -block = declare_dependency(link_whole: [libblock], - dependencies: [crypto, io]) +block = declare_dependency(objects: libblock.extract_all_objects(recursive: false), + dependencies: [block_ss.dependencies(), crypto, io]) blockdev_ss = blockdev_ss.apply({}) libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, @@ -3771,8 +3775,8 @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -blockdev = declare_dependency(link_whole: [libblockdev], - dependencies: [block, event_loop_base]) +blockdev = declare_dependency(objects: libblockdev.extract_all_objects(recursive: false), + dependencies: [blockdev_ss.dependencies(), block, event_loop_base]) qmp_ss = qmp_ss.apply({}) libqmp = static_library('qmp', qmp_ss.sources() + genh, @@ -3780,20 +3784,22 @@ libqmp = static_library('qmp', qmp_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -qmp = declare_dependency(link_whole: [libqmp]) +qmp = declare_dependency(objects: libqmp.extract_all_objects(recursive: false), + dependencies: qmp_ss.dependencies()) libchardev = static_library('chardev', chardev_ss.sources() + genh, name_suffix: 'fa', dependencies: chardev_ss.dependencies(), build_by_default: false) -chardev = declare_dependency(link_whole: libchardev) +chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false), + dependencies: chardev_ss.dependencies()) hwcore_ss = hwcore_ss.apply({}) libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) -hwcore = declare_dependency(link_whole: libhwcore) +hwcore = declare_dependency(objects: libhwcore.extract_all_objects(recursive: false)) common_ss.add(hwcore) ########### diff --git a/pythondeps.toml b/pythondeps.toml index 9c16602d30..6aba0c9daa 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -19,7 +19,7 @@ [meson] # The install key should match the version in python/wheels/ -meson = { accepted = ">=0.63.0", installed = "1.2.3", canary = "meson" } +meson = { accepted = ">=1.1.0", installed = "1.2.3", canary = "meson" } [docs] # Please keep the installed versions in sync with docs/requirements.txt diff --git a/tcg/meson.build b/tcg/meson.build index ffbe754d8b..165e773abb 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -36,7 +36,8 @@ libtcg_user = static_library('tcg_user', c_args: '-DCONFIG_USER_ONLY', build_by_default: false) -tcg_user = declare_dependency(link_with: libtcg_user) +tcg_user = declare_dependency(objects: libtcg_user.extract_all_objects(recursive: false), + dependencies: tcg_ss.dependencies()) user_ss.add(tcg_user) libtcg_system = static_library('tcg_system', @@ -46,5 +47,6 @@ libtcg_system = static_library('tcg_system', c_args: '-DCONFIG_SOFTMMU', build_by_default: false) -tcg_system = declare_dependency(link_with: libtcg_system) +tcg_system = declare_dependency(objects: libtcg_system.extract_all_objects(recursive: false), + dependencies: tcg_ss.dependencies()) system_ss.add(tcg_system) diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 558eb4c24b..05fe57a4b9 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -72,4 +72,4 @@ libqos = static_library('qos', libqos_srcs + genh, name_suffix: 'fa', build_by_default: false) -qos = declare_dependency(link_whole: libqos) +qos = declare_dependency(objects: libqos.extract_all_objects(recursive: false)) From 7b1070a7e17cc65c3134350728c85e1d218c3578 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 24 May 2024 17:00:23 +0900 Subject: [PATCH 1922/2884] Revert "meson: Propagate gnutls dependency" This reverts commit 3eacf70bb5a83e4775ad8003cbca63a40f70c8c2. It was only needed because of duplicate objects caused by declare_dependency(link_whole: ...), and can be dropped now that meson.build specifies objects and dependencies separately for the internal dependencies. Signed-off-by: Akihiko Odaki Message-ID: <20240524-objects-v1-2-07cbbe96166b@daynix.com> Signed-off-by: Paolo Bonzini --- block/meson.build | 2 +- io/meson.build | 2 +- meson.build | 4 ++-- storage-daemon/meson.build | 2 +- ui/meson.build | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/block/meson.build b/block/meson.build index 158dc3b89d..f1262ec2ba 100644 --- a/block/meson.build +++ b/block/meson.build @@ -39,7 +39,7 @@ block_ss.add(files( 'throttle.c', 'throttle-groups.c', 'write-threshold.c', -), zstd, zlib, gnutls) +), zstd, zlib) system_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) system_ss.add(files('block-ram-registrar.c')) diff --git a/io/meson.build b/io/meson.build index 283b9b2bdb..1164812f91 100644 --- a/io/meson.build +++ b/io/meson.build @@ -13,4 +13,4 @@ io_ss.add(files( 'dns-resolver.c', 'net-listener.c', 'task.c', -), gnutls) +)) diff --git a/meson.build b/meson.build index 0c314ae570..429899d860 100644 --- a/meson.build +++ b/meson.build @@ -3526,7 +3526,7 @@ if have_block 'blockdev-nbd.c', 'iothread.c', 'job-qmp.c', - ), gnutls) + )) # os-posix.c contains POSIX-specific functions used by qemu-storage-daemon, # os-win32.c does not @@ -4044,7 +4044,7 @@ if have_tools dependencies: [block, qemuutil], install: true) qemu_nbd = executable('qemu-nbd', files('qemu-nbd.c'), link_args: '@block.syms', link_depends: block_syms, - dependencies: [blockdev, qemuutil, gnutls, selinux], + dependencies: [blockdev, qemuutil, selinux], install: true) subdir('storage-daemon') diff --git a/storage-daemon/meson.build b/storage-daemon/meson.build index fd5e32f4b2..5e61a9d1bd 100644 --- a/storage-daemon/meson.build +++ b/storage-daemon/meson.build @@ -1,6 +1,6 @@ qsd_ss = ss.source_set() qsd_ss.add(files('qemu-storage-daemon.c')) -qsd_ss.add(blockdev, chardev, qmp, qom, qemuutil, gnutls) +qsd_ss.add(blockdev, chardev, qmp, qom, qemuutil) subdir('qapi') diff --git a/ui/meson.build b/ui/meson.build index cfbf29428d..28c7381dd1 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -44,7 +44,7 @@ vnc_ss.add(files( 'vnc-jobs.c', 'vnc-clipboard.c', )) -vnc_ss.add(zlib, jpeg, gnutls) +vnc_ss.add(zlib, jpeg) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss) system_ss.add(when: vnc, if_false: files('vnc-stubs.c')) From 4408155ac593644f04e22db0656d79a0e198be63 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 May 2024 10:56:55 +0200 Subject: [PATCH 1923/2884] meson: Drop the .fa library suffix The non-standard .fa library suffix breaks the link source de-duplication done by Meson so drop it. The lack of link source de-duplication causes AddressSanitizer to complain ODR violations, and makes GNU ld abort when combined with clang's LTO. Fortunately, the non-standard suffix is not necessary anymore for two reasons. First, the non-standard suffix was necessary for fork-fuzzing. Meson wraps all standard-suffixed libraries with --start-group and --end-group. This made a fork-fuzz.ld linker script wrapped as well and broke builds. Commit d2e6f9272d33 ("fuzz: remove fork-fuzzing scaffolding") dropped fork-fuzzing so we can now restore the standard suffix. Second, the libraries are not even built anymore, because it is possible to just use the object files directly via extract_all_objects(). The occurences of the suffix were detected and removed by performing a tree-wide search with 'fa' and .fa (note the quotes and dot). Signed-off-by: Akihiko Odaki Message-ID: <20240524-xkb-v4-4-2de564e5c859@daynix.com> Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest-template.yml | 2 -- .gitlab-ci.d/buildtest.yml | 2 -- docs/devel/build-system.rst | 5 ----- gdbstub/meson.build | 2 -- meson.build | 17 ++--------------- stubs/blk-exp-close-all.c | 2 +- tcg/meson.build | 2 -- tests/Makefile.include | 2 +- tests/qtest/libqos/meson.build | 1 - 9 files changed, 4 insertions(+), 31 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 278a5ea966..8f7ebfaed8 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -45,10 +45,8 @@ exclude: - build/**/*.p - build/**/*.a.p - - build/**/*.fa.p - build/**/*.c.o - build/**/*.c.o.d - - build/**/*.fa .common_test_job_template: extends: .base_job_template diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 0eec570310..425fc6479b 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -178,10 +178,8 @@ build-previous-qemu: exclude: - build-previous/**/*.p - build-previous/**/*.a.p - - build-previous/**/*.fa.p - build-previous/**/*.c.o - build-previous/**/*.c.o.d - - build-previous/**/*.fa needs: job: amd64-opensuse-leap-container variables: diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 39a1934c63..79eceb179d 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -235,16 +235,11 @@ Subsystem sourcesets: are then turned into static libraries as follows:: libchardev = static_library('chardev', chardev_ss.sources(), - name_suffix: 'fa', build_by_default: false) chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false), dependencies: chardev_ss.dependencies()) - As of Meson 0.55.1, the special ``.fa`` suffix should be used for everything - that is used with ``link_whole``, to ensure that the link flags are placed - correctly in the command line. - Target-independent emulator sourcesets: Various general purpose helper code is compiled only once and the .o files are linked into all output binaries that need it. diff --git a/gdbstub/meson.build b/gdbstub/meson.build index c56b54eae7..dff741ddd4 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -19,13 +19,11 @@ gdb_system_ss = gdb_system_ss.apply({}) libgdb_user = static_library('gdb_user', gdb_user_ss.sources() + genh, - name_suffix: 'fa', c_args: '-DCONFIG_USER_ONLY', build_by_default: false) libgdb_system = static_library('gdb_system', gdb_system_ss.sources() + genh, - name_suffix: 'fa', build_by_default: false) gdb_user = declare_dependency(objects: libgdb_user.extract_all_objects(recursive: false)) diff --git a/meson.build b/meson.build index 429899d860..3a1ad4ddeb 100644 --- a/meson.build +++ b/meson.build @@ -3467,7 +3467,6 @@ endif qom_ss = qom_ss.apply({}) libqom = static_library('qom', qom_ss.sources() + genh, dependencies: [qom_ss.dependencies()], - name_suffix: 'fa', build_by_default: false) qom = declare_dependency(objects: libqom.extract_all_objects(recursive: false), dependencies: qom_ss.dependencies()) @@ -3475,7 +3474,6 @@ qom = declare_dependency(objects: libqom.extract_all_objects(recursive: false), event_loop_base = files('event-loop-base.c') event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh, - name_suffix: 'fa', build_by_default: false) event_loop_base = declare_dependency(objects: event_loop_base.extract_all_objects(recursive: false), dependencies: [qom]) @@ -3728,7 +3726,6 @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms', authz_ss = authz_ss.apply({}) libauthz = static_library('authz', authz_ss.sources() + genh, dependencies: [authz_ss.dependencies()], - name_suffix: 'fa', build_by_default: false) authz = declare_dependency(objects: libauthz.extract_all_objects(recursive: false), @@ -3737,7 +3734,6 @@ authz = declare_dependency(objects: libauthz.extract_all_objects(recursive: fals crypto_ss = crypto_ss.apply({}) libcrypto = static_library('crypto', crypto_ss.sources() + genh, dependencies: [crypto_ss.dependencies()], - name_suffix: 'fa', build_by_default: false) crypto = declare_dependency(objects: libcrypto.extract_all_objects(recursive: false), @@ -3747,14 +3743,12 @@ io_ss = io_ss.apply({}) libio = static_library('io', io_ss.sources() + genh, dependencies: [io_ss.dependencies()], link_with: libqemuutil, - name_suffix: 'fa', build_by_default: false) io = declare_dependency(objects: libio.extract_all_objects(recursive: false), dependencies: [io_ss.dependencies(), crypto, qom]) libmigration = static_library('migration', sources: migration_files + genh, - name_suffix: 'fa', build_by_default: false) migration = declare_dependency(objects: libmigration.extract_all_objects(recursive: false), dependencies: [qom, io]) @@ -3763,7 +3757,6 @@ system_ss.add(migration) block_ss = block_ss.apply({}) libblock = static_library('block', block_ss.sources() + genh, dependencies: block_ss.dependencies(), - name_suffix: 'fa', build_by_default: false) block = declare_dependency(objects: libblock.extract_all_objects(recursive: false), @@ -3772,7 +3765,6 @@ block = declare_dependency(objects: libblock.extract_all_objects(recursive: fals blockdev_ss = blockdev_ss.apply({}) libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, dependencies: blockdev_ss.dependencies(), - name_suffix: 'fa', build_by_default: false) blockdev = declare_dependency(objects: libblockdev.extract_all_objects(recursive: false), @@ -3781,14 +3773,12 @@ blockdev = declare_dependency(objects: libblockdev.extract_all_objects(recursive qmp_ss = qmp_ss.apply({}) libqmp = static_library('qmp', qmp_ss.sources() + genh, dependencies: qmp_ss.dependencies(), - name_suffix: 'fa', build_by_default: false) qmp = declare_dependency(objects: libqmp.extract_all_objects(recursive: false), dependencies: qmp_ss.dependencies()) libchardev = static_library('chardev', chardev_ss.sources() + genh, - name_suffix: 'fa', dependencies: chardev_ss.dependencies(), build_by_default: false) @@ -3797,7 +3787,6 @@ chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: hwcore_ss = hwcore_ss.apply({}) libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh, - name_suffix: 'fa', build_by_default: false) hwcore = declare_dependency(objects: libhwcore.extract_all_objects(recursive: false)) common_ss.add(hwcore) @@ -3820,8 +3809,7 @@ common_all = static_library('common', sources: common_ss.all_sources() + genh, include_directories: common_user_inc, implicit_include_directories: false, - dependencies: common_ss.all_dependencies(), - name_suffix: 'fa') + dependencies: common_ss.all_dependencies()) feature_to_c = find_program('scripts/feature_to_c.py') @@ -3930,8 +3918,7 @@ foreach target : target_dirs objects: objects, include_directories: target_inc, c_args: c_args, - build_by_default: false, - name_suffix: 'fa') + build_by_default: false) if target.endswith('-softmmu') execs = [{ diff --git a/stubs/blk-exp-close-all.c b/stubs/blk-exp-close-all.c index 1c71316763..2f68e06d7d 100644 --- a/stubs/blk-exp-close-all.c +++ b/stubs/blk-exp-close-all.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "block/export.h" -/* Only used in programs that support block exports (libblockdev.fa) */ +/* Only used in programs that support block exports (libblockdev.a) */ void blk_exp_close_all(void) { } diff --git a/tcg/meson.build b/tcg/meson.build index 165e773abb..69ebb4908a 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -31,7 +31,6 @@ tcg_ss = tcg_ss.apply({}) libtcg_user = static_library('tcg_user', tcg_ss.sources() + genh, - name_suffix: 'fa', dependencies: tcg_ss.dependencies(), c_args: '-DCONFIG_USER_ONLY', build_by_default: false) @@ -42,7 +41,6 @@ user_ss.add(tcg_user) libtcg_system = static_library('tcg_system', tcg_ss.sources() + genh, - name_suffix: 'fa', dependencies: tcg_ss.dependencies(), c_args: '-DCONFIG_SOFTMMU', build_by_default: false) diff --git a/tests/Makefile.include b/tests/Makefile.include index c9d1674bd0..d39d5dd6a4 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -87,7 +87,7 @@ distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) .PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning # Build up our target list from the filtered list of ninja targets -TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets))) +TARGETS=$(patsubst libqemu-%.a, %, $(filter libqemu-%.a, $(ninja-targets))) TESTS_VENV_TOKEN=$(BUILD_DIR)/pyvenv/tests.group TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 05fe57a4b9..1b2b2dbb22 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -69,7 +69,6 @@ if have_virtfs endif libqos = static_library('qos', libqos_srcs + genh, - name_suffix: 'fa', build_by_default: false) qos = declare_dependency(objects: libqos.extract_all_objects(recursive: false)) From 8dee38483274bd0fcf3f74dea024d719b958200d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Jun 2024 01:12:42 +0200 Subject: [PATCH 1924/2884] target/i386: pass X86CPU to x86_cpu_get_supported_feature_word This allows modifying the bits in "-cpu max"/"-cpu host" depending on the guest CPU vendor (which, at least by default, is the host vendor in the case of KVM). For example, machine check architecture differs between Intel and AMD, and bits from AMD should be dropped when configuring the guest for an Intel model. Cc: Xiaoyao Li Cc: John Allen Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 11 +++++------ target/i386/cpu.h | 3 +-- target/i386/kvm/kvm-cpu.c | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4c2e6f3a71..4364cb0f8e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6035,8 +6035,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) #endif /* !CONFIG_USER_ONLY */ -uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, - bool migratable_only) +uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) { FeatureWordInfo *wi = &feature_word_info[w]; uint64_t r = 0; @@ -6078,7 +6077,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, r &= ~unavail; } #endif - if (migratable_only) { + if (cpu && cpu->migratable) { r &= x86_cpu_get_migratable_flags(w); } return r; @@ -7371,7 +7370,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) * by the user. */ env->features[w] |= - x86_cpu_get_supported_feature_word(w, cpu->migratable) & + x86_cpu_get_supported_feature_word(cpu, w) & ~env->user_features[w] & ~feature_word_info[w].no_autoenable_flags; } @@ -7497,7 +7496,7 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose) for (w = 0; w < FEATURE_WORDS; w++) { uint64_t host_feat = - x86_cpu_get_supported_feature_word(w, false); + x86_cpu_get_supported_feature_word(NULL, w); uint64_t requested_features = env->features[w]; uint64_t unavailable_features = requested_features & ~host_feat; mark_unavailable_features(cpu, w, unavailable_features, prefix); @@ -7617,7 +7616,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_LBR_FMT; if (requested_lbr_fmt && kvm_enabled()) { uint64_t host_perf_cap = - x86_cpu_get_supported_feature_word(FEAT_PERF_CAPABILITIES, false); + x86_cpu_get_supported_feature_word(NULL, FEAT_PERF_CAPABILITIES); unsigned host_lbr_fmt = host_perf_cap & PERF_CAP_LBR_FMT; if (!cpu->enable_pmu) { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 29daf37048..9bea7142bf 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -666,8 +666,7 @@ typedef enum FeatureWord { } FeatureWord; typedef uint64_t FeatureWordArray[FEATURE_WORDS]; -uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, - bool migratable_only); +uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* cpuid_features bits */ #define CPUID_FP87 (1U << 0) diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index d57a68a301..6bf8dcfc60 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -143,7 +143,7 @@ static void kvm_cpu_xsave_init(void) if (!esa->size) { continue; } - if ((x86_cpu_get_supported_feature_word(esa->feature, false) & esa->bits) + if ((x86_cpu_get_supported_feature_word(NULL, esa->feature) & esa->bits) != esa->bits) { continue; } From 0b2757412cb1d1947d7e2c1fe14985f1e72bba32 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Jun 2024 01:13:08 +0200 Subject: [PATCH 1925/2884] target/i386: drop AMD machine check bits from Intel CPUID The recent addition of the SUCCOR bit to kvm_arch_get_supported_cpuid() causes the bit to be visible when "-cpu host" VMs are started on Intel processors. While this should in principle be harmless, it's not tidy and we don't even know for sure that it doesn't cause any guest OS to take unexpected paths. Since x86_cpu_get_supported_feature_word() can return different different values depending on the guest, adjust it to hide the SUCCOR bit if the guest has non-AMD vendor. Suggested-by: Xiaoyao Li Cc: John Allen Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4364cb0f8e..5e5bf71702 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6039,6 +6039,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) { FeatureWordInfo *wi = &feature_word_info[w]; uint64_t r = 0; + uint32_t unavail = 0; if (kvm_enabled()) { switch (wi->type) { @@ -6064,19 +6065,33 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) } else { return ~0; } + + switch (w) { #ifndef TARGET_X86_64 - if (w == FEAT_8000_0001_EDX) { + case FEAT_8000_0001_EDX: /* * 32-bit TCG can emulate 64-bit compatibility mode. If there is no * way for userspace to get out of its 32-bit jail, we can leave * the LM bit set. */ - uint32_t unavail = tcg_enabled() + unavail = tcg_enabled() ? CPUID_EXT2_LM & ~CPUID_EXT2_KERNEL_FEATURES : CPUID_EXT2_LM; - r &= ~unavail; - } + break; #endif + + case FEAT_8000_0007_EBX: + if (cpu && !IS_AMD_CPU(&cpu->env)) { + /* Disable AMD machine check architecture for Intel CPU. */ + unavail = ~0; + } + break; + + default: + break; + } + + r &= ~unavail; if (cpu && cpu->migratable) { r &= x86_cpu_get_migratable_flags(w); } From 9b40d376f66640eb7b6080ca000c866dfe630dc7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 3 Jul 2024 10:37:23 +0200 Subject: [PATCH 1926/2884] target/i386: SEV: fix formatting of CPUID mismatch message Fixes: 70943ad8e4d ("i386/sev: Add support for SNP CPUID validation", 2024-06-05) Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 3ab8b3c28b..2a0f94d390 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -841,7 +841,7 @@ sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old, size_t i; if (old->count != new->count) { - error_report("SEV-SNP: CPUID validation failed due to count mismatch," + error_report("SEV-SNP: CPUID validation failed due to count mismatch, " "provided: %d, expected: %d", old->count, new->count); return; } @@ -853,8 +853,8 @@ sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old, new_func = &new->entries[i]; if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) { - error_report("SEV-SNP: CPUID validation failed for function 0x%x, index: 0x%x" - "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x" + error_report("SEV-SNP: CPUID validation failed for function 0x%x, index: 0x%x, " + "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x, " "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x", old_func->eax_in, old_func->ecx_in, old_func->eax, old_func->ebx, old_func->ecx, old_func->edx, From 29a51b2bb55c6b2e0224d82c154d82498ccece35 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 3 Jul 2024 11:29:17 +0200 Subject: [PATCH 1927/2884] target/i386: do not include undefined bits in the AMD topoext leaf Commit d7c72735f61 ("target/i386: Add new EPYC CPU versions with updated cache_info", 2023-05-08) ensured that AMD-defined CPU models did not have the 'complex_indexing' bit set, but left it set in "-cpu host" which uses the default ("legacy") cache information. Reimplement that commit using a CPU feature, so that it can be applied to all guests using a new machine type, independent of the CPU model. Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/cpu.c | 4 ++++ target/i386/cpu.h | 3 +++ 3 files changed, 8 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 77415064c6..5dff91422f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -80,6 +80,7 @@ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, GlobalProperty pc_compat_9_0[] = { + { TYPE_X86_CPU, "x-amd-topoext-features-only", "false" }, { TYPE_X86_CPU, "x-l1-cache-per-thread", "false" }, { TYPE_X86_CPU, "guest-phys-bits", "0" }, { "sev-guest", "legacy-vm-type", "true" }, diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5e5bf71702..c40551d9bf 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6982,6 +6982,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; break; } + if (cpu->amd_topoext_features_only) { + *edx &= CACHE_NO_INVD_SHARING | CACHE_INCLUSIVE; + } break; case 0x8000001E: if (cpu->core_id <= 255) { @@ -8293,6 +8296,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("x-vendor-cpuid-only", X86CPU, vendor_cpuid_only, true), + DEFINE_PROP_BOOL("x-amd-topoext-features-only", X86CPU, amd_topoext_features_only, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), DEFINE_PROP_BOOL("kvm-pv-enforce-cpuid", X86CPU, kvm_pv_enforce_cpuid, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9bea7142bf..0d5624355e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2104,6 +2104,9 @@ struct ArchCPU { /* Only advertise CPUID leaves defined by the vendor */ bool vendor_cpuid_only; + /* Only advertise TOPOEXT features that AMD defines */ + bool amd_topoext_features_only; + /* Enable auto level-increase for Intel Processor Trace leave */ bool intel_pt_auto_level; From ab5f4edf721fc0403d40a4db7e96808175157def Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Mon, 24 Jun 2024 10:52:48 +0200 Subject: [PATCH 1928/2884] i386/sev: Fix error message in sev_get_capabilities() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a custom path is provided to sev-guest object and opening the path fails an error message is reported. But the error message still mentions DEFAULT_SEV_DEVICE ("/dev/sev") instead of the custom path. Fixes: 16dcf200dc951c1cde3e5b442457db5f690b8cf0 Signed-off-by: Michal Privoznik Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/b4648905d399780063dc70851d3d6a3cd28719a5.1719218926.git.mprivozn@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 2a0f94d390..054366878a 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -597,7 +597,7 @@ static SevCapability *sev_get_capabilities(Error **errp) fd = open(sev_device, O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "SEV: Failed to open %s", - DEFAULT_SEV_DEVICE); + sev_device); g_free(sev_device); return NULL; } From f4e5f302b3361f13349b2a44378e7cf578a8e012 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Mon, 24 Jun 2024 10:52:49 +0200 Subject: [PATCH 1929/2884] i386/sev: Fallback to the default SEV device if none provided in sev_get_capabilities() When management tools (e.g. libvirt) query QEMU capabilities, they start QEMU with a minimalistic configuration and issue various commands on monitor. One of the command issued is/might be "query-sev-capabilities" to learn values like cbitpos or reduced-phys-bits. But as of v9.0.0-1145-g16dcf200dc the monitor command returns an error instead. This creates a chicken-egg problem because in order to query those aforementioned values QEMU needs to be started with a 'sev-guest' object. But to start QEMU with the values must be known. I think it's safe to assume that the default path ("/dev/sev") provides the same data as user provided one. So fall back to it. Fixes: 16dcf200dc951c1cde3e5b442457db5f690b8cf0 Signed-off-by: Michal Privoznik Link: https://lore.kernel.org/r/157f93712c23818be193ce785f648f0060b33dee.1719218926.git.mprivozn@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 054366878a..2f3dbe289f 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -587,13 +587,13 @@ static SevCapability *sev_get_capabilities(Error **errp) } sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - if (!sev_common) { - error_setg(errp, "SEV is not configured"); - return NULL; + if (sev_common) { + sev_device = object_property_get_str(OBJECT(sev_common), "sev-device", + &error_abort); + } else { + sev_device = g_strdup(DEFAULT_SEV_DEVICE); } - sev_device = object_property_get_str(OBJECT(sev_common), "sev-device", - &error_abort); fd = open(sev_device, O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "SEV: Failed to open %s", From 138c3377a9b27accec516b2c0da90dedef98a780 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 3 Jul 2024 13:42:49 +0200 Subject: [PATCH 1930/2884] target/i386: add avx-vnni-int16 feature AVX-VNNI-INT16 (CPUID[EAX=7,ECX=1).EDX[10]) is supported by Clearwater Forest processor, add it to QEMU as it does not need any specific enablement. Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c40551d9bf..c05765eeaf 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1131,7 +1131,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { NULL, NULL, NULL, NULL, "avx-vnni-int8", "avx-ne-convert", NULL, NULL, - "amx-complex", NULL, NULL, NULL, + "amx-complex", NULL, "avx-vnni-int16", NULL, NULL, NULL, "prefetchiti", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, From 6527cee257e3c8e7add941a06118009c83ba9e75 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Fri, 28 Jun 2024 20:58:15 -0400 Subject: [PATCH 1931/2884] util/cpuinfo-ppc: Add FreeBSD support Signed-off-by: Brad Smith Message-Id: Signed-off-by: Richard Henderson --- util/cpuinfo-ppc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/cpuinfo-ppc.c b/util/cpuinfo-ppc.c index 47af55aa0c..1304f9aa80 100644 --- a/util/cpuinfo-ppc.c +++ b/util/cpuinfo-ppc.c @@ -14,6 +14,13 @@ # include "elf.h" # endif #endif +#ifdef __FreeBSD__ +# include +# ifndef PPC_FEATURE2_ARCH_3_1 +# define PPC_FEATURE2_ARCH_3_1 0 +# endif +# define PPC_FEATURE2_VEC_CRYPTO PPC_FEATURE2_HAS_VEC_CRYPTO +#endif unsigned cpuinfo; @@ -28,7 +35,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info = CPUINFO_ALWAYS; -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(__FreeBSD__) unsigned long hwcap = qemu_getauxval(AT_HWCAP); unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); From ab089908b42f22e7edfa0d40db963c136ab35419 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Sat, 22 Jun 2024 22:12:23 -0400 Subject: [PATCH 1932/2884] util/cpuinfo-aarch64: Add OpenBSD support Signed-off-by: Brad Smith Reviewed-by: Richard Henderson Message-Id: Signed-off-by: Richard Henderson --- util/cpuinfo-aarch64.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/util/cpuinfo-aarch64.c b/util/cpuinfo-aarch64.c index 4c8a005715..8ca775a14b 100644 --- a/util/cpuinfo-aarch64.c +++ b/util/cpuinfo-aarch64.c @@ -20,6 +20,12 @@ #ifdef CONFIG_DARWIN # include #endif +#ifdef __OpenBSD__ +# include +# include +# include +# include +#endif unsigned cpuinfo; @@ -72,6 +78,36 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= sysctl_for_bool("hw.optional.arm.FEAT_PMULL") * CPUINFO_PMULL; info |= sysctl_for_bool("hw.optional.arm.FEAT_BTI") * CPUINFO_BTI; #endif +#ifdef __OpenBSD__ + int mib[2]; + uint64_t isar0; + uint64_t pfr1; + size_t len; + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_ID_AA64ISAR0; + len = sizeof(isar0); + if (sysctl(mib, 2, &isar0, &len, NULL, 0) != -1) { + if (ID_AA64ISAR0_ATOMIC(isar0) >= ID_AA64ISAR0_ATOMIC_IMPL) { + info |= CPUINFO_LSE; + } + if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_BASE) { + info |= CPUINFO_AES; + } + if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_PMULL) { + info |= CPUINFO_PMULL; + } + } + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_ID_AA64PFR1; + len = sizeof(pfr1); + if (sysctl(mib, 2, &pfr1, &len, NULL, 0) != -1) { + if (ID_AA64PFR1_BT(pfr1) >= ID_AA64PFR1_BT_IMPL) { + info |= CPUINFO_BTI; + } + } +#endif cpuinfo = info; return info; From b86c6ba689662256ea32f3e27927524ccb13f81d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Jun 2024 04:54:47 +0000 Subject: [PATCH 1933/2884] util/cpuinfo-riscv: Support host/cpuinfo.h for riscv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move detection code out of tcg, similar to other hosts. Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Signed-off-by: Richard Henderson --- host/include/riscv/host/cpuinfo.h | 23 +++++++++ tcg/riscv/tcg-target.c.inc | 84 +++--------------------------- tcg/riscv/tcg-target.h | 46 ++++++++--------- util/cpuinfo-riscv.c | 85 +++++++++++++++++++++++++++++++ util/meson.build | 2 + 5 files changed, 139 insertions(+), 101 deletions(-) create mode 100644 host/include/riscv/host/cpuinfo.h create mode 100644 util/cpuinfo-riscv.c diff --git a/host/include/riscv/host/cpuinfo.h b/host/include/riscv/host/cpuinfo.h new file mode 100644 index 0000000000..2b00660e36 --- /dev/null +++ b/host/include/riscv/host/cpuinfo.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for RISC-V. + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_ZBA (1u << 1) +#define CPUINFO_ZBB (1u << 2) +#define CPUINFO_ZICOND (1u << 3) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; + +/* + * We cannot rely on constructor ordering, so other constructors must + * use the function interface rather than the variable above. + */ +unsigned cpuinfo_init(void); + +#endif /* HOST_CPUINFO_H */ diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 639363039b..d334857226 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -113,20 +113,6 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_A7, }; -#ifndef have_zbb -bool have_zbb; -#endif -#if defined(__riscv_arch_test) && defined(__riscv_zba) -# define have_zba true -#else -static bool have_zba; -#endif -#if defined(__riscv_arch_test) && defined(__riscv_zicond) -# define have_zicond true -#else -static bool have_zicond; -#endif - static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) { tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); @@ -594,7 +580,7 @@ static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) { - if (have_zbb) { + if (cpuinfo & CPUINFO_ZBB) { tcg_out_opc_reg(s, OPC_ZEXT_H, ret, arg, TCG_REG_ZERO); } else { tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); @@ -604,7 +590,7 @@ static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { - if (have_zba) { + if (cpuinfo & CPUINFO_ZBA) { tcg_out_opc_reg(s, OPC_ADD_UW, ret, arg, TCG_REG_ZERO); } else { tcg_out_opc_imm(s, OPC_SLLI, ret, arg, 32); @@ -614,7 +600,7 @@ static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - if (have_zbb) { + if (cpuinfo & CPUINFO_ZBB) { tcg_out_opc_imm(s, OPC_SEXT_B, ret, arg, 0); } else { tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 24); @@ -624,7 +610,7 @@ static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - if (have_zbb) { + if (cpuinfo & CPUINFO_ZBB) { tcg_out_opc_imm(s, OPC_SEXT_H, ret, arg, 0); } else { tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); @@ -1080,7 +1066,7 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, int tmpflags; TCGReg t; - if (!have_zicond && (!c_cmp2 || cmp2 == 0)) { + if (!(cpuinfo & CPUINFO_ZICOND) && (!c_cmp2 || cmp2 == 0)) { tcg_out_movcond_br2(s, cond, ret, cmp1, cmp2, val1, c_val1, val2, c_val2); return; @@ -1089,7 +1075,7 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, cmp1, cmp2, c_cmp2); t = tmpflags & ~SETCOND_FLAGS; - if (have_zicond) { + if (cpuinfo & CPUINFO_ZICOND) { if (tmpflags & SETCOND_INV) { tcg_out_movcond_zicond(s, ret, t, val2, c_val2, val1, c_val1); } else { @@ -1304,7 +1290,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, /* TLB Hit - translate address using addend. */ if (addr_type != TCG_TYPE_I32) { tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); - } else if (have_zba) { + } else if (cpuinfo & CPUINFO_ZBA) { tcg_out_opc_reg(s, OPC_ADD_UW, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); } else { @@ -1335,7 +1321,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, if (addr_type != TCG_TYPE_I32) { tcg_out_opc_reg(s, OPC_ADD, base, addr_reg, TCG_GUEST_BASE_REG); - } else if (have_zba) { + } else if (cpuinfo & CPUINFO_ZBA) { tcg_out_opc_reg(s, OPC_ADD_UW, base, addr_reg, TCG_GUEST_BASE_REG); } else { @@ -2110,62 +2096,8 @@ static void tcg_out_tb_start(TCGContext *s) /* nothing to do */ } -static volatile sig_atomic_t got_sigill; - -static void sigill_handler(int signo, siginfo_t *si, void *data) -{ - /* Skip the faulty instruction */ - ucontext_t *uc = (ucontext_t *)data; - uc->uc_mcontext.__gregs[REG_PC] += 4; - - got_sigill = 1; -} - -static void tcg_target_detect_isa(void) -{ -#if !defined(have_zba) || !defined(have_zbb) || !defined(have_zicond) - /* - * TODO: It is expected that this will be determinable via - * linux riscv_hwprobe syscall, not yet merged. - * In the meantime, test via sigill. - */ - - struct sigaction sa_old, sa_new; - - memset(&sa_new, 0, sizeof(sa_new)); - sa_new.sa_flags = SA_SIGINFO; - sa_new.sa_sigaction = sigill_handler; - sigaction(SIGILL, &sa_new, &sa_old); - -#ifndef have_zba - /* Probe for Zba: add.uw zero,zero,zero. */ - got_sigill = 0; - asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero" : : : "memory"); - have_zba = !got_sigill; -#endif - -#ifndef have_zbb - /* Probe for Zba: andn zero,zero,zero. */ - got_sigill = 0; - asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero" : : : "memory"); - have_zbb = !got_sigill; -#endif - -#ifndef have_zicond - /* Probe for Zicond: czero.eqz zero,zero,zero. */ - got_sigill = 0; - asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero" : : : "memory"); - have_zicond = !got_sigill; -#endif - - sigaction(SIGILL, &sa_old, NULL); -#endif -} - static void tcg_target_init(TCGContext *s) { - tcg_target_detect_isa(); - tcg_target_available_regs[TCG_TYPE_I32] = 0xffffffff; tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 2c1b680b93..1a347eaf6e 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -25,6 +25,8 @@ #ifndef RISCV_TCG_TARGET_H #define RISCV_TCG_TARGET_H +#include "host/cpuinfo.h" + #define TCG_TARGET_INSN_UNIT_SIZE 4 #define TCG_TARGET_NB_REGS 32 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -80,18 +82,12 @@ typedef enum { #define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL #define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL -#if defined(__riscv_arch_test) && defined(__riscv_zbb) -# define have_zbb true -#else -extern bool have_zbb; -#endif - /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 -#define TCG_TARGET_HAS_rot_i32 have_zbb +#define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_extract_i32 0 #define TCG_TARGET_HAS_sextract_i32 0 @@ -106,17 +102,17 @@ extern bool have_zbb; #define TCG_TARGET_HAS_ext16s_i32 1 #define TCG_TARGET_HAS_ext8u_i32 1 #define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 have_zbb -#define TCG_TARGET_HAS_bswap32_i32 have_zbb +#define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 have_zbb -#define TCG_TARGET_HAS_orc_i32 have_zbb -#define TCG_TARGET_HAS_eqv_i32 have_zbb +#define TCG_TARGET_HAS_andc_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_orc_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_clz_i32 have_zbb -#define TCG_TARGET_HAS_ctz_i32 have_zbb -#define TCG_TARGET_HAS_ctpop_i32 have_zbb +#define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_brcond2 1 #define TCG_TARGET_HAS_setcond2 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -125,7 +121,7 @@ extern bool have_zbb; #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 -#define TCG_TARGET_HAS_rot_i64 have_zbb +#define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_extract_i64 0 #define TCG_TARGET_HAS_sextract_i64 0 @@ -137,18 +133,18 @@ extern bool have_zbb; #define TCG_TARGET_HAS_ext8u_i64 1 #define TCG_TARGET_HAS_ext16u_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 have_zbb -#define TCG_TARGET_HAS_bswap32_i64 have_zbb -#define TCG_TARGET_HAS_bswap64_i64 have_zbb +#define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 have_zbb -#define TCG_TARGET_HAS_orc_i64 have_zbb -#define TCG_TARGET_HAS_eqv_i64 have_zbb +#define TCG_TARGET_HAS_andc_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_orc_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 have_zbb -#define TCG_TARGET_HAS_ctz_i64 have_zbb -#define TCG_TARGET_HAS_ctpop_i64 have_zbb +#define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c new file mode 100644 index 0000000000..1236ee5fa4 --- /dev/null +++ b/util/cpuinfo-riscv.c @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for RISC-V. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" + +unsigned cpuinfo; +static volatile sig_atomic_t got_sigill; + +static void sigill_handler(int signo, siginfo_t *si, void *data) +{ + /* Skip the faulty instruction */ + ucontext_t *uc = (ucontext_t *)data; + uc->uc_mcontext.__gregs[REG_PC] += 4; + + got_sigill = 1; +} + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND; + unsigned info = cpuinfo; + + if (info) { + return info; + } + + /* Test for compile-time settings. */ +#if defined(__riscv_arch_test) && defined(__riscv_zba) + info |= CPUINFO_ZBA; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zbb) + info |= CPUINFO_ZBB; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zicond) + info |= CPUINFO_ZICOND; +#endif + left &= ~info; + + if (left) { + struct sigaction sa_old, sa_new; + + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_flags = SA_SIGINFO; + sa_new.sa_sigaction = sigill_handler; + sigaction(SIGILL, &sa_new, &sa_old); + + if (left & CPUINFO_ZBA) { + /* Probe for Zba: add.uw zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBA; + left &= ~CPUINFO_ZBA; + } + + if (left & CPUINFO_ZBB) { + /* Probe for Zbb: andn zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBB; + left &= ~CPUINFO_ZBB; + } + + if (left & CPUINFO_ZICOND) { + /* Probe for Zicond: czero.eqz zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZICOND; + left &= ~CPUINFO_ZICOND; + } + + sigaction(SIGILL, &sa_old, NULL); + assert(left == 0); + } + + info |= CPUINFO_ALWAYS; + cpuinfo = info; + return info; +} diff --git a/util/meson.build b/util/meson.build index 72b505df11..5d8bef9891 100644 --- a/util/meson.build +++ b/util/meson.build @@ -127,4 +127,6 @@ elif cpu == 'loongarch64' util_ss.add(files('cpuinfo-loongarch.c')) elif cpu in ['ppc', 'ppc64'] util_ss.add(files('cpuinfo-ppc.c')) +elif cpu in ['riscv32', 'riscv64'] + util_ss.add(files('cpuinfo-riscv.c')) endif From adc028428a6da5ea8d6a688085966a33be4c97c7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Jun 2024 15:13:31 +0000 Subject: [PATCH 1934/2884] util/cpuinfo-riscv: Support OpenBSD signal frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Brad Smith Reviewed-by: Philippe Mathieu-Daudé Acked-by: Daniel Henrique Barboza Acked-by: Alistair Francis Signed-off-by: Richard Henderson --- util/cpuinfo-riscv.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c index 1236ee5fa4..7dcfbee2ab 100644 --- a/util/cpuinfo-riscv.c +++ b/util/cpuinfo-riscv.c @@ -13,7 +13,14 @@ static void sigill_handler(int signo, siginfo_t *si, void *data) { /* Skip the faulty instruction */ ucontext_t *uc = (ucontext_t *)data; + +#ifdef __linux__ uc->uc_mcontext.__gregs[REG_PC] += 4; +#elif defined(__OpenBSD__) + uc->sc_sepc += 4; +#else +# error Unsupported OS +#endif got_sigill = 1; } From e57173085aab2dae3fb8b777ea9a4a252feb78a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Jun 2024 17:36:43 +0000 Subject: [PATCH 1935/2884] util/cpuinfo-riscv: Use linux __riscv_hwprobe syscall With recent linux kernels, there is a syscall to probe for various ISA extensions. These bits were phased in over several kernel releases, so we still require checks for symbol availability. Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Signed-off-by: Richard Henderson --- meson.build | 6 ++++++ util/cpuinfo-riscv.c | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/meson.build b/meson.build index 54e6b09f4f..2f981f936e 100644 --- a/meson.build +++ b/meson.build @@ -2862,6 +2862,12 @@ have_cpuid_h = cc.links(''' }''') config_host_data.set('CONFIG_CPUID_H', have_cpuid_h) +# Don't bother to advertise asm/hwprobe.h for old versions that do +# not contain RISCV_HWPROBE_EXT_ZBA. +config_host_data.set('CONFIG_ASM_HWPROBE_H', + cc.has_header_symbol('asm/hwprobe.h', + 'RISCV_HWPROBE_EXT_ZBA')) + config_host_data.set('CONFIG_AVX2_OPT', get_option('avx2') \ .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX2') \ .require(cc.links(''' diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c index 7dcfbee2ab..497ce12680 100644 --- a/util/cpuinfo-riscv.c +++ b/util/cpuinfo-riscv.c @@ -6,6 +6,11 @@ #include "qemu/osdep.h" #include "host/cpuinfo.h" +#ifdef CONFIG_ASM_HWPROBE_H +#include +#include +#endif + unsigned cpuinfo; static volatile sig_atomic_t got_sigill; @@ -47,6 +52,27 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) #endif left &= ~info; +#ifdef CONFIG_ASM_HWPROBE_H + if (left) { + /* + * TODO: glibc 2.40 will introduce , which + * provides __riscv_hwprobe and __riscv_hwprobe_one, + * which is a slightly cleaner interface. + */ + struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 }; + if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0 + && pair.key >= 0) { + info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0; + info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0; + left &= ~(CPUINFO_ZBA | CPUINFO_ZBB); +#ifdef RISCV_HWPROBE_EXT_ZICOND + info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; + left &= ~CPUINFO_ZICOND; +#endif + } + } +#endif /* CONFIG_ASM_HWPROBE_H */ + if (left) { struct sigaction sa_old, sa_new; From a71d9dfbf63db42d6e6ae87fc112d1f5502183bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 30 Jun 2024 19:46:23 -0700 Subject: [PATCH 1936/2884] tcg/optimize: Fix TCG_COND_TST* simplification of setcond2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Argument ordering for setcond2 is: output, a_low, a_high, b_low, b_high, cond The test is supposed to be against b_low, not a_high. Cc: qemu-stable@nongnu.org Fixes: ceb9ee06b71 ("tcg/optimize: Handle TCG_COND_TST{EQ,NE}") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2413 Signed-off-by: Richard Henderson Tested-by: Alex Bennée Message-Id: <20240701024623.1265028-1-richard.henderson@linaro.org> --- tcg/optimize.c | 2 +- tests/tcg/x86_64/Makefile.target | 2 ++ tests/tcg/x86_64/test-2413.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/x86_64/test-2413.c diff --git a/tcg/optimize.c b/tcg/optimize.c index 8886f7037a..ba16ec27e2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2384,7 +2384,7 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) case TCG_COND_TSTEQ: case TCG_COND_TSTNE: - if (arg_is_const_val(op->args[2], 0)) { + if (arg_is_const_val(op->args[3], 0)) { goto do_setcond_high; } if (arg_is_const_val(op->args[4], 0)) { diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index 5fedf22117..eda9bd7396 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -8,6 +8,8 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target +X86_64_TESTS += test-2413 + ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET)) X86_64_TESTS += vsyscall X86_64_TESTS += noexec diff --git a/tests/tcg/x86_64/test-2413.c b/tests/tcg/x86_64/test-2413.c new file mode 100644 index 0000000000..456e5332fc --- /dev/null +++ b/tests/tcg/x86_64/test-2413.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright 2024 Linaro, Ltd. */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2413 */ + +#include + +void test(unsigned long *a, unsigned long *d, unsigned long c) +{ + asm("xorl %%eax, %%eax\n\t" + "xorl %%edx, %%edx\n\t" + "testb $0x20, %%cl\n\t" + "sete %%al\n\t" + "setne %%dl\n\t" + "shll %%cl, %%eax\n\t" + "shll %%cl, %%edx\n\t" + : "=a"(*a), "=d"(*d) + : "c"(c)); +} + +int main(void) +{ + unsigned long a, c, d; + + for (c = 0; c < 64; c++) { + test(&a, &d, c); + assert(a == (c & 0x20 ? 0 : 1u << (c & 0x1f))); + assert(d == (c & 0x20 ? 1u << (c & 0x1f) : 0)); + } + return 0; +} From a0124e333e2176640f233e5ea57a2f413985d9b5 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Wed, 3 Jul 2024 22:08:12 +0300 Subject: [PATCH 1937/2884] char-stdio: Restore blocking mode of stdout on exit qemu_chr_open_fd() sets stdout into non-blocking mode. Restore the old fd flags on exit to avoid breaking unsuspecting applications that run on the same terminal after qemu and don't expect to get EAGAIN. While at at, also ensure term_exit is called once (at the moment it's called both from char_stdio_finalize() and as the atexit() hook. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2423 Signed-off-by: Maxim Mikityanskiy Link: https://lore.kernel.org/r/20240703190812.3459514-1-maxtram95@gmail.com Signed-off-by: Paolo Bonzini --- chardev/char-stdio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 3c648678ab..b960ddd4e4 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -41,6 +41,7 @@ /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; +static int old_fd1_flags; static bool stdio_in_use; static bool stdio_allow_signal; static bool stdio_echo_state; @@ -50,6 +51,8 @@ static void term_exit(void) if (stdio_in_use) { tcsetattr(0, TCSANOW, &oldtty); fcntl(0, F_SETFL, old_fd0_flags); + fcntl(1, F_SETFL, old_fd1_flags); + stdio_in_use = false; } } @@ -102,6 +105,7 @@ static void qemu_chr_open_stdio(Chardev *chr, stdio_in_use = true; old_fd0_flags = fcntl(0, F_GETFL); + old_fd1_flags = fcntl(1, F_GETFL); tcgetattr(0, &oldtty); if (!g_unix_set_fd_nonblocking(0, true, NULL)) { error_setg_errno(errp, errno, "Failed to set FD nonblocking"); From 4e647fa08586a5ada74cf6d3ae1cdf3a027202cb Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:05:19 +0200 Subject: [PATCH 1938/2884] hostmem: add a new memory backend based on POSIX shm_open() shm_open() creates and opens a new POSIX shared memory object. A POSIX shared memory object allows creating memory backend with an associated file descriptor that can be shared with external processes (e.g. vhost-user). The new `memory-backend-shm` can be used as an alternative when `memory-backend-memfd` is not available (Linux only), since shm_open() should be provided by any POSIX-compliant operating system. This backend mimics memfd, allocating memory that is practically anonymous. In theory shm_open() requires a name, but this is allocated for a short time interval and shm_unlink() is called right after shm_open(). After that, only fd is shared with external processes (e.g., vhost-user) as if it were associated with anonymous memory. In the future we may also allow the user to specify the name to be passed to shm_open(), but for now we keep the backend simple, mimicking anonymous memory such as memfd. Acked-by: David Hildenbrand Acked-by: Stefan Hajnoczi Acked-by: Markus Armbruster (QAPI schema) Signed-off-by: Stefano Garzarella Message-Id: <20240618100519.145853-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- backends/hostmem-shm.c | 123 +++++++++++++++++++++++++++++ backends/meson.build | 1 + docs/system/devices/vhost-user.rst | 5 +- qapi/qom.json | 23 +++++- qemu-options.hx | 16 ++++ 5 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 backends/hostmem-shm.c diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c new file mode 100644 index 0000000000..374edc3db8 --- /dev/null +++ b/backends/hostmem-shm.c @@ -0,0 +1,123 @@ +/* + * QEMU host POSIX shared memory object backend + * + * Copyright (C) 2024 Red Hat Inc + * + * Authors: + * Stefano Garzarella + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "sysemu/hostmem.h" +#include "qapi/error.h" + +#define TYPE_MEMORY_BACKEND_SHM "memory-backend-shm" + +OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendShm, MEMORY_BACKEND_SHM) + +struct HostMemoryBackendShm { + HostMemoryBackend parent_obj; +}; + +static bool +shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + g_autoptr(GString) shm_name = g_string_new(NULL); + g_autofree char *backend_name = NULL; + uint32_t ram_flags; + int fd, oflag; + mode_t mode; + + if (!backend->size) { + error_setg(errp, "can't create shm backend with size 0"); + return false; + } + + if (!backend->share) { + error_setg(errp, "can't create shm backend with `share=off`"); + return false; + } + + /* + * Let's use `mode = 0` because we don't want other processes to open our + * memory unless we share the file descriptor with them. + */ + mode = 0; + oflag = O_RDWR | O_CREAT | O_EXCL; + backend_name = host_memory_backend_get_name(backend); + + /* + * Some operating systems allow creating anonymous POSIX shared memory + * objects (e.g. FreeBSD provides the SHM_ANON constant), but this is not + * defined by POSIX, so let's create a unique name. + * + * From Linux's shm_open(3) man-page: + * For portable use, a shared memory object should be identified + * by a name of the form /somename;" + */ + g_string_printf(shm_name, "/qemu-" FMT_pid "-shm-%s", getpid(), + backend_name); + + fd = shm_open(shm_name->str, oflag, mode); + if (fd < 0) { + error_setg_errno(errp, errno, + "failed to create POSIX shared memory"); + return false; + } + + /* + * We have the file descriptor, so we no longer need to expose the + * POSIX shared memory object. However it will remain allocated as long as + * there are file descriptors pointing to it. + */ + shm_unlink(shm_name->str); + + if (ftruncate(fd, backend->size) == -1) { + error_setg_errno(errp, errno, + "failed to resize POSIX shared memory to %" PRIu64, + backend->size); + close(fd); + return false; + } + + ram_flags = RAM_SHARED; + ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; + + return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), + backend_name, backend->size, + ram_flags, fd, 0, errp); +} + +static void +shm_backend_instance_init(Object *obj) +{ + HostMemoryBackendShm *m = MEMORY_BACKEND_SHM(obj); + + MEMORY_BACKEND(m)->share = true; +} + +static void +shm_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = shm_backend_memory_alloc; +} + +static const TypeInfo shm_backend_info = { + .name = TYPE_MEMORY_BACKEND_SHM, + .parent = TYPE_MEMORY_BACKEND, + .instance_init = shm_backend_instance_init, + .class_init = shm_backend_class_init, + .instance_size = sizeof(HostMemoryBackendShm), +}; + +static void register_types(void) +{ + type_register_static(&shm_backend_info); +} + +type_init(register_types); diff --git a/backends/meson.build b/backends/meson.build index 106312f0c8..749b491f12 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -13,6 +13,7 @@ system_ss.add([files( if host_os != 'windows' system_ss.add(files('rng-random.c')) system_ss.add(files('hostmem-file.c')) + system_ss.add([files('hostmem-shm.c'), rt]) endif if host_os == 'linux' system_ss.add(files('hostmem-memfd.c')) diff --git a/docs/system/devices/vhost-user.rst b/docs/system/devices/vhost-user.rst index 9b2da106ce..35259d8ec7 100644 --- a/docs/system/devices/vhost-user.rst +++ b/docs/system/devices/vhost-user.rst @@ -98,8 +98,9 @@ Shared memory object In order for the daemon to access the VirtIO queues to process the requests it needs access to the guest's address space. This is -achieved via the ``memory-backend-file`` or ``memory-backend-memfd`` -objects. A reference to a file-descriptor which can access this object +achieved via the ``memory-backend-file``, ``memory-backend-memfd``, or +``memory-backend-shm`` objects. +A reference to a file-descriptor which can access this object will be passed via the socket as part of the protocol negotiation. Currently the shared memory object needs to match the size of the main diff --git a/qapi/qom.json b/qapi/qom.json index 9b8f6a7ab5..92b0fea76c 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -601,8 +601,8 @@ # # @share: if false, the memory is private to QEMU; if true, it is # shared (default false for backends memory-backend-file and -# memory-backend-ram, true for backends memory-backend-epc and -# memory-backend-memfd) +# memory-backend-ram, true for backends memory-backend-epc, +# memory-backend-memfd, and memory-backend-shm) # # @reserve: if true, reserve swap space (or huge pages) if applicable # (default: true) (since 6.1) @@ -721,6 +721,21 @@ '*hugetlbsize': 'size', '*seal': 'bool' } } +## +# @MemoryBackendShmProperties: +# +# Properties for memory-backend-shm objects. +# +# This memory backend supports only shared memory, which is the +# default. +# +# Since: 9.1 +## +{ 'struct': 'MemoryBackendShmProperties', + 'base': 'MemoryBackendProperties', + 'data': { }, + 'if': 'CONFIG_POSIX' } + ## # @MemoryBackendEpcProperties: # @@ -1049,6 +1064,8 @@ { 'name': 'memory-backend-memfd', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram', + { 'name': 'memory-backend-shm', + 'if': 'CONFIG_POSIX' }, 'pef-guest', { 'name': 'pr-manager-helper', 'if': 'CONFIG_LINUX' }, @@ -1121,6 +1138,8 @@ 'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram': 'MemoryBackendProperties', + 'memory-backend-shm': { 'type': 'MemoryBackendShmProperties', + 'if': 'CONFIG_POSIX' }, 'pr-manager-helper': { 'type': 'PrManagerHelperProperties', 'if': 'CONFIG_LINUX' }, 'qtest': 'QtestProperties', diff --git a/qemu-options.hx b/qemu-options.hx index 8ca7f34ef0..ad6521ef5e 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5240,6 +5240,22 @@ SRST The ``share`` boolean option is on by default with memfd. + ``-object memory-backend-shm,id=id,merge=on|off,dump=on|off,share=on|off,prealloc=on|off,size=size,host-nodes=host-nodes,policy=default|preferred|bind|interleave`` + Creates a POSIX shared memory backend object, which allows + QEMU to share the memory with an external process (e.g. when + using vhost-user). + + ``memory-backend-shm`` is a more portable and less featureful version + of ``memory-backend-memfd``. It can then be used in any POSIX system, + especially when memfd is not supported. + + Please refer to ``memory-backend-file`` for a description of the + options. + + The ``share`` boolean option is on by default with shm. Setting it to + off will cause a failure during allocation because it is not supported + by this backend. + ``-object iommufd,id=id[,fd=fd]`` Creates an iommufd backend which allows control of DMA mapping through the ``/dev/iommu`` device. From e349062727c7b35ac4c9bd08c534f64f571389fe Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:05:27 +0200 Subject: [PATCH 1939/2884] tests/qtest/vhost-user-blk-test: use memory-backend-shm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `memory-backend-memfd` is available only on Linux while the new `memory-backend-shm` can be used on any POSIX-compliant operating system. Let's use it so we can run the test in multiple environments. Since we are here, let`s remove `share=on` which is the default for shm (and also for memfd). Acked-by: Thomas Huth Acked-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Signed-off-by: Stefano Garzarella Message-Id: <20240618100527.145883-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/vhost-user-blk-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index 117b9acd10..ea90d41232 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -906,7 +906,7 @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances, vhost_user_blk_bin); g_string_append_printf(cmd_line, - " -object memory-backend-memfd,id=mem,size=256M,share=on " + " -object memory-backend-shm,id=mem,size=256M " " -M memory-backend=mem -m 256M "); for (i = 0; i < vus_instances; i++) { From 0173ce4b2bb4f159f4a6d54e14adbe35f826a848 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 18 Jun 2024 12:05:34 +0200 Subject: [PATCH 1940/2884] tests/qtest/vhost-user-test: add a test case for memory-backend-shm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `memory-backend-shm` can be used with vhost-user devices, so let's add a new test case for it. Acked-by: Thomas Huth Acked-by: Stefan Hajnoczi Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Stefano Garzarella Message-Id: <20240618100534.145917-1-sgarzare@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/vhost-user-test.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 255bde54ab..0fa8951c9f 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -44,6 +44,8 @@ "mem-path=%s,share=on -numa node,memdev=mem" #define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ " -numa node,memdev=mem" +#define QEMU_CMD_SHM " -m %d -object memory-backend-shm,id=mem,size=%dM," \ + " -numa node,memdev=mem" #define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" #define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce=on" @@ -195,6 +197,7 @@ enum test_memfd { TEST_MEMFD_AUTO, TEST_MEMFD_YES, TEST_MEMFD_NO, + TEST_MEMFD_SHM, }; static void append_vhost_net_opts(TestServer *s, GString *cmd_line, @@ -228,6 +231,8 @@ static void append_mem_opts(TestServer *server, GString *cmd_line, if (memfd == TEST_MEMFD_YES) { g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); + } else if (memfd == TEST_MEMFD_SHM) { + g_string_append_printf(cmd_line, QEMU_CMD_SHM, size, size); } else { const char *root = init_hugepagefs() ? : server->tmpfs; @@ -791,6 +796,19 @@ static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) return server; } +static void *vhost_user_test_setup_shm(GString *cmd_line, void *arg) +{ + TestServer *server = test_server_new("vhost-user-test", arg); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_SHM); + server->vu_ops->append_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) { TestServer *server = arg; @@ -1084,6 +1102,11 @@ static void register_vhost_user_test(void) "virtio-net", test_read_guest_mem, &opts); + opts.before = vhost_user_test_setup_shm; + qos_add_test("vhost-user/read-guest-mem/shm", + "virtio-net", + test_read_guest_mem, &opts); + if (qemu_memfd_check(MFD_ALLOW_SEALING)) { opts.before = vhost_user_test_setup_memfd; qos_add_test("vhost-user/read-guest-mem/memfd", From d72479b11797c28893e1e3fc565497a9cae5ca16 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Jun 2024 14:19:58 +0200 Subject: [PATCH 1941/2884] hw/virtio: Fix the de-initialization of vhost-user devices The unrealize functions of the various vhost-user devices are calling the corresponding vhost_*_set_status() functions with a status of 0 to shut down the device correctly. Now these vhost_*_set_status() functions all follow this scheme: bool should_start = virtio_device_should_start(vdev, status); if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { return; } if (should_start) { /* ... do the initialization stuff ... */ } else { /* ... do the cleanup stuff ... */ } The problem here is virtio_device_should_start(vdev, 0) currently always returns "true" since it internally only looks at vdev->started instead of looking at the "status" parameter. Thus once the device got started once, virtio_device_should_start() always returns true and thus the vhost_*_set_status() functions return early, without ever doing any clean-up when being called with status == 0. This causes e.g. problems when trying to hot-plug and hot-unplug a vhost user devices multiple times since the de-initialization step is completely skipped during the unplug operation. This bug has been introduced in commit 9f6bcfd99f ("hw/virtio: move vm_running check to virtio_device_started") which replaced should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; with should_start = virtio_device_started(vdev, status); which later got replaced by virtio_device_should_start(). This blocked the possibility to set should_start to false in case the status flag VIRTIO_CONFIG_S_DRIVER_OK was not set. Fix it by adjusting the virtio_device_should_start() function to only consider the status flag instead of vdev->started. Since this function is only used in the various vhost_*_set_status() functions for exactly the same purpose, it should be fine to fix it in this central place there without any risk to change the behavior of other code. Fixes: 9f6bcfd99f ("hw/virtio: move vm_running check to virtio_device_started") Buglink: https://issues.redhat.com/browse/RHEL-40708 Signed-off-by: Thomas Huth Message-Id: <20240618121958.88673-1-thuth@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/virtio.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 1451926a13..7512afbc84 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -472,9 +472,9 @@ static inline bool virtio_device_started(VirtIODevice *vdev, uint8_t status) * @vdev - the VirtIO device * @status - the devices status bits * - * This is similar to virtio_device_started() but also encapsulates a - * check on the VM status which would prevent a device starting - * anyway. + * This is similar to virtio_device_started() but ignores vdev->started + * and also encapsulates a check on the VM status which would prevent a + * device from starting anyway. */ static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status) { @@ -482,7 +482,7 @@ static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status return false; } - return virtio_device_started(vdev, status); + return status & VIRTIO_CONFIG_S_DRIVER_OK; } static inline void virtio_set_started(VirtIODevice *vdev, bool started) From e9fd827711ed47edfe8cf23036a56e5a83f2bfda Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 18 Jun 2024 17:17:08 -0700 Subject: [PATCH 1942/2884] hw/arm/virt-acpi-build: Drop local iort_node_offset Both the other two callers of build_iort_id_mapping() just directly pass in the IORT_NODE_OFFSET macro. Keeping a "const uint32_t" local variable storing the same value doesn't have any gain. Simplify this by replacing the only place using this local variable with the macro directly. Signed-off-by: Nicolin Chen Message-Id: <20240619001708.926511-1-nicolinc@nvidia.com> Reviewed-by: Richard Henderson Reviewed-by: Eric Auger Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index b2366f24f9..102e2da934 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -269,7 +269,6 @@ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i, nb_nodes, rc_mapping_count; - const uint32_t iort_node_offset = IORT_NODE_OFFSET; size_t node_size, smmu_offset = 0; AcpiIortIdMapping *idmap; uint32_t id = 0; @@ -415,7 +414,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) range = &g_array_index(its_idmaps, AcpiIortIdMapping, i); /* output IORT node is the ITS group node (the first node) */ build_iort_id_mapping(table_data, range->input_base, - range->id_count, iort_node_offset); + range->id_count, IORT_NODE_OFFSET); } } else { /* output IORT node is the ITS group node (the first node) */ From 93c76555d842b5d84b95f66abecb6b19545338d9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 19 Jun 2024 14:03:08 +0100 Subject: [PATCH 1943/2884] hw/i386/fw_cfg: Add etc/e820 to fw_cfg late In e820_add_entry() the e820_table is reallocated with g_renew() to make space for a new entry. However, fw_cfg_arch_create() just uses the existing e820_table pointer. This leads to a use-after-free if anything adds a new entry after fw_cfg is set up. Shift the addition of the etc/e820 file to the machine done notifier, via a new fw_cfg_add_e820() function. Also make e820_table private and use an e820_get_table() accessor function for it, which sets a flag that will trigger an assert() for any *later* attempts to add to the table. Make e820_add_entry() return void, as most callers don't check for error anyway. Signed-off-by: David Woodhouse Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/e820_memory_layout.c | 17 ++++++++++++----- hw/i386/e820_memory_layout.h | 8 ++------ hw/i386/fw_cfg.c | 18 +++++++++++++----- hw/i386/fw_cfg.h | 1 + hw/i386/microvm.c | 4 ++-- hw/i386/pc.c | 1 + target/i386/kvm/kvm.c | 6 +----- target/i386/kvm/xen-emu.c | 7 +------ 8 files changed, 33 insertions(+), 29 deletions(-) diff --git a/hw/i386/e820_memory_layout.c b/hw/i386/e820_memory_layout.c index 06970ac44a..3e848fb69c 100644 --- a/hw/i386/e820_memory_layout.c +++ b/hw/i386/e820_memory_layout.c @@ -11,22 +11,29 @@ #include "e820_memory_layout.h" static size_t e820_entries; -struct e820_entry *e820_table; +static struct e820_entry *e820_table; +static gboolean e820_done; -int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) +void e820_add_entry(uint64_t address, uint64_t length, uint32_t type) { + assert(!e820_done); + /* new "etc/e820" file -- include ram and reserved entries */ e820_table = g_renew(struct e820_entry, e820_table, e820_entries + 1); e820_table[e820_entries].address = cpu_to_le64(address); e820_table[e820_entries].length = cpu_to_le64(length); e820_table[e820_entries].type = cpu_to_le32(type); e820_entries++; - - return e820_entries; } -int e820_get_num_entries(void) +int e820_get_table(struct e820_entry **table) { + e820_done = true; + + if (table) { + *table = e820_table; + } + return e820_entries; } diff --git a/hw/i386/e820_memory_layout.h b/hw/i386/e820_memory_layout.h index 7c239aa033..b50acfa201 100644 --- a/hw/i386/e820_memory_layout.h +++ b/hw/i386/e820_memory_layout.h @@ -22,13 +22,9 @@ struct e820_entry { uint32_t type; } QEMU_PACKED __attribute((__aligned__(4))); -extern struct e820_entry *e820_table; - -int e820_add_entry(uint64_t address, uint64_t length, uint32_t type); -int e820_get_num_entries(void); +void e820_add_entry(uint64_t address, uint64_t length, uint32_t type); bool e820_get_entry(int index, uint32_t type, uint64_t *address, uint64_t *length); - - +int e820_get_table(struct e820_entry **table); #endif diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 7c43c325ef..0e4494627c 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -48,6 +48,15 @@ const char *fw_cfg_arch_key_name(uint16_t key) return NULL; } +/* Add etc/e820 late, once all regions should be present */ +void fw_cfg_add_e820(FWCfgState *fw_cfg) +{ + struct e820_entry *table; + int nr_e820 = e820_get_table(&table); + + fw_cfg_add_file(fw_cfg, "etc/e820", table, nr_e820 * sizeof(*table)); +} + void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, SmbiosEntryPointType ep_type) { @@ -60,6 +69,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); MachineClass *mc = MACHINE_GET_CLASS(pcms); X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu); + int nr_e820; if (pcmc->smbios_defaults) { /* These values are guest ABI, do not change */ @@ -78,8 +88,9 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, } /* build the array of physical mem area from e820 table */ - mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries()); - for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) { + nr_e820 = e820_get_table(NULL); + mem_array = g_malloc0(sizeof(*mem_array) * nr_e820); + for (i = 0, array_count = 0; i < nr_e820; i++) { uint64_t addr, len; if (e820_get_entry(i, E820_RAM, &addr, &len)) { @@ -138,9 +149,6 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, #endif fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, - sizeof(struct e820_entry) * e820_get_num_entries()); - fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); /* allocate memory for the NUMA channel: one (64bit) word for the number * of nodes, one word for each VCPU->node and one word for each node to diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h index 92e310f5fd..e560fd7be8 100644 --- a/hw/i386/fw_cfg.h +++ b/hw/i386/fw_cfg.h @@ -27,5 +27,6 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, SmbiosEntryPointType ep_type); void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg); void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg); +void fw_cfg_add_e820(FWCfgState *fw_cfg); #endif diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index fec63cacfa..40edcee7af 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -324,8 +324,6 @@ static void microvm_memory_init(MicrovmMachineState *mms) fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, machine->smp.max_cpus); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, - sizeof(struct e820_entry) * e820_get_num_entries()); rom_set_fw(fw_cfg); @@ -586,9 +584,11 @@ static void microvm_machine_done(Notifier *notifier, void *data) { MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, machine_done); + X86MachineState *x86ms = X86_MACHINE(mms); acpi_setup_microvm(mms); dt_setup_microvm(mms); + fw_cfg_add_e820(x86ms->fw_cfg); } static void microvm_powerdown_req(Notifier *notifier, void *data) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 77415064c6..d2c29fbfcb 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -625,6 +625,7 @@ void pc_machine_done(Notifier *notifier, void *data) acpi_setup(); if (x86ms->fw_cfg) { fw_cfg_build_smbios(pcms, x86ms->fw_cfg, pcms->smbios_entry_point_type); + fw_cfg_add_e820(x86ms->fw_cfg); fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg); /* update FW_CFG_NB_CPUS to account for -device added CPUs */ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index dd8b0f3313..bf182570fe 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2706,11 +2706,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } /* Tell fw_cfg to notify the BIOS to reserve the range. */ - ret = e820_add_entry(identity_base, 0x4000, E820_RESERVED); - if (ret < 0) { - fprintf(stderr, "e820_add_entry() table is full\n"); - return ret; - } + e820_add_entry(identity_base, 0x4000, E820_RESERVED); shadow_mem = object_property_get_int(OBJECT(s), "kvm-shadow-mem", &error_abort); if (shadow_mem != -1) { diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index fc2c2321ac..2f89dc628e 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -176,12 +176,7 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) s->xen_caps = xen_caps; /* Tell fw_cfg to notify the BIOS to reserve the range. */ - ret = e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, - E820_RESERVED); - if (ret < 0) { - fprintf(stderr, "e820_add_entry() table is full\n"); - return ret; - } + e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, E820_RESERVED); /* The pages couldn't be overlaid until KVM was initialized */ xen_primary_console_reset(); From 5786827f47a0721bb997ad3f653d2b843ba3fd76 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 19 Jun 2024 13:12:43 -0700 Subject: [PATCH 1944/2884] hw/arm/virt-acpi-build: Fix id_count in build_iort_id_mapping It's observed that Linux kernel booting with the VM reports a "conflicting mapping for input ID" FW_BUG. The IORT doc defines "Number of IDs" to be "the number of IDs in the range minus one", while virt-acpi-build.c simply stores the number of IDs in the id_count without the "minus one". Meanwhile, some of the callers pass in a 0xFFFF following the spec. So, this is a mismatch between the function and its callers. Fix build_iort_id_mapping() by internally subtracting one from the pass-in @id_count. Accordingly make sure that all existing callers pass in a value without the "minus one", i.e. change all 0xFFFFs to 0x10000s. Also, add a few lines of comments to highlight this change along with the referencing document for this build_iort_id_mapping(). Fixes: 42e0f050e3a5 ("hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3") Suggested-by: Michael S. Tsirkin Reviewed-by: Eric Auger Signed-off-by: Nicolin Chen Message-Id: <20240619201243.936819-1-nicolinc@nvidia.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 102e2da934..e10cad86dd 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -209,12 +209,19 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) #define ROOT_COMPLEX_ENTRY_SIZE 36 #define IORT_NODE_OFFSET 48 +/* + * Append an ID mapping entry as described by "Table 4 ID mapping format" in + * "IO Remapping Table System Software on ARM Platforms", Chapter 3. + * Document number: ARM DEN 0049E.f, Apr 2024 + * + * Note that @id_count gets internally subtracted by one, following the spec. + */ static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, uint32_t id_count, uint32_t out_ref) { - /* Table 4 ID mapping format */ build_append_int_noprefix(table_data, input_base, 4); /* Input base */ - build_append_int_noprefix(table_data, id_count, 4); /* Number of IDs */ + /* Number of IDs - The number of IDs in the range minus one */ + build_append_int_noprefix(table_data, id_count - 1, 4); build_append_int_noprefix(table_data, input_base, 4); /* Output base */ build_append_int_noprefix(table_data, out_ref, 4); /* Output Reference */ /* Flags */ @@ -305,8 +312,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } /* Append the last RC -> ITS ID mapping */ - if (next_range.input_base < 0xFFFF) { - next_range.id_count = 0xFFFF - next_range.input_base; + if (next_range.input_base < 0x10000) { + next_range.id_count = 0x10000 - next_range.input_base; g_array_append_val(its_idmaps, next_range); } @@ -365,7 +372,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 4); /* output IORT node is the ITS group node (the first node) */ - build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); } /* Table 17 Root Complex Node */ @@ -418,7 +425,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } } else { /* output IORT node is the ITS group node (the first node) */ - build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); } acpi_table_end(linker, &table); From b05ff4086f79cdc59c4adcfd278259792a8bb714 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:25 +0530 Subject: [PATCH 1945/2884] uefi-test-tools/UefiTestToolsPkg: Add RISC-V support Enable building the test application for RISC-V with appropriate dependencies updated. Signed-off-by: Sunil V L Acked-by: Gerd Hoffmann Acked-by: Alistair Francis Acked-by: Igor Mammedov Message-Id: <20240625150839.1358279-3-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc index c8511cd732..0902fd3c73 100644 --- a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc @@ -19,7 +19,7 @@ PLATFORM_VERSION = 0.1 PLATFORM_NAME = UefiTestTools SKUID_IDENTIFIER = DEFAULT - SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64 + SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64 BUILD_TARGETS = DEBUG [BuildOptions.IA32] @@ -60,6 +60,10 @@ [LibraryClasses.IA32, LibraryClasses.X64] BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf + RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + +[LibraryClasses.RISCV64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F From ad8560fa29fc18acda2e8cfc1fdd87f6c6cca122 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:26 +0530 Subject: [PATCH 1946/2884] uefi-test-tools: Add support for python based build script edk2-funcs.sh which is used in this Makefile, was removed in the commit c28a2891f3 ("edk2: update build script"). It is replaced with a python based script. So, update the Makefile and add the configuration file as required to support the python based build script. Signed-off-by: Sunil V L Acked-by: Gerd Hoffmann Acked-by: Igor Mammedov Message-Id: <20240625150839.1358279-4-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/uefi-test-tools/Makefile | 19 +++---- tests/uefi-test-tools/uefi-test-build.config | 52 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 tests/uefi-test-tools/uefi-test-build.config diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile index 0c003f2877..f4eaebd8ff 100644 --- a/tests/uefi-test-tools/Makefile +++ b/tests/uefi-test-tools/Makefile @@ -12,7 +12,7 @@ edk2_dir := ../../roms/edk2 images_dir := ../data/uefi-boot-images -emulation_targets := arm aarch64 i386 x86_64 +emulation_targets := arm aarch64 i386 x86_64 riscv64 uefi_binaries := bios-tables-test intermediate_suffixes := .efi .fat .iso.raw @@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat # stripped from, the argument. map_arm_to_uefi = $(subst arm,ARM,$(1)) map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1))) -map_i386_to_uefi = $(subst i386,IA32,$(call map_aarch64_to_uefi,$(1))) +map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1))) +map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1))) map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1))) map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1))) @@ -70,7 +71,7 @@ Build/%.fat: Build/%.efi uefi_bin_b=$$(stat --format=%s -- $<) && \ uefi_fat_kb=$$(( (uefi_bin_b * 11 / 10 + 1023) / 1024 )) && \ uefi_fat_kb=$$(( uefi_fat_kb >= 64 ? uefi_fat_kb : 64 )) && \ - mkdosfs -C $@ -n $(basename $(@F)) -- $$uefi_fat_kb + mkdosfs -C $@ -n "bios-test" -- $$uefi_fat_kb MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI/BOOT MTOOLS_SKIP_CHECK=1 mcopy -i $@ -- $< \ @@ -95,15 +96,9 @@ Build/%.fat: Build/%.efi # we must mark the recipe manually as recursive, by using the "+" indicator. # This way, when the inner "make" starts a parallel build of the target edk2 # module, it can communicate with the outer "make"'s job server. -Build/bios-tables-test.%.efi: build-edk2-tools - +./build.sh $(edk2_dir) BiosTablesTest $* $@ - -build-edk2-tools: - cd $(edk2_dir)/BaseTools && git submodule update --init --force - $(MAKE) -C $(edk2_dir)/BaseTools \ - PYTHON_COMMAND=$${EDK2_PYTHON_COMMAND:-python3} \ - EXTRA_OPTFLAGS='$(EDK2_BASETOOLS_OPTFLAGS)' \ - EXTRA_LDFLAGS='$(EDK2_BASETOOLS_LDFLAGS)' +Build/bios-tables-test.%.efi: + $(PYTHON) ../../roms/edk2-build.py --config uefi-test-build.config \ + --match $* clean: rm -rf Build Conf log diff --git a/tests/uefi-test-tools/uefi-test-build.config b/tests/uefi-test-tools/uefi-test-build.config new file mode 100644 index 0000000000..1f389ae541 --- /dev/null +++ b/tests/uefi-test-tools/uefi-test-build.config @@ -0,0 +1,52 @@ +[global] +core = ../../roms/edk2 + +#################################################################################### +# arm + +[build.arm] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = ARM +cpy1 = ARM/BiosTablesTest.efi bios-tables-test.arm.efi + +#################################################################################### +# aarch64 + +[build.aarch64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = AARCH64 +cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi + +#################################################################################### +# riscv64 + +[build.riscv] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = RISCV64 +cpy1 = RISCV64/BiosTablesTest.efi bios-tables-test.riscv64.efi + +#################################################################################### +# ia32 + +[build.ia32] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = IA32 +cpy1 = IA32/BiosTablesTest.efi bios-tables-test.i386.efi + +#################################################################################### +# x64 + +[build.x64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = X64 +cpy1 = X64/BiosTablesTest.efi bios-tables-test.x86_64.efi From 2f95279aa8c3ced9607eb5959c20d2995ecd980b Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:27 +0530 Subject: [PATCH 1947/2884] tests/data/uefi-boot-images: Add RISC-V ISO image To test ACPI tables, edk2 needs to be booted with a disk image having EFI partition. This image is created using UefiTestToolsPkg. The image is generated using tests/uefi-test-tools source. Signed-off-by: Sunil V L Message-Id: <20240625150839.1358279-5-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- .../bios-tables-test.riscv64.iso.qcow2 | Bin 0 -> 16896 bytes tests/uefi-test-tools/uefi-test-build.config | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 diff --git a/tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 new file mode 100644 index 0000000000000000000000000000000000000000..c720bf99a45fab6d1e21963cca563ee0ea059b82 GIT binary patch literal 16896 zcmeIZbyQp5w=Wvpf_t$*DNx*s76?*Gkpjhw1#fW*1ef3~T4-^K6sNdLaVJ>O;85J1 zQtYN*dfvO^_r|&BjPd@sc6P{~`K&p2_F8kzHD!gZF8|^R005vN-~auA^AEuE|F<^$ z`wy6ZG3pwPn*YYNLL{ z@~dP#ETCmdJ*7_kgqm;AKA-YZ)H7i(_@NMQyg)%>KxsGsR-2gBcMs8>CNZu}?giTr zW$PK&8%mMm2N;bN$uC%lqEk?%*o3+>e_ic4JOyZ|QDL}X0t1oRteN9K<8(Pw$37xF z1|I2P6F9n_yPUW{fk&qoW*`a$lHzw{Kz0D&>R{It0_pAL#g%74US}QyK_2E*7!OJR zAOI>3rhJGxFB1wt!!!m0K*PZ#AnIyU>wpil!a2G-TU)W^B7llPn!FEq6{Iy4q~)j} z>{K9cNmkXnrC&tPStW#fh-_pr=`de}dA<-N&D^d}y}pq>O>MJ&j$(%bF!LVvC;g^~ zXE=dk`Gi+tSz8DwS_3uBWFbZj%AzyV%_=l~oF%n-a=AdsJV^C9}F;wuhCstjLCnUP*F48|zU^QfHp zBMJ#O{w?+cY7szMfzGGoxOAWs2Ye9nKr0PthyUvz;D5RZZ20$j`j@}-Kj!00+{+OG zApZywo!w&+yn9UQdymNi?=d<2o>EHQw^NnfV`{`brO~{nv}N~vdis0JU~x}DfBiTU z_#QueeNUO^!GJ#p3peB+${KJ_*~Y>DV0OiO%z=B4IqmK-*N1z`!*k!xn|6=+obNIJ z-|rFdzsG_R_xvXb_gE=9YrM4Ci?)1f@8Iihn0CWpe5WAz@Sgkk! z`gPpBm$H~L9JTE~PC!(x9k^sr57SWaemy2t{-hFrElVz*fv{aT9xicJN6~n1W2%AH zFz7ge3Yn;tW&tB8wW|-F68=c@4o$;g>UMmqbv5_1c)wkvMLraUAyg?f9Xt3O+n84 zxsFBX$^U1H-jvM19JShUm36pu-|w@BOyrj!y)XL!K-(D4iF`Jx?;EZRkq(^%-V;CE zcCCm2TZd0Zyx9J>IzrROgqwwaV4a)5#CQHST8)M~U(xf(CglJ&*7=;~S6QbpX;V)T zw2syD4S@~j;erLP2kkcLLaZLfMRkwS0(o~v2kvzw!X~{MrJ5esl&4GEOJV7d&hn8{ zeXmzCDRM1cZ?;5Rdn$LUpGE2RNlNXtlH|}3`e>#khWUA_OBnq{A4nn&@%q-@rrO#r zxunkjqcl+%<=?T&QPlY8on}hQAT0X`EyB5^YASJg%Nk_nMh*dNzwzGuKByng+~1e4 z(>xQDn?MQP0zxSLe$(8ldOyAz4en>*y!(XjKOM3bU4b#c5Vo*p2u|1JNb!apiEl7F z6oe$t^09h6zbrWyIyW4@vXsP?ApYD`Ds;wbvlic;r&SvoW5h{zAnIo~U0XGI;nY+E^rWKx**=fVr<=(@I zO5qv}aXk5CR8{LkI<~~9xK4fKH@V}TA6(DfZG-1LPdWF)-~zu^aB6Q!#@P^;GI@u; z4tpm`m-41mquxn8e1f?;V%ABiZlX3YrJ~3ltEae`Zce;$8)|WdW*6O}gvLkKGd3_9 zK`!RvNGvcn)8@&>_L<;My9Y^gH$Wy9eAsc z*l+dtwVGq!z*W<*a1;Eh1G*bV8qoDlueCBHZfEf7Q`bwwGQ{VxZ8~GZq<3WpzWh?k zT^6RC1loy@L<6#`Y~N7#q0j_Ql`njd2(+Sh@_)AET3l<&=RUtU`}k1AHlY=2AWFiR zovd$E!^p++iWWXf$oR@TjbdO6BO>4t4(8kFvAP_iAL-p&EUHtW1AuDMT@z$@l<*S619(Zb8 z11;tS2SD(-G3d&nqSq}%+pP^FazC!cZ==@6M{`LtD{*?fm96(&ehVYezoIYJk4S)) zQ@i?t9&_f*Va3bo2_~kAP|b(*l9fU*#m*RR{`>>b<@(Mp5FAI6`bxJvxvJG&>q@ii zg+L}t4!No&{*r%U&TG_aRqAZhC;`bhs?$*wv3TL6Y8Z{_D;vpiNzt+web{los3i}D zk=~Bo$0P=jR8wMzSnp-9$l=K-yPkD#7M)*XV5<^J@Ux1wx3U7f9})pl;@=IKPZese z;0ivjh}nz&5sitdmdK%Rd-?DXy!yTqFG;u`lM~+0!UT^)NS`K1Wr!rt4c$`qVj_-) zONtSn)_5x44j6~gkfT#mgH6(L1vL^s9+gzQ&xoy%{z?)E9PzpMgkLj<)^HeA(e+t; zZ2nJZY*Z3rKz0D&^~+lf4XxuO1hZaiPlHf6rE)Ii(#RkZCY?CQ<;uT0auZ$W*bk0WdiVP6=D`al1d^tu2Ltds|9(CyFZmdEpQ3olTc!W)ow(-WJ(Ev zwi!l6z4{Hr?d$K(pG0wVla<{5y%qX0=B$2JR@RyaLH#_5k@PH40g9?rA|TJG6q(a1 zC>p>j>_f`2cPTO`1%Z0{y)kBl@U`3s2ou*@iRwJrR=)?3|! zzb=BOYGq}bI)_Y74y>ePd|u(&FjaV31ajTwHU^%S`I)=w*t-w4_5w6I{@8s(;o3pc zpx*7E`y7z@g6yD=0_y{AC4=AWCd&|}c{$yc>xfpO7C7^*=huINDC<+poR;LcId^*N zS5G>b&k{)bw2M18?fr&;r?U|~W7K~V3y~(Hi3KlOrjzTf)YYVet_kqIdSw#a)BlA5 zd9S5Pb#T629fsBg)e2=;e!FuJ&VDozOO@zLOD#x9;*vFvdWs|d>NZ`Mn)7}K-2D*S zME*)5h`MK&rBVny2ze4b-Rds`CUae3wFSI=ecVBi37ia{_9X$`wzVfKjeoXPs^+Kc!n;i1!26 zA`<~LJYj@y42SeBtp3-&M_Bm81;_vhutDByM$ixe>1+AxgU^etTRa+&nY8gSK9nFV zvE%m76x250soo4>6YDub{@fXE$$6Js#E-OMJ1x0**F?^e?p6@IM)@uO4w{iu2Cm7Z zCQklkk*1Ea8`kz)sb-It$EnACww!*66Vcb0$!A_jrEzp}L9R;61OyxOxd9b-Y$@I- zAF8h_ZG7UF;fgfVejt~<>InIEyHe52-fM@#)Fjt12qKU-5uJ* z6j-Jy(hHe)^D%cCW{NQ=o0_+-zf;S9l7a>>yR3cackc`@5CZh7RuG8$f+J+V@OH!+K$dW|Xf^n8IhY(28FQjzKhKv?y ze6l{B5)tTJ?F2fh8%aR(uX%Y=Vwmm0|L!t?qb z{rTWLU#p-QVdwifL^>0+dA|Cy0+8l;$K4(H3_+Aofuo9Cz_7a0eC+XS7VMi0hULa{ z`k;fkY?jIg;Ik@R8MsD1R(;QX#l$hH2=v02G=?LJP8APMYjgQqLv!*QYvGgx#F);KHR7v7iorV`*AdN zN%-+a5@19pszp)hFGb=uz$S{&)ezk}-gX9g#<09oveKK$Z%;T2t!WrK0Evv+JX-j~Aj zsW6n@A!HH4nuJ_l=~}bR+CerXU!<=A??#cf7^}%-SOp@SfA-ckPLDhY#Qn-5H2+;# z$6nT{U`BWapW&(_<=C@`07VBXbnwcb1@WX|wwfiUET9Xq!1f)`S9U#S066}Mw6trZnJ;%&bBRIBNS|kMb>&4SDDpb>mFe|)6A@ul913}EBRz{^MT-)L>_a<<8ykGHdKa24#OxJD~jA&`h6*jiA za=)ZuHl1q7_b=#tQeKc*p|*t=7*sJBE7eb-e+yW<6{5#V;yZEr+Q|KqM)#vFAPIj3 zWl(Bp=#bdDnPl4KxpWPyA|aJXt^hCZQwNdO&fYRM*yFB`#D**|V#W@^%mUa)lx&$T z95tgfn8`bFuUT5Fj3mfqzf28}B7?uZklwK>0x}?Fk@65+YkVnigEZw~ng`xG$kG-F zHN1eItXKnq?Y7qSNYgodUxbSIrg{R}rm}_I{t~_{6p{TZE->;Onb7zJBdBx}k@d2) zAxBFgooMPIqHrnX?N0f4-#09D#+sU3*>Nu3V1(E_?P@7Bl)7(F*Uk?VW$R_mILna3 zfDbHleE?#>Hw4VsK&8y|4{t=K}1(|XNK{H@e>m_(=V*BCAA_EYiUOnyB{Ee zX94I~(sEda1_rwznW8A}fTA~i1dkrSikNBK6gTVITA24*JlOCmRrIvE;R$r5T@&Rz zd)I(%RrLZF=gVfYd?qv#%Jad%fH9a#wLVb>KPcw0>Ug;ewqch_QFKw9jZzZ6hoqZY zT%+T5%+e}#+pEt@n{;gjGpzCM7w+kom~=ga>>n&hop5q;URpHLosQK{5$)ugI?f+e zO3uCJl^wu*S+4n^=!33x!(n$Xv-l1#A+GiBswHoJ*VmDpmY@Omm!OUV{@>}{KR-ms zOFtLucFElC4!3wx`~c;gL(n;EnYoNfYigjqHmS?GbulY@GX)Omq~=zGQwd zlPN+Jydg0^MTOIqaZdNSsbD}-=%et71_vzEX}!wsR&?G|;N7(IN^#BDS;4+O-C3%) zeoNCA2OA+PkEvfxqY}ogZB@w1V}A8ums3eqmGYm+{`5`MN%zT^)b-t6Q>L^fvLMKJ z_gtdgU^(jUgRgTQ#7RB1BKc=~N4&<9pCd52?s3du{|xSEPAmTC@_uOc&mCvXG9S;h z{eDm5+g(F|y7L^3$tMu@aDU_n?;|y3qJ-c z=8|W(008%q=gYaX8T+PT976xhl;<5ESWL_N%zo;HQ)bEn08lf}gfZcWp6jx9qt84v z5WV@K+$VZ$9+|MYPilwlb>mR6>Sqp+8fK9LKWvN5)QD-iSzacA*3o~3^6;3-#k^0 zE_=FTa-^B(xt#T!+>l&zN7)C$FCkW0vEA0yU7YzfEo@EAm~l|!+_xM1_fO_ijMu>= z*SS2BX>OvLk&g{&%B%~d_tk2W&wbTv8qP~_=jKE(eD)ZJQWYJkFQKKmuem|XRUvZ*Zbu= z3GdbP^667MSFHM482#91^()d(>Yrupewt15O%%VG+{+xxv6gtiWXf}mbyiVZl|`&F z)%1uw!zwy_YY(K#W7_aqCl;$Vo1_VYu^FQ{{L)|8-BYX#bnAhsf1bidP#in~eF~|&Z_SS#kjfDgC%UY&q z8PuTIsBXSB;OOG8Z6IlbT3q;jpHa)LaWa0-g3%tq23@J+ugi3((`(VrIpA1->#a@x zfLM%?V9M6m<=iPRRoKU=&BFc~FXi?*Vs)SqS-QH#rB@0tl_$ka(>k8JL8H1aK$BAl z5T7N;#`O1~Z{l^^etl`mpx#2jL=(BagOys1$Fb$hcV8PSx)O{zDE(I{D$o!P;BTQT z&Z*KXO&s8+q*C=We{(XOjO{lI3%3l+1Qj->ZW~8^yFle|M2k1tk$BrRzT<8Cem8Qz zI2-|b8Z$7}Y`j%sd$OifdG*=h&)!A4`vazv8=l^qVvuM?A7@LPLz#fI_j#q?wwVG| zzJUL@vSkb4N&4D__u4b{AZ`R%*6eB_|98OU7P0Y&b3ubMOy-4{2L(& zvgh$>@S$TAYo575!UPl7Y7_z;8h@9{YinWwC) zrN!l&3y5K;dXG@P{)9QBeQ=#-CjR=5+M=?C@s$C}Z`>FkI$Th@npuo3txPa~_;u)~ z_?@F!w4gj|LH^J}b|slK@BjF6!kHo_%<<~tt`Qkj+*GhkoHBTV5eUk?WCH$Ba`R!G z7^9ey`u#q_F9I;##+g}v>#L>hSyt)sz+Ee3$@7`>$`HHV@)GyxrvOSA!`|yW0so(0 z4c?Kfozsn6w}8K60{rz*)t+pddnrKv zBIPD>8@e~R&gJKOj@+2PY(;L!nB;g>th$A4Z3BVG+`isFtL2>z*nJy9Ka$;&f{+s7 zMN^r3Vu)B8XjIO>eg}~QFj1@Q6r#T=?*4g%bs~W-139jc6zh}6JYl@#T;Yy zG$Rfr$kMdmoN2qIQtUIo#m|wwLrv4?o*I$nNACk+V1$Y(j%Egeh{ar>L~S`Tn2+#1 zPh%%vFLqIUZkYRi;gf#x-Hp&Hsb#+ABk>ZQ5DP{}twgr;ZfP1zw#bHp&ACI-7+JoF zMvoo^OU6Tm52@YtsIZ4_9d_P|8Yr*;8 zOSLWY2(_gTk{;c>QLCaE{w@6Zk+T|0R_VLmM@}6(N6<+RcDsj<4G}BsBHq}cD&d+3 zxhK@RQ?T#6%hN%Tv@HNW+cOGIwb%8Gzbf9AD)RRgq$>_liM83TPgs}Mx$KkPc$hnX zX>xl%w6X~&ryJNGC*6O}mj`ef(?#6u)NSDObUDPc-k@F`2P0ENL#yAzx9R_^7^$cD zt9SN7R+sfjH;sXY`p&;Uwm|pd5LAgNDtfnkDK$;$x65~T8~B9u?6aI68;yY6*EI~; zB>AB@Y{wQ;E*5b-j&XSbDqp;4L;IBTS;Xg1lIC`A*Jf7_7B4C3F$+2bE6+rG;R_ug zS2&pbYDRZnZBW#a&O8eF+mLXgUSIJpD)_3Uwt_?7`|{isV$`1KSlxzffu755&RE)oJhPF(w{uIIA|@jUr6mcG6BLwF z6I2+_SQ?pGA;H4I@7i*7xv3ZTl6vw+^o!R){l&%8(k_Lg_S*5z&725)p&_6CErT8M z(SnGp6I|a=;fD~64t9F{4NFb}Zy~sEZuk$&j{0tjj&3|lm4|#|=oYlLzT`>{OPiX> zenyU5$5-RS9EF5@Kb~$>oCHTr?wuK7+(a=Cvn|G~q@tx{v1nnc@D@Orxf}_Y#jn9G zVhSwsQBN?w+!iL0A>ob2^YC6Wf~jWZuO0My6mqr#F^E zK4Zh6Wg~F=2E$No;AK0QjE>QMy3TR=hso$Pw<91pSX~zHa~aBy&{?sO$3mZKpt&ee zIY!?AuY~<;lp72wSmY50`fLE^(>~^nFJAj_3FkPmCBXFJjkjMiPl7S&;gO@Je)hoF zL9Jecpy9Xi%bkIrYr65}s{KRJJ^undZa`ZUx71>mxSkDZliBmQO+!zZY**{;sK zbA*mMh?s^2y?oQ-gN!MnK5;#9LzL`A=3b&g^tihO*Qj+x_Ee;2^=lbL*L zUqZW{-^MJ$AU<=Ww7Q;o5!GiiN-yy=S=b5_h}p#fv;ms22_04=gxEPmjb_uVX1z}p zlp_HbQ$ zDHCbT7LR?8UjFXo{AI%{?@JTl{a}&4~Nh} zi22saY}Y(Uj!pC}jUw<9uPx{KYrWX95We%xrZ$-IAkUZDOl>JOIxUj@G@tiq*JGc& zSvyy%R~)GAJwiNK!e?DrzeU(7-PT3FexP|8q^6*o=S>5wLM_?95b)f!AsV4)zq@GFa3gS@YI8x-Gz60UE#+@Y<2CE zZz_Uz7t6*DljLONqbIS^&X;KeqE)-(JKRs|4tU^*29rdG0|bB1S&cc~cc*?jy?|gN zQ@eS-LiUsbZT+7T=N*f76cJ2}rrcxhLaJs%=^{jRZW_YaHuzsX)I#XxXL+YKhjYbN;s1tpP0G?P({a;ilV{ zHVPfbRuVar^Yimp*u@d-TF!&@Txkby?qV!4X#}w)k_K^du!70!O@7Ap)bZxwd@O_% z%F1p+wNFJ@C4Bv7NVZD@z1JK9a?-*8!UZMK(0Dy z%rQ@~-f$u%sqZZr+&y$Xv#FLGxp-5#c!Tb$W~dqnCTdErXpZUVq(9SB?v(r_M}t;e zI*5a3Q)l6pnm9l@w-6(@ z>$|(kQE_iq_;1h)E8^Y<;+&>Q%kFj`uNRC>>^{fKw)uoPq{R%pL@xJdmFpZK&tmY2 z-wAONN#kbb5XWTI8R~2AVk#9FQs?3NMUQ0@YL%G?`NGqjOG9D|yQpH(6^KAp@bG7_ zg12^$mT?uH4>32&3?C0X9!%qZz)cE%WAn-128eF9NGOVn5fZbArjkc0PibM)RAy`R zOMjb7S1852lx>wt+u+HoIn+--sW)$*#om@%7{}zQT5OGe z!q8kax#wA+Wf7D;#BSK~!mv>_S`_Yu7NF`L%`4(%;Sg`bTD*?GRz$2$1U4xyf9IaJ=oBWnZEqGDl86w8fYym%BB$DU5JZ zAKQk)AN#dbIA(kzLm$uIraCqf!30;9Cx1Pomw|Bc9wpRKW7`(#Gl=-rey)Br8eIbI zwy`pg8?a_S9vy<`eT7DP_iRebdsbM&-u%Kz`zS>iz)RG1$+iInp>cMjV2x9~xTYLU z5>z`RVZ+2_^!4QElIzGeO!lg8zM8kOLMy4c^&p6+j#nXfraAro6E$}!S>8|Wb5awm z77$UPkt#>C?Y=T#A?rc%*ko9YJ-NNqEK5zBng6D9m+r6_FDJymq2m_u?fLJNc3u`( zOcMzwpG#~%rOvCt{TPnRqT<6Nvt_2TzY z2vdh#X@8D+hP}&*ch?UT0Nf4<(@U|?-?1?n1$L|*qT9s`*ta7M`jV`)&|CxSw~@;z z*(*l%4-be2tdoI|u4vvGywe>U=?|!ThPq$9ECtyb!jG zj^^At{H)$vJSB4+nfXChJ)AI7zh^4nJ74H3AWl8rOz-TM&HFg(zUM1({?Mn$xgnXs z2)v_6c(#$j_QA5_$OZYW0@zW>r_Wgfnbg=OrZ$_6Pe6Uw&-df(CrlGpf_1 zlF_!x!UkP(2IVU$>x*>kMxMVX(8HARY^O+!6FG`rB8pInG^s~@91XfIlbgiCvKkN| zzV^1FE7UlQqA+xyG9rpci2#1G1)Jx)cBuKm$R4EtV7Mk1wz}@-xO@w!4w!DaARsqKh*`#&ADG+hCSqu zkaJ9~z9`&ILyyYeQ+YD8G(jE2C2*8}+4qu&Ksi8uL=qm&2{yKn&4Oc&&8PNmP-n8P z(^^m)!x?#&f(g8;Ue-Fgy0v=mI9_%=-KS;4nuS7hL8P|4bv2b=NFOsBz%1#$;w4ym zyR3owvtDbdkqidl7oHdhyrygrC~ho--rOGY)S*7;wyI=#5prEvh8MD)-<2lO9+XOp z;VKiS^h-FA_VN`+{HvRCzt;sdZIf=_1i0RUj7QrCc4i49)!LAT0Nl%SPbio-*;;Bx zA!r55q82<*5ZBIh3VfDa#vE)fb9Ih^8`eQ^vWD;-9(Ib|Bstg|*;fb8sl?ehXJI6g z9 zib8qSYv}pZYO)UtQw0WIF`;j;!)?$Vuj4c8x?^zhWYc z#Bj(jEXw2vi{oouijknA6hQJcR5^-w?eZi`wfe zhtASXU|NPxJ{VvEn05Y%l#??}*}1!GoWHvBqaBOHs25jrJ!IN@DK2ZpM;Z<*^vXm-hi>;-eHvq9`3;b z_$bdXPyeKG15V&MZZr zyv`L(kpAMMO3rb3cKb9N+Y@0v0iH+I3Oq?BBLR16fS*^-w1ZNc84kubTug7GOQP7( z=IcwvU3l<@NX!y5?EOY+51+~R^spviT(I#8($;RhaUs6BdfV?pSDNIh>XxqK{KPHRJSf-v~g@h=J+?p!6S&CU$%! z*7WAbc$Kmtkwz`ElvJMXVu7Fl>oX}bwg;$WK$P(MISMKq8)GlDWoV_o{Bt zJ=MJx7oCcV)?brsj478=nHv6zvr>0e*Go6AY|AOk@v~F)5$%#_M`d(E;4rK-tTe!Z zZV|27%KPPY+%Ky`zhx|IO+YeXwgFWWRUy?&K5r9DuT9PMc2iPqR-ilwmkgIC1}c+Vi-5t-U&Iha>^T4fR5D-)+v# zJ<*-qoy&dkaByX@{VtkeR~roz69?4Q1!~XegmjigEfaLcfe96O1$eKQe|L1X4z>=W z2GN3h(SlIHY^XsbU^cWMTyQ>W&;xKjS`a4q4Qdbt_zhYR0eBNNh#rg$Aa5gfltDh1 zIzlMzX5%JJgf7jC9q@TT-X&YXT*K*^u)n83hyMOI^(Ds#e*r>_#WWOViCs2V1hx*)jJkg1iMdi|D zCL-N(FsT+Y8f(F!eN$mMAEKgPvo~FBwFr}a9+f8Ix;_`Ycw6VPHWR7ucH3~VG#Y2c zWAxqQVtFi~&Fhe!-?lTK?bW6>2rDhbE@r_>N;{0>t7xa_%HZ>v Date: Tue, 25 Jun 2024 20:38:28 +0530 Subject: [PATCH 1948/2884] qtest: bios-tables-test: Rename aarch64 tests with aarch64 in them Existing AARCH64 virt test functions do not have AARCH64 in their name. To add RISC-V virt related test cases, better to rename existing functions to indicate they are ARM only. Signed-off-by: Sunil V L Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-6-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 35 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index d1ff4db7a2..c4a4d1c7bf 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1570,7 +1570,7 @@ static void test_acpi_piix4_tcg_dimm_pxm(void) test_acpi_tcg_dimm_pxm(MACHINE_PC); } -static void test_acpi_virt_tcg_memhp(void) +static void test_acpi_aarch64_virt_tcg_memhp(void) { test_data data = { .machine = "virt", @@ -1663,7 +1663,7 @@ static void test_acpi_microvm_ioapic2_tcg(void) free_test_data(&data); } -static void test_acpi_virt_tcg_numamem(void) +static void test_acpi_aarch64_virt_tcg_numamem(void) { test_data data = { .machine = "virt", @@ -1685,7 +1685,7 @@ static void test_acpi_virt_tcg_numamem(void) } -static void test_acpi_virt_tcg_pxb(void) +static void test_acpi_aarch64_virt_tcg_pxb(void) { test_data data = { .machine = "virt", @@ -1758,7 +1758,7 @@ static void test_acpi_piix4_tcg_acpi_hmat(void) test_acpi_tcg_acpi_hmat(MACHINE_PC); } -static void test_acpi_virt_tcg_acpi_hmat(void) +static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) { test_data data = { .machine = "virt", @@ -1914,7 +1914,7 @@ static void test_acpi_microvm_acpi_erst(void) } #endif /* CONFIG_POSIX */ -static void test_acpi_virt_tcg(void) +static void test_acpi_aarch64_virt_tcg(void) { test_data data = { .machine = "virt", @@ -1933,7 +1933,7 @@ static void test_acpi_virt_tcg(void) free_test_data(&data); } -static void test_acpi_virt_tcg_topology(void) +static void test_acpi_aarch64_virt_tcg_topology(void) { test_data data = { .machine = "virt", @@ -2016,7 +2016,7 @@ static void test_acpi_q35_cxl(void) } #endif /* CONFIG_POSIX */ -static void test_acpi_virt_viot(void) +static void test_acpi_aarch64_virt_viot(void) { test_data data = { .machine = "virt", @@ -2192,7 +2192,7 @@ static void test_acpi_microvm_oem_fields(void) g_free(args); } -static void test_acpi_virt_oem_fields(void) +static void test_acpi_aarch64_virt_oem_fields(void) { test_data data = { .machine = "virt", @@ -2364,16 +2364,19 @@ int main(int argc, char *argv[]) } } else if (strcmp(arch, "aarch64") == 0) { if (has_tcg && qtest_has_device("virtio-blk-pci")) { - qtest_add_func("acpi/virt", test_acpi_virt_tcg); + qtest_add_func("acpi/virt", test_acpi_aarch64_virt_tcg); qtest_add_func("acpi/virt/acpihmatvirt", - test_acpi_virt_tcg_acpi_hmat); - qtest_add_func("acpi/virt/topology", test_acpi_virt_tcg_topology); - qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); - qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); - qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); - qtest_add_func("acpi/virt/oem-fields", test_acpi_virt_oem_fields); + test_acpi_aarch64_virt_tcg_acpi_hmat); + qtest_add_func("acpi/virt/topology", + test_acpi_aarch64_virt_tcg_topology); + qtest_add_func("acpi/virt/numamem", + test_acpi_aarch64_virt_tcg_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp); + qtest_add_func("acpi/virt/pxb", test_acpi_aarch64_virt_tcg_pxb); + qtest_add_func("acpi/virt/oem-fields", + test_acpi_aarch64_virt_oem_fields); if (qtest_has_device("virtio-iommu-pci")) { - qtest_add_func("acpi/virt/viot", test_acpi_virt_viot); + qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); } } } From c9ad3decca13cd0f01ef16dba7a3d44abb096964 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:29 +0530 Subject: [PATCH 1949/2884] tests/qtest/bios-tables-test.c: Add support for arch in path Since machine name can be common for multiple architectures (ex: virt), add "arch" in the path to search for expected AML files. Since the AML files are still under old path, add support for searching with and without arch in the path. Signed-off-by: Sunil V L Acked-by: Alistair Francis Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-7-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index c4a4d1c7bf..29c52952f4 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -78,6 +78,7 @@ typedef struct { bool tcg_only; const char *machine; + const char *arch; const char *machine_param; const char *variant; const char *uefi_fl1; @@ -262,8 +263,19 @@ static void dump_aml_files(test_data *data, bool rebuild) g_assert(exp_sdt->aml); if (rebuild) { - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, + aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, + data->arch, data->machine, sdt->aml, ext); + + /* + * To keep test cases not failing before the DATA files are moved to + * ${arch}/${machine} folder, add this check as well. + */ + if (!g_file_test(aml_file, G_FILE_TEST_EXISTS)) { + aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, + data->machine, sdt->aml, ext); + } + if (!g_file_test(aml_file, G_FILE_TEST_EXISTS) && sdt->aml_len == exp_sdt->aml_len && !memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) { @@ -398,8 +410,13 @@ static GArray *load_expected_aml(test_data *data) memset(&exp_sdt, 0, sizeof(exp_sdt)); try_again: - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - sdt->aml, ext); + aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch, + data->machine, sdt->aml, ext); + if (!g_file_test(aml_file, G_FILE_TEST_EXISTS)) { + aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, + sdt->aml, ext); + } + if (verbosity_level >= 2) { fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } From 193e4b90d60a3a976ee7940d6e318ebab4db00e9 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:30 +0530 Subject: [PATCH 1950/2884] tests/qtest/bios-tables-test.c: Set "arch" for aarch64 tests To search for expected AML files under ${arch}/${machine} path, set this field for AARCH64 related test cases. Signed-off-by: Sunil V L Acked-by: Alistair Francis Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-8-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 29c52952f4..007c281c9a 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1591,6 +1591,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1684,6 +1685,7 @@ static void test_acpi_aarch64_virt_tcg_numamem(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1706,6 +1708,7 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1779,6 +1782,7 @@ static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1935,6 +1939,7 @@ static void test_acpi_aarch64_virt_tcg(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1954,6 +1959,7 @@ static void test_acpi_aarch64_virt_tcg_topology(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .variant = ".topology", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", @@ -2037,6 +2043,7 @@ static void test_acpi_aarch64_virt_viot(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -2213,6 +2220,7 @@ static void test_acpi_aarch64_virt_oem_fields(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", From d488c66b13f6070996f493f94400397ff835ed05 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:31 +0530 Subject: [PATCH 1951/2884] tests/qtest/bios-tables-test.c: Set "arch" for x86 tests To search for expected AML files under ${arch}/${machine} path, set this field for X86 related test cases. Signed-off-by: Sunil V L Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-9-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 77 ++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 007c281c9a..f4c4704bab 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -933,6 +933,7 @@ static void test_acpi_piix4_tcg(void) * This is to make guest actually run. */ data.machine = MACHINE_PC; + data.arch = "x86"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one(NULL, &data); @@ -944,6 +945,7 @@ static void test_acpi_piix4_tcg_bridge(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".bridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -981,6 +983,7 @@ static void test_acpi_piix4_no_root_hotplug(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".roothp"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -997,6 +1000,7 @@ static void test_acpi_piix4_no_bridge_hotplug(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".hpbridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -1013,6 +1017,7 @@ static void test_acpi_piix4_no_acpi_pci_hotplug(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".hpbrroot"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -1034,6 +1039,7 @@ static void test_acpi_q35_tcg(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one(NULL, &data); @@ -1049,6 +1055,7 @@ static void test_acpi_q35_kvm_type4_count(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".type4-count", .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), @@ -1065,6 +1072,7 @@ static void test_acpi_q35_kvm_core_count(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".core-count", .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), @@ -1082,6 +1090,7 @@ static void test_acpi_q35_kvm_core_count2(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".core-count2", .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), @@ -1099,6 +1108,7 @@ static void test_acpi_q35_kvm_thread_count(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".thread-count", .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), @@ -1116,6 +1126,7 @@ static void test_acpi_q35_kvm_thread_count2(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".thread-count2", .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), @@ -1134,6 +1145,7 @@ static void test_acpi_q35_tcg_bridge(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".bridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -1148,6 +1160,7 @@ static void test_acpi_q35_tcg_no_acpi_hotplug(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".noacpihp"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -1176,6 +1189,7 @@ static void test_acpi_q35_multif_bridge(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".multi-bridge", }; test_vm_prepare("-S" @@ -1225,6 +1239,7 @@ static void test_acpi_q35_tcg_mmio64(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".mmio64", .tcg_only = true, .required_struct_types = base_required_struct_types, @@ -1245,6 +1260,7 @@ static void test_acpi_piix4_tcg_cphp(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".cphp"; test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6" " -object memory-backend-ram,id=ram0,size=64M" @@ -1260,6 +1276,7 @@ static void test_acpi_q35_tcg_cphp(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".cphp"; test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6" " -object memory-backend-ram,id=ram0,size=64M" @@ -1279,6 +1296,7 @@ static void test_acpi_q35_tcg_ipmi(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".ipmibt"; data.required_struct_types = ipmi_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); @@ -1293,6 +1311,7 @@ static void test_acpi_q35_tcg_smbus_ipmi(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".ipmismbus"; data.required_struct_types = ipmi_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); @@ -1310,6 +1329,7 @@ static void test_acpi_piix4_tcg_ipmi(void) * This is to make guest actually run. */ data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".ipmikcs"; data.required_struct_types = ipmi_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); @@ -1324,6 +1344,7 @@ static void test_acpi_q35_tcg_memhp(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".memhp"; test_acpi_one(" -m 128,slots=3,maxmem=1G" " -object memory-backend-ram,id=ram0,size=64M" @@ -1339,6 +1360,7 @@ static void test_acpi_piix4_tcg_memhp(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".memhp"; test_acpi_one(" -m 128,slots=3,maxmem=1G" " -object memory-backend-ram,id=ram0,size=64M" @@ -1354,6 +1376,7 @@ static void test_acpi_piix4_tcg_nosmm(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); free_test_data(&data); @@ -1364,6 +1387,7 @@ static void test_acpi_piix4_tcg_smm_compat(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".smm-compat"; test_acpi_one("-global PIIX4_PM.smm-compat=on", &data); free_test_data(&data); @@ -1374,6 +1398,7 @@ static void test_acpi_piix4_tcg_smm_compat_nosmm(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".smm-compat-nosmm"; test_acpi_one("-global PIIX4_PM.smm-compat=on -machine smm=off", &data); free_test_data(&data); @@ -1384,6 +1409,7 @@ static void test_acpi_piix4_tcg_nohpet(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.machine_param = ",hpet=off"; data.variant = ".nohpet"; test_acpi_one(NULL, &data); @@ -1395,6 +1421,7 @@ static void test_acpi_q35_tcg_numamem(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".numamem"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" " -numa node -numa node,memdev=ram0", &data); @@ -1406,6 +1433,7 @@ static void test_acpi_q35_kvm_xapic(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".xapic"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" " -numa node -numa node,memdev=ram0" @@ -1418,6 +1446,7 @@ static void test_acpi_q35_tcg_nosmm(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); free_test_data(&data); @@ -1428,6 +1457,7 @@ static void test_acpi_q35_tcg_smm_compat(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".smm-compat"; test_acpi_one("-global ICH9-LPC.smm-compat=on", &data); free_test_data(&data); @@ -1438,6 +1468,7 @@ static void test_acpi_q35_tcg_smm_compat_nosmm(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".smm-compat-nosmm"; test_acpi_one("-global ICH9-LPC.smm-compat=on -machine smm=off", &data); free_test_data(&data); @@ -1448,6 +1479,7 @@ static void test_acpi_q35_tcg_nohpet(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.machine_param = ",hpet=off"; data.variant = ".nohpet"; test_acpi_one(NULL, &data); @@ -1459,6 +1491,7 @@ static void test_acpi_q35_kvm_dmar(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".dmar"; test_acpi_one("-machine kernel-irqchip=split -accel kvm" " -device intel-iommu,intremap=on,device-iotlb=on", &data); @@ -1470,6 +1503,7 @@ static void test_acpi_q35_tcg_ivrs(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".ivrs"; data.tcg_only = true, test_acpi_one(" -device amd-iommu", &data); @@ -1481,6 +1515,7 @@ static void test_acpi_piix4_tcg_numamem(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".numamem"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" " -numa node -numa node,memdev=ram0", &data); @@ -1489,8 +1524,9 @@ static void test_acpi_piix4_tcg_numamem(void) uint64_t tpm_tis_base_addr; -static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, - uint64_t base, enum TPMVersion tpm_version) +static void test_acpi_tcg_tpm(const char *machine, const char *arch, + const char *tpm_if, uint64_t base, + enum TPMVersion tpm_version) { gchar *tmp_dir_name = g_strdup_printf("qemu-test_acpi_%s_tcg_%s.XXXXXX", machine, tpm_if); @@ -1517,6 +1553,7 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, tpm_emu_test_wait_cond(&test); data.machine = machine; + data.arch = arch; data.variant = variant; args = g_strdup_printf( @@ -1540,19 +1577,20 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, static void test_acpi_q35_tcg_tpm2_tis(void) { - test_acpi_tcg_tpm("q35", "tis", 0xFED40000, TPM_VERSION_2_0); + test_acpi_tcg_tpm("q35", "x86", "tis", 0xFED40000, TPM_VERSION_2_0); } static void test_acpi_q35_tcg_tpm12_tis(void) { - test_acpi_tcg_tpm("q35", "tis", 0xFED40000, TPM_VERSION_1_2); + test_acpi_tcg_tpm("q35", "x86", "tis", 0xFED40000, TPM_VERSION_1_2); } -static void test_acpi_tcg_dimm_pxm(const char *machine) +static void test_acpi_tcg_dimm_pxm(const char *machine, const char *arch) { test_data data = {}; data.machine = machine; + data.arch = arch; data.variant = ".dimmpxm"; test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" " -smp 4,sockets=4" @@ -1579,12 +1617,12 @@ static void test_acpi_tcg_dimm_pxm(const char *machine) static void test_acpi_q35_tcg_dimm_pxm(void) { - test_acpi_tcg_dimm_pxm(MACHINE_Q35); + test_acpi_tcg_dimm_pxm(MACHINE_Q35, "x86"); } static void test_acpi_piix4_tcg_dimm_pxm(void) { - test_acpi_tcg_dimm_pxm(MACHINE_PC); + test_acpi_tcg_dimm_pxm(MACHINE_PC, "x86"); } static void test_acpi_aarch64_virt_tcg_memhp(void) @@ -1621,6 +1659,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) static void test_acpi_microvm_prepare(test_data *data) { data->machine = "microvm"; + data->arch = "x86"; data->required_struct_types = NULL; /* no smbios */ data->required_struct_types_len = 0; data->blkdev = "virtio-blk-device"; @@ -1737,11 +1776,12 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) free_test_data(&data); } -static void test_acpi_tcg_acpi_hmat(const char *machine) +static void test_acpi_tcg_acpi_hmat(const char *machine, const char *arch) { test_data data = {}; data.machine = machine; + data.arch = arch; data.variant = ".acpihmat"; test_acpi_one(" -machine hmat=on" " -smp 2,sockets=2" @@ -1770,12 +1810,12 @@ static void test_acpi_tcg_acpi_hmat(const char *machine) static void test_acpi_q35_tcg_acpi_hmat(void) { - test_acpi_tcg_acpi_hmat(MACHINE_Q35); + test_acpi_tcg_acpi_hmat(MACHINE_Q35, "x86"); } static void test_acpi_piix4_tcg_acpi_hmat(void) { - test_acpi_tcg_acpi_hmat(MACHINE_PC); + test_acpi_tcg_acpi_hmat(MACHINE_PC, "x86"); } static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) @@ -1841,6 +1881,7 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86"; data.variant = ".acpihmat-noinitiator"; test_acpi_one(" -machine hmat=on" " -smp 4,sockets=2" @@ -1884,13 +1925,14 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) } #ifdef CONFIG_POSIX -static void test_acpi_erst(const char *machine) +static void test_acpi_erst(const char *machine, const char *arch) { gchar *tmp_path = g_dir_make_tmp("qemu-test-erst.XXXXXX", NULL); gchar *params; test_data data = {}; data.machine = machine; + data.arch = arch; data.variant = ".acpierst"; params = g_strdup_printf( " -object memory-backend-file,id=erstnvram," @@ -1905,12 +1947,12 @@ static void test_acpi_erst(const char *machine) static void test_acpi_piix4_acpi_erst(void) { - test_acpi_erst(MACHINE_PC); + test_acpi_erst(MACHINE_PC, "x86"); } static void test_acpi_q35_acpi_erst(void) { - test_acpi_erst(MACHINE_Q35); + test_acpi_erst(MACHINE_Q35, "x86"); } static void test_acpi_microvm_acpi_erst(void) @@ -1978,6 +2020,7 @@ static void test_acpi_q35_viot(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".viot", }; @@ -2002,6 +2045,7 @@ static void test_acpi_q35_cxl(void) test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".cxl", }; /* @@ -2067,6 +2111,7 @@ static void test_acpi_q35_slic(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".slic", }; @@ -2081,6 +2126,7 @@ static void test_acpi_q35_applesmc(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".applesmc", }; @@ -2094,6 +2140,7 @@ static void test_acpi_q35_pvpanic_isa(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".pvpanic-isa", }; @@ -2106,6 +2153,7 @@ static void test_acpi_pc_smbios_options(void) uint8_t req_type11[] = { 11 }; test_data data = { .machine = MACHINE_PC, + .arch = "x86", .variant = ".pc_smbios_options", .required_struct_types = req_type11, .required_struct_types_len = ARRAY_SIZE(req_type11), @@ -2120,6 +2168,7 @@ static void test_acpi_pc_smbios_blob(void) uint8_t req_type11[] = { 11 }; test_data data = { .machine = MACHINE_PC, + .arch = "x86", .variant = ".pc_smbios_blob", .required_struct_types = req_type11, .required_struct_types_len = ARRAY_SIZE(req_type11), @@ -2169,6 +2218,7 @@ static void test_acpi_piix4_oem_fields(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -2187,6 +2237,7 @@ static void test_acpi_q35_oem_fields(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); From 7c08eefcaf6588b80bd8ce027fab748db3c53f11 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:32 +0530 Subject: [PATCH 1952/2884] tests/data/acpi: Move x86 ACPI tables under x86/${machine} path To support multiple architectures using same machine name, create x86 folder and move all x86 related AML files for each machine type inside. Signed-off-by: Sunil V L Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-10-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/{ => x86}/microvm/APIC | Bin tests/data/acpi/{ => x86}/microvm/APIC.ioapic2 | Bin tests/data/acpi/{ => x86}/microvm/APIC.pcie | Bin tests/data/acpi/{ => x86}/microvm/DSDT | Bin tests/data/acpi/{ => x86}/microvm/DSDT.ioapic2 | Bin tests/data/acpi/{ => x86}/microvm/DSDT.pcie | Bin tests/data/acpi/{ => x86}/microvm/DSDT.rtc | Bin tests/data/acpi/{ => x86}/microvm/DSDT.usb | Bin tests/data/acpi/{ => x86}/microvm/ERST.pcie | Bin tests/data/acpi/{ => x86}/microvm/FACP | Bin tests/data/acpi/{ => x86}/pc/APIC | Bin tests/data/acpi/{ => x86}/pc/APIC.acpihmat | Bin tests/data/acpi/{ => x86}/pc/APIC.cphp | Bin tests/data/acpi/{ => x86}/pc/APIC.dimmpxm | Bin tests/data/acpi/{ => x86}/pc/DSDT | Bin tests/data/acpi/{ => x86}/pc/DSDT.acpierst | Bin tests/data/acpi/{ => x86}/pc/DSDT.acpihmat | Bin tests/data/acpi/{ => x86}/pc/DSDT.bridge | Bin tests/data/acpi/{ => x86}/pc/DSDT.cphp | Bin tests/data/acpi/{ => x86}/pc/DSDT.dimmpxm | Bin tests/data/acpi/{ => x86}/pc/DSDT.hpbridge | Bin tests/data/acpi/{ => x86}/pc/DSDT.hpbrroot | Bin tests/data/acpi/{ => x86}/pc/DSDT.ipmikcs | Bin tests/data/acpi/{ => x86}/pc/DSDT.memhp | Bin tests/data/acpi/{ => x86}/pc/DSDT.nohpet | Bin tests/data/acpi/{ => x86}/pc/DSDT.numamem | Bin tests/data/acpi/{ => x86}/pc/DSDT.roothp | Bin tests/data/acpi/{ => x86}/pc/ERST.acpierst | Bin tests/data/acpi/{ => x86}/pc/FACP | Bin tests/data/acpi/{ => x86}/pc/FACP.nosmm | Bin tests/data/acpi/{ => x86}/pc/FACS | Bin tests/data/acpi/{ => x86}/pc/HMAT.acpihmat | Bin tests/data/acpi/{ => x86}/pc/HPET | Bin tests/data/acpi/{ => x86}/pc/NFIT.dimmpxm | Bin tests/data/acpi/{ => x86}/pc/SLIT.cphp | Bin tests/data/acpi/{ => x86}/pc/SLIT.memhp | Bin tests/data/acpi/{ => x86}/pc/SRAT.acpihmat | Bin tests/data/acpi/{ => x86}/pc/SRAT.cphp | Bin tests/data/acpi/{ => x86}/pc/SRAT.dimmpxm | Bin tests/data/acpi/{ => x86}/pc/SRAT.memhp | Bin tests/data/acpi/{ => x86}/pc/SRAT.numamem | Bin tests/data/acpi/{ => x86}/pc/SSDT.dimmpxm | Bin tests/data/acpi/{ => x86}/pc/WAET | Bin tests/data/acpi/{ => x86}/q35/APIC | Bin tests/data/acpi/{ => x86}/q35/APIC.acpihmat | Bin .../acpi/{ => x86}/q35/APIC.acpihmat-noinitiator | Bin tests/data/acpi/{ => x86}/q35/APIC.core-count | Bin tests/data/acpi/{ => x86}/q35/APIC.core-count2 | Bin tests/data/acpi/{ => x86}/q35/APIC.cphp | Bin tests/data/acpi/{ => x86}/q35/APIC.dimmpxm | Bin tests/data/acpi/{ => x86}/q35/APIC.thread-count | Bin tests/data/acpi/{ => x86}/q35/APIC.thread-count2 | Bin tests/data/acpi/{ => x86}/q35/APIC.type4-count | Bin tests/data/acpi/{ => x86}/q35/APIC.xapic | Bin tests/data/acpi/{ => x86}/q35/CEDT.cxl | Bin tests/data/acpi/{ => x86}/q35/DMAR.dmar | Bin tests/data/acpi/{ => x86}/q35/DSDT | Bin tests/data/acpi/{ => x86}/q35/DSDT.acpierst | Bin tests/data/acpi/{ => x86}/q35/DSDT.acpihmat | Bin .../acpi/{ => x86}/q35/DSDT.acpihmat-noinitiator | Bin tests/data/acpi/{ => x86}/q35/DSDT.applesmc | Bin tests/data/acpi/{ => x86}/q35/DSDT.bridge | Bin tests/data/acpi/{ => x86}/q35/DSDT.core-count | Bin tests/data/acpi/{ => x86}/q35/DSDT.core-count2 | Bin tests/data/acpi/{ => x86}/q35/DSDT.cphp | Bin tests/data/acpi/{ => x86}/q35/DSDT.cxl | Bin tests/data/acpi/{ => x86}/q35/DSDT.dimmpxm | Bin tests/data/acpi/{ => x86}/q35/DSDT.ipmibt | Bin tests/data/acpi/{ => x86}/q35/DSDT.ipmismbus | Bin tests/data/acpi/{ => x86}/q35/DSDT.ivrs | Bin tests/data/acpi/{ => x86}/q35/DSDT.memhp | Bin tests/data/acpi/{ => x86}/q35/DSDT.mmio64 | Bin tests/data/acpi/{ => x86}/q35/DSDT.multi-bridge | Bin tests/data/acpi/{ => x86}/q35/DSDT.noacpihp | Bin tests/data/acpi/{ => x86}/q35/DSDT.nohpet | Bin tests/data/acpi/{ => x86}/q35/DSDT.numamem | Bin tests/data/acpi/{ => x86}/q35/DSDT.pvpanic-isa | Bin tests/data/acpi/{ => x86}/q35/DSDT.thread-count | Bin tests/data/acpi/{ => x86}/q35/DSDT.thread-count2 | Bin tests/data/acpi/{ => x86}/q35/DSDT.tis.tpm12 | Bin tests/data/acpi/{ => x86}/q35/DSDT.tis.tpm2 | Bin tests/data/acpi/{ => x86}/q35/DSDT.type4-count | Bin tests/data/acpi/{ => x86}/q35/DSDT.viot | Bin tests/data/acpi/{ => x86}/q35/DSDT.xapic | Bin tests/data/acpi/{ => x86}/q35/ERST.acpierst | Bin tests/data/acpi/{ => x86}/q35/FACP | Bin tests/data/acpi/{ => x86}/q35/FACP.core-count | Bin tests/data/acpi/{ => x86}/q35/FACP.core-count2 | Bin tests/data/acpi/{ => x86}/q35/FACP.nosmm | Bin tests/data/acpi/{ => x86}/q35/FACP.slic | Bin tests/data/acpi/{ => x86}/q35/FACP.thread-count | Bin tests/data/acpi/{ => x86}/q35/FACP.thread-count2 | Bin tests/data/acpi/{ => x86}/q35/FACP.type4-count | Bin tests/data/acpi/{ => x86}/q35/FACP.xapic | Bin tests/data/acpi/{ => x86}/q35/FACS | Bin tests/data/acpi/{ => x86}/q35/HMAT.acpihmat | Bin .../acpi/{ => x86}/q35/HMAT.acpihmat-noinitiator | Bin tests/data/acpi/{ => x86}/q35/HPET | Bin tests/data/acpi/{ => x86}/q35/IVRS.ivrs | Bin tests/data/acpi/{ => x86}/q35/MCFG | Bin tests/data/acpi/{ => x86}/q35/NFIT.dimmpxm | Bin tests/data/acpi/{ => x86}/q35/SLIC.slic | Bin tests/data/acpi/{ => x86}/q35/SLIT.cphp | Bin tests/data/acpi/{ => x86}/q35/SLIT.memhp | Bin tests/data/acpi/{ => x86}/q35/SRAT.acpihmat | Bin .../acpi/{ => x86}/q35/SRAT.acpihmat-noinitiator | Bin tests/data/acpi/{ => x86}/q35/SRAT.cphp | Bin tests/data/acpi/{ => x86}/q35/SRAT.dimmpxm | Bin tests/data/acpi/{ => x86}/q35/SRAT.memhp | Bin tests/data/acpi/{ => x86}/q35/SRAT.mmio64 | Bin tests/data/acpi/{ => x86}/q35/SRAT.numamem | Bin tests/data/acpi/{ => x86}/q35/SRAT.xapic | Bin tests/data/acpi/{ => x86}/q35/SSDT.dimmpxm | Bin tests/data/acpi/{ => x86}/q35/TCPA.tis.tpm12 | Bin tests/data/acpi/{ => x86}/q35/TPM2.tis.tpm2 | Bin tests/data/acpi/{ => x86}/q35/VIOT.viot | Bin tests/data/acpi/{ => x86}/q35/WAET | Bin 117 files changed, 0 insertions(+), 0 deletions(-) rename tests/data/acpi/{ => x86}/microvm/APIC (100%) rename tests/data/acpi/{ => x86}/microvm/APIC.ioapic2 (100%) rename tests/data/acpi/{ => x86}/microvm/APIC.pcie (100%) rename tests/data/acpi/{ => x86}/microvm/DSDT (100%) rename tests/data/acpi/{ => x86}/microvm/DSDT.ioapic2 (100%) rename tests/data/acpi/{ => x86}/microvm/DSDT.pcie (100%) rename tests/data/acpi/{ => x86}/microvm/DSDT.rtc (100%) rename tests/data/acpi/{ => x86}/microvm/DSDT.usb (100%) rename tests/data/acpi/{ => x86}/microvm/ERST.pcie (100%) rename tests/data/acpi/{ => x86}/microvm/FACP (100%) rename tests/data/acpi/{ => x86}/pc/APIC (100%) rename tests/data/acpi/{ => x86}/pc/APIC.acpihmat (100%) rename tests/data/acpi/{ => x86}/pc/APIC.cphp (100%) rename tests/data/acpi/{ => x86}/pc/APIC.dimmpxm (100%) rename tests/data/acpi/{ => x86}/pc/DSDT (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.acpierst (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.acpihmat (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.bridge (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.cphp (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.hpbridge (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.hpbrroot (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.ipmikcs (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.memhp (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.nohpet (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.numamem (100%) rename tests/data/acpi/{ => x86}/pc/DSDT.roothp (100%) rename tests/data/acpi/{ => x86}/pc/ERST.acpierst (100%) rename tests/data/acpi/{ => x86}/pc/FACP (100%) rename tests/data/acpi/{ => x86}/pc/FACP.nosmm (100%) rename tests/data/acpi/{ => x86}/pc/FACS (100%) rename tests/data/acpi/{ => x86}/pc/HMAT.acpihmat (100%) rename tests/data/acpi/{ => x86}/pc/HPET (100%) rename tests/data/acpi/{ => x86}/pc/NFIT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/pc/SLIT.cphp (100%) rename tests/data/acpi/{ => x86}/pc/SLIT.memhp (100%) rename tests/data/acpi/{ => x86}/pc/SRAT.acpihmat (100%) rename tests/data/acpi/{ => x86}/pc/SRAT.cphp (100%) rename tests/data/acpi/{ => x86}/pc/SRAT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/pc/SRAT.memhp (100%) rename tests/data/acpi/{ => x86}/pc/SRAT.numamem (100%) rename tests/data/acpi/{ => x86}/pc/SSDT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/pc/WAET (100%) rename tests/data/acpi/{ => x86}/q35/APIC (100%) rename tests/data/acpi/{ => x86}/q35/APIC.acpihmat (100%) rename tests/data/acpi/{ => x86}/q35/APIC.acpihmat-noinitiator (100%) rename tests/data/acpi/{ => x86}/q35/APIC.core-count (100%) rename tests/data/acpi/{ => x86}/q35/APIC.core-count2 (100%) rename tests/data/acpi/{ => x86}/q35/APIC.cphp (100%) rename tests/data/acpi/{ => x86}/q35/APIC.dimmpxm (100%) rename tests/data/acpi/{ => x86}/q35/APIC.thread-count (100%) rename tests/data/acpi/{ => x86}/q35/APIC.thread-count2 (100%) rename tests/data/acpi/{ => x86}/q35/APIC.type4-count (100%) rename tests/data/acpi/{ => x86}/q35/APIC.xapic (100%) rename tests/data/acpi/{ => x86}/q35/CEDT.cxl (100%) rename tests/data/acpi/{ => x86}/q35/DMAR.dmar (100%) rename tests/data/acpi/{ => x86}/q35/DSDT (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.acpierst (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.acpihmat (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.acpihmat-noinitiator (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.applesmc (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.bridge (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.core-count (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.core-count2 (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.cphp (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.cxl (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.ipmibt (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.ipmismbus (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.ivrs (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.memhp (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.mmio64 (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.multi-bridge (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.noacpihp (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.nohpet (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.numamem (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.pvpanic-isa (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.thread-count (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.thread-count2 (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.tis.tpm12 (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.tis.tpm2 (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.type4-count (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.viot (100%) rename tests/data/acpi/{ => x86}/q35/DSDT.xapic (100%) rename tests/data/acpi/{ => x86}/q35/ERST.acpierst (100%) rename tests/data/acpi/{ => x86}/q35/FACP (100%) rename tests/data/acpi/{ => x86}/q35/FACP.core-count (100%) rename tests/data/acpi/{ => x86}/q35/FACP.core-count2 (100%) rename tests/data/acpi/{ => x86}/q35/FACP.nosmm (100%) rename tests/data/acpi/{ => x86}/q35/FACP.slic (100%) rename tests/data/acpi/{ => x86}/q35/FACP.thread-count (100%) rename tests/data/acpi/{ => x86}/q35/FACP.thread-count2 (100%) rename tests/data/acpi/{ => x86}/q35/FACP.type4-count (100%) rename tests/data/acpi/{ => x86}/q35/FACP.xapic (100%) rename tests/data/acpi/{ => x86}/q35/FACS (100%) rename tests/data/acpi/{ => x86}/q35/HMAT.acpihmat (100%) rename tests/data/acpi/{ => x86}/q35/HMAT.acpihmat-noinitiator (100%) rename tests/data/acpi/{ => x86}/q35/HPET (100%) rename tests/data/acpi/{ => x86}/q35/IVRS.ivrs (100%) rename tests/data/acpi/{ => x86}/q35/MCFG (100%) rename tests/data/acpi/{ => x86}/q35/NFIT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/q35/SLIC.slic (100%) rename tests/data/acpi/{ => x86}/q35/SLIT.cphp (100%) rename tests/data/acpi/{ => x86}/q35/SLIT.memhp (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.acpihmat (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.acpihmat-noinitiator (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.cphp (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.memhp (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.mmio64 (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.numamem (100%) rename tests/data/acpi/{ => x86}/q35/SRAT.xapic (100%) rename tests/data/acpi/{ => x86}/q35/SSDT.dimmpxm (100%) rename tests/data/acpi/{ => x86}/q35/TCPA.tis.tpm12 (100%) rename tests/data/acpi/{ => x86}/q35/TPM2.tis.tpm2 (100%) rename tests/data/acpi/{ => x86}/q35/VIOT.viot (100%) rename tests/data/acpi/{ => x86}/q35/WAET (100%) diff --git a/tests/data/acpi/microvm/APIC b/tests/data/acpi/x86/microvm/APIC similarity index 100% rename from tests/data/acpi/microvm/APIC rename to tests/data/acpi/x86/microvm/APIC diff --git a/tests/data/acpi/microvm/APIC.ioapic2 b/tests/data/acpi/x86/microvm/APIC.ioapic2 similarity index 100% rename from tests/data/acpi/microvm/APIC.ioapic2 rename to tests/data/acpi/x86/microvm/APIC.ioapic2 diff --git a/tests/data/acpi/microvm/APIC.pcie b/tests/data/acpi/x86/microvm/APIC.pcie similarity index 100% rename from tests/data/acpi/microvm/APIC.pcie rename to tests/data/acpi/x86/microvm/APIC.pcie diff --git a/tests/data/acpi/microvm/DSDT b/tests/data/acpi/x86/microvm/DSDT similarity index 100% rename from tests/data/acpi/microvm/DSDT rename to tests/data/acpi/x86/microvm/DSDT diff --git a/tests/data/acpi/microvm/DSDT.ioapic2 b/tests/data/acpi/x86/microvm/DSDT.ioapic2 similarity index 100% rename from tests/data/acpi/microvm/DSDT.ioapic2 rename to tests/data/acpi/x86/microvm/DSDT.ioapic2 diff --git a/tests/data/acpi/microvm/DSDT.pcie b/tests/data/acpi/x86/microvm/DSDT.pcie similarity index 100% rename from tests/data/acpi/microvm/DSDT.pcie rename to tests/data/acpi/x86/microvm/DSDT.pcie diff --git a/tests/data/acpi/microvm/DSDT.rtc b/tests/data/acpi/x86/microvm/DSDT.rtc similarity index 100% rename from tests/data/acpi/microvm/DSDT.rtc rename to tests/data/acpi/x86/microvm/DSDT.rtc diff --git a/tests/data/acpi/microvm/DSDT.usb b/tests/data/acpi/x86/microvm/DSDT.usb similarity index 100% rename from tests/data/acpi/microvm/DSDT.usb rename to tests/data/acpi/x86/microvm/DSDT.usb diff --git a/tests/data/acpi/microvm/ERST.pcie b/tests/data/acpi/x86/microvm/ERST.pcie similarity index 100% rename from tests/data/acpi/microvm/ERST.pcie rename to tests/data/acpi/x86/microvm/ERST.pcie diff --git a/tests/data/acpi/microvm/FACP b/tests/data/acpi/x86/microvm/FACP similarity index 100% rename from tests/data/acpi/microvm/FACP rename to tests/data/acpi/x86/microvm/FACP diff --git a/tests/data/acpi/pc/APIC b/tests/data/acpi/x86/pc/APIC similarity index 100% rename from tests/data/acpi/pc/APIC rename to tests/data/acpi/x86/pc/APIC diff --git a/tests/data/acpi/pc/APIC.acpihmat b/tests/data/acpi/x86/pc/APIC.acpihmat similarity index 100% rename from tests/data/acpi/pc/APIC.acpihmat rename to tests/data/acpi/x86/pc/APIC.acpihmat diff --git a/tests/data/acpi/pc/APIC.cphp b/tests/data/acpi/x86/pc/APIC.cphp similarity index 100% rename from tests/data/acpi/pc/APIC.cphp rename to tests/data/acpi/x86/pc/APIC.cphp diff --git a/tests/data/acpi/pc/APIC.dimmpxm b/tests/data/acpi/x86/pc/APIC.dimmpxm similarity index 100% rename from tests/data/acpi/pc/APIC.dimmpxm rename to tests/data/acpi/x86/pc/APIC.dimmpxm diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/x86/pc/DSDT similarity index 100% rename from tests/data/acpi/pc/DSDT rename to tests/data/acpi/x86/pc/DSDT diff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/x86/pc/DSDT.acpierst similarity index 100% rename from tests/data/acpi/pc/DSDT.acpierst rename to tests/data/acpi/x86/pc/DSDT.acpierst diff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/x86/pc/DSDT.acpihmat similarity index 100% rename from tests/data/acpi/pc/DSDT.acpihmat rename to tests/data/acpi/x86/pc/DSDT.acpihmat diff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/x86/pc/DSDT.bridge similarity index 100% rename from tests/data/acpi/pc/DSDT.bridge rename to tests/data/acpi/x86/pc/DSDT.bridge diff --git a/tests/data/acpi/pc/DSDT.cphp b/tests/data/acpi/x86/pc/DSDT.cphp similarity index 100% rename from tests/data/acpi/pc/DSDT.cphp rename to tests/data/acpi/x86/pc/DSDT.cphp diff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/x86/pc/DSDT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/DSDT.dimmpxm rename to tests/data/acpi/x86/pc/DSDT.dimmpxm diff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/x86/pc/DSDT.hpbridge similarity index 100% rename from tests/data/acpi/pc/DSDT.hpbridge rename to tests/data/acpi/x86/pc/DSDT.hpbridge diff --git a/tests/data/acpi/pc/DSDT.hpbrroot b/tests/data/acpi/x86/pc/DSDT.hpbrroot similarity index 100% rename from tests/data/acpi/pc/DSDT.hpbrroot rename to tests/data/acpi/x86/pc/DSDT.hpbrroot diff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/x86/pc/DSDT.ipmikcs similarity index 100% rename from tests/data/acpi/pc/DSDT.ipmikcs rename to tests/data/acpi/x86/pc/DSDT.ipmikcs diff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/x86/pc/DSDT.memhp similarity index 100% rename from tests/data/acpi/pc/DSDT.memhp rename to tests/data/acpi/x86/pc/DSDT.memhp diff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/x86/pc/DSDT.nohpet similarity index 100% rename from tests/data/acpi/pc/DSDT.nohpet rename to tests/data/acpi/x86/pc/DSDT.nohpet diff --git a/tests/data/acpi/pc/DSDT.numamem b/tests/data/acpi/x86/pc/DSDT.numamem similarity index 100% rename from tests/data/acpi/pc/DSDT.numamem rename to tests/data/acpi/x86/pc/DSDT.numamem diff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/x86/pc/DSDT.roothp similarity index 100% rename from tests/data/acpi/pc/DSDT.roothp rename to tests/data/acpi/x86/pc/DSDT.roothp diff --git a/tests/data/acpi/pc/ERST.acpierst b/tests/data/acpi/x86/pc/ERST.acpierst similarity index 100% rename from tests/data/acpi/pc/ERST.acpierst rename to tests/data/acpi/x86/pc/ERST.acpierst diff --git a/tests/data/acpi/pc/FACP b/tests/data/acpi/x86/pc/FACP similarity index 100% rename from tests/data/acpi/pc/FACP rename to tests/data/acpi/x86/pc/FACP diff --git a/tests/data/acpi/pc/FACP.nosmm b/tests/data/acpi/x86/pc/FACP.nosmm similarity index 100% rename from tests/data/acpi/pc/FACP.nosmm rename to tests/data/acpi/x86/pc/FACP.nosmm diff --git a/tests/data/acpi/pc/FACS b/tests/data/acpi/x86/pc/FACS similarity index 100% rename from tests/data/acpi/pc/FACS rename to tests/data/acpi/x86/pc/FACS diff --git a/tests/data/acpi/pc/HMAT.acpihmat b/tests/data/acpi/x86/pc/HMAT.acpihmat similarity index 100% rename from tests/data/acpi/pc/HMAT.acpihmat rename to tests/data/acpi/x86/pc/HMAT.acpihmat diff --git a/tests/data/acpi/pc/HPET b/tests/data/acpi/x86/pc/HPET similarity index 100% rename from tests/data/acpi/pc/HPET rename to tests/data/acpi/x86/pc/HPET diff --git a/tests/data/acpi/pc/NFIT.dimmpxm b/tests/data/acpi/x86/pc/NFIT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/NFIT.dimmpxm rename to tests/data/acpi/x86/pc/NFIT.dimmpxm diff --git a/tests/data/acpi/pc/SLIT.cphp b/tests/data/acpi/x86/pc/SLIT.cphp similarity index 100% rename from tests/data/acpi/pc/SLIT.cphp rename to tests/data/acpi/x86/pc/SLIT.cphp diff --git a/tests/data/acpi/pc/SLIT.memhp b/tests/data/acpi/x86/pc/SLIT.memhp similarity index 100% rename from tests/data/acpi/pc/SLIT.memhp rename to tests/data/acpi/x86/pc/SLIT.memhp diff --git a/tests/data/acpi/pc/SRAT.acpihmat b/tests/data/acpi/x86/pc/SRAT.acpihmat similarity index 100% rename from tests/data/acpi/pc/SRAT.acpihmat rename to tests/data/acpi/x86/pc/SRAT.acpihmat diff --git a/tests/data/acpi/pc/SRAT.cphp b/tests/data/acpi/x86/pc/SRAT.cphp similarity index 100% rename from tests/data/acpi/pc/SRAT.cphp rename to tests/data/acpi/x86/pc/SRAT.cphp diff --git a/tests/data/acpi/pc/SRAT.dimmpxm b/tests/data/acpi/x86/pc/SRAT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/SRAT.dimmpxm rename to tests/data/acpi/x86/pc/SRAT.dimmpxm diff --git a/tests/data/acpi/pc/SRAT.memhp b/tests/data/acpi/x86/pc/SRAT.memhp similarity index 100% rename from tests/data/acpi/pc/SRAT.memhp rename to tests/data/acpi/x86/pc/SRAT.memhp diff --git a/tests/data/acpi/pc/SRAT.numamem b/tests/data/acpi/x86/pc/SRAT.numamem similarity index 100% rename from tests/data/acpi/pc/SRAT.numamem rename to tests/data/acpi/x86/pc/SRAT.numamem diff --git a/tests/data/acpi/pc/SSDT.dimmpxm b/tests/data/acpi/x86/pc/SSDT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/SSDT.dimmpxm rename to tests/data/acpi/x86/pc/SSDT.dimmpxm diff --git a/tests/data/acpi/pc/WAET b/tests/data/acpi/x86/pc/WAET similarity index 100% rename from tests/data/acpi/pc/WAET rename to tests/data/acpi/x86/pc/WAET diff --git a/tests/data/acpi/q35/APIC b/tests/data/acpi/x86/q35/APIC similarity index 100% rename from tests/data/acpi/q35/APIC rename to tests/data/acpi/x86/q35/APIC diff --git a/tests/data/acpi/q35/APIC.acpihmat b/tests/data/acpi/x86/q35/APIC.acpihmat similarity index 100% rename from tests/data/acpi/q35/APIC.acpihmat rename to tests/data/acpi/x86/q35/APIC.acpihmat diff --git a/tests/data/acpi/q35/APIC.acpihmat-noinitiator b/tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/APIC.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator diff --git a/tests/data/acpi/q35/APIC.core-count b/tests/data/acpi/x86/q35/APIC.core-count similarity index 100% rename from tests/data/acpi/q35/APIC.core-count rename to tests/data/acpi/x86/q35/APIC.core-count diff --git a/tests/data/acpi/q35/APIC.core-count2 b/tests/data/acpi/x86/q35/APIC.core-count2 similarity index 100% rename from tests/data/acpi/q35/APIC.core-count2 rename to tests/data/acpi/x86/q35/APIC.core-count2 diff --git a/tests/data/acpi/q35/APIC.cphp b/tests/data/acpi/x86/q35/APIC.cphp similarity index 100% rename from tests/data/acpi/q35/APIC.cphp rename to tests/data/acpi/x86/q35/APIC.cphp diff --git a/tests/data/acpi/q35/APIC.dimmpxm b/tests/data/acpi/x86/q35/APIC.dimmpxm similarity index 100% rename from tests/data/acpi/q35/APIC.dimmpxm rename to tests/data/acpi/x86/q35/APIC.dimmpxm diff --git a/tests/data/acpi/q35/APIC.thread-count b/tests/data/acpi/x86/q35/APIC.thread-count similarity index 100% rename from tests/data/acpi/q35/APIC.thread-count rename to tests/data/acpi/x86/q35/APIC.thread-count diff --git a/tests/data/acpi/q35/APIC.thread-count2 b/tests/data/acpi/x86/q35/APIC.thread-count2 similarity index 100% rename from tests/data/acpi/q35/APIC.thread-count2 rename to tests/data/acpi/x86/q35/APIC.thread-count2 diff --git a/tests/data/acpi/q35/APIC.type4-count b/tests/data/acpi/x86/q35/APIC.type4-count similarity index 100% rename from tests/data/acpi/q35/APIC.type4-count rename to tests/data/acpi/x86/q35/APIC.type4-count diff --git a/tests/data/acpi/q35/APIC.xapic b/tests/data/acpi/x86/q35/APIC.xapic similarity index 100% rename from tests/data/acpi/q35/APIC.xapic rename to tests/data/acpi/x86/q35/APIC.xapic diff --git a/tests/data/acpi/q35/CEDT.cxl b/tests/data/acpi/x86/q35/CEDT.cxl similarity index 100% rename from tests/data/acpi/q35/CEDT.cxl rename to tests/data/acpi/x86/q35/CEDT.cxl diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/x86/q35/DMAR.dmar similarity index 100% rename from tests/data/acpi/q35/DMAR.dmar rename to tests/data/acpi/x86/q35/DMAR.dmar diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/x86/q35/DSDT similarity index 100% rename from tests/data/acpi/q35/DSDT rename to tests/data/acpi/x86/q35/DSDT diff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/x86/q35/DSDT.acpierst similarity index 100% rename from tests/data/acpi/q35/DSDT.acpierst rename to tests/data/acpi/x86/q35/DSDT.acpierst diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/x86/q35/DSDT.acpihmat similarity index 100% rename from tests/data/acpi/q35/DSDT.acpihmat rename to tests/data/acpi/x86/q35/DSDT.acpihmat diff --git a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/DSDT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator diff --git a/tests/data/acpi/q35/DSDT.applesmc b/tests/data/acpi/x86/q35/DSDT.applesmc similarity index 100% rename from tests/data/acpi/q35/DSDT.applesmc rename to tests/data/acpi/x86/q35/DSDT.applesmc diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/x86/q35/DSDT.bridge similarity index 100% rename from tests/data/acpi/q35/DSDT.bridge rename to tests/data/acpi/x86/q35/DSDT.bridge diff --git a/tests/data/acpi/q35/DSDT.core-count b/tests/data/acpi/x86/q35/DSDT.core-count similarity index 100% rename from tests/data/acpi/q35/DSDT.core-count rename to tests/data/acpi/x86/q35/DSDT.core-count diff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/x86/q35/DSDT.core-count2 similarity index 100% rename from tests/data/acpi/q35/DSDT.core-count2 rename to tests/data/acpi/x86/q35/DSDT.core-count2 diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/x86/q35/DSDT.cphp similarity index 100% rename from tests/data/acpi/q35/DSDT.cphp rename to tests/data/acpi/x86/q35/DSDT.cphp diff --git a/tests/data/acpi/q35/DSDT.cxl b/tests/data/acpi/x86/q35/DSDT.cxl similarity index 100% rename from tests/data/acpi/q35/DSDT.cxl rename to tests/data/acpi/x86/q35/DSDT.cxl diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/x86/q35/DSDT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/DSDT.dimmpxm rename to tests/data/acpi/x86/q35/DSDT.dimmpxm diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/x86/q35/DSDT.ipmibt similarity index 100% rename from tests/data/acpi/q35/DSDT.ipmibt rename to tests/data/acpi/x86/q35/DSDT.ipmibt diff --git a/tests/data/acpi/q35/DSDT.ipmismbus b/tests/data/acpi/x86/q35/DSDT.ipmismbus similarity index 100% rename from tests/data/acpi/q35/DSDT.ipmismbus rename to tests/data/acpi/x86/q35/DSDT.ipmismbus diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/x86/q35/DSDT.ivrs similarity index 100% rename from tests/data/acpi/q35/DSDT.ivrs rename to tests/data/acpi/x86/q35/DSDT.ivrs diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/x86/q35/DSDT.memhp similarity index 100% rename from tests/data/acpi/q35/DSDT.memhp rename to tests/data/acpi/x86/q35/DSDT.memhp diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/x86/q35/DSDT.mmio64 similarity index 100% rename from tests/data/acpi/q35/DSDT.mmio64 rename to tests/data/acpi/x86/q35/DSDT.mmio64 diff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/x86/q35/DSDT.multi-bridge similarity index 100% rename from tests/data/acpi/q35/DSDT.multi-bridge rename to tests/data/acpi/x86/q35/DSDT.multi-bridge diff --git a/tests/data/acpi/q35/DSDT.noacpihp b/tests/data/acpi/x86/q35/DSDT.noacpihp similarity index 100% rename from tests/data/acpi/q35/DSDT.noacpihp rename to tests/data/acpi/x86/q35/DSDT.noacpihp diff --git a/tests/data/acpi/q35/DSDT.nohpet b/tests/data/acpi/x86/q35/DSDT.nohpet similarity index 100% rename from tests/data/acpi/q35/DSDT.nohpet rename to tests/data/acpi/x86/q35/DSDT.nohpet diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/x86/q35/DSDT.numamem similarity index 100% rename from tests/data/acpi/q35/DSDT.numamem rename to tests/data/acpi/x86/q35/DSDT.numamem diff --git a/tests/data/acpi/q35/DSDT.pvpanic-isa b/tests/data/acpi/x86/q35/DSDT.pvpanic-isa similarity index 100% rename from tests/data/acpi/q35/DSDT.pvpanic-isa rename to tests/data/acpi/x86/q35/DSDT.pvpanic-isa diff --git a/tests/data/acpi/q35/DSDT.thread-count b/tests/data/acpi/x86/q35/DSDT.thread-count similarity index 100% rename from tests/data/acpi/q35/DSDT.thread-count rename to tests/data/acpi/x86/q35/DSDT.thread-count diff --git a/tests/data/acpi/q35/DSDT.thread-count2 b/tests/data/acpi/x86/q35/DSDT.thread-count2 similarity index 100% rename from tests/data/acpi/q35/DSDT.thread-count2 rename to tests/data/acpi/x86/q35/DSDT.thread-count2 diff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/x86/q35/DSDT.tis.tpm12 similarity index 100% rename from tests/data/acpi/q35/DSDT.tis.tpm12 rename to tests/data/acpi/x86/q35/DSDT.tis.tpm12 diff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 similarity index 100% rename from tests/data/acpi/q35/DSDT.tis.tpm2 rename to tests/data/acpi/x86/q35/DSDT.tis.tpm2 diff --git a/tests/data/acpi/q35/DSDT.type4-count b/tests/data/acpi/x86/q35/DSDT.type4-count similarity index 100% rename from tests/data/acpi/q35/DSDT.type4-count rename to tests/data/acpi/x86/q35/DSDT.type4-count diff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/x86/q35/DSDT.viot similarity index 100% rename from tests/data/acpi/q35/DSDT.viot rename to tests/data/acpi/x86/q35/DSDT.viot diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/x86/q35/DSDT.xapic similarity index 100% rename from tests/data/acpi/q35/DSDT.xapic rename to tests/data/acpi/x86/q35/DSDT.xapic diff --git a/tests/data/acpi/q35/ERST.acpierst b/tests/data/acpi/x86/q35/ERST.acpierst similarity index 100% rename from tests/data/acpi/q35/ERST.acpierst rename to tests/data/acpi/x86/q35/ERST.acpierst diff --git a/tests/data/acpi/q35/FACP b/tests/data/acpi/x86/q35/FACP similarity index 100% rename from tests/data/acpi/q35/FACP rename to tests/data/acpi/x86/q35/FACP diff --git a/tests/data/acpi/q35/FACP.core-count b/tests/data/acpi/x86/q35/FACP.core-count similarity index 100% rename from tests/data/acpi/q35/FACP.core-count rename to tests/data/acpi/x86/q35/FACP.core-count diff --git a/tests/data/acpi/q35/FACP.core-count2 b/tests/data/acpi/x86/q35/FACP.core-count2 similarity index 100% rename from tests/data/acpi/q35/FACP.core-count2 rename to tests/data/acpi/x86/q35/FACP.core-count2 diff --git a/tests/data/acpi/q35/FACP.nosmm b/tests/data/acpi/x86/q35/FACP.nosmm similarity index 100% rename from tests/data/acpi/q35/FACP.nosmm rename to tests/data/acpi/x86/q35/FACP.nosmm diff --git a/tests/data/acpi/q35/FACP.slic b/tests/data/acpi/x86/q35/FACP.slic similarity index 100% rename from tests/data/acpi/q35/FACP.slic rename to tests/data/acpi/x86/q35/FACP.slic diff --git a/tests/data/acpi/q35/FACP.thread-count b/tests/data/acpi/x86/q35/FACP.thread-count similarity index 100% rename from tests/data/acpi/q35/FACP.thread-count rename to tests/data/acpi/x86/q35/FACP.thread-count diff --git a/tests/data/acpi/q35/FACP.thread-count2 b/tests/data/acpi/x86/q35/FACP.thread-count2 similarity index 100% rename from tests/data/acpi/q35/FACP.thread-count2 rename to tests/data/acpi/x86/q35/FACP.thread-count2 diff --git a/tests/data/acpi/q35/FACP.type4-count b/tests/data/acpi/x86/q35/FACP.type4-count similarity index 100% rename from tests/data/acpi/q35/FACP.type4-count rename to tests/data/acpi/x86/q35/FACP.type4-count diff --git a/tests/data/acpi/q35/FACP.xapic b/tests/data/acpi/x86/q35/FACP.xapic similarity index 100% rename from tests/data/acpi/q35/FACP.xapic rename to tests/data/acpi/x86/q35/FACP.xapic diff --git a/tests/data/acpi/q35/FACS b/tests/data/acpi/x86/q35/FACS similarity index 100% rename from tests/data/acpi/q35/FACS rename to tests/data/acpi/x86/q35/FACS diff --git a/tests/data/acpi/q35/HMAT.acpihmat b/tests/data/acpi/x86/q35/HMAT.acpihmat similarity index 100% rename from tests/data/acpi/q35/HMAT.acpihmat rename to tests/data/acpi/x86/q35/HMAT.acpihmat diff --git a/tests/data/acpi/q35/HMAT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/HMAT.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/HMAT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/HMAT.acpihmat-noinitiator diff --git a/tests/data/acpi/q35/HPET b/tests/data/acpi/x86/q35/HPET similarity index 100% rename from tests/data/acpi/q35/HPET rename to tests/data/acpi/x86/q35/HPET diff --git a/tests/data/acpi/q35/IVRS.ivrs b/tests/data/acpi/x86/q35/IVRS.ivrs similarity index 100% rename from tests/data/acpi/q35/IVRS.ivrs rename to tests/data/acpi/x86/q35/IVRS.ivrs diff --git a/tests/data/acpi/q35/MCFG b/tests/data/acpi/x86/q35/MCFG similarity index 100% rename from tests/data/acpi/q35/MCFG rename to tests/data/acpi/x86/q35/MCFG diff --git a/tests/data/acpi/q35/NFIT.dimmpxm b/tests/data/acpi/x86/q35/NFIT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/NFIT.dimmpxm rename to tests/data/acpi/x86/q35/NFIT.dimmpxm diff --git a/tests/data/acpi/q35/SLIC.slic b/tests/data/acpi/x86/q35/SLIC.slic similarity index 100% rename from tests/data/acpi/q35/SLIC.slic rename to tests/data/acpi/x86/q35/SLIC.slic diff --git a/tests/data/acpi/q35/SLIT.cphp b/tests/data/acpi/x86/q35/SLIT.cphp similarity index 100% rename from tests/data/acpi/q35/SLIT.cphp rename to tests/data/acpi/x86/q35/SLIT.cphp diff --git a/tests/data/acpi/q35/SLIT.memhp b/tests/data/acpi/x86/q35/SLIT.memhp similarity index 100% rename from tests/data/acpi/q35/SLIT.memhp rename to tests/data/acpi/x86/q35/SLIT.memhp diff --git a/tests/data/acpi/q35/SRAT.acpihmat b/tests/data/acpi/x86/q35/SRAT.acpihmat similarity index 100% rename from tests/data/acpi/q35/SRAT.acpihmat rename to tests/data/acpi/x86/q35/SRAT.acpihmat diff --git a/tests/data/acpi/q35/SRAT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/SRAT.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/SRAT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/SRAT.acpihmat-noinitiator diff --git a/tests/data/acpi/q35/SRAT.cphp b/tests/data/acpi/x86/q35/SRAT.cphp similarity index 100% rename from tests/data/acpi/q35/SRAT.cphp rename to tests/data/acpi/x86/q35/SRAT.cphp diff --git a/tests/data/acpi/q35/SRAT.dimmpxm b/tests/data/acpi/x86/q35/SRAT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/SRAT.dimmpxm rename to tests/data/acpi/x86/q35/SRAT.dimmpxm diff --git a/tests/data/acpi/q35/SRAT.memhp b/tests/data/acpi/x86/q35/SRAT.memhp similarity index 100% rename from tests/data/acpi/q35/SRAT.memhp rename to tests/data/acpi/x86/q35/SRAT.memhp diff --git a/tests/data/acpi/q35/SRAT.mmio64 b/tests/data/acpi/x86/q35/SRAT.mmio64 similarity index 100% rename from tests/data/acpi/q35/SRAT.mmio64 rename to tests/data/acpi/x86/q35/SRAT.mmio64 diff --git a/tests/data/acpi/q35/SRAT.numamem b/tests/data/acpi/x86/q35/SRAT.numamem similarity index 100% rename from tests/data/acpi/q35/SRAT.numamem rename to tests/data/acpi/x86/q35/SRAT.numamem diff --git a/tests/data/acpi/q35/SRAT.xapic b/tests/data/acpi/x86/q35/SRAT.xapic similarity index 100% rename from tests/data/acpi/q35/SRAT.xapic rename to tests/data/acpi/x86/q35/SRAT.xapic diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/x86/q35/SSDT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/SSDT.dimmpxm rename to tests/data/acpi/x86/q35/SSDT.dimmpxm diff --git a/tests/data/acpi/q35/TCPA.tis.tpm12 b/tests/data/acpi/x86/q35/TCPA.tis.tpm12 similarity index 100% rename from tests/data/acpi/q35/TCPA.tis.tpm12 rename to tests/data/acpi/x86/q35/TCPA.tis.tpm12 diff --git a/tests/data/acpi/q35/TPM2.tis.tpm2 b/tests/data/acpi/x86/q35/TPM2.tis.tpm2 similarity index 100% rename from tests/data/acpi/q35/TPM2.tis.tpm2 rename to tests/data/acpi/x86/q35/TPM2.tis.tpm2 diff --git a/tests/data/acpi/q35/VIOT.viot b/tests/data/acpi/x86/q35/VIOT.viot similarity index 100% rename from tests/data/acpi/q35/VIOT.viot rename to tests/data/acpi/x86/q35/VIOT.viot diff --git a/tests/data/acpi/q35/WAET b/tests/data/acpi/x86/q35/WAET similarity index 100% rename from tests/data/acpi/q35/WAET rename to tests/data/acpi/x86/q35/WAET From 7434f904673c36b78871a3b18ab1f5c09c640131 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:33 +0530 Subject: [PATCH 1953/2884] tests/data/acpi/virt: Move ARM64 ACPI tables under aarch64/${machine} path Same machine name can be used by different architectures. Hence, create aarch64 folder and move all aarch64 related AML files for virt machine inside. Signed-off-by: Sunil V L Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-11-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/{ => aarch64}/virt/APIC | Bin .../data/acpi/{ => aarch64}/virt/APIC.acpihmatvirt | Bin tests/data/acpi/{ => aarch64}/virt/APIC.topology | Bin tests/data/acpi/{ => aarch64}/virt/DBG2 | Bin tests/data/acpi/{ => aarch64}/virt/DSDT | Bin .../data/acpi/{ => aarch64}/virt/DSDT.acpihmatvirt | Bin tests/data/acpi/{ => aarch64}/virt/DSDT.memhp | Bin tests/data/acpi/{ => aarch64}/virt/DSDT.pxb | Bin tests/data/acpi/{ => aarch64}/virt/DSDT.topology | Bin tests/data/acpi/{ => aarch64}/virt/FACP | Bin tests/data/acpi/{ => aarch64}/virt/GTDT | Bin .../data/acpi/{ => aarch64}/virt/HMAT.acpihmatvirt | Bin tests/data/acpi/{ => aarch64}/virt/IORT | Bin tests/data/acpi/{ => aarch64}/virt/MCFG | Bin tests/data/acpi/{ => aarch64}/virt/NFIT.memhp | Bin tests/data/acpi/{ => aarch64}/virt/PPTT | Bin .../data/acpi/{ => aarch64}/virt/PPTT.acpihmatvirt | Bin tests/data/acpi/{ => aarch64}/virt/PPTT.topology | Bin tests/data/acpi/{ => aarch64}/virt/SLIT.memhp | Bin tests/data/acpi/{ => aarch64}/virt/SPCR | Bin .../data/acpi/{ => aarch64}/virt/SRAT.acpihmatvirt | Bin tests/data/acpi/{ => aarch64}/virt/SRAT.memhp | Bin tests/data/acpi/{ => aarch64}/virt/SRAT.numamem | Bin tests/data/acpi/{ => aarch64}/virt/SSDT.memhp | Bin tests/data/acpi/{ => aarch64}/virt/VIOT | Bin 25 files changed, 0 insertions(+), 0 deletions(-) rename tests/data/acpi/{ => aarch64}/virt/APIC (100%) rename tests/data/acpi/{ => aarch64}/virt/APIC.acpihmatvirt (100%) rename tests/data/acpi/{ => aarch64}/virt/APIC.topology (100%) rename tests/data/acpi/{ => aarch64}/virt/DBG2 (100%) rename tests/data/acpi/{ => aarch64}/virt/DSDT (100%) rename tests/data/acpi/{ => aarch64}/virt/DSDT.acpihmatvirt (100%) rename tests/data/acpi/{ => aarch64}/virt/DSDT.memhp (100%) rename tests/data/acpi/{ => aarch64}/virt/DSDT.pxb (100%) rename tests/data/acpi/{ => aarch64}/virt/DSDT.topology (100%) rename tests/data/acpi/{ => aarch64}/virt/FACP (100%) rename tests/data/acpi/{ => aarch64}/virt/GTDT (100%) rename tests/data/acpi/{ => aarch64}/virt/HMAT.acpihmatvirt (100%) rename tests/data/acpi/{ => aarch64}/virt/IORT (100%) rename tests/data/acpi/{ => aarch64}/virt/MCFG (100%) rename tests/data/acpi/{ => aarch64}/virt/NFIT.memhp (100%) rename tests/data/acpi/{ => aarch64}/virt/PPTT (100%) rename tests/data/acpi/{ => aarch64}/virt/PPTT.acpihmatvirt (100%) rename tests/data/acpi/{ => aarch64}/virt/PPTT.topology (100%) rename tests/data/acpi/{ => aarch64}/virt/SLIT.memhp (100%) rename tests/data/acpi/{ => aarch64}/virt/SPCR (100%) rename tests/data/acpi/{ => aarch64}/virt/SRAT.acpihmatvirt (100%) rename tests/data/acpi/{ => aarch64}/virt/SRAT.memhp (100%) rename tests/data/acpi/{ => aarch64}/virt/SRAT.numamem (100%) rename tests/data/acpi/{ => aarch64}/virt/SSDT.memhp (100%) rename tests/data/acpi/{ => aarch64}/virt/VIOT (100%) diff --git a/tests/data/acpi/virt/APIC b/tests/data/acpi/aarch64/virt/APIC similarity index 100% rename from tests/data/acpi/virt/APIC rename to tests/data/acpi/aarch64/virt/APIC diff --git a/tests/data/acpi/virt/APIC.acpihmatvirt b/tests/data/acpi/aarch64/virt/APIC.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/APIC.acpihmatvirt rename to tests/data/acpi/aarch64/virt/APIC.acpihmatvirt diff --git a/tests/data/acpi/virt/APIC.topology b/tests/data/acpi/aarch64/virt/APIC.topology similarity index 100% rename from tests/data/acpi/virt/APIC.topology rename to tests/data/acpi/aarch64/virt/APIC.topology diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/aarch64/virt/DBG2 similarity index 100% rename from tests/data/acpi/virt/DBG2 rename to tests/data/acpi/aarch64/virt/DBG2 diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT similarity index 100% rename from tests/data/acpi/virt/DSDT rename to tests/data/acpi/aarch64/virt/DSDT diff --git a/tests/data/acpi/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/DSDT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp similarity index 100% rename from tests/data/acpi/virt/DSDT.memhp rename to tests/data/acpi/aarch64/virt/DSDT.memhp diff --git a/tests/data/acpi/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb similarity index 100% rename from tests/data/acpi/virt/DSDT.pxb rename to tests/data/acpi/aarch64/virt/DSDT.pxb diff --git a/tests/data/acpi/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology similarity index 100% rename from tests/data/acpi/virt/DSDT.topology rename to tests/data/acpi/aarch64/virt/DSDT.topology diff --git a/tests/data/acpi/virt/FACP b/tests/data/acpi/aarch64/virt/FACP similarity index 100% rename from tests/data/acpi/virt/FACP rename to tests/data/acpi/aarch64/virt/FACP diff --git a/tests/data/acpi/virt/GTDT b/tests/data/acpi/aarch64/virt/GTDT similarity index 100% rename from tests/data/acpi/virt/GTDT rename to tests/data/acpi/aarch64/virt/GTDT diff --git a/tests/data/acpi/virt/HMAT.acpihmatvirt b/tests/data/acpi/aarch64/virt/HMAT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/HMAT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/HMAT.acpihmatvirt diff --git a/tests/data/acpi/virt/IORT b/tests/data/acpi/aarch64/virt/IORT similarity index 100% rename from tests/data/acpi/virt/IORT rename to tests/data/acpi/aarch64/virt/IORT diff --git a/tests/data/acpi/virt/MCFG b/tests/data/acpi/aarch64/virt/MCFG similarity index 100% rename from tests/data/acpi/virt/MCFG rename to tests/data/acpi/aarch64/virt/MCFG diff --git a/tests/data/acpi/virt/NFIT.memhp b/tests/data/acpi/aarch64/virt/NFIT.memhp similarity index 100% rename from tests/data/acpi/virt/NFIT.memhp rename to tests/data/acpi/aarch64/virt/NFIT.memhp diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/aarch64/virt/PPTT similarity index 100% rename from tests/data/acpi/virt/PPTT rename to tests/data/acpi/aarch64/virt/PPTT diff --git a/tests/data/acpi/virt/PPTT.acpihmatvirt b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/PPTT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt diff --git a/tests/data/acpi/virt/PPTT.topology b/tests/data/acpi/aarch64/virt/PPTT.topology similarity index 100% rename from tests/data/acpi/virt/PPTT.topology rename to tests/data/acpi/aarch64/virt/PPTT.topology diff --git a/tests/data/acpi/virt/SLIT.memhp b/tests/data/acpi/aarch64/virt/SLIT.memhp similarity index 100% rename from tests/data/acpi/virt/SLIT.memhp rename to tests/data/acpi/aarch64/virt/SLIT.memhp diff --git a/tests/data/acpi/virt/SPCR b/tests/data/acpi/aarch64/virt/SPCR similarity index 100% rename from tests/data/acpi/virt/SPCR rename to tests/data/acpi/aarch64/virt/SPCR diff --git a/tests/data/acpi/virt/SRAT.acpihmatvirt b/tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/SRAT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt diff --git a/tests/data/acpi/virt/SRAT.memhp b/tests/data/acpi/aarch64/virt/SRAT.memhp similarity index 100% rename from tests/data/acpi/virt/SRAT.memhp rename to tests/data/acpi/aarch64/virt/SRAT.memhp diff --git a/tests/data/acpi/virt/SRAT.numamem b/tests/data/acpi/aarch64/virt/SRAT.numamem similarity index 100% rename from tests/data/acpi/virt/SRAT.numamem rename to tests/data/acpi/aarch64/virt/SRAT.numamem diff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/aarch64/virt/SSDT.memhp similarity index 100% rename from tests/data/acpi/virt/SSDT.memhp rename to tests/data/acpi/aarch64/virt/SSDT.memhp diff --git a/tests/data/acpi/virt/VIOT b/tests/data/acpi/aarch64/virt/VIOT similarity index 100% rename from tests/data/acpi/virt/VIOT rename to tests/data/acpi/aarch64/virt/VIOT From 008115bba06a30b13b3fd86cade8a280490e06f3 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:34 +0530 Subject: [PATCH 1954/2884] meson.build: Add RISC-V to the edk2-target list so that ACPI table test can be supported. Signed-off-by: Sunil V L Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-12-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 54e6b09f4f..efd3d4c6e5 100644 --- a/meson.build +++ b/meson.build @@ -93,7 +93,7 @@ else iasl = find_program(get_option('iasl'), required: true) endif -edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] +edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu', 'riscv64-softmmu' ] unpack_edk2_blobs = false foreach target : edk2_targets if target in target_dirs From ce7325c160953e717ff662eabdc7bb911029760f Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:35 +0530 Subject: [PATCH 1955/2884] pc-bios/meson.build: Add support for RISC-V in unpack_edk2_blobs Update list of images supported in unpack_edk2_blobs to enable RISC-V ACPI table testing. Signed-off-by: Sunil V L Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-13-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- pc-bios/meson.build | 2 ++ tests/qtest/meson.build | 3 +++ 2 files changed, 5 insertions(+) diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 0760612bea..8602b45b9b 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -4,6 +4,8 @@ if unpack_edk2_blobs 'edk2-aarch64-code.fd', 'edk2-arm-code.fd', 'edk2-arm-vars.fd', + 'edk2-riscv-code.fd', + 'edk2-riscv-vars.fd', 'edk2-i386-code.fd', 'edk2-i386-secure-code.fd', 'edk2-i386-vars.fd', diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 12792948ff..6508bfb1a2 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -259,6 +259,9 @@ qtests_s390x = \ qtests_riscv32 = \ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) +qtests_riscv64 = \ + (unpack_edk2_blobs ? ['bios-tables-test'] : []) + qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', From 0f130d9e372552ef900bfac062ffc6a77b4049cc Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 25 Jun 2024 20:38:36 +0530 Subject: [PATCH 1956/2884] tests/data/acpi/rebuild-expected-aml.sh: Add RISC-V Update the list of supported architectures to include RISC-V. Signed-off-by: Sunil V L Reviewed-by: Alistair Francis Reviewed-by: Igor Mammedov Message-Id: <20240625150839.1358279-14-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/rebuild-expected-aml.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index dcf2e2f221..c1092fb8ba 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -12,7 +12,7 @@ # This work is licensed under the terms of the GNU GPLv2. # See the COPYING.LIB file in the top-level directory. -qemu_arches="x86_64 aarch64" +qemu_arches="x86_64 aarch64 riscv64" if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." @@ -36,7 +36,8 @@ fi if [ -z "$qemu_bins" ]; then echo "Only the following architectures are currently supported: $qemu_arches" echo "None of these configured!" - echo "To fix, run configure --target-list=x86_64-softmmu,aarch64-softmmu" + echo "To fix, run configure \ + --target-list=x86_64-softmmu,aarch64-softmmu,riscv64-softmmu" exit 1; fi From efc4ad6f9901bbba08c0e11443ba89f18b1a28e9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 25 Jun 2024 18:08:04 +0100 Subject: [PATCH 1957/2884] hw/cxl/events: Improve QMP interfaces and documentation for add/release dynamic capacity. New DCD command definitions updated in response to review comments from Markus. - Used CxlXXXX instead of CXLXXXXX for newly added types. - Expanded some abreviations in type names to be easier to read. - Additional documentation for some fields. - Replace slightly vague cxl r3.1 references with "Compute Express Link (CXL) Specification, Revision 3.1, XXXX" to bring them inline with what it says on the specification cover. Suggested-by: Markus Armbruster Signed-off-by: Jonathan Cameron Message-Id: <20240625170805.359278-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 18 ++--- hw/mem/cxl_type3_stubs.c | 8 +-- qapi/cxl.json | 144 ++++++++++++++++++++++++--------------- 3 files changed, 103 insertions(+), 67 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 3274e5dcbb..35ac59883a 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1874,7 +1874,7 @@ static bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, */ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, uint16_t hid, CXLDCEventType type, uint8_t rid, - CXLDynamicCapacityExtentList *records, Error **errp) + CxlDynamicCapacityExtentList *records, Error **errp) { Object *obj; CXLEventDynamicCapacity dCap = {}; @@ -1882,7 +1882,7 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, CXLType3Dev *dcd; uint8_t flags = 1 << CXL_EVENT_TYPE_INFO; uint32_t num_extents = 0; - CXLDynamicCapacityExtentList *list; + CxlDynamicCapacityExtentList *list; CXLDCExtentGroup *group = NULL; g_autofree CXLDCExtentRaw *extents = NULL; uint8_t enc_log = CXL_EVENT_TYPE_DYNAMIC_CAP; @@ -2032,13 +2032,13 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, } void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, - CXLExtSelPolicy sel_policy, uint8_t region, - const char *tag, - CXLDynamicCapacityExtentList *extents, + CxlExtentSelectionPolicy sel_policy, + uint8_t region, const char *tag, + CxlDynamicCapacityExtentList *extents, Error **errp) { switch (sel_policy) { - case CXL_EXT_SEL_POLICY_PRESCRIPTIVE: + case CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE: qmp_cxl_process_dynamic_capacity_prescriptive(path, host_id, DC_EVENT_ADD_CAPACITY, region, extents, errp); @@ -2050,14 +2050,14 @@ void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, } void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, - CXLExtRemovalPolicy removal_policy, + CxlExtentRemovalPolicy removal_policy, bool has_forced_removal, bool forced_removal, bool has_sanitize_on_release, bool sanitize_on_release, uint8_t region, const char *tag, - CXLDynamicCapacityExtentList *extents, + CxlDynamicCapacityExtentList *extents, Error **errp) { CXLDCEventType type = DC_EVENT_RELEASE_CAPACITY; @@ -2070,7 +2070,7 @@ void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, } switch (removal_policy) { - case CXL_EXT_REMOVAL_POLICY_PRESCRIPTIVE: + case CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE: qmp_cxl_process_dynamic_capacity_prescriptive(path, host_id, type, region, extents, errp); return; diff --git a/hw/mem/cxl_type3_stubs.c b/hw/mem/cxl_type3_stubs.c index 45419bbefe..c1a5e4a7c1 100644 --- a/hw/mem/cxl_type3_stubs.c +++ b/hw/mem/cxl_type3_stubs.c @@ -70,24 +70,24 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, - CXLExtSelPolicy sel_policy, + CxlExtentSelectionPolicy sel_policy, uint8_t region, const char *tag, - CXLDynamicCapacityExtentList *extents, + CxlDynamicCapacityExtentList *extents, Error **errp) { error_setg(errp, "CXL Type 3 support is not compiled in"); } void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, - CXLExtRemovalPolicy removal_policy, + CxlExtentRemovalPolicy removal_policy, bool has_forced_removal, bool forced_removal, bool has_sanitize_on_release, bool sanitize_on_release, uint8_t region, const char *tag, - CXLDynamicCapacityExtentList *extents, + CxlDynamicCapacityExtentList *extents, Error **errp) { error_setg(errp, "CXL Type 3 support is not compiled in"); diff --git a/qapi/cxl.json b/qapi/cxl.json index 57d9f82014..a38622a0d1 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -363,9 +363,11 @@ 'data': {'path': 'str', 'type': 'CxlCorErrorType'}} ## -# @CXLDynamicCapacityExtent: +# @CxlDynamicCapacityExtent: # -# A single dynamic capacity extent +# A single dynamic capacity extent. This is a contiguous allocation +# of memory by Device Physical Address within a single Dynamic +# Capacity Region on a CXL Type 3 Device. # # @offset: The offset (in bytes) to the start of the region # where the extent belongs to. @@ -374,7 +376,7 @@ # # Since: 9.1 ## -{ 'struct': 'CXLDynamicCapacityExtent', +{ 'struct': 'CxlDynamicCapacityExtent', 'data': { 'offset':'uint64', 'len': 'uint64' @@ -382,22 +384,40 @@ } ## -# @CXLExtSelPolicy: +# @CxlExtentSelectionPolicy: # # The policy to use for selecting which extents comprise the added -# capacity, as defined in cxl spec r3.1 Table 7-70. +# capacity, as defined in Compute Express Link (CXL) Specification, +# Revision 3.1, Table 7-70. # -# @free: 0h = Free +# @free: Device is responsible for allocating the requested memory +# capacity and is free to do this using any combination of +# supported extents. # -# @contiguous: 1h = Continuous +# @contiguous: Device is responsible for allocating the requested +# memory capacity but must do so as a single contiguous +# extent. # -# @prescriptive: 2h = Prescriptive +# @prescriptive: The precise set of extents to be allocated is +# specified by the command. Thus allocation is being managed +# by the issuer of the allocation command, not the device. # -# @enable-shared-access: 3h = Enable Shared Access +# @enable-shared-access: Capacity has already been allocated to a +# different host using free, contiguous or prescriptive policy +# with a known tag. This policy then instructs the device to +# make the capacity with the specified tag available to an +# additional host. Capacity is implicit as it matches that +# already associated with the tag. Note that the extent list +# (and hence Device Physical Addresses) used are per host, so +# a device may use different representations on each host. +# The ordering of the extents provided to each host is indicated +# to the host using per extent sequence numbers generated by +# the device. Has a similar meaning for temporal sharing, but +# in that case there may be only one host involved. # # Since: 9.1 ## -{ 'enum': 'CXLExtSelPolicy', +{ 'enum': 'CxlExtentSelectionPolicy', 'data': ['free', 'contiguous', 'prescriptive', @@ -407,54 +427,60 @@ ## # @cxl-add-dynamic-capacity: # -# Command to initiate to add dynamic capacity extents to a host. It -# simulates operations defined in cxl spec r3.1 7.6.7.6.5. +# Initiate adding dynamic capacity extents to a host. This simulates +# operations defined in Compute Express Link (CXL) Specification, +# Revision 3.1, Section 7.6.7.6.5. Note that, currently, establishing +# success or failure of the full Add Dynamic Capacity flow requires +# out of band communication with the OS of the CXL host. # -# @path: CXL DCD canonical QOM path. +# @path: path to the CXL Dynamic Capacity Device in the QOM tree. # -# @host-id: The "Host ID" field as defined in cxl spec r3.1 -# Table 7-70. +# @host-id: The "Host ID" field as defined in Compute Express Link +# (CXL) Specification, Revision 3.1, Table 7-70. # # @selection-policy: The "Selection Policy" bits as defined in -# cxl spec r3.1 Table 7-70. It specifies the policy to use for -# selecting which extents comprise the added capacity. +# Compute Express Link (CXL) Specification, Revision 3.1, +# Table 7-70. It specifies the policy to use for selecting +# which extents comprise the added capacity. # -# @region: The "Region Number" field as defined in cxl spec r3.1 -# Table 7-70. The dynamic capacity region where the capacity -# is being added. Valid range is from 0-7. +# @region: The "Region Number" field as defined in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-70. Valid +# range is from 0-7. # -# @tag: The "Tag" field as defined in cxl spec r3.1 Table 7-70. +# @tag: The "Tag" field as defined in Compute Express Link (CXL) +# Specification, Revision 3.1, Table 7-70. # -# @extents: The "Extent List" field as defined in cxl spec r3.1 -# Table 7-70. +# @extents: The "Extent List" field as defined in Compute Express Link +# (CXL) Specification, Revision 3.1, Table 7-70. # # Since : 9.1 ## { 'command': 'cxl-add-dynamic-capacity', 'data': { 'path': 'str', 'host-id': 'uint16', - 'selection-policy': 'CXLExtSelPolicy', + 'selection-policy': 'CxlExtentSelectionPolicy', 'region': 'uint8', '*tag': 'str', - 'extents': [ 'CXLDynamicCapacityExtent' ] + 'extents': [ 'CxlDynamicCapacityExtent' ] } } ## -# @CXLExtRemovalPolicy: +# @CxlExtentRemovalPolicy: # # The policy to use for selecting which extents comprise the released -# capacity, defined in the "Flags" field in cxl spec r3.1 Table 7-71. +# capacity, defined in the "Flags" field in Compute Express Link (CXL) +# Specification, Revision 3.1, Table 7-71. # -# @tag-based: value = 0h. Extents are selected by the device based -# on tag, with no requirement for contiguous extents. +# @tag-based: Extents are selected by the device based on tag, with +# no requirement for contiguous extents. # -# @prescriptive: value = 1h. Extent list of capacity to release is -# included in the request payload. +# @prescriptive: Extent list of capacity to release is included in +# the request payload. # # Since: 9.1 ## -{ 'enum': 'CXLExtRemovalPolicy', +{ 'enum': 'CxlExtentRemovalPolicy', 'data': ['tag-based', 'prescriptive'] } @@ -462,45 +488,55 @@ ## # @cxl-release-dynamic-capacity: # -# Command to initiate to release dynamic capacity extents from a -# host. It simulates operations defined in cxl spec r3.1 7.6.7.6.6. +# Initiate release of dynamic capacity extents from a host. This +# simulates operations defined in Compute Express Link (CXL) +# Specification, Revision 3.1, Section 7.6.7.6.6. Note that, +# currently, success or failure of the full Release Dynamic Capacity +# flow requires out of band communication with the OS of the CXL host. # -# @path: CXL DCD canonical QOM path. +# @path: path to the CXL Dynamic Capacity Device in the QOM tree. # -# @host-id: The "Host ID" field as defined in cxl spec r3.1 +# @host-id: The "Host ID" field as defined in Compute Express Link +# (CXL) Specification, Revision 3.1, Table 7-71. +# +# @removal-policy: Bit[3:0] of the "Flags" field as defined in +# Compute Express Link (CXL) Specification, Revision 3.1, # Table 7-71. # -# @removal-policy: Bit[3:0] of the "Flags" field as defined in cxl -# spec r3.1 Table 7-71. +# @forced-removal: Bit[4] of the "Flags" field in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-71. When set, +# the device does not wait for a Release Dynamic Capacity command +# from the host. Instead, the host immediately looses access to +# the released capacity. # -# @forced-removal: Bit[4] of the "Flags" field in cxl spec r3.1 -# Table 7-71. When set, device does not wait for a Release -# Dynamic Capacity command from the host. Host immediately -# loses access to released capacity. +# @sanitize-on-release: Bit[5] of the "Flags" field in Compute +# Express Link (CXL) Specification, Revision 3.1, Table 7-71. +# When set, the device should sanitize all released capacity as +# a result of this request. This ensures that all user data +# and metadata is made permanently unavailable by whatever +# means is appropriate for the media type. Note that changing +# encryption keys is not sufficient. # -# @sanitize-on-release: Bit[5] of the "Flags" field in cxl spec r3.1 -# Table 7-71. When set, device should sanitize all released -# capacity as a result of this request. +# @region: The "Region Number" field as defined in Compute Express +# Link Specification, Revision 3.1, Table 7-71. Valid range +# is from 0-7. # -# @region: The "Region Number" field as defined in cxl spec r3.1 -# Table 7-71. The dynamic capacity region where the capacity -# is being added. Valid range is from 0-7. +# @tag: The "Tag" field as defined in Compute Express Link (CXL) +# Specification, Revision 3.1, Table 7-71. # -# @tag: The "Tag" field as defined in cxl spec r3.1 Table 7-71. -# -# @extents: The "Extent List" field as defined in cxl spec r3.1 -# Table 7-71. +# @extents: The "Extent List" field as defined in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-71. # # Since : 9.1 ## { 'command': 'cxl-release-dynamic-capacity', 'data': { 'path': 'str', 'host-id': 'uint16', - 'removal-policy': 'CXLExtRemovalPolicy', + 'removal-policy': 'CxlExtentRemovalPolicy', '*forced-removal': 'bool', '*sanitize-on-release': 'bool', 'region': 'uint8', '*tag': 'str', - 'extents': [ 'CXLDynamicCapacityExtent' ] + 'extents': [ 'CxlDynamicCapacityExtent' ] } } From 5e3cd0a2f526c2e52dd513ee6b4385f1fb47a19e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 25 Jun 2024 18:08:05 +0100 Subject: [PATCH 1958/2884] hw/cxl/events: Mark cxl-add-dynamic-capacity and cxl-release-dynamic-capcity unstable Markus suggested that we make the unstable. I don't expect these interfaces to change because of their tight coupling to the Compute Express Link (CXL) Specification, Revision 3.1 Fabric Management API definitions which can only be extended in backwards compatible way. However, there seems little disadvantage in taking a cautious path for now and marking them as unstable interfaces. Suggested-by: Markus Armbruster Signed-off-by: Jonathan Cameron Message-Id: <20240625170805.359278-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- qapi/cxl.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/qapi/cxl.json b/qapi/cxl.json index a38622a0d1..bdfac67c47 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -453,6 +453,10 @@ # @extents: The "Extent List" field as defined in Compute Express Link # (CXL) Specification, Revision 3.1, Table 7-70. # +# Features: +# +# @unstable: For now this command is subject to change. +# # Since : 9.1 ## { 'command': 'cxl-add-dynamic-capacity', @@ -462,7 +466,8 @@ 'region': 'uint8', '*tag': 'str', 'extents': [ 'CxlDynamicCapacityExtent' ] - } + }, + 'features': [ 'unstable' ] } ## @@ -527,6 +532,10 @@ # @extents: The "Extent List" field as defined in Compute Express # Link (CXL) Specification, Revision 3.1, Table 7-71. # +# Features: +# +# @unstable: For now this command is subject to change. +# # Since : 9.1 ## { 'command': 'cxl-release-dynamic-capacity', @@ -538,5 +547,6 @@ 'region': 'uint8', '*tag': 'str', 'extents': [ 'CxlDynamicCapacityExtent' ] - } + }, + 'features': [ 'unstable' ] } From 7aa6492401e95fb296dec7cda81e67d91f6037d7 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Mon, 1 Jul 2024 09:52:08 +0200 Subject: [PATCH 1959/2884] virtio: remove virtio_tswap16s() call in vring_packed_event_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d152cdd6f6 ("virtio: use virtio accessor to access packed event") switched using of address_space_read_cached() to virito_lduw_phys_cached() to access packed descriptor event. When we used address_space_read_cached(), we needed to call virtio_tswap16s() to handle the endianess of the field, but virito_lduw_phys_cached() already handles it internally, so we no longer need to call virtio_tswap16s() (as the commit had done for `off_wrap`, but forgot for `flags`). Fixes: d152cdd6f6 ("virtio: use virtio accessor to access packed event") Cc: jasowang@redhat.com Cc: qemu-stable@nongnu.org Reported-by: Xoykie Link: https://lore.kernel.org/qemu-devel/CAFU8RB_pjr77zMLsM0Unf9xPNxfr_--Tjr49F_eX32ZBc5o2zQ@mail.gmail.com Signed-off-by: Stefano Garzarella Message-Id: <20240701075208.19634-1-sgarzare@redhat.com> Acked-by: Jason Wang Reviewed-by: Peter Maydell Reviewed-by: Eugenio Pérez Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 3678ec2f88..583a224163 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -323,7 +323,6 @@ static void vring_packed_event_read(VirtIODevice *vdev, /* Make sure flags is seen before off_wrap */ smp_rmb(); e->off_wrap = virtio_lduw_phys_cached(vdev, cache, off_off); - virtio_tswap16s(vdev, &e->flags); } static void vring_packed_off_wrap_write(VirtIODevice *vdev, From 1b889d6e39c32d709f1114699a014b381bcf1cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Jul 2024 12:14:53 +0200 Subject: [PATCH 1960/2884] virtio-iommu: Clear IOMMUDevice when VFIO device is unplugged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a VFIO device is hoplugged in a VM using virtio-iommu, IOMMUPciBus and IOMMUDevice cache entries are created in the .get_address_space() handler of the machine IOMMU device. However, these entries are never destroyed, not even when the VFIO device is detached from the machine. This can lead to an assert if the device is reattached again. When reattached, the .get_address_space() handler reuses an IOMMUDevice entry allocated when the VFIO device was first attached. virtio_iommu_set_host_iova_ranges() is called later on from the .set_iommu_device() handler an fails with an assert on 'probe_done' because the device appears to have been already probed when this is not the case. The IOMMUDevice entry is allocated in pci_device_iommu_address_space() called from under vfio_realize(), the VFIO PCI realize handler. Since pci_device_unset_iommu_device() is called from vfio_exitfn(), a sub function of the PCIDevice unrealize() handler, it seems that the .unset_iommu_device() handler is the best place to release resources allocated at realize time. Clear the IOMMUDevice cache entry there to fix hotplug. Fixes: 817ef10da23c ("virtio-iommu: Implement set|unset]_iommu_device() callbacks") Signed-off-by: Cédric Le Goater Message-Id: <20240701101453.203985-1-clg@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-iommu.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index ed7426afc7..7c54c6b5e2 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -467,6 +467,26 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, return &sdev->as; } +static void virtio_iommu_device_clear(VirtIOIOMMU *s, PCIBus *bus, int devfn) +{ + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + IOMMUDevice *sdev; + + if (!sbus) { + return; + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + return; + } + + g_list_free_full(sdev->resv_regions, g_free); + sdev->resv_regions = NULL; + g_free(sdev); + sbus->pbdev[devfn] = NULL; +} + static gboolean hiod_equal(gconstpointer v1, gconstpointer v2) { const struct hiod_key *key1 = v1; @@ -650,6 +670,7 @@ virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) } g_hash_table_remove(viommu->host_iommu_devices, &key); + virtio_iommu_device_clear(viommu, bus, devfn); } static const PCIIOMMUOps virtio_iommu_ops = { From 6a31b219a5338564f3978251c79f96f689e037da Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:50 +0900 Subject: [PATCH 1961/2884] hw/pci: Rename has_power to enabled The renamed state will not only represent powering state of PFs, but also represent SR-IOV VF enablement in the future. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-1-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 14 +++++++------- hw/pci/pci_host.c | 4 ++-- include/hw/pci/pci.h | 7 ++++++- include/hw/pci/pci_device.h | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 50b86d5790..68d30feb86 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1525,7 +1525,7 @@ static void pci_update_mappings(PCIDevice *d) continue; new_addr = pci_bar_address(d, i, r->type, r->size); - if (!d->has_power) { + if (!d->enabled) { new_addr = PCI_BAR_UNMAPPED; } @@ -1613,7 +1613,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int pci_update_irq_disabled(d, was_irq_disabled); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); + & PCI_COMMAND_MASTER) && d->enabled); } msi_write_config(d, addr, val_in, l); @@ -2884,18 +2884,18 @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) return msg; } -void pci_set_power(PCIDevice *d, bool state) +void pci_set_enabled(PCIDevice *d, bool state) { - if (d->has_power == state) { + if (d->enabled == state) { return; } - d->has_power = state; + d->enabled = state; pci_update_mappings(d); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); - if (!d->has_power) { + & PCI_COMMAND_MASTER) && d->enabled); + if (!d->enabled) { pci_device_reset(d); } } diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index dfe6fe6184..0d82727cc9 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -86,7 +86,7 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return; } @@ -111,7 +111,7 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return ~0x0; } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index eb26cac810..fe04b4fafd 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -678,6 +678,11 @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) } MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); -void pci_set_power(PCIDevice *pci_dev, bool state); +void pci_set_enabled(PCIDevice *pci_dev, bool state); + +static inline void pci_set_power(PCIDevice *pci_dev, bool state) +{ + pci_set_enabled(pci_dev, state); +} #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index d3dd0f64b2..d57f9ce838 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -56,7 +56,7 @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIDevice { DeviceState qdev; bool partially_hotplugged; - bool has_power; + bool enabled; /* PCI config space */ uint8_t *config; From 723c5b4628d047e43825a046c6ee517b82b88117 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:51 +0900 Subject: [PATCH 1962/2884] hw/ppc/spapr_pci: Do not create DT for disabled PCI device Disabled means it is a disabled SR-IOV VF or it is powered off, and hidden from the guest. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-2-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ppc/spapr_pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 7cf9904c35..f63182a03c 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1296,6 +1296,10 @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev, return; } + if (!pdev->enabled) { + return; + } + err = spapr_dt_pci_device(p->sphb, pdev, p->fdt, p->offset); if (err < 0) { p->err = err; From 26f86093ec989cb73ad03e8a234f5dc321e1e267 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:52 +0900 Subject: [PATCH 1963/2884] hw/ppc/spapr_pci: Do not reject VFs created after a PF A PF may automatically create VFs and the PF may be function 0. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-3-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ppc/spapr_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index f63182a03c..ed4454bbf7 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1573,7 +1573,9 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, * hotplug, we do not allow functions to be hotplugged to a * slot that already has function 0 present */ - if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && + if (plugged_dev->hotplugged && + !pci_is_vf(pdev) && + bus->devices[PCI_DEVFN(slotnr, 0)] && PCI_FUNC(pdev->devfn) != 0) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " additional functions can no longer be exposed to guest.", From c613ad25125bf3016aa8f81ce170f5ac91d2379f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:53 +0900 Subject: [PATCH 1964/2884] pcie_sriov: Do not manually unrealize A device gets automatically unrealized when being unparented. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-4-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index e9b23221d7..499becd527 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -204,11 +204,7 @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - Error *err = NULL; PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - if (!object_property_set_bool(OBJECT(vf), "realized", false, &err)) { - error_reportf_err(err, "Failed to unplug: "); - } object_unparent(OBJECT(vf)); object_unref(OBJECT(vf)); } From 77718701157f6ca77ea7a57b536fa0a22f676082 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:54 +0900 Subject: [PATCH 1965/2884] pcie_sriov: Ensure VF function number does not overflow pci_new() aborts when creating a VF with a function number equals to or is greater than PCI_DEVFN_MAX. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-5-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/pcie_sriov.txt | 8 +++++--- hw/net/igb.c | 13 ++++++++++--- hw/nvme/ctrl.c | 24 ++++++++++++++++-------- hw/pci/pcie_sriov.c | 19 +++++++++++++++++-- include/hw/pci/pcie_sriov.h | 5 +++-- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt index a47aad0bfa..ab2142807f 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -52,9 +52,11 @@ setting up a BAR for a VF. ... /* Add and initialize the SR/IOV capability */ - pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", - vf_devid, initial_vfs, total_vfs, - fun_offset, stride); + if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", + vf_devid, initial_vfs, total_vfs, + fun_offset, stride, errp)) { + return; + } /* Set up individual VF BARs (parameters as for normal BARs) */ pcie_sriov_pf_init_vf_bar( ... ) diff --git a/hw/net/igb.c b/hw/net/igb.c index b92bba402e..b6ca2f1b8a 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -446,9 +446,16 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_ari_init(pci_dev, 0x150); - pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF, - IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, - IGB_VF_OFFSET, IGB_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, + TYPE_IGBVF, IGB_82576_VF_DEV_ID, + IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, + IGB_VF_OFFSET, IGB_VF_STRIDE, + errp)) { + pcie_cap_exit(pci_dev); + igb_cleanup_msix(s); + msi_uninit(pci_dev); + return; + } pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 127c3d2383..066389e391 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8048,7 +8048,8 @@ out: return pow2ceil(bar_size); } -static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) +static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset, + Error **errp) { uint16_t vf_dev_id = n->params.use_intel_id ? PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME; @@ -8057,12 +8058,17 @@ static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) le16_to_cpu(cap->vifrsm), NULL, NULL); - pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, - n->params.sriov_max_vfs, n->params.sriov_max_vfs, - NVME_VF_OFFSET, NVME_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE, + errp)) { + return false; + } pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); + + return true; } static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) @@ -8155,6 +8161,12 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) return false; } + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs && + !nvme_init_sriov(n, pci_dev, 0x120, errp)) { + msix_uninit(pci_dev, &n->bar0, &n->bar0); + return false; + } + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); if (n->params.cmb_size_mb) { @@ -8165,10 +8177,6 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_pmr(n, pci_dev); } - if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { - nvme_init_sriov(n, pci_dev, 0x120); - } - return true; } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 499becd527..f0bde0d3fc 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -24,14 +24,27 @@ static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, uint16_t vf_num); static void unregister_vfs(PCIDevice *dev); -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride) + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) { uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (total_vfs) { + uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI); + uint16_t first_vf_devfn = dev->devfn + vf_offset; + uint16_t last_vf_devfn = first_vf_devfn + vf_stride * (total_vfs - 1); + + if ((!ari_cap && PCI_SLOT(dev->devfn) != PCI_SLOT(last_vf_devfn)) || + last_vf_devfn >= PCI_DEVFN_MAX) { + error_setg(errp, "VF function number overflows"); + return false; + } + } + pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; @@ -69,6 +82,8 @@ void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); qdev_prop_set_bit(&dev->qdev, "multifunction", true); + + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 450cbef6c2..aa704e8f9d 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -27,10 +27,11 @@ typedef struct PCIESriovVF { uint16_t vf_number; /* Logical VF number of this function */ } PCIESriovVF; -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride); + uint16_t vf_offset, uint16_t vf_stride, + Error **errp); void pcie_sriov_pf_exit(PCIDevice *dev); /* Set up a VF bar in the SR/IOV bar area */ From 139610ae67f6ecf92127bb7bf53ac6265b459ec8 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:55 +0900 Subject: [PATCH 1966/2884] pcie_sriov: Reuse SR-IOV VF device instances Disable SR-IOV VF devices by reusing code to power down PCI devices instead of removing them when the guest requests to disable VFs. This allows to realize devices and report VF realization errors at PF realization time. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-6-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 2 +- hw/pci/pcie_sriov.c | 95 ++++++++++++++++--------------------- include/hw/pci/pci.h | 5 -- include/hw/pci/pci_device.h | 15 ++++++ include/hw/pci/pcie_sriov.h | 1 - 5 files changed, 56 insertions(+), 62 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 68d30feb86..e32a69f3fa 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2895,7 +2895,7 @@ void pci_set_enabled(PCIDevice *d, bool state) memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_MASTER) && d->enabled); - if (!d->enabled) { + if (d->qdev.realized) { pci_device_reset(d); } } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index f0bde0d3fc..faadb0d2ea 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -20,9 +20,16 @@ #include "qapi/error.h" #include "trace.h" -static PCIDevice *register_vf(PCIDevice *pf, int devfn, - const char *name, uint16_t vf_num); -static void unregister_vfs(PCIDevice *dev); +static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) +{ + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + object_unparent(OBJECT(vf)); + object_unref(OBJECT(vf)); + } + g_free(dev->exp.sriov_pf.vf); + dev->exp.sriov_pf.vf = NULL; +} bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, @@ -30,6 +37,8 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint16_t vf_offset, uint16_t vf_stride, Error **errp) { + BusState *bus = qdev_get_parent_bus(&dev->qdev); + int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -49,7 +58,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; dev->exp.sriov_pf.num_vfs = 0; - dev->exp.sriov_pf.vfname = g_strdup(vfname); dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -83,14 +91,34 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); + dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); + + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = pci_new(devfn, vfname); + vf->exp.sriov_vf.pf = dev; + vf->exp.sriov_vf.vf_number = i; + + if (!qdev_realize(&vf->qdev, bus, errp)) { + unparent_vfs(dev, i); + return false; + } + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vf->config, 0xffff); + pci_config_set_device_id(vf->config, 0xffff); + + dev->exp.sriov_pf.vf[i] = vf; + devfn += vf_stride; + } + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) { - unregister_vfs(dev); - g_free((char *)dev->exp.sriov_pf.vfname); - dev->exp.sriov_pf.vfname = NULL; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -156,38 +184,11 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, } } -static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, - uint16_t vf_num) -{ - PCIDevice *dev = pci_new(devfn, name); - dev->exp.sriov_vf.pf = pf; - dev->exp.sriov_vf.vf_number = vf_num; - PCIBus *bus = pci_get_bus(pf); - Error *local_err = NULL; - - qdev_realize(&dev->qdev, &bus->qbus, &local_err); - if (local_err) { - error_report_err(local_err); - return NULL; - } - - /* set vid/did according to sr/iov spec - they are not used */ - pci_config_set_vendor_id(dev->config, 0xffff); - pci_config_set_device_id(dev->config, 0xffff); - - return dev; -} - static void register_vfs(PCIDevice *dev) { uint16_t num_vfs; uint16_t i; uint16_t sriov_cap = dev->exp.sriov_cap; - uint16_t vf_offset = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET); - uint16_t vf_stride = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - int32_t devfn = dev->devfn + vf_offset; assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); @@ -195,18 +196,10 @@ static void register_vfs(PCIDevice *dev) return; } - dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn, - dev->exp.sriov_pf.vfname, i); - if (!dev->exp.sriov_pf.vf[i]) { - num_vfs = i; - break; - } - devfn += vf_stride; + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } dev->exp.sriov_pf.num_vfs = num_vfs; } @@ -219,12 +212,8 @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - object_unparent(OBJECT(vf)); - object_unref(OBJECT(vf)); + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } - g_free(dev->exp.sriov_pf.vf); - dev->exp.sriov_pf.vf = NULL; dev->exp.sriov_pf.num_vfs = 0; } @@ -246,14 +235,10 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, PCI_FUNC(dev->devfn), off, val, len); if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (dev->exp.sriov_pf.num_vfs) { - if (!(val & PCI_SRIOV_CTRL_VFE)) { - unregister_vfs(dev); - } + if (val & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); } else { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); - } + unregister_vfs(dev); } } } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index fe04b4fafd..14a869eeaa 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -680,9 +680,4 @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); void pci_set_enabled(PCIDevice *pci_dev, bool state); -static inline void pci_set_power(PCIDevice *pci_dev, bool state) -{ - pci_set_enabled(pci_dev, state); -} - #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index d57f9ce838..ca15132508 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -205,6 +205,21 @@ static inline uint16_t pci_get_bdf(PCIDevice *dev) return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn); } +static inline void pci_set_power(PCIDevice *pci_dev, bool state) +{ + /* + * Don't change the enabled state of VFs when powering on/off the device. + * + * When powering on, VFs must not be enabled immediately but they must + * wait until the guest configures SR-IOV. + * When powering off, their corresponding PFs will be reset and disable + * VFs. + */ + if (!pci_is_vf(pci_dev)) { + pci_set_enabled(pci_dev, state); + } +} + uint16_t pci_requester_id(PCIDevice *dev); /* DMA access functions */ diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index aa704e8f9d..70649236c1 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -18,7 +18,6 @@ typedef struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ - const char *vfname; /* Reference to the device type used for the VFs */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; From 1a9bf009012e590cb166a4a9bae4bc18fb084d76 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:56 +0900 Subject: [PATCH 1967/2884] pcie_sriov: Release VFs failed to realize Release VFs failed to realize just as we do in unregister_vfs(). Fixes: 7c0fa8dff811 ("pcie: Add support for Single Root I/O Virtualization (SR/IOV)") Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-7-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index faadb0d2ea..9bd7f8acc3 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -99,6 +99,8 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, vf->exp.sriov_vf.vf_number = i; if (!qdev_realize(&vf->qdev, bus, errp)) { + object_unparent(OBJECT(vf)); + object_unref(vf); unparent_vfs(dev, i); return false; } From cbd9e5120bac3e292eee77b7a2e3692f235a1a26 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:57 +0900 Subject: [PATCH 1968/2884] pcie_sriov: Remove num_vfs from PCIESriovPF num_vfs is not migrated so use PCI_SRIOV_CTRL_VFE and PCI_SRIOV_NUM_VF instead. Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-8-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 28 ++++++++++++++++++++-------- hw/pci/trace-events | 2 +- include/hw/pci/pcie_sriov.h | 1 - 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 9bd7f8acc3..fae6acea4a 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -57,7 +57,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; - dev->exp.sriov_pf.num_vfs = 0; dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -186,6 +185,12 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, } } +static void clear_ctrl_vfe(PCIDevice *dev) +{ + uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL; + pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE); +} + static void register_vfs(PCIDevice *dev) { uint16_t num_vfs; @@ -195,6 +200,7 @@ static void register_vfs(PCIDevice *dev) assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { + clear_ctrl_vfe(dev); return; } @@ -203,20 +209,18 @@ static void register_vfs(PCIDevice *dev) for (i = 0; i < num_vfs; i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } - dev->exp.sriov_pf.num_vfs = num_vfs; } static void unregister_vfs(PCIDevice *dev) { - uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; uint16_t i; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } - dev->exp.sriov_pf.num_vfs = 0; } void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, @@ -242,6 +246,9 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } else { unregister_vfs(dev); } + } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { + clear_ctrl_vfe(dev); + unregister_vfs(dev); } } @@ -304,7 +311,7 @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) { assert(!pci_is_vf(dev)); - if (n < dev->exp.sriov_pf.num_vfs) { + if (n < pcie_sriov_num_vfs(dev)) { return dev->exp.sriov_pf.vf[n]; } return NULL; @@ -312,5 +319,10 @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) uint16_t pcie_sriov_num_vfs(PCIDevice *dev) { - return dev->exp.sriov_pf.num_vfs; + uint16_t sriov_cap = dev->exp.sriov_cap; + uint8_t *cfg = dev->config + sriov_cap; + + return sriov_cap && + (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ? + pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0; } diff --git a/hw/pci/trace-events b/hw/pci/trace-events index 19643aa8c6..e98f575a9d 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -14,7 +14,7 @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask # hw/pci/pcie_sriov.c sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs" -sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs" +sriov_unregister_vfs(const char *name, int slot, int function) "%s %02x:%x: Unregistering vf devs" sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d" # pcie.c diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 70649236c1..5148c5b77d 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -16,7 +16,6 @@ #include "hw/pci/pci.h" typedef struct PCIESriovPF { - uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; From 107a64b9a360cf5ca046852bc03334f7a9f22aef Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:58 +0900 Subject: [PATCH 1969/2884] pcie_sriov: Register VFs after migration pcie_sriov doesn't have code to restore its state after migration, but igb, which uses pcie_sriov, naively claimed its migration capability. Add code to register VFs after migration and fix igb migration. Fixes: 3a977deebe6b ("Intrdocue igb device emulation") Signed-off-by: Akihiko Odaki Message-Id: <20240627-reuse-v10-9-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 7 +++++++ hw/pci/pcie_sriov.c | 7 +++++++ include/hw/pci/pcie_sriov.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e32a69f3fa..fa85f87b1c 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -733,10 +733,17 @@ static bool migrate_is_not_pcie(void *opaque, int version_id) return !pci_is_express((PCIDevice *)opaque); } +static int pci_post_load(void *opaque, int version_id) +{ + pcie_sriov_pf_post_load(opaque); + return 0; +} + const VMStateDescription vmstate_pci_device = { .name = "PCIDevice", .version_id = 2, .minimum_version_id = 1, + .post_load = pci_post_load, .fields = (const VMStateField[]) { VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice, diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index fae6acea4a..56523ab4e8 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -252,6 +252,13 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } } +void pcie_sriov_pf_post_load(PCIDevice *dev) +{ + if (dev->exp.sriov_cap) { + register_vfs(dev); + } +} + /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 5148c5b77d..c5d2d318d3 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -57,6 +57,8 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize); void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, uint32_t val, int len); +void pcie_sriov_pf_post_load(PCIDevice *dev); + /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev); From 6a67577d8003428bdbeba61d32a9f8158f12624b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 27 Jun 2024 15:07:59 +0900 Subject: [PATCH 1970/2884] hw/pci: Replace -1 with UINT32_MAX for romsize romsize is an uint32_t variable. Specifying -1 as an uint32_t value is obscure way to denote UINT32_MAX. Worse, if int is wider than 32-bit, it will change the behavior of a construct like the following: romsize = -1; if (romsize != -1) { ... } When -1 is assigned to romsize, -1 will be implicitly casted into uint32_t, resulting in UINT32_MAX. On contrary, when evaluating romsize != -1, romsize will be casted into int, and it will be a comparison of UINT32_MAX and -1, and result in false. Replace -1 with UINT32_MAX for statements involving the variable to clarify the intent and prevent potential breakage. Signed-off-by: Akihiko Odaki Reviewed-by: Markus Armbruster Message-Id: <20240627-reuse-v10-10-7ca0b8ed3d9f@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 8 ++++---- hw/xen/xen_pt_load_rom.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index fa85f87b1c..4c7be52951 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -70,7 +70,7 @@ static bool pcie_has_upstream_port(PCIDevice *dev); static Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), - DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, -1), + DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), @@ -2073,7 +2073,7 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) g_cmp_uint32, NULL); } - if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) { + if (pci_dev->romsize != UINT32_MAX && !is_power_of_2(pci_dev->romsize)) { error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize); return; } @@ -2359,7 +2359,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, return; } - if (load_file || pdev->romsize == -1) { + if (load_file || pdev->romsize == UINT32_MAX) { path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); if (path == NULL) { path = g_strdup(pdev->romfile); @@ -2378,7 +2378,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, pdev->romfile); return; } - if (pdev->romsize != -1) { + if (pdev->romsize != UINT_MAX) { if (size > pdev->romsize) { error_setg(errp, "romfile \"%s\" (%u bytes) " "is too large for ROM size %u", diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index 03422a8a71..6bc64acd33 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -53,7 +53,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, } fseek(fp, 0, SEEK_SET); - if (dev->romsize != -1) { + if (dev->romsize != UINT_MAX) { if (st.st_size > dev->romsize) { error_report("ROM BAR \"%s\" (%ld bytes) is too large for ROM size %u", rom_file, (long) st.st_size, dev->romsize); From c28d8b097f6da0389d0e915e26ab210aaac38ba0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 3 Jul 2024 11:12:06 +0200 Subject: [PATCH 1971/2884] target/i386: add support for masking CPUID features in confidential guests Some CPUID features may be provided by KVM for some guests, independent of processor support, for example TSC deadline or TSC adjust. If these are not supported by the confidential computing firmware, however, the guest will fail to start. Add support for removing unsupported features from "-cpu host". Signed-off-by: Paolo Bonzini --- target/i386/confidential-guest.h | 24 ++++++++++++++++++++++++ target/i386/kvm/kvm.c | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index 532e172a60..7342d2843a 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -39,6 +39,8 @@ struct X86ConfidentialGuestClass { /* */ int (*kvm_type)(X86ConfidentialGuest *cg); + uint32_t (*mask_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, + int reg, uint32_t value); }; /** @@ -56,4 +58,26 @@ static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg) return 0; } } + +/** + * x86_confidential_guest_mask_cpuid_features: + * + * Removes unsupported features from a confidential guest's CPUID values, returns + * the value with the bits removed. The bits removed should be those that KVM + * provides independent of host-supported CPUID features, but are not supported by + * the confidential computing firmware. + */ +static inline int x86_confidential_guest_mask_cpuid_features(X86ConfidentialGuest *cg, + uint32_t feature, uint32_t index, + int reg, uint32_t value) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->mask_cpuid_features) { + return klass->mask_cpuid_features(cg, feature, index, reg, value); + } else { + return value; + } +} + #endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index dd8b0f3313..056d117cd1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -548,6 +548,11 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, ret |= 1U << KVM_HINTS_REALTIME; } + if (current_machine->cgs) { + ret = x86_confidential_guest_mask_cpuid_features( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), + function, index, reg, ret); + } return ret; } From 188569c10d5dc6996bde90ce25645083e9661ecb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 3 Jul 2024 11:16:56 +0200 Subject: [PATCH 1972/2884] target/i386/SEV: implement mask_cpuid_features Drop features that are listed as "BitMask" in the PPR and currently not supported by AMD processors. The only ones that may become useful in the future are TSC deadline timer and x2APIC, everything else is not needed for SEV-SNP guests (e.g. VIRT_SSBD) or would require processor support (e.g. TSC_ADJUST). This allows running SEV-SNP guests with "-cpu host". Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 4 ++++ target/i386/sev.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 0d5624355e..c43ac01c79 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -812,6 +812,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* Support RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE */ #define CPUID_7_0_EBX_FSGSBASE (1U << 0) +/* Support TSC adjust MSR */ +#define CPUID_7_0_EBX_TSC_ADJUST (1U << 1) /* Support SGX */ #define CPUID_7_0_EBX_SGX (1U << 2) /* 1st Group of Advanced Bit Manipulation Extensions */ @@ -1002,6 +1004,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_8000_0008_EBX_STIBP_ALWAYS_ON (1U << 17) /* Speculative Store Bypass Disable */ #define CPUID_8000_0008_EBX_AMD_SSBD (1U << 24) +/* Paravirtualized Speculative Store Bypass Disable MSR */ +#define CPUID_8000_0008_EBX_VIRT_SSBD (1U << 25) /* Predictive Store Forwarding Disable */ #define CPUID_8000_0008_EBX_AMD_PSFD (1U << 28) diff --git a/target/i386/sev.c b/target/i386/sev.c index 2f3dbe289f..2ba5f51722 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -945,6 +945,38 @@ out: return ret; } +static uint32_t +sev_snp_mask_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, + int reg, uint32_t value) +{ + switch (feature) { + case 1: + if (reg == R_ECX) { + return value & ~CPUID_EXT_TSC_DEADLINE_TIMER; + } + break; + case 7: + if (index == 0 && reg == R_EBX) { + return value & ~CPUID_7_0_EBX_TSC_ADJUST; + } + if (index == 0 && reg == R_EDX) { + return value & ~(CPUID_7_0_EDX_SPEC_CTRL | + CPUID_7_0_EDX_STIBP | + CPUID_7_0_EDX_FLUSH_L1D | + CPUID_7_0_EDX_ARCH_CAPABILITIES | + CPUID_7_0_EDX_CORE_CAPABILITY | + CPUID_7_0_EDX_SPEC_CTRL_SSBD); + } + break; + case 0x80000008: + if (reg == R_EBX) { + return value & ~CPUID_8000_0008_EBX_VIRT_SSBD; + } + break; + } + return value; +} + static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, uint8_t *addr, size_t len) @@ -2315,6 +2347,7 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) klass->launch_finish = sev_snp_launch_finish; klass->launch_update_data = sev_snp_launch_update_data; klass->kvm_init = sev_snp_kvm_init; + x86_klass->mask_cpuid_features = sev_snp_mask_cpuid_features; x86_klass->kvm_type = sev_snp_kvm_type; object_class_property_add(oc, "policy", "uint64", From 515632d5205a2abc9beb80cc70f51235fe2b47f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:08 +0100 Subject: [PATCH 1973/2884] tests/lcitool: fix debian-i686-cross toolchain prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I guess we never noticed and tried to build with this cross image. Fix the toolchain prefix so we actually build 32 bit images. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-2-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-i686-cross.docker | 2 +- tests/lcitool/refresh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index f1e5b0b877..f4ef054a2e 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -169,7 +169,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/i686-linux-gnu && \ ENV ABI "i686-linux-gnu" ENV MESON_OPTS "--cross-file=i686-linux-gnu" -ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-linux-gnu- +ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-linux-gnu- ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user # As a final step configure the user (if env is defined) ARG USER diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index b25e3ac4dd..ac803e34f1 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -167,7 +167,7 @@ try: generate_dockerfile("debian-i686-cross", "debian-11", cross="i686", - trailer=cross_build("x86_64-linux-gnu-", + trailer=cross_build("i686-linux-gnu-", "x86_64-softmmu," "x86_64-linux-user," "i386-softmmu,i386-linux-user")) From 4d17cc5a7811316d5c6ff09a33af0f1dd6a16d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:09 +0100 Subject: [PATCH 1974/2884] testing: restore some testing for i686 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit 4f9a8315e6 (gitlab-ci.d/crossbuilds: Drop the i386 system emulation job) was a little too aggressive dropping testing for 32 bit system builds. Partially revert but using the debian-i686 cross build images this time as fedora has deprecated the 32 bit stuff. As the SEV breakage gets in the way and its TCG issues we want to catch I've added --disable-kvm to the build. Reported-by: Richard Henderson Suggested-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-3-alex.bennee@linaro.org> --- .gitlab-ci.d/crossbuilds.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 47bdb99b5b..3de0341afe 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -37,6 +37,17 @@ cross-arm64-kvm-only: IMAGE: debian-arm64-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features +cross-i686-system: + extends: + - .cross_system_build_job + - .cross_test_artifacts + needs: + job: i686-debian-cross-container + variables: + IMAGE: debian-i686-cross + EXTRA_CONFIGURE_OPTS: --disable-kvm + MAKE_CHECK_ARGS: check-qtest + cross-i686-user: extends: - .cross_user_build_job From d44fe13b2b8631956b4e4ea72c5b39d8c9f194e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:10 +0100 Subject: [PATCH 1975/2884] tracepoints: move physmem trace points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They don't need to be in the global trace-events file and can have a local trace header. Also add address_space_map tracepoint for tracking mapping behaviour. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-4-alex.bennee@linaro.org> --- system/physmem.c | 4 +++- system/trace-events | 6 ++++++ trace-events | 5 ----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 261196cde0..14aa025d41 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -53,7 +53,7 @@ #include "sysemu/hostmem.h" #include "sysemu/hw_accel.h" #include "sysemu/xen-mapcache.h" -#include "trace/trace-root.h" +#include "trace.h" #ifdef CONFIG_FALLOCATE_PUNCH_HOLE #include @@ -3193,6 +3193,8 @@ void *address_space_map(AddressSpace *as, MemoryRegion *mr; FlatView *fv; + trace_address_space_map(as, addr, len, is_write, *(uint32_t *) &attrs); + if (len == 0) { return NULL; } diff --git a/system/trace-events b/system/trace-events index 69c9044151..2ed1d59b1f 100644 --- a/system/trace-events +++ b/system/trace-events @@ -21,6 +21,12 @@ flatview_destroy(void *view, void *root) "%p (root %p)" flatview_destroy_rcu(void *view, void *root) "%p (root %p)" global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32 +# physmem.c +address_space_map(void *as, uint64_t addr, uint64_t len, bool is_write, uint32_t attrs) "as:%p addr 0x%"PRIx64":%"PRIx64" write:%d attrs:0x%x" +find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64 +find_ram_offset_loop(uint64_t size, uint64_t candidate, uint64_t offset, uint64_t next, uint64_t mingap) "trying size: 0x%" PRIx64 " @ 0x%" PRIx64 ", offset: 0x%" PRIx64" next: 0x%" PRIx64 " mingap: 0x%" PRIx64 +ram_block_discard_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d" + # cpus.c vm_stop_flush_all(int ret) "ret %d" diff --git a/trace-events b/trace-events index dd318ed1af..9cb96f64c4 100644 --- a/trace-events +++ b/trace-events @@ -37,11 +37,6 @@ dma_complete(void *dbs, int ret, void *cb) "dbs=%p ret=%d cb=%p" dma_blk_cb(void *dbs, int ret) "dbs=%p ret=%d" dma_map_wait(void *dbs) "dbs=%p" -# exec.c -find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64 -find_ram_offset_loop(uint64_t size, uint64_t candidate, uint64_t offset, uint64_t next, uint64_t mingap) "trying size: 0x%" PRIx64 " @ 0x%" PRIx64 ", offset: 0x%" PRIx64" next: 0x%" PRIx64 " mingap: 0x%" PRIx64 -ram_block_discard_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d" - # job.c job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" From f5fcc648083e71cfb9394894903f8ea108ff8831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:11 +0100 Subject: [PATCH 1976/2884] hw/core: ensure kernel_end never gets used undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Really the problem here is the return values of fit_load_[kernel|fdt]() are a little all over the place. However we don't want to somehow get through not having set kernel_end and having it just be random unused data. The compiler complained on an --enable-gcov build: In file included from ../../hw/core/loader-fit.c:20: /home/alex/lsrc/qemu.git/include/qemu/osdep.h: In function ‘load_fit’: /home/alex/lsrc/qemu.git/include/qemu/osdep.h:486:45: error: ‘kernel_end’ may be used uninitialized [-Werror=maybe-uninitialized] 486 | #define ROUND_UP(n, d) ROUND_DOWN((n) + (d) - 1, (d)) | ^ ../../hw/core/loader-fit.c:270:12: note: ‘kernel_end’ was declared here 270 | hwaddr kernel_end; | ^~~~~~~~~~ Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Reviewed-by: Aleksandar Rikalo Message-Id: <20240705084047.857176-5-alex.bennee@linaro.org> --- hw/core/loader-fit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 9f20007dbb..7ccc9d5fbc 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -267,7 +267,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) const char *def_cfg_name; char path[FIT_LOADER_MAX_PATH]; int itb_size, configs, cfg_off, off; - hwaddr kernel_end; + hwaddr kernel_end = 0; int ret; itb = load_device_tree(filename, &itb_size); From aa8246d8b4f81d5a552837c825fa4c72c55e1de9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 5 Jul 2024 09:40:12 +0100 Subject: [PATCH 1977/2884] tests/docker: Specify --userns keep-id for Podman MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously we are always specifying -u $(UID) to match the UID in the container with one outside. This causes a problem with rootless Podman. Rootless Podman remaps user IDs in the container to ones controllable for the current user outside. The -u option instructs Podman to use a specified UID in the container but does not affect the UID remapping. Therefore, the UID in the container can be remapped to some other UID outside the container. This can make the access to bind-mounted volumes fail because the remapped UID mismatches with the owner of the directories. Replace -u $(UID) with --userns keep-id, which fixes the UID remapping. This change is limited to Podman because Docker does not support --userns keep-id. Signed-off-by: Akihiko Odaki Message-Id: <20240626-podman-v1-1-f8c8daf2bb0a@daynix.com> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-6-alex.bennee@linaro.org> --- tests/docker/Makefile.include | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8df50a0ca0..708e3a72fb 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -207,7 +207,12 @@ docker-run: docker-qemu-src $(call quiet-command, \ $(RUNC) run \ --rm \ - $(if $(NOUSER),,-u $(UID)) \ + $(if $(NOUSER),, \ + $(if $(filter docker,$(RUNC)), \ + -u $(UID), \ + --userns keep-id \ + ) \ + ) \ --security-opt seccomp=unconfined \ $(if $(DEBUG),-ti,) \ $(if $(NETWORK),$(if $(subst $(NETWORK),,1),--net=$(NETWORK)),--net=none) \ From 7e3798eddde9bcbf3e83ddcd236eeca4c413f34f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:13 +0100 Subject: [PATCH 1978/2884] tests/tcg/minilib: Constify digits in print_num MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids a memcpy to the stack when compiled with clang. Since we don't enable optimization, nor provide memcpy, this results in an undefined symbol error at link time. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-2-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-7-alex.bennee@linaro.org> --- tests/tcg/minilib/printf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/minilib/printf.c b/tests/tcg/minilib/printf.c index 10472b4f58..fb0189c2bb 100644 --- a/tests/tcg/minilib/printf.c +++ b/tests/tcg/minilib/printf.c @@ -27,7 +27,7 @@ static void print_str(char *s) static void print_num(unsigned long long value, int base) { - char digits[] = "0123456789abcdef"; + static const char digits[] = "0123456789abcdef"; char buf[32]; int i = sizeof(buf) - 2, j; From 546215d38aea068d4761dc41715161fcc2d127d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:14 +0100 Subject: [PATCH 1979/2884] tests/tcg: Adjust variable defintion from cc-option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the variable to the compiler flag used, not "y". This avoids replication of the compiler flag itself. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-3-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-8-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 2 +- tests/tcg/aarch64/Makefile.softmmu-target | 2 +- tests/tcg/aarch64/Makefile.target | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index f21be50d3b..cb8cfeb6da 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -49,7 +49,7 @@ quiet-command = $(call quiet-@,$2,$3)$1 cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>&1 cc-option = if $(call cc-test, $1); then \ - echo "$(TARGET_PREFIX)$1 detected" && echo "$(strip $2)=y" >&3; else \ + echo "$(TARGET_PREFIX)$1 detected" && echo "$(strip $2)=$(strip $1)" >&3; else \ echo "$(TARGET_PREFIX)$1 not detected"; fi # $1 = test name, $2 = cmd, $3 = desc diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 4b03ef602e..39d3f961c5 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -81,7 +81,7 @@ run-memory-replay: memory-replay run-memory-record EXTRA_RUNS+=run-memory-replay ifneq ($(CROSS_CC_HAS_ARMV8_3),) -pauth-3: CFLAGS += -march=armv8.3-a +pauth-3: CFLAGS += $(CROSS_CC_HAS_ARMV8_3) else pauth-3: $(call skip-test, "BUILD of $@", "missing compiler support") diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 4ecbca6a41..11ccde5579 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -32,17 +32,17 @@ config-cc.mak: Makefile ifneq ($(CROSS_CC_HAS_ARMV8_2),) AARCH64_TESTS += dcpop -dcpop: CFLAGS += -march=armv8.2-a +dcpop: CFLAGS += $(CROSS_CC_HAS_ARMV8_2) endif ifneq ($(CROSS_CC_HAS_ARMV8_5),) AARCH64_TESTS += dcpodp -dcpodp: CFLAGS += -march=armv8.5-a +dcpodp: CFLAGS += $(CROSS_CC_HAS_ARMV8_5) endif # Pauth Tests ifneq ($(CROSS_CC_HAS_ARMV8_3),) AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 test-2375 -pauth-%: CFLAGS += -march=armv8.3-a +pauth-%: CFLAGS += $(CROSS_CC_HAS_ARMV8_3) test-2375: CFLAGS += -march=armv8.3-a run-pauth-1: QEMU_OPTS += -cpu max run-pauth-2: QEMU_OPTS += -cpu max @@ -55,7 +55,7 @@ endif # bti-1 tests the elf notes, so we require special compiler support. ifneq ($(CROSS_CC_HAS_ARMV8_BTI),) AARCH64_TESTS += bti-1 bti-3 -bti-1 bti-3: CFLAGS += -fno-stack-protector -mbranch-protection=standard +bti-1 bti-3: CFLAGS += -fno-stack-protector $(CROSS_CC_HAS_ARMV8_BTI) bti-1 bti-3: LDFLAGS += -nostdlib endif # bti-2 tests PROT_BTI, so no special compiler support required. @@ -64,12 +64,13 @@ AARCH64_TESTS += bti-2 # MTE Tests ifneq ($(CROSS_CC_HAS_ARMV8_MTE),) AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 -mte-%: CFLAGS += -march=armv8.5-a+memtag +mte-%: CFLAGS += $(CROSS_CC_HAS_ARMV8_MTE) endif # SME Tests ifneq ($(CROSS_AS_HAS_ARMV9_SME),) AARCH64_TESTS += sme-outprod1 sme-smopa-1 sme-smopa-2 +sme-outprod1 sme-smopa-1 sme-smopa-2: CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif # System Registers Tests @@ -99,7 +100,7 @@ TESTS += sha512-vector ifneq ($(CROSS_CC_HAS_SVE),) # SVE ioctl test AARCH64_TESTS += sve-ioctls -sve-ioctls: CFLAGS+=-march=armv8.1-a+sve +sve-ioctls: CFLAGS += $(CROSS_CC_HAS_SVE) sha512-sve: CFLAGS=-O3 -march=armv8.1-a+sve sha512-sve: sha512.c @@ -134,7 +135,7 @@ endif ifneq ($(CROSS_CC_HAS_SVE2),) AARCH64_TESTS += test-826 -test-826: CFLAGS+=-march=armv8.1-a+sve2 +test-826: CFLAGS += $(CROSS_CC_HAS_SVE2) endif TESTS += $(AARCH64_TESTS) From e35562b3a506437a084c58fbddab8a56b0c7c90e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:15 +0100 Subject: [PATCH 1980/2884] tests/tcg/aarch64: Drop -fno-tree-loop-distribute-patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option is not supported by clang, and is not required in order to get sve code generation with gcc 12. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-4-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-9-alex.bennee@linaro.org> --- tests/tcg/aarch64/Makefile.softmmu-target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 39d3f961c5..dd6d595830 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -39,7 +39,7 @@ memory: CFLAGS+=-DCHECK_UNALIGNED=1 memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3 -fno-tree-loop-distribute-patterns +memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3 TESTS+=memory-sve From 79e73b5df0a7b94efe7875a80ffb55b34eeb7ecc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 5 Jul 2024 09:40:16 +0100 Subject: [PATCH 1981/2884] tests/tcg/aarch64: Explicitly specify register width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clang version 18.1.6 assumes a register is 64-bit by default and complains if a 32-bit value is given. Explicitly specify register width when passing a 32-bit value. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240627-tcg-v2-3-1690a813348e@daynix.com> Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-5-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-10-alex.bennee@linaro.org> --- tests/tcg/aarch64/bti-1.c | 6 +++--- tests/tcg/aarch64/bti-3.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/tcg/aarch64/bti-1.c b/tests/tcg/aarch64/bti-1.c index 99a879af23..1fada8108d 100644 --- a/tests/tcg/aarch64/bti-1.c +++ b/tests/tcg/aarch64/bti-1.c @@ -17,15 +17,15 @@ static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) #define BTI_JC "hint #38" #define BTYPE_1(DEST) \ - asm("mov %0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %w0,#0" \ : "=r"(skipped) : : "x16") #define BTYPE_2(DEST) \ - asm("mov %0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %w0,#0" \ : "=r"(skipped) : : "x16", "x30") #define BTYPE_3(DEST) \ - asm("mov %0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %0,#0" \ + asm("mov %w0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %w0,#0" \ : "=r"(skipped) : : "x15") #define TEST(WHICH, DEST, EXPECT) \ diff --git a/tests/tcg/aarch64/bti-3.c b/tests/tcg/aarch64/bti-3.c index 8c534c09d7..6a3bd037bc 100644 --- a/tests/tcg/aarch64/bti-3.c +++ b/tests/tcg/aarch64/bti-3.c @@ -11,15 +11,15 @@ static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) } #define BTYPE_1() \ - asm("mov %0,#1; adr x16, 1f; br x16; 1: hint #25; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; br x16; 1: hint #25; mov %w0,#0" \ : "=r"(skipped) : : "x16", "x30") #define BTYPE_2() \ - asm("mov %0,#1; adr x16, 1f; blr x16; 1: hint #25; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; blr x16; 1: hint #25; mov %w0,#0" \ : "=r"(skipped) : : "x16", "x30") #define BTYPE_3() \ - asm("mov %0,#1; adr x15, 1f; br x15; 1: hint #25; mov %0,#0" \ + asm("mov %w0,#1; adr x15, 1f; br x15; 1: hint #25; mov %w0,#0" \ : "=r"(skipped) : : "x15", "x30") #define TEST(WHICH, EXPECT) \ From 69375de17bd345f85defc54dae53bc0440e3fc18 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 5 Jul 2024 09:40:17 +0100 Subject: [PATCH 1982/2884] tests/tcg/aarch64: Fix irg operand type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit irg expects 64-bit integers. Passing a 32-bit integer results in compilation failure with clang version 18.1.6. Signed-off-by: Akihiko Odaki Message-Id: <20240627-tcg-v2-4-1690a813348e@daynix.com> Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-6-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-11-alex.bennee@linaro.org> --- tests/tcg/aarch64/mte-1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/aarch64/mte-1.c b/tests/tcg/aarch64/mte-1.c index 88dcd617ad..146cad4a04 100644 --- a/tests/tcg/aarch64/mte-1.c +++ b/tests/tcg/aarch64/mte-1.c @@ -15,7 +15,7 @@ int main(int ac, char **av) enable_mte(PR_MTE_TCF_NONE); p0 = alloc_mte_mem(sizeof(*p0)); - asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(1)); + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(1l)); assert(p1 != p0); asm("subp %0,%1,%2" : "=r"(c) : "r"(p0), "r"(p1)); assert(c == 0); From 98b323fa656fe3ae4304d4803705fe4e6f3abc72 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 5 Jul 2024 09:40:18 +0100 Subject: [PATCH 1983/2884] tests/tcg/aarch64: Do not use x constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clang version 18.1.6 does not support x constraint for AArch64. Use w instead. Signed-off-by: Akihiko Odaki Message-Id: <20240627-tcg-v2-5-1690a813348e@daynix.com> Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-7-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-12-alex.bennee@linaro.org> --- tests/tcg/arm/fcvt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c index 7ac47b564e..f631197287 100644 --- a/tests/tcg/arm/fcvt.c +++ b/tests/tcg/arm/fcvt.c @@ -126,7 +126,7 @@ static void convert_single_to_half(void) asm("vcvtb.f16.f32 %0, %1" : "=t" (output) : "x" (input)); #else uint16_t output; - asm("fcvt %h0, %s1" : "=w" (output) : "x" (input)); + asm("fcvt %h0, %s1" : "=w" (output) : "w" (input)); #endif print_half_number(i, output); } @@ -149,7 +149,7 @@ static void convert_single_to_double(void) #if defined(__arm__) asm("vcvt.f64.f32 %P0, %1" : "=w" (output) : "t" (input)); #else - asm("fcvt %d0, %s1" : "=w" (output) : "x" (input)); + asm("fcvt %d0, %s1" : "=w" (output) : "w" (input)); #endif print_double_number(i, output); } @@ -244,7 +244,7 @@ static void convert_double_to_half(void) /* asm("vcvtb.f16.f64 %0, %P1" : "=t" (output) : "x" (input)); */ output = input; #else - asm("fcvt %h0, %d1" : "=w" (output) : "x" (input)); + asm("fcvt %h0, %d1" : "=w" (output) : "w" (input)); #endif print_half_number(i, output); } @@ -267,7 +267,7 @@ static void convert_double_to_single(void) #if defined(__arm__) asm("vcvt.f32.f64 %0, %P1" : "=w" (output) : "x" (input)); #else - asm("fcvt %s0, %d1" : "=w" (output) : "x" (input)); + asm("fcvt %s0, %d1" : "=w" (output) : "w" (input)); #endif print_single_number(i, output); @@ -335,7 +335,7 @@ static void convert_half_to_double(void) /* asm("vcvtb.f64.f16 %P0, %1" : "=w" (output) : "t" (input)); */ output = input; #else - asm("fcvt %d0, %h1" : "=w" (output) : "x" (input)); + asm("fcvt %d0, %h1" : "=w" (output) : "w" (input)); #endif print_double_number(i, output); } @@ -357,7 +357,7 @@ static void convert_half_to_single(void) #if defined(__arm__) asm("vcvtb.f32.f16 %0, %1" : "=w" (output) : "x" ((uint32_t)input)); #else - asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); + asm("fcvt %s0, %h1" : "=w" (output) : "w" (input)); #endif print_single_number(i, output); } @@ -380,7 +380,7 @@ static void convert_half_to_integer(void) /* asm("vcvt.s32.f16 %0, %1" : "=t" (output) : "t" (input)); v8.2*/ output = input; #else - asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); + asm("fcvt %s0, %h1" : "=w" (output) : "w" (input)); #endif print_int64(i, output); } From b04bed529ec2bcd8f17fb127e700639bf2da9ac7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:19 +0100 Subject: [PATCH 1984/2884] tests/tcg/aarch64: Add -fno-integrated-as for sme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only use of SME is inline assembly. Both gcc and clang only support SME with very recent releases; by deferring detection to the assembler we get better test coverage. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-8-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-13-alex.bennee@linaro.org> --- tests/tcg/aarch64/Makefile.target | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 11ccde5579..ad1774c2ce 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -20,6 +20,7 @@ run-fcvt: fcvt config-cc.mak: Makefile $(quiet-@)( \ + fnia=`$(call cc-test,-fno-integrated-as) && echo -fno-integrated-as`; \ $(call cc-option,-march=armv8.1-a+sve, CROSS_CC_HAS_SVE); \ $(call cc-option,-march=armv8.1-a+sve2, CROSS_CC_HAS_SVE2); \ $(call cc-option,-march=armv8.2-a, CROSS_CC_HAS_ARMV8_2); \ @@ -27,7 +28,7 @@ config-cc.mak: Makefile $(call cc-option,-march=armv8.5-a, CROSS_CC_HAS_ARMV8_5); \ $(call cc-option,-mbranch-protection=standard, CROSS_CC_HAS_ARMV8_BTI); \ $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE); \ - $(call cc-option,-Wa$(COMMA)-march=armv9-a+sme, CROSS_AS_HAS_ARMV9_SME)) 3> config-cc.mak + $(call cc-option,-Wa$(COMMA)-march=armv9-a+sme $$fnia, CROSS_AS_HAS_ARMV9_SME)) 3> config-cc.mak -include config-cc.mak ifneq ($(CROSS_CC_HAS_ARMV8_2),) From 3693408c5ebc21008ce0ba7bc03c8a80fbb432ad Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 5 Jul 2024 09:40:20 +0100 Subject: [PATCH 1985/2884] tests/tcg/arm: Fix fcvt result messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test cases for "converting double-precision to single-precision" emits float but the result variable was typed as uint32_t and corrupted the printed values. Propertly type it as float. Signed-off-by: Akihiko Odaki Fixes: 8ec8a55e3fc9 ("tests/tcg/arm: add fcvt test cases for AArch32/64") Message-Id: <20240627-tcg-v2-1-1690a813348e@daynix.com> [rth: Update arm ref file as well] Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-9-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-14-alex.bennee@linaro.org> --- tests/tcg/aarch64/fcvt.ref | 604 ++++++++++++++++++------------------- tests/tcg/arm/fcvt.c | 2 +- tests/tcg/arm/fcvt.ref | 604 ++++++++++++++++++------------------- 3 files changed, 605 insertions(+), 605 deletions(-) diff --git a/tests/tcg/aarch64/fcvt.ref b/tests/tcg/aarch64/fcvt.ref index e7af24dc58..2726b41063 100644 --- a/tests/tcg/aarch64/fcvt.ref +++ b/tests/tcg/aarch64/fcvt.ref @@ -211,45 +211,45 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -257,41 +257,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -574,87 +574,87 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -937,45 +937,45 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -983,41 +983,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1300,45 +1300,45 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1346,41 +1346,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1845,45 +1845,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1891,41 +1891,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2208,87 +2208,87 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2571,45 +2571,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2617,41 +2617,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2934,45 +2934,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2980,41 +2980,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c index f631197287..157790e679 100644 --- a/tests/tcg/arm/fcvt.c +++ b/tests/tcg/arm/fcvt.c @@ -258,7 +258,7 @@ static void convert_double_to_single(void) for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { double input = double_numbers[i].d; - uint32_t output; + float output; feclearexcept(FE_ALL_EXCEPT); diff --git a/tests/tcg/arm/fcvt.ref b/tests/tcg/arm/fcvt.ref index f052b6d7e5..8e007c3345 100644 --- a/tests/tcg/arm/fcvt.ref +++ b/tests/tcg/arm/fcvt.ref @@ -211,45 +211,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -257,41 +257,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -574,87 +574,87 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -937,45 +937,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -983,41 +983,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1300,45 +1300,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1346,41 +1346,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1845,45 +1845,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1891,41 +1891,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2208,87 +2208,87 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2571,45 +2571,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2617,41 +2617,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2934,45 +2934,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2980,41 +2980,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) From 8807477c36bc304259b370d3d62c014ee9a0e5ad Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:21 +0100 Subject: [PATCH 1986/2884] tests/tcg/arm: Drop -N from LDFLAGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is redudant with a linker script, and is not supported by clang. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-10-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-15-alex.bennee@linaro.org> --- tests/tcg/arm/Makefile.softmmu-target | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target index 39e01ce49d..547063c08c 100644 --- a/tests/tcg/arm/Makefile.softmmu-target +++ b/tests/tcg/arm/Makefile.softmmu-target @@ -13,7 +13,7 @@ VPATH += $(ARM_SRC) test-armv6m-undef: test-armv6m-undef.S $(CC) -mcpu=cortex-m0 -mfloat-abi=soft \ -Wl,--build-id=none -x assembler-with-cpp \ - $< -o $@ -nostdlib -N -static \ + $< -o $@ -nostdlib -static \ -T $(ARM_SRC)/$@.ld run-test-armv6m-undef: QEMU_OPTS=-semihosting-config enable=on,target=native,chardev=output -M microbit -kernel @@ -30,7 +30,7 @@ CRT_PATH=$(ARM_SRC) LINK_SCRIPT=$(ARM_SRC)/kernel.ld LDFLAGS=-Wl,-T$(LINK_SCRIPT) CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) -LDFLAGS+=-static -nostdlib -N $(CRT_OBJS) $(MINILIB_OBJS) -lgcc +LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc # building head blobs .PRECIOUS: $(CRT_OBJS) From 40126a16bddb85fb2f6300be5a919789ccf4d66f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:22 +0100 Subject: [PATCH 1987/2884] tests/tcg/arm: Use -fno-integrated-as for test-arm-iwmmxt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang does not support IWMXT instructions. Fall back to the external assembler. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-11-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-16-alex.bennee@linaro.org> --- tests/tcg/arm/Makefile.target | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 0a1965fce7..95f891bf8c 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -8,6 +8,11 @@ ARM_SRC=$(SRC_PATH)/tests/tcg/arm # Set search path for all sources VPATH += $(ARM_SRC) +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-fno-integrated-as, CROSS_CC_HAS_FNIA)) 3> config-cc.mak +-include config-cc.mak + float_madds: CFLAGS+=-mfpu=neon-vfpv4 # Basic Hello World @@ -17,7 +22,8 @@ hello-arm: LDFLAGS+=-nostdlib # IWMXT floating point extensions ARM_TESTS += test-arm-iwmmxt -test-arm-iwmmxt: CFLAGS+=-marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 +# Clang assembler does not support IWMXT, so use the external assembler. +test-arm-iwmmxt: CFLAGS += -marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 $(CROSS_CC_HAS_FNIA) test-arm-iwmmxt: test-arm-iwmmxt.S $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) From 1e7c9ba4a24d97dfeb59de2c0ac158c749945abc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 5 Jul 2024 09:40:23 +0100 Subject: [PATCH 1988/2884] tests/tcg/arm: Manually register allocate half-precision numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang does not allow specifying an integer as the value of a single precision register. Explicitly move value from a general register. Signed-off-by: Akihiko Odaki [rth: Use one single inline asm block.] Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-12-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-17-alex.bennee@linaro.org> --- tests/tcg/arm/fcvt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c index 157790e679..d8c61cd29f 100644 --- a/tests/tcg/arm/fcvt.c +++ b/tests/tcg/arm/fcvt.c @@ -355,7 +355,12 @@ static void convert_half_to_single(void) print_half_number(i, input); #if defined(__arm__) - asm("vcvtb.f32.f16 %0, %1" : "=w" (output) : "x" ((uint32_t)input)); + /* + * Clang refuses to allocate an integer to a fp register. + * Perform the move from a general register by hand. + */ + asm("vmov %0, %1\n\t" + "vcvtb.f32.f16 %0, %0" : "=w" (output) : "r" (input)); #else asm("fcvt %s0, %h1" : "=w" (output) : "w" (input)); #endif From e3693cd3c9fe868e6b87d0677ad9740b56937d34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:24 +0100 Subject: [PATCH 1989/2884] tests/tcg/arm: Use -march and -mfpu for fcvt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang requires the architecture to be set properly in order to assemble the half-precision instructions. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-13-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-18-alex.bennee@linaro.org> --- tests/tcg/arm/Makefile.target | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 95f891bf8c..8e287191af 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -29,8 +29,8 @@ test-arm-iwmmxt: test-arm-iwmmxt.S # Float-convert Tests ARM_TESTS += fcvt -fcvt: LDFLAGS+=-lm -# fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 +fcvt: LDFLAGS += -lm +fcvt: CFLAGS += -march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 run-fcvt: fcvt $(call run-test,fcvt,$(QEMU) $<) $(call diff-out,fcvt,$(ARM_SRC)/fcvt.ref) From 2f93ff3113d60635328fdeaec5d1fece6ceade5a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:25 +0100 Subject: [PATCH 1990/2884] tests/tcg/arm: Use vmrs/vmsr instead of mcr/mrc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang 14 generates /home/rth/qemu/src/tests/tcg/arm/fcvt.c:431:9: error: invalid operand for instruction asm("mrc p10, 7, r1, cr1, cr0, 0\n\t" ^ :1:6: note: instantiated into assembly here mrc p10, 7, r1, cr1, cr0, 0 ^ /home/rth/qemu/src/tests/tcg/arm/fcvt.c:432:32: error: invalid operand for instruction "orr r1, r1, %[flags]\n\t" ^ :3:6: note: instantiated into assembly here mcr p10, 7, r1, cr1, cr0, 0 ^ This is perhaps a clang bug, but using the neon mnemonic is clearer. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-14-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-19-alex.bennee@linaro.org> --- tests/tcg/arm/fcvt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c index d8c61cd29f..ecebbb0247 100644 --- a/tests/tcg/arm/fcvt.c +++ b/tests/tcg/arm/fcvt.c @@ -427,10 +427,9 @@ int main(int argc, char *argv[argc]) /* And now with ARM alternative FP16 */ #if defined(__arm__) - /* See glibc sysdeps/arm/fpu_control.h */ - asm("mrc p10, 7, r1, cr1, cr0, 0\n\t" + asm("vmrs r1, fpscr\n\t" "orr r1, r1, %[flags]\n\t" - "mcr p10, 7, r1, cr1, cr0, 0\n\t" + "vmsr fpscr, r1" : /* no output */ : [flags] "n" (1 << 26) : "r1" ); #else asm("mrs x1, fpcr\n\t" From f324a03ee23177e41509786bf09c9e7263989ff8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 5 Jul 2024 09:40:26 +0100 Subject: [PATCH 1991/2884] linux-user/main: Suppress out-of-range comparison warning for clang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For arm32 host and arm64 guest we get .../main.c:851:32: error: result of comparison of constant 70368744177664 with expression of type 'unsigned long' is always false [-Werror,-Wtautological-constant-out-of-range-compare] if (TASK_UNMAPPED_BASE < reserved_va) { ~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~ We already disable -Wtype-limits here, for this exact comparison, but that is not enough for clang. Disable -Wtautological-compare as well, which is a superset. GCC ignores the unknown warning flag. Signed-off-by: Richard Henderson Reviewed-by: Akihiko Odaki Message-Id: <20240630190050.160642-15-richard.henderson@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-20-alex.bennee@linaro.org> --- linux-user/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/main.c b/linux-user/main.c index 94c99a1366..7d3cf45fa9 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -843,6 +843,7 @@ int main(int argc, char **argv, char **envp) */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wtautological-compare" /* * Select an initial value for task_unmapped_base that is in range. From a4ad4db484c15ac3ed21d6700a3aa9bdc7eed719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:27 +0100 Subject: [PATCH 1992/2884] gitlab: don't bother with KVM for TCI builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In fact any other accelerator would be pointless as the point is to exercise the TCI accelerator anyway. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-21-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 2 +- .gitlab-ci.d/crossbuilds.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 425fc6479b..e3a0758bd9 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -635,7 +635,7 @@ build-tci: - TARGETS="aarch64 arm hppa m68k microblaze ppc64 s390x x86_64" - mkdir build - cd build - - ../configure --enable-tcg-interpreter --disable-docs --disable-gtk --disable-vnc + - ../configure --enable-tcg-interpreter --disable-kvm --disable-docs --disable-gtk --disable-vnc --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; } - make -j"$JOBS" diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 3de0341afe..cb499e4ee0 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -68,7 +68,7 @@ cross-i686-tci: variables: IMAGE: debian-i686-cross ACCEL: tcg-interpreter - EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user --disable-plugins + EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user --disable-plugins --disable-kvm MAKE_CHECK_ARGS: check check-tcg cross-mipsel-system: From 03295660c854119646baa2e429aac9f2d1fb8a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:28 +0100 Subject: [PATCH 1993/2884] test/plugin: make insn plugin less noisy by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the match functionality is useful lets make the verbosity optional while we are actually running. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-22-alex.bennee@linaro.org> --- tests/plugin/insn.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index 5e0aa03223..524f9ddde8 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -20,6 +20,7 @@ static qemu_plugin_u64 insn_count; static bool do_inline; static bool do_size; +static bool do_trace; static GArray *sizes; typedef struct { @@ -73,30 +74,30 @@ static void vcpu_insn_matched_exec_before(unsigned int cpu_index, void *udata) MatchCount *match = qemu_plugin_scoreboard_find(insn_match->counts, cpu_index); - g_autoptr(GString) ts = g_string_new(""); - insn->hits++; - g_string_append_printf(ts, "0x%" PRIx64 ", '%s', %"PRId64 " hits", - insn->vaddr, insn->disas, insn->hits); uint64_t icount = qemu_plugin_u64_get(insn_count, cpu_index); uint64_t delta = icount - match->last_hit; match->hits++; match->total_delta += delta; - - g_string_append_printf(ts, - " , cpu %u," - " %"PRId64" match hits," - " Δ+%"PRId64 " since last match," - " %"PRId64 " avg insns/match\n", - cpu_index, - match->hits, delta, - match->total_delta / match->hits); - match->last_hit = icount; - qemu_plugin_outs(ts->str); + if (do_trace) { + g_autoptr(GString) ts = g_string_new(""); + g_string_append_printf(ts, "0x%" PRIx64 ", '%s', %"PRId64 " hits", + insn->vaddr, insn->disas, insn->hits); + g_string_append_printf(ts, + " , cpu %u," + " %"PRId64" match hits," + " Δ+%"PRId64 " since last match," + " %"PRId64 " avg insns/match\n", + cpu_index, + match->hits, delta, + match->total_delta / match->hits); + + qemu_plugin_outs(ts->str); + } } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -216,6 +217,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } else if (g_strcmp0(tokens[0], "match") == 0) { parse_match(tokens[1]); + } else if (g_strcmp0(tokens[0], "trace") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_trace)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; From ee3878bda1057e3e693ddbc5de657ccc6bcca2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:29 +0100 Subject: [PATCH 1994/2884] test/plugins: preserve the instruction record over translations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are interested in the particular instruction so we should use a stable record for it. We could bring this down to physical address but for now vaddr + disas seems to do the trick. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-23-alex.bennee@linaro.org> --- tests/plugin/insn.c | 76 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index 524f9ddde8..baf2d07205 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -43,6 +43,44 @@ typedef struct { char *disas; } Instruction; +/* A hash table to hold matched instructions */ +static GHashTable *match_insn_records; +static GMutex match_hash_lock; + + +static Instruction * get_insn_record(const char *disas, uint64_t vaddr, Match *m) +{ + g_autofree char *str_hash = g_strdup_printf("%"PRIx64" %s", vaddr, disas); + Instruction *record; + + g_mutex_lock(&match_hash_lock); + + if (!match_insn_records) { + match_insn_records = g_hash_table_new(g_str_hash, g_str_equal); + } + + record = g_hash_table_lookup(match_insn_records, str_hash); + + if (!record) { + g_autoptr(GString) ts = g_string_new(str_hash); + + record = g_new0(Instruction, 1); + record->disas = g_strdup(disas); + record->vaddr = vaddr; + record->match = m; + + g_hash_table_insert(match_insn_records, str_hash, record); + + g_string_prepend(ts, "Created record for: "); + g_string_append(ts, "\n"); + qemu_plugin_outs(ts->str); + } + + g_mutex_unlock(&match_hash_lock); + + return record; +} + /* * Initialise a new vcpu with reading the register list */ @@ -131,16 +169,19 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * If we are tracking certain instructions we will need more * information about the instruction which we also need to * save if there is a hit. + * + * We only want one record for each occurrence of the matched + * instruction. */ if (matches->len) { char *insn_disas = qemu_plugin_insn_disas(insn); for (int j = 0; j < matches->len; j++) { Match *m = &g_array_index(matches, Match, j); if (g_str_has_prefix(insn_disas, m->match_string)) { - Instruction *rec = g_new0(Instruction, 1); - rec->disas = g_strdup(insn_disas); - rec->vaddr = qemu_plugin_insn_vaddr(insn); - rec->match = m; + Instruction *rec = get_insn_record(insn_disas, + qemu_plugin_insn_vaddr(insn), + m); + qemu_plugin_register_vcpu_insn_exec_cb( insn, vcpu_insn_matched_exec_before, QEMU_PLUGIN_CB_NO_REGS, rec); @@ -173,13 +214,38 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) qemu_plugin_u64_sum(insn_count)); } qemu_plugin_outs(out->str); - qemu_plugin_scoreboard_free(insn_count.score); + + g_mutex_lock(&match_hash_lock); + for (i = 0; i < matches->len; ++i) { Match *m = &g_array_index(matches, Match, i); + GHashTableIter iter; + Instruction *record; + qemu_plugin_u64 hit_e = qemu_plugin_scoreboard_u64_in_struct(m->counts, MatchCount, hits); + uint64_t hits = qemu_plugin_u64_sum(hit_e); + + g_string_printf(out, "Match: %s, hits %"PRId64"\n", m->match_string, hits); + qemu_plugin_outs(out->str); + + g_hash_table_iter_init(&iter, match_insn_records); + while (g_hash_table_iter_next(&iter, NULL, (void **)&record)) { + if (record->match == m) { + g_string_printf(out, + " %"PRIx64": %s (hits %"PRId64")\n", + record->vaddr, + record->disas, + record->hits); + qemu_plugin_outs(out->str); + } + } + g_free(m->match_string); qemu_plugin_scoreboard_free(m->counts); } + + g_mutex_unlock(&match_hash_lock); + g_array_free(matches, TRUE); g_array_free(sizes, TRUE); } From bb3dd92d32e1ea805a1eecbb23b35f7675d8a83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:30 +0100 Subject: [PATCH 1995/2884] plugins/lockstep: preserve sock_path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't assign sock_path directly from the autofree'd GStrv, take a copy. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-24-alex.bennee@linaro.org> --- contrib/plugins/lockstep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index 237543b43a..67a779ee9d 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -347,7 +347,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, return -1; } } else if (g_strcmp0(tokens[0], "sockpath") == 0) { - sock_path = tokens[1]; + sock_path = g_strdup(tokens[1]); } else { fprintf(stderr, "option parsing failed: %s\n", p); return -1; From 5e77f22ac95d18fdbe7e5c542002786f2126a780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:31 +0100 Subject: [PATCH 1996/2884] plugins/lockstep: make mixed-mode safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ExecState is shared across the socket and if we want to compare say 64 bit and 32 bit binaries we need the two to use the same sizes for things. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-25-alex.bennee@linaro.org> --- contrib/plugins/lockstep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index 67a779ee9d..8b90b37f67 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -57,7 +57,7 @@ typedef struct { /* The execution state we compare */ typedef struct { uint64_t pc; - unsigned long insn_count; + uint64_t insn_count; } ExecState; typedef struct { @@ -148,7 +148,7 @@ static void report_divergance(ExecState *us, ExecState *them) g_string_printf(out, "Δ insn_count @ 0x%016" PRIx64 - " (%ld) vs 0x%016" PRIx64 " (%ld)\n", + " (%"PRId64") vs 0x%016" PRIx64 " (%"PRId64")\n", us->pc, us->insn_count, them->pc, them->insn_count); for (entry = log, i = 0; From 13167b5fe93c2faa91aa08f91353b066423b5ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:32 +0100 Subject: [PATCH 1997/2884] plugins/lockstep: mention the one-insn-per-tb option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This really helps with lockstep although its super slow on big jobs. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-26-alex.bennee@linaro.org> --- contrib/plugins/lockstep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index 8b90b37f67..1765fd6c36 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -14,7 +14,8 @@ * particular run may execute the exact same sequence of blocks. An * asynchronous event (for example X11 graphics update) may cause a * block to end early and a new partial block to start. This means - * serial only test cases are a better bet. -d nochain may also help. + * serial only test cases are a better bet. -d nochain may also help + * as well as -accel tcg,one-insn-per-tb=on * * This code is not thread safe! * From 3b8550c955ccd0a31667414d69a469bfdb7a56ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 5 Jul 2024 09:40:33 +0100 Subject: [PATCH 1998/2884] plugins/lockstep: clean-up output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were repeating information which wasn't super clear. As we already will have dumped the last failing PC just note the divergence and dump the previous instruction log. Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-27-alex.bennee@linaro.org> --- contrib/plugins/lockstep.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index 1765fd6c36..6a7e9bbb39 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -135,10 +135,13 @@ static void report_divergance(ExecState *us, ExecState *them) /* Output short log entry of going out of sync... */ if (verbose || divrec.distance == 1 || diverged) { - g_string_printf(out, - "@ 0x%016" PRIx64 " vs 0x%016" PRIx64 + g_string_printf(out, "@ " + "0x%016" PRIx64 " (%" PRId64 ") vs " + "0x%016" PRIx64 " (%" PRId64 ")" " (%d/%d since last)\n", - us->pc, them->pc, g_slist_length(divergence_log), + us->pc, us->insn_count, + them->pc, them->insn_count, + g_slist_length(divergence_log), divrec.distance); qemu_plugin_outs(out->str); } @@ -147,10 +150,7 @@ static void report_divergance(ExecState *us, ExecState *them) int i; GSList *entry; - g_string_printf(out, - "Δ insn_count @ 0x%016" PRIx64 - " (%"PRId64") vs 0x%016" PRIx64 " (%"PRId64")\n", - us->pc, us->insn_count, them->pc, them->insn_count); + g_string_printf(out, "Δ too high, we have diverged, previous insns\n"); for (entry = log, i = 0; g_slist_next(entry) && i < 5; @@ -163,7 +163,7 @@ static void report_divergance(ExecState *us, ExecState *them) prev->insn_count); } qemu_plugin_outs(out->str); - qemu_plugin_outs("too much divergence... giving up."); + qemu_plugin_outs("giving up\n"); qemu_plugin_uninstall(our_id, plugin_cleanup); } } From 2089a2e5bb81a258b7d30bf51a2ab7447ecc4e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 5 Jul 2024 09:40:34 +0100 Subject: [PATCH 1999/2884] plugins: Ensure vCPU index is assigned in init/exit hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since vCPUs are hashed by their index, this index can't be uninitialized (UNASSIGNED_CPU_INDEX). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20240606124010.2460-2-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-28-alex.bennee@linaro.org> --- plugins/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/core.c b/plugins/core.c index 9d737d8278..a864275ae7 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -245,6 +245,7 @@ void qemu_plugin_vcpu_init_hook(CPUState *cpu) { bool success; + assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); qemu_rec_mutex_lock(&plugin.lock); plugin.num_vcpus = MAX(plugin.num_vcpus, cpu->cpu_index + 1); plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL); @@ -263,6 +264,7 @@ void qemu_plugin_vcpu_exit_hook(CPUState *cpu) plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT); + assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); qemu_rec_mutex_lock(&plugin.lock); success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index); g_assert(success); From 853c685e8cb4a53d29c3c61145638b22fd1b9659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 5 Jul 2024 09:40:35 +0100 Subject: [PATCH 2000/2884] plugins: Free CPUPluginState before destroying vCPU state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu::plugin_state is allocated in cpu_common_initfn() when the vCPU state is created. Release it in cpu_common_finalize() when we are done. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20240606124010.2460-3-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-29-alex.bennee@linaro.org> --- hw/core/cpu-common.c | 5 +++++ include/qemu/plugin.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index f131cde2c0..8f6cb64da3 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -283,6 +283,11 @@ static void cpu_common_finalize(Object *obj) { CPUState *cpu = CPU(obj); +#ifdef CONFIG_PLUGIN + if (tcg_enabled()) { + g_free(cpu->plugin_state); + } +#endif g_array_free(cpu->gdb_regs, TRUE); qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index bc5aef979e..af5f9db469 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -149,6 +149,9 @@ struct CPUPluginState { /** * qemu_plugin_create_vcpu_state: allocate plugin state + * + * The returned data must be released with g_free() + * when no longer required. */ CPUPluginState *qemu_plugin_create_vcpu_state(void); From 0f3974b64c7dd56e98ebcb98575932eadde421c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 5 Jul 2024 09:40:36 +0100 Subject: [PATCH 2001/2884] accel/tcg: Move qemu_plugin_vcpu_init__async() to plugins/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling qemu_plugin_vcpu_init__async() on the vCPU thread is a detail of plugins, not relevant to TCG vCPU management. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20240606124010.2460-4-philmd@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-30-alex.bennee@linaro.org> --- hw/core/cpu-common.c | 9 +-------- plugins/core.c | 8 +++++++- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 8f6cb64da3..b19e1fdacf 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -192,13 +192,6 @@ static void cpu_common_parse_features(const char *typename, char *features, } } -#ifdef CONFIG_PLUGIN -static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused) -{ - qemu_plugin_vcpu_init_hook(cpu); -} -#endif - static void cpu_common_realizefn(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); @@ -274,7 +267,7 @@ static void cpu_common_initfn(Object *obj) #ifdef CONFIG_PLUGIN if (tcg_enabled()) { cpu->plugin_state = qemu_plugin_create_vcpu_state(); - async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL); + qemu_plugin_vcpu_init_hook(cpu); } #endif } diff --git a/plugins/core.c b/plugins/core.c index a864275ae7..12c67b4b4e 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -241,7 +241,7 @@ static void plugin_grow_scoreboards__locked(CPUState *cpu) end_exclusive(); } -void qemu_plugin_vcpu_init_hook(CPUState *cpu) +static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused) { bool success; @@ -258,6 +258,12 @@ void qemu_plugin_vcpu_init_hook(CPUState *cpu) plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); } +void qemu_plugin_vcpu_init_hook(CPUState *cpu) +{ + /* Plugin initialization must wait until the cpu start executing code */ + async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL); +} + void qemu_plugin_vcpu_exit_hook(CPUState *cpu) { bool success; From 0ef6b12e5839667fa0cec4f463a95fef77b18fda Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:37 +0100 Subject: [PATCH 2002/2884] gdbstub: Clean up process_string_cmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change 'process_string_cmd' to return true on success and false on failure, instead of 0 and -1. Signed-off-by: Gustavo Romero Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240628050850.536447-2-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-31-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index b3574997ea..37314b92e5 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -962,14 +962,14 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(const char *data, - const GdbCmdParseEntry *cmds, int num_cmds) +static bool process_string_cmd(const char *data, + const GdbCmdParseEntry *cmds, int num_cmds) { int i; g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant)); if (!cmds) { - return -1; + return false; } for (i = 0; i < num_cmds; i++) { @@ -984,16 +984,16 @@ static int process_string_cmd(const char *data, if (cmd->schema) { if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params)) { - return -1; + return false; } } gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; cmd->handler(params, NULL); - return 0; + return true; } - return -1; + return false; } static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) @@ -1007,7 +1007,7 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) /* In case there was an error during the command parsing we must * send a NULL packet to indicate the command is not supported */ - if (process_string_cmd(data, cmd, 1)) { + if (!process_string_cmd(data, cmd, 1)) { gdb_put_packet(""); } } @@ -1523,9 +1523,9 @@ static void handle_v_commands(GArray *params, void *user_ctx) return; } - if (process_string_cmd(get_param(params, 0)->data, - gdb_v_commands_table, - ARRAY_SIZE(gdb_v_commands_table))) { + if (!process_string_cmd(get_param(params, 0)->data, + gdb_v_commands_table, + ARRAY_SIZE(gdb_v_commands_table))) { gdb_put_packet(""); } } @@ -1889,15 +1889,15 @@ static void handle_gen_query(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(get_param(params, 0)->data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { + if (process_string_cmd(get_param(params, 0)->data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(get_param(params, 0)->data, - gdb_gen_query_table, - ARRAY_SIZE(gdb_gen_query_table))) { + if (!process_string_cmd(get_param(params, 0)->data, + gdb_gen_query_table, + ARRAY_SIZE(gdb_gen_query_table))) { gdb_put_packet(""); } } @@ -1908,13 +1908,13 @@ static void handle_gen_set(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(get_param(params, 0)->data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { + if (process_string_cmd(get_param(params, 0)->data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(get_param(params, 0)->data, + if (!process_string_cmd(get_param(params, 0)->data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { gdb_put_packet(""); From 133f202b19d8332de8d433c627fe6354e2ecf889 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:38 +0100 Subject: [PATCH 2003/2884] gdbstub: Move GdbCmdParseEntry into a new header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move GdbCmdParseEntry and its associated types into a separate header file to allow the use of GdbCmdParseEntry and other gdbstub command functions outside of gdbstub.c. Since GdbCmdParseEntry and get_param are now public, kdoc GdbCmdParseEntry and rename get_param to gdb_get_cmd_param. This commit also makes gdb_put_packet public since is used in gdbstub command handling. Signed-off-by: Gustavo Romero Reviewed-by: Alex Bennée Message-Id: <20240628050850.536447-3-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-32-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 134 ++++++++++++++----------------------- gdbstub/internals.h | 22 ------ gdbstub/syscalls.c | 7 +- gdbstub/system.c | 7 +- gdbstub/user-target.c | 25 +++---- gdbstub/user.c | 7 +- include/gdbstub/commands.h | 72 ++++++++++++++++++++ 7 files changed, 146 insertions(+), 128 deletions(-) create mode 100644 include/gdbstub/commands.h diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 37314b92e5..9ff2f4177d 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -30,6 +30,7 @@ #include "qemu/error-report.h" #include "trace.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "gdbstub/syscalls.h" #ifdef CONFIG_USER_ONLY #include "accel/tcg/vcpu-state.h" @@ -920,43 +921,6 @@ static int cmd_parse_params(const char *data, const char *schema, return 0; } -typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); - -/* - * cmd_startswith -> cmd is compared using startswith - * - * allow_stop_reply -> true iff the gdbstub can respond to this command with a - * "stop reply" packet. The list of commands that accept such response is - * defined at the GDB Remote Serial Protocol documentation. see: - * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. - * - * schema definitions: - * Each schema parameter entry consists of 2 chars, - * the first char represents the parameter type handling - * the second char represents the delimiter for the next parameter - * - * Currently supported schema types: - * 'l' -> unsigned long (stored in .val_ul) - * 'L' -> unsigned long long (stored in .val_ull) - * 's' -> string (stored in .data) - * 'o' -> single char (stored in .opcode) - * 't' -> thread id (stored in .thread_id) - * '?' -> skip according to delimiter - * - * Currently supported delimiters: - * '?' -> Stop at any delimiter (",;:=\0") - * '0' -> Stop at "\0" - * '.' -> Skip 1 char unless reached "\0" - * Any other value is treated as the delimiter value itself - */ -typedef struct GdbCmdParseEntry { - GdbCmdHandler handler; - const char *cmd; - bool cmd_startswith; - const char *schema; - bool allow_stop_reply; -} GdbCmdParseEntry; - static inline int startswith(const char *string, const char *pattern) { return !strncmp(string, pattern, strlen(pattern)); @@ -1023,7 +987,7 @@ static void handle_detach(GArray *params, void *user_ctx) return; } - pid = get_param(params, 0)->val_ul; + pid = gdb_get_cmd_param(params, 0)->val_ul; } #ifdef CONFIG_USER_ONLY @@ -1061,13 +1025,13 @@ static void handle_thread_alive(GArray *params, void *user_ctx) return; } - if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + if (gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, - get_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, + gdb_get_cmd_param(params, 0)->thread_id.tid); if (!cpu) { gdb_put_packet("E22"); return; @@ -1079,7 +1043,7 @@ static void handle_thread_alive(GArray *params, void *user_ctx) static void handle_continue(GArray *params, void *user_ctx) { if (params->len) { - gdb_set_cpu_pc(get_param(params, 0)->val_ull); + gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } gdbserver_state.signal = 0; @@ -1095,7 +1059,7 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) * omit the addr parameter */ if (params->len) { - signal = get_param(params, 0)->val_ul; + signal = gdb_get_cmd_param(params, 0)->val_ul; } gdbserver_state.signal = gdb_signal_to_target(signal); @@ -1115,18 +1079,18 @@ static void handle_set_thread(GArray *params, void *user_ctx) return; } - if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { + if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { + if (gdb_get_cmd_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { gdb_put_packet("OK"); return; } - pid = get_param(params, 1)->thread_id.pid; - tid = get_param(params, 1)->thread_id.tid; + pid = gdb_get_cmd_param(params, 1)->thread_id.pid; + tid = gdb_get_cmd_param(params, 1)->thread_id.tid; #ifdef CONFIG_USER_ONLY if (gdb_handle_set_thread_user(pid, tid)) { return; @@ -1142,7 +1106,7 @@ static void handle_set_thread(GArray *params, void *user_ctx) * Note: This command is deprecated and modern gdb's will be using the * vCont command instead. */ - switch (get_param(params, 0)->opcode) { + switch (gdb_get_cmd_param(params, 0)->opcode) { case 'c': gdbserver_state.c_cpu = cpu; gdb_put_packet("OK"); @@ -1167,9 +1131,9 @@ static void handle_insert_bp(GArray *params, void *user_ctx) } res = gdb_breakpoint_insert(gdbserver_state.c_cpu, - get_param(params, 0)->val_ul, - get_param(params, 1)->val_ull, - get_param(params, 2)->val_ull); + gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, + gdb_get_cmd_param(params, 2)->val_ull); if (res >= 0) { gdb_put_packet("OK"); return; @@ -1191,9 +1155,9 @@ static void handle_remove_bp(GArray *params, void *user_ctx) } res = gdb_breakpoint_remove(gdbserver_state.c_cpu, - get_param(params, 0)->val_ul, - get_param(params, 1)->val_ull, - get_param(params, 2)->val_ull); + gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, + gdb_get_cmd_param(params, 2)->val_ull); if (res >= 0) { gdb_put_packet("OK"); return; @@ -1225,10 +1189,10 @@ static void handle_set_reg(GArray *params, void *user_ctx) return; } - reg_size = strlen(get_param(params, 1)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size); + reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, - get_param(params, 0)->val_ull); + gdb_get_cmd_param(params, 0)->val_ull); gdb_put_packet("OK"); } @@ -1243,7 +1207,7 @@ static void handle_get_reg(GArray *params, void *user_ctx) reg_size = gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, - get_param(params, 0)->val_ull); + gdb_get_cmd_param(params, 0)->val_ull); if (!reg_size) { gdb_put_packet("E14"); return; @@ -1264,16 +1228,16 @@ static void handle_write_mem(GArray *params, void *user_ctx) } /* gdb_hextomem() reads 2*len bytes */ - if (get_param(params, 1)->val_ull > - strlen(get_param(params, 2)->data) / 2) { + if (gdb_get_cmd_param(params, 1)->val_ull > + strlen(gdb_get_cmd_param(params, 2)->data) / 2) { gdb_put_packet("E22"); return; } - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data, - get_param(params, 1)->val_ull); + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 2)->data, + gdb_get_cmd_param(params, 1)->val_ull); if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, + gdb_get_cmd_param(params, 0)->val_ull, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, true)) { gdb_put_packet("E14"); @@ -1291,16 +1255,16 @@ static void handle_read_mem(GArray *params, void *user_ctx) } /* gdb_memtohex() doubles the required space */ - if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { + if (gdb_get_cmd_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { gdb_put_packet("E22"); return; } g_byte_array_set_size(gdbserver_state.mem_buf, - get_param(params, 1)->val_ull); + gdb_get_cmd_param(params, 1)->val_ull); if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, + gdb_get_cmd_param(params, 0)->val_ull, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, false)) { gdb_put_packet("E14"); @@ -1324,8 +1288,8 @@ static void handle_write_all_regs(GArray *params, void *user_ctx) } cpu_synchronize_state(gdbserver_state.g_cpu); - len = strlen(get_param(params, 0)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); registers = gdbserver_state.mem_buf->data; for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; @@ -1360,7 +1324,7 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) static void handle_step(GArray *params, void *user_ctx) { if (params->len) { - gdb_set_cpu_pc(get_param(params, 0)->val_ull); + gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); @@ -1373,7 +1337,7 @@ static void handle_backward(GArray *params, void *user_ctx) gdb_put_packet("E22"); } if (params->len == 1) { - switch (get_param(params, 0)->opcode) { + switch (gdb_get_cmd_param(params, 0)->opcode) { case 's': if (replay_reverse_step()) { gdb_continue(); @@ -1408,7 +1372,7 @@ static void handle_v_cont(GArray *params, void *user_ctx) return; } - res = gdb_handle_vcont(get_param(params, 0)->data); + res = gdb_handle_vcont(gdb_get_cmd_param(params, 0)->data); if ((res == -EINVAL) || (res == -ERANGE)) { gdb_put_packet("E22"); } else if (res) { @@ -1426,7 +1390,7 @@ static void handle_v_attach(GArray *params, void *user_ctx) goto cleanup; } - process = gdb_get_process(get_param(params, 0)->val_ul); + process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); if (!process) { goto cleanup; } @@ -1523,7 +1487,7 @@ static void handle_v_commands(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(get_param(params, 0)->data, + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_v_commands_table, ARRAY_SIZE(gdb_v_commands_table))) { gdb_put_packet(""); @@ -1555,7 +1519,7 @@ static void handle_set_qemu_sstep(GArray *params, void *user_ctx) return; } - new_sstep_flags = get_param(params, 0)->val_ul; + new_sstep_flags = gdb_get_cmd_param(params, 0)->val_ul; if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { gdb_put_packet("E22"); @@ -1615,13 +1579,13 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) CPUState *cpu; if (!params->len || - get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, - get_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, + gdb_get_cmd_param(params, 0)->thread_id.tid); if (!cpu) { return; } @@ -1673,7 +1637,7 @@ static void handle_query_supported(GArray *params, void *user_ctx) #endif if (params->len) { - const char *gdb_supported = get_param(params, 0)->data; + const char *gdb_supported = gdb_get_cmd_param(params, 0)->data; if (strstr(gdb_supported, "multiprocess+")) { gdbserver_state.multiprocess = true; @@ -1707,15 +1671,15 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) return; } - p = get_param(params, 0)->data; + p = gdb_get_cmd_param(params, 0)->data; xml = get_feature_xml(p, &p, process); if (!xml) { gdb_put_packet("E00"); return; } - addr = get_param(params, 1)->val_ul; - len = get_param(params, 2)->val_ul; + addr = gdb_get_cmd_param(params, 1)->val_ul; + len = gdb_get_cmd_param(params, 2)->val_ul; total_len = strlen(xml); if (addr > total_len) { gdb_put_packet("E00"); @@ -1889,13 +1853,13 @@ static void handle_gen_query(GArray *params, void *user_ctx) return; } - if (process_string_cmd(get_param(params, 0)->data, + if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (!process_string_cmd(get_param(params, 0)->data, + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { gdb_put_packet(""); @@ -1908,13 +1872,13 @@ static void handle_gen_set(GArray *params, void *user_ctx) return; } - if (process_string_cmd(get_param(params, 0)->data, + if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (!process_string_cmd(get_param(params, 0)->data, + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { gdb_put_packet(""); diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 32f9f63297..34121dc61a 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -106,7 +106,6 @@ static inline int tohex(int v) */ void gdb_put_strbuf(void); -int gdb_put_packet(const char *buf); int gdb_put_packet_binary(const char *buf, int len, bool dump); void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_memtohex(GString *buf, const uint8_t *mem, int len); @@ -166,27 +165,6 @@ void gdb_put_buffer(const uint8_t *buf, int len); */ void gdb_init_gdbserver_state(void); -typedef enum GDBThreadIdKind { - GDB_ONE_THREAD = 0, - GDB_ALL_THREADS, /* One process, all threads */ - GDB_ALL_PROCESSES, - GDB_READ_THREAD_ERR -} GDBThreadIdKind; - -typedef union GdbCmdVariant { - const char *data; - uint8_t opcode; - unsigned long val_ul; - unsigned long long val_ull; - struct { - GDBThreadIdKind kind; - uint32_t pid; - uint32_t tid; - } thread_id; -} GdbCmdVariant; - -#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) - void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ diff --git a/gdbstub/syscalls.c b/gdbstub/syscalls.c index 02e3a8f74c..4e1295b782 100644 --- a/gdbstub/syscalls.c +++ b/gdbstub/syscalls.c @@ -16,6 +16,7 @@ #include "sysemu/runstate.h" #include "gdbstub/user.h" #include "gdbstub/syscalls.h" +#include "gdbstub/commands.h" #include "trace.h" #include "internals.h" @@ -154,9 +155,9 @@ void gdb_handle_file_io(GArray *params, void *user_ctx) uint64_t ret; int err; - ret = get_param(params, 0)->val_ull; + ret = gdb_get_cmd_param(params, 0)->val_ull; if (params->len >= 2) { - err = get_param(params, 1)->val_ull; + err = gdb_get_cmd_param(params, 1)->val_ull; } else { err = 0; } @@ -196,7 +197,7 @@ void gdb_handle_file_io(GArray *params, void *user_ctx) gdbserver_syscall_state.current_syscall_cb = NULL; } - if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') { + if (params->len >= 3 && gdb_get_cmd_param(params, 2)->opcode == (uint8_t)'C') { gdb_put_packet("T02"); return; } diff --git a/gdbstub/system.c b/gdbstub/system.c index d235403855..1ad87fe7fd 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -16,6 +16,7 @@ #include "qemu/cutils.h" #include "exec/gdbstub.h" #include "gdbstub/syscalls.h" +#include "gdbstub/commands.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "sysemu/cpus.h" @@ -501,7 +502,7 @@ void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx) return; } - if (!get_param(params, 0)->val_ul) { + if (!gdb_get_cmd_param(params, 0)->val_ul) { phy_memory_mode = 0; } else { phy_memory_mode = 1; @@ -519,7 +520,7 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) return; } - len = strlen(get_param(params, 0)->data); + len = strlen(gdb_get_cmd_param(params, 0)->data); if (len % 2) { gdb_put_packet("E01"); return; @@ -527,7 +528,7 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) g_assert(gdbserver_state.mem_buf->len == 0); len = len / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); qemu_chr_be_write(gdbserver_system_state.mon_chr, gdbserver_state.mem_buf->data, diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c index a9c6c64512..b5e01fd8b0 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "qemu.h" #include "internals.h" #ifdef CONFIG_LINUX @@ -250,8 +251,8 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) return; } - offset = get_param(params, 0)->val_ul; - len = get_param(params, 1)->val_ul; + offset = gdb_get_cmd_param(params, 0)->val_ul; + len = gdb_get_cmd_param(params, 1)->val_ul; ts = get_task_state(gdbserver_state.c_cpu); saved_auxv = ts->info->saved_auxv; auxv_len = ts->info->auxv_len; @@ -288,7 +289,7 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) static const char *get_filename_param(GArray *params, int i) { - const char *hex_filename = get_param(params, i)->data; + const char *hex_filename = gdb_get_cmd_param(params, i)->data; gdb_hextomem(gdbserver_state.mem_buf, hex_filename, strlen(hex_filename) / 2); g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1); @@ -306,8 +307,8 @@ static void hostio_reply_with_data(const void *buf, size_t n) void gdb_handle_v_file_open(GArray *params, void *user_ctx) { const char *filename = get_filename_param(params, 0); - uint64_t flags = get_param(params, 1)->val_ull; - uint64_t mode = get_param(params, 2)->val_ull; + uint64_t flags = gdb_get_cmd_param(params, 1)->val_ull; + uint64_t mode = gdb_get_cmd_param(params, 2)->val_ull; #ifdef CONFIG_LINUX int fd = do_guest_openat(cpu_env(gdbserver_state.g_cpu), 0, filename, @@ -325,7 +326,7 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx) void gdb_handle_v_file_close(GArray *params, void *user_ctx) { - int fd = get_param(params, 0)->val_ul; + int fd = gdb_get_cmd_param(params, 0)->val_ul; if (close(fd) == -1) { g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); @@ -338,9 +339,9 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx) void gdb_handle_v_file_pread(GArray *params, void *user_ctx) { - int fd = get_param(params, 0)->val_ul; - size_t count = get_param(params, 1)->val_ull; - off_t offset = get_param(params, 2)->val_ull; + int fd = gdb_get_cmd_param(params, 0)->val_ul; + size_t count = gdb_get_cmd_param(params, 1)->val_ull; + off_t offset = gdb_get_cmd_param(params, 2)->val_ull; size_t bufsiz = MIN(count, BUFSIZ); g_autofree char *buf = g_try_malloc(bufsiz); @@ -383,9 +384,9 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) { - uint32_t pid = get_param(params, 0)->val_ul; - uint32_t offset = get_param(params, 1)->val_ul; - uint32_t length = get_param(params, 2)->val_ul; + uint32_t pid = gdb_get_cmd_param(params, 0)->val_ul; + uint32_t offset = gdb_get_cmd_param(params, 1)->val_ul; + uint32_t length = gdb_get_cmd_param(params, 2)->val_ul; GDBProcess *process = gdb_get_process(pid); if (!process) { diff --git a/gdbstub/user.c b/gdbstub/user.c index e34b58b407..b36033bc7a 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -16,6 +16,7 @@ #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "gdbstub/syscalls.h" #include "gdbstub/user.h" #include "gdbstub/enums.h" @@ -793,7 +794,7 @@ void gdb_syscall_return(CPUState *cs, int num) void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx) { - const char *param = get_param(params, 0)->data; + const char *param = gdb_get_cmd_param(params, 0)->data; GDBSyscallsMask catch_syscalls_mask; bool catch_all_syscalls; unsigned int num; @@ -858,8 +859,8 @@ void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx) unsigned long offset, len; uint8_t *siginfo_offset; - offset = get_param(params, 0)->val_ul; - len = get_param(params, 1)->val_ul; + offset = gdb_get_cmd_param(params, 0)->val_ul; + len = gdb_get_cmd_param(params, 1)->val_ul; if (offset + len > gdbserver_user_state.siginfo_len) { /* Invalid offset and/or requested length. */ diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h new file mode 100644 index 0000000000..639257493e --- /dev/null +++ b/include/gdbstub/commands.h @@ -0,0 +1,72 @@ +#ifndef GDBSTUB_COMMANDS_H +#define GDBSTUB + +typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); + +typedef enum GDBThreadIdKind { + GDB_ONE_THREAD = 0, + GDB_ALL_THREADS, /* One process, all threads */ + GDB_ALL_PROCESSES, + GDB_READ_THREAD_ERR +} GDBThreadIdKind; + +typedef union GdbCmdVariant { + const char *data; + uint8_t opcode; + unsigned long val_ul; + unsigned long long val_ull; + struct { + GDBThreadIdKind kind; + uint32_t pid; + uint32_t tid; + } thread_id; +} GdbCmdVariant; + +#define gdb_get_cmd_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) + +/** + * typedef GdbCmdParseEntry - gdb command parser + * + * This structure keeps the information necessary to match a gdb command, + * parse it (extract its parameters), and select the correct handler for it. + * + * @cmd: The command to be matched + * @cmd_startswith: If true, @cmd is compared using startswith + * @schema: Each schema for the command parameter entry consists of 2 chars, + * the first char represents the parameter type handling the second char + * represents the delimiter for the next parameter. + * + * Currently supported schema types: + * 'l' -> unsigned long (stored in .val_ul) + * 'L' -> unsigned long long (stored in .val_ull) + * 's' -> string (stored in .data) + * 'o' -> single char (stored in .opcode) + * 't' -> thread id (stored in .thread_id) + * '?' -> skip according to delimiter + * + * Currently supported delimiters: + * '?' -> Stop at any delimiter (",;:=\0") + * '0' -> Stop at "\0" + * '.' -> Skip 1 char unless reached "\0" + * Any other value is treated as the delimiter value itself + * + * @allow_stop_reply: True iff the gdbstub can respond to this command with a + * "stop reply" packet. The list of commands that accept such response is + * defined at the GDB Remote Serial Protocol documentation. See: + * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. + */ +typedef struct GdbCmdParseEntry { + GdbCmdHandler handler; + const char *cmd; + bool cmd_startswith; + const char *schema; + bool allow_stop_reply; +} GdbCmdParseEntry; + +/** + * gdb_put_packet() - put string into gdb server's buffer so it is sent + * to the client + */ +int gdb_put_packet(const char *buf); + +#endif /* GDBSTUB_COMMANDS_H */ From 60f4ce8e2cb9afebc7f3e40062087b91b7243c36 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:39 +0100 Subject: [PATCH 2004/2884] gdbstub: Add support for target-specific stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, it's not possible to have stubs specific to a given target, even though there are GDB features which are target-specific, like, for instance, memory tagging. This commit introduces gdb_extend_qsupported_features, gdb_extend_query_table, and gdb_extend_set_table functions as interfaces to extend the qSupported string, the query handler table, and the set handler table, allowing target-specific stub implementations. Signed-off-by: Gustavo Romero Reviewed-by: Alex Bennée Message-Id: <20240628050850.536447-4-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-33-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 102 ++++++++++++++++++++++++++++++++++--- include/gdbstub/commands.h | 22 ++++++++ 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 9ff2f4177d..b1ca253f97 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1609,6 +1609,20 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) gdb_put_strbuf(); } +static char *extended_qsupported_features; +void gdb_extend_qsupported_features(char *qsupported_features) +{ + /* + * We don't support different sets of CPU gdb features on different CPUs yet + * so assert the feature strings are the same on all CPUs, or is set only + * once (1 CPU). + */ + g_assert(extended_qsupported_features == NULL || + g_strcmp0(extended_qsupported_features, qsupported_features) == 0); + + extended_qsupported_features = qsupported_features; +} + static void handle_query_supported(GArray *params, void *user_ctx) { CPUClass *cc; @@ -1648,6 +1662,11 @@ static void handle_query_supported(GArray *params, void *user_ctx) } g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); + + if (extended_qsupported_features) { + g_string_append(gdbserver_state.str_buf, extended_qsupported_features); + } + gdb_put_strbuf(); } @@ -1729,6 +1748,41 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { }, }; +/* Compares if a set of command parsers is equal to another set of parsers. */ +static bool cmp_cmds(GdbCmdParseEntry *c, GdbCmdParseEntry *d, int size) +{ + for (int i = 0; i < size; i++) { + if (!(c[i].handler == d[i].handler && + g_strcmp0(c[i].cmd, d[i].cmd) == 0 && + c[i].cmd_startswith == d[i].cmd_startswith && + g_strcmp0(c[i].schema, d[i].schema) == 0)) { + + /* Sets are different. */ + return false; + } + } + + /* Sets are equal, i.e. contain the same command parsers. */ + return true; +} + +static GdbCmdParseEntry *extended_query_table; +static int extended_query_table_size; +void gdb_extend_query_table(GdbCmdParseEntry *table, int size) +{ + /* + * We don't support different sets of CPU gdb features on different CPUs yet + * so assert query table is the same on all CPUs, or is set only once + * (1 CPU). + */ + g_assert(extended_query_table == NULL || + (extended_query_table_size == size && + cmp_cmds(extended_query_table, table, size))); + + extended_query_table = table; + extended_query_table_size = size; +} + static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_curr_tid, @@ -1821,6 +1875,22 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { #endif }; +static GdbCmdParseEntry *extended_set_table; +static int extended_set_table_size; +void gdb_extend_set_table(GdbCmdParseEntry *table, int size) +{ + /* + * We don't support different sets of CPU gdb features on different CPUs yet + * so assert set table is the same on all CPUs, or is set only once (1 CPU). + */ + g_assert(extended_set_table == NULL || + (extended_set_table_size == size && + cmp_cmds(extended_set_table, table, size))); + + extended_set_table = table; + extended_set_table_size = size; +} + static const GdbCmdParseEntry gdb_gen_set_table[] = { /* Order is important if has same prefix */ { @@ -1859,11 +1929,21 @@ static void handle_gen_query(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, - gdb_gen_query_table, - ARRAY_SIZE(gdb_gen_query_table))) { - gdb_put_packet(""); + if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, + gdb_gen_query_table, + ARRAY_SIZE(gdb_gen_query_table))) { + return; } + + if (extended_query_table && + process_string_cmd(gdb_get_cmd_param(params, 0)->data, + extended_query_table, + extended_query_table_size)) { + return; + } + + /* Can't handle query, return Empty response. */ + gdb_put_packet(""); } static void handle_gen_set(GArray *params, void *user_ctx) @@ -1878,11 +1958,21 @@ static void handle_gen_set(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, + if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - gdb_put_packet(""); + return; } + + if (extended_set_table && + process_string_cmd(gdb_get_cmd_param(params, 0)->data, + extended_set_table, + extended_set_table_size)) { + return; + } + + /* Can't handle set, return Empty response. */ + gdb_put_packet(""); } static void handle_target_halt(GArray *params, void *user_ctx) diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h index 639257493e..306dfdef97 100644 --- a/include/gdbstub/commands.h +++ b/include/gdbstub/commands.h @@ -69,4 +69,26 @@ typedef struct GdbCmdParseEntry { */ int gdb_put_packet(const char *buf); +/** + * gdb_extend_query_table() - Extend query table. + * @table: The table with the additional query packet handlers. + * @size: The number of handlers to be added. + */ +void gdb_extend_query_table(GdbCmdParseEntry *table, int size); + +/** + * gdb_extend_set_table() - Extend set table. + * @table: The table with the additional set packet handlers. + * @size: The number of handlers to be added. + */ +void gdb_extend_set_table(GdbCmdParseEntry *table, int size); + +/** + * gdb_extend_qsupported_features() - Extend the qSupported features string. + * @qsupported_features: The additional qSupported feature(s) string. The string + * should start with a semicolon and, if there are more than one feature, the + * features should be separate by a semiocolon. + */ +void gdb_extend_qsupported_features(char *qsupported_features); + #endif /* GDBSTUB_COMMANDS_H */ From 41bfb6704eed12015ddead4e507b97b39b1ff5f6 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:40 +0100 Subject: [PATCH 2005/2884] target/arm: Fix exception case in allocation_tag_mem_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If page in 'ptr_access' is inaccessible and probe is 'true' allocation_tag_mem_probe should not throw an exception, but currently it does, so fix it. Signed-off-by: Gustavo Romero Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-Id: <20240628050850.536447-5-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-34-alex.bennee@linaro.org> --- target/arm/tcg/mte_helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 037ac6dd60..a50d576294 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -96,6 +96,9 @@ static uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, assert(!(probe && ra)); if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) { + if (probe) { + return NULL; + } cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access, !(flags & PAGE_VALID), ra); } From 0c9b437c90b127bb99fc2e0d3cd41136b2148078 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:41 +0100 Subject: [PATCH 2006/2884] target/arm: Make some MTE helpers widely available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the MTE helpers allocation_tag_mem_probe, load_tag1, and store_tag1 available to other subsystems. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20240628050850.536447-6-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-35-alex.bennee@linaro.org> --- target/arm/tcg/mte_helper.c | 45 ++++--------------------- target/arm/tcg/mte_helper.h | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 target/arm/tcg/mte_helper.h diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index a50d576294..9d2ba287ee 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -29,6 +29,7 @@ #include "hw/core/tcg-cpu-ops.h" #include "qapi/error.h" #include "qemu/guest-random.h" +#include "mte_helper.h" static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) @@ -50,42 +51,10 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) return tag; } -/** - * allocation_tag_mem_probe: - * @env: the cpu environment - * @ptr_mmu_idx: the addressing regime to use for the virtual address - * @ptr: the virtual address for which to look up tag memory - * @ptr_access: the access to use for the virtual address - * @ptr_size: the number of bytes in the normal memory access - * @tag_access: the access to use for the tag memory - * @probe: true to merely probe, never taking an exception - * @ra: the return address for exception handling - * - * Our tag memory is formatted as a sequence of little-endian nibbles. - * That is, the byte at (addr >> (LOG2_TAG_GRANULE + 1)) contains two - * tags, with the tag at [3:0] for the lower addr and the tag at [7:4] - * for the higher addr. - * - * Here, resolve the physical address from the virtual address, and return - * a pointer to the corresponding tag byte. - * - * If there is no tag storage corresponding to @ptr, return NULL. - * - * If the page is inaccessible for @ptr_access, or has a watchpoint, there are - * three options: - * (1) probe = true, ra = 0 : pure probe -- we return NULL if the page is not - * accessible, and do not take watchpoint traps. The calling code must - * handle those cases in the right priority compared to MTE traps. - * (2) probe = false, ra = 0 : probe, no fault expected -- the caller guarantees - * that the page is going to be accessible. We will take watchpoint traps. - * (3) probe = false, ra != 0 : non-probe -- we will take both memory access - * traps and watchpoint traps. - * (probe = true, ra != 0 is invalid and will assert.) - */ -static uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, - uint64_t ptr, MMUAccessType ptr_access, - int ptr_size, MMUAccessType tag_access, - bool probe, uintptr_t ra) +uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + bool probe, uintptr_t ra) { #ifdef CONFIG_USER_ONLY uint64_t clean_ptr = useronly_clean_ptr(ptr); @@ -287,7 +256,7 @@ uint64_t HELPER(addsubg)(CPUARMState *env, uint64_t ptr, return address_with_allocation_tag(ptr + offset, rtag); } -static int load_tag1(uint64_t ptr, uint8_t *mem) +int load_tag1(uint64_t ptr, uint8_t *mem) { int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; return extract32(*mem, ofs, 4); @@ -321,7 +290,7 @@ static void check_tag_aligned(CPUARMState *env, uint64_t ptr, uintptr_t ra) } /* For use in a non-parallel context, store to the given nibble. */ -static void store_tag1(uint64_t ptr, uint8_t *mem, int tag) +void store_tag1(uint64_t ptr, uint8_t *mem, int tag) { int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; *mem = deposit32(*mem, ofs, 4, tag); diff --git a/target/arm/tcg/mte_helper.h b/target/arm/tcg/mte_helper.h new file mode 100644 index 0000000000..1f471fb69b --- /dev/null +++ b/target/arm/tcg/mte_helper.h @@ -0,0 +1,66 @@ +/* + * ARM MemTag operation helpers. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef TARGET_ARM_MTE_H +#define TARGET_ARM_MTE_H + +#include "exec/mmu-access-type.h" + +/** + * allocation_tag_mem_probe: + * @env: the cpu environment + * @ptr_mmu_idx: the addressing regime to use for the virtual address + * @ptr: the virtual address for which to look up tag memory + * @ptr_access: the access to use for the virtual address + * @ptr_size: the number of bytes in the normal memory access + * @tag_access: the access to use for the tag memory + * @probe: true to merely probe, never taking an exception + * @ra: the return address for exception handling + * + * Our tag memory is formatted as a sequence of little-endian nibbles. + * That is, the byte at (addr >> (LOG2_TAG_GRANULE + 1)) contains two + * tags, with the tag at [3:0] for the lower addr and the tag at [7:4] + * for the higher addr. + * + * Here, resolve the physical address from the virtual address, and return + * a pointer to the corresponding tag byte. + * + * If there is no tag storage corresponding to @ptr, return NULL. + * + * If the page is inaccessible for @ptr_access, or has a watchpoint, there are + * three options: + * (1) probe = true, ra = 0 : pure probe -- we return NULL if the page is not + * accessible, and do not take watchpoint traps. The calling code must + * handle those cases in the right priority compared to MTE traps. + * (2) probe = false, ra = 0 : probe, no fault expected -- the caller guarantees + * that the page is going to be accessible. We will take watchpoint traps. + * (3) probe = false, ra != 0 : non-probe -- we will take both memory access + * traps and watchpoint traps. + * (probe = true, ra != 0 is invalid and will assert.) + */ +uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + bool probe, uintptr_t ra); + +/** + * load_tag1 - Load 1 tag (nibble) from byte + * @ptr: The tagged address + * @mem: The tag address (packed, 2 tags in byte) + */ +int load_tag1(uint64_t ptr, uint8_t *mem); + +/** + * store_tag1 - Store 1 tag (nibble) into byte + * @ptr: The tagged address + * @mem: The tag address (packed, 2 tags in byte) + * @tag: The tag to be stored in the nibble + */ +void store_tag1(uint64_t ptr, uint8_t *mem, int tag); + +#endif /* TARGET_ARM_MTE_H */ From bef6a77f6da8bdba7dca36aec4976b434d0d8f1c Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:42 +0100 Subject: [PATCH 2007/2884] target/arm: Factor out code for setting MTE TCF0 field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out the code used for setting the MTE TCF0 field from the prctl code into a convenient function. Other subsystems, like gdbstub, need to set this field as well, so keep it as a separate function to avoid duplication and ensure consistency in how this field is set across the board. Signed-off-by: Gustavo Romero Message-Id: <20240628050850.536447-7-gustavo.romero@linaro.org> [AJB: clean-up includes, move MTE defines] Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-36-alex.bennee@linaro.org> --- linux-user/aarch64/meson.build | 2 ++ linux-user/aarch64/mte_user_helper.c | 35 ++++++++++++++++++++++++++++ linux-user/aarch64/mte_user_helper.h | 32 +++++++++++++++++++++++++ linux-user/aarch64/target_prctl.h | 22 ++--------------- linux-user/syscall.c | 9 ------- 5 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 linux-user/aarch64/mte_user_helper.c create mode 100644 linux-user/aarch64/mte_user_helper.h diff --git a/linux-user/aarch64/meson.build b/linux-user/aarch64/meson.build index 248c578d15..f75bb3cd75 100644 --- a/linux-user/aarch64/meson.build +++ b/linux-user/aarch64/meson.build @@ -9,3 +9,5 @@ vdso_le_inc = gen_vdso.process('vdso-le.so', extra_args: ['-r', '__kernel_rt_sigreturn']) linux_user_ss.add(when: 'TARGET_AARCH64', if_true: [vdso_be_inc, vdso_le_inc]) + +linux_user_ss.add(when: 'TARGET_AARCH64', if_true: [files('mte_user_helper.c')]) diff --git a/linux-user/aarch64/mte_user_helper.c b/linux-user/aarch64/mte_user_helper.c new file mode 100644 index 0000000000..a5b1c8503b --- /dev/null +++ b/linux-user/aarch64/mte_user_helper.c @@ -0,0 +1,35 @@ +/* + * ARM MemTag convenience functions. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "mte_user_helper.h" + +void arm_set_mte_tcf0(CPUArchState *env, abi_long value) +{ + /* + * Write PR_MTE_TCF to SCTLR_EL1[TCF0]. + * + * The kernel has a per-cpu configuration for the sysadmin, + * /sys/devices/system/cpu/cpu/mte_tcf_preferred, + * which qemu does not implement. + * + * Because there is no performance difference between the modes, and + * because SYNC is most useful for debugging MTE errors, choose SYNC + * as the preferred mode. With this preference, and the way the API + * uses only two bits, there is no way for the program to select + * ASYMM mode. + */ + unsigned tcf = 0; + if (value & PR_MTE_TCF_SYNC) { + tcf = 1; + } else if (value & PR_MTE_TCF_ASYNC) { + tcf = 2; + } + env->cp15.sctlr_el[1] = deposit64(env->cp15.sctlr_el[1], 38, 2, tcf); +} diff --git a/linux-user/aarch64/mte_user_helper.h b/linux-user/aarch64/mte_user_helper.h new file mode 100644 index 0000000000..8685e5175a --- /dev/null +++ b/linux-user/aarch64/mte_user_helper.h @@ -0,0 +1,32 @@ +/* + * ARM MemTag convenience functions. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef AARCH64_MTE_USER_HELPER_H +#define AARCH64_MTE USER_HELPER_H + +#ifndef PR_MTE_TCF_SHIFT +# define PR_MTE_TCF_SHIFT 1 +# define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TAG_SHIFT 3 +# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) +#endif + +/** + * arm_set_mte_tcf0 - Set TCF0 field in SCTLR_EL1 register + * @env: The CPU environment + * @value: The value to be set for the Tag Check Fault in EL0 field. + * + * Only SYNC and ASYNC modes can be selected. If ASYMM mode is given, the SYNC + * mode is selected instead. So, there is no way to set the ASYMM mode. + */ +void arm_set_mte_tcf0(CPUArchState *env, abi_long value); + +#endif /* AARCH64_MTE_USER_HELPER_H */ diff --git a/linux-user/aarch64/target_prctl.h b/linux-user/aarch64/target_prctl.h index aa8e203c15..ed75b9e4b5 100644 --- a/linux-user/aarch64/target_prctl.h +++ b/linux-user/aarch64/target_prctl.h @@ -7,6 +7,7 @@ #define AARCH64_TARGET_PRCTL_H #include "target/arm/cpu-features.h" +#include "mte_user_helper.h" static abi_long do_prctl_sve_get_vl(CPUArchState *env) { @@ -173,26 +174,7 @@ static abi_long do_prctl_set_tagged_addr_ctrl(CPUArchState *env, abi_long arg2) env->tagged_addr_enable = arg2 & PR_TAGGED_ADDR_ENABLE; if (cpu_isar_feature(aa64_mte, cpu)) { - /* - * Write PR_MTE_TCF to SCTLR_EL1[TCF0]. - * - * The kernel has a per-cpu configuration for the sysadmin, - * /sys/devices/system/cpu/cpu/mte_tcf_preferred, - * which qemu does not implement. - * - * Because there is no performance difference between the modes, and - * because SYNC is most useful for debugging MTE errors, choose SYNC - * as the preferred mode. With this preference, and the way the API - * uses only two bits, there is no way for the program to select - * ASYMM mode. - */ - unsigned tcf = 0; - if (arg2 & PR_MTE_TCF_SYNC) { - tcf = 1; - } else if (arg2 & PR_MTE_TCF_ASYNC) { - tcf = 2; - } - env->cp15.sctlr_el[1] = deposit64(env->cp15.sctlr_el[1], 38, 2, tcf); + arm_set_mte_tcf0(env, arg2); /* * Write PR_MTE_TAG to GCR_EL1[Exclude]. diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e2804312fc..b8c278b91d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6281,15 +6281,6 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) # define PR_GET_TAGGED_ADDR_CTRL 56 # define PR_TAGGED_ADDR_ENABLE (1UL << 0) #endif -#ifndef PR_MTE_TCF_SHIFT -# define PR_MTE_TCF_SHIFT 1 -# define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TAG_SHIFT 3 -# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) -#endif #ifndef PR_SET_IO_FLUSHER # define PR_SET_IO_FLUSHER 57 # define PR_GET_IO_FLUSHER 58 From 3ce0fc57a0e7d528ce0435c93205f9f6558c38ff Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:43 +0100 Subject: [PATCH 2008/2884] gdbstub: Make hex conversion function non-internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make gdb_hextomem non-internal so it's not confined to use only in gdbstub.c. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Message-Id: <20240628050850.536447-8-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-37-alex.bennee@linaro.org> --- gdbstub/internals.h | 1 - include/gdbstub/commands.h | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 34121dc61a..bf5a5c6302 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -107,7 +107,6 @@ static inline int tohex(int v) void gdb_put_strbuf(void); int gdb_put_packet_binary(const char *buf, int len, bool dump); -void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_memtohex(GString *buf, const uint8_t *mem, int len); void gdb_memtox(GString *buf, const char *mem, int len); void gdb_read_byte(uint8_t ch); diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h index 306dfdef97..e51f276b40 100644 --- a/include/gdbstub/commands.h +++ b/include/gdbstub/commands.h @@ -91,4 +91,10 @@ void gdb_extend_set_table(GdbCmdParseEntry *table, int size); */ void gdb_extend_qsupported_features(char *qsupported_features); +/** + * Convert a hex string to bytes. Conversion is done per byte, so 2 hex digits + * are converted to 1 byte. Invalid hex digits are treated as 0 digits. + */ +void gdb_hextomem(GByteArray *mem, const char *buf, int len); + #endif /* GDBSTUB_COMMANDS_H */ From 2be4d5db1e50f5aabdeea6d1e63ef75cccd0bbdb Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:44 +0100 Subject: [PATCH 2009/2884] gdbstub: Pass CPU context to command handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow passing the current CPU context to command handlers via user_ctx when the handler requires it. Signed-off-by: Gustavo Romero Message-Id: <20240628050850.536447-9-gustavo.romero@linaro.org> Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-38-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 7 ++++++- include/gdbstub/commands.h | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index b1ca253f97..5c1612ed2a 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -938,6 +938,7 @@ static bool process_string_cmd(const char *data, for (i = 0; i < num_cmds; i++) { const GdbCmdParseEntry *cmd = &cmds[i]; + void *user_ctx = NULL; g_assert(cmd->handler && cmd->cmd); if ((cmd->cmd_startswith && !startswith(data, cmd->cmd)) || @@ -952,8 +953,12 @@ static bool process_string_cmd(const char *data, } } + if (cmd->need_cpu_context) { + user_ctx = (void *)gdbserver_state.g_cpu; + } + gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; - cmd->handler(params, NULL); + cmd->handler(params, user_ctx); return true; } diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h index e51f276b40..f3058f9dda 100644 --- a/include/gdbstub/commands.h +++ b/include/gdbstub/commands.h @@ -54,6 +54,8 @@ typedef union GdbCmdVariant { * "stop reply" packet. The list of commands that accept such response is * defined at the GDB Remote Serial Protocol documentation. See: * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. + * + * @need_cpu_context: Pass current CPU context to command handler via user_ctx. */ typedef struct GdbCmdParseEntry { GdbCmdHandler handler; @@ -61,6 +63,7 @@ typedef struct GdbCmdParseEntry { bool cmd_startswith; const char *schema; bool allow_stop_reply; + bool need_cpu_context; } GdbCmdParseEntry; /** From 3b6c27d8f23bfc298cae3a7e404421107705b211 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:45 +0100 Subject: [PATCH 2010/2884] gdbstub: Use true to set cmd_startswith MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd_startswith is a boolean so use 'true' to set it instead of 1. Signed-off-by: Gustavo Romero Message-Id: <20240628050850.536447-10-gustavo.romero@linaro.org> Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-39-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 80 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 5c1612ed2a..b9ad0a063e 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1433,26 +1433,26 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { { .handler = handle_v_cont_query, .cmd = "Cont?", - .cmd_startswith = 1 + .cmd_startswith = true }, { .handler = handle_v_cont, .cmd = "Cont", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "s0" }, { .handler = handle_v_attach, .cmd = "Attach;", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "l0" }, { .handler = handle_v_kill, .cmd = "Kill;", - .cmd_startswith = 1 + .cmd_startswith = true }, #ifdef CONFIG_USER_ONLY /* @@ -1462,25 +1462,25 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { { .handler = gdb_handle_v_file_open, .cmd = "File:open:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s,L,L0" }, { .handler = gdb_handle_v_file_close, .cmd = "File:close:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, { .handler = gdb_handle_v_file_pread, .cmd = "File:pread:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,L,L0" }, { .handler = gdb_handle_v_file_readlink, .cmd = "File:readlink:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, #endif @@ -1748,7 +1748,7 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { { .handler = handle_set_qemu_sstep, .cmd = "qemu.sstep=", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, }; @@ -1804,7 +1804,7 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_thread_extra, .cmd = "ThreadExtraInfo,", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "t0" }, #ifdef CONFIG_USER_ONLY @@ -1816,14 +1816,14 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = gdb_handle_query_rcmd, .cmd = "Rcmd,", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, #endif { .handler = handle_query_supported, .cmd = "Supported:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, { @@ -1834,7 +1834,7 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_xfer_features, .cmd = "Xfer:features:read:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s:l,l0" }, #if defined(CONFIG_USER_ONLY) @@ -1842,27 +1842,27 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = gdb_handle_query_xfer_auxv, .cmd = "Xfer:auxv:read::", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,l0" }, { .handler = gdb_handle_query_xfer_siginfo, .cmd = "Xfer:siginfo:read::", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,l0" }, #endif { .handler = gdb_handle_query_xfer_exec_file, .cmd = "Xfer:exec-file:read:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l:l,l0" }, #endif { .handler = gdb_handle_query_attached, .cmd = "Attached:", - .cmd_startswith = 1 + .cmd_startswith = true }, { .handler = gdb_handle_query_attached, @@ -1901,14 +1901,14 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = { { .handler = handle_set_qemu_sstep, .cmd = "qemu.sstep:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, #ifndef CONFIG_USER_ONLY { .handler = gdb_handle_set_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, #endif @@ -1916,7 +1916,7 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = { { .handler = gdb_handle_set_catch_syscalls, .cmd = "CatchSyscalls:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0", }, #endif @@ -2012,7 +2012,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry target_halted_cmd_desc = { .handler = handle_target_halt, .cmd = "?", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, }; cmd_parser = &target_halted_cmd_desc; @@ -2023,7 +2023,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry continue_cmd_desc = { .handler = handle_continue, .cmd = "c", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "L0" }; @@ -2035,7 +2035,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry cont_with_sig_cmd_desc = { .handler = handle_cont_with_sig, .cmd = "C", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "l0" }; @@ -2047,7 +2047,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry v_cmd_desc = { .handler = handle_v_commands, .cmd = "v", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &v_cmd_desc; @@ -2064,7 +2064,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry detach_cmd_desc = { .handler = handle_detach, .cmd = "D", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "?.l0" }; cmd_parser = &detach_cmd_desc; @@ -2075,7 +2075,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry step_cmd_desc = { .handler = handle_step, .cmd = "s", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "L0" }; @@ -2087,7 +2087,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry backward_cmd_desc = { .handler = handle_backward, .cmd = "b", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "o0" }; @@ -2099,7 +2099,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry file_io_cmd_desc = { .handler = gdb_handle_file_io, .cmd = "F", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L,o0" }; cmd_parser = &file_io_cmd_desc; @@ -2110,7 +2110,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry read_all_regs_cmd_desc = { .handler = handle_read_all_regs, .cmd = "g", - .cmd_startswith = 1 + .cmd_startswith = true }; cmd_parser = &read_all_regs_cmd_desc; } @@ -2120,7 +2120,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry write_all_regs_cmd_desc = { .handler = handle_write_all_regs, .cmd = "G", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &write_all_regs_cmd_desc; @@ -2131,7 +2131,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry read_mem_cmd_desc = { .handler = handle_read_mem, .cmd = "m", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L0" }; cmd_parser = &read_mem_cmd_desc; @@ -2142,7 +2142,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry write_mem_cmd_desc = { .handler = handle_write_mem, .cmd = "M", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L:s0" }; cmd_parser = &write_mem_cmd_desc; @@ -2153,7 +2153,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry get_reg_cmd_desc = { .handler = handle_get_reg, .cmd = "p", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L0" }; cmd_parser = &get_reg_cmd_desc; @@ -2164,7 +2164,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry set_reg_cmd_desc = { .handler = handle_set_reg, .cmd = "P", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L?s0" }; cmd_parser = &set_reg_cmd_desc; @@ -2175,7 +2175,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry insert_bp_cmd_desc = { .handler = handle_insert_bp, .cmd = "Z", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l?L?L0" }; cmd_parser = &insert_bp_cmd_desc; @@ -2186,7 +2186,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry remove_bp_cmd_desc = { .handler = handle_remove_bp, .cmd = "z", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l?L?L0" }; cmd_parser = &remove_bp_cmd_desc; @@ -2197,7 +2197,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry set_thread_cmd_desc = { .handler = handle_set_thread, .cmd = "H", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "o.t0" }; cmd_parser = &set_thread_cmd_desc; @@ -2208,7 +2208,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry thread_alive_cmd_desc = { .handler = handle_thread_alive, .cmd = "T", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "t0" }; cmd_parser = &thread_alive_cmd_desc; @@ -2219,7 +2219,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry gen_query_cmd_desc = { .handler = handle_gen_query, .cmd = "q", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &gen_query_cmd_desc; @@ -2230,7 +2230,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry gen_set_cmd_desc = { .handler = handle_gen_set, .cmd = "Q", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &gen_set_cmd_desc; From f81198cefad223afc8e1ae60e9830b60e5f2d6ff Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:46 +0100 Subject: [PATCH 2011/2884] gdbstub: Add support for MTE in user mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements the stubs to handle the qIsAddressTagged, qMemTag, and QMemTag GDB packets, allowing all GDB 'memory-tag' subcommands to work with QEMU gdbstub on aarch64 user mode. It also implements the get/set functions for the special GDB MTE register 'tag_ctl', used to control the MTE fault type at runtime. Signed-off-by: Gustavo Romero Message-Id: <20240628050850.536447-11-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-40-alex.bennee@linaro.org> --- configs/targets/aarch64-linux-user.mak | 2 +- gdb-xml/aarch64-mte.xml | 11 ++ target/arm/cpu.c | 1 + target/arm/gdbstub.c | 46 +++++ target/arm/gdbstub64.c | 223 +++++++++++++++++++++++++ target/arm/internals.h | 6 + 6 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 gdb-xml/aarch64-mte.xml diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index ba8bc5fe3f..8f0ed21d76 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -1,6 +1,6 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml gdb-xml/aarch64-mte.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/gdb-xml/aarch64-mte.xml b/gdb-xml/aarch64-mte.xml new file mode 100644 index 0000000000..4b70b4f17a --- /dev/null +++ b/gdb-xml/aarch64-mte.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 35fa281f1b..14d4eca127 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2518,6 +2518,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); + arm_cpu_register_gdb_commands(cpu); init_cpreg_list(cpu); diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index a3bb73cfa7..c3a9b5eb1e 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" #include "gdbstub/helpers.h" +#include "gdbstub/commands.h" #include "sysemu/tcg.h" #include "internals.h" #include "cpu-features.h" @@ -474,6 +475,41 @@ static GDBFeature *arm_gen_dynamic_m_secextreg_feature(CPUState *cs, #endif #endif /* CONFIG_TCG */ +void arm_cpu_register_gdb_commands(ARMCPU *cpu) +{ + GArray *query_table = + g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry)); + GArray *set_table = + g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry)); + GString *qsupported_features = g_string_new(NULL); + + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + #ifdef TARGET_AARCH64 + aarch64_cpu_register_gdb_commands(cpu, qsupported_features, query_table, + set_table); + #endif + } + + /* Set arch-specific handlers for 'q' commands. */ + if (query_table->len) { + gdb_extend_query_table(&g_array_index(query_table, + GdbCmdParseEntry, 0), + query_table->len); + } + + /* Set arch-specific handlers for 'Q' commands. */ + if (set_table->len) { + gdb_extend_set_table(&g_array_index(set_table, + GdbCmdParseEntry, 0), + set_table->len); + } + + /* Set arch-specific qSupported feature. */ + if (qsupported_features->len) { + gdb_extend_qsupported_features(qsupported_features->str); + } +} + void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) { CPUState *cs = CPU(cpu); @@ -507,6 +543,16 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) gdb_find_static_feature("aarch64-pauth.xml"), 0); } + +#ifdef CONFIG_USER_ONLY + /* Memory Tagging Extension (MTE) 'tag_ctl' pseudo-register. */ + if (cpu_isar_feature(aa64_mte, cpu)) { + gdb_register_coprocessor(cs, aarch64_gdb_get_tag_ctl_reg, + aarch64_gdb_set_tag_ctl_reg, + gdb_find_static_feature("aarch64-mte.xml"), + 0); + } +#endif #endif } else { if (arm_feature(env, ARM_FEATURE_NEON)) { diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index caa31ff3fa..2e2bc2700b 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -21,6 +21,12 @@ #include "cpu.h" #include "internals.h" #include "gdbstub/helpers.h" +#include "gdbstub/commands.h" +#include "tcg/mte_helper.h" +#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX) +#include +#include "mte_user_helper.h" +#endif int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { @@ -381,3 +387,220 @@ GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg) return &cpu->dyn_svereg_feature.desc; } + +#ifdef CONFIG_USER_ONLY +int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint64_t tcf0; + + assert(reg == 0); + + tcf0 = extract64(env->cp15.sctlr_el[1], 38, 2); + + return gdb_get_reg64(buf, tcf0); +} + +int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + uint8_t tcf; + + assert(reg == 0); + + tcf = *buf << PR_MTE_TCF_SHIFT; + + if (!tcf) { + return 0; + } + + /* + * 'tag_ctl' register is actually a "pseudo-register" provided by GDB to + * expose options regarding the type of MTE fault that can be controlled at + * runtime. + */ + arm_set_mte_tcf0(env, tcf); + + return 1; +} + +static void handle_q_memtag(GArray *params, void *user_ctx) +{ + ARMCPU *cpu = ARM_CPU(user_ctx); + CPUARMState *env = &cpu->env; + + uint64_t addr = gdb_get_cmd_param(params, 0)->val_ull; + uint64_t len = gdb_get_cmd_param(params, 1)->val_ul; + int type = gdb_get_cmd_param(params, 2)->val_ul; + + uint8_t *tags; + uint8_t addr_tag; + + g_autoptr(GString) str_buf = g_string_new(NULL); + + /* + * GDB does not query multiple tags for a memory range on remote targets, so + * that's not supported either by gdbstub. + */ + if (len != 1) { + gdb_put_packet("E02"); + } + + /* GDB never queries a tag different from an allocation tag (type 1). */ + if (type != 1) { + gdb_put_packet("E03"); + } + + /* Note that tags are packed here (2 tags packed in one byte). */ + tags = allocation_tag_mem_probe(env, 0, addr, MMU_DATA_LOAD, 8 /* 64-bit */, + MMU_DATA_LOAD, true, 0); + if (!tags) { + /* Address is not in a tagged region. */ + gdb_put_packet("E04"); + return; + } + + /* Unpack tag from byte. */ + addr_tag = load_tag1(addr, tags); + g_string_printf(str_buf, "m%.2x", addr_tag); + + gdb_put_packet(str_buf->str); +} + +static void handle_q_isaddresstagged(GArray *params, void *user_ctx) +{ + ARMCPU *cpu = ARM_CPU(user_ctx); + CPUARMState *env = &cpu->env; + + uint64_t addr = gdb_get_cmd_param(params, 0)->val_ull; + + uint8_t *tags; + const char *reply; + + tags = allocation_tag_mem_probe(env, 0, addr, MMU_DATA_LOAD, 8 /* 64-bit */, + MMU_DATA_LOAD, true, 0); + reply = tags ? "01" : "00"; + + gdb_put_packet(reply); +} + +static void handle_Q_memtag(GArray *params, void *user_ctx) +{ + ARMCPU *cpu = ARM_CPU(user_ctx); + CPUARMState *env = &cpu->env; + + uint64_t start_addr = gdb_get_cmd_param(params, 0)->val_ull; + uint64_t len = gdb_get_cmd_param(params, 1)->val_ul; + int type = gdb_get_cmd_param(params, 2)->val_ul; + char const *new_tags_str = gdb_get_cmd_param(params, 3)->data; + + uint64_t end_addr; + + int num_new_tags; + uint8_t *tags; + + g_autoptr(GByteArray) new_tags = g_byte_array_new(); + + /* + * Only the allocation tag (i.e. type 1) can be set at the stub side. + */ + if (type != 1) { + gdb_put_packet("E02"); + return; + } + + end_addr = start_addr + (len - 1); /* 'len' is always >= 1 */ + /* Check if request's memory range does not cross page boundaries. */ + if ((start_addr ^ end_addr) & TARGET_PAGE_MASK) { + gdb_put_packet("E03"); + return; + } + + /* + * Get all tags in the page starting from the tag of the start address. + * Note that there are two tags packed into a single byte here. + */ + tags = allocation_tag_mem_probe(env, 0, start_addr, MMU_DATA_STORE, + 8 /* 64-bit */, MMU_DATA_STORE, true, 0); + if (!tags) { + /* Address is not in a tagged region. */ + gdb_put_packet("E04"); + return; + } + + /* Convert tags provided by GDB, 2 hex digits per tag. */ + num_new_tags = strlen(new_tags_str) / 2; + gdb_hextomem(new_tags, new_tags_str, num_new_tags); + + uint64_t address = start_addr; + int new_tag_index = 0; + while (address <= end_addr) { + uint8_t new_tag; + int packed_index; + + /* + * Find packed tag index from unpacked tag index. There are two tags + * in one packed index (one tag per nibble). + */ + packed_index = new_tag_index / 2; + + new_tag = new_tags->data[new_tag_index % num_new_tags]; + store_tag1(address, tags + packed_index, new_tag); + + address += TAG_GRANULE; + new_tag_index++; + } + + gdb_put_packet("OK"); +} + +enum Command { + qMemTags, + qIsAddressTagged, + QMemTags, + NUM_CMDS +}; + +static GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = { + [qMemTags] = { + .handler = handle_q_memtag, + .cmd_startswith = true, + .cmd = "MemTags:", + .schema = "L,l:l0", + .need_cpu_context = true + }, + [qIsAddressTagged] = { + .handler = handle_q_isaddresstagged, + .cmd_startswith = true, + .cmd = "IsAddressTagged:", + .schema = "L0", + .need_cpu_context = true + }, + [QMemTags] = { + .handler = handle_Q_memtag, + .cmd_startswith = true, + .cmd = "MemTags:", + .schema = "L,l:l:s0", + .need_cpu_context = true + }, +}; +#endif /* CONFIG_USER_ONLY */ + +void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *qsupported, + GArray *qtable, GArray *stable) +{ +#ifdef CONFIG_USER_ONLY + /* MTE */ + if (cpu_isar_feature(aa64_mte, cpu)) { + g_string_append(qsupported, ";memory-tagging+"); + + g_array_append_val(qtable, cmd_handler_table[qMemTags]); + g_array_append_val(qtable, cmd_handler_table[qIsAddressTagged]); + + g_array_append_val(stable, cmd_handler_table[QMemTags]); + } +#endif +} diff --git a/target/arm/internals.h b/target/arm/internals.h index 11b5da2562..e1aa1a63b9 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -358,6 +358,10 @@ void init_cpreg_list(ARMCPU *cpu); void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); +void arm_cpu_register_gdb_commands(ARMCPU *cpu); +void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, GArray *, + GArray *); + void arm_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data); @@ -1640,6 +1644,8 @@ int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg); int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_pauth_reg(CPUState *cs, uint8_t *buf, int reg); +int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg); void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp); void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp); void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp); From 340ca46b681b1e9cac1643a7fd964947aeb68a56 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 5 Jul 2024 09:40:47 +0100 Subject: [PATCH 2012/2884] tests/tcg/aarch64: Add MTE gdbstub tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests to exercise the MTE stubs. The tests will only run if a version of GDB that supports MTE is available in the test environment. Signed-off-by: Gustavo Romero [AJB: re-base and checkpatch fixes] Message-Id: <20240628050850.536447-12-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée Message-Id: <20240705084047.857176-41-alex.bennee@linaro.org> --- configure | 4 ++ tests/tcg/aarch64/Makefile.target | 14 +++- tests/tcg/aarch64/gdbstub/test-mte.py | 86 +++++++++++++++++++++++ tests/tcg/aarch64/mte-8.c | 99 +++++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/aarch64/gdbstub/test-mte.py create mode 100644 tests/tcg/aarch64/mte-8.c diff --git a/configure b/configure index 8b6a2f16ce..019fcbd0ef 100755 --- a/configure +++ b/configure @@ -1673,6 +1673,10 @@ for target in $target_list; do echo "GDB=$gdb_bin" >> $config_target_mak fi + if test "${arch}" = "aarch64" && version_ge ${gdb_version##* } 15.0; then + echo "GDB_HAS_MTE=y" >> $config_target_mak + fi + echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> Makefile.prereqs tcg_tests_targets="$tcg_tests_targets $target" fi diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index ad1774c2ce..b53218e115 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -64,7 +64,7 @@ AARCH64_TESTS += bti-2 # MTE Tests ifneq ($(CROSS_CC_HAS_ARMV8_MTE),) -AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 +AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 mte-8 mte-%: CFLAGS += $(CROSS_CC_HAS_ARMV8_MTE) endif @@ -131,6 +131,18 @@ run-gdbstub-sve-ioctls: sve-ioctls basic gdbstub SVE ZLEN support) EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls + +ifeq ($(GDB_HAS_MTE),y) +run-gdbstub-mte: mte-8 + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py, \ + gdbstub MTE support) + +EXTRA_RUNS += run-gdbstub-mte +endif + endif endif diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py b/tests/tcg/aarch64/gdbstub/test-mte.py new file mode 100644 index 0000000000..2db0663c1a --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-mte.py @@ -0,0 +1,86 @@ +from __future__ import print_function +# +# Test GDB memory-tag commands that exercise the stubs for the qIsAddressTagged, +# qMemTag, and QMemTag packets. Logical tag-only commands rely on local +# operations, hence don't exercise any stub. +# +# The test consists in breaking just after a atag() call (which sets the +# allocation tag -- see mte-8.c for details) and setting/getting tags in +# different memory locations and ranges starting at the address of the array +# 'a'. +# +# This is launched via tests/guest-debug/run-test.py +# + + +import gdb +import re +from test_gdbstub import main, report + + +PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)." +PATTERN_1 = ".*(0x[0-9a-f]+)" + + +def run_test(): + gdb.execute("break 95", False, True) + gdb.execute("continue", False, True) + try: + # Test if we can check correctly that the allocation tag for + # array 'a' matches the logical tag after atag() is called. + co = gdb.execute("memory-tag check a", False, True) + tags_match = re.findall(PATTERN_0, co, re.MULTILINE) + if tags_match: + report(True, f"{tags_match[0]}") + else: + report(False, "Logical and allocation tags don't match!") + + # Test allocation tag 'set and print' commands. Commands on logical + # tags rely on local operation and so don't exercise any stub. + + # Set the allocation tag for the first granule (16 bytes) of + # address starting at 'a' address to a known value, i.e. 0x04. + gdb.execute("memory-tag set-allocation-tag a 1 04", False, True) + + # Then set the allocation tag for the second granule to a known + # value, i.e. 0x06. This tests that contiguous tag granules are + # set correct and don't run over each other. + gdb.execute("memory-tag set-allocation-tag a+16 1 06", False, True) + + # Read the known values back and check if they remain the same. + + co = gdb.execute("memory-tag print-allocation-tag a", False, True) + first_tag = re.match(PATTERN_1, co)[1] + + co = gdb.execute("memory-tag print-allocation-tag a+16", False, True) + second_tag = re.match(PATTERN_1, co)[1] + + if first_tag == "0x4" and second_tag == "0x6": + report(True, "Allocation tags are correctly set/printed.") + else: + report(False, "Can't set/print allocation tags!") + + # Now test fill pattern by setting a whole page with a pattern. + gdb.execute("memory-tag set-allocation-tag a 4096 0a0b", False, True) + + # And read back the tags of the last two granules in page so + # we also test if the pattern is set correctly up to the end of + # the page. + co = gdb.execute("memory-tag print-allocation-tag a+4096-32", False, True) + tag = re.match(PATTERN_1, co)[1] + + co = gdb.execute("memory-tag print-allocation-tag a+4096-16", False, True) + last_tag = re.match(PATTERN_1, co)[1] + + if tag == "0xa" and last_tag == "0xb": + report(True, "Fill pattern is ok.") + else: + report(False, "Fill pattern failed!") + + except gdb.error: + # This usually happens because a GDB version that does not + # support memory tagging was used to run the test. + report(False, "'memory-tag' command failed!") + + +main(run_test, expected_arch="aarch64") diff --git a/tests/tcg/aarch64/mte-8.c b/tests/tcg/aarch64/mte-8.c new file mode 100644 index 0000000000..808135ba43 --- /dev/null +++ b/tests/tcg/aarch64/mte-8.c @@ -0,0 +1,99 @@ +/* + * To be compiled with -march=armv8.5-a+memtag + * + * This test is adapted from a Linux test. Please see: + * + * https://www.kernel.org/doc/html/next/arch/arm64/memory-tagging-extension.html#example-of-correct-usage + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * From arch/arm64/include/uapi/asm/hwcap.h + */ +#define HWCAP2_MTE (1 << 18) + +/* + * From arch/arm64/include/uapi/asm/mman.h + */ +#define PROT_MTE 0x20 + +/* + * Insert a random logical tag into the given pointer. + */ +#define insert_random_tag(ptr) ({ \ + uint64_t __val; \ + asm("irg %0, %1" : "=r" (__val) : "r" (ptr)); \ + __val; \ +}) + +/* + * Set the allocation tag on the destination address. + */ +#define set_tag(tagged_addr) do { \ + asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ +} while (0) + + +int main(int argc, char *argv[]) +{ + unsigned char *a; + unsigned long page_sz = sysconf(_SC_PAGESIZE); + unsigned long hwcap2 = getauxval(AT_HWCAP2); + + /* check if MTE is present */ + if (!(hwcap2 & HWCAP2_MTE)) { + return EXIT_FAILURE; + } + + /* + * Enable the tagged address ABI, synchronous or asynchronous MTE + * tag check faults (based on per-CPU preference) and allow all + * non-zero tags in the randomly generated set. + */ + if (prctl(PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC | + (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) { + perror("prctl() failed"); + return EXIT_FAILURE; + } + + a = mmap(0, page_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (a == MAP_FAILED) { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + printf("a[] address is %p\n", a); + + /* + * Enable MTE on the above anonymous mmap. The flag could be passed + * directly to mmap() and skip this step. + */ + if (mprotect(a, page_sz, PROT_READ | PROT_WRITE | PROT_MTE)) { + perror("mprotect() failed"); + return EXIT_FAILURE; + } + + /* access with the default tag (0) */ + a[0] = 1; + a[1] = 2; + + printf("a[0] = %hhu a[1] = %hhu\n", a[0], a[1]); + + /* set the logical and allocation tags */ + a = (unsigned char *)insert_random_tag(a); + set_tag(a); + + printf("%p\n", a); + + return 0; +} From bb8dacedd51c75f03c95b5c0b22d92e9f14527ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 2 Jul 2024 14:28:30 +0200 Subject: [PATCH 2013/2884] hw/sd/sdhci: Log non-sequencial access as GUEST_ERROR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Message-Id: <20240702140842.54242-3-philmd@linaro.org> --- hw/sd/sdhci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 27673e1c70..d02c3e3963 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -983,8 +983,9 @@ static inline bool sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num) { if ((s->data_count & 0x3) != byte_num) { - trace_sdhci_error("Non-sequential access to Buffer Data Port register" - "is prohibited\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "SDHCI: Non-sequential access to Buffer Data Port" + " register is prohibited\n"); return false; } return true; From f28cfc39d7ed7ef717e49d48e703538692c53238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 2 Jul 2024 14:28:14 +0200 Subject: [PATCH 2014/2884] hw/sd/npcm7xx_sdhci: Use TYPE_SYSBUS_SDHCI definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the macro instead of two explicit string literals. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Message-Id: <20240702140842.54242-2-philmd@linaro.org> --- hw/sd/npcm7xx_sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c index e93dab8dbd..fb51821e11 100644 --- a/hw/sd/npcm7xx_sdhci.c +++ b/hw/sd/npcm7xx_sdhci.c @@ -16,6 +16,7 @@ #include "qemu/osdep.h" +#include "hw/sd/sdhci.h" #include "hw/sd/npcm7xx_sdhci.h" #include "migration/vmstate.h" #include "sdhci-internal.h" @@ -162,7 +163,7 @@ static void npcm7xx_sdhci_instance_init(Object *obj) { NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj); - object_initialize_child(OBJECT(s), "generic-sdhci", &s->sdhci, + object_initialize_child(OBJECT(s), TYPE_SYSBUS_SDHCI, &s->sdhci, TYPE_SYSBUS_SDHCI); } From 3baae281501f6eea879bff2200ca15ee2ff637be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 2 Jul 2024 15:06:04 +0200 Subject: [PATCH 2015/2884] tests/qtest/npcm7xx_sdhci: Access the card using its published address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently setup_sd_card() asks the card its address, but discard the response and use hardcoded 0x4567. Set the SDHC_CMD_RESPONSE bit to have the controller record the bus response, and read the response from the RSPREG0 register. Then we can select the card with its real address. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Message-Id: <20240702140842.54242-4-philmd@linaro.org> --- tests/qtest/libqos/sdhci-cmd.h | 2 ++ tests/qtest/npcm7xx_sdhci-test.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/qtest/libqos/sdhci-cmd.h b/tests/qtest/libqos/sdhci-cmd.h index 9e61dd4944..90efa028ef 100644 --- a/tests/qtest/libqos/sdhci-cmd.h +++ b/tests/qtest/libqos/sdhci-cmd.h @@ -22,6 +22,7 @@ #define SDHC_ARGUMENT 0x08 #define SDHC_TRNMOD 0x0C #define SDHC_CMDREG 0x0E +#define SDHC_RSPREG0 0x10 #define SDHC_BDATA 0x20 #define SDHC_PRNSTS 0x24 #define SDHC_BLKGAP 0x2A @@ -38,6 +39,7 @@ #define SDHC_TRNS_MULTI 0x0020 /* CMD Reg */ +#define SDHC_CMD_RESPONSE (3 << 0) #define SDHC_CMD_DATA_PRESENT (1 << 5) #define SDHC_ALL_SEND_CID (2 << 8) #define SDHC_SEND_RELATIVE_ADDR (3 << 8) diff --git a/tests/qtest/npcm7xx_sdhci-test.c b/tests/qtest/npcm7xx_sdhci-test.c index 5d68540e52..01f237a816 100644 --- a/tests/qtest/npcm7xx_sdhci-test.c +++ b/tests/qtest/npcm7xx_sdhci-test.c @@ -30,6 +30,8 @@ char *sd_path; static QTestState *setup_sd_card(void) { + uint16_t rca; + QTestState *qts = qtest_initf( "-machine kudo-bmc " "-device sd-card,drive=drive0 " @@ -43,8 +45,10 @@ static QTestState *setup_sd_card(void) sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); - sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR); - sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0, + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR + | SDHC_CMD_RESPONSE); + rca = qtest_readl(qts, NPCM7XX_MMC_BA + SDHC_RSPREG0) >> 16; + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, rca << 16, 0, SDHC_SELECT_DESELECT_CARD); return qts; From 6d0dc86070d6b65caaf2c96d7254ead5fc519203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 17:57:54 +0200 Subject: [PATCH 2016/2884] hw/sd/sdcard: Generate random RCA value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using the obscure 0x4567 magic value, use a real random one. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Message-Id: <20240702140842.54242-5-philmd@linaro.org> --- hw/sd/sd.c | 11 ++++++++--- hw/sd/trace-events | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 552957b2e5..b158402704 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -46,6 +46,7 @@ #include "qemu/error-report.h" #include "qemu/timer.h" #include "qemu/log.h" +#include "qemu/guest-random.h" #include "qemu/module.h" #include "sdmmc-internal.h" #include "trace.h" @@ -515,9 +516,10 @@ static void sd_set_csd(SDState *sd, uint64_t size) /* Relative Card Address register */ -static void sd_set_rca(SDState *sd) +static void sd_set_rca(SDState *sd, uint16_t value) { - sd->rca += 0x4567; + trace_sdcard_set_rca(value); + sd->rca = value; } static uint16_t sd_req_get_rca(SDState *s, SDRequest req) @@ -1212,11 +1214,14 @@ static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) /* CMD3 */ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) { + uint16_t random_rca; + switch (sd->state) { case sd_identification_state: case sd_standby_state: sd->state = sd_standby_state; - sd_set_rca(sd); + qemu_guest_getrandom_nofail(&random_rca, sizeof(random_rca)); + sd_set_rca(sd, random_rca); return sd_r6; default: diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 43eaeba149..6a51b0e906 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -43,6 +43,7 @@ sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" sdcard_reset(void) "" +sdcard_set_rca(uint16_t value) "new RCA: 0x%04x" sdcard_set_blocklen(uint16_t length) "block len 0x%03x" sdcard_set_block_count(uint32_t cnt) "block cnt 0x%"PRIx32 sdcard_inserted(bool readonly) "read_only: %u" From a5487ddba583ea68f50cd871c0a8fcdff18aaf4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Jul 2024 09:59:49 +0200 Subject: [PATCH 2017/2884] hw/sd/sdcard: Remove leftover comment about removed 'spi' Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit c3287c0f70 ("hw/sd: Introduce a "sd-card" SPI variant model") removed the 'spi' property. Remove the comment left over. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Tested-by: Andrew Jeffery Message-Id: <20240703085907.66775-2-philmd@linaro.org> --- hw/sd/sd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b158402704..53767beaf8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2473,10 +2473,6 @@ static Property sd_properties[] = { DEFINE_PROP_UINT8("spec_version", SDState, spec_version, SD_PHY_SPECv2_00_VERS), DEFINE_PROP_DRIVE("drive", SDState, blk), - /* We do not model the chip select pin, so allow the board to select - * whether card should be in SSI or MMC/SD mode. It is also up to the - * board to ensure that ssi transfers only occur when the chip select - * is asserted. */ DEFINE_PROP_END_OF_LIST() }; From 1ca19583e7a043b1414b9ecf5663384103d57a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 09:03:33 +0200 Subject: [PATCH 2018/2884] hw/sd/sdcard: Use spec v3.01 by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent SDHCI expect cards to support the v3.01 spec to negociate lower I/O voltage. Select it by default. Versioned machine types with a version of 9.0 or earlier retain the old default (spec v2.00). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240703134356.85972-2-philmd@linaro.org> --- hw/core/machine.c | 1 + hw/sd/sd.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index f4cba6496c..bc38cad7f2 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -39,6 +39,7 @@ GlobalProperty hw_compat_9_0[] = { {"scsi-disk-base", "migrate-emulated-scsi-request", "false" }, {"vfio-pci", "skip-vsc-check", "false" }, { "virtio-pci", "x-pcie-pm-no-soft-reset", "off" }, + {"sd-card", "spec_version", "2" }, }; const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 53767beaf8..a08a452d81 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2471,7 +2471,7 @@ static void sd_realize(DeviceState *dev, Error **errp) static Property sd_properties[] = { DEFINE_PROP_UINT8("spec_version", SDState, - spec_version, SD_PHY_SPECv2_00_VERS), + spec_version, SD_PHY_SPECv3_01_VERS), DEFINE_PROP_DRIVE("drive", SDState, blk), DEFINE_PROP_END_OF_LIST() }; From a7487722dc1c6bd278feb0a1ec58e9455cb3daa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Jul 2024 14:56:25 +0200 Subject: [PATCH 2019/2884] hw/sd/sdcard: Rename sd_cmd_SEND_OP_COND handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct command name is 'SD SEND_OP_COND', rename accordingly. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240703134356.85972-4-philmd@linaro.org> --- hw/sd/sd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a08a452d81..8618c2bcf0 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1689,7 +1689,7 @@ static sd_rsp_type_t sd_acmd_SET_WR_BLK_ERASE_COUNT(SDState *sd, SDRequest req) } /* ACMD41 */ -static sd_rsp_type_t sd_acmd_SD_APP_OP_COND(SDState *sd, SDRequest req) +static sd_rsp_type_t sd_cmd_SEND_OP_COND(SDState *sd, SDRequest req) { if (sd->state != sd_idle_state) { return sd_invalid_state_for_cmd(sd, req); @@ -2392,7 +2392,7 @@ static const SDProto sd_proto_sd = { [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS}, [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, - [41] = {8, sd_bcr, "SD_APP_OP_COND", sd_acmd_SD_APP_OP_COND}, + [41] = {8, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [42] = {8, sd_ac, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, [51] = {8, sd_adtc, "SEND_SCR", sd_acmd_SEND_SCR}, }, From e7364ae74c51666e83f43975decb61b7f66cd409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 11:57:54 +0200 Subject: [PATCH 2020/2884] hw/sd/sdcard: Add sd_cmd_GEN_CMD handler (CMD56) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "General command" (GEN_CMD, CMD56) is described as: GEN_CMD is the same as the single block read or write commands (CMD24 or CMD17). The difference is that [...] the data block is not a memory payload data but has a vendor specific format and meaning. Thus this block must not be stored overwriting data block on underlying storage drive. Handle as RAZ/WI. Signed-off-by: Philippe Mathieu-Daudé Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240703134356.85972-3-philmd@linaro.org> --- hw/sd/sd.c | 54 ++++++++++++++++++++---------------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8618c2bcf0..10f2764a53 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -243,7 +243,6 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) [25] = "WRITE_MULTIPLE_BLOCK", [26] = "MANUF_RSVD", [40] = "DPS_spec", - [56] = "GEN_CMD", [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", }; @@ -902,9 +901,6 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) } } -#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len) -#define APP_WRITE_BLOCK(a, len) - static void sd_erase(SDState *sd) { uint64_t erase_start = sd->erase_start; @@ -1641,6 +1637,22 @@ static sd_rsp_type_t sd_cmd_APP_CMD(SDState *sd, SDRequest req) return sd_r1; } +/* CMD56 */ +static sd_rsp_type_t sd_cmd_GEN_CMD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + /* Vendor specific command: our model is RAZ/WI */ + if (req.arg & 1) { + memset(sd->data, 0, sizeof(sd->data)); + return sd_cmd_to_sendingdata(sd, req, 0, NULL, 0); + } else { + return sd_cmd_to_receivingdata(sd, req, 0, 0); + } +} + /* CMD58 */ static sd_rsp_type_t spi_cmd_READ_OCR(SDState *sd, SDRequest req) { @@ -1836,22 +1848,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 26: /* CMD26: PROGRAM_CID */ return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); - /* Application specific commands (Class 8) */ - case 56: /* CMD56: GEN_CMD */ - switch (sd->state) { - case sd_transfer_state: - sd->data_offset = 0; - if (req.arg & 1) - sd->state = sd_sendingdata_state; - else - sd->state = sd_receivingdata_state; - return sd_r1; - - default: - break; - } - break; - default: qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; @@ -2187,11 +2183,7 @@ void sd_write_byte(SDState *sd, uint8_t value) break; case 56: /* CMD56: GEN_CMD */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { - APP_WRITE_BLOCK(sd->data_start, sd->data_offset); - sd->state = sd_transfer_state; - } + sd_generic_write_byte(sd, value); break; default: @@ -2233,6 +2225,7 @@ uint8_t sd_read_byte(SDState *sd) case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ case 30: /* CMD30: SEND_WRITE_PROT */ case 51: /* ACMD51: SEND_SCR */ + case 56: /* CMD56: GEN_CMD */ sd_generic_read_byte(sd, &ret); break; @@ -2260,15 +2253,6 @@ uint8_t sd_read_byte(SDState *sd) } break; - case 56: /* CMD56: GEN_CMD */ - if (sd->data_offset == 0) - APP_READ_BLOCK(sd->data_start, sd->blk_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= sd->blk_len) - sd->state = sd_transfer_state; - break; - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__); return 0x00; @@ -2323,6 +2307,7 @@ static const SDProto sd_proto_spi = { [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, [55] = {8, sd_spi, "APP_CMD", sd_cmd_APP_CMD}, + [56] = {8, sd_spi, "GEN_CMD", sd_cmd_GEN_CMD}, [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional}, [58] = {0, sd_spi, "READ_OCR", spi_cmd_READ_OCR}, [59] = {0, sd_spi, "CRC_ON_OFF", spi_cmd_CRC_ON_OFF}, @@ -2383,6 +2368,7 @@ static const SDProto sd_proto_sd = { [52] = {9, sd_bc, "IO_RW_DIRECT", sd_cmd_optional}, [53] = {9, sd_bc, "IO_RW_EXTENDED", sd_cmd_optional}, [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD}, + [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD}, [57] = {10, sd_adtc, "DIRECT_SECURE_WRITE", sd_cmd_optional}, [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, From 69372c7e3345f29599e040a7cc629bdf350c3518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Jun 2024 21:58:12 +0200 Subject: [PATCH 2021/2884] hw/sd/sdcard: Remove sd_none enum from sd_cmd_type_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All handlers using the 'sd_none' enum got converted, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-76-philmd@linaro.org> --- hw/sd/sd.c | 7 +------ include/hw/sd/sd.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 10f2764a53..43f60cf089 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -524,17 +524,12 @@ static void sd_set_rca(SDState *sd, uint16_t value) static uint16_t sd_req_get_rca(SDState *s, SDRequest req) { switch (s->proto->cmd[req.cmd].type) { - case sd_none: - /* Called from legacy code not ported to SDProto array */ - assert(!s->proto->cmd[req.cmd].handler); - /* fall-through */ case sd_ac: case sd_adtc: return req.arg >> 16; case sd_spi: - g_assert_not_reached(); default: - return 0; + g_assert_not_reached(); } } diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 29c76935a0..c1a35ab420 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -76,7 +76,6 @@ typedef enum { } sd_uhs_mode_t; typedef enum { - sd_none = 0, sd_spi, sd_bc, /* broadcast -- no response */ sd_bcr, /* broadcast with response */ From e84e0c4df57103a9047e607eeff105cbf0fbb923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 26 Jun 2024 23:51:55 +0200 Subject: [PATCH 2022/2884] hw/sd/sdcard: Remove noise from sd_acmd_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These ACMD names weren't really useful, "UNKNOWN_ACMD" is simpler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-77-philmd@linaro.org> --- hw/sd/sd.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 43f60cf089..274a917ba6 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -257,23 +257,13 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) static const char *sd_acmd_name(SDState *sd, uint8_t cmd) { - static const char *acmd_abbrev[SDMMC_CMD_MAX] = { - [14] = "DPS_spec", [15] = "DPS_spec", - [16] = "DPS_spec", - [18] = "SECU_spec", - [52] = "SECU_spec", [53] = "SECU_spec", - [54] = "SECU_spec", - [56] = "SECU_spec", [57] = "SECU_spec", - [58] = "SECU_spec", [59] = "SECU_spec", - }; const SDProto *sdp = sd->proto; if (sdp->acmd[cmd].handler) { - assert(!acmd_abbrev[cmd]); return sdp->acmd[cmd].name; } - return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD"; + return "UNKNOWN_ACMD"; } static uint8_t sd_get_dat_lines(SDState *sd) From 48741580a8e4b7e18406962425ce608e2ef0c6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 26 Jun 2024 23:52:36 +0200 Subject: [PATCH 2023/2884] hw/sd/sdcard: Remove noise from sd_cmd_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These CMD names weren't really useful, "UNKNOWN_CMD" is simpler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-78-philmd@linaro.org> --- hw/sd/sd.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 274a917ba6..bc1a574b62 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -239,12 +239,7 @@ static const char *sd_cmd_name(SDState *sd, uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { [18] = "READ_MULTIPLE_BLOCK", - [21] = "DPS_spec", [25] = "WRITE_MULTIPLE_BLOCK", - [26] = "MANUF_RSVD", - [40] = "DPS_spec", - [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", - [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", }; const SDProto *sdp = sd->proto; From e2dec2eab04db00ad718353b77adfd59dd5ee3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 16 Jun 2024 12:39:18 +0200 Subject: [PATCH 2024/2884] hw/sd/sdcard: Remove default case in read/write on DAT lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All read/write on DAT lines are explicitly handled. Reaching this point would be a programming error: replace by an assertion. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-79-philmd@linaro.org> --- hw/sd/sd.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index bc1a574b62..3f495f91fe 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1949,7 +1949,6 @@ int sd_do_command(SDState *sd, SDRequest *req, /* Valid command, we can update the 'state before command' bits. * (Do this now so they appear in r1 responses.) */ - sd->current_cmd = req->cmd; sd->card_status = FIELD_DP32(sd->card_status, CSR, CURRENT_STATE, last_state); } @@ -2014,6 +2013,8 @@ send_response: qemu_hexdump(stderr, "Response", response, rsplen); #endif + sd->current_cmd = rtype == sd_illegal ? 0 : req->cmd; + return rsplen; } @@ -2167,8 +2168,7 @@ void sd_write_byte(SDState *sd, uint8_t value) break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__); - break; + g_assert_not_reached(); } } @@ -2234,8 +2234,7 @@ uint8_t sd_read_byte(SDState *sd) break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__); - return 0x00; + g_assert_not_reached(); } return ret; From 2a97045b483e1844dedb81aaf3f4e01d2c3249f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jun 2024 09:28:52 +0200 Subject: [PATCH 2025/2884] hw/sd/sdcard: Trace length of data read on DAT lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some commands expect less than BLOCK_LENGTH. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-80-philmd@linaro.org> --- hw/sd/sd.c | 4 ++-- hw/sd/trace-events | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 3f495f91fe..9e6b9c9c63 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2193,8 +2193,8 @@ uint8_t sd_read_byte(SDState *sd) io_len = sd_blk_len(sd); trace_sdcard_read_data(sd->proto->name, - sd->last_cmd_name, - sd->current_cmd, sd->data_offset, io_len); + sd->last_cmd_name, sd->current_cmd, + sd->data_offset, sd->data_size, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ case 9: /* CMD9: SEND_CSD */ diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 6a51b0e906..5dfe6be7b7 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -55,7 +55,7 @@ sdcard_req_addr(uint32_t req_arg, uint64_t addr) "req 0x%" PRIx32 " addr 0x%" PR sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint8_t value) "%s %20s/ CMD%02d ofs %"PRIu32" value 0x%02x" -sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint32_t length) "%s %20s/ CMD%02d ofs %"PRIu32" len %" PRIu32 +sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint64_t size, uint32_t blklen) "%s %20s/ CMD%02d ofs %"PRIu32" size %"PRIu64" blklen %" PRIu32 sdcard_set_voltage(uint16_t millivolts) "%u mV" # pxa2xx_mmci.c From f18a78de24244986ab37c017f5a56a83d93ec779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Jun 2024 12:05:21 +0200 Subject: [PATCH 2026/2884] hw/sd/sdcard: Cover more SDCardStates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far eMMC will only use sd_sleep_state, but all all states specified for completeness. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-81-philmd@linaro.org> --- hw/sd/sd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 9e6b9c9c63..ecb1b2f405 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -76,7 +76,9 @@ enum SDCardModes { }; enum SDCardStates { + sd_waitirq_state = -2, /* emmc */ sd_inactive_state = -1, + sd_idle_state = 0, sd_ready_state = 1, sd_identification_state = 2, @@ -86,6 +88,9 @@ enum SDCardStates { sd_receivingdata_state = 6, sd_programming_state = 7, sd_disconnect_state = 8, + sd_bus_test_state = 9, /* emmc */ + sd_sleep_state = 10, /* emmc */ + sd_io_state = 15 /* sd */ }; #define SDMMC_CMD_MAX 64 @@ -203,13 +208,19 @@ static const char *sd_state_name(enum SDCardStates state) [sd_standby_state] = "standby", [sd_transfer_state] = "transfer", [sd_sendingdata_state] = "sendingdata", + [sd_bus_test_state] = "bus-test", [sd_receivingdata_state] = "receivingdata", [sd_programming_state] = "programming", [sd_disconnect_state] = "disconnect", + [sd_sleep_state] = "sleep", + [sd_io_state] = "i/o" }; if (state == sd_inactive_state) { return "inactive"; } + if (state == sd_waitirq_state) { + return "wait-irq"; + } assert(state < ARRAY_SIZE(state_name)); return state_name[state]; } From 5241b759bcf1e9952a5a5503bc802d76409b4ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jun 2024 15:49:10 +0200 Subject: [PATCH 2027/2884] hw/sd/sdcard: Introduce set_csd/set_cid handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of introducing eMMC support which have different CSD/CID structures, introduce a pair of handlers in SDCardClass. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240628070216.92609-82-philmd@linaro.org> --- hw/sd/sd.c | 7 +++++-- include/hw/sd/sd.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index ecb1b2f405..058a70a0f8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -680,6 +680,7 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr) static void sd_reset(DeviceState *dev) { SDState *sd = SD_CARD(dev); + SDCardClass *sc = SD_CARD_GET_CLASS(sd); uint64_t size; uint64_t sect; @@ -700,8 +701,8 @@ static void sd_reset(DeviceState *dev) sd->size = size; sd_set_ocr(sd); sd_set_scr(sd); - sd_set_cid(sd); - sd_set_csd(sd, size); + sc->set_cid(sd); + sc->set_csd(sd, size); sd_set_cardstatus(sd); sd_set_sdstatus(sd); @@ -2475,6 +2476,8 @@ static void sd_class_init(ObjectClass *klass, void *data) sc->enable = sd_enable; sc->get_inserted = sd_get_inserted; sc->get_readonly = sd_get_readonly; + sc->set_cid = sd_set_cid; + sc->set_csd = sd_set_csd; sc->proto = &sd_proto_sd; } diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index c1a35ab420..0d6d9e452b 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -127,6 +127,8 @@ struct SDCardClass { void (*enable)(SDState *sd, bool enable); bool (*get_inserted)(SDState *sd); bool (*get_readonly)(SDState *sd); + void (*set_cid)(SDState *sd); + void (*set_csd)(SDState *sd, uint64_t size); const struct SDProto *proto; }; From 0bcea3f74b04cdc23ecd6822bea7e46a55eb4be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Jul 2024 11:12:00 +0200 Subject: [PATCH 2028/2884] hw/sd/sdcard: Extract TYPE_SDMMC_COMMON from TYPE_SD_CARD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to keep eMMC model simpler to maintain, extract common properties and the common code from class_init to the (internal) TYPE_SDMMC_COMMON. Update the corresponding QOM cast macros. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Andrew Jeffery Tested-by: Cédric Le Goater Message-Id: <20240703134356.85972-6-philmd@linaro.org> --- hw/sd/core.c | 29 ++++++++++++------------ hw/sd/sd.c | 51 +++++++++++++++++++++++++++++------------- hw/sd/sdmmc-internal.h | 3 +++ 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/hw/sd/core.c b/hw/sd/core.c index 52d5d90045..4b30218b52 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -24,6 +24,7 @@ #include "hw/sd/sd.h" #include "qemu/module.h" #include "qapi/error.h" +#include "sdmmc-internal.h" #include "trace.h" static inline const char *sdbus_name(SDBus *sdbus) @@ -39,7 +40,7 @@ static SDState *get_card(SDBus *sdbus) if (!kid) { return NULL; } - return SD_CARD(kid->child); + return SDMMC_COMMON(kid->child); } uint8_t sdbus_get_dat_lines(SDBus *sdbus) @@ -48,7 +49,7 @@ uint8_t sdbus_get_dat_lines(SDBus *sdbus) uint8_t dat_lines = 0b1111; /* 4 bit bus width */ if (slave) { - SDCardClass *sc = SD_CARD_GET_CLASS(slave); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(slave); if (sc->get_dat_lines) { dat_lines = sc->get_dat_lines(slave); @@ -65,7 +66,7 @@ bool sdbus_get_cmd_line(SDBus *sdbus) bool cmd_line = true; if (slave) { - SDCardClass *sc = SD_CARD_GET_CLASS(slave); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(slave); if (sc->get_cmd_line) { cmd_line = sc->get_cmd_line(slave); @@ -82,7 +83,7 @@ void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts) trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); assert(sc->set_voltage); sc->set_voltage(card, millivolts); @@ -95,7 +96,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->do_command(card, req, response); } @@ -109,7 +110,7 @@ void sdbus_write_byte(SDBus *sdbus, uint8_t value) trace_sdbus_write(sdbus_name(sdbus), value); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); sc->write_byte(card, value); } @@ -121,7 +122,7 @@ void sdbus_write_data(SDBus *sdbus, const void *buf, size_t length) const uint8_t *data = buf; if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); for (size_t i = 0; i < length; i++) { trace_sdbus_write(sdbus_name(sdbus), data[i]); @@ -136,7 +137,7 @@ uint8_t sdbus_read_byte(SDBus *sdbus) uint8_t value = 0; if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); value = sc->read_byte(card); } @@ -151,7 +152,7 @@ void sdbus_read_data(SDBus *sdbus, void *buf, size_t length) uint8_t *data = buf; if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); for (size_t i = 0; i < length; i++) { data[i] = sc->read_byte(card); @@ -165,7 +166,7 @@ bool sdbus_receive_ready(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->receive_ready(card); } @@ -178,7 +179,7 @@ bool sdbus_data_ready(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->data_ready(card); } @@ -191,7 +192,7 @@ bool sdbus_get_inserted(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->get_inserted(card); } @@ -204,7 +205,7 @@ bool sdbus_get_readonly(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->get_readonly(card); } @@ -250,7 +251,7 @@ void sdbus_reparent_card(SDBus *from, SDBus *to) return; } - sc = SD_CARD_GET_CLASS(card); + sc = SDMMC_COMMON_GET_CLASS(card); readonly = sc->get_readonly(card); sdbus_set_inserted(from, false); diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 058a70a0f8..d6a07f0ade 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -679,8 +679,8 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr) static void sd_reset(DeviceState *dev) { - SDState *sd = SD_CARD(dev); - SDCardClass *sc = SD_CARD_GET_CLASS(sd); + SDState *sd = SDMMC_COMMON(dev); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(sd); uint64_t size; uint64_t sect; @@ -2377,8 +2377,8 @@ static const SDProto sd_proto_sd = { static void sd_instance_init(Object *obj) { - SDState *sd = SD_CARD(obj); - SDCardClass *sc = SD_CARD_GET_CLASS(sd); + SDState *sd = SDMMC_COMMON(obj); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(sd); sd->proto = sc->proto; sd->last_cmd_name = "UNSET"; @@ -2388,14 +2388,14 @@ static void sd_instance_init(Object *obj) static void sd_instance_finalize(Object *obj) { - SDState *sd = SD_CARD(obj); + SDState *sd = SDMMC_COMMON(obj); timer_free(sd->ocr_power_timer); } static void sd_realize(DeviceState *dev, Error **errp) { - SDState *sd = SD_CARD(dev); + SDState *sd = SDMMC_COMMON(dev); int ret; switch (sd->spec_version) { @@ -2446,20 +2446,23 @@ static void sd_realize(DeviceState *dev, Error **errp) } } -static Property sd_properties[] = { - DEFINE_PROP_UINT8("spec_version", SDState, - spec_version, SD_PHY_SPECv3_01_VERS), +static Property sdmmc_common_properties[] = { DEFINE_PROP_DRIVE("drive", SDState, blk), DEFINE_PROP_END_OF_LIST() }; -static void sd_class_init(ObjectClass *klass, void *data) +static Property sd_properties[] = { + DEFINE_PROP_UINT8("spec_version", SDState, + spec_version, SD_PHY_SPECv3_01_VERS), + DEFINE_PROP_END_OF_LIST() +}; + +static void sdmmc_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SDCardClass *sc = SD_CARD_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); - dc->realize = sd_realize; - device_class_set_props(dc, sd_properties); + device_class_set_props(dc, sdmmc_common_properties); dc->vmsd = &sd_vmstate; dc->reset = sd_reset; dc->bus_type = TYPE_SD_BUS; @@ -2476,6 +2479,16 @@ static void sd_class_init(ObjectClass *klass, void *data) sc->enable = sd_enable; sc->get_inserted = sd_get_inserted; sc->get_readonly = sd_get_readonly; +} + +static void sd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); + + dc->realize = sd_realize; + device_class_set_props(dc, sd_properties); + sc->set_cid = sd_set_cid; sc->set_csd = sd_set_csd; sc->proto = &sd_proto_sd; @@ -2490,7 +2503,7 @@ static void sd_class_init(ObjectClass *klass, void *data) static void sd_spi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SDCardClass *sc = SD_CARD_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); dc->desc = "SD SPI"; sc->proto = &sd_proto_spi; @@ -2498,14 +2511,20 @@ static void sd_spi_class_init(ObjectClass *klass, void *data) static const TypeInfo sd_types[] = { { - .name = TYPE_SD_CARD, + .name = TYPE_SDMMC_COMMON, .parent = TYPE_DEVICE, + .abstract = true, .instance_size = sizeof(SDState), .class_size = sizeof(SDCardClass), - .class_init = sd_class_init, + .class_init = sdmmc_common_class_init, .instance_init = sd_instance_init, .instance_finalize = sd_instance_finalize, }, + { + .name = TYPE_SD_CARD, + .parent = TYPE_SDMMC_COMMON, + .class_init = sd_class_init, + }, { .name = TYPE_SD_CARD_SPI, .parent = TYPE_SD_CARD, diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index cc0b69e834..91eb5b6b2f 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -11,6 +11,9 @@ #ifndef SDMMC_INTERNAL_H #define SDMMC_INTERNAL_H +#define TYPE_SDMMC_COMMON "sdmmc-common" +DECLARE_OBJ_CHECKERS(SDState, SDCardClass, SDMMC_COMMON, TYPE_SDMMC_COMMON) + /* * EXT_CSD Modes segment * From f64e753149a0472e381246f0e06e5b293aa40541 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 26 Jun 2024 18:21:08 -0400 Subject: [PATCH 2029/2884] qapi: linter fixups Fix minor irritants to pylint/flake8 et al. (Yes, these need to be guarded by the Python tests. That's a work in progress, a series that's quite likely to follow once I finish this Sphinx project. Please pardon the temporary irritation.) Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-ID: <20240626222128.406106-3-jsnow@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/introspect.py | 8 ++++---- scripts/qapi/schema.py | 6 +++--- scripts/qapi/visit.py | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 86c075a6ad..ac14b20f30 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -27,8 +27,8 @@ from .gen import QAPISchemaMonolithicCVisitor from .schema import ( QAPISchema, QAPISchemaAlternatives, - QAPISchemaBranches, QAPISchemaArrayType, + QAPISchemaBranches, QAPISchemaBuiltinType, QAPISchemaEntity, QAPISchemaEnumMember, @@ -233,9 +233,9 @@ const QLitObject %(c_name)s = %(c_string)s; typ = type_int elif (isinstance(typ, QAPISchemaArrayType) and typ.element_type.json_type() == 'int'): - type_intList = self._schema.lookup_type('intList') - assert type_intList - typ = type_intList + type_intlist = self._schema.lookup_type('intList') + assert type_intlist + typ = type_intlist # Add type to work queue if new if typ not in self._used_types: self._used_types.append(typ) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 721c470d2b..d65c35f6ee 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -730,6 +730,7 @@ class QAPISchemaVariants: for v in self.variants: v.set_defined_in(name) + # pylint: disable=unused-argument def check( self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] ) -> None: @@ -1166,7 +1167,7 @@ class QAPISchema: defn.info, "%s is already defined" % other_defn.describe()) self._entity_dict[defn.name] = defn - def lookup_entity(self,name: str) -> Optional[QAPISchemaEntity]: + def lookup_entity(self, name: str) -> Optional[QAPISchemaEntity]: return self._entity_dict.get(name) def lookup_type(self, name: str) -> Optional[QAPISchemaType]: @@ -1302,11 +1303,10 @@ class QAPISchema: name = 'q_obj_%s-%s' % (name, role) typ = self.lookup_entity(name) if typ: - assert(isinstance(typ, QAPISchemaObjectType)) + assert isinstance(typ, QAPISchemaObjectType) # The implicit object type has multiple users. This can # only be a duplicate definition, which will be flagged # later. - pass else: self._def_definition(QAPISchemaObjectType( name, info, None, ifcond, None, None, members, None)) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index e766acaac9..12f92e429f 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -280,8 +280,9 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, abort(); default: assert(visit_is_input(v)); - error_setg(errp, "Invalid parameter type for '%%s', expected: %(name)s", - name ? name : "null"); + error_setg(errp, + "Invalid parameter type for '%%s', expected: %(name)s", + name ? name : "null"); /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */ g_free(*obj); *obj = NULL; From 5dd22c20730922a2d31ea0f38e6a5b8ccc998699 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 26 Jun 2024 18:21:09 -0400 Subject: [PATCH 2030/2884] docs/qapidoc: remove unused intersperse function This function has been unused since since commit fd62bff901b (sphinx/qapidoc: Drop code to generate doc for simple union tag). Signed-off-by: John Snow Message-ID: <20240626222128.406106-4-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Commit message tweaked] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index f270b494f0..3c0565d0ce 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -50,16 +50,6 @@ else: __version__ = '1.0' -# Function borrowed from pydash, which is under the MIT license -def intersperse(iterable, separator): - """Yield the members of *iterable* interspersed with *separator*.""" - iterable = iter(iterable) - yield next(iterable) - for item in iterable: - yield separator - yield item - - class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): """A QAPI schema visitor which generates docutils/Sphinx nodes From 36c6dcc26693ac385cd3c130a4804898b2d7d388 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 26 Jun 2024 18:21:10 -0400 Subject: [PATCH 2031/2884] docs/qapidoc: delint a tiny portion of the module In a forthcoming series that adds a new QMP documentation generator, it will be helpful to have a linting baseline. However, there's no need to shuffle around the deck chairs too much, because most of this code will be removed once that new qapidoc generator (the "transmogrifier") is in place. To ease my pain: just turn off the black auto-formatter for most, but not all, of qapidoc.py. This will help ensure that *new* code follows a coding standard without bothering too much with cleaning up the existing code. Code that I intend to keep is still subject to the delinting beam. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-ID: <20240626222128.406106-5-jsnow@redhat.com> Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 62 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 3c0565d0ce..659e507353 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -28,26 +28,33 @@ import os import re from docutils import nodes +from docutils.parsers.rst import Directive, directives from docutils.statemachine import ViewList -from docutils.parsers.rst import directives, Directive +from qapi.error import QAPIError, QAPISemError +from qapi.gen import QAPISchemaVisitor +from qapi.schema import QAPISchema + +import sphinx from sphinx.errors import ExtensionError from sphinx.util.nodes import nested_parse_with_titles -import sphinx -from qapi.gen import QAPISchemaVisitor -from qapi.error import QAPIError, QAPISemError -from qapi.schema import QAPISchema # Sphinx up to 1.6 uses AutodocReporter; 1.7 and later # use switch_source_input. Check borrowed from kerneldoc.py. -Use_SSI = sphinx.__version__[:3] >= '1.7' -if Use_SSI: +USE_SSI = sphinx.__version__[:3] >= "1.7" +if USE_SSI: from sphinx.util.docutils import switch_source_input else: - from sphinx.ext.autodoc import AutodocReporter + from sphinx.ext.autodoc import ( # pylint: disable=no-name-in-module + AutodocReporter, + ) -__version__ = '1.0' +__version__ = "1.0" + + +# Disable black auto-formatter until re-enabled: +# fmt: off class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): @@ -441,6 +448,10 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): return self._top_node.children +# Turn the black formatter on for the rest of the file. +# fmt: on + + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module @@ -448,34 +459,34 @@ class QAPISchemaGenDepVisitor(QAPISchemaVisitor): that the generated documentation output depends on the input schema file associated with each module in the QAPI input. """ + def __init__(self, env, qapidir): self._env = env self._qapidir = qapidir def visit_module(self, name): if name != "./builtin": - qapifile = self._qapidir + '/' + name + qapifile = self._qapidir + "/" + name self._env.note_dependency(os.path.abspath(qapifile)) super().visit_module(name) class QAPIDocDirective(Directive): """Extract documentation from the specified QAPI .json file""" + required_argument = 1 optional_arguments = 1 - option_spec = { - 'qapifile': directives.unchanged_required - } + option_spec = {"qapifile": directives.unchanged_required} has_content = False def new_serialno(self): """Return a unique new ID string suitable for use as a node's ID""" env = self.state.document.settings.env - return 'qapidoc-%d' % env.new_serialno('qapidoc') + return "qapidoc-%d" % env.new_serialno("qapidoc") def run(self): env = self.state.document.settings.env - qapifile = env.config.qapidoc_srctree + '/' + self.arguments[0] + qapifile = env.config.qapidoc_srctree + "/" + self.arguments[0] qapidir = os.path.dirname(qapifile) try: @@ -513,13 +524,14 @@ class QAPIDocDirective(Directive): # plain self.state.nested_parse(), and so we can drop the saving # of title_styles and section_level that kerneldoc.py does, # because nested_parse_with_titles() does that for us. - if Use_SSI: + if USE_SSI: with switch_source_input(self.state, rstlist): nested_parse_with_titles(self.state, rstlist, node) else: save = self.state.memo.reporter self.state.memo.reporter = AutodocReporter( - rstlist, self.state.memo.reporter) + rstlist, self.state.memo.reporter + ) try: nested_parse_with_titles(self.state, rstlist, node) finally: @@ -527,12 +539,12 @@ class QAPIDocDirective(Directive): def setup(app): - """ Register qapi-doc directive with Sphinx""" - app.add_config_value('qapidoc_srctree', None, 'env') - app.add_directive('qapi-doc', QAPIDocDirective) + """Register qapi-doc directive with Sphinx""" + app.add_config_value("qapidoc_srctree", None, "env") + app.add_directive("qapi-doc", QAPIDocDirective) - return dict( - version=__version__, - parallel_read_safe=True, - parallel_write_safe=True - ) + return { + "version": __version__, + "parallel_read_safe": True, + "parallel_write_safe": True, + } From 939c639e1d7c1d78bcc262368ab1fb78f09d0bfe Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 26 Jun 2024 18:21:11 -0400 Subject: [PATCH 2032/2884] qapi/parser: preserve indentation in QAPIDoc sections Change get_doc_indented() to preserve indentation on all subsequent text lines, and create a compatibility dedent() function for qapidoc.py that removes indentation the same way get_doc_indented() did. This is being done for the benefit of a new qapidoc generator which requires that indentation in argument and features sections are preserved. Prior to this patch, a section like this: ``` @name: lorem ipsum dolor sit amet consectetur adipiscing elit ``` would have its body text be parsed into: ``` lorem ipsum dolor sit amet consectetur adipiscing elit ``` We want to preserve the indentation for even the first body line so that the entire block can be parsed directly as rST. This patch would now parse that segment into: ``` lorem ipsum dolor sit amet consectetur adipiscing elit ``` This is helpful for formatting arguments and features as field lists in rST, where the new generator will format this information as: ``` :arg type name: lorem ipsum dolor sit amet consectetur apidiscing elit ``` ...and can be formed by the simple concatenation of the field list construct and the body text. The indents help preserve the continuation of a block-level element, and further allow the use of additional rST block-level constructs such as code blocks, lists, and other such markup. This understandably breaks the existing qapidoc.py; so a new function is added there to dedent the text for compatibility. Once the new generator is merged, this function will not be needed any longer and can be dropped. I verified this patch changes absolutely nothing by comparing the md5sums of the QMP ref html pages both before and after the change, so it's certified inert. QAPI test output has been updated to reflect the new strategy of preserving indents for rST. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-ID: <20240626222128.406106-6-jsnow@redhat.com> [Lost commit message paragraph restored] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 27 ++++++++++++++++++++++----- scripts/qapi/parser.py | 4 ++-- tests/qapi-schema/doc-good.out | 32 ++++++++++++++++---------------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 659e507353..f9683444b1 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -26,6 +26,7 @@ https://www.sphinx-doc.org/en/master/development/index.html import os import re +import textwrap from docutils import nodes from docutils.parsers.rst import Directive, directives @@ -53,6 +54,19 @@ else: __version__ = "1.0" +def dedent(text: str) -> str: + # Adjust indentation to make description text parse as paragraph. + + lines = text.splitlines(True) + if re.match(r"\s+", lines[0]): + # First line is indented; description started on the line after + # the name. dedent the whole block. + return textwrap.dedent(text) + + # Descr started on same line. Dedent line 2+. + return lines[0] + textwrap.dedent("".join(lines[1:])) + + # Disable black auto-formatter until re-enabled: # fmt: off @@ -164,7 +178,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): term = self._nodes_for_one_member(section.member) # TODO drop fallbacks when undocumented members are outlawed if section.text: - defn = section.text + defn = dedent(section.text) else: defn = [nodes.Text('Not documented')] @@ -202,7 +216,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) # TODO drop fallbacks when undocumented members are outlawed if section.text: - defn = section.text + defn = dedent(section.text) else: defn = [nodes.Text('Not documented')] @@ -237,7 +251,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): dlnode = nodes.definition_list() for section in doc.features.values(): dlnode += self._make_dlitem( - [nodes.literal('', section.member.name)], section.text) + [nodes.literal('', section.member.name)], dedent(section.text)) seen_item = True if not seen_item: @@ -260,9 +274,12 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): continue snode = self._make_section(section.tag) if section.tag and section.tag.startswith('Example'): - snode += self._nodes_for_example(section.text) + snode += self._nodes_for_example(dedent(section.text)) else: - self._parse_text_into_node(section.text, snode) + self._parse_text_into_node( + dedent(section.text) if section.tag else section.text, + snode, + ) nodelist.append(snode) return nodelist diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 7b13a583ac..1ef1f85b02 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -448,7 +448,7 @@ class QAPISchemaParser: indent = must_match(r'\s*', line).end() if not indent: return line - doc.append_line(line[indent:]) + doc.append_line(line) prev_line_blank = False while True: self.accept(False) @@ -465,7 +465,7 @@ class QAPISchemaParser: self, "unexpected de-indent (expected at least %d spaces)" % indent) - doc.append_line(line[indent:]) + doc.append_line(line) prev_line_blank = True def get_doc_paragraph(self, doc: 'QAPIDoc') -> Optional[str]: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 716a9a4102..435f6e6d76 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -117,8 +117,8 @@ doc symbol=Base body= arg=base1 -description starts on a new line, -minimally indented + description starts on a new line, + minimally indented doc symbol=Variant1 body= A paragraph @@ -145,8 +145,8 @@ doc symbol=Alternate arg=i description starts on the same line -remainder indented the same -@b is undocumented + remainder indented the same + @b is undocumented arg=b feature=alt-feat @@ -158,11 +158,11 @@ doc symbol=cmd body= arg=arg1 -description starts on a new line, -indented + description starts on a new line, + indented arg=arg2 description starts on the same line -remainder indented differently + remainder indented differently arg=arg3 feature=cmd-feat1 @@ -178,16 +178,16 @@ some section=TODO frobnicate section=Notes -- Lorem ipsum dolor sit amet -- Ut enim ad minim veniam + - Lorem ipsum dolor sit amet + - Ut enim ad minim veniam -Duis aute irure dolor + Duis aute irure dolor section=Example --> in -<- out + -> in + <- out section=Examples -- *verbatim* -- {braces} + - *verbatim* + - {braces} section=Since 2.10 doc symbol=cmd-boxed @@ -198,9 +198,9 @@ a feature feature=cmd-feat2 another feature section=Example --> in + -> in -<- out + <- out doc symbol=EVT_BOXED body= From 83deda876940116d31bf47c51e62e0fe625e8655 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 26 Jun 2024 18:21:12 -0400 Subject: [PATCH 2033/2884] qapi/parser: fix comment parsing immediately following a doc block If a comment immediately follows a doc block, the parser doesn't ignore that token appropriately. Fix that. e.g. > ## > # = Hello World! > ## > > # I'm a comment! will break the parser, because it does not properly ignore the comment token if it immediately follows a doc block. Fixes: 3d035cd2cca6 (qapi: Rewrite doc comment parser) Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-ID: <20240626222128.406106-7-jsnow@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 2 +- tests/qapi-schema/doc-good.json | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 1ef1f85b02..c3d20cc01b 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -583,7 +583,7 @@ class QAPISchemaParser: line = self.get_doc_line() first = False - self.accept(False) + self.accept() doc.end() return doc diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index de38a386e8..8b39eb946a 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -55,6 +55,8 @@ # - {braces} ## +# Not a doc comment + ## # @Enum: # From 2664f3176a85aff556e5d82ad4396bda67a20b39 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 26 Jun 2024 18:21:13 -0400 Subject: [PATCH 2034/2884] docs/qapidoc: fix nested parsing under untagged sections Sphinx does not like sections without titles, because it wants to convert every section into a reference. When there is no title, it struggles to do this and transforms the tree inproperly. Depending on the rST used, this may result in an assertion error deep in the docutils HTMLWriter. (Observed when using ".. admonition:: Notes" under such a section - When this is transformed with its own element, Sphinx is fooled into believing this title belongs to the section and incorrect mutates the docutils tree, leading to errors during rendering time.) When parsing an untagged section (free paragraphs), skip making a hollow section and instead append the parse results to the prior section. Many Bothans died to bring us this information. The resulting output changes are basically invisible. Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240626222128.406106-8-jsnow@redhat.com> [Mention output changes in commit message] Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/sphinx/qapidoc.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index f9683444b1..efcd84656f 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -272,14 +272,20 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): if section.tag and section.tag == 'TODO': # Hide TODO: sections continue + + if not section.tag: + # Sphinx cannot handle sectionless titles; + # Instead, just append the results to the prior section. + container = nodes.container() + self._parse_text_into_node(section.text, container) + nodelist += container.children + continue + snode = self._make_section(section.tag) - if section.tag and section.tag.startswith('Example'): + if section.tag.startswith('Example'): snode += self._nodes_for_example(dedent(section.text)) else: - self._parse_text_into_node( - dedent(section.text) if section.tag else section.text, - snode, - ) + self._parse_text_into_node(dedent(section.text), snode) nodelist.append(snode) return nodelist From 9f2b848857fc2bd7f36802524ef89d149c77092c Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 18:21:14 -0400 Subject: [PATCH 2035/2884] qapi: fix non-compliant JSON examples The new QMP documentation generator wants to parse all examples as "QMP". We have an existing QMP lexer in docs/sphinx/qmp_lexer.py (Seen in-use here: https://qemu-project.gitlab.io/qemu/interop/bitmaps.html) that allows the use of "->", "<-" and "..." tokens to denote QMP protocol flow with elisions, but otherwise defers to the JSON lexer. To utilize this lexer for the existing QAPI documentation, we need them to conform to a standard so that they lex and render correctly. Once the QMP lexer is active for examples, errant QMP/JSON will produce warning messages and fail the build. Fix any invalid JSON found in QAPI documentation (identified by attempting to lex all examples as QMP; see subsequent commits). Additionally, elisions must be standardized for the QMP lexer; they must be represented as the value "...", so three examples have been adjusted to support that format here. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240626222128.406106-9-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/control.json | 3 ++- qapi/machine.json | 2 +- qapi/migration.json | 2 +- qapi/misc.json | 3 ++- qapi/net.json | 6 +++--- qapi/rocker.json | 2 +- qapi/ui.json | 2 +- 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/qapi/control.json b/qapi/control.json index 6bdbf077c2..10c906fa0e 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -145,7 +145,8 @@ # }, # { # "name":"system_powerdown" -# } +# }, +# ... # ] # } # diff --git a/qapi/machine.json b/qapi/machine.json index 2fd3e9c3d5..a982c94503 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1057,7 +1057,7 @@ # "vcpus-count": 1 }, # { "props": { "core-id": 0 }, "type": "POWER8-spapr-cpu-core", # "vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"} -# ]}' +# ]} # # For pc machine type started with -smp 1,maxcpus=2: # diff --git a/qapi/migration.json b/qapi/migration.json index 0f24206bce..9ec9ef36c4 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -2106,7 +2106,7 @@ # Example: # # -> {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, -# 'sample-pages': 512} } +# "sample-pages": 512} } # <- { "return": {} } # # Measure dirty rate using dirty bitmap for 500 milliseconds: diff --git a/qapi/misc.json b/qapi/misc.json index ec30e5c570..4b41e15dcd 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -287,7 +287,8 @@ # # Example: # -# -> { "execute": "get-win32-socket", "arguments": { "info": "abcd123..", fdname": "skclient" } } +# -> { "execute": "get-win32-socket", +# "arguments": { "info": "abcd123..", "fdname": "skclient" } } # <- { "return": {} } ## { 'command': 'get-win32-socket', 'data': {'info': 'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' } diff --git a/qapi/net.json b/qapi/net.json index 0f5a259475..c19df435a5 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -1003,9 +1003,9 @@ # # Example: # -# <- { 'event': 'NETDEV_STREAM_DISCONNECTED', -# 'data': {'netdev-id': 'netdev0'}, -# 'timestamp': {'seconds': 1663330937, 'microseconds': 526695} } +# <- { "event": "NETDEV_STREAM_DISCONNECTED", +# "data": {"netdev-id": "netdev0"}, +# "timestamp": {"seconds": 1663330937, "microseconds": 526695} } ## { 'event': 'NETDEV_STREAM_DISCONNECTED', 'data': { 'netdev-id': 'str' } } diff --git a/qapi/rocker.json b/qapi/rocker.json index 5635cf174f..f5225eb62c 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -250,7 +250,7 @@ # "action": {"goto-tbl": 10}, # "mask": {"in-pport": 4294901760} # }, -# {...more...}, +# {...}, # ]} ## { 'command': 'query-rocker-of-dpa-flows', diff --git a/qapi/ui.json b/qapi/ui.json index f610bce118..c12f529257 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -361,7 +361,7 @@ # "channel-id": 0, # "tls": false # }, -# [ ... more channels follow ... ] +# ... # ] # } # } From b32a6b62a82a4c1a07535e86b784ceaaa948fd85 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 18:21:15 -0400 Subject: [PATCH 2036/2884] qapi: nail down convention that Errors sections are lists By unstated convention, Errors sections are rST lists. Document the convention, and make the one exception conform. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240626222128.406106-10-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 7 +++++++ qapi/transaction.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index f453bd3546..cee43222f1 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1011,6 +1011,13 @@ like this:: "Returns" and "Errors" sections are only valid for commands. They document the success and the error response, respectively. +"Errors" sections should be formatted as an rST list, each entry +detailing a relevant error condition. For example:: + + # Errors: + # - If @device does not exist, DeviceNotFound + # - Any other error returns a GenericError. + A "Since: x.y.z" tagged section lists the release that introduced the definition. diff --git a/qapi/transaction.json b/qapi/transaction.json index 5749c133d4..07afc269d5 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -235,7 +235,7 @@ # additional detail. # # Errors: -# Any errors from commands in the transaction +# - Any errors from commands in the transaction # # Note: The transaction aborts on the first failure. Therefore, there # will be information on only one failed operation returned in an From d461c2797374e0becc5227cdc099d85bc329b236 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 18:21:16 -0400 Subject: [PATCH 2037/2884] qapi: convert "Note" sections to plain rST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not need a dedicated section for notes. By eliminating a specially parsed section, these notes can be treated as normal rST paragraphs in the new QMP reference manual, and can be placed and styled much more flexibly. Convert all existing "Note" and "Notes" sections to pure rST. As part of the conversion, capitalize the first letter of each sentence and add trailing punctuation where appropriate to ensure notes look sensible and consistent in rendered HTML documentation. Markup is also re-aligned to the de-facto standard of 3 spaces for directives. Update docs/devel/qapi-code-gen.rst to reflect the new paradigm, and update the QAPI parser to prohibit "Note" sections while suggesting a new syntax. The exact formatting to use is a matter of taste, but a good candidate is simply: .. note:: lorem ipsum ... ... dolor sit amet ... ... consectetur adipiscing elit ... ... but there are other choices, too. The Sphinx readthedocs theme offers theming for the following forms (capitalization unimportant); all are adorned with a (!) symbol () in the title bar for rendered HTML docs. See https://sphinx-rtd-theme.readthedocs.io/en/stable/demo/demo.html#admonitions for examples of each directive/admonition in use. These are rendered in orange: .. Attention:: ... .. Caution:: ... .. WARNING:: ... These are rendered in red: .. DANGER:: ... .. Error:: ... These are rendered in green: .. Hint:: ... .. Important:: ... .. Tip:: ... These are rendered in blue: .. Note:: ... .. admonition:: custom title admonition body text This patch uses ".. note::" almost everywhere, with just two "caution" directives. Several instances of "Notes:" have been converted to merely ".. note::", or multiple ".. note::" where appropriate. ".. admonition:: notes" is used in a few places where we had an ordered list of multiple notes that would not make sense as standalone/separate admonitions. Two "Note:" following "Example:" have been turned into ordinary paragraphs within the example. NOTE: Because qapidoc.py does not attempt to preserve source ordering of sections, the conversion of Notes from a "tagged section" to an "untagged section" means that rendering order for some notes *may change* as a result of this patch. The forthcoming qapidoc.py rewrite strictly preserves source ordering in the rendered documentation, so this issue will be rectified in the new generator. Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Stefan Hajnoczi <stefanha@redhat.com> [for block*.json] Message-ID: <20240626222128.406106-11-jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Commit message clarified slightly, period added to one more note] Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 7 +- qapi/block-core.json | 28 +++--- qapi/block.json | 2 +- qapi/char.json | 12 +-- qapi/control.json | 17 ++-- qapi/dump.json | 2 +- qapi/introspect.json | 6 +- qapi/machine-target.json | 26 +++--- qapi/machine.json | 47 +++++----- qapi/migration.json | 14 +-- qapi/misc.json | 88 +++++++++---------- qapi/net.json | 6 +- qapi/pci.json | 8 +- qapi/qdev.json | 28 +++--- qapi/qom.json | 17 ++-- qapi/rocker.json | 16 ++-- qapi/run-state.json | 18 ++-- qapi/sockets.json | 10 +-- qapi/stats.json | 22 ++--- qapi/transaction.json | 8 +- qapi/ui.json | 29 +++--- qapi/virtio.json | 12 +-- qga/qapi-schema.json | 48 +++++----- scripts/qapi/parser.py | 15 ++++ tests/qapi-schema/doc-empty-section.err | 2 +- tests/qapi-schema/doc-empty-section.json | 2 +- tests/qapi-schema/doc-good.json | 4 +- tests/qapi-schema/doc-good.out | 8 +- tests/qapi-schema/doc-good.txt | 10 +-- .../qapi-schema/doc-interleaved-section.json | 2 +- 30 files changed, 261 insertions(+), 253 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index cee43222f1..ae97b335cb 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -995,14 +995,13 @@ line "Features:", like this:: # @feature: Description text A tagged section begins with a paragraph that starts with one of the -following words: "Note:"/"Notes:", "Since:", "Example:"/"Examples:", -"Returns:", "Errors:", "TODO:". It ends with the start of a new -section. +following words: "Since:", "Example:"/"Examples:", "Returns:", +"Errors:", "TODO:". It ends with the start of a new section. The second and subsequent lines of tagged sections must be indented like this:: - # Note: Ut enim ad minim veniam, quis nostrud exercitation ullamco + # TODO: Ut enim ad minim veniam, quis nostrud exercitation ullamco # laboris nisi ut aliquip ex ea commodo consequat. # # Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/qapi/block-core.json b/qapi/block-core.json index df5e07debd..cacedfb771 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1619,9 +1619,9 @@ # # @unstable: Member @x-perf is experimental. # -# Note: @on-source-error and @on-target-error only affect background -# I/O. If an error occurs during a guest write request, the -# device's rerror/werror actions will be used. +# .. note:: @on-source-error and @on-target-error only affect background +# I/O. If an error occurs during a guest write request, the device's +# rerror/werror actions will be used. # # Since: 4.2 ## @@ -5545,8 +5545,8 @@ # after this event and must be repaired (Since 2.2; before, every # BLOCK_IMAGE_CORRUPTED event was fatal) # -# Note: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event. +# .. note:: If action is "stop", a STOP event will eventually follow the +# BLOCK_IO_ERROR event. # # Example: # @@ -5592,8 +5592,8 @@ # field is a debugging aid for humans, it should not be parsed by # applications) (since: 2.2) # -# Note: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event +# .. note:: If action is "stop", a STOP event will eventually follow the +# BLOCK_IO_ERROR event. # # Since: 0.13 # @@ -5731,8 +5731,8 @@ # # @speed: rate limit, bytes per second # -# Note: The "ready to complete" status is always reset by a -# @BLOCK_JOB_ERROR event +# .. note:: The "ready to complete" status is always reset by a +# @BLOCK_JOB_ERROR event. # # Since: 1.3 # @@ -5985,7 +5985,7 @@ # # @sectors-count: failed read operation sector count # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.0 # @@ -6016,7 +6016,7 @@ # # @sectors-count: failed read operation sector count # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.0 # @@ -6048,9 +6048,9 @@ # # @name: the name of the internal snapshot to be created # -# Notes: In transaction, if @name is empty, or any snapshot matching -# @name exists, the operation will fail. Only some image formats -# support it, for example, qcow2, and rbd. +# .. note:: In transaction, if @name is empty, or any snapshot matching +# @name exists, the operation will fail. Only some image formats +# support it, for example, qcow2, and rbd. # # Since: 1.7 ## diff --git a/qapi/block.json b/qapi/block.json index 5de99fe09d..ea81d9e192 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -113,7 +113,7 @@ # Errors: # - If @device is not a valid block device, DeviceNotFound # -# Notes: Ejecting a device with no media results in success +# .. note:: Ejecting a device with no media results in success. # # Since: 0.14 # diff --git a/qapi/char.json b/qapi/char.json index 777dde55d9..5eabf8e764 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -21,8 +21,8 @@ # backend (e.g. with the chardev=... option) is in open or closed # state (since 2.1) # -# Notes: @filename is encoded using the QEMU command line character -# device encoding. See the QEMU man page for details. +# .. note:: @filename is encoded using the QEMU command line character +# device encoding. See the QEMU man page for details. # # Since: 0.14 ## @@ -388,9 +388,9 @@ # # @rows: console height, in chars # -# Note: the options are only effective when the VNC or SDL graphical -# display backend is active. They are ignored with the GTK, -# Spice, VNC and D-Bus display backends. +# .. note:: The options are only effective when the VNC or SDL graphical +# display backend is active. They are ignored with the GTK, Spice, +# VNC and D-Bus display backends. # # Since: 1.5 ## @@ -806,7 +806,7 @@ # # @open: true if the guest has opened the virtio-serial port # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.1 # diff --git a/qapi/control.json b/qapi/control.json index 10c906fa0e..59d5e00c15 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -22,14 +22,13 @@ # "arguments": { "enable": [ "oob" ] } } # <- { "return": {} } # -# Notes: This command is valid exactly when first connecting: it must -# be issued before any other command will be accepted, and will -# fail once the monitor is accepting other commands. (see qemu -# docs/interop/qmp-spec.rst) +# .. note:: This command is valid exactly when first connecting: it must +# be issued before any other command will be accepted, and will fail +# once the monitor is accepting other commands. (see qemu +# docs/interop/qmp-spec.rst) # -# The QMP client needs to explicitly enable QMP capabilities, -# otherwise all the QMP capabilities will be turned off by -# default. +# .. note:: The QMP client needs to explicitly enable QMP capabilities, +# otherwise all the QMP capabilities will be turned off by default. # # Since: 0.13 ## @@ -150,8 +149,8 @@ # ] # } # -# Note: This example has been shortened as the real response is too -# long. +# This example has been shortened as the real response is too long. +# ## { 'command': 'query-commands', 'returns': ['CommandInfo'], 'allow-preconfig': true } diff --git a/qapi/dump.json b/qapi/dump.json index 2fa9504d86..f9aee7ea1d 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -90,7 +90,7 @@ # and @length is not allowed to be specified with non-elf @format # at the same time (since 2.0) # -# Note: All boolean arguments default to false +# .. note:: All boolean arguments default to false. # # Since: 1.2 # diff --git a/qapi/introspect.json b/qapi/introspect.json index b041b02ba8..b15052ec21 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -41,9 +41,9 @@ # names are guaranteed to be unique (no name will be duplicated # with different meta-types). # -# Note: the QAPI schema is also used to help define *internal* -# interfaces, by defining QAPI types. These are not part of the -# QMP wire ABI, and therefore not returned by this command. +# .. note:: The QAPI schema is also used to help define *internal* +# interfaces, by defining QAPI types. These are not part of the QMP +# wire ABI, and therefore not returned by this command. # # Since: 2.5 ## diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 2942853092..a8d9ec87f5 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -49,15 +49,15 @@ # to be migration-safe, but allows tooling to get an insight and # work with model details. # -# Note: When a non-migration-safe CPU model is expanded in static -# mode, some features enabled by the CPU model may be omitted, -# because they can't be implemented by a static CPU model -# definition (e.g. cache info passthrough and PMU passthrough in -# x86). If you need an accurate representation of the features -# enabled by a non-migration-safe CPU model, use @full. If you -# need a static representation that will keep ABI compatibility -# even when changing QEMU version or machine-type, use @static -# (but keep in mind that some features may be omitted). +# .. note:: When a non-migration-safe CPU model is expanded in static +# mode, some features enabled by the CPU model may be omitted, +# because they can't be implemented by a static CPU model definition +# (e.g. cache info passthrough and PMU passthrough in x86). If you +# need an accurate representation of the features enabled by a +# non-migration-safe CPU model, use @full. If you need a static +# representation that will keep ABI compatibility even when changing +# QEMU version or machine-type, use @static (but keep in mind that +# some features may be omitted). # # Since: 2.8 ## @@ -175,8 +175,8 @@ # - if a model contains an unknown cpu definition name, unknown # properties or properties with wrong types. # -# Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. +# .. note:: This command isn't specific to s390x, but is only +# implemented on this architecture currently. # # Since: 2.8 ## @@ -229,8 +229,8 @@ # - if a model contains an unknown cpu definition name, unknown # properties or properties with wrong types. # -# Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. +# .. note:: This command isn't specific to s390x, but is only +# implemented on this architecture currently. # # Since: 2.8 ## diff --git a/qapi/machine.json b/qapi/machine.json index a982c94503..f15ad1b43e 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -24,9 +24,9 @@ # # @avr: since 5.1 # -# Notes: The resulting QMP strings can be appended to the -# "qemu-system-" prefix to produce the corresponding QEMU -# executable name. This is true even for "qemu-system-x86_64". +# .. note:: The resulting QMP strings can be appended to the +# "qemu-system-" prefix to produce the corresponding QEMU executable +# name. This is true even for "qemu-system-x86_64". # # Since: 3.0 ## @@ -305,8 +305,9 @@ # # Since: 0.14 # -# Notes: If no UUID was specified for the guest, a null UUID is -# returned. +# .. note:: If no UUID was specified for the guest, a null UUID is +# returned. +# ## { 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } @@ -367,10 +368,10 @@ # # Since: 0.14 # -# Notes: A guest may or may not respond to this command. This command -# returning does not indicate that a guest has accepted the -# request or that it has shut down. Many guests will respond to -# this command by prompting the user in some way. +# .. note:: A guest may or may not respond to this command. This +# command returning does not indicate that a guest has accepted the +# request or that it has shut down. Many guests will respond to this +# command by prompting the user in some way. # # Example: # @@ -389,8 +390,8 @@ # # Since: 1.1 # -# Note: prior to 4.0, this command does nothing in case the guest -# isn't suspended. +# .. note:: Prior to 4.0, this command does nothing in case the guest +# isn't suspended. # # Example: # @@ -440,8 +441,8 @@ # # Since: 0.14 # -# Note: prior to 2.1, this command was only supported for x86 and s390 -# VMs +# .. note:: Prior to 2.1, this command was only supported for x86 and +# s390 VMs. # # Example: # @@ -839,7 +840,7 @@ # # Since: 0.14 # -# Notes: Errors were not reliably returned until 1.1 +# .. caution:: Errors were not reliably returned until 1.1. # # Example: # @@ -865,7 +866,7 @@ # # Since: 0.14 # -# Notes: Errors were not reliably returned until 1.1 +# .. caution:: Errors were not reliably returned until 1.1. # # Example: # @@ -995,8 +996,8 @@ # # @thread-id: thread number within the core the CPU belongs to # -# Note: management should be prepared to pass through additional -# properties with device_add. +# .. note:: Management should be prepared to pass through additional +# properties with device_add. # # Since: 2.7 ## @@ -1123,9 +1124,9 @@ # the KVM kernel module cannot support it, KVMMissingCap # - If no balloon device is present, DeviceNotActive # -# Notes: This command just issues a request to the guest. When it -# returns, the balloon size may not have changed. A guest can -# change the balloon size independent of this command. +# .. note:: This command just issues a request to the guest. When it +# returns, the balloon size may not have changed. A guest can change +# the balloon size independent of this command. # # Since: 0.14 # @@ -1185,7 +1186,7 @@ # @actual: the logical size of the VM in bytes Formula used: # logical_vm_size = vm_ram_size - balloon_size # -# Note: this event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 1.2 # @@ -1248,7 +1249,7 @@ # Emitted when the hv-balloon driver receives a "STATUS" message from # the guest. # -# Note: this event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 8.2 # @@ -1593,7 +1594,7 @@ # # @qom-path: path to the device object in the QOM tree (since 6.2) # -# Note: this event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 5.1 # diff --git a/qapi/migration.json b/qapi/migration.json index 9ec9ef36c4..1234bef888 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1456,8 +1456,8 @@ # # Cancel the current executing migration process. # -# Notes: This command succeeds even if there is no migration process -# running. +# .. note:: This command succeeds even if there is no migration process +# running. # # Since: 0.14 # @@ -1589,16 +1589,16 @@ # # Since: 0.14 # -# Notes: +# .. admonition:: Notes # # 1. The 'query-migrate' command should be used to check # migration's progress and final result (this information is -# provided by the 'status' member) +# provided by the 'status' member). # -# 2. All boolean arguments default to false +# 2. All boolean arguments default to false. # # 3. The user Monitor's "detach" argument is invalid in QMP and -# should not be used +# should not be used. # # 4. The uri argument should have the Uniform Resource Identifier # of default destination VM. This connection will be bound to @@ -1672,7 +1672,7 @@ # # Since: 2.3 # -# Notes: +# .. admonition:: Notes # # 1. It's a bad idea to use a string for the uri, but it needs to # stay compatible with -incoming and the format of the uri is diff --git a/qapi/misc.json b/qapi/misc.json index 4b41e15dcd..13ea82f525 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -103,9 +103,9 @@ # # Returns a list of information about each iothread. # -# Note: this list excludes the QEMU main loop thread, which is not -# declared using the -object iothread command-line option. It is -# always the main thread of the process. +# .. note:: This list excludes the QEMU main loop thread, which is not +# declared using the -object iothread command-line option. It is +# always the main thread of the process. # # Returns: a list of @IOThreadInfo for each iothread # @@ -136,13 +136,13 @@ # # Since: 0.14 # -# Notes: This function will succeed even if the guest is already in -# the stopped state. In "inmigrate" state, it will ensure that -# the guest remains paused once migration finishes, as if the -S -# option was passed on the command line. +# .. note:: This function will succeed even if the guest is already in +# the stopped state. In "inmigrate" state, it will ensure that the +# guest remains paused once migration finishes, as if the -S option +# was passed on the command line. # -# In the "suspended" state, it will completely stop the VM and -# cause a transition to the "paused" state. (Since 9.0) +# In the "suspended" state, it will completely stop the VM and cause +# a transition to the "paused" state. (Since 9.0) # # Example: # @@ -158,15 +158,15 @@ # # Since: 0.14 # -# Notes: This command will succeed if the guest is currently running. -# It will also succeed if the guest is in the "inmigrate" state; -# in this case, the effect of the command is to make sure the -# guest starts once migration finishes, removing the effect of the -# -S command line option if it was passed. +# .. note:: This command will succeed if the guest is currently running. +# It will also succeed if the guest is in the "inmigrate" state; in +# this case, the effect of the command is to make sure the guest +# starts once migration finishes, removing the effect of the -S +# command line option if it was passed. # -# If the VM was previously suspended, and not been reset or woken, -# this command will transition back to the "suspended" state. -# (Since 9.0) +# If the VM was previously suspended, and not been reset or woken, +# this command will transition back to the "suspended" state. (Since +# 9.0) # # Example: # @@ -219,18 +219,18 @@ # # Since: 0.14 # -# Notes: This command only exists as a stop-gap. Its use is highly -# discouraged. The semantics of this command are not guaranteed: -# this means that command names, arguments and responses can -# change or be removed at ANY time. Applications that rely on -# long term stability guarantees should NOT use this command. +# .. note:: This command only exists as a stop-gap. Its use is highly +# discouraged. The semantics of this command are not guaranteed: +# this means that command names, arguments and responses can change +# or be removed at ANY time. Applications that rely on long term +# stability guarantees should NOT use this command. # -# Known limitations: +# Known limitations: # -# * This command is stateless, this means that commands that -# depend on state information (such as getfd) might not work +# * This command is stateless, this means that commands that +# depend on state information (such as getfd) might not work. # -# * Commands that prompt the user for data don't currently work +# * Commands that prompt the user for data don't currently work. # # Example: # @@ -252,11 +252,11 @@ # # Since: 0.14 # -# Notes: If @fdname already exists, the file descriptor assigned to it -# will be closed and replaced by the received file descriptor. +# .. note:: If @fdname already exists, the file descriptor assigned to +# it will be closed and replaced by the received file descriptor. # -# The 'closefd' command can be used to explicitly close the file -# descriptor when it is no longer needed. +# The 'closefd' command can be used to explicitly close the file +# descriptor when it is no longer needed. # # Example: # @@ -279,11 +279,11 @@ # # Since: 8.0 # -# Notes: If @fdname already exists, the file descriptor assigned to it -# will be closed and replaced by the received file descriptor. +# .. note:: If @fdname already exists, the file descriptor assigned to +# it will be closed and replaced by the received file descriptor. # -# The 'closefd' command can be used to explicitly close the file -# descriptor when it is no longer needed. +# The 'closefd' command can be used to explicitly close the file +# descriptor when it is no longer needed. # # Example: # @@ -339,10 +339,9 @@ # - If file descriptor was not received, GenericError # - If @fdset-id is a negative value, GenericError # -# Notes: -# The list of fd sets is shared by all monitor connections. +# .. note:: The list of fd sets is shared by all monitor connections. # -# If @fdset-id is not specified, a new fd set will be created. +# .. note:: If @fdset-id is not specified, a new fd set will be created. # # Since: 1.2 # @@ -370,11 +369,10 @@ # # Since: 1.2 # -# Notes: -# The list of fd sets is shared by all monitor connections. +# .. note:: The list of fd sets is shared by all monitor connections. # -# If @fd is not specified, all file descriptors in @fdset-id will -# be removed. +# .. note:: If @fd is not specified, all file descriptors in @fdset-id +# will be removed. # # Example: # @@ -420,7 +418,7 @@ # # Since: 1.2 # -# Note: The list of fd sets is shared by all monitor connections. +# .. note:: The list of fd sets is shared by all monitor connections. # # Example: # @@ -561,9 +559,9 @@ # # @qom-path: path to the RTC object in the QOM tree # -# Note: This event is rate-limited. It is not guaranteed that the RTC -# in the system implements this event, or even that the system has -# an RTC at all. +# .. note:: This event is rate-limited. It is not guaranteed that the +# RTC in the system implements this event, or even that the system +# has an RTC at all. # # Since: 0.13 # diff --git a/qapi/net.json b/qapi/net.json index c19df435a5..dd6c365c34 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -22,9 +22,9 @@ # # Since: 0.14 # -# Notes: Not all network adapters support setting link status. This -# command will succeed even if the network adapter does not -# support link status notification. +# .. note:: Not all network adapters support setting link status. This +# command will succeed even if the network adapter does not support +# link status notification. # # Example: # diff --git a/qapi/pci.json b/qapi/pci.json index 08bf695863..8287d15dd0 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -146,8 +146,8 @@ # # @regions: a list of the PCI I/O regions associated with the device # -# Notes: the contents of @class_info.desc are not stable and should -# only be treated as informational. +# .. note:: The contents of @class_info.desc are not stable and should +# only be treated as informational. # # Since: 0.14 ## @@ -311,7 +311,7 @@ # ] # } # -# Note: This example has been shortened as the real response is too -# long. +# This example has been shortened as the real response is too long. +# ## { 'command': 'query-pci', 'returns': ['PciInfo'] } diff --git a/qapi/qdev.json b/qapi/qdev.json index facaa0bc6a..f5b35a814f 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -20,9 +20,9 @@ # Returns: a list of ObjectPropertyInfo describing a devices # properties # -# Note: objects can create properties at runtime, for example to -# describe links between different devices and/or objects. These -# properties are not included in the output of this command. +# .. note:: Objects can create properties at runtime, for example to +# describe links between different devices and/or objects. These +# properties are not included in the output of this command. # # Since: 1.2 ## @@ -51,7 +51,7 @@ # supports JSON syntax without the reference counting leak that # broke hot-unplug # -# Notes: +# .. admonition:: Notes # # 1. Additional arguments depend on the type. # @@ -60,7 +60,7 @@ # # 3. It's possible to list device properties by running QEMU with # the "-device DEVICE,help" command-line argument, where DEVICE -# is the device's name +# is the device's name. # # Example: # @@ -92,15 +92,15 @@ # Errors: # - If @id is not a valid device, DeviceNotFound # -# Notes: When this command completes, the device may not be removed -# from the guest. Hot removal is an operation that requires guest -# cooperation. This command merely requests that the guest begin -# the hot removal process. Completion of the device removal -# process is signaled with a DEVICE_DELETED event. Guest reset -# will automatically complete removal for all devices. If a -# guest-side error in the hot removal process is detected, the -# device will not be removed and a DEVICE_UNPLUG_GUEST_ERROR event -# is sent. Some errors cannot be detected. +# .. note:: When this command completes, the device may not be removed +# from the guest. Hot removal is an operation that requires guest +# cooperation. This command merely requests that the guest begin the +# hot removal process. Completion of the device removal process is +# signaled with a DEVICE_DELETED event. Guest reset will +# automatically complete removal for all devices. If a guest-side +# error in the hot removal process is detected, the device will not +# be removed and a DEVICE_UNPLUG_GUEST_ERROR event is sent. Some +# errors cannot be detected. # # Since: 0.14 # diff --git a/qapi/qom.json b/qapi/qom.json index 92b0fea76c..8e75a419c3 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -195,9 +195,9 @@ # # @typename: the type name of an object # -# Note: objects can create properties at runtime, for example to -# describe links between different devices and/or objects. These -# properties are not included in the output of this command. +# .. note:: Objects can create properties at runtime, for example to +# describe links between different devices and/or objects. These +# properties are not included in the output of this command. # # Returns: a list of ObjectPropertyInfo describing object properties # @@ -614,12 +614,11 @@ # older to allow migration with newer QEMU versions. # (default: false generally, but true for machine types <= 4.0) # -# Note: prealloc=true and reserve=false cannot be set at the same -# time. With reserve=true, the behavior depends on the operating -# system: for example, Linux will not reserve swap space for -# shared file mappings -- "not applicable". In contrast, -# reserve=false will bail out if it cannot be configured -# accordingly. +# .. note:: prealloc=true and reserve=false cannot be set at the same +# time. With reserve=true, the behavior depends on the operating +# system: for example, Linux will not reserve swap space for shared +# file mappings -- "not applicable". In contrast, reserve=false will +# bail out if it cannot be configured accordingly. # # Since: 2.1 ## diff --git a/qapi/rocker.json b/qapi/rocker.json index f5225eb62c..9f95e63830 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -138,8 +138,8 @@ # # @ip-dst: IP header destination address # -# Note: optional members may or may not appear in the flow key -# depending if they're relevant to the flow key. +# .. note:: Optional members may or may not appear in the flow key +# depending if they're relevant to the flow key. # # Since: 2.4 ## @@ -168,8 +168,8 @@ # # @ip-tos: IP header TOS field # -# Note: optional members may or may not appear in the flow mask -# depending if they're relevant to the flow mask. +# .. note:: Optional members may or may not appear in the flow mask +# depending if they're relevant to the flow mask. # # Since: 2.4 ## @@ -195,8 +195,8 @@ # # @out-pport: physical output port # -# Note: optional members may or may not appear in the flow action -# depending if they're relevant to the flow action. +# .. note:: Optional members may or may not appear in the flow action +# depending if they're relevant to the flow action. # # Since: 2.4 ## @@ -288,8 +288,8 @@ # # @ttl-check: perform TTL check # -# Note: optional members may or may not appear in the group depending -# if they're relevant to the group type. +# .. note:: Optional members may or may not appear in the group depending +# if they're relevant to the group type. # # Since: 2.4 ## diff --git a/qapi/run-state.json b/qapi/run-state.json index 5ac0fec852..30cd25d3c9 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -146,9 +146,9 @@ # @reason: The @ShutdownCause which resulted in the SHUTDOWN. # (since 4.0) # -# Note: If the command-line option "-no-shutdown" has been specified, -# qemu will not exit, and a STOP event will eventually follow the -# SHUTDOWN event +# .. note:: If the command-line option "-no-shutdown" has been +# specified, qemu will not exit, and a STOP event will eventually +# follow the SHUTDOWN event. # # Since: 0.12 # @@ -247,8 +247,8 @@ # saved on disk, for example, S4 state, which is sometimes called # hibernate state # -# Note: QEMU shuts down (similar to event @SHUTDOWN) when entering -# this state +# .. note:: QEMU shuts down (similar to event @SHUTDOWN) when entering +# this state. # # Since: 1.2 # @@ -281,11 +281,11 @@ # # @action: action that has been taken # -# Note: If action is "reset", "shutdown", or "pause" the WATCHDOG -# event is followed respectively by the RESET, SHUTDOWN, or STOP -# events +# .. note:: If action is "reset", "shutdown", or "pause" the WATCHDOG +# event is followed respectively by the RESET, SHUTDOWN, or STOP +# events. # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 0.13 # diff --git a/qapi/sockets.json b/qapi/sockets.json index aa97c89768..3970118bf4 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -104,8 +104,8 @@ # # @port: port # -# Note: string types are used to allow for possible future hostname or -# service resolution support. +# .. note:: String types are used to allow for possible future hostname +# or service resolution support. # # Since: 2.8 ## @@ -179,9 +179,9 @@ # # @type: Transport type # -# Note: This type is deprecated in favor of SocketAddress. The -# difference between SocketAddressLegacy and SocketAddress is that -# the latter has fewer {} on the wire. +# .. note:: This type is deprecated in favor of SocketAddress. The +# difference between SocketAddressLegacy and SocketAddress is that +# the latter has fewer {} on the wire. # # Since: 1.3 ## diff --git a/qapi/stats.json b/qapi/stats.json index 578b52c7ef..efbbe26244 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -258,17 +258,17 @@ # # @provider: a provider to restrict the query to. # -# Note: runtime-collected statistics and their names fall outside -# QEMU's usual deprecation policies. QEMU will try to keep the -# set of available data stable, together with their names, but -# will not guarantee stability at all costs; the same is true of -# providers that source statistics externally, e.g. from Linux. -# For example, if the same value is being tracked with different -# names on different architectures or by different providers, one -# of them might be renamed. A statistic might go away if an -# algorithm is changed or some code is removed; changing a default -# might cause previously useful statistics to always report 0. -# Such changes, however, are expected to be rare. +# .. note:: Runtime-collected statistics and their names fall outside +# QEMU's usual deprecation policies. QEMU will try to keep the set +# of available data stable, together with their names, but will not +# guarantee stability at all costs; the same is true of providers +# that source statistics externally, e.g. from Linux. For example, +# if the same value is being tracked with different names on +# different architectures or by different providers, one of them +# might be renamed. A statistic might go away if an algorithm is +# changed or some code is removed; changing a default might cause +# previously useful statistics to always report 0. Such changes, +# however, are expected to be rare. # # Since: 7.1 ## diff --git a/qapi/transaction.json b/qapi/transaction.json index 07afc269d5..bcb05fdedd 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -237,10 +237,10 @@ # Errors: # - Any errors from commands in the transaction # -# Note: The transaction aborts on the first failure. Therefore, there -# will be information on only one failed operation returned in an -# error condition, and subsequent actions will not have been -# attempted. +# .. note:: The transaction aborts on the first failure. Therefore, +# there will be information on only one failed operation returned in +# an error condition, and subsequent actions will not have been +# attempted. # # Since: 1.1 # diff --git a/qapi/ui.json b/qapi/ui.json index c12f529257..a1999965e4 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -107,11 +107,10 @@ # - '+INT' where INT is the number of seconds from now (integer) # - 'INT' where INT is the absolute time in seconds # -# Notes: Time is relative to the server and currently there is no way -# to coordinate server time with client time. It is not -# recommended to use the absolute time version of the @time -# parameter unless you're sure you are on the same machine as the -# QEMU instance. +# .. note:: Time is relative to the server and currently there is no way +# to coordinate server time with client time. It is not recommended +# to use the absolute time version of the @time parameter unless +# you're sure you are on the same machine as the QEMU instance. # # Since: 7.0 ## @@ -274,7 +273,7 @@ # @unknown: No information is available about mouse mode used by the # spice server. # -# Note: spice/enums.h has a SpiceMouseMode already, hence the name. +# .. note:: spice/enums.h has a SpiceMouseMode already, hence the name. # # Since: 1.1 ## @@ -705,9 +704,9 @@ # # Since: 1.1 # -# Notes: An empty password in this command will set the password to -# the empty string. Existing clients are unaffected by executing -# this command. +# .. note:: An empty password in this command will set the password to +# the empty string. Existing clients are unaffected by executing +# this command. ## { 'command': 'change-vnc-password', 'data': { 'password': 'str' }, @@ -722,8 +721,8 @@ # # @client: client information # -# Note: This event is emitted before any authentication takes place, -# thus the authentication ID is not provided +# .. note:: This event is emitted before any authentication takes place, +# thus the authentication ID is not provided. # # Since: 0.13 # @@ -1268,10 +1267,10 @@ # # Since: 2.6 # -# Note: The consoles are visible in the qom tree, under -# /backend/console[$index]. They have a device link and head -# property, so it is possible to map which console belongs to -# which device and display. +# .. note:: The consoles are visible in the qom tree, under +# /backend/console[$index]. They have a device link and head +# property, so it is possible to map which console belongs to which +# device and display. # # Examples: # diff --git a/qapi/virtio.json b/qapi/virtio.json index 74fc27c702..b91f3cdd0d 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -559,12 +559,12 @@ # # Returns: VirtQueueStatus of the VirtQueue # -# Notes: last_avail_idx will not be displayed in the case where the -# selected VirtIODevice has a running vhost device and the -# VirtIODevice VirtQueue index (queue) does not exist for the -# corresponding vhost device vhost_virtqueue. Also, -# shadow_avail_idx will not be displayed in the case where the -# selected VirtIODevice has a running vhost device. +# .. note:: last_avail_idx will not be displayed in the case where the +# selected VirtIODevice has a running vhost device and the +# VirtIODevice VirtQueue index (queue) does not exist for the +# corresponding vhost device vhost_virtqueue. Also, shadow_avail_idx +# will not be displayed in the case where the selected VirtIODevice +# has a running vhost device. # # Since: 7.2 # diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index b3de1fb6b3..57598331c5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -422,8 +422,9 @@ # Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined # below) # -# Note: This may fail to properly report the current state as a result -# of some other guest processes having issued an fs freeze/thaw. +# .. note:: This may fail to properly report the current state as a +# result of some other guest processes having issued an fs +# freeze/thaw. # # Since: 0.15.0 ## @@ -443,9 +444,9 @@ # # Returns: Number of file systems currently frozen. # -# Note: On Windows, the command is implemented with the help of a -# Volume Shadow-copy Service DLL helper. The frozen state is -# limited for up to 10 seconds by VSS. +# .. note:: On Windows, the command is implemented with the help of a +# Volume Shadow-copy Service DLL helper. The frozen state is limited +# for up to 10 seconds by VSS. # # Since: 0.15.0 ## @@ -479,10 +480,10 @@ # # Returns: Number of file systems thawed by this call # -# Note: if return value does not match the previous call to -# guest-fsfreeze-freeze, this likely means some freezable -# filesystems were unfrozen before this call, and that the -# filesystem state may have changed before issuing this command. +# .. note:: If return value does not match the previous call to +# guest-fsfreeze-freeze, this likely means some freezable filesystems +# were unfrozen before this call, and that the filesystem state may +# have changed before issuing this command. # # Since: 0.15.0 ## @@ -560,8 +561,8 @@ # Errors: # - If suspend to disk is not supported, Unsupported # -# Notes: It's strongly recommended to issue the guest-sync command -# before sending commands when the guest resumes +# .. note:: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes. # # Since: 1.1 ## @@ -596,8 +597,8 @@ # Errors: # - If suspend to ram is not supported, Unsupported # -# Notes: It's strongly recommended to issue the guest-sync command -# before sending commands when the guest resumes +# .. note:: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes. # # Since: 1.1 ## @@ -631,8 +632,8 @@ # Errors: # - If hybrid suspend is not supported, Unsupported # -# Notes: It's strongly recommended to issue the guest-sync command -# before sending commands when the guest resumes +# .. note:: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes. # # Since: 1.1 ## @@ -1461,16 +1462,15 @@ # * POSIX: as defined by os-release(5) # * Windows: contains string "server" or "client" # -# Notes: On POSIX systems the fields @id, @name, @pretty-name, -# @version, @version-id, @variant and @variant-id follow the -# definition specified in os-release(5). Refer to the manual page -# for exact description of the fields. Their values are taken -# from the os-release file. If the file is not present in the -# system, or the values are not present in the file, the fields -# are not included. +# .. note:: On POSIX systems the fields @id, @name, @pretty-name, +# @version, @version-id, @variant and @variant-id follow the +# definition specified in os-release(5). Refer to the manual page for +# exact description of the fields. Their values are taken from the +# os-release file. If the file is not present in the system, or the +# values are not present in the file, the fields are not included. # -# On Windows the values are filled from information gathered from -# the system. +# On Windows the values are filled from information gathered from +# the system. # # Since: 2.10 ## diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index c3d20cc01b..0a13f0f541 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -547,6 +547,21 @@ class QAPISchemaParser: r'(Returns|Errors|Since|Notes?|Examples?|TODO): *', line): # tagged section + + # TODO: Remove this error sometime in 2025 or so + # after we've fully transitioned to the new qapidoc + # generator. + + # See commit message for more markup suggestions O:-) + if 'Note' in match.group(1): + emsg = ( + f"The '{match.group(1)}' section is no longer " + "supported. Please use rST's '.. note::' or " + "'.. admonition:: notes' directives, or another " + "suitable admonition instead." + ) + raise QAPIParseError(self, emsg) + doc.new_tagged_section(self.info, match.group(1)) text = line[match.end():] if text: diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err index 5f03a6d733..711a0d629c 100644 --- a/tests/qapi-schema/doc-empty-section.err +++ b/tests/qapi-schema/doc-empty-section.err @@ -1 +1 @@ -doc-empty-section.json:6: text required after 'Note:' +doc-empty-section.json:6: text required after 'Errors:' diff --git a/tests/qapi-schema/doc-empty-section.json b/tests/qapi-schema/doc-empty-section.json index f3384e9a3b..f179d3eff6 100644 --- a/tests/qapi-schema/doc-empty-section.json +++ b/tests/qapi-schema/doc-empty-section.json @@ -3,6 +3,6 @@ ## # @foo: # -# Note: +# Errors: ## { 'command': 'foo', 'data': {'a': 'int'} } diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 8b39eb946a..32ff910b4f 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -157,7 +157,7 @@ # @cmd-feat1: a feature # @cmd-feat2: another feature # -# Note: @arg3 is undocumented +# .. note:: @arg3 is undocumented # # Returns: @Object # @@ -165,7 +165,7 @@ # # TODO: frobnicate # -# Notes: +# .. admonition:: Notes # # - Lorem ipsum dolor sit amet # - Ut enim ad minim veniam diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 435f6e6d76..631dc9f8da 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -169,15 +169,17 @@ description starts on the same line a feature feature=cmd-feat2 another feature - section=Note -@arg3 is undocumented + section=None +.. note:: @arg3 is undocumented section=Returns @Object section=Errors some section=TODO frobnicate - section=Notes + section=None +.. admonition:: Notes + - Lorem ipsum dolor sit amet - Ut enim ad minim veniam diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 847db70412..d8bfa742c2 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -193,11 +193,9 @@ Features "cmd-feat2" another feature +Note: -Note -~~~~ - -"arg3" is undocumented + "arg3" is undocumented Returns @@ -211,9 +209,7 @@ Errors some - -Notes -~~~~~ +Notes: * Lorem ipsum dolor sit amet diff --git a/tests/qapi-schema/doc-interleaved-section.json b/tests/qapi-schema/doc-interleaved-section.json index adb29e98da..eec01ed565 100644 --- a/tests/qapi-schema/doc-interleaved-section.json +++ b/tests/qapi-schema/doc-interleaved-section.json @@ -10,7 +10,7 @@ # # bao # -# Note: a section. +# TODO: a section. # # @foobar: catch this # From 649c6fa4eec03667b29d52685d327c73a7e9a467 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 18:21:17 -0400 Subject: [PATCH 2038/2884] qapi: update prose in note blocks Where I've noticed, rephrase the note to read more fluently. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240626222128.406106-12-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/block-core.json | 4 ++-- qga/qapi-schema.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index cacedfb771..9ef23ec02a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -6048,9 +6048,9 @@ # # @name: the name of the internal snapshot to be created # -# .. note:: In transaction, if @name is empty, or any snapshot matching +# .. note:: In a transaction, if @name is empty or any snapshot matching # @name exists, the operation will fail. Only some image formats -# support it, for example, qcow2, and rbd. +# support it; for example, qcow2, and rbd. # # Since: 1.7 ## diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 57598331c5..1273d85bb5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -480,7 +480,7 @@ # # Returns: Number of file systems thawed by this call # -# .. note:: If return value does not match the previous call to +# .. note:: If the return value does not match the previous call to # guest-fsfreeze-freeze, this likely means some freezable filesystems # were unfrozen before this call, and that the filesystem state may # have changed before issuing this command. From 543ff13a20663b9aa93a4531fc75a1ebe7c69269 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 18:21:18 -0400 Subject: [PATCH 2039/2884] qapi: add markup to note blocks Generally, surround command-line options with ``literal`` markup to help it stand out from prose in rendered HTML, and add cross-references to replace "see also" messages. References to types, values, and other QAPI definitions are not yet adjusted here; they will be converted en masse in a subsequent patch after the new QAPI doc generator is merged. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240626222128.406106-13-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/control.json | 4 ++-- qapi/misc.json | 8 ++++---- qapi/qdev.json | 2 +- qapi/run-state.json | 2 +- qapi/sockets.json | 2 +- qapi/ui.json | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qapi/control.json b/qapi/control.json index 59d5e00c15..fe2af45120 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -24,8 +24,8 @@ # # .. note:: This command is valid exactly when first connecting: it must # be issued before any other command will be accepted, and will fail -# once the monitor is accepting other commands. (see qemu -# docs/interop/qmp-spec.rst) +# once the monitor is accepting other commands. +# (see :doc:`/interop/qmp-spec`) # # .. note:: The QMP client needs to explicitly enable QMP capabilities, # otherwise all the QMP capabilities will be turned off by default. diff --git a/qapi/misc.json b/qapi/misc.json index 13ea82f525..b04efbadec 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -104,7 +104,7 @@ # Returns a list of information about each iothread. # # .. note:: This list excludes the QEMU main loop thread, which is not -# declared using the -object iothread command-line option. It is +# declared using the ``-object iothread`` command-line option. It is # always the main thread of the process. # # Returns: a list of @IOThreadInfo for each iothread @@ -138,8 +138,8 @@ # # .. note:: This function will succeed even if the guest is already in # the stopped state. In "inmigrate" state, it will ensure that the -# guest remains paused once migration finishes, as if the -S option -# was passed on the command line. +# guest remains paused once migration finishes, as if the ``-S`` +# option was passed on the command line. # # In the "suspended" state, it will completely stop the VM and cause # a transition to the "paused" state. (Since 9.0) @@ -161,7 +161,7 @@ # .. note:: This command will succeed if the guest is currently running. # It will also succeed if the guest is in the "inmigrate" state; in # this case, the effect of the command is to make sure the guest -# starts once migration finishes, removing the effect of the -S +# starts once migration finishes, removing the effect of the ``-S`` # command line option if it was passed. # # If the VM was previously suspended, and not been reset or woken, diff --git a/qapi/qdev.json b/qapi/qdev.json index f5b35a814f..d031fc3590 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -59,7 +59,7 @@ # the 'docs/qdev-device-use.txt' file. # # 3. It's possible to list device properties by running QEMU with -# the "-device DEVICE,help" command-line argument, where DEVICE +# the ``-device DEVICE,help`` command-line argument, where DEVICE # is the device's name. # # Example: diff --git a/qapi/run-state.json b/qapi/run-state.json index 30cd25d3c9..4d40c88876 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -146,7 +146,7 @@ # @reason: The @ShutdownCause which resulted in the SHUTDOWN. # (since 4.0) # -# .. note:: If the command-line option "-no-shutdown" has been +# .. note:: If the command-line option ``-no-shutdown`` has been # specified, qemu will not exit, and a STOP event will eventually # follow the SHUTDOWN event. # diff --git a/qapi/sockets.json b/qapi/sockets.json index 3970118bf4..4d78d2ccb7 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -181,7 +181,7 @@ # # .. note:: This type is deprecated in favor of SocketAddress. The # difference between SocketAddressLegacy and SocketAddress is that -# the latter has fewer {} on the wire. +# the latter has fewer ``{}`` on the wire. # # Since: 1.3 ## diff --git a/qapi/ui.json b/qapi/ui.json index a1999965e4..5bcccbfc93 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1268,7 +1268,7 @@ # Since: 2.6 # # .. note:: The consoles are visible in the qom tree, under -# /backend/console[$index]. They have a device link and head +# ``/backend/console[$index]``. They have a device link and head # property, so it is possible to map which console belongs to which # device and display. # From 65fa48c79f20a37c7204f9e43dd7cc3058aa066c Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 18:21:19 -0400 Subject: [PATCH 2040/2884] qapi/parser: don't parse rST markup as section headers The double-colon synax is rST formatting that precedes a literal code block. We do not want to capture these as QAPI-specific sections. Coerce blocks that start with e.g. "Example::" to be parsed as untagged paragraphs instead of special tagged sections. Signed-off-by: John Snow <jsnow@redhat.com> Message-ID: <20240626222128.406106-14-jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Indentation tweaked for consistency] Signed-off-by: Markus Armbruster <armbru@redhat.com> --- scripts/qapi/parser.py | 9 +++++++-- tests/qapi-schema/doc-good.json | 3 +++ tests/qapi-schema/doc-good.out | 3 +++ tests/qapi-schema/doc-good.txt | 3 +++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 0a13f0f541..6ad5663e54 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -544,10 +544,15 @@ class QAPISchemaParser: line = self.get_doc_indented(doc) no_more_args = True elif match := re.match( - r'(Returns|Errors|Since|Notes?|Examples?|TODO): *', - line): + r'(Returns|Errors|Since|Notes?|Examples?|TODO)' + r'(?!::): *', + line, + ): # tagged section + # Note: "sections" with two colons are left alone as + # rST markup and not interpreted as a section heading. + # TODO: Remove this error sometime in 2025 or so # after we've fully transitioned to the new qapidoc # generator. diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 32ff910b4f..b565895858 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -181,6 +181,9 @@ # - *verbatim* # - {braces} # +# Note:: +# Ceci n'est pas une note +# # Since: 2.10 ## { 'command': 'cmd', diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 631dc9f8da..a8e9456f60 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -190,6 +190,9 @@ frobnicate section=Examples - *verbatim* - {braces} + section=None +Note:: + Ceci n'est pas une note section=Since 2.10 doc symbol=cmd-boxed diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index d8bfa742c2..30d457e548 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -231,6 +231,9 @@ Examples - *verbatim* - {braces} +Note:: + Ceci n'est pas une note + Since ~~~~~ From e389929d19a543ea5b34d02553b355f9f1c03162 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Fri, 28 Jun 2024 13:27:56 +0200 Subject: [PATCH 2041/2884] sphinx/qapidoc: Fix to generate doc for explicit, unboxed arguments When a command's arguments are specified as an explicit type T, generated documentation points to the members of T. Example: ## # @announce-self: # # Trigger generation of broadcast RARP frames to update network [...] ## { 'command': 'announce-self', 'boxed': true, 'data' : 'AnnounceParameters'} generates "announce-self" (Command) ------------------------- Trigger generation of broadcast RARP frames to update network [...] Arguments ~~~~~~~~~ The members of "AnnounceParameters" Except when the command takes its arguments unboxed , i.e. it doesn't have 'boxed': true, we generate *nothing*. A few commands have a reference in their doc comment to compensate, but most don't. Example: ## # @blockdev-snapshot-sync: # # Takes a synchronous snapshot of a block device. # # For the arguments, see the documentation of BlockdevSnapshotSync. [...] ## { 'command': 'blockdev-snapshot-sync', 'data': 'BlockdevSnapshotSync', 'allow-preconfig': true } generates "blockdev-snapshot-sync" (Command) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Takes a synchronous snapshot of a block device. For the arguments, see the documentation of BlockdevSnapshotSync. [...] Same for event data. Fix qapidoc.py to generate the reference regardless of boxing. Delete now redundant references in the doc comments. Fixes: 4078ee5469e5 (docs/sphinx: Add new qapi-doc Sphinx extension) Cc: qemu-stable@nongnu.org Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240628112756.794237-1-armbru@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> --- docs/sphinx/qapidoc.py | 12 +++++------- qapi/block-core.json | 7 ------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index efcd84656f..2b06750a3c 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -230,15 +230,15 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): section += dlnode return [section] - def _nodes_for_arguments(self, doc, boxed_arg_type): + def _nodes_for_arguments(self, doc, arg_type): """Return list of doctree nodes for the arguments section""" - if boxed_arg_type: + if arg_type and not arg_type.is_implicit(): assert not doc.args section = self._make_section('Arguments') dlnode = nodes.definition_list() dlnode += self._make_dlitem( [nodes.Text('The members of '), - nodes.literal('', boxed_arg_type.name)], + nodes.literal('', arg_type.name)], None) section += dlnode return [section] @@ -352,8 +352,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): allow_preconfig, coroutine): doc = self._cur_doc self._add_doc('Command', - self._nodes_for_arguments(doc, - arg_type if boxed else None) + self._nodes_for_arguments(doc, arg_type) + self._nodes_for_features(doc) + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) @@ -361,8 +360,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): def visit_event(self, name, info, ifcond, features, arg_type, boxed): doc = self._cur_doc self._add_doc('Event', - self._nodes_for_arguments(doc, - arg_type if boxed else None) + self._nodes_for_arguments(doc, arg_type) + self._nodes_for_features(doc) + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) diff --git a/qapi/block-core.json b/qapi/block-core.json index 9ef23ec02a..096bdbe0aa 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1675,8 +1675,6 @@ # # Takes a synchronous snapshot of a block device. # -# For the arguments, see the documentation of BlockdevSnapshotSync. -# # Errors: # - If @device is not a valid block device, DeviceNotFound # @@ -1705,8 +1703,6 @@ # device, the block device changes to using 'overlay' as its new # active image. # -# For the arguments, see the documentation of BlockdevSnapshot. -# # Features: # # @allow-write-only-overlay: If present, the check whether this @@ -6065,9 +6061,6 @@ # string, or a snapshot with name already exists, the operation will # fail. # -# For the arguments, see the documentation of -# BlockdevSnapshotInternal. -# # Errors: # - If @device is not a valid block device, GenericError # - If any snapshot matching @name exists, or @name is empty, From eec2f9cc69ba68c09dfba6812f58e550c1e83d68 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:15 +0800 Subject: [PATCH 2042/2884] hw/net:ftgmac100: update memory region size to 64KB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the datasheet of ASPEED SOCs, one MAC controller owns 128KB of register space for AST2500. However, one MAC controller only owns 64KB of register space for AST2600 and AST2700. It set the memory region size 128KB and it occupied another controllers Address Spaces. Update one MAC controller memory region size to 0x1000 because AST2500 did not use register spaces over than 64KB. Introduce a new container region size to 0x1000 and its range is from 0 to 0xfff. This container is mapped a sub region for the current set of register. This sub region range is from 0 to 0xff. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/net/ftgmac100.c | 11 ++++++++--- include/hw/net/ftgmac100.h | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 25e4c0cd5b..9e1f12cd33 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -1107,9 +1107,14 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) s->rxdes0_edorr = FTGMAC100_RXDES0_EDORR; } - memory_region_init_io(&s->iomem, OBJECT(dev), &ftgmac100_ops, s, - TYPE_FTGMAC100, 0x2000); - sysbus_init_mmio(sbd, &s->iomem); + memory_region_init(&s->iomem_container, OBJECT(s), + TYPE_FTGMAC100 ".container", FTGMAC100_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem_container); + + memory_region_init_io(&s->iomem, OBJECT(s), &ftgmac100_ops, s, + TYPE_FTGMAC100 ".regs", FTGMAC100_REG_MEM_SIZE); + memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + sysbus_init_irq(sbd, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index 765d1538a4..269446e858 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -14,6 +14,9 @@ #define TYPE_FTGMAC100 "ftgmac100" OBJECT_DECLARE_SIMPLE_TYPE(FTGMAC100State, FTGMAC100) +#define FTGMAC100_MEM_SIZE 0x1000 +#define FTGMAC100_REG_MEM_SIZE 0x100 + #include "hw/sysbus.h" #include "net/net.h" @@ -30,6 +33,7 @@ struct FTGMAC100State { NICState *nic; NICConf conf; qemu_irq irq; + MemoryRegion iomem_container; MemoryRegion iomem; uint8_t frame[FTGMAC100_MAX_FRAME_SIZE]; From 0b51fd0f99f09df4560c267922cabdbc67198ae8 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:16 +0800 Subject: [PATCH 2043/2884] hw/net:ftgmac100: update ring base address to 64 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update TX and RX ring base address data type to uint64_t for 64 bits dram address DMA support. Both "Normal Priority Transmit Ring Base Address Register(0x20)" and "Receive Ring Base Address Register (0x24)" are used for saving the low part physical address of descriptor manager. Therefore, changes to set TX and RX descriptor manager address bits [31:0] in ftgmac100_read and ftgmac100_write functions. Incrementing the version of vmstate to 2. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/net/ftgmac100.c | 33 ++++++++++++++++----------------- include/hw/net/ftgmac100.h | 9 ++++----- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 9e1f12cd33..d026242e2b 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -515,12 +515,12 @@ out: return frame_size; } -static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, - uint32_t tx_descriptor) +static void ftgmac100_do_tx(FTGMAC100State *s, uint64_t tx_ring, + uint64_t tx_descriptor) { int frame_size = 0; uint8_t *ptr = s->frame; - uint32_t addr = tx_descriptor; + uint64_t addr = tx_descriptor; uint32_t flags = 0; while (1) { @@ -726,9 +726,9 @@ static uint64_t ftgmac100_read(void *opaque, hwaddr addr, unsigned size) case FTGMAC100_MATH1: return s->math[1]; case FTGMAC100_RXR_BADR: - return s->rx_ring; + return extract64(s->rx_ring, 0, 32); case FTGMAC100_NPTXR_BADR: - return s->tx_ring; + return extract64(s->tx_ring, 0, 32); case FTGMAC100_ITC: return s->itc; case FTGMAC100_DBLAC: @@ -799,9 +799,8 @@ static void ftgmac100_write(void *opaque, hwaddr addr, HWADDR_PRIx "\n", __func__, value); return; } - - s->rx_ring = value; - s->rx_descriptor = s->rx_ring; + s->rx_ring = deposit64(s->rx_ring, 0, 32, value); + s->rx_descriptor = deposit64(s->rx_descriptor, 0, 32, value); break; case FTGMAC100_RBSR: /* DMA buffer size */ @@ -814,8 +813,8 @@ static void ftgmac100_write(void *opaque, hwaddr addr, HWADDR_PRIx "\n", __func__, value); return; } - s->tx_ring = value; - s->tx_descriptor = s->tx_ring; + s->tx_ring = deposit64(s->tx_ring, 0, 32, value); + s->tx_descriptor = deposit64(s->tx_descriptor, 0, 32, value); break; case FTGMAC100_NPTXPD: /* Trigger transmit */ @@ -957,7 +956,7 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); FTGMAC100Desc bd; uint32_t flags = 0; - uint32_t addr; + uint64_t addr; uint32_t crc; uint32_t buf_addr; uint8_t *crc_ptr; @@ -1126,18 +1125,14 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_ftgmac100 = { .name = TYPE_FTGMAC100, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32(irq_state, FTGMAC100State), VMSTATE_UINT32(isr, FTGMAC100State), VMSTATE_UINT32(ier, FTGMAC100State), VMSTATE_UINT32(rx_enabled, FTGMAC100State), - VMSTATE_UINT32(rx_ring, FTGMAC100State), VMSTATE_UINT32(rbsr, FTGMAC100State), - VMSTATE_UINT32(tx_ring, FTGMAC100State), - VMSTATE_UINT32(rx_descriptor, FTGMAC100State), - VMSTATE_UINT32(tx_descriptor, FTGMAC100State), VMSTATE_UINT32_ARRAY(math, FTGMAC100State, 2), VMSTATE_UINT32(itc, FTGMAC100State), VMSTATE_UINT32(aptcr, FTGMAC100State), @@ -1156,6 +1151,10 @@ static const VMStateDescription vmstate_ftgmac100 = { VMSTATE_UINT32(phy_int_mask, FTGMAC100State), VMSTATE_UINT32(txdes0_edotr, FTGMAC100State), VMSTATE_UINT32(rxdes0_edorr, FTGMAC100State), + VMSTATE_UINT64(rx_ring, FTGMAC100State), + VMSTATE_UINT64(tx_ring, FTGMAC100State), + VMSTATE_UINT64(rx_descriptor, FTGMAC100State), + VMSTATE_UINT64(tx_descriptor, FTGMAC100State), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index 269446e858..aae57ae8cb 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -42,10 +42,6 @@ struct FTGMAC100State { uint32_t isr; uint32_t ier; uint32_t rx_enabled; - uint32_t rx_ring; - uint32_t rx_descriptor; - uint32_t tx_ring; - uint32_t tx_descriptor; uint32_t math[2]; uint32_t rbsr; uint32_t itc; @@ -58,7 +54,10 @@ struct FTGMAC100State { uint32_t phycr; uint32_t phydata; uint32_t fcr; - + uint64_t rx_ring; + uint64_t rx_descriptor; + uint64_t tx_ring; + uint64_t tx_descriptor; uint32_t phy_status; uint32_t phy_control; From 578c6e9ed5d0484da5b478f932c420ecc4f751f6 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:17 +0800 Subject: [PATCH 2044/2884] hw/net:ftgmac100: introduce TX and RX ring base address high registers to support 64 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASPEED AST2700 SOC is a 64 bits quad core CPUs (Cortex-a35) And the base address of dram is "0x4 00000000" which is 64bits address. It have "Normal Priority Transmit Ring Base Address Register High(0x17C)", "High Priority Transmit Ring Base Address Register High(0x184)" and "Receive Ring Base Address Register High(0x18C)" to save the high part physical address of descriptor manager. Ex: TX descriptor manager address [34:0] The "Normal Priority Transmit Ring Base Address Register High(0x17C)" bits [2:0] which corresponds the bits [34:32] of the 64 bits address of the TX ring buffer address. The "Normal Priority Transmit Ring Base Address Register(0x20)" bits [31:0] which corresponds the bits [31:0] of the 64 bits address of the TX ring buffer address. Introduce a new sub region which size is 0x100 for the set of new registers and map it at 0x100 in the container region. This sub region range is from 0x100 to 0x1ff. Introduce a new property and object attribute to activate the region for new registers. Introduce a new memop handlers for the new register read and write. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/net/ftgmac100.c | 82 ++++++++++++++++++++++++++++++++++++++ include/hw/net/ftgmac100.h | 4 ++ 2 files changed, 86 insertions(+) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index d026242e2b..68956aeb94 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -56,6 +56,16 @@ #define FTGMAC100_PHYDATA 0x64 #define FTGMAC100_FCR 0x68 +/* + * FTGMAC100 registers high + * + * values below are offset by - FTGMAC100_REG_HIGH_OFFSET from datasheet + * because its memory region is start at FTGMAC100_REG_HIGH_OFFSET + */ +#define FTGMAC100_NPTXR_BADR_HIGH (0x17C - FTGMAC100_REG_HIGH_OFFSET) +#define FTGMAC100_HPTXR_BADR_HIGH (0x184 - FTGMAC100_REG_HIGH_OFFSET) +#define FTGMAC100_RXR_BADR_HIGH (0x18C - FTGMAC100_REG_HIGH_OFFSET) + /* * Interrupt status register & interrupt enable register */ @@ -913,6 +923,60 @@ static void ftgmac100_write(void *opaque, hwaddr addr, ftgmac100_update_irq(s); } +static uint64_t ftgmac100_high_read(void *opaque, hwaddr addr, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + uint64_t val = 0; + + switch (addr) { + case FTGMAC100_NPTXR_BADR_HIGH: + val = extract64(s->tx_ring, 32, 32); + break; + case FTGMAC100_HPTXR_BADR_HIGH: + /* High Priority Transmit Ring Base High Address */ + qemu_log_mask(LOG_UNIMP, "%s: read to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + case FTGMAC100_RXR_BADR_HIGH: + val = extract64(s->rx_ring, 32, 32); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + return val; +} + +static void ftgmac100_high_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + + switch (addr) { + case FTGMAC100_NPTXR_BADR_HIGH: + s->tx_ring = deposit64(s->tx_ring, 32, 32, value); + s->tx_descriptor = deposit64(s->tx_descriptor, 32, 32, value); + break; + case FTGMAC100_HPTXR_BADR_HIGH: + /* High Priority Transmit Ring Base High Address */ + qemu_log_mask(LOG_UNIMP, "%s: write to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + case FTGMAC100_RXR_BADR_HIGH: + s->rx_ring = deposit64(s->rx_ring, 32, 32, value); + s->rx_descriptor = deposit64(s->rx_descriptor, 32, 32, value); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + ftgmac100_update_irq(s); +} + static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len) { unsigned mcast_idx; @@ -1077,6 +1141,14 @@ static const MemoryRegionOps ftgmac100_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const MemoryRegionOps ftgmac100_high_ops = { + .read = ftgmac100_high_read, + .write = ftgmac100_high_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void ftgmac100_cleanup(NetClientState *nc) { FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); @@ -1114,6 +1186,15 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) TYPE_FTGMAC100 ".regs", FTGMAC100_REG_MEM_SIZE); memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + if (s->dma64) { + memory_region_init_io(&s->iomem_high, OBJECT(s), &ftgmac100_high_ops, + s, TYPE_FTGMAC100 ".regs.high", + FTGMAC100_REG_HIGH_MEM_SIZE); + memory_region_add_subregion(&s->iomem_container, + FTGMAC100_REG_HIGH_OFFSET, + &s->iomem_high); + } + sysbus_init_irq(sbd, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); @@ -1162,6 +1243,7 @@ static const VMStateDescription vmstate_ftgmac100 = { static Property ftgmac100_properties[] = { DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), + DEFINE_PROP_BOOL("dma64", FTGMAC100State, dma64, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index aae57ae8cb..24ccdf0260 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -16,6 +16,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(FTGMAC100State, FTGMAC100) #define FTGMAC100_MEM_SIZE 0x1000 #define FTGMAC100_REG_MEM_SIZE 0x100 +#define FTGMAC100_REG_HIGH_MEM_SIZE 0x100 +#define FTGMAC100_REG_HIGH_OFFSET 0x100 #include "hw/sysbus.h" #include "net/net.h" @@ -35,6 +37,7 @@ struct FTGMAC100State { qemu_irq irq; MemoryRegion iomem_container; MemoryRegion iomem; + MemoryRegion iomem_high; uint8_t frame[FTGMAC100_MAX_FRAME_SIZE]; @@ -68,6 +71,7 @@ struct FTGMAC100State { bool aspeed; uint32_t txdes0_edotr; uint32_t rxdes0_edorr; + bool dma64; }; #define TYPE_ASPEED_MII "aspeed-mmi" From 2095468d2cce73d1d3825cb953bd5114fffe73c8 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:18 +0800 Subject: [PATCH 2045/2884] hw/net:ftgmac100: update TX and RX packet buffers address to 64 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASPEED AST2700 SOC is a 64 bits quad core CPUs (Cortex-a35) And the base address of dram is "0x4 00000000" which is 64bits address. It have "TXDES 2" and "RXDES 2" to save the high part physical address of packet buffer. Ex: TX packet buffer address [34:0] The "TXDES 2" bits [18:16] which corresponds the bits [34:32] of the 64 bits address of the TX packet buffer address and "TXDES 3" bits [31:0] which corresponds the bits [31:0] of the 64 bits address of the TX packet buffer address. Update TX and RX packet buffers address type to 64 bits for dram 64 bits address DMA support. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/net/ftgmac100.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 68956aeb94..80f9cd56d5 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -175,6 +175,8 @@ #define FTGMAC100_TXDES1_TX2FIC (1 << 30) #define FTGMAC100_TXDES1_TXIC (1 << 31) +#define FTGMAC100_TXDES2_TXBUF_BADR_HI(x) (((x) >> 16) & 0x7) + /* * Receive descriptor */ @@ -208,13 +210,15 @@ #define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) #define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) +#define FTGMAC100_RXDES2_RXBUF_BADR_HI(x) (((x) >> 16) & 0x7) + /* * Receive and transmit Buffer Descriptor */ typedef struct { uint32_t des0; uint32_t des1; - uint32_t des2; /* not used by HW */ + uint32_t des2; /* used by HW 64 bits DMA */ uint32_t des3; } FTGMAC100Desc; @@ -531,6 +535,7 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint64_t tx_ring, int frame_size = 0; uint8_t *ptr = s->frame; uint64_t addr = tx_descriptor; + uint64_t buf_addr = 0; uint32_t flags = 0; while (1) { @@ -569,7 +574,12 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint64_t tx_ring, len = sizeof(s->frame) - frame_size; } - if (dma_memory_read(&address_space_memory, bd.des3, + buf_addr = bd.des3; + if (s->dma64) { + buf_addr = deposit64(buf_addr, 32, 32, + FTGMAC100_TXDES2_TXBUF_BADR_HI(bd.des2)); + } + if (dma_memory_read(&address_space_memory, buf_addr, ptr, len, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to read packet @ 0x%x\n", __func__, bd.des3); @@ -1022,7 +1032,7 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, uint32_t flags = 0; uint64_t addr; uint32_t crc; - uint32_t buf_addr; + uint64_t buf_addr = 0; uint8_t *crc_ptr; uint32_t buf_len; size_t size = len; @@ -1087,7 +1097,12 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, if (size < 4) { buf_len += size - 4; } + buf_addr = bd.des3; + if (s->dma64) { + buf_addr = deposit64(buf_addr, 32, 32, + FTGMAC100_RXDES2_RXBUF_BADR_HI(bd.des2)); + } if (first && proto == ETH_P_VLAN && buf_len >= 18) { bd.des1 = lduw_be_p(buf + 14) | FTGMAC100_RXDES1_VLANTAG_AVAIL; From f2146bc6cb98e3e6d5749a6a974a53a1a1a754fc Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:19 +0800 Subject: [PATCH 2046/2884] aspeed/soc: set dma64 property for AST2700 ftgmac100 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASPEED AST2700 SOC is a 64 bits quad core CPUs (Cortex-a35) And the base address of dram is "0x4 00000000" which is 64bits address. Set dma64 property for ftgmac100 model to support 64bits dram address DMA. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/arm/aspeed_ast27x0.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 18e6a8b10c..a9fb0d4b88 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -552,9 +552,12 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) return; } + /* Net */ for (i = 0; i < sc->macs_num; i++) { object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, &error_abort); + object_property_set_bool(OBJECT(&s->ftgmac100[i]), "dma64", true, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } From 61f93767751341e29445c052d4db53791aeab035 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:20 +0800 Subject: [PATCH 2047/2884] hw/block: m25p80: support quad mode for w25q01jvq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the w25q01jv datasheet at page 16, it is required to set QE bit in "Status Register 2". Besides, users are able to utilize "Write Status Register 1(0x01)" command to set QE bit in "Status Register 2" and utilize "Read Status Register 2(0x35)" command to get the QE bit status. To support quad mode for w25q01jvq, update collecting data needed 2 bytes for WRSR command in decode_new_cmd function and verify QE bit at the second byte of collecting data bit 2 in complete_collecting_data. Update RDCR_EQIO command to set bit 2 of return data if quad mode enable in decode_new_cmd. Signed-off-by: Troy Lee <troy_lee@aspeedtech.com> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/block/m25p80.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 8dec134832..9e99107b42 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -416,6 +416,7 @@ typedef enum { /* * Micron: 0x35 - enable QPI * Spansion: 0x35 - read control register + * Winbond: 0x35 - quad enable */ RDCR_EQIO = 0x35, RSTQIO = 0xf5, @@ -798,6 +799,11 @@ static void complete_collecting_data(Flash *s) s->four_bytes_address_mode = extract32(s->data[1], 5, 1); } break; + case MAN_WINBOND: + if (s->len > 1) { + s->quad_enable = !!(s->data[1] & 0x02); + } + break; default: break; } @@ -1254,6 +1260,10 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->needed_bytes = 2; s->state = STATE_COLLECTING_VAR_LEN_DATA; break; + case MAN_WINBOND: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_VAR_LEN_DATA; + break; default: s->needed_bytes = 1; s->state = STATE_COLLECTING_DATA; @@ -1431,6 +1441,12 @@ static void decode_new_cmd(Flash *s, uint32_t value) case MAN_MACRONIX: s->quad_enable = true; break; + case MAN_WINBOND: + s->data[0] = (!!s->quad_enable) << 1; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; default: break; } From d8b76d9ab1860d116375b657605312e5ad493fdd Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:21 +0800 Subject: [PATCH 2048/2884] machine_aspeed.py: update to test ASPEED OpenBMC SDK v09.02 for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update test case to test ASPEED OpenBMC SDK v09.02 for AST2700. ASPEED fixed TX mask issue from linux/drivers/ftgmac100.c. It is required to use ASPEED OpenBMC SDK since v09.02 for AST2700 QEMU network testing. A test image is downloaded from the ASPEED Forked OpenBMC GitHub release repository : https://github.com/AspeedTech-BMC/openbmc/releases/ Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- tests/avocado/machine_aspeed.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index 3a20644fb2..13fe128fc9 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -387,15 +387,15 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): year = time.strftime("%Y") self.ssh_command_output_contains('/sbin/hwclock -f /dev/rtc1', year); - def test_aarch64_ast2700_evb_sdk_v09_01(self): + def test_aarch64_ast2700_evb_sdk_v09_02(self): """ :avocado: tags=arch:aarch64 :avocado: tags=machine:ast2700-evb """ image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' - 'download/v09.01/ast2700-default-obmc.tar.gz') - image_hash = 'b1cc0fd73c7650d34c9c8459a243f52a91e9e27144b8608b2645ab19461d1e07' + 'download/v09.02/ast2700-default-obmc.tar.gz') + image_hash = 'ac969c2602f4e6bdb69562ff466b89ae3fe1d86e1f6797bb7969d787f82116a7' image_path = self.fetch_asset(image_url, asset_hash=image_hash, algorithm='sha256') archive.extract(image_path, self.workdir) From d847ea7cfc6321e2519f587d4077428d90557178 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 4 Jul 2024 16:29:22 +0800 Subject: [PATCH 2049/2884] machine_aspeed.py: update to test network for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update test case to test network connection via SSH. Test command: ``` cd build pyvenv/bin/avocado run ../qemu/tests/avocado/machine_aspeed.py:AST2x00MachineSDK.test_aarch64_ast2700_evb_sdk_v09_02 ``` Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- tests/avocado/machine_aspeed.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index 13fe128fc9..f66ad38d35 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -313,14 +313,14 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): def do_test_aarch64_aspeed_sdk_start(self, image): self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw') + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user,hostfwd=:127.0.0.1:0-:22') self.vm.launch() self.wait_for_console_pattern('U-Boot 2023.10') self.wait_for_console_pattern('## Loading kernel from FIT Image') self.wait_for_console_pattern('Starting kernel ...') - self.wait_for_console_pattern("systemd[1]: Hostname set to") @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') @@ -436,4 +436,6 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): self.vm.add_args('-smp', str(num_cpu)) self.do_test_aarch64_aspeed_sdk_start(image_dir + 'image-bmc') + self.wait_for_console_pattern('nodistro.0 ast2700-default ttyS12') + self.ssh_connect('root', '0penBmc', False) From 37baedf8e8a78cf799956447e40da79b967882bb Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:53 +0200 Subject: [PATCH 2050/2884] virtio-iommu: Fix error handling in virtio_iommu_set_host_iova_ranges() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case no IOMMUPciBus/IOMMUDevice are found we need to properly set the error handle and return. Fixes : Coverity CID 1549006 Signed-off-by: Eric Auger <eric.auger@redhat.com> Fixes: cf2647a76e ("virtio-iommu: Compute host reserved regions") Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-iommu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 7c54c6b5e2..8fe69ab094 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -563,10 +563,15 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, int ret = -EINVAL; if (!sbus) { - error_report("%s no sbus", __func__); + error_setg(errp, "%s: no IOMMUPciBus found!", __func__); + return ret; } sdev = sbus->pbdev[devfn]; + if (!sdev) { + error_setg(errp, "%s: no IOMMUDevice found!", __func__); + return ret; + } current_ranges = sdev->host_resv_ranges; From 3966bca539967a05b3256549eb00f9488c2790e6 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:54 +0200 Subject: [PATCH 2051/2884] vfio-container-base: Introduce vfio_container_get_iova_ranges() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce vfio_container_get_iova_ranges() to retrieve the usable IOVA regions of the base container and use it in the Host IOMMU device implementations of get_iova_ranges() callback. We also fix a UAF bug as the list was shallow copied while g_list_free_full() was used both on the single call site, in virtio_iommu_set_iommu_device() but also in vfio_container_instance_finalize(). Instead use g_list_copy_deep. Fixes: cf2647a76e ("virtio-iommu: Compute host reserved regions") Signed-off-by: Eric Auger <eric.auger@redhat.com> Suggested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/vfio/container-base.c | 15 +++++++++++++++ hw/vfio/container.c | 8 +------- hw/vfio/iommufd.c | 8 +------- include/hw/vfio/vfio-container-base.h | 2 ++ 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 50b1664f89..809b157674 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -83,6 +83,21 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, errp); } +static gpointer copy_iova_range(gconstpointer src, gpointer data) +{ + Range *source = (Range *)src; + Range *dest = g_new(Range, 1); + + range_set_bounds(dest, range_lob(source), range_upb(source)); + return dest; +} + +GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer) +{ + assert(bcontainer); + return g_list_copy_deep(bcontainer->iova_ranges, copy_iova_range, NULL); +} + static void vfio_container_instance_finalize(Object *obj) { VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 88ede913d6..0804c1b8de 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1168,15 +1168,9 @@ static GList * hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) { VFIODevice *vdev = hiod->agent; - GList *l = NULL; g_assert(vdev); - - if (vdev->bcontainer) { - l = g_list_copy(vdev->bcontainer->iova_ranges); - } - - return l; + return vfio_container_get_iova_ranges(vdev->bcontainer); } static void vfio_iommu_legacy_instance_init(Object *obj) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index c2f158e603..890d8d6a38 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -647,15 +647,9 @@ static GList * hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) { VFIODevice *vdev = hiod->agent; - GList *l = NULL; g_assert(vdev); - - if (vdev->bcontainer) { - l = g_list_copy(vdev->bcontainer->iova_ranges); - } - - return l; + return vfio_container_get_iova_ranges(vdev->bcontainer); } static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 419e45ee7a..45d7c40fce 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -86,6 +86,8 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); +GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); + #define TYPE_VFIO_IOMMU "vfio-iommu" #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" From d59ca1ca17480fc776aa02bf536ca3c87dc79323 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:55 +0200 Subject: [PATCH 2052/2884] HostIOMMUDevice : remove Error handle from get_iova_ranges callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error handle argument is not used anywhere. let's remove it. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/vfio/container.c | 2 +- hw/vfio/iommufd.c | 2 +- hw/virtio/virtio-iommu.c | 2 +- include/sysemu/host_iommu_device.h | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 0804c1b8de..ddd835996c 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1165,7 +1165,7 @@ static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap, } static GList * -hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) +hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod) { VFIODevice *vdev = hiod->agent; diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 890d8d6a38..211e7223f1 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -644,7 +644,7 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, } static GList * -hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp) +hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod) { VFIODevice *vdev = hiod->agent; diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 8fe69ab094..f278417f2b 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -635,7 +635,7 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, if (hiodc->get_iova_ranges) { int ret; - host_iova_ranges = hiodc->get_iova_ranges(hiod, errp); + host_iova_ranges = hiodc->get_iova_ranges(hiod); if (!host_iova_ranges) { return true; /* some old kernels may not support that capability */ } diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index ee6c813c8b..05c7324a0d 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -87,9 +87,8 @@ struct HostIOMMUDeviceClass { * @hiod Host IOMMU device * * @hiod: handle to the host IOMMU device - * @errp: error handle */ - GList* (*get_iova_ranges)(HostIOMMUDevice *hiod, Error **errp); + GList* (*get_iova_ranges)(HostIOMMUDevice *hiod); }; /* From 8fe0ebe15d7775be72b1aaceb7405b68d3ec6368 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:56 +0200 Subject: [PATCH 2053/2884] HostIOMMUDevice: Introduce get_page_size_mask() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This callback will be used to retrieve the page size mask supported along a given Host IOMMU device. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/vfio/container.c | 10 ++++++++++ hw/vfio/iommufd.c | 11 +++++++++++ include/hw/vfio/vfio-container-base.h | 7 +++++++ include/sysemu/host_iommu_device.h | 8 ++++++++ 4 files changed, 36 insertions(+) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index ddd835996c..425db1a14c 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1173,6 +1173,15 @@ hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod) return vfio_container_get_iova_ranges(vdev->bcontainer); } +static uint64_t +hiod_legacy_vfio_get_page_size_mask(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_page_size_mask(vdev->bcontainer); +} + static void vfio_iommu_legacy_instance_init(Object *obj) { VFIOContainer *container = VFIO_IOMMU_LEGACY(obj); @@ -1187,6 +1196,7 @@ static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) hioc->realize = hiod_legacy_vfio_realize; hioc->get_cap = hiod_legacy_vfio_get_cap; hioc->get_iova_ranges = hiod_legacy_vfio_get_iova_ranges; + hioc->get_page_size_mask = hiod_legacy_vfio_get_page_size_mask; }; static const TypeInfo types[] = { diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 211e7223f1..7b5f87a148 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -652,12 +652,23 @@ hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod) return vfio_container_get_iova_ranges(vdev->bcontainer); } +static uint64_t +hiod_iommufd_vfio_get_page_size_mask(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_page_size_mask(vdev->bcontainer); +} + + static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) { HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); hiodc->realize = hiod_iommufd_vfio_realize; hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges; + hiodc->get_page_size_mask = hiod_iommufd_vfio_get_page_size_mask; }; static const TypeInfo types[] = { diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 45d7c40fce..62a8b60d87 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -88,6 +88,13 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); +static inline uint64_t +vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) +{ + assert(bcontainer); + return bcontainer->pgsizes; +} + #define TYPE_VFIO_IOMMU "vfio-iommu" #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index 05c7324a0d..c1bf74ae2c 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -89,6 +89,14 @@ struct HostIOMMUDeviceClass { * @hiod: handle to the host IOMMU device */ GList* (*get_iova_ranges)(HostIOMMUDevice *hiod); + /** + * + * @get_page_size_mask: Return the page size mask supported along this + * @hiod Host IOMMU device + * + * @hiod: handle to the host IOMMU device + */ + uint64_t (*get_page_size_mask)(HostIOMMUDevice *hiod); }; /* From d7c8c95fbca2e07d11b98fb1b191d7194d084c7e Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:57 +0200 Subject: [PATCH 2054/2884] virtio-iommu : Retrieve page size mask on virtio_iommu_set_iommu_device() Retrieve the Host IOMMU Device page size mask when this latter is set. This allows to get the information much sooner than when relying on IOMMU MR set_page_size_mask() call, whcih happens when the IOMMU MR gets enabled. We introduce check_page_size_mask() helper whose code is inherited from current virtio_iommu_set_page_size_mask() implementation. This callback will be removed in a subsequent patch. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/trace-events | 1 + hw/virtio/virtio-iommu.c | 57 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 3cf84e04a7..599d855ff6 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -132,6 +132,7 @@ virtio_iommu_notify_map(const char *name, uint64_t virt_start, uint64_t virt_end virtio_iommu_notify_unmap(const char *name, uint64_t virt_start, uint64_t virt_end) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64 virtio_iommu_set_page_size_mask(const char *name, uint64_t old, uint64_t new) "mr=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 +virtio_iommu_update_page_size_mask(const char *name, uint64_t old, uint64_t new) "host iommu device=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" virtio_iommu_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index f278417f2b..d6654985bd 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -618,9 +618,39 @@ out: return ret; } +static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask, + Error **errp) +{ + uint64_t cur_mask = viommu->config.page_size_mask; + + if ((cur_mask & new_mask) == 0) { + error_setg(errp, "virtio-iommu reports a page size mask 0x%"PRIx64 + " incompatible with currently supported mask 0x%"PRIx64, + new_mask, cur_mask); + return false; + } + /* + * Once the granule is frozen we can't change the mask anymore. If by + * chance the hotplugged device supports the same granule, we can still + * accept it. + */ + if (viommu->granule_frozen) { + int cur_granule = ctz64(cur_mask); + + if (!(BIT_ULL(cur_granule) & new_mask)) { + error_setg(errp, + "virtio-iommu does not support frozen granule 0x%llx", + BIT_ULL(cur_granule)); + return false; + } + } + return true; +} + static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, HostIOMMUDevice *hiod, Error **errp) { + ERRP_GUARD(); VirtIOIOMMU *viommu = opaque; HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod); struct hiod_key *new_key; @@ -643,8 +673,28 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, hiod->aliased_devfn, host_iova_ranges, errp); if (ret) { - g_list_free_full(host_iova_ranges, g_free); - return false; + goto error; + } + } + if (hiodc->get_page_size_mask) { + uint64_t new_mask = hiodc->get_page_size_mask(hiod); + + if (check_page_size_mask(viommu, new_mask, errp)) { + /* + * The default mask depends on the "granule" property. For example, + * with 4k granule, it is -(4 * KiB). When an assigned device has + * page size restrictions due to the hardware IOMMU configuration, + * apply this restriction to the mask. + */ + trace_virtio_iommu_update_page_size_mask(hiod->name, + viommu->config.page_size_mask, + new_mask); + if (!viommu->granule_frozen) { + viommu->config.page_size_mask &= new_mask; + } + } else { + error_prepend(errp, "%s: ", hiod->name); + goto error; } } @@ -657,6 +707,9 @@ static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, g_list_free_full(host_iova_ranges, g_free); return true; +error: + g_list_free_full(host_iova_ranges, g_free); + return false; } static void From 2457343d05a168963b52ab6d5c2932dd29b93f46 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:58 +0200 Subject: [PATCH 2055/2884] memory: remove IOMMU MR iommu_set_page_size_mask() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everything is now in place to use the Host IOMMU Device callbacks to retrieve the page size mask usable with a given assigned device. This new method brings the advantage to pass the info much earlier to the virtual IOMMU and before the IOMMU MR gets enabled. So let's remove the call to memory_region_iommu_set_page_size_mask in vfio common.c and remove the single implementation of the IOMMU MR callback in the virtio-iommu.c Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/vfio/common.c | 8 ------- hw/virtio/trace-events | 1 - hw/virtio/virtio-iommu.c | 45 ---------------------------------------- include/exec/memory.h | 38 --------------------------------- system/memory.c | 13 ------------ 5 files changed, 105 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 7cdb969fd3..6d15b36e0b 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -622,14 +622,6 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend), iommu_idx); - ret = memory_region_iommu_set_page_size_mask(giommu->iommu_mr, - bcontainer->pgsizes, - &err); - if (ret) { - g_free(giommu); - goto fail; - } - ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, &err); if (ret) { diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 599d855ff6..b7c04f0856 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -131,7 +131,6 @@ virtio_iommu_fill_resv_property(uint32_t devid, uint8_t subtype, uint64_t start, virtio_iommu_notify_map(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64" flags=%d" virtio_iommu_notify_unmap(const char *name, uint64_t virt_start, uint64_t virt_end) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64 -virtio_iommu_set_page_size_mask(const char *name, uint64_t old, uint64_t new) "mr=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 virtio_iommu_update_page_size_mask(const char *name, uint64_t old, uint64_t new) "host iommu device=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index d6654985bd..76f34ea6b3 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1408,50 +1408,6 @@ static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, return 0; } -/* - * The default mask depends on the "granule" property. For example, with - * 4k granule, it is -(4 * KiB). When an assigned device has page size - * restrictions due to the hardware IOMMU configuration, apply this restriction - * to the mask. - */ -static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, - uint64_t new_mask, - Error **errp) -{ - IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); - VirtIOIOMMU *s = sdev->viommu; - uint64_t cur_mask = s->config.page_size_mask; - - trace_virtio_iommu_set_page_size_mask(mr->parent_obj.name, cur_mask, - new_mask); - - if ((cur_mask & new_mask) == 0) { - error_setg(errp, "virtio-iommu %s reports a page size mask 0x%"PRIx64 - " incompatible with currently supported mask 0x%"PRIx64, - mr->parent_obj.name, new_mask, cur_mask); - return -1; - } - - /* - * Once the granule is frozen we can't change the mask anymore. If by - * chance the hotplugged device supports the same granule, we can still - * accept it. - */ - if (s->granule_frozen) { - int cur_granule = ctz64(cur_mask); - - if (!(BIT_ULL(cur_granule) & new_mask)) { - error_setg(errp, "virtio-iommu %s does not support frozen granule 0x%llx", - mr->parent_obj.name, BIT_ULL(cur_granule)); - return -1; - } - return 0; - } - - s->config.page_size_mask &= new_mask; - return 0; -} - static void virtio_iommu_system_reset(void *opaque) { VirtIOIOMMU *s = opaque; @@ -1776,7 +1732,6 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = virtio_iommu_translate; imrc->replay = virtio_iommu_replay; imrc->notify_flag_changed = virtio_iommu_notify_flag_changed; - imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask; } static const TypeInfo virtio_iommu_info = { diff --git a/include/exec/memory.h b/include/exec/memory.h index c26ede33d2..02f7528ec0 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -504,32 +504,6 @@ struct IOMMUMemoryRegionClass { * @iommu: the IOMMUMemoryRegion */ int (*num_indexes)(IOMMUMemoryRegion *iommu); - - /** - * @iommu_set_page_size_mask: - * - * Restrict the page size mask that can be supported with a given IOMMU - * memory region. Used for example to propagate host physical IOMMU page - * size mask limitations to the virtual IOMMU. - * - * Optional method: if this method is not provided, then the default global - * page mask is used. - * - * @iommu: the IOMMUMemoryRegion - * - * @page_size_mask: a bitmask of supported page sizes. At least one bit, - * representing the smallest page size, must be set. Additional set bits - * represent supported block sizes. For example a host physical IOMMU that - * uses page tables with a page size of 4kB, and supports 2MB and 4GB - * blocks, will set mask 0x40201000. A granule of 4kB with indiscriminate - * block sizes is specified with mask 0xfffffffffffff000. - * - * Returns 0 on success, or a negative error. In case of failure, the error - * object must be created. - */ - int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, - uint64_t page_size_mask, - Error **errp); }; typedef struct RamDiscardListener RamDiscardListener; @@ -1919,18 +1893,6 @@ int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, */ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); -/** - * memory_region_iommu_set_page_size_mask: set the supported page - * sizes for a given IOMMU memory region - * - * @iommu_mr: IOMMU memory region - * @page_size_mask: supported page size mask - * @errp: pointer to Error*, to store an error if it happens. - */ -int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, - uint64_t page_size_mask, - Error **errp); - /** * memory_region_name: get a memory region's name * diff --git a/system/memory.c b/system/memory.c index 2d69521360..5e6eb459d5 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1901,19 +1901,6 @@ static int memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr, return ret; } -int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, - uint64_t page_size_mask, - Error **errp) -{ - IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); - int ret = 0; - - if (imrc->iommu_set_page_size_mask) { - ret = imrc->iommu_set_page_size_mask(iommu_mr, page_size_mask, errp); - } - return ret; -} - int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) { From 956b30b9cf856470612e8d2c2c4985fb7e40a970 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Mon, 1 Jul 2024 10:48:59 +0200 Subject: [PATCH 2056/2884] virtio-iommu: Revert transient enablement of IOMMU MR in bypass mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 94df5b2180d6 ("virtio-iommu: Fix 64kB host page size VFIO device assignment"), in case of bypass mode, we transiently enabled the IOMMU MR to allow the set_page_size_mask() to be called and pass information about the page size mask constraint of cold plugged VFIO devices. Now we do not use the IOMMU MR callback anymore, we can just get rid of this hack. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-iommu.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 76f34ea6b3..33ae61c4a6 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1430,18 +1430,6 @@ static void virtio_iommu_freeze_granule(Notifier *notifier, void *data) VirtIOIOMMU *s = container_of(notifier, VirtIOIOMMU, machine_done); int granule; - if (likely(s->config.bypass)) { - /* - * Transient IOMMU MR enable to collect page_size_mask requirements - * through memory_region_iommu_set_page_size_mask() called by - * VFIO region_add() callback - */ - s->config.bypass = false; - virtio_iommu_switch_address_space_all(s); - /* restore default */ - s->config.bypass = true; - virtio_iommu_switch_address_space_all(s); - } s->granule_frozen = true; granule = ctz64(s->config.page_size_mask); trace_virtio_iommu_freeze_granule(BIT_ULL(granule)); From f15da599a1c5326e105425a74d2fb004a2a2649e Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan <zhenzhong.duan@intel.com> Date: Mon, 1 Jul 2024 09:48:08 +0800 Subject: [PATCH 2057/2884] vfio/display: Fix potential memleak of edid info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EDID related device region info is leaked in vfio_display_edid_init() error path and VFIODisplay destroying path. Fixes: 08479114b0de ("vfio/display: add edid support.") Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- hw/vfio/display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 661e921616..9c57fd3888 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -171,7 +171,9 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) err: trace_vfio_display_edid_write_error(); + g_free(dpy->edid_info); g_free(dpy->edid_regs); + dpy->edid_info = NULL; dpy->edid_regs = NULL; return; } @@ -182,6 +184,7 @@ static void vfio_display_edid_exit(VFIODisplay *dpy) return; } + g_free(dpy->edid_info); g_free(dpy->edid_regs); g_free(dpy->edid_blob); timer_free(dpy->edid_link_timer); From 83d90192026eaded6319a6d27466ad7d606a27e0 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan <zhenzhong.duan@intel.com> Date: Mon, 1 Jul 2024 09:48:09 +0800 Subject: [PATCH 2058/2884] vfio/display: Fix vfio_display_edid_init() error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_display_edid_init() can fail for many reasons and return silently. It would be good to report the error. Old mdev driver may not support vfio edid region and we allow to go through in this case. vfio_display_edid_update() isn't changed because it can be called at runtime when UI changes (i.e. window resize). Fixes: 08479114b0de ("vfio/display: add edid support.") Suggested-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- hw/vfio/display.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 9c57fd3888..ea87830fe0 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -124,7 +124,7 @@ static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, } } -static void vfio_display_edid_init(VFIOPCIDevice *vdev) +static bool vfio_display_edid_init(VFIOPCIDevice *vdev, Error **errp) { VFIODisplay *dpy = vdev->dpy; int fd = vdev->vbasedev.fd; @@ -135,7 +135,8 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) VFIO_REGION_SUBTYPE_GFX_EDID, &dpy->edid_info); if (ret) { - return; + /* Failed to get GFX edid info, allow to go through without edid. */ + return true; } trace_vfio_display_edid_available(); @@ -167,15 +168,16 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) vfio_display_edid_link_up, vdev); vfio_display_edid_update(vdev, true, 0, 0); - return; + return true; err: + error_setg(errp, "vfio: failed to read GFX edid field"); trace_vfio_display_edid_write_error(); g_free(dpy->edid_info); g_free(dpy->edid_regs); dpy->edid_info = NULL; dpy->edid_regs = NULL; - return; + return false; } static void vfio_display_edid_exit(VFIODisplay *dpy) @@ -368,8 +370,7 @@ static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) return false; } } - vfio_display_edid_init(vdev); - return true; + return vfio_display_edid_init(vdev, errp); } static void vfio_display_dmabuf_exit(VFIODisplay *dpy) From 4d13ae45ff93fa825ceb39dfd16b305f4baccd18 Mon Sep 17 00:00:00 2001 From: David Hildenbrand <david@redhat.com> Date: Tue, 16 Apr 2024 16:14:26 +0200 Subject: [PATCH 2059/2884] virtio-mem: improve error message when unplug of device fails due to plugged memory The error message is actually expressive, considering QEMU only. But when called from Libvirt, talking about "size" can be confusing, because in Libvirt "size" translates to the memory backend size in QEMU (maximum size) and "current" translates to the QEMU "size" property. Let's simply avoid talking about the "size" property and spell out that some device memory is still plugged. Message-ID: <20240416141426.588544-1-david@redhat.com> Tested-by: Mario Casquero <mcasquer@redhat.com> Cc: Liang Cong <lcong@redhat.com> Cc: Mario Casquero <mcasquer@redhat.com> Cc: "Michael S. Tsirkin" <mst@redhat.com> Signed-off-by: David Hildenbrand <david@redhat.com> --- hw/virtio/virtio-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index ffd119ebac..ef64bf1b4a 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1832,8 +1832,8 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) } if (vmem->size) { - error_setg(errp, "virtio-mem device cannot get unplugged while" - " '" VIRTIO_MEM_SIZE_PROP "' != '0'"); + error_setg(errp, "virtio-mem device cannot get unplugged while some" + " of its memory is still plugged"); return; } if (vmem->requested_size) { From 249d0f83977c71c5a392c00947e1e7d5415ac5be Mon Sep 17 00:00:00 2001 From: Nir Soffer <nsoffer@redhat.com> Date: Fri, 28 Jun 2024 23:20:57 +0300 Subject: [PATCH 2060/2884] qemu-iotest/245: Add missing discard=unmap The test works since we punch holes by default even when opening the image without discard=on or discard=unmap. Fix the test to enable discard. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> --- tests/qemu-iotests/245 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index a934c9d1e6..f96610f510 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -592,7 +592,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): @iotests.skip_if_unsupported(['compress']) def test_insert_compress_filter(self): # Add an image to the VM: hd (raw) -> hd0 (qcow2) -> hd0-file (file) - opts = {'driver': 'raw', 'node-name': 'hd', 'file': hd_opts(0)} + opts = {'driver': 'raw', 'node-name': 'hd', 'file': hd_opts(0), 'discard': 'unmap'} self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Add a 'compress' filter From d05ae948cc887054495977855b0859d0d4ab2613 Mon Sep 17 00:00:00 2001 From: Nir Soffer <nsoffer@redhat.com> Date: Fri, 28 Jun 2024 23:20:58 +0300 Subject: [PATCH 2061/2884] Consider discard option when writing zeros When opening an image with discard=off, we punch hole in the image when writing zeroes, making the image sparse. This breaks users that want to ensure that writes cannot fail with ENOSPACE by using fully allocated images[1]. bdrv_co_pwrite_zeroes() correctly disables BDRV_REQ_MAY_UNMAP if we opened the child without discard=unmap or discard=on. But we don't go through this function when accessing the top node. Move the check down to bdrv_co_do_pwrite_zeroes() which seems to be used in all code paths. This change implements the documented behavior, punching holes only when opening the image with discard=on or discard=unmap. This may not be the best default but can improve it later. The test depends on a file system supporting discard, deallocating the entire file when punching hole with the length of the entire file. Tested with xfs, ext4, and tmpfs. [1] https://lists.nongnu.org/archive/html/qemu-discuss/2024-06/msg00003.html Signed-off-by: Nir Soffer <nsoffer@redhat.com> Message-id: 20240628202058.1964986-3-nsoffer@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> --- block/io.c | 9 +- tests/qemu-iotests/tests/write-zeroes-unmap | 127 ++++++++++++++++++ .../qemu-iotests/tests/write-zeroes-unmap.out | 81 +++++++++++ 3 files changed, 213 insertions(+), 4 deletions(-) create mode 100755 tests/qemu-iotests/tests/write-zeroes-unmap create mode 100644 tests/qemu-iotests/tests/write-zeroes-unmap.out diff --git a/block/io.c b/block/io.c index 7217cf811b..301514c880 100644 --- a/block/io.c +++ b/block/io.c @@ -1862,6 +1862,11 @@ bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, return -EINVAL; } + /* If opened with discard=off we should never unmap. */ + if (!(bs->open_flags & BDRV_O_UNMAP)) { + flags &= ~BDRV_REQ_MAY_UNMAP; + } + /* Invalidate the cached block-status data range if this write overlaps */ bdrv_bsc_invalidate_range(bs, offset, bytes); @@ -2315,10 +2320,6 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); assert_bdrv_graph_readable(); - if (!(child->bs->open_flags & BDRV_O_UNMAP)) { - flags &= ~BDRV_REQ_MAY_UNMAP; - } - return bdrv_co_pwritev(child, offset, bytes, NULL, BDRV_REQ_ZERO_WRITE | flags); } diff --git a/tests/qemu-iotests/tests/write-zeroes-unmap b/tests/qemu-iotests/tests/write-zeroes-unmap new file mode 100755 index 0000000000..7cfeeaf839 --- /dev/null +++ b/tests/qemu-iotests/tests/write-zeroes-unmap @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# group: quick +# +# Test write zeros unmap. +# +# Copyright (C) Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +trap _cleanup_test_img exit + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +create_test_image() { + _make_test_img -f $IMGFMT 1m +} + +filter_command() { + _filter_testdir | _filter_qemu_io | _filter_qemu | _filter_hmp +} + +print_disk_usage() { + du -sh $TEST_IMG | _filter_testdir +} + +echo +echo "=== defaults - write zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -z 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT \ + | filter_command +print_disk_usage + +echo +echo "=== defaults - write zeros unmap ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -zu 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT \ + | filter_command +print_disk_usage + + +echo +echo "=== defaults - write actual zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -P 0 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT \ + | filter_command +print_disk_usage + +echo +echo "=== discard=off - write zeroes unmap ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -zu 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,discard=off \ + | filter_command +print_disk_usage + +echo +echo "=== detect-zeroes=on - write actual zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -P 0 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,detect-zeroes=on \ + | filter_command +print_disk_usage + +echo +echo "=== detect-zeroes=on,discard=on - write actual zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -P 0 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,detect-zeroes=on,discard=on \ + | filter_command +print_disk_usage + +echo +echo "=== discard=on - write zeroes ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -z 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,discard=on \ + | filter_command +print_disk_usage + +echo +echo "=== discard=on - write zeroes unmap ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -zu 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,discard=on \ + | filter_command +print_disk_usage diff --git a/tests/qemu-iotests/tests/write-zeroes-unmap.out b/tests/qemu-iotests/tests/write-zeroes-unmap.out new file mode 100644 index 0000000000..c931994897 --- /dev/null +++ b/tests/qemu-iotests/tests/write-zeroes-unmap.out @@ -0,0 +1,81 @@ +QA output created by write-zeroes-unmap + +=== defaults - write zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -z 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== defaults - write zeros unmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -zu 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== defaults - write actual zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -P 0 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== discard=off - write zeroes unmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -zu 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== detect-zeroes=on - write actual zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -P 0 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== detect-zeroes=on,discard=on - write actual zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -P 0 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== discard=on - write zeroes === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -z 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +1.0M TEST_DIR/t.raw + +=== discard=on - write zeroes unmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -zu 0 1m" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit +0 TEST_DIR/t.raw From ea8618382aba2a7a8a993e61237f2af933fba9ad Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:39 +0100 Subject: [PATCH 2062/2884] target/arm: Correct comments about M-profile FPSCR The M-profile FPSCR LTPSIZE is bits [18:16]; this is the same field as A-profile FPSCR Len, not Stride. Correct the comment in vfp_get_fpscr(). We also implemented M-profile FPSCR.QC, but forgot to delete a TODO comment from vfp_set_fpscr(); remove it now. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-2-peter.maydell@linaro.org --- target/arm/vfp_helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 50d7042fa9..e168600555 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -176,8 +176,8 @@ uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) | (env->vfp.vec_stride << 20); /* - * M-profile LTPSIZE overlaps A-profile Stride; whichever of the - * two is not applicable to this CPU will always be zero. + * M-profile LTPSIZE is the same bits [18:16] as A-profile Len; whichever + * of the two is not applicable to this CPU will always be zero. */ fpscr |= env->v7m.ltpsize << 16; @@ -226,7 +226,6 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) /* * The bit we set within fpscr_q is arbitrary; the register as a * whole being zero/non-zero is what counts. - * TODO: M-profile MVE also has a QC bit. */ env->vfp.qc[0] = val & FPCR_QC; env->vfp.qc[1] = 0; From 2de7cf9e0568f18203004a839c9bea0cb9ce96d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:40 +0100 Subject: [PATCH 2063/2884] target/arm: Make vfp_get_fpscr() call vfp_get_{fpcr, fpsr} In AArch32, the floating point control and status bits are all in a single register, FPSCR. In AArch64, these were split into separate FPCR and FPSR registers, but the bit layouts remained the same, with no overlaps, so that you could construct an FPSCR value by ORing FPCR and FPSR, or equivalently could produce FPSR and FPCR by masking an FPSCR value. For QEMU's implementation, we opted to use masking to produce FPSR and FPCR, because we started with an AArch32 implementation of FPSCR. The addition of the (AArch64-only) FEAT_AFP adds new bits to the FPCR which overlap with some bits in the FPSR. This means we'll no longer be able to consider the FPSCR-encoded value as the primary one, but instead need to treat FPSR/FPCR as the primary encoding and construct the FPSCR from those. (This remains possible because the FEAT_AFP bits in FPCR don't appear in the FPSCR.) As the first step in this refactoring, make vfp_get_fpscr() call vfp_get_fpcr() and vfp_get_fpsr(), instead of the other way around. Note that vfp_get_fpcsr_from_host() returns only bits in the FPSR (for the cumulative fp exception bits), so we can simply rename it without needing to add a new function for getting FPCR bits. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-3-peter.maydell@linaro.org --- target/arm/cpu.h | 24 +++++++++++++++--------- target/arm/vfp_helper.c | 34 ++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index d8eb986a04..abeb2f8976 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1714,10 +1714,21 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); #define FPCR_NZCV_MASK (FPCR_N | FPCR_Z | FPCR_C | FPCR_V) #define FPCR_NZCVQC_MASK (FPCR_NZCV_MASK | FPCR_QC) -static inline uint32_t vfp_get_fpsr(CPUARMState *env) -{ - return vfp_get_fpscr(env) & FPSR_MASK; -} +/** + * vfp_get_fpsr: read the AArch64 FPSR + * @env: CPU context + * + * Return the current AArch64 FPSR value + */ +uint32_t vfp_get_fpsr(CPUARMState *env); + +/** + * vfp_get_fpcr: read the AArch64 FPCR + * @env: CPU context + * + * Return the current AArch64 FPCR value + */ +uint32_t vfp_get_fpcr(CPUARMState *env); static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val) { @@ -1725,11 +1736,6 @@ static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val) vfp_set_fpscr(env, new_fpscr); } -static inline uint32_t vfp_get_fpcr(CPUARMState *env) -{ - return vfp_get_fpscr(env) & FPCR_MASK; -} - static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val) { uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK); diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index e168600555..f0692f0c81 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -85,7 +85,7 @@ static inline int vfp_exceptbits_to_host(int target_bits) return host_bits; } -static uint32_t vfp_get_fpscr_from_host(CPUARMState *env) +static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { uint32_t i; @@ -156,7 +156,7 @@ static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) #else -static uint32_t vfp_get_fpscr_from_host(CPUARMState *env) +static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { return 0; } @@ -167,26 +167,36 @@ static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) #endif -uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +uint32_t vfp_get_fpcr(CPUARMState *env) { - uint32_t i, fpscr; - - fpscr = env->vfp.xregs[ARM_VFP_FPSCR] - | (env->vfp.vec_len << 16) - | (env->vfp.vec_stride << 20); + uint32_t fpcr = (env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_MASK) + | (env->vfp.vec_len << 16) + | (env->vfp.vec_stride << 20); /* * M-profile LTPSIZE is the same bits [18:16] as A-profile Len; whichever * of the two is not applicable to this CPU will always be zero. */ - fpscr |= env->v7m.ltpsize << 16; + fpcr |= env->v7m.ltpsize << 16; - fpscr |= vfp_get_fpscr_from_host(env); + return fpcr; +} + +uint32_t vfp_get_fpsr(CPUARMState *env) +{ + uint32_t fpsr = env->vfp.xregs[ARM_VFP_FPSCR] & FPSR_MASK; + uint32_t i; + + fpsr |= vfp_get_fpsr_from_host(env); i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3]; - fpscr |= i ? FPCR_QC : 0; + fpsr |= i ? FPCR_QC : 0; + return fpsr; +} - return fpscr; +uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +{ + return (vfp_get_fpcr(env) & FPCR_MASK) | (vfp_get_fpsr(env) & FPSR_MASK); } uint32_t vfp_get_fpscr(CPUARMState *env) From b167325e938706433f00400a0586cedbe965d76d Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:41 +0100 Subject: [PATCH 2064/2884] target/arm: Make vfp_set_fpscr() call vfp_set_{fpcr, fpsr} Make vfp_set_fpscr() call vfp_set_fpsr() and vfp_set_fpcr() instead of the other way around. The masking we do when getting and setting vfp.xregs[ARM_VFP_FPSCR] is a little awkward, but we are going to change where we store the underlying FPSR and FPCR information in a later commit, so it will go away then. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-4-peter.maydell@linaro.org --- target/arm/cpu.h | 22 +++++---- target/arm/vfp_helper.c | 100 ++++++++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 44 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index abeb2f8976..b1b48c1d77 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1730,17 +1730,19 @@ uint32_t vfp_get_fpsr(CPUARMState *env); */ uint32_t vfp_get_fpcr(CPUARMState *env); -static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val) -{ - uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPSR_MASK) | (val & FPSR_MASK); - vfp_set_fpscr(env, new_fpscr); -} +/** + * vfp_set_fpsr: write the AArch64 FPSR + * @env: CPU context + * @value: new value + */ +void vfp_set_fpsr(CPUARMState *env, uint32_t value); -static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val) -{ - uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK); - vfp_set_fpscr(env, new_fpscr); -} +/** + * vfp_set_fpcr: write the AArch64 FPCR + * @env: CPU context + * @value: new value + */ +void vfp_set_fpcr(CPUARMState *env, uint32_t value); enum arm_cpu_mode { ARM_CPU_MODE_USR = 0x10, diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index f0692f0c81..678de5eb6f 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -99,14 +99,27 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) return vfp_exceptbits_from_host(i); } -static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) +static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) +{ + /* + * The exception flags are ORed together when we read fpscr so we + * only need to preserve the current state in one of our + * float_status values. + */ + int i = vfp_exceptbits_to_host(val); + set_float_exception_flags(i, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.fp_status_f16); + set_float_exception_flags(0, &env->vfp.standard_fp_status); + set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); +} + +static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val) { - int i; uint32_t changed = env->vfp.xregs[ARM_VFP_FPSCR]; changed ^= val; if (changed & (3 << 22)) { - i = (val >> 22) & 3; + int i = (val >> 22) & 3; switch (i) { case FPROUNDING_TIEEVEN: i = float_round_nearest_even; @@ -141,17 +154,6 @@ static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); } - - /* - * The exception flags are ORed together when we read fpscr so we - * only need to preserve the current state in one of our - * float_status values. - */ - i = vfp_exceptbits_to_host(val); - set_float_exception_flags(i, &env->vfp.fp_status); - set_float_exception_flags(0, &env->vfp.fp_status_f16); - set_float_exception_flags(0, &env->vfp.standard_fp_status); - set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); } #else @@ -161,7 +163,11 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) return 0; } -static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) +static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) +{ +} + +static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val) { } @@ -204,7 +210,37 @@ uint32_t vfp_get_fpscr(CPUARMState *env) return HELPER(vfp_get_fpscr)(env); } -void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +void vfp_set_fpsr(CPUARMState *env, uint32_t val) +{ + ARMCPU *cpu = env_archcpu(env); + + vfp_set_fpsr_to_host(env, val); + + if (arm_feature(env, ARM_FEATURE_NEON) || + cpu_isar_feature(aa32_mve, cpu)) { + /* + * The bit we set within vfp.qc[] is arbitrary; the array as a + * whole being zero/non-zero is what counts. + */ + env->vfp.qc[0] = val & FPCR_QC; + env->vfp.qc[1] = 0; + env->vfp.qc[2] = 0; + env->vfp.qc[3] = 0; + } + + /* + * The only FPSR bits we keep in vfp.xregs[FPSCR] are NZCV: + * the exception flags IOC|DZC|OFC|UFC|IXC|IDC are stored in + * fp_status, and QC is in vfp.qc[]. Store the NZCV bits there, + * and zero any of the other FPSR bits (but preserve the FPCR + * bits). + */ + val &= FPCR_NZCV_MASK; + env->vfp.xregs[ARM_VFP_FPSCR] &= ~FPSR_MASK; + env->vfp.xregs[ARM_VFP_FPSCR] |= val; +} + +void vfp_set_fpcr(CPUARMState *env, uint32_t val) { ARMCPU *cpu = env_archcpu(env); @@ -213,7 +249,7 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) val &= ~FPCR_FZ16; } - vfp_set_fpscr_to_host(env, val); + vfp_set_fpcr_to_host(env, val); if (!arm_feature(env, ARM_FEATURE_M)) { /* @@ -231,28 +267,24 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) FPCR_LTPSIZE_LENGTH); } - if (arm_feature(env, ARM_FEATURE_NEON) || - cpu_isar_feature(aa32_mve, cpu)) { - /* - * The bit we set within fpscr_q is arbitrary; the register as a - * whole being zero/non-zero is what counts. - */ - env->vfp.qc[0] = val & FPCR_QC; - env->vfp.qc[1] = 0; - env->vfp.qc[2] = 0; - env->vfp.qc[3] = 0; - } - /* * We don't implement trapped exception handling, so the * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) * - * The exception flags IOC|DZC|OFC|UFC|IXC|IDC are stored in - * fp_status; QC, Len and Stride are stored separately earlier. - * Clear out all of those and the RES0 bits: only NZCV, AHP, DN, - * FZ, RMode and FZ16 are kept in vfp.xregs[FPSCR]. + * The FPCR bits we keep in vfp.xregs[FPSCR] are AHP, DN, FZ, RMode + * and FZ16. Len, Stride and LTPSIZE we just handled. Store those bits + * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI + * bits. */ - env->vfp.xregs[ARM_VFP_FPSCR] = val & 0xf7c80000; + val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16; + env->vfp.xregs[ARM_VFP_FPSCR] &= ~FPCR_MASK; + env->vfp.xregs[ARM_VFP_FPSCR] |= val; +} + +void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +{ + vfp_set_fpcr(env, val & FPCR_MASK); + vfp_set_fpsr(env, val & FPSR_MASK); } void vfp_set_fpscr(CPUARMState *env, uint32_t val) From abf1046a155bc73ceb2c3be3fdfb9c8ffebf9dd1 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:42 +0100 Subject: [PATCH 2065/2884] target/arm: Support migration when FPSR/FPCR won't fit in the FPSCR To support FPSR and FPCR bits that don't exist in the AArch32 FPSCR view of floating point control and status (such as the FEAT_AFP ones), we need to make sure those bits can be migrated. This commit allows that, whilst maintaining backwards and forwards migration compatibility for CPUs where there are no such bits: On sending: * If either the FPCR or the FPSR include set bits that are not visible in the AArch32 FPSCR view of floating point control/status then we send the FPCR and FPSR as two separate fields in a new cpu/vfp/fpcr_fpsr subsection, and we send a 0 for the old FPSCR field in cpu/vfp * Otherwise, we don't send the fpcr_fpsr subsection, and we send an FPSCR-format value in cpu/vfp as we did previously On receiving: * if we see a non-zero FPSCR field, that is the right information * if we see a fpcr_fpsr subsection then that has the information * if we see neither, then FPSCR/FPCR/FPSR are all zero on the source; cpu_pre_load() ensures the CPU state defaults to that * if we see both, then the migration source is buggy or malicious; either the fpcr_fpsr or the FPSCR will "win" depending which is first in the migration stream; we don't care which that is We make the new FPCR and FPSR on-the-wire data be 64 bits, because architecturally these registers are that wide, and this avoids the need to engage in further migration-compatibility contortions in future if some new architecture revision defines bits in the high half of either register. (We won't ever send the new migration subsection until we add support for a CPU feature which enables setting overlapping FPCR bits, like FEAT_AFP.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-5-peter.maydell@linaro.org --- target/arm/machine.c | 134 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 2 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index 0a722ca7e7..8c820955d9 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -18,6 +18,34 @@ static bool vfp_needed(void *opaque) : cpu_isar_feature(aa32_vfp_simd, cpu)); } +static bool vfp_fpcr_fpsr_needed(void *opaque) +{ + /* + * If either the FPCR or the FPSR include set bits that are not + * visible in the AArch32 FPSCR view of floating point control/status + * then we must send the FPCR and FPSR as two separate fields in the + * cpu/vfp/fpcr_fpsr subsection, and we will send a 0 for the old + * FPSCR field in cpu/vfp. + * + * If all the set bits are representable in an AArch32 FPSCR then we + * send that value as the cpu/vfp FPSCR field, and don't send the + * cpu/vfp/fpcr_fpsr subsection. + * + * On incoming migration, if the cpu/vfp FPSCR field is non-zero we + * use it, and if the fpcr_fpsr subsection is present we use that. + * (The subsection will never be present with a non-zero FPSCR field, + * and if FPSCR is zero and the subsection is not present that means + * that FPSCR/FPSR/FPCR are zero.) + * + * This preserves migration compatibility with older QEMU versions, + * in both directions. + */ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + return (vfp_get_fpcr(env) & ~FPCR_MASK) || (vfp_get_fpsr(env) & ~FPSR_MASK); +} + static int get_fpscr(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { @@ -25,7 +53,10 @@ static int get_fpscr(QEMUFile *f, void *opaque, size_t size, CPUARMState *env = &cpu->env; uint32_t val = qemu_get_be32(f); - vfp_set_fpscr(env, val); + if (val) { + /* 0 means we might have the data in the fpcr_fpsr subsection */ + vfp_set_fpscr(env, val); + } return 0; } @@ -34,8 +65,9 @@ static int put_fpscr(QEMUFile *f, void *opaque, size_t size, { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; + uint32_t fpscr = vfp_fpcr_fpsr_needed(opaque) ? 0 : vfp_get_fpscr(env); - qemu_put_be32(f, vfp_get_fpscr(env)); + qemu_put_be32(f, fpscr); return 0; } @@ -45,6 +77,86 @@ static const VMStateInfo vmstate_fpscr = { .put = put_fpscr, }; +static int get_fpcr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val = qemu_get_be64(f); + + vfp_set_fpcr(env, val); + return 0; +} + +static int put_fpcr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + qemu_put_be64(f, vfp_get_fpcr(env)); + return 0; +} + +static const VMStateInfo vmstate_fpcr = { + .name = "fpcr", + .get = get_fpcr, + .put = put_fpcr, +}; + +static int get_fpsr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val = qemu_get_be64(f); + + vfp_set_fpsr(env, val); + return 0; +} + +static int put_fpsr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + qemu_put_be64(f, vfp_get_fpsr(env)); + return 0; +} + +static const VMStateInfo vmstate_fpsr = { + .name = "fpsr", + .get = get_fpsr, + .put = put_fpsr, +}; + +static const VMStateDescription vmstate_vfp_fpcr_fpsr = { + .name = "cpu/vfp/fpcr_fpsr", + .version_id = 1, + .minimum_version_id = 1, + .needed = vfp_fpcr_fpsr_needed, + .fields = (const VMStateField[]) { + { + .name = "fpcr", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &vmstate_fpcr, + .flags = VMS_SINGLE, + .offset = 0, + }, + { + .name = "fpsr", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &vmstate_fpsr, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_vfp = { .name = "cpu/vfp", .version_id = 3, @@ -100,6 +212,10 @@ static const VMStateDescription vmstate_vfp = { .offset = 0, }, VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_vfp_fpcr_fpsr, + NULL } }; @@ -784,6 +900,20 @@ static int cpu_pre_load(void *opaque) ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; + /* + * In an inbound migration where on the source FPSCR/FPSR/FPCR are 0, + * there will be no fpcr_fpsr subsection so we won't call vfp_set_fpcr() + * and vfp_set_fpsr() from get_fpcr() and get_fpsr(); also the get_fpscr() + * function will not call vfp_set_fpscr() because it will see a 0 in the + * inbound data. Ensure that in this case we have a correctly set up + * zero FPSCR/FPCR/FPSR. + * + * This is not strictly needed because FPSCR is zero out of reset, but + * it avoids the possibility of future confusing migration bugs if some + * future architecture change makes the reset value non-zero. + */ + vfp_set_fpscr(env, 0); + /* * Pre-initialize irq_line_state to a value that's never valid as * real data, so cpu_post_load() can tell whether we've seen the From 81ae37dbb4a5c5b8eb54bc7f5e6c69097eacb9d2 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:43 +0100 Subject: [PATCH 2066/2884] target/arm: Implement store_cpu_field_low32() macro We already have a load_cpu_field_low32() to load the low half of a 64-bit CPU struct field to a TCGv_i32; however we haven't yet needed the store equivalent. We'll want that in the next patch, so implement it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-6-peter.maydell@linaro.org --- target/arm/tcg/translate-a32.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/arm/tcg/translate-a32.h b/target/arm/tcg/translate-a32.h index 19de6e0a1a..0b1fa57965 100644 --- a/target/arm/tcg/translate-a32.h +++ b/target/arm/tcg/translate-a32.h @@ -83,6 +83,13 @@ void store_cpu_offset(TCGv_i32 var, int offset, int size); sizeof_field(CPUARMState, name)); \ }) +/* Store to the low half of a 64-bit field from a TCGv_i32 */ +#define store_cpu_field_low32(val, name) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof_field(CPUARMState, name) != 8); \ + store_cpu_offset(val, offsetoflow32(CPUARMState, name), 4); \ + }) + #define store_cpu_field_constant(val, name) \ store_cpu_field(tcg_constant_i32(val), name) From ce07ea61ed3b2c92f3a679016d8fa1c285c207a2 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:44 +0100 Subject: [PATCH 2067/2884] target/arm: Store FPSR and FPCR in separate CPU state fields Now that we have refactored the set/get functions so that the FPSCR format is no longer the authoritative one, we can keep FPSR and FPCR in separate CPU state fields. As well as the get and set functions, we also have a scattering of places in the code which directly access vfp.xregs[ARM_VFP_FPSCR] to extract single fields which are stored there. These all change to directly access either vfp.fpsr or vfp.fpcr, depending on the location of the field. (Most commonly, this is the NZCV flags.) We make the field in the CPU state struct 64 bits, because architecturally FPSR and FPCR are 64 bits. However we leave the types of the arguments and return values of the get/set functions as 32 bits, since we don't need to make that change with the current architecture and various callsites would be unable to handle set bits in the high half (for instance the gdbstub protocol assumes they're only 32 bit registers). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-7-peter.maydell@linaro.org --- target/arm/cpu.h | 7 +++++++ target/arm/tcg/mve_helper.c | 12 ++++++------ target/arm/tcg/translate-m-nocp.c | 6 +++--- target/arm/tcg/translate-vfp.c | 2 +- target/arm/tcg/translate.h | 3 +-- target/arm/vfp_helper.c | 25 ++++++++++--------------- 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b1b48c1d77..5323223919 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -619,6 +619,13 @@ typedef struct CPUArchState { int vec_len; int vec_stride; + /* + * Floating point status and control registers. Some bits are + * stored separately in other fields or in the float_status below. + */ + uint64_t fpsr; + uint64_t fpcr; + uint32_t xregs[16]; /* Scratch space for aa32 neon expansion. */ diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 8b99736aad..234f395b09 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -1115,21 +1115,21 @@ static void do_vadc(CPUARMState *env, uint32_t *d, uint32_t *n, uint32_t *m, if (update_flags) { /* Store C, clear NZV. */ - env->vfp.xregs[ARM_VFP_FPSCR] &= ~FPCR_NZCV_MASK; - env->vfp.xregs[ARM_VFP_FPSCR] |= carry_in * FPCR_C; + env->vfp.fpsr &= ~FPCR_NZCV_MASK; + env->vfp.fpsr |= carry_in * FPCR_C; } mve_advance_vpt(env); } void HELPER(mve_vadc)(CPUARMState *env, void *vd, void *vn, void *vm) { - bool carry_in = env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_C; + bool carry_in = env->vfp.fpsr & FPCR_C; do_vadc(env, vd, vn, vm, 0, carry_in, false); } void HELPER(mve_vsbc)(CPUARMState *env, void *vd, void *vn, void *vm) { - bool carry_in = env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_C; + bool carry_in = env->vfp.fpsr & FPCR_C; do_vadc(env, vd, vn, vm, -1, carry_in, false); } @@ -3343,7 +3343,7 @@ static void do_vcvt_sh(CPUARMState *env, void *vd, void *vm, int top) uint32_t *m = vm; uint16_t r; uint16_t mask = mve_element_mask(env); - bool ieee = !(env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_AHP); + bool ieee = !(env->vfp.fpcr & FPCR_AHP); unsigned e; float_status *fpst; float_status scratch_fpst; @@ -3373,7 +3373,7 @@ static void do_vcvt_hs(CPUARMState *env, void *vd, void *vm, int top) uint16_t *m = vm; uint32_t r; uint16_t mask = mve_element_mask(env); - bool ieee = !(env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_AHP); + bool ieee = !(env->vfp.fpcr & FPCR_AHP); unsigned e; float_status *fpst; float_status scratch_fpst; diff --git a/target/arm/tcg/translate-m-nocp.c b/target/arm/tcg/translate-m-nocp.c index f564d06ccf..875f6a8725 100644 --- a/target/arm/tcg/translate-m-nocp.c +++ b/target/arm/tcg/translate-m-nocp.c @@ -341,10 +341,10 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, 16, 16, qc); } tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); - fpscr = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); + fpscr = load_cpu_field_low32(vfp.fpsr); tcg_gen_andi_i32(fpscr, fpscr, ~FPCR_NZCV_MASK); tcg_gen_or_i32(fpscr, fpscr, tmp); - store_cpu_field(fpscr, vfp.xregs[ARM_VFP_FPSCR]); + store_cpu_field_low32(fpscr, vfp.fpsr); break; } case ARM_VFP_FPCXT_NS: @@ -465,7 +465,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, * Read just NZCV; this is a special case to avoid the * helper call for the "VMRS to CPSR.NZCV" insn. */ - tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); + tmp = load_cpu_field_low32(vfp.fpsr); tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); storefn(s, opaque, tmp, true); break; diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 39ec971ff7..0d9788e810 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -833,7 +833,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) break; case ARM_VFP_FPSCR: if (a->rt == 15) { - tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); + tmp = load_cpu_field_low32(vfp.fpsr); tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); } else { tmp = tcg_temp_new_i32(); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index aba21f730f..a8672c857c 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -351,8 +351,7 @@ static inline TCGv_i32 get_ahp_flag(void) { TCGv_i32 ret = tcg_temp_new_i32(); - tcg_gen_ld_i32(ret, tcg_env, - offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPSCR])); + tcg_gen_ld_i32(ret, tcg_env, offsetoflow32(CPUARMState, vfp.fpcr)); tcg_gen_extract_i32(ret, ret, 26, 1); return ret; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 678de5eb6f..a8c89a910f 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -115,7 +115,7 @@ static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val) { - uint32_t changed = env->vfp.xregs[ARM_VFP_FPSCR]; + uint64_t changed = env->vfp.fpcr; changed ^= val; if (changed & (3 << 22)) { @@ -175,7 +175,7 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val) uint32_t vfp_get_fpcr(CPUARMState *env) { - uint32_t fpcr = (env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_MASK) + uint32_t fpcr = env->vfp.fpcr | (env->vfp.vec_len << 16) | (env->vfp.vec_stride << 20); @@ -190,7 +190,7 @@ uint32_t vfp_get_fpcr(CPUARMState *env) uint32_t vfp_get_fpsr(CPUARMState *env) { - uint32_t fpsr = env->vfp.xregs[ARM_VFP_FPSCR] & FPSR_MASK; + uint32_t fpsr = env->vfp.fpsr; uint32_t i; fpsr |= vfp_get_fpsr_from_host(env); @@ -229,15 +229,13 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val) } /* - * The only FPSR bits we keep in vfp.xregs[FPSCR] are NZCV: + * The only FPSR bits we keep in vfp.fpsr are NZCV: * the exception flags IOC|DZC|OFC|UFC|IXC|IDC are stored in * fp_status, and QC is in vfp.qc[]. Store the NZCV bits there, - * and zero any of the other FPSR bits (but preserve the FPCR - * bits). + * and zero any of the other FPSR bits. */ val &= FPCR_NZCV_MASK; - env->vfp.xregs[ARM_VFP_FPSCR] &= ~FPSR_MASK; - env->vfp.xregs[ARM_VFP_FPSCR] |= val; + env->vfp.fpsr = val; } void vfp_set_fpcr(CPUARMState *env, uint32_t val) @@ -271,14 +269,13 @@ void vfp_set_fpcr(CPUARMState *env, uint32_t val) * We don't implement trapped exception handling, so the * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) * - * The FPCR bits we keep in vfp.xregs[FPSCR] are AHP, DN, FZ, RMode + * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode * and FZ16. Len, Stride and LTPSIZE we just handled. Store those bits * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI * bits. */ val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16; - env->vfp.xregs[ARM_VFP_FPSCR] &= ~FPCR_MASK; - env->vfp.xregs[ARM_VFP_FPSCR] |= val; + env->vfp.fpcr = val; } void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) @@ -356,8 +353,7 @@ static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) default: g_assert_not_reached(); } - env->vfp.xregs[ARM_VFP_FPSCR] = - deposit32(env->vfp.xregs[ARM_VFP_FPSCR], 28, 4, flags); + env->vfp.fpsr = deposit64(env->vfp.fpsr, 28, 4, flags); /* NZCV */ } /* XXX: check quiet/signaling case */ @@ -1160,8 +1156,7 @@ uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) uint32_t z = (pair >> 32) == 0; /* Store Z, clear NCV, in FPSCR.NZCV. */ - env->vfp.xregs[ARM_VFP_FPSCR] - = (env->vfp.xregs[ARM_VFP_FPSCR] & ~CPSR_NZCV) | (z * CPSR_Z); + env->vfp.fpsr = (env->vfp.fpsr & ~FPCR_NZCV_MASK) | (z * FPCR_Z); return result; } From a26db547b7d571d97d33afc0f952afa34678ea3b Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:45 +0100 Subject: [PATCH 2068/2884] target/arm: Rename FPCR_ QC, NZCV macros to FPSR_ The QC, N, Z, C, V bits live in the FPSR, not the FPCR. Rename the macros that define these bits accordingly. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-8-peter.maydell@linaro.org --- target/arm/cpu.h | 17 ++++++++++------- target/arm/tcg/mve_helper.c | 8 ++++---- target/arm/tcg/translate-m-nocp.c | 16 ++++++++-------- target/arm/tcg/translate-vfp.c | 2 +- target/arm/vfp_helper.c | 8 ++++---- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 5323223919..c75b0a73ae 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1697,6 +1697,7 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); #define FPSR_MASK 0xf800009f #define FPCR_MASK 0x07ff9f00 +/* FPCR bits */ #define FPCR_IOE (1 << 8) /* Invalid Operation exception trap enable */ #define FPCR_DZE (1 << 9) /* Divide by Zero exception trap enable */ #define FPCR_OFE (1 << 10) /* Overflow exception trap enable */ @@ -1708,18 +1709,20 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); #define FPCR_FZ (1 << 24) /* Flush-to-zero enable bit */ #define FPCR_DN (1 << 25) /* Default NaN enable bit */ #define FPCR_AHP (1 << 26) /* Alternative half-precision */ -#define FPCR_QC (1 << 27) /* Cumulative saturation bit */ -#define FPCR_V (1 << 28) /* FP overflow flag */ -#define FPCR_C (1 << 29) /* FP carry flag */ -#define FPCR_Z (1 << 30) /* FP zero flag */ -#define FPCR_N (1 << 31) /* FP negative flag */ #define FPCR_LTPSIZE_SHIFT 16 /* LTPSIZE, M-profile only */ #define FPCR_LTPSIZE_MASK (7 << FPCR_LTPSIZE_SHIFT) #define FPCR_LTPSIZE_LENGTH 3 -#define FPCR_NZCV_MASK (FPCR_N | FPCR_Z | FPCR_C | FPCR_V) -#define FPCR_NZCVQC_MASK (FPCR_NZCV_MASK | FPCR_QC) +/* FPSR bits */ +#define FPSR_QC (1 << 27) /* Cumulative saturation bit */ +#define FPSR_V (1 << 28) /* FP overflow flag */ +#define FPSR_C (1 << 29) /* FP carry flag */ +#define FPSR_Z (1 << 30) /* FP zero flag */ +#define FPSR_N (1 << 31) /* FP negative flag */ + +#define FPSR_NZCV_MASK (FPSR_N | FPSR_Z | FPSR_C | FPSR_V) +#define FPSR_NZCVQC_MASK (FPSR_NZCV_MASK | FPSR_QC) /** * vfp_get_fpsr: read the AArch64 FPSR diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 234f395b09..03ebef5ef2 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -1115,21 +1115,21 @@ static void do_vadc(CPUARMState *env, uint32_t *d, uint32_t *n, uint32_t *m, if (update_flags) { /* Store C, clear NZV. */ - env->vfp.fpsr &= ~FPCR_NZCV_MASK; - env->vfp.fpsr |= carry_in * FPCR_C; + env->vfp.fpsr &= ~FPSR_NZCV_MASK; + env->vfp.fpsr |= carry_in * FPSR_C; } mve_advance_vpt(env); } void HELPER(mve_vadc)(CPUARMState *env, void *vd, void *vn, void *vm) { - bool carry_in = env->vfp.fpsr & FPCR_C; + bool carry_in = env->vfp.fpsr & FPSR_C; do_vadc(env, vd, vn, vm, 0, carry_in, false); } void HELPER(mve_vsbc)(CPUARMState *env, void *vd, void *vn, void *vm) { - bool carry_in = env->vfp.fpsr & FPCR_C; + bool carry_in = env->vfp.fpsr & FPSR_C; do_vadc(env, vd, vn, vm, -1, carry_in, false); } diff --git a/target/arm/tcg/translate-m-nocp.c b/target/arm/tcg/translate-m-nocp.c index 875f6a8725..b92773b4af 100644 --- a/target/arm/tcg/translate-m-nocp.c +++ b/target/arm/tcg/translate-m-nocp.c @@ -332,7 +332,7 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, if (dc_isar_feature(aa32_mve, s)) { /* QC is only present for MVE; otherwise RES0 */ TCGv_i32 qc = tcg_temp_new_i32(); - tcg_gen_andi_i32(qc, tmp, FPCR_QC); + tcg_gen_andi_i32(qc, tmp, FPSR_QC); /* * The 4 vfp.qc[] fields need only be "zero" vs "non-zero"; * here writing the same value into all elements is simplest. @@ -340,9 +340,9 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, tcg_gen_gvec_dup_i32(MO_32, offsetof(CPUARMState, vfp.qc), 16, 16, qc); } - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCV_MASK); fpscr = load_cpu_field_low32(vfp.fpsr); - tcg_gen_andi_i32(fpscr, fpscr, ~FPCR_NZCV_MASK); + tcg_gen_andi_i32(fpscr, fpscr, ~FPSR_NZCV_MASK); tcg_gen_or_i32(fpscr, fpscr, tmp); store_cpu_field_low32(fpscr, vfp.fpsr); break; @@ -390,7 +390,7 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, tcg_gen_deposit_i32(control, control, sfpa, R_V7M_CONTROL_SFPA_SHIFT, 1); store_cpu_field(control, v7m.control[M_REG_S]); - tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, tmp, ~FPSR_NZCV_MASK); gen_helper_vfp_set_fpscr(tcg_env, tmp); s->base.is_jmp = DISAS_UPDATE_NOCHAIN; break; @@ -457,7 +457,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, case ARM_VFP_FPSCR_NZCVQC: tmp = tcg_temp_new_i32(); gen_helper_vfp_get_fpscr(tmp, tcg_env); - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCVQC_MASK); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCVQC_MASK); storefn(s, opaque, tmp, true); break; case QEMU_VFP_FPSCR_NZCV: @@ -466,7 +466,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, * helper call for the "VMRS to CPSR.NZCV" insn. */ tmp = load_cpu_field_low32(vfp.fpsr); - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCV_MASK); storefn(s, opaque, tmp, true); break; case ARM_VFP_FPCXT_S: @@ -476,7 +476,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, tmp = tcg_temp_new_i32(); sfpa = tcg_temp_new_i32(); gen_helper_vfp_get_fpscr(tmp, tcg_env); - tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, tmp, ~FPSR_NZCV_MASK); control = load_cpu_field(v7m.control[M_REG_S]); tcg_gen_andi_i32(sfpa, control, R_V7M_CONTROL_SFPA_MASK); tcg_gen_shli_i32(sfpa, sfpa, 31 - R_V7M_CONTROL_SFPA_SHIFT); @@ -529,7 +529,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, sfpa = tcg_temp_new_i32(); fpscr = tcg_temp_new_i32(); gen_helper_vfp_get_fpscr(fpscr, tcg_env); - tcg_gen_andi_i32(tmp, fpscr, ~FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, fpscr, ~FPSR_NZCV_MASK); control = load_cpu_field(v7m.control[M_REG_S]); tcg_gen_andi_i32(sfpa, control, R_V7M_CONTROL_SFPA_MASK); tcg_gen_shli_i32(sfpa, sfpa, 31 - R_V7M_CONTROL_SFPA_SHIFT); diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 0d9788e810..cd5b848357 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -834,7 +834,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) case ARM_VFP_FPSCR: if (a->rt == 15) { tmp = load_cpu_field_low32(vfp.fpsr); - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCV_MASK); } else { tmp = tcg_temp_new_i32(); gen_helper_vfp_get_fpscr(tmp, tcg_env); diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index a8c89a910f..85e50ede37 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -196,7 +196,7 @@ uint32_t vfp_get_fpsr(CPUARMState *env) fpsr |= vfp_get_fpsr_from_host(env); i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3]; - fpsr |= i ? FPCR_QC : 0; + fpsr |= i ? FPSR_QC : 0; return fpsr; } @@ -222,7 +222,7 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val) * The bit we set within vfp.qc[] is arbitrary; the array as a * whole being zero/non-zero is what counts. */ - env->vfp.qc[0] = val & FPCR_QC; + env->vfp.qc[0] = val & FPSR_QC; env->vfp.qc[1] = 0; env->vfp.qc[2] = 0; env->vfp.qc[3] = 0; @@ -234,7 +234,7 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val) * fp_status, and QC is in vfp.qc[]. Store the NZCV bits there, * and zero any of the other FPSR bits. */ - val &= FPCR_NZCV_MASK; + val &= FPSR_NZCV_MASK; env->vfp.fpsr = val; } @@ -1156,7 +1156,7 @@ uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) uint32_t z = (pair >> 32) == 0; /* Store Z, clear NCV, in FPSCR.NZCV. */ - env->vfp.fpsr = (env->vfp.fpsr & ~FPCR_NZCV_MASK) | (z * FPCR_Z); + env->vfp.fpsr = (env->vfp.fpsr & ~FPSR_NZCV_MASK) | (z * FPSR_Z); return result; } From db397a81eea8a7dce25804398caa750fd6700e49 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:46 +0100 Subject: [PATCH 2069/2884] target/arm: Rename FPSR_MASK and FPCR_MASK and define them symbolically Now that we store FPSR and FPCR separately, the FPSR_MASK and FPCR_MASK macros are slightly confusingly named and the comment describing them is out of date. Rename them to FPSCR_FPSR_MASK and FPSCR_FPCR_MASK, document that they are the mask of which FPSCR bits are architecturally mapped to which AArch64 register, and define them symbolically rather than as hex values. (This latter requires defining some extra macros for bits which we haven't previously defined.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-9-peter.maydell@linaro.org --- target/arm/cpu.h | 41 ++++++++++++++++++++++++++++++++++------- target/arm/machine.c | 3 ++- target/arm/vfp_helper.c | 7 ++++--- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c75b0a73ae..4c656bdbb7 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1687,15 +1687,19 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) uint32_t vfp_get_fpscr(CPUARMState *env); void vfp_set_fpscr(CPUARMState *env, uint32_t val); -/* FPCR, Floating Point Control Register - * FPSR, Floating Poiht Status Register +/* + * FPCR, Floating Point Control Register + * FPSR, Floating Point Status Register * - * For A64 the FPSCR is split into two logically distinct registers, - * FPCR and FPSR. However since they still use non-overlapping bits - * we store the underlying state in fpscr and just mask on read/write. + * For A64 floating point control and status bits are stored in + * two logically distinct registers, FPCR and FPSR. We store these + * in QEMU in vfp.fpcr and vfp.fpsr. + * For A32 there was only one register, FPSCR. The bits are arranged + * such that FPSCR bits map to FPCR or FPSR bits in the same bit positions, + * so we can use appropriate masking to handle FPSCR reads and writes. + * Note that the FPCR has some bits which are not visible in the + * AArch32 view (for FEAT_AFP). Writing the FPSCR leaves these unchanged. */ -#define FPSR_MASK 0xf800009f -#define FPCR_MASK 0x07ff9f00 /* FPCR bits */ #define FPCR_IOE (1 << 8) /* Invalid Operation exception trap enable */ @@ -1704,7 +1708,9 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); #define FPCR_UFE (1 << 11) /* Underflow exception trap enable */ #define FPCR_IXE (1 << 12) /* Inexact exception trap enable */ #define FPCR_IDE (1 << 15) /* Input Denormal exception trap enable */ +#define FPCR_LEN_MASK (7 << 16) /* LEN, A-profile only */ #define FPCR_FZ16 (1 << 19) /* ARMv8.2+, FP16 flush-to-zero */ +#define FPCR_STRIDE_MASK (3 << 20) /* Stride */ #define FPCR_RMODE_MASK (3 << 22) /* Rounding mode */ #define FPCR_FZ (1 << 24) /* Flush-to-zero enable bit */ #define FPCR_DN (1 << 25) /* Default NaN enable bit */ @@ -1714,16 +1720,37 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); #define FPCR_LTPSIZE_MASK (7 << FPCR_LTPSIZE_SHIFT) #define FPCR_LTPSIZE_LENGTH 3 +/* Cumulative exception trap enable bits */ +#define FPCR_EEXC_MASK (FPCR_IOE | FPCR_DZE | FPCR_OFE | FPCR_UFE | FPCR_IXE | FPCR_IDE) + /* FPSR bits */ +#define FPSR_IOC (1 << 0) /* Invalid Operation cumulative exception */ +#define FPSR_DZC (1 << 1) /* Divide by Zero cumulative exception */ +#define FPSR_OFC (1 << 2) /* Overflow cumulative exception */ +#define FPSR_UFC (1 << 3) /* Underflow cumulative exception */ +#define FPSR_IXC (1 << 4) /* Inexact cumulative exception */ +#define FPSR_IDC (1 << 7) /* Input Denormal cumulative exception */ #define FPSR_QC (1 << 27) /* Cumulative saturation bit */ #define FPSR_V (1 << 28) /* FP overflow flag */ #define FPSR_C (1 << 29) /* FP carry flag */ #define FPSR_Z (1 << 30) /* FP zero flag */ #define FPSR_N (1 << 31) /* FP negative flag */ +/* Cumulative exception status bits */ +#define FPSR_CEXC_MASK (FPSR_IOC | FPSR_DZC | FPSR_OFC | FPSR_UFC | FPSR_IXC | FPSR_IDC) + #define FPSR_NZCV_MASK (FPSR_N | FPSR_Z | FPSR_C | FPSR_V) #define FPSR_NZCVQC_MASK (FPSR_NZCV_MASK | FPSR_QC) +/* A32 FPSCR bits which architecturally map to FPSR bits */ +#define FPSCR_FPSR_MASK (FPSR_NZCVQC_MASK | FPSR_CEXC_MASK) +/* A32 FPSCR bits which architecturally map to FPCR bits */ +#define FPSCR_FPCR_MASK (FPCR_EEXC_MASK | FPCR_LEN_MASK | FPCR_FZ16 | \ + FPCR_STRIDE_MASK | FPCR_RMODE_MASK | \ + FPCR_FZ | FPCR_DN | FPCR_AHP) +/* These masks don't overlap: each bit lives in only one place */ +QEMU_BUILD_BUG_ON(FPSCR_FPSR_MASK & FPSCR_FPCR_MASK); + /** * vfp_get_fpsr: read the AArch64 FPSR * @env: CPU context diff --git a/target/arm/machine.c b/target/arm/machine.c index 8c820955d9..a3c1e05e65 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -43,7 +43,8 @@ static bool vfp_fpcr_fpsr_needed(void *opaque) ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; - return (vfp_get_fpcr(env) & ~FPCR_MASK) || (vfp_get_fpsr(env) & ~FPSR_MASK); + return (vfp_get_fpcr(env) & ~FPSCR_FPCR_MASK) || + (vfp_get_fpsr(env) & ~FPSCR_FPSR_MASK); } static int get_fpscr(QEMUFile *f, void *opaque, size_t size, diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 85e50ede37..cbe69ae3fe 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -202,7 +202,8 @@ uint32_t vfp_get_fpsr(CPUARMState *env) uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) { - return (vfp_get_fpcr(env) & FPCR_MASK) | (vfp_get_fpsr(env) & FPSR_MASK); + return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) | + (vfp_get_fpsr(env) & FPSCR_FPSR_MASK); } uint32_t vfp_get_fpscr(CPUARMState *env) @@ -280,8 +281,8 @@ void vfp_set_fpcr(CPUARMState *env, uint32_t val) void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) { - vfp_set_fpcr(env, val & FPCR_MASK); - vfp_set_fpsr(env, val & FPSR_MASK); + vfp_set_fpcr(env, val & FPSCR_FPCR_MASK); + vfp_set_fpsr(env, val & FPSCR_FPSR_MASK); } void vfp_set_fpscr(CPUARMState *env, uint32_t val) From a8ab8706d4cc461481ce419e62e3ad48b03638a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 28 Jun 2024 15:23:47 +0100 Subject: [PATCH 2070/2884] target/arm: Allow FPCR bits that aren't in FPSCR In order to allow FPCR bits that aren't in the FPSCR (like the new bits that are defined for FEAT_AFP), we need to make sure that writes to the FPSCR only write to the bits of FPCR that are architecturally mapped, and not the others. Implement this with a new function vfp_set_fpcr_masked() which takes a mask of which bits to update. (We could do the same for FPSR, but we leave that until we actually are likely to need it.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240628142347.1283015-10-peter.maydell@linaro.org --- target/arm/vfp_helper.c | 54 ++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index cbe69ae3fe..b3698da8ca 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -113,11 +113,12 @@ static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); } -static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val) +static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { uint64_t changed = env->vfp.fpcr; changed ^= val; + changed &= mask; if (changed & (3 << 22)) { int i = (val >> 22) & 3; switch (i) { @@ -167,7 +168,7 @@ static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) { } -static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val) +static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { } @@ -239,8 +240,13 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val) env->vfp.fpsr = val; } -void vfp_set_fpcr(CPUARMState *env, uint32_t val) +static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) { + /* + * We only set FPCR bits defined by mask, and leave the others alone. + * We assume the mask is sensible (e.g. doesn't try to set only + * part of a field) + */ ARMCPU *cpu = env_archcpu(env); /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ @@ -248,22 +254,24 @@ void vfp_set_fpcr(CPUARMState *env, uint32_t val) val &= ~FPCR_FZ16; } - vfp_set_fpcr_to_host(env, val); + vfp_set_fpcr_to_host(env, val, mask); - if (!arm_feature(env, ARM_FEATURE_M)) { - /* - * Short-vector length and stride; on M-profile these bits - * are used for different purposes. - * We can't make this conditional be "if MVFR0.FPShVec != 0", - * because in v7A no-short-vector-support cores still had to - * allow Stride/Len to be written with the only effect that - * some insns are required to UNDEF if the guest sets them. - */ - env->vfp.vec_len = extract32(val, 16, 3); - env->vfp.vec_stride = extract32(val, 20, 2); - } else if (cpu_isar_feature(aa32_mve, cpu)) { - env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT, - FPCR_LTPSIZE_LENGTH); + if (mask & (FPCR_LEN_MASK | FPCR_STRIDE_MASK)) { + if (!arm_feature(env, ARM_FEATURE_M)) { + /* + * Short-vector length and stride; on M-profile these bits + * are used for different purposes. + * We can't make this conditional be "if MVFR0.FPShVec != 0", + * because in v7A no-short-vector-support cores still had to + * allow Stride/Len to be written with the only effect that + * some insns are required to UNDEF if the guest sets them. + */ + env->vfp.vec_len = extract32(val, 16, 3); + env->vfp.vec_stride = extract32(val, 20, 2); + } else if (cpu_isar_feature(aa32_mve, cpu)) { + env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT, + FPCR_LTPSIZE_LENGTH); + } } /* @@ -276,12 +284,18 @@ void vfp_set_fpcr(CPUARMState *env, uint32_t val) * bits. */ val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16; - env->vfp.fpcr = val; + env->vfp.fpcr &= ~mask; + env->vfp.fpcr |= val; +} + +void vfp_set_fpcr(CPUARMState *env, uint32_t val) +{ + vfp_set_fpcr_masked(env, val, MAKE_64BIT_MASK(0, 32)); } void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) { - vfp_set_fpcr(env, val & FPSCR_FPCR_MASK); + vfp_set_fpcr_masked(env, val, FPSCR_FPCR_MASK); vfp_set_fpsr(env, val & FPSCR_FPSR_MASK); } From b88cfee90268cad376682da8f99ccf024d7aa304 Mon Sep 17 00:00:00 2001 From: Zheyu Ma <zheyuma97@gmail.com> Date: Tue, 2 Jul 2024 17:57:52 +0200 Subject: [PATCH 2071/2884] hw/char/pl011: Avoid division-by-zero in pl011_get_baudrate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pl011_get_baudrate(), when we calculate the baudrate we can accidentally divide by zero. This happens because although (as the specification requires) we treat UARTIBRD = 0 as invalid, we aren't correctly limiting UARTIBRD and UARTFBRD values to the 16-bit and 6-bit ranges the hardware allows, and so some non-zero values of UARTIBRD can result in a zero divisor. Enforce the correct register field widths on guest writes and on inbound migration to avoid the division by zero. ASAN log: ==2973125==ERROR: AddressSanitizer: FPE on unknown address 0x55f72629b348 (pc 0x55f72629b348 bp 0x7fffa24d0e00 sp 0x7fffa24d0d60 T0) #0 0x55f72629b348 in pl011_get_baudrate hw/char/pl011.c:255:17 #1 0x55f726298d94 in pl011_trace_baudrate_change hw/char/pl011.c:260:33 #2 0x55f726296fc8 in pl011_write hw/char/pl011.c:378:9 Reproducer: cat << EOF | qemu-system-aarch64 -display \ none -machine accel=qtest, -m 512M -machine realview-pb-a8 -qtest stdio writeq 0x1000b024 0xf8000000 EOF Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240702155752.3022007-1-zheyuma97@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/char/pl011.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 8753b84a84..f8078aa216 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -87,6 +87,12 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) #define CR_DTR (1 << 10) #define CR_LBE (1 << 7) +/* Integer Baud Rate Divider, UARTIBRD */ +#define IBRD_MASK 0x3f + +/* Fractional Baud Rate Divider, UARTFBRD */ +#define FBRD_MASK 0xffff + static const unsigned char pl011_id_arm[8] = { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; static const unsigned char pl011_id_luminary[8] = @@ -374,11 +380,11 @@ static void pl011_write(void *opaque, hwaddr offset, s->ilpr = value; break; case 9: /* UARTIBRD */ - s->ibrd = value; + s->ibrd = value & IBRD_MASK; pl011_trace_baudrate_change(s); break; case 10: /* UARTFBRD */ - s->fbrd = value; + s->fbrd = value & FBRD_MASK; pl011_trace_baudrate_change(s); break; case 11: /* UARTLCR_H */ @@ -531,6 +537,9 @@ static int pl011_post_load(void *opaque, int version_id) s->read_pos = 0; } + s->ibrd &= IBRD_MASK; + s->fbrd &= FBRD_MASK; + return 0; } From 7b11e7cf73fd637671c88c71c28204d157533193 Mon Sep 17 00:00:00 2001 From: Zheyu Ma <zheyuma97@gmail.com> Date: Tue, 2 Jul 2024 17:40:41 +0200 Subject: [PATCH 2072/2884] hw/misc/bcm2835_thermal: Fix access size handling in bcm2835_thermal_ops The current implementation of bcm2835_thermal_ops sets impl.max_access_size and valid.min_access_size to 4, but leaves impl.min_access_size and valid.max_access_size unset, defaulting to 1. This causes issues when the memory system is presented with an access of size 2 at an offset of 3, leading to an attempt to synthesize it as a pair of byte accesses at offsets 3 and 4, which trips an assert. Additionally, the lack of valid.max_access_size setting causes another issue: the memory system tries to synthesize a read using a 4-byte access at offset 3 even though the device doesn't allow unaligned accesses. This patch addresses these issues by explicitly setting both impl.min_access_size and valid.max_access_size to 4, ensuring proper handling of access sizes. Error log: ERROR:hw/misc/bcm2835_thermal.c:55:bcm2835_thermal_read: code should not be reached Bail out! ERROR:hw/misc/bcm2835_thermal.c:55:bcm2835_thermal_read: code should not be reached Aborted Reproducer: cat << EOF | qemu-system-aarch64 -display \ none -machine accel=qtest, -m 512M -machine raspi3b -m 1G -qtest stdio readw 0x3f212003 EOF Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Message-id: 20240702154042.3018932-1-zheyuma97@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/misc/bcm2835_thermal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/misc/bcm2835_thermal.c b/hw/misc/bcm2835_thermal.c index ee7816b8a5..0c49c088a7 100644 --- a/hw/misc/bcm2835_thermal.c +++ b/hw/misc/bcm2835_thermal.c @@ -80,8 +80,10 @@ static void bcm2835_thermal_write(void *opaque, hwaddr addr, static const MemoryRegionOps bcm2835_thermal_ops = { .read = bcm2835_thermal_read, .write = bcm2835_thermal_write, + .impl.min_access_size = 4, .impl.max_access_size = 4, .valid.min_access_size = 4, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; From efceb7d2bd5cb0f82045b7945810c5c029f60eb2 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 2 Jul 2024 08:49:11 -0700 Subject: [PATCH 2073/2884] target/arm: Use cpu_env in cpu_untagged_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a completely artifical memset benchmark object_dynamic_cast_assert dominates the profile, even above guest address resolution and the underlying host memset. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240702154911.1667418-1-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4c656bdbb7..a12859fc53 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3354,8 +3354,8 @@ extern const uint64_t pred_esz_masks[5]; */ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) { - ARMCPU *cpu = ARM_CPU(cs); - if (cpu->env.tagged_addr_enable) { + CPUARMState *env = cpu_env(cs); + if (env->tagged_addr_enable) { /* * TBI is enabled for userspace but not kernelspace addresses. * Only clear the tag if bit 55 is clear. From fcee3707eb0331a7270d9c93ba68e832e8ff8b98 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Thu, 4 Jul 2024 16:57:09 +0100 Subject: [PATCH 2074/2884] target/arm: Set arm_v7m_tcg_ops cpu_exec_halt to arm_cpu_exec_halt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit a96edb687e76 we set the cpu_exec_halt field of the TCGCPUOps arm_tcg_ops to arm_cpu_exec_halt(), but we left the arm_v7m_tcg_ops struct unchanged. That isn't wrong, because for M-profile FEAT_WFxT doesn't exist and the default handling for "no cpu_exec_halt method" is correct, but it's perhaps a little confusing. We would also like to make setting the cpu_exec_halt method mandatory. Initialize arm_v7m_tcg_ops cpu_exec_halt to the same function we use for A-profile. (On M-profile we never set up the wfxt timer so there is no change in behaviour here.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- target/arm/cpu.c | 2 +- target/arm/internals.h | 3 +++ target/arm/tcg/cpu-v7m.c | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 14d4eca127..19191c2391 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1133,7 +1133,7 @@ static bool arm_cpu_virtio_is_big_endian(CPUState *cs) } #ifdef CONFIG_TCG -static bool arm_cpu_exec_halt(CPUState *cs) +bool arm_cpu_exec_halt(CPUState *cs) { bool leave_halt = cpu_has_work(cs); diff --git a/target/arm/internals.h b/target/arm/internals.h index e1aa1a63b9..da22d04121 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -368,6 +368,9 @@ void arm_restore_state_to_opc(CPUState *cs, #ifdef CONFIG_TCG void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); + +/* Our implementation of TCGCPUOps::cpu_exec_halt */ +bool arm_cpu_exec_halt(CPUState *cs); #endif /* CONFIG_TCG */ typedef enum ARMFPRounding { diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index c059c681e9..5496f14dc1 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -244,6 +244,7 @@ static const TCGCPUOps arm_v7m_tcg_ops = { #else .tlb_fill = arm_cpu_tlb_fill, .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, + .cpu_exec_halt = arm_cpu_exec_halt, .do_interrupt = arm_v7m_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, .do_unaligned_access = arm_cpu_do_unaligned_access, From 4f7b1ecba81c9dab8066e891ead8a4fff95781af Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Thu, 4 Jul 2024 16:57:10 +0100 Subject: [PATCH 2075/2884] target: Set TCGCPUOps::cpu_exec_halt to target's has_work implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the TCGCPUOps::cpu_exec_halt method is optional, and if it is not set then the default is to call the CPUClass::has_work method (which has an identical function signature). We would like to make the cpu_exec_halt method mandatory so we can remove the runtime check and fallback handling. In preparation for that, make all the targets which don't need special handling in their cpu_exec_halt set it to their cpu_has_work implementation instead of leaving it unset. (This is every target except for arm and i386.) In the riscv case this requires us to make the function not be local to the source file it's defined in. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- target/alpha/cpu.c | 1 + target/avr/cpu.c | 1 + target/cris/cpu.c | 2 ++ target/hppa/cpu.c | 1 + target/loongarch/cpu.c | 1 + target/m68k/cpu.c | 1 + target/microblaze/cpu.c | 1 + target/mips/cpu.c | 1 + target/openrisc/cpu.c | 1 + target/ppc/cpu_init.c | 2 ++ target/riscv/cpu.c | 2 +- target/riscv/internals.h | 3 +++ target/riscv/tcg/tcg-cpu.c | 2 ++ target/rx/cpu.c | 1 + target/s390x/cpu.c | 1 + target/sh4/cpu.c | 1 + target/sparc/cpu.c | 1 + target/tricore/cpu.c | 1 + target/xtensa/cpu.c | 1 + 19 files changed, 24 insertions(+), 1 deletion(-) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 0e2fbcb397..9db1dffc03 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -219,6 +219,7 @@ static const TCGCPUOps alpha_tcg_ops = { #else .tlb_fill = alpha_cpu_tlb_fill, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, + .cpu_exec_halt = alpha_cpu_has_work, .do_interrupt = alpha_cpu_do_interrupt, .do_transaction_failed = alpha_cpu_do_transaction_failed, .do_unaligned_access = alpha_cpu_do_unaligned_access, diff --git a/target/avr/cpu.c b/target/avr/cpu.c index f53e1192b1..3132842d56 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -210,6 +210,7 @@ static const TCGCPUOps avr_tcg_ops = { .synchronize_from_tb = avr_cpu_synchronize_from_tb, .restore_state_to_opc = avr_restore_state_to_opc, .cpu_exec_interrupt = avr_cpu_exec_interrupt, + .cpu_exec_halt = avr_cpu_has_work, .tlb_fill = avr_cpu_tlb_fill, .do_interrupt = avr_cpu_do_interrupt, }; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index 535ec39c73..ff31ca7fbc 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -186,6 +186,7 @@ static const TCGCPUOps crisv10_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = cris_cpu_tlb_fill, .cpu_exec_interrupt = cris_cpu_exec_interrupt, + .cpu_exec_halt = cris_cpu_has_work, .do_interrupt = crisv10_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; @@ -197,6 +198,7 @@ static const TCGCPUOps crisv32_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = cris_cpu_tlb_fill, .cpu_exec_interrupt = cris_cpu_exec_interrupt, + .cpu_exec_halt = cris_cpu_has_work, .do_interrupt = cris_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index f0507874ce..7cf2e2f266 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -228,6 +228,7 @@ static const TCGCPUOps hppa_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = hppa_cpu_tlb_fill, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, + .cpu_exec_halt = hppa_cpu_has_work, .do_interrupt = hppa_cpu_do_interrupt, .do_unaligned_access = hppa_cpu_do_unaligned_access, .do_transaction_failed = hppa_cpu_do_transaction_failed, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 270f711f11..69f9ad7711 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -736,6 +736,7 @@ static const TCGCPUOps loongarch_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, + .cpu_exec_halt = loongarch_cpu_has_work, .do_interrupt = loongarch_cpu_do_interrupt, .do_transaction_failed = loongarch_cpu_do_transaction_failed, #endif diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index efd6bbded8..1d49f4cb23 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -536,6 +536,7 @@ static const TCGCPUOps m68k_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = m68k_cpu_tlb_fill, .cpu_exec_interrupt = m68k_cpu_exec_interrupt, + .cpu_exec_halt = m68k_cpu_has_work, .do_interrupt = m68k_cpu_do_interrupt, .do_transaction_failed = m68k_cpu_transaction_failed, #endif /* !CONFIG_USER_ONLY */ diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 41ad47d04c..135947ee80 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -413,6 +413,7 @@ static const TCGCPUOps mb_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = mb_cpu_tlb_fill, .cpu_exec_interrupt = mb_cpu_exec_interrupt, + .cpu_exec_halt = mb_cpu_has_work, .do_interrupt = mb_cpu_do_interrupt, .do_transaction_failed = mb_cpu_transaction_failed, .do_unaligned_access = mb_cpu_do_unaligned_access, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index bbe01d07dd..89655b1900 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -555,6 +555,7 @@ static const TCGCPUOps mips_tcg_ops = { #if !defined(CONFIG_USER_ONLY) .tlb_fill = mips_cpu_tlb_fill, .cpu_exec_interrupt = mips_cpu_exec_interrupt, + .cpu_exec_halt = mips_cpu_has_work, .do_interrupt = mips_cpu_do_interrupt, .do_transaction_failed = mips_cpu_do_transaction_failed, .do_unaligned_access = mips_cpu_do_unaligned_access, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index fdaaa09fc8..6ec54ad7a6 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -233,6 +233,7 @@ static const TCGCPUOps openrisc_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = openrisc_cpu_tlb_fill, .cpu_exec_interrupt = openrisc_cpu_exec_interrupt, + .cpu_exec_halt = openrisc_cpu_has_work, .do_interrupt = openrisc_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 01e358a4a5..cdada7987d 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -1,3 +1,4 @@ + /* * PowerPC CPU initialization for qemu. * @@ -7481,6 +7482,7 @@ static const TCGCPUOps ppc_tcg_ops = { #else .tlb_fill = ppc_cpu_tlb_fill, .cpu_exec_interrupt = ppc_cpu_exec_interrupt, + .cpu_exec_halt = ppc_cpu_has_work, .do_interrupt = ppc_cpu_do_interrupt, .cpu_exec_enter = ppc_cpu_exec_enter, .cpu_exec_exit = ppc_cpu_exec_exit, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a2640cf259..c53b0d5b40 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -903,7 +903,7 @@ static vaddr riscv_cpu_get_pc(CPUState *cs) return env->pc; } -static bool riscv_cpu_has_work(CPUState *cs) +bool riscv_cpu_has_work(CPUState *cs) { #ifndef CONFIG_USER_ONLY RISCVCPU *cpu = RISCV_CPU(cs); diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 8239ae83cc..0ac17bc5ad 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -136,4 +136,7 @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) } } +/* Our implementation of CPUClass::has_work */ +bool riscv_cpu_has_work(CPUState *cs); + #endif diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index ae25686824..ecf366d6c7 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -21,6 +21,7 @@ #include "exec/exec-all.h" #include "tcg-cpu.h" #include "cpu.h" +#include "internals.h" #include "pmu.h" #include "time_helper.h" #include "qapi/error.h" @@ -138,6 +139,7 @@ static const TCGCPUOps riscv_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = riscv_cpu_tlb_fill, .cpu_exec_interrupt = riscv_cpu_exec_interrupt, + .cpu_exec_halt = riscv_cpu_has_work, .do_interrupt = riscv_cpu_do_interrupt, .do_transaction_failed = riscv_cpu_do_transaction_failed, .do_unaligned_access = riscv_cpu_do_unaligned_access, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 8a584f0a11..36d2a6f189 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -192,6 +192,7 @@ static const TCGCPUOps rx_tcg_ops = { #ifndef CONFIG_USER_ONLY .cpu_exec_interrupt = rx_cpu_exec_interrupt, + .cpu_exec_halt = rx_cpu_has_work, .do_interrupt = rx_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 2bbeaca36e..0fbfcd35d8 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -370,6 +370,7 @@ static const TCGCPUOps s390_tcg_ops = { #else .tlb_fill = s390_cpu_tlb_fill, .cpu_exec_interrupt = s390_cpu_exec_interrupt, + .cpu_exec_halt = s390_cpu_has_work, .do_interrupt = s390_cpu_do_interrupt, .debug_excp_handler = s390x_cpu_debug_excp_handler, .do_unaligned_access = s390x_cpu_do_unaligned_access, diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 618aa7154e..8f07261dcf 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -254,6 +254,7 @@ static const TCGCPUOps superh_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = superh_cpu_tlb_fill, .cpu_exec_interrupt = superh_cpu_exec_interrupt, + .cpu_exec_halt = superh_cpu_has_work, .do_interrupt = superh_cpu_do_interrupt, .do_unaligned_access = superh_cpu_do_unaligned_access, .io_recompile_replay_branch = superh_io_recompile_replay_branch, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 9bacfb68cb..54cb269e0a 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -926,6 +926,7 @@ static const TCGCPUOps sparc_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = sparc_cpu_tlb_fill, .cpu_exec_interrupt = sparc_cpu_exec_interrupt, + .cpu_exec_halt = sparc_cpu_has_work, .do_interrupt = sparc_cpu_do_interrupt, .do_transaction_failed = sparc_cpu_do_transaction_failed, .do_unaligned_access = sparc_cpu_do_unaligned_access, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index bdefb84511..4d9c0368f2 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -169,6 +169,7 @@ static const TCGCPUOps tricore_tcg_ops = { .synchronize_from_tb = tricore_cpu_synchronize_from_tb, .restore_state_to_opc = tricore_restore_state_to_opc, .tlb_fill = tricore_cpu_tlb_fill, + .cpu_exec_halt = tricore_cpu_has_work, }; static void tricore_cpu_class_init(ObjectClass *c, void *data) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index de907cfeb1..a08c7a0b1f 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -234,6 +234,7 @@ static const TCGCPUOps xtensa_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = xtensa_cpu_tlb_fill, .cpu_exec_interrupt = xtensa_cpu_exec_interrupt, + .cpu_exec_halt = xtensa_cpu_has_work, .do_interrupt = xtensa_cpu_do_interrupt, .do_transaction_failed = xtensa_cpu_do_transaction_failed, .do_unaligned_access = xtensa_cpu_do_unaligned_access, From 0487c631801bf21e5e2ca8a54bf207fb78bd64bf Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Thu, 4 Jul 2024 16:57:10 +0100 Subject: [PATCH 2076/2884] accel/tcg: Make TCGCPUOps::cpu_exec_halt mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all targets set TCGCPUOps::cpu_exec_halt, we can make it mandatory and remove the fallback handling that calls cpu_has_work. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- accel/tcg/cpu-exec.c | 11 +++++------ include/hw/core/tcg-cpu-ops.h | 9 ++++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 6711b58e0b..245fd6327d 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -682,13 +682,8 @@ static inline bool cpu_handle_halt(CPUState *cpu) #ifndef CONFIG_USER_ONLY if (cpu->halted) { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - bool leave_halt; + bool leave_halt = tcg_ops->cpu_exec_halt(cpu); - if (tcg_ops->cpu_exec_halt) { - leave_halt = tcg_ops->cpu_exec_halt(cpu); - } else { - leave_halt = cpu_has_work(cpu); - } if (!leave_halt) { return true; } @@ -1082,6 +1077,10 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) static bool tcg_target_initialized; if (!tcg_target_initialized) { + /* Check mandatory TCGCPUOps handlers */ +#ifndef CONFIG_USER_ONLY + assert(cpu->cc->tcg_ops->cpu_exec_halt); +#endif /* !CONFIG_USER_ONLY */ cpu->cc->tcg_ops->initialize(); tcg_target_initialized = true; } diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 099de3375e..34318cf0e6 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -122,10 +122,13 @@ struct TCGCPUOps { * to do when the CPU is in the halted state. * * Return true to indicate that the CPU should now leave halt, false - * if it should remain in the halted state. + * if it should remain in the halted state. (This should generally + * be the same value that cpu_has_work() would return.) * - * If this method is not provided, the default is to do nothing, and - * to leave halt if cpu_has_work() returns true. + * This method must be provided. If the target does not need to + * do anything special for halt, the same function used for its + * CPUClass::has_work method can be used here, as they have the + * same function signature. */ bool (*cpu_exec_halt)(CPUState *cpu); /** From 27d405301a676beffea4c13f5108c344fa6b8165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= <ines.varhol@telecom-paris.fr> Date: Sun, 7 Jul 2024 10:58:53 +0200 Subject: [PATCH 2077/2884] hw/misc: In STM32L4x5 EXTI, consolidate 2 constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, the EXTI implementation had 16 inbound GPIOs connected to the 16 outbound GPIOs of STM32L4x5 SYSCFG. The EXTI actually handles 40 lines (namely 5 from STM32L4x5 USART devices which are already implemented in QEMU). In order to connect USART devices to EXTI, this commit consolidates constants `EXTI_NUM_INTERRUPT_OUT_LINES` (40) and `EXTI_NUM_GPIO_EVENT_IN_LINES` (16) into `EXTI_NUM_LINES` (40). Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240707085927.122867-2-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/misc/stm32l4x5_exti.c | 6 ++---- include/hw/misc/stm32l4x5_exti.h | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index 6a2ec62d78..b9a69a69f6 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -42,7 +42,6 @@ #define EXTI_SWIER2 0x30 #define EXTI_PR2 0x34 -#define EXTI_NUM_GPIO_EVENT_IN_LINES 16 #define EXTI_MAX_IRQ_PER_BANK 32 #define EXTI_IRQS_BANK0 32 #define EXTI_IRQS_BANK1 8 @@ -238,7 +237,7 @@ static void stm32l4x5_exti_init(Object *obj) { Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); - for (size_t i = 0; i < EXTI_NUM_INTERRUPT_OUT_LINES; i++) { + for (size_t i = 0; i < EXTI_NUM_LINES; i++) { sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); } @@ -246,8 +245,7 @@ static void stm32l4x5_exti_init(Object *obj) TYPE_STM32L4X5_EXTI, 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); - qdev_init_gpio_in(DEVICE(obj), stm32l4x5_exti_set_irq, - EXTI_NUM_GPIO_EVENT_IN_LINES); + qdev_init_gpio_in(DEVICE(obj), stm32l4x5_exti_set_irq, EXTI_NUM_LINES); } static const VMStateDescription vmstate_stm32l4x5_exti = { diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h index 55f763fa37..62f79362f2 100644 --- a/include/hw/misc/stm32l4x5_exti.h +++ b/include/hw/misc/stm32l4x5_exti.h @@ -30,7 +30,7 @@ #define TYPE_STM32L4X5_EXTI "stm32l4x5-exti" OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5ExtiState, STM32L4X5_EXTI) -#define EXTI_NUM_INTERRUPT_OUT_LINES 40 +#define EXTI_NUM_LINES 40 #define EXTI_NUM_REGISTER 2 struct Stm32l4x5ExtiState { @@ -47,7 +47,7 @@ struct Stm32l4x5ExtiState { /* used for edge detection */ uint32_t irq_levels[EXTI_NUM_REGISTER]; - qemu_irq irq[EXTI_NUM_INTERRUPT_OUT_LINES]; + qemu_irq irq[EXTI_NUM_LINES]; }; #endif From bc080002cea5893fcbb0e651f6482f27a19356fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= <ines.varhol@telecom-paris.fr> Date: Sun, 7 Jul 2024 10:58:54 +0200 Subject: [PATCH 2078/2884] hw/misc: In STM32L4x5 EXTI, handle direct interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation for EXTI interrupts only handled "configurable" interrupts, like those originating from STM32L4x5 SYSCFG (the only device currently connected to the EXTI up until now). In order to connect STM32L4x5 USART to the EXTI, this commit adds handling for direct interrupts (interrupts without configurable edge). Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr> Message-id: 20240707085927.122867-3-ines.varhol@telecom-paris.fr Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/misc/stm32l4x5_exti.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index b9a69a69f6..e281841dcf 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -113,6 +113,13 @@ static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level) return; } + /* In case of a direct line interrupt */ + if (extract32(exti_romask[bank], irq, 1)) { + qemu_set_irq(s->irq[oirq], level); + return; + } + + /* In case of a configurable interrupt */ if ((level && extract32(s->rtsr[bank], irq, 1)) || (!level && extract32(s->ftsr[bank], irq, 1))) { From 29f0bef71a80d05cd19b95fce93833383c2b9aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= <ines.varhol@telecom-paris.fr> Date: Sun, 7 Jul 2024 10:58:55 +0200 Subject: [PATCH 2079/2884] hw/arm: In STM32L4x5 SOC, connect USART devices to EXTI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USART devices were previously connecting their outbound IRQs directly to the CPU because the EXTI wasn't handling direct lines interrupts. Now the USART connects to the EXTI inbound GPIOs, and the EXTI connects its IRQs to the CPU. The existing QTest for the USART (tests/qtest/stm32l4x5_usart-test.c) checks that USART1_IRQ in the CPU is pending when expected so it confirms that the connection through the EXTI still works. Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240707085927.122867-4-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/stm32l4x5_soc.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 38f7a2d5d9..fac83d349c 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -81,6 +81,10 @@ static const int exti_irq[NUM_EXTI_IRQ] = { #define RCC_BASE_ADDRESS 0x40021000 #define RCC_IRQ 5 +#define EXTI_USART1_IRQ 26 +#define EXTI_UART4_IRQ 29 +#define EXTI_LPUART1_IRQ 31 + static const int exti_or_gates_out[NUM_EXTI_OR_GATES] = { 23, 40, 63, 1, }; @@ -129,10 +133,6 @@ static const hwaddr uart_addr[] = { #define LPUART_BASE_ADDRESS 0x40008000 -static const int usart_irq[] = { 37, 38, 39 }; -static const int uart_irq[] = { 52, 53 }; -#define LPUART_IRQ 70 - static void stm32l4x5_soc_initfn(Object *obj) { Stm32l4x5SocState *s = STM32L4X5_SOC(obj); @@ -297,6 +297,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) } } + /* Connect SYSCFG to EXTI */ for (unsigned i = 0; i < GPIO_NUM_PINS; i++) { qdev_connect_gpio_out(DEVICE(&s->syscfg), i, qdev_get_gpio_in(DEVICE(&s->exti), i)); @@ -322,15 +323,10 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_mmio_map(busdev, 0, usart_addr[i]); - sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), + EXTI_USART1_IRQ + i)); } - /* - * TODO: Connect the USARTs, UARTs and LPUART to the EXTI once the EXTI - * can handle other gpio-in than the gpios. (e.g. Direct Lines for the - * usarts) - */ - /* UART devices */ for (int i = 0; i < STM_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d-out", STM_NUM_USARTS + i + 1); @@ -343,7 +339,8 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_mmio_map(busdev, 0, uart_addr[i]); - sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, uart_irq[i])); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), + EXTI_UART4_IRQ + i)); } /* LPUART device*/ @@ -356,7 +353,8 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_mmio_map(busdev, 0, LPUART_BASE_ADDRESS); - sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, LPUART_IRQ)); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), + EXTI_LPUART1_IRQ)); /* APB1 BUS */ create_unimplemented_device("TIM2", 0x40000000, 0x400); From 97b06ab705fcd1e4454ac6efba1abe0d72e444b2 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 8 Jul 2024 17:06:05 -0700 Subject: [PATCH 2080/2884] target/arm: Convert SMULL, UMULL, SMLAL, UMLAL, SMLSL, UMLSL to decodetree Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240709000610.382391-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/a64.decode | 22 ++++ target/arm/tcg/translate-a64.c | 184 ++++++++++++++++++++++++--------- 2 files changed, 156 insertions(+), 50 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 223eac3cac..513d112787 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -962,6 +962,13 @@ FCADD_270 0.10 1110 ..0 ..... 11110 1 ..... ..... @qrrr_e FCMLA_v 0 q:1 10 1110 esz:2 0 rm:5 110 rot:2 1 rn:5 rd:5 +SMULL_v 0.00 1110 ..1 ..... 11000 0 ..... ..... @qrrr_e +UMULL_v 0.10 1110 ..1 ..... 11000 0 ..... ..... @qrrr_e +SMLAL_v 0.00 1110 ..1 ..... 10000 0 ..... ..... @qrrr_e +UMLAL_v 0.10 1110 ..1 ..... 10000 0 ..... ..... @qrrr_e +SMLSL_v 0.00 1110 ..1 ..... 10100 0 ..... ..... @qrrr_e +UMLSL_v 0.10 1110 ..1 ..... 10100 0 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h @@ -1047,6 +1054,21 @@ FCMLA_vi 0 0 10 1111 01 idx:1 rm:5 0 rot:2 1 0 0 rn:5 rd:5 esz=1 q=0 FCMLA_vi 0 1 10 1111 01 . rm:5 0 rot:2 1 . 0 rn:5 rd:5 esz=1 idx=%hl q=1 FCMLA_vi 0 1 10 1111 10 0 rm:5 0 rot:2 1 idx:1 0 rn:5 rd:5 esz=2 q=1 +SMULL_vi 0.00 1111 01 .. .... 1010 . 0 ..... ..... @qrrx_h +SMULL_vi 0.00 1111 10 . ..... 1010 . 0 ..... ..... @qrrx_s +UMULL_vi 0.10 1111 01 .. .... 1010 . 0 ..... ..... @qrrx_h +UMULL_vi 0.10 1111 10 . ..... 1010 . 0 ..... ..... @qrrx_s + +SMLAL_vi 0.00 1111 01 .. .... 0010 . 0 ..... ..... @qrrx_h +SMLAL_vi 0.00 1111 10 . ..... 0010 . 0 ..... ..... @qrrx_s +UMLAL_vi 0.10 1111 01 .. .... 0010 . 0 ..... ..... @qrrx_h +UMLAL_vi 0.10 1111 10 . ..... 0010 . 0 ..... ..... @qrrx_s + +SMLSL_vi 0.00 1111 01 .. .... 0110 . 0 ..... ..... @qrrx_h +SMLSL_vi 0.00 1111 10 . ..... 0110 . 0 ..... ..... @qrrx_s +UMLSL_vi 0.10 1111 01 .. .... 0110 . 0 ..... ..... @qrrx_h +UMLSL_vi 0.10 1111 10 . ..... 0110 . 0 ..... ..... @qrrx_s + # Floating-point conditional select FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 6c07aeaf3b..8f0781e1f7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5664,6 +5664,121 @@ static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) return true; } +/* + * Widening vector x vector/indexed. + * + * These read from the top or bottom half of a 128-bit vector. + * After widening, optionally accumulate with a 128-bit vector. + * Implement these inline, as the number of elements are limited + * and the related SVE and SME operations on larger vectors use + * even/odd elements instead of top/bottom half. + * + * If idx >= 0, operand 2 is indexed, otherwise vector. + * If acc, operand 0 is loaded with rd. + */ + +/* For low half, iterating up. */ +static bool do_3op_widening(DisasContext *s, MemOp memop, int top, + int rd, int rn, int rm, int idx, + NeonGenTwo64OpFn *fn, bool acc) +{ + TCGv_i64 tcg_op0 = tcg_temp_new_i64(); + TCGv_i64 tcg_op1 = tcg_temp_new_i64(); + TCGv_i64 tcg_op2 = tcg_temp_new_i64(); + MemOp esz = memop & MO_SIZE; + int half = 8 >> esz; + int top_swap, top_half; + + /* There are no 64x64->128 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + if (idx >= 0) { + read_vec_element(s, tcg_op2, rm, idx, memop); + } + + /* + * For top half inputs, iterate forward; backward for bottom half. + * This means the store to the destination will not occur until + * overlapping input inputs are consumed. + * Use top_swap to conditionally invert the forward iteration index. + */ + top_swap = top ? 0 : half - 1; + top_half = top ? half : 0; + + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; + + read_vec_element(s, tcg_op1, rn, elt + top_half, memop); + if (idx < 0) { + read_vec_element(s, tcg_op2, rm, elt + top_half, memop); + } + if (acc) { + read_vec_element(s, tcg_op0, rd, elt, memop + 1); + } + fn(tcg_op0, tcg_op1, tcg_op2); + write_vec_element(s, tcg_op0, rd, elt, esz + 1); + } + clear_vec_high(s, 1, rd); + return true; +} + +static void gen_muladd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_mul_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} + +static void gen_mulsub_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_mul_i64(t, n, m); + tcg_gen_sub_i64(d, d, t); +} + +TRANS(SMULL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_mul_i64, false) +TRANS(UMULL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_mul_i64, false) +TRANS(SMLAL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_muladd_i64, true) +TRANS(UMLAL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_muladd_i64, true) +TRANS(SMLSL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_mulsub_i64, true) +TRANS(UMLSL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_mulsub_i64, true) + +TRANS(SMULL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + tcg_gen_mul_i64, false) +TRANS(UMULL_vi, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, a->idx, + tcg_gen_mul_i64, false) +TRANS(SMLAL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + gen_muladd_i64, true) +TRANS(UMLAL_vi, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, a->idx, + gen_muladd_i64, true) +TRANS(SMLSL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + gen_mulsub_i64, true) +TRANS(UMLSL_vi, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, a->idx, + gen_mulsub_i64, true) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10684,11 +10799,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, tcg_op1, tcg_op2, tcg_tmp1, tcg_tmp2); break; } - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ - tcg_gen_mul_i64(tcg_passres, tcg_op1, tcg_op2); - break; case 9: /* SQDMLAL, SQDMLAL2 */ case 11: /* SQDMLSL, SQDMLSL2 */ case 13: /* SQDMULL, SQDMULL2 */ @@ -10697,6 +10807,9 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, tcg_passres, tcg_passres); break; default: + case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ + case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ + case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ g_assert_not_reached(); } @@ -10763,23 +10876,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, } } break; - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ - if (size == 0) { - if (is_u) { - gen_helper_neon_mull_u8(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_mull_s8(tcg_passres, tcg_op1, tcg_op2); - } - } else { - if (is_u) { - gen_helper_neon_mull_u16(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_mull_s16(tcg_passres, tcg_op1, tcg_op2); - } - } - break; case 9: /* SQDMLAL, SQDMLAL2 */ case 11: /* SQDMLSL, SQDMLSL2 */ case 13: /* SQDMULL, SQDMULL2 */ @@ -10789,6 +10885,9 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, tcg_passres, tcg_passres); break; default: + case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ + case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ + case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ g_assert_not_reached(); } @@ -10981,9 +11080,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* SMULL, SMULL2, UMULL, UMULL2 */ /* 64 x 64 -> 128 */ if (size == 3) { unallocated_encoding(s); @@ -10996,6 +11092,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm); break; default: + case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ + case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ + case 12: /* SMULL, SMULL2, UMULL, UMULL2 */ /* opcode 15 not allocated */ unallocated_encoding(s); break; @@ -11979,17 +12078,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) int index; switch (16 * u + opcode) { - case 0x02: /* SMLAL, SMLAL2 */ - case 0x12: /* UMLAL, UMLAL2 */ - case 0x06: /* SMLSL, SMLSL2 */ - case 0x16: /* UMLSL, UMLSL2 */ - case 0x0a: /* SMULL, SMULL2 */ - case 0x1a: /* UMULL, UMULL2 */ - if (is_scalar) { - unallocated_encoding(s); - return; - } - break; case 0x03: /* SQDMLAL, SQDMLAL2 */ case 0x07: /* SQDMLSL, SQDMLSL2 */ case 0x0b: /* SQDMULL, SQDMULL2 */ @@ -11997,22 +12085,28 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) default: case 0x00: /* FMLAL */ case 0x01: /* FMLA */ + case 0x02: /* SMLAL, SMLAL2 */ case 0x04: /* FMLSL */ case 0x05: /* FMLS */ + case 0x06: /* SMLSL, SMLSL2 */ case 0x08: /* MUL */ case 0x09: /* FMUL */ + case 0x0a: /* SMULL, SMULL2 */ case 0x0c: /* SQDMULH */ case 0x0d: /* SQRDMULH */ case 0x0e: /* SDOT */ case 0x0f: /* SUDOT / BFDOT / USDOT / BFMLAL */ case 0x10: /* MLA */ case 0x11: /* FCMLA #0 */ + case 0x12: /* UMLAL, UMLAL2 */ case 0x13: /* FCMLA #90 */ case 0x14: /* MLS */ case 0x15: /* FCMLA #180 */ + case 0x16: /* UMLSL, UMLSL2 */ case 0x17: /* FCMLA #270 */ case 0x18: /* FMLAL2 */ case 0x19: /* FMULX */ + case 0x1a: /* UMULL, UMULL2 */ case 0x1c: /* FMLSL2 */ case 0x1d: /* SQRDMLAH */ case 0x1e: /* UDOT */ @@ -12098,12 +12192,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element(s, tcg_res[pass], rd, pass, MO_64); switch (opcode) { - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_passres); - break; - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - tcg_gen_sub_i64(tcg_res[pass], tcg_res[pass], tcg_passres); - break; case 0x7: /* SQDMLSL, SQDMLSL2 */ tcg_gen_neg_i64(tcg_passres, tcg_passres); /* fall through */ @@ -12113,6 +12201,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_passres); break; default: + case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ + case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ g_assert_not_reached(); } } @@ -12170,14 +12260,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element(s, tcg_res[pass], rd, pass, MO_64); switch (opcode) { - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - gen_helper_neon_addl_u32(tcg_res[pass], tcg_res[pass], - tcg_passres); - break; - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - gen_helper_neon_subl_u32(tcg_res[pass], tcg_res[pass], - tcg_passres); - break; case 0x7: /* SQDMLSL, SQDMLSL2 */ gen_helper_neon_negl_u32(tcg_passres, tcg_passres); /* fall through */ @@ -12187,6 +12269,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_passres); break; default: + case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ + case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ g_assert_not_reached(); } } From eb191187f68eed87cb694c98af985a8cd199af6a Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 8 Jul 2024 17:06:06 -0700 Subject: [PATCH 2081/2884] target/arm: Convert SADDL, SSUBL, SABDL, SABAL, and unsigned to decodetree Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240709000610.382391-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/a64.decode | 9 ++ target/arm/tcg/translate-a64.c | 150 +++++++++++++++++---------------- 2 files changed, 87 insertions(+), 72 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 513d112787..e626ee067f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -969,6 +969,15 @@ UMLAL_v 0.10 1110 ..1 ..... 10000 0 ..... ..... @qrrr_e SMLSL_v 0.00 1110 ..1 ..... 10100 0 ..... ..... @qrrr_e UMLSL_v 0.10 1110 ..1 ..... 10100 0 ..... ..... @qrrr_e +SADDL_v 0.00 1110 ..1 ..... 00000 0 ..... ..... @qrrr_e +UADDL_v 0.10 1110 ..1 ..... 00000 0 ..... ..... @qrrr_e +SSUBL_v 0.00 1110 ..1 ..... 00100 0 ..... ..... @qrrr_e +USUBL_v 0.10 1110 ..1 ..... 00100 0 ..... ..... @qrrr_e +SABAL_v 0.00 1110 ..1 ..... 01010 0 ..... ..... @qrrr_e +UABAL_v 0.10 1110 ..1 ..... 01010 0 ..... ..... @qrrr_e +SABDL_v 0.00 1110 ..1 ..... 01110 0 ..... ..... @qrrr_e +UABDL_v 0.10 1110 ..1 ..... 01110 0 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8f0781e1f7..95a37c71bc 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5779,6 +5779,65 @@ TRANS(UMLSL_vi, do_3op_widening, a->esz, a->q, a->rd, a->rn, a->rm, a->idx, gen_mulsub_i64, true) +static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t1, n, m); + tcg_gen_sub_i64(t2, m, n); + tcg_gen_movcond_i64(TCG_COND_GE, d, n, m, t1, t2); +} + +static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t1, n, m); + tcg_gen_sub_i64(t2, m, n); + tcg_gen_movcond_i64(TCG_COND_GEU, d, n, m, t1, t2); +} + +static void gen_saba_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + gen_sabd_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + gen_uabd_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} + +TRANS(SADDL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_add_i64, false) +TRANS(UADDL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_add_i64, false) +TRANS(SSUBL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_sub_i64, false) +TRANS(USUBL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_sub_i64, false) +TRANS(SABDL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_sabd_i64, false) +TRANS(UABDL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_uabd_i64, false) +TRANS(SABAL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_saba_i64, true) +TRANS(UABAL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_uaba_i64, true) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10780,25 +10839,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, } switch (opcode) { - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - tcg_gen_add_i64(tcg_passres, tcg_op1, tcg_op2); - break; - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - tcg_gen_sub_i64(tcg_passres, tcg_op1, tcg_op2); - break; - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - { - TCGv_i64 tcg_tmp1 = tcg_temp_new_i64(); - TCGv_i64 tcg_tmp2 = tcg_temp_new_i64(); - - tcg_gen_sub_i64(tcg_tmp1, tcg_op1, tcg_op2); - tcg_gen_sub_i64(tcg_tmp2, tcg_op2, tcg_op1); - tcg_gen_movcond_i64(is_u ? TCG_COND_GEU : TCG_COND_GE, - tcg_passres, - tcg_op1, tcg_op2, tcg_tmp1, tcg_tmp2); - break; - } case 9: /* SQDMLAL, SQDMLAL2 */ case 11: /* SQDMLSL, SQDMLSL2 */ case 13: /* SQDMULL, SQDMULL2 */ @@ -10810,20 +10850,20 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ + case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ + case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ + case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ + case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ g_assert_not_reached(); } - if (opcode == 9 || opcode == 11) { + if (accop != 0) { /* saturating accumulate ops */ if (accop < 0) { tcg_gen_neg_i64(tcg_passres, tcg_passres); } gen_helper_neon_addl_saturate_s64(tcg_res[pass], tcg_env, tcg_res[pass], tcg_passres); - } else if (accop > 0) { - tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_passres); - } else if (accop < 0) { - tcg_gen_sub_i64(tcg_res[pass], tcg_res[pass], tcg_passres); } } } else { @@ -10844,38 +10884,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, } switch (opcode) { - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - { - TCGv_i64 tcg_op2_64 = tcg_temp_new_i64(); - static NeonGenWidenFn * const widenfns[2][2] = { - { gen_helper_neon_widen_s8, gen_helper_neon_widen_u8 }, - { gen_helper_neon_widen_s16, gen_helper_neon_widen_u16 }, - }; - NeonGenWidenFn *widenfn = widenfns[size][is_u]; - - widenfn(tcg_op2_64, tcg_op2); - widenfn(tcg_passres, tcg_op1); - gen_neon_addl(size, (opcode == 2), tcg_passres, - tcg_passres, tcg_op2_64); - break; - } - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - if (size == 0) { - if (is_u) { - gen_helper_neon_abdl_u16(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_abdl_s16(tcg_passres, tcg_op1, tcg_op2); - } - } else { - if (is_u) { - gen_helper_neon_abdl_u32(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_abdl_s32(tcg_passres, tcg_op1, tcg_op2); - } - } - break; case 9: /* SQDMLAL, SQDMLAL2 */ case 11: /* SQDMLSL, SQDMLSL2 */ case 13: /* SQDMULL, SQDMULL2 */ @@ -10888,22 +10896,21 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ + case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ + case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ + case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ + case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ g_assert_not_reached(); } if (accop != 0) { - if (opcode == 9 || opcode == 11) { - /* saturating accumulate ops */ - if (accop < 0) { - gen_helper_neon_negl_u32(tcg_passres, tcg_passres); - } - gen_helper_neon_addl_saturate_s32(tcg_res[pass], tcg_env, - tcg_res[pass], - tcg_passres); - } else { - gen_neon_addl(size, (accop < 0), tcg_res[pass], - tcg_res[pass], tcg_passres); + /* saturating accumulate ops */ + if (accop < 0) { + gen_helper_neon_negl_u32(tcg_passres, tcg_passres); } + gen_helper_neon_addl_saturate_s32(tcg_res[pass], tcg_env, + tcg_res[pass], + tcg_passres); } } } @@ -11075,11 +11082,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - /* fall through */ - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ /* 64 x 64 -> 128 */ if (size == 3) { unallocated_encoding(s); @@ -11092,6 +11094,10 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm); break; default: + case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ + case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ + case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ + case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ case 12: /* SMULL, SMULL2, UMULL, UMULL2 */ From 7575c5710c2e269299534f598d1094fb4b30c87b Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 8 Jul 2024 17:06:07 -0700 Subject: [PATCH 2082/2884] target/arm: Convert SQDMULL, SQDMLAL, SQDMLSL to decodetree Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240709000610.382391-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/a64.decode | 33 ++ target/arm/tcg/translate-a64.c | 604 ++++++--------------------------- 2 files changed, 138 insertions(+), 499 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index e626ee067f..cf69e7e1be 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -785,6 +785,14 @@ SQRDMULH_s 0111 1110 ..1 ..... 10110 1 ..... ..... @rrr_e SQRDMLAH_s 0111 1110 ..0 ..... 10000 1 ..... ..... @rrr_e SQRDMLSH_s 0111 1110 ..0 ..... 10001 1 ..... ..... @rrr_e +# Decode scalar x scalar as scalar x indexed, with index 0. +SQDMULL_si 0101 1110 011 rm:5 11010 0 rn:5 rd:5 &rrx_e idx=0 esz=1 +SQDMULL_si 0101 1110 101 rm:5 11010 0 rn:5 rd:5 &rrx_e idx=0 esz=2 +SQDMLAL_si 0101 1110 011 rm:5 10010 0 rn:5 rd:5 &rrx_e idx=0 esz=1 +SQDMLAL_si 0101 1110 101 rm:5 10010 0 rn:5 rd:5 &rrx_e idx=0 esz=2 +SQDMLSL_si 0101 1110 011 rm:5 10110 0 rn:5 rd:5 &rrx_e idx=0 esz=1 +SQDMLSL_si 0101 1110 101 rm:5 10110 0 rn:5 rd:5 &rrx_e idx=0 esz=2 + ### Advanced SIMD scalar pairwise FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h @@ -978,6 +986,13 @@ UABAL_v 0.10 1110 ..1 ..... 01010 0 ..... ..... @qrrr_e SABDL_v 0.00 1110 ..1 ..... 01110 0 ..... ..... @qrrr_e UABDL_v 0.10 1110 ..1 ..... 01110 0 ..... ..... @qrrr_e +SQDMULL_v 0.00 1110 011 ..... 11010 0 ..... ..... @qrrr_h +SQDMULL_v 0.00 1110 101 ..... 11010 0 ..... ..... @qrrr_s +SQDMLAL_v 0.00 1110 011 ..... 10010 0 ..... ..... @qrrr_h +SQDMLAL_v 0.00 1110 101 ..... 10010 0 ..... ..... @qrrr_s +SQDMLSL_v 0.00 1110 011 ..... 10110 0 ..... ..... @qrrr_h +SQDMLSL_v 0.00 1110 101 ..... 10110 0 ..... ..... @qrrr_s + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h @@ -1008,6 +1023,15 @@ SQRDMLAH_si 0111 1111 10 .. .... 1101 . 0 ..... ..... @rrx_s SQRDMLSH_si 0111 1111 01 .. .... 1111 . 0 ..... ..... @rrx_h SQRDMLSH_si 0111 1111 10 .. .... 1111 . 0 ..... ..... @rrx_s +SQDMULL_si 0101 1111 01 .. .... 1011 . 0 ..... ..... @rrx_h +SQDMULL_si 0101 1111 10 . ..... 1011 . 0 ..... ..... @rrx_s + +SQDMLAL_si 0101 1111 01 .. .... 0011 . 0 ..... ..... @rrx_h +SQDMLAL_si 0101 1111 10 . ..... 0011 . 0 ..... ..... @rrx_s + +SQDMLSL_si 0101 1111 01 .. .... 0111 . 0 ..... ..... @rrx_h +SQDMLSL_si 0101 1111 10 . ..... 0111 . 0 ..... ..... @rrx_s + ### Advanced SIMD vector x indexed element FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h @@ -1078,6 +1102,15 @@ SMLSL_vi 0.00 1111 10 . ..... 0110 . 0 ..... ..... @qrrx_s UMLSL_vi 0.10 1111 01 .. .... 0110 . 0 ..... ..... @qrrx_h UMLSL_vi 0.10 1111 10 . ..... 0110 . 0 ..... ..... @qrrx_s +SQDMULL_vi 0.00 1111 01 .. .... 1011 . 0 ..... ..... @qrrx_h +SQDMULL_vi 0.00 1111 10 . ..... 1011 . 0 ..... ..... @qrrx_s + +SQDMLAL_vi 0.00 1111 01 .. .... 0011 . 0 ..... ..... @qrrx_h +SQDMLAL_vi 0.00 1111 10 . ..... 0011 . 0 ..... ..... @qrrx_s + +SQDMLSL_vi 0.00 1111 01 .. .... 0111 . 0 ..... ..... @qrrx_h +SQDMLSL_vi 0.00 1111 10 . ..... 0111 . 0 ..... ..... @qrrx_s + # Floating-point conditional select FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 95a37c71bc..07b9cdd78f 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5838,6 +5838,76 @@ TRANS(UABAL_v, do_3op_widening, a->esz, a->q, a->rd, a->rn, a->rm, -1, gen_uaba_i64, true) +static void gen_sqdmull_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + tcg_gen_mul_i64(d, n, m); + gen_helper_neon_addl_saturate_s32(d, tcg_env, d, d); +} + +static void gen_sqdmull_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + tcg_gen_mul_i64(d, n, m); + gen_helper_neon_addl_saturate_s64(d, tcg_env, d, d); +} + +static void gen_sqdmlal_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s32(t, tcg_env, t, t); + gen_helper_neon_addl_saturate_s32(d, tcg_env, d, t); +} + +static void gen_sqdmlal_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s64(t, tcg_env, t, t); + gen_helper_neon_addl_saturate_s64(d, tcg_env, d, t); +} + +static void gen_sqdmlsl_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s32(t, tcg_env, t, t); + tcg_gen_neg_i64(t, t); + gen_helper_neon_addl_saturate_s32(d, tcg_env, d, t); +} + +static void gen_sqdmlsl_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s64(t, tcg_env, t, t); + tcg_gen_neg_i64(t, t); + gen_helper_neon_addl_saturate_s64(d, tcg_env, d, t); +} + +TRANS(SQDMULL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + a->esz == MO_16 ? gen_sqdmull_h : gen_sqdmull_s, false) +TRANS(SQDMLAL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + a->esz == MO_16 ? gen_sqdmlal_h : gen_sqdmlal_s, true) +TRANS(SQDMLSL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) + +TRANS(SQDMULL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + a->esz == MO_16 ? gen_sqdmull_h : gen_sqdmull_s, false) +TRANS(SQDMLAL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + a->esz == MO_16 ? gen_sqdmlal_h : gen_sqdmlal_s, true) +TRANS(SQDMLSL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -5989,6 +6059,38 @@ static bool do_env_scalar3_idx_hs(DisasContext *s, arg_rrx_e *a, TRANS_FEAT(SQRDMLAH_si, aa64_rdm, do_env_scalar3_idx_hs, a, &f_scalar_sqrdmlah) TRANS_FEAT(SQRDMLSH_si, aa64_rdm, do_env_scalar3_idx_hs, a, &f_scalar_sqrdmlsh) +static bool do_scalar_muladd_widening_idx(DisasContext *s, arg_rrx_e *a, + NeonGenTwo64OpFn *fn, bool acc) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + unsigned vsz, dofs; + + if (acc) { + read_vec_element(s, t0, a->rd, 0, a->esz + 1); + } + read_vec_element(s, t1, a->rn, 0, a->esz | MO_SIGN); + read_vec_element(s, t2, a->rm, a->idx, a->esz | MO_SIGN); + fn(t0, t1, t2); + + /* Clear the whole register first, then store scalar. */ + vsz = vec_full_reg_size(s); + dofs = vec_full_reg_offset(s, a->rd); + tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0); + write_vec_element(s, t0, a->rd, 0, a->esz + 1); + } + return true; +} + +TRANS(SQDMULL_si, do_scalar_muladd_widening_idx, a, + a->esz == MO_16 ? gen_sqdmull_h : gen_sqdmull_s, false) +TRANS(SQDMLAL_si, do_scalar_muladd_widening_idx, a, + a->esz == MO_16 ? gen_sqdmlal_h : gen_sqdmlal_s, true) +TRANS(SQDMLSL_si, do_scalar_muladd_widening_idx, a, + a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) + static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, gen_helper_gvec_3_ptr * const fns[3]) { @@ -9821,102 +9923,6 @@ static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn) } } -/* AdvSIMD scalar three different - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +-----+---+-----------+------+---+------+--------+-----+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd | - * +-----+---+-----------+------+---+------+--------+-----+------+------+ - */ -static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) -{ - bool is_u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 4); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (is_u) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0x9: /* SQDMLAL, SQDMLAL2 */ - case 0xb: /* SQDMLSL, SQDMLSL2 */ - case 0xd: /* SQDMULL, SQDMULL2 */ - if (size == 0 || size == 3) { - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (size == 2) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, 0, MO_32 | MO_SIGN); - read_vec_element(s, tcg_op2, rm, 0, MO_32 | MO_SIGN); - - tcg_gen_mul_i64(tcg_res, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s64(tcg_res, tcg_env, tcg_res, tcg_res); - - switch (opcode) { - case 0xd: /* SQDMULL, SQDMULL2 */ - break; - case 0xb: /* SQDMLSL, SQDMLSL2 */ - tcg_gen_neg_i64(tcg_res, tcg_res); - /* fall through */ - case 0x9: /* SQDMLAL, SQDMLAL2 */ - read_vec_element(s, tcg_op1, rd, 0, MO_64); - gen_helper_neon_addl_saturate_s64(tcg_res, tcg_env, - tcg_res, tcg_op1); - break; - default: - g_assert_not_reached(); - } - - write_fp_dreg(s, rd, tcg_res); - } else { - TCGv_i32 tcg_op1 = read_fp_hreg(s, rn); - TCGv_i32 tcg_op2 = read_fp_hreg(s, rm); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - gen_helper_neon_mull_s16(tcg_res, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s32(tcg_res, tcg_env, tcg_res, tcg_res); - - switch (opcode) { - case 0xd: /* SQDMULL, SQDMULL2 */ - break; - case 0xb: /* SQDMLSL, SQDMLSL2 */ - gen_helper_neon_negl_u32(tcg_res, tcg_res); - /* fall through */ - case 0x9: /* SQDMLAL, SQDMLAL2 */ - { - TCGv_i64 tcg_op3 = tcg_temp_new_i64(); - read_vec_element(s, tcg_op3, rd, 0, MO_32); - gen_helper_neon_addl_saturate_s32(tcg_res, tcg_env, - tcg_res, tcg_op3); - break; - } - default: - g_assert_not_reached(); - } - - tcg_gen_ext32u_i64(tcg_res, tcg_res); - write_fp_dreg(s, rd, tcg_res); - } -} - static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) @@ -10784,141 +10790,6 @@ static void gen_neon_addl(int size, bool is_sub, TCGv_i64 tcg_res, genfn(tcg_res, tcg_op1, tcg_op2); } -static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) -{ - /* 3-reg-different widening insns: 64 x 64 -> 128 */ - TCGv_i64 tcg_res[2]; - int pass, accop; - - tcg_res[0] = tcg_temp_new_i64(); - tcg_res[1] = tcg_temp_new_i64(); - - /* Does this op do an adding accumulate, a subtracting accumulate, - * or no accumulate at all? - */ - switch (opcode) { - case 5: - case 8: - case 9: - accop = 1; - break; - case 10: - case 11: - accop = -1; - break; - default: - accop = 0; - break; - } - - if (accop != 0) { - read_vec_element(s, tcg_res[0], rd, 0, MO_64); - read_vec_element(s, tcg_res[1], rd, 1, MO_64); - } - - /* size == 2 means two 32x32->64 operations; this is worth special - * casing because we can generally handle it inline. - */ - if (size == 2) { - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_passres; - MemOp memop = MO_32 | (is_u ? 0 : MO_SIGN); - - int elt = pass + is_q * 2; - - read_vec_element(s, tcg_op1, rn, elt, memop); - read_vec_element(s, tcg_op2, rm, elt, memop); - - if (accop == 0) { - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - switch (opcode) { - case 9: /* SQDMLAL, SQDMLAL2 */ - case 11: /* SQDMLSL, SQDMLSL2 */ - case 13: /* SQDMULL, SQDMULL2 */ - tcg_gen_mul_i64(tcg_passres, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s64(tcg_passres, tcg_env, - tcg_passres, tcg_passres); - break; - default: - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - g_assert_not_reached(); - } - - if (accop != 0) { - /* saturating accumulate ops */ - if (accop < 0) { - tcg_gen_neg_i64(tcg_passres, tcg_passres); - } - gen_helper_neon_addl_saturate_s64(tcg_res[pass], tcg_env, - tcg_res[pass], tcg_passres); - } - } - } else { - /* size 0 or 1, generally helper functions */ - for (pass = 0; pass < 2; pass++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i64 tcg_passres; - int elt = pass + is_q * 2; - - read_vec_element_i32(s, tcg_op1, rn, elt, MO_32); - read_vec_element_i32(s, tcg_op2, rm, elt, MO_32); - - if (accop == 0) { - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - switch (opcode) { - case 9: /* SQDMLAL, SQDMLAL2 */ - case 11: /* SQDMLSL, SQDMLSL2 */ - case 13: /* SQDMULL, SQDMULL2 */ - assert(size == 1); - gen_helper_neon_mull_s16(tcg_passres, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s32(tcg_passres, tcg_env, - tcg_passres, tcg_passres); - break; - default: - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - g_assert_not_reached(); - } - - if (accop != 0) { - /* saturating accumulate ops */ - if (accop < 0) { - gen_helper_neon_negl_u32(tcg_passres, tcg_passres); - } - gen_helper_neon_addl_saturate_s32(tcg_res[pass], tcg_env, - tcg_res[pass], - tcg_passres); - } - } - } - - write_vec_element(s, tcg_res[0], rd, 0, MO_64); - write_vec_element(s, tcg_res[1], rd, 1, MO_64); -} - static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, int opcode, int rd, int rn, int rm) { @@ -11075,32 +10946,17 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) break; } return; - case 9: /* SQDMLAL, SQDMLAL2 */ - case 11: /* SQDMLSL, SQDMLSL2 */ - case 13: /* SQDMULL, SQDMULL2 */ - if (is_u || size == 0) { - unallocated_encoding(s); - return; - } - /* 64 x 64 -> 128 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm); - break; default: case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ + case 9: /* SQDMLAL, SQDMLAL2 */ case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ + case 11: /* SQDMLSL, SQDMLSL2 */ case 12: /* SMULL, SMULL2, UMULL, UMULL2 */ + case 13: /* SQDMULL, SQDMULL2 */ /* opcode 15 not allocated */ unallocated_encoding(s); break; @@ -12049,253 +11905,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) } } -/* AdvSIMD scalar x indexed element - * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0 - * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+ - * | 0 1 | U | 1 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd | - * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+ - * AdvSIMD vector x indexed element - * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+ - * | 0 | Q | U | 0 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd | - * +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+ - */ -static void disas_simd_indexed(DisasContext *s, uint32_t insn) -{ - /* This encoding has two kinds of instruction: - * normal, where we perform elt x idxelt => elt for each - * element in the vector - * long, where we perform elt x idxelt and generate a result of - * double the width of the input element - * The long ops have a 'part' specifier (ie come in INSN, INSN2 pairs). - */ - bool is_scalar = extract32(insn, 28, 1); - bool is_q = extract32(insn, 30, 1); - bool u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int l = extract32(insn, 21, 1); - int m = extract32(insn, 20, 1); - /* Note that the Rm field here is only 4 bits, not 5 as it usually is */ - int rm = extract32(insn, 16, 4); - int opcode = extract32(insn, 12, 4); - int h = extract32(insn, 11, 1); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - int index; - - switch (16 * u + opcode) { - case 0x03: /* SQDMLAL, SQDMLAL2 */ - case 0x07: /* SQDMLSL, SQDMLSL2 */ - case 0x0b: /* SQDMULL, SQDMULL2 */ - break; - default: - case 0x00: /* FMLAL */ - case 0x01: /* FMLA */ - case 0x02: /* SMLAL, SMLAL2 */ - case 0x04: /* FMLSL */ - case 0x05: /* FMLS */ - case 0x06: /* SMLSL, SMLSL2 */ - case 0x08: /* MUL */ - case 0x09: /* FMUL */ - case 0x0a: /* SMULL, SMULL2 */ - case 0x0c: /* SQDMULH */ - case 0x0d: /* SQRDMULH */ - case 0x0e: /* SDOT */ - case 0x0f: /* SUDOT / BFDOT / USDOT / BFMLAL */ - case 0x10: /* MLA */ - case 0x11: /* FCMLA #0 */ - case 0x12: /* UMLAL, UMLAL2 */ - case 0x13: /* FCMLA #90 */ - case 0x14: /* MLS */ - case 0x15: /* FCMLA #180 */ - case 0x16: /* UMLSL, UMLSL2 */ - case 0x17: /* FCMLA #270 */ - case 0x18: /* FMLAL2 */ - case 0x19: /* FMULX */ - case 0x1a: /* UMULL, UMULL2 */ - case 0x1c: /* FMLSL2 */ - case 0x1d: /* SQRDMLAH */ - case 0x1e: /* UDOT */ - case 0x1f: /* SQRDMLSH */ - unallocated_encoding(s); - return; - } - - /* Given MemOp size, adjust register and indexing. */ - switch (size) { - case MO_8: - case MO_64: - unallocated_encoding(s); - return; - case MO_16: - index = h << 2 | l << 1 | m; - break; - case MO_32: - index = h << 1 | l; - rm |= m << 4; - break; - default: - g_assert_not_reached(); - } - - if (!fp_access_check(s)) { - return; - } - - if (size == 3) { - g_assert_not_reached(); - } else { - /* long ops: 16x16->32 or 32x32->64 */ - TCGv_i64 tcg_res[2]; - int pass; - bool satop = extract32(opcode, 0, 1); - MemOp memop = MO_32; - - if (satop || !u) { - memop |= MO_SIGN; - } - - if (size == 2) { - TCGv_i64 tcg_idx = tcg_temp_new_i64(); - - read_vec_element(s, tcg_idx, rm, index, memop); - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_passres; - int passelt; - - if (is_scalar) { - passelt = 0; - } else { - passelt = pass + (is_q * 2); - } - - read_vec_element(s, tcg_op, rn, passelt, memop); - - tcg_res[pass] = tcg_temp_new_i64(); - - if (opcode == 0xa || opcode == 0xb) { - /* Non-accumulating ops */ - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - tcg_gen_mul_i64(tcg_passres, tcg_op, tcg_idx); - - if (satop) { - /* saturating, doubling */ - gen_helper_neon_addl_saturate_s64(tcg_passres, tcg_env, - tcg_passres, tcg_passres); - } - - if (opcode == 0xa || opcode == 0xb) { - continue; - } - - /* Accumulating op: handle accumulate step */ - read_vec_element(s, tcg_res[pass], rd, pass, MO_64); - - switch (opcode) { - case 0x7: /* SQDMLSL, SQDMLSL2 */ - tcg_gen_neg_i64(tcg_passres, tcg_passres); - /* fall through */ - case 0x3: /* SQDMLAL, SQDMLAL2 */ - gen_helper_neon_addl_saturate_s64(tcg_res[pass], tcg_env, - tcg_res[pass], - tcg_passres); - break; - default: - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - g_assert_not_reached(); - } - } - - clear_vec_high(s, !is_scalar, rd); - } else { - TCGv_i32 tcg_idx = tcg_temp_new_i32(); - - assert(size == 1); - read_vec_element_i32(s, tcg_idx, rm, index, size); - - if (!is_scalar) { - /* The simplest way to handle the 16x16 indexed ops is to - * duplicate the index into both halves of the 32 bit tcg_idx - * and then use the usual Neon helpers. - */ - tcg_gen_deposit_i32(tcg_idx, tcg_idx, tcg_idx, 16, 16); - } - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i64 tcg_passres; - - if (is_scalar) { - read_vec_element_i32(s, tcg_op, rn, pass, size); - } else { - read_vec_element_i32(s, tcg_op, rn, - pass + (is_q * 2), MO_32); - } - - tcg_res[pass] = tcg_temp_new_i64(); - - if (opcode == 0xa || opcode == 0xb) { - /* Non-accumulating ops */ - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - if (memop & MO_SIGN) { - gen_helper_neon_mull_s16(tcg_passres, tcg_op, tcg_idx); - } else { - gen_helper_neon_mull_u16(tcg_passres, tcg_op, tcg_idx); - } - if (satop) { - gen_helper_neon_addl_saturate_s32(tcg_passres, tcg_env, - tcg_passres, tcg_passres); - } - - if (opcode == 0xa || opcode == 0xb) { - continue; - } - - /* Accumulating op: handle accumulate step */ - read_vec_element(s, tcg_res[pass], rd, pass, MO_64); - - switch (opcode) { - case 0x7: /* SQDMLSL, SQDMLSL2 */ - gen_helper_neon_negl_u32(tcg_passres, tcg_passres); - /* fall through */ - case 0x3: /* SQDMLAL, SQDMLAL2 */ - gen_helper_neon_addl_saturate_s32(tcg_res[pass], tcg_env, - tcg_res[pass], - tcg_passres); - break; - default: - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - g_assert_not_reached(); - } - } - - if (is_scalar) { - tcg_gen_ext32u_i64(tcg_res[0], tcg_res[0]); - } - } - - if (is_scalar) { - tcg_res[1] = tcg_constant_i64(0); - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } - } -} - /* C3.6 Data processing - SIMD, inc Crypto * * As the decode gets a little complex we are using a table based @@ -12306,16 +11915,13 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes }, - { 0x0f000000, 0x9f000400, disas_simd_indexed }, /* vector indexed */ /* simd_mod_imm decode is a subset of simd_shift_imm, so must precede it */ { 0x0f000400, 0x9ff80400, disas_simd_mod_imm }, { 0x0f000400, 0x9f800400, disas_simd_shift_imm }, { 0x0e000000, 0xbf208c00, disas_simd_tb }, { 0x0e000800, 0xbf208c00, disas_simd_zip_trn }, { 0x2e000000, 0xbf208400, disas_simd_ext }, - { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, - { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, { 0x00000000, 0x00000000, NULL } From 26cb9dbed83ec1028d1dfac5ce7f37c8257e133b Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 8 Jul 2024 17:06:08 -0700 Subject: [PATCH 2083/2884] target/arm: Convert SADDW, SSUBW, UADDW, USUBW to decodetree Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240709000610.382391-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/a64.decode | 5 ++ target/arm/tcg/translate-a64.c | 86 +++++++++++++++++----------------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index cf69e7e1be..32e2f3a0d5 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -993,6 +993,11 @@ SQDMLAL_v 0.00 1110 101 ..... 10010 0 ..... ..... @qrrr_s SQDMLSL_v 0.00 1110 011 ..... 10110 0 ..... ..... @qrrr_h SQDMLSL_v 0.00 1110 101 ..... 10110 0 ..... ..... @qrrr_s +SADDW 0.00 1110 ..1 ..... 00010 0 ..... ..... @qrrr_e +UADDW 0.10 1110 ..1 ..... 00010 0 ..... ..... @qrrr_e +SSUBW 0.00 1110 ..1 ..... 00110 0 ..... ..... @qrrr_e +USUBW 0.10 1110 ..1 ..... 00110 0 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 07b9cdd78f..264d2eeb27 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5908,6 +5908,47 @@ TRANS(SQDMLSL_vi, do_3op_widening, a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) +static bool do_addsub_wide(DisasContext *s, arg_qrrr_e *a, + MemOp sign, bool sub) +{ + TCGv_i64 tcg_op0, tcg_op1; + MemOp esz = a->esz; + int half = 8 >> esz; + bool top = a->q; + int top_swap = top ? 0 : half - 1; + int top_half = top ? half : 0; + + /* There are no 64x64->128 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + tcg_op0 = tcg_temp_new_i64(); + tcg_op1 = tcg_temp_new_i64(); + + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; + + read_vec_element(s, tcg_op1, a->rm, elt + top_half, esz | sign); + read_vec_element(s, tcg_op0, a->rn, elt, esz + 1); + if (sub) { + tcg_gen_sub_i64(tcg_op0, tcg_op0, tcg_op1); + } else { + tcg_gen_add_i64(tcg_op0, tcg_op0, tcg_op1); + } + write_vec_element(s, tcg_op0, a->rd, elt, esz + 1); + } + clear_vec_high(s, 1, a->rd); + return true; +} + +TRANS(SADDW, do_addsub_wide, a, MO_SIGN, false) +TRANS(UADDW, do_addsub_wide, a, 0, false) +TRANS(SSUBW, do_addsub_wide, a, MO_SIGN, true) +TRANS(USUBW, do_addsub_wide, a, 0, true) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10790,37 +10831,6 @@ static void gen_neon_addl(int size, bool is_sub, TCGv_i64 tcg_res, genfn(tcg_res, tcg_op1, tcg_op2); } -static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) -{ - TCGv_i64 tcg_res[2]; - int part = is_q ? 2 : 0; - int pass; - - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i64 tcg_op2_wide = tcg_temp_new_i64(); - static NeonGenWidenFn * const widenfns[3][2] = { - { gen_helper_neon_widen_s8, gen_helper_neon_widen_u8 }, - { gen_helper_neon_widen_s16, gen_helper_neon_widen_u16 }, - { tcg_gen_ext_i32_i64, tcg_gen_extu_i32_i64 }, - }; - NeonGenWidenFn *widenfn = widenfns[size][is_u]; - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element_i32(s, tcg_op2, rm, part + pass, MO_32); - widenfn(tcg_op2_wide, tcg_op2); - tcg_res[pass] = tcg_temp_new_i64(); - gen_neon_addl(size, (opcode == 3), - tcg_res[pass], tcg_op1, tcg_op2_wide); - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } -} - static void do_narrow_round_high_u32(TCGv_i32 res, TCGv_i64 in) { tcg_gen_addi_i64(in, in, 1U << 31); @@ -10889,18 +10899,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) int rd = extract32(insn, 0, 5); switch (opcode) { - case 1: /* SADDW, SADDW2, UADDW, UADDW2 */ - case 3: /* SSUBW, SSUBW2, USUBW, USUBW2 */ - /* 64 x 128 -> 128 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_3rd_wide(s, is_q, is_u, size, opcode, rd, rn, rm); - break; case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */ case 6: /* SUBHN, SUBHN2, RSUBHN, RSUBHN2 */ /* 128 x 128 -> 64 */ @@ -10948,7 +10946,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) return; default: case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ + case 1: /* SADDW, SADDW2, UADDW, UADDW2 */ case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ + case 3: /* SSUBW, SSUBW2, USUBW, USUBW2 */ case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ From f7a8456586840d3ab83f63e023186e58ef9ae6cb Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 8 Jul 2024 17:06:09 -0700 Subject: [PATCH 2084/2884] target/arm: Convert ADDHN, SUBHN, RADDHN, RSUBHN to decodetree Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240709000610.382391-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/a64.decode | 5 ++ target/arm/tcg/translate-a64.c | 127 +++++++++++++++------------------ 2 files changed, 61 insertions(+), 71 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 32e2f3a0d5..afb34a8fd4 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -998,6 +998,11 @@ UADDW 0.10 1110 ..1 ..... 00010 0 ..... ..... @qrrr_e SSUBW 0.00 1110 ..1 ..... 00110 0 ..... ..... @qrrr_e USUBW 0.10 1110 ..1 ..... 00110 0 ..... ..... @qrrr_e +ADDHN 0.00 1110 ..1 ..... 01000 0 ..... ..... @qrrr_e +RADDHN 0.10 1110 ..1 ..... 01000 0 ..... ..... @qrrr_e +SUBHN 0.00 1110 ..1 ..... 01100 0 ..... ..... @qrrr_e +RSUBHN 0.10 1110 ..1 ..... 01100 0 ..... ..... @qrrr_e + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 264d2eeb27..3e0dacdd63 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -5949,6 +5949,60 @@ TRANS(UADDW, do_addsub_wide, a, 0, false) TRANS(SSUBW, do_addsub_wide, a, MO_SIGN, true) TRANS(USUBW, do_addsub_wide, a, 0, true) +static bool do_addsub_highnarrow(DisasContext *s, arg_qrrr_e *a, + bool sub, bool round) +{ + TCGv_i64 tcg_op0, tcg_op1; + MemOp esz = a->esz; + int half = 8 >> esz; + bool top = a->q; + int ebits = 8 << esz; + uint64_t rbit = 1ull << (ebits - 1); + int top_swap, top_half; + + /* There are no 128x128->64 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + tcg_op0 = tcg_temp_new_i64(); + tcg_op1 = tcg_temp_new_i64(); + + /* + * For top half inputs, iterate backward; forward for bottom half. + * This means the store to the destination will not occur until + * overlapping input inputs are consumed. + */ + top_swap = top ? half - 1 : 0; + top_half = top ? half : 0; + + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; + + read_vec_element(s, tcg_op1, a->rm, elt, esz + 1); + read_vec_element(s, tcg_op0, a->rn, elt, esz + 1); + if (sub) { + tcg_gen_sub_i64(tcg_op0, tcg_op0, tcg_op1); + } else { + tcg_gen_add_i64(tcg_op0, tcg_op0, tcg_op1); + } + if (round) { + tcg_gen_addi_i64(tcg_op0, tcg_op0, rbit); + } + tcg_gen_shri_i64(tcg_op0, tcg_op0, ebits); + write_vec_element(s, tcg_op0, a->rd, elt + top_half, esz); + } + clear_vec_high(s, top, a->rd); + return true; +} + +TRANS(ADDHN, do_addsub_highnarrow, a, false, false) +TRANS(SUBHN, do_addsub_highnarrow, a, true, false) +TRANS(RADDHN, do_addsub_highnarrow, a, false, true) +TRANS(RSUBHN, do_addsub_highnarrow, a, true, true) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10813,65 +10867,6 @@ static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) } } -/* Generate code to do a "long" addition or subtraction, ie one done in - * TCGv_i64 on vector lanes twice the width specified by size. - */ -static void gen_neon_addl(int size, bool is_sub, TCGv_i64 tcg_res, - TCGv_i64 tcg_op1, TCGv_i64 tcg_op2) -{ - static NeonGenTwo64OpFn * const fns[3][2] = { - { gen_helper_neon_addl_u16, gen_helper_neon_subl_u16 }, - { gen_helper_neon_addl_u32, gen_helper_neon_subl_u32 }, - { tcg_gen_add_i64, tcg_gen_sub_i64 }, - }; - NeonGenTwo64OpFn *genfn; - assert(size < 3); - - genfn = fns[size][is_sub]; - genfn(tcg_res, tcg_op1, tcg_op2); -} - -static void do_narrow_round_high_u32(TCGv_i32 res, TCGv_i64 in) -{ - tcg_gen_addi_i64(in, in, 1U << 31); - tcg_gen_extrh_i64_i32(res, in); -} - -static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) -{ - TCGv_i32 tcg_res[2]; - int part = is_q ? 2 : 0; - int pass; - - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_wideres = tcg_temp_new_i64(); - static NeonGenNarrowFn * const narrowfns[3][2] = { - { gen_helper_neon_narrow_high_u8, - gen_helper_neon_narrow_round_high_u8 }, - { gen_helper_neon_narrow_high_u16, - gen_helper_neon_narrow_round_high_u16 }, - { tcg_gen_extrh_i64_i32, do_narrow_round_high_u32 }, - }; - NeonGenNarrowFn *gennarrow = narrowfns[size][is_u]; - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - - gen_neon_addl(size, (opcode == 6), tcg_wideres, tcg_op1, tcg_op2); - - tcg_res[pass] = tcg_temp_new_i32(); - gennarrow(tcg_res[pass], tcg_wideres); - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, pass + part, MO_32); - } - clear_vec_high(s, is_q, rd); -} - /* AdvSIMD three different * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+--------+-----+------+------+ @@ -10899,18 +10894,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) int rd = extract32(insn, 0, 5); switch (opcode) { - case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */ - case 6: /* SUBHN, SUBHN2, RSUBHN, RSUBHN2 */ - /* 128 x 128 -> 64 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_3rd_narrowing(s, is_q, is_u, size, opcode, rd, rn, rm); - break; case 14: /* PMULL, PMULL2 */ if (is_u) { unallocated_encoding(s); @@ -10949,7 +10932,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) case 1: /* SADDW, SADDW2, UADDW, UADDW2 */ case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ case 3: /* SSUBW, SSUBW2, USUBW, USUBW2 */ + case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */ case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ + case 6: /* SUBHN, SUBHN2, RSUBHN, RSUBHN2 */ case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ case 9: /* SQDMLAL, SQDMLAL2 */ From 7f49089158a4db644fcbadfa90cd3d30a4868735 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 8 Jul 2024 17:06:10 -0700 Subject: [PATCH 2085/2884] target/arm: Convert PMULL to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240709000610.382391-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/a64.decode | 3 ++ target/arm/tcg/translate-a64.c | 94 +++++----------------------------- 2 files changed, 15 insertions(+), 82 deletions(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index afb34a8fd4..2922de700c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -1003,6 +1003,9 @@ RADDHN 0.10 1110 ..1 ..... 01000 0 ..... ..... @qrrr_e SUBHN 0.00 1110 ..1 ..... 01100 0 ..... ..... @qrrr_e RSUBHN 0.10 1110 ..1 ..... 01100 0 ..... ..... @qrrr_e +PMULL_p8 0.00 1110 001 ..... 11100 0 ..... ..... @qrrr_b +PMULL_p64 0.00 1110 111 ..... 11100 0 ..... ..... @qrrr_b + ### Advanced SIMD scalar x indexed element FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 3e0dacdd63..559a6cd799 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6003,6 +6003,18 @@ TRANS(SUBHN, do_addsub_highnarrow, a, true, false) TRANS(RADDHN, do_addsub_highnarrow, a, false, true) TRANS(RSUBHN, do_addsub_highnarrow, a, true, true) +static bool do_pmull(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3 *fn) +{ + if (fp_access_check(s)) { + /* The Q field specifies lo/hi half input for these insns. */ + gen_gvec_op3_ool(s, true, a->rd, a->rn, a->rm, a->q, fn); + } + return true; +} + +TRANS(PMULL_p8, do_pmull, a, gen_helper_neon_pmull_h) +TRANS_FEAT(PMULL_p64, aa64_pmull, do_pmull, a, gen_helper_gvec_pmull_q) + /* * Advanced SIMD scalar/vector x indexed element */ @@ -10867,87 +10879,6 @@ static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) } } -/* AdvSIMD three different - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd | - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - */ -static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) -{ - /* Instructions in this group fall into three basic classes - * (in each case with the operation working on each element in - * the input vectors): - * (1) widening 64 x 64 -> 128 (with possibly Vd as an extra - * 128 bit input) - * (2) wide 64 x 128 -> 128 - * (3) narrowing 128 x 128 -> 64 - * Here we do initial decode, catch unallocated cases and - * dispatch to separate functions for each class. - */ - int is_q = extract32(insn, 30, 1); - int is_u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 4); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - switch (opcode) { - case 14: /* PMULL, PMULL2 */ - if (is_u) { - unallocated_encoding(s); - return; - } - switch (size) { - case 0: /* PMULL.P8 */ - if (!fp_access_check(s)) { - return; - } - /* The Q field specifies lo/hi half input for this insn. */ - gen_gvec_op3_ool(s, true, rd, rn, rm, is_q, - gen_helper_neon_pmull_h); - break; - - case 3: /* PMULL.P64 */ - if (!dc_isar_feature(aa64_pmull, s)) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - /* The Q field specifies lo/hi half input for this insn. */ - gen_gvec_op3_ool(s, true, rd, rn, rm, is_q, - gen_helper_gvec_pmull_q); - break; - - default: - unallocated_encoding(s); - break; - } - return; - default: - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 1: /* SADDW, SADDW2, UADDW, UADDW2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - case 3: /* SSUBW, SSUBW2, USUBW, USUBW2 */ - case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */ - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 6: /* SUBHN, SUBHN2, RSUBHN, RSUBHN2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 9: /* SQDMLAL, SQDMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 11: /* SQDMLSL, SQDMLSL2 */ - case 12: /* SMULL, SMULL2, UMULL, UMULL2 */ - case 13: /* SQDMULL, SQDMULL2 */ - /* opcode 15 not allocated */ - unallocated_encoding(s); - break; - } -} - static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -11897,7 +11828,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) */ static const AArch64DecodeTable data_proc_simd[] = { /* pattern , mask , fn */ - { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes }, /* simd_mod_imm decode is a subset of simd_shift_imm, so must precede it */ From bc432bc522f5f4ab57e8994d63be41ba20f95b37 Mon Sep 17 00:00:00 2001 From: John Berg <jhnberg@amazon.com> Date: Thu, 4 Apr 2024 13:04:18 +0100 Subject: [PATCH 2086/2884] hw/nvme: Add support for setting the MQES for the NVMe emulation The MQES field in the CAP register describes the Maximum Queue Entries Supported for the IO queues of an NVMe controller. Adding a +1 to the value in this field results in the total queue size. A full queue is when a queue of size N contains N - 1 entries, and the minimum queue size is 2. Thus the lowest MQES value is 1. This patch adds the new mqes property to the NVMe emulation which allows a user to specify the maximum queue size by setting this property. This is useful as it enables testing of NVMe controller where the MQES is relatively small. The smallest NVMe queue size supported in NVMe is 2 submission and completion entries, which means that the smallest legal mqes value is 1. The following example shows how the mqes can be set for a the NVMe emulation: -drive id=nvme0,if=none,file=nvme.img,format=raw -device nvme,drive=nvme0,serial=foo,mqes=1 If the mqes property is not provided then the default mqes will still be 0x7ff (the queue size is 2048 entries). Signed-off-by: John Berg <jhnberg@amazon.co.uk> Reviewed-by: Keith Busch <kbusch@kernel.org> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 8 +++++++- hw/nvme/nvme.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 066389e391..fa7ec0e794 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -7805,6 +7805,11 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) return false; } + if (params->mqes < 1) { + error_setg(errp, "mqes property cannot be less than 1"); + return false; + } + if (n->pmr.dev) { if (params->msix_exclusive_bar) { error_setg(errp, "not enough BARs available to enable PMR"); @@ -8289,7 +8294,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->ctratt = cpu_to_le32(ctratt); - NVME_CAP_SET_MQES(cap, 0x7ff); + NVME_CAP_SET_MQES(cap, n->params.mqes); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NVM); @@ -8459,6 +8464,7 @@ static Property nvme_props[] = { params.sriov_max_vq_per_vf, 0), DEFINE_PROP_BOOL("msix-exclusive-bar", NvmeCtrl, params.msix_exclusive_bar, false), + DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index bed8191bd5..2e7d31c0ae 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -521,6 +521,7 @@ typedef struct NvmeParams { uint32_t num_queues; /* deprecated since 5.1 */ uint32_t max_ioqpairs; uint16_t msix_qsize; + uint16_t mqes; uint32_t cmb_size_mb; uint8_t aerl; uint32_t aer_max_queued; From 3936bbdf9a2e9233875f850c7576c79d06add261 Mon Sep 17 00:00:00 2001 From: Vincent Fu <vincentfu@gmail.com> Date: Fri, 3 May 2024 13:50:04 -0400 Subject: [PATCH 2087/2884] hw/nvme: fix number of PIDs for FDP RUH update The number of PIDs is in the upper 16 bits of cdw10. So we need to right-shift by 16 bits instead of only a single bit. Fixes: 73064edfb864 ("hw/nvme: flexible data placement emulation") Cc: qemu-stable@nongnu.org Signed-off-by: Vincent Fu <vincent.fu@samsung.com> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index fa7ec0e794..231e1127ce 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4352,7 +4352,7 @@ static uint16_t nvme_io_mgmt_send_ruh_update(NvmeCtrl *n, NvmeRequest *req) NvmeNamespace *ns = req->ns; uint32_t cdw10 = le32_to_cpu(cmd->cdw10); uint16_t ret = NVME_SUCCESS; - uint32_t npid = (cdw10 >> 1) + 1; + uint32_t npid = (cdw10 >> 16) + 1; unsigned int i = 0; g_autofree uint16_t *pids = NULL; uint32_t maxnpid; From 8ab8a6dbe416d9db1edc7897133ab4d55a080cc2 Mon Sep 17 00:00:00 2001 From: Minwoo Im <minwoo.im@samsung.com> Date: Wed, 5 Jun 2024 06:13:06 +0900 Subject: [PATCH 2088/2884] hw/nvme: fix BAR size mismatch of SR-IOV VF PF initializes SR-IOV VF BAR0 region in nvme_init_sriov() with bar_size calcaulted by Primary Controller Capability such as VQFRSM and VIFRSM rather than `max_ioqpairs` and `msix_qsize` which is for PF only. In this case, the bar size reported in nvme_init_sriov() by PF and nvme_init_pci() by VF might differ especially with large number of sriov_max_vfs (e.g., 127 which is curret maximum number of VFs). And this reports invalid BAR0 address of VFs to the host operating system so that MMIO access will not be caught properly and, of course, NVMe driver initialization is failed. For example, if we give the following options, BAR size will be initialized by PF with 4K, but VF will try to allocate 8K BAR0 size in nvme_init_pci(). #!/bin/bash nr_vf=$((127)) nr_vq=$(($nr_vf * 2 + 2)) nr_vi=$(($nr_vq / 2 + 1)) nr_ioq=$(($nr_vq + 2)) ... -device nvme,serial=foo,id=nvme0,bus=rp2,subsys=subsys0,mdts=9,msix_qsize=$nr_ioq,max_ioqpairs=$nr_ioq,sriov_max_vfs=$nr_vf,sriov_vq_flexible=$nr_vq,sriov_vi_flexible=$nr_vi \ To fix this issue, this patch modifies the calculation of BAR size in the PF and VF initialization by using different elements: PF: `max_ioqpairs + 1` with `msix_qsize` VF: VQFRSM with VIFRSM Signed-off-by: Minwoo Im <minwoo.im@samsung.com> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 231e1127ce..f3ae54896f 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8104,6 +8104,7 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) uint8_t *pci_conf = pci_dev->config; uint64_t bar_size; unsigned msix_table_offset = 0, msix_pba_offset = 0; + unsigned nr_vectors; int ret; pci_conf[PCI_INTERRUPT_PIN] = 1; @@ -8136,9 +8137,19 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) assert(n->params.msix_qsize >= 1); /* add one to max_ioqpairs to account for the admin queue pair */ - bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, - n->params.msix_qsize, &msix_table_offset, - &msix_pba_offset); + if (!pci_is_vf(pci_dev)) { + nr_vectors = n->params.msix_qsize; + bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, + nr_vectors, &msix_table_offset, + &msix_pba_offset); + } else { + NvmeCtrl *pn = NVME(pcie_sriov_get_pf(pci_dev)); + NvmePriCtrlCap *cap = &pn->pri_ctrl_cap; + + nr_vectors = le16_to_cpu(cap->vifrsm); + bar_size = nvme_mbar_size(le16_to_cpu(cap->vqfrsm), nr_vectors, + &msix_table_offset, &msix_pba_offset); + } memory_region_init(&n->bar0, OBJECT(n), "nvme-bar0", bar_size); memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", @@ -8152,7 +8163,7 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); } - ret = msix_init(pci_dev, n->params.msix_qsize, + ret = msix_init(pci_dev, nr_vectors, &n->bar0, 0, msix_table_offset, &n->bar0, 0, msix_pba_offset, 0, errp); } From 6471556500378c9ce38f58cb6c97217778d14226 Mon Sep 17 00:00:00 2001 From: Minwoo Im <minwoo.im@samsung.com> Date: Wed, 29 May 2024 21:42:31 +0900 Subject: [PATCH 2089/2884] hw/nvme: add Identify Endurance Group List Commit 73064edfb864 ("hw/nvme: flexible data placement emulation") intorudced NVMe FDP feature to nvme-subsys and nvme-ctrl with a single endurance group #1 supported. This means that controller should return proper identify data to host with Identify Endurance Group List (CNS 19h). But, yes, only just for the endurance group #1. This patch allows host applications to ask for which endurance group is available and utilize FDP through that endurance group. Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Minwoo Im <minwoo.im@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 22 ++++++++++++++++++++++ include/block/nvme.h | 1 + 2 files changed, 23 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index f3ae54896f..50f8cc90b0 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -5629,6 +5629,26 @@ static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req, return nvme_c2h(n, list, data_len, req); } +static uint16_t nvme_endurance_group_list(NvmeCtrl *n, NvmeRequest *req) +{ + uint16_t list[NVME_CONTROLLER_LIST_SIZE] = {}; + uint16_t *nr_ids = &list[0]; + uint16_t *ids = &list[1]; + uint16_t endgid = le32_to_cpu(req->cmd.cdw11) & 0xffff; + + /* + * The current nvme-subsys only supports Endurance Group #1. + */ + if (!endgid) { + *nr_ids = 1; + ids[0] = 1; + } else { + *nr_ids = 0; + } + + return nvme_c2h(n, list, sizeof(list), req); +} + static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns; @@ -5744,6 +5764,8 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) return nvme_identify_nslist(n, req, false); case NVME_ID_CNS_CS_NS_ACTIVE_LIST: return nvme_identify_nslist_csi(n, req, true); + case NVME_ID_CNS_ENDURANCE_GROUP_LIST: + return nvme_endurance_group_list(n, req); case NVME_ID_CNS_CS_NS_PRESENT_LIST: return nvme_identify_nslist_csi(n, req, false); case NVME_ID_CNS_NS_DESCR_LIST: diff --git a/include/block/nvme.h b/include/block/nvme.h index bb231d0b9a..7c77d38174 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1074,6 +1074,7 @@ enum NvmeIdCns { NVME_ID_CNS_CTRL_LIST = 0x13, NVME_ID_CNS_PRIMARY_CTRL_CAP = 0x14, NVME_ID_CNS_SECONDARY_CTRL_LIST = 0x15, + NVME_ID_CNS_ENDURANCE_GROUP_LIST = 0x19, NVME_ID_CNS_CS_NS_PRESENT_LIST = 0x1a, NVME_ID_CNS_CS_NS_PRESENT = 0x1b, NVME_ID_CNS_IO_COMMAND_SET = 0x1c, From 1a494d119abb57e835f1230f4524f1eb67eb83e9 Mon Sep 17 00:00:00 2001 From: Minwoo Im <minwoo.im@samsung.com> Date: Wed, 29 May 2024 21:42:32 +0900 Subject: [PATCH 2090/2884] hw/nvme: separate identify data for sec. ctrl list Secondary controller list for virtualization has been managed by Identify Secondary Controller List data structure with NvmeSecCtrlList where up to 127 secondary controller entries can be managed. The problem hasn't arisen so far because NVME_MAX_VFS has been 127. This patch separated identify data itself from the actual secondary controller list managed by controller to support more than 127 secondary controllers with the following patch. This patch reused NvmeSecCtrlEntry structure to manage all the possible secondary controllers, and copy entries to identify data structure when the command comes in. Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Minwoo Im <minwoo.im@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 21 ++++++++++----------- hw/nvme/nvme.h | 14 ++++++++------ hw/nvme/subsys.c | 8 ++++---- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 50f8cc90b0..8a838e5b65 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -219,7 +219,6 @@ #define NVME_TEMPERATURE_CRITICAL 0x175 #define NVME_NUM_FW_SLOTS 1 #define NVME_DEFAULT_MAX_ZA_SIZE (128 * KiB) -#define NVME_MAX_VFS 127 #define NVME_VF_RES_GRANULARITY 1 #define NVME_VF_OFFSET 0x1 #define NVME_VF_STRIDE 1 @@ -5480,14 +5479,14 @@ static uint16_t nvme_identify_sec_ctrl_list(NvmeCtrl *n, NvmeRequest *req) NvmeIdentify *c = (NvmeIdentify *)&req->cmd; uint16_t pri_ctrl_id = le16_to_cpu(n->pri_ctrl_cap.cntlid); uint16_t min_id = le16_to_cpu(c->ctrlid); - uint8_t num_sec_ctrl = n->sec_ctrl_list.numcntl; + uint8_t num_sec_ctrl = n->nr_sec_ctrls; NvmeSecCtrlList list = {0}; uint8_t i; for (i = 0; i < num_sec_ctrl; i++) { - if (n->sec_ctrl_list.sec[i].scid >= min_id) { - list.numcntl = num_sec_ctrl - i; - memcpy(&list.sec, n->sec_ctrl_list.sec + i, + if (n->sec_ctrl_list[i].scid >= min_id) { + list.numcntl = MIN(num_sec_ctrl - i, 127); + memcpy(&list.sec, n->sec_ctrl_list + i, list.numcntl * sizeof(NvmeSecCtrlEntry)); break; } @@ -7144,8 +7143,8 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) if (n->params.sriov_max_vfs) { if (!pci_is_vf(pci_dev)) { - for (i = 0; i < n->sec_ctrl_list.numcntl; i++) { - sctrl = &n->sec_ctrl_list.sec[i]; + for (i = 0; i < n->nr_sec_ctrls; i++) { + sctrl = &n->sec_ctrl_list[i]; nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); } } @@ -7939,7 +7938,7 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) static void nvme_init_state(NvmeCtrl *n) { NvmePriCtrlCap *cap = &n->pri_ctrl_cap; - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; NvmeSecCtrlEntry *sctrl; PCIDevice *pci = PCI_DEVICE(n); uint8_t max_vfs; @@ -7964,9 +7963,9 @@ static void nvme_init_state(NvmeCtrl *n) n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); QTAILQ_INIT(&n->aer_queue); - list->numcntl = max_vfs; + n->nr_sec_ctrls = max_vfs; for (i = 0; i < max_vfs; i++) { - sctrl = &list->sec[i]; + sctrl = &list[i]; sctrl->pcid = cpu_to_le16(n->cntlid); sctrl->vfn = cpu_to_le16(i + 1); } @@ -8559,7 +8558,7 @@ static void nvme_sriov_post_write_config(PCIDevice *dev, uint16_t old_num_vfs) int i; for (i = pcie_sriov_num_vfs(dev); i < old_num_vfs; i++) { - sctrl = &n->sec_ctrl_list.sec[i]; + sctrl = &n->sec_ctrl_list[i]; nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); } } diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 2e7d31c0ae..9da5343ffe 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -26,6 +26,7 @@ #define NVME_MAX_CONTROLLERS 256 #define NVME_MAX_NAMESPACES 256 +#define NVME_MAX_VFS 127 #define NVME_EUI64_DEFAULT ((uint64_t)0x5254000000000000) #define NVME_FDP_MAX_EVENTS 63 #define NVME_FDP_MAXPIDS 128 @@ -613,7 +614,8 @@ typedef struct NvmeCtrl { } features; NvmePriCtrlCap pri_ctrl_cap; - NvmeSecCtrlList sec_ctrl_list; + uint32_t nr_sec_ctrls; + NvmeSecCtrlEntry sec_ctrl_list[NVME_MAX_VFS]; struct { uint16_t vqrfap; uint16_t virfap; @@ -663,7 +665,7 @@ static inline NvmeSecCtrlEntry *nvme_sctrl(NvmeCtrl *n) NvmeCtrl *pf = NVME(pcie_sriov_get_pf(pci_dev)); if (pci_is_vf(pci_dev)) { - return &pf->sec_ctrl_list.sec[pcie_sriov_vf_number(pci_dev)]; + return &pf->sec_ctrl_list[pcie_sriov_vf_number(pci_dev)]; } return NULL; @@ -672,12 +674,12 @@ static inline NvmeSecCtrlEntry *nvme_sctrl(NvmeCtrl *n) static inline NvmeSecCtrlEntry *nvme_sctrl_for_cntlid(NvmeCtrl *n, uint16_t cntlid) { - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; uint8_t i; - for (i = 0; i < list->numcntl; i++) { - if (le16_to_cpu(list->sec[i].scid) == cntlid) { - return &list->sec[i]; + for (i = 0; i < n->nr_sec_ctrls; i++) { + if (le16_to_cpu(list[i].scid) == cntlid) { + return &list[i]; } } diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index d30bb8bfd5..561ed04a53 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -17,13 +17,13 @@ static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num) { NvmeSubsystem *subsys = n->subsys; - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; NvmeSecCtrlEntry *sctrl; int i, cnt = 0; for (i = start; i < ARRAY_SIZE(subsys->ctrls) && cnt < num; i++) { if (!subsys->ctrls[i]) { - sctrl = &list->sec[cnt]; + sctrl = &list[cnt]; sctrl->scid = cpu_to_le16(i); subsys->ctrls[i] = SUBSYS_SLOT_RSVD; cnt++; @@ -36,12 +36,12 @@ static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num) static void nvme_subsys_unreserve_cntlids(NvmeCtrl *n) { NvmeSubsystem *subsys = n->subsys; - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; NvmeSecCtrlEntry *sctrl; int i, cntlid; for (i = 0; i < n->params.sriov_max_vfs; i++) { - sctrl = &list->sec[i]; + sctrl = &list[i]; cntlid = le16_to_cpu(sctrl->scid); if (cntlid) { From c6159d0e384f4176e69555a9bae37ac21fe69b57 Mon Sep 17 00:00:00 2001 From: Minwoo Im <minwoo.im@samsung.com> Date: Wed, 29 May 2024 21:42:33 +0900 Subject: [PATCH 2091/2884] hw/nvme: Allocate sec-ctrl-list as a dynamic array To prevent further bumping up the number of maximum VF te support, this patch allocates a dynamic array (NvmeCtrl *)->sec_ctrl_list based on number of VF supported by sriov_max_vfs property. Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Minwoo Im <minwoo.im@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 8 +------- hw/nvme/nvme.h | 5 ++--- hw/nvme/subsys.c | 2 ++ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 8a838e5b65..1e50b57707 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -7868,12 +7868,6 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) return false; } - if (params->sriov_max_vfs > NVME_MAX_VFS) { - error_setg(errp, "sriov_max_vfs must be between 0 and %d", - NVME_MAX_VFS); - return false; - } - if (params->cmb_size_mb) { error_setg(errp, "CMB is not supported with SR-IOV"); return false; @@ -8485,7 +8479,7 @@ static Property nvme_props[] = { DEFINE_PROP_UINT8("zoned.zasl", NvmeCtrl, params.zasl, 0), DEFINE_PROP_BOOL("zoned.auto_transition", NvmeCtrl, params.auto_transition_zones, true), - DEFINE_PROP_UINT8("sriov_max_vfs", NvmeCtrl, params.sriov_max_vfs, 0), + DEFINE_PROP_UINT16("sriov_max_vfs", NvmeCtrl, params.sriov_max_vfs, 0), DEFINE_PROP_UINT16("sriov_vq_flexible", NvmeCtrl, params.sriov_vq_flexible, 0), DEFINE_PROP_UINT16("sriov_vi_flexible", NvmeCtrl, diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 9da5343ffe..180df26cce 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -26,7 +26,6 @@ #define NVME_MAX_CONTROLLERS 256 #define NVME_MAX_NAMESPACES 256 -#define NVME_MAX_VFS 127 #define NVME_EUI64_DEFAULT ((uint64_t)0x5254000000000000) #define NVME_FDP_MAX_EVENTS 63 #define NVME_FDP_MAXPIDS 128 @@ -533,7 +532,7 @@ typedef struct NvmeParams { bool auto_transition_zones; bool legacy_cmb; bool ioeventfd; - uint8_t sriov_max_vfs; + uint16_t sriov_max_vfs; uint16_t sriov_vq_flexible; uint16_t sriov_vi_flexible; uint8_t sriov_max_vq_per_vf; @@ -615,7 +614,7 @@ typedef struct NvmeCtrl { NvmePriCtrlCap pri_ctrl_cap; uint32_t nr_sec_ctrls; - NvmeSecCtrlEntry sec_ctrl_list[NVME_MAX_VFS]; + NvmeSecCtrlEntry *sec_ctrl_list; struct { uint16_t vqrfap; uint16_t virfap; diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 561ed04a53..77deaf2c2c 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -61,6 +61,8 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) if (pci_is_vf(&n->parent_obj)) { cntlid = le16_to_cpu(sctrl->scid); } else { + n->sec_ctrl_list = g_new0(NvmeSecCtrlEntry, num_vfs); + for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { if (!subsys->ctrls[cntlid]) { break; From 15ef124c93a4d4ba6b98b55492e3a1b3297248b0 Mon Sep 17 00:00:00 2001 From: Minwoo Im <minwoo.im@samsung.com> Date: Wed, 29 May 2024 21:42:34 +0900 Subject: [PATCH 2092/2884] hw/nvme: Expand VI/VQ resource to uint32 VI and VQ resources cover queue resources in each VFs in SR-IOV. Current maximum I/O queue pair size is 0xffff, we can expand them to cover the full number of I/O queue pairs. This patch also fixed Identify Secondary Controller List overflow due to expand of number of secondary controllers. Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Minwoo Im <minwoo.im@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 8 ++++---- hw/nvme/nvme.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 1e50b57707..5b1b0cabcf 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8484,10 +8484,10 @@ static Property nvme_props[] = { params.sriov_vq_flexible, 0), DEFINE_PROP_UINT16("sriov_vi_flexible", NvmeCtrl, params.sriov_vi_flexible, 0), - DEFINE_PROP_UINT8("sriov_max_vi_per_vf", NvmeCtrl, - params.sriov_max_vi_per_vf, 0), - DEFINE_PROP_UINT8("sriov_max_vq_per_vf", NvmeCtrl, - params.sriov_max_vq_per_vf, 0), + DEFINE_PROP_UINT32("sriov_max_vi_per_vf", NvmeCtrl, + params.sriov_max_vi_per_vf, 0), + DEFINE_PROP_UINT32("sriov_max_vq_per_vf", NvmeCtrl, + params.sriov_max_vq_per_vf, 0), DEFINE_PROP_BOOL("msix-exclusive-bar", NvmeCtrl, params.msix_exclusive_bar, false), DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff), diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 180df26cce..781985754d 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -535,8 +535,8 @@ typedef struct NvmeParams { uint16_t sriov_max_vfs; uint16_t sriov_vq_flexible; uint16_t sriov_vi_flexible; - uint8_t sriov_max_vq_per_vf; - uint8_t sriov_max_vi_per_vf; + uint32_t sriov_max_vq_per_vf; + uint32_t sriov_max_vi_per_vf; bool msix_exclusive_bar; } NvmeParams; From d01a6fffa9e5e605cde216733948f97beb01bdb1 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini <sstabellini@kernel.org> Date: Wed, 10 Jul 2024 13:28:52 -0700 Subject: [PATCH 2093/2884] MAINTAINERS: add Edgar as Xen maintainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Edgar as Xen subsystem maintainer in QEMU. Edgar has been a QEMU maintainer for years, and has already made key changes to one of the most difficult areas of the Xen subsystem (the mapcache). Edgar volunteered helping us maintain the Xen subsystem in QEMU and we are very happy to welcome him to the team. His knowledge and expertise with QEMU internals will be of great help. Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> Reviewed-by: Paul Durrant <paul@xen.org> Acked-by: Anthony PERARD <anthony@xenproject.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6725913c8b..63e11095a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -536,6 +536,7 @@ X86 Xen CPUs M: Stefano Stabellini <sstabellini@kernel.org> M: Anthony PERARD <anthony@xenproject.org> M: Paul Durrant <paul@xen.org> +M: Edgar E. Iglesias <edgar.iglesias@gmail.com> L: xen-devel@lists.xenproject.org S: Supported F: */xen* From 596ccccdbfa124adb42be8c2faf0c74f4849c7a6 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Date: Tue, 2 Jul 2024 00:44:20 +0200 Subject: [PATCH 2094/2884] physmem: Bail out qemu_ram_block_from_host() for invalid ram addrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bail out in qemu_ram_block_from_host() when xen_ram_addr_from_mapcache() does not find an existing mapping. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- system/physmem.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/physmem.c b/system/physmem.c index 14aa025d41..2154432cb6 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2277,6 +2277,10 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, ram_addr_t ram_addr; RCU_READ_LOCK_GUARD(); ram_addr = xen_ram_addr_from_mapcache(ptr); + if (ram_addr == RAM_ADDR_INVALID) { + return NULL; + } + block = qemu_get_ram_block(ram_addr); if (block) { *offset = ram_addr - block->offset; From 872cb9cced796e75d4f719c31d70ed5fd629efca Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Date: Tue, 2 Jul 2024 00:44:21 +0200 Subject: [PATCH 2095/2884] xen: mapcache: Fix unmapping of first entries in buckets This fixes the clobbering of the entry->next pointer when unmapping the first entry in a bucket of a mapcache. Fixes: 123acd816d ("xen: mapcache: Unmap first entries in buckets") Reported-by: Anthony PERARD <anthony.perard@vates.tech> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Anthony PERARD <anthony.perard@vates.tech> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- hw/xen/xen-mapcache.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index 5f23b0adbe..18ba7b1d8f 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -597,7 +597,17 @@ static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, pentry->next = entry->next; g_free(entry); } else { - memset(entry, 0, sizeof *entry); + /* + * Invalidate mapping but keep entry->next pointing to the rest + * of the list. + * + * Note that lock is already zero here, otherwise we don't unmap. + */ + entry->paddr_index = 0; + entry->vaddr_base = NULL; + entry->valid_mapping = NULL; + entry->flags = 0; + entry->size = 0; } } From 5e21b1317f900a574d85e0890ba156078964ba2c Mon Sep 17 00:00:00 2001 From: Dmitry Frolov <frolov@swemel.ru> Date: Fri, 28 Jun 2024 15:39:10 +0300 Subject: [PATCH 2096/2884] hw/loongarch/boot.c: fix out-of-bound reading memcpy() is trying to READ 512 bytes from memory, pointed by info->kernel_cmdline, which was (presumable) allocated by g_strdup(""); Found with ASAN, making check with enabled sanitizers. Signed-off-by: Dmitry Frolov <frolov@swemel.ru> Reviewed-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240628123910.577740-1-frolov@swemel.ru> Signed-off-by: Song Gao <gaosong@loongson.cn> --- hw/loongarch/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index b8e1aa18d5..cb668703bd 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -163,7 +163,7 @@ static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) info->a0 = 1; info->a1 = cmdline_addr; - memcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE); + g_strlcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE); } static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) From 0aca736433dcb56f365fe8a18ed1b969ca247304 Mon Sep 17 00:00:00 2001 From: Xianglai Li <lixianglai@loongson.cn> Date: Mon, 24 Jun 2024 11:23:00 +0800 Subject: [PATCH 2097/2884] hw/loongarch: Change the tpm support by default Add devices that support tpm by default, Fixed incomplete tpm acpi table information. Signed-off-by: Xianglai Li <lixianglai@loongson.cn> Reviewed-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240624032300.999157-1-lixianglai@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> --- hw/loongarch/Kconfig | 1 + hw/loongarch/acpi-build.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 90a0dba9d5..89be737726 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -8,6 +8,7 @@ config LOONGARCH_VIRT imply VIRTIO_VGA imply PCI_DEVICES imply NVDIMM + imply TPM_TIS_SYSBUS select SERIAL select VIRTIO_PCI select PLATFORM_BUS diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index af45ce526d..72bfc35ae6 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -646,6 +646,9 @@ void loongarch_acpi_setup(LoongArchVirtMachineState *lvms) build_state, tables.rsdp, ACPI_BUILD_RSDP_FILE); + fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, + acpi_data_len(tables.tcpalog)); + qemu_register_reset(acpi_build_reset, build_state); acpi_build_reset(build_state); vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); From 5efbc384c6dee9f4960e5ef05be84af370cef6bb Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Wed, 12 Jun 2024 11:36:37 +0800 Subject: [PATCH 2098/2884] hw/loongarch/virt: Remove unused assignment There is abuse usage about local variable gap. Remove duplicated assignment and solve Coverity reported error. Resolves: Coverity CID 1546441 Fixes: 3cc451cbce ("hw/loongarch: Refine fwcfg memory map") Signed-off-by: Bibo Mao <maobibo@loongson.cn> Reviewed-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240612033637.167787-1-maobibo@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> --- hw/loongarch/virt.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 8be2d2ff6a..e592b1b6b7 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1054,7 +1054,6 @@ static void fw_cfg_add_memory(MachineState *ms) memmap_add_entry(base, gap, 1); size -= gap; base = VIRT_HIGHMEM_BASE; - gap = ram_size - VIRT_LOWMEM_SIZE; } if (size) { @@ -1067,17 +1066,17 @@ static void fw_cfg_add_memory(MachineState *ms) } /* add fw_cfg memory map of other nodes */ - size = ram_size - numa_info[0].node_mem; - gap = VIRT_LOWMEM_BASE + VIRT_LOWMEM_SIZE; - if (base < gap && (base + size) > gap) { + if (numa_info[0].node_mem < gap && ram_size > gap) { /* * memory map for the maining nodes splited into two part - * lowram: [base, +(gap - base)) - * highram: [VIRT_HIGHMEM_BASE, +(size - (gap - base))) + * lowram: [base, +(gap - numa_info[0].node_mem)) + * highram: [VIRT_HIGHMEM_BASE, +(ram_size - gap)) */ - memmap_add_entry(base, gap - base, 1); - size -= gap - base; + memmap_add_entry(base, gap - numa_info[0].node_mem, 1); + size = ram_size - gap; base = VIRT_HIGHMEM_BASE; + } else { + size = ram_size - numa_info[0].node_mem; } if (size) From 36ff178716054d36ca2a61cdf3452514bd95220b Mon Sep 17 00:00:00 2001 From: Jiaxun Yang <jiaxun.yang@flygoat.com> Date: Thu, 27 Jun 2024 05:13:28 +0100 Subject: [PATCH 2099/2884] MAINTAINERS: Add myself as a reviewer of LoongArch virt machine I would like to be informed on changes made to the LoongArch virt machine. I'm fairly familiar with Loongson-3 series platform hardware and doing firmwre (U-Boot) development as hobbyist on LoongArch virt platform, so I believe I can give positive review input to changes on that machine. Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Reviewed-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240627-ipi-fixes-v1-2-9b061dc28a3a@flygoat.com> Signed-off-by: Song Gao <gaosong@loongson.cn> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6725913c8b..41bece23c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1240,6 +1240,7 @@ LoongArch Machines ------------------ Virt M: Song Gao <gaosong@loongson.cn> +R: Jiaxun Yang <jiaxun.yang@flygoat.com> S: Maintained F: docs/system/loongarch/virt.rst F: configs/targets/loongarch64-softmmu.mak From d38e31ef740d102020d1b14b5a2becd4336b462d Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Fri, 7 Jun 2024 11:50:16 +0800 Subject: [PATCH 2100/2884] target/loongarch/kvm: Add software breakpoint support With KVM virtualization, debug exception is injected to guest kernel rather than host for normal break intruction. Here hypercall instruction with special code is used for sw breakpoint usage, and detailed instruction comes from kvm kernel with user API KVM_REG_LOONGARCH_DEBUG_INST. Now only software breakpoint is supported, and it is allowed to insert/remove software breakpoint. We can debug guest kernel with gdb method after kernel is loaded, hardware breakpoint will be added in later. Signed-off-by: Bibo Mao <maobibo@loongson.cn> Reviewed-by: Song Gao <gaosong@loongson.cn> Tested-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240607035016.2975799-1-maobibo@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> --- configs/targets/loongarch64-softmmu.mak | 1 + target/loongarch/kvm/kvm.c | 76 +++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index 84beb19b90..65b65e0c34 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -1,5 +1,6 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml # all boards require libfdt diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 8e6e27c8bf..e1be6a6959 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -28,6 +28,7 @@ #include "trace.h" static bool cap_has_mp_state; +static unsigned int brk_insn; const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -664,7 +665,14 @@ static void kvm_loongarch_vm_stage_change(void *opaque, bool running, int kvm_arch_init_vcpu(CPUState *cs) { + uint64_t val; + qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + + if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { + brk_insn = val; + } + return 0; } @@ -739,6 +747,67 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cs) return true; } +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) +{ + if (kvm_sw_breakpoints_active(cpu)) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + } +} + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { + error_report("%s failed", __func__); + return -EINVAL; + } + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + static uint32_t brk; + + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) || + brk != brk_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + error_report("%s failed", __func__); + return -EINVAL; + } + return 0; +} + +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + return -ENOSYS; +} + +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + return -ENOSYS; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ +} + +static bool kvm_loongarch_handle_debug(CPUState *cs, struct kvm_run *run) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + + kvm_cpu_synchronize_state(cs); + if (cs->singlestep_enabled) { + return true; + } + + if (kvm_find_sw_breakpoint(cs, env->pc)) { + return true; + } + + return false; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { int ret = 0; @@ -757,6 +826,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) run->iocsr_io.len, run->iocsr_io.is_write); break; + + case KVM_EXIT_DEBUG: + if (kvm_loongarch_handle_debug(cs, run)) { + ret = EXCP_DEBUG; + } + break; + default: ret = -1; warn_report("KVM: unknown exit reason %d", run->exit_reason); From 785875874da1f419c09016b0fcea5e756caaa00c Mon Sep 17 00:00:00 2001 From: Feiyang Chen <chris.chenfeiyang@gmail.com> Date: Fri, 28 Jun 2024 13:33:57 +1000 Subject: [PATCH 2101/2884] target/loongarch: Remove avail_64 in trans_srai_w() and simplify it Since srai.w is a valid instruction on la32, remove the avail_64 check and simplify trans_srai_w(). Fixes: c0c0461e3a06 ("target/loongarch: Add avail_64 to check la64-only instructions") Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Feiyang Chen <chris.chenfeiyang@gmail.com> Message-Id: <20240628033357.50027-1-chris.chenfeiyang@gmail.com> Signed-off-by: Song Gao <gaosong@loongson.cn> --- target/loongarch/tcg/insn_trans/trans_shift.c.inc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc index 2f4bd6ff28..377307785a 100644 --- a/target/loongarch/tcg/insn_trans/trans_shift.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -67,19 +67,9 @@ static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) tcg_gen_rotr_tl(dest, src1, t0); } -static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) +static void gen_sari_w(TCGv dest, TCGv src1, target_long imm) { - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); - - if (!avail_64(ctx)) { - return false; - } - - tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; + tcg_gen_sextract_tl(dest, src1, imm, 32 - imm); } TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) @@ -94,6 +84,7 @@ TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) +TRANS(srai_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w) TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) From bba1c36da0589b4179c16e0895256ca731023f2c Mon Sep 17 00:00:00 2001 From: Song Gao <gaosong@loongson.cn> Date: Fri, 5 Jul 2024 10:18:38 +0800 Subject: [PATCH 2102/2884] target/loongarch: Set CSR_PRCFG1 and CSR_PRCFG2 values We set the value of register CSR_PRCFG3, but left out CSR_PRCFG1 and CSR_PRCFG2. Set CSR_PRCFG1 and CSR_PRCFG2 according to the default values of the physical machine. Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Message-Id: <20240705021839.1004374-1-gaosong@loongson.cn> --- target/loongarch/cpu.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 69f9ad7711..61af018eec 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -457,6 +457,18 @@ static void loongarch_la464_initfn(Object *obj) env->cpucfg[20] = data; env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); + + env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8); + env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f); + env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7); + + env->CSR_PRCFG2 = 0x3ffff000; + + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); + loongarch_cpu_post_init(obj); } @@ -538,11 +550,6 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); env->CSR_TID = cs->cpu_index; - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); - for (n = 0; n < 4; n++) { env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0); From 3ef4b21a5c767ff0b15047e709762abef490ad07 Mon Sep 17 00:00:00 2001 From: Song Gao <gaosong@loongson.cn> Date: Fri, 5 Jul 2024 10:18:39 +0800 Subject: [PATCH 2103/2884] target/loongarch: Fix cpu_reset set wrong CSR_CRMD After cpu_reset, DATF in CSR_CRMD is 0, DATM is 0. See the manual[1] 6.4. [1]: https://github.com/loongson/LoongArch-Documentation/releases/download/2023.04.20/LoongArch-Vol1-v1.10-EN.pdf Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Message-Id: <20240705021839.1004374-2-gaosong@loongson.cn> --- target/loongarch/cpu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 61af018eec..5e85b9dbef 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -523,13 +523,13 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) env->fcsr0 = 0x0; int n; - /* Set csr registers value after reset */ + /* Set csr registers value after reset, see the manual 6.4. */ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 0); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 0); env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0); env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0); From 84e327e8448eacef879a0f85d38d212b6f58ee4a Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 19:22:27 -0400 Subject: [PATCH 2104/2884] python: linter changes for pylint 3.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New bleeding edge versions, new nits to iron out. This addresses the 'check-python-tox' optional GitLab test, while 'check-python-minreqs' saw no regressions, since it's frozen on an older version of pylint. Fixes: qemu/machine/machine.py:345:52: E0606: Possibly using variable 'sock' before assignment (possibly-used-before-assignment) qemu/utils/qemu_ga_client.py:168:4: R1711: Useless return at end of function or method (useless-return) Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240626232230.408004-2-jsnow@redhat.com Signed-off-by: John Snow <jsnow@redhat.com> --- python/qemu/machine/machine.py | 1 + python/qemu/utils/qemu_ga_client.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index f648f6af45..ebb58d5b68 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -335,6 +335,7 @@ class QEMUMachine: def _pre_launch(self) -> None: if self._qmp_set: + sock = None if self._monitor_address is None: self._sock_pair = socket.socketpair() os.set_inheritable(self._sock_pair[0].fileno(), True) diff --git a/python/qemu/utils/qemu_ga_client.py b/python/qemu/utils/qemu_ga_client.py index 9a665e6e99..cf0fcf9a8b 100644 --- a/python/qemu/utils/qemu_ga_client.py +++ b/python/qemu/utils/qemu_ga_client.py @@ -174,7 +174,7 @@ class QemuGuestAgentClient: # On error exception will raise except asyncio.TimeoutError: # On success command will timed out - return + pass def shutdown(self, mode: str = 'powerdown') -> None: if mode not in ['powerdown', 'halt', 'reboot']: From c5be244534f918adf5aea0ced9cf13344be3e62b Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 19:22:28 -0400 Subject: [PATCH 2105/2884] python: Do not use pylint 3.2.4 with python 3.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a bug in this version, see: https://github.com/pylint-dev/pylint/issues/9751 Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240626232230.408004-3-jsnow@redhat.com Signed-off-by: John Snow <jsnow@redhat.com> --- python/setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/python/setup.cfg b/python/setup.cfg index 48668609d3..8ebd345d7e 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -41,6 +41,7 @@ devel = isort >= 5.1.2 mypy >= 1.4.0 pylint >= 2.17.3 + pylint != 3.2.4; python_version<"3.9" tox >= 3.18.0 urwid >= 2.1.2 urwid-readline >= 0.13 From e38900450fa900828cd7d5bc84c3a6a8af693696 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 19:22:29 -0400 Subject: [PATCH 2106/2884] iotests: Change imports for Python 3.13 Python 3.13 isn't out yet, but it's in beta and Fedora is ramping up to make it the default system interpreter for Fedora 41. They moved our cheese for where ContextManager lives; add a conditional to locate it while we support both pre-3.9 and 3.13+. Signed-off-by: John Snow <jsnow@redhat.com> Message-id: 20240626232230.408004-4-jsnow@redhat.com Signed-off-by: John Snow <jsnow@redhat.com> --- tests/qemu-iotests/testenv.py | 7 ++++++- tests/qemu-iotests/testrunner.py | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 588f30a4f1..96d69e5696 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -25,7 +25,12 @@ import collections import random import subprocess import glob -from typing import List, Dict, Any, Optional, ContextManager +from typing import List, Dict, Any, Optional + +if sys.version_info >= (3, 9): + from contextlib import AbstractContextManager as ContextManager +else: + from typing import ContextManager DEF_GDB_OPTIONS = 'localhost:12345' diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 7b322272e9..2e236c8fa3 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -27,11 +27,14 @@ import json import shutil import sys from multiprocessing import Pool -from typing import List, Optional, Any, Sequence, Dict, \ - ContextManager - +from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv +if sys.version_info >= (3, 9): + from contextlib import AbstractContextManager as ContextManager +else: + from typing import ContextManager + def silent_unlink(path: Path) -> None: try: From 45b14be9b63ac3df9f2b31c249fc4520837c00b9 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 26 Jun 2024 19:22:30 -0400 Subject: [PATCH 2107/2884] python: enable testing for 3.13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.13 is in beta and Fedora 41 is preparing to make it the default system interpreter; enable testing for it. (In the event problems develop prior to release, it should only impact the check-python-tox job, which is not run by default and is allowed to fail.) Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240626232230.408004-5-jsnow@redhat.com Signed-off-by: John Snow <jsnow@redhat.com> --- python/setup.cfg | 3 ++- tests/docker/dockerfiles/python.docker | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python/setup.cfg b/python/setup.cfg index 8ebd345d7e..3b4e2cc550 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -19,6 +19,7 @@ classifiers = Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Typing :: Typed [options] @@ -184,7 +185,7 @@ multi_line_output=3 # of python available on your system to run this test. [tox:tox] -envlist = py38, py39, py310, py311, py312 +envlist = py38, py39, py310, py311, py312, py313 skip_missing_interpreters = true [testenv] diff --git a/tests/docker/dockerfiles/python.docker b/tests/docker/dockerfiles/python.docker index a3c1321190..8f0af9ef25 100644 --- a/tests/docker/dockerfiles/python.docker +++ b/tests/docker/dockerfiles/python.docker @@ -14,6 +14,7 @@ ENV PACKAGES \ python3.10 \ python3.11 \ python3.12 \ + python3.13 \ python3.8 \ python3.9 From fe791b7fcc760cd332cfabe5a328bc63e0437aa4 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 3 Jul 2024 13:52:34 -0400 Subject: [PATCH 2108/2884] Python: bump minimum sphinx version to 3.4.3 With RHEL 8 support retired (It's been two years since RHEL9 released), our very oldest build platform version of Sphinx is now 3.4.3; and keeping backwards compatibility for versions as old as v1.6 when using domain extensions is a lot of work we don't need to do. This patch is motivated by my work creating a new QAPI domain, which unlike the dbus documentation, cannot be allowed to regress by creating a "dummy" doc when operating under older sphinx versions. Easier is to raise our minimum version as far as we can push it forwards, reducing my burden in creating cross-compatibility hacks and patches. A sampling of sphinx versions from various distributions, courtesy https://repology.org/project/python:sphinx/versions Alpine 3.16: v4.3.0 (QEMU support ended 2024-05-23) Alpine 3.17: v5.3.0 Alpine 3.18: v6.1.3 Alpine 3.19: v6.2.1 Ubuntu 20.04 LTS: EOL Ubuntu 22.04 LTS: v4.3.2 Ubuntu 22.10: EOL Ubuntu 23.04: EOL Ubuntu 23.10: v5.3.0 Ubuntu 24.04 LTS: v7.2.6 Debian 11: v3.4.3 (QEMU support ends 2024-07-xx) Debian 12: v5.3.0 Fedora 38: EOL Fedora 39: v6.2.1 Fedora 40: v7.2.6 CentOS Stream 8: v1.7.6 (QEMU support ended 2024-05-17) CentOS Stream 9: v3.4.3 OpenSUSE Leap 15.4: EOL OpenSUSE Leap 15.5: 2.3.1, 4.2.0 and 7.2.6 RHEL9 / CentOS Stream 9 becomes the new defining factor in staying at Sphinx 3.4.3 due to downstream offline build requirements that force us to use platform Sphinx instead of newer packages from PyPI. Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-id: 20240703175235.239004-2-jsnow@redhat.com Signed-off-by: John Snow <jsnow@redhat.com> --- docs/conf.py | 7 +++---- pythondeps.toml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index aae0304ac6..876f676881 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -53,10 +53,9 @@ sys.path.insert(0, os.path.join(qemu_docdir, "../scripts")) # If your documentation needs a minimal Sphinx version, state it here. # -# Sphinx 1.5 and earlier can't build our docs because they are too -# picky about the syntax of the argument to the option:: directive -# (see Sphinx bugs #646, #3366). -needs_sphinx = '1.6' +# 3.4.3 is the oldest version of Sphinx that ships on a platform we +# pledge build support for. +needs_sphinx = '3.4.3' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom diff --git a/pythondeps.toml b/pythondeps.toml index 6aba0c9daa..f6e590fdd8 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -23,7 +23,7 @@ meson = { accepted = ">=1.1.0", installed = "1.2.3", canary = "meson" } [docs] # Please keep the installed versions in sync with docs/requirements.txt -sphinx = { accepted = ">=1.6", installed = "5.3.0", canary = "sphinx-build" } +sphinx = { accepted = ">=3.4.3", installed = "5.3.0", canary = "sphinx-build" } sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" } [avocado] From dd23f9ec519db9c424223cff8767715de5532718 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Wed, 3 Jul 2024 13:52:35 -0400 Subject: [PATCH 2109/2884] docs: remove Sphinx 1.x compatibility code In general, the Use_SSI workaround is no longer needed, and neither is the pre-1.6 logging shim for kerneldoc. Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-id: 20240703175235.239004-3-jsnow@redhat.com [rebased on top of origin/master. --js] Signed-off-by: John Snow <jsnow@redhat.com> --- docs/sphinx/hxtool.py | 21 ++++----------------- docs/sphinx/kerneldoc.py | 38 ++++++++++++-------------------------- docs/sphinx/kernellog.py | 28 ---------------------------- docs/sphinx/qapidoc.py | 33 +++------------------------------ 4 files changed, 19 insertions(+), 101 deletions(-) delete mode 100644 docs/sphinx/kernellog.py diff --git a/docs/sphinx/hxtool.py b/docs/sphinx/hxtool.py index 3729084a36..a84723be19 100644 --- a/docs/sphinx/hxtool.py +++ b/docs/sphinx/hxtool.py @@ -24,16 +24,10 @@ from docutils import nodes from docutils.statemachine import ViewList from docutils.parsers.rst import directives, Directive from sphinx.errors import ExtensionError +from sphinx.util.docutils import switch_source_input from sphinx.util.nodes import nested_parse_with_titles import sphinx -# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later -# use switch_source_input. Check borrowed from kerneldoc.py. -Use_SSI = sphinx.__version__[:3] >= '1.7' -if Use_SSI: - from sphinx.util.docutils import switch_source_input -else: - from sphinx.ext.autodoc import AutodocReporter __version__ = '1.0' @@ -185,16 +179,9 @@ class HxtoolDocDirective(Directive): # of title_styles and section_level that kerneldoc.py does, # because nested_parse_with_titles() does that for us. def do_parse(self, result, node): - if Use_SSI: - with switch_source_input(self.state, result): - nested_parse_with_titles(self.state, result, node) - else: - save = self.state.memo.reporter - self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) - try: - nested_parse_with_titles(self.state, result, node) - finally: - self.state.memo.reporter = save + with switch_source_input(self.state, result): + nested_parse_with_titles(self.state, result, node) + def setup(app): """ Register hxtool-doc directive with Sphinx""" diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index 72c403a737..3aa972f2e8 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -38,20 +38,14 @@ from docutils import nodes, statemachine from docutils.statemachine import ViewList from docutils.parsers.rst import directives, Directive -# -# AutodocReporter is only good up to Sphinx 1.7 -# import sphinx +from sphinx.util import logging +from sphinx.util.docutils import switch_source_input -Use_SSI = sphinx.__version__[:3] >= '1.7' -if Use_SSI: - from sphinx.util.docutils import switch_source_input -else: - from sphinx.ext.autodoc import AutodocReporter - -import kernellog __version__ = '1.0' +logger = logging.getLogger('kerneldoc') + class KernelDocDirective(Directive): """Extract kernel-doc comments from the specified file""" @@ -111,8 +105,7 @@ class KernelDocDirective(Directive): cmd += [filename] try: - kernellog.verbose(env.app, - 'calling kernel-doc \'%s\'' % (" ".join(cmd))) + logger.verbose('calling kernel-doc \'%s\'' % (" ".join(cmd))) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() @@ -122,8 +115,10 @@ class KernelDocDirective(Directive): if p.returncode != 0: sys.stderr.write(err) - kernellog.warn(env.app, - 'kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode)) + logger.warning( + 'kernel-doc \'%s\' failed with return code %d' % + (" ".join(cmd), p.returncode) + ) return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] elif env.config.kerneldoc_verbosity > 0: sys.stderr.write(err) @@ -149,22 +144,13 @@ class KernelDocDirective(Directive): return node.children except Exception as e: # pylint: disable=W0703 - kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' % + logger.warning('kernel-doc \'%s\' processing failed with: %s' % (" ".join(cmd), str(e))) return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] def do_parse(self, result, node): - if Use_SSI: - with switch_source_input(self.state, result): - self.state.nested_parse(result, 0, node, match_titles=1) - else: - save = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter - self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) - self.state.memo.title_styles, self.state.memo.section_level = [], 0 - try: - self.state.nested_parse(result, 0, node, match_titles=1) - finally: - self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = save + with switch_source_input(self.state, result): + self.state.nested_parse(result, 0, node, match_titles=1) def setup(app): diff --git a/docs/sphinx/kernellog.py b/docs/sphinx/kernellog.py deleted file mode 100644 index af924f51a7..0000000000 --- a/docs/sphinx/kernellog.py +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Sphinx has deprecated its older logging interface, but the replacement -# only goes back to 1.6. So here's a wrapper layer to keep around for -# as long as we support 1.4. -# -import sphinx - -if sphinx.__version__[:3] >= '1.6': - UseLogging = True - from sphinx.util import logging - logger = logging.getLogger('kerneldoc') -else: - UseLogging = False - -def warn(app, message): - if UseLogging: - logger.warning(message) - else: - app.warn(message) - -def verbose(app, message): - if UseLogging: - logger.verbose(message) - else: - app.verbose(message) - - diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 2b06750a3c..62b39833ca 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -35,22 +35,11 @@ from qapi.error import QAPIError, QAPISemError from qapi.gen import QAPISchemaVisitor from qapi.schema import QAPISchema -import sphinx from sphinx.errors import ExtensionError +from sphinx.util.docutils import switch_source_input from sphinx.util.nodes import nested_parse_with_titles -# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later -# use switch_source_input. Check borrowed from kerneldoc.py. -USE_SSI = sphinx.__version__[:3] >= "1.7" -if USE_SSI: - from sphinx.util.docutils import switch_source_input -else: - from sphinx.ext.autodoc import ( # pylint: disable=no-name-in-module - AutodocReporter, - ) - - __version__ = "1.0" @@ -539,24 +528,8 @@ class QAPIDocDirective(Directive): subheadings (titles) without confusing the rendering of anything else. """ - # This is from kerneldoc.py -- it works around an API change in - # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use - # sphinx.util.nodes.nested_parse_with_titles() rather than the - # plain self.state.nested_parse(), and so we can drop the saving - # of title_styles and section_level that kerneldoc.py does, - # because nested_parse_with_titles() does that for us. - if USE_SSI: - with switch_source_input(self.state, rstlist): - nested_parse_with_titles(self.state, rstlist, node) - else: - save = self.state.memo.reporter - self.state.memo.reporter = AutodocReporter( - rstlist, self.state.memo.reporter - ) - try: - nested_parse_with_titles(self.state, rstlist, node) - finally: - self.state.memo.reporter = save + with switch_source_input(self.state, rstlist): + nested_parse_with_titles(self.state, rstlist, node) def setup(app): From 50475f1511964775ff73c2b07239c3ff571f75cd Mon Sep 17 00:00:00 2001 From: Jeuk Kim <jeuk20.kim@samsung.com> Date: Tue, 9 Jul 2024 20:53:49 +0900 Subject: [PATCH 2110/2884] hw/ufs: Fix mcq register range check logic The function ufs_is_mcq_reg() and ufs_is_mcq_op_reg() only evaluated the range of the mcq_reg and mcq_op_reg offset, which is defined as a constant. Therefore, it was possible for them to return true even though the ufs device is configured to not support the mcq. This could cause ufs_mmio_read()/ufs_mmio_write() to result in Null-pointer-dereference. So fix it. Resolves: #2428 Fixes: 5c079578d2e4 ("hw/ufs: Add support MCQ of UFSHCI 4.0") Reported-by: Zheyu Ma <zheyuma97@gmail.com> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com> Reviewed-by: Minwoo Im <minwoo.im@samsung.com> --- hw/ufs/ufs.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 683fff5840..945a0ea127 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -57,14 +57,26 @@ static inline uint64_t ufs_reg_size(UfsHc *u) static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr, unsigned size) { - uint64_t mcq_reg_addr = ufs_mcq_reg_addr(u, 0); + uint64_t mcq_reg_addr; + + if (!u->params.mcq) { + return false; + } + + mcq_reg_addr = ufs_mcq_reg_addr(u, 0); return (addr >= mcq_reg_addr && addr + size <= mcq_reg_addr + sizeof(u->mcq_reg)); } static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr, unsigned size) { - uint64_t mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0); + uint64_t mcq_op_reg_addr; + + if (!u->params.mcq) { + return false; + } + + mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0); return (addr >= mcq_op_reg_addr && addr + size <= mcq_op_reg_addr + sizeof(u->mcq_op_reg)); } From 9d38d9dca2a81aaf5752d45d221021ef96d496cd Mon Sep 17 00:00:00 2001 From: Michael Roth <michael.roth@amd.com> Date: Tue, 9 Jul 2024 23:10:05 -0500 Subject: [PATCH 2111/2884] i386/sev: Don't allow automatic fallback to legacy KVM_SEV*_INIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if the 'legacy-vm-type' property of the sev-guest object is 'on', QEMU will attempt to use the newer KVM_SEV_INIT2 kernel interface in conjunction with the newer KVM_X86_SEV_VM and KVM_X86_SEV_ES_VM KVM VM types. This can lead to measurement changes if, for instance, an SEV guest was created on a host that originally had an older kernel that didn't support KVM_SEV_INIT2, but is booted on the same host later on after the host kernel was upgraded. Instead, if legacy-vm-type is 'off', QEMU should fail if the KVM_SEV_INIT2 interface is not provided by the current host kernel. Modify the fallback handling accordingly. In the future, VMSA features and other flags might be added to QEMU which will require legacy-vm-type to be 'off' because they will rely on the newer KVM_SEV_INIT2 interface. It may be difficult to convey to users what values of legacy-vm-type are compatible with which features/options, so as part of this rework, switch legacy-vm-type to a tri-state OnOffAuto option. 'auto' in this case will automatically switch to using the newer KVM_SEV_INIT2, but only if it is required to make use of new VMSA features or other options only available via KVM_SEV_INIT2. Defining 'auto' in this way would avoid inadvertantly breaking compatibility with older kernels since it would only be used in cases where users opt into newer features that are only available via KVM_SEV_INIT2 and newer kernels, and provide better default behavior than the legacy-vm-type=off behavior that was previously in place, so make it the default for 9.1+ machine types. Cc: Daniel P. Berrangé <berrange@redhat.com> Cc: Paolo Bonzini <pbonzini@redhat.com> cc: kvm@vger.kernel.org Signed-off-by: Michael Roth <michael.roth@amd.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Link: https://lore.kernel.org/r/20240710041005.83720-1-michael.roth@amd.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/i386/pc.c | 2 +- qapi/qom.json | 18 ++++++---- target/i386/sev.c | 87 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 84 insertions(+), 23 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4fbc577470..c74931d577 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -83,7 +83,7 @@ GlobalProperty pc_compat_9_0[] = { { TYPE_X86_CPU, "x-amd-topoext-features-only", "false" }, { TYPE_X86_CPU, "x-l1-cache-per-thread", "false" }, { TYPE_X86_CPU, "guest-phys-bits", "0" }, - { "sev-guest", "legacy-vm-type", "true" }, + { "sev-guest", "legacy-vm-type", "on" }, { TYPE_X86_CPU, "legacy-multi-node", "on" }, }; const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0); diff --git a/qapi/qom.json b/qapi/qom.json index 8e75a419c3..7eccd2e14e 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -924,12 +924,16 @@ # @handle: SEV firmware handle (default: 0) # # @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating the VM. -# The newer KVM_SEV_INIT2 interface syncs additional vCPU -# state when initializing the VMSA structures, which will -# result in a different guest measurement. Set this to -# maintain compatibility with older QEMU or kernel versions -# that rely on legacy KVM_SEV_INIT behavior. -# (default: false) (since 9.1) +# The newer KVM_SEV_INIT2 interface, from Linux >= 6.10, syncs +# additional vCPU state when initializing the VMSA structures, +# which will result in a different guest measurement. Set +# this to 'on' to force compatibility with older QEMU or kernel +# versions that rely on legacy KVM_SEV_INIT behavior. 'auto' +# will behave identically to 'on', but will automatically +# switch to using KVM_SEV_INIT2 if the user specifies any +# additional options that require it. If set to 'off', QEMU +# will require KVM_SEV_INIT2 unconditionally. +# (default: off) (since 9.1) # # Since: 2.12 ## @@ -939,7 +943,7 @@ '*session-file': 'str', '*policy': 'uint32', '*handle': 'uint32', - '*legacy-vm-type': 'bool' } } + '*legacy-vm-type': 'OnOffAuto' } } ## # @SevSnpGuestProperties: diff --git a/target/i386/sev.c b/target/i386/sev.c index 2ba5f51722..a1157c0ede 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -144,7 +144,7 @@ struct SevGuestState { uint32_t policy; char *dh_cert_file; char *session_file; - bool legacy_vm_type; + OnOffAuto legacy_vm_type; }; struct SevSnpGuestState { @@ -1369,6 +1369,17 @@ sev_vm_state_change(void *opaque, bool running, RunState state) } } +/* + * This helper is to examine sev-guest properties and determine if any options + * have been set which rely on the newer KVM_SEV_INIT2 interface and associated + * KVM VM types. + */ +static bool sev_init2_required(SevGuestState *sev_guest) +{ + /* Currently no KVM_SEV_INIT2-specific options are exposed via QEMU */ + return false; +} + static int sev_kvm_type(X86ConfidentialGuest *cg) { SevCommonState *sev_common = SEV_COMMON(cg); @@ -1379,14 +1390,39 @@ static int sev_kvm_type(X86ConfidentialGuest *cg) goto out; } - kvm_type = (sev_guest->policy & SEV_POLICY_ES) ? - KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; - if (kvm_is_vm_type_supported(kvm_type) && !sev_guest->legacy_vm_type) { - sev_common->kvm_type = kvm_type; - } else { + /* These are the only cases where legacy VM types can be used. */ + if (sev_guest->legacy_vm_type == ON_OFF_AUTO_ON || + (sev_guest->legacy_vm_type == ON_OFF_AUTO_AUTO && + !sev_init2_required(sev_guest))) { sev_common->kvm_type = KVM_X86_DEFAULT_VM; + goto out; } + /* + * Newer VM types are required, either explicitly via legacy-vm-type=on, or + * implicitly via legacy-vm-type=auto along with additional sev-guest + * properties that require the newer VM types. + */ + kvm_type = (sev_guest->policy & SEV_POLICY_ES) ? + KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; + if (!kvm_is_vm_type_supported(kvm_type)) { + if (sev_guest->legacy_vm_type == ON_OFF_AUTO_AUTO) { + error_report("SEV: host kernel does not support requested %s VM type, which is required " + "for the set of options specified. To allow use of the legacy " + "KVM_X86_DEFAULT_VM VM type, please disable any options that are not " + "compatible with the legacy VM type, or upgrade your kernel.", + kvm_type == KVM_X86_SEV_VM ? "KVM_X86_SEV_VM" : "KVM_X86_SEV_ES_VM"); + } else { + error_report("SEV: host kernel does not support requested %s VM type. To allow use of " + "the legacy KVM_X86_DEFAULT_VM VM type, the 'legacy-vm-type' argument " + "must be set to 'on' or 'auto' for the sev-guest object.", + kvm_type == KVM_X86_SEV_VM ? "KVM_X86_SEV_VM" : "KVM_X86_SEV_ES_VM"); + } + + return -1; + } + + sev_common->kvm_type = kvm_type; out: return sev_common->kvm_type; } @@ -1477,14 +1513,24 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } trace_kvm_sev_init(); - if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == KVM_X86_DEFAULT_VM) { + switch (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common))) { + case KVM_X86_DEFAULT_VM: cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT; ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error); - } else { + break; + case KVM_X86_SEV_VM: + case KVM_X86_SEV_ES_VM: + case KVM_X86_SNP_VM: { struct kvm_sev_init args = { 0 }; ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); + break; + } + default: + error_setg(errp, "%s: host kernel does not support the requested SEV configuration.", + __func__); + return -1; } if (ret) { @@ -2074,14 +2120,23 @@ sev_guest_set_session_file(Object *obj, const char *value, Error **errp) SEV_GUEST(obj)->session_file = g_strdup(value); } -static bool sev_guest_get_legacy_vm_type(Object *obj, Error **errp) +static void sev_guest_get_legacy_vm_type(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { - return SEV_GUEST(obj)->legacy_vm_type; + SevGuestState *sev_guest = SEV_GUEST(obj); + OnOffAuto legacy_vm_type = sev_guest->legacy_vm_type; + + visit_type_OnOffAuto(v, name, &legacy_vm_type, errp); } -static void sev_guest_set_legacy_vm_type(Object *obj, bool value, Error **errp) +static void sev_guest_set_legacy_vm_type(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { - SEV_GUEST(obj)->legacy_vm_type = value; + SevGuestState *sev_guest = SEV_GUEST(obj); + + visit_type_OnOffAuto(v, name, &sev_guest->legacy_vm_type, errp); } static void @@ -2107,9 +2162,9 @@ sev_guest_class_init(ObjectClass *oc, void *data) sev_guest_set_session_file); object_class_property_set_description(oc, "session-file", "guest owners session parameters (encoded with base64)"); - object_class_property_add_bool(oc, "legacy-vm-type", - sev_guest_get_legacy_vm_type, - sev_guest_set_legacy_vm_type); + object_class_property_add(oc, "legacy-vm-type", "OnOffAuto", + sev_guest_get_legacy_vm_type, + sev_guest_set_legacy_vm_type, NULL, NULL); object_class_property_set_description(oc, "legacy-vm-type", "use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions."); } @@ -2125,6 +2180,8 @@ sev_guest_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "policy", &sev_guest->policy, OBJ_PROP_FLAG_READWRITE); object_apply_compat_props(obj); + + sev_guest->legacy_vm_type = ON_OFF_AUTO_AUTO; } /* guest info specific sev/sev-es */ From e0bf95443ee9326d44031373420cf9f3513ee255 Mon Sep 17 00:00:00 2001 From: Sergey Dyasli <sergey.dyasli@nutanix.com> Date: Fri, 12 Jul 2024 09:26:59 +0000 Subject: [PATCH 2112/2884] Revert "qemu-char: do not operate on sources from finalize callbacks" This reverts commit 2b316774f60291f57ca9ecb6a9f0712c532cae34. After 038b4217884c ("Revert "chardev: use a child source for qio input source"") we've been observing the "iwp->src == NULL" assertion triggering periodically during the initial capabilities querying by libvirtd. One of possible backtraces: Thread 1 (Thread 0x7f16cd4f0700 (LWP 43858)): 0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 1 0x00007f16c6c21e65 in __GI_abort () at abort.c:79 2 0x00007f16c6c21d39 in __assert_fail_base at assert.c:92 3 0x00007f16c6c46e86 in __GI___assert_fail (assertion=assertion@entry=0x562e9bcdaadd "iwp->src == NULL", file=file@entry=0x562e9bcdaac8 "../chardev/char-io.c", line=line@entry=99, function=function@entry=0x562e9bcdab10 <__PRETTY_FUNCTION__.20549> "io_watch_poll_finalize") at assert.c:101 4 0x0000562e9ba20c2c in io_watch_poll_finalize (source=<optimized out>) at ../chardev/char-io.c:99 5 io_watch_poll_finalize (source=<optimized out>) at ../chardev/char-io.c:88 6 0x00007f16c904aae0 in g_source_unref_internal () from /lib64/libglib-2.0.so.0 7 0x00007f16c904baf9 in g_source_destroy_internal () from /lib64/libglib-2.0.so.0 8 0x0000562e9ba20db0 in io_remove_watch_poll (source=0x562e9d6720b0) at ../chardev/char-io.c:147 9 remove_fd_in_watch (chr=chr@entry=0x562e9d5f3800) at ../chardev/char-io.c:153 10 0x0000562e9ba23ffb in update_ioc_handlers (s=0x562e9d5f3800) at ../chardev/char-socket.c:592 11 0x0000562e9ba2072f in qemu_chr_fe_set_handlers_full at ../chardev/char-fe.c:279 12 0x0000562e9ba207a9 in qemu_chr_fe_set_handlers at ../chardev/char-fe.c:304 13 0x0000562e9ba2ca75 in monitor_qmp_setup_handlers_bh (opaque=0x562e9d4c2c60) at ../monitor/qmp.c:509 14 0x0000562e9bb6222e in aio_bh_poll (ctx=ctx@entry=0x562e9d4c2f20) at ../util/async.c:216 15 0x0000562e9bb4de0a in aio_poll (ctx=0x562e9d4c2f20, blocking=blocking@entry=true) at ../util/aio-posix.c:722 16 0x0000562e9b99dfaa in iothread_run (opaque=0x562e9d4c26f0) at ../iothread.c:63 17 0x0000562e9bb505a4 in qemu_thread_start (args=0x562e9d4c7ea0) at ../util/qemu-thread-posix.c:543 18 0x00007f16c70081ca in start_thread (arg=<optimized out>) at pthread_create.c:479 19 0x00007f16c6c398d3 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 io_remove_watch_poll(), which makes sure that iwp->src is NULL, calls g_source_destroy() which finds that iwp->src is not NULL in the finalize callback. This can only happen if another thread has managed to trigger io_watch_poll_prepare() callback in the meantime. Move iwp->src destruction back to the finalize callback to prevent the described race, and also remove the stale comment. The deadlock glib bug was fixed back in 2010 by b35820285668 ("gmain: move finalization of GSource outside of context lock"). Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Sergey Dyasli <sergey.dyasli@nutanix.com> Link: https://lore.kernel.org/r/20240712092659.216206-1-sergey.dyasli@nutanix.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- chardev/char-io.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/chardev/char-io.c b/chardev/char-io.c index dab77b112e..3be17b51ca 100644 --- a/chardev/char-io.c +++ b/chardev/char-io.c @@ -87,16 +87,12 @@ static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, static void io_watch_poll_finalize(GSource *source) { - /* - * Due to a glib bug, removing the last reference to a source - * inside a finalize callback causes recursive locking (and a - * deadlock). This is not a problem inside other callbacks, - * including dispatch callbacks, so we call io_remove_watch_poll - * to remove this source. At this point, iwp->src must - * be NULL, or we would leak it. - */ IOWatchPoll *iwp = io_watch_poll_from_source(source); - assert(iwp->src == NULL); + if (iwp->src) { + g_source_destroy(iwp->src); + g_source_unref(iwp->src); + iwp->src = NULL; + } } static GSourceFuncs io_watch_poll_funcs = { @@ -139,11 +135,6 @@ static void io_remove_watch_poll(GSource *source) IOWatchPoll *iwp; iwp = io_watch_poll_from_source(source); - if (iwp->src) { - g_source_destroy(iwp->src); - g_source_unref(iwp->src); - iwp->src = NULL; - } g_source_destroy(&iwp->parent); } From f8b64d35a625e49ee73f7d54ae80cb5503be975b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Sun, 14 Jul 2024 19:46:52 +0900 Subject: [PATCH 2113/2884] cpu: Free queued CPU work Running qemu-system-aarch64 -M virt -nographic and terminating it will result in a LeakSanitizer error due to remaining queued CPU work so free it. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Link: https://lore.kernel.org/r/20240714-cpu-v1-1-19c2f8de2055@daynix.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- cpu-common.c | 11 +++++++++++ hw/core/cpu-common.c | 1 + include/hw/core/cpu.h | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/cpu-common.c b/cpu-common.c index ce78273af5..7ae136f98c 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -331,6 +331,17 @@ void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, queue_work_on_cpu(cpu, wi); } +void free_queued_cpu_work(CPUState *cpu) +{ + while (!QSIMPLEQ_EMPTY(&cpu->work_list)) { + struct qemu_work_item *wi = QSIMPLEQ_FIRST(&cpu->work_list); + QSIMPLEQ_REMOVE_HEAD(&cpu->work_list, node); + if (wi->free) { + g_free(wi); + } + } +} + void process_queued_cpu_work(CPUState *cpu) { struct qemu_work_item *wi; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index b19e1fdacf..d2e3e4570a 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -281,6 +281,7 @@ static void cpu_common_finalize(Object *obj) g_free(cpu->plugin_state); } #endif + free_queued_cpu_work(cpu); g_array_free(cpu->gdb_regs, TRUE); qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index a2c8536943..8e6466c1dd 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1000,6 +1000,12 @@ void cpu_resume(CPUState *cpu); */ void cpu_remove_sync(CPUState *cpu); +/** + * free_queued_cpu_work() - free all items on CPU work queue + * @cpu: The CPU which work queue to free. + */ +void free_queued_cpu_work(CPUState *cpu); + /** * process_queued_cpu_work() - process all items on CPU work queue * @cpu: The CPU which work queue to process. From 9971cbac2f30a89ddb094dc9627d2d16dc6e5875 Mon Sep 17 00:00:00 2001 From: Gustavo Romero <gustavo.romero@linaro.org> Date: Mon, 15 Jul 2024 21:39:43 +0000 Subject: [PATCH 2114/2884] disas: Fix build against Capstone v6 Capstone v6 made major changes, such as renaming for AArch64, which broke programs using the old headers, like QEMU. However, Capstone v6 provides the CAPSTONE_AARCH64_COMPAT_HEADER compatibility definition allowing to build against v6 with the old definitions, so fix the QEMU build using it. We can lift that definition and switch to the new naming once our supported distros have Capstone v6 in place. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> Suggested-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240715213943.1210355-1-gustavo.romero@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- include/disas/capstone.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/disas/capstone.h b/include/disas/capstone.h index e29068dd97..a11985151d 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -3,6 +3,7 @@ #ifdef CONFIG_CAPSTONE +#define CAPSTONE_AARCH64_COMPAT_HEADER #include <capstone.h> #else From a4975023fb13cf229bd59c9ceec1b8cbdc5b9a20 Mon Sep 17 00:00:00 2001 From: Fiona Ebner <f.ebner@proxmox.com> Date: Mon, 15 Jul 2024 15:14:03 +0200 Subject: [PATCH 2115/2884] hw/scsi/lsi53c895a: bump instruction limit in scripts processing to fix regression Commit 9876359990 ("hw/scsi/lsi53c895a: add timer to scripts processing") reduced the maximum allowed instruction count by a factor of 100 all the way down to 100. This causes the "Check Point R81.20 Gaia" appliance [0] to fail to boot after fully finishing the installation via the appliance's web interface (there is already one reboot before that). With a limit of 150, the appliance still fails to boot, while with a limit of 200, it works. Bump to 500 to fix the regression and be on the safe side. Originally reported in the Proxmox community forum[1]. [0]: https://support.checkpoint.com/results/download/124397 [1]: https://forum.proxmox.com/threads/149772/post-683459 Cc: qemu-stable@nongnu.org Fixes: 9876359990 ("hw/scsi/lsi53c895a: add timer to scripts processing") Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Acked-by: Sven Schnelle <svens@stackframe.org> Link: https://lore.kernel.org/r/20240715131403.223239-1-f.ebner@proxmox.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/scsi/lsi53c895a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index eb9828dd5e..f1935e5328 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -188,7 +188,7 @@ static const char *names[] = { #define LSI_TAG_VALID (1 << 16) /* Maximum instructions to process. */ -#define LSI_MAX_INSN 100 +#define LSI_MAX_INSN 500 typedef struct lsi_request { SCSIRequest *req; From 57a8a80d1a5b28797b21d30bfc60601945820e51 Mon Sep 17 00:00:00 2001 From: Fiona Ebner <f.ebner@proxmox.com> Date: Wed, 10 Jul 2024 17:25:29 +0200 Subject: [PATCH 2116/2884] scsi: fix regression and honor bootindex again for legacy drives Commit 3089637461 ("scsi: Don't ignore most usb-storage properties") removed the call to object_property_set_int() and thus the 'set' method for the bootindex property was also not called anymore. Here that method is device_set_bootindex() (as configured by scsi_dev_instance_init() -> device_add_bootindex_property()) which as a side effect registers the device via add_boot_device_path(). As reported by a downstream user [0], the bootindex property did not have the desired effect anymore for legacy drives. Fix the regression by explicitly calling the add_boot_device_path() function after checking that the bootindex is not yet used (to avoid add_boot_device_path() calling exit()). [0]: https://forum.proxmox.com/threads/149772/post-679433 Cc: qemu-stable@nongnu.org Fixes: 3089637461 ("scsi: Don't ignore most usb-storage properties") Suggested-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Link: https://lore.kernel.org/r/20240710152529.1737407-1-f.ebner@proxmox.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/scsi/scsi-bus.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 9e40b0c920..53eff5dd3d 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -384,6 +384,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, DeviceState *dev; SCSIDevice *s; DriveInfo *dinfo; + Error *local_err = NULL; if (blk_is_sg(blk)) { driver = "scsi-generic"; @@ -403,6 +404,14 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, s = SCSI_DEVICE(dev); s->conf = *conf; + check_boot_index(conf->bootindex, &local_err); + if (local_err) { + object_unparent(OBJECT(dev)); + error_propagate(errp, local_err); + return NULL; + } + add_boot_device_path(conf->bootindex, dev, NULL); + qdev_prop_set_uint32(dev, "scsi-id", unit); if (object_property_find(OBJECT(dev), "removable")) { qdev_prop_set_bit(dev, "removable", removable); From d16ccfea238e51a17650c6e32f7c4a1e43cfaa09 Mon Sep 17 00:00:00 2001 From: Song Gao <gaosong@loongson.cn> Date: Tue, 16 Jul 2024 11:15:00 +0800 Subject: [PATCH 2117/2884] qemu/timer: Add host ticks function for LoongArch Signed-off-by: Song Gao <gaosong@loongson.cn> Link: https://lore.kernel.org/r/20240716031500.4193498-1-gaosong@loongson.cn Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- include/qemu/timer.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 5ce83c7911..fa56ec9481 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -1016,6 +1016,15 @@ static inline int64_t cpu_get_host_ticks(void) return val; } +#elif defined(__loongarch64) +static inline int64_t cpu_get_host_ticks(void) +{ + uint64_t val; + + asm volatile("rdtime.d %0, $zero" : "=r"(val)); + return val; +} + #else /* The host CPU doesn't have an easily accessible cycle counter. Just return a monotonically increasing value. This will be From de12ebfdabe224597f624827ad57348b83e7d88a Mon Sep 17 00:00:00 2001 From: Boqiao Fu <bfu@redhat.com> Date: Mon, 15 Jul 2024 17:04:32 +0800 Subject: [PATCH 2118/2884] docs: Update description of 'user=username' for '-run-with' The description of '-runas' and '-run-with' didn't explain that QEMU will use setuid/setgid to implement the option, so the user might get confused if using 'elevateprivileges=deny' as well. Since '-runas' is going to be deprecated and replaced by '-run-with' in the coming qemu9.1, add the message there. Signed-off-by: Boqiao Fu <bfu@redhat.com> Link: https://lore.kernel.org/r/CAFRHJ6J9uMk+HMZL+W+KE1yoRCOLPgbPUVVDku55sdXYiGXXHg@mail.gmail.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- qemu-options.hx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index ad6521ef5e..694fa37f28 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5024,8 +5024,11 @@ SRST in combination with -runas. ``user=username`` or ``user=uid:gid`` can be used to drop root privileges - by switching to the specified user (via username) or user and group - (via uid:gid) immediately before starting guest execution. + before starting guest execution. QEMU will use the ``setuid`` and ``setgid`` + system calls to switch to the specified identity. Note that the + ``user=username`` syntax will also apply the full set of supplementary + groups for the user, whereas the ``user=uid:gid`` will use only the + ``gid`` group. ERST #endif From c9669d6d5741f7aa86e160c5e42979f90d25d168 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 11:56:35 +0200 Subject: [PATCH 2119/2884] hpet: fix clamping of period When writing a new period, the clamping should use a maximum value rather tyhan a bit mask. Also, when writing the high bits new_val is shifted right by 32, so the maximum allowed period should also be shifted right. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 01efe4885d..ad881448bf 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -548,7 +548,9 @@ static void hpet_ram_write(void *opaque, hwaddr addr, * FIXME: Clamp period to reasonable min value? * Clamp period to reasonable max value */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; + if (timer->config & HPET_TN_32BIT) { + new_val = MIN(new_val, ~0u >> 1); + } timer->period = (timer->period & 0xffffffff00000000ULL) | new_val; } @@ -567,7 +569,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, * FIXME: Clamp period to reasonable min value? * Clamp period to reasonable max value */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; + new_val = MIN(new_val, ~0u >> 1); timer->period = (timer->period & 0xffffffffULL) | new_val << 32; } From 340627ec14968d64cfa3dd12d0e998639f227c49 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 11:27:28 +0200 Subject: [PATCH 2120/2884] hpet: fix HPET_TN_SETVAL for high 32-bits of the comparator Commit 3787324101b ("hpet: Fix emulation of HPET_TN_SETVAL (Jan Kiszka)", 2009-04-17) applied the fix only to the low 32-bits of the comparator, but it should be done for the high bits as well. Otherwise, the high 32-bits of the comparator cannot be written and they remain fixed to 0xffffffff. Co-developed-by: TaiseiIto <taisei1212@outlook.jp> Signed-off-by: TaiseiIto <taisei1212@outlook.jp> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index ad881448bf..4cb5393c0b 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -554,6 +554,10 @@ static void hpet_ram_write(void *opaque, hwaddr addr, timer->period = (timer->period & 0xffffffff00000000ULL) | new_val; } + /* + * FIXME: on a 64-bit write, HPET_TN_SETVAL should apply to the + * high bits part as well. + */ timer->config &= ~HPET_TN_SETVAL; if (hpet_enabled(s)) { hpet_set_timer(timer); @@ -564,7 +568,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr, if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) { timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; - } else { + } + if (timer_is_periodic(timer)) { /* * FIXME: Clamp period to reasonable min value? * Clamp period to reasonable max value @@ -572,12 +577,12 @@ static void hpet_ram_write(void *opaque, hwaddr addr, new_val = MIN(new_val, ~0u >> 1); timer->period = (timer->period & 0xffffffffULL) | new_val << 32; - } - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled(s)) { - hpet_set_timer(timer); - } - break; + } + timer->config &= ~HPET_TN_SETVAL; + if (hpet_enabled(s)) { + hpet_set_timer(timer); + } + break; case HPET_TN_ROUTE: timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val; break; From 3afc6539a84d3d003025c9352a0596dbcb350ff4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 12:40:24 +0200 Subject: [PATCH 2121/2884] target/i386/tcg: fix POP to memory in long mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In long mode, POP to memory will write a full 64-bit value. However, the call to gen_writeback() in gen_POP will use MO_32 because the decoding table is incorrect. The bug was latent until commit aea49fbb01a ("target/i386: use gen_writeback() within gen_POP()", 2024-06-08), and then became visible because gen_op_st_v now receives op->ot instead of the "ot" returned by gen_pop_T0. Analyzed-by: Clément Chigot <chigot@adacore.com> Fixes: 5e9e21bcc4d ("target/i386: move 60-BF opcodes to new decoder", 2024-05-07) Tested-by: Clément Chigot <chigot@adacore.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/decode-new.c.inc | 2 +- target/i386/tcg/emit.c.inc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 0d846c32c2..d2da1d396d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1717,7 +1717,7 @@ static const X86OpEntry opcodes_root[256] = { [0x8C] = X86_OP_ENTRYwr(MOV, E,v, S,w, op0_Mw), [0x8D] = X86_OP_ENTRYwr(LEA, G,v, M,v, nolea), [0x8E] = X86_OP_ENTRYwr(MOV, S,w, E,w), - [0x8F] = X86_OP_GROUPw(group1A, E,v), + [0x8F] = X86_OP_GROUPw(group1A, E,d64), [0x98] = X86_OP_ENTRY1(CBW, 0,v), /* rAX */ [0x99] = X86_OP_ENTRYwr(CWD, 2,v, 0,v), /* rDX, rAX */ diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index fc7477833b..016dce8146 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -2788,6 +2788,7 @@ static void gen_POP(DisasContext *s, X86DecodedInsn *decode) X86DecodedOp *op = &decode->op[0]; MemOp ot = gen_pop_T0(s); + assert(ot >= op->ot); if (op->has_ea || op->unit == X86_OP_SEG) { /* NOTE: order is important for MMU exceptions */ gen_writeback(s, decode, 0, s->T0); From a7cf4949938743e9ecb73efcb51d27bd18d3c3fa Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 17 Jun 2024 09:12:09 -0700 Subject: [PATCH 2122/2884] target/i386/tcg: Remove SEG_ADDL This truncation is now handled by MMU_*32_IDX. The introduction of MMU_*32_IDX in fact applied correct 32-bit wraparound to 16-bit accesses with a high segment base (e.g. big real mode or vm86 mode), which did not use SEG_ADDL. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240617161210.4639-3-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index aee3d19f29..19d6b41a58 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -579,10 +579,6 @@ int exception_has_error_code(int intno) } while (0) #endif -/* in 64-bit machines, this can overflow. So this segment addition macro - * can be used to trim the value to 32-bit whenever needed */ -#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) - /* XXX: add a is_user flag to have proper security support */ #define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ { \ @@ -593,7 +589,7 @@ int exception_has_error_code(int intno) #define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ { \ sp -= 4; \ - cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \ + cpu_stl_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ } #define POPW_RA(ssp, sp, sp_mask, val, ra) \ @@ -604,7 +600,7 @@ int exception_has_error_code(int intno) #define POPL_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \ + val = (uint32_t)cpu_ldl_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ sp += 4; \ } From 0bd385e7e3c33e987d7a8879918be6df7b111ac4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 11 Jun 2024 22:04:56 +0200 Subject: [PATCH 2123/2884] target/i386/tcg: Allow IRET from user mode to user mode with SMAP This fixes a bug wherein i386/tcg assumed an interrupt return using the IRET instruction was always returning from kernel mode to either kernel mode or user mode. This assumption is violated when IRET is used as a clever way to restore thread state, as for example in the dotnet runtime. There, IRET returns from user mode to user mode. This bug is that stack accesses from IRET and RETF, as well as accesses to the parameters in a call gate, are normal data accesses using the current CPL. This manifested itself as a page fault in the guest Linux kernel due to SMAP preventing the access. This bug appears to have been in QEMU since the beginning. Analyzed-by: Robert R. Henry <rrh.henry@gmail.com> Co-developed-by: Robert R. Henry <rrh.henry@gmail.com> Signed-off-by: Robert R. Henry <rrh.henry@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 19d6b41a58..224e73e9ed 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -594,13 +594,13 @@ int exception_has_error_code(int intno) #define POPW_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ + val = cpu_lduw_data_ra(env, (ssp) + (sp & (sp_mask)), ra); \ sp += 2; \ } #define POPL_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = (uint32_t)cpu_ldl_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ + val = (uint32_t)cpu_ldl_data_ra(env, (ssp) + (sp & (sp_mask)), ra); \ sp += 4; \ } @@ -847,7 +847,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, #define POPQ_RA(sp, val, ra) \ { \ - val = cpu_ldq_kernel_ra(env, sp, ra); \ + val = cpu_ldq_data_ra(env, sp, ra); \ sp += 8; \ } @@ -1797,18 +1797,18 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { - val = cpu_ldl_kernel_ra(env, old_ssp + - ((env->regs[R_ESP] + i * 4) & - old_sp_mask), GETPC()); + val = cpu_ldl_data_ra(env, + old_ssp + ((env->regs[R_ESP] + i * 4) & old_sp_mask), + GETPC()); PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); } } else { PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { - val = cpu_lduw_kernel_ra(env, old_ssp + - ((env->regs[R_ESP] + i * 2) & - old_sp_mask), GETPC()); + val = cpu_lduw_data_ra(env, + old_ssp + ((env->regs[R_ESP] + i * 2) & old_sp_mask), + GETPC()); PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); } } From 312ef3243e3bae07e49314efb2854f6f17080ec5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 18 Jun 2024 09:28:22 +0200 Subject: [PATCH 2124/2884] target/i386/tcg: use PUSHL/PUSHW for error code Do not pre-decrement esp, let the macros subtract the appropriate operand size. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 224e73e9ed..b985382d70 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -670,22 +670,20 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } shift = switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); if (has_error_code) { - uint32_t mask; - /* push the error code */ if (env->segs[R_SS].flags & DESC_B_MASK) { - mask = 0xffffffff; + sp_mask = 0xffffffff; } else { - mask = 0xffff; + sp_mask = 0xffff; } - esp = (env->regs[R_ESP] - (2 << shift)) & mask; - ssp = env->segs[R_SS].base + esp; + esp = env->regs[R_ESP]; + ssp = env->segs[R_SS].base; if (shift) { - cpu_stl_kernel(env, ssp, error_code); + PUSHL(ssp, esp, sp_mask, error_code); } else { - cpu_stw_kernel(env, ssp, error_code); + PUSHW(ssp, esp, sp_mask, error_code); } - SET_ESP(esp, mask); + SET_ESP(esp, sp_mask); } return; } From 059368bcf589f4c6cba860516f57cec0b51c1fa1 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 17 Jun 2024 09:12:10 -0700 Subject: [PATCH 2125/2884] target/i386/tcg: Reorg push/pop within seg_helper.c Interrupts and call gates should use accesses with the DPL as the privilege level. While computing the applicable MMU index is easy, the harder thing is how to plumb it in the code. One possibility could be to add a single argument to the PUSH* macros for the privilege level, but this is repetitive and risks confusion between the involved privilege levels. Another possibility is to pass both CPL and DPL, and adjusting both PUSH* and POP* to use specific privilege levels (instead of using cpu_{ld,st}*_data). This makes the code more symmetric. However, a more complicated but much nicer approach is to use a structure to contain the stack parameters, env, unwind return address, and rewrite the macros into functions. The struct provides an easy home for the MMU index as well. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240617161210.4639-4-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 481 +++++++++++++++++++---------------- 1 file changed, 259 insertions(+), 222 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index b985382d70..b6902ca3fb 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -28,6 +28,68 @@ #include "helper-tcg.h" #include "seg_helper.h" +#ifdef TARGET_X86_64 +#define SET_ESP(val, sp_mask) \ + do { \ + if ((sp_mask) == 0xffff) { \ + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | \ + ((val) & 0xffff); \ + } else if ((sp_mask) == 0xffffffffLL) { \ + env->regs[R_ESP] = (uint32_t)(val); \ + } else { \ + env->regs[R_ESP] = (val); \ + } \ + } while (0) +#else +#define SET_ESP(val, sp_mask) \ + do { \ + env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) | \ + ((val) & (sp_mask)); \ + } while (0) +#endif + +/* XXX: use mmu_index to have proper DPL support */ +typedef struct StackAccess +{ + CPUX86State *env; + uintptr_t ra; + target_ulong ss_base; + target_ulong sp; + target_ulong sp_mask; +} StackAccess; + +static void pushw(StackAccess *sa, uint16_t val) +{ + sa->sp -= 2; + cpu_stw_kernel_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), + val, sa->ra); +} + +static void pushl(StackAccess *sa, uint32_t val) +{ + sa->sp -= 4; + cpu_stl_kernel_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), + val, sa->ra); +} + +static uint16_t popw(StackAccess *sa) +{ + uint16_t ret = cpu_lduw_data_ra(sa->env, + sa->ss_base + (sa->sp & sa->sp_mask), + sa->ra); + sa->sp += 2; + return ret; +} + +static uint32_t popl(StackAccess *sa) +{ + uint32_t ret = cpu_ldl_data_ra(sa->env, + sa->ss_base + (sa->sp & sa->sp_mask), + sa->ra); + sa->sp += 4; + return ret; +} + int get_pg_mode(CPUX86State *env) { int pg_mode = 0; @@ -559,68 +621,19 @@ int exception_has_error_code(int intno) return 0; } -#ifdef TARGET_X86_64 -#define SET_ESP(val, sp_mask) \ - do { \ - if ((sp_mask) == 0xffff) { \ - env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | \ - ((val) & 0xffff); \ - } else if ((sp_mask) == 0xffffffffLL) { \ - env->regs[R_ESP] = (uint32_t)(val); \ - } else { \ - env->regs[R_ESP] = (val); \ - } \ - } while (0) -#else -#define SET_ESP(val, sp_mask) \ - do { \ - env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) | \ - ((val) & (sp_mask)); \ - } while (0) -#endif - -/* XXX: add a is_user flag to have proper security support */ -#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ - { \ - sp -= 2; \ - cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ - } - -#define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ - { \ - sp -= 4; \ - cpu_stl_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ - } - -#define POPW_RA(ssp, sp, sp_mask, val, ra) \ - { \ - val = cpu_lduw_data_ra(env, (ssp) + (sp & (sp_mask)), ra); \ - sp += 2; \ - } - -#define POPL_RA(ssp, sp, sp_mask, val, ra) \ - { \ - val = (uint32_t)cpu_ldl_data_ra(env, (ssp) + (sp & (sp_mask)), ra); \ - sp += 4; \ - } - -#define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0) -#define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0) -#define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0) -#define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0) - /* protected mode interrupt */ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, int error_code, unsigned int next_eip, int is_hw) { SegmentCache *dt; - target_ulong ptr, ssp; + target_ulong ptr; int type, dpl, selector, ss_dpl, cpl; int has_error_code, new_stack, shift; - uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; - uint32_t old_eip, sp_mask, eflags; + uint32_t e1, e2, offset, ss = 0, ss_e1 = 0, ss_e2 = 0; + uint32_t old_eip, eflags; int vm86 = env->eflags & VM_MASK; + StackAccess sa; bool set_rf; has_error_code = 0; @@ -662,6 +675,9 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); } + sa.env = env; + sa.ra = 0; + if (type == 5) { /* task gate */ /* must do that check here to return the correct error code */ @@ -672,18 +688,18 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, if (has_error_code) { /* push the error code */ if (env->segs[R_SS].flags & DESC_B_MASK) { - sp_mask = 0xffffffff; + sa.sp_mask = 0xffffffff; } else { - sp_mask = 0xffff; + sa.sp_mask = 0xffff; } - esp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; + sa.sp = env->regs[R_ESP]; + sa.ss_base = env->segs[R_SS].base; if (shift) { - PUSHL(ssp, esp, sp_mask, error_code); + pushl(&sa, error_code); } else { - PUSHW(ssp, esp, sp_mask, error_code); + pushw(&sa, error_code); } - SET_ESP(esp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); } return; } @@ -717,6 +733,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } if (dpl < cpl) { /* to inner privilege */ + uint32_t esp; get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); if ((ss & 0xfffc) == 0) { raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); @@ -740,17 +757,18 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); } new_stack = 1; - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); + sa.sp = esp; + sa.sp_mask = get_sp_mask(ss_e2); + sa.ss_base = get_seg_base(ss_e1, ss_e2); } else { /* to same privilege */ if (vm86) { raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); } new_stack = 0; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - esp = env->regs[R_ESP]; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; } shift = type >> 3; @@ -775,36 +793,36 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, if (shift == 1) { if (new_stack) { if (vm86) { - PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector); + pushl(&sa, env->segs[R_GS].selector); + pushl(&sa, env->segs[R_FS].selector); + pushl(&sa, env->segs[R_DS].selector); + pushl(&sa, env->segs[R_ES].selector); } - PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); - PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]); + pushl(&sa, env->segs[R_SS].selector); + pushl(&sa, env->regs[R_ESP]); } - PUSHL(ssp, esp, sp_mask, eflags); - PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, esp, sp_mask, old_eip); + pushl(&sa, eflags); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, old_eip); if (has_error_code) { - PUSHL(ssp, esp, sp_mask, error_code); + pushl(&sa, error_code); } } else { if (new_stack) { if (vm86) { - PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector); + pushw(&sa, env->segs[R_GS].selector); + pushw(&sa, env->segs[R_FS].selector); + pushw(&sa, env->segs[R_DS].selector); + pushw(&sa, env->segs[R_ES].selector); } - PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); - PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]); + pushw(&sa, env->segs[R_SS].selector); + pushw(&sa, env->regs[R_ESP]); } - PUSHW(ssp, esp, sp_mask, eflags); - PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, esp, sp_mask, old_eip); + pushw(&sa, eflags); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, old_eip); if (has_error_code) { - PUSHW(ssp, esp, sp_mask, error_code); + pushw(&sa, error_code); } } @@ -822,10 +840,10 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0); } ss = (ss & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); + cpu_x86_load_seg_cache(env, R_SS, ss, sa.ss_base, + get_seg_limit(ss_e1, ss_e2), ss_e2); } - SET_ESP(esp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); selector = (selector & ~3) | dpl; cpu_x86_load_seg_cache(env, R_CS, selector, @@ -837,20 +855,18 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, #ifdef TARGET_X86_64 -#define PUSHQ_RA(sp, val, ra) \ - { \ - sp -= 8; \ - cpu_stq_kernel_ra(env, sp, (val), ra); \ - } +static void pushq(StackAccess *sa, uint64_t val) +{ + sa->sp -= 8; + cpu_stq_kernel_ra(sa->env, sa->sp, val, sa->ra); +} -#define POPQ_RA(sp, val, ra) \ - { \ - val = cpu_ldq_data_ra(env, sp, ra); \ - sp += 8; \ - } - -#define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0) -#define POPQ(sp, val) POPQ_RA(sp, val, 0) +static uint64_t popq(StackAccess *sa) +{ + uint64_t ret = cpu_ldq_data_ra(sa->env, sa->sp, sa->ra); + sa->sp += 8; + return ret; +} static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) { @@ -893,8 +909,9 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, int type, dpl, selector, cpl, ist; int has_error_code, new_stack; uint32_t e1, e2, e3, ss, eflags; - target_ulong old_eip, esp, offset; + target_ulong old_eip, offset; bool set_rf; + StackAccess sa; has_error_code = 0; if (!is_int && !is_hw) { @@ -962,10 +979,15 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, if (e2 & DESC_C_MASK) { dpl = cpl; } + + sa.env = env; + sa.ra = 0; + sa.sp_mask = -1; + sa.ss_base = 0; if (dpl < cpl || ist != 0) { /* to inner privilege */ new_stack = 1; - esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); + sa.sp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); ss = 0; } else { /* to same privilege */ @@ -973,9 +995,9 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); } new_stack = 0; - esp = env->regs[R_ESP]; + sa.sp = env->regs[R_ESP]; } - esp &= ~0xfLL; /* align stack */ + sa.sp &= ~0xfLL; /* align stack */ /* See do_interrupt_protected. */ eflags = cpu_compute_eflags(env); @@ -983,13 +1005,13 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, eflags |= RF_MASK; } - PUSHQ(esp, env->segs[R_SS].selector); - PUSHQ(esp, env->regs[R_ESP]); - PUSHQ(esp, eflags); - PUSHQ(esp, env->segs[R_CS].selector); - PUSHQ(esp, old_eip); + pushq(&sa, env->segs[R_SS].selector); + pushq(&sa, env->regs[R_ESP]); + pushq(&sa, eflags); + pushq(&sa, env->segs[R_CS].selector); + pushq(&sa, old_eip); if (has_error_code) { - PUSHQ(esp, error_code); + pushq(&sa, error_code); } /* interrupt gate clear IF mask */ @@ -1002,7 +1024,7 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, ss = 0 | dpl; cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT); } - env->regs[R_ESP] = esp; + env->regs[R_ESP] = sa.sp; selector = (selector & ~3) | dpl; cpu_x86_load_seg_cache(env, R_CS, selector, @@ -1074,10 +1096,11 @@ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, int error_code, unsigned int next_eip) { SegmentCache *dt; - target_ulong ptr, ssp; + target_ulong ptr; int selector; - uint32_t offset, esp; + uint32_t offset; uint32_t old_cs, old_eip; + StackAccess sa; /* real mode (simpler!) */ dt = &env->idt; @@ -1087,8 +1110,13 @@ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, ptr = dt->base + intno * 4; offset = cpu_lduw_kernel(env, ptr); selector = cpu_lduw_kernel(env, ptr + 2); - esp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; + + sa.env = env; + sa.ra = 0; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = 0xffff; + sa.ss_base = env->segs[R_SS].base; + if (is_int) { old_eip = next_eip; } else { @@ -1096,12 +1124,12 @@ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, } old_cs = env->segs[R_CS].selector; /* XXX: use SS segment size? */ - PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env)); - PUSHW(ssp, esp, 0xffff, old_cs); - PUSHW(ssp, esp, 0xffff, old_eip); + pushw(&sa, cpu_compute_eflags(env)); + pushw(&sa, old_cs); + pushw(&sa, old_eip); /* update processor state */ - env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff); + SET_ESP(sa.sp, sa.sp_mask); env->eip = offset; env->segs[R_CS].selector = selector; env->segs[R_CS].base = (selector << 4); @@ -1544,21 +1572,23 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, void helper_lcall_real(CPUX86State *env, uint32_t new_cs, uint32_t new_eip, int shift, uint32_t next_eip) { - uint32_t esp, esp_mask; - target_ulong ssp; + StackAccess sa; + + sa.env = env; + sa.ra = GETPC(); + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; - esp = env->regs[R_ESP]; - esp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; if (shift) { - PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC()); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, next_eip); } else { - PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC()); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, next_eip); } - SET_ESP(esp, esp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->eip = new_eip; env->segs[R_CS].selector = new_cs; env->segs[R_CS].base = (new_cs << 4); @@ -1570,9 +1600,10 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, { int new_stack, i; uint32_t e1, e2, cpl, dpl, rpl, selector, param_count; - uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask; + uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl; uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp, offset, sp; + target_ulong old_ssp, offset; + StackAccess sa; LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift); LOG_PCALL_STATE(env_cpu(env)); @@ -1584,6 +1615,10 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } cpl = env->hflags & HF_CPL_MASK; LOG_PCALL("desc=%08x:%08x\n", e1, e2); + + sa.env = env; + sa.ra = GETPC(); + if (e2 & DESC_S_MASK) { if (!(e2 & DESC_CS_MASK)) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); @@ -1611,14 +1646,14 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, #ifdef TARGET_X86_64 /* XXX: check 16/32 bit cases in long mode */ if (shift == 2) { - target_ulong rsp; - /* 64 bit case */ - rsp = env->regs[R_ESP]; - PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC()); - PUSHQ_RA(rsp, next_eip, GETPC()); + sa.sp = env->regs[R_ESP]; + sa.sp_mask = -1; + sa.ss_base = 0; + pushq(&sa, env->segs[R_CS].selector); + pushq(&sa, next_eip); /* from this point, not restartable */ - env->regs[R_ESP] = rsp; + env->regs[R_ESP] = sa.sp; cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); @@ -1626,15 +1661,15 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } else #endif { - sp = env->regs[R_ESP]; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; if (shift) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, next_eip); } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, next_eip); } limit = get_seg_limit(e1, e2); @@ -1642,7 +1677,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } /* from this point, not restartable */ - SET_ESP(sp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); env->eip = new_eip; @@ -1737,13 +1772,13 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* to inner privilege */ #ifdef TARGET_X86_64 if (shift == 2) { - sp = get_rsp_from_tss(env, dpl); ss = dpl; /* SS = NULL selector with RPL = new CPL */ new_stack = 1; - sp_mask = 0; - ssp = 0; /* SS base is always zero in IA-32e mode */ + sa.sp = get_rsp_from_tss(env, dpl); + sa.sp_mask = -1; + sa.ss_base = 0; /* SS base is always zero in IA-32e mode */ LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]=" - TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]); + TARGET_FMT_lx "\n", ss, sa.sp, env->regs[R_ESP]); } else #endif { @@ -1752,7 +1787,6 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" TARGET_FMT_lx "\n", ss, sp32, param_count, env->regs[R_ESP]); - sp = sp32; if ((ss & 0xfffc) == 0) { raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } @@ -1775,63 +1809,64 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); + sa.sp = sp32; + sa.sp_mask = get_sp_mask(ss_e2); + sa.ss_base = get_seg_base(ss_e1, ss_e2); } /* push_size = ((param_count * 2) + 8) << shift; */ - old_sp_mask = get_sp_mask(env->segs[R_SS].flags); old_ssp = env->segs[R_SS].base; + #ifdef TARGET_X86_64 if (shift == 2) { /* XXX: verify if new stack address is canonical */ - PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC()); - PUSHQ_RA(sp, env->regs[R_ESP], GETPC()); + pushq(&sa, env->segs[R_SS].selector); + pushq(&sa, env->regs[R_ESP]); /* parameters aren't supported for 64-bit call gates */ } else #endif if (shift == 1) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); + pushl(&sa, env->segs[R_SS].selector); + pushl(&sa, env->regs[R_ESP]); for (i = param_count - 1; i >= 0; i--) { val = cpu_ldl_data_ra(env, old_ssp + ((env->regs[R_ESP] + i * 4) & old_sp_mask), GETPC()); - PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); + pushl(&sa, val); } } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); + pushw(&sa, env->segs[R_SS].selector); + pushw(&sa, env->regs[R_ESP]); for (i = param_count - 1; i >= 0; i--) { val = cpu_lduw_data_ra(env, old_ssp + ((env->regs[R_ESP] + i * 2) & old_sp_mask), GETPC()); - PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); + pushw(&sa, val); } } new_stack = 1; } else { /* to same privilege */ - sp = env->regs[R_ESP]; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; /* push_size = (4 << shift); */ new_stack = 0; } #ifdef TARGET_X86_64 if (shift == 2) { - PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC()); - PUSHQ_RA(sp, next_eip, GETPC()); + pushq(&sa, env->segs[R_CS].selector); + pushq(&sa, next_eip); } else #endif if (shift == 1) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, next_eip); } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, next_eip); } /* from this point, not restartable */ @@ -1845,7 +1880,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, { ss = (ss & ~3) | dpl; cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, + sa.ss_base, get_seg_limit(ss_e1, ss_e2), ss_e2); } @@ -1856,7 +1891,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); - SET_ESP(sp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->eip = offset; } } @@ -1864,26 +1899,28 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* real and vm86 mode iret */ void helper_iret_real(CPUX86State *env, int shift) { - uint32_t sp, new_cs, new_eip, new_eflags, sp_mask; - target_ulong ssp; + uint32_t new_cs, new_eip, new_eflags; int eflags_mask; + StackAccess sa; + + sa.env = env; + sa.ra = GETPC(); + sa.sp_mask = 0xffff; /* XXXX: use SS segment size? */ + sa.sp = env->regs[R_ESP]; + sa.ss_base = env->segs[R_SS].base; - sp_mask = 0xffff; /* XXXX: use SS segment size? */ - sp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; if (shift == 1) { /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_eip, GETPC()); - POPL_RA(ssp, sp, sp_mask, new_cs, GETPC()); - new_cs &= 0xffff; - POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC()); + new_eip = popl(&sa); + new_cs = popl(&sa) & 0xffff; + new_eflags = popl(&sa); } else { /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_eip, GETPC()); - POPW_RA(ssp, sp, sp_mask, new_cs, GETPC()); - POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC()); + new_eip = popw(&sa); + new_cs = popw(&sa); + new_eflags = popw(&sa); } - env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->segs[R_CS].selector = new_cs; env->segs[R_CS].base = (new_cs << 4); env->eip = new_eip; @@ -1936,47 +1973,49 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, uint32_t new_es, new_ds, new_fs, new_gs; uint32_t e1, e2, ss_e1, ss_e2; int cpl, dpl, rpl, eflags_mask, iopl; - target_ulong ssp, sp, new_eip, new_esp, sp_mask; + target_ulong new_eip, new_esp; + StackAccess sa; + + sa.env = env; + sa.ra = retaddr; #ifdef TARGET_X86_64 if (shift == 2) { - sp_mask = -1; + sa.sp_mask = -1; } else #endif { - sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); } - sp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; + sa.sp = env->regs[R_ESP]; + sa.ss_base = env->segs[R_SS].base; new_eflags = 0; /* avoid warning */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ_RA(sp, new_eip, retaddr); - POPQ_RA(sp, new_cs, retaddr); - new_cs &= 0xffff; + new_eip = popq(&sa); + new_cs = popq(&sa) & 0xffff; if (is_iret) { - POPQ_RA(sp, new_eflags, retaddr); + new_eflags = popq(&sa); } } else #endif { if (shift == 1) { /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_eip, retaddr); - POPL_RA(ssp, sp, sp_mask, new_cs, retaddr); - new_cs &= 0xffff; + new_eip = popl(&sa); + new_cs = popl(&sa) & 0xffff; if (is_iret) { - POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr); + new_eflags = popl(&sa); if (new_eflags & VM_MASK) { goto return_to_vm86; } } } else { /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_eip, retaddr); - POPW_RA(ssp, sp, sp_mask, new_cs, retaddr); + new_eip = popw(&sa); + new_cs = popw(&sa); if (is_iret) { - POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr); + new_eflags = popw(&sa); } } } @@ -2012,7 +2051,7 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr); } - sp += addend; + sa.sp += addend; if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || ((env->hflags & HF_CS64_MASK) && !is_iret))) { /* return to same privilege level */ @@ -2024,21 +2063,19 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, /* return to different privilege level */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ_RA(sp, new_esp, retaddr); - POPQ_RA(sp, new_ss, retaddr); - new_ss &= 0xffff; + new_esp = popq(&sa); + new_ss = popq(&sa) & 0xffff; } else #endif { if (shift == 1) { /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); - new_ss &= 0xffff; + new_esp = popl(&sa); + new_ss = popl(&sa) & 0xffff; } else { /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPW_RA(ssp, sp, sp_mask, new_ss, retaddr); + new_esp = popw(&sa); + new_ss = popw(&sa); } } LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", @@ -2088,14 +2125,14 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); - sp = new_esp; + sa.sp = new_esp; #ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { - sp_mask = -1; + sa.sp_mask = -1; } else #endif { - sp_mask = get_sp_mask(ss_e2); + sa.sp_mask = get_sp_mask(ss_e2); } /* validate data segments */ @@ -2104,9 +2141,9 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, validate_seg(env, R_FS, rpl); validate_seg(env, R_GS, rpl); - sp += addend; + sa.sp += addend; } - SET_ESP(sp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->eip = new_eip; if (is_iret) { /* NOTE: 'cpl' is the _old_ CPL */ @@ -2126,12 +2163,12 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, return; return_to_vm86: - POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); - POPL_RA(ssp, sp, sp_mask, new_es, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ds, retaddr); - POPL_RA(ssp, sp, sp_mask, new_fs, retaddr); - POPL_RA(ssp, sp, sp_mask, new_gs, retaddr); + new_esp = popl(&sa); + new_ss = popl(&sa); + new_es = popl(&sa); + new_ds = popl(&sa); + new_fs = popl(&sa); + new_gs = popl(&sa); /* modify processor state */ cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | From fffe424b38340428f50111713c20d54b46101c7d Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 17 Jun 2024 09:12:08 -0700 Subject: [PATCH 2126/2884] target/i386/tcg: Introduce x86_mmu_index_{kernel_,}pl Disconnect mmu index computation from the current pl as stored in env->hflags. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240617161210.4639-2-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/cpu.c | 27 ++++++++++++++++++++++++--- target/i386/cpu.h | 11 ++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c05765eeaf..4688d140c2 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8122,18 +8122,39 @@ static bool x86_cpu_has_work(CPUState *cs) return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0; } -static int x86_cpu_mmu_index(CPUState *cs, bool ifetch) +int x86_mmu_index_pl(CPUX86State *env, unsigned pl) { - CPUX86State *env = cpu_env(cs); int mmu_index_32 = (env->hflags & HF_CS64_MASK) ? 0 : 1; int mmu_index_base = - (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER64_IDX : + pl == 3 ? MMU_USER64_IDX : !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : (env->eflags & AC_MASK) ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX; return mmu_index_base + mmu_index_32; } +static int x86_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUX86State *env = cpu_env(cs); + return x86_mmu_index_pl(env, env->hflags & HF_CPL_MASK); +} + +static int x86_mmu_index_kernel_pl(CPUX86State *env, unsigned pl) +{ + int mmu_index_32 = (env->hflags & HF_LMA_MASK) ? 0 : 1; + int mmu_index_base = + !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : + (pl < 3 && (env->eflags & AC_MASK) + ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX); + + return mmu_index_base + mmu_index_32; +} + +int cpu_mmu_index_kernel(CPUX86State *env) +{ + return x86_mmu_index_kernel_pl(env, env->hflags & HF_CPL_MASK); +} + static void x86_disas_set_info(CPUState *cs, disassemble_info *info) { X86CPU *cpu = X86_CPU(cs); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c43ac01c79..1e121acef5 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2445,15 +2445,8 @@ static inline bool is_mmu_index_32(int mmu_index) return mmu_index & 1; } -static inline int cpu_mmu_index_kernel(CPUX86State *env) -{ - int mmu_index_32 = (env->hflags & HF_LMA_MASK) ? 0 : 1; - int mmu_index_base = - !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : - ((env->hflags & HF_CPL_MASK) < 3 && (env->eflags & AC_MASK)) ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX; - - return mmu_index_base + mmu_index_32; -} +int x86_mmu_index_pl(CPUX86State *env, unsigned pl); +int cpu_mmu_index_kernel(CPUX86State *env); #define CC_DST (env->cc_dst) #define CC_SRC (env->cc_src) From 8053862af969a934dca67da9b38992e48fa1a95d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 18 Jun 2024 09:21:52 +0200 Subject: [PATCH 2127/2884] target/i386/tcg: Compute MMU index once Add the MMU index to the StackAccess struct, so that it can be cached or (in the next patch) computed from information that is not in CPUX86State. Co-developed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index b6902ca3fb..8a6d92b358 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -56,36 +56,37 @@ typedef struct StackAccess target_ulong ss_base; target_ulong sp; target_ulong sp_mask; + int mmu_index; } StackAccess; static void pushw(StackAccess *sa, uint16_t val) { sa->sp -= 2; - cpu_stw_kernel_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), - val, sa->ra); + cpu_stw_mmuidx_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), + val, sa->mmu_index, sa->ra); } static void pushl(StackAccess *sa, uint32_t val) { sa->sp -= 4; - cpu_stl_kernel_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), - val, sa->ra); + cpu_stl_mmuidx_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), + val, sa->mmu_index, sa->ra); } static uint16_t popw(StackAccess *sa) { - uint16_t ret = cpu_lduw_data_ra(sa->env, - sa->ss_base + (sa->sp & sa->sp_mask), - sa->ra); + uint16_t ret = cpu_lduw_mmuidx_ra(sa->env, + sa->ss_base + (sa->sp & sa->sp_mask), + sa->mmu_index, sa->ra); sa->sp += 2; return ret; } static uint32_t popl(StackAccess *sa) { - uint32_t ret = cpu_ldl_data_ra(sa->env, - sa->ss_base + (sa->sp & sa->sp_mask), - sa->ra); + uint32_t ret = cpu_ldl_mmuidx_ra(sa->env, + sa->ss_base + (sa->sp & sa->sp_mask), + sa->mmu_index, sa->ra); sa->sp += 4; return ret; } @@ -677,6 +678,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, sa.env = env; sa.ra = 0; + sa.mmu_index = cpu_mmu_index_kernel(env); if (type == 5) { /* task gate */ @@ -858,12 +860,12 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, static void pushq(StackAccess *sa, uint64_t val) { sa->sp -= 8; - cpu_stq_kernel_ra(sa->env, sa->sp, val, sa->ra); + cpu_stq_mmuidx_ra(sa->env, sa->sp, val, sa->mmu_index, sa->ra); } static uint64_t popq(StackAccess *sa) { - uint64_t ret = cpu_ldq_data_ra(sa->env, sa->sp, sa->ra); + uint64_t ret = cpu_ldq_mmuidx_ra(sa->env, sa->sp, sa->mmu_index, sa->ra); sa->sp += 8; return ret; } @@ -982,6 +984,7 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, sa.env = env; sa.ra = 0; + sa.mmu_index = cpu_mmu_index_kernel(env); sa.sp_mask = -1; sa.ss_base = 0; if (dpl < cpl || ist != 0) { @@ -1116,6 +1119,7 @@ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, sa.sp = env->regs[R_ESP]; sa.sp_mask = 0xffff; sa.ss_base = env->segs[R_SS].base; + sa.mmu_index = cpu_mmu_index_kernel(env); if (is_int) { old_eip = next_eip; @@ -1579,6 +1583,7 @@ void helper_lcall_real(CPUX86State *env, uint32_t new_cs, uint32_t new_eip, sa.sp = env->regs[R_ESP]; sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); sa.ss_base = env->segs[R_SS].base; + sa.mmu_index = cpu_mmu_index_kernel(env); if (shift) { pushl(&sa, env->segs[R_CS].selector); @@ -1618,6 +1623,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, sa.env = env; sa.ra = GETPC(); + sa.mmu_index = cpu_mmu_index_kernel(env); if (e2 & DESC_S_MASK) { if (!(e2 & DESC_CS_MASK)) { @@ -1905,6 +1911,7 @@ void helper_iret_real(CPUX86State *env, int shift) sa.env = env; sa.ra = GETPC(); + sa.mmu_index = x86_mmu_index_pl(env, 0); sa.sp_mask = 0xffff; /* XXXX: use SS segment size? */ sa.sp = env->regs[R_ESP]; sa.ss_base = env->segs[R_SS].base; @@ -1976,8 +1983,11 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, target_ulong new_eip, new_esp; StackAccess sa; + cpl = env->hflags & HF_CPL_MASK; + sa.env = env; sa.ra = retaddr; + sa.mmu_index = x86_mmu_index_pl(env, cpl); #ifdef TARGET_X86_64 if (shift == 2) { @@ -2032,7 +2042,6 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, !(e2 & DESC_CS_MASK)) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } - cpl = env->hflags & HF_CPL_MASK; rpl = new_cs & 3; if (rpl < cpl) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); From 05d41bbcb34ee30465517229a888da93666b4f3f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 19 Jun 2024 14:24:09 +0200 Subject: [PATCH 2128/2884] target/i386/tcg: check for correct busy state before switching to a new task This step is listed in the Intel manual: "Checks that the new task is available (call, jump, exception, or interrupt) or busy (IRET return)". The AMD manual lists the same operation under the "Preventing recursion" paragraph of "12.3.4 Nesting Tasks", though it is not clear if the processor checks the busy bit in the IRET case. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 8a6d92b358..a5d5ce61f5 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -369,6 +369,11 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, old_tss_limit_max = 43; } + /* new TSS must be busy iff the source is an IRET instruction */ + if (!!(e2 & DESC_TSS_BUSY_MASK) != (source == SWITCH_TSS_IRET)) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); + } + /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ From 8b131065080af3cf2dda04e4e190c5a74fec2f31 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 18 Jun 2024 09:13:49 +0200 Subject: [PATCH 2129/2884] target/i386/tcg: use X86Access for TSS access This takes care of probing the vaddr range in advance, and is also faster because it avoids repeated TLB lookups. It also matches the Intel manual better, as it says "Checks that the current (old) TSS, new TSS, and all segment descriptors used in the task switch are paged into system memory"; note however that it's not clear how the processor checks for segment descriptors, and this check is not included in the AMD manual. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 110 ++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index a5d5ce61f5..36d2f089ca 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -27,6 +27,7 @@ #include "exec/log.h" #include "helper-tcg.h" #include "seg_helper.h" +#include "access.h" #ifdef TARGET_X86_64 #define SET_ESP(val, sp_mask) \ @@ -313,14 +314,15 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, uint32_t e1, uint32_t e2, int source, uint32_t next_eip, uintptr_t retaddr) { - int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; + int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, i; target_ulong tss_base; uint32_t new_regs[8], new_segs[6]; uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; uint32_t old_eflags, eflags_mask; SegmentCache *dt; - int index; + int mmu_index, index; target_ulong ptr; + X86Access old, new; type = (e2 >> DESC_TYPE_SHIFT) & 0xf; LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, @@ -374,35 +376,45 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); } + /* X86Access avoids memory exceptions during the task switch */ + mmu_index = cpu_mmu_index_kernel(env); + access_prepare_mmu(&old, env, env->tr.base, old_tss_limit_max, + MMU_DATA_STORE, mmu_index, retaddr); + + if (source == SWITCH_TSS_CALL) { + /* Probe for future write of parent task */ + probe_access(env, tss_base, 2, MMU_DATA_STORE, + mmu_index, retaddr); + } + access_prepare_mmu(&new, env, tss_base, tss_limit, + MMU_DATA_LOAD, mmu_index, retaddr); + /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ - new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr); - new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr); - new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr); + new_cr3 = access_ldl(&new, tss_base + 0x1c); + new_eip = access_ldl(&new, tss_base + 0x20); + new_eflags = access_ldl(&new, tss_base + 0x24); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4), - retaddr); + new_regs[i] = access_ldl(&new, tss_base + (0x28 + i * 4)); } for (i = 0; i < 6; i++) { - new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4), - retaddr); + new_segs[i] = access_ldw(&new, tss_base + (0x48 + i * 4)); } - new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr); - new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr); + new_ldt = access_ldw(&new, tss_base + 0x60); + new_trap = access_ldl(&new, tss_base + 0x64); } else { /* 16 bit */ new_cr3 = 0; - new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr); - new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr); + new_eip = access_ldw(&new, tss_base + 0x0e); + new_eflags = access_ldw(&new, tss_base + 0x10); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2), retaddr); + new_regs[i] = access_ldw(&new, tss_base + (0x12 + i * 2)); } for (i = 0; i < 4; i++) { - new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 2), - retaddr); + new_segs[i] = access_ldw(&new, tss_base + (0x22 + i * 2)); } - new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr); + new_ldt = access_ldw(&new, tss_base + 0x2a); new_segs[R_FS] = 0; new_segs[R_GS] = 0; new_trap = 0; @@ -412,16 +424,6 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */ (void)new_trap; - /* NOTE: we must avoid memory exceptions during the task switch, - so we make dummy accesses before */ - /* XXX: it can still fail in some cases, so a bigger hack is - necessary to valid the TLB after having done the accesses */ - - v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr); - v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr); - cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr); - cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr); - /* clear busy bit (it is restartable) */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { tss_set_busy(env, env->tr.selector, 0, retaddr); @@ -434,35 +436,35 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, /* save the current state in the old TSS */ if (old_type & 8) { /* 32 bit */ - cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr); - cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr); + access_stl(&old, env->tr.base + 0x20, next_eip); + access_stl(&old, env->tr.base + 0x24, old_eflags); + access_stl(&old, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); + access_stl(&old, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); + access_stl(&old, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); + access_stl(&old, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); + access_stl(&old, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); + access_stl(&old, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); + access_stl(&old, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); + access_stl(&old, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); for (i = 0; i < 6; i++) { - cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4), - env->segs[i].selector, retaddr); + access_stw(&old, env->tr.base + (0x48 + i * 4), + env->segs[i].selector); } } else { /* 16 bit */ - cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr); - cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr); + access_stw(&old, env->tr.base + 0x0e, next_eip); + access_stw(&old, env->tr.base + 0x10, old_eflags); + access_stw(&old, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); + access_stw(&old, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); + access_stw(&old, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); + access_stw(&old, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); + access_stw(&old, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); + access_stw(&old, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); + access_stw(&old, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); + access_stw(&old, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); for (i = 0; i < 4; i++) { - cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 2), - env->segs[i].selector, retaddr); + access_stw(&old, env->tr.base + (0x22 + i * 2), + env->segs[i].selector); } } @@ -470,7 +472,11 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, context */ if (source == SWITCH_TSS_CALL) { - cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr); + /* + * Thanks to the probe_access above, we know the first two + * bytes addressed by &new are writable too. + */ + access_stw(&new, tss_base, env->tr.selector); new_eflags |= NT_MASK; } From 6a079f2e68e1832ebca0e7d64bc31ffebde9b2dd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 19 Jun 2024 14:33:39 +0200 Subject: [PATCH 2130/2884] target/i386/tcg: save current task state before loading new one This is how the steps are ordered in the manual. EFLAGS.NT is overwritten after the fact in the saved image. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/seg_helper.c | 85 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 36d2f089ca..aac092a356 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -389,6 +389,42 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, access_prepare_mmu(&new, env, tss_base, tss_limit, MMU_DATA_LOAD, mmu_index, retaddr); + /* save the current state in the old TSS */ + old_eflags = cpu_compute_eflags(env); + if (old_type & 8) { + /* 32 bit */ + access_stl(&old, env->tr.base + 0x20, next_eip); + access_stl(&old, env->tr.base + 0x24, old_eflags); + access_stl(&old, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); + access_stl(&old, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); + access_stl(&old, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); + access_stl(&old, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); + access_stl(&old, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); + access_stl(&old, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); + access_stl(&old, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); + access_stl(&old, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); + for (i = 0; i < 6; i++) { + access_stw(&old, env->tr.base + (0x48 + i * 4), + env->segs[i].selector); + } + } else { + /* 16 bit */ + access_stw(&old, env->tr.base + 0x0e, next_eip); + access_stw(&old, env->tr.base + 0x10, old_eflags); + access_stw(&old, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); + access_stw(&old, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); + access_stw(&old, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); + access_stw(&old, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); + access_stw(&old, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); + access_stw(&old, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); + access_stw(&old, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); + access_stw(&old, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); + for (i = 0; i < 4; i++) { + access_stw(&old, env->tr.base + (0x22 + i * 2), + env->segs[i].selector); + } + } + /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ @@ -428,49 +464,16 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { tss_set_busy(env, env->tr.selector, 0, retaddr); } - old_eflags = cpu_compute_eflags(env); + if (source == SWITCH_TSS_IRET) { old_eflags &= ~NT_MASK; + if (old_type & 8) { + access_stl(&old, env->tr.base + 0x24, old_eflags); + } else { + access_stw(&old, env->tr.base + 0x10, old_eflags); + } } - /* save the current state in the old TSS */ - if (old_type & 8) { - /* 32 bit */ - access_stl(&old, env->tr.base + 0x20, next_eip); - access_stl(&old, env->tr.base + 0x24, old_eflags); - access_stl(&old, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); - access_stl(&old, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); - access_stl(&old, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); - access_stl(&old, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); - access_stl(&old, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); - access_stl(&old, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); - access_stl(&old, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); - access_stl(&old, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); - for (i = 0; i < 6; i++) { - access_stw(&old, env->tr.base + (0x48 + i * 4), - env->segs[i].selector); - } - } else { - /* 16 bit */ - access_stw(&old, env->tr.base + 0x0e, next_eip); - access_stw(&old, env->tr.base + 0x10, old_eflags); - access_stw(&old, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); - access_stw(&old, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); - access_stw(&old, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); - access_stw(&old, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); - access_stw(&old, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); - access_stw(&old, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); - access_stw(&old, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); - access_stw(&old, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); - for (i = 0; i < 4; i++) { - access_stw(&old, env->tr.base + (0x22 + i * 2), - env->segs[i].selector); - } - } - - /* now if an exception occurs, it will occurs in the next task - context */ - if (source == SWITCH_TSS_CALL) { /* * Thanks to the probe_access above, we know the first two @@ -486,7 +489,9 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, } /* set the new CPU state */ - /* from this point, any exception which occurs can give problems */ + + /* now if an exception occurs, it will occur in the next task context */ + env->cr[0] |= CR0_TS_MASK; env->hflags |= HF_TS_MASK; env->tr.selector = tss_selector; From c88d07488c7d9cfdb755d460c63ca80aba323465 Mon Sep 17 00:00:00 2001 From: Gregor Haas <gregorhaas1997@gmail.com> Date: Fri, 28 Jun 2024 11:27:06 -0700 Subject: [PATCH 2131/2884] hw/core/loader: allow loading larger ROMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The read() syscall is not guaranteed to return all data from a file. The default ROM loader implementation currently does not take this into account, instead failing if all bytes are not read at once. This change loads the ROM using g_file_get_contents() instead, which correctly reads all data using multiple calls to read() while also returning the loaded ROM size. Signed-off-by: Gregor Haas <gregorhaas1997@gmail.com> Reviewed-by: Xingtao Yao <yaoxt.fnst@fujitsu.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240628182706.99525-1-gregorhaas1997@gmail.com> [PMD: Use gsize with g_file_get_contents()] Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/core/loader.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index a3bea1e718..39bd8f9e4d 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1076,8 +1076,8 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; - ssize_t rc; - int fd = -1; + gsize size; + g_autoptr(GError) gerr = NULL; char devpath[100]; if (as && mr) { @@ -1095,10 +1095,10 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, rom->path = g_strdup(file); } - fd = open(rom->path, O_RDONLY | O_BINARY); - if (fd == -1) { - fprintf(stderr, "Could not open option rom '%s': %s\n", - rom->path, strerror(errno)); + if (!g_file_get_contents(rom->path, (gchar **) &rom->data, + &size, &gerr)) { + fprintf(stderr, "rom: file %-20s: error %s\n", + rom->name, gerr->message); goto err; } @@ -1107,23 +1107,8 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, rom->fw_file = g_strdup(file); } rom->addr = addr; - rom->romsize = lseek(fd, 0, SEEK_END); - if (rom->romsize == -1) { - fprintf(stderr, "rom: file %-20s: get size error: %s\n", - rom->name, strerror(errno)); - goto err; - } - + rom->romsize = size; rom->datasize = rom->romsize; - rom->data = g_malloc0(rom->datasize); - lseek(fd, 0, SEEK_SET); - rc = read(fd, rom->data, rom->datasize); - if (rc != rom->datasize) { - fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n", - rom->name, rc, rom->datasize); - goto err; - } - close(fd); rom_insert(rom); if (rom->fw_file && fw_cfg) { const char *basename; @@ -1160,9 +1145,6 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, return 0; err: - if (fd != -1) - close(fd); - rom_free(rom); return -1; } From 9a365c25362c0d6b4cc0a53f88aae22b7d2532c6 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow <shentey@gmail.com> Date: Thu, 4 Jul 2024 22:58:52 +0200 Subject: [PATCH 2132/2884] hw/isa/vt82c686: Turn "intr" irq into a named gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the code more comprehensible, matches the datasheet and the piix4 device model. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240704205854.18537-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/isa/vt82c686.c | 2 +- hw/mips/fuloong2e.c | 2 +- hw/ppc/amigaone.c | 5 +++-- hw/ppc/pegasos2.c | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 8582ac0322..505b44c4e6 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -719,7 +719,7 @@ static void via_isa_realize(PCIDevice *d, Error **errp) ISABus *isa_bus; int i; - qdev_init_gpio_out(dev, &s->cpu_intr, 1); + qdev_init_gpio_out_named(dev, &s->cpu_intr, "intr", 1); qdev_init_gpio_in_named(dev, via_isa_pirq, "pirq", PCI_NUM_PINS); isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d), diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index a45aac368c..6e4303ba47 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -299,7 +299,7 @@ static void mips_fuloong2e_init(MachineState *machine) object_resolve_path_component(OBJECT(pci_dev), "rtc"), "date"); - qdev_connect_gpio_out(DEVICE(pci_dev), 0, env->irq[5]); + qdev_connect_gpio_out_named(DEVICE(pci_dev), "intr", 0, env->irq[5]); dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); pci_ide_create_devs(PCI_DEVICE(dev)); diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index ddfa09457a..900f93c15e 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -153,8 +153,9 @@ static void amigaone_init(MachineState *machine) object_property_add_alias(OBJECT(machine), "rtc-time", object_resolve_path_component(via, "rtc"), "date"); - qdev_connect_gpio_out(DEVICE(via), 0, - qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); + qdev_connect_gpio_out_named(DEVICE(via), "intr", 0, + qdev_get_gpio_in(DEVICE(cpu), + PPC6xx_INPUT_INT)); for (i = 0; i < PCI_NUM_PINS; i++) { qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via), "pirq", i)); diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index c1bd8dfa21..9b0a6b70ab 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -195,8 +195,8 @@ static void pegasos2_init(MachineState *machine) object_property_add_alias(OBJECT(machine), "rtc-time", object_resolve_path_component(via, "rtc"), "date"); - qdev_connect_gpio_out(DEVICE(via), 0, - qdev_get_gpio_in_named(pm->mv, "gpp", 31)); + qdev_connect_gpio_out_named(DEVICE(via), "intr", 0, + qdev_get_gpio_in_named(pm->mv, "gpp", 31)); dev = PCI_DEVICE(object_resolve_path_component(via, "ide")); pci_ide_create_devs(dev); From 1ee5f645721832a1285157f6ee2a34cc549c30d2 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 8 Jul 2024 16:33:12 +0100 Subject: [PATCH 2133/2884] include/hw/qdev-core.h: Correct and clarify gpio doc comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The doc comments for the functions for named GPIO inputs and outputs had a couple of problems: * some copy-and-paste errors meant the qdev_connect_gpio_out_named() doc comment had references to input GPIOs that should be to output GPIOs * it wasn't very clear that named GPIOs are arrays and so the connect functions specify a single GPIO line by giving both the name of the array and the index within that array Fix the copy-and-paste errors and slightly expand the text to say that functions are connecting one line in a named GPIO array, not a single named GPIO line. Reported-by: BALATON Zoltan <balaton@eik.bme.hu> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240708153312.3109380-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- include/hw/qdev-core.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 5336728a23..77bfcbdf73 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -624,8 +624,9 @@ qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); * @name: Name of the input GPIO array * @n: Number of the GPIO line in that array (which must be in range) * - * Returns the qemu_irq corresponding to a named input GPIO line - * (which the device has set up with qdev_init_gpio_in_named()). + * Returns the qemu_irq corresponding to a single input GPIO line + * in a named array of input GPIO lines on a device (which the device + * has set up with qdev_init_gpio_in_named()). * The @name string must correspond to an input GPIO array which exists on * the device, and the index @n of the GPIO line must be valid (i.e. * be at least 0 and less than the total number of input GPIOs in that @@ -673,15 +674,15 @@ void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); * GPIO lines * @dev: Device whose GPIO to connect * @name: Name of the output GPIO array - * @n: Number of the anonymous output GPIO line (which must be in range) + * @n: Number of the output GPIO line within that array (which must be in range) * @input_pin: qemu_irq to connect the output line to * - * This function connects an anonymous output GPIO line on a device - * up to an arbitrary qemu_irq, so that when the device asserts that - * output GPIO line, the qemu_irq's callback is invoked. + * This function connects a single GPIO output in a named array of output + * GPIO lines on a device up to an arbitrary qemu_irq, so that when the + * device asserts that output GPIO line, the qemu_irq's callback is invoked. * The @name string must correspond to an output GPIO array which exists on * the device, and the index @n of the GPIO line must be valid (i.e. - * be at least 0 and less than the total number of input GPIOs in that + * be at least 0 and less than the total number of output GPIOs in that * array); this function will assert() if passed an invalid name or index. * * Outbound GPIO lines can be connected to any qemu_irq, but the common @@ -796,7 +797,7 @@ void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); * @dev: Device to create output GPIOs for * @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines * @name: Name to give this array of GPIO lines - * @n: Number of GPIO lines to create + * @n: Number of GPIO lines to create in this array * * Like qdev_init_gpio_out(), but creates an array of GPIO output lines * with a name. Code using the device can then connect these GPIO lines From a376a8d58a164c28a8402d0ea7b05c1235c02f7a Mon Sep 17 00:00:00 2001 From: Ani Sinha <anisinha@redhat.com> Date: Thu, 11 Jul 2024 12:54:47 +0530 Subject: [PATCH 2134/2884] loader: remove load_image_gzipped function as its not used anywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_image_gzipped() does not seem to be used anywhere. Remove it. Signed-off-by: Ani Sinha <anisinha@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240711072448.32673-1-anisinha@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/core/loader.c | 13 ------------- include/hw/loader.h | 4 +--- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 39bd8f9e4d..31593a1171 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -845,19 +845,6 @@ ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, return ret; } -/* Load a gzip-compressed kernel. */ -ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) -{ - ssize_t bytes; - uint8_t *data; - - bytes = load_image_gzipped_buffer(filename, max_sz, &data); - if (bytes != -1) { - rom_add_blob_fixed(filename, data, bytes, addr); - g_free(data); - } - return bytes; -} /* The PE/COFF MS-DOS stub magic number */ #define EFI_PE_MSDOS_MAGIC "MZ" diff --git a/include/hw/loader.h b/include/hw/loader.h index 9844c5e3cf..7f6d06b956 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -77,15 +77,13 @@ ssize_t load_image_targphys(const char *filename, hwaddr, ssize_t load_image_mr(const char *filename, MemoryRegion *mr); /* This is the limit on the maximum uncompressed image size that - * load_image_gzipped_buffer() and load_image_gzipped() will read. It prevents + * load_image_gzipped_buffer() will read. It prevents * g_malloc() in those functions from allocating a huge amount of memory. */ #define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, uint8_t **buffer); -ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); - /** * unpack_efi_zboot_image: * @buffer: pointer to a variable holding the address of a buffer containing the From de680286b527965a503b87dda59ebc8f539684f2 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 12 Jul 2024 12:39:49 +0100 Subject: [PATCH 2135/2884] accel/tcg: Make cpu_exec_interrupt hook mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TCGCPUOps::cpu_exec_interrupt hook is currently not mandatory; if it is left NULL then we treat it as if it had returned false. However since pretty much every architecture needs to handle interrupts, almost every target we have provides the hook. The one exception is Tricore, which doesn't currently implement the architectural interrupt handling. Add a "do nothing" implementation of cpu_exec_hook for Tricore, assert on startup that the CPU does provide the hook, and remove the runtime NULL check before calling it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240712113949.4146855-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- accel/tcg/cpu-exec.c | 4 ++-- target/tricore/cpu.c | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 245fd6327d..9010dad073 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -857,8 +857,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - if (tcg_ops->cpu_exec_interrupt && - tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { + if (tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { if (!tcg_ops->need_replay_interrupt || tcg_ops->need_replay_interrupt(interrupt_request)) { replay_interrupt(); @@ -1080,6 +1079,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) /* Check mandatory TCGCPUOps handlers */ #ifndef CONFIG_USER_ONLY assert(cpu->cc->tcg_ops->cpu_exec_halt); + assert(cpu->cc->tcg_ops->cpu_exec_interrupt); #endif /* !CONFIG_USER_ONLY */ cpu->cc->tcg_ops->initialize(); tcg_target_initialized = true; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 4d9c0368f2..1a26171590 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -155,6 +155,11 @@ static void tc37x_initfn(Object *obj) set_feature(&cpu->env, TRICORE_FEATURE_162); } +static bool tricore_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + /* Interrupts are not implemented */ + return false; +} #include "hw/core/sysemu-cpu-ops.h" @@ -169,6 +174,7 @@ static const TCGCPUOps tricore_tcg_ops = { .synchronize_from_tb = tricore_cpu_synchronize_from_tb, .restore_state_to_opc = tricore_restore_state_to_opc, .tlb_fill = tricore_cpu_tlb_fill, + .cpu_exec_interrupt = tricore_cpu_exec_interrupt, .cpu_exec_halt = tricore_cpu_has_work, }; From 6b6593107d65113d3b83a936b06bab6c1c9fafe0 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 12 Jul 2024 22:02:43 +1000 Subject: [PATCH 2136/2884] system/cpus: Add cpu_pause() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This factors the CPU pause function from pause_all_vcpus() into a new cpu_pause() function, similarly to cpu_resume(). cpu_resume() is moved to keep it next to cpu_pause(). Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Peter Xu <peterx@redhat.com> Cc: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Peter Xu <peterx@redhat.com> Message-ID: <20240712120247.477133-17-npiggin@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- include/hw/core/cpu.h | 8 ++++++++ system/cpus.c | 30 +++++++++++++++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index a2c8536943..e6acfcb59a 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -984,6 +984,14 @@ void cpu_reset_interrupt(CPUState *cpu, int mask); */ void cpu_exit(CPUState *cpu); +/** + * cpu_pause: + * @cpu: The CPU to pause. + * + * Pauses CPU, i.e. puts CPU into stopped state. + */ +void cpu_pause(CPUState *cpu); + /** * cpu_resume: * @cpu: The CPU to resume. diff --git a/system/cpus.c b/system/cpus.c index d3640c9503..5e3a988a0a 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -568,6 +568,22 @@ void cpu_thread_signal_destroyed(CPUState *cpu) qemu_cond_signal(&qemu_cpu_cond); } +void cpu_pause(CPUState *cpu) +{ + if (qemu_cpu_is_self(cpu)) { + qemu_cpu_stop(cpu, true); + } else { + cpu->stop = true; + qemu_cpu_kick(cpu); + } +} + +void cpu_resume(CPUState *cpu) +{ + cpu->stop = false; + cpu->stopped = false; + qemu_cpu_kick(cpu); +} static bool all_vcpus_paused(void) { @@ -588,12 +604,7 @@ void pause_all_vcpus(void) qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false); CPU_FOREACH(cpu) { - if (qemu_cpu_is_self(cpu)) { - qemu_cpu_stop(cpu, true); - } else { - cpu->stop = true; - qemu_cpu_kick(cpu); - } + cpu_pause(cpu); } /* We need to drop the replay_lock so any vCPU threads woken up @@ -613,13 +624,6 @@ void pause_all_vcpus(void) bql_lock(); } -void cpu_resume(CPUState *cpu) -{ - cpu->stop = false; - cpu->stopped = false; - qemu_cpu_kick(cpu); -} - void resume_all_vcpus(void) { CPUState *cpu; From dfaf55a19ab0e0afba8aa34c7fb04c1566e41519 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Date: Sat, 13 Jul 2024 23:42:49 +0100 Subject: [PATCH 2137/2884] esp: remove transfer size check from DMA DATA IN and DATA OUT transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transfer size check was originally added to prevent consecutive DMA TI commands from causing an assert() due to an existing SCSI request being in progress, but since the last set of updates [*] this is no longer required. Remove the transfer size check from DMA DATA IN and DATA OUT transfers so that issuing a DMA TI command when there is no data left to transfer does not cause an assert() due to an existing SCSI request being in progress. [*] See commits f3ace75be8..78d68f312a Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2415 Message-ID: <20240713224249.468084-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/scsi/esp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 5d9b52632e..8504dd30a0 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -594,7 +594,7 @@ static void esp_do_dma(ESPState *s) if (!s->current_req) { return; } - if (s->async_len == 0 && esp_get_tc(s) && s->ti_size) { + if (s->async_len == 0 && esp_get_tc(s)) { /* Defer until data is available. */ return; } @@ -647,7 +647,7 @@ static void esp_do_dma(ESPState *s) if (!s->current_req) { return; } - if (s->async_len == 0 && esp_get_tc(s) && s->ti_size) { + if (s->async_len == 0 && esp_get_tc(s)) { /* Defer until data is available. */ return; } From 3f5ef05fe029419159fc67772b0bc1cc81e633bd Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:25:42 +0900 Subject: [PATCH 2138/2884] ui/cocoa: Release CGColorSpace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CGImageCreate | Apple Developer Documentation https://developer.apple.com/documentation/coregraphics/1455149-cgimagecreate > The color space is retained; on return, you may safely release it. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Tested-by: Phil Dennis-Jordan <phil@philjordan.eu> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240715-cursor-v3-1-afa5b9492dbf@daynix.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- ui/cocoa.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 2935247cdd..79a054b128 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -307,6 +307,7 @@ static void handleAnyDeviceErrors(Error * err) BOOL isMouseGrabbed; BOOL isAbsoluteEnabled; CFMachPortRef eventsTap; + CGColorSpaceRef colorspace; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; @@ -359,6 +360,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [trackingArea release]; screen.width = frameRect.size.width; screen.height = frameRect.size.height; + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0 [self setClipsToBounds:YES]; #endif @@ -379,6 +381,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven CFRelease(eventsTap); } + CGColorSpaceRelease(colorspace); [super dealloc]; } @@ -456,7 +459,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven DIV_ROUND_UP(bitsPerPixel, 8) * 2, //bitsPerComponent bitsPerPixel, //bitsPerPixel stride, //bytesPerRow - CGColorSpaceCreateWithName(kCGColorSpaceSRGB), //colorspace + colorspace, //colorspace kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, //bitmapInfo dataProviderRef, //provider NULL, //decode From a418e7aeea14d1cbe7dc9160aa0874bc1056ae74 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:25:43 +0900 Subject: [PATCH 2139/2884] ui/console: Convert mouse visibility parameter into bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Tested-by: Phil Dennis-Jordan <phil@philjordan.eu> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240715-cursor-v3-2-afa5b9492dbf@daynix.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/display/ati.c | 2 +- hw/display/virtio-gpu.c | 3 +-- hw/display/vmware_vga.c | 2 +- include/ui/console.h | 4 ++-- ui/console.c | 5 +++-- ui/dbus-listener.c | 2 +- ui/gtk.c | 2 +- ui/sdl2.c | 4 ++-- ui/spice-display.c | 11 ++++++----- ui/vnc.c | 2 +- 10 files changed, 19 insertions(+), 18 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 8d2501bd82..b1f94f5b76 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -742,7 +742,7 @@ static void ati_mm_write(void *opaque, hwaddr addr, if (!s->cursor_guest_mode && (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(t & BIT(31))) { dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16, - s->regs.cur_hv_pos & 0xffff, 1); + s->regs.cur_hv_pos & 0xffff, true); } break; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index d60b1b2973..3281842bfe 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -109,8 +109,7 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) s->cursor.pos.x = cursor->pos.x; s->cursor.pos.y = cursor->pos.y; } - dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, - cursor->resource_id ? 1 : 0); + dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, cursor->resource_id); } struct virtio_gpu_simple_resource * diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 1c0f9d9a99..512f224b9f 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1167,7 +1167,7 @@ static void vmsvga_reset(DeviceState *dev) s->enable = 0; s->config = 0; s->svgaid = SVGA_ID; - s->cursor.on = 0; + s->cursor.on = false; s->redraw_fifo_last = 0; s->syncing = 0; diff --git a/include/ui/console.h b/include/ui/console.h index a208a68b88..82b573e680 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -233,7 +233,7 @@ typedef struct DisplayChangeListenerOps { /* optional */ void (*dpy_mouse_set)(DisplayChangeListener *dcl, - int x, int y, int on); + int x, int y, bool on); /* optional */ void (*dpy_cursor_define)(DisplayChangeListener *dcl, QEMUCursor *cursor); @@ -322,7 +322,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, void dpy_text_cursor(QemuConsole *con, int x, int y); void dpy_text_update(QemuConsole *con, int x, int y, int w, int h); void dpy_text_resize(QemuConsole *con, int w, int h); -void dpy_mouse_set(QemuConsole *con, int x, int y, int on); +void dpy_mouse_set(QemuConsole *con, int x, int y, bool on); void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor); bool dpy_cursor_define_supported(QemuConsole *con); bool dpy_gfx_check_format(QemuConsole *con, diff --git a/ui/console.c b/ui/console.c index e67c5dae2b..8e9cc1628a 100644 --- a/ui/console.c +++ b/ui/console.c @@ -49,7 +49,8 @@ typedef struct QemuGraphicConsole { uint32_t head; QEMUCursor *cursor; - int cursor_x, cursor_y, cursor_on; + int cursor_x, cursor_y; + bool cursor_on; } QemuGraphicConsole; typedef QemuConsoleClass QemuGraphicConsoleClass; @@ -957,7 +958,7 @@ void dpy_text_resize(QemuConsole *con, int w, int h) } } -void dpy_mouse_set(QemuConsole *c, int x, int y, int on) +void dpy_mouse_set(QemuConsole *c, int x, int y, bool on) { QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); DisplayState *s = c->ds; diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 5490088043..a54123acea 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -726,7 +726,7 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl, } static void dbus_mouse_set(DisplayChangeListener *dcl, - int x, int y, int on) + int x, int y, bool on) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); diff --git a/ui/gtk.c b/ui/gtk.c index 93b13b7a30..bc29f7a1b4 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -446,7 +446,7 @@ static GdkDevice *gd_get_pointer(GdkDisplay *dpy) } static void gd_mouse_set(DisplayChangeListener *dcl, - int x, int y, int visible) + int x, int y, bool visible) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkDisplay *dpy; diff --git a/ui/sdl2.c b/ui/sdl2.c index 0a0eb5a42d..98ed974371 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -49,7 +49,7 @@ static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static SDL_Cursor *sdl_cursor_normal; static SDL_Cursor *sdl_cursor_hidden; static int absolute_enabled; -static int guest_cursor; +static bool guest_cursor; static int guest_x, guest_y; static SDL_Cursor *guest_sprite; static Notifier mouse_mode_notifier; @@ -729,7 +729,7 @@ void sdl2_poll_events(struct sdl2_console *scon) } static void sdl_mouse_warp(DisplayChangeListener *dcl, - int x, int y, int on) + int x, int y, bool on) { struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); diff --git a/ui/spice-display.c b/ui/spice-display.c index 8a8472d029..c794ae0649 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -254,7 +254,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) static SimpleSpiceCursor* qemu_spice_create_cursor_update(SimpleSpiceDisplay *ssd, QEMUCursor *c, - int on) + bool on) { size_t size = c ? c->width * c->height * 4 : 0; SimpleSpiceCursor *update; @@ -448,7 +448,8 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, qemu_mutex_lock(&ssd->lock); if (ssd->cursor) { g_free(ssd->ptr_define); - ssd->ptr_define = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0); + ssd->ptr_define = + qemu_spice_create_cursor_update(ssd, ssd->cursor, false); } qemu_mutex_unlock(&ssd->lock); } @@ -476,7 +477,7 @@ void qemu_spice_cursor_refresh_bh(void *opaque) ssd->mouse_x = -1; ssd->mouse_y = -1; qemu_mutex_unlock(&ssd->lock); - dpy_mouse_set(ssd->dcl.con, x, y, 1); + dpy_mouse_set(ssd->dcl.con, x, y, true); } else { qemu_mutex_unlock(&ssd->lock); } @@ -747,7 +748,7 @@ static void display_refresh(DisplayChangeListener *dcl) } static void display_mouse_set(DisplayChangeListener *dcl, - int x, int y, int on) + int x, int y, bool on) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); @@ -774,7 +775,7 @@ static void display_mouse_define(DisplayChangeListener *dcl, g_free(ssd->ptr_move); ssd->ptr_move = NULL; g_free(ssd->ptr_define); - ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, 0); + ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, false); qemu_mutex_unlock(&ssd->lock); qemu_spice_wakeup(ssd); } diff --git a/ui/vnc.c b/ui/vnc.c index dd530f04e5..dae5d51210 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -981,7 +981,7 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) } static void vnc_mouse_set(DisplayChangeListener *dcl, - int x, int y, int visible) + int x, int y, bool visible) { /* can we ask the client(s) to move the pointer ??? */ } From d2277f02b819c795e671ce27f4a48d5e8fce97b9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:25:44 +0900 Subject: [PATCH 2140/2884] ui/cocoa: Add cursor composition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add accelerated cursor composition to ui/cocoa. This does not only improve performance for display devices that exposes the capability to the guest according to dpy_cursor_define_supported(), but fixes the cursor display for devices that unconditionally expects the availability of the capability (e.g., virtio-gpu). The common pattern to implement accelerated cursor composition is to replace the cursor and warp it so that the replaced cursor is shown at the correct position on the guest display for relative pointer devices. Unfortunately, ui/cocoa cannot do the same because warping the cursor position interfers with the mouse input so it uses CALayer instead; although it is not specialized for cursor composition, it still can compose images with hardware acceleration. Co-authored-by: Phil Dennis-Jordan <phil@philjordan.eu> Tested-by: Phil Dennis-Jordan <phil@philjordan.eu> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20240715-cursor-v3-3-afa5b9492dbf@daynix.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- meson.build | 3 +- ui/cocoa.m | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6a93da48e1..a1e51277b0 100644 --- a/meson.build +++ b/meson.build @@ -1070,7 +1070,8 @@ if get_option('attr').allowed() endif endif -cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'], +cocoa = dependency('appleframeworks', + modules: ['Cocoa', 'CoreVideo', 'QuartzCore'], required: get_option('cocoa')) vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet')) diff --git a/ui/cocoa.m b/ui/cocoa.m index 79a054b128..4c2dd33532 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #import <Cocoa/Cocoa.h> +#import <QuartzCore/QuartzCore.h> #include <crt_externs.h> #include "qemu/help-texts.h" @@ -79,12 +80,16 @@ static void cocoa_switch(DisplayChangeListener *dcl, DisplaySurface *surface); static void cocoa_refresh(DisplayChangeListener *dcl); +static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on); +static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor); static const DisplayChangeListenerOps dcl_ops = { .dpy_name = "cocoa", .dpy_gfx_update = cocoa_update, .dpy_gfx_switch = cocoa_switch, .dpy_refresh = cocoa_refresh, + .dpy_mouse_set = cocoa_mouse_set, + .dpy_cursor_define = cocoa_cursor_define, }; static DisplayChangeListener dcl = { .ops = &dcl_ops, @@ -308,6 +313,11 @@ static void handleAnyDeviceErrors(Error * err) BOOL isAbsoluteEnabled; CFMachPortRef eventsTap; CGColorSpaceRef colorspace; + CALayer *cursorLayer; + QEMUCursor *cursor; + int mouseX; + int mouseY; + bool mouseOn; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; @@ -364,6 +374,12 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0 [self setClipsToBounds:YES]; #endif + [self setWantsLayer:YES]; + cursorLayer = [[CALayer alloc] init]; + [cursorLayer setAnchorPoint:CGPointMake(0, 1)]; + [cursorLayer setAutoresizingMask:kCALayerMaxXMargin | + kCALayerMinYMargin]; + [[self layer] addSublayer:cursorLayer]; } return self; @@ -382,6 +398,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } CGColorSpaceRelease(colorspace); + [cursorLayer release]; + cursor_unref(cursor); [super dealloc]; } @@ -426,6 +444,72 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [NSCursor unhide]; } +- (void)setMouseX:(int)x y:(int)y on:(bool)on +{ + CGPoint position; + + mouseX = x; + mouseY = y; + mouseOn = on; + + position.x = mouseX; + position.y = screen.height - mouseY; + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [cursorLayer setPosition:position]; + [cursorLayer setHidden:!mouseOn]; + [CATransaction commit]; +} + +- (void)setCursor:(QEMUCursor *)given_cursor +{ + CGDataProviderRef provider; + CGImageRef image; + CGRect bounds = CGRectZero; + + cursor_unref(cursor); + cursor = given_cursor; + + if (!cursor) { + return; + } + + cursor_ref(cursor); + + bounds.size.width = cursor->width; + bounds.size.height = cursor->height; + + provider = CGDataProviderCreateWithData( + NULL, + cursor->data, + cursor->width * cursor->height * 4, + NULL + ); + + image = CGImageCreate( + cursor->width, //width + cursor->height, //height + 8, //bitsPerComponent + 32, //bitsPerPixel + cursor->width * 4, //bytesPerRow + colorspace, //colorspace + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, //bitmapInfo + provider, //provider + NULL, //decode + 0, //interpolate + kCGRenderingIntentDefault //intent + ); + + CGDataProviderRelease(provider); + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [cursorLayer setBounds:bounds]; + [cursorLayer setContents:(id)image]; + [CATransaction commit]; + CGImageRelease(image); +} + - (void) drawRect:(NSRect) rect { COCOA_DEBUG("QemuCocoaView: drawRect\n"); @@ -2015,6 +2099,21 @@ static void cocoa_refresh(DisplayChangeListener *dcl) [pool release]; } +static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView setMouseX:x y:y on:on]; + }); +} + +static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + BQL_LOCK_GUARD(); + [cocoaView setCursor:qemu_console_get_cursor(dcl->con)]; + }); +} + static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; From 4bba839808bb1c4f500a11462220a687b4d9ab25 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:25:45 +0900 Subject: [PATCH 2141/2884] ui/console: Remove dpy_cursor_define_supported() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove dpy_cursor_define_supported() as it brings no benefit today and it has a few inherent problems. All graphical displays except egl-headless support cursor composition without DMA-BUF, and egl-headless is meant to be used in conjunction with another graphical display, so dpy_cursor_define_supported() always returns true and meaningless. Even if we add a new display without cursor composition in the future, dpy_cursor_define_supported() will be problematic as a cursor display fix for it because some display devices like virtio-gpu cannot tell the lack of cursor composition capability to the guest and are unable to utilize the value the function returns. Therefore, all non-headless graphical displays must actually implement cursor composition for correct cursor display. Another problem with dpy_cursor_define_supported() is that it returns true even if only some of the display listeners support cursor composition, which is wrong unless all display listeners that lack cursor composition is headless. Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Phil Dennis-Jordan <phil@philjordan.eu> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20240715-cursor-v3-4-afa5b9492dbf@daynix.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/display/qxl-render.c | 4 ---- hw/display/vmware_vga.c | 6 ++---- include/ui/console.h | 1 - ui/console.c | 13 ------------- 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 8daae72c8d..335d01edde 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -307,10 +307,6 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) return 1; } - if (!dpy_cursor_define_supported(qxl->vga.con)) { - return 0; - } - if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) { fprintf(stderr, "%s", __func__); qxl_log_cmd_cursor(qxl, cmd, ext->group_id); diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 512f224b9f..3db3ff98f7 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -904,10 +904,8 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) caps |= SVGA_CAP_RECT_FILL; #endif #ifdef HW_MOUSE_ACCEL - if (dpy_cursor_define_supported(s->vga.con)) { - caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | - SVGA_CAP_CURSOR_BYPASS; - } + caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | + SVGA_CAP_CURSOR_BYPASS; #endif ret = caps; break; diff --git a/include/ui/console.h b/include/ui/console.h index 82b573e680..fa986ab97e 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -324,7 +324,6 @@ void dpy_text_update(QemuConsole *con, int x, int y, int w, int h); void dpy_text_resize(QemuConsole *con, int w, int h); void dpy_mouse_set(QemuConsole *con, int x, int y, bool on); void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor); -bool dpy_cursor_define_supported(QemuConsole *con); bool dpy_gfx_check_format(QemuConsole *con, pixman_format_code_t format); diff --git a/ui/console.c b/ui/console.c index 8e9cc1628a..e8f0083af7 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1001,19 +1001,6 @@ void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor) } } -bool dpy_cursor_define_supported(QemuConsole *con) -{ - DisplayState *s = con->ds; - DisplayChangeListener *dcl; - - QLIST_FOREACH(dcl, &s->listeners, next) { - if (dcl->ops->dpy_cursor_define) { - return true; - } - } - return false; -} - QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, struct QEMUGLParams *qparams) { From a99dc9cd611cbaf10edee6260272e299626d0871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Mon, 15 Jul 2024 15:44:20 +0400 Subject: [PATCH 2142/2884] vl: fix "type is NULL" in -vga help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't pass NULL to module_object_class_by_name(), when the interface is unavailable. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240715114420.2062870-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- system/vl.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/system/vl.c b/system/vl.c index bdd2f6ecf6..9e8f16f155 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1000,9 +1000,16 @@ static bool vga_interface_available(VGAInterfaceType t) const VGAInterfaceInfo *ti = &vga_interfaces[t]; assert(t < VGA_TYPE_MAX); - return !ti->class_names[0] || - module_object_class_by_name(ti->class_names[0]) || - module_object_class_by_name(ti->class_names[1]); + + if (!ti->class_names[0] || module_object_class_by_name(ti->class_names[0])) { + return true; + } + + if (ti->class_names[1] && module_object_class_by_name(ti->class_names[1])) { + return true; + } + + return false; } static const char * From 644a52778a90581dbda909f38b9eaf71501fd9cd Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan <zhenzhong.duan@intel.com> Date: Tue, 16 Jul 2024 14:42:12 +0800 Subject: [PATCH 2143/2884] system/physmem: use return value of ram_block_discard_require() as errno MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When ram_block_discard_require() fails, errno is passed to error_setg_errno(). It's a stale value or 0 which is unrelated to ram_block_discard_require(). As ram_block_discard_require() already returns -EBUSY in failure case, use it as errno for error_setg_errno(). Fixes: 852f0048f3ea ("make guest_memfd require uncoordinated discard") Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: David Hildenbrand <david@redhat.com> Message-ID: <20240716064213.290696-1-zhenzhong.duan@intel.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- system/physmem.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 2154432cb6..9a3b3a7636 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1845,11 +1845,14 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) } if (new_block->flags & RAM_GUEST_MEMFD) { + int ret; + assert(kvm_enabled()); assert(new_block->guest_memfd < 0); - if (ram_block_discard_require(true) < 0) { - error_setg_errno(errp, errno, + ret = ram_block_discard_require(true); + if (ret < 0) { + error_setg_errno(errp, -ret, "cannot set up private guest memory: discard currently blocked"); error_append_hint(errp, "Are you using assigned devices?\n"); goto out_free; From 1b5a561c7376189ff0afc2d081625e33ffd09478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <f4bug@amsat.org> Date: Mon, 30 May 2022 20:06:20 +0200 Subject: [PATCH 2144/2884] hw/sd/sdcard: Basis for eMMC support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since eMMC are soldered on boards, it is not user-creatable. RCA register is initialized to 0x0001, per spec v4.3, chapter 8.5 "RCA register": The default value of the RCA register is 0x0001. The value 0x0000 is reserved to set all cards into the Stand-by State with CMD7. The CSD register is very similar to SD one, except the version announced is v4.3. eMMC CID register is slightly different from SD: - One extra PNM (5 -> 6) - MDT is only 1 byte (2 -> 1). Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-2-philmd@linaro.org> --- hw/sd/sd.c | 107 ++++++++++++++++++++++++++++++++++++++++++++- include/hw/sd/sd.h | 3 ++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d6a07f0ade..ea01bf6e28 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2,6 +2,8 @@ * SD Memory Card emulation as defined in the "SD Memory Card Physical * layer specification, Version 2.00." * + * eMMC emulation defined in "JEDEC Standard No. 84-A43" + * * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org> * Copyright (c) 2007 CodeSourcery * Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -169,12 +171,18 @@ struct SDState { static void sd_realize(DeviceState *dev, Error **errp); static const SDProto sd_proto_spi; +static const SDProto sd_proto_emmc; static bool sd_is_spi(SDState *sd) { return sd->proto == &sd_proto_spi; } +static bool sd_is_emmc(SDState *sd) +{ + return sd->proto == &sd_proto_emmc; +} + static const char *sd_version_str(enum SDPhySpecificationVersion version) { static const char *sdphy_version[] = { @@ -438,6 +446,23 @@ static void sd_set_cid(SDState *sd) sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; } +static void emmc_set_cid(SDState *sd) +{ + sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */ + sd->cid[1] = 0b01; /* CBX: soldered BGA */ + sd->cid[2] = OID[0]; /* OEM/Application ID (OID) */ + sd->cid[3] = PNM[0]; /* Fake product name (PNM) */ + sd->cid[4] = PNM[1]; + sd->cid[5] = PNM[2]; + sd->cid[6] = PNM[3]; + sd->cid[7] = PNM[4]; + sd->cid[8] = PNM[4]; + sd->cid[9] = PRV; /* Fake product revision (PRV) */ + stl_be_p(&sd->cid[10], 0xdeadbeef); /* Fake serial number (PSN) */ + sd->cid[14] = (MDT_MON << 4) | (MDT_YR - 1997); /* Manufacture date (MDT) */ + sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; +} + /* Card-Specific Data register */ #define HWBLOCK_SHIFT 9 /* 512 bytes */ @@ -451,6 +476,44 @@ static const uint8_t sd_csd_rw_mask[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe, }; +static void emmc_set_csd(SDState *sd, uint64_t size) +{ + int hwblock_shift = HWBLOCK_SHIFT; + uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; + uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; + + sd->csd[0] = (3 << 6) | (4 << 2); /* Spec v4.3 with EXT_CSD */ + sd->csd[1] = (1 << 3) | 6; /* Asynchronous data access time: 1ms */ + sd->csd[2] = 0x00; + sd->csd[3] = (1 << 3) | 3;; /* Maximum bus clock frequency: 100MHz */ + sd->csd[4] = 0x0f; + if (size <= 2 * GiB) { + /* use 1k blocks */ + uint32_t csize1k = (size >> (CMULT_SHIFT + 10)) - 1; + sd->csd[5] = 0x5a; + sd->csd[6] = 0x80 | ((csize1k >> 10) & 0xf); + sd->csd[7] = (csize1k >> 2) & 0xff; + } else { /* >= 2GB : size stored in ext CSD, block addressing */ + sd->csd[5] = 0x59; + sd->csd[6] = 0x8f; + sd->csd[7] = 0xff; + sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1); + } + sd->csd[8] = 0xff; + sd->csd[9] = 0xfc | /* Max. write current */ + ((CMULT_SHIFT - 2) >> 1); + sd->csd[10] = 0x40 | /* Erase sector size */ + (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1); + sd->csd[11] = 0x00 | /* Write protect group size */ + ((sectsize << 7) & 0x80) | wpsize; + sd->csd[12] = 0x90 | /* Write speed factor */ + (hwblock_shift >> 2); + sd->csd[13] = 0x20 | /* Max. write data block length */ + ((hwblock_shift << 6) & 0xc0); + sd->csd[14] = 0x00; + sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; +} + static void sd_set_csd(SDState *sd, uint64_t size) { int hwblock_shift = HWBLOCK_SHIFT; @@ -697,7 +760,7 @@ static void sd_reset(DeviceState *dev) sd->state = sd_idle_state; /* card registers */ - sd->rca = 0x0000; + sd->rca = sd_is_emmc(sd) ? 0x0001 : 0x0000; sd->size = size; sd_set_ocr(sd); sd_set_scr(sd); @@ -2375,6 +2438,13 @@ static const SDProto sd_proto_sd = { }, }; +static const SDProto sd_proto_emmc = { + /* Only v4.3 is supported */ + .name = "eMMC", + .cmd = { + }, +}; + static void sd_instance_init(Object *obj) { SDState *sd = SDMMC_COMMON(obj); @@ -2446,6 +2516,15 @@ static void sd_realize(DeviceState *dev, Error **errp) } } +static void emmc_realize(DeviceState *dev, Error **errp) +{ + SDState *sd = SDMMC_COMMON(dev); + + sd->spec_version = SD_PHY_SPECv3_01_VERS; /* Actually v4.3 */ + + sd_realize(dev, errp); +} + static Property sdmmc_common_properties[] = { DEFINE_PROP_DRIVE("drive", SDState, blk), DEFINE_PROP_END_OF_LIST() @@ -2457,6 +2536,10 @@ static Property sd_properties[] = { DEFINE_PROP_END_OF_LIST() }; +static Property emmc_properties[] = { + DEFINE_PROP_END_OF_LIST() +}; + static void sdmmc_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2509,6 +2592,23 @@ static void sd_spi_class_init(ObjectClass *klass, void *data) sc->proto = &sd_proto_spi; } +static void emmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); + + dc->desc = "eMMC"; + dc->realize = emmc_realize; + device_class_set_props(dc, emmc_properties); + /* Reason: Soldered on board */ + dc->user_creatable = false; + + sc->proto = &sd_proto_emmc; + + sc->set_cid = emmc_set_cid; + sc->set_csd = emmc_set_csd; +} + static const TypeInfo sd_types[] = { { .name = TYPE_SDMMC_COMMON, @@ -2530,6 +2630,11 @@ static const TypeInfo sd_types[] = { .parent = TYPE_SD_CARD, .class_init = sd_spi_class_init, }, + { + .name = TYPE_EMMC, + .parent = TYPE_SDMMC_COMMON, + .class_init = emmc_class_init, + }, }; DEFINE_TYPES(sd_types) diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 0d6d9e452b..d35a839f5e 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -96,6 +96,9 @@ OBJECT_DECLARE_TYPE(SDState, SDCardClass, SD_CARD) #define TYPE_SD_CARD_SPI "sd-card-spi" DECLARE_INSTANCE_CHECKER(SDState, SD_CARD_SPI, TYPE_SD_CARD_SPI) +#define TYPE_EMMC "emmc" +DECLARE_INSTANCE_CHECKER(SDState, EMMC, TYPE_EMMC) + struct SDCardClass { /*< private >*/ DeviceClass parent_class; From 99e84304b31e1948864ca6df07248a380d2ba2b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 17 Jun 2024 08:51:27 +0200 Subject: [PATCH 2145/2884] hw/sd/sdcard: Register generic command handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-3-philmd@linaro.org> --- hw/sd/sd.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index ea01bf6e28..0093bbab3e 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2442,6 +2442,29 @@ static const SDProto sd_proto_emmc = { /* Only v4.3 is supported */ .name = "eMMC", .cmd = { + [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [1] = {0, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, + [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, + [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, + [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, + [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, + [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, + [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, + [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, + [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, + [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, + [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, + [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [35] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, + [36] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, + [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE}, + [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, + [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD}, + [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD}, }, }; From 4143d2374a06c3bde232f02250bdf1fdcb421dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 27 Jun 2024 12:57:01 +0200 Subject: [PATCH 2146/2884] hw/sd/sdcard: Register unimplemented command handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the spec v4.3 these commands are mandatory, but we don't implement them. Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-4-philmd@linaro.org> --- hw/sd/sd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0093bbab3e..8a23e9eb23 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2445,24 +2445,33 @@ static const SDProto sd_proto_emmc = { [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, [1] = {0, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, + [11] = {1, sd_adtc, "READ_DAT_UNTIL_STOP", sd_cmd_unimplemented}, [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [14] = {0, sd_adtc, "BUSTEST_R", sd_cmd_unimplemented}, [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, + [19] = {0, sd_adtc, "BUSTEST_W", sd_cmd_unimplemented}, + [20] = {3, sd_adtc, "WRITE_DAT_UNTIL_STOP", sd_cmd_unimplemented}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [31] = {6, sd_adtc, "SEND_WRITE_PROT_TYPE", sd_cmd_unimplemented}, [35] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, [36] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE}, + [39] = {9, sd_ac, "FAST_IO", sd_cmd_unimplemented}, + [40] = {9, sd_bcr, "GO_IRQ_STATE", sd_cmd_unimplemented}, [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, + [49] = {0, sd_adtc, "SET_TIME", sd_cmd_unimplemented}, [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD}, [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD}, }, From 3007fa1156222a564b5311663644101ae76135f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 25 May 2022 09:21:22 +0200 Subject: [PATCH 2147/2884] hw/sd/sdcard: Add emmc_cmd_SET_RELATIVE_ADDR handler (CMD3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Cédric Le Goater <clg@kaod.org> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-5-philmd@linaro.org> --- hw/sd/sd.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8a23e9eb23..ab502d19b8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1280,6 +1280,20 @@ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) } } +static sd_rsp_type_t emmc_cmd_SET_RELATIVE_ADDR(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_identification_state: + case sd_standby_state: + sd->state = sd_standby_state; + sd_set_rca(sd, req.arg >> 16); + return sd_r1; + + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + /* CMD6 */ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) { @@ -2445,6 +2459,7 @@ static const SDProto sd_proto_emmc = { [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, [1] = {0, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [3] = {0, sd_ac, "SET_RELATIVE_ADDR", emmc_cmd_SET_RELATIVE_ADDR}, [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, From 7851548485671a7dc298cdf9fbcd852b08572fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Tue, 11 May 2021 13:58:47 +0200 Subject: [PATCH 2148/2884] hw/sd/sdcard: Fix SET_BLOCK_COUNT command argument on eMMC (CMD23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The number of blocks is defined in the lower bits [15:0]. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Cédric Le Goater <clg@kaod.org> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-6-philmd@linaro.org> --- hw/sd/sd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index ab502d19b8..09077f0154 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1533,6 +1533,9 @@ static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) } sd->multi_blk_cnt = req.arg; + if (sd_is_emmc(sd)) { + sd->multi_blk_cnt &= 0xffff; + } trace_sdcard_set_block_count(sd->multi_blk_cnt); return sd_r1; From b13b29ed3812cf249db61fec8840917966a9d0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Sat, 15 Jun 2024 15:33:45 +0200 Subject: [PATCH 2149/2884] hw/sd/sdcard: Add emmc_cmd_PROGRAM_CID handler (CMD26) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Message-Id: <20240712162719.88165-7-philmd@linaro.org> --- hw/sd/sd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 09077f0154..a90612af58 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1568,6 +1568,12 @@ static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req) return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); } +/* CMD26 */ +static sd_rsp_type_t emmc_cmd_PROGRAM_CID(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); +} + /* CMD27 */ static sd_rsp_type_t sd_cmd_PROGRAM_CSD(SDState *sd, SDRequest req) { @@ -1917,9 +1923,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 26: /* CMD26: PROGRAM_CID */ - return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); - default: qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; @@ -2478,6 +2481,7 @@ static const SDProto sd_proto_emmc = { [20] = {3, sd_adtc, "WRITE_DAT_UNTIL_STOP", sd_cmd_unimplemented}, [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [26] = {4, sd_adtc, "PROGRAM_CID", emmc_cmd_PROGRAM_CID}, [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, From cfde1788dd41cdc9720af7a91feeb4efde000a81 Mon Sep 17 00:00:00 2001 From: Luc Michel <luc.michel@amd.com> Date: Tue, 20 Feb 2024 12:17:18 +0100 Subject: [PATCH 2150/2884] hw/sd/sdcard: Implement eMMC sleep state (CMD5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JEDEC standards specifies a sleep state where the eMMC won't answer any command appart from RESET and WAKEUP and go to low power state. Implement this state and the corresponding command number 5. Signed-off-by: Luc Michel <luc.michel@amd.com> Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-8-philmd@linaro.org> --- hw/sd/sd.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a90612af58..d98952a12f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1236,8 +1236,19 @@ static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, /* CMD0 */ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) { - sd->state = sd_idle_state; - sd_reset(DEVICE(sd)); + if (sd->state == sd_sleep_state) { + switch (req.arg) { + case 0x00000000: + case 0xf0f0f0f0: + break; + default: + return sd_r0; + } + } + if (sd->state != sd_inactive_state) { + sd->state = sd_idle_state; + sd_reset(DEVICE(sd)); + } return sd_is_spi(sd) ? sd_r1 : sd_r0; } @@ -1294,6 +1305,30 @@ static sd_rsp_type_t emmc_cmd_SET_RELATIVE_ADDR(SDState *sd, SDRequest req) } } +/* CMD5 */ +static sd_rsp_type_t emmc_cmd_sleep_awake(SDState *sd, SDRequest req) +{ + bool do_sleep = extract32(req.arg, 15, 1); + + switch (sd->state) { + case sd_sleep_state: + if (!do_sleep) { + /* Awake */ + sd->state = sd_standby_state; + } + return sd_r1b; + + case sd_standby_state: + if (do_sleep) { + sd->state = sd_sleep_state; + } + return sd_r1b; + + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + /* CMD6 */ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) { @@ -1696,6 +1731,7 @@ static sd_rsp_type_t sd_cmd_APP_CMD(SDState *sd, SDRequest req) case sd_ready_state: case sd_identification_state: case sd_inactive_state: + case sd_sleep_state: return sd_invalid_state_for_cmd(sd, req); case sd_idle_state: if (!sd_is_spi(sd) && sd_req_get_rca(sd, req) != 0x0000) { @@ -2018,6 +2054,12 @@ int sd_do_command(SDState *sd, SDRequest *req, req->cmd &= 0x3f; } + if (sd->state == sd_sleep_state && req->cmd) { + qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is sleeping\n"); + rtype = sd_r0; + goto send_response; + } + if (sd->card_status & CARD_IS_LOCKED) { if (!cmd_valid_while_locked(sd, req->cmd)) { sd->card_status |= ILLEGAL_COMMAND; @@ -2467,6 +2509,7 @@ static const SDProto sd_proto_emmc = { [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, [3] = {0, sd_ac, "SET_RELATIVE_ADDR", emmc_cmd_SET_RELATIVE_ADDR}, [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, + [5] = {0, sd_ac, "SLEEP/AWAKE", emmc_cmd_sleep_awake}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, From a1e04619950f29bfad781fb0d4cbcda65f5d5245 Mon Sep 17 00:00:00 2001 From: Vincent Palatin <vpalatin@chromium.org> Date: Mon, 25 Jul 2011 16:19:08 -0700 Subject: [PATCH 2151/2884] hw/sd/sdcard: Add emmc_cmd_SEND_EXT_CSD handler (CMD8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parameters mimick a real 4GB eMMC, but it can be set to various sizes. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Signed-off-by: Cédric Le Goater <clg@redhat.com> EXT_CSD values from Vincent's patch simplivied for Spec v4.3: - Remove deprecated keys: . EXT_CSD_SEC_ERASE_MULT . EXT_CSD_SEC_TRIM_MULT - Set some keys to not defined / implemented: . EXT_CSD_HPI_FEATURES . EXT_CSD_BKOPS_SUPPORT . EXT_CSD_SEC_FEATURE_SUPPORT . EXT_CSD_ERASE_TIMEOUT_MULT . EXT_CSD_PART_SWITCH_TIME . EXT_CSD_OUT_OF_INTERRUPT_TIME - Simplify: . EXT_CSD_ACC_SIZE (6 -> 1) 16KB of super_page_size -> 512B (BDRV_SECTOR_SIZE) . EXT_CSD_HC_ERASE_GRP_SIZE (4 -> 1) . EXT_CSD_HC_WP_GRP_SIZE (4 -> 1) . EXT_CSD_S_C_VCC[Q] (8 -> 1) . EXT_CSD_S_A_TIMEOUT (17 -> 1) . EXT_CSD_CARD_TYPE (7 -> 3) Dual data rate -> High-Speed mode - Update: . EXT_CSD_CARD_TYPE (7 -> 3) High-Speed MultiMediaCard @ 26MHz & 52MHz . Performances (0xa -> 0x46) Class B at 3MB/s. -> Class J at 21MB/s . EXT_CSD_REV (5 -> 3) Rev 1.5 (spec v4.41) -> Rev 1.3 (spec v4.3) - Use load/store API to set EXT_CSD_SEC_CNT - Remove R/W keys, normally zeroed at reset . EXT_CSD_BOOT_INFO Migrate the Modes segment (192 lower bytes) but not the full EXT_CSD register, see Spec v4.3, chapter 8.4 "Extended CSD register": The Extended CSD register defines the card properties and selected modes. It is 512 bytes long. The most significant 320 bytes are the Properties segment, which defines the card capabilities and cannot be modified by the host. The lower 192 bytes are the Modes segment, which defines the configuration the card is working in. These modes can be changed by the host by means of the SWITCH command. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-9-philmd@linaro.org> --- hw/sd/sd.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d98952a12f..2246213b31 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -124,6 +124,13 @@ struct SDState { uint16_t rca; uint32_t card_status; uint8_t sd_status[64]; + union { + uint8_t ext_csd[512]; + struct { + uint8_t ext_csd_rw[192]; /* Modes segment */ + uint8_t ext_csd_ro[320]; /* Properties segment */ + }; + }; /* Static properties */ @@ -476,6 +483,36 @@ static const uint8_t sd_csd_rw_mask[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe, }; +static void emmc_set_ext_csd(SDState *sd, uint64_t size) +{ + uint32_t sectcount = size >> HWBLOCK_SHIFT; + + memset(sd->ext_csd, 0, sizeof(sd->ext_csd)); /* FIXME only RW at reset */ + + /* Properties segment (RO) */ + sd->ext_csd[EXT_CSD_S_CMD_SET] = 0b1; /* supported command sets */ + sd->ext_csd[EXT_CSD_BOOT_INFO] = 0x0; /* Boot information */ + sd->ext_csd[EXT_CSD_BOOT_MULT] = 0x8; /* Boot partition size. 128KB unit */ + sd->ext_csd[EXT_CSD_ACC_SIZE] = 0x1; /* Access size */ + sd->ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] = 0x01; /* HC Erase unit size */ + sd->ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] = 0x01; /* HC erase timeout */ + sd->ext_csd[EXT_CSD_REL_WR_SEC_C] = 0x1; /* Reliable write sector count */ + sd->ext_csd[EXT_CSD_HC_WP_GRP_SIZE] = 0x01; /* HC write protect group size */ + sd->ext_csd[EXT_CSD_S_C_VCC] = 0x01; /* Sleep current VCC */ + sd->ext_csd[EXT_CSD_S_C_VCCQ] = 0x01; /* Sleep current VCCQ */ + sd->ext_csd[EXT_CSD_S_A_TIMEOUT] = 0x01; /* Sleep/Awake timeout */ + stl_le_p(&sd->ext_csd[EXT_CSD_SEC_CNT], sectcount); /* Sector count */ + sd->ext_csd[210] = 0x46; /* Min write perf for 8bit@52Mhz */ + sd->ext_csd[209] = 0x46; /* Min read perf for 8bit@52Mhz */ + sd->ext_csd[208] = 0x46; /* Min write perf for 4bit@52Mhz */ + sd->ext_csd[207] = 0x46; /* Min read perf for 4bit@52Mhz */ + sd->ext_csd[206] = 0x46; /* Min write perf for 4bit@26Mhz */ + sd->ext_csd[205] = 0x46; /* Min read perf for 4bit@26Mhz */ + sd->ext_csd[EXT_CSD_CARD_TYPE] = 0b11; + sd->ext_csd[EXT_CSD_STRUCTURE] = 2; + sd->ext_csd[EXT_CSD_REV] = 3; +} + static void emmc_set_csd(SDState *sd, uint64_t size) { int hwblock_shift = HWBLOCK_SHIFT; @@ -512,6 +549,7 @@ static void emmc_set_csd(SDState *sd, uint64_t size) ((hwblock_shift << 6) & 0xc0); sd->csd[14] = 0x00; sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; + emmc_set_ext_csd(sd, size); } static void sd_set_csd(SDState *sd, uint64_t size) @@ -847,6 +885,24 @@ static const VMStateDescription sd_ocr_vmstate = { }, }; +static bool vmstate_needed_for_emmc(void *opaque) +{ + SDState *sd = opaque; + + return sd_is_emmc(sd); +} + +static const VMStateDescription emmc_extcsd_vmstate = { + .name = "sd-card/ext_csd_modes-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_needed_for_emmc, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(ext_csd_rw, SDState, 192), + VMSTATE_END_OF_LIST() + }, +}; + static int sd_vmstate_pre_load(void *opaque) { SDState *sd = opaque; @@ -894,6 +950,7 @@ static const VMStateDescription sd_vmstate = { }, .subsections = (const VMStateDescription * const []) { &sd_ocr_vmstate, + &emmc_extcsd_vmstate, NULL }, }; @@ -1405,6 +1462,17 @@ static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) return sd_r7; } +/* CMD8 */ +static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->ext_csd, sizeof(sd->ext_csd)); +} + /* CMD9 */ static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) { @@ -2334,6 +2402,7 @@ uint8_t sd_read_byte(SDState *sd) sd->data_offset, sd->data_size, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ + case 8: /* CMD8: SEND_EXT_CSD */ case 9: /* CMD9: SEND_CSD */ case 10: /* CMD10: SEND_CID */ case 13: /* ACMD13: SD_STATUS */ @@ -2511,6 +2580,7 @@ static const SDProto sd_proto_emmc = { [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [5] = {0, sd_ac, "SLEEP/AWAKE", emmc_cmd_sleep_awake}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, + [8] = {0, sd_adtc, "SEND_EXT_CSD", emmc_cmd_SEND_EXT_CSD}, [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, [11] = {1, sd_adtc, "READ_DAT_UNTIL_STOP", sd_cmd_unimplemented}, From c3561ab6249322893273707016b2b2253d0e89c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 9 Jul 2024 16:35:42 +0200 Subject: [PATCH 2152/2884] hw/sd/sdcard: Add eMMC 'boot-partition-size' property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid hardcoding 1MiB boot size in EXT_CSD_BOOT_MULT, expose it as 'boot-partition-size' QOM property. By default, do not use any size. The board is responsible to set the boot partition size property. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-10-philmd@linaro.org> --- hw/sd/sd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 2246213b31..d49b144214 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -135,6 +135,7 @@ struct SDState { /* Static properties */ uint8_t spec_version; + uint64_t boot_part_size; BlockBackend *blk; const SDProto *proto; @@ -492,7 +493,8 @@ static void emmc_set_ext_csd(SDState *sd, uint64_t size) /* Properties segment (RO) */ sd->ext_csd[EXT_CSD_S_CMD_SET] = 0b1; /* supported command sets */ sd->ext_csd[EXT_CSD_BOOT_INFO] = 0x0; /* Boot information */ - sd->ext_csd[EXT_CSD_BOOT_MULT] = 0x8; /* Boot partition size. 128KB unit */ + /* Boot partition size. 128KB unit */ + sd->ext_csd[EXT_CSD_BOOT_MULT] = sd->boot_part_size / (128 * KiB); sd->ext_csd[EXT_CSD_ACC_SIZE] = 0x1; /* Access size */ sd->ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] = 0x01; /* HC Erase unit size */ sd->ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] = 0x01; /* HC erase timeout */ @@ -2704,6 +2706,7 @@ static Property sd_properties[] = { }; static Property emmc_properties[] = { + DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), DEFINE_PROP_END_OF_LIST() }; From 8f25b74a32316b718884796459921696cd43f048 Mon Sep 17 00:00:00 2001 From: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com> Date: Wed, 25 May 2022 09:21:22 +0200 Subject: [PATCH 2153/2884] hw/sd/sdcard: Add mmc SWITCH function support (CMD6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit switch operation in mmc cards, updated the ext_csd register to request changes in card operations. Here we implement similar sequence but requests are mostly dummy and make no change. Implement SWITCH_ERROR if the write operation offset goes beyond length of ext_csd. Signed-off-by: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [PMD: Convert to SDProto handlers, add trace events] Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Cédric Le Goater <clg@redhat.com> Message-Id: <20240712162719.88165-11-philmd@linaro.org> --- hw/sd/sd.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ hw/sd/trace-events | 2 ++ 2 files changed, 58 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d49b144214..1f16c16fd1 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -642,6 +642,7 @@ static bool sd_req_rca_same(SDState *s, SDRequest req) FIELD(CSR, AKE_SEQ_ERROR, 3, 1) FIELD(CSR, APP_CMD, 5, 1) FIELD(CSR, FX_EVENT, 6, 1) +FIELD(CSR, SWITCH_ERROR, 7, 1) FIELD(CSR, READY_FOR_DATA, 8, 1) FIELD(CSR, CURRENT_STATE, 9, 4) FIELD(CSR, ERASE_RESET, 13, 1) @@ -1091,6 +1092,47 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr) return ret; } +enum ExtCsdAccessMode { + EXT_CSD_ACCESS_MODE_COMMAND_SET = 0, + EXT_CSD_ACCESS_MODE_SET_BITS = 1, + EXT_CSD_ACCESS_MODE_CLEAR_BITS = 2, + EXT_CSD_ACCESS_MODE_WRITE_BYTE = 3 +}; + +static void emmc_function_switch(SDState *sd, uint32_t arg) +{ + uint8_t access = extract32(arg, 24, 2); + uint8_t index = extract32(arg, 16, 8); + uint8_t value = extract32(arg, 8, 8); + uint8_t b = sd->ext_csd[index]; + + trace_sdcard_switch(access, index, value, extract32(arg, 0, 2)); + + if (index >= 192) { + qemu_log_mask(LOG_GUEST_ERROR, "MMC switching illegal offset\n"); + sd->card_status |= R_CSR_SWITCH_ERROR_MASK; + return; + } + + switch (access) { + case EXT_CSD_ACCESS_MODE_COMMAND_SET: + qemu_log_mask(LOG_UNIMP, "MMC Command set switching not supported\n"); + return; + case EXT_CSD_ACCESS_MODE_SET_BITS: + b |= value; + break; + case EXT_CSD_ACCESS_MODE_CLEAR_BITS: + b &= ~value; + break; + case EXT_CSD_ACCESS_MODE_WRITE_BYTE: + b = value; + break; + } + + trace_sdcard_ext_csd_update(index, sd->ext_csd[index], b); + sd->ext_csd[index] = b; +} + static void sd_function_switch(SDState *sd, uint32_t arg) { int i, mode, new_func; @@ -1402,6 +1444,19 @@ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); } +static sd_rsp_type_t emmc_cmd_SWITCH(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_programming_state; + emmc_function_switch(sd, req.arg); + sd->state = sd_transfer_state; + return sd_r1b; + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + /* CMD7 */ static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req) { @@ -2581,6 +2636,7 @@ static const SDProto sd_proto_emmc = { [3] = {0, sd_ac, "SET_RELATIVE_ADDR", emmc_cmd_SET_RELATIVE_ADDR}, [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, [5] = {0, sd_ac, "SLEEP/AWAKE", emmc_cmd_sleep_awake}, + [6] = {10, sd_adtc, "SWITCH", emmc_cmd_SWITCH}, [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, [8] = {0, sd_adtc, "SEND_EXT_CSD", emmc_cmd_SEND_EXT_CSD}, [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 5dfe6be7b7..43671dc791 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -57,6 +57,8 @@ sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint8_t value) "%s %20s/ CMD%02d ofs %"PRIu32" value 0x%02x" sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint64_t size, uint32_t blklen) "%s %20s/ CMD%02d ofs %"PRIu32" size %"PRIu64" blklen %" PRIu32 sdcard_set_voltage(uint16_t millivolts) "%u mV" +sdcard_ext_csd_update(unsigned index, uint8_t oval, uint8_t nval) "index %u: 0x%02x -> 0x%02x" +sdcard_switch(unsigned access, unsigned index, unsigned value, unsigned set) "SWITCH acc:%u idx:%u val:%u set:%u" # pxa2xx_mmci.c pxa2xx_mmci_read(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x" From c8cb19876d3e29bffd7ffd87586ff451f97f5f46 Mon Sep 17 00:00:00 2001 From: Joel Stanley <joel@jms.id.au> Date: Wed, 25 May 2022 09:21:22 +0200 Subject: [PATCH 2154/2884] hw/sd/sdcard: Support boot area in emmc image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This assumes a specially constructed image: $ dd if=/dev/zero of=mmc-bootarea.img count=2 bs=1M $ dd if=u-boot-spl.bin of=mmc-bootarea.img conv=notrunc $ dd if=u-boot.bin of=mmc-bootarea.img conv=notrunc count=64 bs=1K $ cat mmc-bootarea.img obmc-phosphor-image.wic > mmc.img $ truncate --size 16GB mmc.img For now this still requires a mtd image to load the SPL: $ qemu-system-arm -M tacoma-bmc -nographic \ -global driver=sd-card,property=emmc,value=true \ -drive file=mmc.img,if=sd,index=2,format=raw Signed-off-by: Joel Stanley <joel@jms.id.au> Signed-off-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240712162719.88165-12-philmd@linaro.org> --- hw/sd/sd.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1f16c16fd1..07cb97d88c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -137,6 +137,7 @@ struct SDState { uint8_t spec_version; uint64_t boot_part_size; BlockBackend *blk; + uint8_t boot_config; const SDProto *proto; @@ -513,6 +514,9 @@ static void emmc_set_ext_csd(SDState *sd, uint64_t size) sd->ext_csd[EXT_CSD_CARD_TYPE] = 0b11; sd->ext_csd[EXT_CSD_STRUCTURE] = 2; sd->ext_csd[EXT_CSD_REV] = 3; + + /* Mode segment (RW) */ + sd->ext_csd[EXT_CSD_PART_CONFIG] = sd->boot_config; } static void emmc_set_csd(SDState *sd, uint64_t size) @@ -763,6 +767,40 @@ static uint32_t sd_blk_len(SDState *sd) return sd->blk_len; } +/* + * This requires a disk image that has two boot partitions inserted at the + * beginning of it. The size of the boot partitions is the "boot-size" + * property. + */ +static uint32_t sd_bootpart_offset(SDState *sd) +{ + bool partitions_enabled; + unsigned partition_access; + + if (!sd->boot_part_size || !sd_is_emmc(sd)) { + return 0; + } + + partitions_enabled = sd->ext_csd[EXT_CSD_PART_CONFIG] + & EXT_CSD_PART_CONFIG_EN_MASK; + if (!partitions_enabled) { + return 0; + } + + partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG] + & EXT_CSD_PART_CONFIG_ACC_MASK; + switch (partition_access) { + case EXT_CSD_PART_CONFIG_ACC_DEFAULT: + return sd->boot_part_size * 2; + case EXT_CSD_PART_CONFIG_ACC_BOOT0: + return 0; + case EXT_CSD_PART_CONFIG_ACC_BOOT0 + 1: + return sd->boot_part_size * 1; + default: + g_assert_not_reached(); + } +} + static uint64_t sd_req_get_address(SDState *sd, SDRequest req) { uint64_t addr; @@ -795,6 +833,7 @@ static void sd_reset(DeviceState *dev) sect = 0; } size = sect << HWBLOCK_SHIFT; + size -= sd_bootpart_offset(sd); sect = sd_addr_to_wpnum(size) + 1; @@ -1003,6 +1042,7 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_read_block(addr, len); + addr += sd_bootpart_offset(sd); if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } @@ -1011,6 +1051,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_write_block(addr, len); + addr += sd_bootpart_offset(sd); if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } @@ -2763,6 +2804,7 @@ static Property sd_properties[] = { static Property emmc_properties[] = { DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), + DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), DEFINE_PROP_END_OF_LIST() }; From 3becc939081097ccfed6968771f33d65ce8551eb Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Wed, 3 Jul 2024 11:53:10 +0200 Subject: [PATCH 2155/2884] qapi/qom: Document feature unstable of @x-vfio-user-server Commit 8f9a9259d32c added ObjectType member @x-vfio-user-server with feature unstable, but neglected to explain why it is unstable. Do that now. Fixes: 8f9a9259d32c (vfio-user: define vfio-user-server object) Cc: Elena Ufimtseva <elena.ufimtseva@oracle.com> Cc: John G Johnson <john.g.johnson@oracle.com> Cc: Jagannathan Raman <jag.raman@oracle.com> Cc: qemu-stable@nongnu.org Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240703095310.1242102-1-armbru@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> [Indentation fixed] --- qapi/qom.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qapi/qom.json b/qapi/qom.json index 8e75a419c3..3e52aec8fb 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1024,7 +1024,8 @@ # # Features: # -# @unstable: Member @x-remote-object is experimental. +# @unstable: Members @x-remote-object and @x-vfio-user-server are +# experimental. # # Since: 6.0 ## From fbcb6e0c487abd09cf1fab7913302174c3a2fa16 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 11 Jul 2024 13:22:24 +0200 Subject: [PATCH 2156/2884] qapi/pci: Clean up documentation around PciDeviceClass PciDeviceInfo's doc comment has a note on PciDeviceClass member @desc. Since the note applies always, not just within PciDeviceInfo, merge it into PciDeviceClass's description of member @desc. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240711112228.2140606-2-armbru@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> --- qapi/pci.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qapi/pci.json b/qapi/pci.json index 8287d15dd0..97179920fb 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -93,7 +93,8 @@ # # Information about the Class of a PCI device # -# @desc: a string description of the device's class +# @desc: a string description of the device's class (not stable, and +# should only be treated as informational) # # @class: the class code of the device # @@ -146,9 +147,6 @@ # # @regions: a list of the PCI I/O regions associated with the device # -# .. note:: The contents of @class_info.desc are not stable and should -# only be treated as informational. -# # Since: 0.14 ## { 'struct': 'PciDeviceInfo', From d07f0efcbca66178d42f8c9c4f624e9de517100a Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 11 Jul 2024 13:22:25 +0200 Subject: [PATCH 2157/2884] qapi/machine: Clean up documentation around CpuInstanceProperties CpuInstanceProperties' doc comment describes its members as properties to be passed to device_add when hot-plugging a CPU. This was in fact the initial use of this type, with query-hotpluggable-cpus: letting management applications find out what properties need to be passed with device_add to hot-plug a CPU. We've since added other uses: set-numa-node (commit 419fcdec3c1 and f3be67812c2), and query-cpus-fast (commit ce74ee3dea6). These are not about device-add. query-hotpluggable-cpus uses CpuInstanceProperties within HotpluggableCPU. Lift the documentation related to device-add from CpuInstanceProperties to HotpluggableCPU. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240711112228.2140606-3-armbru@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> --- qapi/machine.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/qapi/machine.json b/qapi/machine.json index f15ad1b43e..50ff102d56 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -960,9 +960,7 @@ ## # @CpuInstanceProperties: # -# List of properties to be used for hotplugging a CPU instance, it -# should be passed by management with device_add command when a CPU is -# being hotplugged. +# Properties identifying a CPU. # # Which members are optional and which mandatory depends on the # architecture and board. @@ -996,9 +994,6 @@ # # @thread-id: thread number within the core the CPU belongs to # -# .. note:: Management should be prepared to pass through additional -# properties with device_add. -# # Since: 2.7 ## { 'struct': 'CpuInstanceProperties', @@ -1020,7 +1015,8 @@ # # @type: CPU object type for usage with device_add command # -# @props: list of properties to be used for hotplugging CPU +# @props: list of properties to pass for hotplugging a CPU with +# device_add # # @vcpus-count: number of logical VCPU threads @HotpluggableCPU # provides @@ -1028,6 +1024,9 @@ # @qom-path: link to existing CPU object if CPU is present or omitted # if CPU is not present. # +# .. note:: Management should be prepared to pass through additional +# properties with device_add. +# # Since: 2.7 ## { 'struct': 'HotpluggableCPU', From dd950220c08938e14abb0357107ca8500cb913c9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 11 Jul 2024 13:22:26 +0200 Subject: [PATCH 2158/2884] qapi/machine: Clarify query-uuid value when none has been specified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no UUID has been specified, query-uuid returns {"UUID": "00000000-0000-0000-0000-000000000000"} The doc comment calls this "a null UUID", which I find less than clear. RFC 9562 calls it "the nil UUID (all zeroes)", so use that instead. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240711112228.2140606-4-armbru@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> [Wording improved, commit message adjusted] Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- qapi/machine.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qapi/machine.json b/qapi/machine.json index 50ff102d56..eb2fd01e95 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -305,9 +305,8 @@ # # Since: 0.14 # -# .. note:: If no UUID was specified for the guest, a null UUID is -# returned. -# +# .. note:: If no UUID was specified for the guest, the nil UUID (all +# zeroes) is returned. ## { 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } From 6cab0537926bafbb6be2fc93202611fa5113219b Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 11 Jul 2024 13:22:27 +0200 Subject: [PATCH 2159/2884] qapi/sockets: Move deprecation note out of SocketAddress doc comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doc comments are reference documentation for users of QMP. SocketAddress's doc comment contains a deprecation note advising developers to use SocketAddress for new code. Irrelevant for users of QMP. Move the note out of the doc comment. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240711112228.2140606-5-armbru@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- qapi/sockets.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qapi/sockets.json b/qapi/sockets.json index 4d78d2ccb7..e76fdb9925 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -179,10 +179,6 @@ # # @type: Transport type # -# .. note:: This type is deprecated in favor of SocketAddress. The -# difference between SocketAddressLegacy and SocketAddress is that -# the latter has fewer ``{}`` on the wire. -# # Since: 1.3 ## { 'union': 'SocketAddressLegacy', @@ -193,6 +189,9 @@ 'unix': 'UnixSocketAddressWrapper', 'vsock': 'VsockSocketAddressWrapper', 'fd': 'FdSocketAddressWrapper' } } +# Note: This type is deprecated in favor of SocketAddress. The +# difference between SocketAddressLegacy and SocketAddress is that the +# latter has fewer ``{}`` on the wire. ## # @SocketAddressType: From 29a9efb09f95cf54a45d4ed97b82d2158b1a3983 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 11 Jul 2024 13:22:28 +0200 Subject: [PATCH 2160/2884] qapi/ui: Drop note on naming of SpiceQueryMouseMode Doc comments are reference documentation for users of QMP. SpiceQueryMouseMode's doc comment contains a note explaining why it's not named SpiceMouseMode: spice/enums.h has it already. Irrelevant for users of QMP; delete the note. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240711112228.2140606-6-armbru@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> --- qapi/ui.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index 5bcccbfc93..df089869a5 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -273,8 +273,6 @@ # @unknown: No information is available about mouse mode used by the # spice server. # -# .. note:: spice/enums.h has a SpiceMouseMode already, hence the name. -# # Since: 1.1 ## { 'enum': 'SpiceQueryMouseMode', From a7d07ccd208dc1a8dbd0b03d95761a6424ccb375 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:04 -0400 Subject: [PATCH 2161/2884] docs/qapidoc: factor out do_parse() Factor out the compatibility parser helper into a base class, so it can be shared by other directives. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240717021312.606116-3-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/sphinx/qapidoc.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 62b39833ca..b3be82998a 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -481,7 +481,25 @@ class QAPISchemaGenDepVisitor(QAPISchemaVisitor): super().visit_module(name) -class QAPIDocDirective(Directive): +class NestedDirective(Directive): + def run(self): + raise NotImplementedError + + def do_parse(self, rstlist, node): + """ + Parse rST source lines and add them to the specified node + + Take the list of rST source lines rstlist, parse them as + rST, and add the resulting docutils nodes as children of node. + The nodes are parsed in a way that allows them to include + subheadings (titles) without confusing the rendering of + anything else. + """ + with switch_source_input(self.state, rstlist): + nested_parse_with_titles(self.state, rstlist, node) + + +class QAPIDocDirective(NestedDirective): """Extract documentation from the specified QAPI .json file""" required_argument = 1 @@ -519,18 +537,6 @@ class QAPIDocDirective(Directive): # so they are displayed nicely to the user raise ExtensionError(str(err)) from err - def do_parse(self, rstlist, node): - """Parse rST source lines and add them to the specified node - - Take the list of rST source lines rstlist, parse them as - rST, and add the resulting docutils nodes as children of node. - The nodes are parsed in a way that allows them to include - subheadings (titles) without confusing the rendering of - anything else. - """ - with switch_source_input(self.state, rstlist): - nested_parse_with_titles(self.state, rstlist, node) - def setup(app): """Register qapi-doc directive with Sphinx""" From 547864f9d4cead4fe70981d2c4ffa5905b40e671 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:05 -0400 Subject: [PATCH 2162/2884] docs/qapidoc: create qmp-example directive This is a directive that creates a syntactic sugar for creating "Example" boxes very similar to the ones already used in the bitmaps.rst document, please see e.g. https://www.qemu.org/docs/master/interop/bitmaps.html#creation-block-dirty-bitmap-add In its simplest form, when a custom title is not needed or wanted, and the example body is *solely* a QMP example: ``` .. qmp-example:: {body} ``` is syntactic sugar for: ``` .. admonition:: Example: .. code-block:: QMP {body} ``` When a custom, plaintext title that describes the example is desired, this form: ``` .. qmp-example:: :title: Defrobnification {body} ``` Is syntactic sugar for: ``` .. admonition:: Example: Defrobnification .. code-block:: QMP {body} ``` Lastly, when Examples are multi-step processes that require non-QMP exposition, have lengthy titles, or otherwise involve prose with rST markup (lists, cross-references, etc), the most complex form: ``` .. qmp-example:: :annotated: This example shows how to use `foo-command`:: {body} For more information, please see `frobnozz`. ``` Is desugared to: ``` .. admonition:: Example: This example shows how to use `foo-command`:: {body} For more information, please see `frobnozz`. ``` Note that :annotated: and :title: options can be combined together, if desired. The primary benefit here being documentation source consistently using the same directive for all forms of examples to ensure consistent visual styling, and ensuring all relevant prose is visually grouped alongside the code literal block. Note that as of this commit, the code-block rST syntax "::" does not apply QMP highlighting; you would need to use ".. code-block:: QMP". The very next commit changes this behavior to assume all "::" code blocks within this directive are QMP blocks. Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240717021312.606116-4-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/sphinx/qapidoc.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index b3be82998a..11defcfa3f 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -27,6 +27,7 @@ https://www.sphinx-doc.org/en/master/development/index.html import os import re import textwrap +from typing import List from docutils import nodes from docutils.parsers.rst import Directive, directives @@ -35,6 +36,7 @@ from qapi.error import QAPIError, QAPISemError from qapi.gen import QAPISchemaVisitor from qapi.schema import QAPISchema +from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError from sphinx.util.docutils import switch_source_input from sphinx.util.nodes import nested_parse_with_titles @@ -538,10 +540,63 @@ class QAPIDocDirective(NestedDirective): raise ExtensionError(str(err)) from err +class QMPExample(CodeBlock, NestedDirective): + """ + Custom admonition for QMP code examples. + + When the :annotated: option is present, the body of this directive + is parsed as normal rST instead. Code blocks must be explicitly + written by the user, but this allows for intermingling explanatory + paragraphs with arbitrary rST syntax and code blocks for more + involved examples. + + When :annotated: is absent, the directive body is treated as a + simple standalone QMP code block literal. + """ + + required_argument = 0 + optional_arguments = 0 + has_content = True + option_spec = { + "annotated": directives.flag, + "title": directives.unchanged, + } + + def admonition_wrap(self, *content) -> List[nodes.Node]: + title = "Example:" + if "title" in self.options: + title = f"{title} {self.options['title']}" + + admon = nodes.admonition( + "", + nodes.title("", title), + *content, + classes=["admonition", "admonition-example"], + ) + return [admon] + + def run_annotated(self) -> List[nodes.Node]: + content_node: nodes.Element = nodes.section() + self.do_parse(self.content, content_node) + return content_node.children + + def run(self) -> List[nodes.Node]: + annotated = "annotated" in self.options + + if annotated: + content_nodes = self.run_annotated() + else: + self.arguments = ["QMP"] + content_nodes = super().run() + + return self.admonition_wrap(*content_nodes) + + def setup(app): """Register qapi-doc directive with Sphinx""" app.add_config_value("qapidoc_srctree", None, "env") app.add_directive("qapi-doc", QAPIDocDirective) + app.add_directive("qmp-example", QMPExample) return { "version": __version__, From 76e375fc3c538bd6e4232314f693b56536a50b73 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:06 -0400 Subject: [PATCH 2163/2884] docs/qapidoc: add QMP highlighting to annotated qmp-example blocks For any code literal blocks inside of a qmp-example directive, apply and enforce the QMP lexer/highlighter to those blocks. This way, you won't need to write: ``` .. qmp-example:: :annotated: Blah blah .. code-block:: QMP -> { "lorem": "ipsum" } ``` But instead, simply: ``` .. qmp-example:: :annotated: Blah blah:: -> { "lorem": "ipsum" } ``` Once the directive block is exited, whatever the previous default highlight language was will be restored; localizing the forced QMP lexing to exclusively this directive. Note, if the default language is *already* QMP, this directive will not generate and restore redundant highlight configuration nodes. We may well decide that the default language ought to be QMP for any QAPI reference pages, but this way the directive behaves consistently no matter where it is used. Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240717021312.606116-5-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/sphinx/qapidoc.py | 53 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 11defcfa3f..738b2450fb 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -26,6 +26,7 @@ https://www.sphinx-doc.org/en/master/development/index.html import os import re +import sys import textwrap from typing import List @@ -36,6 +37,7 @@ from qapi.error import QAPIError, QAPISemError from qapi.gen import QAPISchemaVisitor from qapi.schema import QAPISchema +from sphinx import addnodes from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError from sphinx.util.docutils import switch_source_input @@ -545,10 +547,10 @@ class QMPExample(CodeBlock, NestedDirective): Custom admonition for QMP code examples. When the :annotated: option is present, the body of this directive - is parsed as normal rST instead. Code blocks must be explicitly - written by the user, but this allows for intermingling explanatory - paragraphs with arbitrary rST syntax and code blocks for more - involved examples. + is parsed as normal rST, but with any '::' code blocks set to use + the QMP lexer. Code blocks must be explicitly written by the user, + but this allows for intermingling explanatory paragraphs with + arbitrary rST syntax and code blocks for more involved examples. When :annotated: is absent, the directive body is treated as a simple standalone QMP code block literal. @@ -562,6 +564,33 @@ class QMPExample(CodeBlock, NestedDirective): "title": directives.unchanged, } + def _highlightlang(self) -> addnodes.highlightlang: + """Return the current highlightlang setting for the document""" + node = None + doc = self.state.document + + if hasattr(doc, "findall"): + # docutils >= 0.18.1 + for node in doc.findall(addnodes.highlightlang): + pass + else: + for elem in doc.traverse(): + if isinstance(elem, addnodes.highlightlang): + node = elem + + if node: + return node + + # No explicit directive found, use defaults + node = addnodes.highlightlang( + lang=self.env.config.highlight_language, + force=False, + # Yes, Sphinx uses this value to effectively disable line + # numbers and not 0 or None or -1 or something. ¯\_(ツ)_/¯ + linenothreshold=sys.maxsize, + ) + return node + def admonition_wrap(self, *content) -> List[nodes.Node]: title = "Example:" if "title" in self.options: @@ -576,8 +605,24 @@ class QMPExample(CodeBlock, NestedDirective): return [admon] def run_annotated(self) -> List[nodes.Node]: + lang_node = self._highlightlang() + content_node: nodes.Element = nodes.section() + + # Configure QMP highlighting for "::" blocks, if needed + if lang_node["lang"] != "QMP": + content_node += addnodes.highlightlang( + lang="QMP", + force=False, # "True" ignores lexing errors + linenothreshold=lang_node["linenothreshold"], + ) + self.do_parse(self.content, content_node) + + # Restore prior language highlighting, if needed + if lang_node["lang"] != "QMP": + content_node += addnodes.highlightlang(**lang_node.attributes) + return content_node.children def run(self) -> List[nodes.Node]: From e597a73a8c561141f757b3b1ef13470ebdec5640 Mon Sep 17 00:00:00 2001 From: Harmonie Snow <harmonie@gmail.com> Date: Tue, 16 Jul 2024 22:13:07 -0400 Subject: [PATCH 2164/2884] docs/sphinx: add CSS styling for qmp-example directive Add CSS styling for qmp-example directives to increase readability and consistently style all example blocks. Signed-off-by: Harmonie Snow <harmonie@gmail.com> Signed-off-by: John Snow <jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240717021312.606116-6-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/sphinx-static/theme_overrides.css | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index c70ef95128..965ecac54f 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -87,6 +87,55 @@ div[class^="highlight"] pre { padding-bottom: 1px; } +/* qmp-example directive styling */ + +.rst-content .admonition-example { + /* do not apply the standard admonition background */ + background-color: transparent; + border: solid #ffd2ed 1px; +} + +.rst-content .admonition-example > .admonition-title:before { + content: "▷"; +} + +.rst-content .admonition-example > .admonition-title { + background-color: #5980a6; +} + +.rst-content .admonition-example > div[class^="highlight"] { + /* make code boxes take up the full width of the admonition w/o margin */ + margin-left: -12px; + margin-right: -12px; + + border-top: 1px solid #ffd2ed; + border-bottom: 1px solid #ffd2ed; + border-left: 0px; + border-right: 0px; +} + +.rst-content .admonition-example > div[class^="highlight"]:nth-child(2) { + /* If a code box is the second element in an example admonition, + * it is the first child after the title. let it sit flush against + * the title. */ + margin-top: -12px; + border-top: 0px; +} + +.rst-content .admonition-example > div[class^="highlight"]:last-child { + /* If a code box is the final element in an example admonition, don't + * render margin below it; let it sit flush with the end of the + * admonition box */ + margin-bottom: -12px; + border-bottom: 0px; +} + +.rst-content .admonition-example .highlight { + background-color: #fffafd; +} + +/* end qmp-example styling */ + @media screen { /* content column From 14b48aaab92de5962e8cdb563875fc6937ef916e Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:08 -0400 Subject: [PATCH 2165/2884] qapi: convert "Example" sections without titles Use the no-option form of ".. qmp-example::" to convert any Examples that do not have any form of caption or explanation whatsoever. Note that in a few cases, example sections are split into two or more separate example blocks. This is only done stylistically to create a delineation between two or more logically independent examples. See commit-3: "docs/qapidoc: create qmp-example directive", for a detailed explanation of this custom directive syntax. See commit+3: "qapi: remove "Example" doc section" for a detailed explanation of why. Note: an empty "TODO" line was added to announce-self to keep the example from floating up into the body; this will be addressed more rigorously in the new qapidoc generator. Signed-off-by: John Snow <jsnow@redhat.com> Message-ID: <20240717021312.606116-7-jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Markup fixed in one place] Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/acpi.json | 4 +-- qapi/block-core.json | 64 +++++++++++++++++++++------------------- qapi/block.json | 18 ++++++----- qapi/char.json | 24 +++++++++------ qapi/control.json | 8 ++--- qapi/dump.json | 8 ++--- qapi/machine-target.json | 2 +- qapi/machine.json | 38 ++++++++++++------------ qapi/migration.json | 58 ++++++++++++++++++------------------ qapi/misc-target.json | 22 +++++++------- qapi/misc.json | 32 ++++++++++---------- qapi/net.json | 22 ++++++++------ qapi/pci.json | 2 +- qapi/qdev.json | 10 ++++--- qapi/qom.json | 8 ++--- qapi/replay.json | 8 ++--- qapi/rocker.json | 8 ++--- qapi/run-state.json | 32 ++++++++++---------- qapi/tpm.json | 6 ++-- qapi/trace.json | 4 +-- qapi/transaction.json | 2 +- qapi/ui.json | 34 ++++++++++----------- qapi/vfio.json | 2 +- qapi/virtio.json | 2 +- qapi/yank.json | 4 +-- 25 files changed, 219 insertions(+), 203 deletions(-) diff --git a/qapi/acpi.json b/qapi/acpi.json index aa4dbe5794..045dab6228 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -111,7 +111,7 @@ # # Since: 2.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-acpi-ospm-status" } # <- { "return": [ { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0}, @@ -131,7 +131,7 @@ # # Since: 2.1 # -# Example: +# .. qmp-example:: # # <- { "event": "ACPI_DEVICE_OST", # "data": { "info": { "device": "d1", "slot": "0", diff --git a/qapi/block-core.json b/qapi/block-core.json index 096bdbe0aa..84a020aa9e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -764,7 +764,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-block" } # <- { @@ -1168,7 +1168,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-blockstats" } # <- { @@ -1461,7 +1461,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "block_resize", # "arguments": { "device": "scratch", "size": 1073741824 } } @@ -1680,7 +1680,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-snapshot-sync", # "arguments": { "device": "ide-hd0", @@ -1711,7 +1711,7 @@ # # Since: 2.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-add", # "arguments": { "driver": "qcow2", @@ -1857,7 +1857,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-commit", # "arguments": { "device": "virtio0", @@ -1895,7 +1895,7 @@ # # Since: 1.6 # -# Example: +# .. qmp-example:: # # -> { "execute": "drive-backup", # "arguments": { "device": "drive0", @@ -1921,7 +1921,7 @@ # # Since: 2.3 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-backup", # "arguments": { "device": "src-id", @@ -1945,7 +1945,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-named-block-nodes" } # <- { "return": [ { "ro":false, @@ -2126,7 +2126,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # -> { "execute": "drive-mirror", # "arguments": { "device": "ide-hd0", @@ -2303,7 +2303,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-dirty-bitmap-add", # "arguments": { "node": "drive0", "name": "bitmap0" } } @@ -2327,7 +2327,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-dirty-bitmap-remove", # "arguments": { "node": "drive0", "name": "bitmap0" } } @@ -2350,7 +2350,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-dirty-bitmap-clear", # "arguments": { "node": "drive0", "name": "bitmap0" } } @@ -2371,7 +2371,7 @@ # # Since: 4.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-dirty-bitmap-enable", # "arguments": { "node": "drive0", "name": "bitmap0" } } @@ -2392,7 +2392,7 @@ # # Since: 4.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-dirty-bitmap-disable", # "arguments": { "node": "drive0", "name": "bitmap0" } } @@ -2424,7 +2424,7 @@ # # Since: 4.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-dirty-bitmap-merge", # "arguments": { "node": "drive0", "target": "bitmap0", @@ -2533,7 +2533,7 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-mirror", # "arguments": { "device": "ide-hd0", @@ -2858,7 +2858,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-stream", # "arguments": { "device": "virtio0", @@ -4797,7 +4797,7 @@ # # Since: 2.9 # -# Examples: +# .. qmp-example:: # # -> { "execute": "blockdev-add", # "arguments": { @@ -4811,6 +4811,8 @@ # } # <- { "return": {} } # +# .. qmp-example:: +# # -> { "execute": "blockdev-add", # "arguments": { # "driver": "qcow2", @@ -4895,7 +4897,7 @@ # # Since: 2.9 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-add", # "arguments": { @@ -5544,7 +5546,7 @@ # .. note:: If action is "stop", a STOP event will eventually follow the # BLOCK_IO_ERROR event. # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_IMAGE_CORRUPTED", # "data": { "device": "", "node-name": "drive", "fatal": false, @@ -5593,7 +5595,7 @@ # # Since: 0.13 # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_IO_ERROR", # "data": { "device": "ide0-hd1", @@ -5633,7 +5635,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_JOB_COMPLETED", # "data": { "type": "stream", "device": "virtio-disk0", @@ -5668,7 +5670,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_JOB_CANCELLED", # "data": { "type": "stream", "device": "virtio-disk0", @@ -5697,7 +5699,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_JOB_ERROR", # "data": { "device": "ide0-hd1", @@ -5732,7 +5734,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_JOB_READY", # "data": { "device": "drive0", "type": "mirror", "speed": 0, @@ -5760,7 +5762,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # <- { "event": "BLOCK_JOB_PENDING", # "data": { "type": "mirror", "id": "backup_1" }, @@ -5834,7 +5836,7 @@ # # Since: 2.3 # -# Example: +# .. qmp-example:: # # -> { "execute": "block-set-write-threshold", # "arguments": { "node-name": "mydev", @@ -5985,7 +5987,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # <- { "event": "QUORUM_FAILURE", # "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, @@ -6070,7 +6072,7 @@ # # Since: 1.7 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-snapshot-internal-sync", # "arguments": { "device": "ide-hd0", @@ -6109,7 +6111,7 @@ # # Since: 1.7 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-snapshot-delete-internal-sync", # "arguments": { "device": "ide-hd0", diff --git a/qapi/block.json b/qapi/block.json index ea81d9e192..c8e52bc2d2 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -117,7 +117,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "eject", "arguments": { "id": "ide1-0-1" } } # <- { "return": {} } @@ -161,7 +161,7 @@ # # Since: 2.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-open-tray", # "arguments": { "id": "ide0-1-0" } } @@ -199,7 +199,7 @@ # # Since: 2.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-close-tray", # "arguments": { "id": "ide0-1-0" } } @@ -231,7 +231,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-remove-medium", # "arguments": { "id": "ide0-1-0" } } @@ -272,7 +272,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # -> { "execute": "blockdev-add", # "arguments": { @@ -397,7 +397,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # <- { "event": "DEVICE_TRAY_MOVED", # "data": { "device": "ide1-cd0", @@ -421,7 +421,7 @@ # # Since: 3.0 # -# Example: +# .. qmp-example:: # # <- { "event": "PR_MANAGER_STATUS_CHANGED", # "data": { "id": "pr-helper0", @@ -463,7 +463,7 @@ # # Since: 1.1 # -# Examples: +# .. qmp-example:: # # -> { "execute": "block_set_io_throttle", # "arguments": { "id": "virtio-blk-pci0/virtio-backend", @@ -483,6 +483,8 @@ # "iops_size": 0 } } # <- { "return": {} } # +# .. qmp-example:: +# # -> { "execute": "block_set_io_throttle", # "arguments": { "id": "ide0-1-0", # "bps": 1000000, diff --git a/qapi/char.json b/qapi/char.json index 5eabf8e764..5e4aeb9799 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -40,7 +40,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-chardev" } # <- { @@ -86,7 +86,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-chardev-backends" } # <- { @@ -141,7 +141,7 @@ # # Since: 1.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "ringbuf-write", # "arguments": { "device": "foo", @@ -177,7 +177,7 @@ # # Since: 1.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "ringbuf-read", # "arguments": { "device": "foo", @@ -699,19 +699,23 @@ # # Since: 1.4 # -# Examples: +# .. qmp-example:: # # -> { "execute" : "chardev-add", # "arguments" : { "id" : "foo", # "backend" : { "type" : "null", "data" : {} } } } # <- { "return": {} } # +# .. qmp-example:: +# # -> { "execute" : "chardev-add", # "arguments" : { "id" : "bar", # "backend" : { "type" : "file", # "data" : { "out" : "/tmp/bar.log" } } } } # <- { "return": {} } # +# .. qmp-example:: +# # -> { "execute" : "chardev-add", # "arguments" : { "id" : "baz", # "backend" : { "type" : "pty", "data" : {} } } } @@ -735,13 +739,15 @@ # # Since: 2.10 # -# Examples: +# .. qmp-example:: # # -> { "execute" : "chardev-change", # "arguments" : { "id" : "baz", # "backend" : { "type" : "pty", "data" : {} } } } # <- { "return": { "pty" : "/dev/pty/42" } } # +# .. qmp-example:: +# # -> {"execute" : "chardev-change", # "arguments" : { # "id" : "charchannel2", @@ -772,7 +778,7 @@ # # Since: 1.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } # <- { "return": {} } @@ -789,7 +795,7 @@ # # Since: 2.10 # -# Example: +# .. qmp-example:: # # -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } # <- { "return": {} } @@ -810,7 +816,7 @@ # # Since: 2.1 # -# Example: +# .. qmp-example:: # # <- { "event": "VSERPORT_CHANGE", # "data": { "id": "channel0", "open": true }, diff --git a/qapi/control.json b/qapi/control.json index fe2af45120..950443df9d 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -16,7 +16,7 @@ # the QMP greeting message. If the field is not provided, it # means no QMP capabilities will be enabled. (since 2.12) # -# Example: +# .. qmp-example:: # # -> { "execute": "qmp_capabilities", # "arguments": { "enable": [ "oob" ] } } @@ -97,7 +97,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-version" } # <- { @@ -134,7 +134,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-commands" } # <- { @@ -165,7 +165,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "quit" } # <- { "return": {} } diff --git a/qapi/dump.json b/qapi/dump.json index f9aee7ea1d..d8145dad97 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -94,7 +94,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "dump-guest-memory", # "arguments": { "paging": false, "protocol": "fd:dump" } } @@ -150,7 +150,7 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-dump" } # <- { "return": { "status": "active", "completed": 1024000, @@ -171,7 +171,7 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # # <- { "event": "DUMP_COMPLETED", # "data": { "result": { "total": 1090650112, "status": "completed", @@ -202,7 +202,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-dump-guest-memory-capability" } # <- { "return": { "formats": diff --git a/qapi/machine-target.json b/qapi/machine-target.json index a8d9ec87f5..7edb876b5c 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -475,7 +475,7 @@ # # Since: 8.2 # -# Example: +# .. qmp-example:: # # <- { "event": "CPU_POLARIZATION_CHANGE", # "data": { "polarization": "horizontal" }, diff --git a/qapi/machine.json b/qapi/machine.json index eb2fd01e95..193b2b7c16 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -104,7 +104,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-cpus-fast" } # <- { "return": [ @@ -221,7 +221,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-machines", "arguments": { "compat-props": true } } # <- { "return": [ @@ -319,7 +319,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-uuid" } # <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } @@ -353,7 +353,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "system_reset" } # <- { "return": {} } @@ -372,7 +372,7 @@ # request or that it has shut down. Many guests will respond to this # command by prompting the user in some way. # -# Example: +# .. qmp-example:: # # -> { "execute": "system_powerdown" } # <- { "return": {} } @@ -392,7 +392,7 @@ # .. note:: Prior to 4.0, this command does nothing in case the guest # isn't suspended. # -# Example: +# .. qmp-example:: # # -> { "execute": "system_wakeup" } # <- { "return": {} } @@ -443,7 +443,7 @@ # .. note:: Prior to 2.1, this command was only supported for x86 and # s390 VMs. # -# Example: +# .. qmp-example:: # # -> { "execute": "inject-nmi" } # <- { "return": {} } @@ -472,7 +472,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-kvm" } # <- { "return": { "enabled": true, "present": true } } @@ -841,7 +841,7 @@ # # .. caution:: Errors were not reliably returned until 1.1. # -# Example: +# .. qmp-example:: # # -> { "execute": "memsave", # "arguments": { "val": 10, @@ -867,7 +867,7 @@ # # .. caution:: Errors were not reliably returned until 1.1. # -# Example: +# .. qmp-example:: # # -> { "execute": "pmemsave", # "arguments": { "val": 10, @@ -928,7 +928,7 @@ # # Since: 2.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-memdev" } # <- { "return": [ @@ -1164,7 +1164,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-balloon" } # <- { "return": { @@ -1188,7 +1188,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # <- { "event": "BALLOON_CHANGE", # "data": { "actual": 944766976 }, @@ -1230,7 +1230,7 @@ # # Since: 8.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-hv-balloon-status-report" } # <- { "return": { @@ -1251,7 +1251,7 @@ # # Since: 8.2 # -# Example: +# .. qmp-example:: # # <- { "event": "HV_BALLOON_STATUS_REPORT", # "data": { "committed": 816640000, "available": 3333054464 }, @@ -1283,7 +1283,7 @@ # Return the amount of initially allocated and present hotpluggable # (if enabled) memory in bytes. # -# Example: +# .. qmp-example:: # # -> { "execute": "query-memory-size-summary" } # <- { "return": { "base-memory": 4294967296, "plugged-memory": 0 } } @@ -1562,7 +1562,7 @@ # # Since: 2.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-memory-devices" } # <- { "return": [ { "data": @@ -1596,7 +1596,7 @@ # # Since: 5.1 # -# Example: +# .. qmp-example:: # # <- { "event": "MEMORY_DEVICE_SIZE_CHANGE", # "data": { "id": "vm0", "size": 1073741824, @@ -1854,7 +1854,7 @@ # # Since: 7.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "dumpdtb" } # "arguments": { "filename": "fdt.dtb" } } diff --git a/qapi/migration.json b/qapi/migration.json index 1234bef888..3c65720238 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -514,7 +514,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-set-capabilities" , "arguments": # { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } @@ -532,7 +532,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-migrate-capabilities" } # <- { "return": [ @@ -1053,7 +1053,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-set-parameters" , # "arguments": { "multifd-channels": 5 } } @@ -1256,7 +1256,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-migrate-parameters" } # <- { "return": { @@ -1280,7 +1280,7 @@ # # Since: 2.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-start-postcopy" } # <- { "return": {} } @@ -1296,7 +1296,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, # "event": "MIGRATION", @@ -1315,7 +1315,7 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # # <- { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, # "event": "MIGRATION_PASS", "data": {"pass": 2} } @@ -1399,7 +1399,7 @@ # # Since: 3.1 # -# Example: +# .. qmp-example:: # # <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, # "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } @@ -1442,7 +1442,7 @@ # # Since: 2.8 # -# Example: +# .. qmp-example:: # # -> { "execute": "x-colo-lost-heartbeat" } # <- { "return": {} } @@ -1461,7 +1461,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate_cancel" } # <- { "return": {} } @@ -1477,7 +1477,7 @@ # # Since: 2.11 # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-continue" , "arguments": # { "state": "pre-switchover" } } @@ -1610,7 +1610,7 @@ # 6. The 'uri' and 'channels' arguments are mutually exclusive; # exactly one of the two should be present. # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } # <- { "return": {} } @@ -1689,7 +1689,7 @@ # 5. The 'uri' and 'channels' arguments are mutually exclusive; # exactly one of the two should be present. # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-incoming", # "arguments": { "uri": "tcp:0:4446" } } @@ -1740,7 +1740,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-save-devices-state", # "arguments": { "filename": "/tmp/save" } } @@ -1758,7 +1758,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-set-global-dirty-log", # "arguments": { "enable": true } } @@ -1778,7 +1778,7 @@ # # Since: 2.7 # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-load-devices-state", # "arguments": { "filename": "/tmp/resume" } } @@ -1798,7 +1798,7 @@ # @failover: true to do failover, false to stop. Cannot be specified # if 'enable' is true. Default value is false. # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-set-replication", # "arguments": {"enable": true, "primary": false} } @@ -1833,7 +1833,7 @@ # # Returns: A @ReplicationStatus object showing the status. # -# Example: +# .. qmp-example:: # # -> { "execute": "query-xen-replication-status" } # <- { "return": { "error": false } } @@ -1849,7 +1849,7 @@ # # Xen uses this command to notify replication to trigger a checkpoint. # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-colo-do-checkpoint" } # <- { "return": {} } @@ -1887,7 +1887,7 @@ # # Returns: A @COLOStatus object showing the status. # -# Example: +# .. qmp-example:: # # -> { "execute": "query-colo-status" } # <- { "return": { "mode": "primary", "last-mode": "none", "reason": "request" } } @@ -1905,7 +1905,7 @@ # # @uri: the URI to be used for the recovery of migration stream. # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-recover", # "arguments": { "uri": "tcp:192.168.1.200:12345" } } @@ -1922,7 +1922,7 @@ # # Pause a migration. Currently it only supports postcopy. # -# Example: +# .. qmp-example:: # # -> { "execute": "migrate-pause" } # <- { "return": {} } @@ -1943,7 +1943,7 @@ # # Since: 4.2 # -# Example: +# .. qmp-example:: # # <- { "event": "UNPLUG_PRIMARY", # "data": { "device-id": "hostdev0" }, @@ -2182,7 +2182,7 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # # -> {"execute": "set-vcpu-dirty-limit"} # "arguments": { "dirty-rate": 200, @@ -2206,7 +2206,7 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # # -> {"execute": "cancel-vcpu-dirty-limit"}, # "arguments": { "cpu-index": 1 } } @@ -2223,7 +2223,7 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # # -> {"execute": "query-vcpu-dirty-limit"} # <- {"return": [ @@ -2287,7 +2287,7 @@ # # If @tag already exists, an error will be reported # -# Example: +# .. qmp-example:: # # -> { "execute": "snapshot-save", # "arguments": { @@ -2357,7 +2357,7 @@ # device nodes that can have changed since the original @snapshot-save # command execution. # -# Example: +# .. qmp-example:: # # -> { "execute": "snapshot-load", # "arguments": { @@ -2418,7 +2418,7 @@ # to determine completion and to fetch details of any errors that # arise. # -# Example: +# .. qmp-example:: # # -> { "execute": "snapshot-delete", # "arguments": { diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 2d7d4d89bd..8d70bd24d8 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -11,7 +11,7 @@ # # Since: 2.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "rtc-reset-reinjection" } # <- { "return": {} } @@ -133,7 +133,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-sev" } # <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, @@ -164,7 +164,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-sev-launch-measure" } # <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } @@ -209,7 +209,7 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-sev-capabilities" } # <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", @@ -263,7 +263,7 @@ # # Since: 6.1 # -# Example: +# .. qmp-example:: # # -> { "execute" : "query-sev-attestation-report", # "arguments": { "mnonce": "aaaaaaa" } } @@ -283,7 +283,7 @@ # # Since: 2.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "dump-skeys", # "arguments": { "filename": "/tmp/skeys" } } @@ -328,7 +328,7 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-gic-capabilities" } # <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, @@ -386,7 +386,7 @@ # # Since: 6.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-sgx" } # <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, @@ -405,7 +405,7 @@ # # Since: 6.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-sgx-capabilities" } # <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, @@ -480,7 +480,7 @@ # # Since: 8.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-event-list" } # <- { "return": [ @@ -518,7 +518,7 @@ # # Since: 8.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "xen-event-inject", "arguments": { "port": 1 } } # <- { "return": { } } diff --git a/qapi/misc.json b/qapi/misc.json index b04efbadec..4a6f3baeae 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -30,7 +30,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "add_client", "arguments": { "protocol": "vnc", # "fdname": "myclient" } } @@ -60,7 +60,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-name" } # <- { "return": { "name": "qemu-name" } } @@ -111,7 +111,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-iothreads" } # <- { "return": [ @@ -144,7 +144,7 @@ # In the "suspended" state, it will completely stop the VM and cause # a transition to the "paused" state. (Since 9.0) # -# Example: +# .. qmp-example:: # # -> { "execute": "stop" } # <- { "return": {} } @@ -168,7 +168,7 @@ # this command will transition back to the "suspended" state. (Since # 9.0) # -# Example: +# .. qmp-example:: # # -> { "execute": "cont" } # <- { "return": {} } @@ -192,7 +192,7 @@ # # Since: 3.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "x-exit-preconfig" } # <- { "return": {} } @@ -232,7 +232,7 @@ # # * Commands that prompt the user for data don't currently work. # -# Example: +# .. qmp-example:: # # -> { "execute": "human-monitor-command", # "arguments": { "command-line": "info kvm" } } @@ -258,7 +258,7 @@ # The 'closefd' command can be used to explicitly close the file # descriptor when it is no longer needed. # -# Example: +# .. qmp-example:: # # -> { "execute": "getfd", "arguments": { "fdname": "fd1" } } # <- { "return": {} } @@ -285,7 +285,7 @@ # The 'closefd' command can be used to explicitly close the file # descriptor when it is no longer needed. # -# Example: +# .. qmp-example:: # # -> { "execute": "get-win32-socket", # "arguments": { "info": "abcd123..", "fdname": "skclient" } } @@ -302,7 +302,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } # <- { "return": {} } @@ -345,7 +345,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } } # <- { "return": { "fdset-id": 1, "fd": 3 } } @@ -374,7 +374,7 @@ # .. note:: If @fd is not specified, all file descriptors in @fdset-id # will be removed. # -# Example: +# .. qmp-example:: # # -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } } # <- { "return": {} } @@ -420,7 +420,7 @@ # # .. note:: The list of fd sets is shared by all monitor connections. # -# Example: +# .. qmp-example:: # # -> { "execute": "query-fdsets" } # <- { "return": [ @@ -523,7 +523,7 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-command-line-options", # "arguments": { "option": "option-rom" } } @@ -565,7 +565,7 @@ # # Since: 0.13 # -# Example: +# .. qmp-example:: # # <- { "event": "RTC_CHANGE", # "data": { "offset": 78 }, @@ -592,7 +592,7 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # # <- { "event": "VFU_CLIENT_HANGUP", # "data": { "vfu-id": "vfu1", diff --git a/qapi/net.json b/qapi/net.json index dd6c365c34..31b3417d65 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -26,7 +26,7 @@ # command will succeed even if the network adapter does not support # link status notification. # -# Example: +# .. qmp-example:: # # -> { "execute": "set_link", # "arguments": { "name": "e1000.0", "up": false } } @@ -46,7 +46,7 @@ # Errors: # - If @type is not a valid network backend, DeviceNotFound # -# Example: +# .. qmp-example:: # # -> { "execute": "netdev_add", # "arguments": { "type": "user", "id": "netdev1", @@ -68,7 +68,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } # <- { "return": {} } @@ -836,7 +836,7 @@ # # Since: 1.6 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } } # <- { "return": [ @@ -881,7 +881,7 @@ # # Since: 1.6 # -# Example: +# .. qmp-example:: # # <- { "event": "NIC_RX_FILTER_CHANGED", # "data": { "name": "vnet0", @@ -930,7 +930,9 @@ # switches. This can be useful when network bonds fail-over the # active slave. # -# Example: +# TODO: This line is a hack to separate the example from the body +# +# .. qmp-example:: # # -> { "execute": "announce-self", # "arguments": { @@ -955,7 +957,7 @@ # # Since: 4.2 # -# Example: +# .. qmp-example:: # # <- { "event": "FAILOVER_NEGOTIATED", # "data": { "device-id": "net1" }, @@ -975,7 +977,7 @@ # # Since: 7.2 # -# Examples: +# .. qmp-example:: # # <- { "event": "NETDEV_STREAM_CONNECTED", # "data": { "netdev-id": "netdev0", @@ -983,6 +985,8 @@ # "host": "::1", "type": "inet" } }, # "timestamp": { "seconds": 1666269863, "microseconds": 311222 } } # +# .. qmp-example:: +# # <- { "event": "NETDEV_STREAM_CONNECTED", # "data": { "netdev-id": "netdev0", # "addr": { "path": "/tmp/qemu0", "type": "unix" } }, @@ -1001,7 +1005,7 @@ # # Since: 7.2 # -# Example: +# .. qmp-example:: # # <- { "event": "NETDEV_STREAM_DISCONNECTED", # "data": {"netdev-id": "netdev0"}, diff --git a/qapi/pci.json b/qapi/pci.json index 97179920fb..ec28f1d9b4 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -180,7 +180,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-pci" } # <- { "return": [ diff --git a/qapi/qdev.json b/qapi/qdev.json index d031fc3590..e91ca0309d 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -62,7 +62,7 @@ # the ``-device DEVICE,help`` command-line argument, where DEVICE # is the device's name. # -# Example: +# .. qmp-example:: # # -> { "execute": "device_add", # "arguments": { "driver": "e1000", "id": "net1", @@ -104,12 +104,14 @@ # # Since: 0.14 # -# Examples: +# .. qmp-example:: # # -> { "execute": "device_del", # "arguments": { "id": "net1" } } # <- { "return": {} } # +# .. qmp-example:: +# # -> { "execute": "device_del", # "arguments": { "id": "/machine/peripheral-anon/device[0]" } } # <- { "return": {} } @@ -130,7 +132,7 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # # <- { "event": "DEVICE_DELETED", # "data": { "device": "virtio-net-pci-0", @@ -152,7 +154,7 @@ # # Since: 6.2 # -# Example: +# .. qmp-example:: # # <- { "event": "DEVICE_UNPLUG_GUEST_ERROR", # "data": { "device": "core1", diff --git a/qapi/qom.json b/qapi/qom.json index 3e52aec8fb..46096a8fa4 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -59,7 +59,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "qom-list", # "arguments": { "path": "/chardevs" } } @@ -139,7 +139,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "qom-set", # "arguments": { "path": "/machine", @@ -1172,7 +1172,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "object-add", # "arguments": { "qom-type": "rng-random", "id": "rng1", @@ -1194,7 +1194,7 @@ # # Since: 2.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "object-del", "arguments": { "id": "rng1" } } # <- { "return": {} } diff --git a/qapi/replay.json b/qapi/replay.json index d3559f9c8f..35e0c4a692 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -54,7 +54,7 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-replay" } # <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } @@ -76,7 +76,7 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "replay-break", "arguments": { "icount": 220414 } } # <- { "return": {} } @@ -91,7 +91,7 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "replay-delete-break" } # <- { "return": {} } @@ -112,7 +112,7 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "replay-seek", "arguments": { "icount": 220414 } } # <- { "return": {} } diff --git a/qapi/rocker.json b/qapi/rocker.json index 9f95e63830..2e63dcb3d6 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -30,7 +30,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } # <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} @@ -98,7 +98,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } } # <- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1", @@ -240,7 +240,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-rocker-of-dpa-flows", # "arguments": { "name": "sw1" } } @@ -315,7 +315,7 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-rocker-of-dpa-groups", # "arguments": { "name": "sw1" } } diff --git a/qapi/run-state.json b/qapi/run-state.json index 4d40c88876..287691ca0e 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -123,7 +123,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-status" } # <- { "return": { "running": true, @@ -152,7 +152,7 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # # <- { "event": "SHUTDOWN", # "data": { "guest": true, "reason": "guest-shutdown" }, @@ -168,7 +168,7 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # # <- { "event": "POWERDOWN", # "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } @@ -189,7 +189,7 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # # <- { "event": "RESET", # "data": { "guest": false, "reason": "guest-reset" }, @@ -204,7 +204,7 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # # <- { "event": "STOP", # "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } @@ -218,7 +218,7 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # # <- { "event": "RESUME", # "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } @@ -233,7 +233,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # <- { "event": "SUSPEND", # "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } @@ -252,7 +252,7 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # # <- { "event": "SUSPEND_DISK", # "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } @@ -267,7 +267,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # <- { "event": "WAKEUP", # "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } @@ -289,7 +289,7 @@ # # Since: 0.13 # -# Example: +# .. qmp-example:: # # <- { "event": "WATCHDOG", # "data": { "action": "reset" }, @@ -382,7 +382,7 @@ # # Since: 2.11 # -# Example: +# .. qmp-example:: # # -> { "execute": "watchdog-set-action", # "arguments": { "action": "inject-nmi" } } @@ -406,7 +406,7 @@ # # Since: 6.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "set-action", # "arguments": { "reboot": "shutdown", @@ -433,7 +433,7 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # # <- { "event": "GUEST_PANICKED", # "data": { "action": "pause" }, @@ -453,7 +453,7 @@ # # Since: 5.0 # -# Example: +# .. qmp-example:: # # <- { "event": "GUEST_CRASHLOADED", # "data": { "action": "run" }, @@ -469,7 +469,7 @@ # # Since: 9.1 # -# Example: +# .. qmp-example:: # # <- { "event": "GUEST_PVSHUTDOWN", # "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } @@ -611,7 +611,7 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # # <- { "event": "MEMORY_FAILURE", # "data": { "recipient": "hypervisor", diff --git a/qapi/tpm.json b/qapi/tpm.json index 1577b5c259..a16a72edb9 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -31,7 +31,7 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-tpm-models" } # <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } @@ -62,7 +62,7 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-tpm-types" } # <- { "return": [ "passthrough", "emulator" ] } @@ -168,7 +168,7 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-tpm" } # <- { "return": diff --git a/qapi/trace.json b/qapi/trace.json index 9ebb6d9eaf..eb5f63f513 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -51,7 +51,7 @@ # # Since: 2.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "trace-event-get-state", # "arguments": { "name": "qemu_memalign" } } @@ -74,7 +74,7 @@ # # Since: 2.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "trace-event-set-state", # "arguments": { "name": "qemu_memalign", "enable": true } } diff --git a/qapi/transaction.json b/qapi/transaction.json index bcb05fdedd..b0ae3437eb 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -244,7 +244,7 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "transaction", # "arguments": { "actions": [ diff --git a/qapi/ui.json b/qapi/ui.json index df089869a5..6bda98417b 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -83,7 +83,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "set_password", "arguments": { "protocol": "vnc", # "password": "secret" } } @@ -144,7 +144,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "expire_password", "arguments": { "protocol": "vnc", # "time": "+60" } } @@ -186,7 +186,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "screendump", # "arguments": { "filename": "/tmp/image" } } @@ -328,7 +328,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-spice" } # <- { "return": { @@ -377,7 +377,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, # "event": "SPICE_CONNECTED", @@ -403,7 +403,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, # "event": "SPICE_INITIALIZED", @@ -430,7 +430,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, # "event": "SPICE_DISCONNECTED", @@ -451,7 +451,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, # "event": "SPICE_MIGRATE_COMPLETED" } @@ -659,7 +659,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-vnc" } # <- { "return": { @@ -724,7 +724,7 @@ # # Since: 0.13 # -# Example: +# .. qmp-example:: # # <- { "event": "VNC_CONNECTED", # "data": { @@ -751,7 +751,7 @@ # # Since: 0.13 # -# Example: +# .. qmp-example:: # # <- { "event": "VNC_INITIALIZED", # "data": { @@ -777,7 +777,7 @@ # # Since: 0.13 # -# Example: +# .. qmp-example:: # # <- { "event": "VNC_DISCONNECTED", # "data": { @@ -825,7 +825,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "query-mice" } # <- { "return": [ @@ -1034,7 +1034,7 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # # -> { "execute": "send-key", # "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" }, @@ -1613,7 +1613,7 @@ # # Since: 6.0 # -# Example: +# .. qmp-example:: # # -> { "execute": "display-reload", # "arguments": { "type": "vnc", "tls-certs": true } } @@ -1670,7 +1670,7 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # # -> { "execute": "display-update", # "arguments": { "type": "vnc", "addresses": @@ -1701,7 +1701,7 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # # -> { "execute": "client_migrate_info", # "arguments": { "protocol": "spice", diff --git a/qapi/vfio.json b/qapi/vfio.json index a0e5013188..40cbcde02e 100644 --- a/qapi/vfio.json +++ b/qapi/vfio.json @@ -50,7 +50,7 @@ # # Since: 9.1 # -# Example: +# .. qmp-example:: # # <- { "timestamp": { "seconds": 1713771323, "microseconds": 212268 }, # "event": "VFIO_MIGRATION", diff --git a/qapi/virtio.json b/qapi/virtio.json index b91f3cdd0d..f4323cc35e 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -34,7 +34,7 @@ # # Since: 7.2 # -# Example: +# .. qmp-example:: # # -> { "execute": "x-query-virtio" } # <- { "return": [ diff --git a/qapi/yank.json b/qapi/yank.json index 89f2f4d199..30f46c97c9 100644 --- a/qapi/yank.json +++ b/qapi/yank.json @@ -81,7 +81,7 @@ # Errors: # - If any of the YankInstances doesn't exist, DeviceNotFound # -# Example: +# .. qmp-example:: # # -> { "execute": "yank", # "arguments": { @@ -104,7 +104,7 @@ # # Returns: list of @YankInstance # -# Example: +# .. qmp-example:: # # -> { "execute": "query-yank" } # <- { "return": [ From a9eab6e2e65f5b876ededec1cd4bacc58cd4bce0 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:09 -0400 Subject: [PATCH 2166/2884] qapi: convert "Example" sections with titles When an Example section has a brief explanation, convert it to a qmp-example:: section using the :title: option. Rule of thumb: If the title can fit on a single line and requires no rST markup, it's a good candidate for using the :title: option of qmp-example. In this patch, trailing punctuation is removed from the title section for consistent headline aesthetics. In just one case, specifics of the example are removed to make the title read better. See commit-4: "docs/qapidoc: create qmp-example directive", for a detailed explanation of this custom directive syntax. See commit+2: "qapi: remove "Example" doc section" for a detailed explanation of why. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240717021312.606116-8-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/block-core.json | 24 ++++++++++++------------ qapi/block.json | 13 ++++++------- qapi/migration.json | 25 ++++++++++++++----------- qapi/qom.json | 8 ++++---- qapi/ui.json | 11 ++++++----- qapi/virtio.json | 19 ++++++++++--------- 6 files changed, 52 insertions(+), 48 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 84a020aa9e..f400b334c8 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -5881,9 +5881,8 @@ # # Since: 2.7 # -# Examples: -# -# 1. Add a new node to a quorum +# .. qmp-example:: +# :title: Add a new node to a quorum # # -> { "execute": "blockdev-add", # "arguments": { @@ -5897,7 +5896,8 @@ # "node": "new_node" } } # <- { "return": {} } # -# 2. Delete a quorum's node +# .. qmp-example:: +# :title: Delete a quorum's node # # -> { "execute": "x-blockdev-change", # "arguments": { "parent": "disk1", @@ -5933,16 +5933,16 @@ # # Since: 2.12 # -# Examples: -# -# 1. Move a node into an IOThread +# .. qmp-example:: +# :title: Move a node into an IOThread # # -> { "execute": "x-blockdev-set-iothread", # "arguments": { "node-name": "disk1", # "iothread": "iothread0" } } # <- { "return": {} } # -# 2. Move a node into the main loop +# .. qmp-example:: +# :title: Move a node into the main loop # # -> { "execute": "x-blockdev-set-iothread", # "arguments": { "node-name": "disk1", @@ -6018,16 +6018,16 @@ # # Since: 2.0 # -# Examples: -# -# 1. Read operation +# .. qmp-example:: +# :title: Read operation # # <- { "event": "QUORUM_REPORT_BAD", # "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5, # "type": "read" }, # "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } # -# 2. Flush operation +# .. qmp-example:: +# :title: Flush operation # # <- { "event": "QUORUM_REPORT_BAD", # "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120, diff --git a/qapi/block.json b/qapi/block.json index c8e52bc2d2..5ddd061e96 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -342,9 +342,8 @@ # # Since: 2.5 # -# Examples: -# -# 1. Change a removable medium +# .. qmp-example:: +# :title: Change a removable medium # # -> { "execute": "blockdev-change-medium", # "arguments": { "id": "ide0-1-0", @@ -352,7 +351,8 @@ # "format": "raw" } } # <- { "return": {} } # -# 2. Load a read-only medium into a writable drive +# .. qmp-example:: +# :title: Load a read-only medium into a writable drive # # -> { "execute": "blockdev-change-medium", # "arguments": { "id": "floppyA", @@ -577,9 +577,8 @@ # "boundaries-write": [1000, 5000] } } # <- { "return": {} } # -# Example: -# -# Remove all latency histograms: +# .. qmp-example:: +# :title: Remove all latency histograms # # -> { "execute": "block-latency-histogram-set", # "arguments": { "id": "drive0" } } diff --git a/qapi/migration.json b/qapi/migration.json index 3c65720238..74968a1417 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -287,14 +287,14 @@ # # Since: 0.14 # -# Examples: -# -# 1. Before the first migration +# .. qmp-example:: +# :title: Before the first migration # # -> { "execute": "query-migrate" } # <- { "return": {} } # -# 2. Migration is done and has succeeded +# .. qmp-example:: +# :title: Migration is done and has succeeded # # -> { "execute": "query-migrate" } # <- { "return": { @@ -314,12 +314,14 @@ # } # } # -# 3. Migration is done and has failed +# .. qmp-example:: +# :title: Migration is done and has failed # # -> { "execute": "query-migrate" } # <- { "return": { "status": "failed" } } # -# 4. Migration is being performed: +# .. qmp-example:: +# :title: Migration is being performed # # -> { "execute": "query-migrate" } # <- { @@ -340,7 +342,8 @@ # } # } # -# 5. Migration is being performed and XBZRLE is active: +# .. qmp-example:: +# :title: Migration is being performed and XBZRLE is active # # -> { "execute": "query-migrate" } # <- { @@ -2131,15 +2134,15 @@ # # Since: 5.2 # -# Examples: -# -# 1. Measurement is in progress: +# .. qmp-example:: +# :title: Measurement is in progress # # <- {"status": "measuring", "sample-pages": 512, # "mode": "page-sampling", "start-time": 1693900454, "calc-time": 10, # "calc-time-unit": "second"} # -# 2. Measurement has been completed: +# .. qmp-example:: +# :title: Measurement has been completed # # <- {"status": "measured", "sample-pages": 512, "dirty-rate": 108, # "mode": "page-sampling", "start-time": 1693900454, "calc-time": 10, diff --git a/qapi/qom.json b/qapi/qom.json index 46096a8fa4..ae947e0fd9 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -104,16 +104,16 @@ # # Since: 1.2 # -# Examples: -# -# 1. Use absolute path +# .. qmp-example:: +# :title: Use absolute path # # -> { "execute": "qom-get", # "arguments": { "path": "/machine/unattached/device[0]", # "property": "hotplugged" } } # <- { "return": false } # -# 2. Use partial path +# .. qmp-example:: +# :title: Use partial path # # -> { "execute": "qom-get", # "arguments": { "path": "unattached/sysbus", diff --git a/qapi/ui.json b/qapi/ui.json index 6bda98417b..5daca5168c 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1270,9 +1270,8 @@ # property, so it is possible to map which console belongs to which # device and display. # -# Examples: -# -# 1. Press left mouse button. +# .. qmp-example:: +# :title: Press left mouse button # # -> { "execute": "input-send-event", # "arguments": { "device": "video0", @@ -1286,7 +1285,8 @@ # "data" : { "down": false, "button": "left" } } ] } } # <- { "return": {} } # -# 2. Press ctrl-alt-del. +# .. qmp-example:: +# :title: Press ctrl-alt-del # # -> { "execute": "input-send-event", # "arguments": { "events": [ @@ -1298,7 +1298,8 @@ # "key": {"type": "qcode", "data": "delete" } } } ] } } # <- { "return": {} } # -# 3. Move mouse pointer to absolute coordinates (20000, 400). +# .. qmp-example:: +# :title: Move mouse pointer to absolute coordinates # # -> { "execute": "input-send-event" , # "arguments": { "events": [ diff --git a/qapi/virtio.json b/qapi/virtio.json index f4323cc35e..d965c98ad2 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -690,9 +690,8 @@ # # Since: 7.2 # -# Examples: -# -# 1. Get vhost_virtqueue status for vhost-crypto +# .. qmp-example:: +# :title: Get vhost_virtqueue status for vhost-crypto # # -> { "execute": "x-query-virtio-vhost-queue-status", # "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend", @@ -715,7 +714,8 @@ # } # } # -# 2. Get vhost_virtqueue status for vhost-vsock +# .. qmp-example:: +# :title: Get vhost_virtqueue status for vhost-vsock # # -> { "execute": "x-query-virtio-vhost-queue-status", # "arguments": { "path": "/machine/peripheral/vsock0/virtio-backend", @@ -839,9 +839,8 @@ # # Since: 7.2 # -# Examples: -# -# 1. Introspect on virtio-net's VirtQueue 0 at index 5 +# .. qmp-example:: +# :title: Introspect on virtio-net's VirtQueue 0 at index 5 # # -> { "execute": "x-query-virtio-queue-element", # "arguments": { "path": "/machine/peripheral-anon/device[1]/virtio-backend", @@ -870,7 +869,8 @@ # } # } # -# 2. Introspect on virtio-crypto's VirtQueue 1 at head +# .. qmp-example:: +# :title: Introspect on virtio-crypto's VirtQueue 1 at head # # -> { "execute": "x-query-virtio-queue-element", # "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend", @@ -898,7 +898,8 @@ # } # } # -# 3. Introspect on virtio-scsi's VirtQueue 2 at head +# .. qmp-example:: +# :title: Introspect on virtio-scsi's VirtQueue 2 at head # # -> { "execute": "x-query-virtio-queue-element", # "arguments": { "path": "/machine/peripheral-anon/device[2]/virtio-backend", From 6f07c59fb16c68e91de82c086ce6c27d9e4748ca Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:10 -0400 Subject: [PATCH 2167/2884] qapi: convert "Example" sections with longer prose These examples require longer explanations or have explanations that require markup to look reasonable when rendered and so use the longer form of the ".. qmp-example::" directive. By using the :annotated: option, the content in the example block is assumed *not* to be a code block literal and is instead parsed as normal rST - with the exception that any code literal blocks after `::` will assumed to be a QMP code literal block. Note: There's one title-less conversion in this patch that comes along for the ride because it's part of a larger "Examples" block that was better to convert all at once. See commit-5: "docs/qapidoc: create qmp-example directive", for a detailed explanation of this custom directive syntax. See commit+1: "qapi: remove "Example" doc section" for a detailed explanation of why. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240717021312.606116-9-jsnow@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/block.json | 30 +++++++++++++++++++----------- qapi/machine.json | 30 ++++++++++++++++++++---------- qapi/migration.json | 7 +++++-- qapi/virtio.json | 24 ++++++++++++++++++------ 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/qapi/block.json b/qapi/block.json index 5ddd061e96..ce9490a367 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -545,31 +545,37 @@ # # Since: 4.0 # -# Example: +# .. qmp-example:: +# :annotated: # -# Set new histograms for all io types with intervals -# [0, 10), [10, 50), [50, 100), [100, +inf): +# Set new histograms for all io types with intervals +# [0, 10), [10, 50), [50, 100), [100, +inf):: # # -> { "execute": "block-latency-histogram-set", # "arguments": { "id": "drive0", # "boundaries": [10, 50, 100] } } # <- { "return": {} } # -# Example: +# .. qmp-example:: +# :annotated: # -# Set new histogram only for write, other histograms will remain -# not changed (or not created): +# Set new histogram only for write, other histograms will remain +# not changed (or not created):: # # -> { "execute": "block-latency-histogram-set", # "arguments": { "id": "drive0", # "boundaries-write": [10, 50, 100] } } # <- { "return": {} } # -# Example: +# .. qmp-example:: +# :annotated: # -# Set new histograms with the following intervals: -# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf) -# write: [0, 1000), [1000, 5000), [5000, +inf) +# Set new histograms with the following intervals: +# +# - read, flush: [0, 10), [10, 50), [50, 100), [100, +inf) +# - write: [0, 1000), [1000, 5000), [5000, +inf) +# +# :: # # -> { "execute": "block-latency-histogram-set", # "arguments": { "id": "drive0", @@ -578,7 +584,9 @@ # <- { "return": {} } # # .. qmp-example:: -# :title: Remove all latency histograms +# :annotated: +# +# Remove all latency histograms:: # # -> { "execute": "block-latency-histogram-set", # "arguments": { "id": "drive0" } } diff --git a/qapi/machine.json b/qapi/machine.json index 193b2b7c16..f9ea6b3e97 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1045,10 +1045,11 @@ # # Since: 2.7 # -# Examples: +# .. qmp-example:: +# :annotated: # -# For pseries machine type started with -smp 2,cores=2,maxcpus=4 -# -cpu POWER8: +# For pseries machine type started with +# ``-smp 2,cores=2,maxcpus=4 -cpu POWER8``:: # # -> { "execute": "query-hotpluggable-cpus" } # <- {"return": [ @@ -1058,7 +1059,10 @@ # "vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"} # ]} # -# For pc machine type started with -smp 1,maxcpus=2: +# .. qmp-example:: +# :annotated: +# +# For pc machine type started with ``-smp 1,maxcpus=2``:: # # -> { "execute": "query-hotpluggable-cpus" } # <- {"return": [ @@ -1073,8 +1077,11 @@ # } # ]} # -# For s390x-virtio-ccw machine type started with -smp 1,maxcpus=2 -# -cpu qemu (Since: 2.11): +# .. qmp-example:: +# :annotated: +# +# For s390x-virtio-ccw machine type started with +# ``-smp 1,maxcpus=2 -cpu qemu`` (Since: 2.11):: # # -> { "execute": "query-hotpluggable-cpus" } # <- {"return": [ @@ -1128,12 +1135,15 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: +# :annotated: # -# -> { "execute": "balloon", "arguments": { "value": 536870912 } } -# <- { "return": {} } +# :: # -# With a 2.5GiB guest this command inflated the ballon to 3GiB. +# -> { "execute": "balloon", "arguments": { "value": 536870912 } } +# <- { "return": {} } +# +# With a 2.5GiB guest this command inflated the ballon to 3GiB. ## { 'command': 'balloon', 'data': {'value': 'int'} } diff --git a/qapi/migration.json b/qapi/migration.json index 74968a1417..073b67c052 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -2106,13 +2106,16 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # # -> {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, # "sample-pages": 512} } # <- { "return": {} } # -# Measure dirty rate using dirty bitmap for 500 milliseconds: +# .. qmp-example:: +# :annotated: +# +# Measure dirty rate using dirty bitmap for 500 milliseconds:: # # -> {"execute": "calc-dirty-rate", "arguments": {"calc-time": 500, # "calc-time-unit": "millisecond", "mode": "dirty-bitmap"} } diff --git a/qapi/virtio.json b/qapi/virtio.json index d965c98ad2..26df8b3064 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -203,9 +203,11 @@ # # Since: 7.2 # -# Examples: +# .. qmp-example:: +# :annotated: # -# 1. Poll for the status of virtio-crypto (no vhost-crypto active) +# Poll for the status of virtio-crypto (no vhost-crypto active) +# :: # # -> { "execute": "x-query-virtio-status", # "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend" } @@ -261,7 +263,11 @@ # } # } # -# 2. Poll for the status of virtio-net (vhost-net is active) +# .. qmp-example:: +# :annotated: +# +# Poll for the status of virtio-net (vhost-net is active) +# :: # # -> { "execute": "x-query-virtio-status", # "arguments": { "path": "/machine/peripheral-anon/device[1]/virtio-backend" } @@ -568,9 +574,11 @@ # # Since: 7.2 # -# Examples: +# .. qmp-example:: +# :annotated: # -# 1. Get VirtQueueStatus for virtio-vsock (vhost-vsock running) +# Get VirtQueueStatus for virtio-vsock (vhost-vsock running) +# :: # # -> { "execute": "x-query-virtio-queue-status", # "arguments": { "path": "/machine/peripheral/vsock0/virtio-backend", @@ -593,7 +601,11 @@ # } # } # -# 2. Get VirtQueueStatus for virtio-serial (no vhost) +# .. qmp-example:: +# :annotated: +# +# Get VirtQueueStatus for virtio-serial (no vhost) +# :: # # -> { "execute": "x-query-virtio-queue-status", # "arguments": { "path": "/machine/peripheral-anon/device[0]/virtio-backend", From 3c5f6114d9ffc70bc9b1a7cc0dddd72a911f966d Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Tue, 16 Jul 2024 22:13:11 -0400 Subject: [PATCH 2168/2884] qapi: remove "Example" doc section Fully eliminate the "Example" sections in QAPI doc blocks now that they have all been converted to arbitrary rST syntax using the ".. qmp-example::" directive. Update tests to match. Migrating to the new syntax --------------------------- The old "Example:" or "Examples:" section syntax is now caught as an error, but "Example::" is stil permitted as explicit rST syntax for an un-lexed, generic preformatted text block. ('Example' is not special in this case, any sentence that ends with "::" will start an indented code block in rST.) Arbitrary rST for Examples is now possible, but it's strongly recommended that documentation authors use the ".. qmp-example::" directive for consistent visual formatting in rendered HTML docs. The ":title:" directive option may be used to add extra information into the title bar for the example. The ":annotated:" option can be used to write arbitrary rST instead, with nested "::" blocks applying QMP formatting where desired. Other choices available are ".. code-block:: QMP" which will not create an "Example:" box, or the short-form "::" code-block syntax which will not apply QMP highlighting when used outside of the qmp-example directive. Why? ---- This patch has several benefits: 1. Example sections can now be written more arbitrarily, mixing explanatory paragraphs and code blocks however desired. 2. Example sections can now use fully arbitrary rST. 3. All code blocks are now lexed and validated as QMP; increasing usability of the docs and ensuring validity of example snippets. (To some extent - This patch only gaurantees it lexes correctly, not that it's valid under the JSON or QMP grammars. It will catch most small mistakes, however.) 4. Each qmp-example can be titled or annotated independently without bypassing the QMP lexer/validator. (i.e. code blocks are now for *code* only, so we don't have to sacrifice exposition for having lexically valid examples.) NOTE: As with the "Notes" conversion (d461c279737), this patch (and the three preceding) may change the rendering order for Examples in the current generator. The forthcoming qapidoc rewrite will fix this by always generating documentation in source order. Signed-off-by: John Snow <jsnow@redhat.com> Message-ID: <20240717021312.606116-10-jsnow@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 58 ++++++++++++++++++++++++++++----- scripts/qapi/parser.py | 10 +++++- tests/qapi-schema/doc-good.json | 19 +++++++---- tests/qapi-schema/doc-good.out | 26 ++++++++++----- tests/qapi-schema/doc-good.txt | 23 ++++++------- 5 files changed, 98 insertions(+), 38 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index ae97b335cb..583207a8ec 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -899,7 +899,7 @@ Documentation markup ~~~~~~~~~~~~~~~~~~~~ Documentation comments can use most rST markup. In particular, -a ``::`` literal block can be used for examples:: +a ``::`` literal block can be used for pre-formatted text:: # :: # @@ -995,8 +995,8 @@ line "Features:", like this:: # @feature: Description text A tagged section begins with a paragraph that starts with one of the -following words: "Since:", "Example:"/"Examples:", "Returns:", -"Errors:", "TODO:". It ends with the start of a new section. +following words: "Since:", "Returns:", "Errors:", "TODO:". It ends with +the start of a new section. The second and subsequent lines of tagged sections must be indented like this:: @@ -1020,13 +1020,53 @@ detailing a relevant error condition. For example:: A "Since: x.y.z" tagged section lists the release that introduced the definition. -An "Example" or "Examples" section is rendered entirely -as literal fixed-width text. "TODO" sections are not rendered at all -(they are for developers, not users of QMP). In other sections, the -text is formatted, and rST markup can be used. +"TODO" sections are not rendered (they are for developers, not users of +QMP). In other sections, the text is formatted, and rST markup can be +used. + +QMP Examples can be added by using the ``.. qmp-example::`` +directive. In its simplest form, this can be used to contain a single +QMP code block which accepts standard JSON syntax with additional server +directionality indicators (``->`` and ``<-``), and elisions (``...``). + +Optionally, a plaintext title may be provided by using the ``:title:`` +directive option. If the title is omitted, the example title will +default to "Example:". + +A simple QMP example:: + + # .. qmp-example:: + # :title: Using query-block + # + # -> { "execute": "query-block" } + # <- { ... } + +More complex or multi-step examples where exposition is needed before or +between QMP code blocks can be created by using the ``:annotated:`` +directive option. When using this option, nested QMP code blocks must be +entered explicitly with rST's ``::`` syntax. + +Highlighting in non-QMP languages can be accomplished by using the +``.. code-block:: lang`` directive, and non-highlighted text can be +achieved by omitting the language argument. For example:: + # .. qmp-example:: + # :annotated: + # :title: A more complex demonstration + # + # This is a more complex example that can use + # ``arbitrary rST syntax`` in its exposition:: + # + # -> { "execute": "query-block" } + # <- { ... } + # + # Above, lengthy output has been omitted for brevity. + + +Examples of complete definition documentation:: + ## # @BlockStats: # @@ -1058,11 +1098,11 @@ For example:: # # Since: 0.14 # - # Example: + # .. qmp-example:: # # -> { "execute": "query-blockstats" } # <- { - # ... lots of output ... + # ... # } ## { 'command': 'query-blockstats', diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 6ad5663e54..adc85b5b39 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -553,7 +553,7 @@ class QAPISchemaParser: # Note: "sections" with two colons are left alone as # rST markup and not interpreted as a section heading. - # TODO: Remove this error sometime in 2025 or so + # TODO: Remove these errors sometime in 2025 or so # after we've fully transitioned to the new qapidoc # generator. @@ -567,6 +567,14 @@ class QAPISchemaParser: ) raise QAPIParseError(self, emsg) + if 'Example' in match.group(1): + emsg = ( + f"The '{match.group(1)}' section is no longer " + "supported. Please use the '.. qmp-example::' " + "directive, or other suitable markup instead." + ) + raise QAPIParseError(self, emsg) + doc.new_tagged_section(self.info, match.group(1)) text = line[match.end():] if text: diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index b565895858..f64bf38d85 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -172,12 +172,17 @@ # # Duis aute irure dolor # -# Example: +# .. qmp-example:: +# :title: Ideal fast-food burger situation # -# -> in -# <- out +# -> "in" +# <- "out" # -# Examples: +# Examples:: +# +# - Not a QMP code block +# - Merely a preformatted code block literal +# It isn't even an rST list. # - *verbatim* # - {braces} # @@ -199,11 +204,11 @@ # @cmd-feat1: a feature # @cmd-feat2: another feature # -# Example: +# .. qmp-example:: # -# -> in +# -> "this example" # -# <- out +# <- "has no title" ## { 'command': 'cmd-boxed', 'boxed': true, 'data': 'Object', diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index a8e9456f60..6d24f1127b 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -184,13 +184,21 @@ frobnicate - Ut enim ad minim veniam Duis aute irure dolor - section=Example - -> in - <- out - section=Examples + +.. qmp-example:: + :title: Ideal fast-food burger situation + + -> "in" + <- "out" + +Examples:: + + - Not a QMP code block + - Merely a preformatted code block literal + It isn't even an rST list. - *verbatim* - {braces} - section=None + Note:: Ceci n'est pas une note section=Since @@ -202,10 +210,12 @@ If you're bored enough to read this, go see a video of boxed cats a feature feature=cmd-feat2 another feature - section=Example - -> in + section=None +.. qmp-example:: - <- out + -> "this example" + + <- "has no title" doc symbol=EVT_BOXED body= diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 30d457e548..cb37db606a 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -217,17 +217,16 @@ Notes: Duis aute irure dolor +Example: Ideal fast-food burger situation: -Example -~~~~~~~ + -> "in" + <- "out" - -> in - <- out - - -Examples -~~~~~~~~ +Examples: + - Not a QMP code block + - Merely a preformatted code block literal + It isn't even an rST list. - *verbatim* - {braces} @@ -261,13 +260,11 @@ Features "cmd-feat2" another feature +Example:: -Example -~~~~~~~ + -> "this example" - -> in - - <- out + <- "has no title" "EVT_BOXED" (Event) From 2abc22e63943371128990e4dffcf8d1f7d44c10f Mon Sep 17 00:00:00 2001 From: Michael Tokarev <mjt@tls.msk.ru> Date: Sat, 29 Jun 2024 16:27:00 +0300 Subject: [PATCH 2169/2884] block/curl: rewrite http header parsing function Existing code was long, unclear and twisty. This also relaxes the rules a tiny bit: allows to have whitespace before header name and colon and makes the header value match to be case-insensitive. Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- block/curl.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/block/curl.c b/block/curl.c index ef5252d00b..0fdb6d39ac 100644 --- a/block/curl.c +++ b/block/curl.c @@ -210,37 +210,29 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) { BDRVCURLState *s = opaque; size_t realsize = size * nmemb; - const char *header = (char *)ptr; - const char *end = header + realsize; - const char *accept_ranges = "accept-ranges:"; - const char *bytes = "bytes"; + const char *p = ptr; + const char *end = p + realsize; + const char *t = "accept-ranges : bytes "; /* A lowercase template */ - if (realsize >= strlen(accept_ranges) - && g_ascii_strncasecmp(header, accept_ranges, - strlen(accept_ranges)) == 0) { - - char *p = strchr(header, ':') + 1; - - /* Skip whitespace between the header name and value. */ - while (p < end && *p && g_ascii_isspace(*p)) { - p++; - } - - if (end - p >= strlen(bytes) - && strncmp(p, bytes, strlen(bytes)) == 0) { - - /* Check that there is nothing but whitespace after the value. */ - p += strlen(bytes); - while (p < end && *p && g_ascii_isspace(*p)) { - p++; - } - - if (p == end || !*p) { - s->accept_range = true; + /* check if header matches the "t" template */ + for (;;) { + if (*t == ' ') { /* space in t matches any amount of isspace in p */ + if (p < end && g_ascii_isspace(*p)) { + ++p; + } else { + ++t; } + } else if (*t && p < end && *t == g_ascii_tolower(*p)) { + ++p, ++t; + } else { + break; } } + if (!*t && p == end) { /* if we managed to reach ends of both strings */ + s->accept_range = true; + } + return realsize; } From e215ba533cbe5274330a0374b22edb7af1a779f6 Mon Sep 17 00:00:00 2001 From: Zhihai Dong <dongzhihai@eswincomputing.com> Date: Wed, 3 Jul 2024 11:39:01 +0800 Subject: [PATCH 2170/2884] README.rst: add the missing punctuations Make the README more clearly. Signed-off-by: Zhihai Dong <dongzhihai@eswincomputing.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 21df79ef43..b120a1f69e 100644 --- a/README.rst +++ b/README.rst @@ -82,7 +82,7 @@ guidelines set out in the `style section the Developers Guide. Additional information on submitting patches can be found online via -the QEMU website +the QEMU website: * `<https://wiki.qemu.org/Contribute/SubmitAPatch>`_ * `<https://wiki.qemu.org/Contribute/TrivialPatches>`_ @@ -102,7 +102,7 @@ requires a working 'git send-email' setup, and by default doesn't automate everything, so you may want to go through the above steps manually for once. -For installation instructions, please go to +For installation instructions, please go to: * `<https://github.com/stefanha/git-publish>`_ @@ -159,7 +159,7 @@ Contact ======= The QEMU community can be contacted in a number of ways, with the two -main methods being email and IRC +main methods being email and IRC: * `<mailto:qemu-devel@nongnu.org>`_ * `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_ From 44fd9cf638db2d027502c32841c81c2b036232f5 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Thu, 4 Jul 2024 16:47:56 +0800 Subject: [PATCH 2171/2884] accel/kvm/kvm-all: Fix superfluous trailing semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- accel/kvm/kvm-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 2b4ab89679..64bf47a033 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3878,7 +3878,7 @@ static StatsList *add_kvmstat_entry(struct kvm_stats_desc *pdesc, /* Alloc and populate data list */ stats = g_new0(Stats, 1); stats->name = g_strdup(pdesc->name); - stats->value = g_new0(StatsValue, 1);; + stats->value = g_new0(StatsValue, 1); if ((pdesc->flags & KVM_STATS_UNIT_MASK) == KVM_STATS_UNIT_BOOLEAN) { stats->value->u.boolean = *stats_data; From eed52398f5bbc14cccc595fd39243e7db4c9a317 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Thu, 4 Jul 2024 16:47:57 +0800 Subject: [PATCH 2172/2884] hw/i386/x86: Fix superfluous trailing semicolon Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/i386/x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index a4aa8e0810..01fc5e6562 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -242,7 +242,7 @@ static void x86_machine_get_pit(Object *obj, Visitor *v, const char *name, static void x86_machine_set_pit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - X86MachineState *x86ms = X86_MACHINE(obj);; + X86MachineState *x86ms = X86_MACHINE(obj); visit_type_OnOffAuto(v, name, &x86ms->pit, errp); } From 083c4e71cfb043a190e866f79948ee636f10874b Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Thu, 4 Jul 2024 16:47:58 +0800 Subject: [PATCH 2173/2884] util/oslib-posix: Fix superfluous trailing semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- util/oslib-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/oslib-posix.c b/util/oslib-posix.c index e76441695b..b090fe0eed 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -263,7 +263,7 @@ int qemu_socketpair(int domain, int type, int protocol, int sv[2]) return ret; } #endif - ret = socketpair(domain, type, protocol, sv);; + ret = socketpair(domain, type, protocol, sv); if (ret == 0) { qemu_set_cloexec(sv[0]); qemu_set_cloexec(sv[1]); From 29ea19465b7ef690bd1c6d55e1a476a3cdf7971e Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Thu, 4 Jul 2024 16:47:59 +0800 Subject: [PATCH 2174/2884] target/hexagon/imported/mmvec: Fix superfluous trailing semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the superfluous trailing semicolon in target/hexagon/imported/mmvec/ ext.idef. Cc: Brian Cain <bcain@quicinc.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/hexagon/imported/mmvec/ext.idef | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 98daabfb07..03d31f6181 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -2855,7 +2855,7 @@ EXTINSN(V6_vscattermhw_add, "vscatter(Rt32,Mu2,Vvv32.w).h+=Vw32", ATTRIBS(A_EXT fVALIGN(RtV, element_size); fVFOREACH(32, i) { for(j = 0; j < 2; j++) { - EA = RtV + fVALIGN(VvvV.v[j].uw[i],ALIGNMENT);; + EA = RtV + fVALIGN(VvvV.v[j].uw[i],ALIGNMENT); fVLOG_VTCM_HALFWORD_INCREMENT_DV(EA,VvvV.v[j].uw[i],VwV,(2*i+j),i,j,ALIGNMENT,MuV); } } From cb8de74ac6df6d6f77221ebd808e12dbd2995a9a Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 8 Jul 2024 17:26:30 +0800 Subject: [PATCH 2175/2884] doc/net/l2tpv3: Update boolean fields' description to avoid short-form use The short-form boolean options has been deprecated since v6.0 (refer to docs/about/deprecated.rst). Update the description and example of boolean fields in l2tpv3 option to avoid deprecation warning. Cc: Jason Wang <jasowang@redhat.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- qemu-options.hx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index ad6521ef5e..edeaefe2c7 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3353,7 +3353,7 @@ SRST -device e1000,netdev=n1,mac=52:54:00:12:34:56 \\ -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 -``-netdev l2tpv3,id=id,src=srcaddr,dst=dstaddr[,srcport=srcport][,dstport=dstport],txsession=txsession[,rxsession=rxsession][,ipv6=on|off][,udp=on|off][,cookie64][,counter][,pincounter][,txcookie=txcookie][,rxcookie=rxcookie][,offset=offset]`` +``-netdev l2tpv3,id=id,src=srcaddr,dst=dstaddr[,srcport=srcport][,dstport=dstport],txsession=txsession[,rxsession=rxsession][,ipv6=on|off][,udp=on|off][,cookie64=on|off][,counter=on|off][,pincounter=on|off][,txcookie=txcookie][,rxcookie=rxcookie][,offset=offset]`` Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3931) is a popular protocol to transport Ethernet (and other Layer 2) data frames between two systems. It is present in routers, firewalls and @@ -3368,7 +3368,7 @@ SRST ``dst=dstaddr`` destination address (mandatory) - ``udp`` + ``udp=on`` select udp encapsulation (default is ip). ``srcport=srcport`` @@ -3377,7 +3377,7 @@ SRST ``dstport=dstport`` destination udp port. - ``ipv6`` + ``ipv6=on`` force v6, otherwise defaults to v4. ``rxcookie=rxcookie``; \ ``txcookie=txcookie`` @@ -3385,7 +3385,7 @@ SRST Their function is mostly to prevent misconfiguration. By default they are 32 bit. - ``cookie64`` + ``cookie64=on`` Set cookie size to 64 bit instead of the default 32 ``counter=off`` @@ -3419,7 +3419,7 @@ SRST # launch QEMU instance - if your network has reorder or is very lossy add ,pincounter |qemu_system| linux.img -device e1000,netdev=n1 \\ - -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter + -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp=on,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter=on ``-netdev vde,id=id[,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]`` Configure VDE backend to connect to PORT n of a vde switch running From 1b1238c7a55532d2ca6db5f4a78756124d762d5b Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Wed, 10 Jul 2024 14:03:30 +0200 Subject: [PATCH 2176/2884] tests/avocado: Remove the non-working virtio_check_params test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test has been marked as broken more than 4 years ago [*], and so far nobody ever cared to fix it. Thus let's simply remove it now ... if somebody ever needs it again, they can restore the file from an older version of QEMU. [*] https://lore.kernel.org/qemu-devel/4bbe9ff8-e1a8-917d-5a57-ce5185da19fa@redhat.com/ Signed-off-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> (mjt: add reference as suggested by philm) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- tests/avocado/virtio_check_params.py | 143 --------------------------- 1 file changed, 143 deletions(-) delete mode 100644 tests/avocado/virtio_check_params.py diff --git a/tests/avocado/virtio_check_params.py b/tests/avocado/virtio_check_params.py deleted file mode 100644 index 5fe370a179..0000000000 --- a/tests/avocado/virtio_check_params.py +++ /dev/null @@ -1,143 +0,0 @@ -# -# Test virtio-scsi and virtio-blk queue settings for all machine types -# -# Copyright (c) 2019 Virtuozzo International GmbH -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -import sys -import os -import re -import logging - -from qemu.machine import QEMUMachine -from avocado_qemu import QemuSystemTest -from avocado import skip - -#list of machine types and virtqueue properties to test -VIRTIO_SCSI_PROPS = {'seg_max_adjust': 'seg_max_adjust'} -VIRTIO_BLK_PROPS = {'seg_max_adjust': 'seg-max-adjust'} - -DEV_TYPES = {'virtio-scsi-pci': VIRTIO_SCSI_PROPS, - 'virtio-blk-pci': VIRTIO_BLK_PROPS} - -VM_DEV_PARAMS = {'virtio-scsi-pci': ['-device', 'virtio-scsi-pci,id=scsi0'], - 'virtio-blk-pci': ['-device', - 'virtio-blk-pci,id=scsi0,drive=drive0', - '-drive', - 'driver=null-co,id=drive0,if=none']} - - -class VirtioMaxSegSettingsCheck(QemuSystemTest): - @staticmethod - def make_pattern(props): - pattern_items = [r'{0} = \w+'.format(prop) for prop in props] - return '|'.join(pattern_items) - - def query_virtqueue(self, vm, dev_type_name): - query_ok = False - error = None - props = None - - output = vm.cmd('human-monitor-command', - command_line = 'info qtree') - props_list = DEV_TYPES[dev_type_name].values(); - pattern = self.make_pattern(props_list) - res = re.findall(pattern, output) - - if len(res) != len(props_list): - props_list = set(props_list) - res = set(res) - not_found = props_list.difference(res) - not_found = ', '.join(not_found) - error = '({0}): The following properties not found: {1}'\ - .format(dev_type_name, not_found) - else: - query_ok = True - props = dict() - for prop in res: - p = prop.split(' = ') - props[p[0]] = p[1] - return query_ok, props, error - - def check_mt(self, mt, dev_type_name): - mt['device'] = dev_type_name # Only for the debug() call. - logger = logging.getLogger('machine') - logger.debug(mt) - with QEMUMachine(self.qemu_bin) as vm: - vm.set_machine(mt["name"]) - vm.add_args('-nodefaults') - for s in VM_DEV_PARAMS[dev_type_name]: - vm.add_args(s) - try: - vm.launch() - query_ok, props, error = self.query_virtqueue(vm, dev_type_name) - except: - query_ok = False - error = sys.exc_info()[0] - - if not query_ok: - self.fail('machine type {0}: {1}'.format(mt['name'], error)) - - for prop_name, prop_val in props.items(): - expected_val = mt[prop_name] - self.assertEqual(expected_val, prop_val) - - @staticmethod - def seg_max_adjust_enabled(mt): - # machine types >= 5.0 should have seg_max_adjust = true - # others seg_max_adjust = false - mt = mt.split("-") - - # machine types with one line name and name like pc-x.x - if len(mt) <= 2: - return False - - # machine types like pc-<chip_name>-x.x[.x] - ver = mt[2] - ver = ver.split("."); - - # versions >= 5.0 goes with seg_max_adjust enabled - major = int(ver[0]) - - if major >= 5: - return True - return False - - @skip("break multi-arch CI") - def test_machine_types(self): - # collect all machine types except 'none', 'isapc', 'microvm' - with QEMUMachine(self.qemu_bin) as vm: - vm.launch() - machines = [m['name'] for m in vm.cmd('query-machines')] - vm.shutdown() - machines.remove('none') - machines.remove('isapc') - machines.remove('microvm') - - for dev_type in DEV_TYPES: - # create the list of machine types and their parameters. - mtypes = list() - for m in machines: - if self.seg_max_adjust_enabled(m): - enabled = 'true' - else: - enabled = 'false' - mtypes.append({'name': m, - DEV_TYPES[dev_type]['seg_max_adjust']: enabled}) - - # test each machine type for a device type - for mt in mtypes: - self.check_mt(mt, dev_type) From 1a48869c8f30de935b7b003b0d4ba30dd2185ed7 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:49 +0800 Subject: [PATCH 2177/2884] hw/i386/sgx: Get rid of qemu_open_old() For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). And considering the SGX enablement description is useful, convert it into a error message hint. Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Richard Henderson <richard.henderson@linaro.org> Cc: Eduardo Habkost <eduardo@habkost.net> Cc: "Michael S. Tsirkin" <mst@redhat.com> Cc: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/i386/sgx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index de76397bcf..a14a84bc6f 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -157,10 +157,12 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) { SGXInfo *info = NULL; uint32_t eax, ebx, ecx, edx; + Error *local_err = NULL; - int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); + int fd = qemu_open("/dev/sgx_vepc", O_RDWR, &local_err); if (fd < 0) { - error_setg(errp, "SGX is not enabled in KVM"); + error_append_hint(&local_err, "SGX is not enabled in KVM"); + error_propagate(errp, local_err); return NULL; } From a3c45ef33e5c0205c270b716593bcc5f6718d501 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:50 +0800 Subject: [PATCH 2178/2884] hw/usb/host-libusb: Get rid of qemu_open_old() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/usb/host-libusb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 80122b4125..691bc881fb 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1212,9 +1212,8 @@ static void usb_host_realize(USBDevice *udev, Error **errp) if (s->hostdevice) { int fd; s->needs_autoscan = false; - fd = qemu_open_old(s->hostdevice, O_RDWR); + fd = qemu_open(s->hostdevice, O_RDWR, errp); if (fd < 0) { - error_setg_errno(errp, errno, "failed to open %s", s->hostdevice); return; } rc = usb_host_open(s, NULL, fd); From f80d59f3771d464354489cfa66d141d8acba174f Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:51 +0800 Subject: [PATCH 2179/2884] hw/usb/u2f-passthru: Get rid of qemu_open_old() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/usb/u2f-passthru.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index b7025d303d..c4a783d128 100644 --- a/hw/usb/u2f-passthru.c +++ b/hw/usb/u2f-passthru.c @@ -482,10 +482,8 @@ static void u2f_passthru_realize(U2FKeyState *base, Error **errp) return; #endif } else { - fd = qemu_open_old(key->hidraw, O_RDWR); + fd = qemu_open(key->hidraw, O_RDWR, errp); if (fd < 0) { - error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU, - key->hidraw); return; } From eb92e6e3e7c439138e91a24f57a12f73ad7612c1 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:52 +0800 Subject: [PATCH 2180/2884] hw/vfio/container: Get rid of qemu_open_old() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). Cc: Alex Williamson <alex.williamson@redhat.com> Cc: "Cédric Le Goater" <clg@redhat.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/vfio/container.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 425db1a14c..38a9df3496 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -600,9 +600,8 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, } } - fd = qemu_open_old("/dev/vfio/vfio", O_RDWR); + fd = qemu_open("/dev/vfio/vfio", O_RDWR, errp); if (fd < 0) { - error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio"); goto put_space_exit; } @@ -743,9 +742,8 @@ static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) group = g_malloc0(sizeof(*group)); snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); - group->fd = qemu_open_old(path, O_RDWR); + group->fd = qemu_open(path, O_RDWR, errp); if (group->fd < 0) { - error_setg_errno(errp, errno, "failed to open %s", path); goto free_group_exit; } From 514d3035b97b686ed9af321605e9eba2a82b20c9 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:53 +0800 Subject: [PATCH 2181/2884] backends/hostmem-epc: Get rid of qemu_open_old() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). Cc: David Hildenbrand <david@redhat.com> Cc: Igor Mammedov <imammedo@redhat.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- backends/hostmem-epc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index f58fcf00a1..6c024d6217 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -29,10 +29,8 @@ sgx_epc_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return false; } - fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); + fd = qemu_open("/dev/sgx_vepc", O_RDWR, errp); if (fd < 0) { - error_setg_errno(errp, errno, - "failed to open /dev/sgx_vepc to alloc SGX EPC"); return false; } From 47cd2f1a360196beb9769a2636ccdd3ae9d0370f Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:54 +0800 Subject: [PATCH 2182/2884] backends/iommufd: Get rid of qemu_open_old() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). Cc: Yi Liu <yi.l.liu@intel.com> Cc: Eric Auger <eric.auger@redhat.com> Cc: Zhenzhong Duan <zhenzhong.duan@intel.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Yi Liu <yi.l.liu@intel.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- backends/iommufd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index 84fefbc9ee..cabd1b5002 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -77,9 +77,8 @@ bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) int fd; if (be->owned && !be->users) { - fd = qemu_open_old("/dev/iommu", O_RDWR); + fd = qemu_open("/dev/iommu", O_RDWR, errp); if (fd < 0) { - error_setg_errno(errp, errno, "/dev/iommu opening failed"); return false; } be->fd = fd; From 18f3a07b206b228b041c260d48df393111c8299b Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Mon, 15 Jul 2024 16:21:55 +0800 Subject: [PATCH 2183/2884] backends/rng-random: Get rid of qemu_open_old() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For qemu_open_old(), osdep.h said: > Don't introduce new usage of this function, prefer the following > qemu_open/qemu_create that take an "Error **errp". So replace qemu_open_old() with qemu_open(). And considering rng_random_opened() will lose its obvious error handling case after removing error_setg_file_open(), add comment to remind here. Cc: Laurent Vivier <lvivier@redhat.com> Cc: Amit Shah <amit@kernel.org> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> (mjt: drop superfluous commit as suggested by philmd) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- backends/rng-random.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backends/rng-random.c b/backends/rng-random.c index 80eb5be138..489c0917f0 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -75,10 +75,7 @@ static void rng_random_opened(RngBackend *b, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filename", "a valid filename"); } else { - s->fd = qemu_open_old(s->filename, O_RDONLY | O_NONBLOCK); - if (s->fd == -1) { - error_setg_file_open(errp, errno, s->filename); - } + s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK, errp); } } From 66a8de9889ceb929e2abe7fb0e424f45210d9dda Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Fri, 5 Jul 2024 13:49:03 +0800 Subject: [PATCH 2184/2884] meson: Update meson-buildoptions.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update meson-buildoptions.sh to stay in sync with meson_options.txt. Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- scripts/meson-buildoptions.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index cfadb5ea86..c97079a38c 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -83,7 +83,7 @@ meson_options_help() { printf "%s\n" ' (can be empty) [qemu]' printf "%s\n" ' --with-trace-file=VALUE Trace file prefix for simple backend [trace]' printf "%s\n" ' --x86-version=CHOICE tweak required x86_64 architecture version beyond' - printf "%s\n" ' compiler default [1] (choices: 0/1/2/3)' + printf "%s\n" ' compiler default [1] (choices: 0/1/2/3/4)' printf "%s\n" '' printf "%s\n" 'Optional features, enabled with --enable-FEATURE and' printf "%s\n" 'disabled with --disable-FEATURE, default is enabled if available' @@ -166,6 +166,7 @@ meson_options_help() { printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)' + printf "%s\n" ' qpl Query Processing Library support' printf "%s\n" ' rbd Ceph block device driver' printf "%s\n" ' rdma Enable RDMA-based migration' printf "%s\n" ' replication replication support' @@ -187,6 +188,7 @@ meson_options_help() { printf "%s\n" ' tools build support utilities that come with QEMU' printf "%s\n" ' tpm TPM support' printf "%s\n" ' u2f U2F emulation support' + printf "%s\n" ' uadk UADK Library support' printf "%s\n" ' usb-redir libusbredir support' printf "%s\n" ' vde vde network backend support' printf "%s\n" ' vdi vdi image format support' @@ -221,8 +223,6 @@ meson_options_help() { printf "%s\n" ' Xen PCI passthrough support' printf "%s\n" ' xkbcommon xkbcommon support' printf "%s\n" ' zstd zstd compression support' - printf "%s\n" ' qpl Query Processing Library support' - printf "%s\n" ' uadk UADK Library support' } _meson_option_parse() { case $1 in @@ -440,6 +440,8 @@ _meson_option_parse() { --disable-qga-vss) printf "%s" -Dqga_vss=disabled ;; --enable-qom-cast-debug) printf "%s" -Dqom_cast_debug=true ;; --disable-qom-cast-debug) printf "%s" -Dqom_cast_debug=false ;; + --enable-qpl) printf "%s" -Dqpl=enabled ;; + --disable-qpl) printf "%s" -Dqpl=disabled ;; --enable-rbd) printf "%s" -Drbd=enabled ;; --disable-rbd) printf "%s" -Drbd=disabled ;; --enable-rdma) printf "%s" -Drdma=enabled ;; @@ -501,6 +503,8 @@ _meson_option_parse() { --disable-tsan) printf "%s" -Dtsan=false ;; --enable-u2f) printf "%s" -Du2f=enabled ;; --disable-u2f) printf "%s" -Du2f=disabled ;; + --enable-uadk) printf "%s" -Duadk=enabled ;; + --disable-uadk) printf "%s" -Duadk=disabled ;; --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;; --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;; --enable-vde) printf "%s" -Dvde=enabled ;; @@ -560,10 +564,6 @@ _meson_option_parse() { --disable-xkbcommon) printf "%s" -Dxkbcommon=disabled ;; --enable-zstd) printf "%s" -Dzstd=enabled ;; --disable-zstd) printf "%s" -Dzstd=disabled ;; - --enable-qpl) printf "%s" -Dqpl=enabled ;; - --disable-qpl) printf "%s" -Dqpl=disabled ;; - --enable-uadk) printf "%s" -Duadk=enabled ;; - --disable-uadk) printf "%s" -Duadk=disabled ;; *) return 1 ;; esac } From 6eab278d389bc3569135773265e0d481d89ef82e Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:42 +0800 Subject: [PATCH 2185/2884] target/riscv: Add zimop extension Zimop extension defines an encoding space for 40 MOPs.The Zimop extension defines 32 MOP instructions named MOP.R.n, where n is an integer between 0 and 31, inclusive. The Zimop extension additionally defines 8 MOP instructions named MOP.RR.n, where n is an integer between 0 and 7. These 40 MOPs initially are defined to simply write zero to x[rd], but are designed to be redefined by later extensions to perform some other action. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Deepak Gupta <debug@rivosinc.com> Message-ID: <20240709113652.1239-2-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/insn32.decode | 11 ++++++ target/riscv/insn_trans/trans_rvzimop.c.inc | 37 +++++++++++++++++++++ target/riscv/translate.c | 1 + 5 files changed, 52 insertions(+) create mode 100644 target/riscv/insn_trans/trans_rvzimop.c.inc diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index c53b0d5b40..ac503826ce 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -113,6 +113,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zihintntl, PRIV_VERSION_1_10_0, ext_zihintntl), ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), ISA_EXT_DATA_ENTRY(zihpm, PRIV_VERSION_1_12_0, ext_zihpm), + ISA_EXT_DATA_ENTRY(zimop, PRIV_VERSION_1_13_0, ext_zimop), ISA_EXT_DATA_ENTRY(zmmul, PRIV_VERSION_1_12_0, ext_zmmul), ISA_EXT_DATA_ENTRY(za64rs, PRIV_VERSION_1_12_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), @@ -1471,6 +1472,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true), MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true), + MULTI_EXT_CFG_BOOL("zimop", ext_zimop, false), MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), MULTI_EXT_CFG_BOOL("zaamo", ext_zaamo, false), MULTI_EXT_CFG_BOOL("zalrsc", ext_zalrsc, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index fb7eebde52..9f53512053 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -71,6 +71,7 @@ struct RISCVCPUConfig { bool ext_zihintntl; bool ext_zihintpause; bool ext_zihpm; + bool ext_zimop; bool ext_ztso; bool ext_smstateen; bool ext_sstc; diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index f22df04cfd..60da673153 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -38,6 +38,8 @@ %imm_bs 30:2 !function=ex_shift_3 %imm_rnum 20:4 %imm_z6 26:1 15:5 +%imm_mop5 30:1 26:2 20:2 +%imm_mop3 30:1 26:2 # Argument sets: &empty @@ -56,6 +58,8 @@ &r2nfvm vm rd rs1 nf &rnfvm vm rd rs1 rs2 nf &k_aes shamt rs2 rs1 rd +&mop5 imm rd rs1 +&mop3 imm rd rs1 rs2 # Formats 32: @r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd @@ -98,6 +102,9 @@ @k_aes .. ..... ..... ..... ... ..... ....... &k_aes shamt=%imm_bs %rs2 %rs1 %rd @i_aes .. ..... ..... ..... ... ..... ....... &i imm=%imm_rnum %rs1 %rd +@mop5 . . .. .. .... .. ..... ... ..... ....... &mop5 imm=%imm_mop5 %rd %rs1 +@mop3 . . .. .. . ..... ..... ... ..... ....... &mop3 imm=%imm_mop3 %rd %rs1 %rs2 + # Formats 64: @sh5 ....... ..... ..... ... ..... ....... &shift shamt=%sh5 %rs1 %rd @@ -1010,3 +1017,7 @@ amocas_w 00101 . . ..... ..... 010 ..... 0101111 @atom_st amocas_d 00101 . . ..... ..... 011 ..... 0101111 @atom_st # *** RV64 Zacas Standard Extension *** amocas_q 00101 . . ..... ..... 100 ..... 0101111 @atom_st + +# *** Zimop may-be-operation extension *** +mop_r_n 1 . 00 .. 0111 .. ..... 100 ..... 1110011 @mop5 +mop_rr_n 1 . 00 .. 1 ..... ..... 100 ..... 1110011 @mop3 diff --git a/target/riscv/insn_trans/trans_rvzimop.c.inc b/target/riscv/insn_trans/trans_rvzimop.c.inc new file mode 100644 index 0000000000..165aacd2b6 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzimop.c.inc @@ -0,0 +1,37 @@ +/* + * RISC-V translation routines for May-Be-Operation(zimop). + * + * Copyright (c) 2024 Alibaba Group. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define REQUIRE_ZIMOP(ctx) do { \ + if (!ctx->cfg_ptr->ext_zimop) { \ + return false; \ + } \ +} while (0) + +static bool trans_mop_r_n(DisasContext *ctx, arg_mop_r_n *a) +{ + REQUIRE_ZIMOP(ctx); + gen_set_gpr(ctx, a->rd, ctx->zero); + return true; +} + +static bool trans_mop_rr_n(DisasContext *ctx, arg_mop_rr_n *a) +{ + REQUIRE_ZIMOP(ctx); + gen_set_gpr(ctx, a->rd, ctx->zero); + return true; +} diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 0569224e53..379b68289f 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1099,6 +1099,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvzacas.c.inc" #include "insn_trans/trans_rvzawrs.c.inc" #include "insn_trans/trans_rvzicbo.c.inc" +#include "insn_trans/trans_rvzimop.c.inc" #include "insn_trans/trans_rvzfa.c.inc" #include "insn_trans/trans_rvzfh.c.inc" #include "insn_trans/trans_rvk.c.inc" From d98883d127d0029532601e9ca6832dfcb3ee2ca0 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:43 +0800 Subject: [PATCH 2186/2884] disas/riscv: Support zimop disassemble Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Deepak Gupta <debug@rivosinc.com> Message-ID: <20240709113652.1239-3-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- disas/riscv.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index 90d6b26de9..0b82ab2322 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -906,6 +906,46 @@ typedef enum { rv_op_amocas_w = 875, rv_op_amocas_d = 876, rv_op_amocas_q = 877, + rv_mop_r_0 = 878, + rv_mop_r_1 = 879, + rv_mop_r_2 = 880, + rv_mop_r_3 = 881, + rv_mop_r_4 = 882, + rv_mop_r_5 = 883, + rv_mop_r_6 = 884, + rv_mop_r_7 = 885, + rv_mop_r_8 = 886, + rv_mop_r_9 = 887, + rv_mop_r_10 = 888, + rv_mop_r_11 = 889, + rv_mop_r_12 = 890, + rv_mop_r_13 = 891, + rv_mop_r_14 = 892, + rv_mop_r_15 = 893, + rv_mop_r_16 = 894, + rv_mop_r_17 = 895, + rv_mop_r_18 = 896, + rv_mop_r_19 = 897, + rv_mop_r_20 = 898, + rv_mop_r_21 = 899, + rv_mop_r_22 = 900, + rv_mop_r_23 = 901, + rv_mop_r_24 = 902, + rv_mop_r_25 = 903, + rv_mop_r_26 = 904, + rv_mop_r_27 = 905, + rv_mop_r_28 = 906, + rv_mop_r_29 = 907, + rv_mop_r_30 = 908, + rv_mop_r_31 = 909, + rv_mop_rr_0 = 910, + rv_mop_rr_1 = 911, + rv_mop_rr_2 = 912, + rv_mop_rr_3 = 913, + rv_mop_rr_4 = 914, + rv_mop_rr_5 = 915, + rv_mop_rr_6 = 916, + rv_mop_rr_7 = 917, } rv_op; /* register names */ @@ -2096,6 +2136,46 @@ const rv_opcode_data rvi_opcode_data[] = { { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "mop.r.0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.2", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.3", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.4", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.5", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.6", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.7", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.9", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.10", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.11", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.12", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.13", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.14", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.15", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.16", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.17", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.18", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.19", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.20", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.21", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.22", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.23", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.24", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.25", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.26", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.27", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.28", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.29", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.30", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.31", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -3855,6 +3935,24 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: op = rv_op_csrrw; break; case 2: op = rv_op_csrrs; break; case 3: op = rv_op_csrrc; break; + case 4: + if (dec->cfg->ext_zimop) { + int imm_mop5, imm_mop3; + if ((extract32(inst, 22, 10) & 0b1011001111) + == 0b1000000111) { + imm_mop5 = deposit32(deposit32(extract32(inst, 20, 2), + 2, 2, + extract32(inst, 26, 2)), + 4, 1, extract32(inst, 30, 1)); + op = rv_mop_r_0 + imm_mop5; + } else if ((extract32(inst, 25, 7) & 0b1011001) + == 0b1000001) { + imm_mop3 = deposit32(extract32(inst, 26, 2), + 2, 1, extract32(inst, 30, 1)); + op = rv_mop_rr_0 + imm_mop3; + } + } + break; case 5: op = rv_op_csrrwi; break; case 6: op = rv_op_csrrsi; break; case 7: op = rv_op_csrrci; break; From 197e4d29889453760c74763662e3812d0ec7a645 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:44 +0800 Subject: [PATCH 2187/2884] target/riscv: Add zcmop extension Zcmop defines eight 16-bit MOP instructions named C.MOP.n, where n is an odd integer between 1 and 15, inclusive. C.MOP.n is encoded in the reserved encoding space corresponding to C.LUI xn, 0. Unlike the MOPs defined in the Zimop extension, the C.MOP.n instructions are defined to not write any register. In current implementation, C.MOP.n only has an check function, without any other more behavior. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Deepak Gupta <debug@rivosinc.com> Message-ID: <20240709113652.1239-4-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/insn16.decode | 1 + target/riscv/insn_trans/trans_rvzcmop.c.inc | 29 +++++++++++++++++++++ target/riscv/tcg/tcg-cpu.c | 5 ++++ target/riscv/translate.c | 1 + 6 files changed, 39 insertions(+) create mode 100644 target/riscv/insn_trans/trans_rvzcmop.c.inc diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ac503826ce..f4f8287a6d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -131,6 +131,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zcf, PRIV_VERSION_1_12_0, ext_zcf), ISA_EXT_DATA_ENTRY(zcd, PRIV_VERSION_1_12_0, ext_zcd), ISA_EXT_DATA_ENTRY(zce, PRIV_VERSION_1_12_0, ext_zce), + ISA_EXT_DATA_ENTRY(zcmop, PRIV_VERSION_1_13_0, ext_zcmop), ISA_EXT_DATA_ENTRY(zcmp, PRIV_VERSION_1_12_0, ext_zcmp), ISA_EXT_DATA_ENTRY(zcmt, PRIV_VERSION_1_12_0, ext_zcmt), ISA_EXT_DATA_ENTRY(zba, PRIV_VERSION_1_12_0, ext_zba), @@ -1473,6 +1474,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true), MULTI_EXT_CFG_BOOL("zimop", ext_zimop, false), + MULTI_EXT_CFG_BOOL("zcmop", ext_zcmop, false), MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), MULTI_EXT_CFG_BOOL("zaamo", ext_zaamo, false), MULTI_EXT_CFG_BOOL("zalrsc", ext_zalrsc, false), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 9f53512053..d85e54b475 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -72,6 +72,7 @@ struct RISCVCPUConfig { bool ext_zihintpause; bool ext_zihpm; bool ext_zimop; + bool ext_zcmop; bool ext_ztso; bool ext_smstateen; bool ext_sstc; diff --git a/target/riscv/insn16.decode b/target/riscv/insn16.decode index b96c534e73..3953bcf82d 100644 --- a/target/riscv/insn16.decode +++ b/target/riscv/insn16.decode @@ -140,6 +140,7 @@ sw 110 ... ... .. ... 00 @cs_w addi 000 . ..... ..... 01 @ci addi 010 . ..... ..... 01 @c_li { + c_mop_n 011 0 0 n:3 1 00000 01 illegal 011 0 ----- 00000 01 # c.addi16sp and c.lui, RES nzimm=0 addi 011 . 00010 ..... 01 @c_addi16sp lui 011 . ..... ..... 01 @c_lui diff --git a/target/riscv/insn_trans/trans_rvzcmop.c.inc b/target/riscv/insn_trans/trans_rvzcmop.c.inc new file mode 100644 index 0000000000..7205586508 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzcmop.c.inc @@ -0,0 +1,29 @@ +/* + * RISC-V translation routines for compressed May-Be-Operation(zcmop). + * + * Copyright (c) 2024 Alibaba Group. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define REQUIRE_ZCMOP(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcmop) { \ + return false; \ + } \ +} while (0) + +static bool trans_c_mop_n(DisasContext *ctx, arg_c_mop_n *a) +{ + REQUIRE_ZCMOP(ctx); + return true; +} diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index ecf366d6c7..b8814ab753 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -549,6 +549,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) } } + if (cpu->cfg.ext_zcmop && !cpu->cfg.ext_zca) { + error_setg(errp, "Zcmop extensions require Zca"); + return; + } + if (mcc->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) { error_setg(errp, "Zcf extension is only relevant to RV32"); return; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 379b68289f..8a546f4ece 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1114,6 +1114,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) /* Include the auto-generated decoder for 16 bit insn */ #include "decode-insn16.c.inc" #include "insn_trans/trans_rvzce.c.inc" +#include "insn_trans/trans_rvzcmop.c.inc" /* Include decoders for factored-out extensions */ #include "decode-XVentanaCondOps.c.inc" From 67e98ebad063e8b028466a754578dd8386aaa5f6 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:45 +0800 Subject: [PATCH 2188/2884] disas/riscv: Support zcmop disassemble Although in QEMU disassemble, we usually lift compressed instruction to an normal format when display the instruction name. For C.MOP.n, it is more reasonable to directly display its compressed name, because its behavior can be redefined by later extension. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Deepak Gupta <debug@rivosinc.com> Message-ID: <20240709113652.1239-5-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- disas/riscv.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index 0b82ab2322..d29cb1ff7d 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -946,6 +946,14 @@ typedef enum { rv_mop_rr_5 = 915, rv_mop_rr_6 = 916, rv_mop_rr_7 = 917, + rv_c_mop_1 = 918, + rv_c_mop_3 = 919, + rv_c_mop_5 = 920, + rv_c_mop_7 = 921, + rv_c_mop_9 = 922, + rv_c_mop_11 = 923, + rv_c_mop_13 = 924, + rv_c_mop_15 = 925, } rv_op; /* register names */ @@ -2176,6 +2184,14 @@ const rv_opcode_data rvi_opcode_data[] = { { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "c.mop.1", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.3", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.5", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.7", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.9", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.11", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.13", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.15", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -2532,6 +2548,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 2: op = rv_op_c_li; break; case 3: + if (dec->cfg->ext_zcmop) { + if ((((inst >> 2) & 0b111111) == 0b100000) && + (((inst >> 11) & 0b11) == 0b0)) { + op = rv_c_mop_1 + ((inst >> 8) & 0b111); + break; + } + } switch ((inst >> 7) & 0b11111) { case 2: op = rv_op_c_addi16sp; break; default: op = rv_op_c_lui; break; From a60ce58fd971bdcbec6ba96ce989fd399ca1f2d7 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:46 +0800 Subject: [PATCH 2189/2884] target/riscv: Support Zama16b extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zama16b is the property that misaligned load/stores/atomics within a naturally aligned 16-byte region are atomic. According to the specification, Zama16b applies only to AMOs, loads and stores defined in the base ISAs, and loads and stores of no more than XLEN bits defined in the F, D, and Q extensions. Thus it should not apply to zacas or RVC instructions. For an instruction in that set, if all accessed bytes lie within 16B granule, the instruction will not raise an exception for reasons of address alignment, and the instruction will give rise to only one memory operation for the purposes of RVWMO—i.e., it will execute atomically. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-6-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_cfg.h | 1 + target/riscv/insn_trans/trans_rva.c.inc | 42 ++++++++++++++----------- target/riscv/insn_trans/trans_rvd.c.inc | 14 +++++++-- target/riscv/insn_trans/trans_rvf.c.inc | 14 +++++++-- target/riscv/insn_trans/trans_rvi.c.inc | 6 ++++ 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f4f8287a6d..de9c06904f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -118,6 +118,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(za64rs, PRIV_VERSION_1_12_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), ISA_EXT_DATA_ENTRY(zacas, PRIV_VERSION_1_12_0, ext_zacas), + ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), ISA_EXT_DATA_ENTRY(zalrsc, PRIV_VERSION_1_12_0, ext_zalrsc), ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa), @@ -1476,6 +1477,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zimop", ext_zimop, false), MULTI_EXT_CFG_BOOL("zcmop", ext_zcmop, false), MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), + MULTI_EXT_CFG_BOOL("zama16b", ext_zama16b, false), MULTI_EXT_CFG_BOOL("zaamo", ext_zaamo, false), MULTI_EXT_CFG_BOOL("zalrsc", ext_zalrsc, false), MULTI_EXT_CFG_BOOL("zawrs", ext_zawrs, true), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index d85e54b475..ddbfae37e5 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -83,6 +83,7 @@ struct RISCVCPUConfig { bool ext_zdinx; bool ext_zaamo; bool ext_zacas; + bool ext_zama16b; bool ext_zalrsc; bool ext_zawrs; bool ext_zfa; diff --git a/target/riscv/insn_trans/trans_rva.c.inc b/target/riscv/insn_trans/trans_rva.c.inc index 4a9e4591d1..eb080baddd 100644 --- a/target/riscv/insn_trans/trans_rva.c.inc +++ b/target/riscv/insn_trans/trans_rva.c.inc @@ -103,6 +103,12 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, TCGv dest = dest_gpr(ctx, a->rd); TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + if (ctx->cfg_ptr->ext_zama16b) { + mop |= MO_ATOM_WITHIN16; + } else { + mop |= MO_ALIGN; + } + decode_save_opc(ctx); src1 = get_address(ctx, a->rs1, 0); func(dest, src1, src2, ctx->mem_idx, mop); @@ -126,55 +132,55 @@ static bool trans_sc_w(DisasContext *ctx, arg_sc_w *a) static bool trans_amoswap_w(DisasContext *ctx, arg_amoswap_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_TESL); } static bool trans_amoadd_w(DisasContext *ctx, arg_amoadd_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_TESL); } static bool trans_amoxor_w(DisasContext *ctx, arg_amoxor_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_TESL); } static bool trans_amoand_w(DisasContext *ctx, arg_amoand_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_TESL); } static bool trans_amoor_w(DisasContext *ctx, arg_amoor_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_TESL); } static bool trans_amomin_w(DisasContext *ctx, arg_amomin_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_TESL); } static bool trans_amomax_w(DisasContext *ctx, arg_amomax_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_TESL); } static bool trans_amominu_w(DisasContext *ctx, arg_amominu_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_TESL); } static bool trans_amomaxu_w(DisasContext *ctx, arg_amomaxu_w *a) { REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TESL)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_TESL); } static bool trans_lr_d(DisasContext *ctx, arg_lr_d *a) @@ -195,61 +201,61 @@ static bool trans_amoswap_d(DisasContext *ctx, arg_amoswap_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_TEUQ); } static bool trans_amoadd_d(DisasContext *ctx, arg_amoadd_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_TEUQ); } static bool trans_amoxor_d(DisasContext *ctx, arg_amoxor_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_TEUQ); } static bool trans_amoand_d(DisasContext *ctx, arg_amoand_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_TEUQ); } static bool trans_amoor_d(DisasContext *ctx, arg_amoor_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_TEUQ); } static bool trans_amomin_d(DisasContext *ctx, arg_amomin_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_TEUQ); } static bool trans_amomax_d(DisasContext *ctx, arg_amomax_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_TEUQ); } static bool trans_amominu_d(DisasContext *ctx, arg_amominu_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_TEUQ); } static bool trans_amomaxu_d(DisasContext *ctx, arg_amomaxu_d *a) { REQUIRE_64BIT(ctx); REQUIRE_A_OR_ZAAMO(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TEUQ)); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_TEUQ); } diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index d9ce9e407f..1f5fac65a2 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -42,13 +42,18 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) { TCGv addr; + MemOp memop = MO_TEUQ; REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); + if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + memop |= MO_ATOM_WITHIN16; + } + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); - tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, memop); mark_fs_dirty(ctx); return true; @@ -57,13 +62,18 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) static bool trans_fsd(DisasContext *ctx, arg_fsd *a) { TCGv addr; + MemOp memop = MO_TEUQ; REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); + if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + memop |= MO_ATOM_WITHIN16; + } + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); - tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, memop); return true; } diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index 97a368970b..f771aa1939 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -43,14 +43,19 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) { TCGv_i64 dest; TCGv addr; + MemOp memop = MO_TEUL; REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); + if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + memop |= MO_ATOM_WITHIN16; + } + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); dest = cpu_fpr[a->rd]; - tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, memop); gen_nanbox_s(dest, dest); mark_fs_dirty(ctx); @@ -60,13 +65,18 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) static bool trans_fsw(DisasContext *ctx, arg_fsw *a) { TCGv addr; + MemOp memop = MO_TEUL; REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); + if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + memop |= MO_ATOM_WITHIN16; + } + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); - tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, memop); return true; } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index ad40d3e87f..98e3806d5e 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -268,6 +268,9 @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) { bool out; + if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + memop |= MO_ATOM_WITHIN16; + } decode_save_opc(ctx); if (get_xl(ctx) == MXL_RV128) { out = gen_load_i128(ctx, a, memop); @@ -366,6 +369,9 @@ static bool gen_store_i128(DisasContext *ctx, arg_sb *a, MemOp memop) static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) { + if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + memop |= MO_ATOM_WITHIN16; + } decode_save_opc(ctx); if (get_xl(ctx) == MXL_RV128) { return gen_store_i128(ctx, a, memop); From 24da9cbacacadaf95fbddc11da81ff68d3b1e795 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:47 +0800 Subject: [PATCH 2190/2884] target/riscv: Move gen_amo before implement Zabha Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-7-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rva.c.inc | 21 --------------------- target/riscv/translate.c | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/target/riscv/insn_trans/trans_rva.c.inc b/target/riscv/insn_trans/trans_rva.c.inc index eb080baddd..39bbf60f3c 100644 --- a/target/riscv/insn_trans/trans_rva.c.inc +++ b/target/riscv/insn_trans/trans_rva.c.inc @@ -96,27 +96,6 @@ static bool gen_sc(DisasContext *ctx, arg_atomic *a, MemOp mop) return true; } -static bool gen_amo(DisasContext *ctx, arg_atomic *a, - void(*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), - MemOp mop) -{ - TCGv dest = dest_gpr(ctx, a->rd); - TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); - - if (ctx->cfg_ptr->ext_zama16b) { - mop |= MO_ATOM_WITHIN16; - } else { - mop |= MO_ALIGN; - } - - decode_save_opc(ctx); - src1 = get_address(ctx, a->rs1, 0); - func(dest, src1, src2, ctx->mem_idx, mop); - - gen_set_gpr(ctx, a->rd, dest); - return true; -} - static bool trans_lr_w(DisasContext *ctx, arg_lr_w *a) { REQUIRE_A_OR_ZALRSC(ctx); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 8a546f4ece..133550d6e2 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1077,6 +1077,27 @@ static bool gen_unary_per_ol(DisasContext *ctx, arg_r2 *a, DisasExtend ext, return gen_unary(ctx, a, ext, f_tl); } +static bool gen_amo(DisasContext *ctx, arg_atomic *a, + void(*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), + MemOp mop) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + if (ctx->cfg_ptr->ext_zama16b) { + mop |= MO_ATOM_WITHIN16; + } else { + mop |= MO_ALIGN; + } + + decode_save_opc(ctx); + src1 = get_address(ctx, a->rs1, 0); + func(dest, src1, src2, ctx->mem_idx, mop); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) { DisasContext *ctx = container_of(dcbase, DisasContext, base); From be4a8db7f304347395b081ae5848bad2f507d0c4 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:48 +0800 Subject: [PATCH 2191/2884] target/riscv: Add AMO instructions for Zabha Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-8-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_cfg.h | 1 + target/riscv/insn32.decode | 20 +++ target/riscv/insn_trans/trans_rvzabha.c.inc | 131 ++++++++++++++++++++ target/riscv/translate.c | 4 +- 4 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 target/riscv/insn_trans/trans_rvzabha.c.inc diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index ddbfae37e5..120905a254 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -84,6 +84,7 @@ struct RISCVCPUConfig { bool ext_zaamo; bool ext_zacas; bool ext_zama16b; + bool ext_zabha; bool ext_zalrsc; bool ext_zawrs; bool ext_zfa; diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 60da673153..3bad6372f2 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -1021,3 +1021,23 @@ amocas_q 00101 . . ..... ..... 100 ..... 0101111 @atom_st # *** Zimop may-be-operation extension *** mop_r_n 1 . 00 .. 0111 .. ..... 100 ..... 1110011 @mop5 mop_rr_n 1 . 00 .. 1 ..... ..... 100 ..... 1110011 @mop3 + +# *** Zabhb Standard Extension *** +amoswap_b 00001 . . ..... ..... 000 ..... 0101111 @atom_st +amoadd_b 00000 . . ..... ..... 000 ..... 0101111 @atom_st +amoxor_b 00100 . . ..... ..... 000 ..... 0101111 @atom_st +amoand_b 01100 . . ..... ..... 000 ..... 0101111 @atom_st +amoor_b 01000 . . ..... ..... 000 ..... 0101111 @atom_st +amomin_b 10000 . . ..... ..... 000 ..... 0101111 @atom_st +amomax_b 10100 . . ..... ..... 000 ..... 0101111 @atom_st +amominu_b 11000 . . ..... ..... 000 ..... 0101111 @atom_st +amomaxu_b 11100 . . ..... ..... 000 ..... 0101111 @atom_st +amoswap_h 00001 . . ..... ..... 001 ..... 0101111 @atom_st +amoadd_h 00000 . . ..... ..... 001 ..... 0101111 @atom_st +amoxor_h 00100 . . ..... ..... 001 ..... 0101111 @atom_st +amoand_h 01100 . . ..... ..... 001 ..... 0101111 @atom_st +amoor_h 01000 . . ..... ..... 001 ..... 0101111 @atom_st +amomin_h 10000 . . ..... ..... 001 ..... 0101111 @atom_st +amomax_h 10100 . . ..... ..... 001 ..... 0101111 @atom_st +amominu_h 11000 . . ..... ..... 001 ..... 0101111 @atom_st +amomaxu_h 11100 . . ..... ..... 001 ..... 0101111 @atom_st diff --git a/target/riscv/insn_trans/trans_rvzabha.c.inc b/target/riscv/insn_trans/trans_rvzabha.c.inc new file mode 100644 index 0000000000..9093a1cfc1 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzabha.c.inc @@ -0,0 +1,131 @@ +/* + * RISC-V translation routines for the Zabha Standard Extension. + * + * Copyright (c) 2024 Alibaba Group + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define REQUIRE_ZABHA(ctx) do { \ + if (!ctx->cfg_ptr->ext_zabha) { \ + return false; \ + } \ +} while (0) + +static bool trans_amoswap_b(DisasContext *ctx, arg_amoswap_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_SB); +} + +static bool trans_amoadd_b(DisasContext *ctx, arg_amoadd_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_SB); +} + +static bool trans_amoxor_b(DisasContext *ctx, arg_amoxor_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_SB); +} + +static bool trans_amoand_b(DisasContext *ctx, arg_amoand_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_SB); +} + +static bool trans_amoor_b(DisasContext *ctx, arg_amoor_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_SB); +} + +static bool trans_amomin_b(DisasContext *ctx, arg_amomin_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_SB); +} + +static bool trans_amomax_b(DisasContext *ctx, arg_amomax_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_SB); +} + +static bool trans_amominu_b(DisasContext *ctx, arg_amominu_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_SB); +} + +static bool trans_amomaxu_b(DisasContext *ctx, arg_amomaxu_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_SB); +} + +static bool trans_amoswap_h(DisasContext *ctx, arg_amoswap_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_TESW); +} + +static bool trans_amoadd_h(DisasContext *ctx, arg_amoadd_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_TESW); +} + +static bool trans_amoxor_h(DisasContext *ctx, arg_amoxor_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_TESW); +} + +static bool trans_amoand_h(DisasContext *ctx, arg_amoand_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_TESW); +} + +static bool trans_amoor_h(DisasContext *ctx, arg_amoor_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_TESW); +} + +static bool trans_amomin_h(DisasContext *ctx, arg_amomin_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_TESW); +} + +static bool trans_amomax_h(DisasContext *ctx, arg_amomax_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_TESW); +} + +static bool trans_amominu_h(DisasContext *ctx, arg_amominu_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_TESW); +} + +static bool trans_amomaxu_h(DisasContext *ctx, arg_amomaxu_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_TESW); +} diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 133550d6e2..4a3e786560 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1083,8 +1083,9 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, { TCGv dest = dest_gpr(ctx, a->rd); TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + MemOp size = mop & MO_SIZE; - if (ctx->cfg_ptr->ext_zama16b) { + if (ctx->cfg_ptr->ext_zama16b && size >= MO_32) { mop |= MO_ATOM_WITHIN16; } else { mop |= MO_ALIGN; @@ -1118,6 +1119,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvb.c.inc" #include "insn_trans/trans_rvzicond.c.inc" #include "insn_trans/trans_rvzacas.c.inc" +#include "insn_trans/trans_rvzabha.c.inc" #include "insn_trans/trans_rvzawrs.c.inc" #include "insn_trans/trans_rvzicbo.c.inc" #include "insn_trans/trans_rvzimop.c.inc" From 8d07887bcbf13749224bd10b0303f7a79a959edb Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:49 +0800 Subject: [PATCH 2192/2884] target/riscv: Move gen_cmpxchg before adding amocas.[b|h] Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-9-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvzacas.c.inc | 13 ------------- target/riscv/translate.c | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvzacas.c.inc b/target/riscv/insn_trans/trans_rvzacas.c.inc index 5d274d4c08..fcced99fc7 100644 --- a/target/riscv/insn_trans/trans_rvzacas.c.inc +++ b/target/riscv/insn_trans/trans_rvzacas.c.inc @@ -22,19 +22,6 @@ } \ } while (0) -static bool gen_cmpxchg(DisasContext *ctx, arg_atomic *a, MemOp mop) -{ - TCGv dest = get_gpr(ctx, a->rd, EXT_NONE); - TCGv src1 = get_address(ctx, a->rs1, 0); - TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); - - decode_save_opc(ctx); - tcg_gen_atomic_cmpxchg_tl(dest, src1, dest, src2, ctx->mem_idx, mop); - - gen_set_gpr(ctx, a->rd, dest); - return true; -} - static bool trans_amocas_w(DisasContext *ctx, arg_amocas_w *a) { REQUIRE_ZACAS(ctx); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 4a3e786560..acba90f170 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1099,6 +1099,19 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, return true; } +static bool gen_cmpxchg(DisasContext *ctx, arg_atomic *a, MemOp mop) +{ + TCGv dest = get_gpr(ctx, a->rd, EXT_NONE); + TCGv src1 = get_address(ctx, a->rs1, 0); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + decode_save_opc(ctx); + tcg_gen_atomic_cmpxchg_tl(dest, src1, dest, src2, ctx->mem_idx, mop); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) { DisasContext *ctx = container_of(dcbase, DisasContext, base); From d34e4066025378dff8d444104100d3b07e127fe6 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:50 +0800 Subject: [PATCH 2193/2884] target/riscv: Add amocas.[b|h] for Zabha Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-10-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn32.decode | 2 ++ target/riscv/insn_trans/trans_rvzabha.c.inc | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 3bad6372f2..c45b8fa1d8 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -1041,3 +1041,5 @@ amomin_h 10000 . . ..... ..... 001 ..... 0101111 @atom_st amomax_h 10100 . . ..... ..... 001 ..... 0101111 @atom_st amominu_h 11000 . . ..... ..... 001 ..... 0101111 @atom_st amomaxu_h 11100 . . ..... ..... 001 ..... 0101111 @atom_st +amocas_b 00101 . . ..... ..... 000 ..... 0101111 @atom_st +amocas_h 00101 . . ..... ..... 001 ..... 0101111 @atom_st diff --git a/target/riscv/insn_trans/trans_rvzabha.c.inc b/target/riscv/insn_trans/trans_rvzabha.c.inc index 9093a1cfc1..ce8edcba62 100644 --- a/target/riscv/insn_trans/trans_rvzabha.c.inc +++ b/target/riscv/insn_trans/trans_rvzabha.c.inc @@ -129,3 +129,17 @@ static bool trans_amomaxu_h(DisasContext *ctx, arg_amomaxu_h *a) REQUIRE_ZABHA(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_TESW); } + +static bool trans_amocas_b(DisasContext *ctx, arg_amocas_b *a) +{ + REQUIRE_ZACAS(ctx); + REQUIRE_ZABHA(ctx); + return gen_cmpxchg(ctx, a, MO_SB); +} + +static bool trans_amocas_h(DisasContext *ctx, arg_amocas_h *a) +{ + REQUIRE_ZACAS(ctx); + REQUIRE_ZABHA(ctx); + return gen_cmpxchg(ctx, a, MO_ALIGN | MO_TESW); +} From 8aebaa2591320619cbd3e75f91f6e088f2c3dcfa Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:51 +0800 Subject: [PATCH 2194/2884] target/riscv: Expose zabha extension as a cpu property Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-11-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index de9c06904f..33ef4eb795 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -117,6 +117,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zmmul, PRIV_VERSION_1_12_0, ext_zmmul), ISA_EXT_DATA_ENTRY(za64rs, PRIV_VERSION_1_12_0, has_priv_1_11), ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), + ISA_EXT_DATA_ENTRY(zabha, PRIV_VERSION_1_13_0, ext_zabha), ISA_EXT_DATA_ENTRY(zacas, PRIV_VERSION_1_12_0, ext_zacas), ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), ISA_EXT_DATA_ENTRY(zalrsc, PRIV_VERSION_1_12_0, ext_zalrsc), @@ -1478,6 +1479,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("zcmop", ext_zcmop, false), MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), MULTI_EXT_CFG_BOOL("zama16b", ext_zama16b, false), + MULTI_EXT_CFG_BOOL("zabha", ext_zabha, false), MULTI_EXT_CFG_BOOL("zaamo", ext_zaamo, false), MULTI_EXT_CFG_BOOL("zalrsc", ext_zalrsc, false), MULTI_EXT_CFG_BOOL("zawrs", ext_zawrs, true), From ae4bdcef6fd3166264a47ed6a17cb9870e32306e Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Tue, 9 Jul 2024 19:36:52 +0800 Subject: [PATCH 2195/2884] disas/riscv: Support zabha disassemble Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709113652.1239-12-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- disas/riscv.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index d29cb1ff7d..c8364c2b07 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -954,6 +954,26 @@ typedef enum { rv_c_mop_11 = 923, rv_c_mop_13 = 924, rv_c_mop_15 = 925, + rv_op_amoswap_b = 926, + rv_op_amoadd_b = 927, + rv_op_amoxor_b = 928, + rv_op_amoor_b = 929, + rv_op_amoand_b = 930, + rv_op_amomin_b = 931, + rv_op_amomax_b = 932, + rv_op_amominu_b = 933, + rv_op_amomaxu_b = 934, + rv_op_amoswap_h = 935, + rv_op_amoadd_h = 936, + rv_op_amoxor_h = 937, + rv_op_amoor_h = 938, + rv_op_amoand_h = 939, + rv_op_amomin_h = 940, + rv_op_amomax_h = 941, + rv_op_amominu_h = 942, + rv_op_amomaxu_h = 943, + rv_op_amocas_b = 944, + rv_op_amocas_h = 945, } rv_op; /* register names */ @@ -2192,6 +2212,26 @@ const rv_opcode_data rvi_opcode_data[] = { { "c.mop.11", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, { "c.mop.13", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, { "c.mop.15", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -2986,9 +3026,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_amoadd_b; break; + case 1: op = rv_op_amoadd_h; break; case 2: op = rv_op_amoadd_w; break; case 3: op = rv_op_amoadd_d; break; case 4: op = rv_op_amoadd_q; break; + case 8: op = rv_op_amoswap_b; break; + case 9: op = rv_op_amoswap_h; break; case 10: op = rv_op_amoswap_w; break; case 11: op = rv_op_amoswap_d; break; case 12: op = rv_op_amoswap_q; break; @@ -3010,27 +3054,43 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 26: op = rv_op_sc_w; break; case 27: op = rv_op_sc_d; break; case 28: op = rv_op_sc_q; break; + case 32: op = rv_op_amoxor_b; break; + case 33: op = rv_op_amoxor_h; break; case 34: op = rv_op_amoxor_w; break; case 35: op = rv_op_amoxor_d; break; case 36: op = rv_op_amoxor_q; break; + case 40: op = rv_op_amocas_b; break; + case 41: op = rv_op_amocas_h; break; case 42: op = rv_op_amocas_w; break; case 43: op = rv_op_amocas_d; break; case 44: op = rv_op_amocas_q; break; + case 64: op = rv_op_amoor_b; break; + case 65: op = rv_op_amoor_h; break; case 66: op = rv_op_amoor_w; break; case 67: op = rv_op_amoor_d; break; case 68: op = rv_op_amoor_q; break; + case 96: op = rv_op_amoand_b; break; + case 97: op = rv_op_amoand_h; break; case 98: op = rv_op_amoand_w; break; case 99: op = rv_op_amoand_d; break; case 100: op = rv_op_amoand_q; break; + case 128: op = rv_op_amomin_b; break; + case 129: op = rv_op_amomin_h; break; case 130: op = rv_op_amomin_w; break; case 131: op = rv_op_amomin_d; break; case 132: op = rv_op_amomin_q; break; + case 160: op = rv_op_amomax_b; break; + case 161: op = rv_op_amomax_h; break; case 162: op = rv_op_amomax_w; break; case 163: op = rv_op_amomax_d; break; case 164: op = rv_op_amomax_q; break; + case 192: op = rv_op_amominu_b; break; + case 193: op = rv_op_amominu_h; break; case 194: op = rv_op_amominu_w; break; case 195: op = rv_op_amominu_d; break; case 196: op = rv_op_amominu_q; break; + case 224: op = rv_op_amomaxu_b; break; + case 225: op = rv_op_amomaxu_h; break; case 226: op = rv_op_amomaxu_w; break; case 227: op = rv_op_amomaxu_d; break; case 228: op = rv_op_amomaxu_q; break; From 910c18a91738f9b77c5faf66e3534bb10d6e306e Mon Sep 17 00:00:00 2001 From: Jiayi Li <lijiayi@eswincomputing.com> Date: Mon, 1 Jul 2024 10:25:53 +0800 Subject: [PATCH 2196/2884] target/riscv: Validate the mode in write_vstvec Base on the riscv-privileged spec, vstvec substitutes for the usual stvec. Therefore, the encoding of the MODE should also be restricted to 0 and 1. Signed-off-by: Jiayi Li <lijiayi@eswincomputing.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Message-ID: <20240701022553.1982-1-lijiayi@eswincomputing.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 432c59dc66..f9229d92ab 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3791,7 +3791,12 @@ static RISCVException read_vstvec(CPURISCVState *env, int csrno, static RISCVException write_vstvec(CPURISCVState *env, int csrno, target_ulong val) { - env->vstvec = val; + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val & 3) < 2) { + env->vstvec = val; + } else { + qemu_log_mask(LOG_UNIMP, "CSR_VSTVEC: reserved mode not supported\n"); + } return RISCV_EXCP_NONE; } From 4d46d84ea7fbb694125b95c88e788caf6cd039fa Mon Sep 17 00:00:00 2001 From: Balaji Ravikumar <bravikumar@rivosinc.com> Date: Fri, 5 Jul 2024 17:53:16 +0100 Subject: [PATCH 2197/2884] disas/riscv: Add decode for Zawrs extension Add disassembly support for these instructions from Zawrs: * wrs.sto * wrs.nto Signed-off-by: Balaji Ravikumar <bravikumar@rivosinc.com> Signed-off-by: Rob Bradford <rbradford@rivosinc.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240705165316.127494-1-rbradford@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- disas/riscv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index c8364c2b07..5965574d87 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -974,6 +974,8 @@ typedef enum { rv_op_amomaxu_h = 943, rv_op_amocas_b = 944, rv_op_amocas_h = 945, + rv_op_wrs_sto = 946, + rv_op_wrs_nto = 947, } rv_op; /* register names */ @@ -2232,6 +2234,8 @@ const rv_opcode_data rvi_opcode_data[] = { { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "wrs.sto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "wrs.nto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -3980,6 +3984,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: op = rv_op_ecall; break; case 32: op = rv_op_ebreak; break; case 64: op = rv_op_uret; break; + case 416: op = rv_op_wrs_nto; break; + case 928: op = rv_op_wrs_sto; break; } break; case 256: From 3cb9f20499cd7da644387a17a79230f8ffd89993 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Date: Tue, 9 Jul 2024 05:54:31 -0300 Subject: [PATCH 2198/2884] target/riscv/kvm: update KVM regs to Linux 6.10-rc5 Two new regs added: ztso and zacas. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240709085431.455541-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/kvm/kvm-cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 1047961fed..f6e3156b8d 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -281,6 +281,7 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zihintntl", ext_zihintntl, KVM_RISCV_ISA_EXT_ZIHINTNTL), KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), KVM_EXT_CFG("zihpm", ext_zihpm, KVM_RISCV_ISA_EXT_ZIHPM), + KVM_EXT_CFG("zacas", ext_zacas, KVM_RISCV_ISA_EXT_ZACAS), KVM_EXT_CFG("zfa", ext_zfa, KVM_RISCV_ISA_EXT_ZFA), KVM_EXT_CFG("zfh", ext_zfh, KVM_RISCV_ISA_EXT_ZFH), KVM_EXT_CFG("zfhmin", ext_zfhmin, KVM_RISCV_ISA_EXT_ZFHMIN), @@ -298,6 +299,7 @@ static KVMCPUConfig kvm_multi_ext_cfgs[] = { KVM_EXT_CFG("zksed", ext_zksed, KVM_RISCV_ISA_EXT_ZKSED), KVM_EXT_CFG("zksh", ext_zksh, KVM_RISCV_ISA_EXT_ZKSH), KVM_EXT_CFG("zkt", ext_zkt, KVM_RISCV_ISA_EXT_ZKT), + KVM_EXT_CFG("ztso", ext_ztso, KVM_RISCV_ISA_EXT_ZTSO), KVM_EXT_CFG("zvbb", ext_zvbb, KVM_RISCV_ISA_EXT_ZVBB), KVM_EXT_CFG("zvbc", ext_zvbc, KVM_RISCV_ISA_EXT_ZVBC), KVM_EXT_CFG("zvfh", ext_zvfh, KVM_RISCV_ISA_EXT_ZVFH), From 68c05fb53036f59a55a54c943b713e8ac78fc627 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal <rkanwal@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:04 -0700 Subject: [PATCH 2199/2884] target/riscv: Combine set_mode and set_virt functions. Combining riscv_cpu_set_virt_enabled() and riscv_cpu_set_mode() functions. This is to make complete mode change information available through a single function. This allows to easily differentiate between HS->VS, VS->HS and VS->VS transitions when executing state update codes. For example: One use-case which inspired this change is to update mode-specific instruction and cycle counters which requires information of both prev mode and current mode. Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20240711-smcntrpmf_v7-v8-1-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 2 +- target/riscv/cpu_helper.c | 57 +++++++++++++++++++-------------------- target/riscv/op_helper.c | 17 +++++------- 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 87742047ce..6520e0f5d5 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -567,7 +567,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); #endif /* !CONFIG_USER_ONLY */ -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); void riscv_translate_init(void); G_NORETURN void riscv_raise_exception(CPURISCVState *env, diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 6709622dd3..10d3fdaed3 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -619,30 +619,6 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) env->geilen = geilen; } -/* This function can only be called to set virt when RVH is enabled */ -void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) -{ - /* Flush the TLB on all virt mode changes. */ - if (env->virt_enabled != enable) { - tlb_flush(env_cpu(env)); - } - - env->virt_enabled = enable; - - if (enable) { - /* - * The guest external interrupts from an interrupt controller are - * delivered only when the Guest/VM is running (i.e. V=1). This means - * any guest external interrupt which is triggered while the Guest/VM - * is not running (i.e. V=0) will be missed on QEMU resulting in guest - * with sluggish response to serial console input and other I/O events. - * - * To solve this, we check and inject interrupt after setting V=1. - */ - riscv_cpu_update_mip(env, 0, 0); - } -} - int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) { CPURISCVState *env = &cpu->env; @@ -715,7 +691,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, } } -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) { g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); @@ -736,6 +712,28 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * preemptive context switch. As a result, do both. */ env->load_res = -1; + + if (riscv_has_ext(env, RVH)) { + /* Flush the TLB on all virt mode changes. */ + if (env->virt_enabled != virt_en) { + tlb_flush(env_cpu(env)); + } + + env->virt_enabled = virt_en; + if (virt_en) { + /* + * The guest external interrupts from an interrupt controller are + * delivered only when the Guest/VM is running (i.e. V=1). This + * means any guest external interrupt which is triggered while the + * Guest/VM is not running (i.e. V=0) will be missed on QEMU + * resulting in guest with sluggish response to serial console + * input and other I/O events. + * + * To solve this, we check and inject interrupt after setting V=1. + */ + riscv_cpu_update_mip(env, 0, 0); + } + } } /* @@ -1648,6 +1646,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + bool virt = env->virt_enabled; bool write_gva = false; uint64_t s; @@ -1778,7 +1777,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) htval = env->guest_phys_fault_addr; - riscv_cpu_set_virt_enabled(env, 0); + virt = false; } else { /* Trap into HS mode */ env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false); @@ -1799,7 +1798,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->htinst = tinst; env->pc = (env->stvec >> 2 << 2) + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); - riscv_cpu_set_mode(env, PRV_S); + riscv_cpu_set_mode(env, PRV_S, virt); } else { /* handle the trap in M-mode */ if (riscv_has_ext(env, RVH)) { @@ -1815,7 +1814,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) mtval2 = env->guest_phys_fault_addr; /* Trapping to M mode, virt is disabled */ - riscv_cpu_set_virt_enabled(env, 0); + virt = false; } s = env->mstatus; @@ -1830,7 +1829,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mtinst = tinst; env->pc = (env->mtvec >> 2 << 2) + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); - riscv_cpu_set_mode(env, PRV_M); + riscv_cpu_set_mode(env, PRV_M, virt); } /* diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 2baf5bc3ca..ec1408ba0f 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -264,7 +264,7 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address) target_ulong helper_sret(CPURISCVState *env) { uint64_t mstatus; - target_ulong prev_priv, prev_virt; + target_ulong prev_priv, prev_virt = env->virt_enabled; if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); @@ -307,11 +307,9 @@ target_ulong helper_sret(CPURISCVState *env) if (prev_virt) { riscv_cpu_swap_hypervisor_regs(env); } - - riscv_cpu_set_virt_enabled(env, prev_virt); } - riscv_cpu_set_mode(env, prev_priv); + riscv_cpu_set_mode(env, prev_priv, prev_virt); return retpc; } @@ -347,16 +345,13 @@ target_ulong helper_mret(CPURISCVState *env) mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } env->mstatus = mstatus; - riscv_cpu_set_mode(env, prev_priv); - if (riscv_has_ext(env, RVH)) { - if (prev_virt) { - riscv_cpu_swap_hypervisor_regs(env); - } - - riscv_cpu_set_virt_enabled(env, prev_virt); + if (riscv_has_ext(env, RVH) && prev_virt) { + riscv_cpu_swap_hypervisor_regs(env); } + riscv_cpu_set_mode(env, prev_priv, prev_virt); + return retpc; } From be470e597708451e6fecb0631728fa759164d03e Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:05 -0700 Subject: [PATCH 2200/2884] target/riscv: Fix the predicate functions for mhpmeventhX CSRs mhpmeventhX CSRs are available for RV32. The predicate function should check that first before checking sscofpmf extension. Fixes: 14664483457b ("target/riscv: Add sscofpmf extension support") Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Message-ID: <20240711-smcntrpmf_v7-v8-2-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 67 ++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index f9229d92ab..1bcf75f91f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -227,6 +227,15 @@ static RISCVException sscofpmf(CPURISCVState *env, int csrno) return RISCV_EXCP_NONE; } +static RISCVException sscofpmf_32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return sscofpmf(env, csrno); +} + static RISCVException any(CPURISCVState *env, int csrno) { return RISCV_EXCP_NONE; @@ -5106,91 +5115,91 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, write_mhpmevent }, - [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, From 251dccc09af363900436656461151681687e2470 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue <kaiwenx@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:06 -0700 Subject: [PATCH 2201/2884] target/riscv: Add cycle & instret privilege mode filtering properties This adds the properties for ISA extension smcntrpmf. Patches implementing it will follow. Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240711-smcntrpmf_v7-v8-3-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 1 + target/riscv/cpu_cfg.h | 1 + 2 files changed, 2 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 33ef4eb795..4efe7ee3b0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -182,6 +182,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), + ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 120905a254..8b272fb826 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -76,6 +76,7 @@ struct RISCVCPUConfig { bool ext_ztso; bool ext_smstateen; bool ext_sstc; + bool ext_smcntrpmf; bool ext_svadu; bool ext_svinval; bool ext_svnapot; From 6d1e3893cfaeedb47a16edbb766fcc0c7907ab94 Mon Sep 17 00:00:00 2001 From: Kaiwen Xue <kaiwenx@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:07 -0700 Subject: [PATCH 2202/2884] target/riscv: Add cycle & instret privilege mode filtering definitions This adds the definitions for ISA extension smcntrpmf. Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Message-ID: <20240711-smcntrpmf_v7-v8-4-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 6 ++++++ target/riscv/cpu_bits.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 6520e0f5d5..980e2154cd 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -362,6 +362,12 @@ struct CPUArchState { uint32_t mcountinhibit; + /* PMU cycle & instret privilege mode filtering */ + target_ulong mcyclecfg; + target_ulong mcyclecfgh; + target_ulong minstretcfg; + target_ulong minstretcfgh; + /* PMU counter state */ PMUCTRState pmu_ctrs[RV_MAX_MHPMCOUNTERS]; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index c257c5ed7d..5faa817453 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -397,6 +397,10 @@ /* Machine counter-inhibit register */ #define CSR_MCOUNTINHIBIT 0x320 +/* Machine counter configuration registers */ +#define CSR_MCYCLECFG 0x321 +#define CSR_MINSTRETCFG 0x322 + #define CSR_MHPMEVENT3 0x323 #define CSR_MHPMEVENT4 0x324 #define CSR_MHPMEVENT5 0x325 @@ -427,6 +431,9 @@ #define CSR_MHPMEVENT30 0x33e #define CSR_MHPMEVENT31 0x33f +#define CSR_MCYCLECFGH 0x721 +#define CSR_MINSTRETCFGH 0x722 + #define CSR_MHPMEVENT3H 0x723 #define CSR_MHPMEVENT4H 0x724 #define CSR_MHPMEVENT5H 0x725 @@ -884,6 +891,28 @@ typedef enum RISCVException { /* PMU related bits */ #define MIE_LCOFIE (1 << IRQ_PMU_OVF) +#define MCYCLECFG_BIT_MINH BIT_ULL(62) +#define MCYCLECFGH_BIT_MINH BIT(30) +#define MCYCLECFG_BIT_SINH BIT_ULL(61) +#define MCYCLECFGH_BIT_SINH BIT(29) +#define MCYCLECFG_BIT_UINH BIT_ULL(60) +#define MCYCLECFGH_BIT_UINH BIT(28) +#define MCYCLECFG_BIT_VSINH BIT_ULL(59) +#define MCYCLECFGH_BIT_VSINH BIT(27) +#define MCYCLECFG_BIT_VUINH BIT_ULL(58) +#define MCYCLECFGH_BIT_VUINH BIT(26) + +#define MINSTRETCFG_BIT_MINH BIT_ULL(62) +#define MINSTRETCFGH_BIT_MINH BIT(30) +#define MINSTRETCFG_BIT_SINH BIT_ULL(61) +#define MINSTRETCFGH_BIT_SINH BIT(29) +#define MINSTRETCFG_BIT_UINH BIT_ULL(60) +#define MINSTRETCFGH_BIT_UINH BIT(28) +#define MINSTRETCFG_BIT_VSINH BIT_ULL(59) +#define MINSTRETCFGH_BIT_VSINH BIT(27) +#define MINSTRETCFG_BIT_VUINH BIT_ULL(58) +#define MINSTRETCFGH_BIT_VUINH BIT(26) + #define MHPMEVENT_BIT_OF BIT_ULL(63) #define MHPMEVENTH_BIT_OF BIT(31) #define MHPMEVENT_BIT_MINH BIT_ULL(62) From b54a84c15e389a4795022cc7edfe3a7e32dc065d Mon Sep 17 00:00:00 2001 From: Kaiwen Xue <kaiwenx@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:08 -0700 Subject: [PATCH 2203/2884] target/riscv: Add cycle & instret privilege mode filtering support QEMU only calculates dummy cycles and instructions, so there is no actual means to stop the icount in QEMU. Hence this patch merely adds the functionality of accessing the cfg registers, and cause no actual effects on the counting of cycle and instret counters. Signed-off-by: Atish Patra <atishp@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240711-smcntrpmf_v7-v8-5-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu_bits.h | 12 ++++ target/riscv/csr.c | 138 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 5faa817453..32b068f18a 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -926,6 +926,18 @@ typedef enum RISCVException { #define MHPMEVENT_BIT_VUINH BIT_ULL(58) #define MHPMEVENTH_BIT_VUINH BIT(26) +#define MHPMEVENT_FILTER_MASK (MHPMEVENT_BIT_MINH | \ + MHPMEVENT_BIT_SINH | \ + MHPMEVENT_BIT_UINH | \ + MHPMEVENT_BIT_VSINH | \ + MHPMEVENT_BIT_VUINH) + +#define MHPMEVENTH_FILTER_MASK (MHPMEVENTH_BIT_MINH | \ + MHPMEVENTH_BIT_SINH | \ + MHPMEVENTH_BIT_UINH | \ + MHPMEVENTH_BIT_VSINH | \ + MHPMEVENTH_BIT_VUINH) + #define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) #define MHPMEVENT_IDX_MASK 0xFFFFF #define MHPMEVENT_SSCOF_RESVD 16 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 1bcf75f91f..8831d4f5ec 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -30,7 +30,6 @@ #include "qemu/guest-random.h" #include "qapi/error.h" - /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) { @@ -236,6 +235,24 @@ static RISCVException sscofpmf_32(CPURISCVState *env, int csrno) return sscofpmf(env, csrno); } +static RISCVException smcntrpmf(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smcntrpmf) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException smcntrpmf_32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smcntrpmf(env, csrno); +} + static RISCVException any(CPURISCVState *env, int csrno) { return RISCV_EXCP_NONE; @@ -830,6 +847,111 @@ static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, #else /* CONFIG_USER_ONLY */ +static RISCVException read_mcyclecfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mcyclecfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcyclecfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t inh_avail_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->mcyclecfg = val; + } else { + /* Set xINH fields if priv mode supported */ + inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MCYCLECFG_BIT_MINH; + inh_avail_mask |= riscv_has_ext(env, RVU) ? MCYCLECFG_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MCYCLECFG_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MCYCLECFG_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MCYCLECFG_BIT_VSINH : 0; + env->mcyclecfg = val & inh_avail_mask; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException read_mcyclecfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mcyclecfgh; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcyclecfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MCYCLECFGH_BIT_MINH); + + /* Set xINH fields if priv mode supported */ + inh_avail_mask |= riscv_has_ext(env, RVU) ? MCYCLECFGH_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MCYCLECFGH_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MCYCLECFGH_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MCYCLECFGH_BIT_VSINH : 0; + + env->mcyclecfgh = val & inh_avail_mask; + return RISCV_EXCP_NONE; +} + +static RISCVException read_minstretcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->minstretcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_minstretcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t inh_avail_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->minstretcfg = val; + } else { + inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MINSTRETCFG_BIT_MINH; + inh_avail_mask |= riscv_has_ext(env, RVU) ? MINSTRETCFG_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MINSTRETCFG_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MINSTRETCFG_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MINSTRETCFG_BIT_VSINH : 0; + env->minstretcfg = val & inh_avail_mask; + } + return RISCV_EXCP_NONE; +} + +static RISCVException read_minstretcfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->minstretcfgh; + return RISCV_EXCP_NONE; +} + +static RISCVException write_minstretcfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MINSTRETCFGH_BIT_MINH); + + inh_avail_mask |= riscv_has_ext(env, RVU) ? MINSTRETCFGH_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MINSTRETCFGH_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MINSTRETCFGH_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MINSTRETCFGH_BIT_VSINH : 0; + + env->minstretcfgh = val & inh_avail_mask; + return RISCV_EXCP_NONE; +} + static RISCVException read_mhpmevent(CPURISCVState *env, int csrno, target_ulong *val) { @@ -5056,6 +5178,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_mcountinhibit, .min_priv_ver = PRIV_VERSION_1_11_0 }, + [CSR_MCYCLECFG] = { "mcyclecfg", smcntrpmf, read_mcyclecfg, + write_mcyclecfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MINSTRETCFG] = { "minstretcfg", smcntrpmf, read_minstretcfg, + write_minstretcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_mhpmevent, write_mhpmevent }, [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_mhpmevent, @@ -5115,6 +5244,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, write_mhpmevent }, + [CSR_MCYCLECFGH] = { "mcyclecfgh", smcntrpmf_32, read_mcyclecfgh, + write_mcyclecfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MINSTRETCFGH] = { "minstretcfgh", smcntrpmf_32, read_minstretcfgh, + write_minstretcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, From 3b31b7baff02b357a6c921b26ae953e04d0cfdbb Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:09 -0700 Subject: [PATCH 2204/2884] target/riscv: Only set INH fields if priv mode is available Currently, the INH fields are set in mhpmevent uncoditionally without checking if a particular priv mode is supported or not. Suggested-by: Alistair Francis <alistair23@gmail.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240711-smcntrpmf_v7-v8-6-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 8831d4f5ec..364583dc03 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -967,13 +967,24 @@ static RISCVException write_mhpmevent(CPURISCVState *env, int csrno, { int evt_index = csrno - CSR_MCOUNTINHIBIT; uint64_t mhpmevt_val = val; - - env->mhpmevent_val[evt_index] = val; + uint64_t inh_avail_mask; if (riscv_cpu_mxl(env) == MXL_RV32) { + env->mhpmevent_val[evt_index] = val; mhpmevt_val = mhpmevt_val | ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + } else { + inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MHPMEVENT_BIT_MINH; + inh_avail_mask |= riscv_has_ext(env, RVU) ? MHPMEVENT_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MHPMEVENT_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MHPMEVENT_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MHPMEVENT_BIT_VSINH : 0; + mhpmevt_val = val & inh_avail_mask; + env->mhpmevent_val[evt_index] = mhpmevt_val; } + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); return RISCV_EXCP_NONE; @@ -993,11 +1004,21 @@ static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong val) { int evt_index = csrno - CSR_MHPMEVENT3H + 3; - uint64_t mhpmevth_val = val; + uint64_t mhpmevth_val; uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MHPMEVENTH_BIT_MINH); + inh_avail_mask |= riscv_has_ext(env, RVU) ? MHPMEVENTH_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MHPMEVENTH_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MHPMEVENTH_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MHPMEVENTH_BIT_VSINH : 0; + + mhpmevth_val = val & inh_avail_mask; mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); - env->mhpmeventh_val[evt_index] = val; + env->mhpmeventh_val[evt_index] = mhpmevth_val; riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); From b2d7a7c7e4e30fb5341d38deac968de675f9419c Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:10 -0700 Subject: [PATCH 2205/2884] target/riscv: Implement privilege mode filtering for cycle/instret Privilege mode filtering can also be emulated for cycle/instret by tracking host_ticks/icount during each privilege mode switch. This patch implements that for both cycle/instret and mhpmcounters. The first one requires Smcntrpmf while the other one requires Sscofpmf to be enabled. The cycle/instret are still computed using host ticks when icount is not enabled. Otherwise, they are computed using raw icount which is more accurate in icount mode. Co-Developed-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Message-ID: <20240711-smcntrpmf_v7-v8-7-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 11 ++++ target/riscv/cpu_helper.c | 9 ++- target/riscv/csr.c | 117 ++++++++++++++++++++++++++------------ target/riscv/pmu.c | 92 ++++++++++++++++++++++++++++++ target/riscv/pmu.h | 2 + 5 files changed, 194 insertions(+), 37 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 980e2154cd..f515ad072b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -181,6 +181,15 @@ typedef struct PMUCTRState { target_ulong irq_overflow_left; } PMUCTRState; +typedef struct PMUFixedCtrState { + /* Track cycle and icount for each privilege mode */ + uint64_t counter[4]; + uint64_t counter_prev[4]; + /* Track cycle and icount for each privilege mode when V = 1*/ + uint64_t counter_virt[2]; + uint64_t counter_virt_prev[2]; +} PMUFixedCtrState; + struct CPUArchState { target_ulong gpr[32]; target_ulong gprh[32]; /* 64 top bits of the 128-bit registers */ @@ -377,6 +386,8 @@ struct CPUArchState { /* PMU event selector configured values for RV32 */ target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; + PMUFixedCtrState pmu_fixed_ctrs[2]; + target_ulong sscratch; target_ulong mscratch; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 10d3fdaed3..395a1d9140 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -695,9 +695,14 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) { g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); - if (icount_enabled() && newpriv != env->priv) { - riscv_itrigger_update_priv(env); + if (newpriv != env->priv || env->virt_enabled != virt_en) { + if (icount_enabled()) { + riscv_itrigger_update_priv(env); + } + + riscv_pmu_update_fixed_ctrs(env, newpriv, virt_en); } + /* tlb_flush is unnecessary as mode is contained in mmu_idx */ env->priv = newpriv; env->xl = cpu_recompute_xl(env); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 364583dc03..85d3f0aa3f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -787,36 +787,16 @@ static RISCVException write_vcsr(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +#if defined(CONFIG_USER_ONLY) /* User Timers and Counters */ -static target_ulong get_ticks(bool shift, bool instructions) +static target_ulong get_ticks(bool shift) { - int64_t val; - target_ulong result; - -#if !defined(CONFIG_USER_ONLY) - if (icount_enabled()) { - if (instructions) { - val = icount_get_raw(); - } else { - val = icount_get(); - } - } else { - val = cpu_get_host_ticks(); - } -#else - val = cpu_get_host_ticks(); -#endif - - if (shift) { - result = val >> 32; - } else { - result = val; - } + int64_t val = cpu_get_host_ticks(); + target_ulong result = shift ? val >> 32 : val; return result; } -#if defined(CONFIG_USER_ONLY) static RISCVException read_time(CPURISCVState *env, int csrno, target_ulong *val) { @@ -834,14 +814,14 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, static RISCVException read_hpmcounter(CPURISCVState *env, int csrno, target_ulong *val) { - *val = get_ticks(false, (csrno == CSR_INSTRET)); + *val = get_ticks(false); return RISCV_EXCP_NONE; } static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) { - *val = get_ticks(true, (csrno == CSR_INSTRETH)); + *val = get_ticks(true); return RISCV_EXCP_NONE; } @@ -1025,17 +1005,82 @@ static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static target_ulong riscv_pmu_ctr_get_fixed_counters_val(CPURISCVState *env, + int counter_idx, + bool upper_half) +{ + int inst = riscv_pmu_ctr_monitor_instructions(env, counter_idx); + uint64_t *counter_arr_virt = env->pmu_fixed_ctrs[inst].counter_virt; + uint64_t *counter_arr = env->pmu_fixed_ctrs[inst].counter; + target_ulong result = 0; + uint64_t curr_val = 0; + uint64_t cfg_val = 0; + + if (counter_idx == 0) { + cfg_val = upper_half ? ((uint64_t)env->mcyclecfgh << 32) : + env->mcyclecfg; + } else if (counter_idx == 2) { + cfg_val = upper_half ? ((uint64_t)env->minstretcfgh << 32) : + env->minstretcfg; + } else { + cfg_val = upper_half ? + ((uint64_t)env->mhpmeventh_val[counter_idx] << 32) : + env->mhpmevent_val[counter_idx]; + cfg_val &= MHPMEVENT_FILTER_MASK; + } + + if (!cfg_val) { + if (icount_enabled()) { + curr_val = inst ? icount_get_raw() : icount_get(); + } else { + curr_val = cpu_get_host_ticks(); + } + + goto done; + } + + if (!(cfg_val & MCYCLECFG_BIT_MINH)) { + curr_val += counter_arr[PRV_M]; + } + + if (!(cfg_val & MCYCLECFG_BIT_SINH)) { + curr_val += counter_arr[PRV_S]; + } + + if (!(cfg_val & MCYCLECFG_BIT_UINH)) { + curr_val += counter_arr[PRV_U]; + } + + if (!(cfg_val & MCYCLECFG_BIT_VSINH)) { + curr_val += counter_arr_virt[PRV_S]; + } + + if (!(cfg_val & MCYCLECFG_BIT_VUINH)) { + curr_val += counter_arr_virt[PRV_U]; + } + +done: + if (riscv_cpu_mxl(env) == MXL_RV32) { + result = upper_half ? curr_val >> 32 : curr_val; + } else { + result = curr_val; + } + + return result; +} + static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) { int ctr_idx = csrno - CSR_MCYCLE; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; uint64_t mhpmctr_val = val; - bool instr = riscv_pmu_ctr_monitor_instructions(env, ctr_idx); counter->mhpmcounter_val = val; - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || instr) { - counter->mhpmcounter_prev = get_ticks(false, instr); + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + counter->mhpmcounter_prev = riscv_pmu_ctr_get_fixed_counters_val(env, + ctr_idx, false); if (ctr_idx > 2) { if (riscv_cpu_mxl(env) == MXL_RV32) { mhpmctr_val = mhpmctr_val | @@ -1058,12 +1103,13 @@ static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; uint64_t mhpmctr_val = counter->mhpmcounter_val; uint64_t mhpmctrh_val = val; - bool instr = riscv_pmu_ctr_monitor_instructions(env, ctr_idx); counter->mhpmcounterh_val = val; mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || instr) { - counter->mhpmcounterh_prev = get_ticks(true, instr); + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + counter->mhpmcounterh_prev = riscv_pmu_ctr_get_fixed_counters_val(env, + ctr_idx, true); if (ctr_idx > 2) { riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); } @@ -1082,7 +1128,6 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, counter->mhpmcounter_prev; target_ulong ctr_val = upper_half ? counter->mhpmcounterh_val : counter->mhpmcounter_val; - bool instr = riscv_pmu_ctr_monitor_instructions(env, ctr_idx); if (get_field(env->mcountinhibit, BIT(ctr_idx))) { /* @@ -1103,8 +1148,10 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, * The kernel computes the perf delta by subtracting the current value from * the value it initialized previously (ctr_val). */ - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || instr) { - *val = get_ticks(upper_half, instr) - ctr_prev + ctr_val; + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + *val = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx, upper_half) - + ctr_prev + ctr_val; } else { *val = ctr_val; } diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 0e7d58b8a5..ac648cff8d 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" +#include "qemu/timer.h" #include "cpu.h" #include "pmu.h" #include "sysemu/cpu-timers.h" @@ -176,6 +177,97 @@ static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) return 0; } +/* + * Information needed to update counters: + * new_priv, new_virt: To correctly save starting snapshot for the newly + * started mode. Look at array being indexed with newprv. + * old_priv, old_virt: To correctly select previous snapshot for old priv + * and compute delta. Also to select correct counter + * to inc. Look at arrays being indexed with env->priv. + * + * To avoid the complexity of calling this function, we assume that + * env->priv and env->virt_enabled contain old priv and old virt and + * new priv and new virt values are passed in as arguments. + */ +static void riscv_pmu_icount_update_priv(CPURISCVState *env, + target_ulong newpriv, bool new_virt) +{ + uint64_t *snapshot_prev, *snapshot_new; + uint64_t current_icount; + uint64_t *counter_arr; + uint64_t delta; + + if (icount_enabled()) { + current_icount = icount_get_raw(); + } else { + current_icount = cpu_get_host_ticks(); + } + + if (env->virt_enabled) { + counter_arr = env->pmu_fixed_ctrs[1].counter_virt; + snapshot_prev = env->pmu_fixed_ctrs[1].counter_virt_prev; + } else { + counter_arr = env->pmu_fixed_ctrs[1].counter; + snapshot_prev = env->pmu_fixed_ctrs[1].counter_prev; + } + + if (new_virt) { + snapshot_new = env->pmu_fixed_ctrs[1].counter_virt_prev; + } else { + snapshot_new = env->pmu_fixed_ctrs[1].counter_prev; + } + + /* + * new_priv can be same as env->priv. So we need to calculate + * delta first before updating snapshot_new[new_priv]. + */ + delta = current_icount - snapshot_prev[env->priv]; + snapshot_new[newpriv] = current_icount; + + counter_arr[env->priv] += delta; +} + +static void riscv_pmu_cycle_update_priv(CPURISCVState *env, + target_ulong newpriv, bool new_virt) +{ + uint64_t *snapshot_prev, *snapshot_new; + uint64_t current_ticks; + uint64_t *counter_arr; + uint64_t delta; + + if (icount_enabled()) { + current_ticks = icount_get(); + } else { + current_ticks = cpu_get_host_ticks(); + } + + if (env->virt_enabled) { + counter_arr = env->pmu_fixed_ctrs[0].counter_virt; + snapshot_prev = env->pmu_fixed_ctrs[0].counter_virt_prev; + } else { + counter_arr = env->pmu_fixed_ctrs[0].counter; + snapshot_prev = env->pmu_fixed_ctrs[0].counter_prev; + } + + if (new_virt) { + snapshot_new = env->pmu_fixed_ctrs[0].counter_virt_prev; + } else { + snapshot_new = env->pmu_fixed_ctrs[0].counter_prev; + } + + delta = current_ticks - snapshot_prev[env->priv]; + snapshot_new[newpriv] = current_ticks; + + counter_arr[env->priv] += delta; +} + +void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, + bool new_virt) +{ + riscv_pmu_cycle_update_priv(env, newpriv, new_virt); + riscv_pmu_icount_update_priv(env, newpriv, new_virt); +} + int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) { uint32_t ctr_idx; diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 7c0ad661e0..ca40cfeed6 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -34,5 +34,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name); int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); +void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, + bool new_virt); #endif /* RISCV_PMU_H */ From 46023470e014234f3e15ec4497e003550bd7da0d Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:11 -0700 Subject: [PATCH 2206/2884] target/riscv: Save counter values during countinhibit update Currently, if a counter monitoring cycle/instret is stopped via mcountinhibit we just update the state while the value is saved during the next read. This is not accurate as the read may happen many cycles after the counter is stopped. Ideally, the read should return the value saved when the counter is stopped. Thus, save the value of the counter during the inhibit update operation and return that value during the read if corresponding bit in mcountihibit is set. Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Message-ID: <20240711-smcntrpmf_v7-v8-8-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 1 - target/riscv/csr.c | 34 ++++++++++++++++++++++------------ target/riscv/machine.c | 5 ++--- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f515ad072b..093c86b8b9 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -176,7 +176,6 @@ typedef struct PMUCTRState { target_ulong mhpmcounter_prev; /* Snapshort value of a counter in RV32 */ target_ulong mhpmcounterh_prev; - bool started; /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt trigger */ target_ulong irq_overflow_left; } PMUCTRState; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 85d3f0aa3f..b7a24f9c60 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1131,17 +1131,11 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, if (get_field(env->mcountinhibit, BIT(ctr_idx))) { /* - * Counter should not increment if inhibit bit is set. We can't really - * stop the icount counting. Just return the counter value written by - * the supervisor to indicate that counter was not incremented. + * Counter should not increment if inhibit bit is set. Just return the + * current counter value. */ - if (!counter->started) { - *val = ctr_val; - return RISCV_EXCP_NONE; - } else { - /* Mark that the counter has been stopped */ - counter->started = false; - } + *val = ctr_val; + return RISCV_EXCP_NONE; } /* @@ -2183,9 +2177,25 @@ static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, /* Check if any other counter is also monitoring cycles/instructions */ for (cidx = 0; cidx < RV_MAX_MHPMCOUNTERS; cidx++) { - if (!get_field(env->mcountinhibit, BIT(cidx))) { counter = &env->pmu_ctrs[cidx]; - counter->started = true; + if (get_field(env->mcountinhibit, BIT(cidx)) && (val & BIT(cidx))) { + /* + * Update the counter value for cycle/instret as we can't stop the + * host ticks. But we should show the current value at this moment. + */ + if (riscv_pmu_ctr_monitor_cycles(env, cidx) || + riscv_pmu_ctr_monitor_instructions(env, cidx)) { + counter->mhpmcounter_val = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, false) - + counter->mhpmcounter_prev + + counter->mhpmcounter_val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + counter->mhpmcounterh_val = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, true) - + counter->mhpmcounterh_prev + + counter->mhpmcounterh_val; + } + } } } diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 76f2150f78..492c2c6d9d 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -320,15 +320,14 @@ static bool pmu_needed(void *opaque) static const VMStateDescription vmstate_pmu_ctr_state = { .name = "cpu/pmu", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = pmu_needed, .fields = (const VMStateField[]) { VMSTATE_UINTTL(mhpmcounter_val, PMUCTRState), VMSTATE_UINTTL(mhpmcounterh_val, PMUCTRState), VMSTATE_UINTTL(mhpmcounter_prev, PMUCTRState), VMSTATE_UINTTL(mhpmcounterh_prev, PMUCTRState), - VMSTATE_BOOL(started, PMUCTRState), VMSTATE_END_OF_LIST() } }; From 8cff74c26dbdfc746d8f0165c233be3d396d4572 Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:12 -0700 Subject: [PATCH 2207/2884] target/riscv: Enforce WARL behavior for scounteren/hcounteren scounteren/hcountern are also WARL registers similar to mcountern. Only set the bits for the available counters during the write to preserve the WARL behavior. Signed-off-by: Atish Patra <atishp@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240711-smcntrpmf_v7-v8-9-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index b7a24f9c60..d6c5b73afd 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3063,7 +3063,11 @@ static RISCVException read_scounteren(CPURISCVState *env, int csrno, static RISCVException write_scounteren(CPURISCVState *env, int csrno, target_ulong val) { - env->scounteren = val; + RISCVCPU *cpu = env_archcpu(env); + + /* WARL register - disable unavailable counters */ + env->scounteren = val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_TM | + COUNTEREN_IR); return RISCV_EXCP_NONE; } @@ -3722,7 +3726,11 @@ static RISCVException read_hcounteren(CPURISCVState *env, int csrno, static RISCVException write_hcounteren(CPURISCVState *env, int csrno, target_ulong val) { - env->hcounteren = val; + RISCVCPU *cpu = env_archcpu(env); + + /* WARL register - disable unavailable counters */ + env->hcounteren = val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_TM | + COUNTEREN_IR); return RISCV_EXCP_NONE; } From 22c721c34c1609b5ac53dfc0d34125ec479205a0 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal <rkanwal@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:13 -0700 Subject: [PATCH 2208/2884] target/riscv: Start counters from both mhpmcounter and mcountinhibit Currently we start timer counter from write_mhpmcounter path only without checking for mcountinhibit bit. This changes adds mcountinhibit check and also programs the counter from write_mcountinhibit as well. When a counter is stopped using mcountinhibit we simply update the value of the counter based on current host ticks and save it for future reads. We don't need to disable running timer as pmu_timer_trigger_irq will discard the interrupt if the counter has been inhibited. Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20240711-smcntrpmf_v7-v8-10-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 75 ++++++++++++++++++++++++++++++++-------------- target/riscv/pmu.c | 3 +- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index d6c5b73afd..bb6ac33ac2 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1077,8 +1077,9 @@ static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, uint64_t mhpmctr_val = val; counter->mhpmcounter_val = val; - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + if (!get_field(env->mcountinhibit, BIT(ctr_idx)) && + (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) { counter->mhpmcounter_prev = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx, false); if (ctr_idx > 2) { @@ -1106,8 +1107,9 @@ static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, counter->mhpmcounterh_val = val; mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + if (!get_field(env->mcountinhibit, BIT(ctr_idx)) && + (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) { counter->mhpmcounterh_prev = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx, true); if (ctr_idx > 2) { @@ -2170,31 +2172,60 @@ static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, int cidx; PMUCTRState *counter; RISCVCPU *cpu = env_archcpu(env); + uint32_t present_ctrs = cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_IR; + target_ulong updated_ctrs = (env->mcountinhibit ^ val) & present_ctrs; + uint64_t mhpmctr_val, prev_count, curr_count; /* WARL register - disable unavailable counters; TM bit is always 0 */ - env->mcountinhibit = - val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_IR); + env->mcountinhibit = val & present_ctrs; /* Check if any other counter is also monitoring cycles/instructions */ for (cidx = 0; cidx < RV_MAX_MHPMCOUNTERS; cidx++) { - counter = &env->pmu_ctrs[cidx]; - if (get_field(env->mcountinhibit, BIT(cidx)) && (val & BIT(cidx))) { - /* - * Update the counter value for cycle/instret as we can't stop the - * host ticks. But we should show the current value at this moment. - */ - if (riscv_pmu_ctr_monitor_cycles(env, cidx) || - riscv_pmu_ctr_monitor_instructions(env, cidx)) { - counter->mhpmcounter_val = - riscv_pmu_ctr_get_fixed_counters_val(env, cidx, false) - - counter->mhpmcounter_prev + - counter->mhpmcounter_val; + if (!(updated_ctrs & BIT(cidx)) || + (!riscv_pmu_ctr_monitor_cycles(env, cidx) && + !riscv_pmu_ctr_monitor_instructions(env, cidx))) { + continue; + } + + counter = &env->pmu_ctrs[cidx]; + + if (!get_field(env->mcountinhibit, BIT(cidx))) { + counter->mhpmcounter_prev = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, false); + if (riscv_cpu_mxl(env) == MXL_RV32) { + counter->mhpmcounterh_prev = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, true); + } + + if (cidx > 2) { + mhpmctr_val = counter->mhpmcounter_val; if (riscv_cpu_mxl(env) == MXL_RV32) { - counter->mhpmcounterh_val = - riscv_pmu_ctr_get_fixed_counters_val(env, cidx, true) - - counter->mhpmcounterh_prev + - counter->mhpmcounterh_val; + mhpmctr_val = mhpmctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); } + riscv_pmu_setup_timer(env, mhpmctr_val, cidx); + } + } else { + curr_count = riscv_pmu_ctr_get_fixed_counters_val(env, cidx, false); + + mhpmctr_val = counter->mhpmcounter_val; + prev_count = counter->mhpmcounter_prev; + if (riscv_cpu_mxl(env) == MXL_RV32) { + uint64_t tmp = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, true); + + curr_count = curr_count | (tmp << 32); + mhpmctr_val = mhpmctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + prev_count = prev_count | + ((uint64_t)counter->mhpmcounterh_prev << 32); + } + + /* Adjust the counter for later reads. */ + mhpmctr_val = curr_count - prev_count + mhpmctr_val; + counter->mhpmcounter_val = mhpmctr_val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + counter->mhpmcounterh_val = mhpmctr_val >> 32; } } } diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index ac648cff8d..63420d9f36 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -285,8 +285,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) } ctr_idx = GPOINTER_TO_UINT(value); - if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || - get_field(env->mcountinhibit, BIT(ctr_idx))) { + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { return -1; } From 74112400df659b0edb1da35db5c948313ffeffd0 Mon Sep 17 00:00:00 2001 From: Rajnesh Kanwal <rkanwal@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:14 -0700 Subject: [PATCH 2209/2884] target/riscv: More accurately model priv mode filtering. In case of programmable counters configured to count inst/cycles we often end-up with counter not incrementing at all from kernel's perspective. For example: - Kernel configures hpm3 to count instructions and sets hpmcounter to -10000 and all modes except U mode are inhibited. - In QEMU we configure a timer to expire after ~10000 instructions. - Problem is, it's often the case that kernel might not even schedule Umode task and we hit the timer callback in QEMU. - In the timer callback we inject the interrupt into kernel, kernel runs the handler and reads hpmcounter3 value. - Given QEMU maintains individual counters to count for each privilege mode, and given umode never ran, the umode counter didn't increment and QEMU returns same value as was programmed by the kernel when starting the counter. - Kernel checks for overflow using previous and current value of the counter and reprograms the counter given there wasn't an overflow as per the counter value. (Which itself is a problem. We have QEMU telling kernel that counter3 overflowed but the counter value returned by QEMU doesn't seem to reflect that.). This change makes sure that timer is reprogrammed from the handler if the counter didn't overflow based on the counter value. Second, this change makes sure that whenever the counter is read, it's value is updated to reflect the latest count. Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20240711-smcntrpmf_v7-v8-11-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/csr.c | 5 ++++- target/riscv/pmu.c | 30 +++++++++++++++++++++++++++--- target/riscv/pmu.h | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index bb6ac33ac2..781ef27eba 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1039,6 +1039,9 @@ static target_ulong riscv_pmu_ctr_get_fixed_counters_val(CPURISCVState *env, goto done; } + /* Update counter before reading. */ + riscv_pmu_update_fixed_ctrs(env, env->priv, env->virt_enabled); + if (!(cfg_val & MCYCLECFG_BIT_MINH)) { curr_val += counter_arr[PRV_M]; } @@ -1122,7 +1125,7 @@ static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, +RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, bool upper_half, uint32_t ctr_idx) { PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 63420d9f36..a4729f6c53 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -425,6 +425,8 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, target_ulong *mhpmevent_val; uint64_t of_bit_mask; int64_t irq_trigger_at; + uint64_t curr_ctr_val, curr_ctrh_val; + uint64_t ctr_val; if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { @@ -454,6 +456,26 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, return; } + riscv_pmu_read_ctr(env, (target_ulong *)&curr_ctr_val, false, ctr_idx); + ctr_val = counter->mhpmcounter_val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + riscv_pmu_read_ctr(env, (target_ulong *)&curr_ctrh_val, true, ctr_idx); + curr_ctr_val = curr_ctr_val | (curr_ctrh_val << 32); + ctr_val = ctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + } + + /* + * We can not accommodate for inhibited modes when setting up timer. Check + * if the counter has actually overflowed or not by comparing current + * counter value (accommodated for inhibited modes) with software written + * counter value. + */ + if (curr_ctr_val >= ctr_val) { + riscv_pmu_setup_timer(env, curr_ctr_val, ctr_idx); + return; + } + if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { /* Generate interrupt only if OF bit is clear */ if (!(*mhpmevent_val & of_bit_mask)) { @@ -475,7 +497,7 @@ void riscv_pmu_timer_cb(void *priv) int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) { - uint64_t overflow_delta, overflow_at; + uint64_t overflow_delta, overflow_at, curr_ns; int64_t overflow_ns, overflow_left = 0; RISCVCPU *cpu = env_archcpu(env); PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; @@ -506,8 +528,10 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) } else { return -1; } - overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - overflow_ns; + curr_ns = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + overflow_at = curr_ns + overflow_ns; + if (overflow_at <= curr_ns) + overflow_at = UINT64_MAX; if (overflow_at > INT64_MAX) { overflow_left += overflow_at - INT64_MAX; diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index ca40cfeed6..3853d0e262 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -36,5 +36,7 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, bool new_virt); +RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, + bool upper_half, uint32_t ctr_idx); #endif /* RISCV_PMU_H */ From dd4c123636dbdabd7dcc4f76c97af90271cd5fb6 Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:15 -0700 Subject: [PATCH 2210/2884] target/riscv: Do not setup pmu timer if OF is disabled The timer is setup function is invoked in both hpmcounter write and mcountinhibit write path. If the OF bit set, the LCOFI interrupt is disabled. There is no benefitting in setting up the qemu timer until LCOFI is cleared to indicate that interrupts can be fired again. Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Atish Patra <atishp@rivosinc.com> Message-ID: <20240711-smcntrpmf_v7-v8-12-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/pmu.c | 56 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index a4729f6c53..3cc0b3648c 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -416,14 +416,49 @@ int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, return 0; } +static bool pmu_hpmevent_is_of_set(CPURISCVState *env, uint32_t ctr_idx) +{ + target_ulong mhpmevent_val; + uint64_t of_bit_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevent_val = env->mhpmeventh_val[ctr_idx]; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpmevent_val = env->mhpmevent_val[ctr_idx]; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + return get_field(mhpmevent_val, of_bit_mask); +} + +static bool pmu_hpmevent_set_of_if_clear(CPURISCVState *env, uint32_t ctr_idx) +{ + target_ulong *mhpmevent_val; + uint64_t of_bit_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpmevent_val = &env->mhpmevent_val[ctr_idx]; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + if (!get_field(*mhpmevent_val, of_bit_mask)) { + *mhpmevent_val |= of_bit_mask; + return true; + } + + return false; +} + static void pmu_timer_trigger_irq(RISCVCPU *cpu, enum riscv_pmu_event_idx evt_idx) { uint32_t ctr_idx; CPURISCVState *env = &cpu->env; PMUCTRState *counter; - target_ulong *mhpmevent_val; - uint64_t of_bit_mask; int64_t irq_trigger_at; uint64_t curr_ctr_val, curr_ctrh_val; uint64_t ctr_val; @@ -439,12 +474,9 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, return; } - if (riscv_cpu_mxl(env) == MXL_RV32) { - mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; - of_bit_mask = MHPMEVENTH_BIT_OF; - } else { - mhpmevent_val = &env->mhpmevent_val[ctr_idx]; - of_bit_mask = MHPMEVENT_BIT_OF; + /* Generate interrupt only if OF bit is clear */ + if (pmu_hpmevent_is_of_set(env, ctr_idx)) { + return; } counter = &env->pmu_ctrs[ctr_idx]; @@ -477,9 +509,7 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, } if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { - /* Generate interrupt only if OF bit is clear */ - if (!(*mhpmevent_val & of_bit_mask)) { - *mhpmevent_val |= of_bit_mask; + if (pmu_hpmevent_set_of_if_clear(env, ctr_idx)) { riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); } } @@ -502,7 +532,9 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) RISCVCPU *cpu = env_archcpu(env); PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; - if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { + /* No need to setup a timer if LCOFI is disabled when OF is set */ + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf || + pmu_hpmevent_is_of_set(env, ctr_idx)) { return -1; } From 6f6592d62ebaffff353bdd27ec4480972695d24b Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Thu, 11 Jul 2024 15:31:16 -0700 Subject: [PATCH 2211/2884] target/riscv: Expose the Smcntrpmf config Create a new config for Smcntrpmf extension so that it can be enabled/ disabled from the qemu commandline. Signed-off-by: Atish Patra <atishp@rivosinc.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240711-smcntrpmf_v7-v8-13-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 4efe7ee3b0..a90808a3ba 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1472,6 +1472,7 @@ const char *riscv_get_misa_ext_description(uint32_t bit) const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { /* Defaults for standard extensions */ MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), + MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false), MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true), MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), From 38c83e8d3a333b8b377367756a2b6c700c7d0084 Mon Sep 17 00:00:00 2001 From: Yu-Ming Chang <yumin686@andestech.com> Date: Fri, 8 Mar 2024 15:48:03 +0800 Subject: [PATCH 2212/2884] target/riscv: raise an exception when CSRRS/CSRRC writes a read-only CSR Both CSRRS and CSRRC always read the addressed CSR and cause any read side effects regardless of rs1 and rd fields. Note that if rs1 specifies a register holding a zero value other than x0, the instruction will still attempt to write the unmodified value back to the CSR and will cause any attendant side effects. So if CSRRS or CSRRC tries to write a read-only CSR with rs1 which specifies a register holding a zero value, an illegal instruction exception should be raised. Signed-off-by: Yu-Ming Chang <yumin686@andestech.com> Signed-off-by: Alvin Chang <alvinga@andestech.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <172100444279.18077.6893072378718059541-0@git.sr.ht> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/cpu.h | 4 +++ target/riscv/csr.c | 57 ++++++++++++++++++++++++++++++++++++---- target/riscv/op_helper.c | 6 ++--- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 093c86b8b9..1619c3acb6 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -751,6 +751,8 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, void riscv_cpu_update_mask(CPURISCVState *env); bool riscv_cpu_is_32bit(RISCVCPU *cpu); +RISCVException riscv_csrr(CPURISCVState *env, int csrno, + target_ulong *ret_value); RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); @@ -783,6 +785,8 @@ typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, target_ulong new_value, target_ulong write_mask); +RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, + Int128 *ret_value); RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, Int128 *ret_value, Int128 new_value, Int128 write_mask); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 781ef27eba..ea3560342c 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -4624,7 +4624,7 @@ static RISCVException rmw_seed(CPURISCVState *env, int csrno, static inline RISCVException riscv_csrrw_check(CPURISCVState *env, int csrno, - bool write_mask) + bool write) { /* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */ bool read_only = get_field(csrno, 0xC00) == 3; @@ -4646,7 +4646,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, } /* read / write check */ - if (write_mask && read_only) { + if (write && read_only) { return RISCV_EXCP_ILLEGAL_INST; } @@ -4733,11 +4733,22 @@ static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +RISCVException riscv_csrr(CPURISCVState *env, int csrno, + target_ulong *ret_value) +{ + RISCVException ret = riscv_csrrw_check(env, csrno, false); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + return riscv_csrrw_do64(env, csrno, ret_value, 0, 0); +} + RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { - RISCVException ret = riscv_csrrw_check(env, csrno, write_mask); + RISCVException ret = riscv_csrrw_check(env, csrno, true); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -4785,13 +4796,45 @@ static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, + Int128 *ret_value) +{ + RISCVException ret; + + ret = riscv_csrrw_check(env, csrno, false); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + if (csr_ops[csrno].read128) { + return riscv_csrrw_do128(env, csrno, ret_value, + int128_zero(), int128_zero()); + } + + /* + * Fall back to 64-bit version for now, if the 128-bit alternative isn't + * at all defined. + * Note, some CSRs don't need to extend to MXLEN (64 upper bits non + * significant), for those, this fallback is correctly handling the + * accesses + */ + target_ulong old_value; + ret = riscv_csrrw_do64(env, csrno, &old_value, + (target_ulong)0, + (target_ulong)0); + if (ret == RISCV_EXCP_NONE && ret_value) { + *ret_value = int128_make64(old_value); + } + return ret; +} + RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, Int128 *ret_value, Int128 new_value, Int128 write_mask) { RISCVException ret; - ret = riscv_csrrw_check(env, csrno, int128_nz(write_mask)); + ret = riscv_csrrw_check(env, csrno, true); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -4830,7 +4873,11 @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, #if !defined(CONFIG_USER_ONLY) env->debugger = true; #endif - ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); + if (!write_mask) { + ret = riscv_csrr(env, csrno, ret_value); + } else { + ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); + } #if !defined(CONFIG_USER_ONLY) env->debugger = false; #endif diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index ec1408ba0f..25a5263573 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -51,7 +51,7 @@ target_ulong helper_csrr(CPURISCVState *env, int csr) } target_ulong val = 0; - RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); + RISCVException ret = riscv_csrr(env, csr, &val); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -84,9 +84,7 @@ target_ulong helper_csrrw(CPURISCVState *env, int csr, target_ulong helper_csrr_i128(CPURISCVState *env, int csr) { Int128 rv = int128_zero(); - RISCVException ret = riscv_csrrw_i128(env, csr, &rv, - int128_zero(), - int128_zero()); + RISCVException ret = riscv_csrr_i128(env, csr, &rv); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); From 38facfa84328e4ee5a80f7082a01e10c768ee50a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Date: Mon, 15 Jul 2024 06:04:55 -0300 Subject: [PATCH 2213/2884] hw/riscv/virt.c: re-insert and deprecate 'riscv,delegate' Commit b1f1e9dcfa renamed 'riscv,delegate' to 'riscv,delegation' since it is the correct name as per dt-bindings, and the absence of the correct name will result in validation fails when dumping the dtb and using dt-validate. But this change has a side-effect: every other firmware available that is AIA capable is using 'riscv,delegate', and it will fault/misbehave if this property isn't present. The property was added back in QEMU 7.0, meaning we have 2 years of firmware development using the wrong property. Re-introducing 'riscv,delegate' while keeping 'riscv,delegation' allows older firmwares to keep booting with the 'virt' machine. 'riscv,delegate' is then marked for future deprecation with its use being discouraged from now on. Cc: Conor Dooley <conor@kernel.org> Cc: Anup Patel <apatel@ventanamicro.com> Fixes: b1f1e9dcfa ("hw/riscv/virt.c: aplic DT: rename prop to 'riscv, delegation'") Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240715090455.145888-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- docs/about/deprecated.rst | 11 +++++++++++ hw/riscv/virt.c | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 20b7a17cf0..88f0f03786 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -479,6 +479,17 @@ versions, aliases will point to newer CPU model versions depending on the machine type, so management software must resolve CPU model aliases before starting a virtual machine. +RISC-V "virt" board "riscv,delegate" DT property (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The "riscv,delegate" DT property was added in QEMU 7.0 as part of +the AIA APLIC support. The property changed name during the review +process in Linux and the correct name ended up being +"riscv,delegation". Changing the DT property name will break all +available firmwares that are using the current (wrong) name. The +property is kept as is in 9.1, together with "riscv,delegation", to +give more time for firmware developers to change their code. + Migration --------- diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index bc0893e087..9981e0f6c9 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -651,6 +651,15 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, qemu_fdt_setprop_cells(ms->fdt, aplic_name, "riscv,delegation", aplic_child_phandle, 0x1, VIRT_IRQCHIP_NUM_SOURCES); + /* + * DEPRECATED_9.1: Compat property kept temporarily + * to allow old firmwares to work with AIA. Do *not* + * use 'riscv,delegate' in new code: use + * 'riscv,delegation' instead. + */ + qemu_fdt_setprop_cells(ms->fdt, aplic_name, "riscv,delegate", + aplic_child_phandle, 0x1, + VIRT_IRQCHIP_NUM_SOURCES); } riscv_socket_fdt_write_id(ms, aplic_name, socket); From daff9f7f7a457f78ce455e6abf19c2a37dfe7630 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Date: Mon, 15 Jul 2024 14:15:21 -0300 Subject: [PATCH 2214/2884] roms/opensbi: Update to v1.5 Update OpenSBI and the pre-built opensbi32 and opensbi64 images to v1.5. The following commits were included in v1.5: 455de67 include: Bump-up version to 1.5 23b7bad lib: sbi: check incoming dbtr shmem address 0e45b63 docs: Fix wrong filename caae2f7 lib: sbi: fwft: return SBI_EINVAL rather than SBI_ERR_INVALID_PARAM e8717d1 lib: sbi: fwft: check feature value to be exactly 1 or 0 ecef14d lib: sbi: implement SBI FWFT extension e9ee967 lib: sbi: fwft: add support for SBI_FWFT_PTE_AD_HW_UPDATING c97a1d5 lib: sbi: fwft: add support for SBI_FWFT_MISALIGNED_EXC_DELEG aa5a859 lib: sbi: add support for firmware features extension 53844c9 lib: sbi: Add support for Svade and Svadu extensions 52dcf35 platform: generic: andes: Add support for RV32 to set up PMA f09f164 platform: generic: andes: Refine Andes PMA related code 7830e98 lib: serial: fix RX path in litex-uart 62e178a lib: utils/reset: Try initializing all reset devices in dt 3a94a32 sbi: sbi_domain_context: Fix trap context for domain context switching a73ff04 lib: utils/reset: Fix fdt_reset to search for more dt nodes b5c984b lib: utils/reset: Skip initialize reset when dt is not enabled 86bbe6c lib: utils/serial: Fix fdt_serial to match more dt nodes 179e00a lib: utils/serial: Skip initialize serial when dt is not enabled b1c7c75 lib: utils/irqchip: Skip initialize irqchip when dt is not enabled 5e3ad7d lib: utils/timer: Skip initialize timer when dt is not enabled c5be0e1 lib: utils/ipi: Skip initialize ipi when dt is not enabled df3db6a lib: utils/fdt: Fix DT property for APLIC delegation d962db2 lib: utils/gpio: respect flag GPIO_FLAG_ACTIVE_LOW ae5ef18 lib: sbi: sse: handle missing writable attributes 858754a lib: utils/irqchip: Add sanity checks in imsic_get_data() and imsic_get_target_file() 96f0a2e firmware: Bring back FW_TEXT_START as an optional parameter e3a30a2 lib: utils/irqchip: Check before initializing imsic 2bed4c1 platform: generic: thead: add Sophgo CV18XX/SG200X series 533067d lib: sbi: Put event after use in sbi_sse_exit() loop ea9cf6a utils/reset: Add SG2042 hwmon MCU support. 1cb792d lib: sbi: simplify inline function in sbi_dtbr.c 7b37da3 lib: sbi: fix return type of sbi_dbtr_shmem_disabled e065c3c lib: sbi: Fixed memory permission check in sbi_dbtr_setup_shmem 7f54527 lib: sbi: fix DBTR_SHMEM_MAKE_PHYS for RV64 744f495 lib: sbi: Removal unnecessary check dbtr_thishart_state_ptr 4953bd7 lib: sbi: fix hart_shmem_base 019a8e6 platform: generic: thead: add Canaan Kendryte K230 33e21c9 platform: generic: thead: separate T-Head PMU Errata 2b93ce0 platform: andes: Change all occurrences of andes45 to andes f68b3ae platform: andes: Rename files with the prefix andes45 17e8291 sbi: sbi_domain_context: Add spinlock for updating domain assigned_harts 1d89a9d lib: sbi: Refine the settings for switching to Virtual Supervisor Mode. 033104d lib: sbi: sse: check handler entry to belong to supervisor mode bd00765 lib: sbi: sse: use PRV_S instead of hardcoded value for mode ce3c82c lib: sbi: sse: call enable callback before sending IPI d528dbf lib: sbi: sse: remove superfluous sbi_list_empty() check 22ff750 lib: sbi: sse: simplify 32bits overflow check 7aa80ea lib: sbi: sse: rename sse_hart_unlock() to sse_enabled_event_unlock() c21c99d lib: sbi: sse: fix typos, comments and spacing errors 7b1ed96 lib: tests: Add test for spinlocks 7bdf41a lib: tests: Add test for atomic_t f6243d9 lib: tests: Add test suite init function d4d2582 firmware: remove FW_TEXT_START 73344d4 lib: utils: check correct value in fdt_node_offset_by_compatible 37e1544 lib: sbi: sse_event_get() may return NULL 68bc031 lib: sbi: Add missing sscrind and sscfg extensions in sbi_hart_ext[] a7c5c2c Makefile: Remove unnecessary dependencies 268feab Makefile: Respect manual changes to .config 29ecda9 sbi: sbi_domain_context: Check privilege spec version before accessing S-mode CSRs 7862c24 lib: sbi: Wakeup non-coldboot HARTs early in the coldboot path beb0cd1 lib: sbi: Simplify wait_for_coldboot() implementation f5375bc platform: generic: allwinner: Optimize current hart scratch access b94396c lib: utils/timer: Optimize current hart scratch access 5c9a735 include: sbi: Support byteorder macros in assembly 06fc453 lib: sbi: Add SSE support for PMU events 09ad214 lib: sbi: Implement SBI SSE extension c8cdf01 lib: sbi: Add support for Supervisor Software Events extension 76d7e9b firmware: remove copy-base relocation 5186da6 platform: generic: allwinner: sun20i-d1: Remove duplicated CSR definitions 3b2f89e docs: writing_tests: Make docs correspond the latest changes f7d0050 lib: sbi: Extend sbi_trap_error() to dump state in a nested trap 5b11f16 lib: sbi: Pass trap context pointer to sbi_ecall_handler() 43d346c lib: sbi: Remove regs parameter from trap irq handling functions d84e7eb lib: sbi: Remove regs paramter of sbi_irqchip_process() f414cf9 lib: sbi: Simplify parameters of sbi_illegal_insn_handler() fea33a9 lib: sbi: Simplify parameters of misaligned and access fault handlers abea949 lib: sbi: Introduce trap context 60ffc15 include: sbi: Add trap_context pointer in struct sbi_scratch ebb697a lib: sbi: Remove sbi_trap_exit() and related code 2e85178 lib: sbi: Remove epc from struct sbi_trap_info 86224ec docs/writing_tests: Update tests paths 5c992a1 lib: tests: Move tests to a separate directory 81e3ba7 lib: sbi: call platform load/store emulators ddf3b64 include: sbi: add emulate_load/store handler to platform ops 4c11265 lib: sbi: abstract out insn decoding to unify mem fault handlers 9221fe5 lib: sbi: change prototype of sbi_misaligned_load/store_handler a17600c lib: sbi: change prototype of sbi_trap_redirect 2471cf2 include: sbi: rename sbi_misaligned_ldst.h to sbi_trap_ldst.h c0a6320 lib: sbi: rename sbi_misaligned_ldst.c to sbi_trap_ldst.c e11025c lib: sbi: Add initial domain context management support 87d8fe7 lib: tests: Add sbi_console test e5f53fd lib: tests: Add a test for sbi_bitmap 874fcef lib: Add SBIUnit testing macros and functions b9e4de0 docs: Add documentation about tests and SBIUnit 526b9ce firmware: fw_base.S: fix _reset_regs 8151105 firmware: fw_base.S: Remove _relocate_lottery 187397f firmware: fw_dynamic.S: Remove _bad_dynamic_info b27b7c6 firmware: fw_base: Simplified setup trap handler fdf5589 firmware: fw_base.S: Simplify address get 748bef1 lib: sbi_misaligned_ldst: Add handling of C.LHU/C.LH and C.SH bc36678 platform: andes: Drop andes_pmu_setup() 6bb6b61 lib: sbi: Add support for smcsrind and smcdeleg 322b598 lib: sbi_hsm: Restor hart state to stop when fails to start 96a35db docs/firmware: document new options for jump and payload firmwares 2cff7f3 platform: Apply relocatable address f056939 firmware: Add relocatable FW_PAYLOAD_FDT_ADDR 7227cdd firmware: Add relocatable FW_JUMP_ADDR and FW_JUMP_FDT_ADDR 741e941 platform: starfive: call starfive_jh7110_inst_init() in pm_reset_init() 3edf044 platform: starfive: return error if needed devices are not present 80ae046 platform: starfive: rename "stf,axp15060-regulator" -> "x-powers,axp15060" 5335340 platform: starfive: remove redundant compatibility check in pmic_ops 4d8569d platform: starfive: get I2C offset address from clocks property 034af1f platform: starfive: correct system clock device tree node 88273fe lib: sbi_pmu: Before using we should ensure PMU init done 46c8c65 docs: move documentation of system suspend test. 8df836d platform: generic: Parse system suspend test from config node. 23e7e48 docs: Add OpenSBI DT configuration guide. 67ce5a7 platform: generic: Add support for specify coldboot harts in DT 9c8b18e firmware: fw_base.S: remove _runtime_offset 4c6b7cb firmware: fw_base.S: Improve loading u32 92e8aff firmware: always create dynsym section d1dad07 Makefile: check for --exclude-libs 4a76f79 Makefile: don't pass -mstrict-align if not supported 21caaa3 fw_base.S: Fix comment errors 1ec353d lib: sbi: Use mask to check the free bit during trigger allocation bb90a9e lib: sbi: Print number of debug triggers found 76a2a15 lib: sbi: Implement SBI debug trigger extension fa87ec9 include: sbi: Add SBI debug trigger extension related defines 97f234f lib: sbi: Introduce the SBI debug triggers extension support 40dac6b lib: sbi: Detect support of debug triggers 2499769 include: sbi: Introduce debug trigger register encodings 20ca19a include: sbi: Add TINFO debug trigger CSR b752099 include: sbi: Introduce common endianess conversion macro Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240715171521.179517-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- .../opensbi-riscv32-generic-fw_dynamic.bin | Bin 267416 -> 268312 bytes .../opensbi-riscv64-generic-fw_dynamic.bin | Bin 270808 -> 272504 bytes roms/opensbi | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index 60ca1165c820ff29b984566db22b9453541da19a..7ec260ff4047d11f7683c9a0b2fed73e1996cd75 100644 GIT binary patch delta 73365 zcmZ^M3s_Uf67V@mPEHa)g8?EciUbmguP7?Dtr~<70kx>$1KavI6|T0mSZb~9^#%+l zAs_`@G16j-ULl&cmdaIbtrSoZ!56i@uUBa!QnebaB7&m-Y)%lo|Nr}aAt$@Dv$M0a zv$Hd^$8CG0+sdV0VFHS%$fSrcv5+Vz@ESPLhZ-pt2?&`etDBuMcEmt2GgvetW4fGA z5Hfz`(+d7Pt#v$)KpQtckw@}y*&=3R!QX>sXJ}_B<Z5Uskvz602?&%eL5e^L;Vp5@ zN_sArKzqq;O`0@GBZpd<#NKXM6-FkJeJQB02#GKWP1Gt7j|}B6jUR%>kP5{d<4Bs~ zkJ3}dQD#a$7h3an-mNg_@TYE;djVdrQ-7k5NcDL7b_IP;s|fs^Pz3!&SZN@_T>nQ= zo+9`!LZRGADn@L6sk_l5Q4=|YH&KD^kpYI0iZMHhFiJpFumU1XC?@1I;HFcgfRHB# zWHOqLQUu{=TwX6%1pm%cD0w`^2p+HRD9#U8sCV-EVj7AZv(oPWQY)j|`uBxgIXsLH zym$49HuF}$%AD&@X!wf2c?4Br0j5Kze8sPP1)r!m!sqpT9BBEo46W~&G^tj@5!M=p zUgeMUnERQSW6#D@D3u=+T`zo~l4`aa*&ItfN408Tnsk9CErgiRsSMdo5;B4ytk4ZI z8b=y;MpDLq@~H}Ag|@;tk0+mpyXV)VHhvtvGw6{l<8V8gE=U;iASiHxU;Tq1%>=&& z-U;5RjOl)7!YGn(#%opRE5SxSU`71}qh2(cOD!DJx6y;3@(F$?9t4$4@H_P&h!}^? zE+^1m1Bf0pOJ#uO6QmYOkyhx7770~@Frk%+(_;|ZK0!3yF>L=?i*Ckr$BELj=qusC zH|cGjM>tm4tUl2`?q`)X;l;CUt52-Y5wSjDI&=Ic%q^ZzTVVo}ZA{0=<uD5}ibbM` zzQ5}>iz3ip!k{RCAkv8V0Uxw2X~fU?-v^r1cX87XG);fwzVqMXzNKp>t_eCNJfl1j z+c3L+Zr%JNNwtJG+DL~R*w%qwdWLN!a?d!Mq|2F2;(<n+SXdmHQLkh-S|``CI(Y~t zCB)=RE%yt!g=?=yG8y#1jaH@~<#(%6M#WRkrRBk4ua*!@w%7+lY>Q<2(*u+&&&v2y zL0p@nBO9%Ax~-uwy%tNl(Pkm~69x@0<4#3A&EtM*c<uj53(?+p21;i+^P3XNSZ8z1 zD~>4YQGuQjv=33T&cM_-Cz1aKpd|R}7~T&T`L-Bh@~@57ZINVj8B5XIj>JN%k~ptU zMl2zWDv7XIpHaWj>hPxi%Fsbwd1{nnFxBRIHfWms9KP-!fq_!TFzTMGB2YWV;qQJX z;@@$|dY(129RbvzE*Ne8D2Ke~dFdO_0asqWbOdy8yQllBT=xU0+qm=Q`Oy9kBM@E2 z#<B)XH>TOyteqsIG{DGsDQ4vRRSsXtA6FwC+vq=Qs~oiCPnZZ_JP~#8J5#m)a16!h zFbAB0ZBraR)D1l9r4$U{cZ}Fs5<d|J@VmDT>LacGnCUB5DoXYcj($`#F3q2=*ty3* zIB2RFGm`@5VPaxkECjZPI|fnL`V&$<t5@vmBp68#GcEO$4%j)Ey2`rPSw0pxcon+L zj}Lc9-KCmTQf(SnYdudAh%LP&uY<)+l`eDUYy8*zgh{Pg4u8uHZ9HJ%CH$DKa}1y^ zxoB?=Om@g6m$2B7m99>t<sYj};!_;Dx?eeTb#FtLKF+4LNiLW`tqi8%oKY3SXLE|2 zzR|!jSGPKVYx3@GBlUK{6H>j}@B%4&nggIdaOO+<JDbYKaX_hKdpp*h?1gFabT*}q zb-`%kJ$qrOMt4W6o%#6^XOm_$2U9Y-7pAVBgJD<4!w5t$0<{aMJdo-IV(WT<fFL0d zG{QZvz}Oy6=#KQlH1UBTC93i8HMp40)^K$?AVf++dxbN#X<=_$_Ghjh5124O{cxi? zhL5@i5zIEpP%K#tEqN7D*#rky2YAx|F7psPn#;<Bgy;(EBvN0$!g94OP+RgTUoD}| zvIchpK%BnbNi_M{S;mW+z?H5@b>;_t;?xoYU8UoVYKk}C#MPBT-O`Vp`KkS(PC{wi zc-8=cYn{#M5?ARMH=Yln!!)O~T<<C!#g$TN&U~%m!wemzb84g`ph`rk-3_3sBsD`v zLQSd?YJ}7XcLz{IeBjhHO@&fVYN#9jBY=P38DEZx@uY^hJ6H=Hyk`u7{n#a26p*WZ z*BBDx{;gZLC&K_ETxATgxQkYN0>4)pRTiQbY{mNRxqC98gA`*(Ll4}i@H?5W7Q<vo z_4aHK3#}~4PN;KWa$b`yC?(FaX#O{Z?1J6hf?W?BW?+H$S7*b5?$0J$KA&r14J+Iu z*Z>HZVYLm_sT#T3be5HaY{)1o9Y(y=J%VX4f+g-%)5_KB(w!ygGENf{z=xX8uxzNa zUD8n`8Q@$R7^lu*-YoGfbI<<@7<6+INDC=HZ@^uwNS8?jkAQ$hSXg4&v#`W#LVE~c zF#!v4O%kqIx-_<@rUh!2&hKrrG_0pa2mGo@098>E|DL^PARB=L^Ke=Ii?TWc6l&+< z!loC6R3>yd2bW1x?B?7PGIPiC&OBD~-)bw&Bf`pcKlfDX=e-}$yJi;)YsoBUlQtE% zOl8!j3q2hJF~m%#G(V+>lv3!mIaYpwBn&cPSth5dS~6W-E_VH0r#3lwR0&r@nK@Ro zMi_zK*%Y{n>%OiZ`xEyR((i`~?uqpZaX+<q4tGE4JX?O?YD+yXyPqhJSw8^w8Cd0} z%UvUA34;+t;8lCYwQ527iF~qW)lw6HrPGRjjPMRR8e1bgraVlD<gi!B#4CkHl^FdZ z^%)+hoo5B}#Aq}By*SeGo9!9igo(Kl$|}dDwsce?9mNw9=(JQmJg|8t2dw(I7q}!2 zfL$o`pCY06I748Kp8HL&DUM_w+6ZS8F^}md=0Tsr;<y4Z6rdB0Rs(;f8X<npPlCTh z&0F|y`YH@}wFj;#H6BAO9Swh_@qGMTz5v5DyV{hb+^>f59V0|(OCu(XkBT6r8`JTC zg;2!mV3qw1?eo^tflFV7bvk|X00kfGQRRSP{J@flXvTn%vZmDGvo7;PqdE~qhyZgs z5sgd(f(D6ySB!Z0xI)rN$UOuEe`MT3D|{QjiF*qj9-!vc%tiMATYBIE<Re?+pH9rN z=Q`HdyNs&L&cga0T+qiR*&tr%S+rZm@SECb)X!%kKd{CZMf;2i)V|ujAfP%!|6?UL zhJaH;64fU5j1_DF#I87kw)jNzT7O1YeTMLkRiQ2)H81)nr1TA*db`pXED@dF!mZ+T zf!Y$Fb4t@k8db3(Oa0Y(MPMgwwDu?BsSF*AX=!S#J=3d}oBhy#p)B7qJk<~A-@fB{ zssrdxUyUDC5ka~*2Ye$N@a!YAc@G-zH>O|KUWO8R$~*XA#hS~|YQI+oX+D$7K#=(V z+@T2kr%0VOrNP?H2a;R<LN$JBUg=lpPd_Cur49MWQ~Y<?{=^g0J1<s1Wwfd8pe=F@ zkNyK4lLzuzThJeJB`?{5qyv|Q1g3({vIuq@vE&q<u^xjpY!W_^!?#Q*d~%w_v!r<g zZ5^ocaINo_4rgHbY;=6!o1PpaEh%XwdJP)tAL3Oa6_dBz0%<_;{v*9g@+n_;iTN{> z<*ykyIoueM+073<+C3k5Rl{p!=fby^hnoC@f=_&8+?_)h?Qw)XbE{~KWuIkI&a0b# z-ZVY?*cbS41IjcU`3?FHCIyu5IcIv)0Q7WF@X$NUjH(j^^I5x;im_)hbcpxc?B4A0 z*#|7n65?$%b#Mf~M7siQ92{YYZ(DwWoy|JqQ<uvq@xRZq%0k$ipuGoC_vEc=WIp$N zjoz`k<FU*l_z=`hyvFl^N;^TE(wTSpfsjIpz637Kf7|i7_|aRH4wLvXwGxTN<z+>) z*$kaCzU@sn{x|#JUtCzZHZiwyMdGL*tF3SlM_Du8;M+u`zhiCU#`FhDVgmj($tsOp zafNT2LE6oZk6NCgr9%c8WbKa#hO89SCd;xOJ+IP*t)ofCe?Lz~*`Dq2V;+|A7!l3q z+l~`OdAI1#>8V>q(`Qc4sySJ+Pa71qIf~R_f#9CkCgNx4iXe3vx;57qE?9QmSbCfg zO|M`pwLI&sN~WchS1IH{kLP(z-gzPdbqwjxTRRzv0+d7NOvY&X$`{Ojxn2{o!U;wr z(ges2B}BCNu3H9P&!~SIiIq@KEBq~9(c2!YNeqk3`pMhkyy`I@isQs72dtTqX&Su- zJ`;4|X@L_pid_OvZV0$F*DLfqA%?3BCOmoh7?S~+Z4&_1i5|t2rot!>T8kqoFuz+p zEz{C86E!Xb6R_a+kfv~<2w1c8<lEOwr^^}}kDoqv_UMJfS8A4DO}?_~(&`K8Qmm9v z&NpL>HV;xU=3iZkQRli8>Kk25Iz0gQ1LZREpq0OeCKy^QHL8w2sZEB>-abraXuar% z>N}oy8;xagWI*0rE3@{ZCt9Z@n6GIcBsuy7)EI9*i`M%Q&eRga20fSsQ8r0L3Zl)= z+7mXsRW(cI+3_6oWLNjhJ-fr!nMsB+dl*%MC!uoYNvo7^J3)lee`w+435T`JCrD@` zKNmJ`Ek0~iMLijN^{`PH<H_p7tgS?1UIutPZh6Xtg9)x%XKMZc=<w9HJiaZC3=_qn zyrC*l#3Xy>`_=6U==9K_ktYaanFsh=1Wzice`{stQnDW%Yvn6w!l=*_KQ{9B3i*tv zpWsqbC>lI0fbt-Wr6<soVZ+`Gsa!A5remtw6DC*xH=(c+GkMjemzn(bHOywrWVsh; zJFi*&jVdovq5M}DlSBKsm<zuTf?P00fe&!D3pfk<0eCyb@Q;iAP)8`iY^4VQC%bxv zRI{{OZ_j1wYy(k;KT-RhBKTn!qK`gN1i`m?)3ddUHzZA&X^B$=KJ4PFW|0+}Mw85= zKSii$6cJ{gKrW0#^J%gwoAK1sOs>g;(?A5lMl=5{6FdkZ#`9ID2*nr)5ZV6c0LFNA z07?xUX4pbWJtHf&mJz2u_c_7`AxM+b0P01E>tBiVKavcGc`}YU|0N02)OFb*GX=bu z1!6TO+3{c7L!&Lzv!I@nQ}2?siyvu|qb@{6#~g@J#%_)MYw_|o&%QbRt$lALvBJ2J zWgF9LQ;@0SIbSL0LeB!n8R817E3zh#ik&k^nfdDT`_Ycpn~ySdGS+jvGrsxncqq9# zI3RpF`Le6@LuY*YXL~9!mAZ=qmn8n0rLABOfksg0MEp0Ta_!;*_)wM07LTibYw?cC zMQe7RJA2Y}q3mp9)9KR}8m}C`cI@|~HxJ*gS$!>ipj<K#=1>xQ$2dYlDuQoxVU9CM z!~;=!8WgDDrc(mbX|&UXBTMqMHpQO1*Rn5Xl7+d^K47nT1F8-frB}wnN?C1ne01?K zS|BDt0h&Mo=7{xS8yX14+&Z3F-Qme>g!Qu^fXL0sjg~E#LO60SJ}lBZZnr)}9fJwR zNKZ%cLGcEMXU9Ddo6hs~Fe9B+dWUbvJ=8Ih$T(wEH4+)oq`t!ZP>c2F1?!e0myVz( zafHL@@n>yHx7--gNHAXAuN#$(u$jhsQu+$hIc=EyB5^rSv9rr_eHF4&<P`m_swsNM zw$?uo{T*m#$Ad?IF#S(Wl3_Adc+kOBdc~NBkEc{(vxPZDYxp6Vo!3}rTi$8dvUHRN zS%ZlSR>e*g2*%M)M<IO!6Ucl)k8u%`>+yR=G#tC4VM}i7e{1yqJ$lFQHU}Q1VvOLi z<4gKaWLE@?mv>H3jC%N(A9Az{PqB1v#4q^Snct>k^~?nu@jyk4$1-nJWfPqVWZVwa zsrWD~HGMoRmJQz$iPb$4nany<1IK|pF8j6%Sw{nL;78ePd(NTLyo$aG4kcN1JXpui z&!38<AtUGHm%qmNQbUX?2~kjg2pl$L?Au@+8ACFk$XLO6zWV4Ba3{W&tAialzwH%A zf9l#<7HUBLnkU!&<QU*})Z$_j9+u+^`ZU@UqVh;31h6hLnOd74`Z;8&ft#jnu(+bS zE*)WN1%fbhHmL|wk+Xh~nS5N!2|7q^_OWL=;=g%}Cy^Q2P4Ho=f72J`@nPZNiSEJB znUL}G%bU11l-tZ}M&L~x3_CCqh!O2SC;|mtX=uALz<`J7NT<t-=Q(oez5G(}>V#x= z@s&|s8?A~lyB<3dsoQn`azPpC!&esr>|qSWS7vq@0jh%imlnJ~6jcLC_7I{npS%t8 z`h6}{)xH4FU+oVYhB!QPA9N<HImB67+)mlLd0U+Ki3bs|Rc?#J1_OD_7!vJiX(4r- z>6fTy-nR)Z>G`|$q~l}yIr38xwSNl}{h{5rmDG0+99}}v4hh|jR;vd23B8QUXfK&r zPE>?<5C^P^5jQ;*YV?)Lk5`wC4ycBE<d?WkBCti9(eEml>5^GUJ>o3`7@_<S_V4b^ z986jNI&5qL+-KlHtmkOSI>LRE-a#jojj_jP9}}J?q}EP=vow4HekoFI$%raCVh6<z zR#pDewIHJBZNzU2((>u?xJ3IAlUPD@!|xQ$jv8?Hk>F<C&;e&&pjkpAvw&1uyXS8? zQb0StYJCR3VKf3?GCCtV|H!!`J7~w3t<MxY1)kV~_)EJy>TJ~Xn0+x6sLYp%Qd{;~ zfPe`oI&|`^uf!sb$e4GnE!f)D9RwRkTBF4ze|MzOniV<jR4*}ZT4;(?_oaoVyks=x ziw=bjF(`s5&o}Ex-OD7%D8koS9zTEdsWg@LK1M#Uc50C#a1XJjQoO=hUY@{_v2vMW z=N@8Q1Na*W%{ayGJ;d-@@HFspj~tp;yrAIBMyuN7-(cPEauWV3hi`BkG=ay$+j1P4 zeTX`PKvC97DC|W<X!WV6T>YtPerXH=UC)tGfwlYHhoXsf3w7e?Av!d_o@COG3H+ns zFK<W$Ova~a@zPI+#H8h`K7CV{mM{O5n^^S7OcdU$6sAA#_FJ2W9?@j(=}I9%Cg#Z= zsR;D(y666fQL>pZ6hN#3-4v1ub+f9gzDTHB3u-W(;N?S8ut#viTaTcj3r$bcH5V>d zu3r;$PI)%ANqB)6xHogZPHh2Y_Y{`lGer=5g5jeSJcHBsLhwWRFkcFMtPf$rGH<+5 ztZlR!cY`UGC-N~G%4Uq~UZ5y>tWoTItYNl82!cA=ouQXM1~DG-M->$*>qqc%OZZ9= z)}~Lx%t9i~Z$5-nq@Siq=WC40qusLkb>yvk%T7NSLrmDBl@-c~sfCZWZkgVX3iCTc z*um0jI;_|wBot-v*$p4N5NxI_4NJ2^gJRbu4U<<d#vSZ6z0?I}E_A_%F7}#6-@_2S z-4lK4Cf{R*whLaa*Y>F&_ZQP|1<y_1Rv>Syi#*}Jt;L4J$N#^yVbUCh^&Q}!wm;Sw zZCM&c*<%f<+ZP{{usMOZNC#^L3tpR;W9>VyX4Sy?L`L1lvU96iMx~DfNmARhR$P9d z)ZQ=FDn^{wgi#=GfxhIj{n!*YU?%~#I`+esq@7CDVjF-voSG+M>%6EQTjKmuvj%Gv z{LGzOdKc;Z!@|>Zj}lT?sM-564_F>Re1oiWrltxa>Uy!+G+=;(Rl$fP!tMo3)<Xeb zCDE)jBr+ZyMDOAYirpNGbpRaVVeFtcIwlagPNrBaV8mh~H+sIQ3_Jf*HGbo26@g!9 z6hW_!BXzZvj_jgmpwl%g6CGbzVF!iQTdiGd6W^-bkT|Q7TYx$nw*c8<XDX4jCUIru z`-z4{m1)2lOsD)AobLP0RvK0P2%z<!Kx==#5HqRAb7TxQYQ|V5Hy{wW+>`9=im$E_ z<cy3c+}b#zAA#fpiNn@|+l;$K#OE|$Wz-ylotY9<bX+7W!rO?Sw>aMY<Ehfjk<;=u zkYfOaP`VYi^EryZB7!?f@nSd@Z-5ghX0z}#r=l^FT@2;a^k7g+KxlDf!wJ6441_Tc zT0~*lbp&I$Ei~?;H5(Q%xi<vk4*!DPAL-=`KE>fSGPyqyYdruh`_W0i2o74e=_#?I z?8dW8dd2P=&$h)W>^GiWjZ=L4@R>6`^?ir8y1TD_4B{rprq{fc=-6!S>N|OO>eeRy zQoSLuwtDkmAGC?}n1==G2`v4bxc+bu!>s)VSliEoXAJhxbT0t&GMmYL=%cidvErGG zN$m3rj=H4BFW{?U2(+M{M2{+;GbR&XQ4&i!)7zGBNVIBSA$?HWKLluXhS0#UX0j;S zQFQTXB~6HsnHFrwTl0XDfP-Yo9I?au)hFPD(12-wPcILb5b3g5Nt<|PQLMEJ)N=0o zCi!=umc8!&qtY{-a80F)$}N$%8jZGNn)~%en@Q7ogp9_^#8~P9{PQC!=z79l$lpT| zpBv~#Yq6fqX}E*Ijin~dc(z-vD0`$~ETy9H)_(M>Mc6rxrvbIf2}N)x0eqctNef;+ zIJn&+wngovD?0QLFuz5di;`g&^<|<-s|dbDp!vs0W~0f2*)*TJ$F9Pb->6vj{&?J$ z^7uud*lWPkR37i}d99t){Z?gE#c3SKdi$-!`};95i1s6<vW_Q}kEWV3D&4Z6C3PzS z1p$g)w$20xAk-S}A`Gn)l125HT-2&}l~k<T`l7__DgiE&8$O?Sjf0{MaFpmIsw!Vb z6p=m74ZxxKu~JdeMTAu0eM5Kw?;NvF69X#>OF^yw>uIl8hyKZlwd=6X7fT?_ph1G8 z-D(pE@lxVV=v2e3Y!`gDds`%&-(dSQz)lb!_$lYB0qGX{5=wTGbzr3T8O1Idv_5=b zyHSAhF{<i8>noHU8jKd4{0m&@K56Imt_@#8?Oq$Mg^?5Uyhq>u6|*)G>)c)q{8tS8 zrqVYg)`7J98C;`Lx<|u_{54MGjo9^I6pM_d7L81D^r_l}y=#H9Au6n~XUe|;0rZG_ z+iC}a%#K~ieAq7FhT}mXs}E6Ton+;Q(x6k6rYr1xc4`rr&=OcAZFx^z7{Rm#eEpUN z#&8H7%iEul*5T2l%rG1X-jLYh+OK=~KKJDOMq*%?-vQwL(Wkg4YZolgZ2MWhO+s*e zF{D@op&C#kd(8|53I)%D_-OZ3lkaYhHVNoEU&0Qw4UGJp#>|?^QaN5{_;7rgBiVMs z+2(9BPK9RJ@&~JbY5G*mZLs{80d+5SvA<%L$DWPFd|jK^4hP*BtX2l;JL^Dm-$%b> z%M34zi5wU!^B4&-iggJ-s4|-*z}W1*ViIn&WowwV?ZR+L`ZG(6e@wujklmN`F8j$m ziDC+sWY{`Ad8wWgqN{prIbj@}^f;F`k9T4C2qYsBt4-)k2BYm2jq{*Xu$>g}<)!+y z)&D!3>2Y{K8xrrj4LG|={s;^jrSOpTB5>Ml-iPwlQIgQTa?s^6@vQ^sn))@qvSS|_ zGIGYs($jG2_uwE?cJb}{!28?S<S_aRLJrnFY#1k*-);&NJ6i*t@iiU}v80Q$MsL~4 z7;YvdcD#xxvC@>G3uE<U(GiG>fL#x#Ke6M1>Ul(^Gd@sMicX9SFhm`Ll@$-fvDS0o zl-?yc1ZO@0-inm$W|vcONQ79Qo5NUJ1$Z}99>zAqn~C;J@Uq7h9T6pNv|d_RypYrl zf%AE>HoX2?myB-xX4|FvUxwFzcMa=28QpR3(xqaz=M!5Ji(qrZ*n|dGP-b|_$~><# z+(E@$l^;3$^wmgGA6fz#MSMnhQ?O+L9FD#ydnRMe{GT(OTFobY348#W*$NCsrei-A zM>j?9g}u$c@v0uT`Z<#^mtmIk9sc#tGNSsn!^+6O>7uzOiGjikZp-IDg`gd#<!Cn* zyJKae%hz7wOWlR@b@z|qb~EIy@b6yS7KuI%Cp!I@6;}vZik{eZq<BF^M<?;yf1FzB z8m^Ugz?$>D>SMV}%<-0vH*|dB0-MQ@**<02sey?*z>&QN=0<y;K~}+#2+zbF$+C`c zV!fP-zWX_|{w85LhC6m@<KJDBIIaZi=OMKXH+#2c3PdhShcP%ZtWP<N`|l`Og);cL zv+a7LwH3!4=!X!Jx?#gXr?#&fB$R<GRz0=@({`M|5(ekTGBCrRO)Ye$#{XmZZ)I#o zwEO~=-(z1u+_~ww8SnXF6H5quXbYoCw76@Ixcp@-7@nBSwxx@}w_XU#n61QP#v@iX z&OXN3iY|?hgCGW*{%csrpde-07Ocj!<w;n08FQyJH(DHxGU@JOE#^c)4&!yPA7i4W zj8DDvQk08V!#euiT~J&D+o7Pi365TZtN5=7UDgw*gO;MsP@?Dvy5$ecCSBmv#C!QS zJWvr!WgWE0AIf9EGYw$(?<n?8AR&9pr}!jpE>3~$v-AylsH5X9fVI+6S%->nJ@FJl zogxldzR0T%Bjc+qBH21xH02DyI{{h*Y7Cc1xwddjTM7h<K*{)AouLTq1nmx|N`O%W zT{Z|hKgI}<>4G7DMtdHc41u2X^Les$9UY3m1_0?4DS~f_FlXB0{@2(lUWobQDCoRb zSBb}j37GQ$cNkHPZdr)YF4kkFb5`X1!-Ujs8I%2j{WgQy%(=#q#6m#qN82_X+ZK#x zXFrVEOFyoJ>xAKV_AA!r<~WL4pShVov;#`EMcw}rwW^5X8njMM+;v<{0KE`dbR;Wb zqcsgU3deDgqU4rn{W>^JN<kACI`b^YC>MvpPiNTK!_JyCU)S95ZNQC3G%&EEqv)_K zg%&Ax-4b1z31e$TAM1&t!<faxb4K5BF&5Cn$a(EwVrIkHwxgq40XQ$s6z6aghIzu3 zP&wGrp{4lO)p+<TD2_KMO5kfIo*Ou51$Ge`QJ4#<6EUl?O9CHBl<e6kEaSNGno1?x z^7=UNzyDT^<+`tV9x{~?Q{dd1p|jos+1|6K%Gp+p_#n<1dS<<?pBzeMVsR==IBZ^7 zk;5scK`R|qtflnmQ8>XA-IN}G!A@t}uzYvZbv93~$)?ESh@VYh<90K~^SmbQ<dWjW z?%z4*iA#~p-)<5P`ORnB7Gxzj(psK1Ee5X(f6Jw&O9KyI`VXF(Vo=$5YPKa{jq`)U zK*k=Aa<&;P?yff2gng*fN<%n6OTCQEgjFpK?iIngEO!<t))wTPx_&YWw8Sy2C2lUn zTB854w<<B_>qdx<yHrogKCEN9t6cqUbe9@z^bfe$%LP0vqX*dbJucl|y(lTSa&6KY zmmK$Hb?vK(wV2WJ+t8&t*5+(`qZ^Ktn9-lvgqZ!vvS$&<QV*lG<-SVRGG1-L-RE_= zVLvt4AhyyAr15)syc%C_yjTum+379^l?%b69zs>6n9#M=xO8lLPig%>ap`x}YZujG z8vu4qe2OGQpz$-)a0X!82Y-PD_-UB|EF%r+-GI-vr(W$j0pcxkZQ>~$*muS8A3&2| zT(xe;o_iZEj@X--QNJVQR^Ij_wHmKlto5VLcjgtff=L2fIJbejRkNra6!P6lWNv>x zwXh<*o%pd~htp81XKvdBeKj;8Me8OJ=kvx}a_{WTJZRzQ&1;2t@CUQaY_x1^FwqEP z$G<9W=-qxe=iU2Y3**pfqWkdLb9BXCf}|Qkx;JxQ_9V+eliecL$3nJ=(!l0UQ)iQ> zdc|sUEHAD{w7_m`JM&DPGp-I5sfp9Bvwvzb_Q*3C5?%${39+?K?UlkS)-XCHB0sW1 z*g+J~`G*}}Rz7p4w!uQn;bA#H^{b4^0r6e>$+GITNpq^-TC?+L+2O{T+1EzRy_$U1 zbZq{Wq)TsKSblC*)9TadIK-WKVADa9I-AlbyIc;P6YUnST5Gls8E61i+~$0s&Sgl- zufnT{K!w3XGMvof7c(FJ=#{*wF*7923N}5;$w6MRMztrz8NQTYqOXBWT(fR8SLvBM z3up+XL3o5I)Qj=-8X;)K<hKi1fw!k)>-MK1vDRIHBlBp1u@o@8qZT81D4Yd~4UuJl z0ynng{MgD(&KoD;aFk$F<?@E7fS0=qQh&}|z(<TE|2i;Rp1^ZS>ZG@RX<y`3!nG{8 z!|EMR7Id~RB6o8owHGfh@-8{Q2>7;`5126D969to8I=p1(=vDWD}t-Qs5BIKI<}4O zxK*tPs^U>%_*F2v<9?+g(EaO+(d`?m6=N_c)S=Psx@yHJ_pjp74`zXf@f2ioR}ugH zPjTd_|L{X{U0A^BzJ2ulIhBBwYq@pw{q5E5iwdjyYFUs?Lx(T%-JLNOd*;3#(L4mA z8Re~KqrIcCV5at;+<@(yGneg|!|R4bh!U~=Fxhec%S#EaBlzxMQemG!7T1XDhv;$M zZdL*rgI7_HnoCdUS@u1Pe(j>}%J&!Dtz5Lo@SA&C;x$c(U?u-Lxpqhd1(6V!694ih zZ(*W${X#uiFG}QDi?#Q^EUqDAUe+t=v$^FN%kpAe_VA**%C(EyU1|<nNy5{y$FZek zw^(n0D$Bg#QFXUC>a=xJBx5j9KAUP!)+%;Ugmo*3h$;C)If5f3Uy(-Ycl<4XI+BM& z(m~iKe&px0jFJ;ffQY!HJ7~?nm<HBie&nTkwON*IBJz(E2p#^vK0P@jYeqq|Z}jQH z*<kV!kz`EI=zEnOf46{;7eOy#gXP_(DESxgC{e%HXg+L$oP`-XR*Wkt0Ea3&gOPL* z+all3d;duRO$jNTV^G&K@7kuLOs0s~QZp$_<~sv&@Tmw1<t%BQeY_?kdidXTb$lgc zSXrZq!n`nWL1fd^kxSaCR_R47-ky<7+8Onzg(W<X?6WEi-_kr*=q+~0)!pF#Z2Mq$ z*3po<2zWYU%Q`;%)EsykYdd~?c%8fsAD3E`&Zh6P)E0>439R*eLQ!J7$Fb7!6?OY+ zl&mnE2*~Y6MPPz-cGmk;aTLS{$c1&MtyxEwRb|b9xZbx}px(d#MkWrdHT-u!{^7KD zx+D`vPv#!Q`VGR6QY)|s(!UphIIZ%H<(G;HX!ZlnJX+{7%^BYgVn_t!^t2Wy;2pK1 zqYDgFr?zG^tQN6jU`%(fBJ6R7HnQ?U;5M})5i(-d71J((YN)J`jQW{JwxEVFBj`pL zs3iL?dORkPh9SbSsTC!R4du!kZ=lSvYlbynhjV5Ae0BDNQN)CdC<w_v#qQL(EH%?v zQt}$|(TrVGlK#pvh&+QfD+x>khpNhQ2cBy_cwPp7zpie~{H^*Lo5bc$dH?*IbMVtf zYn}UyJ3dMdAsK#1-7fbQ#l@ar=ZC}BKT)y9U%@@^*cR#CDhDdT#{OlSI}*iN2=!m1 zOB$sCXCf@W`oEb7J?S3mO<a=4(Jq(nYc~7@$v}}0Mq=-#2Pl?@U1_0kiXtc<qe`c; zXG5mK0Qp_$&g+yFYdVNM54bdsA?N|c^O#&(x*P@pLCgVEzXvKp^PN#)fMgC!4wDbZ z2Xibrl~t%AF(EUSaB3jqip!AGrp`cg8x4`!n>K;<GPd;^u3E3c_qQjnQE@U!8*K!u zZ$lz;vy#Ly+M8hQF~SfML`=>EtPs0nxj5TSTmBZra@X6R@<XDYKyF`L63!lhRIDcj z(dc0~p)@su(+3im=8bOfhDbBSBTJh5jfOxIj!i1|ehI#<CxqhLkr+9&^@0o!58iV2 z>O4HI&{{IOz}2S2!}k=Vj06tF{oGH0EaAw@)!hjV)prv*K_?%D04Zc1j6)xe4^rbR z3*%jfe|$#9VIJk#*{8ANDCzjy$Ce)@2FhX3GO@*g4vrtsE1!n$jn@wWDTT~T2Pds9 z%|J5oOSM%D*HbKFG$(8beFfS`Zl|G-!g6SEgurR^T_l_kil0GbQ_!Rd;R;uI$~)LR zJ{j37N@>`S?5eXZNBI-{aT~tMwiK052=Hn$l%;_Wpg;#XI@8gW3Bx$-s>PcpPMe$> z_`0|S(F70jiY8#K+lv%r_B@u^mW19ih?ly^vCTuDObp`?nd8yEiD9~C=VQ5R2<n-N zU_0S|st~)@Q<rLR$bQykx$CBq?U*9yHjxO|MgnWsx^@cAW5N+{o`KYpB59Y}F8cv} zG-))yX_^J?nlyS?e(E&G?cnEW`QpD7?KUndC1&Yx#Du>`5BZ~pOw=}Mq%6PbHOCze zW?V@KSlM5vqcP#b`Au&;LvzE&@|(s!L7Cy>_)X3Z^zZOt@_gxN$F1P!1=Yq-ir|Op zh$+w}?;*Mu9uSt_q{jVGm1zVqAhbG-(8#>2LC~Na(=faeE;EFRE5X{yFIU~If=qrj zn)*s;h&Fu@mVInB+B2=7d5rek@5|wvufF}E{68?o9wQZK(<{TgHR*&q><<ajV0^7c zhhCXxI0=xWHm%^RJRyF}tf~jknv%eZ*=VyR<A4UUX`PtE^R7R*vkAEnZh#4eva{^Z zTY%q7iDZi&)VZ*o#}Dx!SRENNzYvndKRTdQMs1DyYtizgvq{qz?^_)7W-Kd=SqA4e z$R>n#OW$=Yq@KRQ4~Zj?K=7oDg=R|wKlExa0x28jGq=LYY%xcO>h06k{K#u=#mi=Z zLv9$_H#tNGQV+u1k6#)CCNN|lEDd}Q-JYzP=n{)n5=coU)D|QbJhfv|1LIemW|{aB zAT*YU#n375x6fvWPsDZk42pi0<!iO0(CJtG`Py_fy8UVZC)egDsNZV?hpPUT4W8Ak zNN3X<HD#a%X0!4Gz&&O1pK!J0GKzX_w92&%S3Y!|+0C>McpeCbPx}~^kcyKh9AMoK zd@XIciMG8q&cN~bm8?jLlKD|25)!<KjWEAUn@42C^!TZ;J9akfa&4!>_S*b`m0Rj7 z?<gSt{njbiXq(4wRRrxJX4Sds3l+*eL}HyBdV<?KgPi5pAKCF{E3{U^X8TXuRdCP> z!S^UcGhPoj<Tnqqvy(x2H9$Npt7dYwo8hA{Rdtp8kOsop?A*!^fiQUU2k;F$XtT4B zs-{*fszzc>Z2-XFvhTMb*o=o)V@UkuY|cYmO@AZ?eWn913@iH1h#=t2<O>I3D^l5r zp1nTQ&@^*m1KeJMgRYq9)-yL+;o@E(xN*cp#l=>F&FO?tPNvJr1HOX}#jb}UNQ94> z%z2K?ozG?Aaw3R^y4K5iGVnzhRbmpn6;E)G?+f~J9_R3}5NY}0@8E%^<=fsFzWW7E z^yL(>JU(j0Z$_9bfB_eYz~vkQJm{w>D}Cc%ch>~FYl3mj<f%(Ulh*`YRbCNZL+PyF zDtqRGP)JY>trIaUzLwU&Q<OZED9Va>Osu%ycQ7y&EjNSZK%3(wVjP9S0mF@E5I)UP z4||T>g3US0B<3p*snHfC^3s22g!2eomIH|9@2$|e1l)&ZoxGO=Ziqb!`_`<m9t<ld zZ2+GLq%_6$l!(=9s=)_PbNwll6RCkc<qf0eRC+=Ak|(%a6Vy{~(d@5;%>A0{ow&Sd za8G%Is-Rko;UDAj`~X)u#7xBvBPUj4jSq>vOH*Z#VlV;r?k5ft{nzN2iE+8H0kO44 zhE;_#@4?Nk&lgN&zSfiAA@~(TbwBW~WUMKqvu1349i*PGtwJ6|Eys+8A!%yu{fro; znXjTHr?Xk{isy0`8(*ab;2Ilu%^YhsTrkZwS)mZIAyc@@78SUpHP%f~wrNzJ9KL0q ze~z%`!0((pBRJG6U$th#@9b40thw-=`<+@2-!d=w1~{3=H|mUC#@$gOJmpDQ+`P4h zo`$|$+rL($Htx<Ou4?6mEC3fBAK{+b5xpSN5yssD@@kaa1u;V9MxyM6XbXWZc*XG? zL{<m~p%3YWXi^w=ALSJ%a1eheI0&1f7s3EHsdh*4ix+YrGlDn}I;a;Ue;6R)7r)6t z><;80QUZG+QUd^o;E<Ms5DnuX>WB71#194>f<x}X{4IckPz9Ve^s>YskWhzaa7d01 z=0MDYdyz=xfP^|UlY<yBh=XYL?}cdd0UW|ZJscUxLFfneLNpBk9Ku6A9Pu-7AU3~V zka8)^4SCf~$P8Z&g7)o&$nOVJLtk|hvRlSMq{w<9Qau3&{nX7>(Etvj-n$nfUh?!! zC<XbaZBnk76F*Ao)aHvIbR#)`G#NJ9CK@^u8*N8L-Tuq7-ZuB5&<I_wYiaXphZl7T zIs~XF@#?HqePG)7RX+*EvPJ#`s1RYNfVrb0bM*|}$=j}H<efYN9Iw9q?ZAFL<K4+~ z*1Un%{_HGYlE3x`XZf`K_8bR~YJC4kc+N{az)AkCR=mJA;QET5mA&U?%mE0{2V#X5 zdr5drd1dy6x##AeO^5B1SzIC*lf4fz(C`^$cnK?Dctrv@ONqf>R5;EM0><i791c?u zHlqDku!FnBDaJC3n8|9zhYB8(*$RHZ!ryViKjiE^#hz5}l34by_~i*_6Q(cPw<suS za}o}X{w%0yJxBDyeVyyNOaneQ?r?8ZMfa%1%vQm^%rT}1Dg!a$>O5nqKwNO<(7dN_ z6rE68Hd@JBFQ4Z*9SI?pEL^U(Y>w(V>91XM`6d5>(e|PXwpDN-bDs%!(zlO8^Iq!1 z@B&p&CrdW|zdFG_uEv`AS4_vs4$r<e_i8%c5XBjKat83E3sRk)V23?U+hI1sQD+_a z&MP6`kZ#kW&erGX9f-Jyx2GU;2i=kf$$cAJrQRt>7)}@%^F@(383`v5O!mbAxyjpw zOfIB)3qJ;@+(iMKoGWDVE)FQf-=AL`@MB6YAYgJ^rDB?~v<_f0>OI9N@N8`L^iN^F zX!Vg@3?>3{hSSJr9T987DVN^5jbFg+JD%)f6j<kwqq@%A-wNpp2d%9eZHD=<JBs{M zAb0?=k%_x09U03tgGO;B1ZM^cV>m+)n}7JEB)gbE$kx@<6;?eNYpuAbj~H?UHbfDr zJ8gxKjOZ4a6_f}<+)q!lwWG0w*)xA|qIDAqFTEwkvAy+PLCpCoO1CW5FsJ(erg%6| z>_{kij475;f~PN0{BB=@*Hn4~!5P&qXJ*@(o0gZb;W}T~^CDuWREps_q!L~Hi0IC_ z-%*<~93~_DXp12a327~3yvPGtk7Bm90-Jgnh?}0T64Q>{*4v$xFV~Q<x2h1;fQ|9C zH?H5R;=G`<ubGZD)}(_K!;0;hdo8jKIgxk-2fVPI6Gk^{W$Qs6AVb7kEW4y44FM^# zPma?N$)T0oAVwzC@QfiM$d<Y&vLwscNj!$Kd3vpoQEPb&&*q6tAM+UgMF|dUL7m{D z;Kvm*^F(4p-Ve!)=wd&Mkda*Mw<nn)FM4v%jHI&PkIC@NzbK7>I8pe~(q!nzB&=b4 zFG}wla>}fm=81LGZaiZUrhVGTLLx)g<dJ-sHAvTcVmD80mBGwG{4mBb@Zv-7m_3!0 zuFSNth*?+y#|#0?dZQJLB~h=eU^%HX%Gx<WvxA_e1cEO}cdHUgVM0-dxxZXN5L1z% zm3L(7aGfzk0G&lWeZSHb>uvj>F|pcW+@6DhcR;>62i(fzzupU5Rej$@ytl5f8g<mu zFpx-ZU1_C>USBOcuOb30!>N80mUI^ZaD~`gw+Cwbl0_TjxY)P4XZ1S|PjvaS@jPOm z_S~!UucV6<qu?S8a0N_pP^Xi!W#Y^yeH3a2DAW*%*r*c|^G(XaIyIJfO5EL}O_kaI z4{bWvrA<NqpZUqVWH#H&JbdE94D3;-93TkUk3H%XE)-GV2Vg^X=1%Q%26qKFxEkHV z?XUg6YpC5*`nDO}rxE+DC9F>)a5>>ahtZ~gvlw2NpeUgTwCm*P{;o3f!WU)DC&zMS zwuBdDLr=NkTH{}oC7p7^(X(EZ)tpuj+-Guk6H^=+F5*E#rI0v~MMk45r&PXNXS)BX z@jLCVsgL-dnz^S#IAT;&`*tGbx-h@mr*Pze3ON2L9FvSHJ`oPr!C-`_`}AP482O(G z4K9xO?fLkqX!Z-X*PS8%8Cr}Pqv1n>;sLXw1SOvdB)1{+8I6~VP?fqkQf-3rqh~aR z;)scQCI@aJ=^0Zi&sWcEY9%-~(D<ky^u@J`GK#>c$49Y!eqGhg(Qm7T;}C4a4omCP z@lic+5Wa#b0WS+UVj-*GTrXMRWr5QuWTpSwCo8?b+ZiZ_D)8<6f(}GR&<${`6o;Nd z&mvQ}=a^&Yy(o50xz_)GW}Z9V9s+lC{qe;(J%J+~h~Ol~)uHX+w#ajlZ!Ye={f2v! z(W)QOYTYm|*K_@WA(56d#q&^`&QG#2odZbu7dog5#okqTOU)1Hwk}|qi-EQdd~)r* z+6m{N_x$U?i}GO)d2o~3r{}qc7&p#uhC~+BUtN$<KO=IZ)$2F+lt1E^>Xpv6^4C#v zw8G%(CFOx}mtF~{ptiOEW+piP*TNZsrCx)Qn)E1UAq6)FaUWd35AJM#42J}vX)>-) zO>|7Bp4o90hpSZSDoqbsf8N<Ntoyf@;*)Y0yTuSB62oQBqWa=DkWfEZ>7t?jmYW9Y z{l$Pcki@;WHGhU-fBVnXdH0b{AILGNbsKt5Kggh2%Z<~vbx0(Hp`P7(j=kHY4#(21 zz)7RcTv+5Fb$z(kq~YOlkgnU2T<iMHLqJ4fLgI>E+#j3?{OaMUwG{g}N{?@Ez&OF% zoKNcUZ3-raU&-4N2{#Z1UwDS*FVJQS-2i(JfKw$bv4x;4IG=B0dN|D^_L}#xj86+> zOLF9JZaaIXv#m*rZt4{}Zo1FeGx791fWU;zlu0-Vavph?BQ(W?r?c2Kon_!v?wt_q z(QZ7BJ=#|7Y|<xTjD?%P(m}8n*DM{*XsKZNPayRpPc-6WGjOc^2!q-adNL!H$)uHh zyO_WcV!l#Bu#y5XBNPTZ+v2qhj|#?+aBX7)`gX=3L(~&)j)guLakJXaiW_9YHohEq z1fh`coK4k5*fTA+!*9u|ZwpRbjm$WK-C1xwCfF`eC=Uua(pE@aq~Vua=&6D5Tu!_L zZt@svXhtmDw|{~>+UPPi$pkgT4uuyuGr*;002-M;p8!{u4MtXo*jmMPe5hb0m+CS7 z@EVlmkO7!TK_-6^ADJcOUfK^_TwrBRLmhNYEbK5{0>y|V%1PYk+07g&Isuf!J6iDU zDN5<jUElw7f5Tqu`u#n<$ysAa_h6!_em(LZP2w?V0$>brlqV8qy15h0$K`?GUCex9 z&$bw`LA<{9{*l^u$+$|-=WuHGXp?y!ePTTYtj+z-OwF1MD<bm=<xwDEmOOzox&&?; zqYGvd6`Dzz)l8hv4O?d=cNmP9b1OR5k^jwwWtj}!YAK;YxvOiri%rYC?U|EXjBx7< zGi{mpt@B2ugsQuMZ5F)rA+KW@KJGYj&OP4dlH2-cV7F=|^lGbTWSH-Eq9NDwNF6wA z<E8l1QVn+}$~prTyWwMp&$qnJK=f>FAl!oTARNtIS2)>-S%l@sF;sG;j77p85lL>t zOwiJH7GAG&^_QP^q-4aF?)U$#?{ef6QvD7r@ovF#VwWOf>()A@WUO~xpT+7DF?JhV zS|!(@NwKdPTpq!e4<Oq)$iIbK4C`Gto#En^iSrjyt0C2UsM7SE&i(s+XS!6SG~rH+ zr6F6m($aS!@moc<><`4lh7jdPSdYF~pe19izHn0~@ILyuqkq9hWL3hIsVO<AE!KM^ zM?*5S59BD|Gz->?dA~bLK5+CdQZy#+Eq;F9Pw4Zw-G<SlHk{AJ?)~^0pMc+XxSj%i zyzf%crLJdf|8}_iB;Qa8x0}=Q7i_47+=>nEgB^FJq7*Kj6W-pgfxFdNAzp-~AHzX7 zyO(dhL)n|@l-^IMYz6+J?wNre01k})*eQ(|hepf_p#KHGGc)yQ<}9^R6N#uc3CEtg zC}&On1b*33kD_SIIK;Nh#_qS&*HG~+|4^>k%%W)QJcf67#FeqQwrPAXh85EQ!zgrZ z)>zubFx?E~Gh0QwWK#7Cnm+sWS8>*FZA!R`;IUx)*EbD0Qc07-ZF7+cavu*v;2GZX zBzFz41;3MJOz!~E@3Rl_Qg&68yfJ|67xG9BTNeZ~^T7#8p_+ItPhWth&L2#>Je4Xd zS~`DFzmR(zRA~;p*5L0IGw0k9(CeTXZGOKOHYLpkyW&gv3XHKO9D)ENDzfIAM7E?= zqyY6>^5bY08y<Ci-s~_w%4!BJvdgxw*BG&yfM=#;IeNHcGR-~9(1fMaXn=$>R)6&A z(r`Kpo<$=((7%_i4%$zQ)-#`Wh-RDBrZTrLa7BgN7x+EAv`2$xEE^JdegU(joj2QD zW>UL>O26lTLcVoM+eV?xWpDWwx%51K?-;TDgmnx2+7)#dJz5qwrRIamUM8;ZNtgwL zN)Dp}SNlU}+mioa2=_=_R!&bH`p5EDd|HdM1qZhjYq8hY<@jm7o`D*dXYfm|m!a8A zB)_JbMO&DW{F1M~Mu(a2houj*^dS7dJ6v%d<-g<4&4bE-%HNqm7NX8~=F%>5s1s=J zifR0%5YE`LV(j9j&C?u<sHeYxn-;R2N<Mgo-*f_5<UD7*bd}>xs_T7s&6%g*To-^1 z!H1XykFT`w?5xs^qY~-;Azt%1=Eb7ohDOz(p~=ICl@9|&DZb_SBKSF`cykn&N8uiR z6?!{)GQa%mFVGjs3i$pC)g;UL<<GxISCWSt%CFn~>o~}ke>vNp=l6`cNYDIH=}EdG z1#*|Xr7XgEy4lph!s3YW?14<oNzr`pQNX2(=VxJ0cy_ghi`ZkErl)4fFL0cCsm7Uq zeG6R9ild%ht?5VVIx7K`jV5q0%1Rl=&;NcevZoB5m~Ob$i*9D3G)a``%+E9Tmi^z; z!rD+*%4EaHzNuJTV&0IX9jynr9GQ|2E;yxuZ|5hROhEGwf=?qi+bNZX`qphqIGzCO zW-<O|BoJ7U60azB?jf9kO(~>qLnX8tT?ZF>5<$P|T&5yUV}sHJyP4D#)w>zK9qs$u zC|+JOYW`c`s5e@A>2<TDY3zUhmnEY4kR*5sjaoUxaOP}ZWFdu+U2&&pPF`5dE-Axx zV{iX&-9B@{DN4USk+HTAZpH@2D|?`%NhcR%Er3A|>nV8|XDL+x9o|5ntQ;<AI<t2h z+P-qAcS+tsr&K##COrBKf(-HTBhbl}0aM`~zhVS@)bJSvpE2+W7IZPqwn3b{*i;hg z)xseW`!XTR%_S>v-=k|T_G)$QK_gcM^Gou6fTcG{$%kvnGNIrpV6Vx`@B1Es5SJv) zoDY$-Z_wUV{;_{2b^on|c*&>#3*}5d^A!m$3eo)DULvN<X~4JvZ~E9T=T!HtD@5M! z4j$1%PsWsS4aEyDCCp}7c<;nRxV?GfDU9Z+Lb>HBtk@kW;oV6?U7^#OH%f4FY$qHt z{HtBB7%P9{#QMa#D)jGn6Af#zHY!~usEeutKK_K6%pF6xCvNY9XpIK^$1Ropxcx4+ zhGUqD#`451u+bUgfobTYiNr?p%=sW6`Op94aBz#;RaWA3c{_U+ylB3GTS(9U*%y@4 zI%9L4(q<eBb?KEdh*1qI><fkUQT%+^<nyoZ>09mzWik_9p3f`#miNLhalR-wz=4>f z_WHROg;3Uar9uZ5P&qD6wJd^^AXj%-Zv#(mR7x;UIDY6K_wr*ri$&-F<a@kv9%IJv zxTi@bXUX%No)W{rw-b`ws-on1QBQH-(rG(;N_*gzRP~eqn@bEXGY+H2zsBC^TX&|j z=U3kWaYLcrfYc~zI|is2^WLv~8a6Xzxz&I~sSWfOAQlpJ6}pr<k6-fqYcys}J6#O* zn>=?{s6X^4eN)Rn*lV`u;B&!jQ%3a9V8354t5}%kAnEsEL-#th0!?3Qq?6&KYf4^L zadK@UInuQ|-&~;Bc~dIK`?v=^pRX4Z@@(9heZFE0ShqRw>~UuWnz?RxxT}2Cyb88R z8%BLW<i29x;<!EG`FB;^o^P51?srH^Z|m=Pr{m^tm2&6|ETnL>YuyvP`$L9`7L|Q6 zZ(>RRf2u8<u47{%Fnxsagrx4M3JUPDMUdq6!Ck-%sTLfJZUzLckDxK@1BP2LOB!_c z%-QT1i?jKSpE-A-^AB9&Z0=u!-dnGtU34~T&=>0$xu4ajYkkCIXbsWMNdu3+c^niD zyxzu#v}ewQbCUFE!coAs7wmb+5W;Ixp*;6&&IEfVSoO_$-=mK{owdZJGFOZNI2+uQ ze#XHKe;uxzaTdfV7|Cv|t8s!mj)zQMBRq}6<b*W#xZKsZMg9NlyZ=rU_?a*DPe|+- zfL{G<DzB~!efn9{h>%XNX&}$As=@IC{qRF{UTzBo%EWap`>O;T!+(7iK5`xAv1+*M zP6`i_r(M5oA$B;Q!#lXR8ITv$Pc4KO!`6)QF>nB?0BF!`>uoR`XIsN&1Pvf?D?4c2 z)aMNcJvUDXDH#(`i@>e^Vlphitn{z%;d~Y{hUvBucuKy{=C-eWF+ItjXhEX3K#)!U zY}mrz1FtJyw1<$b_lM*roE<g$G<UJCZ+28hOz0_@r;b$TEQeXohytV=Ghz^=g?W3h zkRC?xTw$%OD1I0l47XYhHyyX^&u}-{^c5INnDtA@MX*6G!maD9pyJjo!ciR6y__8g zVwK)EQ)7DEzPO;7vspCr=|Imh?3nEF7I?Xqw)r*m_J)rJO*w@+l!WZLALJOyJ{Sv^ zBch4@4!)``1l``SoLq@!{bS;&5wjqEJ_skaIh&XKhFM|#2wS0oM=~*_`E=Td0`Row zP|jvUC))cDzkxZV6%1}TWDMdA@6yX)L;bM!#kE`s??ecC`j3IU92N32jQ8t-FCE}E z)X&4Mh&||S!w?MdcSDrnM9BSWzAXksA(n8g>wxzL80t^6<Dzjk()DBGqOU<9&zV2% znBz`VH`D$tX$(CPva*`5JfURuOl!Ts7@VUlh)!b5*xE2`NjSFBf0mhK!WTVLT+bnA zq37bY`+I9B0n{vl{fn)EDB+heRp8on4UXkuAg);*TxMKSw7sggSt-HQl-2`6>koZu zsORoB7~fX3Eqx3RxP3fbP5y=+r0+MpeSwR{;e_+$=aSFjg!8>Qo)M5WBxK3AP43o* zpvR3yrC4bU5feS7E8sfG^Yp$Yx}U)r4=;YPP~7}o--G85QnN&ejsYzgLz3{?JNX7! z7Nc{ktQ+_S!uVZowe@wFzfCg_$~@a>oZZNWq(PjYp2Xg>b}wSyv4ZP~CakLfD%-w~ z0eQU%uj`y<hgnEB<k`(hEVK40q}5*k<|gFu&vp~7tAyR40~6De_2q27e$bvtNgS!) zcF9BVA<e)h0L2fD!3`l9F<Z1urm)|Jnba)#yHM%jagggW_K+O9mPr~9S>MBidVXVt z4FTnzvR~3d%2>)(qQdXL#KEn|{OCF6#x>JyQxcA8IYD6BoRHniG|~fc91BtDJZ1nr z2uHIRnjVTKLxG#c2cW^9#Zd)0h^pab)WeCR?TtG(S*~`)eo*PRxo3Pv`2H69*cjwW z{U6&3K(4{)l70|?IeK%8mKE=LWK>#SnyrvhKyd?J@AXnCRR&CQ^qs>HxVz#6_gRCI z(lZ&YJ`ii&)anCSsy<xzA>Tp$bvPiYjBzknt7|{Jxy@3~JqfqH5RY%5Lol;rR2V&1 zo4mCL&Q#a~*J1~RSFCS)lS5H@ro^Dz2_#*Dq~{Kr^ge;P!F5dSfQvF0ile|W$8%)S z_i)Xr{g|a(J^K~ZpTq6sMBjG0?SSuBTn-^+XyX`6-(dI4E-uhwKbsL=dnTc$!mVM& zm>XT-fi`6FRY&1EZRV4(1sw#H<M4o20J#Qv%)Gw>Pft1z-n#U=)wM1ubHIMC`<87m zEhq20e}Uhr?aYY{Ra_msQ=!e-sH-CTTs*|rExG-O&G4<YX2QOl8JuAa9RQ+b*`sP? z1IEo}3t&P&s7LRx;i3JgLGV(A9BPntz00ck)Cd>T7(@?(8*PL-H}{MjHY>R4x*i>6 z!#$j>16Hv`8b~)Df<&f(>1r={Ek1mL;iH7l2>6tBf{M+%ftM)U74VovIBwb=Q!}ma zgS!1L_Y>=T@cr$k4Knbo!UYV(m_9V5;0?|E;X&(`NG2C9baQV}!V%o&u@AtL`tdi9 zZo!>@FQvn%hqd5E%<Wl>8$q1ERRqpb-LJHRD$I2&ZQT}mB3_8>2B^2M2hG(}Q@WX} zDg6}qx*JI}wGsOT?MeHqP|a;WwAxGud<J~TIq{ZpPmYNBQtTsN<T6gAb{4YsLeOUO z0$INE2qa;P!Qu|FCINJIqZmCk2Z(=%_m$nniyj4Kt>foA|BZ^X1`nPO?S;zcRv-xS zhCg$6&%g}iqqAA*Jm(>lV9^f6IU;k<B{ktJk=RSKFU&vt_UYv(lN(nZUwtf{Kzl86 zg9`)oK8Jx?2zuvv?QZL*m;!aUaaSxM|2y>M`R640N?>Sc=u`w-oAq7b_cwQ@7u;C0 z+ZgOgczgbGoL?$XI@3ScjAI%wJI@ZybU5}rLvO;y?0FM5X4!^z5;kTMk_0azd5G&q z&;4KD;e$i?t?M{>)lm$|;<-`eYS!8G!ER)Zg4A#0w={u&kF{4^{gy;ehY${n=VN}E zKxr(?R5Cuvlw#Bs*!+$vy*(9Uy5K2FsZhgr@X6<;kKyFCt4owI)0Kp$ojz;dtYr4= zOl+-TI6kz>(1G&Oln=6R@;#5Ss?ErhNpfs$N#dB&0v2iY-3^tl$^0W*HO<AG*x}Cf z{yB;gJ7`DBPe#>v!fR<qsk0dmN@`t<r+ehD5jw@*xDcnNMh&rKf*JtMZ?WQAG&^S) z#1U8LgeDf^5=t)<rt{HOIq~z>Y#~dZ-P)Z<>J;DJQ7U$K;Y%_Icm!9N<dDRXO`nD6 zJG_&jBu5463SNiz1YWyhAr-sucLS*?d!j^t=8QEY!lY5`(SY&bH@}O`9^NAEeF2== z0~gd2bifC0q;-+pNZH9?$DBukz*>H(gn-FRzg)>dltyq6D*$5JuNVUJZK?ln)v&Q? zng^b$#%0T@-T0;kbKrGgKrd-(Pl#}%(M{pXbsCVf)9B|-Bl+pi@1Z|84fm?~8eIFv zb4LkhP4!bWC^y8v#?WEBc$DB@+yxPXip*9m`j-#XUVn@h=7xILEO~&TA(r4Ooz{*v z=Y|fisdgGKW)l1hyNE1phAvF_h^HtMz)+SnW0(EcZRl$5WPZ)C+i2kD)4+%K2TJ;U zFu&&dP4vm<!{PfkRQ!3!xb&}o#XZC6o>pvq2z?F%*FI<2;(M*wUsaTMAKW_al`#C8 zY1h$TpR1ttWi%wupP#<uBzh$;)KD|*(*L%?(A5`Y6j9p(t<S^96)*$i9;~sPcB0@e zWZP3|<tlht5f_4W{Vq{y@ZG>}_k9saxz&uvdkclrW{Yb%HvxBW=)qG-8;HQPavQwk zheHJK9tlh<v5k8fQOUURQSI>GZuCLo@`d6I(Nj9FvEFrDa1@_H)+hcS_P#wXs^X3N z%r3jjO~DWs6m=CA1Qpg><_%G~c!$b+rs#^MnV@K8WsAasAPRQSK|@_l0XI|3473zT zz`Rp=&lFt+wQ@19$nE{kIlH2npZBl#*B@_uc=nt#^UTaM&)jF8d7j^%ZYT-{3G#L| z{@veic`Unaz}6zVoWp0B1h#gxtvtSy49X^KpVJBCpX{Otfv_2-^N?;*N?Q);phzEV zYW-uw`N69cs})R>06FZ<bW7e~Dt@e|wBm-w;;|+>+Xi0oxUpH7u!x4Tw>=QRa(PhI z$Tm0@yXqLt^TA+jZaLa=BI=%J&=$MG533NS#(=3X7=6+|nd#8_I-%FVwpYQ(n^Y1H zwZ3f7(y!QHV*AWYkUT4F8FjzvxdmE>Ec&EGk?%#gz&RgPf{mP52kM;VCwC?=Z2<MX z+;2#6hMTST7<37No3{8+X2MwvyA5YdSb0^6@e&%ge_(wm?kf&ibNKmOLux!`>Shtk zuE<lhoiip)s~444y&i&%$3crO*oCpsZpE#Y;}Mx+)*lm!#ka$MOekf7<LJqjr+w)n zbo}ZXOS`^|^{kl>O;_pC&f;#qw?<?27@mmTp9=Hu_q<gWw{4e2w_U<+qIEk-tbaHp z5Ei1dj97S%YFfNn-K%l~*9;X-sQ-qa19x_x&zC8;5LUoqL{-}mk#N!PdG}dc`R1$J zcSBzcLq1#Laew(FQznGE{L>+00tvy6L6Ot0iMN=~aQ#W>JAoXt$~l6cvU<jFbLz7# z(|TJBJ%q}6s4;OqZlGw2OY`rcDzlji*2QZvK!uSI7Jbu6I(8r9dB@Lu6he-3R`Y#7 z>*^D;%LG>b4%u?Gi-Vrfw=hDl_`L`V5^m`oz2PFXM}nJ2`Bk3{^UT{+%WfO!ZMxZ= zc_KEc@SS!?*sT)b%pouOEB?u6KAmHn&YLSvT`{hRja;yhgqZW%-<57Z24PM+uI8Bc z3O*Ift;v|0I68jAkK|wNl=j`66&MY;Zg%SqnWputkFAJVXbrIMm#Yp+5QVEI*f;09 zOU!%u%Y6H`c>{0N6dG|a&78OC=2wNdWZ_%C*BX#-oza#}sN!>G%||&6vXrYR93r?^ zMF*~#3~eX=dO8j{PMo<NA^{0il4fDzcV#Z*7&)2cOP&KM8BX*m%Tb1c9C_j#@a3R& z$BY#=f9}B`&bEGqQkcp~4$tNbGWscmkDMs}NydP_GeFYT8Pj$q`=#JubUnJ)IHw;v z*#E>v_40rtCvdS;dn`vzhAAhbo;aqa(Kf6&9Nr$q&tK!-+PrHfLWCQ6g{yGghJ}NQ zpz`p`{MI!A&0K}a)i@Sw_*Z<z8f8CBdt4me9V^Hgq!_7-EXYQ>JqfqR8f&4xz+>GE z7g?mtY-nHWUd&&&iHuFR(FnzAXsqG8uYE%oaFYdP+ClSWT+vz7sm&{JhcRzgC62Dw z^{i*UP~sV87cX1kdnnesNORA-sGAh7<ERHTWoefxTB0LAc_yu8m_B87rCV4ZU}_x| zt$HgsXI!6fF4M%OAMPf`bACv2nD%s)ZLE($b5CvP7Oz7@I}^{+^f2FLvJ`hgLqvL5 zSL?gYGw;&$uoiG9Wl&)lcVbm~*Z|?Hx14kS316eEU#g#0WrZC<vq(M+!Yb;O^{Zl? zb3jD4g@~*$X4Yw&IW{6lGhAeb?z($%Yy{`V*k?9uGQG`ofoR-uY5MNrrC~0}2??QP zjg1G_RxK;L4~3V|n-LqK!GuO5!;Dm7HcYMHm(#XkhN+ifVCLG=;I?})PGWfbU?HB) zv3VaX{HhS-+-KzA`NNsC_YRmN1}1(V>@r*3k+(^APF}I$ccG0L+F#`Vplx)9f#<tD z&=-P_)k~B$X|wl!g~E)p;S(~PvCDgQ_Js==t@Zh$XmFEYuwODV)^qz+<s8-#`E`M4 zAh*Y6J9YtVxTohFDx3^-1mJ`~n`8%(jIKSNl>lV&e|%h~Y`mfIl-&`EZX27YU6o<j zr0w!(9A*m=bBt4xn1a2xr^?7p6#D!{h3rxz?!t1Y_K6hukl-1^+m_U&4`H(&&mDj* z(C}T6*@YzV&DNz!sz2a2uItwd2QY#{M%S^L*iKx(Sm&|O2bsW|*R^GM>)kwnAd~%A z0#TnNsEriRoS&i%TF=m?u6OJE4`?bYTJ5?YqMf-8%ZGfO_%xDqwbhs3z21#<;g7Cw z-9JIVtZ2h98Oxfmds(psj3W0~x7Ye<Z3;NV>;7Xf!{$|=w+1c!K5yGgjK<J1)@`T- zslr7>dND5%)`<C)PeaBz#kx(fge+?QvXGHaLtgouaiRY6e`Hq7Q?zbb3@tqCADI<r zMH87Nd7fE%t1rJftK~lxN%~WSpR(GtOsF+w$v=v9%j{=zEBHvvz-Jp6d)jOee5w5X zShw7MCYOTuE!-Qvq4fv^OP4QvKIOgEh-*!s$rr@B74$Q;D~Nd+Pl~MA{4Oz}4f46M zZa?-jxfjf}By@R0>z+txt$falrF_O3ocX^K>vq1M>6L<aECIW06e!#zpYc3}H<|*i zmd}WFyV=jwwqW|pD6F<(^Q$*n7&Rr<?J;B26iY%y8yTm56}_o^QmmV8e{gD&#oK<9 zg;Nurr^B_srSULMjf-`2>km$ic^MrlD>fg!NuXncJR;VuQ-5$O!jjO&O^l9xo7(8W zuT1$!)cO9VmIWhU7G;n%%39NE`S9n73u*G><wMah`kUGpguX1Mh$ieTdGK=nO@k|| zQyb-j(J=a(S{DqqkobwAH6xD7gPu<=swskX^1xWPclw)L3j$x3Tuc-8XY&3mrso^~ zqG$d4#=3pTIQyC<w$?9nPjOasK47zjwLN3qmNVA&w9q|eGh^-I%@)>b&{P;}H7}#k zV2zTo*8lmuZfo-6<z3LM7;F7r7E@jmc9y);a^7W&g|*&jTa2~d783h!5vm!Ldp)0A zX;TF2<Q>tt7;8JeEV*(kHotj`z>5rdJG86*Cg*~7mW0Z;FkbvFdT)}up#}9fwJC73 zc(?vSpd(Y>>UlaISYxg=t(T*#eca#Fs-We|=->ueM;6@|0v&7Q&S+NyOf3qatx4#= zcYe{DbqpKjjxW{#S8H(QZ;!S$0GzV71gsM&q;lKmDO5KFS|^vIaSZ^c<S(O8W5wpD zZMASpf)+OboRU})O5e&j^`+pg-6a16&1itOO~EG??~7X*r?i>!kDjMvtTpCZ?Rxo# zXh8$CtqMMT86A;UY<}Q2Mu&Edd?6ap0Bwtcg_eZoZDVvKZ)3F=w{m09A_izZ3g*2m z$~bG3wc2&^xz7`)Z}R(GJ_pTYfL2*B=VdXaH(_VU-&xLkX0zI>T`Ql77BoQnO2JGE zi6gUF#{ecxe?GZwO%be?Pe)@Jplw?a`Lg8lny}Z)r?8lw?-=$z>pvL{h;eqZCAJqk zh9WCE-<WM-?F2L+#@Y!My8Uw)Ylr1nSUU!7g|YUHmr+<@jgql8;`zMZX!849J{rx6 zv3B&!VybS!&XA8>&YN;9tR0TF#aKJsLZV}?P|Y^UL!VDh7HA!Rw5#QzXk39{ZSc#I zbGBmh^K%7WWXXfjt^&1g1wodC47rRK--+H--XAR}Q0r3A-{O5+q$6G4_jx*cS!1r% zekOknO(;;?uHe;|(Gg_D=EJuObZn4!N4pBtx)*e}B=qU_*60{e$uuw4fU(x#%-<hv zD-fLWw*)-C-NGrq=P8V83bb18hsMP?)#+sv##piWZbl2IywKtpr@Sl)MH(5WJ{G;H zT!m)DIHj_98;t@T>GF2Z)3L@HGvky8S`gz@>zC1CuwwI0Us^cT3Jr*Hs+A?7z%Lmc z6TfUT8T`tWzk(JKsBKyB%FCiGvPM~pD&zb-aivfF>aU@Z1ZtfNEK^~=TuA2kFWtK) z)Rw~YJma73%Mz)uM#Oj9;r>cO?e;rp3W3_z1vVC{C+}!&pHRD@QY>9QZbwVRyYx1y zeW2F00NR}O^3x@$&ousfYuI8%lNXyVE>?<nw6Vp=u}a=9bqH2Ct@wr=y`{I#^ygpu zs@S@~5yJ#M6UN0i#QIfhN|HB!F@*@LKVQ7Vg`cw1wgXa0liR+C8Tl-xZ<O_m#cT5- zc8t}Z#p}Ds`c-R6kvqPK9cT3yu=UojTGO(Bq9onwFVH-Cmq2rhT=HVPhG#K-w^_ex zwej*#U&PL{`U_O-wSLuVQ{*4Mh+Sm$7qCmMUo7s0FJhNl{RNr>^BB$ARQbFYF)N<M z^u1yIs@3Y{b6&)*w)zWHJg|P%nv(yCAKdPy`sCs(vwqcTm;DnzoUQ%>&HlRun*Wg( zs%J5M)z&W-??3QDWA#V8Ejw917@Plr9YI!qA%h{-FBbJbup`3iFOb~sYYWLUUSwS4 zvzWe7)-M)s<crubR)2wtMb<Ay^Q0HC<E;Jyw%+>1;vV-RcDmJHpt<=T3(XNPVj7;s z^xbCtV)2fA5j)T7FHo`9`o(AteG$9J>MvlITEAG_gI~lhxB3e-H&|)z{~~6^vzWd& ztY0kN*IvY~w)(Spbq}mxtnz!kfUS<ySgLdF>dMnW+7dZzdz~(N5s&Ns9Nw66F8Lqv z3{T_JelB-AtqanJXfK?Rto%o0?xHnPOnKc>gHH!B&2y<G+7tiK&#Ik^o?@?%w*s+8 z|DhjLbG1*gmdRW8KOK}DqW$U2i=DB$yrmT}xiTY|8D_~S5wI_wv2@0K-rkl|5^6>7 zP|o80%b6E@NwxD+_GnkhHK46jF3Ea!ETXVY?+r^P>Eofn3UO9t1>50TJzuxiTN3aE z@Aa*>F4}O<CuX4@x0-{B*O{U#qBk)(4Gx-)ovECEfSoQ}dNcVL?A^S%8s}U%R4R_H z2;Tsykt&5`RQ~CpZ3jdix`KyM2ZT&XGD;@y#TdEU)F$_0#z^cUV{<m+c+?)uxbn2G z<W^*i<SP@ojN{R|>a;7E<m74HbDuCrxeXkvqn}{5l&5Wx`%rL`2yPEB^<sF{f*TRs zsxe&WA(4!Xk*lLv65GGVke_F2nVX(5GFfnWP=Faio~dnaO2$aN;IbqSbB{bztK7tl zkve@8ZdnS{?FM2lLyQv=2+BC#2Z)(@+E%%Bf{ThJ=~RaaRGzk-g*0cu?Gff&4DT-i z&sK2z3#9EfwaYCN@*l*AJFpwX?MA{s2yQC4>5li=2l^P|_kyD{L#+806f(pD!Od0( zd=TU@ysrf}8|F3uc$o}ur{E?L!hR0|8D6g7Mwr{;Z$Kc!+sfSHI3X<X-r21K;zs5e zBskWCeG?esdgi7Q+!pT#<5`4Ya=y%2aN7sG*BBnyoG-H#!-9XE7#<j%FS8Nc7^~eF z9$1|(lZat~XUp)w=i)v>i4ZtF&eJLxqc;mK(bWNUE^B1uKqy##NUUHo=hTwY-8&PP zghW@QE$9C#7@>^2QY^d5u8c9b0mbAnKp(fMh##{LNA`sW_+|S#>hwk9V4^veQ)#fP zEs1rWsKTzk7xu&1v(p)zqE*<K<J#|={Tuu^xY@vuyPJ<MXMDB97kj$BCuoCanmd`! zTh4juu#jTqFzlz>9cjkfJYY8t)2G%k@dg@-J?=#B=KOu^r1Q=8w^j%ap$@$N{yuMt z@3t)uO+J))$kXc<PH&SqWe6gYZL>iX53NV|qxIq;zUX&DvUC{4ChwofUpma>PU}t& z$s8j3gb;rJ{<iL7yo4Fi^FX{clKAaO?EZ<FuioEA8Zq@ZzWH};$BP8l2vWr9Ivn&2 zlZfP&J<+C~<1)jvmDjoQu<lfQ^*TBeQ|7q_F%>;*VGq2?4!*2Msf+?T6j@!q{2{;W zyNO~h<<<PP?*{0C;hvCiTQE1AKJdb&krt->zBCx>MY!`}Swr?`m^N5rzfomQ-*kb+ zZZ%4PqqV1oc|<mEzk#Df67Q9alhO^T$Qg7Q93e{N8xq|&w8@7#5_TH#z6wRN&W9V; zrCQi?4O0HU@H@X(D!^o^T78v2{(aW~c5dOLxa2s<<!mU&PBCy1?9xuEM22t;>^BNr zXfwDZ#<A@Z-|4`4zTv>A&cMX^;?cl0xXhPszT}szUAdLR^)CnvLSoiM6dc^v+?-b! z&%b}LL$9DlN_(>~KE;q}zLg`vZG@o0wGf3CT*FG4WOMQ<mJVn;1fBfpr^@-`2R$TH zxA3<Q_Hhpym?ommDnJC+a^&9nlQ{lt{{`Q_Fj|N5l={y8#%~?d>_I}4Ls&J8F2j+U z(}SmEsT)PI)Q`=10e?becbe1~OyY3>T{6;%bC%+GMs|dl)13-QSa#w#m|4eZVzq9S z^(<Q?E@WrZK{>AX3kNxp!#nscMJkONwhK53SQ4};iTQZ$$jO~pOkYLDv%QT<Jf+^* z*(8kig3C7j7yL&>Z9V;MxvW7g<feuy*bA}Nda=&4IO1~-f5GoBY9~2`TU|wM-Nh=k zDKp5JBnw>`(ii4=6cS{1H}72BK`jVus&62J=O8FpV=a-8TVmGr73RFOGyJ$>Pu)8{ zE`BRumMG!dl)#niPJGQWn|JDw)=}oXI+W?GX5Uj+L1@~UmDQ<Ak^PEvu>W>WbLN(I z!&-hm8Aidlfp(>xBOQr4lX`r$)ab~`aOO37y^G^fG93ky_Qj|_5l)r%nQ;Zm&#c<D zf^R5xb2A%y;a-#0_6AIJiUq;2cYV(L|KO+Fd`P>rvy3Gd@T*znbjg@-w_Ju2#;I|- z)91-yCfS20)@<s}irFkZj;s_9D!esje!hu!sEb!Spu$O@C-kTM2V-8({}Vz^tYy8R zt~|(m8V9_WM85khJ4#(0jx<9a0+CpF27mB}*1C{#F^!N?b6)uV`Z2c(nJ9u&l2}VD z)p<$antMJ$8V?+n2KD40`A8<?O{MXGZaqUcj}!RSOg<vY`iOL}+c31J&|56d3UUu! z9U?S=6|2Lv9uJ@nZ7I|z2)2y*kV7qW`X4Pcd=DCSLk9F0BFM2=O&luwp!wSQE|fy( zKx`4~P*K3IL|pxQrbU#NTKJU@e(iwt8qAS0VV3hF*tu|KIDWfj;@wA+85ZjtrnwZx z@)E0qhSAd_(?=d3O2SwZaXMiY%^7`i+|lVJ^0RLoX9`p~H>h2L;j3>G`liO>ex$M= z$Z|L~2Q`NgULy5#;D)d7TAc5|#qo!08<|}Zoas8(D;F!ZA3t)|{%Xf$t*%m=)uG;~ zk>)(}6=)-O)_MA`gYhUx(sh|g*s&_=LY(fgF`>l?T&WhiAlZHkhm1&QQ~j7>x$Kx7 zcGSpGm}>~qWFE$EPnQpTB<Jy_(qtaP!F#vNQ}?_L8Yz@<o<8>aA&>FJhel(nS2JGg z_&`43Pw+;vjH+`zb(W+V8P?=CRS!bPJVHofGi;=+IVA>#P=nnANsFsXgPDR_Mrh-o zDAX1YY+f$t2Saci8_MrOf!|`n-X`W76N>X|#w=p?81i+0j`=D7X`TPlQih+S4AW7D zdyEMWEC#4JH~%DK5|fkatG9z;Q(Syg-GAP6VdSZ7MhKHH%$IB856JzKv<_z^tU#I3 z!+#9z)nxfF(!9kQ+=<gj<8x$CI<#8hX?Vb!N_&n@Ip=6haG3-%GxCJGaqMQ0LG__A zx$9LPf`UpPh>|F(40l&BiBgx3p1>q8)A*iH*W=wMoYvl+n<Od)G?afRgKSU&A63@b zHaQIWse>q?q0Chmxhun%d^P`#6(LZC$->ogCaL|CVTJ6=WkhXbkB{MEZ~u6Z;O{cx ziReP?A0NmcXQ?YuROF!gGYAhnte?2eqBmdE7$s;F^OZqo6a>07(O<6|#(&U<)2`_) zb@@PdL_w8kv`JynEDP+4*E0HdX#vRE+PYx->}TlEO-lx0wSLKKs+h_xQ(W(QF+0qB z4Q?iO@~X$3%Q|eK*Wu1(ojLMU;;2GVt7|}FR6d74RMwuEKg9ZIdY=EmbK+Jx#H|6a z_Xr~-JzFSZos~V=B#%6Y80(YQGqVgeyytoRa^2h^h?05q`4B8}juZPYpUzaJB0Y7l z7t<~nfp(;JiqCOGd13tWIjolYhIxN=n0AKGm+y8UBXKFSj<=617Mu49ulVf_T>Thn zHq+(tGt&qWqiSnRV9e2$*g!mjTVhONUo}^lwBGK+&<jxqwfMGU>)T%SRP`el@>yy! z$JNaoOz+k`v5?fBpZn8zY4xd0zU-&2ZQ_b%<0?r?0yq1HKI~FC%&SVy>e0GlVjTG9 zKMzy5qg0ZQK!EJ$4x_|grx^wTX8FaC_l{q(>#eK^D&mY>_dDwv#IIW;4jg7Unl~9~ zo`#;}Qc=PoCO+7~fFY>bX$61$=LHhy@qExPZH9>asE6tp)bEmp{Rw`_<~VMNd#QC4 zXTqoKJ}?ilkKuZkq$KWAN#>6*Yqnr<J)$$Df5dP6Wua!<+o*WENXUu&W^yG2nOtPD zNXX6XgvrN3DhM{oW{U2m6U>GyJNUt;x5)M5fr7cw>5jVT$KyB(V&ZdhOiXtTt_QPO zSZ5@miR`7FER!q!P;`DNCMIB~O;b3Fg?H>K8OhtDp*X}9vqD=kkrmoe{n<o>j7*>C zm&`8lX$RRghxX?_#w7M77TSqZQtt$z(4y-9qJv%|N}UlpH%jW1t(bUNWjITk8{n5b z-~#erx9wM6W`K2tsXKce=^<AS67B|Sg4!69O~>r+pG36gn4s<=k$&eHk-ldrNRBtA zoFyZ8khl4ibUUO5fyq?p!zjlvXmj#nTI{^_?q!(258&1vLnV4;(IXa|X)rOpQ(M;p zcA1tIipdpuBDoT~JFIg6hU01n)4V#EeTXX_kk5}ir<TU3mhjWh4v@xF%;&e99V&^K z$p3NH-z#FARBcnwM63Rk%Vz4MFt_@H+(Q2Ap&P9p<Da2=Iv;qhhcqT~Ccp3;!bi^F zzY(6(`TOU(OVUI5PUk%&6Fc*v=liseQN7gyLrF=46u0W*y@MNIHN7ETT~aSqC)9Tc z;5VM{>KPOFt{tRM6Y9;JL1*n>#R-NI^JN!B*f##od0&P2zJ+ghp@%}4JZ#;_Pq;8t zQn-@ea=~|iD^O)Jv)OR!cSinjy)^^7EGEiC@mvySnqB&2tVLj1OrVPAZ7-_D1bkNW zJuh|@->X*g?_BKTDa0Ig9oMHXv(?J8Ld|0MS<Cs7i?6dVd{Wgi-s9IjVwjNAW<%Ps z7s8<OLfOV5{;yxVDunl>1-$pA-UIQ@C*{mre#q{q6Bk5Yk?%QEiuIUhdOoKVX*g~+ zn1$RtM}rW@tatgWOC1zyRDJc0cleS^YK0f#OYoY>S6}KVNjuB8{H>dV7|jp35Bv=u z@mm`)`LHScg5P{~LNsA-c?{XCh)+`BUg0ArCr)L%#$-E0DwA<8iyjEyj5LoSc45T| z&p4|lF<2vfDuB(}CKSJqTYiXPLeW{+PXeZxh0=W}pxl3Ai4A!2ChvSXoDsz*%^J<m zy!<A#Bw<-yHk2>A{FX>;&S1Xd6`ti2rQL`B{z{)VV##Nj4dKgK$$MK%-Z1bt-nn9! zg~tJWWQB#_VNhpL(Yv?EjMc2C_Y8RCjg|J3Cc?xKLj1*i6AB}!f^|Aoclm~j-3sA7 zr;`8n>QeTOk^MJ5@LF%rR=~+ll-*l9PUGTSjp>Xt=MoVU6q`OyL*+;(SAL$)yyoFJ zBNd}y$BYKEX5deJ(KU4&J49k<_X~3qd%uw7P=yKm1OrceW>P!JSbjt1vHSX11)t`7 znj&>g^HA}cDialFbI_&3>&IZ2s%JgjGXqnEbBtwR`oYEq`oY7W9hk0O!N61x;{u89 zSM~O{@~MoiZDk)R@dTqkG^NhOb*_^7^daW-!X5ABYggL8#RT7<%9%42<D!W~*2&zk z*eDY>PMFaXHsr)_x4X?`)s>h=;0DwmQok^7{UIfMu%4}28XT{-<KEEgZPdN_{?~_w z#(b<DF0(<;0qN6{`U`3M4#A$GFDgXx?3>E7On<z0cc_Up+~Q!Tr<pOKEiT+8)GGy6 z>Bqqo7`Qj}B7gLHKsO=O_G{WUcARgncOu)JL|;}h)$DppZ-c3>%v5>5fz?+kR=6v? z-;H*X!UBHyjrIW!V1dK$)0Orp1AF6kZHhgo6x||4H@}>p6G^Dh@dj1A&1c^5YiH<X zSBeojs5*GipO+Jra$%sDETE_=dhiuDUb8QoD1H_N;Ik_qUTND$EagFF&6T3Ag~sHz zg<@vZL(H1OLbavOvMcRw>4R_=8zHcTkjbZ4dMbqXkPLo*rN;#4-aldvPQsRk^0K%< zvqo19F=WuA8L)gPa}oW_n#MbN!9A}Z><(aME~?dA8s=UcsDH1wrOK~*@GE=6%x@L% zU**m!Jk(mP;73=zCW#n<aYM!DRyFsC82ZdOksdRtsd9u&quJ9*mq@m7W4c$G9a-Xv zwa&F1iP&4r+8tjS#w{PRAq+;-<o6qzg?^!c30zHeZ#KDL6AKZqYNv>Yc_gqHjx4NM zD6C%e67guobkbfLQQnVcO(kwRR0fQzxO1Az6tjAEd;$$96(e!OZn&(@sDFda)-(}4 zj0yAa#)k|CA2CoO;)V4DQRW5M-G~ocSnp}`gtL@_CmLhsG}h4xw*Z#7y7W>DsuM*Q z){kJuxFug<Y{koypue%cs=arF2CCL1!tjS-@Io5uyHLvN4lGNao2xb=*46yro84T5 zN_y*aZK6FVR`Xly_{5w2h!_9E%>fFb3VF}r<+p|r4?g0SJ88quxi!{7h;1A0c<p-3 zAHCI0{J!B})|l_}^4smz<pCF-FV<TF*)$$%`gAB(ykX4x6vf)gFGTRb5cKi#A&V{q z>PszIZK`l0dd^}#<+i6n2&(vi|K@gg^c7XNH35!bK$kAFndt?J#u9Hd&cTGnF|K&S zvG_0TF+m=!mu5Q%^A*@5eVY&ceIj|X$MCzWL|L9zjK!`8kJ+^`;jE>>pszgsXFqa= zx4qL=8es_MJKgCn3E06;zT*z77zO-?cV3kYyv*m_>8JehXOu9s-?Y$&xz}66XCLMz z<{Ab1T|O~;4=<_iCcb;;?de-ROR9JzVQlzzPwu_f<-~o@lbZPwsVZ$VKd-K>R2A?& zzpCzCsVY2gPhDL$k(}DFXKTYQB6Tio+&hj8k>KSZ&5{sLsq>W|=?@aJLh9VUoJL4V z4|tt|E(+o+b)NcF{?}5n7{NBJ$@iC$9(cR5na+@tfdiZiH~6jSQ@Y1)O^_0kSQnW( z!t8tshJ?i_ZAkby{eXec<TgU~#5<9MolG33A3MO9@b*t;=jbQ2LQc9$oueP+J1d9- zVoj^0?QBVpA-!=|2xd;r&bzJ|6Gta0D_KWb){FHuefqhVLT5yMi!fu7Hm=e}OjY00 z>}<Z$gqzqSKf{*vAujD;GA-Hc>~zdZk-jahup?n@oKKY+lf}RxZU|grMTk=754I;# zP!oM5f2adVA}xl>Y)I%uRyY-nChQLc(F#Z6+9^aPK?m%dmT63!lr$O_d4}tUX%_#+ zI(k!9Sp6Gp1H?Ie&wSdx8R@0gB_d?7OhQ8I#}3!G$Tb4}r~YjGBB5AlCG{q_{6`lz zBfT}zD~-v#y(_1kx}9s53+i|5u~`$Ox9MTGE~r^CZv&sveF*G?Fmuh$0Xejy8R^^N zR3h%vDoX0T6Gauy;k0ve(m@iQNJlp(;cdB14bS^K-RTNTg;+dsjdgT?b96eKuAW|M zPFhQLt)Y*blOeBgh4-yt&a&)R2hdSYq@$E;f1fUJB6HfyiUj_018<9XVo7CNbcux1 zS|>8%_3(2{-JqCHC~MdOK7`3^&`j#-EoOsfcK(vLd~?~1+O>!4Eh}E*cHj*WSj;uD zqD>qJUNIulD0L=1q4Axq7?o@=*1lOSVc(d2DCsq*({K?zTQE<0ABYLTEofj1;wH!S z+0dE>Y8Xmd@uR!Tb?kb8@=po$5d0}+Hj0#0c=z$KVQc0KzbgO0SC^6UrC4^AK(!>+ z%ukSv{cXDqqvCyGC}s>9Ax=-uvYuEK@v&ukatiBajq0surzh*!y1ACEn-|rqYqTB? z9F{d*I2Wi@euLsJBy4<ocI-`LBSM7r&d<rFqWZrWNqR?E-n(7*m{8>_q?7yO4#j?n ze#ycjOcV1FcEe8~X~U)(I^h*E*7Gjz$~gNa8dHRkSyMg4F9k~zBbJdZ?Imof-2N|G z{tD^Xy4u$-WqysY1{B)Y%vC~&YV%3z=t3rYOwBT)y_pWlpr?M|Dx-cn7MZ5wvL|MZ zA(SS#kl7s`f)#S_s+NN%47LqDxHgcc4|W{FRHQ6vRqqPB7=38-mc&N^jzL|+HQFCW z3jH^o^-Jk<#1H#KY$#V@Md^;6q7hI8@&>kIP@lQHS7Fld1m18lgc&yU^sElt#jXOw z?l$bH6c>WzTFcyNfk^VMuW30;NhrsaKhlPlq*IVCj?u6EL55qt;eQ$RGeN)hm`u)Y z3=d$7wOVs^1B{Qr@>WHT-141?4f0BKso9B8W8g~Oa<6XK;JMYvnG~`+f)<d~iksM0 zs@|AIFS?Q*Ex>~K?CjE4sqK)1pgl%H+qEL@qO<&v)j3^44XsEU#RG5xLlT|S3XLqC z(95leo1{Ybj3=z&37#>cr$>>FP3M~qFxa`a5`8`f>Am@{D9Jd9ZNNje8joH_W8BGN z^*J^k%vy{+|LZW5BBR20)e<8v>Tfuz6iO^Kkv4BlCVTm+=xMaS1h0184CctRyx4I? zz1)uuTE=*4a(lJWTz=;uUDcZOa;P5tgBVfLyYysh;;yT1USy3sXC6BVx_gDO4Z6@j z$Q*ona3Hlg^ewJZ_a;B#Ue6KhP28q~v0JRI4qM1JuQ%gDp}D&GcVYrzGeCKjaN1OT za-TJgS?>sXbdE`Z&-kHZS6{8?dCY;mKV47$Ety;IVYk7O%+7Bt40*dLnR$Dx$tb2< zlNpBF;E<iO#LU>wt`(Ouy*k)qrM(5N`XCav>*uMmd2Wjq^|PK`v$-eAF+j$G$uX4F zZ!poaHbmV<*t@mPXV^CT)m{@}sPV|5p~GmP2T``D%dz403d!n~OGDePayMvG=}ZsO z#lPvY@Xn;zblYCBd98YxuI4<sRk$?t>G!Ri$G5=m3x)Kk2l>hKI_vvNMq~wTJ9a5( z_bDZmont9<B;O7j_%<oTT)t{2J=m5g+eA9;L@yFK^`|~pO&P(B$}=b-*^OV#<vZ8X zY9P2&kIwwDXktBYuPUx7WKCc+&2Qa~RFhVl%B*#i-MFUcKv4s|o^D89!uO6;^wZdo z1mCb|YH8V!{1rZ5c5eC1hUD)^)!|s%7d9kS6%NmANUDn7eYRuW{eRt%6iIT!hGZvN zry}lBgLyf1>wtNWCT<c9>_9r|(p~+MTig<-`|K`1%wkMrd+caih5-GId>3>*CtwP* zM%W}^DqiVnkdJLl&ekO<0ac&ryMigcmgEL&kJYb=wWq1+(zC3Agl1<A)~-E=B^+wt zigTPaC#=!bCWq!{5)XxdQMQF%%p?y2z#VDl5x3c1r}84+{f1X99nOZJHOHh~EHhDW zN2pzHVQ+Shc$+q?BjL*Q#%t<Q3<+V_Z<i-v6>t(Wg!1?4jP+!YG`)QV&0SBzoH(as z43%C>hjlQRIqWzztS46`N+_H2J%W=GENGj;<_(|^H;~TK^r^YjYa>}Ktqwd%4I4?6 zwEEy-YP*S?l2&irPcLpFV-?ly_G%@L_Vn^5(uTfnAffQuN!J_5T>5<n(#NX|y-lLr zM~Nrwruq6MqxC$*-uXkQZH#5MDE9~X(fNakEA{V4oH`t13|KKAs-e#qQwDTyc0}s! zzzsXvau7Yts+iBabV^6!?H9e-xI#NfCTIJZ!H>kqE8C(nmv1`Wt5{&)am3W6%S^hn zBWWd3P0latNZJw|n2Jk<ky30f!j8<fs+PFvio-92!^%oKRQ3wNN{aCiaXAOgazE0v z7<^`@z#+K)VxpL;w@jFMl^1@EF}OJt%G7JeRyX$;B>E;wPl~FUGep(QG;;uHX@3#( zhTl?==1v;tMaH)g+TFI-Cm*A*+J|+_?H@8|8LK#zcj;eVq^py_yqS+IDSbix)ucib z{`HHct_t-__QerbLcQnKVB(?=hADK$Ijof~(?D<Xsw8bEecziHG^aLqeWA`6dif=H z`^V5e<uufXyyj-!U~LuR+EFBP<_WsihjdejG#{ZSeMph6EsWi+Je(0WVUTvgIm!7E z%???+KaUXH`98$&b>$@V*<Qp&7E`<|PIiLfiTad11{o9991`Nto#V2?^@o<xtY8+} zrI)KPDu~J*q4`0u^vq)TGhcSZW6&Cvm~<?X4tM>>q-DNjvO_*9q&E(6%EN!7bA5@Y z-CpG})KrHNdugUGQMWIj`lWHjXuau}!$Wh$r-d%K*L*T@QsQMcDUHy)PcQirAJ?Mx z5B!ps2bn9Te8(m^n8s8D=2JI666sN<hVJgV6vZ`5b;-_V0t@U)^r@pX!;cKI+agFX zln0d2%YG!>ZiD!_Yc+kfGnwj;u|EOyH9$M*ero7UdcCDb4bjUyaEMW!whdKw7_^27 z)uU`RtNsJ?;TA5F;JwIv7qjVMG6_A3Fe$@Vv&93$Dcs3=($RQdXOgV9!|a=3pSnvm zUC5Yrm1q#CnmMy?3DZO&ZtoOfB~)(SPPcR+Ube@Rk`1r}`2{`Mg-nIP$NN;%m5lKy z^4Xd4^$HxG=0hN1xL>lM!zHeLzNi0oFKXyYTD>uKFZ8)6WX(w^%qJny#sZ_>Mb6o{ z$da)XI+*x{gPjepfFe_sJtoEg*f8|;w&xDw#>z06@>|x4guc(h;wbJhSin<Tf8y0T z(%G0)30J5@>TY(9v~61~RhQIYAZwtZ{$yk)r_09FIhYGp>P_byAESWc+HZF^@Qy0r z9Z{R(2IhhVaaVGvY9?t(Px=#22cah#v6-oNqjmm7)ANYh)9dsTocLGxH?TEE&AoUZ z=#23$c`VZ9J>9cQjrUo7{diF459*5AH_*rc($+;)<82{*s+jHewKP3|ba3ft^(dPx zdR%!#O9F_e(#z`MJxR!{f6p3dMY&HK0*HHvca8T_=+9=nC>t+^oBBf!*qE@oR>IY@ z({^vh;ld*3<LS5En+jFu#yNrSztPDW;xQ%KXk3X^cMTS)j)UehWl!`wRF)OLzu9aQ zlpj=++&e-!?E;>5u*`mRqVUaW-idl3^(m=gU#95a>&#sBRKFy~izx!e>~l!DlAhHN zP0Lf?8dI?saNe}m_*g8Jc|XzC-H2anr^XF?c$rNtcptRsF0iDsvzzxEOkKT)#sm{D zx~v;ntJ<{<Cqg4-r`g0F3x#1Ox$Lxyyrf203vuP)^tJA!gRXq^0dtU3sb7ki%WdBZ zdH)_gx~&kfC_Kf}Z!7ELVR@O^>7o0O&*WP@hND)nXpKqU`ebvwIvB?}d4r5t+vEGN z@EzeifjwD_yOxWwFslJr$#L6FVtSp_JA?%H3ayV53#YOJ+{(ib3yE@gkBE6|Z4c6# zOrvdjkT#yLYgn|VW3tCo4q_5R<Dr8W$v~@b9qBIhv8nG$-|9hpd&fAMM#~<a)hi!1 zHsaha3MhU(>pi;!*L(VV)%(6CL{ldFi^3m+?RXOEt)KszFD*nW-EV9{doF6+hV{5D zRP_7ZuVZEf*-nHxukkPR5F@ZIx5JQ^EA#_-y8G0zC-Lnn)URxJv2z%h*8;W^O6oCY z9;%WkK`zuwtmb9G^@yfA;31vflk`>>HR5D^Yh8%Y7mEXg5@Q0ysor)iqoo*z^!m5y z!JcH0PKaIJynZYe0_9HG5KY5&0Zb)`ExZ|HmTHXn<=vbuPVoIEj8BWTo(VQZsNPr^ zg1wlC_P^L2`MXS(h$sI|3>g<*iX+Avkaeq2E0y{8nNL6MMU<VzEZT@f?8*WW!(@JF z4yYz46U8VJrAVqsmr09y5&q4*o%i()+Lp3*nA+EMvp4-v``C(DStF!X)R?-sIKER! z9dzklQ9A4n+jIW0#M8TFy>_{+z48!dAJQQMrP!<&M`gu8n*A#I+)1Fs`;HhzbR&)E zO@5Q+-MK(Duc3o2+Ehh9dyTv)Ez;ejH(w($l2e!Iq&@`4W0&ZseTWtw+v)W_WKW-> z!gORKMAi}e#AsDTZkQ*%ZtxZcrJ%TWeyKQ`@WKqIR?J@II%aemMzs&q^1dX-PvwMm zKF;uph+omtn2N!~8SVOsg&jKupQ2L-=$wA!fwU;#KAqm5Y>}5{m^I;b)P4Y&YoDF= zGcyuh6rDxm29Un?GgHOyfJ|C4fGqb4O<*I7--`WEvU_TkF~t`XwvsySlxm2LE)8yx ziv`L98WTvA((-wK(v(0lY;3kr;if@9Oky)^U@5oZTNd&$I`}`ahlej-AN3JRs@(sU z`wj76Ulzjl-$hLIVsm-(->sM-A2Q5Um9*19(ruU!b@oEk!@Twlekl`6K}>z|VKLy1 zSabQw>(+p>Sn#TRbcJ7v`UduDg^*<<g^nn1>Jz$UAc=Kf#AvzkE81LM<5kw#h%GH` z7xfP!JtgLybVd*fPzz}2)L5z)wV@0ROlc6~OpFA9d5t$|K@fRQCoJc-uFVSaRDJW< zvrEZC?OH{1zf>&8icVf!UIg`MURoPRM=C@a^&_P6@^mLrlc5Wby*p(=58x=7J+DZp zfoAQ;yrQHA!xI#$+B}hZ2R}XwtR+2$-C$4Ge#44U*pEaqD&JskI#6-}-Gof2^)=ep ztJ}r<Ce)zi70o+Lb0nCR=fq%Lb0f!tx%ro2Wi7ZKUdUF~Y@F66DO$57A`W6wvWB4d z@?vB6&hO(7+u#IJCG%T}L)&KBv?}?cvD<T5qUOA4-k4%~UG^}(x0emGxLUNcNQ|~> zHWD-Ev}A7c3ayDLgdGVjQ&fqBa3mW~Ck-au+qT3zOgkWYn9X}AJ?sUwRT`<b*&{Cg za8J5@FlnRvitNkRH#5l&JlK=?4Ury}b0+uv2AEBkEg4~Y^3^@&qHm^k2qUI@U){6f zAcQ+saZkRwOTyf9wGOkSGQ}R*Ent3;cBVml?<*<8j4zXON0UvfLmPR8qHa%O66YBw zZwR>ptISXO7?@iRKM0(7a*77J1FYOJast1?(Y}LWv!f~HlbZN#BbgT9@>QFn@a{<8 z8$xb63DvIBN0=w(4ctkqgGo16?;7rDjl3*+UD-|{IosQU-(GaoZDaTml2cpg;1Dvk zpApqH#qT2T0I85K(Wo3cpKmd;w|yhqR;~VOSS6;6jM3ri(Gv`n$uDE>%jQ`S8D2{- zg^<sii7oROgvRm)rqYB^^0t^@Hbhhyo=#7zL#<Sl)r*epm(o|mtd5mWM8{oq46|FS z>>BQ8OXHG17QMnBU!`|LiN-az$<O<N=x4r{Z#$IqBGRL>AR06ba{-w=(3q^1$O8RS zT;xH{OG7Y56@7Z%MPc(7dcM0PRA5E*u9X;b`b&Kh?xMxty^y9&R`iYcw!!vm@Ly-r zw96EIdHdkIo*gg1q}r{g-1WXFGSYkwcz3nqswEx}%NAC5gkPJ)y`DxZh7px6lo#<% z{eqFRQ}A7g6W&(1TFYqW8A~ibwLpIpe7D(1?aVqi=*fjN!7yh*AK@10B;2atb}LNo zj~cAuvVRf7MV~}Jt&nw*b`HM#RhYJMmP7G7E^l?_$S5*WFd9Uc3@4MslJGuiNvo7r z4ktg8ujsxJq>E2p`()D~*?rz70sExhiKF$qdp4_KWqoB?vj<o~{^i-JhBGBl$C1RR z>!L@mY*ykBUlQ+QbKgqL%sotW-xB#wQ*+rRDY9bvh(?Yip6a}SM$=%~Z6gGgEZxah zsLFY1Mw5f=JVa{lHVZSoPjg2SU(dYoCzv2!7s*)D9U6$PGeQFHo1A6W87Zc8dLLvA zUGhi*GP;OlfE*k5J=WOEgoPF4NDao7rWUfFPwZjm`8D6M-(_(u%%ZWy-?1z#8he!v z8%2C2Q%}%^qllk<_9mu;4iZC|G<OsR)4Zt}wE1X~Dq%`AM-x9u`vUsoXcFMQmW@JX zgIMuQ4q~;By+IUtDRmAflRN@WVWu}kX2YtzQ!(C0$s|JaslYycVmR6SmJmVW3~bz+ zP4=?u5bLVt9c^mIB~>OW#mE;4mAYc;uNXSy$a<|5`e@%y>vjs$!CFt%ZPdkDt_eRh z)`EYYE{h=9evPZOgE3zFz(yn+<Hb1JdKzkQ?vPN&=5p-F0qOrnbKf8n+68=R)a&hr zvmJ9A2<~PX^;57c#E!WHhc;(uyElmkz5Y5`@0V8hV#G6TUyOL*k7C59);t?=TGF!- zw=ZH5C$u#02ZuPLo(b{`8!i<Ax9GQTl3=gsy%=zY%Iw)jsX4FtX0z&4u3z$`q_e<d zr?F6BevP&pLpsWjc$-xLB{Xyl>C`vq6JwHc4VxLrWTxYahZr+HV+;0dJq06hlqwY& zQYl3Y8pBXbeNgRDeny%*hD3%M7GUZWjM=a(p%!ImD9mF8;FpYZ8t4jU$Kf=Jjqat( zneUZVz!vBiWA)ZB*YI<6^jP8(AaK62i%>cm!!*B@n0Iwzr$+OCE}lb1(kw)j`bP)4 z0ne3xo}pXEl3p!@K-nEIIzK^Nnu;FuHsWe2IL~Zvb&k|P?2-OCmUL*zoGgXxAxNNV zD!R}~<49LAs<KjR7|xewjw9_lMvlh%dx*?_CbGTp4r5Mb;=DvLXpowTQ_?$QiAvKH zakiuATht*wCz&%XRorI68DgR{Eq<Jeb{bDSbpoOC<}3h<06g}v!v@QV?@S8&%K|IX z3-=k5O+m8D#jqc%sOH=HanLT01?RifB1?8uE5Q<%W`Ek}V-UjR;R%5d1h|oYr2rv^ z$Sy7oor1$o#%uG#B|lXnBkj&FuAh{2C((Y6=%Im>{^{}LU4>!eZ)WGIH)!Yt(n%N8 zA7yJ#$FdSskHL^&2)KwgI$UOp_w*|{sBytC;fjNN7l(v8x*m8bezhWXzZWY}U@neb zeqeP5_!Ul-;%@Xl=q3!fY)ra{t0=5_Z8&B&INgX>V-1mty5(GW&6qSrzxuGYv))l? zgX?}b=LH@UtL5cH!4{jQf(Wau^r>4Q;}IcyBAU2gl`QwOZkbV3YgD|f#XYSXX3zVm zH_*uwiH9n2BW9G4A4A4MU_qG8D1|~v%w!A;xIi-}l22^q8SG2<3-y>p!rcv*VO?g^ z53r;62ex7`T%x66Gdu7%nlOokIu^BD8e&XF098?IdUg`&qzZDuG9nDRs0E@z&alXw zcEfEbrie`2a=W+4`iKe{VoG7*u#dk&sxG;X<I*INoX}G|)4}FEaEsKt<oY#Mn(P=g z2@}KXP1URNgtz`6x+aCaS>^l&wVh0oT@qOr?EUdU1o2e4K>uXKP`Y(8dB-d9TR~7r zmHv)jDz*zAvu?#&PeapL0_g*(=55lqdG>CWAY|+-5p6&E;oGE(u54$MTTE}$7};6B z)EfuH49dP1I~Dy`IN87B;;1REIncC4DOM=i6UO(nc+4mySjCQGREDzA)G!boA<BZo zyC%r@+4BK;8QbPWUDb1AQMgf~5k+<MMR7wx&xCWU(=K@H)vrP@5P;3Dn_^-i_wbc| zrS93p690mmL5Cw-YhrO7sqGZ<_9#83!?JC#+;s43Yu%YV2-mn){LR>9b^CheFP^Af z0tU8OP0!lj!`b%RG<yo^rT9RCNq<-jy*33sozS4RT><gw18-@^%Q(yO3yUwynntXF zpEII$kLYVtNiQ8@@brB&^JhUu>ThQLD2qL3l@w<FD2RtP?os9#-I^xi`alP_sC_$* zlV5)0G~2Sb!Ir%-8MDyxfd*UxIKs@U4;GB8!<@*N4j=}DV!iSxm|<_+tYZVdAXjAw z-;FjW$W^)E_5&LZHsJ=+Y|IH0>gvZ@`ngrW7RF7d-@)L~GJ9Ast|wtfF&rU8d3)Sw z8&o5;2>e+?g`xxS^umbk%|_hlDuf(_ix(`^U?EuH8e@WQy&!uA;g)!8CwX={imkef z5Vwa04koBAur4$iiKI7H__8gSy3M$k^2lZaoga)IHw8lz<h+D3mi0B~oxJ;CFK-hU z_}ktGV@~W98705W6qPfB_LIt)vT}%_mw>Ga329kD$%pk}m@D7U;mi@yr?ZA;j@(Z( zhcNNk7FA5F5Ep&oG7mw0{tAVyw6g=pd|W<0=A>=wU=v>&>;j$8E_V0pWXP0@TqVU6 zqLI#<hV#z6@Y+Ea#P+i-UWuJYNC|xc`Je4y71Ev4h`%I(qnD?V_93g19xQQZO%;0t ziFP68yn~Prw`-V`XlVzZh=G~q!L-c}4v4N%ABnC}F%X3@{I-`4o=&_ZMIGon(}_xA zUO`i)llLXje?Aqb+t14Vb__c}5sEz>f-z5vUD(9RJ9S+w_TYMvoqBJf$n%;XTVz#0 z4E@93CWw-Lm^EBsf_Av56L<faDo>MG?M(_t`|!5Z>s}V&M6oify>Lw}Cj88^wiqp0 zjkRQTHu{+)dR$A|EArW*xk^j_(h?u<yi-5hoe*)i%@c8NL_v%x&K!~Ug(%56THlQh zoIzZPI~_ZN_&MiAA8&humtU_B31wCOE=`+3+UfGrj>y8S0lsj;@kd_tkLsG$kYfBf zag>1hh0}ewIvw_{=M`~s=HBNS_q5XF{J+N#Uefl#v*BmdBm5!Xr}EYM5RqirG|5Ci z>K{egc5vQw#o8s+e}MTwu#qka)4uzNQzoIpC=@ebQ_)q2%ERkJ7Kowslg(WFN3rP- z4lIj{O}`=ho)Laah2Oox?>6Cgjqs}%eisS9QNr(7;WtG1?IrxGg<n_US0=iD&*E3( zw_|L&tMDPi?>~{56WK(j`W=x>_3LJ?@b_4CHH%DLA^esLzeU1tp73iBe$$2DIN>)& z_>B~PBZS`|;a4O4s)S!>;g=KL8D(k}elb^e7CyM|>6j?urF;8`|B4ew28C&7<=dhe z2i2r$Qx&i2-}Or|CM+EZySd99zIRD<I3V2~Ea+C{6*Y}LW?>JpzKYYnw^*kAyqQgA zu*;<`+~BNcFdfum<#EpU(2)Tdu%*!-yC@3CKx~C-(Ik$4_)y`=ox}YIn<xAQU(RJ> zA8vXd{n2%${p#K4;0ExG9&_}1$7ut!3ER0vZ_5nlgY~oS+VtpM!R=q~lBhV~vRv`K zsqN>Fw7k89jACant#sdxnxhXLWyiMg(4?R++A-cM%zM4Pv<&2yjp5=;7jn|WGq@k* z)8%7Vjat=FF>%$nwb{Z{LN&l|r71-Agq<W6zsq)@_#}sFw(G<zf-Re1HZ^1z<^Yd- zZ4u@T&Z^gpN$l<53bQj8DAnP#{Z67%m~LeEn2zgtoWyU%IDNeDa2OPB{N=W|F``m+ zMN&bs`%)i!qHUw_kopWK+V+ZeQlAvk>=+f=Z%&)cA*Qy9JD84$@o8=gWO5RW;H{y- zS-=W&&pd?twNpQ68?O$2tiw*kV;lrLb{U&WFcv*_QKph%X)di&x!$ryZuTmdDXG|N z=<#U-*JIXUl39#yX;lcP9XE^ctMpJ(E8|?wCW|3ACc`XnGS_3?N!V#`<mRXG7ueSw zZcj}Oo`IxC(!1og-d-V+S7yZQNM|@nAeQ<eayM93sc$B;n{RLWWZey^&rxpCuT*H0 zrMK9So>uC>yJ`v1y9zw#Z6@r)9?IZ1Ba3OvrQSO@X;7W|gWn;v6cguuRYd-^yad z<gA>}xY+b1B4Sqb>!zDJfjOLnAlnU4hnz?-O`ohsvS4$Gpp2j}K-M=FZ#OJZTYz zT>vqnYmBv?s*+lWN6f{N)yCx<at{vhYb|*e;$iPiahy$T5oRfxE0*v&7Qs6>=DF;* zat*AhvH>irrP(lL7n{0MBnFM#E@GgW@j`hzM?{(4lvVhxbp1}4dI|)23lYMqs1_DT z5m$%rFjuMhR^Myy;<vl)fh@V(13Bco>R@}oW96ZHKB~Gw(AvEEFm0Gk21v_SHPFC0 zSc6ZRM}pX<f7U$WEeXFwOXiVj(}Yg4sznNHRzpJpZe{X{h)8Y9!;kQsrp<cDA^LSS zoK}guSa~NiQj17Px7=9opu1x6g}f?eOYdOjrY0>OE6&@j<`}T7rZh1c$?eLZJEO4+ zVqQ%zMw8Zk1Jd&qoXxGY1GU4k-kKe<Za}XyP=>v4bG}&>&6Gn+Zl!H+WPVX0T?cfy za8I4SuNVAwrBeSG(oV7~g^m`U%jrDfnMhZ~5J)cXN}xN%mu2*%_!39&FqFau+I&8H z)=_`q`Gk(1Pdp@79@Dw(+4zvAuxI-RG?zUG-lJu}n>_F?|A8#Xl%Y7!$ng!?&<(p( zXF+Viy6><uG{aW~b3SL8uR3INH<Od>G{`Mb!&mnwe4Syv>ooK25Yn}qiDSNT1-|gz z^g9h&fRqYv(JAbCrHX#So{cx?diHF8ofa%0UBhhb2BXrP+qN4KR)9C;R+?#pqKrj3 zEo5^bL=4Li<-X0#|1vrf!DmUxfI#dImE2QT?;Zf3E0<`OSY-doMLJS=o~Lt#=UJL6 zJWtad!t-Z(TzHn#KZGZwIv28V$7vVgd6bS6o<GvLc<Qc{<tuDCP?WCN$tW5XvZOE4 z-EmK?*}dc&q+7x)N(=rwnE$Yl#mwJ$Pp#U$nEC&ZuaJoTM&=(9a)9~g-cvj8KEV8o zif1A8YDF&d9TcMPj=jI^dund?((ldAjk<%InfGUkZ00>6B)PlEQMMJC&T9tm_6Jro zUtY15`SuBM?QUu&+j37`k=Fvg1HVgVz8Q)w%(qvFdv}zq;hwrY?>DSwr|#1;-!%#Y z^X(RLLxYmtcu!rF7hSz+1N`&(@q0O)egz`f$WXe5T+kqbtb6J_py)nF0(&i0CyV*} zhP=)E*Q4q%|8?-+BUGLY#d_wi4w=aOGf{b%KZXBpq55PfGMRsekPXa#Ey|ktuYvzA zUiX2_76hzi2yH{wF@%hJ>KKN=1K}$~U<3aQ=I<WTmid2nPaVnpSHu5H_)Fmb8S{6A zaGov>97?~ZjsV8gG;nY`FbFu9&M=txUU&mIyy~7h2p9ub>QDyR{0DjtMX-vYGy_UN z9m*&bH2^5>S1?yh%BxR3LFI?53a*9NTeq_@8xF2B>Z7Z#EJtJ5B9!wc#d3x}h~Ymn z*~*rqr1t?oQ6FYnQ<G?Nv4gG9DeGA%9Tuw_op>PvESwuY1H}nc^8xAA&k!h>4Heg+ zO}&+@K<uqbSD3l9BsOgn&9}=!O&br_O=|=_JT-hk1}Spbbirpbz4-w|{O<V#-Hl}i zRI)KI&z4Vo6}G<GJ{1Rw7%?Wy?XA^ms}u)eB527tSxA#a(z3Yc!-kH1KCFMDQ9o1& zpDcubKCH*<H6{p-()tgHzqI`F3F^O?6!^ya2Hiz(-%}~jm^64lO&GGW#9M!3!1<x4 zgR>^|u)7u1+)fs9ln(faC~e{(eM+Z%MBbce6xOYM*cws0T;Z%u><L3fJ*DhMH5FvW zs?J?HExsu(WSysrk=yOQO8OC=Mn8{c$9^0-e2qD7${&K{{GA-m^8Gut|Cj`N4J>xO znmY8yU|!yblg%`91B+VR$_i30Vp|8Y2Od9}4b7|R+>ePTTl#x$QQW6%KSuwd+yC}Y zZx^z=iz{*IcK;fTBl}*zFg71mEpEzfPnD5tTbw!ax4lY%{M)dnWY6l_9l`%5^LK~; zkps-!$^omuFw?Oi6jr4dpA^bv4p|P}aHb768lGz600t`>B`ay1^mU+pKOvpPkDPXN z?kA*g_o-j{CEMM~3btgyBxFIbPySgjq&%Ooe!_|Zw;+}D{3m3rlTa?RTqv^1C;2>~ z{g*(38;-ab>P)9BfsEVx_()6y1D23CyNbo4P;O(4+NSuz2?w<Pr(sC0-Pw3ZF0mul zrNdYu@F^*O&{m%!JW7TKX>|0bq|bOETnMDQ8cuy1;XP*U82QA9!)6XYJ3I>WUJDVX zrJ*E@X-Ya$D7q-Fd2aQAU8>$hipeM|YFvq^*jw1Fm>rOU(#Q1jr=)|YSbD|g>M6UL z_D-0zt`#xqzJTZ;AnFUv>FrL?(K-xX0pHTOIx<C`U52&gE?TZ5GvuL#u-UPl_KhP8 zB#8xdXB=h*p?mYs#*q>tsm#pJT1HwBY2?%s`FZg~Nu=q`EAo#f5O=(dK1nO}xb%}g z`WS7MNES)cPaeu&l}M0*$W6b}ACs7<>CQFuVG>bGBL`OJw_8rM#3g<7XV;STBNrbq zr8xctd4jmhwKOA{ER&r2BHt~Ayh<eD=`>;mnd6xDnP0N(2sTOL4xXoFD<I+<clmev z#|kn*lD3bArji5FxWJ1P*5xKkcID6+E6FrzT)UI>$Ca3N#I-w4Z?1%7r1^8IT18^J zMNYk|jgZ;bk2ke_0F?<P_2bZ~VSXAJ{VOI^t`BTwBjRDih@I@5gsam<TC$3aZx*-l z+u`C)dR)5#+9?egj@!72PDmqp&37gFC1ZE=6&6e5A-YjUT2oCr*(6~6oKCh%PVJ;0 zu7*HIH0F4#NxDS0h5CO+&a{afct$2jB=k_M<Fq%cB&>(^MpNGTn6~4|uwIc<A9g5> z_e-ePQa>5ukDK?K_4>|9W_u>G@pxMy0&UfJDN~nZjys9Hb{@CABCizDpLrYxJ7rO? z3=-Np4Le`#n1e|`n9eF1*hwiiyCZae(S!`rt;cxuCOK8?p1~c#-mUOG>r-~jzw)q> z3d^+hYy+vqDJuBoET%UzNP98h;f2o!3|stcz^aUA112q`eb*3A7EtOlE81;{cHSdN zib5%TSH&`mjm-|zwX9E1Ok@2rQXI#=yrXIQ8uC`}^+KCl4r_2+>7k{=l#>vVUrLnn zEj&A~n6o^1^cr)_$^Alyitf<zy^nTWOM-1z{Q^0Uipvk_ytTyLbu4S#A*0O^&A%fd zf*5?v&I2@SEt;5+$ypPTuL|Mfh45W9$w<?Ou#F&W-7KaRYf0yB!pGtFCF$uy^s~OE z-_lHrO<++<O5|438yS{hJ_~7oO4JG=OWCi}cPQx<Ch}qgc(Le_r1zutoMo?RZVM~` zB<bR|9ow{Haf`#J$giB?Y>A*uL_Xx%v*~q8R=-*?<&H7I{f1J=TvRu6#LmxIC-IC% zGu96a`3b`<@bNH@2B~&B$j0VazGgF-;IKB2*h*t#qiAG4Wb4(jed*+U($>j7JSJ-1 zn}dg`-dE4?SI@}rok#W(o0%WXT*weT@^9`Y<u<;9qoWt9h7NjTj4C*4`l4B?F$-qT zniaJmR;B8zQqNE=w0z<|>E3V272WK4vsCX)Td+_y55cCz#?GEKFA7-Es)g^&j#bTw zes9|BdG@&S4k`pZ^^CsoT{vy-)0kAzGgS*0$3&^ra~C0rxr-ROfIoKLyYr$ym?!#n zS~z3c!fD<5sx%*}=YFUfgAl;h&7I+EFCa(Fn>KxJlr>f{p4jQLr_PwZaKY5rsD+DS zrpCVWUetRm@~7_e7QHu><p^#9o+Wo8{xf)D<W^KJZdOC%LcsJ{x`xJ1cwr6<{+QX& zSP5WH1|P!hJb)qZ2e5AjI{+I2>40SbMidvveh8qkk&A0=40%6l-a^Lm+4J!K{<OKX zXQ*NqPFonInzm3C_2KM=z*N(f`J`X#EsGi&ivV{3s`ncjCjo5nK4B5PmQMm{UIEsr zJqySuy1CKQW~gRPTQqkeN@L!Pxls%7HG6K<3`7>YFnU4MOFS1Ym=>d25H(}=f++36 zrXbH3D{xrxs$*51q82QOUI5}zINW;+K|JDrPdm+ZQEb%T>H-4MPxcWH@AnqYo)?Qu z3FzJNWSLzQ%e@yBqeW;gHiqLCOrvY}k!6D!Ibu=+P#rX{&Hd1xoBVM@BZC;r^J_eh z0+s>RTihI<KIt3#aYU_+o;P#$EV^tz@pIBf&z;eAI%<DcR-a?(m-~sDUfK_)9n3@t zWi>QD`Mja=KHv`ECg2+265uSL9B>p+0yqdL0DKMD0mudz0P6u6fK`CyfMtMB0Ph20 z0CNB{08;=H0B-<Be%_#KR8f@czXqt||6bxqTlcT!T+H}%mithYMG&AbpeMi|&<W5H z-~ng_a0b`|Yyb`G8yX)2{sQ~~s0361E&@&iP6B=e`~diVJ)QI&`IJsLKxXLvRaVpg zD%-zY+5Rj4|Gzt%|H}Wr^8YtE@ZV(P-<|FJ*Z%*_+W+rQHvcRC|H}X0<iK114-=>R z50C}Aud$jg0LZbT?y?*!<HUwWZ#-3i)_|6P<^WrO1W=dM(D)Eg4Y&=s4!8_B5BM2y z98d-*0_+ET1K0`31#AIi0oDT20VzpX;3VMXQ^1D+miYe`u$ungCiu7XbpP`lKTm+} z@3~)KDTcXt&ICjPCIQ9(MgxWd1_Sy7UIl0XegH2(JAfPD6@Vi^4&VS!kjeXiJAj*j zYk*6DvxyC~>KJLEdy;|(S0O@x2H*$q0<;6T0bT((0^|S=@MI+-1l$4K1Y84L0-Obu z1C9bp00#jDfUf~N0NDToU_BrMunMqzB`V?MWb9V`Yk*4l?<J13b^lt<#f(p9xnG8| z_yq7iAO<i8Fat0JFahueU?d<E5CrH8=n3!#bOLk)cmP@foB{R#8$d%UwtiCSq?2q* zryN^4|0=7K|Ep~Oa%KCk{Qv*%Z2l|%|H}X0<iLNEjemEx^I!Y_H*5dDKiT}R{QoQe zf0F}m{Xa~c?k~rd&K<0#?*j&7rTorESQ&o^*%&-01I7U&0K);nfIz@&fbM`U0B?W_ z&>GMZ&>Ub3kO1lyW9tV{4Y&=s4!8_B5BM2y98k6x3!Ean><4@UV2S^40juf%ZGwMG zPxn91@$&@e{+|1tmSV`oa|<8~uojRGNC6}OJ_UUEe~LT%n7GdDjK6od#$y}f;Un?H zAufqoL@m4>W8%<?I>aF^_Bvi{vs-GR444@_Bz(-yjBV`FT;gTjNx~+?OEV-p8%<J@ zrCpRFl^W!tEQwHsBGk~WrJ$5`SqE7Y3#B+qV(O)yKIhIuxXEr-+P^Z=!}*=_p7-m1 zyfgE1v7rFe1T{dl&@reAIs{cfd!bTjH{^o0LwV3v$O&bh!IsVyEhAF>E6s0Q!X}Sw z44s8ykZcS-hS+n^K5QmdLN!nW6ov|*QfLHM?*udhEkilaVwAA2K(a5i2RZ;f1~owE zpf{i>zVB~&9*=ZD*Pp-rK*G_DMxipO8X7>s7&HWa7n+4Wfz%7Q)S({mYu%AOSG99S z`OA1d<fWv-7M7T`O#E(^ms#?9!8K6!@6qomZ~U!_@*qps?`X^#i(1O1@!!>fH6|RH z{7~C6{LY8kf3JDSlYIUz*jyhBG`7fdXbSA{HhJZ#JQWOD;=%g*21J9W>O6Ri%(N#} z<du)Kw^Q)QR^-ri?ZVbW*Kh!!Q&1-)egLvVFJ2qIeO=p|vN_o5_nl}h44x=*6&LOB zoo;Sz+0k5oGV<WZ+CSuXrzaAfP!Dv8R$^B5qz|Y6kG55dwB6E*BZUjv`;ouCq^*lA zE@<!Q{^pZv_{|&IKWS?kTU(SGKJ;ttl=kQBwEoo25|`^ZhMydS_&bYypZhW>mrJI% zI!ISh<n2W*E33Y)rLL}}@Ck2=CvYa@-SI@xuE=|f+C%BVGr{@*4o!ViYyA_!$TvUL z&S$ml@VB1ElbeNgZT_A2yLO|^;eY;AdrG@U9>!dGxbmSEk2jcG`RGHr4<9{}`;BdF zk)1cS2aMxq6Ii|zSiTcjz7tq(|1cduopNXoR00)2g-||}3vGe2p>)U&*`Q_g@foym zCSfFc5x4+#LFXY!`a{%#{IgI9YJq&vmnZergWR4-;PFUr(Cu%obDs`0hP-Y(E2>sw z0dJkh45>)pP5eo3r;$jspHCzjpmOLwNI|ndNF>If7}NpPLVKXCkb<suCKBV&8_;>E z0on)UL#fcU?<W%f2E75DhZ>-LP(G9jT|1XZOrFE>a$*2qpT5@4W#V7+01P?w)zE7= zAKg#@s)BYyPUzNCiNp-_78HXzpyN<Eln>dVXK=kt;K|>9C=B_a3a9|GLj!1M5`7Fp zJy0AmpI9T2k|;AG`)+APhHCJH+yO71cy@aNji+$`tEReO$Sn`GVtw_tfllG6Z8sK- zx=_8(d>C2^-QKp4Svl0$><xTg`L~*L2OH0NmA|Pj)X*AecDJI7K%>X&Zf^CQZ1Nhx zWVzd2=iPO`%graJ>sx)^fVZXojQez5pjn=>HM?pos(06U>inTEb$G(x*!raroM^7| zuY{6a$Wz?j`nskj89BT48J|xx)S8^L#~qR<!!hD!b{hCzy|3|0jeXU8g4W~X$HUKF zw1=mUziRkKa%_Q>83w%8fhTME@l-lbjXUI%g(TE;+}+T0GU#(RG`8VMb?E`4Hy_Y8 z>jb@l#=54@tNEK6>w~A&>N&+^ljqa$ak_HezE~?cGKqiT46lY8R|mElw4RD&)a^Ep ze4FKHxP2=ZkJ0$WqiY_%)w6`RwG8N9=x#_}8=%e5J&+T+16m7hg8m3v1IeERWJ2p8 z8zirdNn`#@Cp(m3i2}DmI+P1#LGnsV8uMp1*$yiMlUGX8m_KtYyak*J-DQd036^>? zC~3oP_OExSeex)O?2&Y@re6A%{_1X<4HH;@?y$aB(pG0-I`|<C%Pi$+$N*(V(y(+O zM@3%Ju}mlz>PWfti^^l^UB`}m=iQIq+;*k=uJ>O*^xnHep6~wE#IK&pch4Mt^vch+ zU3+>tI@59dC)$n7`<Cv1=<Ls%ZvFV?ACKoc|LbpJqYhvHnV+;rwGVflSpLDowXZ$B z_QRvm_FvvOkbmXb15?l3@x(XU!hiD8boBA*4L2YB*K^UMXX;+x_1gQ5x3eSnzN+OI zDVX6N3}t+SZq7XeKmQMY9*($<10sG7?5GYOaq%nQXRlfH@rU@z)ruc@Indr1Y)__c z9s<e#4=J$ZKZg48MG4|eONG-2lw_<{_%Z%~Vh(=tgxXalGA~;OunYNVmImdb7|LB4 z&_;#NP^#Fr;@i!Mj9KdMM0wwOUB%=|lM&vJ4t4w~b}kCU|33UOG|-NY#1F&I#(-9i zz_W<Q?7AvtKK{!kRch&QGx9t3>q@T73|!<N!5@(Un(b{={fJlL2$@%!bdZAtyJbe- zh9AQSS2}Ef@355rOnkY)@RB14SIwobPT)NRiqmwQ#^eO9!tcj`<f@bbt-OY}zpC}E zH)_zd+%Z=jN`=XvfV;>8xPp7g0bI#3@*&*sedJrX&8EoJ58#B6#ZFszncR=NOQo9q z8tM|9yriS-hm|TI7adWmki3X^H#r8|OSW&rnIum&VnE~pa6j1t9w0CHlo}*Ij!R`I z)kx018_pOD{EsU&PVPj73GyEBB)J4RQ)DONm&y7AnwlnG+^wk@a@9Uf&6112b1OLp zI*!x12A(IEeX7&~c`JC4T!@?{@&T}1x^ns|vv5g~m%wQ&@oXJeOERYyoK1G!p(`gC ztAJ9yaB^6X2b)W-#C0yd<aEK7l3&JOVI3r2#NA&>&h{(SPCkV%P95ay$d~%k&d3ao z5INk6yKsh12^yLs9|Fh8GpJC3fk@@l@1bLIH#(C6N<0kfBTpeGL=GXRja&hiJjtIx zo4qDu6%dHBKv?c>Nt%J}BQL@Ble5?9YJltl50VdphsZwg2)PqHN{)fY$fMwKgU&RZ z3GyO%lI*}rFhwo|UnW<8r^z1hJURR{t`)KmY)4x%nLe<CJO<7q8yBC&ZAPaTTt;r| zQmTUd3&d;4<v+yV+L3#Z(?+gEyo1~a?jh$tr&N^u8RCQF+~;vEtkVs1bO(?$$%21J z!DVtOI8F|A<BlLNBCccBGP9QvPa|Ih=aN4L7m#aSz#>hq{*hAU<Zu26=f9dxv`490 zazBzn<fgyFRZV^iIi2LpOSquO4frn)y2uy6J>*Zoz2rB+QSt|1S>#jX^Trt75c^qB zy}_mi$+h4SvJX5)4uL1g9pEW)7(7kx2G5dv!EtgFyg=>;8%uPC;3#|&kV85Owv)%f z4)P?}NxlruCC`Ej$n#(qc?n!XR%td>Mz({?lWeHYPjI2JK=`1AE6F2>S6SlK7Ot^y zt%W@n_F1^e!u}0h|E1%QrJ&8i?H2B^aHoaC7Vffew}pEw+`GaCI*zV5;FyK`EZlG5 z0SgaWc!+HOHEu0SeAL2Y79Izi>;EKRf(6<5=pepa2XnAf<n+&QTawFO$2%KY?5u^Q zj@0elh>MP#lWtS<<bK2#R^x_Rq%(qoC2|@nER!b@R~VEux(wFIaj*<l;(4%6^2AN0 za>&a+)yy)<DMXv{xk$Jgw!~!Ie;t5Q7WBfFkq2SR$rG>@<XPB*WVKOOm1HNlid+Vk zPNcJ1*c$Tmt)y=bXp>F(3>K8a@skgNWl)k7hV3Lff0Ohj?m><WRyYP5C08Rye2IsU zBa;vwfgK`WM~?UsH#(3cvlC|uc9Oh(Iq6Hh7df+x$6@Elk0VEX$>~SV0(lH9M@Hgy zSe^Wj$QR%23pqGq$@M=M!5kJWAxV5mnnh9pISzJ_7r}CfC8q+moLr9YFXEdWJ8Y_r z{3PNX<Tt^6<lfsj{{wW!Z)3*fs)Sr@$%fA1BesqY*wW}lZ~?hc)0B%`1+F67Y?`Ve z7lAv-$5J#ECXay!gk}A|t!rv%r2_tQ^%-&uJWH-h#fL_6@g`lx$wT0I@&I^&JOf@N z=Q(t>L~a5vljp&PLZZxU4o)7qWF2Nneg`ZaN=_+k3Hbox;!C_5hq{T}j{gSTPtHu! zRCFZ=@fi7IunZP;4K)fFWI^G290~Fy?1&{UdD2-QI+HTtA@B@&4tADYf{w+P9Jvd! zF*)Ijh&##nKZjMGgX@1Gf(0ztyHQi(ONDWCP(z+Vyq0_wEQiwU0M<`VP1jV2JcGJ@ z<O>c>^^+^Wm&q*#oM}3FU^xxa=pt;KjQ?*~$+?oaj<2E%mbm0e+?j!u5oN*!VEM|L zjALaa=fdWYd$KeozPbLlqk@YCVv8*-U(ThWa@bO`2eypd2`goiQw3W=E-ca1LGo6x zlu6DE`jYy>p$y#rJ~~}UY9b%pi^E7>Mg{3aD)hjH$vJ<fsV?%hKh?}K$w|w!sVL)l z;6AboJU}i751NeoZxWSfSP*{@r+_>U8z&dy8@u?@!7}Uu`M$r<)FQcaOf$<QN8bGu z>Ie^_Go38&es)Vd&7dRiehy1PHn|g(bI4J!3`80&g>{h!F^FRF_&c~Q$wSCLNETaZ zVcCL^c8nT8HJuQw_)_43ttFq|ubIBYC(x+m3Ae+xk)4lfrZ4f!$mwJ}4(=hxV0+1< zu+dDe|I=`!P%00=_K{<Uaodm^j$qLs7iZa&G>{w+zEWB8$H~uBX&7uW=OFT>56Pc{ zogv2#8(3oLT=)*oJ-G%I=E-f~WwKa>uh-I$a2ff@A8M+cTn(-uw}9oVx#WjoJ>*%$ z+k|EPe+!NjO48(Jo9ZGvv0c(lo<Y3F67MC?BObNHW8`JT`z-N(a+(u&0l5I}8-sLm zP%uQ!MZp-k44Vey<mKly6(`%k^D8;snvzWinScxVc1SoMIS%q9xPV*<mOSL(^B+2v zPNaf<mrYfY!>}ImD=*-1t;E4nCi&&amt76vDcBhK3Ub7kcolM{$sX`5IRuVpbNvs) zabg!lDtwH}Ib{4hP^y3&hAku)T)+&-KG+hn6Y&c2Bv{I%&3Ux%CohBB$PQzRO-aL& zR0x)>7~v_{0rHbQni?b@2al0u>t&oQTQ8I34_?CT$sV++-<fRl;@@k^PR{MqR2tY^ z|L0Ip%z_epWG*3hflJB9z_sKL`Y|(dDY%P#0NhQk29J_we~v+ui?B-0k>hV`hKkeq zE>^Dg&B>8E*5YFmSw8i2l4YkiOzy)M*DkVrlj$bQZgCG;c7l7!vP~T&%jy?18TX&8 zPJJv;xbpkSva35l&Qti1LYCd#A+l`Tj*xY1SdEh9yZsnhHgCttva>uv&c^%3q(MjC zbf#7+ti=*amTxT6WZA`?A<LHXELrxq=g6{IA1BK``8-)R?H9<h0l!GzB=7G_bg=XF zw;9>C;f(B*9hV{<4&DAQ1-jF=F&7u49g?l)P4dfwyCbzWz51v4*Q#uirbk9?dTyj| zt?qa+MYl&5ZF=51yaAe5vMut<wfbf~fmg(pqI)8!<w)OOx!-;C=p*j^hYmdQaFw)~ zAL&WatF$%4AEoHqG|95Rn5Cx<XRgstnQq!}I8}FQ_Nsk<xlbgOoYHBr{Ycf`a*>3x z*FJLesDi|~C*se-@bV;6<`bD)t5@&bh@r_#en}&*O-Y08^yDjpHj_4M7){-68?Ich z_nSk}BlxCzV_ov`6M69DMw(tE9i$E8`%s5XMqd%}ZPI&$T#@BXdiyYn2W;kej&<H^ zQ=i^Wv{v0`Q}X+<_*R?w8>I4WHuJYgFXQ0JZ<1s{^7}M-;iACmijNO4a|o`3O@n@u cNZ`s=_Wy<bPfaGhJ!RoeUErxmd$ykQ|F{E6r~m)} delta 62279 zcma%k30zah^Z3j0UPu591_&TZASMW2@vhb*3L%21sNhu(>Z?+FC>E`?t>1v*f#E3N zsu7Eo`W2A0T9hK#Zxv7w#fw_+TE#}B+9O!SgG2wb?<J!3=jZeH^ND%cnVp%Po!On; zowsZ)k!~%Px(*eRL}@Zf3>8yES&pmM?B1kE?j$5+PRBc$NrU=&@n{bx%FGuMXnjDZ zTtE;qfwf(kznHL>br^T@iBiE#t-Kr5>Nc=VsrV1T_<+CoiCo49eB~%QIcHwLB)9ys zU0kij56KWrqdTDyBx#o^<-t%_lJ>wn5BQ{|WhxaC!cAhGGVhsO0vsiGha^bnY2*Nv zNrL~$+3UmC^~<Rb5@-`w<~&p?gN*$|q@cf^H1<y=<ud?_A8@xcHC-@nqudqhyH2@- z#_)nYQ*W1w{?;lLzY)rSe+aV(n5F8Q(C56KJSE!7^A!&6r|gfm@q7%Ul*)lSiJ_#B zC}o7iP)bb5ML<Z@EQQ*9In<PiM83)Z{EW-%yp??%&&?{O`ZvC^FQ5OSa_{*ogLm>@ zq}Ur-!xQfPR4o&=bbk>uGiWFZ=dT%UZNTbLDrOR7zG<Rd06Yc%Ju6TOh|;41zT;y> zi=QTG-3BH|wHi*YIA_$x@8`VmGcl)%^(T-<5D=z!x*sUjY&SCLraDgOK(7SpY>lHd z>7>1he)j{3VV5r@sEA<;)T-S)LPijT8Q3GEk%tlmo_u39+A5eV+8gjtmNfAZx+{ow z8cOmAo2JDN%@eLijlzD<fj!7Mc3??20Dgj`l$7FVA_e6k2;=F|FsiR1fryf}R`Q7Q zn2F(tX&vS?(JC){YSK-bXgyj0-*jjhF)3nw;<}jiiC7@6t*KjFj&e_dTinCk?axJ< zqn|$qZg!7wKPuKoPo$6Ew$}>W6b)r|I?%^5l0=UvkL4^~_BWI5G~q>fZ5I9wY@h!F zd3&sAxpB%-utJd^hwHCU5KFl4s&~0>?T0H@23(*nseg|6Y3kV-r)Qm<cVb~3;f<n1 zBMnK-y<PP*(@bQZwUspXpf`wn8;itLUPw}%nptliRn6$+DlDNc*%qaU?R5u3uZ7T= zBH#69T0;8XDVLE^q)n?>K6G?GL8pj)Fvivp+F9hKX82}WO7`dK<Q`pbp5AHphK@8X zTW2gXf%_RW{43UkV6BFUL#e5W3Hf@B+yk>yeQt}&k5@A`1rdNmEh4(JD1d`7pW33T zR&v!f{9GFuClf(U=)U`b+F?(!I)8D%S}$=mmaMTwHNKB&X?wA>9v1m>kXplPTNLqO zf;2*tpwiw0gr2te{NaU+t)wc}8czP5qo;}1-neSMw~f$5K}!fha5&$ZF8WhY6i!%0 zqCe}+TSItZg$ya$dNcxR#wJ*O$-A^zLKp)jRGvPmZoSzmBkv~Z0Gimc!rF`coqd+J z7z|dq{RbgkYISoww@kD4ByY1w8bV?9us<tWCR+W-TMp9KIMSZvElfCD4E1jrL8w9| zg26&zfovs;SQ)8-4$?;B4$3UEdP{!07Glj4-7Yv}H8@LdLl3hmU$WE|rJZQ)LEesF za2rAO-K<{Zb+%1|pas`4VOwHVH)z2%Hyyy;x!ybxtIRJZtbDFCzlU-s{BkC*Vr`M2 z87xc$s~=lQ{@d2x<YijIX~&SKH`S3kP>?%$nPC+qT?hylfl++EkJX#J$d*duKMYQp zUTrSp5r}CzAuj+ACJW};VjDg?Sc+!HXwyV;IoORTaPBy>dkCX1H5U=6?$NW>EZ?L4 zhh%^u5t#u4rHW?%0OBxVrCSP#7(>?9qw7j>3mZt>Hq8*Llx$#IxeB`A4BO%ojo2!c zG+-;fQ)Ubd7cl9WRx^lz7-l9X3NzCdt4ZLVYvDQmBUWspWm0g8_%tUrbC`pZ?*KgB zmLET#(HVdh5jD6AN%vzHl4iLrUo(#-q-Zg8;gCPr8PT@bmiKM>EweeCDE}^;+Hwwu z0i2R$w)~P9wkh#Zi@I=_GI@7akr*jH4kVh1qMSVptdeW@a|Cplz&k)7$`%#7u&Z)y zNtd7{ivd2>7#uD@Z9W9ORni;V5A7@&gUB?ZOIS^;%vsnvgOO2$(^Y7Aw)QH+L7RX} z)Ive9ggnO>>=i&wnguqZ#mB<XJn{{$bjcFv^S5k@7FV`(gfW=p&r0E71pt%IgHHm$ zx{*Wdbe2K`=GaPz5iik+1BSD0Qt23u4u?y=3mP@VB<V=7ZmohPGtAByKvB_2Ivzms zdjmvF_O~+tNXZQFhLKR}LiV=fF9ZDPHckE#w$#_o;5}e4&8UL;oYg$}KK!0)RE67r z>pmz>GC&Kb7*!^F(UK40ccd}UM0BAoS-w59I0+a`HmXiI@K(U@2tlxz2c7YeCC!$f z{ef(rg$Pc!<wwQIW)~3W7&Pk}LUz$&ufZ$_4eM>v?6-r{Y*NkJ%u>@AGg27C5_=0i z0)jdi6MO+2RplF;a?ajc<Z5Vf2#2K`0WFxwc|~rsy=*l&?@D$=l{V;rNi4RF;561c zvL!ui+A3*qIz2z%rO-}s$tqiH_BcdF@Z<#s?AuFL%Ot{95MV4emI&q?I47~tApnB~ zcoReBVThKHIv`B|k&f=FlON}R=spG`e*?RAB6O@>{cAwDk*vEvFRL{`Vasq_NccZT zTnh|`;WBMcOKRpxS?Z%_PqG-vua#zY9MDNU%DAV3?rvp{TG&{l27~&0;hOpK;FOCF z#vn||AbBA)m7+ee)A$5q#Y*v*kQm#abv15~`Ls*g`2OIgFtOUC@fu57cS0&)^xW{> z<fPp7gs^Ef>kvse*~>(>C}}h(-nm62?+N3h^GS;SbF!FDj^NE;{^}>Ad<cv^kFiB% zzaN}-p&Udpz__+VFrbu^s+M29k9$(_*B<1a7~UTH(~@{OYsX8}V59-qyRWj|4PFAV zqr<xyfVL>c+UXQ@F0<DrJD=0((Cy<yW;&v6+J=M`Ee15fEubc%iaMq~0_}mYK_(Wr z83V;c(jWV^0ihd1`Qbd>1m5~YOR`N`;%A1S*jN<!MqY^ZX3<ktL%k&=SE?U_u6FM~ zy7)fW>Hk?h%9}&18{Ac0>gA~3JRE7IgAG!RJBL-&KQDxSP(;}B^FO7<Vm=5(<xS3k z_y)60T#e&}*vV?<iBiq19Dt$M9MF8}8jPb&z&MI<HS;0h6b$&a92$F!a7u`sFl<OD zZWL}j1tp9Q;xbp<U?g-45lN*^V8#Jxs+&@k-!LWUjxn&NZH6hgS+$!nD%B6#Nb&YM z*&{je;V)oD(kKDi;?_@=-!N&)6+uww6WrOrIVxL)&btNp1YK74eek$c@`R8(3kgBL zejaA{Hh$C32T6Mb^J7EMm>x<&RLKamxW^*zsBDcT)4I|Eosj%wbDe{h#sgjL;lmF< zjvn@)1^H2f(0ti&fi#vzn`8qOjlr$6dsQas587MU>$Go7a0+wQ3^51Hp9j%zvM|1= z3<bOU@(WARB==yxdLLTl-p5O_*VsqmbY>GbjwogXn|kSNQiU%nahG@3wm4goU8^~> z2)Ce%?gRPyB1Cx%;p@Leqdhd9s$#nuL5aK*+}vNZ10{M4?6!LsO**;A9|<5}h|5Di zdc5INHAyZ53lkhQD;58B3f?gGgt=9K1*}8eJcIenCN$bp%@@_8#h!8A#YI&wcaj-M z8H?&F(Qlp_e$kJ}U9RA#{(wfv)qJV~EtJ2lk|xfCAzb+NQB(Tnvu5ynl%l@F`M>(k z83y_!Hh&<xB@c9FU7oyclU7Yco?i1&vX`H0eAH;(9lI%ij7q%vxyCn)@UWNEeT07Z z(s+?0jjH5MK~PQSEXJmZ*VQvK;JYOhX}kmaIDcT=olY1nlL<@m7N>!x{iYG=qcaX? zOiVkr4KK88@m24lkG+4v+#%LKYKw|bK=Xb2^zJv;7<iJPKWmke;g)1tq;gxx>`fbz zcEDtdkLr%T@|hrr&-O&W`AjfqvQ-zEslZdJl9A$ybBubkO%v6}80bvYwlAnespp@q z)LWN7dMq;uKLX>_R87SZyEyJi)|LAdiDEnmT&nT6ej$Fipxl}weoQVyVsS}f?o=j8 zXVYZ++3EYeK)?6q^3^e!<x66EAFNDJ6T`U8gGph6KqsD2J&8BOEDfO(E;ykxJ$($8 zR9Wjog6>`}tX_&XJbZRYH*}4hN6X8EGP3CD*Pir0g+iJy7YG866S-M8MPG=<ZE*^V z4BJq3s*0!MMjdQbK%C&|l5|Th&6&OWXQS#k;WV+7DcADNS1MP>+=S2iM7pVfUrzA} zns<RuXI&t`q0us5SN^@h=o4SHI%Y5?_oAoce#q3>;)zeie{p>+8i5*peWv$Xx%1Sy znv^qz_4UWk9J_erYSqH)i?1!c`rhT`7ZatyY081$w<-IdZ&L=}YP%0a%r4+s8GmX7 zMJ_xT9@?vy4m1mbM8rBh{ooRDL^Kvn2<UGpjO6vonqj6tzT}D4s0n(D$P-Cw+QF;& zvlz726Q+`u57y~}GiY6r`-C_oN`2ZIy>7vwDKh6r&tTYS>zrJ?d+3@t9_<?EYz!2( z2g&JwntKR}P7y;zzia6XF|2=*+Yyz@`+}<9l6TA)7}|dAm{A?>!sv&ZRn0_9R#K=k z;Es!o^2M_du!WJ|01Rut5ekYTc|)Bd(ZSw<Ud4kkh9%j76aQIohV{lbZRq#j0aH#9 z#zGgOl&sLoQW;)1QG{8bOeKuUI^ydyg1wYLsTQ`=qHe;=^Ze`RG%;TQv6n!d+-_8d zwks9yv?&8twei0KRT0L5Q)srI-)#TN*62g!t<k*7U!%8|W98Q$zjy?z|ID#-HUCn+ zNvZz6ZGReTd9E;kxMsQu0$s7%+p3oiF$+UpRQdqJ6wrBung0Be7kU&-&|itXWRjXT z+%RxfZOfESy(N=AUF3-#`4H6~DEmBUL!z2?WdMAqW;|UzzjN-P$>PaM#e+6M;1phI z#sD7u@J}bStv@j|HJUdk1kDoh4yDm9BSkdxv9sN?$1-8`uZ2P|4YwhJz|#b_7rOIL zDV=^zinc2J1bJjP^n=2GY$P4?C5WEfcIXdTN-wrguvnV6uvpe#iyl1GE)BgH8Ww&a zTph6`f|~r#Tj%CaT(Ez^VrKDt)YpH!A#!?o>4s>Ya_1X7S?aZC_rk2U+Ygg;GR9?y zO+!p^n7Crp*q_+ak03yvTwDn%BXpO%7RCI$L0iiF2!0ftG2vHL<?8u4@F6STo<F>N z!Thhw=dRp&zP{nind`@vUr)JMc<$V(i}hEJ|5kJR$la<$FH7d2y{7%?BTULZx1e7i zwB5(!fdqO*QvbIxP{NxKcff44hzR;&lP5BhJnRTVwY1?<4>ViMt6nNgfgvGXtRK4W z1VN`Y52Keo>P9o*7qfd2ndzBfve{z^dc!4YuAXjcmZC>JVZTEZioOqsnoo-!Nubl8 z+&L+L{}1VD&qqS^s2`DZ))-h%B*89IY3c(l9#JluH`QIPL+z6ZI@y_D9oH#0s_F?^ z(5W@5>tPm&a3S@jDd)9A<(G&nS<0PlF6gPh!Z21(Z*Ary(H=ZZ-G9iZT~8A-EP+6z zKBQL;eDK)6a&PK7{jx*QQdonDMzlt+E{D#1LZ1?81ghjG*BM*Ov}-Vkhmsm(?n7KO zD|ZHBOMUXweJm=yO*9aTWleW}m=p#poiLb{T5J!umlx};-R~B);|41S3LjgyiteJ{ zmA!hpKN+U%|KPDeRnvwCE`?k8Pxu*zFcK;Hc=C$T$UYwp)k^)rsMmc<#8f)0YAIM% zi67Y1+uyb!^8gS9b|f|yJ!5OfMy*BH`ULS(5T()yVoTPcsj7aW*oIH(-lP{wRP`F5 zsdCQL!Auh?{lrR;jprB{BLqu|m7M;c)yMUkiB$)d)@l}=LiMUZXKms~V8hAunId=8 zp^7zd9kI1e5E$BaxsI+D3Wuhq@st69yt0F`6nED2fq^z&U`e({ee)Q16WzO$5I|>M z(dT9fz)5gdIw(AeUb*k3$x6|%mYuW|QETyJm865oP?7yf>prDI*p`5PQTH<B&9s_C z#{~tj6QW9P6R1Pm)|-_BcRjYwCGWOId+bAGa{EH)fvg<|**>*qiVk0U%bF%SjO)M{ z)&^r(TPgFs7Pe2ilRsLLYY(^1#vO8H-#TAw_ssiGqMsb*;!4&N*j?kmt-g~Fn6pES z2344gsfnlKBAE+%`mZA4e7&Hro@ae1dWN<H64meS-!jzcv6iRr^zzRqMOMl09oHu2 zP^FLLJZo;z)6z>#1e4x$sheo@7Gq$Fi&1^dMV2ZjN=H5-4w#jFZo4Se4_(&6Y`C(L z6JtoI9JV(Fs)FnZB;xn=bB@h+!BZVd4a`LS`$BJLyP{Qn7Z_NqCF{E)IPY3JUJ`7! z)p0U!INBA3sgK}b8CC+ZEh88`eW8fBIXfX+Gz14$4a4b}e4-P6$7yh+hEnT3-!%E! zWLN!xP1#y{-6H}^EfFCR1>r*HtW8Jz=7_9cH9tk76c?E&#T`%e8;+hj`nAaVW%E;I z0OjHc1yFX7hak3UHQF6CYU~y<WqtzWO~oI<UJ<X}ydh-43FEGCUZ$SzGy@ZNmI=uV zW|;tw-j9F>13VaaG%(oLFu#_k`$xMt^jzq~@crTB+v^j>OKjP(QjYA2_wmNs*b{C# zGFZ90m>6CS%NhRmqd(5fo6W%DBRIwTgn2XGmQr1m!?%oVI%kVYBn1UxUgmKG>}!P) zo1G>o3!e~5OD^HxDI*izfxg7GIw4Sd&o~OC_NpG2t5g&du9c@lGxaAb1qI=RExS=7 zBNf%#?J>M1MgC9#78v%9`4X$nc}O@sRI+TA9GWi^2Q5M~hQBH3`)D4@9zH3q>0@xs z@f>DRq`;zA+}p}59V*&J$RWfAAy?6jw}r}`%?g+d##_abHm<orGkM|WHlUvtF$45T zkYaU8l5QxY=j9x&4K@}@h#ax?LEy9Sa+^jmW)GS$qF2DK<<Q|8Vx*cepNEiq*E{dn zG>vcI-cmCg*=VcOGxRW@cLH4q>npkn>?QT#NT`nyu?>O2JXRmXl}jd|Z2d~VmPNZ^ zteO~{cE3L{EGZO1q>foBg%Delk+7M&94RIZ@=I)Z|LrSDIvCZm35a*=DbpYD+?bV7 z2>-7*iIz|Dn+;7Bs7TdI7R}kB^8d7Gux7ih`fm+!YnFq2GLe43R=dq}D-MwY-I}B} zELgd-X7TxyV}(cRtEOI`ac$Psd6ySnTzc-khUI4xoq{vC0I{ZxT}ib(+ho&54Z7ct z7-lSThI66^Z3#izdvteFI%cjVjZPOy&7Gk2%mH4;B3FVoilmok(u7EwHxqXKyDf%O z0Za1ACQye4UHXww20t*gnbMoAC^-TTn|OJtX&Hw1*)19L?INN14s=9uv0DBYXkfse zzT{+?q(y`~bX^R6+ss4e{={8y6`E4<Y>82kwD%IREQO5-Sz4M1ONMo=r2~hL#y&Oe zIbo<{B(W6w0LP2*V+YiRxWQ}%BV}SkNYH5`#GDHm<+opgiMJEVy<hUcz+G`mj0Mj8 z#mu#tSbo>jys#X$PPW$L!~w0^yzgC}O!3bYHM1r3ZScE$(N8KnqwiGy6@8@=2e<3Z zu=VpU%8v*@$0GbhoSpO0pAkd&Rene@SwEh^j#sOf!#AAg=xo~j<v&%L#RQB8;SZLS zuU#2%UVSd2Vd|L~rxw>x7iZPaJHGH3;UxzPhoi@bsBm(+2n@nlRP@)zo$e?y(gXiQ zsd0DFyOAReEJ)P+18C}0NS75Qz*!k*3;HI29aZiW5zj|Ou%Y_^mqT4apx*j}`S?pC zCf-{ioPtdry1mH|8WiB@EJtUHC3xPa6Qoz%6SB{HAQ5azz^M%CJ;jw1ukKegWXkwa zQQ4o0n*hoO=&%=-GC&bVdDhyQOI>op_|3LPe-T-x4CN>V{)}!-8LDnMd!AE5{TXE+ z3*miwvkq)1s$sb?FpZdU#eOP?#!mIJS38O3PYn^J;5GCy4^%jHfS{%O26S=i0KY_H zwe@bFX9?NjcXM|e=N1rCbOMO?1jTv+8fZiT)B4F0V^>=L;BW@#t8}(T!!r~+&0o+s zz7r)+8zgA-YeNU74Hh)sc!=&!^OGm$FSg$4^DL**7(^0+ARRFl*SU`rQN09-v9U-S z<<S$qF;D+;I2~CiE{C8wQM=%;L$<`mMJPThNTt#0*@LIMMM6um`7ZRbMRHIM->~C- zSl$j6;C!+G9ggyIQ)r{?@ds>r!nigc{T($v<WIot|80pN&;_P3`n@_rCRY>YW^65W z`+hLPZdAq^F-ONa&lotFkW+9fOi{9P%;8N~ncB%He!4q9KNO`*_ZuITA7!0Gb_i6H z2_zJ@gC}i<&HdS~Cyc;g-7NafNM4$lBd;$$1Hr;|hBNDeCOAiOMYpD_WZXoTji-lt zywZ<~osN8G1P*7-S&|7`H$<=riG>{&EP#R-eukl=@?rTEK}2Be$PZhmGXBFcOgj{P zGJ_Fl2%n8Jy#*Sr7xJCiO9ZVG1P1;GX=i%%)}KlfVavBQj;}0CsfS$#`7r>i!{TiX z-!-F;XATI2Q3m!0*Wmp@I#uK@fQ>!~jopoE2<5Y=9_Ct5RzdB1sD9>P1E)CSh7gjJ zd6GOt5&Q}3q0^!i-IBuhM+HQ0jONc>9C0pUD)Udc4n_kw>}p(eU(WGZj;F^3rzb#} z-#^NLVq!`y3%{pS7ZWkHa^M8}*K2JJevHLL>qyF6Cm@tNi(=^v2?f&@g+7fQX^1b0 zu`r`x20uX@-n5}=R3)AujYax3SgP7`0qpDB8Y}-1s9^eS91q`O{QDkRNiLaNiNu=f zUVwwk3@u2ksP?bKnD_}KwQ-Oee~V!OpmP!gGERGp=wTp1XHz~rw}4aXdZdZzJu}gF z_z5^nqy(U>PCb3AnYtgSh{eB3FEvAG_v8;_QL+;R39yuT^hmkuffMXpdXG8*gG#I{ z^~p2YNI4N80$B5vELpS2$ru=pzK)SeP7n#%Bj16p;Gx4YUUI^H$xYb&!iDZo2;Xw} z4o82*Eb|~-?2tZo2z-a5_h&6)C$+*ODaT%%(iWdjgcvU&NHOewux1z*3<TnnwfBQc zh9v~&52HiG{5^X5W}rx(3d69d`FkV}BCvz}U|yffi%1H$P8UDL;~eZKMKvy*a2lG@ z-Yas?WY@`ij#eA#)YJ)d)|v@)TAX2mj3lbf>?jkKJAJo|Jos$r@O(l{+8Vn*!+H@E z37v3^o(m}rYDU=~)|><Tz@9>Mr!rtzr?O9IC+x>|ygihoL1p;gfq5pB3jb&B;(6!h zO?+$rTLJSO(+zfmDmM=EcJ`G@BsX{yjIo!VHa3qLpr=!sN5C<CTJvbTK-pNhgnrt4 zW_H?Pii1<<rEor~%ZB+^H7m6~WnKygEhyq3O8^qpht(P(T|s-0!vsa4PR2r^lLM11 zu+u;`gQmn^usfN!tTlrwQEKxhj&<QC2eNJ_hg^$*VQ;~1A@6eN>u{MJ94?sy8+^Wk z-J$%gQ5<g3Mvm^%jSjlCc^KCK+mK(tKBl)|cMX4QG>06Q%^|C^9mwQ1jxdvM)0B7u zVZqip9B$wj9PaH*2d>U+7kC2*ECafNt@G^yr`sEs?!aXXo9q&&fW&S<SXg1#M3KoZ zvB`m4o5SJij396~AS|q~Ya%YyE--YDne8evi^F9SL11T~OIA#>3(VLBPR6)!3WgY6 zhI};+Cu-M$#7;n%tcbKryxuPHdIw>~#Nm>kfxr|HrYamNPP7Z0=)kQ_;c#`IfWQKv zOI1Xh*WhlB`;Yl!Jh7?`|8XFb$s95qx@k%XunFGfNgQ(E=N$6w&m6eAB)h<{9B*6) zHX*tk$>BDy<!}qv0xmQPw$}f{;gaz%&wU1TMUUrjxJ7HsALI6?EM4P3uKkQd)_n+z ziBBHSw-a0a9KP1hcoo1uw#COTE=-w`at6nUiHTD<>|?X1@9Qe9O5jSD&N;M?jU^MS zv|I@_bKgO=fw47AtAi|t8n=D03zClq3hO4(;hcT8hOGS4mEf@r(`rAwU>9Weo`W+| z&tR8h+tw7%iL+$BO_TVc4(d$X9t3n&<hkvGr7(N4gF~}xv)7xSFO8dEN>h=KxS**o zJ`RGY3FazTueQFrMvwypCddcq_L2!Ac4jKJp@B=|xaSkUqr9bzAwPS*BR0eMkSTM+ zHzeomrpb&x>5M~=$dL7099WEg9K#+y0e3C}h`R>M-@4;(;lb~DWXU#wlP&J*){yfd z^WR!;cF^EBHDC4(5t`0kn})uM^Tx9|Fu#bl#`$s2f#=cTxFGxUPw4NsUW?cXu=xg_ z0K3k=Y}rv0ylZXQ(n${Tw#1ST9M6s>a4Z8)LgVVL%}%PD6tdn--sGm#y6d}V+8X0m zq7U9x8dx56zwDX`{nywqgN`H#uE&@$P*SBO={Y29?PL1fdJ)cY!66`)dK1H=JgpPS zrxqqDj4M^+(vm28?FC!ojl7$9eO#kH!Ufj$aG?rMY?kEQy1Z$oIwW1@-G`g#&#^K{ z{ksDNssDTMe`B@=<KFq@+RP_t+A_r=R%xPxz9E@)*Cua23jJipy90P%K&jsK)RtYM z=Nevjz&8Z;&Ys?RhT%LN#=yM;i;1#aD^K@=IvWzTxN-R86peV#PtX_@iRQeg^yd_F z)RMe==6y(olBFa=$XwEm_m^F@d(c<!c^O8n47e6?nYymNI`!g=^Rv#)Ygl+Dkr-&Q zTfGuu){^*TbO`A#{{_smU-}RD_y=O%D<Dkqo&P08&|ipp2rObYNlBoV5R4>8OcPWO z=%LRcdqRqri^wD{Q09ljHgYkXAxH$KI%7dN91g8Fm<t(1)pT&m55vdmB`qIW81V@i z)gs^`FV#L<<9AhfeJMW)M(6y^cR44og(RKCkt%Gk_4!7qRR2%N30&aK3cL_fG$KeG zaF-DMVV5w3bm2uHNpc51H;1ty1t2X|i(GFSV0GD12w70{pY5QY#Mww5Oq?-`YXwkF zN-o!7n5`r+7<LU~$c~|+2mumFczZ>j5Wb?+N8`~WQB?$^>^qrM_HP&3TBPHZyNZc_ zq83M=i{3vsVBW@g{I~v@9KlSS>knOsliKj736`MFg!Oit;ZIII;RvhH>Mr^FMSU!F z!OW$LEsWKN{F_()bsR5ZdZqWx7yI8HW$xNN({8{LGYmVBQ?=JdOqaXWI&5QoqFtMN zA(dmHqZIxA;ZOtM!veS1mKg8h-NZS{+88`&!6s*loQ0~2U?uOSPT8Zg|1Pv8N`2HR z<_l~TzJ)8T-32=+KD?$w3fxFYh*-?Ik9{YvYS49$(+Po!d+6gw-E-C>vl@04$EKrg z%iRWWGUP*j87Bdk2>?#i@T}EVHNKcD#Ztin{NnNjf|BlSXvB)$0jvA0yO4<&nA&Vf zubW@9Pc34%zsRrx_E}19jQ26%lmi?l;?NzO=<@>Iu9-Hil=8S~H@#b(F+ihAG0Kw4 z)6pkH{4R!^;+jmhh8u@C?4>t3Z0!h)ZT&^mfw5(boWO(X#?YZ;zSh1}hS39>FLHtv zxirZW&52hz*W&)EO|=m>*P;#ay+p9Fg_4Dzqi^Gb)eT;VY>~)8tq?A)wN+^(g2Ec- z+%T?Fml$CmqDtz9I^w;9Zn4#pa>L}e0h>}MLdGIM;~|E1P-sd33Vbwf<scDDvG#Mc zYGoj|HCFTi+PQMfJCNAXu%nVwi)aokJe!MzxNOnk>bQ}C1fLN>GIE3OQ4S;SEeUCX zY`bz1k3uPW!fWQ%BRu`rke4Vg!y*^N+u|GDu`;;+GXe#EHuC!DN51G_>BWzF@VeDX zxS5nJuqon|=wX7E-&}y+Sf$|CeS_w$^66H~=MZ%HHbsd~>H1ZF2!q(*KX5%>G;*PT z=Nd@-i?Wzyq82gz{v$$e#>)fI29^R@B42`|EGE*XXbdYoyf(!Ng{B-FP=M#-ra*ho zup1lZB3Q~56sfz)%!Q!Wlw(U!aOx-#_xv_`FLk^KFd@5O0V+uyDcTLsP8;T;zf+e7 zY$jBC`qM{FQ&WRe3LQbAlRYSe^$esb|7U}*;@8>qqE`OY)WVctJCaVwA=MH#X^RB? zuwjA6K|HFO`ryHC`$_Zn@N3tuzNo)x^4Ny&in`Rg7LKEz&y>(ASe{O_wJiDtV>s*% zJDuDPL*JX;a8JFLCj4Pjo))j1*fm4rjp~hQNrEcdKj?DK2=qgmtDwQwit5w)397#P z13garPEeASiefi=1hRBUrLDzK;ot#hW{u?%EJZ|>ob8{wpV95~NxU31C}W0*tx@2G zHe`$!L^Vu9^%;ZSiqgCdXCWOwRVLuv+^Vd4o3!yANQ~p#qFTCJ=aX$8+99^%uneSO z?i9dspD$3G_!dT;iWl@z!(pT2aX3DHU_%zJg$1rCI@8~;s&XSE6yLFK>+=jti4%&j zHtbq(Mw>E63980tqJ~Umud4Amo0~8d?x10R>Z#e%c^hUQNVZk&*omZH_`6mawtLrd zbs6xXFT6!;D@A+J!Y{n_CDR&U?s$5wire1D6SUJTSUo<qm3(&w&fs2xKwFKLRO@82 z;~JzLJShiECXB$P=<pYQf|4Cyp~f$I4lnucY8SQ080kEx7+cApCtYQ)_PeSTX|hJ~ z#OS@OUT;@r?F5aE$&Nl1jb{Boxau<@4KQBdQJWEcJQ_}`B>0<_z$Sx~@Ko+BCcpzd zdAfDwkcH!1Te&VK22w9{Y|MkL2TF(F@h!K`uE9Uh?^*r%&q|PIw!%<Z)+Lv{<qbRb z{JZ5&vuq89-MHn)?V93(8At!w8pi+kMIN=kL2S;ZgS~3U7&hpx-Trzf@?hU1;y85% z4q2s;6!3JQ$)kEdvmZ{36w@!Gz?|NODqA|(ky=L8JjL4sv5>J-^Yo`moB`lSzBpHt zD7_E6<zMTSeS~d5rcK!wKEd$m51)bX2@tl?zZdy%Hm*;SkfS&IPS~G3!h{{kX_icq z+%_Wxk)dt5<6+O;{o0(ZbIwOA-5+$IsvHmSr1jukZx}+`WR$Q2#ArCnym;!aVhty9 zmaWR~Yo6}k5P>d*4;izIc)DN9Apvi}zu~t0MLT)AJJ_Ja$!$|vF;)&=P%BV{wgIpF z%CDwZvLX(!%gBsXou10TnPmsGs{`8H0d4M30fDY|>>G}L+}IPW@*i|{<A{Mq*cO4@ z9|k8c*c!TTX7ozM69}nCm1D>m=GSsIiMGmCy*8${3=Kpv{;cyeKL~3>YoR+UIcLNj z%02OP=*P{$rF%CQ3q&yDA=8NkXkqRN(GM_^NhH(Jz-=>u`}Zh$TdU|tfM@*qW@+M= z-aL;)t!i(oB^?K4Q&W<{4rkaJ2IZFS&9m}E>tMil{qQo{`jt_{E_kW)r2#u)c!mS) zaQ=sK=WVIH90q%d=&nFd5ptGU+Fj+q(02L%fU)z*Co-3sc3!x(m*|?YC`<}dOLuGR zqq|5<NJ~vmv`L{{orvaACRaO@+(u-+VcBH8JM7t>LuQuC(x&J2-g$KUW;qTPgfc43 zg4*)iMZnxx)ciB*_<E*W$#><!CT_}HavhG8no#Uc?{IbsRb8_++L}1uavU1++^92n zlaG)_9G`m3%<8Q8iY0j}GtgvfoYn{%w)kZKcU%IyS-+tpI|DgwT53?s&bjvI!zk*% z6R^f*;^KNVf0vifv<5_l68IR7r&FrWKb=0zk_<6tW7bco{=ZXHtU$UWz~Y2aUGtQ~ z^B-eLw$JueAbLF-v&)+!y6i9z_3l~oGR^8CnBt0Bo2KC{l)Y;lf60faVOQv|+V@?@ z!!W>3!<e2xrTW_L^iT#XWl$JwQ|)+hrHsZDj5L_BBJ0*cta7V}4=J56F~dIVdJF6) zLtY@O=0HnM-8eWI<<_@D?9oV9<jf8FmgIqqEq{C?yOsaAh;UdZmV$c+AxC%^W=vc@ z6i%QwhLWZUJj43L7temUb02&HM0i<D1Jja1lOklW%pfCWVuFvHbp*XpEM$b%?Jz6Z zx1#?EUmP)U^8U#Ik+Ya{5jq$GICj?|x*00ZBN$=nr6Sm`O*&h8_by>&`g9IuLLm)& z#|7&L-*~tEREf`NtZ5J0xZ?5Wt#5zRwyqMLDfH#XiqM~ht9ZlF+eI?r9_t%Z(5FR< zc`MPcMZ^2Q8%|TC4?fhhH9otAn{D0%%G)KJEfd2lIZuj+oZWDUK*C0NCmOfg(`z!1 zTUV)axQME0nyqC=+3Fb7(&{OhI0dh1j6vxn%GmA2pDaR!yN7r>sx)ojs;D9%GTDRv z-tCJqT#7>t>EyjiK~Xq#S_ENT^XMrIQ#Z~qgTr8JC*SYJ;IQjB=`i1L-TFspC*Arj z4|7;nDNv_VkO<IRCp7k%Oyz{lV+xrQL)rLyi|Eh76d47aA?$;t&u1LxC6j=2{_3(W zi0)H>%!P2Nc7&~^@~iMe`%}0owVz-lt{MrU)U&+phq|gYeau0c#sa}F_q_not+v-0 z3G{MNU&}x~L!vpBVBQ^Mw3Hd9UN1c2IKEwco;$$ZYjT+YCn=PUH#Nmxdnj;ZAk-m^ zDly?GEr8M;$uE{L!yo`N3RK}F*M){XFh+^!r9~1Xf;8w*`ImT!{zvcNOmiM|>$j&2 z3i$^xF1lp&Gmi$%q(wyHqgu!<Y8#M$omp;Y-=wj%bbpG;$|yET46Nfub#Nvlq?cKQ zMzu4XP7NTInmZTJw+ea20V3kJ^qj_v>C2&)20go&VtpTH5ZAPGFXfCgdxe;nvYUN< z3`1uZ5oG$@cJm0fAarH9d^nEGb$4O7dT8upB6$&;v~)7nZCxZe#r!^CrJo!qhi{p) z^I-FPSm?CDW@rS;IOS*l5WX`r{Vr?a&@Ap6QR?|C@xvCB`tTXn(izwv#OXCF?m_l@ zOZR(uq4e~6$j5`+X+$RS=^i2<*&{eIaS?$BB=R}cGyR~#-+7@q>4>c2C)I%swygN; zHp4+x!H})<C-+<sWNSJ52m1Tl00Tyvb_bByw$%Q$4;KE@e&3`eCB+XR9g=#n&zih} z#BHNm{8DMH4}>yJFX8nBnQy__2}R=50V$jgBsIh9dm<^vzR4fpzrpDeWt<F+<uqLA zYO3R&j=4C@Mx_9THz0NvVe%YQ?d-q{lQ{4S{D9!E=5OESd70x!@~FODN+IPS-POEM z*&`9p?IczVCqu1=t>wGRNU_()pp(JlMT{aRUPK{#K-CMlJP@>A#kckp-D1kHb`$y5 zY|(8q>!GS=`wnp=NjJbhCoboH6-YsmKV7iHYP&&SH2hlD73fVpMo3}aED$JRwrKM4 zs5b9{IU+gP$2<xw!*rsmp2-<JmC1qru$@9Zz8e|jO8N{m(P^ZQxfn1_;qLG*p%cAP z1TQs)6Tz98XXWg&PA@|reK*qZcXN;Tpl=tYK-z>4DK!?IfW<U^TSaCLB$SFyVyamg zP{d<I$^chF*#|yq`1FNO;S+G0L3i<x3VE&0QwVEw(PJ{wydC^-Pv$S2KV~%Ii2)Kl zNpvY1-f+XNf*wBL4V`@nykvMC3NCpI&u)Ui)C)hDH^EDpVZ0X(j~mZEhHa>izj=5M z(#KAdR-+#5-j$ePcVngvTD?y>a0+>^+ycYl6#H<<xY&EI72x0LU^sL@nj{>g=^s%0 zUL?^}hnERk=N&qPO1}3(ihX3SZ$N43lOGw2)1Bxq#qM$wjvY+jS{Q-KM52lNX3O#i zU5Bg_u~BUz&1rz0mR5ki-PcQeD>!|^U)aIXxqWK{`GbB&k^6i0Oo94T;WjfjPT#k) z{{<{@3tGQFk>9-%2}`vGJipF3HSaiek;|}{Rlo4q;+myL2(S6mgVP44;}ofDGjWQP zB}04~*3&FKrir7+G=Oa~y4_|AmgjDRaaRN(hwg`{r$G(_qoDr>Un7|B%rIQSs}Y4c zx5{=K`-llQaoO?TBW?Lvh7^2m4n6p^W0Lh&@l$SK!&?vN<5phjrAsh1-~0v1RRo@} zpZqK+ln`&9+Yisy?Pf^q4P{R#IlS6SEs+nRXz5beAiPTa80{uj4s0WSj8X=-5yn0z zo>f1FczRcxQ&MDCaDXI7BqNqG8zXUK@MBa3yrl+fUrp6}mW;tyS|Wo}QA()2bIFTb zo5>nDRDQUm5_)dzc(&b^L$>_yl9c%tf)<gUGP26WrfB#)IE{9OJ=xE2zn)w<L8tr{ zW_v}{XK=7hkUiiqTdcI8{$+l=?I^M=C}tZjA@wpU5#BqI6Nk5?QH<!^md+TSPHFjr zQtobZ0w3Kf04yYzL$lLF=gi7{5#;Hllg2>U@y&0iU{HgdU;KT7r`**}q585xhSks? zq>9cRZAl*W7j8O#E{=-M+2bO}?uy2(=X$p3FxSe0?_p;86$us9IAhY5{p{hDat@>R zM-GDljLKgy253ytvg~Fh#1e}5Wv9Wg6xDB6+UXta!=X171HsF!(G%=smQ--%OTK|q z_e&`6KtDl#_Y72jz~8l@dqD(aJYPe=nxhCkKA`e$uzhX3R6__Z?SifB(&T0>`raL& z)BcM_ln1#roZXJGBN(pqyB#RLJjlOc&^F_xWI}Lp7qLN`q#H^-<SPq>&=f-_M9B@^ zzeGQjj}kOwZ9)&r#{)LvtfEUb5e$DWRt(H^4byVatO|cY!?X<aX@zQVc4Z5eXDS2Z z)0PJyNmly5vk!B#?!gkjwH%bv@SO=YR0R4pOiRYK_MH@5OeTz%ZV?$;XiaJz|L|sz z>JIWe7!=r$wf^6fFoi)$GLoon0*V*l13~Qh|9xZNTziXMkBOk)uW<a#?FZszzvDOs zzs0;ad5FnY@~qjl_P$!9any-`Trkl8m-p2Qds#suSLU~ki_y7_9mndy%&rLHK7@#W zN^I8>Vn`?lXnud(=1J^}W&f)19+a~?L@$>&zYB!^kMg)Uz@LH?!#!Q@2SFCrHZWm{ zWC=<<<mqmxnhS4kNuayWKt{Tr2R5fimP7CG7k!1KhrPVw$6HcX!khJ)>e}-AIE9NH z=0$Un_OK`a*(NmSaG;27MNu?LIXoaRdwi0uamSZ}AcmAV>v-n3V17&gV==@LMAF|( z{#6G0zj0a{x_US~u<BU`#E!6N7%aZf#=aX~QducJkw6eVWF%x%l-xK>dy~?QM?6t% zrE9<JtX7Ervn=Teu^+91t==lbdO<PvRx$D9q&gjCKp;FHM~yDC(ALTkeA`{rQ0d-- z!#Mc!PtI}>M&{fv3O%eG!k^ZJf~))tRatFMDlH5|URAaydn#e5QFN9iG{&!TkhU5{ ze_D4GKU#MPmo7Q<vdZIzfodn%yN)}HWVO*eWZdTY6U6#~jCmGlAZ6*~{S2Ml%y-pu zzi&s-nau>LAH)5m!Xb1bxaVWxa;PGsQd~e$e`B+8SO&V(I)QNNOy~Nbu0Il1zloJE z2rTN0!m7uPX4gdT^x10)PJ<(mZD8uzekuNvGCT@5WIf)86VFc;QM_>Z=U@UZk%8uZ zxO<#?4n~Kny?v(LSPC_!{f$H(1fITey>Yp=hRRITQaxyq3UHYMcT9EKy`L(1u#GkA z$h<1Nk%d?HGinGa4&-Id?|ZRlZE3K}O=^XL;#8ikWKkxJ=3cq)$jDgIR$|ETu12?n zkgF!ta2>xW9sTFX48x8mxGK(~FDHqri{?E2*Xg-%cYxnxScv8h>%>q<cGruIWV&nJ zeRY*4A+TyNSDL41(p&4wMa1<hSqa(4Gzr;tcF5%rlb*ibEW96>ueq<vA1nYW15;+1 z-Sx%mnY7#^2vUaJ^?TMc2JVND{lPs8y!39sj8`|3A`+*3k&s=aNstykXNj*7scue) z(%Re6z0JEiA&T(AEysJH3u?h@G~V!P*`1?FdYOoj@^t2l1k6ay>(%2Sk2|;dX+o3+ z<`fNS+$D+N+e&P$L68k^)|Bzwk*0>!m)P#Z<qWrzLidzG<8F1z*JZ)?kt<oF%3(RJ zNXTEa3cjTYC2LmgfxN#J7k><uEmH*`VYU?B@oVN>={p<xyr!pb$=SaKw=$J`pdTQY zDtVI>c76X``D-<##_vIgYUGB4H|@j21A$6HlnUY8vsehB;=lG5?fY<as#qAoY<?5x z6K%0)6yc;*j;F0je8QXxu}V?gVfh-^!W#7G9~gM~w(LR092d)9N`-|8hxBhn_3BE< zo(+UIbl|)<`@1t{r~tbtEyyXkBRd4oHOg<{vlBkB`G-EL?avdU9kp3}>1w1o_NFK7 zd=5>ehy;%qO+sJd%>-$T2Hr|QNymEeLeY+6UZVklB#+l@2<bwTI?&p4kF1a?)Eh$f zr#y#ddpMxz-Z3v;Fp|{yyb;P`68<k^svMXc9lr#xzHidP3Sxycw)gSS{VB>|G{4Sg zHdnc^C$5ZlYP(nse~CbqcY@a&b^?SqN8nZAwT?RIbX~9E9PNDf1g}Z<nvJs8)r@*x z)9*Oe&e-E#zW;}IVxOa}WYMyY|DQ^Xcc5{N7rm^S)eb1Sc^oUT?YNJ@u0;OJn2ZCH zqeCQkZFtk#tKvVeYen{ReCsyq^OtFD?t<k}pC)*1ebeey@u{7X&ntG2??6X;PV{sS zZMT}Kj}yFhziE|Ke9Tos*G~9M20%8o>Sa!4j;ig}jnqd8UKMXzdsoE2PUb2HHkwe+ zGTKgkkl=OvO{;gs2X;pB^*+OZ(H81G%;;q^x!|ad;qNARU4GNrqvBnA#l!VJ(3e;0 zdxCzgIn+A|UcY1gEq<LWEnUR6QL!(#s<jK2M=eb7dW`kA@O6}kF?Mfjw{E89Z$Jqr zJvb%KOYm|U14^7{7cuXoU5RsEX6MmGU?Vjv!OLq5C^7nV>=X`c#5-kIVib7X7*JxA zoss4g*2QEFyp@Use;Q+TuZXmRKRw0DkwZniEQiiPx!sDWN#IIjtbP??uahInfsJl( zjJ~2KCV0&mWA&++XlK;zG?pXiG}|Y+)VKt%cgI*gD#pEzWW0kUwx%(!8o=ro;iD70 zKEaB5<8{;$yRgyG)84pGwo}8vudu3y*+pz&MRY#HYH};30cXLg(%8YH&)8Kp@MTpM zyg-}k56*&B75q9$N*vf|2gm3uN)5h(Ri(BwI&%iA>dqOvssg|*u&M%HN3y{|601t_ zvTzr=V4JAk;3s3O(hB=X{OUm3*oD2F^4WllXIb;zL3x1-jj{Hu@Un~e?O9eATPe?% z*|l~N*haw!{CJGjv%>v#f)isMbCX*GtMtv36#QhY)vZEmXQXXl$AGUWmsd@|!%@B6 z`V}PsUl|KZl-MhN(SVg$++bHC1#U4Glt{f!7EKqijg;`^Rt@ihZKC+#L1RIQ{MS*^ zKF6lFQ~%k3LVm(Zq<2uCgBy*ddscjI7x9CiuoCI5)Tb}Ai|Hb;jrs)qXe{kn@yYAh z#X7LjPd{NL(wnJO;9O&Aw~AF<osH+PDtew{q1jYCILlbtw_>?njEHlr7`fC3FN?uA z2)EN;QtyGUjHTrj@4ZfpYzH>_j$^ciS_%#{mhMrp)XwNSE60;_xJ&39YB9LVSh`n5 z?CVIDI7qhB+o*-F_J7%n@cH0GSWoj`N3E(08=;@^5CEE*1HOedHODSu)z4T}zx-@h zRWvvZR@DqUSaRO3s;HM$)zn4%OKK|k3RczB*Gbaiz(y<1vy8S-5#TCVRS|YZU!BKt z96WDVRTy~1I8asS>qtt+Ip!Tu)x?*D^XP)*QCe`5akN*3_H~pLUD%tcaU0Og3#|2; zsWIR|<7ipM7`up{Uto2SO^tq;-S92~8>u(IS;o=5D@MGIoz{VkZeCz@v7H(MZZeMc zt{7rxB)#Y}3_5EIrFqo^VjR^md?2{WI8fq1d&Pwpu@cu_v@0<fyaFq+@9SiV?;^I1 z`d0+Jx(k*^^#KpUO6>DGN{L<Ao2h^eNOsAtL<P7JR-(c#;+RWzCH8(<iJQ9!Y@~d_ zkFXMbUdJxqfsMYtWLKgmI2Tr;r=3y5C9I1-IPg}g2YAak+P$KO9qe=2C!BRk>C1A| zIw-ey{syixj+Rz*dz};w4qUYEviF?O_FAZM87tiBbu}6tHQLMWzRb7jIi&b+aHny! zPleDf>fe{K%C_Hr)fifzS4z4I{xpvEs36XVGSvB+j2Gg^`<wByNK=xK%`J=kue-|} zKiaKE$~Pl~T~>X3Ws2bTh0*BPm1@U|imR%1V58~3$WX~uCx3`0k|~#0nMA#W`R7$9 z7r@M*C=PSuFYeGKKqF|00}s>Tze*?G0SD}!m|H3(c@-=11uPlL|Amz+ixR$ynf(&x zwO?4dV8zH`qL$}^6*v&v=|t-DR|%Ck;Ap}%EEt_ief%m`)pIPYu<UDCFrfF*tC$Th zVV=5%1*1*W2X?_4uQ@c<<Unkv*HiDlN~pyFM}vQ5HJ0LN9k<rxdXm`ZFaj)C;;*b= zS=6Vm(v!Y~`QxvwV9y(i8h&*Mrg9**!y9h#uM*NY;K={FU1Rpvy(pOWITjWy{<>YT zrLWS9dI|H}>vq8wbC{^=x<jxf4n!>2{8tIZJK)IehF!2ZuVN)W$HIce-mnWc<5kS; zmoW2g*ae$v7p&xlL$Cq|A{K1YtAt7%aKvl03l{n+R@HMXELc>dU9fSlVm7>lnbBw$ zY_wgl{6>dhO%6mX*sxa#wK(ADUSm%@LuF6_uVAU*3QBIEY0tTRUQ5?d1QT|?%FaxC z(;Z?V77*P)n4SY(umx5Aa{>@4dY;!ZI{L~5e&+v)OI&p@qs^3OM(BAhYGR0t8oK`f z7@kzpD!?9#_lDZ(4U{`bSNDI6D=MP>IngXidP-{wqklf&WN(4=H}|EX?WWtCLeXuC zx2c8&Upw&XpreBQNxLFA>@dQPec=E<@5S-iJX@2XmJUhCo(->F0PW_yP}r4}qLSa# z{O$$l@^5Ozw4L_@<5w~1u(ep7YhBSe?Q8tOy0M<Q@cW4}RP~$Ou!F6~pHhBayY|qg z&{JA;+Y@*`foG7Fzo}+*8AK-~bg$jNZHh#<Q;1EcR<zh)(t5nl>c0EhrbyU#O+5X@ zrc)pO4<d&W+IM#c#-oH7?`#JI8zr>+?#FBypDlX?kqgHA2bUQ@9T@Q+n7>M_y?1Zf z6q&OGlVg5>d9lPQ-@S2Dq=_wY-4AnXiB-D$i%pRXTk=x{Owc7(@7)`431UcAknj?i z)iGW&%T&ARln3B#Eup=4KV?f4nB$745M-3l2Dja9Y@w7bd;&2GMts1QIk9CAK(u{U zx83z@TeVoY=?6i$eZaVeEmPpKoP8i3#;an>q`2(DVGt1G{lJzv;j)+mARfj$fXfVo zkgW`eTQOn@i^#`iZh*J}BYw-4@z}~gf`AyW5SJwqxNP(Hpm>b86PIb(GB?1RgYoik znSw3*3G_D!<88%dQm(E6)cgiU1T~kFPHdqEAoj(Gpy+bA&jd5Us`kQopz3mx&z4OG zMLS_UP;@ohA-w?WaS82%75z0^vZ5vHscbXJI}`>guicxnPPd|QMLKsTg5RA$KQ>K} zXRo+Q-o(G~r>T<N;Ljd(1-akuC&-?55ly@u@J`Mt$RJ}92nFn>&y~WCxjXEk<8QAf zIBjYa+Olmwt^E~X1e6H@MpUyW4pWbD=M983DO^`AUn_vq@nu(#uvGxZkonUt;<H-w z$FOyra_kDae%nvPKHt8Ggw3O7a?d}zADw@6>rrK&JA7kMDj{c2baf3-yTOsYIF;cp zJ>g)?nWS)A{)#iF%i*<MZ*#}_kgXvcr0EpOZti8^=$_hi^zu<(p!<YxEKVhC`DahO z&E|VM4i1A&H^*9%#kCQTwrGpDv9E^k;pRo|{jOyvK#k828SO2rWz!n-R~&&eX+GDk zrN`)uOT<<^eft*CpdVtUZ=(Lci<R8D^byExI`ZOCBbL?t3Fu=(-y<Na7mELV0;iM0 z|DlTC#~M}vEV<7C_PUJxL8aFs?b<t#6iTOFA};IUy@gHI?U$a~bx{g=2BnyTLvAef z^UE>W{s`X(XL3Bqp@37x?HQm4$gV!YlY^EZr*t1}sv~&%^FVim*}8pT-P2rnw-i8R z(;i#CZ4V~hr4-Jx;2sCS5U6UuMLX{d7=(`!`iOSBOpsj+Bk;k3OjJ~~*DQx-!fpM# z&43H<sT6Tkb-U5iJD1VryOa9?Cfv)pW!z?XrzP9(QLu^5+(keV24q9QMWa7(Sy8Go zICuOXn)-+T%-HdV&9iZxqFIo$d8ddE$*Qr>euT_TwzO`Z1u`d0XF1uLz8DAR&d=C1 zZ>~e(blKkhI4K=?mfX06j{GsoJJwLfQD~k5bbl+N)XW)pUuyg%<o@Rh1E^3C6qFd8 zYf0W~;)R)XVA$C^8sP-1d+T#)3ko=C3%-J*wF1ce$q`sWc}7bzuO<@T#R_+tk#PTR z_V_D2ePPNhxG;Ar<Oe;=dalnUSfAvlj-y33Z#53|mda241PT9wYrc+5*MZEj&-R$1 zT=`AWs=3wXS)h%=W8>xU4R;=l;pxpWP*R{8l{b+T<xoR66cM>6eJ7TtKG33aIRWSS zipOwx<V566aHFhGn>+q}8jop%epi$VTM0;K*F3*{U8fO;d*|Z;1eBibS8wg9g4;C0 zHPtXW;IksmSEyqVRq%S~JJ^Qbd4H?cl1heU>PBxC6pP@Lsq!Sc`<K6pOLj|#Q^zdG z`Dk}^cvF_W!&9MSGLhzQrD18HTW~tu+?ISGZCqyV>HSz~C5An4yL`8tun4#_7ytG< zN(;$u*c8TR+RZ}+-S~Tv3u_+f&&?yqVm21T9dGGkiTMMNFyq?=0*edWL!P9Yx=rRH z;Vp)i-8eM&4%E}`_|uX;0zXtXSyGjIgLq0yF;DsJT%I_L!N;&B=*$0&IsrL$PEGvQ zXs(YjfoEmN$re1+be8_29}i2gBgqzZ_ItR4tK&rZo#-i*)-BFYjVfm_UOwD6bGl_U z+$gxD{IBR3v~R3uuh^_k$YU^b{h*JkH+!6L1p&ul(Mr&dHlO)TYvge93pp>#Z6#+P z;HzlBAhnCEoGEb?<0g;Wd_PdD@rBb)?J)ExkUzhcOJR2OlcSevCgo|p!*X=kSq#M} zv?aixTgfrdErqksKOxuaGLO9(0v{?<X9%fz87R-REJOe=7eVGT_(U#rF?8>L;Z+{a zC)lHDW;sWv_HA3qir=BD&*RsT3gNUkL8{kW(_^vl9Wm!4vm;MU;_0CoErWhuYj}Pi z4$jr0F3vcGGouNwU=zN>??CeCj0AY=$NU}$krw=>Ep|mS*h`YY0yz`g;Bs&2NM|Au z>D!-`K6*logmc(V`C2)>>XDSbB6fnj-@nXYJL;6{*m82QB`F8y!d*p>S=4_m58oGL zfW!H0Kj@k%e?ig`{6UV<aA8*hD06Ea{6EZC7(vL^1oAJ}oj8^^>&nroaP85P+0brk zKobnJ{Pkvhfe@x~V<Y^L0N+-p*C!=8*kTbdYh24<$nw95Gnz5uoU?helg@H1AXh-t zO2pht3e}avJxi7JR=6+&{4ptXLM5HNjQ1q^?Q;CPtkRMUFI2K!pqpdwf^g`9hR*2i zaD!zgTwQdi9K4RjP|e~n3iP&;X=fmP?*}$%4kw`-3rVNFsgNW_CU=fw;O@Ns=Q|+f zw?nD81y`L3pTY*kQ~bV+Eq>ZV+(w*DR8DD(hp4@&boUE<oP6h?^Z!G|c8{gf1IXaN zFCr)+op?Gokutvtk@ts9{o;~~C0RDF6(+6Fj#>DHh5QzY#Wdh1g1jktAh?96F5+G~ z&aRVv+Q!2#)j;IlG4%a50{>Bgd2l-n<lB~LT}eo=2}P;*`@u`QVti3Y2fTRO=RpUw z5U%A=_TAJ0r+gjo+77f<*kOwwWVM*8%V(s&4KLebdhLX@yX5gQxIrbfLpHhn+0f6R zgpxciVBxaKZCr_~1TILNWmj{pmQyqHCLHkZ@0`n2!qr@c8tz`Mlw&il;p<D>bkL}% zXU{Uj(W7Amy?LZS_NYc=)`O*|rb=<c@cS9-&Hp~#7pTNea4CBGIk-7r3i&so?Uv-t zf1owP32Wj=NYQv*BMmRM(suqsXptD+(7~@E!y7vBXMcXX65p#?cn;sI`CCoO#rg*B zUQK+f=JM-sucirZ3C!4=d?4Kxwc`O=<4fR|xY3&agzWt>M77NXY2wLk+v7}2;T305 z>6&&zmO6rfOdBWHxLzXKklgGgOZ^z`%53%_<6K3MkmKcnztfw24#s7&72M+J_07Iw z5p8PrrHz+-OcZUp<nA3ur#Aa~$3gjqW=~^LI^4P945H&3SCnwa=R(E}I`bC1gEg#O z8N3PJ!Afq&+3L3VtYc-n9Re8~66LURTFuQ5b0T<ClBOi-d6V#UiO~N~Wa8u}NumDv zki>3`77t}|;kNucYDiOWQxe5+^<2{>W1%x#aFg6Fj#KtI-v)w$C5rg?Pp2g5X}OE! z9OPI5HLTIY2cp!`TcWAC55t#6TtwmkSHlu8cR_(ObYODZIcAv(Qw~l)VZ+_$sy9W# zb%S?vA{QK@H@or$aA_x+{T6{9)rp46i+I<~kQYB;>rqIY1y{KZ36t$kb1RL7lbvL5 zO(t^V{Ph>IWS^yXr#IZ<3*e$pc@aU(GdJ_&dO;ec)1C6KBY1`>%#@(=!@|<$WL{FG zDPjUN|F1(bQrPA|E*g!I*H&WHFjJl!!pztv;n%q02kq-sZeo@8Q<?0oeuV6^&j^3# z-p)W8uyu!CEaQ~B@(`N+8PS;zS8)EGRA%@$0yCxT5Pq=^U#g3*)J-{4fBam{#UqQa zB}y$OQnx>SZ}JGRw?(JfYgQ9sO%7L-%9`FJTARhVAKTGv6%T9ZDmaWd4pAfd_>Hse z@ONY$iSA_4S(k*FOHs<ld@!eOGJ|LhpH6J<f&W;{NIrC)^dK1P2;xrG;5hiz11#)| zX7`{txNgUjwYDBWz!R)6_mUSy%2qSo&7f3^xp}?*v9iyuwwwqsm^Qn?q((a%bz%Y= z&5V?F{h@2yS%Xot2Gaos)8Ful#caaroyU$yw)hoazWAmdceC!|`6{p(H@2<baJv(0 zJK+4LAk3hwnX?>mq;-4q-)@l?4(XE68m2pRX}kQt1ThXZ^Z)BAfSc>^K%q2%x8U31 zzm6yd_s3@R8(ehW?zD`xiB-JqU=w7eE(y0ZC&JOzqC2>){xIO#g=NkAyX>4ww5FB^ zil3PiftCL2Atd(zb4`7T{ZZ1vKD^uj71*BQDqJap-XpfBe+~EHz9cc&J|}Tfr8udV z$)D^-c8OEhh{ah3e}V-D0#+n;Ab^XP<}u!I=PNW(0T(X?z{uWb6?^#-EccitWe^O> zU@3q{SYuJ@r+FdDop8rT7;;)XpgUVu_wn<x(8YtevgVJTm#uf;1+I8rcG!WZfB$(| z$KnAV9FI6<07c}%Uj-WJ#3v4HK;!5@4pK+|3-YN0B8vSN<N&SWB#E2<0*7EJ*Ni%N z_uk%&j{n!*mxsrE?f-wytXXA9h$R@Zh}059Ewuz$L_<<VRh2MeyGkl3y_g}HY(m1| zs9L5i2~(G-)}Tm2tW{FgR5g{<5KA$!g^*-^uk-nw+@`nv-rw_kp6|W=<JWnf_ng-` z@Aui4&)Lu9AW!ch7#jHcj!x=;K?7pfOC>W+n$n7tnwCuI=vO-EXGiz22;)ZMuLBS7 z5wOu^_6T`kBVm84@5iRIQ?%r=>6a<N?qVFK4c#;(C<W$$@xgHB&G^>((n8-IN`X1i zJ<J&St8P=&Ymt`=aSd3l^E6xf&6@6E^>XZSs>BYOkPXLhk3-01{0E0@IO&_C5`1@n zEZ8^(Zew+##yf;fO0Wm3elMTcAur_e$;6jBm_s(4P3$0aYk{4Cmw_5u#S>xT@GvrA z*@e<bc7x)8Cq8=ruW2-Leb=mBNx0$L8!?l2$PG#As8$`kKwkyt(^rEDs#sBt$;D9U z{T7C@uAk13?{pPvN6E2yHph4|1*4C+xP_MF9E?MB=kYv3hbUq1DS1Fq4es4-G`hC+ z&%WB1$eDcRgVJrat8G$EjT9C&iKovaNT8`v6}wl^WD2e7EKkscnG&jSYiz<aTtOeD z?!&s-x2KsdSG5~1`Tsh`4FWserd@)|8E<cjrIL*9gI`b+1xe?uJHk#Mnd7Fgqba`T z5K>x@jO!n9t*aU1k@AT~Q^eD`GTd2t=xHk{;?vwssz~&!is}v}2eJG6-!R>M+RhaZ z&@8Hj6|=^})co1JVC52=1i+;sUt8T(-c8vUBwml%hjJ2Lm@oAv<BgKJ|3bUjqs(;p z*~cne@@meyWSUalewIOyis-aQ+g_(PaQ%ky#3(o)Y-p_;rBrzjLmmXp#7i8wr8-sJ zh(e!au{<DACfDQ|Pgg@>(oPqlcJ$R0d5*IXU{pHW;|hA>Jik%*mC}X9^`{H+fMle- zP9E>Nrd2b&MY<BE8A7T`VNZqyYXgGP-~0y6WKH3>CZ$<!kDXK1`Rg7k|3Tu(*WfsX zC0z=~S#cNk-mh#$&Q(%K?%X7=nh**(Lc!i;V5I;@#5<ra_!ZSKlNKjg3D}I&B(~=B zpC)xiM3k<pU2M}|)bv49iEC+HY*VYT<<d~N42R_RS|v{nZkF_St)@FK{E_u<t$ER3 z9v2*I3VktDX6R+gc+thr5F+<i)LOCM^`1&O)fjKj^Z1b5MqF!?V2)B~HsfOK7d>z# zc9^M24eaL)HjNb3P}7>4?%D!h-4G>u0E#-a`E_Tq*xlpFb#feH{~>GjsY$-3>otAg zcOR3swxz6N2b1PSXWzKuDCZk;JRD&c3YOM)@1-%OH|i5^u()T+tnH|l@Ez0P+8*jr zD6z>!6{f1%p)wr14z26hzb8bM@lJMgjG0AXn2|@pyw%=tT8u>6W}J9Fnq3)y23hhz zU24jy^Orn`{K0gwuCs*IB_@@lkFV(RiCWB2tzj<x^IX6`?WY!-COckZc5ubVSEfUb zeUhE+_fR;+6Xd_QbMwf$Y{LD?J+!Wf^cpDIKea#Yxbs;LA(S*x?fdQo)(U*Ht%A1{ zh8q~QJ^3HPunz|&Njo*BWiY+7mrf?cUrMI1-E<Wp?PYUn>1Tv=bCsO>5yR+P^gbc2 zc*5pTjf`|`C8hEwK0`iIC#9<KMSbewA!waG_{C|+x>C~i6)BKg(~h$s;`<W$joR06 zlLt_F36m99|2TlBHqpZ}5+kM3G)kMu$?IxNCt<#JHD*c3TT(c3SJ3lvVq)>Y6+g@A zbOq_tN(^%Negydl%iDl|=B*=>9#)VjHCk2N?X{4)DUqh4CUQOvRgzvkM626BWzCOo zho=b`EbsX>eC*@EewcP((+wTi*LhlNx?4&1szptQCUmNbEMpp!LuYzlMSA(Z46&S) z=8x8o3HH=fl0-oxc*Z7#CT5QbrUCF~n@T~4s)>)A-yYmS(0*;5Jyic8O;8j6RvqDv zb2<jKYwM);OZlm9d-i0Kp3*!Bcu1L(#_lUXUT4tjY7(bM@Nkr#-5eokvIp%>#{E-u zwWHB~#umilDt$$1=+5K#7~oPcTu_Digw};7wV@D$co`a?I!g*g6N=03wqy_Cjp-(P zcGh(^<vkda&=<wqyi5v1VXc+<R6o(qw)L}yKB?o*jz||=X4MO2t3$3#9;8+ok9&h9 z@0eb@EG)_2Sr}<FDs~Q}ZJf!_i1N2_m6ZZR2#g4eYJbQ&cpP_ON3n0rnGkR6fcI3L zj5}-eHQz?7rkRc4;%d(1T5o@r<2Mq0KW6EXpG$W+ldj!FYtCJDt(=99I|5SJ8WozM z^0FsQZyOaDMJjC-kLy@bq!Fwr1$3gAUTY(}aENwtA#J<1LW}JBRhoAY+;$muS(o7= zcuQs}lG)ksE~bzOsd>yEOy74QK^+7?=M$J92{m*abnj}4x7ezmTrSFJL)#-xHM%jC zG9S9vm-tb;3vu<#(Mw!yo8NLH12HAA0)C2d+djb_I&f9zd`o)ke96QZXAf<=(w{d* z&X_rq8VXE%!-m}d#7TBLQyV2+r6Da{>P>GPqnlib&scM-(_sY9IxLGYDGdWqhQ7qv z4}`nCZQ4*~BJu2@j)O+gJgyCnP;EiOCmvT>7TKb3fbpGPQmIcp^5jQ`u<`yd)wq!s ztjL>Ea}UyBH_}^H(3#G3BVF1Q1U2{=WXH{C%{9BJR=T5>7Q4e@Br7FqfmXVcz<@YE zT_ipT_z19~_(K-Tjg%IzvM;pnr#GM5L%UYe0gXsEPcg4MR_jt+1gW@md`g!$BCn8f z^k^f}UoB<?j$+C@NE8`KqdiCq@*17$K}NfZZu<AKhwk~Fp7bEyVnl=Zm+hhUZ(lny zW89!4^T)l*df{T!8=n{zu8DFBcN(PZU@vgotv8pF<n}<R^KhyZNrFJ)Wq?waB+Y`t zMGlhaa@vsjKMXLQV3X1K3jCUJlKjmQnxY}TEG6)|?j@S1A>E0V+B77%Yd@q;r@Ptg zxF+q(`W-XZ;A`(@DBS#2(x)!y?Ts+Xb6myGId1zJNe4G3Z<1i^rpBbH%)7wxE#4vY zVK=~Al1oy&Rjk9IYD7<al2_WfvI!Nux7BvWnPri~jHLyh>f~CJN>NK9Fkcf|JcRmr zk?w8mw@#nKl7e%!OXEnL`)vhgDurs<{5Mf@3bvl5^SsE*GRMbsmlt`(yH0Xa)7Ti< zqPE_@oLXAvMXJ?k>QLdX(z>Rko21FPVtu(8nI>0zvxf9km3133NJV0-Rc*<;GI?;8 zfzE7CTFZmGen>OglfTJ>W3sHz+LIh2E32`Z{m337*XCcfjtn3}WI&tghCt#g*Oukd zqk&|FT-$aJjqXT#fVG=0??^hzwG%U~m@;1kds&aIK`)UWKxGCR*NF^lr`=N%w4%?6 zea`Dby`P}jQTnIYwHJQGgd}vh+0;l2I*}G~?VKNIX(!Sdpv3CdnYc2?PSBQJNRJ`! zbioDEMYVSAo<d8?h*a+ivGw{JtL=SWX>kHqjMO%cw4~|`6$)?kGjV~}OuN>8^jF!G z8P@DBqz~~N)deqJ*|i=PcrW%yjjz+vt|YRR_QE!F1x3r@BVhTHdV%=i)&appj?m26 zVGZj>Qb}x-Qb8iFuxu`O*f32yNU892Evj|ikz*tg%yjEDm_{gNXd^XSpITDJrH;VK zL^j_&Fzwx+SaWRqFtT<e7BuYIu4`t{7ClH$e;giXFS5u;MD6Hd#>P9?XXH#jORD6k zF0~iI?HV+(2kF&h;$2I+$)%~Fn~3Kbb|ADF^l%T-O0M-tp;vm4zKyYnk>aT?stv%! zr_9@~)IWr@lMTE<hlY@tw>0{ne#^Jt>s)9_#wbEl_9J^Z%oC0We4u;cr(70=sW4|< zrJr)2@`F&6PR+zpc0P?w3<uy&aozL}a6UiMAx1{C^)Y=ILWVTaZ2h+0gAy?S9yGEi zX)o6}F49>&$;`Glo`@NtX?sSJld?4BvDknkxKDbLS6}P;y)IO#s!!C3yO2JSN(HK2 zExnDRlOA<%$y*(FB>5NBHto^$cD-ELPRODA%M!ngm4TEoNJEwibLi@qNr-m~)R^?! z^Vqjk_j;v_>G4#>JX-oPiR#o6%#1s;*-8NQ0#2SXn?&g*R0^9ymCmab>8CbDvgeB% z<HjBr`dTj%pm#@8(s|`eNgdn;Z#8RlPrby3l|TIG?It!dotH(Ubi80g32(TLlb_Qz z50`hu1@5Dk4U>kJSJ==}8#;#%Ezc;>V~7$yU_(p1H(+ZtzqV(sZj%cJoGt>pQD7ux zD|^qeWYB@MG7g(B4mK`k*iP0yWWg}HHFmR=*F5h=TlXe`EmpKE3`z+~7vGi(*1{9= zHB!U8HIq*0O-B1B;{j7m5N<9I(>~bTJ17GK0W7cRyj<8zT;{e$TGE@eZ<!Yul<{sg zdObB#ZmCX4xs`Q+Ht9pg`?TF`L4~v(SJv1o%T`&8lT)BOr+khht*_9GK4eC_W{AE@ z749{7>|m#eqw5FI?7?nB*!4S0n$<SN^S8aJS6|X$w3r7Op-H(J;(Al}6}n6{CVhvo zCn)d~g$vFccMHcT^L9{1pEE(2=VwE`;6|4878t}~9`yJ~^uN$`5E6P?(%ruXGS9h9 zv-=WXUro#{cCvl+19m}rVLl@Bg106+q{zhVqa_G~TFljZl~nNx2@D;$jKxfMOzDKx zv4<ApRKyH}P!+C5Yuld<nqJs79^bO0*Olk0xc(v*B2}!9T4ATNULkL_(0F|ByUill z)XMuTGPc#~TevP>v*$Zn{tD^w3K9qXS>Knb(~zSGdoW=gWIjzJelIPlT~aXV5kf`D z3T?e)ETJR%VF1w-#nrgIHBIP8TFPPtn%R#umzkF~VEW_64#o_Ym_CN34gA0t^btE? zjIRc|m!8(<{mB@a)1D(p+_<u<biqKf$RFh!UzCr;8<Z4?pQw+_vEa^$&1b#E><ze1 zy+X-&zcXX#B~;SnqEX$<SS@&wIHsUyi?f5o4xFv|%MN?xQdkQmJqHT<7m`CaO>p>m zF0Kfx-v=V?0zqjN23FU5r>1P#|3Mi`22)DKCF(SY_~4#Sw#biXe}sWgB-e6RimSqG z(}3W3RB!v#YG!-c1r~8Bs?*R*O&cO@8DOSsejQr!JN}rfWp*Cfv|nA_dcWS)X&o~q zXC)_7&oU<^$5;-s)9zpMUJ7kLnE2}(WGuU3^pRia0~XG$Hzi-x2GJK6W;U_H6BkBs z^JKOv^ut$@H#ZBJuJF``A+H@jjf<R>O5KB8g>tCr@*CK1;^gL8^_1YbbcM;NmaSg7 zETZ+Q7G~YbC$b)?bk$%ID87bMAKTrjiP=s)6Z4w&w3X*R|2cnI#6R`7a-aQdl_E1= zyxx^wA56Z{i>>2z%cjunUzLXLz2L26AKvh@R%a~7Dl&+@a>9pr&cxZ%IjnZ2^SRKo zXox>JlNS1s!X8^zg)EJ8&^|xg)`mH}pU9X^)UBmSzxHG&539f83YA32zj(Uf&03R- zwy-*1)PG|Q2_u!H<-nCJWGs={MFLdp@}wsH%gkSm3^RY728lC&Ph}|H9R~23zZc+q z`c?l1Gk>mVqg;Pk{JWVy3^D&7Xa1zhK^C%^zd`bB$7VVplJ%pqW9XDf%mfsK(VEeu z<tqa>V#^hpqpz*=->m-U#tQlvLEAQ^li=i;nv8-ThTGOKq2)|&pim@er-~UuV`9mG z))>s0Qc`Tvpf2S`P&(GJs<56}h3gvMrIuLoQai*`{*tRNu2U<sWb}W@{fr#9Gew>C z8bpyE*Z|@~t71vO;EA7FR-ikhVl!!BPbCMZUO{G+$CR)~rd$PYq$OS;@y18Z-B4)! zDyg%Za1~!yd5$}~9dev?!fT{8>E^;dbU~4Fe6UtV>aN}N!kPH=&oVF@nSv{phbo(p zNRuiVePXz+(Z=Jss^X)>ix!L|YJYKTno}tDT*~Jh#^<RKrm<>uqbnQv43fg6Dxh~q zqT^W}bAUE~oxIUX3ICMadyoH$s*xC}xp$7OrByN{OU_3$<8{*6Lv*xaA9wU0^yKTL zJb2D|e{Aq#I}xP7oJU}Uu%PsgSlLgm_1zXmJdI((rUI5oaC8+d8HH~7p0l*kXkrew zAL;x%+lC%N>D~U-_DyE{rk-9MO?o%?I8>k8(x`=2uwVu(p<TwH121ZF7SX9=NCAB| zhBT6mf0bqrC2g3-gT#oqRqMj$gWQjMMUEe&tNUIUACrEee(bcIo_K?dcU^!ofa@7? zL+@?cX)N(|p6C4)s;BF#^XS;I#J_F%9;;==2&3&Q*DAZP$Kr_&9lNrgWh7qx=}I?^ zB^{a;Ph1w1o)&5svU1o*mVHp?d`vHoC2>9}N?dtZlcBzr;(e0!#dG4>1}An`LEG=? zs5i+V=Skv@r(*p@y5&t0<2+u{q7GEONhZ3!ekfVoNl-lTB#j$KdcL&|xn@+}#$-xy z*<O?obg{kHGnXT7vC@}y9A+^}8F0bCI`r?Nlrnl2b~4V6;Ma1Odwj|I$|F*S89%Ve zHaep}$fVoW(aLdTRGX=haJ$^TZ^mu010#hxQtZSR58O{DzeW6<qQle8_VNpR=&HBK zM0xoh1Fd|EjPfz4cV~RI!fx1Ff=lp*1*MBu;u!M3^X;@#LF2}gW+NTn;(8plvJr_W zVSSO}t|u()@oR;rG79Zr-Gfh@ofRgv0;^JNe+dY0;`j!*2uE?gI7%rc;mID%E|%t= zAqlq9)8mO>%L0uhwSx6LOzQ4<2H2@}q1?Zy8tvq9s(PD52kP%wGH0SMQem`x@AeEC zWXM0&!fbNWLM~ltH<X=35*YL+>HBY!Uar5eHsICK>{dK__-zs#lCES6*w_hFx%4*c z4Oc%x_eqV>)H9x|ZGD5PRVBvSHxRHcEMOBesCEKr?b)ST!9(pM#du;Wjh#T+d1|Xw z5+kCe#AusLlP3^g@AmbK9B(n6T|%mH!K;BDoj_WI`&FxOkG_~E5ltm~S?AfDOTFn- znED3nRI$XhpJN=~z?~{8mYVqOVv^sXJ`;)0g#7)Ml^82lV<hD^XcoIRi=yP4R{X88 z`*7S$Qb~5)XIWI>fShiy61SR(dLesy{1Z7|z+^hxoL_3t8;uJ}Woa=%l$h~7e11sR zPb9%!1CQe&vR9RtZR;)U$xCF&fc5msL=x08fAi*jCM>)n*<<${gOprZ^3lGdnXC8F zghWhJ4UQx0wQWDMq}!sEmsp<?8#sp9RLV=9s-kMKgDwQe(r0m`oxZZoa?B-d3d)dT z>7Ojd{WRK6C(&ZT*7d$Y+iFlR!ZG}GE~=qt5YO}(4~C&YFlQ~P0mgKDl7ARx^fiMm z7=l87O4P@dYnUpg!&=D|c1t}QUhK3{Biu0t`Y#eaEEFrA;GM!M<J5)BEdN{4Qvo<G z6m9k-(h?IuKTaa8{IL--jk#<)t9(XPa3>`o3D?)cC){9BQ`=kKK~d{ZTTCV$UkMJk z4Odo`7`>|;4opSHLnH`mww=Oi@pw)xZZKRXI#i^5NFjmg=`qqXq+8P)(?yd>tM1Bo zSZi@%FUE*YwQu0{xtheF&<AYPY%jRL+K5*%R&>TU2E%e@Hqk&&!sE_jF*{iz8WwvT zZj3AYi)!(aI(}2;jUeMf^wqeOm))vVdEp85(UD&M#g25AO5x&j&Qi1OjjxM5;ABM8 zlN8+-^t&VILLC{T7t<-=?b^}kG#0o040~KLgvHYlqOUn;kY=%YZjvtLtT^O|&2v4= z`2k;vd>8Re(^yBf^O=9`3QIOCwn#QLPA9BOMpq+PxAS#!WtTqh(%w^u_e)|RPQES$ zB7X-05v2@5tw3ox&s*#Y>D`iT^!+Ks^m_S-+eTNNmr|TK?dD=T_TpK+V8njWui^_E z78NE17S(Ky)RlR=?v31c`I*c&z^hi5=H%l22t7EtE>MoiDCt|-A&|z!lMg+_Xs1{t zCt_Amm#O3@dHJgI^v+Z?vBg=BX!JDly1aPKWBTzlk|4{!L%pV>Z=}CPdrv1iFutH$ zrjvbrinBH$8sW<J7WN?u4SfwAmDkJx;%6yTcmr=m4^#T#EB?ZJKg+|EvFv3=Wcs%> zV+KhGGPCED$C#T)@^QoQB4G^f=?f@&F3DHS6gdU?XQ<yy@<d+jVW2*LBOj~$U$X~q zy-17xMrOIFqA#*7Wd##=(7v-sUzd|ZC4Jj%bm=UTHXtXIeOrQ79Lls8<ZrZObVQG- zs0Nb<vSm@r!W!?emoF`6CYH5E1?E&PK9==J-zbP4mruA%!`>lLqs7n%XmPpH8h1>| zcz=P|D7W{rmv6a@87b(nh|2b<mbbaof?xKl3|C@*h$Lz3X)mw)u3mC`2$Nj;4ZZdb z=@unAt?Gt?S>V78MQ@%!SZWJTNfyI{?d3-=)LV@2io`1)@KsQT{|!tyiZ-XZiVal3 zz!W-uHc4zDHWSL7sDuR<?y*Klta^F+FX^q>Bt%wrh_;zSy84S^{vR=4?Kdzf1~H^k z9Zz;21hC-3V>)*Zc}K5c^DRaCO`*Qp&!72rDyqU$<c)$d(BCf~ur;j!7ts0X6fSO3 z17%#t@;BxRinhkW@;c9vixM(ii91b`tMTabGn2Zg8lSBylS08B)qTbidP$F{8%%Bb zN>m|o|EUy=2oGkaM@Vj<U?|0EP_28dvQ1LQ<Z4XKlwVyzzn7t(9O;K~*^OKuX0{~* zqd(DfS|A(zu}?SFv0Ji{8lF!|+%#k=+tl+6lDSEz+^}+`RR(2ZCC^=#byGEe^iIq; zuwYNz8kBB(O<9%H%TMu4%vq!4Y-($GX3zCvW>=HyJ<Dy!Ji|<gmp44Z%Vx8v*Sn;< z-Um2N$(+YNpep&PPJ&4tEZ4a|6_QT%WBtz>)6?%g@bHW}PHyvg`grwlld@8&t5``6 zT8-{D<>4p$QVtXODV1PrVXgCYuT<7X*`BX_#7vCtI@ercd$jUVy$o!$$aR4KfJC;~ zp>ypuS*}#?GhT=2Ct2Us=^m|=Gts1zDj{cuZFP>rq*mALOGy=c4XV2EGLx&~c^@+~ z>yZjGL0#wlBkP^~;%ahPp=xJt=W26#t2)X5Rbc`mzceEr*p+seOKy3HWijHq*q<!$ zKp{&cy}U%j9J6G&=L^=N8|j`z(y3`+wNPJxBT6NH%wA@x8~rhnw3U_ZrZtIV;wy(x zvoc&ZnOrfu^(E?@tLw(b(Y;(8m>P7h{6|y;-hN?4bz%+*$WW!~DtgPTXNIfzKc?&F zkq_OaI<gWwItx5D&;j$w+ZxeGbwq6Z3Sw3-qe~W$;HFquyU43lz#)le&!m;}NiWGl zgtSedAS;bqz$3l&7N&kKS?HQf7cU^aq{K+sD;nmNIjqMPke)<-PC0-+e-C{KwW^;b z9lPcGb;<Blg=&_C<D*tS;<%?;@eobr1GXZ8l3clm^@RG$J0w3qi}2uwEWG^MJ1Ibc zxx}!C-(~5N)xAwFVGn%U{~-8kZ~rqnQ`U>N^U7?HdmygG;Xp_#@x;4iruC`9Ygx3w zmo%k&782L)0iz}1{4#uR1H&F*r}S-g)B2n_xtcrmQ)4trVGrD+b@BA>Lekb#O2TZ3 zJ@JZV<)T8WT|^S4nCGNO497S0<3;2m`GU@WpLFU_&~=?{kn*ufk&L;z0L-l(3~{eU zUKQIj-Jf7&_s|!m!e*qW-zOb9FMO)`$Quj2m~vG-Mu2kI=c8f{J4M7DKV<VzmdX(< z+uy0r--&NfLCgx<VC5Z)v0i-YOeAC8*8Mg&<z*afeBd5wd++<pK7&Tkn9jt{w_r{R zx{KGPux##bYpT31mZdtIMtPlu$F`d0{hf5^lJHc9uym5bf-ouC?PHrqZhXo9mm3&o z{^)!ASZL>u{+hO3OghTiUZBGklOUIr@$5BBY>nSemoCPav%uqHdVMj;lx;ms=Pn^Z zva*A8%M#MHg&0BHDvHZC$A_|#!w{OGELyRIjPr4nqIWk$sXz$x^3MYgSIWe)Uq0~# z9lw-(^pfa6PJ7(0R%>%nUN^BVm~M&}V^S-4>g9A73uJlQpJ<?-wDS-x1O#)#D|D=$ z<OJoLQ0O#B^cwN2Ersf5Uvo8PxuvI-*bFy3`GqcBM#i>r9IzOT&coO&EG~B2YOxq6 zV5o;#SQ#!b{hn?yko`e1b-#C0b?*0WUieOObL%G!-5j{Cp_^re%uOGvSx%;V<)20} z2o@uW%)vS}t3~!ZG;=u#^TSvm84ZF<BF-q-i@V&kYqw(5JTA2aa%>e5GcMQZ<K?8i zDjn0s`5UND5((^^jrV`Nx3f=s{0ZARb(Ot%go6#jbBq)TVM<XIN#!u22rdLy%Fvl% zO9XlP19WK;iEC2X37<M_IlyN27c8S<j?&5`(#&r=Mrzpk&$>!tcqLa<_h4#vTxY8y z*j*L+4sOR3)BEIYF;9XkTLrB||0$4dw0ieq;Y<=ucOe*==dRL!KwY`=1Nwe4>DgGc zlya5rtcHuh+S#-q850GfaYSjov3VvoXA!#Fh>a8?E9VFH3<Tt-(O@I#EIBh3vs98Z zp*lL-NZPc?bHXTkh|*;$YHF7sS#ngQ%tj4oRt*y=Qoo~A_EOGBhQfGU$}HHHsqe7W zcb;OL7dnpC8Hul63_!(M@f`}bAw0Nzj5D54`1(tm+U2SkdvnYwjK@NiR|~OYSY2sq z9VF0C5$hEzcJox&?Tu9MbpQI472gAV=AV-tVpqsK=Ok<nQC?XVF#!v02xnea@{(^V z5dPgoi)zQE-cNCvDKUaW2UAN5`J1|U_a|6>JW731Nub`a0Ga4Q<Kdf8>5F1zF1v{u zC%r&sp7RBYR+thy#fz{VF1dzUAeZ5CKnmTf%e|3DxT1YID;%syUbx97K!zC)+E6^p zEU8y;?u_X-e}#+kA13*u<C%#<r1dypNu6L^eM<L|(M_yuAN*o3K5v!E-_?}K%rU&9 zE)I~)_C2^dI#&4{7x%ZMPHCjk*!Z|bA@y^v_0vw$81+Qlx_<Q>B5sypTV<AoOEWnE zlhFsvv!mHWXVFRQ1FmOnZNPXWoVl!m&Q2qXopQty-1TSrFpb2tD86s7Yhyn2bXDBN zd<vXG)q?Prujx1(Lvj;hmW5l=VO=YXrn}Nfpw=)PBY{ZV+KtL(GWxG!PUjzm*-&)q z*j3Yy)5*qIZ75vIVD_{TmKAdUqU$9o!&0RJ6p(3RS0ri%qR&`#{hCzzdRkJ^mAn3H zF*^y{N6{9kUF^Z3ZuDvfN%s_Elk&<@Sn<_*5)@h)eSZa+?l(12+z}F7&^{;=1OI2N z9YJ{&<)@Bd!6;rtRhgu3qm<dqLtpLV3f!$cpN_~Rou=gc?N_tBcWtATB|(`tjz}qf zW41TuK+uZJ#JcQBPq*O0YFv=2I?2wlVxO}(KjPmE>0X(2Rnmof!@tTsKLr^7fnh|? zZqOCaxlc!|ByYF*LY#-!*bA0^T%V2g8H^P@`rS&Rm51Vm^k*wcPj#*g{o$KoG-MU& z&{ZtKCs1CoDp#j_Ln%lLIBZ<PyZd5fbH>3tb6F<MSVem3;ev0Mr+uZ3PYqwj5Spt> zx@XkS5VLb&n8?KHZ{ss;c|f}Fx&EH#B+OaJ*}kRc2)xUk^=yLIxBVSVvEk!k#`1Eq zEkcQLFAfp0A$@RgOC0pVj;OF&y|)-&B@w?f1zR4Ewl@BGIbH{no=h2b8vCxSERxfP zVEZa$u~&g0{D!X9+R=P+GMdF_s{=(k0!=HM(ykL{DT?zy>i;u>C2qIzu13(DqgMI` z-p0YRYC*DlQXeJ01!vBG!39?6VwDPPrV>`3${Zp(W8SmDJwhtV@!|-o?jX*JoeDvB zUR}$20l!WUNJ-r=2wOjx|DZkyzMbH8mug`+u3N6Q_QBhY0as{5Hu2PBl>uX}<(op& zPZ=Z8S$v!;*kilaZHnCx%^pp|L8SH@hOn)Njc>C3T{vK8*l>K)P|p==CoCukjN|$D zuRL^3T~(Q3QenK&&t9<QQzY_<Fx*w`>HN4x8F?1>%4_9#kyZcpJ$WRaIGUz*)2+ob z1IMz7cdOun_dEY2mCqA{Bu8*?N~2F(XQhv`NtdY#GHFJyVIHhAJ`?xlI=N$~wu|4g zVK|#22Emq+`eaE9R;w@~PjL>n7vE2_HF2)PE39H2*xpxi)&FH|$T0l$g=A{%Et#I^ zYS(mi6ds{ztBId1cqZMwnrLxBF)dzA7L4m!^*@z7l)5{t)D6=~rH+-jfhLr=VGT=M z=oCxx4D_tSYNZ0V{i;;pjvl4vHKb{Luy&&JjikuOb-sqj4eE_l_HMc!e*M?iXV?;_ zR1)J$2VfPbtgf;6?xM6^O^VcsZc?O_fp*QD3RaI9*A#cu%SC98vp{TYI)?T&kq-TW z<3>7vE!n!!NwVedhn4Jdd#uMfX+z!!W1oSGYUQs!uC@8RReKS+sBvQb%yx9Mi3Dka zh3M8dOsea(;StQVntpF0ZC(o2Mkyof?SJBdb%<bLsDJh9NM<@*l>EfwF-*-7RJ)cm z)dy?C)2;t%rF=;1Ck-u3sfC5X9mNGQmJr5pDahe%J8S>7x!N3|oXW0M=!JD5eDDs| z^IN?8acRocoGWK9pD($1spR67;;ZMcouk_I#7)1Xu*=GOg@Yn>bF5A%=AqSW92ACg zcF!%D7mau?CPF!Tb4;HYx&@z>RjYh155YqVGft^Zil>eqiB!;e%2o1DQU%Y%9)iwY zu6UBU_m=MY1=S;8?ct=4nfU@7kC)>(<-F%T<Y|KG;X}2r@OquxSMfYi<EuaHo|L}) z(0WgGm7}(qZkoNXi+Taxd00SYSemUm@~A`Q5nWc5;>p`pu~SEtjEZ=3|C<5h){j%Y zRrTuB=#uD&;j@kVhdZOiebin!%mRW3$e(*AKElS}cpM2~p2!cso4;&$p*1nP;z(kL z@*}p570;Eb2Wq3z)>8lVLr?GiLJxKBAQyEI4z-RCe^<G$SEVpuy{%Pl-HZ}mAg|zT zx2mNoDKbh(ig;5<3ST71BWDQ5RM*tsWqqC1Uj0MXfc0n53gZ@ijb$b3!s9`+Olr4E zHa}ughgGvV5iEvOFN<Q6G@U;dN2Qtsy&JyCD_~8N&_uq=O~^@++Q|ypWSmG9WR?{p zjY*bhEHZkWyf5{5wAn=T;J!T9jp)49e)8OCqSL4Y<++)}{VS=kcWAd^h|ODlxBi1$ z;kYjbE^Z7ah?^@TC8<+w^c#+24_@)K4oUvW&oGF4h8gQ;7!N=59Gyu#y)#KvmS?j} zlqiQVW0mLYnL>}mM;PPwm^Mw=P-u+wT!r_VO5P)$tBm5g8_$vqBHA`(U__iD^w1s0 zSsaJ(ZkFi^)7gZ5&!0nmo%B%uo!mFN=+*q2!aiWy*(kPg(TH8NvYbs8K5(;D?Zlp= za{_ilicQ*y0;E;;#Cwo=O?g*J#L$KHX&AcHk|DX5k}vYK<+C9fuEgl>*Um@EMwU(G z;pj0r={>(kND*4o`V7J9>JSFa7DHLJfOxt!sstCI{Z{DlUMgYc7gHKXat{u%1ohQU z!A76ZK^TB3Jl|MBJUv$sQ?nIPOqm;RF#JdMBdnr*z2rcD+gr-3mX+@N60@73jB1Xo z1PTDs9{K3Y#O#-Wf{z@8Sp-n%k<~zBfMSny26rk@+z}<v0-%H=t-(zJG92j!v-LpP zN1TCjfXqkQfx90l??~;z#BBXZz=9*+0hRz2AGx=eH;JY8f>pa&@0g+!gB6V*rNxk( zH>HB4?QVNRdD*hk2%9R{6pn-+{56SHyCtcDu6Td4#IkbdUhD;}-fw(oU-jGl52D01 zf1<*+n;H|n@d1f$UB2opx}A0VdYN(jNn11i80$z$FCWeyy>IYxnS#b{B%v~FmRz%u z1jy_q^w35!X%a;*P#u}^>pQ0=b!?n2t!gpUNxDr{0(|<QTF`mp3`;?yZ!+<kWVaoO z0XSDLeIIvAalMg*{U&=CjsETa8*JBHO&5O%?_#p(whu9vkd;Y`Kg33&3mNn-RJ}*K z6%UKu&f4i1hVf2(c<27@et6`ceXyt1u8m0rFZ6bnZL9hr<1PyC*5Br=eE7(}{9rf6 zH(J#OoWBIWlJVaTfAR|Wm5=-r4nFw;{3NS7RN~)f{5QiNF#i2V{&5E%F#a;DUage` z?lOVV;q#fm-ADeh2j_o*j<Lh4)=2z2jQ?8ryNrM5k$>pHcNxFds&<n2w-|qT_!o?S z>ydx(!7mv9g;lMP_!W#F6~348D<1i45AJ3BYQ0r0lLW3af$;F2OyK$>f6c+3OyDU} zKqJQR8sonk-i`6EJ@PL&*p2a@d^8Q*Y_-VkA70xB^D5<!{3{RC?n5u=(Q3wBtuAMF z`-Gq9hPUXiBJ~aweu>`9gY;>)Y0_rWLY8l*TQ(CPS=l{$cr$k2m;FLZnci`mK4)s% zTeSHW;xm3=h3P$|6OQ9vWDd3t@7B#mloyfQ2c8^6V6KC!ux(HmGj5CU+LvuIWib-_ zKsC7fYv9UkAE?hWZqx9>0a)TZkCZ-8xEFybr`hm9mVcG*-a<ON%bdedcTC&CYWpL- zwS~0NxBUS_?UT&%lJNcm5YHmSb8ml0=)dRXBwJm?_>02(F#a*bbZ;NV{|<bum`=wS ze|~svKky44`4{i4-4Fh^Je9NYvNrhdhF3HGQKa(TYR3Nte7%$Cz)>dfPWTcgaQKmb z-rgll-~t2`BL6Vs&j??{_=g_(oA)ka{I9`BAxB0XV*L2<BaD9l1%2-k#{UX@BJvL~ z{-p4OjKBYpzhUn|#y?|K>&23ut=`WB-VX221U^U7@9ob7PVthSt^S<x-wf}=_@5#1 z_x54@6IQ99XRAMB{L$gHeZkK|^6#zv9Q@<Fpl7S|82`2KYR2D<g1)z!@e5H?#FCz^ z-pvGthvQC~4^b3%p{(zn50Q!a|19ekCJ_aRvJY%dN()N+-WiY>c!U@BE$SUiA{-L= z6q_1%pt$eFLpz$bUly_gyG5PD_=6aK9r!tq{Dr;xkC?zgvAl0lZ({=enScob+fWkn z{2AXWmiR5|j~TxY<F5w)W0Zuvc8vc8$~^PH%=kSSKMQ;_N<!X^&%objl=xd1zZ>JP z1b+)kf<A9f<<?Dh4c?-C;|sjTWZR_P#DqFSNWa3SRBl3PfXu{=c1`|nl=V>Chw6<? zq9Y_GrrQ+CjVKS07`OrDeHSFOwvFlyOu`=$15;5SHlRd6qV0Mlxdjp$)R*;4qFuP& zUfI@&3bh`E0wN9*l6(h5oKWG`GLhDB#IYO|axF>)B+6DJ$#Wo~K!shyBw9eC%wQ9g zYfvsAk)MSaZ-a!)wqBjhB$`5EPaS5hSD|D;V(Uso_#;RVn|_^o6%)alT4h#^t&uVl zg##jbJa}8S73anU+gf!dlW>Q`oaZ)oWd_OyBqpXaQw*&tJ*p7_m}+6_NY%-lF&{es zyBQGURK)m0G51%i)0jNVeUB;>!W88G!8w(Ij$~t`jaDbynmS`P=&EQfKgpig22;&& z0BGsg2=Q|2@(JnrO5V|L>qjul4dUm@$mVsnckzabX%D<{dqF%%CcQhajIeDvRWr$f z@SLabe?kVSu|9_l+1~WnC#1bzn#^dh-!kw1cA6^=@sDCj?P>i(o@_TE2KIT4wqemt z<lii0i*EP6^%mVzH)i`TAN+XvRotTW)(4rZ6gha=YRMR7_;SjEEA1s^GhoscI_&*b zyzr?{e<W@5%v-fZa#n4Hw|G4Bx1U?)_3TacUPm5cURRe%UQcsoUjO_Xug@EYr7*95 z>x$27)i!BTMO8Aac*OaU=jiNh*nU`kzL>7vMh@uf19Lu23QQ#1)_XTsocLdrTJQa| z?acd+j!WLBK0ZXz{}*(w&az>~>Yj4E)RlxMgfMF%XX&5}mr??k;)F{nAw9U{i4CwP za1ouM@SasCQ?^9fP`aH&tFN%}^VvJ}&UVtW_q4A|FHv#-Uh4Txm+4OA%+9Da)B@Eq zjJkhydg!oqcJqK|;?Dj1xdPWerTucq0KbVpH2pbq=;<(%s*j+Yihj)H#<w?xdar{I z${T<4`37aoj3<@6aGT|KS({g2+PD&nC{{`_4YgA}lb+5YZQ{0GeEXZX^V#Xfl{k8P zXq{cqfAiW8m>CL($5rgHl-7kCqJR3rTMW3%Wj6g}Tcf@&@*gvP3-Hh2LJe)<5%Fy+ z6goC)FF_=7R3$Vbm$YT25(~FD3Xw}*k~F6-&@H*7Z+HFipmgWkn=q3i2K!Ah*ej%@ z#0y>x9+zSnf_q2KP?sHKv<EbJ{I7^b24|h-)7d+)AsmKS_bsH^JFv5GJv1pfbLi<E z<n_){vLEpNj`?!pz$YG9Z|`#HxxM`Sx+LsP^K@1`Ulv6-!b*l^8XdY5_CJ6oz2g*W z*h%`lA=-=IV=v#G^_v?`hQ(w2$pbNA84ks<EBVgkOC}xO_TAQj_~!H?@{Yp<Dmr-k ztuON33#k;+om68X?R=%E<6+b`U;kS9+3wzY!QAblpsOf|(`DwR4r;KV-?25FZn2OF zs)#7NVPX=MeM+XNDhAmNdur&cPsv<az$>);Q*>i1dRd)zks=~XX=OeC57L;(6ZV*` z*LD+cA~!ExW3AbPGi`G7Q3Gwh7blX;yKCvty=1=J+@;D|u$LeL3HIIe`DX;Lyfr#a z1NRYsc|z84YwSLvBcA5nMc2}e(Tk4QGTa`r0~B3$)03Z*<+83%t;4<`FB91w8(qGi z%yhG3udeb8=H$(dR?uhrQ9I3Dw$L_KGFFBowF|A}h}^s?llDG9#>+fj(Det%B)NIE zfj&Qg<6GHF>uAe^*pC`xry~!N#8wG=iZFxdQu~Ij^%LA;P*ghx4KNy$IIMA(A0%Ef z{Xb~cLGp%sb{+b;;u1>s(mQnAAw)5|?i-qMh~zc$_%SFQljTidFCm7~jW3awbn=&E zt0<v3Ot#4e;%w_-Y|$y(OV1xB*|M@HH0}ubu2n+T4yCxIq=)(gL3it>jJ5v(xF@X5 zr!@8`iRziK=S;g3NkPfAIvS*e``K5s>JJH>XAgiS#F$%)4(M*aA!k?S*|P_HLeC$? zNwb8mYpEii#LDK_>6m;H(GtrD>@W}(ZHsDcC2GvNif82FE}WqI^GUZJsjO93KS7Ij zKbJlAEb3FMSzEU9l(&`DmJY8Tq8o-Zz98{VJ*F)SNL$I`P5W;xPJ7(YqB^3X#ax{3 zC?LMfB5L5B=0kL|pUN`S-lAS@XHRvypQ8I&_0I`?89Mlw!gLXL=&=IwR<A>1E$hS1 zwVXJ<EXq3r4h31ty;nee>C+o&VIz=+yK&V3PUN7`<N6bHR3Qm-I)4vaNGk8IqUJ)< zqN$NpXa5uSgbCFoT-+$1z3L1-Ux-R2CBP-bt2~#T+z|EZ5(~{9!X|bwq5KkUa*Vvx zP1M}%Aj{4kVx0CF-A^|h|L09|tRWLE=6=1FTZ{Ckvyb77yBMXD=jg^`q-UfU_tcX} zi}_Dwy`Hueq`9Nq){QZxEX$TAl-YDLb6c9cl+x?e2{wZDMrvV8U`yKUI9c7fayAae zwYcFeMlO7ZJ)!Og)+{u*{*na?{^Kyn4q6$Bwo24Y-A)6Ck|&;GI=+-EZeADDHfS3S zj>3t<?5!0vI*P=2Yv*MAZXY;q44|JyVc)(sE0tD7kw}m1^UpE;?8gqo*|Qrxw?+>m zKg+x|6Tj(r2DiyY2K>5*Jlk=Sh7CvKpeegR=ME=}<k|Q0>HXm(UY<SsARRM;T##pv z*hK?l$Y^=?`5bD9VXP5mdMk#^YR<nH@3A5r8NzNPmNLNlAY68>(fVTIBu{u}K6SfD zT6iw#)+K};op*^zh@buX;3%zsqV@G+@&&PWx=79`sNeUblqUZ`LR!Y@>l{l#yFgb# z%?)*qlS}Iy&Ghu*4`hJnyt$JSw9_Zeo-!+bu6F9A8MET2(3h@|B~)_-$7}o(`+_-l zp8u4-@Io6HKX>k&xmy1z9bE*WA@3dCr1^>Q{eu_!2lt+}Q2Wlj1(Rmg>md4DS;p*M z&xP!a*@^S&wZC<@oA#af1YJE(`%dD#N%Q6>*6W$Wi3x%*chavO(0)H+@!s$wG4%Cs zAgD3cIT&3|lhj>MWzZkvD&xg*nK&aITuRzEnlk5|Ni$}5(#@GYb;dM-&MYNCbXzI$ zbDcXQQMaHI@<I^ksZz2;HjmPe%g9oF$+|knpGf*Y({uQZ()B0C-cdr<(Kh8|Djj-{ zB<PQBsB;_ueFoYE$^n@{8$oM9t3YX><)Fo&1)v1bOwbh21khN}NKiB=0u&1B3km^s z0R@8EgZ?1a|G(#j{?Dxn4*ehG$sgs@f6DBy{Qtvh@t;cW-?^**EC2u06!@$D|EbCU zr+<t8%KyLe|IbW;zuN!beG>nx+3{EX|F0(Z?|R$+i(@e#jBuKPG$0p{0#rxq9M3=x zL3cqFpmNX^&?V6M;rKfZItDrn+7HSDSwP!BTR<B?Ye1Qx6p#V*K4>~<Ht6uiI>)t? zI>#6z4Z@WV`UAk9fp&p%KxWWJ&|1(cP#S1CXfbF3C;>DRGzByPG!`@x6b*_1g@XEm zLO@+WV=w{mZ%F^|nEwSHvh?4C?my4d|NG?mUkN}U&xZC;eL&4X8juS}ufSg&0`Uy= z5Ofz*0V)Sw0bK%}2b~5T104qK2jzh*plzTnpbelkpiEE-$N+jDG!K-Q4!1MmHmCwr z4!Q!mlvzi=d`RN;O)z8hCzAfp^c;Sp5dMj=cQhesw9O+jb=^=|g8q3%o#Qd+KIj(c z8mJUh0xAZb1r>pgf)0Q_1MLFkfXtwcptYb?pfu2O&|=U6Py%QsXbNZo=no?O|9e*G z|J<73(EmZ6{82vrr_BD!|39o2|Ec8uoxA$K^8ZgwfxqhipPKxC`ltA>{QoQe|I8Hl ztNs7oC-J|U9e>sT|7vpouDAWaI20R;5za_ZG$;ZT3hE0A0d)Zdg4%<8K+QlJkjrrV zDL{1?GCyO(X3$+w1*jZ!1#}5?9&{RX40IT@AG8f*0X@Uey3HaC$KRtta#>6L<AoT` zfo_4Wfl5IopkmNjP!Z@T=m6+5&@NC8$PC&DS_@hQN&_tiEe0(BC4gpv*1Y#`N&kNn z_z&}urT>=@{%866f1f=6E1pl`*)Rd>SkOpNG$;b355-?!Pzb0CC=k>h<O6C3(tun* z3Q!#a`wa9DbQe?sDhFKwT>_m4odz8P9R}?O<$>OrkvM5$;=H*tW=|7ziE}5;jh{4S z;_UhFU^fw+D3^`aCoF>_py1`0aRV&|c|jirssUCdVc-Mu0Sy3Mh1o;UY|I}m1+lrM z&p;PJeL<r@8K}P7KzX1d&^gd8kQ*w23~Dyiz}fR>&FU!7M1^dTzNHDmYb2V`05h|O zJf@$@`OO;0%?9ocx&!|IME{1b|DB~PaBT7)IM~o^ZUa7Z{Es|nXvQ$xMux3r33Cz? zXH1?IuN^vQ<S6Ys);l=Yprno6WJjsZO}4B}-jArspmNZC5Yv}}W`cxL>vDJ5U^y+- z$aVw-yE+_xpb*eFt}?TR6yj>NG?uj?w5Eydd-{u~Y@gNQCCeq&_-3+c#C6_ul!;FB zrg!Vvopx<58`MTO>u=NIXJfnBys4cgEllXvGx+7;&U53Z&7U=C-kiB~X>(bMwRsDf zfzSgjWg-1Gyz6j$4B7?S4=Mni1ziGN1>FKY1l534a~%#1==p4i!vdmSJ~BUj-Fps4 z6FnrBIviI(2SDpV^FSj(!JtN<yGtC7)1Vws3TP@Q0u%sJfUYfu?;tbC0Ez>Jg0vt3 zRQ`AHL1vHv6bFj<yMr}^jz5tQ^FK8J`ad=H4u_t_{vyZ+`QZY(`@X|*9+U@K1DXdK z1L_TG33|TB;V1<i1(`w1L0Y8ZV5sduGSIb!4#zRj$Drk)0G8KKgFuZylbH<soV?H> zcN}_;?r$UOPER+Hh0trqNvkFXG?0Q^AWRYeW%MJ3!kdP9DL%JW9w*}!dN)B~kc)i5 z;8l-f6^5~L$$V{N!H~@HHsCalCwL2nOpckF-2i{i@un_<;Q+_GflE0q=`9$_C9D^O zzJlR8XB_Jz7=|h&kH<od<yi1VfH<xR6Aa@y{y0)F#Bn?hcnZh2A_T*9j;DqThFKhE z0x#sa4)Ti?ddah;uwdYfzQD;GpM+2v$6hec<k+P#F=TUmSVIhJIqs_^h7BAy2j0Z- zXYkm}@ipLW9ADHI3x*wBlLx$u<3I@IaU22sIme%SC=3TU4$&wKhdIs!F5vhQa1qC| z8Y>K^>&=1B)nll|AibRBy-H!Y#2NjR3d3EFA4Um=8jc%93kCr^CO-?f6UXO<35H;f zLxGunCR1>T7}7W{e-$amaW?Q;j&oiW^o9*wQwa;t>KVh~F~`BcEP%|>-cXx!oG?N# z_;Kt7nLv(jA#hBN$(O>-p&T<cx&e+AG0L9-U=(L;f;yJt15n3vd<kkC$CXg0aO|d5 z7^ZU^2t149FyI7^<ACRJycl>P#~Xkb>$&E0XtFsTIT<&%aNHeu7ss=KKj%0D_%z3c zDT1Mx<H^9+IUXD@818bs3FZR)V5!n*s$g*9SU&+mK3o$7h9AeXfqQdY2^`9C0q`h} z?@U8g<k$;1f#VI+QO!730cUbNU<UG@<D)R&p;qX{5KRFik25X;7jb;;Z-U_*#|MCm zIUb6da+%{!w~3*g;}GCm9QOmRtd|FV#_=d%!AVMq@wfHF;KVgkz|e3!3%EJQ3BX#8 z7Xt@!oCX}saW-&ojyC}J<9Or=q+A0W)&LJ>80Ak`1)=B$jMxTvR0BM=0UqA~$2GuH z8sO;-@T>+np#j#<YoJ-!055KU4GnN|1Dw_XXEwmu4e;6qctZocNy2)8C6T!SV_O5f zqXFK<aRub_8km3H03T?84|7}uwSZ$cXN7_3#kK+JX(!(Pul)uEjWez+B8E#GGxc%< zT*|T6FDP^zZvei|u^;fQ2IhA;4uN?k$7#TiIUcHq<{8&41g_zD3NQ;T3#kuO4aXzC z#b=u1vx|v=@tDjG$ams67HTlZ3!#Q^oDH=%$2*|*1BvZ_0YE5cT!tFP@iVAHIo7zK z`Q*4aF!O|YJQnIGj{BAf2BsGSbe9<7I6eS8o#WHM3pqA8qy8`En)hKD2BPJ12(i#I zhc-Z6%kd$|Fg-J`gba%X!+B75aUA_U$_K|akSXB!G}I!FGa<wDasl<v>mD&&=8STv zr5vAw5Ysac{2+6S<9@);I2Mo+H5|KLMiUD?laGcBOEHF<L-pc#9AucDnU9BzR?jt4 zpb6wS0hlE*6AFdekK-IzW_o6x44DLu%VEBdW6ckOVH3yCfwyrSa0M~uct7xCj`i1n z#HW^PnwJU&t*hk0D&Rnl_X9_BJmn{}{~WIcUdZw8tH?f%y~^M*$0N(}0pmCY_!7sJ zz?WTl`@ijHbPhQq&`n_|=XeM3b&k!zw>T~azRPiQcZH#n;|;)%InD-d4mp-uW3J;P z$8k9@^MjcOyP^Jf;+pAT1ao`|$s5P<#tL*S>n+?A4A~qnfcaXE-C)iF$Q;dsx{c#u zH}I9?xB%*|2IfpoZ2z_QQA#-@7`T|@%TO<IJO&;xJ@bH-d?&<?;Y%=g<G2Etr3N$C zLiOR;<rcD)<50LC%kla<f<ZruYoehsa6A_{nd3lUmXyp<Gt{*lkA$NvxtVz$)J+Y{ znH)1;49^&k;Y=1uj*Fl&g1KHe2XLA*Hrzuq%yBs^lyc0}@&@=i$CXfTaja>CGQn{$ zFyk?g1>naV2Q)#|<+wSp(1^GHp?1_)2r>&=s9qeWK&Uy#m90=;IgW!o^MuKVLJi@# znHQ=q$K{^Hz<5k10rEpRP6LkRcqv0SqQcs$1^VL6TCYc0?ka=ZcRT8=M6WqRhp z4yc<ruED^=%<;%|XeBvjAFW;WxDr_dEVlpbtMz#U#sQAmSL<*C^8$`<A)u!@7T^#I z2n)n5sHGfdw8IyY<24kmC&$ep|BPdW2kL(f*Jv@a5kSnLeo(;_u?DIW$4<WZDsp@t znDLnW6gbN4Gu#QPAID5SkmHB`_(F2L81l>yvHc$k5Xu?MLKw%-1CVDNXTm}($2)-I zIA-dU26!9Cy(b{qIc@^Hi{n6GHiTmVy$kiUo@-WtQOfZ`V8&!ZMUT;O;8-h1oyORX zndd=9%klL%#Ej#`P=h&se<CrkF(Q+n1vQN0ORyWwvAzyMQ@ExM3?|4dY<ofs%rS<W z17~tv1l7#(*vW_u$Af_xkI9!qo()17cEg~vl;aJMVR~j>2^p`(y!{ud(39Z|4X_`_ ze!!zR-T}*FIWEy5&>R;*ozC$!$gmMF^I#j)c^qGac{0Z-u*-N%Cantn|6N?O0iJ!% z@h)KIFcUfre2HVXCTMUtemNf5#BodD8ji)0GUS+DHd1!t`1%aQo@3!D+6IQv{}4*w zL!sl0g$q&WICcR};8=ye-aL-S0Wah@5O@>EJC-139J>IY=D0cVIgSH>pK-i<Eive8 zxJFoq0_7=%djEQ40mpu*yMY`twNnEe%yDn1Asoj-WqKC-P^kSl?!6H)=6K<A)Fuh* zg|PsmIOFVQ<T1y|5Q^h?18@z;ao5nz@{&AYeFg=`MOw5I9ACm%&y8a?4$#!g%h0rQ z917gri?{!2n3eG13>GUb$E>CD<Jd{AFa&Zu6qY-293r5~aU2(gl;Btc`Q99}>6?BW zvq5Yq$DQ;L!fc$FT+wiJD97wm8O<?k`(rr{RipdGG3)P-<v0x<kLUO_QX;M%!_6ri z7XeS_*bDy70`4S7J~f9Xfio6jXg!Z(_L*JCaV#bhQgN&uN89CX5ABbTRdyo=YIae0 zebHReT;}Wqci23m8yzH5%%PaO@*`^cokH<SJLU!ZNz;&m;J{A?eN=4J#Mg(%goV?e zj}u=Sr&RQ&rAoypqOf*TDSQdlG*hUolhuj^#EJn{b3!p?sh~bCiVzX4(<q$i=Prt| z)?3GMXNGmGyJ82S3J=8tYk!SGFQW=i#acY|tXRy%ooIoFWYJ)K=B0Qhx)w&WT@;FO zmr%Z*)H&wdf~rKwilM5Y=0SDgA$NtU0W}4&pIZEv8Cb*>UV`8Z)eZE*;g|!(sT%61 Tghee#k$@t_kb%~CD}4SBw_k;G diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index bae158d4577341eb3d752ca13cc66149961f8cb4..090c0cf6ac988f811b220c45565eca027fc21d50 100644 GIT binary patch delta 73168 zcmZ_03tSV&_Bg(qWV0lI1_J~X6^wvVA1Eklec&TR@I|d?wY3dh5Z_|)akb?pkSsAE z8aryB(29NwD1FhQMlFceQm$GnwN+|wA83Gx)dnl?g8t9!2EqRB-_J*~J9FmDnKNh3 zoH^&r{&Y#!Q6rOui72AXKoMaQG2tLCBp#3{CJmupP`HQ)xy#xfcK-NLJ`!e-OGw%r zg^(cRLS=WE@a0I$AOV5W-~3xKR{&*<l}SJF?-Ai?kq$z)N-+)EO0TqA){6*~8Pll@ zl@b9`+oDxZ6;i+;Et!xaTNSF<jiJ`J9=Jc0d=0sfqlKE#$tavuD*vtDKvTkvYD&M+ zNGaX|Fu|OMWyUPw%y$*;fWUpz1GJkA@h)yEqyLOlhTbKV;eQbp8hSG(-9zWdq2$Zx zCvv!Osd5VXnH(Dbl5+YnK^P?>%2*K*CYBHi8qm<iGH5GOKs!0DPF9BFXIxJED5u;N zDW?lW%20v$c?J=SmEp(4{c#OLwrHdicnc!uqa4Am7k?N?gbJ0RFB4RO30MZmg~}Vk zFd<QPNhs+3Z$ocB!OXfaF+~;`%9Bw223jb5!S$uLCH$D*{1&APm2<_9gJq%b>)9+* zbuW1-vK65zfke22h@9?qoFpvJ3)1UX=<m*@^fn<?rZ0;u)4wcGq~P9#-=Lp_3+R(0 z9>~+u|3D$4WqyxG_=kJ{`FKQVxc6UzHbGa~9PcM#6iGN@HFHshC|xKg2-Ged)4$QU z{*4}wC=K^+dpx2b+`H@X2qF~ScvFD>8btJ>xiB8k=zIVTF;GSj<m|NB<eaoQ<lMBm z<h-<b$e$Xe#g*2$InjA+Yq-msHu<R^O)8f)w(EsAOe$vHDZ*t(x-K;?U5DX#4jC<O zHvTEe;<?__`5bKV+}!(YKkWIIr%j^vT*LgQ(b+2au;&g>n-dQRft;d{OMJSPN6Zzl z??OSpcj_PS>;YU4bPy>-?4T{pzhA-6_}`XDLV^k05~&gMzy4DGw_w18ts^?cJwdH8 zj}|{%+O)i3)g2xAcx2=w{Zv*YEJ6##6Fhv})eLJVvadU8VqazUOMK8ivA0&0;YgOz zL!0zgi8w!rG0;Bg7KWsJnyhjvmU3uHXN65GAed~4Cx*yj7y<1W#0o5oA2ot+lY1%M z@=}jwG{}XiNeg8pNTnd{kFsi7(EHZ<-;}_}M`WPGKY2O)HOI~?VjXn_VK(g?s_lT9 zp<4ZdSciY<GN>eCmqRl`IL#zF`-9MGA_9K6V##61s3Ml4b8Iot@PgW_m61pZeS}n; zzagzU-C`R?HK(bdu2eS1=Hq-0m5s9vaz6V<dfNP`KRFox$S|8X|Ez`XHrQm&3Zi+L zZ7}tK1Bx_9**trnFRXzM?(-e!MnVVopLM^5>wY+O4|lFx3hnRdf#@POhQ(xKk{xya z6XjGWaI-lCb2F~OHb{CeKgo88);?%DWfMssuqs6$78iDtBRO)jZ3uNghQ%Ck_{-Eb z52_xJ*TiaJ1a~oFM?vxg7{T2DmBT-DG0*ySYn7!Z2%9Tahf8Y~Do?_jh^oW9q=0vr zm>7<Oj=H)qn}YgnAR!ZKS+(*^C&5U2nQ5w~RKQLj>KB${XY5#D;V;moxirW&l=_(~ z4K4Kv$(V4_a+4$w+whB`iUiUdG!C<wUil@!)@d~Gw=|Xn4*rCn{Ab&SQ@1$M>n<=h zPw7uUmYB4%THJ}OW_L)2<Qk8T`iVzJy$)Rp95prLxi0-9eP9Z1=p#&GHY?ZR6$KpQ zx@A{8%%KGTtb#TgEeB7?7Wd(-y2001ZHD?(M~yjzhY6jm?E_+KoE>j*nEl@dc8}tK zB4dO4U{v4rA*%bAqed3Q!w{-I80uRdhK0@wcEA9X9875>*#}c~*@<T?rf3^b6u{3b zk=h5uR`(<5Eg)z(GE2R*9Mh}5?juCA1={;LlFc!FZL7X=*27>lo1lI$NRwE;doaPs zr2cx4EXF9Ej@V2m=c)j%ZoS-9)FKxXF3Z`USqG6k_h**J(g3x@I(?y(QdDWVxlV?H zwGN_mXbH;<peFNR>{b}@8b_pVJXabnoJI+jW;h9fy1E4T|8J;sr6xKNzvVFN7+BC^ zt~At%Ck1BUO^3|9fGZuxmj-Tgm@jD3v?@yF2rX4Xm4q7OYyegMZ=|V6sFBTs8k!1m zb^tZH)sE1b)llk2`8(ma1N>`_*wPTLbeOY)9nirl{Uq44Imrs#3GXZQlT6O{!hfAe zOAFKj!dC!@bat={-dE@&BxE08;M?zKV^A1v9F#gCRqw$2GT}4{7V5~7Oh--Ww2&<M zsuIFclRSwGk*_Eu(DIK6`K=Oy48Fmp1(88cLf&?mBVTg(n|(tv9a{fTUv`b@gCSxm z!6rHZfT+kNj^t7r0FpyphKMO5kCGA!kQF&ql?F1fh=*inz!WWXt|B{MuS$0m)Jb|3 zH1Q5r&~@uTM#LeRs0$?Kh;)!cb0Hxli+QPFfVPO`n<WBTVm_j-kk0pXxm45wEQ`V- z6vN)Y>ptj)6Pis2maA~hDqN%SPw1^_fEwN0zBZAey)`O55HcGSL_y7(Jc{Cnqoo?4 zSQm+loBN6>)&L#M!lkmLXTVe;D6R`LaA{~fQc!|4lzmlhY=7FhpOyYvX@Pl0Y@(;f zDgLQSezUB^Df3vE{iiu<B9n0I*wvV)w_q@w`~%C(nkf!h?0Nv_m>dY*M@A@a0mH>> zGAz7i%)m`+mf^pp$srjw0rdf2qcZWWs(`$@Fh>m$?<BzWY#IL)=sMQ<#7cdgPYse^ zKexQh(X5i({+tIabcu65Q7*GTcXphv;3T+VB23XZ1k-}qOR8?csy2&Z6kIJ81|uAk zrt&(AE)4XZDIwDSxT~3ya4bQvj#N=#gp?Q@{*kLJ3K5~V7VpfTO9tBNil5+(y6zRs zO!>`P(h`hot97Vo;FtyLx<Ru3CmPMmJeum{{%DA$&(Lt4*msp!@|mU|gb^n6*+4He zQRb!fFIdDkr8nTFOiMAW7u|ejfJ6YRPMn`|0PGK-DO3{;e`RJdeh%FXe~HL={MUao z2e;D+S5t%GLUmE_S5_*-&#@aYoaS}>H*&+H%Gn+x>Is(=&Ft_I3Fw?`ps*&ELEp+| zOZ;__cyVaPA#V?*uqJdEn(h%G^w+(JG#+DxHO)TA?4c0q3MQfwj}d;7-%xG@oEKVJ zuH-6)hzQ|}8(y-&ul~zFUPbpj#*!}~??FnTE;0&DAC%;)E0|T1ZR;uN&_@_rk6uL- z;Q|EdbOES*&`iPTtEhWWw6I2#iss8Bg#H>|lrEnzJYxHb&#k2Tkd>!-=rzAxAsOs- z3m6lKt*Ssb<cs~Q&y-k{r8F@jVK$kr1Hd{l3@<H8(IzB>L5)@v={ZIabqa0v3>s|u zTz^vHf8$`UHt?rIOhZ);88Hfd>gg-g$imST&*_4s66E4FQBYisrhA2YvtK(A16uMj zmI!35K8Di0#trySB_nomQ?v{5S*HM<_nJ1?{BMO4pA{~>rwsi+|ByX1E?ZiKK&@&J z8sr@!pmNYm?;wFuhSqqm^F3GW_B`1ZK&7|ZUC}-7P(if|8m#aa6pPVxMUWs!gc20- zf&S+0AcdmOFPXBAUb1`xD?B5rLjk{Xaa6~5Eas^!0^L`<;L5F}z*ew36Oq4<x73uB zQlJjS{h@I_8{7*nXvrpCzLU(T$S1(PpzfWa5BU;fHu}+Ld|-k9u3g_F%Fw--bu`cV zOFzlby_9vtHfx<wd#?SxsS?HR_e9fu-TXo_^u?J3^RLzcRCI}f5d{u>ogJ6?g~?H% zOF{|0(ZT}VW>o4sYf-HEExlDrbUuD$%`^V^bgSC7uDxrBtLwy7fIKwpad2d~B&l=% z_fN$Xit{3HY4R4ENz$>s(v~Laq7sKtROt;i@O>a<6Pi9a$Uk$N-kwS7&q~P>!(n=y zX=3K&taDlCbGfr!!G*yneemyEPAybbI&_+~sd1;Q79W_&J@Xl%)kWZI+YaZW$3a9m zew%EOF^NA5i}wghjJ9nJPe>2sO$kQa3#u1Ckk>YnglwYamARi9dgAhy)1<t$jbKQt z=;9jrp0=kIs<53j$@rcU455mj9Q9^eiUf=cEff~d+e73Yky~g1T2V{RJiK=Eo;qG3 z@#=_w-X4cl6A(d=s>C0Bv|E4I7w#ck=6uOoBL$XDE0XyQ6KLyDsNV?ftly4l1kXu5 zOmV3?batY{UlZ`pI?Re}m7}0wmgFI?a@4VD)#aSx=!M0<l0+=-q{oPR;s-(Xgbc$X zqj6DAidQveP=1Oe>9obbq-aCcp(&y2a7Xj-t`z@Cp~~YTH+~R!2s|l34zBOu>PHc7 z0yB@Ucs`)J+Phd&f-%~2pd2Z+^0?j2(fqU}C3I3Kha((IZ7+dJ4#%XeBbvVJMlHL2 zCR%)fI+JM*7~BG7_{k0?o%Y1&@cTK7@GMO*63HOYIUSR;dX}fX3w!=8ze(E1ZQjf! znn;b%+f9O{7Dn6dhIR%KOg`<2q=pW()0+SdC0foZLt8qO(_7jc$p!24+$mV+%|d3r z)pd3fqK!|;kk(~AugzG5T-%>2r?j-m-I|3bj)(2sNiy_KSAB%2eRKrVY?ld(O>mkM zb>x09BtuN3T}Atbx(dqIpuC~OT$Agx7%{V?T!_9NI?fGj)8^qtC5E`l)@4XGY}8m~ z(2s3jvZo{EhhR@yag&U(By0)zN;pL<V%%K>Q`UH0#i$Ql7Y0g*RVZ=TaM`5FT@jh| zx{B6i?v=kTtEfb)LWhmBcmk&kB4vKEa{9?OW)JPnu|3(zB|_p2Tx!QWDuLw#T$-mf z2o_tIZS69&GlXEw^kBKGp&hdY7G!eW0!%f+?6_uC$afAVE+#3b)VCc~$Z11I+UV9- zdsbB}lq^(+*S7%^+wzyam^p(S;w~kaojoqdODaIM4dfNeFLU#5(qx5+*}H;f_8)TP zR;;;nC#zz#zZQz%bz=TJW%!33%sV|YMp_}uThTQ;ueO*JMi|<@G^)wG+N8*R^f}8O zM*goZVOShRQl`YFJxtjlioBe?*M{dl#Y|7k*O;oyQ*tk@XVWcd3z)c%NjyKdZ9_7K za&wIbrFpe?P`2CF)-n2EiTi<SUJ5-5T3@}diAg9)kgjXo6}7u)xA^U-jYS*9o9?E* z&Ngm96a7Q9C6Q&8VggmSKYh4g8Q$rZw%|}D@*YcEIjlUkoXn$te)=fN*4WmTrW!&? zCpu#5cAu@x*WCG`e8bJ(@$`L3or_Gjlw_^GIjOSs)z9I-SLK~osmfnpWvyGcj(D{6 zj<_}GVNBEFhFje~cl_3Jx4H4b{d<4b6TXVUu<3Y6rB8)Zqyjt-jNC=gy3DfJpY+xi zZ3<%VCoF<hdD_J3xCkleIQ=x5uo<P@sVhiSl0DA!MHaKm>N(Dc#LdFg<+Ib7ght}x z<T-Te@&NAn&g2pB?7=-hnmh=eCERo8WGT(erHP)&!lX>WpNDNdjcs{G2_dhQkh9ky zs!jV8R#3}~EK^iikCk8s)`l`ojRTm0wC9i&LkagR_o%$(GYFfYy2V#*+fw_NynP74 z5OgFQ-a&BW3W<P3Y4ap@`AUfZi3kCd2xlYGesLs}kn)|K14;W)cs3F6Opx{i@Ei@# zQ{mYJ&*(;gkGD<I{s1PtEp6n@t3vp#0LMf7gLg;xYRhRi^!Eb7w%_&M#iSmEJ}{FQ zViFOId(RYMa3*ZWnQkvinj$H6m?D_Gy<d5<%PqS?ZlMG{QHv@nqHKd|8}r1UJPGig zY_z>|tDYe$!ciNHz$hM3!HvMT5;|^WF4N<LlSHk*!TR3v9qf;@sc|5~ty*LmN!+sh zbO}>ud!MdD?Jp3_A$kl)6zf_?MuE7nBEr#p{pQ0jN2_h?i+{%~QicNjF#5hST-3(R zYEmYloc?1Q)B11}rDiglLL_PiHHDhgpqg5QlML;k{>`!TC;f__9p>g20cv@%mH<64 z5ie61ElDouTsGmq$W7Fdx-fP1i~{`{Bf&hpO;~9G!Jtl&=N2HdUACcTALDjQpbyF< zDc2#iE5YolbrD8LN#y-10iv}=P1tO=yKSwLdoTvugULM@gDtJITbM)>W!`7W%^77o zYl%B~uYpDptl<<bXqBNQ*-YxkM50yBFH5L#B<uXZblmW9hukujSPna{ZBX%3ehf9^ zVR^UZWw;YzwY3DRrm~nHiixd1oHksvcCN5xAn%bggyzyoX!*!styvbv_)<RLRwE8n zm%p7>UBtc)%0NHK&;k5~<>)^(pu#rX{VU)Y7ChjwZ4h09=VgZ4cEDagg#P5o?2XFs zk)24q_X*S5=q8uIW&s)z&On-)EqM?D;5LXzi)Wt6o#=TDxpzO6yEY54AffJ&FAsMx zmqa3<xwR}Kll<kY+n*1~@Z{7FU`W;p(dNKlt)p|<iPOB1lcvH(PQj>2W&fOI+mnag z`~oq3shlEeIr=5ApRl>mr9LS(iY^rvg3oJ`s9j)DhJM&)TS`5+S&m0;)6?ISC)srL zHyA=VS=9a|brzJ4ji$aXF<dT3l-AJkI>_iiRCua&1s0vrWvPBP*X-8LWpU-aA<l2& zNyl?iBv-NM<)mPf$@i;`YfKHKiZ^NF)XdsqQI1-8+$gZ^pr0ZISU$Jx<i)iwy4;KW z;xMW1@d+rPf`R0oU*;STm$fTMTW;}_vaz)d1e?`RiyO#mYsqPc-`UL4@BEo)-5$5v zc@h|rpzwktM_^R}IvEt`YOYi9B&pv=^+8L7W;k?A4i2Wzl7w*T<u;TMJX5PW&}YW` zR`KAp89cAmJiU8t{O_)<BbtLA#N1n4FK!`xoc2XFwkJYNY4dU~m4NL7lVy(F!m62D z#YDQ|jsov5xQy7$#1#-dD^ko!p`TyS7n=m&mEPJe7-Fnm$uU4)YbRf#kx@hjS$dWq zymWxJePDlrj0ShP(cp!TF-K|J`}QZwlOi{4PaR7ZY`dE<XWg-ND$w4~lNMt7)C66Q zL2r(l8)%kDEfQ=BNwk&*eBNMx1v5}~VKFAW;?fn^9s@6;bE86rvF>u3?d@u4`;%;S zx`I;oD3sxpn{pI-GRjA*^Kz8d#EiX`5)3YOXzz1zX09^y1QBnQBsxk%qxqhiwkgk? zASPS}hb)mi8QimifQ$HOl6|Q>rF_GI>*<z|4ByL^Q`iX+_@e@T<y2seBia9L(|_`e zo<!A1<%gYw^21z#Z{@Xl+3IVR!c!8`QQE9k$tnMfr<_wgsmf=QB+73cT3#(=bl-@4 zqu{T=FCKWlJ0(`O+iz=%Ie53<rWCVcH$Q`@%}5;j(<;_I?eX5eAGOhB_IFk>LB{Qu zw*?bu$Ihp|Um?&+2OtoH8pWjN!$p-9?|DAlZq-K+q`<6&rbHaqu&E6ia%g&+ZoPHe zx_axex}ffuj*NPp%cq7@st}WMx~L2EaW|~eZdi@o@ZJrssFxTK<;moVJS`$@eAOr9 zYpz&?Wnh%L3qAEgm*RU?C_^7>l;MvxOrBUKcQO1;Mp?8FG1Q+qewI?n2M_}GxE@Xe zxPvgGn0Rbi5&C$XY<G%zMJT9|ztL~s2|NE_G6%S&j3>emYLLR4_`=e4_~4uJBVNS2 zbl0KNma<_t$#*H`$y)N$!^$&aLRk*~OT_}^NwL6e(+3OVDKQ(Y90f#(zme#zS{HDy ziHWZ=`gfXQrk-{BRmLeBCaRAhqF>!nJA=Lx^{-DEAK9-y`)@}0nc(rbK62ZDT=W4Z zw++aBR|~Y-$=pg%Z{r?oFn!99#~M;qq!B0F-8N@eJS+uH=mzp^{cy>iIyk6#QTMUj zG*!we;!TK@3=h#K-hLbu`6yqb9CK3>MuD(}CK6juJv(;5iRct;iJA!NBDRGs5WfUx zPs}=e63MN`XAWT@9go!pekN7;Y3kMy9YHNI&5Iu_uNQYOy{98!9cF%E0LuzuK$0r( zizXLrnO)Tnp$bgwb1>&FQgP1;W>1R<G__`39OK%KX77+|^%0pG+ab^b?c0cK2a_)m z!I6c?j#{3<In9$x1rshRL*D}_nHEo~E?RAwxlcfo0|wiBmM*UKVM`a*jj3^qtj_gz z+_~P~2WsYSjaz5k6t~iv0?fd4DF%SCIAHUO`iKF<IV~@cu0j_Q&U`U-{HaerQW!tZ zG`0#YuM%c|%Puba;5UMtpEeIS*H0NhAcYU{mE~NH{)`Jun-J#c8%ur?HL9$=mMn{C zCe9zUZMfGRWCT6ODS*NPCqWh<XqqxKm*9_zf^}N{_?zyTX6|3im*S`;)_UR=UL`NB zBYZf{a=LEac+gG+o~_$gg?1VUW?CCu!ZND}X6t<y{YhGrx`NqXFADeuQ91#3jWoZ4 zw=3LAuJ9#dET=8`T4oT93<fwY=4_xO%JTZ|oGr@I`X@Ol%9Hg^@^>oFv^;U>lHarq z5BZx^^(kb*daRQ5R-<NhoXu?M`e)7dxbG`d<1SXN+<L4zqowG<m3!aS->6&Oy{hB& zmN%Q%JXn8kbG;4*vkU~!6|_UvM-Xvzh0zGV1X&sAD$s|!YI;_D3WqT!yG0yiN{f*! zWcEqKcr38l=wcUwBauKWs)eY{`jpAYfLQq|(xIzaks4<i5Jh^U>Hl`oGOUrzjk4v| zg0=9c0vTyh>i(^d9lFqE$T*mgOH2Z0Zzpj+b0rw5SsEL%bgYg`wUVk$R(;Zfcya;b zh)^JjtPb7wB_s=dV=NV*&$GuG6rX@TcOUzX)lG*DNZ=`^{UCGaioQ51zNuPYERKJ4 ziPT2n1D*bqD=^%fD5I;1l0(9?6v)gKOa2Bn>*606Twf@TA3}+Vo(N@0n}#tKx=geT zqNnBJzym*Z$gUuiQ#uKUuJpy61SkiM*hJ*4Jw}(cN07?UCc(jtatsrGTVPI5PHBQ* z!#6@EO`GA$WJOVbvg^UV@*f0zYkmWx1#N5)D9iEGmd-^qSb8q6KAM>oScyr=qrHil ztgXk|HZ#Ly?UkL-a0tLOYY)LDK?uz9of(9r8ri`G3S^<3VluayBiF9xO3HQ~#wA)j z0=tnbveE>Zx^KpBJQz*lhHT~Yh-PO_b=LD}(khjqUBuK1tailRyyDRjWrqr3q$j)k zmcr#8ekpJs51~Y=Pn5(Xk)azRfFSo8Lak4d#53z!MV}ncVNzuL)|nFPzneQb;PoK( zB7)e-79Ymry7CMSn*{tXrD2nx1^QrhJZuml?HY_0q8oEKZ2xK%^sOc@A_T7{SWMhP z%8Gt^H+jysW5c%zcHLdKJuU#&3aR3XxDx!H8dt?R54Ad(b3k3(fVkE0hl7_WJ$B*f zB`*2~NxWQY=nBaI52VI_s@^V%FEJ=S0x5KT;WbMMNM&Y89P?JIh#!$Ffh?Xxk!7+) zIF$x*3(EMJUCz$Tb!cQ;u@sE&;4p&qy>uW(7|tWKz1iB0zV-Fdrp7gJ`+P<{-su;& zENx!?K<5(XeHsMs>n_}fMGC7nGp1e$u1vlcMj&y42pDLcPmPTDzL=W?iQ(^=8AFfr zR7pW`dJ!c+l@IeS$EletmF0CEb_7x`%@pK3hQr}QXg57x4y1wGRihb=nO$PAX<EDc z_P}Gi1iSC9$2;NnxK=AD0<496)tyza2|PmN9=TTM!ZCIFNsg%sV5;2pqo4E<c$w<) zc9bTS>Kmk3O8(}Bq%J^jm&P-@Tg4Gl-4j!^Z}iZ?X$9sjC=OQA-boa5D3A=>5k;CE zA+S2jjEw{@O8Hx`?S*miLgdgy4%LsxsJ6EXQn0?AjF-O`EdOHS3)t)5rB7R~we!Z- z(D%%aMU|w=q8UMY`lZG_beer784592_(4I}EVW?I8jDY$uSU<4Mi(kT^~)vE|3R{l zXc~5GWl;N9NE0%569wf|;8RDWEQN29^q%mX(f>{kyC#f&k5KUSJ<Od(5!e_0j>y;) zn?%~h8?&RoB%}EWm7c>Zi5eZ0k){e`)nx7^hfL$DFP0KVCAP=GPiI>lk)>OZ>;+#f zXViV&1`90GygrDv{0Pqblk>iCL?&;<yIT-@ECIVh{o*C+>?|hNF2Xx3b|Mq9>6=On zrt18ixtB&or(4c!D^Iqbw$98H``L2r`I?BGA4N#%M|oK5p^V0l<aGO=C!5c-<Ui*z z)~<xz5u@a{Y_y^k&P``8S%7cQ=wbexPp)EjGl}xoAa6C1yKkTP{Nd3B;LN3&y|tn< z3v5G*pU7F7K5u{lyxyN=PQHP;kOtP2vmt-UWQ!+r=#mrLX5<|K*xnrm=*}!SZ-7mO zW~gGq_FnCiw0Y0aSY~TTxt2Go)Qx4LR+u(JHx+Yq-4!lOp%Z&O6b2f)A+qqe#0a@W zbVF?pgLXs^o!;PnC*(<LBF9#~04w;k;77GEVaxfJb%OYteX3;6daO)(?J{xq;s=~f zW^goDK4i2KYQXg93P}1oYJUvJnRG=OVh&qGe(~YaM-%1k5yUP974>i*v#XIXeS?W` zL^dy388@K-fg<<2bNkPKYAw;GUOcvamM$LmWm_J2j25%Xkt2IxIN$JL9HT9Nt`=f? z_lm_he)9X*&%%LRl&AOs4hNpNiUm_RN%bR+v&=l?NN(OZ`c4rG0jyg%fEASu2>~?& zd+>O}CKV9JIQ1Du6-44uI|-imIT*c|^w@cYmD}Ue!0`CoitVNt?LBNdEw0CgQ;3OE zubh>e^et!sP_6Pz2?+`{QZ^GyJ(JNX>xn}Kx+^<M5|wr!UxPVxAd8XK4q)`OjPa<J z<!r>`f4H9+(mvqfiu^j*&0NaiugRsn9{$SEZ8c=P(K6IIn#jGRMNPi2EOjD?TKf84 zejH2yO4`Nu5^9R(z#iF^<>5le%=65L(4a{sNrF(CE_FX@Z+{3-c3LKH4<@)LaPW7^ zcwEVzR29ahpKOxJYj+5!tVvg)g#+MSP@7nm`xR~pM?|d#=z|A^XZ|*2Xs1gGKGH%r z0tJbb;Y}`zVsH^e1pjp~xpP0obqyZA(AzTH73NFw_<!1U<;j>owu7C2R$1{-aJMVl zp)1YGO4MW`+$*stE`G4|9^S9DG0n^Cbwr72TxK8ht55PycO;dN%W)?`KqSPFI%OEO zyLv(a-wET=IUrw?on_0lKjGQViSM9xS-vJ`EmjY~M1CFGsURBc<r_hrB;{T@K0n=( z0^EcwEt#^kNh{m66HdS~&>-5*{ibi8jqzkJE9<oG*DxznSLjchWWyAx3V9ptvVA}K z4IA}=cO3XTn`Alfz$oo#2PhHDSeJmTv47wZ<4%AFg4R4dL2$NiZ|_k8=A3sVS^Sh~ zF?;wTDhu07v<$zMC%|8qa*eXI(<T33czWQl=if!7&BOeN^}*7P4Tm}FkAY%6whhZV zZoAbA%C`Awi3bEnZdYOn?|(9Yz{zI}MR}JPToze6K^dRDto;boK(?@0LXd%JYUb_Y z0sQ`PIZp_d8IBsvra<`tn<+JFiy)YTjf3_q>}4%<W$M?~jc>D;$&Wvu0a~w%(cc7X zb810(it|0|CXtiGtZgK0ipD2r795|iwWTyXDcvbIio_?IOIv*Io@wW13IY*$rpo7I z)pXWI#_U-LibMabvy@_Rr}hyTOY@MXYB?*y>s;I?mP=XwD3Y%^kX7~j-i@F+zQLL! zC<Zi#w#IQb&We$5>3LrLs0*?BxmkhhKKuLmI^+N0I*&?R=jeRCuKFmhJ7itC>a2D9 zs!#sMBs@L5`ZAsZMVFA!3a4E`&_Sy(Sjta9a<L1)<zChct$icZ?Z7j8Bs;{!5%kwH z_}#^V{%{v|JkN;>+j&kLTPIi_!_MLSWEjK#KRL3sIEjN(w@HUF-K)7VU4QWWn2HbL zx=*azSH@N14K=79pMwb*9PJG$d}?R&{w7F{&}dfVVOP9{c*FNHcRX*_=HeX>r<LHn zetX<CoM2Iha}XXwOKrMW+5wnZ3-%!N23C0!AV2<7Fah{oo2>>h)w^Gu9zF6QmWW(C zY&#rUjFi{ykx8xK0euJ|^VX;73V~M|A`g^UKEH-n0d`j66Efj*&J?I-*0hVz&K<<f z{S#Ft*3mbHEx+P1L@q!UkzlcrjgsH0C9i;u8R{pw-2ZHgPqv%DI>$lrI532f?6jx$ zQ^OaTlTGI`N=y=UOvWv|hc_Q-3&|v}_3g#S)(bW)W%(T$?|J)ML$W4ZMiz+n-L56S z!^xe=p^N3vJr06{2`VUKJBcv5GxFy{KU>1+q}gVsOxzA$Q1e||p7n_%IrbHOuqy>T z(Pmx#T~4`z@Ylu7uH3$AtJQbwv5umaE6v|MxN-0G?u_nR^*`6W*|DZ&ee>oA1PK8x z!|AMZ86laJ`gPuw(p>pRiOKz9V{+8CqHW?`QR|D=i#ObTd(Jz@-VvnU-L#l(Tql8% zgWhi*cpm~0V#)_|>P5F)0^QWslHe9kPM*VTH4RK%_1oSnhQb*B{Nc*VS4joJ3O9ne z`<@$)=lpERpZl{V-B_V!8jHcD=1#a{6ENRC2ydSHQ`ux$yP9NjtYUWLQ8$}Bzq@Bu zV2tHMIK>z~Oh-RU1^CF!YzUDc#b`Li<j*bA0y>Py1ePQ{fE?Q^ms={iIUTDncBcZ8 zln^QH+@BVF==yz_S0dcE3IZh^@LbS5c;)TZl|u`79KYIGwe4|nXX{E2@GoMl-#ct? zm$bus!OfMBG(8*R!=h-*q#wvxcUlXXk2CL_Ql6;*<M^nn&0|b^Q>F4`g@}6_Jf`)L zRaxqMlaFaltyGp*2)H`GF^^T1(2jre8uNIORd3Cvpq}{kzxm9y{|XD;DGUiw2=6hE zUb2?8#*u)T?`P;3M|S1yl@(fR>&g?S`m21x2eo?<6@QE~l^DJ-_3~r?r`$UA^H8%< z0Z~}G3pP!?jD>aJwGu->m0$d*Xl&I?wLQu^vyt;Fl#UcCPlgNg>m=2F8*sw+@%d!* zj5Nw5sp8cw0akz(d*Y9+>(w#aSN2#pt$b)*x$+LDGO!IEQ%A@^*L^efqF+3vE-`Q$ zN?xO2$|jF$pDkqdsAz#DU-Kw0zmAN4UW*Z<`PCl7@@iz|$|_Jst(*;q*R{B1v6i#p z;K&Obn_O~Y{Pz#RXdsI$LO855E-pSjuP8%ZNM$(uALT+=4udFVe4|l(00-m&`{K!{ z00#Z!BG@}et`NhK04czP-yhbV2+5f5(nBb3!q*{;*aJJ_$5*kNFoy}-;SDD}d6E%~ z@Q{oHVy#X7TQ}r;3WSp+f&)=rQALM>z*_YWN{jB@iv;TzvwWmN55a6pE|6e4@rp1A zazx()GNd6Z7aKp#05{_NfW!#}9yF+s`Hbs*!f$R9h-|~(9yAq1&A0j9f5M2s<vr#y z!Lab_f15bXrfWfw$`7ds_v`@_WE724hMxeBWc=mSxT9;y1NtKD(sSWP2387;%$p>G z_t8Ty3BgG=(-M9aYhxJ$1|!jwEnkyCxH)9D(-!4%T1<^PAhF4&^?*;gdA~(D<peSI zT0A^oKMX++!HkP9!SlewTIf(P8Zw)K{ZvD^<HGNMm6(`?l*m#oB$Oq^ki6YH9X3Cg zMtzcrlpl&9hGq|<Kz|Dy1sY|@9(Rb4K**Y=1U?UoEPHHcPhoBX`uE_Mgzj^Z#Q)-= z3jXc0oWxVun8e=Zr7Q0(uU}P%ZAu-{vr>_V9Urc9$!K(2A~?F<D@GXT3L)b0M@kG7 zL`<-n!3QVCtDW$RH)XMZl+VYqT-Lq<4pVTZjr51*=;3Pfita%Ln>@X*u!=)~l1iNE zh&9jWr8Kp&EcS)-0>OpZNH#8*z5*kc#&n^X<Kk!-Gn^w^(0k)X^W~Y3(3Nq!By~^E z!t#r)2}TRY4;L0(P@v7@#}6+k@QlBmrdkhb$bYFmSQ>5m1D-Ejw-v$P;l-$Ye839G zk`qo!{OY+N9c<jl7<}>n4bs6D3mYun*rC2Mhw-p0fFe0<|0T!92Z5ZMegz5-U8${m zqYzUSDo+^$-iQb{xQ^7)mNGi>5#vI6>Vs9$ko>EsJuN+eFZ<z1tPq(jdQi?z$lMx~ zpTe;8pdg48L(qOOHQ;eD5&DT95+B<@73aV~ht48rvN-(*NwNq>s5t;4!Y;7;MV9(P zehgi**N=HnEb3X7W)9nxODOKcK&OSAQ#^#<8K0a-J`)18=9;a{@a+<Lh93?FbB&ur zOZM8@OS&OvpdFGphvGmt4pkqBLM;(QPzKJ5iwz9C<^kyeZ$j*`d2L`7z;wLHDyQc| z3P?93kQ&;R;oTs51yk^l^po5~fUP5cCmzb^i)7RRu4QbZ!0Q^M8u?GbeLY$R`Q)Cr zD|?n*u6($x6JlbbwsZ^Nm+(wa36x#nS<JuVi!kWPgdsGimzwgC|HPMvf+#`C<9EDB zsjk9psHJx*EfNA00)%7D6BRVrhA2~d5PdpvHO<*)>V2qmu_p=&gQE(bR(mFj3=7tB zb+n1I)JZPC6DB?dM{Bs6BqP)~r<|t;rYBqhb;5{Z_MGKEmv~yqXP%c5gBKjAwFgqb zFMU7-LHAMQwG{J@o`?p#kZRUvcHtf%j-}S2`(ZCmO)eNAX#gMr0EPEjb9|Q&jfR*j zoZ)vrC%tw&40;!OJv@wtXT3FP2g(T_tEwCS01JU!dVi~ON(te6?T`w#r{qWlM0H5j zug<$^HOlbs0><bQFy(<sFzsfEL&-i)M^R?wW1Z!AD-Ck++WI9kx2N#lo`Wg~3& zM?Ot4OV%Deu3s5QE>hv>lklt&GK&W`qg#{4D$KFNY=7`D6AA*cf8hbXT!s|^8aG)Y zthwHVrcE9%tof`FZJazoShK~33MLQNn#}`kO(UNksMJrTAhOO9GjQ9wGW%#U`#Qw7 zTssA6^QiSUspPLPHk_2C-8cvtC+?UY%9=tDgGN`T5TSC9N;u^~Ii@kd3KwBqB~~!H z%%ReU6%KQa8!oS^z&;&uHy>@&5%@rjW=6<^BXtvTo&{c6%%2vK<!9jRx%DqA;8!^9 z{%XY^z**Nn=A(Z_jPx|Wg3~Jm-sHP@RmH%P)J3Dxh}m!a0>INZ#t4Jmh_nzOd}B2@ zX2p<hU?;Kf5E8-{L0D-nvwwxe0tivo3*aBgA{1h{)hKS1-(b%lgelYLqD|@$6C-XF z2*F%qRb=$4LvUwlA&Q+cT&2-1vAsfd3r}Sc;A8D5Vxdl>Tkd{M551-?XC6e5$O0Kn zw|o1np7))_JQwoUSxylrgC}YmIyq&uya^~Ujh-kxWg@@<23YErpynx~BRC;iKTaPm zg&-u77`jR@RsJ<Ee#gRH2H2=Gi5P&s5VNTj8{<cQ!?n#*QS4N9ZX_WjRjSH^cYM|$ zBMS;FrvX70eZ2TQa4C|cR4-M&7UQdCn07I?x%x69Jl7~d1cedy;mBv2zh8A6EWo*r znk6TSGOn<TS;c9n3<~<eQTqE=XxX%}!9cuR(gd;%9u6JtjW=Lsuh<i|WfHWjr{xr| zyRh&YIzBBz%dy@##5|V@n&XYcI>GFBK`u0P(zNK_;4gN2gO$-61g_g+RB%0<7yz3! zv9$^|?z-_A*f>yKgAKiI3#h{r#G-2o=m#!~yx=IEb(h<>#TF~<+r`CSF?y*Bq$7)| zMD+TLbG5pf)Dm_o&SW6ZAKX(n)yfNmdO-eMHu!d>5N^CU>IP=OFX5=$&%#a(zXMr! zE0q{;MUv2q0RV%`uIuq$KH7>Q@sp$Ovvj^@eEQ7_%+#fIz1|A~vXWSt{AVi>A;-y< z9`xW6!}|z3yT+MNr-G30o<=xi$qUC`WA;kKLSQDBdf8})ORfGIct<Oy4|lDZKbrI9 z$muSWHeEhaB9QNF{~O0+-=lBu=R7uGex#Tq67cX+%*6>4jw8)<?}*sBi49OhKoxk7 zM4ZD6&lsduc%}r$%JfzPc$4bEnFQK-3mb)<WsJ+uh?+6u1vWZW(Yar4*(JcqaV@-u zPF6b2iEpG6-$=lRHq96(@SKL;pD|Kvl04W{u`BKb7a%QqkkRtL1EeK}$D<)Ddn)U} zWZ+wxCQ4b_;;Agp6)_7W13pnR^})2l2n&V|j*?0sWEhMi!s$jV>azHfEQJZ`Buyij zBd**i+|{4x;UvZmvO~DbQRgrLd2r=ZS>7q?3z{O<-~qMGvu^hy7_HonT~FrZF})=c z?{$^nV7YL_2IYiV`K;VG+UgGF!|!tCfzQf^ega`tfUm>lHN&2jdzvaG82)#z+#kxd z9L*A+v6Xm}N>jl>ZaY{i;jp#+ZDZ-b#zs$CkR3BL=Axc421hU(;fAAbMFew14Ho2< z?=V!)V~LfqB$18_<3FpaOjB>FK(53^o);#Ul#s|rY0+dzkWm;xd*)?I2zGSp--_*6 z?G$cvL~3h<zZqkUw=$MySU?xS9ioX&o#Y9gVu9ryDBC;cEyZ4F;P$bXB@5oOKJwzx zpBQh+g!jx4FH1K3W`Ckkz^~jLegO_*P>Qq<^=Aw)86w%LJ^a+!y}yILu0Vj!5lOtG z*PaoP`3Z_W0PgZ1FCL`Ys}H1Zm;Q`MkiU_KIPS?q6npkTT-XU+h{`oQ#2z^hVU+hl z6l@0ws(dXEv33v-ku(THXn9I>DF7laPv$}9d+;D?k3J-ttpGun|BHthCF3E2WqlBl zTL8l47bhVu19=E~U>}74CV-Iq;w0prJ3wf$CF|Y?LTmsK=`T(~{^Q0Ysdnpws9OsV zuD>`5Iqu3s6ub68Tv!7TZr!~ck@65mX&*#E0zkNT10nbln9EuT50WJ51JS(+j2Gr_ zb8^ImhfurpL1;7pF(CgPCr5ghCfx@S`8q%h>~`XC5%V}`aUX=g_O<SJ_~KxSFXqLs zO5}*ttkRzpdEWeb4Tws?N<cLTbdyKV)HnCMZtO!ryu!r^WW*+$JM|Ml$pLE6<~{5C z!CZ)|_);v9uk<BAi-<c#%pbvIwwj?k1={!3jG~h}mR&x5O3iq{bLsHESD$y3j_*qR z+)-M2q$JBmP*+x;b(9Y5+;yIp{B=LzMYajozu&vMN8dPg9&S0kgR?-c+{1Ywd=`if zqA|>C1tNSuCT$+vIp_>1U`336Bn3%u+Cku5JLDh;P}7D^9K2K##l*uOz!d-6JxPCB zB4_s4K}Ki+W3Urx>O*&L!OjaOkS^Hd_jwe=utmw@ZH)<0>x$Ni*WcZp2u`i@qB8rB zaK*0|Zus6u&-pF%5g9V4ZZv4^qAv{NGO#6Go}@n|8g$@#dCD)^+^Zp`bPL(^ysM&D zR}MGg@(|OpgkC4c_LaB)f4lf!J#;h99<={IT|CwOuP%7jepio~ixX^gu$NMD$inLg zG0EE%#HXf3S8)sp)khGfn0Wj9NyykvH|&Sh%nZAmN0K|u?6VJ$Bq1@l|I=y*Wv|i7 zGbR&^saoc{UM`*n%C&mX`(h@$cF<6o(c8T#+{9#;dEsA!-REplHlSnD)xN$-5?ljF zaXCD{XCHuSw-G&AP4<gP0nl;8*A4rTn2u!9Ykgn>=P?$$52_tWyq|THMhABi5|vyE zPILCSa1tehe22~(uvrjWg&+qdhLv`-a}szGO)<73_P_eZaUce=NdKZ#F&T&wb;_kW zVR|mJ8VtY)Q75XM2VoiGz%YdxZd-({P|wb>!A>lL>ynTe+$obBr&MGN-!eVdrY`6% zO4j~Uc=$0K{UHEAZ&!L?8U>x!8`z!OeBFK#O30X0*u|h+xVlo3#F*{U{-fg22dJQf zr3GD>BsghyVzLy$1bAocg5v35F35b7z=R0z)XG>Z-^2qqSy|A=H3@vaiP0A~DYL7= z>D4KlVU++8V=Mc2i^&bN_AlW{_o$T6Hlw|bt>|Be+dr&8l*vPj$F=3>hZRG&9=lgm zf2A(t!QyV642xBXVH_t^s%lQ88bPF@S3Bgj-sJm9@}0(kShUL6dZKl|TpTLc8_(#g zrIIyrYO;XYQ|#uRD0i6z30JhcoZ2p6(u&>W;vE7EgX3}$0>)?;O-({tIsy>D8g~N` z%C<`<Br#dsbDDj?=p>NZ0Z^{9%Pw*P6_^B_(ewr8UAqj`263Xak1n$8WhAPx1z3*o z(cn9fqYIER28V=<2@a<mMcWSATMk5<tduG!Ly*h7QUFIEkqlDuzJoiJEwV;UR6<dL zc-`GyfuKUBu@;!E5{o^Z{tMqh_E0^t_LWu3O4tC^rb_M(A~-?6w;~JNqJlZbqy(r` zFcJNU3sCL)M~R2(R3+C&>udxCR{6~!VyG#A8bI}Su9o7COqCg8d27bN-cq{@E}c}_ ztByW9e}D@ReD@%u8wUw)+`CmzfTW!40P7zV@5D@;yWdasriW9)Nzy@jm4whP&*0Rr zKUTk#q_tP^PL}=;#k<t0ct5jx6hH*Q8Cz&vhocKPn$W9wd*Q~P;;!Iw5h4o(hi*3U z2&9#r?B`NQ>Q||1fvDzB{VMy_1r|bpvh;t~u_s_J__1Fd_Lo)Tqpf;IK74LOSqf+9 zaaYC;<jRawpOvk@GM+Chp7gBjcrToN!n3maD`7lb_1I@+L089naro?`^O*=20VLdt ziPL+?D75}+FkcfK^pBdN-kPKl|ERfpHPG3^uxCAZf9s@}9`da0Ki`J<e39WCoZ=ry z4u0!IQY`P+;01S%Oj?%Dgk&g7-@P_L8$U@6tBina3mJBSmzvpYcU~zTk?^@X|BkYp zBCvO7L;~CIEq~KX>YO_`?CqXpGugXGB=o{P^9<dD^YEm=j^{i>aLxq>S6hF-<^bML zrGP47gt`?yn4_X^<D;{w8BLkHfq!1tgi7Y_SjgjcIx6mvs<i6kJ8^EvGq)TsBU~@V zzvnOqlcs~quU8gu7jRno&5to%&)1^!^L%Npr24<;%)H_JbJA_}+q^*jx%d{6s)on& zvh&|PbE&|-Yu0ceTIpK_z8p<2cF3`PyZ0IB<S5~LG}ibraPJ1bDC3fpZt;D{PgwA8 z!c#`IquD$k?NJTaI(yNdG^*h^)Lb(fJnV!8PA0HS!l2~Bg<;Gq6kLw&bJM{$3U+|O zDsG&B>yszj=1@;caQ+Nent@A8lXvgB38(e+J65t^cvrzOcpbP+B}9oKw>tlAG%{+) zB94aQ@0~QrUfKy5{Nq={-)6HI!`}ICdD5d=J)G;YSD2@SkK17E*h}a8kRP-9Jj#q3 zqD|o6=yE>>!f{WUp5iuqhO-5DuP;veGS^0``th}<U=!vdpL5Q)4nE@t!U}VilKbrD ztQ&B$m)n*?>`77mm+j4%JDnCts__+@C`M=%9GnZ6P4d2Zf|kR5Nipsg9QZ$;h75?X zxCVkU;rz^WofY5n@@yr>v3zP7AF75=B{YW)eODbYgX2wgd5Hnf+G7Z*$TJK?TA+i7 zXGrIm?H_UT3?D9XNh*5A9~{TF>{v<;(~dJa=Ti>bY-!Eg%p7W@;wp&!i&sCx>4_x< z%VikJ*>z=UF>swXNO(@deQ!uO89oUjJt|>D;*pN#(j^Q*jkLfFB2)DLDNCavVt9f8 z531Nytv_WVVxZ?D7K!I|@Nyt_hu$ve#3fo*a#=2J7Ak;UaNND%sHyw_=RPS);9VM0 znRYcl=|DC1wWV8>;UyxT*g^>=R<;$ssIq|a!O#BVARG`)4NHrG@5*!_*Jc_#eE5?X z#L=*!cl5y`@c6~B%%6Y5$Kc{$kmC^e3Oo6oWtgN&2$gI|&UG$<pj0;ok~?3fy1&d= zCCoFTee;KF$8?F5qgGK4;*nR0ktcxaj$xI@I|)2-%FpSOy}xiDXAKiW4d`73;x2BS z_&YR9C^sZFILDs>?O_1h^}&XYl1!6PIkKf=*F|P&v)jcD<N~YPQ#iuAHp|_<?y&r- z1*T@Ze@ZQyT7bc+7Y<07*Z+3GIgl}I-Yp}UcgRQ%u^5S)*<m{^a0Ay!+|Dj*-%0+O z4We*C69t{mro_tWAGW>8afA$j$ZHcsFWNDGNZ&;_^+8hV<_%#GbjL|zurVzNPCz4V zdw%T58IMJENcEE;wF!{}=*<>4Cd})7;&Qf|tqQ!zu`>L>p%U)Sly~|=2GIz#@hyM2 zM&U}>?sol*g$G}Q!eV2SNq^}t1Vv4l387303*VRe<<6H!lhR@eD||tP_ANbLEzy}+ zz<JS$x@Q3;4)Kh9hwM&*M^(Rt$;nWa6~0YFE<>+H&(;DUMEedqbY>Ud23|Al;;!_k zfrX{uY6B0#CmKXS8K0<}?+BpXgEBZz=qc4MzO?E9WbzM?G{BcTXgprHocuDcyRd$N z1zE5}7Xa5*{2zgfZ(!bXHIZitB9LE<VglDo;a=#4<|*Jc4c3m1?;#FWu%w1B$7>S4 zxJDpN%xa-|%TaV8reuWw$n7_?aX=z6`Csl0N4ck(ozxvFs*rmweGBbf;4`mRw_^`g zp$dSvc{Kc!F^PvArCT2NmG;s9slg%Jx&wtS40Ht+0&8;}va}eaSvZvb8r}_GZ$yTL zAzE1=qMD_A$hB%cD0gpRk-;rD%2I_J1#ZXwx7eY5gJG6YZls6!0nXroPzjIftz0f4 zN-i$i3g1|R7NwqjSQ8Tg>wt?%5j07&!E(BKu-~N!1wlh@3|)=JF7lzFEBJ8PX0&k8 zXg=zxUX9*aG+l&G$LQXo054t!43O9&>l-Q>x%ewVaempo#bR<mA+6wiYk?+k&aXt> zOXdsInaF?HC_(X;C^^<|K-Ci-ph^$^`6XrV#TtZy;yoM$eGVXox__legguT5jB@zL z4l3KWEQ&5Zw}BL)bFW?;%VG<Iy3}W3j7YE;K}(!3zR*`@$pDsR6s<+aUz<wv&k5+> zYqMy8gpW3A(74qR^d5M2S^FB=xO(%5Q^Z&`v%B48u`wj0D8mBXBew9NlZ^ZDaiFoN zd$phc%@xcWt&mJqlo8?tYG~qt5*y)rHKWm#*H?R;!1}zY4XfGruUbBYx08@tT=IJK zj0*#~k67^g+%8Br>Xj=6j0&RBF<=`17qitl65gKR>H-&{&^M-g(zThQa|iPyvD24x z`qUjMLAp2I78YpFqbF~ugcmA5K~b6)gayN`Xq)E$hU?z=pcmo)+o5VanjAk&pgxC| z$ImAZp(F82X|A`zAk+~*O9(d)(6l$lGaB7O+e)hYd+^Y5U;ZmNau`;YELjAZ4-|ZQ zE5){o>RSD*w(=mZHFMwefd~jC43bbKY>*L0H;5s&WmLyHocG>ubPay!+cyJ*rL(?8 z&2M_~G$i^VH)fWw^o?)OVn#W%^o=Wr8v54M7sijbguZor!D$0HtA0iMm;m?Er?tLS zJSQ7GQ8^Q&4ML|6<;?!VIN2gFrm6Gmd$Yb&pb8x3BgJGO+@y=6o;<XUih-jP;V?ff zKE_=_#Sl|=hwu1!%kd4NZz-qO!v)7JUjy{(RM+-OTn?RYfD7o|4xRRCUz^aka0`d| zv-hEF66DZeM1Y(!^f+|G;j)XNC%?WA{QsSjDk(agFhvkK8r38W*A|R-U{(^VOV@t9 z5iLK56RR>EGTAt<s*l$m-w4sSB>c-rAp|3<n}m%5SW|D|3r*1Kg(^smRzVuw)@NCI zQ2KI-kyPbY^NeD_@3+7GnS^k$FMJc&q&{t6_M^>-GQq4Yq)Qwoc*Bee5{GGL>3Nnl z^`WfU|D-=k3X;P2|JjRj_H*-G^#oif5B^#Ah4H{OO1&$Nu{02PA~IkQp<%sH97`9# zhkeFC$*ljeG`J4!z2G?1gj0xu9uPkGV(K`P*TpZ`FANa0v*&2gs5Qgo1zUc^T#cL~ z7aMvY;1F9AhE}Z^3fEb?z>(I4FM>Y1-a1{>#v~RGR;&PpM5`0f2Wz}%8oHK(?+Lyt z&^)UZzOU(BzU@r%$&E^%mJX8|?3U0UJCxz|9ZFwOhccw4!%^V4GK8k^rPdBW+l8c> z!CFHn{)$Hq!$y@IE`sZ~IW{nr+vQ?W2hek2$+;4ZG@)@@)UKjk;@#0$gw{Js-}sSK z{j*%C5`*Dnej6g`R*19w>!0-w-I7zJs>wQPJ-$xVOSV>fZ{=m&A#|qO^7%`Z&sQI5 z47`TN0rZB$fAB2^{u4p{)XR9x>^z(@2(Wm(Q{dw<Fm^}5XXl<9FT}2=v9^o$zw%bJ zb00G~dt@^HjtP7^#mR-TgC7zq;6YEt4Hj?QTdqQabOC(bLM5r>z64l$y<=Bg&nXnU zHcrbasD@jjCecGkKdb255BqQbuu+GCU)ydSKst{={0s)q#Rf$tb?#99?6GMRA=3hF zn?$4=$7}`;7c60U9Q^TyFWZvWziiZcEhnGm-_P_f?$wLqi@4?L$!WdG5~>J-Xh%-; zhtyA!P}~>I%ug%(mydzWZ+QktzWI$S{mY-h7i=+d<yvR%Ek~XC>4W~j{nZH|`k+@G z7DB@9_>X#52=JxeD#Z(v=LsE-^LKfkOoS|wbmM7AU0TAKGI%3`o?<3vI0}w@f-z`u z5mt?37KJVy740Z^qoNma|I%6C^p=j<6#5p18(!aAGAhPVpnawV@Y~b${&ln7>wW7# zAg97|IS=Xu?`i1i+OcJSt(Ayq*l^)k<cS2EF4Nq(<_e8&Y>E>WR92&q&41G$L;c<{ zf0d2d@^69Q;fLtst(k%~1!&4!-2yfbO-@<zLJ?;!e5CxaQKl#cW5N~isKw4xQb~z2 zd|s#GBiN!{|2T_EQ-YOTFVf;NHa9YidJi(uW|>P4+M2?jo~p2LxW4%ib*0RvqwrBZ zDh18iHd4@FL~FNwP6K+#jd>UOZ-04Gf#XJqi9ebpz7H|<Zx}cDa^Ji%3ed8-fObnO zMmzE;4-=T@s9REvj&C0tzzz9CPKjYLJI(~L_KVzIp=L)jE`dCNOQ>;sAkT~#JsPy* zRr)<3UmB8%w(XdW83sv}CRDz|$M3Vx;n)NjR(PFp`&sj>;t~U#9O|xrftq(LQWpX+ z6_^GNg%bT_Lk|xL7fTBuJM$2(OVmQ$M|e{UV(%s-g0@<@RosHJC<vdJdx~7#+2~Z< znP7Y&`e3KGcuNuFrud<AJEzhrZ0M<`pst<j@!WIbWN_vE1=0yJ38_ZVgK$#e>wTu3 zq{?D}8TzvLHT2HEA|`-Ar?=AMA$!GMEO?gZ&XpEVDS`7oe8djm_15aO2dZZta*0QE zFL-LP#RzFHA<)eB&t~dyoE{GwBs3F7Bqf})?6vW}roOFZaXsNfMpIQt!ooM$_&IQ7 z0@KX<jbtFs)*+*ro??8Zv2Ofc%$ok$v`D)mG4iNrAASCy@GN{;eB}v3zRQ>Rz7l_G zVR1_acL@_;!CZCk_4+sKbS`PpQ?JS0STZDQoC&hu>XsPD(P{J2qLH+j4sc}`KqNwh z&y$R4p72F>BwUN0^%Oqvc(3FMf}Zx#kf6<kS@MXo+G4mgcjxyIc4S#o6Jg7n*MrfG zPw$^^GH1iF4T6omStsbsE<b@zg>LOi9~^xVwZp0FsW)78LXPp^?v5e~?ZOB)3MKDe zL#{-%yQhrZ3SWU3h7*t+b+hVtURVsk?UM<lTmo54UB$%b$06(|cGNxXLet;&^S(@4 zVCE6q@S1{`055O?e~LE0J;Kiv#kfia3&9I@g_XP12$2gRN{#b%z{f_S(Z_F(d)BsU z8Q-?b1DwQ#sQGO_0YD6RXN}er)mbToWDiKfCTu&~pMYL$zQV>wZHCFJbi_w-aUJt; zhi%CF-OQ6bQh3%BMY-j<x)EjWD@og(4V`zSw#K}t!qcYt@C}rE)gt(+;`{?qi&;?a z;H<``k1e0RSCk>o=p9MoJf4~csbf>~kwb+QeN|O+oP>4&q3CP{xN^C{t7c;uzK#`v zLV4G8s;^bzEWRcYbixsHV85EG8GMbZ6Y%Xrn$&Rum=1I5IPwitnfjS_`&Mzoh&x5y zgyc%cZ}s1`+-Sb_;OBdH>(+Fy@7Ub(X_nh;NTw23k*{Ys`)-3xwM!o)QRA%U-qJv@ zb(VDYFQI_`@g0!iBj)E?`{A=ZHHXCo(I!xqCX&>~D6(N0&G_VN3;gOU#$L9Jfw|uM zaWZ^8j>g}b<F7~9Nnx-%eEaZEOV3Is!9wUy(1axhATu-XGa%lL@QK|cg|M7-vySC~ zF|~?i-sui<)C@0fgv%L=oy1px0(AU|v*0jcI1^6mq|S<|qe5)E^E(uQSmgw+h7mo` zhnu3(q7Q1AJ>miEA_O0_Q!Ip9w{IUS7QnCTf;RMC52*k`;J$LZViEkhXWz|9fFwHm zFGShlUm-E6{po27=FD#yi!)#~H9Hz1S+#k8WB%H(_7yLa%uw0~2WAna6EI%zS-JLL z_(V4iOGGDdviuRS+?K!gKo+7d2-2$InAH5)-TN$GTL@#ieusZfgy(^G&~jaX_I7SH z#(-hR|Mm>F<qn9>_+Jld*wIJ-(odGm?>}W#zru#{UocIco7#ro0O!yB;Z$`n2)H|K znop^j{dP|X9kX^%h~o|7sZiZ>G%t1Bg50BM+{^--9Lf-XZ>r{>8n5?CO;saw_@h2J zx*uG|cfD|HuJyvH0M79}3hnl`M8=XXqxx8r_zMtsJ%24+yCXf|GpZMyEKB?^aQGV9 zmo`NE6FgnT5DMZsh+1fQ1(!oC9NO7@==u{Caxgm~8yfCj8rlcYGvQu3wp<-GTb>9b z3{WcS2wTxkP+2w+eaktT^Jl-r6QdK@-lgwaxV2h%o;t;|g8gw7%+;kw&ey^lFjrrt zRyg?tZ;p#9F8Pf=k>G6uHhgYRYq&b}qU9ji9fq&3T1Ep4O_#@Cf!Q2>aWQ)oW^u`R zv@ktlv>WC3sev(3e*8Y#^fj>EgE>I^!Tb;<+1c0eHYKN%zD6IXN4P(<dxBnT-v1;d zlkr8(=>h7G-3jGM_<siehjhwdSN|2SA31DM@~mtkZL}@1y@Oh4c^g*emoNO=vKM~K zKi<Qy+Og?)hY?!c#b_DaN%|AFv~_m1-LBA2780o|F7H!@{y^CLB6@_y?rNJ$M327E zop`ny%OPzX)Wl~;f!!U-<1^H$))LT?*PQxZb=}D)D~QRFXi#dsHHFvsn!uC5&-dlh z(2IvoZC@o|Pa*#-Pqf(}8JY)!c6BY%hq!t$hos(?mB2`o#J7Y6S9TQo{}_7{xF(P1 zaroIJPr@ld#E7Cq5m2fUTf7y~3aIs<*1J})5v;8W3bxg%2||Kg5`Dx4MTu5L)2|w9 zEz}FBBJ~8Vcdeii@D3Ibx&O1zlLXQB_r8AQdGhSe%+Ai<yR$P};=XXwL{##<k{sch zXP{&*K-=gX8G)*oP3m1;Qow`QUjR&|QVi3i{zXhLlsCo#aC#`%<mlHZ9#*RQ<QVWO znF)x(a3SW3-BijRnNT`$%wyG7Ug{A>srKtZlrpZB&3$9Gd@{4?ZL25DZMCbSa4(mo z!c=3K;-5SFaUyV>_;gL|FJQJEgzjY5Z>?k%bd|#njAme*<O<~3giVhoYh!)wsGfG` zFY)&Zf{e=IETZ)X!+Uvl%gIQ?!vihMJQ(EdDqxUzT3gG9K_01+Ufps6tIp)!*?kEA zTTx03Hw2Ut^D%~ung%8%4na*Rao7pwxTp#o3kERAcX8Iyd;ddBlPhIV+r8oM69mQn zDi|;C6i&Mjy~_i&qVYZL+($N$UqSA^@b*n^Jy-*)wfzm^m`UvwamN0z`v=~du}gO^ z4Ob0+xP(j$2^t@W!~{UcC5!B)OYVM^B;p;<sLz_K1!JlfN|+&(uFZim@62@B;4uay z2JT_PVMz=2xpZLal<w?AD_xAT{JwC?ZBL6Op92J#eHatq?mHsLDD&?FYDYY}W2{%A zqr2=UA+v5n4_<LHvvWfqo=+n4$A+FC^?Nqny<dYEDquW+QKBOwk$^tsMiEg60E-}f zeh7k#)FT@MGrxD2HIC0*<L=uyo^eeWXgdQ^q6^QH>$c47gzkz#(129|Yb5Z7>FjbS z<03{NEr2t2Fya!i@j(t|Av{)d5FP*#Qb-_RNR#Dvevk)qN11=;%XuVhaUKU9@STZX zIMDmOcKQSpmBN)=c;O<DP=4+7gE>r1f)`&?oXYe_>}j9xRy9!-dtCtfkfZ-$CM9~f z<hMSLy(K{WTaVyK<7ix^Kl4i`%9tPW5{B2lj`^jJ3G_r27n=8!Ih)v*pFg^ssY-OV z*7(E#Z~1N&Ooyal{QRs^W@6HWp7~jSb8|+{GzXb8!d)>|PZ>tP{Jg)IUy^#lX5>xg zc2eJg%B*UV{uCA1RY|Xbl=E(wx8ls!-qNxjz(!SV4#*?F^iSr)<UTyb6=q&?cfQi? zIkP6&TcNo0E)L7rMJJjnC<ojd@W<|`!omS2YDu-!rWbI7fsWF9$;@W@*l(?uj9m6? zHrd_)m13|H&D4o#qB@)j$0Kcv4WN@)OU1l+Es0X-vn^y^J4b2v$}R(uLb448308Y) zfk-W*N_vX`yTM703@!&4sj?`?LdXEDpb7a^Gyb-0pt0^m^lm%ejrZOVMQ(nuOO>Kt z3UVn#9%v<u?F066<E(@THa5%#tUygiq@rT_#J~xt%1fRg;dhdc5oyL6x(Zv<!#9g| ziHJoCROAWXUQju)JY_yp=C(Rk%`jM7wX7YQ6104V;gP6y)r`wI%R!p3CnvI^*l@)R zd9{e;yv_WCk|G8uKxZ3_IlvAZY=?D!K<3Dp7dtGj3DlQiwXp+?r;+LV6jkOoa1j2$ z{THx?MU3TGEaMO<E{CG$H1DY{!kR;IK#|y%sB$TXZK~#dD%S;y%m_IBwQSCk+>_&4 zatl!xm-0Z804>rw0o0b@{1iI5w4(Mo><O1&6o)yezO44VE`C9?*@T*s2l~^am)?O{ zFW`46kjw&VqU=w*lykpCpLG$vAPVv;bC^rXKXT2z+?Q*HVG>Y@e@IG}t2!KO+?{Gt zwuj<AOn{@ITKmFkPnnFv*(;f-hV?^P>iud@sVS`76gKtmxA%0$m{!7EJ@BgcOB+9V zURw?2PCsBRTL+8V3Gat{y@v3~?}umC0HJ?_uVyB0bb|vy($`ioksCXAa`^GnCvemZ zUB?w1`!pVb<DV(awvAu%9F$u>K%IP{8A>&QI`t~Jz9TCr%&8`KlAzYYt%*laX&2FO zw-jB5e6Z~S1sL_MwM<~Dmjhd~<IDWg_<d<47cy&7dkkG%8O5D3L^=>3LuJPMvA(Ln zEujamtal-o|6)F2YzmQN1oX4EpVT|<zo~-$rp)0KQ<6Hhr#|n+mL;kW9ti$^%{8is zOV;PfO&k~}1V(-8MaEy#R}q=|h)|BW=A9H3x+0n|6!I|EHz_bP6nrLiP)(HEu5w_u z!)2AfY<Y~`MI;#sso5nz+wc|h-Ad|wG4q{@fAtyk{RPNl(48%znuXm`z_mujw}_bq zX0;y&Sy!mYy1Mv(PZKkjJP>4@F&Mm?qsk(qNh*CZLwEhT-bFQ$WM{c8hO9727t1-~ zF-3%US}`o<PB7w4QiXBfLzadiLG;q_z`OayF_rg(Je7BC4&zMoVm5&ixn6PiRbJay z)*V$IoV<BFIb}{UGZd~hKQ*|ls#|%CgndDb0z#){t~)`ep)QOu)e*LkOr6e2KSkQS zG1fr?RCq9%<ew}1Sp~YDVCR$j4lDzOJ&HDch2IBdB8rRgER_I;lhCsiwioo~UXLWZ z{Z{Q5%8J;u=ITiNuLrY+r*U?zd(wVss-P|lj^Z53@!S`wT^=w4HoKEUJFr^vGc$Yh zXa&2KaQ5J#1n)#RWZnkDh0@_cPn)Y`LQ!(~1X<=?4w7HOLNq}#4=S;pT`lfLBy206 z43+@KQ4e9R6HlE`d5@W|u26rZ>I)ZKQnlegXX=F!)*gtg9yw~Mf3l0lzJ?jwo8)|J z-i3@GQ<=_8&jrq8urHiP()PK`?b?u?8gAdlY&;7m!2~bqn_Xmf>7As*PPD%u_COJs z{B%eMWe(Sjg-&!R4La7?rGR-P2#wMRWL%|n*vh0cpU=?yyfADT*Glm3;`Bw<v2>Ds zakW>zj1M7mai>M9M0tX+<BpjXoo}R)uT2$Q77YU^ur=M84q720cVLQS9RBT(6Tp6J zr4{qxmXV51i#DxSCB~d<p4q~po?^dLf&Z{ymrY-<;j7D{2|&=gXzi?T!Bk2;5X?3a z%v`hsuFIdVj0*NQv0WVFQV<nPbs-rw>W#`7#Qu%U1za0Exfm1VB%(kI7oTfwJ>6|< z<@$4v!SpK_dWi~_!GuXhL&FdlwRz3Vy)AI+mDLFTp&rJJ^Eg#I^J(Yi&b6s3HkVJ1 z!2m9e(tZ~WZ*i3VyXd_+#$dOUlM9)J9=&3ECnPypYs4xe+uXII>s$(RL{;QkqW?#W z>*0EjuPjR3SN19NQ=yRB=-Oeg4(6<#wSvvsr^os)CU{QPiVQLAPggI#a}{P32l9yF zg$q?guacNvpb!`q3ikIs@<6hh^pQ5J$Q<ve0JL!qfO)RB&P>ebJ@O6K0_Y;E6o0<0 zE=iOnIW|k5Tc5{^VA+)eHn(BiaSs??cM_s_#>iBLOXR`|GU5G{9j;d9^@KaHPUN&w z8L<}w0uvu}+<iCL1G9>2(8$4%F6Dj*4xC4B^+9i#C0lp$#LF2wJXr#~5gVpa&NF1y zeW(#p;u*5o!Jy0&iCuZ{+K4410EG6~@W%#u8=%+%nDKjrSn9b!{<GQ~8>Ml!#4=@A zGUH$Hr4|T+PxWk%@6j$+>Q<fJXM{X1mx>UF_Si7~XEhNhGbz2xY;Y$4=w;mqItgrJ zVNg2~`7YJzLro+o7c+@>Gv#l=VNRyIf)EICZKvw=@g~$Vr~g6?ZbR*mVMbLtRHx52 zq2_n^FVxUB)X)r8=Ib*4RpyZvKvw3W_A*;(iDgPMB#M8LxzYl`%AD68U)3&FN>!(C z7-86vyY9c()>z;T+k|V{(@GmL*j$ecnZni8>FFa3>A9=_i<H(vipkv8wX@)j0Smva zPTxPmz~p{wMq(~)>oN(VG{Wfii5;_G8#W79RHx^TFzn2YG^4#+#`zX#Mz)<zYlm=2 zb^0G83>mpg%t%b=_AXHThONS{Nm}h|)(r~<i2tfO{q_jM=G?C=sE4+9Q2=U|aAA8k z%iGX42^UtUKPJ-s(v0>ln^kSl+k~O*Db=>cWeVq4r@tY>o&OdkNxP;>jWm}9GcVI5 z-MQ82RwF^WbIn*xa;8bTv)d=;Y{3TU&a6)FG7_Xa-Hi4w+foZOQ^lpVLl_LLcO*zR z*o?$TcXT0L%kUjtiKNnnlb~^qG;GP8WQKT`tiOe<VUuuTdj<pA;uzt0XsaU)S-FA# zMJc!qI#W1q1M}w&XHwsG3dcfI9ckE>JJyWJwCrFD5)1v?Cm7a7B17m`o&MEGLt3t% z8SP!xBio=i3r7-4?Q7G@x8sLbr+-i6JJdw2+k5@4(gM!tcbclNFEmvmU|%y2OxaFW zzzsW10+vAwBm$P1A>QRuYat5)9?(9&d)wj|VLxb=M8MwvMM>WVohj_Ifnl;t0``QK zNd)X^#$--snFQRseS(E;Br=4(pluQX_cEis%X�^k$*7J*69Maa)8vps9{DXmTwh z2vBO@Qpzmg%n~gt+$>=iXsII&+jF~^(`PcZY)eTOc5a_otpyu8L>M&Qj5KV`?PNxK zmu+g4xm9l7#U_;|6hosNW!RJ}Hsdg|U2HeBQ)ow$YF}}jEd-$U+Cak`1;Vwlpsw3x z60TKyE<M`NGK4~CnxjCtLNnUCT*}&@Hwy*rDGh3i+alya6CMS^<-J9zy>R_4;7q^W zB**eB;TmYJqvYFj*O;-GdAo^l<>|uJ?Gu|}!Ip0meh+PRlzeOM_hz(r*$%ZpGx=Ov zX~Jk|siWkZa-+>iOx13-mDv19T8UgEjD*%VN}id!%nb1^S*3-nJVUsoJ%g&Yxb4EP zp+%08<J_<Ri;|`ddW-O@4a|=}l2#($Dh!8~I!eAdH{6WLT=|g{Bna!v_6hE7Be6;N zB{a)X@*TOMX0&%%*SA4$6V4};+SjIIZ^zGrrb^^H&qVG0ZYbXZ&M5b=g_$Ot4K0-j zc($1bM!$y@@P$1l0Z)e(NCZ614Dl|PWfrm^;NbT8EpLn4E}R0bk_dRpe^IJxgWe*X zw1JWDH3@hkG)yAkiDpb@<6e`11KTG^`<Yv)e3LK`+NK`}c$^vSUDm~I(A$J#+f#CG zi^~-HLsRvWr|0^=MX7yDky^l+k^5NTV&O<=seW=MccdAMS+b99DH`GM_K6L$V9PfP zheEUTlkdzOY({&RZGQ_iQ^KXSL---IN<Vo<?uTZi&Do?Ig4!t~Nwu#yGb{ukegHH~ zKM?K!GbS@7+az4?_FRUwp=}a+L(?R}?PErJm&?dD=xxGx)*36@;xdK3p$QY=_I`^} zd*LcA;7n7tNw`vIu0*&}GZxctze%_~+OwTz!3N>FLR%%m?P^AQmu;;Dn$hfM)7l~I z3@w!ix3d|E$=}bmlDqraR+2942(8agz9qM#8RA{C`4+NJF~#i}EVRTmKZN$^C)ecu z=fb1J0(<!-ONEI#;5b+CrVuc1lk%|oFAmBqn8%*BPruv}bKoVkQ9t>%T!EPibN>LT z3_B_aO8Z(=Ye9>73Jugxo|a2(8L#r(@<;Mpu+?c~jVjYeX}C42>LH1x1IVh6#6^h* zS>g=wLf2$C#}%|!-egwkT>0rjF_W%aYF_%XsD1n`sEq4R?7V5cu*18lNA&@<=KoB6 zh9%CBD71PXH`EgUKIdVUIKz6OAi3K+*^cTHX#rqLe(Kr_CatMLyZ6&jwt?CGkIhE> zzp<jRfW6O3nk5dB;Ju%O))N0d?p{kAC{L{Wmu%=Q08IY>NH*lD!tdWtBfkx-iOmL( zi+&%h!~*s{D>p20@)%*{`?zJ6`1f(kEphTh;n)9?jVcQOqd&-&#>RgW6?Md1TbSnu z+4da|QhBdz#TKylS#h?+{kx>3miYH^JuGqHi7onHvLUkoFxo?=(pdIBfBtP?_YRq| z@%8&)!4|OhS(#ypgKR8(A2-wz|2}S*B@VI?`Y+jtv;Z)g!=`M^eLoFl8`!18rfkf7 zA53Ebd!LmwOB`e)_<dZhCH{Tfy_Pu0#-xAAhTa0eDD|dn1iqg}ejC_Hy(t@G-v=wP zfW6Pk4NDwkW90j|WtRB&amy`nkd2}Ll8q`0026tH<u6g#`@J+gLS^Q*v8m#^zx=F_ zQUzYme;<zeupQj=5-Q<e;j|WdFj?+$-Cq?TzjK3^qWD)}>aLlnoDp_SUU=P~DOXX+ zkK_eH@%t=@^DWRDg`I)fEC15<dQgoPD2=c)N7I>7BtJ?BkYBy={&?U~(#edFP_bnp zal^Ic(RF`-zI#LPJ`?$t{Bh8MlnMIJ8}E109%8f9d`J%~1D*?oyv(<I9`K?S<w=>G zQVHH9RAw%;2HO*i_^2oEa5gjasHb=Fo@%eiXjKwy-WOyTBFc(?j3_hyNJMKWx>51z zDdHwm<rz^mwU&_{?X2+GmAMcsl=BwkUH9K{jLi{m%cC*JuKU9YQ<pbK44wDiy*??2 zrj(aAUca>JL{yir^zt728?H}+ybG>hil}mEjOsF5FYmnnH5<lb!(K;q8BgG<*f7L~ zy@H9YUY@c4Aqi7cBrHPlFsjRAJtVT_`lL-^B#D@xqq^MI8{GD9ygn&`4Y`~f)#VSp zq09cn>yuP$2n^a?a`gts{b~}zsA!hC1c2L5;FN5l{?{)#0$jRY?zq2+4Uw`*0=paZ zau<$Wg;2}}0>9rAz<*dkD>m#Euy(}Yvi}^Le}BRpbR}JGAAy8_XTzi<%<(s1kHDQ| z!^9-)>aV~cfy-sXtXSOBz#f77nGF+=FlPk-rW3$JY#@(CJ_#HW!2N6(B4MBY0t^zk zJtRy?vB&`T6@lAH!u;8=8z8;e1a2D%ld@r-9*4ppa3JFxTFi!R0=S_7r`WF{fmR$c z2(S+U1VQJ}0yd0@x(gu)qRydt95TRJ5jYTZ0b3w00QE*M???o_iw%jWN^Lx(cA~5J z5oJ?TxrRlHlONUp#+_J3ara>v`(L|>bcq8J;Q|Na^Q)^wnPs3Km?Gh_EPIZo40*_W z`Ky;gnODku*I4IrzIoP8a0yjc_t6iH;4Wts?`Y*BunH%F$)t-fXf4FoP^$cys%xq! zN-C4bTl1pCbEI&f)f<j`$h*%CI5#R27?pW<7yk`$6od&Nj(U{?Ut;*`1YhLcWhrYX zo-<SKN$ixzOqfw10Ub5aPzYfJwR|`a(zw-v3LhVXiW%HgT)=e6aTBq>1Lre-Iip6i zzlHNU8C)yV4dCKQ5IanSB&^<%$4sN9^yO|=7h>#UvlU`4E^K<t1h~fboH?G;zb8j; z#ujk1;tDkC1su%Yso)F)0SAv6tK9DV&|)RiC%1bymcJ(xS>=CCRvB<7;JEXg!i46! za5_(WgFu10jETz~D4GqNZ}=*RIhi|(<J@!X=HEssrbBSt!V#3%lVtL0<{FO3xlu9W z<ngzt^oUIP@hyfOw^}%63WpjsimOE9!R$c>y`|V<653!;%K%+7Yfw_!kqmm<-~>8> z&uQlc4h7pq6bjk`bO&R@jsZn28ObI&eFSp4NJ#S*n~8Jv2rd?YB>98rkC>;w_4gsC zZUaR9>=roP45T=XkIH_Bumo}lHraeIz=C$y029MLOyKdmJh(_KJ@Ls0fEt@q+1)P< zF8ON6bq;yT9ttjp(&UV|{8I_M;fBN{IQ4zQY(3F)w7*+64k9><&Zd7%^eN>_>v$k3 z|E#^1+Y4O$WpWK+Q?ZHB&fqLX<8zLAeWC|14>O%lj_&S1dIv`;{<w)$c%6{DW#5XK z6(_$_K#B5)Sqr^0ba96gP+&p;sduTz;V9Ul*21J}1ji)pYt$bu1<x(X{Nr)F*y93z z+!_Z;%!k89+L{lEA9YFt=fpw#)HBuBREXI5PiimC^42yz6L2~@O9*0%_iAC5o$~PE zgM9q+(#UzGdro%4nDZR#MSElSgQKt@$#`IO=;OmU0AzD44BQ``@^Tw0<QW<tgC8h! zRp-oY6B^)&u7<HX-Gk@wBh%+}cNezOjjKuz)y30+DFMU5?V3mc*D&`m^H2BmVhh1T z8sh;u`~y@<Yb^!T+f+c^pn!^)eW%?O5nfK-Nr#9z{Y1Z%jLScR;u6F?lk@40n*xa^ z0P<bBp6DBrVsofA6{jZiWAJ`cmd%D9e$~D(<1=)u0~)!~x&sEa1Sa|U1O9P4iiV@y zDchXvZ1|urqVd_f;K5iRDlG=D*yJ6d(Z#!;><jkV5AfY{=kToqRiNkK2aZrv8B%vn z71KdrI0;-|a|K0+IBaAR^Sl)Y&dAq)KobrRSAe#Q)S+A#_oRe|Isl3inyD!XK?XrB z_!r|UYJejrhe9DJD*e1F=gIWNIfl))U;*7$Ky=1*aaVJQ{^1MouTGjLvHngj(!J#} z!rupOdw`^_u*Kt;lYR99c0O+p8T2Cwsu^j%vx03Ew5_SawL)>|nx{V@%8yc?WUexC zrCW5#FRj|kU*`G*p1w?-7ti$tQPgvNMKtYPYq<+K94mwo%=J3wGecbhHw8djQI2le z6eM@8r7{=B6lE$logt=66vTA}4JYsBe3$c0<^LKu-wmqs#henF*{aBwRH&D1X zlmT@MuKd2qXxE0q5J#u{6=)gl0F~zpoE}>GcTwVOS$b5I*h`lFO_ZBBNR}Bvazo52 zZ&{=HJ~^6eRHQjY{J{(_=&SHg{R4S$x+{~8gCfN-{%||WbDKxO5L-&g1U0rE1;5`z zK>wk6$SR3^ZJk&?&zdMamJOldRti*5VBt@wuHbqm8fwjSK5;oj>}Es3EAyT@UbbYu z)|B~2ka<n>oaOt$SU9ENT#yrc%=^H%j=wDZ89aTRzVqUx8>65I(yOaIwKD#YWNJ&@ zoFdmB&KD8uBlWXD2bZd~l5epFOO!F-=4;gce`Vm|gjXbmSimPviJnrs&Sd_OR&1f6 zu&(7fJ^+KAP>w9erBIG;wY>knq9pGMrdwfR@G3{{ycm&RV{+=}6o3bXS2-`{JjgNU zm#*aUtGWSNH!+~YeP|f#XHZEEh}Xpv9me4*>!3im5ddCoYgn(guy@r0J4-FbP@l6z zX_0v5$r%z$Id!IayG3a8DbXRd-1arkObGOtflNZsU=>sLyR#y+IM0mbK*{*AFpok3 zn?&yVTJd@%(JQ&~{BBvB#jg2H6pGDJiJ3Wtlv7=T!@<}AI3+11Fm{B9>h^g60zYPU z;Adej_!fhU!gI;ZKdsE)J8{$lrhXMA5y4O+3c8nQeLN#J8%*Xl%xnM;2jE8ot~DHe z)<DS<ZQw%B1)?ygr)Yc7=@Y!!q-1L194>6hgyG&=46K2Xyf!rz&SS_lRUm8ID<KFn z_XgC2rDq{)K1aM0?+|<GZ<vb>tw$RhDZ_f3hB0|uYVeh0^JYTM&7@Oo2y+qYErC(m zyg8OL*VOH(u5l$oUj}WQinVihu*x<>(MH`wjF?e~KaB3Ei*pPx%8GwYnZSUTdpRD$ zcakW;&lVV<!(QwVbDyZSlaX&dZeyNfntx0HmIVcGu7S<*)V+i`?}RkgvY=t(kVZC3 z4PX%0w+40(dpEv)_1hTg@Z;n>_Cj)K#O|;hVmpMQpauA*Ka2|TlKG}ryzO5Y+)(*C zilW34UowA0W<VA$%1iH?{xr%<>zn>EiYP@fbW-|1?J+p=++gChnyI?%tC&-ON5W8d z@>0Lp96L%fgs8!Z`tXOv2OTTIBOvC)2U0YN*iB??lm(JcV76kL6cziME!8}o|0}U0 zbd4CKiOdhw#S!Vq3k9H5B=*w9ldt+xqM&==B7bmPJ($RkH;865)1VtX0$l}<B``KQ z*n#7dubUbqFdu=0m*Ea>s2_9fiVwfaxS0_Y_m?QeJ>d3Oq5!P|wcnFv?ARCy{^!RR zPZ0Hml1X^-2a{ghb27`>vw1L@P@t~}%XjYt=O_2(VGibO#xr8hhWoNUd#Z@Ql!OxQ z-1WXF^HH3EC=K`2!e~~N3RaFvR`7yDnbjrTIFV$xFv56f5^>GP<Gd5V5R8oZUrA5f zNLlw)&48jW6Ee3;l(NEfxM#cr1zgUt_vES_+^5x+W`dXDJj@PE9=o{ky$sxH-p*{e zdQwPlf|{AiOuW`h@#SNs80sFGx*3SIEHG98Q#_B6kc3R>C7(wNCBs2b!)?H}v<)Rs z6^XerN_vPUfyvVbCtGC!DVw!(NZDLa-6XzBBeh`6oZQ`&kFoAxj4Xrs3;E*8=5jHV zO#}Feu?lOhz}>x(M4=-_o*E=VqFWiBK#~rXc_)mz6YDjuVeelIt>qj2-7XF~KSJm+ zhP@Ij)7t403>U3ySWAVG`ExiXb$1ApeQ#i3IrjL(Tj&!8*xOai|ILOXko>`5Q6e-l z670qKo&|G?o+_7fFy|gcWn2V)I<BlE=(O!F8^c$PO^)D(GbWEquFM&dJ8S)N0yKMo zAXR8t(@RiiD;Sl-?7ZQ@j|}!=e*becKQgi#bMkr*W?Z>jK%|xzGr<MqCTLPk*d>D2 z*aVl7o9Ms`Dnu-!fs|B%U_`U2MfgHUhpWP1;!uH}fOKGnB4~mcnN}aksIPy-kJLK? zRiLhSU|QI>J>!4Fx3eZfJ_U^U4-&LEo`>5=Eg3~d%6~4Ccw<=4NU_M}1z3J+0Yg<S zv7T|wd{04htVbO2MbTuWw9$<CMt}E6eHUxcMJFL80&}O%%~(kL029X5jPK39BJOt; zv+(9e<TvQ%y<m3U4D03$Ff^?&YJKjh0$YThDsx7BPBJ}id71DYGLvq3aKF9EnD1}( zH-+D0{&#DXJ4?eO+NdqQ+@@{*HV=z#F?P2<<<hOZ$}GHnjKkTx5O9wFJ5J?UX5<}H zI@twG<egz0e)cIwe@DXM2YdlWEDrxWe*AIf=^b|w%-i{c%5#`bciFsv&gC&??A;(+ zmgl|GA^jhjEq6_M%Rb2D-|eelWtFXSmG2V8#*5%Oah-z$CAHdNu!89e+)-Ou%YGvd z2g)D0%B_P6MD9wnp+az3Yq>!|dQC`i-v$GL1j~m(S6N62C-q5Sl7clH_x9BeNf0bg zV7&a13H|GHF3Vm!nBV`JB?<&$>Ojm$?=9kfhijOP_jZvif-f8uGx+ZbTychd0M;F< z+ZCra>rZCe-!nwePr#+TADGvF_ve0leb0E`A2x&&hm>?&?xX5f!1|Q6ioh|(4FTK} z=AmGQ1&g40W_vj@8}A<xvA-*6Ca`oD`Ryd~YGhuP4srJcm|p`MtGCXSIf<)a0wkuK zf+PK-G&5xw^60)>_?j7C=4M+!#<?v`Mp?*XW<{AtH*26_-M5N_Nov8>108nCy%#v0 zmW4cGel8p2z*V<KcPWHv+)gsD%f42S8|rN?g6BR~TB#qwA^_ZVH&2IwsF8GXZw)=6 zWe%pH=XZg?7%Gi?d+2#k3Jtgsh90%c1C{ra91Ki=UiCqanZln8$>ELARvR^P*mBss zG)JCd!-sn-#CNW90CXtj?2Qq#Gq`Es^OFoAvA<1#fekF&d9kBbIe&P3P|6tz#K6_M zzhcILK@Dn}id?bI`iHT37&Iod%vByl3m~PY-_C$v+hOd$C6nA_y<d#DpaP?76xci) zs*3|lcL2rJs={(`>PUoTWHvt>)*t#&xG>>8OnXHIv(~cK3i^erH(b=CyQu|~p`xaR z)J!~>RILgx=FLM`#qY=WeZg<}0E4{(G6vX%*VG8QJm~QJLl@&lkVaBc8u)O#Y_?20 zcJ=PnydNHZGeyPOsFiSd`E7V*VH}?ACSAhfS|{7H4R_UK&IdH5TB&s*@a7*pZF2ct zwd6I)pHM^M1V(9D59YLCxXrm_6Kq%b^ig=jtTDlc;&L2z)hHW%-6fCvQeB6<|JgG~ zm*B{nndIE^+Fw=$fVnn*@dSRk0L27?F;rUVXY?;7+WL^TY%0He93o*P&gefiLB)pa zCmTyfYoIS<uOGw&p+Z<SR?W_9U|T>=(4iN9kze2$`Cz%lcGPxJZ7Usw0_4$ka4){K zj)M^CHprLW@l%=acUc$qGF{ApT_2isQ?R{YRqi2Te|zj@e9L9Ll5d#s@*b`wi{Bbk zG{GTnUsS@Oa@0pbmYih4X@7q0AXs!g%;u3f0d(^Xv4!!}2KkO4I*1Y0wAcs!C<1@$ zGQA(!e<Ev64z8$<gM*nTKoJ-^$UYGK#f~(?k+p=`^|`QV+G7NeaZM<YG&7?jSn~zU zhtCtnIG3SjhoNlEWNV@B6>&Z2-n#+JC{d%p+~P#KNvE1drh+o@a9!N;SFr(!AO;wo zfpj#GhO3x^t;=JBqMO`>4W`l#IAN5z9b90#-_~p4+Cff(JgAOsJ3OQhgDXDInBU9$ zNX66S<7ima#kHu;aP`CEq^XmuFv=AB7@LZLPF%|>m#b|ku9Y1yFjFdqh$=`;44lIx zSB&9)pPRv4uL$VLhO0j%HPU?_v^BAQrY`<2Oq(QZOn5Leyz&bduA4ewl(i<GKgOjU z@IT-^nbB8HR+JR~@oufkY9>Vi)3`sNQn7_sRP<k!s<eTF@H+cnmC6+VdwRs$TfG); z_^eGpq=GO#%I&N;koY`C^Ek}M6{I%u(tNVi#$ADp<B4Cw<jYQ3pel|%VFPpaDJuT2 zw(Q)>7+ki7>HTB|ClvL7qsk{CJW0vn|H(VhT^XCR4Z66SYEBmWM9k}Fqk9TiYZhV+ zy`V4#Oi|s-2OCR`j8M>sz|IBurTT`M`8)s|9sS7ceEuP?^a6A3`6##~Ab#PD>W+H8 zutp*nFc`Jxr_A7&1G(S(CLdk)^1M)V3kpe;{oPT&T5wS+jkw1=uItX18fzJ=H>>zk zpC?DbbS6YNTs@lHa0|f?{n7BI#XOO?^o6_h0L9Ep=)$~g5%I;Xb<CTV6nHl?8(KeN z-=|yq^2H(djy`S8Kzwo5?>TCOCc`hqC47^IK6Vqk{h>=3c3yG=c%W6=jpEV!y9vQk zE_^d8)iykokKBO3UObnN2J*#jyK^=|2oPvZ#d-l6;-Z8*iq>bFjN;Oy?SMEXUUH7@ z{S;eiBylMuWcRLx*bQ&Ts@LSmXyBt88($a(J~k7KVxNE5q#+EFUWn6$=#%c^)aSYc z2@6Jt7{!O5wSmy{e+AP8z>34moK9Bg1o99UpCYNug+<sD(k83iVix}$>vgu#uq~*0 z8ri=Q``p4c)+o(c?DO+>8=Bf~?M%r_rz89QMzOIN@3cYP_~OGC@JSmqj4zJ(3BR!+ z*)G=MezxdSzSy`MD{YYrU+l9BXWA0c9NvXbvmwQ~_^vG)$1|p5sU7m=`K05ScIX=y z0hhU{8wqPpqO(n$fGT+St{qy58gZ~a>cu-8i=*w)#}b%1fUm;{vdbX<tcpjzVI~oQ zuh^prjsl2`+p{>+p9@Y{jQeyzqZQ3OVg|%Ic8EPCse|7`Ds9M0wxCqf1YaZV;QBbM zlEAbqtYq0??dgIYlVgY3!CDr5!c8e|vRzncU(=ML7*?|~z4hBuii{%Yzu%A*)5>O# zdbz7D6$_qto$wF}-|c{Gx(`A0CjogR>{^_mp!pxdjmoa4o#AQI8*7XlkXKhlUEcz4 zwRZx$Uh<d9ba4obp~2mxH+ZZAn%P@XC3m33-fCSUJ5tIbzw=Inm4ObHe4T7~Wa;Ym z3ZHa9y<EhDyc3t#Gs3g=oG{N_V==0HaGe7Rb5(59LC0n&pus16X_8L$C3I|t3v>gl zCZqB9VziXBKPREuA3qnPE<EuhEObO(B3lTwwH=9l9g%cE-gRB#u=8ND5;}bF5b~LK zqRSJqZq7I}6ZS%Y87gvuccT4e!dBjO=Hz;*2V8`$>Tw#b!&5U}IoU`H3i61}*t2gq z9`_w1HPQzcIih|dC^E3vasf9uBJc6WW*sCVFQTnr=gg>6eGP>n&xbD=HQR%@wg*cO z^RvN^DwrmaKgt$zA%6QT8{w~k+ONVZcu7a}g-g}_pWKK3L>V0D=dnYQC_{#*f-tsD zhaYxC9}S0`$NsABF&JCP!9y2SWlPo`Z(mT<#Sc)!PKqy?;Ku%1(aj90$5Wk<D;e5h zY2F22a6(;qGCRD{2_&eo!NE?b8yC5^BNwUZKq6hb*}atld!HNbu)>4`{OWbc>DmeH z=UMrbla;8?CVZhYO8B4>a+Bi~4F;qhXA3NOWJ8PA>hKo1>WYLdzlz&<odnJ4{~L5N z)=%c)U!dcNdo*{T));EsUOg*+P(pD*Y&W$Cb_AgOw+?AFmUQ?X3%Z~|imCyBfU^il zCqB${z_YpzCcwK*2Y=jY2q~{ah3z?rUrNr7>i;6rfy2c=(!bmi?uP}$1E_uoS_}8q zSHd4>^V)ocoNKL5So7e!iiizt#gu$5`Rp&)vyG?O<e;kR<>~f`6@|0*vM!Q6ax#Y@ z_teb}qRBV;jRMFFh|HbZCua@21PK(E+_K-e)QV=BQd6+Wb;xa>TxEzkxdbrPu;F8K z5+ng)3ZRg9q;FDE95K<HOrjbH8@liZ)*#Irx@1blDid<L5und8OGai;I}D6SDuPqt zK~ceYp$qCN{gU1EHP0cUxXZ~^if0oFUB}!xW3?3Db3rbS%}@zcBEhDVn85BkU9|EP zEbNN<$@2bbyYhc1K1yRXC`#XXkinS!=0RRepyj&~zTvLnojkmxE4rg#d(eg3Geh@Y z%v^Y+ST#XHlM#wO*J8~GY<R|!Lx<twVMlQ3(2!!Cyzl}T9?CesA+!KiSclW0K7{6- z9rl3S9hH>x052fPSh5kD*ANd^ja@xcwR^P($cr?&Lzx>M&1q(!C#cV3zqjX{?S|Iz zI*0shZW5%bVCb{qmiag4jmhWmd2*5+yD<rM6!r&2rj5yG5Od-K(OVmnjlhMiZP<Go zlhS;Tw>BoFV&U6e`Tw7dNp>8G4<Q?qfxM7vJkt%i^JSC*uW^IXO5=mIZm8>&JY#u| zleM(qGB_EoHyvLIpW{cs=PwIuDKWUKM+_Kt_f*6Dcqn3<xn??7uSl(evWFf4W+Kp* z!au{I>1d#c{iv9V)6-GC59F9X;@Mwh6I1dOeh=5(1oO@?kfmMZk2Fo`2HSx-_ntOj z7WZ46pMgG=Xj(6M6vE&F(=={=e}yd39C4p*XbfL7`T}0E4UM*^4re%7Tlem%HBt^4 zIl0@A4_~6mN*#FKJAv$sSun-H({alV)Ss_$+lUA3L@W7Kes}S{ok-5Fy8kD(%R=Y* zRXvLE?JV>K{3yUfwJ3yN)p8tf)1q+P;0BYVeb6(h)iWgUhK1p5T>><i8ZdoqjVDS` zU$4K|Tz(1OM03fcgbXhJv+2wH!M4<dE1eeLTcm<4nuWJXQ9tk0cvyFgrQfjom9-qn zz)7%CR}ylle*wGQ3-n@7*iOS$Qq-BJU7lmr6L}$@dXRF$f!%zVTBHQYpI3G|l;{m= z9$@|JfldX+^E<K2A(+p?N>*I97SHd6x_hu3SA2MrEWJxg8FKgq8={T!kg5)@W4OfC z1F^Oj$frb+ic5N-MGi0=LD$GgfNj`G$-;r|XqW>_$g?*`C@U6kbVpBRK3n_0TTtS% zx!wr_VZ$=6$$kGgL~i3|mJtbnmw{xwzc(7ni`a}G_C`O;BH{+Uhb^7poiOZO>>`34 zv<{#3K*PJFD$RA)RQt}c?5@R~J)!ooAD*l66i;;4qdPeHPAR$`)FnW^;ubGG%KnVG zvziy6N&-uL$DAo1zUqmb7SWdxdw5G0u&xVqacO5*G{r3{^E(xsklq_d5S#`%vmH5i zR0@Vw*s;Qijx2bQL!WnDkj5nz1&RIO{I7{6cp-mBR+ef5R3lG816bcZiSK$LHxC`u zE*qN%_$R1sNl6cAt+8w;*vECIuE9pdLvp$vT1Md>ePBjgqDaQm`k;@U^WDH1*M?AI z**ch-U0^p@=AXj4K4^X~b)XX_HhaL9f5S%6L$#^2ORfPWzYIt=8+-Rf-*6=xc%B4< zoq#O7r!Sh!1q#oyf%zeoTfNa76Whm+$GH!H&EAr!4F1|14f<RK?5aFE!udje7R-de z7lMFGDm)i@+>+Kb2%Adt;CFt+Z>+BbI5h{H#HTs?R5QuMwPbV(>EFnlKf}P=X8nYn zv;j13@J64yv*HTZLjonOs#P!8X(Jo9Pk)!p?n)eD@v;w)hyDM6YVz}go3zjpW3cuE zw2<d>6$|^Jc?xPGcvwp^Dzmo095ommf)Vpb$~-t`JrD?M#-Xa9&5|(z?y}zXtYt2x z+h$#&Tts&ut3w#t^FtDKDkmW&aH7wHRRq5cKiXRh7}43+?s^-rB4$IK24(&^B9n+S z2@Y8Bs{u1Z&ttvLzD~do`k|rxl4Z}ZYkxGYPwH`9^4Bn_txy@R+r5GQLfNWv(K2?# zEK))sn8PO7Qs%|st^Lt3Tef@oNfkRRDG=Z7j|PL`M)%&2-oR0QS%(&~q4%H~Omt0% z9UN!sPQv5e`_+%XrfQxAZ1k>qO{@wzg13pYuc71kqjErOwk;)B2la=o{nrG`y-$50 z$NITAF1vZxICwCKTU6Pb5AZQTa_1ma4HL^uoH_srokp5*)ayAMMKRt#0J$q<_2W5W zo=R2@y?yJzaM48z0~aehu?(aHxyLSwdks#z#)FfVt!&r2jw2PamAJ7nhk8P&d!Ga2 zNjc<gAu;aW2gYNz-j!csOXnAzN|$VyL_hRY`ND+eHT2J*w@_N8O97u(uVE9@Zp;!V zaLoahKA~;>wAy|$n7^n(`(Kj6)sKFK*ZDxJ$}c^QfA&GXog5gTHi5ov=%jnY_lSE9 zvxHN)!3Xv0kouSEC^%Om{ww@jFXs5l&_{@$-`aq`9*D+_D4DVwG(G%%G~S6Edwcf~ z_S}-?<7x0q#in)M-E&U^G>HHj3@VIkz!|_%Lf_AD#X#g99xR(1>v0y2UBP!?5eEoX z*qg|*?0~^UGJ_?lVp|o($*p2Egq1|>V98SO8}`E9p4D)38pcM@cvA)13^?LL6vdNS z;YS~$so=dC+HZL>X}?ozfPz151uivu)ihOuV|2);zEmFr3rp~KB7lADCbXYzo%)cU z@sf{FUkA3}&JE$3?BQ0t=OZ-7BR}h^d#^oBE8j9aC8>Z>(o>!8d=A6h3*7jm<6ge7 z`sE6~U-Jx@Sc4!zP0XJbf)^7uT>SybjC3;hwXYw$ObnJzx?qm)&&Gd}5&)f7u;M4{ z^Y1PKOmtpzn9A{ma3WU^-S(3_xg9O*z8shNBIggdO!VTI$a@7$d`u63eGw?AD{7OB zdqPtA#!4(5gm%r+clcBFfxHVXh34Hb(8h47@wFLnpM*^-KlQH#=lSku8=wtcUu1VA z=%df7dD~&(<nCG1@b^W@eHdLkEL#=p(SaKC6^1WnV~16^%V4yN6RFykBNK5M|1lUf z^7V&~;d4V^j*{>88}=NEJ_9>s$MKq>D4GY`8~+T2X<h1m+<6#^;O9Hk;|;@5{!oWf zD7Ue6FCFRIWtug*ByjE4n{1>R{quR??0yX08zxF`xEu<P5L;A`$4yu{9DUbU7P&%~ z0zT5jEP_n`y)GGQt@x=)Ai_V`@O)!DmX1J=c@Af=%}B)9rjZ$W;bEgJq5$t7iDq%x z+4s{m;?`B~_5^nrg+_3J!u?#J;t`%b3dMiS7Q?<Q(!+Zv9Z!a;cUPA<umEIS4>J}x z35^-&xZhav@+k>9kg4je>MQ@VhWAmzHdrH)8BT|XxR)RLWQLmbCg)az0ibL41;iRN z%hOgseF!=)@C7wK4a&1**?pJt-R!E74hVo5EeE-Gi?L)%nHjQ>A&^)8!oT~WAs=g? zp6XlAdnb-L4_q{!FXB+jQz7-cW)wP=aI^7-cVho?*ob7=2@GR5PV}SC;gO@!N|%*{ z3%U^6onG-jXm^1$F@~bedV(*EMnm}8(#Kffk9<5>xbl+(E@TgMBmy#XJlo6>4PQfA zsNxqq+aE1g)IcqYi@Tf|>Mr}G#(n77f8?2>4&KSIc*$ROVnZ1WXr6K#J3GuJeFT)^ z^on#ydMw=$ervOCz@YarcncDdZn>t09pFaR{NrRTsDs_3nw!LSQ&q^_PoHqVvaU@i zgz=-AV@178!3OJF_du<4pnHILUdunP>Ji&F?lq}bhxXwdG7}b!W0$cnvt3E(ls4^7 zHr8u0S;dmkQ=TH~N)}g4{G$9!ISge(VMHz2sE;iIi!LH5?VSSWqW0j4(`x066fLCv z3FIN<k}lEkIb9R$E2tq2c3BpeXwgVE(MQ>xNoa?imZdYBXk`FNRIU<rB0F$!KKUE& zI2L`_y%YQnrAabilIJzQs5e-(mGR~FbyRGTFJ3SfbyFNf896F@13FcERQ(I$7YQkY zOHPZEO%=WN6T|C+)g(k^FSiXshSr0x%rJoA64VOuuTYT7e!1-;J}o**KLN-eQ*X4$ zs}J%CWb8R1bs@p9DX^6hiRzE4<0*HguqB|BfdUz(C1D?V$3q|`YQZN#v?rf?Gf8%x zdTI;o42f&%V~-^hJKH4#Hiy;6?zO`nAEVa}Y+bAv!_MsVQ|qwzI5esg8@R8E3rzio z4;5R5ea4{=on`era|I3`#>E%EAV@=S{W#PEoRC!Et^sJlh(xH)iC;fwY++L`37VVj z2TvU*4Y6s3y&dbSgA*!Xst1zS=T$;4GKRLgub!)}U`NE%`}o%YbkLsVA^aYwZt0EJ zaAY8g;CMClr+SBr_+=pU94wr=6WcrLBhKNWLFT}4M=r3qfItrF3@0Pk%#E)D7v*#6 z0X7CAUuSTkWsdXYdRl#IZcgX%Xb_YkJq}NqfO>w;6OIMlJqOx<K%$e7>J$?IjV*tE zAsii4z|lbknH++AtBT|0lW9ND(WsCoi2V3N;wt2{$PbPoKxe3)a`$~gnMGJ(67kd> z_`(D<YzUX<-6pxFI~GElDR&H#%j-IuQH0OASmRbM)_#(FDIPu%xjA!!c*;dbFwj?v zXc=BI5h*ws1(tD9K8d(tBC0`0u<;W#(0gjJVJcn42n3Znm8Y8U-<suEOIn4)4MRt| z3S5>|!pSzzkboynLS8<r>pJd|xR0!dGc3UqGglSoVBQ92DuAxdsxt8DyHID!4T|Tt zv33&j>}@;?v#|;tsPb@~Lq$G;Y<*_vKwppbJX;PnaaZA|lhA<P#d(I0>4#=k@-zll zx{NS$xX>V>%Lp?D4IVied5vFN#{))u>xG07U`Tl9h=u%b94jFYbSVZ$`bxkFnBW(z zJW8JHl>}$yj8!K{CRWavgEvh^eR-v4@X5)@#U#a}?9iue1$Cjbjq=&JWCH4kJ5Pbt zs?T3IcnV79<sHHIr=Z@vki%Fo74>!bmJBh%DehVk<+_kmF7gYsrq1BNsc61y1X=cs zqd`a5s4rH-?<q8I8fgJ#Sw@^c747ih2z<-dOReGvNahjDuih|rf;ZFRD@<||a(n4z zqp#xRU?jDvV3nYN$QAr+Fw%OvCBldRrX4}oP#9MyVQH$p$+<kNz~7KbnY#>5JGG;= zSotaX!cANHN~cowov4zZp~29Xn?a|VPqwQs(L7^W=|v3A#lFQWrlIlP#ntaq4(#+k z<<<m3S%y1(hWfi{v;KOUeCegP$!8rS<P$qZ#6WYQbfCbrPmKbhNtGQg#Vwzqj{~%= zjWBcs(Ke>dM6KUzm`{t{K#^iDxS>aZ?2Mz#c-t@@CdGpUjSp@>HXBob$lstckuV;V zrPpKibTnYN|6*Od1jaeo<a07y7Qt%3ubHf+_o*-l2~-UnIbnr&go-K4|8O511CuHm z@(RD4j=}@|LtqjJ<;T$D-ax5oBmRMX6VQ+ZJFUHH-T;q0JcFG^gp=5*R{)yLl&J`Y zvpAdawi(cZ*is1`!j`nmf3kN9tcUxMgPY~o3cf~@@ERbLJZUgq3-6T4Ww>ev8q$eH z3Li}T^8qcHvU_ay1dL`PPcA0V*Bn#k0yh6ijC&^%WMW_-yOILKx%huG(Le{TVicPK zJL2avk!vsQFffK4K-+{v9i4xhNT))*3~n%x^t2s-DuqYRM7?BfWY0;tSjsjw`#fbx z5y2TeEYmpZrZ|6Vyk-{iP_TjLdJzOd0EA38pYl#9KWgCF-DhQ?ad@mtFnmJaV_fZc z1z&gT+K-Ze44mwjQk*PaP|Q6$UXCp|B}}w|<r+JI(xg@bmOp(rCh%)D8={?>v-avs z$V88VRn2qbp<joz!KJd=m5r9lB5}Xj=o@S8{UfA0<z->*Y&1Zj^@0Mn;fj$SNakJ< zWHL07wu0Z9)EiJ0$I(_!HfQUdY~$+jYABt=L<jr>ypxiyNTAeU5m`(s4qJ!saC@}B z8Nj^dRdO0XQ%&06j*CWZ>K<6t)$5X>f{7J7bn)|5n~LNgsO;bjF0MY~88H3}S2ypg zSx1S|$gkP;l*Xq7HVcF42HYVKb?s-~z2fF?9{b=RBHO$Z-9D~-A3i+?xp=T;5WXL# ztlHLHkhih46+3ri!z*^Nl{6hU%|XlU*&yLg5|ovWSI<SCb<?ifNaQuO2|QcgfdNn& z6QGL&9qGs(+4%8X*l-aCM?g>lWM5pe5_kO^4UkflcQS~r7sq43Ga|~oDhLatJ!5~3 z3&i1{qa8!o#MF-&n&JWd`!Kn@`VeFVPVK$|E+^4ohIUU$Th*$lVB<#N;q%b%ymM;Y zG7q`*^-+2Ew59Fk33KS!AbX08&au9>Fy7Xb06&w68BD@i@MqZn3*^Q%fxxR=nyG)_ zWnZ8#`f|B0WFe)RN#4m&9bs#>E$MUQ*g&f9iEF+<gPd4!&wRqGd!L$X;Eby~9yuR* zag|+tsx3m}Vwg!^@lO8xlt~i5n|h;@a8j*xvXd3OI@Px2XsU!z+HmFT1gwC0umYY* zMwq~2QX<8Ff>;7*inPo!r?w4vvy;R5lo*FWy;OyL3M8shuW?`WXMj+3_$qlS{zsl} zx$rD;xMoW5JbY;szqCv64+zs1L(gqV&<>Z(M_+iS!OWNrdtlToYcSV3*RV`^LQ;m| zZO56A2>v7lN%^w;DR@~38YKD-rl#3JbFnT2IzzVC3_C`0TB?=LqA70qMHXMCg$*m@ zb3YN*LiAfh(GUd?aPLzW%=wIa$4w^&;yFd15jUL}ID{pY!5I0r(cw15Jfe~}t%_`u z3qZ9;mjF}E62(TMG(@gYj9o=6c7vmy=mo$F45>{LxXT7RvgZ_Ja=>a0X<A>HwT7I8 zagETP^~Fzhaf@G(VW@eAsUzG8(Cnx?^#Dk5JZ%H#QB5f50t{e)Q4#_t0AuO_qsfd> zkqH<>$vEw^4=|?E!Zv6{obJQ8{;&7|PV|P|N5Z|3i^(5@vU4Ms+&*IJRg<@E80_$k zYupticd8T2#$#mA1#bth=<d=6o`fyBy(LEd%RkFcF#@IE<x{oOn~!mR?bXjOluc=x z_GV_y+^YG{L6!SDSw^1yB{MXkNEHMV;F@g880@e)wo<@LUjn|_K1N<d8`ZIrXzsy% z8)f<#a3_%{vf_K&P$b4_^#uoccN>WywlI#`5b9)I(?CP^orF?82<%Vn{U!3~s`n}W z^aj^BJkq$v;gMpbLhkLri@!uZNPsiGL{i1Z?`qc$BPwxsRn`HJfks0|>y~M14l!&a zhgcqOq_VERFts&0nG03L7^%^J-XJ!8S>-v<CMX~^_iba;WWyioqKxX4@rbl5^KD3? zyE^39@2Eoxs8y3&zQbvwxb*@Kl%r*e;(PyxrWsplo^G{hnrTA7O){rx2A5M<_q#r@ z4^HX~R57+>R9D#gqThIM<y*GCe~n`m=9=LS(l0~@t)|&sSwou3)M&0HV6N^v!!dZ! zJFS9k2_Bb0RWQ=w$G1JQa&n{NCD2`f#?Trgm3JH~7b0-@O;*~K33%T^<mIpT$+o`C z!FsIcVE29xh4YKiU-5ubs)SlsRr+dqGyjvCMnkvHo3BCnimljrbX5qp4@2G^^{HC= z2_t;e7!b&XXW<E9;QASEg3&={DnB~FA&5RAvw!U-&=9<)p3K7j=uiWp4%-LTGa{vs zgb#ZwJT+rb-n$g0rqi`=<1cIkeC%jZg;oV{`3Q>^7^(cO_$hGGCu^s<D{KC4d%S4e zfvoMIaIiK=zU&soY!&e&i$F}GpjF^}wTpu7U`>)MkANC?AWBnvYP~W_Q_enbu+KvF zd5nGTWuIy6Q^h`4vd=K~IfH!$u+Ks4(}R6Fvrn1}KS}6&kY`_wGy6fq^Z(>_C2%WI zu;W4b8ZOi2a$t8ylt&dI>QTl%OW0>V`_!{fE&J54PbK?|WS^nzGnjq)vrifOl(J7T z`=q#V!l8#0o{A_BG5bNmbH?)B57gzJ%@gaV)J}Uf^TpgJ^UJ?{P|@`C&C434b+wnY zX+goEq#KvV1j$2lY@kc=uTPc7h{n=O=%qTix)L*)=s@*5+0EYmRP3+>I7o(DT=Dg- zA5~6309lbB+FJ?Nw9p9KQ|P8TCFwx%4n>gsO^)?oIET4j0~d0igAxW}>ed39sNQ|( zp4YoEI7}9?#~;ed7FbNwInDK^m^;I$!Kx|SU=aIzZP&SQ=N(4QdvjH){;*80lnZp! z$}f8E2gg0&vMYhN>5QtMA>%}ye5)M~O+FObVd2y0iK}$0Y*s)0S~2k(-8VK%D%R{< z`?V@??P3^H7@^%sRUX%B<KaR{e9-pHB@{n!0>uxSL&XNJqGE#<Q{CXYTDQ(>G-u<C zjoWcN^~J^!>PxV{N#-F+H}4d~Wcm#w64Z@Z%I(qFSCNe|Ml|j<`0s%?d-_oW{-t0K z?Vshlxk-=~i~?}Kq#a_xvl<=5KM_YUx4@M9J?pXF$uWK9(`O;@p3|F11hfFd*~!N^ zGFzGt=h?06pRx-@(3`af7ADbEnUmz{qF(nV$#;l)X9lNT#Ox_;_NdH{4I0r)6r_N4 z0ZT+U_9@|A92^SMj2@~W2>`;m@(9;6pe6kGEcfj<lhJmZ435N;sT-*|!d7Z!>YY^n z5JBzsd&v}ku%PB>({qaKGajwP$X#0VdOJ=)-EP1iPM~t=R%&VLc{9cx@UF!Vq>k1< zQBX%KhalJAA(310d}fyI*lvHa-pWz8w0P>HMT@8>DU(y7+-r&7awoo;dI3b{{oZY6 z651|FLfv2&>uTx{U}w3Bc(>1!sHdures~6y7oidHi2&n8nIyFzW`xpe(8R6!lr5tB z3K-yPVYLVwnY1_T%JK{=j+fpWU;7tk8)kqJWTMQg@lJ^y5m#dfp&zluLj^B2&8Vr8 zC|#mSf-kwE)2sy2u<$X>Pg&`nD<HDfPn3U5gXwr+2`8fMTN9}v3UK8<=9@24kG@tT z&@rikNbsh5v~Cds`zE%6I>(^<YL2|;6H-7hpM7l<%L2(+?e<%&aDh+)#ZiC|3-*1r zV-Sa_u4uyqml|Ktx4l!)QBs4h#4Ey0mFOJtdB;{F<Nbo8Tz5oEjat7AWX*#i7vLky zjhpSN*Kl>EWE9Mv?|P@q_>G;3b2%;N;>06ie-{#3@EgqIUJq9PePY!Y%a1@Yi)+Ez z2-O7sN?cQls&T+KXbQh%%Aa`aH>fv09)bLM4qI_k1oGsiGPrjn3Nv-?-I<$}==j#m zvE)i94Duyd8wtmHm+|#T)DvCAugU8??y?+SMR+)Q72<j1bq23n4%)N@cqe(~;nU=G z3g0KM6S#%Ee#1Rhz$*v)tw7z;F+6Vt`p8=Acdq|2SPCA&=_`QDVSJoK9>llF*H5^C zybfU3D0pRKKl0jxL*eDVo0;VdE{x<`L}E`^`vwHSU5(%2!hHwmY>x~(0c%?=-UEQ2 z?__4xf{T$?1_Cw;z!awgE<^XPum25zGMQO>fz01*>|#RX<OGP-9w|5is=nKpS!obk zP)TsZAU5y#Ac)l*nGCV5pdK3y%wp_EUTJt<G^j4HRobHhd76qBoRICT28Z{*se%le z`j%HsM(zClSR0L;`P%!taBej6>8oA0_w5N+l&AkIu4(Kowb1z!=sd&Xm1wyLdJX=N zq9b_2N|+Q9U~(2s`Vt5|L~gmJ6`ALGTV^58^a$sjOlKkQ<Tp+?Z=&H_n`h7g;3dVR zED3{DI||vl|1SzU)$pwr3VZ}=U;Do$iWsMl;CtVq?`OBCSGoN-20IIsyn{7wqH4Q9 z2)8g4)v=xlJpPab1S1taLJj7YfIq?=9YFY7kK6lYA$ymFdHCLUXsY4@yAat-765Q6 z(owGNjoeT6=DX_!0xTpANPQ=B_AigP-{F7#@>srA)B#Pv39vZ&Ex`sS!uz<5bNAv6 z(?`%A;01HtGoqpl`lxVC!yM-K)O;v1GtG5DLJ=QQJC5TF_oidr_o$z{+Y#pn$&nkD zFv4LJ9d4xD^o~z5LnSJ*EG)m{`pT%if?K_jGw!eoIVj>`&2~a`J99~T<c5XGUmXPp z7zURcZ^*owOsS8j!Q7Mz(S33Ci##lXEW~FQoqWXosw_JB*fFOK(nytNkNSI64xD2t zcRnTt|GO95p1$&w<Tw$FZVeHOVTb4`8(%@<T?z)@o*s6LII-Eq>gYh#08KT?F{mzs z_WoeCpqE)^mepwWe<jHwgHrtW_}VJeU&MX{t;E9BXyk_>+q@I3pJaZ;Wt9GdWb|bP zw+5f6;=BZz$g_Bhmf`5tXhMJZh|RA22CHFStq!lDgJKMr2<4RMpY#gW#}Uz;`<2A1 z7vra^Q8!1321T)|7k^0zpr7a^?)(E9G!?jpsro|v3t5-Nfu7mAd~&G=+$tdXHESNp z6~sgM=>k|Cz~W%+V&2!?RQ0X97n5Z*AKWiA&&vEm0<{2~HCEznKcEjrkram1JzB*Y z<nerNw(9^+_*ilQp98rZE=j?hWPdO_5TFW_KxSaq`vK;yLBm9Z#8A;aJa!Fow`H-) zrx{Cn{EQ>lfI2qhAGEfn;GJur<6<jG;T~gEi>7S_MzkmG3&{B#Ov)y*fVM0ky~n7z ze-C$9i(GkdRMKxP3bAE(Lxg;z^6*x?buC)N1ynQ`m8q%NY8{FYar-4Tc-=Z!&9EO8 zRXKasp%}z{h2PHUr$hq~&n+!yUJUva@iiS@<mlI<5%6ok9sDvDjo@ph+`#?fP%K~b z@>0&vacC;yhn8kzff^dSro&D=RE<7@kdryf)Mz_$)(l8fO^*E_?oHY;L!5mTocbzH zx8fcfkb)mN`gM+W1NsCAggn466VP||p`&3Zo4yPyC*``kcts-mfUj(MhP8=kHm^7Z zHzcCVeC7N*_(Br;oF58WC4G|7A`j*LOR79Lb85Jvo~FJ}=2BI8Oh7%je`+8`Ee6t% z<^}vqG8)7W&3lQTC!>`gg_ah`=g>CIvkcv9o8Tm>aVED_8tRaVJYcNsUTZ~v65E%A zd#bL0W?AYsyd?!KvRAeooyctgE7u*vRvSSvP}u{ry%7~S1iPK^PJn}qPOvo-ng>!( zMdc8ifvZx{UVd=MQ=G1W6B6SNd|HDx^BkVxX`9gN9;0_TlXI=65uyxA{<M;}zEg~5 zB4G#2g={q9@Xct#u+Y}CeG6l~;~M4Z-ZYR`F1um6Z_!9bA=q>b%}Vc1$TZ3;XYq-h z9YC(+?mTwFO`8#18%)Bk44TRhZoP$<F({y0u;NelIDtn}H{2ApkOMmqU-0Pt_zHst zj#xz6TTw8ms+VUAc+O~(lVlKu+0aFE$%v40QIbtY7}0IwlHU1@4b8>IPs78vAP*CP ziT_GqdOHHw-XSmvZ`*?U5(4}o@#DID9OESqs}ro3#4ya(A(Vs-7fl%qemLzf;(fs9 zulp{NFXuBjoO}fa;+8FF(I_1>p2VkTVsmV$8$~e_Bwqu;;a?Avd;{+h&pzHTegr)T z86%f%i}kWG_xGl-0U3CA8u~<$0rj!0rEprt^1s_F4e@^PfbK&QM!FYp<rRDgMkrPl zI`Ya6cfF26=kJZ*Rre`$utZ0jD49zO)|#%xndH<DL{nkD22hE(P%oJdYd*#xSvcDN z1uxl(1`cFDD)l^#My^`)Gd_;DoO!#C<`64;Z!w_-9l}?(A|DYeiwX}cz-X8oE3`^i zR+!(_@rH=3{GCLSe3B4!t9$W0SUxzhKt<h;0|%sRokXJDcr!-3`}v*F#krJoxmDE} z{qNVjJw4%ic#e(iPB|TRp|RFm^~jDN6%~f{ImiX;kHD7I!5rk;$!2O~*s{;YPLO`< zv2cLLLK|{#IcKCE<s(7(cj3_lT$=sn2%`IrjfjYrjvq5^x^!IFmn#=Zr>|JNXi?aT zD5-R~)MKGE+Vlyx0$=@#S`~|zEs}n<U`4ca8K5nQidwvASr|Y?NTa`693@>Cv2?-W zWh6C^sNpP}$HL(d8@*tOg_1NPTpGP9GEC~RWF;iAWF^ZSfuFJLn`IH-E#qSQL@!(r zy<p&QsqA}?CErV@0|r1VmMrYc!m|m4EnD#Ak}xx6j#ku{i$fNE8NDJTDlB?sWJuIk zOT(6u+*`tzty~&Hazz5+9}8>BC5OX>Tj04wOenAc6Zch1Yc%|Q34cA{k9?EAn~Pdn zOW=>Z$=@0Xy9a;Y!{2H6Bj4mt4}ZJiZwvgX;g3yDNuCJ)T3ab4{K3C~Z^M>FlOkBW z4E}$+V9DZz(x`6H3!=lM3!<fA-!G0PQ1Gw0vb9yw+S-aA=OF*CyT5H|&4<62@F)GQ zrFAa+3E=n4Z}He%6o{q2At~18BBf$U#Day=@C7TEL_<j}Teu`_1$-@D61I>dOGQOT ztO)x*0HRkch?K4fTex^dm^}I|)OJM=Fr*52L`nOEtymGU0y1POz@F67Xvh^Lwp6}g zWfaWc-$le$$C0$((&)v@q9Cs<^oQ_{0vW7~qLzk5$_b}YksQA(7El!KeH^`+NEqW1 z9R-y~HgNEg@3}eyfV?#=t>mwZ={*<TE8x!?{>GZZe*S@j`S}Mt{{-qKlt=u(;?6(F z$vVH|PnHOgA8UXWB61i}FxrKSh?p|gMpU|Gl*=vXZJ8UBWp^Q~3!B?rAvo4VYrPy& zno=YIIl5A`k)uw^aO_qY%uvc?7|TUUF>=VI9Ak5gwUH|B*uCApzt8vc*-bv0o4N51 zpP4sLp4aEs_t*36ljPZD{hIc*iLGy27F^fT+0i<0HO`rN>MZC^yz{o@_Qb&3mU!g8 zokPRRpa-GV&<~-<ARqK@a%gx9_Af$F=xL}2S`P)GgS&=?4?wR&z0fXbJG2#wLC-_a zKpUVC^aRujJq|TNjk|^-!@Cj){MHotHjATn<Xh{x?s!BE{ll1xrH~g|3@wD_LoR4G zG!vQz*`e`}6&k|)4?tI-kD-sC0(1^K1D%4h&@t!;bQnrQ`=Nc%9w@mJ^S=!*c4MLK zfa1_*=mqFmC=7K$hoCp16tox0KyN`OpaqDphvq^~Xa-aV)j~F?1{#FlRj34AhAu%D zpnMYZe;O|)zA`jC7BZkO(Yw!}PoN@n5jqc@g>ukI=s0u~>W4fiQ2#P6S=-SF?48gI zs1B-yY)}m}2>Vs21YL$MK^GD?yo0xVC-5=@y#*bD-h@)nUT8P81ByeNp%<WMp)k~h z(yh?r&@<2mC<HwLeS~hkj(sn*3)&8Cg<{b2(7R~*2`HmZ!ng3^5cDRLg7!kYp&d{h z+6=t_Jqv}QE~o=Kgw^xSg-Q74Nxnf2brOD?4TyY`;%hJs0caJp5?T&5Kue%|p+%4z zx&yiunguzaDbPe{teSrVFTTX0_ze04Dnb{b^Uzr+2c3kDLr0;0=pb|eI*f&uhW11I zpgm9$+6HZbPC;4d7<2?W2c3Zm(8Gvdx)by7#f!zzLTEnZf@VWAp=ppE8V^~aAp{OU zSD=rfxj1Q^&<v;!s)cM&4K#?3U4=@}W#|%g0m?(Cp+@9eItOQG4^Ag9_KTr~(0s@R z&4y+|(_T)TIA)o>r~ton&>83yl!cB#N1(${8rl!-gZ4m4XxsM0*yENBk!gRAGYC2h z4MMZ`A#N`^0d2?YJ<xvW5OfyW0Bwd!ucOn@lz+gL23iWOf(l@(_v7a>^dhti+6!f% zi_qCO;P*2O$^lIOfuVbbSK>tn^a8XS`WQw7`vIu-AF)25MbLv#Ba{dK=s@Diam(h& z%EMR|KiAq-_FA!Wn>yZ0c3+v_e!SiX&HWvEzPc^)l|Yqn&({Q$vr}bumHg`(P+nBM zR`+EV`}5{~E%r`ze><nU@97hkACG;cRo{bmuWRY<YG2RyoCc0t+dA6#?%e3^7WVFz zmNl?;Z)k4CEu*=A%18{oYkBV)+>qfG@W{Ve_T99*W_UONorW$!>bL-{gfcb5eUVd^ zO=G5acZPy%JL|jGE^segFfX`qUFZ6F>sp>leDR*;tvRmY;a=zvRG@tt`*Iy}^_@L! zSz<{veQ5C}9A_;5#rKN;ea7;EEwt__H3UZA(f_dg%5rUc=X&$d=-c|f<)kG-)mt{) z>2bSPqbs^K>iEtD!I5LtI=35<WvzANKG^69wl%lb_XO{}dx0_=EzRqjo7dMr*|xs5 zYg13#yeAjjWpdmvcdcpP&{jWk+d8iu<DH0nV7a}vdsBBy7sj)tqqF77?!<`?ESsl? z=Y={q;w@l(b2xP8-R?zZ7Ifo#=PYkn60RebiIeYN-`dvg^gVLF^MMtO&N;V)6QBPQ z9}=r(*mn-&0yB&Y%rGu6!^6?vQv>k70(}g91Qnoj&>83yl!cB#N1(${8rl!-OX6M7 z9=!0O@G|H@=sqX|_WQ_#m<3QhG#7G0GoU)C7P3J#&>-?&g-Xz6=u&dgFm8s1hw)~r zt=k`3*X-Ze)!x(Q$2(3nFvckF!px0eBN081KVCGy7iFNqze5)2D6|K97J3}I51IuH z?!kCK{m>3*&8z4tG#fIY3&?Q<+69H73t$;&FZ2R52=yS&3wdB)1)*_?+dj1188Ozh z_V~Nn@ZQ<q+J!aK)opY%clY?!n;o2`ZDG9HfX@K@ST)T(EkW~bwKDqK!aZhiPy4#I zu94ucr1N*TKiy`8I+}adbat)tccMgBduyA2U1#f49c__rZTI_|+wQvC?WgzNEuF!( zuD10poBSJ_yVj{?Y?f+Y(Bf}yZ4UK_f@?$VouUP6*ENS2=@RPweOpU&M~7-Tl?_Hl zy4lmI``qg9QJ*oO#p|eO*N<9)?IKyV{&bg*PY7?5+mH|MPOCLPqTANRJ?v^LZ@7*M z;qwie8ozl7@b`4A_OI!9sypak(;miW9;yObZ@#5BW4hbA+M7E@!a^PGE!`W9(K$tD z^?XKx)Lk`il~KB7idW3=C~hBZ*eEQY3QhffbL7Kiqu~$o;)%3Zj;=X;qqAh5MTRjM z`<tN~>39P6)1mJ`4(NJl9CRIY6LbxvJ^+{kT?^sQ!<*YA+N)iy`5NB#GN@k8f~=4e znwog7#yWLU9bT%<22D=<(**0(M`xNq|1$$Sd?RMuP{yX=wQ9C1t+J_5ys#`aq88)T zhidVoZ07{)&5M@QM5h?G*!QhV;F);uL(9~NN~Kz_dN~nNEmf&hO4VDnsZy$;PAEF& zt<%0$wOdv-K6>&uZ{Bhte#85(E&s*ILy!Gg_CKGS>p%bfNATxeKKjYv?(@;rKec>1 zW%j_`_doqk$Cs~sar<$n<ClNld&C}0ZTjgmyDh)IYwh4)&U+xR_b20iy<+z>AAY)Q z?uD0@<u+gc<eYHqyF2r{pU6-6;*Z~ce)oz^&9B|H_x<*vy2Q<|T4qM@mjKa_%I!WJ zPDa1oQ&s;*$N9I!J?6MzP-u2$Vz2yd<FvmyV(@Js9u@pWGpEHUgbee?5DxX0!A>Pg z&(eu_%-$m&5Q!c~0XsTS{7lF5zZ<ErV`gQZSI}3L-}fEv7}fVt0hcIn7bOY_KGpP} zWCR!JMEYODQ1?P+ff`MCB-BVLZWnoQ71xH6T?cq+HtwdDddCxopW6O{c#sP8Qso%& z+>MomU!uariJFIX8sj(Es|G~^qNUT#M3aoPUkA82fOv>{Y@@;{O5~;~<8yK*H&pca zd&G;n!x6MWZ)?a976Cs4S99!8mx;0sSP!*Bx5P`fUr-)<o#qY1FPeOkVGC%k*H+)$ zS1ny*BY7%1hMZmMti@)hZZh`}cL-juu4&j@g72pl>=pcVb^XKEpn1gL0?Ww|6#?}g z9S{?okK<K%zu<8ij!Xr|D%?wkVuisl_B86kCBe_F(0oAfUBm;|>S5-Nh3PmXxcwgZ zsk49`IAFt*x<FI}G!l;qKAw17@EuR+JW0W)wQJrBZe9hr@V#W{7Xee$=ik^eg5N;} zvVs>WPfqXxH8elU&HBbM3Mh;k%z{T3skLqxMZrVFOM?6UMEeg2KA*Tj*A(vX>ULe< zunEIfYLH!U@8@QF42R&eh&u((2erRT@T<hV5n-@7>D(3EbF=0?!HdM31P{;F{sF;b zw`d+Dj-P^hYr0h%LLwka$6>)6=(5Uo&f}h-^Q49U;!a&BSIHwr@JHH^uQV9%61P)A zE-)*k{T+gDrUKjm_Ah;?*F;MAFE;aI{?o#6nF?eCH>g2b!5tTL0S}D`7e3mh8|oFj zfw(z>YW;CQKmVi>0h_yZqPXDolqe~<L7ek(euEmCFY`CoKZ8P<L6v0AKSDWCV3MBW zlJHLw9}ql8+@L+@vDI4j`DYUbZ>=t17d%YdA$Xd&Q}7~jm*CFpbbj}wh<?E>Y9fP2 z1jLDZ1<w+15Zs`f8lT`E;!VtT{|zHVhA12Klp6<QH1~pUC*CjkDdJhdFB2~b9yzm_ zD$!8!NE{~a5`4!?+TSC1iTneCKe0vohXg;U^F)lKFuX_sDZx(?&k5f6SGqt!@Y%#| zG)3I8g<G}1!{iu$Hf*2(uL$^v0(^q|h=&E=@YlLPOz?@s)8$;v|86p5$_?QC#EXKL zhz|(9I<5;lX)STbg4;BA3w|HCx&E4jA^o>HASif0C5j6k-LCz61wT!B(t=<8XYJo_ zr}Lj%`Y;(XA|OmWC-@oSdBL|6F9?2wINgF{>goK;(A%g15s>*rU)ybyE3Xq-;ts)c z#9e|Hh<gMtMaa+~3<JcQ1ULRiHz+8$jd)maJMoy{PU4ZIFu2K(65K<)UvMw+tl$mA z^Md<`7d0Qf{|}I1Km>${+on`bL6o>d@ECEI;Bn&a3Xb^?UD2!2Q(+k8-YUF7@O_lW zCphPC`Z})GU*PKu;K3?9RE3AD@Mskt12@+{pMKoZc$I);72aEgr>gMuDA(s77w#W5 zm~+BBQ-x=%@LUz1{|eXjjlx$9dQB9o@KO~%P=y=xd6FDyTcnD?F8ClVE{EVeLe47O zRfY2hTqF1YUUKw^fE*pOeWVA(8-#z|RXuV(!Oy;?FI<629x)gMD-9z&BzVK8I#F2g zJn^XDO~hk@Yk$mtTo{5BkQ6*iyjSoj@s!~G#M6TJ63;2F)*laPi5&AH;N%y2NDG1= zPwA21IL=d~0{l4;^CtRS$U%F>&Gnxm;4Cw!l6g9I32wVyUy9v=dx(1k4-)qZp1PiX z{x=9io&tP=yQb;F+!O9uko*F|-}pa0a%^Y+(iK`mb>jZt_+M>^i9~kdDZ%^cI4$@R zD$I5+?4mq*_NVKQQ6x}cgKoI-0wrQQC-P7to(kp;8aX@dnSV)n*v>rCK#6$n*wDap zCKBB{s9VN%_76~=pzsgVaY*oIb)Ja94xA`NiDDukMm!~WKOLt9&rn0z&iRv+CvW;= z{Mk^X1O<`Esy_V3#&-5kQKA9ipC)dj#mfE};=G7WuIArEj&2dqNQK$XiSm>vEc~~S ze@yTr#M6TB8=~_+BMdnh)K(O{by#!f^vYYY?etBGTktILCc&3j^jQ|1F7E$3$PgC+ zjv5`%D|iF(tl-a&(f)bCKO=6?C+8#g|3Ae4-lsPEh(Y%>PTVWFF;@FG2!4pTPw*Ta zHwhk(+@OatAPiCBLBR{eLxTHe=saP;GsL5UN9o&=NK6<)WJn8s_FCQJe!*9b*PMIE zi?l#~dEsBAJY*le|95|;6FI4eoan?v&0T_ri3bGlB_0&KkvNa6&X4o2mmFgv;3V}d zE_jBHlY+B9=i?5<sAC+*JgK-k{|h2fiH?hcU!fkbofGlT0yoVy^OckI$aw^Rnz&js zs{Y6oFhGt?BH%m)u$>E}sAs*x|A|^%I3@Tq#CcI7k6GWykz-Z_^ilxZxp0OG+i9^f zUp!eCb_o87U2~t{(<Vo>p-C9B6u@T#7l@NzSomM0hVp5}{%P`yRq>BUgu(W?ZV(4@ zA_wuD@XymR@7cddydeBH*XjIhAG!YzQ2`r$am<PM*j|P67tQSNCO@a}57Du!l1Gd* z0S@FuKH{Fr0OQg<x(8mt&l2Z2&XcE(g@k|HJjVJ93&T>6E)W%b{}Ro0;OJRnT-7`+ z{Bv~NFZjhj)czU8)%xd_9v}k;GPhHKf=J{dUJ~3x+@SCJIggLHt&E%J-!>}jEi<U= zg43&8$`1{2q5%0d3I75evz`5;#Dl{B)&`3{|3kts?O$|Z4&+4q-Y+KnGt{%V;QZb% zDLDK0en;H@^LxKkm4LM1aVne<JVm@9xIxbf6q)P(8^*x~-QyA))c9kI|EuO4$Rm-Z z0!~WEoR3{qI6thw{t+)ZdPG2oj@ixuKH?3+f6FpjD}ooOp`4HNM71C8|3$#+<uvz# zmnczOaN8H!KP`Bcj{5~SZqj#38IxoDxkVl_a3J#nCCG_Hg+^UCFZjU~nsXfI2~b11 z0VY@T@1Vjq+H(LGunYd#qq+dcv45Bfa7UQ?DBdgbsQl*oYY+ze4|D+z<U}zl5D@+; z;!(l*I97!hZxZ+a%l}3fE{Oma@d3f_BhKHNb3?=QExMcb%+uuGG{Wip+e!uw<bd8m z-Js}5fO%FLF?wnyCU~0s<ASq)Qt(WK0(ymk15$$L$UiMO`}Yf8ApeZuN$Nl(D-0zH z$O#^&fTG}8dH|s$_^EBQNXgD4dYVSssrl&r|Jdz1fS+{WfD}LBAQD}rL_xv#P~oKD zPU4(T=g0i#eya;}Pq=V_cu^#3qDL$2v}ga;m#G7SFDK4%?C%<azgmC%tOgs3lprP& zy-tbP&i)>i2-|@0Zy;`?XFk}!iMRvYT>oqck)cTh9HPPj!7uF4g~Nh-=vk7e;6=*A z522v0KK};DF)0GhQ9!@oTc|L{aR>6;V`>2NBKKHuW0>y$xy2md9M(PKr(&3Axo0BL z(xmo}3GO1E5}Y4;Nk@c%pL)p%?%ksca09qO0cxmyj{d2}Jho5!I|RQ<+(~@o{{Ije zd?LU`A2K%y-bg$k_$=Zv!Rz<y1|=&wzW;wi8#0v!V=VEk;B~}}xs^SB?3m7D6Z|l7 zx8S>uD}Ss%k1*8HEmzZRl?CGCbb)~2KKg_sC^$bM8xp)k-!X?3SL>hW%uS!~M@4{Z zoNh==a9@q)al!F$vAHD$chikZZy7hwKfZNJl^Ik9e&{$YITh{~oS%-)2+mK`W(5z9 zqo4mdVerzkV0pp$d;5am{N!v=aDG;}Bsjl|9=I)1ImbS_{We^coFBop3C>@!*aaVX z)K+kQhS<qmU;psI<2YT=#RffvBM+9l1?R`IJ%aNC=U&12Iq?R;Z{+vyK4HK!Uq7B~ ztLd9;8}sP#pL?wfYp(aAO*U$mJ{9oW3+h?=TN0@nYpBotss(>$EB7J(yvNKj)_RLg z*`_A~W2_!aP2#yR*6DS4CfnS2$q!>o92#TYlE{s*-a3B8iiiAvx_sF~53E$7ixXa} zHE6k}?+vT<SC$0szo+$W9cx`(lRza$AD+u~*Gycw^ao2-O0`!(3lpvh*5Jg(l}jE} zSnU<ApYYHM{d_L=3j7iNXGg0ok(+>jkk0k8*J@9ctT@8+fT`y4-?~tF)bBHtZ`YL{ z;I&;GPZq~h#PQTVwBa6WU(}9Hs0P^j@U8fBmTUEI`KjOUtDR|Gp1?<Ijy`<P7PTs; zcppBNwOQ25K%)N!YrYTmkk#zUl79N7gwKbDJ5{yVHF~twFP5myQKOI5uWG0*JXRl% z*N4Wgn#ARstlyn%*O+HI`v-|ToYwg@|87yk_ShKjT&rsPHI|os7_7|AR>%JX>>Jsl delta 59449 zcmZ@h30xD$*PCNEghQhN0)h%gK(K16C?2Ts2nh8+KkrsWSH!bo(OR`O#IRsE6dW)h zsA%Po9%500Rt0J))p}6tQEjVeK-6l36$BLId$XGa(f<AYLN@b`dGqGInKv_U$L1=@ z@8=|vAwrTUjwOj9Vu~;k%9*JW*_>XaNM<7>q&5%Rm`Q{Bx$tNw8%oa?5@_2GZ88Bt zNCoF!$%~c{Tp7Pj`yHPs7W|)5)&;1#ba^RPd_m~%^8kN*i%h@={0W=r*wl~uEbo@{ z&DUm5FUSd)M^{22&?rp=p^}XRYTM8T{Yt1Q;leYy!j|x~HBOn|F0+L?ZT;Slmdsbk z04lY8{7koU2yZ^`P4SC$6fwkBgqsnZTIDNHmMuwbmCJ`}SBXf$>R?j4I-Zm*0c!pa zkBZ|H1!H!}9DuLG(}!q0&(GiIL9yr`rCf2Jko*5j=tUqzd|gfEPWkYE33=c_o?Nj@ z@Md);qHGMx;q~T?MrH6f5LNS>XZUmj20|GSOGUvxa)0~`b%^3+^5K@}e7QXEzEG~< zQ=Qqx443;Kq=t|}qL>j9LntxP3mH{N$#>?L4MubMn?_Y(Wyux)N02$X@iGBaB9LDZ z3=t5;=LCGq$B34^#tJQSkCrGEW>K7xy`aC{ye%S50ZV>H;|1~|WVB?CBHD}aHxbo7 zJqx4+LFj=i(h9n2?@u7LMgdu@Emjt5|Bo-DaczQgs6g<6=yacdq?&F2pqGM~HbY1r zVN#?xqLspJE?p%A!5gO;#~ZI1&zqo`z?-O<s0rc)qd=-(uV^An(pbtP470|EWHF8Y zHcO1M<F9opn`Opxxv$X|l&ktHdm|@s<X4U>9nH@^Upankd9EvT{KU~H4t88d|NMa9 zaD56LKXo*2i^1Z{KzmY>%(q$m=5Hpi^KbI4-%Kus6U0P<03o}ttzUE(c(MQ1dgQFd zjMsV)Yq;;SDDGRiVb;2X_u^{3+d^BWHqLl9`|12g3u|M<Xs&3Kx|c&R&D0UeS4=rY z8|j_mURr~g%BD4y-pn@rh;l|H8;S+>$~1+=jPCh}g+|h`qF&qdG>`1{$RH)dNR!gz z(;*{s2s&BZ17l>+G+)%io8jwe8TlSZlX-5NetMgJ1oWCNBo)edU?Y6>t-5GGrGkk> z@$oUyIl&69x{4H2SlKjh#^gcxW{F6!%X$(0NmE}ATD!v(mJ?=Sk!d0$!ER%4uRZrs z`nT+C`@;fjylLi_2>e1>etoU{HZ{4+)NJc&b-?h0Xh|s8nDQCV%jA*c34f!)yX7&a zqMRV(1oEHMV4Bz13$v*14KPA67b+nHBXvS!tmuirpd*Zg=*b0r2F(k~V@OekF%)QH z<{O>J$F$g%&<5C2*{d{_+w?|v@}Wir(3q6DYNNCHhewXkC^0`1VILcNkPlcSqJECi z(fq6+`WkzYHEg*@*hHfXSz~3rmSx?WypI`sL;?MMEeMszgfdttERd;)2$qrx=&tY& zad)MjF{-=U-p`ISri&hYbKEGjeE|LKh5I{VGbpT=(T%JLWpEo!9v+UyZsZ-@CZ!{^ z;7`oh6jikz8t|v5%H*MR)BEDSJs&MEI!qYt$lJKI+EsoGehJCjSX(4$1`89)>c>=6 zHc4%ik-yWn7WL>VNfjulCwYTm6;u=i@EcH3c-W`L?&ea7;uF8P>E-(4Fm5vu_hh*| z5V&@=DLiVnrdWD2QrShM_Xbhoyw{xCow9@sE|xVBsIsa3+0N|y;xp(*q;dx(^MPiH zMCw(;^o}$cR4jWzT;J(#oK=ilSp6~ljfrwIx{{aiQ}~C_1((?t7b!n6y4qfb)=-a* zYXfu?lbCGOg9wPJSjLG`vCI@+HHCX7BTeCwDQsg1#dN<o@g+`d@&*T2GaCWn15-|z zl0ym`(ScO)o0%ctR82DFNXD=@ifTaz4)ZIAgZZQcnR2Q|u{a(@eL8SdS2!FM;Dk*y z<)n<@I<J1BpIe7SmpT|p#)CisDBR9T%_>=S;SC03fq_5xOW+$7{A`AQ1{I73_+YRZ zO1ilRK}&2sv|vB9##V`#1fm14YNXUo_*_a6HnW-AjESh4berLz5x}K>xHi|8l;x^% z-$rbLf&ddCkry(w4LOoSBR4~fhnYO0y0WFi1tUrRj5u=z0ITwc&s2bs<S;YiEnp)< zz<_%~so2WrbLfG=CP|8tEgjC`=0a(aLZeoZV8vxV0HR1Uh};SUgET50Ks>wwLXZk` z1ptW}09HR1O6^Gxu9W-&@ZU2jJXW)%z04IrX_23HsMxHNTs8dm)ehAWGSE4UxSGQn zjTaO~^#ddxR~WGke*0(x#Jmo~T<zXuj0&ywf>JYPMJ)X8D;O!}n{uM&6ed82PWMZc z&MzdujwbW`q;ql!H2W(;dZUow`CMf*-aK!vj$ExNT;XYwge~_=FsUW=k<2pPJ~zsi zU?R*2@qp0Vq^#Nm2+FX{ZWKvmk+x>JRS94NJ-IeAlc0;-IWNhq<ESeVOi{!P^EfN1 z3Wc#WJY^)<2<4Z6UV_E!w(V|GRy+G8($jOgsPh<(42J0NdKn@U%<Zd9klG5LgUXz- z357CO!A+H_t7dF65mfGkA@earsdTnLA^;L4?Vt&tW`R@yq^cWscY?|yNv=6+G9n2G zB)Hh4qnIR<p@OcsRQX1!*A5WKfgLdjBqRA6l>Cb{zNx)sCu93psh%ArB0Ret=bkD& zt``@YZ5|u5#MYFfR0DNX%I8?8H=r@B{e#NF!)#<Hz$_>#S<kv8lZ+7+8*pVrw+>e% zQ#gGwAhv`AhMQr^lBD=&*^&^!N(4+WH^4JJ<RvWxbl*Hg5;j~}g{vv7YM>inBD;E# ztUrv9YP&=i`rZe2G!x330bUv&;YRf!_|Cjtm~1{{3QL)U37(l-mn&^N$+eD@hM1os zx-;gdif%`ba^icL+wC>n6rQsgL094~;i+yw9Yr?Ka&}Nx4F~%#`wU#|s99wW-<Y4g z91feG7<=ZVdD3z)DHYS!*6&Z@b?fsmw3>S2T|-D@FfB>NzP8{sgy!%2;p3McT*_a; zQ;`r!6~>w*^?md*A)z(IJ7!Pdc^RwoU$YwX#2qNLyMB|$f%AJJN6CBRLj+*X|H((< zZbo!?lTT3(mXDQ9r8C+l8KRbiIdj-`B8*Kp<bz*^&!_Fge2|mMj!uPe3CmHo0f!vQ zN${7b4#$7X*02~G%@{c;^D&+>8sn9zFh=-7{8zc^l3`r8J~f0*boe;`K1;{(w#C5j zY6?myMrF!Te1d|yjT1}i#n=aPHldVmazRemAatUer$ACKM7O&Q=$Rw&TX0=4LfFVm z#ywuFe~;(Q$Q2DQOko~dQQz);+<og%W&kWj8X7KS%G`v6V3eIn58vAF6I;-d?gMz^ zP)c{XAgoG)PIQlS4O7?_CL7xdo3#P4jR$_gOhMW(Vl^5j9m5Z~hCY>s2y#-opdX|G z0!i5t)GB?S=Ys}1y7Oy)LX#b*^6Sb`HvFP~K&Kqv<1Z^k4;{US9X_Q!Cib|RW*Jyw z!u)hSRVE4H0#T4hYGw9DO(oAa6oT5WL>Q^TdQz5!qlr#|{P<$D&1o>dvI?DWQaI;c zGAjzy#5=EtFacIqU!hi~fn6T{Oj9=YvL*pU4nBowxbw&!)xk13n3ur#rCjl)hu`+m z=k<*Ohy`kIqa<fPzSlui;_S`$xrM5oSGbnuU+dfrdSC^HcMUC(Dfp4UqBNNYKllnd zA@k<{bP?T=E%uTqXMnv4kDk*d9yq7ZgD@j*S+fkjrPQ+K%M7$4Jcfssy7YHt2NzYN zNfJK(U=)A-{iWkq99*FS8%L*HR*i}(>cM-28>2Pk?@7LLdduElPL}C{x6YE<dJLv_ zH}IL@m$Cy8b3s40LRcs3`IN)1)T40ML7q_($4$Q?GPZRpW2ztfuXbo`D+~vUssU!K zU8u;lulvdrZGHklf7aNA3@MDIg<f43G6NI7)0v{8_^94BL=Y7vMu9!X3KUTu=!+h% zt_or?4%x+#Ejxd0r${u*iGVRh5!MqG^>7j>s@%}eJ-qoBzCiUo+yqf&Ls1vEyJ~i5 zAj?f*6928UjvET&@xoB%4BbF^uW|XrakigcFkD2?|K7CWbwj&F{5}u8Yp5sEn-6$| zWW0Xvgl}WpW@XJ5@uZC}2-?9QEMJDUzi2<B8nRIaI|#>xZe;%J1I~1Po{;9t1TI0D z`=xpjAI-VR8<Qazs<3g`Ud>Z+0|_2PT$b=uTW`9k*W9!Z5_s7V-!ld!U;nIh!z}!P z4k&W(#V@^ws@(hV)pwDkr)*VJ%mVRK^RTl(Q+b+o=7ZsU)=BZVI09SY&~<$tU2VSq zkb3G}8+z|@>y3un^>>~;sCiudaVre^33C5q&BKzOdlUY8S2p&ZD=^>zB(`~&zHPQ< zmtcg5Km$D{F46I4=;`%M&S;}IL8pnFk!@@<+UQJx_X%fXcW#ykf=eq?CS*CVt1SUN z#ULFQiA3?Qd47#E+ddjSMQYd7E+5|T0_Fz6;esI>BY89t>7We|Hu(n7&FIfwJ^3YZ z=vA*?cFL-`m<|2&_ay4udtk5oU^9z~-Qs!@g|S?XCZ%L1Mlt^R{VTMvcR%&_%n7A5 z9R}Pv*Lk7(rK@~@5Dce;a_?&|XnPy}@MR9aKt;`|R|H;SVo4l71KLSwb9IE)`^Oi< zBQZ6Gc*pVeg|Pz+jWZX+$HDN|%y9;+!^^F=>p2}>ZNAZP=gIwt79AGq<blUt4DOl8 zD$x;?82jV!j3`iHQzsRMs6TCTK^yxMbcV<UwAW;5M^s<j4kW4<&=m%mbfX(l9w{GQ z^WuO^DvE8^_%?pDb^hqdx)0?3H81e6nLTq<;%J_mgB?L{Y_ma5c6@ZRmUlt_=Y*{5 zBHkGtofIsh;~6__)35;T&8X<Va)r@e!-&Dz6XpJgn(6gz5}JQTk~QbmxU8G`JV8M0 zi|^wjd0DXV*&#Zm-%d+EdnFh$i{z2Ih<oTg&)!~>>FZzda7Uf_8+U?5^!gzd(T%qZ zjVLGI(S}-cf9o<PZ#ByH^izMOEY|0<3#X?$<^C=9n#t1*$aw&9VYmEX5HCygNBgrN z<3BH6XjE>b-C&a<XV@7-cKE&DPOrLt7x%{Z<oC*L`og3I*N+++KROPdRKvZGG={%E zVhron^?4ThXlm`9hJR|PM(?LH?%im;-F(0P!IQ@i3D?5pfx7Q}6Hn-{CUMWmdVqJ^ z`Z)^%Z24_-G-#M!I}$wnZ-WcclYBJXV}v>~f&a0dj%sp-B@KaYih1DG%!_t;;ExSw z62-woww@qpO`Sizv`Ip1MJ`!$dJ@Tr$w6*&MiVr1vwLPReZQ_dYH}m!ZKCl^FzzcP z6GI56nW(Fi&J**Ikl;g!pd4xH%KJhI4{dDes;N5w&pJX4KL|}-D*Ol`(8dw)tb=D? zLb}P7prvAh>HAL_()T59=zpKkHix!vf`CN$Leuxz6#;yo38r0|gs7=Mp}C^<Ng%$D zZ%|qWof~?2iO3$k`hcM0?D*x8Z8EJ_0^z0;5;VUpPypCkLjw9u-VM#}<DniMOz)}V zyNSMmA?#E<-SB`<cR52yal^WZ`kY}K#xr~y4_hjNGq8fSC1|~9aO2Df1IW`z|0EhH z7%FVM11`JsPZ-0elJ9|qHJT+qhh;v~j^(70=shfxal73MUJ%#-Yz3O?yQ*s#!{K1# zqx@G`mvV*hg>jGQq1<2iBHD-W*G4A57G=!~`gvPFCYavd;-?Fy_q7a!aiu{pG`1;O z1&e`o<9@}@u*ir2oE!PXb01<b%u6poF+AP}Ei(_?MfSqxt&_Jd7@Z@iizn!(kB9&f zA4Ga2RA;Oe3==k`ty;H}PI|xx?eIvuED^X{bDa&emM4{cNSx9O>VgTQ@lmUBLqHp5 zVO$;1hFKUjk6N{LB4UW_B=6(6mYw&qz+3ps*dfxSBt9!lrni1gtT9MAKLUtJne!<G zG^@XEM*0E7jRD}v#$EaC<`&F@k>rJ2yaT}m72m8Qm_+)wJmT4Jr((+uEpv=Il;Aa5 z5H4ASE_nH<!=pZ;9Z6^0C8?FCw`eN!n1v9pXoto&gK`I(JW@7{IAfGJRO>kF1pOLQ zM3*`9F-n_ivY^w)t(N=uZIOzSUeg<z?4@ED=V6S8Rf4iQBn!6C*^%m!zI&F=foS^( zIkdJ*VLZo1grdCvulL{dIiV0_ZY+*V;Qjf-l%sBOj_iPeIpi!r_q~18f49t>b&`y5 zBgrC-%4@I5LlQxL|8@@>U$?b#UX=O0e7LaT@b}r##stwtK`!j|3>7wQ)5{fyUKnSS zPa0?LJqnRU^FlDWL5D)l;PxALh<-R7X-pLTfbnsMeovl+!NkZXe^3|3_B`1*2b+xe z`>pQAF3F88Go6ldp)NaEKsXPZ+U4%iC-g~leT4V{PsPPu*MsSY`9k+#LBDvu@r9_J z^|0A>yz=GW?jB{+tmFl^xp?M~K1Q2|t$&VBrHY$mJY#15>*6^NpzCUHBAJLU^}KnL zcPhPg1tV&G6k*sfYk@h+?z2?p1ktaT>J;+t=Vce}5rrps6kmA)E$uf=5FQhSPW1B; zW%3BYh!7E~={H7QQP>e{x>j)L@rPjtv67Q_yOI0m!Ey$NFoXmaEa4J2&Mmgd?#ZH4 zM1ye<SmjF3$|2h3M5jb5I6k7>wCL<P4#6kVQo`wVO?<cb%63-%n{~YDA|#@CgZn{{ zkajLrWZYNx8VVewBFaf#Bq9!ojC<=|%l#>PbC84X`8o^2%jTe$z7w2wi*2}QL%aue z!T1+o#(Dn|TH@zNlE8sPseUr`+;aC?o~m;kDkTXgQ;cHmxQb{W<ugp`Qa&bAt~gAr zGl(N#Gjt8d);CpNa+nxU4%>%Bb$8fI<d3MpG4S4Vr=w4=Vqjh57w39jpU&?7)yd!+ zc8H9y9WhsMT+C0sm=*6hfvD&knr^dLeyD|zAIjv<`&FB-<CCSACME~h8UT$4yL&67 zq(}M5QqFD)<8S~e@LrarzX|bseB?@$H*}({c$o|WE2&r)j~a%Ch@d~^9!=ZO#9<TE z(AQA!S(75%nIrM}P;e^V<6Z`{Y=~$Nfo%ccr|EkSgz{r`9xy#R8^yLS^<o^Tt=P;9 z(c8jPLg*&Y1VQ?G$7xhUm|$M!IauNK(&pO|2gJtbKJDWSCIunQHH&F62kOZ{Y!QS+ z&xS0*6JP_1Y6ovde+^a?$Ar-)MOjzedEU&yr8q_{eyuvo=f{jiXD9iHZo>k`wr~-8 zGHI5GZEf8=G%3WFD}Ov2eHOAwT)#gLR)xwiXY?|pw;)C_8M%iJ>K!8qS$s>QTFIDF zltLJeZFGObbM=+@Jor290$LgBIR_3l1VdG;I$1UxGog6V%6BcB)4raAFVCR6)yY62 z@ZRwmX4}Uwts!_Bx*s}EUEiLHr7Ge@zXy{^fV~h;3(>yHXRs~UUF%a}1G`3AQFpxq z)R%l|f!5$a(1{{R#zxvkB*~7Z8Ic5^YZS(=dkT|^;u|dvXmyEnrl<=NC*pz51{OGq zC>t0iMTJT9lYC*@Oijv=O__x3rGT&(jI?c)wFwBp=wt%j_`q{)jKYU@+aQ+4xm$^g zdKV@cn+jWDs%*kzcWMx72!PNO7B<SNE?(!m!DP7vc5SM9d6lCvlV$Rlu+>;l?NECm zOufz78?k^6mv}*`ES|#6={&^99`8My*;3jz^HJ&3nYT*uw6aZaiYXh%X)xMLQpOnv z_ZwABb`ycANyXwx=-<izH;TdTQaeuu->_Le8;$_R-z(LN2{1-+)7H`v>-sc%H%xsp z<KgU@`PEcwXnhR98K~b_6f<Rki0y^Q5vXR04}ON;9EQ42^`Y5vAAcNocJw%&>bZW4 z9}Ez4PjZrh-u>}$m>{51aZ3O5Ig3emjBrjWQ@tq<2f(Q+*pP}u*;8H3_4h}or;ZYY z)h|Xbr@FbZmKNdFQ7??4tZY7gkz(2qjwap{t(-Q%yT0omGH^Jlt!+>F@Iu1%QaauO z2uqobV*=G*+~H?1Iy241T$L;ObJ`qH9A0t9y@y7I^%T^vO+fR*1_@$v=Axu9S3&(w z1{H)2ki}HbH2&SUJvv3a{QVp+5PqiyA1@-s6vtiD#e(WZ?Z|%mAVKxmRy1b%`y;B` zn~Upwd1rosC{Wx&X(lo&jimU+5XPT(mAR27*1%3<knvI9_S91C2ofd>h8V3fRqt;^ z1=D*DiK(7oeB8I4G(;0bci+-zLLu!|IvdQXdIA=}(*Qe?<HZJ8oX3<YpPqrUiwUTC zdVe29Ods>~FQWg^3-w^l+EWR;u~manACTFBZtUzDQ0R=l?qzT+;Aq|CgPC6uGXfJE z8!}sQtI)m~Ug}mL3`|NE_}CK~KWNJ8O4#zG_)?f&xNVKflvDcb3G~i6Vm&ZNi66k> zVIm=;;FOtqAic_*O~W0KGf)@or=zJxzTmhW!Kip4^V3Zp;k_P21+C9p|6oPX%Dk1- zs{32Ue;&$Eo5V1bUB$Z|VH0!wjdRIX!Ex|fVQh0A17u-2qZ}@2L7=U(=_dg^q=%sW zmPMSp_{;i~39z%sdH~sKQq(U+asTHlWe4PtzXZp1#0AD1xDZ3p5C7*Iz*<YfEv>&T z;ip65*q4P^U=O0pWd?Sp`GTPdL?~ca4_ecRH%0c}!B7u-)Mq9$!9x<wQ>jYR?zt>O z@eFSalTnLce{(T)7I4DWk*evXABDOG({vNHX<eUs?<b)Tr`Avngp1=c&NoEApvN;k z+#~OS`%ExZ-#wcbcY&G8$Zmkvz2ml-s-`_e-m?bya4rsExtO41M9$h_I)YB390dV7 z9t?So`U>dgTyiVgJS)Jp4sSN)FHD(0dXINTR1AOIE-(|NBnn-a)nojk7c!RF`IGFF zT(KBzu6Lu{|1hDt1XFSKq6T^3VPeW9*>xD7$p~bsDvdAH(UVC^UnwBu2lGEc-m|Bu zqf-0}nGxkMVe`(WZLc0-(6iM>PS2je_}nTLzyXD+x*dXj9bu|g!?zf|+n+la$cV)T zBvzDr0uC-K{TGQn%6$zO6F-3mJmVm}pItwLwT3l@H8-OgHu(Y=3A$w<0;Em81l>*4 zqcC<al9DmPfja3+8G%|x5GF}ZuK|~6ds_;$!^Oc5>Zs>F9^vpC`OfLBCfs~4!SEC= zjc;j}2FQp$BGlAWcWbA#P6SH_9bdY!=~Zz+J+Vi0YbOL=;kc2}DFI7C`~2vX{H5;~ z2n?~3wmJJ`ZL*e~@?#AWEP+W9;}lT~I2hLv_f&L>48C>n?c*5j6DH9DmAt41RnF<c z-+l|-oHM#V8M+l%NM)P(x87h>ZEt4N*USdKb!hC|f&8=(w03S^wb=H_nlqbb9WHfU zcd$9HA+Gg8{pBZDAKs|BUA?e%Ni)GKjE(m7@PF>7@aIHR3doA*zD54gemVa11Tp`c zVEUeqNEQ#Xged+hlKH|~DSN^x{kC{ss3ycXUHlr4%NiJ$W5$JGWxeV-`I`?MJ~)1^ zTubZJ@#ARC#tC#%<T!}-%JuB%>HW>T0x5a2eaO%pLQI;fcedj83JgLCB8APFh)Kj7 z$~Db+-69`8vqc{0-y&B`d<9ecOK_{39G9T|o{#XDKy7++Oz>v%*5fe&Tu)N9La$~k z%sX_e6v<o(M%y!(j;#wE5KPC{4TC&~#JZ6jGwKi)FS<XR_4oIS*RykD#ZU9&xUs?O z5S-O*o*jQ7ZhjmGBP%!v1CW@{Sj`btYiTFaA9uL+giyj^s^L8P2M)D19|E!sf)XJw zW2qk03G0BvT;)F<wy@1DHCK2TDvSnPe#UAJH@=X=jVrX^)}H2YBk~~Pj1rW@3QNMx zd<)Ec3+AiM@_L=(kaNF;uyYL{lO@YJWKq6Z1Nj!l5eBosIUw*#z@<u}INab|4mT** zf}4BHEbu-McsbyTN<K3S{3XU!!(92L1-bU9S>h~^I0BGuez!<`z%20r3$E7@v%nc3 z@Djik|85aD$1HG;1vlbrv%u*f@FKvq{oNw4dY@V1eHP^0!)A#QNW1`$?S8jNoXz3J zWm|A-^UMOLfWY$s*S^^z@E)_kdn~wKhs**egTQkD*P$5%hSNF-j55s<XIhXW4wxlQ z0EuTpKMOL}n^mkg3#_-`=I%2Kyd4Cd0k~Z<J~yj4-7Ijr1-CZKEHKQ_;nM(D-L=`w zxDuHqMiykROtZw>K;p^GpPMV*Wrgd_@Kk_@nxd-5=Ecp3yUgI&B7Cs^Qyd5_TNQG& zqcmndS4youel(B8Q-^z*5tki42|igh=)BRE{E>GQ0=g_bSQvMS&gzbus;(TkRLa6D z*Ief)f>!08oDqN7>T;?bvJE&?t%!o9%s3U$?A+%GeAep4c&X2xV&PEo^xAEDF0h34 z_fR-{5e&^8*C`@wV%KxZch?^HAP1t*G3ZdFO4I_gnpiv%)km)4o*xZHOO}7C=1zO! z`@7SrSJI-HV9jMq?e>w|c3ZaWncy)Dd%6aPMqKn0*^j5vR2FQ+qn<S|XQjq)SYUc* z&}or@5bIlba5dHSb*FV#velWtu=Sw+E4mK=0vL_^V8ChG41kJkB0Gq;#c<?49%$tX zFA+--*%R$v(R&d)|B6m+pFnzb8~~b96n?H1rWDEhmg->v9%gyAG?C*OEQ7{Zo`wV? zTw}MV+>Bm#x?qc`I%)%QUfEmC*5PyN&=%;_>Z;ig6%cypJ{S%^gp#7L59zri@#dKc zoO^>sK_EB};APdH7{`;Z3mHuiS4!a{&7%5Yo329~kaN$#WA{Jsfi4{9vOTo|&xCNC zkXf0%MOTSVtaR<m%8*~g${;z=2RKMlh48n!Xb8rA^vCJQ7Sy=X!>k+<xvz2?J=m-^ zfjbR5cCR0`<3%%fHmnCFYV+gynMR)K4aHSe`(QQkRO5K)&MG%Sb$u9WUFA87tv>!> zVQj&c=a57ujf*8TUZ(mm8Sg1qM`a7EMo0m(iOhpVsCjP%W-lc9$8mOyWT1}InQ8)r z+(kuW=@8OU_BYt*sQ$aP0d~+@{UZ?R<oQfZC}iL{3QmdH^e(|L$jz`lAf_p*yQ!Xl zLo2eo9<+pXho7N~t9z(_d}&s8AZ8pDTF8jcOR0K+45C9R$^2rf9`qyLh0tFBq6FIu znqRUbQ!8;m47+s43Wc0l%m_?f58)j>p0tJM9LXoJLHq$}&kL2>#lFNj0}zJC0i>Q- z-t(T;C-x=IGosc1yn>zKvPDvvEgYiN@h<Dd6#@W~ei>KTM5LUksKQv||B<cVQ=x(^ zku5JvZy?<C-o()TZn6O4<o>Mqrm?6=RHpHP1ImI>QvUTzA*eP~UT~Or5FWHNZ|VI_ z<2N7N%-?c<6)Y20c!5%JIw1bkma4JxjMXona7yvLixp?=W&0$LL6k)|LH%*-s)iRb zkm~i%^)m+|d7;xwUGKfw_nhJTY%ADeHd9~ju$OsdBG&nKtFYJwI3uUo-{1sCc@SyV zD%CJDNl7@Kjmi1cwU+DpO|$UM3>-zr$yjK_b6EL(;vM&m%KZCZz~15=r-z7Jdz@Lv zm6Fe3t@pXO)<k%SaQh(RM9KMwd4I6%$RD2N(LNr}AYAR5H9MHd0!<=ilxzT7OYPrK z%LYyY&d|X}s@fZ2U!|<Q@C~Ef9<@MF)E<u>Mir{d`fs?N%$}JOYaD71uutKu$$Ml_ zF8i#EWP-QJ8euz9w$%Vj@aU8|Ti3whXGAF$jmyKpfg|22(;6b@<*E*CfvlY6PWL!X z2Jzcwff!{Tj)n0&6@6aB?+^rZ84SAfDuNZq_Z<4Nb`D+H4Wk?X5Vc}-DI+8BaC>m+ z5Hd%Jb=#qrZz0h`GYCmO@v?)ifn2>J6Y;ApQhw4?)BwL@f2>4HKk-wSZ9rtbEvM*; z3XmsKkQZy88Kei*A;zl;lEKP|k2_?eXM**2qn?;I<2Y8AGJ|9<pu4HOi;xHk6yXsa zTqDYW>!R|=01;2b_((;SJ>1X1QbWW9!83A*b`i0?#GFU(Mf>8R3dIMPppT*h6+D%x zs&^@;!F&aqHS#!Lh)YY)SaVER{Z@`B)MwKuJKEEYv%^ftom$^vo!S6G$WK96qkrIw zZWixePw~2t<GJaUIcM^S*@y}^OycMNh%RsP<-I_?K6UTnHG+eCorT=kHN}%YjTP`k z=K!G}KeB<vU|P)_VqlG|DDJa4NQ9O%<pXl@lpGP>F(vm|s?Uc7<H`Jm9cB6x(9k&Q z44NG`Lc~3vM7!h0iVS$_%Rh$xj0+Hzz_ZQIMW{#o$9>WXuV8v}lg-q4zqq_OeFUCs z$mKHlh5>D<Ng}aBh4Jnlf6bv6HS(v%=f(M%kyJ}Lq>6CJa1)^i@e7>JvxBM+9s&3M zqW=+o9jj5Gx!XfVS6%s~Lu;GhD1V$e@{k>8-FdI6Ui}BgFt>!A-#Hk$AKlp==omRY zL3k=HTZvat?7E?P|6?>zw?$CaI}3fSQwgfnS*StRUr<(xP@jY^#}@58YGL?pg<jj> zApN8T4o8;N23u_7q1CcTXt^lg9|;pD@lw&0#2F$`Jm_K*%1az82n+iVJxm-#hbh(= z=aH?ymPT`V&Sit1gWpT4muS6UX%;4NG0rDnF0ewU;aq45u^8r03bqzVfw!0}WW3{H z+aN4ztZ^pUyaFG7aLs1xt5}ORCwcZR8+)4RD}H3$)0dr4al#e$M;VKwT8mC4jTMwF zI)Pe~<UPw4ol39m@YL_(zh0~Oq~L=Sv8J-#mr!7`r$bqLrE3L8TM>y?CVPj)Ih;t( z824TLFg-tB6P#URIcrcsC8n~qg*>mqc;hVc^;1K?P{hO%rm`!A2idF`j4^z(|EFKC zEm)=a1kNH`O+}?Yqv~X*{zZ3VJ8%@AU<^~y{x3VqfEO9tW-1y~k34qt<P@B{08QL6 zoKJ|*7dv{>WmkT|8jBfKwEXMUX!a?>Avie!Qp`xFim#U!tcJZGNBm9O!VZTdM~ZW! z;rPshr&?oxR4<!~(yg;paHO;jZ}+oWg;E6`<1#C`_D9Uxwevmx{@ZBaPRJ>_i>B`E z)p!3@uCb3gsH)m8y-8I&4W;aKvSYQMJKa=&<p~m|c#`47ij1YmFQvD%Y}yI7r@f_A zY#UzPhc7@MrSw!QgfHP>?<E}Iy_5&SM<Hy5->vZb1-&M}huP{N#vhsPrV|nF<GzbM zp~JS`ZzZH>zMK&UOZ+BNeR~7EE8u*3eM!`@)pEy%W?eAM<AK*8Ap(~DPQqrncSEzO zZ0FZ*km?V-AsI#3jCbHsLyGe-VGB-c$e`f_b$1>yYiEWLcFCJS&?XSHs_Qp};kHX3 ztPk3hw~5*u0=?UBB^XX6Ql`47!xqBUuE9j4eE2q!DQEvBp6ZbS<P-~EhMMIZ`%rZr zGY7^wrRS%Yc5cO@2VR%3+(M8WWb_v{dtv%F+Bvpn#ChCCW+KuH`Jrm_z6Q>&x%R?T zHZA+@299Km<#qpN&1qs&IUvx1Bo3*_(&FN@Lxc+2+jd3^yCrzv6S5LsnyQYRfs@so z76_<)4H$BP`OCnjBbQxyvT0V!8FpP1$(%z$yQauFm-7=o5<WH6A1UvcySOJD0-!GG ze#JkgiG`wDFbwNfTt?S2&YMqz%F&_BSr7-@LamvvM1KN2Y4X>_ulIbx=O>RwU+hib zQzMY=zE-}MKeE|Bt^ZGWApa>se&~TjRtdwry=bW*m?C5lT1ss%ES39DY?1u}&vtg} z(YF0QBDUObF)G^cBw}eFEJPRgj}y&iR|j^;F{dw|@jxSUj@kh`NS2$dxstsFEnM${ zx*P}?S~RHEPiKy{=9sFxKBDa*%X4BenW-GYWJ1`vwy`h`Z>DoNs#&oA^ayP}(9;t} zeRx@6Mq%t!W}wbgZ8E~<FGQ}UdR$_vK2nQH4tR0mtsISN4t&TxhX$aagX2U%Eft@i zfOZ^oaX0;js8~4Q!Ce3y-3keE`Gv6%PF8olj(#{eCAb2Q#HvwnPIgf{EVhlqgpoX_ zFxD*k2!LI}1C2Lxe`y35@w|0Z1Kt-VTtXkp*vdizaAo|pl_=^<XX?n$CPmRwl=S5Y z(JVaY)mEX4Uj~n2pUEq*ZT2sWAh1S+MKRPa7+T@dG1<Z?38)fYxRJfNCPw2A1*l=V zh44%R+xB(&kn95aSU7Q&Efpme!Wj=_M6%uB(<ilZ44klXOWQu|a%fu&7cTSOIISKC zLG`qREb}Wo5ZbM3BJe932ZUWjr_#2E51G|SkUEP|25yU4u<l^BcWY?#)P@=Lv!Bd= zxUgnPbzDu1O`*;$frOsbz*?hTL{5f`8X>Y{2zr-T$n-Vt?c0vd9_=v|&53|4s5=n; z43T9+&Q0;nd^ow!JXieqF=1r-wvA#Y@=Q7H?U3lZ>Z_OMOFMSoXH?_;_rlnLkg+&e zcR~l*9^H{JZ=1`wa?}I?bh|T=c@CKd5vVDMIAa{X4&~%6<1I#_{NV%E&_c2oK0Jgi z^9MXHJap*~;W%RHe5n}TTzZ*L94&zL6bVCb3|gJ<?yTeKp|4<l{7T04Uoni5#b{rC zA9pbYS-?F6uw=c!NNuVF$c7MZo#krM=8MJX&-{T_TGexoR#jxGzxx&PC~)Tk#+ZU- zYBAMPDu4}Uts$8(ZftsODq6e!0<$>iW9W@5@r#45;&*q72E`lQ_O{Zmvv`7&Vv4lS zva=_O9p>?j_g1ysvo$`*Y8RXk+d!_t!%Cs{m`;=$G?mE%7a99I8+B)k9_GbK<18(y zz0XM#aoDt`UY(VDrURt9XBJcsf@YpP^Ie>tZG6RDjKjgmFHQBmGjzv0sA~V>Ahr7- z%G&Mq21NBI2dTjP(oo9bfqareWrqjy)W~%Bh}v~sT<gK+3-tu=>cf@wc@3AJST?|y zHGKSJV1oTPIMAUO9=t8WIZQe@OpMlBM?n}wSW0uDbWr=7CCqL}aC!{~5+v8xiwfN! z_fLx7O<R?3heVJUK2i3SKn*MQzkBMx1_L;~x{vH5@K5_EiTY6J?}U{}2n+(YwwuX< zJIu!x0T3J-HoH+h!BcglCA%JssT^0p^rfF1CXurP9Iid!X}#@;W%{-SbWI*lJ3vI- zPfV@8kqAATv;Pp!>qeZhcJ<+xa6H^B1y|@I8l!ii^)Q&CxS-e}bMgr?F{DW!gn$2} z9cPdY#e+w#JIoCAT7NO62}uj5w^Lo1Daao72UyWBrszKe{KOBCG^>rLpO2f7kS3b~ zln%*iRR5Lp@~d%E<KRfUe%jwSiO2Lfd-;$iNDu8Pa*;N{;R_kVr=3JSq)k2q-Br{B z|K=3W>;Q#6%wC?Fh{!U&rV{oV>$}!HW;keWGIYQL?l}UU*VdwszxGsrnpufCVBDSe zt+=W8U`Jr*y|i$~_rSjptBTAWr@{@a*bI($S36AHskC+r@VxUTo@y(!;V+1GZAg4_ z&>hYU66z$hMkL{+P(htDrv0Jy(b-@!PLI^bx=QZp!D@@`RA`~X-?cvH-@uDoWx-pv z(t@V~ylFS|k^o*WHLDg@TzKE9e(i7B%cV^sUN^{)FXPL&^3OoIzh*Cotq{I(yXYSb zxpe#ux(xuog6U*LC%W~(?=mj;fzBx78(l;-htFW8$MB7NMKyZhjv^UyQRW%0qFPMw zi`c;ZBBMdG7p|LOxu1Zsod4@M9yvoF;uE;3HG~T{3Bx>-+_M}np-}9hWc>(Wt2_Mf z1<+di4^x=~&_i8+LVLaq@U<n~zm28iNq25;J0GX@5qG2ai+bQ=vBKo!D>!z5ERZ<# z^4kE1zv~>qzU$R7ehIV;jXB~u;g|y<FM*F=iv(uQTFd+*O-v><zQ)L=W^%IrEAYSX zKe)yD;YFkD8<+=2Px%Yk){6v&WD?~aVbm`$rLpDZi$`a)!vw_EIp=rC6~7TicgPCN zPo|7E0d3k`2V!cRxdRqp&xQ6M#ttSwG+MOPPzYmSGLKauQ@hrzzd4}4{CO~BSs<#9 zIY`A!FpONOt)e{l_=frX^y4VU;E2kK#67>qK~ewZT0gsPbh@pxUWen8!_|chq$^SA zLD2$fPN_!Vlh{prLQKgL0NXb77MgvukL{5^3lmlM;PnRzZ9A$Sk#nU{Yp^3~Uugk3 zBfKp@{@yFeh`Y=OkSZT1ivvRHr%Rl?s@Z0#0>KQE(8%w0^RE=6tKW_3(R1CwhclYz z*T+4{tGQ4;wRLvG!Y4}}E~|-=pn=8i>f9iXFjoN^v@aJF=ud&P387=P$KVye(@=$d z(pcGHQ1}RO79Q}-ospYb6OyC#w<kR9zqwd2)0ERSA3C0Z$@oG3>(i@^#>20<Q40<o zY4mBMp!nuZn7&(XN}VCgcg_tSp4jjGCQzTSC8pf@4t^N-G`vLdvG|md=8!M_Ry6b2 z=d@%QDIb2D|7W<Ty*%(G|MX{a|CfBNe^UGT1_+)DUP?5Rxt&95MaSOZ_|?mpgR8wD z{yP2H@8KF1e#N0|bz#zo>qRsD;!#|zxt}E!!_114!`b}fh*Ic9XOwZwYaHkS!rcDQ zGI$}Rkbt)gouy=qE4-z+1rEy{NQo>FFzot&IZh>4j1rkV>Ni3518mqrGF3Nu;8#NQ z`dBZ%DHZiP-j6?RCz^EJb4b6xZRGF0lz<Z^2_h4^A(;%CyF+wYk8}0peYc3R*UN{$ zqOw1e2g2Xa<%(A}DEs*PT^Xo}RHeZjnNk@0{!{%vAD)Yv>-dV^X89fAVE-x-dXx)> z+QJ)1gEH<JIE>t$97Zl+jIF{LQz3OBYWclV2pv4?BQKTWvK6Ifem%_m#K7-T<IE5< z7nLhluCfC?{n9w|8x(z_pCHHdHQIN=)1hk6-cUw+O~`|EhW+T`2`|^Gu6wjMg*?H{ z8X}Y_j;&LoV~&KWYHb#>HTZU`GNoheP=+hLqDK=AzJjW0yHJ$DNt`onABa{p2%#N@ z5rV2CN$7$h#Ix#1LS8tv^#1yToA{gWuN=SXAnXv2-?SL|7!tCou53sCC7yz+D_S(S z#P|K2BPqE4sSM*I&uD;}uWaMJmByI9{~oN+8x5e2DpL&luB0D-|10!oNl!t}ARTHg z@$=r_@-_{~RW`O+N)qL@KzkiNu<2j${g+Za@G@R4W>r^%GwnhPPxn@rP228J@!VUX z;2^ctp>Ys-Rgc~I+}p!k=5>!NYft8)XKshZ67_R2d!%tp`lKlxkw0v`v9WyJJ&4^y z-Yt);hH}97V|TjP<%5~v4lw#Jg1?aWncnZSR&V<U&fLAM&yHlYOyc=-g|R#ptgOn~ zk57MuA)(A=z4mcD&*V|1LZ6;l!f)TDFZ+n+IEFQ-(S>oF;4T40d4&NlUu7VtAi(De zq`yRx8dAr*1TABCL}0_S?C$o1;oPPh=utVu5h>4#yRt2-yf7wR;7jso63so^YuMtt zXl|n`Wq%HAa8xf!>ANm1{F}?$T+uDa?f(GTGCbJ_5F*%p^+UIU8w{94^_8dS+q2U= z%SvO?Rx|zY3IBQlVv(jI^<D9KoOA{LEQEcbJE&KwuWQQKG|abM>z5cE{>geQ+_Xlm zza53bOP$>kd9umC-2Tb2?aR@|4aE5?Xd@%|^>1)Vlv>(*%+9|aCuFa_C(Jx&H2Sqg zS4XYKq}kyIe%t2CgkljF+v!0(l-U0W$3atxIi$l1ulH&6PpJq0$W0_E^Hi6uefq2v zIgxx#Dg;JlJD)J6CJ#l=M`t>iLiY7zqwA)o4aafWg5#Y{CBFcA*m7{nDw^wgpZqcD zAB__S4`jF)$Gh-520|mgLooL{?=YQQM<_}2&!6(?xH@xS6jvaFKvPP^6~OYk+R|gV z+N_(6;|Lr4_6pa}bvdZMjNR-N1jJ{fVdY~3kHPGHtZ&A*I==)sA=v}omeTu>r|oe( zCReR}g45BC2GbYy6fZ=UgeRQUN&5a+Q>#MWM@P$DU8eoI3}_Z5j}m+Fa5@`DxhNLh zFCVhli}kw+nCsY-UrX_#6E8L6YC4vlF*bUbl2PI>iFh{kgVnE;gq+vIl{`~X@2}{t zq;uv{DH#riF}BdP9MuTJqf>+m&Qy9IMxXxh;n=c4F~+os?U??yJ|j}BB@>;_J@+Y7 z2%u!X-X)Suq<JFA=sC+|IH9Og!D0SB0bYuzs-uwixj?l`WH%t=Rc@X01&fq#M#_%d z3tsk(DLPDPuCzVFwLChEaKU1Nh#9lagCi+i;nli(m6~9wh>-A9`Wpmsc6Z&j2WrUi z&a8VK9R|t8VBK2(M4-8w_C927bHnBMP3(Y)9DeMgE-Kw+><w2?O#J2y5)mHJIUCo* zw<Nk~<NDmvh&d}|I9G{eTY)nUr#(Nt6fO1Yqv@+ll!{X%d+RutS+v8o!XT>0DH6&d zD_Y=?$X^jH@l{L*zYmA%7bT5&_Kjfx<YmVQrQ%EpXf|=(Uo8gphFR%`4eR>cyU<M4 z=CxjKxLQw${dCZB{{ywMLOkR8Eywd$vG6dVJq$V8us!+aHl1Zlk=`yA!eCS1e)H|^ zK(m0c;`4ev7@i^jd)Y?Vyc*Fo7a$Y(rj#R<Tt$V8@w7g!4d;$;DD@1|sHAv%ImPru z4+JRQXSVzxWK!A)U#8IV0ubd}_`ohIT39iN*MxRd?Bmyej0XNRWhiVT4vD9TXs20< z=lzIL(UMsT*c@ug{9&y`*?&9zU7Q6PokCinsOTrzETAO4w*S1HwqQK(C$VB+dJpf; z^iG|i%D&GtTt?BcCB#}ujC}i~@8Ynokb9-eNUnOqvopC@CvuP8&ola&$-{eD$v>`i znahz^yJ2$7(;{DV%Fs?M%3Pmk3^%hNTrDi#Zj;hVgYHzin^lwJ_&j5hnY3E^zevM6 zNEJW3^Wkm1sXxm`{fFMdt%$7N?K?L-!U{z>KmVuRqpTRL-bJ11H*|t3$>$lLS@bTn zh>q;fyQs~2=XWM&I+1%2&oj1<GVU$;!b1AC!cwgG#y!;Moq45qz_O{$&oeSc88b>Y zn>o3?V^29&baeK759in>qn`Toc}Bq~BP#jSQa}2K72TLZt?yjfaVxcP7xl^WjFM5t zeI-#AvbV)9>%h*UKJLt^q64;<TKzoZ=TXMYlGSf;`k<2=RCS>5rdDEBoz3w2oABk& zGkzas+*PvN!VXnkaKX0n_(Bip5o0Q~^m)cZtnnoljJHLv@8GwG3hyj>V+SmoTKGJp z8Ebsu|KdahS_S}PI`z?Zr2WN-)B61988(5S_4yV`^zAR$U_k40J6Gt`!D1IR8+<$v zv_8{9_O|FA9oSjajLw|AI$(RLFmU%k(0bS#oMfG}uCSt`k1n#tl0i)d4-Yh^l}xr! zp`?p0lUc2XcCJiir8XjJ5_or@aeqmWh3svy!>rh-o~vsgHU4?V+(6@=lJOQI<Z+4Z z$dQ-Wj?ARSJkMAjXiP5|W5IZvZIqQQSZp8`u(RQ8coRMn{21$Y`2XS-(}5k5MGfoR zUZw-Mm+}Xn#>(~oU#wEB*vS2|S+@#sU#we&g$PZ)Y}W0-&UNKlsX@2zf&XIN`dP@{ zRzi^#8x?VN?W4TGd$Df4EkvmLGOOE{m(9BE173-B+sA_OHrpyIThOgXX92Hwz|yE* z;E;jFloIQt@;0aH4(z>DkL?IuVVz_@<pPcxXv`{cvB-eVUSXXigL3X%VPgl2Jrv9( z&4I?<C5{%dw<{z@TfGud-8*w4J7DRQ1iW;#F||bU2B*$LlcWQEAH~iyoh{7iO}H(1 z?`Y7vt%V)RsbaN$s>-Z&3cPYOXq~cPye+z-gWoPn*je<U9k4Wt51u+2w9fxuoRn5< zdN1|GcJzP0Vy)Brsn5Y#N7Gp)pIa!=7r$by(;3v}&K1t;V6lh#4BT}zy}RTy3)$PE zhj(Bj>eJ4gB06B{)Ov91(R6Cb`ZqXr9-5-8=;*zx*nQ{}Dhk|pG@V`Yv4skKbQQY~ zok^|kTp44frn9J(;Hsl(eaT7-+1p~LSg}zVSJ!T88Mx|bdRNIZ3lXZn%66pfZ@44r zRB8!$<!E|uNw@{$ZMH>Lw)7rqAr`Q+;T(Sx{t>t<*6m0Ci(6R-b_k;8cW!S*2QZzQ z3x182JNJLFs<L9EC%>6>I}=<O>vo2P2)SP~>o%-&UA0zf(Ct+4U##1y7P7aMP;bRX zG1u6-c2l9?y;!%Q79v!1jn(aiYi8XBfme<J-A=S%yv<fJ#^T?g+wq+Rbn1X*Q%Z2i zF?2?W@_%vi=)g{=#%xFQb=FDrR3JF&7#fuXS}4(u>#UQcP$N56IJASsE^0V<<rsQj z$uJAq+oCHwu(K%t&YV;ou)Wk^@X|4KX35|;ICUPH!aC4*Qwq$gvxUui6Fv~UcMNEK zpoJZZy1{Ba;f7i3e&Cf@>-{VkZ;Kw)!EX=s)>36d2P~U{h014G>wW(hr<e}xbgIvG zB)@6ax(7Hb*1Cs<5>15*T3Gk*)mc~R9V~WH?%=Li>pd)FZ;PJOft^LUbmo-X0ozMC zgIi;*JHNrH^UzdeMMteSS(DD7x`X?Uq0>sbTd2@`zq^F8<AkJhWffNHw)^0yW9XEU zE*7G<g|4#Vq8-1x%#UrV0FvKv7uozTl4>i-@yDGjuC+oA{{vn-hTc<RUCZpcg>}65 z0jB6|LXB1`&0}!eF?4zfkv56(PWw6O4czDHcrC0bicaCy!pwaPB~^GGiDA)_p;nkN zj_Q#_<E3?{1SPbpLX5I+$pq<C7j)v*8ku<+%98g}TFKGK+w4rbld^qVg6Mu>R<w5| zm}P|-<0<N0+HfoUUD^mM%(#;xlHA^EdUU@iD+2oGc8@*~E+_q$d@&tpet)pdi2W~7 zQmkn2ijr=HK^4Ti&B(FB-=)p9!f1y2{6CseWJN%C{=m(ER}w#cw~n$7v>t!5g9k%> z@-AAH742P7u3KUB4r=wgwAEJlyR@}d7#&Y7|Bq(WTM^LZKiS?${4Z4zM=kb>Z2w}L z5er&*r*9=zw0A{uvcmpbQyy0MyR=?b7#$1hd)KHGRs?kRFLQ7FhpLnvXfG|zSpIG^ z!mMcTiZaUzgJvvwmp0rAf0s7G3WH`W{EuctSrO2;cg)S0`)(aE9cYj5n0sU9yJ#s^ zw0A{Gx5A(qQ{Sb{vBKY_&9%ay8KM8tj3O%n+IQF7jEV2oQPzQW`>wee%6HMKtZ46w za@`7pW{iB7w%Q7Rm$udlgJul-k7m?c5zvlmR=#+u&pUN^g)1z+k^118lKzo`dvLCm zy^BW-?u0k9iqQQhUXGO?olQAkgFAESTi5x?>i<M0Zd;f_)bEnC^qLaYGDK1heVMX< zR|H9!6+4k~1Yv*qkD=F#cxA=fOLb3MdQpiUI1}o$8eI_m+X<U@*;YAP2y_q9mZAp~ zFRkXH60rX~A$%9R%-TK<JBTo2Uq8Xmesf6C!*%H1642Tl1*9T~n{;*AOHrLjf@a?L z=I=a=KELm+K9cv`Cu%*D2nPb?nZ~u%M{?IzU&+NvGhVp<aLpM6uK+RLYwM3R;S;yh zk-A^tf)#kv3*V68TE4cr>z6D~{^HVW%5}$C;rVGb3yxn@o<9@q(tOnDTJYz!$p(^$ zxwPZj#b1k~U6vQo-3ne`n+(kqUQ=IOTfep~+GTDL?OM>x7V@v*vgT-)Nf@z#E#t9e z&mrC|qW2Wk;IcRZm+h>Hc6nF?mF&JYIW+=TvG==Zm*0zw-3xYIo2+9?JWoZt{9I&o zD@eLFnPE#F6-B$06d4^0wySXwycNJoms=9;Qh*V+v6U*XT^s<28AY_CSzZrZ<rNTj zdlBt!7FWWSfw-SxJWgC2w(L2GcGT!z@H5+fB^GYQQ4sDZRQw}b=5a|4g#%84d>HX8 zTPVSZPfvh^81WQaX2TMn1@SQ6F}6&I%MJrx2F5F5%lItqX%G<O6|!YKT(;&I2#E3W za9Iq&(gL1(IY!LEg-W*27ZB%S#B5yV!IrH71x~_vpkxCnVavV&H4n#lpk@PU!_fj> zKa2;8Ho(0bxDu>t7mT+Zm+?7T(6bH3+jh-oayi>05VN^77tu~w(FfR)we_a9Z{wt7 zy<lzi+Ef%)+fU?|@GaaEITERB-DN39+Q@tO)h7I@EoG0?Qr5Pj?`nP2DOXz1##c?Q z=kZ(iD88v_$d*@d@5Cm4aa|<5UoQgxarK9k#rOsy_2LZX1`|zqK-oMAoQyfbi4p#G zsf7clYN08mYvZQB07gKW5MV?(n+|HZR)BEPgv=_JK^~4&ykZ6tHVQ%+i797i733%h z^pHmbFFVgfUp(k7VxPUIqeBlyk7b`9Ooc1b>oSb;;g5vc5h9+9O~oKXOgYmo95i3f zIhEnA=j33jU=2Xm{){~826%G(CZ!oO>fC`xi;%-%#%oWCAbWM^IrMQ|PeIhQi)cq( z&#|n4=SQ;{$2nb3gLFbpI%*u9bd$(fML(z^Qq~UE)Awp%5HV)ld~N18(v)-Md-S;O zebIJk&GtznQJ;sSxz<oa(Xxl5)v-_>J8>WZx8^{-UuVNx!?@jf3e7}19(b?Krcb3A z_gXmSLgI_*Yn+c@xp#=izMW#eh?i5PFXY!jCXN{LIQAw%%1J)fCzqGx-JjM~61?Cv z;5$rj+&i%Hb!L)TdETJBe9W4^!ou8gf{if*Lu<WI*rNf1;^ExROXOj{Sb8Ifkijo_ zud4UAdKolPYE#fh54ezW;mL8$?SsC4bQ^7d{6T-f)Eep}W76SOv6TJ)KvqujAp+hS za>7T&wY_BU4cA_{LLR_T^!l;q*ziSn^^q)1SjGgLMn<n`;)9yP@BU>?oaW&3@Y`6a zS2!t2@tAZ|%H4Zt;orUZy)UCL{vO*iTz#EmB?>mPTGm9dZ$=iWFQVIjZ&ZWkAeE}c zFS9WATOBV*hcCxZ8G8fHiMuvhg=B0vAxV5foeBBnI|Qd<`Px%LLG0)F>Ss8sB;h*n zl)IOu>0&-FI4^E?+*O8`YN{_ydP|l6cI1XYws<@RI*a#thHgCZ8pH?vgtuSPgTCaY z`A^23=R^-4tSt~h*U6THsKb{B>LWlm$7_cozo$OE6%^n2>S62KX)%}wWc1gesHgBU z%UP82w5Ka)amI}B!gw+$Sv4|Ua8d-Wx~L5O^wiUb?F6r<8ZT%eybCq0$=X5B%pLR) zIw%Hp`A4o^<I~MA@mn3dk&-YbIrGwYaD58E4#1lzlGLEkxEtK>6SN)K1$Wz`ugu-) z&c0grPrf}aZ9m+(GezEodhkMm-XXTtL*4;OdU8#0${zk_1C46x9;v-8AM#*R?jD`~ z6f+H58(#>wTbRPS#-OwRxVVo!f(_-s7052#UdL~o$@L1RC_Svt>Be(Jl*xHm>J@rt zf4F5CYTpORC1IxD3S<9jE;l@ysVX(5+r8EfwS)JX@lB@{^=sgs(*=g7Gp86(^%$8t zymSvx6sZ$9)z<xVLGN_l0c0D4?d6d8QF2XZ7qaND;&)Des>_hC{{Y`I1s104{hG52 zj+^L*G3n2JlnVDp@Yf$T`S9JJa{2$*%klkm&Tt_a+ks0<v%_73QdO+2XQP4jJ=Lrq zMO-y^(@&6+eg_gMf9puV$LaT6)}#280N%xeRD3Y?OfUgR{m{e9Zf}@+?G=tsZMmt) zbPLjiui&@$^7I+2q9wtK?6p`p_}@hQ(>_o6ENQ*eNzAw51!GG;atYn3_jL>3AItON z@<!~BLQed1!AN_;dk;>;LmtnZ)Stm6p$pjD_j>`|AnzTl1=1rD=q_Xz`LXo^dC5$H z{ItISU*!e)C@Jl_b@$*lGD0c?zd~~V#~?~HXe#4x`0KA7mPAN5`tUPM<rlIy|5}MJ zp>CN2>43aO8#-M~oDSE7Y}4cGzA&$`Olq)@c5k#n7jt{4|EspU8sB@kCT&0x!PSOe ziAnv%%9m^;=Cx@;R+Q2%aM=t@qMDFR23oj+*D`M&+#3}p`KHv;@3kdnMHYY}%i8Ac zEp42aT>5n083T)<hOw{W1cy?Q2Z}liDZIx<z-3`nYYd@1K}x=jhr~!y*gA&6w^Ck% z*TWj%^{|>&*#BvT-5Iz=Q25#uwJ7y|)cEzDkaTAV&=NSC^Pq^g>x^h%Jl*vL-^vdn zkit034c>EV9SI!O96Mm!iJdyh!Vdlq8G#nac&b#9yr@yKEe0owTyD7i5T}Y<Xuev1 z<H?;GLaa5w{Xp|72x*h9>o?kgdVKY(c9e*4`%uNBV{UeVi;3qqLatJ5(|r6oP>v$R zFJbgO6wtDXkjY@=7m2<~5TD9~cQ6R_@+A+y;s$YdQ}N#~2|B@+SO~W>LGD-)(ZvCB z-QWy4=DDM~Bgy2!%Ai$wt7b6V3u-X`u~*-~yJT?jGN{cTUJx6e#=iAt+0ZgYO>6T@ z*oyCYYeK)gY%hNG3NFR8ZC(twwBl;vSG;ZWH>Ecc_nL8DaX3k~FVYQgr-u`d?YkVV z?^Idf)(5xFKf~tHV8vW+plWd*O<`;|oTV4OGb1?!HTlD3umJ+8-VqEozN<bMkNcv{ zIJp+T9R>H&z4fjgT*xGW7vP$%!hQ28@L$}-HWkLE|BW^d1!2_zugood$+q+*+fsO! zFs;+Ogr=w~|Ez&liQxq|z=QkvOi@RkE-uA41baW7T8nQ5rW!*Z;TwYQG~oMz|EYl+ zf^|IA;G}P3hrxf^U`j$8-3fdn0^0Z<fmQ_)*NwW#Qnjt{#okCU{Ley7qT(iq5~%=! z*50&DUM7tj2EA74;<^lx@RVfev6rHgZ@Tmp(b_t9GSWc=Ic@Gv_<Kj4`^m^;mL5Wr z9`J6CuF|nvB;fVx7AY3dyXxH0%`HS*;?X*7K%zttU<b{>cN)nR>Y5kxnQqX#aPjkS z|0a208oW0c+rnm$M;*C)yx?u&QYV8dUl>zwzDNy~IuLx#6tMaRxWctjGx7Z#Mwk^3 zH~wCqBgQYbVTFeWg2G?OiNgdbe)^ksn4JW!9Bg)o1UGy^>+00lYWes-chbP+!uGaT z!9uaYnW9WL3!`k;KVBNTqHu-n$_JZ+w&ZQ0K7X(}XieUlDd3}mp?0`eU$B+XxRS!e zUro4I9fEa}jrzLBsgom)qdz`?s0*$JewEmj);RFB!xAN#k)FZV;o{xLLRr3=AQtND z`1v2n{a;CV1w)*2dAGBM%*q`nFZv2h#SVP-0bBz6N|Ij6snDnqJvk7{o1&TGYd9+` zu8ZZJG>ApU-F1&~DKiD$K*;BKMsCDBH<YHb*w5g~Sgdxod0;(VFE|(gq6U)lAZ337 zz1l(?u-j+HG2ePzht(_4Oqm5wxVm*`#F57Ay5_fWnJFQi=q=rH{5T$Da3<RAhj(hI zT5g!RUk%=*BURrevSt~kzH7u+Apdm^-<B+`8%6y3w;1<uvvih>_y5IxcUV+M+y0rY zbTFvcT}6t-vc_Jbs}WFQBJs7QDP{$Uv0zI~-6gOzMb@l3SfZ;@RFYRoG{G*$u2Ik= zN{op_1k@P81Ob)uyXTxai06I1uJ8B9cYR#f!=AaHXP$Yco@r;scahC|m;Os(ojH{i zM-@AM{d2LXKFBcJPP%!rqNy@x<Z66%@2bq{Cw&}A__bbkvBfwbZ%(@W%wC)=*H3`_ z(#y}J>dc9kn|~dHXCFMLTyDPI5@TM5pBOkZFSpd`eHWodBLTB%*{8|>@*lFfYGXvI zvzchpK^(It=6Q{n=OdBlH6h8ri8VPr%oTZlxw6@qjk%5fdy)XA0V(2&?{FU%&x@1v z38Ah6E25@xNjS<hfT?OyJ|BwZv&Ou%Oe&r!V)3-O3oOSM5}u0>QAq#oi28SP3;Uv1 zDlFH<!h(xNcpPSgZH!<x*w6qrW<N^z-0jhaQzNz?c1#KqK3}p?zf6Ax?H5bn<+)E{ z%ztH-7Ker5Bc<wOZzd;Uy=2THf=sMauJ42(p?zv6t(SIVglt66P-a3M&e49d^53X= z&DNULeYEU2&TI%;cjH3oNVB-P8#?jhT<Ij%0p2<f=IzG$IAQ%=RKl(#t?-XHz2jm` z8083@-qu@`Dw{1!IU=X`lra6wMr_&i?h+Or(}<m!-dn<g-fF}=qI<Rw^@UHt0lz4J zQmEghI3lBiBu?^c3eHlQb8@1Bb1FK3hw!h)5Ne~jB{Rl0Vmo3&TH&idDY`fd|A~M< zh~Y}~qndbl)>D+8F>;+bB*}lbqco-$iFUM_(cV(xpQBZ=0ZQlTw(xb0u66TX3CA&u z)zx*LGm2BoYCBJt<rW0_^vS%ABT@QfmM#b~_Q|~Me?TYr=jc@ayLBqYZKhw~<JX7H z6|gJw{!@NIP)?uBy9<JxeKP;<ynt=1b!eAl;z;wRUU4Vkdn`Zow;h4^`&hc4qi?`@ zpj*F$To1tyu~GM*(F@Z7`YlK4>)`BP(v_h0B|dPtS&#UAi1DfWJH_g~?n2#obw75r z4?e5IAi&rmJ24A`6>~lg!o}xN+s~@|B|gBrIJww!LY=n6-}o#!a+rmo_1NfM993J( z_O7bc{kQ(^xOx9|b<ttFBdn^sUtHWmeD;{8A@BcT3qMdKj(_6vJRNCOqxGZlqEAh| z=D1STsjq0rl5-ek2-r&Qj3XhbEpA<XgGw^LuKp9%O^{sMuN-9G>^X6Hi?O-|PC89+ zjH;fmvcBUuP~BFF@ktklE}3<#>h>juq?TKwbdI~#o!p}$7rkGHA&%47!qKM2YApE! zGY3DB&~v$-fqq&x)DFFY*@+)X=#j$|Zig-OwuB$Qiiv=Wjx#j}bOBc|eQ?6D^g(Bf z)rS|ZzcznJ@8j@Qn=J$CjZxk>qDANT8WLpC%yRY_f;n05Md41^cl!<*?ZClBzV6l| z347gyx(UB0;fq%x(57`Qj}l(RNf$QrS*;tpM(?+LB>KZosaYNE{-56s1z6BZW#crV zkq`ZJY|HzJA6ek0)*-1ldOB$2!y`XBv`%$-<c5{q&kfcE3>UvO$9eAAsgh}Da6Y7) z@b%f^W3E+_aUsRzMF$L1!!UNw3o8-jQBe`73w@3~WJv21tTcRn6sMxM>ba<zDbTua zbSsX1R67t&A`XytD~@^aH#RUIiO0DTU*}4J#(k2VI?vJYFi7Pjj^Ibb)KLYC95Wx` z7<%DNNA{!k0a3#1wlJK6P+PD2OQ(&oMPp<@$t}dcHUTJ#q|-<ocOLb|!S%D?vxCD_ z+X>Gz9n))}%_r88Roka!(P&&9fc}A|zS(KK0_s<vM2F>7M@8*GbyVCmN4L6ms=Nh` zL65roNBJypEn(r63l(a)UoVY2{V;)o>6;kGn!2uf>DlZ!Q`eh4V?rXzp{b8<8wwe1 z3B~N$N?TaHF3`rUc3S>&B-RIUe-j+v)CWk<`hPioukX(Bv5sdNp7$4Z;7%{6cQX4< zfgSE8e*z=f5FX)}-|&hQ?#YpkGY#MR_hsSQKE~j*^1u5z*?;9G!ox}Kv+@!dI9#+l z*_cw??2NZyB5hlS3HyIOj#)W>d#>OoY7BT6W58SgUE_@IG?Ea1J?2)_eUFFG4+s(C zhZ~B5=r%&a-6f-D!>HfrLqd3rlMOUbMY^^XlQ6k`JM%_;Lb4w3ylQW6!Qc{Kj&bN+ z(rK`0-pSS}#mqRo8p1E={jFu(u;bB~*tqcD4b%J4pH$>^DSgU(Xg@WXq>n)+CF$Jh z3N@J_IRZ6Qr6C)c{rKwm0nOHs(QTzzhhh%o-%sY>VH@?*k_l2q1(nm8S~)e#{-o=* zWZ<)+*XThki~I*ymtr8k{;_?SSzt2?L8$6mgI=UwI`Xw%bd(LFCv>m^4qtsxUphxm z`um4L?7J}6e^yO2`ubFm1%i*@8yRO>V4rBDfl$9mXQZd}q@z3Lb^QCRs4X{HzNLTb zNkH4KP~+M)gn@o&c`!-n=HHoJt!DX}KIcOEdW%+WY2j}GXfLm#@4Aqg7J1%-&3f3U z?MTEC5_NSG&_YI<W}8Q9HS4BtKZ+_>myJOYI}g155r^J-#H_#1uJ@_SPU4MRt=-H6 zeOPXvo4*N@%Ofy9dVM-O=f<J29;wS^A<1*v?5JGUbSFH!_w5QcW#SjkUQ0n$>Op#a z`cXfy)B{i7i65DOGkC|@Z1`gL@O^r~m4pwE6h1PCYc&YHcJI+Z8+}`RWo+{+W#>63 ztvqA%&q4g+gqE-slOc(<QgJT9JC-HJhQoBE8yVPrCQ{37=B9zp^R{vL*s9?#?O(Po z_y|<nHc2BCmsZxZqDRG|leB?myO9p6{2g?C3~5P=+=yq}5a$23>S7WJ6WcSR&x`aC z+WRG6mQWXW(*8wBpzg7NJzNra_K)n;x9|J9AA5KuiH+CMLK{rpuE$}9si^l$C0CMg z2PonaSS0bRo;1##400>I%4J=dyQd`tL;h6Ub|_V~yU6Fwtq)7CE%y-7FFNIBr#2JM zER^QGJ<r)fac_gk8KN%H5Zp2x=FnP)mYc2>;>#u$cn@{SNWw72D>qHOY!mUQ?)WZp z6<R{^bFBDqavZaG70nMn6w7q(j6eVSl|A8P+1vDv4{7DrP%XKG%BWW}xN-PL0Bb31 zrF5SM+5*uLdYzVf5TmLng0}J`9flT-YSOaXtqWNTylJ88{p)YD=^CjW?mYeDzP^0} zgeI+`*qtd`K-0WPu)#V4MV1xJdfQQ+62<KG@Be)D9Lyr7(2HKAm+z~r#N3)~PI42d z_`Llc+R~f!*Ne#>wwI3gCc`DiiErsTZxY%`<RpE~GQd9z#mFaTVs`SCq(GaLRWUhK z<wM?VE!D7sa#P0L)89%49RIQ0wDT{z*oV9nlsC0VnTi|gr2fK;NF*XGMIw%6iP%;z zI)cWq6l~2M5wu4$w7McEIh;;zMxuSkAYq4$iD$EZG=lXe+-$S}Ueu<z+4N2`G+UDG z%W_9OL#C?y@~(Z4*~2ytw_^{p6E7u6)h25OJ=B~GBhzVpbJAXIs(77tXhA~NdDr&R zm=@$Y)v(X#nik}Fzscf>9X>_x*}}E=8+}j8z1xDg5<S`{tjR2<^({#+$q~3Hx1S$5 zt<#sGMC-R+&ee4wTUBbK_aT~NKuE@n%e25i-d7v*&*YBjM6}@T{3dt2Ke<fsv^=** z0C`)bHdylMnjqq@Hf;EX<^_>-JY1uZ!KAnNkQ7Y1stpYXbH52DtBKk$Y;*3*A><`I zVZmknv*d-2hQr&lQwN^*`<b<iI403>EtTDDN#RBh?cu6!q$3n0(azmSFSP-k*oi>k zKjR)<+@16vUDN|7uOF)|H_ZKOE&RQl?01P(QQbAMN>&^`-}*Ri0IO{lo}Fy|v{WO7 zv*kC)-)<_ObTzl8I}B?p+Hw3iZkI|eH{}1=#Fk?;swa7|y`krM^vOgqExw=}d+xD_ z_E7Gro}?M9+nJwx)kyZ^Y3>HP<~bIJ{5|x*b7W<2L%xIEw7J%`g<!rmo_KC6H)MQW z*Qd8@!0`QvU50xe@nxLz;|Wf4PeRLM82c4X>`ex$4c_TAuQyADxl3tfZ}Q5UhKxiO z2VecMM}atp7xVEI-lg4-WGAA>ZOH#r-Sya`wr(wiz}4paU9|C|J2`D#%@tj=?C2xb zw2e(zt8XFr_o#drh@<w+v8z?Z_V9*rY$n?<cMaXthm2Hhct~&dA$?TW9?%Yb$vds5 ziwWb?P|gFc^*h0myYwx(sV`YXUZ<`4k<O~AC3HwX@`B$$R-d<5vpG@ozbaJBjTd1x zUDc0_Zq^6P#Lf5CnIt&%9<As{hFRL9+C1OATS9+N%&X$L|BUWzM0DhuUprQzmOd9h z24{)Zu%55qAhD^x_4!NcfgTtW#Yae^qAP4vWT<G(M@6SKXn0bE)hE%6)u1ozwWzLd zop~K@847X}d})7S9JK}c8y@0@${^C#7P06sV_ZxuK3VrSGsEW5$)mWa_TfPKbAJ-l zX6mx8M?#W9QpLL!jd7ju6*CU~!r>|zwB-OYvCCA<SZjSklCsmpB4~8tmRxjSvoWw} z?#2!!aW*7V`vB6pbzGm2w0RG3!sr7j6_a{Pg|?iY9YCVn4O^RyR;~C5ZX&4Iur%BD z7FxC9GunsvJUW_=8AxX9aUg}SF6=bT8%SEI;$Eaz2BNn&7@+&vW7K63G4#mzGdpeI zX`Fz_N{7+-LP%O`T(W?~I64uX1{i}|l|#}z&oQ$y{&cwQH0U~omiu%}rymU>t@JT1 zS)nSYYX^~TdgM&P!P$S&^MgppsGT^B7bmx#)ViS1Rb<;{;#6029$bdV(E)#{0}^Y> zea2QLZ1u$14dxqLbrRC^e&b?UOR+n3kD3OfdG^W2tnZgXvA6aP=1GL&)%sFq#@q+= zyTPRQV8jz$udV&4iy7A=2$Alr5653M;;Sz^xknNP-F?ulgk;ucS<=UYel!G4HkyAd z(K_jRINCP)UJ?Br=5xMD8j-ZtR^5QXg=-V(=(S9`O}`3HIWVPoq=(Fr-huzHuhRRA zMV98?9YVILTrzf|qD4*pg{~h)QlCX}#@Siy$_v~v)tC?>s^6GRSD0<*{m@e5SkP&7 zoQb^J;xy`(>-K~67#hRG&HH;f%OgA9p?N0KLS1p_BrP-%%*pknR8(;?h2jXW_7<O= zE1uMS($1EbAN<OCsW_qgmCUzR#0;gH;_q-gM1S%z4q;s+-a<*5oXUmFvrB5r;`xg6 zJL2NCPyVtewU?za?eY0+)zA-}4U4*e_>1@`-@Hk@R*1oyFkI52tDp!Vjp|u~lzpfO zAVm>Cq7(pAuI{f7WA|N1TA5CEWWqT<S?16i^b4qM1nFpbyy__Ru>R>{x)<LCnc*`w zo(DUh2Rp0U4Y+U(cVFQ&GhE#zo5dD2PUlO(CJ$k*3nw#Q7=AKkF5a<P^WR%F+EI?e z8A6haBgMtTx2jrgu1CJw^qS?a>G5Ih*SB_>m(jZ;NVh>#|8ANNJCYaJUHUG_Y1y~< z?$(-L4#$Tzx=Z2Cm1Gx9VDY07FOXj>U07RrtjoHnEoawF|2p3`+E2^gms?KMyE-&( zs`85ZZ*gzUGni_9nC`lC`Wc+IwEA>P)Dca2<l$me;V4V`z(3fR=6+g~FKxi?@`~R- zZ#qjc(YEP>00f6xCrrJQa^uxHhnwNZgTtcx&)iKT$tS9o-jC!0&yJ^HJxNSu-Iu4b zJm5Ug4IiIlDhnB_?q3$dr?T9^+3)^rlc}s{SsV4v-T#=%^2zc0|DMWX#R5}Vw`ff` z2~%xYLQSJse+1{i$Be?e$TXQwnMm3UZAiwZ9^4XgE_czZ55z-<*?Kh9z_#6*HiD0D zvWDRN7M#+DBlv?!3CcvA>0`N|QTr=oVC(Nt50jFH`r#K{jN$BRP)#N7mC(`auaK@m zh^Kn+JI|CC%{|Uu@vL<<P1c@M_j4CUpKm^mmn$88ZXD@0V#5lYXgxw(BTfq4msE!J z#fz(G?EdaY#2GX(-IfqC#^GT-l73cw&>d5LFJq#|6a8Gn++6245=44pbcvU2t{N}d z^l<Z?vgSL|LU2AEjxoHO%?{#jPQo#Cd_=;~ned7FkUIC1mfd{aM?S)EMvO>l+e_j| zE8l5WYke>o^~gN6!mHhIiJR1h4j50mtBdBYqp{=33~#9~xy`Ry#X61=^!M>(ptq<f z&{I;BVW)u;$Zg};z1@%F?HG>ZSagZCB<wshU~))mFU(~n*7|Q7N!X|H>xw8;VHI%z zHBBS~R2kpW4<?dtj2T<|{)ca8SV-!?|LXf>SNg^zGN{$gFXU{N8f97q-ARF6>5fV0 z`ihRM09rbU9HSpiCf;NLoidqpC-1SJ4sF{XX*JFB==kt<q2~I_Lc0%B^Pxq2!+c6d zze-Haq@2%3&If96&gQkVbj_=zW57NX2)DE!=zbKCCGO|6&T?Vr{<i2{UBp20I@?DQ zCG@Aizlz>lQTbZ>%xmO@76lnsLQ+$r%7u(?LNM4w?g(>t(3!81DV=pXU&@Z~@%1?N zaGCx(&nMl1i_kQ5HG=()-hPd|Bc=L@-3%@-7`Bs6f1SJ}fk`_hFu#iCy-r@`c4zHN z^zVUbw1R7!XviC+@9QgJuPwk6lL`eL(@|PRYc+nWSmBQGK&w$ScMGtwS{0u28cyLT z_t9DvMO+<ew>RQG2X>Hq(av?*$tab0UR|tO<9bW%X+WCpdxN~<FQ!+_8U#=@cjfzk ziG3Xr%UW}Vbx8^~d`5e`Ndi0%K<m;wbh#5Jf!Jx(n`FA`a1lN6CV9hRb;S+QOUkXo z_Mu~iOS>^&*}B|1mEA(5!-#2sE&MaT;W#V_m&0J4yxe*?H#^O&*Lo40Vu)H?kiR<H z=BpDlzBnYNykg3OFWg-<4*jtz{~R~Lf47@(2%n<7s^z5}`<*b798y_{LsW4-*0HKo zx^W5_pspBtnqHkk#szmglf85%dSa!v;-5TfP{*wszHIGuc(4PKTy+RN8_}{(&88El zl7a4zRA>^u?4K~uT}RhUCH-*lqJNhgHQ4o1F|?dbul(^(LXf*_LchfOepzC|&+`v{ z;BwJcHwk8R!BTfY&yr?14F6aaD)*_f9tm4<QE5_mcbuJH#eDg9d7zVgo|q#=x94?9 z{_}A2$+9kL$j679TZ9wVJy}P`MiHIw0NGE{EXhy#?=&`w_*;g^jKG;<I&|q%jTwet z8x1&H)?73oJ={NG23D4h4_t5~gJ`%eR#KI}g`Kc2qk%yEyKHx1`OtGS<M;<|ccEgr zi#{dh&e3ceuEQIny&h=mBcIDro;TH+o$hc=tip_v$M9L~L{Iu9ipY{Lr+dES>)}yB zw%ui!i!*rW&}pbv1!K?C`O`?B<{LIa+h}cJar5jNbwFYja(pe_H;r`j8WwNc#ZDVW z{zQ6zky|;9^d>l6JB@~z$?(Ay9jw?GWy&uPNt4VAa5D2U)|y9YU06rFJa1>Af8gea zs1l)C96vJhA>9s(QoBA+v&_UlCd%ZK5O54*sq(y@mnA_QFkQ>W65ER<m0Q^TU<DZ~ z#9AdtHLcXau!gThwE~`_oW4!p%+h<=;ih?X$yWF3G}?1IS)hs>N;gj@V+Ts<n8NCB zegZU@v@Ug%{5x0H{(~D25zoA_dGTGvD;P9bMye&-q}rK(gm#N2UA@JOJJCr53wmbI z>CxoXfCBG7{5x;1T_m1CB{iac=&Y*)bvZ3g&=z7(dMCXejj>u1E3rw7>n33G3JHlF zktVGU7k7wylbRzkk&$Z2mDNHV+eAjH&dB_hBA=v(qV<a0@Qdqd<?cUTuu3JL&QFf# zemvm;x~jJhn?1C~_VcJShO~HAG$L%SWW=y|81aI(2j&EjQRkB+6{W0)muIP_MKNUK zq#~~p+q33YS_7*0!(ML1KRl|E1+xS(th|nYPCKpkKlTuH=%*PTUx)Yp*FUPY=+xjC z>W9Cb_3MD{i`QLyLV%YrJeI=cShmbg3ulmxQj#WBN@^Oe(v(>8NL{q!Bu#poys0k8 zI7Lg}CjY|WW2fnmnPj1A>OT6-OmtH^?xB}vlDUvbp`+d*c>^}wMZS&DcFtyvyUF=n z_7d}CXBYPV$9&)b++sOg+Xb)H8lKqe*yV4C<3?(lMc(UbvIgTK;y0W=l83w=xT-Q# z>vNkYhz{5Bh~ktM%_28d8!pl_vx&of3o=xD99Jw&b<)^5WRet}tQ8m8<vn>BHT3)( zGFSq2%O$Y<AKGdzNq#}h^Q;us!iOx`w-iP1c6M5?BS_;1=uW8ON5_wBhGU*OTxH1i zb+#^;jQFuq)jM?!(i|_pqM<+1>vPGA6U6sVSWJ0d`5&H`%kV=PhfNlE{RunH#1dcf z3G%aOeSYhbP2yf@ZD=U^u9D=IkIRcbxgbj(|A<M}|3E*DBmG}^ALTS}?k^!}!%xGC zN2gCpUP=}t^s}-T?P6xlp%Wo#-Agc$CYp01QXK8)?fjY6#*sy>ZK(J*TOR6NUip61 zyD+T=wJpc{0Zp4n`m1xs*3e_~NY4OKE_x4>%io2DM8kS}$LjbyHk#GY@}soXd@@h! z;%iFQMfn?dSNab)_P2SR-YX;x9q^)|t5b^bLJKszxp`12=&xX7Uinr8Jwp2oo^!l$ z$~6kUheMBMxld)K_<|c!`L{!(sm8nq3M8Z|re1mFmh`mY;^^aefBYkvk~GO8!g+Id zqo8<eJ0N+g%&%8;Na&hag@RJ_Ng_7tu<m{p-}~B*p&b$$hiW1>sDTaD*xS>bu5ZHz z2v~Qzyv`iMc5PK6Q8y->#2!e!K^w9ZOG}>EB;~SbLb?-Szlb>G{~D57{JOR>p^v7L zWm}IrDbSO5#XxUtWi3Y0`A1I%T2{>h#l@}7*-*__UsgabE+EgfZ-?h7Eei(cSXHS{ zV&j+Is5X063kfHO&`t|UTT41woon+fCI@ht^#P(jsS}D@=hpjrR%q)+7gweK!w{RN z*?lW1Zb-i;%fLtLtqtJcBjK&Lnce%VwfbGQ8<6}W<L7$wKj~^FnQ%fUWGyLPp4H&c z>mTe&N*4UBy86&-4tGuUKqtd`ha>|O^!0v!vR=ZUR~>Hdo}HY--HY8`e~{33iGZVW zi>4$*KS&60rzHzXmA6<HOGCu2U0!|?y}gJ$-&_O(izLwdQ#A^<jg~JW&$Tc<P{{>2 zrnAV;nOes@^{0E^B^}gx`3>~SyJXtnH7L$$w|{lGV<u`Xs+;?lzcd@u->m_&53Uve z9$kvA9#TH9XEh4PaIMSlxPz;dy%BRd-lgxpNB-?88Fm)i1amCcsc|uxE7{81)0_=g z=#Ir`IYhanI<a-kY51AeG=eb?C2;BmCfT<o=JGG{$`@8Gu@3+62i>`t^l7o}F)vVR zX>&TB%DwbH=}Xk7wIit4hosZ%8l5Q{8)&tr9%<<OHIENPWh<KVov%Kz)S=Uqvd$rj za7BJHI=I*-rztfr(GOAgPb@X-^+PZTk6jbE8oJLdK~BQbU!)|w_7zS0kPPfE#Tl(2 z)Z|Rv*6cXyOi8nF^IO#|WiPs)C9Wk);(CrT&!P`MBprRFg!oZnH*CsIpLd$}_=s4f z6pA^`gIY}wd_*eAUi$UNq*qA8@5N)a6{yi=*|tD7$63+Q+3Y^cnW@Fa&uA+Wf;44V zLsS)~QIAhZP|uI6eKz{}4|#y;PtC1Hn{xd7JkUt`hgpj?+AAnACB<=fuF$tWA%R^s z{Di((X|}CVKAXia;}PcOe!@(8iD!862lT5?NRKX-{Nfk1*JV?#*^Aq0i<mJDKNkCG zi<mLR%V|9f=@D10f+2k#=$IieNcYFrY}3InyV(Cvt&D@4fc7&}7flGIvn`}+$cE!+ zRhfs{Z;v8dY6s@8E6Tiq*7Ff>lBG9ZPR~DSo`uX*S?<toR<cxO*+utRNf%YeuW6B$ zbZsql4~F?Warwk_R*={e+RLA5=Xf%u-Qg^>p;%27OLHckU)HL|vC>{N^&VXpPd@J{ zDmW1(mBF3g7kg=Mpf6S9@X*wZOD@HExvUcuwm{?;HC&{zpAv&x;5^AawSa#1DRG8e zTY}bK+<l36cD3Z^L|e8XilJ}~>wx(iAHuA;U(>uqVx&tGNcWC89TT!`wyqI2^HD8M zzZ+YbZJWZTS%1)~aM$TMI>biar7tIvQ6U-k|5JUE_CMA4T+7tI*)b>oPE*a^zc<x9 zHJ52lYkv4M^GjMG+s3{FAz`fg%>9F2v5^--bB3j(a}us~TOggCS+L`0^qmcJkB6kb zmV61qV(+48=au47=m7WCJo@8jMDtNPl>BxLbM%-c_Wq5&l|*_BGIq*N_QN|3i$6l~ zIX%WZUKMQMoMod(Sd82oq{PZ<Kru8rXJ*;Pv67#&;Zu4hiNu5%{qWvH{yDTGYgiuc zOuz(2O-L$c^gCD9z(y5~W-sf2>EJpyT!XY2DV_!X3_bh>eK#5PMa;=0gP3zhqc$WR zO+zr-?=$b>k(p!+T?$1@mv*G-_)Sm0Kz~Rk{hN!PvN~d{#=<v7O-R@}n!b`k{F`62 zNr5D_mzjC5(3exlv)-bg6K%u+lhN=pO-UiWIGD%52zn}owCil0hZC(swQeyOBlI}S z5~eh1jyOieJ)>c`vtFRTCzH-b7J?L-4%>K{Aw?q+ww@M>)Act=I7VYqkgi<&a5_1a z1W5gNod*M%0;KQrbq7OIOLi5jJbo9G!9Mjtc53m9+N+Ls9;zcV+qUT@_#yhfp56bH zlHuQfI<9)Ca@`y7264lvOzbDwuhqbpNqZ$eY$g__SQ>9Q_3?$fh{WAp@7KMSjHVq1 zd0-oqR%9E6VCs@a-f=B>nO)y*@Q$Y+rI8*M>r~{eo0Nm0Jx~OlST3}gBeX7fwqGkn zDIBSF@pU`)0Bb_%d?91f&eYH)_+4^#zaKKp*JI29R%XQF`zxEOh_LM~#^l(KY?*kX zxG?hh*>dN#Z!vg#ki8UT%ctX`?Bpr7<tNPnHV-TkB|cdFCk)S$iskP~Z!u*?Q)hA5 zWVif5UtU7GPLsNNxNpF+Rh}i4=Z>U4TR<kZN^FsN((Vqnw<A7O%A72Gx`itjb|HQv zwAd4eZlaiJfoy<?c+<iqWS*x8g|VG$Ir-b^#HHk4ZS(5?LYny;{>|52^NX)eFvMds z0lqbGPT56|Eydi5!4L!}6;>OZ-D%ZQ(xXFB|B$8VF5+W1B$dtf6L`*scwnL0z}<0l zWIFl0zZg{M1&3BpAx#dW7kS}2QV&r#z{WB7(8aj9;Bny!D_{XcE~2fLk?&Q33+dHm zq;=OBwvbNlT2FK8tJ;KcPl3ILae^>d#}jhCB^ByOnt}xzi3Aq@z>`vi3<~PTfw& zWRN$ziiX7O5G9RPO~_IdNX(l)4mvA=>BxDNUd$kUeMRx4?aWsH;L3|Q<#qyXV<$mU zaWBn!%&@;wJV|>#Wa)1QrF7WxrJvXoZP|k4jQ9009=Uh0$>nW}SXz~YA)6}lFHzBD z8-<*xDF2!LKb*t<Pd$MD1>S}7%(1jX!WNEeKaI=Diye+*+Eg2wS)MmV+(_1R6$^*{ z(k;u0L2Zg^O;0T+ef3*a*qSuML^YqGc@o<(?LGwWEZ=LMq!pyW4;%XMVTF`X6b3lw zo~EyTM*8=J2LG<r{jQ7KeFoyQoWRP6ikI(<yJ;14wa=xAbHkXL4N2XBIfRBawAK?x zfV*M#9@}%=(4BTWbBnDk`**pGLsRjFa#~U`ZylzsWpiqpT2>r#@QKJvb?i;;Cq^#7 zF22k1rd*wn+w6}|t?%=jY9el8Ytc!>C|9q?9!$vMc9Z2W);c!1Zh}<UOH<&sI4ws| z!c1Cxf{52;9SLJe+R@(iFT6$S*y4i!u<9boLkzsK_)b0K=L&OOZ~bnLWP6Y>8Gdr< z3!+c^uzb%jDTB4gka~GTztH}baI%lEHd$YX`7_nyc?T&iElq<xoU-U13l`l$)ojd( z{BoF`e4lkA=?**feq7DO%GWBU+`;ZrtRy%RHJ8^#rJl5fqw86@RVa_lKe}#wX53mD zBz)?}ZfK2n?4y}4kRZ0Hu2~t|$<$vjSTm2Vo8_CPcTtD93E=c}@%`Y8x4DIzZfUF^ ziWwGqK7iFLUV4ylJW)uA@^!6zs0}}p<f}8Nas2#T`Vo$pZeujO)BP8zR35iT-a_BU zde^b9>HZa@CrPA#t{?_W(tGzm9>}&4U9q`_mgm*~Q~Zo;{kSB_qxOBtqjq7rPsV}c z-%99~cO`Vo>~f!;`){#xp~X3I^gq96?U}S0k~CdDG#aO@7C$7Ezr<x5_xgYPJ)vnU ziM!fRo=r1Xk~ymK5?12cJ}(wEw>?r(W5uleCls@Yrp3&FS*Ev<6>>4#VwH;7?^|iq zD$>GY6e3+q62dF%{H>Kus)khJ$_fsAsJN>0Te19EuwyE2YC*G-#}SbteSDr2>ChSF zKKYv-bJ<DIO;3S-?IzBFMv>HDyXlwo;VOc!`b;mlo|Ckc%#yT3o3Wa`t~_$*a%tNc zUNaR9_vY29U#xso-1c>BM)TGAxQKH&6&5;;l8|SN&LP@c4&9AMp<xn!n0`)i3y9G< zP#Z35`NA8^5Ju~O?)R66GxQ}<_6u)Z0%kPy@AUv%0(A@@*aUa>?R!|MA*zq|epCO` zAHjd`W{vm7*+Qjw1WeW7lMNLenh8q|*UN9SUh_IX(A5RrkGS{pU*m2~EP3sZ-)pbe zlvduZxO>NnnP)@oqT@T3mK+}*ZZ_w-p~#!qM=bi6@y4nSNq;Q?*9C>y=6pV4?^r*4 z6y)ojF(q@x3bT3kWxYdN-O&4!T*2(;rBff$6W>pU3TEP^yPy8G<u7yn_qu=l4F?#* zvFKg2Ct6$(zp!f4O5acFu`%^{13I783+w?0JQGqsU8}8X*te=}vR0^TX)Y=s=}U2C z#Dp^P5Eli0Kvh_jt-{4<6$i~3Rhs*6-j0h{n7z>L{o8Lx%*>wY_D<=CVIO6Er2BY| zt#o!*G`S0UScC&h-@!~xwd4MMy}z(akOI>lc;#^XOQ&-e*h>#C2r4>QyteeAHV~I- zskG|iRX_agOFi%jsKYNvyrUtTUeLdyC1I_GH#@2gL@!uX-1dI`w2M-)ow0uNhizNN zV8&vkkZ@wWkZ^pVpguWWSi4F`)>?4u_s<#UGTQnj3%_JMzdnm~v<!Lkv(qsyt2}~l zh4d9{-o>FG$)|)g?9l7V_|f>ttMZj>ac`kd)a&B;>FD+pO;Zo_6tv+9gVF`ZBv+O1 z8?Ga=(X}y;4<}}az8hO^h)DK(7a63B8Sc<p1QmK2f~Dau?(`LdjzZ93*uoK%PRy(I zAqhd}i1{;pkAxsk)ylKdDqq?BbuSb<^tZ*Ax2WpG(7U4H&fXY%wX=mw3yU-2+8xCy z4Hv?@uSF+kt>0487Tvj@Gjeqe!lH~{GSvMwmiukrSSlp+(^U5U=ni{hRqiTFCjN%X zs<jhR;UyKjr&EP<8M)d9VfL~M8R~wT`)yxM#nf%M(EI(<!mhF)A_h|Up4xHQZA9Cj zlS$iE$z<)yWMTBO{_u4x{iS8d)cb2M;{ye+WZ|!j3mI=5ZF?yV_YbELOIvg}FJ$zG zMe`(!?ytR)Cfv2{dUzYkCy~MW4`6kWASGETx(EJ-dR*sd2oSUA3SKMsF;2mBnXM~s zN>UY9ul_9|=p}*;hhpdA_&-Qh`>>6WenWdh$}XoVJ~Si^xR1dSjyrXvB>Y=s8j40i zG+g@=?;D9c9b08F2p!nO&Du#w6Qs*pq)RTQcaFUCX2!0ni3Dqw72x6O#9h_zkzX>d zBsB|{GzZ>gnS*Xr6-riXwidW{QW|t11zK9t1j##~|Kc_2&&OU3EfIsl?DUuRF0i*m zt?)T`98ZCGG93I32L<)Rlkwm^U}1PN9XySviFk@U_y@Q#c#1msF|c>>6nAg}o{|nO zumo8T&IWrGK>NW^JZ0g@dGJ-Rzr$0`!Fj+=;3@CmXgpoQQ^CP4ds#U?ll&Gh$yvUv zv&jnbupj&Gms+`!=H+=ima+amMH)JiN`0l&h>4eqs>u~AI~V(9r~mxj0=o`sg@c}7 zKQ7U_>ylWJu6UxhAUplzLzr=@>X&?Q*Mm1-Iz&zD$y9YwkL`5RdeY^K*mw;+v7VSz z<(brF0|`{kT}6!>NVMO3sAqcp_l`?tWZ$ZFBiIMU<vtBR(yumP-Ku~VZ6KZe&pRfS z;QAZ$8vOva18`s{`k#Md6@S2jfTEn)xG~i71MT7@oyZv)#(qxGSoU**ra7^T@)*r# zKi|_c>?e=jW<Q6h+eZ8xpdsvMAC1^Z+LJvrb|dNSns+|6`?pZKn{M7nI(7U8Te~8( z?O4bkvyk%<a=Qfq6<`0g3qm>c2875Cs@sI0uV^s)$)*wPXDgk>ezwqM?B@%bgP)EY z9h2@OGWUv^!snsTawzm1)a5+bi|+UaTCoY1d``W-z|UIRjs2{l5$q?E#(qIMJ}>pp z(@3Bny2|33fzWd+?$F+A3oo`msK1AcT<ibrwU?<UP(SSJMHdzX2svHxeg7vV^w<~J zOOeyz7VEgny_N-mrh8Idj81G+`(C-wa|3O+natB;;{xuZSWj1MCI(0COCA=!%P7a| zFwJE>-K{y|)OGb%;qiC?^)bim@rBHyT+m)NiGA-HYy7Z*in85C4=!g;vd=y6&gyl( zxGU5#{xS(%RX0H!+Vo<0*6Cum-Ur?gd_q$HvB=w;a=dc60o(XCr^VIW1nC+WX2kj3 zjWg+@5oI~dh+EtUeFih)-hVOT4DPI!jgYjx@#>LcHn^asEhGrbU5Dw+EoAZA|1_F& zce7+p@+vVsu5Z;b1D1X9qybwuFa!1;^($kkaPs4Q6lZ$~W-nJ2#u*idE7U=$Ds{h9 zg6_TcajIxT18Ju$(!xzRavWy?S&#gcO9y3<vHIH>YPsEBK<!ziO<30Xb(44BpgLBc zlL~g<G_TTok<oN7a%u0|Npv56_uaO)&rcaYSQ~)zQM?>%+bT-<^{pxyJHMP(WRa0w zu3c#H$I`fzSq|MmK^ud;V+GA_t&8&8!iILo$F#dwp0oa2@#Y#jYb)`#urb&-`Z4Qf zWyYmUUmCj$_n#EE{<(&AiPQ1@)V`JIg9;tbzIoxz!&DFZl20Bua6@|1A3SigS?*)W za$&=%=YM-Y7t2C9JuuG77TO;v9KYzGpBUG)DeS8*T-bJ7<m0BYY@zj$eud+szh&1^ zY!tWoq4$n^d;Cg}#C>gV$`^D>=s!WGdo1yN^)p_h#%=hTMsx&CVvbhyqiv+ia~=1G zq`KZ(7b``#WDtw)p$$^;j<8APfrlY_4v(Z4w~^7W!jZ7`<V!S*ZO93W(}u?v)2&FE z1Nu3crN7DFyOuI;%!}(r#OG(mw8L3r{zknpaGSZ~NtLvxkNHW+4WISbuYSz;^ZJ=| z;dat@EaHO?Yr^OaC`g<>5_A1a(8aFCZ6CyZ(N)Fb3qJ52OE>WYmQ*v_+8#7Whb*aa zD@fg>b*KEYA?G&JOl!82XP;*w^sA0~zsW72Y{~}jT~S@Y^HBGx6y%LTOHJ{kNvI9` zA)rE6`UZ{4CIi_J-oL+oGIeGXe|OQ>@jm4R9qQ=$Y;4vogSE*)Mr!&Jt@##a;ugR1 zibLWJ-qNs&ZME|}!WP1?miDYD=q?JDv_uW2bk>)oooZ(lP5Y8eb&nDIX1mL+-W9a+ zOEN<O!oDiEmfxf?Uy-?bQ6g&#z4{gDC^^D5<*NQo;)&!sk(!&fgY+OO%iY|4JIO0V z?d-5E*XwIC7*9i2&@tbT!D{CeJ5BqBB&ePB$+<qe$XKF|>lshq+Kr7EPOo3+s@>TC zAJ=nL?hm`kTGGPVqrmo3!n27rE59vH^sK-_q;tnU`q6j9qK?bR&#nKCya)w5_t7zX z$a|h~8JL39p2G;qIcFs;*h8LGJMU)E`aNW_s%IIUyqBC)J6~QwgZGiwam3Y6G-)51 zp?026rWO0J(cZbkMxWVF`l{o+Ptl0|WKr+9o@>poYTfE46}P`%i<O;66Y&B-^Ak1q zJ_$e{v;BP+?TZQ6EYDWC&I8Ai_TT%-3{QLg-3V!})&5BZjX8i_aP|&}-2rmMJ8JHV zkW_S7*(Qgmj<@I!A;dtF4wB;#`H8-Kh-^_u?L11W4q+)`Y&C6nn0%(P9HTjhNtI#4 z!xn6@sCKY^wP3zoruwvbygh>1gmY}xXX6f@sQpk@nn!~A$BkXz^>{)^;v;irh!#QF zKe0F6@{evyRKvaYOxq)K*(5bPt_eCe(I{7Z4)xj*d>hrVgnn>@j8#XC{hI!8goL(@ z%Ku87yjL+3iZAKw*-{wV*Qktm+U<MNYcORMZ0ir$rd+aBqiR8x`}*}2sECGNV(*0X zvwoLZ16{TklNsg~N8Pj%cgA^|`8^5X3Qqi21qDwi*riZ#iq?Ejx-tdW_n+75h4>(I zbhYMFzj%jZRe!-x#LG_i!;Ph$XXz!_`CZb#nXAQ7x{A31zo%~;B{QDCg4&XHW=le@ zoAC3=_|blApm0#cw|;B!JNWG3lu?7VeM!09>-z-k&f#r5$Ij=iI!0dXdjRFH=uqjn z)$@LPayO3Z!vKu_{8qBM8k$xfSAU;`iu+{kbB<H%G1954m{K#gA^N5AHt#YbrI7xs zY-)djBJ~`};j8GGW29Fv(Q)!KmECT(&G-gy{uM{1mQHF!C66baE4a5HJMDIy^wf*- zJ+YC#b({?BC??FwOfkXUt5)^byO@0Set9e)w3W$p-*MvaD++C0y$=?!{KHCl0<Abs zHg&82H9N6&iJy%{DSBpkT!$Z<_JOz=Nw7I7=^gs_DD0yY9lBT=GMYT-xr8OE`-g1H zs$UvQ=rfDrBiQ#DE6=KjxM3xSZCUjYY;K+0@Cj!z9T!1f@$)%+EkL=w(|)a<?u#J9 z)jrrbQy)QwdD?f}#}agxPEO?2{oIHbNl&$(&(ux5PKTtD@W990O6`^-)c!K+r_VJf z&3l<FQrmlcM+b~0X0`qNPP%?9Ijy#PeL-hNViS(t>vMW05`A#{-Bq;PD<sB?M=@C} z_}bh<C&&ob+@cdCN4u(kc&OuMFQOYSlGe@N?bV|XzWx{7#>Pd@`PYcihJXcbxo;GZ zJd!)|BB|BTxXYx5x?CXx+T8rOzCrg%eM28m49E$J0JiR9`q~u|=DTqIv^YcTv^mpf zMb9_HOnZA)^mMxa3b9e=6-?3vEEojl{DlG22O$hYaP<87bLSfZrgwE?!3#oD{%rHK zMGK;zH!cn^_Ubolv0?VYcc;y2bV?qckZ1ho8U-N#_M8O^8{N#q=qCe+jx#s14YL<4 zoVIY$f<{0JU_qQ{moR^t^eYEQi+)8@X1q!&M+7MSK(b##eFGy^vh;f?slFi??mPUK z@lv=<N&=np8|mydeeUdOZ_nvwo;xSz?HK}H^&5GP7XC(pz30EZ!2E7EWR_$CZTO8W zw`8uYZ`cf44>|z)4wM7R1_gpjK}Db|pr1hJL8n09gFGnm5F~&euBvaS1l<MQ1YHMR z1ziFafX;x9fewN8fW85JwW{9IQ0aij|1+}uUj~k_E&pdRmm+RqNuPzhaDvu=R)Cg) zQb3=AJ_5Z9iUZ9AO$SW@O$LnvjRA#$OrSxaKA;|;U{GgJJ5WoI56BIq0o9}I)PVk8 zS+8!mvyxW*K{{K!0JwlupxPDn4Q=q-9Ap431to#3pbtO`L32Q{ptnG8fF^<>K@p&l zprN1vpyxo{Krtw!QJ~jA<3TTjMuA3vhENp5et0l~LO@+W9YC!>&wxIIf~4j34ZH0y z0>2-C7J}x0VnJ_#-T+MmMS>zgBSAw!13=G#x`6^g22dMNbC4Iv1*BSzf>;aXRiFyc zZO{$S@1R1^MbI}e;t*&L=n|*^bOv+`WC96D+K2e91l<MQ1YHMR1qC7!22dMNbC4Iv z1*8Ji!e5mI|0+PYK{r6Zg9<?xK|g>_fV_|`|664F-%|WP)NA=)i`oCo0ObGwL@*E8 zun)8wv;(vav<b8hv<hSgrGXMbpMVyF=7VN|Vn9)#*FfVzFM~#bMu3I{;9ozG5flRI z0_p&21$qYL0n)9a-<6=M2BScA2DJmV1oZ*+01X0Vft;W<pcSBHpcK%jppQWBg5p3k zLDNA~K$Ah^Kx05*AS(*x2cU(ZIiOh3Tc9^U6IoS_#6tvVBxopT0O&bTH_%s5;6#-= zj~cTEzbin?Kq;V4K_7wM1;v48f~JF}fF^^+fyRKsKqk;2P#;haP%x-7sNHe{VvPqM zkQ+z?sz+copua(P?9{82ylaVDiy8znu7lN}k3cQq4g=lAa}7xIIkFvO1Qo!20~E0V z#(-vll0Z(-ZqOjmtWC&TkR7xc^cCm~=nkl8BU0=OGz0hxp0kL)P)6b{XTC!0IQjhh z@iW7k@|e37=bvce#{jGiXdSSpQO(nNGycO9Q`r2!C~S(2Y{F;C|C<3#1FiWgH~S{p zprfnH$$4t2Aj>=E+(%-7?t+9WbZPKg3yK9@y<eZ(^Bz9kr+L^q7|^+)!3o+8D&oI6 z_-#tNJLj&yPeu@Gs3v8!V+}c(TUCP({c}qm;v;tVg|X4orgvKy+pF($R9#0xJD6v^ zGb4Hqj=@?O({0+~xL$pY{fyn`N6%O^YuduO^XbGol9pRjN461K*g!n#xdt+9NJV-> z!$Xi}Swn*ts1>L)s2ivs$OMW2jR#Eu#e#~`8X7(WQ4>)GTduEcK=%xmgFXYz2aN+g z2YLqd_lkywi=Z6PGSDng1gH&dXsB7$fUX)+0rVLt4)ijp8*_ud5Ht}q0F(m?hQCTj z1K!@S7BVPvw2Y{F(V{A1qL)sPb}njxu%c+C7fz7otD0#vM8zI`*(1)Q5(JV9r?<+| zO3+y06x8gW^k-#1!Ggw$pT~HV#tNr!Q!V|<@;iJ45jSX_#1C;iSb=}jQj#}HxTP@w z4hRDj9%MX9fnV<~DT-C#l^nM#@E`h1@|jQISkQGDC^4K*Fa+V$010=|J{dCucehgD z75yYRnJ=f1VWgxdSRtPm1{~oFMg?OrW8iOq0@rceq`=d70NBAV`jZ3B;dqn+Zy7G> zj|I-cZ_I*qp%Nob;o%LAFIM0;xItD0-jB<tDDWyzA{uCajEmt5+c+chF@xD9%<mw1 za4PUfj&D=omw1eG6u62Tv`2xvowrCa&QmaMo|EuX3VaL?pg@7o<anV14{oiovWOHZ zaF;d`e$%30?BR?G1wN{+<e^4^Pi`mSf=>FC^FFw}<aSZut+?BV|5{`ZMS97DEFj~j zxZo(IqS3q%DR9$^ybLMu%^ctMByJHNaz!~$GK3h8%Ld2>749ci0e>cLu#7*$0~n>i zzmJd%nykPJMI7ZXO2P0sAO%pMz<(bjDJoRpu^g9EP&O#iMM_a0{ww3zFG+fW8P3YT z%oxKJ85JH39G4Z!@-;jq;uP|-d+}3nYZF|oe?=TfX~KYALMvBTh(EgmkK}l!0>}O2 zqP$aq=W%?S0<Uq^SVRvw3Wm{5QnW{b$8tPRfjc>VN`V)0yg-5bxJ&vAT`baroTQ^T zL*N$6xMr%9XFdwNbc%!zP~ZkGKT3fQ;dq>ki|H>2Lf|w>kyT~@V}C9j-=@HCa(s^h zAHne=1%BWyNq>a`w`e${6;CQT#)TXYR^aW-lA=fjzM11u3Vb2QQxtg8bWx8*$W$;+ zaSwS4d_c4maDf8fBq<VV6!<AF@4~ZGj@T5A8$}%HFEj3PhDqTeDn<%8R)K#IE8&Y3 zcqGTI3f#)yDfXr~%m2kcNDYp&DFe8b<2edEh2wb&Jd@)E3Va*KixhYcaIySVC>VRV z2f;&Qg&mD4l*e%&1%8U-1_fToaiapi$#Ihhum5ruRB%S5!b1(mCoAw{-%D8#^%Okz zDR^8XF4w=KJTDs=EZ%}32Vi{)p7Ipj{uDe@iCcIKoJt1Aw><^Vc?!PgDR|yfaLcKu zFbbZ67d{0qdJ2B?DR_m1Kkom_NnP_44}z!0%CbZ*E3&-HQ*fWB;H{p(k^izH!xN0h zc<@tj<5Tbf3OtIJEt3Kl!)4`f)PG|Dk9-O~S%JrLca#FJ;cl5Pr(7n-<M1n%f0=Rd zJg)@`MWf!68mJTnF1zhd!R3Ht{h8-^L-$x-s(-=x5@XwA2Fq$;8~2c-z-9S83VaWj z&r{&7xc*ZLyx_b=3b;VQaN!;b6?g>?phkhW^3qtD!2<u4W1LbTrKpPnkNH3fx0OY~ z7{CJ_punx%ZBpO`+&xNx3*H(lvnNu48#z9i<Bk3QD9(sdc(8LkR)H6AJWhcNK2pGo z6?i1a<%sYUvIurAn4<8o=0_>%WWJoFrUG7w6!MWA&r#q}94}DdMWP$!uTa6b$^(`J z9yd6Pxgt4fWrKWpO1SV}8Sn6u6akqp<0)K^k;$|2FEdPB!2pG#6<m?bm*q3LB01}1 zJdXR1Rp`0I^~iiCFV?>tuE?(N;N<R11>WjsDP}TX4mgkN$x+A`a=bu+7kTsdf1!f$ z8V>*hRv6isLN~c0xsb^L`|!f$!hdCaJ=Y`iWxR&#G5A=X3_!Rb6)vMfkq^h^QY<Tq z;&PJ}@^^T^GGCSt=6am6yqNxoVc;bxNpoZdi#Glya=cK154|kO-&Ei;IquU;V`cv3 zfN@}y=pLoOEj@mb7?Tx@w>WN9;8!_rSKzuUlAb*Z{2Pv+QsCDm+#*yc7+zN;MZz;0 z(PB9UR*nZN@SDF$@&gojyWb@|R)JeN9w*{Re>s3Y*CY>%6&@<M+p55Gd?f=@6u6V) zb_Fgpm*g{>;w=B;IK$bL0o>R^@~};T8#rE|z`I<RB2cKnw{cvKh#X-7hKl8{M&Y3! z_rL^TM`H?EdFeGN@a82_z@rrSi5n7bRp56yp5m+M|2sLusqm1;-P;s+t70htIV<Ft z6>+&ePm!1P;Xp_hKfD8Y%w&(UqMIC-^G?Qnc$T^FUl}*?l#ux{F1PuS3i(YI?qRZm zv76&^*^m`kx!hufd?A;Y`LcWsk3gP6e*R4<<t(QZj6lv1cp;M&z07eJ1uk%0E|jvo ziOU5m<lB@=0rSHBxc?u;1qVFEgRHPIfQwR7E&G%4NRC?-dQ!Ms{*~mB|CyX&S9tjT zPsvi5F9)!V2e3yWFT3-ef}c|0Ma<30Ux9*QXbA@X3Ke)1$3>6e%LWM?zp0S-@s?7k zLV>F|&iskxUsmMvqvSz0K*mixfMEVB<MX*9qXJKCE$NZ{$?^lZ{;0?DQvI`XAoekX zC4k`7Oi~o5zzcmOT=pjiFp39YSI9?k+^N9he&qfC90enVd)TAE5AlF26!;iF$soZ` zV`YZR2KjKeivqWEw~s}^FmiV*1wN>w6o5g2?^z+?vOhUsd8jo&Az#BIX5x5b|6d+z zje3fQNChqrwI(ZYIe;hyeu@Vi_atr+ia5jmBtyvI?o0)Kil>BAfj>v3l#u<&DIxqM z;ROo$o1z=#uTa4-w370$NP$o0xa9G19t#|=P{@z!Bn4cfz}s||aM2$t|FWf%xuvoJ zGCqI@5X^sNTn@mfz~AU11t9yA<>R=1IU)=f>t7sK5T#Hg2N0{k7Y9fI$o}L2tULg# zLf*-7rxI_)-~ZeGlVOE%f(u_hlLHW@N&)*Q@GDa!yp;m4;JCaDC+o@Na*?eq8ZjW5 zfnza6_hN-26USwbvi#&<BzdPo-l>-K7Ao+?Tu+gVi|H>2!kTH4q8gb2jQwTu^+19D z$^nmzlH}!uL>XVq<$@LR>v+He6u9LSR}`yYoRCvOft#*K0n3KT0q5|P*rSjS<_nia z3j7(fsK+9>Z~<9S?ORd+vPT)W@_^-)P#NFD{ZCft87~DO$b4De&h?xU<&pj}1ACjT zEXfNMigGx9Q-Po2cragrmG!*E12!t~wCR%mQB84{|1Ny3HnJ%L_$sbQUgMPmIK|~+ z74j)uK1G2K;sMM4WIe`T`TT#O!h;D6gfHAw;FCEn8!RhY%yD_qS;jNvi1A+;|2taJ zKe@f4|Nj$bELM1s7nQ9FTwYYREAT$=NCC(O$N}%+h88u-^ZNJEA|B&L28+KSba_|8 zD-`%PE^p|du`)S1fb91r`CtWJ$#J6s&$DpGWCi2=hmxXL1wM%5b_Kre14%wpfq%#G z0tH^<kmN0e3dZCW5<}?tWXy)Fl5iIV{vF4C6u9he^%UG7;g9?OMlKkv@DRn_GG9(= z6UPTA<R7k<43+t^{N&#xT-GPy77V|+f>?#3g3l!nGGA5{%N1D_@>Y)TF+7=aK1I?v zB2R&f^9%@IIHhDfCn+dU;3<5Wu26wb=9AGy3Vf7W(sNURFIGu-1#lLAW9i+)w@TE2 z0k{!2@R$mnp2X!Ym5Tzm@_>C5cnS}=)ni-?Ux*SU#fHZW1dptlD&fHjJcS2fRN(UV zn*j=3UWzp-@Eo;83Sg9iVds`cDsXvFIaz@VypI>9#C1~M$0~4ndOyyhV4UJHUaY{a zyi8aXxRIwsiUJSjxLtwUIiAV!@oJXN_{-!Br@})HU$x$*z~#|wjsj<!OOh`PR^?t8 ztPVexTQpcz*qeyk5Zhtzq0hI$nl36&7N86Jhd)Al;bseZ75fxvqDm9Tb|}(Xou(Vn zvmJ|m6dQd8P^(_!(ws;>ga-VvM-_-})@znh>_mwrnq1<lxkhp;+%&POT&!*xRTSSz zxll|a$BIwTotJBTMl)HLYj35AC%N%$G!{*+y`#p3P?Y*YCgqb`&`A@gVUKodY^HG; zQNU|9wy(}*;ojtKEpy=S6nE<+r;y3rZrn`-nii<(*{n!vP~@1q1k*EtnjWez35)1N SjkBZ1%64UH2%Q|PG5kNOYE9h$ diff --git a/roms/opensbi b/roms/opensbi index a2b255b889..455de672dd 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit a2b255b88918715173942f2c5e1f97ac9e90c877 +Subproject commit 455de672dd7c2aa1992df54dfb08dc11abbc1b1a From 5669d26ec614b3f4c56cf1489b9095ed327938b1 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 16 Jul 2024 11:30:32 +0100 Subject: [PATCH 2215/2884] target/arm: Fix handling of LDAPR/STLR with negative offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we converted the LDAPR/STLR instructions to decodetree we accidentally introduced a regression where the offset is negative. The 9-bit immediate field is signed, and the old hand decoder correctly used sextract32() to get it out of the insn word, but the ldapr_stlr_i pattern in the decode file used "imm:9" instead of "imm:s9", so it treated the field as unsigned. Fix the pattern to treat the field as a signed immediate. Cc: qemu-stable@nongnu.org Fixes: 2521b6073b7 ("target/arm: Convert LDAPR/STLR (imm) to decodetree") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2419 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240709134504.3500007-2-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 2922de700c..62df4c4ceb 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -520,7 +520,7 @@ LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 LDRA 11 111 0 00 m:1 . 1 ......... w:1 1 rn:5 rt:5 imm=%ldra_imm &ldapr_stlr_i rn rt imm sz sign ext -@ldapr_stlr_i .. ...... .. . imm:9 .. rn:5 rt:5 &ldapr_stlr_i +@ldapr_stlr_i .. ...... .. . imm:s9 .. rn:5 rt:5 &ldapr_stlr_i STLR_i sz:2 011001 00 0 ......... 00 ..... ..... @ldapr_stlr_i sign=0 ext=0 LDAPR_i sz:2 011001 01 0 ......... 00 ..... ..... @ldapr_stlr_i sign=0 ext=0 LDAPR_i 00 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=0 From 25489b521b61b874c4c6583956db0012a3674e3a Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 16 Jul 2024 11:30:32 +0100 Subject: [PATCH 2216/2884] target/arm: LDAPR should honour SCTLR_ELx.nAA In commit c1a1f80518d360b when we added the FEAT_LSE2 relaxations to the alignment requirements for atomic and ordered loads and stores, we didn't quite get it right for LDAPR/LDAPRH/LDAPRB with no immediate offset. These instructions were handled in the old decoder as part of disas_ldst_atomic(), but unlike all the other insns that function decoded (LDADD, LDCLR, etc) these insns are "ordered", not "atomic", so they should be using check_ordered_align() rather than check_atomic_align(). Commit c1a1f80518d360b used check_atomic_align() regardless for everything in disas_ldst_atomic(). We then carried that incorrect check over in the decodetree conversion, where LDAPR/LDAPRH/LDAPRB are now handled by trans_LDAPR(). The effect is that when FEAT_LSE2 is implemented, these instructions don't honour the SCTLR_ELx.nAA bit and will generate alignment faults when they should not. (The LDAPR insns with an immediate offset were in disas_ldst_ldapr_stlr() and then in trans_LDAPR_i() and trans_STLR_i(), and have always used the correct check_ordered_align().) Use check_ordered_align() in trans_LDAPR(). Cc: qemu-stable@nongnu.org Fixes: c1a1f80518d360b ("target/arm: Relax ordered/atomic alignment checks for LSE2") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240709134504.3500007-3-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 559a6cd799..148be2826e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3543,7 +3543,7 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) if (a->rn == 31) { gen_check_sp_alignment(s); } - mop = check_atomic_align(s, a->rn, a->sz); + mop = check_ordered_align(s, a->rn, 0, false, a->sz); clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, a->rn != 31, mop); /* From 345acc443905eda8008a1d328dd89b73c4a3f89e Mon Sep 17 00:00:00 2001 From: SamJakob <me@samjakob.com> Date: Sat, 13 Jul 2024 17:03:53 +0100 Subject: [PATCH 2217/2884] hw/display/bcm2835_fb: fix fb_use_offsets condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is common practice when implementing double-buffering on VideoCore to do so by multiplying the height of the virtual buffer by the number of virtual screens desired (i.e., two - in the case of double-bufferring). At present, this won't work in QEMU because the logic in fb_use_offsets require that both the virtual width and height exceed their physical counterparts. This appears to be unintentional/a typo and indeed the comment states; "Experimentally, the hardware seems to do this only if the viewport size is larger than the physical screen". The viewport/virtual size would be larger than the physical size if either virtual dimension were larger than their physical counterparts and not necessarily both. Signed-off-by: SamJakob <me@samjakob.com> Message-id: 20240713160353.62410-1-me@samjakob.com Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/display/bcm2835_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index e40ed2d2e1..650db3da82 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -145,7 +145,7 @@ static bool fb_use_offsets(BCM2835FBConfig *config) * viewport size is larger than the physical screen. (It doesn't * prevent the guest setting this silly viewport setting, though...) */ - return config->xres_virtual > config->xres && + return config->xres_virtual > config->xres || config->yres_virtual > config->yres; } From bde809f05f66b4be4475ffa9819d82a01686d1c7 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:01 +0000 Subject: [PATCH 2218/2884] hw/arm/smmu-common: Add missing size check for stage-1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the SMMU architecture specification (ARM IHI 0070 F.b), in “3.4 Address sizes” The address output from the translation causes a stage 1 Address Size fault if it exceeds the range of the effective IPA size for the given CD. However, this check was missing. There is already a similar check for stage-2 against effective PA. Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Message-id: 20240715084519.1189624-2-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index b6601cc102..e81b684d06 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -381,6 +381,16 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, goto error; } + /* + * The address output from the translation causes a stage 1 Address + * Size fault if it exceeds the range of the effective IPA size for + * the given CD. + */ + if (gpa >= (1ULL << cfg->oas)) { + info->type = SMMU_PTW_ERR_ADDR_SIZE; + goto error; + } + tlbe->entry.translated_addr = gpa; tlbe->entry.iova = iova & ~mask; tlbe->entry.addr_mask = mask; From 48f9e9eb2914cf1ccd67bf7a011d2706490d81f0 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:02 +0000 Subject: [PATCH 2219/2884] hw/arm/smmu: Fix IPA for stage-2 events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the following events (ARM IHI 0070 F.b - 7.3 Event records): - F_TRANSLATION - F_ACCESS - F_PERMISSION - F_ADDR_SIZE If fault occurs at stage 2, S2 == 1 and: - If translating an IPA for a transaction (whether by input to stage 2-only configuration, or after successful stage 1 translation), CLASS == IN, and IPA is provided. At the moment only CLASS == IN is used which indicates input translation. However, this was not implemented correctly, as for stage 2, the code only sets the S2 bit but not the IPA. This field has the same bits as FetchAddr in F_WALK_EABT which is populated correctly, so we don’t change that. The setting of this field should be done from the walker as the IPA address wouldn't be known in case of nesting. For stage 1, the spec says: If fault occurs at stage 1, S2 == 0 and: CLASS == IN, IPA is UNKNOWN. So, no need to set it to for stage 1, as ptw_info is initialised by zero in smmuv3_translate(). Fixes: e703f7076a “hw/arm/smmuv3: Add page table walk for stage-2” Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Message-id: 20240715084519.1189624-3-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 10 ++++++---- hw/arm/smmuv3.c | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index e81b684d06..e8cdbcd8ae 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -448,7 +448,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, */ if (ipa >= (1ULL << inputsize)) { info->type = SMMU_PTW_ERR_TRANSLATION; - goto error; + goto error_ipa; } while (level < VMSA_LEVELS) { @@ -494,13 +494,13 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, */ if (!PTE_AF(pte) && !cfg->s2cfg.affd) { info->type = SMMU_PTW_ERR_ACCESS; - goto error; + goto error_ipa; } s2ap = PTE_AP(pte); if (is_permission_fault_s2(s2ap, perm)) { info->type = SMMU_PTW_ERR_PERMISSION; - goto error; + goto error_ipa; } /* @@ -509,7 +509,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, */ if (gpa >= (1ULL << cfg->s2cfg.eff_ps)) { info->type = SMMU_PTW_ERR_ADDR_SIZE; - goto error; + goto error_ipa; } tlbe->entry.translated_addr = gpa; @@ -522,6 +522,8 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, } info->type = SMMU_PTW_ERR_TRANSLATION; +error_ipa: + info->addr = ipa; error: info->stage = 2; tlbe->entry.perm = IOMMU_NONE; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 445e04ddf7..cab545a0b4 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -949,6 +949,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_TRANSLATION; event.u.f_translation.addr = addr; + event.u.f_translation.addr2 = ptw_info.addr; event.u.f_translation.rnw = flag & 0x1; } break; @@ -956,6 +957,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_ADDR_SIZE; event.u.f_addr_size.addr = addr; + event.u.f_addr_size.addr2 = ptw_info.addr; event.u.f_addr_size.rnw = flag & 0x1; } break; @@ -963,6 +965,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_ACCESS; event.u.f_access.addr = addr; + event.u.f_access.addr2 = ptw_info.addr; event.u.f_access.rnw = flag & 0x1; } break; @@ -970,6 +973,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; + event.u.f_permission.addr2 = ptw_info.addr; event.u.f_permission.rnw = flag & 0x1; } break; From 2731ea049d13dfed57f4b0b8bf38725321f752f9 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:03 +0000 Subject: [PATCH 2220/2884] hw/arm/smmuv3: Fix encoding of CLASS in events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMMUv3 spec (ARM IHI 0070 F.b - 7.3 Event records) defines the class of events faults as: CLASS: The class of the operation that caused the fault: - 0b00: CD, CD fetch. - 0b01: TTD, Stage 1 translation table fetch. - 0b10: IN, Input address However, this value was not set and left as 0 which means CD and not IN (0b10). Another problem was that stage-2 class is considered IN not TT for EABT, according to the spec: Translation of an IPA after successful stage 1 translation (or, in stage 2-only configuration, an input IPA) - S2 == 1 (stage 2), CLASS == IN (Input to stage) This would change soon when nested translations are supported. While at it, add an enum for class as it would be used for nesting. However, at the moment stage-1 and stage-2 use the same class values, except for EABT. Fixes: 9bde7f0674 “hw/arm/smmuv3: Implement translate callback” Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Message-id: 20240715084519.1189624-4-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-internal.h | 6 ++++++ hw/arm/smmuv3.c | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index e4dd11e1e6..0f3ecec804 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -32,6 +32,12 @@ typedef enum SMMUTranslationStatus { SMMU_TRANS_SUCCESS, } SMMUTranslationStatus; +typedef enum SMMUTranslationClass { + SMMU_CLASS_CD, + SMMU_CLASS_TT, + SMMU_CLASS_IN, +} SMMUTranslationClass; + /* MMIO Registers */ REG32(IDR0, 0x0) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index cab545a0b4..472fdf2e5f 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -942,7 +942,9 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.type = SMMU_EVT_F_WALK_EABT; event.u.f_walk_eabt.addr = addr; event.u.f_walk_eabt.rnw = flag & 0x1; - event.u.f_walk_eabt.class = 0x1; + /* Stage-2 (only) is class IN while stage-1 is class TT */ + event.u.f_walk_eabt.class = (ptw_info.stage == 2) ? + SMMU_CLASS_IN : SMMU_CLASS_TT; event.u.f_walk_eabt.addr2 = ptw_info.addr; break; case SMMU_PTW_ERR_TRANSLATION: @@ -950,6 +952,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.type = SMMU_EVT_F_TRANSLATION; event.u.f_translation.addr = addr; event.u.f_translation.addr2 = ptw_info.addr; + event.u.f_translation.class = SMMU_CLASS_IN; event.u.f_translation.rnw = flag & 0x1; } break; @@ -958,6 +961,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.type = SMMU_EVT_F_ADDR_SIZE; event.u.f_addr_size.addr = addr; event.u.f_addr_size.addr2 = ptw_info.addr; + event.u.f_translation.class = SMMU_CLASS_IN; event.u.f_addr_size.rnw = flag & 0x1; } break; @@ -966,6 +970,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.type = SMMU_EVT_F_ACCESS; event.u.f_access.addr = addr; event.u.f_access.addr2 = ptw_info.addr; + event.u.f_translation.class = SMMU_CLASS_IN; event.u.f_access.rnw = flag & 0x1; } break; @@ -974,6 +979,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; event.u.f_permission.addr2 = ptw_info.addr; + event.u.f_translation.class = SMMU_CLASS_IN; event.u.f_permission.rnw = flag & 0x1; } break; From f6cc198050faf055b17105fdf4da63cf4d295765 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:04 +0000 Subject: [PATCH 2221/2884] hw/arm/smmu: Use enum for SMMU stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, translation stage is represented as an int, where 1 is stage-1 and 2 is stage-2, when nested is added, 3 would be confusing to represent nesting, so we use an enum instead. While keeping the same values, this is useful for: - Doing tricks with bit masks, where BIT(0) is stage-1 and BIT(1) is stage-2 and both is nested. - Tracing, as stage is printed as int. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Message-id: 20240715084519.1189624-5-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 14 +++++++------- hw/arm/smmuv3.c | 17 +++++++++-------- include/hw/arm/smmu-common.h | 11 +++++++++-- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index e8cdbcd8ae..408fc4efca 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -304,7 +304,7 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { dma_addr_t baseaddr, indexmask; - int stage = cfg->stage; + SMMUStage stage = cfg->stage; SMMUTransTableInfo *tt = select_tt(cfg, iova); uint8_t level, granule_sz, inputsize, stride; @@ -402,7 +402,7 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, info->type = SMMU_PTW_ERR_TRANSLATION; error: - info->stage = 1; + info->stage = SMMU_STAGE_1; tlbe->entry.perm = IOMMU_NONE; return -EINVAL; } @@ -425,7 +425,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, dma_addr_t ipa, IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { - const int stage = 2; + const SMMUStage stage = SMMU_STAGE_2; int granule_sz = cfg->s2cfg.granule_sz; /* ARM DDI0487I.a: Table D8-7. */ int inputsize = 64 - cfg->s2cfg.tsz; @@ -525,7 +525,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, error_ipa: info->addr = ipa; error: - info->stage = 2; + info->stage = SMMU_STAGE_2; tlbe->entry.perm = IOMMU_NONE; return -EINVAL; } @@ -544,9 +544,9 @@ error: int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { - if (cfg->stage == 1) { + if (cfg->stage == SMMU_STAGE_1) { return smmu_ptw_64_s1(cfg, iova, perm, tlbe, info); - } else if (cfg->stage == 2) { + } else if (cfg->stage == SMMU_STAGE_2) { /* * If bypassing stage 1(or unimplemented), the input address is passed * directly to stage 2 as IPA. If the input address of a transaction @@ -555,7 +555,7 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, */ if (iova >= (1ULL << cfg->oas)) { info->type = SMMU_PTW_ERR_ADDR_SIZE; - info->stage = 1; + info->stage = SMMU_STAGE_1; tlbe->entry.perm = IOMMU_NONE; return -EINVAL; } diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 472fdf2e5f..aab09df23e 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -34,7 +34,8 @@ #include "smmuv3-internal.h" #include "smmu-internal.h" -#define PTW_RECORD_FAULT(cfg) (((cfg)->stage == 1) ? (cfg)->record_faults : \ +#define PTW_RECORD_FAULT(cfg) (((cfg)->stage == SMMU_STAGE_1) ? \ + (cfg)->record_faults : \ (cfg)->s2cfg.record_faults) /** @@ -402,7 +403,7 @@ static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran) static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) { - cfg->stage = 2; + cfg->stage = SMMU_STAGE_2; if (STE_S2AA64(ste) == 0x0) { qemu_log_mask(LOG_UNIMP, @@ -678,7 +679,7 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) /* we support only those at the moment */ cfg->aa64 = true; - cfg->stage = 1; + cfg->stage = SMMU_STAGE_1; cfg->oas = oas2bits(CD_IPS(cd)); cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); @@ -762,7 +763,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, return ret; } - if (cfg->aborted || cfg->bypassed || (cfg->stage == 2)) { + if (cfg->aborted || cfg->bypassed || (cfg->stage == SMMU_STAGE_2)) { return 0; } @@ -882,7 +883,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto epilogue; } - if (cfg->stage == 1) { + if (cfg->stage == SMMU_STAGE_1) { /* Select stage1 translation table. */ tt = select_tt(cfg, addr); if (!tt) { @@ -919,7 +920,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, * nesting is not supported. So it is sufficient to check the * translation stage to know the TLB stage for now. */ - event.u.f_walk_eabt.s2 = (cfg->stage == 2); + event.u.f_walk_eabt.s2 = (cfg->stage == SMMU_STAGE_2); if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; @@ -935,7 +936,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { /* All faults from PTW has S2 field. */ - event.u.f_walk_eabt.s2 = (ptw_info.stage == 2); + event.u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2); g_free(cached_entry); switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: @@ -943,7 +944,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.u.f_walk_eabt.addr = addr; event.u.f_walk_eabt.rnw = flag & 0x1; /* Stage-2 (only) is class IN while stage-1 is class TT */ - event.u.f_walk_eabt.class = (ptw_info.stage == 2) ? + event.u.f_walk_eabt.class = (ptw_info.stage == SMMU_STAGE_2) ? SMMU_CLASS_IN : SMMU_CLASS_TT; event.u.f_walk_eabt.addr2 = ptw_info.addr; break; diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 687b7ca132..260582089e 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -49,8 +49,15 @@ typedef enum { SMMU_PTW_ERR_PERMISSION, /* Permission fault */ } SMMUPTWEventType; +/* SMMU Stage */ +typedef enum { + SMMU_STAGE_1 = 1, + SMMU_STAGE_2, + SMMU_NESTED, +} SMMUStage; + typedef struct SMMUPTWEventInfo { - int stage; + SMMUStage stage; SMMUPTWEventType type; dma_addr_t addr; /* fetched address that induced an abort, if any */ } SMMUPTWEventInfo; @@ -88,7 +95,7 @@ typedef struct SMMUS2Cfg { */ typedef struct SMMUTransCfg { /* Shared fields between stage-1 and stage-2. */ - int stage; /* translation stage */ + SMMUStage stage; /* translation stage */ bool disabled; /* smmu is disabled */ bool bypassed; /* translation is bypassed */ bool aborted; /* translation is aborted */ From a9e3f4c1ebeead57008ebe1c0e9f4e50d5020105 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:05 +0000 Subject: [PATCH 2222/2884] hw/arm/smmu: Split smmuv3_translate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smmuv3_translate() does everything from STE/CD parsing to TLB lookup and PTW. Soon, when nesting is supported, stage-1 data (tt, CD) needs to be translated using stage-2. Split smmuv3_translate() to 3 functions: - smmu_translate(): in smmu-common.c, which does the TLB lookup, PTW, TLB insertion, all the functions are already there, this just puts them together. This also simplifies the code as it consolidates event generation in case of TLB lookup permission failure or in TT selection. - smmuv3_do_translate(): in smmuv3.c, Calls smmu_translate() and does the event population in case of errors. - smmuv3_translate(), now calls smmuv3_do_translate() for translation while the rest is the same. Also, add stage in trace_smmuv3_translate_success() Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-6-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 59 +++++++++++ hw/arm/smmuv3.c | 194 +++++++++++++---------------------- hw/arm/trace-events | 2 +- include/hw/arm/smmu-common.h | 8 ++ 4 files changed, 142 insertions(+), 121 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 408fc4efca..8d52472863 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -566,6 +566,65 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, g_assert_not_reached(); } +SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, + IOMMUAccessFlags flag, SMMUPTWEventInfo *info) +{ + uint64_t page_mask, aligned_addr; + SMMUTLBEntry *cached_entry = NULL; + SMMUTransTableInfo *tt; + int status; + + /* + * Combined attributes used for TLB lookup, as only one stage is supported, + * it will hold attributes based on the enabled stage. + */ + SMMUTransTableInfo tt_combined; + + if (cfg->stage == SMMU_STAGE_1) { + /* Select stage1 translation table. */ + tt = select_tt(cfg, addr); + if (!tt) { + info->type = SMMU_PTW_ERR_TRANSLATION; + info->stage = SMMU_STAGE_1; + return NULL; + } + tt_combined.granule_sz = tt->granule_sz; + tt_combined.tsz = tt->tsz; + + } else { + /* Stage2. */ + tt_combined.granule_sz = cfg->s2cfg.granule_sz; + tt_combined.tsz = cfg->s2cfg.tsz; + } + + /* + * TLB lookup looks for granule and input size for a translation stage, + * as only one stage is supported right now, choose the right values + * from the configuration. + */ + page_mask = (1ULL << tt_combined.granule_sz) - 1; + aligned_addr = addr & ~page_mask; + + cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, aligned_addr); + if (cached_entry) { + if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { + info->type = SMMU_PTW_ERR_PERMISSION; + info->stage = cfg->stage; + return NULL; + } + return cached_entry; + } + + cached_entry = g_new0(SMMUTLBEntry, 1); + status = smmu_ptw(cfg, aligned_addr, flag, cached_entry, info); + if (status) { + g_free(cached_entry); + return NULL; + } + smmu_iotlb_insert(bs, cfg, cached_entry); + return cached_entry; +} + /** * The bus number is used for lookup when SID based invalidation occurs. * In that case we lazily populate the SMMUPciBus array from the bus hash diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index aab09df23e..76d9969c93 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -827,6 +827,76 @@ static void smmuv3_flush_config(SMMUDevice *sdev) g_hash_table_remove(bc->configs, sdev); } +/* Do translation with TLB lookup. */ +static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, + SMMUTransCfg *cfg, + SMMUEventInfo *event, + IOMMUAccessFlags flag, + SMMUTLBEntry **out_entry) +{ + SMMUPTWEventInfo ptw_info = {}; + SMMUState *bs = ARM_SMMU(s); + SMMUTLBEntry *cached_entry = NULL; + + cached_entry = smmu_translate(bs, cfg, addr, flag, &ptw_info); + if (!cached_entry) { + /* All faults from PTW has S2 field. */ + event->u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2); + switch (ptw_info.type) { + case SMMU_PTW_ERR_WALK_EABT: + event->type = SMMU_EVT_F_WALK_EABT; + event->u.f_walk_eabt.addr = addr; + event->u.f_walk_eabt.rnw = flag & 0x1; + event->u.f_walk_eabt.class = (ptw_info.stage == SMMU_STAGE_2) ? + SMMU_CLASS_IN : SMMU_CLASS_TT; + event->u.f_walk_eabt.addr2 = ptw_info.addr; + break; + case SMMU_PTW_ERR_TRANSLATION: + if (PTW_RECORD_FAULT(cfg)) { + event->type = SMMU_EVT_F_TRANSLATION; + event->u.f_translation.addr = addr; + event->u.f_translation.addr2 = ptw_info.addr; + event->u.f_translation.class = SMMU_CLASS_IN; + event->u.f_translation.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ADDR_SIZE: + if (PTW_RECORD_FAULT(cfg)) { + event->type = SMMU_EVT_F_ADDR_SIZE; + event->u.f_addr_size.addr = addr; + event->u.f_addr_size.addr2 = ptw_info.addr; + event->u.f_addr_size.class = SMMU_CLASS_IN; + event->u.f_addr_size.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ACCESS: + if (PTW_RECORD_FAULT(cfg)) { + event->type = SMMU_EVT_F_ACCESS; + event->u.f_access.addr = addr; + event->u.f_access.addr2 = ptw_info.addr; + event->u.f_access.class = SMMU_CLASS_IN; + event->u.f_access.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_PERMISSION: + if (PTW_RECORD_FAULT(cfg)) { + event->type = SMMU_EVT_F_PERMISSION; + event->u.f_permission.addr = addr; + event->u.f_permission.addr2 = ptw_info.addr; + event->u.f_permission.class = SMMU_CLASS_IN; + event->u.f_permission.rnw = flag & 0x1; + } + break; + default: + g_assert_not_reached(); + } + return SMMU_TRANS_ERROR; + } + *out_entry = cached_entry; + return SMMU_TRANS_SUCCESS; +} + +/* Entry point to SMMU, does everything. */ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, IOMMUAccessFlags flag, int iommu_idx) { @@ -836,12 +906,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid, .inval_ste_allowed = false}; - SMMUPTWEventInfo ptw_info = {}; SMMUTranslationStatus status; - SMMUState *bs = ARM_SMMU(s); - uint64_t page_mask, aligned_addr; - SMMUTLBEntry *cached_entry = NULL; - SMMUTransTableInfo *tt; SMMUTransCfg *cfg = NULL; IOMMUTLBEntry entry = { .target_as = &address_space_memory, @@ -850,11 +915,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .addr_mask = ~(hwaddr)0, .perm = IOMMU_NONE, }; - /* - * Combined attributes used for TLB lookup, as only one stage is supported, - * it will hold attributes based on the enabled stage. - */ - SMMUTransTableInfo tt_combined; + SMMUTLBEntry *cached_entry = NULL; qemu_mutex_lock(&s->mutex); @@ -883,115 +944,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto epilogue; } - if (cfg->stage == SMMU_STAGE_1) { - /* Select stage1 translation table. */ - tt = select_tt(cfg, addr); - if (!tt) { - if (cfg->record_faults) { - event.type = SMMU_EVT_F_TRANSLATION; - event.u.f_translation.addr = addr; - event.u.f_translation.rnw = flag & 0x1; - } - status = SMMU_TRANS_ERROR; - goto epilogue; - } - tt_combined.granule_sz = tt->granule_sz; - tt_combined.tsz = tt->tsz; - - } else { - /* Stage2. */ - tt_combined.granule_sz = cfg->s2cfg.granule_sz; - tt_combined.tsz = cfg->s2cfg.tsz; - } - /* - * TLB lookup looks for granule and input size for a translation stage, - * as only one stage is supported right now, choose the right values - * from the configuration. - */ - page_mask = (1ULL << tt_combined.granule_sz) - 1; - aligned_addr = addr & ~page_mask; - - cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, aligned_addr); - if (cached_entry) { - if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { - status = SMMU_TRANS_ERROR; - /* - * We know that the TLB only contains either stage-1 or stage-2 as - * nesting is not supported. So it is sufficient to check the - * translation stage to know the TLB stage for now. - */ - event.u.f_walk_eabt.s2 = (cfg->stage == SMMU_STAGE_2); - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_PERMISSION; - event.u.f_permission.addr = addr; - event.u.f_permission.rnw = flag & 0x1; - } - } else { - status = SMMU_TRANS_SUCCESS; - } - goto epilogue; - } - - cached_entry = g_new0(SMMUTLBEntry, 1); - - if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { - /* All faults from PTW has S2 field. */ - event.u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2); - g_free(cached_entry); - switch (ptw_info.type) { - case SMMU_PTW_ERR_WALK_EABT: - event.type = SMMU_EVT_F_WALK_EABT; - event.u.f_walk_eabt.addr = addr; - event.u.f_walk_eabt.rnw = flag & 0x1; - /* Stage-2 (only) is class IN while stage-1 is class TT */ - event.u.f_walk_eabt.class = (ptw_info.stage == SMMU_STAGE_2) ? - SMMU_CLASS_IN : SMMU_CLASS_TT; - event.u.f_walk_eabt.addr2 = ptw_info.addr; - break; - case SMMU_PTW_ERR_TRANSLATION: - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_TRANSLATION; - event.u.f_translation.addr = addr; - event.u.f_translation.addr2 = ptw_info.addr; - event.u.f_translation.class = SMMU_CLASS_IN; - event.u.f_translation.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_ADDR_SIZE: - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_ADDR_SIZE; - event.u.f_addr_size.addr = addr; - event.u.f_addr_size.addr2 = ptw_info.addr; - event.u.f_translation.class = SMMU_CLASS_IN; - event.u.f_addr_size.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_ACCESS: - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_ACCESS; - event.u.f_access.addr = addr; - event.u.f_access.addr2 = ptw_info.addr; - event.u.f_translation.class = SMMU_CLASS_IN; - event.u.f_access.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_PERMISSION: - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_PERMISSION; - event.u.f_permission.addr = addr; - event.u.f_permission.addr2 = ptw_info.addr; - event.u.f_translation.class = SMMU_CLASS_IN; - event.u.f_permission.rnw = flag & 0x1; - } - break; - default: - g_assert_not_reached(); - } - status = SMMU_TRANS_ERROR; - } else { - smmu_iotlb_insert(bs, cfg, cached_entry); - status = SMMU_TRANS_SUCCESS; - } + status = smmuv3_do_translate(s, addr, cfg, &event, flag, &cached_entry); epilogue: qemu_mutex_unlock(&s->mutex); @@ -1002,7 +955,8 @@ epilogue: (addr & cached_entry->entry.addr_mask); entry.addr_mask = cached_entry->entry.addr_mask; trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr, - entry.translated_addr, entry.perm); + entry.translated_addr, entry.perm, + cfg->stage); break; case SMMU_TRANS_DISABLE: entry.perm = flag; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index f1a54a02df..cc12924a84 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -37,7 +37,7 @@ smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64 smmuv3_translate_disable(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x bypass (smmu disabled) iova:0x%"PRIx64" is_write=%d" smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x STE bypass iova:0x%"PRIx64" is_write=%d" smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x abort on iova:0x%"PRIx64" is_write=%d" -smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=0x%x iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" +smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm, int stage) "%s sid=0x%x iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x stage=%d" smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 smmuv3_decode_cd(uint32_t oas) "oas=%d" smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz, bool had) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d had:%d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 260582089e..366b53be77 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -183,6 +183,14 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev) int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info); + +/* + * smmu_translate - Look for a translation in TLB, if not, do a PTW. + * Returns NULL on PTW error or incase of TLB permission errors. + */ +SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, + IOMMUAccessFlags flag, SMMUPTWEventInfo *info); + /** * select_tt - compute which translation table shall be used according to * the input iova and translation config and return the TT specific info From d883822641748e2d3629fdea722192986238d2ff Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:06 +0000 Subject: [PATCH 2223/2884] hw/arm/smmu: Consolidate ASID and VMID types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASID and VMID used to be uint16_t in the translation config, however, in other contexts they can be int as -1 in case of TLB invalidation, to represent all (don’t care). When stage-2 was added asid was set to -1 in stage-2 and vmid to -1 in stage-1 configs. However, that meant they were set as (65536), this was not an issue as nesting was not supported and no commands/lookup uses both. With nesting, it’s critical to get this right as translation must be tagged correctly with ASID/VMID, and with ASID=-1 meaning stage-2. Represent ASID/VMID everywhere as int. Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-7-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 10 +++++----- hw/arm/smmuv3.c | 4 ++-- hw/arm/trace-events | 18 +++++++++--------- include/hw/arm/smmu-common.h | 14 +++++++------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 8d52472863..7fbf8e22fe 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -57,7 +57,7 @@ static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) (k1->vmid == k2->vmid); } -SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint16_t vmid, uint64_t iova, +SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, uint8_t tg, uint8_t level) { SMMUIOTLBKey key = {.asid = asid, .vmid = vmid, .iova = iova, @@ -130,7 +130,7 @@ void smmu_iotlb_inv_all(SMMUState *s) static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, gpointer user_data) { - uint16_t asid = *(uint16_t *)user_data; + int asid = *(int *)user_data; SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; return SMMU_IOTLB_ASID(*iotlb_key) == asid; @@ -139,7 +139,7 @@ static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, static gboolean smmu_hash_remove_by_vmid(gpointer key, gpointer value, gpointer user_data) { - uint16_t vmid = *(uint16_t *)user_data; + int vmid = *(int *)user_data; SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; return SMMU_IOTLB_VMID(*iotlb_key) == vmid; @@ -191,13 +191,13 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, &info); } -void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) +void smmu_iotlb_inv_asid(SMMUState *s, int asid) { trace_smmu_iotlb_inv_asid(asid); g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); } -void smmu_iotlb_inv_vmid(SMMUState *s, uint16_t vmid) +void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) { trace_smmu_iotlb_inv_vmid(vmid); g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 76d9969c93..e71b842162 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1240,7 +1240,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) } case SMMU_CMD_TLBI_NH_ASID: { - uint16_t asid = CMD_ASID(&cmd); + int asid = CMD_ASID(&cmd); if (!STAGE1_SUPPORTED(s)) { cmd_error = SMMU_CERROR_ILL; @@ -1273,7 +1273,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) break; case SMMU_CMD_TLBI_S12_VMALL: { - uint16_t vmid = CMD_VMID(&cmd); + int vmid = CMD_VMID(&cmd); if (!STAGE2_SUPPORTED(s)) { cmd_error = SMMU_CERROR_ILL; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index cc12924a84..09ccd39548 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -11,13 +11,13 @@ smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint6 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 smmu_iotlb_inv_all(void) "IOTLB invalidate all" -smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" -smmu_iotlb_inv_vmid(uint16_t vmid) "IOTLB invalidate vmid=%d" -smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_iotlb_inv_asid(int asid) "IOTLB invalidate asid=%d" +smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d" +smmu_iotlb_inv_iova(int asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" -smmu_iotlb_lookup_hit(uint16_t asid, uint16_t vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_lookup_miss(uint16_t asid, uint16_t vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_insert(uint16_t asid, uint16_t vmid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d vmid=%d addr=0x%"PRIx64" tg=%d level=%d" +smmu_iotlb_lookup_hit(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_lookup_miss(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_insert(int asid, int vmid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d vmid=%d addr=0x%"PRIx64" tg=%d level=%d" # smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" @@ -48,12 +48,12 @@ smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t p smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d" smmuv3_cmdq_tlbi_nh(void) "" -smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" -smmuv3_cmdq_tlbi_s12_vmid(uint16_t vmid) "vmid=%d" +smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d" +smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" -smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint16_t vmid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 +smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 366b53be77..5cb3024464 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -84,7 +84,7 @@ typedef struct SMMUS2Cfg { bool record_faults; /* Record fault events (S2R) */ uint8_t granule_sz; /* Granule page shift (based on S2TG) */ uint8_t eff_ps; /* Effective PA output range (based on S2PS) */ - uint16_t vmid; /* Virtual Machine ID (S2VMID) */ + int vmid; /* Virtual Machine ID (S2VMID) */ uint64_t vttb; /* Address of translation table base (S2TTB) */ } SMMUS2Cfg; @@ -108,7 +108,7 @@ typedef struct SMMUTransCfg { uint64_t ttb; /* TT base address */ uint8_t oas; /* output address width */ uint8_t tbi; /* Top Byte Ignore */ - uint16_t asid; + int asid; SMMUTransTableInfo tt[2]; /* Used by stage-2 only. */ struct SMMUS2Cfg s2cfg; @@ -132,8 +132,8 @@ typedef struct SMMUPciBus { typedef struct SMMUIOTLBKey { uint64_t iova; - uint16_t asid; - uint16_t vmid; + int asid; + int vmid; uint8_t tg; uint8_t level; } SMMUIOTLBKey; @@ -205,11 +205,11 @@ SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid); SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, SMMUTransTableInfo *tt, hwaddr iova); void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry); -SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint16_t vmid, uint64_t iova, +SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, uint8_t tg, uint8_t level); void smmu_iotlb_inv_all(SMMUState *s); -void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid); -void smmu_iotlb_inv_vmid(SMMUState *s, uint16_t vmid); +void smmu_iotlb_inv_asid(SMMUState *s, int asid); +void smmu_iotlb_inv_vmid(SMMUState *s, int vmid); void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); From ec31ef9115a7511c90fcdc0d89cb1491c2702003 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:07 +0000 Subject: [PATCH 2224/2884] hw/arm/smmu: Introduce CACHED_ENTRY_TO_ADDR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Soon, smmuv3_do_translate() will be used to translate the CD and the TTBx, instead of re-writting the same logic to convert the returned cached entry to an address, add a new macro CACHED_ENTRY_TO_ADDR. Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-8-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 3 +-- include/hw/arm/smmu-common.h | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index e71b842162..dc63e07d68 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -951,8 +951,7 @@ epilogue: switch (status) { case SMMU_TRANS_SUCCESS: entry.perm = cached_entry->entry.perm; - entry.translated_addr = cached_entry->entry.translated_addr + - (addr & cached_entry->entry.addr_mask); + entry.translated_addr = CACHED_ENTRY_TO_ADDR(cached_entry, addr); entry.addr_mask = cached_entry->entry.addr_mask; trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr, entry.translated_addr, entry.perm, diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 5cb3024464..f793b54388 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -37,6 +37,9 @@ #define VMSA_IDXMSK(isz, strd, lvl) ((1ULL << \ VMSA_BIT_LVL(isz, strd, lvl)) - 1) +#define CACHED_ENTRY_TO_ADDR(ent, addr) ((ent)->entry.translated_addr + \ + ((addr) & (ent)->entry.addr_mask)) + /* * Page table walk error types */ From 9dd6aa9b05d535f6d66e7b07134bddf1689ab23f Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:08 +0000 Subject: [PATCH 2225/2884] hw/arm/smmuv3: Translate CD and TT using stage-2 table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to ARM SMMU architecture specification (ARM IHI 0070 F.b), In "5.2 Stream Table Entry": [51:6] S1ContextPtr If Config[1] == 1 (stage 2 enabled), this pointer is an IPA translated by stage 2 and the programmed value must be within the range of the IAS. In "5.4.1 CD notes": The translation table walks performed from TTB0 or TTB1 are always performed in IPA space if stage 2 translations are enabled. This patch implements translation of the S1 context descriptor pointer and TTBx base addresses through the S2 stage (IPA -> PA) smmuv3_do_translate() is updated to have one arg which is translation class, this is useful to: - Decide wether a translation is stage-2 only or use the STE config. - Populate the class in case of faults, WALK_EABT is left unchanged for stage-1 as it is always IN, while stage-2 would match the used class (TT, IN, CD), this will change slightly when the ptw supports nested translation as it can also issue TT event with class IN. In case for stage-2 only translation, used in the context of nested translation, the stage and asid are saved and restored before and after calling smmu_translate(). Translating CD or TTBx can fail for the following reasons: 1) Large address size: This is described in (3.4.3 Address sizes of SMMU-originated accesses) - For CD ptr larger than IAS, for SMMUv3.1, it can trigger either C_BAD_STE or Translation fault, we implement the latter as it requires no extra code. - For TTBx, if larger than the effective stage 1 output address size, it triggers C_BAD_CD. 2) Faults from PTWs (7.3 Event records) - F_ADDR_SIZE: large address size after first level causes stage 2 Address Size fault (Also in 3.4.3 Address sizes of SMMU-originated accesses) - F_PERMISSION: Same as an address translation. However, when CLASS == CD, the access is implicitly Data and a read. - F_ACCESS: Same as an address translation. - F_TRANSLATION: Same as an address translation. - F_WALK_EABT: Same as an address translation. These are already implemented in the PTW logic, so no extra handling required. As in CD and TTBx translation context, the iova is not known, setting the InputAddr was removed from "smmuv3_do_translate" and set after from "smmuv3_translate" with the new function "smmuv3_fixup_event" Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-9-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 120 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 17 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index dc63e07d68..5c5fee2799 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -337,14 +337,35 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, } +static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, + SMMUTransCfg *cfg, + SMMUEventInfo *event, + IOMMUAccessFlags flag, + SMMUTLBEntry **out_entry, + SMMUTranslationClass class); /* @ssid > 0 not supported yet */ -static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, - CD *buf, SMMUEventInfo *event) +static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg, + uint32_t ssid, CD *buf, SMMUEventInfo *event) { dma_addr_t addr = STE_CTXPTR(ste); int ret, i; + SMMUTranslationStatus status; + SMMUTLBEntry *entry; trace_smmuv3_get_cd(addr); + + if (cfg->stage == SMMU_NESTED) { + status = smmuv3_do_translate(s, addr, cfg, event, + IOMMU_RO, &entry, SMMU_CLASS_CD); + + /* Same PTW faults are reported but with CLASS = CD. */ + if (status != SMMU_TRANS_SUCCESS) { + return -EINVAL; + } + + addr = CACHED_ENTRY_TO_ADDR(entry, addr); + } + /* TODO: guarantee 64-bit single-copy atomicity */ ret = dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf), MEMTXATTRS_UNSPECIFIED); @@ -659,10 +680,13 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, return 0; } -static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) +static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg, + CD *cd, SMMUEventInfo *event) { int ret = -EINVAL; int i; + SMMUTranslationStatus status; + SMMUTLBEntry *entry; if (!CD_VALID(cd) || !CD_AARCH64(cd)) { goto bad_cd; @@ -713,9 +737,26 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) tt->tsz = tsz; tt->ttb = CD_TTB(cd, i); + if (tt->ttb & ~(MAKE_64BIT_MASK(0, cfg->oas))) { goto bad_cd; } + + /* Translate the TTBx, from IPA to PA if nesting is enabled. */ + if (cfg->stage == SMMU_NESTED) { + status = smmuv3_do_translate(s, tt->ttb, cfg, event, IOMMU_RO, + &entry, SMMU_CLASS_TT); + /* + * Same PTW faults are reported but with CLASS = TT. + * If TTBx is larger than the effective stage 1 output addres + * size, it reports C_BAD_CD, which is handled by the above case. + */ + if (status != SMMU_TRANS_SUCCESS) { + return -EINVAL; + } + tt->ttb = CACHED_ENTRY_TO_ADDR(entry, tt->ttb); + } + tt->had = CD_HAD(cd, i); trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz, tt->had); } @@ -767,12 +808,12 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, return 0; } - ret = smmu_get_cd(s, &ste, 0 /* ssid */, &cd, event); + ret = smmu_get_cd(s, &ste, cfg, 0 /* ssid */, &cd, event); if (ret) { return ret; } - return decode_cd(cfg, &cd, event); + return decode_cd(s, cfg, &cd, event); } /** @@ -832,58 +873,80 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, SMMUTransCfg *cfg, SMMUEventInfo *event, IOMMUAccessFlags flag, - SMMUTLBEntry **out_entry) + SMMUTLBEntry **out_entry, + SMMUTranslationClass class) { SMMUPTWEventInfo ptw_info = {}; SMMUState *bs = ARM_SMMU(s); SMMUTLBEntry *cached_entry = NULL; + int asid, stage; + bool desc_s2_translation = class != SMMU_CLASS_IN; + + /* + * The function uses the argument class to identify which stage is used: + * - CLASS = IN: Means an input translation, determine the stage from STE. + * - CLASS = CD: Means the addr is an IPA of the CD, and it would be + * translated using the stage-2. + * - CLASS = TT: Means the addr is an IPA of the stage-1 translation table + * and it would be translated using the stage-2. + * For the last 2 cases instead of having intrusive changes in the common + * logic, we modify the cfg to be a stage-2 translation only in case of + * nested, and then restore it after. + */ + if (desc_s2_translation) { + asid = cfg->asid; + stage = cfg->stage; + cfg->asid = -1; + cfg->stage = SMMU_STAGE_2; + } cached_entry = smmu_translate(bs, cfg, addr, flag, &ptw_info); + + if (desc_s2_translation) { + cfg->asid = asid; + cfg->stage = stage; + } + if (!cached_entry) { /* All faults from PTW has S2 field. */ event->u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2); switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: event->type = SMMU_EVT_F_WALK_EABT; - event->u.f_walk_eabt.addr = addr; event->u.f_walk_eabt.rnw = flag & 0x1; event->u.f_walk_eabt.class = (ptw_info.stage == SMMU_STAGE_2) ? - SMMU_CLASS_IN : SMMU_CLASS_TT; + class : SMMU_CLASS_TT; event->u.f_walk_eabt.addr2 = ptw_info.addr; break; case SMMU_PTW_ERR_TRANSLATION: if (PTW_RECORD_FAULT(cfg)) { event->type = SMMU_EVT_F_TRANSLATION; - event->u.f_translation.addr = addr; event->u.f_translation.addr2 = ptw_info.addr; - event->u.f_translation.class = SMMU_CLASS_IN; + event->u.f_translation.class = class; event->u.f_translation.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_ADDR_SIZE: if (PTW_RECORD_FAULT(cfg)) { event->type = SMMU_EVT_F_ADDR_SIZE; - event->u.f_addr_size.addr = addr; event->u.f_addr_size.addr2 = ptw_info.addr; - event->u.f_addr_size.class = SMMU_CLASS_IN; + event->u.f_addr_size.class = class; event->u.f_addr_size.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_ACCESS: if (PTW_RECORD_FAULT(cfg)) { event->type = SMMU_EVT_F_ACCESS; - event->u.f_access.addr = addr; event->u.f_access.addr2 = ptw_info.addr; - event->u.f_access.class = SMMU_CLASS_IN; + event->u.f_access.class = class; event->u.f_access.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_PERMISSION: if (PTW_RECORD_FAULT(cfg)) { event->type = SMMU_EVT_F_PERMISSION; - event->u.f_permission.addr = addr; event->u.f_permission.addr2 = ptw_info.addr; - event->u.f_permission.class = SMMU_CLASS_IN; + event->u.f_permission.class = class; event->u.f_permission.rnw = flag & 0x1; } break; @@ -896,6 +959,27 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, return SMMU_TRANS_SUCCESS; } +/* + * Sets the InputAddr for an SMMU_TRANS_ERROR, as it can't be + * set from all contexts, as smmuv3_get_config() can return + * translation faults in case of nested translation (for CD + * and TTBx). But in that case the iova is not known. + */ +static void smmuv3_fixup_event(SMMUEventInfo *event, hwaddr iova) +{ + switch (event->type) { + case SMMU_EVT_F_WALK_EABT: + case SMMU_EVT_F_TRANSLATION: + case SMMU_EVT_F_ADDR_SIZE: + case SMMU_EVT_F_ACCESS: + case SMMU_EVT_F_PERMISSION: + event->u.f_walk_eabt.addr = iova; + break; + default: + break; + } +} + /* Entry point to SMMU, does everything. */ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, IOMMUAccessFlags flag, int iommu_idx) @@ -944,7 +1028,8 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto epilogue; } - status = smmuv3_do_translate(s, addr, cfg, &event, flag, &cached_entry); + status = smmuv3_do_translate(s, addr, cfg, &event, flag, + &cached_entry, SMMU_CLASS_IN); epilogue: qemu_mutex_unlock(&s->mutex); @@ -975,6 +1060,7 @@ epilogue: entry.perm); break; case SMMU_TRANS_ERROR: + smmuv3_fixup_event(&event, addr); qemu_log_mask(LOG_GUEST_ERROR, "%s translation failed for iova=0x%"PRIx64" (%s)\n", mr->parent_obj.name, addr, smmu_event_string(event.type)); From 7eb57be1efb8cdcf613fd8b089295ed5a4f48d7d Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:09 +0000 Subject: [PATCH 2226/2884] hw/arm/smmu-common: Rework TLB lookup for nesting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the next patch, combine_tlb() will be added which combines 2 TLB entries into one for nested translations, which chooses the granule and level from the smallest entry. This means that with nested translation, an entry can be cached with the granule of stage-2 and not stage-1. However, currently, the lookup for an IOVA is done with input stage granule, which is stage-1 for nested configuration, which will not work with the above logic. This patch reworks lookup in that case, so it falls back to stage-2 granule if no entry is found using stage-1 granule. Also, drop aligning the iova to avoid over-aligning in case the iova is cached with a smaller granule, the TLB lookup will align the iova anyway for each granule and level, and the page table walker doesn't consider the page offset bits. Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-10-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 64 +++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 7fbf8e22fe..e374cae0db 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -66,8 +66,10 @@ SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, return key; } -SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, - SMMUTransTableInfo *tt, hwaddr iova) +static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs, + SMMUTransCfg *cfg, + SMMUTransTableInfo *tt, + hwaddr iova) { uint8_t tg = (tt->granule_sz - 10) / 2; uint8_t inputsize = 64 - tt->tsz; @@ -88,6 +90,36 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, } level++; } + return entry; +} + +/** + * smmu_iotlb_lookup - Look up for a TLB entry. + * @bs: SMMU state which includes the TLB instance + * @cfg: Configuration of the translation + * @tt: Translation table info (granule and tsz) + * @iova: IOVA address to lookup + * + * returns a valid entry on success, otherwise NULL. + * In case of nested translation, tt can be updated to include + * the granule of the found entry as it might different from + * the IOVA granule. + */ +SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, + SMMUTransTableInfo *tt, hwaddr iova) +{ + SMMUTLBEntry *entry = NULL; + + entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova); + /* + * For nested translation also try the s2 granule, as the TLB will insert + * it if the size of s2 tlb entry was smaller. + */ + if (!entry && (cfg->stage == SMMU_NESTED) && + (cfg->s2cfg.granule_sz != tt->granule_sz)) { + tt->granule_sz = cfg->s2cfg.granule_sz; + entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova); + } if (entry) { cfg->iotlb_hits++; @@ -569,18 +601,21 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, IOMMUAccessFlags flag, SMMUPTWEventInfo *info) { - uint64_t page_mask, aligned_addr; SMMUTLBEntry *cached_entry = NULL; SMMUTransTableInfo *tt; int status; /* - * Combined attributes used for TLB lookup, as only one stage is supported, - * it will hold attributes based on the enabled stage. + * Combined attributes used for TLB lookup, holds the attributes for + * the input stage. */ SMMUTransTableInfo tt_combined; - if (cfg->stage == SMMU_STAGE_1) { + if (cfg->stage == SMMU_STAGE_2) { + /* Stage2. */ + tt_combined.granule_sz = cfg->s2cfg.granule_sz; + tt_combined.tsz = cfg->s2cfg.tsz; + } else { /* Select stage1 translation table. */ tt = select_tt(cfg, addr); if (!tt) { @@ -590,22 +625,9 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, } tt_combined.granule_sz = tt->granule_sz; tt_combined.tsz = tt->tsz; - - } else { - /* Stage2. */ - tt_combined.granule_sz = cfg->s2cfg.granule_sz; - tt_combined.tsz = cfg->s2cfg.tsz; } - /* - * TLB lookup looks for granule and input size for a translation stage, - * as only one stage is supported right now, choose the right values - * from the configuration. - */ - page_mask = (1ULL << tt_combined.granule_sz) - 1; - aligned_addr = addr & ~page_mask; - - cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, aligned_addr); + cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, addr); if (cached_entry) { if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { info->type = SMMU_PTW_ERR_PERMISSION; @@ -616,7 +638,7 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, } cached_entry = g_new0(SMMUTLBEntry, 1); - status = smmu_ptw(cfg, aligned_addr, flag, cached_entry, info); + status = smmu_ptw(cfg, addr, flag, cached_entry, info); if (status) { g_free(cached_entry); return NULL; From d7cdf89c276abeb392cbf9b9d0dde5060a8c778f Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:10 +0000 Subject: [PATCH 2227/2884] hw/arm/smmu-common: Add support for nested TLB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for nested (combined) TLB entries. The main function combine_tlb() is not used here but in the next patches, but to simplify the patches it is introduced first. Main changes: 1) New field added in the SMMUTLBEntry struct: parent_perm, for nested TLB, holds the stage-2 permission, this can be used to know the origin of a permission fault from a cached entry as caching the “and” of the permissions loses this information. SMMUPTWEventInfo is used to hold information about PTW faults so the event can be populated, the value of stage used to be set based on the current stage for TLB permission faults, however with the parent_perm, it is now set based on which perm has the missing permission When nesting is not enabled it has the same value as perm which doesn't change the logic. 2) As combined TLB implementation is used, the combination logic chooses: - tg and level from the entry which has the smallest addr_mask. - Based on that the iova that would be cached is recalculated. - Translated_addr is chosen from stage-2. Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-11-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 37 ++++++++++++++++++++++++++++++++---- include/hw/arm/smmu-common.h | 1 + 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index e374cae0db..bf55b9c5a4 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -426,7 +426,8 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, tlbe->entry.translated_addr = gpa; tlbe->entry.iova = iova & ~mask; tlbe->entry.addr_mask = mask; - tlbe->entry.perm = PTE_AP_TO_PERM(ap); + tlbe->parent_perm = PTE_AP_TO_PERM(ap); + tlbe->entry.perm = tlbe->parent_perm; tlbe->level = level; tlbe->granule = granule_sz; return 0; @@ -547,7 +548,8 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, tlbe->entry.translated_addr = gpa; tlbe->entry.iova = ipa & ~mask; tlbe->entry.addr_mask = mask; - tlbe->entry.perm = s2ap; + tlbe->parent_perm = s2ap; + tlbe->entry.perm = tlbe->parent_perm; tlbe->level = level; tlbe->granule = granule_sz; return 0; @@ -562,6 +564,30 @@ error: return -EINVAL; } +/* + * combine S1 and S2 TLB entries into a single entry. + * As a result the S1 entry is overriden with combined data. + */ +static void __attribute__((unused)) combine_tlb(SMMUTLBEntry *tlbe, + SMMUTLBEntry *tlbe_s2, + dma_addr_t iova, + SMMUTransCfg *cfg) +{ + if (tlbe_s2->entry.addr_mask < tlbe->entry.addr_mask) { + tlbe->entry.addr_mask = tlbe_s2->entry.addr_mask; + tlbe->granule = tlbe_s2->granule; + tlbe->level = tlbe_s2->level; + } + + tlbe->entry.translated_addr = CACHED_ENTRY_TO_ADDR(tlbe_s2, + tlbe->entry.translated_addr); + + tlbe->entry.iova = iova & ~tlbe->entry.addr_mask; + /* parent_perm has s2 perm while perm keeps s1 perm. */ + tlbe->parent_perm = tlbe_s2->entry.perm; + return; +} + /** * smmu_ptw - Walk the page tables for an IOVA, according to @cfg * @@ -629,9 +655,12 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, addr); if (cached_entry) { - if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { + if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & + cached_entry->parent_perm & IOMMU_WO)) { info->type = SMMU_PTW_ERR_PERMISSION; - info->stage = cfg->stage; + info->stage = !(cached_entry->entry.perm & IOMMU_WO) ? + SMMU_STAGE_1 : + SMMU_STAGE_2; return NULL; } return cached_entry; diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index f793b54388..0877536472 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -77,6 +77,7 @@ typedef struct SMMUTLBEntry { IOMMUTLBEntry entry; uint8_t level; uint8_t granule; + IOMMUAccessFlags parent_perm; } SMMUTLBEntry; /* Stage-2 configuration. */ From f42a0a57c0a22210456e83676f43e88782e43942 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:11 +0000 Subject: [PATCH 2228/2884] hw/arm/smmu-common: Support nested translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When nested translation is requested, do the following: - Translate stage-1 table address IPA into PA through stage-2. - Translate stage-1 table walk output (IPA) through stage-2. - Create a single TLB entry from stage-1 and stage-2 translations using logic introduced before. smmu_ptw() has a new argument SMMUState which include the TLB as stage-1 table address can be cached in there. Also in smmu_ptw(), a separate path used for nesting to simplify the code, although some logic can be combined. With nested translation class of translation fault can be different, from the class of the translation, as faults from translating stage-1 tables are considered as CLASS_TT and not CLASS_IN, a new member "is_ipa_descriptor" added to "SMMUPTWEventInfo" to differ faults from walking stage 1 translation table and faults from translating an IPA for a transaction. Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-12-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 74 +++++++++++++++++++++++++++++++----- hw/arm/smmuv3.c | 14 +++++++ include/hw/arm/smmu-common.h | 7 ++-- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index bf55b9c5a4..912b89b5ee 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -318,8 +318,41 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) return NULL; } +/* Translate stage-1 table address using stage-2 page table. */ +static inline int translate_table_addr_ipa(SMMUState *bs, + dma_addr_t *table_addr, + SMMUTransCfg *cfg, + SMMUPTWEventInfo *info) +{ + dma_addr_t addr = *table_addr; + SMMUTLBEntry *cached_entry; + int asid; + + /* + * The translation table walks performed from TTB0 or TTB1 are always + * performed in IPA space if stage 2 translations are enabled. + */ + asid = cfg->asid; + cfg->stage = SMMU_STAGE_2; + cfg->asid = -1; + cached_entry = smmu_translate(bs, cfg, addr, IOMMU_RO, info); + cfg->asid = asid; + cfg->stage = SMMU_NESTED; + + if (cached_entry) { + *table_addr = CACHED_ENTRY_TO_ADDR(cached_entry, addr); + return 0; + } + + info->stage = SMMU_STAGE_2; + info->addr = addr; + info->is_ipa_descriptor = true; + return -EINVAL; +} + /** * smmu_ptw_64_s1 - VMSAv8-64 Walk of the page tables for a given IOVA + * @bs: smmu state which includes TLB instance * @cfg: translation config * @iova: iova to translate * @perm: access type @@ -331,7 +364,7 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) * Upon success, @tlbe is filled with translated_addr and entry * permission rights. */ -static int smmu_ptw_64_s1(SMMUTransCfg *cfg, +static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { @@ -381,6 +414,11 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, goto error; } baseaddr = get_table_pte_address(pte, granule_sz); + if (cfg->stage == SMMU_NESTED) { + if (translate_table_addr_ipa(bs, &baseaddr, cfg, info)) { + goto error; + } + } level++; continue; } else if (is_page_pte(pte, level)) { @@ -568,10 +606,8 @@ error: * combine S1 and S2 TLB entries into a single entry. * As a result the S1 entry is overriden with combined data. */ -static void __attribute__((unused)) combine_tlb(SMMUTLBEntry *tlbe, - SMMUTLBEntry *tlbe_s2, - dma_addr_t iova, - SMMUTransCfg *cfg) +static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2, + dma_addr_t iova, SMMUTransCfg *cfg) { if (tlbe_s2->entry.addr_mask < tlbe->entry.addr_mask) { tlbe->entry.addr_mask = tlbe_s2->entry.addr_mask; @@ -591,6 +627,7 @@ static void __attribute__((unused)) combine_tlb(SMMUTLBEntry *tlbe, /** * smmu_ptw - Walk the page tables for an IOVA, according to @cfg * + * @bs: smmu state which includes TLB instance * @cfg: translation configuration * @iova: iova to translate * @perm: tentative access type @@ -599,11 +636,15 @@ static void __attribute__((unused)) combine_tlb(SMMUTLBEntry *tlbe, * * return 0 on success */ -int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, - SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, + IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { + int ret; + SMMUTLBEntry tlbe_s2; + dma_addr_t ipa; + if (cfg->stage == SMMU_STAGE_1) { - return smmu_ptw_64_s1(cfg, iova, perm, tlbe, info); + return smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info); } else if (cfg->stage == SMMU_STAGE_2) { /* * If bypassing stage 1(or unimplemented), the input address is passed @@ -621,7 +662,20 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, return smmu_ptw_64_s2(cfg, iova, perm, tlbe, info); } - g_assert_not_reached(); + /* SMMU_NESTED. */ + ret = smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info); + if (ret) { + return ret; + } + + ipa = CACHED_ENTRY_TO_ADDR(tlbe, iova); + ret = smmu_ptw_64_s2(cfg, ipa, perm, &tlbe_s2, info); + if (ret) { + return ret; + } + + combine_tlb(tlbe, &tlbe_s2, iova, cfg); + return 0; } SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, @@ -667,7 +721,7 @@ SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, } cached_entry = g_new0(SMMUTLBEntry, 1); - status = smmu_ptw(cfg, addr, flag, cached_entry, info); + status = smmu_ptw(bs, cfg, addr, flag, cached_entry, info); if (status) { g_free(cached_entry); return NULL; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 5c5fee2799..0faa08c8d8 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -910,6 +910,20 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, if (!cached_entry) { /* All faults from PTW has S2 field. */ event->u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2); + /* + * Fault class is set as follows based on "class" input to + * the function and to "ptw_info" from "smmu_translate()" + * For stage-1: + * - EABT => CLASS_TT (hardcoded) + * - other events => CLASS_IN (input to function) + * For stage-2 => CLASS_IN (input to function) + * For nested, for all events: + * - CD fetch => CLASS_CD (input to function) + * - walking stage 1 translation table => CLASS_TT (from + * is_ipa_descriptor or input in case of TTBx) + * - s2 translation => CLASS_IN (input to function) + */ + class = ptw_info.is_ipa_descriptor ? SMMU_CLASS_TT : class; switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: event->type = SMMU_EVT_F_WALK_EABT; diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 0877536472..a51005e8b8 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -63,6 +63,7 @@ typedef struct SMMUPTWEventInfo { SMMUStage stage; SMMUPTWEventType type; dma_addr_t addr; /* fetched address that induced an abort, if any */ + bool is_ipa_descriptor; /* src for fault in nested translation. */ } SMMUPTWEventInfo; typedef struct SMMUTransTableInfo { @@ -184,9 +185,9 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev) * smmu_ptw - Perform the page table walk for a given iova / access flags * pair, according to @cfg translation config */ -int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, - SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info); - +int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, + IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, + SMMUPTWEventInfo *info); /* * smmu_translate - Look for a translation in TLB, if not, do a PTW. From 1ea8a6f59b8d6dbcc5d0aa59380ae45a18231b88 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:12 +0000 Subject: [PATCH 2229/2884] hw/arm/smmu: Support nesting in smmuv3_range_inval() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With nesting, we would need to invalidate IPAs without over-invalidating stage-1 IOVAs. This can be done by distinguishing IPAs in the TLBs by having ASID=-1. To achieve that, rework the invalidation for IPAs to have a separate function, while for IOVA invalidation ASID=-1 means invalidate for all ASIDs. Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-13-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 47 ++++++++++++++++++++++++++++++++++++ hw/arm/smmuv3.c | 23 ++++++++++++------ hw/arm/trace-events | 2 +- include/hw/arm/smmu-common.h | 3 ++- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 912b89b5ee..4b0857ab4d 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -195,6 +195,25 @@ static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value, ((entry->iova & ~info->mask) == info->iova); } +static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value, + gpointer user_data) +{ + SMMUTLBEntry *iter = (SMMUTLBEntry *)value; + IOMMUTLBEntry *entry = &iter->entry; + SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data; + SMMUIOTLBKey iotlb_key = *(SMMUIOTLBKey *)key; + + if (SMMU_IOTLB_ASID(iotlb_key) >= 0) { + /* This is a stage-1 address. */ + return false; + } + if (info->vmid != SMMU_IOTLB_VMID(iotlb_key)) { + return false; + } + return ((info->iova & ~entry->addr_mask) == entry->iova) || + ((entry->iova & ~info->mask) == info->iova); +} + void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl) { @@ -223,6 +242,34 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, &info); } +/* + * Similar to smmu_iotlb_inv_iova(), but for Stage-2, ASID is always -1, + * in Stage-1 invalidation ASID = -1, means don't care. + */ +void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, + uint64_t num_pages, uint8_t ttl) +{ + uint8_t granule = tg ? tg * 2 + 10 : 12; + int asid = -1; + + if (ttl && (num_pages == 1)) { + SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, ipa, tg, ttl); + + if (g_hash_table_remove(s->iotlb, &key)) { + return; + } + } + + SMMUIOTLBPageInvInfo info = { + .iova = ipa, + .vmid = vmid, + .mask = (num_pages << granule) - 1}; + + g_hash_table_foreach_remove(s->iotlb, + smmu_hash_remove_by_vmid_ipa, + &info); +} + void smmu_iotlb_inv_asid(SMMUState *s, int asid) { trace_smmu_iotlb_inv_asid(asid); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 0faa08c8d8..ebf29f3adf 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1168,7 +1168,7 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid, } } -static void smmuv3_range_inval(SMMUState *s, Cmd *cmd) +static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage) { dma_addr_t end, addr = CMD_ADDR(cmd); uint8_t type = CMD_TYPE(cmd); @@ -1193,9 +1193,13 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd) } if (!tg) { - trace_smmuv3_range_inval(vmid, asid, addr, tg, 1, ttl, leaf); + trace_smmuv3_range_inval(vmid, asid, addr, tg, 1, ttl, leaf, stage); smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1); - smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl); + if (stage == SMMU_STAGE_1) { + smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl); + } else { + smmu_iotlb_inv_ipa(s, vmid, addr, tg, 1, ttl); + } return; } @@ -1211,9 +1215,14 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd) uint64_t mask = dma_aligned_pow2_mask(addr, end, 64); num_pages = (mask + 1) >> granule; - trace_smmuv3_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf); + trace_smmuv3_range_inval(vmid, asid, addr, tg, num_pages, + ttl, leaf, stage); smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages); - smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl); + if (stage == SMMU_STAGE_1) { + smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl); + } else { + smmu_iotlb_inv_ipa(s, vmid, addr, tg, num_pages, ttl); + } addr += mask + 1; } } @@ -1368,7 +1377,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) cmd_error = SMMU_CERROR_ILL; break; } - smmuv3_range_inval(bs, &cmd); + smmuv3_range_inval(bs, &cmd, SMMU_STAGE_1); break; case SMMU_CMD_TLBI_S12_VMALL: { @@ -1393,7 +1402,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) * As currently only either s1 or s2 are supported * we can reuse same function for s2. */ - smmuv3_range_inval(bs, &cmd); + smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2); break; case SMMU_CMD_TLBI_EL3_ALL: case SMMU_CMD_TLBI_EL3_VA: diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 09ccd39548..7d9c1703da 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -46,7 +46,7 @@ smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%x - end=0x%x" smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x" smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" -smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d" +smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf, int stage) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d stage=%d" smmuv3_cmdq_tlbi_nh(void) "" smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d" smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index a51005e8b8..da9ff45fb5 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -217,7 +217,8 @@ void smmu_iotlb_inv_asid(SMMUState *s, int asid); void smmu_iotlb_inv_vmid(SMMUState *s, int vmid); void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); - +void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, + uint64_t num_pages, uint8_t ttl); /* Unmap the range of all the notifiers registered to any IOMMU mr */ void smmu_inv_notifiers_all(SMMUState *s); From eb41313c4be1d9b3c8fcd43ee706c10eabd5c188 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:13 +0000 Subject: [PATCH 2230/2884] hw/arm/smmu: Introduce smmu_iotlb_inv_asid_vmid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Soon, Instead of doing TLB invalidation by ASID only, VMID will be also required. Add smmu_iotlb_inv_asid_vmid() which invalidates by both ASID and VMID. However, at the moment this function is only used in SMMU_CMD_TLBI_NH_ASID which is a stage-1 command, so passing VMID = -1 keeps the original behaviour. Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-14-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 20 +++++++++++++------- hw/arm/smmuv3.c | 2 +- hw/arm/trace-events | 2 +- include/hw/arm/smmu-common.h | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 4b0857ab4d..e7f9c758fa 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -159,13 +159,14 @@ void smmu_iotlb_inv_all(SMMUState *s) g_hash_table_remove_all(s->iotlb); } -static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, - gpointer user_data) +static gboolean smmu_hash_remove_by_asid_vmid(gpointer key, gpointer value, + gpointer user_data) { - int asid = *(int *)user_data; + SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data; SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; - return SMMU_IOTLB_ASID(*iotlb_key) == asid; + return (SMMU_IOTLB_ASID(*iotlb_key) == info->asid) && + (SMMU_IOTLB_VMID(*iotlb_key) == info->vmid); } static gboolean smmu_hash_remove_by_vmid(gpointer key, gpointer value, @@ -270,10 +271,15 @@ void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, &info); } -void smmu_iotlb_inv_asid(SMMUState *s, int asid) +void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid) { - trace_smmu_iotlb_inv_asid(asid); - g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); + SMMUIOTLBPageInvInfo info = { + .asid = asid, + .vmid = vmid, + }; + + trace_smmu_iotlb_inv_asid_vmid(asid, vmid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid_vmid, &info); } void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index ebf29f3adf..847fc56676 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1357,7 +1357,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) trace_smmuv3_cmdq_tlbi_nh_asid(asid); smmu_inv_notifiers_all(&s->smmu_state); - smmu_iotlb_inv_asid(bs, asid); + smmu_iotlb_inv_asid_vmid(bs, asid, -1); break; } case SMMU_CMD_TLBI_NH_ALL: diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 7d9c1703da..4aa71b1b19 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -11,7 +11,7 @@ smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint6 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 smmu_iotlb_inv_all(void) "IOTLB invalidate all" -smmu_iotlb_inv_asid(int asid) "IOTLB invalidate asid=%d" +smmu_iotlb_inv_asid_vmid(int asid, int vmid) "IOTLB invalidate asid=%d vmid=%d" smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_iova(int asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index da9ff45fb5..eaee867e45 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -213,7 +213,7 @@ void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry); SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, uint8_t tg, uint8_t level); void smmu_iotlb_inv_all(SMMUState *s); -void smmu_iotlb_inv_asid(SMMUState *s, int asid); +void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid); void smmu_iotlb_inv_vmid(SMMUState *s, int vmid); void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); From b8fa4c233bd4235081080fca919159fd770db53f Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:14 +0000 Subject: [PATCH 2231/2884] hw/arm/smmu: Support nesting in the rest of commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some commands need rework for nesting, as they used to assume S1 and S2 are mutually exclusive: - CMD_TLBI_NH_ASID: Consider VMID if stage-2 is supported - CMD_TLBI_NH_ALL: Consider VMID if stage-2 is supported, otherwise invalidate everything, this required a new vmid invalidation function for stage-1 only (ASID >= 0) Also, rework trace events to reflect the new implementation. Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-15-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 16 ++++++++++++++++ hw/arm/smmuv3.c | 28 ++++++++++++++++++++++++++-- hw/arm/trace-events | 4 +++- include/hw/arm/smmu-common.h | 1 + 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index e7f9c758fa..00d7579cd3 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -178,6 +178,16 @@ static gboolean smmu_hash_remove_by_vmid(gpointer key, gpointer value, return SMMU_IOTLB_VMID(*iotlb_key) == vmid; } +static gboolean smmu_hash_remove_by_vmid_s1(gpointer key, gpointer value, + gpointer user_data) +{ + int vmid = *(int *)user_data; + SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; + + return (SMMU_IOTLB_VMID(*iotlb_key) == vmid) && + (SMMU_IOTLB_ASID(*iotlb_key) >= 0); +} + static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value, gpointer user_data) { @@ -288,6 +298,12 @@ void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); } +inline void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid) +{ + trace_smmu_iotlb_inv_vmid_s1(vmid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid_s1, &vmid); +} + /* VMSAv8-64 Translation */ /** diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 847fc56676..b05f2ab929 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1349,25 +1349,49 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_TLBI_NH_ASID: { int asid = CMD_ASID(&cmd); + int vmid = -1; if (!STAGE1_SUPPORTED(s)) { cmd_error = SMMU_CERROR_ILL; break; } + /* + * VMID is only matched when stage 2 is supported, otherwise set it + * to -1 as the value used for stage-1 only VMIDs. + */ + if (STAGE2_SUPPORTED(s)) { + vmid = CMD_VMID(&cmd); + } + trace_smmuv3_cmdq_tlbi_nh_asid(asid); smmu_inv_notifiers_all(&s->smmu_state); - smmu_iotlb_inv_asid_vmid(bs, asid, -1); + smmu_iotlb_inv_asid_vmid(bs, asid, vmid); break; } case SMMU_CMD_TLBI_NH_ALL: + { + int vmid = -1; + if (!STAGE1_SUPPORTED(s)) { cmd_error = SMMU_CERROR_ILL; break; } + + /* + * If stage-2 is supported, invalidate for this VMID only, otherwise + * invalidate the whole thing. + */ + if (STAGE2_SUPPORTED(s)) { + vmid = CMD_VMID(&cmd); + trace_smmuv3_cmdq_tlbi_nh(vmid); + smmu_iotlb_inv_vmid_s1(bs, vmid); + break; + } QEMU_FALLTHROUGH; + } case SMMU_CMD_TLBI_NSNH_ALL: - trace_smmuv3_cmdq_tlbi_nh(); + trace_smmuv3_cmdq_tlbi_nsnh(); smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_all(bs); break; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 4aa71b1b19..593cc571da 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -13,6 +13,7 @@ smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "base smmu_iotlb_inv_all(void) "IOTLB invalidate all" smmu_iotlb_inv_asid_vmid(int asid, int vmid) "IOTLB invalidate asid=%d vmid=%d" smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d" +smmu_iotlb_inv_vmid_s1(int vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_iova(int asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" smmu_iotlb_lookup_hit(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" @@ -47,7 +48,8 @@ smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x" smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf, int stage) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d stage=%d" -smmuv3_cmdq_tlbi_nh(void) "" +smmuv3_cmdq_tlbi_nh(int vmid) "vmid=%d" +smmuv3_cmdq_tlbi_nsnh(void) "" smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d" smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index eaee867e45..d1a4a64551 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -215,6 +215,7 @@ SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, void smmu_iotlb_inv_all(SMMUState *s); void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid); void smmu_iotlb_inv_vmid(SMMUState *s, int vmid); +void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid); void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, From 46727727965f0d0769f167e300bf11aca77aadd6 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:15 +0000 Subject: [PATCH 2232/2884] hw/arm/smmuv3: Support nested SMMUs in smmuv3_notify_iova() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IOMMUTLBEvent only understands IOVA, for stage-1 or stage-2 SMMU instances we consider the input address as the IOVA, but when nesting is used, we can't mix stage-1 and stage-2 addresses, so for nesting only stage-1 is considered the IOVA and would be notified. Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-16-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 39 +++++++++++++++++++++++++-------------- hw/arm/trace-events | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b05f2ab929..a3cb30501e 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1096,27 +1096,38 @@ epilogue: * @iova: iova * @tg: translation granule (if communicated through range invalidation) * @num_pages: number of @granule sized pages (if tg != 0), otherwise 1 + * @stage: Which stage(1 or 2) is used */ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, IOMMUNotifier *n, int asid, int vmid, dma_addr_t iova, uint8_t tg, - uint64_t num_pages) + uint64_t num_pages, int stage) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUEventInfo eventinfo = {.inval_ste_allowed = true}; + SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo); IOMMUTLBEvent event; uint8_t granule; - SMMUv3State *s = sdev->smmu; + + if (!cfg) { + return; + } + + /* + * stage is passed from TLB invalidation commands which can be either + * stage-1 or stage-2. + * However, IOMMUTLBEvent only understands IOVA, for stage-1 or stage-2 + * SMMU instances we consider the input address as the IOVA, but when + * nesting is used, we can't mix stage-1 and stage-2 addresses, so for + * nesting only stage-1 is considered the IOVA and would be notified. + */ + if ((stage == SMMU_STAGE_2) && (cfg->stage == SMMU_NESTED)) + return; if (!tg) { - SMMUEventInfo eventinfo = {.inval_ste_allowed = true}; - SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo); SMMUTransTableInfo *tt; - if (!cfg) { - return; - } - if (asid >= 0 && cfg->asid != asid) { return; } @@ -1125,7 +1136,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, return; } - if (STAGE1_SUPPORTED(s)) { + if (stage == SMMU_STAGE_1) { tt = select_tt(cfg, iova); if (!tt) { return; @@ -1151,7 +1162,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, /* invalidate an asid/vmid/iova range tuple in all mr's */ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, - uint64_t num_pages) + uint64_t num_pages, int stage) { SMMUDevice *sdev; @@ -1160,10 +1171,10 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid, IOMMUNotifier *n; trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, vmid, - iova, tg, num_pages); + iova, tg, num_pages, stage); IOMMU_NOTIFIER_FOREACH(n, mr) { - smmuv3_notify_iova(mr, n, asid, vmid, iova, tg, num_pages); + smmuv3_notify_iova(mr, n, asid, vmid, iova, tg, num_pages, stage); } } } @@ -1194,7 +1205,7 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage) if (!tg) { trace_smmuv3_range_inval(vmid, asid, addr, tg, 1, ttl, leaf, stage); - smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1); + smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1, stage); if (stage == SMMU_STAGE_1) { smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl); } else { @@ -1217,7 +1228,7 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage) num_pages = (mask + 1) >> granule; trace_smmuv3_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf, stage); - smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages); + smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages, stage); if (stage == SMMU_STAGE_1) { smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl); } else { diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 593cc571da..be6c8f720b 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -55,7 +55,7 @@ smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" -smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 +smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d" # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" From f9131185937c16da9258a565bbfc543e238d323b Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:16 +0000 Subject: [PATCH 2233/2884] hw/arm/smmuv3: Handle translation faults according to SMMUPTWEventInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, to check if faults are enabled, it was sufficient to check the current stage of translation and check the corresponding record_faults flag. However, with nesting, it is possible for stage-1 (nested) translation to trigger a stage-2 fault, so we check SMMUPTWEventInfo as it would have the correct stage set from the page table walk. Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-17-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index a3cb30501e..2d61637aed 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -34,9 +34,10 @@ #include "smmuv3-internal.h" #include "smmu-internal.h" -#define PTW_RECORD_FAULT(cfg) (((cfg)->stage == SMMU_STAGE_1) ? \ - (cfg)->record_faults : \ - (cfg)->s2cfg.record_faults) +#define PTW_RECORD_FAULT(ptw_info, cfg) (((ptw_info).stage == SMMU_STAGE_1 && \ + (cfg)->record_faults) || \ + ((ptw_info).stage == SMMU_STAGE_2 && \ + (cfg)->s2cfg.record_faults)) /** * smmuv3_trigger_irq - pulse @irq if enabled and update @@ -933,7 +934,7 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, event->u.f_walk_eabt.addr2 = ptw_info.addr; break; case SMMU_PTW_ERR_TRANSLATION: - if (PTW_RECORD_FAULT(cfg)) { + if (PTW_RECORD_FAULT(ptw_info, cfg)) { event->type = SMMU_EVT_F_TRANSLATION; event->u.f_translation.addr2 = ptw_info.addr; event->u.f_translation.class = class; @@ -941,7 +942,7 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, } break; case SMMU_PTW_ERR_ADDR_SIZE: - if (PTW_RECORD_FAULT(cfg)) { + if (PTW_RECORD_FAULT(ptw_info, cfg)) { event->type = SMMU_EVT_F_ADDR_SIZE; event->u.f_addr_size.addr2 = ptw_info.addr; event->u.f_addr_size.class = class; @@ -949,7 +950,7 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, } break; case SMMU_PTW_ERR_ACCESS: - if (PTW_RECORD_FAULT(cfg)) { + if (PTW_RECORD_FAULT(ptw_info, cfg)) { event->type = SMMU_EVT_F_ACCESS; event->u.f_access.addr2 = ptw_info.addr; event->u.f_access.class = class; @@ -957,7 +958,7 @@ static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, } break; case SMMU_PTW_ERR_PERMISSION: - if (PTW_RECORD_FAULT(cfg)) { + if (PTW_RECORD_FAULT(ptw_info, cfg)) { event->type = SMMU_EVT_F_PERMISSION; event->u.f_permission.addr2 = ptw_info.addr; event->u.f_permission.class = class; From 58377c363291d76d0a40be4be34f8bc42785e484 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:17 +0000 Subject: [PATCH 2234/2884] hw/arm/smmuv3: Support and advertise nesting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everything is in place, consolidate parsing of STE cfg and setting translation stage. Advertise nesting if stage requested is "nested". Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-18-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 2d61637aed..3db6c7c135 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -261,6 +261,9 @@ static void smmuv3_init_regs(SMMUv3State *s) /* Based on sys property, the stages supported in smmu will be advertised.*/ if (s->stage && !strcmp("2", s->stage)) { s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S2P, 1); + } else if (s->stage && !strcmp("nested", s->stage)) { + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S2P, 1); } else { s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); } @@ -425,8 +428,6 @@ static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran) static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) { - cfg->stage = SMMU_STAGE_2; - if (STE_S2AA64(ste) == 0x0) { qemu_log_mask(LOG_UNIMP, "SMMUv3 AArch32 tables not supported\n"); @@ -509,6 +510,27 @@ bad_ste: return -EINVAL; } +static void decode_ste_config(SMMUTransCfg *cfg, uint32_t config) +{ + + if (STE_CFG_ABORT(config)) { + cfg->aborted = true; + return; + } + if (STE_CFG_BYPASS(config)) { + cfg->bypassed = true; + return; + } + + if (STE_CFG_S1_ENABLED(config)) { + cfg->stage = SMMU_STAGE_1; + } + + if (STE_CFG_S2_ENABLED(config)) { + cfg->stage |= SMMU_STAGE_2; + } +} + /* Returns < 0 in case of invalid STE, 0 otherwise */ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, STE *ste, SMMUEventInfo *event) @@ -525,13 +547,9 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, config = STE_CONFIG(ste); - if (STE_CFG_ABORT(config)) { - cfg->aborted = true; - return 0; - } + decode_ste_config(cfg, config); - if (STE_CFG_BYPASS(config)) { - cfg->bypassed = true; + if (cfg->aborted || cfg->bypassed) { return 0; } @@ -704,7 +722,6 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg, /* we support only those at the moment */ cfg->aa64 = true; - cfg->stage = SMMU_STAGE_1; cfg->oas = oas2bits(CD_IPS(cd)); cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); From 6783a184bb381350c43b4e35b89cd188b40c7266 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 15 Jul 2024 08:45:18 +0000 Subject: [PATCH 2235/2884] hw/arm/smmu: Refactor SMMU OAS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SMMUv3 OAS is currently hardcoded in the code to 44 bits, for nested configurations that can be a problem, as stage-2 might be shared with the CPU which might have different PARANGE, and according to SMMU manual ARM IHI 0070F.b: 6.3.6 SMMU_IDR5, OAS must match the system physical address size. This patch doesn't change the SMMU OAS, but refactors the code to make it easier to do that: - Rely everywhere on IDR5 for reading OAS instead of using the SMMU_IDR5_OAS macro, so, it is easier just to change IDR5 and it propagages correctly. - Add additional checks when OAS is greater than 48bits. - Remove unused functions/macros: pa_range/MAX_PA. Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240715084519.1189624-19-smostafa@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmu-common.c | 7 ++++--- hw/arm/smmuv3-internal.h | 13 ------------- hw/arm/smmuv3.c | 35 ++++++++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 00d7579cd3..d73ad62211 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -452,7 +452,8 @@ static int smmu_ptw_64_s1(SMMUState *bs, SMMUTransCfg *cfg, inputsize = 64 - tt->tsz; level = 4 - (inputsize - 4) / stride; indexmask = VMSA_IDXMSK(inputsize, stride, level); - baseaddr = extract64(tt->ttb, 0, 48); + + baseaddr = extract64(tt->ttb, 0, cfg->oas); baseaddr &= ~indexmask; while (level < VMSA_LEVELS) { @@ -576,8 +577,8 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, * Get the ttb from concatenated structure. * The offset is the idx * size of each ttb(number of ptes * (sizeof(pte)) */ - uint64_t baseaddr = extract64(cfg->s2cfg.vttb, 0, 48) + (1 << stride) * - idx * sizeof(uint64_t); + uint64_t baseaddr = extract64(cfg->s2cfg.vttb, 0, cfg->s2cfg.eff_ps) + + (1 << stride) * idx * sizeof(uint64_t); dma_addr_t indexmask = VMSA_IDXMSK(inputsize, stride, level); baseaddr &= ~indexmask; diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 0f3ecec804..0ebf2eebcf 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -602,19 +602,6 @@ static inline int oas2bits(int oas_field) return -1; } -static inline int pa_range(STE *ste) -{ - int oas_field = MIN(STE_S2PS(ste), SMMU_IDR5_OAS); - - if (!STE_S2AA64(ste)) { - return 40; - } - - return oas2bits(oas_field); -} - -#define MAX_PA(ste) ((1 << pa_range(ste)) - 1) - /* CD fields */ #define CD_VALID(x) extract32((x)->word[0], 31, 1) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 3db6c7c135..3971976389 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -402,10 +402,10 @@ static bool s2t0sz_valid(SMMUTransCfg *cfg) } if (cfg->s2cfg.granule_sz == 16) { - return (cfg->s2cfg.tsz >= 64 - oas2bits(SMMU_IDR5_OAS)); + return (cfg->s2cfg.tsz >= 64 - cfg->s2cfg.eff_ps); } - return (cfg->s2cfg.tsz >= MAX(64 - oas2bits(SMMU_IDR5_OAS), 16)); + return (cfg->s2cfg.tsz >= MAX(64 - cfg->s2cfg.eff_ps, 16)); } /* @@ -426,8 +426,11 @@ static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran) return nr_concat <= VMSA_MAX_S2_CONCAT; } -static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) +static int decode_ste_s2_cfg(SMMUv3State *s, SMMUTransCfg *cfg, + STE *ste) { + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); + if (STE_S2AA64(ste) == 0x0) { qemu_log_mask(LOG_UNIMP, "SMMUv3 AArch32 tables not supported\n"); @@ -460,7 +463,15 @@ static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) } /* For AA64, The effective S2PS size is capped to the OAS. */ - cfg->s2cfg.eff_ps = oas2bits(MIN(STE_S2PS(ste), SMMU_IDR5_OAS)); + cfg->s2cfg.eff_ps = oas2bits(MIN(STE_S2PS(ste), oas)); + /* + * For SMMUv3.1 and later, when OAS == IAS == 52, the stage 2 input + * range is further limited to 48 bits unless STE.S2TG indicates a + * 64KB granule. + */ + if (cfg->s2cfg.granule_sz != 16) { + cfg->s2cfg.eff_ps = MIN(cfg->s2cfg.eff_ps, 48); + } /* * It is ILLEGAL for the address in S2TTB to be outside the range * described by the effective S2PS value. @@ -536,6 +547,7 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, STE *ste, SMMUEventInfo *event) { uint32_t config; + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); int ret; if (!STE_VALID(ste)) { @@ -579,8 +591,8 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, * Stage-1 OAS defaults to OAS even if not enabled as it would be used * in input address check for stage-2. */ - cfg->oas = oas2bits(SMMU_IDR5_OAS); - ret = decode_ste_s2_cfg(cfg, ste); + cfg->oas = oas2bits(oas); + ret = decode_ste_s2_cfg(s, cfg, ste); if (ret) { goto bad_ste; } @@ -706,6 +718,7 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg, int i; SMMUTranslationStatus status; SMMUTLBEntry *entry; + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); if (!CD_VALID(cd) || !CD_AARCH64(cd)) { goto bad_cd; @@ -724,7 +737,7 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg, cfg->aa64 = true; cfg->oas = oas2bits(CD_IPS(cd)); - cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); + cfg->oas = MIN(oas2bits(oas), cfg->oas); cfg->tbi = CD_TBI(cd); cfg->asid = CD_ASID(cd); cfg->affd = CD_AFFD(cd); @@ -753,6 +766,14 @@ static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg, goto bad_cd; } + /* + * An address greater than 48 bits in size can only be output from a + * TTD when, in SMMUv3.1 and later, the effective IPS is 52 and a 64KB + * granule is in use for that translation table + */ + if (tt->granule_sz != 16) { + cfg->oas = MIN(cfg->oas, 48); + } tt->tsz = tsz; tt->ttb = CD_TTB(cd, i); From 31d93fedf41c24b0badb38cd9317590d1ef74e37 Mon Sep 17 00:00:00 2001 From: Daniyal Khan <danikhan632@gmail.com> Date: Wed, 17 Jul 2024 16:01:47 +1000 Subject: [PATCH 2236/2884] target/arm: Use float_status copy in sme_fmopa_s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We made a copy above because the fp exception flags are not propagated back to the FPST register, but then failed to use the copy. Cc: qemu-stable@nongnu.org Fixes: 558e956c719 ("target/arm: Implement FMOPA, FMOPS (non-widening)") Signed-off-by: Daniyal Khan <danikhan632@gmail.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240717060149.204788-2-richard.henderson@linaro.org [rth: Split from a larger patch] Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/sme_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index e2e0575039..5a6dd76489 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -916,7 +916,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, if (pb & 1) { uint32_t *a = vza_row + H1_4(col); uint32_t *m = vzm + H1_4(col); - *a = float32_muladd(n, *m, *a, 0, vst); + *a = float32_muladd(n, *m, *a, 0, &fpst); } col += 4; pb >>= 4; From 207d30b5fdb5b45a36f26eefcf52fe2c1714dd4f Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Wed, 17 Jul 2024 16:01:48 +1000 Subject: [PATCH 2237/2884] target/arm: Use FPST_F16 for SME FMOPA (widening) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This operation has float16 inputs and thus must use the FZ16 control not the FZ control. Cc: qemu-stable@nongnu.org Fixes: 3916841ac75 ("target/arm: Implement FMOPA, FMOPS (widening)") Reported-by: Daniyal Khan <danikhan632@gmail.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240717060149.204788-3-richard.henderson@linaro.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2374 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/translate-sme.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 46c7fce8b4..185a8a917b 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -304,6 +304,7 @@ static bool do_outprod(DisasContext *s, arg_op *a, MemOp esz, } static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, + ARMFPStatusFlavour e_fpst, gen_helper_gvec_5_ptr *fn) { int svl = streaming_vec_reg_size(s); @@ -319,15 +320,18 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, zm = vec_full_reg_ptr(s, a->zm); pn = pred_full_reg_ptr(s, a->pn); pm = pred_full_reg_ptr(s, a->pm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(e_fpst); fn(za, zn, zm, pn, pm, fpst, tcg_constant_i32(desc)); return true; } -TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_h) -TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_s) -TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, gen_helper_sme_fmopa_d) +TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a, + MO_32, FPST_FPCR_F16, gen_helper_sme_fmopa_h) +TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, + MO_32, FPST_FPCR, gen_helper_sme_fmopa_s) +TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, + MO_64, FPST_FPCR, gen_helper_sme_fmopa_d) /* TODO: FEAT_EBF16 */ TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa) From f103ecccb68184b53a9c9bd80536cf2d4080f834 Mon Sep 17 00:00:00 2001 From: Daniyal Khan <danikhan632@gmail.com> Date: Wed, 17 Jul 2024 16:01:49 +1000 Subject: [PATCH 2238/2884] tests/tcg/aarch64: Add test cases for SME FMOPA (widening) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniyal Khan <danikhan632@gmail.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20240717060149.204788-4-richard.henderson@linaro.org Message-Id: 172090222034.13953.16888708708822922098-1@git.sr.ht [rth: Split test from a larger patch, tidy assembly] Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- tests/tcg/aarch64/Makefile.target | 5 ++- tests/tcg/aarch64/sme-fmopa-1.c | 63 +++++++++++++++++++++++++++++++ tests/tcg/aarch64/sme-fmopa-2.c | 56 +++++++++++++++++++++++++++ tests/tcg/aarch64/sme-fmopa-3.c | 63 +++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/aarch64/sme-fmopa-1.c create mode 100644 tests/tcg/aarch64/sme-fmopa-2.c create mode 100644 tests/tcg/aarch64/sme-fmopa-3.c diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index b53218e115..8cc62eb456 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -70,8 +70,9 @@ endif # SME Tests ifneq ($(CROSS_AS_HAS_ARMV9_SME),) -AARCH64_TESTS += sme-outprod1 sme-smopa-1 sme-smopa-2 -sme-outprod1 sme-smopa-1 sme-smopa-2: CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) +SME_TESTS = sme-outprod1 sme-smopa-1 sme-smopa-2 sme-fmopa-1 sme-fmopa-2 sme-fmopa-3 +AARCH64_TESTS += $(SME_TESTS) +$(SME_TESTS): CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif # System Registers Tests diff --git a/tests/tcg/aarch64/sme-fmopa-1.c b/tests/tcg/aarch64/sme-fmopa-1.c new file mode 100644 index 0000000000..652c4ea090 --- /dev/null +++ b/tests/tcg/aarch64/sme-fmopa-1.c @@ -0,0 +1,63 @@ +/* + * SME outer product, 1 x 1. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdio.h> + +static void foo(float *dst) +{ + asm(".arch_extension sme\n\t" + "smstart\n\t" + "ptrue p0.s, vl4\n\t" + "fmov z0.s, #1.0\n\t" + /* + * An outer product of a vector of 1.0 by itself should be a matrix of 1.0. + * Note that we are using tile 1 here (za1.s) rather than tile 0. + */ + "zero {za}\n\t" + "fmopa za1.s, p0/m, p0/m, z0.s, z0.s\n\t" + /* + * Read the first 4x4 sub-matrix of elements from tile 1: + * Note that za1h should be interchangeable here. + */ + "mov w12, #0\n\t" + "mova z0.s, p0/m, za1v.s[w12, #0]\n\t" + "mova z1.s, p0/m, za1v.s[w12, #1]\n\t" + "mova z2.s, p0/m, za1v.s[w12, #2]\n\t" + "mova z3.s, p0/m, za1v.s[w12, #3]\n\t" + /* + * And store them to the input pointer (dst in the C code): + */ + "st1w {z0.s}, p0, [%0]\n\t" + "add x0, x0, #16\n\t" + "st1w {z1.s}, p0, [x0]\n\t" + "add x0, x0, #16\n\t" + "st1w {z2.s}, p0, [x0]\n\t" + "add x0, x0, #16\n\t" + "st1w {z3.s}, p0, [x0]\n\t" + "smstop" + : : "r"(dst) + : "x12", "d0", "d1", "d2", "d3", "memory"); +} + +int main() +{ + float dst[16] = { }; + + foo(dst); + + for (int i = 0; i < 16; i++) { + if (dst[i] != 1.0f) { + goto failure; + } + } + /* success */ + return 0; + + failure: + for (int i = 0; i < 16; i++) { + printf("%f%c", dst[i], i % 4 == 3 ? '\n' : ' '); + } + return 1; +} diff --git a/tests/tcg/aarch64/sme-fmopa-2.c b/tests/tcg/aarch64/sme-fmopa-2.c new file mode 100644 index 0000000000..15f0972d83 --- /dev/null +++ b/tests/tcg/aarch64/sme-fmopa-2.c @@ -0,0 +1,56 @@ +/* + * SME outer product, FZ vs FZ16 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdint.h> +#include <stdio.h> + +static void test_fmopa(uint32_t *result) +{ + asm(".arch_extension sme\n\t" + "smstart\n\t" /* Z*, P* and ZArray cleared */ + "ptrue p2.b, vl16\n\t" /* Limit vector length to 16 */ + "ptrue p5.b, vl16\n\t" + "movi d0, #0x00ff\n\t" /* fp16 denormal */ + "movi d16, #0x00ff\n\t" + "mov w15, #0x0001000000\n\t" /* FZ=1, FZ16=0 */ + "msr fpcr, x15\n\t" + "fmopa za3.s, p2/m, p5/m, z16.h, z0.h\n\t" + "mov w15, #0\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "mov w15, #2\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "smstop" + : "+r"(result) : + : "x15", "x16", "p2", "p5", "d0", "d16", "memory"); +} + +int main(void) +{ + uint32_t result[4 * 4] = { }; + + test_fmopa(result); + + if (result[0] != 0x2f7e0100) { + printf("Test failed: Incorrect output in first 4 bytes\n" + "Expected: %08x\n" + "Got: %08x\n", + 0x2f7e0100, result[0]); + return 1; + } + + for (int i = 1; i < 16; ++i) { + if (result[i] != 0) { + printf("Test failed: Non-zero word at position %d\n", i); + return 1; + } + } + + return 0; +} diff --git a/tests/tcg/aarch64/sme-fmopa-3.c b/tests/tcg/aarch64/sme-fmopa-3.c new file mode 100644 index 0000000000..3bfec34914 --- /dev/null +++ b/tests/tcg/aarch64/sme-fmopa-3.c @@ -0,0 +1,63 @@ +/* + * SME outer product, [ 1 2 3 4 ] squared + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <math.h> + +static const float i_1234[4] = { + 1.0f, 2.0f, 3.0f, 4.0f +}; + +static const float expected[4] = { + 4.515625f, 5.750000f, 6.984375f, 8.218750f +}; + +static void test_fmopa(float *result) +{ + asm(".arch_extension sme\n\t" + "smstart\n\t" /* ZArray cleared */ + "ptrue p2.b, vl16\n\t" /* Limit vector length to 16 */ + "ld1w {z0.s}, p2/z, [%1]\n\t" + "mov w15, #0\n\t" + "mov za3h.s[w15, 0], p2/m, z0.s\n\t" + "mov za3h.s[w15, 1], p2/m, z0.s\n\t" + "mov w15, #2\n\t" + "mov za3h.s[w15, 0], p2/m, z0.s\n\t" + "mov za3h.s[w15, 1], p2/m, z0.s\n\t" + "msr fpcr, xzr\n\t" + "fmopa za3.s, p2/m, p2/m, z0.h, z0.h\n\t" + "mov w15, #0\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "mov w15, #2\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "smstop" + : "+r"(result) : "r"(i_1234) + : "x15", "x16", "p2", "d0", "memory"); +} + +int main(void) +{ + float result[4 * 4] = { }; + int ret = 0; + + test_fmopa(result); + + for (int i = 0; i < 4; i++) { + float actual = result[i]; + if (fabsf(actual - expected[i]) > 0.001f) { + printf("Test failed at element %d: Expected %f, got %f\n", + i, expected[i], actual); + ret = 1; + } + } + return ret; +} From 71328d82896642d79d4e538e5a56c5e6762a219a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Tue, 16 Jul 2024 21:50:30 +0900 Subject: [PATCH 2239/2884] tests/arm-cpu-features: Do not assume PMU availability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asahi Linux supports KVM but lacks PMU support. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240716-pmu-v3-1-8c7c1858a227@daynix.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- tests/qtest/arm-cpu-features.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 966c65d5c3..cfd6f77353 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -509,6 +509,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data) assert_set_feature(qts, "host", "kvm-no-adjvtime", false); if (g_str_equal(qtest_get_arch(), "aarch64")) { + bool kvm_supports_pmu; bool kvm_supports_steal_time; bool kvm_supports_sve; char max_name[8], name[8]; @@ -537,11 +538,6 @@ static void test_query_cpu_model_expansion_kvm(const void *data) assert_has_feature_enabled(qts, "host", "aarch64"); - /* Enabling and disabling pmu should always work. */ - assert_has_feature_enabled(qts, "host", "pmu"); - assert_set_feature(qts, "host", "pmu", false); - assert_set_feature(qts, "host", "pmu", true); - /* * Some features would be enabled by default, but they're disabled * because this instance of KVM doesn't support them. Test that the @@ -551,11 +547,18 @@ static void test_query_cpu_model_expansion_kvm(const void *data) assert_has_feature(qts, "host", "sve"); resp = do_query_no_props(qts, "host"); + kvm_supports_pmu = resp_get_feature(resp, "pmu"); kvm_supports_steal_time = resp_get_feature(resp, "kvm-steal-time"); kvm_supports_sve = resp_get_feature(resp, "sve"); vls = resp_get_sve_vls(resp); qobject_unref(resp); + if (kvm_supports_pmu) { + /* If we have pmu then we should be able to toggle it. */ + assert_set_feature(qts, "host", "pmu", false); + assert_set_feature(qts, "host", "pmu", true); + } + if (kvm_supports_steal_time) { /* If we have steal-time then we should be able to toggle it. */ assert_set_feature(qts, "host", "kvm-steal-time", false); From 30a1690f2402e6c1582d5b3ebcf7940bfe2fad4b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Tue, 16 Jul 2024 21:50:33 +0900 Subject: [PATCH 2240/2884] hvf: arm: Do not advance PC when raising an exception hvf did not advance PC when raising an exception for most unhandled system registers, but it mistakenly advanced PC when raising an exception for GICv3 registers. Cc: qemu-stable@nongnu.org Fixes: a2260983c655 ("hvf: arm: Add support for GICv3") Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-id: 20240716-pmu-v3-4-8c7c1858a227@daynix.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index ef9bc42738..eb090e67a2 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1278,6 +1278,7 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ if (!hvf_sysreg_read_cp(cpu, reg, &val)) { hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + return 1; } break; case SYSREG_DBGBVR0_EL1: From 1c15dd632bf05a1649f8314d103efe47cde32e84 Mon Sep 17 00:00:00 2001 From: Song Gao <gaosong@loongson.cn> Date: Thu, 11 Jul 2024 10:44:54 +0800 Subject: [PATCH 2241/2884] target/loongarch/gdbstub: Add vector registers support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB already support LoongArch vector extension[1], QEMU gdb adds LoongArch vector registers support, so that users can use 'info all-registers' to get all vector registers values. [1]: https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=1e9569f383a3d5a88ee07d0c2401bd95613c222e Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewd-by: Bibo Mao <maobibo@loongson.cn> Message-Id: <20240711024454.3075183-1-gaosong@loongson.cn> --- configs/targets/loongarch64-linux-user.mak | 2 +- configs/targets/loongarch64-softmmu.mak | 2 +- gdb-xml/loongarch-lasx.xml | 60 ++++++++++++++++++ gdb-xml/loongarch-lsx.xml | 59 +++++++++++++++++ target/loongarch/gdbstub.c | 73 +++++++++++++++++++++- 5 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 gdb-xml/loongarch-lasx.xml create mode 100644 gdb-xml/loongarch-lsx.xml diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak index d878e5a113..ea9b7e839a 100644 --- a/configs/targets/loongarch64-linux-user.mak +++ b/configs/targets/loongarch64-linux-user.mak @@ -1,4 +1,4 @@ # Default configuration for loongarch64-linux-user TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch -TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index 65b65e0c34..ce19ab6a16 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -2,6 +2,6 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml # all boards require libfdt TARGET_NEED_FDT=y diff --git a/gdb-xml/loongarch-lasx.xml b/gdb-xml/loongarch-lasx.xml new file mode 100644 index 0000000000..753b982c65 --- /dev/null +++ b/gdb-xml/loongarch-lasx.xml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.loongarch.lasx"> + <vector id="v8f32" type="ieee_single" count="8"/> + <vector id="v4f64" type="ieee_double" count="4"/> + <vector id="v32i8" type="int8" count="32"/> + <vector id="v16i16" type="int16" count="16"/> + <vector id="v8i32" type="int32" count="8"/> + <vector id="v4i64" type="int64" count="4"/> + <vector id="v2ui128" type="uint128" count="2"/> + + <union id="lasxv"> + <field name="v8_float" type="v8f32"/> + <field name="v4_double" type="v4f64"/> + <field name="v32_int8" type="v32i8"/> + <field name="v16_int16" type="v16i16"/> + <field name="v8_int32" type="v8i32"/> + <field name="v4_int64" type="v4i64"/> + <field name="v2_uint128" type="v2ui128"/> + </union> + + <reg name="xr0" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr1" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr2" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr3" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr4" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr5" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr6" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr7" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr8" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr9" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr10" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr11" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr12" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr13" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr14" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr15" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr16" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr17" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr18" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr19" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr20" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr21" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr22" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr23" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr24" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr25" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr26" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr27" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr28" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr29" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr30" bitsize="256" type="lasxv" group="lasx"/> + <reg name="xr31" bitsize="256" type="lasxv" group="lasx"/> +</feature> diff --git a/gdb-xml/loongarch-lsx.xml b/gdb-xml/loongarch-lsx.xml new file mode 100644 index 0000000000..51af1c6fd5 --- /dev/null +++ b/gdb-xml/loongarch-lsx.xml @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.loongarch.lsx"> + <vector id="v4f32" type="ieee_single" count="4"/> + <vector id="v2f64" type="ieee_double" count="2"/> + <vector id="v16i8" type="int8" count="16"/> + <vector id="v8i16" type="int16" count="8"/> + <vector id="v4i32" type="int32" count="4"/> + <vector id="v2i64" type="int64" count="2"/> + + <union id="lsxv"> + <field name="v4_float" type="v4f32"/> + <field name="v2_double" type="v2f64"/> + <field name="v16_int8" type="v16i8"/> + <field name="v8_int16" type="v8i16"/> + <field name="v4_int32" type="v4i32"/> + <field name="v2_int64" type="v2i64"/> + <field name="uint128" type="uint128"/> + </union> + + <reg name="vr0" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr1" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr2" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr3" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr4" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr5" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr6" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr7" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr8" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr9" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr10" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr11" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr12" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr13" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr14" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr15" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr16" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr17" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr18" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr19" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr20" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr21" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr22" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr23" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr26" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr25" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr26" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr27" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr28" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr29" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr30" bitsize="128" type="lsxv" group="lsx"/> + <reg name="vr31" bitsize="128" type="lsxv" group="lsx"/> +</feature> diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index a0e1439bd0..7ca245ee81 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -116,8 +116,77 @@ static int loongarch_gdb_set_fpu(CPUState *cs, uint8_t *mem_buf, int n) return length; } +#define VREG_NUM 32 +#define REG64_LEN 64 + +static int loongarch_gdb_get_vec(CPUState *cs, GByteArray *mem_buf, int n, int vl) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + int i, length = 0; + + if (0 <= n && n < VREG_NUM) { + for (i = 0; i < vl / REG64_LEN; i++) { + length += gdb_get_reg64(mem_buf, env->fpr[n].vreg.D(i)); + } + } + + return length; +} + +static int loongarch_gdb_set_vec(CPUState *cs, uint8_t *mem_buf, int n, int vl) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + int i, length = 0; + + if (0 <= n && n < VREG_NUM) { + for (i = 0; i < vl / REG64_LEN; i++) { + env->fpr[n].vreg.D(i) = ldq_le_p(mem_buf + 8 * i); + length += 8; + } + } + + return length; +} + +static int loongarch_gdb_get_lsx(CPUState *cs, GByteArray *mem_buf, int n) +{ + return loongarch_gdb_get_vec(cs, mem_buf, n, LSX_LEN); +} + +static int loongarch_gdb_set_lsx(CPUState *cs, uint8_t *mem_buf, int n) +{ + return loongarch_gdb_set_vec(cs, mem_buf, n, LSX_LEN); +} + +static int loongarch_gdb_get_lasx(CPUState *cs, GByteArray *mem_buf, int n) +{ + return loongarch_gdb_get_vec(cs, mem_buf, n, LASX_LEN); +} + +static int loongarch_gdb_set_lasx(CPUState *cs, uint8_t *mem_buf, int n) +{ + return loongarch_gdb_set_vec(cs, mem_buf, n, LASX_LEN); +} + void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs) { - gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu, - gdb_find_static_feature("loongarch-fpu.xml"), 0); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, FP)) { + gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu, + gdb_find_static_feature("loongarch-fpu.xml"), 0); + } + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LSX)) { + gdb_register_coprocessor(cs, loongarch_gdb_get_lsx, loongarch_gdb_set_lsx, + gdb_find_static_feature("loongarch-lsx.xml"), 0); + } + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LASX)) { + gdb_register_coprocessor(cs, loongarch_gdb_get_lasx, loongarch_gdb_set_lasx, + gdb_find_static_feature("loongarch-lasx.xml"), 0); + } } From a00c22e5052dacb334b5c7865f4edb0035639864 Mon Sep 17 00:00:00 2001 From: Song Gao <gaosong@loongson.cn> Date: Thu, 18 Jul 2024 16:32:54 +0800 Subject: [PATCH 2242/2884] hw/loongarch: Remove unimplemented extioi INT_encode mode Remove extioi INT_encode encode mode, because we don't emulate it. Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Message-Id: <20240718083254.748179-1-gaosong@loongson.cn> --- include/hw/intc/loongarch_extioi.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index eccc2e0d18..626a37dfa1 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -50,7 +50,6 @@ #define EXTIOI_HAS_CPU_ENCODE (3) #define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ | BIT(EXTIOI_HAS_ENABLE_OPTION) \ - | BIT(EXTIOI_HAS_INT_ENCODE) \ | BIT(EXTIOI_HAS_CPU_ENCODE)) #define EXTIOI_VIRT_CONFIG (0x4) #define EXTIOI_ENABLE (1) From 3ed016f525c8010e66be62d3ca6829eaa9b7cfb5 Mon Sep 17 00:00:00 2001 From: Xianglai Li <lixianglai@loongson.cn> Date: Mon, 24 Jun 2024 11:33:19 +0800 Subject: [PATCH 2243/2884] hw/loongarch: Modify flash block size to 256K loongarch added a common library for edk2 to parse flash base addresses through fdt. For compatibility with other architectures, the flash block size in qemu is now changed to 256k. Signed-off-by: Xianglai Li <lixianglai@loongson.cn> Reviewed-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240624033319.999631-1-lixianglai@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> --- include/hw/loongarch/virt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 8fdfacf268..603c1cebdb 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -20,7 +20,7 @@ #define VIRT_FWCFG_BASE 0x1e020000UL #define VIRT_BIOS_BASE 0x1c000000UL #define VIRT_BIOS_SIZE (16 * MiB) -#define VIRT_FLASH_SECTOR_SIZE (128 * KiB) +#define VIRT_FLASH_SECTOR_SIZE (256 * KiB) #define VIRT_FLASH0_BASE VIRT_BIOS_BASE #define VIRT_FLASH0_SIZE VIRT_BIOS_SIZE #define VIRT_FLASH1_BASE 0x1d000000UL From 66181376200870ea53ba6c6488683cff1c8795b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:38 +0100 Subject: [PATCH 2244/2884] qga: drop blocking of guest-get-memory-block-size command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This command has never existed in tree, since it was renamed to guest-get-memory-block-info before being merged. Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240712132459.3974109-2-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 2 +- qga/commands-win32.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7f05996495..76af98ba32 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -3099,7 +3099,7 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) "guest-suspend-disk", "guest-suspend-ram", "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus", "guest-get-memory-blocks", "guest-set-memory-blocks", - "guest-get-memory-block-size", "guest-get-memory-block-info", + "guest-get-memory-block-info", NULL}; char **p = (char **)list; diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 0d1b836e87..9fe670d5b4 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1995,7 +1995,7 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) "guest-suspend-hybrid", "guest-set-vcpus", "guest-get-memory-blocks", "guest-set-memory-blocks", - "guest-get-memory-block-size", "guest-get-memory-block-info", + "guest-get-memory-block-info", NULL}; char **p = (char **)list_unsupported; From 4210027b5002e66a308d7c52b2e3204438c363cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:39 +0100 Subject: [PATCH 2245/2884] qga: move linux vcpu command impls to commands-linux.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qmp_guest_set_vcpus and qmp_guest_get_vcpus command impls in commands-posix.c are surrounded by '#ifdef __linux__' so should instead live in commands-linux.c Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240712132459.3974109-3-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 141 +++++++++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 139 ------------------------------------------ 2 files changed, 141 insertions(+), 139 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 214e408fcd..78580ac39d 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qga-qapi-commands.h" #include "commands-common.h" #include "cutils.h" #include <mntent.h> @@ -284,3 +285,143 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp) return i; } #endif /* CONFIG_FSFREEZE */ + +/* Transfer online/offline status between @vcpu and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@vcpu direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - W: vcpu->online + * - W: vcpu->can_offline + * + * In @vcpu-to-system direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - R: vcpu->online + * + * Written members remain unmodified on error. + */ +static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, + char *dirpath, Error **errp) +{ + int fd; + int res; + int dirfd; + static const char fn[] = "online"; + + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + return; + } + + fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); + if (fd == -1) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); + } else if (sys2vcpu) { + vcpu->online = true; + vcpu->can_offline = false; + } else if (!vcpu->online) { + error_setg(errp, "logical processor #%" PRId64 " can't be " + "offlined", vcpu->logical_id); + } /* otherwise pretend successful re-onlining */ + } else { + unsigned char status; + + res = pread(fd, &status, 1, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); + } else if (res == 0) { + error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, + fn); + } else if (sys2vcpu) { + vcpu->online = (status != '0'); + vcpu->can_offline = true; + } else if (vcpu->online != (status != '0')) { + status = '0' + vcpu->online; + if (pwrite(fd, &status, 1, 0) == -1) { + error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, + fn); + } + } /* otherwise pretend successful re-(on|off)-lining */ + + res = close(fd); + g_assert(res == 0); + } + + res = close(dirfd); + g_assert(res == 0); +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + GuestLogicalProcessorList *head, **tail; + const char *cpu_dir = "/sys/devices/system/cpu"; + const gchar *line; + g_autoptr(GDir) cpu_gdir = NULL; + Error *local_err = NULL; + + head = NULL; + tail = &head; + cpu_gdir = g_dir_open(cpu_dir, 0, NULL); + + if (cpu_gdir == NULL) { + error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir); + return NULL; + } + + while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) { + GuestLogicalProcessor *vcpu; + int64_t id; + if (sscanf(line, "cpu%" PRId64, &id)) { + g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/" + "cpu%" PRId64 "/", id); + vcpu = g_malloc0(sizeof *vcpu); + vcpu->logical_id = id; + vcpu->has_can_offline = true; /* lolspeak ftw */ + transfer_vcpu(vcpu, true, path, &local_err); + QAPI_LIST_APPEND(tail, vcpu); + } + } + + if (local_err == NULL) { + /* there's no guest with zero VCPUs */ + g_assert(head != NULL); + return head; + } + + qapi_free_GuestLogicalProcessorList(head); + error_propagate(errp, local_err); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + int64_t processed; + Error *local_err = NULL; + + processed = 0; + while (vcpus != NULL) { + char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", + vcpus->value->logical_id); + + transfer_vcpu(vcpus->value, false, path, &local_err); + g_free(path); + if (local_err != NULL) { + break; + } + ++processed; + vcpus = vcpus->next; + } + + if (local_err != NULL) { + if (processed == 0) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + } + } + + return processed; +} diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 76af98ba32..a8ef41f175 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2003,145 +2003,6 @@ void qmp_guest_suspend_hybrid(Error **errp) guest_suspend(SUSPEND_MODE_HYBRID, errp); } -/* Transfer online/offline status between @vcpu and the guest system. - * - * On input either @errp or *@errp must be NULL. - * - * In system-to-@vcpu direction, the following @vcpu fields are accessed: - * - R: vcpu->logical_id - * - W: vcpu->online - * - W: vcpu->can_offline - * - * In @vcpu-to-system direction, the following @vcpu fields are accessed: - * - R: vcpu->logical_id - * - R: vcpu->online - * - * Written members remain unmodified on error. - */ -static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, - char *dirpath, Error **errp) -{ - int fd; - int res; - int dirfd; - static const char fn[] = "online"; - - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd == -1) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - return; - } - - fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); - if (fd == -1) { - if (errno != ENOENT) { - error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); - } else if (sys2vcpu) { - vcpu->online = true; - vcpu->can_offline = false; - } else if (!vcpu->online) { - error_setg(errp, "logical processor #%" PRId64 " can't be " - "offlined", vcpu->logical_id); - } /* otherwise pretend successful re-onlining */ - } else { - unsigned char status; - - res = pread(fd, &status, 1, 0); - if (res == -1) { - error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); - } else if (res == 0) { - error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, - fn); - } else if (sys2vcpu) { - vcpu->online = (status != '0'); - vcpu->can_offline = true; - } else if (vcpu->online != (status != '0')) { - status = '0' + vcpu->online; - if (pwrite(fd, &status, 1, 0) == -1) { - error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, - fn); - } - } /* otherwise pretend successful re-(on|off)-lining */ - - res = close(fd); - g_assert(res == 0); - } - - res = close(dirfd); - g_assert(res == 0); -} - -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) -{ - GuestLogicalProcessorList *head, **tail; - const char *cpu_dir = "/sys/devices/system/cpu"; - const gchar *line; - g_autoptr(GDir) cpu_gdir = NULL; - Error *local_err = NULL; - - head = NULL; - tail = &head; - cpu_gdir = g_dir_open(cpu_dir, 0, NULL); - - if (cpu_gdir == NULL) { - error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir); - return NULL; - } - - while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) { - GuestLogicalProcessor *vcpu; - int64_t id; - if (sscanf(line, "cpu%" PRId64, &id)) { - g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/" - "cpu%" PRId64 "/", id); - vcpu = g_malloc0(sizeof *vcpu); - vcpu->logical_id = id; - vcpu->has_can_offline = true; /* lolspeak ftw */ - transfer_vcpu(vcpu, true, path, &local_err); - QAPI_LIST_APPEND(tail, vcpu); - } - } - - if (local_err == NULL) { - /* there's no guest with zero VCPUs */ - g_assert(head != NULL); - return head; - } - - qapi_free_GuestLogicalProcessorList(head); - error_propagate(errp, local_err); - return NULL; -} - -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - int64_t processed; - Error *local_err = NULL; - - processed = 0; - while (vcpus != NULL) { - char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", - vcpus->value->logical_id); - - transfer_vcpu(vcpus->value, false, path, &local_err); - g_free(path); - if (local_err != NULL) { - break; - } - ++processed; - vcpus = vcpus->next; - } - - if (local_err != NULL) { - if (processed == 0) { - error_propagate(errp, local_err); - } else { - error_free(local_err); - } - } - - return processed; -} #endif /* __linux__ */ #if defined(__linux__) || defined(__FreeBSD__) From 0c6f8824faa70d0782ce8b6a59d500bbd95acf64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:40 +0100 Subject: [PATCH 2246/2884] qga: move linux suspend command impls to commands-linux.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qmp_guest_suspend_{disk,ram,hybrid} command impls in commands-posix.c are surrounded by '#ifdef __linux__' so should instead live in commands-linux.c Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240712132459.3974109-4-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 265 +++++++++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 265 ------------------------------------------- 2 files changed, 265 insertions(+), 265 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 78580ac39d..3fabf54882 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -286,6 +286,271 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp) } #endif /* CONFIG_FSFREEZE */ + +#define LINUX_SYS_STATE_FILE "/sys/power/state" +#define SUSPEND_SUPPORTED 0 +#define SUSPEND_NOT_SUPPORTED 1 + +typedef enum { + SUSPEND_MODE_DISK = 0, + SUSPEND_MODE_RAM = 1, + SUSPEND_MODE_HYBRID = 2, +} SuspendMode; + +/* + * Executes a command in a child process using g_spawn_sync, + * returning an int >= 0 representing the exit status of the + * process. + * + * If the program wasn't found in path, returns -1. + * + * If a problem happened when creating the child process, + * returns -1 and errp is set. + */ +static int run_process_child(const char *command[], Error **errp) +{ + int exit_status, spawn_flag; + GError *g_err = NULL; + bool success; + + spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL; + + success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, + NULL, NULL, NULL, NULL, + &exit_status, &g_err); + + if (success) { + return WEXITSTATUS(exit_status); + } + + if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { + error_setg(errp, "failed to create child process, error '%s'", + g_err->message); + } + + g_error_free(g_err); + return -1; +} + +static bool systemd_supports_mode(SuspendMode mode, Error **errp) +{ + const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", + "systemd-hybrid-sleep"}; + const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, errp); + + /* + * systemctl status uses LSB return codes so we can expect + * status > 0 and be ok. To assert if the guest has support + * for the selected suspend mode, status should be < 4. 4 is + * the code for unknown service status, the return value when + * the service does not exist. A common value is status = 3 + * (program is not running). + */ + if (status > 0 && status < 4) { + return true; + } + + return false; +} + +static void systemd_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; + const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program 'systemctl %s' was not found", + systemctl_args[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "the helper program 'systemctl %s' returned an " + "unexpected exit status code (%d)", + systemctl_args[mode], status); + } +} + +static bool pmutils_supports_mode(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_args[3] = {"--hibernate", "--suspend", + "--suspend-hybrid"}; + const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == SUSPEND_SUPPORTED) { + return true; + } + + if ((status == -1) && !local_err) { + return false; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", "pm-is-supported", status); + } + + return false; +} + +static void pmutils_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", + "pm-suspend-hybrid"}; + const char *cmd[2] = {pmutils_binaries[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program '%s' was not found", + pmutils_binaries[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", pmutils_binaries[mode], status); + } +} + +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) +{ + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; + char buf[32]; /* hopefully big enough */ + int fd; + ssize_t ret; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return false; + } + + fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); + if (fd < 0) { + return false; + } + + ret = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (ret <= 0) { + return false; + } + buf[ret] = '\0'; + + if (strstr(buf, sysfile_str)) { + return true; + } + return false; +} + +static void linux_sys_state_suspend(SuspendMode mode, Error **errp) +{ + g_autoptr(GError) local_gerr = NULL; + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return; + } + + if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str, + -1, &local_gerr)) { + error_setg(errp, "suspend: cannot write to '%s': %s", + LINUX_SYS_STATE_FILE, local_gerr->message); + return; + } +} + +static void guest_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + bool mode_supported = false; + + if (systemd_supports_mode(mode, &local_err)) { + mode_supported = true; + systemd_suspend(mode, &local_err); + + if (!local_err) { + return; + } + } + + error_free(local_err); + local_err = NULL; + + if (pmutils_supports_mode(mode, &local_err)) { + mode_supported = true; + pmutils_suspend(mode, &local_err); + + if (!local_err) { + return; + } + } + + error_free(local_err); + local_err = NULL; + + if (linux_sys_state_supports_mode(mode, &local_err)) { + mode_supported = true; + linux_sys_state_suspend(mode, &local_err); + } + + if (!mode_supported) { + error_free(local_err); + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + } else { + error_propagate(errp, local_err); + } +} + +void qmp_guest_suspend_disk(Error **errp) +{ + guest_suspend(SUSPEND_MODE_DISK, errp); +} + +void qmp_guest_suspend_ram(Error **errp) +{ + guest_suspend(SUSPEND_MODE_RAM, errp); +} + +void qmp_guest_suspend_hybrid(Error **errp) +{ + guest_suspend(SUSPEND_MODE_HYBRID, errp); +} + /* Transfer online/offline status between @vcpu and the guest system. * * On input either @errp or *@errp must be NULL. diff --git a/qga/commands-posix.c b/qga/commands-posix.c index a8ef41f175..ef21da63be 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1738,271 +1738,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) } #endif /* CONFIG_FSTRIM */ - -#define LINUX_SYS_STATE_FILE "/sys/power/state" -#define SUSPEND_SUPPORTED 0 -#define SUSPEND_NOT_SUPPORTED 1 - -typedef enum { - SUSPEND_MODE_DISK = 0, - SUSPEND_MODE_RAM = 1, - SUSPEND_MODE_HYBRID = 2, -} SuspendMode; - -/* - * Executes a command in a child process using g_spawn_sync, - * returning an int >= 0 representing the exit status of the - * process. - * - * If the program wasn't found in path, returns -1. - * - * If a problem happened when creating the child process, - * returns -1 and errp is set. - */ -static int run_process_child(const char *command[], Error **errp) -{ - int exit_status, spawn_flag; - GError *g_err = NULL; - bool success; - - spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | - G_SPAWN_STDERR_TO_DEV_NULL; - - success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, - NULL, NULL, NULL, NULL, - &exit_status, &g_err); - - if (success) { - return WEXITSTATUS(exit_status); - } - - if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { - error_setg(errp, "failed to create child process, error '%s'", - g_err->message); - } - - g_error_free(g_err); - return -1; -} - -static bool systemd_supports_mode(SuspendMode mode, Error **errp) -{ - const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", - "systemd-hybrid-sleep"}; - const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; - int status; - - status = run_process_child(cmd, errp); - - /* - * systemctl status uses LSB return codes so we can expect - * status > 0 and be ok. To assert if the guest has support - * for the selected suspend mode, status should be < 4. 4 is - * the code for unknown service status, the return value when - * the service does not exist. A common value is status = 3 - * (program is not running). - */ - if (status > 0 && status < 4) { - return true; - } - - return false; -} - -static void systemd_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; - const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; - int status; - - status = run_process_child(cmd, &local_err); - - if (status == 0) { - return; - } - - if ((status == -1) && !local_err) { - error_setg(errp, "the helper program 'systemctl %s' was not found", - systemctl_args[mode]); - return; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, "the helper program 'systemctl %s' returned an " - "unexpected exit status code (%d)", - systemctl_args[mode], status); - } -} - -static bool pmutils_supports_mode(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *pmutils_args[3] = {"--hibernate", "--suspend", - "--suspend-hybrid"}; - const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; - int status; - - status = run_process_child(cmd, &local_err); - - if (status == SUSPEND_SUPPORTED) { - return true; - } - - if ((status == -1) && !local_err) { - return false; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, - "the helper program '%s' returned an unexpected exit" - " status code (%d)", "pm-is-supported", status); - } - - return false; -} - -static void pmutils_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", - "pm-suspend-hybrid"}; - const char *cmd[2] = {pmutils_binaries[mode], NULL}; - int status; - - status = run_process_child(cmd, &local_err); - - if (status == 0) { - return; - } - - if ((status == -1) && !local_err) { - error_setg(errp, "the helper program '%s' was not found", - pmutils_binaries[mode]); - return; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, - "the helper program '%s' returned an unexpected exit" - " status code (%d)", pmutils_binaries[mode], status); - } -} - -static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) -{ - const char *sysfile_strs[3] = {"disk", "mem", NULL}; - const char *sysfile_str = sysfile_strs[mode]; - char buf[32]; /* hopefully big enough */ - int fd; - ssize_t ret; - - if (!sysfile_str) { - error_setg(errp, "unknown guest suspend mode"); - return false; - } - - fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); - if (fd < 0) { - return false; - } - - ret = read(fd, buf, sizeof(buf) - 1); - close(fd); - if (ret <= 0) { - return false; - } - buf[ret] = '\0'; - - if (strstr(buf, sysfile_str)) { - return true; - } - return false; -} - -static void linux_sys_state_suspend(SuspendMode mode, Error **errp) -{ - g_autoptr(GError) local_gerr = NULL; - const char *sysfile_strs[3] = {"disk", "mem", NULL}; - const char *sysfile_str = sysfile_strs[mode]; - - if (!sysfile_str) { - error_setg(errp, "unknown guest suspend mode"); - return; - } - - if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str, - -1, &local_gerr)) { - error_setg(errp, "suspend: cannot write to '%s': %s", - LINUX_SYS_STATE_FILE, local_gerr->message); - return; - } -} - -static void guest_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - bool mode_supported = false; - - if (systemd_supports_mode(mode, &local_err)) { - mode_supported = true; - systemd_suspend(mode, &local_err); - - if (!local_err) { - return; - } - } - - error_free(local_err); - local_err = NULL; - - if (pmutils_supports_mode(mode, &local_err)) { - mode_supported = true; - pmutils_suspend(mode, &local_err); - - if (!local_err) { - return; - } - } - - error_free(local_err); - local_err = NULL; - - if (linux_sys_state_supports_mode(mode, &local_err)) { - mode_supported = true; - linux_sys_state_suspend(mode, &local_err); - } - - if (!mode_supported) { - error_free(local_err); - error_setg(errp, - "the requested suspend mode is not supported by the guest"); - } else { - error_propagate(errp, local_err); - } -} - -void qmp_guest_suspend_disk(Error **errp) -{ - guest_suspend(SUSPEND_MODE_DISK, errp); -} - -void qmp_guest_suspend_ram(Error **errp) -{ - guest_suspend(SUSPEND_MODE_RAM, errp); -} - -void qmp_guest_suspend_hybrid(Error **errp) -{ - guest_suspend(SUSPEND_MODE_HYBRID, errp); -} - #endif /* __linux__ */ #if defined(__linux__) || defined(__FreeBSD__) From 329cefe405e0b4e1e7a97ef84734dac82668a2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:41 +0100 Subject: [PATCH 2247/2884] qga: move linux fs/disk command impls to commands-linux.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qmp_guest_{fstrim, get_fsinfo, get_disks} command impls in commands-posix.c are surrounded by '#ifdef __linux__' so should instead live in commands-linux.c Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240712132459.3974109-5-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 904 ++++++++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 909 ------------------------------------------- 2 files changed, 904 insertions(+), 909 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 3fabf54882..084e6c9e85 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -14,10 +14,21 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qga-qapi-commands.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" #include "commands-common.h" #include "cutils.h" #include <mntent.h> #include <sys/ioctl.h> +#include <mntent.h> +#include <linux/nvme_ioctl.h> +#include "block/nvme.h" + +#ifdef CONFIG_LIBUDEV +#include <libudev.h> +#endif + +#include <sys/statvfs.h> #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) static int dev_major_minor(const char *devpath, @@ -286,6 +297,899 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp) } #endif /* CONFIG_FSFREEZE */ +#if defined(CONFIG_FSFREEZE) + +static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) +{ + char *path; + char *dpath; + char *driver = NULL; + char buf[PATH_MAX]; + ssize_t len; + + path = g_strndup(syspath, pathlen); + dpath = g_strdup_printf("%s/driver", path); + len = readlink(dpath, buf, sizeof(buf) - 1); + if (len != -1) { + buf[len] = 0; + driver = g_path_get_basename(buf); + } + g_free(dpath); + g_free(path); + return driver; +} + +static int compare_uint(const void *_a, const void *_b) +{ + unsigned int a = *(unsigned int *)_a; + unsigned int b = *(unsigned int *)_b; + + return a < b ? -1 : a > b ? 1 : 0; +} + +/* Walk the specified sysfs and build a sorted list of host or ata numbers */ +static int build_hosts(char const *syspath, char const *host, bool ata, + unsigned int *hosts, int hosts_max, Error **errp) +{ + char *path; + DIR *dir; + struct dirent *entry; + int i = 0; + + path = g_strndup(syspath, host - syspath); + dir = opendir(path); + if (!dir) { + error_setg_errno(errp, errno, "opendir(\"%s\")", path); + g_free(path); + return -1; + } + + while (i < hosts_max) { + entry = readdir(dir); + if (!entry) { + break; + } + if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) { + ++i; + } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) { + ++i; + } + } + + qsort(hosts, i, sizeof(hosts[0]), compare_uint); + + g_free(path); + closedir(dir); + return i; +} + +/* + * Store disk device info for devices on the PCI bus. + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_pci_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int pci[4], host, hosts[8], tgt[3]; + int i, nhosts = 0, pcilen; + GuestPCIAddress *pciaddr = disk->pci_controller; + bool has_ata = false, has_host = false, has_tgt = false; + char *p, *q, *driver = NULL; + bool ret = false; + + p = strstr(syspath, "/devices/pci"); + if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { + g_debug("only pci device is supported: sysfs path '%s'", syspath); + return false; + } + + p += 12 + pcilen; + while (true) { + driver = get_pci_driver(syspath, p - syspath, errp); + if (driver && (g_str_equal(driver, "ata_piix") || + g_str_equal(driver, "sym53c8xx") || + g_str_equal(driver, "virtio-pci") || + g_str_equal(driver, "ahci") || + g_str_equal(driver, "nvme") || + g_str_equal(driver, "xhci_hcd") || + g_str_equal(driver, "ehci-pci"))) { + break; + } + + g_free(driver); + if (sscanf(p, "/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { + p += pcilen; + continue; + } + + g_debug("unsupported driver or sysfs path '%s'", syspath); + return false; + } + + p = strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + tgt, tgt + 1, tgt + 2) == 3) { + has_tgt = true; + } + + p = strstr(syspath, "/ata"); + if (p) { + q = p + 4; + has_ata = true; + } else { + p = strstr(syspath, "/host"); + q = p + 5; + } + if (p && sscanf(q, "%u", &host) == 1) { + has_host = true; + nhosts = build_hosts(syspath, p, has_ata, hosts, + ARRAY_SIZE(hosts), errp); + if (nhosts < 0) { + goto cleanup; + } + } + + pciaddr->domain = pci[0]; + pciaddr->bus = pci[1]; + pciaddr->slot = pci[2]; + pciaddr->function = pci[3]; + + if (strcmp(driver, "ata_piix") == 0) { + /* a host per ide bus, target*:0:<unit>:0 */ + if (!has_host || !has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + for (i = 0; i < nhosts; i++) { + if (host == hosts[i]) { + disk->bus_type = GUEST_DISK_BUS_TYPE_IDE; + disk->bus = i; + disk->unit = tgt[1]; + break; + } + } + if (i >= nhosts) { + g_debug("no host for '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + } else if (strcmp(driver, "sym53c8xx") == 0) { + /* scsi(LSI Logic): target*:0:<unit>:0 */ + if (!has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->unit = tgt[1]; + } else if (strcmp(driver, "virtio-pci") == 0) { + if (has_tgt) { + /* virtio-scsi: target*:0:0:<unit> */ + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->unit = tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; + } + } else if (strcmp(driver, "ahci") == 0) { + /* ahci: 1 host per 1 unit */ + if (!has_host || !has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + for (i = 0; i < nhosts; i++) { + if (host == hosts[i]) { + disk->unit = i; + disk->bus_type = GUEST_DISK_BUS_TYPE_SATA; + break; + } + } + if (i >= nhosts) { + g_debug("no host for '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + } else if (strcmp(driver, "nvme") == 0) { + disk->bus_type = GUEST_DISK_BUS_TYPE_NVME; + } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) { + disk->bus_type = GUEST_DISK_BUS_TYPE_USB; + } else { + g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); + goto cleanup; + } + + ret = true; + +cleanup: + g_free(driver); + return ret; +} + +/* + * Store disk device info for non-PCI virtio devices (for example s390x + * channel I/O devices). Returns true if information has been stored, or + * false for failure. + */ +static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int tgt[3]; + char *p; + + if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { + g_debug("Unsupported virtio device '%s'", syspath); + return false; + } + + p = strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + &tgt[0], &tgt[1], &tgt[2]) == 3) { + /* virtio-scsi: target*:0:<target>:<unit> */ + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->bus = tgt[0]; + disk->target = tgt[1]; + disk->unit = tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; + } + + return true; +} + +/* + * Store disk device info for CCW devices (s390x channel I/O devices). + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int cssid, ssid, subchno, devno; + char *p; + + p = strstr(syspath, "/devices/css"); + if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/", + &cssid, &ssid, &subchno, &devno) < 4) { + g_debug("could not parse ccw device sysfs path: %s", syspath); + return false; + } + + disk->ccw_address = g_new0(GuestCCWAddress, 1); + disk->ccw_address->cssid = cssid; + disk->ccw_address->ssid = ssid; + disk->ccw_address->subchno = subchno; + disk->ccw_address->devno = devno; + + if (strstr(p, "/virtio")) { + build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); + } + + return true; +} + +/* Store disk device info specified by @sysfs into @fs */ +static void build_guest_fsinfo_for_real_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + GuestDiskAddress *disk; + GuestPCIAddress *pciaddr; + bool has_hwinf; +#ifdef CONFIG_LIBUDEV + struct udev *udev = NULL; + struct udev_device *udevice = NULL; +#endif + + pciaddr = g_new0(GuestPCIAddress, 1); + pciaddr->domain = -1; /* -1 means field is invalid */ + pciaddr->bus = -1; + pciaddr->slot = -1; + pciaddr->function = -1; + + disk = g_new0(GuestDiskAddress, 1); + disk->pci_controller = pciaddr; + disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN; + +#ifdef CONFIG_LIBUDEV + udev = udev_new(); + udevice = udev_device_new_from_syspath(udev, syspath); + if (udev == NULL || udevice == NULL) { + g_debug("failed to query udev"); + } else { + const char *devnode, *serial; + devnode = udev_device_get_devnode(udevice); + if (devnode != NULL) { + disk->dev = g_strdup(devnode); + } + serial = udev_device_get_property_value(udevice, "ID_SERIAL"); + if (serial != NULL && *serial != 0) { + disk->serial = g_strdup(serial); + } + } + + udev_unref(udev); + udev_device_unref(udevice); +#endif + + if (strstr(syspath, "/devices/pci")) { + has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp); + } else if (strstr(syspath, "/devices/css")) { + has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp); + } else if (strstr(syspath, "/virtio")) { + has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); + } else { + g_debug("Unsupported device type for '%s'", syspath); + has_hwinf = false; + } + + if (has_hwinf || disk->dev || disk->serial) { + QAPI_LIST_PREPEND(fs->disk, disk); + } else { + qapi_free_GuestDiskAddress(disk); + } +} + +static void build_guest_fsinfo_for_device(char const *devpath, + GuestFilesystemInfo *fs, + Error **errp); + +/* Store a list of slave devices of virtual volume specified by @syspath into + * @fs */ +static void build_guest_fsinfo_for_virtual_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + Error *err = NULL; + DIR *dir; + char *dirpath; + struct dirent *entry; + + dirpath = g_strdup_printf("%s/slaves", syspath); + dir = opendir(dirpath); + if (!dir) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath); + } + g_free(dirpath); + return; + } + + for (;;) { + errno = 0; + entry = readdir(dir); + if (entry == NULL) { + if (errno) { + error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath); + } + break; + } + + if (entry->d_type == DT_LNK) { + char *path; + + g_debug(" slave device '%s'", entry->d_name); + path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name); + build_guest_fsinfo_for_device(path, fs, &err); + g_free(path); + + if (err) { + error_propagate(errp, err); + break; + } + } + } + + g_free(dirpath); + closedir(dir); +} + +static bool is_disk_virtual(const char *devpath, Error **errp) +{ + g_autofree char *syspath = realpath(devpath, NULL); + + if (!syspath) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return false; + } + return strstr(syspath, "/devices/virtual/block/") != NULL; +} + +/* Dispatch to functions for virtual/real device */ +static void build_guest_fsinfo_for_device(char const *devpath, + GuestFilesystemInfo *fs, + Error **errp) +{ + ERRP_GUARD(); + g_autofree char *syspath = NULL; + bool is_virtual = false; + + syspath = realpath(devpath, NULL); + if (!syspath) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return; + } + + /* ENOENT: This devpath may not exist because of container config */ + if (!fs->name) { + fs->name = g_path_get_basename(devpath); + } + return; + } + + if (!fs->name) { + fs->name = g_path_get_basename(syspath); + } + + g_debug(" parse sysfs path '%s'", syspath); + is_virtual = is_disk_virtual(syspath, errp); + if (*errp != NULL) { + return; + } + if (is_virtual) { + build_guest_fsinfo_for_virtual_device(syspath, fs, errp); + } else { + build_guest_fsinfo_for_real_device(syspath, fs, errp); + } +} + +#ifdef CONFIG_LIBUDEV + +/* + * Wrapper around build_guest_fsinfo_for_device() for getting just + * the disk address. + */ +static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp) +{ + g_autoptr(GuestFilesystemInfo) fs = NULL; + + fs = g_new0(GuestFilesystemInfo, 1); + build_guest_fsinfo_for_device(syspath, fs, errp); + if (fs->disk != NULL) { + return g_steal_pointer(&fs->disk->value); + } + return NULL; +} + +static char *get_alias_for_syspath(const char *syspath) +{ + struct udev *udev = NULL; + struct udev_device *udevice = NULL; + char *ret = NULL; + + udev = udev_new(); + if (udev == NULL) { + g_debug("failed to query udev"); + goto out; + } + udevice = udev_device_new_from_syspath(udev, syspath); + if (udevice == NULL) { + g_debug("failed to query udev for path: %s", syspath); + goto out; + } else { + const char *alias = udev_device_get_property_value( + udevice, "DM_NAME"); + /* + * NULL means there was an error and empty string means there is no + * alias. In case of no alias we return NULL instead of empty string. + */ + if (alias == NULL) { + g_debug("failed to query udev for device alias for: %s", + syspath); + } else if (*alias != 0) { + ret = g_strdup(alias); + } + } + +out: + udev_unref(udev); + udev_device_unref(udevice); + return ret; +} + +static char *get_device_for_syspath(const char *syspath) +{ + struct udev *udev = NULL; + struct udev_device *udevice = NULL; + char *ret = NULL; + + udev = udev_new(); + if (udev == NULL) { + g_debug("failed to query udev"); + goto out; + } + udevice = udev_device_new_from_syspath(udev, syspath); + if (udevice == NULL) { + g_debug("failed to query udev for path: %s", syspath); + goto out; + } else { + ret = g_strdup(udev_device_get_devnode(udevice)); + } + +out: + udev_unref(udev); + udev_device_unref(udevice); + return ret; +} + +static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk) +{ + g_autofree char *deps_dir = NULL; + const gchar *dep; + GDir *dp_deps = NULL; + + /* List dependent disks */ + deps_dir = g_strdup_printf("%s/slaves", disk_dir); + g_debug(" listing entries in: %s", deps_dir); + dp_deps = g_dir_open(deps_dir, 0, NULL); + if (dp_deps == NULL) { + g_debug("failed to list entries in %s", deps_dir); + return; + } + disk->has_dependencies = true; + while ((dep = g_dir_read_name(dp_deps)) != NULL) { + g_autofree char *dep_dir = NULL; + char *dev_name; + + /* Add dependent disks */ + dep_dir = g_strdup_printf("%s/%s", deps_dir, dep); + dev_name = get_device_for_syspath(dep_dir); + if (dev_name != NULL) { + g_debug(" adding dependent device: %s", dev_name); + QAPI_LIST_PREPEND(disk->dependencies, dev_name); + } + } + g_dir_close(dp_deps); +} + +/* + * Detect partitions subdirectory, name is "<disk_name><number>" or + * "<disk_name>p<number>" + * + * @disk_name -- last component of /sys path (e.g. sda) + * @disk_dir -- sys path of the disk (e.g. /sys/block/sda) + * @disk_dev -- device node of the disk (e.g. /dev/sda) + */ +static GuestDiskInfoList *get_disk_partitions( + GuestDiskInfoList *list, + const char *disk_name, const char *disk_dir, + const char *disk_dev) +{ + GuestDiskInfoList *ret = list; + struct dirent *de_disk; + DIR *dp_disk = NULL; + size_t len = strlen(disk_name); + + dp_disk = opendir(disk_dir); + while ((de_disk = readdir(dp_disk)) != NULL) { + g_autofree char *partition_dir = NULL; + char *dev_name; + GuestDiskInfo *partition; + + if (!(de_disk->d_type & DT_DIR)) { + continue; + } + + if (!(strncmp(disk_name, de_disk->d_name, len) == 0 && + ((*(de_disk->d_name + len) == 'p' && + isdigit(*(de_disk->d_name + len + 1))) || + isdigit(*(de_disk->d_name + len))))) { + continue; + } + + partition_dir = g_strdup_printf("%s/%s", + disk_dir, de_disk->d_name); + dev_name = get_device_for_syspath(partition_dir); + if (dev_name == NULL) { + g_debug("Failed to get device name for syspath: %s", + disk_dir); + continue; + } + partition = g_new0(GuestDiskInfo, 1); + partition->name = dev_name; + partition->partition = true; + partition->has_dependencies = true; + /* Add parent disk as dependent for easier tracking of hierarchy */ + QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev)); + + QAPI_LIST_PREPEND(ret, partition); + } + closedir(dp_disk); + + return ret; +} + +static void get_nvme_smart(GuestDiskInfo *disk) +{ + int fd; + GuestNVMeSmart *smart; + NvmeSmartLog log = {0}; + struct nvme_admin_cmd cmd = { + .opcode = NVME_ADM_CMD_GET_LOG_PAGE, + .nsid = NVME_NSID_BROADCAST, + .addr = (uintptr_t)&log, + .data_len = sizeof(log), + .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ + | (((sizeof(log) >> 2) - 1) << 16) + }; + + fd = qga_open_cloexec(disk->name, O_RDONLY, 0); + if (fd == -1) { + g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno)); + return; + } + + if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { + g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno)); + close(fd); + return; + } + + disk->smart = g_new0(GuestDiskSmart, 1); + disk->smart->type = GUEST_DISK_BUS_TYPE_NVME; + + smart = &disk->smart->u.nvme; + smart->critical_warning = log.critical_warning; + smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */ + smart->available_spare = log.available_spare; + smart->available_spare_threshold = log.available_spare_threshold; + smart->percentage_used = log.percentage_used; + smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]); + smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]); + smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]); + smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]); + smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]); + smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]); + smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]); + smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]); + smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]); + smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]); + smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]); + smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]); + smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]); + smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]); + smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]); + smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]); + smart->media_errors_lo = le64_to_cpu(log.media_errors[0]); + smart->media_errors_hi = le64_to_cpu(log.media_errors[1]); + smart->number_of_error_log_entries_lo = + le64_to_cpu(log.number_of_error_log_entries[0]); + smart->number_of_error_log_entries_hi = + le64_to_cpu(log.number_of_error_log_entries[1]); + + close(fd); +} + +static void get_disk_smart(GuestDiskInfo *disk) +{ + if (disk->address + && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) { + get_nvme_smart(disk); + } +} + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + GuestDiskInfoList *ret = NULL; + GuestDiskInfo *disk; + DIR *dp = NULL; + struct dirent *de = NULL; + + g_debug("listing /sys/block directory"); + dp = opendir("/sys/block"); + if (dp == NULL) { + error_setg_errno(errp, errno, "Can't open directory \"/sys/block\""); + return NULL; + } + while ((de = readdir(dp)) != NULL) { + g_autofree char *disk_dir = NULL, *line = NULL, + *size_path = NULL; + char *dev_name; + Error *local_err = NULL; + if (de->d_type != DT_LNK) { + g_debug(" skipping entry: %s", de->d_name); + continue; + } + + /* Check size and skip zero-sized disks */ + g_debug(" checking disk size"); + size_path = g_strdup_printf("/sys/block/%s/size", de->d_name); + if (!g_file_get_contents(size_path, &line, NULL, NULL)) { + g_debug(" failed to read disk size"); + continue; + } + if (g_strcmp0(line, "0\n") == 0) { + g_debug(" skipping zero-sized disk"); + continue; + } + + g_debug(" adding %s", de->d_name); + disk_dir = g_strdup_printf("/sys/block/%s", de->d_name); + dev_name = get_device_for_syspath(disk_dir); + if (dev_name == NULL) { + g_debug("Failed to get device name for syspath: %s", + disk_dir); + continue; + } + disk = g_new0(GuestDiskInfo, 1); + disk->name = dev_name; + disk->partition = false; + disk->alias = get_alias_for_syspath(disk_dir); + QAPI_LIST_PREPEND(ret, disk); + + /* Get address for non-virtual devices */ + bool is_virtual = is_disk_virtual(disk_dir, &local_err); + if (local_err != NULL) { + g_debug(" failed to check disk path, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + /* Don't try to get the address */ + is_virtual = true; + } + if (!is_virtual) { + disk->address = get_disk_address(disk_dir, &local_err); + if (local_err != NULL) { + g_debug(" failed to get device info, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + } + } + + get_disk_deps(disk_dir, disk); + get_disk_smart(disk); + ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name); + } + + closedir(dp); + + return ret; +} + +#else + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +#endif + +/* Return a list of the disk device(s)' info which @mount lies on */ +static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, + Error **errp) +{ + GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); + struct statvfs buf; + unsigned long used, nonroot_total, fr_size; + char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", + mount->devmajor, mount->devminor); + + fs->mountpoint = g_strdup(mount->dirname); + fs->type = g_strdup(mount->devtype); + build_guest_fsinfo_for_device(devpath, fs, errp); + + if (statvfs(fs->mountpoint, &buf) == 0) { + fr_size = buf.f_frsize; + used = buf.f_blocks - buf.f_bfree; + nonroot_total = used + buf.f_bavail; + fs->used_bytes = used * fr_size; + fs->total_bytes = nonroot_total * fr_size; + fs->total_bytes_privileged = buf.f_blocks * fr_size; + + fs->has_total_bytes = true; + fs->has_total_bytes_privileged = true; + fs->has_used_bytes = true; + } + + g_free(devpath); + + return fs; +} + +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) +{ + FsMountList mounts; + struct FsMount *mount; + GuestFilesystemInfoList *ret = NULL; + Error *local_err = NULL; + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return NULL; + } + + QTAILQ_FOREACH(mount, &mounts, next) { + g_debug("Building guest fsinfo for '%s'", mount->dirname); + + QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err)); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_GuestFilesystemInfoList(ret); + ret = NULL; + break; + } + } + + free_fs_mount_list(&mounts); + return ret; +} +#endif /* CONFIG_FSFREEZE */ + +#if defined(CONFIG_FSTRIM) +/* + * Walk list of mounted file systems in the guest, and trim them. + */ +GuestFilesystemTrimResponse * +qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) +{ + GuestFilesystemTrimResponse *response; + GuestFilesystemTrimResult *result; + int ret = 0; + FsMountList mounts; + struct FsMount *mount; + int fd; + struct fstrim_range r; + + slog("guest-fstrim called"); + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, errp)) { + return NULL; + } + + response = g_malloc0(sizeof(*response)); + + QTAILQ_FOREACH(mount, &mounts, next) { + result = g_malloc0(sizeof(*result)); + result->path = g_strdup(mount->dirname); + + QAPI_LIST_PREPEND(response->paths, result); + + fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd == -1) { + result->error = g_strdup_printf("failed to open: %s", + strerror(errno)); + continue; + } + + /* We try to cull filesystems we know won't work in advance, but other + * filesystems may not implement fstrim for less obvious reasons. + * These will report EOPNOTSUPP; while in some other cases ENOTTY + * will be reported (e.g. CD-ROMs). + * Any other error means an unexpected error. + */ + r.start = 0; + r.len = -1; + r.minlen = has_minimum ? minimum : 0; + ret = ioctl(fd, FITRIM, &r); + if (ret == -1) { + if (errno == ENOTTY || errno == EOPNOTSUPP) { + result->error = g_strdup("trim not supported"); + } else { + result->error = g_strdup_printf("failed to trim: %s", + strerror(errno)); + } + close(fd); + continue; + } + + result->has_minimum = true; + result->minimum = r.minlen; + result->has_trimmed = true; + result->trimmed = r.len; + close(fd); + } + + free_fs_mount_list(&mounts); + return response; +} +#endif /* CONFIG_FSTRIM */ #define LINUX_SYS_STATE_FILE "/sys/power/state" #define SUSPEND_SUPPORTED 0 diff --git a/qga/commands-posix.c b/qga/commands-posix.c index ef21da63be..98aafc45f3 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -24,23 +24,12 @@ #include "qemu/base64.h" #include "qemu/cutils.h" #include "commands-common.h" -#include "block/nvme.h" #include "cutils.h" #ifdef HAVE_UTMPX #include <utmpx.h> #endif -#if defined(__linux__) -#include <mntent.h> -#include <sys/statvfs.h> -#include <linux/nvme_ioctl.h> - -#ifdef CONFIG_LIBUDEV -#include <libudev.h> -#endif -#endif - #ifdef HAVE_GETIFADDRS #include <arpa/inet.h> #include <sys/socket.h> @@ -842,904 +831,6 @@ static void guest_fsfreeze_cleanup(void) } #endif -/* linux-specific implementations. avoid this if at all possible. */ -#if defined(__linux__) -#if defined(CONFIG_FSFREEZE) - -static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) -{ - char *path; - char *dpath; - char *driver = NULL; - char buf[PATH_MAX]; - ssize_t len; - - path = g_strndup(syspath, pathlen); - dpath = g_strdup_printf("%s/driver", path); - len = readlink(dpath, buf, sizeof(buf) - 1); - if (len != -1) { - buf[len] = 0; - driver = g_path_get_basename(buf); - } - g_free(dpath); - g_free(path); - return driver; -} - -static int compare_uint(const void *_a, const void *_b) -{ - unsigned int a = *(unsigned int *)_a; - unsigned int b = *(unsigned int *)_b; - - return a < b ? -1 : a > b ? 1 : 0; -} - -/* Walk the specified sysfs and build a sorted list of host or ata numbers */ -static int build_hosts(char const *syspath, char const *host, bool ata, - unsigned int *hosts, int hosts_max, Error **errp) -{ - char *path; - DIR *dir; - struct dirent *entry; - int i = 0; - - path = g_strndup(syspath, host - syspath); - dir = opendir(path); - if (!dir) { - error_setg_errno(errp, errno, "opendir(\"%s\")", path); - g_free(path); - return -1; - } - - while (i < hosts_max) { - entry = readdir(dir); - if (!entry) { - break; - } - if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) { - ++i; - } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) { - ++i; - } - } - - qsort(hosts, i, sizeof(hosts[0]), compare_uint); - - g_free(path); - closedir(dir); - return i; -} - -/* - * Store disk device info for devices on the PCI bus. - * Returns true if information has been stored, or false for failure. - */ -static bool build_guest_fsinfo_for_pci_dev(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int pci[4], host, hosts[8], tgt[3]; - int i, nhosts = 0, pcilen; - GuestPCIAddress *pciaddr = disk->pci_controller; - bool has_ata = false, has_host = false, has_tgt = false; - char *p, *q, *driver = NULL; - bool ret = false; - - p = strstr(syspath, "/devices/pci"); - if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", - pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { - g_debug("only pci device is supported: sysfs path '%s'", syspath); - return false; - } - - p += 12 + pcilen; - while (true) { - driver = get_pci_driver(syspath, p - syspath, errp); - if (driver && (g_str_equal(driver, "ata_piix") || - g_str_equal(driver, "sym53c8xx") || - g_str_equal(driver, "virtio-pci") || - g_str_equal(driver, "ahci") || - g_str_equal(driver, "nvme") || - g_str_equal(driver, "xhci_hcd") || - g_str_equal(driver, "ehci-pci"))) { - break; - } - - g_free(driver); - if (sscanf(p, "/%x:%x:%x.%x%n", - pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { - p += pcilen; - continue; - } - - g_debug("unsupported driver or sysfs path '%s'", syspath); - return false; - } - - p = strstr(syspath, "/target"); - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", - tgt, tgt + 1, tgt + 2) == 3) { - has_tgt = true; - } - - p = strstr(syspath, "/ata"); - if (p) { - q = p + 4; - has_ata = true; - } else { - p = strstr(syspath, "/host"); - q = p + 5; - } - if (p && sscanf(q, "%u", &host) == 1) { - has_host = true; - nhosts = build_hosts(syspath, p, has_ata, hosts, - ARRAY_SIZE(hosts), errp); - if (nhosts < 0) { - goto cleanup; - } - } - - pciaddr->domain = pci[0]; - pciaddr->bus = pci[1]; - pciaddr->slot = pci[2]; - pciaddr->function = pci[3]; - - if (strcmp(driver, "ata_piix") == 0) { - /* a host per ide bus, target*:0:<unit>:0 */ - if (!has_host || !has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - for (i = 0; i < nhosts; i++) { - if (host == hosts[i]) { - disk->bus_type = GUEST_DISK_BUS_TYPE_IDE; - disk->bus = i; - disk->unit = tgt[1]; - break; - } - } - if (i >= nhosts) { - g_debug("no host for '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - } else if (strcmp(driver, "sym53c8xx") == 0) { - /* scsi(LSI Logic): target*:0:<unit>:0 */ - if (!has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; - disk->unit = tgt[1]; - } else if (strcmp(driver, "virtio-pci") == 0) { - if (has_tgt) { - /* virtio-scsi: target*:0:0:<unit> */ - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; - disk->unit = tgt[2]; - } else { - /* virtio-blk: 1 disk per 1 device */ - disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; - } - } else if (strcmp(driver, "ahci") == 0) { - /* ahci: 1 host per 1 unit */ - if (!has_host || !has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - for (i = 0; i < nhosts; i++) { - if (host == hosts[i]) { - disk->unit = i; - disk->bus_type = GUEST_DISK_BUS_TYPE_SATA; - break; - } - } - if (i >= nhosts) { - g_debug("no host for '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - } else if (strcmp(driver, "nvme") == 0) { - disk->bus_type = GUEST_DISK_BUS_TYPE_NVME; - } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) { - disk->bus_type = GUEST_DISK_BUS_TYPE_USB; - } else { - g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); - goto cleanup; - } - - ret = true; - -cleanup: - g_free(driver); - return ret; -} - -/* - * Store disk device info for non-PCI virtio devices (for example s390x - * channel I/O devices). Returns true if information has been stored, or - * false for failure. - */ -static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int tgt[3]; - char *p; - - if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { - g_debug("Unsupported virtio device '%s'", syspath); - return false; - } - - p = strstr(syspath, "/target"); - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", - &tgt[0], &tgt[1], &tgt[2]) == 3) { - /* virtio-scsi: target*:0:<target>:<unit> */ - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; - disk->bus = tgt[0]; - disk->target = tgt[1]; - disk->unit = tgt[2]; - } else { - /* virtio-blk: 1 disk per 1 device */ - disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; - } - - return true; -} - -/* - * Store disk device info for CCW devices (s390x channel I/O devices). - * Returns true if information has been stored, or false for failure. - */ -static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int cssid, ssid, subchno, devno; - char *p; - - p = strstr(syspath, "/devices/css"); - if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/", - &cssid, &ssid, &subchno, &devno) < 4) { - g_debug("could not parse ccw device sysfs path: %s", syspath); - return false; - } - - disk->ccw_address = g_new0(GuestCCWAddress, 1); - disk->ccw_address->cssid = cssid; - disk->ccw_address->ssid = ssid; - disk->ccw_address->subchno = subchno; - disk->ccw_address->devno = devno; - - if (strstr(p, "/virtio")) { - build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); - } - - return true; -} - -/* Store disk device info specified by @sysfs into @fs */ -static void build_guest_fsinfo_for_real_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) -{ - GuestDiskAddress *disk; - GuestPCIAddress *pciaddr; - bool has_hwinf; -#ifdef CONFIG_LIBUDEV - struct udev *udev = NULL; - struct udev_device *udevice = NULL; -#endif - - pciaddr = g_new0(GuestPCIAddress, 1); - pciaddr->domain = -1; /* -1 means field is invalid */ - pciaddr->bus = -1; - pciaddr->slot = -1; - pciaddr->function = -1; - - disk = g_new0(GuestDiskAddress, 1); - disk->pci_controller = pciaddr; - disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN; - -#ifdef CONFIG_LIBUDEV - udev = udev_new(); - udevice = udev_device_new_from_syspath(udev, syspath); - if (udev == NULL || udevice == NULL) { - g_debug("failed to query udev"); - } else { - const char *devnode, *serial; - devnode = udev_device_get_devnode(udevice); - if (devnode != NULL) { - disk->dev = g_strdup(devnode); - } - serial = udev_device_get_property_value(udevice, "ID_SERIAL"); - if (serial != NULL && *serial != 0) { - disk->serial = g_strdup(serial); - } - } - - udev_unref(udev); - udev_device_unref(udevice); -#endif - - if (strstr(syspath, "/devices/pci")) { - has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp); - } else if (strstr(syspath, "/devices/css")) { - has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp); - } else if (strstr(syspath, "/virtio")) { - has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); - } else { - g_debug("Unsupported device type for '%s'", syspath); - has_hwinf = false; - } - - if (has_hwinf || disk->dev || disk->serial) { - QAPI_LIST_PREPEND(fs->disk, disk); - } else { - qapi_free_GuestDiskAddress(disk); - } -} - -static void build_guest_fsinfo_for_device(char const *devpath, - GuestFilesystemInfo *fs, - Error **errp); - -/* Store a list of slave devices of virtual volume specified by @syspath into - * @fs */ -static void build_guest_fsinfo_for_virtual_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) -{ - Error *err = NULL; - DIR *dir; - char *dirpath; - struct dirent *entry; - - dirpath = g_strdup_printf("%s/slaves", syspath); - dir = opendir(dirpath); - if (!dir) { - if (errno != ENOENT) { - error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath); - } - g_free(dirpath); - return; - } - - for (;;) { - errno = 0; - entry = readdir(dir); - if (entry == NULL) { - if (errno) { - error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath); - } - break; - } - - if (entry->d_type == DT_LNK) { - char *path; - - g_debug(" slave device '%s'", entry->d_name); - path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name); - build_guest_fsinfo_for_device(path, fs, &err); - g_free(path); - - if (err) { - error_propagate(errp, err); - break; - } - } - } - - g_free(dirpath); - closedir(dir); -} - -static bool is_disk_virtual(const char *devpath, Error **errp) -{ - g_autofree char *syspath = realpath(devpath, NULL); - - if (!syspath) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); - return false; - } - return strstr(syspath, "/devices/virtual/block/") != NULL; -} - -/* Dispatch to functions for virtual/real device */ -static void build_guest_fsinfo_for_device(char const *devpath, - GuestFilesystemInfo *fs, - Error **errp) -{ - ERRP_GUARD(); - g_autofree char *syspath = NULL; - bool is_virtual = false; - - syspath = realpath(devpath, NULL); - if (!syspath) { - if (errno != ENOENT) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); - return; - } - - /* ENOENT: This devpath may not exist because of container config */ - if (!fs->name) { - fs->name = g_path_get_basename(devpath); - } - return; - } - - if (!fs->name) { - fs->name = g_path_get_basename(syspath); - } - - g_debug(" parse sysfs path '%s'", syspath); - is_virtual = is_disk_virtual(syspath, errp); - if (*errp != NULL) { - return; - } - if (is_virtual) { - build_guest_fsinfo_for_virtual_device(syspath, fs, errp); - } else { - build_guest_fsinfo_for_real_device(syspath, fs, errp); - } -} - -#ifdef CONFIG_LIBUDEV - -/* - * Wrapper around build_guest_fsinfo_for_device() for getting just - * the disk address. - */ -static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp) -{ - g_autoptr(GuestFilesystemInfo) fs = NULL; - - fs = g_new0(GuestFilesystemInfo, 1); - build_guest_fsinfo_for_device(syspath, fs, errp); - if (fs->disk != NULL) { - return g_steal_pointer(&fs->disk->value); - } - return NULL; -} - -static char *get_alias_for_syspath(const char *syspath) -{ - struct udev *udev = NULL; - struct udev_device *udevice = NULL; - char *ret = NULL; - - udev = udev_new(); - if (udev == NULL) { - g_debug("failed to query udev"); - goto out; - } - udevice = udev_device_new_from_syspath(udev, syspath); - if (udevice == NULL) { - g_debug("failed to query udev for path: %s", syspath); - goto out; - } else { - const char *alias = udev_device_get_property_value( - udevice, "DM_NAME"); - /* - * NULL means there was an error and empty string means there is no - * alias. In case of no alias we return NULL instead of empty string. - */ - if (alias == NULL) { - g_debug("failed to query udev for device alias for: %s", - syspath); - } else if (*alias != 0) { - ret = g_strdup(alias); - } - } - -out: - udev_unref(udev); - udev_device_unref(udevice); - return ret; -} - -static char *get_device_for_syspath(const char *syspath) -{ - struct udev *udev = NULL; - struct udev_device *udevice = NULL; - char *ret = NULL; - - udev = udev_new(); - if (udev == NULL) { - g_debug("failed to query udev"); - goto out; - } - udevice = udev_device_new_from_syspath(udev, syspath); - if (udevice == NULL) { - g_debug("failed to query udev for path: %s", syspath); - goto out; - } else { - ret = g_strdup(udev_device_get_devnode(udevice)); - } - -out: - udev_unref(udev); - udev_device_unref(udevice); - return ret; -} - -static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk) -{ - g_autofree char *deps_dir = NULL; - const gchar *dep; - GDir *dp_deps = NULL; - - /* List dependent disks */ - deps_dir = g_strdup_printf("%s/slaves", disk_dir); - g_debug(" listing entries in: %s", deps_dir); - dp_deps = g_dir_open(deps_dir, 0, NULL); - if (dp_deps == NULL) { - g_debug("failed to list entries in %s", deps_dir); - return; - } - disk->has_dependencies = true; - while ((dep = g_dir_read_name(dp_deps)) != NULL) { - g_autofree char *dep_dir = NULL; - char *dev_name; - - /* Add dependent disks */ - dep_dir = g_strdup_printf("%s/%s", deps_dir, dep); - dev_name = get_device_for_syspath(dep_dir); - if (dev_name != NULL) { - g_debug(" adding dependent device: %s", dev_name); - QAPI_LIST_PREPEND(disk->dependencies, dev_name); - } - } - g_dir_close(dp_deps); -} - -/* - * Detect partitions subdirectory, name is "<disk_name><number>" or - * "<disk_name>p<number>" - * - * @disk_name -- last component of /sys path (e.g. sda) - * @disk_dir -- sys path of the disk (e.g. /sys/block/sda) - * @disk_dev -- device node of the disk (e.g. /dev/sda) - */ -static GuestDiskInfoList *get_disk_partitions( - GuestDiskInfoList *list, - const char *disk_name, const char *disk_dir, - const char *disk_dev) -{ - GuestDiskInfoList *ret = list; - struct dirent *de_disk; - DIR *dp_disk = NULL; - size_t len = strlen(disk_name); - - dp_disk = opendir(disk_dir); - while ((de_disk = readdir(dp_disk)) != NULL) { - g_autofree char *partition_dir = NULL; - char *dev_name; - GuestDiskInfo *partition; - - if (!(de_disk->d_type & DT_DIR)) { - continue; - } - - if (!(strncmp(disk_name, de_disk->d_name, len) == 0 && - ((*(de_disk->d_name + len) == 'p' && - isdigit(*(de_disk->d_name + len + 1))) || - isdigit(*(de_disk->d_name + len))))) { - continue; - } - - partition_dir = g_strdup_printf("%s/%s", - disk_dir, de_disk->d_name); - dev_name = get_device_for_syspath(partition_dir); - if (dev_name == NULL) { - g_debug("Failed to get device name for syspath: %s", - disk_dir); - continue; - } - partition = g_new0(GuestDiskInfo, 1); - partition->name = dev_name; - partition->partition = true; - partition->has_dependencies = true; - /* Add parent disk as dependent for easier tracking of hierarchy */ - QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev)); - - QAPI_LIST_PREPEND(ret, partition); - } - closedir(dp_disk); - - return ret; -} - -static void get_nvme_smart(GuestDiskInfo *disk) -{ - int fd; - GuestNVMeSmart *smart; - NvmeSmartLog log = {0}; - struct nvme_admin_cmd cmd = { - .opcode = NVME_ADM_CMD_GET_LOG_PAGE, - .nsid = NVME_NSID_BROADCAST, - .addr = (uintptr_t)&log, - .data_len = sizeof(log), - .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ - | (((sizeof(log) >> 2) - 1) << 16) - }; - - fd = qga_open_cloexec(disk->name, O_RDONLY, 0); - if (fd == -1) { - g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno)); - return; - } - - if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { - g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno)); - close(fd); - return; - } - - disk->smart = g_new0(GuestDiskSmart, 1); - disk->smart->type = GUEST_DISK_BUS_TYPE_NVME; - - smart = &disk->smart->u.nvme; - smart->critical_warning = log.critical_warning; - smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */ - smart->available_spare = log.available_spare; - smart->available_spare_threshold = log.available_spare_threshold; - smart->percentage_used = log.percentage_used; - smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]); - smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]); - smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]); - smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]); - smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]); - smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]); - smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]); - smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]); - smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]); - smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]); - smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]); - smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]); - smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]); - smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]); - smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]); - smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]); - smart->media_errors_lo = le64_to_cpu(log.media_errors[0]); - smart->media_errors_hi = le64_to_cpu(log.media_errors[1]); - smart->number_of_error_log_entries_lo = - le64_to_cpu(log.number_of_error_log_entries[0]); - smart->number_of_error_log_entries_hi = - le64_to_cpu(log.number_of_error_log_entries[1]); - - close(fd); -} - -static void get_disk_smart(GuestDiskInfo *disk) -{ - if (disk->address - && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) { - get_nvme_smart(disk); - } -} - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - GuestDiskInfoList *ret = NULL; - GuestDiskInfo *disk; - DIR *dp = NULL; - struct dirent *de = NULL; - - g_debug("listing /sys/block directory"); - dp = opendir("/sys/block"); - if (dp == NULL) { - error_setg_errno(errp, errno, "Can't open directory \"/sys/block\""); - return NULL; - } - while ((de = readdir(dp)) != NULL) { - g_autofree char *disk_dir = NULL, *line = NULL, - *size_path = NULL; - char *dev_name; - Error *local_err = NULL; - if (de->d_type != DT_LNK) { - g_debug(" skipping entry: %s", de->d_name); - continue; - } - - /* Check size and skip zero-sized disks */ - g_debug(" checking disk size"); - size_path = g_strdup_printf("/sys/block/%s/size", de->d_name); - if (!g_file_get_contents(size_path, &line, NULL, NULL)) { - g_debug(" failed to read disk size"); - continue; - } - if (g_strcmp0(line, "0\n") == 0) { - g_debug(" skipping zero-sized disk"); - continue; - } - - g_debug(" adding %s", de->d_name); - disk_dir = g_strdup_printf("/sys/block/%s", de->d_name); - dev_name = get_device_for_syspath(disk_dir); - if (dev_name == NULL) { - g_debug("Failed to get device name for syspath: %s", - disk_dir); - continue; - } - disk = g_new0(GuestDiskInfo, 1); - disk->name = dev_name; - disk->partition = false; - disk->alias = get_alias_for_syspath(disk_dir); - QAPI_LIST_PREPEND(ret, disk); - - /* Get address for non-virtual devices */ - bool is_virtual = is_disk_virtual(disk_dir, &local_err); - if (local_err != NULL) { - g_debug(" failed to check disk path, ignoring error: %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - /* Don't try to get the address */ - is_virtual = true; - } - if (!is_virtual) { - disk->address = get_disk_address(disk_dir, &local_err); - if (local_err != NULL) { - g_debug(" failed to get device info, ignoring error: %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - } - } - - get_disk_deps(disk_dir, disk); - get_disk_smart(disk); - ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name); - } - - closedir(dp); - - return ret; -} - -#else - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif - -/* Return a list of the disk device(s)' info which @mount lies on */ -static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, - Error **errp) -{ - GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); - struct statvfs buf; - unsigned long used, nonroot_total, fr_size; - char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", - mount->devmajor, mount->devminor); - - fs->mountpoint = g_strdup(mount->dirname); - fs->type = g_strdup(mount->devtype); - build_guest_fsinfo_for_device(devpath, fs, errp); - - if (statvfs(fs->mountpoint, &buf) == 0) { - fr_size = buf.f_frsize; - used = buf.f_blocks - buf.f_bfree; - nonroot_total = used + buf.f_bavail; - fs->used_bytes = used * fr_size; - fs->total_bytes = nonroot_total * fr_size; - fs->total_bytes_privileged = buf.f_blocks * fr_size; - - fs->has_total_bytes = true; - fs->has_total_bytes_privileged = true; - fs->has_used_bytes = true; - } - - g_free(devpath); - - return fs; -} - -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) -{ - FsMountList mounts; - struct FsMount *mount; - GuestFilesystemInfoList *ret = NULL; - Error *local_err = NULL; - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return NULL; - } - - QTAILQ_FOREACH(mount, &mounts, next) { - g_debug("Building guest fsinfo for '%s'", mount->dirname); - - QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err)); - if (local_err) { - error_propagate(errp, local_err); - qapi_free_GuestFilesystemInfoList(ret); - ret = NULL; - break; - } - } - - free_fs_mount_list(&mounts); - return ret; -} -#endif /* CONFIG_FSFREEZE */ - -#if defined(CONFIG_FSTRIM) -/* - * Walk list of mounted file systems in the guest, and trim them. - */ -GuestFilesystemTrimResponse * -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) -{ - GuestFilesystemTrimResponse *response; - GuestFilesystemTrimResult *result; - int ret = 0; - FsMountList mounts; - struct FsMount *mount; - int fd; - struct fstrim_range r; - - slog("guest-fstrim called"); - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, errp)) { - return NULL; - } - - response = g_malloc0(sizeof(*response)); - - QTAILQ_FOREACH(mount, &mounts, next) { - result = g_malloc0(sizeof(*result)); - result->path = g_strdup(mount->dirname); - - QAPI_LIST_PREPEND(response->paths, result); - - fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd == -1) { - result->error = g_strdup_printf("failed to open: %s", - strerror(errno)); - continue; - } - - /* We try to cull filesystems we know won't work in advance, but other - * filesystems may not implement fstrim for less obvious reasons. - * These will report EOPNOTSUPP; while in some other cases ENOTTY - * will be reported (e.g. CD-ROMs). - * Any other error means an unexpected error. - */ - r.start = 0; - r.len = -1; - r.minlen = has_minimum ? minimum : 0; - ret = ioctl(fd, FITRIM, &r); - if (ret == -1) { - if (errno == ENOTTY || errno == EOPNOTSUPP) { - result->error = g_strdup("trim not supported"); - } else { - result->error = g_strdup_printf("failed to trim: %s", - strerror(errno)); - } - close(fd); - continue; - } - - result->has_minimum = true; - result->minimum = r.minlen; - result->has_trimmed = true; - result->trimmed = r.len; - close(fd); - } - - free_fs_mount_list(&mounts); - return response; -} -#endif /* CONFIG_FSTRIM */ - -#endif /* __linux__ */ - #if defined(__linux__) || defined(__FreeBSD__) void qmp_guest_set_user_password(const char *username, const char *password, From 74cbd9bcefb1781648cf593597018a05199d8ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:42 +0100 Subject: [PATCH 2248/2884] qga: move linux disk/cpu stats command impls to commands-linux.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qmp_guest_{diskstats,cpustats} command impls in commands-posix.c are surrounded by '#ifdef __linux__' so should instead live in commands-linux.c This also removes a "#ifdef CONFIG_LINUX" that was nested inside a "#ifdef __linux__". Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-6-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 195 ++++++++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 199 ------------------------------------------- 2 files changed, 195 insertions(+), 199 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 084e6c9e85..c0e8bd4062 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -1594,3 +1594,198 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return processed; } + +#define MAX_NAME_LEN 128 +static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) +{ + GuestDiskStatsInfoList *head = NULL, **tail = &head; + const char *diskstats = "/proc/diskstats"; + FILE *fp; + size_t n; + char *line = NULL; + + fp = fopen(diskstats, "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", diskstats); + return NULL; + } + + while (getline(&line, &n, fp) != -1) { + g_autofree GuestDiskStatsInfo *diskstatinfo = NULL; + g_autofree GuestDiskStats *diskstat = NULL; + char dev_name[MAX_NAME_LEN]; + unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks; + unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios; + unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; + unsigned long dc_ios, dc_merges, dc_sec, fl_ios; + unsigned int major, minor; + int i; + + i = sscanf(line, "%u %u %s %lu %lu %lu" + "%lu %lu %lu %lu %u %u %u %u" + "%lu %lu %lu %u %lu %u", + &major, &minor, dev_name, + &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, + &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, + &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks, + &dc_ios, &dc_merges, &dc_sec, &dc_ticks, + &fl_ios, &fl_ticks); + + if (i < 7) { + continue; + } + + diskstatinfo = g_new0(GuestDiskStatsInfo, 1); + diskstatinfo->name = g_strdup(dev_name); + diskstatinfo->major = major; + diskstatinfo->minor = minor; + + diskstat = g_new0(GuestDiskStats, 1); + if (i == 7) { + diskstat->has_read_ios = true; + diskstat->read_ios = rd_ios; + diskstat->has_read_sectors = true; + diskstat->read_sectors = rd_merges_or_rd_sec; + diskstat->has_write_ios = true; + diskstat->write_ios = rd_sec_or_wr_ios; + diskstat->has_write_sectors = true; + diskstat->write_sectors = rd_ticks_or_wr_sec; + } + if (i >= 14) { + diskstat->has_read_ios = true; + diskstat->read_ios = rd_ios; + diskstat->has_read_sectors = true; + diskstat->read_sectors = rd_sec_or_wr_ios; + diskstat->has_read_merges = true; + diskstat->read_merges = rd_merges_or_rd_sec; + diskstat->has_read_ticks = true; + diskstat->read_ticks = rd_ticks_or_wr_sec; + diskstat->has_write_ios = true; + diskstat->write_ios = wr_ios; + diskstat->has_write_sectors = true; + diskstat->write_sectors = wr_sec; + diskstat->has_write_merges = true; + diskstat->write_merges = wr_merges; + diskstat->has_write_ticks = true; + diskstat->write_ticks = wr_ticks; + diskstat->has_ios_pgr = true; + diskstat->ios_pgr = ios_pgr; + diskstat->has_total_ticks = true; + diskstat->total_ticks = tot_ticks; + diskstat->has_weight_ticks = true; + diskstat->weight_ticks = rq_ticks; + } + if (i >= 18) { + diskstat->has_discard_ios = true; + diskstat->discard_ios = dc_ios; + diskstat->has_discard_merges = true; + diskstat->discard_merges = dc_merges; + diskstat->has_discard_sectors = true; + diskstat->discard_sectors = dc_sec; + diskstat->has_discard_ticks = true; + diskstat->discard_ticks = dc_ticks; + } + if (i >= 20) { + diskstat->has_flush_ios = true; + diskstat->flush_ios = fl_ios; + diskstat->has_flush_ticks = true; + diskstat->flush_ticks = fl_ticks; + } + + diskstatinfo->stats = g_steal_pointer(&diskstat); + QAPI_LIST_APPEND(tail, diskstatinfo); + diskstatinfo = NULL; + } + free(line); + fclose(fp); + return head; +} + +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) +{ + return guest_get_diskstats(errp); +} + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + GuestCpuStatsList *head = NULL, **tail = &head; + const char *cpustats = "/proc/stat"; + int clk_tck = sysconf(_SC_CLK_TCK); + FILE *fp; + size_t n; + char *line = NULL; + + fp = fopen(cpustats, "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", cpustats); + return NULL; + } + + while (getline(&line, &n, fp) != -1) { + GuestCpuStats *cpustat = NULL; + GuestLinuxCpuStats *linuxcpustat; + int i; + unsigned long user, system, idle, iowait, irq, softirq, steal, guest; + unsigned long nice, guest_nice; + char name[64]; + + i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + name, &user, &nice, &system, &idle, &iowait, &irq, &softirq, + &steal, &guest, &guest_nice); + + /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */ + if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) { + continue; + } + + if (i < 5) { + slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats); + break; + } + + cpustat = g_new0(GuestCpuStats, 1); + cpustat->type = GUEST_CPU_STATS_TYPE_LINUX; + + linuxcpustat = &cpustat->u.q_linux; + linuxcpustat->cpu = atoi(&name[3]); + linuxcpustat->user = user * 1000 / clk_tck; + linuxcpustat->nice = nice * 1000 / clk_tck; + linuxcpustat->system = system * 1000 / clk_tck; + linuxcpustat->idle = idle * 1000 / clk_tck; + + if (i > 5) { + linuxcpustat->has_iowait = true; + linuxcpustat->iowait = iowait * 1000 / clk_tck; + } + + if (i > 6) { + linuxcpustat->has_irq = true; + linuxcpustat->irq = irq * 1000 / clk_tck; + linuxcpustat->has_softirq = true; + linuxcpustat->softirq = softirq * 1000 / clk_tck; + } + + if (i > 8) { + linuxcpustat->has_steal = true; + linuxcpustat->steal = steal * 1000 / clk_tck; + } + + if (i > 9) { + linuxcpustat->has_guest = true; + linuxcpustat->guest = guest * 1000 / clk_tck; + } + + if (i > 10) { + linuxcpustat->has_guest = true; + linuxcpustat->guest = guest * 1000 / clk_tck; + linuxcpustat->has_guestnice = true; + linuxcpustat->guestnice = guest_nice * 1000 / clk_tck; + } + + QAPI_LIST_APPEND(tail, cpustat); + } + + free(line); + fclose(fp); + return head; +} diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 98aafc45f3..5da60e65ab 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1195,205 +1195,6 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) return info; } -#define MAX_NAME_LEN 128 -static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) -{ -#ifdef CONFIG_LINUX - GuestDiskStatsInfoList *head = NULL, **tail = &head; - const char *diskstats = "/proc/diskstats"; - FILE *fp; - size_t n; - char *line = NULL; - - fp = fopen(diskstats, "r"); - if (fp == NULL) { - error_setg_errno(errp, errno, "open(\"%s\")", diskstats); - return NULL; - } - - while (getline(&line, &n, fp) != -1) { - g_autofree GuestDiskStatsInfo *diskstatinfo = NULL; - g_autofree GuestDiskStats *diskstat = NULL; - char dev_name[MAX_NAME_LEN]; - unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks; - unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios; - unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; - unsigned long dc_ios, dc_merges, dc_sec, fl_ios; - unsigned int major, minor; - int i; - - i = sscanf(line, "%u %u %s %lu %lu %lu" - "%lu %lu %lu %lu %u %u %u %u" - "%lu %lu %lu %u %lu %u", - &major, &minor, dev_name, - &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, - &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, - &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks, - &dc_ios, &dc_merges, &dc_sec, &dc_ticks, - &fl_ios, &fl_ticks); - - if (i < 7) { - continue; - } - - diskstatinfo = g_new0(GuestDiskStatsInfo, 1); - diskstatinfo->name = g_strdup(dev_name); - diskstatinfo->major = major; - diskstatinfo->minor = minor; - - diskstat = g_new0(GuestDiskStats, 1); - if (i == 7) { - diskstat->has_read_ios = true; - diskstat->read_ios = rd_ios; - diskstat->has_read_sectors = true; - diskstat->read_sectors = rd_merges_or_rd_sec; - diskstat->has_write_ios = true; - diskstat->write_ios = rd_sec_or_wr_ios; - diskstat->has_write_sectors = true; - diskstat->write_sectors = rd_ticks_or_wr_sec; - } - if (i >= 14) { - diskstat->has_read_ios = true; - diskstat->read_ios = rd_ios; - diskstat->has_read_sectors = true; - diskstat->read_sectors = rd_sec_or_wr_ios; - diskstat->has_read_merges = true; - diskstat->read_merges = rd_merges_or_rd_sec; - diskstat->has_read_ticks = true; - diskstat->read_ticks = rd_ticks_or_wr_sec; - diskstat->has_write_ios = true; - diskstat->write_ios = wr_ios; - diskstat->has_write_sectors = true; - diskstat->write_sectors = wr_sec; - diskstat->has_write_merges = true; - diskstat->write_merges = wr_merges; - diskstat->has_write_ticks = true; - diskstat->write_ticks = wr_ticks; - diskstat->has_ios_pgr = true; - diskstat->ios_pgr = ios_pgr; - diskstat->has_total_ticks = true; - diskstat->total_ticks = tot_ticks; - diskstat->has_weight_ticks = true; - diskstat->weight_ticks = rq_ticks; - } - if (i >= 18) { - diskstat->has_discard_ios = true; - diskstat->discard_ios = dc_ios; - diskstat->has_discard_merges = true; - diskstat->discard_merges = dc_merges; - diskstat->has_discard_sectors = true; - diskstat->discard_sectors = dc_sec; - diskstat->has_discard_ticks = true; - diskstat->discard_ticks = dc_ticks; - } - if (i >= 20) { - diskstat->has_flush_ios = true; - diskstat->flush_ios = fl_ios; - diskstat->has_flush_ticks = true; - diskstat->flush_ticks = fl_ticks; - } - - diskstatinfo->stats = g_steal_pointer(&diskstat); - QAPI_LIST_APPEND(tail, diskstatinfo); - diskstatinfo = NULL; - } - free(line); - fclose(fp); - return head; -#else - g_debug("disk stats reporting available only for Linux"); - return NULL; -#endif -} - -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) -{ - return guest_get_diskstats(errp); -} - -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) -{ - GuestCpuStatsList *head = NULL, **tail = &head; - const char *cpustats = "/proc/stat"; - int clk_tck = sysconf(_SC_CLK_TCK); - FILE *fp; - size_t n; - char *line = NULL; - - fp = fopen(cpustats, "r"); - if (fp == NULL) { - error_setg_errno(errp, errno, "open(\"%s\")", cpustats); - return NULL; - } - - while (getline(&line, &n, fp) != -1) { - GuestCpuStats *cpustat = NULL; - GuestLinuxCpuStats *linuxcpustat; - int i; - unsigned long user, system, idle, iowait, irq, softirq, steal, guest; - unsigned long nice, guest_nice; - char name[64]; - - i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", - name, &user, &nice, &system, &idle, &iowait, &irq, &softirq, - &steal, &guest, &guest_nice); - - /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */ - if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) { - continue; - } - - if (i < 5) { - slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats); - break; - } - - cpustat = g_new0(GuestCpuStats, 1); - cpustat->type = GUEST_CPU_STATS_TYPE_LINUX; - - linuxcpustat = &cpustat->u.q_linux; - linuxcpustat->cpu = atoi(&name[3]); - linuxcpustat->user = user * 1000 / clk_tck; - linuxcpustat->nice = nice * 1000 / clk_tck; - linuxcpustat->system = system * 1000 / clk_tck; - linuxcpustat->idle = idle * 1000 / clk_tck; - - if (i > 5) { - linuxcpustat->has_iowait = true; - linuxcpustat->iowait = iowait * 1000 / clk_tck; - } - - if (i > 6) { - linuxcpustat->has_irq = true; - linuxcpustat->irq = irq * 1000 / clk_tck; - linuxcpustat->has_softirq = true; - linuxcpustat->softirq = softirq * 1000 / clk_tck; - } - - if (i > 8) { - linuxcpustat->has_steal = true; - linuxcpustat->steal = steal * 1000 / clk_tck; - } - - if (i > 9) { - linuxcpustat->has_guest = true; - linuxcpustat->guest = guest * 1000 / clk_tck; - } - - if (i > 10) { - linuxcpustat->has_guest = true; - linuxcpustat->guest = guest * 1000 / clk_tck; - linuxcpustat->has_guestnice = true; - linuxcpustat->guestnice = guest_nice * 1000 / clk_tck; - } - - QAPI_LIST_APPEND(tail, cpustat); - } - - free(line); - fclose(fp); - return head; -} #else /* defined(__linux__) */ From 8b93e5685f71be972b64f50cc5cdf9a02a2bb2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:43 +0100 Subject: [PATCH 2249/2884] qga: move linux memory block command impls to commands-linux.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qmp_guest_{set,get}_{memory_blocks,block_info} command impls in commands-posix.c are surrounded by '#ifdef __linux__' so should instead live in commands-linux.c This also removes a "#ifdef CONFIG_LINUX" that was nested inside a "#ifdef __linux__". Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-7-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 308 ++++++++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 311 +------------------------------------------ 2 files changed, 309 insertions(+), 310 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index c0e8bd4062..73b13fbaf6 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -1595,6 +1595,314 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return processed; } + +static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, + int size, Error **errp) +{ + int fd; + int res; + + errno = 0; + fd = openat(dirfd, pathname, O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + res = pread(fd, buf, size, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); + } else if (res == 0) { + error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname); + } + close(fd); +} + +static void ga_write_sysfs_file(int dirfd, const char *pathname, + const char *buf, int size, Error **errp) +{ + int fd; + + errno = 0; + fd = openat(dirfd, pathname, O_WRONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + if (pwrite(fd, buf, size, 0) == -1) { + error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname); + } + + close(fd); +} + +/* Transfer online/offline status between @mem_blk and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@mem_blk direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - W: mem_blk->online + * - W: mem_blk->can_offline + * + * In @mem_blk-to-system direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - R: mem_blk->online + *- R: mem_blk->can_offline + * Written members remain unmodified on error. + */ +static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, + GuestMemoryBlockResponse *result, + Error **errp) +{ + char *dirpath; + int dirfd; + char *status; + Error *local_err = NULL; + + if (!sys2memblk) { + DIR *dp; + + if (!result) { + error_setg(errp, "Internal error, 'result' should not be NULL"); + return; + } + errno = 0; + dp = opendir("/sys/devices/system/memory/"); + /* if there is no 'memory' directory in sysfs, + * we think this VM does not support online/offline memory block, + * any other solution? + */ + if (!dp) { + if (errno == ENOENT) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + } + goto out1; + } + closedir(dp); + } + + dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/", + mem_blk->phys_index); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + if (sys2memblk) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + } else { + if (errno == ENOENT) { + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND; + } else { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + g_free(dirpath); + goto out1; + } + g_free(dirpath); + + status = g_malloc0(10); + ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); + if (local_err) { + /* treat with sysfs file that not exist in old kernel */ + if (errno == ENOENT) { + error_free(local_err); + if (sys2memblk) { + mem_blk->online = true; + mem_blk->can_offline = false; + } else if (!mem_blk->online) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + } + } else { + if (sys2memblk) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + goto out2; + } + + if (sys2memblk) { + char removable = '0'; + + mem_blk->online = (strncmp(status, "online", 6) == 0); + + ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); + if (local_err) { + /* if no 'removable' file, it doesn't support offline mem blk */ + if (errno == ENOENT) { + error_free(local_err); + mem_blk->can_offline = false; + } else { + error_propagate(errp, local_err); + } + } else { + mem_blk->can_offline = (removable != '0'); + } + } else { + if (mem_blk->online != (strncmp(status, "online", 6) == 0)) { + const char *new_state = mem_blk->online ? "online" : "offline"; + + ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state), + &local_err); + if (local_err) { + error_free(local_err); + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + goto out2; + } + + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; + result->has_error_code = false; + } /* otherwise pretend successful re-(on|off)-lining */ + } + g_free(status); + close(dirfd); + return; + +out2: + g_free(status); + close(dirfd); +out1: + if (!sys2memblk) { + result->has_error_code = true; + result->error_code = errno; + } +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + GuestMemoryBlockList *head, **tail; + Error *local_err = NULL; + struct dirent *de; + DIR *dp; + + head = NULL; + tail = &head; + + dp = opendir("/sys/devices/system/memory/"); + if (!dp) { + /* it's ok if this happens to be a system that doesn't expose + * memory blocks via sysfs, but otherwise we should report + * an error + */ + if (errno != ENOENT) { + error_setg_errno(errp, errno, "Can't open directory" + "\"/sys/devices/system/memory/\""); + } + return NULL; + } + + /* Note: the phys_index of memory block may be discontinuous, + * this is because a memblk is the unit of the Sparse Memory design, which + * allows discontinuous memory ranges (ex. NUMA), so here we should + * traverse the memory block directory. + */ + while ((de = readdir(dp)) != NULL) { + GuestMemoryBlock *mem_blk; + + if ((strncmp(de->d_name, "memory", 6) != 0) || + !(de->d_type & DT_DIR)) { + continue; + } + + mem_blk = g_malloc0(sizeof *mem_blk); + /* The d_name is "memoryXXX", phys_index is block id, same as XXX */ + mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10); + mem_blk->has_can_offline = true; /* lolspeak ftw */ + transfer_memory_block(mem_blk, true, NULL, &local_err); + if (local_err) { + break; + } + + QAPI_LIST_APPEND(tail, mem_blk); + } + + closedir(dp); + if (local_err == NULL) { + /* there's no guest with zero memory blocks */ + if (head == NULL) { + error_setg(errp, "guest reported zero memory blocks!"); + } + return head; + } + + qapi_free_GuestMemoryBlockList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + GuestMemoryBlockResponseList *head, **tail; + Error *local_err = NULL; + + head = NULL; + tail = &head; + + while (mem_blks != NULL) { + GuestMemoryBlockResponse *result; + GuestMemoryBlock *current_mem_blk = mem_blks->value; + + result = g_malloc0(sizeof(*result)); + result->phys_index = current_mem_blk->phys_index; + transfer_memory_block(current_mem_blk, false, result, &local_err); + if (local_err) { /* should never happen */ + goto err; + } + + QAPI_LIST_APPEND(tail, result); + mem_blks = mem_blks->next; + } + + return head; +err: + qapi_free_GuestMemoryBlockResponseList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + Error *local_err = NULL; + char *dirpath; + int dirfd; + char *buf; + GuestMemoryBlockInfo *info; + + dirpath = g_strdup_printf("/sys/devices/system/memory/"); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + g_free(dirpath); + return NULL; + } + g_free(dirpath); + + buf = g_malloc0(20); + ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); + close(dirfd); + if (local_err) { + g_free(buf); + error_propagate(errp, local_err); + return NULL; + } + + info = g_new0(GuestMemoryBlockInfo, 1); + info->size = strtol(buf, NULL, 16); /* the unit is bytes */ + + g_free(buf); + + return info; +} + #define MAX_NAME_LEN 128 static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) { diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 5da60e65ab..2a3bef7445 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -887,316 +887,7 @@ void qmp_guest_set_user_password(const char *username, } #endif /* __linux__ || __FreeBSD__ */ -#ifdef __linux__ -static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, - int size, Error **errp) -{ - int fd; - int res; - - errno = 0; - fd = openat(dirfd, pathname, O_RDONLY); - if (fd == -1) { - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); - return; - } - - res = pread(fd, buf, size, 0); - if (res == -1) { - error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); - } else if (res == 0) { - error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname); - } - close(fd); -} - -static void ga_write_sysfs_file(int dirfd, const char *pathname, - const char *buf, int size, Error **errp) -{ - int fd; - - errno = 0; - fd = openat(dirfd, pathname, O_WRONLY); - if (fd == -1) { - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); - return; - } - - if (pwrite(fd, buf, size, 0) == -1) { - error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname); - } - - close(fd); -} - -/* Transfer online/offline status between @mem_blk and the guest system. - * - * On input either @errp or *@errp must be NULL. - * - * In system-to-@mem_blk direction, the following @mem_blk fields are accessed: - * - R: mem_blk->phys_index - * - W: mem_blk->online - * - W: mem_blk->can_offline - * - * In @mem_blk-to-system direction, the following @mem_blk fields are accessed: - * - R: mem_blk->phys_index - * - R: mem_blk->online - *- R: mem_blk->can_offline - * Written members remain unmodified on error. - */ -static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, - GuestMemoryBlockResponse *result, - Error **errp) -{ - char *dirpath; - int dirfd; - char *status; - Error *local_err = NULL; - - if (!sys2memblk) { - DIR *dp; - - if (!result) { - error_setg(errp, "Internal error, 'result' should not be NULL"); - return; - } - errno = 0; - dp = opendir("/sys/devices/system/memory/"); - /* if there is no 'memory' directory in sysfs, - * we think this VM does not support online/offline memory block, - * any other solution? - */ - if (!dp) { - if (errno == ENOENT) { - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; - } - goto out1; - } - closedir(dp); - } - - dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/", - mem_blk->phys_index); - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd == -1) { - if (sys2memblk) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - } else { - if (errno == ENOENT) { - result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND; - } else { - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - } - } - g_free(dirpath); - goto out1; - } - g_free(dirpath); - - status = g_malloc0(10); - ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); - if (local_err) { - /* treat with sysfs file that not exist in old kernel */ - if (errno == ENOENT) { - error_free(local_err); - if (sys2memblk) { - mem_blk->online = true; - mem_blk->can_offline = false; - } else if (!mem_blk->online) { - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; - } - } else { - if (sys2memblk) { - error_propagate(errp, local_err); - } else { - error_free(local_err); - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - } - } - goto out2; - } - - if (sys2memblk) { - char removable = '0'; - - mem_blk->online = (strncmp(status, "online", 6) == 0); - - ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); - if (local_err) { - /* if no 'removable' file, it doesn't support offline mem blk */ - if (errno == ENOENT) { - error_free(local_err); - mem_blk->can_offline = false; - } else { - error_propagate(errp, local_err); - } - } else { - mem_blk->can_offline = (removable != '0'); - } - } else { - if (mem_blk->online != (strncmp(status, "online", 6) == 0)) { - const char *new_state = mem_blk->online ? "online" : "offline"; - - ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state), - &local_err); - if (local_err) { - error_free(local_err); - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - goto out2; - } - - result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; - result->has_error_code = false; - } /* otherwise pretend successful re-(on|off)-lining */ - } - g_free(status); - close(dirfd); - return; - -out2: - g_free(status); - close(dirfd); -out1: - if (!sys2memblk) { - result->has_error_code = true; - result->error_code = errno; - } -} - -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - GuestMemoryBlockList *head, **tail; - Error *local_err = NULL; - struct dirent *de; - DIR *dp; - - head = NULL; - tail = &head; - - dp = opendir("/sys/devices/system/memory/"); - if (!dp) { - /* it's ok if this happens to be a system that doesn't expose - * memory blocks via sysfs, but otherwise we should report - * an error - */ - if (errno != ENOENT) { - error_setg_errno(errp, errno, "Can't open directory" - "\"/sys/devices/system/memory/\""); - } - return NULL; - } - - /* Note: the phys_index of memory block may be discontinuous, - * this is because a memblk is the unit of the Sparse Memory design, which - * allows discontinuous memory ranges (ex. NUMA), so here we should - * traverse the memory block directory. - */ - while ((de = readdir(dp)) != NULL) { - GuestMemoryBlock *mem_blk; - - if ((strncmp(de->d_name, "memory", 6) != 0) || - !(de->d_type & DT_DIR)) { - continue; - } - - mem_blk = g_malloc0(sizeof *mem_blk); - /* The d_name is "memoryXXX", phys_index is block id, same as XXX */ - mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10); - mem_blk->has_can_offline = true; /* lolspeak ftw */ - transfer_memory_block(mem_blk, true, NULL, &local_err); - if (local_err) { - break; - } - - QAPI_LIST_APPEND(tail, mem_blk); - } - - closedir(dp); - if (local_err == NULL) { - /* there's no guest with zero memory blocks */ - if (head == NULL) { - error_setg(errp, "guest reported zero memory blocks!"); - } - return head; - } - - qapi_free_GuestMemoryBlockList(head); - error_propagate(errp, local_err); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - GuestMemoryBlockResponseList *head, **tail; - Error *local_err = NULL; - - head = NULL; - tail = &head; - - while (mem_blks != NULL) { - GuestMemoryBlockResponse *result; - GuestMemoryBlock *current_mem_blk = mem_blks->value; - - result = g_malloc0(sizeof(*result)); - result->phys_index = current_mem_blk->phys_index; - transfer_memory_block(current_mem_blk, false, result, &local_err); - if (local_err) { /* should never happen */ - goto err; - } - - QAPI_LIST_APPEND(tail, result); - mem_blks = mem_blks->next; - } - - return head; -err: - qapi_free_GuestMemoryBlockResponseList(head); - error_propagate(errp, local_err); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - Error *local_err = NULL; - char *dirpath; - int dirfd; - char *buf; - GuestMemoryBlockInfo *info; - - dirpath = g_strdup_printf("/sys/devices/system/memory/"); - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd == -1) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - g_free(dirpath); - return NULL; - } - g_free(dirpath); - - buf = g_malloc0(20); - ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); - close(dirfd); - if (local_err) { - g_free(buf); - error_propagate(errp, local_err); - return NULL; - } - - info = g_new0(GuestMemoryBlockInfo, 1); - info->size = strtol(buf, NULL, 16); /* the unit is bytes */ - - g_free(buf); - - return info; -} - - -#else /* defined(__linux__) */ +#ifndef __linux__ void qmp_guest_suspend_disk(Error **errp) { From 4bb3da4b145b8cb51a71a429c8b7cb6392c0cdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:44 +0100 Subject: [PATCH 2250/2884] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defining these at the meson level allows them to be used a conditional tests in the QAPI schemas. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240712132459.3974109-8-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- meson.build | 15 +++++++++++++++ qga/commands-common.h | 9 --------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index a1e51277b0..83f9728524 100644 --- a/meson.build +++ b/meson.build @@ -2187,6 +2187,19 @@ have_virtfs_proxy_helper = get_option('virtfs_proxy_helper') \ .require(libcap_ng.found(), error_message: 'the virtfs proxy helper requires libcap-ng') \ .allowed() +qga_fsfreeze = false +qga_fstrim = false +if host_os == 'linux' + if cc.has_header_symbol('linux/fs.h', 'FIFREEZE') + qga_fsfreeze = true + endif + if cc.has_header_symbol('linux/fs.h', 'FITRIM') + qga_fstrim = true + endif +elif host_os == 'freebsd' and cc.has_header_symbol('ufs/ffs/fs.h', 'UFSSUSPEND') + qga_fsfreeze = true +endif + if get_option('block_drv_ro_whitelist') == '' config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '') else @@ -2423,6 +2436,8 @@ config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg')) config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap')) config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug')) config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed()) +config_host_data.set('CONFIG_FSFREEZE', qga_fsfreeze) +config_host_data.set('CONFIG_FSTRIM', qga_fstrim) # has_header config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h')) diff --git a/qga/commands-common.h b/qga/commands-common.h index 8c1c56aac9..263e7c0525 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -15,19 +15,10 @@ #if defined(__linux__) #include <linux/fs.h> -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#ifdef FITRIM -#define CONFIG_FSTRIM -#endif #endif /* __linux__ */ #ifdef __FreeBSD__ #include <ufs/ffs/fs.h> -#ifdef UFSSUSPEND -#define CONFIG_FSFREEZE -#endif #endif /* __FreeBSD__ */ #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) From 0e90127d9b385cca45b48117d4050fd2f57a49ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:45 +0100 Subject: [PATCH 2251/2884] qga: conditionalize schema for commands unsupported on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the commands on Windows. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This also fixes an accidental inconsistency where some commands (guest-get-diskstats & guest-get-cpustats) are implemented as stubs, yet not added to the blockedrpc list. Those change their error message from {"class": "GenericError, "desc": "this feature or command is not currently supported"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} The final additional benefit is that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240712132459.3974109-9-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 2 +- qga/commands-win32.c | 56 +------------------------------------------- qga/qapi-schema.json | 45 +++++++++++++++++++++++------------ 3 files changed, 32 insertions(+), 71 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 2a3bef7445..0dd8555867 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1280,7 +1280,7 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) "guest-get-memory-blocks", "guest-set-memory-blocks", "guest-get-memory-block-info", NULL}; - char **p = (char **)list; + const char **p = list; while (*p) { blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 9fe670d5b4..2533e4c748 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1494,11 +1494,6 @@ out: } } -void qmp_guest_suspend_hybrid(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp) { IP_ADAPTER_ADDRESSES *adptr_addrs = NULL; @@ -1862,12 +1857,6 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) return NULL; } -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return -1; -} - static gchar * get_net_error_message(gint error) { @@ -1969,46 +1958,15 @@ done: g_free(rawpasswddata); } -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - /* add unsupported commands to the list of blocked RPCs */ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { - const char *list_unsupported[] = { - "guest-suspend-hybrid", - "guest-set-vcpus", - "guest-get-memory-blocks", "guest-set-memory-blocks", - "guest-get-memory-block-info", - NULL}; - char **p = (char **)list_unsupported; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - if (!vss_init(true)) { g_debug("vss_init failed, vss commands are going to be disabled"); const char *list[] = { "guest-get-fsinfo", "guest-fsfreeze-status", "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL}; - p = (char **)list; + char **p = (char **)list; while (*p) { blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); @@ -2505,15 +2463,3 @@ char *qga_get_host_name(Error **errp) return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL); } - -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 1273d85bb5..2f0215afc7 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -637,7 +637,8 @@ # # Since: 1.1 ## -{ 'command': 'guest-suspend-hybrid', 'success-response': false } +{ 'command': 'guest-suspend-hybrid', 'success-response': false, + 'if': 'CONFIG_POSIX' } ## # @GuestIpAddressType: @@ -807,7 +808,8 @@ ## { 'command': 'guest-set-vcpus', 'data': {'vcpus': ['GuestLogicalProcessor'] }, - 'returns': 'int' } + 'returns': 'int', + 'if': 'CONFIG_POSIX' } ## # @GuestDiskBusType: @@ -1100,7 +1102,8 @@ { 'struct': 'GuestMemoryBlock', 'data': {'phys-index': 'uint64', 'online': 'bool', - '*can-offline': 'bool'} } + '*can-offline': 'bool'}, + 'if': 'CONFIG_POSIX' } ## # @guest-get-memory-blocks: @@ -1116,7 +1119,8 @@ # Since: 2.3 ## { 'command': 'guest-get-memory-blocks', - 'returns': ['GuestMemoryBlock'] } + 'returns': ['GuestMemoryBlock'], + 'if': 'CONFIG_POSIX' } ## # @GuestMemoryBlockResponseType: @@ -1139,7 +1143,8 @@ ## { 'enum': 'GuestMemoryBlockResponseType', 'data': ['success', 'not-found', 'operation-not-supported', - 'operation-failed'] } + 'operation-failed'], + 'if': 'CONFIG_POSIX' } ## # @GuestMemoryBlockResponse: @@ -1157,7 +1162,8 @@ { 'struct': 'GuestMemoryBlockResponse', 'data': { 'phys-index': 'uint64', 'response': 'GuestMemoryBlockResponseType', - '*error-code': 'int' }} + '*error-code': 'int' }, + 'if': 'CONFIG_POSIX'} ## # @guest-set-memory-blocks: @@ -1188,7 +1194,8 @@ ## { 'command': 'guest-set-memory-blocks', 'data': {'mem-blks': ['GuestMemoryBlock'] }, - 'returns': ['GuestMemoryBlockResponse'] } + 'returns': ['GuestMemoryBlockResponse'], + 'if': 'CONFIG_POSIX' } ## # @GuestMemoryBlockInfo: @@ -1200,7 +1207,8 @@ # Since: 2.3 ## { 'struct': 'GuestMemoryBlockInfo', - 'data': {'size': 'uint64'} } + 'data': {'size': 'uint64'}, + 'if': 'CONFIG_POSIX' } ## # @guest-get-memory-block-info: @@ -1212,7 +1220,8 @@ # Since: 2.3 ## { 'command': 'guest-get-memory-block-info', - 'returns': 'GuestMemoryBlockInfo' } + 'returns': 'GuestMemoryBlockInfo', + 'if': 'CONFIG_POSIX' } ## # @GuestExecStatus: @@ -1702,7 +1711,8 @@ 'data': {'name': 'str', 'major': 'uint64', 'minor': 'uint64', - 'stats': 'GuestDiskStats' } } + 'stats': 'GuestDiskStats' }, + 'if': 'CONFIG_POSIX' } ## # @guest-get-diskstats: @@ -1714,7 +1724,8 @@ # Since: 7.1 ## { 'command': 'guest-get-diskstats', - 'returns': ['GuestDiskStatsInfo'] + 'returns': ['GuestDiskStatsInfo'], + 'if': 'CONFIG_POSIX' } ## @@ -1727,7 +1738,8 @@ # Since: 7.1 ## { 'enum': 'GuestCpuStatsType', - 'data': [ 'linux' ] } + 'data': [ 'linux' ], + 'if': 'CONFIG_POSIX' } ## @@ -1772,7 +1784,8 @@ '*steal': 'uint64', '*guest': 'uint64', '*guestnice': 'uint64' - } } + }, + 'if': 'CONFIG_POSIX' } ## # @GuestCpuStats: @@ -1786,7 +1799,8 @@ { 'union': 'GuestCpuStats', 'base': { 'type': 'GuestCpuStatsType' }, 'discriminator': 'type', - 'data': { 'linux': 'GuestLinuxCpuStats' } } + 'data': { 'linux': 'GuestLinuxCpuStats' }, + 'if': 'CONFIG_POSIX' } ## # @guest-get-cpustats: @@ -1798,5 +1812,6 @@ # Since: 7.1 ## { 'command': 'guest-get-cpustats', - 'returns': ['GuestCpuStats'] + 'returns': ['GuestCpuStats'], + 'if': 'CONFIG_POSIX' } From dacc52461b045148d87606bada5cd24d1ca8c220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:46 +0100 Subject: [PATCH 2252/2884] qga: conditionalize schema for commands unsupported on non-Linux POSIX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the commands on non-Linux POSIX platforms The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240712132459.3974109-10-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 66 -------------------------------------------- qga/qapi-schema.json | 30 +++++++++++--------- 2 files changed, 17 insertions(+), 79 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0dd8555867..559d71ffae 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -887,56 +887,6 @@ void qmp_guest_set_user_password(const char *username, } #endif /* __linux__ || __FreeBSD__ */ -#ifndef __linux__ - -void qmp_guest_suspend_disk(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - -void qmp_guest_suspend_ram(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - -void qmp_guest_suspend_hybrid(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return -1; -} - -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif - #ifdef HAVE_GETIFADDRS static GuestNetworkInterface * guest_find_interface(GuestNetworkInterfaceList *head, @@ -1272,22 +1222,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) /* add unsupported commands to the list of blocked RPCs */ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { -#if !defined(__linux__) - { - const char *list[] = { - "guest-suspend-disk", "guest-suspend-ram", - "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus", - "guest-get-memory-blocks", "guest-set-memory-blocks", - "guest-get-memory-block-info", - NULL}; - const char **p = list; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - } -#endif - #if !defined(HAVE_GETIFADDRS) blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-network-get-interfaces")); diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 2f0215afc7..38483652ac 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -566,7 +566,8 @@ # # Since: 1.1 ## -{ 'command': 'guest-suspend-disk', 'success-response': false } +{ 'command': 'guest-suspend-disk', 'success-response': false, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-suspend-ram: @@ -602,7 +603,8 @@ # # Since: 1.1 ## -{ 'command': 'guest-suspend-ram', 'success-response': false } +{ 'command': 'guest-suspend-ram', 'success-response': false, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-suspend-hybrid: @@ -638,7 +640,7 @@ # Since: 1.1 ## { 'command': 'guest-suspend-hybrid', 'success-response': false, - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestIpAddressType: @@ -751,7 +753,8 @@ { 'struct': 'GuestLogicalProcessor', 'data': {'logical-id': 'int', 'online': 'bool', - '*can-offline': 'bool'} } + '*can-offline': 'bool'}, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-get-vcpus: @@ -766,7 +769,8 @@ # Since: 1.5 ## { 'command': 'guest-get-vcpus', - 'returns': ['GuestLogicalProcessor'] } + 'returns': ['GuestLogicalProcessor'], + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-set-vcpus: @@ -809,7 +813,7 @@ { 'command': 'guest-set-vcpus', 'data': {'vcpus': ['GuestLogicalProcessor'] }, 'returns': 'int', - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestDiskBusType: @@ -1103,7 +1107,7 @@ 'data': {'phys-index': 'uint64', 'online': 'bool', '*can-offline': 'bool'}, - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @guest-get-memory-blocks: @@ -1120,7 +1124,7 @@ ## { 'command': 'guest-get-memory-blocks', 'returns': ['GuestMemoryBlock'], - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockResponseType: @@ -1144,7 +1148,7 @@ { 'enum': 'GuestMemoryBlockResponseType', 'data': ['success', 'not-found', 'operation-not-supported', 'operation-failed'], - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockResponse: @@ -1163,7 +1167,7 @@ 'data': { 'phys-index': 'uint64', 'response': 'GuestMemoryBlockResponseType', '*error-code': 'int' }, - 'if': 'CONFIG_POSIX'} + 'if': 'CONFIG_LINUX'} ## # @guest-set-memory-blocks: @@ -1195,7 +1199,7 @@ { 'command': 'guest-set-memory-blocks', 'data': {'mem-blks': ['GuestMemoryBlock'] }, 'returns': ['GuestMemoryBlockResponse'], - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockInfo: @@ -1208,7 +1212,7 @@ ## { 'struct': 'GuestMemoryBlockInfo', 'data': {'size': 'uint64'}, - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @guest-get-memory-block-info: @@ -1221,7 +1225,7 @@ ## { 'command': 'guest-get-memory-block-info', 'returns': 'GuestMemoryBlockInfo', - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestExecStatus: From f8edff9b779bc39e71a832ad0b7d32184b057b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:47 +0100 Subject: [PATCH 2253/2884] qga: conditionalize schema for commands requiring getifaddrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every comamnd that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the network interface command on POSIX platforms lacking getifaddrs(). The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-11-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 13 ------------- qga/qapi-schema.json | 15 ++++++++++----- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 559d71ffae..09d08ee2ca 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1142,14 +1142,6 @@ error: return NULL; } -#else - -GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - #endif /* HAVE_GETIFADDRS */ #if !defined(CONFIG_FSFREEZE) @@ -1222,11 +1214,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) /* add unsupported commands to the list of blocked RPCs */ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { -#if !defined(HAVE_GETIFADDRS) - blockedrpcs = g_list_append(blockedrpcs, - g_strdup("guest-network-get-interfaces")); -#endif - #if !defined(CONFIG_FSFREEZE) { const char *list[] = { diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 38483652ac..79ed4f0e21 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -654,7 +654,8 @@ # Since: 1.1 ## { 'enum': 'GuestIpAddressType', - 'data': [ 'ipv4', 'ipv6' ] } + 'data': [ 'ipv4', 'ipv6' ], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestIpAddress: @@ -670,7 +671,8 @@ { 'struct': 'GuestIpAddress', 'data': {'ip-address': 'str', 'ip-address-type': 'GuestIpAddressType', - 'prefix': 'int'} } + 'prefix': 'int'}, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestNetworkInterfaceStat: @@ -702,7 +704,8 @@ 'tx-packets': 'uint64', 'tx-errs': 'uint64', 'tx-dropped': 'uint64' - } } + }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestNetworkInterface: @@ -722,7 +725,8 @@ 'data': {'name': 'str', '*hardware-address': 'str', '*ip-addresses': ['GuestIpAddress'], - '*statistics': 'GuestNetworkInterfaceStat' } } + '*statistics': 'GuestNetworkInterfaceStat' }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @guest-network-get-interfaces: @@ -734,7 +738,8 @@ # Since: 1.1 ## { 'command': 'guest-network-get-interfaces', - 'returns': ['GuestNetworkInterface'] } + 'returns': ['GuestNetworkInterface'], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestLogicalProcessor: From 83a7a1ab9af79396831fbb6df510c91e51d1d174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:48 +0100 Subject: [PATCH 2254/2884] qga: conditionalize schema for commands requiring linux/win32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some commands were blocked based on CONFIG_FSFREEZE, but their impl had nothing todo with CONFIG_FSFREEZE, and were instead either Linux-only, or Win+Linux-only. Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the stats and fsinfo commands on platforms that can't support them. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-12-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-bsd.c | 24 ----------------------- qga/commands-posix.c | 30 ++--------------------------- qga/qapi-schema.json | 45 +++++++++++++++++++++++++++----------------- 3 files changed, 30 insertions(+), 69 deletions(-) diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c index 17bddda1cf..9ce48af311 100644 --- a/qga/commands-bsd.c +++ b/qga/commands-bsd.c @@ -149,30 +149,6 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp) } return ret; } - -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} #endif /* CONFIG_FSFREEZE */ #ifdef HAVE_GETIFADDRS diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 09d08ee2ca..838dc3cf98 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1146,12 +1146,6 @@ error: #if !defined(CONFIG_FSFREEZE) -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) { error_setg(errp, QERR_UNSUPPORTED); @@ -1181,25 +1175,6 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) return 0; } - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - #endif /* CONFIG_FSFREEZE */ #if !defined(CONFIG_FSTRIM) @@ -1217,10 +1192,9 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) #if !defined(CONFIG_FSFREEZE) { const char *list[] = { - "guest-get-fsinfo", "guest-fsfreeze-status", + "guest-fsfreeze-status", "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list", - "guest-fsfreeze-thaw", "guest-get-fsinfo", - "guest-get-disks", NULL}; + "guest-fsfreeze-thaw", NULL}; char **p = (char **)list; while (*p) { diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 79ed4f0e21..9bd5aa53bc 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -870,7 +870,8 @@ { 'enum': 'GuestDiskBusType', 'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata', 'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi', - 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ] } + 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## @@ -888,7 +889,8 @@ ## { 'struct': 'GuestPCIAddress', 'data': {'domain': 'int', 'bus': 'int', - 'slot': 'int', 'function': 'int'} } + 'slot': 'int', 'function': 'int'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestCCWAddress: @@ -907,7 +909,8 @@ 'data': {'cssid': 'int', 'ssid': 'int', 'subchno': 'int', - 'devno': 'int'} } + 'devno': 'int'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestDiskAddress: @@ -936,7 +939,8 @@ 'bus-type': 'GuestDiskBusType', 'bus': 'int', 'target': 'int', 'unit': 'int', '*serial': 'str', '*dev': 'str', - '*ccw-address': 'GuestCCWAddress'} } + '*ccw-address': 'GuestCCWAddress'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestNVMeSmart: @@ -973,7 +977,8 @@ 'media-errors-lo': 'uint64', 'media-errors-hi': 'uint64', 'number-of-error-log-entries-lo': 'uint64', - 'number-of-error-log-entries-hi': 'uint64' } } + 'number-of-error-log-entries-hi': 'uint64' }, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestDiskSmart: @@ -987,7 +992,8 @@ { 'union': 'GuestDiskSmart', 'base': { 'type': 'GuestDiskBusType' }, 'discriminator': 'type', - 'data': { 'nvme': 'GuestNVMeSmart' } } + 'data': { 'nvme': 'GuestNVMeSmart' }, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestDiskInfo: @@ -1012,7 +1018,8 @@ { 'struct': 'GuestDiskInfo', 'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'], '*address': 'GuestDiskAddress', '*alias': 'str', - '*smart': 'GuestDiskSmart'} } + '*smart': 'GuestDiskSmart'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @guest-get-disks: @@ -1025,7 +1032,8 @@ # Since: 5.2 ## { 'command': 'guest-get-disks', - 'returns': ['GuestDiskInfo'] } + 'returns': ['GuestDiskInfo'], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestFilesystemInfo: @@ -1051,7 +1059,8 @@ { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', '*used-bytes': 'uint64', '*total-bytes': 'uint64', - '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']} } + '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @guest-get-fsinfo: @@ -1064,7 +1073,8 @@ # Since: 2.2 ## { 'command': 'guest-get-fsinfo', - 'returns': ['GuestFilesystemInfo'] } + 'returns': ['GuestFilesystemInfo'], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @guest-set-user-password: @@ -1703,7 +1713,8 @@ '*ios-pgr': 'uint64', '*total-ticks': 'uint64', '*weight-ticks': 'uint64' - } } + }, + 'if': 'CONFIG_LINUX' } ## # @GuestDiskStatsInfo: @@ -1721,7 +1732,7 @@ 'major': 'uint64', 'minor': 'uint64', 'stats': 'GuestDiskStats' }, - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @guest-get-diskstats: @@ -1734,7 +1745,7 @@ ## { 'command': 'guest-get-diskstats', 'returns': ['GuestDiskStatsInfo'], - 'if': 'CONFIG_POSIX' + 'if': 'CONFIG_LINUX' } ## @@ -1748,7 +1759,7 @@ ## { 'enum': 'GuestCpuStatsType', 'data': [ 'linux' ], - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## @@ -1794,7 +1805,7 @@ '*guest': 'uint64', '*guestnice': 'uint64' }, - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @GuestCpuStats: @@ -1809,7 +1820,7 @@ 'base': { 'type': 'GuestCpuStatsType' }, 'discriminator': 'type', 'data': { 'linux': 'GuestLinuxCpuStats' }, - 'if': 'CONFIG_POSIX' } + 'if': 'CONFIG_LINUX' } ## # @guest-get-cpustats: @@ -1822,5 +1833,5 @@ ## { 'command': 'guest-get-cpustats', 'returns': ['GuestCpuStats'], - 'if': 'CONFIG_POSIX' + 'if': 'CONFIG_LINUX' } From dedf99f3032aa2968af55c62a5642ed3648ff58d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:49 +0100 Subject: [PATCH 2255/2884] qga: conditionalize schema for commands only supported on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the commands on non-Windows. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-13-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 9 --------- qga/qapi-schema.json | 15 ++++++++++----- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 838dc3cf98..b7f96aa005 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1207,8 +1207,6 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim")); #endif - blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices")); - return blockedrpcs; } @@ -1419,13 +1417,6 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) return info; } -GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return NULL; -} - #ifndef HOST_NAME_MAX # ifdef _POSIX_HOST_NAME_MAX # define HOST_NAME_MAX _POSIX_HOST_NAME_MAX diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 9bd5aa53bc..de3fc46d2e 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1527,7 +1527,8 @@ # @pci: PCI device ## { 'enum': 'GuestDeviceType', - 'data': [ 'pci' ] } + 'data': [ 'pci' ], + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceIdPCI: @@ -1539,7 +1540,8 @@ # Since: 5.2 ## { 'struct': 'GuestDeviceIdPCI', - 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' } } + 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' }, + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceId: @@ -1553,7 +1555,8 @@ { 'union': 'GuestDeviceId', 'base': { 'type': 'GuestDeviceType' }, 'discriminator': 'type', - 'data': { 'pci': 'GuestDeviceIdPCI' } } + 'data': { 'pci': 'GuestDeviceIdPCI' }, + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceInfo: @@ -1574,7 +1577,8 @@ '*driver-date': 'int', '*driver-version': 'str', '*id': 'GuestDeviceId' - } } + }, + 'if': 'CONFIG_WIN32' } ## # @guest-get-devices: @@ -1586,7 +1590,8 @@ # Since: 5.2 ## { 'command': 'guest-get-devices', - 'returns': ['GuestDeviceInfo'] } + 'returns': ['GuestDeviceInfo'], + 'if': 'CONFIG_WIN32' } ## # @GuestAuthorizedKeys: From 49d8c8e2374e0ed10e8d510760bd993e311cfb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:50 +0100 Subject: [PATCH 2256/2884] qga: conditionalize schema for commands requiring fsfreeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the schema to fully exclude generation of the filesystem freezing commands on POSIX platforms lacking the required APIs. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-14-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 47 -------------------------------------------- qga/qapi-schema.json | 15 +++++++++----- 2 files changed, 10 insertions(+), 52 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index b7f96aa005..9207cb7a8f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1144,39 +1144,6 @@ error: #endif /* HAVE_GETIFADDRS */ -#if !defined(CONFIG_FSFREEZE) - -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -int64_t qmp_guest_fsfreeze_freeze(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, - strList *mountpoints, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -int64_t qmp_guest_fsfreeze_thaw(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} -#endif /* CONFIG_FSFREEZE */ - #if !defined(CONFIG_FSTRIM) GuestFilesystemTrimResponse * qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) @@ -1189,20 +1156,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) /* add unsupported commands to the list of blocked RPCs */ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { -#if !defined(CONFIG_FSFREEZE) - { - const char *list[] = { - "guest-fsfreeze-status", - "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list", - "guest-fsfreeze-thaw", NULL}; - char **p = (char **)list; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - } -#endif - #if !defined(CONFIG_FSTRIM) blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim")); #endif diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index de3fc46d2e..62462f092c 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -412,7 +412,8 @@ # Since: 0.15.0 ## { 'enum': 'GuestFsfreezeStatus', - 'data': [ 'thawed', 'frozen' ] } + 'data': [ 'thawed', 'frozen' ], + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @guest-fsfreeze-status: @@ -429,7 +430,8 @@ # Since: 0.15.0 ## { 'command': 'guest-fsfreeze-status', - 'returns': 'GuestFsfreezeStatus' } + 'returns': 'GuestFsfreezeStatus', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @guest-fsfreeze-freeze: @@ -451,7 +453,8 @@ # Since: 0.15.0 ## { 'command': 'guest-fsfreeze-freeze', - 'returns': 'int' } + 'returns': 'int', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @guest-fsfreeze-freeze-list: @@ -471,7 +474,8 @@ ## { 'command': 'guest-fsfreeze-freeze-list', 'data': { '*mountpoints': ['str'] }, - 'returns': 'int' } + 'returns': 'int', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @guest-fsfreeze-thaw: @@ -488,7 +492,8 @@ # Since: 0.15.0 ## { 'command': 'guest-fsfreeze-thaw', - 'returns': 'int' } + 'returns': 'int', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @GuestFilesystemTrimResult: From 21ca6854d8ef8020d605433773fceeecd10b11fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:51 +0100 Subject: [PATCH 2257/2884] qga: conditionalize schema for commands requiring fstrim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the filesystem trimming commands on POSIX platforms lacking required APIs. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-15-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 13 ------------- qga/qapi-schema.json | 9 ++++++--- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9207cb7a8f..d92fa0ec87 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1144,22 +1144,9 @@ error: #endif /* HAVE_GETIFADDRS */ -#if !defined(CONFIG_FSTRIM) -GuestFilesystemTrimResponse * -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} -#endif - /* add unsupported commands to the list of blocked RPCs */ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { -#if !defined(CONFIG_FSTRIM) - blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim")); -#endif - return blockedrpcs; } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 62462f092c..21c65d1806 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -510,7 +510,8 @@ ## { 'struct': 'GuestFilesystemTrimResult', 'data': {'path': 'str', - '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} } + '*trimmed': 'int', '*minimum': 'int', '*error': 'str'}, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @GuestFilesystemTrimResponse: @@ -520,7 +521,8 @@ # Since: 2.4 ## { 'struct': 'GuestFilesystemTrimResponse', - 'data': {'paths': ['GuestFilesystemTrimResult']} } + 'data': {'paths': ['GuestFilesystemTrimResult']}, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @guest-fstrim: @@ -542,7 +544,8 @@ ## { 'command': 'guest-fstrim', 'data': { '*minimum': 'int' }, - 'returns': 'GuestFilesystemTrimResponse' } + 'returns': 'GuestFilesystemTrimResponse', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @guest-suspend-disk: From adbe794a6ef7e8c52cec2dc3c8685f1a96507f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:52 +0100 Subject: [PATCH 2258/2884] qga: conditionalize schema for commands requiring libudev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the schema to fully exclude generation of the filesystem trimming commands on POSIX platforms lacking required APIs. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-16-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 8 -------- qga/qapi-schema.json | 8 ++++---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 73b13fbaf6..89bdcded01 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -1049,14 +1049,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) return ret; } -#else - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - #endif /* Return a list of the disk device(s)' info which @mount lies on */ diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 21c65d1806..cf1ad42519 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -986,7 +986,7 @@ 'media-errors-hi': 'uint64', 'number-of-error-log-entries-lo': 'uint64', 'number-of-error-log-entries-hi': 'uint64' }, - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @GuestDiskSmart: @@ -1001,7 +1001,7 @@ 'base': { 'type': 'GuestDiskBusType' }, 'discriminator': 'type', 'data': { 'nvme': 'GuestNVMeSmart' }, - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @GuestDiskInfo: @@ -1027,7 +1027,7 @@ 'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'], '*address': 'GuestDiskAddress', '*alias': 'str', '*smart': 'GuestDiskSmart'}, - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @guest-get-disks: @@ -1041,7 +1041,7 @@ ## { 'command': 'guest-get-disks', 'returns': ['GuestDiskInfo'], - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @GuestFilesystemInfo: From 2799f434a404276027f3e33debdcfae465ddac97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:53 +0100 Subject: [PATCH 2259/2884] qga: conditionalize schema for commands requiring utmpx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the get-users command on POSIX platforms lacking required APIs. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-17-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 10 +--------- qga/qapi-schema.json | 6 ++++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index d92fa0ec87..a353f64ae6 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1212,15 +1212,7 @@ GuestUserList *qmp_guest_get_users(Error **errp) return head; } -#else - -GuestUserList *qmp_guest_get_users(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif +#endif /* HAVE_UTMPX */ /* Replace escaped special characters with their real values. The replacement * is done in place -- returned value is in the original string. diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index cf1ad42519..0662a68c43 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1414,7 +1414,8 @@ # Since: 2.10 ## { 'struct': 'GuestUser', - 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } } + 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } } ## # @guest-get-users: @@ -1426,7 +1427,8 @@ # Since: 2.10 ## { 'command': 'guest-get-users', - 'returns': ['GuestUser'] } + 'returns': ['GuestUser'], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } } ## # @GuestTimezone: From 4be55a4fce18de5ce660a0c2b0961a6333fd957f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:54 +0100 Subject: [PATCH 2260/2884] qga: conditionalize schema for commands not supported on other UNIX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than creating stubs for every command that just return QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to fully exclude generation of the commands on other UNIX. The command will be rejected at QMP dispatch time instead, avoiding reimplementing rejection by blocking the stub commands. This changes the error message for affected commands from {"class": "CommandNotFound", "desc": "Command FOO has been disabled"} to {"class": "CommandNotFound", "desc": "The command FOO has not been found"} This has the additional benefit that the QGA protocol reference now documents what conditions enable use of the command. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-18-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- meson.build | 1 + qga/commands-posix.c | 8 -------- qga/qapi-schema.json | 3 ++- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 83f9728524..23f35193ee 100644 --- a/meson.build +++ b/meson.build @@ -2276,6 +2276,7 @@ config_host_data.set('CONFIG_ATTR', libattr.found()) config_host_data.set('CONFIG_BDRV_WHITELIST_TOOLS', get_option('block_drv_whitelist_in_tools')) config_host_data.set('CONFIG_BRLAPI', brlapi.found()) config_host_data.set('CONFIG_BSD', host_os in bsd_oses) +config_host_data.set('CONFIG_FREEBSD', host_os == 'freebsd') config_host_data.set('CONFIG_CAPSTONE', capstone.found()) config_host_data.set('CONFIG_COCOA', cocoa.found()) config_host_data.set('CONFIG_DARWIN', host_os == 'darwin') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index a353f64ae6..f4104f2760 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -877,14 +877,6 @@ void qmp_guest_set_user_password(const char *username, return; } } -#else /* __linux__ || __FreeBSD__ */ -void qmp_guest_set_user_password(const char *username, - const char *password, - bool crypted, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} #endif /* __linux__ || __FreeBSD__ */ #ifdef HAVE_GETIFADDRS diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 0662a68c43..c763163fcd 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1109,7 +1109,8 @@ # Since: 2.3 ## { 'command': 'guest-set-user-password', - 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } } + 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' }, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX', 'CONFIG_FREEBSD'] } } ## # @GuestMemoryBlock: From b81837f0d37ab1e1ee718d6a356e88089ec604b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:55 +0100 Subject: [PATCH 2261/2884] qga: don't disable fsfreeze commands if vss_init fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fsfreeze commands are already written to report an error if vss_init() fails. Reporting a more specific error message is more helpful than a generic "command is disabled" message, which cannot between an admin config decision and lack of platform support. Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240712132459.3974109-19-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-win32.c | 18 +++--------------- qga/main.c | 4 ++++ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 2533e4c748..5866cc2e3c 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1203,7 +1203,7 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) { if (!vss_initialized()) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "fsfreeze not possible as VSS failed to initialize"); return 0; } @@ -1231,7 +1231,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, Error *local_err = NULL; if (!vss_initialized()) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "fsfreeze not possible as VSS failed to initialize"); return 0; } @@ -1266,7 +1266,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) int i; if (!vss_initialized()) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "fsfreeze not possible as VSS failed to initialize"); return 0; } @@ -1961,18 +1961,6 @@ done: /* add unsupported commands to the list of blocked RPCs */ GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { - if (!vss_init(true)) { - g_debug("vss_init failed, vss commands are going to be disabled"); - const char *list[] = { - "guest-get-fsinfo", "guest-fsfreeze-status", - "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL}; - char **p = (char **)list; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - } - return blockedrpcs; } diff --git a/qga/main.c b/qga/main.c index f4d5f15bb3..17b6ce18ac 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1395,6 +1395,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) " '%s': %s", config->state_dir, strerror(errno)); return NULL; } + + if (!vss_init(true)) { + g_debug("vss_init failed, vss commands will not function"); + } #endif if (ga_is_frozen(s)) { From 3390a0de58426ae323f4ac63bef0d44234e4fad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:56 +0100 Subject: [PATCH 2262/2884] qga: move declare of QGAConfig struct to top of file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is referenced by QGAState already, and it is clearer to declare all data types at the top of the file, rather than have them mixed with code later. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-ID: <20240712132459.3974109-20-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/main.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/qga/main.c b/qga/main.c index 17b6ce18ac..647d27037c 100644 --- a/qga/main.c +++ b/qga/main.c @@ -70,6 +70,28 @@ typedef struct GAPersistentState { typedef struct GAConfig GAConfig; +struct GAConfig { + char *channel_path; + char *method; + char *log_filepath; + char *pid_filepath; +#ifdef CONFIG_FSFREEZE + char *fsfreeze_hook; +#endif + char *state_dir; +#ifdef _WIN32 + const char *service; +#endif + gchar *bliststr; /* blockedrpcs may point to this string */ + gchar *aliststr; /* allowedrpcs may point to this string */ + GList *blockedrpcs; + GList *allowedrpcs; + int daemonize; + GLogLevelFlags log_level; + int dumpconf; + bool retry_path; +}; + struct GAState { JSONMessageParser parser; GMainLoop *main_loop; @@ -996,28 +1018,6 @@ static GList *split_list(const gchar *str, const gchar *delim) return list; } -struct GAConfig { - char *channel_path; - char *method; - char *log_filepath; - char *pid_filepath; -#ifdef CONFIG_FSFREEZE - char *fsfreeze_hook; -#endif - char *state_dir; -#ifdef _WIN32 - const char *service; -#endif - gchar *bliststr; /* blockedrpcs may point to this string */ - gchar *aliststr; /* allowedrpcs may point to this string */ - GList *blockedrpcs; - GList *allowedrpcs; - int daemonize; - GLogLevelFlags log_level; - int dumpconf; - bool retry_path; -}; - static void config_load(GAConfig *config) { GError *gerr = NULL; From 8909fc17329a01dc0dc973486484cc73c8520754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:57 +0100 Subject: [PATCH 2263/2884] qga: remove pointless 'blockrpcs_key' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This variable was used to support back compat for the old config file key name, and became redundant after the following change: commit a7a2d636ae4549ef0551134d4bf8e084a14431c4 Author: Philippe Mathieu-Daudé <philmd@linaro.org> Date: Thu May 30 08:36:43 2024 +0200 qga: Remove deprecated 'blacklist' argument / config key Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240712132459.3974109-21-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qga/main.c b/qga/main.c index 647d27037c..6ff022a85d 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1023,7 +1023,6 @@ static void config_load(GAConfig *config) GError *gerr = NULL; GKeyFile *keyfile; g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT); - const gchar *blockrpcs_key = "block-rpcs"; /* read system config */ keyfile = g_key_file_new(); @@ -1071,9 +1070,9 @@ static void config_load(GAConfig *config) g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr); } - if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL)) { + if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL)) { config->bliststr = - g_key_file_get_string(keyfile, "general", blockrpcs_key, &gerr); + g_key_file_get_string(keyfile, "general", "block-rpcs", &gerr); config->blockedrpcs = g_list_concat(config->blockedrpcs, split_list(config->bliststr, ",")); } @@ -1084,7 +1083,7 @@ static void config_load(GAConfig *config) split_list(config->aliststr, ",")); } - if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL) && + if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL) && g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at" " the same time is not allowed"); From f8bf2347ed6036adf195202fa55c2cb587004679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:58 +0100 Subject: [PATCH 2264/2884] qga: allow configuration file path via the cli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allowing the user to set the QGA_CONF environment variable to change the default configuration file path is very unusual practice, made more obscure since this ability is not documented. This introduces the more normal '-c PATH' / '--config=PATH' command line argument approach. This requires that we parse the comamnd line twice, since we want the command line arguments to take priority over the configuration file settings in general. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Message-ID: <20240712132459.3974109-22-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- docs/interop/qemu-ga.rst | 5 +++++ qga/main.c | 43 ++++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 72fb75a6f5..e42b370319 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -33,6 +33,11 @@ Options .. program:: qemu-ga +.. option:: -c, --config=PATH + + Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``, + unless overriden by the QGA_CONF environment variable) + .. option:: -m, --method=METHOD Transport method: one of ``unix-listen``, ``virtio-serial``, or diff --git a/qga/main.c b/qga/main.c index 6ff022a85d..6ae911eb15 100644 --- a/qga/main.c +++ b/qga/main.c @@ -248,12 +248,16 @@ static void usage(const char *cmd) #ifdef CONFIG_FSFREEZE g_autofree char *fsfreeze_hook = get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT); #endif + g_autofree char *conf_path = get_relocated_path(QGA_CONF_DEFAULT); printf( "Usage: %s [-m <method> -p <path>] [<options>]\n" "QEMU Guest Agent " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n" "\n" +" -c, --config=PATH configuration file path (default is\n" +" %s/qemu-ga.conf\n" +" unless overriden by the QGA_CONF environment variable)\n" " -m, --method transport method: one of unix-listen, virtio-serial,\n" " isa-serial, or vsock-listen (virtio-serial is the default)\n" " -p, --path device/socket path (the default for virtio-serial is:\n" @@ -294,8 +298,8 @@ QEMU_COPYRIGHT "\n" " plug/unplug, etc.)\n" " -h, --help display this help and exit\n" "\n" -QEMU_HELP_BOTTOM "\n" - , cmd, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, +QEMU_HELP_BOTTOM "\n", + cmd, conf_path, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE fsfreeze_hook, @@ -1018,15 +1022,14 @@ static GList *split_list(const gchar *str, const gchar *delim) return list; } -static void config_load(GAConfig *config) +static void config_load(GAConfig *config, const char *confpath, bool required) { GError *gerr = NULL; GKeyFile *keyfile; - g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT); /* read system config */ keyfile = g_key_file_new(); - if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) { + if (!g_key_file_load_from_file(keyfile, confpath, 0, &gerr)) { goto end; } if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) { @@ -1092,10 +1095,10 @@ static void config_load(GAConfig *config) end: g_key_file_free(keyfile); - if (gerr && - !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT)) { + if (gerr && (required || + !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT))) { g_critical("error loading configuration from path: %s, %s", - conf, gerr->message); + confpath, gerr->message); exit(EXIT_FAILURE); } g_clear_error(&gerr); @@ -1167,12 +1170,13 @@ static void config_dump(GAConfig *config) static void config_parse(GAConfig *config, int argc, char **argv) { - const char *sopt = "hVvdm:p:l:f:F::b:a:s:t:Dr"; + const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; bool block_rpcs = false, allow_rpcs = false; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, + { "config", 1, NULL, 'c' }, { "dump-conf", 0, NULL, 'D' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, @@ -1192,6 +1196,26 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "retry-path", 0, NULL, 'r' }, { NULL, 0, NULL, 0 } }; + g_autofree char *confpath = g_strdup(g_getenv("QGA_CONF")) ?: + get_relocated_path(QGA_CONF_DEFAULT); + bool confrequired = false; + + while ((ch = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (ch) { + case 'c': + g_free(confpath); + confpath = g_strdup(optarg); + confrequired = true; + break; + default: + break; + } + } + + config_load(config, confpath, confrequired); + + /* Reset for second pass */ + optind = 1; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -1582,7 +1606,6 @@ int main(int argc, char **argv) qga_qmp_init_marshal(&ga_commands); init_dfl_pathnames(); - config_load(config); config_parse(config, argc, argv); if (config->pid_filepath == NULL) { From 2e3b166c41e20eba7e74d9a9203a663cc455bc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 12 Jul 2024 14:24:59 +0100 Subject: [PATCH 2265/2884] qga: centralize logic for disabling/enabling commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is confusing having many different pieces of code enabling and disabling commands, and it is not clear that they all have the same semantics, especially wrt prioritization of the block/allow lists. The code attempted to prevent the user from setting both the block and allow lists concurrently, however, the logic was flawed as it checked settings in the configuration file separately from the command line arguments. Thus it was possible to set a block list in the config file and an allow list via a command line argument. The --dump-conf option also creates a configuration file with both keys present, even if unset, which means it is creating a config that cannot actually be loaded again. Centralizing the code in a single method "ga_apply_command_filters" will provide a strong guarantee of consistency and clarify the intended behaviour. With this there is no compelling technical reason to prevent concurrent setting of both the allow and block lists, so this flawed restriction is removed. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Message-ID: <20240712132459.3974109-23-berrange@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- docs/interop/qemu-ga.rst | 14 +++++ qga/commands-posix.c | 6 -- qga/commands-win32.c | 6 -- qga/main.c | 128 +++++++++++++++++---------------------- 4 files changed, 70 insertions(+), 84 deletions(-) diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index e42b370319..fb75cfd8d4 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -28,6 +28,20 @@ configuration options on the command line. For the same key, the last option wins, but the lists accumulate (see below for configuration file format). +If an allowed RPCs list is defined in the configuration, then all +RPCs will be blocked by default, except for the allowed list. + +If a blocked RPCs list is defined in the configuration, then all +RPCs will be allowed by default, except for the blocked list. + +If both allowed and blocked RPCs lists are defined in the configuration, +then all RPCs will be blocked by default, then the allowed list will +be applied, followed by the blocked list. + +While filesystems are frozen, all except for a designated safe set +of RPCs will blocked, regardless of what the general configuration +declares. + Options ------- diff --git a/qga/commands-posix.c b/qga/commands-posix.c index f4104f2760..578d29f228 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1136,12 +1136,6 @@ error: #endif /* HAVE_GETIFADDRS */ -/* add unsupported commands to the list of blocked RPCs */ -GList *ga_command_init_blockedrpcs(GList *blockedrpcs) -{ - return blockedrpcs; -} - /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 5866cc2e3c..61b36da469 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1958,12 +1958,6 @@ done: g_free(rawpasswddata); } -/* add unsupported commands to the list of blocked RPCs */ -GList *ga_command_init_blockedrpcs(GList *blockedrpcs) -{ - return blockedrpcs; -} - /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { diff --git a/qga/main.c b/qga/main.c index 6ae911eb15..b8f7b1e4a3 100644 --- a/qga/main.c +++ b/qga/main.c @@ -423,58 +423,77 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) return strcmp(str1, str2); } -/* disable commands that aren't safe for fsfreeze */ -static void ga_disable_not_allowed_freeze(const QmpCommand *cmd, void *opaque) +static bool ga_command_is_allowed(const QmpCommand *cmd, GAState *state) { - bool allowed = false; int i = 0; + GAConfig *config = state->config; const char *name = qmp_command_name(cmd); + /* Fallback policy is allow everything */ + bool allowed = true; - while (ga_freeze_allowlist[i] != NULL) { - if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + if (config->allowedrpcs) { + /* + * If an allow-list is given, this changes the fallback + * policy to deny everything + */ + allowed = false; + + if (g_list_find_custom(config->allowedrpcs, name, ga_strcmp) != NULL) { allowed = true; } - i++; } - if (!allowed) { - g_debug("disabling command: %s", name); - qmp_disable_command(&ga_commands, name, "the agent is in frozen state"); + + /* + * If both allowedrpcs and blockedrpcs are set, the blocked + * list will take priority + */ + if (config->blockedrpcs) { + if (g_list_find_custom(config->blockedrpcs, name, ga_strcmp) != NULL) { + allowed = false; + } } + + /* + * If frozen, this filtering must take priority over + * absolutely everything + */ + if (state->frozen) { + allowed = false; + + while (ga_freeze_allowlist[i] != NULL) { + if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + allowed = true; + } + i++; + } + } + + return allowed; } -/* [re-]enable all commands, except those explicitly blocked by user */ -static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque) +static void ga_apply_command_filters_iter(const QmpCommand *cmd, void *opaque) { - GAState *s = opaque; - GList *blockedrpcs = s->blockedrpcs; - GList *allowedrpcs = s->allowedrpcs; + GAState *state = opaque; + bool want = ga_command_is_allowed(cmd, state); + bool have = qmp_command_is_enabled(cmd); const char *name = qmp_command_name(cmd); - if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) { - if (qmp_command_is_enabled(cmd)) { - return; - } - - if (allowedrpcs && - g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { - return; - } + if (want == have) { + return; + } + if (have) { + g_debug("disabling command: %s", name); + qmp_disable_command(&ga_commands, name, "the command is not allowed"); + } else { g_debug("enabling command: %s", name); qmp_enable_command(&ga_commands, name); } } -/* disable commands that aren't allowed */ -static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque) +static void ga_apply_command_filters(GAState *state) { - GList *allowedrpcs = opaque; - const char *name = qmp_command_name(cmd); - - if (g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { - g_debug("disabling command: %s", name); - qmp_disable_command(&ga_commands, name, "the command is not allowed"); - } + qmp_for_each_command(&ga_commands, ga_apply_command_filters_iter, state); } static bool ga_create_file(const char *path) @@ -509,15 +528,14 @@ void ga_set_frozen(GAState *s) if (ga_is_frozen(s)) { return; } - /* disable all forbidden (for frozen state) commands */ - qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); g_warning("disabling logging due to filesystem freeze"); - ga_disable_logging(s); s->frozen = true; if (!ga_create_file(s->state_filepath_isfrozen)) { g_warning("unable to create %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } + ga_apply_command_filters(s); + ga_disable_logging(s); } void ga_unset_frozen(GAState *s) @@ -549,12 +567,12 @@ void ga_unset_frozen(GAState *s) } /* enable all disabled, non-blocked and allowed commands */ - qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s); s->frozen = false; if (!ga_delete_file(s->state_filepath_isfrozen)) { g_warning("unable to delete %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } + ga_apply_command_filters(s); } #ifdef CONFIG_FSFREEZE @@ -1086,13 +1104,6 @@ static void config_load(GAConfig *config, const char *confpath, bool required) split_list(config->aliststr, ",")); } - if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL) && - g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { - g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at" - " the same time is not allowed"); - exit(EXIT_FAILURE); - } - end: g_key_file_free(keyfile); if (gerr && (required || @@ -1172,7 +1183,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) { const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; - bool block_rpcs = false, allow_rpcs = false; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1268,7 +1278,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) } config->blockedrpcs = g_list_concat(config->blockedrpcs, split_list(optarg, ",")); - block_rpcs = true; break; } case 'a': { @@ -1278,7 +1287,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) } config->allowedrpcs = g_list_concat(config->allowedrpcs, split_list(optarg, ",")); - allow_rpcs = true; break; } #ifdef _WIN32 @@ -1319,12 +1327,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) exit(EXIT_FAILURE); } } - - if (block_rpcs && allow_rpcs) { - g_critical("wrong commandline, using --block-rpcs and --allow-rpcs at the" - " same time is not allowed"); - exit(EXIT_FAILURE); - } } static void config_free(GAConfig *config) @@ -1435,7 +1437,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->deferred_options.log_filepath = config->log_filepath; } ga_disable_logging(s); - qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); } else { if (config->daemonize) { become_daemon(config->pid_filepath); @@ -1459,25 +1460,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) return NULL; } - if (config->allowedrpcs) { - qmp_for_each_command(&ga_commands, ga_disable_not_allowed, config->allowedrpcs); - s->allowedrpcs = config->allowedrpcs; - } - - /* - * Some commands can be blocked due to system limitation. - * Initialize blockedrpcs list even if allowedrpcs specified. - */ - config->blockedrpcs = ga_command_init_blockedrpcs(config->blockedrpcs); - if (config->blockedrpcs) { - GList *l = config->blockedrpcs; - s->blockedrpcs = config->blockedrpcs; - do { - g_debug("disabling command: %s", (char *)l->data); - qmp_disable_command(&ga_commands, l->data, NULL); - l = g_list_next(l); - } while (l); - } s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); @@ -1503,6 +1485,8 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) } #endif + ga_apply_command_filters(s); + ga_state = s; return s; } From 1d523869d71aa5e4e17f652c04628ee8432bb8d2 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Wed, 17 Jul 2024 00:23:51 +0800 Subject: [PATCH 2266/2884] qga/commands-posix: Make ga_wait_child() return boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make ga_wait_child() return boolean and check the returned boolean in ga_run_command() instead of dereferencing @errp. Cc: Michael Roth <michael.roth@amd.com> Cc: Konstantin Kostiuk <kkostiuk@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Message-ID: <20240716162351.270095-1-zhao1.liu@intel.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-posix.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 578d29f228..c2bd0b4316 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -48,7 +48,7 @@ #endif #endif -static void ga_wait_child(pid_t pid, int *status, Error **errp) +static bool ga_wait_child(pid_t pid, int *status, Error **errp) { pid_t rpid; @@ -59,10 +59,11 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp) if (rpid == -1) { error_setg_errno(errp, errno, "failed to wait for child (pid: %d)", pid); - return; + return false; } g_assert(rpid == pid); + return true; } static ssize_t ga_pipe_read_str(int fd[2], char **str) @@ -167,8 +168,7 @@ static int ga_run_command(const char *argv[], const char *in_str, goto out; } - ga_wait_child(pid, &status, errp); - if (*errp) { + if (!ga_wait_child(pid, &status, errp)) { goto out; } From ce22616181c34d8a9755e8db1b8c27b6f8cedb74 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht <t.lamprecht@proxmox.com> Date: Thu, 18 Jul 2024 16:04:07 +0200 Subject: [PATCH 2267/2884] guest-agent: document allow-rpcs in config file section While the `allow-rpcs` option is documented in the CLI options section, it was missing in the section about the configuration file syntax. And while it's mentioned that "the list of keys follows the command line options", having `block-rpcs` there but not `allow-rpcs` seems like being a potential source of confusion; and as it's cheap to add let's just do so. Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Message-ID: <20240718140407.444160-1-t.lamprecht@proxmox.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- docs/interop/qemu-ga.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index fb75cfd8d4..9c7380896a 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -150,6 +150,7 @@ fsfreeze-hook string statedir string verbose boolean block-rpcs string list +allow-rpcs string list ============= =========== See also From 13951ccfcdf0f31902a93859506ccf8c0ef66583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@redhat.com> Date: Fri, 12 Jul 2024 17:13:15 +0200 Subject: [PATCH 2268/2884] aspeed/smc: Fix possible integer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reports a possible integer overflow because routine aspeeed_smc_hclk_divisor() has a codepath returning 0, which could lead to an integer overflow when computing variable 'hclk_shift' in the caller aspeed_smc_dma_calibration(). The value passed to aspeed_smc_hclk_divisor() is always between 0 and 15 and, in this case, there is always a matching hclk divisor. Remove the return 0 and use g_assert_not_reached() instead. Fixes: Coverity CID 1547822 Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- hw/ssi/aspeed_smc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 49205ab76d..f39fb85a35 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -789,8 +789,7 @@ static uint8_t aspeed_smc_hclk_divisor(uint8_t hclk_mask) } } - aspeed_smc_error("invalid HCLK mask %x", hclk_mask); - return 0; + g_assert_not_reached(); } /* From dddfc771e034887560c30fd2720b7798c04f5642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:15 +0200 Subject: [PATCH 2269/2884] aspeed: Change type of eMMC device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QEMU device model representing the eMMC device of the machine is currently created with type SD_CARD. Change the type to EMMC now that it is available. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/arm/aspeed.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 53a4f665d0..105b990233 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -327,14 +327,14 @@ void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, } } -static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) +static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc) { DeviceState *card; if (!dinfo) { return; } - card = qdev_new(TYPE_SD_CARD); + card = qdev_new(emmc ? TYPE_EMMC : TYPE_SD_CARD); qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); qdev_realize_and_unref(card, @@ -436,12 +436,13 @@ static void aspeed_machine_init(MachineState *machine) for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { sdhci_attach_drive(&bmc->soc->sdhci.slots[i], - drive_get(IF_SD, 0, i)); + drive_get(IF_SD, 0, i), false); } if (bmc->soc->emmc.num_slots) { sdhci_attach_drive(&bmc->soc->emmc.slots[0], - drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots)); + drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots), + true); } if (!bmc->mmio_exec) { From 255aed8134190966d0bd090c97391f6512c2fbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:16 +0200 Subject: [PATCH 2270/2884] aspeed: Load eMMC first boot area as a boot rom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first boot area partition (64K) of the eMMC device should contain an initial boot loader (u-boot SPL). Load it as a ROM only if an eMMC device is available to boot from but no flash device is. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/arm/aspeed.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 105b990233..756deb91ef 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -364,6 +364,7 @@ static void aspeed_machine_init(MachineState *machine) AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); AspeedSoCClass *sc; int i; + DriveInfo *emmc0 = NULL; bmc->soc = ASPEED_SOC(object_new(amc->soc_name)); object_property_add_child(OBJECT(machine), "soc", OBJECT(bmc->soc)); @@ -440,9 +441,8 @@ static void aspeed_machine_init(MachineState *machine) } if (bmc->soc->emmc.num_slots) { - sdhci_attach_drive(&bmc->soc->emmc.slots[0], - drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots), - true); + emmc0 = drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots); + sdhci_attach_drive(&bmc->soc->emmc.slots[0], emmc0, true); } if (!bmc->mmio_exec) { @@ -452,6 +452,8 @@ static void aspeed_machine_init(MachineState *machine) if (fmc0) { uint64_t rom_size = memory_region_size(&bmc->soc->spi_boot); aspeed_install_boot_rom(bmc, fmc0, rom_size); + } else if (emmc0) { + aspeed_install_boot_rom(bmc, blk_by_legacy_dinfo(emmc0), 64 * KiB); } } From cc8bae6f6270c52c8f9854a83f9cefec3e5ec108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:17 +0200 Subject: [PATCH 2271/2884] aspeed/scu: Add boot-from-eMMC HW strapping bit for AST2600 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bit SCU500[2] of the AST2600 controls the boot device of the SoC. Future changes will configure this bit to boot from eMMC disk images specially built for this purpose. Signed-off-by: Joel Stanley <joel@jms.id.au> Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- include/hw/misc/aspeed_scu.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 58db28db45..356be95e45 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -349,6 +349,10 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); #define SCU_AST2600_H_PLL_BYPASS_EN (0x1 << 24) #define SCU_AST2600_H_PLL_OFF (0x1 << 23) +/* STRAP1 SCU500 */ +#define SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC (0x1 << 2) +#define SCU_AST2600_HW_STRAP_BOOT_SRC_SPI (0x0 << 2) + /* * SCU310 Clock Selection Register Set 4 (for Aspeed AST1030 SOC) * From eea55625df839813c66c5e2e8f41d9131d591f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:18 +0200 Subject: [PATCH 2272/2884] aspeed: Introduce a AspeedSoCClass 'boot_from_emmc' handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report support on the AST2600 SoC if the boot-from-eMMC HW strapping bit is set at the board level. AST2700 also has support but it is not yet ready in QEMU and others SoCs do not have support, so return false always for these. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/arm/aspeed_ast2600.c | 8 ++++++++ hw/arm/aspeed_soc_common.c | 7 +++++++ include/hw/arm/aspeed_soc.h | 1 + 3 files changed, 16 insertions(+) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 31713de74a..be3eb70cdd 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -646,6 +646,13 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } } +static bool aspeed_soc_ast2600_boot_from_emmc(AspeedSoCState *s) +{ + uint32_t hw_strap1 = object_property_get_uint(OBJECT(&s->scu), + "hw-strap1", &error_abort); + return !!(hw_strap1 & SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC); +} + static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) { static const char * const valid_cpu_types[] = { @@ -673,6 +680,7 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) sc->memmap = aspeed_soc_ast2600_memmap; sc->num_cpus = 2; sc->get_irq = aspeed_soc_ast2600_get_irq; + sc->boot_from_emmc = aspeed_soc_ast2600_boot_from_emmc; } static const TypeInfo aspeed_soc_ast2600_types[] = { diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 1e8f2558fd..05551461ae 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -134,6 +134,11 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } } +static bool aspeed_soc_boot_from_emmc(AspeedSoCState *s) +{ + return false; +} + static Property aspeed_soc_properties[] = { DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), @@ -145,9 +150,11 @@ static Property aspeed_soc_properties[] = { static void aspeed_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); dc->realize = aspeed_soc_realize; device_class_set_props(dc, aspeed_soc_properties); + sc->boot_from_emmc = aspeed_soc_boot_from_emmc; } static const TypeInfo aspeed_soc_types[] = { diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 849ba37f95..624d489e0d 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -164,6 +164,7 @@ struct AspeedSoCClass { const hwaddr *memmap; uint32_t num_cpus; qemu_irq (*get_irq)(AspeedSoCState *s, int dev); + bool (*boot_from_emmc)(AspeedSoCState *s); }; const char *aspeed_soc_cpu_type(AspeedSoCClass *sc); From e554e45b447880e37538d300ac6e12111788ac4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:19 +0200 Subject: [PATCH 2273/2884] aspeed: Tune eMMC device properties to reflect HW strapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the boot-from-eMMC HW strapping bit is set, use the 'boot-config' property to set the boot config register to boot from the first boot area partition of the eMMC device. Also set the boot partition size of the device. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/arm/aspeed.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 756deb91ef..bc4ca17542 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -327,7 +327,8 @@ void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, } } -static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc) +static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc, + bool boot_emmc) { DeviceState *card; @@ -335,6 +336,11 @@ static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc) return; } card = qdev_new(emmc ? TYPE_EMMC : TYPE_SD_CARD); + if (emmc) { + qdev_prop_set_uint64(card, "boot-partition-size", 1 * MiB); + qdev_prop_set_uint8(card, "boot-config", + boot_emmc ? 0x1 << 3 : 0x0); + } qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); qdev_realize_and_unref(card, @@ -365,6 +371,7 @@ static void aspeed_machine_init(MachineState *machine) AspeedSoCClass *sc; int i; DriveInfo *emmc0 = NULL; + bool boot_emmc; bmc->soc = ASPEED_SOC(object_new(amc->soc_name)); object_property_add_child(OBJECT(machine), "soc", OBJECT(bmc->soc)); @@ -437,19 +444,21 @@ static void aspeed_machine_init(MachineState *machine) for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { sdhci_attach_drive(&bmc->soc->sdhci.slots[i], - drive_get(IF_SD, 0, i), false); + drive_get(IF_SD, 0, i), false, false); } + boot_emmc = sc->boot_from_emmc(bmc->soc); + if (bmc->soc->emmc.num_slots) { emmc0 = drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots); - sdhci_attach_drive(&bmc->soc->emmc.slots[0], emmc0, true); + sdhci_attach_drive(&bmc->soc->emmc.slots[0], emmc0, true, boot_emmc); } if (!bmc->mmio_exec) { DeviceState *dev = ssi_get_cs(bmc->soc->fmc.spi, 0); BlockBackend *fmc0 = dev ? m25p80_get_blk(dev) : NULL; - if (fmc0) { + if (fmc0 && !boot_emmc) { uint64_t rom_size = memory_region_size(&bmc->soc->spi_boot); aspeed_install_boot_rom(bmc, fmc0, rom_size); } else if (emmc0) { From 01ea09eb7a6e8369a98fb360d2ed3bf1ecfc1753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:20 +0200 Subject: [PATCH 2274/2884] aspeed: Add boot-from-eMMC HW strapping bit to rainier-bmc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This value is taken from a running Rainier machine. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/arm/aspeed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index bc4ca17542..a8f5c14ae5 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -189,7 +189,7 @@ struct AspeedMachineState { #define TACOMA_BMC_HW_STRAP2 0x00000040 /* Rainier hardware value: (QEMU prototype) */ -#define RAINIER_BMC_HW_STRAP1 0x00422016 +#define RAINIER_BMC_HW_STRAP1 (0x00422016 | SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC) #define RAINIER_BMC_HW_STRAP2 0x80000848 /* Fuji hardware value */ From 621845a9e38215977432cff8c946b78f6820c6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:21 +0200 Subject: [PATCH 2275/2884] aspeed: Introduce a 'hw_strap1' machine attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To change default behavior of a machine and boot from eMMC, future changes will add a machine option to let the user configure the boot-from-eMMC HW strapping bit. Add a new machine attribute first. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/arm/aspeed.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a8f5c14ae5..9939559f6d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -46,6 +46,7 @@ struct AspeedMachineState { uint32_t uart_chosen; char *fmc_model; char *spi_model; + uint32_t hw_strap1; }; /* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */ @@ -393,7 +394,7 @@ static void aspeed_machine_init(MachineState *machine) } } - object_property_set_int(OBJECT(bmc->soc), "hw-strap1", amc->hw_strap1, + object_property_set_int(OBJECT(bmc->soc), "hw-strap1", bmc->hw_strap1, &error_abort); object_property_set_int(OBJECT(bmc->soc), "hw-strap2", amc->hw_strap2, &error_abort); @@ -1077,7 +1078,10 @@ static void aspeed_set_mmio_exec(Object *obj, bool value, Error **errp) static void aspeed_machine_instance_init(Object *obj) { + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(obj); + ASPEED_MACHINE(obj)->mmio_exec = false; + ASPEED_MACHINE(obj)->hw_strap1 = amc->hw_strap1; } static char *aspeed_get_fmc_model(Object *obj, Error **errp) From 056b779eaf10ab84e8ca9d02662a975f4de3d3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org> Date: Wed, 17 Jul 2024 08:30:22 +0200 Subject: [PATCH 2276/2884] aspeed: Introduce a 'boot-emmc' machine option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default behavior of some Aspeed machines is to boot from the eMMC device, like the rainier-bmc. Others like ast2600-evb could also boot from eMMC if the HW strapping boot-from-eMMC bit was set. Add a property to set or unset this bit. This is useful to test boot images. For now, only activate this property on the ast2600-evb and rainier-bmc machines for which eMMC images are available or can be built. Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Andrew Jeffery <andrew@codeconstruct.com.au> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/system/arm/aspeed.rst | 2 ++ hw/arm/aspeed.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index cd9559e3e2..6733ffd2b9 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -123,6 +123,8 @@ To boot the machine from the flash image, use an MTD drive : Options specific to Aspeed machines are : + * ``boot-emmc`` to set or unset boot from eMMC (AST2600). + * ``execute-in-place`` which emulates the boot from the CE0 flash device by using the FMC controller to load the instructions, and not simply from RAM. This takes a little longer. diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 9939559f6d..5cdef873a5 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1178,6 +1178,34 @@ static void aspeed_machine_class_init_cpus_defaults(MachineClass *mc) mc->valid_cpu_types = sc->valid_cpu_types; } +static bool aspeed_machine_ast2600_get_boot_from_emmc(Object *obj, Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + + return !!(bmc->hw_strap1 & SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC); +} + +static void aspeed_machine_ast2600_set_boot_from_emmc(Object *obj, bool value, + Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + + if (value) { + bmc->hw_strap1 |= SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC; + } else { + bmc->hw_strap1 &= ~SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC; + } +} + +static void aspeed_machine_ast2600_class_emmc_init(ObjectClass *oc) +{ + object_class_property_add_bool(oc, "boot-emmc", + aspeed_machine_ast2600_get_boot_from_emmc, + aspeed_machine_ast2600_set_boot_from_emmc); + object_class_property_set_description(oc, "boot-emmc", + "Set or unset boot from EMMC"); +} + static void aspeed_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1377,6 +1405,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) amc->i2c_init = ast2600_evb_i2c_init; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); + aspeed_machine_ast2600_class_emmc_init(oc); }; static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) @@ -1449,6 +1478,7 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) amc->i2c_init = rainier_bmc_i2c_init; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); + aspeed_machine_ast2600_class_emmc_init(oc); }; #define FUJI_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) From a82e7e9eca7396ca39b0eb07d52e58d406d15728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@redhat.com> Date: Wed, 17 Jul 2024 14:00:34 +0200 Subject: [PATCH 2277/2884] tests/avocado/machine_aspeed.py: Add eMMC boot tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The image was built using the process described in commit c8cb19876d3e ("hw/sd/sdcard: Support boot area in emmc image") with artefacts from the latest successful build of the IBM P10 BMC platform available on: https://jenkins.openbmc.org/job/ci-openbmc/distro=ubuntu,label=docker-builder,target=p10bmc/lastSuccessfulBuild/ Signed-off-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tests/avocado/machine_aspeed.py | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index f66ad38d35..29e6b388a9 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -439,3 +439,42 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): self.wait_for_console_pattern('nodistro.0 ast2700-default ttyS12') self.ssh_connect('root', '0penBmc', False) +class AST2x00MachineMMC(QemuSystemTest): + + timeout = 240 + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def test_arm_aspeed_emmc_boot(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:rainier-bmc + :avocado: tags=device:emmc + """ + + image_url = ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/' + 'mmc-p10bmc-20240617.qcow2') + image_hash = ('d523fb478d2b84d5adc5658d08502bc64b1486955683814f89c6137518acd90b') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.require_netdev('user') + + self.vm.set_console() + self.vm.add_args('-drive', + 'file=' + image_path + ',if=sd,id=sd2,index=2', + '-net', 'nic', '-net', 'user') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot SPL 2019.04') + self.wait_for_console_pattern('Trying to boot from MMC1') + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('eMMC 2nd Boot') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern('Booting Linux on physical CPU 0xf00') + self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7') + self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise') From 13b5ae94ed4d03d4992af867d0edb075651a4da9 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 18 Jul 2024 14:49:11 +0800 Subject: [PATCH 2278/2884] aspeed/adc: Add AST2700 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 and AST2600 ADC controllers are identical. Introduce ast2700 class and set 2 engines. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/adc/aspeed_adc.c | 16 ++++++++++++++++ include/hw/adc/aspeed_adc.h | 1 + 2 files changed, 17 insertions(+) diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index 68bdbc73b0..48328ef891 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -398,6 +398,15 @@ static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 2; } +static void aspeed_2700_adc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); + + dc->desc = "ASPEED 2700 ADC Controller"; + aac->nr_engines = 2; +} + static const TypeInfo aspeed_adc_info = { .name = TYPE_ASPEED_ADC, .parent = TYPE_SYS_BUS_DEVICE, @@ -430,6 +439,12 @@ static const TypeInfo aspeed_1030_adc_info = { .class_init = aspeed_1030_adc_class_init, /* No change since AST2600 */ }; +static const TypeInfo aspeed_2700_adc_info = { + .name = TYPE_ASPEED_2700_ADC, + .parent = TYPE_ASPEED_ADC, + .class_init = aspeed_2700_adc_class_init, +}; + static void aspeed_adc_register_types(void) { type_register_static(&aspeed_adc_engine_info); @@ -438,6 +453,7 @@ static void aspeed_adc_register_types(void) type_register_static(&aspeed_2500_adc_info); type_register_static(&aspeed_2600_adc_info); type_register_static(&aspeed_1030_adc_info); + type_register_static(&aspeed_2700_adc_info); } type_init(aspeed_adc_register_types); diff --git a/include/hw/adc/aspeed_adc.h b/include/hw/adc/aspeed_adc.h index ff1d06ea91..f502f197ac 100644 --- a/include/hw/adc/aspeed_adc.h +++ b/include/hw/adc/aspeed_adc.h @@ -18,6 +18,7 @@ #define TYPE_ASPEED_2500_ADC TYPE_ASPEED_ADC "-ast2500" #define TYPE_ASPEED_2600_ADC TYPE_ASPEED_ADC "-ast2600" #define TYPE_ASPEED_1030_ADC TYPE_ASPEED_ADC "-ast1030" +#define TYPE_ASPEED_2700_ADC TYPE_ASPEED_ADC "-ast2700" OBJECT_DECLARE_TYPE(AspeedADCState, AspeedADCClass, ASPEED_ADC) #define TYPE_ASPEED_ADC_ENGINE "aspeed.adc.engine" From 11bea810f7c1215e36824ed291a64e8f4e6f28df Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 18 Jul 2024 14:49:12 +0800 Subject: [PATCH 2279/2884] aspeed/soc: support ADC for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ADC model for AST2700 ADC support. The ADC controller registers base address is start at 0x14C0_0000 and its address space is 0x1000. The ADC controller interrupt is connected to GICINT130_INTC group at bit 16. The GIC IRQ is 130. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/arm/aspeed_ast27x0.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index a9fb0d4b88..4257b5e8af 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -60,6 +60,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_SLIIO] = 0x14C1E000, [ASPEED_GIC_DIST] = 0x12200000, [ASPEED_GIC_REDIST] = 0x12280000, + [ASPEED_DEV_ADC] = 0x14C00000, }; #define AST2700_MAX_IRQ 288 @@ -344,6 +345,9 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "sli", &s->sli, TYPE_ASPEED_2700_SLI); object_initialize_child(obj, "sliio", &s->sliio, TYPE_ASPEED_2700_SLIIO); object_initialize_child(obj, "intc", &a->intc, TYPE_ASPEED_2700_INTC); + + snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); + object_initialize_child(obj, "adc", &s->adc, typename); } /* @@ -601,6 +605,14 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sliio), 0, sc->memmap[ASPEED_DEV_SLIIO]); + /* ADC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); From f2202be27852eeb18d05ee6cebaba6f09ea74e55 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 18 Jul 2024 14:49:13 +0800 Subject: [PATCH 2280/2884] hw/i2c/aspeed: support to set the different memory size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the datasheet of ASPEED SOCs, an I2C controller owns 8KB of register space for AST2700, owns 4KB of register space for AST2600, AST2500 and AST2400, and owns 64KB of register space for AST1030. It set the memory region size 4KB by default and it does not compatible register space for AST2700. Introduce a new class attribute to set the I2C controller memory size for different ASPEED SOCs. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/i2c/aspeed_i2c.c | 6 +++++- include/hw/i2c/aspeed_i2c.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index b43afd250d..7d5a53c4c0 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -1014,7 +1014,7 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, - "aspeed.i2c", 0x1000); + "aspeed.i2c", aic->mem_size); sysbus_init_mmio(sbd, &s->iomem); for (i = 0; i < aic->num_busses; i++) { @@ -1286,6 +1286,7 @@ static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) aic->pool_size = 0x800; aic->pool_base = 0x800; aic->bus_pool_base = aspeed_2400_i2c_bus_pool_base; + aic->mem_size = 0x1000; } static const TypeInfo aspeed_2400_i2c_info = { @@ -1320,6 +1321,7 @@ static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; aic->check_sram = true; aic->has_dma = true; + aic->mem_size = 0x1000; } static const TypeInfo aspeed_2500_i2c_info = { @@ -1353,6 +1355,7 @@ static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) aic->pool_base = 0xC00; aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; aic->has_dma = true; + aic->mem_size = 0x1000; } static const TypeInfo aspeed_2600_i2c_info = { @@ -1376,6 +1379,7 @@ static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) aic->pool_base = 0xC00; aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; aic->has_dma = true; + aic->mem_size = 0x10000; } static const TypeInfo aspeed_1030_i2c_info = { diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index a064479e59..065b636d29 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -283,7 +283,7 @@ struct AspeedI2CClass { uint8_t *(*bus_pool_base)(AspeedI2CBus *); bool check_sram; bool has_dma; - + uint64_t mem_size; }; static inline bool aspeed_i2c_is_new_mode(AspeedI2CState *s) From 5d337540c417997b589d71020497e0c127d1c4e1 Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 18 Jul 2024 14:49:15 +0800 Subject: [PATCH 2281/2884] hw/i2c/aspeed: rename the I2C class pool attribute to share_pool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the datasheet of ASPEED SOCs, each I2C bus has their own pool buffer since AST2500. Only AST2400 utilized a pool buffer share to all I2C bus. And firmware required to set the offset of pool buffer by writing "Function Control Register(I2CD 00)" To make this model more readable, will change to introduce a new bus pool buffer attribute in AspeedI2Cbus. So, it does not need to calculate the pool buffer offset for different I2C bus. This patch rename the I2C class pool attribute to share_pool. It make user more understand share pool and bus pool are different. Incrementing the version of aspeed_i2c_vmstate to 3. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/i2c/aspeed_i2c.c | 39 ++++++++++++++++++++----------------- include/hw/i2c/aspeed_i2c.h | 4 ++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 7d5a53c4c0..b52a99896c 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -906,7 +906,7 @@ static const MemoryRegionOps aspeed_i2c_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, +static uint64_t aspeed_i2c_share_pool_read(void *opaque, hwaddr offset, unsigned size) { AspeedI2CState *s = opaque; @@ -914,26 +914,26 @@ static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, int i; for (i = 0; i < size; i++) { - ret |= (uint64_t) s->pool[offset + i] << (8 * i); + ret |= (uint64_t) s->share_pool[offset + i] << (8 * i); } return ret; } -static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, +static void aspeed_i2c_share_pool_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { AspeedI2CState *s = opaque; int i; for (i = 0; i < size; i++) { - s->pool[offset + i] = (value >> (8 * i)) & 0xFF; + s->share_pool[offset + i] = (value >> (8 * i)) & 0xFF; } } -static const MemoryRegionOps aspeed_i2c_pool_ops = { - .read = aspeed_i2c_pool_read, - .write = aspeed_i2c_pool_write, +static const MemoryRegionOps aspeed_i2c_share_pool_ops = { + .read = aspeed_i2c_share_pool_read, + .write = aspeed_i2c_share_pool_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, @@ -953,14 +953,15 @@ static const VMStateDescription aspeed_i2c_bus_vmstate = { static const VMStateDescription aspeed_i2c_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_UINT32(intr_status, AspeedI2CState), VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, AspeedI2CBus), - VMSTATE_UINT8_ARRAY(pool, AspeedI2CState, ASPEED_I2C_MAX_POOL_SIZE), + VMSTATE_UINT8_ARRAY(share_pool, AspeedI2CState, + ASPEED_I2C_SHARE_POOL_SIZE), VMSTATE_END_OF_LIST() } }; @@ -995,7 +996,7 @@ static void aspeed_i2c_instance_init(Object *obj) * 0x140 ... 0x17F: Device 5 * 0x180 ... 0x1BF: Device 6 * 0x1C0 ... 0x1FF: Device 7 - * 0x200 ... 0x2FF: Buffer Pool (unused in linux driver) + * 0x200 ... 0x2FF: Buffer Pool (AST2500 unused in linux driver) * 0x300 ... 0x33F: Device 8 * 0x340 ... 0x37F: Device 9 * 0x380 ... 0x3BF: Device 10 @@ -1003,7 +1004,7 @@ static void aspeed_i2c_instance_init(Object *obj) * 0x400 ... 0x43F: Device 12 * 0x440 ... 0x47F: Device 13 * 0x480 ... 0x4BF: Device 14 - * 0x800 ... 0xFFF: Buffer Pool (unused in linux driver) + * 0x800 ... 0xFFF: Buffer Pool (AST2400 unused in linux driver) */ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) { @@ -1037,8 +1038,9 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) &s->busses[i].mr); } - memory_region_init_io(&s->pool_iomem, OBJECT(s), &aspeed_i2c_pool_ops, s, - "aspeed.i2c-pool", aic->pool_size); + memory_region_init_io(&s->pool_iomem, OBJECT(s), + &aspeed_i2c_share_pool_ops, s, + "aspeed.i2c-share-pool", aic->pool_size); memory_region_add_subregion(&s->iomem, aic->pool_base, &s->pool_iomem); if (aic->has_dma) { @@ -1266,8 +1268,9 @@ static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) static uint8_t *aspeed_2400_i2c_bus_pool_base(AspeedI2CBus *bus) { uint8_t *pool_page = - &bus->controller->pool[ARRAY_FIELD_EX32(bus->regs, I2CD_FUN_CTRL, - POOL_PAGE_SEL) * 0x100]; + &bus->controller->share_pool[ARRAY_FIELD_EX32(bus->regs, + I2CD_FUN_CTRL, + POOL_PAGE_SEL) * 0x100]; return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; } @@ -1302,7 +1305,7 @@ static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus) static uint8_t *aspeed_2500_i2c_bus_pool_base(AspeedI2CBus *bus) { - return &bus->controller->pool[bus->id * 0x10]; + return &bus->controller->share_pool[bus->id * 0x10]; } static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) @@ -1337,7 +1340,7 @@ static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) static uint8_t *aspeed_2600_i2c_bus_pool_base(AspeedI2CBus *bus) { - return &bus->controller->pool[bus->id * 0x20]; + return &bus->controller->share_pool[bus->id * 0x20]; } static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 065b636d29..fad5e9259a 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -34,7 +34,7 @@ OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C) #define ASPEED_I2C_NR_BUSSES 16 -#define ASPEED_I2C_MAX_POOL_SIZE 0x800 +#define ASPEED_I2C_SHARE_POOL_SIZE 0x800 #define ASPEED_I2C_OLD_NUM_REG 11 #define ASPEED_I2C_NEW_NUM_REG 22 @@ -257,7 +257,7 @@ struct AspeedI2CState { uint32_t ctrl_global; uint32_t new_clk_divider; MemoryRegion pool_iomem; - uint8_t pool[ASPEED_I2C_MAX_POOL_SIZE]; + uint8_t share_pool[ASPEED_I2C_SHARE_POOL_SIZE]; AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES]; MemoryRegion *dram_mr; From 4db1c16441923fc152142ae4bcc1cba23064cb8b Mon Sep 17 00:00:00 2001 From: Jamin Lin <jamin_lin@aspeedtech.com> Date: Thu, 18 Jul 2024 14:49:24 +0800 Subject: [PATCH 2282/2884] aspeed: fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl Test command: ./scripts/checkpatch.pl --no-tree -f hw/arm/aspeed.c Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> --- hw/arm/aspeed.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 5cdef873a5..fd5603f7aa 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -266,7 +266,8 @@ static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, g_autofree void *storage = NULL; int64_t size; - /* The block backend size should have already been 'validated' by + /* + * The block backend size should have already been 'validated' by * the creation of the m25p80 object. */ size = blk_getlength(blk); @@ -476,8 +477,10 @@ static void palmetto_bmc_i2c_init(AspeedMachineState *bmc) DeviceState *dev; uint8_t *eeprom_buf = g_malloc0(32 * 1024); - /* The palmetto platform expects a ds3231 RTC but a ds1338 is - * enough to provide basic RTC features. Alarms will be missing */ + /* + * The palmetto platform expects a ds3231 RTC but a ds1338 is + * enough to provide basic RTC features. Alarms will be missing + */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 0), "ds1338", 0x68); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 0), 0x50, @@ -568,8 +571,10 @@ static void romulus_bmc_i2c_init(AspeedMachineState *bmc) { AspeedSoCState *soc = bmc->soc; - /* The romulus board expects Epson RX8900 I2C RTC but a ds1338 is - * good enough */ + /* + * The romulus board expects Epson RX8900 I2C RTC but a ds1338 is + * good enough + */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "ds1338", 0x32); } @@ -677,8 +682,10 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), TYPE_TMP105, 0x4a); - /* The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is - * good enough */ + /* + * The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is + * good enough + */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "ds1338", 0x32); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 11), 0x51, From 89cf6574bc4bcdb99894e9401ecea0063d8212ce Mon Sep 17 00:00:00 2001 From: Zheyu Ma <zheyuma97@gmail.com> Date: Tue, 2 Jul 2024 23:18:35 +0200 Subject: [PATCH 2283/2884] hw/virtio/virtio-crypto: Fix op_code assignment in virtio_crypto_create_asym_session Currently, if the function fails during the key_len check, the op_code does not have a proper value, causing virtio_crypto_free_create_session_req not to free the memory correctly, leading to a memory leak. By setting the op_code before performing any checks, we ensure that virtio_crypto_free_create_session_req has the correct context to perform cleanup operations properly, thus preventing memory leaks. ASAN log: ==3055068==ERROR: LeakSanitizer: detected memory leaks Direct leak of 512 byte(s) in 1 object(s) allocated from: #0 0x5586a75e6ddd in malloc llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3 #1 0x7fb6b63b6738 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x5e738) #2 0x5586a864bbde in virtio_crypto_handle_ctrl hw/virtio/virtio-crypto.c:407:19 #3 0x5586a94fc84c in virtio_queue_notify_vq hw/virtio/virtio.c:2277:9 #4 0x5586a94fc0a2 in virtio_queue_host_notifier_read hw/virtio/virtio.c:3641:9 Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Message-Id: <20240702211835.3064505-1-zheyuma97@gmail.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index bbe8aa4b99..5034768bff 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -205,6 +205,7 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, int queue_index; uint32_t algo, keytype, keylen; + sreq->info.op_code = opcode; algo = ldl_le_p(&sess_req->para.algo); keytype = ldl_le_p(&sess_req->para.keytype); keylen = ldl_le_p(&sess_req->para.keylen); @@ -224,7 +225,6 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, iov_discard_front(&iov, &out_num, keylen); } - sreq->info.op_code = opcode; asym_info = &sreq->info.u.asym_sess_info; asym_info->algo = algo; asym_info->keytype = keytype; From 7967b7e0b17cb5e700ca040e0b68ba8fb0035c0d Mon Sep 17 00:00:00 2001 From: Stefano Garzarella <sgarzare@redhat.com> Date: Thu, 4 Jul 2024 10:13:36 +0200 Subject: [PATCH 2284/2884] MAINTAINERS: add Stefano Garzarella as vhost/vhost-user reviewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have recently been working on supporting vhost-user on any POSIX, so I want to help maintain it. Cc: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> Message-Id: <20240704081336.21208-1-sgarzare@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7d9811458c..b7e9ced3e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2209,6 +2209,7 @@ F: docs/devel/vfio-iommufd.rst vhost M: Michael S. Tsirkin <mst@redhat.com> +R: Stefano Garzarella <sgarzare@redhat.com> S: Supported F: hw/*/*vhost* F: docs/interop/vhost-user.json From b7dbfe4f47884e67a309c206b3eadec339a0d3b6 Mon Sep 17 00:00:00 2001 From: Fan Ni <fan.ni@samsung.com> Date: Fri, 5 Jul 2024 12:39:55 +0100 Subject: [PATCH 2285/2884] hw/cxl/cxl-mailbox-utils: remove unneeded mailbox output payload space zeroing The whole mailbox output payload space is already zeroed after copying out the input payload, which happens before processing the specific mailbox command: https://elixir.bootlin.com/qemu/v8.2.1/source/hw/cxl/cxl-device-utils.c#L204 Signed-off-by: Fan Ni <fan.ni@samsung.com> Link: https://lore.kernel.org/r/20240221221824.1092966-1-nifan.cxl@gmail.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705113956.941732-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 74eeb6fde7..facec42dc8 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -235,7 +235,6 @@ static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, log_type = payload_in[0]; pl = (CXLGetEventPayload *)payload_out; - memset(pl, 0, sizeof(*pl)); max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / CXL_EVENT_RECORD_SIZE; @@ -273,7 +272,6 @@ static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, CXLEventLog *log; policy = (CXLEventInterruptPolicy *)payload_out; - memset(policy, 0, sizeof(*policy)); log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; if (log->irq_enabled) { @@ -372,7 +370,6 @@ static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd, QEMU_BUILD_BUG_ON(sizeof(*is_identify) != 18); is_identify = (void *)payload_out; - memset(is_identify, 0, sizeof(*is_identify)); is_identify->pcie_vid = class->vendor_id; is_identify->pcie_did = class->device_id; if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { @@ -606,7 +603,6 @@ static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd, QEMU_BUILD_BUG_ON(sizeof(*bg_op_status) != 8); bg_op_status = (void *)payload_out; - memset(bg_op_status, 0, sizeof(*bg_op_status)); bg_op_status->status = cci->bg.complete_pct << 1; if (cci->bg.runtime > 0) { bg_op_status->status |= 1U << 0; @@ -647,7 +643,6 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, } fw_info = (void *)payload_out; - memset(fw_info, 0, sizeof(*fw_info)); fw_info->slots_supported = 2; fw_info->slot_info = BIT(0) | BIT(3); @@ -805,7 +800,6 @@ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, } id = (void *)payload_out; - memset(id, 0, sizeof(*id)); snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); @@ -1095,7 +1089,6 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); - memset(out, 0, out_pl_len); QLIST_FOREACH(ent, poison_list, node) { uint64_t start, stop; From 9de2049c95e1a94e27430e71a9d9ace07ec8eda6 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron <Jonathan.Cameron@huawei.com> Date: Fri, 5 Jul 2024 12:39:56 +0100 Subject: [PATCH 2286/2884] hw/cxl: Check for multiple mappings of memory backends. Similar protection to that provided for -numa memdev=x to make sure that memory used to back a type3 device is not also mapped as normal RAM, or for multiple type3 devices. This is an easy footgun to remove and seems multiple people have run into it. Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705113956.941732-4-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/mem/cxl_type3.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 35ac59883a..e7fbbb4d51 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -737,6 +737,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) error_setg(errp, "volatile memdev must have backing device"); return false; } + if (host_memory_backend_is_mapped(ct3d->hostvmem)) { + error_setg(errp, "memory backend %s can't be used multiple times.", + object_get_canonical_path_component(OBJECT(ct3d->hostvmem))); + return false; + } memory_region_set_nonvolatile(vmr, false); memory_region_set_enabled(vmr, true); host_memory_backend_set_mapped(ct3d->hostvmem, true); @@ -760,6 +765,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) error_setg(errp, "persistent memdev must have backing device"); return false; } + if (host_memory_backend_is_mapped(ct3d->hostpmem)) { + error_setg(errp, "memory backend %s can't be used multiple times.", + object_get_canonical_path_component(OBJECT(ct3d->hostpmem))); + return false; + } memory_region_set_nonvolatile(pmr, true); memory_region_set_enabled(pmr, true); host_memory_backend_set_mapped(ct3d->hostpmem, true); @@ -790,6 +800,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) return false; } + if (host_memory_backend_is_mapped(ct3d->dc.host_dc)) { + error_setg(errp, "memory backend %s can't be used multiple times.", + object_get_canonical_path_component(OBJECT(ct3d->dc.host_dc))); + return false; + } /* * Set DC regions as volatile for now, non-volatile support can * be added in the future if needed. From a207d5f87d66f7933b50677e047498fc4af63e1f Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Fri, 5 Jul 2024 12:39:54 +0100 Subject: [PATCH 2287/2884] hw/cxl/cxl-host: Fix segmentation fault when getting cxl-fmw property QEMU crashes (Segmentation fault) when getting cxl-fmw property via qmp: (QEMU) qom-get path=machine property=cxl-fmw This issue is caused by accessing wrong callback (opaque) type in machine_get_cfmw(). cxl_machine_init() sets the callback as `CXLState *` type but machine_get_cfmw() treats the callback as `CXLFixedMemoryWindowOptionsList **`. Fix this error by casting opaque to `CXLState *` type in machine_get_cfmw(). Fixes: 03b39fcf64bc ("hw/cxl: Make the CXL fixed memory window setup a machine parameter.") Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Li Zhijian <lizhijian@fujitsu.com> Reviewed-by: Xingtao Yao <yaoxt.fnst@fujitsu.com> Link: https://lore.kernel.org/r/20240704093404.1848132-1-zhao1.liu@linux.intel.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705113956.941732-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index c5f5fcfd64..e9f2543c43 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -315,7 +315,8 @@ static void machine_set_cxl(Object *obj, Visitor *v, const char *name, static void machine_get_cfmw(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - CXLFixedMemoryWindowOptionsList **list = opaque; + CXLState *state = opaque; + CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list; visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp); } From d61cc5b6a8d373376a8bf5ec29cfc3267a29efbe Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso <dave@stgolabs.net> Date: Fri, 5 Jul 2024 13:06:40 +0100 Subject: [PATCH 2288/2884] hw/cxl: Add get scan media capabilities cmd support Use simple heuristics to determine the cost of scanning any given chunk, assuming cost is equal across the whole device, without differentiating between volatile or persistent partitions. This is aligned to the fact that these constraints are not enforced in respective poison query commands. Signed-off-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://lore.kernel.org/r/20230908073152.4386-3-dave@stgolabs.net Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705120643.959422-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 211 +++++++++++++++++++++++++++++++++++- hw/mem/cxl_type3.c | 22 ++-- include/hw/cxl/cxl_device.h | 9 ++ 3 files changed, 233 insertions(+), 9 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index facec42dc8..8ae9c6a699 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -83,6 +83,8 @@ enum { #define GET_POISON_LIST 0x0 #define INJECT_POISON 0x1 #define CLEAR_POISON 0x2 + #define GET_SCAN_MEDIA_CAPABILITIES 0x3 + #define SCAN_MEDIA 0x4 DCD_CONFIG = 0x48, #define GET_DC_CONFIG 0x0 #define GET_DYN_CAP_EXT_LIST 0x1 @@ -1110,6 +1112,10 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, out->flags = (1 << 1); stq_le_p(&out->overflow_timestamp, ct3d->poison_list_overflow_ts); } + if (scan_media_running(cci)) { + out->flags |= (1 << 2); + } + stw_le_p(&out->count, record_count); *len_out = out_pl_len; return CXL_MBOX_SUCCESS; @@ -1139,6 +1145,16 @@ static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } } + /* + * Freeze the list if there is an on-going scan media operation. + */ + if (scan_media_running(cci)) { + /* + * XXX: Spec is ambiguous - is this case considered + * a successful return despite not adding to the list? + */ + goto success; + } if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { return CXL_MBOX_INJECT_POISON_LIMIT; @@ -1154,6 +1170,7 @@ static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, */ QLIST_INSERT_HEAD(poison_list, p, node); ct3d->poison_list_cnt++; +success: *len_out = 0; return CXL_MBOX_SUCCESS; @@ -1193,6 +1210,17 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, } } + /* + * Freeze the list if there is an on-going scan media operation. + */ + if (scan_media_running(cci)) { + /* + * XXX: Spec is ambiguous - is this case considered + * a successful return despite not removing from the list? + */ + goto success; + } + QLIST_FOREACH(ent, poison_list, node) { /* * Test for contained in entry. Simpler than general case @@ -1203,7 +1231,7 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, } } if (!ent) { - return CXL_MBOX_SUCCESS; + goto success; } QLIST_REMOVE(ent, node); @@ -1240,11 +1268,180 @@ static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, } /* Any fragments have been added, free original entry */ g_free(ent); +success: *len_out = 0; return CXL_MBOX_SUCCESS; } +/* + * CXL r3.1 section 8.2.9.9.4.4: Get Scan Media Capabilities + */ +static CXLRetCode +cmd_media_get_scan_media_capabilities(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct get_scan_media_capabilities_pl { + uint64_t pa; + uint64_t length; + } QEMU_PACKED; + + struct get_scan_media_capabilities_out_pl { + uint32_t estimated_runtime_ms; + }; + + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + struct get_scan_media_capabilities_pl *in = (void *)payload_in; + struct get_scan_media_capabilities_out_pl *out = (void *)payload_out; + uint64_t query_start; + uint64_t query_length; + + query_start = ldq_le_p(&in->pa); + /* 64 byte alignment required */ + if (query_start & 0x3f) { + return CXL_MBOX_INVALID_INPUT; + } + query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; + + if (query_start + query_length > cxl_dstate->static_mem_size) { + return CXL_MBOX_INVALID_PA; + } + + /* + * Just use 400 nanosecond access/read latency + 100 ns for + * the cost of updating the poison list. For small enough + * chunks return at least 1 ms. + */ + stl_le_p(&out->estimated_runtime_ms, + MAX(1, query_length * (0.0005L / 64))); + + *len_out = sizeof(*out); + return CXL_MBOX_SUCCESS; +} + +static void __do_scan_media(CXLType3Dev *ct3d) +{ + CXLPoison *ent; + unsigned int results_cnt = 0; + + QLIST_FOREACH(ent, &ct3d->scan_media_results, node) { + results_cnt++; + } + + /* only scan media may clear the overflow */ + if (ct3d->poison_list_overflowed && + ct3d->poison_list_cnt == results_cnt) { + cxl_clear_poison_list_overflowed(ct3d); + } +} + +/* + * CXL r3.1 section 8.2.9.9.4.5: Scan Media + */ +static CXLRetCode cmd_media_scan_media(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct scan_media_pl { + uint64_t pa; + uint64_t length; + uint8_t flags; + } QEMU_PACKED; + + struct scan_media_pl *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + uint64_t query_start; + uint64_t query_length; + CXLPoison *ent, *next; + + query_start = ldq_le_p(&in->pa); + /* 64 byte alignment required */ + if (query_start & 0x3f) { + return CXL_MBOX_INVALID_INPUT; + } + query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; + + if (query_start + query_length > cxl_dstate->static_mem_size) { + return CXL_MBOX_INVALID_PA; + } + if (ct3d->dc.num_regions && query_start + query_length >= + cxl_dstate->static_mem_size + ct3d->dc.total_capacity) { + return CXL_MBOX_INVALID_PA; + } + + if (in->flags == 0) { /* TODO */ + qemu_log_mask(LOG_UNIMP, + "Scan Media Event Log is unsupported\n"); + } + + /* any previous results are discarded upon a new Scan Media */ + QLIST_FOREACH_SAFE(ent, &ct3d->scan_media_results, node, next) { + QLIST_REMOVE(ent, node); + g_free(ent); + } + + /* kill the poison list - it will be recreated */ + if (ct3d->poison_list_overflowed) { + QLIST_FOREACH_SAFE(ent, &ct3d->poison_list, node, next) { + QLIST_REMOVE(ent, node); + g_free(ent); + ct3d->poison_list_cnt--; + } + } + + /* + * Scan the backup list and move corresponding entries + * into the results list, updating the poison list + * when possible. + */ + QLIST_FOREACH_SAFE(ent, &ct3d->poison_list_bkp, node, next) { + CXLPoison *res; + + if (ent->start >= query_start + query_length || + ent->start + ent->length <= query_start) { + continue; + } + + /* + * If a Get Poison List cmd comes in while this + * scan is being done, it will see the new complete + * list, while setting the respective flag. + */ + if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) { + CXLPoison *p = g_new0(CXLPoison, 1); + + p->start = ent->start; + p->length = ent->length; + p->type = ent->type; + QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); + ct3d->poison_list_cnt++; + } + + res = g_new0(CXLPoison, 1); + res->start = ent->start; + res->length = ent->length; + res->type = ent->type; + QLIST_INSERT_HEAD(&ct3d->scan_media_results, res, node); + + QLIST_REMOVE(ent, node); + g_free(ent); + } + + cci->bg.runtime = MAX(1, query_length * (0.0005L / 64)); + *len_out = 0; + + return CXL_MBOX_BG_STARTED; +} + /* * CXL r3.1 section 8.2.9.9.9.1: Get Dynamic Capacity Configuration * (Opcode: 4800h) @@ -1857,6 +2054,11 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_media_inject_poison, 8, 0 }, [MEDIA_AND_POISON][CLEAR_POISON] = { "MEDIA_AND_POISON_CLEAR_POISON", cmd_media_clear_poison, 72, 0 }, + [MEDIA_AND_POISON][GET_SCAN_MEDIA_CAPABILITIES] = { + "MEDIA_AND_POISON_GET_SCAN_MEDIA_CAPABILITIES", + cmd_media_get_scan_media_capabilities, 16, 0 }, + [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA", + cmd_media_scan_media, 17, BACKGROUND_OPERATION }, }; static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { @@ -1988,8 +2190,13 @@ static void bg_timercb(void *opaque) cxl_dev_enable_media(&ct3d->cxl_dstate); } break; - case 0x4304: /* TODO: scan media */ + case 0x4304: /* scan media */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + + __do_scan_media(ct3d); break; + } default: __builtin_unreachable(); break; diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index e7fbbb4d51..e763b9bff0 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1319,6 +1319,12 @@ void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d) cxl_device_get_timestamp(&ct3d->cxl_dstate); } +void cxl_clear_poison_list_overflowed(CXLType3Dev *ct3d) +{ + ct3d->poison_list_overflowed = false; + ct3d->poison_list_overflow_ts = 0; +} + void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, Error **errp) { @@ -1355,19 +1361,21 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, } } - if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { - cxl_set_poison_list_overflowed(ct3d); - return; - } - p = g_new0(CXLPoison, 1); p->length = length; p->start = start; /* Different from injected via the mbox */ p->type = CXL_POISON_TYPE_INTERNAL; - QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); - ct3d->poison_list_cnt++; + if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) { + QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); + ct3d->poison_list_cnt++; + } else { + if (!ct3d->poison_list_overflowed) { + cxl_set_poison_list_overflowed(ct3d); + } + QLIST_INSERT_HEAD(&ct3d->poison_list_bkp, p, node); + } } /* For uncorrectable errors include support for multiple header recording */ diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 0a4fcb2800..b5beb7f90e 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -397,6 +397,11 @@ static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val) #define cxl_dev_enable_media(cxlds) \ do { __toggle_media((cxlds), 0x1); } while (0) +static inline bool scan_media_running(CXLCCI *cci) +{ + return !!cci->bg.runtime && cci->bg.opcode == 0x4304; +} + static inline bool sanitize_running(CXLCCI *cci) { return !!cci->bg.runtime && cci->bg.opcode == 0x4400; @@ -491,6 +496,9 @@ struct CXLType3Dev { unsigned int poison_list_cnt; bool poison_list_overflowed; uint64_t poison_list_overflow_ts; + /* Poison Injection - backup */ + CXLPoisonList poison_list_bkp; + CXLPoisonList scan_media_results; struct dynamic_capacity { HostMemoryBackend *host_dc; @@ -558,6 +566,7 @@ CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, void cxl_event_irq_assert(CXLType3Dev *ct3d); void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d); +void cxl_clear_poison_list_overflowed(CXLType3Dev *ct3d); CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); From 75b800dd3bd8042503ddd4e8a4169f34349325e2 Mon Sep 17 00:00:00 2001 From: Hyeonggon Yoo <42.hyeyoo@gmail.com> Date: Fri, 5 Jul 2024 13:06:41 +0100 Subject: [PATCH 2289/2884] hw/cxl/mbox: replace sanitize_running() with cxl_dev_media_disabled() The spec states that reads/writes should have no effect and a part of commands should be ignored when the media is disabled, not when the sanitize command is running. Introduce cxl_dev_media_disabled() to check if the media is disabled and replace sanitize_running() with it. Make sure that the media has been correctly disabled during sanitation by adding an assert to __toggle_media(). Now, enabling when already enabled or vice versa results in an assert() failure. Suggested-by: Davidlohr Bueso <dave@stgolabs.net> Signed-off-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> Link: https://lore.kernel.org/r/20231222090051.3265307-4-42.hyeyoo@gmail.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705120643.959422-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 29 +++++++++++++++++------------ hw/mem/cxl_type3.c | 4 ++-- include/hw/cxl/cxl_device.h | 10 +++++----- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 8ae9c6a699..522d9aa589 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -2108,6 +2108,7 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, int ret; const struct cxl_cmd *cxl_cmd; opcode_handler h; + CXLDeviceState *cxl_dstate; *len_out = 0; cxl_cmd = &cci->cxl_cmd_set[set][cmd]; @@ -2128,18 +2129,22 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, return CXL_MBOX_BUSY; } - /* forbid any selected commands while overwriting */ - if (sanitize_running(cci)) { - if (h == cmd_events_get_records || - h == cmd_ccls_get_partition_info || - h == cmd_ccls_set_lsa || - h == cmd_ccls_get_lsa || - h == cmd_logs_get_log || - h == cmd_media_get_poison_list || - h == cmd_media_inject_poison || - h == cmd_media_clear_poison || - h == cmd_sanitize_overwrite) { - return CXL_MBOX_MEDIA_DISABLED; + /* forbid any selected commands while the media is disabled */ + if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + + if (cxl_dev_media_disabled(cxl_dstate)) { + if (h == cmd_events_get_records || + h == cmd_ccls_get_partition_info || + h == cmd_ccls_set_lsa || + h == cmd_ccls_get_lsa || + h == cmd_logs_get_log || + h == cmd_media_get_poison_list || + h == cmd_media_inject_poison || + h == cmd_media_clear_poison || + h == cmd_sanitize_overwrite) { + return CXL_MBOX_MEDIA_DISABLED; + } } } diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index e763b9bff0..c7910687ae 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1142,7 +1142,7 @@ MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, return MEMTX_ERROR; } - if (sanitize_running(&ct3d->cci)) { + if (cxl_dev_media_disabled(&ct3d->cxl_dstate)) { qemu_guest_getrandom_nofail(data, size); return MEMTX_OK; } @@ -1164,7 +1164,7 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data, return MEMTX_ERROR; } - if (sanitize_running(&ct3d->cci)) { + if (cxl_dev_media_disabled(&ct3d->cxl_dstate)) { return MEMTX_OK; } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index b5beb7f90e..42a622197e 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -397,16 +397,16 @@ static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val) #define cxl_dev_enable_media(cxlds) \ do { __toggle_media((cxlds), 0x1); } while (0) +static inline bool cxl_dev_media_disabled(CXLDeviceState *cxl_dstate) +{ + uint64_t dev_status_reg = cxl_dstate->mbox_reg_state64[R_CXL_MEM_DEV_STS]; + return FIELD_EX64(dev_status_reg, CXL_MEM_DEV_STS, MEDIA_STATUS) == 0x3; +} static inline bool scan_media_running(CXLCCI *cci) { return !!cci->bg.runtime && cci->bg.opcode == 0x4304; } -static inline bool sanitize_running(CXLCCI *cci) -{ - return !!cci->bg.runtime && cci->bg.opcode == 0x4400; -} - typedef struct CXLError { QTAILQ_ENTRY(CXLError) node; int type; /* Error code as per FE definition */ From 7d65874ba0ea8cdb2a5ac51c397d721d7d49d828 Mon Sep 17 00:00:00 2001 From: Hyeonggon Yoo <42.hyeyoo@gmail.com> Date: Fri, 5 Jul 2024 13:06:42 +0100 Subject: [PATCH 2290/2884] hw/cxl/events: discard all event records during sanitation Per CXL r3.1 Section 8.2.9.9.5.1: Sanitize (Opcode 4400h), the sanitize command should delete all event logs. Introduce cxl_discard_all_event_logs() and call this in __do_sanitization(). Signed-off-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://lore.kernel.org/r/20231222090051.3265307-5-42.hyeyoo@gmail.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705120643.959422-4-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-events.c | 13 +++++++++++++ hw/cxl/cxl-mailbox-utils.c | 1 + include/hw/cxl/cxl_device.h | 1 + 3 files changed, 15 insertions(+) diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index d397718b1b..12dee2e467 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -139,6 +139,19 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, return cxl_event_count(log) == 1; } +void cxl_discard_all_event_records(CXLDeviceState *cxlds) +{ + CXLEventLogType log_type; + CXLEventLog *log; + + for (log_type = 0; log_type < CXL_EVENT_TYPE_MAX; log_type++) { + log = &cxlds->event_logs[log_type]; + while (!cxl_event_empty(log)) { + cxl_event_delete_head(cxlds, log_type, log); + } + } +} + CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, uint8_t log_type, int max_recs, size_t *len) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 522d9aa589..3c9600c39c 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -949,6 +949,7 @@ static void __do_sanitization(CXLType3Dev *ct3d) memset(lsa, 0, memory_region_size(mr)); } } + cxl_discard_all_event_records(&ct3d->cxl_dstate); } /* diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 42a622197e..0509d961c3 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -562,6 +562,7 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, size_t *len); CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPayload *pl); +void cxl_discard_all_event_records(CXLDeviceState *cxlds); void cxl_event_irq_assert(CXLType3Dev *ct3d); From 89b5cfcc31e655a40919698a7f95a9208d6f12a3 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso <dave@stgolabs.net> Date: Fri, 5 Jul 2024 13:06:43 +0100 Subject: [PATCH 2291/2884] hw/cxl: Add get scan media results cmd support Iterate over the list keeping the output payload size into account, returning the results from a previous scan media operation. The scan media operation does not fail prematurely due to device being out of storage, so this implementation does not deal with the retry/restart functionality. Signed-off-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://lore.kernel.org/r/20230908073152.4386-5-dave@stgolabs.net Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705120643.959422-5-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 85 +++++++++++++++++++++++++++++++++++++ include/hw/cxl/cxl_device.h | 1 + 2 files changed, 86 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 3c9600c39c..82120a6e7b 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -85,6 +85,7 @@ enum { #define CLEAR_POISON 0x2 #define GET_SCAN_MEDIA_CAPABILITIES 0x3 #define SCAN_MEDIA 0x4 + #define GET_SCAN_MEDIA_RESULTS 0x5 DCD_CONFIG = 0x48, #define GET_DC_CONFIG 0x0 #define GET_DYN_CAP_EXT_LIST 0x1 @@ -1339,6 +1340,8 @@ static void __do_scan_media(CXLType3Dev *ct3d) ct3d->poison_list_cnt == results_cnt) { cxl_clear_poison_list_overflowed(ct3d); } + /* scan media has run since last conventional reset */ + ct3d->scan_media_hasrun = true; } /* @@ -1443,6 +1446,85 @@ static CXLRetCode cmd_media_scan_media(const struct cxl_cmd *cmd, return CXL_MBOX_BG_STARTED; } +/* + * CXL r3.1 section 8.2.9.9.4.6: Get Scan Media Results + */ +static CXLRetCode cmd_media_get_scan_media_results(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct get_scan_media_results_out_pl { + uint64_t dpa_restart; + uint64_t length; + uint8_t flags; + uint8_t rsvd1; + uint16_t count; + uint8_t rsvd2[0xc]; + struct { + uint64_t addr; + uint32_t length; + uint32_t resv; + } QEMU_PACKED records[]; + } QEMU_PACKED; + + struct get_scan_media_results_out_pl *out = (void *)payload_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLPoisonList *scan_media_results = &ct3d->scan_media_results; + CXLPoison *ent, *next; + uint16_t total_count = 0, record_count = 0, i = 0; + uint16_t out_pl_len; + + if (!ct3d->scan_media_hasrun) { + return CXL_MBOX_UNSUPPORTED; + } + + /* + * Calculate limits, all entries are within the same address range of the + * last scan media call. + */ + QLIST_FOREACH(ent, scan_media_results, node) { + size_t rec_size = record_count * sizeof(out->records[0]); + + if (sizeof(*out) + rec_size < CXL_MAILBOX_MAX_PAYLOAD_SIZE) { + record_count++; + } + total_count++; + } + + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + memset(out, 0, out_pl_len); + QLIST_FOREACH_SAFE(ent, scan_media_results, node, next) { + uint64_t start, stop; + + if (i == record_count) { + break; + } + + start = ROUND_DOWN(ent->start, 64ull); + stop = ROUND_DOWN(ent->start, 64ull) + ent->length; + stq_le_p(&out->records[i].addr, start | (ent->type & 0x7)); + stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE); + i++; + + /* consume the returning entry */ + QLIST_REMOVE(ent, node); + g_free(ent); + } + + stw_le_p(&out->count, record_count); + if (total_count > record_count) { + out->flags = (1 << 0); /* More Media Error Records */ + } + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + /* * CXL r3.1 section 8.2.9.9.9.1: Get Dynamic Capacity Configuration * (Opcode: 4800h) @@ -2060,6 +2142,9 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_media_get_scan_media_capabilities, 16, 0 }, [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA", cmd_media_scan_media, 17, BACKGROUND_OPERATION }, + [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = { + "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS", + cmd_media_get_scan_media_results, 0, 0 }, }; static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 0509d961c3..cc98553583 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -499,6 +499,7 @@ struct CXLType3Dev { /* Poison Injection - backup */ CXLPoisonList poison_list_bkp; CXLPoisonList scan_media_results; + bool scan_media_hasrun; struct dynamic_capacity { HostMemoryBackend *host_dc; From 25da36d5d04deb8869bd6ad1223832f09f3efe79 Mon Sep 17 00:00:00 2001 From: Gregory Price <gourry.memverge@gmail.com> Date: Fri, 5 Jul 2024 13:30:35 +0100 Subject: [PATCH 2292/2884] cxl/mailbox: move mailbox effect definitions to a header Preparation for allowing devices to define their own CCI commands Signed-off-by: Gregory Price <gregory.price@memverge.com> Link: https://lore.kernel.org/r/20230906001517.324380-2-gregory.price@memverge.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705123039.963781-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 34 +++++++++++++++------------------- include/hw/cxl/cxl_mailbox.h | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 include/hw/cxl/cxl_mailbox.h diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 82120a6e7b..dbaa83a110 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -12,6 +12,7 @@ #include "hw/pci/msix.h" #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_events.h" +#include "hw/cxl/cxl_mailbox.h" #include "hw/pci/pci.h" #include "hw/pci-bridge/cxl_upstream_port.h" #include "qemu/cutils.h" @@ -2095,28 +2096,21 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } -#define IMMEDIATE_CONFIG_CHANGE (1 << 1) -#define IMMEDIATE_DATA_CHANGE (1 << 2) -#define IMMEDIATE_POLICY_CHANGE (1 << 3) -#define IMMEDIATE_LOG_CHANGE (1 << 4) -#define SECURITY_STATE_CHANGE (1 << 5) -#define BACKGROUND_OPERATION (1 << 6) - static const struct cxl_cmd cxl_cmd_set[256][256] = { [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", cmd_events_get_records, 1, 0 }, [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS", - cmd_events_clear_records, ~0, IMMEDIATE_LOG_CHANGE }, + cmd_events_clear_records, ~0, CXL_MBOX_IMMEDIATE_LOG_CHANGE }, [EVENTS][GET_INTERRUPT_POLICY] = { "EVENTS_GET_INTERRUPT_POLICY", cmd_events_get_interrupt_policy, 0, 0 }, [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY", cmd_events_set_interrupt_policy, - ~0, IMMEDIATE_CONFIG_CHANGE }, + ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE }, [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", cmd_firmware_update_get_info, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, - 8, IMMEDIATE_POLICY_CHANGE }, + 8, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, @@ -2126,9 +2120,11 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { cmd_ccls_get_partition_info, 0, 0 }, [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 }, [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, - ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE }, + ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE }, [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, - IMMEDIATE_DATA_CHANGE | SECURITY_STATE_CHANGE | BACKGROUND_OPERATION }, + (CXL_MBOX_IMMEDIATE_DATA_CHANGE | + CXL_MBOX_SECURITY_STATE_CHANGE | + CXL_MBOX_BACKGROUND_OPERATION)}, [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE", cmd_get_security_state, 0, 0 }, [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", @@ -2141,7 +2137,7 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { "MEDIA_AND_POISON_GET_SCAN_MEDIA_CAPABILITIES", cmd_media_get_scan_media_capabilities, 16, 0 }, [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA", - cmd_media_scan_media, 17, BACKGROUND_OPERATION }, + cmd_media_scan_media, 17, CXL_MBOX_BACKGROUND_OPERATION }, [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = { "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS", cmd_media_get_scan_media_results, 0, 0 }, @@ -2155,10 +2151,10 @@ static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { 8, 0 }, [DCD_CONFIG][ADD_DYN_CAP_RSP] = { "DCD_ADD_DYNAMIC_CAPACITY_RESPONSE", cmd_dcd_add_dyn_cap_rsp, - ~0, IMMEDIATE_DATA_CHANGE }, + ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE }, [DCD_CONFIG][RELEASE_DYN_CAP] = { "DCD_RELEASE_DYNAMIC_CAPACITY", cmd_dcd_release_dyn_cap, - ~0, IMMEDIATE_DATA_CHANGE }, + ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE }, }; static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { @@ -2166,8 +2162,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS", cmd_infostat_bg_op_sts, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, - [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 0, - IMMEDIATE_POLICY_CHANGE }, + [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, + CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, @@ -2210,7 +2206,7 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, } /* Only one bg command at a time */ - if ((cxl_cmd->effect & BACKGROUND_OPERATION) && + if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) && cci->bg.runtime > 0) { return CXL_MBOX_BUSY; } @@ -2235,7 +2231,7 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, } ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); - if ((cxl_cmd->effect & BACKGROUND_OPERATION) && + if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) && ret == CXL_MBOX_BG_STARTED) { *bg_started = true; } else { diff --git a/include/hw/cxl/cxl_mailbox.h b/include/hw/cxl/cxl_mailbox.h new file mode 100644 index 0000000000..beb048052e --- /dev/null +++ b/include/hw/cxl/cxl_mailbox.h @@ -0,0 +1,18 @@ +/* + * QEMU CXL Mailbox + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + */ + +#ifndef CXL_MAILBOX_H +#define CXL_MAILBOX_H + +#define CXL_MBOX_IMMEDIATE_CONFIG_CHANGE (1 << 1) +#define CXL_MBOX_IMMEDIATE_DATA_CHANGE (1 << 2) +#define CXL_MBOX_IMMEDIATE_POLICY_CHANGE (1 << 3) +#define CXL_MBOX_IMMEDIATE_LOG_CHANGE (1 << 4) +#define CXL_MBOX_SECURITY_STATE_CHANGE (1 << 5) +#define CXL_MBOX_BACKGROUND_OPERATION (1 << 6) + +#endif From d80378943a6b88b8d211e1468073d9dd44239e27 Mon Sep 17 00:00:00 2001 From: Shiju Jose <shiju.jose@huawei.com> Date: Fri, 5 Jul 2024 13:30:36 +0100 Subject: [PATCH 2293/2884] hw/cxl/cxl-mailbox-utils: Add support for feature commands (8.2.9.6) CXL spec 3.1 section 8.2.9.6 describes optional device specific features. CXL devices supports features with changeable attributes. Get Supported Features retrieves the list of supported device specific features. The settings of a feature can be retrieved using Get Feature and optionally modified using Set Feature. Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Reviewed-by: Fan Ni <fan.ni@samsung.com> Signed-off-by: Shiju Jose <shiju.jose@huawei.com> Link: https://lore.kernel.org/r/20240223085902.1549-2-shiju.jose@huawei.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705123039.963781-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 258 ++++++++++++++++++++++++++++++++++++ include/hw/cxl/cxl_device.h | 10 ++ 2 files changed, 268 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index dbaa83a110..7b7d8e5eae 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -69,6 +69,10 @@ enum { LOGS = 0x04, #define GET_SUPPORTED 0x0 #define GET_LOG 0x1 + FEATURES = 0x05, + #define GET_SUPPORTED 0x0 + #define GET_FEATURE 0x1 + #define SET_FEATURE 0x2 IDENTIFY = 0x40, #define MEMORY_DEVICE 0x0 CCLS = 0x41, @@ -767,6 +771,248 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.1 section 8.2.9.6: Features */ +/* + * Get Supported Features output payload + * CXL r3.1 section 8.2.9.6.1 Table 8-96 + */ +typedef struct CXLSupportedFeatureHeader { + uint16_t entries; + uint16_t nsuppfeats_dev; + uint32_t reserved; +} QEMU_PACKED CXLSupportedFeatureHeader; + +/* + * Get Supported Features Supported Feature Entry + * CXL r3.1 section 8.2.9.6.1 Table 8-97 + */ +typedef struct CXLSupportedFeatureEntry { + QemuUUID uuid; + uint16_t feat_index; + uint16_t get_feat_size; + uint16_t set_feat_size; + uint32_t attr_flags; + uint8_t get_feat_version; + uint8_t set_feat_version; + uint16_t set_feat_effects; + uint8_t rsvd[18]; +} QEMU_PACKED CXLSupportedFeatureEntry; + +/* + * Get Supported Features Supported Feature Entry + * CXL rev 3.1 section 8.2.9.6.1 Table 8-97 + */ +/* Supported Feature Entry : attribute flags */ +#define CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE BIT(0) +#define CXL_FEAT_ENTRY_ATTR_FLAG_DEEPEST_RESET_PERSISTENCE_MASK GENMASK(3, 1) +#define CXL_FEAT_ENTRY_ATTR_FLAG_PERSIST_ACROSS_FIRMWARE_UPDATE BIT(4) +#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SELECTION BIT(5) +#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_SAVED_SELECTION BIT(6) + +/* Supported Feature Entry : set feature effects */ +#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_COLD_RESET BIT(0) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE BIT(1) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_DATA_CHANGE BIT(2) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_POLICY_CHANGE BIT(3) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_LOG_CHANGE BIT(4) +#define CXL_FEAT_ENTRY_SFE_SECURITY_STATE_CHANGE BIT(5) +#define CXL_FEAT_ENTRY_SFE_BACKGROUND_OPERATION BIT(6) +#define CXL_FEAT_ENTRY_SFE_SUPPORT_SECONDARY_MAILBOX BIT(7) +#define CXL_FEAT_ENTRY_SFE_SUPPORT_ABORT_BACKGROUND_OPERATION BIT(8) +#define CXL_FEAT_ENTRY_SFE_CEL_VALID BIT(9) +#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CONV_RESET BIT(10) +#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CXL_RESET BIT(11) + +enum CXL_SUPPORTED_FEATURES_LIST { + CXL_FEATURE_MAX +}; + +/* Get Feature CXL 3.1 Spec 8.2.9.6.2 */ +/* + * Get Feature input payload + * CXL r3.1 section 8.2.9.6.2 Table 8-99 + */ +/* Get Feature : Payload in selection */ +enum CXL_GET_FEATURE_SELECTION { + CXL_GET_FEATURE_SEL_CURRENT_VALUE, + CXL_GET_FEATURE_SEL_DEFAULT_VALUE, + CXL_GET_FEATURE_SEL_SAVED_VALUE, + CXL_GET_FEATURE_SEL_MAX +}; + +/* Set Feature CXL 3.1 Spec 8.2.9.6.3 */ +/* + * Set Feature input payload + * CXL r3.1 section 8.2.9.6.3 Table 8-101 + */ +typedef struct CXLSetFeatureInHeader { + QemuUUID uuid; + uint32_t flags; + uint16_t offset; + uint8_t version; + uint8_t rsvd[9]; +} QEMU_PACKED QEMU_ALIGNED(16) CXLSetFeatureInHeader; + +/* Set Feature : Payload in flags */ +#define CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK 0x7 +enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER { + CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX +}; +#define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3) + +/* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */ +static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint32_t count; + uint16_t start_index; + uint16_t reserved; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_in = (void *)payload_in; + + struct { + CXLSupportedFeatureHeader hdr; + CXLSupportedFeatureEntry feat_entries[]; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_out = (void *)payload_out; + uint16_t index, req_entries; + uint16_t entry; + + if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + return CXL_MBOX_UNSUPPORTED; + } + if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) || + /* + * Temporary: suppress compiler error due to unsigned + * comparioson to zero. + */ + true /*get_feats_in->start_index >= CXL_FEATURE_MAX*/) { + return CXL_MBOX_INVALID_INPUT; + } + + req_entries = (get_feats_in->count - + sizeof(CXLSupportedFeatureHeader)) / + sizeof(CXLSupportedFeatureEntry); + req_entries = MIN(req_entries, + (CXL_FEATURE_MAX - get_feats_in->start_index)); + + for (entry = 0, index = get_feats_in->start_index; + entry < req_entries; index++) { + switch (index) { + default: + __builtin_unreachable(); + } + } + get_feats_out->hdr.nsuppfeats_dev = CXL_FEATURE_MAX; + get_feats_out->hdr.entries = req_entries; + *len_out = sizeof(CXLSupportedFeatureHeader) + + req_entries * sizeof(CXLSupportedFeatureEntry); + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.6.2: Get Feature (Opcode 0501h) */ +static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + QemuUUID uuid; + uint16_t offset; + uint16_t count; + uint8_t selection; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feature; + uint16_t bytes_to_copy = 0; + CXLType3Dev *ct3d; + CXLSetFeatureInfo *set_feat_info; + + if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + return CXL_MBOX_UNSUPPORTED; + } + + ct3d = CXL_TYPE3(cci->d); + get_feature = (void *)payload_in; + + set_feat_info = &ct3d->set_feat_info; + if (qemu_uuid_is_equal(&get_feature->uuid, &set_feat_info->uuid)) { + return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS; + } + + if (get_feature->selection != CXL_GET_FEATURE_SEL_CURRENT_VALUE) { + return CXL_MBOX_UNSUPPORTED; + } + if (get_feature->offset + get_feature->count > cci->payload_max) { + return CXL_MBOX_INVALID_INPUT; + } + + *len_out = bytes_to_copy; + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.6.3: Set Feature (Opcode 0502h) */ +static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLSetFeatureInHeader *hdr = (void *)payload_in; + CXLSetFeatureInfo *set_feat_info; + uint8_t data_transfer_flag; + CXLType3Dev *ct3d; + + + if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + return CXL_MBOX_UNSUPPORTED; + } + ct3d = CXL_TYPE3(cci->d); + set_feat_info = &ct3d->set_feat_info; + + if (!qemu_uuid_is_null(&set_feat_info->uuid) && + !qemu_uuid_is_equal(&hdr->uuid, &set_feat_info->uuid)) { + return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS; + } + if (hdr->flags & CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET) { + set_feat_info->data_saved_across_reset = true; + } else { + set_feat_info->data_saved_across_reset = false; + } + + data_transfer_flag = + hdr->flags & CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK; + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER) { + set_feat_info->uuid = hdr->uuid; + set_feat_info->data_size = 0; + } + set_feat_info->data_transfer_flag = data_transfer_flag; + set_feat_info->data_offset = hdr->offset; + + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) { + memset(&set_feat_info->uuid, 0, sizeof(QemuUUID)); + set_feat_info->data_transfer_flag = 0; + set_feat_info->data_saved_across_reset = false; + set_feat_info->data_offset = 0; + set_feat_info->data_size = 0; + } + + return CXL_MBOX_SUCCESS; +} + /* CXL r3.1 Section 8.2.9.9.1.1: Identify Memory Device (Opcode 4000h) */ static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -2114,6 +2360,18 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [FEATURES][GET_SUPPORTED] = { "FEATURES_GET_SUPPORTED", + cmd_features_get_supported, 0x8, 0 }, + [FEATURES][GET_FEATURE] = { "FEATURES_GET_FEATURE", + cmd_features_get_feature, 0x15, 0 }, + [FEATURES][SET_FEATURE] = { "FEATURES_SET_FEATURE", + cmd_features_set_feature, + ~0, + (CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE | + CXL_MBOX_IMMEDIATE_POLICY_CHANGE | + CXL_MBOX_IMMEDIATE_LOG_CHANGE | + CXL_MBOX_SECURITY_STATE_CHANGE)}, [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE", cmd_identify_memory_device, 0, 0 }, [CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO", diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index cc98553583..48ed0d9240 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -464,6 +464,14 @@ typedef struct CXLDCRegion { unsigned long *blk_bitmap; } CXLDCRegion; +typedef struct CXLSetFeatureInfo { + QemuUUID uuid; + uint8_t data_transfer_flag; + bool data_saved_across_reset; + uint16_t data_offset; + size_t data_size; +} CXLSetFeatureInfo; + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; @@ -501,6 +509,8 @@ struct CXLType3Dev { CXLPoisonList scan_media_results; bool scan_media_hasrun; + CXLSetFeatureInfo set_feat_info; + struct dynamic_capacity { HostMemoryBackend *host_dc; AddressSpace host_dc_as; From d88f667414106c7216485774293d0831c2482d20 Mon Sep 17 00:00:00 2001 From: Shiju Jose <shiju.jose@huawei.com> Date: Fri, 5 Jul 2024 13:30:37 +0100 Subject: [PATCH 2294/2884] hw/cxl/cxl-mailbox-utils: Add device patrol scrub control feature CXL spec 3.1 section 8.2.9.9.11.1 describes the device patrol scrub control feature. The device patrol scrub proactively locates and makes corrections to errors in regular cycle. The patrol scrub control allows the request to configure patrol scrub input configurations. The patrol scrub control allows the requester to specify the number of hours for which the patrol scrub cycles must be completed, provided that the requested number is not less than the minimum number of hours for the patrol scrub cycle that the device is capable of. In addition, the patrol scrub controls allow the host to disable and enable the feature in case disabling of the feature is needed for other purposes such as performance-aware operations which require the background operations to be turned off. Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Reviewed-by: Fan Ni <fan.ni@samsung.com> Signed-off-by: Shiju Jose <shiju.jose@huawei.com> Link: https://lore.kernel.org/r/20240223085902.1549-3-shiju.jose@huawei.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705123039.963781-4-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 79 ++++++++++++++++++++++++++++++++++--- hw/mem/cxl_type3.c | 9 +++++ include/hw/cxl/cxl_device.h | 24 +++++++++++ 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 7b7d8e5eae..485beb9dba 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -824,6 +824,7 @@ typedef struct CXLSupportedFeatureEntry { #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CXL_RESET BIT(11) enum CXL_SUPPORTED_FEATURES_LIST { + CXL_FEATURE_PATROL_SCRUB = 0, CXL_FEATURE_MAX }; @@ -865,6 +866,17 @@ enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER { }; #define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3) +/* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */ +static const QemuUUID patrol_scrub_uuid = { + .data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33, + 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a) +}; + +typedef struct CXLMemPatrolScrubSetFeature { + CXLSetFeatureInHeader hdr; + CXLMemPatrolScrubWriteAttrs feat_data; +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature; + /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -890,11 +902,7 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) || - /* - * Temporary: suppress compiler error due to unsigned - * comparioson to zero. - */ - true /*get_feats_in->start_index >= CXL_FEATURE_MAX*/) { + get_feats_in->start_index >= CXL_FEATURE_MAX) { return CXL_MBOX_INVALID_INPUT; } @@ -907,6 +915,21 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, for (entry = 0, index = get_feats_in->start_index; entry < req_entries; index++) { switch (index) { + case CXL_FEATURE_PATROL_SCRUB: + /* Fill supported feature entry for device patrol scrub control */ + get_feats_out->feat_entries[entry++] = + (struct CXLSupportedFeatureEntry) { + .uuid = patrol_scrub_uuid, + .feat_index = index, + .get_feat_size = sizeof(CXLMemPatrolScrubReadAttrs), + .set_feat_size = sizeof(CXLMemPatrolScrubWriteAttrs), + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE, + .get_feat_version = CXL_MEMDEV_PS_GET_FEATURE_VERSION, + .set_feat_version = CXL_MEMDEV_PS_SET_FEATURE_VERSION, + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE | + CXL_FEAT_ENTRY_SFE_CEL_VALID, + }; + break; default: __builtin_unreachable(); } @@ -956,6 +979,20 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd, return CXL_MBOX_INVALID_INPUT; } + if (qemu_uuid_is_equal(&get_feature->uuid, &patrol_scrub_uuid)) { + if (get_feature->offset >= sizeof(CXLMemPatrolScrubReadAttrs)) { + return CXL_MBOX_INVALID_INPUT; + } + bytes_to_copy = sizeof(CXLMemPatrolScrubReadAttrs) - + get_feature->offset; + bytes_to_copy = MIN(bytes_to_copy, get_feature->count); + memcpy(payload_out, + (uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset, + bytes_to_copy); + } else { + return CXL_MBOX_UNSUPPORTED; + } + *len_out = bytes_to_copy; return CXL_MBOX_SUCCESS; @@ -970,7 +1007,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, CXLCCI *cci) { CXLSetFeatureInHeader *hdr = (void *)payload_in; + CXLMemPatrolScrubWriteAttrs *ps_write_attrs; + CXLMemPatrolScrubSetFeature *ps_set_feature; CXLSetFeatureInfo *set_feat_info; + uint16_t bytes_to_copy = 0; uint8_t data_transfer_flag; CXLType3Dev *ct3d; @@ -999,11 +1039,40 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, } set_feat_info->data_transfer_flag = data_transfer_flag; set_feat_info->data_offset = hdr->offset; + bytes_to_copy = len_in - sizeof(CXLSetFeatureInHeader); + + if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { + if (hdr->version != CXL_MEMDEV_PS_SET_FEATURE_VERSION) { + return CXL_MBOX_UNSUPPORTED; + } + + ps_set_feature = (void *)payload_in; + ps_write_attrs = &ps_set_feature->feat_data; + memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset, + ps_write_attrs, + bytes_to_copy); + set_feat_info->data_size += bytes_to_copy; + + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) { + ct3d->patrol_scrub_attrs.scrub_cycle &= ~0xFF; + ct3d->patrol_scrub_attrs.scrub_cycle |= + ct3d->patrol_scrub_wr_attrs.scrub_cycle_hr & 0xFF; + ct3d->patrol_scrub_attrs.scrub_flags &= ~0x1; + ct3d->patrol_scrub_attrs.scrub_flags |= + ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1; + } + } else { + return CXL_MBOX_UNSUPPORTED; + } if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER || data_transfer_flag == CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) { memset(&set_feat_info->uuid, 0, sizeof(QemuUUID)); + if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { + memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size); + } set_feat_info->data_transfer_flag = 0; set_feat_info->data_saved_across_reset = false; set_feat_info->data_offset = 0; diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index c7910687ae..7c583d80f5 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -908,6 +908,15 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) } cxl_event_init(&ct3d->cxl_dstate, 2); + /* Set default value for patrol scrub attributes */ + ct3d->patrol_scrub_attrs.scrub_cycle_cap = + CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT | + CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT; + ct3d->patrol_scrub_attrs.scrub_cycle = + CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT | + (CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT << 8); + ct3d->patrol_scrub_attrs.scrub_flags = CXL_MEMDEV_PS_ENABLE_DEFAULT; + return; err_release_cdat: diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 48ed0d9240..2c1df25453 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -427,6 +427,26 @@ typedef struct CXLPoison { typedef QLIST_HEAD(, CXLPoison) CXLPoisonList; #define CXL_POISON_LIST_LIMIT 256 +/* CXL memory device patrol scrub control attributes */ +typedef struct CXLMemPatrolScrubReadAttrs { + uint8_t scrub_cycle_cap; + uint16_t scrub_cycle; + uint8_t scrub_flags; +} QEMU_PACKED CXLMemPatrolScrubReadAttrs; + +typedef struct CXLMemPatrolScrubWriteAttrs { + uint8_t scrub_cycle_hr; + uint8_t scrub_flags; +} QEMU_PACKED CXLMemPatrolScrubWriteAttrs; + +#define CXL_MEMDEV_PS_GET_FEATURE_VERSION 0x01 +#define CXL_MEMDEV_PS_SET_FEATURE_VERSION 0x01 +#define CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT BIT(0) +#define CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT BIT(1) +#define CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT 12 +#define CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT 1 +#define CXL_MEMDEV_PS_ENABLE_DEFAULT 0 + #define DCD_MAX_NUM_REGION 8 typedef struct CXLDCExtentRaw { @@ -511,6 +531,10 @@ struct CXLType3Dev { CXLSetFeatureInfo set_feat_info; + /* Patrol scrub control attributes */ + CXLMemPatrolScrubReadAttrs patrol_scrub_attrs; + CXLMemPatrolScrubWriteAttrs patrol_scrub_wr_attrs; + struct dynamic_capacity { HostMemoryBackend *host_dc; AddressSpace host_dc_as; From 2d41ce38fb9af3e66f85c8b8f9c3f83148c3d549 Mon Sep 17 00:00:00 2001 From: Shiju Jose <shiju.jose@huawei.com> Date: Fri, 5 Jul 2024 13:30:38 +0100 Subject: [PATCH 2295/2884] hw/cxl/cxl-mailbox-utils: Add device DDR5 ECS control feature CXL spec 3.1 section 8.2.9.9.11.2 describes the DDR5 Error Check Scrub (ECS) control feature. The Error Check Scrub (ECS) is a feature defined in JEDEC DDR5 SDRAM Specification (JESD79-5) and allows the DRAM to internally read, correct single-bit errors, and write back corrected data bits to the DRAM array while providing transparency to error counts. The ECS control feature allows the request to configure ECS input configurations during system boot or at run-time. The ECS control allows the requester to change the log entry type, the ECS threshold count provided that the request is within the definition specified in DDR5 mode registers, change mode between codeword mode and row count mode, and reset the ECS counter. Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Reviewed-by: Fan Ni <fan.ni@samsung.com> Signed-off-by: Shiju Jose <shiju.jose@huawei.com> Link: https://lore.kernel.org/r/20240223085902.1549-4-shiju.jose@huawei.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705123039.963781-5-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 71 +++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 14 ++++++++ include/hw/cxl/cxl_device.h | 24 +++++++++++++ 3 files changed, 109 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 485beb9dba..0621f686f4 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -825,6 +825,7 @@ typedef struct CXLSupportedFeatureEntry { enum CXL_SUPPORTED_FEATURES_LIST { CXL_FEATURE_PATROL_SCRUB = 0, + CXL_FEATURE_ECS, CXL_FEATURE_MAX }; @@ -877,6 +878,20 @@ typedef struct CXLMemPatrolScrubSetFeature { CXLMemPatrolScrubWriteAttrs feat_data; } QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature; +/* + * CXL r3.1 section 8.2.9.9.11.2: + * DDR5 Error Check Scrub (ECS) Control Feature + */ +static const QemuUUID ecs_uuid = { + .data = UUID(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba, + 0xb9, 0x69, 0x1e, 0x89, 0x33, 0x86) +}; + +typedef struct CXLMemECSSetFeature { + CXLSetFeatureInHeader hdr; + CXLMemECSWriteAttrs feat_data[]; +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature; + /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -930,6 +945,23 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, CXL_FEAT_ENTRY_SFE_CEL_VALID, }; break; + case CXL_FEATURE_ECS: + /* Fill supported feature entry for device DDR5 ECS control */ + get_feats_out->feat_entries[entry++] = + (struct CXLSupportedFeatureEntry) { + .uuid = ecs_uuid, + .feat_index = index, + .get_feat_size = CXL_ECS_NUM_MEDIA_FRUS * + sizeof(CXLMemECSReadAttrs), + .set_feat_size = CXL_ECS_NUM_MEDIA_FRUS * + sizeof(CXLMemECSWriteAttrs), + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE, + .get_feat_version = CXL_ECS_GET_FEATURE_VERSION, + .set_feat_version = CXL_ECS_SET_FEATURE_VERSION, + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE | + CXL_FEAT_ENTRY_SFE_CEL_VALID, + }; + break; default: __builtin_unreachable(); } @@ -989,6 +1021,18 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd, memcpy(payload_out, (uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset, bytes_to_copy); + } else if (qemu_uuid_is_equal(&get_feature->uuid, &ecs_uuid)) { + if (get_feature->offset >= CXL_ECS_NUM_MEDIA_FRUS * + sizeof(CXLMemECSReadAttrs)) { + return CXL_MBOX_INVALID_INPUT; + } + bytes_to_copy = CXL_ECS_NUM_MEDIA_FRUS * + sizeof(CXLMemECSReadAttrs) - + get_feature->offset; + bytes_to_copy = MIN(bytes_to_copy, get_feature->count); + memcpy(payload_out, + (uint8_t *)&ct3d->ecs_attrs + get_feature->offset, + bytes_to_copy); } else { return CXL_MBOX_UNSUPPORTED; } @@ -1009,10 +1053,13 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, CXLSetFeatureInHeader *hdr = (void *)payload_in; CXLMemPatrolScrubWriteAttrs *ps_write_attrs; CXLMemPatrolScrubSetFeature *ps_set_feature; + CXLMemECSWriteAttrs *ecs_write_attrs; + CXLMemECSSetFeature *ecs_set_feature; CXLSetFeatureInfo *set_feat_info; uint16_t bytes_to_copy = 0; uint8_t data_transfer_flag; CXLType3Dev *ct3d; + uint16_t count; if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { @@ -1062,6 +1109,28 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, ct3d->patrol_scrub_attrs.scrub_flags |= ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1; } + } else if (qemu_uuid_is_equal(&hdr->uuid, + &ecs_uuid)) { + if (hdr->version != CXL_ECS_SET_FEATURE_VERSION) { + return CXL_MBOX_UNSUPPORTED; + } + + ecs_set_feature = (void *)payload_in; + ecs_write_attrs = ecs_set_feature->feat_data; + memcpy((uint8_t *)ct3d->ecs_wr_attrs + hdr->offset, + ecs_write_attrs, + bytes_to_copy); + set_feat_info->data_size += bytes_to_copy; + + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) { + for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) { + ct3d->ecs_attrs[count].ecs_log_cap = + ct3d->ecs_wr_attrs[count].ecs_log_cap; + ct3d->ecs_attrs[count].ecs_config = + ct3d->ecs_wr_attrs[count].ecs_config & 0x1F; + } + } } else { return CXL_MBOX_UNSUPPORTED; } @@ -1072,6 +1141,8 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, memset(&set_feat_info->uuid, 0, sizeof(QemuUUID)); if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size); + } else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) { + memset(ct3d->ecs_wr_attrs, 0, set_feat_info->data_size); } set_feat_info->data_transfer_flag = 0; set_feat_info->data_saved_across_reset = false; diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 7c583d80f5..d648192ab9 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -844,6 +844,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) uint8_t *pci_conf = pci_dev->config; unsigned short msix_num = 6; int i, rc; + uint16_t count; QTAILQ_INIT(&ct3d->error_list); @@ -917,6 +918,19 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) (CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT << 8); ct3d->patrol_scrub_attrs.scrub_flags = CXL_MEMDEV_PS_ENABLE_DEFAULT; + /* Set default value for DDR5 ECS read attributes */ + for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) { + ct3d->ecs_attrs[count].ecs_log_cap = + CXL_ECS_LOG_ENTRY_TYPE_DEFAULT; + ct3d->ecs_attrs[count].ecs_cap = + CXL_ECS_REALTIME_REPORT_CAP_DEFAULT; + ct3d->ecs_attrs[count].ecs_config = + CXL_ECS_THRESHOLD_COUNT_DEFAULT | + (CXL_ECS_MODE_DEFAULT << 3); + /* Reserved */ + ct3d->ecs_attrs[count].ecs_flags = 0; + } + return; err_release_cdat: diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 2c1df25453..5cae7159e6 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -447,6 +447,27 @@ typedef struct CXLMemPatrolScrubWriteAttrs { #define CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT 1 #define CXL_MEMDEV_PS_ENABLE_DEFAULT 0 +/* CXL memory device DDR5 ECS control attributes */ +typedef struct CXLMemECSReadAttrs { + uint8_t ecs_log_cap; + uint8_t ecs_cap; + uint16_t ecs_config; + uint8_t ecs_flags; +} QEMU_PACKED CXLMemECSReadAttrs; + +typedef struct CXLMemECSWriteAttrs { + uint8_t ecs_log_cap; + uint16_t ecs_config; +} QEMU_PACKED CXLMemECSWriteAttrs; + +#define CXL_ECS_GET_FEATURE_VERSION 0x01 +#define CXL_ECS_SET_FEATURE_VERSION 0x01 +#define CXL_ECS_LOG_ENTRY_TYPE_DEFAULT 0x01 +#define CXL_ECS_REALTIME_REPORT_CAP_DEFAULT 1 +#define CXL_ECS_THRESHOLD_COUNT_DEFAULT 3 /* 3: 256, 4: 1024, 5: 4096 */ +#define CXL_ECS_MODE_DEFAULT 0 +#define CXL_ECS_NUM_MEDIA_FRUS 3 /* Default */ + #define DCD_MAX_NUM_REGION 8 typedef struct CXLDCExtentRaw { @@ -534,6 +555,9 @@ struct CXLType3Dev { /* Patrol scrub control attributes */ CXLMemPatrolScrubReadAttrs patrol_scrub_attrs; CXLMemPatrolScrubWriteAttrs patrol_scrub_wr_attrs; + /* ECS control attributes */ + CXLMemECSReadAttrs ecs_attrs[CXL_ECS_NUM_MEDIA_FRUS]; + CXLMemECSWriteAttrs ecs_wr_attrs[CXL_ECS_NUM_MEDIA_FRUS]; struct dynamic_capacity { HostMemoryBackend *host_dc; From 3c1e1e5e240e683f0611b96cd325471639f22c6d Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso <dave@stgolabs.net> Date: Fri, 5 Jul 2024 13:59:15 +0100 Subject: [PATCH 2296/2884] hw/cxl: Support firmware updates Implement transfer and activate functionality per 3.1 spec for supporting update metadata (no actual buffers). Transfer times are arbitrarily set to ten and two seconds for full and part transfers, respectively. cxl update-firmware mem0 -F fw.img <on-going fw update> cxl update-firmware mem0 "memdev":"mem0", "pmem_size":"1024.00 MiB (1073.74 MB)", "serial":"0", "host":"0000:0d:00.0", "firmware":{ "num_slots":2, "active_slot":1, "online_activate_capable":true, "slot_1_version":"BWFW VERSION 0", "fw_update_in_progress":true, "remaining_size":22400 } } <completed fw update> cxl update-firmware mem0 { "memdev":"mem0", "pmem_size":"1024.00 MiB (1073.74 MB)", "serial":"0", "host":"0000:0d:00.0", "firmware":{ "num_slots":2, "active_slot":1, "staged_slot":2, "online_activate_capable":true, "slot_1_version":"BWFW VERSION 0", "slot_2_version":"BWFW VERSION 1", "fw_update_in_progress":false } } Signed-off-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://lore.kernel.org/r/20240627164912.25630-1-dave@stgolabs.net Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Message-Id: <20240705125915.991672-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/cxl/cxl-mailbox-utils.c | 205 +++++++++++++++++++++++++++++++++++- include/hw/cxl/cxl_device.h | 15 +++ 2 files changed, 215 insertions(+), 5 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 0621f686f4..b752920ec8 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -63,6 +63,8 @@ enum { #define SET_INTERRUPT_POLICY 0x3 FIRMWARE_UPDATE = 0x02, #define GET_INFO 0x0 + #define TRANSFER 0x1 + #define ACTIVATE 0x2 TIMESTAMP = 0x03, #define GET 0x0 #define SET 0x1 @@ -622,6 +624,9 @@ static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +#define CXL_FW_SLOTS 2 +#define CXL_FW_SIZE 0x02000000 /* 32 mb */ + /* CXL r3.1 Section 8.2.9.3.1: Get FW Info (Opcode 0200h) */ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -652,15 +657,192 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, fw_info = (void *)payload_out; - fw_info->slots_supported = 2; - fw_info->slot_info = BIT(0) | BIT(3); - fw_info->caps = 0; - pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); + fw_info->slots_supported = CXL_FW_SLOTS; + fw_info->slot_info = (cci->fw.active_slot & 0x7) | + ((cci->fw.staged_slot & 0x7) << 3); + fw_info->caps = BIT(0); /* online update supported */ + + if (cci->fw.slot[0]) { + pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); + } + if (cci->fw.slot[1]) { + pstrcpy(fw_info->fw_rev2, sizeof(fw_info->fw_rev2), "BWFW VERSION 1"); + } *len_out = sizeof(*fw_info); return CXL_MBOX_SUCCESS; } +/* CXL r3.1 section 8.2.9.3.2: Transfer FW (Opcode 0201h) */ +#define CXL_FW_XFER_ALIGNMENT 128 + +#define CXL_FW_XFER_ACTION_FULL 0x0 +#define CXL_FW_XFER_ACTION_INIT 0x1 +#define CXL_FW_XFER_ACTION_CONTINUE 0x2 +#define CXL_FW_XFER_ACTION_END 0x3 +#define CXL_FW_XFER_ACTION_ABORT 0x4 + +static CXLRetCode cmd_firmware_update_transfer(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t action; + uint8_t slot; + uint8_t rsvd1[2]; + uint32_t offset; + uint8_t rsvd2[0x78]; + uint8_t data[]; + } QEMU_PACKED *fw_transfer = (void *)payload_in; + size_t offset, length; + + if (fw_transfer->action == CXL_FW_XFER_ACTION_ABORT) { + /* + * At this point there aren't any on-going transfers + * running in the bg - this is serialized before this + * call altogether. Just mark the state machine and + * disregard any other input. + */ + cci->fw.transferring = false; + return CXL_MBOX_SUCCESS; + } + + offset = fw_transfer->offset * CXL_FW_XFER_ALIGNMENT; + length = len - sizeof(*fw_transfer); + if (offset + length > CXL_FW_SIZE) { + return CXL_MBOX_INVALID_INPUT; + } + + if (cci->fw.transferring) { + if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL || + fw_transfer->action == CXL_FW_XFER_ACTION_INIT) { + return CXL_MBOX_FW_XFER_IN_PROGRESS; + } + /* + * Abort partitioned package transfer if over 30 secs + * between parts. As opposed to the explicit ABORT action, + * semantically treat this condition as an error - as + * if a part action were passed without a previous INIT. + */ + if (difftime(time(NULL), cci->fw.last_partxfer) > 30.0) { + cci->fw.transferring = false; + return CXL_MBOX_INVALID_INPUT; + } + } else if (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE || + fw_transfer->action == CXL_FW_XFER_ACTION_END) { + return CXL_MBOX_INVALID_INPUT; + } + + /* allow back-to-back retransmission */ + if ((offset != cci->fw.prev_offset || length != cci->fw.prev_len) && + (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE || + fw_transfer->action == CXL_FW_XFER_ACTION_END)) { + /* verify no overlaps */ + if (offset < cci->fw.prev_offset + cci->fw.prev_len) { + return CXL_MBOX_FW_XFER_OUT_OF_ORDER; + } + } + + switch (fw_transfer->action) { + case CXL_FW_XFER_ACTION_FULL: /* ignores offset */ + case CXL_FW_XFER_ACTION_END: + if (fw_transfer->slot == 0 || + fw_transfer->slot == cci->fw.active_slot || + fw_transfer->slot > CXL_FW_SLOTS) { + return CXL_MBOX_FW_INVALID_SLOT; + } + + /* mark the slot used upon bg completion */ + break; + case CXL_FW_XFER_ACTION_INIT: + if (offset != 0) { + return CXL_MBOX_INVALID_INPUT; + } + + cci->fw.transferring = true; + cci->fw.prev_offset = offset; + cci->fw.prev_len = length; + break; + case CXL_FW_XFER_ACTION_CONTINUE: + cci->fw.prev_offset = offset; + cci->fw.prev_len = length; + break; + default: + return CXL_MBOX_INVALID_INPUT; + } + + if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL) { + cci->bg.runtime = 10 * 1000UL; + } else { + cci->bg.runtime = 2 * 1000UL; + } + /* keep relevant context for bg completion */ + cci->fw.curr_action = fw_transfer->action; + cci->fw.curr_slot = fw_transfer->slot; + *len_out = 0; + + return CXL_MBOX_BG_STARTED; +} + +static void __do_firmware_xfer(CXLCCI *cci) +{ + switch (cci->fw.curr_action) { + case CXL_FW_XFER_ACTION_FULL: + case CXL_FW_XFER_ACTION_END: + cci->fw.slot[cci->fw.curr_slot - 1] = true; + cci->fw.transferring = false; + break; + case CXL_FW_XFER_ACTION_INIT: + case CXL_FW_XFER_ACTION_CONTINUE: + time(&cci->fw.last_partxfer); + break; + default: + break; + } +} + +/* CXL r3.1 section 8.2.9.3.3: Activate FW (Opcode 0202h) */ +static CXLRetCode cmd_firmware_update_activate(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t action; + uint8_t slot; + } QEMU_PACKED *fw_activate = (void *)payload_in; + QEMU_BUILD_BUG_ON(sizeof(*fw_activate) != 0x2); + + if (fw_activate->slot == 0 || + fw_activate->slot == cci->fw.active_slot || + fw_activate->slot > CXL_FW_SLOTS) { + return CXL_MBOX_FW_INVALID_SLOT; + } + + /* ensure that an actual fw package is there */ + if (!cci->fw.slot[fw_activate->slot - 1]) { + return CXL_MBOX_FW_INVALID_SLOT; + } + + switch (fw_activate->action) { + case 0: /* online */ + cci->fw.active_slot = fw_activate->slot; + break; + case 1: /* reset */ + cci->fw.staged_slot = fw_activate->slot; + break; + default: + return CXL_MBOX_INVALID_INPUT; + } + + return CXL_MBOX_SUCCESS; +} + /* CXL r3.1 Section 8.2.9.4.1: Get Timestamp (Opcode 0300h) */ static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, uint8_t *payload_in, @@ -2494,6 +2676,10 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE }, [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", cmd_firmware_update_get_info, 0, 0 }, + [FIRMWARE_UPDATE][TRANSFER] = { "FIRMWARE_UPDATE_TRANSFER", + cmd_firmware_update_transfer, ~0, CXL_MBOX_BACKGROUND_OPERATION }, + [FIRMWARE_UPDATE][ACTIVATE] = { "FIRMWARE_UPDATE_ACTIVATE", + cmd_firmware_update_activate, 2, CXL_MBOX_BACKGROUND_OPERATION }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, @@ -2622,7 +2808,9 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, h == cmd_media_get_poison_list || h == cmd_media_inject_poison || h == cmd_media_clear_poison || - h == cmd_sanitize_overwrite) { + h == cmd_sanitize_overwrite || + h == cmd_firmware_update_transfer || + h == cmd_firmware_update_activate) { return CXL_MBOX_MEDIA_DISABLED; } } @@ -2667,6 +2855,9 @@ static void bg_timercb(void *opaque) cci->bg.complete_pct = 100; cci->bg.ret_code = ret; switch (cci->bg.opcode) { + case 0x0201: /* fw transfer */ + __do_firmware_xfer(cci); + break; case 0x4400: /* sanitize */ { CXLType3Dev *ct3d = CXL_TYPE3(cci->d); @@ -2738,6 +2929,10 @@ void cxl_init_cci(CXLCCI *cci, size_t payload_max) cci->bg.runtime = 0; cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, bg_timercb, cci); + + memset(&cci->fw, 0, sizeof(cci->fw)); + cci->fw.active_slot = 1; + cci->fw.slot[cci->fw.active_slot - 1] = true; } static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[256]) diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 5cae7159e6..fdd0f4e62b 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -181,6 +181,21 @@ typedef struct CXLCCI { uint64_t runtime; QEMUTimer *timer; } bg; + + /* firmware update */ + struct { + uint8_t active_slot; + uint8_t staged_slot; + bool slot[4]; + uint8_t curr_action; + uint8_t curr_slot; + /* handle partial transfers */ + bool transferring; + size_t prev_offset; + size_t prev_len; + time_t last_partxfer; + } fw; + size_t payload_max; /* Pointer to device hosting the CCI */ DeviceState *d; From e3f15732c479e6c4baa56af65f52d99fbfb5c417 Mon Sep 17 00:00:00 2001 From: Yi Liu <yi.l.liu@intel.com> Date: Sun, 7 Jul 2024 20:21:12 -0700 Subject: [PATCH 2297/2884] MAINTAINERS: Add myself as a VT-d reviewer Signed-off-by: Yi Liu <yi.l.liu@intel.com> Message-Id: <20240708032112.796339-1-yi.l.liu@intel.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index b7e9ced3e8..8ad64ff76b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3659,6 +3659,7 @@ F: tests/uefi-test-tools/ VT-d Emulation M: Michael S. Tsirkin <mst@redhat.com> R: Jason Wang <jasowang@redhat.com> +R: Yi Liu <yi.l.liu@intel.com> S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h From 98e77e3dd8dd6e7aa9a7dffa60f49c8c8a49d4e3 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Date: Mon, 8 Jul 2024 10:09:49 +0300 Subject: [PATCH 2298/2884] virtio-snd: add max size bounds check in input cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading input audio in the virtio-snd input callback, virtio_snd_pcm_in_cb(), we do not check whether the iov can actually fit the data buffer. This is because we use the buffer->size field as a total-so-far accumulator instead of byte-size-left like in TX buffers. This triggers an out of bounds write if the size of the virtio queue element is equal to virtio_snd_pcm_status, which makes the available space for audio data zero. This commit adds a check for reaching the maximum buffer size before attempting any writes. Reported-by: Zheyu Ma <zheyuma97@gmail.com> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2427 Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-Id: <virtio-snd-fuzz-2427-fix-v1-manos.pitsidianakis@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/audio/virtio-snd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 5993f4f040..e6432ac959 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -1261,7 +1261,7 @@ static void virtio_snd_pcm_in_cb(void *data, int available) { VirtIOSoundPCMStream *stream = data; VirtIOSoundPCMBuffer *buffer; - size_t size; + size_t size, max_size; WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { while (!QSIMPLEQ_EMPTY(&stream->queue)) { @@ -1275,7 +1275,12 @@ static void virtio_snd_pcm_in_cb(void *data, int available) continue; } + max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num); for (;;) { + if (buffer->size >= max_size) { + return_rx_buffer(stream, buffer); + break; + } size = AUD_read(stream->voice.in, buffer->data + buffer->size, MIN(available, (stream->params.period_bytes - From 9b6083465fb8311f2410615f8303a41f580a2a20 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Date: Thu, 11 Jul 2024 10:38:49 +0300 Subject: [PATCH 2299/2884] virtio-snd: check for invalid param shift operands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting the parameters of a PCM stream, we compute the bit flag with the format and rate values as shift operand to check if they are set in supported_formats and supported_rates. If the guest provides a format/rate value which when shifting 1 results in a value bigger than the number of bits in supported_formats/supported_rates, we must report an error. Previously, this ended up triggering the not reached assertions later when converting to internal QEMU values. Reported-by: Zheyu Ma <zheyuma97@gmail.com> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2416 Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-Id: <virtio-snd-fuzz-2416-fix-v1-manos.pitsidianakis@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/audio/virtio-snd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index e6432ac959..e5196aa4bb 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -282,11 +282,13 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, error_report("Number of channels is not supported."); return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); } - if (!(supported_formats & BIT(params->format))) { + if (BIT(params->format) > sizeof(supported_formats) || + !(supported_formats & BIT(params->format))) { error_report("Stream format is not supported."); return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); } - if (!(supported_rates & BIT(params->rate))) { + if (BIT(params->rate) > sizeof(supported_rates) || + !(supported_rates & BIT(params->rate))) { error_report("Stream rate is not supported."); return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); } From a3c8d7e38550c3d5a46e6fa94ffadfa625a4861d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= <clement.mathieu--drif@eviden.com> Date: Tue, 9 Jul 2024 14:26:08 +0000 Subject: [PATCH 2300/2884] intel_iommu: fix FRCD construction macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The constant must be unsigned, otherwise the two's complement overrides the other fields when a PASID is present. Fixes: 1b2b12376c8a ("intel-iommu: PASID support") Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> Reviewed-by: Yi Liu <yi.l.liu@intel.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Minwoo Im <minwoo.im@samsung.com> Message-Id: <20240709142557.317271-2-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/i386/intel_iommu_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index f8cf99bddf..cbc4030031 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -267,7 +267,7 @@ /* For the low 64-bit of 128-bit */ #define VTD_FRCD_FI(val) ((val) & ~0xfffULL) #define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40) -#define VTD_FRCD_PP(val) (((val) & 0x1) << 31) +#define VTD_FRCD_PP(val) (((val) & 0x1ULL) << 31) #define VTD_FRCD_IR_IDX(val) (((val) & 0xffffULL) << 48) /* DMA Remapping Fault Conditions */ From 3a23554f91c01cf75705a36a5eed3ebef6636d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= <clement.mathieu--drif@eviden.com> Date: Tue, 9 Jul 2024 14:26:09 +0000 Subject: [PATCH 2301/2884] intel_iommu: move VTD_FRCD_PV and VTD_FRCD_PP declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These 2 macros are for high 64-bit of the FRCD registers. Declarations have to be moved accordingly. Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> Reviewed-by: Minwoo Im <minwoo.im@samsung.com> Reviewed-by: Yi Liu <yi.l.liu@intel.com> Message-Id: <20240709142557.317271-3-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/i386/intel_iommu_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index cbc4030031..faea23e8d6 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -264,10 +264,10 @@ #define VTD_FRCD_FR(val) (((val) & 0xffULL) << 32) #define VTD_FRCD_SID_MASK 0xffffULL #define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK) -/* For the low 64-bit of 128-bit */ -#define VTD_FRCD_FI(val) ((val) & ~0xfffULL) #define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40) #define VTD_FRCD_PP(val) (((val) & 0x1ULL) << 31) +/* For the low 64-bit of 128-bit */ +#define VTD_FRCD_FI(val) ((val) & ~0xfffULL) #define VTD_FRCD_IR_IDX(val) (((val) & 0xffffULL) << 48) /* DMA Remapping Fault Conditions */ From d7258f7a250716231d23d5412dd6caf923936549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= <clement.mathieu--drif@eviden.com> Date: Tue, 9 Jul 2024 14:26:10 +0000 Subject: [PATCH 2302/2884] intel_iommu: fix type of the mask field in VTDIOTLBPageInvInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the below code, it can overflow as am can be larger than 8 according to the CH 6.5.2.3 IOTLB Invalidate. Use uint64_t to avoid overflows. Fixes: b5a280c00840 ("intel-iommu: add IOTLB using hash table") Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> Reviewed-by: Minwoo Im <minwoo.im@samsung.com> Reviewed-by: Yi Liu <yi.l.liu@intel.com> Message-Id: <20240709142557.317271-4-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/i386/intel_iommu_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index faea23e8d6..5f32c36943 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -436,7 +436,7 @@ struct VTDIOTLBPageInvInfo { uint16_t domain_id; uint32_t pasid; uint64_t addr; - uint8_t mask; + uint64_t mask; }; typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; From bb3a23d5b0b43bed3c9a6ecf5a6871e2871be883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= <clement.mathieu--drif@eviden.com> Date: Tue, 9 Jul 2024 14:26:10 +0000 Subject: [PATCH 2303/2884] intel_iommu: make type match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'level' field in vtd_iotlb_key is an unsigned integer. We don't need to store level as an int in vtd_lookup_iotlb. This is not an issue by itself, but using unsigned here seems cleaner. Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> Reviewed-by: Yi Liu <yi.l.liu@intel.com> Message-Id: <20240709142557.317271-5-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/i386/intel_iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 37c21a0aec..be0cb39b5c 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -358,7 +358,7 @@ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, { struct vtd_iotlb_key key; VTDIOTLBEntry *entry; - int level; + unsigned level; for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) { key.gfn = vtd_get_iotlb_gfn(addr, level); From c303aa0942589427b42192ef7cff75a79ef8646b Mon Sep 17 00:00:00 2001 From: Jonah Palmer <jonah.palmer@oracle.com> Date: Wed, 10 Jul 2024 08:55:14 -0400 Subject: [PATCH 2304/2884] virtio: Add bool to VirtQueueElement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the boolean 'in_order_filled' member to the VirtQueueElement structure. The use of this boolean will signify whether the element has been processed and is ready to be flushed (so long as the element is in-order). This boolean is used to support the VIRTIO_F_IN_ORDER feature. Reviewed-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20240710125522.4168043-2-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- include/hw/virtio/virtio.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 7512afbc84..fdc827f82e 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -69,6 +69,8 @@ typedef struct VirtQueueElement unsigned int ndescs; unsigned int out_num; unsigned int in_num; + /* Element has been processed (VIRTIO_F_IN_ORDER) */ + bool in_order_filled; hwaddr *in_addr; hwaddr *out_addr; struct iovec *in_sg; From 2256e8482b2bba88abcc734dbc6951b825773f0b Mon Sep 17 00:00:00 2001 From: Jonah Palmer <jonah.palmer@oracle.com> Date: Wed, 10 Jul 2024 08:55:15 -0400 Subject: [PATCH 2305/2884] virtio: virtqueue_pop - VIRTIO_F_IN_ORDER support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add VIRTIO_F_IN_ORDER feature support in virtqueue_split_pop and virtqueue_packed_pop. VirtQueueElements popped from the available/descritpor ring are added to the VirtQueue's used_elems array in-order and in the same fashion as they would be added the used and descriptor rings, respectively. This will allow us to keep track of the current order, what elements have been written, as well as an element's essential data after being processed. Reviewed-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20240710125522.4168043-3-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 583a224163..98eb601b09 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1505,7 +1505,7 @@ static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_nu static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) { - unsigned int i, head, max; + unsigned int i, head, max, idx; VRingMemoryRegionCaches *caches; MemoryRegionCache indirect_desc_cache; MemoryRegionCache *desc_cache; @@ -1629,6 +1629,13 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) elem->in_sg[i] = iov[out_num + i]; } + if (virtio_vdev_has_feature(vdev, VIRTIO_F_IN_ORDER)) { + idx = (vq->last_avail_idx - 1) % vq->vring.num; + vq->used_elems[idx].index = elem->index; + vq->used_elems[idx].len = elem->len; + vq->used_elems[idx].ndescs = elem->ndescs; + } + vq->inuse++; trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num); @@ -1762,6 +1769,13 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) elem->index = id; elem->ndescs = (desc_cache == &indirect_desc_cache) ? 1 : elem_entries; + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_IN_ORDER)) { + vq->used_elems[vq->last_avail_idx].index = elem->index; + vq->used_elems[vq->last_avail_idx].len = elem->len; + vq->used_elems[vq->last_avail_idx].ndescs = elem->ndescs; + } + vq->last_avail_idx += elem->ndescs; vq->inuse += elem->ndescs; From b44135daa3721778f4656ca3c15bd9b3060f1176 Mon Sep 17 00:00:00 2001 From: Jonah Palmer <jonah.palmer@oracle.com> Date: Wed, 10 Jul 2024 08:55:16 -0400 Subject: [PATCH 2306/2884] virtio: virtqueue_ordered_fill - VIRTIO_F_IN_ORDER support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add VIRTIO_F_IN_ORDER feature support for the virtqueue_fill operation. The goal of the virtqueue_ordered_fill operation when the VIRTIO_F_IN_ORDER feature has been negotiated is to search for this now-used element, set its length, and mark the element as filled in the VirtQueue's used_elems array. By marking the element as filled, it will indicate that this element has been processed and is ready to be flushed, so long as the element is in-order. Reviewed-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20240710125522.4168043-4-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 98eb601b09..0000a7b41c 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -872,6 +872,46 @@ static void virtqueue_packed_fill(VirtQueue *vq, const VirtQueueElement *elem, vq->used_elems[idx].ndescs = elem->ndescs; } +static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len) +{ + unsigned int i, steps, max_steps; + + i = vq->used_idx % vq->vring.num; + steps = 0; + /* + * We shouldn't need to increase 'i' by more than the distance + * between used_idx and last_avail_idx. + */ + max_steps = (vq->last_avail_idx - vq->used_idx) % vq->vring.num; + + /* Search for element in vq->used_elems */ + while (steps <= max_steps) { + /* Found element, set length and mark as filled */ + if (vq->used_elems[i].index == elem->index) { + vq->used_elems[i].len = len; + vq->used_elems[i].in_order_filled = true; + break; + } + + i += vq->used_elems[i].ndescs; + steps += vq->used_elems[i].ndescs; + + if (i >= vq->vring.num) { + i -= vq->vring.num; + } + } + + /* + * We should be able to find a matching VirtQueueElement in + * used_elems. If we don't, this is an error. + */ + if (steps >= max_steps) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s cannot fill buffer id %u\n", + __func__, vq->vdev->name, elem->index); + } +} + static void virtqueue_packed_fill_desc(VirtQueue *vq, const VirtQueueElement *elem, unsigned int idx, @@ -922,7 +962,9 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, return; } - if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_IN_ORDER)) { + virtqueue_ordered_fill(vq, elem, len); + } else if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { virtqueue_packed_fill(vq, elem, len, idx); } else { virtqueue_split_fill(vq, elem, len, idx); From 844619147c5110f335cbbbad51cda6a898c7ef33 Mon Sep 17 00:00:00 2001 From: Jonah Palmer <jonah.palmer@oracle.com> Date: Wed, 10 Jul 2024 08:55:17 -0400 Subject: [PATCH 2307/2884] virtio: virtqueue_ordered_flush - VIRTIO_F_IN_ORDER support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add VIRTIO_F_IN_ORDER feature support for the virtqueue_flush operation. The goal of the virtqueue_ordered_flush operation when the VIRTIO_F_IN_ORDER feature has been negotiated is to write elements to the used/descriptor ring in-order and then update used_idx. The function iterates through the VirtQueueElement used_elems array in-order starting at vq->used_idx. If the element is valid (filled), the element is written to the used/descriptor ring. This process continues until we find an invalid (not filled) element. For packed VQs, the first entry (at vq->used_idx) is written to the descriptor ring last so the guest doesn't see any invalid descriptors. If any elements were written, the used_idx is updated. Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20240710125522.4168043-5-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Eugenio Pérez <eperezma@redhat.com> --- hw/virtio/virtio.c | 71 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 0000a7b41c..397c261c3c 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1023,6 +1023,73 @@ static void virtqueue_packed_flush(VirtQueue *vq, unsigned int count) } } +static void virtqueue_ordered_flush(VirtQueue *vq) +{ + unsigned int i = vq->used_idx % vq->vring.num; + unsigned int ndescs = 0; + uint16_t old = vq->used_idx; + uint16_t new; + bool packed; + VRingUsedElem uelem; + + packed = virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED); + + if (packed) { + if (unlikely(!vq->vring.desc)) { + return; + } + } else if (unlikely(!vq->vring.used)) { + return; + } + + /* First expected in-order element isn't ready, nothing to do */ + if (!vq->used_elems[i].in_order_filled) { + return; + } + + /* Search for filled elements in-order */ + while (vq->used_elems[i].in_order_filled) { + /* + * First entry for packed VQs is written last so the guest + * doesn't see invalid descriptors. + */ + if (packed && i != vq->used_idx) { + virtqueue_packed_fill_desc(vq, &vq->used_elems[i], ndescs, false); + } else if (!packed) { + uelem.id = vq->used_elems[i].index; + uelem.len = vq->used_elems[i].len; + vring_used_write(vq, &uelem, i); + } + + vq->used_elems[i].in_order_filled = false; + ndescs += vq->used_elems[i].ndescs; + i += vq->used_elems[i].ndescs; + if (i >= vq->vring.num) { + i -= vq->vring.num; + } + } + + if (packed) { + virtqueue_packed_fill_desc(vq, &vq->used_elems[vq->used_idx], 0, true); + vq->used_idx += ndescs; + if (vq->used_idx >= vq->vring.num) { + vq->used_idx -= vq->vring.num; + vq->used_wrap_counter ^= 1; + vq->signalled_used_valid = false; + } + } else { + /* Make sure buffer is written before we update index. */ + smp_wmb(); + new = old + ndescs; + vring_used_idx_set(vq, new); + if (unlikely((int16_t)(new - vq->signalled_used) < + (uint16_t)(new - old))) { + vq->signalled_used_valid = false; + } + } + vq->inuse -= ndescs; +} + void virtqueue_flush(VirtQueue *vq, unsigned int count) { if (virtio_device_disabled(vq->vdev)) { @@ -1030,7 +1097,9 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count) return; } - if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_IN_ORDER)) { + virtqueue_ordered_flush(vq); + } else if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { virtqueue_packed_flush(vq, count); } else { virtqueue_split_flush(vq, count); From c03213fdc9d7b680cc575cd1e725750702a10b09 Mon Sep 17 00:00:00 2001 From: Jonah Palmer <jonah.palmer@oracle.com> Date: Wed, 10 Jul 2024 08:55:18 -0400 Subject: [PATCH 2308/2884] vhost,vhost-user: Add VIRTIO_F_IN_ORDER to vhost feature bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the VIRTIO_F_IN_ORDER feature across a variety of vhost devices. The inclusion of VIRTIO_F_IN_ORDER in the feature bits arrays for these devices ensures that the backend is capable of offering and providing support for this feature, and that it can be disabled if the backend does not support it. Acked-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20240710125522.4168043-6-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/block/vhost-user-blk.c | 1 + hw/net/vhost_net.c | 2 ++ hw/scsi/vhost-scsi.c | 1 + hw/scsi/vhost-user-scsi.c | 1 + hw/virtio/vhost-user-fs.c | 1 + hw/virtio/vhost-user-vsock.c | 1 + net/vhost-vdpa.c | 1 + 7 files changed, 8 insertions(+) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index fdbc30b9ce..5b7f46bbb0 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -51,6 +51,7 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 18898afe81..a788e6937e 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -48,6 +48,7 @@ static const int kernel_feature_bits[] = { VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VIRTIO_NET_F_HASH_REPORT, VHOST_INVALID_FEATURE_BIT @@ -78,6 +79,7 @@ static const int user_feature_bits[] = { VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_GUEST_USO4, diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 3d5fe0994d..49cff2a0cb 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -38,6 +38,7 @@ static const int kernel_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index cc91ade525..55e4be5b34 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -36,6 +36,7 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index ae48cc1c96..32ee7f496d 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -33,6 +33,7 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 802b44a07d..da3b0e0229 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -21,6 +21,7 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index daa38428c5..03457ead66 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -62,6 +62,7 @@ const int vdpa_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, VIRTIO_F_VERSION_1, + VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, From 0b0bb34f342f04ab8255a64edb7c6aec7105dc94 Mon Sep 17 00:00:00 2001 From: Jonah Palmer <jonah.palmer@oracle.com> Date: Wed, 10 Jul 2024 08:55:19 -0400 Subject: [PATCH 2309/2884] virtio: Add VIRTIO_F_IN_ORDER property definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the virtio device property definitions to include the VIRTIO_F_IN_ORDER feature. The default state of this feature is disabled, allowing it to be explicitly enabled where it's supported. Acked-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Jonah Palmer <jonah.palmer@oracle.com> Message-Id: <20240710125522.4168043-7-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- include/hw/virtio/virtio.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index fdc827f82e..d2a1938757 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -373,7 +373,9 @@ typedef struct VirtIORNGConf VirtIORNGConf; DEFINE_PROP_BIT64("packed", _state, _field, \ VIRTIO_F_RING_PACKED, false), \ DEFINE_PROP_BIT64("queue_reset", _state, _field, \ - VIRTIO_F_RING_RESET, true) + VIRTIO_F_RING_RESET, true), \ + DEFINE_PROP_BIT64("in_order", _state, _field, \ + VIRTIO_F_IN_ORDER, false) hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); bool virtio_queue_enabled_legacy(VirtIODevice *vdev, int n); From 99d7c1b99a3fdb69213c09da6b7614243f877bee Mon Sep 17 00:00:00 2001 From: Stefano Garzarella <sgarzare@redhat.com> Date: Fri, 12 Jul 2024 17:38:57 +0200 Subject: [PATCH 2310/2884] contrib/vhost-user-blk: fix overflowing expression Coverity reported: >>> CID 1549454: Integer handling issues (OVERFLOW_BEFORE_WIDEN) >>> Potentially overflowing expression "le32_to_cpu(desc->num_sectors) << 9" with type "uint32_t" (32 bits, unsigned) is evaluated using 32-bit arithmetic, and then used in a context that expects an expression of type "uint64_t" (64 bits, unsigned). 199 le32_to_cpu(desc->num_sectors) << 9 }; Coverity noticed this issue after commit ab04420c3 ("contrib/vhost-user-*: use QEMU bswap helper functions"), but it was pre-existing and introduced from the beginning by commit caa1ee4313 ("vhost-user-blk: add discard/write zeroes features support"). Explicitly cast the 32-bit value before the shift to fix this issue. Fixes: Coverity CID 1549454 Fixes: 5ab04420c3 ("contrib/vhost-user-*: use QEMU bswap helper functions") Fixes: caa1ee4313 ("vhost-user-blk: add discard/write zeroes features support") Cc: changpeng.liu@intel.com Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> Message-Id: <20240712153857.207440-1-sgarzare@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- contrib/vhost-user-blk/vhost-user-blk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 9492146855..6cc18a1c04 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -196,7 +196,7 @@ vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt, VubDev *vdev_blk = req->vdev_blk; desc = buf; uint64_t range[2] = { le64_to_cpu(desc->sector) << 9, - le32_to_cpu(desc->num_sectors) << 9 }; + (uint64_t)le32_to_cpu(desc->num_sectors) << 9 }; if (type == VIRTIO_BLK_T_DISCARD) { if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) { g_free(buf); From b22bf37dffe47993d37f2809cc16b7f4e99c619e Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Fri, 8 Dec 2023 14:09:09 -0500 Subject: [PATCH 2311/2884] tests/avocado/boot_xen.py: merge base classes While it's a good practice to have reusable base classes, in this specific case there's no other user of the BootXenBase class. By unifying the class used in this test, we can improve readability and have the opportunity to add some future improvements in a clearer fashion. Signed-off-by: Cleber Rosa <crosa@redhat.com> Message-ID: <20231208190911.102879-9-crosa@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/boot_xen.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py index fc2faeedb5..f80cbcb8fb 100644 --- a/tests/avocado/boot_xen.py +++ b/tests/avocado/boot_xen.py @@ -17,7 +17,7 @@ from avocado_qemu import wait_for_console_pattern from boot_linux_console import LinuxKernelTest -class BootXenBase(LinuxKernelTest): +class BootXen(LinuxKernelTest): """ Boots a Xen hypervisor with a Linux DomU kernel. """ @@ -59,9 +59,6 @@ class BootXenBase(LinuxKernelTest): console_pattern = 'VFS: Cannot open root device' wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") - -class BootXen(BootXenBase): - def test_arm64_xen_411_and_dom0(self): """ :avocado: tags=arch:aarch64 From 9e6e7d814c941d167621797f6c50aca3628913e2 Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Fri, 8 Dec 2023 14:09:10 -0500 Subject: [PATCH 2312/2884] tests/avocado/boot_xen.py: unify tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because all tests share the same tags, it's possible to have all of them at the class level. Signed-off-by: Cleber Rosa <crosa@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20231208190911.102879-10-crosa@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/boot_xen.py | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py index f80cbcb8fb..f4b63c1ef2 100644 --- a/tests/avocado/boot_xen.py +++ b/tests/avocado/boot_xen.py @@ -20,6 +20,11 @@ from boot_linux_console import LinuxKernelTest class BootXen(LinuxKernelTest): """ Boots a Xen hypervisor with a Linux DomU kernel. + + :avocado: tags=arch:aarch64 + :avocado: tags=accel:tcg + :avocado: tags=cpu:cortex-a57 + :avocado: tags=machine:virt """ timeout = 90 @@ -60,13 +65,6 @@ class BootXen(LinuxKernelTest): wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") def test_arm64_xen_411_and_dom0(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' 'download?path=%2F&files=' @@ -78,13 +76,6 @@ class BootXen(LinuxKernelTest): self.launch_xen(xen_path) def test_arm64_xen_414_and_dom0(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' 'download?path=%2F&files=' @@ -96,13 +87,6 @@ class BootXen(LinuxKernelTest): self.launch_xen(xen_path) def test_arm64_xen_415_and_dom0(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - xen_url = ('https://fileserver.linaro.org/' 's/JSsewXGZ6mqxPr5/download' '?path=%2F&files=xen-upstream-4.15-unstable.deb') From 1b88da0af11f15bf8ebb7d933b458533f72899a5 Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Fri, 8 Dec 2023 14:09:11 -0500 Subject: [PATCH 2313/2884] tests/avocado/boot_xen.py: use class attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than defining a single use variable, let's just use the class attribute directly. Signed-off-by: Cleber Rosa <crosa@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20231208190911.102879-11-crosa@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/boot_xen.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py index f4b63c1ef2..f29bc58b9e 100644 --- a/tests/avocado/boot_xen.py +++ b/tests/avocado/boot_xen.py @@ -50,11 +50,10 @@ class BootXen(LinuxKernelTest): self.vm.set_console() - xen_command_line = self.XEN_COMMON_COMMAND_LINE self.vm.add_args('-machine', 'virtualization=on', '-m', '768', '-kernel', xen_path, - '-append', xen_command_line, + '-append', self.XEN_COMMON_COMMAND_LINE, '-device', 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' % (kernel_path)) From 1f1736a8f16d27a99abd371caaeedc10e6411d15 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann <kraxel@redhat.com> Date: Thu, 30 May 2024 13:10:29 +0200 Subject: [PATCH 2314/2884] vnc: increase max display size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's 2024. 4k display resolutions are a thing these days. Raise width and height limits of the qemu vnc server. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1596 Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240530111029.1726329-1-kraxel@redhat.com> --- ui/vnc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/vnc.h b/ui/vnc.h index 4521dc88f7..e5fa2efa3e 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -81,8 +81,8 @@ typedef void VncSendHextileTile(VncState *vs, /* VNC_MAX_WIDTH must be a multiple of VNC_DIRTY_PIXELS_PER_BIT. */ -#define VNC_MAX_WIDTH ROUND_UP(2560, VNC_DIRTY_PIXELS_PER_BIT) -#define VNC_MAX_HEIGHT 2048 +#define VNC_MAX_WIDTH ROUND_UP(5120, VNC_DIRTY_PIXELS_PER_BIT) +#define VNC_MAX_HEIGHT 2160 /* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */ #define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT) From d84ed5d2d4d495845ed9c709a576e847f9d91746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Thu, 18 Jul 2024 10:45:09 +0100 Subject: [PATCH 2315/2884] testing: bump to latest libvirt-ci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This brings in the latest python mappings for the BSD updates. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-2-alex.bennee@linaro.org> --- .gitlab-ci.d/cirrus/freebsd-13.vars | 2 +- tests/lcitool/libvirt-ci | 2 +- tests/vm/generated/freebsd.json | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars index 3785afca36..29ab9645f9 100644 --- a/.gitlab-ci.d/cirrus/freebsd-13.vars +++ b/.gitlab-ci.d/cirrus/freebsd-13.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-tomli py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-sphinx py311-sphinx_rtd_theme py311-tomli py311-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index 0e9490cebc..789b4601bc 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit 0e9490cebc726ef772b6c9e27dac32e7ae99f9b2 +Subproject commit 789b4601bce4e01f43fdb6ad4ce5ab4e46674440 diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 2d5895ebed..2a361cecd0 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -51,13 +51,13 @@ "pixman", "pkgconf", "png", - "py39-numpy", - "py39-pillow", - "py39-pip", - "py39-sphinx", - "py39-sphinx_rtd_theme", - "py39-tomli", - "py39-yaml", + "py311-numpy", + "py311-pillow", + "py311-pip", + "py311-sphinx", + "py311-sphinx_rtd_theme", + "py311-tomli", + "py311-yaml", "python3", "rpm2cpio", "sdl2", From 955ad1121d7df678603336f4af6c8e774f657558 Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Thu, 18 Jul 2024 10:45:10 +0100 Subject: [PATCH 2316/2884] tests/avocado: Remove non-working sparc leon3 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test has been marked as broken more than 4 years ago, and so far nobody ever cared to fix it. Thus let's simply remove it now ... if somebody ever needs it again, they can restore the file from an older version of QEMU. Signed-off-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Clément Chigot <chigot@adacore.com> Acked-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240710111755.60584-1-thuth@redhat.com> [AJB: fix MAINTAINERS] Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-3-alex.bennee@linaro.org> --- MAINTAINERS | 1 - tests/avocado/machine_sparc_leon3.py | 37 ---------------------------- 2 files changed, 38 deletions(-) delete mode 100644 tests/avocado/machine_sparc_leon3.py diff --git a/MAINTAINERS b/MAINTAINERS index 7d9811458c..d5ff6c2498 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1727,7 +1727,6 @@ S: Maintained F: hw/sparc/leon3.c F: hw/*/grlib* F: include/hw/*/grlib* -F: tests/avocado/machine_sparc_leon3.py S390 Machines ------------- diff --git a/tests/avocado/machine_sparc_leon3.py b/tests/avocado/machine_sparc_leon3.py deleted file mode 100644 index e61b223185..0000000000 --- a/tests/avocado/machine_sparc_leon3.py +++ /dev/null @@ -1,37 +0,0 @@ -# Functional test that boots a Leon3 machine and checks its serial console. -# -# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado import skip - - -class Leon3Machine(QemuSystemTest): - - timeout = 60 - - @skip("Test currently broken") - # A Window Underflow exception occurs before booting the kernel, - # and QEMU exit calling cpu_abort(), which makes this test to fail. - def test_leon3_helenos_uimage(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:leon3_generic - :avocado: tags=binfmt:uimage - """ - kernel_url = ('http://www.helenos.org/releases/' - 'HelenOS-0.6.0-sparc32-leon3.bin') - kernel_hash = 'a88c9cfdb8430c66650e5290a08765f9bf049a30' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path) - - self.vm.launch() - - wait_for_console_pattern(self, 'Copyright (c) 2001-2014 HelenOS project') - wait_for_console_pattern(self, 'Booting the kernel ...') From e8122a7118eb22ace9e60c91bfbf2d4bbfc6f15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Thu, 18 Jul 2024 10:45:11 +0100 Subject: [PATCH 2317/2884] gdbstub: Re-factor gdb command extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reported a memory leak (CID 1549757) in this code and its admittedly rather clumsy handling of extending the command table. Instead of handing over a full array of the commands lets use the lighter weight GPtrArray and simply test for the presence of each entry as we go. This avoids complications of transferring ownership of arrays and keeps the final command entries as static entries in the target code. Cc: Akihiko Odaki <akihiko.odaki@daynix.com> Cc: Gustavo Bueno Romero <gustavo.romero@linaro.org> Cc: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Gustavo Romero <gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-4-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 141 +++++++++++++++++++++---------------- include/gdbstub/commands.h | 19 +++-- target/arm/gdbstub.c | 16 ++--- target/arm/gdbstub64.c | 11 ++- target/arm/internals.h | 4 +- 5 files changed, 105 insertions(+), 86 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index b9ad0a063e..b7be8e5a44 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1614,18 +1614,21 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) gdb_put_strbuf(); } -static char *extended_qsupported_features; -void gdb_extend_qsupported_features(char *qsupported_features) -{ - /* - * We don't support different sets of CPU gdb features on different CPUs yet - * so assert the feature strings are the same on all CPUs, or is set only - * once (1 CPU). - */ - g_assert(extended_qsupported_features == NULL || - g_strcmp0(extended_qsupported_features, qsupported_features) == 0); - extended_qsupported_features = qsupported_features; +static char **extra_query_flags; + +void gdb_extend_qsupported_features(char *qflags) +{ + if (!extra_query_flags) { + extra_query_flags = g_new0(char *, 2); + extra_query_flags[0] = g_strdup(qflags); + } else if (!g_strv_contains((const gchar * const *) extra_query_flags, + qflags)) { + int len = g_strv_length(extra_query_flags); + extra_query_flags = g_realloc_n(extra_query_flags, len + 2, + sizeof(char *)); + extra_query_flags[len] = g_strdup(qflags); + } } static void handle_query_supported(GArray *params, void *user_ctx) @@ -1668,8 +1671,11 @@ static void handle_query_supported(GArray *params, void *user_ctx) g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); - if (extended_qsupported_features) { - g_string_append(gdbserver_state.str_buf, extended_qsupported_features); + if (extra_query_flags) { + int extras = g_strv_length(extra_query_flags); + for (int i = 0; i < extras; i++) { + g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); + } } gdb_put_strbuf(); @@ -1753,39 +1759,58 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { }, }; -/* Compares if a set of command parsers is equal to another set of parsers. */ -static bool cmp_cmds(GdbCmdParseEntry *c, GdbCmdParseEntry *d, int size) +/** + * extend_table() - extend one of the command tables + * @table: the command table to extend (or NULL) + * @extensions: a list of GdbCmdParseEntry pointers + * + * The entries themselves should be pointers to static const + * GdbCmdParseEntry entries. If the entry is already in the table we + * skip adding it again. + * + * Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry + */ +static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) { - for (int i = 0; i < size; i++) { - if (!(c[i].handler == d[i].handler && - g_strcmp0(c[i].cmd, d[i].cmd) == 0 && - c[i].cmd_startswith == d[i].cmd_startswith && - g_strcmp0(c[i].schema, d[i].schema) == 0)) { + if (!table) { + table = g_ptr_array_new(); + } - /* Sets are different. */ - return false; + for (int i = 0; i < extensions->len; i++) { + gpointer entry = g_ptr_array_index(extensions, i); + if (!g_ptr_array_find(table, entry, NULL)) { + g_ptr_array_add(table, entry); } } - /* Sets are equal, i.e. contain the same command parsers. */ - return true; + return table; } -static GdbCmdParseEntry *extended_query_table; -static int extended_query_table_size; -void gdb_extend_query_table(GdbCmdParseEntry *table, int size) +/** + * process_extended_table() - run through an extended command table + * @table: the command table to check + * @data: parameters + * + * returns true if the command was found and executed + */ +static bool process_extended_table(GPtrArray *table, const char *data) { - /* - * We don't support different sets of CPU gdb features on different CPUs yet - * so assert query table is the same on all CPUs, or is set only once - * (1 CPU). - */ - g_assert(extended_query_table == NULL || - (extended_query_table_size == size && - cmp_cmds(extended_query_table, table, size))); + for (int i = 0; i < table->len; i++) { + const GdbCmdParseEntry *entry = g_ptr_array_index(table, i); + if (process_string_cmd(data, entry, 1)) { + return true; + } + } + return false; +} - extended_query_table = table; - extended_query_table_size = size; + +/* Ptr to GdbCmdParseEntry */ +static GPtrArray *extended_query_table; + +void gdb_extend_query_table(GPtrArray *new_queries) +{ + extended_query_table = extend_table(extended_query_table, new_queries); } static const GdbCmdParseEntry gdb_gen_query_table[] = { @@ -1880,20 +1905,12 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { #endif }; -static GdbCmdParseEntry *extended_set_table; -static int extended_set_table_size; -void gdb_extend_set_table(GdbCmdParseEntry *table, int size) -{ - /* - * We don't support different sets of CPU gdb features on different CPUs yet - * so assert set table is the same on all CPUs, or is set only once (1 CPU). - */ - g_assert(extended_set_table == NULL || - (extended_set_table_size == size && - cmp_cmds(extended_set_table, table, size))); +/* Ptr to GdbCmdParseEntry */ +static GPtrArray *extended_set_table; - extended_set_table = table; - extended_set_table_size = size; +void gdb_extend_set_table(GPtrArray *new_set) +{ + extended_set_table = extend_table(extended_set_table, new_set); } static const GdbCmdParseEntry gdb_gen_set_table[] = { @@ -1924,26 +1941,28 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = { static void handle_gen_query(GArray *params, void *user_ctx) { + const char *data; + if (!params->len) { return; } - if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, + data = gdb_get_cmd_param(params, 0)->data; + + if (process_string_cmd(data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, + if (process_string_cmd(data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { return; } if (extended_query_table && - process_string_cmd(gdb_get_cmd_param(params, 0)->data, - extended_query_table, - extended_query_table_size)) { + process_extended_table(extended_query_table, data)) { return; } @@ -1953,26 +1972,28 @@ static void handle_gen_query(GArray *params, void *user_ctx) static void handle_gen_set(GArray *params, void *user_ctx) { + const char *data; + if (!params->len) { return; } - if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, + data = gdb_get_cmd_param(params, 0)->data; + + if (process_string_cmd(data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_get_cmd_param(params, 0)->data, + if (process_string_cmd(data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { return; } if (extended_set_table && - process_string_cmd(gdb_get_cmd_param(params, 0)->data, - extended_set_table, - extended_set_table_size)) { + process_extended_table(extended_set_table, data)) { return; } diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h index f3058f9dda..40f0514fe9 100644 --- a/include/gdbstub/commands.h +++ b/include/gdbstub/commands.h @@ -74,23 +74,28 @@ int gdb_put_packet(const char *buf); /** * gdb_extend_query_table() - Extend query table. - * @table: The table with the additional query packet handlers. - * @size: The number of handlers to be added. + * @table: GPtrArray of GdbCmdParseEntry entries. + * + * The caller should free @table afterwards */ -void gdb_extend_query_table(GdbCmdParseEntry *table, int size); +void gdb_extend_query_table(GPtrArray *table); /** * gdb_extend_set_table() - Extend set table. - * @table: The table with the additional set packet handlers. - * @size: The number of handlers to be added. + * @table: GPtrArray of GdbCmdParseEntry entries. + * + * The caller should free @table afterwards */ -void gdb_extend_set_table(GdbCmdParseEntry *table, int size); +void gdb_extend_set_table(GPtrArray *table); /** * gdb_extend_qsupported_features() - Extend the qSupported features string. * @qsupported_features: The additional qSupported feature(s) string. The string * should start with a semicolon and, if there are more than one feature, the - * features should be separate by a semiocolon. + * features should be separate by a semicolon. + * + * The caller should free @qsupported_features afterwards if + * dynamically allocated. */ void gdb_extend_qsupported_features(char *qsupported_features); diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index c3a9b5eb1e..554b8736bb 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -477,11 +477,9 @@ static GDBFeature *arm_gen_dynamic_m_secextreg_feature(CPUState *cs, void arm_cpu_register_gdb_commands(ARMCPU *cpu) { - GArray *query_table = - g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry)); - GArray *set_table = - g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry)); - GString *qsupported_features = g_string_new(NULL); + g_autoptr(GPtrArray) query_table = g_ptr_array_new(); + g_autoptr(GPtrArray) set_table = g_ptr_array_new(); + g_autoptr(GString) qsupported_features = g_string_new(NULL); if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { #ifdef TARGET_AARCH64 @@ -492,16 +490,12 @@ void arm_cpu_register_gdb_commands(ARMCPU *cpu) /* Set arch-specific handlers for 'q' commands. */ if (query_table->len) { - gdb_extend_query_table(&g_array_index(query_table, - GdbCmdParseEntry, 0), - query_table->len); + gdb_extend_query_table(query_table); } /* Set arch-specific handlers for 'Q' commands. */ if (set_table->len) { - gdb_extend_set_table(&g_array_index(set_table, - GdbCmdParseEntry, 0), - set_table->len); + gdb_extend_set_table(set_table); } /* Set arch-specific qSupported feature. */ diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 2e2bc2700b..c8cef8cbc0 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -564,7 +564,7 @@ enum Command { NUM_CMDS }; -static GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = { +static const GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = { [qMemTags] = { .handler = handle_q_memtag, .cmd_startswith = true, @@ -590,17 +590,16 @@ static GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = { #endif /* CONFIG_USER_ONLY */ void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *qsupported, - GArray *qtable, GArray *stable) + GPtrArray *qtable, GPtrArray *stable) { #ifdef CONFIG_USER_ONLY /* MTE */ if (cpu_isar_feature(aa64_mte, cpu)) { g_string_append(qsupported, ";memory-tagging+"); - g_array_append_val(qtable, cmd_handler_table[qMemTags]); - g_array_append_val(qtable, cmd_handler_table[qIsAddressTagged]); - - g_array_append_val(stable, cmd_handler_table[QMemTags]); + g_ptr_array_add(qtable, (gpointer) &cmd_handler_table[qMemTags]); + g_ptr_array_add(qtable, (gpointer) &cmd_handler_table[qIsAddressTagged]); + g_ptr_array_add(stable, (gpointer) &cmd_handler_table[QMemTags]); } #endif } diff --git a/target/arm/internals.h b/target/arm/internals.h index da22d04121..757b1fae92 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -359,8 +359,8 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); void arm_cpu_register_gdb_commands(ARMCPU *cpu); -void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, GArray *, - GArray *); +void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, + GPtrArray *, GPtrArray *); void arm_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, From 58fc249d9ebd7c6ed979cbf039ccfa93d1bae2b9 Mon Sep 17 00:00:00 2001 From: Simon Hamelin <simon.hamelin@grenoble-inp.org> Date: Thu, 18 Jul 2024 10:45:12 +0100 Subject: [PATCH 2318/2884] plugins/stoptrigger: TCG plugin to stop execution under conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new plugin allows to stop emulation using conditions on the emulation state. By setting this plugin arguments, it is possible to set an instruction count limit and/or trigger address(es) to stop at. The code returned at emulation exit can be customized. This plugin demonstrates how someone could stop QEMU execution. It could be used for research purposes to launch some code and deterministically stop it and understand where its execution flow went. Co-authored-by: Alexandre Iooss <erdnaxe@crans.org> Signed-off-by: Simon Hamelin <simon.hamelin@grenoble-inp.org> Signed-off-by: Alexandre Iooss <erdnaxe@crans.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240715081521.19122-2-simon.hamelin@grenoble-inp.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-5-alex.bennee@linaro.org> --- contrib/plugins/Makefile | 1 + contrib/plugins/stoptrigger.c | 151 ++++++++++++++++++++++++++++++++++ docs/devel/tcg-plugins.rst | 22 +++++ 3 files changed, 174 insertions(+) create mode 100644 contrib/plugins/stoptrigger.c diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 449ead1130..98a89d5c40 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -28,6 +28,7 @@ NAMES += hwprofile NAMES += cache NAMES += drcov NAMES += ips +NAMES += stoptrigger ifeq ($(CONFIG_WIN32),y) SO_SUFFIX := .dll diff --git a/contrib/plugins/stoptrigger.c b/contrib/plugins/stoptrigger.c new file mode 100644 index 0000000000..03ee22f4c6 --- /dev/null +++ b/contrib/plugins/stoptrigger.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2024, Simon Hamelin <simon.hamelin@grenoble-inp.org> + * + * Stop execution once a given address is reached or if the + * count of executed instructions reached a specified limit + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <assert.h> +#include <glib.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> + +#include <qemu-plugin.h> + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +/* Scoreboard to track executed instructions count */ +typedef struct { + uint64_t insn_count; +} InstructionsCount; +static struct qemu_plugin_scoreboard *insn_count_sb; +static qemu_plugin_u64 insn_count; + +static uint64_t icount; +static int icount_exit_code; + +static bool exit_on_icount; +static bool exit_on_address; + +/* Map trigger addresses to exit code */ +static GHashTable *addrs_ht; + +static void exit_emulation(int return_code, char *message) +{ + qemu_plugin_outs(message); + g_free(message); + exit(return_code); +} + +static void exit_icount_reached(unsigned int cpu_index, void *udata) +{ + uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n", + insn_vaddr); + + exit_emulation(icount_exit_code, msg); +} + +static void exit_address_reached(unsigned int cpu_index, void *udata) +{ + uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr); + int exit_code; + + exit_code = GPOINTER_TO_INT( + g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr))); + + exit_emulation(exit_code, msg); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t tb_n = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < tb_n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn)); + + if (exit_on_icount) { + /* Increment and check scoreboard for each instruction */ + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_cond_cb( + insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr); + } + + if (exit_on_address) { + if (g_hash_table_contains(addrs_ht, insn_vaddr)) { + /* Exit triggered by address */ + qemu_plugin_register_vcpu_insn_exec_cb( + insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, + insn_vaddr); + } + } + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_hash_table_destroy(addrs_ht); + qemu_plugin_scoreboard_free(insn_count_sb); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + addrs_ht = g_hash_table_new(NULL, g_direct_equal); + + insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount)); + insn_count = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, insn_count); + + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "icount") == 0) { + g_auto(GStrv) icount_tokens = g_strsplit(tokens[1], ":", 2); + icount = g_ascii_strtoull(icount_tokens[0], NULL, 0); + if (icount < 1 || g_strrstr(icount_tokens[0], "-") != NULL) { + fprintf(stderr, + "icount parsing failed: '%s' must be a positive " + "integer\n", + icount_tokens[0]); + return -1; + } + if (icount_tokens[1]) { + icount_exit_code = g_ascii_strtoull(icount_tokens[1], NULL, 0); + } + exit_on_icount = true; + } else if (g_strcmp0(tokens[0], "addr") == 0) { + g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2); + uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); + int exit_code = 0; + if (addr_tokens[1]) { + exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); + } + g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr), + GINT_TO_POINTER(exit_code)); + exit_on_address = true; + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + if (!exit_on_icount && !exit_on_address) { + fprintf(stderr, "'icount' or 'addr' argument missing\n"); + return -1; + } + + /* Register translation block and exit callbacks */ + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index f7d7b9e3a4..954623f9bf 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -642,6 +642,28 @@ The plugin has a number of arguments, all of them are optional: configuration arguments implies ``l2=on``. (default: N = 2097152 (2MB), B = 64, A = 16) +- contrib/plugins/stoptrigger.c + +The stoptrigger plugin allows to setup triggers to stop emulation. +It can be used for research purposes to launch some code and precisely stop it +and understand where its execution flow went. + +Two types of triggers can be configured: a count of instructions to stop at, +or an address to stop at. Multiple triggers can be set at once. + +By default, QEMU will exit with return code 0. A custom return code can be +configured for each trigger using ``:CODE`` syntax. + +For example, to stop at the 20-th instruction with return code 41, at address +0xd4 with return code 0 or at address 0xd8 with return code 42:: + + $ qemu-system-aarch64 $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libstoptrigger.so,icount=20:41,addr=0xd4,addr=0xd8:42 -d plugin + +The plugin will log the reason of exit, for example:: + + 0xd4 reached, exiting + Plugin API ========== From 94ae227e15bd5ede18a92947412ef47c2b89e269 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <pierrick.bouvier@linaro.org> Date: Thu, 18 Jul 2024 10:45:13 +0100 Subject: [PATCH 2319/2884] plugins: fix mem callback array size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit data was correctly copied, but size of array was not set (g_array_sized_new only reserves memory, but does not set size). As a result, callbacks were not called for code path relying on plugin_register_vcpu_mem_cb(). Found when trying to trigger mem access callbacks for atomic instructions. Reviewed-by: Xingtao Yao <yaoxt.fnst@fujitsu.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240706191335.878142-2-pierrick.bouvier@linaro.org> Message-Id: <20240718094523.1198645-6-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index b6bae32b99..ec89a085b4 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -85,8 +85,7 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, len = insn->mem_cbs->len; arr = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), len); - memcpy(arr->data, insn->mem_cbs->data, - len * sizeof(struct qemu_plugin_dyn_cb)); + g_array_append_vals(arr, insn->mem_cbs->data, len); qemu_plugin_add_dyn_cb_arr(arr); tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, From 2d4f2c8cde67820ce3f6138313fa2d6455681384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Thu, 18 Jul 2024 10:45:14 +0100 Subject: [PATCH 2320/2884] tests/plugins: use qemu_plugin_outs for inline stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using bare printf's in plugins is perfectly acceptable but they do rather mess up the output of "make check-tcg". Convert the printfs to use g_string and then output with the plugin output helper which will already be captured to .pout files by the test harness. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-7-alex.bennee@linaro.org> --- tests/plugin/inline.c | 58 ++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c index cd63827b7d..73dde99578 100644 --- a/tests/plugin/inline.c +++ b/tests/plugin/inline.c @@ -71,10 +71,12 @@ static void stats_insn(void) const uint64_t cond_track_left = qemu_plugin_u64_sum(insn_cond_track_count); const uint64_t conditional = cond_num_trigger * cond_trigger_limit + cond_track_left; - printf("insn: %" PRIu64 "\n", expected); - printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu); - printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); - printf("insn: %" PRIu64 " (cond cb)\n", conditional); + g_autoptr(GString) stats = g_string_new(""); + g_string_append_printf(stats, "insn: %" PRIu64 "\n", expected); + g_string_append_printf(stats, "insn: %" PRIu64 " (per vcpu)\n", per_vcpu); + g_string_append_printf(stats, "insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_string_append_printf(stats, "insn: %" PRIu64 " (cond cb)\n", conditional); + qemu_plugin_outs(stats->str); g_assert(expected > 0); g_assert(per_vcpu == expected); g_assert(inl_per_vcpu == expected); @@ -91,10 +93,12 @@ static void stats_tb(void) const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count); const uint64_t conditional = cond_num_trigger * cond_trigger_limit + cond_track_left; - printf("tb: %" PRIu64 "\n", expected); - printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu); - printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); - printf("tb: %" PRIu64 " (conditional cb)\n", conditional); + g_autoptr(GString) stats = g_string_new(""); + g_string_append_printf(stats, "tb: %" PRIu64 "\n", expected); + g_string_append_printf(stats, "tb: %" PRIu64 " (per vcpu)\n", per_vcpu); + g_string_append_printf(stats, "tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_string_append_printf(stats, "tb: %" PRIu64 " (conditional cb)\n", conditional); + qemu_plugin_outs(stats->str); g_assert(expected > 0); g_assert(per_vcpu == expected); g_assert(inl_per_vcpu == expected); @@ -107,9 +111,11 @@ static void stats_mem(void) const uint64_t per_vcpu = qemu_plugin_u64_sum(count_mem); const uint64_t inl_per_vcpu = qemu_plugin_u64_sum(count_mem_inline); - printf("mem: %" PRIu64 "\n", expected); - printf("mem: %" PRIu64 " (per vcpu)\n", per_vcpu); - printf("mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_autoptr(GString) stats = g_string_new(""); + g_string_append_printf(stats, "mem: %" PRIu64 "\n", expected); + g_string_append_printf(stats, "mem: %" PRIu64 " (per vcpu)\n", per_vcpu); + g_string_append_printf(stats, "mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + qemu_plugin_outs(stats->str); g_assert(expected > 0); g_assert(per_vcpu == expected); g_assert(inl_per_vcpu == expected); @@ -118,6 +124,7 @@ static void stats_mem(void) static void plugin_exit(qemu_plugin_id_t id, void *udata) { const unsigned int num_cpus = qemu_plugin_num_vcpus(); + g_autoptr(GString) stats = g_string_new(""); g_assert(num_cpus == max_cpu_index + 1); for (int i = 0; i < num_cpus ; ++i) { @@ -135,20 +142,21 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) qemu_plugin_u64_get(insn_cond_num_trigger, i); const uint64_t insn_cond_left = qemu_plugin_u64_get(insn_cond_track_count, i); - printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 - ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 - ") | " - "insn (%" PRIu64 ", %" PRIu64 - ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 - ") | " - "mem (%" PRIu64 ", %" PRIu64 ")" - "\n", - i, - tb, tb_inline, - tb_cond_trigger, cond_trigger_limit, tb_cond_left, - insn, insn_inline, - insn_cond_trigger, cond_trigger_limit, insn_cond_left, - mem, mem_inline); + g_string_printf(stats, "cpu %d: tb (%" PRIu64 ", %" PRIu64 + ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 + ") | " + "insn (%" PRIu64 ", %" PRIu64 + ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 + ") | " + "mem (%" PRIu64 ", %" PRIu64 ")" + "\n", + i, + tb, tb_inline, + tb_cond_trigger, cond_trigger_limit, tb_cond_left, + insn, insn_inline, + insn_cond_trigger, cond_trigger_limit, insn_cond_left, + mem, mem_inline); + qemu_plugin_outs(stats->str); g_assert(tb == tb_inline); g_assert(insn == insn_inline); g_assert(mem == mem_inline); From 8d073d48b7eae34dad6ec71d6d0ef418d931602b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9trot?= <frederic.petrot@univ-grenoble-alpes.fr> Date: Thu, 18 Jul 2024 10:45:15 +0100 Subject: [PATCH 2321/2884] plugins/execlog.c: correct dump of registers values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register values are dumped as 'sz' chunks of two nibbles in the execlog plugin, sz was 1 too big. Signed-off-by: Frédéric Pétrot <frederic.petrot@univ-grenoble-alpes.fr> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240620083805.73603-1-frederic.petrot@univ-grenoble-alpes.fr> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-8-alex.bennee@linaro.org> --- contrib/plugins/execlog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index 371db97eb1..1c1601cc0b 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -101,7 +101,7 @@ static void insn_check_regs(CPU *cpu) GByteArray *temp = reg->last; g_string_append_printf(cpu->last_exec, ", %s -> 0x", reg->name); /* TODO: handle BE properly */ - for (int i = sz; i >= 0; i--) { + for (int i = sz - 1; i >= 0; i--) { g_string_append_printf(cpu->last_exec, "%02x", reg->new->data[i]); } From f961773ce13d61f56506dc4964e28f0594c7920f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:16 +0100 Subject: [PATCH 2322/2884] semihosting: Include missing 'gdbstub/syscalls.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "semihosting/syscalls.h" requires definitions from "gdbstub/syscalls.h", include it in order to avoid: include/semihosting/syscalls.h:23:38: error: unknown type name 'gdb_syscall_complete_cb' void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, ^ Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717105723.58965-2-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-9-alex.bennee@linaro.org> --- include/semihosting/syscalls.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 3a5ec229eb..b5937c619a 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -9,6 +9,8 @@ #ifndef SEMIHOSTING_SYSCALLS_H #define SEMIHOSTING_SYSCALLS_H +#include "gdbstub/syscalls.h" + /* * Argument loading from the guest is performed by the caller; * results are returned via the 'complete' callback. From bf9ab9d1317e9e27e27d63e94c2e13db2319e2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:17 +0100 Subject: [PATCH 2323/2884] target/m68k: Add semihosting stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the SEMIHOSTING feature is optional, we need a stub to link when it is disabled. Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717105723.58965-3-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-10-alex.bennee@linaro.org> --- target/m68k/meson.build | 5 ++++- target/m68k/semihosting-stub.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 target/m68k/semihosting-stub.c diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 8d3f9ce288..4d213daaf6 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -11,9 +11,12 @@ m68k_ss.add(files( m68k_system_ss = ss.source_set() m68k_system_ss.add(files( - 'm68k-semi.c', 'monitor.c' )) +m68k_system_ss.add(when: ['CONFIG_SEMIHOSTING'], + if_true: files('m68k-semi.c'), + if_false: files('semihosting-stub.c') +) target_arch += {'m68k': m68k_ss} target_system_arch += {'m68k': m68k_system_ss} diff --git a/target/m68k/semihosting-stub.c b/target/m68k/semihosting-stub.c new file mode 100644 index 0000000000..d6a5965e29 --- /dev/null +++ b/target/m68k/semihosting-stub.c @@ -0,0 +1,15 @@ +/* + * m68k/ColdFire semihosting stub + * + * SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org> + * SPDX-FileCopyrightText: 2024 Linaro Ltd. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" + +void do_m68k_semihosting(CPUM68KState *env, int nr) +{ + g_assert_not_reached(); +} From fca2ffcb0b1292ef5dc29e2d1a33e30de9876da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:18 +0100 Subject: [PATCH 2324/2884] target/mips: Add semihosting stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the SEMIHOSTING feature is optional, we need a stub to link when it is disabled. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717105723.58965-4-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-11-alex.bennee@linaro.org> --- target/mips/tcg/sysemu/meson.build | 6 ++++-- target/mips/tcg/sysemu/semihosting-stub.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 target/mips/tcg/sysemu/semihosting-stub.c diff --git a/target/mips/tcg/sysemu/meson.build b/target/mips/tcg/sysemu/meson.build index ec665a4b1e..911341ac37 100644 --- a/target/mips/tcg/sysemu/meson.build +++ b/target/mips/tcg/sysemu/meson.build @@ -1,10 +1,12 @@ mips_system_ss.add(files( 'cp0_helper.c', - 'mips-semi.c', 'special_helper.c', 'tlb_helper.c', )) - +mips_system_ss.add(when: ['CONFIG_SEMIHOSTING'], + if_true: files('mips-semi.c'), + if_false: files('semihosting-stub.c') +) mips_system_ss.add(when: 'TARGET_MIPS64', if_true: files( 'lcsr_helper.c', )) diff --git a/target/mips/tcg/sysemu/semihosting-stub.c b/target/mips/tcg/sysemu/semihosting-stub.c new file mode 100644 index 0000000000..7ae27d746f --- /dev/null +++ b/target/mips/tcg/sysemu/semihosting-stub.c @@ -0,0 +1,15 @@ +/* + * MIPS semihosting stub + * + * SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org> + * SPDX-FileCopyrightText: 2024 Linaro Ltd. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "internal.h" + +void mips_semihosting(CPUMIPSState *env) +{ + g_assert_not_reached(); +} From 099505b375aae2ab7436e14238826f6a06535334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:19 +0100 Subject: [PATCH 2325/2884] target/m68k: Restrict semihosting to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The semihosting feature depends on TCG (due to the probe_access API access). Although TCG is the single accelerator currently available for the m68k target, use the Kconfig "imply" directive which is more correct (if we were to support a different accel). Reported-by: Anton Johansson <anjo@rev.ng> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717105723.58965-5-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-12-alex.bennee@linaro.org> --- target/m68k/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/m68k/Kconfig b/target/m68k/Kconfig index 9eae71486f..23aae24ebe 100644 --- a/target/m68k/Kconfig +++ b/target/m68k/Kconfig @@ -1,3 +1,3 @@ config M68K bool - select SEMIHOSTING + imply SEMIHOSTING if TCG From 75cdcc7a2c75a0f22040be535572261bf4ee7240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:20 +0100 Subject: [PATCH 2326/2884] target/mips: Restrict semihosting to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semihosting currently uses the TCG probe_access API. To prepare for encoding the TCG dependency in Kconfig, do not enable it unless TCG is available. Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Anton Johansson <anjo@rev.ng> Message-Id: <20240717105723.58965-6-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-13-alex.bennee@linaro.org> --- target/mips/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/Kconfig b/target/mips/Kconfig index eb19c94c7d..876048b150 100644 --- a/target/mips/Kconfig +++ b/target/mips/Kconfig @@ -1,6 +1,6 @@ config MIPS bool - select SEMIHOSTING + imply SEMIHOSTING if TCG config MIPS64 bool From 10425887ba54241be1ce97f8935fc320332b531c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:21 +0100 Subject: [PATCH 2327/2884] target/riscv: Restrict semihosting to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semihosting currently uses the TCG probe_access API. To prepare for encoding the TCG dependency in Kconfig, do not enable it unless TCG is available. Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Anton Johansson <anjo@rev.ng> Message-Id: <20240717105723.58965-7-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-14-alex.bennee@linaro.org> --- target/riscv/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig index 5f30df22f2..c332616d36 100644 --- a/target/riscv/Kconfig +++ b/target/riscv/Kconfig @@ -1,9 +1,9 @@ config RISCV32 bool - select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() + imply ARM_COMPATIBLE_SEMIHOSTING if TCG select DEVICE_TREE # needed by boot.c config RISCV64 bool - select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() + imply ARM_COMPATIBLE_SEMIHOSTING if TCG select DEVICE_TREE # needed by boot.c From 41b37a178bafeaa23ca2121bd024073b1c751879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:22 +0100 Subject: [PATCH 2328/2884] target/xtensa: Restrict semihosting to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The semihosting feature depends on TCG (due to the probe_access API access). Although TCG is the single accelerator currently available for the xtensa target, use the Kconfig "imply" directive which is more correct (if we were to support a different accel). Reported-by: Anton Johansson <anjo@rev.ng> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717105723.58965-8-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-15-alex.bennee@linaro.org> --- target/xtensa/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xtensa/Kconfig b/target/xtensa/Kconfig index 5e46049262..e8c2598c4d 100644 --- a/target/xtensa/Kconfig +++ b/target/xtensa/Kconfig @@ -1,3 +1,3 @@ config XTENSA bool - select SEMIHOSTING + imply SEMIHOSTING if TCG From ddd1731385442db6d09d37157158c52487eda1f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 10:45:23 +0100 Subject: [PATCH 2329/2884] semihosting: Restrict to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semihosting currently uses the TCG probe_access API. It is pointless to have it in the binary when TCG isn't. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240717105723.58965-9-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240718094523.1198645-16-alex.bennee@linaro.org> --- semihosting/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/semihosting/Kconfig b/semihosting/Kconfig index eaf3a20ef5..fbe6ac87f9 100644 --- a/semihosting/Kconfig +++ b/semihosting/Kconfig @@ -1,6 +1,7 @@ config SEMIHOSTING bool + depends on TCG config ARM_COMPATIBLE_SEMIHOSTING bool From c135d5eaafe7aa2533da663d8e5a34a424b71eb9 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Fri, 19 Jul 2024 10:41:43 +1000 Subject: [PATCH 2330/2884] tests/tcg/aarch64: Fix test-mte.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.12 warns: TEST gdbstub MTE support on aarch64 /home/rth/qemu/src/tests/tcg/aarch64/gdbstub/test-mte.py:21: SyntaxWarning: invalid escape sequence '\(' PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)." Double up the \ to pass one through to the pattern. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240719004143.1319260-1-richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> --- tests/tcg/aarch64/gdbstub/test-mte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py b/tests/tcg/aarch64/gdbstub/test-mte.py index 2db0663c1a..66f9c25f8a 100644 --- a/tests/tcg/aarch64/gdbstub/test-mte.py +++ b/tests/tcg/aarch64/gdbstub/test-mte.py @@ -18,7 +18,7 @@ import re from test_gdbstub import main, report -PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)." +PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \\(0x[0-9a-f]+\\)." PATTERN_1 = ".*(0x[0-9a-f]+)" From cc455d7eef7f1c7be58eddc9d76ae1a99d293b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Mon, 17 Jun 2024 12:27:56 +0400 Subject: [PATCH 2331/2884] virtio-gpu-gl: declare dependency on ui-opengl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit e8a2db94 "virtio-gpu-virgl: teach it to get the QEMU EGL display", virtio-gl depends on ui-opengl symbol "qemu_egl_display". Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2391 Fixes: e8a2db94 ("virtio-gpu-virgl: teach it to get the QEMU EGL display") Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- hw/display/virtio-gpu-gl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index e06be60dfb..952820a425 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -170,3 +170,4 @@ static void virtio_register_types(void) type_init(virtio_register_types) module_dep("hw-display-virtio-gpu"); +module_dep("ui-opengl"); From b4189dbf805c297fdd6dd228ffab673d44560bcd Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan <phil@philjordan.eu> Date: Mon, 24 Jun 2024 12:10:40 +0200 Subject: [PATCH 2332/2884] Cursor: 8 -> 1 bit alpha downsampling improvement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mouse cursors with 8 bit alpha were downsampled to 1-bit opacity maps by turning alpha values of 255 into 1 and everything else into 0. This means that mostly-opaque pixels ended up completely invisible. This patch changes the behaviour so that only pixels with less than 50% alpha (0-127) are treated as transparent when converted to 1-bit alpha. This greatly improves the subjective appearance of anti-aliased mouse cursors, such as those used by macOS, when using a front-end UI without support for alpha-blended cursors, such as some VNC clients. Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20240624101040.82726-1-phil@philjordan.eu> --- ui/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/cursor.c b/ui/cursor.c index 29717b3ecb..dd3853320d 100644 --- a/ui/cursor.c +++ b/ui/cursor.c @@ -232,7 +232,7 @@ void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask) for (y = 0; y < c->height; y++) { bit = 0x80; for (x = 0; x < c->width; x++, data++) { - if ((*data & 0xff000000) != 0xff000000) { + if ((*data & 0x80000000) == 0x0) { /* Alpha < 0x80 (128) */ if (transparent != 0) { mask[x/8] |= bit; } From 2e35439f2521770d909b00b6984de8ff82ce9c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Wed, 17 Jul 2024 21:15:37 +0400 Subject: [PATCH 2333/2884] ui: add more tracing for dbus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717171541.201525-2-marcandre.lureau@redhat.com> --- audio/dbusaudio.c | 2 +- audio/trace-events | 2 +- ui/dbus-clipboard.c | 4 ++++ ui/trace-events | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c index 60fcf643ec..095e739382 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -105,7 +105,7 @@ static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size); vo->buf_pos += size; - trace_dbus_audio_put_buffer_out(size); + trace_dbus_audio_put_buffer_out(vo->buf_pos, vo->buf_size); if (vo->buf_pos < vo->buf_size) { return size; diff --git a/audio/trace-events b/audio/trace-events index ab04f020ce..7e3f1593c8 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -15,7 +15,7 @@ oss_version(int version) "OSS version = 0x%x" # dbusaudio.c dbus_audio_register(const char *s, const char *dir) "sender = %s, dir = %s" -dbus_audio_put_buffer_out(size_t len) "len = %zu" +dbus_audio_put_buffer_out(size_t pos, size_t size) "buf_pos = %zu, buf_size = %zu" dbus_audio_read(size_t len) "len = %zu" # pwaudio.c diff --git a/ui/dbus-clipboard.c b/ui/dbus-clipboard.c index fe7fcdecb6..fbb043abca 100644 --- a/ui/dbus-clipboard.c +++ b/ui/dbus-clipboard.c @@ -141,6 +141,8 @@ dbus_clipboard_qemu_request(QemuClipboardInfo *info, const char *mimes[] = { MIME_TEXT_PLAIN_UTF8, NULL }; size_t n; + trace_dbus_clipboard_qemu_request(type); + if (type != QEMU_CLIPBOARD_TYPE_TEXT) { /* unsupported atm */ return; @@ -305,6 +307,8 @@ dbus_clipboard_grab( return DBUS_METHOD_INVOCATION_HANDLED; } + trace_dbus_clipboard_grab(arg_selection, arg_serial); + if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) { g_dbus_method_invocation_return_error( invocation, diff --git a/ui/trace-events b/ui/trace-events index 69ff22955d..07eb494246 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -157,7 +157,9 @@ dbus_mouse_rel_motion(int dx, int dy) "dx=%d, dy=%d" dbus_touch_send_event(unsigned int kind, uint32_t num_slot, uint32_t x, uint32_t y) "kind=%u, num_slot=%u, x=%d, y=%d" dbus_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" dbus_update_gl(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" +dbus_clipboard_grab(int selection, unsigned int serial) "selection=%d serial=%u" dbus_clipboard_grab_failed(void) "" +dbus_clipboard_qemu_request(int type) "type=%d" dbus_clipboard_register(const char *bus_name) "peer %s" dbus_clipboard_unregister(const char *bus_name) "peer %s" dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u" From 81c88ce0b84db9395463901d79f7e87e9210527a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Wed, 17 Jul 2024 21:15:38 +0400 Subject: [PATCH 2334/2884] ui/vdagent: improve vdagent_fe_open() trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Place the trace when the function enters, with arg value. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717171541.201525-3-marcandre.lureau@redhat.com> --- ui/trace-events | 2 +- ui/vdagent.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/trace-events b/ui/trace-events index 07eb494246..f5faa149d2 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -132,7 +132,7 @@ xkeymap_keymap(const char *name) "keymap '%s'" clipboard_check_serial(int cur, int recv, bool ok) "cur:%d recv:%d %d" # vdagent.c -vdagent_open(void) "" +vdagent_fe_open(bool fe_open) "fe_open=%d" vdagent_close(void) "" vdagent_disconnect(void) "" vdagent_send(const char *name) "msg %s" diff --git a/ui/vdagent.c b/ui/vdagent.c index 64d7ab245a..cb74739bc4 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -872,6 +872,8 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); + trace_vdagent_fe_open(fe_open); + if (!fe_open) { trace_vdagent_close(); vdagent_disconnect(vd); @@ -881,7 +883,6 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) return; } - trace_vdagent_open(); } static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, From 63a5d4de589330f0a1c46f59a4b7336adda4635b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Wed, 17 Jul 2024 21:15:39 +0400 Subject: [PATCH 2335/2884] ui/vdagent: notify clipboard peers of serial reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we reset the serial counters, peers should also be reset to be sync. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717171541.201525-4-marcandre.lureau@redhat.com> --- ui/clipboard.c | 2 ++ ui/trace-events | 1 + ui/vdagent.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/ui/clipboard.c b/ui/clipboard.c index 4264884a6c..132086eb13 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -155,6 +155,8 @@ void qemu_clipboard_reset_serial(void) QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL }; int i; + trace_clipboard_reset_serial(); + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { QemuClipboardInfo *info = qemu_clipboard_info(i); if (info) { diff --git a/ui/trace-events b/ui/trace-events index f5faa149d2..fb253c1666 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -130,6 +130,7 @@ xkeymap_keymap(const char *name) "keymap '%s'" # clipboard.c clipboard_check_serial(int cur, int recv, bool ok) "cur:%d recv:%d %d" +clipboard_reset_serial(void) "" # vdagent.c vdagent_fe_open(bool fe_open) "fe_open=%d" diff --git a/ui/vdagent.c b/ui/vdagent.c index cb74739bc4..2a4b3574b1 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -720,6 +720,8 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) memset(vd->last_serial, 0, sizeof(vd->last_serial)); if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) { + qemu_clipboard_reset_serial(); + vd->cbpeer.name = "vdagent"; vd->cbpeer.notifier.notify = vdagent_clipboard_notify; vd->cbpeer.request = vdagent_clipboard_request; From 865714cb3b88320e2e840eb851d084c757511a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Wed, 17 Jul 2024 21:15:40 +0400 Subject: [PATCH 2336/2884] ui/vdagent: send caps on fe_open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spice-vdagentd doesn't send capabilities again on host/client disconnect (but when the session agent connects and sends a GUEST_XORG_RESOLUTION message) When the dbus client disconnects, vdagent_disconnect() is called to reset the agent state. Capabilities must be negotiated again on reconnection. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240717171541.201525-5-marcandre.lureau@redhat.com> --- ui/vdagent.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/vdagent.c b/ui/vdagent.c index 2a4b3574b1..724eff972f 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -185,7 +185,7 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) vdagent_send_buf(vd); } -static void vdagent_send_caps(VDAgentChardev *vd) +static void vdagent_send_caps(VDAgentChardev *vd, bool request) { g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + sizeof(VDAgentAnnounceCapabilities) + @@ -205,6 +205,7 @@ static void vdagent_send_caps(VDAgentChardev *vd) #endif } + caps->request = request; vdagent_send_msg(vd, msg); } @@ -711,7 +712,7 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) vd->caps = caps->caps[0]; if (caps->request) { - vdagent_send_caps(vd); + vdagent_send_caps(vd, false); } if (have_mouse(vd) && vd->mouse_hs) { qemu_input_handler_activate(vd->mouse_hs); @@ -885,6 +886,7 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) return; } + vdagent_send_caps(vd, true); } static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, From c510fe78f1b7c966524489d6ba752107423b20c8 Mon Sep 17 00:00:00 2001 From: Zheyu Ma <zheyuma97@gmail.com> Date: Wed, 3 Jul 2024 01:13:03 +0200 Subject: [PATCH 2337/2884] hw/nvme: fix memory leak in nvme_dsm The allocated memory to hold LBA ranges leaks in the nvme_dsm function. This happens because the allocated memory for iocb->range is not freed in all error handling paths. Fix this by adding a free to ensure that the allocated memory is properly freed. ASAN log: ==3075137==ERROR: LeakSanitizer: detected memory leaks Direct leak of 480 byte(s) in 6 object(s) allocated from: #0 0x55f1f8a0eddd in malloc llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3 #1 0x7f531e0f6738 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x5e738) #2 0x55f1faf1f091 in blk_aio_get block/block-backend.c:2583:12 #3 0x55f1f945c74b in nvme_dsm hw/nvme/ctrl.c:2609:30 #4 0x55f1f945831b in nvme_io_cmd hw/nvme/ctrl.c:4470:16 #5 0x55f1f94561b7 in nvme_process_sq hw/nvme/ctrl.c:7039:29 Cc: qemu-stable@nongnu.org Fixes: d7d1474fd85d ("hw/nvme: reimplement dsm to allow cancellation") Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 5b1b0cabcf..f7b4e4627d 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -2591,6 +2591,7 @@ next: done: iocb->aiocb = NULL; iocb->common.cb(iocb->common.opaque, iocb->ret); + g_free(iocb->range); qemu_aio_unref(iocb); } From 13be929aff804581b21e69087a9caf3698fd5c3c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 16 Jul 2024 18:53:11 +0200 Subject: [PATCH 2338/2884] target/i386: do not crash if microvm guest uses SGX CPUID leaves sgx_epc_get_section assumes a PC platform is in use: bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); However, sgx_epc_get_section is called by CPUID regardless of whether SGX state has been initialized or which platform is in use. Check whether the machine has the right QOM class and if not behave as if there are no EPC sections. Fixes: 1dec2e1f19f ("i386: Update SGX CPUID info according to hardware/KVM/user input", 2021-09-30) Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2142 Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/i386/sgx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index a14a84bc6f..849472a128 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -268,10 +268,12 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { - PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + PCMachineState *pcms = + (PCMachineState *)object_dynamic_cast(qdev_get_machine(), + TYPE_PC_MACHINE); SGXEPCDevice *epc; - if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) { + if (!pcms || pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) { return true; } From 95fa0c79a04f3ebebb9f3b68cc05b4b8082b5444 Mon Sep 17 00:00:00 2001 From: Anthony Harivel <aharivel@redhat.com> Date: Wed, 22 May 2024 17:34:50 +0200 Subject: [PATCH 2339/2884] qio: add support for SO_PEERCRED for socket channel The function qio_channel_get_peercred() returns a pointer to the credentials of the peer process connected to this socket. This credentials structure is defined in <sys/socket.h> as follows: struct ucred { pid_t pid; /* Process ID of the sending process */ uid_t uid; /* User ID of the sending process */ gid_t gid; /* Group ID of the sending process */ }; The use of this function is possible only for connected AF_UNIX stream sockets and for AF_UNIX stream and datagram socket pairs. On platform other than Linux, the function return 0. Signed-off-by: Anthony Harivel <aharivel@redhat.com> Link: https://lore.kernel.org/r/20240522153453.1230389-2-aharivel@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- include/io/channel.h | 21 +++++++++++++++++++++ io/channel-socket.c | 28 ++++++++++++++++++++++++++++ io/channel.c | 13 +++++++++++++ 3 files changed, 62 insertions(+) diff --git a/include/io/channel.h b/include/io/channel.h index 7986c49c71..bdf0bca92a 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -160,6 +160,9 @@ struct QIOChannelClass { void *opaque); int (*io_flush)(QIOChannel *ioc, Error **errp); + int (*io_peerpid)(QIOChannel *ioc, + unsigned int *pid, + Error **errp); }; /* General I/O handling functions */ @@ -981,4 +984,22 @@ int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, int qio_channel_flush(QIOChannel *ioc, Error **errp); +/** + * qio_channel_get_peercred: + * @ioc: the channel object + * @pid: pointer to pid + * @errp: pointer to a NULL-initialized error object + * + * Returns the pid of the peer process connected to this socket. + * + * The use of this function is possible only for connected + * AF_UNIX stream sockets and for AF_UNIX stream and datagram + * socket pairs on Linux. + * Return -1 on error with pid -1 for the non-Linux OS. + * + */ +int qio_channel_get_peerpid(QIOChannel *ioc, + unsigned int *pid, + Error **errp); + #endif /* QIO_CHANNEL_H */ diff --git a/io/channel-socket.c b/io/channel-socket.c index 3a899b0608..608bcf066e 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -841,6 +841,33 @@ qio_channel_socket_set_cork(QIOChannel *ioc, socket_set_cork(sioc->fd, v); } +static int +qio_channel_socket_get_peerpid(QIOChannel *ioc, + unsigned int *pid, + Error **errp) +{ +#ifdef CONFIG_LINUX + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + Error *err = NULL; + socklen_t len = sizeof(struct ucred); + + struct ucred cred; + if (getsockopt(sioc->fd, + SOL_SOCKET, SO_PEERCRED, + &cred, &len) == -1) { + error_setg_errno(&err, errno, "Unable to get peer credentials"); + error_propagate(errp, err); + *pid = -1; + return -1; + } + *pid = (unsigned int)cred.pid; + return 0; +#else + error_setg(errp, "Unsupported feature"); + *pid = -1; + return -1; +#endif +} static int qio_channel_socket_close(QIOChannel *ioc, @@ -938,6 +965,7 @@ static void qio_channel_socket_class_init(ObjectClass *klass, #ifdef QEMU_MSG_ZEROCOPY ioc_klass->io_flush = qio_channel_socket_flush; #endif + ioc_klass->io_peerpid = qio_channel_socket_get_peerpid; } static const TypeInfo qio_channel_socket_info = { diff --git a/io/channel.c b/io/channel.c index a1f12f8e90..e3f17c24a0 100644 --- a/io/channel.c +++ b/io/channel.c @@ -548,6 +548,19 @@ void qio_channel_set_cork(QIOChannel *ioc, } } +int qio_channel_get_peerpid(QIOChannel *ioc, + unsigned int *pid, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (!klass->io_peerpid) { + error_setg(errp, "Channel does not support peer pid"); + return -1; + } + klass->io_peerpid(ioc, pid, errp); + return 0; +} off_t qio_channel_io_seek(QIOChannel *ioc, off_t offset, From 84369d762127157137006e29a971bb08a1bd17cd Mon Sep 17 00:00:00 2001 From: Anthony Harivel <aharivel@redhat.com> Date: Wed, 22 May 2024 17:34:51 +0200 Subject: [PATCH 2340/2884] tools: build qemu-vmsr-helper Introduce a privileged helper to access RAPL MSR. The privileged helper tool, qemu-vmsr-helper, is designed to provide virtual machines with the ability to read specific RAPL (Running Average Power Limit) MSRs without requiring CAP_SYS_RAWIO privileges or relying on external, out-of-tree patches. The helper tool leverages Unix permissions and SO_PEERCRED socket options to enforce access control, ensuring that only processes explicitly requesting read access via readmsr() from a valid Thread ID can access these MSRs. The list of RAPL MSRs that are allowed to be read by the helper tool is defined in rapl-msr-index.h. This list corresponds to the RAPL MSRs that will be supported in the next commit titled "Add support for RAPL MSRs in KVM/QEMU." The tool is intentionally designed to run on the Linux x86 platform. This initial implementation is tailored for Intel CPUs but can be extended to support AMD CPUs in the future. Signed-off-by: Anthony Harivel <aharivel@redhat.com> Link: https://lore.kernel.org/r/20240522153453.1230389-3-aharivel@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- MAINTAINERS | 1 + contrib/systemd/qemu-vmsr-helper.service | 15 + contrib/systemd/qemu-vmsr-helper.socket | 9 + docs/tools/index.rst | 1 + docs/tools/qemu-vmsr-helper.rst | 89 ++++ meson.build | 7 + tools/i386/qemu-vmsr-helper.c | 530 +++++++++++++++++++++++ tools/i386/rapl-msr-index.h | 28 ++ 8 files changed, 680 insertions(+) create mode 100644 contrib/systemd/qemu-vmsr-helper.service create mode 100644 contrib/systemd/qemu-vmsr-helper.socket create mode 100644 docs/tools/qemu-vmsr-helper.rst create mode 100644 tools/i386/qemu-vmsr-helper.c create mode 100644 tools/i386/rapl-msr-index.h diff --git a/MAINTAINERS b/MAINTAINERS index 7d9811458c..f8df6940fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -140,6 +140,7 @@ F: docs/system/target-i386* F: target/i386/*.[ch] F: target/i386/Kconfig F: target/i386/meson.build +F: tools/i386/ Guest CPU cores (TCG) --------------------- diff --git a/contrib/systemd/qemu-vmsr-helper.service b/contrib/systemd/qemu-vmsr-helper.service new file mode 100644 index 0000000000..8fd397bf79 --- /dev/null +++ b/contrib/systemd/qemu-vmsr-helper.service @@ -0,0 +1,15 @@ +[Unit] +Description=Virtual RAPL MSR Daemon for QEMU + +[Service] +WorkingDirectory=/tmp +Type=simple +ExecStart=/usr/bin/qemu-vmsr-helper +PrivateTmp=yes +ProtectSystem=strict +ReadWritePaths=/var/run +RestrictAddressFamilies=AF_UNIX +Restart=always +RestartSec=0 + +[Install] diff --git a/contrib/systemd/qemu-vmsr-helper.socket b/contrib/systemd/qemu-vmsr-helper.socket new file mode 100644 index 0000000000..183e8304d6 --- /dev/null +++ b/contrib/systemd/qemu-vmsr-helper.socket @@ -0,0 +1,9 @@ +[Unit] +Description=Virtual RAPL MSR helper for QEMU + +[Socket] +ListenStream=/run/qemu-vmsr-helper.sock +SocketMode=0600 + +[Install] +WantedBy=multi-user.target diff --git a/docs/tools/index.rst b/docs/tools/index.rst index 8e65ce0dfc..33ad438e86 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -16,3 +16,4 @@ command line utilities and other standalone programs. qemu-pr-helper qemu-trace-stap virtfs-proxy-helper + qemu-vmsr-helper diff --git a/docs/tools/qemu-vmsr-helper.rst b/docs/tools/qemu-vmsr-helper.rst new file mode 100644 index 0000000000..6ec87b49d9 --- /dev/null +++ b/docs/tools/qemu-vmsr-helper.rst @@ -0,0 +1,89 @@ +================================== +QEMU virtual RAPL MSR helper +================================== + +Synopsis +-------- + +**qemu-vmsr-helper** [*OPTION*] + +Description +----------- + +Implements the virtual RAPL MSR helper for QEMU. + +Accessing the RAPL (Running Average Power Limit) MSR enables the RAPL powercap +driver to advertise and monitor the power consumption or accumulated energy +consumption of different power domains, such as CPU packages, DRAM, and other +components when available. + +However those register are accesible under priviliged access (CAP_SYS_RAWIO). +QEMU can use an external helper to access those priviliged register. + +:program:`qemu-vmsr-helper` is that external helper; it creates a listener +socket which will accept incoming connections for communication with QEMU. + +If you want to run VMs in a setup like this, this helper should be started as a +system service, and you should read the QEMU manual section on "RAPL MSR +support" to find out how to configure QEMU to connect to the socket created by +:program:`qemu-vmsr-helper`. + +After connecting to the socket, :program:`qemu-vmsr-helper` can +optionally drop root privileges, except for those capabilities that +are needed for its operation. + +:program:`qemu-vmsr-helper` can also use the systemd socket activation +protocol. In this case, the systemd socket unit should specify a +Unix stream socket, like this:: + + [Socket] + ListenStream=/var/run/qemu-vmsr-helper.sock + +Options +------- + +.. program:: qemu-vmsr-helper + +.. option:: -d, --daemon + + run in the background (and create a PID file) + +.. option:: -q, --quiet + + decrease verbosity + +.. option:: -v, --verbose + + increase verbosity + +.. option:: -f, --pidfile=PATH + + PID file when running as a daemon. By default the PID file + is created in the system runtime state directory, for example + :file:`/var/run/qemu-vmsr-helper.pid`. + +.. option:: -k, --socket=PATH + + path to the socket. By default the socket is created in + the system runtime state directory, for example + :file:`/var/run/qemu-vmsr-helper.sock`. + +.. option:: -T, --trace [[enable=]PATTERN][,events=FILE][,file=FILE] + + .. include:: ../qemu-option-trace.rst.inc + +.. option:: -u, --user=USER + + user to drop privileges to + +.. option:: -g, --group=GROUP + + group to drop privileges to + +.. option:: -h, --help + + Display a help message and exit. + +.. option:: -V, --version + + Display version information and exit. diff --git a/meson.build b/meson.build index a1e51277b0..9d3e2d2ddb 100644 --- a/meson.build +++ b/meson.build @@ -4073,6 +4073,13 @@ if have_tools dependencies: [authz, crypto, io, qom, qemuutil, libcap_ng, mpathpersist], install: true) + + if cpu in ['x86', 'x86_64'] + executable('qemu-vmsr-helper', files('tools/i386/qemu-vmsr-helper.c'), + dependencies: [authz, crypto, io, qom, qemuutil, + libcap_ng, mpathpersist], + install: true) + endif endif if have_ivshmem diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c new file mode 100644 index 0000000000..ebf562c3ff --- /dev/null +++ b/tools/i386/qemu-vmsr-helper.c @@ -0,0 +1,530 @@ +/* + * Privileged RAPL MSR helper commands for QEMU + * + * Copyright (C) 2024 Red Hat, Inc. <aharivel@redhat.com> + * + * Author: Anthony Harivel <aharivel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include <getopt.h> +#include <stdbool.h> +#include <sys/ioctl.h> +#ifdef CONFIG_LIBCAP_NG +#include <cap-ng.h> +#endif +#include <pwd.h> +#include <grp.h> + +#include "qemu/help-texts.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/config-file.h" +#include "qemu-version.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/systemd.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "trace/control.h" +#include "qemu-version.h" +#include "rapl-msr-index.h" + +#define MSR_PATH_TEMPLATE "/dev/cpu/%u/msr" + +static char *socket_path; +static char *pidfile; +static enum { RUNNING, TERMINATE, TERMINATING } state; +static QIOChannelSocket *server_ioc; +static int server_watch; +static int num_active_sockets = 1; + +#ifdef CONFIG_LIBCAP_NG +static int uid = -1; +static int gid = -1; +#endif + +static void compute_default_paths(void) +{ + g_autofree char *state = qemu_get_local_state_dir(); + + socket_path = g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL); + pidfile = g_build_filename(state, "run", "qemu-vmsr-helper.pid", NULL); +} + +static int is_intel_processor(void) +{ + int result; + int ebx, ecx, edx; + + /* Execute CPUID instruction with eax=0 (basic identification) */ + asm volatile ( + "cpuid" + : "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (0) + ); + + /* + * Check if processor is "GenuineIntel" + * 0x756e6547 = "Genu" + * 0x49656e69 = "ineI" + * 0x6c65746e = "ntel" + */ + result = (ebx == 0x756e6547) && (edx == 0x49656e69) && (ecx == 0x6c65746e); + + return result; +} + +static int is_rapl_enabled(void) +{ + const char *path = "/sys/class/powercap/intel-rapl/enabled"; + FILE *file = fopen(path, "r"); + int value = 0; + + if (file != NULL) { + if (fscanf(file, "%d", &value) != 1) { + error_report("INTEL RAPL not enabled"); + } + fclose(file); + } else { + error_report("Error opening %s", path); + } + + return value; +} + +/* + * Check if the TID that request the MSR read + * belongs to the peer. It be should a TID of a vCPU. + */ +static bool is_tid_present(pid_t pid, pid_t tid) +{ + g_autofree char *tidPath = g_strdup_printf("/proc/%d/task/%d", pid, tid); + + /* Check if the TID directory exists within the PID directory */ + if (access(tidPath, F_OK) == 0) { + return true; + } + + error_report("Failed to open /proc at %s", tidPath); + return false; +} + +/* + * Only the RAPL MSR in target/i386/cpu.h are allowed + */ +static bool is_msr_allowed(uint32_t reg) +{ + switch (reg) { + case MSR_RAPL_POWER_UNIT: + case MSR_PKG_POWER_LIMIT: + case MSR_PKG_ENERGY_STATUS: + case MSR_PKG_POWER_INFO: + return true; + default: + return false; + } +} + +static uint64_t vmsr_read_msr(uint32_t msr_register, unsigned int cpu_id) +{ + int fd; + uint64_t result = 0; + + g_autofree char *path = g_strdup_printf(MSR_PATH_TEMPLATE, cpu_id); + + fd = open(path, O_RDONLY); + if (fd < 0) { + error_report("Failed to open MSR file at %s", path); + return result; + } + + if (pread(fd, &result, sizeof(result), msr_register) != sizeof(result)) { + error_report("Failed to read MSR"); + result = 0; + } + + close(fd); + return result; +} + +static void usage(const char *name) +{ + (printf) ( +"Usage: %s [OPTIONS] FILE\n" +"Virtual RAPL MSR helper program for QEMU\n" +"\n" +" -h, --help display this help and exit\n" +" -V, --version output version information and exit\n" +"\n" +" -d, --daemon run in the background\n" +" -f, --pidfile=PATH PID file when running as a daemon\n" +" (default '%s')\n" +" -k, --socket=PATH path to the unix socket\n" +" (default '%s')\n" +" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n" +" specify tracing options\n" +#ifdef CONFIG_LIBCAP_NG +" -u, --user=USER user to drop privileges to\n" +" -g, --group=GROUP group to drop privileges to\n" +#endif +"\n" +QEMU_HELP_BOTTOM "\n" + , name, pidfile, socket_path); +} + +static void version(const char *name) +{ + printf( +"%s " QEMU_FULL_VERSION "\n" +"Written by Anthony Harivel.\n" +"\n" +QEMU_COPYRIGHT "\n" +"This is free software; see the source for copying conditions. There is NO\n" +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + , name); +} + +typedef struct VMSRHelperClient { + QIOChannelSocket *ioc; + Coroutine *co; +} VMSRHelperClient; + +static void coroutine_fn vh_co_entry(void *opaque) +{ + VMSRHelperClient *client = opaque; + Error *local_err = NULL; + unsigned int peer_pid; + uint32_t request[3]; + uint64_t vmsr; + int r; + + qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, NULL); + + qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); + + /* + * Check peer credentials + */ + r = qio_channel_get_peerpid(QIO_CHANNEL(client->ioc), + &peer_pid, + &local_err); + if (r < 0) { + error_report_err(local_err); + goto out; + } + + while (r < 0) { + /* + * Read the requested MSR + * Only RAPL MSR in rapl-msr-index.h is allowed + */ + r = qio_channel_read_all(QIO_CHANNEL(client->ioc), + (char *) &request, sizeof(request), &local_err); + if (r < 0) { + error_report_err(local_err); + break; + } + + if (!is_msr_allowed(request[0])) { + error_report("Requested unallowed msr: %d", request[0]); + break; + } + + vmsr = vmsr_read_msr(request[0], request[1]); + + if (!is_tid_present(peer_pid, request[2])) { + error_report("Requested TID not in peer PID: %d %d", + peer_pid, request[2]); + vmsr = 0; + } + + r = qio_channel_write_all(QIO_CHANNEL(client->ioc), + (char *) &vmsr, + sizeof(vmsr), + &local_err); + if (r < 0) { + error_report_err(local_err); + break; + } + } +out: + object_unref(OBJECT(client->ioc)); + g_free(client); +} + +static gboolean accept_client(QIOChannel *ioc, + GIOCondition cond, + gpointer opaque) +{ + QIOChannelSocket *cioc; + VMSRHelperClient *vmsrh; + + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; + } + + vmsrh = g_new(VMSRHelperClient, 1); + vmsrh->ioc = cioc; + vmsrh->co = qemu_coroutine_create(vh_co_entry, vmsrh); + qemu_coroutine_enter(vmsrh->co); + + return TRUE; +} + +static void termsig_handler(int signum) +{ + qatomic_cmpxchg(&state, RUNNING, TERMINATE); + qemu_notify_event(); +} + +static void close_server_socket(void) +{ + assert(server_ioc); + + g_source_remove(server_watch); + server_watch = -1; + object_unref(OBJECT(server_ioc)); + num_active_sockets--; +} + +#ifdef CONFIG_LIBCAP_NG +static int drop_privileges(void) +{ + /* clear all capabilities */ + capng_clear(CAPNG_SELECT_BOTH); + + if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, + CAP_SYS_RAWIO) < 0) { + return -1; + } + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + const char *sopt = "hVk:f:dT:u:g:vq"; + struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "socket", required_argument, NULL, 'k' }, + { "pidfile", required_argument, NULL, 'f' }, + { "daemon", no_argument, NULL, 'd' }, + { "trace", required_argument, NULL, 'T' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + int opt_ind = 0; + int ch; + Error *local_err = NULL; + bool daemonize = false; + bool pidfile_specified = false; + bool socket_path_specified = false; + unsigned socket_activation; + + struct sigaction sa_sigterm; + memset(&sa_sigterm, 0, sizeof(sa_sigterm)); + sa_sigterm.sa_handler = termsig_handler; + sigaction(SIGTERM, &sa_sigterm, NULL); + sigaction(SIGINT, &sa_sigterm, NULL); + sigaction(SIGHUP, &sa_sigterm, NULL); + + signal(SIGPIPE, SIG_IGN); + + error_init(argv[0]); + module_call_init(MODULE_INIT_TRACE); + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_trace_opts); + qemu_init_exec_dir(argv[0]); + + compute_default_paths(); + + /* + * Sanity check + * 1. cpu must be Intel cpu + * 2. RAPL must be enabled + */ + if (!is_intel_processor()) { + error_report("error: CPU is not INTEL cpu"); + exit(EXIT_FAILURE); + } + + if (!is_rapl_enabled()) { + error_report("error: RAPL driver not enable"); + exit(EXIT_FAILURE); + } + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch (ch) { + case 'k': + g_free(socket_path); + socket_path = g_strdup(optarg); + socket_path_specified = true; + if (socket_path[0] != '/') { + error_report("socket path must be absolute"); + exit(EXIT_FAILURE); + } + break; + case 'f': + g_free(pidfile); + pidfile = g_strdup(optarg); + pidfile_specified = true; + break; +#ifdef CONFIG_LIBCAP_NG + case 'u': { + unsigned long res; + struct passwd *userinfo = getpwnam(optarg); + if (userinfo) { + uid = userinfo->pw_uid; + } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && + (uid_t)res == res) { + uid = res; + } else { + error_report("invalid user '%s'", optarg); + exit(EXIT_FAILURE); + } + break; + } + case 'g': { + unsigned long res; + struct group *groupinfo = getgrnam(optarg); + if (groupinfo) { + gid = groupinfo->gr_gid; + } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && + (gid_t)res == res) { + gid = res; + } else { + error_report("invalid group '%s'", optarg); + exit(EXIT_FAILURE); + } + break; + } +#else + case 'u': + case 'g': + error_report("-%c not supported by this %s", ch, argv[0]); + exit(1); +#endif + case 'd': + daemonize = true; + break; + case 'T': + trace_opt_parse(optarg); + break; + case 'V': + version(argv[0]); + exit(EXIT_SUCCESS); + break; + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + break; + case '?': + error_report("Try `%s --help' for more information.", argv[0]); + exit(EXIT_FAILURE); + } + } + + if (!trace_init_backends()) { + exit(EXIT_FAILURE); + } + trace_init_file(); + qemu_set_log(LOG_TRACE, &error_fatal); + + socket_activation = check_socket_activation(); + if (socket_activation == 0) { + SocketAddress saddr; + saddr = (SocketAddress){ + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = socket_path, + }; + server_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, &saddr, + 1, &local_err) < 0) { + object_unref(OBJECT(server_ioc)); + error_report_err(local_err); + return 1; + } + } else { + /* Using socket activation - check user didn't use -p etc. */ + if (socket_path_specified) { + error_report("Unix socket can't be set when" + "using socket activation"); + exit(EXIT_FAILURE); + } + + /* Can only listen on a single socket. */ + if (socket_activation > 1) { + error_report("%s does not support socket activation" + "with LISTEN_FDS > 1", + argv[0]); + exit(EXIT_FAILURE); + } + server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD, + &local_err); + if (server_ioc == NULL) { + error_reportf_err(local_err, + "Failed to use socket activation: "); + exit(EXIT_FAILURE); + } + } + + qemu_init_main_loop(&error_fatal); + + server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), + G_IO_IN, + accept_client, + NULL, NULL); + + if (daemonize) { + if (daemon(0, 0) < 0) { + error_report("Failed to daemonize: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (daemonize || pidfile_specified) { + qemu_write_pidfile(pidfile, &error_fatal); + } + +#ifdef CONFIG_LIBCAP_NG + if (drop_privileges() < 0) { + error_report("Failed to drop privileges: %s", strerror(errno)); + exit(EXIT_FAILURE); + } +#endif + + info_report("Listening on %s", socket_path); + + state = RUNNING; + do { + main_loop_wait(false); + if (state == TERMINATE) { + state = TERMINATING; + close_server_socket(); + } + } while (num_active_sockets > 0); + + exit(EXIT_SUCCESS); +} diff --git a/tools/i386/rapl-msr-index.h b/tools/i386/rapl-msr-index.h new file mode 100644 index 0000000000..9a7118639a --- /dev/null +++ b/tools/i386/rapl-msr-index.h @@ -0,0 +1,28 @@ +/* + * Allowed list of MSR for Privileged RAPL MSR helper commands for QEMU + * + * Copyright (C) 2023 Red Hat, Inc. <aharivel@redhat.com> + * + * Author: Anthony Harivel <aharivel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Should stay in sync with the RAPL MSR + * in target/i386/cpu.h + */ +#define MSR_RAPL_POWER_UNIT 0x00000606 +#define MSR_PKG_POWER_LIMIT 0x00000610 +#define MSR_PKG_ENERGY_STATUS 0x00000611 +#define MSR_PKG_POWER_INFO 0x00000614 From 49269895a02fb05765e0d326cfcafa409fa4f642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Fri, 19 Jul 2024 20:02:11 +0200 Subject: [PATCH 2341/2884] tests/avocado: Allow overwriting AVOCADO_SHOW env variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'app' level logging is useful, but sometimes we want more, for example QEMU leverages the 'console' logging. Allow overwriting AVOCADO_SHOW from environment, i.e.: $ make check-avocado AVOCADO_SHOW='app,console' Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240719180211.48073-1-philmd@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index d39d5dd6a4..6618bfed70 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -97,7 +97,7 @@ endif # Controls the output generated by Avocado when running tests. # Any number of command separated loggers are accepted. For more # information please refer to "avocado --help". -AVOCADO_SHOW=app +AVOCADO_SHOW?=app ifndef AVOCADO_TAGS AVOCADO_CMDLINE_TAGS=$(patsubst %-softmmu,-t arch:%, \ $(filter %-softmmu,$(TARGETS))) From 816d4201ea8c926fddf766b37abc3e5ff03bb0dd Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Fri, 19 Jul 2024 11:50:31 +0200 Subject: [PATCH 2342/2884] tests/avocado: Move LinuxTest related code into a separate file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only some few tests are using the LinuxTest class. Move the related code into a separate file so that this does not pollute the main namespace. Message-ID: <20240719095031.32814-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/avocado_qemu/__init__.py | 239 +--------------------- tests/avocado/avocado_qemu/linuxtest.py | 253 ++++++++++++++++++++++++ tests/avocado/boot_linux.py | 3 +- tests/avocado/hotplug_blk.py | 2 +- tests/avocado/hotplug_cpu.py | 2 +- tests/avocado/intel_iommu.py | 2 +- tests/avocado/replay_linux.py | 2 +- tests/avocado/smmu.py | 3 +- 8 files changed, 262 insertions(+), 244 deletions(-) create mode 100644 tests/avocado/avocado_qemu/linuxtest.py diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index 304c428168..a3da2a96bb 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -10,7 +10,6 @@ import logging import os -import shutil import subprocess import sys import tempfile @@ -18,7 +17,7 @@ import time import uuid import avocado -from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage +from avocado.utils import ssh from avocado.utils.path import find_command from qemu.machine import QEMUMachine @@ -32,14 +31,6 @@ from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available, #: and build tree, it will not be accurate. BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) -if os.path.islink(os.path.dirname(os.path.dirname(__file__))): - # The link to the avocado tests dir in the source code directory - lnk = os.path.dirname(os.path.dirname(__file__)) - #: The QEMU root source directory - SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) -else: - SOURCE_DIR = BUILD_DIR - def has_cmd(name, args=None): """ @@ -451,231 +442,3 @@ class LinuxSSHMixIn: break else: self.fail('"%s" output does not contain "%s"' % (cmd, exp)) - -class LinuxDistro: - """Represents a Linux distribution - - Holds information of known distros. - """ - #: A collection of known distros and their respective image checksum - KNOWN_DISTROS = { - 'fedora': { - '31': { - 'x86_64': - {'checksum': ('e3c1b309d9203604922d6e255c2c5d09' - '8a309c2d46215d8fc026954f3c5c27a0'), - 'pxeboot_url': ('https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/x86_64/os/images/pxeboot/'), - 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-' - '08a96687f73c ro no_timer_check ' - 'net.ifnames=0 console=tty1 ' - 'console=ttyS0,115200n8'), - }, - 'aarch64': - {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae' - 'd2af0ad0329383d5639c997fdf16fe49'), - 'pxeboot_url': 'https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/aarch64/os/images/pxeboot/', - 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-' - '355e8475b0a7 ro earlyprintk=pl011,0x9000000' - ' ignore_loglevel no_timer_check' - ' printk.time=1 rd_NO_PLYMOUTH' - ' console=ttyAMA0'), - }, - 'ppc64': - {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4' - '3f991c506f2cc390dc4efa2026ad2f58')}, - 's390x': - {'checksum': ('4caaab5a434fd4d1079149a072fdc789' - '1e354f834d355069ca982fdcaf5a122d')}, - }, - '32': { - 'aarch64': - {'checksum': ('b367755c664a2d7a26955bbfff985855' - 'adfa2ca15e908baf15b4b176d68d3967'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/32/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-' - '14d95c0e90c5 ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8'), - }, - }, - '33': { - 'aarch64': - {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1' - 'a81f386a17f969c1d1c7c87031008a6b'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/33/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-' - '1126a0208f8a ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8' - ' console=tty0'), - }, - }, - } - } - - def __init__(self, name, version, arch): - self.name = name - self.version = version - self.arch = arch - try: - info = self.KNOWN_DISTROS.get(name).get(version).get(arch) - except AttributeError: - # Unknown distro - info = None - self._info = info or {} - - @property - def checksum(self): - """Gets the cloud-image file checksum""" - return self._info.get('checksum', None) - - @checksum.setter - def checksum(self, value): - self._info['checksum'] = value - - @property - def pxeboot_url(self): - """Gets the repository url where pxeboot files can be found""" - return self._info.get('pxeboot_url', None) - - @property - def default_kernel_params(self): - """Gets the default kernel parameters""" - return self._info.get('kernel_params', None) - - -class LinuxTest(LinuxSSHMixIn, QemuSystemTest): - """Facilitates having a cloud-image Linux based available. - - For tests that intend to interact with guests, this is a better choice - to start with than the more vanilla `QemuSystemTest` class. - """ - - distro = None - username = 'root' - password = 'password' - smp = '2' - memory = '1024' - - def _set_distro(self): - distro_name = self.params.get( - 'distro', - default=self._get_unique_tag_val('distro')) - if not distro_name: - distro_name = 'fedora' - - distro_version = self.params.get( - 'distro_version', - default=self._get_unique_tag_val('distro_version')) - if not distro_version: - distro_version = '31' - - self.distro = LinuxDistro(distro_name, distro_version, self.arch) - - # The distro checksum behaves differently than distro name and - # version. First, it does not respect a tag with the same - # name, given that it's not expected to be used for filtering - # (distro name versions are the natural choice). Second, the - # order of precedence is: parameter, attribute and then value - # from KNOWN_DISTROS. - distro_checksum = self.params.get('distro_checksum', - default=None) - if distro_checksum: - self.distro.checksum = distro_checksum - - def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'): - super().setUp() - self.require_netdev('user') - self._set_distro() - self.vm.add_args('-smp', self.smp) - self.vm.add_args('-m', self.memory) - # The following network device allows for SSH connections - self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', '%s,netdev=vnet' % network_device_type) - self.set_up_boot() - if ssh_pubkey is None: - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.set_up_cloudinit(ssh_pubkey) - - def set_up_existing_ssh_keys(self): - ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub') - source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa') - ssh_dir = os.path.join(self.workdir, '.ssh') - os.mkdir(ssh_dir, mode=0o700) - ssh_private_key = os.path.join(ssh_dir, - os.path.basename(source_private_key)) - shutil.copyfile(source_private_key, ssh_private_key) - os.chmod(ssh_private_key, 0o600) - return (ssh_public_key, ssh_private_key) - - def download_boot(self): - # Set the qemu-img binary. - # If none is available, the test will cancel. - vmimage.QEMU_IMG = super().get_qemu_img() - - self.log.info('Downloading/preparing boot image') - # Fedora 31 only provides ppc64le images - image_arch = self.arch - if self.distro.name == 'fedora': - if image_arch == 'ppc64': - image_arch = 'ppc64le' - - try: - boot = vmimage.get( - self.distro.name, arch=image_arch, version=self.distro.version, - checksum=self.distro.checksum, - algorithm='sha256', - cache_dir=self.cache_dirs[0], - snapshot_dir=self.workdir) - except: - self.cancel('Failed to download/prepare boot image') - return boot.path - - def prepare_cloudinit(self, ssh_pubkey=None): - self.log.info('Preparing cloudinit image') - try: - cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') - pubkey_content = None - if ssh_pubkey: - with open(ssh_pubkey) as pubkey: - pubkey_content = pubkey.read() - cloudinit.iso(cloudinit_iso, self.name, - username=self.username, - password=self.password, - # QEMU's hard coded usermode router address - phone_home_host='10.0.2.2', - phone_home_port=self.phone_server.server_port, - authorized_key=pubkey_content) - except Exception: - self.cancel('Failed to prepare the cloudinit image') - return cloudinit_iso - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-drive', 'file=%s' % path) - - def set_up_cloudinit(self, ssh_pubkey=None): - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) - self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) - - def launch_and_wait(self, set_up_ssh_connection=True): - self.vm.set_console() - self.vm.launch() - console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), - logger=self.log.getChild('console')) - console_drainer.start() - self.log.info('VM launched, waiting for boot confirmation from guest') - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - - if set_up_ssh_connection: - self.log.info('Setting up the SSH connection') - self.ssh_connect(self.username, self.ssh_key) diff --git a/tests/avocado/avocado_qemu/linuxtest.py b/tests/avocado/avocado_qemu/linuxtest.py new file mode 100644 index 0000000000..e1dc838b1c --- /dev/null +++ b/tests/avocado/avocado_qemu/linuxtest.py @@ -0,0 +1,253 @@ +# Test class and utilities for functional Linux-based tests +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa <crosa@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import shutil + +from avocado.utils import cloudinit, datadrainer, process, vmimage + +from . import LinuxSSHMixIn +from . import QemuSystemTest + +if os.path.islink(os.path.dirname(os.path.dirname(__file__))): + # The link to the avocado tests dir in the source code directory + lnk = os.path.dirname(os.path.dirname(__file__)) + #: The QEMU root source directory + SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) +else: + SOURCE_DIR = BUILD_DIR + +class LinuxDistro: + """Represents a Linux distribution + + Holds information of known distros. + """ + #: A collection of known distros and their respective image checksum + KNOWN_DISTROS = { + 'fedora': { + '31': { + 'x86_64': + {'checksum': ('e3c1b309d9203604922d6e255c2c5d09' + '8a309c2d46215d8fc026954f3c5c27a0'), + 'pxeboot_url': ('https://archives.fedoraproject.org/' + 'pub/archive/fedora/linux/releases/31/' + 'Everything/x86_64/os/images/pxeboot/'), + 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-' + '08a96687f73c ro no_timer_check ' + 'net.ifnames=0 console=tty1 ' + 'console=ttyS0,115200n8'), + }, + 'aarch64': + {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae' + 'd2af0ad0329383d5639c997fdf16fe49'), + 'pxeboot_url': 'https://archives.fedoraproject.org/' + 'pub/archive/fedora/linux/releases/31/' + 'Everything/aarch64/os/images/pxeboot/', + 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-' + '355e8475b0a7 ro earlyprintk=pl011,0x9000000' + ' ignore_loglevel no_timer_check' + ' printk.time=1 rd_NO_PLYMOUTH' + ' console=ttyAMA0'), + }, + 'ppc64': + {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4' + '3f991c506f2cc390dc4efa2026ad2f58')}, + 's390x': + {'checksum': ('4caaab5a434fd4d1079149a072fdc789' + '1e354f834d355069ca982fdcaf5a122d')}, + }, + '32': { + 'aarch64': + {'checksum': ('b367755c664a2d7a26955bbfff985855' + 'adfa2ca15e908baf15b4b176d68d3967'), + 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' + 'releases/32/Server/aarch64/os/images/' + 'pxeboot/'), + 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-' + '14d95c0e90c5 ro no_timer_check net.ifnames=0' + ' console=tty1 console=ttyS0,115200n8'), + }, + }, + '33': { + 'aarch64': + {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1' + 'a81f386a17f969c1d1c7c87031008a6b'), + 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' + 'releases/33/Server/aarch64/os/images/' + 'pxeboot/'), + 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-' + '1126a0208f8a ro no_timer_check net.ifnames=0' + ' console=tty1 console=ttyS0,115200n8' + ' console=tty0'), + }, + }, + } + } + + def __init__(self, name, version, arch): + self.name = name + self.version = version + self.arch = arch + try: + info = self.KNOWN_DISTROS.get(name).get(version).get(arch) + except AttributeError: + # Unknown distro + info = None + self._info = info or {} + + @property + def checksum(self): + """Gets the cloud-image file checksum""" + return self._info.get('checksum', None) + + @checksum.setter + def checksum(self, value): + self._info['checksum'] = value + + @property + def pxeboot_url(self): + """Gets the repository url where pxeboot files can be found""" + return self._info.get('pxeboot_url', None) + + @property + def default_kernel_params(self): + """Gets the default kernel parameters""" + return self._info.get('kernel_params', None) + + +class LinuxTest(LinuxSSHMixIn, QemuSystemTest): + """Facilitates having a cloud-image Linux based available. + + For tests that intend to interact with guests, this is a better choice + to start with than the more vanilla `QemuSystemTest` class. + """ + + distro = None + username = 'root' + password = 'password' + smp = '2' + memory = '1024' + + def _set_distro(self): + distro_name = self.params.get( + 'distro', + default=self._get_unique_tag_val('distro')) + if not distro_name: + distro_name = 'fedora' + + distro_version = self.params.get( + 'distro_version', + default=self._get_unique_tag_val('distro_version')) + if not distro_version: + distro_version = '31' + + self.distro = LinuxDistro(distro_name, distro_version, self.arch) + + # The distro checksum behaves differently than distro name and + # version. First, it does not respect a tag with the same + # name, given that it's not expected to be used for filtering + # (distro name versions are the natural choice). Second, the + # order of precedence is: parameter, attribute and then value + # from KNOWN_DISTROS. + distro_checksum = self.params.get('distro_checksum', + default=None) + if distro_checksum: + self.distro.checksum = distro_checksum + + def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'): + super().setUp() + self.require_netdev('user') + self._set_distro() + self.vm.add_args('-smp', self.smp) + self.vm.add_args('-m', self.memory) + # The following network device allows for SSH connections + self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', '%s,netdev=vnet' % network_device_type) + self.set_up_boot() + if ssh_pubkey is None: + ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() + self.set_up_cloudinit(ssh_pubkey) + + def set_up_existing_ssh_keys(self): + ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub') + source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa') + ssh_dir = os.path.join(self.workdir, '.ssh') + os.mkdir(ssh_dir, mode=0o700) + ssh_private_key = os.path.join(ssh_dir, + os.path.basename(source_private_key)) + shutil.copyfile(source_private_key, ssh_private_key) + os.chmod(ssh_private_key, 0o600) + return (ssh_public_key, ssh_private_key) + + def download_boot(self): + # Set the qemu-img binary. + # If none is available, the test will cancel. + vmimage.QEMU_IMG = super().get_qemu_img() + + self.log.info('Downloading/preparing boot image') + # Fedora 31 only provides ppc64le images + image_arch = self.arch + if self.distro.name == 'fedora': + if image_arch == 'ppc64': + image_arch = 'ppc64le' + + try: + boot = vmimage.get( + self.distro.name, arch=image_arch, version=self.distro.version, + checksum=self.distro.checksum, + algorithm='sha256', + cache_dir=self.cache_dirs[0], + snapshot_dir=self.workdir) + except: + self.cancel('Failed to download/prepare boot image') + return boot.path + + def prepare_cloudinit(self, ssh_pubkey=None): + self.log.info('Preparing cloudinit image') + try: + cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') + pubkey_content = None + if ssh_pubkey: + with open(ssh_pubkey) as pubkey: + pubkey_content = pubkey.read() + cloudinit.iso(cloudinit_iso, self.name, + username=self.username, + password=self.password, + # QEMU's hard coded usermode router address + phone_home_host='10.0.2.2', + phone_home_port=self.phone_server.server_port, + authorized_key=pubkey_content) + except Exception: + self.cancel('Failed to prepare the cloudinit image') + return cloudinit_iso + + def set_up_boot(self): + path = self.download_boot() + self.vm.add_args('-drive', 'file=%s' % path) + + def set_up_cloudinit(self, ssh_pubkey=None): + self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), + self.name) + cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) + self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) + + def launch_and_wait(self, set_up_ssh_connection=True): + self.vm.set_console() + self.vm.launch() + console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), + logger=self.log.getChild('console')) + console_drainer.start() + self.log.info('VM launched, waiting for boot confirmation from guest') + while not self.phone_server.instance_phoned_back: + self.phone_server.handle_request() + + if set_up_ssh_connection: + self.log.info('Setting up the SSH connection') + self.ssh_connect(self.username, self.ssh_key) diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py index cdce4cbcba..a029ef4ad1 100644 --- a/tests/avocado/boot_linux.py +++ b/tests/avocado/boot_linux.py @@ -10,7 +10,8 @@ import os -from avocado_qemu import LinuxTest, BUILD_DIR +from avocado_qemu.linuxtest import LinuxTest +from avocado_qemu import BUILD_DIR from avocado import skipUnless diff --git a/tests/avocado/hotplug_blk.py b/tests/avocado/hotplug_blk.py index 5dc30f6616..d55ded1c1d 100644 --- a/tests/avocado/hotplug_blk.py +++ b/tests/avocado/hotplug_blk.py @@ -9,7 +9,7 @@ import time -from avocado_qemu import LinuxTest +from avocado_qemu.linuxtest import LinuxTest class HotPlug(LinuxTest): diff --git a/tests/avocado/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py index 292bb43e4d..342c838539 100644 --- a/tests/avocado/hotplug_cpu.py +++ b/tests/avocado/hotplug_cpu.py @@ -8,7 +8,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import LinuxTest +from avocado_qemu.linuxtest import LinuxTest class HotPlugCPU(LinuxTest): diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py index 09e694bd40..008f214397 100644 --- a/tests/avocado/intel_iommu.py +++ b/tests/avocado/intel_iommu.py @@ -10,7 +10,7 @@ import os from avocado import skipUnless -from avocado_qemu import LinuxTest +from avocado_qemu.linuxtest import LinuxTest @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py index f3a43dc98c..b4673261ce 100644 --- a/tests/avocado/replay_linux.py +++ b/tests/avocado/replay_linux.py @@ -19,7 +19,7 @@ from avocado.utils import network from avocado.utils import vmimage from avocado.utils import datadrainer from avocado.utils.path import find_command -from avocado_qemu import LinuxTest +from avocado_qemu.linuxtest import LinuxTest class ReplayLinux(LinuxTest): """ diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py index 4ebfa7128c..aadda71e4b 100644 --- a/tests/avocado/smmu.py +++ b/tests/avocado/smmu.py @@ -10,7 +10,8 @@ import os from avocado import skipUnless -from avocado_qemu import LinuxTest, BUILD_DIR +from avocado_qemu import BUILD_DIR +from avocado_qemu.linuxtest import LinuxTest @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') From 3b067b701d08ae292c4757caec81fc17dce69b11 Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Fri, 19 Jul 2024 11:54:08 +0200 Subject: [PATCH 2343/2884] tests/avocado/mem-addr-space-check: Remove unused "import signal" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "signal" module is not used here, so we can remove this import statement. Message-ID: <20240719095408.33298-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/mem-addr-space-check.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/avocado/mem-addr-space-check.py b/tests/avocado/mem-addr-space-check.py index 85541ea051..d3974599f4 100644 --- a/tests/avocado/mem-addr-space-check.py +++ b/tests/avocado/mem-addr-space-check.py @@ -9,7 +9,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later from avocado_qemu import QemuSystemTest -import signal import time class MemAddrCheck(QemuSystemTest): From 322a2e33f46221995999408472c150c5dfba31f4 Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Thu, 18 Jul 2024 19:31:25 +0200 Subject: [PATCH 2344/2884] tests/avocado: Remove the remainders of the virtiofs_submounts test The virtiofs_submounts test has been removed in commit 5da7701e2a ("virtiofsd: Remove test"), so we don't need this files anymore. Message-ID: <20240718173125.489901-1-thuth@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- .../virtiofs_submounts.py.data/cleanup.sh | 46 ------ .../guest-cleanup.sh | 30 ---- .../virtiofs_submounts.py.data/guest.sh | 138 ------------------ .../virtiofs_submounts.py.data/host.sh | 127 ---------------- 4 files changed, 341 deletions(-) delete mode 100644 tests/avocado/virtiofs_submounts.py.data/cleanup.sh delete mode 100644 tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh delete mode 100644 tests/avocado/virtiofs_submounts.py.data/guest.sh delete mode 100644 tests/avocado/virtiofs_submounts.py.data/host.sh diff --git a/tests/avocado/virtiofs_submounts.py.data/cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/cleanup.sh deleted file mode 100644 index 2a6579a0fe..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/cleanup.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 <scratch dir>" -} - -scratch_dir=$1 -if [ -z "$scratch_dir" ]; then - print_usage "$0" 'Scratch dir not given' >&2 - exit 1 -fi - -cd "$scratch_dir/share" || exit 1 -mps=(mnt*) -mp_i=0 -for mp in "${mps[@]}"; do - mp_i=$((mp_i + 1)) - printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}" - - sudo umount -R "$mp" - rm -rf "$mp" -done -echo - -rm some-file -cd .. -rmdir share - -imgs=(fs*.img) -img_i=0 -for img in "${imgs[@]}"; do - img_i=$((img_i + 1)) - printf "Detaching and deleting %i/%i...\r" "$img_i" "${#imgs[@]}" - - dev=$(losetup -j "$img" | sed -e 's/:.*//') - sudo losetup -d "$dev" - rm -f "$img" -done -echo - -echo 'Done.' diff --git a/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh deleted file mode 100644 index 729cb2d1a5..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 <scratch dir>" -} - -scratch_dir=$1 -if [ -z "$scratch_dir" ]; then - print_usage "$0" 'Scratch dir not given' >&2 - exit 1 -fi - -cd "$scratch_dir/share" || exit 1 - -mps=(mnt*) -mp_i=0 -for mp in "${mps[@]}"; do - mp_i=$((mp_i + 1)) - printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}" - - sudo umount -R "$mp" -done -echo - -echo 'Done.' diff --git a/tests/avocado/virtiofs_submounts.py.data/guest.sh b/tests/avocado/virtiofs_submounts.py.data/guest.sh deleted file mode 100644 index 59ba40fde1..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/guest.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 <shared dir>" - echo '(The shared directory is the "share" directory in the scratch' \ - 'directory)' -} - -shared_dir=$1 -if [ -z "$shared_dir" ]; then - print_usage "$0" 'Shared dir not given' >&2 - exit 1 -fi - -cd "$shared_dir" - -# FIXME: This should not be necessary, but it is. In order for all -# submounts to be proper mount points, we need to visit them. -# (Before we visit them, they will not be auto-mounted, and so just -# appear as normal directories, with the catch that their st_ino will -# be the st_ino of the filesystem they host, while the st_dev will -# still be the st_dev of the parent.) -# `find` does not work, because it will refuse to touch the mount -# points as long as they are not mounted; their st_dev being shared -# with the parent and st_ino just being the root node's inode ID -# will practically ensure that this node exists elsewhere on the -# filesystem, and `find` is required to recognize loops and not to -# follow them. -# Thus, we have to manually visit all nodes first. - -mnt_i=0 - -function recursively_visit() -{ - pushd "$1" >/dev/null - for entry in *; do - if [[ "$entry" == mnt* ]]; then - mnt_i=$((mnt_i + 1)) - printf "Triggering auto-mount $mnt_i...\r" - fi - - if [ -d "$entry" ]; then - recursively_visit "$entry" - fi - done - popd >/dev/null -} - -recursively_visit . -echo - - -if [ -n "$(find -name not-mounted)" ]; then - echo "Error: not-mounted files visible on mount points:" >&2 - find -name not-mounted >&2 - exit 1 -fi - -if [ ! -f some-file -o "$(cat some-file)" != 'root' ]; then - echo "Error: Bad file in the share root" >&2 - exit 1 -fi - -shopt -s nullglob - -function check_submounts() -{ - local base_path=$1 - - for mp in mnt*; do - printf "Checking submount %i...\r" "$((${#devs[@]} + 1))" - - mp_i=$(echo "$mp" | sed -e 's/mnt//') - dev=$(stat -c '%D' "$mp") - - if [ -n "${devs[mp_i]}" ]; then - echo "Error: $mp encountered twice" >&2 - exit 1 - fi - devs[mp_i]=$dev - - pushd "$mp" >/dev/null - path="$base_path$mp" - while true; do - expected_content="$(printf '%s\n%s\n' "$mp_i" "$path")" - if [ ! -f some-file ]; then - echo "Error: $PWD/some-file does not exist" >&2 - exit 1 - fi - - if [ "$(cat some-file)" != "$expected_content" ]; then - echo "Error: Bad content in $PWD/some-file:" >&2 - echo '--- found ---' - cat some-file - echo '--- expected ---' - echo "$expected_content" - exit 1 - fi - if [ "$(stat -c '%D' some-file)" != "$dev" ]; then - echo "Error: $PWD/some-file has the wrong device ID" >&2 - exit 1 - fi - - if [ -d sub ]; then - if [ "$(stat -c '%D' sub)" != "$dev" ]; then - echo "Error: $PWD/some-file has the wrong device ID" >&2 - exit 1 - fi - cd sub - path="$path/sub" - else - if [ -n "$(echo mnt*)" ]; then - check_submounts "$path/" - fi - break - fi - done - popd >/dev/null - done -} - -root_dev=$(stat -c '%D' some-file) -devs=() -check_submounts '' -echo - -reused_devs=$(echo "$root_dev ${devs[@]}" | tr ' ' '\n' | sort | uniq -d) -if [ -n "$reused_devs" ]; then - echo "Error: Reused device IDs: $reused_devs" >&2 - exit 1 -fi - -echo "Test passed for ${#devs[@]} submounts." diff --git a/tests/avocado/virtiofs_submounts.py.data/host.sh b/tests/avocado/virtiofs_submounts.py.data/host.sh deleted file mode 100644 index d8a9afebdb..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/host.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -mount_count=128 - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 <scratch dir> [seed]" - echo "(If no seed is given, it will be randomly generated.)" -} - -scratch_dir=$1 -if [ -z "$scratch_dir" ]; then - print_usage "$0" 'No scratch dir given' >&2 - exit 1 -fi - -if [ ! -d "$scratch_dir" ]; then - print_usage "$0" "$scratch_dir is not a directory" >&2 - exit 1 -fi - -seed=$2 -if [ -z "$seed" ]; then - seed=$RANDOM -fi -RANDOM=$seed - -echo "Seed: $seed" - -set -e -shopt -s nullglob - -cd "$scratch_dir" -if [ -d share ]; then - echo 'Error: This directory seems to be in use already' >&2 - exit 1 -fi - -for ((i = 0; i < $mount_count; i++)); do - printf "Setting up fs %i/%i...\r" "$((i + 1))" "$mount_count" - - rm -f fs$i.img - truncate -s 512M fs$i.img - mkfs.xfs -q fs$i.img - devs[i]=$(sudo losetup -f --show fs$i.img) -done -echo - -top_level_mounts=$((RANDOM % mount_count + 1)) - -mkdir -p share -echo 'root' > share/some-file - -for ((i = 0; i < $top_level_mounts; i++)); do - printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count" - - mkdir -p share/mnt$i - touch share/mnt$i/not-mounted - sudo mount "${devs[i]}" share/mnt$i - sudo chown "$(id -u):$(id -g)" share/mnt$i - - pushd share/mnt$i >/dev/null - path=mnt$i - nesting=$((RANDOM % 4)) - for ((j = 0; j < $nesting; j++)); do - cat > some-file <<EOF -$i -$path -EOF - mkdir sub - cd sub - path="$path/sub" - done -cat > some-file <<EOF -$i -$path -EOF - popd >/dev/null -done - -for ((; i < $mount_count; i++)); do - printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count" - - mp_i=$((i % top_level_mounts)) - - pushd share/mnt$mp_i >/dev/null - path=mnt$mp_i - while true; do - sub_mp="$(echo mnt*)" - if cd sub 2>/dev/null; then - path="$path/sub" - elif [ -n "$sub_mp" ] && cd "$sub_mp" 2>/dev/null; then - path="$path/$sub_mp" - else - break - fi - done - mkdir mnt$i - touch mnt$i/not-mounted - sudo mount "${devs[i]}" mnt$i - sudo chown "$(id -u):$(id -g)" mnt$i - - cd mnt$i - path="$path/mnt$i" - nesting=$((RANDOM % 4)) - for ((j = 0; j < $nesting; j++)); do - cat > some-file <<EOF -$i -$path -EOF - mkdir sub - cd sub - path="$path/sub" - done - cat > some-file <<EOF -$i -$path -EOF - popd >/dev/null -done -echo - -echo 'Done.' From 9bde5bb2adcecb8e67e2c0661efc405fbf74e496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@redhat.com> Date: Mon, 22 Jul 2024 10:55:47 +0200 Subject: [PATCH 2345/2884] tests/avocado/machine_aspeed.py: Increase timeout for TPM test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some runners, test_arm_ast2600_evb_buildroot_tpm can take longer than 90s to complete. Increase timeout for these. Reported-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20240722085547.90650-1-clg@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/machine_aspeed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index 29e6b388a9..f8e263d37e 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -87,7 +87,7 @@ class AST1030Machine(QemuSystemTest): class AST2x00Machine(QemuSystemTest): - timeout = 90 + timeout = 180 def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern(self, success_message, From d4ff34965e25f2041646a1337d71b39bb8cb2ba2 Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Wed, 3 Jul 2024 11:09:04 +0200 Subject: [PATCH 2346/2884] hw: Fix crash that happens when introspecting scsi-block on older machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "make check SPEED=slow" is currently failing the device-introspect-test on older machine types since introspecting "scsi-block" is causing an abort: $ ./qemu-system-x86_64 -M pc-q35-8.0 -monitor stdio QEMU 9.0.50 monitor - type 'help' for more information (qemu) device_add scsi-block,help Unexpected error in object_property_find_err() at ../../devel/qemu/qom/object.c:1357: can't apply global scsi-disk-base.migrate-emulated-scsi-request=false: Property 'scsi-block.migrate-emulated-scsi-request' not found Aborted (core dumped) The problem is that the compat code tries to change the "migrate-emulated-scsi-request" property for all devices that are derived from "scsi-block", but the property has only been added to "scsi-hd" and "scsi-cd" via the DEFINE_SCSI_DISK_PROPERTIES macro. Thus let's fix the problem by only changing the property on the devices that really have this property. Fixes: b4912afa5f ("scsi-disk: Fix crash for VM configured with USB CDROM after live migration") Message-ID: <20240703090904.909720-1-thuth@redhat.com> Acked-by: Hyman Huang <yong.huang@smartx.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> --- hw/core/machine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index bc38cad7f2..8a878f84d7 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -36,7 +36,8 @@ GlobalProperty hw_compat_9_0[] = { {"arm-cpu", "backcompat-cntfrq", "true" }, - {"scsi-disk-base", "migrate-emulated-scsi-request", "false" }, + { "scsi-hd", "migrate-emulated-scsi-request", "false" }, + { "scsi-cd", "migrate-emulated-scsi-request", "false" }, {"vfio-pci", "skip-vsc-check", "false" }, { "virtio-pci", "x-pcie-pm-no-soft-reset", "off" }, {"sd-card", "spec_version", "2" }, From 763719d253da18e011d28b08fd92125f7ae5e9ae Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:36 -0400 Subject: [PATCH 2347/2884] qtest/fuzz: make range overlap check more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use ranges_overlap() instead of open-coding the overlap check to improve the readability of the code. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alexander Bulekov <alxndr@bu.edu> Message-ID: <20240722040742.11513-8-yaoxt.fnst@fujitsu.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/qtest/fuzz/generic_fuzz.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index ec842e03c5..d107a496da 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include <wordexp.h> @@ -211,7 +212,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) i < dma_regions->len && (avoid_double_fetches || qtest_log_enabled); ++i) { region = g_array_index(dma_regions, address_range, i); - if (addr < region.addr + region.size && addr + len > region.addr) { + if (ranges_overlap(addr, len, region.addr, region.size)) { double_fetch = true; if (addr < region.addr && avoid_double_fetches) { From 2d9588ad70e5366ffed5d61d6ffa717f4d56a885 Mon Sep 17 00:00:00 2001 From: Igor Mammedov <imammedo@redhat.com> Date: Tue, 16 Jul 2024 14:59:30 +0200 Subject: [PATCH 2348/2884] tests: increase timeout per instance of bios-tables-test CI often fails 'cross-i686-tci' job due to runner slowness Log shows that test almost complete, with a few remaining when bios-tables-test timeout hits: 19/270 qemu:qtest+qtest-aarch64 / qtest-aarch64/bios-tables-test TIMEOUT 610.02s killed by signal 15 SIGTERM ... stderr: TAP parsing error: Too few tests run (expected 8, got 7) At the same time overall job running time is only ~30 out of 1hr allowed. Increase bios-tables-test instance timeout on 5min as a fix for slow CI runners. Signed-off-by: Igor Mammedov <imammedo@redhat.com> Message-ID: <20240716125930.620861-1-imammedo@redhat.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/qtest/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 6508bfb1a2..ff9200f882 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -1,6 +1,6 @@ slow_qtests = { 'aspeed_smc-test': 360, - 'bios-tables-test' : 610, + 'bios-tables-test' : 910, 'cdrom-test' : 610, 'device-introspect-test' : 720, 'migration-test' : 480, From eed0e8ffa38f0695c0519508f6e4f5a3297cbd67 Mon Sep 17 00:00:00 2001 From: Collin Walling <walling@linux.ibm.com> Date: Fri, 19 Jul 2024 14:17:41 -0400 Subject: [PATCH 2349/2884] target/s390x: filter deprecated properties based on model expansion type Currently, there is no way to execute the query-cpu-model-expansion command to retrieve a comprehenisve list of deprecated properties, as the result is dependent per-model. To enable this, the expansion output is modified as such: When reporting a "full" CPU model, show the *entire* list of deprecated properties regardless if they are supported on the model. A full expansion outputs all known CPU model properties anyway, so it makes sense to report all deprecated properties here too. This allows management apps to query a single model (e.g. host) to acquire the full list of deprecated properties. Additionally, when reporting a "static" CPU model, the command will only show deprecated properties that are a subset of the model's *enabled* properties. This is more accurate than how the query was handled before, which blindly reported deprecated properties that were never otherwise introduced for certain models. Acked-by: David Hildenbrand <david@redhat.com> Suggested-by: Jiri Denemark <jdenemar@redhat.com> Signed-off-by: Collin Walling <walling@linux.ibm.com> Message-ID: <20240719181741.35146-1-walling@linux.ibm.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- qapi/machine-target.json | 5 +++-- target/s390x/cpu_models_sysemu.c | 16 +++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 7edb876b5c..a552e2b0ce 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -21,8 +21,9 @@ # @props: a dictionary of QOM properties to be applied # # @deprecated-props: a list of properties that are flagged as deprecated -# by the CPU vendor. These props are a subset of the full model's -# definition list of properties. (since 9.1) +# by the CPU vendor. These properties are either a subset of the +# properties enabled on the CPU model, or a set of properties +# deprecated across all models for the architecture. # # Since: 2.8 ## diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index 977fbc6522..94dd798b4c 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -174,11 +174,15 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, bool delta_changes) { QDict *qdict = qdict_new(); - S390FeatBitmap bitmap; + S390FeatBitmap bitmap, deprecated; /* always fallback to the static base model */ info->name = g_strdup_printf("%s-base", model->def->name); + /* features flagged as deprecated */ + bitmap_zero(deprecated, S390_FEAT_MAX); + s390_get_deprecated_features(deprecated); + if (delta_changes) { /* features deleted from the base feature set */ bitmap_andnot(bitmap, model->def->base_feat, model->features, @@ -193,6 +197,9 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat); } + + /* deprecated features that are a subset of the model's enabled features */ + bitmap_and(deprecated, deprecated, model->features, S390_FEAT_MAX); } else { /* expand all features */ s390_feat_bitmap_to_ascii(model->features, qdict, @@ -207,12 +214,7 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, info->props = QOBJECT(qdict); } - /* features flagged as deprecated */ - bitmap_zero(bitmap, S390_FEAT_MAX); - s390_get_deprecated_features(bitmap); - - bitmap_and(bitmap, bitmap, model->def->full_feat, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(bitmap, &info->deprecated_props, list_add_feat); + s390_feat_bitmap_to_ascii(deprecated, &info->deprecated_props, list_add_feat); info->has_deprecated_props = !!info->deprecated_props; } From d522aef88d42a05e46290c97602936a7f6d307e3 Mon Sep 17 00:00:00 2001 From: Arun Kumar <arun.kka@samsung.com> Date: Mon, 1 Jul 2024 12:38:55 +0530 Subject: [PATCH 2350/2884] hw/nvme: add cross namespace copy support Extend copy command to copy user data across different namespaces via support for specifying a namespace for each source range Signed-off-by: Arun Kumar <arun.kka@samsung.com> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 355 ++++++++++++++++++++++++++++++++----------- include/block/nvme.h | 37 +++-- 2 files changed, 289 insertions(+), 103 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index f7b4e4627d..385438a9ae 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -2696,6 +2696,7 @@ typedef struct NvmeCopyAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; + NvmeCtrl *n; int ret; void *ranges; @@ -2714,6 +2715,8 @@ typedef struct NvmeCopyAIOCB { uint64_t slba; NvmeZone *zone; + NvmeNamespace *sns; + uint32_t tcl; } NvmeCopyAIOCB; static void nvme_copy_cancel(BlockAIOCB *aiocb) @@ -2760,13 +2763,19 @@ static void nvme_copy_done(NvmeCopyAIOCB *iocb) static void nvme_do_copy(NvmeCopyAIOCB *iocb); -static void nvme_copy_source_range_parse_format0(void *ranges, int idx, - uint64_t *slba, uint32_t *nlb, - uint16_t *apptag, - uint16_t *appmask, - uint64_t *reftag) +static void nvme_copy_source_range_parse_format0_2(void *ranges, + int idx, uint64_t *slba, + uint32_t *nlb, + uint32_t *snsid, + uint16_t *apptag, + uint16_t *appmask, + uint64_t *reftag) { - NvmeCopySourceRangeFormat0 *_ranges = ranges; + NvmeCopySourceRangeFormat0_2 *_ranges = ranges; + + if (snsid) { + *snsid = le32_to_cpu(_ranges[idx].sparams); + } if (slba) { *slba = le64_to_cpu(_ranges[idx].slba); @@ -2789,13 +2798,19 @@ static void nvme_copy_source_range_parse_format0(void *ranges, int idx, } } -static void nvme_copy_source_range_parse_format1(void *ranges, int idx, - uint64_t *slba, uint32_t *nlb, - uint16_t *apptag, - uint16_t *appmask, - uint64_t *reftag) +static void nvme_copy_source_range_parse_format1_3(void *ranges, int idx, + uint64_t *slba, + uint32_t *nlb, + uint32_t *snsid, + uint16_t *apptag, + uint16_t *appmask, + uint64_t *reftag) { - NvmeCopySourceRangeFormat1 *_ranges = ranges; + NvmeCopySourceRangeFormat1_3 *_ranges = ranges; + + if (snsid) { + *snsid = le32_to_cpu(_ranges[idx].sparams); + } if (slba) { *slba = le64_to_cpu(_ranges[idx].slba); @@ -2827,18 +2842,20 @@ static void nvme_copy_source_range_parse_format1(void *ranges, int idx, static void nvme_copy_source_range_parse(void *ranges, int idx, uint8_t format, uint64_t *slba, uint32_t *nlb, - uint16_t *apptag, uint16_t *appmask, - uint64_t *reftag) + uint32_t *snsid, uint16_t *apptag, + uint16_t *appmask, uint64_t *reftag) { switch (format) { case NVME_COPY_FORMAT_0: - nvme_copy_source_range_parse_format0(ranges, idx, slba, nlb, apptag, - appmask, reftag); + case NVME_COPY_FORMAT_2: + nvme_copy_source_range_parse_format0_2(ranges, idx, slba, nlb, snsid, + apptag, appmask, reftag); break; case NVME_COPY_FORMAT_1: - nvme_copy_source_range_parse_format1(ranges, idx, slba, nlb, apptag, - appmask, reftag); + case NVME_COPY_FORMAT_3: + nvme_copy_source_range_parse_format1_3(ranges, idx, slba, nlb, snsid, + apptag, appmask, reftag); break; default: @@ -2854,10 +2871,10 @@ static inline uint16_t nvme_check_copy_mcl(NvmeNamespace *ns, for (int idx = 0; idx < nr; idx++) { uint32_t nlb; nvme_copy_source_range_parse(iocb->ranges, idx, iocb->format, NULL, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); copy_len += nlb; } - + iocb->tcl = copy_len; if (copy_len > ns->id_ns.mcl) { return NVME_CMD_SIZE_LIMIT | NVME_DNR; } @@ -2869,11 +2886,11 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *dns = req->ns; uint32_t nlb; nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); if (ret < 0) { iocb->ret = ret; @@ -2882,8 +2899,8 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret) goto out; } - if (ns->params.zoned) { - nvme_advance_zone_wp(ns, iocb->zone, nlb); + if (dns->params.zoned) { + nvme_advance_zone_wp(dns, iocb->zone, nlb); } iocb->idx++; @@ -2896,25 +2913,25 @@ static void nvme_copy_out_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *dns = req->ns; uint32_t nlb; size_t mlen; uint8_t *mbounce; - if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { + if (ret < 0 || iocb->ret < 0 || !dns->lbaf.ms) { goto out; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); - mlen = nvme_m2b(ns, nlb); - mbounce = iocb->bounce + nvme_l2b(ns, nlb); + mlen = nvme_m2b(dns, nlb); + mbounce = iocb->bounce + nvme_l2b(dns, nlb); qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, mbounce, mlen); - iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_moff(ns, iocb->slba), + iocb->aiocb = blk_aio_pwritev(dns->blkconf.blk, nvme_moff(dns, iocb->slba), &iocb->iov, 0, nvme_copy_out_completed_cb, iocb); @@ -2928,12 +2945,15 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *sns = iocb->sns; + NvmeNamespace *dns = req->ns; + NvmeCopyCmd *copy = NULL; + uint8_t *mbounce = NULL; uint32_t nlb; uint64_t slba; uint16_t apptag, appmask; uint64_t reftag; - size_t len; + size_t len, mlen; uint16_t status; if (ret < 0) { @@ -2944,43 +2964,51 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, - &nlb, &apptag, &appmask, &reftag); - len = nvme_l2b(ns, nlb); + &nlb, NULL, &apptag, &appmask, &reftag); trace_pci_nvme_copy_out(iocb->slba, nlb); - if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { - NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + len = nvme_l2b(sns, nlb); + + if (NVME_ID_NS_DPS_TYPE(sns->id_ns.dps)) { + copy = (NvmeCopyCmd *)&req->cmd; uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); - uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); - size_t mlen = nvme_m2b(ns, nlb); - uint8_t *mbounce = iocb->bounce + nvme_l2b(ns, nlb); + mlen = nvme_m2b(sns, nlb); + mbounce = iocb->bounce + nvme_l2b(sns, nlb); - status = nvme_dif_mangle_mdata(ns, mbounce, mlen, slba); + status = nvme_dif_mangle_mdata(sns, mbounce, mlen, slba); if (status) { goto invalid; } - status = nvme_dif_check(ns, iocb->bounce, len, mbounce, mlen, prinfor, + status = nvme_dif_check(sns, iocb->bounce, len, mbounce, mlen, prinfor, slba, apptag, appmask, &reftag); if (status) { goto invalid; } + } + + if (NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + copy = (NvmeCopyCmd *)&req->cmd; + uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); + + mlen = nvme_m2b(dns, nlb); + mbounce = iocb->bounce + nvme_l2b(dns, nlb); apptag = le16_to_cpu(copy->apptag); appmask = le16_to_cpu(copy->appmask); if (prinfow & NVME_PRINFO_PRACT) { - status = nvme_check_prinfo(ns, prinfow, iocb->slba, iocb->reftag); + status = nvme_check_prinfo(dns, prinfow, iocb->slba, iocb->reftag); if (status) { goto invalid; } - nvme_dif_pract_generate_dif(ns, iocb->bounce, len, mbounce, mlen, + nvme_dif_pract_generate_dif(dns, iocb->bounce, len, mbounce, mlen, apptag, &iocb->reftag); } else { - status = nvme_dif_check(ns, iocb->bounce, len, mbounce, mlen, + status = nvme_dif_check(dns, iocb->bounce, len, mbounce, mlen, prinfow, iocb->slba, apptag, appmask, &iocb->reftag); if (status) { @@ -2989,13 +3017,13 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) } } - status = nvme_check_bounds(ns, iocb->slba, nlb); + status = nvme_check_bounds(dns, iocb->slba, nlb); if (status) { goto invalid; } - if (ns->params.zoned) { - status = nvme_check_zone_write(ns, iocb->zone, iocb->slba, nlb); + if (dns->params.zoned) { + status = nvme_check_zone_write(dns, iocb->zone, iocb->slba, nlb); if (status) { goto invalid; } @@ -3008,7 +3036,10 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, iocb->bounce, len); - iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_l2b(ns, iocb->slba), + block_acct_start(blk_get_stats(dns->blkconf.blk), &iocb->acct.write, 0, + BLOCK_ACCT_WRITE); + + iocb->aiocb = blk_aio_pwritev(dns->blkconf.blk, nvme_l2b(dns, iocb->slba), &iocb->iov, 0, nvme_copy_out_cb, iocb); return; @@ -3023,23 +3054,22 @@ out: static void nvme_copy_in_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; - NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *sns = iocb->sns; uint64_t slba; uint32_t nlb; - if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { + if (ret < 0 || iocb->ret < 0 || !sns->lbaf.ms) { goto out; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); qemu_iovec_reset(&iocb->iov); - qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(ns, nlb), - nvme_m2b(ns, nlb)); + qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(sns, nlb), + nvme_m2b(sns, nlb)); - iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_moff(ns, slba), + iocb->aiocb = blk_aio_preadv(sns->blkconf.blk, nvme_moff(sns, slba), &iocb->iov, 0, nvme_copy_in_completed_cb, iocb); return; @@ -3048,14 +3078,78 @@ out: nvme_copy_in_completed_cb(iocb, ret); } +static inline bool nvme_csi_supports_copy(uint8_t csi) +{ + return csi == NVME_CSI_NVM || csi == NVME_CSI_ZONED; +} + +static inline bool nvme_copy_ns_format_match(NvmeNamespace *sns, + NvmeNamespace *dns) +{ + return sns->lbaf.ds == dns->lbaf.ds && sns->lbaf.ms == dns->lbaf.ms; +} + +static bool nvme_copy_matching_ns_format(NvmeNamespace *sns, NvmeNamespace *dns, + bool pi_enable) +{ + if (!nvme_csi_supports_copy(sns->csi) || + !nvme_csi_supports_copy(dns->csi)) { + return false; + } + + if (!pi_enable && !nvme_copy_ns_format_match(sns, dns)) { + return false; + } + + if (pi_enable && (!nvme_copy_ns_format_match(sns, dns) || + sns->id_ns.dps != dns->id_ns.dps)) { + return false; + } + + return true; +} + +static inline bool nvme_copy_corresp_pi_match(NvmeNamespace *sns, + NvmeNamespace *dns) +{ + return sns->lbaf.ms == 0 && + ((dns->lbaf.ms == 8 && dns->pif == 0) || + (dns->lbaf.ms == 16 && dns->pif == 1)); +} + +static bool nvme_copy_corresp_pi_format(NvmeNamespace *sns, NvmeNamespace *dns, + bool sns_pi_en) +{ + if (!nvme_csi_supports_copy(sns->csi) || + !nvme_csi_supports_copy(dns->csi)) { + return false; + } + + if (!sns_pi_en && !nvme_copy_corresp_pi_match(sns, dns)) { + return false; + } + + if (sns_pi_en && !nvme_copy_corresp_pi_match(dns, sns)) { + return false; + } + + return true; +} + static void nvme_do_copy(NvmeCopyAIOCB *iocb) { NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *sns; + NvmeNamespace *dns = req->ns; + NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); + uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); uint64_t slba; uint32_t nlb; size_t len; uint16_t status; + uint32_t dnsid = le32_to_cpu(req->cmd.nsid); + uint32_t snsid = dnsid; if (iocb->ret < 0) { goto done; @@ -3065,40 +3159,124 @@ static void nvme_do_copy(NvmeCopyAIOCB *iocb) goto done; } - nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, - &nlb, NULL, NULL, NULL); - len = nvme_l2b(ns, nlb); + if (iocb->format == 2 || iocb->format == 3) { + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, + &slba, &nlb, &snsid, NULL, NULL, NULL); + if (snsid != dnsid) { + if (snsid == NVME_NSID_BROADCAST || + !nvme_nsid_valid(iocb->n, snsid)) { + status = NVME_INVALID_NSID | NVME_DNR; + goto invalid; + } + iocb->sns = nvme_ns(iocb->n, snsid); + if (unlikely(!iocb->sns)) { + status = NVME_INVALID_FIELD | NVME_DNR; + goto invalid; + } + } else { + if (((slba + nlb) > iocb->slba) && + ((slba + nlb) < (iocb->slba + iocb->tcl))) { + status = NVME_CMD_OVERLAP_IO_RANGE | NVME_DNR; + goto invalid; + } + } + } else { + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, + &slba, &nlb, NULL, NULL, NULL, NULL); + } + + sns = iocb->sns; + if ((snsid == dnsid) && NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + ((prinfor & NVME_PRINFO_PRACT) != (prinfow & NVME_PRINFO_PRACT))) { + status = NVME_INVALID_FIELD | NVME_DNR; + goto invalid; + } else if (snsid != dnsid) { + if (!NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + !NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if (!nvme_copy_matching_ns_format(sns, dns, false)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + if (NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if ((prinfor & NVME_PRINFO_PRACT) != + (prinfow & NVME_PRINFO_PRACT)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } else { + if (!nvme_copy_matching_ns_format(sns, dns, true)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + } + + if (!NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if (!(prinfow & NVME_PRINFO_PRACT)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } else { + if (!nvme_copy_corresp_pi_format(sns, dns, false)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + } + + if (NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + !NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if (!(prinfor & NVME_PRINFO_PRACT)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } else { + if (!nvme_copy_corresp_pi_format(sns, dns, true)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + } + } + len = nvme_l2b(sns, nlb); trace_pci_nvme_copy_source_range(slba, nlb); - if (nlb > le16_to_cpu(ns->id_ns.mssrl)) { + if (nlb > le16_to_cpu(sns->id_ns.mssrl)) { status = NVME_CMD_SIZE_LIMIT | NVME_DNR; goto invalid; } - status = nvme_check_bounds(ns, slba, nlb); + status = nvme_check_bounds(sns, slba, nlb); if (status) { goto invalid; } - if (NVME_ERR_REC_DULBE(ns->features.err_rec)) { - status = nvme_check_dulbe(ns, slba, nlb); + if (NVME_ERR_REC_DULBE(sns->features.err_rec)) { + status = nvme_check_dulbe(sns, slba, nlb); if (status) { goto invalid; } } - if (ns->params.zoned) { - status = nvme_check_zone_read(ns, slba, nlb); + if (sns->params.zoned) { + status = nvme_check_zone_read(sns, slba, nlb); if (status) { goto invalid; } } + g_free(iocb->bounce); + iocb->bounce = g_malloc_n(le16_to_cpu(sns->id_ns.mssrl), + sns->lbasz + sns->lbaf.ms); + qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, iocb->bounce, len); - iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_l2b(ns, slba), + block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, 0, + BLOCK_ACCT_READ); + + iocb->aiocb = blk_aio_preadv(sns->blkconf.blk, nvme_l2b(sns, slba), &iocb->iov, 0, nvme_copy_in_cb, iocb); return; @@ -3117,9 +3295,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) nvme_misc_cb, req); uint16_t nr = copy->nr + 1; uint8_t format = copy->control[0] & 0xf; - uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); - uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); - size_t len = sizeof(NvmeCopySourceRangeFormat0); + size_t len = sizeof(NvmeCopySourceRangeFormat0_2); uint16_t status; @@ -3128,13 +3304,9 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) iocb->ranges = NULL; iocb->zone = NULL; - if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) && - ((prinfor & NVME_PRINFO_PRACT) != (prinfow & NVME_PRINFO_PRACT))) { - status = NVME_INVALID_FIELD | NVME_DNR; - goto invalid; - } - - if (!(n->id_ctrl.ocfs & (1 << format))) { + if (!(n->id_ctrl.ocfs & (1 << format)) || + ((format == 2 || format == 3) && + !(n->features.hbs.cdfe & (1 << format)))) { trace_pci_nvme_err_copy_invalid_format(format); status = NVME_INVALID_FIELD | NVME_DNR; goto invalid; @@ -3145,14 +3317,14 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) goto invalid; } - if ((ns->pif == 0x0 && format != 0x0) || - (ns->pif != 0x0 && format != 0x1)) { + if ((ns->pif == 0x0 && (format != 0x0 && format != 0x2)) || + (ns->pif != 0x0 && (format != 0x1 && format != 0x3))) { status = NVME_INVALID_FORMAT | NVME_DNR; goto invalid; } if (ns->pif) { - len = sizeof(NvmeCopySourceRangeFormat1); + len = sizeof(NvmeCopySourceRangeFormat1_3); } iocb->format = format; @@ -3188,17 +3360,13 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) iocb->idx = 0; iocb->reftag = le32_to_cpu(copy->reftag); iocb->reftag |= (uint64_t)le32_to_cpu(copy->cdw3) << 32; - iocb->bounce = g_malloc_n(le16_to_cpu(ns->id_ns.mssrl), - ns->lbasz + ns->lbaf.ms); qemu_iovec_init(&iocb->iov, 1); - block_acct_start(blk_get_stats(ns->blkconf.blk), &iocb->acct.read, 0, - BLOCK_ACCT_READ); - block_acct_start(blk_get_stats(ns->blkconf.blk), &iocb->acct.write, 0, - BLOCK_ACCT_WRITE); - req->aiocb = &iocb->common; + iocb->sns = req->ns; + iocb->n = n; + iocb->bounce = NULL; nvme_do_copy(iocb); return NVME_NO_COMPLETE; @@ -4407,10 +4575,6 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_io_cmd(nvme_cid(req), nsid, nvme_sqid(req), req->cmd.opcode, nvme_io_opc_str(req->cmd.opcode)); - if (!nvme_nsid_valid(n, nsid)) { - return NVME_INVALID_NSID | NVME_DNR; - } - /* * In the base NVM command set, Flush may apply to all namespaces * (indicated by NSID being set to FFFFFFFFh). But if that feature is used @@ -4430,10 +4594,15 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) * device only supports namespace types that includes the NVM Flush command * (NVM and Zoned), so always do an NVM Flush. */ + if (req->cmd.opcode == NVME_CMD_FLUSH) { return nvme_flush(n, req); } + if (!nvme_nsid_valid(n, nsid) || nsid == NVME_NSID_BROADCAST) { + return NVME_INVALID_NSID | NVME_DNR; + } + ns = nvme_ns(n, nsid); if (unlikely(!ns)) { return NVME_INVALID_FIELD | NVME_DNR; @@ -8288,7 +8457,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->nn = cpu_to_le32(NVME_MAX_NAMESPACES); id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROES | NVME_ONCS_TIMESTAMP | NVME_ONCS_FEATURES | NVME_ONCS_DSM | - NVME_ONCS_COMPARE | NVME_ONCS_COPY); + NVME_ONCS_COMPARE | NVME_ONCS_COPY | + NVME_ONCS_NVMCSA | NVME_ONCS_NVMAFC); /* * NOTE: If this device ever supports a command set that does NOT use 0x0 @@ -8299,7 +8469,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) */ id->vwc = NVME_VWC_NSID_BROADCAST_SUPPORT | NVME_VWC_PRESENT; - id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1); + id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1 | + NVME_OCFS_COPY_FORMAT_2 | NVME_OCFS_COPY_FORMAT_3); id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN); nvme_init_subnqn(n); diff --git a/include/block/nvme.h b/include/block/nvme.h index 7c77d38174..5298bc4a28 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -799,6 +799,8 @@ typedef struct QEMU_PACKED NvmeDsmRange { enum { NVME_COPY_FORMAT_0 = 0x0, NVME_COPY_FORMAT_1 = 0x1, + NVME_COPY_FORMAT_2 = 0x2, + NVME_COPY_FORMAT_3 = 0x3, }; typedef struct QEMU_PACKED NvmeCopyCmd { @@ -820,25 +822,30 @@ typedef struct QEMU_PACKED NvmeCopyCmd { uint16_t appmask; } NvmeCopyCmd; -typedef struct QEMU_PACKED NvmeCopySourceRangeFormat0 { - uint8_t rsvd0[8]; +typedef struct QEMU_PACKED NvmeCopySourceRangeFormat0_2 { + uint32_t sparams; + uint8_t rsvd4[4]; uint64_t slba; uint16_t nlb; - uint8_t rsvd18[6]; + uint8_t rsvd18[4]; + uint16_t sopt; uint32_t reftag; uint16_t apptag; uint16_t appmask; -} NvmeCopySourceRangeFormat0; +} NvmeCopySourceRangeFormat0_2; -typedef struct QEMU_PACKED NvmeCopySourceRangeFormat1 { - uint8_t rsvd0[8]; +typedef struct QEMU_PACKED NvmeCopySourceRangeFormat1_3 { + uint32_t sparams; + uint8_t rsvd4[4]; uint64_t slba; uint16_t nlb; - uint8_t rsvd18[8]; + uint8_t rsvd18[4]; + uint16_t sopt; + uint8_t rsvd24[2]; uint8_t sr[10]; uint16_t apptag; uint16_t appmask; -} NvmeCopySourceRangeFormat1; +} NvmeCopySourceRangeFormat1_3; enum NvmeAsyncEventRequest { NVME_AER_TYPE_ERROR = 0, @@ -937,6 +944,8 @@ enum NvmeStatusCodes { NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, NVME_CMD_SIZE_LIMIT = 0x0183, + NVME_CMD_INCOMP_NS_OR_FMT = 0x0185, + NVME_CMD_OVERLAP_IO_RANGE = 0x0187, NVME_INVALID_ZONE_OP = 0x01b6, NVME_NOZRWA = 0x01b7, NVME_ZONE_BOUNDARY_ERROR = 0x01b8, @@ -1195,11 +1204,15 @@ enum NvmeIdCtrlOncs { NVME_ONCS_TIMESTAMP = 1 << 6, NVME_ONCS_VERIFY = 1 << 7, NVME_ONCS_COPY = 1 << 8, + NVME_ONCS_NVMCSA = 1 << 9, + NVME_ONCS_NVMAFC = 1 << 10, }; enum NvmeIdCtrlOcfs { NVME_OCFS_COPY_FORMAT_0 = 1 << NVME_COPY_FORMAT_0, NVME_OCFS_COPY_FORMAT_1 = 1 << NVME_COPY_FORMAT_1, + NVME_OCFS_COPY_FORMAT_2 = 1 << NVME_COPY_FORMAT_2, + NVME_OCFS_COPY_FORMAT_3 = 1 << NVME_COPY_FORMAT_3, }; enum NvmeIdctrlVwc { @@ -1333,7 +1346,9 @@ typedef struct NvmeHostBehaviorSupport { uint8_t acre; uint8_t etdas; uint8_t lbafee; - uint8_t rsvd3[509]; + uint8_t rsvd3; + uint16_t cdfe; + uint8_t rsvd6[506]; } NvmeHostBehaviorSupport; typedef struct QEMU_PACKED NvmeLBAF { @@ -1833,8 +1848,8 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeZonedResult) != 8); QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat0) != 32); - QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat1) != 40); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat0_2) != 32); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat1_3) != 40); QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64); From 75209c071aab4488aef63c38fb53d0e4e3aaa062 Mon Sep 17 00:00:00 2001 From: Ayush Mishra <ayush.m55@samsung.com> Date: Tue, 2 Jul 2024 13:32:32 +0530 Subject: [PATCH 2351/2884] hw/nvme: actually implement abort Abort was not implemented previously, but we can implement it for AERs and asynchrnously for I/O. Signed-off-by: Ayush Mishra <ayush.m55@samsung.com> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 385438a9ae..39782ddb2e 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1758,6 +1758,10 @@ static void nvme_aio_err(NvmeRequest *req, int ret) break; } + if (ret == -ECANCELED) { + status = NVME_CMD_ABORT_REQ; + } + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); error_setg_errno(&local_err, -ret, "aio failed"); @@ -5950,12 +5954,40 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *req) { uint16_t sqid = le32_to_cpu(req->cmd.cdw10) & 0xffff; + uint16_t cid = (le32_to_cpu(req->cmd.cdw10) >> 16) & 0xffff; + NvmeSQueue *sq = n->sq[sqid]; + NvmeRequest *r, *next; + int i; req->cqe.result = 1; if (nvme_check_sqid(n, sqid)) { return NVME_INVALID_FIELD | NVME_DNR; } + if (sqid == 0) { + for (i = 0; i < n->outstanding_aers; i++) { + NvmeRequest *re = n->aer_reqs[i]; + if (re->cqe.cid == cid) { + memmove(n->aer_reqs + i, n->aer_reqs + i + 1, + (n->outstanding_aers - i - 1) * sizeof(NvmeRequest *)); + n->outstanding_aers--; + re->status = NVME_CMD_ABORT_REQ; + req->cqe.result = 0; + nvme_enqueue_req_completion(&n->admin_cq, re); + return NVME_SUCCESS; + } + } + } + + QTAILQ_FOREACH_SAFE(r, &sq->out_req_list, entry, next) { + if (r->cqe.cid == cid) { + if (r->aiocb) { + blk_aio_cancel_async(r->aiocb); + } + break; + } + } + return NVME_SUCCESS; } From 4ea3de93a353b4462d96edd94be08d3be9484947 Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 05:17:28 -0400 Subject: [PATCH 2352/2884] hw/nvme: remove useless type cast The type of req->cmd is NvmeCmd, cast the pointer of this type to NvmeCmd* is useless. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 39782ddb2e..8d25174d25 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4340,7 +4340,7 @@ static bool nvme_zone_matches_filter(uint32_t zafs, NvmeZone *zl) static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) { - NvmeCmd *cmd = (NvmeCmd *)&req->cmd; + NvmeCmd *cmd = &req->cmd; NvmeNamespace *ns = req->ns; /* cdw12 is zero-based number of dwords to return. Convert to bytes */ uint32_t data_size = (le32_to_cpu(cmd->cdw12) + 1) << 2; From 0418f90809aea5b375c859e744c8e8610e9be446 Mon Sep 17 00:00:00 2001 From: Anthony Harivel <aharivel@redhat.com> Date: Wed, 22 May 2024 17:34:52 +0200 Subject: [PATCH 2353/2884] Add support for RAPL MSRs in KVM/Qemu Starting with the "Sandy Bridge" generation, Intel CPUs provide a RAPL interface (Running Average Power Limit) for advertising the accumulated energy consumption of various power domains (e.g. CPU packages, DRAM, etc.). The consumption is reported via MSRs (model specific registers) like MSR_PKG_ENERGY_STATUS for the CPU package power domain. These MSRs are 64 bits registers that represent the accumulated energy consumption in micro Joules. They are updated by microcode every ~1ms. For now, KVM always returns 0 when the guest requests the value of these MSRs. Use the KVM MSR filtering mechanism to allow QEMU handle these MSRs dynamically in userspace. To limit the amount of system calls for every MSR call, create a new thread in QEMU that updates the "virtual" MSR values asynchronously. Each vCPU has its own vMSR to reflect the independence of vCPUs. The thread updates the vMSR values with the ratio of energy consumed of the whole physical CPU package the vCPU thread runs on and the thread's utime and stime values. All other non-vCPU threads are also taken into account. Their energy consumption is evenly distributed among all vCPUs threads running on the same physical CPU package. To overcome the problem that reading the RAPL MSR requires priviliged access, a socket communication between QEMU and the qemu-vmsr-helper is mandatory. You can specified the socket path in the parameter. This feature is activated with -accel kvm,rapl=true,path=/path/sock.sock Actual limitation: - Works only on Intel host CPU because AMD CPUs are using different MSR adresses. - Only the Package Power-Plane (MSR_PKG_ENERGY_STATUS) is reported at the moment. Signed-off-by: Anthony Harivel <aharivel@redhat.com> Link: https://lore.kernel.org/r/20240522153453.1230389-4-aharivel@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- accel/kvm/kvm-all.c | 27 +++ docs/specs/index.rst | 1 + docs/specs/rapl-msr.rst | 155 ++++++++++++ include/sysemu/kvm_int.h | 32 +++ target/i386/cpu.h | 8 + target/i386/kvm/kvm.c | 431 +++++++++++++++++++++++++++++++++- target/i386/kvm/meson.build | 1 + target/i386/kvm/vmsr_energy.c | 345 +++++++++++++++++++++++++++ target/i386/kvm/vmsr_energy.h | 99 ++++++++ 9 files changed, 1098 insertions(+), 1 deletion(-) create mode 100644 docs/specs/rapl-msr.rst create mode 100644 target/i386/kvm/vmsr_energy.c create mode 100644 target/i386/kvm/vmsr_energy.h diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 64bf47a033..480c7119d1 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3745,6 +3745,21 @@ static void kvm_set_device(Object *obj, s->device = g_strdup(value); } +static void kvm_set_kvm_rapl(Object *obj, bool value, Error **errp) +{ + KVMState *s = KVM_STATE(obj); + s->msr_energy.enable = value; +} + +static void kvm_set_kvm_rapl_socket_path(Object *obj, + const char *str, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + g_free(s->msr_energy.socket_path); + s->msr_energy.socket_path = g_strdup(str); +} + static void kvm_accel_instance_init(Object *obj) { KVMState *s = KVM_STATE(obj); @@ -3764,6 +3779,7 @@ static void kvm_accel_instance_init(Object *obj) s->xen_gnttab_max_frames = 64; s->xen_evtchn_max_pirq = 256; s->device = NULL; + s->msr_energy.enable = false; } /** @@ -3808,6 +3824,17 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "device", "Path to the device node to use (default: /dev/kvm)"); + object_class_property_add_bool(oc, "rapl", + NULL, + kvm_set_kvm_rapl); + object_class_property_set_description(oc, "rapl", + "Allow energy related MSRs for RAPL interface in Guest"); + + object_class_property_add_str(oc, "rapl-helper-socket", NULL, + kvm_set_kvm_rapl_socket_path); + object_class_property_set_description(oc, "rapl-helper-socket", + "Socket Path for comminucating with the Virtual MSR helper daemon"); + kvm_arch_accel_class_init(oc); } diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 1484e3e760..e738ea7d10 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -33,3 +33,4 @@ guest hardware that is specific to QEMU. virt-ctlr vmcoreinfo vmgenid + rapl-msr diff --git a/docs/specs/rapl-msr.rst b/docs/specs/rapl-msr.rst new file mode 100644 index 0000000000..1202ee89be --- /dev/null +++ b/docs/specs/rapl-msr.rst @@ -0,0 +1,155 @@ +================ +RAPL MSR support +================ + +The RAPL interface (Running Average Power Limit) is advertising the accumulated +energy consumption of various power domains (e.g. CPU packages, DRAM, etc.). + +The consumption is reported via MSRs (model specific registers) like +MSR_PKG_ENERGY_STATUS for the CPU package power domain. These MSRs are 64 bits +registers that represent the accumulated energy consumption in micro Joules. + +Thanks to the MSR Filtering patch [#a]_ not all MSRs are handled by KVM. Some +of them can now be handled by the userspace (QEMU). It uses a mechanism called +"MSR filtering" where a list of MSRs is given at init time of a VM to KVM so +that a callback is put in place. The design of this patch uses only this +mechanism for handling the MSRs between guest/host. + +At the moment the following MSRs are involved: + +.. code:: C + + #define MSR_RAPL_POWER_UNIT 0x00000606 + #define MSR_PKG_POWER_LIMIT 0x00000610 + #define MSR_PKG_ENERGY_STATUS 0x00000611 + #define MSR_PKG_POWER_INFO 0x00000614 + +The ``*_POWER_UNIT``, ``*_POWER_LIMIT``, ``*_POWER INFO`` are part of the RAPL +spec and specify the power limit of the package, provide range of parameter(min +power, max power,..) and also the information of the multiplier for the energy +counter to calculate the power. Those MSRs are populated once at the beginning +by reading the host CPU MSRs and are given back to the guest 1:1 when +requested. + +The MSR_PKG_ENERGY_STATUS is a counter; it represents the total amount of +energy consumed since the last time the register was cleared. If you multiply +it with the UNIT provided above you'll get the power in micro-joules. This +counter is always increasing and it increases more or less faster depending on +the consumption of the package. This counter is supposed to overflow at some +point. + +Each core belonging to the same Package reading the MSR_PKG_ENERGY_STATUS (i.e +"rdmsr 0x611") will retrieve the same value. The value represents the energy +for the whole package. Whatever Core reading it will get the same value and a +core that belongs to PKG-0 will not be able to get the value of PKG-1 and +vice-versa. + +High level implementation +------------------------- + +In order to update the value of the virtual MSR, a QEMU thread is created. +The thread is basically just an infinity loop that does: + +1. Snapshot of the time metrics of all QEMU threads (Time spent scheduled in + Userspace and System) + +2. Snapshot of the actual MSR_PKG_ENERGY_STATUS counter of all packages where + the QEMU threads are running on. + +3. Sleep for 1 second - During this pause the vcpu and other non-vcpu threads + will do what they have to do and so the energy counter will increase. + +4. Repeat 2. and 3. and calculate the delta of every metrics representing the + time spent scheduled for each QEMU thread *and* the energy spent by the + packages during the pause. + +5. Filter the vcpu threads and the non-vcpu threads. + +6. Retrieve the topology of the Virtual Machine. This helps identify which + vCPU is running on which virtual package. + +7. The total energy spent by the non-vcpu threads is divided by the number + of vcpu threads so that each vcpu thread will get an equal part of the + energy spent by the QEMU workers. + +8. Calculate the ratio of energy spent per vcpu threads. + +9. Calculate the energy for each virtual package. + +10. The virtual MSRs are updated for each virtual package. Each vCPU that + belongs to the same package will return the same value when accessing the + the MSR. + +11. Loop back to 1. + +Ratio calculation +----------------- + +In Linux, a process has an execution time associated with it. The scheduler is +dividing the time in clock ticks. The number of clock ticks per second can be +found by the sysconf system call. A typical value of clock ticks per second is +100. So a core can run a process at the maximum of 100 ticks per second. If a +package has 4 cores, 400 ticks maximum can be scheduled on all the cores +of the package for a period of 1 second. + +The /proc/[pid]/stat [#b]_ is a sysfs file that can give the executed time of a +process with the [pid] as the process ID. It gives the amount of ticks the +process has been scheduled in userspace (utime) and kernel space (stime). + +By reading those metrics for a thread, one can calculate the ratio of time the +package has spent executing the thread. + +Example: + +A 4 cores package can schedule a maximum of 400 ticks per second with 100 ticks +per second per core. If a thread was scheduled for 100 ticks between a second +on this package, that means my thread has been scheduled for 1/4 of the whole +package. With that, the calculation of the energy spent by the thread on this +package during this whole second is 1/4 of the total energy spent by the +package. + +Usage +----- + +Currently this feature is only working on an Intel CPU that has the RAPL driver +mounted and available in the sysfs. if not, QEMU fails at start-up. + +This feature is activated with -accel +kvm,rapl=true,rapl-helper-socket=/path/sock.sock + +It is important that the socket path is the same as the one +:program:`qemu-vmsr-helper` is listening to. + +qemu-vmsr-helper +---------------- + +The qemu-vmsr-helper is working very much like the qemu-pr-helper. Instead of +making persistent reservation, qemu-vmsr-helper is here to overcome the +CVE-2020-8694 which remove user access to the rapl msr attributes. + +A socket communication is established between QEMU processes that has the RAPL +MSR support activated and the qemu-vmsr-helper. A systemd service and socket +activation is provided in contrib/systemd/qemu-vmsr-helper.(service/socket). + +The systemd socket uses 600, like contrib/systemd/qemu-pr-helper.socket. The +socket can be passed via SCM_RIGHTS by libvirt, or its permissions can be +changed (e.g. 660 and root:kvm for a Debian system for example). Libvirt could +also start a separate helper if needed. All in all, the policy is left to the +user. + +See the qemu-pr-helper documentation or manpage for further details. + +Current Limitations +------------------- + +- Works only on Intel host CPUs because AMD CPUs are using different MSR + addresses. + +- Only the Package Power-Plane (MSR_PKG_ENERGY_STATUS) is reported at the + moment. + +References +---------- + +.. [#a] https://patchwork.kernel.org/project/kvm/patch/20200916202951.23760-7-graf@amazon.com/ +.. [#b] https://man7.org/linux/man-pages/man5/proc.5.html diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 3f3d13f816..1d8fb1473b 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -14,6 +14,9 @@ #include "qemu/accel.h" #include "qemu/queue.h" #include "sysemu/kvm.h" +#include "hw/boards.h" +#include "hw/i386/topology.h" +#include "io/channel-socket.h" typedef struct KVMSlot { @@ -50,6 +53,34 @@ typedef struct KVMMemoryListener { #define KVM_MSI_HASHTAB_SIZE 256 +typedef struct KVMHostTopoInfo { + /* Number of package on the Host */ + unsigned int maxpkgs; + /* Number of cpus on the Host */ + unsigned int maxcpus; + /* Number of cpus on each different package */ + unsigned int *pkg_cpu_count; + /* Each package can have different maxticks */ + unsigned int *maxticks; +} KVMHostTopoInfo; + +struct KVMMsrEnergy { + pid_t pid; + bool enable; + char *socket_path; + QIOChannelSocket *sioc; + QemuThread msr_thr; + unsigned int guest_vcpus; + unsigned int guest_vsockets; + X86CPUTopoInfo guest_topo_info; + KVMHostTopoInfo host_topo; + const CPUArchIdList *guest_cpu_list; + uint64_t *msr_value; + uint64_t msr_unit; + uint64_t msr_limit; + uint64_t msr_info; +}; + enum KVMDirtyRingReaperState { KVM_DIRTY_RING_REAPER_NONE = 0, /* The reaper is sleeping */ @@ -117,6 +148,7 @@ struct KVMState bool kvm_dirty_ring_with_bitmap; uint64_t kvm_eager_split_size; /* Eager Page Splitting chunk size */ struct KVMDirtyRingReaper reaper; + struct KVMMsrEnergy msr_energy; NotifyVmexitOption notify_vmexit; uint32_t notify_window; uint32_t xen_version; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1e121acef5..c6cc035df3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -414,6 +414,10 @@ typedef enum X86Seg { #define MSR_IA32_TSX_CTRL 0x122 #define MSR_IA32_TSCDEADLINE 0x6e0 #define MSR_IA32_PKRS 0x6e1 +#define MSR_RAPL_POWER_UNIT 0x00000606 +#define MSR_PKG_POWER_LIMIT 0x00000610 +#define MSR_PKG_ENERGY_STATUS 0x00000611 +#define MSR_PKG_POWER_INFO 0x00000614 #define MSR_ARCH_LBR_CTL 0x000014ce #define MSR_ARCH_LBR_DEPTH 0x000014cf #define MSR_ARCH_LBR_FROM_0 0x00001500 @@ -1880,6 +1884,10 @@ typedef struct CPUArchState { uintptr_t retaddr; + /* RAPL MSR */ + uint64_t msr_rapl_power_unit; + uint64_t msr_pkg_energy_status; + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index becca2efa5..b4aab9a410 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -16,9 +16,12 @@ #include "qapi/qapi-events-run-state.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include <math.h> #include <sys/ioctl.h> #include <sys/utsname.h> #include <sys/syscall.h> +#include <sys/resource.h> +#include <sys/time.h> #include <linux/kvm.h> #include <linux/kvm_para.h> @@ -27,6 +30,7 @@ #include "cpu.h" #include "host-cpu.h" +#include "vmsr_energy.h" #include "sysemu/sysemu.h" #include "sysemu/hw_accel.h" #include "sysemu/kvm_int.h" @@ -2559,7 +2563,8 @@ static int kvm_get_supported_msrs(KVMState *s) return ret; } -static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, +static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, + uint32_t msr, uint64_t *val) { CPUState *cs = CPU(cpu); @@ -2570,6 +2575,53 @@ static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, return true; } +static bool kvm_rdmsr_rapl_power_unit(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + + *val = cs->kvm_state->msr_energy.msr_unit; + + return true; +} + +static bool kvm_rdmsr_pkg_power_limit(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + + *val = cs->kvm_state->msr_energy.msr_limit; + + return true; +} + +static bool kvm_rdmsr_pkg_power_info(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + + *val = cs->kvm_state->msr_energy.msr_info; + + return true; +} + +static bool kvm_rdmsr_pkg_energy_status(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + *val = cs->kvm_state->msr_energy.msr_value[cs->cpu_index]; + + return true; +} + static Notifier smram_machine_done; static KVMMemoryListener smram_listener; static AddressSpace smram_address_space; @@ -2604,6 +2656,340 @@ static void register_smram_listener(Notifier *n, void *unused) &smram_address_space, 1, "kvm-smram"); } +static void *kvm_msr_energy_thread(void *data) +{ + KVMState *s = data; + struct KVMMsrEnergy *vmsr = &s->msr_energy; + + g_autofree vmsr_package_energy_stat *pkg_stat = NULL; + g_autofree vmsr_thread_stat *thd_stat = NULL; + g_autofree CPUState *cpu = NULL; + g_autofree unsigned int *vpkgs_energy_stat = NULL; + unsigned int num_threads = 0; + + X86CPUTopoIDs topo_ids; + + rcu_register_thread(); + + /* Allocate memory for each package energy status */ + pkg_stat = g_new0(vmsr_package_energy_stat, vmsr->host_topo.maxpkgs); + + /* Allocate memory for thread stats */ + thd_stat = g_new0(vmsr_thread_stat, 1); + + /* Allocate memory for holding virtual package energy counter */ + vpkgs_energy_stat = g_new0(unsigned int, vmsr->guest_vsockets); + + /* Populate the max tick of each packages */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + /* + * Max numbers of ticks per package + * Time in second * Number of ticks/second * Number of cores/package + * ex: 100 ticks/second/CPU, 12 CPUs per Package gives 1200 ticks max + */ + vmsr->host_topo.maxticks[i] = (MSR_ENERGY_THREAD_SLEEP_US / 1000000) + * sysconf(_SC_CLK_TCK) + * vmsr->host_topo.pkg_cpu_count[i]; + } + + while (true) { + /* Get all qemu threads id */ + g_autofree pid_t *thread_ids = + thread_ids = vmsr_get_thread_ids(vmsr->pid, &num_threads); + + if (thread_ids == NULL) { + goto clean; + } + + thd_stat = g_renew(vmsr_thread_stat, thd_stat, num_threads); + /* Unlike g_new0, g_renew0 function doesn't exist yet... */ + memset(thd_stat, 0, num_threads * sizeof(vmsr_thread_stat)); + + /* Populate all the thread stats */ + for (int i = 0; i < num_threads; i++) { + thd_stat[i].utime = g_new0(unsigned long long, 2); + thd_stat[i].stime = g_new0(unsigned long long, 2); + thd_stat[i].thread_id = thread_ids[i]; + vmsr_read_thread_stat(vmsr->pid, + thd_stat[i].thread_id, + thd_stat[i].utime, + thd_stat[i].stime, + &thd_stat[i].cpu_id); + thd_stat[i].pkg_id = + vmsr_get_physical_package_id(thd_stat[i].cpu_id); + } + + /* Retrieve all packages power plane energy counter */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + for (int j = 0; j < num_threads; j++) { + /* + * Use the first thread we found that ran on the CPU + * of the package to read the packages energy counter + */ + if (thd_stat[j].pkg_id == i) { + pkg_stat[i].e_start = + vmsr_read_msr(MSR_PKG_ENERGY_STATUS, + thd_stat[j].cpu_id, + thd_stat[j].thread_id, + s->msr_energy.sioc); + break; + } + } + } + + /* Sleep a short period while the other threads are working */ + usleep(MSR_ENERGY_THREAD_SLEEP_US); + + /* + * Retrieve all packages power plane energy counter + * Calculate the delta of all packages + */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + for (int j = 0; j < num_threads; j++) { + /* + * Use the first thread we found that ran on the CPU + * of the package to read the packages energy counter + */ + if (thd_stat[j].pkg_id == i) { + pkg_stat[i].e_end = + vmsr_read_msr(MSR_PKG_ENERGY_STATUS, + thd_stat[j].cpu_id, + thd_stat[j].thread_id, + s->msr_energy.sioc); + /* + * Prevent the case we have migrate the VM + * during the sleep period or any other cases + * were energy counter might be lower after + * the sleep period. + */ + if (pkg_stat[i].e_end > pkg_stat[i].e_start) { + pkg_stat[i].e_delta = + pkg_stat[i].e_end - pkg_stat[i].e_start; + } else { + pkg_stat[i].e_delta = 0; + } + break; + } + } + } + + /* Delta of ticks spend by each thread between the sample */ + for (int i = 0; i < num_threads; i++) { + vmsr_read_thread_stat(vmsr->pid, + thd_stat[i].thread_id, + thd_stat[i].utime, + thd_stat[i].stime, + &thd_stat[i].cpu_id); + + if (vmsr->pid < 0) { + /* + * We don't count the dead thread + * i.e threads that existed before the sleep + * and not anymore + */ + thd_stat[i].delta_ticks = 0; + } else { + vmsr_delta_ticks(thd_stat, i); + } + } + + /* + * Identify the vcpu threads + * Calculate the number of vcpu per package + */ + CPU_FOREACH(cpu) { + for (int i = 0; i < num_threads; i++) { + if (cpu->thread_id == thd_stat[i].thread_id) { + thd_stat[i].is_vcpu = true; + thd_stat[i].vcpu_id = cpu->cpu_index; + pkg_stat[thd_stat[i].pkg_id].nb_vcpu++; + thd_stat[i].acpi_id = kvm_arch_vcpu_id(cpu); + break; + } + } + } + + /* Retrieve the virtual package number of each vCPU */ + for (int i = 0; i < vmsr->guest_cpu_list->len; i++) { + for (int j = 0; j < num_threads; j++) { + if ((thd_stat[j].acpi_id == + vmsr->guest_cpu_list->cpus[i].arch_id) + && (thd_stat[j].is_vcpu == true)) { + x86_topo_ids_from_apicid(thd_stat[j].acpi_id, + &vmsr->guest_topo_info, &topo_ids); + thd_stat[j].vpkg_id = topo_ids.pkg_id; + } + } + } + + /* Calculate the total energy of all non-vCPU thread */ + for (int i = 0; i < num_threads; i++) { + if ((thd_stat[i].is_vcpu != true) && + (thd_stat[i].delta_ticks > 0)) { + double temp; + temp = vmsr_get_ratio(pkg_stat[thd_stat[i].pkg_id].e_delta, + thd_stat[i].delta_ticks, + vmsr->host_topo.maxticks[thd_stat[i].pkg_id]); + pkg_stat[thd_stat[i].pkg_id].e_ratio + += (uint64_t)lround(temp); + } + } + + /* Calculate the ratio per non-vCPU thread of each package */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + if (pkg_stat[i].nb_vcpu > 0) { + pkg_stat[i].e_ratio = pkg_stat[i].e_ratio / pkg_stat[i].nb_vcpu; + } + } + + /* + * Calculate the energy for each Package: + * Energy Package = sum of each vCPU energy that belongs to the package + */ + for (int i = 0; i < num_threads; i++) { + if ((thd_stat[i].is_vcpu == true) && \ + (thd_stat[i].delta_ticks > 0)) { + double temp; + temp = vmsr_get_ratio(pkg_stat[thd_stat[i].pkg_id].e_delta, + thd_stat[i].delta_ticks, + vmsr->host_topo.maxticks[thd_stat[i].pkg_id]); + vpkgs_energy_stat[thd_stat[i].vpkg_id] += + (uint64_t)lround(temp); + vpkgs_energy_stat[thd_stat[i].vpkg_id] += + pkg_stat[thd_stat[i].pkg_id].e_ratio; + } + } + + /* + * Finally populate the vmsr register of each vCPU with the total + * package value to emulate the real hardware where each CPU return the + * value of the package it belongs. + */ + for (int i = 0; i < num_threads; i++) { + if ((thd_stat[i].is_vcpu == true) && \ + (thd_stat[i].delta_ticks > 0)) { + vmsr->msr_value[thd_stat[i].vcpu_id] = \ + vpkgs_energy_stat[thd_stat[i].vpkg_id]; + } + } + + /* Freeing memory before zeroing the pointer */ + for (int i = 0; i < num_threads; i++) { + g_free(thd_stat[i].utime); + g_free(thd_stat[i].stime); + } + } + +clean: + rcu_unregister_thread(); + return NULL; +} + +static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + struct KVMMsrEnergy *r = &s->msr_energy; + int ret = 0; + + /* + * Sanity check + * 1. Host cpu must be Intel cpu + * 2. RAPL must be enabled on the Host + */ + if (is_host_cpu_intel()) { + error_report("The RAPL feature can only be enabled on hosts\ + with Intel CPU models"); + ret = 1; + goto out; + } + + if (!is_rapl_enabled()) { + ret = 1; + goto out; + } + + /* Retrieve the virtual topology */ + vmsr_init_topo_info(&r->guest_topo_info, ms); + + /* Retrieve the number of vcpu */ + r->guest_vcpus = ms->smp.cpus; + + /* Retrieve the number of virtual sockets */ + r->guest_vsockets = ms->smp.sockets; + + /* Allocate register memory (MSR_PKG_STATUS) for each vcpu */ + r->msr_value = g_new0(uint64_t, r->guest_vcpus); + + /* Retrieve the CPUArchIDlist */ + r->guest_cpu_list = mc->possible_cpu_arch_ids(ms); + + /* Max number of cpus on the Host */ + r->host_topo.maxcpus = vmsr_get_maxcpus(); + if (r->host_topo.maxcpus == 0) { + error_report("host max cpus = 0"); + ret = 1; + goto out; + } + + /* Max number of packages on the host */ + r->host_topo.maxpkgs = vmsr_get_max_physical_package(r->host_topo.maxcpus); + if (r->host_topo.maxpkgs == 0) { + error_report("host max pkgs = 0"); + ret = 1; + goto out; + } + + /* Allocate memory for each package on the host */ + r->host_topo.pkg_cpu_count = g_new0(unsigned int, r->host_topo.maxpkgs); + r->host_topo.maxticks = g_new0(unsigned int, r->host_topo.maxpkgs); + + vmsr_count_cpus_per_package(r->host_topo.pkg_cpu_count, + r->host_topo.maxpkgs); + for (int i = 0; i < r->host_topo.maxpkgs; i++) { + if (r->host_topo.pkg_cpu_count[i] == 0) { + error_report("cpu per packages = 0 on package_%d", i); + ret = 1; + goto out; + } + } + + /* Get QEMU PID*/ + r->pid = getpid(); + + /* Compute the socket path if necessary */ + if (s->msr_energy.socket_path == NULL) { + s->msr_energy.socket_path = vmsr_compute_default_paths(); + } + + /* Open socket with vmsr helper */ + s->msr_energy.sioc = vmsr_open_socket(s->msr_energy.socket_path); + + if (s->msr_energy.sioc == NULL) { + error_report("vmsr socket opening failed"); + ret = 1; + goto out; + } + + /* Those MSR values should not change */ + r->msr_unit = vmsr_read_msr(MSR_RAPL_POWER_UNIT, 0, r->pid, + s->msr_energy.sioc); + r->msr_limit = vmsr_read_msr(MSR_PKG_POWER_LIMIT, 0, r->pid, + s->msr_energy.sioc); + r->msr_info = vmsr_read_msr(MSR_PKG_POWER_INFO, 0, r->pid, + s->msr_energy.sioc); + if (r->msr_unit == 0 || r->msr_limit == 0 || r->msr_info == 0) { + error_report("can't read any virtual msr"); + ret = 1; + goto out; + } + + qemu_thread_create(&r->msr_thr, "kvm-msr", + kvm_msr_energy_thread, + s, QEMU_THREAD_JOINABLE); +out: + return ret; +} + int kvm_arch_get_default_type(MachineState *ms) { return 0; @@ -2804,6 +3190,49 @@ int kvm_arch_init(MachineState *ms, KVMState *s) strerror(-ret)); exit(1); } + + if (s->msr_energy.enable == true) { + r = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, + kvm_rdmsr_rapl_power_unit, NULL); + if (!r) { + error_report("Could not install MSR_RAPL_POWER_UNIT \ + handler: %s", + strerror(-ret)); + exit(1); + } + + r = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, + kvm_rdmsr_pkg_power_limit, NULL); + if (!r) { + error_report("Could not install MSR_PKG_POWER_LIMIT \ + handler: %s", + strerror(-ret)); + exit(1); + } + + r = kvm_filter_msr(s, MSR_PKG_POWER_INFO, + kvm_rdmsr_pkg_power_info, NULL); + if (!r) { + error_report("Could not install MSR_PKG_POWER_INFO \ + handler: %s", + strerror(-ret)); + exit(1); + } + r = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, + kvm_rdmsr_pkg_energy_status, NULL); + if (!r) { + error_report("Could not install MSR_PKG_ENERGY_STATUS \ + handler: %s", + strerror(-ret)); + exit(1); + } + r = kvm_msr_energy_thread_init(s, ms); + if (r) { + error_report("kvm : error RAPL feature requirement not meet"); + exit(1); + } + + } } return 0; diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index e7850981e6..3996cafaf2 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -3,6 +3,7 @@ i386_kvm_ss = ss.source_set() i386_kvm_ss.add(files( 'kvm.c', 'kvm-cpu.c', + 'vmsr_energy.c', )) i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c new file mode 100644 index 0000000000..a1d78f2f2a --- /dev/null +++ b/target/i386/kvm/vmsr_energy.c @@ -0,0 +1,345 @@ +/* + * QEMU KVM support -- x86 virtual RAPL msr + * + * Copyright 2024 Red Hat, Inc. 2024 + * + * Author: + * Anthony Harivel <aharivel@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "vmsr_energy.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "hw/boards.h" +#include "cpu.h" +#include "host-cpu.h" + +char *vmsr_compute_default_paths(void) +{ + g_autofree char *state = qemu_get_local_state_dir(); + + return g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL); +} + +bool is_host_cpu_intel(void) +{ + int family, model, stepping; + char vendor[CPUID_VENDOR_SZ + 1]; + + host_cpu_vendor_fms(vendor, &family, &model, &stepping); + + return strcmp(vendor, CPUID_VENDOR_INTEL); +} + +int is_rapl_enabled(void) +{ + const char *path = "/sys/class/powercap/intel-rapl/enabled"; + FILE *file = fopen(path, "r"); + int value = 0; + + if (file != NULL) { + if (fscanf(file, "%d", &value) != 1) { + error_report("INTEL RAPL not enabled"); + } + fclose(file); + } else { + error_report("Error opening %s", path); + } + + return value; +} + +QIOChannelSocket *vmsr_open_socket(const char *path) +{ + g_autofree char *socket_path = NULL; + + socket_path = g_strdup(path); + + SocketAddress saddr = { + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = socket_path + }; + + QIOChannelSocket *sioc = qio_channel_socket_new(); + Error *local_err = NULL; + + qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper"); + qio_channel_socket_connect_sync(sioc, + &saddr, + &local_err); + if (local_err) { + /* Close socket. */ + qio_channel_close(QIO_CHANNEL(sioc), NULL); + object_unref(OBJECT(sioc)); + sioc = NULL; + goto out; + } + + qio_channel_set_delay(QIO_CHANNEL(sioc), false); +out: + return sioc; +} + +uint64_t vmsr_read_msr(uint32_t reg, uint32_t cpu_id, uint32_t tid, + QIOChannelSocket *sioc) +{ + uint64_t data = 0; + int r = 0; + Error *local_err = NULL; + uint32_t buffer[3]; + /* + * Send the required arguments: + * 1. RAPL MSR register to read + * 2. On which CPU ID + * 3. From which vCPU (Thread ID) + */ + buffer[0] = reg; + buffer[1] = cpu_id; + buffer[2] = tid; + + r = qio_channel_write_all(QIO_CHANNEL(sioc), + (char *)buffer, sizeof(buffer), + &local_err); + if (r < 0) { + goto out_close; + } + + r = qio_channel_read(QIO_CHANNEL(sioc), + (char *)&data, sizeof(data), + &local_err); + if (r < 0) { + data = 0; + goto out_close; + } + +out_close: + return data; +} + +/* Retrieve the max number of physical package */ +unsigned int vmsr_get_max_physical_package(unsigned int max_cpus) +{ + const char *dir = "/sys/devices/system/cpu/"; + const char *topo_path = "topology/physical_package_id"; + g_autofree int *uniquePackages = g_new0(int, max_cpus); + unsigned int packageCount = 0; + FILE *file = NULL; + + for (int i = 0; i < max_cpus; i++) { + g_autofree char *filePath = NULL; + g_autofree char *cpuid = g_strdup_printf("cpu%d", i); + + filePath = g_build_filename(dir, cpuid, topo_path, NULL); + + file = fopen(filePath, "r"); + + if (file == NULL) { + error_report("Error opening physical_package_id file"); + return 0; + } + + char packageId[10]; + if (fgets(packageId, sizeof(packageId), file) == NULL) { + packageCount = 0; + } + + fclose(file); + + int currentPackageId = atoi(packageId); + + bool isUnique = true; + for (int j = 0; j < packageCount; j++) { + if (uniquePackages[j] == currentPackageId) { + isUnique = false; + break; + } + } + + if (isUnique) { + uniquePackages[packageCount] = currentPackageId; + packageCount++; + + if (packageCount >= max_cpus) { + break; + } + } + } + + return (packageCount == 0) ? 1 : packageCount; +} + +/* Retrieve the max number of physical cpu on the host */ +unsigned int vmsr_get_maxcpus(void) +{ + GDir *dir; + const gchar *entry_name; + unsigned int cpu_count = 0; + const char *path = "/sys/devices/system/cpu/"; + + dir = g_dir_open(path, 0, NULL); + if (dir == NULL) { + error_report("Unable to open cpu directory"); + return -1; + } + + while ((entry_name = g_dir_read_name(dir)) != NULL) { + if (g_ascii_strncasecmp(entry_name, "cpu", 3) == 0 && + isdigit(entry_name[3])) { + cpu_count++; + } + } + + g_dir_close(dir); + + return cpu_count; +} + +/* Count the number of physical cpu on each packages */ +unsigned int vmsr_count_cpus_per_package(unsigned int *package_count, + unsigned int max_pkgs) +{ + g_autofree char *file_contents = NULL; + g_autofree char *path = NULL; + g_autofree char *path_name = NULL; + gsize length; + + /* Iterate over cpus and count cpus in each package */ + for (int cpu_id = 0; ; cpu_id++) { + path_name = g_strdup_printf("/sys/devices/system/cpu/cpu%d/" + "topology/physical_package_id", cpu_id); + + path = g_build_filename(path_name, NULL); + + if (!g_file_get_contents(path, &file_contents, &length, NULL)) { + break; /* No more cpus */ + } + + /* Get the physical package ID for this CPU */ + int package_id = atoi(file_contents); + + /* Check if the package ID is within the known number of packages */ + if (package_id >= 0 && package_id < max_pkgs) { + /* If yes, count the cpu for this package*/ + package_count[package_id]++; + } + } + + return 0; +} + +/* Get the physical package id from a given cpu id */ +int vmsr_get_physical_package_id(int cpu_id) +{ + g_autofree char *file_contents = NULL; + g_autofree char *file_path = NULL; + int package_id = -1; + gsize length; + + file_path = g_strdup_printf("/sys/devices/system/cpu/cpu%d" + "/topology/physical_package_id", cpu_id); + + if (!g_file_get_contents(file_path, &file_contents, &length, NULL)) { + goto out; + } + + package_id = atoi(file_contents); + +out: + return package_id; +} + +/* Read the scheduled time for a given thread of a give pid */ +void vmsr_read_thread_stat(pid_t pid, + unsigned int thread_id, + unsigned long long *utime, + unsigned long long *stime, + unsigned int *cpu_id) +{ + g_autofree char *path = NULL; + g_autofree char *path_name = NULL; + + path_name = g_strdup_printf("/proc/%u/task/%d/stat", pid, thread_id); + + path = g_build_filename(path_name, NULL); + + FILE *file = fopen(path, "r"); + if (file == NULL) { + pid = -1; + return; + } + + if (fscanf(file, "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u" + " %llu %llu %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %*u" + " %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*u %*u %u", + utime, stime, cpu_id) != 3) + { + pid = -1; + return; + } + + fclose(file); + return; +} + +/* Read QEMU stat task folder to retrieve all QEMU threads ID */ +pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads) +{ + g_autofree char *task_path = g_strdup_printf("%d/task", pid); + g_autofree char *path = g_build_filename("/proc", task_path, NULL); + + DIR *dir = opendir(path); + if (dir == NULL) { + error_report("Error opening /proc/qemu/task"); + return NULL; + } + + pid_t *thread_ids = NULL; + unsigned int thread_count = 0; + + g_autofree struct dirent *ent = NULL; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] == '.') { + continue; + } + pid_t tid = atoi(ent->d_name); + if (pid != tid) { + thread_ids = g_renew(pid_t, thread_ids, (thread_count + 1)); + thread_ids[thread_count] = tid; + thread_count++; + } + } + + closedir(dir); + + *num_threads = thread_count; + return thread_ids; +} + +void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i) +{ + thd_stat[i].delta_ticks = (thd_stat[i].utime[1] + thd_stat[i].stime[1]) + - (thd_stat[i].utime[0] + thd_stat[i].stime[0]); +} + +double vmsr_get_ratio(uint64_t e_delta, + unsigned long long delta_ticks, + unsigned int maxticks) +{ + return (e_delta / 100.0) * ((100.0 / maxticks) * delta_ticks); +} + +void vmsr_init_topo_info(X86CPUTopoInfo *topo_info, + const MachineState *ms) +{ + topo_info->dies_per_pkg = ms->smp.dies; + topo_info->modules_per_die = ms->smp.modules; + topo_info->cores_per_module = ms->smp.cores; + topo_info->threads_per_core = ms->smp.threads; +} + diff --git a/target/i386/kvm/vmsr_energy.h b/target/i386/kvm/vmsr_energy.h new file mode 100644 index 0000000000..16cc1f4814 --- /dev/null +++ b/target/i386/kvm/vmsr_energy.h @@ -0,0 +1,99 @@ +/* + * QEMU KVM support -- x86 virtual energy-related MSR. + * + * Copyright 2024 Red Hat, Inc. 2024 + * + * Author: + * Anthony Harivel <aharivel@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef VMSR_ENERGY_H +#define VMSR_ENERGY_H + +#include <stdint.h> +#include "qemu/osdep.h" +#include "io/channel-socket.h" +#include "hw/i386/topology.h" + +/* + * Define the interval time in micro seconds between 2 samples of + * energy related MSRs + */ +#define MSR_ENERGY_THREAD_SLEEP_US 1000000.0 + +/* + * Thread statistic + * @ thread_id: TID (thread ID) + * @ is_vcpu: true if TID is vCPU thread + * @ cpu_id: CPU number last executed on + * @ pkg_id: package number of the CPU + * @ vcpu_id: vCPU ID + * @ vpkg: virtual package number + * @ acpi_id: APIC id of the vCPU + * @ utime: amount of clock ticks the thread + * has been scheduled in User mode + * @ stime: amount of clock ticks the thread + * has been scheduled in System mode + * @ delta_ticks: delta of utime+stime between + * the two samples (before/after sleep) + */ +struct vmsr_thread_stat { + unsigned int thread_id; + bool is_vcpu; + unsigned int cpu_id; + unsigned int pkg_id; + unsigned int vpkg_id; + unsigned int vcpu_id; + unsigned long acpi_id; + unsigned long long *utime; + unsigned long long *stime; + unsigned long long delta_ticks; +}; + +/* + * Package statistic + * @ e_start: package energy counter before the sleep + * @ e_end: package energy counter after the sleep + * @ e_delta: delta of package energy counter + * @ e_ratio: store the energy ratio of non-vCPU thread + * @ nb_vcpu: number of vCPU running on this package + */ +struct vmsr_package_energy_stat { + uint64_t e_start; + uint64_t e_end; + uint64_t e_delta; + uint64_t e_ratio; + unsigned int nb_vcpu; +}; + +typedef struct vmsr_thread_stat vmsr_thread_stat; +typedef struct vmsr_package_energy_stat vmsr_package_energy_stat; + +char *vmsr_compute_default_paths(void); +void vmsr_read_thread_stat(pid_t pid, + unsigned int thread_id, + unsigned long long *utime, + unsigned long long *stime, + unsigned int *cpu_id); + +QIOChannelSocket *vmsr_open_socket(const char *path); +uint64_t vmsr_read_msr(uint32_t reg, uint32_t cpu_id, + uint32_t tid, QIOChannelSocket *sioc); +void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i); +unsigned int vmsr_get_maxcpus(void); +unsigned int vmsr_get_max_physical_package(unsigned int max_cpus); +unsigned int vmsr_count_cpus_per_package(unsigned int *package_count, + unsigned int max_pkgs); +int vmsr_get_physical_package_id(int cpu_id); +pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads); +double vmsr_get_ratio(uint64_t e_delta, + unsigned long long delta_ticks, + unsigned int maxticks); +void vmsr_init_topo_info(X86CPUTopoInfo *topo_info, const MachineState *ms); +bool is_host_cpu_intel(void); +int is_rapl_enabled(void); +#endif /* VMSR_ENERGY_H */ From f0ccf770789e48b7a73497b465fdc892d28c1339 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 16 Jul 2024 11:36:44 +0200 Subject: [PATCH 2354/2884] hpet: fix and cleanup persistence of interrupt status There are several bugs in the handling of the ISR register: - switching level->edge was not lowering the interrupt and clearing ISR - switching on the enable bit was not raising a level-triggered interrupt if the timer had fired - the timer must be kept running even if not enabled, in order to set the ISR flag, so writes to HPET_TN_CFG must not call hpet_del_timer() Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 60 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 4cb5393c0b..58073df02b 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -196,21 +196,31 @@ static void update_irq(struct HPETTimer *timer, int set) } s = timer->state; mask = 1 << timer->tn; - if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { + + if (set && (timer->config & HPET_TN_TYPE_LEVEL)) { + /* + * If HPET_TN_ENABLE bit is 0, "the timer will still operate and + * generate appropriate status bits, but will not cause an interrupt" + */ + s->isr |= mask; + } else { s->isr &= ~mask; + } + + if (set && timer_enabled(timer) && hpet_enabled(s)) { + if (timer_fsb_route(timer)) { + address_space_stl_le(&address_space_memory, timer->fsb >> 32, + timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED, + NULL); + } else if (timer->config & HPET_TN_TYPE_LEVEL) { + qemu_irq_raise(s->irqs[route]); + } else { + qemu_irq_pulse(s->irqs[route]); + } + } else { if (!timer_fsb_route(timer)) { qemu_irq_lower(s->irqs[route]); } - } else if (timer_fsb_route(timer)) { - address_space_stl_le(&address_space_memory, timer->fsb >> 32, - timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED, - NULL); - } else if (timer->config & HPET_TN_TYPE_LEVEL) { - s->isr |= mask; - qemu_irq_raise(s->irqs[route]); - } else { - s->isr &= ~mask; - qemu_irq_pulse(s->irqs[route]); } } @@ -414,8 +424,13 @@ static void hpet_set_timer(HPETTimer *t) static void hpet_del_timer(HPETTimer *t) { + HPETState *s = t->state; timer_del(t->qemu_timer); - update_irq(t, 0); + + if (s->isr & (1 << t->tn)) { + /* For level-triggered interrupt, this leaves ISR set but lowers irq. */ + update_irq(t, 1); + } } static uint64_t hpet_ram_read(void *opaque, hwaddr addr, @@ -515,20 +530,26 @@ static void hpet_ram_write(void *opaque, hwaddr addr, switch ((addr - 0x100) % 0x20) { case HPET_TN_CFG: trace_hpet_ram_write_tn_cfg(); - if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { + if (deactivating_bit(old_val, new_val, HPET_TN_TYPE_LEVEL)) { + /* + * Do this before changing timer->config; otherwise, if + * HPET_TN_FSB is set, update_irq will not lower the qemu_irq. + */ update_irq(timer, 0); } val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); timer->config = (timer->config & 0xffffffff00000000ULL) | val; + if (activating_bit(old_val, new_val, HPET_TN_ENABLE) + && (s->isr & (1 << timer_id))) { + update_irq(timer, 1); + } + if (new_val & HPET_TN_32BIT) { timer->cmp = (uint32_t)timer->cmp; timer->period = (uint32_t)timer->period; } - if (activating_bit(old_val, new_val, HPET_TN_ENABLE) && - hpet_enabled(s)) { + if (hpet_enabled(s)) { hpet_set_timer(timer); - } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { - hpet_del_timer(timer); } break; case HPET_TN_CFG + 4: // Interrupt capabilities @@ -606,9 +627,10 @@ static void hpet_ram_write(void *opaque, hwaddr addr, s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); for (i = 0; i < s->num_timers; i++) { - if ((&s->timer[i])->cmp != ~0ULL) { - hpet_set_timer(&s->timer[i]); + if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) { + update_irq(&s->timer[i], 1); } + hpet_set_timer(&s->timer[i]); } } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Halt main counter and disable interrupt generation. */ From 9eb7fad3546a89ee7cf0e90f5b1daccf89725cea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 10:53:05 +0200 Subject: [PATCH 2355/2884] hpet: ignore high bits of comparator in 32-bit mode Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 4 ++++ hw/timer/trace-events | 1 + 2 files changed, 5 insertions(+) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 58073df02b..bbb1e5f089 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -585,6 +585,10 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } break; case HPET_TN_CMP + 4: // comparator register high order + if (timer->config & HPET_TN_32BIT) { + trace_hpet_ram_write_invalid_tn_cmp(); + break; + } trace_hpet_ram_write_tn_cmp(4); if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) { diff --git a/hw/timer/trace-events b/hw/timer/trace-events index de769f4b71..a5fafbc679 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -111,6 +111,7 @@ hpet_ram_write_timer_id(uint64_t timer_id) "hpet_ram_writel timer_id = 0x%" PRIx hpet_ram_write_tn_cfg(void) "hpet_ram_writel HPET_TN_CFG" hpet_ram_write_invalid_tn_cfg(uint8_t reg_off) "invalid HPET_TN_CFG + %" PRIu8 " write" hpet_ram_write_tn_cmp(uint8_t reg_off) "hpet_ram_writel HPET_TN_CMP + %" PRIu8 +hpet_ram_write_invalid_tn_cmp(void) "invalid HPET_TN_CMP + 4 write" hpet_ram_write_invalid(void) "invalid hpet_ram_writel" hpet_ram_write_counter_write_while_enabled(void) "Writing counter while HPET enabled!" hpet_ram_write_counter_written(uint8_t reg_off, uint64_t value, uint64_t counter) "HPET counter + %" PRIu8 "written. crt = 0x%" PRIx64 " -> 0x%" PRIx64 From 5895879aca252f4ebb2d1078eaf836c61ec54e9b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 10:31:48 +0200 Subject: [PATCH 2356/2884] hpet: remove unnecessary variable "index" Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index bbb1e5f089..380e272fbe 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -437,12 +437,12 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, unsigned size) { HPETState *s = opaque; - uint64_t cur_tick, index; + uint64_t cur_tick; trace_hpet_ram_read(addr); - index = addr; + /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { + if (addr >= 0x100 && addr <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; HPETTimer *timer = &s->timer[timer_id]; @@ -469,7 +469,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, break; } } else { - switch (index) { + switch (addr) { case HPET_ID: return s->capability; case HPET_PERIOD: @@ -510,15 +510,14 @@ static void hpet_ram_write(void *opaque, hwaddr addr, { int i; HPETState *s = opaque; - uint64_t old_val, new_val, val, index; + uint64_t old_val, new_val, val; trace_hpet_ram_write(addr, value); - index = addr; old_val = hpet_ram_read(opaque, addr, 4); new_val = value; /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { + if (addr >= 0x100 && addr <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; HPETTimer *timer = &s->timer[timer_id]; @@ -620,7 +619,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } return; } else { - switch (index) { + switch (addr) { case HPET_ID: return; case HPET_CFG: From ba88935b0fac2588b0a739f810b58dfabf7f92c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 10:55:13 +0200 Subject: [PATCH 2357/2884] hpet: place read-only bits directly in "new_val" The variable "val" is used for two different purposes. As an intermediate value when writing configuration registers, and to store the cleared bits when writing ISR. Use "new_val" for the former, and rename the variable so that it is clearer for the latter case. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 380e272fbe..831e5a95b0 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -510,7 +510,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, { int i; HPETState *s = opaque; - uint64_t old_val, new_val, val; + uint64_t old_val, new_val, cleared; trace_hpet_ram_write(addr, value); old_val = hpet_ram_read(opaque, addr, 4); @@ -536,13 +536,12 @@ static void hpet_ram_write(void *opaque, hwaddr addr, */ update_irq(timer, 0); } - val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | val; + new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + timer->config = (timer->config & 0xffffffff00000000ULL) | new_val; if (activating_bit(old_val, new_val, HPET_TN_ENABLE) && (s->isr & (1 << timer_id))) { update_irq(timer, 1); } - if (new_val & HPET_TN_32BIT) { timer->cmp = (uint32_t)timer->cmp; timer->period = (uint32_t)timer->period; @@ -623,8 +622,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr, case HPET_ID: return; case HPET_CFG: - val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = (s->config & 0xffffffff00000000ULL) | val; + new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + s->config = (s->config & 0xffffffff00000000ULL) | new_val; if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Enable main counter and interrupt generation. */ s->hpet_offset = @@ -658,9 +657,9 @@ static void hpet_ram_write(void *opaque, hwaddr addr, trace_hpet_invalid_hpet_cfg(4); break; case HPET_STATUS: - val = new_val & s->isr; + cleared = new_val & s->isr; for (i = 0; i < s->num_timers; i++) { - if (val & (1 << i)) { + if (cleared & (1 << i)) { update_irq(&s->timer[i], 0); } } From c2366567378dd8fb89329816003801f54e30e6f3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Wed, 10 Jul 2024 09:58:01 +0200 Subject: [PATCH 2358/2884] hpet: accept 64-bit reads and writes Declare the MemoryRegionOps so that 64-bit reads and writes to the HPET are received directly. This makes it possible to unify the code to process low and high parts: for 32-bit reads, extract the desired word; for 32-bit writes, just merge the desired part into the old value and proceed as with a 64-bit write. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 137 +++++++++++++----------------------------- hw/timer/trace-events | 3 +- 2 files changed, 44 insertions(+), 96 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 831e5a95b0..ac55dd1ebd 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -437,6 +437,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, unsigned size) { HPETState *s = opaque; + int shift = (addr & 4) * 8; uint64_t cur_tick; trace_hpet_ram_read(addr); @@ -451,52 +452,33 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, return 0; } - switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - return timer->config; - case HPET_TN_CFG + 4: // Interrupt capabilities - return timer->config >> 32; + switch (addr & 0x18) { + case HPET_TN_CFG: // including interrupt capabilities + return timer->config >> shift; case HPET_TN_CMP: // comparator register - return timer->cmp; - case HPET_TN_CMP + 4: - return timer->cmp >> 32; + return timer->cmp >> shift; case HPET_TN_ROUTE: - return timer->fsb; - case HPET_TN_ROUTE + 4: - return timer->fsb >> 32; + return timer->fsb >> shift; default: trace_hpet_ram_read_invalid(); break; } } else { - switch (addr) { - case HPET_ID: - return s->capability; - case HPET_PERIOD: - return s->capability >> 32; + switch (addr & ~4) { + case HPET_ID: // including HPET_PERIOD + return s->capability >> shift; case HPET_CFG: - return s->config; - case HPET_CFG + 4: - trace_hpet_invalid_hpet_cfg(4); - return 0; + return s->config >> shift; case HPET_COUNTER: if (hpet_enabled(s)) { cur_tick = hpet_get_ticks(s); } else { cur_tick = s->hpet_counter; } - trace_hpet_ram_read_reading_counter(0, cur_tick); - return cur_tick; - case HPET_COUNTER + 4: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - trace_hpet_ram_read_reading_counter(4, cur_tick); - return cur_tick >> 32; + trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); + return cur_tick >> shift; case HPET_STATUS: - return s->isr; + return s->isr >> shift; default: trace_hpet_ram_read_invalid(); break; @@ -510,11 +492,11 @@ static void hpet_ram_write(void *opaque, hwaddr addr, { int i; HPETState *s = opaque; + int shift = (addr & 4) * 8; + int len = MIN(size * 8, 64 - shift); uint64_t old_val, new_val, cleared; trace_hpet_ram_write(addr, value); - old_val = hpet_ram_read(opaque, addr, 4); - new_val = value; /*address range of all TN regs*/ if (addr >= 0x100 && addr <= 0x3ff) { @@ -526,9 +508,12 @@ static void hpet_ram_write(void *opaque, hwaddr addr, trace_hpet_timer_id_out_of_range(timer_id); return; } - switch ((addr - 0x100) % 0x20) { + switch (addr & 0x18) { case HPET_TN_CFG: - trace_hpet_ram_write_tn_cfg(); + trace_hpet_ram_write_tn_cfg(addr & 4); + old_val = timer->config; + new_val = deposit64(old_val, shift, len, value); + new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); if (deactivating_bit(old_val, new_val, HPET_TN_TYPE_LEVEL)) { /* * Do this before changing timer->config; otherwise, if @@ -536,8 +521,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, */ update_irq(timer, 0); } - new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | new_val; + timer->config = new_val; if (activating_bit(old_val, new_val, HPET_TN_ENABLE) && (s->isr & (1 << timer_id))) { update_irq(timer, 1); @@ -550,56 +534,28 @@ static void hpet_ram_write(void *opaque, hwaddr addr, hpet_set_timer(timer); } break; - case HPET_TN_CFG + 4: // Interrupt capabilities - trace_hpet_ram_write_invalid_tn_cfg(4); - break; case HPET_TN_CMP: // comparator register - trace_hpet_ram_write_tn_cmp(0); if (timer->config & HPET_TN_32BIT) { - new_val = (uint32_t)new_val; - } - if (!timer_is_periodic(timer) - || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val; - } - if (timer_is_periodic(timer)) { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - if (timer->config & HPET_TN_32BIT) { - new_val = MIN(new_val, ~0u >> 1); + /* High 32-bits are zero, leave them untouched. */ + if (shift) { + trace_hpet_ram_write_invalid_tn_cmp(); + break; } - timer->period = - (timer->period & 0xffffffff00000000ULL) | new_val; + len = 64; + value = (uint32_t) value; } - /* - * FIXME: on a 64-bit write, HPET_TN_SETVAL should apply to the - * high bits part as well. - */ - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled(s)) { - hpet_set_timer(timer); - } - break; - case HPET_TN_CMP + 4: // comparator register high order - if (timer->config & HPET_TN_32BIT) { - trace_hpet_ram_write_invalid_tn_cmp(); - break; - } - trace_hpet_ram_write_tn_cmp(4); + trace_hpet_ram_write_tn_cmp(addr & 4); if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; + timer->cmp = deposit64(timer->cmp, shift, len, value); } if (timer_is_periodic(timer)) { /* * FIXME: Clamp period to reasonable min value? * Clamp period to reasonable max value */ - new_val = MIN(new_val, ~0u >> 1); - timer->period = - (timer->period & 0xffffffffULL) | new_val << 32; + new_val = deposit64(timer->period, shift, len, value); + timer->period = MIN(new_val, (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1); } timer->config &= ~HPET_TN_SETVAL; if (hpet_enabled(s)) { @@ -607,10 +563,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } break; case HPET_TN_ROUTE: - timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val; - break; - case HPET_TN_ROUTE + 4: - timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff); + timer->fsb = deposit64(timer->fsb, shift, len, value); break; default: trace_hpet_ram_write_invalid(); @@ -618,12 +571,14 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } return; } else { - switch (addr) { + switch (addr & ~4) { case HPET_ID: return; case HPET_CFG: + old_val = s->config; + new_val = deposit64(old_val, shift, len, value); new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = (s->config & 0xffffffff00000000ULL) | new_val; + s->config = new_val; if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Enable main counter and interrupt generation. */ s->hpet_offset = @@ -653,10 +608,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr, qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); } break; - case HPET_CFG + 4: - trace_hpet_invalid_hpet_cfg(4); - break; case HPET_STATUS: + new_val = value << shift; cleared = new_val & s->isr; for (i = 0; i < s->num_timers; i++) { if (cleared & (1 << i)) { @@ -668,15 +621,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, if (hpet_enabled(s)) { trace_hpet_ram_write_counter_write_while_enabled(); } - s->hpet_counter = - (s->hpet_counter & 0xffffffff00000000ULL) | value; - trace_hpet_ram_write_counter_written(0, value, s->hpet_counter); - break; - case HPET_COUNTER + 4: - trace_hpet_ram_write_counter_write_while_enabled(); - s->hpet_counter = - (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32); - trace_hpet_ram_write_counter_written(4, value, s->hpet_counter); + s->hpet_counter = deposit64(s->hpet_counter, shift, len, value); break; default: trace_hpet_ram_write_invalid(); @@ -690,7 +635,11 @@ static const MemoryRegionOps hpet_ram_ops = { .write = hpet_ram_write, .valid = { .min_access_size = 4, - .max_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, }, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/timer/trace-events b/hw/timer/trace-events index a5fafbc679..f48a712801 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -108,8 +108,7 @@ hpet_ram_read_reading_counter(uint8_t reg_off, uint64_t cur_tick) "reading count hpet_ram_read_invalid(void) "invalid hpet_ram_readl" hpet_ram_write(uint64_t addr, uint64_t value) "enter hpet_ram_writel at 0x%" PRIx64 " = 0x%" PRIx64 hpet_ram_write_timer_id(uint64_t timer_id) "hpet_ram_writel timer_id = 0x%" PRIx64 -hpet_ram_write_tn_cfg(void) "hpet_ram_writel HPET_TN_CFG" -hpet_ram_write_invalid_tn_cfg(uint8_t reg_off) "invalid HPET_TN_CFG + %" PRIu8 " write" +hpet_ram_write_tn_cfg(uint8_t reg_off) "hpet_ram_writel HPET_TN_CFG + %" PRIu8 hpet_ram_write_tn_cmp(uint8_t reg_off) "hpet_ram_writel HPET_TN_CMP + %" PRIu8 hpet_ram_write_invalid_tn_cmp(void) "invalid HPET_TN_CMP + 4 write" hpet_ram_write_invalid(void) "invalid hpet_ram_writel" From 242d665396407f83a6acbffc804882eeb21cfdad Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 16 Jul 2024 12:26:36 +0200 Subject: [PATCH 2359/2884] hpet: store full 64-bit target value of the counter Store the full 64-bit value at which the timer should fire. This makes it possible to skip the imprecise hpet_calculate_diff() step, and to remove the clamping of the period to 31 or 63 bits. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 111 +++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 62 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index ac55dd1ebd..1654b7cb8b 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -54,6 +54,7 @@ typedef struct HPETTimer { /* timers */ uint64_t cmp; /* comparator */ uint64_t fsb; /* FSB route */ /* Hidden register state */ + uint64_t cmp64; /* comparator (extended to counter width) */ uint64_t period; /* Last value written to comparator */ uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit * mode. Next pop will be actual timer expiration. @@ -115,11 +116,6 @@ static uint32_t timer_enabled(HPETTimer *t) } static uint32_t hpet_time_after(uint64_t a, uint64_t b) -{ - return ((int32_t)(b - a) < 0); -} - -static uint32_t hpet_time_after64(uint64_t a, uint64_t b) { return ((int64_t)(b - a) < 0); } @@ -156,29 +152,34 @@ static uint64_t hpet_get_ticks(HPETState *s) return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset); } -/* - * calculate diff between comparator value and current ticks - */ -static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) +static uint64_t hpet_get_ns(HPETState *s, uint64_t tick) { + return ticks_to_ns(tick) - s->hpet_offset; +} +/* + * calculate next value of the general counter that matches the + * target (either entirely, or the low 32-bit only depending on + * the timer mode). + */ +static uint64_t hpet_calculate_cmp64(HPETTimer *t, uint64_t cur_tick, uint64_t target) +{ if (t->config & HPET_TN_32BIT) { - uint32_t diff, cmp; - - cmp = (uint32_t)t->cmp; - diff = cmp - (uint32_t)current; - diff = (int32_t)diff > 0 ? diff : (uint32_t)1; - return (uint64_t)diff; + uint64_t result = deposit64(cur_tick, 0, 32, target); + if (result < cur_tick) { + result += 0x100000000ULL; + } + return result; } else { - uint64_t diff, cmp; - - cmp = t->cmp; - diff = cmp - current; - diff = (int64_t)diff > 0 ? diff : (uint64_t)1; - return diff; + return target; } } +static uint64_t hpet_next_wrap(uint64_t cur_tick) +{ + return (cur_tick | 0xffffffffU) + 1; +} + static void update_irq(struct HPETTimer *timer, int set) { uint64_t mask; @@ -260,7 +261,12 @@ static bool hpet_validate_num_timers(void *opaque, int version_id) static int hpet_post_load(void *opaque, int version_id) { HPETState *s = opaque; + int i; + for (i = 0; i < s->num_timers; i++) { + HPETTimer *t = &s->timer[i]; + t->cmp64 = hpet_calculate_cmp64(t, s->hpet_counter, t->cmp); + } /* Recalculate the offset between the main counter and guest time */ if (!s->hpet_offset_saved) { s->hpet_offset = ticks_to_ns(s->hpet_counter) @@ -356,14 +362,10 @@ static const VMStateDescription vmstate_hpet = { } }; -static void hpet_arm(HPETTimer *t, uint64_t ticks) +static void hpet_arm(HPETTimer *t, uint64_t tick) { - if (ticks < ns_to_ticks(INT64_MAX / 2)) { - timer_mod(t->qemu_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks_to_ns(ticks)); - } else { - timer_del(t->qemu_timer); - } + /* FIXME: Clamp period to reasonable min value? */ + timer_mod(t->qemu_timer, hpet_get_ns(t->state, tick)); } /* @@ -372,54 +374,44 @@ static void hpet_arm(HPETTimer *t, uint64_t ticks) static void hpet_timer(void *opaque) { HPETTimer *t = opaque; - uint64_t diff; - uint64_t period = t->period; uint64_t cur_tick = hpet_get_ticks(t->state); if (timer_is_periodic(t) && period != 0) { + while (hpet_time_after(cur_tick, t->cmp64)) { + t->cmp64 += period; + } if (t->config & HPET_TN_32BIT) { - while (hpet_time_after(cur_tick, t->cmp)) { - t->cmp = (uint32_t)(t->cmp + t->period); - } + t->cmp = (uint32_t)t->cmp64; } else { - while (hpet_time_after64(cur_tick, t->cmp)) { - t->cmp += period; - } - } - diff = hpet_calculate_diff(t, cur_tick); - hpet_arm(t, diff); - } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { - if (t->wrap_flag) { - diff = hpet_calculate_diff(t, cur_tick); - hpet_arm(t, diff); - t->wrap_flag = 0; + t->cmp = t->cmp64; } + hpet_arm(t, t->cmp64); + } else if (t->wrap_flag) { + t->wrap_flag = 0; + hpet_arm(t, t->cmp64); } update_irq(t, 1); } static void hpet_set_timer(HPETTimer *t) { - uint64_t diff; - uint32_t wrap_diff; /* how many ticks until we wrap? */ uint64_t cur_tick = hpet_get_ticks(t->state); - /* whenever new timer is being set up, make sure wrap_flag is 0 */ t->wrap_flag = 0; - diff = hpet_calculate_diff(t, cur_tick); + t->cmp64 = hpet_calculate_cmp64(t, cur_tick, t->cmp); + if (t->config & HPET_TN_32BIT) { - /* hpet spec says in one-shot 32-bit mode, generate an interrupt when - * counter wraps in addition to an interrupt with comparator match. - */ - if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { - wrap_diff = 0xffffffff - (uint32_t)cur_tick; - if (wrap_diff < (uint32_t)diff) { - diff = wrap_diff; + /* hpet spec says in one-shot 32-bit mode, generate an interrupt when + * counter wraps in addition to an interrupt with comparator match. + */ + if (!timer_is_periodic(t) && t->cmp64 > hpet_next_wrap(cur_tick)) { t->wrap_flag = 1; + hpet_arm(t, hpet_next_wrap(cur_tick)); + return; } } - hpet_arm(t, diff); + hpet_arm(t, t->cmp64); } static void hpet_del_timer(HPETTimer *t) @@ -550,12 +542,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, timer->cmp = deposit64(timer->cmp, shift, len, value); } if (timer_is_periodic(timer)) { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val = deposit64(timer->period, shift, len, value); - timer->period = MIN(new_val, (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1); + timer->period = deposit64(timer->period, shift, len, value); } timer->config &= ~HPET_TN_SETVAL; if (hpet_enabled(s)) { From 7c912ffb59e8137091894d767433e65c3df8b0bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 16 Jul 2024 12:35:39 +0200 Subject: [PATCH 2360/2884] hpet: avoid timer storms on periodic timers If the period is set to a value that is too low, there could be no time left to run the rest of QEMU. Do not trigger interrupts faster than 1 MHz. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/timer/hpet.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 1654b7cb8b..471950adef 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -59,6 +59,7 @@ typedef struct HPETTimer { /* timers */ uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit * mode. Next pop will be actual timer expiration. */ + uint64_t last; /* last value armed, to avoid timer storms */ } HPETTimer; struct HPETState { @@ -266,6 +267,7 @@ static int hpet_post_load(void *opaque, int version_id) for (i = 0; i < s->num_timers; i++) { HPETTimer *t = &s->timer[i]; t->cmp64 = hpet_calculate_cmp64(t, s->hpet_counter, t->cmp); + t->last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - NANOSECONDS_PER_SECOND; } /* Recalculate the offset between the main counter and guest time */ if (!s->hpet_offset_saved) { @@ -364,8 +366,15 @@ static const VMStateDescription vmstate_hpet = { static void hpet_arm(HPETTimer *t, uint64_t tick) { - /* FIXME: Clamp period to reasonable min value? */ - timer_mod(t->qemu_timer, hpet_get_ns(t->state, tick)); + uint64_t ns = hpet_get_ns(t->state, tick); + + /* Clamp period to reasonable min value (1 us) */ + if (timer_is_periodic(t) && ns - t->last < 1000) { + ns = t->last + 1000; + } + + t->last = ns; + timer_mod(t->qemu_timer, ns); } /* From 903cc9e1173e0778caa50871e8275c898770c690 Mon Sep 17 00:00:00 2001 From: songziming <s.ziming@hotmail.com> Date: Mon, 22 Jul 2024 17:52:55 +0800 Subject: [PATCH 2361/2884] chardev/char-win-stdio.c: restore old console mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If I use `-serial stdio` on Windows, after QEMU exits, the terminal could not handle arrow keys and tab any more. Because stdio backend on Windows sets console mode to virtual terminal input when starts, but does not restore the old mode when finalize. This small patch saves the old console mode and set it back. Signed-off-by: Ziming Song <s.ziming@hotmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <ME3P282MB25488BE7C39BF0C35CD0DA5D8CA82@ME3P282MB2548.AUSP282.PROD.OUTLOOK.COM> --- chardev/char-win-stdio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index 1a18999e78..13325ca967 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -33,6 +33,7 @@ struct WinStdioChardev { Chardev parent; HANDLE hStdIn; + DWORD dwOldMode; HANDLE hInputReadyEvent; HANDLE hInputDoneEvent; HANDLE hInputThread; @@ -159,6 +160,7 @@ static void qemu_chr_open_stdio(Chardev *chr, } is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; + stdio->dwOldMode = dwMode; if (is_console) { if (qemu_add_wait_object(stdio->hStdIn, @@ -221,6 +223,9 @@ static void char_win_stdio_finalize(Object *obj) { WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj); + if (stdio->hStdIn != INVALID_HANDLE_VALUE) { + SetConsoleMode(stdio->hStdIn, stdio->dwOldMode); + } if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) { CloseHandle(stdio->hInputReadyEvent); } From ca6dd3aef8a103138c99788bcba8195d4905ddc5 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:08 +0900 Subject: [PATCH 2362/2884] hw/pci: Fix SR-IOV VF number calculation pci_config_get_bar_addr() had a division by vf_stride. vf_stride needs to be non-zero when there are multiple VFs, but the specification does not prohibit to make it zero when there is only one VF. Do not perform the division for the first VF to avoid division by zero. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-2-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 4c7be52951..cf2794879d 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1437,7 +1437,11 @@ static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg, pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET); uint16_t vf_stride = pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride; + uint32_t vf_num = d->devfn - (pf->devfn + vf_offset); + + if (vf_num) { + vf_num /= vf_stride; + } if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { new_addr = pci_get_quad(pf->config + bar); From 78f9d7fd1989311040beff54979bcb2a1ba0aff2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:09 +0900 Subject: [PATCH 2363/2884] pcie_sriov: Ensure PF and VF are mutually exclusive A device cannot be a SR-IOV PF and a VF at the same time. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-3-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 56523ab4e8..6c79658b4c 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -42,6 +42,11 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (pci_is_vf(dev)) { + error_setg(errp, "a device cannot be both an SR-IOV PF and a VF"); + return false; + } + if (total_vfs) { uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI); uint16_t first_vf_devfn = dev->devfn + vf_offset; From 47cc753e50076c25334091783738be9f716253b1 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:10 +0900 Subject: [PATCH 2364/2884] pcie_sriov: Check PCI Express for SR-IOV PF SR-IOV requires PCI Express. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-4-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 6c79658b4c..15a4aac1f4 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -42,6 +42,11 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (!pci_is_express(dev)) { + error_setg(errp, "PCI Express is required for SR-IOV PF"); + return false; + } + if (pci_is_vf(dev)) { error_setg(errp, "a device cannot be both an SR-IOV PF and a VF"); return false; From 122173a5830f7757f8a94a3b1559582f312e140b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:11 +0900 Subject: [PATCH 2365/2884] pcie_sriov: Allow user to create SR-IOV device A user can create a SR-IOV device by specifying the PF with the sriov-pf property of the VFs. The VFs must be added before the PF. A user-creatable VF must have PCIDeviceClass::sriov_vf_user_creatable set. Such a VF cannot refer to the PF because it is created before the PF. A PF that user-creatable VFs can be attached calls pcie_sriov_pf_init_from_user_created_vfs() during realization and pcie_sriov_pf_exit() when exiting. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-5-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 62 +++++--- hw/pci/pcie_sriov.c | 290 ++++++++++++++++++++++++++++-------- include/hw/pci/pci_device.h | 6 +- include/hw/pci/pcie_sriov.h | 18 +++ 4 files changed, 293 insertions(+), 83 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index cf2794879d..8ad5d7e2d8 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -85,6 +85,7 @@ static Property pci_props[] = { QEMU_PCIE_ERR_UNC_MASK_BITNR, true), DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present, QEMU_PCIE_ARI_NEXTFN_1_BITNR, false), + DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf), DEFINE_PROP_END_OF_LIST() }; @@ -959,13 +960,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } - /* - * With SR/IOV and ARI, a device at function 0 need not be a multifunction - * device, as it may just be a VF that ended up with function 0 in - * the legacy PCI interpretation. Avoid failing in such cases: - */ - if (pci_is_vf(dev) && - dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + /* SR/IOV is not handled here. */ + if (pci_is_vf(dev)) { return; } @@ -998,7 +994,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) } /* function 0 indicates single function, so function > 0 must be NULL */ for (func = 1; func < PCI_FUNC_MAX; ++func) { - if (bus->devices[PCI_DEVFN(slot, func)]) { + PCIDevice *device = bus->devices[PCI_DEVFN(slot, func)]; + if (device && !pci_is_vf(device)) { error_setg(errp, "PCI: %x.0 indicates single function, " "but %x.%x is already populated.", slot, slot, func); @@ -1283,6 +1280,7 @@ static void pci_qdev_unrealize(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); + pcie_sriov_unregister_device(pci_dev); if (pc->exit) { pc->exit(pci_dev); @@ -1314,7 +1312,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size = memory_region_size(memory); uint8_t hdr_type; - assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */ assert(region_num >= 0); assert(region_num < PCI_NUM_REGIONS); assert(is_power_of_2(size)); @@ -1325,7 +1322,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2); r = &pci_dev->io_regions[region_num]; - r->addr = PCI_BAR_UNMAPPED; r->size = size; r->type = type; r->memory = memory; @@ -1333,22 +1329,35 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, ? pci_get_bus(pci_dev)->address_space_io : pci_get_bus(pci_dev)->address_space_mem; - wmask = ~(size - 1); - if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writable */ - wmask |= PCI_ROM_ADDRESS_ENABLE; - } + if (pci_is_vf(pci_dev)) { + PCIDevice *pf = pci_dev->exp.sriov_vf.pf; + assert(!pf || type == pf->exp.sriov_pf.vf_bar_type[region_num]); - addr = pci_bar(pci_dev, region_num); - pci_set_long(pci_dev->config + addr, type); - - if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && - r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(pci_dev->wmask + addr, wmask); - pci_set_quad(pci_dev->cmask + addr, ~0ULL); + r->addr = pci_bar_address(pci_dev, region_num, r->type, r->size); + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); + } } else { - pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); - pci_set_long(pci_dev->cmask + addr, 0xffffffff); + r->addr = PCI_BAR_UNMAPPED; + + wmask = ~(size - 1); + if (region_num == PCI_ROM_SLOT) { + /* ROM enable bit is writable */ + wmask |= PCI_ROM_ADDRESS_ENABLE; + } + + addr = pci_bar(pci_dev, region_num); + pci_set_long(pci_dev->config + addr, type); + + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && + r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(pci_dev->wmask + addr, wmask); + pci_set_quad(pci_dev->cmask + addr, ~0ULL); + } else { + pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); + pci_set_long(pci_dev->cmask + addr, 0xffffffff); + } } } @@ -2109,6 +2118,11 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) } } + if (!pcie_sriov_register_device(pci_dev, errp)) { + pci_qdev_unrealize(DEVICE(pci_dev)); + return; + } + /* * A PCIe Downstream Port that do not have ARI Forwarding enabled must * associate only Device 0 with the device attached to the bus diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 15a4aac1f4..0fc9f810b9 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -20,6 +20,8 @@ #include "qapi/error.h" #include "trace.h" +static GHashTable *pfs; + static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) { for (uint16_t i = 0; i < total_vfs; i++) { @@ -31,14 +33,49 @@ static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) dev->exp.sriov_pf.vf = NULL; } -bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, - const char *vfname, uint16_t vf_dev_id, - uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride, - Error **errp) +static void clear_ctrl_vfe(PCIDevice *dev) +{ + uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL; + pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE); +} + +static void register_vfs(PCIDevice *dev) +{ + uint16_t num_vfs; + uint16_t i; + uint16_t sriov_cap = dev->exp.sriov_cap; + + assert(sriov_cap > 0); + num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { + clear_ctrl_vfe(dev); + return; + } + + trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); + } +} + +static void unregister_vfs(PCIDevice *dev) +{ + uint16_t i; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); + } +} + +static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, + uint16_t vf_dev_id, uint16_t init_vfs, + uint16_t total_vfs, uint16_t vf_offset, + uint16_t vf_stride, Error **errp) { - BusState *bus = qdev_get_parent_bus(&dev->qdev); - int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -100,6 +137,28 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); + return true; +} + +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, + const char *vfname, uint16_t vf_dev_id, + uint16_t init_vfs, uint16_t total_vfs, + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) +{ + BusState *bus = qdev_get_parent_bus(&dev->qdev); + int32_t devfn = dev->devfn + vf_offset; + + if (pfs && g_hash_table_contains(pfs, dev->qdev.id)) { + error_setg(errp, "attaching user-created SR-IOV VF unsupported"); + return false; + } + + if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, init_vfs, + total_vfs, vf_offset, vf_stride, errp)) { + return false; + } + dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); for (uint16_t i = 0; i < total_vfs; i++) { @@ -129,7 +188,24 @@ void pcie_sriov_pf_exit(PCIDevice *dev) { uint8_t *cfg = dev->config + dev->exp.sriov_cap; - unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); + if (dev->exp.sriov_pf.vf_user_created) { + uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t total_vfs = pci_get_word(dev->config + PCI_SRIOV_TOTAL_VF); + uint16_t vf_dev_id = pci_get_word(dev->config + PCI_SRIOV_VF_DID); + + unregister_vfs(dev); + + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + + vf->exp.sriov_vf.pf = NULL; + + pci_config_set_vendor_id(vf->config, ven_id); + pci_config_set_device_id(vf->config, vf_dev_id); + } + } else { + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); + } } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -162,74 +238,172 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory) { - PCIIORegion *r; - PCIBus *bus = pci_get_bus(dev); uint8_t type; - pcibus_t size = memory_region_size(memory); - assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */ - assert(region_num >= 0); - assert(region_num < PCI_NUM_REGIONS); + assert(dev->exp.sriov_vf.pf); type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num]; - if (!is_power_of_2(size)) { - error_report("%s: PCI region size must be a power" - " of two - type=0x%x, size=0x%"FMT_PCIBUS, - __func__, type, size); - exit(1); - } - - r = &dev->io_regions[region_num]; - r->memory = memory; - r->address_space = - type & PCI_BASE_ADDRESS_SPACE_IO - ? bus->address_space_io - : bus->address_space_mem; - r->size = size; - r->type = type; - - r->addr = pci_bar_address(dev, region_num, r->type, r->size); - if (r->addr != PCI_BAR_UNMAPPED) { - memory_region_add_subregion_overlap(r->address_space, - r->addr, r->memory, 1); - } + return pci_register_bar(dev, region_num, type, memory); } -static void clear_ctrl_vfe(PCIDevice *dev) +static gint compare_vf_devfns(gconstpointer a, gconstpointer b) { - uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL; - pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE); + return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn; } -static void register_vfs(PCIDevice *dev) +int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, + uint16_t offset, + Error **errp) { - uint16_t num_vfs; + GPtrArray *pf; + PCIDevice **vfs; + BusState *bus = qdev_get_parent_bus(DEVICE(dev)); + uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t vf_dev_id; + uint16_t vf_offset; + uint16_t vf_stride; uint16_t i; - uint16_t sriov_cap = dev->exp.sriov_cap; - assert(sriov_cap > 0); - num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); - if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { - clear_ctrl_vfe(dev); - return; + if (!pfs || !dev->qdev.id) { + return 0; } - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], true); + pf = g_hash_table_lookup(pfs, dev->qdev.id); + if (!pf) { + return 0; } + + if (pf->len > UINT16_MAX) { + error_setg(errp, "too many VFs"); + return -1; + } + + g_ptr_array_sort(pf, compare_vf_devfns); + vfs = (void *)pf->pdata; + + if (vfs[0]->devfn <= dev->devfn) { + error_setg(errp, "a VF function number is less than the PF function number"); + return -1; + } + + vf_dev_id = pci_get_word(vfs[0]->config + PCI_DEVICE_ID); + vf_offset = vfs[0]->devfn - dev->devfn; + vf_stride = pf->len < 2 ? 0 : vfs[1]->devfn - vfs[0]->devfn; + + for (i = 0; i < pf->len; i++) { + if (bus != qdev_get_parent_bus(&vfs[i]->qdev)) { + error_setg(errp, "SR-IOV VF parent bus mismatches with PF"); + return -1; + } + + if (ven_id != pci_get_word(vfs[i]->config + PCI_VENDOR_ID)) { + error_setg(errp, "SR-IOV VF vendor ID mismatches with PF"); + return -1; + } + + if (vf_dev_id != pci_get_word(vfs[i]->config + PCI_DEVICE_ID)) { + error_setg(errp, "inconsistent SR-IOV VF device IDs"); + return -1; + } + + for (size_t j = 0; j < PCI_NUM_REGIONS; j++) { + if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size || + vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) { + error_setg(errp, "inconsistent SR-IOV BARs"); + return -1; + } + } + + if (vfs[i]->devfn - vfs[0]->devfn != vf_stride * i) { + error_setg(errp, "inconsistent SR-IOV stride"); + return -1; + } + } + + if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, pf->len, + pf->len, vf_offset, vf_stride, errp)) { + return -1; + } + + for (i = 0; i < pf->len; i++) { + vfs[i]->exp.sriov_vf.pf = dev; + vfs[i]->exp.sriov_vf.vf_number = i; + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vfs[i]->config, 0xffff); + pci_config_set_device_id(vfs[i]->config, 0xffff); + } + + dev->exp.sriov_pf.vf = vfs; + dev->exp.sriov_pf.vf_user_created = true; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + PCIIORegion *region = &vfs[0]->io_regions[i]; + + if (region->size) { + pcie_sriov_pf_init_vf_bar(dev, i, region->type, region->size); + } + } + + return PCI_EXT_CAP_SRIOV_SIZEOF; } -static void unregister_vfs(PCIDevice *dev) +bool pcie_sriov_register_device(PCIDevice *dev, Error **errp) { - uint16_t i; - uint8_t *cfg = dev->config + dev->exp.sriov_cap; + if (!dev->exp.sriov_pf.vf && dev->qdev.id && + pfs && g_hash_table_contains(pfs, dev->qdev.id)) { + error_setg(errp, "attaching user-created SR-IOV VF unsupported"); + return false; + } - trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], false); + if (dev->sriov_pf) { + PCIDevice *pci_pf; + GPtrArray *pf; + + if (!PCI_DEVICE_GET_CLASS(dev)->sriov_vf_user_creatable) { + error_setg(errp, "user cannot create SR-IOV VF with this device type"); + return false; + } + + if (!pci_is_express(dev)) { + error_setg(errp, "PCI Express is required for SR-IOV VF"); + return false; + } + + if (!pci_qdev_find_device(dev->sriov_pf, &pci_pf)) { + error_setg(errp, "PCI device specified as SR-IOV PF already exists"); + return false; + } + + if (!pfs) { + pfs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } + + pf = g_hash_table_lookup(pfs, dev->sriov_pf); + if (!pf) { + pf = g_ptr_array_new(); + g_hash_table_insert(pfs, g_strdup(dev->sriov_pf), pf); + } + + g_ptr_array_add(pf, dev); + } + + return true; +} + +void pcie_sriov_unregister_device(PCIDevice *dev) +{ + if (dev->sriov_pf && pfs) { + GPtrArray *pf = g_hash_table_lookup(pfs, dev->sriov_pf); + + if (pf) { + g_ptr_array_remove_fast(pf, dev); + + if (!pf->len) { + g_hash_table_remove(pfs, dev->sriov_pf); + g_ptr_array_free(pf, FALSE); + } + } } } @@ -316,7 +490,7 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize) uint16_t pcie_sriov_vf_number(PCIDevice *dev) { - assert(pci_is_vf(dev)); + assert(dev->exp.sriov_vf.pf); return dev->exp.sriov_vf.vf_number; } diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index ca15132508..cefd6f7640 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -37,6 +37,8 @@ struct PCIDeviceClass { uint16_t subsystem_id; /* only for header type = 0 */ const char *romfile; /* rom bar */ + + bool sriov_vf_user_creatable; }; enum PCIReqIDType { @@ -160,6 +162,8 @@ struct PCIDevice { /* ID of standby device in net_failover pair */ char *failover_pair_id; uint32_t acpi_index; + + char *sriov_pf; }; static inline int pci_intx(PCIDevice *pci_dev) @@ -192,7 +196,7 @@ static inline int pci_is_express_downstream_port(const PCIDevice *d) static inline int pci_is_vf(const PCIDevice *d) { - return d->exp.sriov_vf.pf != NULL; + return d->sriov_pf || d->exp.sriov_vf.pf != NULL; } static inline uint32_t pci_config_size(const PCIDevice *d) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index c5d2d318d3..f75b8f22ee 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -18,6 +18,7 @@ typedef struct PCIESriovPF { uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ + bool vf_user_created; /* If VFs are created by user */ } PCIESriovPF; typedef struct PCIESriovVF { @@ -40,6 +41,23 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory); +/** + * pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created + * VFs. + * @dev: A PCIe device being realized. + * @offset: The offset of the SR-IOV capability. + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: The size of added capability. 0 if the user did not create VFs. + * -1 if failed. + */ +int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, + uint16_t offset, + Error **errp); + +bool pcie_sriov_register_device(PCIDevice *dev, Error **errp); +void pcie_sriov_unregister_device(PCIDevice *dev); + /* * Default (minimal) page size support values * as required by the SR/IOV standard: From 3f868ffb0bae0c4feafabe34a371cded57fe3806 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:12 +0900 Subject: [PATCH 2366/2884] virtio-pci: Implement SR-IOV PF Allow user to attach SR-IOV VF to a virtio-pci PF. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-6-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-pci.c | 20 +++++++++++++++----- include/hw/virtio/virtio-pci.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 9534730bba..0c8fcc5627 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1955,6 +1955,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) uint8_t *config; uint32_t size; VirtIODevice *vdev = virtio_bus_get_device(bus); + int16_t res; /* * Virtio capabilities present without @@ -2100,6 +2101,14 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx, PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar); } + + res = pcie_sriov_pf_init_from_user_created_vfs(&proxy->pci_dev, + proxy->last_pcie_cap_offset, + errp); + if (res > 0) { + proxy->last_pcie_cap_offset += res; + virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV); + } } static void virtio_pci_device_unplugged(DeviceState *d) @@ -2187,7 +2196,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) if (pcie_port && pci_is_express(pci_dev)) { int pos; - uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; + proxy->last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); @@ -2207,9 +2216,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); if (proxy->flags & VIRTIO_PCI_FLAG_AER) { - pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset, + pcie_aer_init(pci_dev, PCI_ERR_VER, proxy->last_pcie_cap_offset, PCI_ERR_SIZEOF, NULL); - last_pcie_cap_offset += PCI_ERR_SIZEOF; + proxy->last_pcie_cap_offset += PCI_ERR_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) { @@ -2234,9 +2243,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) } if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { - pcie_ats_init(pci_dev, last_pcie_cap_offset, + pcie_ats_init(pci_dev, proxy->last_pcie_cap_offset, proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED); - last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; + proxy->last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) { @@ -2263,6 +2272,7 @@ static void virtio_pci_exit(PCIDevice *pci_dev) bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) && !pci_bus_is_root(pci_get_bus(pci_dev)); + pcie_sriov_pf_exit(&proxy->pci_dev); msix_uninit_exclusive_bar(pci_dev); if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port && pci_is_express(pci_dev)) { diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 9e67ba38c7..34539f2f67 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -152,6 +152,7 @@ struct VirtIOPCIProxy { uint32_t modern_io_bar_idx; uint32_t modern_mem_bar_idx; int config_cap; + uint16_t last_pcie_cap_offset; uint32_t flags; bool disable_modern; bool ignore_backend_features; From c2d6db6a1f39780b24538440091893f9fbe060a7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:13 +0900 Subject: [PATCH 2367/2884] virtio-net: Implement SR-IOV VF A virtio-net device can be added as a SR-IOV VF to another virtio-pci device that will be the PF. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-7-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-net-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index e03543a70a..dba4987d6e 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -75,6 +75,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VIRTIO_NET; k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_NETWORK_ETHERNET; + k->sriov_vf_user_creatable = true; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); device_class_set_props(dc, virtio_net_properties); vpciklass->realize = virtio_net_pci_realize; From d6f40c95b35bd380340b698e4306704fe22a5d68 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 15 Jul 2024 14:19:14 +0900 Subject: [PATCH 2368/2884] docs: Document composable SR-IOV device Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240715-sriov-v5-8-3f5539093ffc@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- MAINTAINERS | 1 + docs/system/index.rst | 1 + docs/system/sriov.rst | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 docs/system/sriov.rst diff --git a/MAINTAINERS b/MAINTAINERS index 8ad64ff76b..93546cfb14 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2010,6 +2010,7 @@ F: hw/pci-bridge/* F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* +F: docs/system/sriov.rst PCIE DOE M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw> diff --git a/docs/system/index.rst b/docs/system/index.rst index c21065e519..718e9d3c56 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -39,3 +39,4 @@ or Hypervisor.Framework. multi-process confidential-guest-support vm-templating + sriov diff --git a/docs/system/sriov.rst b/docs/system/sriov.rst new file mode 100644 index 0000000000..a851a66a4b --- /dev/null +++ b/docs/system/sriov.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Compsable SR-IOV device +======================= + +SR-IOV (Single Root I/O Virtualization) is an optional extended capability of a +PCI Express device. It allows a single physical function (PF) to appear as +multiple virtual functions (VFs) for the main purpose of eliminating software +overhead in I/O from virtual machines. + +There are devices with predefined SR-IOV configurations, but it is also possible +to compose an SR-IOV device yourself. Composing an SR-IOV device is currently +only supported by virtio-net-pci. + +Users can configure an SR-IOV-capable virtio-net device by adding +virtio-net-pci functions to a bus. Below is a command line example: + +.. code-block:: shell + + -netdev user,id=n -netdev user,id=o + -netdev user,id=p -netdev user,id=q + -device pcie-root-port,id=b + -device virtio-net-pci,bus=b,addr=0x0.0x3,netdev=q,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x2,netdev=p,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x1,netdev=o,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x0,netdev=n,id=f + +The VFs specify the paired PF with ``sriov-pf`` property. The PF must be +added after all VFs. It is the user's responsibility to ensure that VFs have +function numbers larger than one of the PF, and that the function numbers +have a consistent stride. + +You may also need to perform additional steps to activate the SR-IOV feature on +your guest. For Linux, refer to [1]_. + +.. [1] https://docs.kernel.org/PCI/pci-iov-howto.html From 62f182c97b31445012d37181005a83ff8453edaa Mon Sep 17 00:00:00 2001 From: Igor Mammedov <imammedo@redhat.com> Date: Mon, 15 Jul 2024 14:24:17 +0200 Subject: [PATCH 2369/2884] smbios: make memory device size configurable per Machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently QEMU describes initial[1] RAM* in SMBIOS as a series of virtual DIMMs (capped at 16Gb max) using type 17 structure entries. Which is fine for the most cases. However when starting guest with terabytes of RAM this leads to too many memory device structures, which eventually upsets linux kernel as it reserves only 64K for these entries and when that border is crossed out it runs out of reserved memory. Instead of partitioning initial RAM on 16Gb DIMMs, use maximum possible chunk size that SMBIOS spec allows[2]. Which lets encode RAM in lower 31 bits of 32bit field (which amounts upto 2047Tb per DIMM). As result initial RAM will generate only one type 17 structure until host/guest reach ability to use more RAM in the future. Compat changes: We can't unconditionally change chunk size as it will break QEMU<->guest ABI (and migration). Thus introduce a new machine class field that would let older versioned machines to use legacy 16Gb chunks, while new(er) machine type[s] use maximum possible chunk size. PS: While it might seem to be risky to rise max entry size this large (much beyond of what current physical RAM modules support), I'd not expect it causing much issues, modulo uncovering bugs in software running within guest. And those should be fixed on guest side to handle SMBIOS spec properly, especially if guest is expected to support so huge RAM configs. In worst case, QEMU can reduce chunk size later if we would care enough about introducing a workaround for some 'unfixable' guest OS, either by fixing up the next machine type or giving users a CLI option to customize it. 1) Initial RAM - is RAM configured with help '-m SIZE' CLI option/ implicitly defined by machine. It doesn't include memory configured with help of '-device' option[s] (pcdimm,nvdimm,...) 2) SMBIOS 3.1.0 7.18.5 Memory Device — Extended Size PS: * tested on 8Tb host with RHEL6 guest, which seems to parse type 17 SMBIOS table entries correctly (according to 'dmidecode'). Signed-off-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240715122417.4059293-1-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/arm/virt.c | 1 + hw/core/machine.c | 6 ++++++ hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + hw/smbios/smbios.c | 11 ++++++----- include/hw/boards.h | 4 ++++ 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index b0c68d66a3..719e83e6a1 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3308,6 +3308,7 @@ DEFINE_VIRT_MACHINE_AS_LATEST(9, 1) static void virt_machine_9_0_options(MachineClass *mc) { virt_machine_9_1_options(mc); + mc->smbios_memory_device_size = 16 * GiB; compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); } DEFINE_VIRT_MACHINE(9, 0) diff --git a/hw/core/machine.c b/hw/core/machine.c index bc38cad7f2..ac30544e7f 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1004,6 +1004,12 @@ static void machine_class_init(ObjectClass *oc, void *data) /* Default 128 MB as guest ram size */ mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; + /* + * SMBIOS 3.1.0 7.18.5 Memory Device — Extended Size + * use max possible value that could be encoded into + * 'Extended Size' field (2047Tb). + */ + mc->smbios_memory_device_size = 2047 * TiB; /* numa node memory size aligned on 8MB by default. * On Linux, each node's border has to be 8MB aligned diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9445b07b4f..d9e69243b4 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -495,6 +495,7 @@ static void pc_i440fx_machine_9_0_options(MachineClass *m) pc_i440fx_machine_9_1_options(m); m->alias = NULL; m->is_default = false; + m->smbios_memory_device_size = 16 * GiB; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 71d3c6d122..9d108b194e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -374,6 +374,7 @@ static void pc_q35_machine_9_0_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_9_1_options(m); m->alias = NULL; + m->smbios_memory_device_size = 16 * GiB; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); pcmc->isa_bios_alias = false; diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 3b7703489d..a394514264 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -1093,6 +1093,7 @@ static bool smbios_get_tables_ep(MachineState *ms, Error **errp) { unsigned i, dimm_cnt, offset; + MachineClass *mc = MACHINE_GET_CLASS(ms); ERRP_GUARD(); assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 || @@ -1123,12 +1124,12 @@ static bool smbios_get_tables_ep(MachineState *ms, smbios_build_type_9_table(errp); smbios_build_type_11_table(); -#define MAX_DIMM_SZ (16 * GiB) -#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ - : ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1) +#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? mc->smbios_memory_device_size \ + : ((current_machine->ram_size - 1) % mc->smbios_memory_device_size) + 1) - dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / - MAX_DIMM_SZ; + dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, + mc->smbios_memory_device_size) / + mc->smbios_memory_device_size; /* * The offset determines if we need to keep additional space between diff --git a/include/hw/boards.h b/include/hw/boards.h index ef6f18f2c1..48ff6d8b93 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -237,6 +237,9 @@ typedef struct { * purposes only. * Applies only to default memory backend, i.e., explicit memory backend * wasn't used. + * @smbios_memory_device_size: + * Default size of memory device, + * SMBIOS 3.1.0 "7.18 Memory Device (Type 17)" */ struct MachineClass { /*< private >*/ @@ -304,6 +307,7 @@ struct MachineClass { const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine); int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx); ram_addr_t (*fixup_ram_size)(ram_addr_t size); + uint64_t smbios_memory_device_size; }; /** From 08c328682231b64878fc052a11091bea39577a6f Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:14:56 +0100 Subject: [PATCH 2370/2884] accel/kvm: Extract common KVM vCPU {creation,parking} code KVM vCPU creation is done once during the vCPU realization when Qemu vCPU thread is spawned. This is common to all the architectures as of now. Hot-unplug of vCPU results in destruction of the vCPU object in QOM but the corresponding KVM vCPU object in the Host KVM is not destroyed as KVM doesn't support vCPU removal. Therefore, its representative KVM vCPU object/context in Qemu is parked. Refactor architecture common logic so that some APIs could be reused by vCPU Hotplug code of some architectures likes ARM, Loongson etc. Update new/old APIs with trace events. New APIs qemu_{create,park,unpark}_vcpu() can be externally called. No functional change is intended here. Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Reviewed-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716111502.202344-2-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- accel/kvm/kvm-all.c | 95 ++++++++++++++++++++++++++++-------------- accel/kvm/kvm-cpus.h | 1 - accel/kvm/trace-events | 5 ++- include/sysemu/kvm.h | 25 +++++++++++ 4 files changed, 92 insertions(+), 34 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 64bf47a033..0f110cce3e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -340,14 +340,71 @@ err: return ret; } +void kvm_park_vcpu(CPUState *cpu) +{ + struct KVMParkedVcpu *vcpu; + + trace_kvm_park_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); + + vcpu = g_malloc0(sizeof(*vcpu)); + vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); + vcpu->kvm_fd = cpu->kvm_fd; + QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); +} + +int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) +{ + struct KVMParkedVcpu *cpu; + int kvm_fd = -ENOENT; + + QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { + if (cpu->vcpu_id == vcpu_id) { + QLIST_REMOVE(cpu, node); + kvm_fd = cpu->kvm_fd; + g_free(cpu); + } + } + + trace_kvm_unpark_vcpu(vcpu_id, kvm_fd > 0 ? "unparked" : "!found parked"); + + return kvm_fd; +} + +int kvm_create_vcpu(CPUState *cpu) +{ + unsigned long vcpu_id = kvm_arch_vcpu_id(cpu); + KVMState *s = kvm_state; + int kvm_fd; + + /* check if the KVM vCPU already exist but is parked */ + kvm_fd = kvm_unpark_vcpu(s, vcpu_id); + if (kvm_fd < 0) { + /* vCPU not parked: create a new KVM vCPU */ + kvm_fd = kvm_vm_ioctl(s, KVM_CREATE_VCPU, vcpu_id); + if (kvm_fd < 0) { + error_report("KVM_CREATE_VCPU IOCTL failed for vCPU %lu", vcpu_id); + return kvm_fd; + } + } + + cpu->kvm_fd = kvm_fd; + cpu->kvm_state = s; + cpu->vcpu_dirty = true; + cpu->dirty_pages = 0; + cpu->throttle_us_per_full = 0; + + trace_kvm_create_vcpu(cpu->cpu_index, vcpu_id, kvm_fd); + + return 0; +} + static int do_kvm_destroy_vcpu(CPUState *cpu) { KVMState *s = kvm_state; long mmap_size; - struct KVMParkedVcpu *vcpu = NULL; int ret = 0; - trace_kvm_destroy_vcpu(); + trace_kvm_destroy_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); ret = kvm_arch_destroy_vcpu(cpu); if (ret < 0) { @@ -373,10 +430,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) } } - vcpu = g_malloc0(sizeof(*vcpu)); - vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); - vcpu->kvm_fd = cpu->kvm_fd; - QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); + kvm_park_vcpu(cpu); err: return ret; } @@ -389,24 +443,6 @@ void kvm_destroy_vcpu(CPUState *cpu) } } -static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) -{ - struct KVMParkedVcpu *cpu; - - QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { - if (cpu->vcpu_id == vcpu_id) { - int kvm_fd; - - QLIST_REMOVE(cpu, node); - kvm_fd = cpu->kvm_fd; - g_free(cpu); - return kvm_fd; - } - } - - return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); -} - int kvm_init_vcpu(CPUState *cpu, Error **errp) { KVMState *s = kvm_state; @@ -415,19 +451,14 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); - ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu)); + ret = kvm_create_vcpu(cpu); if (ret < 0) { - error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)", + error_setg_errno(errp, -ret, + "kvm_init_vcpu: kvm_create_vcpu failed (%lu)", kvm_arch_vcpu_id(cpu)); goto err; } - cpu->kvm_fd = ret; - cpu->kvm_state = s; - cpu->vcpu_dirty = true; - cpu->dirty_pages = 0; - cpu->throttle_us_per_full = 0; - mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { ret = mmap_size; diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index ca40add32c..171b22fd29 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -22,5 +22,4 @@ bool kvm_supports_guest_debug(void); int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); void kvm_remove_all_breakpoints(CPUState *cpu); - #endif /* KVM_CPUS_H */ diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index 681ccb667d..37626c1ac5 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -9,6 +9,10 @@ kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s" kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s" kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" +kvm_create_vcpu(int cpu_index, unsigned long arch_cpu_id, int kvm_fd) "index: %d, id: %lu, kvm fd: %d" +kvm_destroy_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" +kvm_park_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" +kvm_unpark_vcpu(unsigned long arch_cpu_id, const char *msg) "id: %lu %s" kvm_irqchip_commit_routes(void) "" kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" @@ -25,7 +29,6 @@ kvm_dirty_ring_reaper(const char *s) "%s" kvm_dirty_ring_reap(uint64_t count, int64_t t) "reaped %"PRIu64" pages (took %"PRIi64" us)" kvm_dirty_ring_reaper_kick(const char *reason) "%s" kvm_dirty_ring_flush(int finished) "%d" -kvm_destroy_vcpu(void) "" kvm_failed_get_vcpu_mmap_size(void) "" kvm_cpu_exec(void) "" kvm_interrupt_exit_request(void) "" diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index c31d9c7356..c4a914b3d8 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -313,6 +313,31 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); */ bool kvm_device_supported(int vmfd, uint64_t type); +/** + * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. + * + * @returns: 0 when success, errno (<0) when failed. + */ +int kvm_create_vcpu(CPUState *cpu); + +/** + * kvm_park_vcpu - Park QEMU KVM vCPU context + * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. + * + * @returns: none + */ +void kvm_park_vcpu(CPUState *cpu); + +/** + * kvm_unpark_vcpu - unpark QEMU KVM vCPU context + * @s: KVM State + * @vcpu_id: Architecture vCPU ID of the parked vCPU + * + * @returns: KVM fd + */ +int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); + /* Arch specific hooks */ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; From 2f1a85daf3d8d06e4b204c29c1d47c76a2bc81e6 Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:14:57 +0100 Subject: [PATCH 2371/2884] hw/acpi: Move CPU ctrl-dev MMIO region len macro to common header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU ctrl-dev MMIO region length could be used in ACPI GED and various other architecture specific places. Move ACPI_CPU_HOTPLUG_REG_LEN macro to more appropriate common header file. Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716111502.202344-3-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/acpi/cpu.c | 1 - include/hw/acpi/cpu.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 2d81c1e790..cf5e9183e4 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -7,7 +7,6 @@ #include "trace.h" #include "sysemu/numa.h" -#define ACPI_CPU_HOTPLUG_REG_LEN 12 #define ACPI_CPU_SELECTOR_OFFSET_WR 0 #define ACPI_CPU_FLAGS_OFFSET_RW 4 #define ACPI_CPU_CMD_OFFSET_WR 5 diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index e6e1a9ef59..df87b15997 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -19,6 +19,8 @@ #include "hw/boards.h" #include "hw/hotplug.h" +#define ACPI_CPU_HOTPLUG_REG_LEN 12 + typedef struct AcpiCpuStatus { CPUState *cpu; uint64_t arch_id; From 06f1f4958be70f14f527d1f03d1e9a141650bbc5 Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:14:58 +0100 Subject: [PATCH 2372/2884] hw/acpi: Update ACPI GED framework to support vCPU Hotplug ACPI GED (as described in the ACPI 6.4 spec) uses an interrupt listed in the _CRS object of GED to intimate OSPM about an event. Later then demultiplexes the notified event by evaluating ACPI _EVT method to know the type of event. Use ACPI GED to also notify the guest kernel about any CPU hot(un)plug events. Note, GED interface is used by many hotplug events like memory hotplug, NVDIMM hotplug and non-hotplug events like system power down event. Each of these can be selected using a bit in the 32 bit GED IO interface. A bit has been reserved for the CPU hotplug event. ACPI CPU hotplug related initialization should only happen if ACPI_CPU_HOTPLUG support has been enabled for particular architecture. Add cpu_hotplug_hw_init() stub to avoid compilation break. Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com> Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com> Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Message-Id: <20240716111502.202344-4-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Igor Mammedov <imammedo@redhat.com> --- docs/specs/acpi_hw_reduced_hotplug.rst | 3 +- hw/acpi/acpi-cpu-hotplug-stub.c | 6 ++++ hw/acpi/generic_event_device.c | 47 ++++++++++++++++++++++++++ include/hw/acpi/generic_event_device.h | 4 +++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst index 0bd3f9399f..3acd6fcd8b 100644 --- a/docs/specs/acpi_hw_reduced_hotplug.rst +++ b/docs/specs/acpi_hw_reduced_hotplug.rst @@ -64,7 +64,8 @@ GED IO interface (4 byte access) 0: Memory hotplug event 1: System power down event 2: NVDIMM hotplug event - 3-31: Reserved + 3: CPU hotplug event + 4-31: Reserved **write_access:** diff --git a/hw/acpi/acpi-cpu-hotplug-stub.c b/hw/acpi/acpi-cpu-hotplug-stub.c index 3fc4b14c26..c6c61bb9cd 100644 --- a/hw/acpi/acpi-cpu-hotplug-stub.c +++ b/hw/acpi/acpi-cpu-hotplug-stub.c @@ -19,6 +19,12 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, return; } +void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, + CPUHotplugState *state, hwaddr base_addr) +{ + return; +} + void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list) { return; diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 2d6e91b124..4641933a0f 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -25,6 +25,7 @@ static const uint32_t ged_supported_events[] = { ACPI_GED_MEM_HOTPLUG_EVT, ACPI_GED_PWR_DOWN_EVT, ACPI_GED_NVDIMM_HOTPLUG_EVT, + ACPI_GED_CPU_HOTPLUG_EVT, }; /* @@ -234,6 +235,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } else { acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -248,6 +251,8 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) { acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -261,6 +266,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&s->memhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -272,6 +279,7 @@ static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) AcpiGedState *s = ACPI_GED(adev); acpi_memory_ospm_status(&s->memhp_state, list); + acpi_cpu_ospm_status(&s->cpuhp_state, list); } static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) @@ -286,6 +294,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) sel = ACPI_GED_PWR_DOWN_EVT; } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) { sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; + } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { + sel = ACPI_GED_CPU_HOTPLUG_EVT; } else { /* Unknown event. Return without generating interrupt. */ warn_report("GED: Unsupported event %d. No irq injected", ev); @@ -371,6 +381,42 @@ static const VMStateDescription vmstate_acpi_ged = { } }; +static void acpi_ged_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AcpiGedState *s = ACPI_GED(dev); + uint32_t ged_events; + int i; + + ged_events = ctpop32(s->ged_event_bitmap); + + for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) { + uint32_t event = s->ged_event_bitmap & ged_supported_events[i]; + + if (!event) { + continue; + } + + switch (event) { + case ACPI_GED_CPU_HOTPLUG_EVT: + /* initialize CPU Hotplug related regions */ + memory_region_init(&s->container_cpuhp, OBJECT(dev), + "cpuhp container", + ACPI_CPU_HOTPLUG_REG_LEN); + sysbus_init_mmio(sbd, &s->container_cpuhp); + cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), + &s->cpuhp_state, 0); + break; + } + ged_events--; + } + + if (ged_events) { + error_report("Unsupported events specified"); + abort(); + } +} + static void acpi_ged_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -411,6 +457,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) dc->desc = "ACPI Generic Event Device"; device_class_set_props(dc, acpi_ged_properties); dc->vmsd = &vmstate_acpi_ged; + dc->realize = acpi_ged_realize; hc->plug = acpi_ged_device_plug_cb; hc->unplug_request = acpi_ged_unplug_request_cb; diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index ba84ce0214..e091ac2108 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -62,6 +62,7 @@ #include "hw/sysbus.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/ghes.h" +#include "hw/acpi/cpu.h" #include "qom/object.h" #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -95,6 +96,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define ACPI_GED_MEM_HOTPLUG_EVT 0x1 #define ACPI_GED_PWR_DOWN_EVT 0x2 #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 +#define ACPI_GED_CPU_HOTPLUG_EVT 0x8 typedef struct GEDState { MemoryRegion evt; @@ -106,6 +108,8 @@ struct AcpiGedState { SysBusDevice parent_obj; MemHotplugState memhp_state; MemoryRegion container_memhp; + CPUHotplugState cpuhp_state; + MemoryRegion container_cpuhp; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; From 549c9a9dcbc1592ea79496f7b3ab234f366adeba Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:14:59 +0100 Subject: [PATCH 2373/2884] hw/acpi: Update GED _EVT method AML with CPU scan OSPM evaluates _EVT method to map the event. The CPU hotplug event eventually results in start of the CPU scan. Scan figures out the CPU and the kind of event(plug/unplug) and notifies it back to the guest. Update the GED AML _EVT method with the call to method \\_SB.CPUS.CSCN (via \\_SB.GED.CSCN) Architecture specific code [1] might initialize its CPUs AML code by calling common function build_cpus_aml() like below for ARM: build_cpus_aml(scope, ms, opts, xx_madt_cpu_entry, memmap[VIRT_CPUHP_ACPI].base, "\\_SB", "\\_SB.GED.CSCN", AML_SYSTEM_MEMORY); [1] https://lore.kernel.org/qemu-devel/20240613233639.202896-13-salil.mehta@huawei.com/ Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com> Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com> Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716111502.202344-5-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/acpi/generic_event_device.c | 3 +++ include/hw/acpi/generic_event_device.h | 1 + 2 files changed, 4 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 4641933a0f..15b4c3ebbf 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -108,6 +108,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD)); break; + case ACPI_GED_CPU_HOTPLUG_EVT: + aml_append(if_ctx, aml_call0(AML_GED_EVT_CPU_SCAN_METHOD)); + break; case ACPI_GED_PWR_DOWN_EVT: aml_append(if_ctx, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE), diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index e091ac2108..40af3550b5 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -87,6 +87,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define GED_DEVICE "GED" #define AML_GED_EVT_REG "EREG" #define AML_GED_EVT_SEL "ESEL" +#define AML_GED_EVT_CPU_SCAN_METHOD "\\_SB.GED.CSCN" /* * Platforms need to specify the GED event bitmap From efdb43b831efa5e0c8da3d062a1a37325b4a7708 Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:15:00 +0100 Subject: [PATCH 2374/2884] hw/acpi: Update CPUs AML with cpu-(ctrl)dev change CPUs Control device(\\_SB.PCI0) register interface for the x86 arch is IO port based and existing CPUs AML code assumes _CRS objects would evaluate to a system resource which describes IO Port address. But on ARM arch CPUs control device(\\_SB.PRES) register interface is memory-mapped hence _CRS object should evaluate to system resource which describes memory-mapped base address. Update build CPUs AML function to accept both IO/MEMORY region spaces and accordingly update the _CRS object. Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com> Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com> Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716111502.202344-6-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/acpi/cpu.c | 17 +++++++++++++---- hw/i386/acpi-build.c | 3 ++- include/hw/acpi/cpu.h | 5 +++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index cf5e9183e4..5cb60ca8bc 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -338,9 +338,10 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_FW_EJECT_EVENT "CEJF" void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, - build_madt_cpu_fn build_madt_cpu, hwaddr io_base, + build_madt_cpu_fn build_madt_cpu, hwaddr base_addr, const char *res_root, - const char *event_handler_method) + const char *event_handler_method, + AmlRegionSpace rs) { Aml *ifctx; Aml *field; @@ -364,14 +365,22 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_name_decl("_UID", aml_string("CPU Hotplug resources"))); aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); + assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY)); + crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, + if (rs == AML_SYSTEM_IO) { + aml_append(crs, aml_io(AML_DECODE16, base_addr, base_addr, 1, ACPI_CPU_HOTPLUG_REG_LEN)); + } else if (rs == AML_SYSTEM_MEMORY) { + aml_append(crs, aml_memory32_fixed(base_addr, + ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE)); + } + aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); /* declare CPU hotplug MMIO region with related access fields */ aml_append(cpu_ctrl_dev, - aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base), + aml_operation_region("PRST", rs, aml_int(base_addr), ACPI_CPU_HOTPLUG_REG_LEN)); field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index f4e366f64f..5d4bd2b710 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1536,7 +1536,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, .fw_unplugs_cpu = pm->smi_on_cpu_unplug, }; build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry, - pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); + pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02", + AML_SYSTEM_IO); } if (pcms->memhp_io_base && nr_mem) { diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index df87b15997..32654dc274 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -63,9 +63,10 @@ typedef void (*build_madt_cpu_fn)(int uid, const CPUArchIdList *apic_ids, GArray *entry, bool force_enabled); void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, - build_madt_cpu_fn build_madt_cpu, hwaddr io_base, + build_madt_cpu_fn build_madt_cpu, hwaddr base_addr, const char *res_root, - const char *event_handler_method); + const char *event_handler_method, + AmlRegionSpace rs); void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list); From 24bec42f3d6eae035d5df48c057157f83b260e17 Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:15:01 +0100 Subject: [PATCH 2375/2884] physmem: Add helper function to destroy CPU AddressSpace Virtual CPU Hot-unplug leads to unrealization of a CPU object. This also involves destruction of the CPU AddressSpace. Add common function to help destroy the CPU AddressSpace. Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Acked-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716111502.202344-7-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- include/exec/cpu-common.h | 8 ++++++++ include/hw/core/cpu.h | 1 + system/physmem.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 815342d043..240ee04369 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -129,6 +129,14 @@ size_t qemu_ram_pagesize_largest(void); */ void cpu_address_space_init(CPUState *cpu, int asidx, const char *prefix, MemoryRegion *mr); +/** + * cpu_address_space_destroy: + * @cpu: CPU for which address space needs to be destroyed + * @asidx: integer index of this address space + * + * Note that with KVM only one address space is supported. + */ +void cpu_address_space_destroy(CPUState *cpu, int asidx); void cpu_physical_memory_rw(hwaddr addr, void *buf, hwaddr len, bool is_write); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index d946161717..1c9c775df6 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -496,6 +496,7 @@ struct CPUState { QSIMPLEQ_HEAD(, qemu_work_item) work_list; struct CPUAddressSpace *cpu_ases; + int cpu_ases_count; int num_ases; AddressSpace *as; MemoryRegion *memory; diff --git a/system/physmem.c b/system/physmem.c index 9a3b3a7636..0e19186e1b 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -763,6 +763,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx, if (!cpu->cpu_ases) { cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases); + cpu->cpu_ases_count = cpu->num_ases; } newas = &cpu->cpu_ases[asidx]; @@ -776,6 +777,34 @@ void cpu_address_space_init(CPUState *cpu, int asidx, } } +void cpu_address_space_destroy(CPUState *cpu, int asidx) +{ + CPUAddressSpace *cpuas; + + assert(cpu->cpu_ases); + assert(asidx >= 0 && asidx < cpu->num_ases); + /* KVM cannot currently support multiple address spaces. */ + assert(asidx == 0 || !kvm_enabled()); + + cpuas = &cpu->cpu_ases[asidx]; + if (tcg_enabled()) { + memory_listener_unregister(&cpuas->tcg_as_listener); + } + + address_space_destroy(cpuas->as); + g_free_rcu(cpuas->as, rcu); + + if (asidx == 0) { + /* reset the convenience alias for address space 0 */ + cpu->as = NULL; + } + + if (--cpu->cpu_ases_count == 0) { + g_free(cpu->cpu_ases); + cpu->cpu_ases = NULL; + } +} + AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) { /* Return the AddressSpace corresponding to the specified index */ From 242da18082abc9310ecd528c528501acbcf718c7 Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Tue, 16 Jul 2024 12:15:02 +0100 Subject: [PATCH 2376/2884] gdbstub: Add helper function to unregister GDB register space Add common function to help unregister the GDB register space. This shall be done in context to the CPU unrealization. Note: These are common functions exported to arch specific code. For example, for ARM this code is being referred in associated arch specific patch-set: Link: https://lore.kernel.org/qemu-devel/20230926103654.34424-1-salil.mehta@huawei.com/ Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Tested-by: Xianglai Li <lixianglai@loongson.cn> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Shaoqin Huang <shahuang@redhat.com> Reviewed-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com> Tested-by: Zhao Liu <zhao1.liu@intel.com> Acked-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716111502.202344-8-salil.mehta@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- gdbstub/gdbstub.c | 13 +++++++++++++ hw/core/cpu-common.c | 5 ++++- include/exec/gdbstub.h | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index b9ad0a063e..5da17d6530 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -618,6 +618,19 @@ void gdb_register_coprocessor(CPUState *cpu, } } +void gdb_unregister_coprocessor_all(CPUState *cpu) +{ + /* + * Safe to nuke everything. GDBRegisterState::xml is static const char so + * it won't be freed + */ + g_array_free(cpu->gdb_regs, true); + + cpu->gdb_regs = NULL; + cpu->gdb_num_regs = 0; + cpu->gdb_num_g_regs = 0; +} + static void gdb_process_breakpoint_remove_all(GDBProcess *p) { CPUState *cpu = gdb_get_first_cpu_in_process(p); diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index d2e3e4570a..7982ecd39a 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -282,7 +282,10 @@ static void cpu_common_finalize(Object *obj) } #endif free_queued_cpu_work(cpu); - g_array_free(cpu->gdb_regs, TRUE); + /* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */ + if (cpu->gdb_regs) { + g_array_free(cpu->gdb_regs, TRUE); + } qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); qemu_cond_destroy(cpu->halt_cond); diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 1bd2c4ec2a..d73f424f56 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -40,6 +40,12 @@ void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, int g_pos); +/** + * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers + * @cpu - the CPU associated with registers + */ +void gdb_unregister_coprocessor_all(CPUState *cpu); + /** * gdbserver_start: start the gdb server * @port_or_device: connection spec for gdb From 935c3914184c4ebb1a4c545fc77fe2f0b24645c2 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Tue, 16 Jul 2024 11:45:03 +0200 Subject: [PATCH 2377/2884] Revert "virtio-iommu: Clear IOMMUDevice when VFIO device is unplugged" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1b889d6e39c32d709f1114699a014b381bcf1cb1. There are different problems with that tentative fix: - Some resources are left dangling (resv_regions, host_resv_ranges) and memory subregions are left attached to the root MR although freed as embedded in the sdev IOMMUDevice. Finally the sdev->as is not destroyed and associated listeners are left. - Even when fixing the above we observe a memory corruption associated with the deallocation of the IOMMUDevice. This can be observed when a VFIO device is hotplugged, hot-unplugged and a system reset is issued. At this stage we have not been able to identify the root cause (IOMMU MR or as structs beeing overwritten and used later on?). - Another issue is HostIOMMUDevice are indexed by non aliased BDF whereas the IOMMUDevice is indexed by aliased BDF - yes the current naming is really misleading -. Given the state of the code I don't think the virtio-iommu device works in non singleton group case though. So let's revert the patch for now. This means the IOMMU MR/as survive the hotunplug. This is what is done in the intel_iommu for instance. It does not sound very logical to keep those but currently there is no symetric function to pci_device_iommu_address_space(). probe_done issue will be handled in a subsequent patch. Also resv_regions and host_resv_regions will be deallocated separately. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-Id: <20240716094619.1713905-2-eric.auger@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-iommu.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 33ae61c4a6..4e34dacd6e 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -467,26 +467,6 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, return &sdev->as; } -static void virtio_iommu_device_clear(VirtIOIOMMU *s, PCIBus *bus, int devfn) -{ - IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); - IOMMUDevice *sdev; - - if (!sbus) { - return; - } - - sdev = sbus->pbdev[devfn]; - if (!sdev) { - return; - } - - g_list_free_full(sdev->resv_regions, g_free); - sdev->resv_regions = NULL; - g_free(sdev); - sbus->pbdev[devfn] = NULL; -} - static gboolean hiod_equal(gconstpointer v1, gconstpointer v2) { const struct hiod_key *key1 = v1; @@ -728,7 +708,6 @@ virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) } g_hash_table_remove(viommu->host_iommu_devices, &key); - virtio_iommu_device_clear(viommu, bus, devfn); } static const PCIIOMMUOps virtio_iommu_ops = { From 3745768918c2528f2b00b63bb3a764c458364f70 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Tue, 16 Jul 2024 11:45:04 +0200 Subject: [PATCH 2378/2884] virtio-iommu: Remove probe_done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have switched to PCIIOMMUOps to convey host IOMMU information, the host reserved regions are transmitted when the PCIe topology is built. This happens way before the virtio-iommu driver calls the probe request. So let's remove the probe_done flag that allowed to check the probe was not done before the IOMMU MR got enabled. Besides this probe_done flag had a flaw wrt migration since it was not saved/restored. The only case at risk is if 2 devices were plugged to a PCIe to PCI bridge and thus aliased. First of all we discovered in the past this case was not properly supported for neither SMMU nor virtio-iommu on guest kernel side: see [RFC] virtio-iommu: Take into account possible aliasing in virtio_iommu_mr() https://lore.kernel.org/all/20230116124709.793084-1-eric.auger@redhat.com/ If this were supported by the guest kernel, it is unclear what the call sequence would be from a virtio-iommu driver point of view. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-Id: <20240716094619.1713905-3-eric.auger@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-iommu.c | 3 --- include/hw/virtio/virtio-iommu.h | 1 - 2 files changed, 4 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 4e34dacd6e..2c54c0d976 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -555,8 +555,6 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, current_ranges = sdev->host_resv_ranges; - g_assert(!sdev->probe_done); - /* check that each new resv region is included in an existing one */ if (sdev->host_resv_ranges) { range_inverse_array(iova_ranges, @@ -956,7 +954,6 @@ static int virtio_iommu_probe(VirtIOIOMMU *s, } buf += count; free -= count; - sdev->probe_done = true; return VIRTIO_IOMMU_S_OK; } diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index bdb3da72d0..7db4210b16 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -43,7 +43,6 @@ typedef struct IOMMUDevice { MemoryRegion bypass_mr; /* The alias of shared memory MR */ GList *resv_regions; GList *host_resv_ranges; - bool probe_done; } IOMMUDevice; typedef struct IOMMUPciBus { From 62ac01d1de5096022e08355f8df47bf1191a3ed1 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Tue, 16 Jul 2024 11:45:05 +0200 Subject: [PATCH 2379/2884] virtio-iommu: Free [host_]resv_ranges on unset_iommu_devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are currently missing the deallocation of the [host_]resv_regions in case of hot unplug. Also to make things more simple let's rule out the case where multiple HostIOMMUDevices would be aliased and attached to the same IOMMUDevice. This allows to remove the handling of conflicting Host reserved regions. Anyway this is not properly supported at guest kernel level. On hotunplug the reserved regions are reset to the ones set by virtio-iommu property. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-Id: <20240716094619.1713905-4-eric.auger@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-iommu.c | 62 ++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 2c54c0d976..2de41ab412 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -538,8 +538,6 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, { IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); IOMMUDevice *sdev; - GList *current_ranges; - GList *l, *tmp, *new_ranges = NULL; int ret = -EINVAL; if (!sbus) { @@ -553,33 +551,10 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, return ret; } - current_ranges = sdev->host_resv_ranges; - - /* check that each new resv region is included in an existing one */ if (sdev->host_resv_ranges) { - range_inverse_array(iova_ranges, - &new_ranges, - 0, UINT64_MAX); - - for (tmp = new_ranges; tmp; tmp = tmp->next) { - Range *newr = (Range *)tmp->data; - bool included = false; - - for (l = current_ranges; l; l = l->next) { - Range * r = (Range *)l->data; - - if (range_contains_range(r, newr)) { - included = true; - break; - } - } - if (!included) { - goto error; - } - } - /* all new reserved ranges are included in existing ones */ - ret = 0; - goto out; + error_setg(errp, "%s virtio-iommu does not support aliased BDF", + __func__); + return ret; } range_inverse_array(iova_ranges, @@ -588,14 +563,31 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, rebuild_resv_regions(sdev); return 0; -error: - error_setg(errp, "%s Conflicting host reserved ranges set!", - __func__); -out: - g_list_free_full(new_ranges, g_free); - return ret; } +static void virtio_iommu_unset_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, + int devfn) +{ + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + IOMMUDevice *sdev; + + if (!sbus) { + return; + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + return; + } + + g_list_free_full(g_steal_pointer(&sdev->host_resv_ranges), g_free); + g_list_free_full(sdev->resv_regions, g_free); + sdev->host_resv_ranges = NULL; + sdev->resv_regions = NULL; + add_prop_resv_regions(sdev); +} + + static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask, Error **errp) { @@ -704,6 +696,8 @@ virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) if (!hiod) { return; } + virtio_iommu_unset_host_iova_ranges(viommu, hiod->aliased_bus, + hiod->aliased_devfn); g_hash_table_remove(viommu->host_iommu_devices, &key); } From 1993d634d57718d6eb107c973b7c8b80a6d91338 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Tue, 16 Jul 2024 11:45:06 +0200 Subject: [PATCH 2380/2884] virtio-iommu: Remove the end point on detach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently miss the removal of the endpoint in case of detach. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-Id: <20240716094619.1713905-5-eric.auger@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-iommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 2de41ab412..440dfa6e92 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -786,6 +786,7 @@ static int virtio_iommu_detach(VirtIOIOMMU *s, if (QLIST_EMPTY(&domain->endpoint_list)) { g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id)); } + g_tree_remove(s->endpoints, GUINT_TO_POINTER(ep_id)); return VIRTIO_IOMMU_S_OK; } From a6586419a13ec9698428d9585ef8540c594f978f Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Tue, 16 Jul 2024 11:45:07 +0200 Subject: [PATCH 2381/2884] hw/vfio/common: Add vfio_listener_region_del_iommu trace event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trace when VFIO gets notified about the deletion of an IOMMU MR. Also trace the name of the region in the add_iommu trace message. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-Id: <20240716094619.1713905-6-eric.auger@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/vfio/common.c | 3 ++- hw/vfio/trace-events | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 6d15b36e0b..cfc44a4569 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -599,7 +599,7 @@ static void vfio_listener_region_add(MemoryListener *listener, IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); int iommu_idx; - trace_vfio_listener_region_add_iommu(iova, end); + trace_vfio_listener_region_add_iommu(section->mr->name, iova, end); /* * FIXME: For VFIO iommu types which have KVM acceleration to * avoid bouncing all map/unmaps through qemu this way, this @@ -725,6 +725,7 @@ static void vfio_listener_region_del(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; + trace_vfio_listener_region_del_iommu(section->mr->name); QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { if (MEMORY_REGION(giommu->iommu_mr) == section->mr && giommu->n.start == section->offset_within_region) { diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index e16179b507..98bd4dccea 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -95,7 +95,8 @@ vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t d vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64 vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" -vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] 0x%"PRIx64" - 0x%"PRIx64 +vfio_listener_region_add_iommu(const char* name, uint64_t start, uint64_t end) "region_add [iommu] %s 0x%"PRIx64" - 0x%"PRIx64 +vfio_listener_region_del_iommu(const char *name) "region_del [iommu] %s" vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]" vfio_known_safe_misalignment(const char *name, uint64_t iova, uint64_t offset_within_region, uintptr_t page_size) "Region \"%s\" iova=0x%"PRIx64" offset_within_region=0x%"PRIx64" qemu_real_host_page_size=0x%"PRIxPTR vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA" From 6c027a9de3fae3b8a3a4868e17c7a28ba5372463 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Tue, 16 Jul 2024 11:45:08 +0200 Subject: [PATCH 2382/2884] virtio-iommu: Add trace point on virtio_iommu_detach_endpoint_from_domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a trace point on virtio_iommu_detach_endpoint_from_domain(). Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-Id: <20240716094619.1713905-7-eric.auger@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/trace-events | 1 + hw/virtio/virtio-iommu.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index b7c04f0856..04e36ae047 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -116,6 +116,7 @@ virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, u virtio_iommu_set_config(uint8_t bypass) "bypass=0x%x" virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" +virtio_iommu_detach_endpoint_from_domain(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d" virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 virtio_iommu_unmap_done(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 440dfa6e92..59ef4fb217 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -308,6 +308,7 @@ static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) if (!ep->domain) { return; } + trace_virtio_iommu_detach_endpoint_from_domain(domain->id, ep->id); g_tree_foreach(domain->mappings, virtio_iommu_notify_unmap_cb, ep->iommu_mr); QLIST_REMOVE(ep, next); From a54dd0cd6b9119c44d52547f51a529122f0ec1f1 Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:12:58 +0530 Subject: [PATCH 2383/2884] hw/riscv/virt-acpi-build.c: Add namespace devices for PLIC and APLIC As per the requirement ACPI_080 in the RISC-V Boot and Runtime Services (BRS) specification [1], PLIC and APLIC should be in namespace as well. So, add them using the defined HID. [1] - https://github.com/riscv-non-isa/riscv-brs/releases/download/v0.0.2/riscv-brs-spec.pdf (Chapter 6) Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Acked-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-2-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/riscv/virt-acpi-build.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 0925528160..5f5082a35b 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -141,6 +141,30 @@ static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s) } } +static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count, + uint64_t mmio_base, uint64_t mmio_size, + const char *hid) +{ + uint64_t plic_aplic_addr; + uint32_t gsi_base; + uint8_t socket; + + for (socket = 0; socket < socket_count; socket++) { + plic_aplic_addr = mmio_base + mmio_size * socket; + gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket; + Aml *dev = aml_device("IC%.02X", socket); + aml_append(dev, aml_name_decl("_HID", aml_string("%s", hid))); + aml_append(dev, aml_name_decl("_UID", aml_int(socket))); + aml_append(dev, aml_name_decl("_GSB", aml_int(gsi_base))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(plic_aplic_addr, mmio_size, + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + } +} + static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, uint32_t uart_irq) @@ -411,6 +435,14 @@ static void build_dsdt(GArray *table_data, socket_count = riscv_socket_count(ms); + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_PLIC].base, + memmap[VIRT_PLIC].size, "RSCV0001"); + } else { + acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_APLIC_S].base, + memmap[VIRT_APLIC_S].size, "RSCV0002"); + } + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ); if (socket_count == 1) { From faacd2e6b6a85a5eee2472e5a7f50bf69c4ad44a Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:12:59 +0530 Subject: [PATCH 2384/2884] hw/riscv/virt-acpi-build.c: Update the HID of RISC-V UART The requirement ACPI_060 in the RISC-V BRS specification [1], requires NS16550 compatible UART to have the HID RSCV0003. So, update the HID for the UART. [1] - https://github.com/riscv-non-isa/riscv-brs/releases/download/v0.0.2/riscv-brs-spec.pdf (Chapter 6) Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-3-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/riscv/virt-acpi-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 5f5082a35b..36d6a3a412 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -170,7 +170,7 @@ acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, uint32_t uart_irq) { Aml *dev = aml_device("COM0"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); + aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0003"))); aml_append(dev, aml_name_decl("_UID", aml_int(0))); Aml *crs = aml_resource_template(); From af09c25199a15925c34ae8c9635a79a81fd9b499 Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:00 +0530 Subject: [PATCH 2385/2884] tests/acpi: Allow DSDT acpi table changes for aarch64 so that CI tests don't fail when those ACPI tables are updated in the next patch. This is as per the documentation in bios-tables-tests.c. Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-4-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- tests/qtest/bios-tables-test-allowed-diff.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..9282ea0fb2 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,7 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT", +"tests/data/acpi/aarch64/virt/DSDT.memhp", +"tests/data/acpi/aarch64/virt/DSDT.topology", +"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", +"tests/data/acpi/aarch64/virt/DSDT.pxb", +"tests/data/acpi/x86/microvm/DSDT.pcie", From 35520bc702a8c3d7b434305ac403de437003d4bc Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:01 +0530 Subject: [PATCH 2386/2884] acpi/gpex: Create PCI link devices outside PCI root bridge Currently, PCI link devices (PNP0C0F) are always created within the scope of the PCI root bridge. However, RISC-V needs these link devices to be created outside to ensure the probing order in the OS. This matches the example given in the ACPI specification [1] as well. Hence, create these link devices directly under _SB instead of under the PCI root bridge. To keep these link device names unique for multiple PCI bridges, change the device name from GSIx to LXXY format where XX is the PCI bus number and Y is the INTx. GPEX is currently used by riscv, aarch64/virt and x86/microvm machines. So, this change will alter the DSDT for those systems. [1] - ACPI 5.1: 6.2.13.1 Example: Using _PRT to Describe PCI IRQ Routing Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-5-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci-host/gpex-acpi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index f69413ea2c..391fabb8a8 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -7,7 +7,8 @@ #include "hw/pci/pcie_host.h" #include "hw/acpi/cxl.h" -static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) +static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, + Aml *scope, uint8_t bus_num) { Aml *method, *crs; int i, slot_no; @@ -20,7 +21,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) Aml *pkg = aml_package(4); aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF)); aml_append(pkg, aml_int(i)); - aml_append(pkg, aml_name("GSI%d", gsi)); + aml_append(pkg, aml_name("L%.02X%X", bus_num, gsi)); aml_append(pkg, aml_int(0)); aml_append(rt_pkg, pkg); } @@ -30,7 +31,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) /* Create GSI link device */ for (i = 0; i < PCI_NUM_PINS; i++) { uint32_t irqs = irq + i; - Aml *dev_gsi = aml_device("GSI%d", i); + Aml *dev_gsi = aml_device("L%.02X%X", bus_num, i); aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F"))); aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i))); crs = aml_resource_template(); @@ -45,7 +46,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) aml_append(dev_gsi, aml_name_decl("_CRS", crs)); method = aml_method("_SRS", 1, AML_NOTSERIALIZED); aml_append(dev_gsi, method); - aml_append(dev, dev_gsi); + aml_append(scope, dev_gsi); } } @@ -174,7 +175,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); } - acpi_dsdt_add_pci_route_table(dev, cfg->irq); + acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, bus_num); /* * Resources defined for PXBs are composed of the following parts: @@ -205,7 +206,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - acpi_dsdt_add_pci_route_table(dev, cfg->irq); + acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, 0); method = aml_method("_CBA", 0, AML_NOTSERIALIZED); aml_append(method, aml_return(aml_int(cfg->ecam.base))); From 0af3dfa5c5590ae21d1f58a389968d4b90112fd7 Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:02 +0530 Subject: [PATCH 2387/2884] tests/acpi: update expected DSDT blob for aarch64 and microvm After PCI link devices are moved out of the scope of PCI root complex, the DSDT files of machines which use GPEX, will change. So, update the expected AML files with these changes for these machines. Mainly, there are 2 changes. 1) Since the link devices are created now directly under _SB for all PCI root bridges in the system, they should have unique names. So, instead of GSIx, named those devices as LXXY where L means link, XX will have PCI bus number and Y will have the INTx number (ex: L000 or L001). The _PRT entries will also be updated to reflect this name change. 2) PCI link devices are moved from the scope of each PCI root bridge to directly under _SB. Below is the sample iASL difference for one such link device. Scope (\_SB) { Name (_HID, "LNRO0005") // _HID: Hardware ID Name (_UID, 0x1F) // _UID: Unique ID Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x0A003E00, // Address Base 0x00000200, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x0000004F, } }) + Device (L000) + { + Name (_HID, "PNP0C0F" /* PCI Interrupt Link Device */) + Name (_UID, Zero) // _UID: Unique ID + Name (_PRS, ResourceTemplate () + { + Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) + { + 0x00000023, + } + }) + Name (_CRS, ResourceTemplate () + { + Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) + { + 0x00000023, + } + }) + Method (_SRS, 1, NotSerialized) // _SRS: Set Resource Settings + { + } + } + Device (PCI0) { Name (_HID, "PNP0A08" /* PCI Express Bus */) // _HID: Hardware ID Name (_CID, "PNP0A03" /* PCI Bus */) // _CID: Compatible ID Name (_SEG, Zero) // _SEG: PCI Segment Name (_BBN, Zero) // _BBN: BIOS Bus Number Name (_UID, Zero) // _UID: Unique ID Name (_STR, Unicode ("PCIe 0 Device")) // _STR: Description String Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_PRT, Package (0x80) // _PRT: PCI Routing Table { Package (0x04) { 0xFFFF, Zero, - GSI0, + L000, Zero }, ..... }) Device (GSI0) { Name (_HID, "PNP0C0F" /* PCI Interrupt Link Device */) Name (_UID, Zero) // _UID: Unique ID Name (_PRS, ResourceTemplate () { Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000023, } }) Name (_CRS, ResourceTemplate () { Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000023, } }) Method (_SRS, 1, NotSerialized) // _SRS: Set Resource Settings { } } } } Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Message-Id: <20240716144306.2432257-6-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- tests/data/acpi/aarch64/virt/DSDT | Bin 5196 -> 5196 bytes .../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5282 -> 5282 bytes tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6557 -> 6557 bytes tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7679 -> 7679 bytes tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5398 -> 5398 bytes tests/data/acpi/x86/microvm/DSDT.pcie | Bin 3023 -> 3023 bytes tests/qtest/bios-tables-test-allowed-diff.h | 6 ------ 7 files changed, 6 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT index c47503990715d389914fdf9c8bccb510761741ac..36d3e5d5a5e47359b6dcb3706f98b4f225677591 100644 GIT binary patch literal 5196 zcmZvg%WoT16o>EFlh__VVmr?J;S@^6GqU5nTG|qO>?AIBVmwKMluE9IK$L7EQ6ZHI zDP++?b~J)@kn)Ehv0}%LMb~Wj2iRfGojc?Fj&erwc+Si{-`sC}Y@eCBSKn(Dl#0yM zcHM1nq4sIVU7*EMO6hI=o_$%f9`(Fh@9=cmEtN{~-gdK|uDYEj1#2qa+i%v@;prWB zw;dkqwzo^Aayd8_@3~zsH|<QkNGJ_VsH{>y4lB#LLD4hHUEe%#Hx2ibMth&QOO)&F zRh=XyyH(2|a!$q|B8kZ$vuZ!=hEr<obayFr<?6J9%&U_-DiWN$gov^^J4fi#UPDCG z6`~v<YI+t+62gf{3>z3r$f$^nsKgisMg<wmBEyy#abPSXV?|^PNel-VE67+C8N(7I z1&mc>tci@6#K-_+4H@eqBQ7zrz*tAdZIO|X7<pja21Ysfa_|y!9Eni?#vNpcWy-%K zVX2P;<3nV~b()gqF$s*j$dKzaEitBnaSs`Con|D)EHLgPL$1>ii7^k1kB}kPX;xw^ z0Am9ga-HTR#v8z>B15jzyu`Q(j2bfJIvtf5w}9aSV@a;lg2Z?m7<FXGbvh<7-UUVj z8FHPDON{q{v55@1PA4SBA~3d)A=l}o#3%t{8yRw)PDzX<U^I~-*XgvxSO&%eWXN?o zBQaKi@i8*wI-Qjm>%eFsL$1>~iE#%QZD3U7I-QpocY(2k47pB=65~EFb^}J!ZVeiu z&Q7P~mNo~?M~8ZzjFa^HoHzaPM7_4JFx>OHo^^QYqk`!$6g44;x+$Q{z5(iio>lPe zVJO{<AnPrGW%hAa@GasjYD$oGOJJFO1J=gR9k&^LYMNCot<%+VshKoUMfZG-En3%V zJA>=!?R9fSXSSnU)l{FW1y!O~owmMFPt<4ht~A7N(>mR~(bm__Nj_;O`+cQ98)ddF z+AgbnO7C|f`tpQ9l!A)Nr|nd~Zz-Ka<AO+M(ZGy3=I7DSFw~jE_s3FCX~qVT&J2fA z!>G?lL;Xw&b!K>$sGk|mAgXj`I5cW9X{et`q0Xck`Dd~H%y0&_pBWCN1~ahqGbu#R zA?7*cXNI0bCNrEoh3GlVJgJ4J`GiBMAw^FL(KE(8W6U$gJt;)bIP;7%PikP%lS1@N zFi*PqFjvpNBnj?GA$mH@(_x;}@S-P$=$T}mN#>d4o)n^Iig~7(CpF0ENg;ZsnP-}L zrnx7D=$T=j8Rkh1HF{Ero+He2gn5o|PYThKZvIS-`&FD}p45P&Cxz&lW1czYnd6=m zqGz6Y=9woo?C41$dX6&BQRX?yJt;&_x~&G!0`sH>A3Z5V&oSmX#yrQkCxz%a&OFDN zC*2UBCxz%a!8|9J=LGkp5IyNu96TqPC*44xCxz%a#XP5&=M?v(5Iv`v=QQ)A8xHiO z5Itv@=M3|l;hq$t=PdJ_WuA0{f}Rwj=N$8#W1e%|lS1^IXP)!SlWu6xlS1??GS4FO zEOJi@jb|a-b8A&~NxupzyJ7xVsbkrWIZuB`s|fu-(bCr|>m}7oivBPCCEM@+r*F$> z=`Yd|D@FfmW}&}fTKaU<QfKc$j(_+>AJdB;U9I%~XDaUPcRDKW?sZ$Lu$k!XdaiwT zq}=*J`C>bE{$*~|$sV>}SN<)|Zv1lawEEk<zrHv-a%>u~MDKBWuN><@bM!_<qn_)P zQ`GB=?5p!9)^~KI?2j&AjK4Y$$C=$(eehTh*84Aye$ANu`XuGcqrwNhy4SWZZkXBp zx%S83-paS_pC<lkPyOz;FB0cpsmol{vh<1`bhYhn!-E#c^&fQUwy@%qu3aDhGoY(e z+_<_ldW(KR&2zI_?9rlrGPl*J_skOt^H{E|TPb=hm!<={I~uf|QR^L(deZ1pr$wJ= zJmJ`f-zRnGHoQh_p)l8LJlsnh>)`Q4_Wb!{M|UtUItSgFb8>h(r8msUOhf1ft=j+5 C#r7@$ literal 5196 zcmZvg%WoT16o>EFlh__VVmr>uc{qhq@vO#n^Jr;H?6H%$#EJ2w4N@w(5&}`OsYHcT zDx{D_3)#^~Yza~%{tYBn?AWnj&4zz~9p>D*Gs*8LXQYhh%-r+M{l>@f@oo97-K~;R zv7eed-lo6U{J7^W(q<{8^s#=;zie6$2Yz#~e^mBd*G&#KJFRTP>vbqtQOUvmPD||{ z-ST$2(Y1be({-!W@LF=<_5DKGnR<~@8kkafrM@3kmUV@qXOz3TzUQqQ?nmwJed5+A z*WYb8X-f7QmO&JpoI%7=(_v=Ae$bDmw6)#eq12^|+n#4$+}u&I@a8Tes^;z-p>KN$ z5mOh4YKUm+S=1zi6O$M=FlxxCi;TF$7zIWh88<|REisb7xPgo%kuf4M9AGRVV_9U3 zN{kFJmXWa{G7=Ia2aFYDtcr}J#K;3<6&W{0MoMB7fpHTU)$qf?OU!X3MhO_VkRg^S z|BytbJ_(HXks;S<Mw-VoFm5A5uG6f<C<EgTGUPhVNsKvQ+(m|5r(+Uh0T>@3L$1@j z#8?Ez8ZzWMEl7;lfYCsPT&G2eaTOR%WXN?oE-|hF!vjW5uG5mlcoP^cWXN?oAu-+t zMjIJ&olZ)OcY(2v47pCHB*t}MY#>9f(`kv}0%H>ya-Ggdj2bXH$dK!_EHQ2X;~p~P zI-Qjm%fR>$8FHP@NsLusY#~Fg(|L(;3m9Es)a5!|kQldtv5gG5PAd}QE--dN#`Z5g zuM^Irx7~9a?kY7O9<@g%s_QPMy+QkCbNjq4@pt=$iZj<!p6aMi)uM9guSG{+smJOQ zdQ}<{G#<VF?)cW1&S5car-wte;dfkRjx9Q_s$xc;cVZOOq99H|T@|x-W;nEz&Y^K( zq_b#X&Rh%fXlNAbOyUP)sY^0r!$@aFL#YMQcch^~CWSgPx=TFBjAjs5Ix`v?H<>gv z$fQtb(u{(;*g<AA13So!hEmHES_YXEqUQ+n90@Wb&k>UuO`byZ9A%!=l+t{nq10lb zCxz&lV4ex)nc$ujqGytMCYdL-WavpDdZw5swbSP51&<`fJt;&_hj}{8lUg|Rq!2yR z%rnhA)7+Cn^vp2N4D+Ox4?QVF&n)xIGS4jcq!2xG%rnP4sYOIj3ej_nd5$s9G44qr zdeYAsQ{zDu=b0z9l;}wzdKQ>xfq53VCxz%)WS&LlNi8UPQiz`8%yXQ1j&n~6(UX2C zf@g_&Qp<{-6r$$@^PFIw6Wo(R^qgd#lgyJ^T=b+6J*SxG6!V<oo)n@d{kR0rY34~S zF?v#no-@pIhI!6#PYThq%sk7?lUiu>q!2x4nddC?oaLSrqURj*oMWEUa-%1O=sC|k z=b7g`_oNU#7ntV)^Q0CXJt;)b3iGTm&kFaX(0J}2b!`1snxAj_OWAYR&%cO!v@DTx z(!o;1>%mt#eeYg6R~jAoecuXEVaLEwv`&Dis{+cLJ4fBqvkDtrhSKW=$a+IynRA>K zHoBZe>jucWCa}!8kX6bLyk<k4(f?4DU!$FQ9GeN}>33NbqkreL4fW1?NuRC^br->w z9}fT1Kg(zvUZ*QohI#<AQPV=(kgi(l_#MazE+6V^dJCe9oj(6eCH?!oo=SSV{Vi2m zPYrf_&ptj-UTd#<x|ulnyfE(M_q%`9|E<ie{kZqE@#~#GKRZ5fY#LCb*Cf5xpXfky z^g&&tp6i`c(fiZ<tCPppH*}>MjIN$ezB-A<nbUcF@rfR+&z~LqoHOV3O{!-HrS}Fc zziXdfF|+w&<@dk+rLVg`O#RcH`OWK|rcS<4XN9<B=^ZU}b?tuJXVs_|x^&Ie9rx1n z3El&GI+eAHN28DE`^K!BH4_i7>y^2Uc57ft+$_sgb>m6Pa#^|%@=cw`t+!0-VY^R# zDZQh~lw<FIo7SP%_S;*FrTIbo!EWk_4j!H6Po6(=bienax7Tkvhx<n}`T##qp1GI$ EABWcyJpcdz diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt index aee6ba017cd730948bfa93e91551eb10a6809293..e6154d0355f84fdcc51387b4db8f9ee63acae4e9 100644 GIT binary patch literal 5282 zcmZvg%WoT16o>EFlh__VVmr?J;S@^6GqU5PNkdy=kDbINPK+mMkW$H&5QvgZBr2p* zA%!doRANCRSO=6p1c?>9?m%MAhJSz^=G?h6&hI2=q}X%j-t*1<#>erQseAR^UkXY^ z{;ch|o8DaQ!?rs|o28V}`}#fm{f70R>(#mkCzEceREl>uoAq?nZ8s@cR`Kp$v#!io zSsJqrPNpbtK^k)+X0Rd-Mh{L_-JMo=#!hRqR4SK)Gse4a$IDGRW6u&wFEgRCN_{t| zEbAIYPiRFe>-+Ay0e{eF?a^u%DA(JqI!Q`)tCm6JoQy$465~N;)xO^eMYpy)JCwR~ zaoT?7#mOBN2~J)@L~uPgLZ9{uBBCx3<p5FBs|YR|M>r9QVFP0U8Nt=P#E43aL10vn zv1l&jC5A0A;=ou$#*)YwkQfdymXL8%WDH7-6fkZgV_9UxBt`}p%g9&}8F7h`1;z?8 zZi$S9#K;5V7BI@em*djs;z*1FFjkQvmMQ;|grz<TjCYYC*J(;x$2c%<BSWs!w8WSM z#vNqHb()bF)4;fk47pB+B*rW--b035r&)<H2aGjj$aR{N7_R`MiVV3<^Ah7aFlxw< z>vULR+yI6Pj0L$)3ligXVAPQz*XfAFcoP^6WXN?oDly&$#yT?OIvtZ3^T60ZhFquP z5~Bo+O=QS*Iw3I@fYC&TT&I%~V-XnlkRjLUl*G6RjQ5ct*XgvxSOLZsGUPg)kr=DM zXaS=l*XgXpxDAYLWXN?|lo)q`u@f+w_Ex{mYHznoZfU*We6*?O$v8>B&w16KPt+^N z3WHs*<5A<~qk`oy6g44;x-Ov1u|DdaURChzVJO{<AnP@OWsY%H@GasjYD$oGLtvR> zeb(BKZMPYGYMNCot<cqSsYNwWMfZG-4SKEBw)@x7+wJ6v&U9P3s;NF!bE-stb=vzv zJyIXhyV4N9O>1{{hqpd=j`K-7+3P9&*(kGb(SBLwQ~J0Q(U&I-q7+nAK5eIZJxl2v znioVmiza5wn4d>e!%$}u-=9l8rI{N<Iy0O~4Wm9IP4zP=)S2N~qJCz$f~eA&;nb+f zq^W);g*uZ~<e$a%Gs6|wer7n88qC1b&!iAN2bkx8pBZ`%n9OkT6r$%K^Q0D@))P*p zh7>(1M9&!Wj4{s`_oNU#<IFS8JgI?2PYTg9!93~a!(2W8k|ek%h3M%pPltI@!;792 zqGytMCYfiFds2v=Ddw4Cp41?tCxz&lW}a#0ndY7pqGyJAW|${6)aXeedJZwqA?7*6 zJt;&_y7@CT?pJY^c~S$8o)n^Ij(O&oXO4SPh@N@onP;BVu%jo1=sC<hhneRv_oNU# z>1G`~3(S)meDtIcJx7@52=g4_o)n_zDDxa;o^(Tio)n_z81o!so@3mTLiD6taqt{x zo^%6&o)n_z1oNC=o)g@YLiC(uo|DXzZaC1BLiC(so>R<oihELsp3}^8nt9R<3VKqA zo-@pIhI!6#PYTg<mU+%HPr9K&PYThq$UKY8v&cOuG@gZQ*R566IsI2q*){XSN*&8) z%z6B6T1Ds|6m32IVtqsPlA_;Bf6MlI|LNN@+IsUeVx{O;&202DOk0meE%p2@$nl4d z^_brL=;B2mf1={vUc0U0?rvvG71k5o9nZC&A1b%LUq0K6oqn1dcCrVpzbgM0r`LYk ze_Z|b&L5vVKXh!Gut4u|daoSmKy&m?MWdeT7pJIS&$2I0A6Z|~NZFrVJ{x^;8qPC^ zvwHAIPu9oJ4}Z>><N74!^TWbB-MZJZ&#sx(yj=eMZ*S?#)(>O<v?hLYTW5*W&(wJ? zYFYY)9(1+rPQ!y1$n_s|>9(-ql&)MK|1+SgQ(U{aG<uKzgPP}Nwb+As{k(6ZQSX{3 z6y{-HS+`R3urEymx;yH(ol)xzlX~3fP^U$oXguNA2VW<3=r+8@)?8tx+ql1*IMTtx gv+U{9hmP)Gp0)QoHRt%?WJ2$llbMFl4O+GT0UqoMJpcdz literal 5282 zcmZvg%WoT16o>EFlh__VVmr?J;S@^6vl`n?la{u`9y^IkoET5iAUTpNArK{-N>oUt zLJC<FsKkOsVjWN<{tYBn?AWnj&4zz~9p>D*Gs*9?XQYhh%)RHE`;Cv|<7xWM-JeTJ z#SR)f-lo6Q_^|6O(Pk;7^s#=;f4^ZJ4E)BRe?05CuA3Zewwu|y*KJd<qLPEXc2k+L ziZo{RkLM__DvdcYGguP`<Nf2C-cBdFVz0C5x|K?J#pJ;2`Gr|$>S;>pWu{bKsqaRW zWnH4^F|BBIecxL*;161zJz8y*a{b-9lcr>^ZW%<u$r(f}H63Qw?R&jQbZfh}L#fLb zmp#wCxVWQY;l*7<g!h9Z^krW{#MA|%5+WLU72$2;2qz{nY+zK85#HSw7;%X)3XB>u zu9+M80>hRVNnl(<#<Iv5kr)mzmXUE?WQ<CT3^1-EV?|^nBt{MxE67+C8A*we2gWKg zZitMO#3%ye1~4k&hvS9!#gP~#VBAE8Sf>0#5|#QSFy2LmT&Edn9n-+Lg$%h)vl3$# z7`Krj*J(~-%md>NGUPfPlNgJ@cn=wJo#rLR5-`?~A=ha^V!Q&3Ix^%sElP~bz-S;t zuG4XeaRnG2FsgE$mL$gOz-S^vuG0yL@g^`@$dK!FQewOfjCEwlbvh+6t^#8N8FHOY zOAHqno5+ysbVg!Sfzd{WT&J@V;~FsTB15jzIf-!{81ExPuG4vmu?mbWWXN^8ATe$N zqXUeZT&If?;}$Tsks;S<Sz_D)#!krC{-x`+!*z7GyKdcGrRKw<rprV%-Nm#vXy0gT zpSLCcZm&>w=DW&MZS}EQQZD^9>F5jfKz&57N<)IiqubjZ-}>A+DyHr9aHux?wyVss zMaLCY%;@t@jDl(u#3`t$V%E+KhnCVgG%t*F7ER2Vu^^A8Mxo9melVB1Br`XRbY?V_ zS|EKzni^zMs57Ih#DmOe1#zV_qp5L|NmGMN3Uwx}D7cCpWJW8ngUo0uwM?O9kVzqW zjxf)WAT#nDF`3cgDMZgv=1EN{ttXmFEf#uGh@J`NnP8p??nxnfCYfiFc~VP;o)n^I zig{8yZSG!hOH$mELiBW)r^7s{g+osY(KF3F)66r?Jt;)b4D-w|PipzllS1^&GS4jY z%yLf((KE+9bIg-kMD(N(J;#{m81o$Co)n@d{hTp19#nCjc~VP>o)n^Ifq52~XMuZC zh@M5}S!ABnf}$sd=sC_j$C>9i_oNU#>E|bSmY65CtmsK0dQLFU3FbM$Jt;)bN#;4p zJgLP+PYTg<ig`{k&nfOnA$rn}OYod#p41YfCxz%a!#ro0=M49x5Itv^=PdK278*S% zM9(?qImbNbxF?0^InO-jnJ2Z}=t&`ZE-=pp=DEN<DMZgj=DEl`sYORm3emI7Jj=|p z%snYIp8eytt=~%X^G$y#d+z%A7jc&!OXQq%@F?Qd;47WJcV9YJ8XfpOpPr2bsIX&T zD0)tSsLKM%oI6L|)vF2{T!zxm3y}4iz%u7JD{OQ*i=G=G>x#fK=R#H~KkyoLbw>X~ zRep_j=5}l*oJZegRgC_f(>ByQ>l^xXWvIIdw)}ATpZ-}!+wdxlSQ+X8%tlQMZ9^Kh z)U&rBCm24`V|ojsi=96ISS9_vZdWC}-QJcet)~V%zGpu>R9<txa=Mu~`Lr<Z<olh! zYX6q!*M8i8RR8t%pPxKCbZnYXrPn0A)*k9WbM!$?qn_xUQ`Y;_{PU9s)>kxA31(ML zC!e20^UUeI9(<@L>+@%aKjqAMeUZx9Vd<Sg)9=`)m&|JZSo!^LfBDPK4^#hiW`6TJ zr>T?A)L9{JS$angx;l2R<+Ezk54v>C)g1Sw`xCqeba%>Y7q><q(f5scZq`T~T-DF} zHd@VrDRJ|#uc8}Idf1nxfsk+NJZ`;VQjc0a>PzVzO{N^X|8-i2UdwN7EtM7qt$Vwv hhdQ`_nm>7R-_iZv)9!w+;T-jkXY>Jno;-6c^*?7DCOrTE diff --git a/tests/data/acpi/aarch64/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp index bae36cdd397473afe3923c52f030641a5ab19d5d..33f011d6b635035a04c0b39ce9b4e219f7ae74b7 100644 GIT binary patch delta 1923 zcmY+^OOD!55CzZ&HXna5*!=%npg}#0ltdy@kTOeV*+cK3^`vYd1Jpwn(aI{ak}QMV zS9XcIBm0WFxQ-3Joo=V}{Qdmnwsfxj;XQp$I+ys{;c)#o`Wl7fv8<1;<wf$}@=N^l z*QdAZ=j6p>;NYq(&hGaX&YxVBhx4?JaMTcLCsz&P98=EKb4>~@3>Nig(!;36(8Tx4 zY+IU`ZBl4@oozd*Vpgh%p^9~6IFhQa5V=s*(Wq`>RGNsPsh5eRsVl_N)X~_bzHv$w z*-ASk?MPLno%*7>J))I%N!pdBj&=#^@5ol#BWaJMJ(eO`X`iHhlJ@JSZjabNz1-e} zqzOqAmLghdO45|1DM{~$7Me*jJbzgsBWcD`L@Uinnv*nVDPkj)s|*M_aINtB*Sj)c zDWa7YBrQl<uoTfshtgE&kf46ZQba2qk#t1T5la!Rv?OUMO@)>Zs1JWzWGk&mT9LG3 zDWa8*NjfI!n4yTF>9LbMnF&cJB%QDn(MqQzosx9=kOnBCh0dI7p1-UxBk7E#h*ml$ z>71l<mLi6xLKg(}3z9BaifE;H!Q+ecowy|FlBI}Nx{{`@uzEl}?#ha#h*r8L>6)Z# zmLgi|hNK&UZW^d>-rC4kx+UqBq+6CETIr6YyN49e9YYbLkLHH%NxCQLo~4Mj^iNU! G?)?X92B*>h delta 1922 zcmY+^IhNW`5CzcRk{C1~K+JR2v2BibfCrLJy0hLxdkOy>Z=eHWV#hw-fY;&8;P*?v z4)r2<P*18Wfv(5vu{b@SUal+WlE1%;&w29n>++*`^WUD&A6!#r<?Z%>_a9wTh4)2* z@JU08aCFTet1#tUI~P-EVX$t$lO9Gjh9-_Lvxzh@8&ha{olTrnF)LNXP{lejoJv(! zNL{GvXxcV0Dow=D)YD|r)D<#m>S*Q)ADvP~w$ctsJ5p6?r#-1|k7%V`l6Ix3qg{gf z8?u%5NZKQ5kEMuK+9zqBr2V$3+apG(m&==zG$(1!Qba2)NLrAzAn6U!LQ83e`!6e$ zBrRErXr&cNE0R_$MT}B8%YdK*_Yi*ndQ}E2MYPhIq%}!vmLgi|P?`!I64Vb_ifE-H zl8#6^Vkx4Pj!8O}rb5Ses1JWzWGih*+K{wiDWa85NID_ugrSI`>9LdCnJG!9B%QJp z(Mo3|oso2Qmj)=Jh0dLe_g_|+lXT8fL@Qm8bV1SuOA$j;p-Y1LB}tboMYK{pym26X zCay@jVkx4PuBE9ftnW~dtFmS(qLpq)x*_R?rHEF#CFz!++X(gXsf}!<JCg25x??G# zmF`Kpze@q#GZZoXYA)!3qz957Sc=$6Uw@u69O81yzlRSl@A36tewO@LeECy1@4f#- CLc~`9 diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb index fbd78f44c4785d19759daea909fe6d6f9a6e6b01..c0fdc6e9c1396cc2259dc4bc665ba023adcf4c9b 100644 GIT binary patch literal 7679 zcmds+%Wqp%5XO%mN$l%KY{z*gbqb~8S=n*Zq@^vfubn0(PK<BbAf=Kk5QvgZBq~Iy z5Fv|FvZE2KgOoo6i4{9`ED%dJ`~&PTGjn`1GwPA3Dpt6X?YVc(H{W-ClKgzz2pU_x zS!1G)>zn>+Fjs%K?a#p@VvND}_?kQXJ#wcT)Vn(eQ+~NzPIOn-8kw5kUV~!MB)YfO z8fNF<ioe+k&vja><#MIcKbPqGoghEujXz2n6ik|&G2aZDNaQS34`A)m%C^5^b>C^W zZh`JRtPQqmUJ4fbwTP9-ds!<HO-}Sz*5bE2p{4bW&L%8f;IXGGdAMhy{o!RPQL)pt z1de@5B5GKo(wC^Ci+PuX7j<dGg~q(ps5&)bE{#E<QI#4Oof>hMMnY&@lo|_8jRBX2 zCo~qM#wDl5pi3hyG%iVvMW@D)OCu{Z7Ny3LQzPNh$O(-lsd3q<k#uPkgvMo|QR)A1 z=q=`XE{&qlxFR*2+f@IMgj;<~XuK&k-1{``@-ZPau1XE}KFzo^ri8{dso~zIS(nC) z(6}x&-1~IcrEx}Ryd^c<`!wg$m=hYyQp3GZ^Dd1Sghowjxc6znrEyMZ)TM@dpN_aR z&I=7+Xw19!Y0;(eve0Nq4fj4Bb!ogRG@4Swy-&wn8m|kD6{+Fgr{gY-3qs?D)Nt?9 z371A$Xsk*N_dcC;Y0L|aHL2m=r&BJCi$de3)Nt?9X_v+&q4BoVaPQL@m&TINSeF{^ zeLCyXxFR%KLZj;5r)OLmSB1ug)Nt?9l1t;d(Aey2ti{)tLi%WLw99^Z<rF^Jz&u$m zMV#{@^@HkD$BKj9pc6!P4oIqRM@a#MNU3uUDSPZx>L$AC|MVy+gb}Irl0(ZL(^~zX zBCQ2bBDKytwCu4{E&p-bU+RBqs8uU3!PW8sCT&%Ps$Spl_6aHRCD`iZC%v&f^O<?i z%$bJyz<g@%nfKw<;8KFKc4up3{S$A$kcy{zJ%gW!vF{`BzG4b#JnlvDqJ%{ZimEAO z;^|%wkkt0|D_jMQS$mGwL2FpzO3<fXAW7TXuW)793Ydr!p_Nubi7UYk^~Pvr=pbfr zW!M_CE1{KELWwKE5v`2V%FscaR)(#BmHm{o5=xnKK;;~um0`{SyD~HnrOY{~ass%6 zpRg4$ROW<I<{VNvhg8lXofAr#Gof-OR8GKFnG;HxGpTaIR}Opi=#nILPAFwgPv!Jf zPQYB56H1vgrE;cJ&XmpxrOcUDInyd9V6n^zrOcU8IWsC}M(2c5=FF;`S(OtoTIPgO z<{VZzhgHsDofAr#6Tb4<Jx;qgr*Z;z%bZZkoOzWquX5&fPAFy0g34J?IRVpUPAFy0 z5tVa9<s8vDp_Dm`DrZsU1gw`ip_DmCRnAeBb5!SqQsx{}Imc8^_!=N{LMd~OtDNI1 z=eW)ZrOXLmz{Te|p>o342ALB|nR8O*oK!g{bxtT{&MB31O67#F88Ro7GUv3)IjwR| z>zq)^oHHutjLHdLOJq(cWlp%Q0#_*fjX$e$&gz^{%A99Z&NC_}e2tMgp_DmGDrZUM zEa{w3+ME_kM!mx0jETapBeM_x4BV-CU=qQtcH1QUt<JhBt|YsgfgeBIHGX5ea=bcp z^ih7q%k8v&tNvA*S^j?ee(lF=zkYbQ>&2mA9$pjhTHV6}b?{9Ur5@swSHkDx+>@hw zkuTs(g}N)pV^5C4K6^NaC-<-!kDu)RkhRBgNabX=_(r!8wBpBSt(#vKfBqv__`LP) z_@AxGpZwNw^5|o8l8;3q_yo6_fU{2TVJV;io{&8AY{&+8x$K<*CG}j$2KT%iQqPBM z<??@oY|G!Z{k4BVwxD?ft{s3IJmalEi)|%urfqz)2B<b?%FhgM@Y$Tg8(iFw*P`&D z33!8i0^X>P{;Wh`!&4*uv5(+}r`E8H;b$-4je2QilvajNL-0new36VBry#ts1B5qj zC3vG&T1oK6QxM+R0m2)%61=f_f;XOm@Wu`h-nf<EjdK#b@f3tNc7X84tpsmup5TqA zAiS{ygg0&_cw_SfZ#)IzjU6DoaVx<an<seVDF|=u0O5^W3EtQ|!5dFOcw+|$Z`?}o z#^wp$cnZQBJ3x5jR)RM+Pw>W55Z>4U!W*{|ys>$LH=cs<#tsnPxRv0I%@e%w6ofZ+ zfbhnx1aEAf;Ekssys-m>H*O_(WAg-WJO$y69U#1ME5RF^CwSv22yg5F;f-4f-q<|B z8&5%aV+ROt+)D7q<_X?-3c?#ZKzQR;f;To#@WxXR-q-=c8@Cd?v3Y_wo`Uek4iMhB zmEeud6TI;hgg178@W!nKZ)~36ji(^Iu>*uRZY6kQ^8{}^1>ubyAiQxa!5f<=c;hJu zZ|nf!jav!c*gU}-PeFKN2MBN6O7O<!3Ep@L!W%n4c;i-rH#Sf3##0d9*a5;Dw-UUu zd4e~dg7C%;5Z<_z;El}_yzvx-H+F#V#;pWzY@Xnary#ts1B5qjC3s`=1aCN{nCtrW znmK{r3MRJ<PnPCZhrIhC&KdJ19N#xz!+uG_%?^6xdcD8#PC7h#7vM}J4S&__1MZ=~ zqX%ar=I}MKhEDI}IebvXPk8))35>0Hrx^$Y^5Lr6i(K`}PhB7S4B+aNmif})JNON1 z|Ese;bms#8XX=e+qicE3{!eNJ?|uP3W#B|#-`+ipMP9K>_nRFcE%=j@NP6*|uToh0 r&7iqHSDfuOZ*L{{u(*4iJ9>E6LxOqK-tN@B{hfnJd?Vq~x3kPYFGd+# literal 7679 zcmeI1%WoT16o;=LiS6+tw&OgUms2Pe&&rRcNlRN|kDbINPK+mQkW$GN2t>&y5*4CU z2$MyD>}Vu5Y=RVtKLaFI?AWnDEZOi6uw(e$xiif<%Gt6(o=EnbnR~vOZ*mf!d);)J zJMO$v;U62@_J*_Ac)M#aVhbsy^uB)29{mxz*LNEIy~7#1TrS7^8|`MgZg<-VmQ}oe zr`=S0hu7`xPH?W**(jGQ75`kkZ}*(sj5YNnp)@d|vPykDszRX)h#nz#Wo_49Gu*dY zojc%Gk?ZW#tt67|ddMJhR>mO0iD^Hx9=+QOZfSF?w~f@Lv&%lqJiEB1!v4j}6j3o( z=Lmh-bBM4yLsWc3L*GSJLRevm5haW&Wz<AQL}H8*MvXGAh>WPjh!e&Y%2*N^BND?R zj3vsrDl$eTMv5@5QpU2#h)IkLVJuU|ipYpdj4WZSP{uWpk&qa9!nj5l75|TemzZTq zi~?a?rwp-7`5#G8>XU@=I%UXpnv(8gnlNrqhFqs<i7`VMHz`A|(~QKJBaB;=A=l}c z#8@DVHz-4{)2zf;B#c$ckn1!jF`grgI%UXpnwJ<C38O(7a-EJ#j7x-J6Gm08(}Ki! zkuaK+A=l}I#CVx7T9hHz>7>MXl`z&QL$1>)iE)`Q)+s}-(`kuOCX5Zrkn6N4F{*^o zrVP1GXC%fI!njQta-GgfjH`t4CS}NVIwvt!2xF5n<T{;~7}p7-Ll`x=P8TG`4Z_%> z47pBA65|$OZ2OG*cU^nMzmM)#w_Go;p!u-T9+{|ysL|N6240h--<+LZu4qjjs87^8 zYEd=Sd+KBLK)s81rBl6jqubjV-~7lL<de~)>#B99T~=m4g#C)jr}S|vjG!6>5d<}r zPe)U(izdsA^CO*wff;ko%frwh)S2LWW6>L#v3{g8gP~}A^c6AG%S5O%aW~%Bh?g1M zK}6}yU}(f-VyKsiP-o(fyv(SV8QekC%M6C1QSvRlOoY^Pgn5p5nStks$qa5DA@v+( zo@hmJKfzEmR@4(A^^7sk81syAPlVJn&OGDH6O9=4L`Xdo%o9ztd3xR>NpMeu)YD>~ z7V|{oMm-Ty&m{9qGS4LUL`XeT%rnJ2(dbc6gw!+5Jk!iG%{>uP&kXa-Fi$jw)Dt1~ z9Alni%yW!;BBY*}#F!fQsyNF$(MVEHgw!*~Jaf!5$2}2J&ph+YGfy<0)Dt1~9A}>6 z%yXQ3BBY)L=2>8#XjG{uLh3oeJSUjv1ouQpJtvvxB=baLOFa=%&nf0P#XP6DCqn9p zDJA)RPBTw5!qgKX^(->aBJ(VAPlVKShI!5~Pc+Wd6Cw4SWuCLlbC!D|q@HukbB=kU z(WagVsV5c+@PvZ@`18zjo_ivso(s%#fq9}ar=AF@XNh^1m}iN5A~c?RhwZ5TRhmhg z{wTJ~Yxs243l;Hx7mE=YE9osjJnwyw<8$}ivBGHI={X@E<@X5;h1CIyx+tK`v1h2; z`mX%8mZ9`20cE`)u*@;e^4nX^!fF9!T@qO4n9nly6ITAKv<l;&w;JF_VWsAgiaU3@ zT@|-?dYh`SmgsLgcJydp+0EU`$wut>gWR~4-Rt~b`@1x^`t9z+`cF51d;e(Pief+& z?{U1>4s@V7dZ(sQkM)aH(yu4kr^gRMpW#f!8(le>e0m&=Gl#SK<bfWnkDu;;pE1Yv zMJlKJh1dE`rxQK7VD9GE<zN1EmOkx#Gxb-e__N(PNgRKuPIHk^NWWmIY3qlsO)Eoc z*__ro>I9A3vzpOuTgjU1DqFSDsx7MWuz4FcZ+<^JY~J+5V%WU7?yz~&l`w4HTzA;K z=}H(jZ+@TopEYlVY~OCw)hQ;xD!Ymo)6H$ftcO86r_|>-K2Tp`YLvp#khf)B_aD8I zj?KM{Gocg~aLk4kCu}axhSbq3B*!~F(C75#MQ1Pi`2VtD>_mGlhfO8)cawU;P_xSC zo{#rCz|$$Mo;@19hs6T(Rc9l1@3Q_fb-mT>oB4_PCbgp13Nb-R<Al#Qlaxs4C6hX6 z_0X51L60Y_=-wAe9oj9YwYgZB@3-#mBo1_N|0H|-_`anV>z;IXdkt%_cUaUrbTVX{ HYpH($%hpa? diff --git a/tests/data/acpi/aarch64/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology index 501314c91be01d927fd125e0c72e919fdd85592e..029d03eecc4efddc001e5377e85ac8e831294362 100644 GIT binary patch literal 5398 zcmZvg%WoT16o>EFlh__VVmr?J;S@^6GqU5nTG|qO>@+TMVmwKMluE9IK$L7EQ6ZHI zDb1olb~J)@kn)Ehv0}%LMb~Wj2iRfGojc?Fj(bMRc+T8=zL{_4*f}$Guf8`vrc`94 zw(D+r3$;(%?gA~AQc7>@&+PME>tWZcbq`Oc-BPI(?`}2g>8jgqQn04t-Th`=nX$4o zW*wf+P~MU>=0wb3MI4Np!DVsKHiIkT;E)+y6$gjS;F>rXJ3PJR?zTe9PHU@FDwhMx zc-QTCxoKzOMMCLIlTcZuejHYob&aB@bQRV&58O=y{-n{`rz^BXx!zvYNm8;~wG1NX zWDFvbm<%$j_JdA%akqCmyOg?eb=ZF9)xjMV2@YODL~su{LLc@uL_}R7$^oLL&my>0 z9N|PHh7F7*WCV9~fDx4#!@#H@Be?Mc3|nHvfw7E?pgsl|LlVOQ#tJfmG8$kEON<mS zR*?}@+W;dbF*3kdL&mx(tN}(`Vq}4_j*OckBOx*Jz_<yFa`5FCI1fi+6o7FH8Dg38 zFG*PH<G}b38FHPbr1O{r#%*NCb()qK)4;fc47pA-5@Qw^cab62>4?Ob2gXOpkn1!n zF&2Qafeg7$a}whXU{sMI*J)m2Tn9!C8FHPDN{k!8aDlNT*J(jwybX*xGUPfPlNj#; zqk#;$PRAw2`@q;lhFqr;5@Qh<_mCmi>7>Lc0b>gpa-B{|j3r<+ks;UVw8U5j#(iYS zbvh$4R)O&`GUPg)l^E;5*hYq2r*jhH7BE`CsK|9XFEMTdV+R>>ofaj=U1016jHbQa zZ|&MU?UGyC>^C26>UlCw((iNL^yd@xwS9%*uGjIX+4E7s=`a*EA&9yzpv=BL>b^d! z;M>Dcx*0*%TLR1M<E-FY#97poAnS&}GW+_hji1|YGq`J-RV}U4)pDsVHc>_Qe2sgw zuGMz>*U{VS<ciL0Te+&KK35B>M1OVK`bIrgpV6n%5Wh`pclJiNzjjXYNjur=DSdC0 z*|uoAtnw+n--+nU69!QVDk`70Q@x(0bPml6BArDOGiJ=sqp4x2Gl}odrJmBv4I-Tx zPNjxXACac|nH1{G@F-C~GdzQ+(wX7ZsL7<MekO%Flg`LLitT5HXJGr8;Z$lc14}=X zLi8MBo<n|S=s9FE!;_~FJ%^bmweWO4;Z$l!(UU^-j4{s`^Nev%3ehvpJmbuh8d&tC z5IqyjlO8_I)$=b&f_qYko(}VLm?t&7=t&`ZCYfiFc_z6hh3J`Lo+;)@4KjLCh@NTY znP#48?nxnfW|(J&c~V1-o)n_z2=g3ao+I3oLiD7EKU3p=6=#_zHQ?w;A$sPRXO4O1 zxF?0^nP;AP=1C1ZdQym<qs()Zd5&^V3emH`JPXW|8hrGm5IyPD0$ri-7h;Tgj&V;4 z(Q}-6jx$esAV5zF(Q|@%PB702?nxnf(xW)s&q?M<4-n`{A$m?R&nf0P#XTuR&uQj4 z%{=LW13f83&l%=9!#roWCxz%a%RFb9Cq1B`Cxz%a$2{kl=N$K>5IyIa=REVI2O9LG z5Iu{`v&cM)+>=7%S;%(XT2)=ruY$^Mn18I)v24YhCqJZBgnpoC>FJgAJ=IH!{$Kh_ zw%7YlKbFzbTci;yMgP^zLjQ(o>CvdA&fbF@fA~a?>BWz(R(k(a75DbrZ54O-I@_wS zndt6%u6=f--1<TJVk>t3Wp32T9=2Xp{w>aK{BrQ5`rDnqzBoH_Y?`n{pK<!E9P2=H z^hQOap6QiS)a#4v%k#(9cQjJ=XO}O=U!I5a%<ilnJl2!-{>!6ZGiJX&Ncr-p@Iklk zwd{*)=4}34`{QqK<=fUz6aTcPes^0JiSw`2WiD!2dPOg~T6U-5K?~&iFS>MFSaC|P zT_67*(A6n!TwNNyMZcity;&{xa8bWwyw|9A@x5GGw^H<iF--%yJL<QcQR^L(deZ1n zr$u)(o^b5L=Sdy94X?4iP?+mB9_%HKb@1pSd;aW^qdS-v?SoFuIXOI?(i`SrrXh5L GR_%Y*aw0td literal 5398 zcmZvg%WoT16o>EFlh__VVmr?J;S@^6vl`n?la>}@kDbINPK+mQkX*@?5QvgZB`Ty+ zA*ERq=#EBW9i&M78%V6!v17rS4gUZ;%(-)ClHYO9NEy$Wd(SuX%^YWrr|CEMr>B&P zoiz5mZGWZlN!MGU#ZpS?ZT*>lwrAZR_>DpTc;0heH#yjDH?wuG+ooVmB?ougO=ZR^ z(wNmhUZA|HH0H$2U`-s1o55@1plt?M#lbN%cwHPEH-l^9V4{C~)7$Grmc7ol>sBhE zWpd#4{KC95^E{>WrAev0Qa_9<%eq9-6S@lPn+M*e0e{@;+@&j2rCfi%?xZQ%t6K(9 zaB>C_OU;Ivb^Bf~y0|;Ly*)}@y*TW7=EcDs6$=mUA|kv89H9^U3L>U15S0+o&}R|e zDvoes62k^Y6&c|j9bv>J#yBu)$Ov!z2*Z{bNnl(<Mpz#sj4_Gf0Am#yVHu4u#wA7u z7}t>zR@(?8Au)2mSVP9TDXbAjQexzRv5t%zA|oX+iom!5j7s?B7 |Vw8Y!6B%Ne z@-InL>eIk@9~p9;W~B3&1;#C8$aR{P81ulmjSRU?a}r|_7#|=*uG0yLu?&n4ks;S< zUSg~OV*?p-ofag<Yrv=@L$1@J#JCKM1~TM2os<|?fZ+k7D%WXAV!R2ACNktYost-D z1EYluxlX4g#=F4SM21|aGZNz}Ft(5(*XgXpaDlOn47pC{Bt{h&ZDh!GIxjJ<0pkub z<T_oD7}tUE5i;aDU6dH>z}P{CT&GJC<0ddVz^KV}x-2nn0b>^#a-EhX#s|RI3mLn= zbiH<X9^KupTX)x~`S7UGGf_=<F|93HHyXR=ZHd3%E0mqZuJTk{eWq5FOMgw;`dU3y zpVFt&kf8DC_Vy=tzH*L=X*)d}sx80mDzk0Tc10C4dcPB+pc(~n3TmpDwKKz^rF0I> z3nQIH6LV%P$fK!Is56Nl%%v{L%nc)*8BL`YNFR}=2ALG<%;+fbATv6HxYC)?)VRr{ zsX-=%I+M;QIEo!)MrU9LnbA~gnL^7TlS1?yW1eF{X5=|$GNY5H5Ix74CpD#XKG9Ta zvCxx3^h_|%1oKRAPYTg9$vl(HlUg$Lq!2w*%#+$_bM=BtlH#5eqNl?=9p*_b9C}iS zo@wTpW}a#8Ng;Y>m}iE0Qp<;)6ryLAd1jesmU~i&o;l{3W1iF^q9=vuIl(+9nCArd zq!2yn=ZvZGpo;U%lUhpjq!2v|%(K8e3*3`J^ei&ZBJ-pc6g??K&q?Mv$vh{yCxz%) zVxA@DNi8dSQiz`PW0|f{^dDl1c}{Up3ej_#c}_D=YH`t%LiC(ro-@pIhI>+op7i4q z?&mD?q?Q;xDMZgX<~hea=eQ?@=sC|k=b0z9(CA4adM+@}1?IWHJt;)bMdrE4JgMbI zPYTg<iFqzD&n50jA$l$|&t>LGEjoHqh@NHUS!SMP?n$BX>>syneJjn+H~mod+|Ba` zahG08<eYTyD&qCvkxtLuSN4_02Y%0|_b~w~>=+n|-V-3|vVb!C&QW*tS%nQQL+SSg z$a+IynSGoUHoBZe?+uW3MPQkIA*+-hc#XO`qyM2Qzd<W=Ikpqd<L|R7M*q%f8S0hw z9eukp)LjHiemMM3|16_rc$G%14D|qJp{9kFA&pw<#XFD_3?Jz+y#&$4O7DN7lK$Op zS0%mu-i|75rUrYyXTLa9Uh|-Gx}7-rqA=;?`<=gP|CSdwemZzu|Mm8tpT9VCY?@G| z&m?`;9_c`H^hQmip6ZoT*6Y*!%ae!Jw=_}-W>-$9U!Fws%<jA%e55Dq{bz?i=gfY6 zkjmL%>AgYI@7Sl8%-Q_0_WR%d>NlMqXa4ET{pNK}Qzu`lvqIdm^om||b?jctXVs`* zbm^L_IqoahC%6Z6b;=tTmqu^V^Txb4Yb5Sp)$bU$TFrqear1()q8m?o!I-6ikZ<Zd zZoOqvk6JzIOX-d#Q;yw#me!%y@>@GArKLgZ-hS$l4j!E5Po6$-bien!d(dk*NB!eD My@B5+&m2qr5A>`*Jpcdz diff --git a/tests/data/acpi/x86/microvm/DSDT.pcie b/tests/data/acpi/x86/microvm/DSDT.pcie index 765f14ef3d1e54d3cadccbf0a880f8adb73b3f1f..8eacd21d6ecdf9a3cd3e4f03cf1b40748dcbf53e 100644 GIT binary patch literal 3023 zcmZvezi%T&6vyA%>v%o>k+tK*aeg^mi$LpG(a|8Cy>=2KJ6XKW1>uwSBn09}6A6R_ zC!~<#fOMQCY9r+jprt#Bii+zfD7c1(hLWq!eBVwsnkTZ7XXiWf-rM(iv$LLV*d70t zCrb4?NB%*$-FYwYw{clS#C^WI{hy<-6HPAO+&l62oamt6Exy|u9($g*LrxTq+bZuw z{wORLmD;!5jGM6vU(n=Y$3Gf2WBWq;dB$?0<9-({J1lAkR~7fIm+6;ja_+dqK7)n* zUN_PYY5PIn^O`L@1fE4qpvTWCsr+{fl(urn4o>I2QE#6LM6*}Qo_w8W>G#6kV7s~* z_3n>zXDlAh-09<oDn%;wBsdv$)cNG1&K>=*;4tuf&)3gFA|3nw$h)iO28W{~(C+F< zl|>eQ8zh{|nyNj?kwvASUQvn&d0AFX=XKGy2XHgv?_I}tqTq(ZvXDABOtxgKx)6n< z(3<E_x|l_s>UAijVTLN+5=xKVgnEaw+PNhwm6EYax5Uz8GFI!hSZNt+@s?P6>?T%c z8u)#qT5azwt_fD2f!iLx2O$>Pi8`G_w0I|YWIQT2)J8x)_31;}CJ(<ZdY{uH`T%!= zkzTG~G+r8fs?IBUJ3pTjuMO$Gg?^JN1s+!^E>9xT5G|?{?ZSMH&7keB6??(3q~~Hk zm?n}v5kI!Yi=l0=6?-P8{6cEe*b|aHkxguyjy;nM(!`#K8k?rICrlH`p2#Tnv}4aC z13UIiOl|F%BoAqFW&~#@_DoDO+A~QW(&StaoY)G;Cox@+oRB7GR&ZtoXI64Tnw*Z{ zbOfg(IU!9>d_IgyV-xG@#l_4?PDqne2~H(AmE?ppIq|L4HHp_KFF5m(6Vl|wM*^P7 zdKLs{L2^QxoY?;cXHjq#B`2iGiQTSi5@%ZyoF&N#X>ww38=Q-Rb5U|anw+lSbOom? zIU!BXvfwNW&a&i$G&w7Rvm!Vvk`vP8ToRm1f^$i7LYkaa!C4iYRmllyaxM$bWx=^D zIU!BX6~Vb8I9DVmq{)fDG-Ew$g0m($Ax+Lz!MQ3pS0yK;$ypbib-`JeoRB8xn&4a$ zoNJO3(&Sthoa=&fU2;O2oEw63LvU_LPDqn;Q*dqy&P~Y)X>x7}&Mm>YB{?BY&W7M@ z2+oG&gw&kXLgaVabj8P&7y7hvm{IS4Tci{Z5zV==zQMav;DaMy&*t;_pSnxim#-T$ zdKxV(@NX=ydyqI0g~vfajz1m^sJfSnjzZtQJSD$-(wrS+ryrM>lsg&z(fYTsaqowd z&)U!4{{5rNQ)R<o2X_be))@<q!<`m~dd!V#a65CKP9IrcVWt^dH)ktPr-_{&c6suQ zjd}d)^v9AO=Z7?}POEQ3-Ee5nUenn;zx(T7;qI5i?`wY#>%aKJS#J6XU6s?8#SM;? wLwnQ<jYEzfpP~66*;3yB>hprDg6jOkcjLl0lI!ET5BLPM8&$Hl!oN2C2j(D$9{>OV literal 3023 zcmZveJ!~UI6vyA%>v%o>$oeDSAI{O94y|oPM}ri5?IcEVvUr^f!bp1(0&%2?1VVxn zQb=(?IxZ5mks@kZx|67=xDF%=uA!l!<f=3Ox0B7yQ*I^C&VS~;H}Cg$Jv-g7JNhk8 zl=`D{;O~YTop%C%1D8ca+~>R7|2Yaf(fI83?IVBNiFW(l;@iE!q33x`a-wk5R(U7# zhhe#>)Q;t5+>BNDoW^HO|6tIF?F;Rv8Ow<d`(3o`FsmJ0Rot<jr=O?EdCevE*_+wv zbtAn<+YkDl*Jxo8cos2%9<NhU`EO?^ZRL<1oKAbg-VPOrCNGpd{wmMX?}fd+joMn& zyEn?6u(&^QFCN}kDN?CN!O^gzPRD0U+|i2#`+?_szFrH7bnJUW@2;NP+aDf)c2iHP zEVA(1AmLn=)xx73SycMz1*M3Pmt`&Jyw0|M4E(<LGJe~8g9*UGtF=8Q3z51LbvpYx zIFF2m<+@r8$frJiKpW)Y*G2C$dO+{vPB7951jA8v?-O-e$=mtqly<_tN4jsJ-=Io? z$5jfVnTRw*iz-FCFrDJ@YP&1NUN9`_x!4b;iDXa2k8OdfZLbu2CZ=3sZ5n$*vL~{M zZPT%5l0ll-6H#N+wDyE)BH0re#h!NTnPgzco{6cgJ(J`iP0ozq%*39FX-0b{$wQi) zGlCNj7x^TnGm;b1<je}rtl-Q_PDqo}5uA?TbR;LF$%#LNQE9xEx_WUjbCMI%<Wz!F z2~H(BAx%!~9=ay+Gs+9jyyS#5Iq{E$XYxD?g0mnwAx+Mr;4BKxqU3}$IZJ}GBsfcw z6Vl|I6`Zqzb5?Rfnw+lSbOom?IU!BXvfwNW&a&i$G&w7Rvm!Vvk`vP8tP0Mm;H*kc zNRzWBIBSBlCOIKZ&N;z3CphOMC#1<aFF5A~=e*>EG&!+J8}IFc;9QWLkS6D%;9L}( zi;@%4<XjS*OM-JrazdJ%%Yt)Ra4t(uNRx9#aIOf>70C%{a;^%_Rl&I`IU!BXHNm+i zIM*a6q{+E1IM)T|y5xj3IqQP6E;#Fw6H;@I&-ylRK`%eizPEk-Ar7vO8kU8<3P%n7 z$=An^WUMw5g~QOg3Z>6G@%Q~Y6iy0eC~RIgLg}&VP;YTo%~xclQZg2{vm3GWn2gnW zRjjm(g$?dTEIoD=t5%5oPMa>d7SD7k_A~0;Z;F)SA)+Za*4OxF75L!D*OTdV`j_s~ z#`$eyMo*)K1<o$=+JZ#6!^0pT#~%&%sJ5Mp4np5PKPJC>)R^pMFFq<)l{+5%+4`@( zy7m3hr|l<i{{G?lv9e*%#NENYb;5$<aHqwg9&)4V+)mub7Z0p2G1G{x8<Y9R7m1x7 zc6suIjd}d?_=l1n=S3Qq$F)0AHyqfLTRNMkcYpmm-27tj-NHYErC<EPBzN&KU6#|9 y#SM;?1AEvD#rwu*Xg)}`ly{>(&$ue6&cA#&E__kAKCb&7oWsqilC>4iIP^boV5A=a diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 9282ea0fb2..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,7 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/DSDT", -"tests/data/acpi/aarch64/virt/DSDT.memhp", -"tests/data/acpi/aarch64/virt/DSDT.topology", -"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", -"tests/data/acpi/aarch64/virt/DSDT.pxb", -"tests/data/acpi/x86/microvm/DSDT.pcie", From 329b327924f7e1a7a735c91b31f722708599e10f Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:03 +0530 Subject: [PATCH 2388/2884] tests/qtest/bios-tables-test.c: Remove the fall back path The expected ACPI AML files are moved now under ${arch}/{machine} path. Hence, there is no need to search in old path which didn't have ${arch}. Remove the code which searches for the expected AML files under old path as well. Suggested-by: Igor Mammedov <imammedo@redhat.com> Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-7-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- tests/qtest/bios-tables-test.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index f4c4704bab..498e0e35d9 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -267,15 +267,6 @@ static void dump_aml_files(test_data *data, bool rebuild) data->arch, data->machine, sdt->aml, ext); - /* - * To keep test cases not failing before the DATA files are moved to - * ${arch}/${machine} folder, add this check as well. - */ - if (!g_file_test(aml_file, G_FILE_TEST_EXISTS)) { - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, - data->machine, sdt->aml, ext); - } - if (!g_file_test(aml_file, G_FILE_TEST_EXISTS) && sdt->aml_len == exp_sdt->aml_len && !memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) { @@ -412,11 +403,6 @@ static GArray *load_expected_aml(test_data *data) try_again: aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch, data->machine, sdt->aml, ext); - if (!g_file_test(aml_file, G_FILE_TEST_EXISTS)) { - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - sdt->aml, ext); - } - if (verbosity_level >= 2) { fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } From cc3ba2422554b63f30b697eb67143b5d48c5b7a3 Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:04 +0530 Subject: [PATCH 2389/2884] tests/acpi: Add empty ACPI data files for RISC-V As per process documented (steps 1-3) in bios-tables-test.c, add empty AML data files for RISC-V ACPI tables and add the entries in bios-tables-test-allowed-diff.h. Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-8-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- tests/data/acpi/riscv64/virt/APIC | 0 tests/data/acpi/riscv64/virt/DSDT | 0 tests/data/acpi/riscv64/virt/FACP | 0 tests/data/acpi/riscv64/virt/MCFG | 0 tests/data/acpi/riscv64/virt/RHCT | 0 tests/data/acpi/riscv64/virt/SPCR | 0 tests/qtest/bios-tables-test-allowed-diff.h | 6 ++++++ 7 files changed, 6 insertions(+) create mode 100644 tests/data/acpi/riscv64/virt/APIC create mode 100644 tests/data/acpi/riscv64/virt/DSDT create mode 100644 tests/data/acpi/riscv64/virt/FACP create mode 100644 tests/data/acpi/riscv64/virt/MCFG create mode 100644 tests/data/acpi/riscv64/virt/RHCT create mode 100644 tests/data/acpi/riscv64/virt/SPCR diff --git a/tests/data/acpi/riscv64/virt/APIC b/tests/data/acpi/riscv64/virt/APIC new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/riscv64/virt/DSDT b/tests/data/acpi/riscv64/virt/DSDT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/riscv64/virt/FACP b/tests/data/acpi/riscv64/virt/FACP new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/riscv64/virt/MCFG b/tests/data/acpi/riscv64/virt/MCFG new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/riscv64/virt/SPCR b/tests/data/acpi/riscv64/virt/SPCR new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..70474a097f 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,7 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/riscv64/virt/APIC", +"tests/data/acpi/riscv64/virt/DSDT", +"tests/data/acpi/riscv64/virt/FACP", +"tests/data/acpi/riscv64/virt/MCFG", +"tests/data/acpi/riscv64/virt/RHCT", +"tests/data/acpi/riscv64/virt/SPCR", From 5b966e548fb7a530431379bab053eb1d6fb25867 Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:05 +0530 Subject: [PATCH 2390/2884] tests/qtest/bios-tables-test.c: Enable basic testing for RISC-V Add basic ACPI table test case for RISC-V. Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-9-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- tests/qtest/bios-tables-test.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 498e0e35d9..36e5c0adde 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1963,6 +1963,28 @@ static void test_acpi_microvm_acpi_erst(void) } #endif /* CONFIG_POSIX */ +static void test_acpi_riscv64_virt_tcg(void) +{ + test_data data = { + .machine = "virt", + .arch = "riscv64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-riscv-code.fd", + .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", + .ram_start = 0x80000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + /* + * RHCT will have ISA string encoded. To reduce the effort + * of updating expected AML file for any new default ISA extension, + * use the profile rva22s64. + */ + test_acpi_one("-cpu rva22s64 ", &data); + free_test_data(&data); +} + static void test_acpi_aarch64_virt_tcg(void) { test_data data = { @@ -2441,6 +2463,10 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); } } + } else if (strcmp(arch, "riscv64") == 0) { + if (has_tcg && qtest_has_device("virtio-blk-pci")) { + qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg); + } } ret = g_test_run(); boot_sector_cleanup(disk); From e9c0d54f4aebeeeebf1124d6acd607e3015e0a51 Mon Sep 17 00:00:00 2001 From: Sunil V L <sunilvl@ventanamicro.com> Date: Tue, 16 Jul 2024 20:13:06 +0530 Subject: [PATCH 2391/2884] tests/acpi: Add expected ACPI AML files for RISC-V As per the step 5 in the process documented in bios-tables-test.c, generate the expected ACPI AML data files for RISC-V using the rebuild-expected-aml.sh script and update the bios-tables-test-allowed-diff.h. These are all new files being added for the first time. Hence, iASL diff output is not added. Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Acked-by: Igor Mammedov <imammedo@redhat.com> Message-Id: <20240716144306.2432257-10-sunilvl@ventanamicro.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- tests/data/acpi/riscv64/virt/APIC | Bin 0 -> 116 bytes tests/data/acpi/riscv64/virt/DSDT | Bin 0 -> 3576 bytes tests/data/acpi/riscv64/virt/FACP | Bin 0 -> 276 bytes tests/data/acpi/riscv64/virt/MCFG | Bin 0 -> 60 bytes tests/data/acpi/riscv64/virt/RHCT | Bin 0 -> 332 bytes tests/data/acpi/riscv64/virt/SPCR | Bin 0 -> 80 bytes tests/qtest/bios-tables-test-allowed-diff.h | 6 ------ 7 files changed, 6 deletions(-) diff --git a/tests/data/acpi/riscv64/virt/APIC b/tests/data/acpi/riscv64/virt/APIC index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..66a25dfd2d6ea2b607c024722b2eab95873a01e9 100644 GIT binary patch literal 116 zcmZ<^@N_O=U|?X|;^gn_5v<@85#X!<1dKp25F13pfP@Mo12P{Zj?R|`s)2!c7=s}J I#NvT*0o0BN0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/riscv64/virt/DSDT b/tests/data/acpi/riscv64/virt/DSDT index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6a33f5647ddd6de3a0f000f718b58f6fff44f0fd 100644 GIT binary patch literal 3576 zcmZvfO>Y}j6o&8Elh_%5#CDu-Cr+Uf3sh+w6Os)g_Bd%=*~#E>8YCmRQk04kR6;38 z1tF>|_=woFL2TFrkzaw3Kw`y?9SFpd4SxW3<~{d%%sFI6^31%~=e+kk*Z0mHH-bj@ zpNvwm2lYK~Cs?V!>U%3VETt6P(>3S)@mfEq_j{*1w&%KTvcJ=8WNMz@gjiR}{(iH8 zbGz2fKj&PZyKX7U;>Z7W?{s7Pz}q%PuWYsVVYCX1pj&fN$-d{+ESx(*KJR2do*=ti zZVrZzRPS`Xi5g61C-80~vob2-W>CkyNK|R1?&w4>;qA3$W_6TFISbCL=}hIQ%g@G@ zWjVUnWWNzK3ahdFl#?s|I{59|`=7VZo_zP!_qV>W3X4`@E|xib^R2_<8+RWZz5VXd z{liD-c5?&O*6iDzu-w*eXAkAB{nzP;GweEtu5-h#Y0wB*TT~3Ow4gz{VzI(3Vnd5M zRk0dn;l!dmT;>ty9@R*Xc$CK`^RT(c3y~gnl!wne#<<5?q{mp4#~tP|&OKg?^cauw zIAI<M?y(-}k%;nm%si4qkEY$igfJfduJ3y8_GohWn2}j0rDx%aj)&`&w&Hj{=mb{p zR9g*aiLk;X;a1lotW4X;>K0WsoGrqNCJXm^Cc?|KNw49okzSZI-0Rs0FVi;iYJK5* zO*UFJcY{_to<-zbb7?gPTQ_m8*LO$b7<4=NvQzSvr<&?Wttc0t2JYTd_tYV-icIt} z@;lwB);rGQLds4J28w<gW$rE9S5zTQ?M{q_Clqmrsw!mc^k85q%0b*vQ5FKT<~yB- z&`?q){B<lAs)-#c$_zteN@pTeXF^hD*bA1YiPf2LMVVn}++-qDXF^gYD$=o7lqPnl zhKxGYHko1Zkem}s5t(5HW4g~Ubj)Oi#Y1vVEI?KP9<Weh=(yyB<eXS`CRSIQ5S$6g z3CTIJ*pL~HbW&%ALz0x7keqWuaAF@awd)a@kera56WfM~)zvzJ(~+EzoHHdjQ-U)k zIUzYGHaf<c7My9x3CTIJ|06T}Ju`wcBRL^CXI5}#1!q=rLUPVY!8s{7CnYB&=fwWV zII&fm+V!uJlbn#8GcP#vf-^5UAvtG3a25n-L2^QJ&MCn;B{-)fCnV=A3eKY7EJ{vD z&N(eOrv>M<<b>p$GlFwQaL!0hNX|JcIA;attmK5`oOrQgzvrCboRgf8oO51q&I`_Y z$qC6h7X;^m;9QWLkeqW-a4rhYMac=tIZJ}GBsfcw6OwZ-3C<<Kxg<FuIp?zATo#<m zk`t11mIY^7aF!(}q~R>4`(C}KF7YH%*r(}jWhdc0{4}Ft)TGpaPSz)wD{1VN`q&%{ z1|Nm@_{K`p3#UG?1=9g-sk7%<j&7!RO5_gceT*OGT{9W%`@Twg-A+ptw<r30foGo` zE3a`-x!6gZzfN!Rz4q_bzsse~FApBpet7AZyJyFajeu2Llekt-NRSWRRLSZCIyq%J zU*w*i-?Ki%Hx(USxtMu+9>$sG9DR90!PI_v{B_o}Qzw<n<Khkct7zL7*Gx4(umALC zu=alY$?RY4`5(RZ#l-m=>M|d<EIQ$TK-=zY1?=_5(|?(0kgJY+Wqe#36AaJGd~9%! Jrzicb_!p&=*&hG^ literal 0 HcmV?d00001 diff --git a/tests/data/acpi/riscv64/virt/FACP b/tests/data/acpi/riscv64/virt/FACP index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a5276b65ea8ce46cc9b40d96d98f0669c9089ed4 100644 GIT binary patch literal 276 zcmZ>BbPf<<WME(ucJg=j2v%^42yj*a0-z8Bhz+8t3k1-OV?`GjD1M-;Zz#xa0OIBc A0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/riscv64/virt/MCFG b/tests/data/acpi/riscv64/virt/MCFG index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..37eb923a9320f5573c0c2cdb90bd98409cc7eb6f 100644 GIT binary patch literal 60 rcmeZuc5}C3U|?Y6aq@Te2v%^42yj*a0!E-1hz+8VfB}^KA4CHH3`GY4 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4f231735abad925435c3cd052e6641b1b4187278 100644 GIT binary patch literal 332 zcmXAlu};J=42E3}oe;y#j3;Kss=E2Q+<*iE2DT_kQ#GPVt0XN_CY}QEe2l!r@jGY9 z{(f6docQI`zCBh%)$aJzo?iFI_vdyGLy1^3*}lGi3a=3lMg37lzZBM{wodk)7TM~i zRtz<{3+4+lLrXWwB5YqU#?qxjG@Sbs7?ERdyfzkMus+RlDILR%e&?1^WZBdq0=N=! z3=z}&!C5b|#thwwtU!g=DD-_K5u?^KCV<o8lL^LuB1x+pWud{LxXR8TT8}a~E0^%g ZTR3fLdqp>_=zP8dH)RXFi+dCw;Qyc2X#oHL literal 0 HcmV?d00001 diff --git a/tests/data/acpi/riscv64/virt/SPCR b/tests/data/acpi/riscv64/virt/SPCR index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4da9daf65f71a13ac2b488d4e9728f194b569a43 100644 GIT binary patch literal 80 zcmWFza1IJ!U|?X{>E!S15v<@85#X!<1dKp25F12;fdT`FDF9*%FmM4$c8~z`e;@#f G!2kgKJqrN< literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 70474a097f..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,7 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/riscv64/virt/APIC", -"tests/data/acpi/riscv64/virt/DSDT", -"tests/data/acpi/riscv64/virt/FACP", -"tests/data/acpi/riscv64/virt/MCFG", -"tests/data/acpi/riscv64/virt/RHCT", -"tests/data/acpi/riscv64/virt/SPCR", From 78cc8c69475042ce6b6f720f8c81920fead0d86e Mon Sep 17 00:00:00 2001 From: Alistair Francis <alistair23@gmail.com> Date: Wed, 3 Jul 2024 19:20:25 +1000 Subject: [PATCH 2392/2884] hw/pci: Add all Data Object Types defined in PCIe r6.0 Add all of the defined protocols/features from the PCIe-SIG r6.0 "Table 6-32 PCI-SIG defined Data Object Types (Vendor ID = 0001h)" table. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Wilfred Mallawa <wilfred.mallawa@wdc.com> Message-Id: <20240703092027.644758-2-alistair.francis@wdc.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- include/hw/pci/pcie_doe.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/hw/pci/pcie_doe.h b/include/hw/pci/pcie_doe.h index 87dc17dcef..15d94661f9 100644 --- a/include/hw/pci/pcie_doe.h +++ b/include/hw/pci/pcie_doe.h @@ -46,6 +46,8 @@ REG32(PCI_DOE_CAP_STATUS, 0) /* PCI-SIG defined Data Object Types - r6.0 Table 6-32 */ #define PCI_SIG_DOE_DISCOVERY 0x00 +#define PCI_SIG_DOE_CMA 0x01 +#define PCI_SIG_DOE_SECURED_CMA 0x02 #define PCI_DOE_DW_SIZE_MAX (1 << 18) #define PCI_DOE_PROTOCOL_NUM_MAX 256 From bc419a1cc5b15deec9cf7cb7a382392c112810e2 Mon Sep 17 00:00:00 2001 From: Huai-Cheng Kuo <hchkuo@avery-design.com.tw> Date: Wed, 3 Jul 2024 19:20:26 +1000 Subject: [PATCH 2393/2884] backends: Initial support for SPDM socket support SPDM enables authentication, attestation and key exchange to assist in providing infrastructure security enablement. It's a standard published by the DMTF [1]. SPDM supports multiple transports, including PCIe DOE and MCTP. This patch adds support to QEMU to connect to an external SPDM instance. SPDM support can be added to any QEMU device by exposing a TCP socket to a SPDM server. The server can then implement the SPDM decoding/encoding support, generally using libspdm [2]. This is similar to how the current TPM implementation works and means that the heavy lifting of setting up certificate chains, capabilities, measurements and complex crypto can be done outside QEMU by a well supported and tested library. 1: https://www.dmtf.org/standards/SPDM 2: https://github.com/DMTF/libspdm Signed-off-by: Huai-Cheng Kuo <hchkuo@avery-design.com.tw> Signed-off-by: Chris Browy <cbrowy@avery-design.com> Co-developed-by: Jonathan Cameron <Jonathan.cameron@huawei.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> [ Changes by WM - Bug fixes from testing ] Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com> [ Changes by AF: - Convert to be more QEMU-ified - Move to backends as it isn't PCIe specific ] Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Message-Id: <20240703092027.644758-3-alistair.francis@wdc.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- MAINTAINERS | 6 + backends/Kconfig | 4 + backends/meson.build | 2 + backends/spdm-socket.c | 216 +++++++++++++++++++++++++++++++++++ include/sysemu/spdm-socket.h | 74 ++++++++++++ 5 files changed, 302 insertions(+) create mode 100644 backends/spdm-socket.c create mode 100644 include/sysemu/spdm-socket.h diff --git a/MAINTAINERS b/MAINTAINERS index 93546cfb14..d76b49a597 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3401,6 +3401,12 @@ F: tests/qtest/*tpm* F: docs/specs/tpm.rst T: git https://github.com/stefanberger/qemu-tpm.git tpm-next +SPDM +M: Alistair Francis <alistair.francis@wdc.com> +S: Maintained +F: backends/spdm-socket.c +F: include/sysemu/spdm-socket.h + Checkpatch S: Odd Fixes F: scripts/checkpatch.pl diff --git a/backends/Kconfig b/backends/Kconfig index 2cb23f62fa..d3dbe19868 100644 --- a/backends/Kconfig +++ b/backends/Kconfig @@ -3,3 +3,7 @@ source tpm/Kconfig config IOMMUFD bool depends on VFIO + +config SPDM_SOCKET + bool + default y diff --git a/backends/meson.build b/backends/meson.build index 749b491f12..da714b93d1 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -33,4 +33,6 @@ endif system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) +system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) + subdir('tpm') diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c new file mode 100644 index 0000000000..d0663d696c --- /dev/null +++ b/backends/spdm-socket.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * QEMU SPDM socket support + * + * This is based on: + * https://github.com/DMTF/spdm-emu/blob/07c0a838bcc1c6207c656ac75885c0603e344b6f/spdm_emu/spdm_emu_common/command.c + * but has been re-written to match QEMU style + * + * Copyright (c) 2021, DMTF. All rights reserved. + * Copyright (c) 2023. Western Digital Corporation or its affiliates. + */ + +#include "qemu/osdep.h" +#include "sysemu/spdm-socket.h" +#include "qapi/error.h" + +static bool read_bytes(const int socket, uint8_t *buffer, + size_t number_of_bytes) +{ + ssize_t number_received = 0; + ssize_t result; + + while (number_received < number_of_bytes) { + result = recv(socket, buffer + number_received, + number_of_bytes - number_received, 0); + if (result <= 0) { + return false; + } + number_received += result; + } + return true; +} + +static bool read_data32(const int socket, uint32_t *data) +{ + bool result; + + result = read_bytes(socket, (uint8_t *)data, sizeof(uint32_t)); + if (!result) { + return result; + } + *data = ntohl(*data); + return true; +} + +static bool read_multiple_bytes(const int socket, uint8_t *buffer, + uint32_t *bytes_received, + uint32_t max_buffer_length) +{ + uint32_t length; + bool result; + + result = read_data32(socket, &length); + if (!result) { + return result; + } + + if (length > max_buffer_length) { + return false; + } + + if (bytes_received) { + *bytes_received = length; + } + + if (length == 0) { + return true; + } + + return read_bytes(socket, buffer, length); +} + +static bool receive_platform_data(const int socket, + uint32_t transport_type, + uint32_t *command, + uint8_t *receive_buffer, + uint32_t *bytes_to_receive) +{ + bool result; + uint32_t response; + uint32_t bytes_received; + + result = read_data32(socket, &response); + if (!result) { + return result; + } + *command = response; + + result = read_data32(socket, &transport_type); + if (!result) { + return result; + } + + bytes_received = 0; + result = read_multiple_bytes(socket, receive_buffer, &bytes_received, + *bytes_to_receive); + if (!result) { + return result; + } + *bytes_to_receive = bytes_received; + + return result; +} + +static bool write_bytes(const int socket, const uint8_t *buffer, + uint32_t number_of_bytes) +{ + ssize_t number_sent = 0; + ssize_t result; + + while (number_sent < number_of_bytes) { + result = send(socket, buffer + number_sent, + number_of_bytes - number_sent, 0); + if (result == -1) { + return false; + } + number_sent += result; + } + return true; +} + +static bool write_data32(const int socket, uint32_t data) +{ + data = htonl(data); + return write_bytes(socket, (uint8_t *)&data, sizeof(uint32_t)); +} + +static bool write_multiple_bytes(const int socket, const uint8_t *buffer, + uint32_t bytes_to_send) +{ + bool result; + + result = write_data32(socket, bytes_to_send); + if (!result) { + return result; + } + + return write_bytes(socket, buffer, bytes_to_send); +} + +static bool send_platform_data(const int socket, + uint32_t transport_type, uint32_t command, + const uint8_t *send_buffer, size_t bytes_to_send) +{ + bool result; + + result = write_data32(socket, command); + if (!result) { + return result; + } + + result = write_data32(socket, transport_type); + if (!result) { + return result; + } + + return write_multiple_bytes(socket, send_buffer, bytes_to_send); +} + +int spdm_socket_connect(uint16_t port, Error **errp) +{ + int client_socket; + struct sockaddr_in server_addr; + + client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client_socket < 0) { + error_setg(errp, "cannot create socket: %s", strerror(errno)); + return -1; + } + + memset((char *)&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server_addr.sin_port = htons(port); + + + if (connect(client_socket, (struct sockaddr *)&server_addr, + sizeof(server_addr)) < 0) { + error_setg(errp, "cannot connect: %s", strerror(errno)); + close(client_socket); + return -1; + } + + return client_socket; +} + +uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type, + void *req, uint32_t req_len, + void *rsp, uint32_t rsp_len) +{ + uint32_t command; + bool result; + + result = send_platform_data(socket, transport_type, + SPDM_SOCKET_COMMAND_NORMAL, + req, req_len); + if (!result) { + return 0; + } + + result = receive_platform_data(socket, transport_type, &command, + (uint8_t *)rsp, &rsp_len); + if (!result) { + return 0; + } + + assert(command != 0); + + return rsp_len; +} + +void spdm_socket_close(const int socket, uint32_t transport_type) +{ + send_platform_data(socket, transport_type, + SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0); +} diff --git a/include/sysemu/spdm-socket.h b/include/sysemu/spdm-socket.h new file mode 100644 index 0000000000..5d8bd9aa4e --- /dev/null +++ b/include/sysemu/spdm-socket.h @@ -0,0 +1,74 @@ +/* + * QEMU SPDM socket support + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SPDM_REQUESTER_H +#define SPDM_REQUESTER_H + +/** + * spdm_socket_connect: connect to an external SPDM socket + * @port: port to connect to + * @errp: error object handle + * + * This will connect to an external SPDM socket server. On error + * it will return -1 and errp will be set. On success this function + * will return the socket number. + */ +int spdm_socket_connect(uint16_t port, Error **errp); + +/** + * spdm_socket_rsp: send and receive a message to a SPDM server + * @socket: socket returned from spdm_socket_connect() + * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro + * @req: request buffer + * @req_len: request buffer length + * @rsp: response buffer + * @rsp_len: response buffer length + * + * Send platform data to a SPDM server on socket and then receive + * a response. + */ +uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type, + void *req, uint32_t req_len, + void *rsp, uint32_t rsp_len); + +/** + * spdm_socket_close: send a shutdown command to the server + * @socket: socket returned from spdm_socket_connect() + * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro + * + * This will issue a shutdown command to the server. + */ +void spdm_socket_close(const int socket, uint32_t transport_type); + +#define SPDM_SOCKET_COMMAND_NORMAL 0x0001 +#define SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE 0x8001 +#define SPDM_SOCKET_COMMAND_CONTINUE 0xFFFD +#define SPDM_SOCKET_COMMAND_SHUTDOWN 0xFFFE +#define SPDM_SOCKET_COMMAND_UNKOWN 0xFFFF +#define SPDM_SOCKET_COMMAND_TEST 0xDEAD + +#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01 +#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02 + +#define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200 + +#endif From 4f947b10d525958578002848a92eeb6152ffbf0d Mon Sep 17 00:00:00 2001 From: Wilfred Mallawa <wilfred.mallawa@wdc.com> Date: Wed, 3 Jul 2024 19:20:27 +1000 Subject: [PATCH 2394/2884] hw/nvme: Add SPDM over DOE support Setup Data Object Exchange (DOE) as an extended capability for the NVME controller and connect SPDM to it (CMA) to it. Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Acked-by: Klaus Jensen <k.jensen@samsung.com> Message-Id: <20240703092027.644758-4-alistair.francis@wdc.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- docs/specs/index.rst | 1 + docs/specs/spdm.rst | 134 ++++++++++++++++++++++++++++++++++++ hw/nvme/ctrl.c | 62 +++++++++++++++++ include/hw/pci/pci_device.h | 7 ++ include/hw/pci/pcie_doe.h | 3 + 5 files changed, 207 insertions(+) create mode 100644 docs/specs/spdm.rst diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 1484e3e760..e2d907959a 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -29,6 +29,7 @@ guest hardware that is specific to QEMU. edu ivshmem-spec pvpanic + spdm standard-vga virt-ctlr vmcoreinfo diff --git a/docs/specs/spdm.rst b/docs/specs/spdm.rst new file mode 100644 index 0000000000..f7de080ff0 --- /dev/null +++ b/docs/specs/spdm.rst @@ -0,0 +1,134 @@ +====================================================== +QEMU Security Protocols and Data Models (SPDM) Support +====================================================== + +SPDM enables authentication, attestation and key exchange to assist in +providing infrastructure security enablement. It's a standard published +by the `DMTF`_. + +QEMU supports connecting to a SPDM responder implementation. This allows an +external application to emulate the SPDM responder logic for an SPDM device. + +Setting up a SPDM server +======================== + +When using QEMU with SPDM devices QEMU will connect to a server which +implements the SPDM functionality. + +SPDM-Utils +---------- + +You can use `SPDM Utils`_ to emulate a responder. This is the simplest method. + +SPDM-Utils is a Linux applications to manage, test and develop devices +supporting DMTF Security Protocol and Data Model (SPDM). It is written in Rust +and utilises libspdm. + +To use SPDM-Utils you will need to do the following steps. Details are included +in the SPDM-Utils README. + + 1. `Build libspdm`_ + 2. `Build SPDM Utils`_ + 3. `Run it as a server`_ + +spdm-emu +-------- + +You can use `spdm emu`_ to model the +SPDM responder. + +.. code-block:: shell + + $ cd spdm-emu + $ git submodule init; git submodule update --recursive + $ mkdir build; cd build + $ cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Debug -DCRYPTO=openssl .. + $ make -j32 + $ make copy_sample_key # Build certificates, required for SPDM authentication. + +It is worth noting that the certificates should be in compliance with +PCIe r6.1 sec 6.31.3. This means you will need to add the following to +openssl.cnf + +.. code-block:: + + subjectAltName = otherName:2.23.147;UTF8:Vendor=1b36:Device=0010:CC=010802:REV=02:SSVID=1af4:SSID=1100 + 2.23.147 = ASN1:OID:2.23.147 + +and then manually regenerate some certificates with: + +.. code-block:: shell + + $ openssl req -nodes -newkey ec:param.pem -keyout end_responder.key \ + -out end_responder.req -sha384 -batch \ + -subj "/CN=DMTF libspdm ECP384 responder cert" + + $ openssl x509 -req -in end_responder.req -out end_responder.cert \ + -CA inter.cert -CAkey inter.key -sha384 -days 3650 -set_serial 3 \ + -extensions v3_end -extfile ../openssl.cnf + + $ openssl asn1parse -in end_responder.cert -out end_responder.cert.der + + $ cat ca.cert.der inter.cert.der end_responder.cert.der > bundle_responder.certchain.der + +You can use SPDM-Utils instead as it will generate the correct certificates +automatically. + +The responder can then be launched with + +.. code-block:: shell + + $ cd bin + $ ./spdm_responder_emu --trans PCI_DOE + +Connecting an SPDM NVMe device +============================== + +Once a SPDM server is running we can start QEMU and connect to the server. + +For an NVMe device first let's setup a block we can use + +.. code-block:: shell + + $ cd qemu-spdm/linux/image + $ dd if=/dev/zero of=blknvme bs=1M count=2096 # 2GB NNMe Drive + +Then you can add this to your QEMU command line: + +.. code-block:: shell + + -drive file=blknvme,if=none,id=mynvme,format=raw \ + -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323 + +At which point QEMU will try to connect to the SPDM server. + +Note that if using x64-64 you will want to use the q35 machine instead +of the default. So the entire QEMU command might look like this + +.. code-block:: shell + + qemu-system-x86_64 -M q35 \ + --kernel bzImage \ + -drive file=rootfs.ext2,if=virtio,format=raw \ + -append "root=/dev/vda console=ttyS0" \ + -net none -nographic \ + -drive file=blknvme,if=none,id=mynvme,format=raw \ + -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323 + +.. _DMTF: + https://www.dmtf.org/standards/SPDM + +.. _SPDM Utils: + https://github.com/westerndigitalcorporation/spdm-utils + +.. _spdm emu: + https://github.com/dmtf/spdm-emu + +.. _Build libspdm: + https://github.com/westerndigitalcorporation/spdm-utils?tab=readme-ov-file#build-libspdm + +.. _Build SPDM Utils: + https://github.com/westerndigitalcorporation/spdm-utils?tab=readme-ov-file#build-the-binary + +.. _Run it as a server: + https://github.com/westerndigitalcorporation/spdm-utils#qemu-spdm-device-emulation diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 5b1b0cabcf..6ee72014cf 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -203,6 +203,7 @@ #include "sysemu/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" +#include "sysemu/spdm-socket.h" #include "migration/vmstate.h" #include "nvme.h" @@ -8113,6 +8114,27 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) return 0; } +static bool pcie_doe_spdm_rsp(DOECap *doe_cap) +{ + void *req = pcie_doe_get_write_mbox_ptr(doe_cap); + uint32_t req_len = pcie_doe_get_obj_len(req) * 4; + void *rsp = doe_cap->read_mbox; + uint32_t rsp_len = SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE; + + uint32_t recvd = spdm_socket_rsp(doe_cap->spdm_socket, + SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE, + req, req_len, rsp, rsp_len); + doe_cap->read_mbox_len += DIV_ROUND_UP(recvd, 4); + + return recvd != 0; +} + +static DOEProtocol doe_spdm_prot[] = { + { PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_spdm_rsp }, + { PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_SECURED_CMA, pcie_doe_spdm_rsp }, + { } +}; + static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) { ERRP_GUARD(); @@ -8200,6 +8222,25 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); + pcie_cap_deverr_init(pci_dev); + + /* DOE Initialisation */ + if (pci_dev->spdm_port) { + uint16_t doe_offset = n->params.sriov_max_vfs ? + PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF + : PCI_CONFIG_SPACE_SIZE; + + pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset, + doe_spdm_prot, true, 0); + + pci_dev->doe_spdm.spdm_socket = spdm_socket_connect(pci_dev->spdm_port, + errp); + + if (pci_dev->doe_spdm.spdm_socket < 0) { + return false; + } + } + if (n->params.cmb_size_mb) { nvme_init_cmb(n, pci_dev); } @@ -8446,6 +8487,11 @@ static void nvme_exit(PCIDevice *pci_dev) g_free(n->cmb.buf); } + if (pci_dev->doe_spdm.spdm_socket > 0) { + spdm_socket_close(pci_dev->doe_spdm.spdm_socket, + SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE); + } + if (n->pmr.dev) { host_memory_backend_set_mapped(n->pmr.dev, false); } @@ -8491,6 +8537,7 @@ static Property nvme_props[] = { DEFINE_PROP_BOOL("msix-exclusive-bar", NvmeCtrl, params.msix_exclusive_bar, false), DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff), + DEFINE_PROP_UINT16("spdm_port", PCIDevice, spdm_port, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -8562,11 +8609,25 @@ static void nvme_pci_write_config(PCIDevice *dev, uint32_t address, { uint16_t old_num_vfs = pcie_sriov_num_vfs(dev); + if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) { + pcie_doe_write_config(&dev->doe_spdm, address, val, len); + } pci_default_write_config(dev, address, val, len); pcie_cap_flr_write_config(dev, address, val, len); nvme_sriov_post_write_config(dev, old_num_vfs); } +static uint32_t nvme_pci_read_config(PCIDevice *dev, uint32_t address, int len) +{ + uint32_t val; + if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) { + if (pcie_doe_read_config(&dev->doe_spdm, address, len, &val)) { + return val; + } + } + return pci_default_read_config(dev, address, len); +} + static const VMStateDescription nvme_vmstate = { .name = "nvme", .unmigratable = 1, @@ -8579,6 +8640,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) pc->realize = nvme_realize; pc->config_write = nvme_pci_write_config; + pc->config_read = nvme_pci_read_config; pc->exit = nvme_exit; pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->revision = 2; diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index cefd6f7640..e7e41cb939 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -3,6 +3,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pcie.h" +#include "hw/pci/pcie_doe.h" #define TYPE_PCI_DEVICE "pci-device" typedef struct PCIDeviceClass PCIDeviceClass; @@ -159,6 +160,12 @@ struct PCIDevice { MSIVectorReleaseNotifier msix_vector_release_notifier; MSIVectorPollNotifier msix_vector_poll_notifier; + /* SPDM */ + uint16_t spdm_port; + + /* DOE */ + DOECap doe_spdm; + /* ID of standby device in net_failover pair */ char *failover_pair_id; uint32_t acpi_index; diff --git a/include/hw/pci/pcie_doe.h b/include/hw/pci/pcie_doe.h index 15d94661f9..9e1275db8a 100644 --- a/include/hw/pci/pcie_doe.h +++ b/include/hw/pci/pcie_doe.h @@ -108,6 +108,9 @@ struct DOECap { /* Protocols and its callback response */ DOEProtocol *protocols; uint16_t protocol_num; + + /* Used for spdm-socket */ + int spdm_socket; }; void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset, From 3d75856d1a9c8d187d91066d0747de7a0d1ce3d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 12:52:40 -0700 Subject: [PATCH 2395/2884] accel/tcg: Move {set,clear}_helper_retaddr to cpu_ldst.h Use of these in helpers goes hand-in-hand with tlb_vaddr_to_host and other probing functions. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- accel/tcg/cpu-exec.c | 3 --- accel/tcg/user-exec.c | 1 - accel/tcg/user-retaddr.h | 28 ---------------------------- include/exec/cpu_ldst.h | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 32 deletions(-) delete mode 100644 accel/tcg/user-retaddr.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 9010dad073..8163295f34 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -41,9 +41,6 @@ #include "tb-context.h" #include "internal-common.h" #include "internal-target.h" -#if defined(CONFIG_USER_ONLY) -#include "user-retaddr.h" -#endif /* -icount align implementation. */ diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 80d24540ed..7ddc47b0ba 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -33,7 +33,6 @@ #include "tcg/tcg-ldst.h" #include "internal-common.h" #include "internal-target.h" -#include "user-retaddr.h" __thread uintptr_t helper_retaddr; diff --git a/accel/tcg/user-retaddr.h b/accel/tcg/user-retaddr.h deleted file mode 100644 index e0f57e1994..0000000000 --- a/accel/tcg/user-retaddr.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ACCEL_TCG_USER_RETADDR_H -#define ACCEL_TCG_USER_RETADDR_H - -#include "qemu/atomic.h" - -extern __thread uintptr_t helper_retaddr; - -static inline void set_helper_retaddr(uintptr_t ra) -{ - helper_retaddr = ra; - /* - * Ensure that this write is visible to the SIGSEGV handler that - * may be invoked due to a subsequent invalid memory operation. - */ - signal_barrier(); -} - -static inline void clear_helper_retaddr(void) -{ - /* - * Ensure that previous memory operations have succeeded before - * removing the data visible to the signal handler. - */ - signal_barrier(); - helper_retaddr = 0; -} - -#endif diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 71009f84f5..dac12bd8eb 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -379,4 +379,38 @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, MMUAccessType access_type, int mmu_idx); #endif +/* + * For user-only, helpers that use guest to host address translation + * must protect the actual host memory access by recording 'retaddr' + * for the signal handler. This is required for a race condition in + * which another thread unmaps the page between a probe and the + * actual access. + */ +#ifdef CONFIG_USER_ONLY +extern __thread uintptr_t helper_retaddr; + +static inline void set_helper_retaddr(uintptr_t ra) +{ + helper_retaddr = ra; + /* + * Ensure that this write is visible to the SIGSEGV handler that + * may be invoked due to a subsequent invalid memory operation. + */ + signal_barrier(); +} + +static inline void clear_helper_retaddr(void) +{ + /* + * Ensure that previous memory operations have succeeded before + * removing the data visible to the signal handler. + */ + signal_barrier(); + helper_retaddr = 0; +} +#else +#define set_helper_retaddr(ra) do { } while (0) +#define clear_helper_retaddr() do { } while (0) +#endif + #endif /* CPU_LDST_H */ From 8009519b3094ab515def1fe9bbc444d463579448 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 14:08:24 -0700 Subject: [PATCH 2396/2884] target/arm: Use set/clear_helper_retaddr in helper-a64.c Use these in helper_dc_dva and the FEAT_MOPS routines to avoid a race condition with munmap in another thread. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/tcg/helper-a64.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 0ea8668ab4..c60d2a7ec9 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -928,6 +928,8 @@ uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) { + uintptr_t ra = GETPC(); + /* * Implement DC ZVA, which zeroes a fixed-length block of memory. * Note that we do not implement the (architecturally mandated) @@ -948,8 +950,6 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) #ifndef CONFIG_USER_ONLY if (unlikely(!mem)) { - uintptr_t ra = GETPC(); - /* * Trap if accessing an invalid page. DC_ZVA requires that we supply * the original pointer for an invalid page. But watchpoints require @@ -971,7 +971,9 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) } #endif + set_helper_retaddr(ra); memset(mem, 0, blocklen); + clear_helper_retaddr(); } void HELPER(unaligned_access)(CPUARMState *env, uint64_t addr, @@ -1120,7 +1122,9 @@ static uint64_t set_step(CPUARMState *env, uint64_t toaddr, } #endif /* Easy case: just memset the host memory */ + set_helper_retaddr(ra); memset(mem, data, setsize); + clear_helper_retaddr(); return setsize; } @@ -1163,7 +1167,9 @@ static uint64_t set_step_tags(CPUARMState *env, uint64_t toaddr, } #endif /* Easy case: just memset the host memory */ + set_helper_retaddr(ra); memset(mem, data, setsize); + clear_helper_retaddr(); mte_mops_set_tags(env, toaddr, setsize, *mtedesc); return setsize; } @@ -1497,7 +1503,9 @@ static uint64_t copy_step(CPUARMState *env, uint64_t toaddr, uint64_t fromaddr, } #endif /* Easy case: just memmove the host memory */ + set_helper_retaddr(ra); memmove(wmem, rmem, copysize); + clear_helper_retaddr(); return copysize; } @@ -1572,7 +1580,9 @@ static uint64_t copy_step_rev(CPUARMState *env, uint64_t toaddr, * Easy case: just memmove the host memory. Note that wmem and * rmem here point to the *last* byte to copy. */ + set_helper_retaddr(ra); memmove(wmem - (copysize - 1), rmem - (copysize - 1), copysize); + clear_helper_retaddr(); return copysize; } From 3b9991e35c08be7fd6b84090b2114ff1bfd44d3f Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 15:02:07 -0700 Subject: [PATCH 2397/2884] target/arm: Use set/clear_helper_retaddr in SVE and SME helpers Avoid a race condition with munmap in another thread. Use around blocks that exclusively use "host_fn". Keep the blocks as small as possible, but without setting and clearing for every operation on one page. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/tcg/sme_helper.c | 16 ++++++++++++++ target/arm/tcg/sve_helper.c | 42 +++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 5a6dd76489..50bb088d04 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -517,6 +517,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, clr_fn(za, 0, reg_off); } + set_helper_retaddr(ra); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -529,6 +531,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -543,6 +547,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(ra); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -554,6 +560,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, reg_off += esize; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -701,6 +709,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(ra); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -711,6 +721,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -725,6 +737,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(ra); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -734,6 +748,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, reg_off += 1 << esz; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index dd49e67d7a..f1ee0e060f 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -5738,6 +5738,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(retaddr); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -5752,6 +5754,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -5771,6 +5775,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(retaddr); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -5784,6 +5790,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, mem_off += N << msz; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -5934,15 +5942,11 @@ DO_LDN_2(4, dd, MO_64) /* * Load contiguous data, first-fault and no-fault. * - * For user-only, one could argue that we should hold the mmap_lock during - * the operation so that there is no race between page_check_range and the - * load operation. However, unmapping pages out from under a running thread - * is extraordinarily unlikely. This theoretical race condition also affects - * linux-user/ in its get_user/put_user macros. - * - * TODO: Construct some helpers, written in assembly, that interact with - * host_signal_handler to produce memory ops which can properly report errors - * without racing. + * For user-only, we control the race between page_check_range and + * another thread's munmap by using set/clear_helper_retaddr. Any + * SEGV that occurs between those markers is assumed to be because + * the guest page vanished. Keep that block as small as possible + * so that unrelated QEMU bugs are not blamed on the guest. */ /* Fault on byte I. All bits in FFR from I are cleared. The vector @@ -6093,6 +6097,8 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(retaddr); + do { uint64_t pg = *(uint64_t *)(vg + (reg_off >> 3)); do { @@ -6101,9 +6107,11 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, (cpu_watchpoint_address_matches (env_cpu(env), addr + mem_off, 1 << msz) & BP_MEM_READ)) { + clear_helper_retaddr(); goto do_fault; } if (mtedesc && !mte_probe(env, mtedesc, addr + mem_off)) { + clear_helper_retaddr(); goto do_fault; } host_fn(vd, reg_off, host + mem_off); @@ -6113,6 +6121,8 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, } while (reg_off <= reg_last && (reg_off & 63)); } while (reg_off <= reg_last); + clear_helper_retaddr(); + /* * MemSingleNF is allowed to fail for any reason. We have special * code above to handle the first element crossing a page boundary. @@ -6348,6 +6358,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(retaddr); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -6362,6 +6374,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -6381,6 +6395,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(retaddr); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -6394,6 +6410,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, mem_off += N << msz; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -6560,7 +6578,9 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, if (unlikely(info.flags & TLB_MMIO)) { tlb_fn(env, &scratch, reg_off, addr, retaddr); } else { + set_helper_retaddr(retaddr); host_fn(&scratch, reg_off, info.host); + clear_helper_retaddr(); } } else { /* Element crosses the page boundary. */ @@ -6782,7 +6802,9 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, goto fault; } + set_helper_retaddr(retaddr); host_fn(vd, reg_off, info.host); + clear_helper_retaddr(); } reg_off += esize; } while (reg_off & 63); @@ -6986,7 +7008,9 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, do { void *h = host[i]; if (likely(h != NULL)) { + set_helper_retaddr(retaddr); host_fn(vd, reg_off, h); + clear_helper_retaddr(); } else if ((vg[reg_off >> 6] >> (reg_off & 63)) & 1) { target_ulong addr = base + (off_fn(vm, reg_off) << scale); tlb_fn(env, vd, reg_off, addr, retaddr); From 73a93ae5f4c49a166bc2a286330a1b45f58f4df7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Sat, 22 Jun 2024 22:48:33 +0200 Subject: [PATCH 2398/2884] target/ppc/mem_helper.c: Remove a conditional from dcbz_common() Instead of passing a bool and select a value within dcbz_common() let the callers pass in the right value to avoid this conditional statement. On PPC dcbz is often used to zero memory and some code uses it a lot. This change improves the run time of a test case that copies memory with a dcbz call in every iteration from 6.23 to 5.83 seconds. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Message-Id: <20240622204833.5F7C74E6000@zero.eik.bme.hu> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mem_helper.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index f88155ad45..361fd72226 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -271,12 +271,11 @@ void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, } static void dcbz_common(CPUPPCState *env, target_ulong addr, - uint32_t opcode, bool epid, uintptr_t retaddr) + uint32_t opcode, int mmu_idx, uintptr_t retaddr) { target_ulong mask, dcbz_size = env->dcache_line_size; uint32_t i; void *haddr; - int mmu_idx = epid ? PPC_TLB_EPID_STORE : ppc_env_mmu_index(env, false); #if defined(TARGET_PPC64) /* Check for dcbz vs dcbzl on 970 */ @@ -309,12 +308,12 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) { - dcbz_common(env, addr, opcode, false, GETPC()); + dcbz_common(env, addr, opcode, ppc_env_mmu_index(env, false), GETPC()); } void helper_dcbzep(CPUPPCState *env, target_ulong addr, uint32_t opcode) { - dcbz_common(env, addr, opcode, true, GETPC()); + dcbz_common(env, addr, opcode, PPC_TLB_EPID_STORE, GETPC()); } void helper_icbi(CPUPPCState *env, target_ulong addr) From 521a80d895ec8ef0200dcac9b9b19e60b0cc1d1a Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 1 Jul 2024 18:17:50 -0700 Subject: [PATCH 2399/2884] target/ppc: Hoist dcbz_size out of dcbz_common The 970 logic does not apply to dcbzep, which is an e500 insn. Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/ppc/mem_helper.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 361fd72226..5067919ff8 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -271,22 +271,12 @@ void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, } static void dcbz_common(CPUPPCState *env, target_ulong addr, - uint32_t opcode, int mmu_idx, uintptr_t retaddr) + int dcbz_size, int mmu_idx, uintptr_t retaddr) { - target_ulong mask, dcbz_size = env->dcache_line_size; - uint32_t i; + target_ulong mask = ~(target_ulong)(dcbz_size - 1); void *haddr; -#if defined(TARGET_PPC64) - /* Check for dcbz vs dcbzl on 970 */ - if (env->excp_model == POWERPC_EXCP_970 && - !(opcode & 0x00200000) && ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { - dcbz_size = 32; - } -#endif - /* Align address */ - mask = ~(dcbz_size - 1); addr &= mask; /* Check reservation */ @@ -300,7 +290,7 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, memset(haddr, 0, dcbz_size); } else { /* Slow path */ - for (i = 0; i < dcbz_size; i += 8) { + for (int i = 0; i < dcbz_size; i += 8) { cpu_stq_mmuidx_ra(env, addr + i, 0, mmu_idx, retaddr); } } @@ -308,12 +298,22 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) { - dcbz_common(env, addr, opcode, ppc_env_mmu_index(env, false), GETPC()); + int dcbz_size = env->dcache_line_size; + +#if defined(TARGET_PPC64) + /* Check for dcbz vs dcbzl on 970 */ + if (env->excp_model == POWERPC_EXCP_970 && + !(opcode & 0x00200000) && ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + dcbz_size = 32; + } +#endif + + dcbz_common(env, addr, dcbz_size, ppc_env_mmu_index(env, false), GETPC()); } void helper_dcbzep(CPUPPCState *env, target_ulong addr, uint32_t opcode) { - dcbz_common(env, addr, opcode, PPC_TLB_EPID_STORE, GETPC()); + dcbz_common(env, addr, env->dcache_line_size, PPC_TLB_EPID_STORE, GETPC()); } void helper_icbi(CPUPPCState *env, target_ulong addr) From 62fe57c6d23fe8136d281f0e37ec8a9fab08b60a Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 1 Jul 2024 19:46:15 -0700 Subject: [PATCH 2400/2884] target/ppc: Split out helper_dbczl for 970 We can determine at translation time whether the insn is or is not dbczl. We must retain a runtime check against the HID5 register, but we can move that to a separate function that never affects other ppc models. Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/ppc/helper.h | 7 +++++-- target/ppc/mem_helper.c | 34 +++++++++++++++++++++------------- target/ppc/translate.c | 24 ++++++++++++++---------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 76b8f25c77..afc56855ff 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -46,8 +46,11 @@ DEF_HELPER_FLAGS_3(stmw, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_4(lsw, void, env, tl, i32, i32) DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) DEF_HELPER_FLAGS_4(stsw, TCG_CALL_NO_WG, void, env, tl, i32, i32) -DEF_HELPER_FLAGS_3(dcbz, TCG_CALL_NO_WG, void, env, tl, i32) -DEF_HELPER_FLAGS_3(dcbzep, TCG_CALL_NO_WG, void, env, tl, i32) +DEF_HELPER_FLAGS_2(dcbz, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(dcbzep, TCG_CALL_NO_WG, void, env, tl) +#ifdef TARGET_PPC64 +DEF_HELPER_FLAGS_2(dcbzl, TCG_CALL_NO_WG, void, env, tl) +#endif DEF_HELPER_FLAGS_2(icbi, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 5067919ff8..d4957efd6e 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -296,26 +296,34 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, } } -void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) +void helper_dcbz(CPUPPCState *env, target_ulong addr) { - int dcbz_size = env->dcache_line_size; - -#if defined(TARGET_PPC64) - /* Check for dcbz vs dcbzl on 970 */ - if (env->excp_model == POWERPC_EXCP_970 && - !(opcode & 0x00200000) && ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { - dcbz_size = 32; - } -#endif - - dcbz_common(env, addr, dcbz_size, ppc_env_mmu_index(env, false), GETPC()); + dcbz_common(env, addr, env->dcache_line_size, + ppc_env_mmu_index(env, false), GETPC()); } -void helper_dcbzep(CPUPPCState *env, target_ulong addr, uint32_t opcode) +void helper_dcbzep(CPUPPCState *env, target_ulong addr) { dcbz_common(env, addr, env->dcache_line_size, PPC_TLB_EPID_STORE, GETPC()); } +#ifdef TARGET_PPC64 +void helper_dcbzl(CPUPPCState *env, target_ulong addr) +{ + int dcbz_size = env->dcache_line_size; + + /* + * The translator checked for POWERPC_EXCP_970. + * All that's left is to check HID5. + */ + if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + dcbz_size = 32; + } + + dcbz_common(env, addr, dcbz_size, ppc_env_mmu_index(env, false), GETPC()); +} +#endif + void helper_icbi(CPUPPCState *env, target_ulong addr) { addr &= ~(env->dcache_line_size - 1); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 0bc16d7251..9e472ab7ef 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -178,6 +178,7 @@ struct DisasContext { /* Translation flags */ MemOp default_tcg_memop_mask; #if defined(TARGET_PPC64) + powerpc_excp_t excp_model; bool sf_mode; bool has_cfar; bool has_bhrb; @@ -4445,27 +4446,29 @@ static void gen_dcblc(DisasContext *ctx) /* dcbz */ static void gen_dcbz(DisasContext *ctx) { - TCGv tcgv_addr; - TCGv_i32 tcgv_op; + TCGv tcgv_addr = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); - tcgv_addr = tcg_temp_new(); - tcgv_op = tcg_constant_i32(ctx->opcode & 0x03FF000); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbz(tcg_env, tcgv_addr, tcgv_op); + +#ifdef TARGET_PPC64 + if (ctx->excp_model == POWERPC_EXCP_970 && !(ctx->opcode & 0x00200000)) { + gen_helper_dcbzl(tcg_env, tcgv_addr); + return; + } +#endif + + gen_helper_dcbz(tcg_env, tcgv_addr); } /* dcbzep */ static void gen_dcbzep(DisasContext *ctx) { - TCGv tcgv_addr; - TCGv_i32 tcgv_op; + TCGv tcgv_addr = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); - tcgv_addr = tcg_temp_new(); - tcgv_op = tcg_constant_i32(ctx->opcode & 0x03FF000); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbzep(tcg_env, tcgv_addr, tcgv_op); + gen_helper_dcbzep(tcg_env, tcgv_addr); } /* dst / dstt */ @@ -6486,6 +6489,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE; ctx->flags = env->flags; #if defined(TARGET_PPC64) + ctx->excp_model = env->excp_model; ctx->sf_mode = (hflags >> HFLAGS_64) & 1; ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); ctx->has_bhrb = !!(env->flags & POWERPC_FLAG_BHRB); From c6d84fd7cfb46a67c5c0404e93ed024cd3a14e6e Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 1 Jul 2024 20:10:53 -0700 Subject: [PATCH 2401/2884] target/ppc: Merge helper_{dcbz,dcbzep} Merge the two and pass the mmu_idx directly from translation. Swap the argument order in dcbz_common to avoid extra swaps. Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/ppc/helper.h | 3 +-- target/ppc/mem_helper.c | 14 ++++---------- target/ppc/translate.c | 4 ++-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index afc56855ff..4fa089cbf9 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -46,8 +46,7 @@ DEF_HELPER_FLAGS_3(stmw, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_4(lsw, void, env, tl, i32, i32) DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) DEF_HELPER_FLAGS_4(stsw, TCG_CALL_NO_WG, void, env, tl, i32, i32) -DEF_HELPER_FLAGS_2(dcbz, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_FLAGS_2(dcbzep, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_3(dcbz, TCG_CALL_NO_WG, void, env, tl, int) #ifdef TARGET_PPC64 DEF_HELPER_FLAGS_2(dcbzl, TCG_CALL_NO_WG, void, env, tl) #endif diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index d4957efd6e..24bae3b80c 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -271,7 +271,7 @@ void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, } static void dcbz_common(CPUPPCState *env, target_ulong addr, - int dcbz_size, int mmu_idx, uintptr_t retaddr) + int mmu_idx, int dcbz_size, uintptr_t retaddr) { target_ulong mask = ~(target_ulong)(dcbz_size - 1); void *haddr; @@ -296,15 +296,9 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, } } -void helper_dcbz(CPUPPCState *env, target_ulong addr) +void helper_dcbz(CPUPPCState *env, target_ulong addr, int mmu_idx) { - dcbz_common(env, addr, env->dcache_line_size, - ppc_env_mmu_index(env, false), GETPC()); -} - -void helper_dcbzep(CPUPPCState *env, target_ulong addr) -{ - dcbz_common(env, addr, env->dcache_line_size, PPC_TLB_EPID_STORE, GETPC()); + dcbz_common(env, addr, mmu_idx, env->dcache_line_size, GETPC()); } #ifdef TARGET_PPC64 @@ -320,7 +314,7 @@ void helper_dcbzl(CPUPPCState *env, target_ulong addr) dcbz_size = 32; } - dcbz_common(env, addr, dcbz_size, ppc_env_mmu_index(env, false), GETPC()); + dcbz_common(env, addr, ppc_env_mmu_index(env, false), dcbz_size, GETPC()); } #endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9e472ab7ef..cba943a49d 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -4458,7 +4458,7 @@ static void gen_dcbz(DisasContext *ctx) } #endif - gen_helper_dcbz(tcg_env, tcgv_addr); + gen_helper_dcbz(tcg_env, tcgv_addr, tcg_constant_i32(ctx->mem_idx)); } /* dcbzep */ @@ -4468,7 +4468,7 @@ static void gen_dcbzep(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_CACHE); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbzep(tcg_env, tcgv_addr); + gen_helper_dcbz(tcg_env, tcgv_addr, tcg_constant_i32(PPC_TLB_EPID_STORE)); } /* dst / dstt */ From f6bcc5b8f91e3c1b855c3078d9133f3918080276 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 16:56:48 -0700 Subject: [PATCH 2402/2884] target/ppc: Improve helper_dcbz for user-only Mark the reserve_addr check unlikely. Use tlb_vaddr_to_host instead of probe_write, relying on the memset itself to test for page writability. Use set/clear_helper_retaddr so that we can properly unwind on segfault. With this, a trivial loop around guest memset will no longer spend nearly 25% of runtime within page_get_flags. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/ppc/mem_helper.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 24bae3b80c..953dd08d5d 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -280,20 +280,27 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, addr &= mask; /* Check reservation */ - if ((env->reserve_addr & mask) == addr) { + if (unlikely((env->reserve_addr & mask) == addr)) { env->reserve_addr = (target_ulong)-1ULL; } /* Try fast path translate */ +#ifdef CONFIG_USER_ONLY + haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, mmu_idx); +#else haddr = probe_write(env, addr, dcbz_size, mmu_idx, retaddr); - if (haddr) { - memset(haddr, 0, dcbz_size); - } else { + if (unlikely(!haddr)) { /* Slow path */ for (int i = 0; i < dcbz_size; i += 8) { cpu_stq_mmuidx_ra(env, addr + i, 0, mmu_idx, retaddr); } + return; } +#endif + + set_helper_retaddr(retaddr); + memset(haddr, 0, dcbz_size); + clear_helper_retaddr(); } void helper_dcbz(CPUPPCState *env, target_ulong addr, int mmu_idx) From 814e46594da891955a6e111e2c253137fcd43f07 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 18:05:59 -0700 Subject: [PATCH 2403/2884] target/s390x: Use user_or_likely in do_access_memset Eliminate the ifdef by using a predicate that is always true with CONFIG_USER_ONLY. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/s390x/tcg/mem_helper.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 6cdbc34178..5311a15a09 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -225,10 +225,7 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, uint8_t byte, uint16_t size, int mmu_idx, uintptr_t ra) { -#ifdef CONFIG_USER_ONLY - memset(haddr, byte, size); -#else - if (likely(haddr)) { + if (user_or_likely(haddr)) { memset(haddr, byte, size); } else { MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); @@ -236,7 +233,6 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, cpu_stb_mmu(env, vaddr + i, byte, oi, ra); } } -#endif } static void access_memset(CPUS390XState *env, S390Access *desta, From 573b7783012a26ff1a88c010353ee6e85965dcdc Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 18:59:13 -0700 Subject: [PATCH 2404/2884] target/s390x: Use user_or_likely in access_memmove Invert the conditional, indent the block, and use the macro that expands to true for user-only. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/s390x/tcg/mem_helper.c | 54 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 5311a15a09..331a35b2e5 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -296,41 +296,39 @@ static void access_memmove(CPUS390XState *env, S390Access *desta, S390Access *srca, uintptr_t ra) { int len = desta->size1 + desta->size2; - int diff; assert(len == srca->size1 + srca->size2); /* Fallback to slow access in case we don't have access to all host pages */ - if (unlikely(!desta->haddr1 || (desta->size2 && !desta->haddr2) || - !srca->haddr1 || (srca->size2 && !srca->haddr2))) { - int i; + if (user_or_likely(desta->haddr1 && + srca->haddr1 && + (!desta->size2 || desta->haddr2) && + (!srca->size2 || srca->haddr2))) { + int diff = desta->size1 - srca->size1; - for (i = 0; i < len; i++) { - uint8_t byte = access_get_byte(env, srca, i, ra); - - access_set_byte(env, desta, i, byte, ra); - } - return; - } - - diff = desta->size1 - srca->size1; - if (likely(diff == 0)) { - memmove(desta->haddr1, srca->haddr1, srca->size1); - if (unlikely(srca->size2)) { - memmove(desta->haddr2, srca->haddr2, srca->size2); - } - } else if (diff > 0) { - memmove(desta->haddr1, srca->haddr1, srca->size1); - memmove(desta->haddr1 + srca->size1, srca->haddr2, diff); - if (likely(desta->size2)) { - memmove(desta->haddr2, srca->haddr2 + diff, desta->size2); + if (likely(diff == 0)) { + memmove(desta->haddr1, srca->haddr1, srca->size1); + if (unlikely(srca->size2)) { + memmove(desta->haddr2, srca->haddr2, srca->size2); + } + } else if (diff > 0) { + memmove(desta->haddr1, srca->haddr1, srca->size1); + memmove(desta->haddr1 + srca->size1, srca->haddr2, diff); + if (likely(desta->size2)) { + memmove(desta->haddr2, srca->haddr2 + diff, desta->size2); + } + } else { + diff = -diff; + memmove(desta->haddr1, srca->haddr1, desta->size1); + memmove(desta->haddr2, srca->haddr1 + desta->size1, diff); + if (likely(srca->size2)) { + memmove(desta->haddr2 + diff, srca->haddr2, srca->size2); + } } } else { - diff = -diff; - memmove(desta->haddr1, srca->haddr1, desta->size1); - memmove(desta->haddr2, srca->haddr1 + desta->size1, diff); - if (likely(srca->size2)) { - memmove(desta->haddr2 + diff, srca->haddr2, srca->size2); + for (int i = 0; i < len; i++) { + uint8_t byte = access_get_byte(env, srca, i, ra); + access_set_byte(env, desta, i, byte, ra); } } } From 2730df919093d014e96e68f1f982054da3f79e5b Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 18:40:58 -0700 Subject: [PATCH 2405/2884] target/s390x: Use set/clear_helper_retaddr in mem_helper.c Avoid a race condition with munmap in another thread. For access_memset and access_memmove, manage the value within the helper. For uses of access_{get,set}_byte, manage the value across the for loops. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/s390x/tcg/mem_helper.c | 43 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 331a35b2e5..0e12dae2aa 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -238,14 +238,14 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, static void access_memset(CPUS390XState *env, S390Access *desta, uint8_t byte, uintptr_t ra) { - + set_helper_retaddr(ra); do_access_memset(env, desta->vaddr1, desta->haddr1, byte, desta->size1, desta->mmu_idx, ra); - if (likely(!desta->size2)) { - return; + if (unlikely(desta->size2)) { + do_access_memset(env, desta->vaddr2, desta->haddr2, byte, + desta->size2, desta->mmu_idx, ra); } - do_access_memset(env, desta->vaddr2, desta->haddr2, byte, desta->size2, - desta->mmu_idx, ra); + clear_helper_retaddr(); } static uint8_t access_get_byte(CPUS390XState *env, S390Access *access, @@ -366,6 +366,8 @@ static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + set_helper_retaddr(ra); + for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca1, i, ra) & access_get_byte(env, &srca2, i, ra); @@ -373,6 +375,8 @@ static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, c |= x; access_set_byte(env, &desta, i, x, ra); } + + clear_helper_retaddr(); return c != 0; } @@ -407,6 +411,7 @@ static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, return 0; } + set_helper_retaddr(ra); for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca1, i, ra) ^ access_get_byte(env, &srca2, i, ra); @@ -414,6 +419,7 @@ static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, c |= x; access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); return c != 0; } @@ -441,6 +447,8 @@ static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + set_helper_retaddr(ra); + for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca1, i, ra) | access_get_byte(env, &srca2, i, ra); @@ -448,6 +456,8 @@ static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, c |= x; access_set_byte(env, &desta, i, x, ra); } + + clear_helper_retaddr(); return c != 0; } @@ -484,11 +494,13 @@ static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest, } else if (!is_destructive_overlap(env, dest, src, l)) { access_memmove(env, &desta, &srca, ra); } else { + set_helper_retaddr(ra); for (i = 0; i < l; i++) { uint8_t byte = access_get_byte(env, &srca, i, ra); access_set_byte(env, &desta, i, byte, ra); } + clear_helper_retaddr(); } return env->cc_op; @@ -514,10 +526,12 @@ void HELPER(mvcrl)(CPUS390XState *env, uint64_t l, uint64_t dest, uint64_t src) access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + set_helper_retaddr(ra); for (i = l - 1; i >= 0; i--) { uint8_t byte = access_get_byte(env, &srca, i, ra); access_set_byte(env, &desta, i, byte, ra); } + clear_helper_retaddr(); } /* move inverse */ @@ -534,11 +548,13 @@ void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) src = wrap_address(env, src - l + 1); access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + + set_helper_retaddr(ra); for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca, l - i - 1, ra); - access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); } /* move numerics */ @@ -555,12 +571,15 @@ void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + + set_helper_retaddr(ra); for (i = 0; i < l; i++) { const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0x0f) | (access_get_byte(env, &srca2, i, ra) & 0xf0); access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); } /* move with offset */ @@ -580,6 +599,8 @@ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* Handle rightmost byte */ byte_dest = cpu_ldub_data_ra(env, dest + len_dest - 1, ra); + + set_helper_retaddr(ra); byte_src = access_get_byte(env, &srca, len_src - 1, ra); byte_dest = (byte_dest & 0x0f) | (byte_src << 4); access_set_byte(env, &desta, len_dest - 1, byte_dest, ra); @@ -595,6 +616,7 @@ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) byte_dest |= byte_src << 4; access_set_byte(env, &desta, i, byte_dest, ra); } + clear_helper_retaddr(); } /* move zones */ @@ -611,12 +633,15 @@ void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + + set_helper_retaddr(ra); for (i = 0; i < l; i++) { const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0xf0) | (access_get_byte(env, &srca2, i, ra) & 0x0f); access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); } /* compare unsigned byte arrays */ @@ -961,15 +986,19 @@ uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2) */ access_prepare(&srca, env, s, len, MMU_DATA_LOAD, mmu_idx, ra); access_prepare(&desta, env, d, len, MMU_DATA_STORE, mmu_idx, ra); + + set_helper_retaddr(ra); for (i = 0; i < len; i++) { const uint8_t v = access_get_byte(env, &srca, i, ra); access_set_byte(env, &desta, i, v, ra); if (v == c) { + clear_helper_retaddr(); set_address_zero(env, r1, d + i); return 1; } } + clear_helper_retaddr(); set_address_zero(env, r1, d + len); set_address_zero(env, r2, s + len); return 3; @@ -1060,6 +1089,7 @@ static inline uint32_t do_mvcl(CPUS390XState *env, *dest = wrap_address(env, *dest + len); } else { access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); + set_helper_retaddr(ra); /* The remaining length selects the padding byte. */ for (i = 0; i < len; (*destlen)--, i++) { @@ -1069,6 +1099,7 @@ static inline uint32_t do_mvcl(CPUS390XState *env, access_set_byte(env, &desta, i, pad >> 8, ra); } } + clear_helper_retaddr(); *dest = wrap_address(env, *dest + len); } From 3f57638a7eae5b56f65224c680654a2aaaa09379 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 9 Jul 2024 20:10:55 -0700 Subject: [PATCH 2406/2884] target/riscv: Simplify probing in vext_ldff The current pairing of tlb_vaddr_to_host with extra is either inefficient (user-only, with page_check_range) or incorrect (system, with probe_pages). For proper non-fault behaviour, use probe_access_flags with its nonfault parameter set to true. Reviewed-by: Max Chou <max.chou@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/riscv/vector_helper.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 1b4d5a8e37..10a52ceb5b 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -474,7 +474,6 @@ vext_ldff(void *vd, void *v0, target_ulong base, vext_ldst_elem_fn *ldst_elem, uint32_t log2_esz, uintptr_t ra) { - void *host; uint32_t i, k, vl = 0; uint32_t nf = vext_nf(desc); uint32_t vm = vext_vm(desc); @@ -493,27 +492,31 @@ vext_ldff(void *vd, void *v0, target_ulong base, } addr = adjust_addr(env, base + i * (nf << log2_esz)); if (i == 0) { + /* Allow fault on first element. */ probe_pages(env, addr, nf << log2_esz, ra, MMU_DATA_LOAD); } else { - /* if it triggers an exception, no need to check watchpoint */ remain = nf << log2_esz; while (remain > 0) { + void *host; + int flags; + offset = -(addr | TARGET_PAGE_MASK); - host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, mmu_index); - if (host) { -#ifdef CONFIG_USER_ONLY - if (!page_check_range(addr, offset, PAGE_READ)) { - vl = i; - goto ProbeSuccess; - } -#else - probe_pages(env, addr, offset, ra, MMU_DATA_LOAD); -#endif - } else { + + /* Probe nonfault on subsequent elements. */ + flags = probe_access_flags(env, addr, offset, MMU_DATA_LOAD, + mmu_index, true, &host, 0); + + /* + * Stop if invalid (unmapped) or mmio (transaction may fail). + * Do not stop if watchpoint, as the spec says that + * first-fault should continue to access the same + * elements regardless of any watchpoint. + */ + if (flags & ~TLB_WATCHPOINT) { vl = i; goto ProbeSuccess; } - if (remain <= offset) { + if (remain <= offset) { break; } remain -= offset; From 8e326d36dd16b91d9abc4963b5f75b8f637c2312 Mon Sep 17 00:00:00 2001 From: Dehan Meng <demeng@redhat.com> Date: Thu, 13 Jun 2024 17:28:02 +0800 Subject: [PATCH 2407/2884] qga/linux: Add new api 'guest-network-get-route' The Route information of the Linux VM needs to be used by administrators and users when debugging network problems and troubleshooting. Signed-off-by: Dehan Meng <demeng@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Message-ID: <20240613092802.346246-2-demeng@redhat.com> Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com> --- qga/commands-linux.c | 133 +++++++++++++++++++++++++++++++++++++++++++ qga/qapi-schema.json | 73 ++++++++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 89bdcded01..51d5e3d927 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -28,6 +28,10 @@ #include <libudev.h> #endif +#ifdef HAVE_GETIFADDRS +#include <net/if.h> +#endif + #include <sys/statvfs.h> #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) @@ -2089,3 +2093,132 @@ GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) fclose(fp); return head; } + +static char *hexToIPAddress(const void *hexValue, int is_ipv6) +{ + if (is_ipv6) { + char addr[INET6_ADDRSTRLEN]; + struct in6_addr in6; + const char *hexStr = (const char *)hexValue; + int i; + + for (i = 0; i < 16; i++) { + sscanf(&hexStr[i * 2], "%02hhx", &in6.s6_addr[i]); + } + inet_ntop(AF_INET6, &in6, addr, INET6_ADDRSTRLEN); + + return g_strdup(addr); + } else { + unsigned int hexInt = *(unsigned int *)hexValue; + unsigned int byte1 = (hexInt >> 24) & 0xFF; + unsigned int byte2 = (hexInt >> 16) & 0xFF; + unsigned int byte3 = (hexInt >> 8) & 0xFF; + unsigned int byte4 = hexInt & 0xFF; + + return g_strdup_printf("%u.%u.%u.%u", byte4, byte3, byte2, byte1); + } +} + +GuestNetworkRouteList *qmp_guest_network_get_route(Error **errp) +{ + GuestNetworkRouteList *head = NULL, **tail = &head; + const char *routeFiles[] = {"/proc/net/route", "/proc/net/ipv6_route"}; + FILE *fp; + size_t n; + char *line = NULL; + int firstLine; + int is_ipv6; + int i; + + for (i = 0; i < 2; i++) { + firstLine = 1; + is_ipv6 = (i == 1); + fp = fopen(routeFiles[i], "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", routeFiles[i]); + free(line); + continue; + } + + while (getline(&line, &n, fp) != -1) { + if (firstLine && !is_ipv6) { + firstLine = 0; + continue; + } + GuestNetworkRoute *route = NULL; + GuestNetworkRoute *networkroute; + char Iface[IFNAMSIZ]; + if (is_ipv6) { + char Destination[33], Source[33], NextHop[33]; + int DesPrefixlen, SrcPrefixlen, Metric, RefCnt, Use, Flags; + + /* Parse the line and extract the values */ + if (sscanf(line, "%32s %x %32s %x %32s %x %x %x %x %s", + Destination, &DesPrefixlen, Source, + &SrcPrefixlen, NextHop, &Metric, &RefCnt, + &Use, &Flags, Iface) != 10) { + continue; + } + + route = g_new0(GuestNetworkRoute, 1); + networkroute = route; + networkroute->iface = g_strdup(Iface); + networkroute->destination = hexToIPAddress(Destination, 1); + networkroute->metric = Metric; + networkroute->source = hexToIPAddress(Source, 1); + networkroute->desprefixlen = g_strdup_printf( + "%d", DesPrefixlen + ); + networkroute->srcprefixlen = g_strdup_printf( + "%d", SrcPrefixlen + ); + networkroute->nexthop = hexToIPAddress(NextHop, 1); + networkroute->has_flags = true; + networkroute->flags = Flags; + networkroute->has_refcnt = true; + networkroute->refcnt = RefCnt; + networkroute->has_use = true; + networkroute->use = Use; + networkroute->version = 6; + } else { + unsigned int Destination, Gateway, Mask, Flags; + int RefCnt, Use, Metric, MTU, Window, IRTT; + + /* Parse the line and extract the values */ + if (sscanf(line, "%s %X %X %x %d %d %d %X %d %d %d", + Iface, &Destination, &Gateway, &Flags, &RefCnt, + &Use, &Metric, &Mask, &MTU, &Window, &IRTT) != 11) { + continue; + } + + route = g_new0(GuestNetworkRoute, 1); + networkroute = route; + networkroute->iface = g_strdup(Iface); + networkroute->destination = hexToIPAddress(&Destination, 0); + networkroute->gateway = hexToIPAddress(&Gateway, 0); + networkroute->mask = hexToIPAddress(&Mask, 0); + networkroute->metric = Metric; + networkroute->has_flags = true; + networkroute->flags = Flags; + networkroute->has_refcnt = true; + networkroute->refcnt = RefCnt; + networkroute->has_use = true; + networkroute->use = Use; + networkroute->has_mtu = true; + networkroute->mtu = MTU; + networkroute->has_window = true; + networkroute->window = Window; + networkroute->has_irtt = true; + networkroute->irtt = IRTT; + networkroute->version = 4; + } + + QAPI_LIST_APPEND(tail, route); + } + + free(line); + fclose(fp); + } + + return head; +} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index c763163fcd..495706cf73 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1851,3 +1851,76 @@ 'returns': ['GuestCpuStats'], 'if': 'CONFIG_LINUX' } + +## +# @GuestNetworkRoute: +# +# Route information, currently, only linux supported. +# +# @iface: The destination network or host's egress network interface in the routing table +# +# @destination: The IP address of the target network or host, The final destination of the packet +# +# @metric: Route metric +# +# @gateway: The IP address of the next hop router +# +# @mask: Subnet Mask (IPv4 only) +# +# @irtt: Initial round-trip delay (not for windows, IPv4 only) +# +# @flags: Route flags (not for windows) +# +# @refcnt: The route's reference count (not for windows) +# +# @use: Route usage count (not for windows) +# +# @window: TCP window size, used for flow control (not for windows, IPv4 only) +# +# @mtu: Data link layer maximum packet size (not for windows) +# +# @desprefixlen: Destination prefix length (for IPv6) +# +# @source: Source IP address (for IPv6) +# +# @srcprefixlen: Source prefix length (for IPv6) +# +# @nexthop: Next hop IP address (for IPv6) +# +# @version: IP version (4 or 6) +# +# Since: 9.1 + +## +{ 'struct': 'GuestNetworkRoute', + 'data': {'iface': 'str', + 'destination': 'str', + 'metric': 'int', + '*gateway': 'str', + '*mask': 'str', + '*irtt': 'int', + '*flags': 'uint64', + '*refcnt': 'int', + '*use': 'int', + '*window': 'int', + '*mtu': 'int', + '*desprefixlen': 'str', + '*source': 'str', + '*srcprefixlen': 'str', + '*nexthop': 'str', + 'version': 'int' + }, + 'if': 'CONFIG_LINUX' } + +## +# @guest-network-get-route: +# +# Retrieve information about route of network. +# Returns: List of route info of guest. +# +# Since: 9.1 +## +{ 'command': 'guest-network-get-route', + 'returns': ['GuestNetworkRoute'], + 'if': 'CONFIG_LINUX' +} From 07321a6d087d4ec9866cfb0c8b53692a59758976 Mon Sep 17 00:00:00 2001 From: Eric Auger <eric.auger@redhat.com> Date: Fri, 19 Jul 2024 18:50:11 +0200 Subject: [PATCH 2408/2884] hw/vfio/container: Fix SIGSEV on vfio_container_instance_finalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In vfio_connect_container's error path, the base container is removed twice form the VFIOAddressSpace QLIST: first on the listener_release_exit label and second, on free_container_exit label, through object_unref(container), which calls vfio_container_instance_finalize(). Let's remove the first instance. Fixes: 938026053f4 ("vfio/container: Switch to QOM") Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- hw/vfio/container.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 38a9df3496..ce9a858e56 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -656,7 +656,6 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, return true; listener_release_exit: QLIST_REMOVE(group, container_next); - QLIST_REMOVE(bcontainer, next); vfio_kvm_device_del_group(group); memory_listener_unregister(&bcontainer->listener); if (vioc->release) { From 13e522f644e2b15fa857028a33e6a3b75e45158d Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Fri, 19 Jul 2024 13:04:49 +0100 Subject: [PATCH 2409/2884] vfio/pci: Extract mdev check into an helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to skip initialization of the HostIOMMUDevice for mdev, extract the checks that validate if a device is an mdev into helpers. A vfio_device_is_mdev() is created, and subsystems consult VFIODevice::mdev to check if it's mdev or not. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- hw/vfio/helpers.c | 14 ++++++++++++++ hw/vfio/pci.c | 12 +++--------- include/hw/vfio/vfio-common.h | 2 ++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index b14edd46ed..7e23e9080c 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -675,3 +675,17 @@ int vfio_device_get_aw_bits(VFIODevice *vdev) return HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX; } + +bool vfio_device_is_mdev(VFIODevice *vbasedev) +{ + g_autofree char *subsys = NULL; + g_autofree char *tmp = NULL; + + if (!vbasedev->sysfsdev) { + return false; + } + + tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); + subsys = realpath(tmp, NULL); + return subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index e03d9f3ba5..b34e91468a 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2963,12 +2963,9 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) ERRP_GUARD(); VFIOPCIDevice *vdev = VFIO_PCI(pdev); VFIODevice *vbasedev = &vdev->vbasedev; - char *subsys; int i, ret; - bool is_mdev; char uuid[UUID_STR_LEN]; g_autofree char *name = NULL; - g_autofree char *tmp = NULL; if (vbasedev->fd < 0 && !vbasedev->sysfsdev) { if (!(~vdev->host.domain || ~vdev->host.bus || @@ -2997,14 +2994,11 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) * stays in sync with the active working set of the guest driver. Prevent * the x-balloon-allowed option unless this is minimally an mdev device. */ - tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); - subsys = realpath(tmp, NULL); - is_mdev = subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); - free(subsys); + vbasedev->mdev = vfio_device_is_mdev(vbasedev); - trace_vfio_mdev(vbasedev->name, is_mdev); + trace_vfio_mdev(vbasedev->name, vbasedev->mdev); - if (vbasedev->ram_block_discard_allowed && !is_mdev) { + if (vbasedev->ram_block_discard_allowed && !vbasedev->mdev) { error_setg(errp, "x-balloon-allowed only potentially compatible " "with mdev devices"); goto error; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index e8ddf92bb1..98acae8c1c 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -116,6 +116,7 @@ typedef struct VFIODevice { DeviceState *dev; int fd; int type; + bool mdev; bool reset_works; bool needs_reset; bool no_mmap; @@ -231,6 +232,7 @@ void vfio_region_exit(VFIORegion *region); void vfio_region_finalize(VFIORegion *region); void vfio_reset_handler(void *opaque); struct vfio_device_info *vfio_get_device_info(int fd); +bool vfio_device_is_mdev(VFIODevice *vbasedev); bool vfio_attach_device(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); void vfio_detach_device(VFIODevice *vbasedev); From 9f17604195c03580b16c3cc5631c86f3894dd442 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Fri, 19 Jul 2024 13:04:50 +0100 Subject: [PATCH 2410/2884] vfio/iommufd: Don't initialize nor set a HOST_IOMMU_DEVICE with mdev mdevs aren't "physical" devices and when asking for backing IOMMU info, it fails the entire provisioning of the guest. Fix that by skipping HostIOMMUDevice initialization in the presence of mdevs, and skip setting an iommu device when it is known to be an mdev. Cc: Zhenzhong Duan <zhenzhong.duan@intel.com> Fixes: 930589520128 ("vfio/iommufd: Implement HostIOMMUDeviceClass::realize() handler") Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- hw/vfio/common.c | 4 ++++ hw/vfio/pci.c | 11 ++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 6d15b36e0b..d7f02be595 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1548,6 +1548,10 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, return false; } + if (vbasedev->mdev) { + return true; + } + hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); if (!HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp)) { object_unref(hiod); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b34e91468a..265d3cb82f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3115,7 +3115,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bars_register(vdev); - if (!pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { + if (!vbasedev->mdev && + !pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { error_prepend(errp, "Failed to set iommu_device: "); goto out_teardown; } @@ -3238,7 +3239,9 @@ out_deregister: timer_free(vdev->intx.mmap_timer); } out_unset_idev: - pci_device_unset_iommu_device(pdev); + if (!vbasedev->mdev) { + pci_device_unset_iommu_device(pdev); + } out_teardown: vfio_teardown_msi(vdev); vfio_bars_exit(vdev); @@ -3283,7 +3286,9 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_pci_disable_rp_atomics(vdev); vfio_bars_exit(vdev); vfio_migration_exit(vbasedev); - pci_device_unset_iommu_device(pdev); + if (!vbasedev->mdev) { + pci_device_unset_iommu_device(pdev); + } } static void vfio_pci_reset(DeviceState *dev) From 2d1bf2589736b3714f3940d360404732ac13019c Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Fri, 19 Jul 2024 13:04:51 +0100 Subject: [PATCH 2411/2884] backends/iommufd: Extend iommufd_backend_get_device_info() to fetch HW capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The helper will be able to fetch vendor agnostic IOMMU capabilities supported both by hardware and software. Right now it is only iommu dirty tracking. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- backends/iommufd.c | 4 +++- hw/vfio/iommufd.c | 4 +++- include/sysemu/iommufd.h | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index cabd1b5002..48dfd39624 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -209,7 +209,7 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, - Error **errp) + uint64_t *caps, Error **errp) { struct iommu_hw_info info = { .size = sizeof(info), @@ -225,6 +225,8 @@ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, g_assert(type); *type = info.out_data_type; + g_assert(caps); + *caps = info.out_capabilities; return true; } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7b5f87a148..7c1b9e0284 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -628,11 +628,13 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, union { struct iommu_hw_info_vtd vtd; } data; + uint64_t hw_caps; hiod->agent = opaque; if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, - &type, &data, sizeof(data), errp)) { + &type, &data, sizeof(data), + &hw_caps, errp)) { return false; } diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index 9edfec6045..57d502a1c7 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -49,7 +49,7 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size); bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, - Error **errp); + uint64_t *caps, Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From b07dcb7d4f116b157de5aaacfa9d58a496a9ed1d Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Fri, 19 Jul 2024 13:04:52 +0100 Subject: [PATCH 2412/2884] vfio/iommufd: Return errno in iommufd_cdev_attach_ioas_hwpt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to implement auto domains have the attach function return the errno it got during domain attach instead of a bool. -EINVAL is tracked to track domain incompatibilities, and decide whether to create a new IOMMU domain. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- hw/vfio/iommufd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7c1b9e0284..7390621ee9 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -172,7 +172,7 @@ out: return ret; } -static bool iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, +static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, Error **errp) { int iommufd = vbasedev->iommufd->fd; @@ -187,12 +187,12 @@ static bool iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, error_setg_errno(errp, errno, "[iommufd=%d] error attach %s (%d) to id=%d", iommufd, vbasedev->name, vbasedev->fd, id); - return false; + return -errno; } trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name, vbasedev->fd, id); - return true; + return 0; } static bool iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) @@ -216,7 +216,7 @@ static bool iommufd_cdev_attach_container(VFIODevice *vbasedev, VFIOIOMMUFDContainer *container, Error **errp) { - return iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); + return !iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); } static void iommufd_cdev_detach_container(VFIODevice *vbasedev, From c598d65aef4016b80608c93e2fba2c46d4c5e42e Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan <zhenzhong.duan@intel.com> Date: Mon, 22 Jul 2024 15:07:12 +0800 Subject: [PATCH 2413/2884] vfio/ap: Don't initialize HOST_IOMMU_DEVICE with mdev mdevs aren't "physical" devices and when asking for backing IOMMU info, it fails the entire provisioning of the guest. Fix that by setting vbasedev->mdev true so skipping HostIOMMUDevice initialization in the presence of mdevs. Fixes: 930589520128 ("vfio/iommufd: Implement HostIOMMUDeviceClass::realize() handler") Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- hw/vfio/ap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 0c4354e3e7..71bf32b83c 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -230,6 +230,9 @@ static void vfio_ap_instance_init(Object *obj) */ vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_AP, &vfio_ap_ops, DEVICE(vapdev), true); + + /* AP device is mdev type device */ + vbasedev->mdev = true; } #ifdef CONFIG_IOMMUFD From 8b8705e7f20afa545862fcae15c1515b8a832d2d Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan <zhenzhong.duan@intel.com> Date: Mon, 22 Jul 2024 15:07:13 +0800 Subject: [PATCH 2414/2884] vfio/ccw: Don't initialize HOST_IOMMU_DEVICE with mdev mdevs aren't "physical" devices and when asking for backing IOMMU info, it fails the entire provisioning of the guest. Fix that by setting vbasedev->mdev true so skipping HostIOMMUDevice initialization in the presence of mdevs. Fixes: 930589520128 ("vfio/iommufd: Implement HostIOMMUDeviceClass::realize() handler") Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Joao Martins <joao.m.martins@oracle.com> Acked-by: Eric Farman <farman@linux.ibm.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- hw/vfio/ccw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 1f8e1272c7..115862f430 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -675,6 +675,9 @@ static void vfio_ccw_instance_init(Object *obj) VFIOCCWDevice *vcdev = VFIO_CCW(obj); VFIODevice *vbasedev = &vcdev->vdev; + /* CCW device is mdev type device */ + vbasedev->mdev = true; + /* * All vfio-ccw devices are believed to operate in a way compatible with * discarding of memory in RAM blocks, ie. pages pinned in the host are From 5b1e96e654036a65d3edb2671281e1a30c6f5ce7 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:18 +0100 Subject: [PATCH 2415/2884] vfio/iommufd: Introduce auto domain creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's generally two modes of operation for IOMMUFD: 1) The simple user API which intends to perform relatively simple things with IOMMUs e.g. DPDK. The process generally creates an IOAS and attaches to VFIO and mainly performs IOAS_MAP and UNMAP. 2) The native IOMMUFD API where you have fine grained control of the IOMMU domain and model it accordingly. This is where most new feature are being steered to. For dirty tracking 2) is required, as it needs to ensure that the stage-2/parent IOMMU domain will only attach devices that support dirty tracking (so far it is all homogeneous in x86, likely not the case for smmuv3). Such invariant on dirty tracking provides a useful guarantee to VMMs that will refuse incompatible device attachments for IOMMU domains. Dirty tracking insurance is enforced via HWPT_ALLOC, which is responsible for creating an IOMMU domain. This is contrast to the 'simple API' where the IOMMU domain is created by IOMMUFD automatically when it attaches to VFIO (usually referred as autodomains) but it has the needed handling for mdevs. To support dirty tracking with the advanced IOMMUFD API, it needs similar logic, where IOMMU domains are created and devices attached to compatible domains. Essentially mimicking kernel iommufd_device_auto_get_domain(). With mdevs given there's no IOMMU domain it falls back to IOAS attach. The auto domain logic allows different IOMMU domains to be created when DMA dirty tracking is not desired (and VF can provide it), and others where it is. Here it is not used in this way given how VFIODevice migration state is initialized after the device attachment. But such mixed mode of IOMMU dirty tracking + device dirty tracking is an improvement that can be added on. Keep the 'all of nothing' of type1 approach that we have been using so far between container vs device dirty tracking. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> [ clg: Added ERRP_GUARD() in iommufd_cdev_autodomains_get() ] Signed-off-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- backends/iommufd.c | 30 +++++++++++++ backends/trace-events | 1 + hw/vfio/iommufd.c | 85 +++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 9 ++++ include/sysemu/iommufd.h | 5 +++ 5 files changed, 130 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index 48dfd39624..60a3d14bfa 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -207,6 +207,36 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, return ret; } +bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, + uint32_t pt_id, uint32_t flags, + uint32_t data_type, uint32_t data_len, + void *data_ptr, uint32_t *out_hwpt, + Error **errp) +{ + int ret, fd = be->fd; + struct iommu_hwpt_alloc alloc_hwpt = { + .size = sizeof(struct iommu_hwpt_alloc), + .flags = flags, + .dev_id = dev_id, + .pt_id = pt_id, + .data_type = data_type, + .data_len = data_len, + .data_uptr = (uintptr_t)data_ptr, + }; + + ret = ioctl(fd, IOMMU_HWPT_ALLOC, &alloc_hwpt); + trace_iommufd_backend_alloc_hwpt(fd, dev_id, pt_id, flags, data_type, + data_len, (uintptr_t)data_ptr, + alloc_hwpt.out_hwpt_id, ret); + if (ret) { + error_setg_errno(errp, errno, "Failed to allocate hwpt"); + return false; + } + + *out_hwpt = alloc_hwpt.out_hwpt_id; + return true; +} + bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, uint64_t *caps, Error **errp) diff --git a/backends/trace-events b/backends/trace-events index 211e6f374a..4d8ac02fe7 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -14,4 +14,5 @@ iommufd_backend_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d" +iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" out_hwpt=%u (%d)" iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7390621ee9..58c11c9308 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -212,10 +212,89 @@ static bool iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) return true; } +static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container, + Error **errp) +{ + ERRP_GUARD(); + IOMMUFDBackend *iommufd = vbasedev->iommufd; + uint32_t flags = 0; + VFIOIOASHwpt *hwpt; + uint32_t hwpt_id; + int ret; + + /* Try to find a domain */ + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); + if (ret) { + /* -EINVAL means the domain is incompatible with the device. */ + if (ret == -EINVAL) { + /* + * It is an expected failure and it just means we will try + * another domain, or create one if no existing compatible + * domain is found. Hence why the error is discarded below. + */ + error_free(*errp); + *errp = NULL; + continue; + } + + return false; + } else { + vbasedev->hwpt = hwpt; + QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); + return true; + } + } + + if (!iommufd_backend_alloc_hwpt(iommufd, vbasedev->devid, + container->ioas_id, flags, + IOMMU_HWPT_DATA_NONE, 0, NULL, + &hwpt_id, errp)) { + return false; + } + + hwpt = g_malloc0(sizeof(*hwpt)); + hwpt->hwpt_id = hwpt_id; + QLIST_INIT(&hwpt->device_list); + + ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); + if (ret) { + iommufd_backend_free_id(container->be, hwpt->hwpt_id); + g_free(hwpt); + return false; + } + + vbasedev->hwpt = hwpt; + QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); + QLIST_INSERT_HEAD(&container->hwpt_list, hwpt, next); + return true; +} + +static void iommufd_cdev_autodomains_put(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container) +{ + VFIOIOASHwpt *hwpt = vbasedev->hwpt; + + QLIST_REMOVE(vbasedev, hwpt_next); + vbasedev->hwpt = NULL; + + if (QLIST_EMPTY(&hwpt->device_list)) { + QLIST_REMOVE(hwpt, next); + iommufd_backend_free_id(container->be, hwpt->hwpt_id); + g_free(hwpt); + } +} + static bool iommufd_cdev_attach_container(VFIODevice *vbasedev, VFIOIOMMUFDContainer *container, Error **errp) { + /* mdevs aren't physical devices and will fail with auto domains */ + if (!vbasedev->mdev) { + return iommufd_cdev_autodomains_get(vbasedev, container, errp); + } + return !iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); } @@ -227,6 +306,11 @@ static void iommufd_cdev_detach_container(VFIODevice *vbasedev, if (!iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) { error_report_err(err); } + + if (vbasedev->hwpt) { + iommufd_cdev_autodomains_put(vbasedev, container); + } + } static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) @@ -354,6 +438,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, container = VFIO_IOMMU_IOMMUFD(object_new(TYPE_VFIO_IOMMU_IOMMUFD)); container->be = vbasedev->iommufd; container->ioas_id = ioas_id; + QLIST_INIT(&container->hwpt_list); bcontainer = &container->bcontainer; vfio_address_space_insert(space, bcontainer); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 98acae8c1c..1a96678f8c 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -95,10 +95,17 @@ typedef struct VFIOHostDMAWindow { typedef struct IOMMUFDBackend IOMMUFDBackend; +typedef struct VFIOIOASHwpt { + uint32_t hwpt_id; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOIOASHwpt) next; +} VFIOIOASHwpt; + typedef struct VFIOIOMMUFDContainer { VFIOContainerBase bcontainer; IOMMUFDBackend *be; uint32_t ioas_id; + QLIST_HEAD(, VFIOIOASHwpt) hwpt_list; } VFIOIOMMUFDContainer; OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); @@ -135,6 +142,8 @@ typedef struct VFIODevice { HostIOMMUDevice *hiod; int devid; IOMMUFDBackend *iommufd; + VFIOIOASHwpt *hwpt; + QLIST_ENTRY(VFIODevice) hwpt_next; } VFIODevice; struct VFIODeviceOps { diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index 57d502a1c7..e917e7591d 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -50,6 +50,11 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, uint64_t *caps, Error **errp); +bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, + uint32_t pt_id, uint32_t flags, + uint32_t data_type, uint32_t data_len, + void *data_ptr, uint32_t *out_hwpt, + Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From 6c635326425091e164b563a7ce96408ef74ff2ec Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:19 +0100 Subject: [PATCH 2416/2884] vfio/{iommufd,container}: Remove caps::aw_bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove caps::aw_bits which requires the bcontainer::iova_ranges being initialized after device is actually attached. Instead defer that to .get_cap() and call vfio_device_get_aw_bits() directly. This is in preparation for HostIOMMUDevice::realize() being called early during attach_device(). Suggested-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- backends/iommufd.c | 3 ++- hw/vfio/container.c | 5 +---- hw/vfio/iommufd.c | 1 - include/sysemu/host_iommu_device.h | 3 --- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index 60a3d14bfa..06b135111f 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "monitor/monitor.h" #include "trace.h" +#include "hw/vfio/vfio-common.h" #include <sys/ioctl.h> #include <linux/iommufd.h> @@ -269,7 +270,7 @@ static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) case HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE: return caps->type; case HOST_IOMMU_DEVICE_CAP_AW_BITS: - return caps->aw_bits; + return vfio_device_get_aw_bits(hiod->agent); default: error_setg(errp, "%s: unsupported capability %x", hiod->name, cap); return -EINVAL; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index ce9a858e56..10cb4b4320 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1141,7 +1141,6 @@ static bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque, VFIODevice *vdev = opaque; hiod->name = g_strdup(vdev->name); - hiod->caps.aw_bits = vfio_device_get_aw_bits(vdev); hiod->agent = opaque; return true; @@ -1150,11 +1149,9 @@ static bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque, static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) { - HostIOMMUDeviceCaps *caps = &hiod->caps; - switch (cap) { case HOST_IOMMU_DEVICE_CAP_AW_BITS: - return caps->aw_bits; + return vfio_device_get_aw_bits(hiod->agent); default: error_setg(errp, "%s: unsupported capability %x", hiod->name, cap); return -EINVAL; diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 58c11c9308..f1e7cf3e9c 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -725,7 +725,6 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, hiod->name = g_strdup(vdev->name); caps->type = type; - caps->aw_bits = vfio_device_get_aw_bits(vdev); return true; } diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index c1bf74ae2c..d1c10ff7c2 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -19,12 +19,9 @@ * struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities. * * @type: host platform IOMMU type. - * - * @aw_bits: host IOMMU address width. 0xff if no limitation. */ typedef struct HostIOMMUDeviceCaps { uint32_t type; - uint8_t aw_bits; } HostIOMMUDeviceCaps; #define TYPE_HOST_IOMMU_DEVICE "host-iommu-device" From 21e8d3a3aa1627079088e2430946dea62bf5c703 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:20 +0100 Subject: [PATCH 2417/2884] vfio/iommufd: Add hw_caps field to HostIOMMUDeviceCaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store the value of @caps returned by iommufd_backend_get_device_info() in a new field HostIOMMUDeviceCaps::hw_caps. Right now the only value is whether device IOMMU supports dirty tracking (IOMMU_HW_CAP_DIRTY_TRACKING). This is in preparation for HostIOMMUDevice::realize() being called early during attach_device(). Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- hw/vfio/iommufd.c | 1 + include/sysemu/host_iommu_device.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index f1e7cf3e9c..fb87e64e44 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -725,6 +725,7 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, hiod->name = g_strdup(vdev->name); caps->type = type; + caps->hw_caps = hw_caps; return true; } diff --git a/include/sysemu/host_iommu_device.h b/include/sysemu/host_iommu_device.h index d1c10ff7c2..809cced4ba 100644 --- a/include/sysemu/host_iommu_device.h +++ b/include/sysemu/host_iommu_device.h @@ -19,9 +19,13 @@ * struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities. * * @type: host platform IOMMU type. + * + * @hw_caps: host platform IOMMU capabilities (e.g. on IOMMUFD this represents + * the @out_capabilities value returned from IOMMU_GET_HW_INFO ioctl) */ typedef struct HostIOMMUDeviceCaps { uint32_t type; + uint64_t hw_caps; } HostIOMMUDeviceCaps; #define TYPE_HOST_IOMMU_DEVICE "host-iommu-device" From 83a4d596a93bc22ace521789979f5ccdcd91ab96 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:21 +0100 Subject: [PATCH 2418/2884] vfio/{iommufd, container}: Invoke HostIOMMUDevice::realize() during attach_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the HostIOMMUDevice::realize() to be invoked during the attach of the device before we allocate IOMMUFD hardware pagetable objects (HWPT). This allows the use of the hw_caps obtained by IOMMU_GET_HW_INFO that essentially tell if the IOMMU behind the device supports dirty tracking. Note: The HostIOMMUDevice data from legacy backend is static and doesn't need any information from the (type1-iommu) backend to be initialized. In contrast however, the IOMMUFD HostIOMMUDevice data requires the iommufd FD to be connected and having a devid to be able to successfully GET_HW_INFO. This means vfio_device_hiod_realize() is called in different places within the backend .attach_device() implementation. Suggested-by: Cédric Le Goater <clg@redhat.cm> Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> [ clg: Fixed error handling in iommufd_cdev_attach() ] Signed-off-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- hw/vfio/common.c | 20 ++++++++------------ hw/vfio/container.c | 4 ++++ hw/vfio/helpers.c | 11 +++++++++++ hw/vfio/iommufd.c | 11 +++++++++++ include/hw/vfio/vfio-common.h | 1 + 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d7f02be595..26e74fa430 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1536,7 +1536,7 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, { const VFIOIOMMUClass *ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); - HostIOMMUDevice *hiod; + HostIOMMUDevice *hiod = NULL; if (vbasedev->iommufd) { ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); @@ -1544,21 +1544,17 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, assert(ops); + + if (!vbasedev->mdev) { + hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); + vbasedev->hiod = hiod; + } + if (!ops->attach_device(name, vbasedev, as, errp)) { - return false; - } - - if (vbasedev->mdev) { - return true; - } - - hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); - if (!HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp)) { object_unref(hiod); - ops->detach_device(vbasedev); + vbasedev->hiod = NULL; return false; } - vbasedev->hiod = hiod; return true; } diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 10cb4b4320..9ccdb639ac 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -914,6 +914,10 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, trace_vfio_attach_device(vbasedev->name, groupid); + if (!vfio_device_hiod_realize(vbasedev, errp)) { + return false; + } + group = vfio_get_group(groupid, as, errp); if (!group) { return false; diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 7e23e9080c..ea15c79db0 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -689,3 +689,14 @@ bool vfio_device_is_mdev(VFIODevice *vbasedev) subsys = realpath(tmp, NULL); return subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); } + +bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) +{ + HostIOMMUDevice *hiod = vbasedev->hiod; + + if (!hiod) { + return true; + } + + return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); +} diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index fb87e64e44..798c4798a5 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -404,6 +404,17 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, space = vfio_get_address_space(as); + /* + * The HostIOMMUDevice data from legacy backend is static and doesn't need + * any information from the (type1-iommu) backend to be initialized. In + * contrast however, the IOMMUFD HostIOMMUDevice data requires the iommufd + * FD to be connected and having a devid to be able to successfully call + * iommufd_backend_get_device_info(). + */ + if (!vfio_device_hiod_realize(vbasedev, errp)) { + goto err_alloc_ioas; + } + /* try to attach to an existing container in this space */ QLIST_FOREACH(bcontainer, &space->containers, next) { container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 1a96678f8c..4e44b26d3c 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -242,6 +242,7 @@ void vfio_region_finalize(VFIORegion *region); void vfio_reset_handler(void *opaque); struct vfio_device_info *vfio_get_device_info(int fd); bool vfio_device_is_mdev(VFIODevice *vbasedev); +bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp); bool vfio_attach_device(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); void vfio_detach_device(VFIODevice *vbasedev); From dddfd8d6670d2d63b851b0c976c3ae08592f4339 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:22 +0100 Subject: [PATCH 2419/2884] vfio/iommufd: Probe and request hwpt dirty tracking capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to using the dirty tracking UAPI, probe whether the IOMMU supports dirty tracking. This is done via the data stored in hiod::caps::hw_caps initialized from GET_HW_INFO. Qemu doesn't know if VF dirty tracking is supported when allocating hardware pagetable in iommufd_cdev_autodomains_get(). This is because VFIODevice migration state hasn't been initialized *yet* hence it can't pick between VF dirty tracking vs IOMMU dirty tracking. So, if IOMMU supports dirty tracking it always creates HWPTs with IOMMU_HWPT_ALLOC_DIRTY_TRACKING even if later on VFIOMigration decides to use VF dirty tracking instead. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> [ clg: - Fixed vbasedev->iommu_dirty_tracking assignment in iommufd_cdev_autodomains_get() - Added warning for heterogeneous dirty page tracking support in iommufd_cdev_autodomains_get() ] Signed-off-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- hw/vfio/iommufd.c | 26 ++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 798c4798a5..240c476eaf 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -110,6 +110,11 @@ static void iommufd_cdev_unbind_and_disconnect(VFIODevice *vbasedev) iommufd_backend_disconnect(vbasedev->iommufd); } +static bool iommufd_hwpt_dirty_tracking(VFIOIOASHwpt *hwpt) +{ + return hwpt && hwpt->hwpt_flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; +} + static int iommufd_cdev_getfd(const char *sysfs_path, Error **errp) { ERRP_GUARD(); @@ -243,10 +248,22 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, } else { vbasedev->hwpt = hwpt; QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); + vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); return true; } } + /* + * This is quite early and VFIO Migration state isn't yet fully + * initialized, thus rely only on IOMMU hardware capabilities as to + * whether IOMMU dirty tracking is going to be requested. Later + * vfio_migration_realize() may decide to use VF dirty tracking + * instead. + */ + if (vbasedev->hiod->caps.hw_caps & IOMMU_HW_CAP_DIRTY_TRACKING) { + flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING; + } + if (!iommufd_backend_alloc_hwpt(iommufd, vbasedev->devid, container->ioas_id, flags, IOMMU_HWPT_DATA_NONE, 0, NULL, @@ -256,6 +273,7 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, hwpt = g_malloc0(sizeof(*hwpt)); hwpt->hwpt_id = hwpt_id; + hwpt->hwpt_flags = flags; QLIST_INIT(&hwpt->device_list); ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); @@ -266,8 +284,16 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, } vbasedev->hwpt = hwpt; + vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); QLIST_INSERT_HEAD(&container->hwpt_list, hwpt, next); + container->bcontainer.dirty_pages_supported |= + vbasedev->iommu_dirty_tracking; + if (container->bcontainer.dirty_pages_supported && + !vbasedev->iommu_dirty_tracking) { + warn_report("IOMMU instance for device %s doesn't support dirty tracking", + vbasedev->name); + } return true; } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 4e44b26d3c..1e02c98b09 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -97,6 +97,7 @@ typedef struct IOMMUFDBackend IOMMUFDBackend; typedef struct VFIOIOASHwpt { uint32_t hwpt_id; + uint32_t hwpt_flags; QLIST_HEAD(, VFIODevice) device_list; QLIST_ENTRY(VFIOIOASHwpt) next; } VFIOIOASHwpt; @@ -139,6 +140,7 @@ typedef struct VFIODevice { OnOffAuto pre_copy_dirty_page_tracking; bool dirty_pages_supported; bool dirty_tracking; + bool iommu_dirty_tracking; HostIOMMUDevice *hiod; int devid; IOMMUFDBackend *iommufd; From 52ce88229c2d63a223f4c822240e84c3daeb7f6e Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:23 +0100 Subject: [PATCH 2420/2884] vfio/iommufd: Implement VFIOIOMMUClass::set_dirty_tracking support ioctl(iommufd, IOMMU_HWPT_SET_DIRTY_TRACKING, arg) is the UAPI that enables or disables dirty page tracking. The ioctl is used if the hwpt has been created with dirty tracking supported domain (stored in hwpt::flags) and it is called on the whole list of iommu domains. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> --- backends/iommufd.c | 23 +++++++++++++++++++++++ backends/trace-events | 1 + hw/vfio/iommufd.c | 32 ++++++++++++++++++++++++++++++++ include/sysemu/iommufd.h | 2 ++ 4 files changed, 58 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index 06b135111f..b978835038 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -238,6 +238,29 @@ bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, return true; } +bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, + uint32_t hwpt_id, bool start, + Error **errp) +{ + int ret; + struct iommu_hwpt_set_dirty_tracking set_dirty = { + .size = sizeof(set_dirty), + .hwpt_id = hwpt_id, + .flags = start ? IOMMU_HWPT_DIRTY_TRACKING_ENABLE : 0, + }; + + ret = ioctl(be->fd, IOMMU_HWPT_SET_DIRTY_TRACKING, &set_dirty); + trace_iommufd_backend_set_dirty(be->fd, hwpt_id, start, ret ? errno : 0); + if (ret) { + error_setg_errno(errp, errno, + "IOMMU_HWPT_SET_DIRTY_TRACKING(hwpt_id %u) failed", + hwpt_id); + return false; + } + + return true; +} + bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, uint64_t *caps, Error **errp) diff --git a/backends/trace-events b/backends/trace-events index 4d8ac02fe7..28aca3b859 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -16,3 +16,4 @@ iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t si iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d" iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" out_hwpt=%u (%d)" iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" +iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 240c476eaf..e39fbf4dd6 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -115,6 +115,37 @@ static bool iommufd_hwpt_dirty_tracking(VFIOIOASHwpt *hwpt) return hwpt && hwpt->hwpt_flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; } +static int iommufd_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, + bool start, Error **errp) +{ + const VFIOIOMMUFDContainer *container = + container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + VFIOIOASHwpt *hwpt; + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + if (!iommufd_hwpt_dirty_tracking(hwpt)) { + continue; + } + + if (!iommufd_backend_set_dirty_tracking(container->be, + hwpt->hwpt_id, start, errp)) { + goto err; + } + } + + return 0; + +err: + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + if (!iommufd_hwpt_dirty_tracking(hwpt)) { + continue; + } + iommufd_backend_set_dirty_tracking(container->be, + hwpt->hwpt_id, !start, NULL); + } + return -EINVAL; +} + static int iommufd_cdev_getfd(const char *sysfs_path, Error **errp) { ERRP_GUARD(); @@ -739,6 +770,7 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) vioc->attach_device = iommufd_cdev_attach; vioc->detach_device = iommufd_cdev_detach; vioc->pci_hot_reset = iommufd_cdev_pci_hot_reset; + vioc->set_dirty_page_tracking = iommufd_set_dirty_page_tracking; }; static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index e917e7591d..6fb412f611 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -55,6 +55,8 @@ bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, uint32_t data_type, uint32_t data_len, void *data_ptr, uint32_t *out_hwpt, Error **errp); +bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, uint32_t hwpt_id, + bool start, Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From 7c30710bd99510b1ce91a1b287118c759c55a495 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:24 +0100 Subject: [PATCH 2421/2884] vfio/iommufd: Implement VFIOIOMMUClass::query_dirty_bitmap support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ioctl(iommufd, IOMMU_HWPT_GET_DIRTY_BITMAP, arg) is the UAPI that fetches the bitmap that tells what was dirty in an IOVA range. A single bitmap is allocated and used across all the hwpts sharing an IOAS which is then used in log_sync() to set Qemu global bitmaps. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- backends/iommufd.c | 29 +++++++++++++++++++++++++++++ backends/trace-events | 1 + hw/vfio/iommufd.c | 28 ++++++++++++++++++++++++++++ include/sysemu/iommufd.h | 4 ++++ 4 files changed, 62 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index b978835038..9bc466a89c 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -261,6 +261,35 @@ bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, return true; } +bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, + uint32_t hwpt_id, + uint64_t iova, ram_addr_t size, + uint64_t page_size, uint64_t *data, + Error **errp) +{ + int ret; + struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap = { + .size = sizeof(get_dirty_bitmap), + .hwpt_id = hwpt_id, + .iova = iova, + .length = size, + .page_size = page_size, + .data = (uintptr_t)data, + }; + + ret = ioctl(be->fd, IOMMU_HWPT_GET_DIRTY_BITMAP, &get_dirty_bitmap); + trace_iommufd_backend_get_dirty_bitmap(be->fd, hwpt_id, iova, size, + page_size, ret ? errno : 0); + if (ret) { + error_setg_errno(errp, errno, + "IOMMU_HWPT_GET_DIRTY_BITMAP (iova: 0x%"HWADDR_PRIx + " size: 0x"RAM_ADDR_FMT") failed", iova, size); + return false; + } + + return true; +} + bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, uint32_t *type, void *data, uint32_t len, uint64_t *caps, Error **errp) diff --git a/backends/trace-events b/backends/trace-events index 28aca3b859..40811a3162 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -17,3 +17,4 @@ iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d" iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" out_hwpt=%u (%d)" iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)" +iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" page_size=0x%"PRIx64" (%d)" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index e39fbf4dd6..e7bece4ea1 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -25,6 +25,7 @@ #include "qemu/cutils.h" #include "qemu/chardev_open.h" #include "pci.h" +#include "exec/ram_addr.h" static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly) @@ -146,6 +147,32 @@ err: return -EINVAL; } +static int iommufd_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, Error **errp) +{ + VFIOIOMMUFDContainer *container = container_of(bcontainer, + VFIOIOMMUFDContainer, + bcontainer); + unsigned long page_size = qemu_real_host_page_size(); + VFIOIOASHwpt *hwpt; + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + if (!iommufd_hwpt_dirty_tracking(hwpt)) { + continue; + } + + if (!iommufd_backend_get_dirty_bitmap(container->be, hwpt->hwpt_id, + iova, size, page_size, + (uint64_t *)vbmap->bitmap, + errp)) { + return -EINVAL; + } + } + + return 0; +} + static int iommufd_cdev_getfd(const char *sysfs_path, Error **errp) { ERRP_GUARD(); @@ -771,6 +798,7 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) vioc->detach_device = iommufd_cdev_detach; vioc->pci_hot_reset = iommufd_cdev_pci_hot_reset; vioc->set_dirty_page_tracking = iommufd_set_dirty_page_tracking; + vioc->query_dirty_bitmap = iommufd_query_dirty_bitmap; }; static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h index 6fb412f611..4c4886c778 100644 --- a/include/sysemu/iommufd.h +++ b/include/sysemu/iommufd.h @@ -57,6 +57,10 @@ bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, Error **errp); bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, uint32_t hwpt_id, bool start, Error **errp); +bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id, + uint64_t iova, ram_addr_t size, + uint64_t page_size, uint64_t *data, + Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From f48b472450efd086dec2e32dc93c882a62a53649 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:25 +0100 Subject: [PATCH 2422/2884] vfio/migration: Don't block migration device dirty tracking is unsupported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default VFIO migration is set to auto, which will support live migration if the migration capability is set *and* also dirty page tracking is supported. For testing purposes one can force enable without dirty page tracking via enable-migration=on, but that option is generally left for testing purposes. So starting with IOMMU dirty tracking it can use to accommodate the lack of VF dirty page tracking allowing us to minimize the VF requirements for migration and thus enabling migration by default for those too. While at it change the error messages to mention IOMMU dirty tracking as well. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> [ clg: - spelling in commit log ] Signed-off-by: Cédric Le Goater <clg@redhat.com> --- hw/vfio/migration.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 34d4be2ce1..cbfaef7aff 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1036,16 +1036,16 @@ bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp) return !vfio_block_migration(vbasedev, err, errp); } - if (!vbasedev->dirty_pages_supported) { + if (!vbasedev->dirty_pages_supported && !vbasedev->iommu_dirty_tracking) { if (vbasedev->enable_migration == ON_OFF_AUTO_AUTO) { error_setg(&err, - "%s: VFIO device doesn't support device dirty tracking", - vbasedev->name); + "%s: VFIO device doesn't support device and " + "IOMMU dirty tracking", vbasedev->name); goto add_blocker; } - warn_report("%s: VFIO device doesn't support device dirty tracking", - vbasedev->name); + warn_report("%s: VFIO device doesn't support device and " + "IOMMU dirty tracking", vbasedev->name); } ret = vfio_block_multiple_devices_migration(vbasedev, errp); From 30b9167785177ac43d11b881fe321918124aeb88 Mon Sep 17 00:00:00 2001 From: Joao Martins <joao.m.martins@oracle.com> Date: Mon, 22 Jul 2024 22:13:26 +0100 Subject: [PATCH 2423/2884] vfio/common: Allow disabling device dirty page tracking The property 'x-pre-copy-dirty-page-tracking' allows disabling the whole tracking of VF pre-copy phase of dirty page tracking, though it means that it will only be used at the start of the switchover phase. Add an option that disables the VF dirty page tracking, and fall back into container-based dirty page tracking. This also allows to use IOMMU dirty tracking even on VFs with their own dirty tracker scheme. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com> --- hw/vfio/common.c | 3 +++ hw/vfio/migration.c | 4 +++- hw/vfio/pci.c | 3 +++ include/hw/vfio/vfio-common.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 26e74fa430..17a5865476 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -199,6 +199,9 @@ bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer) VFIODevice *vbasedev; QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (vbasedev->device_dirty_page_tracking == ON_OFF_AUTO_OFF) { + return false; + } if (!vbasedev->dirty_pages_supported) { return false; } diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index cbfaef7aff..262d42a46e 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1036,7 +1036,9 @@ bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp) return !vfio_block_migration(vbasedev, err, errp); } - if (!vbasedev->dirty_pages_supported && !vbasedev->iommu_dirty_tracking) { + if ((!vbasedev->dirty_pages_supported || + vbasedev->device_dirty_page_tracking == ON_OFF_AUTO_OFF) && + !vbasedev->iommu_dirty_tracking) { if (vbasedev->enable_migration == ON_OFF_AUTO_AUTO) { error_setg(&err, "%s: VFIO device doesn't support device and " diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 265d3cb82f..2407720c35 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3361,6 +3361,9 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_ON_OFF_AUTO("x-pre-copy-dirty-page-tracking", VFIOPCIDevice, vbasedev.pre_copy_dirty_page_tracking, ON_OFF_AUTO_ON), + DEFINE_PROP_ON_OFF_AUTO("x-device-dirty-page-tracking", VFIOPCIDevice, + vbasedev.device_dirty_page_tracking, + ON_OFF_AUTO_ON), DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, display, ON_OFF_AUTO_OFF), DEFINE_PROP_UINT32("xres", VFIOPCIDevice, display_xres, 0), diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 1e02c98b09..fed499b199 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -138,6 +138,7 @@ typedef struct VFIODevice { VFIOMigration *migration; Error *migration_blocker; OnOffAuto pre_copy_dirty_page_tracking; + OnOffAuto device_dirty_page_tracking; bool dirty_pages_supported; bool dirty_tracking; bool iommu_dirty_tracking; From 99481a098837d6adc24a82d0d3123e675b3e191b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Wed, 29 May 2024 14:06:24 +0200 Subject: [PATCH 2424/2884] accel: Restrict probe_access*() functions to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This API is specific to TCG (already handled by hardware accelerators), so restrict it with #ifdef'ry. Remove unnecessary stubs. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240529155918.6221-1-philmd@linaro.org> --- accel/stubs/tcg-stub.c | 14 -------------- include/exec/exec-all.h | 7 ++++++- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index dd890d6cf6..7f4208fddf 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -18,20 +18,6 @@ void tb_flush(CPUState *cpu) { } -int probe_access_flags(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t retaddr) -{ - g_assert_not_reached(); -} - -void *probe_access(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - /* Handled by hardware accelerator. */ - g_assert_not_reached(); -} - G_NORETURN void cpu_loop_exit(CPUState *cpu) { g_assert_not_reached(); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index b6b46ad13c..72240ef426 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -301,6 +301,9 @@ static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, { } #endif + +#if defined(CONFIG_TCG) + /** * probe_access: * @env: CPUArchState @@ -357,6 +360,7 @@ int probe_access_flags(CPUArchState *env, vaddr addr, int size, bool nonfault, void **phost, uintptr_t retaddr); #ifndef CONFIG_USER_ONLY + /** * probe_access_full: * Like probe_access_flags, except also return into @pfull. @@ -392,7 +396,8 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, void **phost, CPUTLBEntryFull **pfull); -#endif +#endif /* !CONFIG_USER_ONLY */ +#endif /* CONFIG_TCG */ static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb) { From 35422553bc6a22fd3de8ac7cb121b509e97b9bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= <clement.mathieu--drif@eviden.com> Date: Thu, 2 May 2024 15:29:17 +0000 Subject: [PATCH 2425/2884] hw/i386/intel_iommu: Extract device IOTLB invalidation logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This piece of code can be shared by both IOTLB invalidation and PASID-based IOTLB invalidation No functional changes intended. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Clément Mathieu--Drif <clement.mathieu--drif@eviden.com> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com> Message-ID: <20240718081636.879544-12-zhenzhong.duan@intel.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/i386/intel_iommu.c | 57 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 37c21a0aec..536994c310 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2666,13 +2666,43 @@ static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, return true; } +static void do_invalidate_device_tlb(VTDAddressSpace *vtd_dev_as, + bool size, hwaddr addr) +{ + /* + * According to ATS spec table 2.4: + * S = 0, bits 15:12 = xxxx range size: 4K + * S = 1, bits 15:12 = xxx0 range size: 8K + * S = 1, bits 15:12 = xx01 range size: 16K + * S = 1, bits 15:12 = x011 range size: 32K + * S = 1, bits 15:12 = 0111 range size: 64K + * ... + */ + + IOMMUTLBEvent event; + uint64_t sz; + + if (size) { + sz = (VTD_PAGE_SIZE * 2) << cto64(addr >> VTD_PAGE_SHIFT); + addr &= ~(sz - 1); + } else { + sz = VTD_PAGE_SIZE; + } + + event.type = IOMMU_NOTIFIER_DEVIOTLB_UNMAP; + event.entry.target_as = &vtd_dev_as->as; + event.entry.addr_mask = sz - 1; + event.entry.iova = addr; + event.entry.perm = IOMMU_NONE; + event.entry.translated_addr = 0; + memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); +} + static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { VTDAddressSpace *vtd_dev_as; - IOMMUTLBEvent event; hwaddr addr; - uint64_t sz; uint16_t sid; bool size; @@ -2697,28 +2727,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, goto done; } - /* According to ATS spec table 2.4: - * S = 0, bits 15:12 = xxxx range size: 4K - * S = 1, bits 15:12 = xxx0 range size: 8K - * S = 1, bits 15:12 = xx01 range size: 16K - * S = 1, bits 15:12 = x011 range size: 32K - * S = 1, bits 15:12 = 0111 range size: 64K - * ... - */ - if (size) { - sz = (VTD_PAGE_SIZE * 2) << cto64(addr >> VTD_PAGE_SHIFT); - addr &= ~(sz - 1); - } else { - sz = VTD_PAGE_SIZE; - } - - event.type = IOMMU_NOTIFIER_DEVIOTLB_UNMAP; - event.entry.target_as = &vtd_dev_as->as; - event.entry.addr_mask = sz - 1; - event.entry.iova = addr; - event.entry.perm = IOMMU_NONE; - event.entry.translated_addr = 0; - memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); + do_invalidate_device_tlb(vtd_dev_as, size, addr); done: return true; From 8cbb4fc12e1d10182cbab93f234510bc616594ca Mon Sep 17 00:00:00 2001 From: Stacey Son <sson@FreeBSD.org> Date: Mon, 8 Jul 2024 00:41:21 +0530 Subject: [PATCH 2426/2884] bsd-user:Add CPU initialization and management functions Added function to initialize ARM CPU and check if it supports 64-bit mode. Implemented CPU loop function to handle exceptions and emulate execution of instructions. Added function to clone CPU state to create a new thread. Included AArch64 specific CPU functions for bsd-user to set and receive thread-local-storage value from the tpidr_el0 register. Introduced structure for storing CPU register states for BSD-USER. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Co-authored-by: Kyle Evans <kevans@freebsd.org> Co-authored-by: Sean Bruno <sbruno@freebsd.org> Co-authored-by: Jessica Clarke <jrtc27@jrtc27.com> Reviewed-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-2-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/target_arch_cpu.c | 31 +++++ bsd-user/aarch64/target_arch_cpu.h | 192 +++++++++++++++++++++++++++++ bsd-user/aarch64/target_syscall.h | 51 ++++++++ 3 files changed, 274 insertions(+) create mode 100644 bsd-user/aarch64/target_arch_cpu.c create mode 100644 bsd-user/aarch64/target_arch_cpu.h create mode 100644 bsd-user/aarch64/target_syscall.h diff --git a/bsd-user/aarch64/target_arch_cpu.c b/bsd-user/aarch64/target_arch_cpu.c new file mode 100644 index 0000000000..b2fa59efaf --- /dev/null +++ b/bsd-user/aarch64/target_arch_cpu.c @@ -0,0 +1,31 @@ +/* + * ARM AArch64 specific CPU for bsd-user + * + * Copyright (c) 2015 Stacey Son + * + * 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 "target_arch.h" + +/* See cpu_set_user_tls() in arm64/arm64/vm_machdep.c */ +void target_cpu_set_tls(CPUARMState *env, target_ulong newtls) +{ + env->cp15.tpidr_el[0] = newtls; +} + +target_ulong target_cpu_get_tls(CPUARMState *env) +{ + return env->cp15.tpidr_el[0]; +} diff --git a/bsd-user/aarch64/target_arch_cpu.h b/bsd-user/aarch64/target_arch_cpu.h new file mode 100644 index 0000000000..5c150bb7e9 --- /dev/null +++ b/bsd-user/aarch64/target_arch_cpu.h @@ -0,0 +1,192 @@ +/* + * ARM AArch64 cpu init and loop + * + * Copyright (c) 2015 Stacey Son + * + * 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/>. + */ + +#ifndef TARGET_ARCH_CPU_H +#define TARGET_ARCH_CPU_H + +#include "target_arch.h" +#include "signal-common.h" +#include "target/arm/syndrome.h" + +#define TARGET_DEFAULT_CPU_MODEL "any" + +static inline void target_cpu_init(CPUARMState *env, + struct target_pt_regs *regs) +{ + int i; + + if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { + fprintf(stderr, "The selected ARM CPU does not support 64 bit mode\n"); + exit(1); + } + for (i = 0; i < 31; i++) { + env->xregs[i] = regs->regs[i]; + } + env->pc = regs->pc; + env->xregs[31] = regs->sp; +} + + +static inline void target_cpu_loop(CPUARMState *env) +{ + CPUState *cs = env_cpu(env); + int trapnr, ec, fsc, si_code, si_signo; + uint64_t code, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; + uint32_t pstate; + abi_long ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SWI: + /* See arm64/arm64/trap.c cpu_fetch_syscall_args() */ + code = env->xregs[8]; + if (code == TARGET_FREEBSD_NR_syscall || + code == TARGET_FREEBSD_NR___syscall) { + code = env->xregs[0]; + arg1 = env->xregs[1]; + arg2 = env->xregs[2]; + arg3 = env->xregs[3]; + arg4 = env->xregs[4]; + arg5 = env->xregs[5]; + arg6 = env->xregs[6]; + arg7 = env->xregs[7]; + arg8 = 0; + } else { + arg1 = env->xregs[0]; + arg2 = env->xregs[1]; + arg3 = env->xregs[2]; + arg4 = env->xregs[3]; + arg5 = env->xregs[4]; + arg6 = env->xregs[5]; + arg7 = env->xregs[6]; + arg8 = env->xregs[7]; + } + ret = do_freebsd_syscall(env, code, arg1, arg2, arg3, + arg4, arg5, arg6, arg7, arg8); + /* + * The carry bit is cleared for no error; set for error. + * See arm64/arm64/vm_machdep.c cpu_set_syscall_retval() + */ + pstate = pstate_read(env); + if (ret >= 0) { + pstate &= ~PSTATE_C; + env->xregs[0] = ret; + } else if (ret == -TARGET_ERESTART) { + env->pc -= 4; + break; + } else if (ret != -TARGET_EJUSTRETURN) { + pstate |= PSTATE_C; + env->xregs[0] = -ret; + } + pstate_write(env, pstate); + break; + + case EXCP_INTERRUPT: + /* Just indicate that signals should be handle ASAP. */ + break; + + case EXCP_UDEF: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->pc); + break; + + + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + /* We should only arrive here with EC in {DATAABORT, INSNABORT}. */ + ec = syn_get_ec(env->exception.syndrome); + assert(ec == EC_DATAABORT || ec == EC_INSNABORT); + + /* Both EC have the same format for FSC, or close enough. */ + fsc = extract32(env->exception.syndrome, 0, 6); + switch (fsc) { + case 0x04 ... 0x07: /* Translation fault, level {0-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ + case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x11: /* Synchronous Tag Check Fault */ + si_signo = TARGET_SIGSEGV; + si_code = /* TARGET_SEGV_MTESERR; */ TARGET_SEGV_ACCERR; + break; + case 0x21: /* Alignment fault */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + default: + g_assert_not_reached(); + } + force_sig_fault(si_signo, si_code, env->exception.vaddress); + break; + + case EXCP_DEBUG: + case EXCP_BKPT: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + break; + + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + + case EXCP_YIELD: + /* nothing to do here for user-mode, just resume guest code */ + break; + default: + fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); + cpu_dump_state(cs, stderr, 0); + abort(); + } /* switch() */ + process_pending_signals(env); + /* + * Exception return on AArch64 always clears the exclusive + * monitor, so any return to running guest code implies this. + * A strex (successful or otherwise) also clears the monitor, so + * we don't need to specialcase EXCP_STREX. + */ + env->exclusive_addr = -1; + } /* for (;;) */ +} + + +/* See arm64/arm64/vm_machdep.c cpu_fork() */ +static inline void target_cpu_clone_regs(CPUARMState *env, target_ulong newsp) +{ + if (newsp) { + env->xregs[31] = newsp; + } + env->regs[0] = 0; + env->regs[1] = 0; + pstate_write(env, 0); +} + +static inline void target_cpu_reset(CPUArchState *env) +{ +} + + +#endif /* TARGET_ARCH_CPU_H */ diff --git a/bsd-user/aarch64/target_syscall.h b/bsd-user/aarch64/target_syscall.h new file mode 100644 index 0000000000..08ae913c42 --- /dev/null +++ b/bsd-user/aarch64/target_syscall.h @@ -0,0 +1,51 @@ +/* + * ARM AArch64 specific CPU for bsd-user + * + * Copyright (c) 2015 Stacey D. Son <sson at Freebsd> + * + * 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/>. + */ + +#ifndef BSD_USER_AARCH64_TARGET_SYSCALL_H +#define BSD_USER_AARCH64_TARGET_SYSCALL_H + +/* + * The aarch64 registers are named: + * + * x0 through x30 - for 64-bit-wide access (same registers) + * Register '31' is one of two registers depending on the instruction context: + * For instructions dealing with the stack, it is the stack pointer, named rsp + * For all other instructions, it is a "zero" register, which returns 0 when + * read and discards data when written - named rzr (xzr, wzr) + * + * Usage during syscall/function call: + * r0-r7 are used for arguments and return values + * For syscalls, the syscall number is in r8 + * r9-r15 are for temporary values (may get trampled) + * r16-r18 are used for intra-procedure-call and platform values (avoid) + * The called routine is expected to preserve r19-r28 + * r29 and r30 are used as the frame register and link register (avoid) + * See the ARM Procedure Call Reference for details. + */ +struct target_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#define TARGET_HW_MACHINE "arm64" +#define TARGET_HW_MACHINE_ARCH "aarch64" + +#endif /* BSD_USER_AARCH64_TARGET_SYSCALL_H */ From 1acce7718bd41a20d3db6323959fedf9c3675ebe Mon Sep 17 00:00:00 2001 From: Stacey Son <sson@FreeBSD.org> Date: Mon, 8 Jul 2024 00:41:22 +0530 Subject: [PATCH 2427/2884] bsd-user:Add AArch64 register handling and related functions Added header file for managing CPU register states in FreeBSD user mode. Introduced prototypes for setting and getting thread-local storage (TLS). Implemented AArch64 sysarch() system call emulation and a printing function. Added function for setting up thread upcall to add thread support to BSD-USER. Initialized thread's register state during thread setup. Updated ARM AArch64 VM parameter definitions for bsd-user, including address spaces for FreeBSD/arm64 and a function for getting the stack pointer from CPU and setting a return value. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Co-authored-by: Jessica Clarke <jrtc27@jrtc27.com> Co-authored-by: Sean Bruno <sbruno@freebsd.org> Co-authored-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-3-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/target_arch.h | 28 +++++++++++ bsd-user/aarch64/target_arch_reg.h | 56 +++++++++++++++++++++ bsd-user/aarch64/target_arch_sysarch.h | 42 ++++++++++++++++ bsd-user/aarch64/target_arch_thread.h | 61 +++++++++++++++++++++++ bsd-user/aarch64/target_arch_vmparam.h | 68 ++++++++++++++++++++++++++ 5 files changed, 255 insertions(+) create mode 100644 bsd-user/aarch64/target_arch.h create mode 100644 bsd-user/aarch64/target_arch_reg.h create mode 100644 bsd-user/aarch64/target_arch_sysarch.h create mode 100644 bsd-user/aarch64/target_arch_thread.h create mode 100644 bsd-user/aarch64/target_arch_vmparam.h diff --git a/bsd-user/aarch64/target_arch.h b/bsd-user/aarch64/target_arch.h new file mode 100644 index 0000000000..27f47de8eb --- /dev/null +++ b/bsd-user/aarch64/target_arch.h @@ -0,0 +1,28 @@ +/* + * ARM AArch64 specific prototypes for bsd-user + * + * Copyright (c) 2015 Stacey D. Son <sson at FreeBSD> + * + * 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/>. + */ + +#ifndef TARGET_ARCH_H +#define TARGET_ARCH_H + +#include "qemu.h" + +void target_cpu_set_tls(CPUARMState *env, target_ulong newtls); +target_ulong target_cpu_get_tls(CPUARMState *env); + +#endif /* TARGET_ARCH_H */ diff --git a/bsd-user/aarch64/target_arch_reg.h b/bsd-user/aarch64/target_arch_reg.h new file mode 100644 index 0000000000..5c7154f0c1 --- /dev/null +++ b/bsd-user/aarch64/target_arch_reg.h @@ -0,0 +1,56 @@ +/* + * FreeBSD arm64 register structures + * + * Copyright (c) 2015 Stacey Son + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TARGET_ARCH_REG_H +#define TARGET_ARCH_REG_H + +/* See sys/arm64/include/reg.h */ +typedef struct target_reg { + uint64_t x[30]; + uint64_t lr; + uint64_t sp; + uint64_t elr; + uint64_t spsr; +} target_reg_t; + +typedef struct target_fpreg { + __uint128_t fp_q[32]; + uint32_t fp_sr; + uint32_t fp_cr; +} target_fpreg_t; + +#define tswapreg(ptr) tswapal(ptr) + +static inline void target_copy_regs(target_reg_t *regs, CPUARMState *env) +{ + int i; + + for (i = 0; i < 30; i++) { + regs->x[i] = tswapreg(env->xregs[i]); + } + regs->lr = tswapreg(env->xregs[30]); + regs->sp = tswapreg(env->xregs[31]); + regs->elr = tswapreg(env->pc); + regs->spsr = tswapreg(pstate_read(env)); +} + +#undef tswapreg + +#endif /* TARGET_ARCH_REG_H */ diff --git a/bsd-user/aarch64/target_arch_sysarch.h b/bsd-user/aarch64/target_arch_sysarch.h new file mode 100644 index 0000000000..b003015daf --- /dev/null +++ b/bsd-user/aarch64/target_arch_sysarch.h @@ -0,0 +1,42 @@ +/* + * ARM AArch64 sysarch() system call emulation for bsd-user. + * + * Copyright (c) 2015 <sson at FreeBSD> + * + * 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/>. + */ + +#ifndef TARGET_ARCH_SYSARCH_H +#define TARGET_ARCH_SYSARCH_H + +#include "target_syscall.h" +#include "target_arch.h" + +/* See sysarch() in sys/arm64/arm64/sys_machdep.c */ +static inline abi_long do_freebsd_arch_sysarch(CPUARMState *env, int op, + abi_ulong parms) +{ + int ret = -TARGET_EOPNOTSUPP; + + fprintf(stderr, "sysarch"); + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ +} + +#endif /* TARGET_ARCH_SYSARCH_H */ diff --git a/bsd-user/aarch64/target_arch_thread.h b/bsd-user/aarch64/target_arch_thread.h new file mode 100644 index 0000000000..4c911e605a --- /dev/null +++ b/bsd-user/aarch64/target_arch_thread.h @@ -0,0 +1,61 @@ +/* + * ARM AArch64 thread support for bsd-user. + * + * Copyright (c) 2015 Stacey D. Son <sson at FreeBSD> + * + * 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/>. + */ + +#ifndef TARGET_ARCH_THREAD_H +#define TARGET_ARCH_THREAD_H + +/* Compare to arm64/arm64/vm_machdep.c cpu_set_upcall_kse() */ +static inline void target_thread_set_upcall(CPUARMState *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + abi_ulong sp; + + /* + * Make sure the stack is properly aligned. + * arm64/include/param.h (STACKLIGN() macro) + */ + sp = ROUND_DOWN(stack_base + stack_size, 16); + + /* sp = stack base */ + regs->xregs[31] = sp; + /* pc = start function entry */ + regs->pc = entry; + /* r0 = arg */ + regs->xregs[0] = arg; + + +} + +static inline void target_thread_init(struct target_pt_regs *regs, + struct image_info *infop) +{ + abi_long stack = infop->start_stack; + + /* + * Make sure the stack is properly aligned. + * arm64/include/param.h (STACKLIGN() macro) + */ + + memset(regs, 0, sizeof(*regs)); + regs->regs[0] = infop->start_stack; + regs->pc = infop->entry; + regs->sp = ROUND_DOWN(stack, 16); +} + +#endif /* TARGET_ARCH_THREAD_H */ diff --git a/bsd-user/aarch64/target_arch_vmparam.h b/bsd-user/aarch64/target_arch_vmparam.h new file mode 100644 index 0000000000..dc66e1289b --- /dev/null +++ b/bsd-user/aarch64/target_arch_vmparam.h @@ -0,0 +1,68 @@ +/* + * ARM AArch64 VM parameters definitions for bsd-user. + * + * Copyright (c) 2015 Stacey D. Son <sson at FreeBSD> + * + * 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/>. + */ + +#ifndef TARGET_ARCH_VMPARAM_H +#define TARGET_ARCH_VMPARAM_H + +#include "cpu.h" + +/** + * FreeBSD/arm64 Address space layout. + * + * ARMv8 implements up to a 48 bit virtual address space. The address space is + * split into 2 regions at each end of the 64 bit address space, with an + * out of range "hole" in the middle. + * + * We limit the size of the two spaces to 39 bits each. + * + * Upper region: 0xffffffffffffffff + * 0xffffff8000000000 + * + * Hole: 0xffffff7fffffffff + * 0x0000008000000000 + * + * Lower region: 0x0000007fffffffff + * 0x0000000000000000 + * + * The upper region for the kernel, and the lower region for userland. + */ + + +/* compare to sys/arm64/include/vmparam.h */ +#define TARGET_MAXTSIZ (1 * GiB) /* max text size */ +#define TARGET_DFLDSIZ (128 * MiB) /* initial data size limit */ +#define TARGET_MAXDSIZ (1 * GiB) /* max data size */ +#define TARGET_DFLSSIZ (128 * MiB) /* initial stack size limit */ +#define TARGET_MAXSSIZ (1 * GiB) /* max stack size */ +#define TARGET_SGROWSIZ (128 * KiB) /* amount to grow stack */ + + /* KERNBASE - 512 MB */ +#define TARGET_VM_MAXUSER_ADDRESS (0x00007fffff000000ULL - (512 * MiB)) +#define TARGET_USRSTACK TARGET_VM_MAXUSER_ADDRESS + +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->xregs[31]; /* sp */ +} + +static inline void set_second_rval(CPUARMState *state, abi_ulong retval2) +{ + state->xregs[1] = retval2; /* XXX not really used on 64-bit arch */ +} +#endif /* TARGET_ARCH_VMPARAM_H */ From 1541d87db24063fcb11b13e6bfb66a193ff9be65 Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Mon, 8 Jul 2024 00:41:23 +0530 Subject: [PATCH 2428/2884] bsd-user:Add ARM AArch64 support and capabilities Added function to access rval2 by accessing the x1 register. Defined ARM AArch64 ELF parameters including mmap and dynamic load addresses. Introduced extensive hardware capability definitions and macros for retrieving hardware capability (hwcap) flags. Implemented function to retrieve ARM AArch64 hardware capabilities using the `GET_FEATURE_ID` macro. Added function to retrieve extended ARM AArch64 hardware capability flags. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> Co-authored-by: Kyle Evans <kevans@FreeBSD.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-4-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/target_arch.h | 1 + bsd-user/aarch64/target_arch_elf.h | 163 +++++++++++++++++++++++++ bsd-user/aarch64/target_arch_vmparam.h | 6 + 3 files changed, 170 insertions(+) create mode 100644 bsd-user/aarch64/target_arch_elf.h diff --git a/bsd-user/aarch64/target_arch.h b/bsd-user/aarch64/target_arch.h index 27f47de8eb..4815a56ae3 100644 --- a/bsd-user/aarch64/target_arch.h +++ b/bsd-user/aarch64/target_arch.h @@ -21,6 +21,7 @@ #define TARGET_ARCH_H #include "qemu.h" +#include "target/arm/cpu-features.h" void target_cpu_set_tls(CPUARMState *env, target_ulong newtls); target_ulong target_cpu_get_tls(CPUARMState *env); diff --git a/bsd-user/aarch64/target_arch_elf.h b/bsd-user/aarch64/target_arch_elf.h new file mode 100644 index 0000000000..cc87f475b3 --- /dev/null +++ b/bsd-user/aarch64/target_arch_elf.h @@ -0,0 +1,163 @@ +/* + * ARM AArch64 ELF definitions for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * 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/>. + */ + +#ifndef TARGET_ARCH_ELF_H +#define TARGET_ARCH_ELF_H + +#define ELF_START_MMAP 0x80000000 +#define ELF_ET_DYN_LOAD_ADDR 0x100000 + +#define elf_check_arch(x) ((x) == EM_AARCH64) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_AARCH64 + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +enum { + ARM_HWCAP_A64_FP = 1 << 0, + ARM_HWCAP_A64_ASIMD = 1 << 1, + ARM_HWCAP_A64_EVTSTRM = 1 << 2, + ARM_HWCAP_A64_AES = 1 << 3, + ARM_HWCAP_A64_PMULL = 1 << 4, + ARM_HWCAP_A64_SHA1 = 1 << 5, + ARM_HWCAP_A64_SHA2 = 1 << 6, + ARM_HWCAP_A64_CRC32 = 1 << 7, + ARM_HWCAP_A64_ATOMICS = 1 << 8, + ARM_HWCAP_A64_FPHP = 1 << 9, + ARM_HWCAP_A64_ASIMDHP = 1 << 10, + ARM_HWCAP_A64_CPUID = 1 << 11, + ARM_HWCAP_A64_ASIMDRDM = 1 << 12, + ARM_HWCAP_A64_JSCVT = 1 << 13, + ARM_HWCAP_A64_FCMA = 1 << 14, + ARM_HWCAP_A64_LRCPC = 1 << 15, + ARM_HWCAP_A64_DCPOP = 1 << 16, + ARM_HWCAP_A64_SHA3 = 1 << 17, + ARM_HWCAP_A64_SM3 = 1 << 18, + ARM_HWCAP_A64_SM4 = 1 << 19, + ARM_HWCAP_A64_ASIMDDP = 1 << 20, + ARM_HWCAP_A64_SHA512 = 1 << 21, + ARM_HWCAP_A64_SVE = 1 << 22, + ARM_HWCAP_A64_ASIMDFHM = 1 << 23, + ARM_HWCAP_A64_DIT = 1 << 24, + ARM_HWCAP_A64_USCAT = 1 << 25, + ARM_HWCAP_A64_ILRCPC = 1 << 26, + ARM_HWCAP_A64_FLAGM = 1 << 27, + ARM_HWCAP_A64_SSBS = 1 << 28, + ARM_HWCAP_A64_SB = 1 << 29, + ARM_HWCAP_A64_PACA = 1 << 30, + ARM_HWCAP_A64_PACG = 1UL << 31, + + ARM_HWCAP2_A64_DCPODP = 1 << 0, + ARM_HWCAP2_A64_SVE2 = 1 << 1, + ARM_HWCAP2_A64_SVEAES = 1 << 2, + ARM_HWCAP2_A64_SVEPMULL = 1 << 3, + ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, + ARM_HWCAP2_A64_SVESHA3 = 1 << 5, + ARM_HWCAP2_A64_SVESM4 = 1 << 6, + ARM_HWCAP2_A64_FLAGM2 = 1 << 7, + ARM_HWCAP2_A64_FRINT = 1 << 8, + ARM_HWCAP2_A64_SVEI8MM = 1 << 9, + ARM_HWCAP2_A64_SVEF32MM = 1 << 10, + ARM_HWCAP2_A64_SVEF64MM = 1 << 11, + ARM_HWCAP2_A64_SVEBF16 = 1 << 12, + ARM_HWCAP2_A64_I8MM = 1 << 13, + ARM_HWCAP2_A64_BF16 = 1 << 14, + ARM_HWCAP2_A64_DGH = 1 << 15, + ARM_HWCAP2_A64_RNG = 1 << 16, + ARM_HWCAP2_A64_BTI = 1 << 17, + ARM_HWCAP2_A64_MTE = 1 << 18, +}; + +#define ELF_HWCAP get_elf_hwcap() +#define ELF_HWCAP2 get_elf_hwcap2() + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + +static uint32_t get_elf_hwcap(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + uint32_t hwcaps = 0; + + hwcaps |= ARM_HWCAP_A64_FP; + hwcaps |= ARM_HWCAP_A64_ASIMD; + hwcaps |= ARM_HWCAP_A64_CPUID; + + /* probe for the extra features */ + + GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); + GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); + GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); + GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); + GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); + GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); + GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); + GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); + GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); + GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); + GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); + GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); + GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); + GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); + GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); + GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); + GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); + GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); + + return hwcaps; +} + +static uint32_t get_elf_hwcap2(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + uint32_t hwcaps = 0; + + GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); + GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); + GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); + GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); + GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); + GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); + GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); + GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); + GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); + GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); + GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); + GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); + GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); + GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); + GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); + GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); + GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); + GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + + return hwcaps; +} + +#undef GET_FEATURE_ID + +#endif /* TARGET_ARCH_ELF_H */ diff --git a/bsd-user/aarch64/target_arch_vmparam.h b/bsd-user/aarch64/target_arch_vmparam.h index dc66e1289b..0c35491970 100644 --- a/bsd-user/aarch64/target_arch_vmparam.h +++ b/bsd-user/aarch64/target_arch_vmparam.h @@ -65,4 +65,10 @@ static inline void set_second_rval(CPUARMState *state, abi_ulong retval2) { state->xregs[1] = retval2; /* XXX not really used on 64-bit arch */ } + +static inline abi_ulong get_second_rval(CPUARMState *state) +{ + return state->xregs[1]; +} + #endif /* TARGET_ARCH_VMPARAM_H */ From 7dba5e10a65be276267f379f07a9643100209c0d Mon Sep 17 00:00:00 2001 From: Stacey Son <sson@FreeBSD.org> Date: Mon, 8 Jul 2024 00:41:24 +0530 Subject: [PATCH 2429/2884] bsd-user:Add ARM AArch64 signal handling support Added sigcode setup function for signal trampoline which initializes a sequence of instructions to handle signal returns and exits, copying this code to the target offset. Defined ARM AArch64 specific signal definitions including register indices and sizes, and introduced structures to represent general purpose registers, floating point registers, and machine context. Added function to set up signal handler arguments, populating register values in `CPUARMState` based on the provided signal, signal frame, signal action, and frame address. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> Co-authored-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-5-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/signal.c | 53 ++++++++++++++++ bsd-user/aarch64/target_arch_signal.h | 80 +++++++++++++++++++++++++ bsd-user/aarch64/target_arch_sigtramp.h | 48 +++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 bsd-user/aarch64/signal.c create mode 100644 bsd-user/aarch64/target_arch_signal.h create mode 100644 bsd-user/aarch64/target_arch_sigtramp.h diff --git a/bsd-user/aarch64/signal.c b/bsd-user/aarch64/signal.c new file mode 100644 index 0000000000..98861f9ab3 --- /dev/null +++ b/bsd-user/aarch64/signal.c @@ -0,0 +1,53 @@ +/* + * ARM AArch64 specific signal definitions for bsd-user + * + * Copyright (c) 2015 Stacey D. Son <sson at FreeBSD> + * + * 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 "qemu.h" + +/* + * Compare to sendsig() in sys/arm64/arm64/machdep.c + * Assumes that target stack frame memory is locked. + */ +abi_long set_sigtramp_args(CPUARMState *regs, int sig, + struct target_sigframe *frame, + abi_ulong frame_addr, + struct target_sigaction *ka) +{ + /* + * Arguments to signal handler: + * x0 = signal number + * x1 = siginfo pointer + * x2 = ucontext pointer + * pc/elr = signal handler pointer + * sp = sigframe struct pointer + * lr = sigtramp at base of user stack + */ + + regs->xregs[0] = sig; + regs->xregs[1] = frame_addr + + offsetof(struct target_sigframe, sf_si); + regs->xregs[2] = frame_addr + + offsetof(struct target_sigframe, sf_uc); + + regs->pc = ka->_sa_handler; + regs->xregs[TARGET_REG_SP] = frame_addr; + regs->xregs[TARGET_REG_LR] = TARGET_PS_STRINGS - TARGET_SZSIGCODE; + + return 0; +} diff --git a/bsd-user/aarch64/target_arch_signal.h b/bsd-user/aarch64/target_arch_signal.h new file mode 100644 index 0000000000..df17173316 --- /dev/null +++ b/bsd-user/aarch64/target_arch_signal.h @@ -0,0 +1,80 @@ +/* + * ARM AArch64 specific signal definitions for bsd-user + * + * Copyright (c) 2015 Stacey D. Son <sson at FreeBSD> + * + * 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/>. + */ + +#ifndef TARGET_ARCH_SIGNAL_H +#define TARGET_ARCH_SIGNAL_H + +#include "cpu.h" + +#define TARGET_REG_X0 0 +#define TARGET_REG_X30 30 +#define TARGET_REG_X31 31 +#define TARGET_REG_LR TARGET_REG_X30 +#define TARGET_REG_SP TARGET_REG_X31 + +#define TARGET_INSN_SIZE 4 /* arm64 instruction size */ + +/* Size of the signal trampolin code. See _sigtramp(). */ +#define TARGET_SZSIGCODE ((abi_ulong)(9 * TARGET_INSN_SIZE)) + +/* compare to sys/arm64/include/_limits.h */ +#define TARGET_MINSIGSTKSZ (1024 * 4) /* min sig stack size */ +#define TARGET_SIGSTKSZ (TARGET_MINSIGSTKSZ + 32768) /* recommended size */ + +/* struct __mcontext in sys/arm64/include/ucontext.h */ + +struct target_gpregs { + uint64_t gp_x[30]; + uint64_t gp_lr; + uint64_t gp_sp; + uint64_t gp_elr; + uint32_t gp_spsr; + uint32_t gp_pad; +}; + +struct target_fpregs { + __uint128_t fp_q[32]; + uint32_t fp_sr; + uint32_t fp_cr; + uint32_t fp_flags; + uint32_t fp_pad; +}; + +struct target__mcontext { + struct target_gpregs mc_gpregs; + struct target_fpregs mc_fpregs; + uint32_t mc_flags; +#define TARGET_MC_FP_VALID 0x1 + uint32_t mc_pad; + uint64_t mc_spare[8]; +}; + +typedef struct target__mcontext target_mcontext_t; + +#define TARGET_MCONTEXT_SIZE 880 +#define TARGET_UCONTEXT_SIZE 960 + +#include "target_os_ucontext.h" + +struct target_sigframe { + target_siginfo_t sf_si; /* saved siginfo */ + target_ucontext_t sf_uc; /* saved ucontext */ +}; + +#endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/aarch64/target_arch_sigtramp.h b/bsd-user/aarch64/target_arch_sigtramp.h new file mode 100644 index 0000000000..8cdd33b621 --- /dev/null +++ b/bsd-user/aarch64/target_arch_sigtramp.h @@ -0,0 +1,48 @@ +/* + * ARM AArch64 sigcode for bsd-user + * + * Copyright (c) 2015 Stacey D. Son <sson at FreeBSD> + * + * 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/>. + */ + +#ifndef TARGET_ARCH_SIGTRAMP_H +#define TARGET_ARCH_SIGTRAMP_H + +/* Compare to ENTRY(sigcode) in arm64/arm64/locore.S */ +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + int i; + uint32_t sys_exit = TARGET_FREEBSD_NR_exit; + + uint32_t sigtramp_code[] = { + /* 1 */ 0x910003e0, /* mov x0, sp */ + /* 2 */ 0x91000000 + (sigf_uc << 10), /* add x0, x0, #SIGF_UC */ + /* 3 */ 0xd2800000 + (sys_sigreturn << 5) + 0x8, /* mov x8, #SYS_sigreturn */ + /* 4 */ 0xd4000001, /* svc #0 */ + /* 5 */ 0xd2800028 + (sys_exit << 5) + 0x8, /* mov x8, #SYS_exit */ + /* 6 */ 0xd4000001, /* svc #0 */ + /* 7 */ 0x17fffffc, /* b -4 */ + /* 8 */ sys_sigreturn, + /* 9 */ sys_exit + }; + + for (i = 0; i < 9; i++) { + tswap32s(&sigtramp_code[i]); + } + + return memcpy_to_target(offset, sigtramp_code, TARGET_SZSIGCODE); +} +#endif /* TARGET_ARCH_SIGTRAMP_H */ From 9959fae592b2babddb7c371e27281a4e127a2951 Mon Sep 17 00:00:00 2001 From: Stacey Son <sson@FreeBSD.org> Date: Mon, 8 Jul 2024 00:41:25 +0530 Subject: [PATCH 2430/2884] bsd-user:Add get_mcontext function for ARM AArch64 function to retrieve machine context,it populates the provided target_mcontext_t structure with information from the CPUARMState registers. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Co-authored-by: Kyle Evans <kevans@FreeBSD.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-6-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/signal.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/bsd-user/aarch64/signal.c b/bsd-user/aarch64/signal.c index 98861f9ab3..ab3bf8558a 100644 --- a/bsd-user/aarch64/signal.c +++ b/bsd-user/aarch64/signal.c @@ -51,3 +51,33 @@ abi_long set_sigtramp_args(CPUARMState *regs, int sig, return 0; } + +/* + * Compare to get_mcontext() in arm64/arm64/machdep.c + * Assumes that the memory is locked if mcp points to user memory. + */ +abi_long get_mcontext(CPUARMState *regs, target_mcontext_t *mcp, int flags) +{ + int err = 0, i; + uint64_t *gr = mcp->mc_gpregs.gp_x; + + mcp->mc_gpregs.gp_spsr = pstate_read(regs); + if (flags & TARGET_MC_GET_CLEAR_RET) { + gr[0] = 0UL; + mcp->mc_gpregs.gp_spsr &= ~CPSR_C; + } else { + gr[0] = tswap64(regs->xregs[0]); + } + + for (i = 1; i < 30; i++) { + gr[i] = tswap64(regs->xregs[i]); + } + + mcp->mc_gpregs.gp_sp = tswap64(regs->xregs[TARGET_REG_SP]); + mcp->mc_gpregs.gp_lr = tswap64(regs->xregs[TARGET_REG_LR]); + mcp->mc_gpregs.gp_elr = tswap64(regs->pc); + + /* XXX FP? */ + + return err; +} From c88f44d85ab209576fc6704ff636e68e2bbc41dc Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Mon, 8 Jul 2024 00:41:26 +0530 Subject: [PATCH 2431/2884] bsd-user:Add setup_sigframe_arch function for ARM AArch64 The function utilizes the `get_mcontext` function to retrieve the machine context for the current CPUARMState Signed-off-by: Warner Losh <imp@bsdimp.com> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-7-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/signal.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bsd-user/aarch64/signal.c b/bsd-user/aarch64/signal.c index ab3bf8558a..43c886e603 100644 --- a/bsd-user/aarch64/signal.c +++ b/bsd-user/aarch64/signal.c @@ -81,3 +81,17 @@ abi_long get_mcontext(CPUARMState *regs, target_mcontext_t *mcp, int flags) return err; } + +/* + * Compare to arm64/arm64/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUARMState *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags) +{ + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + get_mcontext(env, mcp, flags); + return 0; +} + From dadfc6d5df1006b52c1c0c8ae3af84ac3be36b31 Mon Sep 17 00:00:00 2001 From: Stacey Son <sson@FreeBSD.org> Date: Mon, 8 Jul 2024 00:41:27 +0530 Subject: [PATCH 2432/2884] bsd-user:Add set_mcontext function for ARM AArch64 The function copies register values from the provided target_mcontext_t structure to the CPUARMState registers. Note:FP is unfinished upstream but will be a separate commit coming soon. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-8-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/signal.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/bsd-user/aarch64/signal.c b/bsd-user/aarch64/signal.c index 43c886e603..13faac8ce6 100644 --- a/bsd-user/aarch64/signal.c +++ b/bsd-user/aarch64/signal.c @@ -95,3 +95,25 @@ abi_long setup_sigframe_arch(CPUARMState *env, abi_ulong frame_addr, return 0; } +/* + * Compare to set_mcontext() in arm64/arm64/machdep.c + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long set_mcontext(CPUARMState *regs, target_mcontext_t *mcp, int srflag) +{ + int err = 0, i; + const uint64_t *gr = mcp->mc_gpregs.gp_x; + + for (i = 0; i < 30; i++) { + regs->xregs[i] = tswap64(gr[i]); + } + + regs->xregs[TARGET_REG_SP] = tswap64(mcp->mc_gpregs.gp_sp); + regs->xregs[TARGET_REG_LR] = tswap64(mcp->mc_gpregs.gp_lr); + regs->pc = mcp->mc_gpregs.gp_elr; + pstate_write(regs, mcp->mc_gpregs.gp_spsr); + + /* XXX FP? */ + + return err; +} From ce6c541dcb8aff5ca6f02bb64b0d6b6753becab4 Mon Sep 17 00:00:00 2001 From: Stacey Son <sson@FreeBSD.org> Date: Mon, 8 Jul 2024 00:41:28 +0530 Subject: [PATCH 2433/2884] bsd-user:Add AArch64 improvements and signal handling functions Added get_ucontext_sigreturn function to check processor state ensuring current execution mode is EL0 and no flags indicating interrupts or exceptions are set. Updated AArch64 code to use CF directly without reading/writing the entire processor state, improving efficiency. Changed FP data structures to use Int128 instead of __uint128_t, leveraging QEMU's generic mechanism for referencing this type. Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Ajeet Singh <itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240707191128.10509-9-itachis@FreeBSD.org> Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/signal.c | 20 +++++++++++++++++++- bsd-user/aarch64/target_arch_cpu.h | 7 ++----- bsd-user/aarch64/target_arch_reg.h | 2 +- bsd-user/aarch64/target_arch_signal.h | 2 +- bsd-user/qemu.h | 3 +++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/bsd-user/aarch64/signal.c b/bsd-user/aarch64/signal.c index 13faac8ce6..6bc73a798f 100644 --- a/bsd-user/aarch64/signal.c +++ b/bsd-user/aarch64/signal.c @@ -21,7 +21,7 @@ #include "qemu.h" /* - * Compare to sendsig() in sys/arm64/arm64/machdep.c + * Compare to sendsig() in sys/arm64/arm64/exec_machdep.c * Assumes that target stack frame memory is locked. */ abi_long set_sigtramp_args(CPUARMState *regs, int sig, @@ -117,3 +117,21 @@ abi_long set_mcontext(CPUARMState *regs, target_mcontext_t *mcp, int srflag) return err; } + +/* Compare to sys_sigreturn() in arm64/arm64/machdep.c */ +abi_long get_ucontext_sigreturn(CPUARMState *regs, abi_ulong target_sf, + abi_ulong *target_uc) +{ + uint32_t pstate = pstate_read(regs); + + *target_uc = 0; + + if ((pstate & PSTATE_M) != PSTATE_MODE_EL0t || + (pstate & (PSTATE_F | PSTATE_I | PSTATE_A | PSTATE_D)) != 0) { + return -TARGET_EINVAL; + } + + *target_uc = target_sf; + + return 0; +} diff --git a/bsd-user/aarch64/target_arch_cpu.h b/bsd-user/aarch64/target_arch_cpu.h index 5c150bb7e9..b288e0d069 100644 --- a/bsd-user/aarch64/target_arch_cpu.h +++ b/bsd-user/aarch64/target_arch_cpu.h @@ -48,7 +48,6 @@ static inline void target_cpu_loop(CPUARMState *env) CPUState *cs = env_cpu(env); int trapnr, ec, fsc, si_code, si_signo; uint64_t code, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; - uint32_t pstate; abi_long ret; for (;;) { @@ -88,18 +87,16 @@ static inline void target_cpu_loop(CPUARMState *env) * The carry bit is cleared for no error; set for error. * See arm64/arm64/vm_machdep.c cpu_set_syscall_retval() */ - pstate = pstate_read(env); if (ret >= 0) { - pstate &= ~PSTATE_C; + env->CF = 0; env->xregs[0] = ret; } else if (ret == -TARGET_ERESTART) { env->pc -= 4; break; } else if (ret != -TARGET_EJUSTRETURN) { - pstate |= PSTATE_C; + env->CF = 1; env->xregs[0] = -ret; } - pstate_write(env, pstate); break; case EXCP_INTERRUPT: diff --git a/bsd-user/aarch64/target_arch_reg.h b/bsd-user/aarch64/target_arch_reg.h index 5c7154f0c1..b53302e7f7 100644 --- a/bsd-user/aarch64/target_arch_reg.h +++ b/bsd-user/aarch64/target_arch_reg.h @@ -31,7 +31,7 @@ typedef struct target_reg { } target_reg_t; typedef struct target_fpreg { - __uint128_t fp_q[32]; + Int128 fp_q[32]; uint32_t fp_sr; uint32_t fp_cr; } target_fpreg_t; diff --git a/bsd-user/aarch64/target_arch_signal.h b/bsd-user/aarch64/target_arch_signal.h index df17173316..bff752a67a 100644 --- a/bsd-user/aarch64/target_arch_signal.h +++ b/bsd-user/aarch64/target_arch_signal.h @@ -49,7 +49,7 @@ struct target_gpregs { }; struct target_fpregs { - __uint128_t fp_q[32]; + Int128 fp_q[32]; uint32_t fp_sr; uint32_t fp_cr; uint32_t fp_flags; diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 9d2fc7148e..3736c41786 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -17,6 +17,9 @@ #ifndef QEMU_H #define QEMU_H +#include <sys/param.h> + +#include "qemu/int128.h" #include "cpu.h" #include "qemu/units.h" #include "exec/cpu_ldst.h" From e6e102b972c5228d9d50b3208b83b025ccfd1455 Mon Sep 17 00:00:00 2001 From: Doug Rabson <dfr@rabson.org> Date: Thu, 8 Dec 2022 14:21:52 +0000 Subject: [PATCH 2434/2884] bsd-user: Simplify the implementation of execve This removes the logic which prepends the emulator to each call to execve and fexecve. This is not necessary with the existing imgact_binmisc support and it avoids the need to install the emulator binary into jail environments when using 'binmiscctl --pre-open'. Signed-off-by: Doug Rabson <dfr@rabson.org> Reviewed-by: Warner Losh <imp@bsdimp.com> Signed-off-by: Warner Losh <imp@bsdimp.com> Acked-by: Richard Henderson <richard.henderson@linaro.org> --- bsd-user/freebsd/os-proc.c | 118 +------------------------------------ bsd-user/main.c | 18 ------ 2 files changed, 3 insertions(+), 133 deletions(-) diff --git a/bsd-user/freebsd/os-proc.c b/bsd-user/freebsd/os-proc.c index e0203e259b..bf993f1b66 100644 --- a/bsd-user/freebsd/os-proc.c +++ b/bsd-user/freebsd/os-proc.c @@ -26,65 +26,13 @@ struct kinfo_proc; #include "qemu.h" -/* - * Get the filename for the given file descriptor. - * Note that this may return NULL (fail) if no longer cached in the kernel. - */ -static char * -get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len) -{ - char *ret = NULL; - unsigned int cnt; - struct procstat *procstat = NULL; - struct kinfo_proc *kp = NULL; - struct filestat_list *head = NULL; - struct filestat *fst; - - procstat = procstat_open_sysctl(); - if (procstat == NULL) { - goto out; - } - - kp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &cnt); - if (kp == NULL) { - goto out; - } - - head = procstat_getfiles(procstat, kp, 0); - if (head == NULL) { - goto out; - } - - STAILQ_FOREACH(fst, head, next) { - if (fd == fst->fs_fd) { - if (fst->fs_path != NULL) { - (void)strlcpy(filename, fst->fs_path, len); - ret = filename; - } - break; - } - } - -out: - if (head != NULL) { - procstat_freefiles(procstat, head); - } - if (kp != NULL) { - procstat_freeprocs(procstat, kp); - } - if (procstat != NULL) { - procstat_close(procstat); - } - return ret; -} - /* * execve/fexecve */ abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, abi_ulong guest_envp, int do_fexec) { - char **argp, **envp, **qargp, **qarg1, **qarg0, **qargend; + char **argp, **envp, **qarg0; int argc, envc; abi_ulong gp; abi_ulong addr; @@ -117,9 +65,7 @@ abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, qarg0 = argp = g_new0(char *, argc + 9); /* save the first argument for the emulator */ *argp++ = (char *)getprogname(); - qargp = argp; *argp++ = (char *)getprogname(); - qarg1 = argp; envp = g_new0(char *, envc + 1); for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) { if (get_user_ual(addr, gp)) { @@ -137,7 +83,6 @@ abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, total_size += strlen(*q) + 1; } *q++ = NULL; - qargend = q; for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) { if (get_user_ual(addr, gp)) { @@ -166,71 +111,14 @@ abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, } if (do_fexec) { - if (((int)path_or_fd > 0 && - is_target_elf_binary((int)path_or_fd)) == 1) { - char execpath[PATH_MAX]; - - /* - * The executable is an elf binary for the target - * arch. execve() it using the emulator if we can - * determine the filename path from the fd. - */ - if (get_filename_from_fd(getpid(), (int)path_or_fd, execpath, - sizeof(execpath)) != NULL) { - memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); - qarg1[1] = qarg1[0]; - qarg1[0] = (char *)"-0"; - qarg1 += 2; - qargend += 2; - *qarg1 = execpath; -#ifndef DONT_INHERIT_INTERP_PREFIX - memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); - *qarg1++ = (char *)"-L"; - *qarg1++ = (char *)interp_prefix; -#endif - ret = get_errno(execve(qemu_proc_pathname, qargp, envp)); - } else { - /* Getting the filename path failed. */ - ret = -TARGET_EBADF; - goto execve_end; - } - } else { - ret = get_errno(fexecve((int)path_or_fd, argp, envp)); - } + ret = get_errno(fexecve((int)path_or_fd, argp, envp)); } else { - int fd; - p = lock_user_string(path_or_fd); if (p == NULL) { ret = -TARGET_EFAULT; goto execve_end; } - - /* - * Check the header and see if it a target elf binary. If so - * then execute using qemu user mode emulator. - */ - fd = open(p, O_RDONLY | O_CLOEXEC); - if (fd > 0 && is_target_elf_binary(fd) == 1) { - close(fd); - /* execve() as a target binary using emulator. */ - memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); - qarg1[1] = qarg1[0]; - qarg1[0] = (char *)"-0"; - qarg1 += 2; - qargend += 2; - *qarg1 = (char *)p; -#ifndef DONT_INHERIT_INTERP_PREFIX - memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); - *qarg1++ = (char *)"-L"; - *qarg1++ = (char *)interp_prefix; -#endif - ret = get_errno(execve(qemu_proc_pathname, qargp, envp)); - } else { - close(fd); - /* Execve() as a host native binary. */ - ret = get_errno(execve(p, argp, envp)); - } + ret = get_errno(execve(p, argp, envp)); unlock_user(p, path_or_fd, 0); } diff --git a/bsd-user/main.c b/bsd-user/main.c index dcad266c2c..82e94a0316 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -90,7 +90,6 @@ unsigned long reserved_va; const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; -char qemu_proc_pathname[PATH_MAX]; /* full path to exeutable */ unsigned long target_maxtsiz = TARGET_MAXTSIZ; /* max text size */ unsigned long target_dfldsiz = TARGET_DFLDSIZ; /* initial data size limit */ @@ -247,22 +246,6 @@ adjust_ssize(void) setrlimit(RLIMIT_STACK, &rl); } -static void save_proc_pathname(char *argv0) -{ - int mib[4]; - size_t len; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PATHNAME; - mib[3] = -1; - - len = sizeof(qemu_proc_pathname); - if (sysctl(mib, 4, qemu_proc_pathname, &len, NULL, 0)) { - perror("sysctl"); - } -} - int main(int argc, char **argv) { const char *filename; @@ -292,7 +275,6 @@ int main(int argc, char **argv) usage(); } - save_proc_pathname(argv[0]); error_init(argv[0]); module_call_init(MODULE_INIT_TRACE); From b314fd06cf2e55c6cbdda753faeda0c453ee2629 Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Tue, 11 Jun 2024 17:41:43 -0600 Subject: [PATCH 2435/2884] bsd-user: Hard wire aarch64 to be 4k pages only Only support 4k pages for aarch64 binaries. The variable page size stuff isn't working just yet, so put in this lessor-of-evils kludge until that is complete. Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/cpu-param.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 2d5f3aa312..fa6cae0e3a 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -21,9 +21,13 @@ #ifdef CONFIG_USER_ONLY # ifdef TARGET_AARCH64 # define TARGET_TAGGED_ADDRESSES +# ifdef __FreeBSD__ +# define TARGET_PAGE_BITS 12 +# else /* Allow user-only to vary page size from 4k */ # define TARGET_PAGE_BITS_VARY # define TARGET_PAGE_BITS_MIN 12 +# endif # else # define TARGET_PAGE_BITS 12 # endif From 5b6828d194fba3c170a5f717cc1fc3d35645aadd Mon Sep 17 00:00:00 2001 From: Jessica Clarke <jrtc27@jrtc27.com> Date: Wed, 19 Jun 2024 20:03:44 +0100 Subject: [PATCH 2436/2884] bsd-user: Sync fork_start/fork_end with linux-user This reorders some of the calls, deduplicates code between branches and, most importantly, fixes a double end_exclusive call in the parent that will cause exclusive_context_count to go negative. Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com> Pull-Request: https://github.com/qemu-bsd-user/qemu-bsd-user/pull/52 Reviewed-by: Warner Losh <imp@bsdimp.com> Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- bsd-user/main.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 82e94a0316..cc980e6f40 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -35,6 +35,7 @@ #include "qemu/path.h" #include "qemu/help_option.h" #include "qemu/module.h" +#include "qemu/plugin.h" #include "exec/exec-all.h" #include "user/guest-base.h" #include "tcg/startup.h" @@ -103,8 +104,9 @@ unsigned long target_sgrowsiz = TARGET_SGROWSIZ; /* amount to grow stack */ void fork_start(void) { start_exclusive(); - cpu_list_lock(); mmap_fork_start(); + cpu_list_lock(); + qemu_plugin_user_prefork_lock(); gdbserver_fork_start(); } @@ -112,31 +114,31 @@ void fork_end(pid_t pid) { bool child = pid == 0; + qemu_plugin_user_postfork(child); + mmap_fork_end(child); if (child) { CPUState *cpu, *next_cpu; /* - * Child processes created by fork() only have a single thread. Discard - * information about the parent threads. + * Child processes created by fork() only have a single thread. + * Discard information about the parent threads. */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); } } - mmap_fork_end(child); - /* - * qemu_init_cpu_list() takes care of reinitializing the exclusive - * state, so we don't need to end_exclusive() here. - */ qemu_init_cpu_list(); get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); - gdbserver_fork_end(thread_cpu, pid); } else { - mmap_fork_end(child); cpu_list_unlock(); - gdbserver_fork_end(thread_cpu, pid); - end_exclusive(); } + gdbserver_fork_end(thread_cpu, pid); + /* + * qemu_init_cpu_list() reinitialized the child exclusive state, but we + * also need to keep current_cpu consistent, so call end_exclusive() for + * both child and parent. + */ + end_exclusive(); } void cpu_loop(CPUArchState *env) From 5fa2a10ba6822fc1e500e921dcae344db46e1649 Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Sun, 23 Jun 2024 15:29:42 -0600 Subject: [PATCH 2437/2884] bsd-user: Define TARGET_SIGSTACK_ALIGN and use it to round stack Most (all?) targets require stacks to be properly aligned. Rather than a series of ifdefs in bsd-user/signal.h, instead use a manditory #define for all architectures. Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- bsd-user/aarch64/target_arch_signal.h | 2 ++ bsd-user/arm/target_arch_signal.h | 2 ++ bsd-user/i386/target_arch_signal.h | 2 ++ bsd-user/signal.c | 9 +-------- bsd-user/x86_64/target_arch_signal.h | 2 ++ 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bsd-user/aarch64/target_arch_signal.h b/bsd-user/aarch64/target_arch_signal.h index bff752a67a..b72ba7aa50 100644 --- a/bsd-user/aarch64/target_arch_signal.h +++ b/bsd-user/aarch64/target_arch_signal.h @@ -77,4 +77,6 @@ struct target_sigframe { target_ucontext_t sf_uc; /* saved ucontext */ }; +#define TARGET_SIGSTACK_ALIGN 16 + #endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/arm/target_arch_signal.h b/bsd-user/arm/target_arch_signal.h index 02b2b33e07..10f96b8bfc 100644 --- a/bsd-user/arm/target_arch_signal.h +++ b/bsd-user/arm/target_arch_signal.h @@ -86,4 +86,6 @@ struct target_sigframe { target_mcontext_vfp_t sf_vfp; /* actual saved VFP context */ }; +#define TARGET_SIGSTACK_ALIGN 8 + #endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/i386/target_arch_signal.h b/bsd-user/i386/target_arch_signal.h index 279dadc22c..2c14153ab6 100644 --- a/bsd-user/i386/target_arch_signal.h +++ b/bsd-user/i386/target_arch_signal.h @@ -88,4 +88,6 @@ struct target_sigframe { uint32_t __spare__[2]; }; +#define TARGET_SIGSTACK_ALIGN 8 + #endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 8b6654b91d..da49b9bffc 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -728,14 +728,7 @@ static inline abi_ulong get_sigframe(struct target_sigaction *ka, sp = ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size; } -/* TODO: make this a target_arch function / define */ -#if defined(TARGET_ARM) - return (sp - frame_size) & ~7; -#elif defined(TARGET_AARCH64) - return (sp - frame_size) & ~15; -#else - return sp - frame_size; -#endif + return ROUND_DOWN(sp - frame_size, TARGET_SIGSTACK_ALIGN); } /* compare to $M/$M/exec_machdep.c sendsig and sys/kern/kern_sig.c sigexit */ diff --git a/bsd-user/x86_64/target_arch_signal.h b/bsd-user/x86_64/target_arch_signal.h index ca24bf1e7f..f833ee66ce 100644 --- a/bsd-user/x86_64/target_arch_signal.h +++ b/bsd-user/x86_64/target_arch_signal.h @@ -97,4 +97,6 @@ struct target_sigframe { uint32_t __spare__[2]; }; +#define TARGET_SIGSTACK_ALIGN 16 + #endif /* TARGET_ARCH_SIGNAL_H */ From 1c687f65b4cd4af2bf4732fd327a4f63c8c33e30 Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Sun, 21 Jul 2024 16:29:03 -0600 Subject: [PATCH 2438/2884] bsd-user: Make compile for non-linux user-mode stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We include the files that define PR_MTE_TCF_SHIFT only on Linux, but use them unconditionally. Restrict its use to Linux-only. "It's ugly, but it's not actually wrong." Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/gdbstub64.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index c8cef8cbc0..5221381cc8 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -404,6 +404,7 @@ int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg) int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg) { +#if defined(CONFIG_LINUX) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -425,6 +426,9 @@ int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg) arm_set_mte_tcf0(env, tcf); return 1; +#else + return 0; +#endif } static void handle_q_memtag(GArray *params, void *user_ctx) From afdb6be1bd8528395af65a087bd668bf7a42ab99 Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Mon, 22 Jul 2024 15:34:01 -0600 Subject: [PATCH 2439/2884] bsd-user: Add aarch64 build to tree Add the aarch64 bsd-user fragments needed to build the new aarch64 code. Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- configs/targets/aarch64-bsd-user.mak | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 configs/targets/aarch64-bsd-user.mak diff --git a/configs/targets/aarch64-bsd-user.mak b/configs/targets/aarch64-bsd-user.mak new file mode 100644 index 0000000000..8aaa5d8c80 --- /dev/null +++ b/configs/targets/aarch64-bsd-user.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=aarch64 +TARGET_BASE_ARCH=arm +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml From 2465c89fb983eed670007742bd68c7d91b6d6f85 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Tue, 16 Jul 2024 23:41:23 +0200 Subject: [PATCH 2440/2884] hw/intc/loongson_ipi: Access memory in little endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loongson IPI is only available in little-endian, so use that to access the guest memory (in case we run on a big-endian host). Cc: qemu-stable@nongnu.org Signed-off-by: Bibo Mao <maobibo@loongson.cn> Fixes: f6783e3438 ("hw/loongarch: Add LoongArch ipi interrupt support") [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240718133312.10324-3-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index e6a7142480..e7979dbdd8 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -14,6 +14,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "exec/address-spaces.h" +#include "exec/memory.h" #include "migration/vmstate.h" #ifdef TARGET_LOONGARCH64 #include "target/loongarch/cpu.h" @@ -102,7 +103,7 @@ static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr, * if the mask is 0, we need not to do anything. */ if ((val >> 27) & 0xf) { - data = address_space_ldl(iocsr_as, addr, attrs, NULL); + data = address_space_ldl_le(iocsr_as, addr, attrs, NULL); for (i = 0; i < 4; i++) { /* get mask for byte writing */ if (val & (0x1 << (27 + i))) { @@ -113,7 +114,7 @@ static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr, data &= mask; data |= (val >> 32) & ~mask; - address_space_stl(iocsr_as, addr, data, attrs, NULL); + address_space_stl_le(iocsr_as, addr, data, attrs, NULL); return MEMTX_OK; } From 0c2086bc7360565dfb9933181dafaefe2c94cddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 23 Jul 2024 13:08:33 +0200 Subject: [PATCH 2441/2884] hw/intc/loongson_ipi: Fix resource leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once initialised, QOM objects can be realized and unrealized multiple times before being finalized. Resources allocated in REALIZE must be deallocated in an equivalent UNREALIZE handler. Free the CPU array in loongson_ipi_unrealize() instead of loongson_ipi_finalize(). Cc: qemu-stable@nongnu.org Fixes: 5e90b8db382 ("hw/loongarch: Set iocsr address space per-board rather than percpu") Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Song Gao <gaosong@loongson.cn> Message-Id: <20240723111405.14208-3-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index e7979dbdd8..4013f81745 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -318,6 +318,13 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) } } +static void loongson_ipi_unrealize(DeviceState *dev) +{ + LoongsonIPI *s = LOONGSON_IPI(dev); + + g_free(s->cpu); +} + static const VMStateDescription vmstate_ipi_core = { .name = "ipi-single", .version_id = 2, @@ -353,23 +360,16 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = loongson_ipi_realize; + dc->unrealize = loongson_ipi_unrealize; device_class_set_props(dc, ipi_properties); dc->vmsd = &vmstate_loongson_ipi; } -static void loongson_ipi_finalize(Object *obj) -{ - LoongsonIPI *s = LOONGSON_IPI(obj); - - g_free(s->cpu); -} - static const TypeInfo loongson_ipi_info = { .name = TYPE_LOONGSON_IPI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongsonIPI), .class_init = loongson_ipi_class_init, - .instance_finalize = loongson_ipi_finalize, }; static void loongson_ipi_register_types(void) From 13e8ec6cf319913df4ae4f3e2e221f0b3df52acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 10 Jul 2023 20:24:51 +0200 Subject: [PATCH 2442/2884] hw/intc/loongson_ipi: Declare QOM types using DEFINE_TYPES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple QOM types are registered in the same file, it is simpler to use the the DEFINE_TYPES() macro. Replace the type_init() / type_register_static() combination. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240718133312.10324-2-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 4013f81745..682cec96f3 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -365,16 +365,13 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_loongson_ipi; } -static const TypeInfo loongson_ipi_info = { - .name = TYPE_LOONGSON_IPI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LoongsonIPI), - .class_init = loongson_ipi_class_init, +static const TypeInfo loongson_ipi_types[] = { + { + .name = TYPE_LOONGSON_IPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongsonIPI), + .class_init = loongson_ipi_class_init, + } }; -static void loongson_ipi_register_types(void) -{ - type_register_static(&loongson_ipi_info); -} - -type_init(loongson_ipi_register_types) +DEFINE_TYPES(loongson_ipi_types) From 9ea0f206b7e292c46d0767c3ef1a5ac1d0c6fecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 18 Jul 2024 15:25:08 +0200 Subject: [PATCH 2443/2884] docs: Correct Loongarch -> LoongArch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240718133312.10324-20-philmd@linaro.org> --- docs/about/emulation.rst | 2 +- hw/rtc/ls7a_rtc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index b5ff9c5f69..3bfe8cc14a 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -42,7 +42,7 @@ depending on the guest architecture. - :ref:`Yes<QEMU-PC-System-emulator>` - Yes - The ubiquitous desktop PC CPU architecture, 32 and 64 bit. - * - Loongarch + * - LoongArch - Yes - Yes - A MIPS-like 64bit RISC architecture developed in China diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index 052201c2cd..3226b6105e 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Loongarch LS7A Real Time Clock emulation + * LoongArch LS7A Real Time Clock emulation * * Copyright (C) 2021 Loongson Technology Corporation Limited */ From 220434f099cd14374cdb223bfe2f830fb3e1cd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <thomas.weissschuh@linutronix.de> Date: Fri, 19 Jul 2024 09:37:22 +0200 Subject: [PATCH 2444/2884] docs/interop/firmware.json: add new enum FirmwareFormat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only a small subset of all blockdev drivers make sense for firmware images. Introduce and use a new enum to represent this. This also reduces the dependency on firmware.json from the global qapi definitions. Claim "Since: 3.0" for the new enum, because that's correct for its members, and the members are what matters in the interface. Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240719-qapi-firmware-json-v6-1-c2e3de390b58@linutronix.de> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/interop/firmware.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 54a1fc6c10..d5d4c17f23 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -15,7 +15,6 @@ ## { 'include' : 'machine.json' } -{ 'include' : 'block-core.json' } ## # @FirmwareOSInterface: @@ -200,6 +199,20 @@ 'enrolled-keys', 'requires-smm', 'secure-boot', 'verbose-dynamic', 'verbose-static' ] } +## +# @FirmwareFormat: +# +# Formats that are supported for firmware images. +# +# @raw: Raw disk image format. +# +# @qcow2: The QCOW2 image format. +# +# Since: 3.0 +## +{ 'enum': 'FirmwareFormat', + 'data': [ 'raw', 'qcow2' ] } + ## # @FirmwareFlashFile: # @@ -219,7 +232,7 @@ ## { 'struct' : 'FirmwareFlashFile', 'data' : { 'filename' : 'str', - 'format' : 'BlockdevDriver' } } + 'format' : 'FirmwareFormat' } } ## From d5d0070bfe2da3ab05e9975d557a1517df56199e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <thomas.weissschuh@linutronix.de> Date: Fri, 19 Jul 2024 09:37:23 +0200 Subject: [PATCH 2445/2884] docs/interop/firmware.json: add new enum FirmwareArchitecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only a small subset of all architectures supported by qemu make use of firmware files. Introduce and use a new enum to represent this. This also removes the dependency to machine.json from the global qapi definitions. Claim "Since: 3.0" for the new enum, because that's correct for most of its members, and the members are what matters in the interface. Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240719-qapi-firmware-json-v6-2-c2e3de390b58@linutronix.de> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/interop/firmware.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index d5d4c17f23..ae41799944 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -14,7 +14,10 @@ # = Firmware ## -{ 'include' : 'machine.json' } +{ 'pragma': { + 'member-name-exceptions': [ + 'FirmwareArchitecture' # x86_64 + ] } } ## # @FirmwareOSInterface: @@ -59,6 +62,27 @@ { 'enum' : 'FirmwareDevice', 'data' : [ 'flash', 'kernel', 'memory' ] } +## +# @FirmwareArchitecture: +# +# Enumeration of architectures for which Qemu uses additional +# firmware files. +# +# @aarch64: 64-bit Arm. +# +# @arm: 32-bit Arm. +# +# @i386: 32-bit x86. +# +# @loongarch64: 64-bit LoongArch. (since: 7.1) +# +# @x86_64: 64-bit x86. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareArchitecture', + 'data' : [ 'aarch64', 'arm', 'i386', 'loongarch64', 'x86_64' ] } + ## # @FirmwareTarget: # @@ -80,7 +104,7 @@ # Since: 3.0 ## { 'struct' : 'FirmwareTarget', - 'data' : { 'architecture' : 'SysEmuTarget', + 'data' : { 'architecture' : 'FirmwareArchitecture', 'machines' : [ 'str' ] } } ## From 16c84ec1bed94997d786671cc154c250375e2363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <thomas.weissschuh@linutronix.de> Date: Fri, 19 Jul 2024 09:37:24 +0200 Subject: [PATCH 2446/2884] docs/interop/firmware.json: convert "Example" section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 3c5f6114d9ff ("qapi: remove "Example" doc section") the "Example" section is not valid anymore. It has been replaced by the "qmp-example" directive. This was not detected earlier as firmware.json was not validated. As this validation is about to be added, adapt firmware.json. Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de> Message-ID: <20240719-qapi-firmware-json-v6-3-c2e3de390b58@linutronix.de> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/interop/firmware.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index ae41799944..57f55f6c54 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -470,7 +470,7 @@ # # Since: 3.0 # -# Examples: +# .. qmp-example:: # # { # "description": "SeaBIOS", From 53858a6a305248a567d441de12afc97d3dd9cc7d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 22 Jul 2024 00:55:06 +0200 Subject: [PATCH 2447/2884] hw/i2c/mpc_i2c: Fix mmio region size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last register of this device is at offset 0x14 occupying 8 bits so to cover it the mmio region needs to be 0x15 bytes long. Also correct the name of the field storing this register value to match the register name. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Fixes: 7abb479c7a ("PPC: E500: Add FSL I2C controller") Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240721225506.B32704E6039@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/i2c/mpc_i2c.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/i2c/mpc_i2c.c b/hw/i2c/mpc_i2c.c index cb051a520f..06d4ce7d68 100644 --- a/hw/i2c/mpc_i2c.c +++ b/hw/i2c/mpc_i2c.c @@ -82,7 +82,7 @@ struct MPCI2CState { uint8_t cr; uint8_t sr; uint8_t dr; - uint8_t dfssr; + uint8_t dfsrr; }; static bool mpc_i2c_is_enabled(MPCI2CState *s) @@ -293,7 +293,7 @@ static void mpc_i2c_write(void *opaque, hwaddr addr, } break; case MPC_I2C_DFSRR: - s->dfssr = value; + s->dfsrr = value; break; default: DPRINTF("ERROR: Bad write addr 0x%x\n", (unsigned int)addr); @@ -319,7 +319,7 @@ static const VMStateDescription mpc_i2c_vmstate = { VMSTATE_UINT8(cr, MPCI2CState), VMSTATE_UINT8(sr, MPCI2CState), VMSTATE_UINT8(dr, MPCI2CState), - VMSTATE_UINT8(dfssr, MPCI2CState), + VMSTATE_UINT8(dfsrr, MPCI2CState), VMSTATE_END_OF_LIST() } }; @@ -329,7 +329,7 @@ static void mpc_i2c_realize(DeviceState *dev, Error **errp) MPCI2CState *i2c = MPC_I2C(dev); sysbus_init_irq(SYS_BUS_DEVICE(dev), &i2c->irq); memory_region_init_io(&i2c->iomem, OBJECT(i2c), &i2c_ops, i2c, - "mpc-i2c", 0x14); + "mpc-i2c", 0x15); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &i2c->iomem); i2c->bus = i2c_init_bus(dev, "i2c"); } From c8f1a322d1f8c15f72cc57e469d2331a2d84da63 Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 05:17:27 -0400 Subject: [PATCH 2448/2884] hw/mips/loongson3_virt: remove useless type cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type of kernel_entry, kernel_low and kernel_high is uint64_t, cast the pointer of this type to uint64_t* is useless. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240722091728.4334-2-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/mips/loongson3_virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 4ad36f0c5b..408e3d7054 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -355,8 +355,8 @@ static uint64_t load_kernel(CPUMIPSState *env) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, - (uint64_t *)&kernel_entry, - (uint64_t *)&kernel_low, (uint64_t *)&kernel_high, + &kernel_entry, + &kernel_low, &kernel_high, NULL, 0, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", From 007643bddcebf9e7fb8460a4e8068c4f80afa17e Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:30 -0400 Subject: [PATCH 2449/2884] util/range: Make ranges_overlap() return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like range_overlaps_range(), use the returned bool value to check whether 2 given ranges overlap. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240722040742.11513-2-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- include/qemu/range.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/qemu/range.h b/include/qemu/range.h index 4ce694a398..d446ad885d 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -210,8 +210,8 @@ static inline int range_covers_byte(uint64_t offset, uint64_t len, /* Check whether 2 given ranges overlap. * Undefined if ranges that wrap around 0. */ -static inline int ranges_overlap(uint64_t first1, uint64_t len1, - uint64_t first2, uint64_t len2) +static inline bool ranges_overlap(uint64_t first1, uint64_t len1, + uint64_t first2, uint64_t len2) { uint64_t last1 = range_get_last(first1, len1); uint64_t last2 = range_get_last(first2, len2); From 7b3e371526d372a41a4d7db89b3a70b24d3a6198 Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:33 -0400 Subject: [PATCH 2450/2884] cxl/mailbox: make range overlap check more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use ranges_overlap() instead of open-coding the overlap check to improve the readability of the code. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240722040742.11513-5-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/cxl/cxl-mailbox-utils.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 74eeb6fde7..507690c0dd 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1086,8 +1086,8 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, QLIST_FOREACH(ent, poison_list, node) { /* Check for no overlap */ - if (ent->start >= query_start + query_length || - ent->start + ent->length <= query_start) { + if (!ranges_overlap(ent->start, ent->length, + query_start, query_length)) { continue; } record_count++; @@ -1100,8 +1100,8 @@ static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, uint64_t start, stop; /* Check for no overlap */ - if (ent->start >= query_start + query_length || - ent->start + ent->length <= query_start) { + if (!ranges_overlap(ent->start, ent->length, + query_start, query_length)) { continue; } From 2a48b590f7479b93623e650642aee2fce015bbd3 Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:37 -0400 Subject: [PATCH 2451/2884] sparc/ldst_helper: make range overlap check more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use ranges_overlap() instead of open-coding the overlap check to improve the readability of the code. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240722040742.11513-9-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- target/sparc/ldst_helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 2d48e98bf4..d92c9f1593 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/range.h" #include "cpu.h" #include "tcg/tcg.h" #include "exec/helper-proto.h" @@ -240,9 +241,7 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, if (new_ctx == ctx) { uint64_t vaddr = tlb[i].tag & ~0x1fffULL; uint64_t size = 8192ULL << 3 * TTE_PGSIZE(tlb[i].tte); - if (new_vaddr == vaddr - || (new_vaddr < vaddr + size - && vaddr < new_vaddr + new_size)) { + if (ranges_overlap(new_vaddr, new_size, vaddr, size)) { DPRINTF_MMU("auto demap entry [%d] %lx->%lx\n", i, vaddr, new_vaddr); replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1); From 13c59a0e9efad5154ff4aba46d5d645c99af8fcf Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:38 -0400 Subject: [PATCH 2452/2884] system/memory_mapping: make range overlap check more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use ranges_overlap() instead of open-coding the overlap check to improve the readability of the code. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: David Hildenbrand <david@redhat.com> Message-ID: <20240722040742.11513-10-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- system/memory_mapping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/memory_mapping.c b/system/memory_mapping.c index 6f884c5b90..ca2390eb80 100644 --- a/system/memory_mapping.c +++ b/system/memory_mapping.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include "qapi/error.h" #include "sysemu/memory_mapping.h" @@ -353,8 +354,7 @@ void memory_mapping_filter(MemoryMappingList *list, int64_t begin, MemoryMapping *cur, *next; QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) { - if (cur->phys_addr >= begin + length || - cur->phys_addr + cur->length <= begin) { + if (!ranges_overlap(cur->phys_addr, cur->length, begin, length)) { QTAILQ_REMOVE(&list->head, cur, next); g_free(cur); list->num--; From 7cd9b9d476e729808f3c9b82a12f51a39673d5cb Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:40 -0400 Subject: [PATCH 2453/2884] crypto/block-luks: make range overlap check more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use ranges_overlap() instead of open-coding the overlap check to improve the readability of the code. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240722040742.11513-12-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- crypto/block-luks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 5b777c15d3..45347adeeb 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -33,6 +33,7 @@ #include "qemu/uuid.h" #include "qemu/bitmap.h" +#include "qemu/range.h" /* * Reference for the LUKS format implemented here is @@ -572,7 +573,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, header_sectors, slot2->stripes); - if (start1 + len1 > start2 && start2 + len2 > start1) { + if (ranges_overlap(start1, len1, start2, len2)) { error_setg(errp, "Keyslots %zu and %zu are overlapping in the header", i, j); From 1aef44805df33f3f6e6302984660f4ea6b31fb3e Mon Sep 17 00:00:00 2001 From: Yao Xingtao <yaoxt.fnst@fujitsu.com> Date: Mon, 22 Jul 2024 00:07:41 -0400 Subject: [PATCH 2454/2884] dump: make range overlap check more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use ranges_overlap() instead of open-coding the overlap check to improve the readability of the code. Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20240722040742.11513-13-yaoxt.fnst@fujitsu.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- dump/dump.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index 84064d890d..45e84428ae 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -30,6 +30,7 @@ #include "migration/blocker.h" #include "hw/core/cpu.h" #include "win_dump.h" +#include "qemu/range.h" #include <zlib.h> #ifdef CONFIG_LZO @@ -574,8 +575,10 @@ static void get_offset_range(hwaddr phys_addr, QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { if (dump_has_filter(s)) { - if (block->target_start >= s->filter_area_begin + s->filter_area_length || - block->target_end <= s->filter_area_begin) { + if (!ranges_overlap(block->target_start, + block->target_end - block->target_start, + s->filter_area_begin, + s->filter_area_length)) { /* This block is out of the range */ continue; } @@ -734,8 +737,9 @@ int64_t dump_filtered_memblock_start(GuestPhysBlock *block, { if (filter_area_length) { /* return -1 if the block is not within filter area */ - if (block->target_start >= filter_area_start + filter_area_length || - block->target_end <= filter_area_start) { + if (!ranges_overlap(block->target_start, + block->target_end - block->target_start, + filter_area_start, filter_area_length)) { return -1; } From 2f28f28e74d9e8861701e5597b314fa85965ecd4 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Wed, 24 Jul 2024 00:18:02 +0800 Subject: [PATCH 2455/2884] hw/nubus/virtio-mmio: Fix missing ERRP_GUARD() in realize handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the comment in qapi/error.h, dereferencing @errp requires ERRP_GUARD(): * = Why, when and how to use ERRP_GUARD() = * * Without ERRP_GUARD(), use of the @errp parameter is restricted: * - It must not be dereferenced, because it may be null. ... * ERRP_GUARD() lifts these restrictions. * * To use ERRP_GUARD(), add it right at the beginning of the function. * @errp can then be used without worrying about the argument being * NULL or &error_fatal. * * Using it when it's not needed is safe, but please avoid cluttering * the source with useless code. In nubus_virtio_mmio_realize(), @errp is dereferenced without ERRP_GUARD(). Although nubus_virtio_mmio_realize() - as a DeviceClass.realize() method - is never passed a null @errp argument, it should follow the rules on @errp usage. Add the ERRP_GUARD() there. Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Message-ID: <20240723161802.1377985-1-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/nubus/nubus-virtio-mmio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/nubus/nubus-virtio-mmio.c b/hw/nubus/nubus-virtio-mmio.c index 58a63c84d0..7a98731c45 100644 --- a/hw/nubus/nubus-virtio-mmio.c +++ b/hw/nubus/nubus-virtio-mmio.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/nubus/nubus-virtio-mmio.h" @@ -23,6 +24,7 @@ static void nubus_virtio_mmio_set_input_irq(void *opaque, int n, int level) static void nubus_virtio_mmio_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); NubusVirtioMMIODeviceClass *nvmdc = NUBUS_VIRTIO_MMIO_GET_CLASS(dev); NubusVirtioMMIO *s = NUBUS_VIRTIO_MMIO(dev); NubusDevice *nd = NUBUS_DEVICE(dev); From c9e0b9a59ce6499c32affad6415560b1ad1d708f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 23 Jul 2024 20:14:28 +0200 Subject: [PATCH 2456/2884] hw/char/goldfish: Use DMA memory API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using address_space_rw(..., 0 or 1), use the simpler DMA memory API which expand to the same code. This allows removing a cast on the 'buf' variable which is really const. Since 'buf' is only used in the CMD_READ_BUFFER case, we can reduce its scope. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240723181850.46000-1-philmd@linaro.org> --- hw/char/goldfish_tty.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index f8ff043c39..cdff46bc13 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -16,6 +16,7 @@ #include "qemu/log.h" #include "trace.h" #include "exec/address-spaces.h" +#include "sysemu/dma.h" #include "hw/char/goldfish_tty.h" #define GOLDFISH_TTY_VERSION 1 @@ -69,7 +70,6 @@ static uint64_t goldfish_tty_read(void *opaque, hwaddr addr, static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) { uint32_t to_copy; - uint8_t *buf; uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE]; int len; uint64_t ptr; @@ -97,8 +97,8 @@ static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) while (len) { to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len); - address_space_rw(&address_space_memory, ptr, - MEMTXATTRS_UNSPECIFIED, data_out, to_copy, 0); + dma_memory_read_relaxed(&address_space_memory, ptr, + data_out, to_copy); qemu_chr_fe_write_all(&s->chr, data_out, to_copy); len -= to_copy; @@ -109,9 +109,9 @@ static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) len = s->data_len; ptr = s->data_ptr; while (len && !fifo8_is_empty(&s->rx_fifo)) { - buf = (uint8_t *)fifo8_pop_buf(&s->rx_fifo, len, &to_copy); - address_space_rw(&address_space_memory, ptr, - MEMTXATTRS_UNSPECIFIED, buf, to_copy, 1); + const uint8_t *buf = fifo8_pop_buf(&s->rx_fifo, len, &to_copy); + + dma_memory_write_relaxed(&address_space_memory, ptr, buf, to_copy); len -= to_copy; ptr += to_copy; From b07f07761d85c97d478d9df3978fcd32f8fbb163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Fri, 10 Nov 2023 06:49:35 +0100 Subject: [PATCH 2457/2884] chardev/char-fe: Document returned value on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_chr_fe_add_watch() and qemu_chr_fe_write[_all]() return -1 on error. Mention it in the documentation. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240722160745.67904-2-philmd@linaro.org> --- include/chardev/char-fe.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index ecef182835..3310449eaf 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -228,6 +228,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, * is thread-safe. * * Returns: the number of bytes consumed (0 if no associated Chardev) + * or -1 on error. */ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); @@ -242,6 +243,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); * attempted to be written. This function is thread-safe. * * Returns: the number of bytes consumed (0 if no associated Chardev) + * or -1 on error. */ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); @@ -253,6 +255,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); * Read data to a buffer from the back end. * * Returns: the number of bytes read (0 if no associated Chardev) + * or -1 on error. */ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len); From 5ebf19430a5fcb31db86dacb2f4beaf7a477a0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 22 Jul 2024 14:15:31 +0200 Subject: [PATCH 2458/2884] util/fifo8: Fix style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Message-Id: <20240722160745.67904-3-philmd@linaro.org> --- include/qemu/fifo8.h | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index c6295c6ff0..2692d6bfda 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -15,10 +15,9 @@ typedef struct { * @fifo: struct Fifo8 to initialise with new FIFO * @capacity: capacity of the newly created FIFO * - * Create a FIFO of the specified size. Clients should call fifo8_destroy() + * Create a FIFO of the specified capacity. Clients should call fifo8_destroy() * when finished using the fifo. The FIFO is initially empty. */ - void fifo8_create(Fifo8 *fifo, uint32_t capacity); /** @@ -26,9 +25,8 @@ void fifo8_create(Fifo8 *fifo, uint32_t capacity); * @fifo: FIFO to cleanup * * Cleanup a FIFO created with fifo8_create(). Frees memory created for FIFO - *storage. The FIFO is no longer usable after this has been called. + * storage. The FIFO is no longer usable after this has been called. */ - void fifo8_destroy(Fifo8 *fifo); /** @@ -39,7 +37,6 @@ void fifo8_destroy(Fifo8 *fifo); * Push a data byte to the FIFO. Behaviour is undefined if the FIFO is full. * Clients are responsible for checking for fullness using fifo8_is_full(). */ - void fifo8_push(Fifo8 *fifo, uint8_t data); /** @@ -52,7 +49,6 @@ void fifo8_push(Fifo8 *fifo, uint8_t data); * Clients are responsible for checking the space left in the FIFO using * fifo8_num_free(). */ - void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); /** @@ -64,7 +60,6 @@ void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); * * Returns: The popped data byte. */ - uint8_t fifo8_pop(Fifo8 *fifo); /** @@ -73,7 +68,7 @@ uint8_t fifo8_pop(Fifo8 *fifo); * @max: maximum number of bytes to pop * @numptr: pointer filled with number of bytes returned (can be NULL) * - * Pop a number of elements from the FIFO up to a maximum of max. The buffer + * Pop a number of elements from the FIFO up to a maximum of @max. The buffer * containing the popped data is returned. This buffer points directly into * the FIFO backing store and data is invalidated once any of the fifo8_* APIs * are called on the FIFO. @@ -82,7 +77,7 @@ uint8_t fifo8_pop(Fifo8 *fifo); * around in the ring buffer; in this case only a contiguous part of the data * is returned. * - * The number of valid bytes returned is populated in *numptr; will always + * The number of valid bytes returned is populated in *@numptr; will always * return at least 1 byte. max must not be 0 or greater than the number of * bytes in the FIFO. * @@ -99,7 +94,7 @@ const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); * @max: maximum number of bytes to peek * @numptr: pointer filled with number of bytes returned (can be NULL) * - * Peek into a number of elements from the FIFO up to a maximum of max. + * Peek into a number of elements from the FIFO up to a maximum of @max. * The buffer containing the data peeked into is returned. This buffer points * directly into the FIFO backing store. Since data is invalidated once any * of the fifo8_* APIs are called on the FIFO, it is the caller responsibility @@ -109,7 +104,7 @@ const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); * around in the ring buffer; in this case only a contiguous part of the data * is returned. * - * The number of valid bytes returned is populated in *numptr; will always + * The number of valid bytes returned is populated in *@numptr; will always * return at least 1 byte. max must not be 0 or greater than the number of * bytes in the FIFO. * @@ -126,7 +121,6 @@ const uint8_t *fifo8_peek_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); * * Reset a FIFO. All data is discarded and the FIFO is emptied. */ - void fifo8_reset(Fifo8 *fifo); /** @@ -137,7 +131,6 @@ void fifo8_reset(Fifo8 *fifo); * * Returns: True if the fifo is empty, false otherwise. */ - bool fifo8_is_empty(Fifo8 *fifo); /** @@ -148,7 +141,6 @@ bool fifo8_is_empty(Fifo8 *fifo); * * Returns: True if the fifo is full, false otherwise. */ - bool fifo8_is_full(Fifo8 *fifo); /** @@ -159,7 +151,6 @@ bool fifo8_is_full(Fifo8 *fifo); * * Returns: Number of free bytes. */ - uint32_t fifo8_num_free(Fifo8 *fifo); /** @@ -170,7 +161,6 @@ uint32_t fifo8_num_free(Fifo8 *fifo); * * Returns: Number of used bytes. */ - uint32_t fifo8_num_used(Fifo8 *fifo); extern const VMStateDescription vmstate_fifo8; From b81bc8dc8df414ff7767ee060d9234de45861089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Fri, 10 Nov 2023 07:11:32 +0100 Subject: [PATCH 2459/2884] util/fifo8: Use fifo8_reset() in fifo8_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid open-coding fifo8_reset() in fifo8_create(). Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240722160745.67904-4-philmd@linaro.org> --- util/fifo8.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/util/fifo8.c b/util/fifo8.c index 4e01b532d9..2925fe5611 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -16,12 +16,17 @@ #include "migration/vmstate.h" #include "qemu/fifo8.h" +void fifo8_reset(Fifo8 *fifo) +{ + fifo->num = 0; + fifo->head = 0; +} + void fifo8_create(Fifo8 *fifo, uint32_t capacity) { fifo->data = g_new(uint8_t, capacity); fifo->capacity = capacity; - fifo->head = 0; - fifo->num = 0; + fifo8_reset(fifo); } void fifo8_destroy(Fifo8 *fifo) @@ -97,12 +102,6 @@ const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr) return fifo8_peekpop_buf(fifo, max, numptr, true); } -void fifo8_reset(Fifo8 *fifo) -{ - fifo->num = 0; - fifo->head = 0; -} - bool fifo8_is_empty(Fifo8 *fifo) { return (fifo->num == 0); From 06a16e7ba98438087b8806bad0ddf094380892a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 22 Jul 2024 13:21:44 +0200 Subject: [PATCH 2460/2884] util/fifo8: Rename fifo8_peek_buf() -> fifo8_peek_bufptr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since fifo8_peek_buf() return a const buffer (which points directly into the FIFO backing store). Rename it using the 'bufptr' suffix to better reflect that it is a pointer to the internal buffer that is being returned. This will help differentiate with methods *copying* the FIFO data. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Message-Id: <20240722160745.67904-5-philmd@linaro.org> --- hw/scsi/esp.c | 2 +- include/qemu/fifo8.h | 11 ++++++----- util/fifo8.c | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 8504dd30a0..412c8cf226 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -486,7 +486,7 @@ static bool esp_cdb_ready(ESPState *s) return false; } - pbuf = fifo8_peek_buf(&s->cmdfifo, len, &n); + pbuf = fifo8_peek_bufptr(&s->cmdfifo, len, &n); if (n < len) { /* * In normal use the cmdfifo should never wrap, but include this check diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index 2692d6bfda..e287e87119 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -89,16 +89,17 @@ uint8_t fifo8_pop(Fifo8 *fifo); const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); /** - * fifo8_peek_buf: read upto max bytes from the fifo + * fifo8_peek_bufptr: read upto max bytes from the fifo * @fifo: FIFO to read from * @max: maximum number of bytes to peek * @numptr: pointer filled with number of bytes returned (can be NULL) * * Peek into a number of elements from the FIFO up to a maximum of @max. * The buffer containing the data peeked into is returned. This buffer points - * directly into the FIFO backing store. Since data is invalidated once any - * of the fifo8_* APIs are called on the FIFO, it is the caller responsibility - * to access it before doing further API calls. + * directly into the internal FIFO backing store (without checking for + * overflow!). Since data is invalidated once any of the fifo8_* APIs are + * called on the FIFO, it is the caller responsibility to access it before + * doing further API calls. * * The function may return fewer bytes than requested when the data wraps * around in the ring buffer; in this case only a contiguous part of the data @@ -113,7 +114,7 @@ const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); * * Returns: A pointer to peekable data. */ -const uint8_t *fifo8_peek_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); +const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); /** * fifo8_reset: diff --git a/util/fifo8.c b/util/fifo8.c index 2925fe5611..566b089344 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -92,7 +92,7 @@ static const uint8_t *fifo8_peekpop_buf(Fifo8 *fifo, uint32_t max, return ret; } -const uint8_t *fifo8_peek_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) { return fifo8_peekpop_buf(fifo, max, numptr, false); } From 06252bf5122f047ed5c14fb75f3167f11b882352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 22 Jul 2024 13:25:01 +0200 Subject: [PATCH 2461/2884] util/fifo8: Rename fifo8_pop_buf() -> fifo8_pop_bufptr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since fifo8_pop_buf() return a const buffer (which points directly into the FIFO backing store). Rename it using the 'bufptr' suffix to better reflect that it is a pointer to the internal buffer that is being returned. This will help differentiate with methods *copying* the FIFO data. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Message-Id: <20240722160745.67904-6-philmd@linaro.org> --- chardev/msmouse.c | 2 +- hw/char/goldfish_tty.c | 2 +- hw/net/allwinner_emac.c | 2 +- hw/scsi/esp.c | 4 ++-- include/qemu/fifo8.h | 15 +++++++-------- ui/console-vc.c | 2 +- ui/gtk.c | 2 +- util/fifo8.c | 2 +- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/chardev/msmouse.c b/chardev/msmouse.c index a774c397b4..2279694cfa 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -81,7 +81,7 @@ static void msmouse_chr_accept_input(Chardev *chr) const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&mouse->outbuf, MIN(len, avail), &size); + buf = fifo8_pop_bufptr(&mouse->outbuf, MIN(len, avail), &size); qemu_chr_be_write(chr, buf, size); len = qemu_chr_be_can_write(chr); avail -= size; diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index cdff46bc13..c2e1f6537f 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -109,7 +109,7 @@ static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) len = s->data_len; ptr = s->data_ptr; while (len && !fifo8_is_empty(&s->rx_fifo)) { - const uint8_t *buf = fifo8_pop_buf(&s->rx_fifo, len, &to_copy); + const uint8_t *buf = fifo8_pop_bufptr(&s->rx_fifo, len, &to_copy); dma_memory_write_relaxed(&address_space_memory, ptr, buf, to_copy); diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 989839784a..d40ff37e99 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -349,7 +349,7 @@ static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, "allwinner_emac: TX length > fifo data length\n"); } if (len > 0) { - data = fifo8_pop_buf(fifo, len, &ret); + data = fifo8_pop_bufptr(fifo, len, &ret); qemu_send_packet(nc, data, ret); aw_emac_tx_reset(s, chan); /* Raise TX interrupt */ diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 412c8cf226..7e9657e9c3 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -208,7 +208,7 @@ static uint32_t esp_fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) } len = maxlen; - buf = fifo8_pop_buf(fifo, len, &n); + buf = fifo8_pop_bufptr(fifo, len, &n); if (dest) { memcpy(dest, buf, n); } @@ -217,7 +217,7 @@ static uint32_t esp_fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) len -= n; len = MIN(len, fifo8_num_used(fifo)); if (len) { - buf = fifo8_pop_buf(fifo, len, &n2); + buf = fifo8_pop_bufptr(fifo, len, &n2); if (dest) { memcpy(&dest[n], buf, n2); } diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index e287e87119..a30220c8e9 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -63,15 +63,15 @@ void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); uint8_t fifo8_pop(Fifo8 *fifo); /** - * fifo8_pop_buf: + * fifo8_pop_bufptr: * @fifo: FIFO to pop from * @max: maximum number of bytes to pop * @numptr: pointer filled with number of bytes returned (can be NULL) * * Pop a number of elements from the FIFO up to a maximum of @max. The buffer * containing the popped data is returned. This buffer points directly into - * the FIFO backing store and data is invalidated once any of the fifo8_* APIs - * are called on the FIFO. + * the internal FIFO backing store and data (without checking for overflow!) + * and is invalidated once any of the fifo8_* APIs are called on the FIFO. * * The function may return fewer bytes than requested when the data wraps * around in the ring buffer; in this case only a contiguous part of the data @@ -86,7 +86,7 @@ uint8_t fifo8_pop(Fifo8 *fifo); * * Returns: A pointer to popped data. */ -const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); +const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); /** * fifo8_peek_bufptr: read upto max bytes from the fifo @@ -96,10 +96,9 @@ const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); * * Peek into a number of elements from the FIFO up to a maximum of @max. * The buffer containing the data peeked into is returned. This buffer points - * directly into the internal FIFO backing store (without checking for - * overflow!). Since data is invalidated once any of the fifo8_* APIs are - * called on the FIFO, it is the caller responsibility to access it before - * doing further API calls. + * directly into the FIFO backing store. Since data is invalidated once any + * of the fifo8_* APIs are called on the FIFO, it is the caller responsibility + * to access it before doing further API calls. * * The function may return fewer bytes than requested when the data wraps * around in the ring buffer; in this case only a contiguous part of the data diff --git a/ui/console-vc.c b/ui/console-vc.c index 899fa11c94..8393d532e7 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -287,7 +287,7 @@ static void kbd_send_chars(QemuTextConsole *s) const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size); + buf = fifo8_pop_bufptr(&s->out_fifo, MIN(len, avail), &size); qemu_chr_be_write(s->chr, buf, size); len = qemu_chr_be_can_write(s->chr); avail -= size; diff --git a/ui/gtk.c b/ui/gtk.c index bc29f7a1b4..8e14c2ac81 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1820,7 +1820,7 @@ static void gd_vc_send_chars(VirtualConsole *vc) const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&vc->vte.out_fifo, MIN(len, avail), &size); + buf = fifo8_pop_bufptr(&vc->vte.out_fifo, MIN(len, avail), &size); qemu_chr_be_write(vc->vte.chr, buf, size); len = qemu_chr_be_can_write(vc->vte.chr); avail -= size; diff --git a/util/fifo8.c b/util/fifo8.c index 566b089344..5bbb6150b6 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -97,7 +97,7 @@ const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) return fifo8_peekpop_buf(fifo, max, numptr, false); } -const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) { return fifo8_peekpop_buf(fifo, max, numptr, true); } From 23ad5711730f994d66520a3283aac2979d89389e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 22 Jul 2024 13:27:53 +0200 Subject: [PATCH 2462/2884] util/fifo8: Expose fifo8_pop_buf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract fifo8_pop_buf() from hw/scsi/esp.c and expose it as part of the <qemu/fifo8.h> API. This function takes care of non-contiguous (wrapped) FIFO buffer (which is an implementation detail). Suggested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Message-Id: <20240722160745.67904-7-philmd@linaro.org> --- hw/scsi/esp.c | 36 +++--------------------------------- include/qemu/fifo8.h | 16 ++++++++++++++++ util/fifo8.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 7e9657e9c3..04c9a557b6 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -197,39 +197,9 @@ static uint8_t esp_fifo_pop(ESPState *s) return val; } -static uint32_t esp_fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) -{ - const uint8_t *buf; - uint32_t n, n2; - int len; - - if (maxlen == 0) { - return 0; - } - - len = maxlen; - buf = fifo8_pop_bufptr(fifo, len, &n); - if (dest) { - memcpy(dest, buf, n); - } - - /* Add FIFO wraparound if needed */ - len -= n; - len = MIN(len, fifo8_num_used(fifo)); - if (len) { - buf = fifo8_pop_bufptr(fifo, len, &n2); - if (dest) { - memcpy(&dest[n], buf, n2); - } - n += n2; - } - - return n; -} - static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t *dest, int maxlen) { - uint32_t len = esp_fifo8_pop_buf(&s->fifo, dest, maxlen); + uint32_t len = fifo8_pop_buf(&s->fifo, dest, maxlen); esp_update_drq(s); return len; @@ -335,7 +305,7 @@ static void do_command_phase(ESPState *s) if (!cmdlen || !s->current_dev) { return; } - esp_fifo8_pop_buf(&s->cmdfifo, buf, cmdlen); + fifo8_pop_buf(&s->cmdfifo, buf, cmdlen); current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, s->lun); if (!current_lun) { @@ -381,7 +351,7 @@ static void do_message_phase(ESPState *s) /* Ignore extended messages for now */ if (s->cmdfifo_cdb_offset) { int len = MIN(s->cmdfifo_cdb_offset, fifo8_num_used(&s->cmdfifo)); - esp_fifo8_pop_buf(&s->cmdfifo, NULL, len); + fifo8_pop_buf(&s->cmdfifo, NULL, len); s->cmdfifo_cdb_offset = 0; } } diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index a30220c8e9..bca6da306f 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -62,12 +62,28 @@ void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); */ uint8_t fifo8_pop(Fifo8 *fifo); +/** + * fifo8_pop_buf: + * @fifo: FIFO to pop from + * @dest: the buffer to write the data into (can be NULL) + * @destlen: size of @dest and maximum number of bytes to pop + * + * Pop a number of elements from the FIFO up to a maximum of @destlen. + * The popped data is copied into the @dest buffer. + * Care is taken when the data wraps around in the ring buffer. + * + * Returns: number of bytes popped. + */ +uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen); + /** * fifo8_pop_bufptr: * @fifo: FIFO to pop from * @max: maximum number of bytes to pop * @numptr: pointer filled with number of bytes returned (can be NULL) * + * New code should prefer to use fifo8_pop_buf() instead of fifo8_pop_bufptr(). + * * Pop a number of elements from the FIFO up to a maximum of @max. The buffer * containing the popped data is returned. This buffer points directly into * the internal FIFO backing store and data (without checking for overflow!) diff --git a/util/fifo8.c b/util/fifo8.c index 5bbb6150b6..a250ea9f80 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -102,6 +102,35 @@ const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) return fifo8_peekpop_buf(fifo, max, numptr, true); } +uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen) +{ + const uint8_t *buf; + uint32_t n1, n2 = 0; + uint32_t len; + + if (destlen == 0) { + return 0; + } + + len = destlen; + buf = fifo8_pop_bufptr(fifo, len, &n1); + if (dest) { + memcpy(dest, buf, n1); + } + + /* Add FIFO wraparound if needed */ + len -= n1; + len = MIN(len, fifo8_num_used(fifo)); + if (len) { + buf = fifo8_pop_bufptr(fifo, len, &n2); + if (dest) { + memcpy(&dest[n1], buf, n2); + } + } + + return n1 + n2; +} + bool fifo8_is_empty(Fifo8 *fifo) { return (fifo->num == 0); From e4e9db25624a93e8b02890a15bba0958db33c669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Mon, 22 Jul 2024 13:37:48 +0200 Subject: [PATCH 2463/2884] util/fifo8: Introduce fifo8_drop() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the fifo8_drop() helper for clarity. It is a simple wrapper over fifo8_pop_buf(). Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Message-Id: <20240722160745.67904-8-philmd@linaro.org> --- hw/scsi/esp.c | 2 +- include/qemu/fifo8.h | 9 +++++++++ util/fifo8.c | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 04c9a557b6..b7af825623 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -351,7 +351,7 @@ static void do_message_phase(ESPState *s) /* Ignore extended messages for now */ if (s->cmdfifo_cdb_offset) { int len = MIN(s->cmdfifo_cdb_offset, fifo8_num_used(&s->cmdfifo)); - fifo8_pop_buf(&s->cmdfifo, NULL, len); + fifo8_drop(&s->cmdfifo, len); s->cmdfifo_cdb_offset = 0; } } diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index bca6da306f..d1d06754d8 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -131,6 +131,15 @@ const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); */ const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); +/** + * fifo8_drop: + * @fifo: FIFO to drop bytes + * @len: number of bytes to drop + * + * Drop (consume) bytes from a FIFO. + */ +void fifo8_drop(Fifo8 *fifo, uint32_t len); + /** * fifo8_reset: * @fifo: FIFO to reset diff --git a/util/fifo8.c b/util/fifo8.c index a250ea9f80..1ffa19d900 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -131,6 +131,12 @@ uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen) return n1 + n2; } +void fifo8_drop(Fifo8 *fifo, uint32_t len) +{ + len -= fifo8_pop_buf(fifo, NULL, len); + assert(len == 0); +} + bool fifo8_is_empty(Fifo8 *fifo) { return (fifo->num == 0); From 632d3b2276d22d879d66652f617826c30fd51f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Wed, 17 Jul 2024 15:58:28 +0200 Subject: [PATCH 2464/2884] MAINTAINERS: Cover guest-agent in QAPI schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240717140025.66397-1-philmd@linaro.org> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index d5ff6c2498..8c048782a6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3218,6 +3218,7 @@ M: Eric Blake <eblake@redhat.com> M: Markus Armbruster <armbru@redhat.com> S: Supported F: qapi/*.json +F: qga/qapi-schema.json T: git https://repo.or.cz/qemu/armbru.git qapi-next QObject From e4a44f94db11fcf873399c60ebb1d93a9ad1cd6e Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Tue, 23 Jul 2024 00:44:12 +0800 Subject: [PATCH 2465/2884] MAINTAINERS: Add myself as a reviewer of machine core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While working on a series of CPU/cache topology work, I became interested in the machine core component and would like to help to review more related patches. Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240722164412.1163414-1-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8c048782a6..a8e9d1b001 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1878,6 +1878,7 @@ M: Eduardo Habkost <eduardo@habkost.net> M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> R: Philippe Mathieu-Daudé <philmd@linaro.org> R: Yanan Wang <wangyanan55@huawei.com> +R: Zhao Liu <zhao1.liu@intel.com> S: Supported F: hw/core/cpu-common.c F: hw/core/cpu-sysemu.c From a18ffbcf8b9fabfc6c850ebb1d3e40a21b885c67 Mon Sep 17 00:00:00 2001 From: Song Gao <gaosong@loongson.cn> Date: Wed, 24 Jul 2024 09:58:53 +0800 Subject: [PATCH 2466/2884] target/loongarch: Fix helper_lddir() a CID INTEGER_OVERFLOW issue When the lddir level is 4 and the base is a HugePage, we may try to put value 4 into a field in the TLBENTRY that is only 2 bits wide. Fixes: Coverity CID 1547717 Fixes: 9c70db9a43388 ("target/loongarch: Fix tlb huge page loading issue") Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240724015853.1317396-1-gaosong@loongson.cn> --- target/loongarch/tcg/tlb_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index d6331f9b0b..97f38fc391 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -525,6 +525,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, if (unlikely(level == 4)) { qemu_log_mask(LOG_GUEST_ERROR, "Attempted use of level 4 huge page\n"); + return base; } if (FIELD_EX64(base, TLBENTRY, LEVEL)) { From e8ad8b9987efdbac4116567e685e6fd8ec28ef48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 2 May 2024 11:56:40 +0200 Subject: [PATCH 2467/2884] crypto: Remove 'crypto-tls-x509-helpers.h' from crypto-tls-psk-helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crypto-tls-psk-helpers.c doesn't access the declarations of "crypto-tls-x509-helpers.h", remove the include line to avoid when building with GNUTLS but without Libtasn1: In file included from tests/unit/crypto-tls-psk-helpers.c:23: tests/unit/crypto-tls-x509-helpers.h:26:10: fatal error: libtasn1.h: No such file or directory 26 | #include <libtasn1.h> | ^~~~~~~~~~~~ compilation terminated. Fixes: e1a6dc91dd ("crypto: Implement TLS Pre-Shared Keys (PSK).") Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- tests/unit/crypto-tls-psk-helpers.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/crypto-tls-psk-helpers.c b/tests/unit/crypto-tls-psk-helpers.c index c6cc740772..36527fd655 100644 --- a/tests/unit/crypto-tls-psk-helpers.c +++ b/tests/unit/crypto-tls-psk-helpers.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" -#include "crypto-tls-x509-helpers.h" #include "crypto-tls-psk-helpers.h" #include "qemu/sockets.h" From 0e9bb8ad4d907d28aea8336d43ae990d87b1c834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 2 May 2024 11:56:41 +0200 Subject: [PATCH 2468/2884] crypto: Restrict pkix_asn1_tab[] to crypto-tls-x509-helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pkix_asn1_tab[] is only accessed by crypto-tls-x509-helpers.c, rename pkix_asn1_tab.c as pkix_asn1_tab.c.inc and include it once. Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> [berrange: updated MAINTAINERS for changed filename] Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- MAINTAINERS | 2 +- tests/qtest/meson.build | 3 +-- tests/unit/crypto-tls-x509-helpers.c | 6 +++++- tests/unit/crypto-tls-x509-helpers.h | 3 --- tests/unit/meson.build | 6 +++--- tests/unit/{pkix_asn1_tab.c => pkix_asn1_tab.c.inc} | 5 +---- 6 files changed, 11 insertions(+), 14 deletions(-) rename tests/unit/{pkix_asn1_tab.c => pkix_asn1_tab.c.inc} (99%) diff --git a/MAINTAINERS b/MAINTAINERS index dd01288992..73040829b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3484,7 +3484,7 @@ F: qapi/crypto.json F: tests/unit/test-crypto-* F: tests/bench/benchmark-crypto-* F: tests/unit/crypto-tls-* -F: tests/unit/pkix_asn1_tab.c +F: tests/unit/pkix_asn1_tab.c.inc F: qemu.sasl Coroutines diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ff9200f882..e7ab2a4312 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -322,8 +322,7 @@ if gnutls.found() migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] if tasn1.found() - migration_files += [files('../unit/crypto-tls-x509-helpers.c', - '../unit/pkix_asn1_tab.c'), tasn1] + migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] endif endif diff --git a/tests/unit/crypto-tls-x509-helpers.c b/tests/unit/crypto-tls-x509-helpers.c index e9937f60d8..3e74ec5b5d 100644 --- a/tests/unit/crypto-tls-x509-helpers.c +++ b/tests/unit/crypto-tls-x509-helpers.c @@ -20,15 +20,19 @@ #include "qemu/osdep.h" +#include <libtasn1.h> + #include "crypto-tls-x509-helpers.h" #include "crypto/init.h" #include "qemu/sockets.h" +#include "pkix_asn1_tab.c.inc" + /* * This stores some static data that is needed when * encoding extensions in the x509 certs */ -asn1_node pkix_asn1; +static asn1_node pkix_asn1; /* * To avoid consuming random entropy to generate keys, diff --git a/tests/unit/crypto-tls-x509-helpers.h b/tests/unit/crypto-tls-x509-helpers.h index 247e7160eb..562c160653 100644 --- a/tests/unit/crypto-tls-x509-helpers.h +++ b/tests/unit/crypto-tls-x509-helpers.h @@ -23,7 +23,6 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> -#include <libtasn1.h> #define QCRYPTO_TLS_TEST_CLIENT_NAME "ACME QEMU Client" @@ -171,6 +170,4 @@ void test_tls_cleanup(const char *keyfile); }; \ test_tls_generate_cert(&varname, cavarname.crt) -extern const asn1_static_node pkix_asn1_tab[]; - #endif diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 26c109c968..490ab8182d 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -99,11 +99,11 @@ if have_block tasn1.found() and \ host_os != 'windows' tests += { - 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', + 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', tasn1, crypto, gnutls], - 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', 'crypto-tls-psk-helpers.c', + 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'crypto-tls-psk-helpers.c', tasn1, crypto, gnutls], - 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', + 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', tasn1, io, crypto, gnutls]} endif if pam.found() diff --git a/tests/unit/pkix_asn1_tab.c b/tests/unit/pkix_asn1_tab.c.inc similarity index 99% rename from tests/unit/pkix_asn1_tab.c rename to tests/unit/pkix_asn1_tab.c.inc index 89521408a1..fe29c4102a 100644 --- a/tests/unit/pkix_asn1_tab.c +++ b/tests/unit/pkix_asn1_tab.c.inc @@ -3,10 +3,7 @@ * and is under copyright of various GNUTLS contributors. */ -#include "qemu/osdep.h" -#include "crypto-tls-x509-helpers.h" - -const asn1_static_node pkix_asn1_tab[] = { +static const asn1_static_node pkix_asn1_tab[] = { {"PKIX1", 536875024, 0}, {0, 1073741836, 0}, {"id-ce", 1879048204, 0}, From dc37d1c56b87c6f4bc354d07791c9e69549e1524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 2 May 2024 11:56:42 +0200 Subject: [PATCH 2469/2884] crypto: Allow building with GnuTLS but without Libtasn1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only use Libtasn1 in unit tests. As noted in commit d47b83b118 ("tests: add migration tests of TLS with x509 credentials"), having GnuTLS without Libtasn1 is a valid configuration, so do not require Libtasn1, to avoid: Dependency gnutls found: YES 3.7.1 (cached) Run-time dependency libtasn1 found: NO (tried pkgconfig) ../meson.build:1914:10: ERROR: Dependency "libtasn1" not found, tried pkgconfig Fixes: ba7ed407e6 ("configure, meson: convert libtasn1 detection to meson") Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index af9f0380e2..4eca361319 100644 --- a/meson.build +++ b/meson.build @@ -1979,6 +1979,7 @@ endif tasn1 = not_found if gnutls.found() tasn1 = dependency('libtasn1', + required: false, method: 'pkg-config') endif keyutils = not_found From 9263c4173ff5941a8ad1865254131da68d0d7084 Mon Sep 17 00:00:00 2001 From: Hyman Huang <yong.huang@smartx.com> Date: Tue, 20 Feb 2024 00:04:42 +0800 Subject: [PATCH 2470/2884] docs/devel: Add introduction to LUKS volume with detached header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hyman Huang <yong.huang@smartx.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- MAINTAINERS | 1 + docs/devel/crypto.rst | 10 ++ docs/devel/index-internals.rst | 1 + docs/devel/luks-detached-header.rst | 182 ++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 docs/devel/crypto.rst create mode 100644 docs/devel/luks-detached-header.rst diff --git a/MAINTAINERS b/MAINTAINERS index 73040829b1..98eddf7ae1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3451,6 +3451,7 @@ Detached LUKS header M: Hyman Huang <yong.huang@smartx.com> S: Maintained F: tests/qemu-iotests/tests/luks-detached-header +F: docs/devel/luks-detached-header.rst D-Bus M: Marc-André Lureau <marcandre.lureau@redhat.com> diff --git a/docs/devel/crypto.rst b/docs/devel/crypto.rst new file mode 100644 index 0000000000..39b1c910e7 --- /dev/null +++ b/docs/devel/crypto.rst @@ -0,0 +1,10 @@ +.. _crypto-ref: + +==================== +Cryptography in QEMU +==================== + +.. toctree:: + :maxdepth: 2 + + luks-detached-header diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index 5636e9cf1d..4ac7725d72 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -20,3 +20,4 @@ Details about QEMU's various subsystems including how to add features to them. vfio-iommufd writing-monitor-commands virtio-backends + crypto diff --git a/docs/devel/luks-detached-header.rst b/docs/devel/luks-detached-header.rst new file mode 100644 index 0000000000..94ec285c27 --- /dev/null +++ b/docs/devel/luks-detached-header.rst @@ -0,0 +1,182 @@ +================================ +LUKS volume with detached header +================================ + +Introduction +============ + +This document gives an overview of the design of LUKS volume with detached +header and how to use it. + +Background +========== + +The LUKS format has ability to store the header in a separate volume from +the payload. We could extend the LUKS driver in QEMU to support this use +case. + +Normally a LUKS volume has a layout: + +:: + + +-----------------------------------------------+ + | | | | + disk | header | key material | disk payload data | + | | | | + +-----------------------------------------------+ + +With a detached LUKS header, you need 2 disks so getting: + +:: + + +--------------------------+ + disk1 | header | key material | + +--------------------------+ + +---------------------+ + disk2 | disk payload data | + +---------------------+ + +There are a variety of benefits to doing this: + + * Secrecy - the disk2 cannot be identified as containing LUKS + volume since there's no header + * Control - if access to the disk1 is restricted, then even + if someone has access to disk2 they can't unlock + it. Might be useful if you have disks on NFS but + want to restrict which host can launch a VM + instance from it, by dynamically providing access + to the header to a designated host + * Flexibility - your application data volume may be a given + size and it is inconvenient to resize it to + add encryption.You can store the LUKS header + separately and use the existing storage + volume for payload + * Recovery - corruption of a bit in the header may make the + entire payload inaccessible. It might be + convenient to take backups of the header. If + your primary disk header becomes corrupt, you + can unlock the data still by pointing to the + backup detached header + +Architecture +============ + +Take the qcow2 encryption, for example. The architecture of the +LUKS volume with detached header is shown in the diagram below. + +There are two children of the root node: a file and a header. +Data from the disk payload is stored in the file node. The +LUKS header and key material are located in the header node, +as previously mentioned. + +:: + + +-----------------------------+ + Root node | foo[luks] | + +-----------------------------+ + | | + file | header | + | | + +---------------------+ +------------------+ + Child node |payload-format[qcow2]| |header-format[raw]| + +---------------------+ +------------------+ + | | + file | file | + | | + +----------------------+ +---------------------+ + Child node |payload-protocol[file]| |header-protocol[file]| + +----------------------+ +---------------------+ + | | + | | + | | + Host storage Host storage + +Usage +===== + +Create a LUKS disk with a detached header using qemu-img +-------------------------------------------------------- + +Shell commandline:: + + # qemu-img create --object secret,id=sec0,data=abc123 -f luks \ + -o cipher-alg=aes-256,cipher-mode=xts -o key-secret=sec0 \ + -o detached-header=true test-header.img + # qemu-img create -f qcow2 test-payload.qcow2 200G + # qemu-img info 'json:{"driver":"luks","file":{"filename": \ + "test-payload.img"},"header":{"filename":"test-header.img"}}' + +Set up a VM's LUKS volume with a detached header +------------------------------------------------ + +Qemu commandline:: + + # qemu-system-x86_64 ... \ + -object '{"qom-type":"secret","id":"libvirt-3-format-secret", \ + "data":"abc123"}' \ + -blockdev '{"driver":"file","filename":"/path/to/test-header.img", \ + "node-name":"libvirt-1-storage"}' \ + -blockdev '{"node-name":"libvirt-1-format","read-only":false, \ + "driver":"raw","file":"libvirt-1-storage"}' \ + -blockdev '{"driver":"file","filename":"/path/to/test-payload.qcow2", \ + "node-name":"libvirt-2-storage"}' \ + -blockdev '{"node-name":"libvirt-2-format","read-only":false, \ + "driver":"qcow2","file":"libvirt-2-storage"}' \ + -blockdev '{"node-name":"libvirt-3-format","driver":"luks", \ + "file":"libvirt-2-format","header":"libvirt-1-format","key-secret": \ + "libvirt-3-format-secret"}' \ + -device '{"driver":"virtio-blk-pci","bus":XXX,"addr":YYY,"drive": \ + "libvirt-3-format","id":"virtio-disk1"}' + +Add LUKS volume to a VM with a detached header +---------------------------------------------- + +1. object-add the secret for decrypting the cipher stored in + LUKS header above:: + + # virsh qemu-monitor-command vm '{"execute":"object-add", \ + "arguments":{"qom-type":"secret", "id": \ + "libvirt-4-format-secret", "data":"abc123"}}' + +2. block-add the protocol node for LUKS header:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-1-storage", "driver":"file", \ + "filename": "/path/to/test-header.img" }}' + +3. block-add the raw-drived node for LUKS header:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-1-format", "driver":"raw", \ + "file":"libvirt-1-storage"}}' + +4. block-add the protocol node for disk payload image:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-2-storage", "driver":"file", \ + "filename":"/path/to/test-payload.qcow2"}}' + +5. block-add the qcow2-drived format node for disk payload data:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-2-format", "driver":"qcow2", \ + "file":"libvirt-2-storage"}}' + +6. block-add the luks-drived format node to link the qcow2 disk + with the LUKS header by specifying the field "header":: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-3-format", "driver":"luks", \ + "file":"libvirt-2-format", "header":"libvirt-1-format", \ + "key-secret":"libvirt-2-format-secret"}}' + +7. hot-plug the virtio-blk device finally:: + + # virsh qemu-monitor-command vm '{"execute":"device_add", \ + "arguments": {"driver":"virtio-blk-pci", \ + "drive": "libvirt-3-format", "id":"virtio-disk2"}} + +TODO +==== + +1. Support the shared detached LUKS header within the VM. From 581b4cd5f16d618787bd1e292b851c62c2626da0 Mon Sep 17 00:00:00 2001 From: Yao Zi <ziyao@disroot.org> Date: Sat, 6 Jul 2024 20:12:26 +0000 Subject: [PATCH 2471/2884] meson.build: fix libgcrypt detection on system without libgcrypt-config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libgcrypt starts providing correct pkg-config configuration since 1.9, in parallel with libgcrypt-config. Since 1.11 it may also stop installing libgcrypt-config in some scenarios. Use the auto method for detection of libgcrypt, in which meson will try both pkg-config and libgcrypt-config. Auto method for libgcrypt is supported by meson since 0.49.0, which is higher than the version qemu requires. Signed-off-by: Yao Zi <ziyao@disroot.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/meson.build b/meson.build index 4eca361319..ec6fb7d69c 100644 --- a/meson.build +++ b/meson.build @@ -1696,7 +1696,6 @@ endif if not gnutls_crypto.found() if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled() gcrypt = dependency('libgcrypt', version: '>=1.8', - method: 'config-tool', required: get_option('gcrypt')) # Debian has removed -lgpg-error from libgcrypt-config # as it "spreads unnecessary dependencies" which in From e818c01ae6e7c54c7019baaf307be59d99ce80b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Mon, 19 Feb 2024 15:12:59 +0000 Subject: [PATCH 2472/2884] qapi: drop unused QCryptoBlockCreateOptionsLUKS.detached-header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'detached-header' field in QCryptoBlockCreateOptionsLUKS was left over from earlier patch iterations. Acked-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- qapi/crypto.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qapi/crypto.json b/qapi/crypto.json index e102be337b..f03bdab8c9 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -226,8 +226,6 @@ # @iter-time: number of milliseconds to spend in PBKDF passphrase # processing. Currently defaults to 2000. (since 2.8) # -# @detached-header: create a detached LUKS header. (since 9.0) -# # Since: 2.6 ## { 'struct': 'QCryptoBlockCreateOptionsLUKS', @@ -237,8 +235,7 @@ '*ivgen-alg': 'QCryptoIVGenAlgorithm', '*ivgen-hash-alg': 'QCryptoHashAlgorithm', '*hash-alg': 'QCryptoHashAlgorithm', - '*iter-time': 'int', - '*detached-header': 'bool'}} + '*iter-time': 'int' }} ## # @QCryptoBlockOpenOptions: From 48fc887436ce0fa54debec23219554194a13a6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Tue, 23 Jul 2024 11:31:24 +0100 Subject: [PATCH 2473/2884] meson: build chardev trace files when have_block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QSD depends on chardev code, and is built when have_tools is true. This means conditionalizing chardev trace on have_system is wrong, we need have_block which is set have_system || have_tools. This latent bug was historically harmless because only the spice chardev included tracing, which wasn't built in a !have_system scenario. Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ec6fb7d69c..5613b62a4f 100644 --- a/meson.build +++ b/meson.build @@ -3343,6 +3343,7 @@ if have_block trace_events_subdirs += [ 'authz', 'block', + 'chardev', 'io', 'nbd', 'scsi', @@ -3354,7 +3355,6 @@ if have_system 'audio', 'backends', 'backends/tpm', - 'chardev', 'ebpf', 'hw/9pfs', 'hw/acpi', From 6ffade7974171b9091b3cdd38a76477fd4f71d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 15 Mar 2024 13:04:44 +0000 Subject: [PATCH 2474/2884] chardev: add tracing of socket error conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds trace points to every error scenario in the chardev socket backend that can lead to termination of the connection. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- chardev/char-socket.c | 37 ++++++++++++++++++++++++++----------- chardev/trace-events | 10 ++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 812d7aa38a..1ca9441b1b 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -33,6 +33,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-sockets.h" #include "qemu/yank.h" +#include "trace.h" #include "chardev/char-io.h" #include "chardev/char-socket.h" @@ -126,6 +127,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) if (ret < 0 && errno != EAGAIN) { if (tcp_chr_read_poll(chr) <= 0) { /* Perform disconnect and return error. */ + trace_chr_socket_poll_err(chr, chr->label); tcp_chr_disconnect_locked(chr); } /* else let the read handler finish it properly */ } @@ -279,15 +281,16 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) size_t i; int *msgfds = NULL; size_t msgfds_num = 0; + Error *err = NULL; if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { ret = qio_channel_readv_full(s->ioc, &iov, 1, &msgfds, &msgfds_num, - 0, NULL); + 0, &err); } else { ret = qio_channel_readv_full(s->ioc, &iov, 1, NULL, NULL, - 0, NULL); + 0, &err); } if (msgfds_num) { @@ -322,7 +325,11 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) errno = EAGAIN; ret = -1; } else if (ret == -1) { + trace_chr_socket_recv_err(chr, chr->label, error_get_pretty(err)); + error_free(err); errno = EIO; + } else if (ret == 0) { + trace_chr_socket_recv_eof(chr, chr->label); } return ret; @@ -463,6 +470,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr) SocketChardev *s = SOCKET_CHARDEV(chr); bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED; + trace_chr_socket_disconnect(chr, chr->label); tcp_chr_free_connection(chr); if (s->listener) { @@ -521,6 +529,7 @@ static gboolean tcp_chr_hup(QIOChannel *channel, void *opaque) { Chardev *chr = CHARDEV(opaque); + trace_chr_socket_hangup(chr, chr->label); tcp_chr_disconnect(chr); return G_SOURCE_REMOVE; } @@ -672,15 +681,18 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, SocketChardev *s = user_data; Chardev *chr = CHARDEV(s); TCPChardevTelnetInit *init = s->telnet_init; + Error *err = NULL; ssize_t ret; assert(init); - ret = qio_channel_write(ioc, init->buf, init->buflen, NULL); + ret = qio_channel_write(ioc, init->buf, init->buflen, &err); if (ret < 0) { if (ret == QIO_CHANNEL_ERR_BLOCK) { ret = 0; } else { + trace_chr_socket_write_err(chr, chr->label, error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); goto end; } @@ -765,9 +777,9 @@ static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) Error *err = NULL; if (qio_task_propagate_error(task, &err)) { - error_reportf_err(err, - "websock handshake of character device %s failed: ", - chr->label); + trace_chr_socket_ws_handshake_err(chr, chr->label, + error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); } else { if (s->do_telnetopt) { @@ -805,9 +817,9 @@ static void tcp_chr_tls_handshake(QIOTask *task, Error *err = NULL; if (qio_task_propagate_error(task, &err)) { - error_reportf_err(err, - "TLS handshake of character device %s failed: ", - chr->label); + trace_chr_socket_tls_handshake_err(chr, chr->label, + error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); } else { if (s->is_websock) { @@ -826,19 +838,22 @@ static void tcp_chr_tls_init(Chardev *chr) SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelTLS *tioc; gchar *name; + Error *err = NULL; if (s->is_listen) { tioc = qio_channel_tls_new_server( s->ioc, s->tls_creds, s->tls_authz, - NULL); + &err); } else { tioc = qio_channel_tls_new_client( s->ioc, s->tls_creds, s->addr->u.inet.host, - NULL); + &err); } if (tioc == NULL) { + trace_chr_socket_tls_init_err(chr, chr->label, error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); return; } diff --git a/chardev/trace-events b/chardev/trace-events index 027107b0c1..7e97b8a988 100644 --- a/chardev/trace-events +++ b/chardev/trace-events @@ -17,3 +17,13 @@ spice_vmc_register_interface(void *scd) "spice vmc registered interface %p" spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p" spice_vmc_event(int event) "spice vmc event %d" +# char-socket.c +chr_socket_poll_err(void *chrdev, const char *label) "chardev socket poll error %p (%s)" +chr_socket_recv_err(void *chrdev, const char *label, const char *err) "chardev socket recv error %p (%s): %s" +chr_socket_recv_eof(void *chrdev, const char *label) "chardev socket recv end-of-file %p (%s)" +chr_socket_write_err(void *chrdev, const char *label, const char *err) "chardev socket write error %p (%s): %s" +chr_socket_disconnect(void *chrdev, const char *label) "chardev socket disconnect %p (%s)" +chr_socket_hangup(void *chrdev, const char *label) "chardev socket hangup %p (%s)" +chr_socket_ws_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket websock handshake error %p (%s): %s" +chr_socket_tls_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket TLS handshake error %p (%s): %s" +chr_socket_tls_init_err(void *chrdev, const char *label, const char *err) "chardev socket TLS init error %p (%s): %s" From 305233349b471840b00068579d0ab0af50395852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 15 Mar 2024 13:54:52 +0000 Subject: [PATCH 2475/2884] crypto: drop gnutls debug logging support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GNUTLS already supports dynamically enabling its logging at runtime by setting the env var 'GNUTLS_DEBUG_LEVEL=10', so there is no need to re-invent this logic in QEMU in a way that requires a re-compile. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- crypto/init.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/crypto/init.c b/crypto/init.c index fb7f1bff10..674d237fa9 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -34,14 +34,11 @@ #include "crypto/random.h" -/* #define DEBUG_GNUTLS */ -#ifdef DEBUG_GNUTLS -static void qcrypto_gnutls_log(int level, const char *str) -{ - fprintf(stderr, "%d: %s", level, str); -} -#endif +/* + * To debug GNUTLS see env vars listed in + * https://gnutls.org/manual/html_node/Debugging-and-auditing.html + */ int qcrypto_init(Error **errp) { #ifdef CONFIG_GNUTLS @@ -53,10 +50,6 @@ int qcrypto_init(Error **errp) gnutls_strerror(ret)); return -1; } -#ifdef DEBUG_GNUTLS - gnutls_global_set_log_level(10); - gnutls_global_set_log_function(qcrypto_gnutls_log); -#endif #endif #ifdef CONFIG_GCRYPT From 57941c9c86357a6a642f9ee3279d881df4043b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 15 Mar 2024 14:07:58 +0000 Subject: [PATCH 2476/2884] crypto: push error reporting into TLS session I/O APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current TLS session I/O APIs just return a synthetic errno value on error, which has been translated from a gnutls error value. This looses a large amount of valuable information that distinguishes different scenarios. Pushing population of the "Error *errp" object into the TLS session I/O APIs gives more detailed error information. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- crypto/tlssession.c | 60 ++++++++++++++++++------------------- include/crypto/tlssession.h | 23 +++++++++++--- io/channel-tls.c | 48 +++++++++++++---------------- 3 files changed, 68 insertions(+), 63 deletions(-) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 1e98f44e0d..926f19c115 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -441,23 +441,20 @@ qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, ssize_t qcrypto_tls_session_write(QCryptoTLSSession *session, const char *buf, - size_t len) + size_t len, + Error **errp) { ssize_t ret = gnutls_record_send(session->handle, buf, len); if (ret < 0) { - switch (ret) { - case GNUTLS_E_AGAIN: - errno = EAGAIN; - break; - case GNUTLS_E_INTERRUPTED: - errno = EINTR; - break; - default: - errno = EIO; - break; + if (ret == GNUTLS_E_AGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else { + error_setg(errp, + "Cannot write to TLS channel: %s", + gnutls_strerror(ret)); + return -1; } - ret = -1; } return ret; @@ -467,26 +464,24 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, ssize_t qcrypto_tls_session_read(QCryptoTLSSession *session, char *buf, - size_t len) + size_t len, + bool gracefulTermination, + Error **errp) { ssize_t ret = gnutls_record_recv(session->handle, buf, len); if (ret < 0) { - switch (ret) { - case GNUTLS_E_AGAIN: - errno = EAGAIN; - break; - case GNUTLS_E_INTERRUPTED: - errno = EINTR; - break; - case GNUTLS_E_PREMATURE_TERMINATION: - errno = ECONNABORTED; - break; - default: - errno = EIO; - break; + if (ret == GNUTLS_E_AGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) && + gracefulTermination){ + return 0; + } else { + error_setg(errp, + "Cannot read from TLS channel: %s", + gnutls_strerror(ret)); + return -1; } - ret = -1; } return ret; @@ -605,9 +600,10 @@ qcrypto_tls_session_set_callbacks( ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, const char *buf, - size_t len) + size_t len, + Error **errp) { - errno = -EIO; + error_setg(errp, "TLS requires GNUTLS support"); return -1; } @@ -615,9 +611,11 @@ qcrypto_tls_session_write(QCryptoTLSSession *sess, ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, - size_t len) + size_t len, + bool gracefulTermination, + Error **errp) { - errno = -EIO; + error_setg(errp, "TLS requires GNUTLS support"); return -1; } diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index 571049bd0e..291e602540 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -107,6 +107,7 @@ typedef struct QCryptoTLSSession QCryptoTLSSession; +#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 /** * qcrypto_tls_session_new: @@ -212,6 +213,7 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, * @sess: the TLS session object * @buf: the plain text to send * @len: the length of @buf + * @errp: pointer to hold returned error object * * Encrypt @len bytes of the data in @buf and send * it to the remote peer using the callback previously @@ -221,32 +223,45 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, * qcrypto_tls_session_get_handshake_status() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * - * Returns: the number of bytes sent, or -1 on error + * Returns: the number of bytes sent, + * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the write would block, + * or -1 on error. */ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, const char *buf, - size_t len); + size_t len, + Error **errp); /** * qcrypto_tls_session_read: * @sess: the TLS session object * @buf: to fill with plain text received * @len: the length of @buf + * @gracefulTermination: treat premature termination as graceful EOF + * @errp: pointer to hold returned error object * * Receive up to @len bytes of data from the remote peer * using the callback previously registered with * qcrypto_tls_session_set_callbacks(), decrypt it and * store it in @buf. * + * If @gracefulTermination is true, then a premature termination + * of the TLS session will be treated as indicating EOF, as + * opposed to an error. + * * It is an error to call this before * qcrypto_tls_session_get_handshake_status() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * - * Returns: the number of bytes received, or -1 on error + * Returns: the number of bytes received, + * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block, + * or -1 on error. */ ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, - size_t len); + size_t len, + bool gracefulTermination, + Error **errp); /** * qcrypto_tls_session_check_pending: diff --git a/io/channel-tls.c b/io/channel-tls.c index 67b9700006..9d8bb158d1 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -277,24 +277,19 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, ssize_t got = 0; for (i = 0 ; i < niov ; i++) { - ssize_t ret = qcrypto_tls_session_read(tioc->session, - iov[i].iov_base, - iov[i].iov_len); - if (ret < 0) { - if (errno == EAGAIN) { - if (got) { - return got; - } else { - return QIO_CHANNEL_ERR_BLOCK; - } - } else if (errno == ECONNABORTED && - (qatomic_load_acquire(&tioc->shutdown) & - QIO_CHANNEL_SHUTDOWN_READ)) { - return 0; + ssize_t ret = qcrypto_tls_session_read( + tioc->session, + iov[i].iov_base, + iov[i].iov_len, + qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, + errp); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + if (got) { + return got; + } else { + return QIO_CHANNEL_ERR_BLOCK; } - - error_setg_errno(errp, errno, - "Cannot read from TLS channel"); + } else if (ret < 0) { return -1; } got += ret; @@ -321,18 +316,15 @@ static ssize_t qio_channel_tls_writev(QIOChannel *ioc, for (i = 0 ; i < niov ; i++) { ssize_t ret = qcrypto_tls_session_write(tioc->session, iov[i].iov_base, - iov[i].iov_len); - if (ret <= 0) { - if (errno == EAGAIN) { - if (done) { - return done; - } else { - return QIO_CHANNEL_ERR_BLOCK; - } + iov[i].iov_len, + errp); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + if (done) { + return done; + } else { + return QIO_CHANNEL_ERR_BLOCK; } - - error_setg_errno(errp, errno, - "Cannot write to TLS channel"); + } else if (ret < 0) { return -1; } done += ret; From 97f7bf113eb50fcdaf0c73aa2ee01e5355abc073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 15 Mar 2024 14:29:11 +0000 Subject: [PATCH 2477/2884] crypto: propagate errors from TLS session I/O callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GNUTLS doesn't know how to perform I/O on anything other than plain FDs, so the TLS session provides it with some I/O callbacks. The GNUTLS API design requires these callbacks to return a unix errno value, which means we're currently loosing the useful QEMU "Error" object. This changes the I/O callbacks in QEMU to stash the "Error" object in the QCryptoTLSSession class, and fetch it when seeing an I/O error returned from GNUTLS, thus preserving useful error messages. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- crypto/tlssession.c | 76 +++++++++++++++++++++++++---- include/crypto/tlssession.h | 10 +++- io/channel-tls.c | 18 +++---- tests/unit/test-crypto-tlssession.c | 30 ++++++++++-- 4 files changed, 108 insertions(+), 26 deletions(-) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 926f19c115..77286e23f4 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -44,6 +44,13 @@ struct QCryptoTLSSession { QCryptoTLSSessionReadFunc readFunc; void *opaque; char *peername; + + /* + * Allow concurrent reads and writes, so track + * errors separately + */ + Error *rerr; + Error *werr; }; @@ -54,6 +61,9 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) return; } + error_free(session->rerr); + error_free(session->werr); + gnutls_deinit(session->handle); g_free(session->hostname); g_free(session->peername); @@ -67,13 +77,26 @@ static ssize_t qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) { QCryptoTLSSession *session = opaque; + ssize_t ret; if (!session->writeFunc) { errno = EIO; return -1; }; - return session->writeFunc(buf, len, session->opaque); + error_free(session->werr); + session->werr = NULL; + + ret = session->writeFunc(buf, len, session->opaque, &session->werr); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } else { + return ret; + } } @@ -81,13 +104,26 @@ static ssize_t qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) { QCryptoTLSSession *session = opaque; + ssize_t ret; if (!session->readFunc) { errno = EIO; return -1; }; - return session->readFunc(buf, len, session->opaque); + error_free(session->rerr); + session->rerr = NULL; + + ret = session->readFunc(buf, len, session->opaque, &session->rerr); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } else { + return ret; + } } #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" @@ -450,9 +486,14 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, if (ret == GNUTLS_E_AGAIN) { return QCRYPTO_TLS_SESSION_ERR_BLOCK; } else { - error_setg(errp, - "Cannot write to TLS channel: %s", - gnutls_strerror(ret)); + if (session->werr) { + error_propagate(errp, session->werr); + session->werr = NULL; + } else { + error_setg(errp, + "Cannot write to TLS channel: %s", + gnutls_strerror(ret)); + } return -1; } } @@ -477,9 +518,14 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, gracefulTermination){ return 0; } else { - error_setg(errp, - "Cannot read from TLS channel: %s", - gnutls_strerror(ret)); + if (session->rerr) { + error_propagate(errp, session->rerr); + session->rerr = NULL; + } else { + error_setg(errp, + "Cannot read from TLS channel: %s", + gnutls_strerror(ret)); + } return -1; } } @@ -507,11 +553,21 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, ret == GNUTLS_E_AGAIN) { ret = 1; } else { - error_setg(errp, "TLS handshake failed: %s", - gnutls_strerror(ret)); + if (session->rerr || session->werr) { + error_setg(errp, "TLS handshake failed: %s: %s", + gnutls_strerror(ret), + error_get_pretty(session->rerr ? + session->rerr : session->werr)); + } else { + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); + } ret = -1; } } + error_free(session->rerr); + error_free(session->werr); + session->rerr = session->werr = NULL; return ret; } diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index 291e602540..f694a5c3c5 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -178,12 +178,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, Error **errp); +/* + * These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O + * would block, but on other errors, must fill 'errp' + */ typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, size_t len, - void *opaque); + void *opaque, + Error **errp); typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, size_t len, - void *opaque); + void *opaque, + Error **errp); /** * qcrypto_tls_session_set_callbacks: diff --git a/io/channel-tls.c b/io/channel-tls.c index 9d8bb158d1..aab630e5ae 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -28,17 +28,16 @@ static ssize_t qio_channel_tls_write_handler(const char *buf, size_t len, - void *opaque) + void *opaque, + Error **errp) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); ssize_t ret; - ret = qio_channel_write(tioc->master, buf, len, NULL); + ret = qio_channel_write(tioc->master, buf, len, errp); if (ret == QIO_CHANNEL_ERR_BLOCK) { - errno = EAGAIN; - return -1; + return QCRYPTO_TLS_SESSION_ERR_BLOCK; } else if (ret < 0) { - errno = EIO; return -1; } return ret; @@ -46,17 +45,16 @@ static ssize_t qio_channel_tls_write_handler(const char *buf, static ssize_t qio_channel_tls_read_handler(char *buf, size_t len, - void *opaque) + void *opaque, + Error **errp) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); ssize_t ret; - ret = qio_channel_read(tioc->master, buf, len, NULL); + ret = qio_channel_read(tioc->master, buf, len, errp); if (ret == QIO_CHANNEL_ERR_BLOCK) { - errno = EAGAIN; - return -1; + return QCRYPTO_TLS_SESSION_ERR_BLOCK; } else if (ret < 0) { - errno = EIO; return -1; } return ret; diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index b12e7b6879..3395f73560 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -35,18 +35,40 @@ #define PSKFILE WORKDIR "keys.psk" #define KEYFILE WORKDIR "key-ctx.pem" -static ssize_t testWrite(const char *buf, size_t len, void *opaque) +static ssize_t +testWrite(const char *buf, size_t len, void *opaque, Error **errp) { int *fd = opaque; + int ret; - return write(*fd, buf, len); + ret = write(*fd, buf, len); + if (ret < 0) { + if (errno == EAGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else { + error_setg_errno(errp, errno, "unable to write"); + return -1; + } + } + return ret; } -static ssize_t testRead(char *buf, size_t len, void *opaque) +static ssize_t +testRead(char *buf, size_t len, void *opaque, Error **errp) { int *fd = opaque; + int ret; - return read(*fd, buf, len); + ret = read(*fd, buf, len); + if (ret < 0) { + if (errno == EAGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else { + error_setg_errno(errp, errno, "unable to read"); + return -1; + } + } + return ret; } static QCryptoTLSCreds *test_tls_creds_psk_create( From ec018b76798e1196882ebcbf3df15e6d86ac9d7c Mon Sep 17 00:00:00 2001 From: Warner Losh <imp@bsdimp.com> Date: Wed, 24 Jul 2024 15:57:44 -0600 Subject: [PATCH 2478/2884] bsd-user: Add target.h for aarch64. For aarch64, it's a 64-bit API, so there's no pairing of registers for 64-bit values. Signed-off-by: Warner Losh <imp@bsdimp.com> --- bsd-user/aarch64/target.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bsd-user/aarch64/target.h diff --git a/bsd-user/aarch64/target.h b/bsd-user/aarch64/target.h new file mode 100644 index 0000000000..702aeb7fc5 --- /dev/null +++ b/bsd-user/aarch64/target.h @@ -0,0 +1,20 @@ +/* + * Aarch64 general target stuff that's common to all aarch details + * + * Copyright (c) 2022 M. Warner Losh <imp@bsdimp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_H +#define TARGET_H + +/* + * aaarch64 ABI does not 'lump' the registers for 64-bit args. + */ +static inline bool regpairs_aligned(void *cpu_env) +{ + return false; +} + +#endif /* TARGET_H */ From 851495571d14fe2226c52b9d423f88a4f5460836 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 16:09:27 +0100 Subject: [PATCH 2479/2884] util/async.c: Forbid negative min/max in aio_context_set_thread_pool_params() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aio_context_set_thread_pool_params() takes two int64_t arguments to set the minimum and maximum number of threads in the pool. We do some bounds checking on these, but we don't catch the case where the inputs are negative. This means that later in the function when we assign these inputs to the AioContext::thread_pool_min and ::thread_pool_max fields, which are of type int, the values might overflow the smaller type. A negative number of threads is meaningless, so make aio_context_set_thread_pool_params() return an error if either min or max are negative. Resolves: Coverity CID 1547605 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723150927.1396456-1-peter.maydell@linaro.org Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> --- util/async.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/async.c b/util/async.c index 0467890052..3e3e4fc712 100644 --- a/util/async.c +++ b/util/async.c @@ -746,7 +746,7 @@ void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, int64_t max, Error **errp) { - if (min > max || !max || min > INT_MAX || max > INT_MAX) { + if (min > max || max <= 0 || min < 0 || min > INT_MAX || max > INT_MAX) { error_setg(errp, "bad thread-pool-min/thread-pool-max values"); return; } From e9964c32ba3476db6190556293b754aa50a489d0 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 26 Jul 2024 00:50:31 +1000 Subject: [PATCH 2480/2884] tests/tcg: Skip failing ppc64 multi-threaded tests In Gitlab CI, some ppc64 multi-threaded tcg tests crash when run in the clang-user job with an assertion failure in glibc that seems to indicate corruption: signals: allocatestack.c:223: allocate_stack: Assertion `powerof2 (pagesize_m1 + 1)' failed. Disable these tests for now. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- tests/tcg/ppc64/Makefile.target | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index 8c3e4e4038..509a20be2b 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -11,6 +11,18 @@ config-cc.mak: Makefile -include config-cc.mak +# multi-threaded tests are known to fail (e.g., clang-user CI job) +# See: https://gitlab.com/qemu-project/qemu/-/issues/2456 +run-signals: signals + $(call skip-test, $<, "BROKEN (flaky with clang) ") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN (flaky with clang) ") + +run-threadcount: threadcount + $(call skip-test, $<, "BROKEN (flaky with clang) ") +run-plugin-threadcount-with-%: + $(call skip-test, $<, "BROKEN (flaky with clang) ") + ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) PPC64_TESTS=bcdsub non_signalling_xscv endif From 1a7a31aec4758d6fd89b60d88669f74f30cdb6bb Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Mon, 6 May 2024 21:56:05 +1000 Subject: [PATCH 2481/2884] spapr: Migrate ail-mode-3 spapr cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This cap did not add the migration code when it was introduced. This results in migration failure when changing the default using the command line. Cc: qemu-stable@nongnu.org Fixes: ccc5a4c5e10 ("spapr: Add SPAPR_CAP_AIL_MODE_3 for AIL mode 3 support for H_SET_MODE hcall") Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/spapr.c | 1 + hw/ppc/spapr_caps.c | 1 + include/hw/ppc/spapr.h | 1 + 3 files changed, 3 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 98fa3aa6a8..370d7c35d3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2195,6 +2195,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_fwnmi, &vmstate_spapr_fwnmi, &vmstate_spapr_cap_rpt_invalidate, + &vmstate_spapr_cap_ail_mode_3, &vmstate_spapr_cap_nested_papr, NULL } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 0a15415a1d..2f74923560 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -974,6 +974,7 @@ SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); SPAPR_CAP_MIG_STATE(rpt_invalidate, SPAPR_CAP_RPT_INVALIDATE); +SPAPR_CAP_MIG_STATE(ail_mode_3, SPAPR_CAP_AIL_MODE_3); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 4aaf23d28f..f6de3e9972 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -1004,6 +1004,7 @@ extern const VMStateDescription vmstate_spapr_cap_large_decr; extern const VMStateDescription vmstate_spapr_cap_ccf_assist; extern const VMStateDescription vmstate_spapr_cap_fwnmi; extern const VMStateDescription vmstate_spapr_cap_rpt_invalidate; +extern const VMStateDescription vmstate_spapr_cap_ail_mode_3; extern const VMStateDescription vmstate_spapr_wdt; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) From 8af863f2bd976b937f7e3d38b2ab1813b2fa1d9d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 8 Jul 2024 15:55:12 +0900 Subject: [PATCH 2482/2884] spapr: Free stdout path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes LeakSanitizer warnings. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/spapr_vof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index 09f29be0b9..c02eaacfed 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -28,7 +28,7 @@ target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr, void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt) { - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + g_autofree char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); vof_build_dt(fdt, spapr->vof); From 785c8637f9d2362a8addf4ded853d975955a9d6b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 8 Jul 2024 15:55:13 +0900 Subject: [PATCH 2483/2884] ppc/vof: Fix unaligned FDT property access FDT properties are aligned by 4 bytes, not 8 bytes. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/vof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index e3b430a81f..b5b6514d79 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -646,7 +646,7 @@ static void vof_dt_memory_available(void *fdt, GArray *claimed, uint64_t base) mem0_reg = fdt_getprop(fdt, offset, "reg", &proplen); g_assert(mem0_reg && proplen == sizeof(uint32_t) * (ac + sc)); if (sc == 2) { - mem0_end = be64_to_cpu(*(uint64_t *)(mem0_reg + sizeof(uint32_t) * ac)); + mem0_end = ldq_be_p(mem0_reg + sizeof(uint32_t) * ac); } else { mem0_end = be32_to_cpu(*(uint32_t *)(mem0_reg + sizeof(uint32_t) * ac)); } From c6a3d7bc9e3acf2431ac23ae6dbeb28aa92f873c Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora <harshpb@linux.ibm.com> Date: Tue, 18 Jun 2024 13:53:52 +0530 Subject: [PATCH 2484/2884] accel/kvm: Introduce kvm_create_and_park_vcpu() helper There are distinct helpers for creating and parking a KVM vCPU. However, there can be cases where a platform needs to create and immediately park the vCPU during early stages of vcpu init which can later be reused when vcpu thread gets initialized. This would help detect failures with kvm_create_vcpu at an early stage. Suggested-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- accel/kvm/kvm-all.c | 12 ++++++++++++ include/sysemu/kvm.h | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 67b773692f..e1d1386306 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -398,6 +398,18 @@ int kvm_create_vcpu(CPUState *cpu) return 0; } +int kvm_create_and_park_vcpu(CPUState *cpu) +{ + int ret = 0; + + ret = kvm_create_vcpu(cpu); + if (!ret) { + kvm_park_vcpu(cpu); + } + + return ret; +} + static int do_kvm_destroy_vcpu(CPUState *cpu) { KVMState *s = kvm_state; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index c4a914b3d8..9cf14ca3d5 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -338,6 +338,14 @@ void kvm_park_vcpu(CPUState *cpu); */ int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); +/** + * kvm_create_and_park_vcpu - Create and park a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be created and parked. + * + * @returns: 0 when success, errno (<0) when failed. + */ +int kvm_create_and_park_vcpu(CPUState *cpu); + /* Arch specific hooks */ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; From 18530e7c57dec3d82d02ed17038661e2005162c1 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora <harshpb@linux.ibm.com> Date: Tue, 18 Jun 2024 13:53:53 +0530 Subject: [PATCH 2485/2884] cpu-common.c: export cpu_get_free_index to be reused later This helper provides an easy way to identify the next available free cpu index which can be used for vcpu creation. Until now, this is being called at a very later stage and there is a need to be able to call it earlier (for now, with ppc64) hence the need to export. Suggested-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- cpu-common.c | 7 ++++--- include/exec/cpu-common.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cpu-common.c b/cpu-common.c index 7ae136f98c..6b262233a3 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -57,14 +57,12 @@ void cpu_list_unlock(void) qemu_mutex_unlock(&qemu_cpu_list_lock); } -static bool cpu_index_auto_assigned; -static int cpu_get_free_index(void) +int cpu_get_free_index(void) { CPUState *some_cpu; int max_cpu_index = 0; - cpu_index_auto_assigned = true; CPU_FOREACH(some_cpu) { if (some_cpu->cpu_index >= max_cpu_index) { max_cpu_index = some_cpu->cpu_index + 1; @@ -83,8 +81,11 @@ unsigned int cpu_list_generation_id_get(void) void cpu_list_add(CPUState *cpu) { + static bool cpu_index_auto_assigned; + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) { + cpu_index_auto_assigned = true; cpu->cpu_index = cpu_get_free_index(); assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); } else { diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 240ee04369..2e1b499cb7 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -35,6 +35,8 @@ void cpu_list_lock(void); void cpu_list_unlock(void); unsigned int cpu_list_generation_id_get(void); +int cpu_get_free_index(void); + void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); From cfb52d07f53aa916003d43f69c945c2b42bc6374 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora <harshpb@linux.ibm.com> Date: Tue, 18 Jun 2024 13:53:54 +0530 Subject: [PATCH 2486/2884] target/ppc: handle vcpu hotplug failure gracefully On ppc64, the PowerVM hypervisor runs with limited memory and a VCPU creation during hotplug may fail during kvm_ioctl for KVM_CREATE_VCPU, leading to termination of guest since errp is set to &error_fatal while calling kvm_init_vcpu. This unexpected behaviour can be avoided by pre-creating and parking vcpu on success or return error otherwise. This enables graceful error delivery for any vcpu hotplug failures while the guest can keep running. Also introducing KVM AccelCPUClass to init cpu_target_realize for kvm. Tested OK by repeatedly doing a hotplug/unplug of vcpus as below: #virsh setvcpus hotplug 40 #virsh setvcpus hotplug 70 error: internal error: unable to execute QEMU command 'device_add': kvmppc_cpu_realize: vcpu hotplug failed with -12 Signed-off by: Harsh Prateek Bora <harshpb@linux.ibm.com> Reported-by: Anushree Mathur <anushree.mathur@linux.vnet.ibm.com> Suggested-by: Shivaprasad G Bhat <sbhat@linux.ibm.com> Suggested-by: Vaibhav Jain <vaibhav@linux.ibm.com> Tested-by: Anushree Mathur <anushree.mathur@linux.vnet.ibm.com> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/kvm.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 2c3932200b..907dba60d1 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -48,6 +48,8 @@ #include "qemu/mmap-alloc.h" #include "elf.h" #include "sysemu/kvm_int.h" +#include "sysemu/kvm.h" +#include "hw/core/accel-cpu.h" #include CONFIG_DEVICES @@ -2346,6 +2348,30 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) } } +static bool kvmppc_cpu_realize(CPUState *cs, Error **errp) +{ + int ret; + const char *vcpu_str = (cs->parent_obj.hotplugged == true) ? + "hotplug" : "create"; + cs->cpu_index = cpu_get_free_index(); + + POWERPC_CPU(cs)->vcpu_id = cs->cpu_index; + + /* create and park to fail gracefully in case vcpu hotplug fails */ + ret = kvm_create_and_park_vcpu(cs); + if (ret) { + /* + * This causes QEMU to terminate if initial CPU creation + * fails, and only CPU hotplug failure if the error happens + * there. + */ + error_setg(errp, "%s: vcpu %s failed with %d", + __func__, vcpu_str, ret); + return false; + } + return true; +} + static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2966,3 +2992,23 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) void kvm_arch_accel_class_init(ObjectClass *oc) { } + +static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_target_realize = kvmppc_cpu_realize; +} + +static const TypeInfo kvm_cpu_accel_type_info = { + .name = ACCEL_CPU_NAME("kvm"), + + .parent = TYPE_ACCEL_CPU, + .class_init = kvm_cpu_accel_class_init, + .abstract = true, +}; +static void kvm_cpu_accel_register_types(void) +{ + type_register_static(&kvm_cpu_accel_type_info); +} +type_init(kvm_cpu_accel_register_types); From 2587a57dbb50257f296b5f28b889e54d0b64c394 Mon Sep 17 00:00:00 2001 From: Omar Sandoval <osandov@osandov.com> Date: Fri, 19 Jul 2024 11:39:05 -0700 Subject: [PATCH 2487/2884] target/ppc/arch_dump: set prstatus pid to cpuid Every other architecture does this, and debuggers need it to be able to identify which prstatus note corresponds to which CPU. Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Omar Sandoval <osandov@osandov.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/arch_dump.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index a8315659d9..f45474133a 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -47,9 +47,14 @@ struct PPCUserRegStruct { } QEMU_PACKED; struct PPCElfPrstatus { - char pad1[112]; + char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pid; + char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ struct PPCUserRegStruct pr_reg; - char pad2[40]; + char pad3[40]; /* 40 == sizeof(struct elf_prstatus) - + offsetof(struct elf_prstatus, pr_reg) - + sizeof(struct user_pt_regs) */ } QEMU_PACKED; @@ -96,7 +101,7 @@ typedef struct NoteFuncArg { DumpState *state; } NoteFuncArg; -static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; reg_t cr; @@ -109,6 +114,7 @@ static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) prstatus = ¬e->contents.prstatus; memset(prstatus, 0, sizeof(*prstatus)); + prstatus->pid = cpu_to_dump32(s, id); reg = &prstatus->pr_reg; for (i = 0; i < 32; i++) { @@ -127,7 +133,7 @@ static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) reg->ccr = cpu_to_dump_reg(s, cr); } -static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfFpregset *fpregset; @@ -146,7 +152,7 @@ static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) fpregset->fpscr = cpu_to_dump_reg(s, cpu->env.fpscr); } -static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfVmxregset *vmxregset; @@ -178,7 +184,7 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) vmxregset->vscr.u32[3] = cpu_to_dump32(s, ppc_get_vscr(&cpu->env)); } -static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfVsxregset *vsxregset; @@ -195,7 +201,7 @@ static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) } } -static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { struct PPCElfSperegset *speregset; Note *note = &arg->note; @@ -211,7 +217,7 @@ static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) static const struct NoteFuncDescStruct { int contents_size; - void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu); + void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu, int id); } note_func[] = { {sizeof_field(Note, contents.prstatus), ppc_write_elf_prstatus}, {sizeof_field(Note, contents.fpregset), ppc_write_elf_fpregset}, @@ -282,7 +288,7 @@ static int ppc_write_all_elf_notes(const char *note_name, arg.note.hdr.n_descsz = cpu_to_dump32(s, nf->contents_size); strncpy(arg.note.name, note_name, sizeof(arg.note.name)); - (*nf->note_contents_func)(&arg, cpu); + (*nf->note_contents_func)(&arg, cpu, id); note_size = sizeof(arg.note) - sizeof(arg.note.contents) + nf->contents_size; From b9c0a2e01c0f38bdc4ba8f69cf298eeebfb3738b Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat <sbhat@linux.ibm.com> Date: Wed, 5 Jun 2024 15:57:52 +0000 Subject: [PATCH 2488/2884] linux-header: PPC: KVM: Update one-reg ids for DEXCR, HASHKEYR and HASHPKEYR This is a placeholder change for these SPRs until the full linux header update. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- linux-headers/asm-powerpc/kvm.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 1691297a76..eaeda00178 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -645,6 +645,9 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_SIER3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc3) #define KVM_REG_PPC_DAWR1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc4) #define KVM_REG_PPC_DAWRX1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc5) +#define KVM_REG_PPC_DEXCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc6) +#define KVM_REG_PPC_HASHKEYR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc7) +#define KVM_REG_PPC_HASHPKEYR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc8) /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs From ca85beb4b783064781a3295feaa7b1a8645f2df9 Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat <sbhat@linux.ibm.com> Date: Wed, 5 Jun 2024 15:58:02 +0000 Subject: [PATCH 2489/2884] target/ppc/cpu_init: Synchronize DEXCR with KVM for migration The patch enables DEXCR migration by hooking with the "KVM one reg" ID KVM_REG_PPC_DEXCR. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/cpu_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index cdada7987d..7c3ee80661 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5886,9 +5886,9 @@ static void register_power10_hash_sprs(CPUPPCState *env) static void register_power10_dexcr_sprs(CPUPPCState *env) { - spr_register(env, SPR_DEXCR, "DEXCR", + spr_register_kvm(env, SPR_DEXCR, "DEXCR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, KVM_REG_PPC_DEXCR, 0); spr_register(env, SPR_UDEXCR, "UDEXCR", From 843b243f8620a92f5ff652550b61fc724e5d520c Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat <sbhat@linux.ibm.com> Date: Wed, 5 Jun 2024 15:58:12 +0000 Subject: [PATCH 2490/2884] target/ppc/cpu_init: Synchronize HASHKEYR with KVM for migration The patch enables HASHKEYR migration by hooking with the "KVM one reg" ID KVM_REG_PPC_HASHKEYR. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/cpu_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 7c3ee80661..d311b190c1 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5873,10 +5873,10 @@ static void register_power10_hash_sprs(CPUPPCState *env) ((uint64_t)g_rand_int(rand) << 32) | (uint64_t)g_rand_int(rand); g_rand_free(rand); #endif - spr_register(env, SPR_HASHKEYR, "HASHKEYR", + spr_register_kvm(env, SPR_HASHKEYR, "HASHKEYR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - hashkeyr_initial_value); + KVM_REG_PPC_HASHKEYR, hashkeyr_initial_value); spr_register_hv(env, SPR_HASHPKEYR, "HASHPKEYR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, From c0840b46d4c8483a93370434f9ea10b8a7b50bde Mon Sep 17 00:00:00 2001 From: Shivaprasad G Bhat <sbhat@linux.ibm.com> Date: Wed, 5 Jun 2024 15:58:22 +0000 Subject: [PATCH 2491/2884] target/ppc/cpu_init: Synchronize HASHPKEYR with KVM for migration The patch enables HASHPKEYR migration by hooking with the "KVM one reg" ID KVM_REG_PPC_HASHPKEYR. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/cpu_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index d311b190c1..2e652f498e 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5877,11 +5877,11 @@ static void register_power10_hash_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_HASHKEYR, hashkeyr_initial_value); - spr_register_hv(env, SPR_HASHPKEYR, "HASHPKEYR", + spr_register_kvm_hv(env, SPR_HASHPKEYR, "HASHPKEYR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - hashpkeyr_initial_value); + KVM_REG_PPC_HASHPKEYR, hashpkeyr_initial_value); } static void register_power10_dexcr_sprs(CPUPPCState *env) From 977e789c4a8ed813d4ab03f17ea20a575bf20cd1 Mon Sep 17 00:00:00 2001 From: Aditya Gupta <adityag@linux.ibm.com> Date: Thu, 2 May 2024 11:57:01 +0530 Subject: [PATCH 2492/2884] ppc/pnv: Update Power10's cfam id to use Power10 DD2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power10 DD1.0 was dropped in: commit 8f054d9ee825 ("ppc: Drop support for POWER9 and POWER10 DD1 chips") Use the newer Power10 DD2 chips cfam id. Signed-off-by: Aditya Gupta <adityag@linux.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 2 +- tests/qtest/pnv-xscom.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 6b41d1d2dd..13cebd6ab9 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2087,7 +2087,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) PnvChipClass *k = PNV_CHIP_CLASS(klass); static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; - k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */ + k->chip_cfam_id = 0x220da04980000000ull; /* P10 DD2.0 (with NX) */ k->cores_mask = POWER10_CORE_MASK; k->chip_pir = pnv_chip_pir_p10; k->intc_create = pnv_chip_power10_intc_create; diff --git a/tests/qtest/pnv-xscom.h b/tests/qtest/pnv-xscom.h index 6f62941744..5aa1701ea7 100644 --- a/tests/qtest/pnv-xscom.h +++ b/tests/qtest/pnv-xscom.h @@ -56,7 +56,7 @@ static const PnvChip pnv_chips[] = { .chip_type = PNV_CHIP_POWER10, .cpu_model = "POWER10", .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x120da04900008000ull, + .cfam_id = 0x220da04980000000ull, .first_core = 0x0, .num_i2c = 4, }, From c6e07f03f7270799a26eb79e17ac40078ad94e5c Mon Sep 17 00:00:00 2001 From: Glenn Miles <milesg@linux.vnet.ibm.com> Date: Fri, 24 May 2024 13:24:14 -0500 Subject: [PATCH 2493/2884] ppc/pnv: Fix loss of LPC SERIRQ interrupts The LPC HC irq status register bits are set when an LPC IRQSER input is asserted. These irq status bits drive the PSI irq to the CPU interrupt controller. The LPC HC irq status bits are cleared by software writing to the register with 1's for the bits to clear. Existing register write was clearing the irq status bits even when the input was asserted, this results in interrupts being lost. This fix changes the behavior to keep track of the device IRQ status in internal state that is separate from the irq status register, and only allowing the irq status bits to be cleared if the associated input is not asserted. Signed-off-by: Glenn Miles <milesg@linux.ibm.com> [np: rebased before P9 PSI SERIRQ patch, adjust changelog/comments] Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_lpc.c | 22 +++++++++++++++++++--- include/hw/ppc/pnv_lpc.h | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d692858bee..7d26b29487 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -505,7 +505,14 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQSTAT: - lpc->lpc_hc_irqstat &= ~val; + /* + * This register is write-to-clear for the IRQSER (LPC device IRQ) + * status. However if the device has not de-asserted its interrupt + * that will just raise this IRQ status bit again. Model this by + * keeping track of the inputs and only clearing if the inputs are + * deasserted. + */ + lpc->lpc_hc_irqstat &= ~(val & ~lpc->lpc_hc_irq_inputs); pnv_lpc_eval_irqs(lpc); break; case LPC_HC_ERROR_ADDRESS: @@ -803,11 +810,20 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) { PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t irq_bit = LPC_HC_IRQ_SERIRQ0 >> n; - /* The Naples HW latches the 1 levels, clearing is done by SW */ if (level) { - lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; + lpc->lpc_hc_irq_inputs |= irq_bit; + + /* + * The LPC HC in Naples and later latches LPC IRQ into a bit field in + * the IRQSTAT register, and that drives the PSI IRQ to the IC. + * Software clears this bit manually (see LPC_HC_IRQSTAT handler). + */ + lpc->lpc_hc_irqstat |= irq_bit; pnv_lpc_eval_irqs(lpc); + } else { + lpc->lpc_hc_irq_inputs &= ~irq_bit; } } diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 5d22c45570..97c6872c3f 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -73,6 +73,9 @@ struct PnvLpcController { uint32_t opb_irq_pol; uint32_t opb_irq_input; + /* LPC device IRQ state */ + uint32_t lpc_hc_irq_inputs; + /* LPC HC registers */ uint32_t lpc_hc_fw_seg_idsel; uint32_t lpc_hc_fw_rd_acc_size; From 24c3caff995584342101a181af2eacd67129e5ec Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 10 May 2024 17:10:40 +1000 Subject: [PATCH 2494/2884] ppc/pnv: Implement POWER9 LPC PSI serirq outputs and auto-clear function The POWER8 LPC ISA device irqs all get combined and reported to the line connected the PSI LPCHC irq. POWER9 changed this so only internal LPC host controller irqs use that line, and the device irqs get routed to 4 new lines connected to PSI SERIRQ0-3. POWER9 also introduced a new feature that automatically clears the irq status in the LPC host controller when EOI'ed, so software does not have to. The powernv OPAL (skiboot) firmware managed to work because the LPCHC irq handler scanned all LPC irqs and handled those including clearing status even on POWER9 systems. So LPC irqs worked despite OPAL thinking it was running in POWER9 mode. After this change, UART interrupts show up on serirq1 which is where OPAL routes them to: cat /proc/interrupts ... 20: 0 XIVE-IRQ 1048563 Level opal-psi#0:lpchc ... 25: 34 XIVE-IRQ 1048568 Level opal-psi#0:lpc_serirq_mux1 Whereas they previously turn up on lpchc. Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 36 +++++++++-- hw/ppc/pnv_lpc.c | 132 +++++++++++++++++++++++++++++++-------- include/hw/ppc/pnv_lpc.h | 14 ++++- 3 files changed, 150 insertions(+), 32 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 13cebd6ab9..f56dcf6597 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -727,7 +727,8 @@ static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(chip); qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_EXTERNAL); - qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); + qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq); + return pnv_lpc_isa_create(&chip8->lpc, true, errp); } @@ -736,25 +737,48 @@ static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(chip); qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_LPC_I2C); - qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); + qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq); + return pnv_lpc_isa_create(&chip8->lpc, false, errp); } static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) { Pnv9Chip *chip9 = PNV9_CHIP(chip); - qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 3, irq); - qdev_connect_gpio_out(DEVICE(&chip9->lpc), 0, irq); return pnv_lpc_isa_create(&chip9->lpc, false, errp); } static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) { Pnv10Chip *chip10 = PNV10_CHIP(chip); - qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 3, irq); - qdev_connect_gpio_out(DEVICE(&chip10->lpc), 0, irq); return pnv_lpc_isa_create(&chip10->lpc, false, errp); } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 7d26b29487..0f14e180af 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -64,6 +64,7 @@ enum { #define LPC_HC_IRQSER_START_4CLK 0x00000000 #define LPC_HC_IRQSER_START_6CLK 0x01000000 #define LPC_HC_IRQSER_START_8CLK 0x02000000 +#define LPC_HC_IRQSER_AUTO_CLEAR 0x00800000 #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ #define LPC_HC_IRQSTAT 0x38 #define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ @@ -420,32 +421,90 @@ static const MemoryRegionOps pnv_lpc_mmio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +/* Program the POWER9 LPC irq to PSI serirq routing table */ +static void pnv_lpc_eval_serirq_routes(PnvLpcController *lpc) +{ + int irq; + + if (!lpc->psi_has_serirq) { + if ((lpc->opb_irq_route0 & PPC_BITMASK(8, 13)) || + (lpc->opb_irq_route1 & PPC_BITMASK(4, 31))) { + qemu_log_mask(LOG_GUEST_ERROR, + "OPB: setting serirq routing on POWER8 system, ignoring.\n"); + } + return; + } + + for (irq = 0; irq <= 13; irq++) { + int serirq = (lpc->opb_irq_route1 >> (31 - 5 - (irq * 2))) & 0x3; + lpc->irq_to_serirq_route[irq] = serirq; + } + + for (irq = 14; irq < ISA_NUM_IRQS; irq++) { + int serirq = (lpc->opb_irq_route0 >> (31 - 9 - (irq * 2))) & 0x3; + lpc->irq_to_serirq_route[irq] = serirq; + } +} + static void pnv_lpc_eval_irqs(PnvLpcController *lpc) { - bool lpc_to_opb_irq = false; + uint32_t active_irqs = 0; + + if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) { + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: " + "0x%08"PRIx32"\n", lpc->lpc_hc_irqstat); + } - /* Update LPC controller to OPB line */ if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { - uint32_t irqs; - - irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; - lpc_to_opb_irq = (irqs != 0); + active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; } - /* We don't honor the polarity register, it's pointless and unused - * anyway - */ - if (lpc_to_opb_irq) { - lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; - } else { - lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; - } - - /* Update OPB internal latch */ - lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; - /* Reflect the interrupt */ - qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0); + if (!lpc->psi_has_serirq) { + /* + * POWER8 ORs all irqs together (also with LPCHC internal interrupt + * sources) and outputs a single line that raises the PSI LPCHC irq + * which then latches an OPB IRQ status register that sends the irq + * to PSI. + * + * We don't honor the polarity register, it's pointless and unused + * anyway + */ + if (active_irqs) { + lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; + } else { + lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; + } + + /* Update OPB internal latch */ + lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; + + qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); + } else { + /* + * POWER9 and POWER10 have routing fields in OPB master registers that + * send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs. + * These don't appear to get latched into an OPB register like the + * LPCHC irqs. + * + * POWER9 LPC controller internal irqs still go via the OPB + * and LPCHC PSI irqs like P8, but we have no such internal sources + * modelled yet. + */ + bool serirq_out[4] = { false, false, false, false }; + int irq; + + for (irq = 0; irq < ISA_NUM_IRQS; irq++) { + if (active_irqs & (LPC_HC_IRQ_SERIRQ0 >> irq)) { + serirq_out[lpc->irq_to_serirq_route[irq]] = true; + } + } + + qemu_set_irq(lpc->psi_irq_serirq[0], serirq_out[0]); + qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]); + qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]); + qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]); + } } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) @@ -543,10 +602,10 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) uint64_t val = 0xfffffffffffffffful; switch (addr) { - case OPB_MASTER_LS_ROUTE0: /* TODO */ + case OPB_MASTER_LS_ROUTE0: val = lpc->opb_irq_route0; break; - case OPB_MASTER_LS_ROUTE1: /* TODO */ + case OPB_MASTER_LS_ROUTE1: val = lpc->opb_irq_route1; break; case OPB_MASTER_LS_IRQ_STAT: @@ -575,11 +634,15 @@ static void opb_master_write(void *opaque, hwaddr addr, PnvLpcController *lpc = opaque; switch (addr) { - case OPB_MASTER_LS_ROUTE0: /* TODO */ + case OPB_MASTER_LS_ROUTE0: lpc->opb_irq_route0 = val; + pnv_lpc_eval_serirq_routes(lpc); + pnv_lpc_eval_irqs(lpc); break; - case OPB_MASTER_LS_ROUTE1: /* TODO */ + case OPB_MASTER_LS_ROUTE1: lpc->opb_irq_route1 = val; + pnv_lpc_eval_serirq_routes(lpc); + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_STAT: lpc->opb_irq_stat &= ~val; @@ -664,6 +727,8 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); Error *local_err = NULL; + object_property_set_bool(OBJECT(lpc), "psi-serirq", true, &error_abort); + plc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -673,6 +738,9 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) /* P9 uses a MMIO region */ memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops, lpc, "lpcm", PNV9_LPCM_SIZE); + + /* P9 LPC routes ISA irqs to 4 PSI SERIRQ lines */ + qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4); } static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) @@ -751,13 +819,19 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); - qdev_init_gpio_out(dev, &lpc->psi_irq, 1); + qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1); } +static Property pnv_lpc_properties[] = { + DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, pnv_lpc_properties); dc->realize = pnv_lpc_realize; dc->desc = "PowerNV LPC Controller"; dc->user_creatable = false; @@ -803,7 +877,7 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) } if (pnv->cpld_irqstate != old_state) { - qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0); + qemu_set_irq(lpc->psi_irq_lpchc, pnv->cpld_irqstate != 0); } } @@ -824,6 +898,13 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) pnv_lpc_eval_irqs(lpc); } else { lpc->lpc_hc_irq_inputs &= ~irq_bit; + + /* POWER9 adds an auto-clear mode that clears IRQSTAT bits on EOI */ + if (lpc->psi_has_serirq && + (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_AUTO_CLEAR)) { + lpc->lpc_hc_irqstat &= ~irq_bit; + pnv_lpc_eval_irqs(lpc); + } } } @@ -854,6 +935,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) handler = pnv_lpc_isa_irq_handler; } + /* POWER has a 17th irq, QEMU only implements the 16 regular device irqs */ irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); isa_bus_register_input_irqs(isa_bus, irqs); diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 97c6872c3f..e0fd5e4130 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -23,6 +23,7 @@ #include "exec/memory.h" #include "hw/ppc/pnv.h" #include "hw/qdev-core.h" +#include "hw/isa/isa.h" /* For ISA_NUM_IRQS */ #define TYPE_PNV_LPC "pnv-lpc" typedef struct PnvLpcClass PnvLpcClass; @@ -87,8 +88,19 @@ struct PnvLpcController { /* XSCOM registers */ MemoryRegion xscom_regs; + /* + * In P8, ISA irqs are combined with internal sources to drive the + * LPCHC interrupt output. P9 ISA irqs raise one of 4 lines that + * drive PSI SERIRQ irqs, routing according to OPB routing registers. + */ + bool psi_has_serirq; + /* PSI to generate interrupts */ - qemu_irq psi_irq; + qemu_irq psi_irq_lpchc; + + /* P9 serirq lines and irq routing table */ + qemu_irq psi_irq_serirq[4]; + int irq_to_serirq_route[ISA_NUM_IRQS]; }; struct PnvLpcClass { From 53f18b3ef2c3e898e7dae21a1f33f9e2f3eed764 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 23 Feb 2024 22:34:56 +1000 Subject: [PATCH 2495/2884] ppc/pnv: Begin a more complete ADU LPC model for POWER9/10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements a framework for an ADU unit model. The ADU unit actually implements XSCOM, which is the bridge between MMIO and PIB. However it also includes control and status registers and other functions that are exposed as PIB (xscom) registers. To keep things simple, pnv_xscom.c remains the XSCOM bridge implementation, and pnv_adu.c implements the ADU registers and other functions. So far, just the ADU no-op registers in the pnv_xscom.c default handler are moved over to the adu model. Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/meson.build | 1 + hw/ppc/pnv.c | 16 ++++++ hw/ppc/pnv_adu.c | 111 +++++++++++++++++++++++++++++++++++++ hw/ppc/pnv_xscom.c | 9 --- hw/ppc/trace-events | 4 ++ include/hw/ppc/pnv_adu.h | 25 +++++++++ include/hw/ppc/pnv_chip.h | 3 + include/hw/ppc/pnv_xscom.h | 6 ++ 8 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 hw/ppc/pnv_adu.c create mode 100644 include/hw/ppc/pnv_adu.h diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 3ebbf329bc..7cd9189869 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -42,6 +42,7 @@ endif ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv.c', 'pnv_xscom.c', + 'pnv_adu.c', 'pnv_core.c', 'pnv_i2c.c', 'pnv_lpc.c', diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index f56dcf6597..689197cbb7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1551,6 +1551,7 @@ static void pnv_chip_power9_instance_init(Object *obj) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; + object_initialize_child(obj, "adu", &chip9->adu, TYPE_PNV_ADU); object_initialize_child(obj, "xive", &chip9->xive, TYPE_PNV_XIVE); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip9->xive), "xive-fabric"); @@ -1661,6 +1662,13 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) return; } + /* ADU */ + if (!qdev_realize(DEVICE(&chip9->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_ADU_BASE, + &chip9->adu.xscom_regs); + pnv_chip_quad_realize(chip9, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1827,6 +1835,7 @@ static void pnv_chip_power10_instance_init(Object *obj) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; + object_initialize_child(obj, "adu", &chip10->adu, TYPE_PNV_ADU); object_initialize_child(obj, "xive", &chip10->xive, TYPE_PNV_XIVE2); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip10->xive), "xive-fabric"); @@ -1919,6 +1928,13 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + /* ADU */ + if (!qdev_realize(DEVICE(&chip10->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_ADU_BASE, + &chip10->adu.xscom_regs); + pnv_chip_power10_quad_realize(chip10, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c new file mode 100644 index 0000000000..8279bc8b26 --- /dev/null +++ b/hw/ppc/pnv_adu.c @@ -0,0 +1,111 @@ +/* + * QEMU PowerPC PowerNV ADU unit + * + * The ADU unit actually implements XSCOM, which is the bridge between MMIO + * and PIB. However it also includes control and status registers and other + * functions that are exposed as PIB (xscom) registers. + * + * To keep things simple, pnv_xscom.c remains the XSCOM bridge + * implementation, and pnv_adu.c implements the ADU registers and other + * functions. + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_adu.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_xscom.h" +#include "trace.h" + +static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width) +{ + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case 0x18: /* Receive status reg */ + case 0x12: /* log register */ + case 0x13: /* error register */ + break; + + default: + qemu_log_mask(LOG_UNIMP, "ADU Unimplemented read register: Ox%08x\n", + offset); + } + + trace_pnv_adu_xscom_read(addr, val); + + return val; +} + +static void pnv_adu_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + uint32_t offset = addr >> 3; + + trace_pnv_adu_xscom_write(addr, val); + + switch (offset) { + case 0x18: /* Receive status reg */ + case 0x12: /* log register */ + case 0x13: /* error register */ + break; + + default: + qemu_log_mask(LOG_UNIMP, "ADU Unimplemented write register: Ox%08x\n", + offset); + } +} + +const MemoryRegionOps pnv_adu_xscom_ops = { + .read = pnv_adu_xscom_read, + .write = pnv_adu_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_adu_realize(DeviceState *dev, Error **errp) +{ + PnvADU *adu = PNV_ADU(dev); + + /* XScom regions for ADU registers */ + pnv_xscom_region_init(&adu->xscom_regs, OBJECT(dev), + &pnv_adu_xscom_ops, adu, "xscom-adu", + PNV9_XSCOM_ADU_SIZE); +} + +static void pnv_adu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_adu_realize; + dc->desc = "PowerNV ADU"; + dc->user_creatable = false; +} + +static const TypeInfo pnv_adu_type_info = { + .name = TYPE_PNV_ADU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvADU), + .class_init = pnv_adu_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } }, +}; + +static void pnv_adu_register_types(void) +{ + type_register_static(&pnv_adu_type_info); +} + +type_init(pnv_adu_register_types); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index a17816d072..d192bbe2c2 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -75,11 +75,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case PRD_P9_IPOLL_REG_MASK: case PRD_P9_IPOLL_REG_STATUS: - /* P9 xscom reset */ - case 0x0090018: /* Receive status reg */ - case 0x0090012: /* log register */ - case 0x0090013: /* error register */ - /* P8 xscom reset */ case 0x2020007: /* ADU stuff, log register */ case 0x2020009: /* ADU stuff, error register */ @@ -119,10 +114,6 @@ static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val) case 0x1010c03: /* PIBAM FIR MASK */ case 0x1010c04: /* PIBAM FIR MASK */ case 0x1010c05: /* PIBAM FIR MASK */ - /* P9 xscom reset */ - case 0x0090018: /* Receive status reg */ - case 0x0090012: /* log register */ - case 0x0090013: /* error register */ /* P8 xscom reset */ case 0x2020007: /* ADU stuff, log register */ diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index bf29bbfd4b..1f125ce841 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -95,6 +95,10 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 +# pnv_adu.c +pnv_adu_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_adu_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 + # pnv_chiptod.c pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 diff --git a/include/hw/ppc/pnv_adu.h b/include/hw/ppc/pnv_adu.h new file mode 100644 index 0000000000..b5f308627b --- /dev/null +++ b/include/hw/ppc/pnv_adu.h @@ -0,0 +1,25 @@ +/* + * QEMU PowerPC PowerNV Emulation of some ADU behaviour + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_ADU_H +#define PPC_PNV_ADU_H + +#include "hw/ppc/pnv.h" +#include "hw/qdev-core.h" + +#define TYPE_PNV_ADU "pnv-adu" + +OBJECT_DECLARE_TYPE(PnvADU, PnvADUClass, PNV_ADU) + +struct PnvADU { + DeviceState xd; + + MemoryRegion xscom_regs; +}; + +#endif /* PPC_PNV_ADU_H */ diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index a4ed17ac59..4eaa7d3999 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -2,6 +2,7 @@ #define PPC_PNV_CHIP_H #include "hw/pci-host/pnv_phb4.h" +#include "hw/ppc/pnv_adu.h" #include "hw/ppc/pnv_chiptod.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_homer.h" @@ -77,6 +78,7 @@ struct Pnv9Chip { PnvChip parent_obj; /*< public >*/ + PnvADU adu; PnvXive xive; Pnv9Psi psi; PnvLpcController lpc; @@ -110,6 +112,7 @@ struct Pnv10Chip { PnvChip parent_obj; /*< public >*/ + PnvADU adu; PnvXive2 xive; Pnv9Psi psi; PnvLpcController lpc; diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 6209e18492..e93d310e79 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -82,6 +82,9 @@ struct PnvXScomInterfaceClass { #define PNV_XSCOM_PBCQ_SPCI_BASE 0x9013c00 #define PNV_XSCOM_PBCQ_SPCI_SIZE 0x5 +#define PNV9_XSCOM_ADU_BASE 0x0090000 +#define PNV9_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 9) */ @@ -128,6 +131,9 @@ struct PnvXScomInterfaceClass { #define PNV9_XSCOM_PEC_PCI_STK1 0x140 #define PNV9_XSCOM_PEC_PCI_STK2 0x180 +#define PNV10_XSCOM_ADU_BASE 0x0090000 +#define PNV10_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 10) */ From 24bd283bccb334109f112839ab6867f0192045d6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Wed, 17 Apr 2024 14:50:13 +1000 Subject: [PATCH 2496/2884] ppc/pnv: Implement ADU access to LPC space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of the functions of the ADU is indirect memory access engines that send and receive data via ADU registers. This implements the ADU LPC memory access functionality sufficiently for IBM proprietary firmware to access the UART and print characters to the serial port as it does on real hardware. This requires a linkage between adu and lpc, which allows adu to perform memory access in the lpc space. Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 4 ++ hw/ppc/pnv_adu.c | 95 ++++++++++++++++++++++++++++++++++++++++ hw/ppc/pnv_lpc.c | 12 ++--- include/hw/ppc/pnv_adu.h | 7 +++ include/hw/ppc/pnv_lpc.h | 5 +++ 5 files changed, 117 insertions(+), 6 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 689197cbb7..91ff1be21f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1663,6 +1663,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) } /* ADU */ + object_property_set_link(OBJECT(&chip9->adu), "lpc", OBJECT(&chip9->lpc), + &error_abort); if (!qdev_realize(DEVICE(&chip9->adu), NULL, errp)) { return; } @@ -1929,6 +1931,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) } /* ADU */ + object_property_set_link(OBJECT(&chip10->adu), "lpc", OBJECT(&chip10->lpc), + &error_abort); if (!qdev_realize(DEVICE(&chip10->adu), NULL, errp)) { return; } diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c index 8279bc8b26..81b7d6e526 100644 --- a/hw/ppc/pnv_adu.c +++ b/hw/ppc/pnv_adu.c @@ -21,11 +21,18 @@ #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_adu.h" #include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_xscom.h" #include "trace.h" +#define ADU_LPC_BASE_REG 0x40 +#define ADU_LPC_CMD_REG 0x41 +#define ADU_LPC_DATA_REG 0x42 +#define ADU_LPC_STATUS_REG 0x43 + static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width) { + PnvADU *adu = PNV_ADU(opaque); uint32_t offset = addr >> 3; uint64_t val = 0; @@ -34,6 +41,24 @@ static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width) case 0x12: /* log register */ case 0x13: /* error register */ break; + case ADU_LPC_BASE_REG: + /* + * LPC Address Map in Pervasive ADU Workbook + * + * return PNV10_LPCM_BASE(chip) & PPC_BITMASK(8, 31); + * XXX: implement as class property, or get from LPC? + */ + qemu_log_mask(LOG_UNIMP, "ADU: LPC_BASE_REG is not implemented\n"); + break; + case ADU_LPC_CMD_REG: + val = adu->lpc_cmd_reg; + break; + case ADU_LPC_DATA_REG: + val = adu->lpc_data_reg; + break; + case ADU_LPC_STATUS_REG: + val = PPC_BIT(0); /* ack / done */ + break; default: qemu_log_mask(LOG_UNIMP, "ADU Unimplemented read register: Ox%08x\n", @@ -45,9 +70,30 @@ static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width) return val; } +static bool lpc_cmd_read(PnvADU *adu) +{ + return !!(adu->lpc_cmd_reg & PPC_BIT(0)); +} + +static bool lpc_cmd_write(PnvADU *adu) +{ + return !lpc_cmd_read(adu); +} + +static uint32_t lpc_cmd_addr(PnvADU *adu) +{ + return (adu->lpc_cmd_reg & PPC_BITMASK(32, 63)) >> PPC_BIT_NR(63); +} + +static uint32_t lpc_cmd_size(PnvADU *adu) +{ + return (adu->lpc_cmd_reg & PPC_BITMASK(5, 11)) >> PPC_BIT_NR(11); +} + static void pnv_adu_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { + PnvADU *adu = PNV_ADU(opaque); uint32_t offset = addr >> 3; trace_pnv_adu_xscom_write(addr, val); @@ -58,6 +104,47 @@ static void pnv_adu_xscom_write(void *opaque, hwaddr addr, uint64_t val, case 0x13: /* error register */ break; + case ADU_LPC_BASE_REG: + qemu_log_mask(LOG_UNIMP, + "ADU: Changing LPC_BASE_REG is not implemented\n"); + break; + + case ADU_LPC_CMD_REG: + adu->lpc_cmd_reg = val; + if (lpc_cmd_read(adu)) { + uint32_t lpc_addr = lpc_cmd_addr(adu); + uint32_t lpc_size = lpc_cmd_size(adu); + uint64_t data = 0; + + pnv_lpc_opb_read(adu->lpc, lpc_addr, (void *)&data, lpc_size); + + /* + * ADU access is performed within 8-byte aligned sectors. Smaller + * access sizes don't get formatted to the least significant byte, + * but rather appear in the data reg at the same offset as the + * address in memory. This shifts them into that position. + */ + adu->lpc_data_reg = be64_to_cpu(data) >> ((lpc_addr & 7) * 8); + } + break; + + case ADU_LPC_DATA_REG: + adu->lpc_data_reg = val; + if (lpc_cmd_write(adu)) { + uint32_t lpc_addr = lpc_cmd_addr(adu); + uint32_t lpc_size = lpc_cmd_size(adu); + uint64_t data; + + data = cpu_to_be64(val) >> ((lpc_addr & 7) * 8); /* See above */ + pnv_lpc_opb_write(adu->lpc, lpc_addr, (void *)&data, lpc_size); + } + break; + + case ADU_LPC_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "ADU: Changing LPC_STATUS_REG is not implemented\n"); + break; + default: qemu_log_mask(LOG_UNIMP, "ADU Unimplemented write register: Ox%08x\n", offset); @@ -78,18 +165,26 @@ static void pnv_adu_realize(DeviceState *dev, Error **errp) { PnvADU *adu = PNV_ADU(dev); + assert(adu->lpc); + /* XScom regions for ADU registers */ pnv_xscom_region_init(&adu->xscom_regs, OBJECT(dev), &pnv_adu_xscom_ops, adu, "xscom-adu", PNV9_XSCOM_ADU_SIZE); } +static Property pnv_adu_properties[] = { + DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_adu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_adu_realize; dc->desc = "PowerNV ADU"; + device_class_set_props(dc, pnv_adu_properties); dc->user_creatable = false; } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 0f14e180af..f8aad955b5 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -236,16 +236,16 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, * TODO: rework to use address_space_stq() and address_space_ldq() * instead. */ -static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, - int sz) +bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz) { /* XXX Handle access size limits and FW read caching here */ return !address_space_read(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, data, sz); } -static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, - int sz) +bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz) { /* XXX Handle access size limits here */ return !address_space_write(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, @@ -277,7 +277,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) } if (cmd & ECCB_CTL_READ) { - success = opb_read(lpc, opb_addr, data, sz); + success = pnv_lpc_opb_read(lpc, opb_addr, data, sz); if (success) { lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | (((uint64_t)data[0]) << 24 | @@ -294,7 +294,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) data[2] = lpc->eccb_data_reg >> 8; data[3] = lpc->eccb_data_reg; - success = opb_write(lpc, opb_addr, data, sz); + success = pnv_lpc_opb_write(lpc, opb_addr, data, sz); lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; } /* XXX Which error bit (if any) to signal OPB error ? */ diff --git a/include/hw/ppc/pnv_adu.h b/include/hw/ppc/pnv_adu.h index b5f308627b..f9dbd8c8b3 100644 --- a/include/hw/ppc/pnv_adu.h +++ b/include/hw/ppc/pnv_adu.h @@ -10,6 +10,7 @@ #define PPC_PNV_ADU_H #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_lpc.h" #include "hw/qdev-core.h" #define TYPE_PNV_ADU "pnv-adu" @@ -19,6 +20,12 @@ OBJECT_DECLARE_TYPE(PnvADU, PnvADUClass, PNV_ADU) struct PnvADU { DeviceState xd; + /* LPCMC (LPC Master Controller) access engine */ + PnvLpcController *lpc; + uint64_t lpc_base_reg; + uint64_t lpc_cmd_reg; + uint64_t lpc_data_reg; + MemoryRegion xscom_regs; }; diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index e0fd5e4130..174add4c53 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -109,6 +109,11 @@ struct PnvLpcClass { DeviceRealize parent_realize; }; +bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz); +bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz); + ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp); int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, uint64_t lpcm_size); From 7f516cdeef6d62e78ee769855dff95666e6b8c1d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Sat, 6 Jul 2024 13:22:58 +1000 Subject: [PATCH 2497/2884] target/ppc: Fix msgsnd for POWER8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POWER8 (ISA v2.07S) introduced the doorbell facility, the msgsnd instruction behaved mostly like msgsndp, it was addressed by TIR and could only send interrupts between threads on the core. ISA v3.0 changed msgsnd to be addressed by PIR and can interrupt any thread in the system. msgsnd only implements the v3.0 semantics, which can make multi-threaded POWER8 hang when booting Linux (due to IPIs failing). This change adds v2.07 semantics. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/excp_helper.c | 74 ++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0cd542675f..c0120c8a88 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2998,6 +2998,41 @@ static inline bool dbell_bcast_subproc(target_ulong rb) return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; } +/* + * Send an interrupt to a thread in the same core as env). + */ +static void msgsnd_core_tir(CPUPPCState *env, uint32_t target_tir, int irq) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t nr_threads = cs->nr_threads; + + if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/ + } + + if (target_tir >= nr_threads) { + return; + } + + if (nr_threads == 1) { + ppc_set_irq(cpu, irq, 1); + } else { + CPUState *ccs; + + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + if (target_tir == ppc_cpu_tir(ccpu)) { + ppc_set_irq(ccpu, irq, 1); + break; + } + } + bql_unlock(); + } +} + void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { if (!dbell_type_server(rb)) { @@ -3018,6 +3053,13 @@ void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) return; } + /* POWER8 msgsnd is like msgsndp (targets a thread within core) */ + if (!(env->insns_flags2 & PPC2_ISA300)) { + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_HDOORBELL); + return; + } + + /* POWER9 and later msgsnd is a global (targets any thread) */ cpu = ppc_get_vcpu_by_pir(pir); if (!cpu) { return; @@ -3064,41 +3106,13 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) */ void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) { - CPUState *cs = env_cpu(env); - PowerPCCPU *cpu = env_archcpu(env); - CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - int ttir = rb & PPC_BITMASK(57, 63); - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/ - } - - if (!dbell_type_server(rb) || ttir >= nr_threads) { + if (!dbell_type_server(rb)) { return; } - if (nr_threads == 1) { - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1); - return; - } - - /* Does iothread need to be locked for walking CPU list? */ - bql_lock(); - THREAD_SIBLING_FOREACH(cs, ccs) { - PowerPCCPU *ccpu = POWERPC_CPU(ccs); - uint32_t thread_id = ppc_cpu_tir(ccpu); - - if (ttir == thread_id) { - ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1); - bql_unlock(); - return; - } - } - - g_assert_not_reached(); + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_DOORBELL); } #endif /* TARGET_PPC64 */ From 060e61436794d13ede9a1d0eb2b1d0cf3b7cfcfd Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Sun, 26 May 2024 15:04:05 +1000 Subject: [PATCH 2498/2884] ppc/pnv: Add pointer from PnvCPUState to PnvCore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps move core state from CPU to core structures. Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_core.c | 3 +++ include/hw/ppc/pnv_core.h | 1 + 2 files changed, 4 insertions(+) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index f40ab721d6..2da271ffb6 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -278,6 +278,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { PowerPCCPU *cpu; + PnvCPUState *pnv_cpu; obj = object_new(typename); cpu = POWERPC_CPU(obj); @@ -288,6 +289,8 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) object_property_add_child(OBJECT(pc), name, obj); cpu->machine_data = g_new0(PnvCPUState, 1); + pnv_cpu = pnv_cpu_state(cpu); + pnv_cpu->pnv_core = pc; object_unref(obj); } diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index c6d62fd145..29cab9dfd9 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -54,6 +54,7 @@ struct PnvCoreClass { #define PNV_CORE_TYPE_NAME(cpu_model) cpu_model PNV_CORE_TYPE_SUFFIX typedef struct PnvCPUState { + PnvCore *pnv_core; Object *intc; } PnvCPUState; From 0ca94b2f11223d41258e6a7a046e5ccde831de46 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 24 May 2024 21:58:18 +1000 Subject: [PATCH 2499/2884] ppc/pnv: Move timebase state into PnvCore The timebase state machine is per per-core state and can be driven by any thread in the core. It is currently implemented as a hack where the state is in a CPU structure and only thread 0's state is accessed by the chiptod, which limits programming the timebase side of the state machine to thread 0 of a core. Move the state out into PnvCore and share it among all threads. Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_chiptod.c | 7 ++--- include/hw/ppc/pnv_core.h | 17 ++++++++++ target/ppc/cpu.h | 21 ------------- target/ppc/timebase_helper.c | 60 +++++++++++++++++++++--------------- 4 files changed, 55 insertions(+), 50 deletions(-) diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index 3831a72101..1e41fe557a 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -364,8 +364,7 @@ static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" " TOD_MOVE_TOD_TO_TB_REG with no slave target\n"); } else { - PowerPCCPU *cpu = chiptod->slave_pc_target->threads[0]; - CPUPPCState *env = &cpu->env; + PnvCore *pc = chiptod->slave_pc_target; /* * Moving TOD to TB will set the TB of all threads in a @@ -377,8 +376,8 @@ static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, * thread 0. */ - if (env->pnv_tod_tbst.tb_ready_for_tod) { - env->pnv_tod_tbst.tod_sent_to_tb = 1; + if (pc->tod_state.tb_ready_for_tod) { + pc->tod_state.tod_sent_to_tb = 1; } else { qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" " TOD_MOVE_TOD_TO_TB_REG with TB not ready to" diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 29cab9dfd9..ffec8516ae 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -25,6 +25,20 @@ #include "hw/ppc/pnv.h" #include "qom/object.h" +/* Per-core ChipTOD / TimeBase state */ +typedef struct PnvCoreTODState { + int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ + int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ + + /* + * "Timers" for async TBST events are simulated by mfTFAC because TFAC + * is polled for such events. These are just used to ensure firmware + * performs the polling at least a few times. + */ + int tb_state_timer; + int tb_sync_pulse_timer; +} PnvCoreTODState; + #define TYPE_PNV_CORE "powernv-cpu-core" OBJECT_DECLARE_TYPE(PnvCore, PnvCoreClass, PNV_CORE) @@ -38,6 +52,9 @@ struct PnvCore { uint32_t pir; uint32_t hwid; uint64_t hrmor; + + PnvCoreTODState tod_state; + PnvChip *chip; MemoryRegion xscom_regs; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2015e603d4..c78d6ca91a 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1196,21 +1196,6 @@ DEXCR_ASPECT(SRAPD, 4) DEXCR_ASPECT(NPHIE, 5) DEXCR_ASPECT(PHIE, 6) -/*****************************************************************************/ -/* PowerNV ChipTOD and TimeBase State Machine */ -struct pnv_tod_tbst { - int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ - int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ - - /* - * "Timers" for async TBST events are simulated by mfTFAC because TFAC - * is polled for such events. These are just used to ensure firmware - * performs the polling at least a few times. - */ - int tb_state_timer; - int tb_sync_pulse_timer; -}; - /*****************************************************************************/ /* The whole PowerPC CPU context */ @@ -1291,12 +1276,6 @@ struct CPUArchState { uint32_t tlb_need_flush; /* Delayed flush needed */ #define TLB_NEED_LOCAL_FLUSH 0x1 #define TLB_NEED_GLOBAL_FLUSH 0x2 - -#if defined(TARGET_PPC64) - /* PowerNV chiptod / timebase facility state. */ - /* Would be nice to put these into PnvCore */ - struct pnv_tod_tbst pnv_tod_tbst; -#endif #endif /* Other registers */ diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 39d397416e..b02535bbd5 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -217,7 +217,13 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) store_booke_tsr(env, val); } -#if defined(TARGET_PPC64) +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +/* + * qemu-user breaks with pnv headers, so they go under ifdefs for now. + * A clean up may be to move powernv specific registers and helpers into + * target/ppc/pnv_helper.c + */ +#include "hw/ppc/pnv_core.h" /* * POWER processor Timebase Facility */ @@ -298,8 +304,17 @@ static void write_tfmr(CPUPPCState *env, target_ulong val) } } +static PnvCoreTODState *cpu_get_tbst(PowerPCCPU *cpu) +{ + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + + return &pc->tod_state; +} + static void tb_state_machine_step(CPUPPCState *env) { + PowerPCCPU *cpu = env_archcpu(env); + PnvCoreTODState *tod_state = cpu_get_tbst(cpu); uint64_t tfmr = env->spr[SPR_TFMR]; unsigned int tbst = tfmr_get_tb_state(tfmr); @@ -307,15 +322,15 @@ static void tb_state_machine_step(CPUPPCState *env) return; } - if (env->pnv_tod_tbst.tb_sync_pulse_timer) { - env->pnv_tod_tbst.tb_sync_pulse_timer--; + if (tod_state->tb_sync_pulse_timer) { + tod_state->tb_sync_pulse_timer--; } else { tfmr |= TFMR_TB_SYNC_OCCURED; write_tfmr(env, tfmr); } - if (env->pnv_tod_tbst.tb_state_timer) { - env->pnv_tod_tbst.tb_state_timer--; + if (tod_state->tb_state_timer) { + tod_state->tb_state_timer--; return; } @@ -332,20 +347,20 @@ static void tb_state_machine_step(CPUPPCState *env) } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { if (tbst == TBST_SYNC_WAIT) { tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD); - env->pnv_tod_tbst.tb_state_timer = 3; + tod_state->tb_state_timer = 3; } else if (tbst == TBST_GET_TOD) { - if (env->pnv_tod_tbst.tod_sent_to_tb) { + if (tod_state->tod_sent_to_tb) { tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING); tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; - env->pnv_tod_tbst.tb_ready_for_tod = 0; - env->pnv_tod_tbst.tod_sent_to_tb = 0; + tod_state->tb_ready_for_tod = 0; + tod_state->tod_sent_to_tb = 0; } } else { qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " "state machine in invalid state 0x%x\n", tbst); tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; - env->pnv_tod_tbst.tb_ready_for_tod = 0; + tod_state->tb_ready_for_tod = 0; } } @@ -361,6 +376,8 @@ target_ulong helper_load_tfmr(CPUPPCState *env) void helper_store_tfmr(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = env_archcpu(env); + PnvCoreTODState *tod_state = cpu_get_tbst(cpu); uint64_t tfmr = env->spr[SPR_TFMR]; uint64_t clear_on_write; unsigned int tbst = tfmr_get_tb_state(tfmr); @@ -384,14 +401,7 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) * after the second mfspr. */ tfmr &= ~TFMR_TB_SYNC_OCCURED; - env->pnv_tod_tbst.tb_sync_pulse_timer = 1; - - if (ppc_cpu_tir(env_archcpu(env)) != 0 && - (val & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB))) { - qemu_log_mask(LOG_UNIMP, "TFMR timebase state machine can only be " - "driven by thread 0\n"); - goto out; - } + tod_state->tb_sync_pulse_timer = 1; if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) == (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) { @@ -399,7 +409,7 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) "MOVE_CHIP_TOD_TO_TB both set\n"); tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; - env->pnv_tod_tbst.tb_ready_for_tod = 0; + tod_state->tb_ready_for_tod = 0; goto out; } @@ -413,8 +423,8 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) tfmr &= ~TFMR_LOAD_TOD_MOD; tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; tfmr &= ~TFMR_FIRMWARE_CONTROL_ERROR; /* XXX: should this be cleared? */ - env->pnv_tod_tbst.tb_ready_for_tod = 0; - env->pnv_tod_tbst.tod_sent_to_tb = 0; + tod_state->tb_ready_for_tod = 0; + tod_state->tod_sent_to_tb = 0; goto out; } @@ -427,19 +437,19 @@ void helper_store_tfmr(CPUPPCState *env, target_ulong val) if (tfmr & TFMR_LOAD_TOD_MOD) { /* Wait for an arbitrary 3 mfspr until the next state transition. */ - env->pnv_tod_tbst.tb_state_timer = 3; + tod_state->tb_state_timer = 3; } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { if (tbst == TBST_NOT_SET) { tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT); - env->pnv_tod_tbst.tb_ready_for_tod = 1; - env->pnv_tod_tbst.tb_state_timer = 3; /* arbitrary */ + tod_state->tb_ready_for_tod = 1; + tod_state->tb_state_timer = 3; /* arbitrary */ } else { qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " "not in TB not set state 0x%x\n", tbst); tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; - env->pnv_tod_tbst.tb_ready_for_tod = 0; + tod_state->tb_ready_for_tod = 0; } } From 60d30cff8472c0bf05a40b0f55221fb4efb768e2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Sun, 26 May 2024 21:24:00 +1000 Subject: [PATCH 2500/2884] target/ppc: Move SPR indirect registers into PnvCore SPRC/SPRD were recently added to all BookS CPUs supported, but they are only tested on POWER9 and POWER10, so restrict them to those CPUs. SPR indirect scratch registers presently replicated per-CPU like SMT SPRs, but the PnvCore is a better place for them since they are restricted to P9/P10. Also add SPR indirect read access to core thread state for POWER9 since skiboot accesses that when booting to check for big-core mode. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- include/hw/ppc/pnv_core.h | 1 + target/ppc/cpu.h | 3 -- target/ppc/cpu_init.c | 21 +++++++------ target/ppc/misc_helper.c | 65 ++++++++++++++++++++------------------- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index ffec8516ae..693acb189b 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -53,6 +53,7 @@ struct PnvCore { uint32_t hwid; uint64_t hrmor; + target_ulong scratch[8]; /* SPRC/SPRD indirect SCRATCH registers */ PnvCoreTODState tod_state; PnvChip *chip; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c78d6ca91a..95ba9e7590 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1253,9 +1253,6 @@ struct CPUArchState { ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ struct CPUBreakpoint *ciabr_breakpoint; struct CPUWatchpoint *dawr0_watchpoint; - - /* POWER CPU regs/state */ - target_ulong scratch[8]; /* SCRATCH registers (shared across core) */ #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 2e652f498e..42bb047b54 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5760,16 +5760,6 @@ static void register_power_common_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_core_write_generic, 0x00000000); - spr_register_hv(env, SPR_POWER_SPRC, "SPRC", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_sprc, - 0x00000000); - spr_register_hv(env, SPR_POWER_SPRD, "SPRD", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_sprd, &spr_write_sprd, - 0x00000000); #endif } @@ -5803,6 +5793,17 @@ static void register_power8_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_WORT, 0); + /* SPRC/SPRD exist in earlier CPUs but only tested on POWER9/10 */ + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sprc, + 0x00000000); + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sprd, &spr_write_sprd, + 0x00000000); #endif } diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index fa47be2298..de7c8813ec 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -308,6 +308,13 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) bql_unlock(); } +/* + * qemu-user breaks with pnv headers, so they go under ifdefs for now. + * A clean up may be to move powernv specific registers and helpers into + * target/ppc/pnv_helper.c + */ +#include "hw/ppc/pnv_core.h" + /* Indirect SCOM (SPRC/SPRD) access to SCRATCH0-7 are implemented. */ void helper_store_sprc(CPUPPCState *env, target_ulong val) { @@ -321,11 +328,18 @@ void helper_store_sprc(CPUPPCState *env, target_ulong val) target_ulong helper_load_sprd(CPUPPCState *env) { + /* + * SPRD is a HV-only register for Power CPUs, so this will only be + * accessed by powernv machines. + */ + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; target_ulong sprc = env->spr[SPR_POWER_SPRC]; - switch (sprc & 0x3c0) { - case 0: /* SCRATCH0-7 */ - return env->scratch[(sprc >> 3) & 0x7]; + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + return pc->scratch[(sprc >> 3) & 0x7]; default: qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" TARGET_FMT_lx"\n", sprc); @@ -334,41 +348,28 @@ target_ulong helper_load_sprd(CPUPPCState *env) return 0; } -static void do_store_scratch(CPUPPCState *env, int nr, target_ulong val) -{ - CPUState *cs = env_cpu(env); - CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - - /* - * Log stores to SCRATCH, because some firmware uses these for debugging - * and logging, but they would normally be read by the BMC, which is - * not implemented in QEMU yet. This gives a way to get at the information. - * Could also dump these upon checkstop. - */ - qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); - - if (nr_threads == 1) { - env->scratch[nr] = val; - return; - } - - THREAD_SIBLING_FOREACH(cs, ccs) { - CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; - cenv->scratch[nr] = val; - } -} - void helper_store_sprd(CPUPPCState *env, target_ulong val) { target_ulong sprc = env->spr[SPR_POWER_SPRC]; + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + int nr; - switch (sprc & 0x3c0) { - case 0: /* SCRATCH0-7 */ - do_store_scratch(env, (sprc >> 3) & 0x7, val); + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + /* + * Log stores to SCRATCH, because some firmware uses these for + * debugging and logging, but they would normally be read by the BMC, + * which is not implemented in QEMU yet. This gives a way to get at the + * information. Could also dump these upon checkstop. + */ + nr = (sprc >> 3) & 0x7; + qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); + pc->scratch[nr] = val; break; default: - qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" TARGET_FMT_lx"\n", sprc); break; } From d76cb5a53b04d52db1500e83bd6bdfbfeca44e4d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 24 May 2024 15:52:04 +1000 Subject: [PATCH 2501/2884] ppc/pnv: use class attribute to limit SMT threads for different machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a class attribute to specify the number of SMT threads per core permitted for different machines, 8 for powernv8 and 4 for powernv9/10. Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 12 +++++++++--- include/hw/ppc/pnv.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 91ff1be21f..a84e0e0e30 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -899,6 +899,7 @@ static void pnv_init(MachineState *machine) PnvMachineState *pnv = PNV_MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine); + int max_smt_threads = pmc->max_smt_threads; char *fw_filename; long fw_size; uint64_t chip_ram_start = 0; @@ -997,17 +998,19 @@ static void pnv_init(MachineState *machine) pnv->num_chips = machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); - if (machine->smp.threads > 8) { - error_report("Cannot support more than 8 threads/core " - "on a powernv machine"); + if (machine->smp.threads > max_smt_threads) { + error_report("Cannot support more than %d threads/core " + "on %s machine", max_smt_threads, mc->desc); exit(1); } + if (!is_power_of_2(machine->smp.threads)) { error_report("Cannot support %d threads/core on a powernv" "machine because it must be a power of 2", machine->smp.threads); exit(1); } + /* * TODO: should we decide on how many chips we can create based * on #cores and Venice vs. Murano vs. Naples chip type etc..., @@ -2490,6 +2493,7 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 8; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } @@ -2514,6 +2518,7 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; pmc->dt_power_mgt = pnv_dt_power_mgt; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); @@ -2538,6 +2543,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; pmc->dt_power_mgt = pnv_dt_power_mgt; xfc->match_nvt = pnv10_xive_match_nvt; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 476b136146..1993dededf 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -76,6 +76,7 @@ struct PnvMachineClass { /*< public >*/ const char *compat; int compat_size; + int max_smt_threads; void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); void (*i2c_init)(PnvMachineState *pnv); From 25de28220cedadac15021ec40047785f30e153fe Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 24 May 2024 11:54:09 +1000 Subject: [PATCH 2502/2884] ppc/pnv: Extend chip_pir class method to TIR as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chip_pir chip class method allows the platform to set the PIR processor identification register. Extend this to a more general ID function which also allows the TIR to be set. This is in preparation for "big core", which is a more complicated topology of cores and threads. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 81 +++++++++++++++++++++++++-------------- hw/ppc/pnv_core.c | 10 +++-- include/hw/ppc/pnv_chip.h | 4 +- 3 files changed, 62 insertions(+), 33 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a84e0e0e30..8827f729b1 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -154,7 +154,7 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) char *nodename; int cpus_offset = get_cpus_node(fdt); - pir = pnv_cc->chip_pir(chip, pc->hwid, 0); + pnv_cc->get_pir_tir(chip, pc->hwid, 0, &pir, NULL); nodename = g_strdup_printf("%s@%x", dc->fw_name, pir); offset = fdt_add_subnode(fdt, cpus_offset, nodename); @@ -236,7 +236,8 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) /* Build interrupt servers properties */ for (i = 0; i < smt_threads; i++) { - servers_prop[i] = cpu_to_be32(pnv_cc->chip_pir(chip, pc->hwid, i)); + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i] = cpu_to_be32(pir); } _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", servers_prop, sizeof(*servers_prop) * smt_threads))); @@ -248,14 +249,17 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t hwid, uint32_t nr_threads) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - uint32_t pir = pcc->chip_pir(chip, hwid, 0); - uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); + uint32_t pir; + uint64_t addr; char *name; const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; uint32_t irange[2], i, rsize; uint64_t *reg; int offset; + pcc->get_pir_tir(chip, hwid, 0, &pir, NULL); + addr = PNV_ICP_BASE(chip) | (pir << 12); + irange[0] = cpu_to_be32(pir); irange[1] = cpu_to_be32(nr_threads); @@ -1106,10 +1110,16 @@ static void pnv_init(MachineState *machine) * 25:28 Core number * 29:31 Thread ID */ -static uint32_t pnv_chip_pir_p8(PnvChip *chip, uint32_t core_id, - uint32_t thread_id) +static void pnv_get_pir_tir_p8(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - return (chip->chip_id << 7) | (core_id << 3) | thread_id; + if (pir) { + *pir = (chip->chip_id << 7) | (core_id << 3) | thread_id; + } + if (tir) { + *tir = thread_id; + } } static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1161,14 +1171,20 @@ static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, * * We only care about the lower bits. uint32_t is fine for the moment. */ -static uint32_t pnv_chip_pir_p9(PnvChip *chip, uint32_t core_id, - uint32_t thread_id) +static void pnv_get_pir_tir_p9(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - if (chip->nr_threads == 8) { - return (chip->chip_id << 8) | ((thread_id & 1) << 2) | (core_id << 3) | - (thread_id >> 1); - } else { - return (chip->chip_id << 8) | (core_id << 2) | thread_id; + if (pir) { + if (chip->nr_threads == 8) { + *pir = (chip->chip_id << 8) | ((thread_id & 1) << 2) | + (core_id << 3) | (thread_id >> 1); + } else { + *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; + } + } + if (tir) { + *tir = thread_id; } } @@ -1183,14 +1199,20 @@ static uint32_t pnv_chip_pir_p9(PnvChip *chip, uint32_t core_id, * * We only care about the lower bits. uint32_t is fine for the moment. */ -static uint32_t pnv_chip_pir_p10(PnvChip *chip, uint32_t core_id, - uint32_t thread_id) +static void pnv_get_pir_tir_p10(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - if (chip->nr_threads == 8) { - return (chip->chip_id << 8) | ((core_id / 4) << 4) | - ((core_id % 2) << 3) | thread_id; - } else { - return (chip->chip_id << 8) | (core_id << 2) | thread_id; + if (pir) { + if (chip->nr_threads == 8) { + *pir = (chip->chip_id << 8) | ((core_id / 4) << 4) | + ((core_id % 2) << 3) | thread_id; + } else { + *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; + } + } + if (tir) { + *tir = thread_id; } } @@ -1370,8 +1392,11 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) int core_hwid = CPU_CORE(pnv_core)->core_id; for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { - uint32_t pir = pcc->chip_pir(chip, core_hwid, j); - PnvICPState *icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); + uint32_t pir; + PnvICPState *icp; + + pcc->get_pir_tir(chip, core_hwid, j, &pir, NULL); + icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); memory_region_add_subregion(&chip8->icp_mmio, pir << 12, &icp->mmio); @@ -1483,7 +1508,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ k->cores_mask = POWER8E_CORE_MASK; k->num_phbs = 3; - k->chip_pir = pnv_chip_pir_p8; + k->get_pir_tir = pnv_get_pir_tir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; @@ -1507,7 +1532,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ k->cores_mask = POWER8_CORE_MASK; k->num_phbs = 3; - k->chip_pir = pnv_chip_pir_p8; + k->get_pir_tir = pnv_get_pir_tir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; @@ -1531,7 +1556,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; k->num_phbs = 4; - k->chip_pir = pnv_chip_pir_p8; + k->get_pir_tir = pnv_get_pir_tir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; @@ -1814,7 +1839,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; - k->chip_pir = pnv_chip_pir_p9; + k->get_pir_tir = pnv_get_pir_tir_p9; k->intc_create = pnv_chip_power9_intc_create; k->intc_reset = pnv_chip_power9_intc_reset; k->intc_destroy = pnv_chip_power9_intc_destroy; @@ -2136,7 +2161,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220da04980000000ull; /* P10 DD2.0 (with NX) */ k->cores_mask = POWER10_CORE_MASK; - k->chip_pir = pnv_chip_pir_p10; + k->get_pir_tir = pnv_get_pir_tir_p10; k->intc_create = pnv_chip_power10_intc_create; k->intc_reset = pnv_chip_power10_intc_reset; k->intc_destroy = pnv_chip_power10_intc_destroy; diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 2da271ffb6..28ca61926d 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -227,8 +227,9 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, { CPUPPCState *env = &cpu->env; int core_hwid; - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - ppc_spr_t *tir = &env->spr_cb[SPR_TIR]; + ppc_spr_t *pir_spr = &env->spr_cb[SPR_PIR]; + ppc_spr_t *tir_spr = &env->spr_cb[SPR_TIR]; + uint32_t pir, tir; Error *local_err = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); @@ -244,8 +245,9 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, core_hwid = object_property_get_uint(OBJECT(pc), "hwid", &error_abort); - tir->default_value = thread_index; - pir->default_value = pcc->chip_pir(pc->chip, core_hwid, thread_index); + pcc->get_pir_tir(pc->chip, core_hwid, thread_index, &pir, &tir); + pir_spr->default_value = pir; + tir_spr->default_value = tir; /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 4eaa7d3999..7d5d08bcdc 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -150,7 +150,9 @@ struct PnvChipClass { DeviceRealize parent_realize; - uint32_t (*chip_pir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id); + /* Get PIR and TIR values for a CPU thread identified by core/thread id */ + void (*get_pir_tir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir); void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp); void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu); void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu); From feb37fdc821242d86c30bff33abd31bcce01e9e2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Thu, 16 May 2024 15:25:12 +1000 Subject: [PATCH 2503/2884] ppc: Add a core_index to CPUPPCState for SMT vCPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way SMT thread siblings are matched is clunky, using hard-coded logic that checks the PIR SPR. Change that to use a new core_index variable in the CPUPPCState, where all siblings have the same core_index. CPU realize routines have flexibility in setting core/sibling topology. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_core.c | 2 ++ hw/ppc/spapr_cpu_core.c | 4 ++++ target/ppc/cpu.h | 9 +++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 28ca61926d..7bda29b9c7 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -249,6 +249,8 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, pir_spr->default_value = pir; tir_spr->default_value = tir; + env->core_index = core_hwid; + /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); } diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index e7c9edd033..b228c1d498 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -300,11 +300,13 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) g_autofree char *id = NULL; CPUState *cs; PowerPCCPU *cpu; + CPUPPCState *env; obj = object_new(scc->cpu_type); cs = CPU(obj); cpu = POWERPC_CPU(obj); + env = &cpu->env; /* * All CPUs start halted. CPU0 is unhalted from the machine level reset code * and the rest are explicitly started up by the guest using an RTAS call. @@ -315,6 +317,8 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) return NULL; } + env->core_index = cc->core_id; + cpu->node_id = sc->node_id; id = g_strdup_printf("thread[%d]", i); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 95ba9e7590..7b52a9bb18 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1247,6 +1247,9 @@ struct CPUArchState { /* when a memory exception occurs, the access type is stored here */ int access_type; + /* For SMT processors */ + int core_index; + #if !defined(CONFIG_USER_ONLY) /* MMU context, only relevant for full system emulation */ #if defined(TARGET_PPC64) @@ -1402,12 +1405,10 @@ struct CPUArchState { uint64_t pmu_base_time; }; -#define _CORE_ID(cs) \ - (POWERPC_CPU(cs)->env.spr_cb[SPR_PIR].default_value & ~(cs->nr_threads - 1)) - #define THREAD_SIBLING_FOREACH(cs, cs_sibling) \ CPU_FOREACH(cs_sibling) \ - if (_CORE_ID(cs) == _CORE_ID(cs_sibling)) + if (POWERPC_CPU(cs)->env.core_index == \ + POWERPC_CPU(cs_sibling)->env.core_index) #define SET_FIT_PERIOD(a_, b_, c_, d_) \ do { \ From 50d8cfb949066e4466700e814a0e26719d70a951 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 24 May 2024 17:49:52 +1000 Subject: [PATCH 2504/2884] target/ppc: Add helpers to check for SMT sibling threads Add helpers for TCG code to determine if there are SMT siblings sharing per-core and per-lpar registers. This simplifies the callers and makes SMT register topology simpler to modify with later changes. Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/cpu.h | 11 +++++++++++ target/ppc/cpu_init.c | 2 +- target/ppc/excp_helper.c | 17 +++++------------ target/ppc/misc_helper.c | 27 ++++++--------------------- target/ppc/timebase_helper.c | 20 +++++++------------- 5 files changed, 30 insertions(+), 47 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 7b52a9bb18..417b284318 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1512,6 +1512,17 @@ struct PowerPCCPUClass { int (*check_attn)(CPUPPCState *env); }; +static inline bool ppc_cpu_core_single_threaded(CPUState *cs) +{ + return cs->nr_threads == 1; +} + +static inline bool ppc_cpu_lpar_single_threaded(CPUState *cs) +{ + return !(POWERPC_CPU(cs)->env.flags & POWERPC_FLAG_SMT_1LPAR) || + ppc_cpu_core_single_threaded(cs); +} + ObjectClass *ppc_cpu_class_by_name(const char *name); PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 42bb047b54..5ec87c56e4 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6976,7 +6976,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) pcc->parent_realize(dev, errp); - if (env_cpu(env)->nr_threads > 1) { + if (!ppc_cpu_core_single_threaded(cs)) { env->flags |= POWERPC_FLAG_SMT; } diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c0120c8a88..f33fc36db2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -3005,18 +3005,11 @@ static void msgsnd_core_tir(CPUPPCState *env, uint32_t target_tir, int irq) { PowerPCCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - uint32_t nr_threads = cs->nr_threads; - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/ - } - - if (target_tir >= nr_threads) { - return; - } - - if (nr_threads == 1) { - ppc_set_irq(cpu, irq, 1); + if (ppc_cpu_lpar_single_threaded(cs)) { + if (target_tir == 0) { + ppc_set_irq(cpu, irq, 1); + } } else { CPUState *ccs; @@ -3071,7 +3064,7 @@ void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) brdcast = true; } - if (cs->nr_threads == 1 || !brdcast) { + if (ppc_cpu_core_single_threaded(cs) || !brdcast) { ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); return; } diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index de7c8813ec..9789d69664 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -48,9 +48,8 @@ void helper_spr_core_write_generic(CPUPPCState *env, uint32_t sprn, { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1) { + if (ppc_cpu_core_single_threaded(cs)) { env->spr[sprn] = val; return; } @@ -195,7 +194,7 @@ void helper_store_ptcr(CPUPPCState *env, target_ulong val) return; } - if (cs->nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { env->spr[SPR_PTCR] = val; tlb_flush(cs); } else { @@ -242,16 +241,12 @@ target_ulong helper_load_dpdes(CPUPPCState *env) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; target_ulong dpdes = 0; helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ - } - - if (nr_threads == 1) { + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { dpdes = 1; } @@ -278,21 +273,11 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) PowerPCCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ - } - - if (val & ~(nr_threads - 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value " - TARGET_FMT_lx"\n", val); - val &= (nr_threads - 1); /* Ignore the invalid bits */ - } - - if (nr_threads == 1) { + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1); return; } diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index b02535bbd5..d86112d60a 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -62,9 +62,8 @@ void helper_store_purr(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_purr(env, val); return; } @@ -81,9 +80,8 @@ void helper_store_tbl(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_tbl(env, val); return; } @@ -98,9 +96,8 @@ void helper_store_tbu(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_tbu(env, val); return; } @@ -140,9 +137,8 @@ void helper_store_hdecr(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_hdecr(env, val); return; } @@ -157,9 +153,8 @@ void helper_store_vtb(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_vtb(env, val); return; } @@ -174,9 +169,8 @@ void helper_store_tbu40(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + if (ppc_cpu_lpar_single_threaded(cs)) { cpu_ppc_store_tbu40(env, val); return; } @@ -293,7 +287,7 @@ static void write_tfmr(CPUPPCState *env, target_ulong val) { CPUState *cs = env_cpu(env); - if (cs->nr_threads == 1) { + if (ppc_cpu_core_single_threaded(cs)) { env->spr[SPR_TFMR] = val; } else { CPUState *ccs; From 59c921f2297d6e293fde593432acf90d819e4d51 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 18 Jun 2024 12:56:53 +1000 Subject: [PATCH 2505/2884] ppc: Add has_smt_siblings property to CPUPPCState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The decision to branch out to a slower SMT path in instruction emulation will become a bit more complicated with the way that "big-core" topology that will be implemented in subsequent changes. Hide these details from the wider CPU emulation code with a bool has_smt_siblings flag that can be set by machine initialisation. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_core.c | 3 +++ hw/ppc/spapr_cpu_core.c | 12 +++++++++--- target/ppc/cpu.h | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 7bda29b9c7..8cfa94fbfa 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -288,6 +288,9 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) cpu = POWERPC_CPU(obj); pc->threads[i] = POWERPC_CPU(obj); + if (cc->nr_threads > 1) { + cpu->env.has_smt_siblings = true; + } snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj); diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index b228c1d498..56090abcd1 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -349,9 +349,15 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) qemu_register_reset(spapr_cpu_core_reset_handler, sc); sc->threads = g_new0(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - sc->threads[i] = spapr_create_vcpu(sc, i, errp); - if (!sc->threads[i] || - !spapr_realize_vcpu(sc->threads[i], spapr, sc, i, errp)) { + PowerPCCPU *cpu; + + cpu = spapr_create_vcpu(sc, i, errp); + sc->threads[i] = cpu; + if (cpu && cc->nr_threads > 1) { + cpu->env.has_smt_siblings = true; + } + + if (!cpu || !spapr_realize_vcpu(cpu, spapr, sc, i, errp)) { spapr_cpu_core_unrealize(dev); return; } diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 417b284318..321ed2da75 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1248,6 +1248,7 @@ struct CPUArchState { int access_type; /* For SMT processors */ + bool has_smt_siblings; int core_index; #if !defined(CONFIG_USER_ONLY) @@ -1514,7 +1515,7 @@ struct PowerPCCPUClass { static inline bool ppc_cpu_core_single_threaded(CPUState *cs) { - return cs->nr_threads == 1; + return !POWERPC_CPU(cs)->env.has_smt_siblings; } static inline bool ppc_cpu_lpar_single_threaded(CPUState *cs) From c26504afd5f5cca1addfab5222621bc32a28522f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Thu, 16 May 2024 15:25:12 +1000 Subject: [PATCH 2506/2884] ppc/pnv: Add a big-core mode that joins two regular cores POWER9 and POWER10 machines come in two variants, big-core and small-core. Big-core machines are SMT8 from software's point of view, but the low level platform topology ("xscom registers and pervasive addressing"), these look more like a pair of small cores ganged together. Presently the way this is modelled is to create one SMT8 PnvCore and add special cases to xscom and pervasive for big-core mode that tries to split this into two small cores, but this is becoming too complicated to manage. A better approach is to create 2 core structures and ganging them together to look like an SMT8 core in TCG. Then the xscom and pervasive models mostly do not need to differentiate big and small core modes. This change adds initial mode bits and QEMU topology handling to split SMT8 cores into 2xSMT4 cores. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 80 ++++++++++++++++++++++++++++++++------- hw/ppc/pnv_core.c | 8 +++- include/hw/ppc/pnv.h | 2 + include/hw/ppc/pnv_chip.h | 1 + include/hw/ppc/pnv_core.h | 1 + 5 files changed, 78 insertions(+), 14 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 8827f729b1..07a29411a6 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1002,14 +1002,39 @@ static void pnv_init(MachineState *machine) pnv->num_chips = machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); + if (pnv->big_core) { + if (machine->smp.threads % 2 == 1) { + error_report("Cannot support %d threads with big-core option " + "because it must be an even number", + machine->smp.threads); + exit(1); + } + max_smt_threads *= 2; + } + if (machine->smp.threads > max_smt_threads) { error_report("Cannot support more than %d threads/core " "on %s machine", max_smt_threads, mc->desc); + if (pmc->max_smt_threads == 4) { + error_report("(use big-core=on for 8 threads per core)"); + } exit(1); } + if (pnv->big_core) { + /* + * powernv models PnvCore as a SMT4 core. Big-core requires 2xPnvCore + * per core, so adjust topology here. pnv_dt_core() processor + * device-tree and TCG SMT code make the 2 cores appear as one big core + * from software point of view. pnv pervasive models and xscoms tend to + * see the big core as 2 small core halves. + */ + machine->smp.cores *= 2; + machine->smp.threads /= 2; + } + if (!is_power_of_2(machine->smp.threads)) { - error_report("Cannot support %d threads/core on a powernv" + error_report("Cannot support %d threads/core on a powernv " "machine because it must be a power of 2", machine->smp.threads); exit(1); @@ -1048,6 +1073,8 @@ static void pnv_init(MachineState *machine) &error_fatal); object_property_set_int(chip, "nr-threads", machine->smp.threads, &error_fatal); + object_property_set_bool(chip, "big-core", pnv->big_core, + &error_fatal); /* * The POWER8 machine use the XICS interrupt interface. * Propagate the XICS fabric to the chip and its controllers. @@ -1175,11 +1202,17 @@ static void pnv_get_pir_tir_p9(PnvChip *chip, uint32_t core_id, uint32_t thread_id, uint32_t *pir, uint32_t *tir) { - if (pir) { - if (chip->nr_threads == 8) { - *pir = (chip->chip_id << 8) | ((thread_id & 1) << 2) | - (core_id << 3) | (thread_id >> 1); - } else { + if (chip->big_core) { + /* Big-core interleaves thread ID between small-cores */ + thread_id <<= 1; + thread_id |= core_id & 1; + core_id >>= 1; + + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 3) | thread_id; + } + } else { + if (pir) { *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; } } @@ -1203,11 +1236,17 @@ static void pnv_get_pir_tir_p10(PnvChip *chip, uint32_t core_id, uint32_t thread_id, uint32_t *pir, uint32_t *tir) { - if (pir) { - if (chip->nr_threads == 8) { - *pir = (chip->chip_id << 8) | ((core_id / 4) << 4) | - ((core_id % 2) << 3) | thread_id; - } else { + if (chip->big_core) { + /* Big-core interleaves thread ID between small-cores */ + thread_id <<= 1; + thread_id |= core_id & 1; + core_id >>= 1; + + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 3) | thread_id; + } + } else { + if (pir) { *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; } } @@ -2180,7 +2219,8 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) +static void pnv_chip_core_sanitize(PnvMachineState *pnv, PnvChip *chip, + Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); int cores_max; @@ -2201,6 +2241,17 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) } chip->cores_mask &= pcc->cores_mask; + /* Ensure small-cores a paired up in big-core mode */ + if (pnv->big_core) { + uint64_t even_cores = chip->cores_mask & 0x5555555555555555ULL; + uint64_t odd_cores = chip->cores_mask & 0xaaaaaaaaaaaaaaaaULL; + + if (even_cores ^ (odd_cores >> 1)) { + error_setg(errp, "warning: unpaired cores in big-core mode !"); + return; + } + } + /* now that we have a sane layout, let check the number of cores */ cores_max = ctpop64(chip->cores_mask); if (chip->nr_cores > cores_max) { @@ -2224,7 +2275,7 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) } /* Cores */ - pnv_chip_core_sanitize(chip, &error); + pnv_chip_core_sanitize(pnv, chip, &error); if (error) { error_propagate(errp, error); return; @@ -2255,6 +2306,8 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) &error_fatal); object_property_set_int(OBJECT(pnv_core), "hrmor", pnv->fw_load_addr, &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "big-core", chip->big_core, + &error_fatal); object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip), &error_abort); qdev_realize(DEVICE(pnv_core), NULL, &error_fatal); @@ -2288,6 +2341,7 @@ static Property pnv_chip_properties[] = { DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), + DEFINE_PROP_BOOL("big-core", PnvChip, big_core, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 8cfa94fbfa..6dc05534d7 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -249,7 +249,12 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, pir_spr->default_value = pir; tir_spr->default_value = tir; - env->core_index = core_hwid; + if (pc->big_core) { + /* 2 "small cores" get the same core index for SMT operations */ + env->core_index = core_hwid >> 1; + } else { + env->core_index = core_hwid; + } /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); @@ -354,6 +359,7 @@ static void pnv_core_unrealize(DeviceState *dev) static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0), DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0), + DEFINE_PROP_BOOL("big-core", PnvCore, big_core, false), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 1993dededf..283ddd50e7 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -101,6 +101,8 @@ struct PnvMachineState { PnvPnor *pnor; hwaddr fw_load_addr; + + bool big_core; }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 7d5d08bcdc..69d8273efe 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -27,6 +27,7 @@ struct PnvChip { uint64_t ram_start; uint64_t ram_size; + bool big_core; uint32_t nr_cores; uint32_t nr_threads; uint64_t cores_mask; diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 693acb189b..50164e9e1f 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -49,6 +49,7 @@ struct PnvCore { /*< public >*/ PowerPCCPU **threads; + bool big_core; uint32_t pir; uint32_t hwid; uint64_t hrmor; From cf0eb929e59cb9074db7b197bb6782a2a47dddda Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Thu, 11 Jul 2024 19:06:14 +1000 Subject: [PATCH 2507/2884] ppc/pnv: Add allow for big-core differences in DT generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit device-tree building needs to account for big-core mode, because it is driven by qemu cores (small cores). Every second core should be skipped, and every core should describe threads for both small-cores that make up the big core. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 07a29411a6..a1c2cbbc3f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -141,9 +141,9 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); PnvChipClass *pnv_cc = PNV_CHIP_GET_CLASS(chip); - g_autofree uint32_t *servers_prop = g_new(uint32_t, smt_threads); + uint32_t *servers_prop; int i; - uint32_t pir; + uint32_t pir, tir; uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; uint32_t tbfreq = PNV_TIMEBASE_FREQ; @@ -154,7 +154,10 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) char *nodename; int cpus_offset = get_cpus_node(fdt); - pnv_cc->get_pir_tir(chip, pc->hwid, 0, &pir, NULL); + pnv_cc->get_pir_tir(chip, pc->hwid, 0, &pir, &tir); + + /* Only one DT node per (big) core */ + g_assert(tir == 0); nodename = g_strdup_printf("%s@%x", dc->fw_name, pir); offset = fdt_add_subnode(fdt, cpus_offset, nodename); @@ -235,12 +238,28 @@ static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) } /* Build interrupt servers properties */ - for (i = 0; i < smt_threads; i++) { - pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); - servers_prop[i] = cpu_to_be32(pir); + if (pc->big_core) { + servers_prop = g_new(uint32_t, smt_threads * 2); + for (i = 0; i < smt_threads; i++) { + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i * 2] = cpu_to_be32(pir); + + pnv_cc->get_pir_tir(chip, pc->hwid + 1, i, &pir, NULL); + servers_prop[i * 2 + 1] = cpu_to_be32(pir); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(*servers_prop) * smt_threads + * 2))); + } else { + servers_prop = g_new(uint32_t, smt_threads); + for (i = 0; i < smt_threads; i++) { + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i] = cpu_to_be32(pir); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(*servers_prop) * smt_threads))); } - _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", - servers_prop, sizeof(*servers_prop) * smt_threads))); + g_free(servers_prop); return offset; } @@ -389,6 +408,10 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features_300, sizeof(pa_features_300)))); + + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { @@ -450,6 +473,10 @@ static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features_31, sizeof(pa_features_31)))); + + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { From 27f61d1b0b708b4659894cd0677f65ebed6eaa0b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Thu, 11 Jul 2024 18:37:25 +1000 Subject: [PATCH 2508/2884] ppc/pnv: Implement big-core PVR for Power9/10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power9/10 CPUs have PVR[51] set in small-core mode and clear in big-core mode. This is used by skiboot firmware. PVR is not hypervisor-privileged but it is not so important that spapr to implement this because it's generally masked out of PVR matching code in kernels, and only used by firmware. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 6dc05534d7..43cfeaa2d4 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -58,6 +58,10 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu) env->nip = 0x10; env->msr |= MSR_HVB; /* Hypervisor mode */ env->spr[SPR_HRMOR] = pc->hrmor; + if (pc->big_core) { + /* Clear "small core" bit on Power9/10 (this is set in default PVR) */ + env->spr[SPR_PVR] &= ~PPC_BIT(51); + } hreg_compute_hflags(env); ppc_maybe_interrupt(env); From 16ffcb3401ddb991ec746de05595ba62eae45a1b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Thu, 11 Jul 2024 18:31:35 +1000 Subject: [PATCH 2509/2884] ppc/pnv: Implement Power9 CPU core thread state indirect register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power9 CPUs have a core thread state register accessible via SPRC/SPRD indirect registers. This register includes a bit for big-core mode, which skiboot requires. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/misc_helper.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 9789d69664..1b83971375 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -325,6 +325,23 @@ target_ulong helper_load_sprd(CPUPPCState *env) case 0: /* SCRATCH0-3 */ case 1: /* SCRATCH4-7 */ return pc->scratch[(sprc >> 3) & 0x7]; + + case 0x1e0: /* core thread state */ + if (env->excp_model == POWERPC_EXCP_POWER9) { + /* + * Only implement for POWER9 because skiboot uses it to check + * big-core mode. Other bits are unimplemented so we would + * prefer to get unimplemented message on POWER10 if it were + * used anywhere. + */ + if (pc->big_core) { + return PPC_BIT(63); + } else { + return 0; + } + } + /* fallthru */ + default: qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" TARGET_FMT_lx"\n", sprc); From 78be3218940c0902d165f42ad0cdcd38e66c5df2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 18 Jun 2024 13:09:54 +1000 Subject: [PATCH 2510/2884] ppc/pnv: Add POWER10 ChipTOD quirk for big-core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POWER10 has a quirk in its ChipTOD addressing that requires the even small-core to be selected even when programming the odd small-core. This allows skiboot chiptod init to run in big-core mode. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 7 ++++++- hw/ppc/pnv_core.c | 2 ++ include/hw/ppc/pnv.h | 1 + include/hw/ppc/pnv_core.h | 7 +++++++ target/ppc/timebase_helper.c | 9 +++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a1c2cbbc3f..4605a49d28 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2290,11 +2290,12 @@ static void pnv_chip_core_sanitize(PnvMachineState *pnv, PnvChip *chip, static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(pnv); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); int i, core_hwid; - PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); if (!object_class_by_name(typename)) { error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename); @@ -2335,8 +2336,11 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) &error_fatal); object_property_set_bool(OBJECT(pnv_core), "big-core", chip->big_core, &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "quirk-tb-big-core", + pmc->quirk_tb_big_core, &error_fatal); object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip), &error_abort); + qdev_realize(DEVICE(pnv_core), NULL, &error_fatal); /* Each core has an XSCOM MMIO region */ @@ -2650,6 +2654,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->max_smt_threads = 4; + pmc->quirk_tb_big_core = true; pmc->dt_power_mgt = pnv_dt_power_mgt; xfc->match_nvt = pnv10_xive_match_nvt; diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 43cfeaa2d4..1783795b23 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -364,6 +364,8 @@ static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0), DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0), DEFINE_PROP_BOOL("big-core", PnvCore, big_core, false), + DEFINE_PROP_BOOL("quirk-tb-big-core", PnvCore, tod_state.big_core_quirk, + false), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 283ddd50e7..c56d152889 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -77,6 +77,7 @@ struct PnvMachineClass { const char *compat; int compat_size; int max_smt_threads; + bool quirk_tb_big_core; void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); void (*i2c_init)(PnvMachineState *pnv); diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 50164e9e1f..c8784777a4 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -27,6 +27,13 @@ /* Per-core ChipTOD / TimeBase state */ typedef struct PnvCoreTODState { + /* + * POWER10 DD2.0 - big core TFMR drives the state machine on the even + * small core. Skiboot has a workaround that targets the even small core + * for CHIPTOD_TO_TB ops. + */ + bool big_core_quirk; + int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index d86112d60a..73120323b4 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -218,6 +218,7 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) * target/ppc/pnv_helper.c */ #include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_chip.h" /* * POWER processor Timebase Facility */ @@ -302,6 +303,14 @@ static PnvCoreTODState *cpu_get_tbst(PowerPCCPU *cpu) { PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + if (pc->big_core && pc->tod_state.big_core_quirk) { + /* Must operate on the even small core */ + int core_id = CPU_CORE(pc)->core_id; + if (core_id & 1) { + pc = pc->chip->cores[core_id & ~1]; + } + } + return &pc->tod_state; } From b1beb69231c8a6a04ec365614e67729ea9af7cbf Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 18 Jun 2024 12:45:34 +1000 Subject: [PATCH 2511/2884] ppc/pnv: Add big-core machine property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Big-core implementation is complete, so expose it as a machine property that may be set with big-core=on option on powernv9 and powernv10 machines. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 61 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4605a49d28..d19516c2d7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2581,6 +2581,34 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, return total_count; } +static bool pnv_machine_get_big_core(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + return pnv->big_core; +} + +static void pnv_machine_set_big_core(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + pnv->big_core = value; +} + +static bool pnv_machine_get_hb(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + return !!pnv->fw_load_addr; +} + +static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + if (value) { + pnv->fw_load_addr = 0x8000000; + } +} + static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -2632,6 +2660,12 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) pmc->dt_power_mgt = pnv_dt_power_mgt; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); + + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); } static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) @@ -2668,6 +2702,17 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) pnv_machine_p10_common_class_init(oc, data); mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; + + /* + * This is the parent of POWER10 Rainier class, so properies go here + * rather than common init (which would add them to both parent and + * child which is invalid). + */ + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); } static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) @@ -2680,22 +2725,6 @@ static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) pmc->i2c_init = pnv_rainier_i2c_init; } -static bool pnv_machine_get_hb(Object *obj, Error **errp) -{ - PnvMachineState *pnv = PNV_MACHINE(obj); - - return !!pnv->fw_load_addr; -} - -static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) -{ - PnvMachineState *pnv = PNV_MACHINE(obj); - - if (value) { - pnv->fw_load_addr = 0x8000000; - } -} - static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) { CPUPPCState *env = cpu_env(cs); From ca4f47752a14221a26cd2bf4710bb21ad2811a22 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 12 Jul 2024 13:16:44 +1000 Subject: [PATCH 2512/2884] ppc/pnv: Add a CPU nmi and resume function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power CPUs have an execution control facility that can pause, resume, and cause NMIs, among other things. Add a function that will nmi a CPU and resume it if it was paused, in preparation for implementing the control facility. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 14 +++++++++++++- include/hw/ppc/pnv.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index d19516c2d7..084cfb9984 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2751,11 +2751,23 @@ static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) */ env->spr[SPR_SRR1] |= SRR1_WAKESCOM; } + if (arg.host_int == 1) { + cpu_resume(cs); + } +} + +/* + * Send a SRESET (NMI) interrupt to the CPU, and resume execution if it was + * paused. + */ +void pnv_cpu_do_nmi_resume(CPUState *cs) +{ + async_run_on_cpu(cs, pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(1)); } static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) { - async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); + async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(0)); } static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index c56d152889..b7858d310d 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -112,6 +112,8 @@ PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb); #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL +void pnv_cpu_do_nmi_resume(CPUState *cs); + /* * BMC helpers */ From c8891955086b2fa795efb7fa0e409e32f25e5447 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Thu, 16 May 2024 23:44:12 +1000 Subject: [PATCH 2513/2884] ppc/pnv: Implement POWER10 PC xscom registers for direct controls The PC unit in the processor core contains xscom registers that provide low level status and control of the CPU. This implements "direct controls", sufficient for skiboot firmware, which uses it to send NMI IPIs between CPUs. POWER10 is sufficiently different from POWER9 (particularly with respect to QME and special wakeup) that it is not trivial to implement POWER9 support by reusing the code. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv_core.c | 89 ++++++++++++++++++++++++++++++++++++--- include/hw/ppc/pnv_core.h | 3 ++ 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 1783795b23..4484fe8c6a 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -185,16 +185,40 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = { */ #define PNV10_XSCOM_EC_CORE_THREAD_STATE 0x412 +#define PNV10_XSCOM_EC_CORE_THREAD_INFO 0x413 +#define PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS 0x449 +#define PNV10_XSCOM_EC_CORE_RAS_STATUS 0x454 static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, unsigned int width) { + PnvCore *pc = PNV_CORE(opaque); + int nr_threads = CPU_CORE(pc)->nr_threads; + int i; uint32_t offset = addr >> 3; uint64_t val = 0; switch (offset) { case PNV10_XSCOM_EC_CORE_THREAD_STATE: - val = 0; + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + + if (cs->halted) { + val |= PPC_BIT(56 + i); + } + } + break; + case PNV10_XSCOM_EC_CORE_THREAD_INFO: + break; + case PNV10_XSCOM_EC_CORE_RAS_STATUS: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + if (cs->stopped) { + val |= PPC_BIT(0 + 8 * i) | PPC_BIT(1 + 8 * i); + } + } break; default: qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, @@ -207,9 +231,46 @@ static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { + PnvCore *pc = PNV_CORE(opaque); + int nr_threads = CPU_CORE(pc)->nr_threads; + int i; uint32_t offset = addr >> 3; switch (offset) { + case PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + + if (val & PPC_BIT(7 + 8 * i)) { /* stop */ + val &= ~PPC_BIT(7 + 8 * i); + cpu_pause(cs); + } + if (val & PPC_BIT(6 + 8 * i)) { /* start */ + val &= ~PPC_BIT(6 + 8 * i); + cpu_resume(cs); + } + if (val & PPC_BIT(4 + 8 * i)) { /* sreset */ + val &= ~PPC_BIT(4 + 8 * i); + pnv_cpu_do_nmi_resume(cs); + } + if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */ + /* + * Hardware has very particular cases for where clear maint + * must be used and where start must be used to resume a + * thread. These are not modelled exactly, just treat + * this and start the same. + */ + val &= ~PPC_BIT(3 + 8 * i); + cpu_resume(cs); + } + } + if (val) { + qemu_log_mask(LOG_UNIMP, "%s: unimp bits in DIRECT_CONTROLS " + "0x%016" PRIx64 "\n", __func__, val); + } + break; + default: qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, offset); @@ -526,6 +587,7 @@ static const MemoryRegionOps pnv_quad_power10_xscom_ops = { static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, unsigned int width) { + PnvQuad *eq = PNV_QUAD(opaque); uint32_t offset = addr >> 3; uint64_t val = -1; @@ -533,10 +595,14 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, * Forth nibble selects the core within a quad, mask it to process read * for any core. */ - switch (offset & ~0xf000) { - case P10_QME_SPWU_HYP: + switch (offset & ~PPC_BITMASK32(16, 19)) { case P10_QME_SSH_HYP: - return 0; + val = 0; + if (eq->special_wakeup_done) { + val |= PPC_BIT(1); /* SPWU DONE */ + val |= PPC_BIT(4); /* SSH SPWU DONE */ + } + break; default: qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, offset); @@ -548,9 +614,22 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, static void pnv_qme_power10_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { + PnvQuad *eq = PNV_QUAD(opaque); uint32_t offset = addr >> 3; + bool set; + int i; - switch (offset) { + switch (offset & ~PPC_BITMASK32(16, 19)) { + case P10_QME_SPWU_HYP: + set = !!(val & PPC_BIT(0)); + eq->special_wakeup_done = set; + for (i = 0; i < 4; i++) { + /* These bits select cores in the quad */ + if (offset & PPC_BIT32(16 + i)) { + eq->special_wakeup[i] = set; + } + } + break; default: qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, offset); diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index c8784777a4..1de79a818e 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -109,6 +109,9 @@ OBJECT_DECLARE_TYPE(PnvQuad, PnvQuadClass, PNV_QUAD) struct PnvQuad { DeviceState parent_obj; + bool special_wakeup_done; + bool special_wakeup[4]; + uint32_t quad_id; MemoryRegion xscom_regs; MemoryRegion xscom_qme_regs; From 3b5ea01e98a5e26c1adb13d966f334cb58680cf8 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Fri, 24 May 2024 15:02:46 +1000 Subject: [PATCH 2514/2884] ppc/pnv: Add an LPAR per core machine option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent POWER CPUs can operate in "LPAR per core" or "LPAR per thread" modes. In per-core mode, some SPRs and IPI doorbells are shared between threads in a core. In per-thread mode, supervisor and user state is not shared between threads. OpenPOWER systems after POWER8 use LPAR per thread mode, and it is required for KVM. Enterprise systems use LPAR per core mode, as they partition the machine by core. Implement a lpar-per-core machine option for powernv machines. This is fixed true for POWER8 machines, and defaults off for P9 and P10. With this change, powernv8 SMT now works sufficiently to run Linux, with a single socket. Multi-threaded KVM guests still have problems, as does multi-socket Linux boot. Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 38 ++++++++++++++++++++++++++++++++++++++ hw/ppc/pnv_core.c | 8 ++++++++ include/hw/ppc/pnv.h | 2 ++ include/hw/ppc/pnv_chip.h | 1 + include/hw/ppc/pnv_core.h | 1 + target/ppc/cpu_init.c | 3 ++- 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 084cfb9984..a3560d25b7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1026,6 +1026,11 @@ static void pnv_init(MachineState *machine) exit(1); } + /* Set lpar-per-core mode if lpar-per-thread is not supported */ + if (!pmc->has_lpar_per_thread) { + pnv->lpar_per_core = true; + } + pnv->num_chips = machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); @@ -1102,6 +1107,8 @@ static void pnv_init(MachineState *machine) &error_fatal); object_property_set_bool(chip, "big-core", pnv->big_core, &error_fatal); + object_property_set_bool(chip, "lpar-per-core", pnv->lpar_per_core, + &error_fatal); /* * The POWER8 machine use the XICS interrupt interface. * Propagate the XICS fabric to the chip and its controllers. @@ -2338,6 +2345,8 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) &error_fatal); object_property_set_bool(OBJECT(pnv_core), "quirk-tb-big-core", pmc->quirk_tb_big_core, &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "lpar-per-core", + chip->lpar_per_core, &error_fatal); object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip), &error_abort); @@ -2373,6 +2382,7 @@ static Property pnv_chip_properties[] = { DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), DEFINE_PROP_BOOL("big-core", PnvChip, big_core, false), + DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), DEFINE_PROP_END_OF_LIST(), }; @@ -2593,6 +2603,18 @@ static void pnv_machine_set_big_core(Object *obj, bool value, Error **errp) pnv->big_core = value; } +static bool pnv_machine_get_lpar_per_core(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + return pnv->lpar_per_core; +} + +static void pnv_machine_set_lpar_per_core(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + pnv->lpar_per_core = value; +} + static bool pnv_machine_get_hb(Object *obj, Error **errp) { PnvMachineState *pnv = PNV_MACHINE(obj); @@ -2632,6 +2654,8 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->max_smt_threads = 8; + /* POWER8 is always lpar-per-core mode */ + pmc->has_lpar_per_thread = false; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } @@ -2657,6 +2681,7 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; pmc->dt_power_mgt = pnv_dt_power_mgt; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); @@ -2666,6 +2691,12 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) pnv_machine_set_big_core); object_class_property_set_description(oc, "big-core", "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); } static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) @@ -2688,6 +2719,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; pmc->quirk_tb_big_core = true; pmc->dt_power_mgt = pnv_dt_power_mgt; @@ -2713,6 +2745,12 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) pnv_machine_set_big_core); object_class_property_set_description(oc, "big-core", "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); } static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 4484fe8c6a..a30693990b 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -208,6 +208,9 @@ static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, val |= PPC_BIT(56 + i); } } + if (pc->lpar_per_core) { + val |= PPC_BIT(62); + } break; case PNV10_XSCOM_EC_CORE_THREAD_INFO: break; @@ -321,6 +324,10 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, env->core_index = core_hwid; } + if (pc->lpar_per_core) { + cpu_ppc_set_1lpar(cpu); + } + /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); } @@ -427,6 +434,7 @@ static Property pnv_core_properties[] = { DEFINE_PROP_BOOL("big-core", PnvCore, big_core, false), DEFINE_PROP_BOOL("quirk-tb-big-core", PnvCore, tod_state.big_core_quirk, false), + DEFINE_PROP_BOOL("lpar-per-core", PnvCore, lpar_per_core, false), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index b7858d310d..fcb6699150 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -77,6 +77,7 @@ struct PnvMachineClass { const char *compat; int compat_size; int max_smt_threads; + bool has_lpar_per_thread; bool quirk_tb_big_core; void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); @@ -104,6 +105,7 @@ struct PnvMachineState { hwaddr fw_load_addr; bool big_core; + bool lpar_per_core; }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 69d8273efe..ee1649babc 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -28,6 +28,7 @@ struct PnvChip { uint64_t ram_size; bool big_core; + bool lpar_per_core; uint32_t nr_cores; uint32_t nr_threads; uint64_t cores_mask; diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 1de79a818e..d8afb4f95f 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -57,6 +57,7 @@ struct PnvCore { /*< public >*/ PowerPCCPU **threads; bool big_core; + bool lpar_per_core; uint32_t pir; uint32_t hwid; uint64_t hrmor; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 5ec87c56e4..23881d09e9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6786,7 +6786,8 @@ void cpu_ppc_set_1lpar(PowerPCCPU *cpu) /* * pseries SMT means "LPAR per core" mode, e.g., msgsndp is usable - * between threads. + * between threads. powernv be in either mode, and it mostly affects + * supervisor visible registers and instructions. */ if (env->flags & POWERPC_FLAG_SMT) { env->flags |= POWERPC_FLAG_SMT_1LPAR; From 117664a1e70e6adff1e4384702a9d8343597b5b9 Mon Sep 17 00:00:00 2001 From: Chalapathi V <chalapathi.v@linux.ibm.com> Date: Wed, 26 Jun 2024 04:05:23 -0500 Subject: [PATCH 2515/2884] ppc/pnv: Remove ppc target dependency from pnv_xscom.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit target specific dependency from include/hw/ppc/pnv_xscom.h has been removed so that pnv_xscom.h can be included outside hw/ppc. Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Caleb Schlossin <calebs@linux.vnet.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- include/hw/ppc/pnv_xscom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index e93d310e79..764b324a00 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -21,9 +21,9 @@ #define PPC_PNV_XSCOM_H #include "exec/memory.h" -#include "hw/ppc/pnv.h" typedef struct PnvXScomInterface PnvXScomInterface; +typedef struct PnvChip PnvChip; #define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" #define PNV_XSCOM_INTERFACE(obj) \ From 29318db133d0b2523bda771f76aa50c08842527f Mon Sep 17 00:00:00 2001 From: Chalapathi V <chalapathi.v@linux.ibm.com> Date: Wed, 26 Jun 2024 04:05:24 -0500 Subject: [PATCH 2516/2884] hw/ssi: Add SPI model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPI controller device model supports a connection to a single SPI responder. This provide access to SPI seeproms, TPM, flash device and an ADC controller. All SPI function control is mapped into the SPI register space to enable full control by firmware. In this commit SPI configuration component is modelled which contains all SPI configuration and status registers as well as the hold registers for data to be sent or having been received. An existing QEMU SSI framework is used and SSI_BUS is created. Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com> Reviewed-by: Caleb Schlossin <calebs@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> [np: Fix FDT macro compile for qtest] Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/Kconfig | 3 + hw/ssi/Kconfig | 4 + hw/ssi/meson.build | 1 + hw/ssi/pnv_spi.c | 215 ++++++++++++++++++++++++++++++++++ hw/ssi/trace-events | 6 + include/hw/ppc/pnv_xscom.h | 3 + include/hw/ssi/pnv_spi.h | 40 +++++++ include/hw/ssi/pnv_spi_regs.h | 67 +++++++++++ 8 files changed, 339 insertions(+) create mode 100644 hw/ssi/pnv_spi.c create mode 100644 include/hw/ssi/pnv_spi.h create mode 100644 include/hw/ssi/pnv_spi_regs.h diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 347212f4db..c235519881 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -39,6 +39,9 @@ config POWERNV select PCI_POWERNV select PCA9552 select PCA9554 + select SSI + select SSI_M25P80 + select PNV_SPI config PPC405 bool diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 83ee53c1d0..8d180de7cf 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -24,3 +24,7 @@ config STM32F2XX_SPI config BCM2835_SPI bool select SSI + +config PNV_SPI + bool + select SSI diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index b999aeb027..b7ad7fca3b 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) +system_ss.add(when: 'CONFIG_PNV_SPI', if_true: files('pnv_spi.c')) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c new file mode 100644 index 0000000000..468afdad07 --- /dev/null +++ b/hw/ssi/pnv_spi.c @@ -0,0 +1,215 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ssi/pnv_spi.h" +#include "hw/ssi/pnv_spi_regs.h" +#include "hw/ssi/ssi.h" +#include <libfdt.h> +#include "hw/irq.h" +#include "trace.h" + +/* + * Macro from include/hw/ppc/fdt.h + * fdt.h cannot be included here as it contain ppc target specific dependency. + */ +#define _FDT(exp) \ + do { \ + int _ret = (exp); \ + if (_ret < 0) { \ + qemu_log_mask(LOG_GUEST_ERROR, \ + "error creating device tree: %s: %s", \ + #exp, fdt_strerror(_ret)); \ + exit(1); \ + } \ + } while (0) + +static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvSpi *s = PNV_SPI(opaque); + uint32_t reg = addr >> 3; + uint64_t val = ~0ull; + + switch (reg) { + case ERROR_REG: + case SPI_CTR_CFG_REG: + case CONFIG_REG1: + case SPI_CLK_CFG_REG: + case SPI_MM_REG: + case SPI_XMIT_DATA_REG: + val = s->regs[reg]; + break; + case SPI_RCV_DATA_REG: + val = s->regs[reg]; + trace_pnv_spi_read_RDR(val); + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + break; + case SPI_SEQ_OP_REG: + val = 0; + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { + val = (val << 8) | s->seq_op[i]; + } + break; + case SPI_STS_REG: + val = s->status; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " + "read at 0x%" PRIx32 "\n", reg); + } + + trace_pnv_spi_read(addr, val); + return val; +} + +static void pnv_spi_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvSpi *s = PNV_SPI(opaque); + uint32_t reg = addr >> 3; + + trace_pnv_spi_write(addr, val); + + switch (reg) { + case ERROR_REG: + case SPI_CTR_CFG_REG: + case CONFIG_REG1: + case SPI_MM_REG: + case SPI_RCV_DATA_REG: + s->regs[reg] = val; + break; + case SPI_CLK_CFG_REG: + /* + * To reset the SPI controller write the sequence 0x5 0xA to + * reset_control field + */ + if ((GETFIELD(SPI_CLK_CFG_RST_CTRL, s->regs[SPI_CLK_CFG_REG]) == 0x5) + && (GETFIELD(SPI_CLK_CFG_RST_CTRL, val) == 0xA)) { + /* SPI controller reset sequence completed, resetting */ + s->regs[reg] = SPI_CLK_CFG_HARD_RST; + } else { + s->regs[reg] = val; + } + break; + case SPI_XMIT_DATA_REG: + /* + * Writing to the transmit data register causes the transmit data + * register full status bit in the status register to be set. Writing + * when the transmit data register full status bit is already set + * causes a "Resource Not Available" condition. This is not possible + * in the model since writes to this register are not asynchronous to + * the operation sequence like it would be in hardware. + */ + s->regs[reg] = val; + trace_pnv_spi_write_TDR(val); + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1); + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0); + break; + case SPI_SEQ_OP_REG: + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { + s->seq_op[i] = (val >> (56 - i * 8)) & 0xFF; + } + break; + case SPI_STS_REG: + /* other fields are ignore_write */ + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, + GETFIELD(SPI_STS_RDR, val)); + s->status = SETFIELD(SPI_STS_TDR_OVERRUN, s->status, + GETFIELD(SPI_STS_TDR, val)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " + "write at 0x%" PRIx32 "\n", reg); + } + return; +} + +static const MemoryRegionOps pnv_spi_xscom_ops = { + .read = pnv_spi_xscom_read, + .write = pnv_spi_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static Property pnv_spi_properties[] = { + DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_spi_realize(DeviceState *dev, Error **errp) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name = g_strdup_printf(TYPE_PNV_SPI_BUS ".%d", + s->spic_num); + s->ssi_bus = ssi_create_bus(dev, name); + s->cs_line = g_new0(qemu_irq, 1); + qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1); + + /* spi scoms */ + pnv_xscom_region_init(&s->xscom_spic_regs, OBJECT(s), &pnv_spi_xscom_ops, + s, "xscom-spi", PNV10_XSCOM_PIB_SPIC_SIZE); +} + +static int pnv_spi_dt_xscom(PnvXScomInterface *dev, void *fdt, + int offset) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name; + int s_offset; + const char compat[] = "ibm,power10-spi"; + uint32_t spic_pcba = PNV10_XSCOM_PIB_SPIC_BASE + + s->spic_num * PNV10_XSCOM_PIB_SPIC_SIZE; + uint32_t reg[] = { + cpu_to_be32(spic_pcba), + cpu_to_be32(PNV10_XSCOM_PIB_SPIC_SIZE) + }; + name = g_strdup_printf("pnv_spi@%x", spic_pcba); + s_offset = fdt_add_subnode(fdt, offset, name); + _FDT(s_offset); + + _FDT(fdt_setprop(fdt, s_offset, "reg", reg, sizeof(reg))); + _FDT(fdt_setprop(fdt, s_offset, "compatible", compat, sizeof(compat))); + _FDT((fdt_setprop_cell(fdt, s_offset, "spic_num#", s->spic_num))); + return 0; +} + +static void pnv_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xscomc->dt_xscom = pnv_spi_dt_xscom; + + dc->desc = "PowerNV SPI"; + dc->realize = pnv_spi_realize; + device_class_set_props(dc, pnv_spi_properties); +} + +static const TypeInfo pnv_spi_info = { + .name = TYPE_PNV_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvSpi), + .class_init = pnv_spi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_spi_register_types(void) +{ + type_register_static(&pnv_spi_info); +} + +type_init(pnv_spi_register_types); diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index 7b5ad6a939..2cc29e1284 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -32,3 +32,9 @@ ibex_spi_host_reset(const char *msg) "%s" ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32 ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:" + +#pnv_spi.c +pnv_spi_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_spi_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_spi_read_RDR(uint64_t val) "data extracted = 0x%" PRIx64 +pnv_spi_write_TDR(uint64_t val) "being written, data written = 0x%" PRIx64 diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 764b324a00..648388a599 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -200,6 +200,9 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PEC_PCI_BASE 0x8010800 /* index goes upwards ... */ #define PNV10_XSCOM_PEC_PCI_SIZE 0x200 +#define PNV10_XSCOM_PIB_SPIC_BASE 0xc0000 +#define PNV10_XSCOM_PIB_SPIC_SIZE 0x20 + void pnv_xscom_init(PnvChip *chip, uint64_t size, hwaddr addr); int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, uint64_t xscom_base, uint64_t xscom_size, diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h new file mode 100644 index 0000000000..833042b74b --- /dev/null +++ b/include/hw/ssi/pnv_spi.h @@ -0,0 +1,40 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This model Supports a connection to a single SPI responder. + * Introduced for P10 to provide access to SPI seeproms, TPM, flash device + * and an ADC controller. + */ + +#ifndef PPC_PNV_SPI_H +#define PPC_PNV_SPI_H + +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" + +#define TYPE_PNV_SPI "pnv-spi" +OBJECT_DECLARE_SIMPLE_TYPE(PnvSpi, PNV_SPI) + +#define PNV_SPI_REG_SIZE 8 +#define PNV_SPI_REGS 7 + +#define TYPE_PNV_SPI_BUS "pnv-spi-bus" +typedef struct PnvSpi { + SysBusDevice parent_obj; + + SSIBus *ssi_bus; + qemu_irq *cs_line; + MemoryRegion xscom_spic_regs; + /* SPI object number */ + uint32_t spic_num; + + /* SPI registers */ + uint64_t regs[PNV_SPI_REGS]; + uint8_t seq_op[PNV_SPI_REG_SIZE]; + uint64_t status; +} PnvSpi; +#endif /* PPC_PNV_SPI_H */ diff --git a/include/hw/ssi/pnv_spi_regs.h b/include/hw/ssi/pnv_spi_regs.h new file mode 100644 index 0000000000..5b6ff72d02 --- /dev/null +++ b/include/hw/ssi/pnv_spi_regs.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_SPI_CONTROLLER_REGS_H +#define PNV_SPI_CONTROLLER_REGS_H + +/* + * Macros from target/ppc/cpu.h + * These macros are copied from ppc target specific file target/ppc/cpu.h + * as target/ppc/cpu.h cannot be included here. + */ +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) | PPC_BIT8(bs)) +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) +#define SETFIELD(m, v, val) \ + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) + +/* Error Register */ +#define ERROR_REG 0x00 + +/* counter_config_reg */ +#define SPI_CTR_CFG_REG 0x01 + +/* config_reg */ +#define CONFIG_REG1 0x02 + +/* clock_config_reset_control_ecc_enable_reg */ +#define SPI_CLK_CFG_REG 0x03 +#define SPI_CLK_CFG_HARD_RST 0x0084000000000000; +#define SPI_CLK_CFG_RST_CTRL PPC_BITMASK(24, 27) + +/* memory_mapping_reg */ +#define SPI_MM_REG 0x04 + +/* transmit_data_reg */ +#define SPI_XMIT_DATA_REG 0x05 + +/* receive_data_reg */ +#define SPI_RCV_DATA_REG 0x06 + +/* sequencer_operation_reg */ +#define SPI_SEQ_OP_REG 0x07 + +/* status_reg */ +#define SPI_STS_REG 0x08 +#define SPI_STS_RDR_FULL PPC_BIT(0) +#define SPI_STS_RDR_OVERRUN PPC_BIT(1) +#define SPI_STS_RDR_UNDERRUN PPC_BIT(2) +#define SPI_STS_TDR_FULL PPC_BIT(4) +#define SPI_STS_TDR_OVERRUN PPC_BIT(5) +#define SPI_STS_TDR_UNDERRUN PPC_BIT(6) +#define SPI_STS_SEQ_FSM PPC_BITMASK(8, 15) +#define SPI_STS_SHIFTER_FSM PPC_BITMASK(16, 27) +#define SPI_STS_SEQ_INDEX PPC_BITMASK(28, 31) +#define SPI_STS_GEN_STATUS PPC_BITMASK(32, 63) +#define SPI_STS_RDR PPC_BITMASK(1, 3) +#define SPI_STS_TDR PPC_BITMASK(5, 7) + +#endif From b4cb930e40f172e2b28a9fbe0189e97469aad648 Mon Sep 17 00:00:00 2001 From: Chalapathi V <chalapathi.v@linux.ibm.com> Date: Wed, 26 Jun 2024 04:05:25 -0500 Subject: [PATCH 2517/2884] hw/ssi: Extend SPI model In this commit SPI shift engine and sequencer logic is implemented. Shift engine performs serialization and de-serialization according to the control by the sequencer and according to the setup defined in the configuration registers. Sequencer implements the main control logic and FSM to handle data transmit and data receive control of the shift engine. Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com> Reviewed-by: Caleb Schlossin <calebs@linux.vnet.ibm.com> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ssi/pnv_spi.c | 1045 +++++++++++++++++++++++++++++++++ hw/ssi/trace-events | 15 + include/hw/ssi/pnv_spi.h | 27 + include/hw/ssi/pnv_spi_regs.h | 68 ++- 4 files changed, 1154 insertions(+), 1 deletion(-) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 468afdad07..cdff3f9621 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -17,6 +17,9 @@ #include "hw/irq.h" #include "trace.h" +#define PNV_SPI_OPCODE_LO_NIBBLE(x) (x & 0x0F) +#define PNV_SPI_MASKED_OPCODE(x) (x & 0xF0) + /* * Macro from include/hw/ppc/fdt.h * fdt.h cannot be included here as it contain ppc target specific dependency. @@ -32,6 +35,1040 @@ } \ } while (0) +/* PnvXferBuffer */ +typedef struct PnvXferBuffer { + + uint32_t len; + uint8_t *data; + +} PnvXferBuffer; + +/* pnv_spi_xfer_buffer_methods */ +static PnvXferBuffer *pnv_spi_xfer_buffer_new(void) +{ + PnvXferBuffer *payload = g_malloc0(sizeof(*payload)); + + return payload; +} + +static void pnv_spi_xfer_buffer_free(PnvXferBuffer *payload) +{ + free(payload->data); + free(payload); +} + +static uint8_t *pnv_spi_xfer_buffer_write_ptr(PnvXferBuffer *payload, + uint32_t offset, uint32_t length) +{ + if (payload->len < (offset + length)) { + payload->len = offset + length; + payload->data = g_realloc(payload->data, payload->len); + } + return &payload->data[offset]; +} + +static bool does_rdr_match(PnvSpi *s) +{ + /* + * According to spec, the mask bits that are 0 are compared and the + * bits that are 1 are ignored. + */ + uint16_t rdr_match_mask = GETFIELD(SPI_MM_RDR_MATCH_MASK, + s->regs[SPI_MM_REG]); + uint16_t rdr_match_val = GETFIELD(SPI_MM_RDR_MATCH_VAL, + s->regs[SPI_MM_REG]); + + if ((~rdr_match_mask & rdr_match_val) == ((~rdr_match_mask) & + GETFIELD(PPC_BITMASK(48, 63), s->regs[SPI_RCV_DATA_REG]))) { + return true; + } + return false; +} + +static uint8_t get_from_offset(PnvSpi *s, uint8_t offset) +{ + uint8_t byte; + + /* + * Offset is an index between 0 and PNV_SPI_REG_SIZE - 1 + * Check the offset before using it. + */ + if (offset < PNV_SPI_REG_SIZE) { + byte = (s->regs[SPI_XMIT_DATA_REG] >> (56 - offset * 8)) & 0xFF; + } else { + /* + * Log an error and return a 0xFF since we have to assign something + * to byte before returning. + */ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid offset = %d used to get byte " + "from TDR\n", offset); + byte = 0xff; + } + return byte; +} + +static uint8_t read_from_frame(PnvSpi *s, uint8_t *read_buf, uint8_t nr_bytes, + uint8_t ecc_count, uint8_t shift_in_count) +{ + uint8_t byte; + int count = 0; + + while (count < nr_bytes) { + shift_in_count++; + if ((ecc_count != 0) && + (shift_in_count == (PNV_SPI_REG_SIZE + ecc_count))) { + shift_in_count = 0; + } else { + byte = read_buf[count]; + trace_pnv_spi_shift_rx(byte, count); + s->regs[SPI_RCV_DATA_REG] = (s->regs[SPI_RCV_DATA_REG] << 8) | byte; + } + count++; + } /* end of while */ + return shift_in_count; +} + +static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) +{ + uint8_t ecc_count; + uint8_t shift_in_count; + + /* + * Processing here must handle: + * - Which bytes in the payload we should move to the RDR + * - Explicit mode counter configuration settings + * - RDR full and RDR overrun status + */ + + /* + * First check that the response payload is the exact same + * number of bytes as the request payload was + */ + if (rsp_payload->len != (s->N1_bytes + s->N2_bytes)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in " + "bytes, expected %d, got %d\n", + (s->N1_bytes + s->N2_bytes), rsp_payload->len); + } else { + uint8_t ecc_control; + trace_pnv_spi_rx_received(rsp_payload->len); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * Adding an ECC count let's us know when we have found a payload byte + * that was shifted in but cannot be loaded into RDR. Bits 29-30 of + * clock_config_reset_control register equal to either 0b00 or 0b10 + * indicate that we are taking in data with ECC and either applying + * the ECC or discarding it. + */ + ecc_count = 0; + ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + ecc_count = 1; + } + /* + * Use the N1_rx and N2_rx counts to control shifting data from the + * payload into the RDR. Keep an overall count of the number of bytes + * shifted into RDR so we can discard every 9th byte when ECC is + * enabled. + */ + shift_in_count = 0; + /* Handle the N1 portion of the frame first */ + if (s->N1_rx != 0) { + trace_pnv_spi_rx_read_N1frame(); + shift_in_count = read_from_frame(s, &rsp_payload->data[0], + s->N1_bytes, ecc_count, shift_in_count); + } + /* Handle the N2 portion of the frame */ + if (s->N2_rx != 0) { + trace_pnv_spi_rx_read_N2frame(); + shift_in_count = read_from_frame(s, + &rsp_payload->data[s->N1_bytes], s->N2_bytes, + ecc_count, shift_in_count); + } + if ((s->N1_rx + s->N2_rx) > 0) { + /* + * Data was received so handle RDR status. + * It is easier to handle RDR_full and RDR_overrun status here + * since the RDR register's shift_byte_in method is called + * multiple times in a row. Controlling RDR status is done here + * instead of in the RDR scoped methods for that reason. + */ + if (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1) { + /* + * Data was shifted into the RDR before having been read + * causing previous data to have been overrun. + */ + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, 1); + } else { + /* + * Set status to indicate that the received data register is + * full. This flag is only cleared once the RDR is unloaded. + */ + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 1); + } + } + } /* end of else */ +} /* end of spi_response() */ + +static void transfer(PnvSpi *s, PnvXferBuffer *payload) +{ + uint32_t tx; + uint32_t rx; + PnvXferBuffer *rsp_payload = NULL; + + rsp_payload = pnv_spi_xfer_buffer_new(); + for (int offset = 0; offset < payload->len; offset += s->transfer_len) { + tx = 0; + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload->len) { + tx <<= 8; + } else { + tx = (tx << 8) | payload->data[offset + i]; + } + } + rx = ssi_transfer(s->ssi_bus, tx); + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload->len) { + break; + } + *(pnv_spi_xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) = + (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF; + } + } + if (rsp_payload != NULL) { + spi_response(s, s->N1_bits, rsp_payload); + } +} + +static inline uint8_t get_seq_index(PnvSpi *s) +{ + return GETFIELD(SPI_STS_SEQ_INDEX, s->status); +} + +static inline void next_sequencer_fsm(PnvSpi *s) +{ + uint8_t seq_index = get_seq_index(s); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, (seq_index + 1)); + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); +} + +/* + * Calculate the N1 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N1 opcode + * and doesn't test it. + * The counters returned are: + * N1 bits: Number of bits in the payload data that are significant + * to the responder. + * N1_bytes: Total count of payload bytes for the N1 (portion of the) frame. + * N1_tx: Total number of bytes taken from TDR for N1 + * N1_rx: Total number of bytes taken from the payload for N1 + */ +static void calculate_N1(PnvSpi *s, uint8_t opcode) +{ + /* + * Shift_N1 opcode form: 0x3M + * Implicit mode: + * If M != 0 the shift count is M bytes and M is the number of tx bytes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count control + * register fields. Note that we only check for forced Implicit mode when + * M != 0 since the mode doesn't make sense when M = 0. + * Explicit mode: + * If M == 0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) { + /* Explicit mode */ + s->N1_bits = GETFIELD(SPI_CTR_CFG_N1, s->regs[SPI_CTR_CFG_REG]); + s->N1_bytes = (s->N1_bits + 7) / 8; + s->N1_tx = 0; + s->N1_rx = 0; + /* If tx count control for N1 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_tx = s->N1_bytes; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_rx = s->N1_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + s->N1_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->N1_bits = s->N1_bytes * 8; + /* + * Assume that we are going to transmit the count + * (pure Implicit only) + */ + s->N1_tx = s->N1_bytes; + s->N1_rx = 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate transmit then reset the tx count to 0 + */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, + s->regs[SPI_CTR_CFG_REG]) == 0) { + s->N1_tx = 0; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, + s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_rx = s->N1_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the known size + * of the shift register, 64 bits or 72 bits if ECC is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM + * error bit. + */ + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, + s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + if (s->N1_bytes > (PNV_SPI_REG_SIZE + 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when " + "ECC enabled, bytes = 0x%x, bits = 0x%x\n", + s->N1_bytes, s->N1_bits); + s->N1_bytes = PNV_SPI_REG_SIZE + 1; + s->N1_bits = s->N1_bytes * 8; + } + } else if (s->N1_bytes > PNV_SPI_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, " + "bytes = 0x%x, bits = 0x%x\n", + s->N1_bytes, s->N1_bits); + s->N1_bytes = PNV_SPI_REG_SIZE; + s->N1_bits = s->N1_bytes * 8; + } +} /* end of calculate_N1 */ + +/* + * Shift_N1 operation handler method + */ +static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, + PnvXferBuffer **payload, bool send_n1_alone) +{ + uint8_t n1_count; + bool stop = false; + + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload == NULL) { + *payload = pnv_spi_xfer_buffer_new(); + } + /* + * Use a combination of N1 counters to build the N1 portion of the + * transmit payload. + * We only care about transmit at this time since the request payload + * only represents data going out on the controller output line. + * Leave mode specific considerations in the calculate function since + * all we really care about are counters that tell use exactly how + * many bytes are in the payload and how many of those bytes to + * include from the TDR into the payload. + */ + calculate_N1(s, opcode); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * Zero out the N2 counters here in case there is no N2 operation following + * the N1 operation in the sequencer. This keeps leftover N2 information + * from interfering with spi_response logic. + */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + /* + * N1_bytes is the overall size of the N1 portion of the frame regardless of + * whether N1 is used for tx, rx or both. Loop over the size to build a + * payload that is N1_bytes long. + * N1_tx is the count of bytes to take from the TDR and "shift" into the + * frame which means append those bytes to the payload for the N1 portion + * of the frame. + * If N1_tx is 0 or if the count exceeds the size of the TDR append 0xFF to + * the frame until the overall N1 count is reached. + */ + n1_count = 0; + while (n1_count < s->N1_bytes) { + /* + * Assuming that if N1_tx is not equal to 0 then it is the same as + * N1_bytes. + */ + if ((s->N1_tx != 0) && (n1_count < PNV_SPI_REG_SIZE)) { + + if (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1) { + /* + * Note that we are only appending to the payload IF the TDR + * is full otherwise we don't touch the payload because we are + * going to NOT send the payload and instead tell the sequencer + * that called us to stop and wait for a TDR write so we have + * data to load into the payload. + */ + uint8_t n1_byte = 0x00; + n1_byte = get_from_offset(s, n1_count); + trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = + n1_byte; + } else { + /* + * We hit a shift_n1 opcode TX but the TDR is empty, tell the + * sequencer to stop and break this loop. + */ + trace_pnv_spi_sequencer_stop_requested("Shift N1" + "set for transmit but TDR is empty"); + stop = true; + break; + } + } else { + /* + * Cases here: + * - we are receiving during the N1 frame segment and the RDR + * is full so we need to stop until the RDR is read + * - we are transmitting and we don't care about RDR status + * since we won't be loading RDR during the frame segment. + * - we are receiving and the RDR is empty so we allow the operation + * to proceed. + */ + if ((s->N1_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL, + s->status) == 1)) { + trace_pnv_spi_sequencer_stop_requested("shift N1" + "set for receive but RDR is full"); + stop = true; + break; + } else { + trace_pnv_spi_tx_append_FF("n1_byte"); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = 0xff; + } + } + n1_count++; + } /* end of while */ + /* + * If we are not stopping due to an empty TDR and we are doing an N1 TX + * and the TDR is full we need to clear the TDR_full status. + * Do this here instead of up in the loop above so we don't log the message + * in every loop iteration. + * Ignore the send_n1_alone flag, all that does is defer the TX until the N2 + * operation, which was found immediately after the current opcode. The TDR + * was unloaded and will be shifted so we have to clear the TDR_full status. + */ + if (!stop && (s->N1_tx != 0) && + (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); + } + /* + * There are other reasons why the shifter would stop, such as a TDR empty + * or RDR full condition with N1 set to receive. If we haven't stopped due + * to either one of those conditions then check if the send_n1_alone flag is + * equal to False, indicating the next opcode is an N2 operation, AND if + * the N2 counter reload switch (bit 0 of the N2 count control field) is + * set. This condition requires a pacing write to "kick" off the N2 + * shift which includes the N1 shift as well when send_n1_alone is False. + */ + if (!stop && !send_n1_alone && + (GETFIELD(SPI_CTR_CFG_N2_CTRL_B0, s->regs[SPI_CTR_CFG_REG]) == 1)) { + trace_pnv_spi_sequencer_stop_requested("N2 counter reload " + "active, stop N1 shift, TDR_underrun set to 1"); + stop = true; + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 1); + } + /* + * If send_n1_alone is set AND we have a full TDR then this is the first and + * last payload to send and we don't have an N2 frame segment to add to the + * payload. + */ + if (send_n1_alone && !stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + trace_pnv_spi_tx_request("Shifting N1 frame", (*payload)->len); + transfer(s, *payload); + /* The N1 frame shift is complete so reset the N1 counters */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + pnv_spi_xfer_buffer_free(*payload); + *payload = NULL; + } + return stop; +} /* end of operation_shiftn1() */ + +/* + * Calculate the N2 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N2 opcode + * and doesn't test it. + * The counters returned are: + * N2 bits: Number of bits in the payload data that are significant + * to the responder. + * N2_bytes: Total count of payload bytes for the N2 frame. + * N2_tx: Total number of bytes taken from TDR for N2 + * N2_rx: Total number of bytes taken from the payload for N2 + */ +static void calculate_N2(PnvSpi *s, uint8_t opcode) +{ + /* + * Shift_N2 opcode form: 0x4M + * Implicit mode: + * If M!=0 the shift count is M bytes and M is the number of rx bytes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count control + * register fields. Note that we only check for Forced Implicit mode when + * M != 0 since the mode doesn't make sense when M = 0. + * Explicit mode: + * If M==0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) { + /* Explicit mode */ + s->N2_bits = GETFIELD(SPI_CTR_CFG_N2, s->regs[SPI_CTR_CFG_REG]); + s->N2_bytes = (s->N2_bits + 7) / 8; + s->N2_tx = 0; + s->N2_rx = 0; + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_tx = s->N2_bytes; + } + /* If rx count control for N2 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_rx = s->N2_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + s->N2_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->N2_bits = s->N2_bytes * 8; + /* Assume that we are going to receive the count */ + s->N2_rx = s->N2_bytes; + s->N2_tx = 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate a receive then reset the rx count to 0 + */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, + s->regs[SPI_CTR_CFG_REG]) == 0) { + s->N2_rx = 0; + } + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, + s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_tx = s->N2_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the + * known size of the shift register, 64 bits or 72 bits if ECC + * is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM + * error bit. + */ + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, + s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + if (s->N2_bytes > (PNV_SPI_REG_SIZE + 1)) { + /* Unsupported N2 shift size when ECC enabled */ + s->N2_bytes = PNV_SPI_REG_SIZE + 1; + s->N2_bits = s->N2_bytes * 8; + } + } else if (s->N2_bytes > PNV_SPI_REG_SIZE) { + /* Unsupported N2 shift size */ + s->N2_bytes = PNV_SPI_REG_SIZE; + s->N2_bits = s->N2_bytes * 8; + } +} /* end of calculate_N2 */ + +/* + * Shift_N2 operation handler method + */ + +static bool operation_shiftn2(PnvSpi *s, uint8_t opcode, + PnvXferBuffer **payload) +{ + uint8_t n2_count; + bool stop = false; + + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload == NULL) { + *payload = pnv_spi_xfer_buffer_new(); + } + /* + * Use a combination of N2 counters to build the N2 portion of the + * transmit payload. + */ + calculate_N2(s, opcode); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * The only difference between this code and the code for shift N1 is + * that this code has to account for the possible presence of N1 transmit + * bytes already taken from the TDR. + * If there are bytes to be transmitted for the N2 portion of the frame + * and there are still bytes in TDR that have not been copied into the + * TX data of the payload, this code will handle transmitting those + * remaining bytes. + * If for some reason the transmit count(s) add up to more than the size + * of the TDR we will just append 0xFF to the transmit payload data until + * the payload is N1 + N2 bytes long. + */ + n2_count = 0; + while (n2_count < s->N2_bytes) { + /* + * If the RDR is full and we need to RX just bail out, letting the + * code continue will end up building the payload twice in the same + * buffer since RDR full causes a sequence stop and restart. + */ + if ((s->N2_rx != 0) && + (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) { + trace_pnv_spi_sequencer_stop_requested("shift N2 set" + "for receive but RDR is full"); + stop = true; + break; + } + if ((s->N2_tx != 0) && ((s->N1_tx + n2_count) < + PNV_SPI_REG_SIZE)) { + /* Always append data for the N2 segment if it is set for TX */ + uint8_t n2_byte = 0x00; + n2_byte = get_from_offset(s, (s->N1_tx + n2_count)); + trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count)); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = n2_byte; + } else { + /* + * Regardless of whether or not N2 is set for TX or RX, we need + * the number of bytes in the payload to match the overall length + * of the operation. + */ + trace_pnv_spi_tx_append_FF("n2_byte"); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = 0xff; + } + n2_count++; + } /* end of while */ + if (!stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + trace_pnv_spi_tx_request("Shifting N2 frame", (*payload)->len); + transfer(s, *payload); + /* + * If we are doing an N2 TX and the TDR is full we need to clear the + * TDR_full status. Do this here instead of up in the loop above so we + * don't log the message in every loop iteration. + */ + if ((s->N2_tx != 0) && + (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); + } + /* + * The N2 frame shift is complete so reset the N2 counters. + * Reset the N1 counters also in case the frame was a combination of + * N1 and N2 segments. + */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + s->N1_bits = 0; + s->N1_bytes = 0; + s->N1_tx = 0; + s->N1_rx = 0; + pnv_spi_xfer_buffer_free(*payload); + *payload = NULL; + } + return stop; +} /* end of operation_shiftn2()*/ + +static void operation_sequencer(PnvSpi *s) +{ + /* + * Loop through each sequencer operation ID and perform the requested + * operations. + * Flag for indicating if we should send the N1 frame or wait to combine + * it with a preceding N2 frame. + */ + bool send_n1_alone = true; + bool stop = false; /* Flag to stop the sequencer */ + uint8_t opcode = 0; + uint8_t masked_opcode = 0; + + /* + * PnvXferBuffer for containing the payload of the SPI frame. + * This is a static because there are cases where a sequence has to stop + * and wait for the target application to unload the RDR. If this occurs + * during a sequence where N1 is not sent alone and instead combined with + * N2 since the N1 tx length + the N2 tx length is less than the size of + * the TDR. + */ + static PnvXferBuffer *payload; + + if (payload == NULL) { + payload = pnv_spi_xfer_buffer_new(); + } + /* + * Clear the sequencer FSM error bit - general_SPI_status[3] + * before starting a sequence. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 0); + /* + * If the FSM is idle set the sequencer index to 0 + * (new/restarted sequence) + */ + if (GETFIELD(SPI_STS_SEQ_FSM, s->status) == SEQ_STATE_IDLE) { + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + } + /* + * There are only 8 possible operation IDs to iterate through though + * some operations may cause more than one frame to be sequenced. + */ + while (get_seq_index(s) < NUM_SEQ_OPS) { + opcode = s->seq_op[get_seq_index(s)]; + /* Set sequencer state to decode */ + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_DECODE); + /* + * Only the upper nibble of the operation ID is needed to know what + * kind of operation is requested. + */ + masked_opcode = PNV_SPI_MASKED_OPCODE(opcode); + switch (masked_opcode) { + /* + * Increment the operation index in each case instead of just + * once at the end in case an operation like the branch + * operation needs to change the index. + */ + case SEQ_OP_STOP: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* A stop operation in any position stops the sequencer */ + trace_pnv_spi_sequencer_op("STOP", get_seq_index(s)); + + stop = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); + break; + + case SEQ_OP_SELECT_SLAVE: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SELECT_SLAVE", get_seq_index(s)); + /* + * This device currently only supports a single responder + * connection at position 0. De-selecting a responder is fine + * and expected at the end of a sequence but selecting any + * responder other than 0 should cause an error. + */ + s->responder_select = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + if (s->responder_select == 0) { + trace_pnv_spi_shifter_done(); + qemu_set_irq(s->cs_line[0], 1); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + (get_seq_index(s) + 1)); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_DONE); + } else if (s->responder_select != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 " + "not supported, select = 0x%x\n", + s->responder_select); + trace_pnv_spi_sequencer_stop_requested("invalid " + "responder select"); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + stop = true; + } else { + /* + * Only allow an FSM_START state when a responder is + * selected + */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_START); + trace_pnv_spi_shifter_stating(); + qemu_set_irq(s->cs_line[0], 0); + /* + * A Shift_N2 operation is only valid after a Shift_N1 + * according to the spec. The spec doesn't say if that means + * immediately after or just after at any point. We will track + * the occurrence of a Shift_N1 to enforce this requirement in + * the most generic way possible by assuming that the rule + * applies once a valid responder select has occurred. + */ + s->shift_n1_done = false; + next_sequencer_fsm(s); + } + break; + + case SEQ_OP_SHIFT_N1: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SHIFT_N1", get_seq_index(s)); + /* + * Only allow a shift_n1 when the state is not IDLE or DONE. + * In either of those two cases the sequencer is not in a proper + * state to perform shift operations because the sequencer has: + * - processed a responder deselect (DONE) + * - processed a stop opcode (IDLE) + * - encountered an error (IDLE) + */ + if ((GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_IDLE) || + (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_DONE)) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N1 not allowed in " + "shifter state = 0x%llx", GETFIELD( + SPI_STS_SHIFTER_FSM, s->status)); + /* + * Set sequencer FSM error bit 3 (general_SPI_status[3]) + * in status reg. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); + trace_pnv_spi_sequencer_stop_requested("invalid shifter state"); + stop = true; + } else { + /* + * Look for the special case where there is a shift_n1 set for + * transmit and it is followed by a shift_n2 set for transmit + * AND the combined transmit length of the two operations is + * less than or equal to the size of the TDR register. In this + * case we want to use both this current shift_n1 opcode and the + * following shift_n2 opcode to assemble the frame for + * transmission to the responder without requiring a refill of + * the TDR between the two operations. + */ + if (PNV_SPI_MASKED_OPCODE(s->seq_op[get_seq_index(s) + 1]) + == SEQ_OP_SHIFT_N2) { + send_n1_alone = false; + } + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N1); + stop = operation_shiftn1(s, opcode, &payload, send_n1_alone); + if (stop) { + /* + * The operation code says to stop, this can occur if: + * (1) RDR is full and the N1 shift is set for receive + * (2) TDR was empty at the time of the N1 shift so we need + * to wait for data. + * (3) Neither 1 nor 2 are occurring and we aren't sending + * N1 alone and N2 counter reload is set (bit 0 of the N2 + * counter reload field). In this case TDR_underrun will + * will be set and the Payload has been loaded so it is + * ok to advance the sequencer. + */ + if (GETFIELD(SPI_STS_TDR_UNDERRUN, s->status)) { + s->shift_n1_done = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N2); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + (get_seq_index(s) + 1)); + } else { + /* + * This is case (1) or (2) so the sequencer needs to + * wait and NOT go to the next sequence yet. + */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_WAIT); + } + } else { + /* Ok to move on to the next index */ + s->shift_n1_done = true; + next_sequencer_fsm(s); + } + } + break; + + case SEQ_OP_SHIFT_N2: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SHIFT_N2", get_seq_index(s)); + if (!s->shift_n1_done) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N2 is not allowed if a " + "Shift_N1 is not done, shifter state = 0x%llx", + GETFIELD(SPI_STS_SHIFTER_FSM, s->status)); + /* + * In case the sequencer actually stops if an N2 shift is + * requested before any N1 shift is done. Set sequencer FSM + * error bit 3 (general_SPI_status[3]) in status reg. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); + trace_pnv_spi_sequencer_stop_requested("shift_n2 " + "w/no shift_n1 done"); + stop = true; + } else { + /* Ok to do a Shift_N2 */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N2); + stop = operation_shiftn2(s, opcode, &payload); + /* + * If the operation code says to stop set the shifter state to + * wait and stop + */ + if (stop) { + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_WAIT); + } else { + /* Ok to move on to the next index */ + next_sequencer_fsm(s); + } + } + break; + + case SEQ_OP_BRANCH_IFNEQ_RDR: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_RDR", get_seq_index(s)); + /* + * The memory mapping register RDR match value is compared against + * the 16 rightmost bytes of the RDR (potentially with masking). + * Since this comparison is performed against the contents of the + * RDR then a receive must have previously occurred otherwise + * there is no data to compare and the operation cannot be + * completed and will stop the sequencer until RDR full is set to + * 1. + */ + if (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1) { + bool rdr_matched = false; + rdr_matched = does_rdr_match(s); + if (rdr_matched) { + trace_pnv_spi_RDR_match("success"); + /* A match occurred, increment the sequencer index. */ + next_sequencer_fsm(s); + } else { + trace_pnv_spi_RDR_match("failed"); + /* + * Branch the sequencer to the index coded into the op + * code. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + } + /* + * Regardless of where the branch ended up we want the + * sequencer to continue shifting so we have to clear + * RDR_full. + */ + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + } else { + trace_pnv_spi_sequencer_stop_requested("RDR not" + "full for 0x6x opcode"); + stop = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT); + } + break; + + case SEQ_OP_TRANSFER_TDR: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\n"); + next_sequencer_fsm(s); + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_1: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_1", get_seq_index(s)); + /* + * The spec says the loop should execute count compare + 1 times. + * However we learned from engineering that we really only loop + * count_compare times, count compare = 0 makes this op code a + * no-op + */ + if (s->loop_counter_1 != + GETFIELD(SPI_CTR_CFG_CMP1, s->regs[SPI_CTR_CFG_REG])) { + /* + * Next index is the lower nibble of the branch operation ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + s->loop_counter_1++; + } else { + /* Continue to next index if loop counter is reached */ + next_sequencer_fsm(s); + } + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_2: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", get_seq_index(s)); + uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, + s->regs[SPI_CTR_CFG_REG]); + /* + * The spec says the loop should execute count compare + 1 times. + * However we learned from engineering that we really only loop + * count_compare times, count compare = 0 makes this op code a + * no-op + */ + if (s->loop_counter_2 != condition2) { + /* + * Next index is the lower nibble of the branch operation ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, + s->status, PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + s->loop_counter_2++; + } else { + /* Continue to next index if loop counter is reached */ + next_sequencer_fsm(s); + } + break; + + default: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* Ignore unsupported operations. */ + next_sequencer_fsm(s); + break; + } /* end of switch */ + /* + * If we used all 8 opcodes without seeing a 00 - STOP in the sequence + * we need to go ahead and end things as if there was a STOP at the + * end. + */ + if (get_seq_index(s) == NUM_SEQ_OPS) { + /* All 8 opcodes completed, sequencer idling */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); + break; + } + /* Break the loop if a stop was requested */ + if (stop) { + break; + } + } /* end of while */ + return; +} /* end of operation_sequencer() */ + +/* + * The SPIC engine and its internal sequencer can be interrupted and reset by + * a hardware signal, the sbe_spicst_hard_reset bits from Pervasive + * Miscellaneous Register of sbe_register_bo device. + * Reset immediately aborts any SPI transaction in progress and returns the + * sequencer and state machines to idle state. + * The configuration register values are not changed. The status register is + * not reset. The engine registers are not reset. + * The SPIC engine reset does not have any affect on the attached devices. + * Reset handling of any attached devices is beyond the scope of the engine. + */ +static void do_reset(DeviceState *dev) +{ + PnvSpi *s = PNV_SPI(dev); + + trace_pnv_spi_reset(); + + /* Reset all N1 and N2 counters, and other constants */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + s->N1_bits = 0; + s->N1_bytes = 0; + s->N1_tx = 0; + s->N1_rx = 0; + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + /* Disconnected from responder */ + qemu_set_irq(s->cs_line[0], 1); +} + static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size) { PnvSpi *s = PNV_SPI(opaque); @@ -51,6 +1088,10 @@ static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size) val = s->regs[reg]; trace_pnv_spi_read_RDR(val); s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + if (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_WAIT) { + trace_pnv_spi_start_sequencer(); + operation_sequencer(s); + } break; case SPI_SEQ_OP_REG: val = 0; @@ -112,6 +1153,8 @@ static void pnv_spi_xscom_write(void *opaque, hwaddr addr, trace_pnv_spi_write_TDR(val); s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1); s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0); + trace_pnv_spi_start_sequencer(); + operation_sequencer(s); break; case SPI_SEQ_OP_REG: for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { @@ -144,6 +1187,7 @@ static const MemoryRegionOps pnv_spi_xscom_ops = { static Property pnv_spi_properties[] = { DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), + DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), DEFINE_PROP_END_OF_LIST(), }; @@ -193,6 +1237,7 @@ static void pnv_spi_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV SPI"; dc->realize = pnv_spi_realize; + dc->reset = do_reset; device_class_set_props(dc, pnv_spi_properties); } diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index 2cc29e1284..089d269994 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -38,3 +38,18 @@ pnv_spi_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 pnv_spi_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 pnv_spi_read_RDR(uint64_t val) "data extracted = 0x%" PRIx64 pnv_spi_write_TDR(uint64_t val) "being written, data written = 0x%" PRIx64 +pnv_spi_start_sequencer(void) "" +pnv_spi_reset(void) "spic engine sequencer configuration and spi communication" +pnv_spi_sequencer_op(const char* op, uint8_t index) "%s at index = 0x%x" +pnv_spi_shifter_stating(void) "pull CS line low" +pnv_spi_shifter_done(void) "pull the CS line high" +pnv_spi_log_Ncounts(uint8_t N1_bits, uint8_t N1_bytes, uint8_t N1_tx, uint8_t N1_rx, uint8_t N2_bits, uint8_t N2_bytes, uint8_t N2_tx, uint8_t N2_rx) "N1_bits = %d, N1_bytes = %d, N1_tx = %d, N1_rx = %d, N2_bits = %d, N2_bytes = %d, N2_tx = %d, N2_rx = %d" +pnv_spi_tx_append(const char* frame, uint8_t byte, uint8_t tdr_index) "%s = 0x%2.2x to payload from TDR at index %d" +pnv_spi_tx_append_FF(const char* frame) "%s to Payload" +pnv_spi_tx_request(const char* frame, uint32_t payload_len) "%s, payload len = %d" +pnv_spi_rx_received(uint32_t payload_len) "payload len = %d" +pnv_spi_rx_read_N1frame(void) "" +pnv_spi_rx_read_N2frame(void) "" +pnv_spi_shift_rx(uint8_t byte, uint32_t index) "byte = 0x%2.2x into RDR from payload index %d" +pnv_spi_sequencer_stop_requested(const char* reason) "due to %s" +pnv_spi_RDR_match(const char* result) "%s" diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h index 833042b74b..8815f67d45 100644 --- a/include/hw/ssi/pnv_spi.h +++ b/include/hw/ssi/pnv_spi.h @@ -8,6 +8,14 @@ * This model Supports a connection to a single SPI responder. * Introduced for P10 to provide access to SPI seeproms, TPM, flash device * and an ADC controller. + * + * All SPI function control is mapped into the SPI register space to enable + * full control by firmware. + * + * SPI Controller has sequencer and shift engine. The SPI shift engine + * performs serialization and de-serialization according to the control by + * the sequencer and according to the setup defined in the configuration + * registers and the SPI sequencer implements the main control logic. */ #ifndef PPC_PNV_SPI_H @@ -31,6 +39,25 @@ typedef struct PnvSpi { MemoryRegion xscom_spic_regs; /* SPI object number */ uint32_t spic_num; + uint8_t transfer_len; + uint8_t responder_select; + /* To verify if shift_n1 happens prior to shift_n2 */ + bool shift_n1_done; + /* Loop counter for branch operation opcode Ex/Fx */ + uint8_t loop_counter_1; + uint8_t loop_counter_2; + /* N1/N2_bits specifies the size of the N1/N2 segment of a frame in bits.*/ + uint8_t N1_bits; + uint8_t N2_bits; + /* Number of bytes in a payload for the N1/N2 frame segment.*/ + uint8_t N1_bytes; + uint8_t N2_bytes; + /* Number of N1/N2 bytes marked for transmit */ + uint8_t N1_tx; + uint8_t N2_tx; + /* Number of N1/N2 bytes marked for receive */ + uint8_t N1_rx; + uint8_t N2_rx; /* SPI registers */ uint64_t regs[PNV_SPI_REGS]; diff --git a/include/hw/ssi/pnv_spi_regs.h b/include/hw/ssi/pnv_spi_regs.h index 5b6ff72d02..596e2c1911 100644 --- a/include/hw/ssi/pnv_spi_regs.h +++ b/include/hw/ssi/pnv_spi_regs.h @@ -28,6 +28,17 @@ /* counter_config_reg */ #define SPI_CTR_CFG_REG 0x01 +#define SPI_CTR_CFG_N1 PPC_BITMASK(0, 7) +#define SPI_CTR_CFG_N2 PPC_BITMASK(8, 15) +#define SPI_CTR_CFG_CMP1 PPC_BITMASK(24, 31) +#define SPI_CTR_CFG_CMP2 PPC_BITMASK(32, 39) +#define SPI_CTR_CFG_N1_CTRL_B1 PPC_BIT(49) +#define SPI_CTR_CFG_N1_CTRL_B2 PPC_BIT(50) +#define SPI_CTR_CFG_N1_CTRL_B3 PPC_BIT(51) +#define SPI_CTR_CFG_N2_CTRL_B0 PPC_BIT(52) +#define SPI_CTR_CFG_N2_CTRL_B1 PPC_BIT(53) +#define SPI_CTR_CFG_N2_CTRL_B2 PPC_BIT(54) +#define SPI_CTR_CFG_N2_CTRL_B3 PPC_BIT(55) /* config_reg */ #define CONFIG_REG1 0x02 @@ -36,9 +47,13 @@ #define SPI_CLK_CFG_REG 0x03 #define SPI_CLK_CFG_HARD_RST 0x0084000000000000; #define SPI_CLK_CFG_RST_CTRL PPC_BITMASK(24, 27) +#define SPI_CLK_CFG_ECC_EN PPC_BIT(28) +#define SPI_CLK_CFG_ECC_CTRL PPC_BITMASK(29, 30) /* memory_mapping_reg */ #define SPI_MM_REG 0x04 +#define SPI_MM_RDR_MATCH_VAL PPC_BITMASK(32, 47) +#define SPI_MM_RDR_MATCH_MASK PPC_BITMASK(48, 63) /* transmit_data_reg */ #define SPI_XMIT_DATA_REG 0x05 @@ -60,8 +75,59 @@ #define SPI_STS_SEQ_FSM PPC_BITMASK(8, 15) #define SPI_STS_SHIFTER_FSM PPC_BITMASK(16, 27) #define SPI_STS_SEQ_INDEX PPC_BITMASK(28, 31) -#define SPI_STS_GEN_STATUS PPC_BITMASK(32, 63) +#define SPI_STS_GEN_STATUS_B3 PPC_BIT(35) #define SPI_STS_RDR PPC_BITMASK(1, 3) #define SPI_STS_TDR PPC_BITMASK(5, 7) +/* + * Shifter states + * + * These are the same values defined for the Shifter FSM field of the + * status register. It's a 12 bit field so we will represent it as three + * nibbles in the constants. + * + * These are shifter_fsm values + * + * Status reg bits 16-27 -> field bits 0-11 + * bits 0,1,2,5 unused/reserved + * bit 4 crc shift in (unused) + * bit 8 crc shift out (unused) + */ + +#define FSM_DONE 0x100 /* bit 3 */ +#define FSM_SHIFT_N2 0x020 /* bit 6 */ +#define FSM_WAIT 0x010 /* bit 7 */ +#define FSM_SHIFT_N1 0x004 /* bit 9 */ +#define FSM_START 0x002 /* bit 10 */ +#define FSM_IDLE 0x001 /* bit 11 */ + +/* + * Sequencer states + * + * These are sequencer_fsm values + * + * Status reg bits 8-15 -> field bits 0-7 + * bits 0-3 unused/reserved + * + */ +#define SEQ_STATE_INDEX_INCREMENT 0x08 /* bit 4 */ +#define SEQ_STATE_EXECUTE 0x04 /* bit 5 */ +#define SEQ_STATE_DECODE 0x02 /* bit 6 */ +#define SEQ_STATE_IDLE 0x01 /* bit 7 */ + +/* + * These are the supported sequencer operations. + * Only the upper nibble is significant because for many operations + * the lower nibble is a variable specific to the operation. + */ +#define SEQ_OP_STOP 0x00 +#define SEQ_OP_SELECT_SLAVE 0x10 +#define SEQ_OP_SHIFT_N1 0x30 +#define SEQ_OP_SHIFT_N2 0x40 +#define SEQ_OP_BRANCH_IFNEQ_RDR 0x60 +#define SEQ_OP_TRANSFER_TDR 0xC0 +#define SEQ_OP_BRANCH_IFNEQ_INC_1 0xE0 +#define SEQ_OP_BRANCH_IFNEQ_INC_2 0xF0 +#define NUM_SEQ_OPS 8 + #endif From 8d970f4162b8a388eef08ee37dab47a650e390ab Mon Sep 17 00:00:00 2001 From: Chalapathi V <chalapathi.v@linux.ibm.com> Date: Wed, 26 Jun 2024 04:05:26 -0500 Subject: [PATCH 2518/2884] hw/block: Add Microchip's 25CSM04 to m25p80 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Microchip's 25CSM04 Serial EEPROM to m25p80. 25CSM04 provides 4 Mbits of Serial EEPROM utilizing the Serial Peripheral Interface (SPI) compatible bus. The device is organized as 524288 bytes of 8 bits each (512Kbyte) and is optimized for use in consumer and industrial applications where reliable and dependable nonvolatile memory storage is essential. Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/block/m25p80.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 9e99107b42..0b94af3653 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -357,6 +357,9 @@ static const FlashPartInfo known_devices[] = { .sfdp_read = m25p80_sfdp_w25q512jv }, { INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K), .sfdp_read = m25p80_sfdp_w25q01jvq }, + + /* Microchip */ + { INFO("25csm04", 0x29cc00, 0x100, 64 << 10, 8, 0) }, }; typedef enum { From bb44dc48628e9168f16c460f778bbef7a91d7708 Mon Sep 17 00:00:00 2001 From: Chalapathi V <chalapathi.v@linux.ibm.com> Date: Wed, 26 Jun 2024 04:05:27 -0500 Subject: [PATCH 2519/2884] hw/ppc: SPI controller wiring to P10 chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, create SPI controller on p10 chip and connect cs irq. The QOM tree of pnv-spi and seeprom are. /machine (powernv10-machine) /chip[0] (power10_v2.0-pnv-chip) /pib_spic[2] (pnv-spi) /pnv-spi-bus.2 (SSI) /xscom-spi[0] (memory-region) /machine (powernv10-machine) /peripheral-anon (container) /device[0] (25csm04) /WP#[0] (irq) /ssi-gpio-cs[0] (irq) (qemu) qom-get /machine/peripheral-anon /device[76] "parent_bus" "/machine/chip[0]/pib_spic[2]/pnv-spi-bus.2" Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/pnv.c | 21 ++++++++++++++++++++- hw/ssi/pnv_spi.c | 8 ++++++++ include/hw/ppc/pnv_chip.h | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a3560d25b7..3526852685 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1962,6 +1962,11 @@ static void pnv_chip_power10_instance_init(Object *obj) for (i = 0; i < pcc->i2c_num_engines; i++) { object_initialize_child(obj, "i2c[*]", &chip10->i2c[i], TYPE_PNV_I2C); } + + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_initialize_child(obj, "pib_spic[*]", &chip10->pib_spic[i], + TYPE_PNV_SPI); + } } static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) @@ -2185,7 +2190,21 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_SBE_I2C)); } - + /* PIB SPI Controller */ + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "spic_num", + i, &error_fatal); + /* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */ + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "transfer_len", + (i == 2) ? 1 : 4, &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT + (&chip10->pib_spic[i])), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PIB_SPIC_BASE + + i * PNV10_XSCOM_PIB_SPIC_SIZE, + &chip10->pib_spic[i].xscom_spic_regs); + } } static void pnv_rainier_i2c_init(PnvMachineState *pnv) diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index cdff3f9621..c1297ab733 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -1051,9 +1051,17 @@ static void operation_sequencer(PnvSpi *s) static void do_reset(DeviceState *dev) { PnvSpi *s = PNV_SPI(dev); + DeviceState *ssi_dev; trace_pnv_spi_reset(); + /* Connect cs irq */ + ssi_dev = ssi_get_cs(s->ssi_bus, 0); + if (ssi_dev) { + qemu_irq cs_line = qdev_get_gpio_in_named(ssi_dev, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(s), "cs", 0, cs_line); + } + /* Reset all N1 and N2 counters, and other constants */ s->N2_bits = 0; s->N2_bytes = 0; diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index ee1649babc..de34cbdc96 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -7,6 +7,7 @@ #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_homer.h" #include "hw/ppc/pnv_n1_chiplet.h" +#include "hw/ssi/pnv_spi.h" #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_occ.h" #include "hw/ppc/pnv_psi.h" @@ -123,6 +124,8 @@ struct Pnv10Chip { PnvSBE sbe; PnvHomer homer; PnvN1Chiplet n1_chiplet; +#define PNV10_CHIP_MAX_PIB_SPIC 6 + PnvSpi pib_spic[PNV10_CHIP_MAX_PIB_SPIC]; uint32_t nr_quads; PnvQuad *quads; From 533074918727c5fafb11a033fcccaac11ee0227b Mon Sep 17 00:00:00 2001 From: Chalapathi V <chalapathi.v@linux.ibm.com> Date: Wed, 26 Jun 2024 04:05:28 -0500 Subject: [PATCH 2520/2884] tests/qtest: Add pnv-spi-seeprom qtest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit Write a qtest pnv-spi-seeprom-test to check the SPI transactions between spi controller and seeprom device. Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com> Acked-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Caleb Schlossin <calebs@linux.vnet.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- tests/qtest/meson.build | 1 + tests/qtest/pnv-spi-seeprom-test.c | 110 +++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 tests/qtest/pnv-spi-seeprom-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index e7ab2a4312..2f0d3ef080 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -171,6 +171,7 @@ qtests_ppc64 = \ qtests_ppc + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-spi-seeprom-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \ (slirp.found() ? ['pxe-test'] : []) + \ diff --git a/tests/qtest/pnv-spi-seeprom-test.c b/tests/qtest/pnv-spi-seeprom-test.c new file mode 100644 index 0000000000..57f20af76e --- /dev/null +++ b/tests/qtest/pnv-spi-seeprom-test.c @@ -0,0 +1,110 @@ +/* + * QTest testcase for PowerNV 10 Seeprom Communications + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <unistd.h> +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bswap.h" +#include "hw/ssi/pnv_spi_regs.h" +#include "pnv-xscom.h" + +#define FLASH_SIZE (512 * 1024) +#define SPIC2_XSCOM_BASE 0xc0040 + +/* To transmit READ opcode and address */ +#define READ_OP_TDR_DATA 0x0300010000000000 +/* + * N1 shift - tx 4 bytes (transmit opcode and address) + * N2 shift - tx and rx 8 bytes. + */ +#define READ_OP_COUNTER_CONFIG 0x2040000000002b00 +/* SEQ_OP_SELECT_RESPONDER - N1 Shift - N2 Shift * 5 - SEQ_OP_STOP */ +#define READ_OP_SEQUENCER 0x1130404040404010 + +/* To transmit WREN(Set Write Enable Latch in status0 register) opcode */ +#define WRITE_OP_WREN 0x0600000000000000 +/* To transmit WRITE opcode, address and data */ +#define WRITE_OP_TDR_DATA 0x0300010012345678 +/* N1 shift - tx 8 bytes (transmit opcode, address and data) */ +#define WRITE_OP_COUNTER_CONFIG 0x4000000000002000 +/* SEQ_OP_SELECT_RESPONDER - N1 Shift - SEQ_OP_STOP */ +#define WRITE_OP_SEQUENCER 0x1130100000000000 + +static void pnv_spi_xscom_write(QTestState *qts, const PnvChip *chip, + uint32_t reg, uint64_t val) +{ + uint32_t pcba = SPIC2_XSCOM_BASE + reg; + qtest_writeq(qts, pnv_xscom_addr(chip, pcba), val); +} + +static uint64_t pnv_spi_xscom_read(QTestState *qts, const PnvChip *chip, + uint32_t reg) +{ + uint32_t pcba = SPIC2_XSCOM_BASE + reg; + return qtest_readq(qts, pnv_xscom_addr(chip, pcba)); +} + +static void spi_seeprom_transaction(QTestState *qts, const PnvChip *chip) +{ + /* SPI transactions to SEEPROM to read from SEEPROM image */ + pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, READ_OP_COUNTER_CONFIG); + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, READ_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, READ_OP_TDR_DATA); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, 0); + /* Read 5*8 bytes from SEEPROM at 0x100 */ + uint64_t rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + g_test_message("RDR READ = 0x%" PRIx64, rdr_val); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + g_test_message("RDR READ = 0x%" PRIx64, rdr_val); + + /* SPI transactions to SEEPROM to write to SEEPROM image */ + pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, WRITE_OP_COUNTER_CONFIG); + /* Set Write Enable Latch bit of status0 register */ + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_WREN); + /* write 8 bytes to SEEPROM at 0x100 */ + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_TDR_DATA); +} + +static void test_spi_seeprom(const void *data) +{ + const PnvChip *chip = data; + QTestState *qts = NULL; + g_autofree char *tmp_path = NULL; + int ret; + int fd; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("qtest-seeprom-XXXXXX", &tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, FLASH_SIZE); + g_assert(ret == 0); + close(fd); + + qts = qtest_initf("-machine powernv10 -smp 2,cores=2," + "threads=1 -accel tcg,thread=single -nographic " + "-blockdev node-name=pib_spic2,driver=file," + "filename=%s -device 25csm04,bus=pnv-spi-bus.2,cs=0," + "drive=pib_spic2", tmp_path); + spi_seeprom_transaction(qts, chip); + qtest_quit(qts); + unlink(tmp_path); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + char *tname = g_strdup_printf("pnv-xscom/spi-seeprom/%s", + pnv_chips[3].cpu_model); + qtest_add_data_func(tname, &pnv_chips[3], test_spi_seeprom); + g_free(tname); + return g_test_run(); +} From 8c01b2e1f7e34e6444abb59a544a52192393e798 Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:20 -0500 Subject: [PATCH 2521/2884] pnv/xive2: XIVE2 Cache Watch, Cache Flush and Sync Injection support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XIVE offers a 'cache watch facility', which allows software to read/update a potentially cached table entry with no software lock. There's one such facility in the Virtualization Controller (VC) to update the ESB and END entries and one in the Presentation Controller (PC) to update the NVP/NVG/NVC entries. Each facility has 4 cache watch engines to control the updates and firmware can request an available engine by querying the hardware 'watch_assign' register of the VC or PC. The engine is then reserved and is released after the data is updated by reading the 'watch_spec' register (which also allows to check for a conflict during the update). If no engine is available, the special value 0xFF is returned and firmware is expected to repeat the request until an engine becomes available. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 251 +++++++++++++++++++++++++++++++++------ hw/intc/pnv_xive2_regs.h | 90 ++++++++++++++ 2 files changed, 305 insertions(+), 36 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 2fb4fa29d4..af9ab68fc6 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -329,40 +329,48 @@ static int pnv_xive2_write_end(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_end_update(PnvXive2 *xive) +static int pnv_xive2_end_update(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - int i; + uint8_t blk; + uint32_t idx; + int i, spec_reg, data_reg; uint64_t endc_watch[4]; + assert(watch_engine < ARRAY_SIZE(endc_watch)); + + spec_reg = (VC_ENDC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (VC_ENDC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, xive->vc_regs[spec_reg]); + idx = GETFIELD(VC_ENDC_WATCH_INDEX, xive->vc_regs[spec_reg]); + for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { - endc_watch[i] = - cpu_to_be64(xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i]); + endc_watch[i] = cpu_to_be64(xive->vc_regs[data_reg + i]); } return pnv_xive2_vst_write(xive, VST_END, blk, idx, endc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_end_cache_load(PnvXive2 *xive) +static void pnv_xive2_end_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + uint8_t blk; + uint32_t idx; uint64_t endc_watch[4] = { 0 }; - int i; + int i, spec_reg, data_reg; + + assert(watch_engine < ARRAY_SIZE(endc_watch)); + + spec_reg = (VC_ENDC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (VC_ENDC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, xive->vc_regs[spec_reg]); + idx = GETFIELD(VC_ENDC_WATCH_INDEX, xive->vc_regs[spec_reg]); if (pnv_xive2_vst_read(xive, VST_END, blk, idx, endc_watch)) { xive2_error(xive, "VST: no END entry %x/%x !?", blk, idx); } for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { - xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i] = - be64_to_cpu(endc_watch[i]); + xive->vc_regs[data_reg + i] = be64_to_cpu(endc_watch[i]); } } @@ -379,40 +387,48 @@ static int pnv_xive2_write_nvp(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_nvp_update(PnvXive2 *xive) +static int pnv_xive2_nvp_update(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - int i; + uint8_t blk; + uint32_t idx; + int i, spec_reg, data_reg; uint64_t nxc_watch[4]; + assert(watch_engine < ARRAY_SIZE(nxc_watch)); + + spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); + idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); + for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { - nxc_watch[i] = - cpu_to_be64(xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i]); + nxc_watch[i] = cpu_to_be64(xive->pc_regs[data_reg + i]); } return pnv_xive2_vst_write(xive, VST_NVP, blk, idx, nxc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_nvp_cache_load(PnvXive2 *xive) +static void pnv_xive2_nvp_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + uint8_t blk; + uint32_t idx; uint64_t nxc_watch[4] = { 0 }; - int i; + int i, spec_reg, data_reg; + + assert(watch_engine < ARRAY_SIZE(nxc_watch)); + + spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); + idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); if (pnv_xive2_vst_read(xive, VST_NVP, blk, idx, nxc_watch)) { xive2_error(xive, "VST: no NVP entry %x/%x !?", blk, idx); } for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { - xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i] = - be64_to_cpu(nxc_watch[i]); + xive->pc_regs[data_reg + i] = be64_to_cpu(nxc_watch[i]); } } @@ -964,12 +980,70 @@ static const MemoryRegionOps pnv_xive2_ic_cq_ops = { }, }; +static uint8_t pnv_xive2_cache_watch_assign(uint64_t engine_mask, + uint64_t *state) +{ + uint8_t val = 0xFF; + int i; + + for (i = 3; i >= 0; i--) { + if (BIT(i) & engine_mask) { + if (!(BIT(i) & *state)) { + *state |= BIT(i); + val = 3 - i; + break; + } + } + } + return val; +} + +static void pnv_xive2_cache_watch_release(uint64_t *state, uint8_t watch_engine) +{ + uint8_t engine_bit = 3 - watch_engine; + + if (*state & BIT(engine_bit)) { + *state &= ~BIT(engine_bit); + } +} + +static uint8_t pnv_xive2_endc_cache_watch_assign(PnvXive2 *xive) +{ + uint64_t engine_mask = GETFIELD(VC_ENDC_CFG_CACHE_WATCH_ASSIGN, + xive->vc_regs[VC_ENDC_CFG >> 3]); + uint64_t state = xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3]; + uint8_t val; + + /* + * We keep track of which engines are currently busy in the + * VC_ENDC_WATCH_ASSIGN register directly. When the firmware reads + * the register, we don't return its value but the ID of an engine + * it can use. + * There are 4 engines. 0xFF means no engine is available. + */ + val = pnv_xive2_cache_watch_assign(engine_mask, &state); + if (val != 0xFF) { + xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3] = state; + } + return val; +} + +static void pnv_xive2_endc_cache_watch_release(PnvXive2 *xive, + uint8_t watch_engine) +{ + uint64_t state = xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3]; + + pnv_xive2_cache_watch_release(&state, watch_engine); + xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3] = state; +} + static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint64_t val = 0; uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1000,24 +1074,44 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, val = xive->vc_regs[reg]; break; + case VC_ENDC_WATCH_ASSIGN: + val = pnv_xive2_endc_cache_watch_assign(xive); + break; + + case VC_ENDC_CFG: + val = xive->vc_regs[reg]; + break; + /* * END cache updates */ case VC_ENDC_WATCH0_SPEC: + case VC_ENDC_WATCH1_SPEC: + case VC_ENDC_WATCH2_SPEC: + case VC_ENDC_WATCH3_SPEC: + watch_engine = (offset - VC_ENDC_WATCH0_SPEC) >> 6; xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT); + pnv_xive2_endc_cache_watch_release(xive, watch_engine); val = xive->vc_regs[reg]; break; case VC_ENDC_WATCH0_DATA0: + case VC_ENDC_WATCH1_DATA0: + case VC_ENDC_WATCH2_DATA0: + case VC_ENDC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the * SPEC register */ - pnv_xive2_end_cache_load(xive); + watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; + pnv_xive2_end_cache_load(xive, watch_engine); val = xive->vc_regs[reg]; break; case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + case VC_ENDC_WATCH1_DATA1 ... VC_ENDC_WATCH1_DATA3: + case VC_ENDC_WATCH2_DATA1 ... VC_ENDC_WATCH2_DATA3: + case VC_ENDC_WATCH3_DATA1 ... VC_ENDC_WATCH3_DATA3: val = xive->vc_regs[reg]; break; @@ -1063,6 +1157,7 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, { PnvXive2 *xive = PNV_XIVE2(opaque); uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1095,19 +1190,32 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* EAS update */ break; + case VC_ENDC_CFG: + break; + /* * END cache updates */ case VC_ENDC_WATCH0_SPEC: + case VC_ENDC_WATCH1_SPEC: + case VC_ENDC_WATCH2_SPEC: + case VC_ENDC_WATCH3_SPEC: val &= ~VC_ENDC_WATCH_CONFLICT; /* HW will set this bit */ break; case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + case VC_ENDC_WATCH1_DATA1 ... VC_ENDC_WATCH1_DATA3: + case VC_ENDC_WATCH2_DATA1 ... VC_ENDC_WATCH2_DATA3: + case VC_ENDC_WATCH3_DATA1 ... VC_ENDC_WATCH3_DATA3: break; case VC_ENDC_WATCH0_DATA0: + case VC_ENDC_WATCH1_DATA0: + case VC_ENDC_WATCH2_DATA0: + case VC_ENDC_WATCH3_DATA0: /* writing to DATA0 triggers the cache write */ + watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; xive->vc_regs[reg] = val; - pnv_xive2_end_update(xive); + pnv_xive2_end_update(xive, watch_engine); break; @@ -1157,12 +1265,43 @@ static const MemoryRegionOps pnv_xive2_ic_vc_ops = { }, }; +static uint8_t pnv_xive2_nxc_cache_watch_assign(PnvXive2 *xive) +{ + uint64_t engine_mask = GETFIELD(PC_NXC_PROC_CONFIG_WATCH_ASSIGN, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + uint64_t state = xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3]; + uint8_t val; + + /* + * We keep track of which engines are currently busy in the + * PC_NXC_WATCH_ASSIGN register directly. When the firmware reads + * the register, we don't return its value but the ID of an engine + * it can use. + * There are 4 engines. 0xFF means no engine is available. + */ + val = pnv_xive2_cache_watch_assign(engine_mask, &state); + if (val != 0xFF) { + xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3] = state; + } + return val; +} + +static void pnv_xive2_nxc_cache_watch_release(PnvXive2 *xive, + uint8_t watch_engine) +{ + uint64_t state = xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3]; + + pnv_xive2_cache_watch_release(&state, watch_engine); + xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3] = state; +} + static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint64_t val = -1; uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1173,24 +1312,44 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, val = xive->pc_regs[reg]; break; + case PC_NXC_WATCH_ASSIGN: + val = pnv_xive2_nxc_cache_watch_assign(xive); + break; + + case PC_NXC_PROC_CONFIG: + val = xive->pc_regs[reg]; + break; + /* * cache updates */ case PC_NXC_WATCH0_SPEC: + case PC_NXC_WATCH1_SPEC: + case PC_NXC_WATCH2_SPEC: + case PC_NXC_WATCH3_SPEC: + watch_engine = (offset - PC_NXC_WATCH0_SPEC) >> 6; xive->pc_regs[reg] &= ~(PC_NXC_WATCH_FULL | PC_NXC_WATCH_CONFLICT); + pnv_xive2_nxc_cache_watch_release(xive, watch_engine); val = xive->pc_regs[reg]; break; case PC_NXC_WATCH0_DATA0: + case PC_NXC_WATCH1_DATA0: + case PC_NXC_WATCH2_DATA0: + case PC_NXC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the * SPEC register */ - pnv_xive2_nvp_cache_load(xive); + watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; + pnv_xive2_nvp_cache_load(xive, watch_engine); val = xive->pc_regs[reg]; break; case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + case PC_NXC_WATCH1_DATA1 ... PC_NXC_WATCH1_DATA3: + case PC_NXC_WATCH2_DATA1 ... PC_NXC_WATCH2_DATA3: + case PC_NXC_WATCH3_DATA1 ... PC_NXC_WATCH3_DATA3: val = xive->pc_regs[reg]; break; @@ -1219,6 +1378,7 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, { PnvXive2 *xive = PNV_XIVE2(opaque); uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { @@ -1231,19 +1391,32 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, case PC_VSD_TABLE_DATA: break; + case PC_NXC_PROC_CONFIG: + break; + /* * cache updates */ case PC_NXC_WATCH0_SPEC: + case PC_NXC_WATCH1_SPEC: + case PC_NXC_WATCH2_SPEC: + case PC_NXC_WATCH3_SPEC: val &= ~PC_NXC_WATCH_CONFLICT; /* HW will set this bit */ break; case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + case PC_NXC_WATCH1_DATA1 ... PC_NXC_WATCH1_DATA3: + case PC_NXC_WATCH2_DATA1 ... PC_NXC_WATCH2_DATA3: + case PC_NXC_WATCH3_DATA1 ... PC_NXC_WATCH3_DATA3: break; case PC_NXC_WATCH0_DATA0: + case PC_NXC_WATCH1_DATA0: + case PC_NXC_WATCH2_DATA0: + case PC_NXC_WATCH3_DATA0: /* writing to DATA0 triggers the cache write */ + watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; xive->pc_regs[reg] = val; - pnv_xive2_nvp_update(xive); + pnv_xive2_nvp_update(xive, watch_engine); break; /* case PC_NXC_FLUSH_CTRL: */ @@ -1814,6 +1987,12 @@ static void pnv_xive2_reset(void *dev) xive->cq_regs[CQ_XIVE_CFG >> 3] |= SETFIELD(CQ_XIVE_CFG_HYP_HARD_BLOCK_ID, 0ull, xive->chip->chip_id); + /* VC and PC cache watch assign mechanism */ + xive->vc_regs[VC_ENDC_CFG >> 3] = + SETFIELD(VC_ENDC_CFG_CACHE_WATCH_ASSIGN, 0ull, 0b0111); + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3] = + SETFIELD(PC_NXC_PROC_CONFIG_WATCH_ASSIGN, 0ull, 0b0111); + /* Set default page size to 64k */ xive->ic_shift = xive->esb_shift = xive->end_shift = 16; xive->nvc_shift = xive->nvpg_shift = xive->tm_shift = 16; diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index 7165dc8704..f8e4a677c6 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -283,6 +283,15 @@ #define VC_ENDC_SYNC_QUEUE_HARD PPC_BIT(6) #define VC_QUEUE_COUNT 7 +/* ENDC cache watch assign */ +#define X_VC_ENDC_WATCH_ASSIGN 0x186 +#define VC_ENDC_WATCH_ASSIGN 0x430 + +/* ENDC configuration register */ +#define X_VC_ENDC_CFG 0x188 +#define VC_ENDC_CFG 0x440 +#define VC_ENDC_CFG_CACHE_WATCH_ASSIGN PPC_BITMASK(32, 35) + /* ENDC cache watch specification 0 */ #define X_VC_ENDC_WATCH0_SPEC 0x1A0 #define VC_ENDC_WATCH0_SPEC 0x500 @@ -302,6 +311,42 @@ #define VC_ENDC_WATCH0_DATA2 0x530 #define VC_ENDC_WATCH0_DATA3 0x538 +/* ENDC cache watch 1 */ +#define X_VC_ENDC_WATCH1_SPEC 0x1A8 +#define VC_ENDC_WATCH1_SPEC 0x540 +#define X_VC_ENDC_WATCH1_DATA0 0x1AC +#define X_VC_ENDC_WATCH1_DATA1 0x1AD +#define X_VC_ENDC_WATCH1_DATA2 0x1AE +#define X_VC_ENDC_WATCH1_DATA3 0x1AF +#define VC_ENDC_WATCH1_DATA0 0x560 +#define VC_ENDC_WATCH1_DATA1 0x568 +#define VC_ENDC_WATCH1_DATA2 0x570 +#define VC_ENDC_WATCH1_DATA3 0x578 + +/* ENDC cache watch 2 */ +#define X_VC_ENDC_WATCH2_SPEC 0x1B0 +#define VC_ENDC_WATCH2_SPEC 0x580 +#define X_VC_ENDC_WATCH2_DATA0 0x1B4 +#define X_VC_ENDC_WATCH2_DATA1 0x1B5 +#define X_VC_ENDC_WATCH2_DATA2 0x1B6 +#define X_VC_ENDC_WATCH2_DATA3 0x1B7 +#define VC_ENDC_WATCH2_DATA0 0x5A0 +#define VC_ENDC_WATCH2_DATA1 0x5A8 +#define VC_ENDC_WATCH2_DATA2 0x5B0 +#define VC_ENDC_WATCH2_DATA3 0x5B8 + +/* ENDC cache watch 3 */ +#define X_VC_ENDC_WATCH3_SPEC 0x1B8 +#define VC_ENDC_WATCH3_SPEC 0x5C0 +#define X_VC_ENDC_WATCH3_DATA0 0x1BC +#define X_VC_ENDC_WATCH3_DATA1 0x1BD +#define X_VC_ENDC_WATCH3_DATA2 0x1BE +#define X_VC_ENDC_WATCH3_DATA3 0x1BF +#define VC_ENDC_WATCH3_DATA0 0x5E0 +#define VC_ENDC_WATCH3_DATA1 0x5E8 +#define VC_ENDC_WATCH3_DATA2 0x5F0 +#define VC_ENDC_WATCH3_DATA3 0x5F8 + /* * PC LSB1 */ @@ -358,6 +403,15 @@ #define PC_NXC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define PC_NXC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* NxC Cache watch assign */ +#define X_PC_NXC_WATCH_ASSIGN 0x286 +#define PC_NXC_WATCH_ASSIGN 0x430 + +/* NxC Proc config */ +#define X_PC_NXC_PROC_CONFIG 0x28A +#define PC_NXC_PROC_CONFIG 0x450 +#define PC_NXC_PROC_CONFIG_WATCH_ASSIGN PPC_BITMASK(0, 3) + /* NxC Cache Watch 0 Specification */ #define X_PC_NXC_WATCH0_SPEC 0x2A0 #define PC_NXC_WATCH0_SPEC 0x500 @@ -381,6 +435,42 @@ #define PC_NXC_WATCH0_DATA2 0x530 #define PC_NXC_WATCH0_DATA3 0x538 +/* NxC Cache Watch 1 */ +#define X_PC_NXC_WATCH1_SPEC 0x2A8 +#define PC_NXC_WATCH1_SPEC 0x540 +#define X_PC_NXC_WATCH1_DATA0 0x2AC +#define X_PC_NXC_WATCH1_DATA1 0x2AD +#define X_PC_NXC_WATCH1_DATA2 0x2AE +#define X_PC_NXC_WATCH1_DATA3 0x2AF +#define PC_NXC_WATCH1_DATA0 0x560 +#define PC_NXC_WATCH1_DATA1 0x568 +#define PC_NXC_WATCH1_DATA2 0x570 +#define PC_NXC_WATCH1_DATA3 0x578 + +/* NxC Cache Watch 2 */ +#define X_PC_NXC_WATCH2_SPEC 0x2B0 +#define PC_NXC_WATCH2_SPEC 0x580 +#define X_PC_NXC_WATCH2_DATA0 0x2B4 +#define X_PC_NXC_WATCH2_DATA1 0x2B5 +#define X_PC_NXC_WATCH2_DATA2 0x2B6 +#define X_PC_NXC_WATCH2_DATA3 0x2B7 +#define PC_NXC_WATCH2_DATA0 0x5A0 +#define PC_NXC_WATCH2_DATA1 0x5A8 +#define PC_NXC_WATCH2_DATA2 0x5B0 +#define PC_NXC_WATCH2_DATA3 0x5B8 + +/* NxC Cache Watch 3 */ +#define X_PC_NXC_WATCH3_SPEC 0x2B8 +#define PC_NXC_WATCH3_SPEC 0x5C0 +#define X_PC_NXC_WATCH3_DATA0 0x2BC +#define X_PC_NXC_WATCH3_DATA1 0x2BD +#define X_PC_NXC_WATCH3_DATA2 0x2BE +#define X_PC_NXC_WATCH3_DATA3 0x2BF +#define PC_NXC_WATCH3_DATA0 0x5E0 +#define PC_NXC_WATCH3_DATA1 0x5E8 +#define PC_NXC_WATCH3_DATA2 0x5F0 +#define PC_NXC_WATCH3_DATA3 0x5F8 + /* * TCTXT Registers */ From 64770efd668e61128f30d6d50861c7a85ba12ec5 Mon Sep 17 00:00:00 2001 From: Michael Kowal <kowal@linux.vnet.ibm.com> Date: Wed, 24 Jul 2024 16:21:21 -0500 Subject: [PATCH 2522/2884] pnv/xive2: Structure/define alignment changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made changes to some structure and define elements to ease review in next patchset. Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index af9ab68fc6..08b6da78fb 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -45,16 +45,16 @@ typedef struct XiveVstInfo { static const XiveVstInfo vst_infos[] = { - [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, - [VST_ESB] = { "ESB", 1, 16 }, - [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, + [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, + [VST_ESB] = { "ESB", 1, 16 }, + [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, - [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, - [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, - [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, + [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, + [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, + [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, - [VST_IC] = { "IC", 1 /* ? */ , 16 }, /* Topology # */ - [VST_SYNC] = { "SYNC", 1 /* ? */ , 16 }, /* Topology # */ + [VST_IC] = { "IC", 1, /* ? */ 16 }, /* Topology # */ + [VST_SYNC] = { "SYNC", 1, /* ? */ 16 }, /* Topology # */ /* * This table contains the backing store pages for the interrupt @@ -1720,13 +1720,13 @@ static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { /* * Sync MMIO page (write only) */ -#define PNV_XIVE2_SYNC_IPI 0x000 -#define PNV_XIVE2_SYNC_HW 0x080 -#define PNV_XIVE2_SYNC_NxC 0x100 -#define PNV_XIVE2_SYNC_INT 0x180 -#define PNV_XIVE2_SYNC_OS_ESC 0x200 -#define PNV_XIVE2_SYNC_POOL_ESC 0x280 -#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_IPI 0x000 +#define PNV_XIVE2_SYNC_HW 0x080 +#define PNV_XIVE2_SYNC_NxC 0x100 +#define PNV_XIVE2_SYNC_INT 0x180 +#define PNV_XIVE2_SYNC_OS_ESC 0x200 +#define PNV_XIVE2_SYNC_POOL_ESC 0x280 +#define PNV_XIVE2_SYNC_HARD_ESC 0x300 static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, unsigned size) From 8e466dd092469e5ab0f355775c571ea96f3a8e23 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Wed, 24 Jul 2024 09:25:42 +1000 Subject: [PATCH 2523/2884] gitlab-ci: Use -fno-sanitize=function in the clang-user job With -fsanitize=undefined, which implies -fsanitize=function, clang will add a "type signature" before functions. It accesses funcptr-8 and funcptr-4 to do so. The generated TCG prologue is directly on a page boundary, so these accesses segfault. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240723232543.18093-1-richard.henderson@linaro.org> --- .gitlab-ci.d/buildtest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e3a0758bd9..aa32782405 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -444,6 +444,7 @@ clang-user: CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system --target-list-exclude=alpha-linux-user,microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined + --extra-cflags=-fno-sanitize=function MAKE_CHECK_ARGS: check-unit check-tcg # Set LD_JOBS=1 because this requires LTO and ld consumes a large amount of memory. From 76125c0132f27f0b4ba1b71d19027aba1fe62fd9 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Wed, 24 Jul 2024 16:21:22 -0500 Subject: [PATCH 2524/2884] pnv/xive: Support cache flush and queue sync inject with notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for writing a completion notification byte in memory whenever a cache flush or queue sync inject operation is requested by software. QEMU does not cache any of the XIVE data that is in memory and therefore it simply writes the completion notification byte at the time that the operation is requested. Co-authored-by: Glenn Miles <milesg@linux.vnet.ibm.com> Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 154 +++++++++++++++++++++++++++++++++++++- hw/intc/pnv_xive2_regs.h | 16 ++++ include/hw/ppc/pnv_chip.h | 1 + 3 files changed, 169 insertions(+), 2 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 08b6da78fb..3dbbfddacb 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -25,6 +25,7 @@ #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/qtest.h" #include <libfdt.h> @@ -32,6 +33,16 @@ #undef XIVE2_DEBUG +/* XIVE Sync or Flush Notification Block */ +typedef struct XiveSfnBlock { + uint8_t bytes[32]; +} XiveSfnBlock; + +/* XIVE Thread Sync or Flush Notification Area */ +typedef struct XiveThreadNA { + XiveSfnBlock topo[16]; +} XiveThreadNA; + /* * Virtual structures table (VST) */ @@ -54,7 +65,7 @@ static const XiveVstInfo vst_infos[] = { [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, [VST_IC] = { "IC", 1, /* ? */ 16 }, /* Topology # */ - [VST_SYNC] = { "SYNC", 1, /* ? */ 16 }, /* Topology # */ + [VST_SYNC] = { "SYNC", sizeof(XiveThreadNA), 16 }, /* Topology # */ /* * This table contains the backing store pages for the interrupt @@ -329,6 +340,73 @@ static int pnv_xive2_write_end(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } +static inline int pnv_xive2_get_current_pir(PnvXive2 *xive) +{ + if (!qtest_enabled()) { + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + return ppc_cpu_pir(cpu); + } + return 0; +} + +/* + * After SW injects a Queue Sync or Cache Flush operation, HW will notify + * SW of the completion of the operation by writing a byte of all 1's (0xff) + * to a specific memory location. The memory location is calculated by first + * looking up a base address in the SYNC VSD using the Topology ID of the + * originating thread as the "block" number. This points to a + * 64k block of memory that is further divided into 128 512 byte chunks of + * memory, which is indexed by the thread id of the requesting thread. + * Finally, this 512 byte chunk of memory is divided into 16 32 byte + * chunks which are indexed by the topology id of the targeted IC's chip. + * The values below are the offsets into that 32 byte chunk of memory for + * each type of cache flush or queue sync operation. + */ +#define PNV_XIVE2_QUEUE_IPI 0x00 +#define PNV_XIVE2_QUEUE_HW 0x01 +#define PNV_XIVE2_QUEUE_NXC 0x02 +#define PNV_XIVE2_QUEUE_INT 0x03 +#define PNV_XIVE2_QUEUE_OS 0x04 +#define PNV_XIVE2_QUEUE_POOL 0x05 +#define PNV_XIVE2_QUEUE_HARD 0x06 +#define PNV_XIVE2_CACHE_ENDC 0x08 +#define PNV_XIVE2_CACHE_ESBC 0x09 +#define PNV_XIVE2_CACHE_EASC 0x0a +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10 +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15 +#define PNV_XIVE2_CACHE_NXC 0x18 + +static int pnv_xive2_inject_notify(PnvXive2 *xive, int type) +{ + uint64_t addr; + int pir = pnv_xive2_get_current_pir(xive); + int thread_nr = PNV10_PIR2THREAD(pir); + int thread_topo_id = PNV10_PIR2CHIP(pir); + int ic_topo_id = xive->chip->chip_id; + uint64_t offset = ic_topo_id * sizeof(XiveSfnBlock); + uint8_t byte = 0xff; + MemTxResult result; + + /* Retrieve the address of requesting thread's notification area */ + addr = pnv_xive2_vst_addr(xive, VST_SYNC, thread_topo_id, thread_nr); + + if (!addr) { + xive2_error(xive, "VST: no SYNC entry %x/%x !?", + thread_topo_id, thread_nr); + return -1; + } + + address_space_stb(&address_space_memory, addr + offset + type, byte, + MEMTXATTRS_UNSPECIFIED, &result); + assert(result == MEMTX_OK); + + return 0; +} + static int pnv_xive2_end_update(PnvXive2 *xive, uint8_t watch_engine) { uint8_t blk; @@ -1178,6 +1256,10 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* ESB update */ break; + case VC_ESBC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_ESBC); + break; + case VC_ESBC_CFG: break; @@ -1190,6 +1272,10 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* EAS update */ break; + case VC_EASC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_EASC); + break; + case VC_ENDC_CFG: break; @@ -1224,6 +1310,10 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID; break; + case VC_ENDC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_ENDC); + break; + /* * Indirect invalidation */ @@ -1424,6 +1514,10 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID; break; + case PC_NXC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_NXC); + break; + /* * Indirect invalidation */ @@ -1727,6 +1821,12 @@ static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { #define PNV_XIVE2_SYNC_OS_ESC 0x200 #define PNV_XIVE2_SYNC_POOL_ESC 0x280 #define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80 static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, unsigned size) @@ -1738,22 +1838,72 @@ static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, return -1; } +/* + * The sync MMIO space spans two pages. The lower page is use for + * queue sync "poll" requests while the upper page is used for queue + * sync "inject" requests. Inject requests require the HW to write + * a byte of all 1's to a predetermined location in memory in order + * to signal completion of the request. Both pages have the same + * layout, so it is easiest to handle both with a single function. + */ static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + int inject_type; + hwaddr pg_offset_mask = (1ull << xive->ic_shift) - 1; - switch (offset) { + /* adjust offset for inject page */ + hwaddr adj_offset = offset & pg_offset_mask; + + switch (adj_offset) { case PNV_XIVE2_SYNC_IPI: + inject_type = PNV_XIVE2_QUEUE_IPI; + break; case PNV_XIVE2_SYNC_HW: + inject_type = PNV_XIVE2_QUEUE_HW; + break; case PNV_XIVE2_SYNC_NxC: + inject_type = PNV_XIVE2_QUEUE_NXC; + break; case PNV_XIVE2_SYNC_INT: + inject_type = PNV_XIVE2_QUEUE_INT; + break; case PNV_XIVE2_SYNC_OS_ESC: + inject_type = PNV_XIVE2_QUEUE_OS; + break; case PNV_XIVE2_SYNC_POOL_ESC: + inject_type = PNV_XIVE2_QUEUE_POOL; + break; case PNV_XIVE2_SYNC_HARD_ESC: + inject_type = PNV_XIVE2_QUEUE_HARD; + break; + case PNV_XIVE2_SYNC_NXC_LD_LCL_NCO: + inject_type = PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO; + break; + case PNV_XIVE2_SYNC_NXC_LD_LCL_CO: + inject_type = PNV_XIVE2_QUEUE_NXC_LD_LCL_CO; + break; + case PNV_XIVE2_SYNC_NXC_ST_LCL_NCI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI; + break; + case PNV_XIVE2_SYNC_NXC_ST_LCL_CI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_LCL_CI; + break; + case PNV_XIVE2_SYNC_NXC_ST_RMT_NCI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI; + break; + case PNV_XIVE2_SYNC_NXC_ST_RMT_CI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_CI; break; default: xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset); + return; + } + + /* Write Queue Sync notification byte if writing to sync inject page */ + if ((offset & ~pg_offset_mask) != 0) { + pnv_xive2_inject_notify(xive, inject_type); } } diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index f8e4a677c6..ca05255d20 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -232,6 +232,10 @@ #define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* ESBC cache flush inject register */ +#define X_VC_ESBC_FLUSH_INJECT 0x142 +#define VC_ESBC_FLUSH_INJECT 0x210 + /* ESBC configuration */ #define X_VC_ESBC_CFG 0x148 #define VC_ESBC_CFG 0x240 @@ -250,6 +254,10 @@ #define VC_EASC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_EASC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* EASC flush inject register */ +#define X_VC_EASC_FLUSH_INJECT 0x162 +#define VC_EASC_FLUSH_INJECT 0x310 + /* * VC2 */ @@ -270,6 +278,10 @@ #define VC_ENDC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define VC_ENDC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* ENDC flush inject register */ +#define X_VC_ENDC_FLUSH_INJECT 0x182 +#define VC_ENDC_FLUSH_INJECT 0x410 + /* ENDC Sync done */ #define X_VC_ENDC_SYNC_DONE 0x184 #define VC_ENDC_SYNC_DONE 0x420 @@ -403,6 +415,10 @@ #define PC_NXC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define PC_NXC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* NxC Cache flush inject */ +#define X_PC_NXC_FLUSH_INJECT 0x282 +#define PC_NXC_FLUSH_INJECT 0x410 + /* NxC Cache watch assign */ #define X_PC_NXC_WATCH_ASSIGN 0x286 #define PC_NXC_WATCH_ASSIGN 0x430 diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index de34cbdc96..24ce37a9c8 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -139,6 +139,7 @@ struct Pnv10Chip { #define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) #define PNV10_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) +#define PNV10_PIR2THREAD(pir) (((pir) & 0x7f)) struct PnvChipClass { /*< private >*/ From d6d5f5c0347b124319ff9c0a43358bdae1d7ea26 Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:23 -0500 Subject: [PATCH 2525/2884] pnv/xive2: Add NVG and NVC to cache watch facility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cache watch facility uses the same register interface to handle entries in the NVP, NVG and NVC tables. A bit-field in the 'watchX specification' register tells the table type. So far, that bit-field was not read and the code assumed a read/write to the NVP table. This patch allows to read/write entries in the NVG and NVC table as well. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 49 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 3dbbfddacb..561e61682e 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -465,10 +465,30 @@ static int pnv_xive2_write_nvp(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_nvp_update(PnvXive2 *xive, uint8_t watch_engine) +static int pnv_xive2_nxc_to_table_type(uint8_t nxc_type, uint32_t *table_type) { - uint8_t blk; - uint32_t idx; + switch (nxc_type) { + case PC_NXC_WATCH_NXC_NVP: + *table_type = VST_NVP; + break; + case PC_NXC_WATCH_NXC_NVG: + *table_type = VST_NVG; + break; + case PC_NXC_WATCH_NXC_NVC: + *table_type = VST_NVC; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid table type for nxc operation\n"); + return -1; + } + return 0; +} + +static int pnv_xive2_nxc_update(PnvXive2 *xive, uint8_t watch_engine) +{ + uint8_t blk, nxc_type; + uint32_t idx, table_type = -1; int i, spec_reg, data_reg; uint64_t nxc_watch[4]; @@ -476,21 +496,24 @@ static int pnv_xive2_nvp_update(PnvXive2 *xive, uint8_t watch_engine) spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + nxc_type = GETFIELD(PC_NXC_WATCH_NXC_TYPE, xive->pc_regs[spec_reg]); blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); + assert(!pnv_xive2_nxc_to_table_type(nxc_type, &table_type)); + for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { nxc_watch[i] = cpu_to_be64(xive->pc_regs[data_reg + i]); } - return pnv_xive2_vst_write(xive, VST_NVP, blk, idx, nxc_watch, + return pnv_xive2_vst_write(xive, table_type, blk, idx, nxc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_nvp_cache_load(PnvXive2 *xive, uint8_t watch_engine) +static void pnv_xive2_nxc_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk; - uint32_t idx; + uint8_t blk, nxc_type; + uint32_t idx, table_type = -1; uint64_t nxc_watch[4] = { 0 }; int i, spec_reg, data_reg; @@ -498,11 +521,15 @@ static void pnv_xive2_nvp_cache_load(PnvXive2 *xive, uint8_t watch_engine) spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + nxc_type = GETFIELD(PC_NXC_WATCH_NXC_TYPE, xive->pc_regs[spec_reg]); blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); - if (pnv_xive2_vst_read(xive, VST_NVP, blk, idx, nxc_watch)) { - xive2_error(xive, "VST: no NVP entry %x/%x !?", blk, idx); + assert(!pnv_xive2_nxc_to_table_type(nxc_type, &table_type)); + + if (pnv_xive2_vst_read(xive, table_type, blk, idx, nxc_watch)) { + xive2_error(xive, "VST: no NXC entry %x/%x in %s table!?", + blk, idx, vst_infos[table_type].name); } for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { @@ -1432,7 +1459,7 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, * SPEC register */ watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; - pnv_xive2_nvp_cache_load(xive, watch_engine); + pnv_xive2_nxc_cache_load(xive, watch_engine); val = xive->pc_regs[reg]; break; @@ -1506,7 +1533,7 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, /* writing to DATA0 triggers the cache write */ watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; xive->pc_regs[reg] = val; - pnv_xive2_nvp_update(xive, watch_engine); + pnv_xive2_nxc_update(xive, watch_engine); break; /* case PC_NXC_FLUSH_CTRL: */ From 1775b7d1091452dab24ef23ddc1b7c1943a5e9e4 Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:24 -0500 Subject: [PATCH 2526/2884] pnv/xive2: Configure Virtualization Structure Tables through the PC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both the virtualization layer (VC) and presentation layer (PC) need to be configured to access the VSTs. Since the information is redundant, the xive model combines both into one set of tables and only the definitions going through the VC are kept. The definitions through the PC are ignored. That works well as long as firmware calls the VC for all the tables. For the NVG and NVC tables, it can make sense to only configure them with the PC, since they are only used by the presenter. So this patch allows firmware to configure the VST tables through the PC as well. The definitions are still shared, since the VST tables can be set through both the VC and/or PC, they are dynamically re-mapped in memory by first deleting the memory subregion. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 47 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 561e61682e..33e76633b5 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -762,6 +762,9 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, * entries provisioned by FW (such as skiboot) and resize the * ESB window accordingly. */ + if (memory_region_is_mapped(&xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->esb_mmio, &xsrc->esb_mmio); + } if (!(VSD_INDIRECT & vsd)) { memory_region_set_size(&xsrc->esb_mmio, vst_tsize * SBE_PER_BYTE * (1ull << xsrc->esb_shift)); @@ -777,6 +780,9 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, /* * Backing store pages for the END. */ + if (memory_region_is_mapped(&end_xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->end_mmio, &end_xsrc->esb_mmio); + } if (!(VSD_INDIRECT & vsd)) { memory_region_set_size(&end_xsrc->esb_mmio, (vst_tsize / info->size) * (1ull << end_xsrc->esb_shift)); @@ -801,13 +807,10 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, * Both PC and VC sub-engines are configured as each use the Virtual * Structure Tables */ -static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) +static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd, + uint8_t type, uint8_t blk) { uint8_t mode = GETFIELD(VSD_MODE, vsd); - uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, - xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); - uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, - xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; if (type > VST_ERQ) { @@ -842,6 +845,16 @@ static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) } } +static void pnv_xive2_vc_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + + pnv_xive2_vst_set_data(xive, vsd, type, blk); +} + /* * MMIO handlers */ @@ -1271,7 +1284,7 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, case VC_VSD_TABLE_ADDR: break; case VC_VSD_TABLE_DATA: - pnv_xive2_vst_set_data(xive, val); + pnv_xive2_vc_vst_set_data(xive, val); break; /* @@ -1490,6 +1503,16 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, return val; } +static void pnv_xive2_pc_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t type = GETFIELD(PC_VSD_TABLE_SELECT, + xive->pc_regs[PC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(PC_VSD_TABLE_ADDRESS, + xive->pc_regs[PC_VSD_TABLE_ADDR >> 3]); + + pnv_xive2_vst_set_data(xive, vsd, type, blk); +} + static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { @@ -1500,12 +1523,18 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, switch (offset) { /* - * VSD table settings. Only taken into account in the VC - * sub-engine because the Xive2Router model combines both VC and PC - * sub-engines + * VSD table settings. + * The Xive2Router model combines both VC and PC sub-engines. We + * allow to configure the tables through both, for the rare cases + * where a table only really needs to be configured for one of + * them (e.g. the NVG table for the presenter). It assumes that + * firmware passes the same address to the VC and PC when tables + * are defined for both, which seems acceptable. */ case PC_VSD_TABLE_ADDR: + break; case PC_VSD_TABLE_DATA: + pnv_xive2_pc_vst_set_data(xive, val); break; case PC_NXC_PROC_CONFIG: From 9d7188a2ba6e520934691612915afb98c10823c5 Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:25 -0500 Subject: [PATCH 2527/2884] pnv/xive2: Enable VST NVG and NVC index compression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable NVG and NVC VST tables for index compression which indicates the number of bits the address is shifted to the right for the table accesses. The compression values are defined as: 0000 - No compression 0001 - 1 bit shift 0010 - 2 bit shift .... 1000 - 8 bit shift 1001-1111 - No compression Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 20 ++++++++++++++++++++ hw/intc/pnv_xive2_regs.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 33e76633b5..c3b5bfe61f 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -217,6 +217,20 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, return pnv_xive2_vst_addr_direct(xive, type, vsd, (idx % vst_per_page)); } +static uint8_t pnv_xive2_nvc_table_compress_shift(PnvXive2 *xive) +{ + uint8_t shift = GETFIELD(PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + return shift > 8 ? 0 : shift; +} + +static uint8_t pnv_xive2_nvg_table_compress_shift(PnvXive2 *xive) +{ + uint8_t shift = GETFIELD(PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + return shift > 8 ? 0 : shift; +} + static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, uint32_t idx) { @@ -238,6 +252,12 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0; } + if (type == VST_NVG) { + idx >>= pnv_xive2_nvg_table_compress_shift(xive); + } else if (type == VST_NVC) { + idx >>= pnv_xive2_nvc_table_compress_shift(xive); + } + if (VSD_INDIRECT & vsd) { return pnv_xive2_vst_addr_indirect(xive, type, vsd, idx); } diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index ca05255d20..e8b87b3d2c 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -427,6 +427,8 @@ #define X_PC_NXC_PROC_CONFIG 0x28A #define PC_NXC_PROC_CONFIG 0x450 #define PC_NXC_PROC_CONFIG_WATCH_ASSIGN PPC_BITMASK(0, 3) +#define PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS PPC_BITMASK(32, 35) +#define PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS PPC_BITMASK(36, 39) /* NxC Cache Watch 0 Specification */ #define X_PC_NXC_WATCH0_SPEC 0x2A0 From 4c81813e25d24ece49141572ad5f07d8efe7bf4d Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:26 -0500 Subject: [PATCH 2528/2884] pnv/xive2: Set Translation Table for the NVC port space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set Translation Table for the NVC port space is missing. The xive model doesn't take into account the remapping of IO operations via the Set Translation Table but firmware is allowed to define it for the Notify Virtual Crowd (NVC), like it's already done for the other VST tables. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index c3b5bfe61f..08b9166a09 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -722,6 +722,7 @@ static int pnv_xive2_stt_set_data(PnvXive2 *xive, uint64_t val) case CQ_TAR_NVPG: case CQ_TAR_ESB: case CQ_TAR_END: + case CQ_TAR_NVC: xive->tables[tsel][entry] = val; break; default: From fa414eb6655228e274811ade0c7bcddb88acaee5 Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:27 -0500 Subject: [PATCH 2529/2884] pnv/xive2: Fail VST entry address computation if table has no VSD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fail VST entry address computation if firmware doesn't define a descriptor for one of the Virtualization Structure Tables (VST), there's no point in trying to compute the address of its entry. Abort the operation and log an error. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 08b9166a09..9fbd44f974 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -244,6 +244,11 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, } vsd = xive->vsds[type][blk]; + if (vsd == 0) { + xive2_error(xive, "VST: vsd == 0 block id %d for VST %s %d !?", + blk, info->name, idx); + return 0; + } /* Remote VST access */ if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { From 96c674bf08365ae0ffa2b960a12718bf2ca90079 Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:28 -0500 Subject: [PATCH 2530/2884] pnv/xive2: Move xive2_nvp_pic_print_info() to xive2.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving xive2_nvp_pic_print_info() to align with the other "pic_print_info" functions. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/pnv_xive2.c | 27 --------------------------- hw/intc/xive2.c | 26 ++++++++++++++++++++++++++ include/hw/ppc/xive2_regs.h | 2 ++ 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 9fbd44f974..78609105a8 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2436,33 +2436,6 @@ static void pnv_xive2_register_types(void) type_init(pnv_xive2_register_types) -static void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, - GString *buf) -{ - uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); - uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); - - if (!xive2_nvp_is_valid(nvp)) { - return; - } - - g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x", - nvp_idx, eq_blk, eq_idx, - xive_get_field32(NVP2_W2_IPB, nvp->w2)); - /* - * When the NVP is HW controlled, more fields are updated - */ - if (xive2_nvp_is_hw(nvp)) { - g_string_append_printf(buf, " CPPR:%02x", - xive_get_field32(NVP2_W2_CPPR, nvp->w2)); - if (xive2_nvp_is_co(nvp)) { - g_string_append_printf(buf, " CO:%04x", - xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); - } - } - g_string_append_c(buf, '\n'); -} - /* * If the table is direct, we can compute the number of PQ entries * provisioned by FW. diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 3e7238c663..ac914b3d1c 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -137,6 +137,32 @@ void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } +void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf) +{ + uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); + uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); + + if (!xive2_nvp_is_valid(nvp)) { + return; + } + + g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x", + nvp_idx, eq_blk, eq_idx, + xive_get_field32(NVP2_W2_IPB, nvp->w2)); + /* + * When the NVP is HW controlled, more fields are updated + */ + if (xive2_nvp_is_hw(nvp)) { + g_string_append_printf(buf, " CPPR:%02x", + xive_get_field32(NVP2_W2_CPPR, nvp->w2)); + if (xive2_nvp_is_co(nvp)) { + g_string_append_printf(buf, " CO:%04x", + xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); + } + } + g_string_append_c(buf, '\n'); +} + static void xive2_end_enqueue(Xive2End *end, uint32_t data) { uint64_t qaddr_base = xive2_end_qaddr(end); diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 4e5e17cd89..ec5d6ec2d6 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -194,6 +194,8 @@ static inline uint32_t xive2_nvp_blk(uint32_t cam_line) return (cam_line >> XIVE2_NVP_SHIFT) & 0xf; } +void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf); + /* * Notification Virtual Group or Crowd (NVG/NVC) */ From 6adb007357752ff665fde7dd43e5e0afabe7dcbc Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:29 -0500 Subject: [PATCH 2531/2884] pnv/xive2: Refine TIMA 'info pic' output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In XIVE Gen 2 there were some minor changes to the TIMA header that were updated when printed. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/xive.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 70f11f993b..5a02dd8e02 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -692,9 +692,15 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf) } } - g_string_append_printf(buf, "CPU[%04x]: " - "QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR W2\n", - cpu_index); + if (xive_presenter_get_config(tctx->xptr) & XIVE_PRESENTER_GEN1_TIMA_OS) { + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR" + " W2\n", cpu_index); + } else { + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB - LGS T PIPR" + " W2\n", cpu_index); + } for (i = 0; i < XIVE_TM_RING_COUNT; i++) { char *s = xive_tctx_ring_print(&tctx->regs[i * XIVE_TM_RING_SIZE]); From 5fc9c71724559be273ee1b68c65ffe45e1386e3c Mon Sep 17 00:00:00 2001 From: Frederic Barrat <fbarrat@linux.ibm.com> Date: Wed, 24 Jul 2024 16:21:30 -0500 Subject: [PATCH 2532/2884] pnv/xive2: Dump more END state with 'info pic' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additional END state 'info pic' information as added. The 'ignore', 'crowd' and 'precluded escalation control' bits of an Event Notification Descriptor are all used when delivering an interrupt targeting a VP-group or crowd. Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.vnet.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/intc/xive2.c | 7 +++++-- include/hw/ppc/xive2_regs.h | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index ac914b3d1c..1f150685bf 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -89,7 +89,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) pq = xive_get_field32(END2_W1_ESn, end->w1); g_string_append_printf(buf, - " %08x %c%c %c%c%c%c%c%c%c%c%c%c " + " %08x %c%c %c%c%c%c%c%c%c%c%c%c%c %c%c " "prio:%d nvp:%02x/%04x", end_idx, pq & XIVE_ESB_VAL_P ? 'P' : '-', @@ -98,12 +98,15 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) xive2_end_is_enqueue(end) ? 'q' : '-', xive2_end_is_notify(end) ? 'n' : '-', xive2_end_is_backlog(end) ? 'b' : '-', + xive2_end_is_precluded_escalation(end) ? 'p' : '-', xive2_end_is_escalate(end) ? 'e' : '-', xive2_end_is_escalate_end(end) ? 'N' : '-', xive2_end_is_uncond_escalation(end) ? 'u' : '-', xive2_end_is_silent_escalation(end) ? 's' : '-', xive2_end_is_firmware1(end) ? 'f' : '-', xive2_end_is_firmware2(end) ? 'F' : '-', + xive2_end_is_ignore(end) ? 'i' : '-', + xive2_end_is_crowd(end) ? 'c' : '-', priority, nvp_blk, nvp_idx); if (qaddr_base) { @@ -676,7 +679,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, - xive_get_field32(END2_W6_IGNORE, end.w7), + xive2_end_is_ignore(&end), priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7)); diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index ec5d6ec2d6..4349d009d0 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -97,6 +97,7 @@ typedef struct Xive2End { uint32_t w6; #define END2_W6_FORMAT_BIT PPC_BIT32(0) #define END2_W6_IGNORE PPC_BIT32(1) +#define END2_W6_CROWD PPC_BIT32(2) #define END2_W6_VP_BLOCK PPC_BITMASK32(4, 7) #define END2_W6_VP_OFFSET PPC_BITMASK32(8, 31) #define END2_W6_VP_OFFSET_GEN1 PPC_BITMASK32(13, 31) @@ -111,6 +112,8 @@ typedef struct Xive2End { #define xive2_end_is_notify(end) \ (be32_to_cpu((end)->w0) & END2_W0_UCOND_NOTIFY) #define xive2_end_is_backlog(end) (be32_to_cpu((end)->w0) & END2_W0_BACKLOG) +#define xive2_end_is_precluded_escalation(end) \ + (be32_to_cpu((end)->w0) & END2_W0_PRECL_ESC_CTL) #define xive2_end_is_escalate(end) \ (be32_to_cpu((end)->w0) & END2_W0_ESCALATE_CTL) #define xive2_end_is_uncond_escalation(end) \ @@ -123,6 +126,10 @@ typedef struct Xive2End { (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE1) #define xive2_end_is_firmware2(end) \ (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE2) +#define xive2_end_is_ignore(end) \ + (be32_to_cpu((end)->w6) & END2_W6_IGNORE) +#define xive2_end_is_crowd(end) \ + (be32_to_cpu((end)->w6) & END2_W6_CROWD) static inline uint64_t xive2_end_qaddr(Xive2End *end) { From a7e10fab78d91d7d0dee60ce1e4d1b28365d570f Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Thu, 23 May 2024 15:14:53 +0530 Subject: [PATCH 2533/2884] target/ppc: Move VMX integer add/sub saturate insns to decodetree. Moving the following instructions to decodetree specification : v{add,sub}{u,s}{b,h,w}s : VX-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/helper.h | 24 +-- target/ppc/insn32.decode | 16 ++ target/ppc/int_helper.c | 22 +-- target/ppc/translate/vmx-impl.c.inc | 238 ++++++++++++++++++++-------- target/ppc/translate/vmx-ops.c.inc | 19 +-- 5 files changed, 220 insertions(+), 99 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 4fa089cbf9..dd7526bc2e 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -203,18 +203,18 @@ DEF_HELPER_FLAGS_3(vsro, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vsrv, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vslv, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VPRTYBQ, TCG_CALL_NO_RWG, void, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddubs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vadduhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vadduws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsububs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubuhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubuws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDSBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDSHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDSWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBUBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBUHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBUWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_3(VADDUQM, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_4(VADDECUQ, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) DEF_HELPER_FLAGS_4(VADDEUQM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index ee33141476..8988fca60e 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -832,6 +832,14 @@ VADDCUW 000100 ..... ..... ..... 00110000000 @VX VADDCUQ 000100 ..... ..... ..... 00101000000 @VX VADDUQM 000100 ..... ..... ..... 00100000000 @VX +VADDSBS 000100 ..... ..... ..... 01100000000 @VX +VADDSHS 000100 ..... ..... ..... 01101000000 @VX +VADDSWS 000100 ..... ..... ..... 01110000000 @VX + +VADDUBS 000100 ..... ..... ..... 01000000000 @VX +VADDUHS 000100 ..... ..... ..... 01001000000 @VX +VADDUWS 000100 ..... ..... ..... 01010000000 @VX + VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA @@ -839,6 +847,14 @@ VSUBCUW 000100 ..... ..... ..... 10110000000 @VX VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX VSUBUQM 000100 ..... ..... ..... 10100000000 @VX +VSUBSBS 000100 ..... ..... ..... 11100000000 @VX +VSUBSHS 000100 ..... ..... ..... 11101000000 @VX +VSUBSWS 000100 ..... ..... ..... 11110000000 @VX + +VSUBUBS 000100 ..... ..... ..... 11000000000 @VX +VSUBUHS 000100 ..... ..... ..... 11001000000 @VX +VSUBUWS 000100 ..... ..... ..... 11010000000 @VX + VSUBECUQ 000100 ..... ..... ..... ..... 111111 @VA VSUBEUQM 000100 ..... ..... ..... ..... 111110 @VA diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 2c6b633d65..ef4b2e75d6 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -541,7 +541,7 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } #define VARITHSAT_DO(name, op, optype, cvt, element) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ + void helper_V##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ ppc_avr_t *a, ppc_avr_t *b, uint32_t desc) \ { \ int sat = 0; \ @@ -555,17 +555,17 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } \ } #define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element) + VARITHSAT_DO(ADDS##suffix##S, +, optype, cvt, element) \ + VARITHSAT_DO(SUBS##suffix##S, -, optype, cvt, element) #define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element) -VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb) -VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh) -VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw) -VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub) -VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh) -VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) + VARITHSAT_DO(ADDU##suffix##S, +, optype, cvt, element) \ + VARITHSAT_DO(SUBU##suffix##S, -, optype, cvt, element) +VARITHSAT_SIGNED(B, s8, int16_t, cvtshsb) +VARITHSAT_SIGNED(H, s16, int32_t, cvtswsh) +VARITHSAT_SIGNED(W, s32, int64_t, cvtsdsw) +VARITHSAT_UNSIGNED(B, u8, uint16_t, cvtshub) +VARITHSAT_UNSIGNED(H, u16, uint32_t, cvtswuh) +VARITHSAT_UNSIGNED(W, u32, uint64_t, cvtsduw) #undef VARITHSAT_CASE #undef VARITHSAT_DO #undef VARITHSAT_SIGNED diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 8084af75cc..fdb283c1d4 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -1047,58 +1047,6 @@ TRANS(VRLQ, do_vector_rotl_quad, false, false) TRANS(VRLQNM, do_vector_rotl_quad, true, false) TRANS(VRLQMI, do_vector_rotl_quad, false, true) -#define GEN_VXFORM_SAT(NAME, VECE, NORM, SAT, OPC2, OPC3) \ -static void glue(glue(gen_, NAME), _vec)(unsigned vece, TCGv_vec t, \ - TCGv_vec sat, TCGv_vec a, \ - TCGv_vec b) \ -{ \ - TCGv_vec x = tcg_temp_new_vec_matching(t); \ - glue(glue(tcg_gen_, NORM), _vec)(VECE, x, a, b); \ - glue(glue(tcg_gen_, SAT), _vec)(VECE, t, a, b); \ - tcg_gen_cmp_vec(TCG_COND_NE, VECE, x, x, t); \ - tcg_gen_or_vec(VECE, sat, sat, x); \ -} \ -static void glue(gen_, NAME)(DisasContext *ctx) \ -{ \ - static const TCGOpcode vecop_list[] = { \ - glue(glue(INDEX_op_, NORM), _vec), \ - glue(glue(INDEX_op_, SAT), _vec), \ - INDEX_op_cmp_vec, 0 \ - }; \ - static const GVecGen4 g = { \ - .fniv = glue(glue(gen_, NAME), _vec), \ - .fno = glue(gen_helper_, NAME), \ - .opt_opc = vecop_list, \ - .write_aofs = true, \ - .vece = VECE, \ - }; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - tcg_gen_gvec_4(avr_full_offset(rD(ctx->opcode)), \ - offsetof(CPUPPCState, vscr_sat), \ - avr_full_offset(rA(ctx->opcode)), \ - avr_full_offset(rB(ctx->opcode)), \ - 16, 16, &g); \ -} - -GEN_VXFORM_SAT(vaddubs, MO_8, add, usadd, 0, 8); -GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ - vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) -GEN_VXFORM_SAT(vadduhs, MO_16, add, usadd, 0, 9); -GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ - vmul10euq, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_SAT(vadduws, MO_32, add, usadd, 0, 10); -GEN_VXFORM_SAT(vaddsbs, MO_8, add, ssadd, 0, 12); -GEN_VXFORM_SAT(vaddshs, MO_16, add, ssadd, 0, 13); -GEN_VXFORM_SAT(vaddsws, MO_32, add, ssadd, 0, 14); -GEN_VXFORM_SAT(vsububs, MO_8, sub, ussub, 0, 24); -GEN_VXFORM_SAT(vsubuhs, MO_16, sub, ussub, 0, 25); -GEN_VXFORM_SAT(vsubuws, MO_32, sub, ussub, 0, 26); -GEN_VXFORM_SAT(vsubsbs, MO_8, sub, sssub, 0, 28); -GEN_VXFORM_SAT(vsubshs, MO_16, sub, sssub, 0, 29); -GEN_VXFORM_SAT(vsubsws, MO_32, sub, sssub, 0, 30); GEN_VXFORM_TRANS(vsl, 2, 7); GEN_VXFORM_TRANS(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); @@ -2641,26 +2589,14 @@ static void gen_xpnd04_2(DisasContext *ctx) } } - -GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \ - xpnd04_2, PPC_NONE, PPC2_ISA300) - GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \ bcdadd, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vsububs, PPC_ALTIVEC, PPC_NONE, \ - bcdadd, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ bcdsub, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ - bcdsub, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vaddshs, PPC_ALTIVEC, PPC_NONE, \ - bcdcpsgn, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubudm, PPC2_ALTIVEC_207, PPC_NONE, \ bcds, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \ bcdus, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \ - bcdtrunc, PPC_NONE, PPC2_ISA300) static void gen_vsbox(DisasContext *ctx) { @@ -2937,6 +2873,180 @@ static bool do_vx_vaddsubcuw(DisasContext *ctx, arg_VX *a, int add) TRANS(VSUBCUW, do_vx_vaddsubcuw, 0) TRANS(VADDCUW, do_vx_vaddsubcuw, 1) +/* Integer Add/Sub Saturate Instructions */ +static inline void do_vadd_vsub_sat +( + unsigned vece, TCGv_vec t, TCGv_vec sat, TCGv_vec a, TCGv_vec b, + void (*norm_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec), + void (*sat_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + norm_op(vece, x, a, b); + sat_op(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); +} + +static void gen_vadd_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_add_vec, tcg_gen_usadd_vec); +} + +static void gen_vadd_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_add_vec, tcg_gen_ssadd_vec); +} + +static void gen_vsub_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_sub_vec, tcg_gen_ussub_vec); +} + +static void gen_vsub_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_sub_vec, tcg_gen_sssub_vec); +} + +/* + * Signed/Unsigned add/sub helper ops for byte/halfword/word + * GVecGen4 struct variants. + */ +static const TCGOpcode vecop_list_sub_u[] = { + INDEX_op_sub_vec, INDEX_op_ussub_vec, INDEX_op_cmp_vec, 0 +}; +static const TCGOpcode vecop_list_sub_s[] = { + INDEX_op_sub_vec, INDEX_op_sssub_vec, INDEX_op_cmp_vec, 0 +}; +static const TCGOpcode vecop_list_add_u[] = { + INDEX_op_add_vec, INDEX_op_usadd_vec, INDEX_op_cmp_vec, 0 +}; +static const TCGOpcode vecop_list_add_s[] = { + INDEX_op_add_vec, INDEX_op_ssadd_vec, INDEX_op_cmp_vec, 0 +}; + +static const GVecGen4 op_vsububs = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUBS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vaddubs = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUBS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vsubuhs = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUHS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vadduhs = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUHS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vsubuws = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUWS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vadduws = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUWS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vsubsbs = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSBS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vaddsbs = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSBS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vsubshs = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSHS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vaddshs = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSHS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vsubsws = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSWS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vaddsws = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSWS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_32 +}; + +static bool do_vx_vadd_vsub_sat(DisasContext *ctx, arg_VX *a, const GVecGen4 *op) +{ + REQUIRE_VECTOR(ctx); + tcg_gen_gvec_4(avr_full_offset(a->vrt), offsetof(CPUPPCState, vscr_sat), + avr_full_offset(a->vra), avr_full_offset(a->vrb), + 16, 16, op); + + return true; +} + +TRANS_FLAGS(ALTIVEC, VSUBUBS, do_vx_vadd_vsub_sat, &op_vsububs) +TRANS_FLAGS(ALTIVEC, VSUBUHS, do_vx_vadd_vsub_sat, &op_vsubuhs) +TRANS_FLAGS(ALTIVEC, VSUBUWS, do_vx_vadd_vsub_sat, &op_vsubuws) +TRANS_FLAGS(ALTIVEC, VSUBSBS, do_vx_vadd_vsub_sat, &op_vsubsbs) +TRANS_FLAGS(ALTIVEC, VSUBSHS, do_vx_vadd_vsub_sat, &op_vsubshs) +TRANS_FLAGS(ALTIVEC, VSUBSWS, do_vx_vadd_vsub_sat, &op_vsubsws) +TRANS_FLAGS(ALTIVEC, VADDUBS, do_vx_vadd_vsub_sat, &op_vaddubs) +TRANS_FLAGS(ALTIVEC, VADDUHS, do_vx_vadd_vsub_sat, &op_vadduhs) +TRANS_FLAGS(ALTIVEC, VADDUWS, do_vx_vadd_vsub_sat, &op_vadduws) +TRANS_FLAGS(ALTIVEC, VADDSBS, do_vx_vadd_vsub_sat, &op_vaddsbs) +TRANS_FLAGS(ALTIVEC, VADDSHS, do_vx_vadd_vsub_sat, &op_vaddshs) +TRANS_FLAGS(ALTIVEC, VADDSWS, do_vx_vadd_vsub_sat, &op_vaddsws) + static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 7bb11b0549..e28958a126 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -54,18 +54,13 @@ GEN_VXFORM(vsro, 6, 17), GEN_VXFORM(xpnd04_1, 0, 22), GEN_VXFORM_300(bcdsr, 0, 23), GEN_VXFORM_300(bcdsr, 0, 31), -GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vadduws, 0, 10), -GEN_VXFORM(vaddsbs, 0, 12), -GEN_VXFORM_DUAL(vaddshs, bcdcpsgn, 0, 13, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vaddsws, 0, 14), -GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vsubuws, 0, 26), -GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_ALTIVEC, PPC2_ISA300), -GEN_VXFORM(vsubshs, 0, 29), -GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_300_EXT(vmul10uq, 0, 8, 0x0000F800), +GEN_VXFORM_300(vmul10euq, 0, 9), +GEN_VXFORM_300(bcdcpsgn, 0, 13), +GEN_VXFORM_207(bcdadd, 0, 24), +GEN_VXFORM_207(bcdsub, 0, 25), +GEN_VXFORM_300(bcdtrunc, 0, 28), +GEN_VXFORM_300(xpnd04_2, 0, 30), GEN_VXFORM_300(bcdtrunc, 0, 20), GEN_VXFORM_300(bcdutrunc, 0, 21), GEN_VXFORM(vsl, 2, 7), From 8fc7b63adaa860c81119f6f8cd6cc981504bfb7b Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Thu, 23 May 2024 15:14:54 +0530 Subject: [PATCH 2534/2884] target/ppc: Improve VMX integer add/sub saturate instructions. No need for a full comparison; xor produces non-zero bits for QC just fine. Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Chinmay Rath <rath.chinmay@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/translate/vmx-impl.c.inc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index fdb283c1d4..152bcde0e3 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -2876,15 +2876,15 @@ TRANS(VADDCUW, do_vx_vaddsubcuw, 1) /* Integer Add/Sub Saturate Instructions */ static inline void do_vadd_vsub_sat ( - unsigned vece, TCGv_vec t, TCGv_vec sat, TCGv_vec a, TCGv_vec b, + unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b, void (*norm_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec), void (*sat_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) { TCGv_vec x = tcg_temp_new_vec_matching(t); norm_op(vece, x, a, b); sat_op(vece, t, a, b); - tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); - tcg_gen_or_vec(vece, sat, sat, x); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); } static void gen_vadd_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, @@ -2916,16 +2916,16 @@ static void gen_vsub_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, * GVecGen4 struct variants. */ static const TCGOpcode vecop_list_sub_u[] = { - INDEX_op_sub_vec, INDEX_op_ussub_vec, INDEX_op_cmp_vec, 0 + INDEX_op_sub_vec, INDEX_op_ussub_vec, 0 }; static const TCGOpcode vecop_list_sub_s[] = { - INDEX_op_sub_vec, INDEX_op_sssub_vec, INDEX_op_cmp_vec, 0 + INDEX_op_sub_vec, INDEX_op_sssub_vec, 0 }; static const TCGOpcode vecop_list_add_u[] = { - INDEX_op_add_vec, INDEX_op_usadd_vec, INDEX_op_cmp_vec, 0 + INDEX_op_add_vec, INDEX_op_usadd_vec, 0 }; static const TCGOpcode vecop_list_add_s[] = { - INDEX_op_add_vec, INDEX_op_ssadd_vec, INDEX_op_cmp_vec, 0 + INDEX_op_add_vec, INDEX_op_ssadd_vec, 0 }; static const GVecGen4 op_vsububs = { From 48eda6abfd7ebf3a21c699d8b13d7506b877d1b7 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Thu, 23 May 2024 15:18:19 +0530 Subject: [PATCH 2535/2884] target/ppc: Move ISA300 flag check out of do_helper_XX3. Moving PPC2_ISA300 flag check out of do_helper_XX3 method in vmx-impl.c.inc so that the helper can be used with other instructions as well. Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/translate/vsx-impl.c.inc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 0266f09119..6025119e5b 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2712,8 +2712,6 @@ static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) { TCGv_ptr xt, xa, xb; - - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_VSX(ctx); xt = gen_vsr_ptr(a->xt); @@ -2724,13 +2722,13 @@ static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, return true; } -TRANS(XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) -TRANS(XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) -TRANS(XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) -TRANS(XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) -TRANS(XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) -TRANS(XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) -TRANS(XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) +TRANS_FLAGS2(ISA300, XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) +TRANS_FLAGS2(ISA300, XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) +TRANS_FLAGS2(ISA300, XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) +TRANS_FLAGS2(ISA300, XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) +TRANS_FLAGS2(ISA300, XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) +TRANS_FLAGS2(ISA300, XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) +TRANS_FLAGS2(ISA300, XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) static bool do_helper_X(arg_X *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) From 638f6d553af70ae5a7dc26200d35e385d649ff75 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Thu, 23 May 2024 15:18:20 +0530 Subject: [PATCH 2536/2884] target/ppc: Move VSX arithmetic and max/min insns to decodetree. Moving the following instructions to decodetree specification: x{s, v}{add, sub, mul, div}{s, d}p : XX3-form xs{max, min}dp, xv{max, min}{s, d}p : XX3-form The changes were verfied by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/fpu_helper.c | 44 ++++++++++---------- target/ppc/helper.h | 44 ++++++++++---------- target/ppc/insn32.decode | 30 ++++++++++++++ target/ppc/translate/vsx-impl.c.inc | 63 +++++++++++++---------------- target/ppc/translate/vsx-ops.c.inc | 22 ---------- 5 files changed, 101 insertions(+), 102 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 51bce99fd5..3f2e4f5827 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -1599,14 +1599,14 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_ADD_SUB(xsadddp, add, 1, float64, VsrD(0), 1, 0) -VSX_ADD_SUB(xsaddsp, add, 1, float64, VsrD(0), 1, 1) -VSX_ADD_SUB(xvadddp, add, 2, float64, VsrD(i), 0, 0) -VSX_ADD_SUB(xvaddsp, add, 4, float32, VsrW(i), 0, 0) -VSX_ADD_SUB(xssubdp, sub, 1, float64, VsrD(0), 1, 0) -VSX_ADD_SUB(xssubsp, sub, 1, float64, VsrD(0), 1, 1) -VSX_ADD_SUB(xvsubdp, sub, 2, float64, VsrD(i), 0, 0) -VSX_ADD_SUB(xvsubsp, sub, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(XSADDDP, add, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(XSADDSP, add, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(XVADDDP, add, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(XVADDSP, add, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(XSSUBDP, sub, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(XSSUBSP, sub, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(XVSUBDP, sub, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(XVSUBSP, sub, 4, float32, VsrW(i), 0, 0) void helper_xsaddqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -1676,10 +1676,10 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_MUL(xsmuldp, 1, float64, VsrD(0), 1, 0) -VSX_MUL(xsmulsp, 1, float64, VsrD(0), 1, 1) -VSX_MUL(xvmuldp, 2, float64, VsrD(i), 0, 0) -VSX_MUL(xvmulsp, 4, float32, VsrW(i), 0, 0) +VSX_MUL(XSMULDP, 1, float64, VsrD(0), 1, 0) +VSX_MUL(XSMULSP, 1, float64, VsrD(0), 1, 1) +VSX_MUL(XVMULDP, 2, float64, VsrD(i), 0, 0) +VSX_MUL(XVMULSP, 4, float32, VsrW(i), 0, 0) void helper_xsmulqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -1750,10 +1750,10 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_DIV(xsdivdp, 1, float64, VsrD(0), 1, 0) -VSX_DIV(xsdivsp, 1, float64, VsrD(0), 1, 1) -VSX_DIV(xvdivdp, 2, float64, VsrD(i), 0, 0) -VSX_DIV(xvdivsp, 4, float32, VsrW(i), 0, 0) +VSX_DIV(XSDIVDP, 1, float64, VsrD(0), 1, 0) +VSX_DIV(XSDIVSP, 1, float64, VsrD(0), 1, 1) +VSX_DIV(XVDIVDP, 2, float64, VsrD(i), 0, 0) +VSX_DIV(XVDIVSP, 4, float32, VsrW(i), 0, 0) void helper_xsdivqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -2383,12 +2383,12 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, false, GETPC()); \ } -VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, VsrD(0)) -VSX_MAX_MIN(xvmaxdp, maxnum, 2, float64, VsrD(i)) -VSX_MAX_MIN(xvmaxsp, maxnum, 4, float32, VsrW(i)) -VSX_MAX_MIN(xsmindp, minnum, 1, float64, VsrD(0)) -VSX_MAX_MIN(xvmindp, minnum, 2, float64, VsrD(i)) -VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(XSMAXDP, maxnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(XVMAXDP, maxnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(XVMAXSP, maxnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(XSMINDP, minnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(XVMINDP, minnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(XVMINSP, minnum, 4, float32, VsrW(i)) #define VSX_MAX_MINC(name, max, tp, fld) \ void helper_##name(CPUPPCState *env, \ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index dd7526bc2e..13f20bb243 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -364,12 +364,12 @@ DEF_HELPER_FLAGS_4(bcdsr, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) DEF_HELPER_FLAGS_4(bcdtrunc, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) DEF_HELPER_FLAGS_4(bcdutrunc, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) -DEF_HELPER_4(xsadddp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSADDDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsaddqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xssubdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmuldp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSSUBDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMULDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsmulqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xsdivdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSDIVDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsdivqp, void, env, i32, vsr, vsr, vsr) DEF_HELPER_3(xsredp, void, env, vsr, vsr) DEF_HELPER_3(xssqrtdp, void, env, vsr, vsr) @@ -392,8 +392,8 @@ DEF_HELPER_4(xscmpodp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpudp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpoqp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpuqp, void, env, i32, vsr, vsr) -DEF_HELPER_4(xsmaxdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmindp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMAXDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMINDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMAXCDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMINCDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMAXJDP, void, env, vsr, vsr, vsr) @@ -439,10 +439,10 @@ DEF_HELPER_4(xsrqpxp, void, env, i32, vsr, vsr) DEF_HELPER_4(xssqrtqp, void, env, i32, vsr, vsr) DEF_HELPER_5(xssubqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xsaddsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xssubsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmulsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsdivsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSADDSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSSUBSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMULSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSDIVSP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xsresp, void, env, vsr, vsr) DEF_HELPER_2(xsrsp, i64, env, i64) DEF_HELPER_3(xssqrtsp, void, env, vsr, vsr) @@ -461,10 +461,10 @@ DEF_HELPER_5(XSNMADDQPO, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(XSNMSUBQP, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(XSNMSUBQPO, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvadddp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvsubdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmuldp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvdivdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVADDDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVSUBDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMULDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVDIVDP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xvredp, void, env, vsr, vsr) DEF_HELPER_3(xvsqrtdp, void, env, vsr, vsr) DEF_HELPER_3(xvrsqrtedp, void, env, vsr, vsr) @@ -474,8 +474,8 @@ DEF_HELPER_5(xvmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvmsubdp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubdp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvmaxdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmindp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMAXDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMINDP, void, env, vsr, vsr, vsr) DEF_HELPER_FLAGS_4(xvcmpeqdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_FLAGS_4(xvcmpgedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_FLAGS_4(xvcmpgtdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) @@ -495,10 +495,10 @@ DEF_HELPER_3(xvrdpim, void, env, vsr, vsr) DEF_HELPER_3(xvrdpip, void, env, vsr, vsr) DEF_HELPER_3(xvrdpiz, void, env, vsr, vsr) -DEF_HELPER_4(xvaddsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvsubsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmulsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvdivsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVADDSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVSUBSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMULSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVDIVSP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xvresp, void, env, vsr, vsr) DEF_HELPER_3(xvsqrtsp, void, env, vsr, vsr) DEF_HELPER_3(xvrsqrtesp, void, env, vsr, vsr) @@ -508,8 +508,8 @@ DEF_HELPER_5(xvmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvmsubsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubsp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvmaxsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvminsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMAXSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMINSP, void, env, vsr, vsr, vsr) DEF_HELPER_FLAGS_4(xvcmpeqsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_FLAGS_4(xvcmpgesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_FLAGS_4(xvcmpgtsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 8988fca60e..1301e5bbc0 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1004,6 +1004,28 @@ XVNEGSP 111100 ..... 00000 ..... 110111001 .. @XX2 XVCPSGNDP 111100 ..... ..... ..... 11110000 ... @XX3 XVCPSGNSP 111100 ..... ..... ..... 11010000 ... @XX3 +## VSX Binary Floating-Point Arithmetic Instructions + +XSADDSP 111100 ..... ..... ..... 00000000 ... @XX3 +XSSUBSP 111100 ..... ..... ..... 00001000 ... @XX3 +XSMULSP 111100 ..... ..... ..... 00010000 ... @XX3 +XSDIVSP 111100 ..... ..... ..... 00011000 ... @XX3 + +XSADDDP 111100 ..... ..... ..... 00100000 ... @XX3 +XSSUBDP 111100 ..... ..... ..... 00101000 ... @XX3 +XSMULDP 111100 ..... ..... ..... 00110000 ... @XX3 +XSDIVDP 111100 ..... ..... ..... 00111000 ... @XX3 + +XVADDSP 111100 ..... ..... ..... 01000000 ... @XX3 +XVSUBSP 111100 ..... ..... ..... 01001000 ... @XX3 +XVMULSP 111100 ..... ..... ..... 01010000 ... @XX3 +XVDIVSP 111100 ..... ..... ..... 01011000 ... @XX3 + +XVADDDP 111100 ..... ..... ..... 01100000 ... @XX3 +XVSUBDP 111100 ..... ..... ..... 01101000 ... @XX3 +XVMULDP 111100 ..... ..... ..... 01110000 ... @XX3 +XVDIVDP 111100 ..... ..... ..... 01111000 ... @XX3 + ## VSX Scalar Multiply-Add Instructions XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3 @@ -1073,6 +1095,14 @@ XSCMPEQQP 111111 ..... ..... ..... 0001000100 - @X XSCMPGEQP 111111 ..... ..... ..... 0011000100 - @X XSCMPGTQP 111111 ..... ..... ..... 0011100100 - @X +XSMAXDP 111100 ..... ..... ..... 10100000 ... @XX3 +XSMINDP 111100 ..... ..... ..... 10101000 ... @XX3 + +XVMAXSP 111100 ..... ..... ..... 11000000 ... @XX3 +XVMINSP 111100 ..... ..... ..... 11001000 ... @XX3 +XVMAXDP 111100 ..... ..... ..... 11100000 ... @XX3 +XVMINDP 111100 ..... ..... ..... 11101000 ... @XX3 + ## VSX Binary Floating-Point Convert Instructions XSCVQPDP 111111 ..... 10100 ..... 1101000100 . @X_tb_rc diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 6025119e5b..0d16e0f02b 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -864,20 +864,6 @@ static void gen_##name(DisasContext *ctx) \ gen_helper_##name(tcg_env, opc); \ } -#define GEN_VSX_HELPER_X3(name, op1, op2, inval, type) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr xt, xa, xb; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ - xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(tcg_env, xt, xa, xb); \ -} - #define GEN_VSX_HELPER_X2(name, op1, op2, inval, type) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -983,12 +969,8 @@ static void gen_##name(DisasContext *ctx) \ set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); \ } -GEN_VSX_HELPER_X3(xsadddp, 0x00, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsaddqp, 0x04, 0x00, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xssubdp, 0x00, 0x05, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsmulqp, 0x04, 0x01, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsdivqp, 0x04, 0x11, 0, PPC2_ISA300) GEN_VSX_HELPER_X2(xsredp, 0x14, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) @@ -1001,8 +983,6 @@ GEN_VSX_HELPER_X2_AB(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R2_AB(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R2_AB(xscmpuqp, 0x04, 0x14, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmindp, 0x00, 0x15, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300) GEN_VSX_HELPER_X2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) GEN_VSX_HELPER_R2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300) @@ -1233,27 +1213,17 @@ GEN_VSX_HELPER_R2(xsrqpi, 0x05, 0x00, 0, PPC2_ISA300) GEN_VSX_HELPER_R2(xsrqpxp, 0x05, 0x01, 0, PPC2_ISA300) GEN_VSX_HELPER_R2(xssqrtqp, 0x04, 0x19, 0x1B, PPC2_ISA300) GEN_VSX_HELPER_R3(xssubqp, 0x04, 0x10, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xsaddsp, 0x00, 0x00, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xssubsp, 0x00, 0x01, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xsmulsp, 0x00, 0x02, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xsdivsp, 0x00, 0x03, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xsresp, 0x14, 0x01, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmuldp, 0x00, 0x0E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvdivdp, 0x00, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvredp, 0x14, 0x0D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvsqrtdp, 0x16, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrsqrtedp, 0x14, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xvtdivdp, 0x14, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xvtsqrtdp, 0x14, 0x0E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmaxdp, 0x00, 0x1C, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) @@ -1269,17 +1239,11 @@ GEN_VSX_HELPER_X2(xvrdpim, 0x12, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrdpip, 0x12, 0x0E, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrdpiz, 0x12, 0x0D, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvaddsp, 0x00, 0x08, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvsubsp, 0x00, 0x09, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmulsp, 0x00, 0x0A, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvdivsp, 0x00, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvresp, 0x14, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvsqrtsp, 0x16, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrsqrtesp, 0x14, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xvtdivsp, 0x14, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xvtsqrtsp, 0x14, 0x0A, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmaxsp, 0x00, 0x18, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvminsp, 0x00, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300) GEN_VSX_HELPER_X2(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300) @@ -2730,6 +2694,33 @@ TRANS_FLAGS2(ISA300, XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) TRANS_FLAGS2(ISA300, XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) TRANS_FLAGS2(ISA300, XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) +TRANS_FLAGS2(VSX207, XSADDSP, do_helper_XX3, gen_helper_XSADDSP) +TRANS_FLAGS2(VSX207, XSSUBSP, do_helper_XX3, gen_helper_XSSUBSP) +TRANS_FLAGS2(VSX207, XSMULSP, do_helper_XX3, gen_helper_XSMULSP) +TRANS_FLAGS2(VSX207, XSDIVSP, do_helper_XX3, gen_helper_XSDIVSP) + +TRANS_FLAGS2(VSX, XSADDDP, do_helper_XX3, gen_helper_XSADDDP) +TRANS_FLAGS2(VSX, XSSUBDP, do_helper_XX3, gen_helper_XSSUBDP) +TRANS_FLAGS2(VSX, XSMULDP, do_helper_XX3, gen_helper_XSMULDP) +TRANS_FLAGS2(VSX, XSDIVDP, do_helper_XX3, gen_helper_XSDIVDP) + +TRANS_FLAGS2(VSX, XVADDSP, do_helper_XX3, gen_helper_XVADDSP) +TRANS_FLAGS2(VSX, XVSUBSP, do_helper_XX3, gen_helper_XVSUBSP) +TRANS_FLAGS2(VSX, XVMULSP, do_helper_XX3, gen_helper_XVMULSP) +TRANS_FLAGS2(VSX, XVDIVSP, do_helper_XX3, gen_helper_XVDIVSP) + +TRANS_FLAGS2(VSX, XVADDDP, do_helper_XX3, gen_helper_XVADDDP) +TRANS_FLAGS2(VSX, XVSUBDP, do_helper_XX3, gen_helper_XVSUBDP) +TRANS_FLAGS2(VSX, XVMULDP, do_helper_XX3, gen_helper_XVMULDP) +TRANS_FLAGS2(VSX, XVDIVDP, do_helper_XX3, gen_helper_XVDIVDP) + +TRANS_FLAGS2(VSX, XSMAXDP, do_helper_XX3, gen_helper_XSMAXDP) +TRANS_FLAGS2(VSX, XSMINDP, do_helper_XX3, gen_helper_XSMINDP) +TRANS_FLAGS2(VSX, XVMAXSP, do_helper_XX3, gen_helper_XVMAXSP) +TRANS_FLAGS2(VSX, XVMINSP, do_helper_XX3, gen_helper_XVMINSP) +TRANS_FLAGS2(VSX, XVMAXDP, do_helper_XX3, gen_helper_XVMAXDP) +TRANS_FLAGS2(VSX, XVMINDP, do_helper_XX3, gen_helper_XVMINDP) + static bool do_helper_X(arg_X *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) { diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index a3ba094d62..18510d757d 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -153,12 +153,8 @@ GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300), GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300), GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300), -GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0), -GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), -GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), GEN_VSX_XFORM_300(xsmulqp, 0x04, 0x01, 0x0), -GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), @@ -170,8 +166,6 @@ GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001), GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001), -GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), -GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300), GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), @@ -191,10 +185,6 @@ GEN_XX2FORM(xsrdpim, 0x12, 0x07, PPC2_VSX), GEN_XX2FORM(xsrdpip, 0x12, 0x06, PPC2_VSX), GEN_XX2FORM(xsrdpiz, 0x12, 0x05, PPC2_VSX), -GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), -GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), -GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), -GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), GEN_VSX_XFORM_300(xsdivqp, 0x04, 0x11, 0x0), GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), @@ -203,10 +193,6 @@ GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), -GEN_XX3FORM(xvadddp, 0x00, 0x0C, PPC2_VSX), -GEN_XX3FORM(xvsubdp, 0x00, 0x0D, PPC2_VSX), -GEN_XX3FORM(xvmuldp, 0x00, 0x0E, PPC2_VSX), -GEN_XX3FORM(xvdivdp, 0x00, 0x0F, PPC2_VSX), GEN_XX2FORM(xvredp, 0x14, 0x0D, PPC2_VSX), GEN_XX2FORM(xvsqrtdp, 0x16, 0x0C, PPC2_VSX), GEN_XX2FORM(xvrsqrtedp, 0x14, 0x0C, PPC2_VSX), @@ -220,8 +206,6 @@ GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddadp", 0x04, 0x1C, PPC2_VSX), GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddmdp", 0x04, 0x1D, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubadp", 0x04, 0x1E, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubmdp", 0x04, 0x1F, PPC2_VSX), -GEN_XX3FORM(xvmaxdp, 0x00, 0x1C, PPC2_VSX), -GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), @@ -241,10 +225,6 @@ GEN_XX2FORM(xvrdpim, 0x12, 0x0F, PPC2_VSX), GEN_XX2FORM(xvrdpip, 0x12, 0x0E, PPC2_VSX), GEN_XX2FORM(xvrdpiz, 0x12, 0x0D, PPC2_VSX), -GEN_XX3FORM(xvaddsp, 0x00, 0x08, PPC2_VSX), -GEN_XX3FORM(xvsubsp, 0x00, 0x09, PPC2_VSX), -GEN_XX3FORM(xvmulsp, 0x00, 0x0A, PPC2_VSX), -GEN_XX3FORM(xvdivsp, 0x00, 0x0B, PPC2_VSX), GEN_XX2FORM(xvresp, 0x14, 0x09, PPC2_VSX), GEN_XX2FORM(xvsqrtsp, 0x16, 0x08, PPC2_VSX), GEN_XX2FORM(xvrsqrtesp, 0x14, 0x08, PPC2_VSX), @@ -258,8 +238,6 @@ GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddasp", 0x04, 0x18, PPC2_VSX), GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddmsp", 0x04, 0x19, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubasp", 0x04, 0x1A, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubmsp", 0x04, 0x1B, PPC2_VSX), -GEN_XX3FORM(xvmaxsp, 0x00, 0x18, PPC2_VSX), -GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), From c1167a9257a22433a16e223a1209c9c72836edee Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Thu, 23 May 2024 15:18:21 +0530 Subject: [PATCH 2537/2884] target/ppc: Move VSX logical instructions to decodetree. Moving the following instructions to decodetree specification : xxl{and, andc, or, orc, nor, xor, nand, eqv} : XX3-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/insn32.decode | 11 +++++++++ target/ppc/translate/vsx-impl.c.inc | 37 +++++++++++++---------------- target/ppc/translate/vsx-ops.c.inc | 11 --------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 1301e5bbc0..4f86b175f1 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1138,6 +1138,17 @@ XXMFACC 011111 ... -- 00000 ----- 0010110001 - @X_a XXMTACC 011111 ... -- 00001 ----- 0010110001 - @X_a XXSETACCZ 011111 ... -- 00011 ----- 0010110001 - @X_a +## VSX Vector Logical instructions + +XXLAND 111100 ..... ..... ..... 10000010 ... @XX3 +XXLANDC 111100 ..... ..... ..... 10001010 ... @XX3 +XXLOR 111100 ..... ..... ..... 10010010 ... @XX3 +XXLXOR 111100 ..... ..... ..... 10011010 ... @XX3 +XXLNOR 111100 ..... ..... ..... 10100010 ... @XX3 +XXLEQV 111100 ..... ..... ..... 10111010 ... @XX3 +XXLNAND 111100 ..... ..... ..... 10110010 ... @XX3 +XXLORC 111100 ..... ..... ..... 10101010 ... @XX3 + ## VSX GER instruction XVI4GER8 111011 ... -- ..... ..... 00100011 ..- @XX3_at xa=%xx_xa diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 0d16e0f02b..a769f199ce 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -1573,26 +1573,24 @@ static void gen_xxbrw(DisasContext *ctx) set_cpu_vsr(xT(ctx->opcode), xtl, false); } -#define VSX_LOGICAL(name, vece, tcg_op) \ -static void glue(gen_, name)(DisasContext *ctx) \ - { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - tcg_op(vece, vsr_full_offset(xT(ctx->opcode)), \ - vsr_full_offset(xA(ctx->opcode)), \ - vsr_full_offset(xB(ctx->opcode)), 16, 16); \ - } +static bool do_logical_op(DisasContext *ctx, arg_XX3 *a, unsigned vece, + void (*helper)(unsigned, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)) +{ + REQUIRE_VSX(ctx); + helper(vece, vsr_full_offset(a->xt), + vsr_full_offset(a->xa), + vsr_full_offset(a->xb), 16, 16); + return true; +} -VSX_LOGICAL(xxland, MO_64, tcg_gen_gvec_and) -VSX_LOGICAL(xxlandc, MO_64, tcg_gen_gvec_andc) -VSX_LOGICAL(xxlor, MO_64, tcg_gen_gvec_or) -VSX_LOGICAL(xxlxor, MO_64, tcg_gen_gvec_xor) -VSX_LOGICAL(xxlnor, MO_64, tcg_gen_gvec_nor) -VSX_LOGICAL(xxleqv, MO_64, tcg_gen_gvec_eqv) -VSX_LOGICAL(xxlnand, MO_64, tcg_gen_gvec_nand) -VSX_LOGICAL(xxlorc, MO_64, tcg_gen_gvec_orc) +TRANS_FLAGS2(VSX, XXLAND, do_logical_op, MO_64, tcg_gen_gvec_and); +TRANS_FLAGS2(VSX, XXLANDC, do_logical_op, MO_64, tcg_gen_gvec_andc); +TRANS_FLAGS2(VSX, XXLOR, do_logical_op, MO_64, tcg_gen_gvec_or); +TRANS_FLAGS2(VSX, XXLXOR, do_logical_op, MO_64, tcg_gen_gvec_xor); +TRANS_FLAGS2(VSX, XXLNOR, do_logical_op, MO_64, tcg_gen_gvec_nor); +TRANS_FLAGS2(VSX207, XXLEQV, do_logical_op, MO_64, tcg_gen_gvec_eqv); +TRANS_FLAGS2(VSX207, XXLNAND, do_logical_op, MO_64, tcg_gen_gvec_nand); +TRANS_FLAGS2(VSX207, XXLORC, do_logical_op, MO_64, tcg_gen_gvec_orc); #define VSX_XXMRG(name, high) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -2899,4 +2897,3 @@ TRANS64(PMXVF64GERNN, do_ger, gen_helper_XVF64GERNN) #undef GEN_XX2IFORM #undef GEN_XX3_RC_FORM #undef GEN_XX3FORM_DM -#undef VSX_LOGICAL diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index 18510d757d..3c0a70cb7c 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -263,17 +263,6 @@ GEN_XX2FORM_EO(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300), GEN_XX2FORM_EO(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300), GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), -#define VSX_LOGICAL(name, opc2, opc3, fl2) \ -GEN_XX3FORM(name, opc2, opc3, fl2) - -VSX_LOGICAL(xxland, 0x8, 0x10, PPC2_VSX), -VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX), -VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX), -VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX), -VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX), -VSX_LOGICAL(xxleqv, 0x8, 0x17, PPC2_VSX207), -VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), -VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), From cff278c9fa4ed6a8c2e5d2aba6c490e6252a6825 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 18 Jun 2024 14:28:28 +0530 Subject: [PATCH 2538/2884] target/ppc: Moving VSX scalar storage access insns to decodetree. Moving the following instructions to decodetree specification : lxs{d, iwa, ibz, ihz, iwz, sp}x : X-form stxs{d, ib, ih, iw, sp}x : X-form The changes were verified by validating that the tcg-ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/insn32.decode | 13 +++++ target/ppc/translate/vsx-impl.c.inc | 79 +++++++++++++---------------- target/ppc/translate/vsx-ops.c.inc | 11 ---- 3 files changed, 49 insertions(+), 54 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 4f86b175f1..f2661df918 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -993,6 +993,19 @@ STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX +LXSDX 011111 ..... ..... ..... 1001001100 . @X_TSX +LXSIWAX 011111 ..... ..... ..... 0001001100 . @X_TSX +LXSIBZX 011111 ..... ..... ..... 1100001101 . @X_TSX +LXSIHZX 011111 ..... ..... ..... 1100101101 . @X_TSX +LXSIWZX 011111 ..... ..... ..... 0000001100 . @X_TSX +LXSSPX 011111 ..... ..... ..... 1000001100 . @X_TSX + +STXSDX 011111 ..... ..... ..... 1011001100 . @X_TSX +STXSIBX 011111 ..... ..... ..... 1110001101 . @X_TSX +STXSIHX 011111 ..... ..... ..... 1110101101 . @X_TSX +STXSIWX 011111 ..... ..... ..... 0010001100 . @X_TSX +STXSSPX 011111 ..... ..... ..... 1010001100 . @X_TSX + ## VSX Vector Binary Floating-Point Sign Manipulation Instructions XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2 diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index a769f199ce..de2a26a213 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -24,30 +24,27 @@ static inline TCGv_ptr gen_acc_ptr(int reg) return r; } -#define VSX_LOAD_SCALAR(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - gen_qemu_##operation(ctx, t0, EA); \ - set_cpu_vsr(xT(ctx->opcode), t0, true); \ - /* NOTE: cpu_vsrl is undefined */ \ +static bool do_lxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) +{ + TCGv EA; + TCGv_i64 t0; + REQUIRE_VSX(ctx); + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + op(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, true); + /* NOTE: cpu_vsrl is undefined */ + return true; } -VSX_LOAD_SCALAR(lxsdx, ld64_i64) -VSX_LOAD_SCALAR(lxsiwax, ld32s_i64) -VSX_LOAD_SCALAR(lxsibzx, ld8u_i64) -VSX_LOAD_SCALAR(lxsihzx, ld16u_i64) -VSX_LOAD_SCALAR(lxsiwzx, ld32u_i64) -VSX_LOAD_SCALAR(lxsspx, ld32fs) +TRANS_FLAGS2(VSX, LXSDX, do_lxs, gen_qemu_ld64_i64); +TRANS_FLAGS2(VSX207, LXSIWAX, do_lxs, gen_qemu_ld32s_i64); +TRANS_FLAGS2(ISA300, LXSIBZX, do_lxs, gen_qemu_ld8u_i64); +TRANS_FLAGS2(ISA300, LXSIHZX, do_lxs, gen_qemu_ld16u_i64); +TRANS_FLAGS2(VSX207, LXSIWZX, do_lxs, gen_qemu_ld32u_i64); +TRANS_FLAGS2(VSX207, LXSSPX, do_lxs, gen_qemu_ld32fs); static void gen_lxvd2x(DisasContext *ctx) { @@ -266,29 +263,25 @@ VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) #endif -#define VSX_STORE_SCALAR(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - get_cpu_vsr(t0, xS(ctx->opcode), true); \ - gen_qemu_##operation(ctx, t0, EA); \ +static bool do_stxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) +{ + TCGv EA; + TCGv_i64 t0; + REQUIRE_VSX(ctx); + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); + op(ctx, t0, EA); + return true; } -VSX_STORE_SCALAR(stxsdx, st64_i64) - -VSX_STORE_SCALAR(stxsibx, st8_i64) -VSX_STORE_SCALAR(stxsihx, st16_i64) -VSX_STORE_SCALAR(stxsiwx, st32_i64) -VSX_STORE_SCALAR(stxsspx, st32fs) +TRANS_FLAGS2(VSX, STXSDX, do_stxs, gen_qemu_st64_i64); +TRANS_FLAGS2(ISA300, STXSIBX, do_stxs, gen_qemu_st8_i64); +TRANS_FLAGS2(ISA300, STXSIHX, do_stxs, gen_qemu_st16_i64); +TRANS_FLAGS2(VSX207, STXSIWX, do_stxs, gen_qemu_st32_i64); +TRANS_FLAGS2(VSX207, STXSSPX, do_stxs, gen_qemu_st32fs); static void gen_stxvd2x(DisasContext *ctx) { diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index 3c0a70cb7c..d44cb55836 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -1,9 +1,3 @@ -GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxsiwax, 0x1F, 0x0C, 0x02, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxsiwzx, 0x1F, 0x0C, 0x00, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxsibzx, 0x1F, 0x0D, 0x18, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxsihzx, 0x1F, 0x0D, 0x19, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxsspx, 0x1F, 0x0C, 0x10, 0, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvwsx, 0x1F, 0x0C, 0x0B, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), @@ -15,11 +9,6 @@ GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300), #endif -GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxsihx, 0x1F, 0xD, 0x1D, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxsiwx, 0x1F, 0xC, 0x04, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(stxsspx, 0x1F, 0xC, 0x14, 0, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), From 29df8d950e20f5caeec137fa20bc1245fb9f702e Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 18 Jun 2024 14:28:29 +0530 Subject: [PATCH 2539/2884] target/ppc: Move VSX vector with length storage access insns to decodetree. Moving the following instructions to decodetree specification : {l, st}xvl(l) : X-form The changes were verified by validating that the tcg-ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Also added a new function do_ea_calc_ra to calculate the effective address : EA <- (RA == 0) ? 0 : GPR[RA], which is now used by the above-said insns, and shall be used later by (p){lx, stx}vp insns. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> [np: Fix 32-bit build] Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/helper.h | 8 +-- target/ppc/insn32.decode | 6 ++ target/ppc/mem_helper.c | 8 +-- target/ppc/translate.c | 17 ++++++ target/ppc/translate/vsx-impl.c.inc | 90 ++++++++++++++++++++--------- target/ppc/translate/vsx-ops.c.inc | 8 --- 6 files changed, 94 insertions(+), 43 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 13f20bb243..85be749004 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -277,10 +277,10 @@ DEF_HELPER_3(STVEBX, void, env, avr, tl) DEF_HELPER_3(STVEHX, void, env, avr, tl) DEF_HELPER_3(STVEWX, void, env, avr, tl) #if defined(TARGET_PPC64) -DEF_HELPER_4(lxvl, void, env, tl, vsr, tl) -DEF_HELPER_4(lxvll, void, env, tl, vsr, tl) -DEF_HELPER_4(stxvl, void, env, tl, vsr, tl) -DEF_HELPER_4(stxvll, void, env, tl, vsr, tl) +DEF_HELPER_4(LXVL, void, env, tl, vsr, tl) +DEF_HELPER_4(LXVLL, void, env, tl, vsr, tl) +DEF_HELPER_4(STXVL, void, env, tl, vsr, tl) +DEF_HELPER_4(STXVLL, void, env, tl, vsr, tl) #endif DEF_HELPER_4(vsumsws, void, env, avr, avr, avr) DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index f2661df918..e87b034159 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1006,6 +1006,12 @@ STXSIHX 011111 ..... ..... ..... 1110101101 . @X_TSX STXSIWX 011111 ..... ..... ..... 0010001100 . @X_TSX STXSSPX 011111 ..... ..... ..... 1010001100 . @X_TSX +LXVL 011111 ..... ..... ..... 0100001101 . @X_TSX +LXVLL 011111 ..... ..... ..... 0100101101 . @X_TSX + +STXVL 011111 ..... ..... ..... 0110001101 . @X_TSX +STXVLL 011111 ..... ..... ..... 0110101101 . @X_TSX + ## VSX Vector Binary Floating-Point Sign Manipulation Instructions XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2 diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 953dd08d5d..51b137febd 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -475,8 +475,8 @@ void helper_##name(CPUPPCState *env, target_ulong addr, \ *xt = t; \ } -VSX_LXVL(lxvl, 0) -VSX_LXVL(lxvll, 1) +VSX_LXVL(LXVL, 0) +VSX_LXVL(LXVLL, 1) #undef VSX_LXVL #define VSX_STXVL(name, lj) \ @@ -504,8 +504,8 @@ void helper_##name(CPUPPCState *env, target_ulong addr, \ } \ } -VSX_STXVL(stxvl, 0) -VSX_STXVL(stxvll, 1) +VSX_STXVL(STXVL, 0) +VSX_STXVL(STXVLL, 1) #undef VSX_STXVL #undef GET_NB #endif /* TARGET_PPC64 */ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cba943a49d..46aabce82b 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2543,6 +2543,7 @@ static inline void gen_align_no_le(DisasContext *ctx) (ctx->opcode & 0x03FF0000) | POWERPC_EXCP_ALIGN_LE); } +/* EA <- {(ra == 0) ? 0 : GPR[ra]} + displ */ static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) { TCGv ea = tcg_temp_new(); @@ -2557,6 +2558,22 @@ static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) return ea; } +#if defined(TARGET_PPC64) +/* EA <- (ra == 0) ? 0 : GPR[ra] */ +static TCGv do_ea_calc_ra(DisasContext *ctx, int ra) +{ + TCGv EA = tcg_temp_new(); + if (!ra) { + tcg_gen_movi_tl(EA, 0); + } else if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[ra]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[ra]); + } + return EA; +} +#endif + /*** Integer load ***/ #define DEF_MEMOP(op) ((op) | ctx->default_tcg_memop_mask) #define BSWAP_MEMOP(op) ((op) | (ctx->default_tcg_memop_mask ^ MO_BSWAP)) diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index de2a26a213..46bab49215 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -232,36 +232,72 @@ static void gen_lxvb16x(DisasContext *ctx) set_cpu_vsr(xT(ctx->opcode), xtl, false); } -#ifdef TARGET_PPC64 -#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_ptr xt; \ - \ - if (xT(ctx->opcode) < 32) { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - EA = tcg_temp_new(); \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - gen_set_access_type(ctx, ACCESS_INT); \ - gen_addr_register(ctx, EA); \ - gen_helper_##name(tcg_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \ +#if defined(TARGET_PPC64) +static bool do_ld_st_vl(DisasContext *ctx, arg_X *a, + void (*helper)(TCGv_ptr, TCGv, TCGv_ptr, TCGv)) +{ + TCGv EA; + TCGv_ptr xt; + if (a->rt < 32) { + REQUIRE_VSX(ctx); + } else { + REQUIRE_VECTOR(ctx); + } + xt = gen_vsr_ptr(a->rt); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc_ra(ctx, a->ra); + helper(tcg_env, EA, xt, cpu_gpr[a->rb]); + return true; +} +#endif + +static bool trans_LXVL(DisasContext *ctx, arg_LXVL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_LXVL); +#else + qemu_build_not_reached(); +#endif + return true; } -VSX_VECTOR_LOAD_STORE_LENGTH(lxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(lxvll) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) +static bool trans_LXVLL(DisasContext *ctx, arg_LXVLL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_LXVLL); +#else + qemu_build_not_reached(); #endif + return true; +} + +static bool trans_STXVL(DisasContext *ctx, arg_STXVL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_STXVL); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_STXVLL(DisasContext *ctx, arg_STXVLL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_STXVLL); +#else + qemu_build_not_reached(); +#endif + return true; +} static bool do_stxs(DisasContext *ctx, arg_X *a, void (*op)(DisasContext *, TCGv_i64, TCGv)) diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index d44cb55836..7f4326c974 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -4,19 +4,11 @@ GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300), -#endif GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300), -#endif GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), From 7419dc5b2b5bcc929d91e8920692041a8f6d1977 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 18 Jun 2024 14:28:30 +0530 Subject: [PATCH 2540/2884] target/ppc: Move VSX vector storage access insns to decodetree. Moving the following instructions to decodetree specification: lxv{b16, d2, h8, w4, ds, ws}x : X-form stxv{b16, d2, h8, w4}x : X-form The changes were verified by validating that the tcg-ops generated for those instructions remain the same, which were captured using the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/insn32.decode | 10 ++ target/ppc/translate/vsx-impl.c.inc | 199 ++++++++++++---------------- target/ppc/translate/vsx-ops.c.inc | 12 -- 3 files changed, 97 insertions(+), 124 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e87b034159..77869cfb33 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1006,9 +1006,19 @@ STXSIHX 011111 ..... ..... ..... 1110101101 . @X_TSX STXSIWX 011111 ..... ..... ..... 0010001100 . @X_TSX STXSSPX 011111 ..... ..... ..... 1010001100 . @X_TSX +LXVB16X 011111 ..... ..... ..... 1101101100 . @X_TSX +LXVD2X 011111 ..... ..... ..... 1101001100 . @X_TSX +LXVH8X 011111 ..... ..... ..... 1100101100 . @X_TSX +LXVW4X 011111 ..... ..... ..... 1100001100 . @X_TSX +LXVDSX 011111 ..... ..... ..... 0101001100 . @X_TSX +LXVWSX 011111 ..... ..... ..... 0101101100 . @X_TSX LXVL 011111 ..... ..... ..... 0100001101 . @X_TSX LXVLL 011111 ..... ..... ..... 0100101101 . @X_TSX +STXVB16X 011111 ..... ..... ..... 1111101100 . @X_TSX +STXVD2X 011111 ..... ..... ..... 1111001100 . @X_TSX +STXVH8X 011111 ..... ..... ..... 1110101100 . @X_TSX +STXVW4X 011111 ..... ..... ..... 1110001100 . @X_TSX STXVL 011111 ..... ..... ..... 0110001101 . @X_TSX STXVLL 011111 ..... ..... ..... 0110101101 . @X_TSX diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 46bab49215..e0fb4bad92 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -46,41 +46,37 @@ TRANS_FLAGS2(ISA300, LXSIHZX, do_lxs, gen_qemu_ld16u_i64); TRANS_FLAGS2(VSX207, LXSIWZX, do_lxs, gen_qemu_ld32u_i64); TRANS_FLAGS2(VSX207, LXSSPX, do_lxs, gen_qemu_ld32fs); -static void gen_lxvd2x(DisasContext *ctx) +static bool trans_LXVD2X(DisasContext *ctx, arg_LXVD2X *a) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, true); + set_cpu_vsr(a->rt, t0, true); tcg_gen_addi_tl(EA, EA, 8); gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, false); + set_cpu_vsr(a->rt, t0, false); + return true; } -static void gen_lxvw4x(DisasContext *ctx) +static bool trans_LXVW4X(DisasContext *ctx, arg_LXVW4X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -97,55 +93,45 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); } - set_cpu_vsr(xT(ctx->opcode), xth, true); - set_cpu_vsr(xT(ctx->opcode), xtl, false); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } -static void gen_lxvwsx(DisasContext *ctx) +static bool trans_LXVWSX(DisasContext *ctx, arg_LXVWSX *a) { TCGv EA; TCGv_i32 data; - if (xT(ctx->opcode) < 32) { - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + if (a->rt < 32) { + REQUIRE_VSX(ctx); } else { - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } + REQUIRE_VECTOR(ctx); } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); - + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); data = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UL)); - tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); + tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(a->rt), 16, 16, data); + return true; } -static void gen_lxvdsx(DisasContext *ctx) +static bool trans_LXVDSX(DisasContext *ctx, arg_LXVDSX *a) { TCGv EA; TCGv_i64 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); - + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); data = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UQ)); - tcg_gen_gvec_dup_i64(MO_UQ, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); + tcg_gen_gvec_dup_i64(MO_UQ, vsr_full_offset(a->rt), 16, 16, data); + return true; } static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, @@ -184,52 +170,47 @@ static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, tcg_gen_deposit_i64(outl, outl, lo, 32, 32); } -static void gen_lxvh8x(DisasContext *ctx) +static bool trans_LXVH8X(DisasContext *ctx, arg_LXVH8X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); if (ctx->le_mode) { gen_bswap16x8(xth, xtl, xth, xtl); } - set_cpu_vsr(xT(ctx->opcode), xth, true); - set_cpu_vsr(xT(ctx->opcode), xtl, false); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } -static void gen_lxvb16x(DisasContext *ctx) +static bool trans_LXVB16X(DisasContext *ctx, arg_LXVB16X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); - set_cpu_vsr(xT(ctx->opcode), xth, true); - set_cpu_vsr(xT(ctx->opcode), xtl, false); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } #if defined(TARGET_PPC64) @@ -319,42 +300,39 @@ TRANS_FLAGS2(ISA300, STXSIHX, do_stxs, gen_qemu_st16_i64); TRANS_FLAGS2(VSX207, STXSIWX, do_stxs, gen_qemu_st32_i64); TRANS_FLAGS2(VSX207, STXSSPX, do_stxs, gen_qemu_st32fs); -static void gen_stxvd2x(DisasContext *ctx) +static bool trans_STXVD2X(DisasContext *ctx, arg_STXVD2X *a) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - get_cpu_vsr(t0, xS(ctx->opcode), true); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - get_cpu_vsr(t0, xS(ctx->opcode), false); + get_cpu_vsr(t0, a->rt, false); gen_qemu_st64_i64(ctx, t0, EA); + return true; } -static void gen_stxvw4x(DisasContext *ctx) +static bool trans_STXVW4X(DisasContext *ctx, arg_STXVW4X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -371,25 +349,23 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } + return true; } -static void gen_stxvh8x(DisasContext *ctx) +static bool trans_STXVH8X(DisasContext *ctx, arg_STXVH8X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 outh = tcg_temp_new_i64(); TCGv_i64 outl = tcg_temp_new_i64(); @@ -403,28 +379,27 @@ static void gen_stxvh8x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } + return true; } -static void gen_stxvb16x(DisasContext *ctx) +static bool trans_STXVB16X(DisasContext *ctx, arg_STXVB16X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); + return true; } static void gen_mfvsrwz(DisasContext *ctx) diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index 7f4326c974..91cde088bc 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -1,15 +1,3 @@ -GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvwsx, 0x1F, 0x0C, 0x0B, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), - -GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), - GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwz, 0x1F, 0x13, 0x07, 0x0000F800, PPC_NONE, PPC2_VSX207), From e77d736d2a069d462b686f2207df06859abb9ace Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 18 Jun 2024 14:28:31 +0530 Subject: [PATCH 2541/2884] target/ppc: Move VSX fp compare insns to decodetree. Moving the following instructions to decodetree specification: xvcmp{eq, gt, ge, ne}{s, d}p : XX3-form The changes were verified by validating that the tcg-ops generated for those instructions remain the same which were captured using the '-d in_asm,op' flag. Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/fpu_helper.c | 16 +++++----- target/ppc/helper.h | 16 +++++----- target/ppc/insn32.decode | 12 ++++++++ target/ppc/translate/vsx-impl.c.inc | 46 +++++++++++++---------------- target/ppc/translate/vsx-ops.c.inc | 18 ----------- 5 files changed, 48 insertions(+), 60 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 3f2e4f5827..230466a87f 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -2527,14 +2527,14 @@ uint32_t helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ return crf6; \ } -VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) -VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1) -VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1) -VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0) -VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1) -VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1) -VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1) -VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) +VSX_CMP(XVCMPEQDP, 2, float64, VsrD(i), eq, 0, 1) +VSX_CMP(XVCMPGEDP, 2, float64, VsrD(i), le, 1, 1) +VSX_CMP(XVCMPGTDP, 2, float64, VsrD(i), lt, 1, 1) +VSX_CMP(XVCMPNEDP, 2, float64, VsrD(i), eq, 0, 0) +VSX_CMP(XVCMPEQSP, 4, float32, VsrW(i), eq, 0, 1) +VSX_CMP(XVCMPGESP, 4, float32, VsrW(i), le, 1, 1) +VSX_CMP(XVCMPGTSP, 4, float32, VsrW(i), lt, 1, 1) +VSX_CMP(XVCMPNESP, 4, float32, VsrW(i), eq, 0, 0) /* * VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 85be749004..5a77e761bd 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -476,10 +476,10 @@ DEF_HELPER_5(xvnmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubdp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_4(XVMAXDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XVMINDP, void, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpeqdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgtdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpnedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPEQDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGEDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGTDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPNEDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvdpsp, void, env, vsr, vsr) DEF_HELPER_3(xvcvdpsxds, void, env, vsr, vsr) DEF_HELPER_3(xvcvdpsxws, void, env, vsr, vsr) @@ -510,10 +510,10 @@ DEF_HELPER_5(xvnmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_4(XVMAXSP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XVMINSP, void, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpeqsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgtsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpnesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPEQSP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGESP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGTSP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPNESP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvspdp, void, env, vsr, vsr) DEF_HELPER_3(xvcvsphp, void, env, vsr, vsr) DEF_HELPER_3(xvcvhpsp, void, env, vsr, vsr) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 77869cfb33..e53fd2840d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -241,6 +241,9 @@ &XX3 xt xa xb @XX3 ...... ..... ..... ..... ........ ... &XX3 xt=%xx_xt xa=%xx_xa xb=%xx_xb +&XX3_rc xt xa xb rc:bool +@XX3_rc ...... ..... ..... ..... rc:1 ....... ... &XX3_rc xt=%xx_xt xa=%xx_xa xb=%xx_xb + # 32 bit GER instructions have all mask bits considered 1 &MMIRR_XX3 xa xb xt pmsk xmsk ymsk %xx_at 23:3 @@ -1124,6 +1127,15 @@ XSCMPEQQP 111111 ..... ..... ..... 0001000100 - @X XSCMPGEQP 111111 ..... ..... ..... 0011000100 - @X XSCMPGTQP 111111 ..... ..... ..... 0011100100 - @X +XVCMPEQSP 111100 ..... ..... ..... . 1000011 ... @XX3_rc +XVCMPGTSP 111100 ..... ..... ..... . 1001011 ... @XX3_rc +XVCMPGESP 111100 ..... ..... ..... . 1010011 ... @XX3_rc +XVCMPNESP 111100 ..... ..... ..... . 1011011 ... @XX3_rc +XVCMPEQDP 111100 ..... ..... ..... . 1100011 ... @XX3_rc +XVCMPGTDP 111100 ..... ..... ..... . 1101011 ... @XX3_rc +XVCMPGEDP 111100 ..... ..... ..... . 1110011 ... @XX3_rc +XVCMPNEDP 111100 ..... ..... ..... . 1111011 ... @XX3_rc + XSMAXDP 111100 ..... ..... ..... 10100000 ... @XX3 XSMINDP 111100 ..... ..... ..... 10101000 ... @XX3 diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index e0fb4bad92..26ebf3fedf 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -792,34 +792,28 @@ static bool do_xvcpsgn(DisasContext *ctx, arg_XX3 *a, unsigned vece) TRANS(XVCPSGNSP, do_xvcpsgn, MO_32) TRANS(XVCPSGNDP, do_xvcpsgn, MO_64) -#define VSX_CMP(name, op1, op2, inval, type) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_i32 ignored; \ - TCGv_ptr xt, xa, xb; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ - xb = gen_vsr_ptr(xB(ctx->opcode)); \ - if ((ctx->opcode >> (31 - 21)) & 1) { \ - gen_helper_##name(cpu_crf[6], tcg_env, xt, xa, xb); \ - } else { \ - ignored = tcg_temp_new_i32(); \ - gen_helper_##name(ignored, tcg_env, xt, xa, xb); \ - } \ +static bool do_cmp(DisasContext *ctx, arg_XX3_rc *a, + void (*helper)(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_i32 dest; + TCGv_ptr xt, xa, xb; + REQUIRE_VSX(ctx); + xt = gen_vsr_ptr(a->xt); + xa = gen_vsr_ptr(a->xa); + xb = gen_vsr_ptr(a->xb); + dest = a->rc ? cpu_crf[6] : tcg_temp_new_i32(); + helper(dest, tcg_env, xt, xa, xb); + return true; } -VSX_CMP(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) -VSX_CMP(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) -VSX_CMP(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) -VSX_CMP(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300) -VSX_CMP(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) -VSX_CMP(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) -VSX_CMP(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) -VSX_CMP(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) +TRANS_FLAGS2(VSX, XVCMPEQSP, do_cmp, gen_helper_XVCMPEQSP); +TRANS_FLAGS2(VSX, XVCMPGTSP, do_cmp, gen_helper_XVCMPGTSP); +TRANS_FLAGS2(VSX, XVCMPGESP, do_cmp, gen_helper_XVCMPGESP); +TRANS_FLAGS2(ISA300, XVCMPNESP, do_cmp, gen_helper_XVCMPNESP); +TRANS_FLAGS2(VSX, XVCMPEQDP, do_cmp, gen_helper_XVCMPEQDP); +TRANS_FLAGS2(VSX, XVCMPGTDP, do_cmp, gen_helper_XVCMPGTDP); +TRANS_FLAGS2(VSX, XVCMPGEDP, do_cmp, gen_helper_XVCMPGEDP); +TRANS_FLAGS2(ISA300, XVCMPNEDP, do_cmp, gen_helper_XVCMPNEDP); static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a) { diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index 91cde088bc..e553b5b8fa 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -43,16 +43,6 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 1, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 1, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 1, PPC_NONE, fl2) -#define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x10, 0, PPC_NONE, fl2) - #define GEN_XX3FORM_DM(name, opc2, opc3) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ @@ -175,10 +165,6 @@ GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddadp", 0x04, 0x1C, PPC2_VSX), GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddmdp", 0x04, 0x1D, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubadp", 0x04, 0x1E, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubmdp", 0x04, 0x1F, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300), GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), @@ -207,10 +193,6 @@ GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddasp", 0x04, 0x18, PPC2_VSX), GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddmsp", 0x04, 0x19, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubasp", 0x04, 0x1A, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubmsp", 0x04, 0x1B, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300), GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), From bf15bf0a1d07913f22e9e82a0b829d45efc69195 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 9 Jul 2024 17:13:39 +0530 Subject: [PATCH 2542/2884] target/ppc: Move get/set_avr64 functions to vmx-impl.c.inc. Those functions are used to ld/st data to and from Altivec registers, in 64 bits chunks, and are only used in vmx-impl.c.inc file, hence the clean-up movement. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/translate.c | 10 ---------- target/ppc/translate/vmx-impl.c.inc | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 46aabce82b..71513ba964 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5558,16 +5558,6 @@ static inline void set_fpr(int regno, TCGv_i64 src) tcg_gen_st_i64(tcg_constant_i64(0), tcg_env, vsr64_offset(regno, false)); } -static inline void get_avr64(TCGv_i64 dst, int regno, bool high) -{ - tcg_gen_ld_i64(dst, tcg_env, avr64_offset(regno, high)); -} - -static inline void set_avr64(int regno, TCGv_i64 src, bool high) -{ - tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); -} - /* * Helpers for decodetree used by !function for decoding arguments. */ diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 152bcde0e3..a182d2cf81 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -14,6 +14,16 @@ static inline TCGv_ptr gen_avr_ptr(int reg) return r; } +static inline void get_avr64(TCGv_i64 dst, int regno, bool high) +{ + tcg_gen_ld_i64(dst, tcg_env, avr64_offset(regno, high)); +} + +static inline void set_avr64(int regno, TCGv_i64 src, bool high) +{ + tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); +} + static bool trans_LVX(DisasContext *ctx, arg_X *a) { TCGv EA; From acbdee4588d972b8553b2c5c9ec4c17c2fe399a7 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 9 Jul 2024 17:13:40 +0530 Subject: [PATCH 2543/2884] target/ppc: Update VMX storage access insns to use tcg_gen_qemu_ld/st_i128. Updated instructions {l, st}vx to use tcg_gen_qemu_ld/st_i128, instead of using 64 bits loads/stores in succession. Introduced functions {get, set}_avr_full in vmx-impl.c.inc to facilitate the above, and potential future usage. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/translate/vmx-impl.c.inc | 42 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index a182d2cf81..70d0ad2e71 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -24,25 +24,29 @@ static inline void set_avr64(int regno, TCGv_i64 src, bool high) tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); } +static inline void get_avr_full(TCGv_i128 dst, int regno) +{ + tcg_gen_ld_i128(dst, tcg_env, avr_full_offset(regno)); +} + +static inline void set_avr_full(int regno, TCGv_i128 src) +{ + tcg_gen_st_i128(src, tcg_env, avr_full_offset(regno)); +} + static bool trans_LVX(DisasContext *ctx, arg_X *a) { TCGv EA; - TCGv_i64 avr; + TCGv_i128 avr; REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); REQUIRE_VECTOR(ctx); gen_set_access_type(ctx, ACCESS_INT); - avr = tcg_temp_new_i64(); + avr = tcg_temp_new_i128(); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_andi_tl(EA, EA, ~0xf); - /* - * We only need to swap high and low halves. gen_qemu_ld64_i64 - * does necessary 64-bit byteswap already. - */ - gen_qemu_ld64_i64(ctx, avr, EA); - set_avr64(a->rt, avr, !ctx->le_mode); - tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, avr, EA); - set_avr64(a->rt, avr, ctx->le_mode); + tcg_gen_qemu_ld_i128(avr, EA, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR)); + set_avr_full(a->rt, avr); return true; } @@ -56,22 +60,16 @@ static bool trans_LVXL(DisasContext *ctx, arg_LVXL *a) static bool trans_STVX(DisasContext *ctx, arg_STVX *a) { TCGv EA; - TCGv_i64 avr; + TCGv_i128 avr; REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); REQUIRE_VECTOR(ctx); gen_set_access_type(ctx, ACCESS_INT); - avr = tcg_temp_new_i64(); + avr = tcg_temp_new_i128(); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_andi_tl(EA, EA, ~0xf); - /* - * We only need to swap high and low halves. gen_qemu_st64_i64 - * does necessary 64-bit byteswap already. - */ - get_avr64(avr, a->rt, !ctx->le_mode); - gen_qemu_st64_i64(ctx, avr, EA); - tcg_gen_addi_tl(EA, EA, 8); - get_avr64(avr, a->rt, ctx->le_mode); - gen_qemu_st64_i64(ctx, avr, EA); + get_avr_full(avr, a->rt); + tcg_gen_qemu_st_i128(avr, EA, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR)); return true; } From 625b58fde83d226c6ea61004edad04858481c0e4 Mon Sep 17 00:00:00 2001 From: Chinmay Rath <rathc@linux.ibm.com> Date: Tue, 9 Jul 2024 17:13:41 +0530 Subject: [PATCH 2544/2884] target/ppc : Update VSX storage access insns to use tcg_gen_qemu _ld/st_i128. Updated many VSX instructions to use tcg_gen_qemu_ld/st_i128, instead of using tcg_gen_qemu_ld/st_i64 consecutively. Introduced functions {get,set}_vsr_full to facilitate the above & for future use. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Chinmay Rath <rathc@linux.ibm.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/translate/vsx-impl.c.inc | 74 +++++++++++++---------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 26ebf3fedf..40a87ddc4a 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -10,6 +10,16 @@ static inline void set_cpu_vsr(int n, TCGv_i64 src, bool high) tcg_gen_st_i64(src, tcg_env, vsr64_offset(n, high)); } +static inline void get_vsr_full(TCGv_i128 dst, int reg) +{ + tcg_gen_ld_i128(dst, tcg_env, vsr_full_offset(reg)); +} + +static inline void set_vsr_full(int reg, TCGv_i128 src) +{ + tcg_gen_st_i128(src, tcg_env, vsr_full_offset(reg)); +} + static inline TCGv_ptr gen_vsr_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); @@ -196,20 +206,17 @@ static bool trans_LXVH8X(DisasContext *ctx, arg_LXVH8X *a) static bool trans_LXVB16X(DisasContext *ctx, arg_LXVB16X *a) { TCGv EA; - TCGv_i64 xth, xtl; + TCGv_i128 data; REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); - xth = tcg_temp_new_i64(); - xtl = tcg_temp_new_i64(); + data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); - tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); - tcg_gen_addi_tl(EA, EA, 8); - tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); - set_cpu_vsr(a->rt, xth, true); - set_cpu_vsr(a->rt, xtl, false); + tcg_gen_qemu_ld_i128(data, EA, ctx->mem_idx, + MO_BE | MO_128 | MO_ATOM_IFALIGN_PAIR); + set_vsr_full(a->rt, data); return true; } @@ -385,20 +392,17 @@ static bool trans_STXVH8X(DisasContext *ctx, arg_STXVH8X *a) static bool trans_STXVB16X(DisasContext *ctx, arg_STXVB16X *a) { TCGv EA; - TCGv_i64 xsh, xsl; + TCGv_i128 data; REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); - xsh = tcg_temp_new_i64(); - xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, a->rt, true); - get_cpu_vsr(xsl, a->rt, false); + data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); - tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEUQ); - tcg_gen_addi_tl(EA, EA, 8); - tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); + get_vsr_full(data, a->rt); + tcg_gen_qemu_st_i128(data, EA, ctx->mem_idx, + MO_BE | MO_128 | MO_ATOM_IFALIGN_PAIR); return true; } @@ -2175,13 +2179,13 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, int rt, bool store, bool paired) { TCGv ea; - TCGv_i64 xt; + TCGv_i128 data; MemOp mop; int rt1, rt2; - xt = tcg_temp_new_i64(); + data = tcg_temp_new_i128(); - mop = DEF_MEMOP(MO_UQ); + mop = DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR); gen_set_access_type(ctx, ACCESS_INT); ea = do_ea_calc(ctx, ra, displ); @@ -2195,32 +2199,20 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, } if (store) { - get_cpu_vsr(xt, rt1, !ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt1, ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + get_vsr_full(data, rt1); + tcg_gen_qemu_st_i128(data, ea, ctx->mem_idx, mop); if (paired) { - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt2, !ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt2, ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + gen_addr_add(ctx, ea, ea, 16); + get_vsr_full(data, rt2); + tcg_gen_qemu_st_i128(data, ea, ctx->mem_idx, mop); } } else { - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt1, xt, !ctx->le_mode); - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt1, xt, ctx->le_mode); + tcg_gen_qemu_ld_i128(data, ea, ctx->mem_idx, mop); + set_vsr_full(rt1, data); if (paired) { - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt2, xt, !ctx->le_mode); - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt2, xt, ctx->le_mode); + gen_addr_add(ctx, ea, ea, 16); + tcg_gen_qemu_ld_i128(data, ea, ctx->mem_idx, mop); + set_vsr_full(rt2, data); } } return true; From 698faf3304805d8951bb0de0ab08f5d2dbeae7cf Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:36 +0200 Subject: [PATCH 2545/2884] target/ppc: Reorganise and rename ppc_hash32_pp_prot() Reorganise ppc_hash32_pp_prot() swapping the if legs so it does not test for negative first and clean up to make it shorter. Also rename it to ppc_hash32_prot(). Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-hash32.c | 2 +- target/ppc/mmu-hash32.h | 43 ++++++++++++++++------------------------- target/ppc/mmu_common.c | 2 +- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index d5f2057eb1..8a446c8a7d 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -45,7 +45,7 @@ static int ppc_hash32_pte_prot(int mmu_idx, key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); pp = pte.pte1 & HPTE32_R_PP; - return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); + return ppc_hash32_prot(key, pp, !!(sr & SR32_NX)); } static target_ulong hash32_bat_size(int mmu_idx, diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index f0ce6951b4..bc4eedbecc 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -102,49 +102,40 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } -static inline int ppc_hash32_pp_prot(bool key, int pp, bool nx) +static inline int ppc_hash32_prot(bool key, int pp, bool nx) { int prot; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - case 0x3: - prot = PAGE_READ; - break; - - default: - abort(); - } - } else { + if (key) { switch (pp) { case 0x0: prot = 0; break; - case 0x1: case 0x3: prot = PAGE_READ; break; - case 0x2: prot = PAGE_READ | PAGE_WRITE; break; - default: - abort(); + g_assert_not_reached(); + } + } else { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + case 0x3: + prot = PAGE_READ; + break; + default: + g_assert_not_reached(); } } - if (nx == 0) { - prot |= PAGE_EXEC; - } - - return prot; + return nx ? prot : prot | PAGE_EXEC; } typedef struct { diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index e2542694f0..08c5b61f76 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -120,7 +120,7 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, } /* Keep the matching PTE information */ ctx->raddr = pte1; - ctx->prot = ppc_hash32_pp_prot(ctx->key, pp, ctx->nx); + ctx->prot = ppc_hash32_prot(ctx->key, pp, ctx->nx); if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); From 5a902297eedfcef267e525df1e0cf64d95a7d885 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:37 +0200 Subject: [PATCH 2546/2884] target/ppc/mmu_common.c: Remove local name for a constant The mmask local variable is a less descriptive local name for a constant. Drop it and use the constant directly in the two places it is needed. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 08c5b61f76..2618cdec6a 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -98,7 +98,7 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int h, MMUAccessType access_type) { - target_ulong ptem, mmask; + target_ulong ptem; int ret, pteh, ptev, pp; ret = -1; @@ -108,12 +108,11 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, if (ptev && h == pteh) { /* Check vsid & api */ ptem = pte0 & PTE_PTEM_MASK; - mmask = PTE_CHECK_MASK; pp = pte1 & 0x00000003; if (ptem == ctx->ptem) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { + if ((ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) { qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); return -3; } From 15465dd8b9e29d2c18d5eecfa0ba0fbdc6c8e511 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:38 +0200 Subject: [PATCH 2547/2884] target/ppc/mmu_common.c: Remove single use local variable The ptem variable in ppc6xx_tlb_pte_check() is used only once, simplify by removing it as the value is already clear itself without adding a local name for it. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 2618cdec6a..371ec24485 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -98,7 +98,6 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int h, MMUAccessType access_type) { - target_ulong ptem; int ret, pteh, ptev, pp; ret = -1; @@ -107,9 +106,8 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, pteh = (pte0 >> 6) & 1; if (ptev && h == pteh) { /* Check vsid & api */ - ptem = pte0 & PTE_PTEM_MASK; pp = pte1 & 0x00000003; - if (ptem == ctx->ptem) { + if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ if ((ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) { From f6b50257c7c7297be6bcafe8ff977a38c965d0c0 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:39 +0200 Subject: [PATCH 2548/2884] target/ppc/mmu_common.c: Remove single use local variable The ptev variable in ppc6xx_tlb_pte_check() is used only once and just obfuscates an otherwise clear value. Get rid of it. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 371ec24485..16578f7fa5 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -98,13 +98,12 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int h, MMUAccessType access_type) { - int ret, pteh, ptev, pp; + int ret, pteh, pp; ret = -1; /* Check validity and table match */ - ptev = pte_is_valid(pte0); pteh = (pte0 >> 6) & 1; - if (ptev && h == pteh) { + if (pte_is_valid(pte0) && h == pteh) { /* Check vsid & api */ pp = pte1 & 0x00000003; if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) { From 3208c36ad34213eee36b1427d8cb944cfa0a192c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:40 +0200 Subject: [PATCH 2549/2884] target/ppc/mmu_common.c: Remove another single use local variable In ppc6xx_tlb_pte_check() the pteh variable is used only once to compare to the h parameter of the function. Inline its value and use pteh name for the function parameter which is more descriptive. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 16578f7fa5..b21f52290f 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -95,15 +95,14 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, } static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, + target_ulong pte1, int pteh, MMUAccessType access_type) { - int ret, pteh, pp; + int ret, pp; ret = -1; /* Check validity and table match */ - pteh = (pte0 >> 6) & 1; - if (pte_is_valid(pte0) && h == pteh) { + if (pte_is_valid(pte0) && ((pte0 >> 6) & 1) == pteh) { /* Check vsid & api */ pp = pte1 & 0x00000003; if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) { From 7ee01cf8632e0666f933a88ddd97315cc17cc4e0 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:41 +0200 Subject: [PATCH 2550/2884] target/ppc/mmu_common.c: Remove yet another single use local variable In ppc6xx_tlb_pte_check() the pp variable is used only once to pass it to a function parameter with the same name. Remove the local and inline the value. Also use named constant for the hex value to make it clearer. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index b21f52290f..799d2ced9b 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -98,13 +98,12 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int pteh, MMUAccessType access_type) { - int ret, pp; + int ret; ret = -1; /* Check validity and table match */ if (pte_is_valid(pte0) && ((pte0 >> 6) & 1) == pteh) { /* Check vsid & api */ - pp = pte1 & 0x00000003; if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ @@ -115,7 +114,7 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, } /* Keep the matching PTE information */ ctx->raddr = pte1; - ctx->prot = ppc_hash32_prot(ctx->key, pp, ctx->nx); + ctx->prot = ppc_hash32_prot(ctx->key, pte1 & HPTE32_R_PP, ctx->nx); if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); From 0e65cea1bd33d4f2917b272c3b03e0eeb8e7b6fd Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:42 +0200 Subject: [PATCH 2551/2884] target/ppc/mmu_common.c: Return directly in ppc6xx_tlb_pte_check() Instead of using a local ret variable return directly and remove the local. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 799d2ced9b..a5ae11394d 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -98,9 +98,6 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int pteh, MMUAccessType access_type) { - int ret; - - ret = -1; /* Check validity and table match */ if (pte_is_valid(pte0) && ((pte0 >> 6) & 1) == pteh) { /* Check vsid & api */ @@ -118,16 +115,15 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); - ret = 0; + return 0; } else { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - ret = -2; + return -2; } } } - - return ret; + return -1; } static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, From 9e2d6802b5c7d15d0d82bb7c9370ebd3df7492ac Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:43 +0200 Subject: [PATCH 2552/2884] target/ppc/mmu_common.c: Simplify ppc6xx_tlb_pte_check() Invert conditions to avoid deep nested ifs and return early instead. Remove some obvious comments that don't add more clarity. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 43 ++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index a5ae11394d..28adb3ca10 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -99,31 +99,26 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, MMUAccessType access_type) { /* Check validity and table match */ - if (pte_is_valid(pte0) && ((pte0 >> 6) & 1) == pteh) { - /* Check vsid & api */ - if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) { - if (ctx->raddr != (hwaddr)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) { - qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Keep the matching PTE information */ - ctx->raddr = pte1; - ctx->prot = ppc_hash32_prot(ctx->key, pte1 & HPTE32_R_PP, ctx->nx); - if (check_prot_access_type(ctx->prot, access_type)) { - /* Access granted */ - qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); - return 0; - } else { - /* Access right violation */ - qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - return -2; - } - } + if (!pte_is_valid(pte0) || ((pte0 >> 6) & 1) != pteh || + (pte0 & PTE_PTEM_MASK) != ctx->ptem) { + return -1; + } + /* all matches should have equal RPN, WIMG & PP */ + if (ctx->raddr != (hwaddr)-1ULL && + (ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) { + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); + return -3; + } + /* Keep the matching PTE information */ + ctx->raddr = pte1; + ctx->prot = ppc_hash32_prot(ctx->key, pte1 & HPTE32_R_PP, ctx->nx); + if (check_prot_access_type(ctx->prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + return 0; + } else { + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + return -2; } - return -1; } static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, From 0e2d7fc817678d8eedd8cae33bb7c191887466c8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:44 +0200 Subject: [PATCH 2553/2884] target/ppc/mmu_common.c: Remove unused field from mmu_ctx_t The eaddr field of mmu_ctx_t is set once but never used so can be removed. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 28adb3ca10..0a07023f48 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -40,7 +40,6 @@ /* Context used internally during MMU translations */ typedef struct { hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ int prot; /* Protection bits */ hwaddr hash[2]; /* Pagetable hash values */ target_ulong ptem; /* Virtual segment ID | API */ @@ -348,7 +347,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* Perform segment based translation when no BATs matched */ pr = FIELD_EX64(env->msr, MSR, PR); - ctx->eaddr = eaddr; sr = env->sr[eaddr >> 28]; ctx->key = (((sr & 0x20000000) && pr) || From f6f8838b055d231ee4bfc31ddaac95fae20834b6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:45 +0200 Subject: [PATCH 2554/2884] target/ppc/mmu_common.c: Remove hash field from mmu_ctx_t Return hash value via a parameter and remove it from mmu_ctx.t. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 0a07023f48..e3537c63c0 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -41,7 +41,6 @@ typedef struct { hwaddr raddr; /* Real address */ int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ int nx; /* Non-execute area */ @@ -331,7 +330,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, + target_ulong eaddr, hwaddr *hashp, MMUAccessType access_type, int type) { PowerPCCPU *cpu = env_archcpu(env); @@ -379,8 +378,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx " htab_mask " HWADDR_FMT_plx " hash " HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; + *hashp = hash; /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; @@ -761,8 +759,8 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; mmu_ctx_t ctx; - int type; - int ret; + hwaddr hash = 0; /* init to 0 to avoid used uninit warning */ + int type, ret; if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { return true; @@ -779,9 +777,8 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, } ctx.prot = 0; - ctx.hash[0] = 0; - ctx.hash[1] = 0; - ret = mmu6xx_get_physical_address(env, &ctx, eaddr, access_type, type); + ret = mmu6xx_get_physical_address(env, &ctx, eaddr, &hash, + access_type, type); if (ret == 0) { *raddrp = ctx.raddr; *protp = ctx.prot; @@ -834,9 +831,9 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, tlb_miss: env->error_code |= ctx.key << 19; env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); + get_pteg_offset32(cpu, hash); env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); + get_pteg_offset32(cpu, ~hash); break; case -2: /* Access rights violation */ From f8e0cc94192bd040421ff704a8efdc3a83391ffe Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:46 +0200 Subject: [PATCH 2555/2884] target/ppc/mmu_common.c: Remove pte_update_flags() This function is used only once, its return value is ignored and one of its parameter is a return value from a previous call. It is better to inline it in the caller and remove it. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index e3537c63c0..c4902b7632 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -119,39 +119,14 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, } } -static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, MMUAccessType access_type) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & 0x00000100)) { - /* Update accessed flag */ - *pte1p |= 0x00000100; - store = 1; - } - if (!(*pte1p & 0x00000080)) { - if (access_type == MMU_DATA_STORE && ret == 0) { - /* Update changed flag */ - *pte1p |= 0x00000080; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - /* Software driven TLB helpers */ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, MMUAccessType access_type) { ppc6xx_tlb_t *tlb; - int nr, best, way; - int ret; + target_ulong *pte1p; + int nr, best, way, ret; best = -1; ret = -1; /* No TLB found */ @@ -204,7 +179,17 @@ done: " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ - pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); + pte1p = &env->tlb.tlb6[best].pte1; + *pte1p |= 0x00000100; /* Update accessed flag */ + if (!(*pte1p & 0x00000080)) { + if (access_type == MMU_DATA_STORE && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + } else { + /* Force page fault for first write access */ + ctx->prot &= ~PAGE_WRITE; + } + } } #if defined(DUMP_PAGE_TABLES) if (qemu_loglevel_mask(CPU_LOG_MMU)) { From 691cf34f216141138bb0289735a762dd7d812137 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:47 +0200 Subject: [PATCH 2556/2884] target/ppc/mmu_common.c: Remove nx field from mmu_ctx_t Pass it as a parameter instead. Also use named constants instead of hex values when extracting bits from SR. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index c4902b7632..9f402a979d 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -43,7 +43,6 @@ typedef struct { int prot; /* Protection bits */ target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ - int nx; /* Non-execute area */ } mmu_ctx_t; void ppc_store_sdr1(CPUPPCState *env, target_ulong value) @@ -94,7 +93,7 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int pteh, - MMUAccessType access_type) + MMUAccessType access_type, bool nx) { /* Check validity and table match */ if (!pte_is_valid(pte0) || ((pte0 >> 6) & 1) != pteh || @@ -109,7 +108,7 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, } /* Keep the matching PTE information */ ctx->raddr = pte1; - ctx->prot = ppc_hash32_prot(ctx->key, pte1 & HPTE32_R_PP, ctx->nx); + ctx->prot = ppc_hash32_prot(ctx->key, pte1 & HPTE32_R_PP, nx); if (check_prot_access_type(ctx->prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); return 0; @@ -121,8 +120,9 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, /* Software driven TLB helpers */ -static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type) +static int ppc6xx_tlb_check(CPUPPCState *env, + mmu_ctx_t *ctx, target_ulong eaddr, + MMUAccessType access_type, bool nx) { ppc6xx_tlb_t *tlb; target_ulong *pte1p; @@ -150,7 +150,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, access_type == MMU_DATA_STORE ? 'S' : 'L', access_type == MMU_INST_FETCH ? 'I' : 'D'); switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, - 0, access_type)) { + 0, access_type, nx)) { case -2: /* Access violation */ ret = -2; @@ -322,7 +322,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, hwaddr hash; target_ulong vsid, sr, pgidx; int ds, target_page_bits; - bool pr; + bool pr, nx; /* First try to find a BAT entry if there are any */ if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { @@ -336,8 +336,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ctx->key = (((sr & 0x20000000) && pr) || ((sr & 0x40000000) && !pr)) ? 1 : 0; ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; + nx = sr & SR32_NX; + vsid = sr & SR32_VSID; target_page_bits = TARGET_PAGE_BITS; qemu_log_mask(CPU_LOG_MMU, "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx @@ -352,10 +352,10 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ctx->ptem = (vsid << 7) | (pgidx >> 10); qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " - TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); + TARGET_FMT_lx "\n", ctx->key, ds, nx, vsid); if (!ds) { /* Check if instruction fetch is allowed, if needed */ - if (type == ACCESS_CODE && ctx->nx) { + if (type == ACCESS_CODE && nx) { qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); return -3; } @@ -368,7 +368,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ - return ppc6xx_tlb_check(env, ctx, eaddr, access_type); + return ppc6xx_tlb_check(env, ctx, eaddr, access_type, nx); } /* Direct-store segment : absolutely *BUGGY* for now */ From aaf5845b87c6acb7f3e95ea8b45947f98c3fdc7e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:48 +0200 Subject: [PATCH 2557/2884] target/ppc/mmu_common.c: Convert local variable to bool In mmu6xx_get_physical_address() ds is used as bool, declare it as such. Also use named constant instead of hex value. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 9f402a979d..5145bde7f9 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -321,8 +321,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; target_ulong vsid, sr, pgidx; - int ds, target_page_bits; - bool pr, nx; + int target_page_bits; + bool pr, ds, nx; /* First try to find a BAT entry if there are any */ if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { @@ -335,7 +335,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, sr = env->sr[eaddr >> 28]; ctx->key = (((sr & 0x20000000) && pr) || ((sr & 0x40000000) && !pr)) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; + ds = sr & SR32_T; nx = sr & SR32_NX; vsid = sr & SR32_VSID; target_page_bits = TARGET_PAGE_BITS; From 8abd6d4288f69627f49d1e6e228d2f0d9d490c21 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:49 +0200 Subject: [PATCH 2558/2884] target/ppc/mmu_common.c: Remove single use local variable In mmu6xx_get_physical_address() tagtet_page_bits local is declared only to use TARGET_PAGE_BITS once. Drop the unneeded variable. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 5145bde7f9..0152e8d875 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -321,7 +321,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; target_ulong vsid, sr, pgidx; - int target_page_bits; bool pr, ds, nx; /* First try to find a BAT entry if there are any */ @@ -338,7 +337,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ds = sr & SR32_T; nx = sr & SR32_NX; vsid = sr & SR32_VSID; - target_page_bits = TARGET_PAGE_BITS; qemu_log_mask(CPU_LOG_MMU, "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx @@ -347,7 +345,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, (int)FIELD_EX64(env->msr, MSR, IR), (int)FIELD_EX64(env->msr, MSR, DR), pr ? 1 : 0, access_type == MMU_DATA_STORE, type); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; hash = vsid ^ pgidx; ctx->ptem = (vsid << 7) | (pgidx >> 10); From 40df08d2239f00abacdd58a39c34a66264ec91a9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:50 +0200 Subject: [PATCH 2559/2884] target/ppc/mmu_common.c: Simplify a switch statement In mmu6xx_get_physical_address() the switch handles all cases so the default is never reached and can be dropped. Also group together cases which just return -4. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 0152e8d875..b2993e8563 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -375,15 +375,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, case ACCESS_INT: /* Integer load/store : only access allowed */ break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; case ACCESS_CACHE: /* * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi @@ -393,12 +384,10 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, */ ctx->raddr = eaddr; return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need address" - " translation\n"); + case ACCESS_CODE: /* No code fetch is allowed in direct-store areas */ + case ACCESS_FLOAT: /* Floating point load/store */ + case ACCESS_RES: /* lwarx, ldarx or srwcx. */ + case ACCESS_EXT: /* eciwx or ecowx */ return -4; } if ((access_type == MMU_DATA_STORE || ctx->key != 1) && From 0ce61ffaf1c573fdbd2079214499b435b71a1b83 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:51 +0200 Subject: [PATCH 2560/2884] target/ppc/mmu_common.c: Inline and remove ppc6xx_tlb_pte_check() This function is only called once and we can make the caller simpler by inlining it. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 71 +++++++++++++---------------------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index b2993e8563..784e833ff2 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -91,33 +91,6 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, return nr; } -static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int pteh, - MMUAccessType access_type, bool nx) -{ - /* Check validity and table match */ - if (!pte_is_valid(pte0) || ((pte0 >> 6) & 1) != pteh || - (pte0 & PTE_PTEM_MASK) != ctx->ptem) { - return -1; - } - /* all matches should have equal RPN, WIMG & PP */ - if (ctx->raddr != (hwaddr)-1ULL && - (ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) { - qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); - return -3; - } - /* Keep the matching PTE information */ - ctx->raddr = pte1; - ctx->prot = ppc_hash32_prot(ctx->key, pte1 & HPTE32_R_PP, nx); - if (check_prot_access_type(ctx->prot, access_type)) { - qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); - return 0; - } else { - qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - return -2; - } -} - /* Software driven TLB helpers */ static int ppc6xx_tlb_check(CPUPPCState *env, @@ -149,32 +122,32 @@ static int ppc6xx_tlb_check(CPUPPCState *env, tlb->EPN, eaddr, tlb->pte1, access_type == MMU_DATA_STORE ? 'S' : 'L', access_type == MMU_INST_FETCH ? 'I' : 'D'); - switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, - 0, access_type, nx)) { - case -2: - /* Access violation */ - ret = -2; - best = nr; - break; - case -1: /* No match */ - case -3: /* TLB inconsistency */ - default: - break; - case 0: - /* access granted */ - /* - * XXX: we should go on looping to check all TLBs - * consistency but we can speed-up the whole thing as - * the result would be undefined if TLBs are not - * consistent. - */ + /* Check validity and table match */ + if (!pte_is_valid(tlb->pte0) || ((tlb->pte0 >> 6) & 1) != 0 || + (tlb->pte0 & PTE_PTEM_MASK) != ctx->ptem) { + continue; + } + /* all matches should have equal RPN, WIMG & PP */ + if (ctx->raddr != (hwaddr)-1ULL && + (ctx->raddr & PTE_CHECK_MASK) != (tlb->pte1 & PTE_CHECK_MASK)) { + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); + /* TLB inconsistency */ + continue; + } + /* Keep the matching PTE information */ + best = nr; + ctx->raddr = tlb->pte1; + ctx->prot = ppc_hash32_prot(ctx->key, tlb->pte1 & HPTE32_R_PP, nx); + if (check_prot_access_type(ctx->prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); ret = 0; - best = nr; - goto done; + break; + } else { + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + ret = -2; } } if (best != -1) { -done: qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " HWADDR_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); From cab21e2ecb917abec597a184fd44d479b4bb1e66 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:53 +0200 Subject: [PATCH 2561/2884] target/ppc/mmu_common.c: Remove ptem field from mmu_ctx_t Instead of passing around ptem in context use it once in the same function so it can be removed from mmu_ctx_t. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 784e833ff2..339df377e8 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -41,7 +41,6 @@ typedef struct { hwaddr raddr; /* Real address */ int prot; /* Protection bits */ - target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ } mmu_ctx_t; @@ -95,16 +94,18 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type, bool nx) + MMUAccessType access_type, target_ulong ptem, + bool nx) { ppc6xx_tlb_t *tlb; target_ulong *pte1p; int nr, best, way, ret; + bool is_code = (access_type == MMU_INST_FETCH); best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == MMU_INST_FETCH); + nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); tlb = &env->tlb.tlb6[nr]; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { @@ -124,7 +125,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, access_type == MMU_INST_FETCH ? 'I' : 'D'); /* Check validity and table match */ if (!pte_is_valid(tlb->pte0) || ((tlb->pte0 >> 6) & 1) != 0 || - (tlb->pte0 & PTE_PTEM_MASK) != ctx->ptem) { + (tlb->pte0 & PTE_PTEM_MASK) != ptem) { continue; } /* all matches should have equal RPN, WIMG & PP */ @@ -164,6 +165,10 @@ static int ppc6xx_tlb_check(CPUPPCState *env, } } } + if (ret == -1) { + int r = is_code ? SPR_ICMP : SPR_DCMP; + env->spr[r] = ptem; + } #if defined(DUMP_PAGE_TABLES) if (qemu_loglevel_mask(CPU_LOG_MMU)) { CPUState *cs = env_cpu(env); @@ -293,7 +298,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; - target_ulong vsid, sr, pgidx; + target_ulong vsid, sr, pgidx, ptem; bool pr, ds, nx; /* First try to find a BAT entry if there are any */ @@ -320,7 +325,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, access_type == MMU_DATA_STORE, type); pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); + ptem = (vsid << 7) | (pgidx >> 10); /* Virtual segment ID | API */ qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", ctx->key, ds, nx, vsid); @@ -339,7 +344,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ - return ppc6xx_tlb_check(env, ctx, eaddr, access_type, nx); + return ppc6xx_tlb_check(env, ctx, eaddr, access_type, ptem, nx); } /* Direct-store segment : absolutely *BUGGY* for now */ @@ -741,7 +746,7 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, cs->exception_index = POWERPC_EXCP_IFTLB; env->error_code = 1 << 18; env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + env->spr[SPR_ICMP] |= 0x80000000; goto tlb_miss; case -2: /* Access rights violation */ @@ -772,7 +777,7 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, env->error_code = 0; } env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + env->spr[SPR_DCMP] |= 0x80000000; tlb_miss: env->error_code |= ctx.key << 19; env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + From 719a1da19ee67cfd3ef5b50f778d0204daaeb0b2 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:54 +0200 Subject: [PATCH 2562/2884] target/ppc: Add function to get protection key for hash32 MMU Add a function to get key bit from SR and use it instead of open coded version. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-hash32.c | 9 ++++++--- target/ppc/mmu-hash32.h | 5 +++++ target/ppc/mmu_common.c | 3 +-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 8a446c8a7d..93559447ff 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -42,7 +42,7 @@ static int ppc_hash32_pte_prot(int mmu_idx, { unsigned pp, key; - key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); + key = ppc_hash32_key(mmuidx_pr(mmu_idx), sr); pp = pte.pte1 & HPTE32_R_PP; return ppc_hash32_prot(key, pp, !!(sr & SR32_NX)); @@ -145,7 +145,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); @@ -206,7 +205,11 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, cpu_abort(cs, "ERROR: insn should not need address translation\n"); } - *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; + if (ppc_hash32_key(mmuidx_pr(mmu_idx), sr)) { + *prot = PAGE_READ | PAGE_WRITE; + } else { + *prot = PAGE_READ; + } if (check_prot_access_type(*prot, access_type)) { *raddr = eaddr; return true; diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index bc4eedbecc..5902cf8333 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -102,6 +102,11 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } +static inline bool ppc_hash32_key(bool pr, target_ulong sr) +{ + return pr ? (sr & SR32_KP) : (sr & SR32_KS); +} + static inline int ppc_hash32_prot(bool key, int pp, bool nx) { int prot; diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 339df377e8..1ed2f45ac7 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -310,8 +310,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, pr = FIELD_EX64(env->msr, MSR, PR); sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && pr) || - ((sr & 0x40000000) && !pr)) ? 1 : 0; + ctx->key = ppc_hash32_key(pr, sr); ds = sr & SR32_T; nx = sr & SR32_NX; vsid = sr & SR32_VSID; From 620ba617df15ae0bce3be794c870525e329ab78c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:55 +0200 Subject: [PATCH 2563/2884] target/ppc/mmu-hash32.c: Inline and remove ppc_hash32_pte_prot() This is used only once and can be inlined. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-hash32.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 93559447ff..160311de87 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -37,17 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -static int ppc_hash32_pte_prot(int mmu_idx, - target_ulong sr, ppc_hash_pte32_t pte) -{ - unsigned pp, key; - - key = ppc_hash32_key(mmuidx_pr(mmu_idx), sr); - pp = pte.pte1 & HPTE32_R_PP; - - return ppc_hash32_prot(key, pp, !!(sr & SR32_NX)); -} - static target_ulong hash32_bat_size(int mmu_idx, target_ulong batu, target_ulong batl) { @@ -341,10 +330,10 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong sr; - hwaddr pte_offset; + hwaddr pte_offset, raddr; ppc_hash_pte32_t pte; + bool key; int prot; - hwaddr raddr; /* There are no hash32 large pages. */ *psizep = TARGET_PAGE_BITS; @@ -426,8 +415,8 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 7. Check access permissions */ - - prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); + key = ppc_hash32_key(mmuidx_pr(mmu_idx), sr); + prot = ppc_hash32_prot(key, pte.pte1 & HPTE32_R_PP, sr & SR32_NX); if (!check_prot_access_type(prot, access_type)) { /* Access right violation */ From fa7f2cb91b8f6805ec5d8581ca067ac83acc287e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:56 +0200 Subject: [PATCH 2564/2884] target/ppc/mmu_common.c: Init variable in function that relies on it The ppc6xx_tlb_check() relies on the caller to initialise raddr field in ctx. Move this init from the only caller into the function. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 1ed2f45ac7..fe321ab49c 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -102,6 +102,8 @@ static int ppc6xx_tlb_check(CPUPPCState *env, int nr, best, way, ret; bool is_code = (access_type == MMU_INST_FETCH); + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { @@ -340,8 +342,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); *hashp = hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ return ppc6xx_tlb_check(env, ctx, eaddr, access_type, ptem, nx); } From da5c1d20e9d63575cb358158895a0efa55682c35 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:57 +0200 Subject: [PATCH 2565/2884] target/ppc/mmu_common.c: Remove key field from mmu_ctx_t Pass it as a function parameter and remove it from mmu_ctx_t. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index fe321ab49c..be09c3b1a3 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -41,7 +41,6 @@ typedef struct { hwaddr raddr; /* Real address */ int prot; /* Protection bits */ - int key; /* Access key */ } mmu_ctx_t; void ppc_store_sdr1(CPUPPCState *env, target_ulong value) @@ -95,7 +94,7 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, MMUAccessType access_type, target_ulong ptem, - bool nx) + bool key, bool nx) { ppc6xx_tlb_t *tlb; target_ulong *pte1p; @@ -140,7 +139,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, /* Keep the matching PTE information */ best = nr; ctx->raddr = tlb->pte1; - ctx->prot = ppc_hash32_prot(ctx->key, tlb->pte1 & HPTE32_R_PP, nx); + ctx->prot = ppc_hash32_prot(key, tlb->pte1 & HPTE32_R_PP, nx); if (check_prot_access_type(ctx->prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); ret = 0; @@ -295,13 +294,14 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, hwaddr *hashp, + target_ulong eaddr, + hwaddr *hashp, bool *keyp, MMUAccessType access_type, int type) { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; target_ulong vsid, sr, pgidx, ptem; - bool pr, ds, nx; + bool key, pr, ds, nx; /* First try to find a BAT entry if there are any */ if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { @@ -312,7 +312,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, pr = FIELD_EX64(env->msr, MSR, PR); sr = env->sr[eaddr >> 28]; - ctx->key = ppc_hash32_key(pr, sr); + key = ppc_hash32_key(pr, sr); + *keyp = key; ds = sr & SR32_T; nx = sr & SR32_NX; vsid = sr & SR32_VSID; @@ -329,7 +330,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ptem = (vsid << 7) | (pgidx >> 10); /* Virtual segment ID | API */ qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " - TARGET_FMT_lx "\n", ctx->key, ds, nx, vsid); + TARGET_FMT_lx "\n", key, ds, nx, vsid); if (!ds) { /* Check if instruction fetch is allowed, if needed */ if (type == ACCESS_CODE && nx) { @@ -343,7 +344,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, *hashp = hash; /* Software TLB search */ - return ppc6xx_tlb_check(env, ctx, eaddr, access_type, ptem, nx); + return ppc6xx_tlb_check(env, ctx, eaddr, access_type, ptem, key, nx); } /* Direct-store segment : absolutely *BUGGY* for now */ @@ -367,8 +368,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, case ACCESS_EXT: /* eciwx or ecowx */ return -4; } - if ((access_type == MMU_DATA_STORE || ctx->key != 1) && - (access_type == MMU_DATA_LOAD || ctx->key != 0)) { + if ((access_type == MMU_DATA_STORE || !key) && + (access_type == MMU_DATA_LOAD || key)) { ctx->raddr = eaddr; return 2; } @@ -709,6 +710,7 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, CPUPPCState *env = &cpu->env; mmu_ctx_t ctx; hwaddr hash = 0; /* init to 0 to avoid used uninit warning */ + bool key; int type, ret; if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { @@ -726,7 +728,7 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, } ctx.prot = 0; - ret = mmu6xx_get_physical_address(env, &ctx, eaddr, &hash, + ret = mmu6xx_get_physical_address(env, &ctx, eaddr, &hash, &key, access_type, type); if (ret == 0) { *raddrp = ctx.raddr; @@ -778,7 +780,7 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_DMISS] = eaddr; env->spr[SPR_DCMP] |= 0x80000000; tlb_miss: - env->error_code |= ctx.key << 19; + env->error_code |= key << 19; env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + get_pteg_offset32(cpu, hash); env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + From aa781c102a445e1007a307a972fed24c66b9c24c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:58 +0200 Subject: [PATCH 2566/2884] target/ppc/mmu_common.c: Stop using ctx in ppc6xx_tlb_check() Pass raddr and prot in function parameters instead. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index be09c3b1a3..ede409eb99 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -91,10 +91,9 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, /* Software driven TLB helpers */ -static int ppc6xx_tlb_check(CPUPPCState *env, - mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type, target_ulong ptem, - bool key, bool nx) +static int ppc6xx_tlb_check(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong eaddr, MMUAccessType access_type, + target_ulong ptem, bool key, bool nx) { ppc6xx_tlb_t *tlb; target_ulong *pte1p; @@ -102,7 +101,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, bool is_code = (access_type == MMU_INST_FETCH); /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; + *raddr = (hwaddr)-1ULL; best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { @@ -130,17 +129,17 @@ static int ppc6xx_tlb_check(CPUPPCState *env, continue; } /* all matches should have equal RPN, WIMG & PP */ - if (ctx->raddr != (hwaddr)-1ULL && - (ctx->raddr & PTE_CHECK_MASK) != (tlb->pte1 & PTE_CHECK_MASK)) { + if (*raddr != (hwaddr)-1ULL && + (*raddr & PTE_CHECK_MASK) != (tlb->pte1 & PTE_CHECK_MASK)) { qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); /* TLB inconsistency */ continue; } /* Keep the matching PTE information */ best = nr; - ctx->raddr = tlb->pte1; - ctx->prot = ppc_hash32_prot(key, tlb->pte1 & HPTE32_R_PP, nx); - if (check_prot_access_type(ctx->prot, access_type)) { + *raddr = tlb->pte1; + *prot = ppc_hash32_prot(key, tlb->pte1 & HPTE32_R_PP, nx); + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); ret = 0; break; @@ -152,7 +151,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, if (best != -1) { qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " HWADDR_FMT_plx " prot=%01x ret=%d\n", - ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); + *raddr & TARGET_PAGE_MASK, *prot, ret); /* Update page flags */ pte1p = &env->tlb.tlb6[best].pte1; *pte1p |= 0x00000100; /* Update accessed flag */ @@ -162,7 +161,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, *pte1p |= 0x00000080; } else { /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; + *prot &= ~PAGE_WRITE; } } } @@ -344,7 +343,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, *hashp = hash; /* Software TLB search */ - return ppc6xx_tlb_check(env, ctx, eaddr, access_type, ptem, key, nx); + return ppc6xx_tlb_check(env, &ctx->raddr, &ctx->prot, eaddr, + access_type, ptem, key, nx); } /* Direct-store segment : absolutely *BUGGY* for now */ From 68bf3a7bbc64d75d6a78a7d31b6b26e343a96320 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:12:59 +0200 Subject: [PATCH 2567/2884] target/ppc/mmu_common.c: Rename function parameter Rename parameter of get_bat_6xx_tlb() from virtual to eaddr to match other functions. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ede409eb99..110936ca83 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -221,7 +221,7 @@ static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, } static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, MMUAccessType access_type) + target_ulong eaddr, MMUAccessType access_type) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; @@ -230,7 +230,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, bool ifetch = access_type == MMU_INST_FETCH; qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', virtual); + ifetch ? 'I' : 'D', eaddr); if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; @@ -246,15 +246,15 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); - if ((virtual & 0xF0000000) == BEPIu && - ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + ifetch ? 'I' : 'D', i, eaddr, *BATu, *BATl); + if ((eaddr & 0xF0000000) == BEPIu && + ((eaddr & 0x0FFE0000) & ~bl) == BEPIl) { /* BAT matches */ if (valid != 0) { /* Get physical address */ ctx->raddr = (*BATl & 0xF0000000) | - ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | - (virtual & 0x0001F000); + ((eaddr & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + (eaddr & 0x0001F000); /* Compute access rights */ ctx->prot = prot; if (check_prot_access_type(ctx->prot, access_type)) { @@ -273,7 +273,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, if (ret < 0) { if (qemu_log_enabled()) { qemu_log_mask(CPU_LOG_MMU, "no BAT match for " - TARGET_FMT_lx ":\n", virtual); + TARGET_FMT_lx ":\n", eaddr); for (i = 0; i < 4; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; @@ -284,7 +284,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', - i, virtual, *BATu, *BATl, BEPIu, BEPIl, bl); + i, eaddr, *BATu, *BATl, BEPIu, BEPIl, bl); } } } From 6ca35e8763e9c37a9cedd28d286f78fbbf45968c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:00 +0200 Subject: [PATCH 2568/2884] target/ppc/mmu_common.c: Use defines instead of numeric constants Replace some BAT related constants with defines from mmu-hash32.h Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 110936ca83..aa002bba35 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -201,7 +201,7 @@ static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, target_ulong bl; int pp, valid, prot; - bl = (*BATu & 0x00001FFC) << 15; + bl = (*BATu & BATU32_BL) << 15; valid = 0; prot = 0; if ((!FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000002)) || @@ -241,19 +241,19 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', i, eaddr, *BATu, *BATl); - if ((eaddr & 0xF0000000) == BEPIu && - ((eaddr & 0x0FFE0000) & ~bl) == BEPIl) { + if ((eaddr & BATU32_BEPIU) == BEPIu && + ((eaddr & BATU32_BEPIL) & ~bl) == BEPIl) { /* BAT matches */ if (valid != 0) { /* Get physical address */ - ctx->raddr = (*BATl & 0xF0000000) | - ((eaddr & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + ctx->raddr = (*BATl & BATU32_BEPIU) | + ((eaddr & BATU32_BEPIL & bl) | (*BATl & BATU32_BEPIL)) | (eaddr & 0x0001F000); /* Compute access rights */ ctx->prot = prot; @@ -277,9 +277,9 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, for (i = 0; i < 4; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & BATU32_BL) << 15; qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " @@ -520,9 +520,9 @@ static void mmu6xx_dump_BATs(CPUPPCState *env, int type) for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & BATU32_BL) << 15; qemu_printf("%s BAT%d BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", From d323338629588ea985c68384642169045ca0e16d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:01 +0200 Subject: [PATCH 2569/2884] target/ppc: Remove bat_size_prot() There is already a hash32_bat_prot() function that does most if this and the rest can be inlined. Export hash32_bat_prot() and rename it to ppc_hash32_bat_prot() to match other functions and use it in get_bat_6xx_tlb(). Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-hash32.c | 18 +------------- target/ppc/mmu-hash32.h | 14 +++++++++++ target/ppc/mmu_common.c | 52 ++++++++++------------------------------- 3 files changed, 27 insertions(+), 57 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 160311de87..6f0f0bbb00 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -48,22 +48,6 @@ static target_ulong hash32_bat_size(int mmu_idx, return BATU32_BEPI & ~((batu & BATU32_BL) << 15); } -static int hash32_bat_prot(PowerPCCPU *cpu, - target_ulong batu, target_ulong batl) -{ - int pp, prot; - - prot = 0; - pp = batl & BATL32_PP; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } - return prot; -} - static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, MMUAccessType access_type, int *prot, int mmu_idx) @@ -95,7 +79,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { hwaddr raddr = (batl & mask) | (ea & ~mask); - *prot = hash32_bat_prot(cpu, batu, batl); + *prot = ppc_hash32_bat_prot(batu, batl); return raddr & TARGET_PAGE_MASK; } diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 5902cf8333..bd75f7d647 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -143,6 +143,20 @@ static inline int ppc_hash32_prot(bool key, int pp, bool nx) return nx ? prot : prot | PAGE_EXEC; } +static inline int ppc_hash32_bat_prot(target_ulong batu, target_ulong batl) +{ + int prot = 0; + int pp = batl & BATL32_PP; + + if (pp) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + return prot; +} + typedef struct { uint32_t pte0, pte1; } ppc_hash_pte32_t; diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index aa002bba35..624ed51a92 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -193,40 +193,13 @@ static int ppc6xx_tlb_check(CPUPPCState *env, hwaddr *raddr, int *prot, return ret; } -/* Perform BAT hit & translation */ -static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, target_ulong *BATu, - target_ulong *BATl) -{ - target_ulong bl; - int pp, valid, prot; - - bl = (*BATu & BATU32_BL) << 15; - valid = 0; - prot = 0; - if ((!FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000002)) || - (FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000001))) { - valid = 1; - pp = *BATl & 0x00000003; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } - } - *blp = bl; - *validp = valid; - *protp = prot; -} - static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type) + target_ulong eaddr, MMUAccessType access_type, + bool pr) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; - int i, valid, prot; - int ret = -1; + int i, ret = -1; bool ifetch = access_type == MMU_INST_FETCH; qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, @@ -243,20 +216,19 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, BATl = &BATlt[i]; BEPIu = *BATu & BATU32_BEPIU; BEPIl = *BATu & BATU32_BEPIL; - bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', i, eaddr, *BATu, *BATl); - if ((eaddr & BATU32_BEPIU) == BEPIu && - ((eaddr & BATU32_BEPIL) & ~bl) == BEPIl) { - /* BAT matches */ - if (valid != 0) { + bl = (*BATu & BATU32_BL) << 15; + if ((!pr && (*BATu & BATU32_VS)) || (pr && (*BATu & BATU32_VP))) { + if ((eaddr & BATU32_BEPIU) == BEPIu && + ((eaddr & BATU32_BEPIL) & ~bl) == BEPIl) { /* Get physical address */ ctx->raddr = (*BATl & BATU32_BEPIU) | ((eaddr & BATU32_BEPIL & bl) | (*BATl & BATU32_BEPIL)) | (eaddr & 0x0001F000); /* Compute access rights */ - ctx->prot = prot; + ctx->prot = ppc_hash32_bat_prot(*BATu, *BATl); if (check_prot_access_type(ctx->prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx " prot=%c%c\n", i, ctx->raddr, @@ -300,16 +272,16 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; target_ulong vsid, sr, pgidx, ptem; - bool key, pr, ds, nx; + bool key, ds, nx; + bool pr = FIELD_EX64(env->msr, MSR, PR); /* First try to find a BAT entry if there are any */ - if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { + if (env->nb_BATs && + get_bat_6xx_tlb(env, ctx, eaddr, access_type, pr) == 0) { return 0; } /* Perform segment based translation when no BATs matched */ - pr = FIELD_EX64(env->msr, MSR, PR); - sr = env->sr[eaddr >> 28]; key = ppc_hash32_key(pr, sr); *keyp = key; From 7e590cf6160ab2d3b626f67312f605b7e410e82d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:02 +0200 Subject: [PATCH 2570/2884] target/ppc/mmu_common.c: Stop using ctx in get_bat_6xx_tlb() Pass raddr and prot in function parameters instead Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 624ed51a92..4770b43630 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -193,7 +193,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, hwaddr *raddr, int *prot, return ret; } -static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, +static int get_bat_6xx_tlb(CPUPPCState *env, hwaddr *raddr, int *prot, target_ulong eaddr, MMUAccessType access_type, bool pr) { @@ -224,16 +224,16 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, if ((eaddr & BATU32_BEPIU) == BEPIu && ((eaddr & BATU32_BEPIL) & ~bl) == BEPIl) { /* Get physical address */ - ctx->raddr = (*BATl & BATU32_BEPIU) | + *raddr = (*BATl & BATU32_BEPIU) | ((eaddr & BATU32_BEPIL & bl) | (*BATl & BATU32_BEPIL)) | (eaddr & 0x0001F000); /* Compute access rights */ - ctx->prot = ppc_hash32_bat_prot(*BATu, *BATl); - if (check_prot_access_type(ctx->prot, access_type)) { + *prot = ppc_hash32_bat_prot(*BATu, *BATl); + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx - " prot=%c%c\n", i, ctx->raddr, - ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); + " prot=%c%c\n", i, *raddr, + *prot & PAGE_READ ? 'R' : '-', + *prot & PAGE_WRITE ? 'W' : '-'); ret = 0; } else { ret = -2; @@ -277,7 +277,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* First try to find a BAT entry if there are any */ if (env->nb_BATs && - get_bat_6xx_tlb(env, ctx, eaddr, access_type, pr) == 0) { + get_bat_6xx_tlb(env, &ctx->raddr, &ctx->prot, eaddr, + access_type, pr) == 0) { return 0; } From bfb5a5eee5cfbe9f248472f7489fed241a2dab21 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:03 +0200 Subject: [PATCH 2571/2884] target/ppc/mmu_common.c: Remove mmu_ctx_t Completely get rid of mmu_ctx_t after converting the remaining functions to pass raddr and prot without the context struct. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu_common.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 4770b43630..60f8736210 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -37,12 +37,6 @@ /* #define DUMP_PAGE_TABLES */ -/* Context used internally during MMU translations */ -typedef struct { - hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ -} mmu_ctx_t; - void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); @@ -264,8 +258,8 @@ static int get_bat_6xx_tlb(CPUPPCState *env, hwaddr *raddr, int *prot, return ret; } -static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, +static int mmu6xx_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong eaddr, hwaddr *hashp, bool *keyp, MMUAccessType access_type, int type) { @@ -277,8 +271,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* First try to find a BAT entry if there are any */ if (env->nb_BATs && - get_bat_6xx_tlb(env, &ctx->raddr, &ctx->prot, eaddr, - access_type, pr) == 0) { + get_bat_6xx_tlb(env, raddr, prot, eaddr, access_type, pr) == 0) { return 0; } @@ -316,7 +309,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, *hashp = hash; /* Software TLB search */ - return ppc6xx_tlb_check(env, &ctx->raddr, &ctx->prot, eaddr, + return ppc6xx_tlb_check(env, raddr, prot, eaddr, access_type, ptem, key, nx); } @@ -333,7 +326,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, * Should make the instruction do no-op. As it already do * no-op, it's quite easy :-) */ - ctx->raddr = eaddr; + *raddr = eaddr; return 0; case ACCESS_CODE: /* No code fetch is allowed in direct-store areas */ case ACCESS_FLOAT: /* Floating point load/store */ @@ -343,7 +336,7 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } if ((access_type == MMU_DATA_STORE || !key) && (access_type == MMU_DATA_LOAD || key)) { - ctx->raddr = eaddr; + *raddr = eaddr; return 2; } return -2; @@ -681,7 +674,6 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - mmu_ctx_t ctx; hwaddr hash = 0; /* init to 0 to avoid used uninit warning */ bool key; int type, ret; @@ -700,12 +692,9 @@ static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, type = ACCESS_INT; } - ctx.prot = 0; - ret = mmu6xx_get_physical_address(env, &ctx, eaddr, &hash, &key, + ret = mmu6xx_get_physical_address(env, raddrp, protp, eaddr, &hash, &key, access_type, type); if (ret == 0) { - *raddrp = ctx.raddr; - *protp = ctx.prot; *psizep = TARGET_PAGE_BITS; return true; } else if (!guest_visible) { From 51993bef122896b29d1be218d536b6b3211cf2f1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:04 +0200 Subject: [PATCH 2572/2884] target/ppc/mmu-hash32.c: Inline and remove ppc_hash32_pte_raddr() This function is used only once and does not add more clarity than doing it inline. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-hash32.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 6f0f0bbb00..c4de1647e2 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -298,15 +298,6 @@ static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, return pte_offset; } -static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, - target_ulong eaddr) -{ - hwaddr rpn = pte.pte1 & HPTE32_R_RPN; - hwaddr mask = ~TARGET_PAGE_MASK; - - return (rpn & ~mask) | (eaddr & mask); -} - bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible) @@ -440,11 +431,12 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, */ prot &= ~PAGE_WRITE; } - } + } + *protp = prot; /* 9. Determine the real address from the PTE */ - - *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr); - *protp = prot; + *raddrp = pte.pte1 & HPTE32_R_RPN; + *raddrp &= TARGET_PAGE_MASK; + *raddrp |= eaddr & ~TARGET_PAGE_MASK; return true; } From 9eb0530033ac3a52fcca055213bc512e4e29b954 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:05 +0200 Subject: [PATCH 2573/2884] target/ppc/mmu-hash32.c: Move get_pteg_offset32() to the header This function is a simple shared function, move it to other similar static inline functions in the header. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-hash32.c | 7 ------- target/ppc/mmu-hash32.h | 6 +++++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index c4de1647e2..44b16142ab 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -201,13 +201,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, return false; } -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) -{ - target_ulong mask = ppc_hash32_hpt_mask(cpu); - - return (hash * HASH_PTEG_SIZE_32) & mask; -} - static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte32_t *pte) diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index bd75f7d647..2838de031c 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -3,7 +3,6 @@ #ifndef CONFIG_USER_ONLY -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -102,6 +101,11 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } +static inline hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) +{ + return (hash * HASH_PTEG_SIZE_32) & ppc_hash32_hpt_mask(cpu); +} + static inline bool ppc_hash32_key(bool pr, target_ulong sr) { return pr ? (sr & SR32_KP) : (sr & SR32_KS); From 14a43ab3335afb3f68ca103739405178abe070ea Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:06 +0200 Subject: [PATCH 2574/2884] target/ppc: Unexport some functions from mmu-book3s-v3.h The ppc_hash64_hpt_base() and ppc_hash64_hpt_mask() functions are mostly used by mmu-hash64.c only but there is one call to ppc_hash64_hpt_mask() in hw/ppc/spapr_vhyp_mmu.c.in a helper function that can be moved to mmu-hash64.c which allows these functions to be removed from the header. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- hw/ppc/spapr_vhyp_mmu.c | 21 ++++------------ target/ppc/mmu-book3s-v3.h | 40 ------------------------------- target/ppc/mmu-hash64.c | 49 ++++++++++++++++++++++++++++++++++++++ target/ppc/mmu-hash64.h | 1 + 4 files changed, 54 insertions(+), 57 deletions(-) diff --git a/hw/ppc/spapr_vhyp_mmu.c b/hw/ppc/spapr_vhyp_mmu.c index b3dd8b3a59..2d41d7f77b 100644 --- a/hw/ppc/spapr_vhyp_mmu.c +++ b/hw/ppc/spapr_vhyp_mmu.c @@ -15,19 +15,6 @@ #include "helper_regs.h" #include "hw/ppc/spapr.h" #include "mmu-hash64.h" -#include "mmu-book3s-v3.h" - - -static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) -{ - /* - * hash value/pteg group index is normalized by HPT mask - */ - if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { - return false; - } - return true; -} static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) @@ -70,7 +57,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, pteh &= ~0x60ULL; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return H_PARAMETER; } @@ -119,7 +106,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu const ppc_hash_pte64_t *hptes; target_ulong v, r; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return REMOVE_PARM; } @@ -250,7 +237,7 @@ static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, const ppc_hash_pte64_t *hptes; target_ulong v, r; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return H_PARAMETER; } @@ -287,7 +274,7 @@ static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, int i, ridx, n_entries = 1; const ppc_hash_pte64_t *hptes; - if (!valid_ptex(cpu, ptex)) { + if (!ppc_hash64_valid_ptex(cpu, ptex)) { return H_PARAMETER; } diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index f3f7993958..263ce55c1f 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -83,46 +83,6 @@ static inline bool ppc64_v3_radix(PowerPCCPU *cpu) return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR); } -static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) -{ - uint64_t base; - - if (cpu->vhyp) { - return 0; - } - if (cpu->env.mmu_model == POWERPC_MMU_3_00) { - ppc_v3_pate_t pate; - - if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { - return 0; - } - base = pate.dw0; - } else { - base = cpu->env.spr[SPR_SDR1]; - } - return base & SDR_64_HTABORG; -} - -static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) -{ - uint64_t base; - - if (cpu->vhyp) { - return cpu->vhyp_class->hpt_mask(cpu->vhyp); - } - if (cpu->env.mmu_model == POWERPC_MMU_3_00) { - ppc_v3_pate_t pate; - - if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { - return 0; - } - base = pate.dw0; - } else { - base = cpu->env.spr[SPR_SDR1]; - } - return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; -} - #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index cbc8efa0c3..7bc0323f26 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -508,6 +508,46 @@ static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) return prot; } +static hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return 0; + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return base & SDR_64_HTABORG; +} + +static hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return cpu->vhyp_class->hpt_mask(cpu->vhyp); + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; +} + const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n) { @@ -545,6 +585,15 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, false, n * HASH_PTE_SIZE_64); } +bool ppc_hash64_valid_ptex(PowerPCCPU *cpu, target_ulong ptex) +{ + /* hash value/pteg group index is normalized by HPT mask */ + if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { + return false; + } + return true; +} + static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps, uint64_t pte0, uint64_t pte1) { diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index de653fcae5..ae8d4b37ae 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -120,6 +120,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n); void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n); +bool ppc_hash64_valid_ptex(PowerPCCPU *cpu, target_ulong ptex); static inline uint64_t ppc_hash64_hpte0(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, int i) From b864074ce074006f9bcc66a11cd4205355abb9ac Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:07 +0200 Subject: [PATCH 2575/2884] target/ppc/mmu-radix64: Remove externally unused parts from header Move the parts not needed outside of mmu-radix64.c from the header to the C file to leave only parts in the header that need to be exported. Also drop unneded include of this header. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Acked-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-book3s-v3.c | 1 - target/ppc/mmu-radix64.c | 49 +++++++++++++++++++++++++++++++++++ target/ppc/mmu-radix64.h | 53 +------------------------------------- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index c8f69b3df9..a812cb5113 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" -#include "mmu-radix64.h" bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 5a02e4963b..cf9619e847 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -29,6 +29,37 @@ #include "mmu-radix64.h" #include "mmu-book3s-v3.h" +/* Radix Partition Table Entry Fields */ +#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 +#define PATE1_R_PRTS 0x000000000000001F + +/* Radix Process Table Entry Fields */ +#define PRTBE_R_GET_RTS(rts) \ + ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31) +#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00 +#define PRTBE_R_RPDS 0x000000000000001F + +/* Radix Page Directory/Table Entry Fields */ +#define R_PTE_VALID 0x8000000000000000 +#define R_PTE_LEAF 0x4000000000000000 +#define R_PTE_SW0 0x2000000000000000 +#define R_PTE_RPN 0x01FFFFFFFFFFF000 +#define R_PTE_SW1 0x0000000000000E00 +#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7)) +#define R_PTE_R 0x0000000000000100 +#define R_PTE_C 0x0000000000000080 +#define R_PTE_ATT 0x0000000000000030 +#define R_PTE_ATT_NORMAL 0x0000000000000000 +#define R_PTE_ATT_SAO 0x0000000000000010 +#define R_PTE_ATT_NI_IO 0x0000000000000020 +#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030 +#define R_PTE_EAA_PRIV 0x0000000000000008 +#define R_PTE_EAA_R 0x0000000000000004 +#define R_PTE_EAA_RW 0x0000000000000002 +#define R_PTE_EAA_X 0x0000000000000001 +#define R_PDE_NLB PRTBE_R_RPDB +#define R_PDE_NLS PRTBE_R_RPDS + static bool ppc_radix64_get_fully_qualified_addr(const CPUPPCState *env, vaddr eaddr, uint64_t *lpid, uint64_t *pid) @@ -180,6 +211,24 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, } } +static int ppc_radix64_get_prot_eaa(uint64_t pte) +{ + return (pte & R_PTE_EAA_R ? PAGE_READ : 0) | + (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) | + (pte & R_PTE_EAA_X ? PAGE_EXEC : 0); +} + +static int ppc_radix64_get_prot_amr(const PowerPCCPU *cpu) +{ + const CPUPPCState *env = &cpu->env; + int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */ + int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */ + + return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */ + (amr & 0x1 ? 0 : PAGE_READ) | + (iamr & 0x1 ? 0 : PAGE_EXEC); +} + static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, uint64_t pte, int *fault_cause, int *prot, int mmu_idx, bool partition_scoped) diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index c5c04a1527..6620b3d648 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -3,7 +3,7 @@ #ifndef CONFIG_USER_ONLY -#include "exec/page-protection.h" +#ifdef TARGET_PPC64 /* Radix Quadrants */ #define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF @@ -14,61 +14,10 @@ #define R_EADDR_QUADRANT2 0x8000000000000000 #define R_EADDR_QUADRANT3 0xC000000000000000 -/* Radix Partition Table Entry Fields */ -#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 -#define PATE1_R_PRTS 0x000000000000001F - -/* Radix Process Table Entry Fields */ -#define PRTBE_R_GET_RTS(rts) \ - ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31) -#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00 -#define PRTBE_R_RPDS 0x000000000000001F - -/* Radix Page Directory/Table Entry Fields */ -#define R_PTE_VALID 0x8000000000000000 -#define R_PTE_LEAF 0x4000000000000000 -#define R_PTE_SW0 0x2000000000000000 -#define R_PTE_RPN 0x01FFFFFFFFFFF000 -#define R_PTE_SW1 0x0000000000000E00 -#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7)) -#define R_PTE_R 0x0000000000000100 -#define R_PTE_C 0x0000000000000080 -#define R_PTE_ATT 0x0000000000000030 -#define R_PTE_ATT_NORMAL 0x0000000000000000 -#define R_PTE_ATT_SAO 0x0000000000000010 -#define R_PTE_ATT_NI_IO 0x0000000000000020 -#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030 -#define R_PTE_EAA_PRIV 0x0000000000000008 -#define R_PTE_EAA_R 0x0000000000000004 -#define R_PTE_EAA_RW 0x0000000000000002 -#define R_PTE_EAA_X 0x0000000000000001 -#define R_PDE_NLB PRTBE_R_RPDB -#define R_PDE_NLS PRTBE_R_RPDS - -#ifdef TARGET_PPC64 - bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddr, int *psizep, int *protp, int mmu_idx, bool guest_visible); -static inline int ppc_radix64_get_prot_eaa(uint64_t pte) -{ - return (pte & R_PTE_EAA_R ? PAGE_READ : 0) | - (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) | - (pte & R_PTE_EAA_X ? PAGE_EXEC : 0); -} - -static inline int ppc_radix64_get_prot_amr(const PowerPCCPU *cpu) -{ - const CPUPPCState *env = &cpu->env; - int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */ - int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */ - - return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */ - (amr & 0x1 ? 0 : PAGE_READ) | - (iamr & 0x1 ? 0 : PAGE_EXEC); -} - #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ From d741ecffd2ca260ce7875a4596f17736b5ccb7c3 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan <balaton@eik.bme.hu> Date: Mon, 27 May 2024 01:13:08 +0200 Subject: [PATCH 2576/2884] target/ppc: Remove includes from mmu-book3s-v3.h Drop includes from header that is not needed by the header itself and only include them from C files that really need it. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Acked-by: Nicholas Piggin <npiggin@gmail.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- target/ppc/mmu-book3s-v3.h | 3 --- target/ppc/mmu-hash64.c | 1 + target/ppc/mmu-radix64.c | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 263ce55c1f..be66e26604 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -20,9 +20,6 @@ #ifndef PPC_MMU_BOOK3S_V3_H #define PPC_MMU_BOOK3S_V3_H -#include "mmu-hash64.h" -#include "mmu-books.h" - #ifndef CONFIG_USER_ONLY /* diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 7bc0323f26..5e1983e334 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -31,6 +31,7 @@ #include "hw/hw.h" #include "internal.h" #include "mmu-book3s-v3.h" +#include "mmu-books.h" #include "helper_regs.h" #ifdef CONFIG_TCG diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index cf9619e847..be7a45f254 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -28,6 +28,7 @@ #include "internal.h" #include "mmu-radix64.h" #include "mmu-book3s-v3.h" +#include "mmu-books.h" /* Radix Partition Table Entry Fields */ #define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 From 83340193b991e7a974f117baa86a04db1fd835a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Wed, 24 Jul 2024 14:53:38 +1000 Subject: [PATCH 2577/2884] target/rx: Use target_ulong for address in LI Using int32_t meant that the address was sign-extended to uint64_t when passing to translator_ld*, triggering an assert. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2453 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Thomas Huth <thuth@redhat.com> --- target/rx/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 9b81cf20b3..9aade2b6e5 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -85,7 +85,8 @@ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, static uint32_t li(DisasContext *ctx, int sz) { - int32_t tmp, addr; + target_ulong addr; + uint32_t tmp; CPURXState *env = ctx->env; addr = ctx->base.pc_next; From 657ea58ba351093cb7f66e0bf7fc65962598de89 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella <sgarzare@redhat.com> Date: Tue, 4 Jun 2024 15:59:31 +0200 Subject: [PATCH 2578/2884] qapi/qom: make some QOM properties depend on the build settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some QOM properties are associated with ObjectTypes that already depend on CONFIG_* switches. So to avoid generating dead code, let's also make the definition of those properties dependent on the corresponding CONFIG_*. Suggested-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> Message-ID: <20240604135931.311709-1-sgarzare@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Make SecretKeyringProperties conditional, too] Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/crypto.json | 3 ++- qapi/qom.json | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/qapi/crypto.json b/qapi/crypto.json index f03bdab8c9..39b191e8a2 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -485,7 +485,8 @@ ## { 'struct': 'SecretKeyringProperties', 'base': 'SecretCommonProperties', - 'data': { 'serial': 'int32' } } + 'data': { 'serial': 'int32' }, + 'if': 'CONFIG_SECRET_KEYRING' } ## # @TlsCredsProperties: diff --git a/qapi/qom.json b/qapi/qom.json index 7e780e1791..fc035f126a 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -222,7 +222,8 @@ ## { 'struct': 'CanHostSocketcanProperties', 'data': { 'if': 'str', - 'canbus': 'str' } } + 'canbus': 'str' }, + 'if': 'CONFIG_LINUX' } ## # @ColoCompareProperties: @@ -305,7 +306,8 @@ ## { 'struct': 'CryptodevVhostUserProperties', 'base': 'CryptodevBackendProperties', - 'data': { 'chardev': 'str' } } + 'data': { 'chardev': 'str' }, + 'if': 'CONFIG_VHOST_CRYPTO' } ## # @DBusVMStateProperties: @@ -514,7 +516,8 @@ 'data': { 'evdev': 'str', '*grab_all': 'bool', '*repeat': 'bool', - '*grab-toggle': 'GrabToggleKeys' } } + '*grab-toggle': 'GrabToggleKeys' }, + 'if': 'CONFIG_LINUX' } ## # @EventLoopBaseProperties: @@ -718,7 +721,8 @@ 'base': 'MemoryBackendProperties', 'data': { '*hugetlb': 'bool', '*hugetlbsize': 'size', - '*seal': 'bool' } } + '*seal': 'bool' }, + 'if': 'CONFIG_LINUX' } ## # @MemoryBackendShmProperties: @@ -748,7 +752,8 @@ ## { 'struct': 'MemoryBackendEpcProperties', 'base': 'MemoryBackendProperties', - 'data': {} } + 'data': {}, + 'if': 'CONFIG_LINUX' } ## # @PrManagerHelperProperties: @@ -761,7 +766,8 @@ # Since: 2.11 ## { 'struct': 'PrManagerHelperProperties', - 'data': { 'path': 'str' } } + 'data': { 'path': 'str' }, + 'if': 'CONFIG_LINUX' } ## # @QtestProperties: @@ -884,7 +890,8 @@ ## { 'struct': 'RngRandomProperties', 'base': 'RngProperties', - 'data': { '*filename': 'str' } } + 'data': { '*filename': 'str' }, + 'if': 'CONFIG_POSIX' } ## # @SevCommonProperties: From bc2e34e343b40b83cca82409db9c126df2e28c81 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 18 Jul 2024 16:10:01 +0200 Subject: [PATCH 2579/2884] qapi/machine: Belatedly document target loongarch64 is since 7.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: a8a506c39070 (hw/loongarch: Add support loongson3 virt machine type.) Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240718141001.3077709-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Song Gao <gaosong@loongson.cn> --- qapi/machine.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qapi/machine.json b/qapi/machine.json index f9ea6b3e97..fcfd249e2d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -24,6 +24,8 @@ # # @avr: since 5.1 # +# @loongarch64: since 7.1 +# # .. note:: The resulting QMP strings can be appended to the # "qemu-system-" prefix to produce the corresponding QEMU executable # name. This is true even for "qemu-system-x86_64". From 546d574b11e02bfd5b15cdf1564842c14516dfab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20van=20H=C3=B6vell?= <frederik@fvhovell.nl> Date: Mon, 29 Jul 2024 13:34:18 +0100 Subject: [PATCH 2580/2884] hw/char/bcm2835_aux: Fix assert when receive FIFO fills up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a bare-metal application on the raspi3 board reads the AUX_MU_STAT_REG MMIO register while the device's buffer is at full receive FIFO capacity (i.e. `s->read_count == BCM2835_AUX_RX_FIFO_LEN`) the assertion `assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN)` fails. Reported-by: Cryptjar <cryptjar@junk.studio> Suggested-by: Cryptjar <cryptjar@junk.studio> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/459 Signed-off-by: Frederik van Hövell <frederik@fvhovell.nl> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [PMM: commit message tweaks] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/char/bcm2835_aux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 83990e20f7..fca2f27a55 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -138,7 +138,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */ if (s->read_count > 0) { res |= 0x1; /* data in input buffer */ - assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN); + assert(s->read_count <= BCM2835_AUX_RX_FIFO_LEN); res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */ } return res; From 8deba2f36e9f7e34bf1805b08e69699486481c8a Mon Sep 17 00:00:00 2001 From: Mostafa Saleh <smostafa@google.com> Date: Mon, 29 Jul 2024 13:34:18 +0100 Subject: [PATCH 2581/2884] hw/arm/smmuv3: Assert input to oas2bits() is valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity has spotted a possible problem with the OAS handling (CID 1558464), where the error return of oas2bits() -1 is not checked, which can cause an overflow in oas value. oas2bits() is only called with valid inputs, harden the function to assert that. Reported-By: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Message-id: 20240722103531.2377348-1-smostafa@google.com Link: https://lore.kernel.org/qemu-devel/CAFEAcA-H=n-3mHC+eL6YjfL1m+x+b+Fk3mkgZbN74WNxifFVow@mail.gmail.com/ Signed-off-by: Mostafa Saleh <smostafa@google.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/smmuv3-internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 0ebf2eebcf..b6b7399347 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -599,7 +599,8 @@ static inline int oas2bits(int oas_field) case 5: return 48; } - return -1; + + g_assert_not_reached(); } /* CD fields */ From 613d0b8ebbf1fa32f8037cb02b7c82f96011ef2d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Sat, 20 Jul 2024 18:30:49 +0900 Subject: [PATCH 2582/2884] target/arm/kvm: Set PMU for host only when available target/arm/kvm.c checked PMU availability but unconditionally set the PMU feature flag for the host CPU model, which is confusing. Set the feature flag only when available. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 70f79eda33..b20a35052f 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -280,6 +280,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) if (kvm_arm_pmu_supported()) { init.features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; pmu_supported = true; + features |= 1ULL << ARM_FEATURE_PMU; } if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { @@ -448,7 +449,6 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) features |= 1ULL << ARM_FEATURE_V8; features |= 1ULL << ARM_FEATURE_NEON; features |= 1ULL << ARM_FEATURE_AARCH64; - features |= 1ULL << ARM_FEATURE_PMU; features |= 1ULL << ARM_FEATURE_GENERIC_TIMER; ahcf->features = features; From 0d2aac4ab1777162c7a7ecd5a6d5d4de93842116 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Sat, 20 Jul 2024 18:30:50 +0900 Subject: [PATCH 2583/2884] target/arm/kvm: Do not silently remove PMU kvm_arch_init_vcpu() used to remove PMU when it is not available even if the CPU model needs one. It is semantically incorrect, and may continue execution on a misbehaving host that advertises a CPU model while lacking its PMU. Keep the PMU when the CPU model needs one, and let kvm_arm_vcpu_init() fail if the KVM implementation mismatches with our expectation. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/kvm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index b20a35052f..849e2e21b3 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1888,13 +1888,8 @@ int kvm_arch_init_vcpu(CPUState *cs) if (!arm_feature(env, ARM_FEATURE_AARCH64)) { cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT; } - if (!kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) { - cpu->has_pmu = false; - } if (cpu->has_pmu) { cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; - } else { - env->features &= ~(1ULL << ARM_FEATURE_PMU); } if (cpu_isar_feature(aa64_sve, cpu)) { assert(kvm_arm_sve_supported()); From e9e640148c676ac2c087a3dd93e3917c8fca25ea Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Sat, 20 Jul 2024 18:30:52 +0900 Subject: [PATCH 2584/2884] hvf: arm: Raise an exception for sysreg by default Any sysreg access results in an exception unless defined otherwise so we should raise an exception by default. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 174 +++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 89 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index eb090e67a2..1a749534fb 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1199,57 +1199,56 @@ static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) return false; } -static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) +static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) { ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; - uint64_t val = 0; switch (reg) { case SYSREG_CNTPCT_EL0: - val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / + *val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(arm_cpu); - break; + return 0; case SYSREG_PMCR_EL0: - val = env->cp15.c9_pmcr; - break; + *val = env->cp15.c9_pmcr; + return 0; case SYSREG_PMCCNTR_EL0: pmu_op_start(env); - val = env->cp15.c15_ccnt; + *val = env->cp15.c15_ccnt; pmu_op_finish(env); - break; + return 0; case SYSREG_PMCNTENCLR_EL0: - val = env->cp15.c9_pmcnten; - break; + *val = env->cp15.c9_pmcnten; + return 0; case SYSREG_PMOVSCLR_EL0: - val = env->cp15.c9_pmovsr; - break; + *val = env->cp15.c9_pmovsr; + return 0; case SYSREG_PMSELR_EL0: - val = env->cp15.c9_pmselr; - break; + *val = env->cp15.c9_pmselr; + return 0; case SYSREG_PMINTENCLR_EL1: - val = env->cp15.c9_pminten; - break; + *val = env->cp15.c9_pminten; + return 0; case SYSREG_PMCCFILTR_EL0: - val = env->cp15.pmccfiltr_el0; - break; + *val = env->cp15.pmccfiltr_el0; + return 0; case SYSREG_PMCNTENSET_EL0: - val = env->cp15.c9_pmcnten; - break; + *val = env->cp15.c9_pmcnten; + return 0; case SYSREG_PMUSERENR_EL0: - val = env->cp15.c9_pmuserenr; - break; + *val = env->cp15.c9_pmuserenr; + return 0; case SYSREG_PMCEID0_EL0: case SYSREG_PMCEID1_EL0: /* We can't really count anything yet, declare all events invalid */ - val = 0; - break; + *val = 0; + return 0; case SYSREG_OSLSR_EL1: - val = env->cp15.oslsr_el1; - break; + *val = env->cp15.oslsr_el1; + return 0; case SYSREG_OSDLR_EL1: /* Dummy register */ - break; + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: @@ -1276,9 +1275,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_ICC_SRE_EL1: case SYSREG_ICC_CTLR_EL1: /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ - if (!hvf_sysreg_read_cp(cpu, reg, &val)) { - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); - return 1; + if (hvf_sysreg_read_cp(cpu, reg, &val)) { + return 0; } break; case SYSREG_DBGBVR0_EL1: @@ -1297,8 +1295,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGBVR13_EL1: case SYSREG_DBGBVR14_EL1: case SYSREG_DBGBVR15_EL1: - val = env->cp15.dbgbvr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgbvr[SYSREG_CRM(reg)]; + return 0; case SYSREG_DBGBCR0_EL1: case SYSREG_DBGBCR1_EL1: case SYSREG_DBGBCR2_EL1: @@ -1315,8 +1313,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGBCR13_EL1: case SYSREG_DBGBCR14_EL1: case SYSREG_DBGBCR15_EL1: - val = env->cp15.dbgbcr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgbcr[SYSREG_CRM(reg)]; + return 0; case SYSREG_DBGWVR0_EL1: case SYSREG_DBGWVR1_EL1: case SYSREG_DBGWVR2_EL1: @@ -1333,8 +1331,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGWVR13_EL1: case SYSREG_DBGWVR14_EL1: case SYSREG_DBGWVR15_EL1: - val = env->cp15.dbgwvr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgwvr[SYSREG_CRM(reg)]; + return 0; case SYSREG_DBGWCR0_EL1: case SYSREG_DBGWCR1_EL1: case SYSREG_DBGWCR2_EL1: @@ -1351,35 +1349,25 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGWCR13_EL1: case SYSREG_DBGWCR14_EL1: case SYSREG_DBGWCR15_EL1: - val = env->cp15.dbgwcr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgwcr[SYSREG_CRM(reg)]; + return 0; default: if (is_id_sysreg(reg)) { /* ID system registers read as RES0 */ - val = 0; - break; + *val = 0; + return 0; } - cpu_synchronize_state(cpu); - trace_hvf_unhandled_sysreg_read(env->pc, reg, - SYSREG_OP0(reg), - SYSREG_OP1(reg), - SYSREG_CRN(reg), - SYSREG_CRM(reg), - SYSREG_OP2(reg)); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); - return 1; } - trace_hvf_sysreg_read(reg, - SYSREG_OP0(reg), - SYSREG_OP1(reg), - SYSREG_CRN(reg), - SYSREG_CRM(reg), - SYSREG_OP2(reg), - val); - hvf_set_reg(cpu, rt, val); - - return 0; + cpu_synchronize_state(cpu); + trace_hvf_unhandled_sysreg_read(env->pc, reg, + SYSREG_OP0(reg), + SYSREG_OP1(reg), + SYSREG_CRN(reg), + SYSREG_CRM(reg), + SYSREG_OP2(reg)); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + return 1; } static void pmu_update_irq(CPUARMState *env) @@ -1503,7 +1491,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) pmu_op_start(env); env->cp15.c15_ccnt = val; pmu_op_finish(env); - break; + return 0; case SYSREG_PMCR_EL0: pmu_op_start(env); @@ -1523,45 +1511,45 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) env->cp15.c9_pmcr |= (val & PMCR_WRITABLE_MASK); pmu_op_finish(env); - break; + return 0; case SYSREG_PMUSERENR_EL0: env->cp15.c9_pmuserenr = val & 0xf; - break; + return 0; case SYSREG_PMCNTENSET_EL0: env->cp15.c9_pmcnten |= (val & pmu_counter_mask(env)); - break; + return 0; case SYSREG_PMCNTENCLR_EL0: env->cp15.c9_pmcnten &= ~(val & pmu_counter_mask(env)); - break; + return 0; case SYSREG_PMINTENCLR_EL1: pmu_op_start(env); env->cp15.c9_pminten |= val; pmu_op_finish(env); - break; + return 0; case SYSREG_PMOVSCLR_EL0: pmu_op_start(env); env->cp15.c9_pmovsr &= ~val; pmu_op_finish(env); - break; + return 0; case SYSREG_PMSWINC_EL0: pmu_op_start(env); pmswinc_write(env, val); pmu_op_finish(env); - break; + return 0; case SYSREG_PMSELR_EL0: env->cp15.c9_pmselr = val & 0x1f; - break; + return 0; case SYSREG_PMCCFILTR_EL0: pmu_op_start(env); env->cp15.pmccfiltr_el0 = val & PMCCFILTR_EL0; pmu_op_finish(env); - break; + return 0; case SYSREG_OSLAR_EL1: env->cp15.oslsr_el1 = val & 1; - break; + return 0; case SYSREG_OSDLR_EL1: /* Dummy register */ - break; + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: @@ -1591,10 +1579,10 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) if (!hvf_sysreg_write_cp(cpu, reg, val)) { hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); } - break; + return 0; case SYSREG_MDSCR_EL1: env->cp15.mdscr_el1 = val; - break; + return 0; case SYSREG_DBGBVR0_EL1: case SYSREG_DBGBVR1_EL1: case SYSREG_DBGBVR2_EL1: @@ -1612,7 +1600,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGBVR14_EL1: case SYSREG_DBGBVR15_EL1: env->cp15.dbgbvr[SYSREG_CRM(reg)] = val; - break; + return 0; case SYSREG_DBGBCR0_EL1: case SYSREG_DBGBCR1_EL1: case SYSREG_DBGBCR2_EL1: @@ -1630,7 +1618,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGBCR14_EL1: case SYSREG_DBGBCR15_EL1: env->cp15.dbgbcr[SYSREG_CRM(reg)] = val; - break; + return 0; case SYSREG_DBGWVR0_EL1: case SYSREG_DBGWVR1_EL1: case SYSREG_DBGWVR2_EL1: @@ -1648,7 +1636,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGWVR14_EL1: case SYSREG_DBGWVR15_EL1: env->cp15.dbgwvr[SYSREG_CRM(reg)] = val; - break; + return 0; case SYSREG_DBGWCR0_EL1: case SYSREG_DBGWCR1_EL1: case SYSREG_DBGWCR2_EL1: @@ -1666,20 +1654,18 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGWCR14_EL1: case SYSREG_DBGWCR15_EL1: env->cp15.dbgwcr[SYSREG_CRM(reg)] = val; - break; - default: - cpu_synchronize_state(cpu); - trace_hvf_unhandled_sysreg_write(env->pc, reg, - SYSREG_OP0(reg), - SYSREG_OP1(reg), - SYSREG_CRN(reg), - SYSREG_CRM(reg), - SYSREG_OP2(reg)); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); - return 1; + return 0; } - return 0; + cpu_synchronize_state(cpu); + trace_hvf_unhandled_sysreg_write(env->pc, reg, + SYSREG_OP0(reg), + SYSREG_OP1(reg), + SYSREG_CRN(reg), + SYSREG_CRM(reg), + SYSREG_OP2(reg)); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + return 1; } static int hvf_inject_interrupts(CPUState *cpu) @@ -1944,7 +1930,17 @@ int hvf_vcpu_exec(CPUState *cpu) int sysreg_ret = 0; if (isread) { - sysreg_ret = hvf_sysreg_read(cpu, reg, rt); + sysreg_ret = hvf_sysreg_read(cpu, reg, &val); + if (!sysreg_ret) { + trace_hvf_sysreg_read(reg, + SYSREG_OP0(reg), + SYSREG_OP1(reg), + SYSREG_CRN(reg), + SYSREG_CRM(reg), + SYSREG_OP2(reg), + val); + hvf_set_reg(cpu, rt, val); + } } else { val = hvf_get_reg(cpu, rt); sysreg_ret = hvf_sysreg_write(cpu, reg, val); From e6fd3192edb8178613e61f7f0242a774ad8ffa2c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Sat, 20 Jul 2024 18:30:53 +0900 Subject: [PATCH 2585/2884] hvf: arm: Properly disable PMU Setting pmu property used to have no effect for hvf so fix it. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 186 +++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 88 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 1a749534fb..adcdfae0b1 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1204,45 +1204,50 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; + if (arm_feature(env, ARM_FEATURE_PMU)) { + switch (reg) { + case SYSREG_PMCR_EL0: + *val = env->cp15.c9_pmcr; + return 0; + case SYSREG_PMCCNTR_EL0: + pmu_op_start(env); + *val = env->cp15.c15_ccnt; + pmu_op_finish(env); + return 0; + case SYSREG_PMCNTENCLR_EL0: + *val = env->cp15.c9_pmcnten; + return 0; + case SYSREG_PMOVSCLR_EL0: + *val = env->cp15.c9_pmovsr; + return 0; + case SYSREG_PMSELR_EL0: + *val = env->cp15.c9_pmselr; + return 0; + case SYSREG_PMINTENCLR_EL1: + *val = env->cp15.c9_pminten; + return 0; + case SYSREG_PMCCFILTR_EL0: + *val = env->cp15.pmccfiltr_el0; + return 0; + case SYSREG_PMCNTENSET_EL0: + *val = env->cp15.c9_pmcnten; + return 0; + case SYSREG_PMUSERENR_EL0: + *val = env->cp15.c9_pmuserenr; + return 0; + case SYSREG_PMCEID0_EL0: + case SYSREG_PMCEID1_EL0: + /* We can't really count anything yet, declare all events invalid */ + *val = 0; + return 0; + } + } + switch (reg) { case SYSREG_CNTPCT_EL0: *val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(arm_cpu); return 0; - case SYSREG_PMCR_EL0: - *val = env->cp15.c9_pmcr; - return 0; - case SYSREG_PMCCNTR_EL0: - pmu_op_start(env); - *val = env->cp15.c15_ccnt; - pmu_op_finish(env); - return 0; - case SYSREG_PMCNTENCLR_EL0: - *val = env->cp15.c9_pmcnten; - return 0; - case SYSREG_PMOVSCLR_EL0: - *val = env->cp15.c9_pmovsr; - return 0; - case SYSREG_PMSELR_EL0: - *val = env->cp15.c9_pmselr; - return 0; - case SYSREG_PMINTENCLR_EL1: - *val = env->cp15.c9_pminten; - return 0; - case SYSREG_PMCCFILTR_EL0: - *val = env->cp15.pmccfiltr_el0; - return 0; - case SYSREG_PMCNTENSET_EL0: - *val = env->cp15.c9_pmcnten; - return 0; - case SYSREG_PMUSERENR_EL0: - *val = env->cp15.c9_pmuserenr; - return 0; - case SYSREG_PMCEID0_EL0: - case SYSREG_PMCEID1_EL0: - /* We can't really count anything yet, declare all events invalid */ - *val = 0; - return 0; case SYSREG_OSLSR_EL1: *val = env->cp15.oslsr_el1; return 0; @@ -1486,64 +1491,69 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) SYSREG_OP2(reg), val); - switch (reg) { - case SYSREG_PMCCNTR_EL0: - pmu_op_start(env); - env->cp15.c15_ccnt = val; - pmu_op_finish(env); - return 0; - case SYSREG_PMCR_EL0: - pmu_op_start(env); + if (arm_feature(env, ARM_FEATURE_PMU)) { + switch (reg) { + case SYSREG_PMCCNTR_EL0: + pmu_op_start(env); + env->cp15.c15_ccnt = val; + pmu_op_finish(env); + return 0; + case SYSREG_PMCR_EL0: + pmu_op_start(env); - if (val & PMCRC) { - /* The counter has been reset */ - env->cp15.c15_ccnt = 0; - } - - if (val & PMCRP) { - unsigned int i; - for (i = 0; i < pmu_num_counters(env); i++) { - env->cp15.c14_pmevcntr[i] = 0; + if (val & PMCRC) { + /* The counter has been reset */ + env->cp15.c15_ccnt = 0; } + + if (val & PMCRP) { + unsigned int i; + for (i = 0; i < pmu_num_counters(env); i++) { + env->cp15.c14_pmevcntr[i] = 0; + } + } + + env->cp15.c9_pmcr &= ~PMCR_WRITABLE_MASK; + env->cp15.c9_pmcr |= (val & PMCR_WRITABLE_MASK); + + pmu_op_finish(env); + return 0; + case SYSREG_PMUSERENR_EL0: + env->cp15.c9_pmuserenr = val & 0xf; + return 0; + case SYSREG_PMCNTENSET_EL0: + env->cp15.c9_pmcnten |= (val & pmu_counter_mask(env)); + return 0; + case SYSREG_PMCNTENCLR_EL0: + env->cp15.c9_pmcnten &= ~(val & pmu_counter_mask(env)); + return 0; + case SYSREG_PMINTENCLR_EL1: + pmu_op_start(env); + env->cp15.c9_pminten |= val; + pmu_op_finish(env); + return 0; + case SYSREG_PMOVSCLR_EL0: + pmu_op_start(env); + env->cp15.c9_pmovsr &= ~val; + pmu_op_finish(env); + return 0; + case SYSREG_PMSWINC_EL0: + pmu_op_start(env); + pmswinc_write(env, val); + pmu_op_finish(env); + return 0; + case SYSREG_PMSELR_EL0: + env->cp15.c9_pmselr = val & 0x1f; + return 0; + case SYSREG_PMCCFILTR_EL0: + pmu_op_start(env); + env->cp15.pmccfiltr_el0 = val & PMCCFILTR_EL0; + pmu_op_finish(env); + return 0; } + } - env->cp15.c9_pmcr &= ~PMCR_WRITABLE_MASK; - env->cp15.c9_pmcr |= (val & PMCR_WRITABLE_MASK); - - pmu_op_finish(env); - return 0; - case SYSREG_PMUSERENR_EL0: - env->cp15.c9_pmuserenr = val & 0xf; - return 0; - case SYSREG_PMCNTENSET_EL0: - env->cp15.c9_pmcnten |= (val & pmu_counter_mask(env)); - return 0; - case SYSREG_PMCNTENCLR_EL0: - env->cp15.c9_pmcnten &= ~(val & pmu_counter_mask(env)); - return 0; - case SYSREG_PMINTENCLR_EL1: - pmu_op_start(env); - env->cp15.c9_pminten |= val; - pmu_op_finish(env); - return 0; - case SYSREG_PMOVSCLR_EL0: - pmu_op_start(env); - env->cp15.c9_pmovsr &= ~val; - pmu_op_finish(env); - return 0; - case SYSREG_PMSWINC_EL0: - pmu_op_start(env); - pmswinc_write(env, val); - pmu_op_finish(env); - return 0; - case SYSREG_PMSELR_EL0: - env->cp15.c9_pmselr = val & 0x1f; - return 0; - case SYSREG_PMCCFILTR_EL0: - pmu_op_start(env); - env->cp15.pmccfiltr_el0 = val & PMCCFILTR_EL0; - pmu_op_finish(env); - return 0; + switch (reg) { case SYSREG_OSLAR_EL1: env->cp15.oslsr_el1 = val & 1; return 0; From 05b8d7249109c44a30352ac86d1f079ec0a8506e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Sat, 20 Jul 2024 18:30:54 +0900 Subject: [PATCH 2586/2884] hvf: arm: Do not advance PC when raising an exception This is identical with commit 30a1690f2402 ("hvf: arm: Do not advance PC when raising an exception") but for writes instead of reads. Fixes: a2260983c655 ("hvf: arm: Add support for GICv3") Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index adcdfae0b1..c1496ad5be 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1586,10 +1586,10 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ - if (!hvf_sysreg_write_cp(cpu, reg, val)) { - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + if (hvf_sysreg_write_cp(cpu, reg, val)) { + return 0; } - return 0; + break; case SYSREG_MDSCR_EL1: env->cp15.mdscr_el1 = val; return 0; From 0892fffc2abaadfb5d8b79bb0250ae1794862560 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 14:10:26 +0100 Subject: [PATCH 2587/2884] hw/misc/bcm2835_property: Fix handling of FRAMEBUFFER_SET_PALETTE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation of the "Set palette" mailbox property at https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface#set-palette says it has the form: Length: 24..1032 Value: u32: offset: first palette index to set (0-255) u32: length: number of palette entries to set (1-256) u32...: RGBA palette values (offset to offset+length-1) We get this wrong in a couple of ways: * we aren't checking the offset and length are in range, so the guest can make us spin for a long time by providing a large length * the bounds check on our loop is wrong: we should iterate through 'length' palette entries, not 'length - offset' entries Fix the loop to implement the bounds checks and get the loop condition right. In the process, make the variables local to this switch case, rather than function-global, so it's clearer what type they are when reading the code. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723131029.1159908-2-peter.maydell@linaro.org --- hw/misc/bcm2835_property.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 63de3db621..e28fdca984 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -31,7 +31,6 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) size_t resplen; uint32_t tmp; int n; - uint32_t offset, length, color; uint32_t start_num, number, otp_row; /* @@ -274,19 +273,25 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 16; break; case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: - offset = ldl_le_phys(&s->dma_as, value + 12); - length = ldl_le_phys(&s->dma_as, value + 16); - n = 0; - while (n < length - offset) { - color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2)); - stl_le_phys(&s->dma_as, - s->fbdev->vcram_base + ((offset + n) << 2), color); - n++; + { + uint32_t offset = ldl_le_phys(&s->dma_as, value + 12); + uint32_t length = ldl_le_phys(&s->dma_as, value + 16); + int resp; + + if (offset > 255 || length < 1 || length > 256) { + resp = 1; /* invalid request */ + } else { + for (uint32_t e = 0; e < length; e++) { + uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2)); + stl_le_phys(&s->dma_as, + s->fbdev->vcram_base + ((offset + e) << 2), color); + } + resp = 0; } - stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 12, resp); resplen = 4; break; - + } case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: stl_le_phys(&s->dma_as, value + 12, 1); resplen = 4; From 32f1c201eedf992c2ac8033d8af95f1ff1758a4d Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 14:10:27 +0100 Subject: [PATCH 2588/2884] hw/misc/bcm2835_property: Avoid overflow in OTP access properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity points out that in our handling of the property RPI_FWREQ_SET_CUSTOMER_OTP we have a potential overflow. This happens because we read start_num and number from the guest as unsigned 32 bit integers, but then the variable 'n' we use as a loop counter as we iterate from start_num to start_num + number is only an "int". That means that if the guest passes us a very large start_num we will interpret it as negative. This will result in an assertion failure inside bcm2835_otp_set_row(), which checks that we didn't pass it an invalid row number. A similar issue applies to all the properties for accessing OTP rows where we are iterating through with a start and length read from the guest. Use uint32_t for the loop counter to avoid this problem. Because in all cases 'n' is only used as a loop counter, we can do this as part of the for(), restricting its scope to exactly where we need it. Resolves: Coverity CID 1549401 Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723131029.1159908-3-peter.maydell@linaro.org --- hw/misc/bcm2835_property.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index e28fdca984..7eb623b4e9 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -30,7 +30,6 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tot_len; size_t resplen; uint32_t tmp; - int n; uint32_t start_num, number, otp_row; /* @@ -337,7 +336,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8 + 4 * number; - for (n = start_num; n < start_num + number && + for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { otp_row = bcm2835_otp_get_row(s->otp, BCM2835_OTP_CUSTOMER_OTP + n); @@ -366,7 +365,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; } - for (n = start_num; n < start_num + number && + for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { otp_row = ldl_le_phys(&s->dma_as, value + 20 + ((n - start_num) << 2)); @@ -383,7 +382,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8 + 4 * number; - for (n = start_num; n < start_num + number && + for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { otp_row = bcm2835_otp_get_row(s->otp, BCM2835_OTP_PRIVATE_KEY + n); @@ -403,7 +402,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; } - for (n = start_num; n < start_num + number && + for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { otp_row = ldl_le_phys(&s->dma_as, value + 20 + ((n - start_num) << 2)); From 4b648238b8dd9870280b60ff32bf7fe2fe82607f Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 14:10:28 +0100 Subject: [PATCH 2589/2884] hw/misc/bcm2835_property: Restrict scope of start_num, number, otp_row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the long function bcm2835_property_mbox_push(), the variables start_num, number and otp_row are used only in the four cases which access OTP data, and their uses don't overlap with each other. Make these variables have scope restricted to the cases where they're used, so it's easier to read each individual case without having to cross-refer up to the variable declaration at the top of the function and check whether the variable is also used later in the loop. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723131029.1159908-4-peter.maydell@linaro.org --- hw/misc/bcm2835_property.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 7eb623b4e9..443d42a182 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -30,7 +30,6 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tot_len; size_t resplen; uint32_t tmp; - uint32_t start_num, number, otp_row; /* * Copy the current state of the framebuffer config; we will update @@ -331,22 +330,25 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Customer OTP */ case RPI_FWREQ_GET_CUSTOMER_OTP: - start_num = ldl_le_phys(&s->dma_as, value + 12); - number = ldl_le_phys(&s->dma_as, value + 16); + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); resplen = 8 + 4 * number; for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { - otp_row = bcm2835_otp_get_row(s->otp, + uint32_t otp_row = bcm2835_otp_get_row(s->otp, BCM2835_OTP_CUSTOMER_OTP + n); stl_le_phys(&s->dma_as, value + 20 + ((n - start_num) << 2), otp_row); } break; + } case RPI_FWREQ_SET_CUSTOMER_OTP: - start_num = ldl_le_phys(&s->dma_as, value + 12); - number = ldl_le_phys(&s->dma_as, value + 16); + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); resplen = 4; @@ -367,32 +369,35 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { - otp_row = ldl_le_phys(&s->dma_as, + uint32_t otp_row = ldl_le_phys(&s->dma_as, value + 20 + ((n - start_num) << 2)); bcm2835_otp_set_row(s->otp, BCM2835_OTP_CUSTOMER_OTP + n, otp_row); } break; + } /* Device-specific private key */ - case RPI_FWREQ_GET_PRIVATE_KEY: - start_num = ldl_le_phys(&s->dma_as, value + 12); - number = ldl_le_phys(&s->dma_as, value + 16); + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); resplen = 8 + 4 * number; for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { - otp_row = bcm2835_otp_get_row(s->otp, + uint32_t otp_row = bcm2835_otp_get_row(s->otp, BCM2835_OTP_PRIVATE_KEY + n); stl_le_phys(&s->dma_as, value + 20 + ((n - start_num) << 2), otp_row); } break; + } case RPI_FWREQ_SET_PRIVATE_KEY: - start_num = ldl_le_phys(&s->dma_as, value + 12); - number = ldl_le_phys(&s->dma_as, value + 16); + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); resplen = 4; @@ -404,12 +409,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) for (uint32_t n = start_num; n < start_num + number && n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { - otp_row = ldl_le_phys(&s->dma_as, + uint32_t otp_row = ldl_le_phys(&s->dma_as, value + 20 + ((n - start_num) << 2)); bcm2835_otp_set_row(s->otp, BCM2835_OTP_PRIVATE_KEY + n, otp_row); } break; + } default: qemu_log_mask(LOG_UNIMP, "bcm2835_property: unhandled tag 0x%08x\n", tag); From 28fe81f052f1fcc651c4c4c098de68af73bbe4f6 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 14:10:29 +0100 Subject: [PATCH 2590/2884] hw/misc/bcm2835_property: Reduce scope of variables in mbox push function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In bcm2835_property_mbox_push(), some variables are defined at function scope but used only in a smaller scope of the function: * tag, bufsize, resplen are used only in the body of the while() loop * tmp is used only for RPI_FWREQ_SET_POWER_STATE (and is badly named) Declare these variables in the scope where they're needed, so the code is easier to read. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723131029.1159908-5-peter.maydell@linaro.org --- hw/misc/bcm2835_property.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 443d42a182..8ca3128f29 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -25,11 +25,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) { - uint32_t tag; - uint32_t bufsize; uint32_t tot_len; - size_t resplen; - uint32_t tmp; /* * Copy the current state of the framebuffer config; we will update @@ -48,10 +44,10 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* @(addr + 4) : Buffer response code */ value = s->addr + 8; while (value + 8 <= s->addr + tot_len) { - tag = ldl_le_phys(&s->dma_as, value); - bufsize = ldl_le_phys(&s->dma_as, value + 4); + uint32_t tag = ldl_le_phys(&s->dma_as, value); + uint32_t bufsize = ldl_le_phys(&s->dma_as, value + 4); /* @(value + 8) : Request/response indicator */ - resplen = 0; + size_t resplen = 0; switch (tag) { case RPI_FWREQ_PROPERTY_END: break; @@ -95,13 +91,16 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; case RPI_FWREQ_SET_POWER_STATE: - /* Assume that whatever device they asked for exists, - * and we'll just claim we set it to the desired state + { + /* + * Assume that whatever device they asked for exists, + * and we'll just claim we set it to the desired state. */ - tmp = ldl_le_phys(&s->dma_as, value + 16); - stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); + uint32_t state = ldl_le_phys(&s->dma_as, value + 16); + stl_le_phys(&s->dma_as, value + 16, (state & 1)); resplen = 8; break; + } /* Clocks */ From 56f1c0db928aae0b83fd91c89ddb226b137e2b21 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 22 Jul 2024 18:29:54 +0100 Subject: [PATCH 2591/2884] target/arm: Don't assert for 128-bit tile accesses when SVL is 128 For an instruction which accesses a 128-bit element tile when the SVL is also 128 (for example MOV z0.Q, p0/M, ZA0H.Q[w0,0]), we will assert in get_tile_rowcol(): qemu-system-aarch64: ../../tcg/tcg-op.c:926: tcg_gen_deposit_z_i32: Assertion `len > 0' failed. This happens because we calculate len = ctz32(streaming_vec_reg_size(s)) - esz;$ but if the SVL and the element size are the same len is 0, and the deposit operation asserts. In this case the ZA storage contains exactly one 128 bit element ZA tile, and the horizontal or vertical slice is just that tile. This means that regardless of the index value in the Ws register, we always access that tile. (In pseudocode terms, we calculate (index + offset) MOD 1, which is 0.) Special case the len == 0 case to avoid hitting the assertion in tcg_gen_deposit_z_i32(). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240722172957.1041231-2-peter.maydell@linaro.org --- target/arm/tcg/translate-sme.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 185a8a917b..a50a419af2 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -49,7 +49,15 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, /* Prepare a power-of-two modulo via extraction of @len bits. */ len = ctz32(streaming_vec_reg_size(s)) - esz; - if (vertical) { + if (!len) { + /* + * SVL is 128 and the element size is 128. There is exactly + * one 128x128 tile in the ZA storage, and so we calculate + * (Rs + imm) MOD 1, which is always 0. We need to special case + * this because TCG doesn't allow deposit ops with len 0. + */ + tcg_gen_movi_i32(tmp, 0); + } else if (vertical) { /* * Compute the byte offset of the index within the tile: * (index % (svl / size)) * size From ea3f5a90f036734522e9af3bffd77e69e9f47355 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 22 Jul 2024 18:29:55 +0100 Subject: [PATCH 2592/2884] target/arm: Fix UMOPA/UMOPS of 16-bit values The UMOPA/UMOPS instructions are supposed to multiply unsigned 8 or 16 bit elements and accumulate the products into a 64-bit element. In the Arm ARM pseudocode, this is done with the usual infinite-precision signed arithmetic. However our implementation doesn't quite get it right, because in the DEF_IMOP_64() macro we do: sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); where NTYPE and MTYPE are uint16_t or int16_t. In the uint16_t case, the C usual arithmetic conversions mean the values are converted to "int" type and the multiply is done as a 32-bit multiply. This means that if the inputs are, for example, 0xffff and 0xffff then the result is 0xFFFE0001 as an int, which is then promoted to uint64_t for the accumulation into sum; this promotion incorrectly sign extends the multiply. Avoid the incorrect sign extension by casting to int64_t before the multiply, so we do the multiply as 64-bit signed arithmetic, which is a type large enough that the multiply can never overflow into the sign bit. (The equivalent 8-bit operations in DEF_IMOP_32() are fine, because the 8-bit multiplies can never overflow into the sign bit of a 32-bit integer.) Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2372 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240722172957.1041231-3-peter.maydell@linaro.org --- target/arm/tcg/sme_helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 50bb088d04..3ba826a6ce 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1162,10 +1162,10 @@ static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ uint64_t sum = 0; \ /* Apply P to N as a mask, making the inactive elements 0. */ \ n &= expand_pred_h(p); \ - sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ - sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ - sum += (NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ - sum += (NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ + sum += (int64_t)(NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ + sum += (int64_t)(NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ + sum += (int64_t)(NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ + sum += (int64_t)(NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ return neg ? a - sum : a + sum; \ } From 76916dfa89e8900639c1055c07a295c06628a0bc Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 22 Jul 2024 18:29:56 +0100 Subject: [PATCH 2593/2884] target/arm: Avoid shifts by -1 in tszimm_shr() and tszimm_shl() The function tszimm_esz() returns a shift amount, or possibly -1 in certain cases that correspond to unallocated encodings in the instruction set. We catch these later in the trans_ functions (generally with an "a-esz < 0" check), but before we do the decodetree-generated code will also call tszimm_shr() or tszimm_sl(), which will use the tszimm_esz() return value as a shift count without checking that it is not negative, which is undefined behaviour. Avoid the UB by checking the return value in tszimm_shr() and tszimm_shl(). Cc: qemu-stable@nongnu.org Resolves: Coverity CID 1547617, 1547694 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240722172957.1041231-4-peter.maydell@linaro.org --- target/arm/tcg/translate-sve.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 798ab2bfb1..a72c262096 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -50,13 +50,27 @@ static int tszimm_esz(DisasContext *s, int x) static int tszimm_shr(DisasContext *s, int x) { - return (16 << tszimm_esz(s, x)) - x; + /* + * We won't use the tszimm_shr() value if tszimm_esz() returns -1 (the + * trans function will check for esz < 0), so we can return any + * value we like from here in that case as long as we avoid UB. + */ + int esz = tszimm_esz(s, x); + if (esz < 0) { + return esz; + } + return (16 << esz) - x; } /* See e.g. LSL (immediate, predicated). */ static int tszimm_shl(DisasContext *s, int x) { - return x - (8 << tszimm_esz(s, x)); + /* As with tszimm_shr(), value will be unused if esz < 0 */ + int esz = tszimm_esz(s, x); + if (esz < 0) { + return esz; + } + return x - (8 << esz); } /* The SH bit is in bit 8. Extract the low 8 and shift. */ From f573ac059ed060234fcef4299fae9e500d357c33 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 22 Jul 2024 18:29:57 +0100 Subject: [PATCH 2594/2884] target/arm: Ignore SMCR_EL2.LEN and SVCR_EL2.LEN if EL2 is not enabled When determining the current vector length, the SMCR_EL2.LEN and SVCR_EL2.LEN settings should only be considered if EL2 is enabled (compare the pseudocode CurrentSVL and CurrentNSVL which call EL2Enabled()). We were checking against ARM_FEATURE_EL2 rather than calling arm_is_el2_enabled(), which meant that we would look at SMCR_EL2/SVCR_EL2 when in Secure EL1 or Secure EL0 even if Secure EL2 was not enabled. Use the correct check in sve_vqm1_for_el_sm(). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240722172957.1041231-5-peter.maydell@linaro.org --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index ce31957235..8fb4b474e8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7232,7 +7232,7 @@ uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm) if (el <= 1 && !el_is_in_host(env, el)) { len = MIN(len, 0xf & (uint32_t)cr[1]); } - if (el <= 2 && arm_feature(env, ARM_FEATURE_EL2)) { + if (el <= 2 && arm_is_el2_enabled(env)) { len = MIN(len, 0xf & (uint32_t)cr[2]); } if (arm_feature(env, ARM_FEATURE_EL3)) { From b69c95e718ba83fac6d5d725c4a79c4da808ab67 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 16:10:42 +0100 Subject: [PATCH 2595/2884] target/tricore: Use unsigned types for bitops in helper_eq_b() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity points out that in helper_eq_b() we have an int32_t 'msk' and we end up shifting into its sign bit. This is OK for QEMU because we use -fwrapv to give this well defined semantics, but when you look at what this function is doing it's doing bit operations, so we should be using an unsigned variable anyway. This also matches the return type of the function. Make 'ret' and 'msk' uint32_t. Resolves: Coverity CID 1547758 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240723151042.1396610-1-peter.maydell@linaro.org --- target/tricore/op_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index ba9c4444b3..a0d5a0da1d 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -1505,8 +1505,8 @@ uint32_t helper_sub_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) uint32_t helper_eq_b(target_ulong r1, target_ulong r2) { - int32_t ret; - int32_t i, msk; + uint32_t ret, msk; + int32_t i; ret = 0; msk = 0xff; From b42ba4ea4322357fcbfcb0e6e613d79ede84de64 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 16:14:54 +0100 Subject: [PATCH 2596/2884] target/xtensa: Make use of 'segment' in pptlb helper less confusing Coverity gets confused about the use of the 'segment' variable in the pptlb helper function: it thinks that we can take a code path where we first initialize it: unsigned segment = XTENSA_MPU_PROBE_B; // 0x40000000 and then use that value as a shift count: } else if (nhits == 1 && (env->sregs[MPUENB] & (1u << segment))) { In fact this isn't possible, beacuse xtensa_mpu_lookup() is passed '&segment', and it uses that as an output value, which it will always set if it returns nonzero. But the way the code is currently written is confusing to a human reader as well as to Coverity. Instead of initializing 'segment' at the top of the function with a value that's only used in the "nhits == 0" code path, use the constant value directly in that code path, and don't initialize segment. This matches the way we use xtensa_mpu_lookup() in its other callsites in get_physical_addr_mpu(). Resolves: Coverity CID 1547589 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Acked-by: Max Filippov <jcmvbkbc@gmail.com> Message-id: 20240723151454.1396826-1-peter.maydell@linaro.org --- target/xtensa/mmu_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 997b21d389..29b84d5dbf 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -991,7 +991,7 @@ uint32_t HELPER(rptlb1)(CPUXtensaState *env, uint32_t s) uint32_t HELPER(pptlb)(CPUXtensaState *env, uint32_t v) { unsigned nhits; - unsigned segment = XTENSA_MPU_PROBE_B; + unsigned segment; unsigned bg_segment; nhits = xtensa_mpu_lookup(env->mpu_fg, env->config->n_mpu_fg_segments, @@ -1005,7 +1005,7 @@ uint32_t HELPER(pptlb)(CPUXtensaState *env, uint32_t v) xtensa_mpu_lookup(env->config->mpu_bg, env->config->n_mpu_bg_segments, v, &bg_segment); - return env->config->mpu_bg[bg_segment].attr | segment; + return env->config->mpu_bg[bg_segment].attr | XTENSA_MPU_PROBE_B; } } From 7d01623a1b0d282430a1e99fbe62068e27a4b4c4 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 16:42:07 +0100 Subject: [PATCH 2597/2884] target/m68k: avoid shift into sign bit in dump_address_map() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity complains (CID 1547592) that in dump_address_map() we take a value stored in a signed integer variable 'i' and shift it by enough to shift into the sign bit when we construct the value 'logical'. This isn't a bug for QEMU because we use -fwrapv semantics, but we can make Coverity happy by using an unsigned type for the loop variables i, j, k in this function. While we're changing the declaration of the variables, put them in the for() loops so their scope is the minimum required (a style now permitted by our coding style guide). Resolves: Coverity CID 1547592 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723154207.1483665-1-peter.maydell@linaro.org --- target/m68k/helper.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 7967ad13cb..4c85badd5d 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -479,7 +479,6 @@ static void print_address_zone(uint32_t logical, uint32_t physical, static void dump_address_map(CPUM68KState *env, uint32_t root_pointer) { - int i, j, k; int tic_size, tic_shift; uint32_t tib_mask; uint32_t tia, tib, tic; @@ -502,19 +501,19 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer) tic_shift = 12; tib_mask = M68K_4K_PAGE_MASK; } - for (i = 0; i < M68K_ROOT_POINTER_ENTRIES; i++) { + for (unsigned i = 0; i < M68K_ROOT_POINTER_ENTRIES; i++) { tia = address_space_ldl(cs->as, M68K_POINTER_BASE(root_pointer) + i * 4, MEMTXATTRS_UNSPECIFIED, &txres); if (txres != MEMTX_OK || !M68K_UDT_VALID(tia)) { continue; } - for (j = 0; j < M68K_ROOT_POINTER_ENTRIES; j++) { + for (unsigned j = 0; j < M68K_ROOT_POINTER_ENTRIES; j++) { tib = address_space_ldl(cs->as, M68K_POINTER_BASE(tia) + j * 4, MEMTXATTRS_UNSPECIFIED, &txres); if (txres != MEMTX_OK || !M68K_UDT_VALID(tib)) { continue; } - for (k = 0; k < tic_size; k++) { + for (unsigned k = 0; k < tic_size; k++) { tic = address_space_ldl(cs->as, (tib & tib_mask) + k * 4, MEMTXATTRS_UNSPECIFIED, &txres); if (txres != MEMTX_OK || !M68K_PDT_VALID(tic)) { From bde8adb808eeffb02fdcbd3425f09f1645bdcf4a Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 17:25:25 +0100 Subject: [PATCH 2598/2884] target/i386: Remove dead assignment to ss in do_interrupt64() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity points out that in do_interrupt64() in the "to inner privilege" codepath we set "ss = 0", but because we also set "new_stack = 1" there, later in the function we will always override that value of ss with "ss = 0 | dpl". Remove the unnecessary initialization of ss, which allows us to reduce the scope of the variable to only where it is used. Borrow a comment from helper_lcall_protected() that explains what "0 | dpl" means here. Resolves: Coverity CID 1527395 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240723162525.1585743-1-peter.maydell@linaro.org --- target/i386/tcg/seg_helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index aac092a356..bab552cd53 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -926,7 +926,7 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, target_ulong ptr; int type, dpl, selector, cpl, ist; int has_error_code, new_stack; - uint32_t e1, e2, e3, ss, eflags; + uint32_t e1, e2, e3, eflags; target_ulong old_eip, offset; bool set_rf; StackAccess sa; @@ -1007,7 +1007,6 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, /* to inner privilege */ new_stack = 1; sa.sp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); - ss = 0; } else { /* to same privilege */ if (env->eflags & VM_MASK) { @@ -1040,7 +1039,7 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); if (new_stack) { - ss = 0 | dpl; + uint32_t ss = 0 | dpl; /* SS = NULL selector with RPL = new CPL */ cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT); } env->regs[R_ESP] = sa.sp; From 525650cd71104c046e4260b5acaeb275f520d5c0 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 18:24:31 +0100 Subject: [PATCH 2599/2884] target/sh4: Avoid shift into sign bit in update_itlb_use() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In update_itlb_use() the variables or_mask and and_mask are uint8_t, which means that in expressions like "and_mask << 24" the usual C arithmetic conversions will result in the shift being done as a signed int type, and so we will shift into the sign bit. For QEMU this isn't undefined behaviour because we use -fwrapv; but we can avoid it anyway by using uint32_t types for or_mask and and_mask. Resolves: Coverity CID 1547628 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Yoshinori Sato <ysato@users.sourceforge.jp> Message-id: 20240723172431.1757296-1-peter.maydell@linaro.org --- target/sh4/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 6702910627..9659c69550 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -187,7 +187,7 @@ void superh_cpu_do_interrupt(CPUState *cs) static void update_itlb_use(CPUSH4State * env, int itlbnb) { - uint8_t or_mask = 0, and_mask = (uint8_t) - 1; + uint32_t or_mask = 0, and_mask = 0xff; switch (itlbnb) { case 0: From 73188068d7ba40c8a37b4763db38bb1ce24ca07d Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 23 Jul 2024 18:05:13 +0100 Subject: [PATCH 2600/2884] system/physmem: Where we assume we have a RAM MR, assert it In the functions invalidate_and_set_dirty() and cpu_physical_memory_snapshot_and_clear_dirty(), we assume that we are dealing with RAM memory regions. In this case we know that memory_region_get_ram_addr() will succeed. Assert this before we use the returned ram_addr_t in arithmetic. This makes Coverity happier about these functions: it otherwise complains that we might have an arithmetic overflow that stems from the possible -1 return from memory_region_get_ram_addr(). Resolves: Coverity CID 1547629, 1547715 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: David Hildenbrand <david@redhat.com> Message-id: 20240723170513.1676453-1-peter.maydell@linaro.org --- system/physmem.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 0e19186e1b..94600a33ec 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -923,13 +923,19 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client) { DirtyMemoryBlocks *blocks; - ram_addr_t start = memory_region_get_ram_addr(mr) + offset; + ram_addr_t start, first, last; unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL); - ram_addr_t first = QEMU_ALIGN_DOWN(start, align); - ram_addr_t last = QEMU_ALIGN_UP(start + length, align); DirtyBitmapSnapshot *snap; unsigned long page, end, dest; + start = memory_region_get_ram_addr(mr); + /* We know we're only called for RAM MemoryRegions */ + assert(start != RAM_ADDR_INVALID); + start += offset; + + first = QEMU_ALIGN_DOWN(start, align); + last = QEMU_ALIGN_UP(start + length, align); + snap = g_malloc0(sizeof(*snap) + ((last - first) >> (TARGET_PAGE_BITS + 3))); snap->start = first; @@ -2659,7 +2665,11 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr length) { uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr); - addr += memory_region_get_ram_addr(mr); + ram_addr_t ramaddr = memory_region_get_ram_addr(mr); + + /* We know we're only called for RAM MemoryRegions */ + assert(ramaddr != RAM_ADDR_INVALID); + addr += ramaddr; /* No early return if dirty_log_mask is or becomes 0, because * cpu_physical_memory_set_dirty_range will still call From 442110bc6f3f308aedf884103fdba87ba906dfe7 Mon Sep 17 00:00:00 2001 From: Collin Walling <walling@linux.ibm.com> Date: Fri, 26 Jul 2024 16:36:46 -0400 Subject: [PATCH 2601/2884] target/s390x: move @deprecated-props to CpuModelExpansion Info CpuModelInfo is used both as command argument and in command returns. Its @deprecated-props array does not make any sense in arguments, and is silently ignored. We actually want it only as return value of query-cpu-model-expansion. Move it from CpuModelInfo to CpuModelExpansionType, and document its dependence on expansion type property. This was identified late during review [1] and we have to fix it up while it's not part of an official QEMU release yet. [1] https://lore.kernel.org/qemu-devel/20240719181741.35146-1-walling@linux.ibm.com/ Message-ID: <20240726203646.20279-1-walling@linux.ibm.com> Fixes: eed0e8ffa38f ("target/s390x: filter deprecated properties based on model expansion type") Signed-off-by: Collin Walling <walling@linux.ibm.com> [ david: - add "Fixes", adjust description, reference v3 instead - make property s390x-only and non-optional - fixup "populate" vs. "populated" ] Signed-off-by: David Hildenbrand <david@redhat.com> --- qapi/machine-target.json | 19 +++++++++++-------- target/s390x/cpu_models_sysemu.c | 29 ++++++++++++++++++----------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index a552e2b0ce..00bbecc905 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -20,17 +20,11 @@ # # @props: a dictionary of QOM properties to be applied # -# @deprecated-props: a list of properties that are flagged as deprecated -# by the CPU vendor. These properties are either a subset of the -# properties enabled on the CPU model, or a set of properties -# deprecated across all models for the architecture. -# # Since: 2.8 ## { 'struct': 'CpuModelInfo', 'data': { 'name': 'str', - '*props': 'any', - '*deprecated-props': ['str'] } } + '*props': 'any' } } ## # @CpuModelExpansionType: @@ -248,10 +242,19 @@ # # @model: the expanded CpuModelInfo. # +# @deprecated-props: a list of properties that are flagged as deprecated +# by the CPU vendor. The list depends on the CpuModelExpansionType: +# "static" properties are a subset of the enabled-properties for +# the expanded model; "full" properties are a set of properties +# that are deprecated across all models for the architecture. +# (since: 9.1). +# # Since: 2.8 ## { 'struct': 'CpuModelExpansionInfo', - 'data': { 'model': 'CpuModelInfo' }, + 'data': { 'model': 'CpuModelInfo', + 'deprecated-props' : { 'type': ['str'], + 'if': 'TARGET_S390X' } }, 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', 'TARGET_ARM', diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index 94dd798b4c..f6df691b66 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -174,15 +174,11 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, bool delta_changes) { QDict *qdict = qdict_new(); - S390FeatBitmap bitmap, deprecated; + S390FeatBitmap bitmap; /* always fallback to the static base model */ info->name = g_strdup_printf("%s-base", model->def->name); - /* features flagged as deprecated */ - bitmap_zero(deprecated, S390_FEAT_MAX); - s390_get_deprecated_features(deprecated); - if (delta_changes) { /* features deleted from the base feature set */ bitmap_andnot(bitmap, model->def->base_feat, model->features, @@ -197,9 +193,6 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat); } - - /* deprecated features that are a subset of the model's enabled features */ - bitmap_and(deprecated, deprecated, model->features, S390_FEAT_MAX); } else { /* expand all features */ s390_feat_bitmap_to_ascii(model->features, qdict, @@ -213,9 +206,6 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, } else { info->props = QOBJECT(qdict); } - - s390_feat_bitmap_to_ascii(deprecated, &info->deprecated_props, list_add_feat); - info->has_deprecated_props = !!info->deprecated_props; } CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, @@ -226,6 +216,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, CpuModelExpansionInfo *expansion_info = NULL; S390CPUModel s390_model; bool delta_changes = false; + S390FeatBitmap deprecated_feats; /* convert it to our internal representation */ cpu_model_from_info(&s390_model, model, "model", &err); @@ -245,6 +236,22 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, expansion_info = g_new0(CpuModelExpansionInfo, 1); expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); cpu_info_from_model(expansion_info->model, &s390_model, delta_changes); + + /* populate list of deprecated features */ + bitmap_zero(deprecated_feats, S390_FEAT_MAX); + s390_get_deprecated_features(deprecated_feats); + + if (delta_changes) { + /* + * Only populate deprecated features that are a + * subset of the features enabled on the CPU model. + */ + bitmap_and(deprecated_feats, deprecated_feats, + s390_model.features, S390_FEAT_MAX); + } + + s390_feat_bitmap_to_ascii(deprecated_feats, + &expansion_info->deprecated_props, list_add_feat); return expansion_info; } From 22531d8cc58f390986e7070eca5d1628aa7b2fe0 Mon Sep 17 00:00:00 2001 From: Vivian Wang <uwu@dram.page> Date: Tue, 23 Jul 2024 18:05:44 +0800 Subject: [PATCH 2602/2884] util/getauxval: Ensure setting errno if not found Sometimes zero is a valid value for getauxval (e.g. AT_EXECFD). Make sure that we can distinguish between a valid zero value and a not found entry by setting errno. Assumes that getauxval from sys/auxv.h sets errno correctly. Signed-off-by: Vivian Wang <uwu@dram.page> Message-ID: <20240723100545.405476-2-uwu@dram.page> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- util/getauxval.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/getauxval.c b/util/getauxval.c index b124107d61..ad4f6686a8 100644 --- a/util/getauxval.c +++ b/util/getauxval.c @@ -95,6 +95,7 @@ unsigned long qemu_getauxval(unsigned long type) } } + errno = ENOENT; return 0; } @@ -104,7 +105,10 @@ unsigned long qemu_getauxval(unsigned long type) unsigned long qemu_getauxval(unsigned long type) { unsigned long aux = 0; - elf_aux_info(type, &aux, sizeof(aux)); + int ret = elf_aux_info(type, &aux, sizeof(aux)); + if (ret != 0) { + errno = ret; + } return aux; } @@ -112,6 +116,7 @@ unsigned long qemu_getauxval(unsigned long type) unsigned long qemu_getauxval(unsigned long type) { + errno = ENOSYS; return 0; } From 25268a18550323f6babbcc260838fa09941e5c85 Mon Sep 17 00:00:00 2001 From: Vivian Wang <uwu@dram.page> Date: Tue, 23 Jul 2024 18:05:45 +0800 Subject: [PATCH 2603/2884] linux-user/main: Check errno when getting AT_EXECFD It's possible for AT_EXECFD to end up with a valid value of 0. Check errno when using qemu_getauxval instead of return value to handle this case. Not handling this case leads to a confusing condition where the executable ends up as fd 0, i.e. stdin. Signed-off-by: Vivian Wang <uwu@dram.page> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Fixes: 0b959cf5e4cc ("linux-user: Use qemu_getauxval for AT_EXECFD") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2448 Message-ID: <20240723100545.405476-3-uwu@dram.page> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- linux-user/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/main.c b/linux-user/main.c index 7d3cf45fa9..8143a0d4b0 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -755,8 +755,9 @@ int main(int argc, char **argv, char **envp) /* * Manage binfmt-misc open-binary flag */ + errno = 0; execfd = qemu_getauxval(AT_EXECFD); - if (execfd == 0) { + if (errno != 0) { execfd = open(exec_path, O_RDONLY); if (execfd < 0) { printf("Error while loading %s: %s\n", exec_path, strerror(errno)); From 27fca0a0d560ae704457c5f89e0be658afef034d Mon Sep 17 00:00:00 2001 From: Brad Smith <brad@comstyle.com> Date: Sat, 27 Jul 2024 23:58:55 -0400 Subject: [PATCH 2604/2884] util/cpuinfo: Make use of elf_aux_info(3) on OpenBSD Signed-off-by: Brad Smith <brad@comstyle.com> Message-ID: <ZqXB_zz0fR1CpA7k@humpty.home.comstyle.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- meson.build | 8 ++++++++ util/cpuinfo-aarch64.c | 9 ++++++--- util/cpuinfo-ppc.c | 5 +++-- util/getauxval.c | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 5613b62a4f..97f63aa86c 100644 --- a/meson.build +++ b/meson.build @@ -2835,6 +2835,14 @@ config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' return getauxval(AT_HWCAP) == 0; }''')) +config_host_data.set('CONFIG_ELF_AUX_INFO', cc.links(gnu_source_prefix + ''' + #include <sys/auxv.h> + int main(void) { + unsigned long hwcap = 0; + elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)); + return hwcap; + }''')) + config_host_data.set('CONFIG_USBFS', have_linux_user and cc.compiles(''' #include <linux/usbdevice_fs.h> diff --git a/util/cpuinfo-aarch64.c b/util/cpuinfo-aarch64.c index 8ca775a14b..57468890c3 100644 --- a/util/cpuinfo-aarch64.c +++ b/util/cpuinfo-aarch64.c @@ -17,10 +17,13 @@ # define HWCAP2_BTI 0 /* added in glibc 2.32 */ # endif #endif +#ifdef CONFIG_ELF_AUX_INFO +#include <sys/auxv.h> +#endif #ifdef CONFIG_DARWIN # include <sys/sysctl.h> #endif -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) # include <machine/armreg.h> # include <machine/cpu.h> # include <sys/types.h> @@ -61,7 +64,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info = CPUINFO_ALWAYS; -#ifdef CONFIG_LINUX +#if defined(CONFIG_LINUX) || defined(CONFIG_ELF_AUX_INFO) unsigned long hwcap = qemu_getauxval(AT_HWCAP); info |= (hwcap & HWCAP_ATOMICS ? CPUINFO_LSE : 0); info |= (hwcap & HWCAP_USCAT ? CPUINFO_LSE2 : 0); @@ -78,7 +81,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= sysctl_for_bool("hw.optional.arm.FEAT_PMULL") * CPUINFO_PMULL; info |= sysctl_for_bool("hw.optional.arm.FEAT_BTI") * CPUINFO_BTI; #endif -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) int mib[2]; uint64_t isar0; uint64_t pfr1; diff --git a/util/cpuinfo-ppc.c b/util/cpuinfo-ppc.c index 1304f9aa80..4d3d3aae0b 100644 --- a/util/cpuinfo-ppc.c +++ b/util/cpuinfo-ppc.c @@ -14,7 +14,8 @@ # include "elf.h" # endif #endif -#ifdef __FreeBSD__ +#if defined(CONFIG_ELF_AUX_INFO) +# include <sys/auxv.h> # include <machine/cpu.h> # ifndef PPC_FEATURE2_ARCH_3_1 # define PPC_FEATURE2_ARCH_3_1 0 @@ -35,7 +36,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info = CPUINFO_ALWAYS; -#if defined(CONFIG_LINUX) || defined(__FreeBSD__) +#if defined(CONFIG_LINUX) || defined(CONFIG_ELF_AUX_INFO) unsigned long hwcap = qemu_getauxval(AT_HWCAP); unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); diff --git a/util/getauxval.c b/util/getauxval.c index ad4f6686a8..0735cd8271 100644 --- a/util/getauxval.c +++ b/util/getauxval.c @@ -99,7 +99,7 @@ unsigned long qemu_getauxval(unsigned long type) return 0; } -#elif defined(__FreeBSD__) +#elif defined(CONFIG_ELF_AUX_INFO) #include <sys/auxv.h> unsigned long qemu_getauxval(unsigned long type) From d9b019e0a05cbbaa184815dd201b25006950c6d7 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso <fdurso@google.com> Date: Wed, 19 Jun 2024 21:41:09 +0200 Subject: [PATCH 2605/2884] linux-user: open_self_stat: Implement num_threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The num_threads field reports the total number of threads in the process. In QEMU, this is equal to the number of CPU instances. Signed-off-by: Fabio D'Urso <fdurso@google.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-ID: <20240619194109.248066-1-fdurso@google.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- linux-user/syscall.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b8c278b91d..9d5415674d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8168,6 +8168,16 @@ static int open_self_stat(CPUArchState *cpu_env, int fd) } else if (i == 3) { /* ppid */ g_string_printf(buf, FMT_pid " ", getppid()); + } else if (i == 19) { + /* num_threads */ + int cpus = 0; + WITH_RCU_READ_LOCK_GUARD() { + CPUState *cpu_iter; + CPU_FOREACH(cpu_iter) { + cpus++; + } + } + g_string_printf(buf, "%d ", cpus); } else if (i == 21) { /* starttime */ g_string_printf(buf, "%" PRIu64 " ", ts->start_boottime); From c1d822ca344f810710117668b0511590dffb1498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Mon, 29 Jul 2024 15:44:01 +0100 Subject: [PATCH 2606/2884] gitlab: record installed packages in /packages.txt in containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lcitool created containers save the full distro package list details into /packages.txt. The idea is that build jobs will 'cat' this file, so that the build log has a record of what packages were used. This is important info, because when it comes to debug failures, the original container is often lost. This extends the manually written dockerfiles to also create the /packages.txt file. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20240724095505.33544-2-berrange@redhat.com> Acked-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-2-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-all-test-cross.docker | 3 ++- tests/docker/dockerfiles/debian-hexagon-cross.docker | 3 ++- tests/docker/dockerfiles/debian-legacy-test-cross.docker | 3 ++- tests/docker/dockerfiles/debian-loongarch-cross.docker | 3 ++- tests/docker/dockerfiles/debian-tricore-cross.docker | 3 ++- tests/docker/dockerfiles/debian-xtensa-cross.docker | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 6cc38a3633..8ab244e018 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -62,7 +62,8 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ gcc-s390x-linux-gnu \ libc6-dev-s390x-cross \ gcc-sparc64-linux-gnu \ - libc6-dev-sparc64-cross + libc6-dev-sparc64-cross && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker index f2d40f2dee..23152b4918 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -33,7 +33,8 @@ RUN apt-get update && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-legacy-test-cross.docker b/tests/docker/dockerfiles/debian-legacy-test-cross.docker index d75e0b85e2..5a6616b7d3 100644 --- a/tests/docker/dockerfiles/debian-legacy-test-cross.docker +++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker @@ -36,7 +36,8 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ python3-pip \ python3-setuptools \ python3-venv \ - python3-wheel + python3-wheel && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker index 6a9197528b..79eab5621e 100644 --- a/tests/docker/dockerfiles/debian-loongarch-cross.docker +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -32,7 +32,8 @@ RUN apt-get update && \ python3-pip \ python3-setuptools \ python3-venv \ - python3-wheel + python3-wheel && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 16276aa21d..479b4d6eba 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -34,7 +34,8 @@ RUN apt update && \ python3-pip \ python3-setuptools \ python3-wheel \ - python3-venv + python3-venv && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt RUN /usr/bin/pip3 install tomli diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker index 413881899b..d011eee2ad 100644 --- a/tests/docker/dockerfiles/debian-xtensa-cross.docker +++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker @@ -16,7 +16,8 @@ RUN apt-get update && \ curl \ gettext \ git \ - python3-minimal + python3-minimal && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt ENV CPU_LIST dc232b dc233c de233_fpu dsp3400 ENV TOOLCHAIN_RELEASE 2020.07 From b86a46980b4bf3291945454cf564adb0f3894b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Mon, 29 Jul 2024 15:44:02 +0100 Subject: [PATCH 2607/2884] gitlab: display /packages.txt in build jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lcitool created containers save the full distro package list details into /packages.txt. The idea is that build jobs will 'cat' this file, so that the build log has a record of what packages were used. This is important info, because when it comes to debug failures, the original container is often lost. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20240724095505.33544-3-berrange@redhat.com> Acked-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-3-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest-template.yml | 1 + .gitlab-ci.d/crossbuild-template.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 8f7ebfaed8..844c26623d 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -9,6 +9,7 @@ when: always before_script: - JOBS=$(expr $(nproc) + 1) + - cat /packages.txt script: - export CCACHE_BASEDIR="$(pwd)" - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index d9f81b7061..53051ec793 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -8,6 +8,8 @@ key: "$CI_JOB_NAME" when: always timeout: 80m + before_script: + - cat /packages.txt script: - export CCACHE_BASEDIR="$(pwd)" - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" From 6c180490b0fc8c31209c508bf203dd415bab9a1b Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 29 Jul 2024 15:44:03 +0100 Subject: [PATCH 2608/2884] tests/tcg: Use --noexecstack with assembler files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the --noexecstack assembler command-line option to avoid: /usr/bin/ld: warning: boot.o: missing .note.GNU-stack section implies executable stack /usr/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker which is enabled by default with current debian cross toolchains. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240724010733.22129-2-richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-4-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 2 +- tests/tcg/aarch64/Makefile.softmmu-target | 2 +- tests/tcg/alpha/Makefile.softmmu-target | 2 +- tests/tcg/arm/Makefile.softmmu-target | 2 +- tests/tcg/arm/Makefile.target | 2 +- tests/tcg/i386/Makefile.softmmu-target | 2 +- tests/tcg/loongarch64/Makefile.softmmu-target | 2 +- tests/tcg/riscv64/Makefile.softmmu-target | 2 +- tests/tcg/s390x/Makefile.softmmu-target | 2 +- tests/tcg/x86_64/Makefile.softmmu-target | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index cb8cfeb6da..1f8e5b3d30 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -115,7 +115,7 @@ endif %: %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) %: %.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wa,--noexecstack $< -o $@ $(LDFLAGS) else # For system targets we include a different Makefile fragment as the # build options for bare programs are usually pretty different. They diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index dd6d595830..139e04d15f 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -28,7 +28,7 @@ config-cc.mak: Makefile .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) diff --git a/tests/tcg/alpha/Makefile.softmmu-target b/tests/tcg/alpha/Makefile.softmmu-target index 09193a62d6..a0eca4d6ea 100644 --- a/tests/tcg/alpha/Makefile.softmmu-target +++ b/tests/tcg/alpha/Makefile.softmmu-target @@ -22,7 +22,7 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target index 547063c08c..b66074b0b4 100644 --- a/tests/tcg/arm/Makefile.softmmu-target +++ b/tests/tcg/arm/Makefile.softmmu-target @@ -36,7 +36,7 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc .PRECIOUS: $(CRT_OBJS) %.o: $(ARM_SRC)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 8e287191af..06ddf3e04f 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -25,7 +25,7 @@ ARM_TESTS += test-arm-iwmmxt # Clang assembler does not support IWMXT, so use the external assembler. test-arm-iwmmxt: CFLAGS += -marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 $(CROSS_CC_HAS_FNIA) test-arm-iwmmxt: test-arm-iwmmxt.S - $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) -Wa,--noexecstack $< -o $@ $(LDFLAGS) # Float-convert Tests ARM_TESTS += fcvt diff --git a/tests/tcg/i386/Makefile.softmmu-target b/tests/tcg/i386/Makefile.softmmu-target index 5266f2335a..4096a1cf31 100644 --- a/tests/tcg/i386/Makefile.softmmu-target +++ b/tests/tcg/i386/Makefile.softmmu-target @@ -25,7 +25,7 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) diff --git a/tests/tcg/loongarch64/Makefile.softmmu-target b/tests/tcg/loongarch64/Makefile.softmmu-target index 908f3a8c0f..d5d5c1a7f6 100644 --- a/tests/tcg/loongarch64/Makefile.softmmu-target +++ b/tests/tcg/loongarch64/Makefile.softmmu-target @@ -22,7 +22,7 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index d5b126e5f1..7c1d44d3f4 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -10,7 +10,7 @@ LDFLAGS = -T $(LINK_SCRIPT) CFLAGS += -g -Og %.o: %.S - $(CC) $(CFLAGS) $< -c -o $@ + $(CC) $(CFLAGS) $< -Wa,--noexecstack -c -o $@ %: %.o $(LINK_SCRIPT) $(LD) $(LDFLAGS) $< -o $@ diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 4c8e15e625..f60f94b090 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -6,7 +6,7 @@ CFLAGS+=-ggdb -O0 LDFLAGS=-nostdlib -static %.o: %.S - $(CC) -march=z13 -m64 -c $< -o $@ + $(CC) -march=z13 -m64 -Wa,--noexecstack -c $< -o $@ %.o: %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -march=z13 -m64 -c $< -o $@ diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 1bd763f2e6..ef6bcb4dc7 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -25,7 +25,7 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) From 4e4b685143e33b09c09c0f7cd3b5253218e8cf70 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 29 Jul 2024 15:44:04 +0100 Subject: [PATCH 2609/2884] tests/tcg/loongarch64: Use --no-warn-rwx-segments to link system tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent debian cross-linker for loongarch issues ld: warning: hello has a LOAD segment with RWX permissions This is partially related to tests/tcg/loongarch64/system/kernel.ld, but is not fixed by explicitly adding a single LOAD PHDR. Disable the warning, since it does not apply to kernel images. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240724010733.22129-3-richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-5-alex.bennee@linaro.org> --- tests/tcg/loongarch64/Makefile.softmmu-target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tcg/loongarch64/Makefile.softmmu-target b/tests/tcg/loongarch64/Makefile.softmmu-target index d5d5c1a7f6..6d4a20fde7 100644 --- a/tests/tcg/loongarch64/Makefile.softmmu-target +++ b/tests/tcg/loongarch64/Makefile.softmmu-target @@ -16,7 +16,7 @@ LINK_SCRIPT=$(LOONGARCH64_SYSTEM_SRC)/kernel.ld LDFLAGS=-Wl,-T$(LINK_SCRIPT) TESTS+=$(LOONGARCH64_TESTS) $(MULTIARCH_TESTS) CFLAGS+=-nostdlib -g -O1 -march=loongarch64 -mabi=lp64d $(MINILIB_INC) -LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc +LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc -Wl,--no-warn-rwx-segments # building head blobs .PRECIOUS: $(CRT_OBJS) From ec5a4c7ffee2de5d9784bc02e44796e3f32f4f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:05 +0100 Subject: [PATCH 2610/2884] tests/tcg: update README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the document with details about the layout of tests. Remove the out of date cris comments. Refer to the developer guide for details about how to run the tests. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-6-alex.bennee@linaro.org> --- tests/tcg/README | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/tcg/README b/tests/tcg/README index 706bb185b4..6d08ca50dc 100644 --- a/tests/tcg/README +++ b/tests/tcg/README @@ -1,9 +1,14 @@ -This directory contains various interesting guest programs for -regression testing. Tests are either multi-arch, meaning they can be -built for all guest architectures that support linux-user executable, -or they are architecture specific. +This directory contains various interesting guest binaries for +regression testing the Tiny Code Generator doing system and user-mode +emulation. -CRIS -==== -The testsuite for CRIS is in tests/tcg/cris. You can run it -with "make test-cris". +The multiarch directory contains shared code for tests that can be +built for all guest architectures. Architecture specific code can be +found in their respective directories. + +System mode tests will be under the "system" subdirectories. + +GDB scripts for exercising the gdbstub on specific tests will be found +under the "gdbstb" subdirectories. + +See the developer guide for more instructions on "make check-tcg" From c8d6e0422341f8ad8706a446b83a824fdfeacc11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:06 +0100 Subject: [PATCH 2611/2884] docs/devel: update the testing introduction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the mention of "check-help" up to the intro text and also mention the meson test integration. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-7-alex.bennee@linaro.org> --- docs/devel/testing.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 23d3f44f52..b984c5dd26 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -3,13 +3,28 @@ Testing in QEMU =============== -This document describes the testing infrastructure in QEMU. +QEMU's testing infrastructure is fairly complex as it covers +everything from unit testing and exercising specific sub-systems all +the way to full blown acceptance tests. To get an overview of the +tests you can run ``make check-help`` from either the source or build +tree. + +Most (but not all) tests are also integrated into the meson build +system so can be run directly from the build tree, for example: + +.. code:: + + [./pyvenv/bin/]meson test --suite qemu:softfloat + +will run just the softfloat tests. + +The rest of this document will cover the details for specific test +groups. Testing with "make check" ------------------------- -The "make check" testing family includes most of the C based tests in QEMU. For -a quick help, run ``make check-help`` from the source tree. +The "make check" testing family includes most of the C based tests in QEMU. The usual way to run these tests is: From b421206882d8581186b2419b1292e1b189ac976c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:07 +0100 Subject: [PATCH 2612/2884] docs/devel: document how to run individual TCG tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 6f6ca067d2 (tests/tcg: add some help output for running individual tests) we made it easier to run individual tests for a given architecture. Lets reference that in the developer documentation. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-8-alex.bennee@linaro.org> --- docs/devel/testing.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index b984c5dd26..af73d3d64f 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -1490,6 +1490,19 @@ And run with:: Adding ``V=1`` to the invocation will show the details of how to invoke QEMU for the test which is useful for debugging tests. +Running individual tests +~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests can also be run directly from the test build directory. If you +run ``make help`` from the test build directory you will get a list of +all the tests that can be run. Please note that same binaries are used +in multiple tests, for example:: + + make run-plugin-test-mmap-with-libinline.so + +will run the mmap test with the ``libinline.so`` TCG plugin. The +gdbstub tests also re-use the test binaries but while exercising gdb. + TCG test dependencies ~~~~~~~~~~~~~~~~~~~~~ From 6908e4a28bb1585e72ad33a3fb58d4e0c2ae4f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:08 +0100 Subject: [PATCH 2613/2884] tests/avocado: remove tcg_plugins virt_mem_icount test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 4f8d886085 (tests/plugin/mem: migrate to new per_vcpu API) this test was skipping due to not being able to run callback and inline memory instrumentation at the same time. However b480f7a621 (tests/plugin: add test plugin for inline operations) tests for all this matching up so we don't need the additional complexity in avocado. Remove the test. Fixes: 4f8d886085 Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-9-alex.bennee@linaro.org> --- tests/avocado/tcg_plugins.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py index 15fd87b2c1..a930fca2c0 100644 --- a/tests/avocado/tcg_plugins.py +++ b/tests/avocado/tcg_plugins.py @@ -120,36 +120,3 @@ class PluginKernelNormal(PluginKernelBase): else: count = int(m.group("count")) self.log.info(f"Counted: {count} instructions") - - def test_aarch64_virt_mem_icount(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_path = self._grab_aarch64_kernel() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' - - plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", - suffix=".log") - - self.run_vm(kernel_path, kernel_command_line, - "tests/plugin/libmem.so,inline=true,callback=true", plugin_log.name, - console_pattern, - args=('-icount', 'shift=1')) - - with plugin_log as lf, \ - mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: - m = re.findall(br"mem accesses: (?P<count>\d+)", s) - if m is None or len(m) != 2: - self.fail("no memory access counts found") - else: - inline = int(m[0]) - callback = int(m[1]) - if inline != callback: - self.fail("mismatched access counts") - else: - self.log.info(f"Counted {inline} memory accesses") From 1b4c136b782f22876bdd213d0034314001fcd5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:09 +0100 Subject: [PATCH 2614/2884] tests/tcg: move test plugins into tcg subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You cannot use plugins without TCG enabled so it doesn't make sense to have them separated off in the test directory structure. While we are at it rename the directory to plugins to reflect the plural nature of the directory and match up with contrib/plugins. Suggested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-10-alex.bennee@linaro.org> --- MAINTAINERS | 2 +- tests/avocado/tcg_plugins.py | 4 ++-- tests/meson.build | 2 +- tests/tcg/Makefile.target | 4 ++-- tests/{plugin => tcg/plugins}/bb.c | 0 tests/{plugin => tcg/plugins}/empty.c | 0 tests/{plugin => tcg/plugins}/inline.c | 0 tests/{plugin => tcg/plugins}/insn.c | 0 tests/{plugin => tcg/plugins}/mem.c | 0 tests/{plugin => tcg/plugins}/meson.build | 6 +++--- tests/{plugin => tcg/plugins}/syscall.c | 0 11 files changed, 9 insertions(+), 9 deletions(-) rename tests/{plugin => tcg/plugins}/bb.c (100%) rename tests/{plugin => tcg/plugins}/empty.c (100%) rename tests/{plugin => tcg/plugins}/inline.c (100%) rename tests/{plugin => tcg/plugins}/insn.c (100%) rename tests/{plugin => tcg/plugins}/mem.c (100%) rename tests/{plugin => tcg/plugins}/meson.build (70%) rename tests/{plugin => tcg/plugins}/syscall.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 98eddf7ae1..72b3c67360 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3751,7 +3751,7 @@ R: Pierrick Bouvier <pierrick.bouvier@linaro.org> S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ -F: tests/plugin/ +F: tests/tcg/plugins/ F: tests/avocado/tcg_plugins.py F: contrib/plugins/ diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py index a930fca2c0..a6ff457e27 100644 --- a/tests/avocado/tcg_plugins.py +++ b/tests/avocado/tcg_plugins.py @@ -77,7 +77,7 @@ class PluginKernelNormal(PluginKernelBase): suffix=".log") self.run_vm(kernel_path, kernel_command_line, - "tests/plugin/libinsn.so", plugin_log.name, + "tests/tcg/plugins/libinsn.so", plugin_log.name, console_pattern) with plugin_log as lf, \ @@ -107,7 +107,7 @@ class PluginKernelNormal(PluginKernelBase): suffix=".log") self.run_vm(kernel_path, kernel_command_line, - "tests/plugin/libinsn.so", plugin_log.name, + "tests/tcg/plugins/libinsn.so", plugin_log.name, console_pattern, args=('-icount', 'shift=1')) diff --git a/tests/meson.build b/tests/meson.build index acb6807094..80dd3029cf 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -78,7 +78,7 @@ subdir('decode') if 'CONFIG_TCG' in config_all_accel subdir('fp') - subdir('plugin') + subdir('tcg/plugins') endif subdir('unit') diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 1f8e5b3d30..452a2cde65 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -142,8 +142,8 @@ RUN_TESTS=$(patsubst %,run-%, $(TESTS)) # If plugins exist also include those in the tests ifeq ($(CONFIG_PLUGIN),y) -PLUGIN_SRC=$(SRC_PATH)/tests/plugin -PLUGIN_LIB=../../plugin +PLUGIN_SRC=$(SRC_PATH)/tests/tcg/plugins +PLUGIN_LIB=../plugins VPATH+=$(PLUGIN_LIB) PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) diff --git a/tests/plugin/bb.c b/tests/tcg/plugins/bb.c similarity index 100% rename from tests/plugin/bb.c rename to tests/tcg/plugins/bb.c diff --git a/tests/plugin/empty.c b/tests/tcg/plugins/empty.c similarity index 100% rename from tests/plugin/empty.c rename to tests/tcg/plugins/empty.c diff --git a/tests/plugin/inline.c b/tests/tcg/plugins/inline.c similarity index 100% rename from tests/plugin/inline.c rename to tests/tcg/plugins/inline.c diff --git a/tests/plugin/insn.c b/tests/tcg/plugins/insn.c similarity index 100% rename from tests/plugin/insn.c rename to tests/tcg/plugins/insn.c diff --git a/tests/plugin/mem.c b/tests/tcg/plugins/mem.c similarity index 100% rename from tests/plugin/mem.c rename to tests/tcg/plugins/mem.c diff --git a/tests/plugin/meson.build b/tests/tcg/plugins/meson.build similarity index 70% rename from tests/plugin/meson.build rename to tests/tcg/plugins/meson.build index 9eece5bab5..f847849b1b 100644 --- a/tests/plugin/meson.build +++ b/tests/tcg/plugins/meson.build @@ -2,15 +2,15 @@ t = [] if get_option('plugins') foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] if host_os == 'windows' - t += shared_module(i, files(i + '.c') + '../../contrib/plugins/win32_linker.c', - include_directories: '../../include/qemu', + t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', + include_directories: '../../../include/qemu', link_depends: [win32_qemu_plugin_api_lib], link_args: ['-Lplugins', '-lqemu_plugin_api'], dependencies: glib) else t += shared_module(i, files(i + '.c'), - include_directories: '../../include/qemu', + include_directories: '../../../include/qemu', dependencies: glib) endif endforeach diff --git a/tests/plugin/syscall.c b/tests/tcg/plugins/syscall.c similarity index 100% rename from tests/plugin/syscall.c rename to tests/tcg/plugins/syscall.c From 6484a3e55bdb3b44a86c4034307efd930778cd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:10 +0100 Subject: [PATCH 2615/2884] docs: split TCG plugin usage from devel section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The devel section is getting quite messy with the breakdown of the example plugins which should be usable by users. As we mention plugins in the emulation section along with semihosting move the overview there leaving the development section about the details of writing plugins. While we are at make the headings nicer and convert the option lists into nicely formatted tables. Acked-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-11-alex.bennee@linaro.org> --- docs/about/emulation.rst | 567 ++++++++++++++++++++++++++++++++++++- docs/devel/tcg-plugins.rst | 505 --------------------------------- 2 files changed, 564 insertions(+), 508 deletions(-) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index 3bfe8cc14a..c03033e4e9 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -95,9 +95,6 @@ depending on the guest architecture. - Yes - A configurable 32 bit soft core now owned by Cadence -A number of features are only available when running under -emulation including :ref:`Record/Replay<replay>` and :ref:`TCG Plugins`. - .. _Semihosting: Semihosting @@ -182,3 +179,567 @@ for that architecture. * - Xtensa - System - Tensilica ISS SIMCALL + +TCG Plugins +----------- + +QEMU TCG plugins provide a way for users to run experiments taking +advantage of the total system control emulation can have over a guest. +It provides a mechanism for plugins to subscribe to events during +translation and execution and optionally callback into the plugin +during these events. TCG plugins are unable to change the system state +only monitor it passively. However they can do this down to an +individual instruction granularity including potentially subscribing +to all load and store operations. + +See the developer section of the manual for details about +:ref:`writing plugins<TCG Plugins>`. + +Usage +~~~~~ + +Any QEMU binary with TCG support has plugins enabled by default. +Earlier releases needed to be explicitly enabled with:: + + configure --enable-plugins + +Once built a program can be run with multiple plugins loaded each with +their own arguments:: + + $QEMU $OTHER_QEMU_ARGS \ + -plugin contrib/plugin/libhowvec.so,inline=on,count=hint \ + -plugin contrib/plugin/libhotblocks.so + +Arguments are plugin specific and can be used to modify their +behaviour. In this case the howvec plugin is being asked to use inline +ops to count and break down the hint instructions by type. + +Linux user-mode emulation also evaluates the environment variable +``QEMU_PLUGIN``:: + + QEMU_PLUGIN="file=contrib/plugins/libhowvec.so,inline=on,count=hint" $QEMU + +Example Plugins +~~~~~~~~~~~~~~~ + +There are a number of plugins included with QEMU and you are +encouraged to contribute your own plugins plugins upstream. There is a +``contrib/plugins`` directory where they can go. There are also some +basic plugins that are used to test and exercise the API during the +``make check-tcg`` target in ``tests/tcg/plugins`` that are never the +less useful for basic analysis. + +Empty +..... + +``tests/tcg/plugins/empty.c`` + +Purely a test plugin for measuring the overhead of the plugins system +itself. Does no instrumentation. + +Basic Blocks +............ + +``tests/tcg/plugins/bb.c`` + +A very basic plugin which will measure execution in coarse terms as +each basic block is executed. By default the results are shown once +execution finishes:: + + $ qemu-aarch64 -plugin tests/plugin/libbb.so \ + -d plugin ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + bb's: 2277338, insns: 158483046 + +Behaviour can be tweaked with the following arguments: + +.. list-table:: Basic Block plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter. Not per-cpu and not + thread safe. + * - idle=true|false + - Dump the current execution stats whenever the guest vCPU idles + +Instruction +........... + +``tests/tcg/plugins/insn.c`` + +This is a basic instruction level instrumentation which can count the +number of instructions executed on each core/thread:: + + $ qemu-aarch64 -plugin tests/plugin/libinsn.so \ + -d plugin ./tests/tcg/aarch64-linux-user/threadcount + Created 10 threads + Done + cpu 0 insns: 46765 + cpu 1 insns: 3694 + cpu 2 insns: 3694 + cpu 3 insns: 2994 + cpu 4 insns: 1497 + cpu 5 insns: 1497 + cpu 6 insns: 1497 + cpu 7 insns: 1497 + total insns: 63135 + +Behaviour can be tweaked with the following arguments: + +.. list-table:: Instruction plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter. + * - sizes=true|false + - Give a summary of the instruction sizes for the execution + * - match=<string> + - Only instrument instructions matching the string prefix + +The ``match`` option will show some basic stats including how many +instructions have executed since the last execution. For +example:: + + $ qemu-aarch64 -plugin tests/plugin/libinsn.so,match=bl \ + -d plugin ./tests/tcg/aarch64-linux-user/sha512-vector + ... + 0x40069c, 'bl #0x4002b0', 10 hits, 1093 match hits, Δ+1257 since last match, 98 avg insns/match + 0x4006ac, 'bl #0x403690', 10 hits, 1094 match hits, Δ+47 since last match, 98 avg insns/match + 0x4037fc, 'bl #0x4002b0', 18 hits, 1095 match hits, Δ+22 since last match, 98 avg insns/match + 0x400720, 'bl #0x403690', 10 hits, 1096 match hits, Δ+58 since last match, 98 avg insns/match + 0x4037fc, 'bl #0x4002b0', 19 hits, 1097 match hits, Δ+22 since last match, 98 avg insns/match + 0x400730, 'bl #0x403690', 10 hits, 1098 match hits, Δ+33 since last match, 98 avg insns/match + 0x4037ac, 'bl #0x4002b0', 12 hits, 1099 match hits, Δ+20 since last match, 98 avg insns/match + ... + +For more detailed execution tracing see the ``execlog`` plugin for +other options. + +Memory +...... + +``tests/tcg/plugins/mem.c`` + +Basic instruction level memory instrumentation:: + + $ qemu-aarch64 -plugin tests/plugin/libmem.so,inline=true \ + -d plugin ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + inline mem accesses: 79525013 + +Behaviour can be tweaked with the following arguments: + +.. list-table:: Memory plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter + * - callback=true|false + - Use callbacks on each memory instrumentation. + * - hwaddr=true|false + - Count IO accesses (only for system emulation) + +System Calls +............ + +``tests/tcg/plugins/syscall.c`` + +A basic syscall tracing plugin. This only works for user-mode. By +default it will give a summary of syscall stats at the end of the +run:: + + $ qemu-aarch64 -plugin tests/plugin/libsyscall \ + -d plugin ./tests/tcg/aarch64-linux-user/threadcount + Created 10 threads + Done + syscall no. calls errors + 226 12 0 + 99 11 11 + 115 11 0 + 222 11 0 + 93 10 0 + 220 10 0 + 233 10 0 + 215 8 0 + 214 4 0 + 134 2 0 + 64 2 0 + 96 1 0 + 94 1 0 + 80 1 0 + 261 1 0 + 78 1 0 + 160 1 0 + 135 1 0 + +Hot Blocks +.......... + +``contrib/plugins/hotblocks.c`` + +The hotblocks plugin allows you to examine the where hot paths of +execution are in your program. Once the program has finished you will +get a sorted list of blocks reporting the starting PC, translation +count, number of instructions and execution count. This will work best +with linux-user execution as system emulation tends to generate +re-translations as blocks from different programs get swapped in and +out of system memory. + +If your program is single-threaded you can use the ``inline`` option for +slightly faster (but not thread safe) counters. + +Example:: + + $ qemu-aarch64 \ + -plugin contrib/plugins/libhotblocks.so -d plugin \ + ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + collected 903 entries in the hash table + pc, tcount, icount, ecount + 0x0000000041ed10, 1, 5, 66087 + 0x000000004002b0, 1, 4, 66087 + ... + + +Hot Pages +......... + +``contrib/plugins/hotpages.c`` + +Similar to hotblocks but this time tracks memory accesses:: + + $ qemu-aarch64 \ + -plugin contrib/plugins/libhotpages.so -d plugin \ + ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + Addr, RCPUs, Reads, WCPUs, Writes + 0x000055007fe000, 0x0001, 31747952, 0x0001, 8835161 + 0x000055007ff000, 0x0001, 29001054, 0x0001, 8780625 + 0x00005500800000, 0x0001, 687465, 0x0001, 335857 + 0x0000000048b000, 0x0001, 130594, 0x0001, 355 + 0x0000000048a000, 0x0001, 1826, 0x0001, 11 + +The hotpages plugin can be configured using the following arguments: + +.. list-table:: Hot pages arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - sortby=reads|writes|address + - Log the data sorted by either the number of reads, the number of writes, or + memory address. (Default: entries are sorted by the sum of reads and writes) + * - io=on + - Track IO addresses. Only relevant to full system emulation. (Default: off) + * - pagesize=N + - The page size used. (Default: N = 4096) + +Instruction Distribution +........................ + +``contrib/plugins/howvec.c`` + +This is an instruction classifier so can be used to count different +types of instructions. It has a number of options to refine which get +counted. You can give a value to the ``count`` argument for a class of +instructions to break it down fully, so for example to see all the system +registers accesses:: + + $ qemu-system-aarch64 $(QEMU_ARGS) \ + -append "root=/dev/sda2 systemd.unit=benchmark.service" \ + -smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin + +which will lead to a sorted list after the class breakdown:: + + Instruction Classes: + Class: UDEF not counted + Class: SVE (68 hits) + Class: PCrel addr (47789483 hits) + Class: Add/Sub (imm) (192817388 hits) + Class: Logical (imm) (93852565 hits) + Class: Move Wide (imm) (76398116 hits) + Class: Bitfield (44706084 hits) + Class: Extract (5499257 hits) + Class: Cond Branch (imm) (147202932 hits) + Class: Exception Gen (193581 hits) + Class: NOP not counted + Class: Hints (6652291 hits) + Class: Barriers (8001661 hits) + Class: PSTATE (1801695 hits) + Class: System Insn (6385349 hits) + Class: System Reg counted individually + Class: Branch (reg) (69497127 hits) + Class: Branch (imm) (84393665 hits) + Class: Cmp & Branch (110929659 hits) + Class: Tst & Branch (44681442 hits) + Class: AdvSimd ldstmult (736 hits) + Class: ldst excl (9098783 hits) + Class: Load Reg (lit) (87189424 hits) + Class: ldst noalloc pair (3264433 hits) + Class: ldst pair (412526434 hits) + Class: ldst reg (imm) (314734576 hits) + Class: Loads & Stores (2117774 hits) + Class: Data Proc Reg (223519077 hits) + Class: Scalar FP (31657954 hits) + Individual Instructions: + Instr: mrs x0, sp_el0 (2682661 hits) (op=0xd5384100/ System Reg) + Instr: mrs x1, tpidr_el2 (1789339 hits) (op=0xd53cd041/ System Reg) + Instr: mrs x2, tpidr_el2 (1513494 hits) (op=0xd53cd042/ System Reg) + Instr: mrs x0, tpidr_el2 (1490823 hits) (op=0xd53cd040/ System Reg) + Instr: mrs x1, sp_el0 (933793 hits) (op=0xd5384101/ System Reg) + Instr: mrs x2, sp_el0 (699516 hits) (op=0xd5384102/ System Reg) + Instr: mrs x4, tpidr_el2 (528437 hits) (op=0xd53cd044/ System Reg) + Instr: mrs x30, ttbr1_el1 (480776 hits) (op=0xd538203e/ System Reg) + Instr: msr ttbr1_el1, x30 (480713 hits) (op=0xd518203e/ System Reg) + Instr: msr vbar_el1, x30 (480671 hits) (op=0xd518c01e/ System Reg) + ... + +To find the argument shorthand for the class you need to examine the +source code of the plugin at the moment, specifically the ``*opt`` +argument in the InsnClassExecCount tables. + +Lockstep Execution +.................. + +``contrib/plugins/lockstep.c`` + +This is a debugging tool for developers who want to find out when and +where execution diverges after a subtle change to TCG code generation. +It is not an exact science and results are likely to be mixed once +asynchronous events are introduced. While the use of -icount can +introduce determinism to the execution flow it doesn't always follow +the translation sequence will be exactly the same. Typically this is +caused by a timer firing to service the GUI causing a block to end +early. However in some cases it has proved to be useful in pointing +people at roughly where execution diverges. The only argument you need +for the plugin is a path for the socket the two instances will +communicate over:: + + + $ qemu-system-sparc -monitor none -parallel none \ + -net none -M SS-20 -m 256 -kernel day11/zImage.elf \ + -plugin ./contrib/plugins/liblockstep.so,sockpath=lockstep-sparc.sock \ + -d plugin,nochain + +which will eventually report:: + + qemu-system-sparc: warning: nic lance.0 has no peer + @ 0x000000ffd06678 vs 0x000000ffd001e0 (2/1 since last) + @ 0x000000ffd07d9c vs 0x000000ffd06678 (3/1 since last) + Δ insn_count @ 0x000000ffd07d9c (809900609) vs 0x000000ffd06678 (809900612) + previously @ 0x000000ffd06678/10 (809900609 insns) + previously @ 0x000000ffd001e0/4 (809900599 insns) + previously @ 0x000000ffd080ac/2 (809900595 insns) + previously @ 0x000000ffd08098/5 (809900593 insns) + previously @ 0x000000ffd080c0/1 (809900588 insns) + + +Hardware Profile +................ + +``contrib/plugins/hwprofile.c`` + +The hwprofile tool can only be used with system emulation and allows +the user to see what hardware is accessed how often. It has a number of options: + +.. list-table:: Hardware Profile arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - track=[read|write] + - By default the plugin tracks both reads and writes. You can use + this option to limit the tracking to just one class of accesses. + * - source + - Will include a detailed break down of what the guest PC that made the + access was. Not compatible with the pattern option. Example output:: + + cirrus-low-memory @ 0xfffffd00000a0000 + pc:fffffc0000005cdc, 1, 256 + pc:fffffc0000005ce8, 1, 256 + pc:fffffc0000005cec, 1, 256 + + * - pattern + - Instead break down the accesses based on the offset into the HW + region. This can be useful for seeing the most used registers of + a device. Example output:: + + pci0-conf @ 0xfffffd01fe000000 + off:00000004, 1, 1 + off:00000010, 1, 3 + off:00000014, 1, 3 + off:00000018, 1, 2 + off:0000001c, 1, 2 + off:00000020, 1, 2 + ... + + +Execution Log +............. + +``contrib/plugins/execlog.c`` + +The execlog tool traces executed instructions with memory access. It can be used +for debugging and security analysis purposes. +Please be aware that this will generate a lot of output. + +The plugin needs default argument:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so -d plugin + +which will output an execution trace following this structure:: + + # vCPU, vAddr, opcode, disassembly[, load/store, memory addr, device]... + 0, 0xa12, 0xf8012400, "movs r4, #0" + 0, 0xa14, 0xf87f42b4, "cmp r4, r6" + 0, 0xa16, 0xd206, "bhs #0xa26" + 0, 0xa18, 0xfff94803, "ldr r0, [pc, #0xc]", load, 0x00010a28, RAM + 0, 0xa1a, 0xf989f000, "bl #0xd30" + 0, 0xd30, 0xfff9b510, "push {r4, lr}", store, 0x20003ee0, RAM, store, 0x20003ee4, RAM + 0, 0xd32, 0xf9893014, "adds r0, #0x14" + 0, 0xd34, 0xf9c8f000, "bl #0x10c8" + 0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM + +Please note that you need to configure QEMU with Capstone support to get disassembly. + +The output can be filtered to only track certain instructions or +addresses using the ``ifilter`` or ``afilter`` options. You can stack the +arguments if required:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so,ifilter=st1w,afilter=0x40001808 -d plugin + +This plugin can also dump registers when they change value. Specify the name of the +registers with multiple ``reg`` options. You can also use glob style matching if you wish:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so,reg=\*_el2,reg=sp -d plugin + +Be aware that each additional register to check will slow down +execution quite considerably. You can optimise the number of register +checks done by using the rdisas option. This will only instrument +instructions that mention the registers in question in disassembly. +This is not foolproof as some instructions implicitly change +instructions. You can use the ifilter to catch these cases:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so,ifilter=msr,ifilter=blr,reg=x30,reg=\*_el1,rdisas=on + +Cache Modelling +............... + +``contrib/plugins/cache.c`` + +Cache modelling plugin that measures the performance of a given L1 cache +configuration, and optionally a unified L2 per-core cache when a given working +set is run:: + + $ qemu-x86_64 -plugin ./contrib/plugins/libcache.so \ + -d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs + +will report the following:: + + core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate + 0 996695 508 0.0510% 2642799 18617 0.7044% + + address, data misses, instruction + 0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx) + 0x41f395 (_IO_default_xsputn), 49, movb %dl, (%rdi, %rax) + 0x42584d (ptmalloc_init.part.0), 33, movaps %xmm0, (%rax) + 0x454d48 (__tunables_init), 20, cmpb $0, (%r8) + ... + + address, fetch misses, instruction + 0x4160a0 (__vfprintf_internal), 744, movl $1, %ebx + 0x41f0a0 (_IO_setb), 744, endbr64 + 0x415882 (__vfprintf_internal), 744, movq %r12, %rdi + 0x4268a0 (__malloc), 696, andq $0xfffffffffffffff0, %rax + ... + +The plugin has a number of arguments, all of them are optional: + +.. list-table:: Cache modelling arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - limit=N + - Print top N icache and dcache thrashing instructions along with + their address, number of misses, and its disassembly. (default: 32) + * - icachesize=N + iblksize=B + iassoc=A + - Instruction cache configuration arguments. They specify the + cache size, block size, and associativity of the instruction + cache, respectively. (default: N = 16384, B = 64, A = 8) + * - dcachesize=N + - Data cache size (default: 16834) + * - dblksize=B + - Data cache block size (default: 64) + * - dassoc=A + - Data cache associativity (default: 8) + * - evict=POLICY + - Sets the eviction policy to POLICY. Available policies are: + ``lru``, ``fifo``, and ``rand``. The plugin will use + the specified policy for both instruction and data caches. + (default: POLICY = ``lru``) + * - cores=N + - Sets the number of cores for which we maintain separate icache + and dcache. (default: for linux-user, N = 1, for full system + emulation: N = cores available to guest) + * - l2=on + - Simulates a unified L2 cache (stores blocks for both + instructions and data) using the default L2 configuration (cache + size = 2MB, associativity = 16-way, block size = 64B). + * - l2cachesize=N + - L2 cache size (default: 2097152 (2MB)), implies ``l2=on`` + * - l2blksize=B + - L2 cache block size (default: 64), implies ``l2=on`` + * - l2assoc=A + - L2 cache associativity (default: 16), implies ``l2=on`` + +Stop on Trigger +............... + +``contrib/plugins/stoptrigger.c`` + +The stoptrigger plugin allows to setup triggers to stop emulation. +It can be used for research purposes to launch some code and precisely stop it +and understand where its execution flow went. + +Two types of triggers can be configured: a count of instructions to stop at, +or an address to stop at. Multiple triggers can be set at once. + +By default, QEMU will exit with return code 0. A custom return code can be +configured for each trigger using ``:CODE`` syntax. + +For example, to stop at the 20-th instruction with return code 41, at address +0xd4 with return code 0 or at address 0xd8 with return code 42:: + + $ qemu-system-aarch64 $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libstoptrigger.so,icount=20:41,addr=0xd4,addr=0xd8:42 -d plugin + +The plugin will log the reason of exit, for example:: + + 0xd4 reached, exiting + +Other emulation features +------------------------ + +When running system emulation you can also enable deterministic +execution which allows for repeatable record/replay debugging. See +:ref:`Record/Replay<replay>` for more details. + diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 954623f9bf..d8725c2854 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -8,38 +8,6 @@ QEMU TCG Plugins ================ -QEMU TCG plugins provide a way for users to run experiments taking -advantage of the total system control emulation can have over a guest. -It provides a mechanism for plugins to subscribe to events during -translation and execution and optionally callback into the plugin -during these events. TCG plugins are unable to change the system state -only monitor it passively. However they can do this down to an -individual instruction granularity including potentially subscribing -to all load and store operations. - -Usage ------ - -Any QEMU binary with TCG support has plugins enabled by default. -Earlier releases needed to be explicitly enabled with:: - - configure --enable-plugins - -Once built a program can be run with multiple plugins loaded each with -their own arguments:: - - $QEMU $OTHER_QEMU_ARGS \ - -plugin contrib/plugin/libhowvec.so,inline=on,count=hint \ - -plugin contrib/plugin/libhotblocks.so - -Arguments are plugin specific and can be used to modify their -behaviour. In this case the howvec plugin is being asked to use inline -ops to count and break down the hint instructions by type. - -Linux user-mode emulation also evaluates the environment variable -``QEMU_PLUGIN``:: - - QEMU_PLUGIN="file=contrib/plugins/libhowvec.so,inline=on,count=hint" $QEMU Writing plugins --------------- @@ -191,479 +159,6 @@ which means callbacks may still occur after the uninstall operation is requested. The plugin isn't completely uninstalled until the safe work has executed while all vCPUs are quiescent. -Example Plugins -=============== - -There are a number of plugins included with QEMU and you are -encouraged to contribute your own plugins plugins upstream. There is a -``contrib/plugins`` directory where they can go. There are also some -basic plugins that are used to test and exercise the API during the -``make check-tcg`` target in ``tests\plugins``. - -- tests/plugins/empty.c - -Purely a test plugin for measuring the overhead of the plugins system -itself. Does no instrumentation. - -- tests/plugins/bb.c - -A very basic plugin which will measure execution in course terms as -each basic block is executed. By default the results are shown once -execution finishes:: - - $ qemu-aarch64 -plugin tests/plugin/libbb.so \ - -d plugin ./tests/tcg/aarch64-linux-user/sha1 - SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 - bb's: 2277338, insns: 158483046 - -Behaviour can be tweaked with the following arguments: - - * inline=true|false - - Use faster inline addition of a single counter. Not per-cpu and not - thread safe. - - * idle=true|false - - Dump the current execution stats whenever the guest vCPU idles - -- tests/plugins/insn.c - -This is a basic instruction level instrumentation which can count the -number of instructions executed on each core/thread:: - - $ qemu-aarch64 -plugin tests/plugin/libinsn.so \ - -d plugin ./tests/tcg/aarch64-linux-user/threadcount - Created 10 threads - Done - cpu 0 insns: 46765 - cpu 1 insns: 3694 - cpu 2 insns: 3694 - cpu 3 insns: 2994 - cpu 4 insns: 1497 - cpu 5 insns: 1497 - cpu 6 insns: 1497 - cpu 7 insns: 1497 - total insns: 63135 - -Behaviour can be tweaked with the following arguments: - - * inline=true|false - - Use faster inline addition of a single counter. Not per-cpu and not - thread safe. - - * sizes=true|false - - Give a summary of the instruction sizes for the execution - - * match=<string> - - Only instrument instructions matching the string prefix. Will show - some basic stats including how many instructions have executed since - the last execution. For example:: - - $ qemu-aarch64 -plugin tests/plugin/libinsn.so,match=bl \ - -d plugin ./tests/tcg/aarch64-linux-user/sha512-vector - ... - 0x40069c, 'bl #0x4002b0', 10 hits, 1093 match hits, Δ+1257 since last match, 98 avg insns/match - 0x4006ac, 'bl #0x403690', 10 hits, 1094 match hits, Δ+47 since last match, 98 avg insns/match - 0x4037fc, 'bl #0x4002b0', 18 hits, 1095 match hits, Δ+22 since last match, 98 avg insns/match - 0x400720, 'bl #0x403690', 10 hits, 1096 match hits, Δ+58 since last match, 98 avg insns/match - 0x4037fc, 'bl #0x4002b0', 19 hits, 1097 match hits, Δ+22 since last match, 98 avg insns/match - 0x400730, 'bl #0x403690', 10 hits, 1098 match hits, Δ+33 since last match, 98 avg insns/match - 0x4037ac, 'bl #0x4002b0', 12 hits, 1099 match hits, Δ+20 since last match, 98 avg insns/match - ... - -For more detailed execution tracing see the ``execlog`` plugin for -other options. - -- tests/plugins/mem.c - -Basic instruction level memory instrumentation:: - - $ qemu-aarch64 -plugin tests/plugin/libmem.so,inline=true \ - -d plugin ./tests/tcg/aarch64-linux-user/sha1 - SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 - inline mem accesses: 79525013 - -Behaviour can be tweaked with the following arguments: - - * inline=true|false - - Use faster inline addition of a single counter. Not per-cpu and not - thread safe. - - * callback=true|false - - Use callbacks on each memory instrumentation. - - * hwaddr=true|false - - Count IO accesses (only for system emulation) - -- tests/plugins/syscall.c - -A basic syscall tracing plugin. This only works for user-mode. By -default it will give a summary of syscall stats at the end of the -run:: - - $ qemu-aarch64 -plugin tests/plugin/libsyscall \ - -d plugin ./tests/tcg/aarch64-linux-user/threadcount - Created 10 threads - Done - syscall no. calls errors - 226 12 0 - 99 11 11 - 115 11 0 - 222 11 0 - 93 10 0 - 220 10 0 - 233 10 0 - 215 8 0 - 214 4 0 - 134 2 0 - 64 2 0 - 96 1 0 - 94 1 0 - 80 1 0 - 261 1 0 - 78 1 0 - 160 1 0 - 135 1 0 - -- contrib/plugins/hotblocks.c - -The hotblocks plugin allows you to examine the where hot paths of -execution are in your program. Once the program has finished you will -get a sorted list of blocks reporting the starting PC, translation -count, number of instructions and execution count. This will work best -with linux-user execution as system emulation tends to generate -re-translations as blocks from different programs get swapped in and -out of system memory. - -If your program is single-threaded you can use the ``inline`` option for -slightly faster (but not thread safe) counters. - -Example:: - - $ qemu-aarch64 \ - -plugin contrib/plugins/libhotblocks.so -d plugin \ - ./tests/tcg/aarch64-linux-user/sha1 - SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 - collected 903 entries in the hash table - pc, tcount, icount, ecount - 0x0000000041ed10, 1, 5, 66087 - 0x000000004002b0, 1, 4, 66087 - ... - -- contrib/plugins/hotpages.c - -Similar to hotblocks but this time tracks memory accesses:: - - $ qemu-aarch64 \ - -plugin contrib/plugins/libhotpages.so -d plugin \ - ./tests/tcg/aarch64-linux-user/sha1 - SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 - Addr, RCPUs, Reads, WCPUs, Writes - 0x000055007fe000, 0x0001, 31747952, 0x0001, 8835161 - 0x000055007ff000, 0x0001, 29001054, 0x0001, 8780625 - 0x00005500800000, 0x0001, 687465, 0x0001, 335857 - 0x0000000048b000, 0x0001, 130594, 0x0001, 355 - 0x0000000048a000, 0x0001, 1826, 0x0001, 11 - -The hotpages plugin can be configured using the following arguments: - - * sortby=reads|writes|address - - Log the data sorted by either the number of reads, the number of writes, or - memory address. (Default: entries are sorted by the sum of reads and writes) - - * io=on - - Track IO addresses. Only relevant to full system emulation. (Default: off) - - * pagesize=N - - The page size used. (Default: N = 4096) - -- contrib/plugins/howvec.c - -This is an instruction classifier so can be used to count different -types of instructions. It has a number of options to refine which get -counted. You can give a value to the ``count`` argument for a class of -instructions to break it down fully, so for example to see all the system -registers accesses:: - - $ qemu-system-aarch64 $(QEMU_ARGS) \ - -append "root=/dev/sda2 systemd.unit=benchmark.service" \ - -smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin - -which will lead to a sorted list after the class breakdown:: - - Instruction Classes: - Class: UDEF not counted - Class: SVE (68 hits) - Class: PCrel addr (47789483 hits) - Class: Add/Sub (imm) (192817388 hits) - Class: Logical (imm) (93852565 hits) - Class: Move Wide (imm) (76398116 hits) - Class: Bitfield (44706084 hits) - Class: Extract (5499257 hits) - Class: Cond Branch (imm) (147202932 hits) - Class: Exception Gen (193581 hits) - Class: NOP not counted - Class: Hints (6652291 hits) - Class: Barriers (8001661 hits) - Class: PSTATE (1801695 hits) - Class: System Insn (6385349 hits) - Class: System Reg counted individually - Class: Branch (reg) (69497127 hits) - Class: Branch (imm) (84393665 hits) - Class: Cmp & Branch (110929659 hits) - Class: Tst & Branch (44681442 hits) - Class: AdvSimd ldstmult (736 hits) - Class: ldst excl (9098783 hits) - Class: Load Reg (lit) (87189424 hits) - Class: ldst noalloc pair (3264433 hits) - Class: ldst pair (412526434 hits) - Class: ldst reg (imm) (314734576 hits) - Class: Loads & Stores (2117774 hits) - Class: Data Proc Reg (223519077 hits) - Class: Scalar FP (31657954 hits) - Individual Instructions: - Instr: mrs x0, sp_el0 (2682661 hits) (op=0xd5384100/ System Reg) - Instr: mrs x1, tpidr_el2 (1789339 hits) (op=0xd53cd041/ System Reg) - Instr: mrs x2, tpidr_el2 (1513494 hits) (op=0xd53cd042/ System Reg) - Instr: mrs x0, tpidr_el2 (1490823 hits) (op=0xd53cd040/ System Reg) - Instr: mrs x1, sp_el0 (933793 hits) (op=0xd5384101/ System Reg) - Instr: mrs x2, sp_el0 (699516 hits) (op=0xd5384102/ System Reg) - Instr: mrs x4, tpidr_el2 (528437 hits) (op=0xd53cd044/ System Reg) - Instr: mrs x30, ttbr1_el1 (480776 hits) (op=0xd538203e/ System Reg) - Instr: msr ttbr1_el1, x30 (480713 hits) (op=0xd518203e/ System Reg) - Instr: msr vbar_el1, x30 (480671 hits) (op=0xd518c01e/ System Reg) - ... - -To find the argument shorthand for the class you need to examine the -source code of the plugin at the moment, specifically the ``*opt`` -argument in the InsnClassExecCount tables. - -- contrib/plugins/lockstep.c - -This is a debugging tool for developers who want to find out when and -where execution diverges after a subtle change to TCG code generation. -It is not an exact science and results are likely to be mixed once -asynchronous events are introduced. While the use of -icount can -introduce determinism to the execution flow it doesn't always follow -the translation sequence will be exactly the same. Typically this is -caused by a timer firing to service the GUI causing a block to end -early. However in some cases it has proved to be useful in pointing -people at roughly where execution diverges. The only argument you need -for the plugin is a path for the socket the two instances will -communicate over:: - - - $ qemu-system-sparc -monitor none -parallel none \ - -net none -M SS-20 -m 256 -kernel day11/zImage.elf \ - -plugin ./contrib/plugins/liblockstep.so,sockpath=lockstep-sparc.sock \ - -d plugin,nochain - -which will eventually report:: - - qemu-system-sparc: warning: nic lance.0 has no peer - @ 0x000000ffd06678 vs 0x000000ffd001e0 (2/1 since last) - @ 0x000000ffd07d9c vs 0x000000ffd06678 (3/1 since last) - Δ insn_count @ 0x000000ffd07d9c (809900609) vs 0x000000ffd06678 (809900612) - previously @ 0x000000ffd06678/10 (809900609 insns) - previously @ 0x000000ffd001e0/4 (809900599 insns) - previously @ 0x000000ffd080ac/2 (809900595 insns) - previously @ 0x000000ffd08098/5 (809900593 insns) - previously @ 0x000000ffd080c0/1 (809900588 insns) - -- contrib/plugins/hwprofile.c - -The hwprofile tool can only be used with system emulation and allows -the user to see what hardware is accessed how often. It has a number of options: - - * track=read or track=write - - By default the plugin tracks both reads and writes. You can use one - of these options to limit the tracking to just one class of accesses. - - * source - - Will include a detailed break down of what the guest PC that made the - access was. Not compatible with the pattern option. Example output:: - - cirrus-low-memory @ 0xfffffd00000a0000 - pc:fffffc0000005cdc, 1, 256 - pc:fffffc0000005ce8, 1, 256 - pc:fffffc0000005cec, 1, 256 - - * pattern - - Instead break down the accesses based on the offset into the HW - region. This can be useful for seeing the most used registers of a - device. Example output:: - - pci0-conf @ 0xfffffd01fe000000 - off:00000004, 1, 1 - off:00000010, 1, 3 - off:00000014, 1, 3 - off:00000018, 1, 2 - off:0000001c, 1, 2 - off:00000020, 1, 2 - ... - -- contrib/plugins/execlog.c - -The execlog tool traces executed instructions with memory access. It can be used -for debugging and security analysis purposes. -Please be aware that this will generate a lot of output. - -The plugin needs default argument:: - - $ qemu-system-arm $(QEMU_ARGS) \ - -plugin ./contrib/plugins/libexeclog.so -d plugin - -which will output an execution trace following this structure:: - - # vCPU, vAddr, opcode, disassembly[, load/store, memory addr, device]... - 0, 0xa12, 0xf8012400, "movs r4, #0" - 0, 0xa14, 0xf87f42b4, "cmp r4, r6" - 0, 0xa16, 0xd206, "bhs #0xa26" - 0, 0xa18, 0xfff94803, "ldr r0, [pc, #0xc]", load, 0x00010a28, RAM - 0, 0xa1a, 0xf989f000, "bl #0xd30" - 0, 0xd30, 0xfff9b510, "push {r4, lr}", store, 0x20003ee0, RAM, store, 0x20003ee4, RAM - 0, 0xd32, 0xf9893014, "adds r0, #0x14" - 0, 0xd34, 0xf9c8f000, "bl #0x10c8" - 0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM - -Please note that you need to configure QEMU with Capstone support to get disassembly. - -The output can be filtered to only track certain instructions or -addresses using the ``ifilter`` or ``afilter`` options. You can stack the -arguments if required:: - - $ qemu-system-arm $(QEMU_ARGS) \ - -plugin ./contrib/plugins/libexeclog.so,ifilter=st1w,afilter=0x40001808 -d plugin - -This plugin can also dump registers when they change value. Specify the name of the -registers with multiple ``reg`` options. You can also use glob style matching if you wish:: - - $ qemu-system-arm $(QEMU_ARGS) \ - -plugin ./contrib/plugins/libexeclog.so,reg=\*_el2,reg=sp -d plugin - -Be aware that each additional register to check will slow down -execution quite considerably. You can optimise the number of register -checks done by using the rdisas option. This will only instrument -instructions that mention the registers in question in disassembly. -This is not foolproof as some instructions implicitly change -instructions. You can use the ifilter to catch these cases: - - $ qemu-system-arm $(QEMU_ARGS) \ - -plugin ./contrib/plugins/libexeclog.so,ifilter=msr,ifilter=blr,reg=x30,reg=\*_el1,rdisas=on - -- contrib/plugins/cache.c - -Cache modelling plugin that measures the performance of a given L1 cache -configuration, and optionally a unified L2 per-core cache when a given working -set is run:: - - $ qemu-x86_64 -plugin ./contrib/plugins/libcache.so \ - -d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs - -will report the following:: - - core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate - 0 996695 508 0.0510% 2642799 18617 0.7044% - - address, data misses, instruction - 0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx) - 0x41f395 (_IO_default_xsputn), 49, movb %dl, (%rdi, %rax) - 0x42584d (ptmalloc_init.part.0), 33, movaps %xmm0, (%rax) - 0x454d48 (__tunables_init), 20, cmpb $0, (%r8) - ... - - address, fetch misses, instruction - 0x4160a0 (__vfprintf_internal), 744, movl $1, %ebx - 0x41f0a0 (_IO_setb), 744, endbr64 - 0x415882 (__vfprintf_internal), 744, movq %r12, %rdi - 0x4268a0 (__malloc), 696, andq $0xfffffffffffffff0, %rax - ... - -The plugin has a number of arguments, all of them are optional: - - * limit=N - - Print top N icache and dcache thrashing instructions along with their - address, number of misses, and its disassembly. (default: 32) - - * icachesize=N - * iblksize=B - * iassoc=A - - Instruction cache configuration arguments. They specify the cache size, block - size, and associativity of the instruction cache, respectively. - (default: N = 16384, B = 64, A = 8) - - * dcachesize=N - * dblksize=B - * dassoc=A - - Data cache configuration arguments. They specify the cache size, block size, - and associativity of the data cache, respectively. - (default: N = 16384, B = 64, A = 8) - - * evict=POLICY - - Sets the eviction policy to POLICY. Available policies are: :code:`lru`, - :code:`fifo`, and :code:`rand`. The plugin will use the specified policy for - both instruction and data caches. (default: POLICY = :code:`lru`) - - * cores=N - - Sets the number of cores for which we maintain separate icache and dcache. - (default: for linux-user, N = 1, for full system emulation: N = cores - available to guest) - - * l2=on - - Simulates a unified L2 cache (stores blocks for both instructions and data) - using the default L2 configuration (cache size = 2MB, associativity = 16-way, - block size = 64B). - - * l2cachesize=N - * l2blksize=B - * l2assoc=A - - L2 cache configuration arguments. They specify the cache size, block size, and - associativity of the L2 cache, respectively. Setting any of the L2 - configuration arguments implies ``l2=on``. - (default: N = 2097152 (2MB), B = 64, A = 16) - -- contrib/plugins/stoptrigger.c - -The stoptrigger plugin allows to setup triggers to stop emulation. -It can be used for research purposes to launch some code and precisely stop it -and understand where its execution flow went. - -Two types of triggers can be configured: a count of instructions to stop at, -or an address to stop at. Multiple triggers can be set at once. - -By default, QEMU will exit with return code 0. A custom return code can be -configured for each trigger using ``:CODE`` syntax. - -For example, to stop at the 20-th instruction with return code 41, at address -0xd4 with return code 0 or at address 0xd8 with return code 42:: - - $ qemu-system-aarch64 $(QEMU_ARGS) \ - -plugin ./contrib/plugins/libstoptrigger.so,icount=20:41,addr=0xd4,addr=0xd8:42 -d plugin - -The plugin will log the reason of exit, for example:: - - 0xd4 reached, exiting - Plugin API ========== From 33ef9cdc289480bbc87fa3ee74c1398848f4e38c Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 29 Jul 2024 15:44:11 +0100 Subject: [PATCH 2616/2884] contrib/plugins/cache.c: Remove redundant check of l2_access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In append_stats_line(), we have an expression l2_access ? l2_miss_rate : 0.0 But this is inside an if (l2_access && l2_misses) { ... } block, so Coverity points out that the false part of the ?: is dead code. Remove the unnecessary test. Resolves: Coverity CID 1522458 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240725164851.1930964-1-peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-12-alex.bennee@linaro.org> --- contrib/plugins/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index c5c8ac75a9..512ef6776b 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -558,7 +558,7 @@ static void append_stats_line(GString *line, " %-12" PRIu64 " %-11" PRIu64 " %10.4lf%%", l2_access, l2_misses, - l2_access ? l2_miss_rate : 0.0); + l2_miss_rate); } g_string_append(line, "\n"); From f0220d747a8b437d043d9344774cbeeb0b31be25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:12 +0100 Subject: [PATCH 2617/2884] contrib/plugins: be more vocal building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the conversion to meson and removing the old QEMU Makefile baggage we became very silent when building the plugins. Bring in a copy of the quiet-command logic (and some magic COMMAs) so we can at least assure developers we are building them. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2457 Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-13-alex.bennee@linaro.org> --- contrib/plugins/Makefile | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 98a89d5c40..edf256cd9d 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -39,26 +39,41 @@ endif SONAMES := $(addsuffix $(SO_SUFFIX),$(addprefix lib,$(NAMES))) -# The main QEMU uses Glib extensively so it's perfectly fine to use it +# The main QEMU uses Glib extensively so it is perfectly fine to use it # in plugins (which many example do). PLUGIN_CFLAGS := $(shell $(PKG_CONFIG) --cflags glib-2.0) PLUGIN_CFLAGS += -fPIC -Wall PLUGIN_CFLAGS += -I$(TOP_SRC_PATH)/include/qemu +# Helper that honours V=1 so we get some output when compiling +quiet-@ = $(if $(V),,@$(if $1,printf " %-7s %s\n" "$(strip $1)" "$(strip $2)" && )) +quiet-command = $(call quiet-@,$2,$3)$1 + +# for including , in command strings +COMMA := , + all: $(SONAMES) %.o: %.c - $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $< + $(call quiet-command, \ + $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $<, \ + BUILD, plugin $@) ifeq ($(CONFIG_WIN32),y) lib%$(SO_SUFFIX): %.o win32_linker.o ../../plugins/libqemu_plugin_api.a - $(CC) -shared -o $@ $^ $(LDLIBS) + $(call quiet-command, \ + $(CC) -shared -o $@ $^ $(LDLIBS), \ + LINK, plugin $@) else ifeq ($(CONFIG_DARWIN),y) lib%$(SO_SUFFIX): %.o - $(CC) -bundle -Wl,-undefined,dynamic_lookup -o $@ $^ $(LDLIBS) + $(call quiet-command, \ + $(CC) -bundle -Wl$(COMMA)-undefined$(COMMA)dynamic_lookup -o $@ $^ $(LDLIBS), \ + LINK, plugin $@) else lib%$(SO_SUFFIX): %.o - $(CC) -shared -o $@ $^ $(LDLIBS) + $(call quiet-command, \ + $(CC) -shared -o $@ $^ $(LDLIBS), \ + LINK, plugin $@) endif From 44e794896759236885f6d30d1f6b9b8b76355d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:13 +0100 Subject: [PATCH 2618/2884] contrib/plugins: add compat for g_memdup2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were premature if bumping this because some of our builds are still on older glibs. Just copy the compat handler for now and we can remove it later. Fixes: ee293103b0 (plugins: update lockstep to use g_memdup2) Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2161 Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-14-alex.bennee@linaro.org> --- contrib/plugins/lockstep.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index 6a7e9bbb39..62981d4e09 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -101,6 +101,31 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) plugin_cleanup(id); } +/* + * g_memdup has been deprecated in Glib since 2.68 and + * will complain about it if you try to use it. However until + * glib_req_ver for QEMU is bumped we make a copy of the glib-compat + * handler. + */ +static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size) +{ +#if GLIB_CHECK_VERSION(2, 68, 0) + return g_memdup2(mem, byte_size); +#else + gpointer new_mem; + + if (mem && byte_size != 0) { + new_mem = g_malloc(byte_size); + memcpy(new_mem, mem, byte_size); + } else { + new_mem = NULL; + } + + return new_mem; +#endif +} +#define g_memdup2(m, s) g_memdup2_qemu(m, s) + static void report_divergance(ExecState *us, ExecState *them) { DivergeState divrec = { log, 0 }; From 7b690fd3d039211a5bdde6a74b0ff95cb8b872b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 29 Jul 2024 15:44:14 +0100 Subject: [PATCH 2619/2884] plugin/loader: handle basic help query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the list of options isn't fixed we do all the parsing by hand. Without any named arguments we automatically fill the "file" option with the value give so check if it is requesting help and dump some basic usage text. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240729144414.830369-15-alex.bennee@linaro.org> --- plugins/loader.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/loader.c b/plugins/loader.c index 513a429c57..ebc01da9c6 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/config-file.h" +#include "qemu/help_option.h" #include "qapi/error.h" #include "qemu/lockable.h" #include "qemu/option.h" @@ -98,7 +99,12 @@ static int plugin_add(void *opaque, const char *name, const char *value, bool is_on; char *fullarg; - if (strcmp(name, "file") == 0) { + if (is_help_option(value)) { + printf("Plugin options\n"); + printf(" file=<path/to/plugin.so>\n"); + printf(" plugin specific arguments\n"); + exit(0); + } else if (strcmp(name, "file") == 0) { if (strcmp(value, "") == 0) { error_setg(errp, "requires a non-empty argument"); return 1; From d4181658dfb66b89e304077e89d8d173b1201ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <thomas.weissschuh@linutronix.de> Date: Wed, 24 Jul 2024 14:45:53 +0200 Subject: [PATCH 2620/2884] docs: add test for firmware.json QAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make sure that the QAPI description stays valid, add a testcase. Suggested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/d9ce0234-4beb-4b90-b14c-76810d3b81d7@linaro.org/ Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240724-qapi-firmware-json-v7-1-12341f7e362d@linutronix.de> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/meson.build b/docs/meson.build index 9040f860ae..322452c877 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -99,3 +99,8 @@ if build_docs alias_target('html', sphinxdocs) alias_target('man', sphinxmans) endif + +test('QAPI firmware.json regression tests', qapi_gen, + args: ['-o', meson.current_build_dir() / 'qapi', + meson.current_source_dir() / 'interop/firmware.json'], + suite: ['qapi-schema', 'qapi-interop']) From 48e5b5f994bccf161dd88a67fdd819d4bfb400f1 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 29 Jul 2024 13:05:33 +0100 Subject: [PATCH 2621/2884] docs/sphinx/depfile.py: Handle env.doc2path() returning a Path not a str MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In newer versions of Sphinx the env.doc2path() API is going to change to return a Path object rather than a str. This was originally visible in Sphinx 8.0.0rc1, but has been rolled back for the final 8.0.0 release. However it will probably emit a deprecation warning and is likely to change for good in 9.0: https://github.com/sphinx-doc/sphinx/issues/12686 Our use in depfile.py assumes a str, and if it is passed a Path it will fall over: Handler <function write_depfile at 0x77a1775ff560> for event 'build-finished' threw an exception (exception: unsupported operand type(s) for +: 'PosixPath' and 'str') Wrapping the env.doc2path() call in str() will coerce a Path object to the str we expect, and have no effect in older Sphinx versions that do return a str. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2458 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240729120533.2486427-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/sphinx/depfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index afdcbcec6e..e74be6af98 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -19,7 +19,7 @@ __version__ = '1.0' def get_infiles(env): for x in env.found_docs: - yield env.doc2path(x) + yield str(env.doc2path(x)) yield from ((os.path.join(env.srcdir, dep) for dep in env.dependencies[x])) for mod in sys.modules.values(): From b8f5932dda01e9ad5b392606b16f3162d0801a21 Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Fri, 26 Jul 2024 09:44:26 -0400 Subject: [PATCH 2622/2884] tests/avocado: mips: fallback to HTTP given certificate expiration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SSL certificate installed at mipsdistros.mips.com has expired: 0 s:CN = mipsdistros.mips.com i:C = US, O = Amazon, OU = Server CA 1B, CN = Amazon a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Dec 23 00:00:00 2019 GMT; NotAfter: Jan 23 12:00:00 2021 GMT Because this project has no control over that certificate and host, this falls back to plain HTTP instead. The integrity of the downloaded files can be guaranteed by the existing hashes for those files (which are not modified here). Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Cleber Rosa <crosa@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240726134438.14720-2-crosa@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tests/avocado/boot_linux_console.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index c35fc5e9ba..450d67be6a 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -299,7 +299,7 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=endian:little :avocado: tags=cpu:I7200 """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' + kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page4k.xz') kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' @@ -312,7 +312,7 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=endian:little :avocado: tags=cpu:I7200 """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' + kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page16k_up.xz') kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' @@ -325,7 +325,7 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=endian:little :avocado: tags=cpu:I7200 """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' + kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page64k_dbg.xz') kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' From 085ea2b868ab02536320f11a7bc2326d8c26dcd3 Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Fri, 26 Jul 2024 09:44:27 -0400 Subject: [PATCH 2623/2884] tests/avocado: mips: add hint for fetchasset plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avocado's fetchasset plugin runs before the actual Avocado job (and any test). It analyses the test's code looking for occurrences of "self.fetch_asset()" in the either the actual test or setUp() method. It's not able to fully analyze all code, though. The way these tests are written, make the fetchasset plugin blind to the assets. This adds some more code duplication, true, but it will aid the fetchasset plugin to download or verify the existence of these assets in advance. Signed-off-by: Cleber Rosa <crosa@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240726134438.14720-3-crosa@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tests/avocado/boot_linux_console.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 450d67be6a..b8b0a4df10 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -274,8 +274,7 @@ class BootLinuxConsole(LinuxKernelTest): # Wait for VM to shut down gracefully self.vm.wait() - def do_test_mips_malta32el_nanomips(self, kernel_url, kernel_hash): - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + def do_test_mips_malta32el_nanomips(self, kernel_path_xz): kernel_path = self.workdir + "kernel" with lzma.open(kernel_path_xz, 'rb') as f_in: with open(kernel_path, 'wb') as f_out: @@ -303,7 +302,8 @@ class BootLinuxConsole(LinuxKernelTest): 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page4k.xz') kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' - self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) + kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.do_test_mips_malta32el_nanomips(kernel_path_xz) def test_mips_malta32el_nanomips_16k_up(self): """ @@ -316,7 +316,8 @@ class BootLinuxConsole(LinuxKernelTest): 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page16k_up.xz') kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' - self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) + kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.do_test_mips_malta32el_nanomips(kernel_path_xz) def test_mips_malta32el_nanomips_64k_dbg(self): """ @@ -329,7 +330,8 @@ class BootLinuxConsole(LinuxKernelTest): 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page64k_dbg.xz') kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' - self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) + kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.do_test_mips_malta32el_nanomips(kernel_path_xz) def test_aarch64_xlnx_versal_virt(self): """ From 4ec88f164067c399ef2296c353209f9e49166b1a Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Fri, 26 Jul 2024 09:44:33 -0400 Subject: [PATCH 2624/2884] tests/avocado: test_arm_emcraft_sf2: handle RW requirements for asset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The asset used in the mentioned test gets truncated before it's used in the test. This means that the file gets modified, and thus the asset's expected hash doesn't match anymore. This causes cache misses and re-downloads every time the test is re-run. Let's make a copy of the asset so that the one in the cache is preserved and the cache sees a hit on re-runs. Signed-off-by: Cleber Rosa <crosa@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240726134438.14720-9-crosa@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tests/avocado/boot_linux_console.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index b8b0a4df10..2929aa042d 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -401,14 +401,16 @@ class BootLinuxConsole(LinuxKernelTest): 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin') spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501' spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) + spi_path_rw = os.path.join(self.workdir, os.path.basename(spi_path)) + shutil.copy(spi_path, spi_path_rw) - file_truncate(spi_path, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB + file_truncate(spi_path_rw, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE self.vm.add_args('-kernel', uboot_path, '-append', kernel_command_line, - '-drive', 'file=' + spi_path + ',if=mtd,format=raw', + '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw', '-no-reboot') self.vm.launch() self.wait_for_console_pattern('Enter \'help\' for a list') From 7b60b971cc2f52ed2a69006a2ad709df2831cd67 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 30 Jul 2024 10:59:39 +0100 Subject: [PATCH 2625/2884] osdep.h: Clean up no-longer-needed back-compat for macOS 10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our official support policy only supports the most recent two versions of macOS (currently macOS 13 Ventura and macOS 14 Sonoma), and we already have code that assumes at least macOS 12 Monterey or better. In commit 2d27c91e2b72ac7 we dropped some of the back-compat code for older macOS versions, but missed the guard in osdep.h that is providing a fallback for macOS 10 and earlier. Simplify the ifdef to the "ifdef __APPLE__" that we use elsewhere for "is this macOS?". Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240730095939.2781172-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- include/qemu/osdep.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 191916f38e..720ed21a7e 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -786,8 +786,7 @@ size_t qemu_get_host_physmem(void); * Toggle write/execute on the pages marked MAP_JIT * for the current thread. */ -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 +#ifdef __APPLE__ static inline void qemu_thread_jit_execute(void) { pthread_jit_write_protect_np(true); From 4e56e89d6c81589cc47cf5811f570c67889bd18a Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Wed, 31 Jul 2024 16:21:21 +1000 Subject: [PATCH 2626/2884] Update version for v9.1.0-rc0 release Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index bc66ba6669..ef7c56c155 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.50 +9.0.90 From c4afcec90f117e703666e2436592cc4e825ef2a1 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 29 Jul 2024 15:12:44 +1000 Subject: [PATCH 2627/2884] tests/vm/openbsd: Install tomli OpenBSD still defaults to python 3.10, therefore tomli is now required by configure. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240729051244.436851-1-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- tests/vm/openbsd | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 5e646f7c51..49cab08782 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -32,6 +32,7 @@ class OpenBSDVM(basevm.BaseVM): "pkgconf", "bzip2", "xz", "ninja", + "py3-tomli", # gnu tools "bash", From 39635ccd0b4935ecbf184cf4544fce92d5827de2 Mon Sep 17 00:00:00 2001 From: Xiong Zhang <xiong.y.zhang@linux.intel.com> Date: Tue, 30 Jul 2024 16:29:27 +0800 Subject: [PATCH 2628/2884] target/i386: Change unavail from u32 to u64 The feature word 'r' is a u64, and "unavail" is a u32, the operation 'r &= ~unavail' clears the high 32 bits of 'r'. This causes many vmx cases in kvm-unit-tests to fail. Changing 'unavail' from u32 to u64 fixes this issue. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2442 Fixes: 0b2757412cb1 ("target/i386: drop AMD machine check bits from Intel CPUID") Signed-off-by: Xiong Zhang <xiong.y.zhang@linux.intel.com> Link: https://lore.kernel.org/r/20240730082927.250180-1-xiong.y.zhang@linux.intel.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4688d140c2..ef06da54c6 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6039,7 +6039,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) { FeatureWordInfo *wi = &feature_word_info[w]; uint64_t r = 0; - uint32_t unavail = 0; + uint64_t unavail = 0; if (kvm_enabled()) { switch (wi->type) { From eee194dd7120f62896d5fbffeba8ab2ec39a09da Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Tue, 30 Jul 2024 12:55:41 +0800 Subject: [PATCH 2629/2884] target/i386/cpu: Remove unnecessary SGX feature words checks CPUID.0x7.0.ebx and CPUID.0x7.0.ecx leaves have been expressed as the feature word lists, and the Host capability support has been checked in x86_cpu_filter_features(). Therefore, such checks on SGX feature "words" are redundant, and the follow-up adjustments to those feature "words" will not actually take effect. Remove unnecessary SGX feature words related checks. Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Link: https://lore.kernel.org/r/20240730045544.2516284-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/cpu.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ef06da54c6..a9535284aa 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6537,8 +6537,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 7: /* Structured Extended Feature Flags Enumeration Leaf */ if (count == 0) { - uint32_t eax_0_unused, ebx_0, ecx_0, edx_0_unused; - /* Maximum ECX value for sub-leaves */ *eax = env->cpuid_level_func7; *ebx = env->features[FEAT_7_0_EBX]; /* Feature flags */ @@ -6548,20 +6546,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ - /* - * SGX cannot be emulated in software. If hardware does not - * support enabling SGX and/or SGX flexible launch control, - * then we need to update the VM's CPUID values accordingly. - */ - x86_cpu_get_supported_cpuid(0x7, 0, - &eax_0_unused, &ebx_0, - &ecx_0, &edx_0_unused); - if ((*ebx & CPUID_7_0_EBX_SGX) && !(ebx_0 & CPUID_7_0_EBX_SGX)) { - *ebx &= ~CPUID_7_0_EBX_SGX; - } - if ((*ecx & CPUID_7_0_ECX_SGX_LC) - && (!(*ebx & CPUID_7_0_EBX_SGX) || !(ecx_0 & CPUID_7_0_ECX_SGX_LC))) { + && (!(*ebx & CPUID_7_0_EBX_SGX))) { *ecx &= ~CPUID_7_0_ECX_SGX_LC; } } else if (count == 1) { From 4912d6990b806177733efc1365110cd2d92513fa Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Tue, 30 Jul 2024 12:55:42 +0800 Subject: [PATCH 2630/2884] target/i386/cpu: Explicitly express SGX_LC and SGX feature words dependency At present, cpu_x86_cpuid() silently masks off SGX_LC if SGX is absent. This is not proper because the user is not told about the dependency between the two. So explicitly define the dependency between SGX_LC and SGX feature words, so that user could get a warning when SGX_LC is enabled but SGX is absent. Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Link: https://lore.kernel.org/r/20240730045544.2516284-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/cpu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a9535284aa..e864f55d4f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1730,6 +1730,10 @@ static FeatureDep feature_dependencies[] = { .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_WRMSRNS }, .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_SGX_LC }, + }, }; typedef struct X86RegisterInfo32 { @@ -6545,11 +6549,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= CPUID_7_0_ECX_OSPKE; } *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ - - if ((*ecx & CPUID_7_0_ECX_SGX_LC) - && (!(*ebx & CPUID_7_0_EBX_SGX))) { - *ecx &= ~CPUID_7_0_ECX_SGX_LC; - } } else if (count == 1) { *eax = env->features[FEAT_7_1_EAX]; *edx = env->features[FEAT_7_1_EDX]; From 3722a98948d4fedec7a8c4575f520b346b6bc923 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Tue, 30 Jul 2024 12:55:43 +0800 Subject: [PATCH 2631/2884] target/i386/cpu: Add dependencies of CPUID 0x12 leaves As SDM stated, CPUID 0x12 leaves depend on CPUID_7_0_EBX_SGX (SGX feature word). Since FEAT_SGX_12_0_EAX, FEAT_SGX_12_0_EBX and FEAT_SGX_12_1_EAX define multiple feature words, add the dependencies of those registers to report the warning to user if SGX is absent. Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Link: https://lore.kernel.org/r/20240730045544.2516284-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/cpu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e864f55d4f..28b46ef536 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1734,6 +1734,18 @@ static FeatureDep feature_dependencies[] = { .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_SGX_LC }, }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_SGX_12_0_EAX, ~0ull }, + }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_SGX_12_0_EBX, ~0ull }, + }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_SGX_12_1_EAX, ~0ull }, + }, }; typedef struct X86RegisterInfo32 { From ada1f3cab32a4eded6a453c2e22fc897009da555 Mon Sep 17 00:00:00 2001 From: Zhao Liu <zhao1.liu@intel.com> Date: Tue, 30 Jul 2024 12:55:44 +0800 Subject: [PATCH 2632/2884] target/i386/cpu: Mask off SGX/SGX_LC feature words for non-PC machine Only PC machine supports SGX, so mask off SGX related feature words for non-PC machine (microvm). Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Link: https://lore.kernel.org/r/20240730045544.2516284-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- hw/i386/sgx-stub.c | 5 +++++ hw/i386/sgx.c | 8 ++++++++ include/hw/i386/sgx-epc.h | 1 + target/i386/cpu.c | 15 +++++++++++++++ 4 files changed, 29 insertions(+) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 16b1dfd90b..38ff75e9f3 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -32,6 +32,11 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState)); } +bool check_sgx_support(void) +{ + return false; +} + bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { return true; diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 849472a128..4900dd414a 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -266,6 +266,14 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) size); } +bool check_sgx_support(void) +{ + if (!object_dynamic_cast(qdev_get_machine(), TYPE_PC_MACHINE)) { + return false; + } + return true; +} + bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { PCMachineState *pcms = diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 3e00efd870..41d55da479 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -58,6 +58,7 @@ typedef struct SGXEPCState { int nr_sections; } SGXEPCState; +bool check_sgx_support(void); bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); void sgx_epc_build_srat(GArray *table_data); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 28b46ef536..85ef7452c0 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6103,6 +6103,21 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) } break; + case FEAT_7_0_EBX: +#ifndef CONFIG_USER_ONLY + if (!check_sgx_support()) { + unavail = CPUID_7_0_EBX_SGX; + } +#endif + break; + case FEAT_7_0_ECX: +#ifndef CONFIG_USER_ONLY + if (!check_sgx_support()) { + unavail = CPUID_7_0_ECX_SGX_LC; + } +#endif + break; + default: break; } From 5997fbdfaca3e69062c2b706d6228c7c6e133c03 Mon Sep 17 00:00:00 2001 From: Anthony Harivel <aharivel@redhat.com> Date: Fri, 26 Jul 2024 12:26:31 +0200 Subject: [PATCH 2633/2884] target/i386: Fix typo that assign same value twice Should fix: CID 1558553 Signed-off-by: Anthony Harivel <aharivel@redhat.com> Link: https://lore.kernel.org/r/20240726102632.1324432-2-aharivel@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/kvm/kvm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index b4aab9a410..31f149c990 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2694,8 +2694,8 @@ static void *kvm_msr_energy_thread(void *data) while (true) { /* Get all qemu threads id */ - g_autofree pid_t *thread_ids = - thread_ids = vmsr_get_thread_ids(vmsr->pid, &num_threads); + g_autofree pid_t *thread_ids + = vmsr_get_thread_ids(vmsr->pid, &num_threads); if (thread_ids == NULL) { goto clean; From 6e623af30130fce6e94c717176f3e3c9f2742b7d Mon Sep 17 00:00:00 2001 From: Anthony Harivel <aharivel@redhat.com> Date: Fri, 26 Jul 2024 12:26:32 +0200 Subject: [PATCH 2634/2884] target/i386: Clean up error cases for vmsr_read_thread_stat() Fix leaking memory of file handle in case of error Erase unused "pid = -1" Add clearer error_report Should fix Coverity CID 1558557. Signed-off-by: Anthony Harivel <aharivel@redhat.com> Link: https://lore.kernel.org/r/20240726102632.1324432-3-aharivel@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/kvm/vmsr_energy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index a1d78f2f2a..7e064c5aef 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -270,7 +270,7 @@ void vmsr_read_thread_stat(pid_t pid, FILE *file = fopen(path, "r"); if (file == NULL) { - pid = -1; + error_report("Error opening %s", path_name); return; } @@ -279,7 +279,8 @@ void vmsr_read_thread_stat(pid_t pid, " %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*u %*u %u", utime, stime, cpu_id) != 3) { - pid = -1; + fclose(file); + error_report("Error fscanf did not report the right amount of items"); return; } From 768a28394c9412fe1cfdf48509713fd11779a658 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 30 Jul 2024 17:55:37 +0200 Subject: [PATCH 2635/2884] qemu-vmsr-helper: fix socket loop breakage Between v5 and v6 of the series, the socket loop of qemu-vmsr-helper was changed to allow sending multiple requests on the same socket. Unfortunately, the condition of the while loop is botched and the loop will never be entered. Clean it up, and also unify the handling of error reporting. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- tools/i386/qemu-vmsr-helper.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c index ebf562c3ff..585eaf88b3 100644 --- a/tools/i386/qemu-vmsr-helper.c +++ b/tools/i386/qemu-vmsr-helper.c @@ -227,19 +227,17 @@ static void coroutine_fn vh_co_entry(void *opaque) &peer_pid, &local_err); if (r < 0) { - error_report_err(local_err); goto out; } - while (r < 0) { + for (;;) { /* * Read the requested MSR * Only RAPL MSR in rapl-msr-index.h is allowed */ - r = qio_channel_read_all(QIO_CHANNEL(client->ioc), - (char *) &request, sizeof(request), &local_err); - if (r < 0) { - error_report_err(local_err); + r = qio_channel_read_all_eof(QIO_CHANNEL(client->ioc), + (char *) &request, sizeof(request), &local_err); + if (r <= 0) { break; } @@ -261,11 +259,15 @@ static void coroutine_fn vh_co_entry(void *opaque) sizeof(vmsr), &local_err); if (r < 0) { - error_report_err(local_err); break; } } + out: + if (local_err) { + error_report_err(local_err); + } + object_unref(OBJECT(client->ioc)); g_free(client); } From 2a99c2ba822ef9758d739ffdefbe6252520c1719 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 30 Jul 2024 18:00:01 +0200 Subject: [PATCH 2636/2884] qemu-vmsr-helper: implement --verbose/-v Similar to qemu-pr-helper, do not print errors from the socket handling loop unless a --verbose or -v option is provided explicitly on the command line. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- tools/i386/qemu-vmsr-helper.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c index 585eaf88b3..a35dcb88a3 100644 --- a/tools/i386/qemu-vmsr-helper.c +++ b/tools/i386/qemu-vmsr-helper.c @@ -54,6 +54,7 @@ static enum { RUNNING, TERMINATE, TERMINATING } state; static QIOChannelSocket *server_ioc; static int server_watch; static int num_active_sockets = 1; +static bool verbose; #ifdef CONFIG_LIBCAP_NG static int uid = -1; @@ -265,7 +266,11 @@ static void coroutine_fn vh_co_entry(void *opaque) out: if (local_err) { - error_report_err(local_err); + if (!verbose) { + error_free(local_err); + } else { + error_report_err(local_err); + } } object_unref(OBJECT(client->ioc)); @@ -431,6 +436,9 @@ int main(int argc, char **argv) case 'd': daemonize = true; break; + case 'v': + verbose = true; + break; case 'T': trace_opt_parse(optarg); break; From 024d046bf41b5256adec671085bcee767a6da125 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Wed, 24 Jul 2024 06:48:59 -0400 Subject: [PATCH 2637/2884] virtio-rng: block max-bytes=0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit with max-bytes set to 0, quota is 0 and so device does not work. block this to avoid user confusion Message-Id: <73a89a42d82ec8b47358f25119b87063e4a6ea57.1721818306.git.mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/virtio/virtio-rng.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index f74efffef7..7cf31da071 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -184,8 +184,9 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) /* Workaround: Property parsing does not enforce unsigned integers, * So this is a hack to reject such numbers. */ - if (vrng->conf.max_bytes > INT64_MAX) { - error_setg(errp, "'max-bytes' parameter must be non-negative, " + if (vrng->conf.max_bytes == 0 || + vrng->conf.max_bytes > INT64_MAX) { + error_setg(errp, "'max-bytes' parameter must be positive, " "and less than 2^63"); return; } From e57030e8dc071424b0fc35c0e0cf2898e53d5e81 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:16 -0400 Subject: [PATCH 2638/2884] Revert "docs: Document composable SR-IOV device" This reverts commit d6f40c95b35bd380340b698e4306704fe22a5d68. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- MAINTAINERS | 1 - docs/system/index.rst | 1 - docs/system/sriov.rst | 36 ------------------------------------ 3 files changed, 38 deletions(-) delete mode 100644 docs/system/sriov.rst diff --git a/MAINTAINERS b/MAINTAINERS index 72b3c67360..e34c2bd4cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2011,7 +2011,6 @@ F: hw/pci-bridge/* F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* -F: docs/system/sriov.rst PCIE DOE M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw> diff --git a/docs/system/index.rst b/docs/system/index.rst index 718e9d3c56..c21065e519 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -39,4 +39,3 @@ or Hypervisor.Framework. multi-process confidential-guest-support vm-templating - sriov diff --git a/docs/system/sriov.rst b/docs/system/sriov.rst deleted file mode 100644 index a851a66a4b..0000000000 --- a/docs/system/sriov.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0-or-later - -Compsable SR-IOV device -======================= - -SR-IOV (Single Root I/O Virtualization) is an optional extended capability of a -PCI Express device. It allows a single physical function (PF) to appear as -multiple virtual functions (VFs) for the main purpose of eliminating software -overhead in I/O from virtual machines. - -There are devices with predefined SR-IOV configurations, but it is also possible -to compose an SR-IOV device yourself. Composing an SR-IOV device is currently -only supported by virtio-net-pci. - -Users can configure an SR-IOV-capable virtio-net device by adding -virtio-net-pci functions to a bus. Below is a command line example: - -.. code-block:: shell - - -netdev user,id=n -netdev user,id=o - -netdev user,id=p -netdev user,id=q - -device pcie-root-port,id=b - -device virtio-net-pci,bus=b,addr=0x0.0x3,netdev=q,sriov-pf=f - -device virtio-net-pci,bus=b,addr=0x0.0x2,netdev=p,sriov-pf=f - -device virtio-net-pci,bus=b,addr=0x0.0x1,netdev=o,sriov-pf=f - -device virtio-net-pci,bus=b,addr=0x0.0x0,netdev=n,id=f - -The VFs specify the paired PF with ``sriov-pf`` property. The PF must be -added after all VFs. It is the user's responsibility to ensure that VFs have -function numbers larger than one of the PF, and that the function numbers -have a consistent stride. - -You may also need to perform additional steps to activate the SR-IOV feature on -your guest. For Linux, refer to [1]_. - -.. [1] https://docs.kernel.org/PCI/pci-iov-howto.html From cc91ac0a7240dea713acc802d0c304163a1c15c6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:17 -0400 Subject: [PATCH 2639/2884] Revert "virtio-net: Implement SR-IOV VF" This reverts commit c2d6db6a1f39780b24538440091893f9fbe060a7. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-net-pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index dba4987d6e..e03543a70a 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -75,7 +75,6 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VIRTIO_NET; k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_NETWORK_ETHERNET; - k->sriov_vf_user_creatable = true; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); device_class_set_props(dc, virtio_net_properties); vpciklass->realize = virtio_net_pci_realize; From 67f5b279fc72e43ccdee20a1a1e54cb51e24f06a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:18 -0400 Subject: [PATCH 2640/2884] Revert "virtio-pci: Implement SR-IOV PF" This reverts commit 3f868ffb0bae0c4feafabe34a371cded57fe3806. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-pci.c | 20 +++++--------------- include/hw/virtio/virtio-pci.h | 1 - 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 0c8fcc5627..9534730bba 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1955,7 +1955,6 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) uint8_t *config; uint32_t size; VirtIODevice *vdev = virtio_bus_get_device(bus); - int16_t res; /* * Virtio capabilities present without @@ -2101,14 +2100,6 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx, PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar); } - - res = pcie_sriov_pf_init_from_user_created_vfs(&proxy->pci_dev, - proxy->last_pcie_cap_offset, - errp); - if (res > 0) { - proxy->last_pcie_cap_offset += res; - virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV); - } } static void virtio_pci_device_unplugged(DeviceState *d) @@ -2196,7 +2187,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) if (pcie_port && pci_is_express(pci_dev)) { int pos; - proxy->last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; + uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); @@ -2216,9 +2207,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); if (proxy->flags & VIRTIO_PCI_FLAG_AER) { - pcie_aer_init(pci_dev, PCI_ERR_VER, proxy->last_pcie_cap_offset, + pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset, PCI_ERR_SIZEOF, NULL); - proxy->last_pcie_cap_offset += PCI_ERR_SIZEOF; + last_pcie_cap_offset += PCI_ERR_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) { @@ -2243,9 +2234,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) } if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { - pcie_ats_init(pci_dev, proxy->last_pcie_cap_offset, + pcie_ats_init(pci_dev, last_pcie_cap_offset, proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED); - proxy->last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; + last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) { @@ -2272,7 +2263,6 @@ static void virtio_pci_exit(PCIDevice *pci_dev) bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) && !pci_bus_is_root(pci_get_bus(pci_dev)); - pcie_sriov_pf_exit(&proxy->pci_dev); msix_uninit_exclusive_bar(pci_dev); if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port && pci_is_express(pci_dev)) { diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 34539f2f67..9e67ba38c7 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -152,7 +152,6 @@ struct VirtIOPCIProxy { uint32_t modern_io_bar_idx; uint32_t modern_mem_bar_idx; int config_cap; - uint16_t last_pcie_cap_offset; uint32_t flags; bool disable_modern; bool ignore_backend_features; From aa01c4914ed6f6c087ee172483e22a515c4cc66a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:19 -0400 Subject: [PATCH 2641/2884] Revert "pcie_sriov: Allow user to create SR-IOV device" This reverts commit 122173a5830f7757f8a94a3b1559582f312e140b. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 62 +++----- hw/pci/pcie_sriov.c | 290 ++++++++---------------------------- include/hw/pci/pci_device.h | 6 +- include/hw/pci/pcie_sriov.h | 18 --- 4 files changed, 83 insertions(+), 293 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 8ad5d7e2d8..cf2794879d 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -85,7 +85,6 @@ static Property pci_props[] = { QEMU_PCIE_ERR_UNC_MASK_BITNR, true), DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present, QEMU_PCIE_ARI_NEXTFN_1_BITNR, false), - DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf), DEFINE_PROP_END_OF_LIST() }; @@ -960,8 +959,13 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } - /* SR/IOV is not handled here. */ - if (pci_is_vf(dev)) { + /* + * With SR/IOV and ARI, a device at function 0 need not be a multifunction + * device, as it may just be a VF that ended up with function 0 in + * the legacy PCI interpretation. Avoid failing in such cases: + */ + if (pci_is_vf(dev) && + dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { return; } @@ -994,8 +998,7 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) } /* function 0 indicates single function, so function > 0 must be NULL */ for (func = 1; func < PCI_FUNC_MAX; ++func) { - PCIDevice *device = bus->devices[PCI_DEVFN(slot, func)]; - if (device && !pci_is_vf(device)) { + if (bus->devices[PCI_DEVFN(slot, func)]) { error_setg(errp, "PCI: %x.0 indicates single function, " "but %x.%x is already populated.", slot, slot, func); @@ -1280,7 +1283,6 @@ static void pci_qdev_unrealize(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); - pcie_sriov_unregister_device(pci_dev); if (pc->exit) { pc->exit(pci_dev); @@ -1312,6 +1314,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size = memory_region_size(memory); uint8_t hdr_type; + assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */ assert(region_num >= 0); assert(region_num < PCI_NUM_REGIONS); assert(is_power_of_2(size)); @@ -1322,6 +1325,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2); r = &pci_dev->io_regions[region_num]; + r->addr = PCI_BAR_UNMAPPED; r->size = size; r->type = type; r->memory = memory; @@ -1329,35 +1333,22 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, ? pci_get_bus(pci_dev)->address_space_io : pci_get_bus(pci_dev)->address_space_mem; - if (pci_is_vf(pci_dev)) { - PCIDevice *pf = pci_dev->exp.sriov_vf.pf; - assert(!pf || type == pf->exp.sriov_pf.vf_bar_type[region_num]); + wmask = ~(size - 1); + if (region_num == PCI_ROM_SLOT) { + /* ROM enable bit is writable */ + wmask |= PCI_ROM_ADDRESS_ENABLE; + } - r->addr = pci_bar_address(pci_dev, region_num, r->type, r->size); - if (r->addr != PCI_BAR_UNMAPPED) { - memory_region_add_subregion_overlap(r->address_space, - r->addr, r->memory, 1); - } + addr = pci_bar(pci_dev, region_num); + pci_set_long(pci_dev->config + addr, type); + + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && + r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(pci_dev->wmask + addr, wmask); + pci_set_quad(pci_dev->cmask + addr, ~0ULL); } else { - r->addr = PCI_BAR_UNMAPPED; - - wmask = ~(size - 1); - if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writable */ - wmask |= PCI_ROM_ADDRESS_ENABLE; - } - - addr = pci_bar(pci_dev, region_num); - pci_set_long(pci_dev->config + addr, type); - - if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && - r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(pci_dev->wmask + addr, wmask); - pci_set_quad(pci_dev->cmask + addr, ~0ULL); - } else { - pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); - pci_set_long(pci_dev->cmask + addr, 0xffffffff); - } + pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); + pci_set_long(pci_dev->cmask + addr, 0xffffffff); } } @@ -2118,11 +2109,6 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) } } - if (!pcie_sriov_register_device(pci_dev, errp)) { - pci_qdev_unrealize(DEVICE(pci_dev)); - return; - } - /* * A PCIe Downstream Port that do not have ARI Forwarding enabled must * associate only Device 0 with the device attached to the bus diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 0fc9f810b9..15a4aac1f4 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -20,8 +20,6 @@ #include "qapi/error.h" #include "trace.h" -static GHashTable *pfs; - static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) { for (uint16_t i = 0; i < total_vfs; i++) { @@ -33,49 +31,14 @@ static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) dev->exp.sriov_pf.vf = NULL; } -static void clear_ctrl_vfe(PCIDevice *dev) -{ - uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL; - pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE); -} - -static void register_vfs(PCIDevice *dev) -{ - uint16_t num_vfs; - uint16_t i; - uint16_t sriov_cap = dev->exp.sriov_cap; - - assert(sriov_cap > 0); - num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); - if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { - clear_ctrl_vfe(dev); - return; - } - - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], true); - } -} - -static void unregister_vfs(PCIDevice *dev) -{ - uint16_t i; - uint8_t *cfg = dev->config + dev->exp.sriov_cap; - - trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], false); - } -} - -static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, - uint16_t vf_dev_id, uint16_t init_vfs, - uint16_t total_vfs, uint16_t vf_offset, - uint16_t vf_stride, Error **errp) +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, + const char *vfname, uint16_t vf_dev_id, + uint16_t init_vfs, uint16_t total_vfs, + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) { + BusState *bus = qdev_get_parent_bus(&dev->qdev); + int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -137,28 +100,6 @@ static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); - return true; -} - -bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, - const char *vfname, uint16_t vf_dev_id, - uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride, - Error **errp) -{ - BusState *bus = qdev_get_parent_bus(&dev->qdev); - int32_t devfn = dev->devfn + vf_offset; - - if (pfs && g_hash_table_contains(pfs, dev->qdev.id)) { - error_setg(errp, "attaching user-created SR-IOV VF unsupported"); - return false; - } - - if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, init_vfs, - total_vfs, vf_offset, vf_stride, errp)) { - return false; - } - dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); for (uint16_t i = 0; i < total_vfs; i++) { @@ -188,24 +129,7 @@ void pcie_sriov_pf_exit(PCIDevice *dev) { uint8_t *cfg = dev->config + dev->exp.sriov_cap; - if (dev->exp.sriov_pf.vf_user_created) { - uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); - uint16_t total_vfs = pci_get_word(dev->config + PCI_SRIOV_TOTAL_VF); - uint16_t vf_dev_id = pci_get_word(dev->config + PCI_SRIOV_VF_DID); - - unregister_vfs(dev); - - for (uint16_t i = 0; i < total_vfs; i++) { - PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - - vf->exp.sriov_vf.pf = NULL; - - pci_config_set_vendor_id(vf->config, ven_id); - pci_config_set_device_id(vf->config, vf_dev_id); - } - } else { - unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); - } + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -238,172 +162,74 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory) { + PCIIORegion *r; + PCIBus *bus = pci_get_bus(dev); uint8_t type; + pcibus_t size = memory_region_size(memory); - assert(dev->exp.sriov_vf.pf); + assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */ + assert(region_num >= 0); + assert(region_num < PCI_NUM_REGIONS); type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num]; - return pci_register_bar(dev, region_num, type, memory); + if (!is_power_of_2(size)) { + error_report("%s: PCI region size must be a power" + " of two - type=0x%x, size=0x%"FMT_PCIBUS, + __func__, type, size); + exit(1); + } + + r = &dev->io_regions[region_num]; + r->memory = memory; + r->address_space = + type & PCI_BASE_ADDRESS_SPACE_IO + ? bus->address_space_io + : bus->address_space_mem; + r->size = size; + r->type = type; + + r->addr = pci_bar_address(dev, region_num, r->type, r->size); + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); + } } -static gint compare_vf_devfns(gconstpointer a, gconstpointer b) +static void clear_ctrl_vfe(PCIDevice *dev) { - return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn; + uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL; + pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE); } -int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, - uint16_t offset, - Error **errp) +static void register_vfs(PCIDevice *dev) { - GPtrArray *pf; - PCIDevice **vfs; - BusState *bus = qdev_get_parent_bus(DEVICE(dev)); - uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); - uint16_t vf_dev_id; - uint16_t vf_offset; - uint16_t vf_stride; + uint16_t num_vfs; uint16_t i; + uint16_t sriov_cap = dev->exp.sriov_cap; - if (!pfs || !dev->qdev.id) { - return 0; + assert(sriov_cap > 0); + num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { + clear_ctrl_vfe(dev); + return; } - pf = g_hash_table_lookup(pfs, dev->qdev.id); - if (!pf) { - return 0; + trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } - - if (pf->len > UINT16_MAX) { - error_setg(errp, "too many VFs"); - return -1; - } - - g_ptr_array_sort(pf, compare_vf_devfns); - vfs = (void *)pf->pdata; - - if (vfs[0]->devfn <= dev->devfn) { - error_setg(errp, "a VF function number is less than the PF function number"); - return -1; - } - - vf_dev_id = pci_get_word(vfs[0]->config + PCI_DEVICE_ID); - vf_offset = vfs[0]->devfn - dev->devfn; - vf_stride = pf->len < 2 ? 0 : vfs[1]->devfn - vfs[0]->devfn; - - for (i = 0; i < pf->len; i++) { - if (bus != qdev_get_parent_bus(&vfs[i]->qdev)) { - error_setg(errp, "SR-IOV VF parent bus mismatches with PF"); - return -1; - } - - if (ven_id != pci_get_word(vfs[i]->config + PCI_VENDOR_ID)) { - error_setg(errp, "SR-IOV VF vendor ID mismatches with PF"); - return -1; - } - - if (vf_dev_id != pci_get_word(vfs[i]->config + PCI_DEVICE_ID)) { - error_setg(errp, "inconsistent SR-IOV VF device IDs"); - return -1; - } - - for (size_t j = 0; j < PCI_NUM_REGIONS; j++) { - if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size || - vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) { - error_setg(errp, "inconsistent SR-IOV BARs"); - return -1; - } - } - - if (vfs[i]->devfn - vfs[0]->devfn != vf_stride * i) { - error_setg(errp, "inconsistent SR-IOV stride"); - return -1; - } - } - - if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, pf->len, - pf->len, vf_offset, vf_stride, errp)) { - return -1; - } - - for (i = 0; i < pf->len; i++) { - vfs[i]->exp.sriov_vf.pf = dev; - vfs[i]->exp.sriov_vf.vf_number = i; - - /* set vid/did according to sr/iov spec - they are not used */ - pci_config_set_vendor_id(vfs[i]->config, 0xffff); - pci_config_set_device_id(vfs[i]->config, 0xffff); - } - - dev->exp.sriov_pf.vf = vfs; - dev->exp.sriov_pf.vf_user_created = true; - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - PCIIORegion *region = &vfs[0]->io_regions[i]; - - if (region->size) { - pcie_sriov_pf_init_vf_bar(dev, i, region->type, region->size); - } - } - - return PCI_EXT_CAP_SRIOV_SIZEOF; } -bool pcie_sriov_register_device(PCIDevice *dev, Error **errp) +static void unregister_vfs(PCIDevice *dev) { - if (!dev->exp.sriov_pf.vf && dev->qdev.id && - pfs && g_hash_table_contains(pfs, dev->qdev.id)) { - error_setg(errp, "attaching user-created SR-IOV VF unsupported"); - return false; - } + uint16_t i; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; - if (dev->sriov_pf) { - PCIDevice *pci_pf; - GPtrArray *pf; - - if (!PCI_DEVICE_GET_CLASS(dev)->sriov_vf_user_creatable) { - error_setg(errp, "user cannot create SR-IOV VF with this device type"); - return false; - } - - if (!pci_is_express(dev)) { - error_setg(errp, "PCI Express is required for SR-IOV VF"); - return false; - } - - if (!pci_qdev_find_device(dev->sriov_pf, &pci_pf)) { - error_setg(errp, "PCI device specified as SR-IOV PF already exists"); - return false; - } - - if (!pfs) { - pfs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - } - - pf = g_hash_table_lookup(pfs, dev->sriov_pf); - if (!pf) { - pf = g_ptr_array_new(); - g_hash_table_insert(pfs, g_strdup(dev->sriov_pf), pf); - } - - g_ptr_array_add(pf, dev); - } - - return true; -} - -void pcie_sriov_unregister_device(PCIDevice *dev) -{ - if (dev->sriov_pf && pfs) { - GPtrArray *pf = g_hash_table_lookup(pfs, dev->sriov_pf); - - if (pf) { - g_ptr_array_remove_fast(pf, dev); - - if (!pf->len) { - g_hash_table_remove(pfs, dev->sriov_pf); - g_ptr_array_free(pf, FALSE); - } - } + trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } } @@ -490,7 +316,7 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize) uint16_t pcie_sriov_vf_number(PCIDevice *dev) { - assert(dev->exp.sriov_vf.pf); + assert(pci_is_vf(dev)); return dev->exp.sriov_vf.vf_number; } diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index e7e41cb939..1ff3ce94e2 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -38,8 +38,6 @@ struct PCIDeviceClass { uint16_t subsystem_id; /* only for header type = 0 */ const char *romfile; /* rom bar */ - - bool sriov_vf_user_creatable; }; enum PCIReqIDType { @@ -169,8 +167,6 @@ struct PCIDevice { /* ID of standby device in net_failover pair */ char *failover_pair_id; uint32_t acpi_index; - - char *sriov_pf; }; static inline int pci_intx(PCIDevice *pci_dev) @@ -203,7 +199,7 @@ static inline int pci_is_express_downstream_port(const PCIDevice *d) static inline int pci_is_vf(const PCIDevice *d) { - return d->sriov_pf || d->exp.sriov_vf.pf != NULL; + return d->exp.sriov_vf.pf != NULL; } static inline uint32_t pci_config_size(const PCIDevice *d) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index f75b8f22ee..c5d2d318d3 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -18,7 +18,6 @@ typedef struct PCIESriovPF { uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ - bool vf_user_created; /* If VFs are created by user */ } PCIESriovPF; typedef struct PCIESriovVF { @@ -41,23 +40,6 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory); -/** - * pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created - * VFs. - * @dev: A PCIe device being realized. - * @offset: The offset of the SR-IOV capability. - * @errp: pointer to Error*, to store an error if it happens. - * - * Return: The size of added capability. 0 if the user did not create VFs. - * -1 if failed. - */ -int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, - uint16_t offset, - Error **errp); - -bool pcie_sriov_register_device(PCIDevice *dev, Error **errp); -void pcie_sriov_unregister_device(PCIDevice *dev); - /* * Default (minimal) page size support values * as required by the SR/IOV standard: From c8597d3e1c79a27cbf8ec3bed1a58a426f29ae21 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:20 -0400 Subject: [PATCH 2642/2884] Revert "pcie_sriov: Check PCI Express for SR-IOV PF" This reverts commit 47cc753e50076c25334091783738be9f716253b1. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 15a4aac1f4..6c79658b4c 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -42,11 +42,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint8_t *cfg = dev->config + offset; uint8_t *wmask; - if (!pci_is_express(dev)) { - error_setg(errp, "PCI Express is required for SR-IOV PF"); - return false; - } - if (pci_is_vf(dev)) { error_setg(errp, "a device cannot be both an SR-IOV PF and a VF"); return false; From da44479b1d79dd31eabaa865260ad3e679cdf2e1 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:21 -0400 Subject: [PATCH 2643/2884] Revert "pcie_sriov: Ensure PF and VF are mutually exclusive" This reverts commit 78f9d7fd1989311040beff54979bcb2a1ba0aff2. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 6c79658b4c..56523ab4e8 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -42,11 +42,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint8_t *cfg = dev->config + offset; uint8_t *wmask; - if (pci_is_vf(dev)) { - error_setg(errp, "a device cannot be both an SR-IOV PF and a VF"); - return false; - } - if (total_vfs) { uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI); uint16_t first_vf_devfn = dev->devfn + vf_offset; From 558452512f068bfb3584a5a8b15530b0c53074c7 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:22 -0400 Subject: [PATCH 2644/2884] Revert "hw/pci: Fix SR-IOV VF number calculation" This reverts commit ca6dd3aef8a103138c99788bcba8195d4905ddc5. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index cf2794879d..4c7be52951 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1437,11 +1437,7 @@ static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg, pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET); uint16_t vf_stride = pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - uint32_t vf_num = d->devfn - (pf->devfn + vf_offset); - - if (vf_num) { - vf_num /= vf_stride; - } + uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride; if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { new_addr = pci_get_quad(pf->config + bar); From b9ba81769499533488540b6d5ed7c7569476a427 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:38 -0400 Subject: [PATCH 2645/2884] Revert "pcie_sriov: Register VFs after migration" This reverts commit 107a64b9a360cf5ca046852bc03334f7a9f22aef. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 7 ------- hw/pci/pcie_sriov.c | 7 ------- include/hw/pci/pcie_sriov.h | 2 -- 3 files changed, 16 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 4c7be52951..5c0050e178 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -733,17 +733,10 @@ static bool migrate_is_not_pcie(void *opaque, int version_id) return !pci_is_express((PCIDevice *)opaque); } -static int pci_post_load(void *opaque, int version_id) -{ - pcie_sriov_pf_post_load(opaque); - return 0; -} - const VMStateDescription vmstate_pci_device = { .name = "PCIDevice", .version_id = 2, .minimum_version_id = 1, - .post_load = pci_post_load, .fields = (const VMStateField[]) { VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice, diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 56523ab4e8..fae6acea4a 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -252,13 +252,6 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } } -void pcie_sriov_pf_post_load(PCIDevice *dev) -{ - if (dev->exp.sriov_cap) { - register_vfs(dev); - } -} - /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index c5d2d318d3..5148c5b77d 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -57,8 +57,6 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize); void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, uint32_t val, int len); -void pcie_sriov_pf_post_load(PCIDevice *dev); - /* Reset SR/IOV */ void pcie_sriov_pf_reset(PCIDevice *dev); From ae9c192de7ab0f56f32d8b60b6568917e0138919 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:41 -0400 Subject: [PATCH 2646/2884] Revert "pcie_sriov: Remove num_vfs from PCIESriovPF" This reverts commit cbd9e5120bac3e292eee77b7a2e3692f235a1a26. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 28 ++++++++-------------------- hw/pci/trace-events | 2 +- include/hw/pci/pcie_sriov.h | 1 + 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index fae6acea4a..9bd7f8acc3 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -57,6 +57,7 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; + dev->exp.sriov_pf.num_vfs = 0; dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -185,12 +186,6 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, } } -static void clear_ctrl_vfe(PCIDevice *dev) -{ - uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL; - pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE); -} - static void register_vfs(PCIDevice *dev) { uint16_t num_vfs; @@ -200,7 +195,6 @@ static void register_vfs(PCIDevice *dev) assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { - clear_ctrl_vfe(dev); return; } @@ -209,18 +203,20 @@ static void register_vfs(PCIDevice *dev) for (i = 0; i < num_vfs; i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], true); } + dev->exp.sriov_pf.num_vfs = num_vfs; } static void unregister_vfs(PCIDevice *dev) { + uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; uint16_t i; - uint8_t *cfg = dev->config + dev->exp.sriov_cap; trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { pci_set_enabled(dev->exp.sriov_pf.vf[i], false); } + dev->exp.sriov_pf.num_vfs = 0; } void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, @@ -246,9 +242,6 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } else { unregister_vfs(dev); } - } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { - clear_ctrl_vfe(dev); - unregister_vfs(dev); } } @@ -311,7 +304,7 @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) { assert(!pci_is_vf(dev)); - if (n < pcie_sriov_num_vfs(dev)) { + if (n < dev->exp.sriov_pf.num_vfs) { return dev->exp.sriov_pf.vf[n]; } return NULL; @@ -319,10 +312,5 @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) uint16_t pcie_sriov_num_vfs(PCIDevice *dev) { - uint16_t sriov_cap = dev->exp.sriov_cap; - uint8_t *cfg = dev->config + sriov_cap; - - return sriov_cap && - (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ? - pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0; + return dev->exp.sriov_pf.num_vfs; } diff --git a/hw/pci/trace-events b/hw/pci/trace-events index e98f575a9d..19643aa8c6 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -14,7 +14,7 @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask # hw/pci/pcie_sriov.c sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs" -sriov_unregister_vfs(const char *name, int slot, int function) "%s %02x:%x: Unregistering vf devs" +sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs" sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d" # pcie.c diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 5148c5b77d..70649236c1 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -16,6 +16,7 @@ #include "hw/pci/pci.h" typedef struct PCIESriovPF { + uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; From 9bab08da4e932e9c95919951792ae09d0a59f726 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:41 -0400 Subject: [PATCH 2647/2884] Revert "pcie_sriov: Release VFs failed to realize" This reverts commit 1a9bf009012e590cb166a4a9bae4bc18fb084d76. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 9bd7f8acc3..faadb0d2ea 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -99,8 +99,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, vf->exp.sriov_vf.vf_number = i; if (!qdev_realize(&vf->qdev, bus, errp)) { - object_unparent(OBJECT(vf)); - object_unref(vf); unparent_vfs(dev, i); return false; } From b1282f1e352db9947267a6524c6ded9678e82629 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:42 -0400 Subject: [PATCH 2648/2884] Revert "pcie_sriov: Reuse SR-IOV VF device instances" This reverts commit 139610ae67f6ecf92127bb7bf53ac6265b459ec8. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 2 +- hw/pci/pcie_sriov.c | 95 +++++++++++++++++++++---------------- include/hw/pci/pci.h | 5 ++ include/hw/pci/pci_device.h | 15 ------ include/hw/pci/pcie_sriov.h | 1 + 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 5c0050e178..b532888e8f 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2895,7 +2895,7 @@ void pci_set_enabled(PCIDevice *d, bool state) memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_MASTER) && d->enabled); - if (d->qdev.realized) { + if (!d->enabled) { pci_device_reset(d); } } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index faadb0d2ea..f0bde0d3fc 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -20,16 +20,9 @@ #include "qapi/error.h" #include "trace.h" -static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) -{ - for (uint16_t i = 0; i < total_vfs; i++) { - PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - object_unparent(OBJECT(vf)); - object_unref(OBJECT(vf)); - } - g_free(dev->exp.sriov_pf.vf); - dev->exp.sriov_pf.vf = NULL; -} +static PCIDevice *register_vf(PCIDevice *pf, int devfn, + const char *name, uint16_t vf_num); +static void unregister_vfs(PCIDevice *dev); bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, @@ -37,8 +30,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint16_t vf_offset, uint16_t vf_stride, Error **errp) { - BusState *bus = qdev_get_parent_bus(&dev->qdev); - int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -58,6 +49,7 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; dev->exp.sriov_pf.num_vfs = 0; + dev->exp.sriov_pf.vfname = g_strdup(vfname); dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -91,34 +83,14 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); - dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); - - for (uint16_t i = 0; i < total_vfs; i++) { - PCIDevice *vf = pci_new(devfn, vfname); - vf->exp.sriov_vf.pf = dev; - vf->exp.sriov_vf.vf_number = i; - - if (!qdev_realize(&vf->qdev, bus, errp)) { - unparent_vfs(dev, i); - return false; - } - - /* set vid/did according to sr/iov spec - they are not used */ - pci_config_set_vendor_id(vf->config, 0xffff); - pci_config_set_device_id(vf->config, 0xffff); - - dev->exp.sriov_pf.vf[i] = vf; - devfn += vf_stride; - } - return true; } void pcie_sriov_pf_exit(PCIDevice *dev) { - uint8_t *cfg = dev->config + dev->exp.sriov_cap; - - unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); + unregister_vfs(dev); + g_free((char *)dev->exp.sriov_pf.vfname); + dev->exp.sriov_pf.vfname = NULL; } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -184,11 +156,38 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, } } +static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, + uint16_t vf_num) +{ + PCIDevice *dev = pci_new(devfn, name); + dev->exp.sriov_vf.pf = pf; + dev->exp.sriov_vf.vf_number = vf_num; + PCIBus *bus = pci_get_bus(pf); + Error *local_err = NULL; + + qdev_realize(&dev->qdev, &bus->qbus, &local_err); + if (local_err) { + error_report_err(local_err); + return NULL; + } + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(dev->config, 0xffff); + pci_config_set_device_id(dev->config, 0xffff); + + return dev; +} + static void register_vfs(PCIDevice *dev) { uint16_t num_vfs; uint16_t i; uint16_t sriov_cap = dev->exp.sriov_cap; + uint16_t vf_offset = + pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET); + uint16_t vf_stride = + pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE); + int32_t devfn = dev->devfn + vf_offset; assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); @@ -196,10 +195,18 @@ static void register_vfs(PCIDevice *dev) return; } + dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); + trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], true); + dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn, + dev->exp.sriov_pf.vfname, i); + if (!dev->exp.sriov_pf.vf[i]) { + num_vfs = i; + break; + } + devfn += vf_stride; } dev->exp.sriov_pf.num_vfs = num_vfs; } @@ -212,8 +219,12 @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], false); + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + object_unparent(OBJECT(vf)); + object_unref(OBJECT(vf)); } + g_free(dev->exp.sriov_pf.vf); + dev->exp.sriov_pf.vf = NULL; dev->exp.sriov_pf.num_vfs = 0; } @@ -235,10 +246,14 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, PCI_FUNC(dev->devfn), off, val, len); if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); + if (dev->exp.sriov_pf.num_vfs) { + if (!(val & PCI_SRIOV_CTRL_VFE)) { + unregister_vfs(dev); + } } else { - unregister_vfs(dev); + if (val & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); + } } } } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 14a869eeaa..fe04b4fafd 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -680,4 +680,9 @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); void pci_set_enabled(PCIDevice *pci_dev, bool state); +static inline void pci_set_power(PCIDevice *pci_dev, bool state) +{ + pci_set_enabled(pci_dev, state); +} + #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index 1ff3ce94e2..f38fb31119 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -212,21 +212,6 @@ static inline uint16_t pci_get_bdf(PCIDevice *dev) return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn); } -static inline void pci_set_power(PCIDevice *pci_dev, bool state) -{ - /* - * Don't change the enabled state of VFs when powering on/off the device. - * - * When powering on, VFs must not be enabled immediately but they must - * wait until the guest configures SR-IOV. - * When powering off, their corresponding PFs will be reset and disable - * VFs. - */ - if (!pci_is_vf(pci_dev)) { - pci_set_enabled(pci_dev, state); - } -} - uint16_t pci_requester_id(PCIDevice *dev); /* DMA access functions */ diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 70649236c1..aa704e8f9d 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -18,6 +18,7 @@ typedef struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ + const char *vfname; /* Reference to the device type used for the VFs */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ } PCIESriovPF; From 19c45c00dc6a52f80f27dabbd28de1b770c16a89 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:43 -0400 Subject: [PATCH 2649/2884] Revert "pcie_sriov: Ensure VF function number does not overflow" This reverts commit 77718701157f6ca77ea7a57b536fa0a22f676082. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- docs/pcie_sriov.txt | 8 +++----- hw/net/igb.c | 13 +++---------- hw/nvme/ctrl.c | 24 ++++++++---------------- hw/pci/pcie_sriov.c | 19 ++----------------- include/hw/pci/pcie_sriov.h | 5 ++--- 5 files changed, 18 insertions(+), 51 deletions(-) diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt index ab2142807f..a47aad0bfa 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -52,11 +52,9 @@ setting up a BAR for a VF. ... /* Add and initialize the SR/IOV capability */ - if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", - vf_devid, initial_vfs, total_vfs, - fun_offset, stride, errp)) { - return; - } + pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", + vf_devid, initial_vfs, total_vfs, + fun_offset, stride); /* Set up individual VF BARs (parameters as for normal BARs) */ pcie_sriov_pf_init_vf_bar( ... ) diff --git a/hw/net/igb.c b/hw/net/igb.c index b6ca2f1b8a..b92bba402e 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -446,16 +446,9 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_ari_init(pci_dev, 0x150); - if (!pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, - TYPE_IGBVF, IGB_82576_VF_DEV_ID, - IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, - IGB_VF_OFFSET, IGB_VF_STRIDE, - errp)) { - pcie_cap_exit(pci_dev); - igb_cleanup_msix(s); - msi_uninit(pci_dev); - return; - } + pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF, + IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, + IGB_VF_OFFSET, IGB_VF_STRIDE); pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e86ea2e7ce..c6d4f61a47 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8271,8 +8271,7 @@ out: return pow2ceil(bar_size); } -static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset, - Error **errp) +static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) { uint16_t vf_dev_id = n->params.use_intel_id ? PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME; @@ -8281,17 +8280,12 @@ static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset, le16_to_cpu(cap->vifrsm), NULL, NULL); - if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, - n->params.sriov_max_vfs, n->params.sriov_max_vfs, - NVME_VF_OFFSET, NVME_VF_STRIDE, - errp)) { - return false; - } + pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE); pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); - - return true; } static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) @@ -8416,12 +8410,6 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) return false; } - if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs && - !nvme_init_sriov(n, pci_dev, 0x120, errp)) { - msix_uninit(pci_dev, &n->bar0, &n->bar0); - return false; - } - nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); pcie_cap_deverr_init(pci_dev); @@ -8451,6 +8439,10 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_pmr(n, pci_dev); } + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { + nvme_init_sriov(n, pci_dev, 0x120); + } + return true; } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index f0bde0d3fc..499becd527 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -24,27 +24,14 @@ static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, uint16_t vf_num); static void unregister_vfs(PCIDevice *dev); -bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride, - Error **errp) + uint16_t vf_offset, uint16_t vf_stride) { uint8_t *cfg = dev->config + offset; uint8_t *wmask; - if (total_vfs) { - uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI); - uint16_t first_vf_devfn = dev->devfn + vf_offset; - uint16_t last_vf_devfn = first_vf_devfn + vf_stride * (total_vfs - 1); - - if ((!ari_cap && PCI_SLOT(dev->devfn) != PCI_SLOT(last_vf_devfn)) || - last_vf_devfn >= PCI_DEVFN_MAX) { - error_setg(errp, "VF function number overflows"); - return false; - } - } - pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; @@ -82,8 +69,6 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); qdev_prop_set_bit(&dev->qdev, "multifunction", true); - - return true; } void pcie_sriov_pf_exit(PCIDevice *dev) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index aa704e8f9d..450cbef6c2 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -27,11 +27,10 @@ typedef struct PCIESriovVF { uint16_t vf_number; /* Logical VF number of this function */ } PCIESriovVF; -bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, +void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride, - Error **errp); + uint16_t vf_offset, uint16_t vf_stride); void pcie_sriov_pf_exit(PCIDevice *dev); /* Set up a VF bar in the SR/IOV bar area */ From b0fdaee5d1ed52f650ca0c3bebfce3dd4dc8ddb5 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:45 -0400 Subject: [PATCH 2650/2884] Revert "pcie_sriov: Do not manually unrealize" This reverts commit c613ad25125bf3016aa8f81ce170f5ac91d2379f. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pcie_sriov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 499becd527..e9b23221d7 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -204,7 +204,11 @@ static void unregister_vfs(PCIDevice *dev) trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { + Error *err = NULL; PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + if (!object_property_set_bool(OBJECT(vf), "realized", false, &err)) { + error_reportf_err(err, "Failed to unplug: "); + } object_unparent(OBJECT(vf)); object_unref(OBJECT(vf)); } From 47279e8afa84cb28e84e3ac4392487b94a494fa9 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:47 -0400 Subject: [PATCH 2651/2884] Revert "hw/ppc/spapr_pci: Do not reject VFs created after a PF" This reverts commit 26f86093ec989cb73ad03e8a234f5dc321e1e267. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/ppc/spapr_pci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index ed4454bbf7..f63182a03c 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1573,9 +1573,7 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, * hotplug, we do not allow functions to be hotplugged to a * slot that already has function 0 present */ - if (plugged_dev->hotplugged && - !pci_is_vf(pdev) && - bus->devices[PCI_DEVFN(slotnr, 0)] && + if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && PCI_FUNC(pdev->devfn) != 0) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " additional functions can no longer be exposed to guest.", From f1feffc4ef4b1c02daa5ce23fa8ad728c8ef7ce9 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:49 -0400 Subject: [PATCH 2652/2884] Revert "hw/ppc/spapr_pci: Do not create DT for disabled PCI device" This reverts commit 723c5b4628d047e43825a046c6ee517b82b88117. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/ppc/spapr_pci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index f63182a03c..7cf9904c35 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1296,10 +1296,6 @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev, return; } - if (!pdev->enabled) { - return; - } - err = spapr_dt_pci_device(p->sphb, pdev, p->fdt, p->offset); if (err < 0) { p->err = err; From 93829009a685687e2fb48158cb5df18c4eb49d07 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" <mst@redhat.com> Date: Thu, 1 Aug 2024 03:44:50 -0400 Subject: [PATCH 2653/2884] Revert "hw/pci: Rename has_power to enabled" This reverts commit 6a31b219a5338564f3978251c79f96f689e037da. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/pci/pci.c | 14 +++++++------- hw/pci/pci_host.c | 4 ++-- include/hw/pci/pci.h | 7 +------ include/hw/pci/pci_device.h | 2 +- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index b532888e8f..fab86d0567 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1525,7 +1525,7 @@ static void pci_update_mappings(PCIDevice *d) continue; new_addr = pci_bar_address(d, i, r->type, r->size); - if (!d->enabled) { + if (!d->has_power) { new_addr = PCI_BAR_UNMAPPED; } @@ -1613,7 +1613,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int pci_update_irq_disabled(d, was_irq_disabled); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->enabled); + & PCI_COMMAND_MASTER) && d->has_power); } msi_write_config(d, addr, val_in, l); @@ -2884,18 +2884,18 @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) return msg; } -void pci_set_enabled(PCIDevice *d, bool state) +void pci_set_power(PCIDevice *d, bool state) { - if (d->enabled == state) { + if (d->has_power == state) { return; } - d->enabled = state; + d->has_power = state; pci_update_mappings(d); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->enabled); - if (!d->enabled) { + & PCI_COMMAND_MASTER) && d->has_power); + if (!d->has_power) { pci_device_reset(d); } } diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 0d82727cc9..dfe6fe6184 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -86,7 +86,7 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { + !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { return; } @@ -111,7 +111,7 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { + !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { return ~0x0; } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index fe04b4fafd..eb26cac810 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -678,11 +678,6 @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) } MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); -void pci_set_enabled(PCIDevice *pci_dev, bool state); - -static inline void pci_set_power(PCIDevice *pci_dev, bool state) -{ - pci_set_enabled(pci_dev, state); -} +void pci_set_power(PCIDevice *pci_dev, bool state); #endif diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index f38fb31119..15694f2489 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -57,7 +57,7 @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIDevice { DeviceState qdev; bool partially_hotplugged; - bool enabled; + bool has_power; /* PCI config space */ uint8_t *config; From 9a45b0761628cc59267b3283a85d15294464ac31 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 18:00:19 +0100 Subject: [PATCH 2654/2884] hw/i386/amd_iommu: Don't leak memory in amdvi_update_iotlb() In amdvi_update_iotlb() we will only put a new entry in the hash table if to_cache.perm is not IOMMU_NONE. However we allocate the memory for the new AMDVIIOTLBEntry and for the hash table key regardless. This means that in the IOMMU_NONE case we will leak the memory we alloacted. Move the allocations into the if() to the point where we know we're going to add the item to the hash table. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2452 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-Id: <20240731170019.3590563-1-peter.maydell@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/i386/amd_iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 6d4fde72f9..87643d2891 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -357,12 +357,12 @@ static void amdvi_update_iotlb(AMDVIState *s, uint16_t devid, uint64_t gpa, IOMMUTLBEntry to_cache, uint16_t domid) { - AMDVIIOTLBEntry *entry = g_new(AMDVIIOTLBEntry, 1); - uint64_t *key = g_new(uint64_t, 1); - uint64_t gfn = gpa >> AMDVI_PAGE_SHIFT_4K; - /* don't cache erroneous translations */ if (to_cache.perm != IOMMU_NONE) { + AMDVIIOTLBEntry *entry = g_new(AMDVIIOTLBEntry, 1); + uint64_t *key = g_new(uint64_t, 1); + uint64_t gfn = gpa >> AMDVI_PAGE_SHIFT_4K; + trace_amdvi_cache_update(domid, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), gpa, to_cache.translated_addr); From 515457757ff8540c524ff39ea1d9564b251c6532 Mon Sep 17 00:00:00 2001 From: yeeli <seven.yi.lee@gmail.com> Date: Thu, 25 Jul 2024 11:18:58 +0800 Subject: [PATCH 2655/2884] intel_iommu: Fix for IQA reg read dropped DW field If VT-D hardware supports scalable mode, Linux will set the IQA DW field (bit11). In qemu, the vtd_mem_write and vtd_update_iq_dw set DW field well. However, vtd_mem_read the DW field wrong because "& VTD_IQA_QS" dropped the value of DW. Replace "&VTD_IQA_QS" with "& (VTD_IQA_QS | VTD_IQA_DW_MASK)" could save the DW field. Test patch as below: config the "x-scalable-mode" option: "-device intel-iommu,caching-mode=on,x-scalable-mode=on,aw-bits=48" After Linux OS boot, check the IQA_REG DW Field by usage 1 or 2: 1. IOMMU_DEBUGFS: Before fix: cat /sys/kernel/debug/iommu/intel/iommu_regset |grep IQA IQA 0x90 0x00000001001da001 After fix: cat /sys/kernel/debug/iommu/intel/iommu_regset |grep IQA IQA 0x90 0x00000001001da801 Check DW field(bit11) is 1. 2. devmem2 read the IQA_REG (offset 0x90): Before fix: devmem2 0xfed90090 /dev/mem opened. Memory mapped at address 0x7f72c795b000. Value at address 0xFED90090 (0x7f72c795b090): 0x1DA001 After fix: devmem2 0xfed90090 /dev/mem opened. Memory mapped at address 0x7fc95281c000. Value at address 0xFED90090 (0x7fc95281c090): 0x1DA801 Check DW field(bit11) is 1. Signed-off-by: yeeli <seven.yi.lee@gmail.com> Message-Id: <20240725031858.1529902-1-seven.yi.lee@gmail.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/i386/intel_iommu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 9a768f0b44..16d2885fcc 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2947,7 +2947,9 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) /* Invalidation Queue Address Register, 64-bit */ case DMAR_IQA_REG: - val = s->iq | (vtd_get_quad(s, DMAR_IQA_REG) & VTD_IQA_QS); + val = s->iq | + (vtd_get_quad(s, DMAR_IQA_REG) & + (VTD_IQA_QS | VTD_IQA_DW_MASK)); if (size == 4) { val = val & ((1ULL << 32) - 1); } From 5a558be93ad628e5bed6e0ee062870f49251725c Mon Sep 17 00:00:00 2001 From: Marco Palumbi <Marco.Palumbi@tii.ae> Date: Thu, 1 Aug 2024 10:15:02 +0100 Subject: [PATCH 2656/2884] hw/arm/mps2-tz.c: fix RX/TX interrupts order The order of the RX and TX interrupts are swapped. This commit fixes the order as per the following documents: * https://developer.arm.com/documentation/dai0505/latest/ * https://developer.arm.com/documentation/dai0521/latest/ * https://developer.arm.com/documentation/dai0524/latest/ * https://developer.arm.com/documentation/dai0547/latest/ Cc: qemu-stable@nongnu.org Signed-off-by: Marco Palumbi <Marco.Palumbi@tii.ae> Message-id: 20240730073123.72992-1-marco@palumbi.it Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/mps2-tz.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index a2d18afd79..aec57c0d68 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -435,7 +435,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, const int *irqs, const PPCExtraData *extradata) { - /* The irq[] array is tx, rx, combined, in that order */ + /* The irq[] array is rx, tx, combined, in that order */ MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); CMSDKAPBUART *uart = opaque; int i = uart - &mms->uart[0]; @@ -447,8 +447,8 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", mmc->apb_periph_frq); sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal); s = SYS_BUS_DEVICE(uart); - sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); - sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[1])); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[1])); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[0])); sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqs[2])); From 036144cff27ef2f97de7ffc0c1150f3779b94d58 Mon Sep 17 00:00:00 2001 From: Salil Mehta <salil.mehta@huawei.com> Date: Thu, 1 Aug 2024 10:15:03 +0100 Subject: [PATCH 2657/2884] accel/kvm/kvm-all: Fixes the missing break in vCPU unpark logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loop should exit prematurely on successfully finding out the parked vCPU (struct KVMParkedVcpu) in the 'struct KVMState' maintained 'kvm_parked_vcpus' list of parked vCPUs. Fixes: Coverity CID 1558552 Fixes: 08c3286822 ("accel/kvm: Extract common KVM vCPU {creation,parking} code") Reported-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Gavin Shan <gshan@redhat.com> Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-id: 20240725145132.99355-1-salil.mehta@huawei.com Suggested-by: Peter Maydell <peter.maydell@linaro.org> Message-ID: <CAFEAcA-3_d1c7XSXWkFubD-LsW5c5i95e6xxV09r2C9yGtzcdA@mail.gmail.com> Signed-off-by: Salil Mehta <salil.mehta@huawei.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- accel/kvm/kvm-all.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index e1d1386306..75d11a07b2 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -362,6 +362,7 @@ int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) QLIST_REMOVE(cpu, node); kvm_fd = cpu->kvm_fd; g_free(cpu); + break; } } From 55f9f4ee018c5ccea81d8c8c586756d7711ae46f Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Thu, 1 Aug 2024 10:15:03 +0100 Subject: [PATCH 2658/2884] target/arm: Handle denormals correctly for FMOPA (widening) The FMOPA (widening) SME instruction takes pairs of half-precision floating point values, widens them to single-precision, does a two-way dot product and accumulates the results into a single-precision destination. We don't quite correctly handle the FPCR bits FZ and FZ16 which control flushing of denormal inputs and outputs. This is because at the moment we pass a single float_status value to the helper function, which then uses that configuration for all the fp operations it does. However, because the inputs to this operation are float16 and the outputs are float32 we need to use the fp_status_f16 for the float16 input widening but the normal fp_status for everything else. Otherwise we will apply the flushing control FPCR.FZ16 to the 32-bit output rather than the FPCR.FZ control, and incorrectly flush a denormal output to zero when we should not (or vice-versa). (In commit 207d30b5fdb5b we tried to fix the FZ handling but didn't get it right, switching from "use FPCR.FZ for everything" to "use FPCR.FZ16 for everything".) Pass the CPU env to the sme_fmopa_h helper instead of an fp_status pointer, and have the helper pass an extra fp_status into the f16_dotadd() function so that we can use the right status for the right parts of this operation. Cc: qemu-stable@nongnu.org Fixes: 207d30b5fdb5 ("target/arm: Use FPST_F16 for SME FMOPA (widening)") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2373 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/tcg/helper-sme.h | 2 +- target/arm/tcg/sme_helper.c | 39 +++++++++++++++++++++++----------- target/arm/tcg/translate-sme.c | 25 ++++++++++++++++++++-- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 27eef49a11..d22bf9d21b 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -121,7 +121,7 @@ DEF_HELPER_FLAGS_5(sme_addha_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 3ba826a6ce..02106809ce 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -992,12 +992,23 @@ static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg) } static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, - float_status *s_std, float_status *s_odd) + float_status *s_f16, float_status *s_std, + float_status *s_odd) { - float64 e1r = float16_to_float64(e1 & 0xffff, true, s_std); - float64 e1c = float16_to_float64(e1 >> 16, true, s_std); - float64 e2r = float16_to_float64(e2 & 0xffff, true, s_std); - float64 e2c = float16_to_float64(e2 >> 16, true, s_std); + /* + * We need three different float_status for different parts of this + * operation: + * - the input conversion of the float16 values must use the + * f16-specific float_status, so that the FPCR.FZ16 control is applied + * - operations on float32 including the final accumulation must use + * the normal float_status, so that FPCR.FZ is applied + * - we have pre-set-up copy of s_std which is set to round-to-odd, + * for the multiply (see below) + */ + float64 e1r = float16_to_float64(e1 & 0xffff, true, s_f16); + float64 e1c = float16_to_float64(e1 >> 16, true, s_f16); + float64 e2r = float16_to_float64(e2 & 0xffff, true, s_f16); + float64 e2c = float16_to_float64(e2 >> 16, true, s_f16); float64 t64; float32 t32; @@ -1019,20 +1030,23 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, } void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, void *vst, uint32_t desc) + void *vpm, CPUARMState *env, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) * 0x80008000u; uint16_t *pn = vpn, *pm = vpm; - float_status fpst_odd, fpst_std; + float_status fpst_odd, fpst_std, fpst_f16; /* - * Make a copy of float_status because this operation does not - * update the cumulative fp exception status. It also produces - * default nans. Make a second copy with round-to-odd -- see above. + * Make copies of fp_status and fp_status_f16, because this operation + * does not update the cumulative fp exception status. It also + * produces default NaNs. We also need a second copy of fp_status with + * round-to-odd -- see above. */ - fpst_std = *(float_status *)vst; + fpst_f16 = env->vfp.fp_status_f16; + fpst_std = env->vfp.fp_status; set_default_nan_mode(true, &fpst_std); + set_default_nan_mode(true, &fpst_f16); fpst_odd = fpst_std; set_float_rounding_mode(float_round_to_odd, &fpst_odd); @@ -1052,7 +1066,8 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, uint32_t m = *(uint32_t *)(vzm + H1_4(col)); m = f16mop_adj_pair(m, pcol, 0); - *a = f16_dotadd(*a, n, m, &fpst_std, &fpst_odd); + *a = f16_dotadd(*a, n, m, + &fpst_f16, &fpst_std, &fpst_odd); } col += 4; pcol >>= 4; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index a50a419af2..ae42ddef7b 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -334,8 +334,29 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, return true; } -TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a, - MO_32, FPST_FPCR_F16, gen_helper_sme_fmopa_h) +static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz, + gen_helper_gvec_5_ptr *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->sub); + TCGv_ptr za, zn, zm, pn, pm; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + za = get_tile(s, esz, a->zad); + zn = vec_full_reg_ptr(s, a->zn); + zm = vec_full_reg_ptr(s, a->zm); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + + fn(za, zn, zm, pn, pm, tcg_env, tcg_constant_i32(desc)); + return true; +} + +TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_env, a, + MO_32, gen_helper_sme_fmopa_h) TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, FPST_FPCR, gen_helper_sme_fmopa_s) TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, From 5e8e4f098d872818aa9a138a171200068b81c8d1 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 18:22:46 +0100 Subject: [PATCH 2659/2884] target/xtensa: Correct assert condition in handle_interrupt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit ad18376b90c8101 we added an assert that the level value was in-bounds for the array we're about to index into. However, the assert condition is wrong -- env->config->interrupt_vector is an array of uint32_t, so we should bounds check the index against ARRAY_SIZE(...), not against sizeof(). Resolves: Coverity CID 1507131 Fixes: ad18376b90c8101 ("target/xtensa: Assert that interrupt level is within bounds") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Acked-by: Max Filippov <jcmvbkbc@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240731172246.3682311-1-peter.maydell@linaro.org --- target/xtensa/exc_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c index 0514c2c1f3..ca629f071d 100644 --- a/target/xtensa/exc_helper.c +++ b/target/xtensa/exc_helper.c @@ -171,7 +171,7 @@ static void handle_interrupt(CPUXtensaState *env) if (level > 1) { /* env->config->nlevel check should have ensured this */ - assert(level < sizeof(env->config->interrupt_vector)); + assert(level < ARRAY_SIZE(env->config->interrupt_vector)); env->sregs[EPC1 + level - 1] = env->pc; env->sregs[EPS2 + level - 2] = env->sregs[PS]; From 9e3b9f27658cce308b7f71b19a4ec31749575414 Mon Sep 17 00:00:00 2001 From: Hans <sungdgdhtryrt@gmail.com> Date: Sat, 11 May 2024 22:11:36 +0200 Subject: [PATCH 2660/2884] rtl8139: Fix behaviour for old kernels. Old linux kernel rtl8139 drivers (ex. debian 2.1) uses outb to set the rx mode for RxConfig. Unfortunatelly qemu does not support outb for RxConfig. Signed-off-by: Hans <sungdgdhtryrt@gmail.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- hw/net/rtl8139.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 897c86ec41..03a204ef8a 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -2738,7 +2738,11 @@ static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val) } break; - + case RxConfig: + DPRINTF("RxConfig write(b) val=0x%02x\n", val); + rtl8139_RxConfig_write(s, + (rtl8139_RxConfig_read(s) & 0xFFFFFF00) | val); + break; default: DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr, val); From f1595ceb9aad36a6c1da95bcb77ab9509b38822d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Mon, 1 Jul 2024 20:58:04 +0900 Subject: [PATCH 2661/2884] virtio-net: Ensure queue index fits with RSS Ensure the queue index points to a valid queue when software RSS enabled. The new calculation matches with the behavior of Linux's TAP device with the RSS eBPF program. Fixes: 4474e37a5b3a ("virtio-net: implement RX RSS processing") Reported-by: Zhibin Hu <huzhibin5@huawei.com> Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- hw/net/virtio-net.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 8f30972708..5635620a31 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1905,7 +1905,8 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) { int index = virtio_net_process_rss(nc, buf, size, &extra_hdr); if (index >= 0) { - NetClientState *nc2 = qemu_get_subqueue(n->nic, index); + NetClientState *nc2 = + qemu_get_subqueue(n->nic, index % n->curr_queue_pairs); return virtio_net_receive_rcu(nc2, buf, size, true); } } From f937309fbdbb48c354220a3e7110c202ae4aa7fa Mon Sep 17 00:00:00 2001 From: thomas <east.moutain.yang@gmail.com> Date: Fri, 12 Jul 2024 11:10:53 +0800 Subject: [PATCH 2662/2884] virtio-net: Fix network stall at the host side waiting for kick Patch 06b12970174 ("virtio-net: fix network stall under load") added double-check to test whether the available buffer size can satisfy the request or not, in case the guest has added some buffers to the avail ring simultaneously after the first check. It will be lucky if the available buffer size becomes okay after the double-check, then the host can send the packet to the guest. If the buffer size still can't satisfy the request, even if the guest has added some buffers, viritio-net would stall at the host side forever. The patch enables notification and checks whether the guest has added some buffers since last check of available buffers when the available buffers are insufficient. If no buffer is added, return false, else recheck the available buffers in the loop. If the available buffers are sufficient, disable notification and return true. Changes: 1. Change the return type of virtqueue_get_avail_bytes() from void to int, it returns an opaque that represents the shadow_avail_idx of the virtqueue on success, else -1 on error. 2. Add a new API: virtio_queue_enable_notification_and_check(), it takes an opaque as input arg which is returned from virtqueue_get_avail_bytes(). It enables notification firstly, then checks whether the guest has added some buffers since last check of available buffers or not by virtio_queue_poll(), return ture if yes. The patch also reverts patch "06b12970174". The case below can reproduce the stall. Guest 0 +--------+ | iperf | ---------------> | server | Host | +--------+ +--------+ | ... | iperf |---- | client |---- Guest n +--------+ | +--------+ | | iperf | ---------------> | server | +--------+ Boot many guests from qemu with virtio network: qemu ... -netdev tap,id=net_x \ -device virtio-net-pci-non-transitional,\ iommu_platform=on,mac=xx:xx:xx:xx:xx:xx,netdev=net_x Each guest acts as iperf server with commands below: iperf3 -s -D -i 10 -p 8001 iperf3 -s -D -i 10 -p 8002 The host as iperf client: iperf3 -c guest_IP -p 8001 -i 30 -w 256k -P 20 -t 40000 iperf3 -c guest_IP -p 8002 -i 30 -w 256k -P 20 -t 40000 After some time, the host loses connection to the guest, the guest can send packet to the host, but can't receive packet from the host. It's more likely to happen if SWIOTLB is enabled in the guest, allocating and freeing bounce buffer takes some CPU ticks, copying from/to bounce buffer takes more CPU ticks, compared with that there is no bounce buffer in the guest. Once the rate of producing packets from the host approximates the rate of receiveing packets in the guest, the guest would loop in NAPI. receive packets --- | | v | free buf virtnet_poll | | v | add buf to avail ring --- | | need kick the host? | NAPI continues v receive packets --- | | v | free buf virtnet_poll | | v | add buf to avail ring --- | v ... ... On the other hand, the host fetches free buf from avail ring, if the buf in the avail ring is not enough, the host notifies the guest the event by writing the avail idx read from avail ring to the event idx of used ring, then the host goes to sleep, waiting for the kick signal from the guest. Once the guest finds the host is waiting for kick singal (in virtqueue_kick_prepare_split()), it kicks the host. The host may stall forever at the sequences below: Host Guest ------------ ----------- fetch buf, send packet receive packet --- ... ... | fetch buf, send packet add buf | ... add buf virtnet_poll buf not enough avail idx-> add buf | read avail idx add buf | add buf --- receive packet --- write event idx ... | wait for kick add buf virtnet_poll ... | --- no more packet, exit NAPI In the first loop of NAPI above, indicated in the range of virtnet_poll above, the host is sending packets while the guest is receiving packets and adding buffers. step 1: The buf is not enough, for example, a big packet needs 5 buf, but the available buf count is 3. The host read current avail idx. step 2: The guest adds some buf, then checks whether the host is waiting for kick signal, not at this time. The used ring is not empty, the guest continues the second loop of NAPI. step 3: The host writes the avail idx read from avail ring to used ring as event idx via virtio_queue_set_notification(q->rx_vq, 1). step 4: At the end of the second loop of NAPI, recheck whether kick is needed, as the event idx in the used ring written by the host is beyound the range of kick condition, the guest will not send kick signal to the host. Fixes: 06b12970174 ("virtio-net: fix network stall under load") Cc: qemu-stable@nongnu.org Signed-off-by: Wencheng Yang <east.moutain.yang@gmail.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- hw/net/virtio-net.c | 26 +++++++++------- hw/virtio/virtio.c | 64 +++++++++++++++++++++++++++++++++++--- include/hw/virtio/virtio.h | 19 +++++++++-- 3 files changed, 91 insertions(+), 18 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 5635620a31..08aa0b65e3 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1641,24 +1641,28 @@ static bool virtio_net_can_receive(NetClientState *nc) static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) { + int opaque; + unsigned int in_bytes; VirtIONet *n = q->n; - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(q->rx_vq, 1); - /* To avoid a race condition where the guest has made some buffers - * available after the above check but before notification was - * enabled, check for available buffers again. - */ - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { + while (virtio_queue_empty(q->rx_vq) || n->mergeable_rx_bufs) { + opaque = virtqueue_get_avail_bytes(q->rx_vq, &in_bytes, NULL, + bufsize, 0); + /* Buffer is enough, disable notifiaction */ + if (bufsize <= in_bytes) { + break; + } + + if (virtio_queue_enable_notification_and_check(q->rx_vq, opaque)) { + /* Guest has added some buffers, try again */ + continue; + } else { return 0; } } virtio_queue_set_notification(q->rx_vq, 0); + return 1; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 397c261c3c..9e10cbc058 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -744,6 +744,60 @@ int virtio_queue_empty(VirtQueue *vq) } } +static bool virtio_queue_split_poll(VirtQueue *vq, unsigned shadow_idx) +{ + if (unlikely(!vq->vring.avail)) { + return false; + } + + return (uint16_t)shadow_idx != vring_avail_idx(vq); +} + +static bool virtio_queue_packed_poll(VirtQueue *vq, unsigned shadow_idx) +{ + VRingPackedDesc desc; + VRingMemoryRegionCaches *caches; + + if (unlikely(!vq->vring.desc)) { + return false; + } + + caches = vring_get_region_caches(vq); + if (!caches) { + return false; + } + + vring_packed_desc_read(vq->vdev, &desc, &caches->desc, + shadow_idx, true); + + return is_desc_avail(desc.flags, vq->shadow_avail_wrap_counter); +} + +static bool virtio_queue_poll(VirtQueue *vq, unsigned shadow_idx) +{ + if (virtio_device_disabled(vq->vdev)) { + return false; + } + + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + return virtio_queue_packed_poll(vq, shadow_idx); + } else { + return virtio_queue_split_poll(vq, shadow_idx); + } +} + +bool virtio_queue_enable_notification_and_check(VirtQueue *vq, + int opaque) +{ + virtio_queue_set_notification(vq, 1); + + if (opaque >= 0) { + return virtio_queue_poll(vq, (unsigned)opaque); + } else { + return false; + } +} + static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { @@ -1442,9 +1496,9 @@ err: goto done; } -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes) +int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, unsigned max_in_bytes, + unsigned max_out_bytes) { uint16_t desc_size; VRingMemoryRegionCaches *caches; @@ -1477,7 +1531,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, caches); } - return; + return (int)vq->shadow_avail_idx; err: if (in_bytes) { *in_bytes = 0; @@ -1485,6 +1539,8 @@ err: if (out_bytes) { *out_bytes = 0; } + + return -1; } int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index d2a1938757..0fcbc5c0c6 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -273,9 +273,13 @@ void qemu_put_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, VirtQueueElement *elem); int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, unsigned int out_bytes); -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes); +/** + * Return <0 on error or an opaque >=0 to pass to + * virtio_queue_enable_notification_and_check on success. + */ +int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, unsigned max_in_bytes, + unsigned max_out_bytes); void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); @@ -309,6 +313,15 @@ int virtio_queue_ready(VirtQueue *vq); int virtio_queue_empty(VirtQueue *vq); +/** + * Enable notification and check whether guest has added some + * buffers since last call to virtqueue_get_avail_bytes. + * + * @opaque: value returned from virtqueue_get_avail_bytes + */ +bool virtio_queue_enable_notification_and_check(VirtQueue *vq, + int opaque); + void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t idx); /* Host binding interface. */ From bb1326abd9df64692d5c4f6dadf0719a2f1d1c2d Mon Sep 17 00:00:00 2001 From: Laurent Vivier <lvivier@redhat.com> Date: Thu, 4 Jul 2024 14:48:31 +0200 Subject: [PATCH 2663/2884] net: update netdev stream/dgram man page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the description of "-netdev stream" and "-netdev dgram" in the QEMU manpage. Add some examples on how to use them. Fixes: 5166fe0ae46d ("qapi: net: add stream and dgram netdevs") Signed-off-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- qemu-options.hx | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 369ae81d7c..52143cfb8f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3353,6 +3353,120 @@ SRST -device e1000,netdev=n1,mac=52:54:00:12:34:56 \\ -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 +``-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off]`` + Configure a network backend to connect to another QEMU virtual machine or a proxy using a TCP/IP socket. + + ``server=on|off`` + if ``on`` create a server socket + + ``addr.host=host,addr.port=port`` + socket address to listen on (server=on) or connect to (server=off) + + ``to=maxport`` + if present, this is range of possible addresses, with port between ``port`` and ``maxport``. + + ``numeric=on|off`` + if ``on`` ``host`` and ``port`` are guaranteed to be numeric, otherwise a name resolution should be attempted (default: ``off``) + + ``keep-alive=on|off`` + enable keep-alive when connecting to this socket. Not supported for passive sockets. + + ``mptcp=on|off`` + enable multipath TCP + + ``ipv4=on|off`` + whether to accept IPv4 addresses, default to try both IPv4 and IPv6 + + ``ipv6=on|off`` + whether to accept IPv6 addresses, default to try both IPv4 and IPv6 + + Example (two guests connected using a TCP/IP socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev stream,id=net0,server=on,addr.type=inet,addr.host=localhost,addr.port=1234 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev stream,id=net0,server=off,addr.type=inet,addr.host=localhost,addr.port=1234 + +``-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor`` + Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented socket file descriptor. + + ``server=on|off`` + if ``on`` create a server socket + + ``addr.str=file-descriptor`` + file descriptor number to use as a socket + +``-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]`` + Configure a network backend to connect to a multicast address. + + ``remote.host=maddr,remote.port=port`` + multicast address + + ``local.host=addr`` + specify the host address to send packets from + + Example: + + .. parsed-literal:: + + # launch one QEMU instance + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,remote.type=inet,remote.host=224.0.0.1,remote.port=1234 + # launch another QEMU instance on same "bus" + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev dgram,id=net0,remote.type=inet,remote.host=224.0.0.1,remote.port=1234 + # launch yet another QEMU instance on same "bus" + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:58 \\ + -netdev dgram,id=net0,remote.type=inet,remote.host=224.0.0.1,remote.port=1234 + +``-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=fd,local.str=file-descriptor]`` + Configure a network backend to connect to a multicast address using a UDP socket file descriptor. + + ``remote.host=maddr,remote.port=port`` + multicast address + + ``local.str=file-descriptor`` + File descriptor to use to send packets + +``-netdev dgram,id=str,local.type=inet,local.host=addr,local.port=port[,remote.type=inet,remote.host=addr,remote.port=port]`` + Configure a network backend to connect to another QEMU virtual + machine or a proxy using a datagram oriented unix domain socket. + + ``local.host=addr,local.port=port`` + IP address to use to send the packets from + + ``remote.host=addr,remote.port=port`` + Destination IP address + + Example (two guests connected using an UDP/IP socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,local.type=inet,local.host=localhost,local.port=1234,remote.type=inet,remote.host=localhost,remote.port=1235 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,local.type=inet,local.host=localhost,local.port=1235,remote.type=inet,remote.host=localhost,remote.port=1234 + +``-netdev dgram,id=str,local.type=fd,local.str=file-descriptor`` + Configure a network backend to connect to another QEMU virtual + machine or a proxy using a datagram oriented socket file descriptor. + + ``local.str=file-descriptor`` + File descriptor to use to send packets + ``-netdev l2tpv3,id=id,src=srcaddr,dst=dstaddr[,srcport=srcport][,dstport=dstport],txsession=txsession[,rxsession=rxsession][,ipv6=on|off][,udp=on|off][,cookie64=on|off][,counter=on|off][,pincounter=on|off][,txcookie=txcookie][,rxcookie=rxcookie][,offset=offset]`` Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3931) is a popular protocol to transport Ethernet (and other Layer 2) data From 178413a1031af79f1a224e021e3dfd422e3787df Mon Sep 17 00:00:00 2001 From: Laurent Vivier <lvivier@redhat.com> Date: Thu, 4 Jul 2024 14:48:32 +0200 Subject: [PATCH 2664/2884] net: update netdev stream man page with unix socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the description of "-netdev stream" with a unix domain socket. The code has been added but the man page has not been updated. Include an example how to use "-netdev stream" and "passt" in place of "-netdev user". ("passt" is a non privileged translation proxy between layer-2, like "-netdev stream", and layer-4 on host, like TCP, UDP, ICMP/ICMPv6 echo) Fixes: 13c6be96618c ("net: stream: add unix socket") Signed-off-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- qemu-options.hx | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 52143cfb8f..2614eea4d7 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3393,6 +3393,46 @@ SRST -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ -netdev stream,id=net0,server=off,addr.type=inet,addr.host=localhost,addr.port=1234 +``-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off]`` + Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented unix domain socket. + + ``server=on|off`` + if ``on`` create a server socket + + ``addr.path=path`` + filesystem path to use + + ``abstract=on|off`` + if ``on``, this is a Linux abstract socket address. + + ``tight=on|off`` + if false, pad an abstract socket address with enough null bytes to make it fill struct sockaddr_un member sun_path. + + Example (using passt as a replacement of -netdev user): + + .. parsed-literal:: + + # start passt server as a non privileged user + passt + UNIX domain socket bound at /tmp/passt_1.socket + # start QEMU to connect to passt + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0 \\ + -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket + + Example (two guests connected using a stream oriented unix domain socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + netdev stream,id=net0,server=on,addr.type=unix,addr.path=/tmp/qemu0 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/qemu0 + ``-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor`` Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented socket file descriptor. From 8e67680dcb24b5353b936b514f14949cc1935738 Mon Sep 17 00:00:00 2001 From: Laurent Vivier <lvivier@redhat.com> Date: Thu, 4 Jul 2024 14:48:33 +0200 Subject: [PATCH 2665/2884] net: update netdev dgram man page with unix socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the description of "-netdev dgram" with a unix domain socket. The code has been added but the man page has not been updated. Fixes: 784e7a253104 ("net: dgram: add unix socket") Signed-off-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- qemu-options.hx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 2614eea4d7..23a53a7190 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3500,6 +3500,29 @@ SRST -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ -netdev dgram,id=net0,local.type=inet,local.host=localhost,local.port=1235,remote.type=inet,remote.host=localhost,remote.port=1234 +``-netdev dgram,id=str,local.type=unix,local.path=path[,remote.type=unix,remote.path=path]`` + Configure a network backend to connect to another QEMU virtual + machine or a proxy using a datagram oriented unix socket. + + ``local.path=path`` + filesystem path to use to bind the socket + + ``remote.path=path`` + filesystem path to use as a destination (see sendto(2)) + + Example (two guests connected using an UDP/UNIX socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,local.type=unix,local.path=/tmp/qemu0,remote.type=unix,remote.path=/tmp/qemu1 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev dgram,id=net0,local.type=unix,local.path=/tmp/qemu1,remote.type=unix,remote.path=/tmp/qemu0 + ``-netdev dgram,id=str,local.type=fd,local.str=file-descriptor`` Configure a network backend to connect to another QEMU virtual machine or a proxy using a datagram oriented socket file descriptor. From f6a3158c825cc8265e1596cb9b2d0ea2f09d3a4e Mon Sep 17 00:00:00 2001 From: Laurent Vivier <lvivier@redhat.com> Date: Thu, 4 Jul 2024 14:48:34 +0200 Subject: [PATCH 2666/2884] net: update netdev stream man page with the reconnect parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "-netdev stream" supports a reconnect parameter that attempts to reconnect automatically the socket if it is disconnected. The code has been added but the man page has not been updated. Fixes: 148fbf0d58a6 ("net: stream: add a new option to automatically reconnect" Signed-off-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> --- qemu-options.hx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 23a53a7190..cee0da2014 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3353,7 +3353,7 @@ SRST -device e1000,netdev=n1,mac=52:54:00:12:34:56 \\ -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 -``-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off]`` +``-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off][,reconnect=seconds]`` Configure a network backend to connect to another QEMU virtual machine or a proxy using a TCP/IP socket. ``server=on|off`` @@ -3380,6 +3380,10 @@ SRST ``ipv6=on|off`` whether to accept IPv6 addresses, default to try both IPv4 and IPv6 + ``reconnect=seconds`` + for a client socket, if a socket is disconnected, then attempt a reconnect after the given number of seconds. + Setting this to zero disables this function. (default: 0) + Example (two guests connected using a TCP/IP socket): .. parsed-literal:: @@ -3391,9 +3395,9 @@ SRST # second VM |qemu_system| linux.img \\ -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ - -netdev stream,id=net0,server=off,addr.type=inet,addr.host=localhost,addr.port=1234 + -netdev stream,id=net0,server=off,addr.type=inet,addr.host=localhost,addr.port=1234,reconnect=5 -``-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off]`` +``-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect=seconds]`` Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented unix domain socket. ``server=on|off`` @@ -3408,6 +3412,10 @@ SRST ``tight=on|off`` if false, pad an abstract socket address with enough null bytes to make it fill struct sockaddr_un member sun_path. + ``reconnect=seconds`` + for a client socket, if a socket is disconnected, then attempt a reconnect after the given number of seconds. + Setting this to zero disables this function. (default: 0) + Example (using passt as a replacement of -netdev user): .. parsed-literal:: @@ -3431,9 +3439,9 @@ SRST # second VM |qemu_system| linux.img \\ -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ - -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/qemu0 + -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/qemu0,reconnect=5 -``-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor`` +``-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect=seconds]`` Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented socket file descriptor. ``server=on|off`` @@ -3442,6 +3450,10 @@ SRST ``addr.str=file-descriptor`` file descriptor number to use as a socket + ``reconnect=seconds`` + for a client socket, if a socket is disconnected, then attempt a reconnect after the given number of seconds. + Setting this to zero disables this function. (default: 0) + ``-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]`` Configure a network backend to connect to a multicast address. From 64f75f57f9d2c8c12ac6d9355fa5d3a2af5879ca Mon Sep 17 00:00:00 2001 From: David Woodhouse <dwmw@amazon.co.uk> Date: Tue, 9 Jul 2024 13:34:44 +0100 Subject: [PATCH 2667/2884] net: Reinstate '-net nic, model=help' output as documented in man page While refactoring the NIC initialization code, I broke '-net nic,model=help' which no longer outputs a list of available NIC models. Fixes: 2cdeca04adab ("net: report list of available models according to platform") Cc: qemu-stable@nongnu.org Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Jason Wang <jasowang@redhat.com> --- net/net.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/net/net.c b/net/net.c index 6938da05e0..2eb8bc9c0b 100644 --- a/net/net.c +++ b/net/net.c @@ -1139,6 +1139,21 @@ NICInfo *qemu_find_nic_info(const char *typename, bool match_default, return NULL; } +static bool is_nic_model_help_option(const char *model) +{ + if (model && is_help_option(model)) { + /* + * Trigger the help output by instantiating the hash table which + * will gather tha available models as they get registered. + */ + if (!nic_model_help) { + nic_model_help = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + } + return true; + } + return false; +} /* "I have created a device. Please configure it if you can" */ bool qemu_configure_nic_device(DeviceState *dev, bool match_default, @@ -1722,6 +1737,12 @@ void net_check_clients(void) static int net_init_client(void *dummy, QemuOpts *opts, Error **errp) { + const char *model = qemu_opt_get_del(opts, "model"); + + if (is_nic_model_help_option(model)) { + return 0; + } + return net_client_init(opts, false, errp); } @@ -1778,9 +1799,7 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) memset(ni, 0, sizeof(*ni)); ni->model = qemu_opt_get_del(opts, "model"); - if (!nic_model_help && !g_strcmp0(ni->model, "help")) { - nic_model_help = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); + if (is_nic_model_help_option(ni->model)) { return 0; } From d4392415c328f83b2e30517a3561be523874f441 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Fri, 2 Aug 2024 01:43:37 +0200 Subject: [PATCH 2668/2884] target/i386: SEV: fix mismatch in vcek-disabled property name The vcek-disabled property of the sev-snp-guest object is misspelled vcek-required (which I suppose would use the opposite polarity) in the call to object_class_property_add_bool(). Fix it. Reported-by: Zixi Chen <zixchen@redhat.com> Reviewed-by: Pankaj Gupta <pankaj.gupta@amd.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/sev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index a1157c0ede..a0d271f898 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2422,7 +2422,7 @@ sev_snp_guest_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, "author-key-enabled", sev_snp_guest_get_author_key_enabled, sev_snp_guest_set_author_key_enabled); - object_class_property_add_bool(oc, "vcek-required", + object_class_property_add_bool(oc, "vcek-disabled", sev_snp_guest_get_vcek_disabled, sev_snp_guest_set_vcek_disabled); object_class_property_add_str(oc, "host-data", From c80e22517f6cfbbbed20e859f146d331694e6488 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Thu, 27 Jun 2024 22:37:51 +0900 Subject: [PATCH 2669/2884] migration: Free removed SaveStateEntry This fixes LeakSanitizer warnings. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> --- migration/savevm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/savevm.c b/migration/savevm.c index deb57833f8..85958d7b09 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -874,6 +874,8 @@ int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, if (se) { savevm_state_handler_remove(se); + g_free(se->compat); + g_free(se); } return vmstate_register(obj, instance_id, vmsd, opaque); } From 84ac6fa12df3e96fdae8f3d992a7c2914c9a6ca5 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas <farosas@suse.de> Date: Thu, 1 Aug 2024 14:41:00 -0300 Subject: [PATCH 2670/2884] migration: Fix cleanup of iochannel in file migration The QIOChannelFile object already has its reference decremented by g_autoptr. Trying to unref an extra time causes: ERROR:../qom/object.c:1241:object_unref: assertion failed: (obj->ref > 0) Fixes: a701c03dec ("migration: Drop reference to QIOChannel if file seeking fails") Fixes: 6d3279655a ("migration: Fix file migration with fdset") Reported-by: Jim Fehlig <jfehlig@suse.com> Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> --- migration/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/migration/file.c b/migration/file.c index db870f2cf0..6451a21c86 100644 --- a/migration/file.c +++ b/migration/file.c @@ -112,7 +112,6 @@ void file_start_outgoing_migration(MigrationState *s, error_setg_errno(errp, errno, "failed to truncate migration file to offset %" PRIx64, offset); - object_unref(OBJECT(fioc)); return; } @@ -120,7 +119,6 @@ void file_start_outgoing_migration(MigrationState *s, ioc = QIO_CHANNEL(fioc); if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { - object_unref(OBJECT(fioc)); return; } qio_channel_set_name(ioc, "migration-file-outgoing"); From 0bd5b9284fa94a6242a0d27a46380d93e753488b Mon Sep 17 00:00:00 2001 From: Fabiano Rosas <farosas@suse.de> Date: Thu, 1 Aug 2024 14:41:01 -0300 Subject: [PATCH 2671/2884] migration/multifd: Fix multifd_send_setup cleanup when channel creation fails When a channel fails to create, the code currently just returns. This is wrong for two reasons: 1) Channel n+1 will not get to initialize it's semaphores, leading to an assert when terminate_threads tries to post to it: qemu-system-x86_64: ../util/qemu-thread-posix.c:92: qemu_mutex_lock_impl: Assertion `mutex->initialized' failed. 2) (theoretical) If channel n-1 already started creation it will defeat the purpose of the channels_created logic which is in place to avoid migrate_fd_cleanup() to run while channels are still being created. This cannot really happen today because the current failure cases for multifd_new_send_channel_create() are all synchronous, resulting from qio_channel_file_new_path() getting a bad filename. This would hit all channels equally. But I don't want to set a trap for future people, so have all channels try to create (even if failing), and only fail after the channels_created semaphore has been posted. While here, remove the error_report_err call. There's one already at migrate_fd_cleanup later on. Cc: qemu-stable@nongnu.org Reported-by: Jim Fehlig <jfehlig@suse.com> Fixes: b7b03eb614 ("migration/multifd: Add outgoing QIOChannelFile support") Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> --- migration/multifd.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 0b4cbaddfe..552f9723c8 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1156,7 +1156,6 @@ static bool multifd_new_send_channel_create(gpointer opaque, Error **errp) bool multifd_send_setup(void) { MigrationState *s = migrate_get_current(); - Error *local_err = NULL; int thread_count, ret = 0; uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); bool use_packets = multifd_use_packets(); @@ -1177,6 +1176,7 @@ bool multifd_send_setup(void) for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; + Error *local_err = NULL; qemu_sem_init(&p->sem, 0); qemu_sem_init(&p->sem_sync, 0); @@ -1196,7 +1196,8 @@ bool multifd_send_setup(void) p->write_flags = 0; if (!multifd_new_send_channel_create(p, &local_err)) { - return false; + migrate_set_error(s, local_err); + ret = -1; } } @@ -1209,24 +1210,27 @@ bool multifd_send_setup(void) qemu_sem_wait(&multifd_send_state->channels_created); } + if (ret) { + goto err; + } + for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; + Error *local_err = NULL; ret = multifd_send_state->ops->send_setup(p, &local_err); if (ret) { - break; + migrate_set_error(s, local_err); + goto err; } } - if (ret) { - migrate_set_error(s, local_err); - error_report_err(local_err); - migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, - MIGRATION_STATUS_FAILED); - return false; - } - return true; + +err: + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); + return false; } bool multifd_recv(void) From cb14095b3ba5dae8f9dfdcef0065060daffe61a4 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Fri, 2 Aug 2024 17:37:52 +0900 Subject: [PATCH 2672/2884] hvf: arm: Fix hvf_sysreg_read_cp() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed val from uint64_t to a pointer to uint64_t in hvf_sysreg_read, but didn't change its usage in hvf_sysreg_read_cp call. Fixes: e9e640148c ("hvf: arm: Raise an exception for sysreg by default") Reported-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240802-hvf-v1-1-e2c0292037e5@daynix.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/hvf/hvf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index c1496ad5be..ace83671b5 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1280,7 +1280,7 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) case SYSREG_ICC_SRE_EL1: case SYSREG_ICC_CTLR_EL1: /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ - if (hvf_sysreg_read_cp(cpu, reg, &val)) { + if (hvf_sysreg_read_cp(cpu, reg, val)) { return 0; } break; From 5b0c2742c839376b7e03c4654914aaec6a8a7b09 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich <iii@linux.ibm.com> Date: Thu, 1 Aug 2024 22:23:22 +0200 Subject: [PATCH 2673/2884] linux-user/elfload: Fix pr_pid values in core files Analyzing qemu-produced core dumps of multi-threaded apps runs into: (gdb) info threads [...] 21 Thread 0x3ff83cc0740 (LWP 9295) warning: Couldn't find general-purpose registers in core file. <unavailable> in ?? () The reason is that all pr_pid values are the same, because the same TaskState is used for all CPUs when generating NT_PRSTATUS notes. Fix by using TaskStates associated with individual CPUs. Cc: qemu-stable@nongnu.org Fixes: 243c47066253 ("linux-user/elfload: Write corefile elf header in one block") Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240801202340.21845-1-iii@linux.ibm.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- linux-user/elfload.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0d4dc1f6d1..b27dd01734 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -4102,8 +4102,7 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, size_t sz, off_t offset) bswap_phdr(phdr, 1); } -static void fill_prstatus_note(void *data, const TaskState *ts, - CPUState *cpu, int signr) +static void fill_prstatus_note(void *data, CPUState *cpu, int signr) { /* * Because note memory is only aligned to 4, and target_elf_prstatus @@ -4113,7 +4112,7 @@ static void fill_prstatus_note(void *data, const TaskState *ts, struct target_elf_prstatus prstatus = { .pr_info.si_signo = signr, .pr_cursig = signr, - .pr_pid = ts->ts_tid, + .pr_pid = get_task_state(cpu)->ts_tid, .pr_ppid = getppid(), .pr_pgrp = getpgrp(), .pr_sid = getsid(0), @@ -4428,8 +4427,7 @@ static int elf_core_dump(int signr, const CPUArchState *env) CPU_FOREACH(cpu_iter) { dptr = fill_note(&hptr, NT_PRSTATUS, "CORE", sizeof(struct target_elf_prstatus)); - fill_prstatus_note(dptr, ts, cpu_iter, - cpu_iter == cpu ? signr : 0); + fill_prstatus_note(dptr, cpu_iter, cpu_iter == cpu ? signr : 0); } if (dump_write(fd, header, data_offset) < 0) { From 4ec5ebea078ed3a16d6c7e612ff9c2e04ea73931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= <cleger@rivosinc.com> Date: Fri, 2 Aug 2024 16:54:17 +0200 Subject: [PATCH 2674/2884] qemu/osdep: Move close_all_open_fds() to oslib-posix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move close_all_open_fds() in oslib-posix, rename it qemu_close_all_open_fds() and export it. Signed-off-by: Clément Léger <cleger@rivosinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802145423.3232974-2-cleger@rivosinc.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- include/qemu/osdep.h | 7 +++++++ system/async-teardown.c | 37 +------------------------------------ util/oslib-posix.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 720ed21a7e..de77c5c254 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -757,6 +757,13 @@ static inline void qemu_reset_optind(void) int qemu_fdatasync(int fd); +/** + * qemu_close_all_open_fd: + * + * Close all open file descriptors + */ +void qemu_close_all_open_fd(void); + /** * Sync changes made to the memory mapped file back to the backing * storage. For POSIX compliant systems this will fallback diff --git a/system/async-teardown.c b/system/async-teardown.c index 396963c091..edf49e1007 100644 --- a/system/async-teardown.c +++ b/system/async-teardown.c @@ -26,40 +26,6 @@ static pid_t the_ppid; -/* - * Close all open file descriptors. - */ -static void close_all_open_fd(void) -{ - struct dirent *de; - int fd, dfd; - DIR *dir; - -#ifdef CONFIG_CLOSE_RANGE - int r = close_range(0, ~0U, 0); - if (!r) { - /* Success, no need to try other ways. */ - return; - } -#endif - - dir = opendir("/proc/self/fd"); - if (!dir) { - /* If /proc is not mounted, there is nothing that can be done. */ - return; - } - /* Avoid closing the directory. */ - dfd = dirfd(dir); - - for (de = readdir(dir); de; de = readdir(dir)) { - fd = atoi(de->d_name); - if (fd != dfd) { - close(fd); - } - } - closedir(dir); -} - static void hup_handler(int signal) { /* Check every second if this process has been reparented. */ @@ -85,9 +51,8 @@ static int async_teardown_fn(void *arg) /* * Close all file descriptors that might have been inherited from the * main qemu process when doing clone, needed to make libvirt happy. - * Not using close_range for increased compatibility with older kernels. */ - close_all_open_fd(); + qemu_close_all_open_fd(); /* Set up a handler for SIGHUP and unblock SIGHUP. */ sigaction(SIGHUP, &sa, NULL); diff --git a/util/oslib-posix.c b/util/oslib-posix.c index b090fe0eed..1e867efa47 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -807,3 +807,37 @@ int qemu_msync(void *addr, size_t length, int fd) return msync(addr, length, MS_SYNC); } + +/* + * Close all open file descriptors. + */ +void qemu_close_all_open_fd(void) +{ + struct dirent *de; + int fd, dfd; + DIR *dir; + +#ifdef CONFIG_CLOSE_RANGE + int r = close_range(0, ~0U, 0); + if (!r) { + /* Success, no need to try other ways. */ + return; + } +#endif + + dir = opendir("/proc/self/fd"); + if (!dir) { + /* If /proc is not mounted, there is nothing that can be done. */ + return; + } + /* Avoid closing the directory. */ + dfd = dirfd(dir); + + for (de = readdir(dir); de; de = readdir(dir)) { + fd = atoi(de->d_name); + if (fd != dfd) { + close(fd); + } + } + closedir(dir); +} From ffa28f9cf503e22bfe621b062d29cbdb9a0aa786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= <cleger@rivosinc.com> Date: Fri, 2 Aug 2024 16:54:18 +0200 Subject: [PATCH 2675/2884] qemu/osdep: Split qemu_close_all_open_fd() and add fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to make it cleaner, split qemu_close_all_open_fd() logic into multiple subfunctions (close with close_range(), with /proc/self/fd and fallback). Signed-off-by: Clément Léger <cleger@rivosinc.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802145423.3232974-3-cleger@rivosinc.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- util/oslib-posix.c | 50 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 1e867efa47..9b79fc7cff 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -808,27 +808,16 @@ int qemu_msync(void *addr, size_t length, int fd) return msync(addr, length, MS_SYNC); } -/* - * Close all open file descriptors. - */ -void qemu_close_all_open_fd(void) +static bool qemu_close_all_open_fd_proc(void) { struct dirent *de; int fd, dfd; DIR *dir; -#ifdef CONFIG_CLOSE_RANGE - int r = close_range(0, ~0U, 0); - if (!r) { - /* Success, no need to try other ways. */ - return; - } -#endif - dir = opendir("/proc/self/fd"); if (!dir) { /* If /proc is not mounted, there is nothing that can be done. */ - return; + return false; } /* Avoid closing the directory. */ dfd = dirfd(dir); @@ -840,4 +829,39 @@ void qemu_close_all_open_fd(void) } } closedir(dir); + + return true; +} + +static bool qemu_close_all_open_fd_close_range(void) +{ +#ifdef CONFIG_CLOSE_RANGE + int r = close_range(0, ~0U, 0); + if (!r) { + /* Success, no need to try other ways. */ + return true; + } +#endif + return false; +} + +static void qemu_close_all_open_fd_fallback(void) +{ + int open_max = sysconf(_SC_OPEN_MAX), i; + + /* Fallback */ + for (i = 0; i < open_max; i++) { + close(i); + } +} + +/* + * Close all open file descriptors. + */ +void qemu_close_all_open_fd(void) +{ + if (!qemu_close_all_open_fd_close_range() && + !qemu_close_all_open_fd_proc()) { + qemu_close_all_open_fd_fallback(); + } } From a9b5d6e536c2c728a059b36ad434322dd9329c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= <cleger@rivosinc.com> Date: Fri, 2 Aug 2024 16:54:19 +0200 Subject: [PATCH 2676/2884] net/tap: Factorize fd closing after forking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same code is used twice to actually close all open file descriptors after forking. Factorize it in a single place. Signed-off-by: Clément Léger <cleger@rivosinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802145423.3232974-4-cleger@rivosinc.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- net/tap.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/net/tap.c b/net/tap.c index 51f7aec39d..7b2d5d5703 100644 --- a/net/tap.c +++ b/net/tap.c @@ -385,6 +385,17 @@ static TAPState *net_tap_fd_init(NetClientState *peer, return s; } +static void close_all_fds_after_fork(int excluded_fd) +{ + int open_max = sysconf(_SC_OPEN_MAX), i; + + for (i = 3; i < open_max; i++) { + if (i != excluded_fd) { + close(i); + } + } +} + static void launch_script(const char *setup_script, const char *ifname, int fd, Error **errp) { @@ -400,13 +411,7 @@ static void launch_script(const char *setup_script, const char *ifname, return; } if (pid == 0) { - int open_max = sysconf(_SC_OPEN_MAX), i; - - for (i = 3; i < open_max; i++) { - if (i != fd) { - close(i); - } - } + close_all_fds_after_fork(fd); parg = args; *parg++ = (char *)setup_script; *parg++ = (char *)ifname; @@ -490,17 +495,11 @@ static int net_bridge_run_helper(const char *helper, const char *bridge, return -1; } if (pid == 0) { - int open_max = sysconf(_SC_OPEN_MAX), i; char *fd_buf = NULL; char *br_buf = NULL; char *helper_cmd = NULL; - for (i = 3; i < open_max; i++) { - if (i != sv[1]) { - close(i); - } - } - + close_all_fds_after_fork(sv[1]); fd_buf = g_strdup_printf("%s%d", "--fd=", sv[1]); if (strrchr(helper, ' ') || strrchr(helper, '\t')) { From 7532ca570a449bafe990a00f21ae41bff7709845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= <cleger@rivosinc.com> Date: Fri, 2 Aug 2024 16:54:20 +0200 Subject: [PATCH 2677/2884] qemu/osdep: Add excluded fd parameter to qemu_close_all_open_fd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order for this function to be usable by tap.c code, add a list of file descriptors that should not be closed. Signed-off-by: Clément Léger <cleger@rivosinc.com> Message-ID: <20240802145423.3232974-5-cleger@rivosinc.com> [rth: Use max_fd in qemu_close_all_open_fd_close_range] Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- include/qemu/osdep.h | 8 +++- system/async-teardown.c | 2 +- util/oslib-posix.c | 98 ++++++++++++++++++++++++++++++++++------- 3 files changed, 89 insertions(+), 19 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index de77c5c254..4cc4c32b14 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -760,9 +760,13 @@ int qemu_fdatasync(int fd); /** * qemu_close_all_open_fd: * - * Close all open file descriptors + * Close all open file descriptors except the ones supplied in the @skip array + * + * @skip: ordered array of distinct file descriptors that should not be closed + * if any, or NULL. + * @nskip: number of entries in the @skip array or 0 if @skip is NULL. */ -void qemu_close_all_open_fd(void); +void qemu_close_all_open_fd(const int *skip, unsigned int nskip); /** * Sync changes made to the memory mapped file back to the backing diff --git a/system/async-teardown.c b/system/async-teardown.c index edf49e1007..9148ee8d04 100644 --- a/system/async-teardown.c +++ b/system/async-teardown.c @@ -52,7 +52,7 @@ static int async_teardown_fn(void *arg) * Close all file descriptors that might have been inherited from the * main qemu process when doing clone, needed to make libvirt happy. */ - qemu_close_all_open_fd(); + qemu_close_all_open_fd(NULL, 0); /* Set up a handler for SIGHUP and unblock SIGHUP. */ sigaction(SIGHUP, &sa, NULL); diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 9b79fc7cff..11b35e48fb 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -808,11 +808,12 @@ int qemu_msync(void *addr, size_t length, int fd) return msync(addr, length, MS_SYNC); } -static bool qemu_close_all_open_fd_proc(void) +static bool qemu_close_all_open_fd_proc(const int *skip, unsigned int nskip) { struct dirent *de; int fd, dfd; DIR *dir; + unsigned int skip_start = 0, skip_end = nskip; dir = opendir("/proc/self/fd"); if (!dir) { @@ -823,8 +824,33 @@ static bool qemu_close_all_open_fd_proc(void) dfd = dirfd(dir); for (de = readdir(dir); de; de = readdir(dir)) { + bool close_fd = true; + + if (de->d_name[0] == '.') { + continue; + } fd = atoi(de->d_name); - if (fd != dfd) { + if (fd == dfd) { + continue; + } + + for (unsigned int i = skip_start; i < skip_end; i++) { + if (fd < skip[i]) { + /* We are below the next skipped fd, break */ + break; + } else if (fd == skip[i]) { + close_fd = false; + /* Restrict the range as we found fds matching start/end */ + if (i == skip_start) { + skip_start++; + } else if (i == skip_end) { + skip_end--; + } + break; + } + } + + if (close_fd) { close(fd); } } @@ -833,24 +859,60 @@ static bool qemu_close_all_open_fd_proc(void) return true; } -static bool qemu_close_all_open_fd_close_range(void) +static bool qemu_close_all_open_fd_close_range(const int *skip, + unsigned int nskip, + int open_max) { #ifdef CONFIG_CLOSE_RANGE - int r = close_range(0, ~0U, 0); - if (!r) { - /* Success, no need to try other ways. */ - return true; - } -#endif + int max_fd = open_max - 1; + int first = 0, last; + unsigned int cur_skip = 0; + int ret; + + do { + /* Find the start boundary of the range to close */ + while (cur_skip < nskip && first == skip[cur_skip]) { + cur_skip++; + first++; + } + + /* Find the upper boundary of the range to close */ + last = max_fd; + if (cur_skip < nskip) { + last = skip[cur_skip] - 1; + last = MIN(last, max_fd); + } + + /* With the adjustments to the range, we might be done. */ + if (first > last) { + break; + } + + ret = close_range(first, last, 0); + if (ret < 0) { + return false; + } + + first = last + 1; + } while (last < max_fd); + + return true; +#else return false; +#endif } -static void qemu_close_all_open_fd_fallback(void) +static void qemu_close_all_open_fd_fallback(const int *skip, unsigned int nskip, + int open_max) { - int open_max = sysconf(_SC_OPEN_MAX), i; + unsigned int cur_skip = 0; /* Fallback */ - for (i = 0; i < open_max; i++) { + for (int i = 0; i < open_max; i++) { + if (cur_skip < nskip && i == skip[cur_skip]) { + cur_skip++; + continue; + } close(i); } } @@ -858,10 +920,14 @@ static void qemu_close_all_open_fd_fallback(void) /* * Close all open file descriptors. */ -void qemu_close_all_open_fd(void) +void qemu_close_all_open_fd(const int *skip, unsigned int nskip) { - if (!qemu_close_all_open_fd_close_range() && - !qemu_close_all_open_fd_proc()) { - qemu_close_all_open_fd_fallback(); + int open_max = sysconf(_SC_OPEN_MAX); + + assert(skip != NULL || nskip == 0); + + if (!qemu_close_all_open_fd_close_range(skip, nskip, open_max) && + !qemu_close_all_open_fd_proc(skip, nskip)) { + qemu_close_all_open_fd_fallback(skip, nskip, open_max); } } From 9996a35c6433c0e019a1c05791299db5e63a5db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= <cleger@rivosinc.com> Date: Fri, 2 Aug 2024 16:54:21 +0200 Subject: [PATCH 2678/2884] net/tap: Use qemu_close_all_open_fd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using a slow implementation to close all open fd after forking, use qemu_close_all_open_fd(). Signed-off-by: Clément Léger <cleger@rivosinc.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802145423.3232974-6-cleger@rivosinc.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- net/tap.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/net/tap.c b/net/tap.c index 7b2d5d5703..3f90022c0b 100644 --- a/net/tap.c +++ b/net/tap.c @@ -387,13 +387,20 @@ static TAPState *net_tap_fd_init(NetClientState *peer, static void close_all_fds_after_fork(int excluded_fd) { - int open_max = sysconf(_SC_OPEN_MAX), i; + const int skip_fd[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, + excluded_fd}; + unsigned int nskip = ARRAY_SIZE(skip_fd); - for (i = 3; i < open_max; i++) { - if (i != excluded_fd) { - close(i); - } + /* + * skip_fd must be an ordered array of distinct fds, exclude + * excluded_fd if already included in the [STDIN_FILENO - STDERR_FILENO] + * range + */ + if (excluded_fd <= STDERR_FILENO) { + nskip--; } + + qemu_close_all_open_fd(skip_fd, nskip); } static void launch_script(const char *setup_script, const char *ifname, From 01bed0ff14bb94edc3be3c701e6d31679560d388 Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Mon, 29 Jul 2024 08:52:20 +0200 Subject: [PATCH 2679/2884] qapi: Refill doc comments to conform to conventions Sweep the entire documentation again. Last done in commit 209e64d9edf (qapi: Refill doc comments to conform to current conventions). To check the generated documentation does not change, I compared the generated HTML before and after this commit with "wdiff -3". Finds no differences. Comparing with diff is not useful, as the reflown paragraphs are visible there. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240729065220.860163-1-armbru@redhat.com> [Straightforward conflict with commit 442110bc6f3 resolved] --- qapi/block-core.json | 71 ++++++++++++++++++++-------------------- qapi/block-export.json | 8 ++--- qapi/block.json | 12 +++---- qapi/char.json | 8 ++--- qapi/control.json | 14 ++++---- qapi/crypto.json | 8 ++--- qapi/cxl.json | 42 ++++++++++++------------ qapi/dump.json | 2 +- qapi/ebpf.json | 2 +- qapi/introspect.json | 4 +-- qapi/job.json | 8 ++--- qapi/machine-target.json | 54 +++++++++++++++--------------- qapi/machine.json | 22 ++++++------- qapi/migration.json | 68 +++++++++++++++++++------------------- qapi/misc.json | 31 +++++++++--------- qapi/net.json | 27 +++++++++------ qapi/pci.json | 1 - qapi/qdev.json | 20 +++++------ qapi/qom.json | 38 ++++++++++----------- qapi/rocker.json | 4 +-- qapi/run-state.json | 12 +++---- qapi/sockets.json | 5 +-- qapi/stats.json | 6 ++-- qapi/transaction.json | 4 +-- qapi/ui.json | 27 +++++++-------- qapi/vfio.json | 8 ++--- qapi/virtio.json | 6 ++-- 27 files changed, 262 insertions(+), 250 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index f400b334c8..a1b7cdc58e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1158,11 +1158,11 @@ # # @query-nodes: If true, the command will query all the block nodes # that have a node name, in a list which will include "parent" -# information, but not "backing". If false or omitted, the +# information, but not "backing". If false or omitted, the # behavior is as before - query all the device backends, -# recursively including their "parent" and "backing". Filter nodes -# that were created implicitly are skipped over in this mode. -# (Since 2.3) +# recursively including their "parent" and "backing". Filter +# nodes that were created implicitly are skipped over in this +# mode. (Since 2.3) # # Returns: A list of @BlockStats for each virtual block devices. # @@ -1286,7 +1286,7 @@ # jobs, cancel the job # # @ignore: ignore the error, only report a QMP event (BLOCK_IO_ERROR -# or BLOCK_JOB_ERROR). The backup, mirror and commit block jobs +# or BLOCK_JOB_ERROR). The backup, mirror and commit block jobs # retry the failing request later and may still complete # successfully. The stream block job continues to stream and will # complete with an error. @@ -1574,7 +1574,7 @@ # for unlimited. # # @bitmap: The name of a dirty bitmap to use. Must be present if sync -# is "bitmap" or "incremental". Can be present if sync is "full" +# is "bitmap" or "incremental". Can be present if sync is "full" # or "top". Must not be present otherwise. # (Since 2.4 (drive-backup), 3.1 (blockdev-backup)) # @@ -1619,9 +1619,9 @@ # # @unstable: Member @x-perf is experimental. # -# .. note:: @on-source-error and @on-target-error only affect background -# I/O. If an error occurs during a guest write request, the device's -# rerror/werror actions will be used. +# .. note:: @on-source-error and @on-target-error only affect +# background I/O. If an error occurs during a guest write request, +# the device's rerror/werror actions will be used. # # Since: 4.2 ## @@ -1699,7 +1699,7 @@ # Takes a snapshot of a block device. # # Take a snapshot, by installing 'node' as the backing image of -# 'overlay'. Additionally, if 'node' is associated with a block +# 'overlay'. Additionally, if 'node' is associated with a block # device, the block device changes to using 'overlay' as its new # active image. # @@ -1738,7 +1738,7 @@ # Change the backing file in the image file metadata. This does not # cause QEMU to reopen the image file to reparse the backing filename # (it may, however, perform a reopen to change permissions from r/o -> -# r/w -> r/o, if needed). The new backing file string is written into +# r/w -> r/o, if needed). The new backing file string is written into # the image file metadata, and the QEMU internal strings are updated. # # @image-node-name: The name of the block driver state node of the @@ -1882,8 +1882,8 @@ # Start a point-in-time copy of a block device to a new destination. # The status of ongoing drive-backup operations can be checked with # query-block-jobs where the BlockJobInfo.type field has the value -# 'backup'. The operation can be stopped before it has completed using -# the block-job-cancel command. +# 'backup'. The operation can be stopped before it has completed +# using the block-job-cancel command. # # Features: # @@ -1913,8 +1913,8 @@ # Start a point-in-time copy of a block device to a new destination. # The status of ongoing blockdev-backup operations can be checked with # query-block-jobs where the BlockJobInfo.type field has the value -# 'backup'. The operation can be stopped before it has completed using -# the block-job-cancel command. +# 'backup'. The operation can be stopped before it has completed +# using the block-job-cancel command. # # Errors: # - If @device is not a valid block device, DeviceNotFound @@ -2832,7 +2832,7 @@ # # @speed: the maximum speed, in bytes per second # -# @on-error: the action to take on an error (default report). 'stop' +# @on-error: the action to take on an error (default report). 'stop' # and 'enospc' can only be used if the block device supports # io-status (see BlockInfo). (Since 1.3) # @@ -3034,8 +3034,8 @@ # semantics. # # This command will refuse to operate on any job that has not yet -# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that make -# use of the BLOCK_JOB_READY event, block-job-cancel or +# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that +# make use of the BLOCK_JOB_READY event, block-job-cancel or # block-job-complete will still need to be used as appropriate. # # @id: The job identifier. @@ -3351,7 +3351,7 @@ # Driver specific block device options for LUKS. # # @key-secret: the ID of a QCryptoSecret object providing the -# decryption key (since 2.6). Mandatory except when doing a +# decryption key (since 2.6). Mandatory except when doing a # metadata-only probe of the image. # # @header: block device holding a detached LUKS header. (since 9.0) @@ -4050,6 +4050,7 @@ # @path: path to the vhost-vdpa character device. # # Features: +# # @fdset: Member @path supports the special "/dev/fdset/N" path # (since 8.1) # @@ -4427,7 +4428,7 @@ # curl backend. URLs must start with "http://". # # @cookie: List of cookies to set; format is "name1=content1; -# name2=content2;" as explained by CURLOPT_COOKIE(3). Defaults to +# name2=content2;" as explained by CURLOPT_COOKIE(3). Defaults to # no cookies. # # @cookie-secret: ID of a QCryptoSecret object providing the cookie @@ -4447,7 +4448,7 @@ # curl backend. URLs must start with "https://". # # @cookie: List of cookies to set; format is "name1=content1; -# name2=content2;" as explained by CURLOPT_COOKIE(3). Defaults to +# name2=content2;" as explained by CURLOPT_COOKIE(3). Defaults to # no cookies. # # @sslverify: Whether to verify the SSL certificate's validity @@ -4653,10 +4654,10 @@ # # @driver: block driver name # -# @node-name: the node name of the new node (Since 2.0). This option +# @node-name: the node name of the new node (Since 2.0). This option # is required on the top level of blockdev-add. Valid node names # start with an alphabetic character and may contain only -# alphanumeric characters, '-', '.' and '_'. Their maximum length +# alphanumeric characters, '-', '.' and '_'. Their maximum length # is 31 characters. # # @discard: discard-related options (default: ignore) @@ -4664,7 +4665,7 @@ # @cache: cache-related options # # @read-only: whether the block device should be read-only (default: -# false). Note that some block drivers support only read-only +# false). Note that some block drivers support only read-only # access, either generally or in certain configurations. In this # case, the default value does not work and the option must be # specified explicitly. @@ -5231,8 +5232,8 @@ # monolithcFlat, twoGbMaxExtentSparse and twoGbMaxExtentFlat # formats. For monolithicFlat, only one entry is required; for # twoGbMaxExtent* formats, the number of entries required is -# calculated as extent_number = virtual_size / 2GB. Providing more -# extents than will be used is an error. +# calculated as extent_number = virtual_size / 2GB. Providing +# more extents than will be used is an error. # # @subformat: The subformat of the VMDK image. Default: # "monolithicSparse". @@ -5244,7 +5245,7 @@ # Default: ide. # # @hwversion: Hardware version. The meaningful options are "4" or -# "6". Default: "4". +# "6". Default: "4". # # @toolsversion: VMware guest tools version. Default: "2147483647" # (Since 6.2) @@ -5440,7 +5441,7 @@ ## # @BlockdevAmendOptionsQcow2: # -# Driver specific image amend options for qcow2. For now, only +# Driver specific image amend options for qcow2. For now, only # encryption options can be amended # # @encrypt: Encryption options to be amended @@ -5543,8 +5544,8 @@ # after this event and must be repaired (Since 2.2; before, every # BLOCK_IMAGE_CORRUPTED event was fatal) # -# .. note:: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event. +# .. note:: If action is "stop", a STOP event will eventually follow +# the BLOCK_IO_ERROR event. # # .. qmp-example:: # @@ -5590,8 +5591,8 @@ # field is a debugging aid for humans, it should not be parsed by # applications) (since: 2.2) # -# .. note:: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event. +# .. note:: If action is "stop", a STOP event will eventually follow +# the BLOCK_IO_ERROR event. # # Since: 0.13 # @@ -6046,9 +6047,9 @@ # # @name: the name of the internal snapshot to be created # -# .. note:: In a transaction, if @name is empty or any snapshot matching -# @name exists, the operation will fail. Only some image formats -# support it; for example, qcow2, and rbd. +# .. note:: In a transaction, if @name is empty or any snapshot +# matching @name exists, the operation will fail. Only some image +# formats support it; for example, qcow2, and rbd. # # Since: 1.7 ## diff --git a/qapi/block-export.json b/qapi/block-export.json index 3919a2d5b9..665d5fd026 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -163,8 +163,8 @@ # Options for exporting a block graph node on some (file) mountpoint # as a raw image. # -# @mountpoint: Path on which to export the block device via FUSE. This -# must point to an existing regular file. +# @mountpoint: Path on which to export the block device via FUSE. +# This must point to an existing regular file. # # @growable: Whether writes beyond the EOF should grow the block node # accordingly. (default: false) @@ -172,7 +172,7 @@ # @allow-other: If this is off, only qemu's user is allowed access to # this export. That cannot be changed even with chmod or chown. # Enabling this option will allow other users access to the export -# with the FUSE mount option "allow_other". Note that using +# with the FUSE mount option "allow_other". Note that using # allow_other as a non-root user requires user_allow_other to be # enabled in the global fuse.conf configuration file. In auto # mode (the default), the FUSE export driver will first attempt to @@ -199,7 +199,7 @@ # @queue-size: the size of virtqueue. Defaults to 256. # # @logical-block-size: Logical block size in bytes. Range [512, -# PAGE_SIZE] and must be power of 2. Defaults to 512 bytes. +# PAGE_SIZE] and must be power of 2. Defaults to 512 bytes. # # @serial: the serial number of virtio block device. Defaults to # empty string. diff --git a/qapi/block.json b/qapi/block.json index ce9490a367..e66666f5c6 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -454,8 +454,8 @@ # different group. In this case the limits specified in the # parameters will be applied to the new group only. # -# I/O limits can be disabled by setting all of them to 0. In this case -# the device will be removed from its group and the rest of its +# I/O limits can be disabled by setting all of them to 0. In this +# case the device will be removed from its group and the rest of its # members will not be affected. The 'group' parameter is ignored. # # Errors: @@ -519,10 +519,10 @@ # @id: The name or QOM path of the guest device. # # @boundaries: list of interval boundary values (see description in -# BlockLatencyHistogramInfo definition). If specified, all latency -# histograms are removed, and empty ones created for all io types -# with intervals corresponding to @boundaries (except for io -# types, for which specific boundaries are set through the +# BlockLatencyHistogramInfo definition). If specified, all +# latency histograms are removed, and empty ones created for all +# io types with intervals corresponding to @boundaries (except for +# io types, for which specific boundaries are set through the # following parameters). # # @boundaries-read: list of interval boundary values for read latency diff --git a/qapi/char.json b/qapi/char.json index 5e4aeb9799..ef58445cee 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -258,7 +258,7 @@ # @server: create server socket (default: true) # # @wait: wait for incoming connection on server sockets (default: -# false). Silently ignored with server: false. This use is +# false). Silently ignored with server: false. This use is # deprecated. # # @nodelay: set TCP_NODELAY socket option (default: false) @@ -388,9 +388,9 @@ # # @rows: console height, in chars # -# .. note:: The options are only effective when the VNC or SDL graphical -# display backend is active. They are ignored with the GTK, Spice, -# VNC and D-Bus display backends. +# .. note:: The options are only effective when the VNC or SDL +# graphical display backend is active. They are ignored with the +# GTK, Spice, VNC and D-Bus display backends. # # Since: 1.5 ## diff --git a/qapi/control.json b/qapi/control.json index 950443df9d..336386f79e 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -22,13 +22,14 @@ # "arguments": { "enable": [ "oob" ] } } # <- { "return": {} } # -# .. note:: This command is valid exactly when first connecting: it must -# be issued before any other command will be accepted, and will fail -# once the monitor is accepting other commands. -# (see :doc:`/interop/qmp-spec`) +# .. note:: This command is valid exactly when first connecting: it +# must be issued before any other command will be accepted, and +# will fail once the monitor is accepting other commands. (see +# :doc:`/interop/qmp-spec`) # -# .. note:: The QMP client needs to explicitly enable QMP capabilities, -# otherwise all the QMP capabilities will be turned off by default. +# .. note:: The QMP client needs to explicitly enable QMP +# capabilities, otherwise all the QMP capabilities will be turned +# off by default. # # Since: 0.13 ## @@ -150,7 +151,6 @@ # } # # This example has been shortened as the real response is too long. -# ## { 'command': 'query-commands', 'returns': ['CommandInfo'], 'allow-preconfig': true } diff --git a/qapi/crypto.json b/qapi/crypto.json index 39b191e8a2..97e02dbd59 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -44,13 +44,13 @@ # # The supported algorithms for computing content digests # -# @md5: MD5. Should not be used in any new code, legacy compat only +# @md5: MD5. Should not be used in any new code, legacy compat only # -# @sha1: SHA-1. Should not be used in any new code, legacy compat only +# @sha1: SHA-1. Should not be used in any new code, legacy compat only # # @sha224: SHA-224. (since 2.7) # -# @sha256: SHA-256. Current recommended strong hash. +# @sha256: SHA-256. Current recommended strong hash. # # @sha384: SHA-384. (since 2.7) # @@ -440,7 +440,7 @@ # # @iv: the random initialization vector used for encryption of this # particular secret. Should be a base64 encrypted string of the -# 16-byte IV. Mandatory if @keyid is given. Ignored if @keyid is +# 16-byte IV. Mandatory if @keyid is given. Ignored if @keyid is # absent. # # Features: diff --git a/qapi/cxl.json b/qapi/cxl.json index bdfac67c47..e9315d5b4d 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -369,8 +369,8 @@ # of memory by Device Physical Address within a single Dynamic # Capacity Region on a CXL Type 3 Device. # -# @offset: The offset (in bytes) to the start of the region -# where the extent belongs to. +# @offset: The offset (in bytes) to the start of the region where the +# extent belongs to. # # @len: The length of the extent in bytes. # @@ -404,16 +404,16 @@ # # @enable-shared-access: Capacity has already been allocated to a # different host using free, contiguous or prescriptive policy -# with a known tag. This policy then instructs the device to -# make the capacity with the specified tag available to an -# additional host. Capacity is implicit as it matches that -# already associated with the tag. Note that the extent list -# (and hence Device Physical Addresses) used are per host, so -# a device may use different representations on each host. -# The ordering of the extents provided to each host is indicated -# to the host using per extent sequence numbers generated by -# the device. Has a similar meaning for temporal sharing, but -# in that case there may be only one host involved. +# with a known tag. This policy then instructs the device to make +# the capacity with the specified tag available to an additional +# host. Capacity is implicit as it matches that already +# associated with the tag. Note that the extent list (and hence +# Device Physical Addresses) used are per host, so a device may +# use different representations on each host. The ordering of the +# extents provided to each host is indicated to the host using per +# extent sequence numbers generated by the device. Has a similar +# meaning for temporal sharing, but in that case there may be only +# one host involved. # # Since: 9.1 ## @@ -429,7 +429,7 @@ # # Initiate adding dynamic capacity extents to a host. This simulates # operations defined in Compute Express Link (CXL) Specification, -# Revision 3.1, Section 7.6.7.6.5. Note that, currently, establishing +# Revision 3.1, Section 7.6.7.6.5. Note that, currently, establishing # success or failure of the full Add Dynamic Capacity flow requires # out of band communication with the OS of the CXL host. # @@ -495,7 +495,7 @@ # # Initiate release of dynamic capacity extents from a host. This # simulates operations defined in Compute Express Link (CXL) -# Specification, Revision 3.1, Section 7.6.7.6.6. Note that, +# Specification, Revision 3.1, Section 7.6.7.6.6. Note that, # currently, success or failure of the full Release Dynamic Capacity # flow requires out of band communication with the OS of the CXL host. # @@ -514,13 +514,13 @@ # from the host. Instead, the host immediately looses access to # the released capacity. # -# @sanitize-on-release: Bit[5] of the "Flags" field in Compute -# Express Link (CXL) Specification, Revision 3.1, Table 7-71. -# When set, the device should sanitize all released capacity as -# a result of this request. This ensures that all user data -# and metadata is made permanently unavailable by whatever -# means is appropriate for the media type. Note that changing -# encryption keys is not sufficient. +# @sanitize-on-release: Bit[5] of the "Flags" field in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-71. When set, +# the device should sanitize all released capacity as a result of +# this request. This ensures that all user data and metadata is +# made permanently unavailable by whatever means is appropriate +# for the media type. Note that changing encryption keys is not +# sufficient. # # @region: The "Region Number" field as defined in Compute Express # Link Specification, Revision 3.1, Table 7-71. Valid range diff --git a/qapi/dump.json b/qapi/dump.json index d8145dad97..d7826c0e32 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -55,7 +55,7 @@ # allows using gdb to process the core file. # # IMPORTANT: this option can make QEMU allocate several gigabytes -# of RAM. This can happen for a large guest, or a malicious guest +# of RAM. This can happen for a large guest, or a malicious guest # pretending to be large. # # Also, paging=true has the following limitations: diff --git a/qapi/ebpf.json b/qapi/ebpf.json index e500b5a744..db19ae850f 100644 --- a/qapi/ebpf.json +++ b/qapi/ebpf.json @@ -8,7 +8,7 @@ # = eBPF Objects # # eBPF object is an ELF binary that contains the eBPF program and eBPF -# map description(BTF). Overall, eBPF object should contain the +# map description(BTF). Overall, eBPF object should contain the # program and enough metadata to create/load eBPF with libbpf. As the # eBPF maps/program should correspond to QEMU, the eBPF can't be used # from different QEMU build. diff --git a/qapi/introspect.json b/qapi/introspect.json index b15052ec21..14df049580 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -42,8 +42,8 @@ # with different meta-types). # # .. note:: The QAPI schema is also used to help define *internal* -# interfaces, by defining QAPI types. These are not part of the QMP -# wire ABI, and therefore not returned by this command. +# interfaces, by defining QAPI types. These are not part of the +# QMP wire ABI, and therefore not returned by this command. # # Since: 2.5 ## diff --git a/qapi/job.json b/qapi/job.json index b3957207a4..cfc3beedd2 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -138,7 +138,7 @@ # # The job will pause as soon as possible, which means transitioning # into the PAUSED state if it was RUNNING, or into STANDBY if it was -# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. +# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. # # Cancelling a paused job automatically resumes it. # @@ -200,9 +200,9 @@ # dismiss enabled. # # This command will refuse to operate on any job that has not yet -# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that make -# use of JOB_READY event, job-cancel or job-complete will still need -# to be used as appropriate. +# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that +# make use of JOB_READY event, job-cancel or job-complete will still +# need to be used as appropriate. # # @id: The job identifier. # diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 00bbecc905..1a394c08f5 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -12,9 +12,10 @@ # Virtual CPU model. # # A CPU model consists of the name of a CPU definition, to which delta -# changes are applied (e.g. features added/removed). Most magic values -# that an architecture might require should be hidden behind the name. -# However, if required, architectures can expose relevant properties. +# changes are applied (e.g. features added/removed). Most magic +# values that an architecture might require should be hidden behind +# the name. However, if required, architectures can expose relevant +# properties. # # @name: the name of the CPU definition the model is based on # @@ -46,13 +47,13 @@ # # .. note:: When a non-migration-safe CPU model is expanded in static # mode, some features enabled by the CPU model may be omitted, -# because they can't be implemented by a static CPU model definition -# (e.g. cache info passthrough and PMU passthrough in x86). If you -# need an accurate representation of the features enabled by a -# non-migration-safe CPU model, use @full. If you need a static -# representation that will keep ABI compatibility even when changing -# QEMU version or machine-type, use @static (but keep in mind that -# some features may be omitted). +# because they can't be implemented by a static CPU model +# definition (e.g. cache info passthrough and PMU passthrough in +# x86). If you need an accurate representation of the features +# enabled by a non-migration-safe CPU model, use @full. If you +# need a static representation that will keep ABI compatibility +# even when changing QEMU version or machine-type, use @static (but +# keep in mind that some features may be omitted). # # Since: 2.8 ## @@ -155,11 +156,11 @@ # Some architectures may not support comparing CPU models. s390x # supports comparing CPU models. # -# @modela: description of the first CPU model to compare, referred to as -# "model A" in CpuModelCompareResult +# @modela: description of the first CPU model to compare, referred to +# as "model A" in CpuModelCompareResult # -# @modelb: description of the second CPU model to compare, referred to as -# "model B" in CpuModelCompareResult +# @modelb: description of the second CPU model to compare, referred to +# as "model B" in CpuModelCompareResult # # Returns: a CpuModelCompareInfo describing how both CPU models # compare @@ -185,7 +186,8 @@ # # Baseline two CPU models, @modela and @modelb, creating a compatible # third model. The created model will always be a static, -# migration-safe CPU model (see "static" CPU model expansion for details). +# migration-safe CPU model (see "static" CPU model expansion for +# details). # # This interface can be used by tooling to create a compatible CPU # model out two CPU models. The created CPU model will be identical @@ -242,12 +244,12 @@ # # @model: the expanded CpuModelInfo. # -# @deprecated-props: a list of properties that are flagged as deprecated -# by the CPU vendor. The list depends on the CpuModelExpansionType: -# "static" properties are a subset of the enabled-properties for -# the expanded model; "full" properties are a set of properties -# that are deprecated across all models for the architecture. -# (since: 9.1). +# @deprecated-props: a list of properties that are flagged as +# deprecated by the CPU vendor. The list depends on the +# CpuModelExpansionType: "static" properties are a subset of the +# enabled-properties for the expanded model; "full" properties are +# a set of properties that are deprecated across all models for +# the architecture. (since: 9.1). # # Since: 2.8 ## @@ -265,9 +267,9 @@ # @query-cpu-model-expansion: # # Expands a given CPU model, @model, (or a combination of CPU model + -# additional options) to different granularities, specified by -# @type, allowing tooling to get an understanding what a specific -# CPU model looks like in QEMU under a certain configuration. +# additional options) to different granularities, specified by @type, +# allowing tooling to get an understanding what a specific CPU model +# looks like in QEMU under a certain configuration. # # This interface can be used to query the "host" CPU model. # @@ -288,7 +290,7 @@ # Using query-cpu-model-expansion while using these is not advised. # # Some architectures may not support all expansion types. s390x -# supports "full" and "static". Arm only supports "full". +# supports "full" and "static". Arm only supports "full". # # @model: description of the CPU model to expand # @@ -355,7 +357,7 @@ # CPU model attributes that prevent the CPU from running. If the QOM # property is read-only, that means there's no known way to make the # CPU model run in the current host. Implementations that choose not -# to provide specific information return the property name "type". If +# to provide specific information return the property name "type". If # the property is read-write, it means that it MAY be possible to run # the CPU model in the current host if that property is changed. # Management software can use it as hints to suggest or choose an diff --git a/qapi/machine.json b/qapi/machine.json index fcfd249e2d..4582e58f7d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -27,8 +27,8 @@ # @loongarch64: since 7.1 # # .. note:: The resulting QMP strings can be appended to the -# "qemu-system-" prefix to produce the corresponding QEMU executable -# name. This is true even for "qemu-system-x86_64". +# "qemu-system-" prefix to produce the corresponding QEMU +# executable name. This is true even for "qemu-system-x86_64". # # Since: 3.0 ## @@ -371,8 +371,8 @@ # # .. note:: A guest may or may not respond to this command. This # command returning does not indicate that a guest has accepted the -# request or that it has shut down. Many guests will respond to this -# command by prompting the user in some way. +# request or that it has shut down. Many guests will respond to +# this command by prompting the user in some way. # # .. qmp-example:: # @@ -437,7 +437,7 @@ # @inject-nmi: # # Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or -# all CPUs (ppc64). The command fails when the guest doesn't support +# all CPUs (ppc64). The command fails when the guest doesn't support # injecting. # # Since: 0.14 @@ -988,8 +988,8 @@ # @cluster-id: cluster number within the parent container the CPU # belongs to (since 7.1) # -# @module-id: module number within the parent container the CPU belongs -# to (since 9.1) +# @module-id: module number within the parent container the CPU +# belongs to (since 9.1) # # @core-id: core number within the parent container the CPU belongs to # @@ -1132,8 +1132,8 @@ # - If no balloon device is present, DeviceNotActive # # .. note:: This command just issues a request to the guest. When it -# returns, the balloon size may not have changed. A guest can change -# the balloon size independent of this command. +# returns, the balloon size may not have changed. A guest can +# change the balloon size independent of this command. # # Since: 0.14 # @@ -1659,8 +1659,8 @@ # The members other than @cpus and @maxcpus define a topology of # containers. # -# The ordering from highest/coarsest to lowest/finest is: -# @drawers, @books, @sockets, @dies, @clusters, @cores, @threads. +# The ordering from highest/coarsest to lowest/finest is: @drawers, +# @books, @sockets, @dies, @clusters, @cores, @threads. # # Different architectures support different subsets of topology # containers. diff --git a/qapi/migration.json b/qapi/migration.json index 073b67c052..7324571e92 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -142,8 +142,8 @@ # # @postcopy-paused: during postcopy but paused. (since 3.0) # -# @postcopy-recover-setup: setup phase for a postcopy recovery process, -# preparing for a recovery phase to start. (since 9.1) +# @postcopy-recover-setup: setup phase for a postcopy recovery +# process, preparing for a recovery phase to start. (since 9.1) # # @postcopy-recover: trying to recover from a paused postcopy. (since # 3.0) @@ -245,10 +245,10 @@ # blocked. Present and non-empty when migration is blocked. # (since 6.0) # -# @dirty-limit-throttle-time-per-round: Maximum throttle time -# (in microseconds) of virtual CPUs each dirty ring full round, -# which shows how MigrationCapability dirty-limit affects the -# guest during live migration. (Since 8.1) +# @dirty-limit-throttle-time-per-round: Maximum throttle time (in +# microseconds) of virtual CPUs each dirty ring full round, which +# shows how MigrationCapability dirty-limit affects the guest +# during live migration. (Since 8.1) # # @dirty-limit-ring-full-time: Estimated average dirty ring full time # (in microseconds) for each dirty ring full round. The value @@ -381,7 +381,7 @@ # Migration capabilities enumeration # # @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length -# Encoding). This feature allows us to minimize migration traffic +# Encoding). This feature allows us to minimize migration traffic # for certain work loads, by sending compressed difference of the # pages # @@ -393,8 +393,8 @@ # efficiently. This essentially saves 1MB of zeroes per block on # the wire. Enabling requires source and target VM to support # this feature. To enable it is sufficient to enable the -# capability on the source VM. The feature is disabled by default. -# (since 1.6) +# capability on the source VM. The feature is disabled by +# default. (since 1.6) # # @events: generate events for each migration state change (since 2.4) # @@ -562,9 +562,9 @@ # @zstd: use zstd compression method. # # @qpl: use qpl compression method. Query Processing Library(qpl) is -# based on the deflate compression algorithm and use the Intel -# In-Memory Analytics Accelerator(IAA) accelerated compression -# and decompression. (Since 9.1) +# based on the deflate compression algorithm and use the Intel +# In-Memory Analytics Accelerator(IAA) accelerated compression and +# decompression. (Since 9.1) # # @uadk: use UADK library compression method. (Since 9.1) # @@ -790,13 +790,13 @@ # migration, the compression level is an integer between 0 and 9, # where 0 means no compression, 1 means the best compression # speed, and 9 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) +# more CPU. Defaults to 1. (Since 5.0) # # @multifd-zstd-level: Set the compression level to be used in live # migration, the compression level is an integer between 0 and 20, # where 0 means no compression, 1 means the best compression # speed, and 20 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) +# more CPU. Defaults to 1. (Since 5.0) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to # aliases for the purpose of dirty bitmap migration. Such aliases @@ -965,13 +965,13 @@ # migration, the compression level is an integer between 0 and 9, # where 0 means no compression, 1 means the best compression # speed, and 9 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) +# more CPU. Defaults to 1. (Since 5.0) # # @multifd-zstd-level: Set the compression level to be used in live # migration, the compression level is an integer between 0 and 20, # where 0 means no compression, 1 means the best compression # speed, and 20 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) +# more CPU. Defaults to 1. (Since 5.0) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to # aliases for the purpose of dirty bitmap migration. Such aliases @@ -1169,13 +1169,13 @@ # migration, the compression level is an integer between 0 and 9, # where 0 means no compression, 1 means the best compression # speed, and 9 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) +# more CPU. Defaults to 1. (Since 5.0) # # @multifd-zstd-level: Set the compression level to be used in live # migration, the compression level is an integer between 0 and 20, # where 0 means no compression, 1 means the best compression # speed, and 20 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) +# more CPU. Defaults to 1. (Since 5.0) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to # aliases for the purpose of dirty bitmap migration. Such aliases @@ -1201,7 +1201,7 @@ # Defaults to 1. (Since 8.1) # # @mode: Migration mode. See description in @MigMode. Default is -# 'normal'. (Since 8.2) +# 'normal'. (Since 8.2) # # @zero-page-detection: Whether and how to detect zero pages. # See description in @ZeroPageDetection. Default is 'multifd'. @@ -1459,8 +1459,8 @@ # # Cancel the current executing migration process. # -# .. note:: This command succeeds even if there is no migration process -# running. +# .. note:: This command succeeds even if there is no migration +# process running. # # Since: 0.14 # @@ -1604,7 +1604,7 @@ # should not be used. # # 4. The uri argument should have the Uniform Resource Identifier -# of default destination VM. This connection will be bound to +# of default destination VM. This connection will be bound to # default network. # # 5. For now, number of migration streams is restricted to one, @@ -1650,7 +1650,6 @@ # "filename": "/tmp/migfile", # "offset": "0x1000" } } ] } } # <- { "return": {} } -# ## { 'command': 'migrate', 'data': {'*uri': 'str', @@ -1671,7 +1670,8 @@ # # @exit-on-error: Exit on incoming migration failure. Default true. # When set to false, the failure triggers a MIGRATION event, and -# error details could be retrieved with query-migrate. (since 9.1) +# error details could be retrieved with query-migrate. +# (since 9.1) # # Since: 2.3 # @@ -1938,9 +1938,9 @@ # @UNPLUG_PRIMARY: # # Emitted from source side of a migration when migration state is -# WAIT_UNPLUG. Device was unplugged by guest operating system. Device -# resources in QEMU are kept on standby to be able to re-plug it in -# case of migration failure. +# WAIT_UNPLUG. Device was unplugged by guest operating system. +# Device resources in QEMU are kept on standby to be able to re-plug +# it in case of migration failure. # # @device-id: QEMU device id of the unplugged device # @@ -2084,16 +2084,16 @@ # This mode tracks page modification per each vCPU separately. It # requires that KVM accelerator property "dirty-ring-size" is set. # -# @calc-time: time period for which dirty page rate is calculated. -# By default it is specified in seconds, but the unit can be set +# @calc-time: time period for which dirty page rate is calculated. By +# default it is specified in seconds, but the unit can be set # explicitly with @calc-time-unit. Note that larger @calc-time # values will typically result in smaller dirty page rates because -# page dirtying is a one-time event. Once some page is counted -# as dirty during @calc-time period, further writes to this page -# will not increase dirty page rate anymore. +# page dirtying is a one-time event. Once some page is counted as +# dirty during @calc-time period, further writes to this page will +# not increase dirty page rate anymore. # -# @calc-time-unit: time unit in which @calc-time is specified. -# By default it is seconds. (Since 8.2) +# @calc-time-unit: time unit in which @calc-time is specified. By +# default it is seconds. (Since 8.2) # # @sample-pages: number of sampled pages per each GiB of guest memory. # Default value is 512. For 4KiB guest pages this corresponds to diff --git a/qapi/misc.json b/qapi/misc.json index 4a6f3baeae..559b66f201 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -26,7 +26,7 @@ # @skipauth: whether to skip authentication. Only applies to "vnc" # and "spice" protocols # -# @tls: whether to perform TLS. Only applies to the "spice" protocol +# @tls: whether to perform TLS. Only applies to the "spice" protocol # # Since: 0.14 # @@ -104,8 +104,8 @@ # Returns a list of information about each iothread. # # .. note:: This list excludes the QEMU main loop thread, which is not -# declared using the ``-object iothread`` command-line option. It is -# always the main thread of the process. +# declared using the ``-object iothread`` command-line option. It +# is always the main thread of the process. # # Returns: a list of @IOThreadInfo for each iothread # @@ -141,8 +141,8 @@ # guest remains paused once migration finishes, as if the ``-S`` # option was passed on the command line. # -# In the "suspended" state, it will completely stop the VM and cause -# a transition to the "paused" state. (Since 9.0) +# In the "suspended" state, it will completely stop the VM and +# cause a transition to the "paused" state. (Since 9.0) # # .. qmp-example:: # @@ -158,15 +158,15 @@ # # Since: 0.14 # -# .. note:: This command will succeed if the guest is currently running. -# It will also succeed if the guest is in the "inmigrate" state; in -# this case, the effect of the command is to make sure the guest -# starts once migration finishes, removing the effect of the ``-S`` -# command line option if it was passed. +# .. note:: This command will succeed if the guest is currently +# running. It will also succeed if the guest is in the "inmigrate" +# state; in this case, the effect of the command is to make sure +# the guest starts once migration finishes, removing the effect of +# the ``-S`` command line option if it was passed. # # If the VM was previously suspended, and not been reset or woken, -# this command will transition back to the "suspended" state. (Since -# 9.0) +# this command will transition back to the "suspended" state. +# (Since 9.0) # # .. qmp-example:: # @@ -227,8 +227,8 @@ # # Known limitations: # -# * This command is stateless, this means that commands that -# depend on state information (such as getfd) might not work. +# * This command is stateless, this means that commands that depend +# on state information (such as getfd) might not work. # # * Commands that prompt the user for data don't currently work. # @@ -341,7 +341,8 @@ # # .. note:: The list of fd sets is shared by all monitor connections. # -# .. note:: If @fdset-id is not specified, a new fd set will be created. +# .. note:: If @fdset-id is not specified, a new fd set will be +# created. # # Since: 1.2 # diff --git a/qapi/net.json b/qapi/net.json index 31b3417d65..87fc0d0b28 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -22,9 +22,9 @@ # # Since: 0.14 # -# .. note:: Not all network adapters support setting link status. This -# command will succeed even if the network adapter does not support -# link status notification. +# .. note:: Not all network adapters support setting link status. +# This command will succeed even if the network adapter does not +# support link status notification. # # .. qmp-example:: # @@ -403,7 +403,7 @@ # Connect a client to a netmap-enabled NIC or to a VALE switch port # # @ifname: Either the name of an existing network interface supported -# by netmap, or the name of a VALE port (created on the fly). A +# by netmap, or the name of a VALE port (created on the fly). A # VALE port name is in the form 'valeXXX:YYY', where XXX and YYY # are non-negative integers. XXX identifies a switch and YYY # identifies a port of the switch. VALE ports having the same XXX @@ -535,13 +535,13 @@ # interfaces that are in host mode and also with the host. # # @start-address: The starting IPv4 address to use for the interface. -# Must be in the private IP range (RFC 1918). Must be specified +# Must be in the private IP range (RFC 1918). Must be specified # along with @end-address and @subnet-mask. This address is used # as the gateway address. The subsequent address up to and # including end-address are placed in the DHCP pool. # # @end-address: The DHCP IPv4 range end address to use for the -# interface. Must be in the private IP range (RFC 1918). Must be +# interface. Must be in the private IP range (RFC 1918). Must be # specified along with @start-address and @subnet-mask. # # @subnet-mask: The IPv4 subnet mask to use on the interface. Must be @@ -556,7 +556,7 @@ # network vmnet interface should be added to. If set, no DHCP # service is provided for this interface and network communication # is allowed only with other interfaces added to this network -# identified by the UUID. Requires at least macOS Big Sur 11.0. +# identified by the UUID. Requires at least macOS Big Sur 11.0. # # Since: 7.1 ## @@ -575,20 +575,20 @@ # vmnet (shared mode) network backend. # # Allows traffic originating from the vmnet interface to reach the -# Internet through a network address translator (NAT). The vmnet +# Internet through a network address translator (NAT). The vmnet # interface can communicate with the host and with other shared mode # interfaces on the same subnet. If no DHCP settings, subnet mask and # IPv6 prefix specified, the interface can communicate with any of # other interfaces in shared mode. # # @start-address: The starting IPv4 address to use for the interface. -# Must be in the private IP range (RFC 1918). Must be specified +# Must be in the private IP range (RFC 1918). Must be specified # along with @end-address and @subnet-mask. This address is used # as the gateway address. The subsequent address up to and # including end-address are placed in the DHCP pool. # # @end-address: The DHCP IPv4 range end address to use for the -# interface. Must be in the private IP range (RFC 1918). Must be +# interface. Must be in the private IP range (RFC 1918). Must be # specified along with @start-address and @subnet-mask. # # @subnet-mask: The IPv4 subnet mask to use on the interface. Must be @@ -703,12 +703,19 @@ # Available netdev drivers. # # @l2tpv3: since 2.1 +# # @vhost-vdpa: since 5.1 +# # @vmnet-host: since 7.1 +# # @vmnet-shared: since 7.1 +# # @vmnet-bridged: since 7.1 +# # @stream: since 7.2 +# # @dgram: since 7.2 +# # @af-xdp: since 8.2 # # Since: 2.7 diff --git a/qapi/pci.json b/qapi/pci.json index ec28f1d9b4..78bee57b77 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -310,6 +310,5 @@ # } # # This example has been shortened as the real response is too long. -# ## { 'command': 'query-pci', 'returns': ['PciInfo'] } diff --git a/qapi/qdev.json b/qapi/qdev.json index e91ca0309d..53d147c7b4 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -59,8 +59,8 @@ # the 'docs/qdev-device-use.txt' file. # # 3. It's possible to list device properties by running QEMU with -# the ``-device DEVICE,help`` command-line argument, where DEVICE -# is the device's name. +# the ``-device DEVICE,help`` command-line argument, where +# DEVICE is the device's name. # # .. qmp-example:: # @@ -94,13 +94,13 @@ # # .. note:: When this command completes, the device may not be removed # from the guest. Hot removal is an operation that requires guest -# cooperation. This command merely requests that the guest begin the -# hot removal process. Completion of the device removal process is -# signaled with a DEVICE_DELETED event. Guest reset will -# automatically complete removal for all devices. If a guest-side -# error in the hot removal process is detected, the device will not -# be removed and a DEVICE_UNPLUG_GUEST_ERROR event is sent. Some -# errors cannot be detected. +# cooperation. This command merely requests that the guest begin +# the hot removal process. Completion of the device removal +# process is signaled with a DEVICE_DELETED event. Guest reset +# will automatically complete removal for all devices. If a +# guest-side error in the hot removal process is detected, the +# device will not be removed and a DEVICE_UNPLUG_GUEST_ERROR event +# is sent. Some errors cannot be detected. # # Since: 0.14 # @@ -123,7 +123,7 @@ # # Emitted whenever the device removal completion is acknowledged by # the guest. At this point, it's safe to reuse the specified device -# ID. Device removal can be initiated by the guest or by HMP/QMP +# ID. Device removal can be initiated by the guest or by HMP/QMP # commands. # # @device: the device's ID if it has one diff --git a/qapi/qom.json b/qapi/qom.json index fc035f126a..321ccd708a 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -356,7 +356,7 @@ # filter list. "head" means the filter is inserted at the head of # the filter list, before any existing filters. "tail" means the # filter is inserted at the tail of the filter list, behind any -# existing filters (default). "id=<id>" means the filter is +# existing filters (default). "id=<id>" means the filter is # inserted before or behind the filter specified by <id>, # depending on the @insert property. (default: "tail") # @@ -620,8 +620,8 @@ # .. note:: prealloc=true and reserve=false cannot be set at the same # time. With reserve=true, the behavior depends on the operating # system: for example, Linux will not reserve swap space for shared -# file mappings -- "not applicable". In contrast, reserve=false will -# bail out if it cannot be configured accordingly. +# file mappings -- "not applicable". In contrast, reserve=false +# will bail out if it cannot be configured accordingly. # # Since: 2.1 ## @@ -646,9 +646,9 @@ # @align: the base address alignment when QEMU mmap(2)s @mem-path. # Some backend stores specified by @mem-path require an alignment # different than the default one used by QEMU, e.g. the device DAX -# /dev/dax0.0 requires 2M alignment rather than 4K. In such cases, -# users can specify the required alignment via this option. 0 -# selects a default alignment (currently the page size). +# /dev/dax0.0 requires 2M alignment rather than 4K. In such +# cases, users can specify the required alignment via this option. +# 0 selects a default alignment (currently the page size). # (default: 0) # # @offset: the offset into the target file that the region starts at. @@ -709,7 +709,7 @@ # # @hugetlbsize: the hugetlb page size on systems that support multiple # hugetlb page sizes (it must be a power of 2 value supported by -# the system). 0 selects a default page size. This option is +# the system). 0 selects a default page size. This option is # ignored if @hugetlb is false. (default: 0) # # @seal: if true, create a sealed-file, which will block further @@ -930,17 +930,17 @@ # # @handle: SEV firmware handle (default: 0) # -# @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating the VM. -# The newer KVM_SEV_INIT2 interface, from Linux >= 6.10, syncs -# additional vCPU state when initializing the VMSA structures, -# which will result in a different guest measurement. Set -# this to 'on' to force compatibility with older QEMU or kernel -# versions that rely on legacy KVM_SEV_INIT behavior. 'auto' -# will behave identically to 'on', but will automatically -# switch to using KVM_SEV_INIT2 if the user specifies any -# additional options that require it. If set to 'off', QEMU -# will require KVM_SEV_INIT2 unconditionally. -# (default: off) (since 9.1) +# @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating +# the VM. The newer KVM_SEV_INIT2 interface, from Linux >= 6.10, +# syncs additional vCPU state when initializing the VMSA +# structures, which will result in a different guest measurement. +# Set this to 'on' to force compatibility with older QEMU or kernel +# versions that rely on legacy KVM_SEV_INIT behavior. 'auto' will +# behave identically to 'on', but will automatically switch to +# using KVM_SEV_INIT2 if the user specifies any additional options +# that require it. If set to 'off', QEMU will require +# KVM_SEV_INIT2 unconditionally. +# (default: off) (since 9.1) # # Since: 2.12 ## @@ -992,7 +992,7 @@ # @vcek-disabled: Guests are by default allowed to choose between VLEK # (Versioned Loaded Endorsement Key) or VCEK (Versioned Chip # Endorsement Key) when requesting attestation reports from -# firmware. Set this to true to disable the use of VCEK. +# firmware. Set this to true to disable the use of VCEK. # (default: false) (since: 9.1) # # Since: 9.1 diff --git a/qapi/rocker.json b/qapi/rocker.json index 2e63dcb3d6..6950ca9602 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -288,8 +288,8 @@ # # @ttl-check: perform TTL check # -# .. note:: Optional members may or may not appear in the group depending -# if they're relevant to the group type. +# .. note:: Optional members may or may not appear in the group +# depending if they're relevant to the group type. # # Since: 2.4 ## diff --git a/qapi/run-state.json b/qapi/run-state.json index 287691ca0e..ce95cfa46b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -527,20 +527,20 @@ # Hyper-V specific guest panic information (HV crash MSRs) # # @arg1: for Windows, STOP code for the guest crash. For Linux, -# an error code. +# an error code. # # @arg2: for Windows, first argument of the STOP. For Linux, the -# guest OS ID, which has the kernel version in bits 16-47 -# and 0x8100 in bits 48-63. +# guest OS ID, which has the kernel version in bits 16-47 and +# 0x8100 in bits 48-63. # # @arg3: for Windows, second argument of the STOP. For Linux, the -# program counter of the guest. +# program counter of the guest. # # @arg4: for Windows, third argument of the STOP. For Linux, the -# RAX register (x86) or the stack pointer (aarch64) of the guest. +# RAX register (x86) or the stack pointer (aarch64) of the guest. # # @arg5: for Windows, fourth argument of the STOP. For x86 Linux, the -# stack pointer of the guest. +# stack pointer of the guest. # # Since: 2.9 ## diff --git a/qapi/sockets.json b/qapi/sockets.json index e76fdb9925..6a95023315 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -29,6 +29,7 @@ # @InetSocketAddressBase: # # @host: host part of the address +# # @port: port part of the address ## { 'struct': 'InetSocketAddressBase', @@ -104,8 +105,8 @@ # # @port: port # -# .. note:: String types are used to allow for possible future hostname -# or service resolution support. +# .. note:: String types are used to allow for possible future +# hostname or service resolution support. # # Since: 2.8 ## diff --git a/qapi/stats.json b/qapi/stats.json index efbbe26244..8902ef94e0 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -117,10 +117,10 @@ # information for that target. # # @target: the kind of objects to query. Note that each possible -# target may enable additional filtering options +# target may enable additional filtering options # -# @providers: which providers to request statistics from, and optionally -# which named values to return within each provider +# @providers: which providers to request statistics from, and +# optionally which named values to return within each provider # # Since: 7.1 ## diff --git a/qapi/transaction.json b/qapi/transaction.json index b0ae3437eb..021e383496 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -238,8 +238,8 @@ # - Any errors from commands in the transaction # # .. note:: The transaction aborts on the first failure. Therefore, -# there will be information on only one failed operation returned in -# an error condition, and subsequent actions will not have been +# there will be information on only one failed operation returned +# in an error condition, and subsequent actions will not have been # attempted. # # Since: 1.1 diff --git a/qapi/ui.json b/qapi/ui.json index 5daca5168c..8c8464faac 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -48,8 +48,8 @@ # @password: the new password # # @connected: How to handle existing clients when changing the -# password. If nothing is specified, defaults to 'keep'. For VNC, -# only 'keep' is currently implemented. +# password. If nothing is specified, defaults to 'keep'. For +# VNC, only 'keep' is currently implemented. # # Since: 7.0 ## @@ -107,10 +107,11 @@ # - '+INT' where INT is the number of seconds from now (integer) # - 'INT' where INT is the absolute time in seconds # -# .. note:: Time is relative to the server and currently there is no way -# to coordinate server time with client time. It is not recommended -# to use the absolute time version of the @time parameter unless -# you're sure you are on the same machine as the QEMU instance. +# .. note:: Time is relative to the server and currently there is no +# way to coordinate server time with client time. It is not +# recommended to use the absolute time version of the @time +# parameter unless you're sure you are on the same machine as the +# QEMU instance. # # Since: 7.0 ## @@ -624,7 +625,7 @@ # @id: vnc server name. # # @server: A list of @VncBasincInfo describing all listening sockets. -# The list can be empty (in case the vnc server is disabled). It +# The list can be empty (in case the vnc server is disabled). It # also may have multiple entries: normal + websocket, possibly # also ipv4 + ipv6 in the future. # @@ -719,8 +720,8 @@ # # @client: client information # -# .. note:: This event is emitted before any authentication takes place, -# thus the authentication ID is not provided. +# .. note:: This event is emitted before any authentication takes +# place, thus the authentication ID is not provided. # # Since: 0.13 # @@ -1266,7 +1267,7 @@ # Since: 2.6 # # .. note:: The consoles are visible in the qom tree, under -# ``/backend/console[$index]``. They have a device link and head +# ``/backend/console[$index]``. They have a device link and head # property, so it is possible to map which console belongs to which # device and display. # @@ -1416,11 +1417,11 @@ # # @left-command-key: Enable/disable forwarding of left command key to # guest. Allows command-tab window switching on the host without -# sending this key to the guest when "off". Defaults to "on" +# sending this key to the guest when "off". Defaults to "on" # # @full-grab: Capture all key presses, including system combos. This # requires accessibility permissions, since it performs a global -# grab on key events. (default: off) See +# grab on key events. (default: off) See # https://support.apple.com/en-in/guide/mac-help/mh32356/mac # # @swap-opt-cmd: Swap the Option and Command keys so that their key @@ -1432,7 +1433,7 @@ # "off". (Since 8.2) # # @zoom-interpolation: Apply interpolation to smooth output when -# zoom-to-fit is enabled. Defaults to "off". (Since 9.0) +# zoom-to-fit is enabled. Defaults to "off". (Since 9.0) # # Since: 7.0 ## diff --git a/qapi/vfio.json b/qapi/vfio.json index 40cbcde02e..eccca82068 100644 --- a/qapi/vfio.json +++ b/qapi/vfio.json @@ -15,16 +15,16 @@ # # @running: The device is running. # -# @stop-copy: The device is stopped and its internal state is available -# for reading. +# @stop-copy: The device is stopped and its internal state is +# available for reading. # # @resuming: The device is stopped and its internal state is available # for writing. # # @running-p2p: The device is running in the P2P quiescent state. # -# @pre-copy: The device is running, tracking its internal state and its -# internal state is available for reading. +# @pre-copy: The device is running, tracking its internal state and +# its internal state is available for reading. # # @pre-copy-p2p: The device is running in the P2P quiescent state, # tracking its internal state and its internal state is available diff --git a/qapi/virtio.json b/qapi/virtio.json index 26df8b3064..2529c2d8b2 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -568,9 +568,9 @@ # .. note:: last_avail_idx will not be displayed in the case where the # selected VirtIODevice has a running vhost device and the # VirtIODevice VirtQueue index (queue) does not exist for the -# corresponding vhost device vhost_virtqueue. Also, shadow_avail_idx -# will not be displayed in the case where the selected VirtIODevice -# has a running vhost device. +# corresponding vhost device vhost_virtqueue. Also, +# shadow_avail_idx will not be displayed in the case where the +# selected VirtIODevice has a running vhost device. # # Since: 7.2 # From ef71d8209f5786c4e68b5ac7dbc0da7a43f0ed4e Mon Sep 17 00:00:00 2001 From: Josh Junon <junon@oro.sh> Date: Fri, 2 Aug 2024 16:07:03 +0200 Subject: [PATCH 2680/2884] qmp: Fix higher half vaddrs for [p]memsave Fixes higher-half address parsing for QMP commands `[p]memsave`. Signed-off-by: Josh Junon <junon@oro.sh> Message-ID: <20240802140704.13591-1-junon@oro.sh> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Subject tweaked, and one PRId64 updated to PRIu64] Signed-off-by: Markus Armbruster <armbru@redhat.com> --- qapi/machine.json | 11 +++++++++-- system/cpus.c | 12 ++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/qapi/machine.json b/qapi/machine.json index 4582e58f7d..d4317435e7 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -852,7 +852,11 @@ # <- { "return": {} } ## { 'command': 'memsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} } + 'data': { + 'val': 'uint64', + 'size': 'size', + 'filename': 'str', + '*cpu-index': 'int' } } ## # @pmemsave: @@ -878,7 +882,10 @@ # <- { "return": {} } ## { 'command': 'pmemsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } + 'data': { + 'val': 'uint64', + 'size': 'size', + 'filename': 'str' } } ## # @Memdev: diff --git a/system/cpus.c b/system/cpus.c index 5e3a988a0a..1c818ff682 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -792,14 +792,14 @@ int vm_stop_force_state(RunState state) } } -void qmp_memsave(int64_t addr, int64_t size, const char *filename, +void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, bool has_cpu, int64_t cpu_index, Error **errp) { FILE *f; - uint32_t l; + uint64_t l; CPUState *cpu; uint8_t buf[1024]; - int64_t orig_addr = addr, orig_size = size; + uint64_t orig_addr = addr, orig_size = size; if (!has_cpu) { cpu_index = 0; @@ -823,7 +823,7 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, if (l > size) l = size; if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { - error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 " specified", orig_addr, orig_size); goto exit; } @@ -840,11 +840,11 @@ exit: fclose(f); } -void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, +void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, Error **errp) { FILE *f; - uint32_t l; + uint64_t l; uint8_t buf[1024]; f = fopen(filename, "wb"); From ac63755b20013ec6a3d2aef4538d37dc90bc3d10 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 5 Aug 2024 10:31:24 +1000 Subject: [PATCH 2681/2884] target/i386: Fix VSIB decode With normal SIB, index == 4 indicates no index. With VSIB, there is no exception for VR4/VR12. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2474 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240805003130.1421051-3-richard.henderson@linaro.org Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/decode-new.c.inc | 3 ++- target/i386/tcg/translate.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index d2da1d396d..b22210f45d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1811,7 +1811,8 @@ static int decode_modrm(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } else { op->has_ea = true; op->n = -1; - decode->mem = gen_lea_modrm_0(env, s, get_modrm(s, env)); + decode->mem = gen_lea_modrm_0(env, s, modrm, + decode->e.vex_class == 12); } return modrm; } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 95bad55bf4..b72864bf01 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1523,7 +1523,7 @@ typedef struct AddressParts { } AddressParts; static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, - int modrm) + int modrm, bool is_vsib) { int def_seg, base, index, scale, mod, rm; target_long disp; @@ -1552,7 +1552,7 @@ static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, int code = x86_ldub_code(env, s); scale = (code >> 6) & 3; index = ((code >> 3) & 7) | REX_X(s); - if (index == 4) { + if (index == 4 && !is_vsib) { index = -1; /* no index */ } base = (code & 7) | REX_B(s); @@ -1682,21 +1682,21 @@ static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a, bool is_vsib) static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) { - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); TCGv ea = gen_lea_modrm_1(s, a, false); gen_lea_v_seg(s, ea, a.def_seg, s->override); } static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) { - (void)gen_lea_modrm_0(env, s, modrm); + (void)gen_lea_modrm_0(env, s, modrm, false); } /* Used for BNDCL, BNDCU, BNDCN. */ static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, TCGCond cond, TCGv_i64 bndv) { - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); TCGv ea = gen_lea_modrm_1(s, a, false); tcg_gen_extu_tl_i64(s->tmp1_i64, ea); @@ -2417,7 +2417,7 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b) op = ((b & 7) << 3) | ((modrm >> 3) & 7); if (mod != 3) { /* memory op */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); TCGv ea = gen_lea_modrm_1(s, a, false); TCGv last_addr = tcg_temp_new(); bool update_fdp = true; @@ -3078,7 +3078,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) rm = (modrm & 7) | REX_B(s); gen_op_mov_v_reg(s, MO_32, s->T1, reg); if (mod != 3) { - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); /* specific case: we need to add a displacement */ gen_exts(ot, s->T1); tcg_gen_sari_tl(s->tmp0, s->T1, 3 + ot); @@ -3635,7 +3635,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } } else if (mod != 3) { /* bndldx */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); if (reg >= 4 || (prefixes & PREFIX_LOCK) || s->aflag == MO_16 @@ -3679,7 +3679,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) || s->aflag == MO_16) { goto illegal_op; } - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); if (a.base >= 0) { tcg_gen_extu_tl_i64(cpu_bndl[reg], cpu_regs[a.base]); if (!CODE64(s)) { @@ -3740,7 +3740,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b) } } else if (mod != 3) { /* bndstx */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm, false); if (reg >= 4 || (prefixes & PREFIX_LOCK) || s->aflag == MO_16 From b19bbf2cf11297e8c8efd5a048b9e533d8fdface Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Fri, 2 Aug 2024 15:24:15 +0800 Subject: [PATCH 2682/2884] target/riscv: Remove redundant insn length check for zama16b Compressed encodings also applies to zama16b. https://github.com/riscv/riscv-isa-manual/pull/1557 Suggested-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802072417.659-2-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvd.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvf.c.inc | 4 ++-- target/riscv/insn_trans/trans_rvi.c.inc | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 1f5fac65a2..0ac42c3223 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -47,7 +47,7 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); - if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } @@ -67,7 +67,7 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); - if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index f771aa1939..0222a728df 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -48,7 +48,7 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); - if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } @@ -70,7 +70,7 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); - if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 98e3806d5e..fab5c06719 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -268,7 +268,7 @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) { bool out; - if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } decode_save_opc(ctx); @@ -369,7 +369,7 @@ static bool gen_store_i128(DisasContext *ctx, arg_sb *a, MemOp memop) static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) { - if (ctx->cfg_ptr->ext_zama16b && (ctx->cur_insn_len != 2)) { + if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } decode_save_opc(ctx); From 30d24145da72dc3f64d9d720ac2befad28e1daa2 Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Fri, 2 Aug 2024 15:24:16 +0800 Subject: [PATCH 2683/2884] target/riscv: Add MXLEN check for F/D/Q applies to zama16b Zama16b loads and stores of no more than MXLEN bits defined in the F, D, and Q extensions. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802072417.659-3-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvd.c.inc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 0ac42c3223..49682292b8 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -47,7 +47,11 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); - if (ctx->cfg_ptr->ext_zama16b) { + /* + * Zama16b applies to loads and stores of no more than MXLEN bits defined + * in the F, D, and Q extensions. + */ + if ((get_xl_max(ctx) >= MXL_RV64) && ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } @@ -67,7 +71,7 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); - if (ctx->cfg_ptr->ext_zama16b) { + if ((get_xl_max(ctx) >= MXL_RV64) && ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } From 5e54b439f5be1e604453d9b02d85685a266121da Mon Sep 17 00:00:00 2001 From: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Date: Fri, 2 Aug 2024 15:24:17 +0800 Subject: [PATCH 2684/2884] target/riscv: Relax fld alignment requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the risc-v specification: "FLD and FSD are only guaranteed to execute atomically if the effective address is naturally aligned and XLEN≥64." We currently implement fld as MO_ATOM_IFALIGN when XLEN < 64, which does not violate the rules. But it will hide some problems. So relax it to MO_ATOM_NONE. Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240802072417.659-4-zhiwei_liu@linux.alibaba.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/insn_trans/trans_rvd.c.inc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 49682292b8..8a46124f98 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -48,11 +48,17 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) REQUIRE_EXT(ctx, RVD); /* - * Zama16b applies to loads and stores of no more than MXLEN bits defined - * in the F, D, and Q extensions. + * FLD and FSD are only guaranteed to execute atomically if the effective + * address is naturally aligned and XLEN≥64. Also, zama16b applies to + * loads and stores of no more than MXLEN bits defined in the F, D, and + * Q extensions. */ - if ((get_xl_max(ctx) >= MXL_RV64) && ctx->cfg_ptr->ext_zama16b) { + if (get_xl_max(ctx) == MXL_RV32) { + memop |= MO_ATOM_NONE; + } else if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; + } else { + memop |= MO_ATOM_IFALIGN; } decode_save_opc(ctx); @@ -71,8 +77,12 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); - if ((get_xl_max(ctx) >= MXL_RV64) && ctx->cfg_ptr->ext_zama16b) { + if (get_xl_max(ctx) == MXL_RV32) { + memop |= MO_ATOM_NONE; + } else if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; + } else { + memop |= MO_ATOM_IFALIGN; } decode_save_opc(ctx); From 73b0195416c4063c7cbf22b305ee6c48d6cd2d24 Mon Sep 17 00:00:00 2001 From: Atish Patra <atishp@rivosinc.com> Date: Wed, 24 Jul 2024 01:31:36 -0700 Subject: [PATCH 2685/2884] target/riscv: Add asserts for out-of-bound access Coverity complained about the possible out-of-bounds access with counter_virt/counter_virt_prev because these two arrays are accessed with privilege mode. However, these two arrays are accessed only when virt is enabled. Thus, the privilege mode can't be M mode. Add the asserts anyways to detect any wrong usage of these arrays in the future. Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Atish Patra <atishp@rivosinc.com> Fixes: Coverity CID 1558459 Fixes: Coverity CID 1558462 Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20240724-fixes-v1-1-4a64596b0d64@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- target/riscv/pmu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 3cc0b3648c..e05ab067d2 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -204,6 +204,7 @@ static void riscv_pmu_icount_update_priv(CPURISCVState *env, } if (env->virt_enabled) { + g_assert(env->priv <= PRV_S); counter_arr = env->pmu_fixed_ctrs[1].counter_virt; snapshot_prev = env->pmu_fixed_ctrs[1].counter_virt_prev; } else { @@ -212,6 +213,7 @@ static void riscv_pmu_icount_update_priv(CPURISCVState *env, } if (new_virt) { + g_assert(newpriv <= PRV_S); snapshot_new = env->pmu_fixed_ctrs[1].counter_virt_prev; } else { snapshot_new = env->pmu_fixed_ctrs[1].counter_prev; @@ -242,6 +244,7 @@ static void riscv_pmu_cycle_update_priv(CPURISCVState *env, } if (env->virt_enabled) { + g_assert(env->priv <= PRV_S); counter_arr = env->pmu_fixed_ctrs[0].counter_virt; snapshot_prev = env->pmu_fixed_ctrs[0].counter_virt_prev; } else { @@ -250,6 +253,7 @@ static void riscv_pmu_cycle_update_priv(CPURISCVState *env, } if (new_virt) { + g_assert(newpriv <= PRV_S); snapshot_new = env->pmu_fixed_ctrs[0].counter_virt_prev; } else { snapshot_new = env->pmu_fixed_ctrs[0].counter_prev; From b3a34eb90d8264bd73ccb25295b1a7e271a9029c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Date: Sat, 3 Aug 2024 15:12:24 -0300 Subject: [PATCH 2686/2884] roms/opensbi: Update to v1.5.1 A new minor version of OpenSBI was just released after our bump to OpenSBI 1.5. It contains significant bug fixes that it's worth doing a new update for QEMU 9.1. Submodule roms/opensbi 455de672dd..43cace6c36: > lib: sbi: check result of pmp_get() in is_pmp_entry_mapped() > lib: sbi: fwft: fix incorrect size passed to sbi_zalloc() > lib: sbi: dbtr: fix potential NULL pointer dereferences > include: Adjust Sscofpmf mhpmevent mask for upper 8 bits > lib: sbi_hsm: Save/restore menvcfg only when it exists Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20240805120259.1705016-2-dbarboza@ventanamicro.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- .../opensbi-riscv32-generic-fw_dynamic.bin | Bin 268312 -> 268312 bytes .../opensbi-riscv64-generic-fw_dynamic.bin | Bin 272504 -> 272504 bytes roms/opensbi | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index 7ec260ff4047d11f7683c9a0b2fed73e1996cd75..b2e740010b26835d28ba49a8041e7b28881b37d4 100644 GIT binary patch delta 38227 zcmeFZYgkmr(kQ%o4QqzsIyk@pDvZDk0*d4PE+`BLQNaruFHv*^yhSl;5={&s3<!vf zwx-1^8UY=RqD03#pdf0Z61OoiF%g4+7<G|&3o3A`XAN8T`@O%;`<-W>^E~@UvDWIU zuI{d`uCA`GVM~c@ONq?zj7{o%<i#8j8>cRvW;G14DV^{%t4~X1;!eoJut3f*%o0s= zqVOzPC_FVuc$nmUwq`c|QsU{E<l40<tyn6)6RJy<xUC^N2hO$0B<Er{KCZ+E-;j*) z(v0B^hDk+wzBXh~T^RRbLxj%ic^9>wR~n*trUnnU?uL|jmUZXZny|XgTyv^6O3!PW zUFuvc@5&Haofr4ST&|4kS?6wfXAE=e<Q?xx<LbI{kIXb7C4qH{j`x&tbxxM|5+(k{ zy0dq|WT1SIRvgI`>7(>l$d*t}vgHC_Ti##JX`tINj@V7=9nO4F=gB>c(u)EjSzaj{ zUgyl+hmO@MIq1yqv?^a9>kpmz-AN1hP~vXI;H>%ceG2w7bq-uDEiD;|Q>0x`2;L#} zPfqFuBf10C46=cBGVYGvJWc}>kkPw>{D2s&um4F=g%~+kIGZ;#yDdFUn@iEb-;xp* zM)VVX3(?kf;(jtWUt60{r?B~niXWNG++;N$2V_XE3f#Dx0=KG}(4sRxu%={ynMfJe z2}E);%`Z}<&&jPL&?MIDX9~o6AdYc*ThSoHpW1kG1_!=IgSXjuO^&hSYnp@1j6yWF z@3eNsQfV=?X2aK%^blxLdc38ns<t!&G&K^wCMm#7Q&M1sWwx#X1`ZnOE=aZp#L_Vk z?S;41Vnooq3dh*0r3MDbefXppoNTM$Qob<v(AEb=Q)b&$u3RMwu;Fg#ExlU88yL*n zx+E8T!iUtl8udDRu1{J3$4<@=C<>E!L+uKXy93~CIE|&=Qr?iV9CSl!E)5b&<3HjJ z%4NLF5MeIuA(V2<ctiQpR4u0k?NtNDnhUVd14fyYszq8pSrelIl$h&oX#h|ui+N4S za45CoJS`QLLxm6d7z6E$9oNOu0CcZ>VVX+HSiV1o?*(b9NXxhOll`f@p<sTRD$7z5 z|0#UWOY_TOI;fK6T>Q!%xs*_un5MeiLAVOO6GQ=0#Mc<YjoC0aA@XTPCLjl-I#fQT zkh!79lMgWRTSiL_{YsFij#t(=1Z4BdBnN#$*7^w<`gqF#Rs+A;e3Hf$ut_DZ0U0n8 zpyTpnj!S_N&$5gl7)CHt(8T;kGXbYf;R_h~GF8Z*ys{Pq9r9UPK%^)j8^7{kbon-& z__*50fE?Y70=pv1<i;oShPY{%%SHl+_!PN~)eGprRJoY3j?&+t%f?^m0E}NxJ1~W! zrcqR!GSbpFt{yP8v2QEY_UXWAL0%z~VEq-;EHnxu;kY#*fr+$Cwy2}53d+jI)3TU< zmT~FOUJNZWBp8!3bH9~mzHD#F)7$)7M$BuEQ@9)x-r`-IjsYvP5r_wcRF36q%9qfp z&2s`WZgn&aok@9*mn9{1G+PMG#ziS^fq=zD>B91_Pd7I?&HNY4LLI{ukROuiV&^gi zLFv<A1QC2q!F-{4y@UR=@K$esq*Qp*+N&&Yg?8O6Z=7BI5dj--d)x9>Wi$4O&~Raj z!aRWbzA%81a$BnxKkKPXLHij4UhQbF2no(VInv3yGOApBQGK4VR)Fc0Oa1X~M|Usf z1R}9w(uyNK-yT|buegow6SXlm98u8HM10$^=itljpfXJAA4fyH!#0Mokw`ldtw}Ma z^UCt~gxZZO-qucug4$-D5*#e+JEtIA_^M9Y9;$m*3|1s%g3dw8ruj+5+uz%8VGV&c zHOgM_S0)qF_oS)tmnr{%{!Ljx>F#vU)s#e2ijW@gSEdoscf(|SQx<?UxLp=04jBw9 za3mh*<ja<*@Ny?5o6-+|>C~fBiE`|1k$+eV<``?Jm6D>xH=VrQFW>X&{_O8kn-)f4 zYsH8HZQm#GOgmtU9Xkc00z9;niY=Io=X8owq{J9=>y{c{rTL||Y_9I$qr|s6b!MBs z$1gg~g+5G|4`d6R@g{kYmvYGSDP7A_BTkw?_7@rJu2;Ln24v{36BpRH^eTK$9)XIn zzjIfVfg_v)kr*#?Rw<08G+!I*Yny}x87B(J>Z;{sarm&ayF+uNG2OmGn7yV_e9JkA zZ7jiJmp*LcF+9XY<5G2$PK?kTx=g1KXGY*;mtOXx59v5-JH<;8BbP+r(=LPEWvdi& zPy>-}w~z8G>wvYxFO%mY=vX1PcMU}Gc(AJ<^24)T=eh4Ge$*$-!njflBO$;ht+??a z{@qo>RySg2g%U>6U*U(;_ydK`HwH{hmeuYHSvi}plZ&tdGQ_VH@RW<iudnH;^h?I! z`w9(f7>ixq*4r7ZQIkbSzQ84JJ#1y1i=`j}H@XGeq@XMM31B)Hgf;HoC<jk;?*?pF zxYwf?Jl#Wu4&YRe&TPsDc&EoGwtO!B-D8w%$?RMDae6*RGh5C{Pu$R}H}f$`ad=2) z54UEUr+y**q=_wgx1WkR9P7fss*Q>H059t7=2tS^Qg8Y@^`>n$t%%Jv#mAm3Bi10+ zwbrEvIy)j(*$JQN;%1kkl|HtNU?{Hb5`ZMwwreCx#Iapxp@sNZ*EDds274|=GJL?Z z9ogdpUY~)R;-{P>G8mHa7s};q%7^%w(%@F`*)kQ=FINjY<I2IeQ?+uv-A8;($SS<O z+c;!{|L9f#>axyTh1TGs-Wb{8aG(C64p#@rUv_46HWJ^n5k5iB{vK|kTB`d_q7kXm zTYl5?&3V_1>AEmZgNZM5i}>vELur1MEp?kE_wma>F!cDG&ro$sYk)pNCv6GJiqN^X z^z-TQ>~E2(@)hlkVf@JN={s*o!d|{PeV9JRT;1w}Ol+Avqbmceflbi!Nd+2MBeM^_ z!lWm(awoKIV{yAst7I^~<J(`FD{(tB@y--o7WPni`D964apukTr4hQhFI{uBFk1%M z2=~iak;Cjx8z^DQZ|C8esxC0epQ(H%D~AXqhCyIkYj5b~dMgl3`Jk((>*V&8S>}Ob z5JTNI$u$uS?rcO|*O#?e@^#zDMLs?Hy_QSXwdq#8w8PT$*R;dXTvCAuKU1x7j6EXH za5j&E7N%gm+CMq#95)|Jr6ooA<MgRoK1SJHcoz6ky-3wkeX3Qr9`R9Hy;|nS^h@)* z!uY7~zp8uM`bKZ=jMHhK9#(&MJDuid!_=ipO14j|TQ4~zDzpLjApMm{9rkJs@d?`d zcU>&^@ai{as&vWuJJW&oyeJIFMC$ZcX+T!0|0ZM&sS|VM#`G0uFqi&%CQRw|T<+}i zDRh!9A6ea{&Mx<9%cPZOgw;=#5ocE8rGB}n5V!mJNJGX5WqvrYdj#m-vhF&U_|M<k z3->CztZ}9v{-e8J_x+!On~MUnQp55u7)g34tPcaT0+i#2V&>kIWd=zfT2i|V5Ak0F zE3w!=ROY{s)KdeZIyEw$mz4+OSN@AM&9ObNF7pfdlX7#b7?JOz+|MjEN#psFm`S7s z$g?76`|R1z^u7%KJEPIyF9SU7tNx)l2G)Yp*HCY>BET!TIY!PYD~?$#Y0ru-TKP)_ z<A42qtu!1@u131li>!ONZtF!BzsO{EUo(kQnAkl0LW1_6H;EY(n<sza$Ka1YX?H)5 z6Ec|%Ih!xw1~Hmc)nz0MX4SG7*{Uv)%M5<2W@wig6srV#cXsQfswxxIm$kRLuE<+m z25il5@i;lqzf;^)@GG5l+pL_)GZ;yFWeh$Q7{FdSSK1I5fD~av*OnVI;l%Wi_Pwc0 z&ZYF`OPaA?Pk&kM2(E(aj9^I<2jIA#BV;meWYyck%-;A=&oQV5zwX&r9luYINZ}4p z*=b8-=bJu^ooZU94*8Q-P_%(rcL?&slY-{ibGrr7D#9m~<I6$4+5KJco1h>zW-jj0 zs~;;{jOX-<V*_QlvR4n(jQ{MVurlaY$KvMauK1178V6`RpjEBL$0j#ZaTeym-aZ9? zq*=tuB5|!|kZXC5DE*&9U{_{1+wZM>4n1q*Q)0&Bpx!;iF(Z+7{tz73dy|*+F-$pg ziiq}07@bIJ15UCATUE9OWPtsTE5PzTV^KMt-Dl~5z`JS1wm_LiDbu9ZX@yxDxlQGp zfDCH#%g3b^XK9S-iUZIS+l8};5!5Yv!BlMB*ViS*7PPQA?~S<%EMRj5JhJb5Z1z9! z`o7VQf6`{@(uQRnrwqoA`o2HZ_EMUkxjiGax6TII%YL_K1JjDLHM$kg#RH`&ZPKXx z$gb|g3;KTIhje%V&g$3MTG`J`u@WEb=cf4cmn{)o&dMKuilAy(w<Y{_*KI#lkHz2j z^S5a@Lw=+yDzOw>^$)X=9#(+D%cYl1ctZc7s3%_EKPpsz9Sn<|Ko?OcGDax>+@eqD zE7{5@U^j*(a8-A&!OCY(qmqw_v#XQZyb@N;<pU^^yaMjo4{_%KJ|N2A0bZ`Y)gauM z0&t7SP1vIM-ao^~q;PoofK_bbIov$J+oR@F>cpw_8_gT~Q`l<A3*eGbbPD$vILbBA zRBH7c`dHb@MeUw{#k^OCxZ?DIJ)|W|h0j`ia^OVJ8~Z`S!y2D~OLxDRI&ICnE`!_G zNF4)7i;-*(-Tf^cfk+i!jJe9T5u5p><}bS4E7GTiD{eu#x@a35+-khC@cKa>cA7bQ zSRZ;3A7Yq|zZs<N<X0dlggiTo&T2@^bo^-0G;pvY2ET`rFdn>wHH^VlLnd@eTq`IU zw#i~91EnDp&l~bFD!_M#<gl^<c+t=>Rn|GaxeSXyAyXrCD~lZzO>`ofl0<M)J4M!p zcH{Vv68yu^ZB8YsEt7VGUyxNT*QY~wN^t5hpYV%QX<vI)e_*;4#y8(h>lkT7DIIC+ zSz4OBrlYj+2racY(|V<LlxCLThr@cP1NQOF(XArb5uhud6^jXU1!rYV6BWPhN{lNW zHr(5(#`RzOQiGQa@8%{cqTE-M(KdTM@8Dj%7w;SHD=A-91d3jR|1mtC)kyJ>5h0GX zOU7mD!{uN4GFJfAjI&0pU}cFLgS$H$mWupxSUTFP|B!PzFj{6M9u~aZxp}(oqm%Xt z>#n4$-XpN?aL$2ExH5PYdw(Og4pDb2n3U$1%XUkIz3UYmtghdp>vWoX73}n{;k}>+ zM~C>k7-oooin6cEkvx%exzIo@&JEe&P#fkHp$~ToryY{X@Su?)tTGm_8ab0yM&qAH z2C<cP*k+W6O~x_>hE*>LU5<lBh4m;1MUj7n%n7>@b~MsEDoQ^plAFCIMYPX~QRD-e zbpN87FvV7Qvx)i;?;JIPO^L?8jhYiw<;T}t&kD%WX*nxWEn<9%i=S){yGGW9*6rjT z-U*X$9>{dfb>K#Fd_hV$UK|?6?w^Lg3DvN1LAW8*Tdbc9&f%w}4q?w&cEJ;D6)}x1 zet_d5f>>icHbi{vp_vkp{j>+uFEtE<;hLOL1l}q)2F}&PuyVA<vv$yI@Z=IeGAYYo zm-oy11K!JbrNCU3mzCqiqkUZq3;$&&hIIsX1LkzJr)T1xEul`03h208()j(B3&P$c zEVbeSe1G)&V6KNps@Si$;JJ|_)HTtTvq=aTY)P{O{IrH*D{>o_=KlS^k~{D`c<?U1 z6zQ29;->2wFSR&u^jH)UGZP%RmBu$BRoH7@6Eh~w&+4_vzw-5laQti_qt3XZhcy;s znAG#KQ+TQtypVzr$DS2we$*rJIkb)OF}`Me4hAA9pPuGt&!iRFvUBuzCaxjIbpHwz zzQJ6Fv2(flA|wIFN2y$8VYka#CtWUkJgEho)=F@RzzOVw3!=0V^D3R6f*(btI8@zN zY=)`jxZk09gjc;6z+PF7i{3M^jSDyKamHiDc(BFu@Vqg@=0(KQHT_-aTZx)?Rj|cr z#hE(5UGUi>Vg7lffdkD`x`_f_<d-@76uD#(F+DP?&(lLQV;zv?qCJ$(*CZEH_k4qv zPYMaec4K{6$qd|QYzWxjWn;U!R>i(;6Z|Q&_0ODyjbjI~iR1D8vBP9GV|{#$jQiEi zT99@U(;I8X1+a;cc*eNV?8T$FXk0Ipjc<($@~jE2d+6J~%pjeYzb|cSAv0Dhg8fRg zKLXCr$$IP^-J_FcSlu52N$&y`*r#2oI4;@;wd3^Y-slZJ9^DuHjUPrUU6Xp&HTkx0 zC`<F_7?Ho084kpG&#^Mblhp*`kQi5YO(1Qln+g1Uf2j!!u0huQ@mbzbf|tYu`YNYU zJ<>fawlb!ZD<DSWmBk8piUO`IQ~UyYX>03?&&4QZN@Q`PAqoW6WC?x}6E=KNg2>N~ zX`P~5P|e5{YKAUBn25wvV8(RoK9LF>-t$QFdyi3w8G1^T-_W1i1Ofs-Vaa$G*02I+ zj8~3e7S+Aafo8tPU@NQFMf%B`!R&lwe>n}<ub!-H8i=x`0&~f%Ys3)xn_f^Eh}Og? zmH7Vn0Q)8fQDGz)Lh#+=%5mojJvC9baNcGDvar<ZjghvgY>vOC*TodDB2^R<Md=bh zd_GR!tv|(S=HgE$WT0ByKA|(K48&a~db5ddaL7bA&xrZi5~_E6O>{*Otdnti#c{gN zg+Jy?cKwV$p4f+te2$G1J=v;8d}ZPQ*UZ6dLpe_F${{Rf*pX|f^OE9^T_<_7k&p0@ zNxq#5JM$&&FOAf2iHQjemk}pT8s(OvH0lS#n!C)L+qAZPaG7PyJ@J)E>8J!p$M%IC zGA(u@?3O>qCd1z;lTUjVkGGKy$){f1DY7OL#-d^Dj5&%dv*8?wHB(}evj(m<0m4uc z)q(I2Tv`S@U-{iuC})zC9pz0u$D8b-eAXLUuJP_D-{1R~3Bpe0cVE%+n$8{NP2D$? zNhy5`EjM)iXL*nQWpq8k@hVP+oHxEd$T|8_6~s(doW-_Gh0wdEu6WG*Zpl>}VQ=sA zTV_<|t&9m7L=W4NUOPeoPcRDpd`OLktb?+3V+;y-%I)j|NDke)Yy$)~Swl%Sd}n9- zlU#V_UJX>hQ*IAWpp)Bv8<@H`ZC{vmTCtV&w6%&%D3eG6uR&X8o0l9?6Ogtqow*aD zSPLlY2mS&{vHw3wLQ1-)?XyC6!UT%Z-7QSIzojs!p$T?-j6ktZEl?z?-%=#`rtPa_ zw@(r%tW^R<wQurUk{EAbBHI4GKr+uqAo25gOQBH$6Vdh=0>!0n0!3!Gw-ho@;9+&f zGMMg4fuhmtEk$!@;9+&fGMKf=o&rgP=Ub8*cVNPu87(yPpo>6J+~qAri2|5#XT}H= zqdN-}lFn}_3@*S!e5OMp?gB-k`&)`6Iq(oCpXm^Zn?O>ncuNxF1XF|VSUNJ#RiN;5 zeM_NngsG9-v2^5;vp|vQ{FXvy4?HAqEK=<*7bqG#StxpfL2I^oGsDujlTa+EpUR03 z(MZ$wTG{+inE-T2*4S>ko3us@M?Nb!3ge#BJF^BW95ubi@Fkm8oa0N9H>^0qm&`hD z%&B9!D<7VOcSG!PK~bemRQFd?{5Ks7<#J=>aXzJ{v~+qemW`N#?|*QJjaXE=Y35PX zMbgcvchWYQs?y|~)f@aW(^28WFe7jys|&_+;sVfDI5%#vLgG%z_M0*!^5>b3#E)T& zq;XAg>929!5bO8B#yLaS#HkzSxZ~J4K5S+zeZzCQvDM@0+jS^w982Hqb=_FMD6G)| zc{n^e<4m0wTRh7AdO_Efl>`f9?$*>~K?r%M<r|NiD`qbY#FOVPU>$nn^K)mil0fVo zzsWWJhkw~NVSj)y#7+ENyc?T&12@Env(jrgC}9!nUyZjXWVk8a=^^HA9m|e#77t1E zRA!#u5_&Uq#`HC$gGga^og}Bagl1j86ODH!da}hPyd}|>jW~wSB?hq(NAZ(H@9CL; zQ-f@dpPIv*5Q1CiUIq5>qYh3G^>^-gr``ee3<T~G2fKuBsJ=5LwR&{u8e)IXGWM6E z!U#3rY`_cW`FD+YmbN!S4HmGurmJoY$B-3ZhxDppP*NU;kIZu)Tr`Bf$Hdl+;@XV# zr1UYE8gaO}G|qeF4{%;;y+?x}|MW`~m(3>`ESevvD1K<+FZ+Hv@KN5Q^_t7Ec7Ct9 z)%jgQbF1&Xx8uSX=pXf>sqgHg-rELS{R&|WnWRf7xbSUF?G%r+ltK_~;+Oe#Nc)at zYGf>xgK!Zb)Go&Or}@5i`pY{m(@NmJZot+HJlVv<xch=2*q5d(@b}F8!I(}(@)WL{ z<QeG<&)l1U9t#X2GV#s@BfP6RgYQrt(oGk_`6^Ds4jOpiTY6vz*W)DU(|+ky+_s>L zgC<1Ba=x?(XYKz2cVFl|S?xk=mIoO1(#vvjvq(`0vf-)@@ik@nG#H^U!naM-*BidQ z6Po%hbp#;%=4-U_QJ=CB=0fjeZkt|LMsRNMUQ(;W1q<B*Q+@33QCGMKuAz00UPHe} zcE%)txkjW_A}H6}{8UYGd`TRxTj)Rgd)AmKu#D%T^gerA%wCVrLCJb58y}<PxzF+} zlB@?6u+@cgZ~-YydIC$kBI^oWd5^Qh;`G;+aw}TS;13u1vAJvUmy7!KkAr(Dkd_!` z^VOUTKr<tqjOl~x(;!@H{*r$1Lz>!#tGXppSH7m2i8C8<`=TYR|1_NRp|d38Ps&q? zb3XLxUA%|RkI<I7nH&RRec{Kv>|Uf()jLye7Hxb!9P07i4@V|fC4qJFcCV)t8^czb z-C&52W+<S(oU3095r0p0#@AZQ_lJB+lAk(*a!)JdHVdWtMG!~oj_SWwE@NV35a(Jh zC^}taaMx52y~PDWiON6m$1fc>tieRh94EtbGQjDI$Ga9gsjCv9em9{9f@0Fel=nMd zkau4wr4|GKwD<#b1`k+r)GfuO>%HC7509yJc6!W(7n<K0*~TPak8&p`^I$9*zL^fA z@Dnh}Ie0{p7JY)#ll;(Vd@yOCT+5dm`iY7vZS%wFfFP|P8ox+Vd5z5vryd~O5RNI* zQ!lEf59XR}gu%v#;2}!~qe#4bX-_m5?^&A2&4KINUfAKIi)?XW>8+1CA&2;v3NR7S z@CLXa6XO@lLfFiWc+heso45r}Tkhcy&kF?g>)=Xwb?KVr%~ovnVlzeYax+EYc11j8 zp&AU?5&os;R^AbNI5olPu8pqo*;#%27cg|43oE{KU&=JGANS2$)7mGDWP-vL6~A+{ zL^5EM8AaGXGZ+=&`I%Z2fe&X6NAvLG%ru+&Qejh!FTtzU_K~VegwH~JY;B_Be44z% z?D&>!f|=i&jsvsCve%E}Rasp;x#xWItY7FvSy~sYtDZ`pdyc=!8YEBLk!^Kq)AkVR z!J4<$S})w5mBt1a;h#1Pz^AeukQct3-IHzKiT}<nVRvQWxXmuOEQf;_cvX%UTvFBN z>_%Fgxo#ww-plKHPYoGWH<f$yJ$RvT|6Ops6)qLU5Hn=?n40-@Gq_hDS}?udK>C3d zxX+?A^<`G1&SH&vbtZM$Lmc5$@^@s}KlcN3kF|JWu8(K(F1?ksscx%pyP$w<BpsVV z7}GhtDR&6lo{wvCeLJ`B*j)cs-H&E|H$^sW?Udu`pmEr7y^me6(Y;Ejr#gm5toKBX z_=EMG!;}A!=9k`fr<_jT4YNtNo($Im5Rtw6JshV01;ZPHH$y!7J?_n&a-?lB0i`~i zp{4lTdL`TIFs@zS#k0NXEw3@LvT4?_yuop8M;R{3aYj|RSKd_i`VTleZwAZl#1Ha% zu>4)@YEVu&e7}QoO^T%_HF<abYfr2v^EFF;q&>N0>AA4kbchf_Z8waWNSj|R3^^X> z8al(3<bK0MRF7K?eyA5#ZRqC6pX6mBqvYbsHhMoJ!?7EJS-<Z1%MBqcUjiyI%AL=H zFhkg_u<*#^k?JTFD7y9JF_Za{VBB|Oe>Qp-Ua+wXI*c<nj%TkI;GZ`Rb3VKa?rxMV z4AQ!nnKMXA4qw3mcqTNQg$IHLRp71@W<7d$KF3Uk0To2AdAF=XxU;@`w^Y!wymh>+ z*11EEir~bRya{VIxq1Zef0x*9vRW_Cpffb9l%ieAp1g}Tw`vH+Ih#8B2WNqHte`7J zHH~jRY-U3>ZAXOezR8A)Rq+W}0ZBE!zR4x|@J-5x;wg^-8GOO4oQ@K=nUkhjroCWR z{(o1xYj;O!2VL2jjuI+|yOr;L<<-6W)vE=*ux_&gX-hxbd{4ysU%@l;ub~CF=hj#> z3#V;;jmDHFZ|jWY&5I9Z8guBLIxZtM{M<UeJh=4h?M;Xsn}E0MT#Fh?yX~5e)av(r z_B6;8)JEA$CW|7(j3OK4VDoUYPf*xvMGm~%1}5M=1?oP=iKY7dkiOhjCU=l=Q{BUU z?O&f2OxeZpX~p3(xun^#Ztly5dnQE!b-UDqzIs)Fz_z{K9c#N}Pe7KSj8%C09v`H~ z$$R`*xP02TC!DqEjhpw(WNiX*%va#64#YF|y5W+q7J=32y4RiMdg4KQz0qYneeW_> z`x@8m4O17k*aySBz}D6$jPez!^!66J2W8SKv(?>$hZK(RO6+D1va=BC{e(KyjVdFA z8Q^*!1`T-|m=t#NO6&?WwZ(5~y3rsptRJ8e`9<I#3wyEckFj-83VXM{G`k4K6@~-% z^_InVw(Ji-^Udu?RtUF1Yxd2wQON~F{4M-&->0s3FJ9C44u{yEA^2kN@Zadgx?xup z{&fGKWR+N;EW80h_U$W<$;CN{z@`I9ZdL+@tKgfxwnd&jnjw^PB1$9RwBLc*&);$O zZ7r1rqq+qA$dPw2`gThl%J0jpV}qA^-<eR_%h(IC+Os(BfU~^*EDR$h4N|sdW(c%Z zIP*ZD)77(NHAw60t7T*u^laUC_`(5IU_hx(Zo@P{_EoSE29$Dly)ko|Ubo_nny<Ng zpaBwq#{H9v{GiVWE*36bpQZU;R;5O63elyD9afLw#wr$qw%F#JA`5_X_LVaVc!HaG zme7mQb?Xk058;_R_5fafFpAZhaQVS54)w?Q<|Pk>9>yQT4-fVOtLpW2SO3C7K-^DL zN2=c)guaLe-F&yC5XXP*HM#I>zL~!ZyOJNp5l~y4k&vHNExcuH=@3Hw0pR*;nqTBQ zbPZp2(22k7po<4O{%3M<9&#f|1CiJVb1AGvj(eaBKWz__zmy=SZ!uSHDc9r9hrHam zU2qL|lVTOjyVxo`@lZr^_}XKo8*(Tw$T#?%2)dEk^_XS+`CziX`_uBp!PoU~SDzJd zDgqt4qCJb8yuXXOB)+D;GVUTH6PBV#Rh8;eLDIn)NuQfa{9*7}{@t(WyY?QZ-(Up; zt`-$mw#T>ph20Mad-#6__lVXyEb*Y3r~$dT*A=ju<PyJC_@l!H>D5okB2bWn-(lO5 zWK@lFO8UZ#l$9uat2QZOVM00X3%Iy3t^{?=wWzlW3mDaB_*IFflkLp3{W;dUZBl2& zRNyR?I^khQW_F6c0N0yRu=Xl44Z50bV}lPK*@>d@dq=x<N@yp*91ek>TUgEg>Wiaj zjQ&I6Q~y3+d$+w}A6Z3fE*O}$H;Pew4$SR(3{=dArR_g|1eP{S$U^A*fxyrW_m}QV z^R;7~?7qJwDzsAbDc3jPE2ZViBoE^{mmfq$R_cIEoRN-%R^z7&&BRAy<2R$l+1>b* zCE57zZ@NIrip#Mk5J3Aezd&u6K*bH=YiIp{t&|lsgMm-F+kV?5@KKEtDC+U}<En1j zVBt&K3+QW}Pa0{Vsv0hoS7CCzHyHcN$D7!)yZ9T^Bewkp-gDw3w(Q$d*ON9#$+tOG zA#KOTu`>48vVgVA=xw>#{z-0?hMek${Pv%x(aa*SbDOILYMPryjVPoNB$X8?R?=CB z#M@4*?1~?~U!j;n=Ueg^e|P$Xw6LY4%eiM%j@N&%uX?K1XgbO&&d6ESU3}||cS5mo z#AfTUZ%s2e#O7Z3?H{puv^fv1!;A~%#l=u=?!~{B`xOD7|E9cnFVK(0%KomlPyOK* z0%jVF2Tne#V51M=EoVOlU&rxWFW82H&nejJYw-K$dVuq%KiA9Ev5v1@@`uR3N+q|| zBQhNnDQ95?zH)Aw`Yxo6TxRrEy`?|AGG|$r<b5r@yo_Nwnf>OH!v}TloNVKHR~%R7 z&}qq!eC_qS#++qwA1#OQZON==oKohB)?rfi0i;$vFN<|be$Y~8)K7y0?%l?(%XlTE zU6uFL6)%O(kF*F!NDWJ*o04DBh|M!bN+W{wWHPl$4f~Jkb?J?)eT49xuwR$k$Z(dg zjAC6zBO_qK_bI~Z<||&v*YfA6x6rdf!AcfjtBMd9wzeX)@7~#mvI4T<bh(w<>?IZ5 zY`)imBVN<%^3@2)ar(Acj3N~?=py+pk94L&$-$>9y0M4%<KHU6tyNQKwZjK-kMmvn zABZ)F7P5^*&V^Ws_GiMyRq$(_l<Sf|SI))3h2X4L?iKjHRopUzmBuW|f?&M*{6vo> zjkG3BK{9bn`7N?NVs}pd1-^u@#}Cgxl$F)FS1gNR+$dY{iGIiTFZ2=by6az$v_x|Z zIb%(wnBDa&9#q-Gtt{hAn0&s<8(SG<SvfoYFi%mztlRkW$}Vj3k9cdP2V3?7K3=H? zN9otfV4vXY(#r(28Fv4kiU@Fhf?iIRLqZduv?N2ZKv1CIDur~3o&XlofJ$do?NYjW zRdDy{(V9X%T(?8=eahW3?A*|UZ9juYHH>CwoxtlFhA5;C*y_0mVgiO)Z{V5%k#OLW zk$`_~P(kvUs4-ZPVeNcj#)T~xe0-bOH2+LSp@mktdOWqUb7z$(H4<FfXID+Bk#+9m z&CSp)p#oly^BR4CLuq3#w(JhR-{|csePyJ1vxejcusg9rL*m!C%L7laK0_aLW-q<K zhDJA>^dKCCW7C6BdF4ZM0;eJP_MI^u9kXvzu&M`G-Q<m$aAZ^G&JFdttZD{D+>F>B zI)K#esBX(&*U^XMrfzVS*xl48z+ZC5ZlwHHI75X9d69Px5yfbDv!3@WfdOHC{GE=0 zwKn~Te|i|BYP>RHGu#6jdi@Ib$+>$WYo4g60WWk`z!Q?a9pTCbkNv}E{4PQU(kUvg z0N1Rkn1wh)$gD5Hi8Gg0S5XHo>pJBxQWai+{0M{N_tfMGnHei$e=wgY)#qvW16nNE zg)9CTs<^vesNZ-3xc*KvFRsCwoQ36&RwOrl6OeCAKa_>Sv$RyEhc7}JpX2j?_OZTQ z=wtoge94l>;6fQh#xRs-Oh=Vt92uDik`1}bh&M!+p@^c4$&f-0E2X(?eTN|v`dW49 ztucf<PP2kVeEm}>!ji{+>flUT;!{v;oLWH!!1#-bj0$*))aqgsLHa{UA-5M#dOX5A zVlT}#dFmgc(I?`}%#>vX5gKWvdjVBX5SGEQ1b_3`TW(dbCB&G?h39IAY=&ea7RH5} z9{YLK*TX1&1gGRx#~g4Eh4E?c@g>)BuP1|(9sh=Ge%XfbDC)|AoOZ2;j4jGMWJ!Y{ z$4mpr@nNw8E$da^F+0VZj6yn6uGEf<0ZmvpW~0bx$8d1xJ#MSq?h2X%9lZY4*vC*0 zau$p^^toi6G1KReKk_l|M?MFqBB?T550+^}7O2ba{#UsOopn9?e3Bq)%7DibzQ5-B z&%E;b9b@{RH5^PFy?c$`mK9OQJ^$yxv_A?1(_iIF9M9uLe80|lH^J20p|S=ITXTiD zT(*b4jeyE|_%z>n7WchSg}ca?<eh4r)Kmr<p}d|6a|y9ys#BKnuQpGDUx;RV<x{_q ze)y#ydk5Z>n(de1CNl#x=%-@lsb4|w?cc#)&7DhT9mU<6T^uSu-5%l&H~Lg}uV>@1 zX76O~lF)3`X}%=71bnv}2s}6Sm8eVOE{d-)GR0t6Uefu?M6h!Us+tee3UjT`{})x& zR#~Rf1XD?!*J~nwDFZ?-pY<v$LY=SsxtIZiftDGHHIQpepS{5waDo><TEHwbM8NYh zgKL|elMi26xzzin`bJdExN9>n&Z!jNn(*D^E7LA9ZUI?`vX15)Pam?59?i)RnCfif zJU%23Ze;ihk<o^U+N!D;Xv?JFh}Z3<FIyPMN|m)LRw{I#zI4$2Bo#wSWG%7U5U$RE zEZdU2Uzc9>3Cd@D0<w@#kClifNlfSi`%Ul*W4hB-2zi>Vh4zFnJpDeH&>gLl&KCH1 z4?$6SRsXc&DCYYM+wr%5c6C{aC@V7y;IONCWIbSU>!0J%d>s9kmn(PBBBbpgBm)Sk z)R@khaQa^X;4>WlYdH9rkN=w0Dee<vR?mC*$Gr?BXoHzc+>K{G^@dF3)ThzN5r6+Q zR9a62PH<j*=7X-|k<VOECZ6)lkLB0mlxIN>y)wp@K?XI1yYlq$aD;g_S6m#Jdi_oW zO}!ZL+&9L~Kdsmf8ZaL>)gu20_1D!T5VZ5aAKlOxn$Cx9QYsWfVft-tRcZTenbt|7 zB5>dIH)zB<si2E7-5gx6w1RCc<#Tc|95UqYKIhnCFY+oJb;k}bG^}Gj4tWuTLhzCo z3bxq>uYEC&jU11EdZE_5=a8ndr3qjKnt70{(X>5=xWjB_MUKt?gYMMA*ZeVOt0_&% z1RV6z6$GSxxr~J$5S)6c>=e<4#S!3oRJNT;56I$EQa;CzUV2O8K3Yj{zYAno@%IW% z{7k-}>@d<^HQ9?5F4W>z#qkBZ4k2wt8G&!CuSyWg^;7u*udnf$zxQz3_kjfKT6${y z?$aw8QqRyWgZ_p=Q`=8BgA*$PKyU#z_tPLROqVB-38H)NcU<1mMJAgXkOe6VdfiB= z1D{ko2tR5WHn%8-)=n7~kTv`^#Mj;w{(`vz$gpbvy8q?AV%11mf!CCK7_;iWl|D0P z-6xgz7TPiXMBDNIfk3NiMw=hed)PLmV)u2?ejA2TiqujDvdBi`b+0ra?6a>%vIU=S zd_4li;o#SiY|Z;P`*n~{O{_>|)e70MDUZcmf<7M1@gvj(Da);CP!8s{APN8Sx<6YR zk2}2?!NN_-1p1hbH@@iu+E?{vDqAoOD_f&LY71Mv=%K5%7yLclI+*p{g<rIehC@Pd zTQ3(q&lfm8q76mqJDTEgT3a_ZaT6|T3xJ^9_icUI#PwL*KB8;=eZJuEZ`4W&v3p_n zQoybY=~H-i`xG`Y4WDX1;aPPP0>0n=e+WT^!4>#;vjO#CCCfHfqG5ymmaM%Xz2eA( zpibh_AF9~xp*{u^qi_LAuKqi|pjX9z&!v$JM#ztipHJo>6oB?Z;sNqOy~q)S26FZ1 z`GO_nF+$x?HF0DS44nkCC}Kdwl(iRVTiZiX#P<@PltxQS+EX9~b7=2h1}5}(5;?=7 zNvM%HiO^IwJQ7Z`D30|DCD%k~3(}KBD-`Ua-wt;nYpEdB9fB-KCa0}XG%L{%I}Y`s zec;gWfcgz|Df{@m?9Lm?UcDV1{6duhiw>5YNa)tn_!l|EArrKlAV!(!H}XV`h9fcQ zAwlk<0`bpev;=jr-J7e{`*?3^<2AF$5((<ze3O1zmPwY6cMC%?hXEs9LSwHGTKa+9 zm!R`*+RqdmU1Zvob?XvdgPyk<Lcv-5nw+yngFF9WzSUDuo0Iui6cC}-Ur@lJhg0zb z(n*Sz1T-mb(R`wWzE|`<1x;{fjj=bTcZHk0^i~B&XHP%HXAf;XnH-R!0fBY8&QivI zpE?74`V`RM+Pl0<lb(kCxw?jDZFE+M8mvmj#ad!#gFM;flca|Y^7RaytFQrnHb$c^ z+XjA01bwy*oFkM4>?4b9P!y}4Nlw|I5LP^$ys<%{gGce2_VS)(#;mSoLT6NAyykA1 z%97+Rt?FaZV2BVxXGJPiKE18+<j8s%^04VB)ov!+ZBY;PF8CoYEcp=4@U^2M)G`8v zqlnaSUgO2<&E+LS`I=cf$P-(nu+4k^3!OjsaZnzS*qj%e)AWptFhvO|98F!8oW zv(bK%ZjZE9zI1Y~+9PlD6=}9dBkjBi)s6j)J1Jtg0~%z%U6*0Q5PxcKo3@fQ4rmBl zq$1}W&?q)xAz>ZSXf~rO8RLlD*n}=5!4XZjvgb9|NtGiS6luq69G{OrJAc~9vn!@8 zJw`ng>Yq4OP14FD?X=b-ImqASZ+DcZMz~ju&^HCsPCy!23Je7_0B$ss=}=tB3>lK5 z&q;y|hP8@h$dEt!l$6NOe0%sMnH@u5p3;oQg+%Ryma`eJP1~K2E9)A4UWd3YG<(yS zek(&Eh6xmh-zJyjFqDj2#L5{>=o)?S(s$qwNn323B6RjmR<QLmE$oV^AoUuaEOSN! zQ9C){jE1^}U%ml>Yxp^vlRm<|wS!;7CDP`M+R-<p%>`(Wm<G6_4@Jmi+Ut&D#B8yL z>4g{ihz0S__lC|#lPquaAzEU3>5YmJ;!V4J5m=79rtxZ|Wzk(ix+4{|c(yyrMZx4f zf7BleQ~gmON;ZA%k3K`F%;X;kr9d>R2O6ooyCWcHa8;>vbBF?VAULdy=Vd(P*$exT zK8BR{KprTM{L%xgQJ%@RC$dM-8}}g8H!OZo5ah^J!Ufs&-;EjLG8GMUWr+vUeP-}b z_o_5?Wvf$!F;kb)U?p&`>d(s@Yv0md>}N6rp+U&G-YY2UXcjN)^=(JfRi5NdFErYN zuQFx{mG$mWxvB$S=w=$BK~@Ml71|peqn&E$gSw(6M9~*vr@UNa7PfI=bhGv|_WOV_ zIo%hq(d2etG!*f~r61}Iv=RN#_%3B_#tgZnvQ?dNjZRUs<CC5h#&qxr7_V0HO+TdU zx~mmST;NRo43gW<ZPLxlXX^|R?GxY#0b?hB_Cs0_^T7V7F9>~Mf3(m|TVWm#GwY$i zS`bY`8F|tledN3U#Omq(``|9V)updXQ;{BpL$kH*`yo^j%F*8-F<!MKeE^DZa--%< zH?f&jw6HXO=tk}gK+*P{Xfb)lLYOnG6B#rRX?uc2vS$>HEEqmNjyhYne^ESwuO4G0 zsbfHiU(@|u@d~)uJhUOkfoQ8oa7(v}fQ*1FvnQZQS_P-RtI!dMZ;+XT&?I)&U*wxX zNC`vxVG!!e7UdC}!H7o>$g9C9!lw>?@&FEdP*$HD-8zXY^-^_d66mlEbL89JT43`d znL7jpu)#kP!w}@_G4rrbfsu**6HAp96=hgl`PyQNmyv5jKqW5|tD$I27tgJ9Bye*f z7OB!-^Ua?5a0F{$;n<jQi7Xn5W+*&&`s_6_brS9gZM1HK<N-wYn$MBDL(vd)k$4P) z^<7EAhJn_XlcZs23OY?69_067(9Cg@+i*0QwdP;IB2DVGm3$nGlD$^JneTqF6*ToD zG*#UAg^_Fu-El?n0t6-w&m_(vXom0nZsaDoY3XyldKd6<(MOg|F7NMtW#k&F@O5qj z+WDHGa<VA|4H&Wbyuc@O5%7Ig3|EK3CFrQgqvi~Cs%Amu9jz5Fp(p=8@6uty4<kP% z)+141;0hYc+<gJ^9GZhp3+y7|fnB3HaM1kZWJmSH`DEruG&FEA9Dghou6<(}$IQ1C z)|YlvkmyL|NYoR}H2pOaOgtJx&V(Y553U`S+hj}S(y-QC>31PlGR9@(UtSxs+lr67 zeh4}o=XGQ7y}C_d?TQ~jC|vy=zUB!z8sj}_<0N+KUy*a7ChKoRX=j^bt<8(KTq>X1 zifxn?EoF3e7MuEpp+AwwtZM2x&~U+uSV$!TeT8%|)!So(SKm8ObZ{N$5u6=hE?^&b zv78-#0nC9wmv_z%e?UiUzx5=0!%-}9r-y=3?BOgjA_C3z<n{F?IjGuI(~7Ar{|uav zo4{nRm57u_fI%xJ%@JUBzarg7qu2K4LxJLFXfq({0ntRFMQm9e*%^r@!xDHNi6ZTL zWx*QsO^xhUz%xrs??s_Hmfd9}nz68ld+jG5jYX@_O!9avN(8?rb{zU1og;qHpnxMt zax@g4BaPAMfK7OJ#YJi=!vo0n7!-^?B6nhtJ6c3u#GoOrYA+CW&93kILq4x9qxg<4 z;vR{n@bO@{&}_110yqu{VsdQ)>cU>MB9A7ZESCv0z9kY+&CKG5=QojyRE-+`BH0tc zVJ-TboSTRqu#Tl<-z1pyyX4j+G=PmhOdMiS0h_mnl*B^u_vE)&G|27n&JL*wPO`u& z;Tj2=41&61N|=mJA-3oQ@qQomu!%gb_!b6Yn{%AJ|30{iJ4w#_Xr%2Irvo5E7Qz;_ zGs%zdqor&^F^QTA-7flyd^r^sU(rtTWGYHv6E>5{(@>bJwkw>jYVrypX(^Jk5`-hh zq+}ZSQ`s9y%`~*!Rh>g=b`{TW0@LwEph?If)25>ytX&paI~`RzmYslLPE<gaoGZ73 zKqi?!19fJ-j+0Mkpe`^Yn`a;uoAC)bGXsrA*9i9kn&o<Tzk30;g9{ARWfh;&z7J%d zvI}lkJ^-h64>|Dx8sRpsjhfq*r)hpUez2$gtvF66FF{8fW}-oCMjRPE6XYzONj{&6 z`mqVq$<die<0hVZ3(NvUC(ctB4HEavwbWC|tC`4yRoy18v%rxEj3pChp$xR1+@6I3 zJ+8O!D8-!BYv@JDtiO!w>O|bF-KFQ&Z73y4%aAV_6^DM3Ek4VKT(1hqCVF~itT&N> z*+|X$_a_r(qj1;wuW74g@J|eM(HvuG)rTCKjT%_hZt~?EG?Q)cATQ>iPi$wpTqISX zEbzZa$fr6K&W1aZavgGw%AnD&8093(P#w)j#~9_rE0Do%-sEY-EJiuO{OjA8yZ?}0 z{;w8JpbP(vDCeSn$_B1A77SU<;q4G3jpPErZFpnUU&|1vK#P~pMO)b?&xtG^976SL z5)hC4?L$Q1S(9q$V5TI5#KnWUCh&w!fRP0KVtSZ>zGPYTHnL_u+QMEuZ0fQA?LlJh zOK=`rN##OVz6}P_XA#(hqFnOvB4l7Q^u+o@r0jg?IXxd6H|eePQr(SbY~QS{@CyKX zW3Zlteu$!@FV2B9u?%}~0~CcYUQYixUvup-_#W^D$Mnx&^flv}-0Ll=E#|yPfiiv- z>;ivuQ0iAw%0uMdhiKrih7}-!60f#^?5S1s+)`03R8g$}OM1Vfigr2e$Ku+6Y*jtD ziA82{k0X;7qZC&4E4j58eeR)!oJrvHGh_!^8TGIp+620bv&o_*Xr({d9^#|?=5L=K zl`nK(N*n^#fe9{u^7Gnqoh|39v$C@zE6n4#OdOKvF=P^%mV`Wm*H@EbYLJVU!md$Y z+yy+5@^Keo*D#k>ub{h>kNVl)x=e`&HQjJn$k`sM*8BKA`y1p#Oi8Gx=32Qx-namm z@Wsv`fb#Wa9ZV$VtqvRoXX>j!`bj!*UW#72PQ^KPHr)z#+tp=%-jY^0hJm(0ZrwgY zPg%7;ab1SOSaB_x0Z$usH**-eB)O1yE<?S^FUyeLVZkrh7R<r{5SQ)3+hp-_bcwzA z9T~I&&9iMlX8kQm)~AxyA0rL<Wd*Wj#qH$53Sg^#LxMhrXLt))PM--cOs76ZA0x14 zy+1_}Y{D;O)u&+KGd7VkpMuRTx<!8e6f9`NP10#4%3u?|HEmsq7Jya!hKN6-;yg?y zd<MEI-bbE&2D8>sVEXuTGy}2XP}9xTC<u98jECqCUuVPJ$2RG&v7OcH$(aoqiZ9J` z{RvSfqaZe8If+R|=UK6r$vFki@obUQv>+8NN8+H}yfSY;xxWSt08h4a8tSXmitE0U zyx_~PZIpwfTBIky3bp_%fu))wi__2+H!d3{qu}m(Sc79Z2GUB+4JPy^p6RGhfI7%5 zB~8KXfLvV^*PX`VmfomX0Pewd;I^)Z57`0F+@3#@Rq0^58obHzbdW=lk~~QVLn!tn z-Sto|t|oK!s2^;0`Fa%0HrSJ&^<eRe?8yr~(ohrAJp=XYbl^{c?V~>xz*cVG(~d08 zfRM-tlAnQAi_iVa%e)#$mrO9J@n9q~k-F1@H)I?1ASdvG0(vC3TXcbBXCjSDa-A`A zrhY@UPHzVx;Pf~7fAA$U$@NSWF@)Q>vR{YC(Q#ZPn}LZwe&^<49T;SeNYiL^s#Xxn z#a$$3Et~=`=977A(Hb^<GkLTYO?GQL#mi=0fd8oS2>zc6PzPke1!&TU-^jEqw3gN1 zBKNb<#qJq3@OwL!m$mPK^xFHs(|tf0^BG(P_&&Q&kKmSRxlV3mgV{^?oGi{k%RMie z%*S=5VP3$xx-s0}M*Umbj16C|108HQN=B`tvMwQ?uR|U~w519dj5(uO=E%$KG!-pp z=`htoJU^BWRQno?ML}K~<O8!X=aZ?DWxOIa>rf9_VKm^Hm;5YjB~>xRE*E*Y>YqV} z4R_zXTjSzWGAtLZ;3o5i-%82xT(lZ3CH>Z;9=`Rpz{3#Sq?^JqZ&Up&X34B!7q^r2 z^(Z*7+sk=EMxy(@cT>j+F&mvcnq^G%bUP{>N1m)l(XauJ$b+Q-Cy&K>pa&VbWN#kq zQW@FgQ637l8p@YEA$<*~hm&D@NLIBz0)7`o|C>xquOCQ~0WEi^PglSm38&iCZ~`=v zzYJ(j@83vWCu!R>__+`jPSt9Gg8P)0MelijRwym}Oeif}3a4SB-+&_7!|O=R29ytj zUa%4MwD$8+zGC>2AhK&C>eo32u3gptWl-jUs8r<1M&#yNwcOIoLO8>&?FgRXxO2`# zgCRIEmkhzEPpA4N0+}k2mmN;!W1{zxFEDz>YUhyUn~+jDtF}X9TR(w4G0aF-N9Y#5 zWE7b&N=ZaKS+E&-*z1=GT&h0cnY-kdO(>UboNY!q#LpK{@e_C^`H^YkX0#F^dtyh> zr_K%i=44_}+kl+O`ZWJ2NN6v+NwyHw)33{x(7LXYC!n_(RCyYDKQwDJ!-9f%z?Hqp zycKwmM+D5F_#j!n1^f~253+p=9RD(!$fYe{Hq{qM!xor4^?eeQk4CT!wPbNV@)%I) z!WVpTn3^pD+#!kBS!ksw-Q0>SCcbVWR~wM)a@xEa%soh`CFk<d4L0vC*|-(ja{QH? z+6w-ppPYDaL+ZXyHmxg1+TQ!<e?uVVdDaiK$ixl~*K1eC{_Am!SMubuZ4j4TvYG7N z2Aw_(PUkiVeDLItZK!i{uRN24VVm;yw?{#YAYR*Zc(>b6Rfd!OO}uhRF5p6%^6GX; z{-W5G+TO$Kq}*RmRkbI3Q+$p|lERqr%@qHE_W4l2H$PP+oqT=_@L48Fh=6}W@l&;b zQv8#rs<@MXQhcT<S)vpO9#Mi=?Fvfp=&35^<O)ilH%Vjy{vpMW*Dj^_hfh@@Czn!u znn_|U;2%)@SnV;2fACbLIeCoYQ%&%nLChU&p!i7b5sGhks#2aja*UTHw<nt<tUz#| z5@@ykDZ%}xD%r{Yl%N$PATu-go#K~jdsF=HPgUh7dsF=D-HCuTNX*#b+Sb9a^VL38 zHJ@lL<z>5CHc;#aNiD5ASX<S*&X)TPq<5m?G%qWAnU&Z?(ssb{=q}l@1420M<n#^* zg|(Ak;EVNYCa-pYb_G8nZaYzz8M7YYPdMv3BDq1ib=CInU5B_EAnOy)Au~AnAz-cQ zwn%Cy)?M2=ppNBgK+-2#0K4k}VA;BS$u){~(N>HA_xl=%`9#GL=t%=HKnu`~uXm!p z)?fTs2!+A-$b+58GdcKfT?luPR$HwdJ_3eU3Bx-w94L1EYEch^q>|!SY6nyNc^Kf4 z!4&@s;FV_aoTvB|+SXx!FMq14InsI@@V70(HAu=SeyO&F;?IF_kF-$ykAP3MHZwR! z2|m=WrUYl6s!EQmrUX9#ft4A5hT`XIS5o|`r>cS@D=GdTfCt@%`8h@L@!GQ#e**OT z$XSa29`MMFKSA-cwI?b5_)}HNk&_gE-6TmiE4V>&oDzJX9Zm_p0ihomP6@7B6x<;B zhT^Ac2UGk}5c`qA6#uPB&~Jm}D8<KWTZaI?1O$Ji^&7xnw&=G(QbO_LwJj8X2=x0% z3&mH%0x>JNL2`%^jMc8D1P4LEkF2HyRTc#|NDfkbq;@668$rR3tfcr07X3Czj1;fc zo~8JGpx;N%QhY^)prQLHcBEF{2Q;+ksfsz0dIt2OY<D7IcZ0FGOS<khE9g*ouwJLg zwB2xi^*TXT?S?2?JK46|thc9y$1lR;weaY?$BY}g2kw6Ek`Lg)mKBqfJ!pvCZ}~?3 zlIFW>!SVQtT-<{KT))^#OOnBtJBQ6U*xx-d5q|~oubE`zSIEUjds47>TrST<n>QTc z7dTva-OlPav)lQC-^j|Z;28V?IsX-eLZ^{uUqO6fGSTcsbJ(5N$fmuh8ikS>g%C*| zLLL^PUfynG6>QYTfUfr6UK$NQgHHM4kvTm1q?oaMlc7Z@*tfmH<=(o{m%^}kFvBJA zOnbRg)AkU>3K})yUUd1MF9>SJGT8c#79lw@l8Z%1IXw9E2j71H|4oNU$%U)KQ=7p| zpBjHV)&wV)lrJ9oD2+_liY;S)J*F@M8=HrI^t!TcqvVMhPkf5uE<#1Ziy{2&O%@fy zh`h+oVl>3X>tH~Zv}t>orTP2j=E>t?Sjle0W*?d^bta$C{mbt)S-cOV??epyAk^`k zT-t}G`dKRqX}1flzjB20RM5>=e9dpS=+DFDQma>~kyIk^`{BoA^nR%GkfiKK!Q&wg zc&_GnjC#_Slf+YBL?%Stii(G@n5CKfsc;Lk>B3JJE|&=<k~*Kge#ob2AQHi@OI8BM ztjqU~mDyL&gA}A{wGf#R`E@-HvCxtZ*FlJ%b&zDj!P$t^oW7hlK&Z)xR<jM&q|J!t zu^HuL-U0M6d+{WxJpfx}#&MJNK~#xY^;XlhuOW7Vf=sUuqs|B|CW<2v6I)Coj-XG_ z6I0m{h*+U(Ccjcp0jI?qf6vm7S$VcD+wLz2ji<Dks=h(JS%|8<JPtx{Gj%nAjRy4_ zcmh>`6W?+I6ncco{Un53ZO?&WFy{)Sj384_K_ll(S*NJ$an2+<gWh8y7!!RCO@}DR zfpahgPfS<O!HVfggR)P|8SGQ2hnSd551_f#3h)<2ev)mc;Qz)tT_<K-@haj`j#NG2 zpANoTC1TaPRT?+HTP1S^SyYbvdnUPs>K4Cbvn2}PB=T!qK7zP~+E<e=q0OQU!vOe# zRwmOgbrLyWj%I7JXq3C^LTaR98jTiaCn%=FGcaf2+OTo(Uu~Y8GGE%my0Cnci4{m| zJ?<tAiS2wz3M!D7Lo^>V?hO3shK5Izu92%1sC(CG(ED0*6xuwf#_2}*p9Lh7YC+>F z(&;?v9ry`rFofz8kCNl0<NtnGB6a2}nG-izD)_A+i_b#{d;-~e9u1X`fl;n_$qsz! z#ZZQDP4*#qa2|EDe~<o;0!0RBxwvQnalL>x4)ElS>F)K4SgQH)efXFs+o{@h{D+`- zn~NB^%rHeG{ND`IpIWw$-Ip~eO#X52p#DL<8OA0$DSq+9QISg3GSkEwwA*^@;>5Q0 zIRG&Lt^o9#!p{k9?Kc7FlR^TNT?C-C`vB-Sg{=VV0KNc7A|r33PuvYF+S(5R+yQX- zxUGFOz>DQ=?ct{HZ=+B)DC)Deb_z2s&jNU!07wE@YbkrRN}zeQ$~5W@Vo|bjOI!P+ zZEfuh0JQ)=1Kb3-25<@BJir-%;{YW9Mu5ElI{~%;Yy?;bkO7baunJ%~z+!-T0CNCl z089pm0f+(!0~i4?Xj@xyyB%r!H$w8ivvFuU`QJMFw)SKy{ys1lJpj}IN&pW4X8;EP zYXBDD%~nu0fM$S)0QUiY1NaHx2Eca!7XiuvP62!aa0p-@z#f3@0AB(a0I~tn0lwG@ z^ZzNld<3uvAORo_U>d+AfN=n$0Y(7~0~i3H0SEx_1@HuL1Ly=`2OtJu0AA&{wLb%R z0`LIfcYr$pw*YDYt^ia5oXdy#KLIaC0S*Eb0Tcji1=s|z9$+oN8i3CMJ_bkvSO72= zU?#x(0RP)c{C~y#|8MvHf3Cd$S^xh7<@ukBUs(VDng4&A1pc%B|81f-ub%(x|NlQ2 zk^jLI{>T3R$Nv92CGem1|DW~$@07s*6Z`)JIN^)|2nPrO7y{4_APAs4fHy!F09ODR zfDM2ZKpVss{s#Dq#uxw^0BQk#2Dk}uZ3~?LF2T!rfHMHc0ZIUN0_+9&3l5%cYazgp z0cSjT+5w0G7=TxqaK;080`LIfcYr$pw*YDYt^ia5oC7!ka1`JmKoLLzz*c}wnXvxX z!^>KLH2|Ljd<>8TumE5#0B!vL7$pCnWd6U}JO7wC%R7KS@7pU<3V5YRslyH_T}Vgj z*^mw^t+Yd=hBb)TG=`C`Ar3oQu_FW<;(J!sp(4_H>5C0>%rS-yF{ELFhB@pSgAHrW zhID9%xiv_zxi!eJBOT-Rd3s-xHx4&YS+j&cKKFc{@ArA1AMda0y*aM)=-XPfmCa*t z#<u3QYfI!X?4>M(e8`1t*a{i23DO`1k{|)x5CtxnVM0?d0YMmrz%h>hN5~*tfPOd& zz3?V<!f6<Np3WQS9Fiab+z<sWn0b-TVFH3M3IX^C2H^tq!&&HsH=z?wTfDTwtIz}| zpdOBa7mh$B<UO}ISc}#1Jo~j4ZDsSD0LOoR4SAx;VJL(BPz-yZ03L)K*bbSn8Pef9 zkPNp&Jj6mIm@r+>(=-^jc)0>2@G%U*MHqnhpby@LE_egl;WcQ6lh6pyLoGZF)o>Wf z;W5|;k3tdbhCJ8_S+E5x4=)=b71lu_#6dJffP(8UaI1x@Fb0=l7%qVy&ciu)2YTQP zbU+)lz$x&-9DDlz==jeu`?*%Mu$Z+-*Y$iSYt6UeBDGsO{&QXy3p<Iv#yPZf{QqaW zdd@3o>H42@w|tE=dSU%7;QfDL9nbT!UON7ZvRmhU{wy8;dGD%up3w{IZ|VJiQFiOR z-_(}g|L47{{@abJfloMfa1?5w3M$|LltKyYg+j=OT*$U~*$Nr33DO`1k{|)x5Ctxn z;UUBnOh6Dmf&gsd0YuRe9xzn%8ILj#c0v|x0S|11R9FXz5C_p<Mew5F`cr(ygR3wG zmthz#fgjGpId}(p;0$y?8?=BP{42oozpu98H?$r1|C>4i>l<ACDLD*2$_A)|qfi4? zPyq*^6iQ$(>?-6XA95iZwn7GMf;32hBuD@^M1c!tnD`V-KoCal9m~w0T4#7zrk}nY zh9=NYU-u9@2HSaRn+HWu0$xal4Cv%M?t?*?gm}*P8p@NPAGmFSov;T=;0U}5J|3pT zy~Ohpxct)0mQZ9PokAuQKr4wJ=)ljy5L|&Nh&ax}Hp)}><1X`AOMk=Y;wxdi4%abf zPS@)spXq3)zuKg~wWPm^^kw{q{A2&N(KH?D?@4{JGiS`_$m`DM8o(JV>i%H4`SLAy z<c`uW9D*9q^#h=wq5GK_bD3*HO>8Js0bXd6QpcQlyI)^ret(6v`$&izPly{&h#OCc z`^3W7!Zse*2&u3R5+M$vAp#U!r{77q3S)2?hMx*qp<0qpLp2-*-Dn|{v(00$4<3ag z_~N3ze3JH+Z<)KT3M&*UI~L*xGgu77Lhxsy&;WEmJ(NQ}Y=kHX)`mhCpc78OVJL*n z5C;>_hC&zN44i<&PzakL4knJ0hn}PC1Q!#ydEC^&^}=uR1T4MlbD)9aQ3EBA4JqJ) z(Pu)TemD*FP!7An1933v4TY*WXSyhTPzgnl0r4<N-%a$>L)i>o7}RZaX@$&Cy8XL# zW=8iX>&!p8jAZ-u_2ykhp!>u1=C`9ydd$dOqi&_@mh+#z^IKNe@Otsi1f_i9Rf$Tq zh!@_WRIB(*l2U&02knPE=K8x&<<d_?xVsKKRs4E^QfcC;g-WH1kJ`UsGwbZ*^eUlO z@pkb&c!&7qGNn4ji+`+Cm-tb<PkjFYrOt_0(Y{~YgAa&rq>W!ZiuhoJ%et}r$t{K& zl0+LmES|$}@CL+F@DcHL1|Ab1!h_<OpDQ&ko`6q^ucOVBcoy#BZ_o5@FIa8Acei=K zsz0byzQo#|ATI8zP^w0}?2uAk@nPE6i}wyQckybr9Trc+N5r?|W8wkY=kZ^+-#F~_ zpC9Skd86W`QXq*s$};i3CzYxcFQrYjcqXpf>5c~M&o-NXwrZba&9hDM$Q9vq3+@*0 z#Z$%4yukS&?!&XiE9;fY6Td*bSUmehrAoycX;Up;Ox(Ld4rU7#pCnQmlxh~AAl@mS z^pa9N;<>aL5I;|RP&^YK6R&9Gz>1F(cSVI4c9M9s_?bpasbr~KA(1A&_qbA7;zchj zl_S0p-z(naQ)-`hEAdM4GXB!JM*MBs)QZQx&CVC!{}yM1cE(o?RD6=SiZ_X$#+$`2 z;Vt4llU#G+#VbwKDPD^Ah?nDi;?;P+crET1ug9$+sraY_#GCOk@fLhsyaS&S@4?l| z@GkAcBgOl1x40iq6d%Hq#Ygb8uv==JiboRDc&4~3+Em%%(RhxycY+hm;rR|P5bvT* zkt4p>;l&Q$x6%r)KzCZ|NR&Ce+~E}tuXK2|!)qMwb$G4A>t@|z;QHAL-so_j!<!u5 z?C=(cw>rGt;T;a|ba)re58rWV{EbV?k?3`J->lo8e$TusCgK`bn}j^}hD_cLnqxRy zB$OO$s-SoW@o`6dQoNV=l(?Jr)8c;ODmr{T6mge$ka)a!DrKU0-`|x=igv}_xSIuk zYbc%6%kkJBW}44#@L%WpoDJP-sxt9xyh6Ode&!x?%_HuAaA``YjC7ND3uW_cQ`+dd z9-*2xo#H)|UE-H%qwBg&t^HTJj!Bv34NOAK_NuMsnzj20jY+73p2o$`**i$@dX02s zOt_Ol(uv~9lu6>_w9$1v&<JhP#Dlm;e9Hb6-C0@x<a&`%wA)nG;#I`G;%#`Vc;gK3 z;^MuyUpzmg)U@~!Whhr{c=$K)H1Sl!P#*DoyeQW7=#4pEr&275ji#Zz;)Tl$RVUtq zcZgqe8LDfxJwKy4EZ&F*#B=Q9_nJTXQQ8W|zcoCUGkAu07G<XRR^qy@cULj{pj^D1 zU%RXjH=_;JES^WaMf_v?hgs&DgGo`$NJ0aYJ>ojvE8b4mI;V%}!H301C<EeY45RD1 zjXsUy-C=Jdo+$3O|H6<PHpdt$T|)hIS1jH~e4qG5`;Kkqnr+tzRZ1v8(^m1LZbP+; zXX1n6CHRndy!|9y+`T>4P(cZ~R=L!;xQ<VXC#~YLB&R2zZojq7Tpv@r+EB?7^4gzn zGat5+G7Xh4p;){~Jiyka;+2$T;x)7>7th+sLc~>^scOZ2ly&0KTMbn&e(4@V>71UC zoA%ACTw88b(x|jbA_MOb&%-;#d)atce8~Q6yLp!t_W-MvP-UN?rp5J5PQ``C>7oml zxW36nI^xmd`X=Xg#N);5*fvSL1y2{xqV&YM>|fqzE??WiBr+t?b(T|CyvP3iedYr( zRl5vTDxm>;RW`Sj$R8N0MnXe$TPq&4cVwIQT8$5J8c1kQA(xqW=4w;*iuY6YiR<>~ z#E%piO6T+*E};F8_z2~&xNaX1Kl%tKxcENW2jg53H#V-ZjUDFtwTE6bRL<&fe|dPG zc!|Aj2P^Rts+Q0jc%8W4KB&`yHKy`OD3MPyP2%JBFLYWZa?~B^c5%1;C(?YR`b1A5 z*kGtJ$xJXtP~2nRxsy9)0%>=Ac!s@rns_$u5uak9Z1KporYaDxqbwC~IL=bUt8tyv zdohppP2zsa7V!(Tp&q_t?xoR?B+BrBcr`vIUXLf<7Vh+8Hck?ceVIv%*HNa6C)%sN z&-x-yu-7Hji<gOy(_MwQx}A3l@pxQ!qv!6yo5cN;9pZ<4hUyeA#QVhcN27D%`lHdn zZSqh4^RKX2NtDs4D<M2U+pi53Dei7HRJ8aY@eJ`a-t05QYw#@b2l0L4=i6DBcsgD$ zzO|i;t5GU>BzndDe_+<)DO~v@;zRFpvWVw$G1jaJPgL0lbIga0<nDKK%v%g2@bmZY pH@n}zf7y3GSX+OmsXm_xJyD!us^#po@pYz(pv>=%S>sx1{0jhdfNTH& delta 38233 zcmeFZd0bS-@;G|>42NM@2S*q{mQk1)84+<`qA&^rf(yh2*C;xIYa+%aF^Lxxh7lAQ zZB46jK_j4}QIzPYQ2`?$uEZFxNsN*p5Y%`uuDHOfo-<s%-}nCdec$JE-}`*7pHF$t z>8h@-uCA`GuI^!Tv2=5>)bxZ+@ICx&rij(5OD5Y5Mr@J{PqzECL~7pw`J~R5F$}XX ztY(hzG%XOG(nWZ%cp@vEiN6&4`xkrqHDwe@>~BTtSBkw>5xpzt*<_J%2^^nP9E7il zM+Qho@ur$_g$BO1Y(QNU_iTNP-tMWd%D^j9NAQdZ4{_*>^6_+sPBT)Y>N;`FD|K-O zKCH&G&cpVuP)O?nxQEtqg*LR#+xE^R%&L>MzlV*k^Wz>^X|$%0I(hqhQhc4e?Y%e& z|LV}GOKl=h{+Uq}%M=>o3>V1eNKU-@d%jlNPsW9T*a=eXCFv5a{YRZYcQ4K$(!{d7 zqIpPNNA4~NR-43uFn`dhe11Y-5athe9pEF0w;hAC=gtkx-^bLsa<#P7Gz_Opd{G(R zE(uO6?g2fz1=UOm{p+OMErYe6sYsL6vz+{d7;GrGDX&0`j4PPIo5H*oeMg%sn+g9H zH?z>AoAfPRSJ#2NX>Gn%ky0mjyh+uMELLvP;$@mF$z_3C#g78FifPbdN4|GWb3ZGQ zLMsE6T+dL)iVSPE@E*|yoj|Aq!W#Ftm89<Y3rBxW>c-a;<DHHHy=t8Kn$#XvCfWq& zw=HoOY?VIZO(sXaCak+aliwXTJ9>^zaNuj2HP)hH1^2d^p+cx;fND~EXr!x6tDuth zPUyaWOH(6)=2du{lS(N?Kpx1a)=Yd$YB(d1atnDAlV+9KDR5-^7cwWiXJZU9d&c1x z1J9%m{>32B)kCxSpNTY%+%;QC1=LJm&NDThW`o{^>m$%5r}8Gnr@WT&!miGqX-Ug? zQ>q&fIC5bELB%3ya4BD#?r1FywN?8V*e~X##WB{>5TTS?%$u|yuhelmK1|vbFb<r? zMh}?$)Rj5}m{b*D?76Ns{(xZ?!juSy(llqz-&SD>RQQljD5k<VbH26)mO=vyGL#a= z_6_1{=VvHmZQr_2_N|2e%*#+_+e(r@h3~l;>TKpMRr2!fxs)z{c80RLo$fREPDvAK zB%pk0j5!BJRV$lpW;AO++3(3F6foBeIB`ECyJ5CfF)Rm#>S1=cX>wpTxEWHi*Nw?C zB-=W$0vOKVQ;R(Tn;It9WWhkdM38Y@I`lZn)`1A<z;v6nHp<hi1iExSKV7z1seQyN z7|=u6=NXz<ktPSf^kMW{96RtyiV>PM`l<QOg|@LxPUB6rlQEZrgeH>HWsY{wK!8cq zGU5!^U~ZKKw+mpP0!*Zs$rO{66xWVPs|Qq4;@e6|z1vYbP*~YGn56j$3(W03Sp^yx zOUq3EDyx7p?HF2C^RF^46B>M<mPu!ujky<P#^-G<c?QSdN(s0G3{%wOsP!$)P~ho) z6xdrm1+~rmQGAUyl~zsFYqD;%Hw?lsBYCN5cDpDA&}?m-{01o4zA#hp5%rnYCZ|~c z|3s5j$8z}shBFF!GGsspV)z=xJVB7L6|RQwgjc2O;S%A^;G(p>)jM~#z3H6m4-41= zr=M+aoYSbo0{en=xwQlJy`ckmk((bl$vig?ZmxZXT7uoA9s%01L~O@o6dAwR7FqXu zQ7c_Am@$qVk<-#5{F_^MR>ELssaHVlNP$$n>@8{j2q3jGh{2K4-hIkqgfH=;ZISvX zMGRlVjMckJSZL0E+lckwuOiSum^KXlmxkHXchh8Smilx|O`so1-6<kLMBj@i;G0qn zYQ`e>So@?wFnfmKG44Su_@+<Y6>NTAywyFVLu%(yKZ}B+S}?~rK&^Bp8sBv9;w||j zu<Mf-C5|nO+{um+bsI5}z%yh22rTQ+4U`bkLCGp6;FJzYa^`(=ZrvjD%M5j9%ciP! zKKb~!4xQNGYxre{Ss=r7S%0?718<gvA}y|x`JrT7FN;BqxO+!GREbA)?1rN8CmogH z_beGfjt*Bg3X>+?PLu7Y<E81{GSqPn*;Tj3ZuvgWu!^`b3EWB@$eWZJXU=pfm&2+i zlLUW@f9V*?st;pFk6x_$7!LOc^WaK`qE+cb-MdsW4yc+A3pN^YhDQ&V)d%&QgR}g( zh>?knxYT2yci880888xky(>`hwS#8$kZST&1ZG{2-95XZ0zAZ1jb`Iy&pBuhzT)Z0 zs_)|l&oEZ}C-#ynpfdyIYLtR!$n|XMbX+M{uwn7|Pk9(yJR19Wt#g)2P?K$V)9|-m zAx<XF!&YF#_q`$<%h(HsG0-V3i~D$YK^O4^@6IS4FZZrTHF&y@(*OR94E04u|M^oX z7i-Sci<R!v3<q<1<s8fA^O<RQx6d$En~WQLhIuy6ykQt^;1g13$~ejKYX;ROaKSTh zcqbpPFz3f=Z6C?(mb{-I+jDrl2Ltme!88r0cJfj;Pqo#X`cAzmn=DJm=US4-pC}~` zpup9`OAd8%L#$SY%Y40@nP|yhwtfx9cYQS|2D|yiq9Q!WZ#vqKPxxg(<TS*85i;RJ z{%vR!J{0gd*ibjcI8i>s;8ltxEb|e5p)h&Xez91|^vTu1+V$DMpI7Q+2Ir6Yg!IpG ze&^9B4)dM!krA7^C{YGJ)&(OM92eL(((Q6T+4D|}-boyEIwmmm$%`QtYK!{I;xLgi zv*osdZ_c{{+R|}hm;^C5NZ@WY%21zcsoNyJi=TI6^jq=S!1q)y-)IamddHT~>=?bg zrB7hUlNTc8xtCNL>6oEE(0AU{j01z#z?d8g^6*vk1k0as;3X#Af$Ocvx(!8b0#(=` zTpQF^vQF%EYV55E`W)=93{d8XJ+bk6+oBkK@^jBz9gL326pk(rbCOMQLKZPoR?;{d z;UuLmD#k06fqk^^2}I2U>fE^7*9<a)9VnxAV8wTJ9bC?3Tf2&(<8@obS47aA(=m0v z__|WTGIscxa+TYt!?LW73`|HCno6z3Mpbay$}`+tERhr!ZW(P@spAv0!NRkwD;;i_ zQeY-b*QbXA6?QKdtC>C->I+Pu>h8<B$8TO6tSw5u3k<P);r(5P+L5VC7Z-0ETbC<7 zC@OG-bvg5;NEP*R6$uR8^P+A%cdz1GD^-U0?5!z4dsY+$WFl4O%M2hZF?_3CMd~=N z%$)h*Da>WQng%0vD3?3^bOP0V`Qa75b<VkuTgELqB}`vxW7yb;7prqo1x8&1CHYpD zij6V2d)F8+vZY=19)>U8PI&JM0@It~|LUslTKy>mp-7XxGHT2BW|A2RW^YPb3S<6L z5%c?m#U^ntTB2Qo2L~@eNqApyq%?Lusiz)5cXDVlFD=vJ*TD<I5)!*#UaZzWqTIa7 z!?y$~_A!erl4QQPcpPZ~^6a@Y0(b9YdR9Yp&4l&Dn>GFk1wYD<(x4%^20{t<?DN>z zk*Tl?ENo!{&0Cn@(#uh~F_%k41+ffYobbMkQ<NXI2;J^mF;ORp_SNCUDx^=ZwDXRJ z|5-e^n{P+_%*bqdXt7Ukd*it@4?h#5+$-3>n`htbD%@a?##M|eJ-u~Y#Zs6M=?rTE zLT0>`tm&D>p0QSvJySDy*(Ys_Zv2cFbqnrLHW5O)j{2|cI+7>QmCU>G_&_%e`{(JB zo82@>K5NkGGP4mjH20{8O{Fp}y(b8ay}JiXwc%VjH5#gOSP}c;G2O$Zsdhsv-WC>x z;r#9+Q5gQKdv6zAk)RU8Ha?ZX-9zQ<XcdkQo#Rr!Q$Q4a$)_stccDGm>Q4A!Xee8( z$NoL~u&E#7aXpgQ5pMWcj}V`P2_khYgC|rW{LIp(&&m=7=EosEy?G{n-D426)jurI z@xv_nH=ur(zy5(|g)LxHhvREu13VQWafU~|U~y$QryuMB4?bz-)1fnpo+0+dTBMuX z3y<l!kqv%;|Lo}t@eS8&6jI=^y%zNwaXX{Pi8)C!_K6vNhQuMGAUjOvc<!|(i~4WH z=!~N5FmtASKQ!qyVj3|+<(#;Q_+_skMXe)PS##biYZaKF)(Wvgg}RwfU*lSHM?9u? z2y4EE(|gCeHA4&>MrSh2;-vI&e7pBVwp$e*(8pKJ^v@{D3DYlqYTw^6y)`@5JJ!!f zx%Xy_)l!W8IQ=FS>itO!Ueu>kXl*Ym?UJYLV-=m(PK-?M!Dz@avMth8e3Jb1O0W^X zuWnW?EhT5pAW!^DpAL@pr^r9)6fz#c&-z3;1|O7zh07#O$8c2N_mB#w_l=7zxB^bv zNuZm(M`VssJlbqX=`G&M$YK42o#G|^?+tc=yBigJLYY&Y#POvtO`6Oomb?V+IgZ$+ zUm)nVXTJc?s7hGa6TnXx$aOHJJ$9$S>)GLyexI?%)A)~mU3|hmJ)-Rrr7~=YqEr1b zEE(h(aLKAXf&Kdr^E4hSv3m+Ko_oW^?V5YRK;xQx54@~@h(x+b_|)S4{l}sN+}3|c zl=un6T6c?RG}XMb8sf1=YQE|hX3)&8i?jzKrJ)dW=UT%z@u>;_==^)3VP&-Z29&ER zx55^wrp<`c2lzOfW*A^T7(~3bISCgIP<4peDOiXsCz}qlws<nWJzz34J8<9#SPTsV z7qZPG@UwwqIv9;7i3n^7J7zP9K-oMDPad=k7Ret5tzlF9;%V<iDfgY`n@h2%NW!d) z(JwD@l{e9Wkfe%WPjr&3j_l0wS}Cr2?<;qya`U)du)b$k$qbnwjubB*9O%<9k;>{; z`GMtp6yJO&qrIoIkKp3L9lgYdX@^~`$bgmY$f85|*5D9TToK<K|3(BpAKL!B$euvk zID6kDQPJDBi#>4YkS^|Fp8wu>7@jetvzNMna$j0Xo9^+noqN^Sc<YcLvEs8ru<bBh zF(jEaIpC0Rt($h?Xrm!o_GJ)r0Z<9}^YEo?Y6|{4ylcm1=<yns_V4nKGHwm@FX>Yp z8nL8f!W8|-CtOn2UdU9AATagtnl%_7ix|e9TaRBxsGy*;R_3CZ>|Ft?^s5_mD}sk= zgFTw3h}5}OhIMPi4`o~~u+-u-?RHmflzWUJ+C7@ekm`mtL$z%BIJ{u!G&X%SzBDwH zz3YTu5A|_O`B;u&s*3^_W5uv2XefDDfOGcznke~JD0b8=z@}m0Z0%@VGi+vPy^60n zv|5u50f`-{5;1}KMGv<{T_I~D>vnMWZbiv9^=JHYUAdtgpI@uPNs&=(_9R>w8OD}% z$2TLp*e8#NSniV}QBPPl=P&#$dNNzph{wc)vKQ{+RWZwaOo^JD$01Cgl~EX6Qesvi z1eM%KK0p6G++lc_fBJwK5L%@`_Z5p_eO71Rhxf9DZ7>F9J4<oW@F35MJ^!|b!h`?^ z0An@W-`}`vbEJEt9ORaX#XoHRzL#|l3Uj7vC%!R!B6!>$u}bzb!HKcqDhPpvt%VkR zuDDrj&eb;**^!@Nj_s?yMQ*|Kz@^*xWUPO5tz7SyERkjT(V&IyMzqBrK!mm2{92@p zdIdY05gBT`SEAr^uhvK7C;b^!)&&DhmW1Z9-K*WBSLz^s$gg$9{&Aj)+Q}Je7bc^? ziJfV<HFgy-X9ky}==D~Td?$~~_<_H|pK^UP4)Pe0KbO84S6%woxE5F_&p`kK@n=uG zAx<X-F=dkMG<-WQ-L;@jz6nN?tFMJ78N6VGhP}5KZysS{#q)8xydw@D>BCmd#*;@5 zo-=zEospL!->T8N27)JrP2o(yeIK}6%m}&@R{6)UaLNHMQWqtiB<F2<1>8B+*|;)$ z*u7}7J#+^%`I@vs8YHjR@u~U4aO=n*Ry_p=jnabmOdi$Qvwqy$HX;16`tG8!c-yD} ztT6%K7&Ta`85tO4X1p(Nq8q4Gn08%8YgprOJbLtSw(v0CJh}(U#^*<e`lm+J-3w}4 zY?92`vNvN=0W(S`GK(3}z8Kh+re)%e@gbfy@73KGNP6Zg!Mp5Ccat&<Q5;1b@w|8^ z)QXqI_e8Jo-uT|=IldLINS1c5YYJ*xUz!ojF`{4{GX!eoJt=7nM!8qtOwc>Euq&gu zd3BDGzk3_{Aj!&W8zJ4Np@MLh#SGISxO6cSCUY-^froM`TxSd9A%i6r@UW(0^<SqU zkBDa^bPGzKMD0y~ugK1vNiKlq&G$Z(!&9WW_p$s}7+9xnfw(w9Ax&p&fifgmz?po6 zYZIb|tW6fFof(j5UKOmADu&Kf7`v2{;KuZ8KamI==6h$TM=)}G2C^0QvTKGj8$qW@ z44(162iv>^fBL>6JZXO2L=G%?1cUADUKJV=H$i~#u}gIZuwRj=Z|aY7Bm#5wv@66E zdD|eEnPDX?ssZ@M`x+OCD~$_b<)T%w%b4zA^BiHD%xJQ)#O}43wy8)+zGBcP<g+4W z91}<BW`Fo}w4rlfiZki)yfIlwiyw{Y#HMSo>)0*>jIWMlnzKW*^~?Urte6m}uxBQo zl^emn#p*dZ5D%=5<ZI%~3So+kHpq|BwJzlWU!3+co-?)=JNF6RHrAi5Z@{O<_VX+n zusV|CWS$(t_6$386^&?WyJClNUD&zzamcu!4i!Fp@ttR8>hkP~1zg_Oc;>iaUbO*c z!@%+kbv1Kl<La`3r8YH$;8Wu=VaGdkd~aAKmyV|kb@li(_&+xBJAY$>qh!z)8nc}w ztBlaSup}q*8hJK|F3IkPyG%$(o7-=N1rR1_TsuOdSyT$EU)i14P|l<(+RLj$-nY0w z`Se$`JgiH5d3M-Q3#2&9?zGVInojNI)#~-75=#GqmYe+kRo<;{DV>$Dr%FmiGW_?1 z0Udi(Rg|ubS$Y~fG3A1I)xJ1<qE}kMdRX9l-ZsV=Z)A<hA_iD549X#Lc!G0L^V+a5 zvKGqLhUCfNDRX|%m8^m9HTSy;Y~~IkIq;n`uPe!gXYMx|IXq=9@B})U(|*m$JsEqW z95RaR944<82DLHx3bbYQmucECO~&3#=9X5z8ps{acNIvCUH?U*m3Gb8Ylm({2^3w` zHYTdK6wOL#g58!NP^?x86fw%T6g7bvd(W}k#t9UE1_~5KfoX3^iaP@n(YA>K$?z@$ ziMY#K3R3_u5pA0)Q0!F*6tfj?DN_A_hutY#XB+|qiYouN6bYSxhutY#XXd2&2_$O2 zw<KZSz=S(BTxjNeCxOD)=`Dp+4otXHBL#}CJ_1Ff_gjkQj=;nIRJ%sJ1d151w-hxU zfQNnBsdklk3M56IZ%K-!Ff{0vO_1Rp0)^P)ErrPyhDLnLCdgiyKrvhPmLk;|c!*!y zwCd17pr~@UQFI3<m*Dt%s;zOUP%M}qC*`#!2Z*^He>#@}bTwk!I@wDSW)J&3J6>9g z1EzFhn?-o=l#n6uc<C9wc)DrnVZJ!xsCf-!WiNbq0^ak+9}^6<sgc_L3W_gjpDFtq zVvq6ZVI>7qda!I!BEIp#LAGdqN!GL@$al8FY;e~#St>GQoZV~u+~_6`WSDT=$bKJ= z$0ccy2&W|tl+X5}Wcw^xV%bxp8&NZinKZ5vmt0NijM$i|ICQ2rcAD9lHBN-r!EDhu zYl#6$8WXIq<ue0W@hI#%tC!=EEb5Okieh5%G<_#LL?6H!b?`cftr}`AIjZ+#)!~%Z zL7+4af{y`gT|Yd0);u;e41YUo8mrdej>#K64cGr|@r5M=at}Y^Gs#|T(GU1$ax@!! z87op2u(6eRT}qah)|+l*-qx`#GN*9JY=1@3$<2}1Bd1PTMcRqv)~HK;!Z$Me>aBQe zn(fb49m6@ZgV@>M;^Nt%?Cit1c6OKWqUY2hTeF_$XygE%aDjIPtd}3RbAqgU$M$y` zV^AuA`|SN#JEx<>_)Js~qvD&3@dtB)$IgD7u_s0aF0Z-SPd}1l$Wrh-2JSs5DNB#i zPvjV+qwItY8Km-;fn%}H8c6Y}<LicTt!BDqvX-i>rRn(RIh~LMKb_M}Ue#pdE`2lw zYA6up+gywL&+VaV-0T~fTXk#1_U}hVt|EVHXGU=|R;353_O!yHzf=&p=u5nIu9wQV zX-gf_z0>QOFqR61bX36tY1IaNVQ!Ff!TIgg#5y`g6Mi|@pEVxDKJ!9h&54~C>|b=< zoJo8C7!Gb^S(yyapt0yLn|iXaX<m4j0v`w!%91+kwOo+=J6IjNUA#c|)DUT$0CU<G zd<i$t^L0(o>RB#`7UAsb71(Efmo)p1v}UcwY>-sT?3+dM0*FMo`fvD}(k&UVx09RU z+i~83^%rkNuDnRseaNQ;ne75qC+!5aCrhnu)6>5g&I{g)nOTtDjOF}S#=$A#E9}%P zE;LNe0t~Y&(B*d;G#A1_u>Qd-hG&@>`1E`^WH5i4A3Wm`Yc>i@b#`%vz&$P2Fek7; zypC!o@N3(1m+g5-ycWjWNgvI*wvTZ%qQZpvfiNRDXKbJON*Lv65iM8X=?m2CH%7dA zL7%?qDL|y<>|yX;&a_H5!`+-Yus#D4t5nt*k`I2$P&slHH$<v)ujpjMMlpW0V4+{^ zB#~Ld)cHI9QfR>TQMM*hnrRbSyyC;ao>jZ(fC)|8^W-ShHB>C+r6|t5;vE~X>e{fG zPc`8mJ{+1-zmQK)?c!Z8nC|lN)J`A=nk9$&aJ=^spPt~a%KAoU`~Hwmm#S4+lzT>j za-&dMFds5aYE*wzv6xAaLLzI45VO-6WzOXDdl3BTN1CqnDL|$W8WIeW&W*f3`22-+ zLMd&WVet8nK0x1L)xslQWgY!~-$i5e1Vu;pzqsfE>$}zsH!lqGVcgxVV%v;+6BW2% z893S;9Ga>_%kYv^HRR#9rS_La@nxnyqQY}dTcWAxaGWt3H>4^9MsA6wfgK#PjVLtG z;Hah-=9(RaZY4)x&7y&5IG(krI~ssDESk;HGtf}nx~P(!y{Dw|;||DG`AiOu0CJip z2*Nnruvp8+tjArKC|EVYaZ7w$l@A4if;GIN_=}Q{mo(e4#t(s_Gg=G;om{?>CsPLX z{o(YrYstaox9nYESJq1B`Hbw|ee;=;J?WF})fMj?;`E9lMzGC2MMvK8n_1AWtQ4#? zM!*3~vQdY`c)xK7>WZ6;8IH*%w%F<rUbecIq~MV7sl<m@&vpx@7ekos7s*B#)jd&I zojodOq2;a3EFb^YqsR?Z%@t4h=IOuUbtX@?;t4L!9w2MnmScBv<2Ee~NUaNH?H&9o zI|JRpX*oJniYs!uvpaX<^qh{^Va+zbfNaX1O8<Xno{Y=a1Ryp3ea$Y!;f%FIkrr31 z?HODfQ8$Tu{R0G<aG0FGu$3PFL1K{Q6QuL%rgASov|+B|fLw2Rx*O~{Uc{FE%e7kt z=Wu*(pnpKV!A{atw>79uusb%EN@DBUX>7_J#O~aRFXaYx+PPy>{aa%`V!Sj#I(hYk zW0`z$8+KY3=zPcQT_Mz~{}vBg=MQINlh$>LZo8JD&TPF^MhEel)m=LzLV6!ktJ8mg z?e)K5c+;JYkWL=Ky}nh3bS)O3G(<DB6rWtDU=JO{x7PW3rZv6gHFCUkvcq`ZlxM`Q zdA`ViyX8${7v8{Gc~jYn?f7n9$mqP<|0zIj%<}Pi@{}2w=#Yq_VPjMOmZ43!njXLX zPvIOAAzgM8Pc!+tUA<;DBHflMp}R_)ZR&)SxWF_PP6b;`YIGHMUf<a*?<6nP4wKoR zYo%u^3Hbf>5p0nfZ&<Ho^A5xC_rs1GoW1i*&$KB~H=?3rkHxCulrvVPJ0y-wgbfnz zv7v9k)g1^TAcsWACchk$1$uiqbjbVFA~;(Cp6NYs#)kLVg}bb_bZ<k~j`8_$ETd?F z_1C-9dMl$i{vuZ4X&{#Y_Xk5djr}~@9=to$R0(`Ok9q8o*7o(g7{R!D<U*j=F+)|& zid5NbA?%|9HoZ$l%`PDk{F3R;zIV%=p-h%VN3X5q-SW5e)A@JHL5^;Tz~uV7h40P? zz-5DlEv1vLeiA;o(KjvT1@)^r+{sPbhW0Yh`F0GFKJh<M7SxPs-}+_o`E6_3OT4Cy zn{2b0{I)IsPo)vN+e_Q&rk`#vp_)pwt{{}VLkIZmara&1RXk-=C&ZO}vgvmb+f<Dw zZ@GdrIC$%L)P~cxzCz9=pMTW}$%+>qG@94YRd{sP%IGt&jNU2Px2*}Wmy_|v9jj4F ziQmpCNYyYQaCd`LPW_vcI8hX1&&YG2_m1Yoz))EB*1)^d{bal&U)5>e*b>7QZEtQX zlRLn?vF=`<wr@@os~gP1ck(^h!v0@B%SZNN?aHg$CRo!`(%pld+`iUi3pQ-P6Ltq8 z9sYc`nyuF1J-egX3t{-d?rH1=4IcS*G+W&dZ~QvbH#wBAjW5bjooiKPQ3LBd7|t~W zKNy1h?CAozq6vEzvlTD#r9Dw9Ws6G$xC|JhUQv`vq|~`k%?V|a3ajhR$NdVz1Js?Z zX?GTK!Jp6wyhv$=qyrq$L+>++#26QJ4p936O>NOzn$9$f4AT&3L~1eqv7iUL^C7k? zOlQ*?O0o*`5wgSTy*=IPeT0Rj;Ag(M?eJ3kriV99-#gW@po49}`3X1d{nRt9>WZOf zG)*Plsq7hjo1U1PA}aBceFM@8IDt}m4YKarmL8SauR#PBD+qh-1dO4KZw~k>_B3oE ztmOul(g?VUi?Bv)*?#(MtpY~C6jT8}a`+t#*Ji6j`F)vvbl{>cx5kuoGxtEj6{mpB zstlDxl>t|!rez6)$#4+Rmi}&u4rlD|=AL_+tN`78lUqs#gP?0i;?n)fZssF;nIqEx z_n;!o(7*JwJhO4KLBI4hq@nNZZ-A>fqiuY3*zG`IxM3XMeDz62P_=Sp>_)9V)82K( zNN$vTKCIJDPAADc;G7e8N)At%<AqaX0d2{7Klu=zxtGm2^*|h3aSWe2;Om-vly6>m zPmmc_h=v1wz}`B2;}@(f0L0x4RqVfYi!0=t-Rj=$mJ-kSCII~G%5P-D8a8KFkrZeo z%YO5}6tN%pfZL8$(^>*H<;!-utWE87^=7`A|AlOugS<#me`N23xpY>LONZOOk;6(W zKN#R`*a7Eq*SAH<p352ci<m3>7ft-Zn6ygc(USFRXic~`@COl0!RU9?*1yegc75rQ z?F&N8=BpQ1oEC6!jM>az(49u^T`t9)x4)vgFuIbF3KJ2ImR5R{lT3(TGJ{r<VCZ1> zKJ^8IU(eBo^>)y`DpA3?w&a$-vB#kZpQg{?oX|m!#Xj_6s3te}svPEyOx(B}e{{$s z$^Dcp00TI%7K@A1;HHGJxc6Y)Dmb}05U3cXeq=Fc$?S7&R;=479}h#w8CC$^yd3PY z9aFGDz!ZFjpB0C7xcEWFzBLZ|uOuDilb|h0wG<CHJgo!&J)D3_V3kmk$sl~r1qt4J zcn9Kf%#qF=RBZ&9!&TAGZLIWPe|7|o<USNWxru!3owoA5<TF}R(?4TR93%e%m^(k{ zC!Y&bJ2*HTOPa-GKJ;e){bB3xF4~(B<jlA`|8QPZV5j2K7jD3nC1t8&AM;v|pG1Xr zDvc3mWg?-~`pH5w^~3qv(!W+%@U%0Y`0TeKfpMMrbhkBu`DR8h<~(IwnKRs_3en`~ zoj56(0<x^>hTV@gK~VAPsJdH4q`<~7kguKo6Sh;7(_0SEcKXj2p`n7I0!1<&bxhg0 zB3$^Y0FODn=IOYhHY(i^p<D-tQ9WT<s5sWdPOrtqmIv(4AMuXkAG6ahm2^1ah}c7I z*#D$2TlG`P;FEn2nu`~G=O>xIOTL`yU4qGXO6S=RCYH-5&@r!ifX{z-T%v4gmm&L< zGAQqcOT}YVSeUKsb{ri3@U{H?$0~)btl&1jcB+fdY;*V~hf!}`Gep%^FWk5nyPo#s zW*3ET8jgQICF2?kfRMgHbT|IA(#LT(oEb4!$hH_mhGESQ-<va$E;O4jb324Lo?Zsy z?{ubzkN8VSlgA((CE0S3#*o%DL|I-29(N`LZU(J9(*qsChtJ5^`(?QD%w*NhS71tv z!LFy|r<c|X&c*QuCDk<HZw;u67k;Dn=A>9Vq9dMMDh+nK$=6=JV_vg3>Ek7kr!5Y6 zB-(dTNt41Y7gcNUTJXbD!MdHDF)sZ8b;FNJ$Gb-~wUn9-lVQKS^UrTeAuEn2ly#pv zdlB?>s7(=8G0AMY05vWQ-!yfoBqr2A5~<fo*>}XC&unB}VubI8z53inhO>Ry7wNMa z839vSB#(x+?X%|L+A_${%-v9~g)R>*kL*2b#=&e&4s0>EQcvu5wzK08I>?sS^r*fZ z16%luMfNCGK9x>{wRxlyG`q$LTgp4L@n-x>d9*`8BCQtx4eol@x9|M%)`UZjnaDV; zooJsCLD=(3FX4Q*%#v|QaCjK-%DWujwM$xTvJ127G`|fmKRcG~@h84=){{NdfE&)< zlcwGEE?=C$cu_8V{?0mF`+YC_>9xW2NM}9AaR6q3n0_1gITwN=@QiaFY``!0({sN5 z5jT52HI()jGtDdHEqQZ~egh4s+u_WOQiz&v;3MZ$5DWc$E+Vq+s-&8L)xx^oT^<9` zO~~^^8C*=_Q{9a6dC*@+M^&K0%uISiZ<Q=9rKfptbPbcuEsFp>oIaLJso1ITyi?Bo zPWRaq>fx{*Zo;SUEW^@<5VrI*4sRIF2AsgF8wSa%U9jC#nu9P+e+>r=h(rUIEEWE_ zK?yev+ZrO^|A<D9{sneG@l4na42vCYI1)?qOL85^#MQGaV|i2BlgpNsv31_$_4UZj zkpf1C*E9wKheM4$*y+FF+Qu%P)h%XvOVyP2C#*-T&|Kw9?DeO=zX2}rM*K;m#tl)z zZi{jv!$G3etDoVujb3=cpV7!3m;4zSl6lX1^l6It`PN9A(DzqH>5cznZpLhjJh`!M z`#)Q4;x*zfO<nx<)q~lijG%0|#<zVfL^40p{>q`hu(QWAn!IU{*VGx7(d|vWG-~lJ z=b^G2(F`31IKjPjkjOz0nM2p#2y_|_B5n!XVPDhqPdoT+1%qquB`9ZK2;T&Uk0$;W z99-w_VKiApNj-eMOb$=TJ-G74rC4`AuumHy{pm=n%OQ4aDq>-ype_9pqC9J9Oa+bL zvaixcL`voNaHYa@wFYmy?~A766ZhYfN8|~FvyVgdKj^KDD==dNu+xL3X>-5TY%ymZ z%tq1K^j8U{g_Uqs&+X~Igi*E$!YE(x#XbIlI}Ujwa}>$|7j<r=8zU8gQE}CXH@TIf zn8K_?xX2CjsX1-kzamzvP`ZO}-6EWzTFtNOz|Ba6+a9P@JB_p?@IcYtN#&#;bhJ=q zmcvt|Dl9}Xq%V{d7{10M{|aZ}cEj4g{MoCA@PWU&$TD|s)|!o6bgpjDCb;Usg82CN zztmln^&sp&Azo-{*Qu^a=!T(|FV6ev=Tdwo&J$}ND$=e#ha2?L_0e%OXalWGUk7)! zq*TFI8w9P1O`x@VMXt0gufARB>WyaMrc$oNnT&){UVCZ1$n4B;FwI79l{sHPGeMhq zUyu3=>M8OznAgzfp*3b>;K5)NXx@hc4@`n&?VX1bIiKJ6G8dzFsAr#!6Ld=J9>Q%8 z`-b)Sl~=gkGG{)j;b4U5d92%4*)et8)0gdCGu#lmR&j|hzFLlF@qN<x>wNK{TU4oF zGpjBTk7^eXD;z4vZ<I9UT6bG)Z2M)Oc1F4yfsw9)FOEO?W?WM#7=*&jC=8{|)>edX zdaTxl`BuRHeeiQNI~Cp(VZPOm)%iVrFT?*~Q<c^KI)#UmcDhWFKA@q@%;np(-f&~U zYM^UyLUWfi!+Beqr=ZQl5YzogBRbWExbvec?XNIW`9PRRlHesGScU{M-2!6`O32#s zZ)RFiVN-$ylyCrIysLEKDkL{YES469ZH|Zkdv1nzb=c;Z#nPCtTQOAj#cBC5eXfr* zjl47*#ZbF-yS#i+m+Pu)aW$i_Bwm<#&i=-jOOww}t7N=1*$1<a<Q&T!w3hDZ<?n%5 zP8a8aMNq-di8M}3+}7^p7AGbhSG{U0dEUZ+50}1?FPG~-e=gPkDsg~Ik-rnW_0cM@ zG`^U>qeq-HEiiNo6R62XfgzI+_0TZD>1i*_neLY%DQfiw+)0~;%+S!zH{)XW(lsiu z%OG@tEQXQTTm}F4k$s|OZ^D}*Z1cs%FcO=uK33(2iQpjp`o;IZ_nlDw{b;!3-7-P% z{M3HPU!WUy6;bXD#CINX9+ME2qP_K<%|pTajleCB-bc&vh`$3o4f}1n{R(tz0^OFF zGoPNu>3?e=N3;L$ArPQ6{XM;d;!|^W_uuh9_b_l}n+DMNc=F>ekpExtI35C%%a0=^ z$wc6!#NJN=<qMDG#^O%~GWt#ZMY2z-#Y7H90{2SCK2fuI+4zemp&hPfjVgtU)^PaU zBP-cr^_<6_%z_k*-_s!pYrAF?IYVP~-&&;<1;?nqsUqFL=J(I<j7HL{gV-^>To|e~ zK^gm;m^b4@g<utiYcy4!s_-*sT9f$a>|oVoR$%kfUcmadr<2&LoAH2Wp+04E;W|K0 zCNsC*5Ob?0aE~2(1MdCssi{sl{n==?Isw-_Q^j0#$xu4cD`xq{bKpWr)3yZS4L!-O z9=YW{9V_9hX~gL&N)tAg?lS`O`=DE8nQ&!g3)49tI@s)nhdf{0M>?V2vMr(DRe?n& zQIrq0w9f`cYJbJn(>K^Y2G*f-ttT@z+3@Rv75MJ+E|S`hm%jlv`6lf4Vrf{x2M{eB zLb}Tq7kjw}T}-$nh&K)*U3n>iZ`g6C2;~(M`TRQv@V75^bA|~(f~T}}4=bvpr$?k7 z7Aix1H((o<)K@>1vzKc?*P5I<8m{W|#8N@q7k|gcTYROaNt$eW6Hq@?;tF?Q2I9Ld zgVP$~X>DeRCVR-wkPCcW@GIu>;V#(pgMF)e+bf6C3b0}5WX`U;D0y;)ex+)A3GHZZ z(sqJ>BG77vIp+o%ft}LJcU=|jgJ2mbMJfpc_t;`JS%E?4UeglqN(dB~Ef?VqbcdzS zp*G(6(gSY%9ep{JRjj}-UWOw$0(%w9CQKreosps^9Im|K;3^u5frgV35=Bb8H*kwL z{V#hi#gGh($OF^_m#g1oKsoq_{AT>)tG;Yf3U+)Q&L+*mar7}0=e+I(w<=D*p2RAq zV6QiEozv$_>LI`QhG+8AjwEy$r-J}UaHDcqvSs2JobjdyoF$aJ8OZM0fgipZ4k_*a ztvx(Ap3lE}pNbG?XcyuT{=5~=Sux(xssa0~ZtcxB=Hk}Ya4z{S<gM}0HaSfbw@qLh zSK%XV$2r|~K0j~MLL_IamTp>&da=fjH=ROoYWvZqm&nIH`4Xfm%QmGVPc)nKL`con z&mof$(x4z>KqwI9k$njD=aSFTqn3JvI>RC?W)TQX`m-p8tx6>6Eb_FkI03@$rHt6B zvE*A8jRS@f5t_tS4kxojD2Xi^Mk+;UGvdf}I~3u;ZR7LfSJU39+IyEDK}zgUJZtPh z>^RhmHo>7Gy^_~cYY*)4Tm|9ZOB&}1r!v$9wEu&-0V@O%(^anV5!t~Z3u+*V_Q;5S zBe(6*5NOs{jJ!pA?QfC+V&v;IYpu!<*kxlY9~METi&03&AL*Ac5p=5qyR<MAvlB3q zk7x#%W>U#@F*@tTeJ1Cu)9BN>wJC6|_uLy(ByTD`Kn^;fft~JIPZ_~|iPXjiA`KYR z_i}InqTsoWgq5I$nm^??=yfJluM392{3Zsjyt<e({ooie^NpMXgWwE_fxBtz)5%v7 z)UVqg`c4uic&{qU!ZGP!c(v1ck0yg1_-(GP;Yq6-qeVmoJ|?U34tXs>{%q-S;_HZl z{O`|{I|4sP^FDo!BmB?^WOllrB$R22$TUY3$2LqQW=EuDD<_c$jwo{AP(JKVS@%+N zwqL0rj53lBD=k&p?#fFldfAi@`9}~ILd?zd7|EX_|Bxad$M#a)MzYokg{actcf2s0 zv|&S54~NuFIEqFwE2H_awuc65dGUL2r)?X#?S$k`>z@8f#}B;zx-Xqk09r#7&Zrt4 zCik3?8XY7ZT+j@(mn?BXI=etRHm6-s7qpwyx}c%Xoe4FOU5z^^qPr^^;PRC|%aI|$ z)cH1TAs@S<L2OD8Ip~Upv8s9GZ&x&&jrJvj-H;cn>O>OV&=fmJR~#Zo-OzwoCq9gS z^8V?$lZT#OI(gAin6B2Xt9!*boix^2=P;C0!>;n@_VSf6-sRzjrU<%f!Oi?M=!tdx zYWxTE#FLDaA_@AOBub%I%gHh+3Pzuhd?}ji0zbHPW(bT@hS@xic)Ft{Z1hXZT6dU5 zo_XaE75LICrsm8WS#o<AK6^0U<1*+;^iSl842|*Yap3$Vh|wi2j_xsfmnJ)iE0`8` z=_Js3O%I~)i29=l@>NIlo|pakYmgo1rBZi8jQ5*%euY)!VMo-4O2|VGpe?qjJkf_D zbj-5R8ztDYv%M`10qA2EyuhR`Abbz9ybJme#akM?pdy5>TJnO>VT4L8Lsdw}qEce) zij?RM`KBw%MFHf!VAK~17X_nkXu4&4F!~&!ofhwIPzprxA!w+gbh~EFz={&dCaoM+ z7#XKb;H6jLn!m8#=;BFn2=YM-$;A+GISVbXLXZmrX(XYjca*Y!C|uAx_lB1q`oo+x z+9+?J^Qy8hT}KAK=Uo9w^*8P@W}`m6!A{_A=)+6-J8x;TdRx*%(E!x3z&|wmNH#BZ zyV%~e!H-<(frk5BtuSW`m33ZF`CL0bS#AjpLv{#+>fI9^r9w6JLVhToh<hXKzA)FE z4L_4(^wak-E_;D7+20$mJxFzL^d7oOxIU;S(Dv_x-uK<v3W-DUxi_k;D|CpO-5z!? zH)le8z_h(4d-@=SU)mci_F&+*S8%=V%trm3Eo_}Brfm!j9;JuK?~wNeHLLrg-k|g; zebIa`uFTpWCgPsJT2M{l8FITX`Z#E=WyO@>y>Kt!jYn^fra}XXhGuKq_CbmylB2(7 zV%qMICH+v0yPP^R{n%z!-onxxnVekehvHq_X)$@iLL49EPP+C-y6z%+Az0qXg4<Ku z(fI4<U*!+rD`ceo%8_8iaBTwOub05Z@t%Zi?T@zlq`l}|uF2A5TUYw9)X!iac^L%B z-`PS&4?yGCosY<#0Z0LTsu+NJvnlJy%K?Z-jpW`y6chLd{N4h#0ioHw*67!YJu`~p z;Fls|uZ$u`=J3UyI$(2yj2nbBY}yTyJ_rT*gdd`r$?=ac1XJau*#6urn<M_1oEZcr zd7eBOghu+d!rey~22MrnVbOlYH@9wvZC(Ql8_}#PGW9()Ro=QiaF3a(6LSw~qjl@W zf5PvOns<<^@1a4cf;bL_`TafVGZ?JClq3yC6VOTe@FCX*Lo-J$4nt5P>(JvFjc*oi zAt@0kEnqq9EAJNBK~w*PrivO@n90V-?HA<FKw<XLS;S6@rUnHo$aM(AGK2hk=JQE; z#fuZm`g&g&x{4Znomam$zUJW>lBq@g!bhJK_!#E{-<L&jeA%9Y5fU)e%v{UbTPymA zCJi3lq1}Z1|0m>y7Io{kgzlPlK>`v;zZ2NS%mQ|e)|^B0l@smNXU`#{hobkoeMkc? zp~As$ZT<M*ZH13Y+AD}>l5dBi?jk_q+Ls+IFNUHLghr49k;vzRbq8gRITD#9>P@cX zlJ-K@=&UW(tF^oA_$0T3Aa>I99|rzjw=t?s{u8K%tN)d+Irt?Ft2T^dC;c5eGcM6^ zEslm*bWBEXK3_KJ4R%zNx0G7HErC(!5%Szw*?z{j6s{i-u-c5G*+JO<Y5(50;Qhc_ zuzT1|z)-*<-qE(3_!ThwyLEhLH*p^%aJuM6Hb$fI$P3?o+Ld&OK|U<MnuNxnS^hl? z_14h+vSsO{7VBjJx&tQHfe~yUNO27Kv_euF1D<y`@fwa^x!j|B1o<z}t|qRLC}Pn9 zw(SqHE*2%iEU1q~u`X`eFb#uN#&*u<nRv^4aj1@E@9ZOrQLu<l-%DnWLZ6{<^4lmh z8=WD#(dY-XgUI8-2K~vxcqrUK{uz(<JKC$|mDF3>2a~l4C;}}amlBXSnok-M&>+tS zKhSi|&P#pa@-WDAiO%DY6idJN!FQn~^6?mm98?@RGY0vxSt4?449fO+f9gdd2J18y z-8;LHR8mW7ctk!M3n6F9b8>Jj`jd@6LN<>>-O+7QIS%z>^A3{MaVVd?x{KtGhvFJ? zWjq?-#cyxdnh+%myizU`|3pw!wIwkTokVPkg>;yRLL4PW<rkqNP9u&I_-RBh^bhj+ zL^RZC+IJebjSKs;+Hg`e5iMd13(25KAa=@bk~InDU&?lJdlE`vRU1k4WEADe`N9UO zCa(bQP{qOtq*XbU>q-7(2&m%Lld{QZiDyGLrP*0Dw+Xz*Yk@|UL*k~O5cZOhd^!c4 zb8E9e&L~ckZ9V-+4<I9^qE77eqh!uh<O?HWn2MBa^fGc_DjE(k&C{u9x@W*X?|keG z#~;dSC7&L#7j&O~r-;n^0HW4iWbX$k+$*$|dfb-B8R|7^Sk_+1kI}(X%_OZKpaE?3 zbTVKX=(%z#SuhRtVO5jK?rA8@t8&5(@C<O9`Yesn;DVsFR>K5xZyH!>{XdEQbciMH zk0ryVqb&3hsh*Cy`M9-hFTtGME099F^O1RNort@!tK`hu^(7=}G3*L@C83+r(WiN> zTZJZv80faJ;280lfmCc$Z!&BKiuP0<pskj|-zU(=bBwLk&Sb|7)WFv7Bv~`jG`7&2 zG|WVwIDOzzNh-it42BQL96gF=?OjQ+9(l$^y8x2}#W-76U9G#r1jX13a9!TI&eJ4Z zf?|yIN4yCEKT4|qr;`)t?0+NDS*VYq(C*E6@MSfJwn2t9meWAw@Y-y+k|j`q70;T5 zwz3DGe3=XpVM7b?Nk+jg5h94JNfpRv6i1MRWUyA%L-KbrbmZaBmS0lPmn_==(dAsU znaw(Aah`{EBm0Vtd|3Qz^6h+>y@h$C%K`{FQ`VA{1<1rkXOb5SkfPJNr*sEw-e|Bh zNc7j9u)VXl!VeI@UTZ!gy&=Mi&zb?(*Rot75Rex@cRAhX;OPE>=mWm6A^9`td`)PR zcl$*foi(j)rBp8GYh3TQQ|dmYl<UY3AEN$)3zvWfiUV3TIg={rzNNfMU|zqNueox! zy$ZLO$}zfDlcTJMK(WxO?U5wvBb3h8|4J%9LSOiBaEB8({Q`Ft?aXj#@La1vmz6}O zE=0?Nzucw`RDAm)Fy!1b{g+}_&0283WmkS#T?S`?L3%r9XR_4VkMpE;A>D?IA#th5 zKjNb*Qbe6`Q7Wt(^+moAi<E^{!n$ECty)6YsX*0}7kZ=Ehq`UpEaYs9R2c$;p1c74 zkfK!7J?vnaK;AePnD9j%K>=kSm9{exTNgVB6*|&r1<v_0h}|Oe+;bvc<LuZuf0uJz z&KE5i1taP2K|ycsAEU=?gPPbcMp10#Z88#`jtv3U)OK-N0dZZ7dXkHak-=5-3w8pp zupiXr)a@pjwgjDLvo4aZOVJ#sLi+0x$n>QdR+0~wp)hiBDRN>f-;kS2fo;P};=c@@ z_AkgR`cyr&n3tht2%K5xPhpR(`i0E<6#RQMCI>zRmz(kvx$r4C(83=HyBuY)stcB! z<!Bx_#S-%DGpf#S$gt1BRx1n1-Opjv3U^vkzCcqETRGHnZUqWO{#mmiK;r8hxx3gg z^A&csdzENx$dZ3)9qXk;nubEz=*2{vhR(8;eiplQ*w3>m4wlI)(Gq0ux{Fs_-Ak^o zLj545b;>}!6&zQ0N&Jj2JGemxp=zOl04tnTC=ysUh{&`IwArg-HE1FK&N`Taqc{dG zxmp`kg%Q_G)JxM4V%1Vu{tQj7K91{36LgENmCr*X$u{7&_CaT|9iF*={Y2(vg6Asi zObRnW4=DlUb|(12N?+n-fbz-;GR}bdz*?7WKoM-AGl8s*51Zml8Vo3mdKj-P)ThJz zW`XU2W;w8h-!40nX<3l@@F&?>XodZb-+1Ztdg5#ZkE$F;h8dBngXR_a3M5!_zg!Ly z$(%#ZlFy7N%%k)VvvHbXeU;we%)mze^_Kg5aX2Y8qL@Jy+m`ofk2c!(i)0fpF(|ih z8r+US)=W3ea<9}0O3B(mw5wqsn6;TqT#Z(-_8ZBq)hN;HHe7<+c@{1rKakl|YE3pA zxu%ByM&h#3YF76Xxt@(GyGCDy-}te-G-5Yg_PzTDH9kec=WyT<^yDtxg4>4WGC7+A zUQhKonYIQk@y|MD-L5N4b2Mw~MshFAkPK_5jW&_yHDH8=he@xsRM`i~g0;wJ5O+il zy@9O7H@r0dq0Bj~yyZ0Qr!Cn(p7xY`3Em<<F9WV8voPq1)XJh;NZDExB269*xaNhw z2uq0}p1cOGp1Q{%vFXn1cWY$*O+s?fQVwn>o;gAabI}TvN`ls*kf4G)z{3>Lq@Tbs zZ*Lyj{F2qgvc4iq)}e@Q!Ov5KJCokOzk8RR5UbZ&M{hY316_^^Mv>d=P&_Qap?NSD zVDm984-6rCE!mg{t5kG0xs`_^?FRA1S4f}<g}CQ!(`Hv0V&F$u^zX_fEWA#VOlXN~ zL53XGNZ8nZ!AlqJBY&9C%${c;B`Im03_nex$}xN)P*g1ArFpxao)$`zKNCvfZ~I7l zkfrMp{Qa>tq-;If0-c_`0d;p!1u9;`A2V<zc^golPVw*?3)TPVl(i$uAaZ*H^71k) zwly;s#>x0Li-x6rI>Jvcs4s2Q6AebaIuv{)kk-TBWZ={JguJiGM;JX}xfx{EMx;>e zywh&BZ$5#AG0IF<#OUWgXXHlcsaQOVOx}ciTy!4`TntlrCV*Vrh;rGYBrD2QIY&S# z-{+aq`<7Lk&~k*F$?GrCryUD>&opANUCo+ALq>2M{5qiRN3xor?&?mPBkTOc55XX_ zr~ws*-LslC!-A3cz)b;bA7Z@8EdoBV(oAM;hM1(HiLBiWyTIr_$??tLI~&T$jm<EC z4Sx{-EhwBVyhEmKK|cMGJM#I{4pHAlfIHkrkO{4%WLR5~CM4I5<!Uv#9^YA~gjE97 z9dd9By2f6;O;&A%w&HIQ^Hzu{8{J8VuaK(um5poR)@{$di&ff3#5&fxem0#r@yvAN z^6~$^E8`VC$o#J$2YYBE+4vO*%|qb%734p9kYB$-ozfPrvxphCDQ{m}9Ha`8b=`+_ z{`s-ebfT{XP7~JxPTQ1Mw^RH##V*(N98xFY{(h{iJ<*fmb1dR?#)@yI_z!eX-vfN} zV`b`zr$+(5+9K8p_=glfN%x52A3jzlop?m?S(Y@hLLhiR3C8P|Qi2DMl?f-7Qi4p2 zSSsM}QT+S5MHGMUu~K_t5yh{vh#ds{pA<hzca-A)e5?#Rag^fIE%5h8tO7Pre5~#; z#Wy@wDoz|e%1cXI(=1|EAh=5jbh^Hj;O=9k^h94u@ESBAwKDjF;uq<9Qv4r}m1QS- zQvAzZvjJ-oTd_lQZwA8hSNmAmeEdxbFTL|(J;kmU*V4KJbrn79oVeSdz2oKI@zS<u z*|VF-;_a|S3Lv@LA(In9_HT!bSOmEUU+i?A+}jS;mG&2L*nxbf?!1pb;T-CS_!{Nr zr|a3X4sq8&*T<ip07;qvYge~fTtl(mx;OpmSgr;%eY^#*cNzi9)@>1Ap;!-Hc{qgN zS3u3j%MXJj^&}ly05P`jK)oHN{j&fH(`w1h9mqc|?RK4(tEAOd=!S$t_s&804i5o} zJGX4+VG^IC_~p8R6n_>vcz7Vi{{nc0RXt}ZeyQ%wV8E9>R@NMTa}4k|ZOS!?%P4-4 zu7%>yfN~GFQ2Y(Rr#V;|oS_6C>Q+#KQ;(I!hgVR7>p);<#h;@1xw_>PfAX<1|L}5( zzXo`)Z5W@E6rZd+P4UOUz7L<K_!__?EB-je&(NKq_+yWi>4#5H{1uBh&1&E#@i9v9 zfo=#T_!g9YcnBr9WHWG+_*;sfq#H=_M?md|2U7e6i(ua-@ezt2uX{5H@Wr6`!*9L? z{CS&wo5aNw|Gut;;tzs-A8w)eN|+#412>5eQi4&s6_nrr82I58l;E7rz)j)<6d$Ww zPVr_i@Waa~{;bWuO=2^}>vX3nelOVf;nNgfRxViRUWy&6GxP!rEqttG4zD}~c5!Cc zZ1Q(LIEw({vde0q-QdAaKSAPl!5((Hh0NOp$+QRpzkHx}yI*)*6dw15htqB=uG?-n z=LsOA;K8;PlGNR3kn@=>X2ZhfJF6l3*iDY@MjFp)TWLue1av#F6$fj4>jv@+s38T> z^K0Z0$Q>8_UBw!n$+Ipx!moh1=(-(MuV=LJ`De)7uVE`Zl^ppRGNO~n-LE0XFrFy( zpqcFL%OrCTszSra$O1?x4<x@7pdMZ1<TF^T%^F{qi|2>KPqEXdJ+Nje9~LpTZ_=$0 zMFd8ad;Gq3`1vSoKak;4cqXFEy=j|PzLchoxTn59@cFJJw-9xO+{>{-q!^NR@`E2f zD5VEdxp3llauayy6Yu{#-U2(9^lA6#^(nvd%_Dz1DmMcM$9pPzAX&FT{LqRgvLZN) z2qOK8AOYT)Of7=W_>*--XpqPB{hDk^)3zvE%XLD_zZJnGmXnu7Xo^HeKB3F6`X!mR z7Zfig>3bp5@q`@TizcbX@&YPxf%t1T*iVICf63SQ-=M!vmr3kiu8gJ1z&SY&$bfxN zr-`KQLlN({@-?kjk0q$aeL2p4(zDo<xEpcFkP@>sQ@0XMVK#n$^ZV*jp+x*g;2t#! zEbNa&u;7yAz%jeJ_Gqa~Io(KMW7Uf<%}DK6&Nojx^fqvysk3$p;}f7zafVXf1bHSi zTEP}pl80tAhm9^J6ZfNKY}Rp7vme&T=%bbw`_VbXHf*+>`34dv$klTH5bA``Xd*rg z39-?n|6%kAx?(wW7}8bfphaE+WA8qC!ynm(k;_ll<v9NhsqysNmZRUIo-CwP8jpd> zZ(Cd};GDtk)W=adsJ`hqShBxGaspDWPCLLOSg#JG`;*v{(8vzU@{=^?*kO769U8$x zCMMzxngS`1ug<^#T(O)!15>3tP0BvB-j1I@!^7GCpxe*fDmkHfLGjj;@JD0auM#V+ zXgP5#L(1+;{=G`f@^`B=ZhW_jaS54PhJw2<^orDf^qkER%Yl<f-MBcMctyHYkuRam z!mPZ0@CB_T(l6CQa-<B+2wP3F+!f!ijFnHOsluFO`4o6|TQhHU)M)q{I#*6w59MJ( z*uF_*Inp_-`;q3uZa*WYaunb?non^34t^6v6QijI$?0;`)o&6=Uu(@mTRYV_*$jU$ zff%U`G%hFXS=6)JGS*~@G|WB%f7qz~kCWKabk-`yF&k|a)Jw>;vycLRpX8iH@5x3$ zFPA=N`#%q0C_^|UTR?7}MV(#Z=wCySXMv5|7tSU2-=huvrad%gde_UxQ@c;@$tN8A zidtR!6{UBZi_tDNO$ZZi7+HS!9(8g4vIa%DImV|Ze>8SjtYVpRR;Y58WkL<w<uEFJ zc5B-bfSCYY0q8e{2Pv&>4FL2>;ZrEP2|#I20?=;?2LN^em;hE0-OuO~@7+sV+sXhQ z0w|WXwoL|bfcJ@(pMFM>Z0OX_TiYn4+MY+?c>!P*z*bwC^S_>!=v#<IX=gUKwlUjU z+g^Uv+V%wCA;6yie*oM9xB*ZDZ~@>Pz!`w!07n2002Bh`18fD@2(S)dHNYx>F94PS zqyo$Xm<2EmU?RX6fRSIdrnM;u^yWVXY5&Q_q3yK)SkHxyr%}~M!&qnm1_AT|2nFa0 z&;`I3z!N|U;0Ry`(7Ls??FGQ!01p5f0BQk#0k{ru1>iiuS%6aj#{h}}%m8};b^vS! z*svAGe=WRZ0i*+b2CxL+BY-&oGXbUoBmyJ=!~sMBgaZr!=mii0paM_;_yBYSa0PGx zU;$olX>EH3&<t=7;4Z*zfSUl<w!rvbf|p8wGJul+-vS&2*bA^5U>m@f049JOfJ}gY z0DKDYF~9<V|6>FG|HSzJA4~tgH{Sow|Nn*Y{O{G@W}W~49smEB2L3z$|6{7R&Yu6S z|Nk#Ik^jXI{@4Hi*Z=<~HSpj0|KIulpVYwrFV_DQ*x@7rOa>SSFdASuz%YQp0Q~^M z05kwW0R8}80386F0qg-7fR~VKcmnVc;7@=*HpBk!7QEa5r~$YDa1P)Iz;OV3NKAyR zh6F<v?D62K0Pq3m2;d6f0KfvgHo_hcpc&vEz+Hgb05<{tulCMACeHE>;LrPd#j8D# zQnc3&8Cpt6M-)j@QE8<ND<zDeGD8|>SVI_gZLJMyupxc1p7pF)X}$DC!yM~acQLGC z3ju7p?sTL<b2F@I9BVG-Y`W%bSc4t9rn~j_d3s-xH?D4=vSvyD`0#w5@AG}1=Xsy^ z$8m3t<0ORP1ccxy48RcxzR2}|2<e7{&<T6N2fLsZ^x$)bHJ6>wpl@r=_G|`&GqyFO zrQI%vVKZeDG{6R^g=(mPGAMynPzWm^7qY<%8Q_5wP;i+EPVM3NUqE7T1|skwjKTXb z0>|O<UOL}L=THbMAQ!U13mM>n6i{&a6*`9t5Q8%ife&E}-iHx5Zt-Inegl1Q7<%9h z2*5t@L;Xv0gEdzj&#+%>&h~6(6X5vIERiRg9ELX70oz~`G{Tcm2aiD&tb<Z`09L}? zkPml37TgZ$a0|Hf@xRi+<21Mk6L1cqa2ig*2QUin!Z8?xes~Ld;Z5j*1JD6`pdEHX z3v7jE*a-FT1k^w!Smpd!1I17T1&{|huoN;O4cuTt{AF&nFbU`3EPMpxa1z3B0zz;U z2H*$;;TkLbe{}q>G5c%n(d=T@99`Elovby}#5u~las01&S<LPv`WolZjpP46Tk30G zK{u}dYc9*zIHPCR-z;ALXV>uzFY6n}e@>Ql#^=wC<3Ho2n&BBeyZ&yx{?Ey>&iF;` z#_Ru#m+HUWs1ETCryCAJC+r0u?1EO<4x6C~8ejv|TKuSn3MhjTSOtZ!0&*c6ypRDN zNC5>GcqlOiWjt_b-pK=oR^H=L*25D}1C>w?YoHj4paAk92dt(1$b>X-g9-6xd5;H^ za30RWM=%a2Aq*!V1V>>2^x$6sp8tKd4Zor7xc^_*30U9Y-XD>}5TrZ=-Ea^(VK4Y# z7qr55*bM8N_|X6xpcbm30?MESRzV@GfLzE1FJyoRQb56FzHyGPwK~efGX2zTC-i`R z>iQ(HeXx!vwKY%=P2h)oD1l+l<57q}9I`mSeUxv4e$ciCD&a|Jg57W!x_N%G^i`gd z!1-6NtV*PJ(kYZe9rV!<f&u(}h{8os`#I+!fFH8AxXkA*{mG)!uP5<4T*sVky51@6 zrH(xPktY3VCH)DcFXKP7KlN`L>Cal}k4Js6GiR*Z(XKnc)&S0!b~`@LTsm}dp1I@Z zJF3soKkNV>==yeW!@;2!Qq7xO%RAE&2_FRDsFXV9Brx==h30qXTN`&KxB(@&0VTKr zCAeSAj!L#Ehc!?PMNj~FkONC06Vkv9Cd3)zbC`TKVI{f<9Dokk13GCow3ELRT3{<Q z!xwkz%O`1H_?B61`K&~uZBHW61m$oSD2Tq0NQ9sV+Mxl~KsG2i<4Yt);4tigCRhh~ zkOJqPPb5yjVb}vrunzJd1<vhGB*ME{1TPb~eq7hV^^RZX30QjR>);T_qZ776Evy1B zTz)Q*h`@2^fp*vk<&Xz%*vt7c+MY-R!4J()0r}vDKKcnWMn41~Myy$vR>Dk_+HYr@ zD~CSi`<Yz)0r0PvnfDm6p^ulD-}czi-y80Og{FJ`8UDMUe;)r4p6|7~h<Awh-L6zX ze7jev9`Wisl<F0~oTb#5c%A)dp}Fkd;5zzANp^P>&lPt+s#Lys(PK&#h)>zSU^8p% zDSDMqA08BM#QVf2o0aMp-}ED;2E;q@kodMuN{xv7$R8D7gNMb7$r%&(5<ivVvaasF z>pnw`OT*wQLq)`E`SRbWcoBYfI){N{;^*)Q@zT$fniS8*r^E}$xg=hRyZGxfz3erM z>=XBzo2)msC{-n~!KaCfXKqufO?<~zrP{^Mliwje9AWO_ezuK>7vpEetMQn4g!~Hr z^Sd5D<r&6xyUMOsIvK1+8vK;a;$tMWh_{f_Dqex>Jl)Z#{b`Z;C(Hj5`z)bm+&w?p z+>57+hwxnS!IwE7#JliP@zxHdD#T9`uNN<WMX5&d06DGV4aD2$%falW5|D<xeM)tU ze@?t#Jm*!VhQ%w%35$O~{FHbJ9usfr<iLtg5qI5^+_5)^d&CDjEv0g#a)E|?@%sHr zm5A59rc}B3-FTgNcR;BI@gVUQ@y-0bbDQ{aa(v<$M_GLFrnfm8v@^asKqVjz7x6Cf zBY3y?X}m`~G{rS1Ubnzh{o)Pyuy`{b5^upr#oO^QaX)U2OQn-aRJ;q1iFf0Z;z9h9 z_yDdJB$xCU?iL@x)5XKMSNs&7BOb-`lWwUADuvQ;2`?5`9#fTyyYX^y-$hO|hgUhg zMtqo@T1ULj;q?x0SYRdhKzG{cXlQo$W`}Qcc#FeZ9p2{fc8B{M?w@vxfjg!vc&Eby z4)1b!x5IlJ-s|w7!}}cG@9+VfFTmrP{~MQ<qv4ptL(^`5a-VrmYTD;qZ4xTAmlyML z(3Q&BBB87_lLMMu(;)FlM|?_rnD`}e5BYKNFmdHcZX3s4;xRl^JeSfdKKgg1vOKPg zt9P^bFAb#|_3jkde=atkUp{`B>vKAEtErmBOYm*tRrYiDn~T5i{Rfw(gc@n?67Qkx z7Ox{m*YyZ3<n)UVQx1rqCr8(HPP_eAx=vjY=LJkcUG~BU%*9JK5sFDDNKcdEA$u*& z>))c;oto@qjApNR4rP}3C319K4-_RQUp$5vici@;qdTkopIk2za;KZBReTrmcJU*4 zuXxuLUd6>n@G<e)gi>+w-O5m|wB+!Ec)oanVW>j!TD&&Rwc+X<P18{I(olrAi*LBe zP=4_c-Y0&=WvGGae7;RHBHn{X#cS>T51K#zVex#%zco3Rckz|tC6vYDmBe*jFIOFF zuvxr~FTLC*p5ZZ6_jC^N9`OtI4@%6%TMBMrMiL5B4vXvfG4Woy)@^#20X!mpmNF_{ z%rLsHbM$GHnV$4P;$HEn{V_u>FH1F4frLiru3mhMc!T&E`;OJ-;?*f>hH8;egw$T~ z1L=keidW#L#JA$(;<@%Sba8JrT}(*Gwa}#|#dUm2+`Ev=k~Tg0eEaRy=CahlB17d! zsLlRlwfVSJP;96w31#E8;t{rP6mOwy7VjWuvv^e*J0cz@-zOfR^owUbV5ko9bN3rc zx9J&qGECLI(6#DnC6`LCG!)`};^lb1c$keN;xYTvHRe6mU5~K067q!%6&KexIhBzd zr=Kod;`%1%cEmm6`X-m|h-ZrX*fvYN8!r$qp)AaB**|^AoVSE;ep4%@VdPy-UGV|? zw-1?*r0!X7s748m+6zm$rFg$*s5S|mqFbMM%wAh+K4|qk%4r~>rpLI<#8)md)iLo= z%8<Cu9}(YMYbf2OSGbD&aq+X15pkU#6+ghgp3!YOzn=Vw3|Gq4N~>+GHJ2^f^@^d& z7bV;4@Cxy*_Ug6lNt95lgpT8W@vyx`H%AtmDj*>*?`FEhC+wf-<~Ys0j^?1a$NmG& zMwa~-J%#u_L&c=U#3#f{?K{i3W9AV`&rHrRjOUBj;DzE-UVb|fcP%khjkuq(QT)yQ z?8<Z;*KK-L%E|8%AEWFM|A-vw$vb8(N#oMch)2a+@R+zC_uiK5^a2}aiD$pYq{aP| z1>*Vk!tb(w-UF<<gu-~U_ypZ;6OZGq;^|9GrMuB{FTlIR$0+;6cLfa9FTN2EiR-sU zBjWn4QTR6bmH+hX?5s32(y8nA<N$*|H<VkPzw%ceasKdMtrRcj#lBd)11}NZfH#Pr z4zgq7C3uH;Wsr-jQz~^d921ZJj#-Nrapj*CkG;dmBK`yyW8327MAPjp<>uo?;m|wf x=FNr?d-1)8&7t=mzUe!ET;jXaRG(c*Jk5V#SMyl3ShlHB^no8rUF=$5{0n8P6U6`k diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index 090c0cf6ac988f811b220c45565eca027fc21d50..018b4731a710d13926559766a3433a4d058edacd 100644 GIT binary patch delta 27155 zcmc$`dsvOx`#=7yr+sMe&h4@*9kz}NwUL}DR0<X2JPxr{&LWJ{sMPLEn<Yyoict|m zjvIptX=EtgGsesqBFXuQ$st4i?zNw7H0J&Ley{HzpX<|gz4m&Zb>Hh=r+eM&UWcc* zwdQYY%{A}Xh+c=@&o^Kbh8&qI=!4igZ#-9!aKu$4LUsy68N)D({1b$H=r7^JauYmE zJQ<nee6bT-E5haCj%X0RB@Pp7#vwOOV~)EUT8nGHfM-U7M;Q*BQ5#TiBWy|x3{`VV zWw&}u{j(y$s@`7sTo<oM=uzK6|IBFS)ti5OR*tK;6F$}P_?HIOTYY@4i>h}NKGwyT z`r{{t9g#m4OBBcxca}H@F$?QE3Ll564f>@EIE6B(-a>dEa<4UyhaB$}so}Xm*&TAc z*C~)wM2sSy0tTngne9@#m#H@r*3sCe5m+M$vN5Lxz6A+1vF`N_!r#=oYShZ{b4iEz z2=S!vfmBy81Aa@*5tQ#HeX8@XcM{&v<x|H*)muq#CTS%iq}3%hH(%|QBDq3-MhsR9 zZ^|nXBNGazK?W>WtMSqCC=%pQzw1f8(?#mc_4NdJu)1<8&s#Z_(-g$RbG$xb-CA8D z#ysqkVD)vLS-p_c)UAQ4d+|I<zfU}YwR(ZTQ?QDwt@Y6H_$%E$@d&Sek`uWC3h2i3 z)D87Ynfb96m6v&@KoF!@&ecY^=$J~&nNNb$mp%!qi-Q7n;`<?C%_p9~s&)lh3<Yw; zkw$ji)fd_&T?k3-xmfeiPs!Bh^z=ng*ZDxl`;44~YIuvpHAc4ah47RxJE>4A<oOC? z;XQ_9N*zknDpR2!9~-zB3Lnk+M|$gGeGU2v*)b`4$v}&o1y~C-BViA{oF$xwQE_z% zB3-OfFDDLKa|UOw8KH~ymdERQxB;U5BNGFKft*q^3`i1TSABw|kYHM3AOcF$5TJ;K zUiu7x!c66qrBfi*Sg6qRF9ZHBxd@G?F4mEcZGc$+$w|E=jQ)K&yiZE%mCnrMBcfL9 zO-zIV%1i_%#K#q`g!c(ay(Ne;>JzTozMkid90##_PT?A;{aAy35>V0z|B?)@R@u)h zQ#QGT;cA<F@o*FS_=Z)SraXdE#M*dea0+u9b#(fUjv^_ej?xQW3-mB9Rw)L0Y^k`T zNXUF6lrnM{2{NHN5jt-q&lxxnx?&`+{ke@iUAT@bh?K;+`M&_mqNrO%ZB68yq<-ll zuMGUoj#0(uOD(y`1eI5&YIK2Vk(y8D24WL}v9KEH-`B~cf@Wx%Aeo3U3{_u;;acE! zUC$l34w`ltrA(%j$hw&yDTP2`hJH#@?(>l%OoD<0gsS7Yf{5sEghG9N3L%>D2WwCe z+b%XR88R3|V>Rt!qrklQ52UfBkytKd6Y=`XveY+iE!k@6A7w;0`rWK7j`2@w+0REx z)cTeJ#VqK{)s{zx#zXSjDYQ{<=xYN_rm5@AnMce4c3mSkK%vV0<Ts$fMeZp?XMze- zXvPs^>8~=@E2W+h?%*lHbUu|DiWk|Ft4)a17r^M`5&o$`+D-oyC>><0e=L;zafl}i zCBN#Qq729BpDM)xhx8e*<Lgk^1G=ai-eG1lwGti+x+Df#pF|eU4@2ujPu=e`=5jF4 zGKs_h^pr__axZeotX;F%IVey#V_iZ?GN({XB60zfq>cG1ZzyuBuPbhKS96N;3A8C? zKmUS=2c@QBnRz#uAOg&7J^aV;DN<wGr>KkgJB3bOF-|n^<(Dv$=dGGcc~#BDpz+Lb zm8pb<@`&={HlANcAU~!5aQHRXi0HHV0{qn6&Po$PUm^o3q11prE9YW63on>G11*Lc zMh>P*T-Ob2EL_<p2YlSZnT_m+f3xuJSZXu%XM?_iSL%^i$QT|WU6H%mMcMqCOHg1J zOP7w9{=m7t85iH?=IUH|M*(BdzsqO>Vbb^e@C?f@*z!Pp+;TQjU?-V1%pJXCgV{&b zc)2VTqAFy4S$`Y+UKWPbc!X6rO2yw;IiMnZ(yAL0;YKSD%aB7!M<t5un?DXWMuZjC z&a9><9%elNdEi9ro+t(%v+iykcp(6-O9-%VzRwt<OSGkNYAXKA+Q&4lOeHcjmcKDz zWa6rP+^0i7+t{z=F3^<*N!whM-zvP)23{hs4CpL~Gdpx)D+xZ)!5szTJ00fQoG3PS zo~0LECj#LF1SM&U@eFx)C@_|Hq6Lt9un!FIZTU>s*ri}D1c%P2XYM>tE<%-4L~rE~ z%0!~KSJlw;2^v2<%SI_`Xyz1}S$LaGmXUuvdc^C72kvg`VidW?;TiuPjHlancPd!1 z@){N<zYkM$O@m)1^-6v}M$JW(uKmb!(AKTvV|7w-2BTW>%2XIqlB^QAnN3rBXB<xF z3K-1Uj(~#nw+mn+7UQ*cb~a7j|Mc+pmBh4UUuzQy@i=Q{G6+;I#HDr}*wXp<2RnBZ zia9$6@McUpHdv_3xP-cxug4wJ5*ujPG{YkX_C|>HS737o8zaSF$unLx#ki+K7ihTY z4x^w|zH^v?La>=5hQ9vR@hcRGJ32jKFF0dEMGCxRE2bhPex_K))=kE<I&ESLR^qpv z!r1E8Bg37?vqrVSz5X!hmHY-A61>-Sqdd}~WWDO^{Y;crmg2yGI<nDfE|$Tg-E2_= zp651oq_UUArLQO838~DHuUeQ@@?Wz-G2bTjmNMy8r{i+ZPgbuZ>jR+`HJTnOTcHi5 zY^*xHI<cxqJq>8kvTr-81wFWky588zeE^6Z?Vb%ych|iy8|i`VJvM{R9r8#7eeK!V zLlPNCtBq5|Sk-y7PSrbiMd`S{^WYA`ji1!fwvuP7%Eo;>-MvEAgA>Mjr6&%{JzqkS zRbcNl<qOrpsvE`3I{96BoJQP(#w9GkX`ZocLKpnbbENsDwWNWnXy6|KQJi_fC_LUP zRv8i6?Mj@7{|hS6reaX8i*heBLo12mN=t_m3H2rSE_FGvm+5{9JV_>^7k=&K7~k+8 z`7t`tHPmpi1*_73_$JkgsdRPO-NLx+&Sh=Ot_)ire5K6bm;`a9v4Jw7qT-mAA6MNf z?B~ed$QjQXn+1{}Jif|6m3Wc04N`5p$l}+j%+9M?Q35kA8^0E#z2~$d293*>z4l=6 z@~_%#&*A7)W@Bc~fb;l)2w6R|p8*wJ6&GQ?%6@U2#$%QJf;f$Q6+hO_Y_ob+sRaRT zA37|{{<;jr>Yk$Toi3i1iZLpI&`Omnup$DeWAg4OY}eI`oqg&^P*+Ff6k*0k&x%N^ zD@m=l7CxqRcvL16GBdeS4NmIjX<pY`SP|#J_!6<sB9FkQyZM_dVgoAMMFwGG?~$z1 z08jSr;oh)E-)P&pSjDi#<K}1=jGLs5bN7EiQ^-xhnwWYIz=yr(nnvx^vl3wKkG*_? zSWhKh;-h3^C*j>bey9om?lXg(=Yj_*y-_V*pp*+DCVoART>T1lz&n%*REo=#10~Or z<=;U+nLNY7?u%LF0KB4mADaS8P>rVP+vpU8lapDO)6K11f^r!Cp}V_bsV}&0P4Qds z>+YM`^j4hJ!yYBzy*);u0{o`OVs>8>p4-#jrl4<9qhv-&vV0$iH+nLKlz@t7%pH$+ z^mMh>ET$Rwv?Mi1X2?>k7_c;v1^AbqBUotzcJ&=?`aCI$r@%OhtiwsZlV;8PHL14| zbBZLL5;Lj<sWhoUIs*%m|ME%!dlwn$oYW|tp<4M$)LW9!DhbUEaj>=C({s5yQsG`m z!+8-)@u@9&GpRb{Eu>07)5}EG3{9Qjs7t*HkL+b<^XPZYJYmM-8=6IU+(m=>&4GV8 zY}0THv?NjjO_$NjQYyMiet}_<np%R3dJU6$7Rg~4$RwVZaC5Iws2d*Q7dlkPaQ>xn zJYVoP2B%ULcekpedy2O)av?Aag{`W)4+Jhp8x@>?Q(V17`i>t<lG7-JNI~N=9^)E6 z7r%(u!ZLnf@LG#3L{FU`Qb$3rmv5i~{>pjoYVs4z8An}@aQ-kMckZ3QUVM&AdVAX` zr&A}wUA=1-bs_$A=M@sC20z9xdq;HeXgnf#1*K1ZFAUu|r&>3AD8zVTAEmTuJpY<t zfH(D-!fGOLQ=cF<=05Jz*M3~;vl5c0+IyK+$Do(n&vfn>l*7ep7I@w*QYQw<Zx}EJ z?(Q342~g|PdM|d!(w@Bvm|aWRmG|cK6c+e*g2F&Eh&g{v;2{_{^r;E&?dxF?d<A;> zq?nOqNDyd-e<{A(cLsP7L;H;YW0~D=6f6D{-|lC_nk(_!eq$_)kDVe0z>lR8G#>cP zJ@AzNt57-qrT<142Xh95bl$t4YbslBP%L4PTVk+kb+IYmWqb1sU^#G#q^Sag(28dG zw*fmWn#8AvpGi_@rdP?-0$r)n2f8RkBWO*`z8|Me8_6{-`tY~+q?@WeitvvE?Hprv z(?+o<qbyv@KUs=*VZk8p&Lx|<rh~5xJfwV4Vv7y+Ml{(#`>803ukhqS?v{~X{j(^M z%kic`?rbo@rv}-vsX6$@pw1Fz;x}pmLr0K!@M2VlR}K!A)s4VJz=ox&gJd^;LDit+ zwfH}SSA!)8_wQ!aG%?A;NJ#5=>5fcj1a(Xpk2U_Qtj&k27N?tiJ^6N4b#h^-e(YkW zfKhC65?&b41ElW^ur@95wXK9%^X+5mg?Gdk0=j}Zc@ZFE6@#&8$R1P1!6H~4)G3JO z>%S169^wzi;lq&W@H=&=51X#U8-}_{i)P4ofN?NgI1`^9IuZKIdYF@G`XmE)ScYu_ zf>b&g4;eO`RruhnVQZ`^dvWD$f0m@HMhXR_%7Ag%S^N{W4^*&>D;^XWfu7*(!2TfR za-f?ia4>ipAxB;ZHnXhPU-;?p$!yFkJUqmko%<562uWaLJL7jD3bw!o+lMM06Rc<a znix12YQuby<RJ}7e*(|t-2Ec>9lQt64R!Ajc>W)AEp$3e$>(rUsH3B}e5=Z$Q4U1{ z-|vsD_548PUAW-0_+DrvH2csIZtRs~c<zWn-b!cc=7MGOkFdkyCUHrY>S?jy!`MVk z&y~51{4tE1e$wyd&mbJQz6{?O;XJH#iOOl2M3!PtT@qbVP4|+l`Zp!-4SEUQz)IR7 zsdw^g>RN0J!p*%I_Y~@DHMzN6u?R{GoCy<s?HoK{q#c{k7f&2%FHlBXU%}Iy6pl3c zi?N(C*81174`VNtJsaDiP3kRpy^etYBE`o?_G2$?$1g`tG<B<y=Rl_j-G1aEm@n|e zQSRt5jvu9BJ+krrQC-=pOl%p}6*c0)VYX~=8lD_x*Eu!i6#3!&N5e(Umgp&CUXpAL zBPoAVoEzi2F%af2Pn;L#$a*B=(_sTxe;52NOkwS|`XBAxNZY#+`-JypgTKOa!~M*v zm$>vPVQjDD1cCvKt-}YyyRaUM@U8GE?8Tk9UxXuT_8L!)P=W=>jOfVvyuf=RygMkJ z>Yuo_#c3q7yBD}o%k<35oMEtXl+~uuQkHv+jYlilnnpZmbdNsS_m4DsqBEC2Oi)Q$ zM2W-HbL)kY*FDv~=(_sGW(lmdW6kRyxwdUAOX@3R4Em~>fmGsMyl1py52b1SW7jsJ zHjZH|J<H-4rOcuXjFU!bN;B`Ih2}MuXu-T`%#EI%;WVWt`0ePf?uwq@2!ssP3e|HW zAisf0KNUJw4xvH6>S^*PpkqdtLvZLAS9|58jxcqy9ndDK6QQ6^icp%kzB%_jtq|TZ z#)};}0#}c*8<-k)isTJdWlxY05P>s|1}AAV8M%moCBp;Rb@kcJbQ)BIrU!eat3<C@ zgJVXFIuM0S&fyfbxYHM%hAATICko#i9Lr>2(tTT`21=x2g2|O64`b%*@v5i25hfBA z|JsP?oCj-{z^g<v6c*=dsx+|O<gZzVQ@-#vmGm??md?P$0s;~u@VPI%dxZKV`AQkD zbSz1JS3*moh#hcM4I3M*L9YxZ6c{5%EO-S2rYZk6rE2<N=dmeZO!kiL2o|YwtP5ME zz>mi&?2E^PEg#C&E-EQXsaA)n<;UqHB>IUfZ99+M$Ms+@+2G0J99i{MylUJ4s~CID zP@(&f4p@}H25h2UNP-cr9_P%ao8jl<TrCT`a;4MjbtXqdv|w@$VAt^xHnFfV*0%zd zVCd}Tw7R}pzQ!JAIBWbmn4!!j^aXPmGGPL&Ja$e<M;hE^;z>twuvF47H_1Z+KFqpQ zT8+{fC7JScXdcNW7tBr?6EAUHrUingR{4?esKcBxn0Cs`?n6AIarrymHbiRz@q_Di z@os;|JA9`F_pH3^E{(5s{5#$%y-Y&+>vZvozvFGXmC<3C7)Ym&|4iy@6<%6dmKeO? z3^rma_`=PRV8vwnc=a;QKUQ!%H8k}`%9s=a-3sf3p8D>!hFO{=YalMmI8L4gbu!NC zKr-PyGqVFPCS6G~s0%3VK-NRJe!YzxLYWDKz$Y`xv`O5ZbRyY2Nh>g)oF-3&IB}Ky zD&$qIhJh3BU$7?WgaGBulBWU5a8C!GrMSc2EdB|rfP>AO&vT?%^Bk$xpEx2{Ko)}2 zkvzw2E1o09>Jx|ma^Mi2p3QR%m+>6IviMIdilx9JIvvZibhhMKJS;zPFiU_#e0l}X zVQ9f~h%G*G)GY!I!&~}-JT&7u8qGd&lrI1d$t`_Beuy{aS*lDwu@uY$7U?a0LH3yN z9K|M|I1=UnhtVy4LDGzQj#T4M9FZ}=Vf_AMix}}7F-D&_{G))wB>w%!CXw<i!O~AG z3KcZGLEch*i%57556LGEW(IJW=B?4Uh#}7*HvGg<Hw`$<-s@#N6!9F5B0UFe`1zMl zdA~-VP#?{!Um^Hz%$G?=1y(;_hz35`89pJfOwuM>eCRtfZCdK5ObRB-gb4!9yl#HI ziSQ<H$$-mpVcOhwJdxwid@qv7CfPFI1*=3Y0@V`_v_2U7O|=;``2EzgTxs}UQHQwF zI|U_~uzOfN^(0s7^KRu?Uduy%q623ErSJGS$Rz(cc9u&}e0?Nqs+|EFJP_}RRI(m+ zM}CW3j!-ckJyXWUSm5ZH(Ke0J5_PA*^s*F@>{W^x@nDz|(zq^Fa^(I@cf@)O!pdk{ z>^7?t8`DqsIAfLzo9d@~%!k;j9=gZhXL+#2-nvJJC@_s)cx_ZiJT=OR6??#AKQ`D! z7xQzJ16%B*<1>lw1T(i!bbr>vMn_AHc3@MjfYw7yeXS&IBfb-zqZGFMV^#)Bk5jAO zY*nEuev(hqJ3n%u81(jdjd#a{vU6VGrkGeZ<T0K(dmWp44?mdgXqQ^IRkb;0z*p<Y zu1Hq5_eIml#Ns>HagHlnd<zeQAow>tca9gE`U~DN$JO2AR@zXZn{ES6w@b!pBBt6_ z!kjwmqYT(tW^mn{4vLs-_A1zQS(I317_*MpKI1DPzGHAKHJEFP9f>>4bsg(*E9q!z zFj&N<(r(~YGGrMTCUu}M#FQ5}t0oE=6v%JD>Na21^5tN8L>GHddPKkaA;Q)Yy3o<Z zM$_2Rh_!gbTst=8A}*QhWs`bcFW<cTYM?3tX25S#tQ0?;>&<$UVf%S@lGxdNy}@sq zXX_q(I=3DLwyR-n!eUxC6iC}$-$Ezx+Ig<VZnxA|h^_-twfMk11sn4-uAS$_ns>tQ z=eaA2&y*z7%Kiyw2(px91`b1*fEe_Qmr=DJ51Bt`WS|we4W*^=sv$yG`DN&`{`*Sl zD!U|^T!KzN8FDl+RJBg*Zg5J%&;u+3KRDo!?v$tmB3G_S^M#5Lx{_Z(3DP}x;~(cc zk9lAM0?Q*ojU|_4q9y}*A@r$GUBT6s?STCwMqUE%QfYbO<-C}URn!q!M_hbM1boY@ zrL;IrOW~wvgwTdQGd-~X0voHu-VS$@JOto&gsN4~|6-sjK6w5DJ2p~@(-!!Sxn^6E z%8O`tAF6iQ)56e}aXBGA`Z0t9%ekshp(&gV=(_8{J6h0K7P1N#8e>uNTYWcv&=p|? zHeJ{mJ;S{h_Vx1*1*QO@$YsFp*E8wy0dv>2B)QWg@V4LR-t0&Hoa8PQ{(RlQ{lZ(m zOT=loXyI)12%9akHcWxTS;#*IcU|P%qcMZ_3ZI$#Bl0sep4x3YXI?SeqOx62>)NgG z1T3e=;e<s&qJ}M8LX$E6agj}^Fo#QsHFQt;E>I4sVd1<PNEUFwaSXcbnA<8owr~^d z*vKRe9OEyJV<OC96LTzrV}_3|I9ISJ5qrmacUG?laYnqF^QR0{y}4_!TVJr~+9&nL zDX}xqF8na|sEu-^NyAR+cKd5q2=5Am3U$x1aro%s&c4cJ77d?QB#LmQ2b({|ek#a; zWX^o?Dr~-_i*Orfo*0Jxm$;w>c-E3G@t#=NB$ZdvDpt;ezOpkYN;b|7A`k;jww$^} z5uHAjPc>F(+-`H^0&pc?h|3L<UjzxFq9sLYnxfR^lhmkakZM^G(_4(hY7g)>?Cj6? zmNQ~lGmEQOY#Qf)7U8aOK4==A5*H(cRff_6{}6WtZ9lSQsTi3U%E4a1f(Xw4G?Q`3 zvJjMsudeLO7N5qB%N<PGcycA2*_1{c8NOU?0Nb`#S9)TH<qoE9=jBnl#KCZQC_Cc3 zDoto-@d(z`A9lsEOJ}6hy^=uKA)#+5@W_ey<sy&vOHk)oBo#EgwD{a7gG^GO&M6P@ zxRk-@0nSMYgrkGsQwE|v*fupu((sJWF$x!^_LR6i=HCkO-P9N}&o^{%Zof=+g9;rT zh9{(ra9Z@or~X<w?vqK9t7(rE;)=A6C<_0PHqa{gmkhzF&3XRRuhmVjb$9k+@ANh7 zoiliUdJuBLf2Mb1<BwzKjNJq8?4V+4q5myZ?E|}xCqL42gH?4}DOHuwI(ZB<mN*p~ zWbQ-_xG*ySw)Pt{yU+9wub(7*e+_(C-AQRdTMnqLd8{DGO(KN#Z(RLk;k&7SQ=K+b zsz!H|mE@N$G;o&)OVsYE)baQ4SU*<yb`ITy*3B(^HQgNWsw`(V|1#c@Wrqs!$*cgj ztP;P^a<wnJl+!>9#}BB(srwfxm+TpFJeezvzl8^^cQ&5>i*2P|iZ!0Q-W^5bjq4q{ zJMBSkuwy(<*!oP{ITWjt8BP<whxYTmS+vLdtnyvHf6<<K$|WuX?!V_W9KN$&?%CG% zi9@-B(m9RKj!$tQNtpbBOUTDg8(dhS5gxW-GMoJz7i^e@DskI}Zft%A_Re->%d&A; zwxdh_I=&2#=;k6nnbvY2N>Wi^ZWUkXK%AZ3krlS#{n=wsAa2X{U=KdPoi)yuHUFW_ z=s!dzO8x)~hX`|DJVi5z9;|7CQ8=#G1lk-tSrRFo^<Zhx%A%E`RUuHV&U3lavj1TJ zjlQS`FWcyZIGnR_G%~}D8wbm3s@1_R1uZb$O3HLs14?~x-%Zn5w_otKO@467b8VAD zd`&Vx*x5OBHtxC*Bo=Vn+Q)UCi`6O6oYPOVkNv0{*@d*&jFqnE{UpL7SZO)!ZO;i; z1%%3lVAnM9W$oj;&UIJIdZbX%cP_Oj{GFhHOKBgkE1V`C=i`p?cea02gN7>D^WOJS zy|)Le?rWvA_2m<&atHg;gTD;nshqrk9%?$4j>CS`!IvNTL1w653Rlp!|6>f*l&+(c zV>*X$1!brITWo&iuJQWO)^&k$31y70|H#-mlq<ONr2X6Ub?x8sb3eZQd!$w#$=*Cv z0QT%ZTWeUUA>N%k9yuNPD;FU&|Hy}J@hsb|5U<^y0q3le9Unjg#rfmhAMA74{lHva z40g*zTw)L`(z^qXOI&=1waE8@T`}r%3a`uW3g?0c^E<MlLi}TX5K6(a0yowz4fiWJ z4*v3sf^kOS*S*qt-=YwY-su8{XxUB=m~OZ43`7~Ye&-A{7YFVNhM4ucQXQfaxw`C< zB){Yj?kPI;YMNdEClfimA?yCF3p?j)ykVCOUij@4ICZ=H?WoR$ez4i_5_$*v#CaBF zHR#na?USvQR1yO9ID`Fn4}!DE_}$hW8vB&MJ~A8&10hK(p0lxPP>$gYTpnbF&+Znv zHG07YOnou?_ogt|Z#bv-r5naD<{*(l?_&IDw-3vuV7tO(Hox^qLE#C6cH>FKy(Ml_ z<$-j<D8#wNE2YA5{F~4c+wa-dp^U3m&j?yb6`Gq7bRE1TP~qqJ$2}wC3y1NXZbnd{ zoWe7*luL+!Bshm2s+ZBiHMQZBXAY<E@#+A|!_z7Sa2UKP{n)2eg@buYAp`O>ZtW<- zK|DoG<!$JDY|`RgP2-P{y}c3ie@%(CEb0x{6y5@+nVFWtvj*ZfCEYBZT_EeBEzT^s z1SkKngYXph+UIFm{a7WFG7sSHO@1M4{Uqe?#IgJASm6ttwXbvksHq_9X_9YxP-4jD zNL8B1R6PnCgTpxz8p+tWSv~?nqs-TM+Xo~Mg=cB?8~G>*O|D>WUpSO#@O}rAh9<75 z=B~a%c+P$|Fm&1bt%Dny??T0W{@5I;_T2e0$xkx8eW``FVgLVkFeAb3W8Lm>O=iEf zk5S_%`<+IsAHq?<4cPYyp)?W%HB1S*mR`j_xz~Pd3t=Nj{VB;Kq#fVIs~`D_u6*QE z<1fGKW?Og*a;wRkEqfzI7MHQGoc&mfo!>dZL{;&f9DaZOF1WvY*~#xWW@2FiTfa$s zkLbF-;jHi&&tLfyoJO2~p?_<*4()O7>oYtJaysB(<@TdP1=&o!DZ!7#6H1>hQR1uv zV{JVvp;8vAjbeM)31dpuudks>EE5;wmIK&g-dd6coqGCdnjAXUVBiA0{ool^cm_`? zb#zhJ=yT9Qo%2ILiLU6Cvf&C(iNf1UyIYJXO7b$YQ02g$(IhBAdI-K#8f8)Qz`!HZ zBI$T~kbE*^D1D5_9r}*te#3&p3X38U;j5^xQq+-Nhpo{FVG%D^sNw43U;es>WPrR3 zudkDiW-xLnsLXi&*YcH6(7ulbV@Z>ktcJpW-z9Y8{ZR!;zQ#-^<4Zpp6bgd51RtDp zq{^wZV@a07HG?8SuvaQhxu7q-8tdzYS04=)Nn^N#gIn<rNA2NkqT%Q>sj!-!LX{+U ze@1qAAe-)_7afP{HF5}Hm9!X&R)qzM3WHu!hD)efe^<+MM6KdEqOjX>H)r)%dj4qO zKlf^Eh@MNhoR1gI#`BLWkq_Q}{1Ka-fYY?^*tVBA`NVQI{>hQ|Ck&BddY(lk3jAc= z|0wfHqesltrv%gGa>VtNGeVbe@b?aqI|t<H&_0qmc)|A`MvbFw8srmbHwWYV?+;1b zdhwYS;z_67Tn{#zRQ~C%RPu2R^3W8RWb3kT|LLyK$GO?yU8kLGV_F8~3?KE$obv80 z-DUXWwA>-)HCWF7Ms0>v`0i;Lcm!@|-0Y+;=;ey5BrirykFw5}B%?s@bgn#n7oK${ z&Y*O98!4ZHtnl+QcD5c3urnMZOcbW0D<54R*eNX?j60p}hN^J*+3qfjn!rP-<8CMU zs)II1es^`9E&f%)CXT?;N+j>EGbs4mvtKIOo^GD6?s8Y~$7`8TDTJlXWyvMF&#!ZS zOKYmJW0{u~S5Jj4y6%-37r6v{c&;@6E}l~6jDE!NWnZ#J5AdzB@g2&3eNl$3;S$_N z*yAj{a}zGXD9qjQ@N%y&Q_n(e1N3dAbJ6tOb7oM^lmJPvx0?7-V<NhHSglHKWKDwk z_mn!-`bI{me=EAJN@-+xO7tB$q>zcC@s;vUP-m_@5L&WVMWDIhz=3qHjKo0qZPe5* zs>2x-_L7mWNJq#Za|AwE(TP2H316=m?jf8?IkJDeRyK9<uDZz&r`=Dv@BTjYZCLY| zrtwb`3`r&otjv8l;GE1koWKTweeNuoFcMm6(H7eZd|wb5rx7Umb_>tJ%g;?=7d^#4 zox5*lmS|fM7s1%bg#H59Qo~i}d$Mis@%!@%_Rc%(c%kd~{Pm}2;aP6ZiLU9HGKt1q z&FNaaApW&&rB>WTPvrc2&S_!dG``9>b5G@HE#xc!pQpL+i|@2wPgo-VTHca9`dGI) zG@VS6YYvXnOI&b)UPP+85HQr~spJxgp<^JmC)IM70dGzNE6c@lo8+sgt&zsLC`*{p zALwn5Wo4b<a7{)%_=K9>1o+`fz>yJ^qZJ+Q+!NlVt8R)0INSy;Nr3J@+|ZeAyNz!( zgt2YE;;s++$uCXEf>#D~zIJ*AX4HU;2Pv?I`}%<!_&wh~7%C5%0yoDSx7NG#c_c{c zD@O8hP!3@f{_vn9Sa{pUzOs~Yo9nkW+5<<6fTuR#s74R40P7oFtUX7RBvbkMbs%5B z%a6vDjZUzTYG~{atHVwYhr-$<>fz9?yZbZd)ctk3mbZrI6b(uorb_*V*}5oCb!v0{ z_Fr209E5%GPY<1YU-E-HB5abc1cj=%XK|Xc%d`@5sDw<E(npnE#h~ELkkpUVZ?9_2 z&H7mQZjU;_R2%-NM;C?I+jxfTMi9eW6_0KQh<qX>H#Afds4197Z+(tRy#LWH77puD z9#2LG@!iKA*_t=F^|7<v;35)CJ1yqI)}h~sg^X&!8y2eJU$z_fd*aM;m3Z0{M--30 zeiFb=&%@<UY)~2g^~w5?;iuArRc5c?;x-ZLOmTlXgs`RJ3+f8<d486`sbn_kl)xZ) zjTvKZ0KF}g7{HqBuq;QyqTr&G@hR8UXd^g08TjziF!s)7%s$IvYj$DHGhf&}sC?GV ze$fwHQ}(NmSv0uvSwwZ@N^34+)8~D9)I3%h2rV}Tg{ItqBP*~I>r^H}+7JFdFZC7E z=x$@y73*San(&I}z7|~0jJv?3Fxy&^NyCHq%yV1&a7<g^OpP#DmDb3<8p{`(o&eXZ zCj}N@4Mm*7x~2h)lOnvKB>7RTFep8co<o}9{#-{;nh9KQhwwW<FRi&kl^V|Su2mE3 zOD3S~gLHiRIFI;6B_GB<Dg(t*EXqQEFN1mh)F0`U61e@R%zFsGzW9v?oA>lj4}~(X z3C`x!(C9xG1HC|**UT@Q=!&kLl(Eq9$*w=-McM{!41`N131#m$2CM8}F?q8RtMcOE z5-J|pB#SR(`L=X>#g%3kfFpBd<znY1_otx`!|sck85=n`4RE-24(@osmga$U6qFm- z52UrdhP{P$-R=f=a%i^~HgTn9dEoe6GUzK|piRr~w4?{S=h91apx11irdUb!9d44* zFdoHeg5e<@(F(>D1p97Yqgj!)l-CWa6$dM+k>t(e_@|Q}rraOXH0^$ZVM(%g`r(Y@ z>HRYGi&OvbO5{eqglcC#(ZUJC5rb1kjQcjvD_CyJsxe$rGyl-LrGu*FwWVr|L<;x# z;Br~~#-K1z8DPJrG83A+9Gzw0a$>B3%V}Q&dg>2-nh^ic;>zEe_orU-?m^G4|1c@4 zAnBwLgXJ*8VHe+bOi?cFF_#l9wC8kv=5m@oE0iv!Ep$AUJ3WG)t1dRAXUD%@&Wm4t z>2fICXl$7PC!~T`hN{gHI9f{#Sw{x$`EP|OyECvw2HDZ%2(o<4Y_G{%y}sse8HTS| zi@S*-2s)h>zU&rsy7enTQKV?ry`@7|va9aR9Wrm%yfF2yl`}wLhN~=~FTldKXsIr_ z9Bp9>wOTQgpt6QJ0OE7XS|*Jvdo`&-`)ty(vXAYcuIJl9cpYYj4=^@MaO_JjT=!Cn zcfTCKF8Utddl_k2I@l{?;9cCZl97u+H^fyp@z_6I*#rSz^=AZIvmgKX=MYn24KGT4 z2}Iq)_RWrL_7Oaw*@i9t4UcbjXMJ|!wawnf2X~AryRL>Nj5OPUPc+YGi<e{fR|6tO zdM0U&8F_EI_Dgj&=$jgRtcvu9A@}`0do)reO>V)`gbMixP-7#bSlV45#u^-_d-2!l zMs%!yP)V|GPrOn9bCkv%AAaS_rq0C=Urk|svT@LBZ)=SMoU3OD(8{~&;L~+F0~4#n z>951tIhFXg*X};^;Ij!M2E31g(j{K$){kyYA)cUz>2oS_pU|@a{w=-yOcmvcz&>x} zY)v8_{$>eVI~|{Xqp<Y&3yV_0;3t1LmF$%ccaMGW^Eb|tQu~!3pvrK`-}Y^yGBAfL z7`zPyUeTJk>qi^m3Y>CLU_}|x8Q;R~e7u{6E669f`t4~E>`fQMXX31uuFA$c^elxm zfc;k&_5ry{#zQqlD3bSs#_N}HmwJM#Y_U1td`5Th!xnop1zakiSJ+em5>qZV63gBV zjDJA;L0RYHl|Jy-B#&Y53V+AK0t1*VF7~?AU*r}*`8lP+xFo&)vZPsen>yBi5}%FN z4VsPT&jd0}I#vB2Gy)qXRP3xVI0<ejq%-i7z&)&{MP4Z`z8Bun7~6|rADG<B>qx!j zvNw<#*Sxca&C7;&0c@>1?);ZO+hm95{xt|G@ZP_iTojBt2=3&(ZxuZg38U3fTq$=C zJu>i1exC%fFgp||@sq!L!OenB?*rghrTZ;>-wW=?{r*0j)rfG{4<muF@`Do_(*(<3 zZ#L!`uKv)6t$Kt7t#do5SHr&kO?6N!+};0}$D$KBxwR`Ba|a)8^`d8Qw_AI#F~4HV zwjqx2{ZN5fHC-$4hfw^Uh#V$dnYi&kcuCuMHs&g>Zad}ZHUl`d|D7|YB4-!s!Frs{ zxq$`^@HmxLZ&7W=_@j;;f|&1ru4MC6E*dQ(w+5-g%1K;-k8YRi-#Lo4Bm$w%Y}8S* z3ZbrS>LIcRp^+lD(U5lmd54e}>rq17Sp+9+#bi8-hO?==NCAsFII4%kh9Um%<*Ujk zKd~s1EzTu=1_-VYVOaB``Rs!%!U|9k8?}y<2vAsu1`pT+<o?lm1$SzQsSr(O8^0oR zg;2@jrDTT?4f1kxpd;I*`y2QI%jxc?eBnz&1|}+)pokdssMy1ln8NpRh$R<As2IH^ z3&m(41pCFv)}Tc6lKdb>PDW?pn+V^{?>S}seezO_x>!G>uku%Lg+~~5%Eh^b0a6|5 zVTk%m-nuB@RdeSAS#F4~$V+&x%02a2(SN~3vA;AbC{Z?<EJ1xc-qM}Rc~BE1zGu)c zHF*7bIT!$gzI$(y0}?c^OE>uq-8G>;)oPds82Ff}Pf0SI$MsHrukWvWV0vlpPslJS z8f@gzx8SN840v69BiSHD9g!<3ks?PnzL4yQMjc3<6v?d5T~9%9KM=;6f8-qU7Yysn z%uU452)S6F`9!LV;z>puh`$knb9ge_2n|K)WWNy#lwIYN(`#+Z_)@5^K?!Pzz!<cH zOCjFIs6&rMYIPvZp>z~i+m`il*_RlMD#4LyDto7Ee{Jx-XTQ@YoLXE$o|+>YvcVWh zP%$yJfag;BTtrS8qb_I{d0>pbLvP4t6DUkGIc<Vwpy$NV6b{Z#aLU1Z$!t^92|Xkk zrl`NkNnQ8%dUKys=I<icO;K-?d#V&EL;6zty0?yWFoS*`PsW;|U^Is0nV}IK!)TMZ z6hU*EmyRz<{S7WR%OB9@j~+!>bEJ|LaZ0E3OUL*+2kwPT)5r>Q6y}~?`FZ{FKM>GA zaEmLUe((fG^%rRUI-v?$H78U?ye*I?I!z{8pm`==CiOOEh#j?T8PiFv1^Nng(GItS zLD6B+UX`iP4ofe*D@neQ!WXDWK+amB!3dF7D-_KxDj?C;p#1q&WScb_D1W8fg*31G zN*yeG^>><P?Q?5nhEN!3?SOitP_3UF)rinit;iAWVcG1Pq*MW#V)l~MD$pu6yg@s% z6B4nk&lT-h7jzS`KKr%aZfGvcwml--Jdi8v)J)1f&<57&9f|0Sd})}{8H_;uP3_^% zXd7b7&T7NFP#9wKe;_-$pka=KFO;PBy(Isc4jlDX&V2fA`WYF4cUWwYEnSg<jo(R} zyFz6K@6e8icf>knkrmxgcc)5SqOZN-s@jG3oOxU7H;^<eReq6nk!TJ1p&N3LtpY-F zdqT=e?b~kX3uHYL+`e?qJp8#XM-T=%&LELKXr%qYhb8H9op|9C&b+4a6Nm6i?GHZ4 z66*d~drOH<BbGZ!w)KEwhX040?t!-S2tV+R6uvH$STm|SW0A=o&fM(B559277w~<u zYk*0Gj8d+SMQ|f71;(X$%|(*j6ZK=Wi%EG;s3uoLntP%#j%N9^)H3mfzuZ%<(oN(h zvj^QON|IsXHxJ)VX89t8W6kz&#MX?9ck?@UxHYJnoy*p1g4@PWKh&H%N_P38VXRpW zdFTs$5x$u?_CoV)5nt^^8G7;W`SyCyMt1c=%fQmv_@Pb)%$+tezz>C3%uj-Q+Nv*_ zSh<Ay@1FC?7C#hj6rJbKN2EuSCO;I|O_bEzgpt2t!E!Z<sq<8ENB$n(1{j+pj>OXB zw{&urOF@S8@ia2CH`>MyZYHh0Q3p2u73t6ijb{fxBMbXLOWt`#G<{G{R6{QGL4Tov zq`WVT$-cy}AJv9y#H$}wscTqzP$B9R$TbZ*Mb`8~w&*a~(hu~wid^l7!d%yWn^bH9 z*Sb~ri$w-*$#1!)X}kX3?tjfPNykN(iGP1IjcqL^1^tl~yQ7F4?~nSTj-;tSj6@q^ zHvr6xHQ6x$*^$TrU>?lqb2u{6J|6%h#E_#)zF2NO*)bSpyDf$zg@?t0zw>B>JhWA- zO2}rF`l?(6H5WbjnT+vA)6FK4qo8id_a}5O;35xhCFTCm3}I#Dra$tNhFu`Kq^UEB zO#pIKT&?)0$ke&?l%nN#0wQrrQ6(84fV%WN2Wuc^XBB*u*tGC8FQ9m`ZlU_Pn64L^ zOuzr9m?{-H8i4xuJWYKfKH-LAe8Q?P+9&ir`p<-oQ;72r)D4Z-Mhro}BD9rw4MTR* zp6`bZ8B6$d;oW*k)~xE3u$0_OX^|BIE^<*0Xm4bEMc=#in}@Z@8(0|YfoWXr)y)qg zgJu=Y63w}{a>%Oj>RqecU?D+!^y=2M`;$IksiLB#O!rQ-yN02)YzL=<bmpepaLeEn zoPfH)f_OIZ3q}LGNp$WzG#$*H0=@hG1Q@>WB53En{{^`l8O<aG!Ds@SLf!_WAl9jg z_zy?39OIuhXk`*b#TD(cNiAg`m%!vc2st|IB{?@7`Jf2$ayTg2Na7WO+DvXzr(FIR z^6XdgAL18^7P4iRNKq(gbK6DI8jAdlYjWTNQrAue9OILt4IP1$2JG}KvL*}-Vee#+ z@-VcVopyut2}iTo)*7-S996S}&Xc(jpqPWskm?99`HouBbu{``GDcpcGi~F@&C#fD zr|Dd6{0VAoz$7sK6NOg{{EV28t5!3}FSK1^#*!XmP=Iv^II6YN4|Ib&vao>oowkTk zxHb*WkrB!#SHD0bkPopNiyV+Q=`$8>muTdWUMBX)Be%!GoYILnjzce5ZWFN_58a+l z{Kum{u%aQW#-qcmPa^T20MV<-lnJP>ZNl^qAKQ@kP02-;YUwI+U;;GTSK0>?&|ZY< zh-MP<F}qFn>3ogb!UC@D>_u{Q64Ds&IO7GkOsUJV_9U4*8En!OQa2f`Mi<GHFVUAU zU!M39#UL$FOhJLxX0XU9&;J0of<lA>zI~#PkyTS*Mm<WlO+jB-e+L}3ZR(ZJu;@Yy z&r$Rpai0nnri4tMifW^$Z-ga#gjYK3F51E}UE|`rHwfm~KI-62@_;}WrE3fQs*p># zv!NsdK9M&?aH<DGs(gAzi5kAk7RZE`5LY)X`CAi-0E;iYb}5xIE=Q%z*%TM$Br%_5 zN-oh%L!s=3MsjN!n$@9fooxX&rpF8EK_EF1hDbtuA{jp&T6`VZG93-G@qS0W!MlGZ zd1QvbdFd;C?UKpM>0qT2N!Li!O}vF_L`pn~j6^>Ci~HAPOC;(pU#&0X7J7XJZpP_2 z-B**_k>CYhBJU!h0lp&MGf)P5;3PRU19deGzo|Zgg#vn$%D?OfZSxGY0$DEH0ozah zT$Wb`QPWAXa3@)-0*@<#6sb_K^%mL^PHNrVEr!qJ!2kjalsBL%?`DBd=1ZnTq18q? z))xtkeXNekA@`zCV6f+habT{>+up%7cYmP>ZiDfa=?oGh6r=dJB2S%P8lf0j{gLja zqmR&i*BLQ*4_O!u#{W-}77Z10BB!H4lO4%#(P)o-SgpE?%0{S2^0@G>yR`ntzg}NW z(TlYrJ7Q25v*k%{rcA(WavKV^!E`yf83Rtgp*CSQ5+L^ZVQtDBbcjU<NT2yAA605^ z&PSO@xSvz{oF%gtqN!*PxwsI8ph9B42<4zWQnUyu>`faX!<M~yYFD+V>OakF&-86@ z#~w7g*84WOw+M|6{|YXqTsMK)T%H5{C#=L=t?3=u6M;9ZZrB7BtljXNZNo0zMHFEm zy^6wf7H;Hf7vB2FS*f9%&wnB7Vo~q@aF_*G>o0j_1YCmr|GHGgCuzund~bYABFv)Y zIB?A?qgw-bf+<vef@YHDSd@g$krj(kqU}i-Fs0LXtA+~&C2ANnA9$|lmBexh`WjUe z%@Sl~wLzO!2ZO;~B{eoC%V@cU2gr#fFg?yAhH=Po_|;uROYM+2KsTE_FSZ9mRK9TM z-?NF1>Jdt3GnWC)&8qccJ8IE!YRs8DmAl%dPjfSrbU#@bhr0IJ@C`4ms;^Gk{o)Q# z^>XiR|CH7^gi2GX#a7f6k%?oDlk0Kljobrg8e4TLs4%Y2SksbpEQ_HwF>HgjZYf-n zK!eD=WncxL5%1*?_>oo1QI*LfYy{>a6WYh9OCt&U3jL0LC0;AQ4q1_+70~Avq=^Qm zq~A&uA~n*fX{pbdyX4>s<ZA-g9<S@0*WFNibtPJk*tHcTXf^5&8Le9l9!)-}UJbhX zh&)^krm>MI)*y`j&>md_nuN}i(6!K(XUMj-ppnPPka#eJrP>qmFm0g`TJuDhi%=Z# zUk8RRl|-%shb5V0tV3szhjw@pQXyoa-JgusA;W1eVf>oksUv<V$kV{Qt(DA7L4BRf z>#vAkbEVT-I)V#Xq$bOu3sWAL${~aWju|<Xg7W3i4O~I^RT!Y7gbcl|Nr@@FNo*?W z>FKMhveJK)*Lt|O*_rNzExukc7v{z_AS-KRPx3W{>qq@TeoO`DsV9-9f!*p(hNVFp zc$0)QFm*qY@-(Dm*ZxFarl9}_2c6AQGDb<omzetX;)f`h-s#ol>OdmWbt61E9rcoY z;DtZ?K+dHjZ}UNCR(_bA3N6m_+HT1}OATAUqcxAuBWp5rIiAQs9u~%AHIynd^RgUD z3AYn|BuyDe*<tXulGJJHja4eOF_Ywx+>-l*D;-oy{4$}Vz9nlj!DfC-wq+tG+d>#- z1#LUQ;0+NP>V}6d&j&lm^-LHZ-;jtbG}&%P2F=^7MP?-U73K&<Vqi%!d<GWlxSL$a zLW^Ko>bo8tku<>PLM-t8PMX%kTCkp2Za|k%61lYj#YnF5#PCWoG8=mI94XC)V&sq? zvrzzOhN%Yi^*nVC)&?$}o3F#YN89@G!mD0cf1KgRV_=ya#wGY#Cpq(j+exej$*r$I z;WhE!gS};;JIB+84f>JfX;3$_Fd#K$Unfz}JZfPCxv4=;))zqmH1S9O!Cy&?H=<>5 z@j`Rp4N2Jub)G;3n@~5G!&La40@VZ|!&@$DxAZ1N*G3ojkg=OkKo`ZEF#dj>?cMel ztO=p^qAG%3$|X5;su0$Z@=Y)d&J%MC3jYo9$EZ8XB}*`LC?N+h3Km}BN=<9YJB&On z4&_CrSE++x2b}(|jEI^I#CJ1VX|joL2e=!`IQ>daZAOtjOyAa9N?PI58J*>d%H%m3 zk~wDjwP*VFgMUH-Z2{}jwH(t{Lq_Jn^j%g(w&tLX@O_on7Sv60*+uz|;YtteC6QZD z&yMh!Lu&A6b<)+tt(qLzf^1Ea^%7TdOuUx-v<1PzB6$arXgpk`jpylw%b=u(IRC-7 z$shttpv9!$R^-f1|AtK43Q3m3l~x-|njlIquwgzFyoemw3Z`QTP~|e6yoQy$yoN|1 zZ__T!Md=8J+}myFTcZG~T+_gFJlIBxzCj+cetARdHR9*G5f@DrLo}GweS@q~AM*Me zbOq+P3wh87Pf2qgEQ_8H#dc7}vt+<_P{s#j?RFU3_2k5M<Py^HZ_&S39N|kKbl?i6 zB~w#FKsKC{+w;novhp!q+nU!71l|43YyG&BpYp$WkGSpt?W!TWc7S(W13Oar$ej(> z5M@4^XJLnLG$Qznd<eas$C;O1A^Y=T{TyFS>hh7ob@~r@LIb1DRBaRYp)2UVu&$W? z1P=H5#JSa#F3@u3gD(=-0$5TyT_8a;Y^xx%X?UlMWE8;0$DK2zL>GQfs&(N>@>myY ziF7BWA0uA6@CXUriR{_7LuB?&<ZC$XT(@H&{Q%j$6Dbr<`(X_lDzv9%_?niX2FhR` z=hk%S*%9C>A@_lcb=pHjyJ#5-NoN|~`IdyzuxuxpPs99tvVn&2+es;eis|3r39q1b zFY2jW_|&l}{%qhCkXqUTqdh*CH0^>C<r8B1Erew`q$>^YU=m8hwvA-=x5%w`J#yD~ z&rMh!H?15eJgE+@*R-_0c+VBA-QE2p6%My0ng$<Qb}i10DfK+W6|`<A7rsTx&O3I} z<3e{e|0z5D#|T8HbcMJ6mRbu^9}@fBuzPiYY~BspvyX@hk%HXc4aTmRKF?&=9w2iI zQ7Wt>UlyY6D4S@CP;XB$$pB+;N^0p)lQy=$&<$}3I~wU;1hnS8V)kR)v<IZA2n}<+ z^PTnGtk|^qSk#vhMsv)a{T7e%BIRnj)@-<8|AH%c{#JMYgv>8SGM6Lb8+r4$#-`26 zntcRrWYpXK{FZJbuZL~^Q#r65$v730UB$?~YyR$mf2`EP8Z_bTeR|ozq2lJ)WoP84 zK(4f8B|U(s-!6Wvqu(RV#i&!q6^1jMbt_bU{R78$FXZ6qz=i2$M7akhuq8CGA#3&^ z*B(y9D_!y^Z#G|0VI?i7X+3`o8>;3n*Ya|B88P$7^*t!uV>U?v^OzF-LKvJ_Pf}UN z<(Rl>>w`b^|DAvwr__{aGJG%UWc2LW?{doGCw@wn?nV9GW{{&mPfmsf<e)QfT>d6$ zuK=m8OIZ0a+FojiUwfAPv=_Oc`{eCj<kNTXo4S={|2)ryJD)o4z=^<pl$R(NpXHwz zf&$e+aws2IklQ4(1PwG$ihm;oCCF14&eh&NK(3a+f^h`#--p~CD|d2DXV-t)kEiQ; zlM3qV!3C0F%Htv2$!S_|lKK0<MP*3SKC~D%eP`}RE71#b3q}`ure(ha1wub-GY+72 zEbIM9YkCL`Ky290WaMEqfL(ZvWE@7xtm!4K`4N<iSnqAxEl1IHWEHkiJu0b7^4pwa z^~okp;2I~~{!6>&I2yyUJ8qEL6L6~G_)yz=0(A!W?Pt>Y6uQW^{y>^e!C1U~QET}< z8jg$|OGDx20nC&MI}VZP({S1`2;`qe#}PXyM;mq)EoB7<o}fSy`90|VgK}6tZ@5Mj z6=2d2)a(9}g?4fUY;5-$1zSiB#RAqEZIsD-F(ii0hcOf2b79r`%kpB~*2d4L$X1p0 zd8v@6riPQP=TMhk!T&Ys=zk=w`7CJ&d3z4|^a`*Un&_d5dLvjTPsDgjZ$=LJ!Y0ZH zKVqXYxlD?n6vvBxpl{aaNi=;68cL>|M+<tFCuyb3&sB*b^0{;mWUill9)$g$_@@o; zFYJZ5*bQ6Y6aJ55Rv#CZv*iK`^*gWyobbA{=f>nN{-gay(5_YNYPu05kD|&LWyM82 z-;4rv8$q$pb4kJl<l*88wSDk$uMNI()-{wzw-WfunLsna65<f4zJPjn`Ht0SRO*<+ z<P_O_zWuo5lQeePtWOZUk94g>y^u4RS&0VQ+UnZmt2b<Kv7x_AEH9^Gp)YokQ<ccm z#O@U2L^}k&k;^Adm1tMjR&7bLZ3ACubu;Jh__Dou<OeaIX|aX&>_ucL*;0#!TP^SI z-OIZ>!$`wpqZWQKBt+pBC)G}=MaGg*%V)Q?MFYkH%mMVB;)j^lwsQa)QY?hHtAM$H zQUHCY*ag@ONCB)M$9_ebwwsr?wjBo40W802Z5s)Ay{xruxOUQSsFCf~yt=iGLb`?z zM?yFYU=Q%s$L-eh?A9*$11v;8@7&h5egI#<7{EwC5WpXBdTVRj8VHvIVgYjjvjEco z6LU$?J>*3q?j!s74AR>6zliw%&CDV1`2W?~w`G7YJ@-C}Cb9RBeZMT|v=l%BU=?5~ zU?E^OU?yNUU<Y6;U=yGePy#r<mAE$`FS`fuTnG3K@Dt!F;0M6Dtz>Qk46rQ7MA{iN zz&@w7Ep#(E+<>OVXKjW|0SSOrfTe(ifZ2fIz&jQY2AB_s21EiT18#ueEf8t|8GvNK zTEGgx62R$gP%uC#paifRumi9aunCX_NZHmJ-<ALms{l&@3jwnMGXYZoyFokuX8{WE zKbyh-MSlGMcn$y01&IH@)qXs5Lj)icFbpsV&<D^1&=uegPyp-zRsd6gA%F$E2R(QV zXaYP2+y~qS+yq<){Fn><e-R$a0jB^*0S5qk0J{KrfFe+|0>C!FX21qO8ekn@4d5)` z1mG}WKcE_L0Z<DV0O|bzN`M!@72pW40ayZzbD{r5@W24xL4sz$bHGEuJ-{tMhi_mS z07wA>Kr7VjE#M{KDc}L14)7b`C%{#}4}f!kAt2WeM%yeHO-cy80ImQ>fDOPh5`K&U zA^-z;2aL^t=YWTRdw^Sj8-Q9sHQ)l^EZ_v-FknBR2v7jn2G|VP07wI@BPI{fSMiqL z!XN`&2fPJ1?uO*MAPay7p0@#t0HuKIfF*zwz|$fq62P>$wXHK?2w*(m7Q{^}f%yfn z4zLBV18^E}8*qIu@a=<k+YcJDztyiT0v=`q5&&BP4}tg|&<xMg??8nBJplfIFu+d` ze~<KSM49ms$3b(B@z~PtnZ~uJ(f1vC`p9;CMetkzaQ;7F?f*Y5`d<mDz-|8}LHoGs zc62KIzZIZ;rfao(AEFpTho9KCwyglEL0g+WfWF&6m<8cmZMPR_gutz?b6Z<2;6A_{ zK8mCvjcbql+U>7UoOG&xZW}lWZQvxdwRr<dxBkDOGo*0_*aJEM%mGq>0MME<iglTm z08gs`O92Z3O_0O?3uZ%_nSd#P@qh?GC}0?1P;Smu)|r?ic4GXr0*D861(*V!=eM<8 z1sniu04xNA0lEUDfQLKU+AadN?1Zub#sHK6Q@~S5Uk%tpW$l9ULfm!0Q9xIC-wo+v z0keUg0f4(o+D62B3Q;Yw5VOIo;+1x?m_2TYx)KX>R&G1M0$AYB#!#RO4RpT@qa*>8 ze87f(_y3GsLKai<7zdu5@EO^W>@;VCZPyLw$>pDsHxpz5sWoGGa?@wzA4zv0qbw3) z!3Lw*WFx$V44cO@r@=q#qkkU@`hG4SulX!KO&`DYvv@=Dg68c#hfj9mGqQkmvIMfd zKJ^cuk$)qjDfzm-FgHIVA0>&D{2ad1pnnfPBNvcEl$;REliz+uo<{Cc@>qS*4B`I> z(!Y;2O&}IBAg_3(i)Cy+BabKWAKc`&)w6kW=g-Iw$zmDHLATGa0<MXRd9ItEah)a` zDcL+$pNG*ui?D`NSg}6fg1-PRyWjL&<)3ji#S&+0)*E(kMq5Ka_91huSs&X~@Qn@q z(-{9#rad_XTx`=~ZM8Lf5kabrawvi(jb}e$@=x|^QbEZN^yH?`$hG7JB_Gb<Q+I~{ zx=H^&w&5ny$p*+z_2kIU$kAjpB_Du89QwEQGx9o;NXZwocyjG$<S=rGk{#FcWJ9xm z=B*@mDS3&W?Ee}09<i_m^2iN*>J^`nmyiHTHq7S9rJs?vlEsvKR8M~R8Tl#MNy*bR zd}@XHKZ`btT%+WEn|SiX&&c};vZKQFWX)&fHKaQwU&Z>OeMWvsW>E6*lRUZnGxAch zk&<im<eQ(7HKc-)Pw2@HKO?`@zOZB8Alp4JdFg#D|5>v=1UW$RkL2N>k#CXi?a32A zBkv(I+LLGhi@dQtdBwlT746As|02I=Pu}`3awo@jCExQe^62*D6aOM7wkQAaFY=-G z<eUE@-)&ER_%E`BQ@f(Q{TDf)J=sw9d6z7vWZyse9<%uud1w37o&QC?=EP2iBg5_r zuo|KwZK#6vL$>T&URvE}?9JNTPOK9GBXrW4{R*uj-CbZ#IZamne_fqVNK{c2#(mci z75|My&Kt_qM)U8CGdSazEmUOEO^p;WLXfkw7LgH3EyN-!+=N5<Nwi2XG-+WgHKbHp zxQK8zW#J-2q#!6I!lI(@&V2*DdpD2ye&?QZ?wdRB-I@D1!-Meu#_}N%HZCKJ#frFR z+U2I}T|AYqj^d}AJ%-U&&P(1)jF&U%?|)R7nSJ&#ts^0Ero7>@ebVp)a2uki^HrL< z@Uxn4(kyjVN;8xm)pR~1owV!K^a;f1&Ws3!A8L3ABQn~vbcNS6T)Ri<X{^&NTPlmY ze2!nX0!?4Rwv1Mvp>T(WH&J?6yfrhj6`s@ZFdUV_E1cPAtK>c52l^`MY$``t_n7t^ zRxwV@=UL|2$@qoO>R?uhv!W~)SF41_ypgN=hFtc((^#rvq?|nL(9F1x(rUVTf~IP^ zS_KPHc{|TLR9`3S&&!q?d$FZQG(BwTQBB{)2HlCmd=%CQk36>3h<=u$ov#ruTzf+* zO3QwGl#CGKwIXcxTYO04@3CFWWmDl1)qPRJ>*%Tz1Lks*<^6!&djuPGoW7^f>fyM< z==O-O=En|Y{7o}nM9eED`9a94=Uk72bu%Z;f|JU4t=R6Fw<e!B=1Sp$gLdpbjY~cb zE5$oB-hzaWc@K_Qsc1Ngn4bw>z!g822_G!0Xo)?@^Vuc8IAIun;b`Cl5^?3Mq||N} zL~Dca`YJ5ktKrv{KBnmrj5qMyabX%@DuXZsJS%Sy3y1;N<~<dvhyMrg|D4GKb^?6! zoueoVa$aVSD!Vz&ZUN_l;*QxrrtodI-AxpZMh+;oBAe0h0$PcUi|B6T{GFRnPMqa- zCl!yBUZUyTHcRFl(ew}!GU)Xwg?lu79NcjzGS5saJf`6l_#_TKR(M6j?TATyb6Mfc u3VW_w7?GI2wkJ~yX}BFr#Kt1tg*ZW1XRkGhe8KN6x8luWHr|}R_2nNmF6G() delta 26689 zcmc$`d0bOR7dL+Ah7iIU2oOM)up=TwMN!;94G1ctb$_g)VR2~%_q7rXVOOK04k}8t z0vb15qgDl~h`ZFb*0xqr1l%vyeS`Ph+*p-9zu)J5|9bd*zL|UHoS8W@bLPyM<?^~# z^tx80c}vT>?R)<Xk51@+V46vHL|2C3X(q}8KBlI~g%XM>ib{=0Fy%t2ITvcJ@c_X% z6pxDq9<)e+D+TRPUwlIlCrBHGd>KtV?qud5C?5gOR6HJH)_anuU%eB*IYS<+W@Mt^ zdOPE@G{U~#mH)&LFOBF@?`V9c;-}Y(K0Y^(sCVH%GVnxbqU!BGK368!yYn9y;>(rz zp;<em!~&rd1>yjqS5(A|`gZ&Wv1(p;t_dR*Mc3Q%??UOd@##?Fol-SCYtuSGiFZ8Z zj8qm!+M7@~cj^@H@*PyY1;37stsRUt!YHTsjv(+2NT4bAt9Rr7p*B>b+zCGsI;Lv` zV}AfrT|xr<md7KI?>hS=jjZ?JUo(_bSE;DCH@~jZ3r&&UkXTf^C@@QSiTsKvtma>r zR3l2v=TCqFXr{I{#K0qsaDw_>RfXl6s`J+~4pC}3$EW_9(a0$7S)LAw64DI`C{f&} zOslVQ4AnD${vA`R3FLTW-9GWCYmEh`G8*Y(Xqo^6Pei=mCm#9bPx;g>U}{Bv91kVu zc+w3uxX3Z7p)l<{sDZbEsXPLG%BcF>r;HNjfIyyHA5h<Y;!)RpCeSPp$U|XjPOHMq zO^Z^OpW@4xkx#tUrwzP;d_xUWAwyRtO%wYW!;i)Sp>_%10v+$Q@JO$Qx4LO!3zO$! zQ_5_V`VTcjrS<<q&G9w>m71z5G3OiK)8KsyLzTPf)Cy~UH!fB^6RMfaL?}ZHv7x+h zK0Q3kSQ^mk5&=trX2I`l%x4y(Q7V|a%5+mhw9J^%Y-sq&j7T%c5F5<J`p#iAl?fSg zzMPSj%Yh=~2O0T+BAu8aM?et`0E&PgXe<CI%C8w&?bi@%$(I`W=K}wDCcZq-5bG|f zhVBycedj>|<5XRRMkVr{5AS1DU2~1^B@1?BWWX?1jsYHIEMOtLk5+{V(I-CN#haFM zoOL51*2pPe0<|B(3m3Ag9HGl&YRkg|^Tp$I6jPgqy5hlBuIavu8BN46MyhcR%!9$_ ztWM6|+|JaTQYUex`KAMXD3ew$0D79tteq*J+Q>JjBrssa{OSzo(ILhv_J+O~Y#by_ z99_PQ(bfrP`$jwk10<!EuqLT)swzC!G%ydpb)gjL#pZTQLS<rLzG9fxx>U_2OM+~v zftY_D8Q+(Q%}t&|(?qeV605$-j%vTFMsD>oXj?f;8OKr*q=_FX4M0)$|CA;|_K_l2 zfgq8w>L^BAo4k?FH`b>CqLmR~G_=j1#i}!*fWB<3DCHAx30R@Z-fXOF65eI)l%8a7 zdx(Jj!0Kx8uLpESMP^l08qU;4q_C{<U$YH(%_uoEk0zlzBZ{8}+=d4Ag`y(DB-fyE zO_Q^T!74Rmu_<duu)pPLfm!u5e=|ps=W?m4L6o}mE=(;o$tZxu(F5F*uSG}WliJ+b z_|zc$Yad6I3x790m6(k*KJm>W_ZbUbCNY@9hR)C(!5F%LZAv7+1|wBt3YFlDWf!Pm zP=*};Xk#B4!vn>nG9KPZ=$E)Pk)Dd={7K7{x=cnIF^)(~D3!i!eo=qqTVGe!;-_Y$ zbz`uEPm6#34H18qT7xB`j)SJY2Q~Vyxg>nW<x-ROc`nrOe{wOD(X>!UiWK*M=7r^A zPmu%OCUWei9m1s)OlMOno|nNOP!3dB3&E-hO^ar)I<<^|M6!e!_!VhP*=N}d+$?gj zuVtfT$|#ml&SRhBr(<{9K$v!8Z2OxjBUrUg2*zt{eQ23GK4$AhD|_Obw!x?wTiH1y zE%vwbZYR8n*N0Kwq0gV6UoUBALh*V|A4VWl{qPVc*?mPvMB!t0QzY8Hy5;pBbgxxm znJ>0nz<kOK3REg_4{=|*{RO;89E%cgwYUeJ;Jn&C4prl!_Crw(F1B|=jrfFpM-+u0 z+WXt(?o%BUO0TZ@IQFCh?Bn1?i$d{0hh8WJs~x(aGJMpblY{znBwD77wDr0ReS41W z2Ao}kTOC5I&sHc*%`7Fac$8Qm(BfW>;m(?+5^obq%saKgTlT#)FsJu9@`48%FUKn# zJJ9?>eB99wW#f9sY3LY^lQ^O#oFwT4@)3zATb{(9_A|kEB$ItKbD@t-_MOVj-*Sqa zfl-qc^;!a<*fi?(Wi?x8Q5c@$Bs2AW!AQ%K@Mfn1i^_C#pVN~R+|}9JqHc-XW9~g0 zE1WxdNEa^r3G*}G$Elg-zJIH_X1*V;X5yREKk^)K_H7rKr7Fv#6boKh^J8?G3KL(O z3F_dy{ke=*iJv(S0?(zdOXOh9ES27zdh_h5eq;78tuv$Rr@ngI&cdSeILJ@d<(Yp( zs4(Tt%AZ<I`FNxQ#m1(k*5?Xe&#kD>5WeLnwd3>4ud01FLFwP(i!Od1d2>`vc}R8C z9O*LG34(Jw=XJ|JmVa{nY+ovr(N<#Hb`7@G6^t@r%6B6V>4_8cLe9Vg0#^&<o8Y-M zflf4bQk^Mu6PswcoY89g3LkS?9gV}>IzYoK+=f6i?{k}ka<SMQBP}j-pO5OWhsQ(O z#2Z^mv)~0wzeeTwsdO%#@E<(Ib2VMJ2*3A?qs?0n4E7pDTf`6S`WLTj<|{_ijCcF2 zf)ssy9oi{fK{2QAdMzd$sZnH1|9ZQ!R*tI{r}}<vUf$LA+;^k#XaU04e5JIg3x4hE zPD^`WC%;~(7LW8>32ocp*MrsuV5$Ea(7{9g8KA#m0sg`|Ia>{!9gR~0h8adeKoB~I zn*#be%2$0-Pv>fmO`yTO+WQ6OE(b@B4$RFMu>O>eWGcWwY9dnA(TZzj)H2C!$!v|F zGaE<Ezy<BoXetP|v>z-IrjrI%Wz~O0CNZMQI6N*e4dlBJ=<FzqAwm!%_x>F3BRi;0 z=fLx%(z@Zdf$rA6zeo-lX6#xfO}ZHSb#V7-mW%n)szZ8i=ya`i`c@d_Iu(zq;VG17 zOq`?Om&ThrxZ2~F+0@!6dQ)ZV2gkW9@k;?(ei<t}ICjmg!HuTK{_&8C6C3Ow&(*8K zC`4<3ks4`WyZ=&eN*NM~U;0yc-u%{?*M7%8cW7^?9i}kh+bh<a*b@_wIP-2IwhjuU zw;w$a9^{TZ(rmcsZ9{YFblLR|{0FS4_bbGFYBEzU!t*<}7bS%8t7iLCp+sOXys@~v zV}wYX8d?2Wq<nSoV7i>falxIfeYYE1U=x$3?T=RkJJK_H;?m%0)+Jgag#aZSyN5*4 zQ$uiah>UI?g9}5tp)`CYWD>p83wM?UgVCHMlbF<w{%#~`nU5TC&{#9PLFR;H_<*dp zuw$lVH?*@=NBl-MlP>Rxzv<N7Nooh(*gSDPJGbD>OxpYS^A<)~-X9<D<Yy)e1t+XI zeJy^_X$^hu16Fo+1#7aY^AIG(4?EAKA2N7C7gr}~4^@+Jk}gxS6SBAHFrMguD^E8P zukPaGAezY*5b|2pAe<!5vZugoBuMdvE`#XUyVxmonDt{-5=Q~kl8}JshmK9(d0iD| zK^-HiV**N%VQ#K!FrS1?GT#Mef$>VH^HepNPf{#;VH#$xY{`vrj&XCz&}vfg$TK`e zL07(FtuN{_FA8axKw>92kljcXDrSoWi;7ZbYb1-<yu_vs6q{3on<1yUFX-8RRH&+( z1tpd^PzRg^Wz_z4YbLK29441uahl_{LBkBGAQ~Yw>yoZ^=6;vRZ=mYgWjk<T*8%1+ zB@$3?u`uQ=ZtOY)`QgxRvHg8pnF!HrjxS{c?^w3<cA+}Ci(msK;R7?@_k-egqlx#y zCMgpUKf7LN{+1gf?SDWq#2j=l&m32E^QOyFabq`k$I~xC<_OI+KQ;M<N2v}D*v~|y zeS=-Xlyu``Toe}UqM5)t2Y%|UiL7hz4C=&cAkI#?kMD-XJ4QDiFnIw&X1?dgZkc-C zFcT<Eacp;)dHg8uwT{M_-N)0-L-C#NQFL}a4(#DNvg{E%=kK_{O3mvk|An0g`mANr zn!jm(yHuSKCAr3<cz#jK!I!VK`EVz#-<&R8wba%*^ujw+yYRIUo*)dY`~*xy^MHLY zP}rw<ys?MBZOTRH<f8&goF_z}7!e};bB{@&u3f_ifo5lf525}4#<k&2bXo|05I)?l z=HM~H1Ac5SM5BN|%@4=*Tm)A1T+daY{r~9|6L4-P(_FEfS0<#8Z$`9YahWyOGv_w) zz^^|>aukt#Xhmy$vDaqXc){_3CsgYE+#0dk#30q;-riEbShgn2(UJO`!Ax`NhkraE zn66d&=n{Ogw+o&9J^llNnl1Qc@8E#*m}%bog6D6}m6}#&#&zX66;J=EROLK8xR0M* z<-9LTt(=Q9`}ol*>+r5VE_B%%e6~-3Fk$pYwF$)}Q20%snJ5BJ?;9<S7=(!l9ha?+ z5?}ibod*q;;p)DNp}`^}I@-sNQTbc&+3K9TCFWZ|b?QdqWf6-U()uZ8=GuHW?q<RH z%#v7clv-fh$RYH$WjG<Svrj}vRhbpvGsQb}+v_(gGyK<Hc>sA+q0ZGX3%!PCZL<qL z92w*&iewqtBI@%5kHl&#C`8!@zmD8)o#1L4t&Z}HVx`i4i}&=403*Qkn+U%{`-jjw zLa?&GkNKGd$!0JT)+;9AJ^jak84(Qdu--9-=LZX`4L~pt9E(E-45XF8c<F#8_6?y- z<*mPUxr)Jj6H>#Yyme()v9(-ECwOC-JRVu#40%t;<%HbVG^r00uNipYp8PpY$G*ik z2acn&pX2bDU^?$9o))8|YyI(~7%5#T!Pc=dcdh-T-!tUXpl;IPDu45s+=uX7d8NJ- zem%<Y_*g&3q#wVSAEEbgwtp2ajCFVSuP9X5Hc3Dvv7qTjVZE2Zi690ANjZUkiA`X& zc#tps_#hrXs86uQlXW-2!bOxjV?nb(SD<)QX7XV~hNjEH^_1iR450A9W0J=Z_TGIG zpB>~ipmwIhW3Et~<w|`_+APx*)W6cb=XEuC1&*>C_^~fl&_S!B@bfUrFN^h+;(dKD z*+yl^C&T32JOu|2cA>T1aqM7ML41<Kr3VSIcuSdspPyQnXa%E^&FSd~SW(;mUh!eX zxr)al-oQ+0@^Tr0)Iz*{a5#Ox2;Ut%#u~Or)<XaA<rkRvgyA@Lh#xY?GlwYX=oPD- z?D2ykPIOZqHi-*D0^BLinNG>ZgX3HR%GAfm*;8EWAX~3{es@hDQ6gQY0}QbARjjkM zO3tJy1MrGCcRE^$_r&$0wO;s9oYX<S=!@2GVq0H;J%{$7Q|96EL%Z2LnCabJM>$_w zD+jxn7J;`6?LbG*z?X-PryI36DBhiBp5wvsG9<#^#kZqvn(>DCV7qwt`iDNPvo*pg zowUBJ!Fq)Mi1(te+{Z2PGJ0<#mJRDnr`F@dVNyqpP5phJ)>Rd%9(;<|LrwK&`Rnk8 zVeXydt?D26wDR?{Dax*W#cWC@wygk*q=~m;v-Z@(E|-N+4>nCBGk<)7(bQVv2g8E= zl$|%4@F`XU6i<i=`2!5fv1wmPAmoKV_*(K1^vUAkK-_D%kG*D0JD4tMN3@#l|5#8R zWelD(Jc|A%79St((z|TPF;diDv2wJKKr)=)p^r+RPf1KE@beqRSJfxiuv45eCO0}T zS7G{s<{h%2)N&LvZY^v^VDVR;j>@6+WBBiRhw>?yTwj-}fs&vcg=4>Rv9B3$nP~dM zI#kVedQuvg?m2kDSHafpJM#|ZQZQKnZ{1LQ@T=g?(}Go@=2T!V7G}QHv4u)&dtO$< zrUT9Enn%R~V`c2OFW7}c@lBT6+ymQ>$O5~taYQ>5f%PN2=_U_+V}#VTW+d3>{!H!A z9i>_4)p2UcVRmBi`<W@f^&@r~*_pmC!GlM-(<PViHzRx5XSdVz=lk_@#HLC*ay9E% z)S2VsBfaPy*0^q@k6m>Kro8VRgY_^adaxd4*l|?6Q|)&;b&o1oBB7IOa_V~M*$EoV zx;0)pY8lKrXmk(oS$mEi4HF6;oeS=~+nA&7{!!+_@bxNxA-E;Wve{~gCh77exzIeq z`(F6@nBnQ|z31wIph*k*NDw+st$?YdvbY}NDUJ6(@y;=ND~R{FV~F?tC*Ezh9z3qf z;yO0I*8QJ&``ij4%YVxdFa0Opsbd8@hBD;rJW)NiM|#NK>WYl$Z%<$gs)`dX*KBrR z#M6uz6zG@D3lrFH%o9@T_TRme%w>DFWDXNi%e$E!n|&>7cou=)#_FWb#$LCF37IBK zAg&;2mZSjqEq6JPe0a~_>A>Z_BZTC^dtRUeSq@?P^4$^%Jcz9z1V*vNPD#c#)sak* zN^c?>mm|rBI6;%+Whm^tnw`O^C8{GPs3=jA0~E8f4je~~!zYfqg}^}<eZz5Vx92#@ z>_2f-&W93AjweWR*quMK95Kg|E&jx!oeM1d<5Rdyrr2>DDR!SYl(T`u^mrP_G0>Ld zh_?O2kuVcD1jiR}904MZ!(a4?BO*N&Sj=vKB&;B2HXMt<<`awbTi_7hFbZ<disNXq z`ouv^2M+TaMnTS6avU|5pE&BK0Efj5qafQYIF2%l%US6ko3%0-SS;Tg1<5hzShCGO zWui?44y*ScTSUllqzFH8C?`Y1^NQveTf~gxh&KDgk&pl!)<sK<Eh3Pl8~WE@@QEd2 z0<hS;H)djH%4H%j{lp<1|9%M<>(A$6IrXbHVImU7sScXh|9UzZG(|fWa<WtDGi^Wg zn4B{q`%@0o7%^XI!icDEUF)s**MUt8Y<4qprhUd#ITG7{9n{C;-Pb3X@{qSl`<U{I zVqJbc#XtY`QKsDH&BBwMN>BZn9X#K%^i3ZJTKlVqPQp&nk^@V>cHz-wz3_$v8J+EX z;9|l&<S6)?-MO@tKh=k;i9bc@NYk<!5gs!+8AaoY$qsar*@0`5{SY198@DLJ>Fgd5 zIODGpz3H;9hR0Qjo^(?u!{enyf4Zil;n5}u3}btomedXpP4b}q{opa2PVq9t)Fipl zHSPw!)+A4ubUl-M($NwFZDFz-T_!frdL}yySjS1F7vP_g*E;&X{$f%E%ZIa@Ug9Mw zPIUAOye=h{&TYnbQqt(D_wk4+%jmM(`1%xgm$F-hiZv;{<}V{3_e&&@R4nkniA7U= z=$b!qHwaRG$K$64(q-3h&QzcA(SPRj=ldGAzYIGoU>L_bSHldM@G%cY*}VAUGpzIt zTwK1{wXTX)HOEskYRpoW5$DHT75s1V4rNC(&6<JOY?{xA=s#5lv!lTxHrKWX2azIR z(ZG6jftbolcf}Y!h2-33sbQm3Etv;{(zLM?OHb=o-;dv-V;3)m*eo`-JZ%wHPIIBB zR^y^+flg&V8S@tnn-5g!q_5$(S!0TCP79`^Ph#uoE<#NrConiY)1CcNepp|R<eyc! zR{JemK_q8e-PmMDaN2YqOZlJbk1NQ-SUX)xXa9nKoE}Ka-0+j>e$txbx=gmR&tQLF zoRvwzNd+}YfgW>tH0{Kp-}D)rBnGdbymqmoAKyoE0jA2H7xuCXXI&;a2VH)2>Vb?{ z#WH~(@0gHc&!KqT;PAnGi%?-=>ccdb4^>cnAM!h>KyJ+U_~bWU>84hYVWkp|Z-nQ> zrp-J_33M{Qq>`zv*bMtYltc&b=7AL%7m89=)v%7hGU6SoGvQjkM97LGnhD1+gZNJD zvosjC0b}^?8L@B{^%`3=3&6=>x7?{MUEFS~{7t~=h*c||yrZCY9mIIzx6ZUO2rv3J zbof&TT{f33|9!05d;1%TfW~?66CC^)GUc*lMJ(SM_V*24X!_P5JbNTHly8YmGhZ9K z7Se-Qb63v@KvuZ@j2_(*qJSroZ|dD^+snxYage7j)AK&~(yv(Ikn@mVRet9DU$64~ zPQPZ$1HV5T7tWZ1gczkdn3djVvy>^Yd#YDwK_1&tTv<`~$gjYymKQOi+ljW-pLM>W zDXaBx+%p2tN{uq*uVa)NGkh}DDb{xl?8ut<W$l(rAT{g@U?5rfWsyVBi-+V3x!CGe zjI!2R*uYQ;g4tBOh)GNM>T{Qx(GFE&X<Be#NdaUh;545*Bw#h3`3bhtwL@z^jR*?1 zz0xM3E%<ucL8r6@Rt;NNM>?W*KL0j9s>JYIJ_~P~84#K_$F`wuRg%^*<*Cm;#eNcH zPbMRpIdAo>4*d0uC?gijW_hD;@Q7I*(tR<%*<4c1*0FjT3_?d<X{P1+C;~B9?K_us zmEzq#38)ysw?ww4kJx1cB_gMREA#ZM^-+>Dkb`OI%u+R*qTK#du1QIdt94aMm;edX z{$fFmi|eT{2_=99v!I5?EwkLv4D32P1bvMM&racI!m*nOADMj#6&=W$BS2OH&MWfG z1s`@S-Y_=?<>8YH1L(3Ju=PAQs}_!2tpX=@=z;F@)I5{}^!BhJZ#~b|+V@9E5||ji zZxkbqw>x0BD2Hzi3+NA9)95*qa@jqR0|(RRnfmijeKNvci6k5J`ea$<r#O-YHO(r; z!?OCKQoJfl4)e%wS-sH|?2xSzmOM6=g12OM5jNcC-jeWN*(o;nU$Vn=(*?2(H0PiR z{wik>ZF3WAb6impK9SQ7`QqPmdfN+r%QHE)rYM4SWernjU9k=a<}RVrPvY&lQK$kl zxgBW_J$BFAMo-*~)7H2kIlh(W0fMyVZ9ye?OMWCQyX*5i4N?xSAIpFL6ZoNqlSpmx zI?yvwnu*F+$cIDO+4bZ2Z@>PBTCs+uY77TMs&2V_o}ZAfQ~PDJ&O3!C7C6uiu6SO7 z7v1+F-caBIQXeXaq$i%iuM2$WiRZEVawpp3FWh~(mu2g3&eak%t4j^`IC;4rY`QI9 z?&errhJ0aPb|k;$u|6Oct1~G^(^`frm%Bu!o(94M{@b|~q4Rd7%4Th!?%nH`_lz>V zmF=NBiE>yG%VzCL-9DYs?A`P!4zl5AeqfXyxZMhGy4oB^tQbd!Ft}*N1f<6AR&=EM z=HtMX?({?rj$P^Q-B-m4aK9qbIIA(WD_eBgNWQ*?lbDb5SGJ?8Tk!Uk!x109Tj@^^ zZNfepFT2!V*anK|CpOLe0B(JJZ5KR7(+Ap}Xrd8=Z)xOCLyzea%o7{uL@g{`Xu2o{ zgbtX-lu!Hx_gWQ-hT@s4JWwiLwQ3mJi|beQ6{lWMM|*4Ez+@<_Fr2EFU%_2hPox{J z;dQIKA%A>nwVUHmH8<?&sq9=CGy`(frfyt~?K|8$@;T*WFNc(}Vp8^oKI|ssm%_TI z827~zm)45U`Ga7$Qk<0qeE^-8h8-PJv%es-1!-FGM%>O~;^vS2S%Ryu8@M#LutWM# zgNb3s1_%|~EsN0>?`Dk-8`FJJ=)9mIvf%+SjCSIQ{}$W#%*WWEZ)LMsK99Q}V*+9s zZTh3n-&&V_{?>N`yy4kvBuITAe@%ZA6oao6UZKwmuy*|@RB_<ddW2BWfj1k{Y5MUt zyl7J%O2+)nA3!B|#UuSncY1GY6iLcpa9ap;yl7M7?5WdvhYoln*$Ikk@$e{KS{wu$ zDLaeX(Py^dv&B(x#%QDUr5mzwH|=3K$a$<CX~A3#%;l!yBs^q`Hw@yLTl~=uync%u z#o}9ACc$l>$gR;3le;w=sJ7p?gMriWdvDYp5Bq*D4BeXVhXf>bg9Z5CppkIO<&`K| z3;sgak_n%zj<A{#5cUV$Yg->bK?rOuG?mf+Y<+@}f&=F6I@mvjlT2V#>1(E7?KUZ# ziB)ZLu!#<3_lQBify4_-!42C&=#|;nu_Tl3+j^j=<Oo8Ecw||auwlGJ&Kk)iytZti zdG!eHt=bklZQtNHvH84uQq&AkrV3_K)Kzc}7>x&m&u$-_o-}~tY%qsvtSvbq&N_z( z<OEj&`Wf?h%`~^-qbK%faq;p<mWQLMP2kvab?%{0sgn9~lq3rB)A)WysqW2D<ZmFq z6+I4(ow>Dn)B(IB47FFkgl*opDnDT~m^W%d7RTuOifJDD=JOanex~bayZba*4h?mp z<2e!pf)?z;op!dj%X^>@n^TQ&Wu&+SHbs<ux8iShy3p0lIA>=7^xnRmZf+N!Nd`e9 zSqyrKH+(>nSa>$idnFkHq1AHyaOY4^vYxx#tV)`h<}0_26~Rfnd?ozf*Vkaak_Lm? z9CGu4nxlci-sMJD|Ai}e*^3))GR-!Bd@imWH|_EmCcnotGuL2?BZj4sC=fZz^rze! z?y2$0$Ldx$!lw8yD*u?z__8j4<V(Kvkx!0C@9yZFbOTDeQZz;UN`P$P(7lrWDDI}+ z9_*~NTLQno?vC!+c=G5EtMXal>sJfz5JTUVoa7(k_|?Ba)1P{3d@H#M760+O6C4dn z+T&*5aKWvLtT8wV7f5@MKKEP*&e=19zJD6u+~Wv?>BSyw+hHjwfKEBSD@OvI!|UA< zuitxuu0D>(l)HP&uNX7bLlui-Kq)R6m8|3vNAbn$%RAXxl&S(PY!z$G9m!ZIFjt8G zEKjmcZQ}Xo+o}%dMoGrO@HW@sVf%K|EB{=*Uuv6dO1KIcE8u&e(|!jusJev9wt6K~ zm;U$f+esc|m-k?a>R=uv0XfB%LElLhf}A~e^u@wv0a*;P|L`zo)!iXlRj4K9X?gA< zuf!ypQC`8T4%B$0wbK>2{lqIZi4M%hS*MM{%dxRqc>cj?@AJuwa_D+*nT~=>SB^0M zJ2SG%_OMN6#TQBb!`h{t`1ZlaboDu0d&sYYe2JthJ7DEALBtwtBCX<eHK!P5>WbTX z0u=Dv7I7TD*z>TjmwdjFKN<Lcd@&-%$W=X;i?2?>Qx40}6}<lNeL5rqXX@Y5#m}(v z$UNHP(Sg@T%#hS$lWjGUe>BDmNB&h2m}59PHCUk<uLII!UdTUJ#2#9fg4tPH^Kqre z&0n6dr<}f5k_&Yaw#QR{@V6)%=F}h=4etU0-uS~lVM8b<OcEY>+}9`dg;n)4Kbee+ zE0M%z!F*P?^5!!?sWGlWf;S(BGI7;$iF4VjK5NZlK3PuAhg0eO@l%%iywBQ!|CQ=_ z!q??|Gu-r}E|a1ZHG5EVN|%Y`!MRMO&G$Ir#B5%rjgmBvN3fxO!o|7iE^LRU@H6<i z=+Z||0=5UsTd~{8j&LJk(8*4;%~QPeq!bOp#V7wGt$4iV8+C`<CV#yY^JRSS&o2tk zu^ZF|AFq7n1?*B0Xut9<Ox<;<R|E5BC(N3|<~Q*U9#i3k3h?5J|Iim2@b!vOj_H3q zt-ua^I%5^=(v=tguC4%=*cT6|3<O^!wNlc*=6IjA<0FO9!D`ZtwG?OS_Nx_{O|(@s z_a0TJSl&eOjc>^}6<JLbNAbHQffQm>KYXsz6RPpBQVtE<sY>4U^zJ>mfq5BnzYVNm zw7KnR{*xRGS?%5**j@8W(Ta=26var!v*W_@1!)K6=3C&MRi5<F^Z0VrK(p%cY{HOB z*!@TMZqv7@^cGZLzK)3b5hkh~H3+tO3SrUfx;|0NPk{CbA<k9!u1UgdjfsqFF`IRG z){o=qkjJ?8$GbLHGMuYs$5T#hdZwJgr%rXDD?Z@Yr=)c8JM3~g$i-v%@kE^H>y_b? zn=ck>L~6#+*4n<WaKdSKy6+3DJncfKKf^_**#nbPrz88{dL%qYQrHPJx(ln4-r=u~ zLn4|<TZtu$plaslXR(8G*a!Av!Q2W@IL#5WZmcr3gaAKW<v0*s4W|>u_59oHLXbrR z9IHZ)E1@g*Gz8EUxA66bIJ)8w9MBjp$(@KzUhoWa=?mEQ=8;j53#=~|Hu{3QQQX*H zVmcnKC^r?>dw0KYqUs?)l93<>tV7>4vYv!PQx9>}$TjtaO|HQ4#)PAm;7LvXFpP7X zydCbx>N45<i|g2%M_m4XxT?tmmI}9<dcw-Z?OuOy&?nvNAC%aWg8Q{Fum4oEgyTHZ zJ7a(%`!}jEwMcPnP5q|dTDTIbyW`q>UT(SFV4TycP$7y{Zz=%S-~w9-2~+|uG+~?j z&M;}VzwZft^q~8lI|K`YEGLPtMN!;vZwlH&Bx8Wp>}&{uTEW^p*qD0=-hTfE&Ur8f z{Oa2e;DG2gZh7G4;!#GT+1^PxUD$u4fKMsDeMPhD;^Gn<{?LmKIfExWbcZF<cMl`! zzMF96LnqXVe}A}q@XBMk(F&UvRA3h2vu07<Qvx9zcZV|Ndv7t#w+eX2GHKN@6JF*L z8%o53ZsiMkj3(rOc&(5|(WP0VvaYJpD)3bD@%~59eQPoOxPS)Jpm`h$-hB1rj;?#p z!o>IDV-XELToFm_K(%YI^^@+MZ5}Fke7jXqv02yP1O-k`ma%r7t$@ulW5qOvYkGBM zhS<GN@q#Czwjt{#-3BJ<6{5>$!&H3YiL>iU%(lRZEBt6hP80oN1Sd9owrI10)Y^ib zGG(L=dmF)`NNpN)nfGh?QMv4p)eOZwnRc)a8O?;bh13DP+~zW?z2HRtbe=ezvjVN| zW8mA#_>bSK`!M2u1!#~oq=55KIo_I!ebzqnkC3_Ehu={A%Aa<9{LEh}bA9^EUn_Hc z(e_}GjDQ}_OgvYV;2gC|4tErkeOqw9X0P-Hp0S}`f*@PLHM+8JrdPAyqu6_KcTJyD zP7;{L#X{j}xFZ1DXM1u{)OvY1Bu{_6{);|MLR>x#Pnq(So4_ME&&@%F^Jk07WQDV5 zi~MDU(`SpKWksp1xU;p<p=*|C@T+F4^whIo&)iz)_dfP@-1AY7Cfv=sJG^<^y~%Y- zGhJqI?*6>PxySO2>#K;BXOS;w=LR1G%X5eqZb|uV@U4Q~JtdBQ*Lq<78!Ajdi9Uoa zlqkM?B~pwRn!)X?x-kB#s5t0(knU&D1CjT^IXv$pBY57&L(_TexiIvNvf_d5rGGT& zg|T7oLc?f2jGeh%6=n(7U_c?UcjyC{US~u57e0jFQ1N_!x{#R-?O*upnV(k13oD@3 zyc%2My7*j8ta=%FGa8%->V=tNjSx;_GGdmI-rGM3kRJL;fIBfy*&<Ys0awhHQ$>r{ zUil{z;S1XKHk?^OCmIC4wJKWS@q#W*FipHOr{6+)(Vc1irf;22e-o$Px^NP#SQRcp zinW7J^$Lfnh<%FGzZqMle=+u{{_)t`dZ;d2Pxua@iL0;rS1|t>;J5F=+y4%vLr&vg z{vJSIslYDJ66|CVfqA`e<2MT_i2z2hAo>O_c;-V>e7yfzJRPzRn>_C~P<@rliGPXF z_HF!b+@=cWnOk3%KEM1Z>;3uWtM@3x>%q6vW!lOa*_Fe~WAOYQl!Aou#1dypJU`op z__MYgcF<Qm_oG*q;KR>@Ep5IZQgKxc<1Zm~D{g-N4eh@OPk7NQ{$h|yZ%IkQ*tL}) zfY&2C`cMt&$-06aq-e0%Jo61US5`>|ff8Hf&sH6^pgxS?9cFiZud@4_X_ZmBOv6@d zwF%5KB7gkgMJO$pfu%3U)68nT^kuMv$Q@2_^Gwjf+v@1!bq3p!R*tJ)4yEs(#dfd! z20k3G>S{rOH=vcx4$O79e`7pp4>icWe|r5xcGsDE6P`X%!}8Q7sPws%cX=o(x|><= zeQ-VHsfBtquT*2@t66k>0)GBVYFE~RO|!v_XMQ-A8JG*(mNM-3+Dj;NTlfK(%j@v4 z*BP?vLPp!V3CS<%t^Bwq_ua^7ZxtbVRRuA0s_zyqz68TYz(!2JIc~~d1--HopMDc0 zOQ~n~@ks;hcdC7RLB2AjzhXS!R1yx&8E)RdI%JBK0uk4s`FF7Nt*ea|4z$@TQHn^R zHIr6{2fgi`n4HTdPLKuW_WoVvKj3Z2pO~-ZF%cdYx}NK4>Kn=OGcv8EF1P-I@VVip zZCd46t{C0#Y%%SBB~Ym9SnO}$u~{gqwp`&I1#b}2@wy4&vQJtXe)Lub_nF+@Mbh#9 zIN@Ccoz@PId+&}b-${`c-+t#2tfkaZaB<^(i|J!iezH0VW|u~EpVuw(y$WK%=EGHX z_7WC%0Sl+S4}>%9vF{_{ccbC=ulHS%28%xoWj||5{4f{@`#yNkP0#Sv55aU(Gp1X* z)6tJ{ua;?!{B%Yeazh=}V(O><SX{_)e7q%yZn}dXv;@-rb=a}BGu?Cp$F}w}h3`7F zS1yq>gdFLV->|NA6y5aGS{fZQElC8*F^<x7dhH*mGhJ3itP$!%mz^MS2)UZdzh<<| zUXCRjd|!kD=mtI65Bn^%;1H=pXs~I?2*~Dp(wRnZ(zKgQpiwYgrXwq8G|*j8np1B> zMW7-DivCI9t!$zs4lpG6{K1TNFX8fy{+?L!Py+40iOk@k@pKd+7kTI#I$1-)O;9P_ zpeA=rP@H2@2XJSeeNl03@y{fRkH*m{i^*0#$nU>^T;roYolB(b(Dd&83ci53W!PZi zPJH0IvVZ<yzJYm$2%!7R{B21e_(}@#$xBmIhTf6Q0@NFV8v^9a+iLofycQr2iz+|0 zcj%h;jBMgP;$?<9I5e}bk{58ngP(N5+v^Plq`PFY8R{u~=PiX-jmL3PYKAUJc5__S zJL?OQ-@!GZcNzukz^x{5&8|ngTZW??us&c*8hGK^(aTRsU?js5^G|Y9h^BXtNv;_# zk@P&T_AZBP)5^Q+GU2=?Eb~3rp4AQB2Mwh&WR5xNYZ2H(dsz*Jx~{d6oHR%6kU#mu z9J$jK+sWl*<VZvoNbFF3H4DM*Fc=~c2~){C7>)V)Ye{zt<n2)TiBy-wkt|k`Gz;X9 zG^Ef1^+yHdh6R#~e_~{Pe{rthq<|d(*~Fhos3mC0l{_-R5;=ArnyZ$xC6o_gYWr$F zE}Al;Q8hRl%@uDA?XQpC^Y~Bp39fJMCa$)~iJY`VLZl;o*ylR-c?Wr5i8`PX;%J3- zqqpR=6$tZ+JhDQQ&{Gm;4F@YfFfxzbq|h39qKD*|HR@?~+^7bPj4ZvB&^9Q{szH%u zPLUq0q1;hVqHLg_$C6AN6pcobi#BLbyJ2jTc$Y$Ripodnvj2c9u98N!`IF;GXAx3} zcQCTnf^&zsI?H!Kp#@~S2*vqbIoq~=#UEgPVO`g=P(OHruMKM0`gx+$Y}Gu_k7R-^ zYL6;NjxCyQHG!S#i3@9x@+PmdL-SFvet{i~4adE^6xMtfY<~K!F7sLzCr_z>Jh4Z8 zkqHTQK*{vp@5wp`7z4M?lk*O!x8${9w@y?yUmeYV@lTppdUr=;gU~P%EJ0yth(1|@ zu9%|v`fl!MJ58tFAaznu6z0V`Pqc^*xvyX1iA-tQ<|looH@c2!n{xdGUo?%TZ#^dG z{gDsd`ieCBqZM@P2cir>p=@|K08GKe8~Qr|Xak~)f7CAxL~)2tzep~3Km*)8&gyb| zoRj>@4jc7GM$~sZ`<)PhcW}`+5LpnC(i2O_@F1v+$5wq7yd%1GCE4B)b@Jd+DTCp1 z)9Lq&=vMwlNE??eIm7mlX*zk`5xI%K147Q{gsdg{fME0$a!3NdE|(F7{B0<*+E8R7 z(S)GEt}7quawQN0)@dRm+WYVm2Y-V8bqKP9%0JOtc0$JyedPo>-x<UV`Hej4jMjAy z*}u^oz7-QXP>Ne4kkxiZ#9a9h+C}Ia`9r2pq*axerCc6?;0{$53`^18^W<O`6i%n^ zB+Xr*npbqhCln2LXST9Ziv_3O`DI;ZH(Q%+8ar0$GGPK2g=`^fLXp&c@0N`M2g*D6 z`CC7@Ql^-)p03wKw+?50OwpAi<Z37yKr@BJsVj6wNFj;qil#f8aMd2V!kGPguDu#* za<wa(3x;lBH{{6^?NyO)x}g}`R2AITReaS<ON6W!-aeI_>4t_{Of8DwB66n^X&91s z6sp3kD9I}t3|Esm>jYKZlDvnvUY4Sa!Pq<#PME<7GKW0OA50>v!_Wpg{S^uB4pULZ zYZBERjiS?=$>#3Rk{(Y<Wp~sCT_w-EqjxBRH1~iZ*^6`!2V%u9WL!At6ogsfpmXjg z$*yqZj1G}A;h@ClNlQ42^O;ZvcVXeWui|c*DbF_(HlO>H{<B^FlBQILO)rzQo@fGn zb0?|oiR|g6I?~t^^+4`K+6zWuI}+OqOpFt`+zYu7O)oGFcI@*|WUY7a4a37MqycPM zTF5GLxi4DjI|sJp@0FSSQ$`b%p|AN)N7g9RmnEi9ZPUi<WLX57XfvK11YOI#JFb(K zNnkdT<_Ks5>mP|(B<f}!e3lrJW+#z>k;q-Tr~1n*v%h7x^56VPAWPULI7_l3QHL(o z>_TTt4Sa9b99PL@Q1-Q9A^D(;T^~0;Ir(KaHOb^|B<k6v0#@tBgfotE32R1up3wTp zmkFCDlHvVOM>JNi?1z3wXd@Xn0J%)qvsY|xZU?v5-!2yxB%aTT%UXXfC!xxON!VKm zs+&-8y2tJMH3M2D4KxfYc|KFSf;1*XC6*?dPQA0R-=b0Kt&4nNsQ{aU&3lS+?vDL{ z&81auDh%%%^;ZX=rL^P3{U3K7=Z;*aB%o`sV4V&=cvzQ5@O*r*rNI%0_Jhe&V06SE z0_BGeg3lcBcOZg=MG~ovMx)V0aI=HRh=It3p7@NU4Md6V6-<L(ER<GV($5|HrUDr+ z#*({`(ZR$Q<nMtf1bs!kVnEx5l5sJp)#^{y#g;sPa>I4kNOCNiLAPEc*J44Z`<^Gk zgHVL!-u1A8_3_j)l+AkmqCrT;ql;IPU2&)%?XiM1$Dw(2pFhZyp(v5Q`7^mZ6rHEt zYsiLp&`|d(@-`j}f6Fm4ei-^*I8st-FmPjt*>Kdub241kJ;ItCFbq`082%+5d@2!h z*?toFjcuTm(PZ*)6zMP+e9zj6dpg41N>~p3$+n8caQ%_tNRH4}(()A=ggTSh5y%a7 zB2z}7O+qYz^l*!)m{^a5nZ<|1jYNObA?rxLQPBSd1U|IyPN%LWx>0C9otjA|jD~2~ zzaNcyIBQlaKei!EgK(xr&d{v7en)POhGtu&cN~LuAXHB($D$CM+hnJ~;keD$GIg6_ z1dl};%cWI;a0`+3TPB<!8^(cUswJZTpv7>&Rq!A5ADA@n{RgGMHv{qGk=(%!7ATd) zAK>ap4Bv!npX4J%Hy)-{Jvl!f&37mVj@nlB!pGS3^c#+&w499k8tlw&lJ_;LO)g%e zu;IrC<{CELl@+kU-Vp^eZFhC_YDuKrTjuiyK4N?gC(WyMdGOt{HG(rR7+aMS^K@$X zK-5IcKZlsQJ~`i82~Duz^h@t@bISXmIdwA2TQ*j}6`55;Dkq>=dd5RyIT0l~wyK@A z@O2vO+UG_=&a9bNR%DW_iO}L|a%LhL;AGvxx`el%sr>U}RNXRP7;BeJyb{1%Ws>m; zsH4E35n0QKCIN+TFMdjLCINMlEHw(bj=kalH>%dLy5qN$SWg01@FEGE1P!o=Oqhi7 z=oKf(gGngJdgY($1DJ2ZUbl&8y`cA*j20lfxUH~H6cM5c%p+=csw~+?_9(#Z`ifiw zhu&d5+Y%mX!~GtLE2Ob6>70mOfvN;1fnyd%3X;%bi$aGp1dIUO1uG<W$w(d@^kF2} zt;*K7a1%O$Zwfbt3?gQAfE?qcL%6rnAcJ!nFCBdTBfX=6K4`fd6gxhBC)u0~v%pJo zBpE8?MII%CCVLX|6tvyd`e$_qg%e+@@<08ylX?BccP}qz8MBp;%PFXX%|ey0H5EC9 z+yuc^ST7`IQ^51L(3ej^CWzj1NPl=L+DD^(WXd<F7@gIdeT(uDe=j3Tts;dp(AQ`u zc{u~cpzS0g6|F_Z<XS3{x;}XT1-{u)r1nv_SN!~(?vlF!?q-8V*IM5pc4=tX&_zsn z`c*5K(<N(JU!!^*Q~Tr&?1sP_RyVAL3f9iJ>D;i@Z~=lZXRknToF!|R+PJ!poYk1+ zy!<EGpN7JEYM?dCTh9gNMV^E5-<_-Bk~FM@a{u_4gujxNW5ut5c^w<TAIxHvA}Wda z%tR_wO}5WO8P3OHz?4tirWnXK(Wzn3eBii}mymw5(0Aw(shoxE?PvUuQwM{=PhoCp zN#?S0^Y@W^vtXW_LAuXI?gRIf5<P2&1Op7y%9Aoz=&#DSl7FTZ1J!>BJFR*5dj4Fo zT;Rf*cK9%=vPj{l_U``tIY?SgHqS;u-DebYxz+SC<aW2r5vpEkz4^=Bn&fP53bnwV zbx6d5l%s^6gI-AjalWO!r?$$nK5xkz)u954wTZ#2^`g0OTL488yLn&*o5=(==s|Sz zP>t0iYysvXAKJ&F!vnH(KKc{gAmbK*9g>i13!u;KiF6?ZwqzOv=GF!^Ee|<yo7`G} zLapFx+*M=q`dR2(7NU8Go={EZEk-?|p#6)%uh~l8E(TqFM4Xm@X?#H9mmrMl^mmtl zCZRKA(Nbv3N^*WFXyj2cD;*5s0sXynn7UA$J|Y9=A~cVrEdxWBM>Na8bIB&hmZ1|U zP`^Nh6bRYtZ)Bon$gIyx7{4NqyCgXawdYAgmXXz2sE3ER{*nN;UHg1!2aaT^n#_YP z%xbiiKnSZGJMth46-$z#k+nlF!vGz^r`RifEHNvbXtPn5_F;x9%OZvZE{D5)0qoA! z%&S$?&|tCz@+uhIjeG~;@*#EPeKvSe-AH&2*sV}9CkNV~6DiLDQ+I_l=O7t9;X3ik zMUie&gUymr7OJvyRQ+<nJrvE}1vQi^CCXgG2tSyM;QvQ(nLnn;-?=DQ<X*M#!?<i{ zagNuO$nww}vzz<as#g?~U3rEg@8uzXTN|<%#LCaVECDg$#Oex><|CP7`X*iW1of&K zh1!x*`Deaa|BxwnKS+}Ep{Gj8o_sKxrR01*@^Ib`<4oJS1uR}azL{Zs7z%CNLg)e* zADfA?0F855x`Hix=zF+h`8!M!(hRvS6Fvn>b1x&$3s5R7P7{}-1Hyao83_%1H;Hrw ztOy%PzZK{l$|9C4QHl`0?PiH9&ypo8p-Zbt-AWLnki1`sB0)9!Xi$&#C+@)9;T`b& zD*G=j>PPV}2NwKwf*X+XA0;p_!RtB>OA#&6YLLXC7KGQN{{RM;hW;GIwrtcDa#4di z+6)6y^UA9v37SXE|B9HcLLLt1Km#=C2fyGyOM0zBbNRO!&5XC?@G7YDI1;)Vb@V>O zW`0Yn7|o|R)5RJtqYW{%(V3kjb2W<W5b!FVyV&J?`}3Q*gs=vq<}3E<CRxkQ75sYA zyc)*A84`g(<2Mud5~LF<B3m(Z=mv5VqiB9DQ~u;<61WDnw>?;tkXxgUh7E7_-yOu; ztR{(T&_b)VTs!zKXQ+uc$b&T~q4Se>^>)G*__)Ggx{_9K91YnFRs73iWBc(7xL9=t zL-qZcELjWlck6j_b}d?k){$}RP)FfqZ`oUlDPOUhXx5=F?HpA4?C3UiGStKO61lk! zIa_5*pmkxzyM&=Cj=`r!$QR|3K!Q55CBa7MGLBxd03<!gM5Ny)GYG7N=8$QH$crxC zOjZ;^l7(=w(aDZYkYvmd{(B5nyo1~<1lutes1mWE7aNvv`86njyh*=#J<3He<^ndN z?=1$f$~6I8$JS-!+D7Cr?p@TsUL$y77;?$1Vwm<Lq9WvgdJ?}PbO|Q8=S9#54Ds0n z3#2C`eiJBT75QcpDC2#yXA=zWMsjZx@{V!;m^E{=>HsGJU&?6v<gm7eK;CeL#e-A6 ztR)Xv-$zv28+7-Nz~v*4ek%V=0~xUywCiVbbu+lfHtR_DVg$SLm?RXV>9%h8S`#|9 zuBab-VUiKG){+~=u#B#_L_}I7^(nr9M>kOFe8mPqcXlD&16CLl*<-lwvwiEzztuCM z^mAkcaMBZN$UHXeTTKet(Bl+2riJ|vk1FzqA*>*84dDsmvW1QRfrJ~vqhy>R)RRS9 zkSpEy5GmY(Le2V|>39fo-$#Dgf~3-kdto&i%XejE_>Ps~3drC(+qZe&;{(98o7it< zW!Oo&v0>jGWDFa^dFLWFY~4nR*znd?a*_=zw4@F~Y4K(}`UTYPX+4`Sd;-^;eo}r3 zaxH&z6ii+bk$w*n-P%C<uwiQ<8PA3uge+pizH3S0_sBP_5&0Rr=TD}2^RtB``A5~! z^_n*=Pv0}z3EMjzWiyA%2+e)>&HZV%FIC=tAEUjwg*^Wr$pV&^u$Kn?)ZBN?v;88T z5`)r}-1;ZipCQ+WWbiiF#M(zrZv*YwL%Nk9DX}jBW2a-EC(|eQkqsp%8&;HFrDzjc zO)5)KSbKAl2gcx-xr5u4oDn_wzKBsSeaLP}Kx^J9qd&&=c|fFPXn?oJK8M={X*u6u z(;gH*nV~$&ZSNN)NYw1Av*CBwr;PS8Va6i|QdEY--aiPg7kyKhmXla8<pA7(sCWML zHM^(09Cq%Ht%W^FYNC!@Ekk}mx5|3|wNMX>QRU`)?D4-_)%6i`Pe_hIw&uDe?BPNE zCcy&(y^;8AN1p8#n@!@*Ke_b}oRB`1fTtrCWG^5I+ff%XaXWG*yS5{r&J#BT<_hl@ zP2r^EpJk<d(#YM!j8$`&X1H9q98zWwdIuWnKb>TOZOlr3&X3NhC)qUReM0beVdG!y z#a4F7b(p1QO(hF<AWw_t$A3y#mOg?fWZMqZ(>IYE1bSvBEFYIv!d<#osvRasaaBl5 z4zZ1;hWL|J#6*X@Q4<N!p^zS7ukJ3a`0`{Hu09&L<^KV0J(r`&*n)_R7$jFmNkBd@ zAGe7{hkEn63vQ5F9cs@X&eXc^CoMWyF2<3xoygC<dK=T+qG1>J$nY@+yS%rmVm&?h zsx+46@#k-2G&gUMqMhKN@`!X7nhE>9t9PM==sB_64aTrp-+4D^54x^Dwg)YvY3s-O zKKoEFL<j#)mh4Bp=(u0WvHd8MesWPCaR6l^+IqA8%t3S&*$1yx4^ef<e7*KieWq12 zIL6A8t@>Su(Qukx`p3GXaK6#<P#=601%U7NI~j8fouO}DB+?&X9G*O{@Am^5h%8$U z#KQdmm?f1<50Q1p;oQR=vOkUvBienve(6axhc;QkAUV-MGU8YTYv&oilK3hx=PQ~F z|G7Y)R|Q+!UE^URsiDk-c0jAdlCBg<Vduh>aqu~*V)+G0nPG3^`Y}?da5!bo=cw7k z$=M%Khpw^zHR*^il3s0-bTA1xg+jUxaO$7ouSj}jvP_bJ@wzZd0_DOkN?bSMq_Da` zN<oUlr5D&Yhf^e(eT#}B1*g!rVW-&t81rjQMvP<{y8$w-hh#d0Js<YZ8Q7EG6)|Zu zHozy`AIGaPE_gkWokp?Uo~#EKyso8s`1+ZDeZC*GbrHRo-4Bu^vC5bvVdD1?Bsc5_ zrCr`c%1<MI?;xmd<Hs$xCd9Rczf1?;#u8{JSU?;kZ%?DJ4&}5)qfn>pC&$Q|Q=gAY zE=iNm8e@X!J!E_}>WX~H>T1;2+11b}^Iy?n0<#FQKvKzO3!PCy9#o_DR&K|jB(_K3 z-r80oJ%hFe_4z@U>D<5xt$xKs+<gAIZ8VNcXj#~|ve!R3gY1MvE2L%XYSBP@^Py=; zGrsB<Bc11)80?#9PQjmkd@ZsR4#}L-(mDq)8Q>0J-&tHsX=$wmupx^j5O)Kx0B{n( zzOyI?Yz3?Vz=tcpqkQMB^IBRf0Z#zZ`7N#E04DG}N<a1w)I@irm$bC9a59GDAe;pV z1oSh;?O)9C?AL$$7nq6kKI>arV*vvIlL6xaqX6-Mi-j$%1rTNdlz>HmIe-~}DS-VO zT3UAjN&#BH2EZD?3P28E8DI%u9v}@c4Uh<!02l)pzM&<(bpwHf|1+fjFAImV)Bk5R z=PI7gHvM4eizq+@ARG`12nGZIya28MM}P=m4ln_<K>xo6{0(>nXav*&{s8;}xC}T8 z_z`d%a2QYy&;hmqHUkRRL;tUahayn4bpQ<@50D913RnP`1vm)U1K1AO3aA7e1DpYL zhV(%IKY$eA0<Z^I1Iz$4;63oY1T+I40PX^A0<ITA|6hd%%Z)9qrT_}?7Han#@C0xV za0hS$a1BrkI1e}tI0-lc=ne%0t%cFJ284i63UC3~1FQjN02=Tf=q~}yfCqrPfSc*? za~*IMa1n3@Pzg8&I0)DS*bdkVC<3elXaISDOu$mW0>Bs$A`vhFumms<kOr6rXoL!u zLZ}670IUJ50OSDZW$VeF+i-w#@g@pLKM2b3KScWfvT!IHl;M9^`__YQ(EQ1uQ3-%? zfDwQ=z(7F14P-?fN*;9>I<p+018f6q1{4BT0}23_0cQa}0*(Xz0Q>@|0}O@^j{-yh z!U3UxU_iin5_=m3y7+<iO93tbdw?~-3_x!rD{dn%zu~|e1)4ku6fOe7a6l*^7!UyP z0=TXv*KVT;MAV4pq&t2O8Va}ucn|Q}210EGg8^6r&zk`{z(K$@z&t=6pt%$b3;?b( zwFUvA0OJ645I0!|6A>T_umP|YPzksLxV8iMc7pJ`VA9*w(yes_JbVMl1QY=t0g;06 zHNbjzOREo{8(;unIN&<OH<BJrC_jC~VHp00IBfXrnT`9L#=dVg(&Inl)4}sLfZzWQ zmi>RzlK+*E&2aO7N$`2x<<IDB=KoEA&kJ3q-*FG6n7I|vt*r@wtAM)z_Pq`;0#K>% z_!JE?@huN%ZPfw}0_uz*8}~U5>NmYWv(0;p*0;8r155xdg{`g1{}-(NmiC@-7E=5O zI1V@rC<o{O+W?yZ@sM*YU;v;Gpc3N#@3Hm??d5I;$!NfPNcs}c40r&z3%FSb7pdrR z>A~98Ru{ngVu%Br1Z)N@1xx_+0k{L+Z*Fb<18@Ye4v@SBL<4vMD8Nm~<2YagU;*GJ z#8m>e0x|*b0dpW-1fV<6#{pPhvI)`c`KXTA3g~G1-gEsp0e#pE1rS>iEphH{%lexX z<z!}J2ziEIiV7l;ELrEoy+^bmmy($*InIqEE8CER$QBVD?W`Hdkt^Gfw-RIvsW%Vj z$jxoYe~?Z<hW))5TRIwcxmUqk%<$<Pa}NAVH}>zNpytMSO`G_2(>VUZHt~++DO+~0 zsT}!88?qhov;%UWG4<It<a=ZoORh5(dc6(#N0PykPr|1Z?BBgM<TSF6CC5i|<kxM; z!Q?heo@^A&4F203`}eV?D~PQa$Pq6Lv6NFAvO9@n$=9cF<bXEhmt>}xW>7epWDi_Z zW^!ED+i+bZt5~vonz0OvFGa{BRrYiUWbzcaTpk#?D%)^TGl`c29gI$rVGhub1Ibhe zI>b2-K6+vQG?riH8ASF07fsF5pLd|oAhe&Xl7I+naya%QR$sDTCRHr?fsx$YhWrb8 z%90P~aj65Wzf8T3csc>O*+@=kL!L&4vE==5?!*2SwjpPc43>PpfFswoArB|}ShCl0 zj%;T0W!a(RHcOsoBuBI%HxgTCAjhxZQZHyjo<|~CvgJyST;7IUL}s$&BS!MQHsofq zg(XkcaH*xDFGZU|eqzaeR&(SrZOG*Wu1P@VMzW?2SxGvv<g3^yS{w39GKnP*KFX0R z+mIKKRV?`zBl&t8@)}abl8+n7_u7yv^-o>sSI9Z-Z!Y)lc3;*kjUYEj{*gSe4S5^s z^f`G<8*&<%^f`ITzsRdTColLHx$1Lr&cDb{KPMOdi|pzCndIC5MIQD!`N+S>8K0BS z{)@ctbMp0nk#B!azV|P(t;c7gz5W+D@^iA8xNVorWXbQ1J?8W;@|Mq22mFitlLtK! zj$b=T!D?J9(Z@>ZZpiukE3W_R+OVJ2ulJ-q5TeLYFM2-eM>=`KoRUcvLO{QLM}G9C zqu`2}&<A3dl40=w4B%vKANyuP-uOU@;bf*Sn7jhA#TSG={=ul@j$hVk8~KSP*S2uv zdu_;x1P(4FF>5H!@WcOuHSm43>}QSfW7@>`1G(A1DQ)7vA(J3GXD6B?Yuk_ql2vSJ z%Xl35dK>a{V>|`_beaA8D8+cVw$J{F+Qe(fQ?@k9lp_ypLq146{psJFuL(Kwkv8OU z#5RBqq3;PvzW|sM&XAb_kl6xDjyu8mOBuBPr;GCmZ7K-DI2oZ-u$oBGZqWKFRg6i~ zzR)y@1!+9gdZ}$usV9vm_2|K3@t`Mr5Va5L7d(gsv06NoQj9%m#6u4v)Ptyqv_(Wg z{1c>Szvphi-CQ2bZ|0lX%>*{Q-NiI9Cw-AY+|>6?KU6|aNo^Cx6OvZPnsjVFZpV(J znqa1nO{mc=avp8_DRmhIo+OFf11~}9JaQnWV_SF;dHAW1{YO;WhaCGh<3H-ym#|7X zS=)@QNlAy#LrJ;FSMiHUc^3bi(`tP?>@06{2vX9oxa;v>_A3sb#qSiCO=|^>pF8+5 zhC5_T1qS!+wADSVcCdZhQA~5(JOrJVpK5+UYyEPZZ=uvFGi>XPUF`KU819l8wKAfe z-?iGx^N9I!I9|umT{ix692a~JtIh`;9>ucHx(6psDGuI;QicT|#H9?E33JA4)U%V^ zs8lm@G!l7@ST`H!jdAnZY_|p!x#iVgH~6fB?*_VtvSar!-Oah<?=;I&2BET?l?Nzg zWh}lhrDIO>Ciwq*=F@G|vi$PDPGNTs`|^YFd+PXI$GIN4srt)WW}@Ae;X*ve0o7Mz za}IumA~83^p3Jd-C+=&JmOZw}y0LqXeZkm=9D5^{!=xh*&Cnd&3cjb<u2QoammOS& zAL52b8ZSAx8Kn@Pc%gCQUc1&Om<;iqmm23C+>9zQ<SVvq4uXR|~R5_`l^^&?}Xo Jcf*(Oe*q)PI|Kj# diff --git a/roms/opensbi b/roms/opensbi index 455de672dd..43cace6c36 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit 455de672dd7c2aa1992df54dfb08dc11abbc1b1a +Subproject commit 43cace6c3671e5172d0df0a8963e552bb04b7b20 From 40a0815e311fb160e6208fb17aef91d3f82f32e7 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 16:50:51 +0200 Subject: [PATCH 2687/2884] hw/intc/loongson_ipi: Rename LoongsonIPI -> LoongsonIPIState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll have to add LoongsonIPIClass in few commits, so rename LoongsonIPI as LoongsonIPIState for clarity. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-2-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 16 ++++++++-------- include/hw/intc/loongson_ipi.h | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 682cec96f3..903483ae80 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -64,7 +64,7 @@ static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - LoongsonIPI *ipi = opaque; + LoongsonIPIState *ipi = opaque; IPICore *s; if (attrs.requester_id >= ipi->num_cpu) { @@ -160,7 +160,7 @@ static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, MemTxAttrs attrs) { IPICore *s = opaque; - LoongsonIPI *ipi = s->ipi; + LoongsonIPIState *ipi = s->ipi; int index = 0; uint32_t cpuid; uint8_t vector; @@ -214,7 +214,7 @@ static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size, MemTxAttrs attrs) { - LoongsonIPI *ipi = opaque; + LoongsonIPIState *ipi = opaque; IPICore *s; if (attrs.requester_id >= ipi->num_cpu) { @@ -277,7 +277,7 @@ static const MemoryRegionOps loongson_ipi64_ops = { static void loongson_ipi_realize(DeviceState *dev, Error **errp) { - LoongsonIPI *s = LOONGSON_IPI(dev); + LoongsonIPIState *s = LOONGSON_IPI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int i; @@ -320,7 +320,7 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) static void loongson_ipi_unrealize(DeviceState *dev) { - LoongsonIPI *s = LOONGSON_IPI(dev); + LoongsonIPIState *s = LOONGSON_IPI(dev); g_free(s->cpu); } @@ -344,14 +344,14 @@ static const VMStateDescription vmstate_loongson_ipi = { .version_id = 2, .minimum_version_id = 2, .fields = (const VMStateField[]) { - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPI, num_cpu, + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPIState, num_cpu, vmstate_ipi_core, IPICore), VMSTATE_END_OF_LIST() } }; static Property ipi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongsonIPI, num_cpu, 1), + DEFINE_PROP_UINT32("num-cpu", LoongsonIPIState, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; @@ -369,7 +369,7 @@ static const TypeInfo loongson_ipi_types[] = { { .name = TYPE_LOONGSON_IPI, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LoongsonIPI), + .instance_size = sizeof(LoongsonIPIState), .class_init = loongson_ipi_class_init, } }; diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h index 3f795edbf3..efb772f384 100644 --- a/include/hw/intc/loongson_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -31,10 +31,10 @@ #define IPI_MBX_NUM 4 #define TYPE_LOONGSON_IPI "loongson_ipi" -OBJECT_DECLARE_SIMPLE_TYPE(LoongsonIPI, LOONGSON_IPI) +OBJECT_DECLARE_SIMPLE_TYPE(LoongsonIPIState, LOONGSON_IPI) typedef struct IPICore { - LoongsonIPI *ipi; + LoongsonIPIState *ipi; MemoryRegion *ipi_mmio_mem; uint32_t status; uint32_t en; @@ -45,7 +45,7 @@ typedef struct IPICore { qemu_irq irq; } IPICore; -struct LoongsonIPI { +struct LoongsonIPIState { SysBusDevice parent_obj; MemoryRegion ipi_iocsr_mem; MemoryRegion ipi64_iocsr_mem; From 530e6daf74bf506ee8133a490722b5f24aa54744 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 16:57:36 +0200 Subject: [PATCH 2688/2884] hw/intc/loongson_ipi: Extract loongson_ipi_common_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to extract common IPI code in few commits, extract loongson_ipi_common_realize(). Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-3-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 903483ae80..8aab7e48e8 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -275,7 +275,7 @@ static const MemoryRegionOps loongson_ipi64_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void loongson_ipi_realize(DeviceState *dev, Error **errp) +static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) { LoongsonIPIState *s = LOONGSON_IPI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); @@ -301,30 +301,46 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); s->cpu = g_new0(IPICore, s->num_cpu); - if (s->cpu == NULL) { - error_setg(errp, "Memory allocation for IPICore faile"); - return; - } - for (i = 0; i < s->num_cpu; i++) { s->cpu[i].ipi = s; - s->cpu[i].ipi_mmio_mem = g_new0(MemoryRegion, 1); - g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); - memory_region_init_io(s->cpu[i].ipi_mmio_mem, OBJECT(dev), - &loongson_ipi_core_ops, &s->cpu[i], name, 0x48); - sysbus_init_mmio(sbd, s->cpu[i].ipi_mmio_mem); qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); } } -static void loongson_ipi_unrealize(DeviceState *dev) +static void loongson_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongsonIPIState *s = LOONGSON_IPI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; + + loongson_ipi_common_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + for (unsigned i = 0; i < s->num_cpu; i++) { + s->cpu[i].ipi_mmio_mem = g_new0(MemoryRegion, 1); + g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); + memory_region_init_io(s->cpu[i].ipi_mmio_mem, OBJECT(dev), + &loongson_ipi_core_ops, &s->cpu[i], name, 0x48); + sysbus_init_mmio(sbd, s->cpu[i].ipi_mmio_mem); + } +} + +static void loongson_ipi_common_unrealize(DeviceState *dev) { LoongsonIPIState *s = LOONGSON_IPI(dev); g_free(s->cpu); } +static void loongson_ipi_unrealize(DeviceState *dev) +{ + loongson_ipi_common_unrealize(dev); +} + static const VMStateDescription vmstate_ipi_core = { .name = "ipi-single", .version_id = 2, From 7e555781e4b681becf79ad4f89cfb66a43f8a9d0 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Tue, 23 Jul 2024 11:25:53 +0200 Subject: [PATCH 2689/2884] hw/intc/loongson_ipi: Add TYPE_LOONGSON_IPI_COMMON stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce LOONGSON_IPI_COMMON stubs, QDev parent of LOONGSON_IPI. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-4-philmd@linaro.org> --- MAINTAINERS | 4 ++++ hw/intc/Kconfig | 4 ++++ hw/intc/loongson_ipi.c | 10 +++++++--- hw/intc/loongson_ipi_common.c | 22 ++++++++++++++++++++++ hw/intc/meson.build | 1 + include/hw/intc/loongson_ipi.h | 14 ++++++++++++-- include/hw/intc/loongson_ipi_common.h | 26 ++++++++++++++++++++++++++ 7 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 hw/intc/loongson_ipi_common.c create mode 100644 include/hw/intc/loongson_ipi_common.h diff --git a/MAINTAINERS b/MAINTAINERS index e34c2bd4cd..5ca701cf0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1250,8 +1250,10 @@ F: configs/devices/loongarch64-softmmu/default.mak F: hw/loongarch/ F: include/hw/loongarch/virt.h F: include/hw/intc/loongarch_*.h +F: include/hw/intc/loongson_ipi_common.h F: include/hw/intc/loongson_ipi.h F: hw/intc/loongarch_*.c +F: hw/intc/loongson_ipi_common.c F: hw/intc/loongson_ipi.c F: include/hw/pci-host/ls7a.h F: hw/rtc/ls7a_rtc.c @@ -1386,11 +1388,13 @@ Loongson-3 virtual platforms M: Huacai Chen <chenhuacai@kernel.org> R: Jiaxun Yang <jiaxun.yang@flygoat.com> S: Maintained +F: hw/intc/loongson_ipi_common.c F: hw/intc/loongson_ipi.c F: hw/intc/loongson_liointc.c F: hw/mips/loongson3_bootp.c F: hw/mips/loongson3_bootp.h F: hw/mips/loongson3_virt.c +F: include/hw/intc/loongson_ipi_common.h F: include/hw/intc/loongson_ipi.h F: include/hw/intc/loongson_liointc.h F: tests/avocado/machine_mips_loongson3v.py diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 58b6d3a710..a2a0fdca85 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -87,8 +87,12 @@ config GOLDFISH_PIC config M68K_IRQC bool +config LOONGSON_IPI_COMMON + bool + config LOONGSON_IPI bool + select LOONGSON_IPI_COMMON config LOONGARCH_PCH_PIC bool diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 8aab7e48e8..7d15c28e94 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -374,9 +374,12 @@ static Property ipi_properties[] = { static void loongson_ipi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + LoongsonIPIClass *lic = LOONGSON_IPI_CLASS(klass); - dc->realize = loongson_ipi_realize; - dc->unrealize = loongson_ipi_unrealize; + device_class_set_parent_realize(dc, loongson_ipi_realize, + &lic->parent_realize); + device_class_set_parent_unrealize(dc, loongson_ipi_unrealize, + &lic->parent_unrealize); device_class_set_props(dc, ipi_properties); dc->vmsd = &vmstate_loongson_ipi; } @@ -384,8 +387,9 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) static const TypeInfo loongson_ipi_types[] = { { .name = TYPE_LOONGSON_IPI, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_LOONGSON_IPI_COMMON, .instance_size = sizeof(LoongsonIPIState), + .class_size = sizeof(LoongsonIPIClass), .class_init = loongson_ipi_class_init, } }; diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c new file mode 100644 index 0000000000..43002fe556 --- /dev/null +++ b/hw/intc/loongson_ipi_common.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson IPI interrupt common support + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/intc/loongson_ipi_common.h" + +static const TypeInfo loongarch_ipi_common_types[] = { + { + .name = TYPE_LOONGSON_IPI_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongsonIPICommonState), + .class_size = sizeof(LoongsonIPICommonClass), + .abstract = true, + } +}; + +DEFINE_TYPES(loongarch_ipi_common_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index afd1aa51ee..a09a527207 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -69,6 +69,7 @@ specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) +specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h index efb772f384..9c9030761e 100644 --- a/include/hw/intc/loongson_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -8,6 +8,8 @@ #ifndef HW_LOONGSON_IPI_H #define HW_LOONGSON_IPI_H +#include "qom/object.h" +#include "hw/intc/loongson_ipi_common.h" #include "hw/sysbus.h" /* Mainy used by iocsr read and write */ @@ -31,7 +33,7 @@ #define IPI_MBX_NUM 4 #define TYPE_LOONGSON_IPI "loongson_ipi" -OBJECT_DECLARE_SIMPLE_TYPE(LoongsonIPIState, LOONGSON_IPI) +OBJECT_DECLARE_TYPE(LoongsonIPIState, LoongsonIPIClass, LOONGSON_IPI) typedef struct IPICore { LoongsonIPIState *ipi; @@ -45,8 +47,16 @@ typedef struct IPICore { qemu_irq irq; } IPICore; +struct LoongsonIPIClass { + LoongsonIPICommonClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; +}; + struct LoongsonIPIState { - SysBusDevice parent_obj; + LoongsonIPICommonState parent_obj; + MemoryRegion ipi_iocsr_mem; MemoryRegion ipi64_iocsr_mem; uint32_t num_cpu; diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h new file mode 100644 index 0000000000..70ac69d0ba --- /dev/null +++ b/include/hw/intc/loongson_ipi_common.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson ipi interrupt header files + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGSON_IPI_COMMON_H +#define HW_LOONGSON_IPI_COMMON_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common" +OBJECT_DECLARE_TYPE(LoongsonIPICommonState, + LoongsonIPICommonClass, LOONGSON_IPI_COMMON) + +struct LoongsonIPICommonState { + SysBusDevice parent_obj; +}; + +struct LoongsonIPICommonClass { + SysBusDeviceClass parent_class; +}; + +#endif From 2252e6c94e34c2373e8d16573284a00541e3c62c Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 18:46:21 +0200 Subject: [PATCH 2690/2884] hw/intc/loongson_ipi: Move common definitions to loongson_ipi_common.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-5-philmd@linaro.org> --- include/hw/intc/loongson_ipi.h | 18 ------------------ include/hw/intc/loongson_ipi_common.h | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h index 9c9030761e..70e00dc1a7 100644 --- a/include/hw/intc/loongson_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -12,24 +12,6 @@ #include "hw/intc/loongson_ipi_common.h" #include "hw/sysbus.h" -/* Mainy used by iocsr read and write */ -#define SMP_IPI_MAILBOX 0x1000ULL -#define CORE_STATUS_OFF 0x0 -#define CORE_EN_OFF 0x4 -#define CORE_SET_OFF 0x8 -#define CORE_CLEAR_OFF 0xc -#define CORE_BUF_20 0x20 -#define CORE_BUF_28 0x28 -#define CORE_BUF_30 0x30 -#define CORE_BUF_38 0x38 -#define IOCSR_IPI_SEND 0x40 -#define IOCSR_MAIL_SEND 0x48 -#define IOCSR_ANY_SEND 0x158 - -#define MAIL_SEND_ADDR (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND) -#define MAIL_SEND_OFFSET 0 -#define ANY_SEND_OFFSET (IOCSR_ANY_SEND - IOCSR_MAIL_SEND) - #define IPI_MBX_NUM 4 #define TYPE_LOONGSON_IPI "loongson_ipi" diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index 70ac69d0ba..b43b77bda6 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -23,4 +23,23 @@ struct LoongsonIPICommonClass { SysBusDeviceClass parent_class; }; +/* Mainy used by iocsr read and write */ +#define SMP_IPI_MAILBOX 0x1000ULL + +#define CORE_STATUS_OFF 0x0 +#define CORE_EN_OFF 0x4 +#define CORE_SET_OFF 0x8 +#define CORE_CLEAR_OFF 0xc +#define CORE_BUF_20 0x20 +#define CORE_BUF_28 0x28 +#define CORE_BUF_30 0x30 +#define CORE_BUF_38 0x38 +#define IOCSR_IPI_SEND 0x40 +#define IOCSR_MAIL_SEND 0x48 +#define IOCSR_ANY_SEND 0x158 + +#define MAIL_SEND_ADDR (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND) +#define MAIL_SEND_OFFSET 0 +#define ANY_SEND_OFFSET (IOCSR_ANY_SEND - IOCSR_MAIL_SEND) + #endif From a022e0de53579da9df20945de70a82957258a972 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 16:58:55 +0200 Subject: [PATCH 2691/2884] hw/intc/loongson_ipi: Move IPICore::mmio_mem to LoongsonIPIState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is easier to manage one array of MMIO MR rather than one per vCPU. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-6-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 10 +++++++--- include/hw/intc/loongson_ipi.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 7d15c28e94..03878b896f 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -320,12 +320,12 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) return; } + s->ipi_mmio_mem = g_new0(MemoryRegion, s->num_cpu); for (unsigned i = 0; i < s->num_cpu; i++) { - s->cpu[i].ipi_mmio_mem = g_new0(MemoryRegion, 1); g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); - memory_region_init_io(s->cpu[i].ipi_mmio_mem, OBJECT(dev), + memory_region_init_io(&s->ipi_mmio_mem[i], OBJECT(dev), &loongson_ipi_core_ops, &s->cpu[i], name, 0x48); - sysbus_init_mmio(sbd, s->cpu[i].ipi_mmio_mem); + sysbus_init_mmio(sbd, &s->ipi_mmio_mem[i]); } } @@ -338,6 +338,10 @@ static void loongson_ipi_common_unrealize(DeviceState *dev) static void loongson_ipi_unrealize(DeviceState *dev) { + LoongsonIPIState *s = LOONGSON_IPI(dev); + + g_free(s->ipi_mmio_mem); + loongson_ipi_common_unrealize(dev); } diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h index 70e00dc1a7..5a52dfbf4d 100644 --- a/include/hw/intc/loongson_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -19,7 +19,6 @@ OBJECT_DECLARE_TYPE(LoongsonIPIState, LoongsonIPIClass, LOONGSON_IPI) typedef struct IPICore { LoongsonIPIState *ipi; - MemoryRegion *ipi_mmio_mem; uint32_t status; uint32_t en; uint32_t set; @@ -39,6 +38,7 @@ struct LoongsonIPIClass { struct LoongsonIPIState { LoongsonIPICommonState parent_obj; + MemoryRegion *ipi_mmio_mem; MemoryRegion ipi_iocsr_mem; MemoryRegion ipi64_iocsr_mem; uint32_t num_cpu; From 6c8698a5e4d65754addb6afa23c1c2f242593692 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Tue, 23 Jul 2024 11:55:41 +0200 Subject: [PATCH 2692/2884] hw/intc/loongson_ipi: Move IPICore structure to loongson_ipi_common.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the IPICore structure and corresponding common fields of LoongsonIPICommonState to "hw/intc/loongson_ipi_common.h". Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-7-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 50 ++++++--------------------- hw/intc/loongson_ipi_common.c | 42 ++++++++++++++++++++++ include/hw/intc/loongson_ipi.h | 17 --------- include/hw/intc/loongson_ipi_common.h | 18 ++++++++++ 4 files changed, 70 insertions(+), 57 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 03878b896f..347bc26729 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -64,7 +64,7 @@ static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - LoongsonIPIState *ipi = opaque; + LoongsonIPICommonState *ipi = opaque; IPICore *s; if (attrs.requester_id >= ipi->num_cpu) { @@ -160,7 +160,7 @@ static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, MemTxAttrs attrs) { IPICore *s = opaque; - LoongsonIPIState *ipi = s->ipi; + LoongsonIPICommonState *ipi = s->ipi; int index = 0; uint32_t cpuid; uint8_t vector; @@ -214,7 +214,7 @@ static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size, MemTxAttrs attrs) { - LoongsonIPIState *ipi = opaque; + LoongsonIPICommonState *ipi = opaque; IPICore *s; if (attrs.requester_id >= ipi->num_cpu) { @@ -277,7 +277,7 @@ static const MemoryRegionOps loongson_ipi64_ops = { static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) { - LoongsonIPIState *s = LOONGSON_IPI(dev); + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int i; @@ -310,6 +310,7 @@ static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) static void loongson_ipi_realize(DeviceState *dev, Error **errp) { + LoongsonIPICommonState *sc = LOONGSON_IPI_COMMON(dev); LoongsonIPIState *s = LOONGSON_IPI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; @@ -320,18 +321,19 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) return; } - s->ipi_mmio_mem = g_new0(MemoryRegion, s->num_cpu); - for (unsigned i = 0; i < s->num_cpu; i++) { + s->ipi_mmio_mem = g_new0(MemoryRegion, sc->num_cpu); + for (unsigned i = 0; i < sc->num_cpu; i++) { g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); + memory_region_init_io(&s->ipi_mmio_mem[i], OBJECT(dev), - &loongson_ipi_core_ops, &s->cpu[i], name, 0x48); + &loongson_ipi_core_ops, &sc->cpu[i], name, 0x48); sysbus_init_mmio(sbd, &s->ipi_mmio_mem[i]); } } static void loongson_ipi_common_unrealize(DeviceState *dev) { - LoongsonIPIState *s = LOONGSON_IPI(dev); + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); g_free(s->cpu); } @@ -345,36 +347,6 @@ static void loongson_ipi_unrealize(DeviceState *dev) loongson_ipi_common_unrealize(dev); } -static const VMStateDescription vmstate_ipi_core = { - .name = "ipi-single", - .version_id = 2, - .minimum_version_id = 2, - .fields = (const VMStateField[]) { - VMSTATE_UINT32(status, IPICore), - VMSTATE_UINT32(en, IPICore), - VMSTATE_UINT32(set, IPICore), - VMSTATE_UINT32(clear, IPICore), - VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_loongson_ipi = { - .name = TYPE_LOONGSON_IPI, - .version_id = 2, - .minimum_version_id = 2, - .fields = (const VMStateField[]) { - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPIState, num_cpu, - vmstate_ipi_core, IPICore), - VMSTATE_END_OF_LIST() - } -}; - -static Property ipi_properties[] = { - DEFINE_PROP_UINT32("num-cpu", LoongsonIPIState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; - static void loongson_ipi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -384,8 +356,6 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) &lic->parent_realize); device_class_set_parent_unrealize(dc, loongson_ipi_unrealize, &lic->parent_unrealize); - device_class_set_props(dc, ipi_properties); - dc->vmsd = &vmstate_loongson_ipi; } static const TypeInfo loongson_ipi_types[] = { diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index 43002fe556..47796f7ece 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -8,6 +8,47 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/intc/loongson_ipi_common.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +static const VMStateDescription vmstate_ipi_core = { + .name = "ipi-single", + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(status, IPICore), + VMSTATE_UINT32(en, IPICore), + VMSTATE_UINT32(set, IPICore), + VMSTATE_UINT32(clear, IPICore), + VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_loongson_ipi_common = { + .name = "loongson_ipi", + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState, + num_cpu, vmstate_ipi_core, + IPICore), + VMSTATE_END_OF_LIST() + } +}; + +static Property ipi_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, ipi_common_properties); + dc->vmsd = &vmstate_loongson_ipi_common; +} static const TypeInfo loongarch_ipi_common_types[] = { { @@ -15,6 +56,7 @@ static const TypeInfo loongarch_ipi_common_types[] = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongsonIPICommonState), .class_size = sizeof(LoongsonIPICommonClass), + .class_init = loongson_ipi_common_class_init, .abstract = true, } }; diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h index 5a52dfbf4d..4e517cc8dc 100644 --- a/include/hw/intc/loongson_ipi.h +++ b/include/hw/intc/loongson_ipi.h @@ -12,22 +12,9 @@ #include "hw/intc/loongson_ipi_common.h" #include "hw/sysbus.h" -#define IPI_MBX_NUM 4 - #define TYPE_LOONGSON_IPI "loongson_ipi" OBJECT_DECLARE_TYPE(LoongsonIPIState, LoongsonIPIClass, LOONGSON_IPI) -typedef struct IPICore { - LoongsonIPIState *ipi; - uint32_t status; - uint32_t en; - uint32_t set; - uint32_t clear; - /* 64bit buf divide into 2 32bit buf */ - uint32_t buf[IPI_MBX_NUM * 2]; - qemu_irq irq; -} IPICore; - struct LoongsonIPIClass { LoongsonIPICommonClass parent_class; @@ -39,10 +26,6 @@ struct LoongsonIPIState { LoongsonIPICommonState parent_obj; MemoryRegion *ipi_mmio_mem; - MemoryRegion ipi_iocsr_mem; - MemoryRegion ipi64_iocsr_mem; - uint32_t num_cpu; - IPICore *cpu; }; #endif diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index b43b77bda6..967c70ad1c 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -11,12 +11,30 @@ #include "qom/object.h" #include "hw/sysbus.h" +#define IPI_MBX_NUM 4 + #define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common" OBJECT_DECLARE_TYPE(LoongsonIPICommonState, LoongsonIPICommonClass, LOONGSON_IPI_COMMON) +typedef struct IPICore { + LoongsonIPICommonState *ipi; + uint32_t status; + uint32_t en; + uint32_t set; + uint32_t clear; + /* 64bit buf divide into 2 32-bit buf */ + uint32_t buf[IPI_MBX_NUM * 2]; + qemu_irq irq; +} IPICore; + struct LoongsonIPICommonState { SysBusDevice parent_obj; + + MemoryRegion ipi_iocsr_mem; + MemoryRegion ipi64_iocsr_mem; + uint32_t num_cpu; + IPICore *cpu; }; struct LoongsonIPICommonClass { From ed722e0ec4eb275a7710e7af518a0063e75b054f Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 17:40:50 +0200 Subject: [PATCH 2693/2884] hw/intc/loongson_ipi: Pass LoongsonIPICommonState to send_ipi_data() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to get LoongsonIPICommonClass in send_ipi_data() in the next commit, propagate LoongsonIPICommonState. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-8-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 347bc26729..8bf16f26d4 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -88,8 +88,8 @@ static AddressSpace *get_cpu_iocsr_as(CPUState *cpu) return NULL; } -static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr, - MemTxAttrs attrs) +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, + uint64_t val, hwaddr addr, MemTxAttrs attrs) { int i, mask = 0, data = 0; AddressSpace *iocsr_as = get_cpu_iocsr_as(cpu); @@ -119,7 +119,8 @@ static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr, return MEMTX_OK; } -static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) +static MemTxResult mail_send(LoongsonIPICommonState *ipi, + uint64_t val, MemTxAttrs attrs) { uint32_t cpuid; hwaddr addr; @@ -134,10 +135,11 @@ static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) /* override requester_id */ addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); attrs.requester_id = cs->cpu_index; - return send_ipi_data(cs, val, addr, attrs); + return send_ipi_data(ipi, cs, val, addr, attrs); } -static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) +static MemTxResult any_send(LoongsonIPICommonState *ipi, + uint64_t val, MemTxAttrs attrs) { uint32_t cpuid; hwaddr addr; @@ -152,7 +154,7 @@ static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) /* override requester_id */ addr = val & 0xffff; attrs.requester_id = cs->cpu_index; - return send_ipi_data(cs, val, addr, attrs); + return send_ipi_data(ipi, cs, val, addr, attrs); } static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, @@ -249,15 +251,16 @@ static const MemoryRegionOps loongson_ipi_iocsr_ops = { static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, unsigned size, MemTxAttrs attrs) { + LoongsonIPICommonState *ipi = opaque; MemTxResult ret = MEMTX_OK; addr &= 0xfff; switch (addr) { case MAIL_SEND_OFFSET: - ret = mail_send(val, attrs); + ret = mail_send(ipi, val, attrs); break; case ANY_SEND_OFFSET: - ret = any_send(val, attrs); + ret = any_send(ipi, val, attrs); break; default: break; From a81cd679d70b8c954c17d23a7bd28e7c90fb366e Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 17:50:05 +0200 Subject: [PATCH 2694/2884] hw/intc/loongson_ipi: Add LoongsonIPICommonClass::get_iocsr_as handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow Loongson IPI implementations to have their own get_iocsr_as() handler. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-9-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 16 ++++++++++++---- include/hw/intc/loongson_ipi_common.h | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 8bf16f26d4..eb99de9068 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -75,24 +75,30 @@ static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, return loongson_ipi_core_readl(s, addr, data, size, attrs); } -static AddressSpace *get_cpu_iocsr_as(CPUState *cpu) -{ #ifdef TARGET_LOONGARCH64 +static AddressSpace *get_iocsr_as(CPUState *cpu) +{ return LOONGARCH_CPU(cpu)->env.address_space_iocsr; +} #endif + #ifdef TARGET_MIPS +static AddressSpace *get_iocsr_as(CPUState *cpu) +{ if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) { return &MIPS_CPU(cpu)->env.iocsr.as; } -#endif + return NULL; } +#endif static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, uint64_t val, hwaddr addr, MemTxAttrs attrs) { + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); int i, mask = 0, data = 0; - AddressSpace *iocsr_as = get_cpu_iocsr_as(cpu); + AddressSpace *iocsr_as = licc->get_iocsr_as(cpu); if (!iocsr_as) { return MEMTX_DECODE_ERROR; @@ -354,11 +360,13 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongsonIPIClass *lic = LOONGSON_IPI_CLASS(klass); + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); device_class_set_parent_realize(dc, loongson_ipi_realize, &lic->parent_realize); device_class_set_parent_unrealize(dc, loongson_ipi_unrealize, &lic->parent_unrealize); + licc->get_iocsr_as = get_iocsr_as; } static const TypeInfo loongson_ipi_types[] = { diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index 967c70ad1c..1a2ee41cc9 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -39,6 +39,8 @@ struct LoongsonIPICommonState { struct LoongsonIPICommonClass { SysBusDeviceClass parent_class; + + AddressSpace *(*get_iocsr_as)(CPUState *cpu); }; /* Mainy used by iocsr read and write */ From 8f4f38fd2a6cee5c3a9aaa5d8c78b3b7e456e5e8 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 18:24:02 +0200 Subject: [PATCH 2695/2884] hw/intc/loongson_ipi: Add LoongsonIPICommonClass::cpu_by_arch_id handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow Loongson IPI implementations to have their own cpu_by_arch_id() handler. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-10-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 10 +++++++--- include/hw/intc/loongson_ipi_common.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index eb99de9068..4a8e743528 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -128,12 +128,13 @@ static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val, MemTxAttrs attrs) { + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); uint32_t cpuid; hwaddr addr; CPUState *cs; cpuid = extract32(val, 16, 10); - cs = cpu_by_arch_id(cpuid); + cs = licc->cpu_by_arch_id(cpuid); if (cs == NULL) { return MEMTX_DECODE_ERROR; } @@ -147,12 +148,13 @@ static MemTxResult mail_send(LoongsonIPICommonState *ipi, static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val, MemTxAttrs attrs) { + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); uint32_t cpuid; hwaddr addr; CPUState *cs; cpuid = extract32(val, 16, 10); - cs = cpu_by_arch_id(cpuid); + cs = licc->cpu_by_arch_id(cpuid); if (cs == NULL) { return MEMTX_DECODE_ERROR; } @@ -169,6 +171,7 @@ static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, { IPICore *s = opaque; LoongsonIPICommonState *ipi = s->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); int index = 0; uint32_t cpuid; uint8_t vector; @@ -203,7 +206,7 @@ static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, cpuid = extract32(val, 16, 10); /* IPI status vector */ vector = extract8(val, 0, 5); - cs = cpu_by_arch_id(cpuid); + cs = licc->cpu_by_arch_id(cpuid); if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { return MEMTX_DECODE_ERROR; } @@ -367,6 +370,7 @@ static void loongson_ipi_class_init(ObjectClass *klass, void *data) device_class_set_parent_unrealize(dc, loongson_ipi_unrealize, &lic->parent_unrealize); licc->get_iocsr_as = get_iocsr_as; + licc->cpu_by_arch_id = cpu_by_arch_id; } static const TypeInfo loongson_ipi_types[] = { diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index 1a2ee41cc9..8997676f0d 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -41,6 +41,7 @@ struct LoongsonIPICommonClass { SysBusDeviceClass parent_class; AddressSpace *(*get_iocsr_as)(CPUState *cpu); + CPUState *(*cpu_by_arch_id)(int64_t id); }; /* Mainy used by iocsr read and write */ From 2aca564e678ff258480aef644296de2cafb8890f Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 18:51:31 +0200 Subject: [PATCH 2696/2884] hw/intc/loongson_ipi: Expose loongson_ipi_core_read/write helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to access loongson_ipi_core_read/write helpers from loongson_ipi_common.c in the next commit, make their prototype declaration public. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-11-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 10 ++++------ include/hw/intc/loongson_ipi_common.h | 6 ++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 4a8e743528..c13cb5a1d2 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -24,9 +24,8 @@ #endif #include "trace.h" -static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, - uint64_t *data, - unsigned size, MemTxAttrs attrs) +MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) { IPICore *s = opaque; uint64_t ret = 0; @@ -165,9 +164,8 @@ static MemTxResult any_send(LoongsonIPICommonState *ipi, return send_ipi_data(ipi, cs, val, addr, attrs); } -static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size, - MemTxAttrs attrs) +MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) { IPICore *s = opaque; LoongsonIPICommonState *ipi = s->ipi; diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index 8997676f0d..65f8ef7957 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -10,6 +10,7 @@ #include "qom/object.h" #include "hw/sysbus.h" +#include "exec/memattrs.h" #define IPI_MBX_NUM 4 @@ -44,6 +45,11 @@ struct LoongsonIPICommonClass { CPUState *(*cpu_by_arch_id)(int64_t id); }; +MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs); +MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs); + /* Mainy used by iocsr read and write */ #define SMP_IPI_MAILBOX 0x1000ULL From ec8595578fa2b526dba999c1fe0b87129f5b2c9a Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 18:42:13 +0200 Subject: [PATCH 2697/2884] hw/intc/loongson_ipi: Move common code to loongson_ipi_common.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the common code from loongson_ipi.c to loongson_ipi_common.c, call parent_realize() instead of loongson_ipi_common_realize() in loongson_ipi_realize(). Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-12-philmd@linaro.org> --- hw/intc/loongson_ipi.c | 279 +------------------------ hw/intc/loongson_ipi_common.c | 283 ++++++++++++++++++++++++++ include/hw/intc/loongson_ipi_common.h | 2 + 3 files changed, 289 insertions(+), 275 deletions(-) diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index c13cb5a1d2..0b88ae3230 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -24,56 +24,6 @@ #endif #include "trace.h" -MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, - unsigned size, MemTxAttrs attrs) -{ - IPICore *s = opaque; - uint64_t ret = 0; - int index = 0; - - addr &= 0xff; - switch (addr) { - case CORE_STATUS_OFF: - ret = s->status; - break; - case CORE_EN_OFF: - ret = s->en; - break; - case CORE_SET_OFF: - ret = 0; - break; - case CORE_CLEAR_OFF: - ret = 0; - break; - case CORE_BUF_20 ... CORE_BUF_38 + 4: - index = (addr - CORE_BUF_20) >> 2; - ret = s->buf[index]; - break; - default: - qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); - break; - } - - trace_loongson_ipi_read(size, (uint64_t)addr, ret); - *data = ret; - return MEMTX_OK; -} - -static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, - uint64_t *data, - unsigned size, MemTxAttrs attrs) -{ - LoongsonIPICommonState *ipi = opaque; - IPICore *s; - - if (attrs.requester_id >= ipi->num_cpu) { - return MEMTX_DECODE_ERROR; - } - - s = &ipi->cpu[attrs.requester_id]; - return loongson_ipi_core_readl(s, addr, data, size, attrs); -} - #ifdef TARGET_LOONGARCH64 static AddressSpace *get_iocsr_as(CPUState *cpu) { @@ -92,148 +42,6 @@ static AddressSpace *get_iocsr_as(CPUState *cpu) } #endif -static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, - uint64_t val, hwaddr addr, MemTxAttrs attrs) -{ - LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); - int i, mask = 0, data = 0; - AddressSpace *iocsr_as = licc->get_iocsr_as(cpu); - - if (!iocsr_as) { - return MEMTX_DECODE_ERROR; - } - - /* - * bit 27-30 is mask for byte writing, - * if the mask is 0, we need not to do anything. - */ - if ((val >> 27) & 0xf) { - data = address_space_ldl_le(iocsr_as, addr, attrs, NULL); - for (i = 0; i < 4; i++) { - /* get mask for byte writing */ - if (val & (0x1 << (27 + i))) { - mask |= 0xff << (i * 8); - } - } - } - - data &= mask; - data |= (val >> 32) & ~mask; - address_space_stl_le(iocsr_as, addr, data, attrs, NULL); - - return MEMTX_OK; -} - -static MemTxResult mail_send(LoongsonIPICommonState *ipi, - uint64_t val, MemTxAttrs attrs) -{ - LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); - uint32_t cpuid; - hwaddr addr; - CPUState *cs; - - cpuid = extract32(val, 16, 10); - cs = licc->cpu_by_arch_id(cpuid); - if (cs == NULL) { - return MEMTX_DECODE_ERROR; - } - - /* override requester_id */ - addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); - attrs.requester_id = cs->cpu_index; - return send_ipi_data(ipi, cs, val, addr, attrs); -} - -static MemTxResult any_send(LoongsonIPICommonState *ipi, - uint64_t val, MemTxAttrs attrs) -{ - LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); - uint32_t cpuid; - hwaddr addr; - CPUState *cs; - - cpuid = extract32(val, 16, 10); - cs = licc->cpu_by_arch_id(cpuid); - if (cs == NULL) { - return MEMTX_DECODE_ERROR; - } - - /* override requester_id */ - addr = val & 0xffff; - attrs.requester_id = cs->cpu_index; - return send_ipi_data(ipi, cs, val, addr, attrs); -} - -MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size, MemTxAttrs attrs) -{ - IPICore *s = opaque; - LoongsonIPICommonState *ipi = s->ipi; - LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); - int index = 0; - uint32_t cpuid; - uint8_t vector; - CPUState *cs; - - addr &= 0xff; - trace_loongson_ipi_write(size, (uint64_t)addr, val); - switch (addr) { - case CORE_STATUS_OFF: - qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); - break; - case CORE_EN_OFF: - s->en = val; - break; - case CORE_SET_OFF: - s->status |= val; - if (s->status != 0 && (s->status & s->en) != 0) { - qemu_irq_raise(s->irq); - } - break; - case CORE_CLEAR_OFF: - s->status &= ~val; - if (s->status == 0 && s->en != 0) { - qemu_irq_lower(s->irq); - } - break; - case CORE_BUF_20 ... CORE_BUF_38 + 4: - index = (addr - CORE_BUF_20) >> 2; - s->buf[index] = val; - break; - case IOCSR_IPI_SEND: - cpuid = extract32(val, 16, 10); - /* IPI status vector */ - vector = extract8(val, 0, 5); - cs = licc->cpu_by_arch_id(cpuid); - if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { - return MEMTX_DECODE_ERROR; - } - loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF, - BIT(vector), 4, attrs); - break; - default: - qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); - break; - } - - return MEMTX_OK; -} - -static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size, - MemTxAttrs attrs) -{ - LoongsonIPICommonState *ipi = opaque; - IPICore *s; - - if (attrs.requester_id >= ipi->num_cpu) { - return MEMTX_DECODE_ERROR; - } - - s = &ipi->cpu[attrs.requester_id]; - return loongson_ipi_core_writel(s, addr, val, size, attrs); -} - static const MemoryRegionOps loongson_ipi_core_ops = { .read_with_attrs = loongson_ipi_core_readl, .write_with_attrs = loongson_ipi_core_writel, @@ -244,88 +52,15 @@ static const MemoryRegionOps loongson_ipi_core_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static const MemoryRegionOps loongson_ipi_iocsr_ops = { - .read_with_attrs = loongson_ipi_iocsr_readl, - .write_with_attrs = loongson_ipi_iocsr_writel, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .valid.min_access_size = 4, - .valid.max_access_size = 8, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* mail send and any send only support writeq */ -static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, - unsigned size, MemTxAttrs attrs) -{ - LoongsonIPICommonState *ipi = opaque; - MemTxResult ret = MEMTX_OK; - - addr &= 0xfff; - switch (addr) { - case MAIL_SEND_OFFSET: - ret = mail_send(ipi, val, attrs); - break; - case ANY_SEND_OFFSET: - ret = any_send(ipi, val, attrs); - break; - default: - break; - } - - return ret; -} - -static const MemoryRegionOps loongson_ipi64_ops = { - .write_with_attrs = loongson_ipi_writeq, - .impl.min_access_size = 8, - .impl.max_access_size = 8, - .valid.min_access_size = 8, - .valid.max_access_size = 8, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) -{ - LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - if (s->num_cpu == 0) { - error_setg(errp, "num-cpu must be at least 1"); - return; - } - - memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), - &loongson_ipi_iocsr_ops, - s, "loongson_ipi_iocsr", 0x48); - - /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */ - s->ipi_iocsr_mem.disable_reentrancy_guard = true; - - sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); - - memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), - &loongson_ipi64_ops, - s, "loongson_ipi64_iocsr", 0x118); - sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); - - s->cpu = g_new0(IPICore, s->num_cpu); - for (i = 0; i < s->num_cpu; i++) { - s->cpu[i].ipi = s; - - qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); - } -} - static void loongson_ipi_realize(DeviceState *dev, Error **errp) { LoongsonIPICommonState *sc = LOONGSON_IPI_COMMON(dev); LoongsonIPIState *s = LOONGSON_IPI(dev); + LoongsonIPIClass *lic = LOONGSON_IPI_GET_CLASS(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; - loongson_ipi_common_realize(dev, &local_err); + lic->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -341,20 +76,14 @@ static void loongson_ipi_realize(DeviceState *dev, Error **errp) } } -static void loongson_ipi_common_unrealize(DeviceState *dev) -{ - LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); - - g_free(s->cpu); -} - static void loongson_ipi_unrealize(DeviceState *dev) { LoongsonIPIState *s = LOONGSON_IPI(dev); + LoongsonIPIClass *k = LOONGSON_IPI_GET_CLASS(dev); g_free(s->ipi_mmio_mem); - loongson_ipi_common_unrealize(dev); + k->parent_unrealize(dev); } static void loongson_ipi_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index 47796f7ece..a6ce0181f6 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -8,8 +8,286 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/intc/loongson_ipi_common.h" +#include "hw/irq.h" #include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/log.h" #include "migration/vmstate.h" +#include "trace.h" + +MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + IPICore *s = opaque; + uint64_t ret = 0; + int index = 0; + + addr &= 0xff; + switch (addr) { + case CORE_STATUS_OFF: + ret = s->status; + break; + case CORE_EN_OFF: + ret = s->en; + break; + case CORE_SET_OFF: + ret = 0; + break; + case CORE_CLEAR_OFF: + ret = 0; + break; + case CORE_BUF_20 ... CORE_BUF_38 + 4: + index = (addr - CORE_BUF_20) >> 2; + ret = s->buf[index]; + break; + default: + qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); + break; + } + + trace_loongson_ipi_read(size, (uint64_t)addr, ret); + *data = ret; + + return MEMTX_OK; +} + +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + LoongsonIPICommonState *ipi = opaque; + IPICore *s; + + if (attrs.requester_id >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + + s = &ipi->cpu[attrs.requester_id]; + return loongson_ipi_core_readl(s, addr, data, size, attrs); +} + +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, + uint64_t val, hwaddr addr, MemTxAttrs attrs) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + int i, mask = 0, data = 0; + AddressSpace *iocsr_as = licc->get_iocsr_as(cpu); + + if (!iocsr_as) { + return MEMTX_DECODE_ERROR; + } + + /* + * bit 27-30 is mask for byte writing, + * if the mask is 0, we need not to do anything. + */ + if ((val >> 27) & 0xf) { + data = address_space_ldl_le(iocsr_as, addr, attrs, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ + if (val & (0x1 << (27 + i))) { + mask |= 0xff << (i * 8); + } + } + } + + data &= mask; + data |= (val >> 32) & ~mask; + address_space_stl_le(iocsr_as, addr, data, attrs, NULL); + + return MEMTX_OK; +} + +static MemTxResult mail_send(LoongsonIPICommonState *ipi, + uint64_t val, MemTxAttrs attrs) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + uint32_t cpuid; + hwaddr addr; + CPUState *cs; + + cpuid = extract32(val, 16, 10); + cs = licc->cpu_by_arch_id(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); + attrs.requester_id = cs->cpu_index; + return send_ipi_data(ipi, cs, val, addr, attrs); +} + +static MemTxResult any_send(LoongsonIPICommonState *ipi, + uint64_t val, MemTxAttrs attrs) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + uint32_t cpuid; + hwaddr addr; + CPUState *cs; + + cpuid = extract32(val, 16, 10); + cs = licc->cpu_by_arch_id(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = val & 0xffff; + attrs.requester_id = cs->cpu_index; + return send_ipi_data(ipi, cs, val, addr, attrs); +} + +MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + IPICore *s = opaque; + LoongsonIPICommonState *ipi = s->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + int index = 0; + uint32_t cpuid; + uint8_t vector; + CPUState *cs; + + addr &= 0xff; + trace_loongson_ipi_write(size, (uint64_t)addr, val); + switch (addr) { + case CORE_STATUS_OFF: + qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); + break; + case CORE_EN_OFF: + s->en = val; + break; + case CORE_SET_OFF: + s->status |= val; + if (s->status != 0 && (s->status & s->en) != 0) { + qemu_irq_raise(s->irq); + } + break; + case CORE_CLEAR_OFF: + s->status &= ~val; + if (s->status == 0 && s->en != 0) { + qemu_irq_lower(s->irq); + } + break; + case CORE_BUF_20 ... CORE_BUF_38 + 4: + index = (addr - CORE_BUF_20) >> 2; + s->buf[index] = val; + break; + case IOCSR_IPI_SEND: + cpuid = extract32(val, 16, 10); + /* IPI status vector */ + vector = extract8(val, 0, 5); + cs = licc->cpu_by_arch_id(cpuid); + if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF, + BIT(vector), 4, attrs); + break; + default: + qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); + break; + } + + return MEMTX_OK; +} + +static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LoongsonIPICommonState *ipi = opaque; + IPICore *s; + + if (attrs.requester_id >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + + s = &ipi->cpu[attrs.requester_id]; + return loongson_ipi_core_writel(s, addr, val, size, attrs); +} + +static const MemoryRegionOps loongson_ipi_iocsr_ops = { + .read_with_attrs = loongson_ipi_iocsr_readl, + .write_with_attrs = loongson_ipi_iocsr_writel, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* mail send and any send only support writeq */ +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + LoongsonIPICommonState *ipi = opaque; + MemTxResult ret = MEMTX_OK; + + addr &= 0xfff; + switch (addr) { + case MAIL_SEND_OFFSET: + ret = mail_send(ipi, val, attrs); + break; + case ANY_SEND_OFFSET: + ret = any_send(ipi, val, attrs); + break; + default: + break; + } + + return ret; +} + +static const MemoryRegionOps loongson_ipi64_ops = { + .write_with_attrs = loongson_ipi_writeq, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) +{ + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } + + memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), + &loongson_ipi_iocsr_ops, + s, "loongson_ipi_iocsr", 0x48); + + /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */ + s->ipi_iocsr_mem.disable_reentrancy_guard = true; + + sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); + + memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), + &loongson_ipi64_ops, + s, "loongson_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); + + s->cpu = g_new0(IPICore, s->num_cpu); + for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].ipi = s; + + qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); + } +} + +static void loongson_ipi_common_unrealize(DeviceState *dev) +{ + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); + + g_free(s->cpu); +} static const VMStateDescription vmstate_ipi_core = { .name = "ipi-single", @@ -45,7 +323,12 @@ static Property ipi_common_properties[] = { static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + device_class_set_parent_realize(dc, loongson_ipi_common_realize, + &licc->parent_realize); + device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize, + &licc->parent_unrealize); device_class_set_props(dc, ipi_common_properties); dc->vmsd = &vmstate_loongson_ipi_common; } diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index 65f8ef7957..df9d9c5168 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -41,6 +41,8 @@ struct LoongsonIPICommonState { struct LoongsonIPICommonClass { SysBusDeviceClass parent_class; + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; AddressSpace *(*get_iocsr_as)(CPUState *cpu); CPUState *(*cpu_by_arch_id)(int64_t id); }; From c403d5ff935d93ac89ff3e216428609979306cbb Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Wed, 17 Jul 2024 23:11:30 +0200 Subject: [PATCH 2698/2884] hw/intc/loongarch_ipi: Add loongarch IPI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loongarch IPI is added here, it inherits from class TYPE_LOONGSON_IPI_COMMON, and two interfaces get_iocsr_as() and cpu_by_arch_id() are added for Loongarch 3A5000 machine. It can be used when ipi is emulated in userspace with KVM mode. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Rebased and simplified] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-13-philmd@linaro.org> --- hw/intc/Kconfig | 4 ++ hw/intc/loongarch_ipi.c | 68 +++++++++++++++++++++++++++++++++ hw/intc/meson.build | 1 + include/hw/intc/loongarch_ipi.h | 25 ++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 hw/intc/loongarch_ipi.c create mode 100644 include/hw/intc/loongarch_ipi.h diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index a2a0fdca85..dd405bdb5d 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -94,6 +94,10 @@ config LOONGSON_IPI bool select LOONGSON_IPI_COMMON +config LOONGARCH_IPI + bool + select LOONGSON_IPI_COMMON + config LOONGARCH_PCH_PIC bool select UNIMP diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c new file mode 100644 index 0000000000..2ae1a42c46 --- /dev/null +++ b/hw/intc/loongarch_ipi.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch IPI interrupt support + * + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "hw/intc/loongarch_ipi.h" +#include "target/loongarch/cpu.h" + +static AddressSpace *get_iocsr_as(CPUState *cpu) +{ + return LOONGARCH_CPU(cpu)->env.address_space_iocsr; +} + +static int archid_cmp(const void *a, const void *b) +{ + CPUArchId *archid_a = (CPUArchId *)a; + CPUArchId *archid_b = (CPUArchId *)b; + + return archid_a->arch_id - archid_b->arch_id; +} + +static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) +{ + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = id; + found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, + ms->possible_cpus->len, + sizeof(*ms->possible_cpus->cpus), + archid_cmp); + + return found_cpu; +} + +static CPUState *loongarch_cpu_by_arch_id(int64_t arch_id) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + CPUArchId *archid; + + archid = find_cpu_by_archid(machine, arch_id); + if (archid) { + return CPU(archid->cpu); + } + + return NULL; +} + +static void loongarch_ipi_class_init(ObjectClass *klass, void *data) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + + licc->get_iocsr_as = get_iocsr_as; + licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; +} + +static const TypeInfo loongarch_ipi_types[] = { + { + .name = TYPE_LOONGARCH_IPI, + .parent = TYPE_LOONGSON_IPI_COMMON, + .class_init = loongarch_ipi_class_init, + } +}; + +DEFINE_TYPES(loongarch_ipi_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index a09a527207..f4d81eb8e4 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -71,6 +71,7 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h new file mode 100644 index 0000000000..276b3040a3 --- /dev/null +++ b/include/hw/intc/loongarch_ipi.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch IPI interrupt header files + * + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_IPI_H +#define HW_LOONGARCH_IPI_H + +#include "qom/object.h" +#include "hw/intc/loongson_ipi_common.h" + +#define TYPE_LOONGARCH_IPI "loongarch_ipi" +OBJECT_DECLARE_TYPE(LoongarchIPIState, LoongarchIPIClass, LOONGARCH_IPI) + +struct LoongarchIPIState { + LoongsonIPICommonState parent_obj; +}; + +struct LoongarchIPIClass { + LoongsonIPICommonClass parent_class; +}; + +#endif From ef2f11454cc6644d2fb68f67e707bc637036d006 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Mon, 15 Jul 2024 16:23:48 +0200 Subject: [PATCH 2699/2884] hw/loongarch/virt: Replace Loongson IPI with LoongArch IPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loongarch IPI inherits from class LoongsonIPICommonClass, and it only contains Loongarch 3A5000 virt machine specific interfaces, rather than mix different machine implementations together. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Rebased] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-14-philmd@linaro.org> --- hw/loongarch/Kconfig | 2 +- hw/loongarch/virt.c | 4 ++-- include/hw/loongarch/virt.h | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 89be737726..0de713a439 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -12,7 +12,7 @@ config LOONGARCH_VIRT select SERIAL select VIRTIO_PCI select PLATFORM_BUS - select LOONGSON_IPI + select LOONGARCH_IPI select LOONGARCH_PCH_PIC select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index e592b1b6b7..29040422aa 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -23,7 +23,7 @@ #include "net/net.h" #include "hw/loader.h" #include "elf.h" -#include "hw/intc/loongson_ipi.h" +#include "hw/intc/loongarch_ipi.h" #include "hw/intc/loongarch_extioi.h" #include "hw/intc/loongarch_pch_pic.h" #include "hw/intc/loongarch_pch_msi.h" @@ -788,7 +788,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) */ /* Create IPI device */ - ipi = qdev_new(TYPE_LOONGSON_IPI); + ipi = qdev_new(TYPE_LOONGARCH_IPI); qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 603c1cebdb..c373e48f27 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -11,7 +11,6 @@ #include "target/loongarch/cpu.h" #include "hw/boards.h" #include "qemu/queue.h" -#include "hw/intc/loongson_ipi.h" #include "hw/block/flash.h" #include "hw/loongarch/boot.h" From 3fad6db79e892c1e19a8b6b354284f183ed0432c Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Wed, 17 Jul 2024 23:32:49 +0200 Subject: [PATCH 2700/2884] hw/intc/loongson_ipi: Restrict to MIPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now than LoongArch target can use the TYPE_LOONGARCH_IPI model, restrict TYPE_LOONGSON_IPI to MIPS. Signed-off-by: Bibo Mao <maobibo@loongson.cn> [PMD: Extracted from bigger commit, added commit description] Co-Developed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Tested-by: Bibo Mao <maobibo@loongson.cn> Acked-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Message-Id: <20240805180622.21001-15-philmd@linaro.org> --- MAINTAINERS | 2 -- hw/intc/loongson_ipi.c | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5ca701cf0c..74a85360fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1251,10 +1251,8 @@ F: hw/loongarch/ F: include/hw/loongarch/virt.h F: include/hw/intc/loongarch_*.h F: include/hw/intc/loongson_ipi_common.h -F: include/hw/intc/loongson_ipi.h F: hw/intc/loongarch_*.c F: hw/intc/loongson_ipi_common.c -F: hw/intc/loongson_ipi.c F: include/hw/pci-host/ls7a.h F: hw/rtc/ls7a_rtc.c F: gdb-xml/loongarch*.xml diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index 0b88ae3230..8382ceca67 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -16,22 +16,9 @@ #include "exec/address-spaces.h" #include "exec/memory.h" #include "migration/vmstate.h" -#ifdef TARGET_LOONGARCH64 -#include "target/loongarch/cpu.h" -#endif -#ifdef TARGET_MIPS #include "target/mips/cpu.h" -#endif #include "trace.h" -#ifdef TARGET_LOONGARCH64 -static AddressSpace *get_iocsr_as(CPUState *cpu) -{ - return LOONGARCH_CPU(cpu)->env.address_space_iocsr; -} -#endif - -#ifdef TARGET_MIPS static AddressSpace *get_iocsr_as(CPUState *cpu) { if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) { @@ -40,7 +27,6 @@ static AddressSpace *get_iocsr_as(CPUState *cpu) return NULL; } -#endif static const MemoryRegionOps loongson_ipi_core_ops = { .read_with_attrs = loongson_ipi_core_readl, From 22d5fb42a82378c208355ff4a27cf25fc1cd652e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 30 Jul 2024 09:45:48 +0200 Subject: [PATCH 2701/2884] hw/sd/sdcard: Explicit dummy byte value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On error the DAT lines are left unmodified to their previous states. QEMU returns 0x00 for convenience. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240730092138.32443-2-philmd@linaro.org> --- hw/sd/sd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 07cb97d88c..de27e34fc8 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2478,20 +2478,22 @@ void sd_write_byte(SDState *sd, uint8_t value) uint8_t sd_read_byte(SDState *sd) { /* TODO: Append CRCs */ + const uint8_t dummy_byte = 0x00; uint8_t ret; uint32_t io_len; if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) - return 0x00; + return dummy_byte; if (sd->state != sd_sendingdata_state) { qemu_log_mask(LOG_GUEST_ERROR, "%s: not in Sending-Data state\n", __func__); - return 0x00; + return dummy_byte; } - if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) - return 0x00; + if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) { + return dummy_byte; + } io_len = sd_blk_len(sd); @@ -2517,7 +2519,7 @@ uint8_t sd_read_byte(SDState *sd) if (sd->data_offset == 0) { if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", sd->data_start, io_len)) { - return 0x00; + return dummy_byte; } sd_blk_read(sd, sd->data_start, io_len); } From bd6207903eb81c0e876452bba25ed7d57ddf5f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 30 Jul 2024 09:44:46 +0200 Subject: [PATCH 2702/2884] hw/sd/sdcard: Do not abort when reading DAT lines on invalid cmd state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guest should not try to read the DAT lines from invalid command state. If it still insists to do so, return a dummy value. Cc: qemu-stable@nongnu.org Fixes: e2dec2eab0 ("hw/sd/sdcard: Remove default case in read/write on DAT lines") Reported-by: Zheyu Ma <zheyuma97@gmail.com> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2454 Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240730092138.32443-3-philmd@linaro.org> --- hw/sd/sd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index de27e34fc8..a140a32ccd 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2540,7 +2540,9 @@ uint8_t sd_read_byte(SDState *sd) break; default: - g_assert_not_reached(); + qemu_log_mask(LOG_GUEST_ERROR, "%s: DAT read illegal for command %s\n", + __func__, sd->last_cmd_name); + return dummy_byte; } return ret; From ed5a159c3de48a581f46de4c8c02b4b295e6c52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 30 Jul 2024 10:41:25 +0200 Subject: [PATCH 2703/2884] hw/sd/sdhci: Reset @data_count index on invalid ADMA transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We neglected to clear the @data_count index on ADMA error, allowing to trigger assertion in sdhci_read_dataport() or sdhci_write_dataport(). Cc: qemu-stable@nongnu.org Fixes: d7dfca0807 ("hw/sdhci: introduce standard SD host controller") Reported-by: Zheyu Ma <zheyuma97@gmail.com> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2455 Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240730092138.32443-4-philmd@linaro.org> --- hw/sd/sdhci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index d02c3e3963..8293d83556 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -846,6 +846,7 @@ static void sdhci_do_adma(SDHCIState *s) } } if (res != MEMTX_OK) { + s->data_count = 0; if (s->errintstsen & SDHC_EISEN_ADMAERR) { trace_sdhci_error("Set ADMA error flag"); s->errintsts |= SDHC_EIS_ADMAERR; From 8f64e7449e474e18017eb1414bc13e491edd8596 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 15:36:13 +0100 Subject: [PATCH 2704/2884] hw/block/pflash_cfi01: Don't decrement pfl->counter below 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pflash_write() Coverity points out that we can decrement the unsigned pfl->counter below zero, which makes it wrap around. In fact this is harmless, because if pfl->counter is 0 at this point we also increment pfl->wcycle to 3, and the wcycle == 3 handling doesn't look at counter; the only way back into code which looks at the counter value is via wcycle == 1, which will reinitialize the counter. But it's arguably a little clearer to break early in the "counter == 0" if(), to avoid the decrement-below-zero. Resolves: Coverity CID 1547611 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20240731143617.3391947-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/block/pflash_cfi01.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index c8f1cf5a87..2f3d1dd509 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -614,6 +614,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, if (!pfl->counter) { trace_pflash_write(pfl->name, "block write finished"); pfl->wcycle++; + break; } pfl->counter--; From f63085c85d164484a58fa320114f389c91194487 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 15:36:14 +0100 Subject: [PATCH 2705/2884] hw/ide/atapi: Be explicit that assigning to s->lcyl truncates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ide_atapi_cmd_reply_end() we calculate a 16-bit size, and then assign its two halves to s->lcyl and s->hcyl like this: s->lcyl = size; s->hcyl = size >> 8; Coverity warns that the first line here can overflow the 8-bit s->lcyl variable. This is true, and in this case we're deliberately only after the low 8 bits of the value. The code is clearer to both humans and Coverity if we're explicit that we only wanted the low 8 bits, though. Resolves: Coverity CID 1547621 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20240731143617.3391947-5-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/ide/atapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index fcb6cca157..e82959dc2d 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -265,7 +265,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) byte_count_limit--; size = byte_count_limit; } - s->lcyl = size; + s->lcyl = size & 0xff; s->hcyl = size >> 8; s->elementary_transfer_size = size; /* we cannot transmit more than one sector at a time */ From 11b46661613f847cd0c4070baa2d33c9ff2f3fcd Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 15:36:15 +0100 Subject: [PATCH 2706/2884] hw/block/fdc-isa: Assert that isa_fdc_get_drive_max_chs() found something MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity complains about an overflow in isa_fdc_get_drive_max_chs() that can happen if the loop over fd_formats never finds a match, because we initialize *maxc to 0 and then at the end of the function decrement it. This can't ever actually happen because fd_formats has at least one entry for each FloppyDriveType, so we must at least once find a match and update *maxc, *maxh and *maxs. Assert that we did find a match, which should keep Coverity happy and will also detect possible bugs in the data in fd_formats. Resolves: Coverity CID 1547663 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240731143617.3391947-6-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/block/fdc-isa.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index e43dc532af..796835f57b 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -147,6 +147,8 @@ static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc, *maxs = fdf->last_sect; } } + /* fd_formats must contain at least one entry per FloppyDriveType */ + assert(*maxc); (*maxc)--; } From c1a6ae5145f2e72c1eaf53b7a8c96cdbb64211bd Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 15:36:16 +0100 Subject: [PATCH 2707/2884] hw/ide/pci: Remove dead code from bmdma_prepare_buf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity notes that the code at the end of the loop in bmdma_prepare_buf() is unreachable. This is because in commit 9fbf0fa81fca8f527 ("ide: remove hardcoded 2GiB transactional limit") we removed the only codepath in the loop which could "break" out of it, but didn't notice that this meant we should also remove the code at the end of the loop. Remove the dead code. Resolves: Coverity CID 1547772 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [PMD: Break and return once at EOF] Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240805182419.22239-1-philmd@linaro.org> --- hw/ide/pci.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 4675d079a1..a008fe7316 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -237,7 +237,7 @@ static int32_t bmdma_prepare_buf(const IDEDMA *dma, int32_t limit) /* end of table (with a fail safe of one page) */ if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) { - return s->sg.size; + break; } pci_dma_read(pci_dev, bm->cur_addr, &prd, 8); bm->cur_addr += 8; @@ -266,10 +266,7 @@ static int32_t bmdma_prepare_buf(const IDEDMA *dma, int32_t limit) s->io_buffer_size += l; } } - - qemu_sglist_destroy(&s->sg); - s->io_buffer_size = 0; - return -1; + return s->sg.size; } /* return 0 if buffer completed */ From 0fa57cbfa7039e4a579f1a4a0956982b654335c8 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 16:41:34 +0100 Subject: [PATCH 2708/2884] hw/display/virtio-gpu: Improve "opengl is not available" error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the user tries to use the virtio-gpu-gl device but the display backend doesn't have OpenGL support enabled, we currently print a rather uninformative error message: $ qemu-system-aarch64 -M virt -device virtio-gpu-gl qemu-system-aarch64: -device virtio-gpu-gl: opengl is not available Since OpenGL is not enabled on display frontends by default, users are quite likely to run into this. Improve the error message to be more specific and to suggest to the user a path forward. Note that the case of "user tried to enable OpenGL but the display backend doesn't handle it" is caught elsewhere first, so we can assume that isn't the problem: $ qemu-system-aarch64 -M virt -device virtio-gpu-gl -display curses,gl=on qemu-system-aarch64: OpenGL is not supported by the display (Use of error_append_hint() requires us to add an ERRP_GUARD() to the function, as noted in include/qapi/error.h.) With this commit we now produce the hopefully more helpful error: $ ./build/x86/qemu-system-aarch64 -M virt -device virtio-gpu-gl qemu-system-aarch64: -device virtio-gpu-gl: The display backend does not have OpenGL support enabled It can be enabled with '-display BACKEND,gl=on' where BACKEND is the name of the display backend to use. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2443 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-ID: <20240731154136.3494621-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/display/virtio-gpu-gl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index 952820a425..29d20b0132 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -106,6 +106,7 @@ static void virtio_gpu_gl_reset(VirtIODevice *vdev) static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) { + ERRP_GUARD(); VirtIOGPU *g = VIRTIO_GPU(qdev); #if HOST_BIG_ENDIAN @@ -119,7 +120,12 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) } if (!display_opengl) { - error_setg(errp, "opengl is not available"); + error_setg(errp, + "The display backend does not have OpenGL support enabled"); + error_append_hint(errp, + "It can be enabled with '-display BACKEND,gl=on' " + "where BACKEND is the name of the display backend " + "to use.\n"); return; } From 7aea035a60eec599c487d71659fdb5102b69cfb9 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 16:41:35 +0100 Subject: [PATCH 2709/2884] system/vl.c: Expand OpenGL related errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the OpenGL related error messages we produce for various "OpenGL not present/not supported" cases, to hopefully guide the user towards how to fix things. Now if the user tries to enable GL on a backend that doesn't support it the error message is a bit more precise: $ qemu-system-aarch64 -M virt -device virtio-gpu-gl -display curses,gl=on qemu-system-aarch64: OpenGL is not supported by display backend 'curses' Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [AJB: Improved error report message] Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-ID: <20240731154136.3494621-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- system/vl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system/vl.c b/system/vl.c index 9e8f16f155..41d53d2456 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1973,9 +1973,10 @@ static void qemu_create_early_backends(void) if (dpy.has_gl && dpy.gl != DISPLAYGL_MODE_OFF && display_opengl == 0) { #if defined(CONFIG_OPENGL) - error_report("OpenGL is not supported by the display"); + error_report("OpenGL is not supported by display backend '%s'", + DisplayType_str(dpy.type)); #else - error_report("OpenGL support is disabled"); + error_report("OpenGL support was not enabled in this build of QEMU"); #endif exit(1); } From ef0a1212c9c9c4b50fb6f7d83fbb6cbb3c8e5355 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Wed, 31 Jul 2024 16:41:36 +0100 Subject: [PATCH 2710/2884] ui/console: Note in '-display help' that some backends support suboptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently '-display help' only prints the available backends. Some of those backends support suboptions (e.g. '-display gtk,gl=on'). Mention that in the help output, and point the user to where they might be able to find more information about the suboptions. The new output looks like this: $ qemu-system-aarch64 -display help Available display backend types: none gtk sdl egl-headless curses spice-app dbus Some display backends support suboptions, which can be set with -display backend,option=value,option=value... For a short list of the suboptions for each display, see the top-level -help output; more detail is in the documentation. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20240731154136.3494621-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- ui/console.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/console.c b/ui/console.c index e8f0083af7..105a0e2c70 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1632,4 +1632,9 @@ void qemu_display_help(void) printf("%s\n", DisplayType_str(dpys[idx]->type)); } } + printf("\n" + "Some display backends support suboptions, which can be set with\n" + " -display backend,option=value,option=value...\n" + "For a short list of the suboptions for each display, see the " + "top-level -help output; more detail is in the documentation.\n"); } From 50a24291948a25a919147cea45eaa92bd8e401b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Fri, 2 Aug 2024 18:49:55 +0200 Subject: [PATCH 2711/2884] hw/pci-host/gt64120: Set PCI base address register write mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When booting Linux we see: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [mem 0x10000000-0x17ffffff] pci_bus 0000:00: root bus resource [io 0x1000-0x1fffff] pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff] pci 0000:00:00.0: [11ab:4620] type 00 class 0x060000 pci 0000:00:00.0: [Firmware Bug]: reg 0x14: invalid BAR (can't size) pci 0000:00:00.0: [Firmware Bug]: reg 0x18: invalid BAR (can't size) pci 0000:00:00.0: [Firmware Bug]: reg 0x1c: invalid BAR (can't size) pci 0000:00:00.0: [Firmware Bug]: reg 0x20: invalid BAR (can't size) pci 0000:00:00.0: [Firmware Bug]: reg 0x24: invalid BAR (can't size) This is due to missing base address register write mask. Add it to get: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [mem 0x10000000-0x17ffffff] pci_bus 0000:00: root bus resource [io 0x1000-0x1fffff] pci_bus 0000:00: No busn resource found for root bus, will use [bus 00-ff] pci 0000:00:00.0: [11ab:4620] type 00 class 0x060000 pci 0000:00:00.0: reg 0x10: [mem 0x00000000-0x00000fff pref] pci 0000:00:00.0: reg 0x14: [mem 0x01000000-0x01000fff pref] pci 0000:00:00.0: reg 0x18: [mem 0x1c000000-0x1c000fff] pci 0000:00:00.0: reg 0x1c: [mem 0x1f000000-0x1f000fff] pci 0000:00:00.0: reg 0x20: [mem 0x1be00000-0x1be00fff] pci 0000:00:00.0: reg 0x24: [io 0x14000000-0x14000fff] Since this device is only used by MIPS machines which aren't versioned, we don't need to update migration compat machinery. Mention the datasheet referenced. Remove the "Malta assumptions ahead" comment since the reset values from the datasheet are used. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu> Message-Id: <20240802213122.86852-2-philmd@linaro.org> --- hw/pci-host/gt64120.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index e02efc9e2e..573d2619ee 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1,6 +1,8 @@ /* * QEMU GT64120 PCI host * + * (Datasheet GT-64120 Rev 1.4 from Sep 14, 1999) + * * Copyright (c) 2006,2007 Aurelien Jarno * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -1213,17 +1215,27 @@ static void gt64120_realize(DeviceState *dev, Error **errp) static void gt64120_pci_realize(PCIDevice *d, Error **errp) { - /* FIXME: Malta specific hw assumptions ahead */ + /* Values from chapter 17.16 "PCI Configuration" */ + pci_set_word(d->config + PCI_COMMAND, 0); pci_set_word(d->config + PCI_STATUS, PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); pci_config_set_prog_interface(d->config, 0); + + pci_set_long(d->wmask + PCI_BASE_ADDRESS_0, 0xfffff008); /* SCS[1:0] */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_1, 0xfffff008); /* SCS[3:2] */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_2, 0xfffff008); /* CS[2:0] */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_3, 0xfffff008); /* CS[3], BootCS */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_4, 0xfffff000); /* ISD MMIO */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_5, 0xfffff001); /* ISD I/O */ + pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); + pci_set_byte(d->config + 0x3d, 0x01); } From ec70b7737f18de92e4c9aa31742ecfb680ed6005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 1 Aug 2024 15:27:24 +0200 Subject: [PATCH 2712/2884] hw/pci-host/gt64120: Reset config registers during RESET phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reset config values in the device RESET phase, not only once when the device is realized, because otherwise the device can use unknown values at reset. Since we are adding a new reset method, use the preferred Resettable API (for a simple leaf device reset, a DeviceClass::reset method and a ResettableClass::reset_hold method are essentially identical). Reported-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240802213122.86852-3-philmd@linaro.org> --- hw/pci-host/gt64120.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index 573d2619ee..33607dfbec 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1217,17 +1217,24 @@ static void gt64120_pci_realize(PCIDevice *d, Error **errp) { /* Values from chapter 17.16 "PCI Configuration" */ - pci_set_word(d->config + PCI_COMMAND, 0); - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - pci_config_set_prog_interface(d->config, 0); - pci_set_long(d->wmask + PCI_BASE_ADDRESS_0, 0xfffff008); /* SCS[1:0] */ pci_set_long(d->wmask + PCI_BASE_ADDRESS_1, 0xfffff008); /* SCS[3:2] */ pci_set_long(d->wmask + PCI_BASE_ADDRESS_2, 0xfffff008); /* CS[2:0] */ pci_set_long(d->wmask + PCI_BASE_ADDRESS_3, 0xfffff008); /* CS[3], BootCS */ pci_set_long(d->wmask + PCI_BASE_ADDRESS_4, 0xfffff000); /* ISD MMIO */ pci_set_long(d->wmask + PCI_BASE_ADDRESS_5, 0xfffff001); /* ISD I/O */ +} + +static void gt64120_pci_reset_hold(Object *obj, ResetType type) +{ + PCIDevice *d = PCI_DEVICE(obj); + + /* Values from chapter 17.16 "PCI Configuration" */ + + pci_set_word(d->config + PCI_COMMAND, 0); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + pci_config_set_prog_interface(d->config, 0); pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); @@ -1243,7 +1250,9 @@ static void gt64120_pci_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.hold = gt64120_pci_reset_hold; k->realize = gt64120_pci_realize; k->vendor_id = PCI_VENDOR_ID_MARVELL; k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; From 6e717a723073fc05a8438d8ffb289e030207ee62 Mon Sep 17 00:00:00 2001 From: George Matsumura <gorg@gorgnet.net> Date: Sun, 4 Aug 2024 21:10:11 -0600 Subject: [PATCH 2713/2884] docs/specs/pci-ids: Add missing devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the missing devices 1b36:000c (PCIe root port) and 1b36:000e (PCIe-to-PCI bridge). Signed-off-by: George Matsumura <gorg@gorgnet.net> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240805031012.16547-2-gorg@gorgnet.net> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/specs/pci-ids.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index c0a3dec2e7..0de13de1e4 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -82,8 +82,12 @@ PCI devices (other than virtio): PCI-PCI bridge (multiseat) 1b36:000b PCIe Expander Bridge (-device pxb-pcie) +1b36:000c + PCIe Root Port (``-device pcie-root-port``) 1b36:000d PCI xhci usb host adapter +1b36:000e + PCIe-to-PCI bridge (``-device pcie-pci-bridge``) 1b36:000f mdpy (mdev sample device), ``linux/samples/vfio-mdev/mdpy.c`` 1b36:0010 From 7e7085da1f86655cb9c892e14328cc07fe552717 Mon Sep 17 00:00:00 2001 From: George Matsumura <gorg@gorgnet.net> Date: Sun, 4 Aug 2024 21:10:13 -0600 Subject: [PATCH 2714/2884] docs/specs/pci-ids: Fix markup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the markup of the PCI and PCIe Expander Bridge entries to be consistent with the rest of the file. Signed-off-by: George Matsumura <gorg@gorgnet.net> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240805031012.16547-4-gorg@gorgnet.net> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- docs/specs/pci-ids.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index 0de13de1e4..328ab31fe8 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -77,11 +77,11 @@ PCI devices (other than virtio): 1b36:0008 PCIe host bridge 1b36:0009 - PCI Expander Bridge (-device pxb) + PCI Expander Bridge (``-device pxb``) 1b36:000a PCI-PCI bridge (multiseat) 1b36:000b - PCIe Expander Bridge (-device pxb-pcie) + PCIe Expander Bridge (``-device pxb-pcie``) 1b36:000c PCIe Root Port (``-device pcie-root-port``) 1b36:000d From f0e0c46309dac41a8420bcb379b1cffa7da0f62c Mon Sep 17 00:00:00 2001 From: Markus Armbruster <armbru@redhat.com> Date: Thu, 18 Jul 2024 14:36:09 +0200 Subject: [PATCH 2715/2884] qapi-block-core: Clean up blockdev-snapshot-internal-sync doc BlockdevSnapshotInternal is the arguments type of command blockdev-snapshot-internal-sync. Its doc comment contains this note: # .. note:: In a transaction, if @name is empty or any snapshot matching # @name exists, the operation will fail. Only some image formats # support it; for example, qcow2, and rbd. "In a transaction" is misleading, and "if @name is empty or any snapshot matching @name exists, the operation will fail" is redundant with the command's Errors documentation. Drop. The remainder is fine. Move it to the command's doc comment, where it is more prominently visible, with a slight rephrasing for clarity. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20240718123609.3063055-1-armbru@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- qapi/block-core.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index a1b7cdc58e..aa40d44f1d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -6047,10 +6047,6 @@ # # @name: the name of the internal snapshot to be created # -# .. note:: In a transaction, if @name is empty or any snapshot -# matching @name exists, the operation will fail. Only some image -# formats support it; for example, qcow2, and rbd. -# # Since: 1.7 ## { 'struct': 'BlockdevSnapshotInternal', @@ -6071,6 +6067,9 @@ # - If the format of the image used does not support it, # GenericError # +# .. note:: Only some image formats such as qcow2 and rbd support +# internal snapshots. +# # Since: 1.7 # # .. qmp-example:: From d5f6cbb263662af585b866481382c3f5ba5cd638 Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Thu, 27 Jun 2024 20:12:44 +0200 Subject: [PATCH 2716/2884] block-copy: Fix missing graph lock The graph lock needs to be held when calling bdrv_co_pdiscard(). Fix block_copy_task_entry() to take it for the call. WITH_GRAPH_RDLOCK_GUARD() was implemented in a weak way because of limitations in clang's Thread Safety Analysis at the time, so that it only asserts that the lock is held (which allows calling functions that require the lock), but we never deal with the unlocking (so even after the scope of the guard, the compiler assumes that the lock is still held). This is why the compiler didn't catch this locking error. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20240627181245.281403-2-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- block/block-copy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/block-copy.c b/block/block-copy.c index 7e3b378528..cc618e4561 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -595,7 +595,9 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) if (s->discard_source && ret == 0) { int64_t nbytes = MIN(t->req.offset + t->req.bytes, s->len) - t->req.offset; - bdrv_co_pdiscard(s->source, t->req.offset, nbytes); + WITH_GRAPH_RDLOCK_GUARD() { + bdrv_co_pdiscard(s->source, t->req.offset, nbytes); + } } return ret; From 7e1711164683b1036f7da5f87c5f66c17a043ab1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Thu, 27 Jun 2024 20:12:45 +0200 Subject: [PATCH 2717/2884] block/graph-lock: Make WITH_GRAPH_RDLOCK_GUARD() fully checked Upstream clang 18 (and backports to clang 17 in Fedora and RHEL) implemented support for __attribute__((cleanup())) in its Thread Safety Analysis, so we can now actually have a proper implementation of WITH_GRAPH_RDLOCK_GUARD() that understands when we acquire and when we release the lock. -Wthread-safety is now only enabled if the compiler is new enough to understand this pattern. In theory, we could have used some #ifdefs to keep the existing basic checks on old compilers, but as long as someone runs a newer compiler (and our CI does), we will catch locking problems, so it's probably not worth keeping multiple implementations for this. The implementation can't use g_autoptr any more because the glib macros define wrapper functions that don't have the right TSA attributes, so the compiler would complain about them. Just use the cleanup attribute directly instead. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20240627181245.281403-3-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- include/block/graph-lock.h | 21 ++++++++++++++------- meson.build | 14 +++++++++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h index d7545e82d0..dc8d949184 100644 --- a/include/block/graph-lock.h +++ b/include/block/graph-lock.h @@ -209,31 +209,38 @@ typedef struct GraphLockable { } GraphLockable; * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that * we hold the lock while unlocking is left unchecked. */ -static inline GraphLockable * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA coroutine_fn +static inline GraphLockable * TSA_ACQUIRE_SHARED(graph_lock) coroutine_fn graph_lockable_auto_lock(GraphLockable *x) { bdrv_graph_co_rdlock(); return x; } -static inline void TSA_NO_TSA coroutine_fn -graph_lockable_auto_unlock(GraphLockable *x) +static inline void TSA_RELEASE_SHARED(graph_lock) coroutine_fn +graph_lockable_auto_unlock(GraphLockable **x) { bdrv_graph_co_rdunlock(); } -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) +#define GRAPH_AUTO_UNLOCK __attribute__((cleanup(graph_lockable_auto_unlock))) +/* + * @var is only used to break the loop after the first iteration. + * @unlock_var can't be unlocked and then set to NULL because TSA wants the lock + * to be held at the start of every iteration of the loop. + */ #define WITH_GRAPH_RDLOCK_GUARD_(var) \ - for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ + for (GraphLockable *unlock_var GRAPH_AUTO_UNLOCK = \ + graph_lockable_auto_lock(GML_OBJ_()), \ + *var = unlock_var; \ var; \ - graph_lockable_auto_unlock(var), var = NULL) + var = NULL) #define WITH_GRAPH_RDLOCK_GUARD() \ WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) #define GRAPH_RDLOCK_GUARD(x) \ - g_autoptr(GraphLockable) \ + GraphLockable * GRAPH_AUTO_UNLOCK \ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ graph_lockable_auto_lock(GML_OBJ_()) diff --git a/meson.build b/meson.build index 97f63aa86c..c2a050b844 100644 --- a/meson.build +++ b/meson.build @@ -649,7 +649,19 @@ warn_flags = [ ] if host_os != 'darwin' - warn_flags += ['-Wthread-safety'] + tsa_has_cleanup = cc.compiles(''' + struct __attribute__((capability("mutex"))) mutex {}; + void lock(struct mutex *m) __attribute__((acquire_capability(m))); + void unlock(struct mutex *m) __attribute__((release_capability(m))); + + void test(void) { + struct mutex __attribute__((cleanup(unlock))) m; + lock(&m); + } + ''', args: ['-Wthread-safety', '-Werror']) + if tsa_has_cleanup + warn_flags += ['-Wthread-safety'] + endif endif # Set up C++ compiler flags From cfe0880835cd364b590ffd27ef8dbd2ad8838bc5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Wed, 31 Jul 2024 14:32:04 +0200 Subject: [PATCH 2718/2884] scsi-disk: Use positive return value for status in dma_readv/writev In some error cases, scsi_block_sgio_complete() never calls the passed callback, but directly completes the request. This leads to bugs because its error paths are not exact copies of what the callback would normally do. In preparation to fix this, allow passing positive return values to the callbacks that represent the status code that should be used to complete the request. scsi_handle_rw_error() already handles positive values for its ret parameter because scsi_block_sgio_complete() calls directly into it. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20240731123207.27636-2-kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- hw/scsi/scsi-disk.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a67092db6a..3ff6798bde 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -65,6 +65,10 @@ OBJECT_DECLARE_TYPE(SCSIDiskState, SCSIDiskClass, SCSI_DISK_BASE) struct SCSIDiskClass { SCSIDeviceClass parent_class; + /* + * Callbacks receive ret == 0 for success. Errors are represented either as + * negative errno values, or as positive SAM status codes. + */ DMAIOFunc *dma_readv; DMAIOFunc *dma_writev; bool (*need_fua_emulation)(SCSICommand *cmd); @@ -283,7 +287,7 @@ static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) return true; } - if (ret < 0) { + if (ret != 0) { return scsi_handle_rw_error(r, ret, acct_failed); } @@ -360,7 +364,7 @@ static void scsi_write_do_fua(SCSIDiskReq *r) static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) { assert(r->req.aiocb == NULL); - if (scsi_disk_req_check_error(r, ret, false)) { + if (scsi_disk_req_check_error(r, ret, ret > 0)) { goto done; } @@ -385,9 +389,10 @@ static void scsi_dma_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; + /* ret > 0 is accounted for in scsi_disk_req_check_error() */ if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { + } else if (ret == 0) { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); } scsi_dma_complete_noio(r, ret); @@ -403,7 +408,7 @@ static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) qemu_get_current_aio_context()); assert(r->req.aiocb == NULL); - if (scsi_disk_req_check_error(r, ret, false)) { + if (scsi_disk_req_check_error(r, ret, ret > 0)) { goto done; } @@ -424,9 +429,10 @@ static void scsi_read_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; + /* ret > 0 is accounted for in scsi_disk_req_check_error() */ if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { + } else if (ret == 0) { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); trace_scsi_disk_read_complete(r->req.tag, r->qiov.size); } @@ -534,7 +540,7 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) qemu_get_current_aio_context()); assert (r->req.aiocb == NULL); - if (scsi_disk_req_check_error(r, ret, false)) { + if (scsi_disk_req_check_error(r, ret, ret > 0)) { goto done; } @@ -562,9 +568,10 @@ static void scsi_write_complete(void * opaque, int ret) assert (r->req.aiocb != NULL); r->req.aiocb = NULL; + /* ret > 0 is accounted for in scsi_disk_req_check_error() */ if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { + } else if (ret == 0) { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); } scsi_write_complete_noio(r, ret); From 622a70161ac258e4a166a7dca4b5be267e0652d9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Wed, 31 Jul 2024 14:32:05 +0200 Subject: [PATCH 2719/2884] scsi-block: Don't skip callback for sgio error status/driver_status Instead of calling into scsi_handle_rw_error() directly from scsi_block_sgio_complete() and skipping the normal callback, go through the normal cleanup path by calling the callback with a positive error value. The important difference here is not only that the code path is cleaner, but that the callbacks set r->req.aiocb = NULL. If we skip setting this and the error action is BLOCK_ERROR_ACTION_STOP, resuming the VM runs into an assertion failure in scsi_read_data() or scsi_write_data() because the dangling aiocb pointer is unexpected. Fixes: a108557bbf ("scsi: inline sg_io_sense_from_errno() into the callers.") Buglink: https://issues.redhat.com/browse/RHEL-50000 Signed-off-by: Kevin Wolf <kwolf@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20240731123207.27636-3-kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- hw/scsi/scsi-disk.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 3ff6798bde..6e1a5c98df 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2832,16 +2832,6 @@ static void scsi_block_sgio_complete(void *opaque, int ret) } else { ret = io_hdr->status; } - - if (ret > 0) { - if (scsi_handle_rw_error(r, ret, true)) { - scsi_req_unref(&r->req); - return; - } - - /* Ignore error. */ - ret = 0; - } } req->cb(req->cb_opaque, ret); From 8a0495624f23f8f01dfb1484f367174f3b3572e8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Wed, 31 Jul 2024 14:32:06 +0200 Subject: [PATCH 2720/2884] scsi-disk: Add warning comments that host_status errors take a shortcut scsi_block_sgio_complete() has surprising behaviour in that there are error cases in which it directly completes the request and never calls the passed callback. In the current state of the code, this doesn't seem to result in bugs, but with future code changes, we must be careful to never rely on the callback doing some cleanup until this code smell is fixed. For now, just add warnings to make people aware of the trap. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20240731123207.27636-4-kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- hw/scsi/scsi-disk.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 6e1a5c98df..69a195177e 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -68,6 +68,9 @@ struct SCSIDiskClass { /* * Callbacks receive ret == 0 for success. Errors are represented either as * negative errno values, or as positive SAM status codes. + * + * Beware: For errors returned in host_status, the function may directly + * complete the request and never call the callback. */ DMAIOFunc *dma_readv; DMAIOFunc *dma_writev; @@ -381,6 +384,7 @@ done: scsi_req_unref(&r->req); } +/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -421,6 +425,7 @@ done: scsi_req_unref(&r->req); } +/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_read_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -560,6 +565,7 @@ done: scsi_req_unref(&r->req); } +/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_write_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -2821,6 +2827,7 @@ static void scsi_block_sgio_complete(void *opaque, int ret) sg_io_hdr_t *io_hdr = &req->io_header; if (ret == 0) { + /* FIXME This skips calling req->cb() and any cleanup in it */ if (io_hdr->host_status != SCSI_HOST_OK) { scsi_req_complete_failed(&r->req, io_hdr->host_status); scsi_req_unref(&r->req); From 9da6bd39f92434f55573acd017841b195c60188f Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Wed, 31 Jul 2024 14:32:07 +0200 Subject: [PATCH 2721/2884] scsi-disk: Always report RESERVATION_CONFLICT to guest In the case of scsi-block, RESERVATION_CONFLICT is not a backend error, but indicates that the guest tried to make a request that it isn't allowed to execute. Pass the error to the guest so that it can decide what to do with it. Without this, if we stop the VM in response to a RESERVATION_CONFLICT (as is the default policy in management software such as oVirt or KubeVirt), it can happen that the VM cannot be resumed any more because every attempt to resume it immediately runs into the same error and stops the VM again. One case that expects RESERVATION_CONFLICT errors to be visible in the guest is running the validation tests in Windows 2019's Failover Cluster Manager, which intentionally tries to execute invalid requests to see if they are properly rejected. Buglink: https://issues.redhat.com/browse/RHEL-50000 Signed-off-by: Kevin Wolf <kwolf@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20240731123207.27636-5-kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- hw/scsi/scsi-disk.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 69a195177e..4d94b2b816 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -224,7 +224,7 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); SCSISense sense = SENSE_CODE(NO_SENSE); - int error = 0; + int error; bool req_has_sense = false; BlockErrorAction action; int status; @@ -235,11 +235,35 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) } else { /* A passthrough command has completed with nonzero status. */ status = ret; - if (status == CHECK_CONDITION) { + switch (status) { + case CHECK_CONDITION: req_has_sense = true; error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); - } else { + break; + case RESERVATION_CONFLICT: + /* + * Don't apply the error policy, always report to the guest. + * + * This is a passthrough code path, so it's not a backend error, but + * a response to an invalid guest request. + * + * Windows Failover Cluster validation intentionally sends invalid + * requests to verify that reservations work as intended. It is + * crucial that it sees the resulting errors. + * + * Treating a reservation conflict as a guest-side error is obvious + * when a pr-manager is in use. Without one, the situation is less + * clear, but there might be nothing that can be fixed on the host + * (like in the above example), and we don't want to be stuck in a + * loop where resuming the VM and retrying the request immediately + * stops it again. So always reporting is still the safer option in + * this case, too. + */ + error = 0; + break; + default: error = EINVAL; + break; } } @@ -249,8 +273,9 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) * are usually retried immediately, so do not post them to QMP and * do not account them as failed I/O. */ - if (req_has_sense && - scsi_sense_buf_is_guest_recoverable(r->req.sense, sizeof(r->req.sense))) { + if (!error || (req_has_sense && + scsi_sense_buf_is_guest_recoverable(r->req.sense, + sizeof(r->req.sense)))) { action = BLOCK_ERROR_ACTION_REPORT; acct_failed = false; } else { From b881cf00c99e03bc8a3648581f97736ff275b18b Mon Sep 17 00:00:00 2001 From: Amjad Alsharafi <amjadsharafi10@gmail.com> Date: Sat, 20 Jul 2024 18:13:30 +0800 Subject: [PATCH 2722/2884] vvfat: Fix bug in writing to middle of file Before this commit, the behavior when calling `commit_one_file` for example with `offset=0x2000` (second cluster), what will happen is that we won't fetch the next cluster from the fat, and instead use the first cluster for the read operation. This is due to off-by-one error here, where `i=0x2000 !< offset=0x2000`, thus not fetching the next cluster. Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Tested-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <b97c1e1f1bc2f776061ae914f95d799d124fcd73.1721470238.git.amjadsharafi10@gmail.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- block/vvfat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/vvfat.c b/block/vvfat.c index 086fedf474..a67cfa823b 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2525,8 +2525,9 @@ commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset) return -1; } - for (i = s->cluster_size; i < offset; i += s->cluster_size) + for (i = 0; i < offset; i += s->cluster_size) { c = modified_fat_get(s, c); + } fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); if (fd < 0) { From 21b25a0e466a5bba0a45600bb8100ab564202ed1 Mon Sep 17 00:00:00 2001 From: Amjad Alsharafi <amjadsharafi10@gmail.com> Date: Sat, 20 Jul 2024 18:13:31 +0800 Subject: [PATCH 2723/2884] vvfat: Fix usage of `info.file.offset` The field is marked as "the offset in the file (in clusters)", but it was being used like this `cluster_size*(nums)+mapping->info.file.offset`, which is incorrect. Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <72f19a7903886dda1aa78bcae0e17702ee939262.1721470238.git.amjadsharafi10@gmail.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- block/vvfat.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/block/vvfat.c b/block/vvfat.c index a67cfa823b..cfde468c2e 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1408,7 +1408,9 @@ read_cluster_directory: assert(s->current_fd); - offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset; + offset = s->cluster_size * + ((cluster_num - s->current_mapping->begin) + + s->current_mapping->info.file.offset); if(lseek(s->current_fd, offset, SEEK_SET)!=offset) return -3; s->cluster=s->cluster_buffer; @@ -1929,8 +1931,9 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch (mapping->mode & MODE_DIRECTORY) == 0) { /* was modified in qcow */ - if (offset != mapping->info.file.offset + s->cluster_size - * (cluster_num - mapping->begin)) { + if (offset != s->cluster_size + * ((cluster_num - mapping->begin) + + mapping->info.file.offset)) { /* offset of this cluster in file chain has changed */ abort(); copy_it = 1; @@ -2404,7 +2407,7 @@ static int commit_mappings(BDRVVVFATState* s, (mapping->end - mapping->begin); } else next_mapping->info.file.offset = mapping->info.file.offset + - mapping->end - mapping->begin; + (mapping->end - mapping->begin); mapping = next_mapping; } From f60a6f7e17bf2a2a0f0a08265ac9b077fce42858 Mon Sep 17 00:00:00 2001 From: Amjad Alsharafi <amjadsharafi10@gmail.com> Date: Sat, 20 Jul 2024 18:13:32 +0800 Subject: [PATCH 2724/2884] vvfat: Fix wrong checks for cluster mappings invariant How this `abort` was intended to check for was: - if the `mapping->first_mapping_index` is not the same as `first_mapping_index`, which **should** happen only in one case, when we are handling the first mapping, in that case `mapping->first_mapping_index == -1`, in all other cases, the other mappings after the first should have the condition `true`. - From above, we know that this is the first mapping, so if the offset is not `0`, then abort, since this is an invalid state. The issue was that `first_mapping_index` is not set if we are checking from the middle, the variable `first_mapping_index` is only set if we passed through the check `cluster_was_modified` with the first mapping, and in the same function call we checked the other mappings. One approach is to go into the loop even if `cluster_was_modified` is not true so that we will be able to set `first_mapping_index` for the first mapping, but since `first_mapping_index` is only used here, another approach is to just check manually for the `mapping->first_mapping_index != -1` since we know that this is the value for the only entry where `offset == 0` (i.e. first mapping). Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <b0fbca3ee208c565885838f6a7deeaeb23f4f9c2.1721470238.git.amjadsharafi10@gmail.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- block/vvfat.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/block/vvfat.c b/block/vvfat.c index cfde468c2e..e3a83fbc88 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1880,7 +1880,6 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch uint32_t cluster_num = begin_of_direntry(direntry); uint32_t offset = 0; - int first_mapping_index = -1; mapping_t* mapping = NULL; const char* basename2 = NULL; @@ -1942,14 +1941,9 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch if (strcmp(basename, basename2)) copy_it = 1; - first_mapping_index = array_index(&(s->mapping), mapping); - } - - if (mapping->first_mapping_index != first_mapping_index - && mapping->info.file.offset > 0) { - abort(); - copy_it = 1; } + assert(mapping->first_mapping_index == -1 + || mapping->info.file.offset > 0); /* need to write out? */ if (!was_modified && is_file(direntry)) { From 5eed3db336506b529b927ba221fe0d836e5b8819 Mon Sep 17 00:00:00 2001 From: Amjad Alsharafi <amjadsharafi10@gmail.com> Date: Sat, 20 Jul 2024 18:13:33 +0800 Subject: [PATCH 2725/2884] vvfat: Fix reading files with non-continuous clusters When reading with `read_cluster` we get the `mapping` with `find_mapping_for_cluster` and then we call `open_file` for this mapping. The issue appear when its the same file, but a second cluster that is not immediately after it, imagine clusters `500 -> 503`, this will give us 2 mappings one has the range `500..501` and another `503..504`, both point to the same file, but different offsets. When we don't open the file since the path is the same, we won't assign `s->current_mapping` and thus accessing way out of bound of the file. From our example above, after `open_file` (that didn't open anything) we will get the offset into the file with `s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will give us `0x2000 * (504-500)`, which is out of bound for this mapping and will produce some issues. Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com> Message-ID: <1f3ea115779abab62ba32c788073cdc99f9ad5dd.1721470238.git.amjadsharafi10@gmail.com> [kwolf: Simplified the patch based on Amjad's analysis and input] Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- block/vvfat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/vvfat.c b/block/vvfat.c index e3a83fbc88..8ffe8b3b9b 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1369,8 +1369,9 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping) return -1; vvfat_close_current_file(s); s->current_fd = fd; - s->current_mapping = mapping; } + + s->current_mapping = mapping; return 0; } From c8f60bfb4345ea8343a53eaefe88d47b44c53f24 Mon Sep 17 00:00:00 2001 From: Amjad Alsharafi <amjadsharafi10@gmail.com> Date: Sat, 20 Jul 2024 18:13:34 +0800 Subject: [PATCH 2726/2884] iotests: Add `vvfat` tests Added several tests to verify the implementation of the vvfat driver. We needed a way to interact with it, so created a basic `fat16.py` driver that handled writing correct sectors for us. Added `vvfat` to the non-generic formats, as its not a normal image format. Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Tested-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <bb8149c945301aefbdf470a0924c07f69f9c087d.1721470238.git.amjadsharafi10@gmail.com> [kwolf: Made mypy and pylint happy to unbreak 297] Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- tests/qemu-iotests/check | 2 +- tests/qemu-iotests/fat16.py | 690 +++++++++++++++++++++++++++++ tests/qemu-iotests/testenv.py | 2 +- tests/qemu-iotests/tests/vvfat | 485 ++++++++++++++++++++ tests/qemu-iotests/tests/vvfat.out | 5 + 5 files changed, 1182 insertions(+), 2 deletions(-) create mode 100644 tests/qemu-iotests/fat16.py create mode 100755 tests/qemu-iotests/tests/vvfat create mode 100755 tests/qemu-iotests/tests/vvfat.out diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 56d88ca423..545f9ec7bd 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -84,7 +84,7 @@ def make_argparser() -> argparse.ArgumentParser: p.set_defaults(imgfmt='raw', imgproto='file') format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2', - 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg'] + 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg', 'vvfat'] g_fmt = p.add_argument_group( ' image format options', 'The following options set the IMGFMT environment variable. ' diff --git a/tests/qemu-iotests/fat16.py b/tests/qemu-iotests/fat16.py new file mode 100644 index 0000000000..7d2d052413 --- /dev/null +++ b/tests/qemu-iotests/fat16.py @@ -0,0 +1,690 @@ +# A simple FAT16 driver that is used to test the `vvfat` driver in QEMU. +# +# Copyright (C) 2024 Amjad Alsharafi <amjadsharafi10@gmail.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from typing import Callable, List, Optional, Protocol, Set +import string + +SECTOR_SIZE = 512 +DIRENTRY_SIZE = 32 +ALLOWED_FILE_CHARS = set( + "!#$%&'()-@^_`{}~" + string.digits + string.ascii_uppercase +) + + +class MBR: + def __init__(self, data: bytes): + assert len(data) == 512 + self.partition_table = [] + for i in range(4): + partition = data[446 + i * 16 : 446 + (i + 1) * 16] + self.partition_table.append( + { + "status": partition[0], + "start_head": partition[1], + "start_sector": partition[2] & 0x3F, + "start_cylinder": ((partition[2] & 0xC0) << 2) + | partition[3], + "type": partition[4], + "end_head": partition[5], + "end_sector": partition[6] & 0x3F, + "end_cylinder": ((partition[6] & 0xC0) << 2) + | partition[7], + "start_lba": int.from_bytes(partition[8:12], "little"), + "size": int.from_bytes(partition[12:16], "little"), + } + ) + + def __str__(self): + return "\n".join( + [ + f"{i}: {partition}" + for i, partition in enumerate(self.partition_table) + ] + ) + + +class FatBootSector: + # pylint: disable=too-many-instance-attributes + def __init__(self, data: bytes): + assert len(data) == 512 + self.bytes_per_sector = int.from_bytes(data[11:13], "little") + self.sectors_per_cluster = data[13] + self.reserved_sectors = int.from_bytes(data[14:16], "little") + self.fat_count = data[16] + self.root_entries = int.from_bytes(data[17:19], "little") + total_sectors_16 = int.from_bytes(data[19:21], "little") + self.media_descriptor = data[21] + self.sectors_per_fat = int.from_bytes(data[22:24], "little") + self.sectors_per_track = int.from_bytes(data[24:26], "little") + self.heads = int.from_bytes(data[26:28], "little") + self.hidden_sectors = int.from_bytes(data[28:32], "little") + total_sectors_32 = int.from_bytes(data[32:36], "little") + assert ( + total_sectors_16 == 0 or total_sectors_32 == 0 + ), "Both total sectors (16 and 32) fields are non-zero" + self.total_sectors = total_sectors_16 or total_sectors_32 + self.drive_number = data[36] + self.volume_id = int.from_bytes(data[39:43], "little") + self.volume_label = data[43:54].decode("ascii").strip() + self.fs_type = data[54:62].decode("ascii").strip() + + def root_dir_start(self): + """ + Calculate the start sector of the root directory. + """ + return self.reserved_sectors + self.fat_count * self.sectors_per_fat + + def root_dir_size(self): + """ + Calculate the size of the root directory in sectors. + """ + return ( + self.root_entries * DIRENTRY_SIZE + self.bytes_per_sector - 1 + ) // self.bytes_per_sector + + def data_sector_start(self): + """ + Calculate the start sector of the data region. + """ + return self.root_dir_start() + self.root_dir_size() + + def first_sector_of_cluster(self, cluster: int) -> int: + """ + Calculate the first sector of the given cluster. + """ + return ( + self.data_sector_start() + (cluster - 2) * self.sectors_per_cluster + ) + + def cluster_bytes(self): + """ + Calculate the number of bytes in a cluster. + """ + return self.bytes_per_sector * self.sectors_per_cluster + + def __str__(self): + return ( + f"Bytes per sector: {self.bytes_per_sector}\n" + f"Sectors per cluster: {self.sectors_per_cluster}\n" + f"Reserved sectors: {self.reserved_sectors}\n" + f"FAT count: {self.fat_count}\n" + f"Root entries: {self.root_entries}\n" + f"Total sectors: {self.total_sectors}\n" + f"Media descriptor: {self.media_descriptor}\n" + f"Sectors per FAT: {self.sectors_per_fat}\n" + f"Sectors per track: {self.sectors_per_track}\n" + f"Heads: {self.heads}\n" + f"Hidden sectors: {self.hidden_sectors}\n" + f"Drive number: {self.drive_number}\n" + f"Volume ID: {self.volume_id}\n" + f"Volume label: {self.volume_label}\n" + f"FS type: {self.fs_type}\n" + ) + + +class FatDirectoryEntry: + # pylint: disable=too-many-instance-attributes + def __init__(self, data: bytes, sector: int, offset: int): + self.name = data[0:8].decode("ascii").strip() + self.ext = data[8:11].decode("ascii").strip() + self.attributes = data[11] + self.reserved = data[12] + self.create_time_tenth = data[13] + self.create_time = int.from_bytes(data[14:16], "little") + self.create_date = int.from_bytes(data[16:18], "little") + self.last_access_date = int.from_bytes(data[18:20], "little") + high_cluster = int.from_bytes(data[20:22], "little") + self.last_mod_time = int.from_bytes(data[22:24], "little") + self.last_mod_date = int.from_bytes(data[24:26], "little") + low_cluster = int.from_bytes(data[26:28], "little") + self.cluster = (high_cluster << 16) | low_cluster + self.size_bytes = int.from_bytes(data[28:32], "little") + + # extra (to help write back to disk) + self.sector = sector + self.offset = offset + + def as_bytes(self) -> bytes: + return ( + self.name.ljust(8, " ").encode("ascii") + + self.ext.ljust(3, " ").encode("ascii") + + self.attributes.to_bytes(1, "little") + + self.reserved.to_bytes(1, "little") + + self.create_time_tenth.to_bytes(1, "little") + + self.create_time.to_bytes(2, "little") + + self.create_date.to_bytes(2, "little") + + self.last_access_date.to_bytes(2, "little") + + (self.cluster >> 16).to_bytes(2, "little") + + self.last_mod_time.to_bytes(2, "little") + + self.last_mod_date.to_bytes(2, "little") + + (self.cluster & 0xFFFF).to_bytes(2, "little") + + self.size_bytes.to_bytes(4, "little") + ) + + def whole_name(self): + if self.ext: + return f"{self.name}.{self.ext}" + else: + return self.name + + def __str__(self): + return ( + f"Name: {self.name}\n" + f"Ext: {self.ext}\n" + f"Attributes: {self.attributes}\n" + f"Reserved: {self.reserved}\n" + f"Create time tenth: {self.create_time_tenth}\n" + f"Create time: {self.create_time}\n" + f"Create date: {self.create_date}\n" + f"Last access date: {self.last_access_date}\n" + f"Last mod time: {self.last_mod_time}\n" + f"Last mod date: {self.last_mod_date}\n" + f"Cluster: {self.cluster}\n" + f"Size: {self.size_bytes}\n" + ) + + def __repr__(self): + # convert to dict + return str(vars(self)) + + +class SectorReader(Protocol): + def __call__(self, start_sector: int, num_sectors: int = 1) -> bytes: ... + +# pylint: disable=broad-exception-raised +class Fat16: + def __init__( + self, + start_sector: int, + size: int, + sector_reader: SectorReader, + sector_writer: Callable[[int, bytes], None] + ): + self.start_sector = start_sector + self.size_in_sectors = size + self.sector_reader = sector_reader + self.sector_writer = sector_writer + + self.boot_sector = FatBootSector(self.sector_reader(start_sector, 1)) + + fat_size_in_sectors = ( + self.boot_sector.sectors_per_fat * self.boot_sector.fat_count + ) + self.fats = self.read_sectors( + self.boot_sector.reserved_sectors, fat_size_in_sectors + ) + self.fats_dirty_sectors: Set[int] = set() + + def read_sectors(self, start_sector: int, num_sectors: int) -> bytes: + return self.sector_reader(start_sector + self.start_sector, + num_sectors) + + def write_sectors(self, start_sector: int, data: bytes) -> None: + return self.sector_writer(start_sector + self.start_sector, data) + + def directory_from_bytes( + self, data: bytes, start_sector: int + ) -> List[FatDirectoryEntry]: + """ + Convert `bytes` into a list of `FatDirectoryEntry` objects. + Will ignore long file names. + Will stop when it encounters a 0x00 byte. + """ + + entries = [] + for i in range(0, len(data), DIRENTRY_SIZE): + entry = data[i : i + DIRENTRY_SIZE] + + current_sector = start_sector + (i // SECTOR_SIZE) + current_offset = i % SECTOR_SIZE + + if entry[0] == 0: + break + + if entry[0] == 0xE5: + # Deleted file + continue + + if entry[11] & 0xF == 0xF: + # Long file name + continue + + entries.append( + FatDirectoryEntry(entry, current_sector, current_offset) + ) + return entries + + def read_root_directory(self) -> List[FatDirectoryEntry]: + root_dir = self.read_sectors( + self.boot_sector.root_dir_start(), self.boot_sector.root_dir_size() + ) + return self.directory_from_bytes( + root_dir, self.boot_sector.root_dir_start() + ) + + def read_fat_entry(self, cluster: int) -> int: + """ + Read the FAT entry for the given cluster. + """ + fat_offset = cluster * 2 # FAT16 + return int.from_bytes(self.fats[fat_offset : fat_offset + 2], "little") + + def write_fat_entry(self, cluster: int, value: int) -> None: + """ + Write the FAT entry for the given cluster. + """ + fat_offset = cluster * 2 + self.fats = ( + self.fats[:fat_offset] + + value.to_bytes(2, "little") + + self.fats[fat_offset + 2 :] + ) + self.fats_dirty_sectors.add(fat_offset // SECTOR_SIZE) + + def flush_fats(self) -> None: + """ + Write the FATs back to the disk. + """ + for sector in self.fats_dirty_sectors: + data = self.fats[sector * SECTOR_SIZE : (sector + 1) * SECTOR_SIZE] + sector = self.boot_sector.reserved_sectors + sector + self.write_sectors(sector, data) + self.fats_dirty_sectors = set() + + def next_cluster(self, cluster: int) -> Optional[int]: + """ + Get the next cluster in the chain. + If its `None`, then its the last cluster. + The function will crash if the next cluster + is `FREE` (unexpected) or invalid entry. + """ + fat_entry = self.read_fat_entry(cluster) + if fat_entry == 0: + raise Exception("Unexpected: FREE cluster") + if fat_entry == 1: + raise Exception("Unexpected: RESERVED cluster") + if fat_entry >= 0xFFF8: + return None + if fat_entry >= 0xFFF7: + raise Exception("Invalid FAT entry") + + return fat_entry + + def next_free_cluster(self) -> int: + """ + Find the next free cluster. + """ + # simple linear search + for i in range(2, 0xFFFF): + if self.read_fat_entry(i) == 0: + return i + raise Exception("No free clusters") + + def next_free_cluster_non_continuous(self) -> int: + """ + Find the next free cluster, but makes sure + that the cluster before and after it are not allocated. + """ + # simple linear search + before = False + for i in range(2, 0xFFFF): + if self.read_fat_entry(i) == 0: + if before and self.read_fat_entry(i + 1) == 0: + return i + else: + before = True + else: + before = False + + raise Exception("No free clusters") + + def read_cluster(self, cluster: int) -> bytes: + """ + Read the cluster at the given cluster. + """ + return self.read_sectors( + self.boot_sector.first_sector_of_cluster(cluster), + self.boot_sector.sectors_per_cluster, + ) + + def write_cluster(self, cluster: int, data: bytes) -> None: + """ + Write the cluster at the given cluster. + """ + assert len(data) == self.boot_sector.cluster_bytes() + self.write_sectors( + self.boot_sector.first_sector_of_cluster(cluster), + data, + ) + + def read_directory( + self, cluster: Optional[int] + ) -> List[FatDirectoryEntry]: + """ + Read the directory at the given cluster. + """ + entries = [] + while cluster is not None: + data = self.read_cluster(cluster) + entries.extend( + self.directory_from_bytes( + data, self.boot_sector.first_sector_of_cluster(cluster) + ) + ) + cluster = self.next_cluster(cluster) + return entries + + def add_direntry( + self, cluster: Optional[int], name: str, ext: str, attributes: int + ) -> FatDirectoryEntry: + """ + Add a new directory entry to the given cluster. + If the cluster is `None`, then it will be added to the root directory. + """ + + def find_free_entry(data: bytes) -> Optional[int]: + for i in range(0, len(data), DIRENTRY_SIZE): + entry = data[i : i + DIRENTRY_SIZE] + if entry[0] == 0 or entry[0] == 0xE5: + return i + return None + + assert len(name) <= 8, "Name must be 8 characters or less" + assert len(ext) <= 3, "Ext must be 3 characters or less" + assert attributes % 0x15 != 0x15, "Invalid attributes" + + # initial dummy data + new_entry = FatDirectoryEntry(b"\0" * 32, 0, 0) + new_entry.name = name.ljust(8, " ") + new_entry.ext = ext.ljust(3, " ") + new_entry.attributes = attributes + new_entry.reserved = 0 + new_entry.create_time_tenth = 0 + new_entry.create_time = 0 + new_entry.create_date = 0 + new_entry.last_access_date = 0 + new_entry.last_mod_time = 0 + new_entry.last_mod_date = 0 + new_entry.cluster = self.next_free_cluster() + new_entry.size_bytes = 0 + + # mark as EOF + self.write_fat_entry(new_entry.cluster, 0xFFFF) + + if cluster is None: + for i in range(self.boot_sector.root_dir_size()): + sector_data = self.read_sectors( + self.boot_sector.root_dir_start() + i, 1 + ) + offset = find_free_entry(sector_data) + if offset is not None: + new_entry.sector = self.boot_sector.root_dir_start() + i + new_entry.offset = offset + self.update_direntry(new_entry) + return new_entry + else: + while cluster is not None: + data = self.read_cluster(cluster) + offset = find_free_entry(data) + if offset is not None: + new_entry.sector = ( + self.boot_sector.first_sector_of_cluster(cluster) + + (offset // SECTOR_SIZE)) + new_entry.offset = offset % SECTOR_SIZE + self.update_direntry(new_entry) + return new_entry + cluster = self.next_cluster(cluster) + + raise Exception("No free directory entries") + + def update_direntry(self, entry: FatDirectoryEntry) -> None: + """ + Write the directory entry back to the disk. + """ + sector = self.read_sectors(entry.sector, 1) + sector = ( + sector[: entry.offset] + + entry.as_bytes() + + sector[entry.offset + DIRENTRY_SIZE :] + ) + self.write_sectors(entry.sector, sector) + + def find_direntry(self, path: str) -> Optional[FatDirectoryEntry]: + """ + Find the directory entry for the given path. + """ + assert path[0] == "/", "Path must start with /" + + path = path[1:] # remove the leading / + parts = path.split("/") + directory = self.read_root_directory() + + current_entry = None + + for i, part in enumerate(parts): + is_last = i == len(parts) - 1 + + for entry in directory: + if entry.whole_name() == part: + current_entry = entry + break + if current_entry is None: + return None + + if is_last: + return current_entry + + if current_entry.attributes & 0x10 == 0: + raise Exception( + f"{current_entry.whole_name()} is not a directory" + ) + + directory = self.read_directory(current_entry.cluster) + + assert False, "Exited loop with is_last == False" + + def read_file(self, entry: Optional[FatDirectoryEntry]) -> Optional[bytes]: + """ + Read the content of the file at the given path. + """ + if entry is None: + return None + if entry.attributes & 0x10 != 0: + raise Exception(f"{entry.whole_name()} is a directory") + + data = b"" + cluster: Optional[int] = entry.cluster + while cluster is not None and len(data) <= entry.size_bytes: + data += self.read_cluster(cluster) + cluster = self.next_cluster(cluster) + return data[: entry.size_bytes] + + def truncate_file( + self, + entry: FatDirectoryEntry, + new_size: int, + allocate_non_continuous: bool = False, + ) -> None: + """ + Truncate the file at the given path to the new size. + """ + if entry is None: + raise Exception("entry is None") + if entry.attributes & 0x10 != 0: + raise Exception(f"{entry.whole_name()} is a directory") + + def clusters_from_size(size: int) -> int: + return ( + size + self.boot_sector.cluster_bytes() - 1 + ) // self.boot_sector.cluster_bytes() + + # First, allocate new FATs if we need to + required_clusters = clusters_from_size(new_size) + current_clusters = clusters_from_size(entry.size_bytes) + + affected_clusters = set() + + # Keep at least one cluster, easier to manage this way + if required_clusters == 0: + required_clusters = 1 + if current_clusters == 0: + current_clusters = 1 + + cluster: Optional[int] + + if required_clusters > current_clusters: + # Allocate new clusters + cluster = entry.cluster + to_add = required_clusters + for _ in range(current_clusters - 1): + to_add -= 1 + assert cluster is not None, "Cluster is None" + affected_clusters.add(cluster) + cluster = self.next_cluster(cluster) + assert required_clusters > 0, "No new clusters to allocate" + assert cluster is not None, "Cluster is None" + assert ( + self.next_cluster(cluster) is None + ), "Cluster is not the last cluster" + + # Allocate new clusters + for _ in range(to_add - 1): + if allocate_non_continuous: + new_cluster = self.next_free_cluster_non_continuous() + else: + new_cluster = self.next_free_cluster() + self.write_fat_entry(cluster, new_cluster) + self.write_fat_entry(new_cluster, 0xFFFF) + cluster = new_cluster + + elif required_clusters < current_clusters: + # Truncate the file + cluster = entry.cluster + for _ in range(required_clusters - 1): + assert cluster is not None, "Cluster is None" + cluster = self.next_cluster(cluster) + assert cluster is not None, "Cluster is None" + + next_cluster = self.next_cluster(cluster) + # mark last as EOF + self.write_fat_entry(cluster, 0xFFFF) + # free the rest + while next_cluster is not None: + cluster = next_cluster + next_cluster = self.next_cluster(next_cluster) + self.write_fat_entry(cluster, 0) + + self.flush_fats() + + # verify number of clusters + cluster = entry.cluster + count = 0 + while cluster is not None: + count += 1 + affected_clusters.add(cluster) + cluster = self.next_cluster(cluster) + assert ( + count == required_clusters + ), f"Expected {required_clusters} clusters, got {count}" + + # update the size + entry.size_bytes = new_size + self.update_direntry(entry) + + # trigger every affected cluster + for cluster in affected_clusters: + first_sector = self.boot_sector.first_sector_of_cluster(cluster) + first_sector_data = self.read_sectors(first_sector, 1) + self.write_sectors(first_sector, first_sector_data) + + def write_file(self, entry: FatDirectoryEntry, data: bytes) -> None: + """ + Write the content of the file at the given path. + """ + if entry is None: + raise Exception("entry is None") + if entry.attributes & 0x10 != 0: + raise Exception(f"{entry.whole_name()} is a directory") + + data_len = len(data) + + self.truncate_file(entry, data_len) + + cluster: Optional[int] = entry.cluster + while cluster is not None: + data_to_write = data[: self.boot_sector.cluster_bytes()] + if len(data_to_write) < self.boot_sector.cluster_bytes(): + old_data = self.read_cluster(cluster) + data_to_write += old_data[len(data_to_write) :] + + self.write_cluster(cluster, data_to_write) + data = data[self.boot_sector.cluster_bytes() :] + if len(data) == 0: + break + cluster = self.next_cluster(cluster) + + assert ( + len(data) == 0 + ), "Data was not written completely, clusters missing" + + def create_file(self, path: str) -> Optional[FatDirectoryEntry]: + """ + Create a new file at the given path. + """ + assert path[0] == "/", "Path must start with /" + + path = path[1:] # remove the leading / + + parts = path.split("/") + + directory_cluster = None + directory = self.read_root_directory() + + parts, filename = parts[:-1], parts[-1] + + for _, part in enumerate(parts): + current_entry = None + for entry in directory: + if entry.whole_name() == part: + current_entry = entry + break + if current_entry is None: + return None + + if current_entry.attributes & 0x10 == 0: + raise Exception( + f"{current_entry.whole_name()} is not a directory" + ) + + directory = self.read_directory(current_entry.cluster) + directory_cluster = current_entry.cluster + + # add new entry to the directory + + filename, ext = filename.split(".") + + if len(ext) > 3: + raise Exception("Ext must be 3 characters or less") + if len(filename) > 8: + raise Exception("Name must be 8 characters or less") + + for c in filename + ext: + + if c not in ALLOWED_FILE_CHARS: + raise Exception("Invalid character in filename") + + return self.add_direntry(directory_cluster, filename, ext, 0) diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 96d69e5696..c8848f2ec2 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -255,7 +255,7 @@ class TestEnv(ContextManager['TestEnv']): self.qemu_img_options = os.getenv('QEMU_IMG_OPTIONS') self.qemu_nbd_options = os.getenv('QEMU_NBD_OPTIONS') - is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg'] + is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg', 'vvfat'] self.imgfmt_generic = 'true' if is_generic else 'false' self.qemu_io_options = f'--cache {self.cachemode} --aio {self.aiomode}' diff --git a/tests/qemu-iotests/tests/vvfat b/tests/qemu-iotests/tests/vvfat new file mode 100755 index 0000000000..acdc6ce8ff --- /dev/null +++ b/tests/qemu-iotests/tests/vvfat @@ -0,0 +1,485 @@ +#!/usr/bin/env python3 +# group: rw vvfat +# +# Test vvfat driver implementation +# Here, we use a simple FAT16 implementation and check the behavior of +# the vvfat driver. +# +# Copyright (C) 2024 Amjad Alsharafi <amjadsharafi10@gmail.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import shutil +import iotests +from iotests import imgfmt, QMPTestCase +from fat16 import MBR, Fat16, DIRENTRY_SIZE + +filesystem = os.path.join(iotests.test_dir, "filesystem") + +nbd_sock = iotests.file_path("nbd.sock", base_dir=iotests.sock_dir) +nbd_uri = "nbd+unix:///disk?socket=" + nbd_sock + +SECTOR_SIZE = 512 + + +class TestVVFatDriver(QMPTestCase): + # pylint: disable=broad-exception-raised + def setUp(self) -> None: + if os.path.exists(filesystem): + if os.path.isdir(filesystem): + shutil.rmtree(filesystem) + else: + raise Exception(f"{filesystem} exists and is not a directory") + + os.mkdir(filesystem) + + # Add some text files to the filesystem + for i in range(10): + with open(os.path.join(filesystem, f"file{i}.txt"), + "w", encoding="ascii") as f: + f.write(f"Hello, world! {i}\n") + + # Add 2 large files, above the cluster size (8KB) + with open(os.path.join(filesystem, "large1.txt"), "wb") as f: + # write 'A' * 1KB, 'B' * 1KB, 'C' * 1KB, ... + for i in range(8 * 2): # two clusters + f.write(bytes([0x41 + i] * 1024)) + + with open(os.path.join(filesystem, "large2.txt"), "wb") as f: + # write 'A' * 1KB, 'B' * 1KB, 'C' * 1KB, ... + for i in range(8 * 3): # 3 clusters + f.write(bytes([0x41 + i] * 1024)) + + self.vm = iotests.VM() + + self.vm.add_blockdev( + self.vm.qmp_to_opts( + { + "driver": imgfmt, + "node-name": "disk", + "rw": "true", + "fat-type": "16", + "dir": filesystem, + } + ) + ) + + self.vm.launch() + + self.vm.qmp_log("block-dirty-bitmap-add", **{ + "node": "disk", + "name": "bitmap0", + }) + + # attach nbd server + self.vm.qmp_log( + "nbd-server-start", + **{"addr": {"type": "unix", "data": {"path": nbd_sock}}}, + filters=[], + ) + + self.vm.qmp_log( + "nbd-server-add", + **{"device": "disk", "writable": True, "bitmap": "bitmap0"}, + ) + + self.qio = iotests.QemuIoInteractive("-f", "raw", nbd_uri) + + def tearDown(self) -> None: + self.qio.close() + self.vm.shutdown() + # print(self.vm.get_log()) + shutil.rmtree(filesystem) + + def read_sectors(self, sector: int, num: int = 1) -> bytes: + """ + Read `num` sectors starting from `sector` from the `disk`. + This uses `QemuIoInteractive` to read the sectors into `stdout` and + then parse the output. + """ + self.assertGreater(num, 0) + + # The output contains the content of the sector in hex dump format + # We need to extract the content from it + output = self.qio.cmd( + f"read -v {sector * SECTOR_SIZE} {num * SECTOR_SIZE}") + + # Each row is 16 bytes long, and we are writing `num` sectors + rows = num * SECTOR_SIZE // 16 + output_rows = output.split("\n")[:rows] + + hex_content = "".join( + [(row.split(": ")[1]).split(" ")[0] for row in output_rows] + ) + bytes_content = bytes.fromhex(hex_content) + + self.assertEqual(len(bytes_content), num * SECTOR_SIZE) + + return bytes_content + + def write_sectors(self, sector: int, data: bytes) -> None: + """ + Write `data` to the `disk` starting from `sector`. + This uses `QemuIoInteractive` to write the data into the disk. + """ + + self.assertGreater(len(data), 0) + self.assertEqual(len(data) % SECTOR_SIZE, 0) + + temp_file = os.path.join(iotests.test_dir, "temp.bin") + with open(temp_file, "wb") as f: + f.write(data) + + self.qio.cmd( + f"write -s {temp_file} {sector * SECTOR_SIZE} {len(data)}" + ) + + os.remove(temp_file) + + def init_fat16(self): + mbr = MBR(self.read_sectors(0)) + return Fat16( + mbr.partition_table[0]["start_lba"], + mbr.partition_table[0]["size"], + self.read_sectors, + self.write_sectors, + ) + + # Tests + + def test_fat_filesystem(self): + """ + Test that vvfat produce a valid FAT16 and MBR sectors + """ + mbr = MBR(self.read_sectors(0)) + + self.assertEqual(mbr.partition_table[0]["status"], 0x80) + self.assertEqual(mbr.partition_table[0]["type"], 6) + + fat16 = Fat16( + mbr.partition_table[0]["start_lba"], + mbr.partition_table[0]["size"], + self.read_sectors, + self.write_sectors, + ) + self.assertEqual(fat16.boot_sector.bytes_per_sector, 512) + self.assertEqual(fat16.boot_sector.volume_label, "QEMU VVFAT") + + def test_read_root_directory(self): + """ + Test the content of the root directory + """ + fat16 = self.init_fat16() + + root_dir = fat16.read_root_directory() + + self.assertEqual(len(root_dir), 13) # 12 + 1 special file + + files = { + "QEMU VVF.AT": 0, # special empty file + "FILE0.TXT": 16, + "FILE1.TXT": 16, + "FILE2.TXT": 16, + "FILE3.TXT": 16, + "FILE4.TXT": 16, + "FILE5.TXT": 16, + "FILE6.TXT": 16, + "FILE7.TXT": 16, + "FILE8.TXT": 16, + "FILE9.TXT": 16, + "LARGE1.TXT": 0x2000 * 2, + "LARGE2.TXT": 0x2000 * 3, + } + + for entry in root_dir: + self.assertIn(entry.whole_name(), files) + self.assertEqual(entry.size_bytes, files[entry.whole_name()]) + + def test_direntry_as_bytes(self): + """ + Test if we can convert Direntry back to bytes, so that we can write it + back to the disk safely. + """ + fat16 = self.init_fat16() + + root_dir = fat16.read_root_directory() + first_entry_bytes = fat16.read_sectors( + fat16.boot_sector.root_dir_start(), 1) + + # The first entry won't be deleted, so we can compare it with the first + # entry in the root directory + self.assertEqual(root_dir[0].as_bytes(), + first_entry_bytes[:DIRENTRY_SIZE]) + + def test_read_files(self): + """ + Test reading the content of the files + """ + fat16 = self.init_fat16() + + for i in range(10): + file = fat16.find_direntry(f"/FILE{i}.TXT") + self.assertIsNotNone(file) + self.assertEqual( + fat16.read_file(file), f"Hello, world! {i}\n".encode("ascii") + ) + + # test large files + large1 = fat16.find_direntry("/LARGE1.TXT") + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(fat16.read_file(large1), f.read()) + + large2 = fat16.find_direntry("/LARGE2.TXT") + self.assertIsNotNone(large2) + with open(os.path.join(filesystem, "large2.txt"), "rb") as f: + self.assertEqual(fat16.read_file(large2), f.read()) + + def test_write_file_same_content_direct(self): + """ + Similar to `test_write_file_in_same_content`, but we write the file + directly clusters and thus we don't go through the modification of + direntry. + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + data = fat16.read_cluster(file.cluster) + fat16.write_cluster(file.cluster, data) + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(fat16.read_file(file), f.read()) + + def test_write_file_in_same_content(self): + """ + Test writing the same content to the file back to it + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.write_file(file, b"Hello, world! 0\n") + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), b"Hello, world! 0\n") + + def test_modify_content_same_clusters(self): + """ + Test modifying the content of the file without changing the number of + clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + new_content = b"Hello, world! Modified\n" + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_truncate_file_same_clusters_less(self): + """ + Test truncating the file without changing number of clusters + Test decreasing the file size + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.truncate_file(file, 5) + new_content = fat16.read_file(file) + self.assertEqual(new_content, b"Hello") + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_truncate_file_same_clusters_more(self): + """ + Test truncating the file without changing number of clusters + Test increase the file size + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.truncate_file(file, 20) + new_content = fat16.read_file(file) + self.assertIsNotNone(new_content) + + # random pattern will be appended to the file, and its not always the + # same + self.assertEqual(new_content[:16], b"Hello, world! 0\n") + self.assertEqual(len(new_content), 20) + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_large_file(self): + """ + Test writing a large file + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + # The content of LARGE1 is A * 1KB, B * 1KB, C * 1KB, ..., P * 1KB + # Lets change it to be Z * 1KB, Y * 1KB, X * 1KB, ..., K * 1KB + # without changing the number of clusters or filesize + new_content = b"".join([bytes([0x5A - i] * 1024) for i in range(16)]) + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_truncate_file_change_clusters_less(self): + """ + Test truncating a file by reducing the number of clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + fat16.truncate_file(file, 1) + self.assertEqual(fat16.read_file(file), b"A") + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), b"A") + + def test_write_file_change_clusters_less(self): + """ + Test truncating a file by reducing the number of clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE2.TXT") + self.assertIsNotNone(file) + + new_content = b"X" * 8 * 1024 + b"Y" * 8 * 1024 + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large2.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_file_change_clusters_more(self): + """ + Test truncating a file by increasing the number of clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE2.TXT") + self.assertIsNotNone(file) + + # from 3 clusters to 4 clusters + new_content = ( + b"W" * 8 * 1024 + + b"X" * 8 * 1024 + + b"Y" * 8 * 1024 + + b"Z" * 8 * 1024 + ) + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large2.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_file_change_clusters_more_non_contiguous_2_mappings(self): + """ + Test truncating a file by increasing the number of clusters Here we + allocate the new clusters in a way that makes them non-contiguous so + that we will get 2 cluster mappings for the file + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + # from 2 clusters to 3 clusters with non-contiguous allocation + fat16.truncate_file(file, 3 * 0x2000, allocate_non_continuous=True) + new_content = b"X" * 8 * 1024 + b"Y" * 8 * 1024 + b"Z" * 8 * 1024 + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_file_change_clusters_more_non_contiguous_3_mappings(self): + """ + Test truncating a file by increasing the number of clusters Here we + allocate the new clusters in a way that makes them non-contiguous so + that we will get 3 cluster mappings for the file + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + # from 2 clusters to 4 clusters with non-contiguous allocation + fat16.truncate_file(file, 4 * 0x2000, allocate_non_continuous=True) + new_content = ( + b"W" * 8 * 1024 + + b"X" * 8 * 1024 + + b"Y" * 8 * 1024 + + b"Z" * 8 * 1024 + ) + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_create_file(self): + """ + Test creating a new file + """ + fat16 = self.init_fat16() + + new_file = fat16.create_file("/NEWFILE.TXT") + + self.assertIsNotNone(new_file) + self.assertEqual(new_file.size_bytes, 0) + + new_content = b"Hello, world! New file\n" + fat16.write_file(new_file, new_content) + self.assertEqual(fat16.read_file(new_file), new_content) + + with open(os.path.join(filesystem, "newfile.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + # TODO: support deleting files + + +if __name__ == "__main__": + # This is a specific test for vvfat driver + iotests.main(supported_fmts=["vvfat"], supported_protocols=["file"]) diff --git a/tests/qemu-iotests/tests/vvfat.out b/tests/qemu-iotests/tests/vvfat.out new file mode 100755 index 0000000000..b6f257674e --- /dev/null +++ b/tests/qemu-iotests/tests/vvfat.out @@ -0,0 +1,5 @@ +................ +---------------------------------------------------------------------- +Ran 16 tests + +OK From ca1dcc913888a97073233dd9142ca5241dab1b7c Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> Date: Tue, 30 Jul 2024 12:47:01 +0300 Subject: [PATCH 2727/2884] iotests/024: exclude 'backing file format' field from the output Apparently 'qemu-img info' doesn't report the backing file format field for qed (as it does for qcow2): $ qemu-img create -f qed base.qed 1M && qemu-img create -f qed -b base.qed -F qed top.qed 1M $ qemu-img create -f qcow2 base.qcow2 1M && qemu-img create -f qcow2 -b base.qcow2 -F qcow2 top.qcow2 1M $ qemu-img info top.qed | grep 'backing file format' $ qemu-img info top.qcow2 | grep 'backing file format' backing file format: qcow2 This leads to the 024 test failure with -qed. Let's just filter the field out and exclude it from the output. This is a fixup for the commit f93e65ee51 ("iotests/{024, 271}: add testcases for qemu-img rebase"). Reported-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> Message-ID: <20240730094701.790624-1-andrey.drobyshev@virtuozzo.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- tests/qemu-iotests/024 | 2 +- tests/qemu-iotests/024.out | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 285f17e79f..b29c76e161 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -283,7 +283,7 @@ TEST_IMG=$BASE_OLD _make_test_img -b "$BASE_NEW" -F $IMGFMT \ CLUSTER_SIZE=$(( CLUSTER_SIZE * 2 )) TEST_IMG=$OVERLAY \ _make_test_img -b "$BASE_OLD" -F $IMGFMT $(( CLUSTER_SIZE * 6 )) -TEST_IMG=$OVERLAY _img_info +TEST_IMG=$OVERLAY _img_info | grep -v '^backing file format:' echo echo "Fill backing files with data" diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index e1e8eea863..3d1e31927a 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -214,7 +214,6 @@ file format: IMGFMT virtual size: 384 KiB (393216 bytes) cluster_size: 131072 backing file: TEST_DIR/subdir/t.IMGFMT.base_old -backing file format: IMGFMT Fill backing files with data From 682a05280504d2fab32e16096b58d7ea068435c2 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Wed, 7 Aug 2024 04:04:18 +0000 Subject: [PATCH 2728/2884] tcg/ppc: Sync tcg_out_test and constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure the code structure is the same for matching constraints and emitting code, lest we allow constants that cannot be trivially tested. Cc: qemu-stable@nongnu.org Fixes: ad788aebbab ("tcg/ppc: Support TCG_COND_TST{EQ,NE}") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2487 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <44328324-af73-4439-9d2b-d414e0e13dd7@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tcg/ppc/tcg-target.c.inc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 7f3829beeb..3553a47ba9 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -325,9 +325,11 @@ static bool tcg_target_const_match(int64_t sval, int ct, if ((uval & ~0xffff) == 0 || (uval & ~0xffff0000ull) == 0) { return 1; } - if (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32 - ? mask_operand(uval, &mb, &me) - : mask64_operand(uval << clz64(uval), &mb, &me)) { + if (uval == (uint32_t)uval && mask_operand(uval, &mb, &me)) { + return 1; + } + if (TCG_TARGET_REG_BITS == 64 && + mask64_operand(uval << clz64(uval), &mb, &me)) { return 1; } return 0; @@ -1749,8 +1751,6 @@ static void tcg_out_test(TCGContext *s, TCGReg dest, TCGReg arg1, TCGArg arg2, if (type == TCG_TYPE_I32) { arg2 = (uint32_t)arg2; - } else if (arg2 == (uint32_t)arg2) { - type = TCG_TYPE_I32; } if ((arg2 & ~0xffff) == 0) { @@ -1761,12 +1761,11 @@ static void tcg_out_test(TCGContext *s, TCGReg dest, TCGReg arg1, TCGArg arg2, tcg_out32(s, ANDIS | SAI(arg1, dest, arg2 >> 16)); return; } - if (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32) { - if (mask_operand(arg2, &mb, &me)) { - tcg_out_rlw_rc(s, RLWINM, dest, arg1, 0, mb, me, rc); - return; - } - } else { + if (arg2 == (uint32_t)arg2 && mask_operand(arg2, &mb, &me)) { + tcg_out_rlw_rc(s, RLWINM, dest, arg1, 0, mb, me, rc); + return; + } + if (TCG_TARGET_REG_BITS == 64) { int sh = clz64(arg2); if (mask64_operand(arg2 << sh, &mb, &me)) { tcg_out_rld_rc(s, RLDICR, dest, arg1, sh, me, rc); From 6146060a6891848f854b0ed1e46e020a6fdc77c2 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino <quic_mathbern@quicinc.com> Date: Mon, 29 Jul 2024 16:24:06 -0300 Subject: [PATCH 2729/2884] Hexagon: fix F2_conv_* instructions for negative zero The implementation for these instructions handles -0 as an invalid float point value, whereas the Hexagon hardware considers it the same as +0 (which is valid). Let's fix that and add a regression test. Signed-off-by: Matheus Tavares Bernardino <quic_mathbern@quicinc.com> Reviewed-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain <bcain@quicinc.com> --- target/hexagon/op_helper.c | 18 +++++++++--------- tests/tcg/hexagon/usr.c | 12 +++++++++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index ae5a605513..90e7aaa097 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -683,7 +683,7 @@ uint32_t HELPER(conv_sf2uw)(CPUHexagonState *env, float32 RsV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -713,7 +713,7 @@ uint64_t HELPER(conv_sf2ud)(CPUHexagonState *env, float32 RsV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -743,7 +743,7 @@ uint32_t HELPER(conv_df2uw)(CPUHexagonState *env, float64 RssV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -773,7 +773,7 @@ uint64_t HELPER(conv_df2ud)(CPUHexagonState *env, float64 RssV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -803,7 +803,7 @@ uint32_t HELPER(conv_sf2uw_chop)(CPUHexagonState *env, float32 RsV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -833,7 +833,7 @@ uint64_t HELPER(conv_sf2ud_chop)(CPUHexagonState *env, float32 RsV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -863,7 +863,7 @@ uint32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -893,7 +893,7 @@ uint64_t HELPER(conv_df2ud_chop)(CPUHexagonState *env, float64 RssV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { diff --git a/tests/tcg/hexagon/usr.c b/tests/tcg/hexagon/usr.c index 92bc86a213..f0b23d312b 100644 --- a/tests/tcg/hexagon/usr.c +++ b/tests/tcg/hexagon/usr.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1007,6 +1007,11 @@ int main() TEST_P_OP_R(conv_sf2d_chop, SF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); TEST_P_OP_R(conv_sf2d_chop, SF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_R_OP_R(conv_sf2uw, SF_zero_neg, 0, USR_CLEAR); + TEST_R_OP_R(conv_sf2uw_chop, SF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_R(conv_sf2ud, SF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_R(conv_sf2ud_chop, SF_zero_neg, 0, USR_CLEAR); + TEST_R_OP_P(conv_df2sf, DF_QNaN, SF_HEX_NaN, USR_CLEAR); TEST_R_OP_P(conv_df2sf, DF_SNaN, SF_HEX_NaN, USR_FPINVF); TEST_R_OP_P(conv_df2uw, DF_QNaN, 0xffffffff, USR_FPINVF); @@ -1020,6 +1025,11 @@ int main() TEST_R_OP_P(conv_df2uw_chop, DF_QNaN, 0xffffffff, USR_FPINVF); TEST_R_OP_P(conv_df2uw_chop, DF_SNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2uw, DF_zero_neg, 0, USR_CLEAR); + TEST_R_OP_P(conv_df2uw_chop, DF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_P(conv_df2ud, DF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_P(conv_df2ud_chop, DF_zero_neg, 0, USR_CLEAR); + /* Test for typo in HELPER(conv_df2uw_chop) */ TEST_R_OP_P(conv_df2uw_chop, 0xffffff7f00000001ULL, 0xffffffff, USR_FPINVF); From 523e45ac5b881237bd03934751d44767e5716620 Mon Sep 17 00:00:00 2001 From: Taylor Simpson <ltaylorsimpson@gmail.com> Date: Thu, 13 Jun 2024 12:22:09 -0600 Subject: [PATCH 2730/2884] Hexagon: lldb read/write predicate registers p0/p1/p2/p3 hexagon-core.xml only exposes register p3_0 which is an alias that aggregates the predicate registers. It is more convenient for users to interact directly with the predicate registers. Tested with lldb downloaded from this location https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.4/clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04.tar.xz BEFORE: (lldb) reg read p3_0 p3_0 = 0x00000000 (lldb) reg read p0 error: Invalid register name 'p0'. (lldb) reg write p1 0xf error: Register not found for 'p1'. AFTER: (lldb) reg read p3_0 p3_0 = 0x00000000 (lldb) reg read p0 p0 = 0x00 (lldb) reg read -s 1 Predicate Registers: p0 = 0x00 p1 = 0x00 p2 = 0x00 p3 = 0x00 (lldb) reg write p1 0xf (lldb) reg read p3_0 p3_0 = 0x00000f00 (lldb) reg write p3_0 0xff00ff00 (lldb) reg read -s 1 Predicate Registers: p0 = 0x00 p1 = 0xff p2 = 0x00 p3 = 0xff Signed-off-by: Taylor Simpson <ltaylorsimpson@gmail.com> Reviewed-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Matheus Tavares Bernardino <quic_mathbern@quicinc.com> Message-Id: <20240613182209.140082-1-ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain <bcain@quicinc.com> --- gdb-xml/hexagon-core.xml | 6 +++++- target/hexagon/gdbstub.c | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/gdb-xml/hexagon-core.xml b/gdb-xml/hexagon-core.xml index e181163cff..b94378112a 100644 --- a/gdb-xml/hexagon-core.xml +++ b/gdb-xml/hexagon-core.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <!-- - Copyright(c) 2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + Copyright(c) 2023-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. This work is licensed under the terms of the GNU GPL, version 2 or (at your option) any later version. See the COPYING file in the @@ -80,5 +80,9 @@ <reg name="c29" bitsize="32" offset="244" encoding="uint" format="hex" group="Thread Registers" dwarf_regnum="61"/> <reg name="utimerlo" bitsize="32" offset="248" encoding="uint" format="hex" group="Thread Registers" dwarf_regnum="62"/> <reg name="utimerhi" bitsize="32" offset="252" encoding="uint" format="hex" group="Thread Registers" dwarf_regnum="63"/> + <reg name="p0" bitsize="8" offset="256" encoding="uint" format="hex" group="Predicate Registers" dwarf_regnum="64"/> + <reg name="p1" bitsize="8" offset="257" encoding="uint" format="hex" group="Predicate Registers" dwarf_regnum="65"/> + <reg name="p2" bitsize="8" offset="258" encoding="uint" format="hex" group="Predicate Registers" dwarf_regnum="66"/> + <reg name="p3" bitsize="8" offset="259" encoding="uint" format="hex" group="Predicate Registers" dwarf_regnum="67"/> </feature> diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c index 502c6987f0..94e1db8ef8 100644 --- a/target/hexagon/gdbstub.c +++ b/target/hexagon/gdbstub.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +36,14 @@ int hexagon_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) return gdb_get_regl(mem_buf, env->gpr[n]); } + n -= TOTAL_PER_THREAD_REGS; + + if (n < NUM_PREGS) { + return gdb_get_reg8(mem_buf, env->pred[n]); + } + + n -= NUM_PREGS; + g_assert_not_reached(); } @@ -56,6 +64,15 @@ int hexagon_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return sizeof(target_ulong); } + n -= TOTAL_PER_THREAD_REGS; + + if (n < NUM_PREGS) { + env->pred[n] = ldtul_p(mem_buf) & 0xff; + return sizeof(uint8_t); + } + + n -= NUM_PREGS; + g_assert_not_reached(); } From f51e30ff353c5c16ce9d68fe4072a89e7b19b454 Mon Sep 17 00:00:00 2001 From: Anton Johansson <anjo@rev.ng> Date: Thu, 13 Jul 2023 14:08:53 +0200 Subject: [PATCH 2731/2884] target/hexagon/idef-parser: Remove self-assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self assignment is clearly useless, and @1.last_column does not have to be set for an expression with only a single token, so remove it. Reported-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Anton Johansson <anjo@rev.ng> Reviewed-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20230713120853.27023-1-anjo@rev.ng> Signed-off-by: Brian Cain <bcain@quicinc.com> --- target/hexagon/idef-parser/idef-parser.y | 1 - 1 file changed, 1 deletion(-) diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index 9ffb9f9699..c6f17c6afa 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y @@ -800,7 +800,6 @@ rvalue : FAIL lvalue : FAIL { - @1.last_column = @1.last_column; yyassert(c, &@1, false, "Encountered a FAIL token as lvalue.\n"); } | REG From 2442cc6eeb8917ded564c8a71d903becaf4348cf Mon Sep 17 00:00:00 2001 From: Brian Cain <bcain@quicinc.com> Date: Wed, 31 Jul 2024 13:39:10 -0700 Subject: [PATCH 2732/2884] MAINTAINERS: Add my hexagon git tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add my git tree for hexagon. Note that the branch is "hex-next" and not "hex.next" as had been used previously. But I'll keep the "hex.next" branch in sync with "hex-next" until this commit lands to avoid confusion. Signed-off-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 74a85360fd..10af212632 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -245,6 +245,7 @@ F: disas/hexagon.c F: configs/targets/hexagon-linux-user/default.mak F: docker/dockerfiles/debian-hexagon-cross.docker F: gdb-xml/hexagon*.xml +T: git https://github.com/quic/qemu.git hex-next Hexagon idef-parser M: Alessandro Di Federico <ale@rev.ng> From 48ac9e885e47b12a7cbea5e093e3f2d0bff724f7 Mon Sep 17 00:00:00 2001 From: Brian Cain <bcain@quicinc.com> Date: Tue, 30 Jul 2024 18:09:54 -0700 Subject: [PATCH 2733/2884] target/hexagon: define a v66 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, v66 behavior is the same as other CPUs. Signed-off-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> --- target/hexagon/cpu-qom.h | 1 + target/hexagon/cpu.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/target/hexagon/cpu-qom.h b/target/hexagon/cpu-qom.h index da92fe7468..0b149bd5fe 100644 --- a/target/hexagon/cpu-qom.h +++ b/target/hexagon/cpu-qom.h @@ -16,6 +16,7 @@ #define HEXAGON_CPU_TYPE_SUFFIX "-" TYPE_HEXAGON_CPU #define HEXAGON_CPU_TYPE_NAME(name) (name HEXAGON_CPU_TYPE_SUFFIX) +#define TYPE_HEXAGON_CPU_V66 HEXAGON_CPU_TYPE_NAME("v66") #define TYPE_HEXAGON_CPU_V67 HEXAGON_CPU_TYPE_NAME("v67") #define TYPE_HEXAGON_CPU_V68 HEXAGON_CPU_TYPE_NAME("v68") #define TYPE_HEXAGON_CPU_V69 HEXAGON_CPU_TYPE_NAME("v69") diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 64cc05cca7..85f1e97d8f 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -26,6 +26,7 @@ #include "tcg/tcg.h" #include "exec/gdbstub.h" +static void hexagon_v66_cpu_init(Object *obj) { } static void hexagon_v67_cpu_init(Object *obj) { } static void hexagon_v68_cpu_init(Object *obj) { } static void hexagon_v69_cpu_init(Object *obj) { } @@ -373,6 +374,7 @@ static const TypeInfo hexagon_cpu_type_infos[] = { .class_size = sizeof(HexagonCPUClass), .class_init = hexagon_cpu_class_init, }, + DEFINE_CPU(TYPE_HEXAGON_CPU_V66, hexagon_v66_cpu_init), DEFINE_CPU(TYPE_HEXAGON_CPU_V67, hexagon_v67_cpu_init), DEFINE_CPU(TYPE_HEXAGON_CPU_V68, hexagon_v68_cpu_init), DEFINE_CPU(TYPE_HEXAGON_CPU_V69, hexagon_v69_cpu_init), From 47f3361a3af9d709218038a23b8907525310d2c3 Mon Sep 17 00:00:00 2001 From: Brian Cain <bcain@quicinc.com> Date: Tue, 30 Jul 2024 18:12:54 -0700 Subject: [PATCH 2734/2884] target/hexagon: switch to dc set_props() list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a hexagon_cpu_properties list to match the idiom used by other targets. Signed-off-by: Brian Cain <bcain@quicinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> --- target/hexagon/cpu.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 85f1e97d8f..020038fc49 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -48,13 +48,13 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) return oc; } -static Property hexagon_lldb_compat_property = - DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false); -static Property hexagon_lldb_stack_adjust_property = - DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, - 0, qdev_prop_uint32, target_ulong); -static Property hexagon_short_circuit_property = - DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true); +static Property hexagon_cpu_properties[] = { + DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), + DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, + qdev_prop_uint32, target_ulong), + DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true), + DEFINE_PROP_END_OF_LIST() +}; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -317,9 +317,6 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) static void hexagon_cpu_init(Object *obj) { - qdev_property_add_static(DEVICE(obj), &hexagon_lldb_compat_property); - qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property); - qdev_property_add_static(DEVICE(obj), &hexagon_short_circuit_property); } #include "hw/core/tcg-cpu-ops.h" @@ -340,6 +337,7 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, hexagon_cpu_realize, &mcc->parent_realize); + device_class_set_props(dc, hexagon_cpu_properties); resettable_class_set_parent_phases(rc, NULL, hexagon_cpu_reset_hold, NULL, &mcc->parent_phases); From 0173b97a219c63062972744682eba46c560fb7f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Thu, 8 Aug 2024 19:13:19 +1000 Subject: [PATCH 2735/2884] Update version for v9.1.0-rc1 release Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ef7c56c155..ec6abd8638 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.90 +9.0.91 From c719573d71afd38e3ac774e5a331fbaa0fc9f3da Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Thu, 1 Aug 2024 16:49:20 -0500 Subject: [PATCH 2736/2884] nbd: Minor style and typo fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Touch up a comment with the wrong type name, and an over-long line, both noticed while working on upcoming patches. Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20240807174943.771624-10-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- nbd/server.c | 2 +- qemu-nbd.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 892797bb11..ecd9366ba6 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1972,7 +1972,7 @@ static void nbd_export_request_shutdown(BlockExport *blk_exp) blk_exp_ref(&exp->common); /* - * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a + * TODO: Should we expand QMP BlockExportRemoveMode enum to allow a * close mode that stops advertising the export to new clients but * still permits existing clients to run to completion? Because of * that possibility, nbd_export_close() can be called more than diff --git a/qemu-nbd.c b/qemu-nbd.c index d7b3ccab21..8e104ef22c 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -588,7 +588,8 @@ int main(int argc, char **argv) pthread_t client_thread; const char *fmt = NULL; Error *local_err = NULL; - BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + BlockdevDetectZeroesOptions detect_zeroes = + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; const char *export_name = NULL; /* defaults to "" later for server mode */ const char *export_description = NULL; From fb1c2aaa981e0a2fa6362c9985f1296b74f055ac Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Wed, 7 Aug 2024 08:50:01 -0500 Subject: [PATCH 2737/2884] nbd/server: Plumb in new args to nbd_client_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upcoming patches to fix a CVE need to track an opaque pointer passed in by the owner of a client object, as well as request for a time limit on how fast negotiation must complete. Prepare for that by changing the signature of nbd_client_new() and adding an accessor to get at the opaque pointer, although for now the two servers (qemu-nbd.c and blockdev-nbd.c) do not change behavior even though they pass in a new default timeout value. Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20240807174943.771624-11-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> [eblake: s/LIMIT/MAX_SECS/ as suggested by Dan] Signed-off-by: Eric Blake <eblake@redhat.com> --- blockdev-nbd.c | 6 ++++-- include/block/nbd.h | 11 ++++++++++- nbd/server.c | 20 +++++++++++++++++--- qemu-nbd.c | 4 +++- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 213012435f..267a1de903 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -64,8 +64,10 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nbd_update_server_watch(nbd_server); qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); - nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz, - nbd_blockdev_client_closed); + /* TODO - expose handshake timeout as QMP option */ + nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + nbd_server->tlscreds, nbd_server->tlsauthz, + nbd_blockdev_client_closed, NULL); } static void nbd_update_server_watch(NBDServerData *s) diff --git a/include/block/nbd.h b/include/block/nbd.h index 4e7bd6342f..1d4d65922d 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -33,6 +33,12 @@ typedef struct NBDMetaContexts NBDMetaContexts; extern const BlockExportDriver blk_exp_nbd; +/* + * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must + * succeed at NBD_OPT_GO before being forcefully dropped as too slow. + */ +#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 + /* Handshake phase structs - this struct is passed on the wire */ typedef struct NBDOption { @@ -403,9 +409,12 @@ AioContext *nbd_export_aio_context(NBDExport *exp); NBDExport *nbd_export_find(const char *name); void nbd_client_new(QIOChannelSocket *sioc, + uint32_t handshake_max_secs, QCryptoTLSCreds *tlscreds, const char *tlsauthz, - void (*close_fn)(NBDClient *, bool)); + void (*close_fn)(NBDClient *, bool), + void *owner); +void *nbd_client_owner(NBDClient *client); void nbd_client_get(NBDClient *client); void nbd_client_put(NBDClient *client); diff --git a/nbd/server.c b/nbd/server.c index ecd9366ba6..fee04415d8 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -124,12 +124,14 @@ struct NBDMetaContexts { struct NBDClient { int refcount; /* atomic */ void (*close_fn)(NBDClient *client, bool negotiated); + void *owner; QemuMutex lock; NBDExport *exp; QCryptoTLSCreds *tlscreds; char *tlsauthz; + uint32_t handshake_max_secs; QIOChannelSocket *sioc; /* The underlying data channel */ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ @@ -3191,6 +3193,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque) qemu_co_mutex_init(&client->send_lock); + /* TODO - utilize client->handshake_max_secs */ if (nbd_negotiate(client, &local_err)) { if (local_err) { error_report_err(local_err); @@ -3205,14 +3208,17 @@ static coroutine_fn void nbd_co_client_start(void *opaque) } /* - * Create a new client listener using the given channel @sioc. + * Create a new client listener using the given channel @sioc and @owner. * Begin servicing it in a coroutine. When the connection closes, call - * @close_fn with an indication of whether the client completed negotiation. + * @close_fn with an indication of whether the client completed negotiation + * within @handshake_max_secs seconds (0 for unbounded). */ void nbd_client_new(QIOChannelSocket *sioc, + uint32_t handshake_max_secs, QCryptoTLSCreds *tlscreds, const char *tlsauthz, - void (*close_fn)(NBDClient *, bool)) + void (*close_fn)(NBDClient *, bool), + void *owner) { NBDClient *client; Coroutine *co; @@ -3225,13 +3231,21 @@ void nbd_client_new(QIOChannelSocket *sioc, object_ref(OBJECT(client->tlscreds)); } client->tlsauthz = g_strdup(tlsauthz); + client->handshake_max_secs = handshake_max_secs; client->sioc = sioc; qio_channel_set_delay(QIO_CHANNEL(sioc), false); object_ref(OBJECT(client->sioc)); client->ioc = QIO_CHANNEL(sioc); object_ref(OBJECT(client->ioc)); client->close_fn = close_fn; + client->owner = owner; co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } + +void * +nbd_client_owner(NBDClient *client) +{ + return client->owner; +} diff --git a/qemu-nbd.c b/qemu-nbd.c index 8e104ef22c..a186d2e119 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -390,7 +390,9 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nb_fds++; nbd_update_server_watch(); - nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed); + /* TODO - expose handshake timeout as command line option */ + nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + tlscreds, tlsauthz, nbd_client_closed, NULL); } static void nbd_update_server_watch(void) From c8a76dbd90c2f48df89b75bef74917f90a59b623 Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Tue, 6 Aug 2024 13:53:00 -0500 Subject: [PATCH 2738/2884] nbd/server: CVE-2024-7409: Cap default max-connections to 100 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allowing an unlimited number of clients to any web service is a recipe for a rudimentary denial of service attack: the client merely needs to open lots of sockets without closing them, until qemu no longer has any more fds available to allocate. For qemu-nbd, we default to allowing only 1 connection unless more are explicitly asked for (-e or --shared); this was historically picked as a nice default (without an explicit -t, a non-persistent qemu-nbd goes away after a client disconnects, without needing any additional follow-up commands), and we are not going to change that interface now (besides, someday we want to point people towards qemu-storage-daemon instead of qemu-nbd). But for qemu proper, and the newer qemu-storage-daemon, the QMP nbd-server-start command has historically had a default of unlimited number of connections, in part because unlike qemu-nbd it is inherently persistent until nbd-server-stop. Allowing multiple client sockets is particularly useful for clients that can take advantage of MULTI_CONN (creating parallel sockets to increase throughput), although known clients that do so (such as libnbd's nbdcopy) typically use only 8 or 16 connections (the benefits of scaling diminish once more sockets are competing for kernel attention). Picking a number large enough for typical use cases, but not unlimited, makes it slightly harder for a malicious client to perform a denial of service merely by opening lots of connections withot progressing through the handshake. This change does not eliminate CVE-2024-7409 on its own, but reduces the chance for fd exhaustion or unlimited memory usage as an attack surface. On the other hand, by itself, it makes it more obvious that with a finite limit, we have the problem of an unauthenticated client holding 100 fds opened as a way to block out a legitimate client from being able to connect; thus, later patches will further add timeouts to reject clients that are not making progress. This is an INTENTIONAL change in behavior, and will break any client of nbd-server-start that was not passing an explicit max-connections parameter, yet expects more than 100 simultaneous connections. We are not aware of any such client (as stated above, most clients aware of MULTI_CONN get by just fine on 8 or 16 connections, and probably cope with later connections failing by relying on the earlier connections; libvirt has not yet been passing max-connections, but generally creates NBD servers with the intent for a single client for the sake of live storage migration; meanwhile, the KubeSAN project anticipates a large cluster sharing multiple clients [up to 8 per node, and up to 100 nodes in a cluster], but it currently uses qemu-nbd with an explicit --shared=0 rather than qemu-storage-daemon with nbd-server-start). We considered using a deprecation period (declare that omitting max-parameters is deprecated, and make it mandatory in 3 releases - then we don't need to pick an arbitrary default); that has zero risk of breaking any apps that accidentally depended on more than 100 connections, and where such breakage might not be noticed under unit testing but only under the larger loads of production usage. But it does not close the denial-of-service hole until far into the future, and requires all apps to change to add the parameter even if 100 was good enough. It also has a drawback that any app (like libvirt) that is accidentally relying on an unlimited default should seriously consider their own CVE now, at which point they are going to change to pass explicit max-connections sooner than waiting for 3 qemu releases. Finally, if our changed default breaks an app, that app can always pass in an explicit max-parameters with a larger value. It is also intentional that the HMP interface to nbd-server-start is not changed to expose max-connections (any client needing to fine-tune things should be using QMP). Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20240807174943.771624-12-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> [ericb: Expand commit message to summarize Dan's argument for why we break corner-case back-compat behavior without a deprecation period] Signed-off-by: Eric Blake <eblake@redhat.com> --- block/monitor/block-hmp-cmds.c | 3 ++- blockdev-nbd.c | 8 ++++++++ include/block/nbd.h | 7 +++++++ qapi/block-export.json | 4 ++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index d954bec6f1..bdf2eb50b6 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -402,7 +402,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - nbd_server_start(addr, NULL, NULL, 0, &local_err); + nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS, + &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 267a1de903..24ba5382db 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -170,6 +170,10 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, void nbd_server_start_options(NbdServerOptions *arg, Error **errp) { + if (!arg->has_max_connections) { + arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS; + } + nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, arg->max_connections, errp); } @@ -182,6 +186,10 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, { SocketAddress *addr_flat = socket_address_flatten(addr); + if (!has_max_connections) { + max_connections = NBD_DEFAULT_MAX_CONNECTIONS; + } + nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); qapi_free_SocketAddress(addr_flat); } diff --git a/include/block/nbd.h b/include/block/nbd.h index 1d4d65922d..d4f8b21aec 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -39,6 +39,13 @@ extern const BlockExportDriver blk_exp_nbd; */ #define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 +/* + * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at + * once; must be large enough to allow a MULTI_CONN-aware client like + * nbdcopy to create its typical number of 8-16 sockets. + */ +#define NBD_DEFAULT_MAX_CONNECTIONS 100 + /* Handshake phase structs - this struct is passed on the wire */ typedef struct NBDOption { diff --git a/qapi/block-export.json b/qapi/block-export.json index 665d5fd026..ce33fe378d 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -28,7 +28,7 @@ # @max-connections: The maximum number of connections to allow at the # same time, 0 for unlimited. Setting this to 1 also stops the # server from advertising multiple client support (since 5.2; -# default: 0) +# default: 100) # # Since: 4.2 ## @@ -63,7 +63,7 @@ # @max-connections: The maximum number of connections to allow at the # same time, 0 for unlimited. Setting this to 1 also stops the # server from advertising multiple client support (since 5.2; -# default: 0). +# default: 100). # # Errors: # - if the server is already running From b9b72cb3ce15b693148bd09cef7e50110566d8a0 Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Thu, 8 Aug 2024 16:05:08 -0500 Subject: [PATCH 2739/2884] nbd/server: CVE-2024-7409: Drop non-negotiating clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A client that opens a socket but does not negotiate is merely hogging qemu's resources (an open fd and a small amount of memory); and a malicious client that can access the port where NBD is listening can attempt a denial of service attack by intentionally opening and abandoning lots of unfinished connections. The previous patch put a default bound on the number of such ongoing connections, but once that limit is hit, no more clients can connect (including legitimate ones). The solution is to insist that clients complete handshake within a reasonable time limit, defaulting to 10 seconds. A client that has not successfully completed NBD_OPT_GO by then (including the case of where the client didn't know TLS credentials to even reach the point of NBD_OPT_GO) is wasting our time and does not deserve to stay connected. Later patches will allow fine-tuning the limit away from the default value (including disabling it for doing integration testing of the handshake process itself). Note that this patch in isolation actually makes it more likely to see qemu SEGV after nbd-server-stop, as any client socket still connected when the server shuts down will now be closed after 10 seconds rather than at the client's whims. That will be addressed in the next patch. For a demo of this patch in action: $ qemu-nbd -f raw -r -t -e 10 file & $ nbdsh --opt-mode -c ' H = list() for i in range(20): print(i) H.insert(i, nbd.NBD()) H[i].set_opt_mode(True) H[i].connect_uri("nbd://localhost") ' $ kill $! where later connections get to start progressing once earlier ones are forcefully dropped for taking too long, rather than hanging. Suggested-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20240807174943.771624-13-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> [eblake: rebase to changes earlier in series, reduce scope of timer] Signed-off-by: Eric Blake <eblake@redhat.com> --- nbd/server.c | 28 +++++++++++++++++++++++++++- nbd/trace-events | 1 + 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/nbd/server.c b/nbd/server.c index fee04415d8..c30e687fc8 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -3186,22 +3186,48 @@ static void nbd_client_receive_next_request(NBDClient *client) } } +static void nbd_handshake_timer_cb(void *opaque) +{ + QIOChannel *ioc = opaque; + + trace_nbd_handshake_timer_cb(); + qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); +} + static coroutine_fn void nbd_co_client_start(void *opaque) { NBDClient *client = opaque; Error *local_err = NULL; + QEMUTimer *handshake_timer = NULL; qemu_co_mutex_init(&client->send_lock); - /* TODO - utilize client->handshake_max_secs */ + /* + * Create a timer to bound the time spent in negotiation. If the + * timer expires, it is likely nbd_negotiate will fail because the + * socket was shutdown. + */ + if (client->handshake_max_secs > 0) { + handshake_timer = aio_timer_new(qemu_get_aio_context(), + QEMU_CLOCK_REALTIME, + SCALE_NS, + nbd_handshake_timer_cb, + client->sioc); + timer_mod(handshake_timer, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + + client->handshake_max_secs * NANOSECONDS_PER_SECOND); + } + if (nbd_negotiate(client, &local_err)) { if (local_err) { error_report_err(local_err); } + timer_free(handshake_timer); client_close(client, false); return; } + timer_free(handshake_timer); WITH_QEMU_LOCK_GUARD(&client->lock) { nbd_client_receive_next_request(client); } diff --git a/nbd/trace-events b/nbd/trace-events index 00ae3216a1..cbd0a4ab7e 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -76,6 +76,7 @@ nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client sent non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" PRIx64 nbd_co_receive_align_compliance(const char *op, uint64_t from, uint64_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx64 ", align=0x%" PRIx32 nbd_trip(void) "Reading request" +nbd_handshake_timer_cb(void) "client took too long to negotiate" # client-connection.c nbd_connect_thread_sleep(uint64_t timeout) "timeout %" PRIu64 From 3e7ef738c8462c45043a1d39f702a0990406a3b3 Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Wed, 7 Aug 2024 12:23:13 -0500 Subject: [PATCH 2740/2884] nbd/server: CVE-2024-7409: Close stray clients at server-stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A malicious client can attempt to connect to an NBD server, and then intentionally delay progress in the handshake, including if it does not know the TLS secrets. Although the previous two patches reduce this behavior by capping the default max-connections parameter and killing slow clients, they did not eliminate the possibility of a client waiting to close the socket until after the QMP nbd-server-stop command is executed, at which point qemu would SEGV when trying to dereference the NULL nbd_server global which is no longer present. This amounts to a denial of service attack. Worse, if another NBD server is started before the malicious client disconnects, I cannot rule out additional adverse effects when the old client interferes with the connection count of the new server (although the most likely is a crash due to an assertion failure when checking nbd_server->connections > 0). For environments without this patch, the CVE can be mitigated by ensuring (such as via a firewall) that only trusted clients can connect to an NBD server. Note that using frameworks like libvirt that ensure that TLS is used and that nbd-server-stop is not executed while any trusted clients are still connected will only help if there is also no possibility for an untrusted client to open a connection but then stall on the NBD handshake. Given the previous patches, it would be possible to guarantee that no clients remain connected by having nbd-server-stop sleep for longer than the default handshake deadline before finally freeing the global nbd_server object, but that could make QMP non-responsive for a long time. So intead, this patch fixes the problem by tracking all client sockets opened while the server is running, and forcefully closing any such sockets remaining without a completed handshake at the time of nbd-server-stop, then waiting until the coroutines servicing those sockets notice the state change. nbd-server-stop now has a second AIO_WAIT_WHILE_UNLOCKED (the first is indirectly through the blk_exp_close_all_type() that disconnects all clients that completed handshakes), but forced socket shutdown is enough to progress the coroutines and quickly tear down all clients before the server is freed, thus finally fixing the CVE. This patch relies heavily on the fact that nbd/server.c guarantees that it only calls nbd_blockdev_client_closed() from the main loop (see the assertion in nbd_client_put() and the hoops used in nbd_client_put_nonzero() to achieve that); if we did not have that guarantee, we would also need a mutex protecting our accesses of the list of connections to survive re-entrancy from independent iothreads. Although I did not actually try to test old builds, it looks like this problem has existed since at least commit 862172f45c (v2.12.0, 2017) - even back when that patch started using a QIONetListener to handle listening on multiple sockets, nbd_server_free() was already unaware that the nbd_blockdev_client_closed callback can be reached later by a client thread that has not completed handshakes (and therefore the client's socket never got added to the list closed in nbd_export_close_all), despite that patch intentionally tearing down the QIONetListener to prevent new clients. Reported-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com> Fixes: CVE-2024-7409 CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20240807174943.771624-14-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> --- blockdev-nbd.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 24ba5382db..f73409ae49 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -21,12 +21,18 @@ #include "io/channel-socket.h" #include "io/net-listener.h" +typedef struct NBDConn { + QIOChannelSocket *cioc; + QLIST_ENTRY(NBDConn) next; +} NBDConn; + typedef struct NBDServerData { QIONetListener *listener; QCryptoTLSCreds *tlscreds; char *tlsauthz; uint32_t max_connections; uint32_t connections; + QLIST_HEAD(, NBDConn) conns; } NBDServerData; static NBDServerData *nbd_server; @@ -51,6 +57,14 @@ int nbd_server_max_connections(void) static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) { + NBDConn *conn = nbd_client_owner(client); + + assert(qemu_in_main_thread() && nbd_server); + + object_unref(OBJECT(conn->cioc)); + QLIST_REMOVE(conn, next); + g_free(conn); + nbd_client_put(client); assert(nbd_server->connections > 0); nbd_server->connections--; @@ -60,14 +74,20 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, gpointer opaque) { + NBDConn *conn = g_new0(NBDConn, 1); + + assert(qemu_in_main_thread() && nbd_server); nbd_server->connections++; + object_ref(OBJECT(cioc)); + conn->cioc = cioc; + QLIST_INSERT_HEAD(&nbd_server->conns, conn, next); nbd_update_server_watch(nbd_server); qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); /* TODO - expose handshake timeout as QMP option */ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, nbd_server->tlscreds, nbd_server->tlsauthz, - nbd_blockdev_client_closed, NULL); + nbd_blockdev_client_closed, conn); } static void nbd_update_server_watch(NBDServerData *s) @@ -81,12 +101,25 @@ static void nbd_update_server_watch(NBDServerData *s) static void nbd_server_free(NBDServerData *server) { + NBDConn *conn, *tmp; + if (!server) { return; } + /* + * Forcefully close the listener socket, and any clients that have + * not yet disconnected on their own. + */ qio_net_listener_disconnect(server->listener); object_unref(OBJECT(server->listener)); + QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) { + qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); + } + + AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0); + if (server->tlscreds) { object_unref(OBJECT(server->tlscreds)); } From 64678fc45d8f6c0c8d3adff41e0080aa7af15549 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Fri, 9 Aug 2024 17:37:54 +0100 Subject: [PATCH 2741/2884] target/arm: Fix BTI versus CF_PCREL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With pcrel, we cannot check the guarded page bit at translation time, as different mappings of the same physical page may or may not have the GP bit set. Instead, add a couple of helpers to check the page at runtime, after all other filters that might obviate the need for the check. The set_btype_for_br call must be moved after the gen_a64_set_pc call to ensure the current pc can still be computed. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240802003028.795476-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/helper-a64.c | 39 +++++++++++++++++++++ target/arm/tcg/helper-a64.h | 3 ++ target/arm/tcg/translate-a64.c | 64 ++++++++-------------------------- target/arm/tcg/translate.h | 2 -- 4 files changed, 56 insertions(+), 52 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index c60d2a7ec9..21a9abd90a 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1877,3 +1877,42 @@ void HELPER(cpyfe)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, { do_cpye(env, syndrome, wdesc, rdesc, false, GETPC()); } + +static bool is_guarded_page(CPUARMState *env, target_ulong addr, uintptr_t ra) +{ +#ifdef CONFIG_USER_ONLY + return page_get_flags(addr) & PAGE_BTI; +#else + CPUTLBEntryFull *full; + void *host; + int mmu_idx = cpu_mmu_index(env_cpu(env), true); + int flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx, + false, &host, &full, ra); + + assert(!(flags & TLB_INVALID_MASK)); + return full->extra.arm.guarded; +#endif +} + +void HELPER(guarded_page_check)(CPUARMState *env) +{ + /* + * We have already verified that bti is enabled, and that the + * instruction at PC is not ok for BTYPE. This is always at + * the beginning of a block, so PC is always up-to-date and + * no unwind is required. + */ + if (is_guarded_page(env, env->pc, 0)) { + raise_exception(env, EXCP_UDEF, syn_btitrap(env->btype), + exception_target_el(env)); + } +} + +void HELPER(guarded_page_br)(CPUARMState *env, target_ulong pc) +{ + /* + * We have already checked for branch via x16 and x17. + * What remains for choosing BTYPE is checking for a guarded page. + */ + env->btype = is_guarded_page(env, pc, GETPC()) ? 3 : 1; +} diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 371388f61b..481007bf39 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -133,6 +133,9 @@ DEF_HELPER_4(cpyfp, void, env, i32, i32, i32) DEF_HELPER_4(cpyfm, void, env, i32, i32, i32) DEF_HELPER_4(cpyfe, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_1(guarded_page_check, TCG_CALL_NO_WG, void, env) +DEF_HELPER_FLAGS_2(guarded_page_br, TCG_CALL_NO_RWG, void, env, tl) + DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 148be2826e..28a1013503 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1507,7 +1507,14 @@ static void set_btype_for_br(DisasContext *s, int rn) { if (dc_isar_feature(aa64_bti, s)) { /* BR to {x16,x17} or !guard -> 1, else 3. */ - set_btype(s, rn == 16 || rn == 17 || !s->guarded_page ? 1 : 3); + if (rn == 16 || rn == 17) { + set_btype(s, 1); + } else { + TCGv_i64 pc = tcg_temp_new_i64(); + gen_pc_plus_diff(s, pc, 0); + gen_helper_guarded_page_br(tcg_env, pc); + s->btype = -1; + } } } @@ -1521,8 +1528,8 @@ static void set_btype_for_blr(DisasContext *s) static bool trans_BR(DisasContext *s, arg_r *a) { - gen_a64_set_pc(s, cpu_reg(s, a->rn)); set_btype_for_br(s, a->rn); + gen_a64_set_pc(s, cpu_reg(s, a->rn)); s->base.is_jmp = DISAS_JUMP; return true; } @@ -1581,8 +1588,8 @@ static bool trans_BRAZ(DisasContext *s, arg_braz *a) } dst = auth_branch_target(s, cpu_reg(s, a->rn), tcg_constant_i64(0), !a->m); - gen_a64_set_pc(s, dst); set_btype_for_br(s, a->rn); + gen_a64_set_pc(s, dst); s->base.is_jmp = DISAS_JUMP; return true; } @@ -11878,37 +11885,6 @@ static bool trans_FAIL(DisasContext *s, arg_OK *a) return true; } -/** - * is_guarded_page: - * @env: The cpu environment - * @s: The DisasContext - * - * Return true if the page is guarded. - */ -static bool is_guarded_page(CPUARMState *env, DisasContext *s) -{ - uint64_t addr = s->base.pc_first; -#ifdef CONFIG_USER_ONLY - return page_get_flags(addr) & PAGE_BTI; -#else - CPUTLBEntryFull *full; - void *host; - int mmu_idx = arm_to_core_mmu_idx(s->mmu_idx); - int flags; - - /* - * We test this immediately after reading an insn, which means - * that the TLB entry must be present and valid, and thus this - * access will never raise an exception. - */ - flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx, - false, &host, &full, 0); - assert(!(flags & TLB_INVALID_MASK)); - - return full->extra.arm.guarded; -#endif -} - /** * btype_destination_ok: * @insn: The instruction at the branch destination @@ -12151,19 +12127,6 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (dc_isar_feature(aa64_bti, s)) { if (s->base.num_insns == 1) { - /* - * At the first insn of the TB, compute s->guarded_page. - * We delayed computing this until successfully reading - * the first insn of the TB, above. This (mostly) ensures - * that the softmmu tlb entry has been populated, and the - * page table GP bit is available. - * - * Note that we need to compute this even if btype == 0, - * because this value is used for BR instructions later - * where ENV is not available. - */ - s->guarded_page = is_guarded_page(env, s); - /* First insn can have btype set to non-zero. */ tcg_debug_assert(s->btype >= 0); @@ -12172,12 +12135,13 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * priority -- below debugging exceptions but above most * everything else. This allows us to handle this now * instead of waiting until the insn is otherwise decoded. + * + * We can check all but the guarded page check here; + * defer the latter to a helper. */ if (s->btype != 0 - && s->guarded_page && !btype_destination_ok(insn, s->bt, s->btype)) { - gen_exception_insn(s, 0, EXCP_UDEF, syn_btitrap(s->btype)); - return; + gen_helper_guarded_page_check(tcg_env); } } else { /* Not the first insn: btype must be 0. */ diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index a8672c857c..01c217f4a4 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -163,8 +163,6 @@ typedef struct DisasContext { uint8_t dcz_blocksize; /* A copy of cpu->gm_blocksize. */ uint8_t gm_blocksize; - /* True if this page is guarded. */ - bool guarded_page; /* True if the current insn_start has been updated. */ bool insn_start_updated; /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ From 05548400327db52d8f98eb033002fd2baadc3706 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:37:54 +0100 Subject: [PATCH 2742/2884] include: Fix typo in name of MAKE_IDENTFIER macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit bb71846325e23 we added some macro magic to avoid variable-shadowing when using some of our more complicated macros. One of the internal components of this is a macro named MAKE_IDENTFIER. Fix the typo in its name: it should be MAKE_IDENTIFIER. Commit created with sed -i -e 's/MAKE_IDENTFIER/MAKE_IDENTIFIER/g' include/qemu/*.h include/qapi/qmp/qobject.h Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240801102516.3843780-1-peter.maydell@linaro.org --- include/qapi/qmp/qobject.h | 2 +- include/qemu/atomic.h | 2 +- include/qemu/compiler.h | 2 +- include/qemu/osdep.h | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index 89b97d88bc..256d782688 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -54,7 +54,7 @@ struct QObject { typeof(obj) _obj = (obj); \ _obj ? container_of(&_obj->base, QObject, base) : NULL; \ }) -#define QOBJECT(obj) QOBJECT_INTERNAL((obj), MAKE_IDENTFIER(_obj)) +#define QOBJECT(obj) QOBJECT_INTERNAL((obj), MAKE_IDENTIFIER(_obj)) /* Required for qobject_to() */ #define QTYPE_CAST_TO_QNull QTYPE_QNULL diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index dc4118ddd9..7a3f2e6576 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -128,7 +128,7 @@ _val; \ }) #define qatomic_rcu_read(ptr) \ - qatomic_rcu_read_internal((ptr), MAKE_IDENTFIER(_val)) + qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val)) #define qatomic_rcu_set(ptr, i) do { \ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 554c5ce7df..c06954ccb4 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -38,7 +38,7 @@ #endif /* Expands into an identifier stemN, where N is another number each time */ -#define MAKE_IDENTFIER(stem) glue(stem, __COUNTER__) +#define MAKE_IDENTIFIER(stem) glue(stem, __COUNTER__) #ifndef likely #define likely(x) __builtin_expect(!!(x), 1) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 4cc4c32b14..fe7c3c5f67 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -399,7 +399,7 @@ void QEMU_ERROR("code path is reachable") }) #undef MIN #define MIN(a, b) \ - MIN_INTERNAL((a), (b), MAKE_IDENTFIER(_a), MAKE_IDENTFIER(_b)) + MIN_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) #define MAX_INTERNAL(a, b, _a, _b) \ ({ \ @@ -408,7 +408,7 @@ void QEMU_ERROR("code path is reachable") }) #undef MAX #define MAX(a, b) \ - MAX_INTERNAL((a), (b), MAKE_IDENTFIER(_a), MAKE_IDENTFIER(_b)) + MAX_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) #ifdef __COVERITY__ # define MIN_CONST(a, b) ((a) < (b) ? (a) : (b)) @@ -440,7 +440,7 @@ void QEMU_ERROR("code path is reachable") _a == 0 ? _b : (_b == 0 || _b > _a) ? _a : _b; \ }) #define MIN_NON_ZERO(a, b) \ - MIN_NON_ZERO_INTERNAL((a), (b), MAKE_IDENTFIER(_a), MAKE_IDENTFIER(_b)) + MIN_NON_ZERO_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) /* * Round number down to multiple. Safe when m is not a power of 2 (see From 9ca6876dc0befcc07b1c609dd96fa719feb7a318 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:37:54 +0100 Subject: [PATCH 2743/2884] docs/specs/rocker.txt: Convert to rST Convert the rocker.txt specification document to rST format. We make extensive use of the :: marker to introduce a literal block for all the tables and ASCII art, rather than trying to convert the tables to rST table syntax. This produces a valid rST document without needing a huge diff. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240801170131.3977807-2-peter.maydell@linaro.org --- MAINTAINERS | 2 +- docs/specs/index.rst | 1 + docs/specs/{rocker.txt => rocker.rst} | 181 +++++++++++++------------- 3 files changed, 93 insertions(+), 91 deletions(-) rename docs/specs/{rocker.txt => rocker.rst} (91%) diff --git a/MAINTAINERS b/MAINTAINERS index 10af212632..36becfaf49 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2465,7 +2465,7 @@ S: Maintained F: hw/net/rocker/ F: qapi/rocker.json F: tests/rocker/ -F: docs/specs/rocker.txt +F: docs/specs/rocker.rst e1000x M: Dmitry Fleytman <dmitry.fleytman@gmail.com> diff --git a/docs/specs/index.rst b/docs/specs/index.rst index be899b49c2..6495ed5ed9 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -35,3 +35,4 @@ guest hardware that is specific to QEMU. vmcoreinfo vmgenid rapl-msr + rocker diff --git a/docs/specs/rocker.txt b/docs/specs/rocker.rst similarity index 91% rename from docs/specs/rocker.txt rename to docs/specs/rocker.rst index 1857b31703..3a7fc6a7e0 100644 --- a/docs/specs/rocker.txt +++ b/docs/specs/rocker.rst @@ -1,23 +1,23 @@ Rocker Network Switch Register Programming Guide -Copyright (c) Scott Feldman <sfeldma@gmail.com> -Copyright (c) Neil Horman <nhorman@tuxdriver.com> -Version 0.11, 12/29/2014 +************************************************ -LICENSE -======= +.. + Copyright (c) Scott Feldman <sfeldma@gmail.com> + Copyright (c) Neil Horman <nhorman@tuxdriver.com> + Version 0.11, 12/29/2014 -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program 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 General Public License for more details. + This program 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 General Public License for more details. -SECTION 1: Introduction -======================= +Introduction +============ Overview -------- @@ -29,25 +29,25 @@ software. Notations and Conventions ------------------------- -o In register descriptions, [n:m] indicates a range from bit n to bit m, -inclusive. -o Use of leading 0x indicates a hexadecimal number. -o Use of leading 0b indicates a binary number. -o The use of RSVD or Reserved indicates that a bit or field is reserved for -future use. -o Field width is in bytes, unless otherwise noted. -o Register are (R) read-only, (R/W) read/write, (W) write-only, or (COR) clear -on read -o TLV values in network-byte-order are designated with (N). +* In register descriptions, [n:m] indicates a range from bit n to bit m, + inclusive. +* Use of leading 0x indicates a hexadecimal number. +* Use of leading 0b indicates a binary number. +* The use of RSVD or Reserved indicates that a bit or field is reserved for + future use. +* Field width is in bytes, unless otherwise noted. +* Register are (R) read-only, (R/W) read/write, (W) write-only, or (COR) clear + on read +* TLV values in network-byte-order are designated with (N). -SECTION 2: PCI Configuration Registers -====================================== +PCI Configuration Registers +=========================== PCI Configuration Space ----------------------- -Each switch instance registers as a PCI device with PCI configuration space: +Each switch instance registers as a PCI device with PCI configuration space:: offset width description value --------------------------------------------- @@ -74,11 +74,10 @@ Each switch instance registers as a PCI device with PCI configuration space: 0x41 1 Retry count 0x42 2 Reserved + * Assigned by sub-system implementation -* Assigned by sub-system implementation - -SECTION 3: Memory-Mapped Register Space -======================================= +Memory-Mapped Register Space +============================ There are two memory-mapped BARs. BAR0 maps device register space and is 0x2000 in size. BAR1 maps MSI-X vector and PBA tables and is also 0x2000 in @@ -89,7 +88,7 @@ byte registers with one 4-byte access, and 8 byte registers with either two 4-byte accesses or a single 8-byte access. In the case of two 4-byte accesses, access must be lower and then upper 4-bytes, in that order. -BAR0 device register space is organized as follows: +BAR0 device register space is organized as follows:: offset description ------------------------------------------------------ @@ -105,7 +104,7 @@ Reads to reserved registers read back as 0. No fancy stuff like write-combining is enabled on any of the registers. -BAR1 MSI-X register space is organized as follows: +BAR1 MSI-X register space is organized as follows:: offset description ------------------------------------------------------ @@ -113,8 +112,8 @@ BAR1 MSI-X register space is organized as follows: 0x1000-0x1fff MSI-X PBA table -SECTION 4: Interrupts, DMA, and Endianness -========================================== +Interrupts, DMA, and Endianness +=============================== PCI Interrupts -------------- @@ -122,7 +121,7 @@ PCI Interrupts The device supports only MSI-X interrupts. BAR1 memory-mapped region contains the MSI-X vector and PBA tables, with support for up to 256 MSI-X vectors. -The vector assignment is: +The vector assignment is:: vector description ----------------------------------------------------- @@ -134,7 +133,7 @@ The vector assignment is: Tx vector is even Rx vector is odd -A MSI-X vector table entry is 16 bytes: +A MSI-X vector table entry is 16 bytes:: field offset width description ------------------------------------------------------------- @@ -170,7 +169,7 @@ ring, and hardware will set this bit when the descriptor is complete. Descriptor ring sizes must be a power of 2 and range from 2 to 64K entries. Descriptor rings' base address must be 8-byte aligned. Descriptors must be packed within ring. Each descriptor in each ring must also be aligned on an 8 -byte boundary. Each descriptor ring will have these registers: +byte boundary. Each descriptor ring will have these registers:: DMA_DESC_xxx_BASE_ADDR, offset 0x1000 + (x * 32), 64-bit, (R/W) DMA_DESC_xxx_SIZE, offset 0x1008 + (x * 32), 32-bit, (R/W) @@ -180,7 +179,7 @@ byte boundary. Each descriptor ring will have these registers: DMA_DESC_xxx_CREDITS, offset 0x1018 + (x * 32), 32-bit, (R/W) DMA_DESC_xxx_RSVD1, offset 0x101c + (x * 32), 32-bit, (R/W) -Where x is descriptor ring index: +Where x is descriptor ring index:: index ring -------------------- @@ -203,14 +202,14 @@ written past TAIL. To do so would wrap the ring. An empty ring is when HEAD == TAIL. A full ring is when HEAD is one position behind TAIL. Both HEAD and TAIL increment and modulo wrap at the ring size. -CTRL register bits: +CTRL register bits:: bit name description ------------------------------------------------------------------------ [0] CTRL_RESET Reset the descriptor ring [1:31] Reserved -All descriptor types share some common fields: +All descriptor types share some common fields:: field width description ------------------------------------------------------------------- @@ -234,7 +233,7 @@ filled in by the switch. Likewise, the switch will ignore unknown fields filled in by software. Descriptor payload buffer is 8-byte aligned and TLVs are 8-byte aligned. The -value within a TLV is also 8-byte aligned. The (packed, 8 byte) TLV header is: +value within a TLV is also 8-byte aligned. The (packed, 8 byte) TLV header is:: field width description ----------------------------- @@ -246,7 +245,7 @@ The alignment requirements for descriptors and TLVs are to avoid unaligned access exceptions in software. Note that the payload for each TLV is also 8 byte aligned. -Figure 1 shows an example descriptor buffer with two TLVs. +Figure 1 shows an example descriptor buffer with two TLVs:: <------- 8 bytes -------> @@ -316,11 +315,11 @@ network packet data. All non-network-packet TLV multi-byte values will be LE. TLV values in network-byte-order are designated with (N). -SECTION 5: Test Registers -========================= +Test Registers +============== Rocker has several test registers to support troubleshooting register access, -interrupt generation, and DMA operations: +interrupt generation, and DMA operations:: TEST_REG, offset 0x0010, 32-bit (R/W) TEST_REG64, offset 0x0018, 64-bit (R/W) @@ -338,7 +337,7 @@ for that vector. To test basic DMA operations, allocate a DMA-able host buffer and put the buffer address into TEST_DMA_ADDR and size into TEST_DMA_SIZE. Then, write to -TEST_DMA_CTRL to manipulate the buffer contents. TEST_DMA_CTRL operations are: +TEST_DMA_CTRL to manipulate the buffer contents. TEST_DMA_CTRL operations are:: operation value description ----------------------------------------------------------- @@ -351,14 +350,14 @@ issue exists. In particular, buffers that start on odd-8-byte boundary and/or span multiple PAGE sizes should be tested. -SECTION 6: Ports -================ +Ports +===== Physical and Logical Ports ------------------------------------ The switch supports up to 62 physical (front-panel) ports. Register -PORT_PHYS_COUNT returns the actual number of physical ports available: +PORT_PHYS_COUNT returns the actual number of physical ports available:: PORT_PHYS_COUNT, offset 0x0304, 32-bit, (R) @@ -369,7 +368,7 @@ Front-panel ports and logical tunnel ports are mapped into a single 32-bit port space. A special CPU port is assigned port 0. The front-panel ports are mapped to ports 1-62. A special loopback port is assigned port 63. Logical tunnel ports are assigned ports 0x0001000-0x0001ffff. -To summarize the port assignments: +To summarize the port assignments:: port mapping ------------------------------------------------------- @@ -391,14 +390,14 @@ set/get the mode for front-panel ports, see port settings, below. Port Settings ------------- -Link status for all front-panel ports is available via PORT_PHYS_LINK_STATUS: +Link status for all front-panel ports is available via PORT_PHYS_LINK_STATUS:: PORT_PHYS_LINK_STATUS, offset 0x0310, 64-bit, (R) Value is port bitmap. Bits 0 and 63 always read 0. Bits 1-62 read 1 for link UP and 0 for link DOWN for respective front-panel ports. -Other properties for front-panel ports are available via DMA CMD descriptors: +Other properties for front-panel ports are available via DMA CMD descriptors:: Get PORT_SETTINGS descriptor: @@ -438,7 +437,7 @@ Port Enable ----------- Front-panel ports are initially disabled, which means port ingress and egress -packets will be dropped. To enable or disable a port, use PORT_PHYS_ENABLE: +packets will be dropped. To enable or disable a port, use PORT_PHYS_ENABLE:: PORT_PHYS_ENABLE: offset 0x0318, 64-bit, (R/W) @@ -447,15 +446,15 @@ packets will be dropped. To enable or disable a port, use PORT_PHYS_ENABLE: Default is 0. -SECTION 7: Switch Control -========================= +Switch Control +============== This section covers switch-wide register settings. Control ------- -This register is used for low level control of the switch. +This register is used for low level control of the switch:: CONTROL: offset 0x0300, 32-bit, (W) @@ -468,18 +467,18 @@ Switch ID --------- The switch has a SWITCH_ID to be used by software to uniquely identify the -switch: +switch:: SWITCH_ID: offset 0x0320, 64-bit, (R) Value is opaque to switch software and no special encoding is implied. -SECTION 8: Events -================= +Events +====== Non-I/O asynchronous events from the device are notified to the host using the -event ring. The TLV structure for events is: +event ring. The TLV structure for events is:: field width description --------------------------------------------------- @@ -491,7 +490,7 @@ event ring. The TLV structure for events is: Link Changed Event ------------------ -When link status changes on a physical port, this event is generated. +When link status changes on a physical port, this event is generated:: field width description --------------------------------------------------- @@ -510,6 +509,8 @@ driver should install to the device the MAC/VLAN on the port into the bridge table. Once installed, the MAC/VLAN is known on the port and this event will no longer be generated. +:: + field width description --------------------------------------------------- INFO <nest> @@ -518,8 +519,8 @@ no longer be generated. VLAN 2 VLAN ID -SECTION 9: CPU Packet Processing -================================ +CPU Packet Processing +===================== Ingress packets directed to the host CPU for further processing are delivered in the DMA RX ring. Likewise, host CPU originating packets destined to egress @@ -540,7 +541,7 @@ software that Tx is complete and software resources (e.g. skb) backing packet can be released. Figure 2 shows an example 3-fragment packet queued with one Tx descriptor. A -TLV is used for each packet fragment. +TLV is used for each packet fragment:: pkt frag 1 +–––––––+ +–+ @@ -570,7 +571,7 @@ TLV is used for each packet fragment. fig 2. -The TLVs for Tx descriptor buffer are: +The TLVs for Tx descriptor buffer are:: field width description --------------------------------------------------------------------- @@ -600,7 +601,7 @@ The TLVs for Tx descriptor buffer are: TX_FRAG_ADDR 8 DMA address of packet fragment TX_FRAG_LEN 2 Packet fragment length -Possible status return codes in descriptor on completion are: +Possible status return codes in descriptor on completion are:: DESC_COMP_ERR reason -------------------------------------------------------------------- @@ -623,7 +624,7 @@ worst-case packet size. A single Rx descriptor will contain the entire Rx packet data in one RX_FRAG. Other Rx TLVs describe and hardware offloads performed on the packet, such as checksum validation. -The TLVs for Rx descriptor buffer are: +The TLVs for Rx descriptor buffer are:: field width description --------------------------------------------------- @@ -649,7 +650,7 @@ The TLVs for Rx descriptor buffer are: Offload forward RX_FLAG indicates the device has already forwarded the packet so the host CPU should not also forward the packet. -Possible status return codes in descriptor on completion are: +Possible status return codes in descriptor on completion are:: DESC_COMP_ERR reason -------------------------------------------------------------------- @@ -660,14 +661,14 @@ Possible status return codes in descriptor on completion are: packet data TLV and other TLVs. -SECTION 10: OF-DPA Mode -====================== +OF-DPA Mode +=========== OF-DPA mode allows the switch to offload flow packet processing functions to hardware. An OpenFlow controller would communicate with an OpenFlow agent installed on the switch. The OpenFlow agent would (directly or indirectly) communicate with the Rocker switch driver, which in turn would program switch -hardware with flow functionality, as defined in OF-DPA. The block diagram is: +hardware with flow functionality, as defined in OF-DPA. The block diagram is:: +–––––––––––––––----–––+ | OF | @@ -696,14 +697,14 @@ OF-DPA Flow Table Interface There are commands to add, modify, delete, and get stats of flow table entries. The commands are issued using the DMA CMD descriptor ring. The following -commands are defined: +commands are defined:: CMD_ADD: add an entry to flow table CMD_MOD: modify an entry in flow table CMD_DEL: delete an entry from flow table CMD_GET_STATS: get stats for flow entry -TLVs for add and modify commands are: +TLVs for add and modify commands are:: field width description ---------------------------------------------------- @@ -723,14 +724,14 @@ TLVs for add and modify commands are: Additional TLVs based on flow table ID: -Table ID 0: ingress port +Table ID 0: ingress port:: field width description ---------------------------------------------------- OF_DPA_IN_PPORT 4 ingress physical port number OF_DPA_GOTO_TBL 2 goto table ID; zero to drop -Table ID 10: vlan +Table ID 10: vlan:: field width description ---------------------------------------------------- @@ -740,7 +741,7 @@ Table ID 10: vlan OF_DPA_GOTO_TBL 2 goto table ID; zero to drop OF_DPA_NEW_VLAN_ID 2 (N) new vlan ID -Table ID 20: termination mac +Table ID 20: termination mac:: field width description ---------------------------------------------------- @@ -757,7 +758,7 @@ Table ID 20: termination mac OF_DPA_OUT_PPORT 2 if specified, must be controller, set zero otherwise -Table ID 30: unicast routing +Table ID 30: unicast routing:: field width description ---------------------------------------------------- @@ -772,7 +773,7 @@ Table ID 30: unicast routing OF_DPA_GROUP_ID 4 data for GROUP action must be an L3 Unicast group entry -Table ID 40: multicast routing +Table ID 40: multicast routing:: field width description ---------------------------------------------------- @@ -797,7 +798,7 @@ Table ID 40: multicast routing OF_DPA_GROUP_ID 4 data for GROUP action must be an L3 multicast group entry -Table ID 50: bridging +Table ID 50: bridging:: field width description ---------------------------------------------------- @@ -818,7 +819,7 @@ Table ID 50: bridging restricted to CONTROLLER, set to 0 otherwise -Table ID 60: acl policy +Table ID 60: acl policy:: field width description ---------------------------------------------------- @@ -890,7 +891,7 @@ Table ID 60: acl policy dropped (all other instructions ignored) -TLVs for flow delete and get stats command are: +TLVs for flow delete and get stats command are:: field width description --------------------------------------------------- @@ -898,7 +899,7 @@ TLVs for flow delete and get stats command are: OF_DPA_COOKIE 8 Cookie On completion of get stats command, the descriptor buffer is written back with -the following TLVs: +the following TLVs:: field width description --------------------------------------------------- @@ -906,7 +907,7 @@ the following TLVs: OF_DPA_STAT_RX_PKTS 8 Received packets OF_DPA_STAT_TX_PKTS 8 Transmit packets -Possible status return codes in descriptor on completion are: +Possible status return codes in descriptor on completion are:: DESC_COMP_ERR command reason -------------------------------------------------------------------- @@ -928,14 +929,14 @@ Group Table Interface There are commands to add, modify, delete, and get stats of group table entries. The commands are issued using the DMA CMD descriptor ring. The -following commands are defined: +following commands are defined:: CMD_ADD: add an entry to group table CMD_MOD: modify an entry in group table CMD_DEL: delete an entry from group table CMD_GET_STATS: get stats for group entry -TLVs for add and modify commands are: +TLVs for add and modify commands are:: field width description ----------------------------------------------------------- @@ -969,7 +970,7 @@ TLVs for add and modify commands are: FLOW_SRC_MAC 6 (types 1, 2, 5) FLOW_DST_MAC 6 (types 1, 2) -TLVs for flow delete and get stats command are: +TLVs for flow delete and get stats command are:: field width description ----------------------------------------------------------- @@ -977,7 +978,7 @@ TLVs for flow delete and get stats command are: FLOW_GROUP_ID 2 Flow group ID On completion of get stats command, the descriptor buffer is written back with -the following TLVs: +the following TLVs:: field width description --------------------------------------------------- @@ -986,7 +987,7 @@ the following TLVs: FLOW_STAT_REF_COUNT 4 Flow reference count FLOW_STAT_BUCKET_COUNT 4 Flow bucket count -Possible status return codes in descriptor on completion are: +Possible status return codes in descriptor on completion are:: DESC_COMP_ERR command reason -------------------------------------------------------------------- From 8dac93a8ee78631ee69ff8f6e6203fa2d741e378 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:37:54 +0100 Subject: [PATCH 2744/2884] docs/interop/nbd.txt: Convert to rST Convert nbd.txt to rST format. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 20240801170131.3977807-3-peter.maydell@linaro.org --- MAINTAINERS | 2 +- docs/interop/index.rst | 1 + docs/interop/nbd.rst | 89 ++++++++++++++++++++++++++++++++++++++++++ docs/interop/nbd.txt | 72 ---------------------------------- 4 files changed, 91 insertions(+), 73 deletions(-) create mode 100644 docs/interop/nbd.rst delete mode 100644 docs/interop/nbd.txt diff --git a/MAINTAINERS b/MAINTAINERS index 36becfaf49..04a1fd0850 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3871,7 +3871,7 @@ F: nbd/ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c -F: docs/interop/nbd.txt +F: docs/interop/nbd.rst F: docs/tools/qemu-nbd.rst F: tests/qemu-iotests/tests/*nbd* T: git https://repo.or.cz/qemu/ericb.git nbd diff --git a/docs/interop/index.rst b/docs/interop/index.rst index ed65395bfb..b9ceaabc64 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -14,6 +14,7 @@ are useful for making QEMU interoperate with other software. dbus-vmstate dbus-display live-block-operations + nbd pr-helper qmp-spec qemu-ga diff --git a/docs/interop/nbd.rst b/docs/interop/nbd.rst new file mode 100644 index 0000000000..de079d31fd --- /dev/null +++ b/docs/interop/nbd.rst @@ -0,0 +1,89 @@ +QEMU NBD protocol support +========================= + +QEMU supports the NBD protocol, and has an internal NBD client (see +``block/nbd.c``), an internal NBD server (see ``blockdev-nbd.c``), and an +external NBD server tool (see ``qemu-nbd.c``). The common code is placed +in ``nbd/*``. + +The NBD protocol is specified here: +https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md + +The following paragraphs describe some specific properties of NBD +protocol realization in QEMU. + +Metadata namespaces +------------------- + +QEMU supports the ``base:allocation`` metadata context as defined in the +NBD protocol specification, and also defines an additional metadata +namespace ``qemu``. + +``qemu`` namespace +------------------ + +The ``qemu`` namespace currently contains two available metadata context +types. The first is related to exposing the contents of a dirty +bitmap alongside the associated disk contents. That metadata context +is named with the following form:: + + qemu:dirty-bitmap:<dirty-bitmap-export-name> + +Each dirty-bitmap metadata context defines only one flag for extents +in reply for ``NBD_CMD_BLOCK_STATUS``: + +bit 0: + ``NBD_STATE_DIRTY``, set when the extent is "dirty" + +The second is related to exposing the source of various extents within +the image, with a single metadata context named:: + + qemu:allocation-depth + +In the allocation depth context, the entire 32-bit value represents a +depth of which layer in a thin-provisioned backing chain provided the +data (0 for unallocated, 1 for the active layer, 2 for the first +backing layer, and so forth). + +For ``NBD_OPT_LIST_META_CONTEXT`` the following queries are supported +in addition to the specific ``qemu:allocation-depth`` and +``qemu:dirty-bitmap:<dirty-bitmap-export-name>``: + +``qemu:`` + returns list of all available metadata contexts in the namespace +``qemu:dirty-bitmap:`` + returns list of all available dirty-bitmap metadata contexts + +Features by version +------------------- + +The following list documents which qemu version first implemented +various features (both as a server exposing the feature, and as a +client taking advantage of the feature when present), to make it +easier to plan for cross-version interoperability. Note that in +several cases, the initial release containing a feature may require +additional patches from the corresponding stable branch to fix bugs in +the operation of that feature. + +2.6 + ``NBD_OPT_STARTTLS`` with TLS X.509 Certificates +2.8 + ``NBD_CMD_WRITE_ZEROES`` +2.10 + ``NBD_OPT_GO``, ``NBD_INFO_BLOCK`` +2.11 + ``NBD_OPT_STRUCTURED_REPLY`` +2.12 + ``NBD_CMD_BLOCK_STATUS`` for ``base:allocation`` +3.0 + ``NBD_OPT_STARTTLS`` with TLS Pre-Shared Keys (PSK), + ``NBD_CMD_BLOCK_STATUS`` for ``qemu:dirty-bitmap:``, ``NBD_CMD_CACHE`` +4.2 + ``NBD_FLAG_CAN_MULTI_CONN`` for shareable read-only exports, + ``NBD_CMD_FLAG_FAST_ZERO`` +5.2 + ``NBD_CMD_BLOCK_STATUS`` for ``qemu:allocation-depth`` +7.1 + ``NBD_FLAG_CAN_MULTI_CONN`` for shareable writable exports +8.2 + ``NBD_OPT_EXTENDED_HEADERS``, ``NBD_FLAG_BLOCK_STATUS_PAYLOAD`` diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt deleted file mode 100644 index 18efb251de..0000000000 --- a/docs/interop/nbd.txt +++ /dev/null @@ -1,72 +0,0 @@ -QEMU supports the NBD protocol, and has an internal NBD client (see -block/nbd.c), an internal NBD server (see blockdev-nbd.c), and an -external NBD server tool (see qemu-nbd.c). The common code is placed -in nbd/*. - -The NBD protocol is specified here: -https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md - -The following paragraphs describe some specific properties of NBD -protocol realization in QEMU. - -= Metadata namespaces = - -QEMU supports the "base:allocation" metadata context as defined in the -NBD protocol specification, and also defines an additional metadata -namespace "qemu". - -== "qemu" namespace == - -The "qemu" namespace currently contains two available metadata context -types. The first is related to exposing the contents of a dirty -bitmap alongside the associated disk contents. That metadata context -is named with the following form: - - qemu:dirty-bitmap:<dirty-bitmap-export-name> - -Each dirty-bitmap metadata context defines only one flag for extents -in reply for NBD_CMD_BLOCK_STATUS: - - bit 0: NBD_STATE_DIRTY, set when the extent is "dirty" - -The second is related to exposing the source of various extents within -the image, with a single metadata context named: - - qemu:allocation-depth - -In the allocation depth context, the entire 32-bit value represents a -depth of which layer in a thin-provisioned backing chain provided the -data (0 for unallocated, 1 for the active layer, 2 for the first -backing layer, and so forth). - -For NBD_OPT_LIST_META_CONTEXT the following queries are supported -in addition to the specific "qemu:allocation-depth" and -"qemu:dirty-bitmap:<dirty-bitmap-export-name>": - -* "qemu:" - returns list of all available metadata contexts in the - namespace. -* "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap - metadata contexts. - -= Features by version = - -The following list documents which qemu version first implemented -various features (both as a server exposing the feature, and as a -client taking advantage of the feature when present), to make it -easier to plan for cross-version interoperability. Note that in -several cases, the initial release containing a feature may require -additional patches from the corresponding stable branch to fix bugs in -the operation of that feature. - -* 2.6: NBD_OPT_STARTTLS with TLS X.509 Certificates -* 2.8: NBD_CMD_WRITE_ZEROES -* 2.10: NBD_OPT_GO, NBD_INFO_BLOCK -* 2.11: NBD_OPT_STRUCTURED_REPLY -* 2.12: NBD_CMD_BLOCK_STATUS for "base:allocation" -* 3.0: NBD_OPT_STARTTLS with TLS Pre-Shared Keys (PSK), -NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE -* 4.2: NBD_FLAG_CAN_MULTI_CONN for shareable read-only exports, -NBD_CMD_FLAG_FAST_ZERO -* 5.2: NBD_CMD_BLOCK_STATUS for "qemu:allocation-depth" -* 7.1: NBD_FLAG_CAN_MULTI_CONN for shareable writable exports -* 8.2: NBD_OPT_EXTENDED_HEADERS, NBD_FLAG_BLOCK_STATUS_PAYLOAD From 1bc0fc0a0b2a25b10008b0dc870ca87e2a090006 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:37:55 +0100 Subject: [PATCH 2745/2884] docs/interop/parallels.txt: Convert to rST Convert parallels.txt to rST format. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 20240801170131.3977807-4-peter.maydell@linaro.org --- MAINTAINERS | 2 +- docs/interop/index.rst | 1 + docs/interop/{parallels.txt => parallels.rst} | 108 ++++++++++-------- 3 files changed, 60 insertions(+), 51 deletions(-) rename docs/interop/{parallels.txt => parallels.rst} (72%) diff --git a/MAINTAINERS b/MAINTAINERS index 04a1fd0850..ae8bed8c75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3964,7 +3964,7 @@ L: qemu-block@nongnu.org S: Supported F: block/parallels.c F: block/parallels-ext.c -F: docs/interop/parallels.txt +F: docs/interop/parallels.rst T: git https://src.openvz.org/scm/~den/qemu.git parallels qed diff --git a/docs/interop/index.rst b/docs/interop/index.rst index b9ceaabc64..70bba62d90 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -15,6 +15,7 @@ are useful for making QEMU interoperate with other software. dbus-display live-block-operations nbd + parallels pr-helper qmp-spec qemu-ga diff --git a/docs/interop/parallels.txt b/docs/interop/parallels.rst similarity index 72% rename from docs/interop/parallels.txt rename to docs/interop/parallels.rst index bb3fadf369..7b328a40c8 100644 --- a/docs/interop/parallels.txt +++ b/docs/interop/parallels.rst @@ -1,41 +1,46 @@ -= License = +Parallels Expandable Image File Format +====================================== -Copyright (c) 2015 Denis Lunev -Copyright (c) 2015 Vladimir Sementsov-Ogievskiy +.. + Copyright (c) 2015 Denis Lunev + Copyright (c) 2015 Vladimir Sementsov-Ogievskiy -This work is licensed under the terms of the GNU GPL, version 2 or later. -See the COPYING file in the top-level directory. + This work is licensed under the terms of the GNU GPL, version 2 or later. + See the COPYING file in the top-level directory. -= Parallels Expandable Image File Format = A Parallels expandable image file consists of three consecutive parts: - * header - * BAT - * data area + +* header +* BAT +* data area All numbers in a Parallels expandable image are stored in little-endian byte order. -== Definitions == +Definitions +----------- - Sector A 512-byte data chunk. +Sector + A 512-byte data chunk. - Cluster A data chunk of the size specified in the image header. - Currently, the default size is 1MiB (2048 sectors). In previous - versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were - used. +Cluster + A data chunk of the size specified in the image header. + Currently, the default size is 1MiB (2048 sectors). In previous + versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were used. - BAT Block Allocation Table, an entity that contains information for - guest-to-host I/O data address translation. +BAT + Block Allocation Table, an entity that contains information for + guest-to-host I/O data address translation. - -== Header == +Header +------ The header is placed at the start of an image and contains the following -fields: +fields:: -Bytes: + Bytes: 0 - 15: magic Must contain "WithoutFreeSpace" or "WithouFreSpacExt". @@ -103,44 +108,46 @@ Bytes: ext_off must meet the same requirements as cluster offsets defined by BAT entries (see below). - -== BAT == +BAT +--- BAT is placed immediately after the image header. In the file, BAT is a contiguous array of 32-bit unsigned little-endian integers with -(bat_entries * 4) bytes size. +``(bat_entries * 4)`` bytes size. Each BAT entry contains an offset from the start of the file to the -corresponding cluster. The offset set in clusters for "WithouFreSpacExt" images -and in sectors for "WithoutFreeSpace" images. +corresponding cluster. The offset set in clusters for ``WithouFreSpacExt`` +images and in sectors for ``WithoutFreeSpace`` images. If a BAT entry is zero, the corresponding cluster is not allocated and should be considered as filled with zeroes. Cluster offsets specified by BAT entries must meet the following requirements: - - the value must not be lower than data offset (provided by header.data_off - or calculated as specified above), - - the value must be lower than the desired file size, - - the value must be unique among all BAT entries, - - the result of (cluster offset - data offset) must be aligned to cluster - size. +- the value must not be lower than data offset (provided by ``header.data_off`` + or calculated as specified above) +- the value must be lower than the desired file size +- the value must be unique among all BAT entries +- the result of ``(cluster offset - data offset)`` must be aligned to + cluster size -== Data Area == +Data Area +--------- -The data area is an area from the data offset (provided by header.data_off or -calculated as specified above) to the end of the file. It represents a +The data area is an area from the data offset (provided by ``header.data_off`` +or calculated as specified above) to the end of the file. It represents a contiguous array of clusters. Most of them are allocated by the BAT, some may -be allocated by the ext_off field in the header while other may be allocated by -extensions. All clusters allocated by ext_off and extensions should meet the -same requirements as clusters specified by BAT entries. +be allocated by the ``ext_off`` field in the header while other may be +allocated by extensions. All clusters allocated by ``ext_off`` and extensions +should meet the same requirements as clusters specified by BAT entries. -== Format Extension == +Format Extension +---------------- The Format Extension is an area 1 cluster in size that provides additional format features. This cluster is addressed by the ext_off field in the header. -The format of the Format Extension area is the following: +The format of the Format Extension area is the following:: 0 - 7: magic Must be 0xAB234CEF23DCEA87 @@ -149,10 +156,10 @@ The format of the Format Extension area is the following: The MD5 checksum of the entire Header Extension cluster except the first 24 bytes. - The above are followed by feature sections or "extensions". The last - extension must be "End of features" (see below). +The above are followed by feature sections or "extensions". The last +extension must be "End of features" (see below). -Each feature section has the following format: +Each feature section has the following format:: 0 - 7: magic The identifier of the feature: @@ -183,16 +190,17 @@ Each feature section has the following format: variable: data (data_size bytes) - The above is followed by padding to the next 8 bytes boundary, then the - next extension starts. +The above is followed by padding to the next 8 bytes boundary, then the +next extension starts. - The last extension must be "End of features" with all the fields set to 0. +The last extension must be "End of features" with all the fields set to 0. -=== Dirty bitmaps feature === +Dirty bitmaps feature +--------------------- This feature provides a way of storing dirty bitmaps in the image. The fields -of its data area are: +of its data area are:: 0 - 7: size The bitmap size, should be equal to disk size in sectors. @@ -215,7 +223,7 @@ clusters inside the Parallels image file. The offsets of these clusters are saved in the L1 offset table specified by the feature extension. Each L1 table entry is a 64 bit integer as described below: -Given an offset in bytes into the bitmap data, corresponding L1 entry is +Given an offset in bytes into the bitmap data, corresponding L1 entry is:: l1_table[offset / cluster_size] @@ -227,6 +235,6 @@ are assumed to be 1. If an L1 table entry is not 0 or 1, it contains the corresponding cluster offset (in 512b sectors). Given an offset in bytes into the bitmap data the -offset in bytes into the image file can be obtained as follows: +offset in bytes into the image file can be obtained as follows:: offset = l1_table[offset / cluster_size] * 512 + (offset % cluster_size) From 7d9fc7e74d8062e99c51b6b1a71393dcb5266cef Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:37:55 +0100 Subject: [PATCH 2746/2884] docs/interop/prl-xml.txt: Convert to rST Convert prl-xml.txt to rST format. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 20240801170131.3977807-5-peter.maydell@linaro.org --- MAINTAINERS | 1 + docs/interop/index.rst | 1 + docs/interop/prl-xml.rst | 187 +++++++++++++++++++++++++++++++++++++++ docs/interop/prl-xml.txt | 158 --------------------------------- 4 files changed, 189 insertions(+), 158 deletions(-) create mode 100644 docs/interop/prl-xml.rst delete mode 100644 docs/interop/prl-xml.txt diff --git a/MAINTAINERS b/MAINTAINERS index ae8bed8c75..3584d6a6c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3965,6 +3965,7 @@ S: Supported F: block/parallels.c F: block/parallels-ext.c F: docs/interop/parallels.rst +F: docs/interop/prl-xml.rst T: git https://src.openvz.org/scm/~den/qemu.git parallels qed diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 70bba62d90..999e44eae1 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -16,6 +16,7 @@ are useful for making QEMU interoperate with other software. live-block-operations nbd parallels + prl-xml pr-helper qmp-spec qemu-ga diff --git a/docs/interop/prl-xml.rst b/docs/interop/prl-xml.rst new file mode 100644 index 0000000000..aacf11f4c4 --- /dev/null +++ b/docs/interop/prl-xml.rst @@ -0,0 +1,187 @@ +Parallels Disk Format +===================== + +.. + Copyright (c) 2015-2017, Virtuozzo, Inc. + Authors: + 2015 Denis Lunev <den@openvz.org> + 2015 Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> + 2016-2017 Klim Kireev <klim.kireev@virtuozzo.com> + 2016-2017 Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com> + + This work is licensed under the terms of the GNU GPL, version 2 or later. + See the COPYING file in the top-level directory. + +This specification contains minimal information about Parallels Disk Format, +which is enough to proper work with QEMU. Nevertheless, Parallels Cloud Server +and Parallels Desktop are able to add some unspecified nodes to xml and use +them, but they are for internal work and don't affect functionality. Also it +uses auxiliary xml ``Snapshot.xml``, which allows to store optional snapshot +information, but it doesn't influence open/read/write functionality. QEMU and +other software should not use fields not covered in this document and +``Snapshot.xml`` file and must leave them as is. + +Parallels disk consists of two parts: the set of snapshots and the disk +descriptor file, which stores information about all files and snapshots. + +Definitions +----------- + +Snapshot + a record of the contents captured at a particular time, capable + of storing current state. A snapshot has UUID and parent UUID. + +Snapshot image + an overlay representing the difference between this + snapshot and some earlier snapshot. + +Overlay + an image storing the different sectors between two captured states. + +Root image + snapshot image with no parent, the root of snapshot tree. + +Storage + the backing storage for a subset of the virtual disk. When + there is more than one storage in a Parallels disk then that + is referred to as a split image. In this case every storage + covers specific address space area of the disk and has its + particular root image. Split images are not considered here + and are not supported. Each storage consists of disk + parameters and a list of images. The list of images always + contains a root image and may also contain overlays. The + root image can be an expandable Parallels image file or + plain. Overlays must be expandable. + +Description file + ``DiskDescriptor.xml`` stores information about disk parameters, + snapshots, storages. + +Top Snapshot + The overlay between actual state and some previous snapshot. + It is not a snapshot in the classical sense because it + serves as the active image that the guest writes to. + +Sector + a 512-byte data chunk. + +Description file +---------------- + +All information is placed in a single XML element +``Parallels_disk_image``. +The element has only one attribute ``Version``, that must be ``1.0``. + +Schema of ``DiskDescriptor.xml``:: + + <Parallels_disk_image Version="1.0"> + <Disk_Parameters> + ... + </Disk_Parameters> + <StorageData> + ... + </StorageData> + <Snapshots> + ... + </Snapshots> + </Parallels_disk_image> + +``Disk_Parameters`` element +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``Disk_Parameters`` element describes the physical layout of the +virtual disk and some general settings. + +The ``Disk_Parameters`` element MUST contain the following child elements: + +* ``Disk_size`` - number of sectors in the disk, + desired size of the disk. +* ``Cylinders`` - number of the disk cylinders. +* ``Heads`` - number of the disk heads. +* ``Sectors`` - number of the disk sectors per cylinder + (sector size is 512 bytes) + Limitation: Product of the ``Heads``, ``Sectors`` and ``Cylinders`` + values MUST be equal to the value of the Disk_size parameter. +* ``Padding`` - must be 0. Parallels Cloud Server and Parallels Desktop may + use padding set to 1, however this case is not covered + by this spec, QEMU and other software should not open + such disks and should not create them. + +``StorageData`` element +^^^^^^^^^^^^^^^^^^^^^^^ + +This element of the file describes the root image and all snapshot images. + +The ``StorageData`` element consists of the ``Storage`` child element, +as shown below:: + + <StorageData> + <Storage> + ... + </Storage> + </StorageData> + +A ``Storage`` element has following child elements: + +* ``Start`` - start sector of the storage, in case of non split storage + equals to 0. +* ``End`` - number of sector following the last sector, in case of non + split storage equals to ``Disk_size``. +* ``Blocksize`` - storage cluster size, number of sectors per one cluster. + Cluster size for each "Compressed" (see below) image in + parallels disk must be equal to this field. Note: cluster + size for Parallels Expandable Image is in ``tracks`` field of + its header (see :doc:`parallels`). +* Several ``Image`` child elements. + +Each ``Image`` element has following child elements: + +* ``GUID`` - image identifier, UUID in curly brackets. + For instance, ``{12345678-9abc-def1-2345-6789abcdef12}.`` + The GUID is used by the Snapshots element to reference images + (see below) +* ``Type`` - image type of the element. It can be: + + * ``Plain`` for raw files. + * ``Compressed`` for expanding disks. + +* ``File`` - path to image file. Path can be relative to + ``DiskDescriptor.xml`` or absolute. + +``Snapshots`` element +^^^^^^^^^^^^^^^^^^^^^ + +The ``Snapshots`` element describes the snapshot relations with the snapshot tree. + +The element contains the set of ``Shot`` child elements, as shown below:: + + <Snapshots> + <TopGUID> ... </TopGUID> /* Optional child element */ + <Shot> + ... + </Shot> + <Shot> + ... + </Shot> + ... + </Snapshots> + +Each ``Shot`` element contains the following child elements: + +* ``GUID`` - an image GUID. +* ``ParentGUID`` - GUID of the image of the parent snapshot. + +The software may traverse snapshots from child to parent using ``<ParentGUID>`` +field as reference. ``ParentGUID`` of root snapshot is +``{00000000-0000-0000-0000-000000000000}``. There should be only one root +snapshot. Top snapshot could be described via two ways: via ``TopGUID`` child +element of the ``Snapshots`` element or via predefined GUID +``{5fbaabe3-6958-40ff-92a7-860e329aab41}``. If ``TopGUID`` is defined, +predefined GUID is interpreted as usual GUID. All snapshot images +(except Top Snapshot) should be +opened read-only. There is another predefined GUID, +``BackupID = {704718e1-2314-44c8-9087-d78ed36b0f4e}``, which is used by +original and some third-party software for backup, QEMU and other +software may operate with images with ``GUID = BackupID`` as usual, +however, it is not recommended to use this +GUID for new disks. Top snapshot cannot have this GUID. diff --git a/docs/interop/prl-xml.txt b/docs/interop/prl-xml.txt deleted file mode 100644 index cf9b3fba26..0000000000 --- a/docs/interop/prl-xml.txt +++ /dev/null @@ -1,158 +0,0 @@ -= License = - -Copyright (c) 2015-2017, Virtuozzo, Inc. -Authors: - 2015 Denis Lunev <den@openvz.org> - 2015 Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> - 2016-2017 Klim Kireev <klim.kireev@virtuozzo.com> - 2016-2017 Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com> - -This work is licensed under the terms of the GNU GPL, version 2 or later. -See the COPYING file in the top-level directory. - -This specification contains minimal information about Parallels Disk Format, -which is enough to proper work with QEMU. Nevertheless, Parallels Cloud Server -and Parallels Desktop are able to add some unspecified nodes to xml and use -them, but they are for internal work and don't affect functionality. Also it -uses auxiliary xml "Snapshot.xml", which allows to store optional snapshot -information, but it doesn't influence open/read/write functionality. QEMU and -other software should not use fields not covered in this document and -Snapshot.xml file and must leave them as is. - -= Parallels Disk Format = - -Parallels disk consists of two parts: the set of snapshots and the disk -descriptor file, which stores information about all files and snapshots. - -== Definitions == - Snapshot a record of the contents captured at a particular time, - capable of storing current state. A snapshot has UUID and - parent UUID. - - Snapshot image an overlay representing the difference between this - snapshot and some earlier snapshot. - - Overlay an image storing the different sectors between two captured - states. - - Root image snapshot image with no parent, the root of snapshot tree. - - Storage the backing storage for a subset of the virtual disk. When - there is more than one storage in a Parallels disk then that - is referred to as a split image. In this case every storage - covers specific address space area of the disk and has its - particular root image. Split images are not considered here - and are not supported. Each storage consists of disk - parameters and a list of images. The list of images always - contains a root image and may also contain overlays. The - root image can be an expandable Parallels image file or - plain. Overlays must be expandable. - - Description DiskDescriptor.xml stores information about disk parameters, - file snapshots, storages. - - Top The overlay between actual state and some previous snapshot. - Snapshot It is not a snapshot in the classical sense because it - serves as the active image that the guest writes to. - - Sector a 512-byte data chunk. - -== Description file == -All information is placed in a single XML element Parallels_disk_image. -The element has only one attribute "Version", that must be 1.0. -Schema of DiskDescriptor.xml: - -<Parallels_disk_image Version="1.0"> - <Disk_Parameters> - ... - </Disk_Parameters> - <StorageData> - ... - </StorageData> - <Snapshots> - ... - </Snapshots> -</Parallels_disk_image> - -== Disk_Parameters element == -The Disk_Parameters element describes the physical layout of the virtual disk -and some general settings. - -The Disk_Parameters element MUST contain the following child elements: - * Disk_size - number of sectors in the disk, - desired size of the disk. - * Cylinders - number of the disk cylinders. - * Heads - number of the disk heads. - * Sectors - number of the disk sectors per cylinder - (sector size is 512 bytes) - Limitation: Product of the Heads, Sectors and Cylinders - values MUST be equal to the value of the Disk_size parameter. - * Padding - must be 0. Parallels Cloud Server and Parallels Desktop may - use padding set to 1, however this case is not covered - by this spec, QEMU and other software should not open - such disks and should not create them. - -== StorageData element == -This element of the file describes the root image and all snapshot images. - -The StorageData element consists of the Storage child element, as shown below: -<StorageData> - <Storage> - ... - </Storage> -</StorageData> - -A Storage element has following child elements: - * Start - start sector of the storage, in case of non split storage - equals to 0. - * End - number of sector following the last sector, in case of non - split storage equals to Disk_size. - * Blocksize - storage cluster size, number of sectors per one cluster. - Cluster size for each "Compressed" (see below) image in - parallels disk must be equal to this field. Note: cluster - size for Parallels Expandable Image is in 'tracks' field of - its header (see docs/interop/parallels.txt). - * Several Image child elements. - -Each Image element has following child elements: - * GUID - image identifier, UUID in curly brackets. - For instance, {12345678-9abc-def1-2345-6789abcdef12}. - The GUID is used by the Snapshots element to reference images - (see below) - * Type - image type of the element. It can be: - "Plain" for raw files. - "Compressed" for expanding disks. - * File - path to image file. Path can be relative to DiskDescriptor.xml or - absolute. - -== Snapshots element == -The Snapshots element describes the snapshot relations with the snapshot tree. - -The element contains the set of Shot child elements, as shown below: -<Snapshots> - <TopGUID> ... </TopGUID> /* Optional child element */ - <Shot> - ... - </Shot> - <Shot> - ... - </Shot> - ... -</Snapshots> - -Each Shot element contains the following child elements: - * GUID - an image GUID. - * ParentGUID - GUID of the image of the parent snapshot. - -The software may traverse snapshots from child to parent using <ParentGUID> -field as reference. ParentGUID of root snapshot is -{00000000-0000-0000-0000-000000000000}. There should be only one root -snapshot. Top snapshot could be described via two ways: via TopGUID child -element of the Snapshots element or via predefined GUID -{5fbaabe3-6958-40ff-92a7-860e329aab41}. If TopGUID is defined, predefined GUID is -interpreted as usual GUID. All snapshot images (except Top Snapshot) should be -opened read-only. There is another predefined GUID, -BackupID = {704718e1-2314-44c8-9087-d78ed36b0f4e}, which is used by original and -some third-party software for backup, QEMU and other software may operate with -images with GUID = BackupID as usual, however, it is not recommended to use this -GUID for new disks. Top snapshot cannot have this GUID. From 09334420d281777d6b7af8509942f0f604525fc2 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:37:55 +0100 Subject: [PATCH 2747/2884] docs/interop/prl-xml.rst: Fix minor grammar nits Fix some minor grammar nits in the prl-xml documentation. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 20240801170131.3977807-6-peter.maydell@linaro.org --- docs/interop/prl-xml.rst | 73 +++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/docs/interop/prl-xml.rst b/docs/interop/prl-xml.rst index aacf11f4c4..5bb63bb93a 100644 --- a/docs/interop/prl-xml.rst +++ b/docs/interop/prl-xml.rst @@ -13,15 +13,15 @@ Parallels Disk Format See the COPYING file in the top-level directory. This specification contains minimal information about Parallels Disk Format, -which is enough to proper work with QEMU. Nevertheless, Parallels Cloud Server -and Parallels Desktop are able to add some unspecified nodes to xml and use +which is enough to properly work with QEMU. Nevertheless, Parallels Cloud Server +and Parallels Desktop are able to add some unspecified nodes to the xml and use them, but they are for internal work and don't affect functionality. Also it -uses auxiliary xml ``Snapshot.xml``, which allows to store optional snapshot -information, but it doesn't influence open/read/write functionality. QEMU and -other software should not use fields not covered in this document and -``Snapshot.xml`` file and must leave them as is. +uses auxiliary xml ``Snapshot.xml``, which allows storage of optional snapshot +information, but this doesn't influence open/read/write functionality. QEMU and +other software should not use fields not covered in this document or the +``Snapshot.xml`` file, and must leave them as is. -Parallels disk consists of two parts: the set of snapshots and the disk +A Parallels disk consists of two parts: the set of snapshots and the disk descriptor file, which stores information about all files and snapshots. Definitions @@ -29,7 +29,7 @@ Definitions Snapshot a record of the contents captured at a particular time, capable - of storing current state. A snapshot has UUID and parent UUID. + of storing current state. A snapshot has a UUID and a parent UUID. Snapshot image an overlay representing the difference between this @@ -39,13 +39,13 @@ Overlay an image storing the different sectors between two captured states. Root image - snapshot image with no parent, the root of snapshot tree. + a snapshot image with no parent, the root of the snapshot tree. Storage the backing storage for a subset of the virtual disk. When there is more than one storage in a Parallels disk then that is referred to as a split image. In this case every storage - covers specific address space area of the disk and has its + covers a specific address space area of the disk and has its particular root image. Split images are not considered here and are not supported. Each storage consists of disk parameters and a list of images. The list of images always @@ -55,7 +55,7 @@ Storage Description file ``DiskDescriptor.xml`` stores information about disk parameters, - snapshots, storages. + snapshots, and storages. Top Snapshot The overlay between actual state and some previous snapshot. @@ -70,9 +70,9 @@ Description file All information is placed in a single XML element ``Parallels_disk_image``. -The element has only one attribute ``Version``, that must be ``1.0``. +The element has only one attribute, ``Version``, which must be ``1.0``. -Schema of ``DiskDescriptor.xml``:: +The schema of ``DiskDescriptor.xml``:: <Parallels_disk_image Version="1.0"> <Disk_Parameters> @@ -100,11 +100,11 @@ The ``Disk_Parameters`` element MUST contain the following child elements: * ``Heads`` - number of the disk heads. * ``Sectors`` - number of the disk sectors per cylinder (sector size is 512 bytes) - Limitation: Product of the ``Heads``, ``Sectors`` and ``Cylinders`` + Limitation: The product of the ``Heads``, ``Sectors`` and ``Cylinders`` values MUST be equal to the value of the Disk_size parameter. * ``Padding`` - must be 0. Parallels Cloud Server and Parallels Desktop may - use padding set to 1, however this case is not covered - by this spec, QEMU and other software should not open + use padding set to 1; however this case is not covered + by this specification. QEMU and other software should not open such disks and should not create them. ``StorageData`` element @@ -121,20 +121,20 @@ as shown below:: </Storage> </StorageData> -A ``Storage`` element has following child elements: +A ``Storage`` element has the following child elements: * ``Start`` - start sector of the storage, in case of non split storage equals to 0. * ``End`` - number of sector following the last sector, in case of non split storage equals to ``Disk_size``. * ``Blocksize`` - storage cluster size, number of sectors per one cluster. - Cluster size for each "Compressed" (see below) image in - parallels disk must be equal to this field. Note: cluster - size for Parallels Expandable Image is in ``tracks`` field of + The cluster size for each "Compressed" (see below) image in + a parallels disk must be equal to this field. Note: the cluster + size for a Parallels Expandable Image is in the ``tracks`` field of its header (see :doc:`parallels`). * Several ``Image`` child elements. -Each ``Image`` element has following child elements: +Each ``Image`` element has the following child elements: * ``GUID`` - image identifier, UUID in curly brackets. For instance, ``{12345678-9abc-def1-2345-6789abcdef12}.`` @@ -145,7 +145,7 @@ Each ``Image`` element has following child elements: * ``Plain`` for raw files. * ``Compressed`` for expanding disks. -* ``File`` - path to image file. Path can be relative to +* ``File`` - path to image file. The path can be relative to ``DiskDescriptor.xml`` or absolute. ``Snapshots`` element @@ -171,17 +171,22 @@ Each ``Shot`` element contains the following child elements: * ``GUID`` - an image GUID. * ``ParentGUID`` - GUID of the image of the parent snapshot. -The software may traverse snapshots from child to parent using ``<ParentGUID>`` -field as reference. ``ParentGUID`` of root snapshot is -``{00000000-0000-0000-0000-000000000000}``. There should be only one root -snapshot. Top snapshot could be described via two ways: via ``TopGUID`` child -element of the ``Snapshots`` element or via predefined GUID +The software may traverse snapshots from child to parent using the +``<ParentGUID>`` field as reference. The ``ParentGUID`` of the root +snapshot is ``{00000000-0000-0000-0000-000000000000}``. +There should be only one root snapshot. + +The Top snapshot could be +described via two ways: via the ``TopGUID`` child +element of the ``Snapshots`` element, or via the predefined GUID ``{5fbaabe3-6958-40ff-92a7-860e329aab41}``. If ``TopGUID`` is defined, -predefined GUID is interpreted as usual GUID. All snapshot images -(except Top Snapshot) should be -opened read-only. There is another predefined GUID, +the predefined GUID is interpreted as a normal GUID. All snapshot images +(except the Top Snapshot) should be +opened read-only. + +There is another predefined GUID, ``BackupID = {704718e1-2314-44c8-9087-d78ed36b0f4e}``, which is used by -original and some third-party software for backup, QEMU and other -software may operate with images with ``GUID = BackupID`` as usual, -however, it is not recommended to use this -GUID for new disks. Top snapshot cannot have this GUID. +original and some third-party software for backup. QEMU and other +software may operate with images with ``GUID = BackupID`` as usual. +However, it is not recommended to use this +GUID for new disks. The Top snapshot cannot have this GUID. From a8e1ea4c97a010349acbe22f099f0c6d6f2db470 Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Fri, 9 Aug 2024 17:37:55 +0100 Subject: [PATCH 2748/2884] docs: Typo fix in live disk backup Add in the missing space in the section header. Fixes: 1084159b31 ("qapi: deprecate drive-backup", v6.2.0) Signed-off-by: Eric Blake <eblake@redhat.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- docs/interop/live-block-operations.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 691429c7af..6b549ede7c 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -931,8 +931,8 @@ Shutdown the guest, by issuing the ``quit`` QMP command:: } -Live disk backup --- ``blockdev-backup`` and the deprecated``drive-backup`` ---------------------------------------------------------------------------- +Live disk backup --- ``blockdev-backup`` and the deprecated ``drive-backup`` +---------------------------------------------------------------------------- The ``blockdev-backup`` (and the deprecated ``drive-backup``) allows you to create a point-in-time snapshot. From fa62cb989a9146c82f8f172715042852f5d36200 Mon Sep 17 00:00:00 2001 From: David Woodhouse <dwmw@amazon.co.uk> Date: Tue, 6 Aug 2024 18:21:37 +0100 Subject: [PATCH 2749/2884] net: Fix '-net nic,model=' for non-help arguments Oops, don't *delete* the model option when checking for 'help'. Fixes: 64f75f57f9d2 ("net: Reinstate '-net nic, model=help' output as documented in man page") Reported-by: Hans <sungdgdhtryrt@gmail.com> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Cc: qemu-stable@nongnu.org Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Jason Wang <jasowang@redhat.com> --- net/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/net.c b/net/net.c index 2eb8bc9c0b..fc1125111c 100644 --- a/net/net.c +++ b/net/net.c @@ -1737,7 +1737,7 @@ void net_check_clients(void) static int net_init_client(void *dummy, QemuOpts *opts, Error **errp) { - const char *model = qemu_opt_get_del(opts, "model"); + const char *model = qemu_opt_get(opts, "model"); if (is_nic_model_help_option(model)) { return 0; From 446e5e8b4515e9a7be69ef6a29852975289bb6f0 Mon Sep 17 00:00:00 2001 From: Jianzhou Yue <JianZhou.Yue@verisilicon.com> Date: Fri, 9 Aug 2024 17:37:56 +0100 Subject: [PATCH 2750/2884] hw/core/ptimer: fix timer zero period condition for freq > 1GHz The real period is zero when both period and period_frac are zero. Check the method ptimer_set_freq, if freq is larger than 1000 MHz, the period is zero, but the period_frac is not, in this case, the ptimer will work but the current code incorrectly recognizes that the ptimer is disabled. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2306 Signed-off-by: JianZhou Yue <JianZhou.Yue@verisilicon.com> Message-id: 3DA024AEA8B57545AF1B3CAA37077D0FB75E82C8@SHASXM03.verisilicon.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/core/ptimer.c | 4 ++-- tests/unit/ptimer-test.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index b1517592c6..1d8964d804 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -83,7 +83,7 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) delta = s->delta = s->limit; } - if (s->period == 0) { + if (s->period == 0 && s->period_frac == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } @@ -309,7 +309,7 @@ void ptimer_run(ptimer_state *s, int oneshot) assert(s->in_transaction); - if (was_disabled && s->period == 0) { + if (was_disabled && s->period == 0 && s->period_frac == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } diff --git a/tests/unit/ptimer-test.c b/tests/unit/ptimer-test.c index 04b5f4e3d0..08240594bb 100644 --- a/tests/unit/ptimer-test.c +++ b/tests/unit/ptimer-test.c @@ -763,6 +763,33 @@ static void check_oneshot_with_load_0(gconstpointer arg) ptimer_free(ptimer); } +static void check_freq_more_than_1000M(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_freq(ptimer, 2000000000); + ptimer_set_limit(ptimer, 8, 1); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(3); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + + ptimer_free(ptimer); +} + static void add_ptimer_tests(uint8_t policy) { char policy_name[256] = ""; @@ -857,6 +884,12 @@ static void add_ptimer_tests(uint8_t policy) policy_name), g_memdup2(&policy, 1), check_oneshot_with_load_0, g_free); g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/freq_more_than_1000M policy=%s", + policy_name), + g_memdup2(&policy, 1), check_freq_more_than_1000M, g_free); + g_free(tmp); } static void add_all_ptimer_policies_comb_tests(void) From ed5031ad5d4c4c3b6eee6ab21aa95ccfc9dffdd4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Date: Fri, 9 Aug 2024 17:37:56 +0100 Subject: [PATCH 2751/2884] arm/virt: place power button pin number on a define Having magic numbers inside the code is not a good idea, as it is error-prone. So, instead, create a macro with the number definition. Link: https://lore.kernel.org/qemu-devel/CAFEAcA-PYnZ-32MRX+PgvzhnoAV80zBKMYg61j2f=oHaGfwSsg@mail.gmail.com/ Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Message-id: ef0e7f5fca6cd94eda415ecee670c3028c671b74.1723121692.git.mchehab+huawei@kernel.org Suggested-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt-acpi-build.c | 6 +++--- hw/arm/virt.c | 7 ++++--- include/hw/arm/virt.h | 3 +++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index e10cad86dd..f76fb117ad 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -154,10 +154,10 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap, aml_append(dev, aml_name_decl("_CRS", crs)); Aml *aei = aml_resource_template(); - /* Pin 3 for power button */ - const uint32_t pin_list[1] = {3}; + + const uint32_t pin = GPIO_PIN_POWER_BUTTON; aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1, + AML_EXCLUSIVE, AML_PULL_UP, 0, &pin, 1, "GPO0", NULL, 0)); aml_append(dev, aml_name_decl("_AEI", aei)); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 719e83e6a1..687fe0bb8b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1004,7 +1004,7 @@ static void virt_powerdown_req(Notifier *n, void *opaque) if (s->acpi_dev) { acpi_send_event(s->acpi_dev, ACPI_POWER_DOWN_STATUS); } else { - /* use gpio Pin 3 for power button event */ + /* use gpio Pin for power button event */ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); } } @@ -1013,7 +1013,8 @@ static void create_gpio_keys(char *fdt, DeviceState *pl061_dev, uint32_t phandle) { gpio_key_dev = sysbus_create_simple("gpio-key", -1, - qdev_get_gpio_in(pl061_dev, 3)); + qdev_get_gpio_in(pl061_dev, + GPIO_PIN_POWER_BUTTON)); qemu_fdt_add_subnode(fdt, "/gpio-keys"); qemu_fdt_setprop_string(fdt, "/gpio-keys", "compatible", "gpio-keys"); @@ -1024,7 +1025,7 @@ static void create_gpio_keys(char *fdt, DeviceState *pl061_dev, qemu_fdt_setprop_cell(fdt, "/gpio-keys/poweroff", "linux,code", KEY_POWER); qemu_fdt_setprop_cells(fdt, "/gpio-keys/poweroff", - "gpios", phandle, 3, 0); + "gpios", phandle, GPIO_PIN_POWER_BUTTON, 0); } #define SECURE_GPIO_POWEROFF 0 diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index ab961bb6a9..a4d937ed45 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -47,6 +47,9 @@ /* See Linux kernel arch/arm64/include/asm/pvclock-abi.h */ #define PVTIME_SIZE_PER_CPU 64 +/* GPIO pins */ +#define GPIO_PIN_POWER_BUTTON 3 + enum { VIRT_FLASH, VIRT_MEM, From 547c4e50929ec6c091d9c16a7b280e829b12b463 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella <sgarzare@redhat.com> Date: Thu, 8 Aug 2024 10:05:45 +0200 Subject: [PATCH 2752/2884] block/blkio: use FUA flag on write zeroes only if supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libblkio supports BLKIO_REQ_FUA with write zeros requests only since version 1.4.0, so let's inform the block layer that the blkio driver supports it only in this case. Otherwise we can have runtime errors as reported in https://issues.redhat.com/browse/RHEL-32878 Fixes: fd66dbd424 ("blkio: add libblkio block driver") Cc: qemu-stable@nongnu.org Buglink: https://issues.redhat.com/browse/RHEL-32878 Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240808080545.40744-1-sgarzare@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> --- block/blkio.c | 6 ++++-- meson.build | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/block/blkio.c b/block/blkio.c index 3d9a2e764c..e0e765af63 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -899,8 +899,10 @@ static int blkio_open(BlockDriverState *bs, QDict *options, int flags, } bs->supported_write_flags = BDRV_REQ_FUA | BDRV_REQ_REGISTERED_BUF; - bs->supported_zero_flags = BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | - BDRV_REQ_NO_FALLBACK; + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; +#ifdef CONFIG_BLKIO_WRITE_ZEROS_FUA + bs->supported_zero_flags |= BDRV_REQ_FUA; +#endif qemu_mutex_init(&s->blkio_lock); qemu_co_mutex_init(&s->bounce_lock); diff --git a/meson.build b/meson.build index c2a050b844..81ecd4bae7 100644 --- a/meson.build +++ b/meson.build @@ -2305,6 +2305,8 @@ config_host_data.set('CONFIG_BLKIO', blkio.found()) if blkio.found() config_host_data.set('CONFIG_BLKIO_VHOST_VDPA_FD', blkio.version().version_compare('>=1.3.0')) + config_host_data.set('CONFIG_BLKIO_WRITE_ZEROS_FUA', + blkio.version().version_compare('>=1.4.0')) endif config_host_data.set('CONFIG_CURL', curl.found()) config_host_data.set('CONFIG_CURSES', curses.found()) From fe68cc0923ebfa0c12e4176f61ec9b363a07a73a Mon Sep 17 00:00:00 2001 From: Alyssa Ross <hi@alyssa.is> Date: Mon, 5 Aug 2024 12:49:21 +0200 Subject: [PATCH 2753/2884] target/hexagon: don't look for static glib When cross compiling QEMU configured with --static, I've been getting configure errors like the following: Build-time dependency glib-2.0 found: NO ../target/hexagon/meson.build:303:15: ERROR: Dependency lookup for glib-2.0 with method 'pkgconfig' failed: Could not generate libs for glib-2.0: Package libpcre2-8 was not found in the pkg-config search path. Perhaps you should add the directory containing `libpcre2-8.pc' to the PKG_CONFIG_PATH environment variable Package 'libpcre2-8', required by 'glib-2.0', not found This happens because --static sets the prefer_static Meson option, but my build machine doesn't have a static libpcre2. I don't think it makes sense to insist that native dependencies are static, just because I want the non-native QEMU binaries to be static. Signed-off-by: Alyssa Ross <hi@alyssa.is> Link: https://lore.kernel.org/r/20240805104921.4035256-1-hi@alyssa.is Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/hexagon/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index b0b253aa6b..9ea1f4fc59 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -300,7 +300,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'] ) - glib_dep = dependency('glib-2.0', native: true) + glib_dep = dependency('glib-2.0', native: true, static: false) idef_parser = executable( 'idef-parser', From 416f2b16c02c618c0f233372ebfe343f9ee667d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 12 Aug 2024 12:58:42 +1000 Subject: [PATCH 2754/2884] target/i386: Do not apply REX to MMX operands Cc: qemu-stable@nongnu.org Fixes: b3e22b2318a ("target/i386: add core of new i386 decoder") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2495 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Link: https://lore.kernel.org/r/20240812025844.58956-2-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/decode-new.c.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index b22210f45d..03138b3876 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1979,7 +1979,10 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, op->unit = X86_OP_SSE; } get_reg: - op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + op->n = ((get_modrm(s, env) >> 3) & 7); + if (op->unit != X86_OP_MMX) { + op->n |= REX_R(s); + } break; case X86_TYPE_E: /* ALU modrm operand */ From 45230bca852f0a8c3a6bd9cb84e621bcde1c7e9a Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 12 Aug 2024 12:58:43 +1000 Subject: [PATCH 2755/2884] target/i386: Use unit not type in decode_modrm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather that enumerating the types that can produce MMX operands, examine the unit. No functional change. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Link: https://lore.kernel.org/r/20240812025844.58956-3-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/decode-new.c.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 03138b3876..30be9237c3 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1799,13 +1799,13 @@ static void decode_root(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui } -static int decode_modrm(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, - X86DecodedOp *op, X86OpType type) +static int decode_modrm(DisasContext *s, CPUX86State *env, + X86DecodedInsn *decode, X86DecodedOp *op) { int modrm = get_modrm(s, env); if ((modrm >> 6) == 3) { op->n = (modrm & 7); - if (type != X86_TYPE_Q && type != X86_TYPE_N) { + if (op->unit != X86_OP_MMX) { op->n |= REX_B(s); } } else { @@ -2040,7 +2040,7 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, /* fall through */ case X86_TYPE_nop: /* modrm operand decoded but not fetched */ get_modrm: - decode_modrm(s, env, decode, op, type); + decode_modrm(s, env, decode, op); break; case X86_TYPE_O: /* Absolute address encoded in the instruction */ From 20516e8d0e07739bd2e9bc8f51f319e37a9bc86c Mon Sep 17 00:00:00 2001 From: Zheyu Ma <zheyuma97@gmail.com> Date: Tue, 18 Jun 2024 15:55:50 +0200 Subject: [PATCH 2756/2884] hw/misc/stm32l4x5_rcc: Add validation for MCOPRE and MCOSEL values This commit adds validation checks for the MCOPRE and MCOSEL values in the rcc_update_cfgr_register function. If the MCOPRE value exceeds 0b100 or the MCOSEL value exceeds 0b111, an error is logged and the corresponding clock mux is disabled. This helps in identifying and handling invalid configurations in the RCC registers. Reproducer: cat << EOF | qemu-system-aarch64 -display \ none -machine accel=qtest, -m 512M -machine b-l475e-iot01a -qtest \ stdio writeq 0x40021008 0xffffffff EOF Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2356 Signed-off-by: Zheyu Ma <zheyuma97@gmail.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/misc/stm32l4x5_rcc.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index 417bd5e85f..59d428fa66 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -543,19 +543,31 @@ static void rcc_update_cfgr_register(Stm32l4x5RccState *s) uint32_t val; /* MCOPRE */ val = FIELD_EX32(s->cfgr, CFGR, MCOPRE); - assert(val <= 0b100); - clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO], - 1, 1 << val); + if (val > 0b100) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid MCOPRE value: 0x%"PRIx32"\n", + __func__, val); + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false); + } else { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO], + 1, 1 << val); + } /* MCOSEL */ val = FIELD_EX32(s->cfgr, CFGR, MCOSEL); - assert(val <= 0b111); - if (val == 0) { + if (val > 0b111) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid MCOSEL value: 0x%"PRIx32"\n", + __func__, val); clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false); } else { - clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true); - clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO], - val - 1); + if (val == 0) { + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false); + } else { + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true); + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO], + val - 1); + } } /* STOPWUCK */ From 8e0c9a9efa21a16190cbac288e414bbf1d80f639 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 13 Aug 2024 11:42:49 +0100 Subject: [PATCH 2757/2884] target/arm: Clear high SVE elements in handle_vec_simd_wshli AdvSIMD instructions are supposed to zero bits beyond 128. Affects SSHLL, USHLL, SSHLL2, USHLL2. Cc: qemu-stable@nongnu.org Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240717060903.205098-15-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/translate-a64.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 28a1013503..bc2d64e883 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10756,6 +10756,7 @@ static void handle_vec_simd_wshli(DisasContext *s, bool is_q, bool is_u, tcg_gen_shli_i64(tcg_rd, tcg_rd, shift); write_vec_element(s, tcg_rd, rd, i, size + 1); } + clear_vec_high(s, true, rd); } /* SHRN/RSHRN - Shift right with narrowing (and potential rounding) */ From 150c24f34e9c3388c0f0ad04ddd997e5559db800 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:04:29 +0100 Subject: [PATCH 2758/2884] target/arm: Update translation regime comment for new features We have a long comment describing the Arm architectural translation regimes and how we map them to QEMU MMU indexes. This comment has got a bit out of date: * FEAT_SEL2 allows Secure EL2 and corresponding new regimes * FEAT_RME introduces Realm state and its translation regimes * We now model the Cortex-R52 so that is no longer a hypothetical * We separated Secure Stage 2 and NonSecure Stage 2 MMU indexes * We have an MMU index per physical address spacea Add the missing pieces so that the list of architectural translation regimes matches the Arm ARM, and the list and count of QEMU MMU indexes in the comment matches the enum. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Tested-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240809160430.1144805-2-peter.maydell@linaro.org --- target/arm/cpu.h | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a12859fc53..216774f5d3 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2772,8 +2772,14 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * + NonSecure EL1 & 0 stage 2 * + NonSecure EL2 * + NonSecure EL2 & 0 (ARMv8.1-VHE) - * + Secure EL1 & 0 - * + Secure EL3 + * + Secure EL1 & 0 stage 1 + * + Secure EL1 & 0 stage 2 (FEAT_SEL2) + * + Secure EL2 (FEAT_SEL2) + * + Secure EL2 & 0 (FEAT_SEL2) + * + Realm EL1 & 0 stage 1 (FEAT_RME) + * + Realm EL1 & 0 stage 2 (FEAT_RME) + * + Realm EL2 (FEAT_RME) + * + EL3 * If EL3 is 32-bit: * + NonSecure PL1 & 0 stage 1 * + NonSecure PL1 & 0 stage 2 @@ -2805,10 +2811,12 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * table over and over. * 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access * Never (PAN) bit within PSTATE. - * 7. we fold together the secure and non-secure regimes for A-profile, + * 7. we fold together most secure and non-secure regimes for A-profile, * because there are no banked system registers for aarch64, so the * process of switching between secure and non-secure is * already heavyweight. + * 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, + * because both are in use simultaneously for Secure EL2. * * This gives us the following list of cases: * @@ -2820,14 +2828,15 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * EL2 EL2&0 +PAN * EL2 (aka NS PL2) * EL3 (aka S PL1) - * Physical (NS & S) - * Stage2 (NS & S) + * Stage2 Secure + * Stage2 NonSecure + * plus one TLB per Physical address space: S, NS, Realm, Root * - * for a total of 12 different mmu_idx. + * for a total of 14 different mmu_idx. * * R profile CPUs have an MPU, but can use the same set of MMU indexes * as A profile. They only need to distinguish EL0 and EL1 (and - * EL2 if we ever model a Cortex-R52). + * EL2 for cores like the Cortex-R52). * * M profile CPUs are rather different as they do not have a true MMU. * They have the following different MMU indexes: From 4c2c0474693229c1f533239bb983495c5427784d Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Fri, 9 Aug 2024 17:04:30 +0100 Subject: [PATCH 2759/2884] target/arm: Fix usage of MMU indexes when EL3 is AArch32 Our current usage of MMU indexes when EL3 is AArch32 is confused. Architecturally, when EL3 is AArch32, all Secure code runs under the Secure PL1&0 translation regime: * code at EL3, which might be Mon, or SVC, or any of the other privileged modes (PL1) * code at EL0 (Secure PL0) This is different from when EL3 is AArch64, in which case EL3 is its own translation regime, and EL1 and EL0 (whether AArch32 or AArch64) have their own regime. We claimed to be mapping Secure PL1 to our ARMMMUIdx_EL3, but didn't do anything special about Secure PL0, which meant it used the same ARMMMUIdx_EL10_0 that NonSecure PL0 does. This resulted in a bug where arm_sctlr() incorrectly picked the NonSecure SCTLR as the controlling register when in Secure PL0, which meant we were spuriously generating alignment faults because we were looking at the wrong SCTLR control bits. The use of ARMMMUIdx_EL3 for Secure PL1 also resulted in the bug that we wouldn't honour the PAN bit for Secure PL1, because there's no equivalent _PAN mmu index for it. We could fix this in one of two ways: * The most straightforward is to add new MMU indexes EL30_0, EL30_3, EL30_3_PAN to correspond to "Secure PL1&0 at PL0", "Secure PL1&0 at PL1", and "Secure PL1&0 at PL1 with PAN". This matches how we use indexes for the AArch64 regimes, and preserves propirties like being able to determine the privilege level from an MMU index without any other information. However it would add two MMU indexes (we can share one with ARMMMUIdx_EL3), and we are already using 14 of the 16 the core TLB code permits. * The more complicated approach is the one we take here. We use the same MMU indexes (E10_0, E10_1, E10_1_PAN) for Secure PL1&0 than we do for NonSecure PL1&0. This saves on MMU indexes, but means we need to check in some places whether we're in the Secure PL1&0 regime or not before we interpret an MMU index. The changes in this commit were created by auditing all the places where we use specific ARMMMUIdx_ values, and checking whether they needed to be changed to handle the new index value usage. Note for potential stable backports: taking also the previous (comment-change-only) commit might make the backport easier. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2326 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Tested-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240809160430.1144805-3-peter.maydell@linaro.org --- target/arm/cpu.h | 31 ++++++++++++++++++------------- target/arm/helper.c | 34 +++++++++++++++++++++++----------- target/arm/internals.h | 27 +++++++++++++++++++++++---- target/arm/ptw.c | 6 +++++- target/arm/tcg/hflags.c | 4 ++++ target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/translate.c | 9 +++++---- target/arm/tcg/translate.h | 2 ++ 8 files changed, 81 insertions(+), 34 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 216774f5d3..9a3fd59562 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2784,8 +2784,7 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * + NonSecure PL1 & 0 stage 1 * + NonSecure PL1 & 0 stage 2 * + NonSecure PL2 - * + Secure PL0 - * + Secure PL1 + * + Secure PL1 & 0 * (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.) * * For QEMU, an mmu_idx is not quite the same as a translation regime because: @@ -2803,37 +2802,39 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * The only use of stage 2 translations is either as part of an s1+2 * lookup or when loading the descriptors during a stage 1 page table walk, * and in both those cases we don't use the TLB. - * 4. we can also safely fold together the "32 bit EL3" and "64 bit EL3" - * translation regimes, because they map reasonably well to each other - * and they can't both be active at the same time. - * 5. we want to be able to use the TLB for accesses done as part of a + * 4. we want to be able to use the TLB for accesses done as part of a * stage1 page table walk, rather than having to walk the stage2 page * table over and over. - * 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access + * 5. we need separate EL1/EL2 mmu_idx for handling the Privileged Access * Never (PAN) bit within PSTATE. - * 7. we fold together most secure and non-secure regimes for A-profile, + * 6. we fold together most secure and non-secure regimes for A-profile, * because there are no banked system registers for aarch64, so the * process of switching between secure and non-secure is * already heavyweight. - * 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, + * 7. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, * because both are in use simultaneously for Secure EL2. * * This gives us the following list of cases: * - * EL0 EL1&0 stage 1+2 (aka NS PL0) - * EL1 EL1&0 stage 1+2 (aka NS PL1) - * EL1 EL1&0 stage 1+2 +PAN + * EL0 EL1&0 stage 1+2 (or AArch32 PL0 PL1&0 stage 1+2) + * EL1 EL1&0 stage 1+2 (or AArch32 PL1 PL1&0 stage 1+2) + * EL1 EL1&0 stage 1+2 +PAN (or AArch32 PL1 PL1&0 stage 1+2 +PAN) * EL0 EL2&0 * EL2 EL2&0 * EL2 EL2&0 +PAN * EL2 (aka NS PL2) - * EL3 (aka S PL1) + * EL3 (not used when EL3 is AArch32) * Stage2 Secure * Stage2 NonSecure * plus one TLB per Physical address space: S, NS, Realm, Root * * for a total of 14 different mmu_idx. * + * Note that when EL3 is AArch32, the usage is potentially confusing + * because the MMU indexes are named for their AArch64 use, so code + * using the ARMMMUIdx_E10_1 might be at EL3, not EL1. This is because + * Secure PL1 is always at EL3. + * * R profile CPUs have an MPU, but can use the same set of MMU indexes * as A profile. They only need to distinguish EL0 and EL1 (and * EL2 for cores like the Cortex-R52). @@ -3126,6 +3127,10 @@ FIELD(TBFLAG_A32, NS, 10, 1) * This requires an SME trap from AArch32 mode when using NEON. */ FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) +/* + * Indicates whether we are in the Secure PL1&0 translation regime + */ +FIELD(TBFLAG_A32, S_PL1_0, 12, 1) /* * Bit usage when in AArch32 state, for M-profile only. diff --git a/target/arm/helper.c b/target/arm/helper.c index 8fb4b474e8..0a582c1cd3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3700,7 +3700,7 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, */ format64 = arm_s1_regime_using_lpae_format(env, mmu_idx); - if (arm_feature(env, ARM_FEATURE_EL2)) { + if (arm_feature(env, ARM_FEATURE_EL2) && !arm_aa32_secure_pl1_0(env)) { if (mmu_idx == ARMMMUIdx_E10_0 || mmu_idx == ARMMMUIdx_E10_1 || mmu_idx == ARMMMUIdx_E10_1_PAN) { @@ -3774,13 +3774,11 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) case 0: /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ switch (el) { - case 3: - mmu_idx = ARMMMUIdx_E3; - break; case 2: g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ /* fall through */ case 1: + case 3: if (ri->crm == 9 && arm_pan_enabled(env)) { mmu_idx = ARMMMUIdx_Stage1_E1_PAN; } else { @@ -11861,8 +11859,11 @@ void arm_cpu_do_interrupt(CPUState *cs) uint64_t arm_sctlr(CPUARMState *env, int el) { - /* Only EL0 needs to be adjusted for EL1&0 or EL2&0. */ - if (el == 0) { + if (arm_aa32_secure_pl1_0(env)) { + /* In Secure PL1&0 SCTLR_S is always controlling */ + el = 3; + } else if (el == 0) { + /* Only EL0 needs to be adjusted for EL1&0 or EL2&0. */ ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 0); el = mmu_idx == ARMMMUIdx_E20_0 ? 2 : 1; } @@ -12522,8 +12523,12 @@ int fp_exception_el(CPUARMState *env, int cur_el) return 0; } -/* Return the exception level we're running at if this is our mmu_idx */ -int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) +/* + * Return the exception level we're running at if this is our mmu_idx. + * s_pl1_0 should be true if this is the AArch32 Secure PL1&0 translation + * regime. + */ +int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx, bool s_pl1_0) { if (mmu_idx & ARM_MMU_IDX_M) { return mmu_idx & ARM_MMU_IDX_M_PRIV; @@ -12535,7 +12540,7 @@ int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) return 0; case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: - return 1; + return s_pl1_0 ? 3 : 1; case ARMMMUIdx_E2: case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: @@ -12573,6 +12578,15 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) idx = ARMMMUIdx_E10_0; } break; + case 3: + /* + * AArch64 EL3 has its own translation regime; AArch32 EL3 + * uses the Secure PL1&0 translation regime. + */ + if (arm_el_is_aa64(env, 3)) { + return ARMMMUIdx_E3; + } + /* fall through */ case 1: if (arm_pan_enabled(env)) { idx = ARMMMUIdx_E10_1_PAN; @@ -12592,8 +12606,6 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) idx = ARMMMUIdx_E2; } break; - case 3: - return ARMMMUIdx_E3; default: g_assert_not_reached(); } diff --git a/target/arm/internals.h b/target/arm/internals.h index 757b1fae92..203a2dae14 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -275,6 +275,20 @@ FIELD(CNTHCTL, CNTPMASK, 19, 1) #define M_FAKE_FSR_NSC_EXEC 0xf /* NS executing in S&NSC memory */ #define M_FAKE_FSR_SFAULT 0xe /* SecureFault INVTRAN, INVEP or AUVIOL */ +/** + * arm_aa32_secure_pl1_0(): Return true if in Secure PL1&0 regime + * + * Return true if the CPU is in the Secure PL1&0 translation regime. + * This requires that EL3 exists and is AArch32 and we are currently + * Secure. If this is the case then the ARMMMUIdx_E10* apply and + * mean we are in EL3, not EL1. + */ +static inline bool arm_aa32_secure_pl1_0(CPUARMState *env) +{ + return arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3) && arm_is_secure(env); +} + /** * raise_exception: Raise the specified exception. * Raise a guest exception with the specified value, syndrome register @@ -808,7 +822,12 @@ static inline ARMMMUIdx core_to_aa64_mmu_idx(int mmu_idx) return mmu_idx | ARM_MMU_IDX_A; } -int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx); +/** + * Return the exception level we're running at if our current MMU index + * is @mmu_idx. @s_pl1_0 should be true if this is the AArch32 + * Secure PL1&0 translation regime. + */ +int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx, bool s_pl1_0); /* Return the MMU index for a v7M CPU in the specified security state */ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate); @@ -903,11 +922,11 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) return 3; case ARMMMUIdx_E10_0: case ARMMMUIdx_Stage1_E0: - return arm_el_is_aa64(env, 3) || !arm_is_secure_below_el3(env) ? 1 : 3; - case ARMMMUIdx_Stage1_E1: - case ARMMMUIdx_Stage1_E1_PAN: case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: + return arm_el_is_aa64(env, 3) || !arm_is_secure_below_el3(env) ? 1 : 3; case ARMMMUIdx_MPrivNegPri: case ARMMMUIdx_MUserNegPri: case ARMMMUIdx_MPriv: diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 4476b32ff5..278004661b 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3576,7 +3576,11 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: case ARMMMUIdx_E2: - ss = arm_security_space_below_el3(env); + if (arm_aa32_secure_pl1_0(env)) { + ss = ARMSS_Secure; + } else { + ss = arm_security_space_below_el3(env); + } break; case ARMMMUIdx_Stage2: /* diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index f03977b4b0..bab7822ef6 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -198,6 +198,10 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1); } + if (arm_aa32_secure_pl1_0(env)) { + DP_TBFLAG_A32(flags, S_PL1_0, 1); + } + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bc2d64e883..4684e7eb6e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -11979,7 +11979,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->tbii = EX_TBFLAG_A64(tb_flags, TBII); dc->tbid = EX_TBFLAG_A64(tb_flags, TBID); dc->tcma = EX_TBFLAG_A64(tb_flags, TCMA); - dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx, false); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index c5bc691d92..e2748ff2bb 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -7546,10 +7546,6 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); - dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); -#if !defined(CONFIG_USER_ONLY) - dc->user = (dc->current_el == 0); -#endif dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); @@ -7580,7 +7576,12 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) } dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); + dc->s_pl1_0 = EX_TBFLAG_A32(tb_flags, S_PL1_0); } + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx, dc->s_pl1_0); +#if !defined(CONFIG_USER_ONLY) + dc->user = (dc->current_el == 0); +#endif dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; dc->features = env->features; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 01c217f4a4..3f0e9ceaa3 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -165,6 +165,8 @@ typedef struct DisasContext { uint8_t gm_blocksize; /* True if the current insn_start has been updated. */ bool insn_start_updated; + /* True if this is the AArch32 Secure PL1&0 translation regime */ + bool s_pl1_0; /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ int c15_cpar; /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ From 7700d2293c085c0481b71cc0f8c04ca20e7bd7d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 12 Aug 2024 12:58:44 +1000 Subject: [PATCH 2760/2884] target/i386: Assert MMX and XMM registers in range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mmx assert would fire without the fix for #2495. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Link: https://lore.kernel.org/r/20240812025844.58956-4-richard.henderson@linaro.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/tcg/emit.c.inc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 016dce8146..22a06897fb 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -33,8 +33,13 @@ #define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i32_valid #endif +#define MMX_OFFSET(reg) \ + ({ assert((reg) >= 0 && (reg) <= 7); \ + offsetof(CPUX86State, fpregs[reg].mmx); }) -#define ZMM_OFFSET(reg) offsetof(CPUX86State, xmm_regs[reg]) +#define ZMM_OFFSET(reg) \ + ({ assert((reg) >= 0 && (reg) <= 15); \ + offsetof(CPUX86State, xmm_regs[reg]); }) typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); typedef void (*SSEFunc_l_ep)(TCGv_i64 val, TCGv_ptr env, TCGv_ptr reg); @@ -168,7 +173,7 @@ static int vector_elem_offset(X86DecodedOp *op, MemOp ot, int n) static void compute_mmx_offset(X86DecodedOp *op) { if (!op->has_ea) { - op->offset = offsetof(CPUX86State, fpregs[op->n].mmx) + mmx_offset(op->ot); + op->offset = MMX_OFFSET(op->n) + mmx_offset(op->ot); } else { op->offset = offsetof(CPUX86State, mmx_t0) + mmx_offset(op->ot); } From 940d802b24e63650e0eacad3714e2ce171cba17c Mon Sep 17 00:00:00 2001 From: Alexander Ivanov <alexander.ivanov@virtuozzo.com> Date: Fri, 9 Aug 2024 14:13:40 +0200 Subject: [PATCH 2761/2884] module: Prevent crash by resetting local_err in module_load_qom_all() Set local_err to NULL after it has been freed in error_report_err(). This avoids triggering assert(*errp == NULL) failure in error_setv() when local_err is reused in the loop. Signed-off-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com> Reviewed-by: Claudio Fontana <cfontana@suse.de> Reviewed-by: Denis V. Lunev <den@openvz.org> Link: https://lore.kernel.org/r/20240809121340.992049-2-alexander.ivanov@virtuozzo.com [Do the same by moving the declaration instead. - Paolo] Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- util/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/module.c b/util/module.c index 32e263163c..3eb0f06df1 100644 --- a/util/module.c +++ b/util/module.c @@ -354,13 +354,13 @@ int module_load_qom(const char *type, Error **errp) void module_load_qom_all(void) { const QemuModinfo *modinfo; - Error *local_err = NULL; if (module_loaded_qom_all) { return; } for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + Error *local_err = NULL; if (!modinfo->objs) { continue; } From 3ef11c991e501768f2fa646e8438f075be1cd2f5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Tue, 13 Aug 2024 19:01:36 +0200 Subject: [PATCH 2762/2884] po: update Italian translation Reported-by: bovirus <https://gitlab.com/bovirus> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2451 Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- po/it.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/it.po b/po/it.po index c6d9517207..363b9bddf2 100644 --- a/po/it.po +++ b/po/it.po @@ -65,7 +65,7 @@ msgid "Detach Tab" msgstr "_Sposta in una nuova finestra" msgid "Show Menubar" -msgstr "" +msgstr "Mostra _barra dei menu" msgid "_Machine" msgstr "_Macchina virtuale" From a6e65975c3fac1b2f067fef8eeed92584d773f06 Mon Sep 17 00:00:00 2001 From: Anthony Harivel <aharivel@redhat.com> Date: Wed, 7 Aug 2024 14:43:20 +0200 Subject: [PATCH 2763/2884] target/i386: Fix arguments for vmsr_read_thread_stat() Snapshot of the stat utime and stime for each thread, taken before and after the pause, must be stored in separate locations Signed-off-by: Anthony Harivel <aharivel@redhat.com> Link: https://lore.kernel.org/r/20240807124320.1741124-2-aharivel@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target/i386/kvm/kvm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 31f149c990..2fa88ef1e3 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2712,8 +2712,8 @@ static void *kvm_msr_energy_thread(void *data) thd_stat[i].thread_id = thread_ids[i]; vmsr_read_thread_stat(vmsr->pid, thd_stat[i].thread_id, - thd_stat[i].utime, - thd_stat[i].stime, + &thd_stat[i].utime[0], + &thd_stat[i].stime[0], &thd_stat[i].cpu_id); thd_stat[i].pkg_id = vmsr_get_physical_package_id(thd_stat[i].cpu_id); @@ -2777,8 +2777,8 @@ static void *kvm_msr_energy_thread(void *data) for (int i = 0; i < num_threads; i++) { vmsr_read_thread_stat(vmsr->pid, thd_stat[i].thread_id, - thd_stat[i].utime, - thd_stat[i].stime, + &thd_stat[i].utime[1], + &thd_stat[i].stime[1], &thd_stat[i].cpu_id); if (vmsr->pid < 0) { From 3aefee3ec01e607529a9918e2978f365c5c3b5e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 13 Aug 2024 16:33:58 +1000 Subject: [PATCH 2764/2884] linux-user: Preserve NULL hit in target_mmap subroutines Do not pass guest_base to the host mmap instead of zero hint. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2353 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- linux-user/mmap.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 4d09a72fad..6418e811f6 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -560,9 +560,13 @@ static abi_long mmap_h_eq_g(abi_ulong start, abi_ulong len, int host_prot, int flags, int page_flags, int fd, off_t offset) { - void *p, *want_p = g2h_untagged(start); + void *p, *want_p = NULL; abi_ulong last; + if (start || (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + want_p = g2h_untagged(start); + } + p = mmap(want_p, len, host_prot, flags, fd, offset); if (p == MAP_FAILED) { return -1; @@ -610,11 +614,15 @@ static abi_long mmap_h_lt_g(abi_ulong start, abi_ulong len, int host_prot, int mmap_flags, int page_flags, int fd, off_t offset, int host_page_size) { - void *p, *want_p = g2h_untagged(start); + void *p, *want_p = NULL; off_t fileend_adj = 0; int flags = mmap_flags; abi_ulong last, pass_last; + if (start || (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + want_p = g2h_untagged(start); + } + if (!(flags & MAP_ANONYMOUS)) { struct stat sb; @@ -740,12 +748,16 @@ static abi_long mmap_h_gt_g(abi_ulong start, abi_ulong len, int flags, int page_flags, int fd, off_t offset, int host_page_size) { - void *p, *want_p = g2h_untagged(start); + void *p, *want_p = NULL; off_t host_offset = offset & -host_page_size; abi_ulong last, real_start, real_last; bool misaligned_offset = false; size_t host_len; + if (start || (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + want_p = g2h_untagged(start); + } + if (!(flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { /* * Adjust the offset to something representable on the host. From b0d6c037ea0a825192285f149af72a73a1baaa04 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Thu, 15 Aug 2024 20:32:37 +1000 Subject: [PATCH 2765/2884] Update version for v9.1.0-rc2 release Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ec6abd8638..cd036422bd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.91 +9.0.92 From e4a4edc10ab6a621e1c18eb73fc3e6f5d3f7c2e1 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <pierrick.bouvier@linaro.org> Date: Wed, 14 Aug 2024 15:41:31 -0700 Subject: [PATCH 2766/2884] target/s390x: fix build warning (gcc-12 -fsanitize=thread) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found on debian stable. ../target/s390x/tcg/translate.c: In function ‘get_mem_index’: ../target/s390x/tcg/translate.c:398:1: error: control reaches end of non-void function [-Werror=return-type] 398 | } Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Ilya Leoshkevich <iii@linux.ibm.com> Message-ID: <20240814224132.897098-4-pierrick.bouvier@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> --- target/s390x/tcg/translate.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index c81e035dea..bcfff40b25 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -392,7 +392,6 @@ static int get_mem_index(DisasContext *s) return MMU_HOME_IDX; default: g_assert_not_reached(); - break; } #endif } From e25264fe7b0455b45a6eb519d8e9ba2a708228fb Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Thu, 15 Aug 2024 14:27:19 +0200 Subject: [PATCH 2767/2884] meson.build: Check for the availability of __attribute__((gcc_struct)) on MSYS2 Since quite a while MSYS2 now supports Clang as a compiler, too. Unfortunately, this compiler is lacking the __attribute__((gcc_struct)) that we need for compiling on Windows. But since the compiler is available now, some people started to use it to compile QEMU on MSYS2, apparently ignoring the compiler warnings (see for example the ticket at https://gitlab.com/qemu-project/qemu/-/issues/2476 ). These builds are likely broken in a couple of spots, so let's make sure that we rather bail out early in the configuration phase instead of allowing the build to succeed with warnings. Message-ID: <20240815122719.727639-1-thuth@redhat.com> Tested-by: Stefan Weil <sw@weilnetz.de> Signed-off-by: Thomas Huth <thuth@redhat.com> --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build index 81ecd4bae7..fbda17c987 100644 --- a/meson.build +++ b/meson.build @@ -315,6 +315,11 @@ elif host_os == 'sunos' qemu_common_flags += '-D__EXTENSIONS__' elif host_os == 'haiku' qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC'] +elif host_os == 'windows' + if not compiler.compiles('struct x { int y; } __attribute__((gcc_struct));', + args: '-Werror') + error('Your compiler does not support __attribute__((gcc_struct)) - please use GCC instead of Clang') + endif endif # __sync_fetch_and_and requires at least -march=i486. Many toolchains From 3185e5a6f79b349934aebd955f3c1d6e29bc7bc2 Mon Sep 17 00:00:00 2001 From: Cleber Rosa <crosa@redhat.com> Date: Tue, 6 Aug 2024 13:31:12 -0400 Subject: [PATCH 2768/2884] tests/avocado: apply proper skipUnless decorator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9b45cc993 added many cases of skipUnless for the sake of organizing flaky tests. But, Python decorators *must* follow what they decorate, so the newlines added should *not* exist there. Signed-off-by: Cleber Rosa <crosa@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Tested-by: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org> Message-ID: <20240806173119.582857-3-crosa@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/avocado/boot_linux_console.py | 1 - tests/avocado/intel_iommu.py | 1 - tests/avocado/linux_initrd.py | 1 - tests/avocado/machine_aspeed.py | 2 -- tests/avocado/machine_mips_malta.py | 2 -- tests/avocado/machine_rx_gdbsim.py | 1 - tests/avocado/reverse_debugging.py | 4 ---- tests/avocado/smmu.py | 1 - 8 files changed, 13 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 2929aa042d..cffdd6b5a2 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -1522,7 +1522,6 @@ class BootLinuxConsole(LinuxKernelTest): # like issues with a buggy kernel. As a result we don't want it # gating releases on Gitlab. @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_sh4_r2d(self): """ :avocado: tags=arch:sh4 diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py index 008f214397..992583fa7d 100644 --- a/tests/avocado/intel_iommu.py +++ b/tests/avocado/intel_iommu.py @@ -13,7 +13,6 @@ from avocado import skipUnless from avocado_qemu.linuxtest import LinuxTest @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - class IntelIOMMU(LinuxTest): """ :avocado: tags=arch:x86_64 diff --git a/tests/avocado/linux_initrd.py b/tests/avocado/linux_initrd.py index aad5b19bd9..7f47b98ae7 100644 --- a/tests/avocado/linux_initrd.py +++ b/tests/avocado/linux_initrd.py @@ -54,7 +54,6 @@ class LinuxInitrd(QemuSystemTest): self.assertRegex(self.vm.get_log(), expected_msg) @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_with_2gib_file_should_work_with_linux_v4_16(self): """ :avocado: tags=flaky diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index f8e263d37e..c0b01e8f1f 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -323,7 +323,6 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): self.wait_for_console_pattern('Starting kernel ...') @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_arm_ast2500_evb_sdk(self): """ :avocado: tags=arch:arm @@ -343,7 +342,6 @@ class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): self.wait_for_console_pattern('nodistro.0 ast2500-default ttyS4') @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_arm_ast2600_evb_sdk(self): """ :avocado: tags=arch:arm diff --git a/tests/avocado/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py index 8cf84bd805..07a80633b5 100644 --- a/tests/avocado/machine_mips_malta.py +++ b/tests/avocado/machine_mips_malta.py @@ -102,7 +102,6 @@ class MaltaMachineFramebuffer(QemuSystemTest): self.do_test_i6400_framebuffer_logo(1) @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_mips_malta_i6400_framebuffer_logo_7cores(self): """ :avocado: tags=arch:mips64el @@ -114,7 +113,6 @@ class MaltaMachineFramebuffer(QemuSystemTest): self.do_test_i6400_framebuffer_logo(7) @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_mips_malta_i6400_framebuffer_logo_8cores(self): """ :avocado: tags=arch:mips64el diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py index 412a7a5089..9a0bec8a6e 100644 --- a/tests/avocado/machine_rx_gdbsim.py +++ b/tests/avocado/machine_rx_gdbsim.py @@ -49,7 +49,6 @@ class RxGdbSimMachine(QemuSystemTest): #exec_command_and_wait_for_pattern(self, 'version', gcc_version) @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_linux_sash(self): """ Boots a Linux kernel and checks that the console is operational. diff --git a/tests/avocado/reverse_debugging.py b/tests/avocado/reverse_debugging.py index 92855a02a5..f24287cd0a 100644 --- a/tests/avocado/reverse_debugging.py +++ b/tests/avocado/reverse_debugging.py @@ -207,7 +207,6 @@ class ReverseDebugging_X86_64(ReverseDebugging): # unidentified gitlab timeout problem @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_x86_64_pc(self): """ :avocado: tags=arch:x86_64 @@ -225,7 +224,6 @@ class ReverseDebugging_AArch64(ReverseDebugging): # unidentified gitlab timeout problem @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_aarch64_virt(self): """ :avocado: tags=arch:aarch64 @@ -250,7 +248,6 @@ class ReverseDebugging_ppc64(ReverseDebugging): # unidentified gitlab timeout problem @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_ppc64_pseries(self): """ :avocado: tags=arch:ppc64 @@ -265,7 +262,6 @@ class ReverseDebugging_ppc64(ReverseDebugging): # See https://gitlab.com/qemu-project/qemu/-/issues/1992 @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_ppc64_powernv(self): """ :avocado: tags=arch:ppc64 diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py index aadda71e4b..83fd79e922 100644 --- a/tests/avocado/smmu.py +++ b/tests/avocado/smmu.py @@ -14,7 +14,6 @@ from avocado_qemu import BUILD_DIR from avocado_qemu.linuxtest import LinuxTest @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - class SMMU(LinuxTest): """ :avocado: tags=accel:kvm From 6d67a65f10f3aba3fab8eb3e9a71132f23a5db84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Thu, 18 Jul 2024 10:41:58 +0100 Subject: [PATCH 2769/2884] ci: add gtk-vnc to the deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gtk-vnc package is used by the vnc-display-test qtest program. Technically only gvnc is needed, but since we already pull in the gtk3 dep, it is harmless to depend on gtk-vnc. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-ID: <20240718094159.902024-2-berrange@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/lcitool/projects/qemu.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 0c85784259..252e871f80 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -32,6 +32,7 @@ packages: - glusterfs - gnutls - gtk3 + - gtk-vnc - hostname - json-c - libaio From b4be15a9f9e73fa2e7c46da559f10afc94018984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Thu, 18 Jul 2024 10:41:59 +0100 Subject: [PATCH 2770/2884] ci: refresh package lists with lcitool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refresh with the newly added gtk-vnc package Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240718094159.902024-3-berrange@redhat.com> [thuth: fixed conflicts in .gitlab-ci.d/cirrus/*.vars] Signed-off-by: Thomas Huth <thuth@redhat.com> --- .gitlab-ci.d/cirrus/freebsd-13.vars | 2 +- .gitlab-ci.d/cirrus/macos-13.vars | 2 +- .gitlab-ci.d/cirrus/macos-14.vars | 2 +- scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml | 1 + scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml | 1 + tests/docker/dockerfiles/alpine.docker | 1 + tests/docker/dockerfiles/debian-amd64-cross.docker | 1 + tests/docker/dockerfiles/debian-arm64-cross.docker | 1 + tests/docker/dockerfiles/debian-armel-cross.docker | 1 + tests/docker/dockerfiles/debian-armhf-cross.docker | 1 + tests/docker/dockerfiles/debian-i686-cross.docker | 1 + tests/docker/dockerfiles/debian-mips64el-cross.docker | 1 + tests/docker/dockerfiles/debian-mipsel-cross.docker | 1 + tests/docker/dockerfiles/debian-ppc64el-cross.docker | 1 + tests/docker/dockerfiles/debian-s390x-cross.docker | 1 + tests/docker/dockerfiles/debian.docker | 1 + tests/docker/dockerfiles/fedora-win64-cross.docker | 1 + tests/docker/dockerfiles/fedora.docker | 1 + tests/docker/dockerfiles/opensuse-leap.docker | 1 + tests/docker/dockerfiles/ubuntu2204.docker | 1 + tests/vm/generated/freebsd.json | 1 + 21 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars index 29ab9645f9..69c948b503 100644 --- a/.gitlab-ci.d/cirrus/freebsd-13.vars +++ b/.gitlab-ci.d/cirrus/freebsd-13.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-sphinx py311-sphinx_rtd_theme py311-tomli py311-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk-vnc gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-sphinx py311-sphinx_rtd_theme py311-tomli py311-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-13.vars b/.gitlab-ci.d/cirrus/macos-13.vars index 534f029956..ac3fa3a847 100644 --- a/.gitlab-ci.d/cirrus/macos-13.vars +++ b/.gitlab-ci.d/cirrus/macos-13.vars @@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake' NINJA='/opt/homebrew/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/opt/homebrew/bin/pip3' -PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' +PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' PYTHON='/opt/homebrew/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-14.vars b/.gitlab-ci.d/cirrus/macos-14.vars index 43070f4a26..24cfec3b89 100644 --- a/.gitlab-ci.d/cirrus/macos-14.vars +++ b/.gitlab-ci.d/cirrus/macos-14.vars @@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake' NINJA='/opt/homebrew/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/opt/homebrew/bin/pip3' -PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' +PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' PYTHON='/opt/homebrew/bin/python3' diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml index fd5489cd82..71a0f0c433 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -49,6 +49,7 @@ packages: - libglusterfs-dev - libgnutls28-dev - libgtk-3-dev + - libgtk-vnc-2.0-dev - libibverbs-dev - libiscsi-dev - libjemalloc-dev diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml index afa04502cf..d8de967b18 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -49,6 +49,7 @@ packages: - libglusterfs-dev - libgnutls28-dev - libgtk-3-dev + - libgtk-vnc-2.0-dev - libibverbs-dev - libiscsi-dev - libjemalloc-dev diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index b079a83fe2..54b9721997 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -40,6 +40,7 @@ RUN apk update && \ glib-static \ gnutls-dev \ gtk+3.0-dev \ + gtk-vnc-dev \ json-c-dev \ libaio-dev \ libbpf-dev \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 8058695979..136c3a79a1 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 15457d7657..233f6ee1de 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index c26ffc2e9e..8476fc8cce 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 8f87656d89..f26385e0b9 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index f4ef054a2e..3fe8ee623d 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 59c4c68dce..2862785692 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 880c774f1c..0d559ae4ba 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 1d55b9514c..8c1dcec9cf 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 62ccda6ab1..72668e0315 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -30,6 +30,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ + libgtk-vnc-2.0-dev \ libpcre2-dev \ libsndio-dev \ libspice-protocol-dev \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 0d1d401eb8..42bd0067d1 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -55,6 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev \ libgnutls28-dev \ libgtk-3-dev \ + libgtk-vnc-2.0-dev \ libibverbs-dev \ libiscsi-dev \ libjemalloc-dev \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 007e1574bd..6b264d901f 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -34,6 +34,7 @@ exec "$@"\n' > /usr/bin/nosync && \ git \ glib2-devel \ glibc-langpack-en \ + gtk-vnc2-devel \ hostname \ llvm \ make \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 44f239c088..ecdefaff1a 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -50,6 +50,7 @@ exec "$@"\n' > /usr/bin/nosync && \ glibc-static \ glusterfs-api-devel \ gnutls-devel \ + gtk-vnc2-devel \ gtk3-devel \ hostname \ jemalloc-devel \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 836f531ac1..66143621fe 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -33,6 +33,7 @@ RUN zypper update -y && \ glibc-locale \ glibc-static \ glusterfs-devel \ + gtk-vnc-devel \ gtk3-devel \ hostname \ jemalloc-devel \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index beeb44fc28..3a7de6a318 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -55,6 +55,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev \ libgnutls28-dev \ libgtk-3-dev \ + libgtk-vnc-2.0-dev \ libibverbs-dev \ libiscsi-dev \ libjemalloc-dev \ diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 2a361cecd0..d5f0b62ec1 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -29,6 +29,7 @@ "gmake", "gnutls", "gsed", + "gtk-vnc", "gtk3", "json-c", "libepoxy", From 8a69613e9ccac6c9eb97fb969348284469fd9395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 13 Aug 2024 21:23:09 +0100 Subject: [PATCH 2771/2884] tests/avocado: Re-enable gdbsim-r5f562n8 testing U-Boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We disabled all RX tests on commit 9b45cc9931 ("docs/devel: rationalise unstable gitlab tests under FLAKY_TESTS") for being flaky. However I don't recall the U-Boot test to fail (the problematic line checking the 'version' string is already commented out), and I'm running this test reliably, so re-enable it. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Yoshinori Sato <ysato@users.sourceforge.jp> Message-Id: <20240801172332.65701-1-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-2-alex.bennee@linaro.org> --- tests/avocado/machine_rx_gdbsim.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py index 9a0bec8a6e..6bd9ce8199 100644 --- a/tests/avocado/machine_rx_gdbsim.py +++ b/tests/avocado/machine_rx_gdbsim.py @@ -22,8 +22,6 @@ class RxGdbSimMachine(QemuSystemTest): timeout = 30 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_uboot(self): """ U-Boot and checks that the console is operational. From 6fe12bc6593638932d195dd71517dc8048cdb2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Tue, 13 Aug 2024 21:23:10 +0100 Subject: [PATCH 2772/2884] Makefile: trigger re-configure on updated pythondeps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we add additional deps for meson we need to ensure we trigger a reconfigure to make sure everything is set up. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-3-alex.bennee@linaro.org> --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 02a257584b..917c9a34d1 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,8 @@ x := $(shell rm -rf meson-private meson-info meson-logs) endif # 1. ensure config-host.mak is up-to-date -config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/VERSION +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh \ + $(SRC_PATH)/pythondeps.toml $(SRC_PATH)/VERSION @echo config-host.mak is out-of-date, running configure @if test -f meson-private/coredata.dat; then \ ./config.status --skip-meson; \ From 819039a5ad6044467ddad96e656f430198fe1c20 Mon Sep 17 00:00:00 2001 From: Gustavo Romero <gustavo.romero@linaro.org> Date: Tue, 13 Aug 2024 21:23:11 +0100 Subject: [PATCH 2773/2884] configure: Fix arch detection for GDB_HAS_MTE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GDB_HAS_MTE must only be set if GDB supports the aarch64 arch, so the test if "aarch64" string is present must be against GDB-related '$gdb_arches' variable and not against '$arch' variable. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> Message-Id: <20240804161850.2646299-2-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-4-alex.bennee@linaro.org> --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 019fcbd0ef..a3aa257fd3 100755 --- a/configure +++ b/configure @@ -1673,7 +1673,7 @@ for target in $target_list; do echo "GDB=$gdb_bin" >> $config_target_mak fi - if test "${arch}" = "aarch64" && version_ge ${gdb_version##* } 15.0; then + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge ${gdb_version##* } 15.0; then echo "GDB_HAS_MTE=y" >> $config_target_mak fi From 34a4ef1c5cc79bf924938b8cbd5f92146a23a7cb Mon Sep 17 00:00:00 2001 From: Gustavo Romero <gustavo.romero@linaro.org> Date: Tue, 13 Aug 2024 21:23:12 +0100 Subject: [PATCH 2774/2884] configure: Avoid use of param. expansion when using gdb_version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit $gdb_version is now used in more than one conditional case and its usage in such cases may increase in the future. Therefore, avoid using shell parameter expansion when using it by setting gdb_version to its final form. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> Message-Id: <20240804161850.2646299-3-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-5-alex.bennee@linaro.org> --- configure | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/configure b/configure index a3aa257fd3..97de85d710 100755 --- a/configure +++ b/configure @@ -1103,8 +1103,10 @@ fi # gdb test if test -n "$gdb_bin"; then - gdb_version=$($gdb_bin --version | head -n 1) - if version_ge ${gdb_version##* } 9.1; then + gdb_version_string=$($gdb_bin --version | head -n 1) + # Extract last field in the version string + gdb_version=${gdb_version_string##* } + if version_ge $gdb_version 9.1; then gdb_arches=$($python "$source_path/scripts/probe-gdb-support.py" $gdb_bin) else gdb_bin="" @@ -1673,7 +1675,7 @@ for target in $target_list; do echo "GDB=$gdb_bin" >> $config_target_mak fi - if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge ${gdb_version##* } 15.0; then + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.0; then echo "GDB_HAS_MTE=y" >> $config_target_mak fi From 5f9ad35e044b9b295fa2ff1315c83c5f3faa3357 Mon Sep 17 00:00:00 2001 From: Gustavo Romero <gustavo.romero@linaro.org> Date: Tue, 13 Aug 2024 21:23:13 +0100 Subject: [PATCH 2775/2884] configure: Fix GDB version detection for GDB_HAS_MTE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test gdbstub/test-mte.py requires a GDB version that supports the qIsAddressTagged packet. According to GDB NEWS [0], this packet was first made available in the GDB 15.1 release, not in 15.0, so this commit fixes it in configure. [0] https://www.sourceware.org/gdb/news/ Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2477 Message-Id: <20240804161850.2646299-4-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-6-alex.bennee@linaro.org> --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 97de85d710..d08b71f14b 100755 --- a/configure +++ b/configure @@ -1675,7 +1675,7 @@ for target in $target_list; do echo "GDB=$gdb_bin" >> $config_target_mak fi - if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.0; then + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.1; then echo "GDB_HAS_MTE=y" >> $config_target_mak fi From 503eb470e087d4f611be1c584e58fe3b0bf250e2 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella <sgarzare@redhat.com> Date: Tue, 13 Aug 2024 21:23:14 +0100 Subject: [PATCH 2776/2884] scripts/checkpatch: more checks on files imported from Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a file imported from Linux is touched, emit a warning and suggest using scripts/update-linux-headers.sh. Also check that updating imported files from Linux are not mixed with other changes, in which case emit an error. Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> Reviewed-by: Cornelia Huck <cohuck@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20240718072050.9503-1-sgarzare@redhat.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-7-alex.bennee@linaro.org> --- scripts/checkpatch.pl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index ff373a7083..65b6f46f90 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1374,6 +1374,9 @@ sub process { my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch my $reported_maintainer_file = 0; + my $reported_mixing_imported_file = 0; + my $in_imported_file = 0; + my $in_no_imported_file = 0; my $non_utf8_charset = 0; our @report = (); @@ -1673,6 +1676,27 @@ sub process { # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); +# Check that updating imported files from Linux are not mixed with other changes + if ($realfile =~ /^(linux-headers|include\/standard-headers)\//) { + if (!$in_imported_file) { + WARN("added, moved or deleted file(s) " . + "imported from Linux, are you using " . + "scripts/update-linux-headers.sh?\n" . + $herecurr); + } + $in_imported_file = 1; + } else { + $in_no_imported_file = 1; + } + + if (!$reported_mixing_imported_file && + $in_imported_file && $in_no_imported_file) { + ERROR("headers imported from Linux should be self-" . + "contained in a patch with no other changes\n" . + $herecurr); + $reported_mixing_imported_file = 1; + } + # ignore files that are being periodically imported from Linux next if ($realfile =~ /^(linux-headers|include\/standard-headers)\//); From cf584a908acd62bf7bc08b8f7a055209f497a266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Tue, 13 Aug 2024 21:23:15 +0100 Subject: [PATCH 2777/2884] target/i386: allow access_ptr to force slow path on failed probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are using TCG plugin memory callbacks probe_access_internal will return TLB_MMIO to force the slow path for memory access. This results in probe_access returning NULL but the x86 access_ptr function happily accepts an empty haddr resulting in segfault hilarity. Check for an empty haddr to prevent the segfault and enable plugins to track all the memory operations for the x86 save/restore helpers. As we also want to run the slow path when instrumenting *-user we should also not have the short cutting test_ptr macro. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2489 Fixes: 6d03226b42 (plugins: force slow path when plugins instrument memory ops) Reviewed-by: Alexandre Iooss <erdnaxe@crans.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-8-alex.bennee@linaro.org> --- target/i386/tcg/access.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/target/i386/tcg/access.c b/target/i386/tcg/access.c index 56a1181ea5..e68b73a24b 100644 --- a/target/i386/tcg/access.c +++ b/target/i386/tcg/access.c @@ -58,6 +58,11 @@ static void *access_ptr(X86Access *ac, vaddr addr, unsigned len) assert(addr >= ac->vaddr); + /* No haddr means probe_access wants to force slow path */ + if (!ac->haddr1) { + return NULL; + } + #ifdef CONFIG_USER_ONLY assert(offset <= ac->size1 - len); return ac->haddr1 + offset; @@ -78,17 +83,11 @@ static void *access_ptr(X86Access *ac, vaddr addr, unsigned len) #endif } -#ifdef CONFIG_USER_ONLY -# define test_ptr(p) true -#else -# define test_ptr(p) likely(p) -#endif - uint8_t access_ldb(X86Access *ac, vaddr addr) { void *p = access_ptr(ac, addr, sizeof(uint8_t)); - if (test_ptr(p)) { + if (likely(p)) { return ldub_p(p); } return cpu_ldub_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); @@ -98,7 +97,7 @@ uint16_t access_ldw(X86Access *ac, vaddr addr) { void *p = access_ptr(ac, addr, sizeof(uint16_t)); - if (test_ptr(p)) { + if (likely(p)) { return lduw_le_p(p); } return cpu_lduw_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); @@ -108,7 +107,7 @@ uint32_t access_ldl(X86Access *ac, vaddr addr) { void *p = access_ptr(ac, addr, sizeof(uint32_t)); - if (test_ptr(p)) { + if (likely(p)) { return ldl_le_p(p); } return cpu_ldl_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); @@ -118,7 +117,7 @@ uint64_t access_ldq(X86Access *ac, vaddr addr) { void *p = access_ptr(ac, addr, sizeof(uint64_t)); - if (test_ptr(p)) { + if (likely(p)) { return ldq_le_p(p); } return cpu_ldq_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); @@ -128,7 +127,7 @@ void access_stb(X86Access *ac, vaddr addr, uint8_t val) { void *p = access_ptr(ac, addr, sizeof(uint8_t)); - if (test_ptr(p)) { + if (likely(p)) { stb_p(p, val); } else { cpu_stb_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); @@ -139,7 +138,7 @@ void access_stw(X86Access *ac, vaddr addr, uint16_t val) { void *p = access_ptr(ac, addr, sizeof(uint16_t)); - if (test_ptr(p)) { + if (likely(p)) { stw_le_p(p, val); } else { cpu_stw_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); @@ -150,7 +149,7 @@ void access_stl(X86Access *ac, vaddr addr, uint32_t val) { void *p = access_ptr(ac, addr, sizeof(uint32_t)); - if (test_ptr(p)) { + if (likely(p)) { stl_le_p(p, val); } else { cpu_stl_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); @@ -161,7 +160,7 @@ void access_stq(X86Access *ac, vaddr addr, uint64_t val) { void *p = access_ptr(ac, addr, sizeof(uint64_t)); - if (test_ptr(p)) { + if (likely(p)) { stq_le_p(p, val); } else { cpu_stq_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); From 20fdd01e51a2799f8185bc0ae0e3e000fb2ced7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 13 Aug 2024 21:23:16 +0100 Subject: [PATCH 2778/2884] buildsys: Fix building without plugins on Darwin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 0082475e26 the plugin symbol list is unconditionally added to the linker flags, leading to a build failure: Undefined symbols for architecture arm64: "_qemu_plugin_entry_code", referenced from: <initial-undefines> ... ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) ninja: build stopped: subcommand failed. Fix by restricting the whole meson file to the --enable-plugins configure argument. Fixes: 0082475e26 ("meson: merge plugin_ldflags into emulator_link_args") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2476 Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240813112457.92560-1-philmd@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-9-alex.bennee@linaro.org> --- plugins/meson.build | 50 +++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/plugins/meson.build b/plugins/meson.build index 18a0303bff..1cc039d29b 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,3 +1,7 @@ +if not get_option('plugins') + subdir_done() +endif + # Modules need more symbols than just those in plugins/qemu-plugins.symbols if not enable_modules if host_os == 'darwin' @@ -12,29 +16,27 @@ if not enable_modules endif endif -if get_option('plugins') - if host_os == 'windows' - dlltool = find_program('dlltool', required: true) +if host_os == 'windows' + dlltool = find_program('dlltool', required: true) - # Generate a .lib file for plugins to link against. - # First, create a .def file listing all the symbols a plugin should expect to have - # available in qemu - win32_plugin_def = configure_file( - input: files('qemu-plugins.symbols'), - output: 'qemu_plugin_api.def', - capture: true, - command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) - # then use dlltool to assemble a delaylib. - win32_qemu_plugin_api_lib = configure_file( - input: win32_plugin_def, - output: 'libqemu_plugin_api.a', - command: [dlltool, '--input-def', '@INPUT@', - '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] - ) - endif - specific_ss.add(files( - 'loader.c', - 'core.c', - 'api.c', - )) + # Generate a .lib file for plugins to link against. + # First, create a .def file listing all the symbols a plugin should expect to have + # available in qemu + win32_plugin_def = configure_file( + input: files('qemu-plugins.symbols'), + output: 'qemu_plugin_api.def', + capture: true, + command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) + # then use dlltool to assemble a delaylib. + win32_qemu_plugin_api_lib = configure_file( + input: win32_plugin_def, + output: 'libqemu_plugin_api.a', + command: [dlltool, '--input-def', '@INPUT@', + '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + ) endif +specific_ss.add(files( + 'loader.c', + 'core.c', + 'api.c', +)) From 00140e79bb5e74c5a06f9eb0a0110f4e5ebffaec Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:17 +0100 Subject: [PATCH 2779/2884] scripts/replay-dump.py: Update to current rr record format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v12 format support for replay-dump has a few issues still. This fixes async decoding; adds event, shutdown, and end decoding; fixes audio in / out events, fixes checkpoint checking of following async events. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-2-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-10-alex.bennee@linaro.org> --- scripts/replay-dump.py | 127 ++++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 34 deletions(-) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index d668193e79..419ee3257b 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -20,6 +20,7 @@ import argparse import struct +import os from collections import namedtuple from os import path @@ -134,6 +135,17 @@ def swallow_async_qword(eid, name, dumpfile): print(" %s(%d) @ %d" % (name, eid, step_id)) return True +def swallow_bytes(eid, name, dumpfile, nr): + """Swallow nr bytes of data without looking at it""" + dumpfile.seek(nr, os.SEEK_CUR) + +def decode_exception(eid, name, dumpfile): + print_event(eid, name) + return True + +# v12 does away with the additional event byte and encodes it in the main type +# Between v8 and v9, REPLAY_ASYNC_BH_ONESHOT was added, but we don't decode +# those versions so leave it out. async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp), Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp), @@ -142,8 +154,8 @@ async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp), ] # See replay_read_events/replay_read_event -def decode_async(eid, name, dumpfile): - """Decode an ASYNC event""" +def decode_async_old(eid, name, dumpfile): + """Decode an ASYNC event (pre-v8)""" print_event(eid, name) @@ -157,6 +169,35 @@ def decode_async(eid, name, dumpfile): return call_decode(async_decode_table, async_event_kind, dumpfile) +def decode_async_bh(eid, name, dumpfile): + op_id = read_qword(dumpfile) + print_event(eid, name) + return True + +def decode_async_bh_oneshot(eid, name, dumpfile): + op_id = read_qword(dumpfile) + print_event(eid, name) + return True + +def decode_async_char_read(eid, name, dumpfile): + char_id = read_byte(dumpfile) + size = read_dword(dumpfile) + print_event(eid, name, "device:%x chars:%s" % (char_id, dumpfile.read(size))) + return True + +def decode_async_block(eid, name, dumpfile): + op_id = read_qword(dumpfile) + print_event(eid, name) + return True + +def decode_async_net(eid, name, dumpfile): + net_id = read_byte(dumpfile) + flags = read_dword(dumpfile) + size = read_dword(dumpfile) + swallow_bytes(eid, name, dumpfile, size) + print_event(eid, name, "net:%x flags:%x bytes:%d" % (net_id, flags, size)) + return True + total_insns = 0 def decode_instruction(eid, name, dumpfile): @@ -166,6 +207,10 @@ def decode_instruction(eid, name, dumpfile): print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns)) return True +def decode_shutdown(eid, name, dumpfile): + print_event(eid, name) + return True + def decode_char_write(eid, name, dumpfile): res = read_dword(dumpfile) offset = read_dword(dumpfile) @@ -177,7 +222,7 @@ def decode_audio_out(eid, name, dumpfile): print_event(eid, name, "%d" % (audio_data)) return True -def decode_checkpoint(eid, name, dumpfile): +def __decode_checkpoint(eid, name, dumpfile, old): """Decode a checkpoint. Checkpoints contain a series of async events with their own specific data. @@ -189,14 +234,20 @@ def decode_checkpoint(eid, name, dumpfile): # if the next event is EVENT_ASYNC there are a bunch of # async events to read, otherwise we are done - if next_event != 3: - print_event(eid, name, "no additional data", event_number) - else: + if (old and next_event == 3) or (not old and next_event >= 3 and next_event <= 9): print_event(eid, name, "more data follows", event_number) + else: + print_event(eid, name, "no additional data", event_number) replay_state.reuse_event(next_event) return True +def decode_checkpoint_old(eid, name, dumpfile): + return __decode_checkpoint(eid, name, dumpfile, False) + +def decode_checkpoint(eid, name, dumpfile): + return __decode_checkpoint(eid, name, dumpfile, True) + def decode_checkpoint_init(eid, name, dumpfile): print_event(eid, name) return True @@ -212,15 +263,23 @@ def decode_clock(eid, name, dumpfile): def decode_random(eid, name, dumpfile): ret = read_dword(dumpfile) - data = read_array(dumpfile) - print_event(eid, "%d bytes of random data" % len(data)) + size = read_dword(dumpfile) + swallow_bytes(eid, name, dumpfile, size) + if (ret): + print_event(eid, name, "%d bytes (getrandom failed)" % (size)) + else: + print_event(eid, name, "%d bytes" % (size)) return True +def decode_end(eid, name, dumpfile): + print_event(eid, name) + return False + # pre-MTTCG merge v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), Decoder(2, "EVENT_EXCEPTION", decode_plain), - Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(3, "EVENT_ASYNC", decode_async_old), Decoder(4, "EVENT_SHUTDOWN", decode_unimp), Decoder(5, "EVENT_CHAR_WRITE", decode_char_write), Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), @@ -242,7 +301,7 @@ v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), Decoder(2, "EVENT_EXCEPTION", decode_plain), - Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(3, "EVENT_ASYNC", decode_async_old), Decoder(4, "EVENT_SHUTDOWN", decode_unimp), Decoder(5, "EVENT_CHAR_WRITE", decode_char_write), Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), @@ -266,7 +325,7 @@ v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), Decoder(2, "EVENT_EXCEPTION", decode_unimp), - Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(3, "EVENT_ASYNC", decode_async_old), Decoder(4, "EVENT_SHUTDOWN", decode_unimp), Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp), @@ -296,32 +355,31 @@ v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), v12_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), - Decoder(2, "EVENT_EXCEPTION", decode_plain), - Decoder(3, "EVENT_ASYNC", decode_async), - Decoder(4, "EVENT_ASYNC", decode_async), - Decoder(5, "EVENT_ASYNC", decode_async), - Decoder(6, "EVENT_ASYNC", decode_async), - Decoder(6, "EVENT_ASYNC", decode_async), - Decoder(8, "EVENT_ASYNC", decode_async), - Decoder(9, "EVENT_ASYNC", decode_async), - Decoder(10, "EVENT_ASYNC", decode_async), - Decoder(11, "EVENT_SHUTDOWN", decode_unimp), - Decoder(12, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), - Decoder(13, "EVENT_SHUTDOWN_HOST_QMP_QUIT", decode_unimp), - Decoder(14, "EVENT_SHUTDOWN_HOST_QMP_RESET", decode_unimp), - Decoder(14, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp), - Decoder(15, "EVENT_SHUTDOWN_HOST_UI", decode_unimp), - Decoder(16, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp), - Decoder(17, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp), - Decoder(18, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp), - Decoder(19, "EVENT_SHUTDOWN_GUEST_SUBSYSTEM_RESET", decode_unimp), - Decoder(20, "EVENT_SHUTDOWN_GUEST_SNAPSHOT_LOAD", decode_unimp), - Decoder(21, "EVENT_SHUTDOWN___MAX", decode_unimp), + Decoder(2, "EVENT_EXCEPTION", decode_exception), + Decoder(3, "EVENT_ASYNC_BH", decode_async_bh), + Decoder(4, "EVENT_ASYNC_BH_ONESHOT", decode_async_bh_oneshot), + Decoder(5, "EVENT_ASYNC_INPUT", decode_unimp), + Decoder(6, "EVENT_ASYNC_INPUT_SYNC", decode_unimp), + Decoder(7, "EVENT_ASYNC_CHAR_READ", decode_async_char_read), + Decoder(8, "EVENT_ASYNC_BLOCK", decode_async_block), + Decoder(9, "EVENT_ASYNC_NET", decode_async_net), + Decoder(10, "EVENT_SHUTDOWN", decode_shutdown), + Decoder(11, "EVENT_SHUTDOWN_HOST_ERR", decode_shutdown), + Decoder(12, "EVENT_SHUTDOWN_HOST_QMP_QUIT", decode_shutdown), + Decoder(13, "EVENT_SHUTDOWN_HOST_QMP_RESET", decode_shutdown), + Decoder(14, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_shutdown), + Decoder(15, "EVENT_SHUTDOWN_HOST_UI", decode_shutdown), + Decoder(16, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_shutdown), + Decoder(17, "EVENT_SHUTDOWN_GUEST_RESET", decode_shutdown), + Decoder(18, "EVENT_SHUTDOWN_GUEST_PANIC", decode_shutdown), + Decoder(19, "EVENT_SHUTDOWN_SUBSYS_RESET", decode_shutdown), + Decoder(20, "EVENT_SHUTDOWN_SNAPSHOT_LOAD", decode_shutdown), + Decoder(21, "EVENT_SHUTDOWN___MAX", decode_shutdown), Decoder(22, "EVENT_CHAR_WRITE", decode_char_write), Decoder(23, "EVENT_CHAR_READ_ALL", decode_unimp), Decoder(24, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), - Decoder(25, "EVENT_AUDIO_IN", decode_unimp), - Decoder(26, "EVENT_AUDIO_OUT", decode_audio_out), + Decoder(25, "EVENT_AUDIO_OUT", decode_audio_out), + Decoder(26, "EVENT_AUDIO_IN", decode_unimp), Decoder(27, "EVENT_RANDOM", decode_random), Decoder(28, "EVENT_CLOCK_HOST", decode_clock), Decoder(29, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), @@ -334,6 +392,7 @@ v12_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(36, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), Decoder(37, "EVENT_CP_INIT", decode_checkpoint_init), Decoder(38, "EVENT_CP_RESET", decode_checkpoint), + Decoder(39, "EVENT_END", decode_end), ] def parse_arguments(): From 01a385fb49cdc56f44291037d08f4d78fbea941b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:18 +0100 Subject: [PATCH 2780/2884] scripts/replay-dump.py: rejig decoders in event number order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort decoder functions to be ascending in order of event number, same as the decoder tables. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-3-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-11-alex.bennee@linaro.org> --- scripts/replay-dump.py | 56 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index 419ee3257b..b82659cfb6 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -139,6 +139,19 @@ def swallow_bytes(eid, name, dumpfile, nr): """Swallow nr bytes of data without looking at it""" dumpfile.seek(nr, os.SEEK_CUR) +total_insns = 0 + +def decode_instruction(eid, name, dumpfile): + global total_insns + ins_diff = read_dword(dumpfile) + total_insns += ins_diff + print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns)) + return True + +def decode_interrupt(eid, name, dumpfile): + print_event(eid, name) + return True + def decode_exception(eid, name, dumpfile): print_event(eid, name) return True @@ -198,15 +211,6 @@ def decode_async_net(eid, name, dumpfile): print_event(eid, name, "net:%x flags:%x bytes:%d" % (net_id, flags, size)) return True -total_insns = 0 - -def decode_instruction(eid, name, dumpfile): - global total_insns - ins_diff = read_dword(dumpfile) - total_insns += ins_diff - print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns)) - return True - def decode_shutdown(eid, name, dumpfile): print_event(eid, name) return True @@ -222,6 +226,21 @@ def decode_audio_out(eid, name, dumpfile): print_event(eid, name, "%d" % (audio_data)) return True +def decode_random(eid, name, dumpfile): + ret = read_dword(dumpfile) + size = read_dword(dumpfile) + swallow_bytes(eid, name, dumpfile, size) + if (ret): + print_event(eid, name, "%d bytes (getrandom failed)" % (size)) + else: + print_event(eid, name, "%d bytes" % (size)) + return True + +def decode_clock(eid, name, dumpfile): + clock_data = read_qword(dumpfile) + print_event(eid, name, "0x%x" % (clock_data)) + return True + def __decode_checkpoint(eid, name, dumpfile, old): """Decode a checkpoint. @@ -252,25 +271,6 @@ def decode_checkpoint_init(eid, name, dumpfile): print_event(eid, name) return True -def decode_interrupt(eid, name, dumpfile): - print_event(eid, name) - return True - -def decode_clock(eid, name, dumpfile): - clock_data = read_qword(dumpfile) - print_event(eid, name, "0x%x" % (clock_data)) - return True - -def decode_random(eid, name, dumpfile): - ret = read_dword(dumpfile) - size = read_dword(dumpfile) - swallow_bytes(eid, name, dumpfile, size) - if (ret): - print_event(eid, name, "%d bytes (getrandom failed)" % (size)) - else: - print_event(eid, name, "%d bytes" % (size)) - return True - def decode_end(eid, name, dumpfile): print_event(eid, name) return False From 4926b6e6444ffcfcea6f60b855debca88bf9db2e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:19 +0100 Subject: [PATCH 2781/2884] tests/avocado: excercise scripts/replay-dump.py in replay tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This runs replay-dump.py after recording a trace, and fails the test if the script fails. replay-dump.py is modified to exit with non-zero if an error is encountered while parsing, to support this. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> gitlab with this change v5: Update timeout to 180s because x86 was just exceeding 120s in Message-Id: <20240813050638.446172-4-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-12-alex.bennee@linaro.org> --- scripts/replay-dump.py | 6 ++++-- tests/avocado/replay_kernel.py | 13 ++++++++++++- tests/avocado/replay_linux.py | 10 ++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index b82659cfb6..4ce7ff51cc 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -21,6 +21,7 @@ import argparse import struct import os +import sys from collections import namedtuple from os import path @@ -100,7 +101,7 @@ def call_decode(table, index, dumpfile): print("Could not decode index: %d" % (index)) print("Entry is: %s" % (decoder)) print("Decode Table is:\n%s" % (table)) - return False + raise(Exception("unknown event")) else: return decoder.fn(decoder.eid, decoder.name, dumpfile) @@ -121,7 +122,7 @@ def print_event(eid, name, string=None, event_count=None): def decode_unimp(eid, name, _unused_dumpfile): "Unimplemented decoder, will trigger exit" print("%s not handled - will now stop" % (name)) - return False + raise(Exception("unhandled event")) def decode_plain(eid, name, _unused_dumpfile): "Plain events without additional data" @@ -434,6 +435,7 @@ def decode_file(filename): dumpfile) except Exception as inst: print(f"error {inst}") + sys.exit(1) finally: print(f"Reached {dumpfile.tell()} of {dumpsize} bytes") diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 232d287c27..a668af9d36 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -13,6 +13,7 @@ import lzma import shutil import logging import time +import subprocess from avocado import skip from avocado import skipUnless @@ -31,7 +32,7 @@ class ReplayKernelBase(LinuxKernelTest): terminates. """ - timeout = 120 + timeout = 180 KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' def run_vm(self, kernel_path, kernel_command_line, console_pattern, @@ -63,6 +64,8 @@ class ReplayKernelBase(LinuxKernelTest): vm.shutdown() logger.info('finished the recording with log size %s bytes' % os.path.getsize(replay_path)) + self.run_replay_dump(replay_path) + logger.info('successfully tested replay-dump.py') else: vm.wait() logger.info('successfully finished the replay') @@ -70,6 +73,14 @@ class ReplayKernelBase(LinuxKernelTest): logger.info('elapsed time %.2f sec' % elapsed) return elapsed + def run_replay_dump(self, replay_path): + try: + subprocess.check_call(["./scripts/replay-dump.py", + "-f", replay_path], + stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.fail('replay-dump.py failed') + def run_rr(self, kernel_path, kernel_command_line, console_pattern, shift=7, args=None): replay_path = os.path.join(self.workdir, 'replay.bin') diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py index b4673261ce..5916922435 100644 --- a/tests/avocado/replay_linux.py +++ b/tests/avocado/replay_linux.py @@ -94,6 +94,8 @@ class ReplayLinux(LinuxTest): vm.shutdown() logger.info('finished the recording with log size %s bytes' % os.path.getsize(replay_path)) + self.run_replay_dump(replay_path) + logger.info('successfully tested replay-dump.py') else: vm.event_wait('SHUTDOWN', self.timeout) vm.wait() @@ -108,6 +110,14 @@ class ReplayLinux(LinuxTest): logger = logging.getLogger('replay') logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) + def run_replay_dump(self, replay_path): + try: + subprocess.check_call(["./scripts/replay-dump.py", + "-f", replay_path], + stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.fail('replay-dump.py failed') + @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') class ReplayLinuxX8664(ReplayLinux): """ From 9dbab31d9eb710b433f93e9e35962fbfd29f4c3f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:20 +0100 Subject: [PATCH 2782/2884] replay: allow runstate shutdown->running when replaying trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When replaying a trace, it is possible to go from shutdown to running with a reverse-debugging step. This can be useful if the problem being debugged triggers a reset or shutdown. This can be tested by making a recording of a machine that shuts down, then using -action shutdown=pause when replaying it. Continuing to the end of the trace then reverse-stepping in gdb crashes due to invalid runstate transition. Just permitting the transition seems to be all that's necessary for reverse-debugging to work well in such a state. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-5-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-13-alex.bennee@linaro.org> --- include/sysemu/runstate.h | 1 + replay/replay.c | 2 ++ system/runstate.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h index e210a37abf..11c7ff3ffb 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -9,6 +9,7 @@ void runstate_set(RunState new_state); RunState runstate_get(void); bool runstate_is_running(void); bool runstate_needs_reset(void); +void runstate_replay_enable(void); typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); diff --git a/replay/replay.c b/replay/replay.c index a2c576c16e..b8564a4813 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -385,6 +385,8 @@ static void replay_enable(const char *fname, int mode) replay_fetch_data_kind(); } + runstate_replay_enable(); + replay_init_events(); } diff --git a/system/runstate.c b/system/runstate.c index c833316f6d..a0e2a5fd22 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -181,6 +181,12 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE__MAX, RUN_STATE__MAX }, }; +static const RunStateTransition replay_play_runstate_transitions_def[] = { + { RUN_STATE_SHUTDOWN, RUN_STATE_RUNNING}, + + { RUN_STATE__MAX, RUN_STATE__MAX }, +}; + static bool runstate_valid_transitions[RUN_STATE__MAX][RUN_STATE__MAX]; bool runstate_check(RunState state) @@ -188,14 +194,33 @@ bool runstate_check(RunState state) return current_run_state == state; } -static void runstate_init(void) +static void transitions_set_valid(const RunStateTransition *rst) { const RunStateTransition *p; - memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); - for (p = &runstate_transitions_def[0]; p->from != RUN_STATE__MAX; p++) { + for (p = rst; p->from != RUN_STATE__MAX; p++) { runstate_valid_transitions[p->from][p->to] = true; } +} + +void runstate_replay_enable(void) +{ + assert(replay_mode != REPLAY_MODE_NONE); + + if (replay_mode == REPLAY_MODE_PLAY) { + /* + * When reverse-debugging, it is possible to move state from + * shutdown to running. + */ + transitions_set_valid(&replay_play_runstate_transitions_def[0]); + } +} + +static void runstate_init(void) +{ + memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); + + transitions_set_valid(&runstate_transitions_def[0]); qemu_mutex_init(&vmstop_lock); } From 94962ff00d09674047aed896e87ba09736cd6941 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:21 +0100 Subject: [PATCH 2783/2884] Revert "replay: stop us hanging in rr_wait_io_event" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1f881ea4a444ef36a8b6907b0b82be4b3af253a2. That commit causes reverse_debugging.py test failures, and does not seem to solve the root cause of the problem x86-64 still hangs in record/replay tests. The problem with short-cutting the iowait that was taken during record phase is that related events will not get consumed at the same points (e.g., reading the clock). A hang with zero icount always seems to be a symptom of an earlier problem that has caused the recording to become out of synch with the execution and consumption of events by replay. Acked-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-6-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-14-alex.bennee@linaro.org> --- accel/tcg/tcg-accel-ops-rr.c | 2 +- include/sysemu/replay.h | 5 ----- replay/replay.c | 21 --------------------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 48c38714bd..c59c77da4b 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -109,7 +109,7 @@ static void rr_wait_io_event(void) { CPUState *cpu; - while (all_cpu_threads_idle() && replay_can_wait()) { + while (all_cpu_threads_idle()) { rr_stop_kick_timer(); qemu_cond_wait_bql(first_cpu->halt_cond); } diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index f229b2109c..8102fa54f0 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -73,11 +73,6 @@ int replay_get_instructions(void); /*! Updates instructions counter in replay mode. */ void replay_account_executed_instructions(void); -/** - * replay_can_wait: check if we should pause for wait-io - */ -bool replay_can_wait(void); - /* Processing clocks and other time sources */ /*! Save the specified clock */ diff --git a/replay/replay.c b/replay/replay.c index b8564a4813..895fa6b67a 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -451,27 +451,6 @@ void replay_start(void) replay_enable_events(); } -/* - * For none/record the answer is yes. - */ -bool replay_can_wait(void) -{ - if (replay_mode == REPLAY_MODE_PLAY) { - /* - * For playback we shouldn't ever be at a point we wait. If - * the instruction count has reached zero and we have an - * unconsumed event we should go around again and consume it. - */ - if (replay_state.instruction_count == 0 && replay_state.has_unread_data) { - return false; - } else { - replay_sync_error("Playback shouldn't have to iowait"); - } - } - return true; -} - - void replay_finish(void) { if (replay_mode == REPLAY_MODE_NONE) { From 253ec604a84686231817443ea107a636bf1fc09a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:22 +0100 Subject: [PATCH 2784/2884] tests/avocado: replay_kernel.py add x86-64 q35 machine test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The x86-64 pc machine is flaky with record/replay, but q35 is more stable. Add a q35 test to replay_kernel.py. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-7-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-15-alex.bennee@linaro.org> --- tests/avocado/replay_kernel.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index a668af9d36..e22c200a36 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -110,7 +110,7 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) # See https://gitlab.com/qemu-project/qemu/-/issues/2094 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'pc machine is unstable with replay') def test_x86_64_pc(self): """ :avocado: tags=arch:x86_64 @@ -128,6 +128,22 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + def test_x86_64_q35(self): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/29/Everything/x86_64/os/images/pxeboot' + '/vmlinuz') + kernel_hash = '23bebd2680757891cf7adedb033532163a792495' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + def test_mips_malta(self): """ :avocado: tags=arch:mips From 4c193bb129dae164ce19c1197c76b59c62b405aa Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:23 +0100 Subject: [PATCH 2785/2884] chardev: set record/replay on the base device of a muxed device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chardev events to a muxed device don't get recorded because e.g., qemu_chr_be_write() checks whether the base device has the record flag set. This can be seen when replaying a trace that has characters typed into the console, an examination of the log shows they are not recorded. Setting QEMU_CHAR_FEATURE_REPLAY on the base chardev fixes the problem. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-8-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-16-alex.bennee@linaro.org> --- chardev/char.c | 71 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/chardev/char.c b/chardev/char.c index 3c43fb1278..ba847b6e9e 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -615,11 +615,24 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) return backend; } -Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, - Error **errp) +static void qemu_chardev_set_replay(Chardev *chr, Error **errp) +{ + if (replay_mode != REPLAY_MODE_NONE) { + if (CHARDEV_GET_CLASS(chr)->chr_ioctl) { + error_setg(errp, "Replay: ioctl is not supported " + "for serial devices yet"); + return; + } + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); + replay_register_char_driver(chr); + } +} + +static Chardev *__qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, + bool replay, Error **errp) { const ChardevClass *cc; - Chardev *chr = NULL; + Chardev *base = NULL, *chr = NULL; ChardevBackend *backend = NULL; const char *name = qemu_opt_get(opts, "backend"); const char *id = qemu_opts_id(opts); @@ -657,11 +670,11 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, chr = qemu_chardev_new(bid ? bid : id, object_class_get_name(OBJECT_CLASS(cc)), backend, context, errp); - if (chr == NULL) { goto out; } + base = chr; if (bid) { Chardev *mux; qapi_free_ChardevBackend(backend); @@ -681,11 +694,25 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, out: qapi_free_ChardevBackend(backend); g_free(bid); + + if (replay && base) { + /* RR should be set on the base device, not the mux */ + qemu_chardev_set_replay(base, errp); + } + return chr; } -Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, - bool permit_mux_mon, GMainContext *context) +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, + Error **errp) +{ + /* XXX: should this really not record/replay? */ + return __qemu_chr_new_from_opts(opts, context, false, errp); +} + +static Chardev *__qemu_chr_new(const char *label, const char *filename, + bool permit_mux_mon, GMainContext *context, + bool replay) { const char *p; Chardev *chr; @@ -693,14 +720,22 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, Error *err = NULL; if (strstart(filename, "chardev:", &p)) { - return qemu_chr_find(p); + chr = qemu_chr_find(p); + if (replay) { + qemu_chardev_set_replay(chr, &err); + if (err) { + error_report_err(err); + return NULL; + } + } + return chr; } opts = qemu_chr_parse_compat(label, filename, permit_mux_mon); if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, context, &err); + chr = __qemu_chr_new_from_opts(opts, context, replay, &err); if (!chr) { error_report_err(err); goto out; @@ -722,24 +757,18 @@ out: return chr; } +Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, + bool permit_mux_mon, GMainContext *context) +{ + return __qemu_chr_new(label, filename, permit_mux_mon, context, false); +} + static Chardev *qemu_chr_new_permit_mux_mon(const char *label, const char *filename, bool permit_mux_mon, GMainContext *context) { - Chardev *chr; - chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context); - if (chr) { - if (replay_mode != REPLAY_MODE_NONE) { - qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); - } - if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) { - error_report("Replay: ioctl is not supported " - "for serial devices yet"); - } - replay_register_char_driver(chr); - } - return chr; + return __qemu_chr_new(label, filename, permit_mux_mon, context, true); } Chardev *qemu_chr_new(const char *label, const char *filename, From a0bf401b8e532670fccccef53727a5f80eaf9049 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:24 +0100 Subject: [PATCH 2786/2884] virtio-net: Use replay_schedule_bh_event for bhs that affect machine state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The regular qemu_bh_schedule() calls result in non-deterministic execution of the bh in record-replay mode, which causes replay failure. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-9-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-17-alex.bennee@linaro.org> --- hw/net/virtio-net.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 08aa0b65e3..10ebaae5e2 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -40,6 +40,7 @@ #include "migration/misc.h" #include "standard-headers/linux/ethtool.h" #include "sysemu/sysemu.h" +#include "sysemu/replay.h" #include "trace.h" #include "monitor/qdev.h" #include "monitor/monitor.h" @@ -417,7 +418,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) timer_mod(q->tx_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); } else { - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); } } else { if (q->tx_timer) { @@ -2672,7 +2673,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) */ virtio_queue_set_notification(q->tx_vq, 0); if (q->tx_bh) { - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); } else { timer_mod(q->tx_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); @@ -2838,7 +2839,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) return; } virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); } static void virtio_net_tx_timer(void *opaque) @@ -2921,7 +2922,7 @@ static void virtio_net_tx_bh(void *opaque) /* If we flush a full burst of packets, assume there are * more coming and immediately reschedule */ if (ret >= n->tx_burst) { - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); q->tx_waiting = 1; return; } @@ -2935,7 +2936,7 @@ static void virtio_net_tx_bh(void *opaque) return; } else if (ret > 0) { virtio_queue_set_notification(q->tx_vq, 0); - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); q->tx_waiting = 1; } } From 44bc14fa1e78f01bfddcb265fc41c29204ebbfd8 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:25 +0100 Subject: [PATCH 2787/2884] virtio-net: Use virtual time for RSC timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Receive coalescing is visible to the target machine, so its timers should use virtual time like other timers in virtio-net, to be compatible with record-replay. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-10-npiggin@gmail.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-18-alex.bennee@linaro.org> --- hw/net/virtio-net.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 10ebaae5e2..ed33a32877 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2124,7 +2124,7 @@ static void virtio_net_rsc_purge(void *opq) chain->stat.timer++; if (!QTAILQ_EMPTY(&chain->buffers)) { timer_mod(chain->drain_timer, - qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + chain->n->rsc_timeout); } } @@ -2360,7 +2360,7 @@ static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain, chain->stat.empty_cache++; virtio_net_rsc_cache_buf(chain, nc, buf, size); timer_mod(chain->drain_timer, - qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + chain->n->rsc_timeout); return size; } @@ -2598,7 +2598,7 @@ static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n, chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD; chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; } - chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST, + chain->drain_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, virtio_net_rsc_purge, chain); memset(&chain->stat, 0, sizeof(chain->stat)); From 97d2b66dcd8c771065807b4acfd0002dac4385be Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 13 Aug 2024 21:23:26 +0100 Subject: [PATCH 2788/2884] savevm: Fix load_snapshot error path crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An error path missed setting *errp, which can cause a NULL deref. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-Id: <20240813050638.446172-11-npiggin@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-19-alex.bennee@linaro.org> --- migration/savevm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/savevm.c b/migration/savevm.c index 85958d7b09..6bb404b9c8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -3288,6 +3288,7 @@ bool load_snapshot(const char *name, const char *vmstate, /* Don't even try to load empty VM states */ ret = bdrv_snapshot_find(bs_vm_state, &sn, name); if (ret < 0) { + error_setg(errp, "Snapshot can not be found"); return false; } else if (sn.vm_state_size == 0) { error_setg(errp, "This is a disk-only snapshot. Revert to it " From 24c32ed3745af104ee6f47c09c13042e8e4657db Mon Sep 17 00:00:00 2001 From: Stefan Weil <sw@weilnetz.de> Date: Tue, 13 Aug 2024 21:23:27 +0100 Subject: [PATCH 2789/2884] docs: Fix some typos (found by typos) and grammar issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the misspellings of "overriden" also in code comments. Signed-off-by: Stefan Weil <sw@weilnetz.de> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-Id: <20240813125638.395461-1-sw@weilnetz.de> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-20-alex.bennee@linaro.org> --- docs/devel/migration/uadk-compression.rst | 4 ++-- docs/interop/qemu-ga.rst | 2 +- docs/tools/qemu-vmsr-helper.rst | 4 ++-- hw/arm/smmu-common.c | 2 +- include/exec/memory.h | 2 +- qapi/rocker.json | 4 ++-- qga/main.c | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/devel/migration/uadk-compression.rst b/docs/devel/migration/uadk-compression.rst index 3f73345dd5..64cadebd21 100644 --- a/docs/devel/migration/uadk-compression.rst +++ b/docs/devel/migration/uadk-compression.rst @@ -114,7 +114,7 @@ Make sure all these above kernel configurations are selected. Accelerator dev node permissions -------------------------------- -Harware accelerators(eg: HiSilicon Kunpeng Zip accelerator) gets registered to +Hardware accelerators (eg: HiSilicon Kunpeng Zip accelerator) gets registered to UADK and char devices are created in dev directory. In order to access resources on hardware accelerator devices, write permission should be provided to user. @@ -134,7 +134,7 @@ How To Use UADK Compression In QEMU Migration Set ``migrate_set_parameter multifd-compression uadk`` Since UADK uses Shared Virtual Addressing(SVA) and device access virtual memory -directly it is possible that SMMUv3 may enounter page faults while walking the +directly it is possible that SMMUv3 may encounter page faults while walking the IO page tables. This may impact the performance. In order to mitigate this, please make sure to specify ``-mem-prealloc`` parameter to the destination VM boot parameters. diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 9c7380896a..11f7bae460 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -50,7 +50,7 @@ Options .. option:: -c, --config=PATH Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``, - unless overriden by the QGA_CONF environment variable) + unless overridden by the QGA_CONF environment variable) .. option:: -m, --method=METHOD diff --git a/docs/tools/qemu-vmsr-helper.rst b/docs/tools/qemu-vmsr-helper.rst index 6ec87b49d9..9ce10b9af9 100644 --- a/docs/tools/qemu-vmsr-helper.rst +++ b/docs/tools/qemu-vmsr-helper.rst @@ -17,8 +17,8 @@ driver to advertise and monitor the power consumption or accumulated energy consumption of different power domains, such as CPU packages, DRAM, and other components when available. -However those register are accesible under priviliged access (CAP_SYS_RAWIO). -QEMU can use an external helper to access those priviliged register. +However those registers are accessible under privileged access (CAP_SYS_RAWIO). +QEMU can use an external helper to access those privileged registers. :program:`qemu-vmsr-helper` is that external helper; it creates a listener socket which will accept incoming connections for communication with QEMU. diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index d73ad62211..3f82728758 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -674,7 +674,7 @@ error: /* * combine S1 and S2 TLB entries into a single entry. - * As a result the S1 entry is overriden with combined data. + * As a result the S1 entry is overridden with combined data. */ static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2, dma_addr_t iova, SMMUTransCfg *cfg) diff --git a/include/exec/memory.h b/include/exec/memory.h index 02f7528ec0..296fd068c0 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1852,7 +1852,7 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); * memory_region_unregister_iommu_notifier: unregister a notifier for * changes to IOMMU translation entries. * - * @mr: the memory region which was observed and for which notity_stopped() + * @mr: the memory region which was observed and for which notify_stopped() * needs to be called * @n: the notifier to be removed. */ diff --git a/qapi/rocker.json b/qapi/rocker.json index 6950ca9602..73c7363b16 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -42,7 +42,7 @@ ## # @RockerPortDuplex: # -# An eumeration of port duplex states. +# An enumeration of port duplex states. # # @half: half duplex # @@ -55,7 +55,7 @@ ## # @RockerPortAutoneg: # -# An eumeration of port autoneg states. +# An enumeration of port autoneg states. # # @off: autoneg is off # diff --git a/qga/main.c b/qga/main.c index b8f7b1e4a3..50186760bf 100644 --- a/qga/main.c +++ b/qga/main.c @@ -257,7 +257,7 @@ QEMU_COPYRIGHT "\n" "\n" " -c, --config=PATH configuration file path (default is\n" " %s/qemu-ga.conf\n" -" unless overriden by the QGA_CONF environment variable)\n" +" unless overridden by the QGA_CONF environment variable)\n" " -m, --method transport method: one of unix-listen, virtio-serial,\n" " isa-serial, or vsock-listen (virtio-serial is the default)\n" " -p, --path device/socket path (the default for virtio-serial is:\n" From 3f9f9a37ae2404c54458acadb516f0eda7cd2c15 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <pierrick.bouvier@linaro.org> Date: Tue, 13 Aug 2024 21:23:28 +0100 Subject: [PATCH 2790/2884] docs/devel: update tcg-plugins page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reflect recent changes on API (inline ops) and new plugins. Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240812231945.169310-1-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-21-alex.bennee@linaro.org> --- docs/about/emulation.rst | 49 ++++++++++++++++++++++++++++++++------ docs/devel/tcg-plugins.rst | 13 ++++++---- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index c03033e4e9..eea1261baa 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -207,8 +207,8 @@ Once built a program can be run with multiple plugins loaded each with their own arguments:: $QEMU $OTHER_QEMU_ARGS \ - -plugin contrib/plugin/libhowvec.so,inline=on,count=hint \ - -plugin contrib/plugin/libhotblocks.so + -plugin contrib/plugins/libhowvec.so,inline=on,count=hint \ + -plugin contrib/plugins/libhotblocks.so Arguments are plugin specific and can be used to modify their behaviour. In this case the howvec plugin is being asked to use inline @@ -219,6 +219,14 @@ Linux user-mode emulation also evaluates the environment variable QEMU_PLUGIN="file=contrib/plugins/libhowvec.so,inline=on,count=hint" $QEMU +QEMU plugins avoid to write directly to stdin/stderr, and use the log provided +by the API (see function ``qemu_plugin_outs``). +To show output, you may use this additional parameter:: + + $QEMU $OTHER_QEMU_ARGS \ + -d plugin \ + -plugin contrib/plugins/libhowvec.so,inline=on,count=hint + Example Plugins ~~~~~~~~~~~~~~~ @@ -260,8 +268,7 @@ Behaviour can be tweaked with the following arguments: * - Option - Description * - inline=true|false - - Use faster inline addition of a single counter. Not per-cpu and not - thread safe. + - Use faster inline addition of a single counter. * - idle=true|false - Dump the current execution stats whenever the guest vCPU idles @@ -381,6 +388,15 @@ run:: 160 1 0 135 1 0 +Test inline operations +...................... + +``tests/plugins/inline.c`` + +This plugin is used for testing all inline operations, conditional callbacks and +scoreboard. It prints a per-cpu summary of all events. + + Hot Blocks .......... @@ -394,9 +410,6 @@ with linux-user execution as system emulation tends to generate re-translations as blocks from different programs get swapped in and out of system memory. -If your program is single-threaded you can use the ``inline`` option for -slightly faster (but not thread safe) counters. - Example:: $ qemu-aarch64 \ @@ -736,6 +749,28 @@ The plugin will log the reason of exit, for example:: 0xd4 reached, exiting +Limit instructions per second +............................. + +This plugin can limit the number of Instructions Per Second that are executed:: + + # get number of instructions + $ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin /bin/true |& grep total | sed -e 's/.*: //') + # limit speed to execute in 10 seconds + $ time ./build/qemu-x86_64 -plugin ./build/contrib/plugins/libips.so,ips=$(($num_insn/10)) /bin/true + real 10.000s + + +.. list-table:: IPS arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - ips=N + - Maximum number of instructions per cpu that can be executed in one second. + The plugin will sleep when the given number of instructions is reached. + Other emulation features ------------------------ diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index d8725c2854..9463692c41 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -61,11 +61,14 @@ translation event the plugin has an option to enumerate the instructions in a block of instructions and optionally register callbacks to some or all instructions when they are executed. -There is also a facility to add an inline event where code to -increment a counter can be directly inlined with the translation. -Currently only a simple increment is supported. This is not atomic so -can miss counts. If you want absolute precision you should use a -callback which can then ensure atomicity itself. +There is also a facility to add inline instructions doing various operations, +like adding or storing an immediate value. It is also possible to execute a +callback conditionally, with condition being evaluated inline. All those inline +operations are associated to a ``scoreboard``, which is a thread-local storage +automatically expanded when new cores/threads are created and that can be +accessed/modified in a thread-safe way without any lock needed. Combining inline +operations and conditional callbacks offer a more efficient way to instrument +binaries, compared to classic callbacks. Finally when QEMU exits all the registered *atexit* callbacks are invoked. From 278035fc81510bd88501afb78bd5ab652beffa76 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <pierrick.bouvier@linaro.org> Date: Tue, 13 Aug 2024 21:23:29 +0100 Subject: [PATCH 2791/2884] plugins: fix race condition with scoreboards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A deadlock can be created if a new vcpu (a) triggers a scoreboard reallocation, and another vcpu (b) wants to create a new scoreboard at the same time. In this case, (a) holds the plugin lock, and starts an exclusive section, waiting for (b). But at the same time, (b) is waiting for plugin lock. The solution is to drop the lock before entering the exclusive section. This bug can be easily reproduced by creating a callback for any tb exec, that allocates a new scoreboard. In this case, as soon as we reach more than 16 vcpus, the deadlock occurs. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2344 Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240812220748.95167-2-pierrick.bouvier@linaro.org> [AJB: tweak var position to meet coding style] Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240813202329.1237572-22-alex.bennee@linaro.org> --- plugins/core.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/plugins/core.c b/plugins/core.c index 12c67b4b4e..2897453cac 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -214,30 +214,49 @@ CPUPluginState *qemu_plugin_create_vcpu_state(void) static void plugin_grow_scoreboards__locked(CPUState *cpu) { - if (cpu->cpu_index < plugin.scoreboard_alloc_size) { + size_t scoreboard_size = plugin.scoreboard_alloc_size; + bool need_realloc = false; + + if (cpu->cpu_index < scoreboard_size) { return; } - bool need_realloc = FALSE; - while (cpu->cpu_index >= plugin.scoreboard_alloc_size) { - plugin.scoreboard_alloc_size *= 2; - need_realloc = TRUE; + while (cpu->cpu_index >= scoreboard_size) { + scoreboard_size *= 2; + need_realloc = true; } - - if (!need_realloc || QLIST_EMPTY(&plugin.scoreboards)) { - /* nothing to do, we just updated sizes for future scoreboards */ + if (!need_realloc) { return; } + if (QLIST_EMPTY(&plugin.scoreboards)) { + /* just update size for future scoreboards */ + plugin.scoreboard_alloc_size = scoreboard_size; + return; + } + + /* + * A scoreboard creation/deletion might be in progress. If a new vcpu is + * initialized at the same time, we are safe, as the new + * plugin.scoreboard_alloc_size was not yet written. + */ + qemu_rec_mutex_unlock(&plugin.lock); + /* cpus must be stopped, as tb might still use an existing scoreboard. */ start_exclusive(); - struct qemu_plugin_scoreboard *score; - QLIST_FOREACH(score, &plugin.scoreboards, entry) { - g_array_set_size(score->data, plugin.scoreboard_alloc_size); + /* re-acquire lock */ + qemu_rec_mutex_lock(&plugin.lock); + /* in case another vcpu is created between unlock and exclusive section. */ + if (scoreboard_size > plugin.scoreboard_alloc_size) { + struct qemu_plugin_scoreboard *score; + QLIST_FOREACH(score, &plugin.scoreboards, entry) { + g_array_set_size(score->data, scoreboard_size); + } + plugin.scoreboard_alloc_size = scoreboard_size; + /* force all tb to be flushed, as scoreboard pointers were changed. */ + tb_flush(cpu); } - /* force all tb to be flushed, as scoreboard pointers were changed. */ - tb_flush(cpu); end_exclusive(); } From 6df664f87c738788891f3bda701e63e23a0dbbc2 Mon Sep 17 00:00:00 2001 From: Andrew Jones <ajones@ventanamicro.com> Date: Fri, 16 Aug 2024 18:07:45 +0200 Subject: [PATCH 2792/2884] Revert "hw/riscv/virt.c: imsics DT: add '#msi-cells'" This reverts commit f42cdf2ea5b3a1dc369792d7acbf9cd3e5c90815. Linux does not properly handle '#msi-cells=<0>' when searching for MSI controllers for PCI devices which results in the devices being unable to use MSIs. A patch for Linux has been sent[1] but until it, or something like it, is merged and in distro kernels we should stop adding the property. It's harmless to stop adding it since the absence of the property and a value of zero for the property mean the same thing according to the DT binding definition. Link: https://lore.kernel.org/all/20240816124957.130017-2-ajones@ventanamicro.com/ # 1 Signed-off-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20240816160743.220374-5-ajones@ventanamicro.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> --- hw/riscv/virt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 9981e0f6c9..cef41c150a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -552,7 +552,6 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, FDT_IMSIC_INT_CELLS); qemu_fdt_setprop(ms->fdt, imsic_name, "interrupt-controller", NULL, 0); qemu_fdt_setprop(ms->fdt, imsic_name, "msi-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, imsic_name, "#msi-cells", 0); qemu_fdt_setprop(ms->fdt, imsic_name, "interrupts-extended", imsic_cells, ms->smp.cpus * sizeof(uint32_t) * 2); qemu_fdt_setprop(ms->fdt, imsic_name, "reg", imsic_regs, From d762016d51de1d8a11196d27c0aade9881fc26f0 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang <jiaxun.yang@flygoat.com> Date: Fri, 21 Jun 2024 14:11:13 +0100 Subject: [PATCH 2793/2884] hw/mips/loongson3_virt: Store core_iocsr into LoongsonMachineState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link: https://lore.kernel.org/qemu-devel/972034d6-23b3-415a-b401-b8bc1cc515c9@linaro.org/ Suggested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240621-loongson3-ipi-follow-v2-1-848eafcbb67e@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/mips/loongson3_virt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 408e3d7054..27a85e3614 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -97,6 +97,7 @@ struct LoongsonMachineState { MemoryRegion *pio_alias; MemoryRegion *mmio_alias; MemoryRegion *ecam_alias; + MemoryRegion *core_iocsr[LOONGSON_MAX_VCPUS]; }; typedef struct LoongsonMachineState LoongsonMachineState; @@ -493,6 +494,7 @@ static void mips_loongson3_virt_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; ram_addr_t ram_size = machine->ram_size; + LoongsonMachineState *s = LOONGSON_MACHINE(machine); MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); @@ -586,6 +588,7 @@ static void mips_loongson3_virt_init(MachineState *machine) iocsr, 0, UINT32_MAX); memory_region_add_subregion(&MIPS_CPU(cpu)->env.iocsr.mr, 0, core_iocsr); + s->core_iocsr[i] = core_iocsr; } if (node > 0) { From ec276edb384689caae03c8cb53dc6833304df02f Mon Sep 17 00:00:00 2001 From: Jiaxun Yang <jiaxun.yang@flygoat.com> Date: Fri, 21 Jun 2024 14:11:14 +0100 Subject: [PATCH 2794/2884] hw/mips/loongson3_virt: Fix condition of IPI IOCSR connection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >>> CID 1547264: Null pointer dereferences (REVERSE_INULL) >>> Null-checking "ipi" suggests that it may be null, but it has already been dereferenced on all paths leading to the check. Resolves: Coverity CID 1547264 Link: https://lore.kernel.org/qemu-devel/752417ad-ab72-4fed-8d1f-af41f15bc225@app.fastmail.com/ Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240621-loongson3-ipi-follow-v2-2-848eafcbb67e@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/mips/loongson3_virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 27a85e3614..2067b4fecb 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -574,7 +574,7 @@ static void mips_loongson3_virt_init(MachineState *machine) cpu_mips_clock_init(cpu); qemu_register_reset(main_cpu_reset, cpu); - if (ipi) { + if (!kvm_enabled()) { hwaddr base = ((hwaddr)node << 44) + virt_memmap[VIRT_IPI].base; base += core * 0x100; qdev_connect_gpio_out(ipi, i, cpu->env.irq[6]); From 68baeaafa562e360188fb3be8a9451db1c5bd862 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt <heinrich.schuchardt@canonical.com> Date: Mon, 29 Jul 2024 22:48:15 +0200 Subject: [PATCH 2795/2884] qemu-options.hx: correct formatting -smbios type=4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit processor-family and processor-id can be assigned independently. Add missing brackets. Fixes: b5831d79671c ("smbios: add processor-family option") Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240729204816.11905-1-heinrich.schuchardt@canonical.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- qemu-options.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-options.hx b/qemu-options.hx index cee0da2014..d99084a5ee 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2704,7 +2704,7 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 3 fields\n" "-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str]\n" " [,asset=str][,part=str][,max-speed=%d][,current-speed=%d]\n" - " [,processor-family=%d,processor-id=%d]\n" + " [,processor-family=%d][,processor-id=%d]\n" " specify SMBIOS type 4 fields\n" "-smbios type=8[,external_reference=str][,internal_reference=str][,connector_type=%d][,port_type=%d]\n" " specify SMBIOS type 8 fields\n" From 453ba4f675f751fe4dceaff57ac1ebf72f28f6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 13 Aug 2024 15:30:31 +0200 Subject: [PATCH 2796/2884] target/mips: Pass page table entry size as MemOp to get_pte() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to simplify the next commit, pass the PTE size as MemOp. Rename: native_shift -> native_op directory_shift -> directory_mop leaf_shift -> leaf_mop Suggested-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240814090452.2591-2-philmd@linaro.org> --- target/mips/tcg/sysemu/tlb_helper.c | 58 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 3ba6d369a6..60147ba0af 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -592,13 +592,13 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, * resulting in a TLB or XTLB Refill exception. */ -static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, +static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, uint64_t *pte) { - if ((vaddr & ((entry_size >> 3) - 1)) != 0) { + if ((vaddr & (memop_size(op) - 1)) != 0) { return false; } - if (entry_size == 64) { + if (op == MO_64) { *pte = cpu_ldq_code(env, vaddr); } else { *pte = cpu_ldl_code(env, vaddr); @@ -607,8 +607,9 @@ static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, } static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, - int entry_size, int ptei) + MemOp op, int ptei) { + unsigned entry_size = memop_size(op) << 3; uint64_t result = entry; uint64_t rixi; if (ptei > entry_size) { @@ -624,14 +625,12 @@ static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, int directory_index, bool *huge_page, bool *hgpg_directory_hit, uint64_t *pw_entrylo0, uint64_t *pw_entrylo1, - unsigned directory_shift, unsigned leaf_shift, int ptw_mmu_idx) + MemOp directory_mop, MemOp leaf_mop, int ptw_mmu_idx) { int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; - uint32_t direntry_size = 1 << (directory_shift + 3); - uint32_t leafentry_size = 1 << (leaf_shift + 3); uint64_t entry; uint64_t paddr; int prot; @@ -643,14 +642,14 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, /* wrong base address */ return 0; } - if (!get_pte(env, *vaddr, direntry_size, &entry)) { + if (!get_pte(env, *vaddr, directory_mop, &entry)) { return 0; } if ((entry & (1 << psn)) && hugepg) { *huge_page = true; *hgpg_directory_hit = true; - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); + entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); w = directory_index - 1; if (directory_index & 0x1) { /* Generate adjacent page from same PTE for odd TLB page */ @@ -658,7 +657,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, *pw_entrylo0 = entry & ~lsb; /* even page */ *pw_entrylo1 = entry | lsb; /* odd page */ } else if (dph) { - int oddpagebit = 1 << leaf_shift; + int oddpagebit = 1 << leaf_mop; uint64_t vaddr2 = *vaddr ^ oddpagebit; if (*vaddr & oddpagebit) { *pw_entrylo1 = entry; @@ -669,10 +668,10 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, ptw_mmu_idx) != TLBRET_MATCH) { return 0; } - if (!get_pte(env, vaddr2, leafentry_size, &entry)) { + if (!get_pte(env, vaddr2, leaf_mop, &entry)) { return 0; } - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); + entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); if (*vaddr & oddpagebit) { *pw_entrylo0 = entry; } else { @@ -711,7 +710,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* Native pointer size */ /*For the 32-bit architectures, this bit is fixed to 0.*/ - int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; + MemOp native_op = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? MO_32 : MO_64; /* Indices from PWField */ int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; @@ -728,11 +727,10 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* Other HTW configs */ int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; - unsigned directory_shift, leaf_shift; + MemOp directory_mop, leaf_mop; /* Offsets into tables */ unsigned goffset, uoffset, moffset, ptoffset0, ptoffset1; - uint32_t leafentry_size; /* Starting address - Page Table Base */ uint64_t vaddr = env->CP0_PWBase; @@ -759,23 +757,21 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, } /* HTW Shift values (depend on entry size) */ - directory_shift = (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; - leaf_shift = (ptew == 1) ? native_shift + 1 : native_shift; + directory_mop = (hugepg && (ptew == 1)) ? native_op + 1 : native_op; + leaf_mop = (ptew == 1) ? native_op + 1 : native_op; - goffset = gindex << directory_shift; - uoffset = uindex << directory_shift; - moffset = mindex << directory_shift; - ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); - ptoffset1 = ptoffset0 | (1 << (leaf_shift)); - - leafentry_size = 1 << (leaf_shift + 3); + goffset = gindex << directory_mop; + uoffset = uindex << directory_mop; + moffset = mindex << directory_mop; + ptoffset0 = (ptindex >> 1) << (leaf_mop + 1); + ptoffset1 = ptoffset0 | (1 << (leaf_mop)); /* Global Directory */ if (gdw > 0) { vaddr |= goffset; switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, &pw_entrylo0, &pw_entrylo1, - directory_shift, leaf_shift, ptw_mmu_idx)) + directory_mop, leaf_mop, ptw_mmu_idx)) { case 0: return false; @@ -792,7 +788,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, vaddr |= uoffset; switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, &pw_entrylo0, &pw_entrylo1, - directory_shift, leaf_shift, ptw_mmu_idx)) + directory_mop, leaf_mop, ptw_mmu_idx)) { case 0: return false; @@ -809,7 +805,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, vaddr |= moffset; switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, &pw_entrylo0, &pw_entrylo1, - directory_shift, leaf_shift, ptw_mmu_idx)) + directory_mop, leaf_mop, ptw_mmu_idx)) { case 0: return false; @@ -827,10 +823,10 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, ptw_mmu_idx) != TLBRET_MATCH) { return false; } - if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { + if (!get_pte(env, vaddr, leaf_mop, &dir_entry)) { return false; } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); + dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); pw_entrylo0 = dir_entry; /* Leaf Level Page Table - Second half of PTE pair */ @@ -839,10 +835,10 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, ptw_mmu_idx) != TLBRET_MATCH) { return false; } - if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { + if (!get_pte(env, vaddr, leaf_mop, &dir_entry)) { return false; } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); + dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); pw_entrylo1 = dir_entry; refill: From 7ce9760d64e8a884f044f95a1f32f96c2e0fafa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 13 Aug 2024 12:05:42 +0200 Subject: [PATCH 2797/2884] target/mips: Use correct MMU index in get_pte() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When refactoring page_table_walk_refill() in commit 4e999bf419 we missed the indirect call to cpu_mmu_index() in get_pte(): page_table_walk_refill() -> get_pte() -> cpu_ld[lq]_code() -> cpu_mmu_index() Since we don't mask anymore the modes in hflags, cpu_mmu_index() can return UM or SM, while we only expect KM or ERL. Fix by propagating ptw_mmu_idx to get_pte(), and use the cpu_ld/st_code_mmu() API with the correct MemOpIdx. Reported-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> Reported-by: Waldemar Brodkorb <wbx@uclibc-ng.org> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2470 Fixes: 4e999bf419 ("target/mips: Pass ptw_mmu_idx down from mips_cpu_tlb_fill") Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240814090452.2591-3-philmd@linaro.org> --- target/mips/tcg/sysemu/tlb_helper.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 60147ba0af..0e94e00a5f 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -593,16 +593,21 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, */ static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, - uint64_t *pte) + uint64_t *pte, unsigned ptw_mmu_idx) { + MemOpIdx oi; + if ((vaddr & (memop_size(op) - 1)) != 0) { return false; } + + oi = make_memop_idx(op | MO_TE, ptw_mmu_idx); if (op == MO_64) { - *pte = cpu_ldq_code(env, vaddr); + *pte = cpu_ldq_code_mmu(env, vaddr, oi, 0); } else { - *pte = cpu_ldl_code(env, vaddr); + *pte = cpu_ldl_code_mmu(env, vaddr, oi, 0); } + return true; } @@ -642,7 +647,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, /* wrong base address */ return 0; } - if (!get_pte(env, *vaddr, directory_mop, &entry)) { + if (!get_pte(env, *vaddr, directory_mop, &entry, ptw_mmu_idx)) { return 0; } @@ -668,7 +673,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, ptw_mmu_idx) != TLBRET_MATCH) { return 0; } - if (!get_pte(env, vaddr2, leaf_mop, &entry)) { + if (!get_pte(env, vaddr2, leaf_mop, &entry, ptw_mmu_idx)) { return 0; } entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); @@ -823,7 +828,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, ptw_mmu_idx) != TLBRET_MATCH) { return false; } - if (!get_pte(env, vaddr, leaf_mop, &dir_entry)) { + if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { return false; } dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); @@ -835,7 +840,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, ptw_mmu_idx) != TLBRET_MATCH) { return false; } - if (!get_pte(env, vaddr, leaf_mop, &dir_entry)) { + if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { return false; } dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); From 44017c66556da85168d31380ca36f0311d37a1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Tue, 13 Aug 2024 17:22:37 +0200 Subject: [PATCH 2798/2884] target/mips: Load PTE as DATA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PTE is not CODE so load it as normal DATA access. Fixes: 074cfcb4da ("Implement hardware page table walker for MIPS32") Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240814090452.2591-4-philmd@linaro.org> --- target/mips/tcg/sysemu/tlb_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 0e94e00a5f..3836137750 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -603,9 +603,9 @@ static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, oi = make_memop_idx(op | MO_TE, ptw_mmu_idx); if (op == MO_64) { - *pte = cpu_ldq_code_mmu(env, vaddr, oi, 0); + *pte = cpu_ldq_mmu(env, vaddr, oi, 0); } else { - *pte = cpu_ldl_code_mmu(env, vaddr, oi, 0); + *pte = cpu_ldl_mmu(env, vaddr, oi, 0); } return true; From dfad8421af474a38e272cdb19ae3c8e778acf820 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 13 Aug 2024 17:52:45 +0100 Subject: [PATCH 2799/2884] hw/dma/xilinx_axidma: Use semicolon at end of statement, not comma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In axidma_class_init() we accidentally used a comma at the end of a statement rather than a semicolon. This has no ill effects, but it's obviously not intended and it means that Coccinelle scripts for instance will fail to match on the two statements. Use a semicolon instead. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-ID: <20240813165250.2717650-6-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/dma/xilinx_axidma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index c9cfc3169b..7707634253 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -626,7 +626,7 @@ static void axidma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = xilinx_axidma_realize, + dc->realize = xilinx_axidma_realize; dc->reset = xilinx_axidma_reset; device_class_set_props(dc, axidma_properties); } From 7d3a421feab29c03601813c8a0f98d5b2fd4420a Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 13 Aug 2024 17:52:46 +0100 Subject: [PATCH 2800/2884] hw/remote/message.c: Don't directly invoke DeviceClass:reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Directly invoking the DeviceClass::reset method is a bad idea, because if the device is using three-phase reset then it relies on transitional reset machinery which is likely to disappear at some point. Reset the device in the standard way, by calling device_cold_reset(). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240813165250.2717650-7-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/remote/message.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/remote/message.c b/hw/remote/message.c index 50f6bf2d49..38ae6c75b4 100644 --- a/hw/remote/message.c +++ b/hw/remote/message.c @@ -215,13 +215,10 @@ fail: static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev, Error **errp) { - DeviceClass *dc = DEVICE_GET_CLASS(dev); DeviceState *s = DEVICE(dev); MPQemuMsg ret = { 0 }; - if (dc->reset) { - dc->reset(s); - } + device_cold_reset(s); ret.cmd = MPQEMU_CMD_RET; From f7e3d7521b41ada97c5344914d3c9bc6ed04c82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Wed, 14 Aug 2024 12:01:18 +0200 Subject: [PATCH 2801/2884] linux-user/mips: Do not try to use removed R5900 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R5900 emulation was removed in commit 823f2897bd. Remove it from ELF parsing in order to avoid: $ qemu-mipsn32 ./test5900 qemu-mipsn32: unable to find CPU model 'R5900' This reverts commit 4d9e5a0eb7df6e98ac6cf5e16029f35dd05b9537. Fixes: 823f2897bd ("target/mips: Disable R5900 support") Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240814133928.6746-2-philmd@linaro.org> --- linux-user/mips/target_elf.h | 3 --- linux-user/mips64/target_elf.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index b965e86b2b..71a32315a8 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -12,9 +12,6 @@ static inline const char *cpu_get_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } - if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { - return "R5900"; - } if (eflags & EF_MIPS_NAN2008) { return "P5600"; } diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 5f2f2df29f..ec55d8542a 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -12,9 +12,6 @@ static inline const char *cpu_get_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { return "I6400"; } - if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { - return "R5900"; - } return "5KEf"; } #endif From 1e5a7c57a577d7702c668ef9fcdc9fa4187ef8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Wed, 14 Aug 2024 11:14:49 +0200 Subject: [PATCH 2802/2884] linux-user/mips: Select Octeon68XX CPU for Octeon binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Octeon68XX CPU is available since commit 9a6046a655 ("target/mips: introduce Cavium Octeon CPU model"). Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1722 Reported-by: Johnathan Hữu Trí <nhtri2003@gmail.com> Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240814133928.6746-3-philmd@linaro.org> --- linux-user/mips64/target_elf.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index ec55d8542a..ce6fb6541e 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -9,6 +9,14 @@ #define MIPS64_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { + switch (eflags & EF_MIPS_MACH) { + case EF_MIPS_MACH_OCTEON: + case EF_MIPS_MACH_OCTEON2: + case EF_MIPS_MACH_OCTEON3: + return "Octeon68XX"; + default: + break; + } if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { return "I6400"; } From 309ce6af94738ae22f01f4c145b82e51400b14b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Wed, 14 Aug 2024 12:17:26 +0200 Subject: [PATCH 2803/2884] linux-user/mips: Select MIPS64R2-generic for Rel2 binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: YunQiang Su <syq@debian.org> Reported-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240814133928.6746-4-philmd@linaro.org> --- linux-user/mips64/target_elf.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index ce6fb6541e..a3a8b2e385 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -17,8 +17,13 @@ static inline const char *cpu_get_model(uint32_t eflags) default: break; } - if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { + switch (eflags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_64R6: return "I6400"; + case EF_MIPS_ARCH_64R2: + return "MIPS64R2-generic"; + default: + break; } return "5KEf"; } From e922abf5c0e40eb451434c4121b730c8a19d80d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Wed, 14 Aug 2024 12:16:39 +0200 Subject: [PATCH 2804/2884] linux-user/mips: Select Loongson CPU for Loongson binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240814133928.6746-5-philmd@linaro.org> --- linux-user/mips64/target_elf.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index a3a8b2e385..502af9d278 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -14,6 +14,12 @@ static inline const char *cpu_get_model(uint32_t eflags) case EF_MIPS_MACH_OCTEON2: case EF_MIPS_MACH_OCTEON3: return "Octeon68XX"; + case EF_MIPS_MACH_LS2E: + return "Loongson-2E"; + case EF_MIPS_MACH_LS2F: + return "Loongson-2F"; + case EF_MIPS_MACH_LS3A: + return "Loongson-3A1000"; default: break; } From 4a85f23157f7ff766608c1373b71a97513215257 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 6 Aug 2024 09:28:12 +1000 Subject: [PATCH 2805/2884] tests/avocado: exec_command should not consume console output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _console_interaction reads data from the console even when there is only an input string to send, and no output data to wait on. This can cause lines to be missed by wait_for_console_pattern calls that follows an exec_command. Fix this by not reading the console if there is no pattern to wait for. This solves occasional hangs in ppc_hv_tests.py, usually when run on KVM hosts that are fast enough to output important lines quickly enough to be consumed by exec_command, so they get missed by subsequent wait for pattern calls. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-ID: <20240805232814.267843-2-npiggin@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tests/avocado/avocado_qemu/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index a3da2a96bb..ef935614cf 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -135,6 +135,13 @@ def _console_interaction(test, success_message, failure_message, vm.console_socket.sendall(send_string.encode()) if not keep_sending: send_string = None # send only once + + # Only consume console output if waiting for something + if success_message is None and failure_message is None: + if send_string is None: + break + continue + try: msg = console.readline().decode().strip() except UnicodeDecodeError: From 8e540bbe4567745dab310d620aefd7163e74cd93 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin <npiggin@gmail.com> Date: Tue, 6 Aug 2024 09:28:13 +1000 Subject: [PATCH 2806/2884] tests/avocado: Mark ppc_hv_tests.py as non-flaky after fixed console interaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that exec_command doesn't incorrectly consume console output, and guest time is set correctly, ppc_hv_tests.py is working more reliably. Try marking it non-flaky. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Message-ID: <20240805232814.267843-3-npiggin@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- tests/avocado/ppc_hv_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/avocado/ppc_hv_tests.py b/tests/avocado/ppc_hv_tests.py index bf8822bb97..0e83bbac71 100644 --- a/tests/avocado/ppc_hv_tests.py +++ b/tests/avocado/ppc_hv_tests.py @@ -45,7 +45,6 @@ def missing_deps(): # QEMU already installed and use that. # XXX: The order of these tests seems to matter, see git blame. @skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps)) -@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck due to console handling problem') @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') class HypervisorTest(QemuSystemTest): From 47f06fb4c878a6e08b6fd19b7798d32b1e041124 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <pierrick.bouvier@linaro.org> Date: Wed, 14 Aug 2024 16:36:40 -0700 Subject: [PATCH 2807/2884] contrib/plugins/execlog: Fix shadowed declaration warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found on debian stable. ../contrib/plugins/execlog.c: In function ‘vcpu_tb_trans’: ../contrib/plugins/execlog.c:236:22: error: declaration of ‘n’ shadows a previous local [-Werror=shadow=local] 236 | for (int n = 0; n < all_reg_names->len; n++) { | ^ ../contrib/plugins/execlog.c:184:12: note: shadowed declaration is here 184 | size_t n = qemu_plugin_tb_n_insns(tb); | Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-ID: <20240814233645.944327-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- contrib/plugins/execlog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index 1c1601cc0b..d67d010761 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -181,8 +181,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) bool check_regs_this = rmatches; bool check_regs_next = false; - size_t n = qemu_plugin_tb_n_insns(tb); - for (size_t i = 0; i < n; i++) { + size_t n_insns = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < n_insns; i++) { char *insn_disas; uint64_t insn_vaddr; From 12d36294a2d978faf893101862118d1ac1815e85 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Fri, 16 Aug 2024 17:23:06 +1000 Subject: [PATCH 2808/2884] target/sparc: Restrict STQF to sparcv9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to sparcv9, the same encoding was STDFQ. Cc: qemu-stable@nongnu.org Fixes: 06c060d9e5b ("target/sparc: Move simple fp load/store to decodetree") Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240816072311.353234-2-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- target/sparc/insns.decode | 2 +- target/sparc/translate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode index fbcb4f7aef..5fd478191a 100644 --- a/target/sparc/insns.decode +++ b/target/sparc/insns.decode @@ -644,7 +644,7 @@ STF 11 ..... 100100 ..... . ............. @r_r_ri_na STFSR 11 00000 100101 ..... . ............. @n_r_ri STXFSR 11 00001 100101 ..... . ............. @n_r_ri { - STQF 11 ..... 100110 ..... . ............. @q_r_ri_na + STQF 11 ..... 100110 ..... . ............. @q_r_ri_na # v9 STDFQ 11 ----- 100110 ----- - ------------- } STDF 11 ..... 100111 ..... . ............. @d_r_ri_na diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 113639083b..c803e8d1ba 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -4521,7 +4521,7 @@ static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) TRANS(STF, ALL, do_st_fpr, a, MO_32) TRANS(STDF, ALL, do_st_fpr, a, MO_64) -TRANS(STQF, ALL, do_st_fpr, a, MO_128) +TRANS(STQF, 64, do_st_fpr, a, MO_128) TRANS(STFA, 64, do_st_fpr, a, MO_32) TRANS(STDFA, 64, do_st_fpr, a, MO_64) From 6373fc0323c00fa9ee4719628ee63ab4dad159e5 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow <shentey@gmail.com> Date: Wed, 14 Aug 2024 20:15:32 +0200 Subject: [PATCH 2809/2884] hw/ppc/Kconfig: Add missing SERIAL_ISA dependency to POWERNV machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The machine calls serial_hds_isa_init() which is provided by serial-isa.c, guarded by SERIAL_ISA. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240814181534.218964-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/ppc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index c235519881..5addad1124 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -39,6 +39,7 @@ config POWERNV select PCI_POWERNV select PCA9552 select PCA9554 + select SERIAL_ISA select SSI select SSI_M25P80 select PNV_SPI From c911f875f83f938539f1ee36bace83fba6e50d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Szcz=C4=99k?= <kamil@szczek.dev> Date: Sat, 17 Aug 2024 15:25:31 +0000 Subject: [PATCH 2810/2884] hw/i386/pc: Unify vmport=auto handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code which translates vmport=auto to on/off is currently separate for each PC machine variant, while being functionally equivalent. This moves the translation into a shared initialization function, while also tightening the enum assertion. Signed-off-by: Kamil Szczęk <kamil@szczek.dev> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <v8pz1uwgIYWkidgZK-o8H-qJvnSyl0641XVmNO43Qls307AA3QRPuad_py6xGe0JAxB6yDEe76oZ8tau_n-2Y6sJBCKzCujNbEUUFhd-ahI=@szczek.dev> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/i386/pc.c | 5 +++++ hw/i386/pc_piix.c | 5 ----- hw/i386/pc_q35.c | 5 ----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index c74931d577..72229a24ff 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1217,6 +1217,11 @@ void pc_basic_device_init(struct PCMachineState *pcms, isa_realize_and_unref(pcms->pcspk, isa_bus, &error_fatal); } + assert(pcms->vmport >= 0 && pcms->vmport < ON_OFF_AUTO__MAX); + if (pcms->vmport == ON_OFF_AUTO_AUTO) { + pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; + } + /* Super I/O */ pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled, pcms->vmport != ON_OFF_AUTO_ON); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d9e69243b4..347afa4c37 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -310,11 +310,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); - assert(pcms->vmport != ON_OFF_AUTO__MAX); - if (pcms->vmport == ON_OFF_AUTO_AUTO) { - pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; - } - /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, !MACHINE_CLASS(pcmc)->no_floppy, 0x4); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9d108b194e..f2d8edfa84 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -276,11 +276,6 @@ static void pc_q35_init(MachineState *machine) x86_register_ferr_irq(x86ms->gsi[13]); } - assert(pcms->vmport != ON_OFF_AUTO__MAX); - if (pcms->vmport == ON_OFF_AUTO_AUTO) { - pcms->vmport = ON_OFF_AUTO_ON; - } - /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, !mc->no_floppy, 0xff0104); From 702cbdc46b118b1622edeeaa01a22f7af9776388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Szcz=C4=99k?= <kamil@szczek.dev> Date: Sat, 17 Aug 2024 15:26:15 +0000 Subject: [PATCH 2811/2884] hw/i386/pc: Ensure vmport prerequisites are fulfilled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 4ccd5fe22feb95137d325f422016a6473541fe9f ('pc: add option to disable PS/2 mouse/keyboard'), the vmport will not be created unless the i8042 PS/2 controller is enabled. To avoid confusion, let's fail if vmport was explicitly requested, but the i8042 controller is disabled. This also changes the behavior of vmport=auto to take i8042 controller availability into account. Signed-off-by: Kamil Szczęk <kamil@szczek.dev> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Message-ID: <0MS3y5E-hHqODIhiuFxmCnIrXd612JIGq31UuMsz4KGCKZ_wWuF-PHGKTRSGS0nWaPEddOdF4YOczHdgorulECPo792OhWov7O9BBF6UMX4=@szczek.dev> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/i386/pc.c | 11 ++++++++--- qemu-options.hx | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 72229a24ff..7779c88a91 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1075,7 +1075,7 @@ static const MemoryRegionOps ioportF0_io_ops = { }; static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, - bool create_i8042, bool no_vmport) + bool create_i8042, bool no_vmport, Error **errp) { int i; DriveInfo *fd[MAX_FD]; @@ -1100,6 +1100,10 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, } if (!create_i8042) { + if (!no_vmport) { + error_setg(errp, + "vmport requires the i8042 controller to be enabled"); + } return; } @@ -1219,12 +1223,13 @@ void pc_basic_device_init(struct PCMachineState *pcms, assert(pcms->vmport >= 0 && pcms->vmport < ON_OFF_AUTO__MAX); if (pcms->vmport == ON_OFF_AUTO_AUTO) { - pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; + pcms->vmport = (xen_enabled() || !pcms->i8042_enabled) + ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; } /* Super I/O */ pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled, - pcms->vmport != ON_OFF_AUTO_ON); + pcms->vmport != ON_OFF_AUTO_ON, &error_fatal); } void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) diff --git a/qemu-options.hx b/qemu-options.hx index d99084a5ee..d94e2cbbae 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -68,8 +68,8 @@ SRST ``vmport=on|off|auto`` Enables emulation of VMWare IO port, for vmmouse etc. auto says - to select the value based on accel. For accel=xen the default is - off otherwise the default is on. + to select the value based on accel and i8042. For accel=xen or + i8042=off the default is off otherwise the default is on. ``dump-guest-core=on|off`` Include guest memory in a core dump. The default is on. From 87e012f29f2e47dcd8c385ff8bb8188f9e06d4ea Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 19 Aug 2024 15:50:21 +0100 Subject: [PATCH 2812/2884] crypto/tlscredspsk: Free username on finalize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the creds->username property is set we allocate memory for it in qcrypto_tls_creds_psk_prop_set_username(), but we never free this when the QCryptoTLSCredsPSK is destroyed. Free the memory in finalize. This fixes a LeakSanitizer complaint in migration-test: $ (cd build/asan; ASAN_OPTIONS="fast_unwind_on_malloc=0" QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/migration-test --tap -k -p /x86_64/migration/precopy/unix/tls/psk) ================================================================= ==3867512==ERROR: LeakSanitizer: detected memory leaks Direct leak of 5 byte(s) in 1 object(s) allocated from: #0 0x5624e5c99dee in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x218edee) (BuildId: a9e623fa1009a9435c0142c037cd7b8c1ad04ce3) #1 0x7fb199ae9738 in g_malloc debian/build/deb/../../../glib/gmem.c:128:13 #2 0x7fb199afe583 in g_strdup debian/build/deb/../../../glib/gstrfuncs.c:361:17 #3 0x5624e82ea919 in qcrypto_tls_creds_psk_prop_set_username /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../crypto/tlscredspsk.c:255:23 #4 0x5624e812c6b5 in property_set_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object.c:2277:5 #5 0x5624e8125ce5 in object_property_set /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object.c:1463:5 #6 0x5624e8136e7c in object_set_properties_from_qdict /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object_interfaces.c:55:14 #7 0x5624e81372d2 in user_creatable_add_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object_interfaces.c:112:5 #8 0x5624e8137964 in user_creatable_add_qapi /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/object_interfaces.c:157:11 #9 0x5624e891ba3c in qmp_object_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qom/qom-qmp-cmds.c:227:5 #10 0x5624e8af9118 in qmp_marshal_object_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qapi/qapi-commands-qom.c:337:5 #11 0x5624e8bd1d49 in do_qmp_dispatch_bh /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../qapi/qmp-dispatch.c:128:5 #12 0x5624e8cb2531 in aio_bh_call /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/async.c:171:5 #13 0x5624e8cb340c in aio_bh_poll /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/async.c:218:13 #14 0x5624e8c0be98 in aio_dispatch /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/aio-posix.c:423:5 #15 0x5624e8cba3ce in aio_ctx_dispatch /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/async.c:360:5 #16 0x7fb199ae0d3a in g_main_dispatch debian/build/deb/../../../glib/gmain.c:3419:28 #17 0x7fb199ae0d3a in g_main_context_dispatch debian/build/deb/../../../glib/gmain.c:4137:7 #18 0x5624e8cbe1d9 in glib_pollfds_poll /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/main-loop.c:287:9 #19 0x5624e8cbcb13 in os_host_main_loop_wait /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/main-loop.c:310:5 #20 0x5624e8cbc6dc in main_loop_wait /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../util/main-loop.c:589:11 #21 0x5624e6f3f917 in qemu_main_loop /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../system/runstate.c:801:9 #22 0x5624e893379c in qemu_default_main /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../system/main.c:37:14 #23 0x5624e89337e7 in main /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../system/main.c:48:12 #24 0x7fb197972d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #25 0x7fb197972e3f in __libc_start_main csu/../csu/libc-start.c:392:3 #26 0x5624e5c16fa4 in _start (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x210bfa4) (BuildId: a9e623fa1009a9435c0142c037cd7b8c1ad04ce3) SUMMARY: AddressSanitizer: 5 byte(s) leaked in 1 allocation(s). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20240819145021.38524-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- crypto/tlscredspsk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index 546cad1c5a..0d6b71a37c 100644 --- a/crypto/tlscredspsk.c +++ b/crypto/tlscredspsk.c @@ -243,6 +243,7 @@ qcrypto_tls_creds_psk_finalize(Object *obj) QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); qcrypto_tls_creds_psk_unload(creds); + g_free(creds->username); } static void From 6a22121c4f25b181e99479f65958ecde65da1c92 Mon Sep 17 00:00:00 2001 From: Klaus Jensen <k.jensen@samsung.com> Date: Tue, 20 Aug 2024 06:16:48 +0200 Subject: [PATCH 2813/2884] hw/nvme: fix leak of uninitialized memory in io_mgmt_recv Yutaro Shimizu from the Cyber Defense Institute discovered a bug in the NVMe emulation that leaks contents of an uninitialized heap buffer if subsystem and FDP emulation are enabled. Cc: qemu-stable@nongnu.org Reported-by: Yutaro Shimizu <shimizu@cyberdefense.jp> Signed-off-by: Klaus Jensen <k.jensen@samsung.com> --- hw/nvme/ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index c6d4f61a47..9f277b81d8 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4474,7 +4474,7 @@ static uint16_t nvme_io_mgmt_recv_ruhs(NvmeCtrl *n, NvmeRequest *req, nruhsd = ns->fdp.nphs * endgrp->fdp.nrg; trans_len = sizeof(NvmeRuhStatus) + nruhsd * sizeof(NvmeRuhStatusDescr); - buf = g_malloc(trans_len); + buf = g_malloc0(trans_len); trans_len = MIN(trans_len, len); From f8e09b973ae8489b88394bff0118d19f989bf277 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Fri, 2 Aug 2024 14:38:19 +0900 Subject: [PATCH 2814/2884] vhost: Add VIRTIO_NET_F_RSC_EXT to vhost feature bits VIRTIO_NET_F_RSC_EXT is implemented in the rx data path, which vhost implements, so vhost needs to support the feature if it is ever to be enabled with vhost. The feature must be disabled otherwise. Fixes: 2974e916df87 ("virtio-net: support RSC v4/v6 tcp traffic for Windows HCK") Reported-by: Jason Wang <jasowang@redhat.com> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-Id: <20240802-rsc-v1-1-2b607bd2f555@daynix.com> Acked-by: Jason Wang <jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/net/vhost_net.c | 2 ++ net/vhost-vdpa.c | 1 + 2 files changed, 3 insertions(+) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index a788e6937e..dedf9ad7c2 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -50,6 +50,7 @@ static const int kernel_feature_bits[] = { VIRTIO_F_RING_RESET, VIRTIO_F_IN_ORDER, VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_NET_F_RSC_EXT, VIRTIO_NET_F_HASH_REPORT, VHOST_INVALID_FEATURE_BIT }; @@ -81,6 +82,7 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_RESET, VIRTIO_F_IN_ORDER, VIRTIO_NET_F_RSS, + VIRTIO_NET_F_RSC_EXT, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_GUEST_USO4, VIRTIO_NET_F_GUEST_USO6, diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 03457ead66..46b02c50be 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -88,6 +88,7 @@ const int vdpa_feature_bits[] = { VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU, + VIRTIO_NET_F_RSC_EXT, VIRTIO_NET_F_RSS, VIRTIO_NET_F_STATUS, VIRTIO_RING_F_EVENT_IDX, From 7d14471a121878602cb4e748c4707f9ab9a9e3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= <vr_qemu@t-online.de> Date: Fri, 2 Aug 2024 09:18:05 +0200 Subject: [PATCH 2815/2884] hw/audio/virtio-snd: fix invalid param check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9b6083465f ("virtio-snd: check for invalid param shift operands") tries to prevent invalid parameters specified by the guest. However, the code is not correct. Change the code so that the parameters format and rate, which are a bit numbers, are compared with the bit size of the data type. Fixes: 9b6083465f ("virtio-snd: check for invalid param shift operands") Signed-off-by: Volker Rümelin <vr_qemu@t-online.de> Message-Id: <20240802071805.7123-1-vr_qemu@t-online.de> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/audio/virtio-snd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index e5196aa4bb..d1cf5eb445 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -282,12 +282,12 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, error_report("Number of channels is not supported."); return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); } - if (BIT(params->format) > sizeof(supported_formats) || + if (params->format >= sizeof(supported_formats) * BITS_PER_BYTE || !(supported_formats & BIT(params->format))) { error_report("Stream format is not supported."); return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); } - if (BIT(params->rate) > sizeof(supported_rates) || + if (params->rate >= sizeof(supported_rates) * BITS_PER_BYTE || !(supported_rates & BIT(params->rate))) { error_report("Stream rate is not supported."); return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); From a8e63ff289d137197ad7a701a587cc432872d798 Mon Sep 17 00:00:00 2001 From: Cindy Lu <lulu@redhat.com> Date: Tue, 6 Aug 2024 17:37:12 +0800 Subject: [PATCH 2816/2884] virtio-pci: Fix the use of an uninitialized irqfd The crash was reported in MAC OS and NixOS, here is the link for this bug https://gitlab.com/qemu-project/qemu/-/issues/2334 https://gitlab.com/qemu-project/qemu/-/issues/2321 In this bug, they are using the virtio_input device. The guest notifier was not supported for this device, The function virtio_pci_set_guest_notifiers() was not called, and the vector_irqfd was not initialized. So the fix is adding the check for vector_irqfd in virtio_pci_get_notifier() The function virtio_pci_get_notifier() can be used in various devices. It could also be called when VIRTIO_CONFIG_S_DRIVER_OK is not set. In this situation, the vector_irqfd being NULL is acceptable. We can allow the device continue to boot If the vector_irqfd still hasn't been initialized after VIRTIO_CONFIG_S_DRIVER_OK is set, it means that the function set_guest_notifiers was not called before the driver started. This indicates that the device is not using the notifier. At this point, we will let the check fail. This fix is verified in vyatta,MacOS,NixOS,fedora system. The bt tree for this bug is: Thread 6 "CPU 0/KVM" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7c817be006c0 (LWP 1269146)] kvm_virtio_pci_vq_vector_use () at ../qemu-9.0.0/hw/virtio/virtio-pci.c:817 817 if (irqfd->users == 0) { (gdb) thread apply all bt ... Thread 6 (Thread 0x7c817be006c0 (LWP 1269146) "CPU 0/KVM"): 0 kvm_virtio_pci_vq_vector_use () at ../qemu-9.0.0/hw/virtio/virtio-pci.c:817 1 kvm_virtio_pci_vector_use_one () at ../qemu-9.0.0/hw/virtio/virtio-pci.c:893 2 0x00005983657045e2 in memory_region_write_accessor () at ../qemu-9.0.0/system/memory.c:497 3 0x0000598365704ba6 in access_with_adjusted_size () at ../qemu-9.0.0/system/memory.c:573 4 0x0000598365705059 in memory_region_dispatch_write () at ../qemu-9.0.0/system/memory.c:1528 5 0x00005983659b8e1f in flatview_write_continue_step.isra.0 () at ../qemu-9.0.0/system/physmem.c:2713 6 0x000059836570ba7d in flatview_write_continue () at ../qemu-9.0.0/system/physmem.c:2743 7 flatview_write () at ../qemu-9.0.0/system/physmem.c:2774 8 0x000059836570bb76 in address_space_write () at ../qemu-9.0.0/system/physmem.c:2894 9 0x0000598365763afe in address_space_rw () at ../qemu-9.0.0/system/physmem.c:2904 10 kvm_cpu_exec () at ../qemu-9.0.0/accel/kvm/kvm-all.c:2917 11 0x000059836576656e in kvm_vcpu_thread_fn () at ../qemu-9.0.0/accel/kvm/kvm-accel-ops.c:50 12 0x0000598365926ca8 in qemu_thread_start () at ../qemu-9.0.0/util/qemu-thread-posix.c:541 13 0x00007c8185bcd1cf in ??? () at /usr/lib/libc.so.6 14 0x00007c8185c4e504 in clone () at /usr/lib/libc.so.6 Fixes: 2ce6cff94d ("virtio-pci: fix use of a released vector") Cc: qemu-stable@nongnu.org Signed-off-by: Cindy Lu <lulu@redhat.com> Message-Id: <20240806093715.65105-1-lulu@redhat.com> Acked-by: Jason Wang <jasowang@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- hw/virtio/virtio-pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 9534730bba..524b63e5c7 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -866,6 +866,9 @@ static int virtio_pci_get_notifier(VirtIOPCIProxy *proxy, int queue_no, VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtQueue *vq; + if (!proxy->vector_irqfd && vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) + return -1; + if (queue_no == VIRTIO_CONFIG_IRQ_IDX) { *n = virtio_config_get_guest_notifier(vdev); *vector = vdev->config_vector; From 4c107870e8b2ba3951ee0c46123f1c3b5d3a19d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 20 Aug 2024 15:44:29 +0100 Subject: [PATCH 2817/2884] migration/multifd: Free MultiFDRecvParams::data In multifd_recv_setup() we allocate (among other things) * a MultiFDRecvData struct to multifd_recv_state::data * a MultiFDRecvData struct to each multfd_recv_state->params[i].data (Then during execution we might swap these pointers around.) But in multifd_recv_cleanup() we free multifd_recv_state->data in multifd_recv_cleanup_state() but we don't ever free the multifd_recv_state->params[i].data. This results in a memory leak reported by LeakSanitizer: (cd build/asan && \ ASAN_OPTIONS="fast_unwind_on_malloc=0:strip_path_prefix=/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/../../" \ QTEST_QEMU_BINARY=./qemu-system-x86_64 \ ./tests/qtest/migration-test --tap -k -p /x86_64/migration/multifd/file/mapped-ram ) [...] Direct leak of 72 byte(s) in 3 object(s) allocated from: #0 0x561cc0afcfd8 in __interceptor_calloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x218efd8) (BuildId: be72e086d4e47b172b0a72779972213fd9916466) #1 0x7f89d37acc50 in g_malloc0 debian/build/deb/../../../glib/gmem.c:161:13 #2 0x561cc1e9c83c in multifd_recv_setup migration/multifd.c:1606:19 #3 0x561cc1e68618 in migration_ioc_process_incoming migration/migration.c:972:9 #4 0x561cc1e3ac59 in migration_channel_process_incoming migration/channel.c:45:9 #5 0x561cc1e4fa0b in file_accept_incoming_migration migration/file.c:132:5 #6 0x561cc30f2c0c in qio_channel_fd_source_dispatch io/channel-watch.c:84:12 #7 0x7f89d37a3c43 in g_main_dispatch debian/build/deb/../../../glib/gmain.c:3419:28 #8 0x7f89d37a3c43 in g_main_context_dispatch debian/build/deb/../../../glib/gmain.c:4137:7 #9 0x561cc3b21659 in glib_pollfds_poll util/main-loop.c:287:9 #10 0x561cc3b1ff93 in os_host_main_loop_wait util/main-loop.c:310:5 #11 0x561cc3b1fb5c in main_loop_wait util/main-loop.c:589:11 #12 0x561cc1da2917 in qemu_main_loop system/runstate.c:801:9 #13 0x561cc3796c1c in qemu_default_main system/main.c:37:14 #14 0x561cc3796c67 in main system/main.c:48:12 #15 0x7f89d163bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #16 0x7f89d163be3f in __libc_start_main csu/../csu/libc-start.c:392:3 #17 0x561cc0a79fa4 in _start (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x210bfa4) (BuildId: be72e086d4e47b172b0a72779972213fd9916466) Direct leak of 24 byte(s) in 1 object(s) allocated from: #0 0x561cc0afcfd8 in __interceptor_calloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x218efd8) (BuildId: be72e086d4e47b172b0a72779972213fd9916466) #1 0x7f89d37acc50 in g_malloc0 debian/build/deb/../../../glib/gmem.c:161:13 #2 0x561cc1e9bed9 in multifd_recv_setup migration/multifd.c:1588:32 #3 0x561cc1e68618 in migration_ioc_process_incoming migration/migration.c:972:9 #4 0x561cc1e3ac59 in migration_channel_process_incoming migration/channel.c:45:9 #5 0x561cc1e4fa0b in file_accept_incoming_migration migration/file.c:132:5 #6 0x561cc30f2c0c in qio_channel_fd_source_dispatch io/channel-watch.c:84:12 #7 0x7f89d37a3c43 in g_main_dispatch debian/build/deb/../../../glib/gmain.c:3419:28 #8 0x7f89d37a3c43 in g_main_context_dispatch debian/build/deb/../../../glib/gmain.c:4137:7 #9 0x561cc3b21659 in glib_pollfds_poll util/main-loop.c:287:9 #10 0x561cc3b1ff93 in os_host_main_loop_wait util/main-loop.c:310:5 #11 0x561cc3b1fb5c in main_loop_wait util/main-loop.c:589:11 #12 0x561cc1da2917 in qemu_main_loop system/runstate.c:801:9 #13 0x561cc3796c1c in qemu_default_main system/main.c:37:14 #14 0x561cc3796c67 in main system/main.c:48:12 #15 0x7f89d163bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #16 0x7f89d163be3f in __libc_start_main csu/../csu/libc-start.c:392:3 #17 0x561cc0a79fa4 in _start (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/asan/qemu-system-x86_64+0x210bfa4) (BuildId: be72e086d4e47b172b0a72779972213fd9916466) SUMMARY: AddressSanitizer: 96 byte(s) leaked in 4 allocation(s). Free the params[i].data too. Cc: qemu-stable@nongnu.org Fixes: d117ed0699d41 ("migration/multifd: Allow receiving pages without packets") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Fabiano Rosas <farosas@suse.de> --- migration/multifd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/multifd.c b/migration/multifd.c index 552f9723c8..a6db05502a 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1357,6 +1357,8 @@ static void multifd_recv_cleanup_channel(MultiFDRecvParams *p) qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem_sync); qemu_sem_destroy(&p->sem); + g_free(p->data); + p->data = NULL; g_free(p->name); p->name = NULL; p->packet_len = 0; From a4ad4a9d98f7fbde806f07da21e69f39e134cdf1 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Fri, 16 Aug 2024 07:13:31 +1000 Subject: [PATCH 2818/2884] linux-user: Handle short reads in mmap_h_gt_g MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular, if an image has a large bss, we can hit EOF before reading all host_len bytes of the mapping. Create a helper, mmap_pread to handle the job for both the larger block in mmap_h_gt_g itself, as well as the smaller block in mmap_frag. Cc: qemu-stable@nongnu.org Fixes: eb5027ac618 ("linux-user: Split out mmap_h_gt_g") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2504 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240820050848.165253-2-richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- linux-user/mmap.c | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 6418e811f6..e4bf5d5f39 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -283,6 +283,40 @@ static int do_munmap(void *addr, size_t len) return munmap(addr, len); } +/* + * Perform a pread on behalf of target_mmap. We can reach EOF, we can be + * interrupted by signals, and in general there's no good error return path. + * If @zero, zero the rest of the block at EOF. + * Return true on success. + */ +static bool mmap_pread(int fd, void *p, size_t len, off_t offset, bool zero) +{ + while (1) { + ssize_t r = pread(fd, p, len, offset); + + if (likely(r == len)) { + /* Complete */ + return true; + } + if (r == 0) { + /* EOF */ + if (zero) { + memset(p, 0, len); + } + return true; + } + if (r > 0) { + /* Short read */ + p += r; + len -= r; + offset += r; + } else if (errno != EINTR) { + /* Error */ + return false; + } + } +} + /* * Map an incomplete host page. * @@ -357,10 +391,9 @@ static bool mmap_frag(abi_ulong real_start, abi_ulong start, abi_ulong last, /* Read or zero the new guest pages. */ if (flags & MAP_ANONYMOUS) { memset(g2h_untagged(start), 0, last - start + 1); - } else { - if (pread(fd, g2h_untagged(start), last - start + 1, offset) == -1) { - return false; - } + } else if (!mmap_pread(fd, g2h_untagged(start), last - start + 1, + offset, true)) { + return false; } /* Put final protection */ @@ -853,8 +886,7 @@ static abi_long mmap_h_gt_g(abi_ulong start, abi_ulong len, } if (misaligned_offset) { - /* TODO: The read could be short. */ - if (pread(fd, p, host_len, offset + real_start - start) != host_len) { + if (!mmap_pread(fd, p, host_len, offset + real_start - start, false)) { do_munmap(p, host_len); return -1; } From 5b73b248a16dab43b74c4d2dbe4f589e109fdc85 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 20 Aug 2024 09:41:34 +1000 Subject: [PATCH 2819/2884] bsd-user: Handle short reads in mmap_h_gt_g MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular, if an image has a large bss, we can hit EOF before reading all bytes of the mapping. Mirror the similar change to linux-user. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240820050848.165253-3-richard.henderson@linaro.org> --- bsd-user/mmap.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index f3a4f1712d..775e905960 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -128,6 +128,40 @@ error: return ret; } +/* + * Perform a pread on behalf of target_mmap. We can reach EOF, we can be + * interrupted by signals, and in general there's no good error return path. + * If @zero, zero the rest of the block at EOF. + * Return true on success. + */ +static bool mmap_pread(int fd, void *p, size_t len, off_t offset, bool zero) +{ + while (1) { + ssize_t r = pread(fd, p, len, offset); + + if (likely(r == len)) { + /* Complete */ + return true; + } + if (r == 0) { + /* EOF */ + if (zero) { + memset(p, 0, len); + } + return true; + } + if (r > 0) { + /* Short read */ + p += r; + len -= r; + offset += r; + } else if (errno != EINTR) { + /* Error */ + return false; + } + } +} + /* * map an incomplete host page * @@ -190,7 +224,7 @@ static int mmap_frag(abi_ulong real_start, mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); /* read the corresponding file data */ - if (pread(fd, g2h_untagged(start), end - start, offset) == -1) { + if (!mmap_pread(fd, g2h_untagged(start), end - start, offset, true)) { return -1; } @@ -565,7 +599,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, -1, 0); if (retaddr == -1) goto fail; - if (pread(fd, g2h_untagged(start), len, offset) == -1) { + if (!mmap_pread(fd, g2h_untagged(start), len, offset, false)) { goto fail; } if (!(prot & PROT_WRITE)) { From 266d6dddbd85286e64004499f6f8f6fad15e5521 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Thu, 1 Aug 2024 17:42:36 +1000 Subject: [PATCH 2820/2884] target/i386: Split out gen_prepare_val_nz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out the TCG_COND_TSTEQ logic from gen_prepare_eflags_z, and use it for CC_OP_BMILG* as well. Prepare for requiring both zero and non-zero senses. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240801075845.573075-2-richard.henderson@linaro.org> --- target/i386/tcg/translate.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index b72864bf01..4af282e626 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -865,6 +865,18 @@ static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size) } } +static CCPrepare gen_prepare_val_nz(TCGv src, MemOp size, bool eqz) +{ + if (size == MO_TL) { + return (CCPrepare) { .cond = eqz ? TCG_COND_EQ : TCG_COND_NE, + .reg = src }; + } else { + return (CCPrepare) { .cond = eqz ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + .imm = MAKE_64BIT_MASK(0, 8 << size), + .reg = src }; + } +} + /* compute eflags.C, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) { @@ -908,8 +920,7 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) case CC_OP_BMILGB ... CC_OP_BMILGQ: size = s->cc_op - CC_OP_BMILGB; - gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src }; + return gen_prepare_val_nz(cpu_cc_src, size, true); case CC_OP_ADCX: case CC_OP_ADCOX: @@ -1006,12 +1017,7 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) default: { MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - if (size == MO_TL) { - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst }; - } else { - return (CCPrepare) { .cond = TCG_COND_TSTEQ, .reg = cpu_cc_dst, - .imm = (1ull << (8 << size)) - 1 }; - } + return gen_prepare_val_nz(cpu_cc_dst, size, true); } } } From 83a3a20e59fa4b1add714bb4062af0d144b67ab7 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Thu, 1 Aug 2024 17:57:45 +1000 Subject: [PATCH 2821/2884] target/i386: Fix carry flag for BLSI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BLSI has inverted semantics for C as compared to the other two BMI1 instructions, BLSMSK and BLSR. Introduce CC_OP_BLSI* for this purpose. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2175 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20240801075845.573075-3-richard.henderson@linaro.org> --- target/i386/cpu.h | 5 +++++ target/i386/tcg/cc_helper.c | 18 ++++++++++++++++++ target/i386/tcg/cc_helper_template.h.inc | 18 ++++++++++++++++++ target/i386/tcg/emit.c.inc | 2 +- target/i386/tcg/translate.c | 5 +++++ tests/tcg/x86_64/Makefile.target | 1 + tests/tcg/x86_64/test-2175.c | 24 ++++++++++++++++++++++++ 7 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/x86_64/test-2175.c diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c6cc035df3..14edd57a37 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1339,6 +1339,11 @@ typedef enum { CC_OP_BMILGL, CC_OP_BMILGQ, + CC_OP_BLSIB, /* Z,S via CC_DST, C = SRC!=0; O=0; P,A undefined */ + CC_OP_BLSIW, + CC_OP_BLSIL, + CC_OP_BLSIQ, + /* * Note that only CC_OP_POPCNT (i.e. the one with MO_TL size) * is used or implemented, because the translation needs diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c index 301ed95406..dbddaa2fcb 100644 --- a/target/i386/tcg/cc_helper.c +++ b/target/i386/tcg/cc_helper.c @@ -186,6 +186,13 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, case CC_OP_BMILGL: return compute_all_bmilgl(dst, src1); + case CC_OP_BLSIB: + return compute_all_blsib(dst, src1); + case CC_OP_BLSIW: + return compute_all_blsiw(dst, src1); + case CC_OP_BLSIL: + return compute_all_blsil(dst, src1); + case CC_OP_ADCX: return compute_all_adcx(dst, src1, src2); case CC_OP_ADOX: @@ -216,6 +223,8 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, return compute_all_sarq(dst, src1); case CC_OP_BMILGQ: return compute_all_bmilgq(dst, src1); + case CC_OP_BLSIQ: + return compute_all_blsiq(dst, src1); #endif } } @@ -308,6 +317,13 @@ target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, case CC_OP_BMILGL: return compute_c_bmilgl(dst, src1); + case CC_OP_BLSIB: + return compute_c_blsib(dst, src1); + case CC_OP_BLSIW: + return compute_c_blsiw(dst, src1); + case CC_OP_BLSIL: + return compute_c_blsil(dst, src1); + #ifdef TARGET_X86_64 case CC_OP_ADDQ: return compute_c_addq(dst, src1); @@ -321,6 +337,8 @@ target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, return compute_c_shlq(dst, src1); case CC_OP_BMILGQ: return compute_c_bmilgq(dst, src1); + case CC_OP_BLSIQ: + return compute_c_blsiq(dst, src1); #endif } } diff --git a/target/i386/tcg/cc_helper_template.h.inc b/target/i386/tcg/cc_helper_template.h.inc index bb611feb04..c5425e57cf 100644 --- a/target/i386/tcg/cc_helper_template.h.inc +++ b/target/i386/tcg/cc_helper_template.h.inc @@ -235,6 +235,24 @@ static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) return src1 == 0; } +static int glue(compute_all_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + + cf = (src1 != 0); + pf = 0; /* undefined */ + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return src1 != 0; +} + #undef DATA_BITS #undef SIGN_MASK #undef DATA_TYPE diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 22a06897fb..9b50419918 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1304,7 +1304,7 @@ static void gen_BLSI(DisasContext *s, X86DecodedInsn *decode) /* input in T1, which is ready for prepare_update2_cc */ tcg_gen_neg_tl(s->T0, s->T1); tcg_gen_and_tl(s->T0, s->T0, s->T1); - prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); + prepare_update2_cc(decode, s, CC_OP_BLSIB + ot); } static void gen_BLSMSK(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 4af282e626..98f5fe61ed 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -304,6 +304,7 @@ static const uint8_t cc_op_live[CC_OP_NB] = { [CC_OP_SHLB ... CC_OP_SHLQ] = USES_CC_DST | USES_CC_SRC, [CC_OP_SARB ... CC_OP_SARQ] = USES_CC_DST | USES_CC_SRC, [CC_OP_BMILGB ... CC_OP_BMILGQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_BLSIB ... CC_OP_BLSIQ] = USES_CC_DST | USES_CC_SRC, [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC, [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, @@ -922,6 +923,10 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) size = s->cc_op - CC_OP_BMILGB; return gen_prepare_val_nz(cpu_cc_src, size, true); + case CC_OP_BLSIB ... CC_OP_BLSIQ: + size = s->cc_op - CC_OP_BLSIB; + return gen_prepare_val_nz(cpu_cc_src, size, false); + case CC_OP_ADCX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index eda9bd7396..783ab5b21a 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -16,6 +16,7 @@ X86_64_TESTS += noexec X86_64_TESTS += cmpxchg X86_64_TESTS += adox X86_64_TESTS += test-1648 +X86_64_TESTS += test-2175 TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else TESTS=$(MULTIARCH_TESTS) diff --git a/tests/tcg/x86_64/test-2175.c b/tests/tcg/x86_64/test-2175.c new file mode 100644 index 0000000000..aafd037bce --- /dev/null +++ b/tests/tcg/x86_64/test-2175.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2185 */ + +#include <assert.h> + +int test_setc(unsigned int x, unsigned int y) +{ + asm("blsi %1, %0; setc %b0" : "+r"(x) : "r"(y)); + return (unsigned char)x; +} + +int test_pushf(unsigned int x, unsigned int y) +{ + asm("blsi %1, %0; pushf; pop %q0" : "+r"(x) : "r"(y)); + return x & 1; +} + +int main() +{ + assert(test_setc(1, 0xedbf530a)); + assert(test_pushf(1, 0xedbf530a)); + return 0; +} + From ded1db48c9f9b35f6d9569e53503e2b345f6d44e Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Mon, 19 Aug 2024 17:39:55 +1000 Subject: [PATCH 2822/2884] target/i386: Fix tss access size in switch_tss_ra The two limit_max variables represent size - 1, just like the encoding in the GDT, thus the 'old' access was off by one. Access the minimal size of the new tss: the complete tss contains the iopb, which may be a larger block than the access api expects, and irrelevant because the iopb is not accessed during the switch itself. Fixes: 8b131065080a ("target/i386/tcg: use X86Access for TSS access") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2511 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240819074052.207783-1-richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> --- target/i386/tcg/seg_helper.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index bab552cd53..3b8fd827e1 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -378,7 +378,7 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, /* X86Access avoids memory exceptions during the task switch */ mmu_index = cpu_mmu_index_kernel(env); - access_prepare_mmu(&old, env, env->tr.base, old_tss_limit_max, + access_prepare_mmu(&old, env, env->tr.base, old_tss_limit_max + 1, MMU_DATA_STORE, mmu_index, retaddr); if (source == SWITCH_TSS_CALL) { @@ -386,7 +386,8 @@ static int switch_tss_ra(CPUX86State *env, int tss_selector, probe_access(env, tss_base, 2, MMU_DATA_STORE, mmu_index, retaddr); } - access_prepare_mmu(&new, env, tss_base, tss_limit, + /* While true tss_limit may be larger, we don't access the iopb here. */ + access_prepare_mmu(&new, env, tss_base, tss_limit_max + 1, MMU_DATA_LOAD, mmu_index, retaddr); /* save the current state in the old TSS */ From d4f5e5af86d2e28edb578e556b307e3ad01ebf08 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang <jiaxun.yang@flygoat.com> Date: Tue, 20 Aug 2024 19:42:33 +0100 Subject: [PATCH 2823/2884] hw/loongarch: Fix length for lowram in ACPI SRAT The size of lowram should be "gap" instead of the whole node. This is failing kernel's sanity check: [ 0.000000] ACPI: SRAT: Node 0 PXM 0 [mem 0x00000000-0xffffffff] [ 0.000000] ACPI: SRAT: Node 0 PXM 0 [mem 0x80000000-0x16fffffff] [ 0.000000] ACPI: SRAT: Node 1 PXM 1 [mem 0x170000000-0x26fffffff] [ 0.000000] Warning: node 0 [mem 0x00000000-0xffffffff] overlaps with itself [mem 0x80000000-0x16fffffff] Fixes: fc100011f38d ("hw/loongarch: Refine acpi srat table for numa memory") Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Reviewed-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn> --- hw/loongarch/acpi-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c index 72bfc35ae6..2638f87434 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -218,7 +218,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) */ if (len >= gap) { - build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); + build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED); len -= gap; base = VIRT_HIGHMEM_BASE; gap = machine->ram_size - VIRT_LOWMEM_SIZE; From 407f9a4b121eb65166375c410e14d7b704bc1106 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Thu, 22 Aug 2024 15:07:32 +1000 Subject: [PATCH 2824/2884] Update version for v9.1.0-rc3 release Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index cd036422bd..8a836fe32c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.92 +9.0.93 From 80e3541282af347538a37ee0673bcc04c622ad1c Mon Sep 17 00:00:00 2001 From: Ani Sinha <anisinha@redhat.com> Date: Fri, 19 Jul 2024 19:19:37 +0530 Subject: [PATCH 2825/2884] hw/x86: add a couple of comments explaining how the kernel image is parsed Cosmetic: add comments in x86_load_linux() pointing to the kernel documentation so that users can better understand the code. CC: qemu-trivial@nongnu.org Signed-off-by: Ani Sinha <anisinha@redhat.com> Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/i386/x86-common.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index c0c66a0eb5..992ea1f25e 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -665,8 +665,11 @@ void x86_load_linux(X86MachineState *x86ms, exit(1); } - /* kernel protocol version */ - if (ldl_p(header + 0x202) == 0x53726448) { + /* + * kernel protocol version. + * Please see https://www.kernel.org/doc/Documentation/x86/boot.txt + */ + if (ldl_p(header + 0x202) == 0x53726448) /* Magic signature "HdrS" */ { protocol = lduw_p(header + 0x206); } else { /* From d53bb908b552df11f4ac5541ec133ef4123fc0ca Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Thu, 22 Aug 2024 13:23:10 +0100 Subject: [PATCH 2826/2884] system/vl.c: Print machine name, not "(null)", for unknown machine types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 412d294ffdc we tried to improve the error message printed when the machine type is unknown, but we used the wrong variable, resulting in: $ ./build/x86/qemu-system-aarch64 -M bang qemu-system-aarch64: unsupported machine type: "(null)" Use -machine help to list supported machines Use the right variable, so we produce more helpful output: $ ./build/x86/qemu-system-aarch64 -M bang qemu-system-aarch64: unsupported machine type: "bang" Use -machine help to list supported machines Note that we must move the qdict_del() to below the error_setg(), because machine_type points into the value of that qdict entry, and deleting it will make the pointer invalid. Cc: qemu-stable@nongnu.org Fixes: 412d294ffdc ("vl.c: select_machine(): add selected machine type to error message") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- system/vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/vl.c b/system/vl.c index 41d53d2456..01b8b8e77a 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1679,10 +1679,10 @@ static MachineClass *select_machine(QDict *qdict, Error **errp) if (machine_type) { machine_class = find_machine(machine_type, machines); - qdict_del(qdict, "type"); if (!machine_class) { - error_setg(errp, "unsupported machine type: \"%s\"", optarg); + error_setg(errp, "unsupported machine type: \"%s\"", machine_type); } + qdict_del(qdict, "type"); } else { machine_class = find_default_machine(machines); if (!machine_class) { From d6192f3f7593536a4285e8ab6c6cf3f34973ce62 Mon Sep 17 00:00:00 2001 From: Haoran Zhang <wh1sper@zju.edu.cn> Date: Thu, 22 Aug 2024 11:07:58 +0800 Subject: [PATCH 2827/2884] hw/display/vhost-user-gpu.c: fix vhost_user_gpu_chr_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix vhost_user_gpu_chr_read() where `size` was incorrectly passed to `msg->flags`. Fixes: 267f664658 ("hw/display: add vhost-user-vga & gpu-pci") Signed-off-by: Haoran Zhang <wh1sper@zju.edu.cn> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/display/vhost-user-gpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 63c64ddde6..c0c66910f1 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -390,7 +390,7 @@ vhost_user_gpu_chr_read(void *opaque) } msg->request = request; - msg->flags = size; + msg->flags = flags; msg->size = size; if (request == VHOST_USER_GPU_CURSOR_UPDATE || From eb9ca730da158c5f1bdc38105b78e66817888e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Fri, 16 Aug 2024 23:32:03 +0200 Subject: [PATCH 2828/2884] gitlab-ci: Replace build_script -> step_script in Cirrus jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Long due upgrade, see [1]: In GitLab Runner 13.2 a translation for step_script to build_script was added to the custom executor. In 14.0 the build_script stage will be replaced with step_script. We are using GitLab 17 [2]! This removes the following warning: WARNING: Starting with version 17.0 the 'build_script' stage will be replaced with 'step_script': https://gitlab.com/groups/gitlab-org/-/epics/6112 [1] https://about.gitlab.com/releases/2021/05/22/gitlab-13-12-released/#remove-translation-from-stepscript-to-buildscript-in-custom-executor [2] https://about.gitlab.com/releases/2024/05/16/gitlab-17-0-released/ Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Message-ID: <20240816213203.18350-1-philmd@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> --- .gitlab-ci.d/cirrus/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/cirrus/build.yml b/.gitlab-ci.d/cirrus/build.yml index 43dd52dd19..102cdbd8b1 100644 --- a/.gitlab-ci.d/cirrus/build.yml +++ b/.gitlab-ci.d/cirrus/build.yml @@ -26,7 +26,7 @@ build_task: - git clone --depth 100 "$CI_REPOSITORY_URL" . - git fetch origin "$CI_COMMIT_REF_NAME" - git reset --hard "$CI_COMMIT_SHA" - build_script: + step_script: - mkdir build - cd build - ../configure --enable-werror $CONFIGURE_ARGS From 8f97deb99c8b48a34322b5e21e8dc061855551a4 Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Tue, 20 Aug 2024 19:01:42 +0200 Subject: [PATCH 2829/2884] .gitlab-ci.d/windows.yml: Disable the qtests in the MSYS2 job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qtests are broken since a while in the MSYS2 job in the gitlab-CI, likely due to some changes in the MSYS2 environment. So far nobody has neither a clue what's going wrong here, nor an idea how to fix this (in fact most QEMU developers even don't have a Windows environment available for properly analyzing this problem), so we should disable the qtests here for the time being to get at least test coverage again for the remaining tests that are run here. Since we already get compile-test coverage for the system emulation in the cross-win64-system job, and since the MSYS2 job is one of the longest running jobs in our CI (it takes more than 1 hour to complete), let's seize the opportunity and also cut the run time by disabling the system emulation completely here, including the libraries that are only useful for system emulation. In case somebody ever figures out the failure of the qtests on MSYS2, we can revert this patch to get everything back. Suggested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20240820170142.55324-1-thuth@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- .gitlab-ci.d/windows.yml | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index a83f23a786..759e9a76b5 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -17,12 +17,7 @@ msys2-64bit: # This feature doesn't (currently) work with PowerShell, it stops # the echo'ing of commands being run and doesn't show any timing FF_SCRIPT_SECTIONS: 0 - # do not remove "--without-default-devices"! - # commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices" - # changed to compile QEMU with the --without-default-devices switch - # for this job, because otherwise the build could not complete within - # the project timeout. - CONFIGURE_ARGS: --target-list=sparc-softmmu --without-default-devices -Ddebug=false -Doptimization=0 + CONFIGURE_ARGS: --disable-system --enable-tools -Ddebug=false -Doptimization=0 # The Windows git is a bit older so override the default GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet artifacts: @@ -81,33 +76,16 @@ msys2-64bit: bison diffutils flex git grep make sed mingw-w64-x86_64-binutils - mingw-w64-x86_64-capstone mingw-w64-x86_64-ccache mingw-w64-x86_64-curl - mingw-w64-x86_64-cyrus-sasl - mingw-w64-x86_64-dtc mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 - mingw-w64-x86_64-gnutls - mingw-w64-x86_64-gtk3 - mingw-w64-x86_64-libgcrypt - mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-libnfs - mingw-w64-x86_64-libpng mingw-w64-x86_64-libssh - mingw-w64-x86_64-libtasn1 - mingw-w64-x86_64-libusb - mingw-w64-x86_64-lzo2 - mingw-w64-x86_64-nettle mingw-w64-x86_64-ninja mingw-w64-x86_64-pixman mingw-w64-x86_64-pkgconf mingw-w64-x86_64-python - mingw-w64-x86_64-SDL2 - mingw-w64-x86_64-SDL2_image - mingw-w64-x86_64-snappy - mingw-w64-x86_64-spice - mingw-w64-x86_64-usbredir mingw-w64-x86_64-zstd" - Write-Output "Running build at $(Get-Date -Format u)" - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory @@ -120,7 +98,7 @@ msys2-64bit: - mkdir build - cd build - ..\msys64\usr\bin\bash -lc "ccache --zero-stats" - - ..\msys64\usr\bin\bash -lc "../configure --enable-fdt=system $CONFIGURE_ARGS" + - ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS" - ..\msys64\usr\bin\bash -lc "make" - ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;" - ..\msys64\usr\bin\bash -lc "ccache --show-stats" From aee07f2563d27167935ae3557a9f435937eb3f9f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki <akihiko.odaki@daynix.com> Date: Fri, 23 Aug 2024 15:13:12 +0900 Subject: [PATCH 2830/2884] tests/qtest: Delete previous boot file A test run may create boot files several times. Delete the previous boot file before creating a new one. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20240823-san-v4-7-a24c6dfa4ceb@daynix.com> Signed-off-by: Thomas Huth <thuth@redhat.com> --- tests/qtest/migration-test.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 70b606b888..6c06100d91 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -144,12 +144,23 @@ static char *bootpath; #include "tests/migration/ppc64/a-b-kernel.h" #include "tests/migration/s390x/a-b-bios.h" +static void bootfile_delete(void) +{ + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + static void bootfile_create(char *dir, bool suspend_me) { const char *arch = qtest_get_arch(); unsigned char *content; size_t len; + if (bootpath) { + bootfile_delete(); + } + bootpath = g_strdup_printf("%s/bootsect", dir); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { /* the assembled x86 boot sector should be exactly one sector large */ @@ -177,13 +188,6 @@ static void bootfile_create(char *dir, bool suspend_me) fclose(bootfile); } -static void bootfile_delete(void) -{ - unlink(bootpath); - g_free(bootpath); - bootpath = NULL; -} - /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's From 3874f5f73c441c52f1c699c848d463b0eda01e4c Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Thu, 22 Aug 2024 09:35:29 -0500 Subject: [PATCH 2831/2884] nbd/server: CVE-2024-7409: Avoid use-after-free when closing server Commit 3e7ef738 plugged the use-after-free of the global nbd_server object, but overlooked a use-after-free of nbd_server->listener. Although this race is harder to hit, notice that our shutdown path first drops the reference count of nbd_server->listener, then triggers actions that can result in a pending client reaching the nbd_blockdev_client_closed() callback, which in turn calls qio_net_listener_set_client_func on a potentially stale object. If we know we don't want any more clients to connect, and have already told the listener socket to shut down, then we should not be trying to update the listener socket's associated function. Reproducer: > #!/usr/bin/python3 > > import os > from threading import Thread > > def start_stop(): > while 1: > os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-start", +"arguments":{"addr":{"type":"unix","data":{"path":"/tmp/nbd-sock"}}}}\'') > os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-stop"}\'') > > def nbd_list(): > while 1: > os.system('/path/to/build/qemu-nbd -L -k /tmp/nbd-sock') > > def test(): > sst = Thread(target=start_stop) > sst.start() > nlt = Thread(target=nbd_list) > nlt.start() > > sst.join() > nlt.join() > > test() Fixes: CVE-2024-7409 Fixes: 3e7ef738c8 ("nbd/server: CVE-2024-7409: Close stray clients at server-stop") CC: qemu-stable@nongnu.org Reported-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20240822143617.800419-2-eblake@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> --- blockdev-nbd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index f73409ae49..b36f41b7c5 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -92,10 +92,13 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, static void nbd_update_server_watch(NBDServerData *s) { - if (!s->max_connections || s->connections < s->max_connections) { - qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL); - } else { - qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + if (s->listener) { + if (!s->max_connections || s->connections < s->max_connections) { + qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, + NULL); + } else { + qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + } } } @@ -113,6 +116,7 @@ static void nbd_server_free(NBDServerData *server) */ qio_net_listener_disconnect(server->listener); object_unref(OBJECT(server->listener)); + server->listener = NULL; QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) { qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL); From 43e0d14ee09a0565adcf4ce5f35be79695958fb0 Mon Sep 17 00:00:00 2001 From: John Snow <jsnow@redhat.com> Date: Thu, 22 Aug 2024 16:48:03 -0400 Subject: [PATCH 2832/2884] docs/sphinx: fix extra stuff in TOC after freeform QMP sections Freeform sections with titles are currently generating a TOC entry for the first paragraph in the section after the header, which is not what we want. (Easiest to observe directly in the QMP reference manual's "Introduction" section.) When freeform sections are parsed, we create both a section header *and* an empty, title-less section. This causes some problems with sphinx's post-parse tree transforms, see also 2664f317 - this is a similar issue: Sphinx doesn't like section-less titles and it also doesn't like title-less sections. Modify qapidoc.py to parse text directly into the preceding section title as child nodes, eliminating the section duplication. This removes the extra text from the TOC. Only very, very lightly tested: "it looks right at a glance" :tm:. I am still in the process of rewriting qapidoc, so I didn't give it much deeper thought. Reported-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: John Snow <jsnow@redhat.com> Message-ID: <20240822204803.1649762-1-jsnow@redhat.com> --- docs/sphinx/qapidoc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 738b2450fb..5f96b46270 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -388,6 +388,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): self._active_headings[level - 1] += snode self._active_headings = self._active_headings[:level] self._active_headings.append(snode) + return snode def _add_node_to_current_heading(self, node): """Add the node to whatever the current active heading is""" @@ -417,13 +418,11 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): # the first line of the block) (heading, _, text) = text.partition('\n') (leader, _, heading) = heading.partition(' ') - self._start_new_heading(heading, len(leader)) + node = self._start_new_heading(heading, len(leader)) if text == '': return - node = self._make_section(None) self._parse_text_into_node(text, node) - self._add_node_to_current_heading(node) self._cur_doc = None def _parse_text_into_node(self, doctext, node): From cec99171931ea79215c79661d33423ac84e63b6e Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Wed, 28 Aug 2024 22:28:42 +1000 Subject: [PATCH 2833/2884] Update version for v9.1.0-rc4 release Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8a836fe32c..82dc9a9a22 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.93 +9.0.94 From fd1952d814da738ed107e05583b3e02ac11e88ff Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 3 Sep 2024 09:18:26 -0700 Subject: [PATCH 2834/2884] Update version for v9.1.0 release Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 82dc9a9a22..47da986f86 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.94 +9.1.0 From 20eee6cb3d3d75a471fbf200c68441893aa5491a Mon Sep 17 00:00:00 2001 From: Helge Deller <deller@gmx.de> Date: Tue, 3 Sep 2024 17:22:10 +0200 Subject: [PATCH 2835/2884] target/hppa: Fix PSW V-bit packaging in cpu_hppa_get for hppa64 While adding hppa64 support, the psw_v variable got extended from 32 to 64 bits. So, when packaging the PSW-V bit from the psw_v variable for interrupt processing, check bit 31 instead the 63th (sign) bit. This fixes a hard to find Linux kernel boot issue where the loss of the PSW-V bit due to an ITLB interruption in the middle of a series of ds/addc instructions (from the divU milicode library) generated the wrong division result and thus triggered a Linux kernel crash. Link: https://lore.kernel.org/lkml/718b8afe-222f-4b3a-96d3-93af0e4ceff1@roeck-us.net/ Reported-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Helge Deller <deller@gmx.de> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Guenter Roeck <linux@roeck-us.net> Fixes: 931adff31478 ("target/hppa: Update cpu_hppa_get/put_psw for hppa64") Cc: qemu-stable@nongnu.org # v8.2+ (cherry picked from commit ead5078cf1a5f11d16e3e8462154c859620bcc7e) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/hppa/cpu.h | 2 +- target/hppa/helper.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 2bcb3b602b..5478b183dc 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -211,7 +211,7 @@ typedef struct CPUArchState { uint32_t psw; /* All psw bits except the following: */ uint32_t psw_xb; /* X and B, in their normal positions */ target_ulong psw_n; /* boolean */ - target_long psw_v; /* in most significant bit */ + target_long psw_v; /* in bit 31 */ /* Splitting the carry-borrow field into the MSB and "the rest", allows * for "the rest" to be deleted when it is unused, but the MSB is in use. diff --git a/target/hppa/helper.c b/target/hppa/helper.c index b79ddd8184..d4b1a3cd5a 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -53,7 +53,7 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env) } psw |= env->psw_n * PSW_N; - psw |= (env->psw_v < 0) * PSW_V; + psw |= ((env->psw_v >> 31) & 1) * PSW_V; psw |= env->psw | env->psw_xb; return psw; From f74e5bd9b9ecd189a108c6a0f2f386799804fb9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Thu, 29 Aug 2024 11:58:51 +0100 Subject: [PATCH 2836/2884] iotests: fix expected output from gnutls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Error reporting from gnutls was improved by: commit 57941c9c86357a6a642f9ee3279d881df4043b6d Author: Daniel P. Berrangé <berrange@redhat.com> Date: Fri Mar 15 14:07:58 2024 +0000 crypto: push error reporting into TLS session I/O APIs This has the effect of changing the output from one of the NBD tests. Reported-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (cherry picked from commit 48b8583698d96d6290726400789fcd51c55691b1) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- tests/qemu-iotests/233.out | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out index 1910f7df20..d498d55e0e 100644 --- a/tests/qemu-iotests/233.out +++ b/tests/qemu-iotests/233.out @@ -69,8 +69,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == check TLS with authorization == -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: The TLS connection was non-properly terminated. == check TLS fail over UNIX with no hostname == qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0': No hostname for certificate validation @@ -103,14 +103,14 @@ qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0' qemu-nbd: TLS handshake failed: The TLS connection was non-properly terminated. == final server log == -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. *** done From ae2dc2a27acf7115b886c9e1b7138ed4ef025160 Mon Sep 17 00:00:00 2001 From: Tiago Pasqualini <tiago.pasqualini@canonical.com> Date: Wed, 4 Sep 2024 20:52:30 -0300 Subject: [PATCH 2837/2884] crypto: run qcrypto_pbkdf2_count_iters in a new thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU time accounting in the kernel has been demonstrated to have a sawtooth pattern[1][2]. This can cause the getrusage system call to not be as accurate as we are expecting, which can cause this calculation to stall. The kernel discussions shows that this inaccuracy happens when CPU time gets big enough, so this patch changes qcrypto_pbkdf2_count_iters to run in a fresh thread to avoid this inaccuracy. It also adds a sanity check to fail the process if CPU time is not accounted. [1] https://lore.kernel.org/lkml/159231011694.16989.16351419333851309713.tip-bot2@tip-bot2/ [2] https://lore.kernel.org/lkml/20221226031010.4079885-1-maxing.lan@bytedance.com/t/#m1c7f2fdc0ea742776a70fd1aa2a2e414c437f534 Resolves: #2398 Signed-off-by: Tiago Pasqualini <tiago.pasqualini@canonical.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (cherry picked from commit c72cab5ad9f849bbcfcf4be7952b8b8946cc626e) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- crypto/pbkdf.c | 53 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/crypto/pbkdf.c b/crypto/pbkdf.c index 8d198c152c..d1c06ef3ed 100644 --- a/crypto/pbkdf.c +++ b/crypto/pbkdf.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/thread.h" #include "qapi/error.h" #include "crypto/pbkdf.h" #ifndef _WIN32 @@ -85,12 +86,28 @@ static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, #endif } -uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, - const uint8_t *key, size_t nkey, - const uint8_t *salt, size_t nsalt, - size_t nout, - Error **errp) +typedef struct CountItersData { + QCryptoHashAlgorithm hash; + const uint8_t *key; + size_t nkey; + const uint8_t *salt; + size_t nsalt; + size_t nout; + uint64_t iterations; + Error **errp; +} CountItersData; + +static void *threaded_qcrypto_pbkdf2_count_iters(void *data) { + CountItersData *iters_data = (CountItersData *) data; + QCryptoHashAlgorithm hash = iters_data->hash; + const uint8_t *key = iters_data->key; + size_t nkey = iters_data->nkey; + const uint8_t *salt = iters_data->salt; + size_t nsalt = iters_data->nsalt; + size_t nout = iters_data->nout; + Error **errp = iters_data->errp; + uint64_t ret = -1; g_autofree uint8_t *out = g_new(uint8_t, nout); uint64_t iterations = (1 << 15); @@ -114,7 +131,10 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, delta_ms = end_ms - start_ms; - if (delta_ms > 500) { + if (delta_ms == 0) { /* sanity check */ + error_setg(errp, "Unable to get accurate CPU usage"); + goto cleanup; + } else if (delta_ms > 500) { break; } else if (delta_ms < 100) { iterations = iterations * 10; @@ -129,5 +149,24 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, cleanup: memset(out, 0, nout); - return ret; + iters_data->iterations = ret; + return NULL; +} + +uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + size_t nout, + Error **errp) +{ + CountItersData data = { + hash, key, nkey, salt, nsalt, nout, 0, errp + }; + QemuThread thread; + + qemu_thread_create(&thread, "pbkdf2", threaded_qcrypto_pbkdf2_count_iters, + &data, QEMU_THREAD_JOINABLE); + qemu_thread_join(&thread); + + return data.iterations; } From 0e8f3eb43ff4fa7d2ef7a79c943e412b812987f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Thu, 29 Aug 2024 14:47:42 +0100 Subject: [PATCH 2838/2884] crypto: check gnutls & gcrypt support the requested pbkdf hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both gnutls and gcrypt can be configured to exclude support for certain algorithms via a runtime check against system crypto policies. Thus it is not sufficient to have a compile time test for hash support in their pbkdf implementations. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (cherry picked from commit e6c09ea4f9e5f8af92a6453642b84b9efd52892f) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- crypto/pbkdf-gcrypt.c | 2 +- crypto/pbkdf-gnutls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/pbkdf-gcrypt.c b/crypto/pbkdf-gcrypt.c index a8d8e64f4d..bc0719c831 100644 --- a/crypto/pbkdf-gcrypt.c +++ b/crypto/pbkdf-gcrypt.c @@ -33,7 +33,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) case QCRYPTO_HASH_ALG_SHA384: case QCRYPTO_HASH_ALG_SHA512: case QCRYPTO_HASH_ALG_RIPEMD160: - return true; + return qcrypto_hash_supports(hash); default: return false; } diff --git a/crypto/pbkdf-gnutls.c b/crypto/pbkdf-gnutls.c index 2dfbbd382c..911b565bea 100644 --- a/crypto/pbkdf-gnutls.c +++ b/crypto/pbkdf-gnutls.c @@ -33,7 +33,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) case QCRYPTO_HASH_ALG_SHA384: case QCRYPTO_HASH_ALG_SHA512: case QCRYPTO_HASH_ALG_RIPEMD160: - return true; + return qcrypto_hash_supports(hash); default: return false; } From 3148a16b306485c5b6fb30c06f369b4bba476030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Mon, 10 Jun 2024 10:26:18 +0100 Subject: [PATCH 2839/2884] crypto: avoid leak of ctx when bad cipher mode is given MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: Coverity CID 1546884 Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> (cherry picked from commit 586ac2c67d707c2588766c5195d94fa553cc25af) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- crypto/cipher-nettle.c.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 42b39e18a2..766de036ba 100644 --- a/crypto/cipher-nettle.c.inc +++ b/crypto/cipher-nettle.c.inc @@ -734,16 +734,19 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, #ifdef CONFIG_CRYPTO_SM4 case QCRYPTO_CIPHER_ALG_SM4: { - QCryptoNettleSm4 *ctx = g_new0(QCryptoNettleSm4, 1); + QCryptoNettleSm4 *ctx; + const QCryptoCipherDriver *drv; switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->base.driver = &qcrypto_nettle_sm4_driver_ecb; + drv = &qcrypto_nettle_sm4_driver_ecb; break; default: goto bad_cipher_mode; } + ctx = g_new0(QCryptoNettleSm4, 1); + ctx->base.driver = drv; sm4_set_encrypt_key(&ctx->key[0], key); sm4_set_decrypt_key(&ctx->key[1], key); From a160fa0fc3bad5c7fca4b8a332a799b5b9222a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Tue, 10 Sep 2024 18:38:51 +0100 Subject: [PATCH 2840/2884] tests/docker: remove debian-armel-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As debian-11 transitions to LTS we are starting to have problems building the image. While we could update to a later Debian building a 32 bit QEMU without modern floating point is niche host amongst the few remaining 32 bit hosts we regularly build for. For now we still have armhf-debian-cross-container which is currently built from the more recent debian-12. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240910173900.4154726-2-alex.bennee@linaro.org> (cherry picked from commit d0068b746a0a8cd4bb148527a0d199b130cd5288) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- .gitlab-ci.d/container-cross.yml | 6 - .gitlab-ci.d/crossbuilds.yml | 7 - .../dockerfiles/debian-armel-cross.docker | 179 ------------------ tests/lcitool/refresh | 6 - 4 files changed, 198 deletions(-) delete mode 100644 tests/docker/dockerfiles/debian-armel-cross.docker diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index e3103940a0..9a3ebd885e 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -22,12 +22,6 @@ arm64-debian-cross-container: variables: NAME: debian-arm64-cross -armel-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-armel-cross - armhf-debian-cross-container: extends: .container_job_template stage: containers diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index cb499e4ee0..459273f9da 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -1,13 +1,6 @@ include: - local: '/.gitlab-ci.d/crossbuild-template.yml' -cross-armel-user: - extends: .cross_user_build_job - needs: - job: armel-debian-cross-container - variables: - IMAGE: debian-armel-cross - cross-armhf-user: extends: .cross_user_build_job needs: diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker deleted file mode 100644 index 8476fc8cce..0000000000 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ /dev/null @@ -1,179 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all --cross-arch armv6l debian-11 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bison \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - flex \ - gcc \ - gcovr \ - gettext \ - git \ - hostname \ - libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - mtools \ - ncat \ - ninja-build \ - openssh-client \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - socat \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - xorriso \ - zstd && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales && \ - rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED - -RUN /usr/bin/pip3 install tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" - -RUN export DEBIAN_FRONTEND=noninteractive && \ - dpkg --add-architecture armel && \ - eatmydata apt-get update && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-arm-linux-gnueabi \ - libaio-dev:armel \ - libasan6:armel \ - libasound2-dev:armel \ - libattr1-dev:armel \ - libbpf-dev:armel \ - libbrlapi-dev:armel \ - libbz2-dev:armel \ - libc6-dev:armel \ - libcacard-dev:armel \ - libcap-ng-dev:armel \ - libcapstone-dev:armel \ - libcmocka-dev:armel \ - libcurl4-gnutls-dev:armel \ - libdaxctl-dev:armel \ - libdrm-dev:armel \ - libepoxy-dev:armel \ - libfdt-dev:armel \ - libffi-dev:armel \ - libfuse3-dev:armel \ - libgbm-dev:armel \ - libgcrypt20-dev:armel \ - libglib2.0-dev:armel \ - libglusterfs-dev:armel \ - libgnutls28-dev:armel \ - libgtk-3-dev:armel \ - libibverbs-dev:armel \ - libiscsi-dev:armel \ - libjemalloc-dev:armel \ - libjpeg62-turbo-dev:armel \ - libjson-c-dev:armel \ - liblttng-ust-dev:armel \ - liblzo2-dev:armel \ - libncursesw5-dev:armel \ - libnfs-dev:armel \ - libnuma-dev:armel \ - libpam0g-dev:armel \ - libpipewire-0.3-dev:armel \ - libpixman-1-dev:armel \ - libpng-dev:armel \ - libpulse-dev:armel \ - librbd-dev:armel \ - librdmacm-dev:armel \ - libsasl2-dev:armel \ - libsdl2-dev:armel \ - libsdl2-image-dev:armel \ - libseccomp-dev:armel \ - libselinux1-dev:armel \ - libslirp-dev:armel \ - libsnappy-dev:armel \ - libspice-server-dev:armel \ - libssh-gcrypt-dev:armel \ - libsystemd-dev:armel \ - libtasn1-6-dev:armel \ - libubsan1:armel \ - libudev-dev:armel \ - liburing-dev:armel \ - libusb-1.0-0-dev:armel \ - libusbredirhost-dev:armel \ - libvdeplug-dev:armel \ - libvirglrenderer-dev:armel \ - libvte-2.91-dev:armel \ - libzstd-dev:armel \ - nettle-dev:armel \ - systemtap-sdt-dev:armel \ - zlib1g-dev:armel && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - mkdir -p /usr/local/share/meson/cross && \ - printf "[binaries]\n\ -c = '/usr/bin/arm-linux-gnueabi-gcc'\n\ -ar = '/usr/bin/arm-linux-gnueabi-gcc-ar'\n\ -strip = '/usr/bin/arm-linux-gnueabi-strip'\n\ -pkgconfig = '/usr/bin/arm-linux-gnueabi-pkg-config'\n\ -\n\ -[host_machine]\n\ -system = 'linux'\n\ -cpu_family = 'arm'\n\ -cpu = 'arm'\n\ -endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabi && \ - dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ - mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-gcc - -ENV ABI "arm-linux-gnueabi" -ENV MESON_OPTS "--cross-file=arm-linux-gnueabi" -ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabi- -ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user,armeb-linux-user -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index ac803e34f1..199d5fad87 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -154,12 +154,6 @@ try: trailer=cross_build("aarch64-linux-gnu-", "aarch64-softmmu,aarch64-linux-user")) - # migration to bookworm stalled: https://lists.debian.org/debian-arm/2023/09/msg00006.html - generate_dockerfile("debian-armel-cross", "debian-11", - cross="armv6l", - trailer=cross_build("arm-linux-gnueabi-", - "arm-softmmu,arm-linux-user,armeb-linux-user")) - generate_dockerfile("debian-armhf-cross", "debian-12", cross="armv7l", trailer=cross_build("arm-linux-gnueabihf-", From 27a15a2a32a1ce3aec30d6ed181d0de7a0e271c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Tue, 10 Sep 2024 18:38:52 +0100 Subject: [PATCH 2841/2884] tests/docker: update debian i686 and mipsel images to bookworm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whatever issues there were which stopped these being updates when the rest were have now been resolved. However mips64el continues to be broken so don't update it here. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240910173900.4154726-3-alex.bennee@linaro.org> (cherry picked from commit 19d2111059c87d3f58349f27b9be9dee81fc1681) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- tests/docker/dockerfiles/debian-i686-cross.docker | 10 ++++------ tests/docker/dockerfiles/debian-mipsel-cross.docker | 10 ++++------ tests/lcitool/refresh | 4 ++-- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 3fe8ee623d..2328ee1732 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch i686 debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch i686 debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,16 +48,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ @@ -69,8 +68,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -145,6 +142,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvdeplug-dev:i386 \ libvirglrenderer-dev:i386 \ libvte-2.91-dev:i386 \ + libxdp-dev:i386 \ libzstd-dev:i386 \ nettle-dev:i386 \ systemtap-sdt-dev:i386 \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 0d559ae4ba..4ac314e22e 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mipsel debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mipsel debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,16 +48,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ @@ -69,8 +68,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -143,6 +140,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvdeplug-dev:mipsel \ libvirglrenderer-dev:mipsel \ libvte-2.91-dev:mipsel \ + libxdp-dev:mipsel \ libzstd-dev:mipsel \ nettle-dev:mipsel \ systemtap-sdt-dev:mipsel \ diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 199d5fad87..c60490a7fa 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -159,7 +159,7 @@ try: trailer=cross_build("arm-linux-gnueabihf-", "arm-softmmu,arm-linux-user")) - generate_dockerfile("debian-i686-cross", "debian-11", + generate_dockerfile("debian-i686-cross", "debian-12", cross="i686", trailer=cross_build("i686-linux-gnu-", "x86_64-softmmu," @@ -171,7 +171,7 @@ try: trailer=cross_build("mips64el-linux-gnuabi64-", "mips64el-softmmu,mips64el-linux-user")) - generate_dockerfile("debian-mipsel-cross", "debian-11", + generate_dockerfile("debian-mipsel-cross", "debian-12", cross="mipsel", trailer=cross_build("mipsel-linux-gnu-", "mipsel-softmmu,mipsel-linux-user")) From cd320c8a82773b8219e58d5e949db419f51b00f3 Mon Sep 17 00:00:00 2001 From: Thomas Huth <thuth@redhat.com> Date: Mon, 2 Sep 2024 17:47:49 +0200 Subject: [PATCH 2842/2884] contrib/plugins/Makefile: Add a 'distclean' target Running "make distclean" in the build tree currently fails since this tries to run the "distclean" target in the contrib/plugins/ folder, too, but the Makefile there is missing this target. Thus add 'distclean' there to fix this issue. And to avoid regressions with "make distclean", add this command to one of the build jobs, too. Message-ID: <20240902154749.73876-1-thuth@redhat.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> (cherry picked from commit 1231bc7d12c373e445171dda9e7e5146eee7da55) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- .gitlab-ci.d/buildtest.yml | 2 ++ contrib/plugins/Makefile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index aa32782405..0c624813cf 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -345,6 +345,8 @@ build-tcg-disabled: 124 132 139 142 144 145 151 152 155 157 165 194 196 200 202 208 209 216 218 227 234 246 247 248 250 254 255 257 258 260 261 262 263 264 270 272 273 277 279 image-fleecing + - cd ../.. + - make distclean build-user: extends: .native_build_job_template diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index edf256cd9d..05a2a45c5c 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -77,7 +77,7 @@ lib%$(SO_SUFFIX): %.o endif -clean: +clean distclean: rm -f *.o *$(SO_SUFFIX) *.d rm -Rf .libs From df9aa3dd8c05e1ee58cf38ecf3e2bd5994dbc9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= <vr_qemu@t-online.de> Date: Sun, 1 Sep 2024 15:01:12 +0200 Subject: [PATCH 2843/2884] hw/audio/virtio-sound: fix heap buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the guest may write to the device configuration space, whereas the virtio sound device specification in chapter 5.14.4 clearly states that the fields in the device configuration space are driver-read-only. Remove the set_config function from the virtio_snd class. This also prevents a heap buffer overflow. See QEMU issue #2296. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2296 Signed-off-by: Volker Rümelin <vr_qemu@t-online.de> Message-Id: <20240901130112.8242-1-vr_qemu@t-online.de> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> (cherry picked from commit 7fc6611cad3e9627b23ce83e550b668abba6c886) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/audio/trace-events | 1 - hw/audio/virtio-snd.c | 24 ------------------------ 2 files changed, 25 deletions(-) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index b1870ff224..b8ef572767 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -41,7 +41,6 @@ asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)" #virtio-snd.c virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32"" -virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32 virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64 virtio_snd_vm_state_running(void) "vm state running" virtio_snd_vm_state_stopped(void) "vm state stopped" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index d1cf5eb445..69838181dd 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -107,29 +107,6 @@ virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) } -static void -virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIOSound *s = VIRTIO_SND(vdev); - const virtio_snd_config *sndconfig = - (const virtio_snd_config *)config; - - - trace_virtio_snd_set_config(vdev, - s->snd_conf.jacks, - sndconfig->jacks, - s->snd_conf.streams, - sndconfig->streams, - s->snd_conf.chmaps, - sndconfig->chmaps); - - memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); - le32_to_cpus(&s->snd_conf.jacks); - le32_to_cpus(&s->snd_conf.streams); - le32_to_cpus(&s->snd_conf.chmaps); - -} - static void virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer) { @@ -1400,7 +1377,6 @@ static void virtio_snd_class_init(ObjectClass *klass, void *data) vdc->realize = virtio_snd_realize; vdc->unrealize = virtio_snd_unrealize; vdc->get_config = virtio_snd_get_config; - vdc->set_config = virtio_snd_set_config; vdc->get_features = get_features; vdc->reset = virtio_snd_reset; vdc->legacy_features = 0; From bec9a96934539cf4d808cc328aa9c6fa9d36274d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= <jan.kloetzke@kernkonzept.com> Date: Fri, 13 Sep 2024 15:31:50 +0100 Subject: [PATCH 2844/2884] hw/intc/arm_gic: fix spurious level triggered interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On GICv2 and later, level triggered interrupts are pending when either the interrupt line is asserted or the interrupt was made pending by a GICD_ISPENDRn write. Making a level triggered interrupt pending by software persists until either the interrupt is acknowledged or cleared by writing GICD_ICPENDRn. As long as the interrupt line is asserted, the interrupt is pending in any case. This logic is transparently implemented in gic_test_pending() for GICv1 and GICv2. The function combines the "pending" irq_state flag (used for edge triggered interrupts and software requests) and the line status (tracked in the "level" field). However, we also incorrectly set the pending flag on a guest write to GICD_ISENABLERn if the line of a level triggered interrupt was asserted. This keeps the interrupt pending even if the line is de-asserted after some time. This incorrect logic is a leftover of the initial 11MPCore GIC implementation. That handles things slightly differently to the architected GICv1 and GICv2. The 11MPCore TRM does not give a lot of detail on the corner cases of its GIC's behaviour, and historically we have not wanted to investigate exactly what it does in reality, so QEMU's GIC model takes the approach of "retain our existing behaviour for 11MPCore, and implement the architectural standard for later GIC revisions". On that basis, commit 8d999995e45c10 in 2013 is where we added the "level-triggered interrupt with the line asserted" handling to gic_test_pending(), and we deliberately kept the old behaviour of gic_test_pending() for REV_11MPCORE. That commit should have added the "only if 11MPCore" condition to the setting of the pending bit on writes to GICD_ISENABLERn, but forgot it. Add the missing "if REV_11MPCORE" condition, so that our behaviour on GICv1 and GICv2 matches the GIC architecture requirements. Cc: qemu-stable@nongnu.org Fixes: 8d999995e45c10 ("arm_gic: Fix GIC pending behavior") Signed-off-by: Jan Klötzke <jan.kloetzke@kernkonzept.com> Message-id: 20240911114826.3558302-1-jan.kloetzke@kernkonzept.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: expanded comment a little and converted to coding-style form; expanded commit message with the historical backstory] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit 110684c9a69a02cbabfbddcd3afa921826ad565c) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/intc/arm_gic.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 806832439b..2a48f0da2f 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -1263,9 +1263,14 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, trace_gic_enable_irq(irq + i); } GIC_DIST_SET_ENABLED(irq + i, cm); - /* If a raised level triggered IRQ enabled then mark - is as pending. */ - if (GIC_DIST_TEST_LEVEL(irq + i, mask) + /* + * If a raised level triggered IRQ enabled then mark + * it as pending on 11MPCore. For other GIC revisions we + * handle the "level triggered and line asserted" check + * at the other end in gic_test_pending(). + */ + if (s->revision == REV_11MPCORE + && GIC_DIST_TEST_LEVEL(irq + i, mask) && !GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) { DPRINTF("Set %d pending mask %x\n", irq + i, mask); GIC_DIST_SET_PENDING(irq + i, mask); From b95002f47a4da76f98f62e7dcc8e5eed9a83436a Mon Sep 17 00:00:00 2001 From: Gert Wollny <gert.wollny@collabora.com> Date: Wed, 11 Sep 2024 09:14:30 +0000 Subject: [PATCH 2845/2884] ui/sdl2: set swap interval explicitly when OpenGL is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before 176e3783f2ab (ui/sdl2: OpenGL window context) SDL_CreateRenderer was called unconditionally setting the swap interval to 0. Since SDL_CreateRenderer is now no longer called when OpenGL is enabled, the swap interval is no longer set explicitly and vsync handling depends on the environment settings which may lead to a performance regression with virgl as reported in https://gitlab.com/qemu-project/qemu/-/issues/2565 Restore the old vsync handling by explicitly calling SDL_GL_SetSwapInterval if OpenGL is enabled. Fixes: 176e3783f2ab (ui/sdl2: OpenGL window context) Closes: https://gitlab.com/qemu-project/qemu/-/issues/2565 Signed-off-by: Gert Wollny <gert.wollny@collabora.com> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <01020191e05ce6df-84da6386-62c2-4ce8-840e-ad216ac253dd-000000@eu-west-1.amazonses.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> (cherry picked from commit ae23cd00170baaa2777eb1ee87b70f472dbb3c44) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- ui/sdl2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/sdl2.c b/ui/sdl2.c index 98ed974371..51299f3645 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -115,6 +115,7 @@ void sdl2_window_create(struct sdl2_console *scon) SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); scon->winctx = SDL_GL_CreateContext(scon->real_window); + SDL_GL_SetSwapInterval(0); } else { /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); From 02833b07b6718e0c7c10e8cd600d917a866da5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com> Date: Fri, 6 Sep 2024 15:09:58 +0100 Subject: [PATCH 2846/2884] gitlab: fix logic for changing docker tag on stable branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: commit e28112d00703abd136e2411d23931f4f891c9244 Author: Daniel P. Berrangé <berrange@redhat.com> Date: Thu Jun 8 17:40:16 2023 +0100 gitlab: stable staging branches publish containers in a separate tag Due to a copy+paste mistake, that commit included "QEMU_JOB_SKIPPED" in the final rule that was meant to be a 'catch all' for staging branches. As a result stable branches are still splattering dockers from the primary development branch. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Tested-by: Michael Tokarev <mjt@tls.msk.ru> Message-ID: <20240906140958.84755-1-berrange@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> (cherry picked from commit 8d5ab746b1e6668ffb0378820b25665b385c8573) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- .gitlab-ci.d/base.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/base.yml b/.gitlab-ci.d/base.yml index bf3d8efab6..25b88aaa06 100644 --- a/.gitlab-ci.d/base.yml +++ b/.gitlab-ci.d/base.yml @@ -128,7 +128,7 @@ variables: when: manual # Jobs can run if any jobs they depend on were successful - - if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' when: on_success variables: QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG From 0d889c5c86d0c5acbcd218c3dc5130c170f74361 Mon Sep 17 00:00:00 2001 From: Mattias Nissler <mnissler@rivosinc.com> Date: Mon, 19 Aug 2024 06:54:54 -0700 Subject: [PATCH 2847/2884] softmmu: Support concurrent bounce buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When DMA memory can't be directly accessed, as is the case when running the device model in a separate process without shareable DMA file descriptors, bounce buffering is used. It is not uncommon for device models to request mapping of several DMA regions at the same time. Examples include: * net devices, e.g. when transmitting a packet that is split across several TX descriptors (observed with igb) * USB host controllers, when handling a packet with multiple data TRBs (observed with xhci) Previously, qemu only provided a single bounce buffer per AddressSpace and would fail DMA map requests while the buffer was already in use. In turn, this would cause DMA failures that ultimately manifest as hardware errors from the guest perspective. This change allocates DMA bounce buffers dynamically instead of supporting only a single buffer. Thus, multiple DMA mappings work correctly also when RAM can't be mmap()-ed. The total bounce buffer allocation size is limited individually for each AddressSpace. The default limit is 4096 bytes, matching the previous maximum buffer size. A new x-max-bounce-buffer-size parameter is provided to configure the limit for PCI devices. Signed-off-by: Mattias Nissler <mnissler@rivosinc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20240819135455.2957406-1-mnissler@rivosinc.com Signed-off-by: Peter Xu <peterx@redhat.com> (cherry picked from commit 637b0aa139565cb82a7b9269e62214f87082635c) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/pci/pci.c | 8 ++++ include/exec/memory.h | 14 +++---- include/hw/pci/pci_device.h | 3 ++ system/memory.c | 5 ++- system/physmem.c | 80 ++++++++++++++++++++++++++----------- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index fab86d0567..d2caf3ee8b 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -85,6 +85,8 @@ static Property pci_props[] = { QEMU_PCIE_ERR_UNC_MASK_BITNR, true), DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present, QEMU_PCIE_ARI_NEXTFN_1_BITNR, false), + DEFINE_PROP_SIZE32("x-max-bounce-buffer-size", PCIDevice, + max_bounce_buffer_size, DEFAULT_MAX_BOUNCE_BUFFER_SIZE), DEFINE_PROP_END_OF_LIST() }; @@ -1204,6 +1206,8 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, "bus master container", UINT64_MAX); address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_container_region, pci_dev->name); + pci_dev->bus_master_as.max_bounce_buffer_size = + pci_dev->max_bounce_buffer_size; if (phase_check(PHASE_MACHINE_READY)) { pci_init_bus_master(pci_dev); @@ -2633,6 +2637,10 @@ static void pci_device_class_init(ObjectClass *klass, void *data) k->unrealize = pci_qdev_unrealize; k->bus_type = TYPE_PCI_BUS; device_class_set_props(k, pci_props); + object_class_property_set_description( + klass, "x-max-bounce-buffer-size", + "Maximum buffer size allocated for bounce buffers used for mapped " + "access to indirect DMA memory"); } static void pci_device_class_base_init(ObjectClass *klass, void *data) diff --git a/include/exec/memory.h b/include/exec/memory.h index 296fd068c0..e5e865d1a9 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1084,13 +1084,7 @@ typedef struct AddressSpaceMapClient { QLIST_ENTRY(AddressSpaceMapClient) link; } AddressSpaceMapClient; -typedef struct { - MemoryRegion *mr; - void *buffer; - hwaddr addr; - hwaddr len; - bool in_use; -} BounceBuffer; +#define DEFAULT_MAX_BOUNCE_BUFFER_SIZE (4096) /** * struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects @@ -1110,8 +1104,10 @@ struct AddressSpace { QTAILQ_HEAD(, MemoryListener) listeners; QTAILQ_ENTRY(AddressSpace) address_spaces_link; - /* Bounce buffer to use for this address space. */ - BounceBuffer bounce; + /* Maximum DMA bounce buffer size used for indirect memory map requests */ + size_t max_bounce_buffer_size; + /* Total size of bounce buffers currently allocated, atomically accessed */ + size_t bounce_buffer_size; /* List of callbacks to invoke when buffers free up */ QemuMutex map_client_list_lock; QLIST_HEAD(, AddressSpaceMapClient) map_client_list; diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index 15694f2489..91df40f989 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -167,6 +167,9 @@ struct PCIDevice { /* ID of standby device in net_failover pair */ char *failover_pair_id; uint32_t acpi_index; + + /* Maximum DMA bounce buffer size used for indirect memory map requests */ + uint32_t max_bounce_buffer_size; }; static inline int pci_intx(PCIDevice *pci_dev) diff --git a/system/memory.c b/system/memory.c index 5e6eb459d5..f6f6fee6d8 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3148,7 +3148,8 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) as->ioeventfds = NULL; QTAILQ_INIT(&as->listeners); QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link); - as->bounce.in_use = false; + as->max_bounce_buffer_size = DEFAULT_MAX_BOUNCE_BUFFER_SIZE; + as->bounce_buffer_size = 0; qemu_mutex_init(&as->map_client_list_lock); QLIST_INIT(&as->map_client_list); as->name = g_strdup(name ? name : "anonymous"); @@ -3158,7 +3159,7 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) static void do_address_space_destroy(AddressSpace *as) { - assert(!qatomic_read(&as->bounce.in_use)); + assert(qatomic_read(&as->bounce_buffer_size) == 0); assert(QLIST_EMPTY(&as->map_client_list)); qemu_mutex_destroy(&as->map_client_list_lock); diff --git a/system/physmem.c b/system/physmem.c index 94600a33ec..971bfa0855 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3095,6 +3095,20 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len) NULL, len, FLUSH_CACHE); } +/* + * A magic value stored in the first 8 bytes of the bounce buffer struct. Used + * to detect illegal pointers passed to address_space_unmap. + */ +#define BOUNCE_BUFFER_MAGIC 0xb4017ceb4ffe12ed + +typedef struct { + uint64_t magic; + MemoryRegion *mr; + hwaddr addr; + size_t len; + uint8_t buffer[]; +} BounceBuffer; + static void address_space_unregister_map_client_do(AddressSpaceMapClient *client) { @@ -3120,9 +3134,9 @@ void address_space_register_map_client(AddressSpace *as, QEMUBH *bh) QEMU_LOCK_GUARD(&as->map_client_list_lock); client->bh = bh; QLIST_INSERT_HEAD(&as->map_client_list, client, link); - /* Write map_client_list before reading in_use. */ + /* Write map_client_list before reading bounce_buffer_size. */ smp_mb(); - if (!qatomic_read(&as->bounce.in_use)) { + if (qatomic_read(&as->bounce_buffer_size) < as->max_bounce_buffer_size) { address_space_notify_map_clients_locked(as); } } @@ -3251,28 +3265,40 @@ void *address_space_map(AddressSpace *as, mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { - if (qatomic_xchg(&as->bounce.in_use, true)) { + size_t used = qatomic_read(&as->bounce_buffer_size); + for (;;) { + hwaddr alloc = MIN(as->max_bounce_buffer_size - used, l); + size_t new_size = used + alloc; + size_t actual = + qatomic_cmpxchg(&as->bounce_buffer_size, used, new_size); + if (actual == used) { + l = alloc; + break; + } + used = actual; + } + + if (l == 0) { *plen = 0; return NULL; } - /* Avoid unbounded allocations */ - l = MIN(l, TARGET_PAGE_SIZE); - as->bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l); - as->bounce.addr = addr; - as->bounce.len = l; + BounceBuffer *bounce = g_malloc0(l + sizeof(BounceBuffer)); + bounce->magic = BOUNCE_BUFFER_MAGIC; memory_region_ref(mr); - as->bounce.mr = mr; + bounce->mr = mr; + bounce->addr = addr; + bounce->len = l; + if (!is_write) { flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED, - as->bounce.buffer, l); + bounce->buffer, l); } *plen = l; - return as->bounce.buffer; + return bounce->buffer; } - memory_region_ref(mr); *plen = flatview_extend_translation(fv, addr, len, mr, xlat, l, is_write, attrs); @@ -3287,12 +3313,11 @@ void *address_space_map(AddressSpace *as, void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, bool is_write, hwaddr access_len) { - if (buffer != as->bounce.buffer) { - MemoryRegion *mr; - ram_addr_t addr1; + MemoryRegion *mr; + ram_addr_t addr1; - mr = memory_region_from_host(buffer, &addr1); - assert(mr != NULL); + mr = memory_region_from_host(buffer, &addr1); + if (mr != NULL) { if (is_write) { invalidate_and_set_dirty(mr, addr1, access_len); } @@ -3302,15 +3327,22 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, memory_region_unref(mr); return; } + + + BounceBuffer *bounce = container_of(buffer, BounceBuffer, buffer); + assert(bounce->magic == BOUNCE_BUFFER_MAGIC); + if (is_write) { - address_space_write(as, as->bounce.addr, MEMTXATTRS_UNSPECIFIED, - as->bounce.buffer, access_len); + address_space_write(as, bounce->addr, MEMTXATTRS_UNSPECIFIED, + bounce->buffer, access_len); } - qemu_vfree(as->bounce.buffer); - as->bounce.buffer = NULL; - memory_region_unref(as->bounce.mr); - /* Clear in_use before reading map_client_list. */ - qatomic_set_mb(&as->bounce.in_use, false); + + qatomic_sub(&as->bounce_buffer_size, bounce->len); + bounce->magic = ~BOUNCE_BUFFER_MAGIC; + memory_region_unref(bounce->mr); + g_free(bounce); + /* Write bounce_buffer_size before reading map_client_list. */ + smp_mb(); address_space_notify_map_clients(as); } From 659eeb16b35839a0ea683a82a3896e7344d12319 Mon Sep 17 00:00:00 2001 From: David Hildenbrand <david@redhat.com> Date: Wed, 28 Aug 2024 11:07:43 +0200 Subject: [PATCH 2848/2884] softmmu/physmem: fix memory leak in dirty_memory_extend() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As reported by Peter, we might be leaking memory when removing the highest RAMBlock (in the weird ram_addr_t space), and adding a new one. We will fail to realize that we already allocated bitmaps for more dirty memory blocks, and effectively discard the pointers to them. Fix it by getting rid of last_ram_page() and by remembering the number of dirty memory blocks that have been allocated already. While at it, let's use "unsigned int" for the number of blocks, which should be sufficient until we reach ~32 exabytes. Looks like this leak was introduced as we switched from using a single bitmap_zero_extend() to allocating multiple bitmaps: bitmap_zero_extend() relies on g_renew() which should have taken care of this. Resolves: https://lkml.kernel.org/r/CAFEAcA-k7a+VObGAfCFNygQNfCKL=AfX6A4kScq=VSSK0peqPg@mail.gmail.com Reported-by: Peter Maydell <peter.maydell@linaro.org> Fixes: 5b82b703b69a ("memory: RCU ram_list.dirty_memory[] for safe RAM hotplug") Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Tested-by: Peter Maydell <peter.maydell@linaro.org> Cc: qemu-stable@nongnu.org Cc: Stefan Hajnoczi <stefanha@redhat.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Peter Xu <peterx@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: David Hildenbrand <david@redhat.com> Link: https://lore.kernel.org/r/20240828090743.128647-1-david@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> (cherry picked from commit b84f06c2bee727b3870b4eeccbe3a45c5aea14c1) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- include/exec/ramlist.h | 1 + system/physmem.c | 35 +++++++++-------------------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index 2ad2a81acc..d9cfe530be 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -50,6 +50,7 @@ typedef struct RAMList { /* RCU-enabled, writes protected by the ramlist lock. */ QLIST_HEAD(, RAMBlock) blocks; DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM]; + unsigned int num_dirty_blocks; uint32_t version; QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; } RAMList; diff --git a/system/physmem.c b/system/physmem.c index 971bfa0855..d71a2b1bbd 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1534,18 +1534,6 @@ static ram_addr_t find_ram_offset(ram_addr_t size) return offset; } -static unsigned long last_ram_page(void) -{ - RAMBlock *block; - ram_addr_t last = 0; - - RCU_READ_LOCK_GUARD(); - RAMBLOCK_FOREACH(block) { - last = MAX(last, block->offset + block->max_length); - } - return last >> TARGET_PAGE_BITS; -} - static void qemu_ram_setup_dump(void *addr, ram_addr_t size) { int ret; @@ -1799,13 +1787,11 @@ void qemu_ram_msync(RAMBlock *block, ram_addr_t start, ram_addr_t length) } /* Called with ram_list.mutex held */ -static void dirty_memory_extend(ram_addr_t old_ram_size, - ram_addr_t new_ram_size) +static void dirty_memory_extend(ram_addr_t new_ram_size) { - ram_addr_t old_num_blocks = DIV_ROUND_UP(old_ram_size, - DIRTY_MEMORY_BLOCK_SIZE); - ram_addr_t new_num_blocks = DIV_ROUND_UP(new_ram_size, - DIRTY_MEMORY_BLOCK_SIZE); + unsigned int old_num_blocks = ram_list.num_dirty_blocks; + unsigned int new_num_blocks = DIV_ROUND_UP(new_ram_size, + DIRTY_MEMORY_BLOCK_SIZE); int i; /* Only need to extend if block count increased */ @@ -1837,6 +1823,8 @@ static void dirty_memory_extend(ram_addr_t old_ram_size, g_free_rcu(old_blocks, rcu); } } + + ram_list.num_dirty_blocks = new_num_blocks; } static void ram_block_add(RAMBlock *new_block, Error **errp) @@ -1846,11 +1834,9 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) RAMBlock *block; RAMBlock *last_block = NULL; bool free_on_error = false; - ram_addr_t old_ram_size, new_ram_size; + ram_addr_t ram_size; Error *err = NULL; - old_ram_size = last_ram_page(); - qemu_mutex_lock_ramlist(); new_block->offset = find_ram_offset(new_block->max_length); @@ -1901,11 +1887,8 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) } } - new_ram_size = MAX(old_ram_size, - (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS); - if (new_ram_size > old_ram_size) { - dirty_memory_extend(old_ram_size, new_ram_size); - } + ram_size = (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS; + dirty_memory_extend(ram_size); /* Keep the list sorted from biggest to smallest block. Unlike QTAILQ, * QLIST (which has an RCU-friendly variant) does not have insertion at * tail, so save the last element in last_block. From 97fa3d7fccb1975a33caf011dfd83aba437608d9 Mon Sep 17 00:00:00 2001 From: "Fea.Wang" <fea.wang@sifive.com> Date: Thu, 12 Sep 2024 15:04:04 +0800 Subject: [PATCH 2849/2884] softmmu/physmem.c: Keep transaction attribute in address_space_map() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The follow-up transactions may use the data in the attribution, so keep the value of attribution from the function parameter just as flatview_translate() above. Signed-off-by: Fea.Wang <fea.wang@sifive.com> Cc: qemu-stable@nongnu.org Fixes: f26404fbee ("Make address_space_map() take a MemTxAttrs argument") Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Link: https://lore.kernel.org/r/20240912070404.2993976-2-fea.wang@sifive.com Signed-off-by: Peter Xu <peterx@redhat.com> (cherry picked from commit d8d5ca40048b04750de5a0ae0b2b9f153a391951) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- system/physmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index d71a2b1bbd..dc1db3a384 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3274,7 +3274,7 @@ void *address_space_map(AddressSpace *as, bounce->len = l; if (!is_write) { - flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED, + flatview_read(fv, addr, attrs, bounce->buffer, l); } From 73f5d5bfb7b1f53c830bdd41cc20aefe12ab4827 Mon Sep 17 00:00:00 2001 From: Mattias Nissler <mnissler@rivosinc.com> Date: Mon, 16 Sep 2024 10:57:08 -0700 Subject: [PATCH 2850/2884] mac_dbdma: Remove leftover `dma_memory_unmap` calls These were passing a NULL buffer pointer unconditionally, which happens to behave in a mostly benign way (except for the chance of an excess memory region unref and a bounce buffer leak). Per the function comment, this was never meant to be accepted though, and triggers an assertion with the "softmmu: Support concurrent bounce buffers" change. Given that the code in question never sets up any mappings, just remove the unnecessary dma_memory_unmap calls along with the DBDMA_io struct fields that are now entirely unused. Signed-off-by: Mattias Nissler <mnissler@rivosinc.com> Message-Id: <20240916175708.1829059-1-mnissler@rivosinc.com> Fixes: be1e343995 ("macio: switch over to new byte-aligned DMA helpers") Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> (cherry picked from commit 2d0a071e625d7234e8c5623b7e7bf445e1bef72c) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/ide/macio.c | 6 ------ include/hw/ppc/mac_dbdma.h | 4 ---- 2 files changed, 10 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index e84bf2c9f6..15dd40138e 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -119,9 +119,6 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) return; done: - dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len, - io->dir, io->dma_len); - if (ret < 0) { block_acct_failed(blk_get_stats(s->blk), &s->acct); } else { @@ -202,9 +199,6 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) return; done: - dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len, - io->dir, io->dma_len); - if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { if (ret < 0) { block_acct_failed(blk_get_stats(s->blk), &s->acct); diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 4a3f644516..c774f6bf84 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -44,10 +44,6 @@ struct DBDMA_io { DBDMA_end dma_end; /* DMA is in progress, don't start another one */ bool processing; - /* DMA request */ - void *dma_mem; - dma_addr_t dma_len; - DMADirection dir; }; /* From 9b42e33bda413faa9d649643548b72a68f203f53 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas <farosas@suse.de> Date: Tue, 17 Sep 2024 15:58:02 -0300 Subject: [PATCH 2851/2884] migration/multifd: Fix rb->receivedmap cleanup race Fix a segmentation fault in multifd when rb->receivedmap is cleared too early. After commit 5ef7e26bdb ("migration/multifd: solve zero page causing multiple page faults"), multifd started using the rb->receivedmap bitmap, which belongs to ram.c and is initialized and *freed* from the ram SaveVMHandlers. Multifd threads are live until migration_incoming_state_destroy(), which is called after qemu_loadvm_state_cleanup(), leading to a crash when accessing rb->receivedmap. process_incoming_migration_co() ... qemu_loadvm_state() multifd_nocomp_recv() qemu_loadvm_state_cleanup() ramblock_recv_bitmap_set_offset() rb->receivedmap = NULL set_bit_atomic(..., rb->receivedmap) ... migration_incoming_state_destroy() multifd_recv_cleanup() multifd_recv_terminate_threads(NULL) Move the loadvm cleanup into migration_incoming_state_destroy(), after multifd_recv_cleanup() to ensure multifd threads have already exited when rb->receivedmap is cleared. Adjust the postcopy listen thread comment to indicate that we still want to skip the cpu synchronization. CC: qemu-stable@nongnu.org Fixes: 5ef7e26bdb ("migration/multifd: solve zero page causing multiple page faults") Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20240917185802.15619-3-farosas@suse.de [peterx: added comment in migration_incoming_state_destroy()] Signed-off-by: Peter Xu <peterx@redhat.com> (cherry picked from commit 4ce56229087860805877075ddb29dd44578365a9) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- migration/migration.c | 5 +++++ migration/savevm.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 3dea06d577..ae2be31557 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -378,6 +378,11 @@ void migration_incoming_state_destroy(void) struct MigrationIncomingState *mis = migration_incoming_get_current(); multifd_recv_cleanup(); + /* + * RAM state cleanup needs to happen after multifd cleanup, because + * multifd threads can use some of its states (receivedmap). + */ + qemu_loadvm_state_cleanup(); if (mis->to_src_file) { /* Tell source that we are done */ diff --git a/migration/savevm.c b/migration/savevm.c index 6bb404b9c8..b71eba218f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2983,7 +2983,10 @@ int qemu_loadvm_state(QEMUFile *f) trace_qemu_loadvm_state_post_main(ret); if (mis->have_listen_thread) { - /* Listen thread still going, can't clean up yet */ + /* + * Postcopy listen thread still going, don't synchronize the + * cpus yet. + */ return ret; } @@ -3026,7 +3029,6 @@ int qemu_loadvm_state(QEMUFile *f) } } - qemu_loadvm_state_cleanup(); cpu_synchronize_all_post_init(); return ret; From 1faa437db9b5e9217648f1e8db8a03ef93e5aed1 Mon Sep 17 00:00:00 2001 From: Jacob Abrams <satur9nine@gmail.com> Date: Tue, 10 Sep 2024 21:32:55 -0700 Subject: [PATCH 2852/2884] hw/char/stm32l4x5_usart.c: Enable USART ACK bit response SW modifying USART_CR1 TE bit should cuase HW to respond by altering USART_ISR TEACK bit, and likewise for RE and REACK bit. This resolves some but not all issues necessary for the official STM USART HAL driver to function as is. Fixes: 87b77e6e01ca ("hw/char/stm32l4x5_usart: Enable serial read and write") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2540 Signed-off-by: Jacob Abrams <satur9nine@gmail.com> Message-id: 20240911043255.51966-1-satur9nine@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit 6cce0dcc6f7aaaeb7f17577776da510b04f67c99) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/char/stm32l4x5_usart.c | 16 +++++++++++++ tests/qtest/stm32l4x5_usart-test.c | 36 +++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index fc5dcac0c4..3cf200c080 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -154,6 +154,21 @@ REG32(RDR, 0x24) REG32(TDR, 0x28) FIELD(TDR, TDR, 0, 9) +static void stm32l4x5_update_isr(Stm32l4x5UsartBaseState *s) +{ + if (s->cr1 & R_CR1_TE_MASK) { + s->isr |= R_ISR_TEACK_MASK; + } else { + s->isr &= ~R_ISR_TEACK_MASK; + } + + if (s->cr1 & R_CR1_RE_MASK) { + s->isr |= R_ISR_REACK_MASK; + } else { + s->isr &= ~R_ISR_REACK_MASK; + } +} + static void stm32l4x5_update_irq(Stm32l4x5UsartBaseState *s) { if (((s->isr & R_ISR_WUF_MASK) && (s->cr3 & R_CR3_WUFIE_MASK)) || @@ -456,6 +471,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, case A_CR1: s->cr1 = value; stm32l4x5_update_params(s); + stm32l4x5_update_isr(s); stm32l4x5_update_irq(s); return; case A_CR2: diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c index 8902518233..0630f8d53b 100644 --- a/tests/qtest/stm32l4x5_usart-test.c +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -36,6 +36,8 @@ REG32(GTPR, 0x10) REG32(RTOR, 0x14) REG32(RQR, 0x18) REG32(ISR, 0x1C) + FIELD(ISR, REACK, 22, 1) + FIELD(ISR, TEACK, 21, 1) FIELD(ISR, TXE, 7, 1) FIELD(ISR, RXNE, 5, 1) FIELD(ISR, ORE, 3, 1) @@ -191,7 +193,7 @@ static void init_uart(QTestState *qts) /* Enable the transmitter, the receiver and the USART. */ qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), - R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); + cr1 | R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); } static void test_write_read(void) @@ -296,6 +298,37 @@ static void test_send_str(void) qtest_quit(qts); } +static void test_ack(void) +{ + uint32_t cr1; + uint32_t isr; + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + init_uart(qts); + + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + + /* Disable the transmitter and receiver. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 & ~(R_CR1_RE_MASK | R_CR1_TE_MASK)); + + /* Test ISR ACK for transmitter and receiver disabled */ + isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); + g_assert_false(isr & R_ISR_TEACK_MASK); + g_assert_false(isr & R_ISR_REACK_MASK); + + /* Enable the transmitter and receiver. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 | (R_CR1_RE_MASK | R_CR1_TE_MASK)); + + /* Test ISR ACK for transmitter and receiver disabled */ + isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); + g_assert_true(isr & R_ISR_TEACK_MASK); + g_assert_true(isr & R_ISR_REACK_MASK); + + qtest_quit(qts); +} + int main(int argc, char **argv) { int ret; @@ -308,6 +341,7 @@ int main(int argc, char **argv) qtest_add_func("stm32l4x5/usart/send_char", test_send_char); qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); qtest_add_func("stm32l4x5/usart/send_str", test_send_str); + qtest_add_func("stm32l4x5/usart/ack", test_ack); ret = g_test_run(); return ret; From 03ee5e0c532d24a689b59495a36111a960420723 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Tue, 17 Sep 2024 17:13:37 +0100 Subject: [PATCH 2853/2884] target/arm: Correct ID_AA64ISAR1_EL1 value for neoverse-v1 The Neoverse-V1 TRM is a bit confused about the layout of the ID_AA64ISAR1_EL1 register, and so its table 3-6 has the wrong value for this ID register. Trust instead section 3.2.74's list of which fields are set. This means that we stop incorrectly reporting FEAT_XS as present, and now report the presence of FEAT_BF16. Cc: qemu-stable@nongnu.org Reported-by: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20240917161337.3012188-1-peter.maydell@linaro.org (cherry picked from commit 8676007eff04bb4e454bcdf92fab3f855bcc59b3) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/arm/tcg/cpu64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index fe232eb306..8516daefca 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -685,7 +685,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x000001f210305519ull; cpu->isar.id_aa64dfr1 = 0x00000000; cpu->isar.id_aa64isar0 = 0x1011111110212120ull; /* with FEAT_RNG */ - cpu->isar.id_aa64isar1 = 0x0111000001211032ull; + cpu->isar.id_aa64isar1 = 0x0011100001211032ull; cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0220011102101011ull; From eb40b14740b9507c60805fccb723c3bd8903ab66 Mon Sep 17 00:00:00 2001 From: Helge Deller <deller@gmx.de> Date: Tue, 3 Sep 2024 18:21:28 +0200 Subject: [PATCH 2854/2884] target/hppa: Fix random 32-bit linux-user crashes The linux-user hppa target crashes randomly for me since commit 081a0ed188d8 ("target/hppa: Do not mask in copy_iaoq_entry"). That commit dropped the masking of the IAOQ addresses while copying them from other registers and instead keeps them with all 64 bits up until the full gva is formed with the help of hppa_form_gva_psw(). So, when running in linux-user mode on an emulated 64-bit CPU, we need to mask to a 32-bit address space at the very end in hppa_form_gva_psw() if the PSW-W flag isn't set (which is the case for linux-user on hppa). Fixes: 081a0ed188d8 ("target/hppa: Do not mask in copy_iaoq_entry") Cc: qemu-stable@nongnu.org # v9.1+ Signed-off-by: Helge Deller <deller@gmx.de> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> (cherry picked from commit d33d3adb573794903380e03e767e06470514cefe) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/hppa/cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 5478b183dc..43074d80bf 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -319,7 +319,7 @@ static inline target_ulong hppa_form_gva_psw(target_ulong psw, uint64_t spc, target_ulong off) { #ifdef CONFIG_USER_ONLY - return off; + return off & gva_offset_mask(psw); #else return spc | (off & gva_offset_mask(psw)); #endif From f84b79a8fcf46d73bc2345cf14ca863fa8e05ea6 Mon Sep 17 00:00:00 2001 From: Arman Nabiev <nabiev.arman13@gmail.com> Date: Thu, 22 Aug 2024 19:56:53 +0300 Subject: [PATCH 2855/2884] target/ppc: Fix migration of CPUs with TLB_EMB TLB type In vmstate_tlbemb a cut-and-paste error meant we gave this vmstate subsection the same "cpu/tlb6xx" name as the vmstate_tlb6xx subsection. This breaks migration load for any CPU using the TLB_EMB CPU type, because when we see the "tlb6xx" name in the incoming data we try to interpret it as a vmstate_tlb6xx subsection, which it isn't the right format for: $ qemu-system-ppc -drive if=none,format=qcow2,file=/home/petmay01/test-images/virt/dummy.qcow2 -monitor stdio -M bamboo QEMU 9.0.92 monitor - type 'help' for more information (qemu) savevm foo (qemu) loadvm foo Missing section footer for cpu Error: Error -22 while loading VM state Correct the incorrect vmstate section name. Since migration for these CPU types was completely broken before, we don't need to care that this is a migration compatibility break. This affects the PPC 405, 440, 460 and e200 CPU families. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2522 Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Arman Nabiev <nabiev.arman13@gmail.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> (cherry picked from commit 203beb6f047467a4abfc8267c234393cea3f471c) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/ppc/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 731dd8df35..d433fd45fc 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -621,7 +621,7 @@ static bool tlbemb_needed(void *opaque) } static const VMStateDescription vmstate_tlbemb = { - .name = "cpu/tlb6xx", + .name = "cpu/tlbemb", .version_id = 1, .minimum_version_id = 1, .needed = tlbemb_needed, From 9cd1fd4b5025df866eb0bd8a86230e83ae049882 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas <farosas@suse.de> Date: Wed, 28 Aug 2024 11:56:48 -0300 Subject: [PATCH 2856/2884] migration/multifd: Fix p->iov leak in multifd-uadk.c The send_cleanup() hook should free the p->iov that was allocated at send_setup(). This was missed because the UADK code is conditional on the presence of the accelerator, so it's not tested by default. Fixes: 819dd20636 ("migration/multifd: Add UADK initialization") Reported-by: Peter Xu <peterx@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> (cherry picked from commit 405e352d28c24991cacfdebccf67d56c4795cf6e) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- migration/multifd-uadk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index d12353fb21..9a582fc919 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -146,6 +146,8 @@ static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) multifd_uadk_uninit_sess(wd); p->compress_data = NULL; + g_free(p->iov); + p->iov = NULL; } static inline void prepare_next_iov(MultiFDSendParams *p, void *base, From 51c943931d75959b787ea72e2cf8e79369ac1cd0 Mon Sep 17 00:00:00 2001 From: Bibo Mao <maobibo@loongson.cn> Date: Fri, 13 Sep 2024 17:52:02 +0800 Subject: [PATCH 2857/2884] hw/loongarch/virt: Add description for virt machine type The description about virt machine type is removed by mistake, add new description here. Here is output result with command "./qemu-system-loongarch64 -M help" Supported machines are: none empty machine virt QEMU LoongArch Virtual Machine (default) x-remote Experimental remote machine Without the patch, it shows as follows: Supported machines are: none empty machine virt (null) (default) x-remote Experimental remote machine Fixes: ef2f11454c(hw/loongarch/virt: Replace Loongson IPI with LoongArch IPI) Signed-off-by: Bibo Mao <maobibo@loongson.cn> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> (cherry picked from commit 4265b4f358436252ef36164566f316458f1df671) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/loongarch/virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 29040422aa..8e80110b24 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1390,6 +1390,7 @@ static void virt_class_init(ObjectClass *oc, void *data) mc->init = virt_init; mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); mc->default_ram_id = "loongarch.ram"; + mc->desc = "QEMU LoongArch Virtual Machine"; mc->max_cpus = LOONGARCH_MAX_CPUS; mc->is_default = 1; mc->default_kernel_irqchip_split = false; From 4c7c0d2442fbfcbe128198aa5f3613f387f291ac Mon Sep 17 00:00:00 2001 From: TANG Tiancheng <tangtiancheng.ttc@alibaba-inc.com> Date: Wed, 4 Sep 2024 22:27:26 +0800 Subject: [PATCH 2858/2884] tcg: Fix iteration step in 32-bit gvec operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop in the 32-bit case of the vector compare operation was incorrectly incrementing by 8 bytes per iteration instead of 4 bytes. This caused the function to process only half of the intended elements. Cc: qemu-stable@nongnu.org Fixes: 9622c697d1 (tcg: Add gvec compare with immediate and scalar operand) Signed-off-by: TANG Tiancheng <tangtiancheng.ttc@alibaba-inc.com> Reviewed-by: Liu Zhiwei <zhiwei_liu@linux.alibaba.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240904142739.854-2-zhiwei_liu@linux.alibaba.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> (cherry picked from commit 9d8d5a5b9078a16b4c0862fe54248c5cc8435648) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- tcg/tcg-op-gvec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 0308732d9b..78ee1ced80 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -3939,7 +3939,7 @@ void tcg_gen_gvec_cmps(TCGCond cond, unsigned vece, uint32_t dofs, uint32_t i; tcg_gen_extrl_i64_i32(t1, c); - for (i = 0; i < oprsz; i += 8) { + for (i = 0; i < oprsz; i += 4) { tcg_gen_ld_i32(t0, tcg_env, aofs + i); tcg_gen_negsetcond_i32(cond, t0, t0, t1); tcg_gen_st_i32(t0, tcg_env, dofs + i); From f8244f3b8c87bd7483c4cc48f86947993bcf32fa Mon Sep 17 00:00:00 2001 From: Fabiano Rosas <farosas@suse.de> Date: Wed, 11 Sep 2024 11:16:51 -0300 Subject: [PATCH 2859/2884] target/ppc: Fix lxvx/stxvx facility check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XT check for the lxvx/stxvx instructions is currently inverted. This was introduced during the move to decodetree. >From the ISA: Chapter 7. Vector-Scalar Extension Facility Load VSX Vector Indexed X-form lxvx XT,RA,RB if TX=0 & MSR.VSX=0 then VSX_Unavailable() if TX=1 & MSR.VEC=0 then Vector_Unavailable() ... Let XT be the value 32×TX + T. The code currently does the opposite: if (paired || a->rt >= 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); } This was already fixed for lxv/stxv at commit "2cc0e449d1 (target/ppc: Fix lxv/stxv MSR facility check)", but the indexed forms were missed. Cc: qemu-stable@nongnu.org Fixes: 70426b5bb7 ("target/ppc: moved stxvx and lxvx from legacy to decodtree") Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Claudio Fontana <cfontana@suse.de> Acked-by: Ilya Leoshkevich <iii@linux.ibm.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Message-ID: <20240911141651.6914-1-farosas@suse.de> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> (cherry picked from commit 8bded2e73e80823a67f730140788a3c5e60bf4b5) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/ppc/translate/vsx-impl.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 40a87ddc4a..a869f30e86 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2244,7 +2244,7 @@ static bool do_lstxv_PLS_D(DisasContext *ctx, arg_PLS_D *a, static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store, bool paired) { - if (paired || a->rt >= 32) { + if (paired || a->rt < 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); From 8fc8dd2efdedec96082777dc3065a389a9ecf5d9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Date: Sat, 7 Sep 2024 00:01:38 +0100 Subject: [PATCH 2860/2884] hw/mips/jazz: fix typo in in-built NIC alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit e104edbb9d ("hw/mips/jazz: use qemu_find_nic_info()") contained a typo in the NIC alias which caused initialisation of the in-built dp83932 NIC to fail when using the normal -nic user,model=dp83932 command line. Fixes: e104edbb9d ("hw/mips/jazz: use qemu_find_nic_info()") Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: David Woodhouse <dwmw@amazon.co.uk> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> (cherry picked from commit 2e4fdf566062c03456230fd8136b88c5c1e5c4bf) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/mips/jazz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 1bc17e69d3..0d44e19707 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -128,7 +128,7 @@ static void mips_jazz_init_net(IOMMUMemoryRegion *rc4030_dma_mr, uint8_t *prom; NICInfo *nd; - nd = qemu_find_nic_info("dp8393x", true, "dp82932"); + nd = qemu_find_nic_info("dp8393x", true, "dp83932"); if (!nd) { return; } From bb630d92516cb39ee830566b5e0335a4e14b1cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Mon, 16 Sep 2024 09:53:56 +0100 Subject: [PATCH 2861/2884] util/timer: avoid deadlock when shutting down MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we shut down a guest we disable the timers. However this can cause deadlock if the guest has queued some async work that is trying to advance system time and spins forever trying to wind time forward. Pay attention to the return code and bail early if we can't wind time forward. Reported-by: Elisha Hollander <just4now666666@gmail.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240916085400.1046925-15-alex.bennee@linaro.org> (cherry picked from commit bc02be4508d8753d1f6071b77d10f4661587df6f) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- util/qemu-timer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 213114be68..6b1533bc2a 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -685,10 +685,17 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) { int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); AioContext *aio_context; + int64_t deadline; + aio_context = qemu_get_aio_context(); - while (clock < dest) { - int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); + /* + * A deadline of < 0 indicates this timer is not enabled, so we + * won't get far trying to run it forward. + */ + while (deadline >= 0 && clock < dest) { int64_t warp = qemu_soonest_timeout(dest - clock, deadline); qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); @@ -696,6 +703,9 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); } qemu_clock_notify(QEMU_CLOCK_VIRTUAL); From 7eefbf8bb72c1bec0972ca19901207dc6d2acf5a Mon Sep 17 00:00:00 2001 From: Fiona Ebner <f.ebner@proxmox.com> Date: Fri, 12 Jul 2024 16:07:16 +0200 Subject: [PATCH 2862/2884] block/reqlist: allow adding overlapping requests Allow overlapping request by removing the assert that made it impossible. There are only two callers: 1. block_copy_task_create() It already asserts the very same condition before calling reqlist_init_req(). 2. cbw_snapshot_read_lock() There is no need to have read requests be non-overlapping in copy-before-write when used for snapshot-access. In fact, there was no protection against two callers of cbw_snapshot_read_lock() calling reqlist_init_req() with overlapping ranges and this could lead to an assertion failure [1]. In particular, with the reproducer script below [0], two cbw_co_snapshot_block_status() callers could race, with the second calling reqlist_init_req() before the first one finishes and removes its conflicting request. [0]: > #!/bin/bash -e > dd if=/dev/urandom of=/tmp/disk.raw bs=1M count=1024 > ./qemu-img create /tmp/fleecing.raw -f raw 1G > ( > ./qemu-system-x86_64 --qmp stdio \ > --blockdev raw,node-name=node0,file.driver=file,file.filename=/tmp/disk.raw \ > --blockdev raw,node-name=node1,file.driver=file,file.filename=/tmp/fleecing.raw \ > <<EOF > {"execute": "qmp_capabilities"} > {"execute": "blockdev-add", "arguments": { "driver": "copy-before-write", "file": "node0", "target": "node1", "node-name": "node3" } } > {"execute": "blockdev-add", "arguments": { "driver": "snapshot-access", "file": "node3", "node-name": "snap0" } } > {"execute": "nbd-server-start", "arguments": {"addr": { "type": "unix", "data": { "path": "/tmp/nbd.socket" } } } } > {"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "snap0", "type": "nbd", "name": "exp0"}} > EOF > ) & > sleep 5 > while true; do > ./qemu-nbd -d /dev/nbd0 > ./qemu-nbd -c /dev/nbd0 nbd:unix:/tmp/nbd.socket:exportname=exp0 -f raw -r > nbdinfo --map 'nbd+unix:///exp0?socket=/tmp/nbd.socket' > done [1]: > #5 0x000071e5f0088eb2 in __GI___assert_fail (...) at ./assert/assert.c:101 > #6 0x0000615285438017 in reqlist_init_req (...) at ../block/reqlist.c:23 > #7 0x00006152853e2d98 in cbw_snapshot_read_lock (...) at ../block/copy-before-write.c:237 > #8 0x00006152853e3068 in cbw_co_snapshot_block_status (...) at ../block/copy-before-write.c:304 > #9 0x00006152853f4d22 in bdrv_co_snapshot_block_status (...) at ../block/io.c:3726 > #10 0x000061528543a63e in snapshot_access_co_block_status (...) at ../block/snapshot-access.c:48 > #11 0x00006152853f1a0a in bdrv_co_do_block_status (...) at ../block/io.c:2474 > #12 0x00006152853f2016 in bdrv_co_common_block_status_above (...) at ../block/io.c:2652 > #13 0x00006152853f22cf in bdrv_co_block_status_above (...) at ../block/io.c:2732 > #14 0x00006152853d9a86 in blk_co_block_status_above (...) at ../block/block-backend.c:1473 > #15 0x000061528538da6c in blockstatus_to_extents (...) at ../nbd/server.c:2374 > #16 0x000061528538deb1 in nbd_co_send_block_status (...) at ../nbd/server.c:2481 > #17 0x000061528538f424 in nbd_handle_request (...) at ../nbd/server.c:2978 > #18 0x000061528538f906 in nbd_trip (...) at ../nbd/server.c:3121 > #19 0x00006152855a7caf in coroutine_trampoline (...) at ../util/coroutine-ucontext.c:175 Cc: qemu-stable@nongnu.org Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Message-Id: <20240712140716.517911-1-f.ebner@proxmox.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> (cherry picked from commit 6475155d519209c80fdda53e05130365aa769838) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- block/copy-before-write.c | 3 ++- block/reqlist.c | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 853e01a1eb..28f6a096cd 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -66,7 +66,8 @@ typedef struct BDRVCopyBeforeWriteState { /* * @frozen_read_reqs: current read requests for fleecing user in bs->file - * node. These areas must not be rewritten by guest. + * node. These areas must not be rewritten by guest. There can be multiple + * overlapping read requests. */ BlockReqList frozen_read_reqs; diff --git a/block/reqlist.c b/block/reqlist.c index 08cb57cfa4..098e807378 100644 --- a/block/reqlist.c +++ b/block/reqlist.c @@ -20,8 +20,6 @@ void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset, int64_t bytes) { - assert(!reqlist_find_conflict(reqs, offset, bytes)); - *req = (BlockReq) { .offset = offset, .bytes = bytes, From 767e7d8ae1aee94e63f3d94a77dc1515a8a16dab Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel <ardb@kernel.org> Date: Fri, 27 Sep 2024 09:10:51 +0200 Subject: [PATCH 2863/2884] target/arm: Avoid target_ulong for physical address lookups target_ulong is typedef'ed as a 32-bit integer when building the qemu-system-arm target, and this is smaller than the size of an intermediate physical address when LPAE is being used. Given that Linux may place leaf level user page tables in high memory when built for LPAE, the kernel will crash with an external abort as soon as it enters user space when running with more than ~3 GiB of system RAM. So replace target_ulong with vaddr in places where it may carry an address value that is not representable in 32 bits. Fixes: f3639a64f602ea ("target/arm: Use softmmu tlbs for page table walking") Cc: qemu-stable@nongnu.org Reported-by: Arnd Bergmann <arnd@arndb.de> Tested-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Message-id: 20240927071051.1444768-1-ardb+git@google.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit 67d762e716a7127ecc114e9708254316dd521911) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/arm/internals.h | 4 ++-- target/arm/ptw.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 203a2dae14..38545552d0 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1430,7 +1430,7 @@ typedef struct GetPhysAddrResult { * * for PSMAv5 based systems we don't bother to return a full FSR format * value. */ -bool get_phys_addr(CPUARMState *env, target_ulong address, +bool get_phys_addr(CPUARMState *env, vaddr address, MMUAccessType access_type, ARMMMUIdx mmu_idx, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) __attribute__((nonnull)); @@ -1449,7 +1449,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, * Similar to get_phys_addr, but use the given security space and don't perform * a Granule Protection Check on the resulting address. */ -bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, +bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, MMUAccessType access_type, ARMMMUIdx mmu_idx, ARMSecuritySpace space, GetPhysAddrResult *result, diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 278004661b..26e670290f 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -74,13 +74,13 @@ typedef struct S1Translate { } S1Translate; static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi); static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi); @@ -3202,7 +3202,7 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, */ static bool get_phys_addr_disabled(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) @@ -3285,7 +3285,7 @@ static bool get_phys_addr_disabled(CPUARMState *env, } static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) @@ -3390,7 +3390,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, } static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) @@ -3527,7 +3527,7 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, } static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) @@ -3543,7 +3543,7 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, return false; } -bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, +bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, MMUAccessType access_type, ARMMMUIdx mmu_idx, ARMSecuritySpace space, GetPhysAddrResult *result, @@ -3556,7 +3556,7 @@ bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, return get_phys_addr_nogpc(env, &ptw, address, access_type, result, fi); } -bool get_phys_addr(CPUARMState *env, target_ulong address, +bool get_phys_addr(CPUARMState *env, vaddr address, MMUAccessType access_type, ARMMMUIdx mmu_idx, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { From e32ac563b8375ef9dca7b6d02e1cd2feaaab3f58 Mon Sep 17 00:00:00 2001 From: Jan Luebbe <jlu@pengutronix.de> Date: Fri, 6 Sep 2024 18:48:34 +0200 Subject: [PATCH 2864/2884] hw/sd/sdcard: Fix handling of disabled boot partitions The enable bits in the EXT_CSD_PART_CONFIG ext_csd register do *not* specify whether the boot partitions exist, but whether they are enabled for booting. Existence of the boot partitions is specified by a EXT_CSD_BOOT_MULT != 0. Currently, in the case of boot-partition-size=1M and boot-config=0, Linux detects boot partitions of 1M. But as sd_bootpart_offset always returns 0, all reads/writes are mapped to the same offset in the backing file. Fix this bug by calculating the offset independent of which partition is enabled for booting. This bug is unlikely to affect many users with QEMU's current set of boards, because only aspeed sets boot-partition-size, and it also sets boot-config to 8. So to run into this a user would have to manually mark the boot partition non-booting from within the guest. Cc: qemu-stable@nongnu.org Signed-off-by: Jan Luebbe <jlu@pengutronix.de> Message-id: 20240906164834.130257-1-jlu@pengutronix.de Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: added note to commit message about effects of bug] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit 9601076b3b0bced7ed597d1470e3ff2f4e7177d6) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/sd/sd.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a140a32ccd..26d6eebe89 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -774,19 +774,12 @@ static uint32_t sd_blk_len(SDState *sd) */ static uint32_t sd_bootpart_offset(SDState *sd) { - bool partitions_enabled; unsigned partition_access; if (!sd->boot_part_size || !sd_is_emmc(sd)) { return 0; } - partitions_enabled = sd->ext_csd[EXT_CSD_PART_CONFIG] - & EXT_CSD_PART_CONFIG_EN_MASK; - if (!partitions_enabled) { - return 0; - } - partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG] & EXT_CSD_PART_CONFIG_ACC_MASK; switch (partition_access) { From 02ac67c41fbc6c4fe78ccec63dae959f45d13705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org> Date: Wed, 2 Oct 2024 10:03:33 +0200 Subject: [PATCH 2865/2884] testing: bump mips64el cross to bookworm and fix package list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mips64el cross setup is very broken for bullseye which has now entered LTS support so is unlikely to be fixed. While we still can't build the container with all packages for bookworm due to a single missing dependency that will hopefully get fixed in due course. For the sake of keeping the CI green we disable the problematic packages via the lcitool's mappings.yml file. See also: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1081535 Signed-off-by: Alex Bennée <alex.bennee@linaro.org> [thuth: Disable the problematic packages via lcitool's mappings.yml] Message-ID: <20241002080333.127172-1-thuth@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com> (cherry picked from commit c60473d29254b79d9437eface8b342e84663ba66) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- .../dockerfiles/debian-mips64el-cross.docker | 18 +++---------- tests/lcitool/mappings.yml | 26 +++++++++++++++++++ tests/lcitool/refresh | 2 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 2862785692..bfa96cb507 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mips64el debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mips64el debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -48,16 +48,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ @@ -69,8 +68,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -97,17 +94,13 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcmocka-dev:mips64el \ libcurl4-gnutls-dev:mips64el \ libdaxctl-dev:mips64el \ - libdrm-dev:mips64el \ - libepoxy-dev:mips64el \ libfdt-dev:mips64el \ libffi-dev:mips64el \ libfuse3-dev:mips64el \ - libgbm-dev:mips64el \ libgcrypt20-dev:mips64el \ libglib2.0-dev:mips64el \ libglusterfs-dev:mips64el \ libgnutls28-dev:mips64el \ - libgtk-3-dev:mips64el \ libibverbs-dev:mips64el \ libiscsi-dev:mips64el \ libjemalloc-dev:mips64el \ @@ -126,8 +119,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ librbd-dev:mips64el \ librdmacm-dev:mips64el \ libsasl2-dev:mips64el \ - libsdl2-dev:mips64el \ - libsdl2-image-dev:mips64el \ libseccomp-dev:mips64el \ libselinux1-dev:mips64el \ libslirp-dev:mips64el \ @@ -141,8 +132,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libusb-1.0-0-dev:mips64el \ libusbredirhost-dev:mips64el \ libvdeplug-dev:mips64el \ - libvirglrenderer-dev:mips64el \ - libvte-2.91-dev:mips64el \ + libxdp-dev:mips64el \ libzstd-dev:mips64el \ nettle-dev:mips64el \ systemtap-sdt-dev:mips64el \ diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 03b974ad02..0ab3a89013 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -2,6 +2,20 @@ mappings: flake8: OpenSUSELeap15: + # Due to https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1081535 we + # have to disable all packages that depend on libgl1-mesa-dri:mips64el + gtk3: + mips64el-deb: + + libdrm: + mips64el-deb: + + libepoxy: + mips64el-deb: + + mesa-libgbm: + mips64el-deb: + meson: OpenSUSELeap15: @@ -60,6 +74,18 @@ mappings: python3-wheel: OpenSUSELeap15: python311-pip + sdl2: + mips64el-deb: + + sdl2-image: + mips64el-deb: + + virglrenderer: + mips64el-deb: + + vte: + mips64el-deb: + pypi_mappings: # Request more recent version meson: diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index c60490a7fa..35c5266381 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -166,7 +166,7 @@ try: "x86_64-linux-user," "i386-softmmu,i386-linux-user")) - generate_dockerfile("debian-mips64el-cross", "debian-11", + generate_dockerfile("debian-mips64el-cross", "debian-12", cross="mips64el", trailer=cross_build("mips64el-linux-gnuabi64-", "mips64el-softmmu,mips64el-linux-user")) From 5762cdaf45b0b75d55d64f3b8f441b140026ccb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Tue, 20 Aug 2024 17:11:12 +0400 Subject: [PATCH 2866/2884] vnc: fix crash when no console attached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit e99441a3793b5 ("ui/curses: Do not use console_select()") qemu_text_console_put_keysym() no longer checks for NULL console argument, which leads to a later crash: Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. 0x00005555559ee186 in qemu_text_console_handle_keysym (s=0x0, keysym=31) at ../ui/console-vc.c:332 332 } else if (s->echo && (keysym == '\r' || keysym == '\n')) { (gdb) bt #0 0x00005555559ee186 in qemu_text_console_handle_keysym (s=0x0, keysym=31) at ../ui/console-vc.c:332 #1 0x00005555559e18e5 in qemu_text_console_put_keysym (s=<optimized out>, keysym=<optimized out>) at ../ui/console.c:303 #2 0x00005555559f2e88 in do_key_event (vs=vs@entry=0x5555579045c0, down=down@entry=1, keycode=keycode@entry=60, sym=sym@entry=65471) at ../ui/vnc.c:2034 #3 0x00005555559f845c in ext_key_event (vs=0x5555579045c0, down=1, sym=65471, keycode=<optimized out>) at ../ui/vnc.c:2070 #4 protocol_client_msg (vs=0x5555579045c0, data=<optimized out>, len=<optimized out>) at ../ui/vnc.c:2514 #5 0x00005555559f515c in vnc_client_read (vs=0x5555579045c0) at ../ui/vnc.c:1607 Fixes: e99441a3793b5 ("ui/curses: Do not use console_select()") Fixes: https://issues.redhat.com/browse/RHEL-50529 Cc: qemu-stable@nongnu.org Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> (cherry picked from commit 0e60fc80938d9ce84274a36ddfaaa640bdef2be8) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- ui/vnc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c index dae5d51210..5057ec8680 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1935,7 +1935,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } qkbd_state_key_event(vs->vd->kbd, qcode, down); - if (!qemu_console_is_graphic(vs->vd->dcl.con)) { + if (QEMU_IS_TEXT_CONSOLE(vs->vd->dcl.con)) { QemuTextConsole *con = QEMU_TEXT_CONSOLE(vs->vd->dcl.con); bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK); bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL); From 18046fbec50a43f195ad5818efaeed9935afed02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= <philmd@linaro.org> Date: Thu, 22 Aug 2024 11:50:43 +0200 Subject: [PATCH 2867/2884] linux-user/flatload: Take mmap_lock in load_flt_binary() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_flt_binary() calls load_flat_file() -> page_set_flags(). page_set_flags() must be called with the mmap_lock held, otherwise it aborts: $ qemu-arm -L stm32/lib/ stm32/bin/busybox qemu-arm: ../accel/tcg/user-exec.c:505: page_set_flags: Assertion `have_mmap_lock()' failed. Aborted (core dumped) Fix by taking the lock in load_flt_binary(). Fixes: fbd3c4cff6 ("linux-user/arm: Mark the commpage executable") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2525 Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20240822095045.72643-3-philmd@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> (cherry picked from commit a9ee641bd46f5462eeed183ac3c3760bddfc2600) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- linux-user/flatload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 04d8138d12..0e4be5bf44 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -487,7 +487,10 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) stack_len += (bprm->envc + 1) * 4; /* the envp array */ + mmap_lock(); res = load_flat_file(bprm, libinfo, 0, &stack_len); + mmap_unlock(); + if (is_error(res)) { return res; } From 167c8d374aba92fd24f87445b43d093f31f84c09 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Sat, 5 Oct 2024 09:01:22 -0700 Subject: [PATCH 2868/2884] linux-user: Fix parse_elf_properties GNU0_MAGIC check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comparing a string of 4 bytes only works in little-endian. Adjust bulk bswap to only apply to the note payload. Perform swapping of the note header manually; the magic is defined so that it does not need a runtime swap. Fixes: 83f990eb5adb ("linux-user/elfload: Parse NT_GNU_PROPERTY_TYPE_0 notes") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2596 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> (cherry picked from commit 2884596f5f385b5712c356310dd4125a089888a8) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- linux-user/elfload.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index b27dd01734..0b1c230b1c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3136,11 +3136,11 @@ static bool parse_elf_properties(const ImageSource *src, } /* - * The contents of a valid PT_GNU_PROPERTY is a sequence - * of uint32_t -- swap them all now. + * The contents of a valid PT_GNU_PROPERTY is a sequence of uint32_t. + * Swap most of them now, beyond the header and namesz. */ #ifdef BSWAP_NEEDED - for (int i = 0; i < n / 4; i++) { + for (int i = 4; i < n / 4; i++) { bswap32s(note.data + i); } #endif @@ -3150,15 +3150,15 @@ static bool parse_elf_properties(const ImageSource *src, * immediately follows nhdr and is thus at the 4th word. Further, all * of the inputs to the kernel's round_up are multiples of 4. */ - if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 || - note.nhdr.n_namesz != NOTE_NAME_SZ || + if (tswap32(note.nhdr.n_type) != NT_GNU_PROPERTY_TYPE_0 || + tswap32(note.nhdr.n_namesz) != NOTE_NAME_SZ || note.data[3] != GNU0_MAGIC) { error_setg(errp, "Invalid note in PT_GNU_PROPERTY"); return false; } off = sizeof(note.nhdr) + NOTE_NAME_SZ; - datasz = note.nhdr.n_descsz + off; + datasz = tswap32(note.nhdr.n_descsz) + off; if (datasz > n) { error_setg(errp, "Invalid note size in PT_GNU_PROPERTY"); return false; From 854a38fd9d212d2fce594b34bd989f9ef7e40c75 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Sat, 5 Oct 2024 22:09:54 +0000 Subject: [PATCH 2869/2884] tcg/ppc: Use TCG_REG_TMP2 for scratch tcg_out_qemu_st In the fallback when STDBRX is not available, avoid clobbering TCG_REG_TMP1, which might be h.base, which is still in use. Use TCG_REG_TMP2 instead. Cc: qemu-stable@nongnu.org Fixes: 01a112e2e9 ("tcg/ppc: Reorg tcg_out_tlb_read") Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Tested-By: Michael Tokarev <mjt@tls.msk.ru> (cherry picked from commit 4cabcb89b101942346aebff081aa1453e958fe7f) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- tcg/ppc/tcg-target.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 3553a47ba9..69abd30bbb 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2704,9 +2704,9 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; if (!have_isa_2_06 && insn == STDBRX) { tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); - tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, h.index, 4)); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP2, h.index, 4)); tcg_out_shri64(s, TCG_REG_R0, datalo, 32); - tcg_out32(s, STWBRX | SAB(TCG_REG_R0, h.base, TCG_REG_TMP1)); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, h.base, TCG_REG_TMP2)); } else { tcg_out32(s, insn | SAB(datalo, h.base, h.index)); } From 8f583fd99a2832b2e660e8ca5d53fa6496c27dd5 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Sat, 5 Oct 2024 22:09:54 +0000 Subject: [PATCH 2870/2884] tcg/ppc: Use TCG_REG_TMP2 for scratch index in prepare_host_addr In tcg_out_qemu_ldst_i128, we need a non-zero index register, which we then use as a base register in several address modes. Since we always have TCG_REG_TMP2 available, use that. Cc: qemu-stable@nongnu.org Fixes: 526cd4ec01f ("tcg/ppc: Support 128-bit load/store") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2597 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Tested-By: Michael Tokarev <mjt@tls.msk.ru> (cherry picked from commit 3213da7b9539581c6df95f8ced5b09d0b02d425f) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- tcg/ppc/tcg-target.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 69abd30bbb..deb80521b3 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2617,8 +2617,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { /* Zero-extend the guest address for use in the host address. */ - tcg_out_ext32u(s, TCG_REG_R0, addrlo); - h->index = TCG_REG_R0; + tcg_out_ext32u(s, TCG_REG_TMP2, addrlo); + h->index = TCG_REG_TMP2; } else { h->index = addrlo; } From a4f9d9a4b2167201d53050eb7e00bef0c863d075 Mon Sep 17 00:00:00 2001 From: Richard Henderson <richard.henderson@linaro.org> Date: Tue, 13 Aug 2024 10:04:00 +1000 Subject: [PATCH 2871/2884] target/m68k: Always return a temporary from gen_lea_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returning a raw areg does not preserve the value if the areg is subsequently modified. Fixes, e.g. "jsr (sp)", where the return address is pushed before the branch. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2483 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20240813000737.228470-1-richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> (cherry picked from commit 352cc9f300d83ea48b8154bfd2ff985fece887d0) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- target/m68k/translate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 445966fb6a..ad3ce34501 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -720,7 +720,9 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, } /* fallthru */ case 2: /* Indirect register */ - return get_areg(s, reg0); + tmp = tcg_temp_new(); + tcg_gen_mov_i32(tmp, get_areg(s, reg0)); + return tmp; case 4: /* Indirect predecrememnt. */ if (opsize == OS_UNSIZED) { return NULL_QREG; @@ -747,20 +749,23 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, switch (reg0) { case 0: /* Absolute short. */ offset = (int16_t)read_im16(env, s); - return tcg_constant_i32(offset); + break; case 1: /* Absolute long. */ offset = read_im32(env, s); - return tcg_constant_i32(offset); + break; case 2: /* pc displacement */ offset = s->pc; offset += (int16_t)read_im16(env, s); - return tcg_constant_i32(offset); + break; case 3: /* pc index+displacement. */ return gen_lea_indexed(env, s, NULL_QREG); case 4: /* Immediate. */ default: return NULL_QREG; } + tmp = tcg_temp_new(); + tcg_gen_movi_i32(tmp, offset); + return tmp; } /* Should never happen. */ return NULL_QREG; From e894be998d0871f55581cb9cf6ce330bf6d590b2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <pierrick.bouvier@linaro.org> Date: Fri, 4 Oct 2024 15:37:15 -0700 Subject: [PATCH 2872/2884] meson: fix machine option for x86_version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s/mbmi1/mbmi/ When configuring with -Dx86_version >= 3, meson step works, but compilation fails because option -mbmi1 is unknown. Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Alex Bennée <alex.bennee@linaro.org> Link: https://lore.kernel.org/r/20241004223715.1275428-1-pierrick.bouvier@linaro.org Cc: qemu-stable@nongnu.org Fixes: ef7d1adfa85 ("meson: allow configuring the x86-64 baseline", 2024-06-28) Revieved-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> (cherry picked from commit 461a9252e249adab5f0bae3b9634be77dd5be17e) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index fbda17c987..6e467cbe7d 100644 --- a/meson.build +++ b/meson.build @@ -362,7 +362,7 @@ if host_arch in ['i386', 'x86_64'] qemu_common_flags = cc.get_supported_arguments('-mneeded') + qemu_common_flags endif if get_option('x86_version') >= '3' - qemu_common_flags = ['-mmovbe', '-mabm', '-mbmi1', '-mbmi2', '-mfma', '-mf16c'] + qemu_common_flags + qemu_common_flags = ['-mmovbe', '-mabm', '-mbmi', '-mbmi2', '-mfma', '-mf16c'] + qemu_common_flags endif # add required vector instruction set (each level implies those below) From 22359e0e6ee4ddc0ed1bfe6f8a02d81fdb13693b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Mon, 7 Oct 2024 10:31:28 +0200 Subject: [PATCH 2873/2884] meson: define qemu_isa_flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a separate variable for compiler flags that enable specific instruction set extensions, so that they can be used with cc.compiles/cc.links. Note that -mfpmath=sse is a code generation option but it does not enable new instructions, therefore I did not make it part of qemu_isa_flags. Suggested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Alex Bennée <alex.bennee@linaro.org> Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> (cherry picked from commit 6ae8c5382b2396d394e135c2c6d3742d11c6d0c2) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- meson.build | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 6e467cbe7d..3031f37f45 100644 --- a/meson.build +++ b/meson.build @@ -322,6 +322,10 @@ elif host_os == 'windows' endif endif +# Choose instruction set (currently x86-only) + +qemu_isa_flags = [] + # __sync_fetch_and_and requires at least -march=i486. Many toolchains # use i686 as default anyway, but for those that don't, an explicit # specification is necessary @@ -338,7 +342,7 @@ if host_arch == 'i386' and not cc.links(''' sfaa(&val); return val; }''') - qemu_common_flags = ['-march=i486'] + qemu_common_flags + qemu_isa_flags += ['-march=i486'] endif # Pick x86-64 baseline version @@ -354,29 +358,31 @@ if host_arch in ['i386', 'x86_64'] else # present on basically all processors but technically not part of # x86-64-v1, so only include -mneeded for x86-64 version 2 and above - qemu_common_flags = ['-mcx16'] + qemu_common_flags + qemu_isa_flags += ['-mcx16'] endif endif if get_option('x86_version') >= '2' - qemu_common_flags = ['-mpopcnt'] + qemu_common_flags - qemu_common_flags = cc.get_supported_arguments('-mneeded') + qemu_common_flags + qemu_isa_flags += ['-mpopcnt'] + qemu_isa_flags += cc.get_supported_arguments('-mneeded') endif if get_option('x86_version') >= '3' - qemu_common_flags = ['-mmovbe', '-mabm', '-mbmi', '-mbmi2', '-mfma', '-mf16c'] + qemu_common_flags + qemu_isa_flags += ['-mmovbe', '-mabm', '-mbmi', '-mbmi2', '-mfma', '-mf16c'] endif # add required vector instruction set (each level implies those below) if get_option('x86_version') == '1' - qemu_common_flags = ['-msse2'] + qemu_common_flags + qemu_isa_flags += ['-msse2'] elif get_option('x86_version') == '2' - qemu_common_flags = ['-msse4.2'] + qemu_common_flags + qemu_isa_flags += ['-msse4.2'] elif get_option('x86_version') == '3' - qemu_common_flags = ['-mavx2'] + qemu_common_flags + qemu_isa_flags += ['-mavx2'] elif get_option('x86_version') == '4' - qemu_common_flags = ['-mavx512f', '-mavx512bw', '-mavx512cd', '-mavx512dq', '-mavx512vl'] + qemu_common_flags + qemu_isa_flags += ['-mavx512f', '-mavx512bw', '-mavx512cd', '-mavx512dq', '-mavx512vl'] endif endif +qemu_common_flags = qemu_isa_flags + qemu_common_flags + if get_option('prefer_static') qemu_ldflags += get_option('b_pie') ? '-static-pie' : '-static' endif From 997f8d5c2b5ea4d1f07976ed54b944d6cdcdf2b6 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini <pbonzini@redhat.com> Date: Sun, 6 Oct 2024 09:44:00 +0200 Subject: [PATCH 2874/2884] meson: ensure -mcx16 is passed when detecting ATOMIC128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving -mcx16 out of CPU_CFLAGS caused the detection of ATOMIC128 to fail, because flags have to be specified by hand in cc.compiles and cc.links invocations (why oh why??). Ensure that these tests enable all the instruction set extensions that will be used to build the emulators. Fixes: c2bf2ccb266 ("configure: move -mcx16 flag out of CPU_CFLAGS", 2024-05-24) Reported-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Alex Bennée <alex.bennee@linaro.org> Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> (cherry picked from commit 8db4e0f92e83fd80b6609439440b303ddded7ad8) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- meson.build | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 3031f37f45..a11018b3ad 100644 --- a/meson.build +++ b/meson.build @@ -2795,7 +2795,7 @@ config_host_data.set('CONFIG_ATOMIC64', cc.links(''' __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); return 0; - }''')) + }''', args: qemu_isa_flags)) has_int128_type = cc.compiles(''' __int128_t a; @@ -2829,7 +2829,7 @@ if has_int128_type __atomic_compare_exchange_n(&p[4], &p[5], p[6], 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); return 0; }''' - has_atomic128 = cc.links(atomic_test_128) + has_atomic128 = cc.links(atomic_test_128, args: qemu_isa_flags) config_host_data.set('CONFIG_ATOMIC128', has_atomic128) @@ -2838,7 +2838,8 @@ if has_int128_type # without optimization enabled. Try again with optimizations locally # enabled for the function. See # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107389 - has_atomic128_opt = cc.links('__attribute__((optimize("O1")))' + atomic_test_128) + has_atomic128_opt = cc.links('__attribute__((optimize("O1")))' + atomic_test_128, + args: qemu_isa_flags) config_host_data.set('CONFIG_ATOMIC128_OPT', has_atomic128_opt) if not has_atomic128_opt @@ -2849,7 +2850,7 @@ if has_int128_type __sync_val_compare_and_swap_16(&x, y, x); return 0; } - ''')) + ''', args: qemu_isa_flags)) endif endif endif From c5f652a0532961c20bd5ba78a65288209605b522 Mon Sep 17 00:00:00 2001 From: Alexandra Diupina <adiupina@astralinux.ru> Date: Mon, 14 Oct 2024 17:05:50 +0100 Subject: [PATCH 2875/2884] hw/intc/arm_gicv3: Add cast to match the documentation The result of 1 << regbit with regbit==31 has a 1 in the 32nd bit. When cast to uint64_t (for further bitwise OR), the 32 most significant bits will be filled with 1s. However, the documentation states that the upper 32 bits of ICH_AP[0/1]R<n>_EL2 are reserved. Add an explicit cast to match the documentation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: qemu-stable@nongnu.org Fixes: d2c0c6aab6 ("hw/intc/arm_gicv3: Handle icv_nmiar1_read() for icc_nmiar1_read()") Signed-off-by: Alexandra Diupina <adiupina@astralinux.ru> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit e0c0ea6eca4f210a52b9742817586cc97b1ee434) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/intc/arm_gicv3_cpuif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index bdb13b00e9..ebad7aaea1 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -781,7 +781,7 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp) if (nmi) { cs->ich_apr[grp][regno] |= ICV_AP1R_EL1_NMI; } else { - cs->ich_apr[grp][regno] |= (1 << regbit); + cs->ich_apr[grp][regno] |= (1U << regbit); } } From 6fecfc5978e25c2298eed4aa0f254ac7a0384d81 Mon Sep 17 00:00:00 2001 From: Alexandra Diupina <adiupina@astralinux.ru> Date: Mon, 14 Oct 2024 17:05:50 +0100 Subject: [PATCH 2876/2884] hw/intc/arm_gicv3: Add cast to match the documentation The result of 1 << regbit with regbit==31 has a 1 in the 32nd bit. When cast to uint64_t (for further bitwise OR), the 32 most significant bits will be filled with 1s. However, the documentation states that the upper 32 bits of ICC_AP[0/1]R<n>_EL2 are reserved. Add an explicit cast to match the documentation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: qemu-stable@nongnu.org Fixes: 28cca59c46 ("hw/intc/arm_gicv3: Add NMI handling CPU interface registers") Signed-off-by: Alexandra Diupina <adiupina@astralinux.ru> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit 12dc8f6eca1ead876142fd3d6731cf3da1295f2a) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/intc/arm_gicv3_cpuif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index ebad7aaea1..89359db700 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1170,7 +1170,7 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq) if (nmi) { cs->icc_apr[cs->hppi.grp][regno] |= ICC_AP1R_EL1_NMI; } else { - cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + cs->icc_apr[cs->hppi.grp][regno] |= (1U << regbit); } if (irq < GIC_INTERNAL) { From 460ddd62fa559ec6c53e6229a3f1b6ceea7f5390 Mon Sep 17 00:00:00 2001 From: Alexandra Diupina <adiupina@astralinux.ru> Date: Mon, 14 Oct 2024 17:05:51 +0100 Subject: [PATCH 2877/2884] hw/intc/arm_gicv3_cpuif: Add cast to match the documentation The result of 1 << regbit with regbit==31 has a 1 in the 32nd bit. When cast to uint64_t (for further bitwise OR), the 32 most significant bits will be filled with 1s. However, the documentation states that the upper 32 bits of ICH_AP[0/1]R<n>_EL2 are reserved. Add an explicit cast to match the documentation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: qemu-stable@nongnu.org Fixes: c3f21b065a ("hw/intc/arm_gicv3_cpuif: Support vLPIs") Signed-off-by: Alexandra Diupina <adiupina@astralinux.ru> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit 3db74afec3ca87f81fbdf5918ed1e21d837fbfab) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/intc/arm_gicv3_cpuif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 89359db700..ea1d1b3455 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -793,7 +793,7 @@ static void icv_activate_vlpi(GICv3CPUState *cs) int regno = aprbit / 32; int regbit = aprbit % 32; - cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit); + cs->ich_apr[cs->hppvlpi.grp][regno] |= (1U << regbit); gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0); } From 10e3edd9b3b745ca7772a046c06a27ef539fba33 Mon Sep 17 00:00:00 2001 From: Peter Maydell <peter.maydell@linaro.org> Date: Mon, 14 Oct 2024 17:05:53 +0100 Subject: [PATCH 2878/2884] hw/char/pl011: Use correct masks for IBRD and FBRD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit b88cfee90268cad we defined masks for the IBRD and FBRD integer and fractional baud rate divider registers, to prevent the guest from writing invalid values which could cause division-by-zero. Unfortunately we got the mask values the wrong way around: the FBRD register is six bits and the IBRD register is 16 bits, not vice-versa. You would only run into this bug if you programmed the UART to a baud rate of less than 9600, because for 9600 baud and above the IBRD value will fit into 6 bits, as per the table in https://developer.arm.com/documentation/ddi0183/g/programmers-model/register-descriptions/fractional-baud-rate-register--uartfbrd The only visible effects would be that the value read back from the register by the guest would be truncated, and we would print an incorrect baud rate in the debug logs. Cc: qemu-stable@nongnu.org Fixes: b88cfee90268 ("hw/char/pl011: Avoid division-by-zero in pl011_get_baudrate()") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2610 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Gavin Shan <gshan@redhat.com> Message-id: 20241007144732.2491331-1-peter.maydell@linaro.org (cherry picked from commit cd247eae16ab1b9ce97fd34c000c1b883feeda45) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/char/pl011.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index f8078aa216..949e9d0e0d 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -88,10 +88,10 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) #define CR_LBE (1 << 7) /* Integer Baud Rate Divider, UARTIBRD */ -#define IBRD_MASK 0x3f +#define IBRD_MASK 0xffff /* Fractional Baud Rate Divider, UARTFBRD */ -#define FBRD_MASK 0xffff +#define FBRD_MASK 0x3f static const unsigned char pl011_id_arm[8] = { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; From 2787ca0e0abec57be2c2989520b7a19997b592f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Tue, 8 Oct 2024 16:50:10 +0400 Subject: [PATCH 2879/2884] hw/audio/hda: free timer on exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 280c1e1cd ("audio/hda: create millisecond timers that handle IO") Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20241008125028.1177932-2-marcandre.lureau@redhat.com> (cherry picked from commit f27206ceedbe2efae37c8d143c5eb2db05251508) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/audio/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index b22e486fda..ee3d0a7dec 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -751,7 +751,7 @@ static void hda_audio_exit(HDACodecDevice *hda) continue; } if (a->use_timer) { - timer_del(st->buft); + timer_free(st->buft); } if (st->output) { AUD_close_out(&a->card, st->voice.out); From 6d03242a7e47815ed56687ecd13f683d8da3f2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Tue, 8 Oct 2024 16:50:11 +0400 Subject: [PATCH 2880/2884] hw/audio/hda: fix memory leak on audio setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When SET_STREAM_FORMAT is called, we should clear the existing setup. Factor out common function to close a stream. Direct leak of 144 byte(s) in 3 object(s) allocated from: #0 0x7f91d38f7350 in calloc (/lib64/libasan.so.8+0xf7350) (BuildId: a4ad7eb954b390cf00f07fa10952988a41d9fc7a) #1 0x7f91d2ab7871 in g_malloc0 (/lib64/libglib-2.0.so.0+0x64871) (BuildId: 36b60dbd02e796145a982d0151ce37202ec05649) #2 0x562fa2f447ee in timer_new_full /home/elmarco/src/qemu/include/qemu/timer.h:538 #3 0x562fa2f4486f in timer_new /home/elmarco/src/qemu/include/qemu/timer.h:559 #4 0x562fa2f448a9 in timer_new_ns /home/elmarco/src/qemu/include/qemu/timer.h:577 #5 0x562fa2f47955 in hda_audio_setup ../hw/audio/hda-codec.c:490 #6 0x562fa2f4897e in hda_audio_command ../hw/audio/hda-codec.c:605 Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20241008125028.1177932-3-marcandre.lureau@redhat.com> (cherry picked from commit 6d6e23361fc732e4fe36a8bc5873b85f264ed53a) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/audio/hda-codec.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index ee3d0a7dec..4373565371 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -472,6 +472,24 @@ static void hda_audio_set_amp(HDAAudioStream *st) } } +static void hda_close_stream(HDAAudioState *a, HDAAudioStream *st) +{ + if (st->node == NULL) { + return; + } + if (a->use_timer) { + timer_free(st->buft); + st->buft = NULL; + } + if (st->output) { + AUD_close_out(&a->card, st->voice.out); + st->voice.out = NULL; + } else { + AUD_close_in(&a->card, st->voice.in); + st->voice.in = NULL; + } +} + static void hda_audio_setup(HDAAudioStream *st) { bool use_timer = st->state->use_timer; @@ -484,6 +502,7 @@ static void hda_audio_setup(HDAAudioStream *st) trace_hda_audio_format(st->node->name, st->as.nchannels, fmt2name[st->as.fmt], st->as.freq); + hda_close_stream(st->state, st); if (st->output) { if (use_timer) { cb = hda_audio_output_cb; @@ -741,23 +760,11 @@ static void hda_audio_init(HDACodecDevice *hda, static void hda_audio_exit(HDACodecDevice *hda) { HDAAudioState *a = HDA_AUDIO(hda); - HDAAudioStream *st; int i; dprint(a, 1, "%s\n", __func__); for (i = 0; i < ARRAY_SIZE(a->st); i++) { - st = a->st + i; - if (st->node == NULL) { - continue; - } - if (a->use_timer) { - timer_free(st->buft); - } - if (st->output) { - AUD_close_out(&a->card, st->voice.out); - } else { - AUD_close_in(&a->card, st->voice.in); - } + hda_close_stream(a, a->st + i); } AUD_remove_card(&a->card); } From 9391f419c7ef5e180e42177ea9a662389a69bbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Tue, 8 Oct 2024 16:50:12 +0400 Subject: [PATCH 2881/2884] ui/dbus: fix leak on message filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A filter function that wants to drop a message should return NULL, in which case it must also unref the message itself. Fixes: fa88b85de ("ui/dbus: filter out pending messages when scanout") Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20241008125028.1177932-4-marcandre.lureau@redhat.com> (cherry picked from commit 244d52ff736fefc3dd364ed091720aa896af306d) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- ui/dbus-listener.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index a54123acea..434bd608f2 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -1001,6 +1001,7 @@ dbus_filter(GDBusConnection *connection, serial = g_dbus_message_get_serial(message); if (serial <= ddl->out_serial_to_discard) { trace_dbus_filter(serial, ddl->out_serial_to_discard); + g_object_unref(message); return NULL; } From e1324ec9465efbd7ca95c4ad29d3d3cf102d05c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Tue, 8 Oct 2024 16:50:13 +0400 Subject: [PATCH 2882/2884] ui/win32: fix potential use-after-free with dbus shared memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DisplaySurface may be free before the pixman image is freed, since the image is refcounted and used by different objects, including pending dbus messages. Furthermore, setting the destroy function in create_displaysurface_from() isn't appropriate, as it may not be used, and may be overriden as in ramfb. Set the destroy function when the shared handle is set, use the HANDLE directly for destroy data, using a single common helper qemu_pixman_win32_image_destroy(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20241008125028.1177932-5-marcandre.lureau@redhat.com> (cherry picked from commit 330ef31deb2e5461cff907488b710f5bd9cd2327) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- hw/display/virtio-gpu.c | 14 ++------------ include/ui/qemu-pixman.h | 2 ++ ui/console.c | 24 ++---------------------- ui/qemu-pixman.c | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 3281842bfe..017a0f170c 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -238,16 +238,6 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, return height * stride; } -#ifdef WIN32 -static void -win32_pixman_image_destroy(pixman_image_t *image, void *data) -{ - HANDLE handle = data; - - qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn); -} -#endif - static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -308,7 +298,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, bits, c2d.height ? res->hostmem / c2d.height : 0); #ifdef WIN32 if (res->image) { - pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); + pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); } #endif } @@ -1327,7 +1317,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } #ifdef WIN32 - pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); + pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); #endif res->addrs = g_new(uint64_t, res->iov_cnt); diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index ef13a8210c..e3dd72b9e3 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -97,6 +97,8 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, void qemu_pixman_image_unref(pixman_image_t *image); +void qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) #endif /* QEMU_PIXMAN_H */ diff --git a/ui/console.c b/ui/console.c index 105a0e2c70..8f416ff0b9 100644 --- a/ui/console.c +++ b/ui/console.c @@ -461,24 +461,6 @@ void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, surface->handle = h; surface->handle_offset = offset; } - -static void -win32_pixman_image_destroy(pixman_image_t *image, void *data) -{ - DisplaySurface *surface = data; - - if (!surface->handle) { - return; - } - - assert(surface->handle_offset == 0); - - qemu_win32_map_free( - pixman_image_get_data(surface->image), - surface->handle, - &error_warn - ); -} #endif DisplaySurface *qemu_create_displaysurface(int width, int height) @@ -504,6 +486,8 @@ DisplaySurface *qemu_create_displaysurface(int width, int height) #ifdef WIN32 qemu_displaysurface_win32_set_handle(surface, handle, 0); + pixman_image_set_destroy_function(surface->image, + qemu_pixman_win32_image_destroy, handle); #endif return surface; } @@ -519,10 +503,6 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, width, height, (void *)data, linesize); assert(surface->image != NULL); -#ifdef WIN32 - pixman_image_set_destroy_function(surface->image, - win32_pixman_image_destroy, surface); -#endif return surface; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 5ca55dd199..de6c88151c 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -4,6 +4,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "ui/console.h" #include "standard-headers/drm/drm_fourcc.h" #include "trace.h" @@ -268,3 +269,17 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_image_unref(ibg); } #endif /* CONFIG_PIXMAN */ + +#ifdef WIN32 +void +qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data) +{ + HANDLE handle = data; + + qemu_win32_map_free( + pixman_image_get_data(image), + handle, + &error_warn + ); +} +#endif From 01fff50626c2bffd7be1ce92e531852ea69372f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com> Date: Tue, 8 Oct 2024 16:50:14 +0400 Subject: [PATCH 2883/2884] ui/dbus: fix filtering all update messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filtering pending messages when a new scanout is given shouldn't discard pending cursor changes, for example. Since filtering happens in a different thread, use atomic set/get. Fixes: fa88b85dea ("ui/dbus: filter out pending messages when scanout") Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Message-ID: <20241008125028.1177932-6-marcandre.lureau@redhat.com> (cherry picked from commit cf59889781297a5618f1735a5f31402caa806b42) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- ui/dbus-listener.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 434bd608f2..c69afc05a8 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "sysemu/sysemu.h" #include "dbus.h" +#include "glib.h" #ifdef G_OS_UNIX #include <gio/gunixfdlist.h> #endif @@ -85,7 +86,7 @@ struct _DBusDisplayListener { #endif guint dbus_filter; - guint32 out_serial_to_discard; + guint32 display_serial_to_discard; }; G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) @@ -93,10 +94,12 @@ G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) static void dbus_gfx_update(DisplayChangeListener *dcl, int x, int y, int w, int h); -static void ddl_discard_pending_messages(DBusDisplayListener *ddl) +static void ddl_discard_display_messages(DBusDisplayListener *ddl) { - ddl->out_serial_to_discard = g_dbus_connection_get_last_serial( + guint32 serial = g_dbus_connection_get_last_serial( g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); + + g_atomic_int_set(&ddl->display_serial_to_discard, serial); } #ifdef CONFIG_OPENGL @@ -290,7 +293,7 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, return; } - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); width = qemu_dmabuf_get_width(dmabuf); height = qemu_dmabuf_get_height(dmabuf); @@ -338,7 +341,7 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl) return false; } - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( ddl->map_proxy, @@ -401,7 +404,7 @@ dbus_scanout_share_d3d_texture( return false; } - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( ddl->d3d11_proxy, @@ -659,7 +662,7 @@ static void ddl_scanout(DBusDisplayListener *ddl) surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); - ddl_discard_pending_messages(ddl); + ddl_discard_display_messages(ddl); qemu_dbus_display1_listener_call_scanout( ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), @@ -992,17 +995,35 @@ dbus_filter(GDBusConnection *connection, gpointer user_data) { DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data); - guint32 serial; + guint32 serial, discard_serial; if (incoming) { return message; } serial = g_dbus_message_get_serial(message); - if (serial <= ddl->out_serial_to_discard) { - trace_dbus_filter(serial, ddl->out_serial_to_discard); - g_object_unref(message); - return NULL; + + discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard); + if (serial <= discard_serial) { + const char *member = g_dbus_message_get_member(message); + static const char *const display_messages[] = { + "Scanout", + "Update", +#ifdef CONFIG_GBM + "ScanoutDMABUF", + "UpdateDMABUF", +#endif + "ScanoutMap", + "UpdateMap", + "Disable", + NULL, + }; + + if (g_strv_contains(display_messages, member)) { + trace_dbus_filter(serial, discard_serial); + g_object_unref(message); + return NULL; + } } return message; From 0ff5ab6f57a2427a3e83969b2e7dd71e04caae39 Mon Sep 17 00:00:00 2001 From: Michael Tokarev <mjt@tls.msk.ru> Date: Fri, 18 Oct 2024 17:26:37 +0300 Subject: [PATCH 2884/2884] Update version for 9.1.1 release Signed-off-by: Michael Tokarev <mjt@tls.msk.ru> --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 47da986f86..44931da266 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.1.0 +9.1.1